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:
keuchel@chromium.org 2011-12-06 09:41:06 +00:00
parent 087737cbcd
commit 8b34f4630b
2 changed files with 144 additions and 15 deletions

View File

@ -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();

View File

@ -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;