Hydrogen support for stack local harmony bindings in function scope.
This is the first CL in a series that add support for the harmony scoping features to crankshaft. This CL specifically adds support for stack allocated 'let' and 'const' declared variables in function scopes. TEST=mjsunit/harmony/block-let-crankshaft.js Review URL: http://codereview.chromium.org/8806012 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10171 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
087737cbcd
commit
8b34f4630b
@ -3228,11 +3228,11 @@ void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
|
||||
ASSERT(current_block() != NULL);
|
||||
ASSERT(current_block()->HasPredecessor());
|
||||
Variable* variable = expr->var();
|
||||
if (variable->mode() == LET) {
|
||||
return Bailout("reference to let variable");
|
||||
}
|
||||
switch (variable->location()) {
|
||||
case Variable::UNALLOCATED: {
|
||||
if (variable->mode() == LET || variable->mode() == CONST_HARMONY) {
|
||||
return Bailout("reference to global harmony declared variable");
|
||||
}
|
||||
// Handle known global constants like 'undefined' specially to avoid a
|
||||
// load from a global cell for them.
|
||||
Handle<Object> constant_value =
|
||||
@ -3275,14 +3275,19 @@ void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
|
||||
case Variable::PARAMETER:
|
||||
case Variable::LOCAL: {
|
||||
HValue* value = environment()->Lookup(variable);
|
||||
if (variable->mode() == CONST &&
|
||||
value == graph()->GetConstantHole()) {
|
||||
return Bailout("reference to uninitialized const variable");
|
||||
if (value == graph()->GetConstantHole()) {
|
||||
ASSERT(variable->mode() == CONST ||
|
||||
variable->mode() == CONST_HARMONY ||
|
||||
variable->mode() == LET);
|
||||
return Bailout("reference to uninitialized variable");
|
||||
}
|
||||
return ast_context()->ReturnValue(value);
|
||||
}
|
||||
|
||||
case Variable::CONTEXT: {
|
||||
if (variable->mode() == LET || variable->mode() == CONST_HARMONY) {
|
||||
return Bailout("reference to harmony declared context slot");
|
||||
}
|
||||
if (variable->mode() == CONST) {
|
||||
return Bailout("reference to const context slot");
|
||||
}
|
||||
@ -3963,7 +3968,16 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) {
|
||||
HValue* old_value = environment()->Lookup(var);
|
||||
AddInstruction(new HUseConst(old_value));
|
||||
} else if (var->mode() == LET) {
|
||||
return Bailout("unsupported assignment to let");
|
||||
if (!var->IsStackAllocated()) {
|
||||
return Bailout("assignment to let context slot");
|
||||
}
|
||||
} else if (var->mode() == CONST_HARMONY) {
|
||||
if (expr->op() != Token::INIT_CONST_HARMONY) {
|
||||
return Bailout("non-initializer assignment to const");
|
||||
}
|
||||
if (!var->IsStackAllocated()) {
|
||||
return Bailout("assignment to const context slot");
|
||||
}
|
||||
}
|
||||
|
||||
if (proxy->IsArguments()) return Bailout("assignment to arguments");
|
||||
@ -3980,6 +3994,14 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) {
|
||||
|
||||
case Variable::PARAMETER:
|
||||
case Variable::LOCAL: {
|
||||
// Perform an initialization check for let declared variables
|
||||
// or parameters.
|
||||
if (var->mode() == LET && expr->op() == Token::ASSIGN) {
|
||||
HValue* env_value = environment()->Lookup(var);
|
||||
if (env_value == graph()->GetConstantHole()) {
|
||||
return Bailout("assignment to let variable before initialization");
|
||||
}
|
||||
}
|
||||
// We do not allow the arguments object to occur in a context where it
|
||||
// may escape, but assignments to stack-allocated locals are
|
||||
// permitted.
|
||||
@ -6191,23 +6213,22 @@ void HGraphBuilder::VisitDeclaration(Declaration* decl) {
|
||||
void HGraphBuilder::HandleDeclaration(VariableProxy* proxy,
|
||||
VariableMode mode,
|
||||
FunctionLiteral* function) {
|
||||
if (mode == LET || mode == CONST_HARMONY) {
|
||||
return Bailout("unsupported harmony declaration");
|
||||
}
|
||||
Variable* var = proxy->var();
|
||||
bool binding_needs_init =
|
||||
(mode == CONST || mode == CONST_HARMONY || mode == LET);
|
||||
switch (var->location()) {
|
||||
case Variable::UNALLOCATED:
|
||||
return Bailout("unsupported global declaration");
|
||||
case Variable::PARAMETER:
|
||||
case Variable::LOCAL:
|
||||
case Variable::CONTEXT:
|
||||
if (mode == CONST || function != NULL) {
|
||||
if (binding_needs_init || function != NULL) {
|
||||
HValue* value = NULL;
|
||||
if (mode == CONST) {
|
||||
value = graph()->GetConstantHole();
|
||||
} else {
|
||||
if (function != NULL) {
|
||||
VisitForValue(function);
|
||||
value = Pop();
|
||||
} else {
|
||||
value = graph()->GetConstantHole();
|
||||
}
|
||||
if (var->IsContextSlot()) {
|
||||
HValue* context = environment()->LookupContext();
|
||||
|
@ -30,8 +30,116 @@
|
||||
// TODO(ES6): properly activate extended mode
|
||||
"use strict";
|
||||
|
||||
// Check that the following functions are optimizable.
|
||||
var functions = [ f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12 ];
|
||||
|
||||
for (var i = 0; i < functions.length; ++i) {
|
||||
var func = functions[i];
|
||||
print("Testing:");
|
||||
print(func);
|
||||
for (var j = 0; j < 10; ++j) {
|
||||
func(12);
|
||||
}
|
||||
%OptimizeFunctionOnNextCall(func);
|
||||
func(12);
|
||||
assertTrue(%GetOptimizationStatus(func) != 2);
|
||||
}
|
||||
|
||||
function f1() { }
|
||||
|
||||
function f2(x) { }
|
||||
|
||||
function f3() {
|
||||
let x;
|
||||
}
|
||||
|
||||
function f4() {
|
||||
function foo() {
|
||||
}
|
||||
}
|
||||
|
||||
function f5() {
|
||||
let x = 1;
|
||||
}
|
||||
|
||||
function f6() {
|
||||
const x = 1;
|
||||
}
|
||||
|
||||
function f7(x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
function f8() {
|
||||
let x;
|
||||
return x;
|
||||
}
|
||||
|
||||
function f9() {
|
||||
function x() {
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
function f10(x) {
|
||||
x = 1;
|
||||
}
|
||||
|
||||
function f11() {
|
||||
let x;
|
||||
x = 1;
|
||||
}
|
||||
|
||||
function f12() {
|
||||
function x() {};
|
||||
x = 1;
|
||||
}
|
||||
|
||||
|
||||
// Test that temporal dead zone semantics for function and block scoped
|
||||
// ket bindings are handled by the optimizing compiler.
|
||||
// let bindings are handled by the optimizing compiler.
|
||||
|
||||
function TestFunctionLocal(s) {
|
||||
'use strict';
|
||||
var func = eval("(function baz(){" + s + "; })");
|
||||
print("Testing:");
|
||||
print(func);
|
||||
for (var i = 0; i < 5; ++i) {
|
||||
try {
|
||||
func();
|
||||
assertUnreachable();
|
||||
} catch (e) {
|
||||
assertInstanceof(e, ReferenceError);
|
||||
}
|
||||
}
|
||||
%OptimizeFunctionOnNextCall(func);
|
||||
try {
|
||||
func();
|
||||
assertUnreachable();
|
||||
} catch (e) {
|
||||
assertInstanceof(e, ReferenceError);
|
||||
}
|
||||
}
|
||||
|
||||
function TestAll(s) {
|
||||
TestFunctionLocal(s);
|
||||
}
|
||||
|
||||
// Use before initialization in declaration statement.
|
||||
TestAll('let x = x + 1');
|
||||
TestAll('let x = x += 1');
|
||||
TestAll('let x = x++');
|
||||
TestAll('let x = ++x');
|
||||
TestAll('const x = x + 1');
|
||||
|
||||
// Use before initialization in prior statement.
|
||||
TestAll('x + 1; let x;');
|
||||
TestAll('x = 1; let x;');
|
||||
TestAll('x += 1; let x;');
|
||||
TestAll('++x; let x;');
|
||||
TestAll('x++; let x;');
|
||||
TestAll('let y = x; const x = 1;');
|
||||
|
||||
|
||||
function f(x, b) {
|
||||
let y = (b ? y : x) + 42;
|
||||
|
Loading…
Reference in New Issue
Block a user