Remove some initialization checks based on source positions.

This depends on
http://codereview.chromium.org/8352039 and
http://codereview.chromium.org/8423005 .

Review URL: http://codereview.chromium.org/8422010

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9870 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
keuchel@chromium.org 2011-11-03 12:05:48 +00:00
parent 58123bff56
commit fd21937812
6 changed files with 165 additions and 59 deletions

View File

@ -1283,28 +1283,60 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) {
Comment cmnt(masm_, var->IsContextSlot() Comment cmnt(masm_, var->IsContextSlot()
? "Context variable" ? "Context variable"
: "Stack variable"); : "Stack variable");
if (!var->binding_needs_init()) { if (var->binding_needs_init()) {
context()->Plug(var); // var->scope() may be NULL when the proxy is located in eval code and
} else { // refers to a potential outside binding. Currently those bindings are
// Let and const need a read barrier. // always looked up dynamically, i.e. in that case
GetVar(r0, var); // var->location() == LOOKUP.
__ CompareRoot(r0, Heap::kTheHoleValueRootIndex); // always holds.
if (var->mode() == LET || var->mode() == CONST_HARMONY) { ASSERT(var->scope() != NULL);
// Throw a reference error when using an uninitialized let/const
// binding in harmony mode. // Check if the binding really needs an initialization check. The check
Label done; // can be skipped in the following situation: we have a LET or CONST
__ b(ne, &done); // binding in harmony mode, both the Variable and the VariableProxy have
__ mov(r0, Operand(var->name())); // the same declaration scope (i.e. they are both in global code, in the
__ push(r0); // same function or in the same eval code) and the VariableProxy is in
__ CallRuntime(Runtime::kThrowReferenceError, 1); // the source physically located after the initializer of the variable.
__ bind(&done); //
} else { // We cannot skip any initialization checks for CONST in non-harmony
// Uninitalized const bindings outside of harmony mode are unholed. // mode because const variables may be declared but never initialized:
ASSERT(var->mode() == CONST); // if (false) { const x; }; var y = x;
__ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq); //
// The condition on the declaration scopes is a conservative check for
// nested functions that access a binding and are called before the
// binding is initialized:
// function() { f(); let x = 1; function f() { x = 2; } }
//
// Check that we always have valid source position.
ASSERT(var->initializer_position() != RelocInfo::kNoPosition);
ASSERT(proxy->position() != RelocInfo::kNoPosition);
bool skip_init_check =
var->mode() != CONST &&
var->scope()->DeclarationScope() == scope()->DeclarationScope() &&
var->initializer_position() < proxy->position();
if (!skip_init_check) {
// Let and const need a read barrier.
GetVar(r0, var);
__ CompareRoot(r0, Heap::kTheHoleValueRootIndex);
if (var->mode() == LET || var->mode() == CONST_HARMONY) {
// Throw a reference error when using an uninitialized let/const
// binding in harmony mode.
Label done;
__ b(ne, &done);
__ mov(r0, Operand(var->name()));
__ push(r0);
__ CallRuntime(Runtime::kThrowReferenceError, 1);
__ bind(&done);
} else {
// Uninitalized const bindings outside of harmony mode are unholed.
ASSERT(var->mode() == CONST);
__ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq);
}
context()->Plug(r0);
break;
} }
context()->Plug(r0);
} }
context()->Plug(var);
break; break;
} }

View File

