From 8b34f4630b9e6415ca95cfcb66c8e19cb3a27538 Mon Sep 17 00:00:00 2001 From: "keuchel@chromium.org" Date: Tue, 6 Dec 2011 09:41:06 +0000 Subject: [PATCH] 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 --- src/hydrogen.cc | 49 ++++++--- test/mjsunit/harmony/block-let-crankshaft.js | 110 ++++++++++++++++++- 2 files changed, 144 insertions(+), 15 deletions(-) diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 2d9d58a235..ec272f5324 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -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 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(); diff --git a/test/mjsunit/harmony/block-let-crankshaft.js b/test/mjsunit/harmony/block-let-crankshaft.js index ba5bc0d4a5..51112082d6 100644 --- a/test/mjsunit/harmony/block-let-crankshaft.js +++ b/test/mjsunit/harmony/block-let-crankshaft.js @@ -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;