Hydrogen support for stack local harmony bindings in function scope.
authorkeuchel@chromium.org <keuchel@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 6 Dec 2011 09:41:06 +0000 (09:41 +0000)
committerkeuchel@chromium.org <keuchel@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 6 Dec 2011 09:41:06 +0000 (09:41 +0000)
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
test/mjsunit/harmony/block-let-crankshaft.js

index 2d9d58a2352ee085db34da87b5edcf33bca010ea..ec272f532420d54cd13c3286a7ca3bb472db8595 100644 (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();
index ba5bc0d4a573cf0ce14de953678c222c54fd3cd1..51112082d629c8001f01b722c074e65ef029de45 100644 (file)
 // 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;