@ -1234,27 +1234,59 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) {
Comment cmnt(masm_, var->IsContextSlot() Comment cmnt(masm_, var->IsContextSlot()
? "Context variable" ? "Context variable"
: "Stack variable"); : "Stack variable");
if (!var->binding_needs_init()) { if (var->binding_needs_init()) {
context()->Plug(var); // var->scope() may be NULL when the proxy is located in eval code and
} else { // refers to a potential outside binding. Currently those bindings are
// Let and const need a read barrier. // always looked up dynamically, i.e. in that case
Label done; // var->location() == LOOKUP.
GetVar(eax, var); // always holds.
__ cmp(eax, isolate()->factory()->the_hole_value()); ASSERT(var->scope() != NULL);
__ j(not_equal, &done, Label::kNear);
if (var->mode() == LET || var->mode() == CONST_HARMONY) { // Check if the binding really needs an initialization check. The check
// Throw a reference error when using an uninitialized let/const // can be skipped in the following situation: we have a LET or CONST
// binding in harmony mode. // binding in harmony mode, both the Variable and the VariableProxy have
__ push(Immediate(var->name())); // the same declaration scope (i.e. they are both in global code, in the
__ CallRuntime(Runtime::kThrowReferenceError, 1); // same function or in the same eval code) and the VariableProxy is in
} else { // the source physically located after the initializer of the variable.
// Uninitalized const bindings outside of harmony mode are unholed. //
ASSERT(var->mode() == CONST); // We cannot skip any initialization checks for CONST in non-harmony
__ mov(eax, isolate()->factory()->undefined_value()); // mode because const variables may be declared but never initialized:
// if (false) { const x; }; var y = x;
//
// The condition on the declaration scopes is a conservative check for
// nested functions that access a binding and are called before the
// binding is initialized:
// function() { f(); let x = 1; function f() { x = 2; } }
//
// Check that we always have valid source position.
ASSERT(var->initializer_position() != RelocInfo::kNoPosition);
ASSERT(proxy->position() != RelocInfo::kNoPosition);
bool skip_init_check =
var->mode() != CONST &&
var->scope()->DeclarationScope() == scope()->DeclarationScope() &&
var->initializer_position() < proxy->position();
if (!skip_init_check) {
// Let and const need a read barrier.
Label done;
GetVar(eax, var);
__ cmp(eax, isolate()->factory()->the_hole_value());
__ j(not_equal, &done, Label::kNear);
if (var->mode() == LET || var->mode() == CONST_HARMONY) {
// Throw a reference error when using an uninitialized let/const
// binding in harmony mode.
__ push(Immediate(var->name()));
__ CallRuntime(Runtime::kThrowReferenceError, 1);
} else {
// Uninitalized const bindings outside of harmony mode are unholed.
ASSERT(var->mode() == CONST);
__ mov(eax, isolate()->factory()->undefined_value());
}
__ bind(&done);
context()->Plug(eax);
break;
} }
__ bind(&done);
context()->Plug(eax);
} }
context()->Plug(var);
break; break;
} }

View File

@ -1773,7 +1773,7 @@ Block* Parser::ParseVariableDeclarations(
// For let/const declarations in harmony mode, we can also immediately // For let/const declarations in harmony mode, we can also immediately
// pre-resolve the proxy because it resides in the same scope as the // pre-resolve the proxy because it resides in the same scope as the
// declaration. // declaration.
Declare(name, mode, NULL, mode != VAR, CHECK_OK); VariableProxy* proxy = Declare(name, mode, NULL, mode != VAR, CHECK_OK);
nvars++; nvars++;
if (declaration_scope->num_var_or_const() > kMaxNumFunctionLocals) { if (declaration_scope->num_var_or_const() > kMaxNumFunctionLocals) {
ReportMessageAt(scanner().location(), "too_many_variables", ReportMessageAt(scanner().location(), "too_many_variables",
@ -1828,6 +1828,11 @@ Block* Parser::ParseVariableDeclarations(
if (decl_props != NULL) *decl_props = kHasInitializers; if (decl_props != NULL) *decl_props = kHasInitializers;
} }
// Record the end position of the initializer.
if (proxy->var() != NULL) {
proxy->var()->set_initializer_position(scanner().location().end_pos);
}
// Make sure that 'const x' and 'let x' initialize 'x' to undefined. // Make sure that 'const x' and 'let x' initialize 'x' to undefined.
if (value == NULL && needs_init) { if (value == NULL && needs_init) {
value = GetLiteralUndefined(); value = GetLiteralUndefined();

View File

@ -66,6 +66,7 @@ Variable::Variable(Scope* scope,
kind_(kind), kind_(kind),
location_(UNALLOCATED), location_(UNALLOCATED),
index_(-1), index_(-1),
initializer_position_(RelocInfo::kNoPosition),
local_if_not_shadowed_(NULL), local_if_not_shadowed_(NULL),
is_valid_LHS_(is_valid_LHS), is_valid_LHS_(is_valid_LHS),
is_accessed_from_inner_scope_(false), is_accessed_from_inner_scope_(false),

View File

@ -103,6 +103,9 @@ class Variable: public ZoneObject {
bool is_used() { return is_used_; } bool is_used() { return is_used_; }
void set_is_used(bool flag) { is_used_ = flag; } void set_is_used(bool flag) { is_used_ = flag; }
int initializer_position() { return initializer_position_; }
void set_initializer_position(int pos) { initializer_position_ = pos; }
bool IsVariable(Handle<String> n) const { bool IsVariable(Handle<String> n) const {
return !is_this() && name().is_identical_to(n); return !is_this() && name().is_identical_to(n);
} }
@ -163,6 +166,7 @@ class Variable: public ZoneObject {
Kind kind_; Kind kind_;
Location location_; Location location_;
int index_; int index_;
int initializer_position_;
// If this field is set, this variable references the stored locally bound // If this field is set, this variable references the stored locally bound
// variable, but it might be shadowed by variable bindings introduced by // variable, but it might be shadowed by variable bindings introduced by

View File

@ -1240,27 +1240,59 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) {
case Variable::LOCAL: case Variable::LOCAL:
case Variable::CONTEXT: { case Variable::CONTEXT: {
Comment cmnt(masm_, var->IsContextSlot() ? "Context slot" : "Stack slot"); Comment cmnt(masm_, var->IsContextSlot() ? "Context slot" : "Stack slot");
if (!var->binding_needs_init()) { if (var->binding_needs_init()) {
context()->Plug(var); // var->scope() may be NULL when the proxy is located in eval code and
} else { // refers to a potential outside binding. Currently those bindings are
// Let and const need a read barrier. // always looked up dynamically, i.e. in that case
Label done; // var->location() == LOOKUP.
GetVar(rax, var); // always holds.
__ CompareRoot(rax, Heap::kTheHoleValueRootIndex); ASSERT(var->scope() != NULL);
__ j(not_equal, &done, Label::kNear);
if (var->mode() == LET || var->mode() == CONST_HARMONY) { // Check if the binding really needs an initialization check. The check
// Throw a reference error when using an uninitialized let/const // can be skipped in the following situation: we have a LET or CONST
// binding in harmony mode. // binding in harmony mode, both the Variable and the VariableProxy have
__ Push(var->name()); // the same declaration scope (i.e. they are both in global code, in the
__ CallRuntime(Runtime::kThrowReferenceError, 1); // same function or in the same eval code) and the VariableProxy is in
} else { // the source physically located after the initializer of the variable.
// Uninitalized const bindings outside of harmony mode are unholed. //
ASSERT(var->mode() == CONST); // We cannot skip any initialization checks for CONST in non-harmony
__ LoadRoot(rax, Heap::kUndefinedValueRootIndex); // mode because const variables may be declared but never initialized:
// if (false) { const x; }; var y = x;
//
// The condition on the declaration scopes is a conservative check for
// nested functions that access a binding and are called before the
// binding is initialized:
// function() { f(); let x = 1; function f() { x = 2; } }
//
// Check that we always have valid source position.
ASSERT(var->initializer_position() != RelocInfo::kNoPosition);
ASSERT(proxy->position() != RelocInfo::kNoPosition);
bool skip_init_check =
var->mode() != CONST &&
var->scope()->DeclarationScope() == scope()->DeclarationScope() &&
var->initializer_position() < proxy->position();
if (!skip_init_check) {
// Let and const need a read barrier.
Label done;
GetVar(rax, var);
__ CompareRoot(rax, Heap::kTheHoleValueRootIndex);
__ j(not_equal, &done, Label::kNear);
if (var->mode() == LET || var->mode() == CONST_HARMONY) {
// Throw a reference error when using an uninitialized let/const
// binding in harmony mode.
__ Push(var->name());
__ CallRuntime(Runtime::kThrowReferenceError, 1);
} else {
// Uninitalized const bindings outside of harmony mode are unholed.
ASSERT(var->mode() == CONST);
__ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
}
__ bind(&done);
context()->Plug(rax);
break;
} }
__ bind(&done);
context()->Plug(rax);
} }
context()->Plug(var);
break; break;
} }