Preliminary support for block contexts in hydrogen.
authorulan@chromium.org <ulan@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 5 Jun 2014 07:33:01 +0000 (07:33 +0000)
committerulan@chromium.org <ulan@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 5 Jun 2014 07:33:01 +0000 (07:33 +0000)
Patch from Steven Keuchel <keuchel@chromium.org>

BUG=v8:2198
LOG=N
TEST=mjsunit/harmony/block-let-crankshaft.js
R=rossberg@chromium.org

Review URL: https://codereview.chromium.org/307593002

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@21684 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

21 files changed:
src/arm/lithium-arm.cc
src/arm/lithium-arm.h
src/arm/lithium-codegen-arm.cc
src/arm64/lithium-arm64.cc
src/arm64/lithium-arm64.h
src/arm64/lithium-codegen-arm64.cc
src/ast.h
src/full-codegen.cc
src/hydrogen-instructions.cc
src/hydrogen-instructions.h
src/hydrogen.cc
src/hydrogen.h
src/ia32/lithium-codegen-ia32.cc
src/ia32/lithium-ia32.cc
src/ia32/lithium-ia32.h
src/parser.cc
src/x64/lithium-codegen-x64.cc
src/x64/lithium-x64.cc
src/x64/lithium-x64.h
test/mjsunit/harmony/block-let-crankshaft.js
test/mjsunit/harmony/block-scoping.js

index 79eb197..93dc830 100644 (file)
@@ -2579,4 +2579,20 @@ LInstruction* LChunkBuilder::DoLoadFieldByIndex(HLoadFieldByIndex* instr) {
   return AssignPointerMap(result);
 }
 
+
+LInstruction* LChunkBuilder::DoStoreFrameContext(HStoreFrameContext* instr) {
+  LOperand* context = UseRegisterAtStart(instr->context());
+  return new(zone()) LStoreFrameContext(context);
+}
+
+
+LInstruction* LChunkBuilder::DoAllocateBlockContext(
+    HAllocateBlockContext* instr) {
+  LOperand* context = UseFixed(instr->context(), cp);
+  LOperand* function = UseRegisterAtStart(instr->function());
+  LAllocateBlockContext* result =
+      new(zone()) LAllocateBlockContext(context, function);
+  return MarkAsCall(DefineFixed(result, cp), instr);
+}
+
 } }  // namespace v8::internal
index 2bd07fc..127badc 100644 (file)
@@ -21,6 +21,7 @@ class LCodeGen;
   V(AccessArgumentsAt)                          \
   V(AddI)                                       \
   V(Allocate)                                   \
+  V(AllocateBlockContext)                       \
   V(ApplyArguments)                             \
   V(ArgumentsElements)                          \
   V(ArgumentsLength)                            \
@@ -139,6 +140,7 @@ class LCodeGen;
   V(StackCheck)                                 \
   V(StoreCodeEntry)                             \
   V(StoreContextSlot)                           \
+  V(StoreFrameContext)                          \
   V(StoreGlobalCell)                            \
   V(StoreKeyed)                                 \
   V(StoreKeyedGeneric)                          \
@@ -2669,6 +2671,35 @@ class LLoadFieldByIndex V8_FINAL : public LTemplateInstruction<1, 2, 0> {
 };
 
 
+class LStoreFrameContext: public LTemplateInstruction<0, 1, 0> {
+ public:
+  explicit LStoreFrameContext(LOperand* context) {
+    inputs_[0] = context;
+  }
+
+  LOperand* context() { return inputs_[0]; }
+
+  DECLARE_CONCRETE_INSTRUCTION(StoreFrameContext, "store-frame-context")
+};
+
+
+class LAllocateBlockContext: public LTemplateInstruction<1, 2, 0> {
+ public:
+  LAllocateBlockContext(LOperand* context, LOperand* function) {
+    inputs_[0] = context;
+    inputs_[1] = function;
+  }
+
+  LOperand* context() { return inputs_[0]; }
+  LOperand* function() { return inputs_[1]; }
+
+  Handle<ScopeInfo> scope_info() { return hydrogen()->scope_info(); }
+
+  DECLARE_CONCRETE_INSTRUCTION(AllocateBlockContext, "allocate-block-context")
+  DECLARE_HYDROGEN_ACCESSOR(AllocateBlockContext)
+};
+
+
 class LChunkBuilder;
 class LPlatformChunk V8_FINAL : public LChunk {
  public:
index 245115f..f852f3d 100644 (file)
@@ -5818,6 +5818,21 @@ void LCodeGen::DoLoadFieldByIndex(LLoadFieldByIndex* instr) {
 }
 
 
+void LCodeGen::DoStoreFrameContext(LStoreFrameContext* instr) {
+  Register context = ToRegister(instr->context());
+  __ str(context, MemOperand(fp, StandardFrameConstants::kContextOffset));
+}
+
+
+void LCodeGen::DoAllocateBlockContext(LAllocateBlockContext* instr) {
+  Handle<ScopeInfo> scope_info = instr->scope_info();
+  __ Push(scope_info);
+  __ push(ToRegister(instr->function()));
+  CallRuntime(Runtime::kHiddenPushBlockContext, 2, instr);
+  RecordSafepoint(Safepoint::kNoLazyDeopt);
+}
+
+
 #undef __
 
 } }  // namespace v8::internal
index bba6530..8446edf 100644 (file)
@@ -2706,4 +2706,20 @@ LInstruction* LChunkBuilder::DoWrapReceiver(HWrapReceiver* instr) {
 }
 
 
+LInstruction* LChunkBuilder::DoStoreFrameContext(HStoreFrameContext* instr) {
+  LOperand* context = UseRegisterAtStart(instr->context());
+  return new(zone()) LStoreFrameContext(context);
+}
+
+
+LInstruction* LChunkBuilder::DoAllocateBlockContext(
+    HAllocateBlockContext* instr) {
+  LOperand* context = UseFixed(instr->context(), cp);
+  LOperand* function = UseRegisterAtStart(instr->function());
+  LAllocateBlockContext* result =
+      new(zone()) LAllocateBlockContext(context, function);
+  return MarkAsCall(DefineFixed(result, cp), instr);
+}
+
+
 } }  // namespace v8::internal
index 76a569d..18dd927 100644 (file)
@@ -23,6 +23,7 @@ class LCodeGen;
   V(AddI)                                       \
   V(AddS)                                       \
   V(Allocate)                                   \
+  V(AllocateBlockContext)                       \
   V(ApplyArguments)                             \
   V(ArgumentsElements)                          \
   V(ArgumentsLength)                            \
@@ -148,6 +149,7 @@ class LCodeGen;
   V(StackCheck)                                 \
   V(StoreCodeEntry)                             \
   V(StoreContextSlot)                           \
+  V(StoreFrameContext)                          \
   V(StoreGlobalCell)                            \
   V(StoreKeyedExternal)                         \
   V(StoreKeyedFixed)                            \
@@ -2997,6 +2999,35 @@ class LLoadFieldByIndex V8_FINAL : public LTemplateInstruction<1, 2, 0> {
 };
 
 
+class LStoreFrameContext: public LTemplateInstruction<0, 1, 0> {
+ public:
+  explicit LStoreFrameContext(LOperand* context) {
+    inputs_[0] = context;
+  }
+
+  LOperand* context() { return inputs_[0]; }
+
+  DECLARE_CONCRETE_INSTRUCTION(StoreFrameContext, "store-frame-context")
+};
+
+
+class LAllocateBlockContext: public LTemplateInstruction<1, 2, 0> {
+ public:
+  LAllocateBlockContext(LOperand* context, LOperand* function) {
+    inputs_[0] = context;
+    inputs_[1] = function;
+  }
+
+  LOperand* context() { return inputs_[0]; }
+  LOperand* function() { return inputs_[1]; }
+
+  Handle<ScopeInfo> scope_info() { return hydrogen()->scope_info(); }
+
+  DECLARE_CONCRETE_INSTRUCTION(AllocateBlockContext, "allocate-block-context")
+  DECLARE_HYDROGEN_ACCESSOR(AllocateBlockContext)
+};
+
+
 class LWrapReceiver V8_FINAL : public LTemplateInstruction<1, 2, 0> {
  public:
   LWrapReceiver(LOperand* receiver, LOperand* function) {
index a235188..e5e0d1a 100644 (file)
@@ -6039,4 +6039,21 @@ void LCodeGen::DoLoadFieldByIndex(LLoadFieldByIndex* instr) {
   __ Bind(&done);
 }
 
+
+void LCodeGen::DoStoreFrameContext(LStoreFrameContext* instr) {
+  Register context = ToRegister(instr->context());
+  __ Str(context, MemOperand(fp, StandardFrameConstants::kContextOffset));
+}
+
+
+void LCodeGen::DoAllocateBlockContext(LAllocateBlockContext* instr) {
+  Handle<ScopeInfo> scope_info = instr->scope_info();
+  __ Push(scope_info);
+  __ Push(ToRegister(instr->function()));
+  CallRuntime(Runtime::kHiddenPushBlockContext, 2, instr);
+  RecordSafepoint(Safepoint::kNoLazyDeopt);
+}
+
+
+
 } }  // namespace v8::internal
index 35e3a85..0882b25 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -441,6 +441,8 @@ class Block V8_FINAL : public BreakableStatement {
   ZoneList<Statement*>* statements() { return &statements_; }
   bool is_initializer_block() const { return is_initializer_block_; }
 
+  BailoutId DeclsId() const { return decls_id_; }
+
   virtual bool IsJump() const V8_OVERRIDE {
     return !statements_.is_empty() && statements_.last()->IsJump()
         && labels() == NULL;  // Good enough as an approximation...
@@ -458,12 +460,14 @@ class Block V8_FINAL : public BreakableStatement {
       : BreakableStatement(zone, labels, TARGET_FOR_NAMED_ONLY, pos),
         statements_(capacity, zone),
         is_initializer_block_(is_initializer_block),
+        decls_id_(GetNextId(zone)),
         scope_(NULL) {
   }
 
  private:
   ZoneList<Statement*> statements_;
   bool is_initializer_block_;
+  const BailoutId decls_id_;
   Scope* scope_;
 };
 
index 1a60f33..0c82eb3 100644 (file)
@@ -1052,7 +1052,9 @@ void FullCodeGenerator::VisitBlock(Block* stmt) {
 
   Scope* saved_scope = scope();
   // Push a block context when entering a block with block scoped variables.
-  if (stmt->scope() != NULL) {
+  if (stmt->scope() == NULL) {
+    PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
+  } else {
     scope_ = stmt->scope();
     ASSERT(!scope_->is_module_scope());
     { Comment cmnt(masm_, "[ Extend block context");
@@ -1063,17 +1065,17 @@ void FullCodeGenerator::VisitBlock(Block* stmt) {
       // Replace the context stored in the frame.
       StoreToFrameField(StandardFrameConstants::kContextOffset,
                         context_register());
+      PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
     }
     { Comment cmnt(masm_, "[ Declarations");
       VisitDeclarations(scope_->declarations());
+      PrepareForBailoutForId(stmt->DeclsId(), NO_REGISTERS);
     }
   }
 
-  PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
   VisitStatements(stmt->statements());
   scope_ = saved_scope;
   __ bind(nested_block.break_label());
-  PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
 
   // Pop block context if necessary.
   if (stmt->scope() != NULL) {
@@ -1082,6 +1084,7 @@ void FullCodeGenerator::VisitBlock(Block* stmt) {
     StoreToFrameField(StandardFrameConstants::kContextOffset,
                       context_register());
   }
+  PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
 }
 
 
index 44a6dde..96cd016 100644 (file)
@@ -846,6 +846,7 @@ bool HInstruction::CanDeoptimize() {
     case HValue::kReturn:
     case HValue::kSeqStringGetChar:
     case HValue::kStoreCodeEntry:
+    case HValue::kStoreFrameContext:
     case HValue::kStoreKeyed:
     case HValue::kStoreNamedField:
     case HValue::kStoreNamedGeneric:
@@ -858,6 +859,7 @@ bool HInstruction::CanDeoptimize() {
       return false;
 
     case HValue::kAdd:
+    case HValue::kAllocateBlockContext:
     case HValue::kApplyArguments:
     case HValue::kBitwise:
     case HValue::kBoundsCheck:
@@ -1136,6 +1138,13 @@ void HAccessArgumentsAt::PrintDataTo(StringStream* stream) {
 }
 
 
+void HAllocateBlockContext::PrintDataTo(StringStream* stream) {
+  context()->PrintNameTo(stream);
+  stream->Add(" ");
+  function()->PrintNameTo(stream);
+}
+
+
 void HControlInstruction::PrintDataTo(StringStream* stream) {
   stream->Add(" goto (");
   bool first_block = true;
index 9e6f0f0..d1f2372 100644 (file)
@@ -46,6 +46,7 @@ class LChunkBuilder;
   V(AbnormalExit)                              \
   V(AccessArgumentsAt)                         \
   V(Add)                                       \
+  V(AllocateBlockContext)                      \
   V(Allocate)                                  \
   V(ApplyArguments)                            \
   V(ArgumentsElements)                         \
@@ -140,6 +141,7 @@ class LChunkBuilder;
   V(StackCheck)                                \
   V(StoreCodeEntry)                            \
   V(StoreContextSlot)                          \
+  V(StoreFrameContext)                         \
   V(StoreGlobalCell)                           \
   V(StoreKeyed)                                \
   V(StoreKeyedGeneric)                         \
@@ -5798,20 +5800,8 @@ class HLoadContextSlot V8_FINAL : public HUnaryOperation {
     kCheckReturnUndefined
   };
 
-  HLoadContextSlot(HValue* context, Variable* var)
-      : HUnaryOperation(context), slot_index_(var->index()) {
-    ASSERT(var->IsContextSlot());
-    switch (var->mode()) {
-      case LET:
-      case CONST:
-        mode_ = kCheckDeoptimize;
-        break;
-      case CONST_LEGACY:
-        mode_ = kCheckReturnUndefined;
-        break;
-      default:
-        mode_ = kNoCheck;
-    }
+  HLoadContextSlot(HValue* context, int slot_index, Mode mode)
+      : HUnaryOperation(context), slot_index_(slot_index), mode_(mode) {
     set_representation(Representation::Tagged());
     SetFlag(kUseGVN);
     SetDependsOnFlag(kContextSlots);
@@ -7717,6 +7707,57 @@ class HLoadFieldByIndex V8_FINAL : public HTemplateInstruction<2> {
 };
 
 
+class HStoreFrameContext: public HUnaryOperation {
+ public:
+  DECLARE_INSTRUCTION_FACTORY_P1(HStoreFrameContext, HValue*);
+
+  HValue* context() { return OperandAt(0); }
+
+  virtual Representation RequiredInputRepresentation(int index) {
+    return Representation::Tagged();
+  }
+
+  DECLARE_CONCRETE_INSTRUCTION(StoreFrameContext)
+ private:
+  explicit HStoreFrameContext(HValue* context)
+      : HUnaryOperation(context) {
+    set_representation(Representation::Tagged());
+    SetChangesFlag(kContextSlots);
+  }
+};
+
+
+class HAllocateBlockContext: public HTemplateInstruction<2> {
+ public:
+  DECLARE_INSTRUCTION_FACTORY_P3(HAllocateBlockContext, HValue*,
+                                 HValue*, Handle<ScopeInfo>);
+  HValue* context() { return OperandAt(0); }
+  HValue* function() { return OperandAt(1); }
+  Handle<ScopeInfo> scope_info() { return scope_info_; }
+
+  virtual Representation RequiredInputRepresentation(int index) {
+    return Representation::Tagged();
+  }
+
+  virtual void PrintDataTo(StringStream* stream);
+
+  DECLARE_CONCRETE_INSTRUCTION(AllocateBlockContext)
+
+ private:
+  HAllocateBlockContext(HValue* context,
+                        HValue* function,
+                        Handle<ScopeInfo> scope_info)
+      : scope_info_(scope_info) {
+    SetOperandAt(0, context);
+    SetOperandAt(1, function);
+    set_representation(Representation::Tagged());
+  }
+
+  Handle<ScopeInfo> scope_info_;
+};
+
+
+
 #undef DECLARE_INSTRUCTION
 #undef DECLARE_CONCRETE_INSTRUCTION
 
index 36d62d2..a3f4b0b 100644 (file)
@@ -4247,13 +4247,52 @@ void HOptimizedGraphBuilder::VisitBlock(Block* stmt) {
   ASSERT(!HasStackOverflow());
   ASSERT(current_block() != NULL);
   ASSERT(current_block()->HasPredecessor());
-  if (stmt->scope() != NULL) {
-    return Bailout(kScopedBlock);
-  }
-  BreakAndContinueInfo break_info(stmt);
+
+  Scope* outer_scope = scope();
+  Scope* scope = stmt->scope();
+  BreakAndContinueInfo break_info(stmt, outer_scope);
+
   { BreakAndContinueScope push(&break_info, this);
+    if (scope != NULL) {
+      // Load the function object.
+      Scope* declaration_scope = scope->DeclarationScope();
+      HInstruction* function;
+      HValue* outer_context = environment()->context();
+      if (declaration_scope->is_global_scope() ||
+          declaration_scope->is_eval_scope()) {
+        function = new(zone()) HLoadContextSlot(
+            outer_context, Context::CLOSURE_INDEX, HLoadContextSlot::kNoCheck);
+      } else {
+        function = New<HThisFunction>();
+      }
+      AddInstruction(function);
+      // Allocate a block context and store it to the stack frame.
+      HInstruction* inner_context = Add<HAllocateBlockContext>(
+          outer_context, function, scope->GetScopeInfo());
+      HInstruction* instr = Add<HStoreFrameContext>(inner_context);
+      if (instr->HasObservableSideEffects()) {
+        AddSimulate(stmt->EntryId(), REMOVABLE_SIMULATE);
+      }
+      set_scope(scope);
+      environment()->BindContext(inner_context);
+      VisitDeclarations(scope->declarations());
+      AddSimulate(stmt->DeclsId(), REMOVABLE_SIMULATE);
+    }
     CHECK_BAILOUT(VisitStatements(stmt->statements()));
   }
+  set_scope(outer_scope);
+  if (scope != NULL && current_block() != NULL) {
+    HValue* inner_context = environment()->context();
+    HValue* outer_context = Add<HLoadNamedField>(
+        inner_context, static_cast<HValue*>(NULL),
+        HObjectAccess::ForContextSlot(Context::PREVIOUS_INDEX));
+
+    HInstruction* instr = Add<HStoreFrameContext>(outer_context);
+    if (instr->HasObservableSideEffects()) {
+      AddSimulate(stmt->ExitId(), REMOVABLE_SIMULATE);
+    }
+    environment()->BindContext(outer_context);
+  }
   HBasicBlock* break_block = break_info.break_block();
   if (break_block != NULL) {
     if (current_block() != NULL) Goto(break_block);
@@ -4321,6 +4360,7 @@ void HOptimizedGraphBuilder::VisitIfStatement(IfStatement* stmt) {
 HBasicBlock* HOptimizedGraphBuilder::BreakAndContinueScope::Get(
     BreakableStatement* stmt,
     BreakType type,
+    Scope** scope,
     int* drop_extra) {
   *drop_extra = 0;
   BreakAndContinueScope* current = this;
@@ -4329,6 +4369,7 @@ HBasicBlock* HOptimizedGraphBuilder::BreakAndContinueScope::Get(
     current = current->next();
   }
   ASSERT(current != NULL);  // Always found (unless stack is malformed).
+  *scope = current->info()->scope();
 
   if (type == BREAK) {
     *drop_extra += current->info()->drop_extra();
@@ -4362,10 +4403,29 @@ void HOptimizedGraphBuilder::VisitContinueStatement(
   ASSERT(!HasStackOverflow());
   ASSERT(current_block() != NULL);
   ASSERT(current_block()->HasPredecessor());
+  Scope* outer_scope = NULL;
+  Scope* inner_scope = scope();
   int drop_extra = 0;
   HBasicBlock* continue_block = break_scope()->Get(
-      stmt->target(), BreakAndContinueScope::CONTINUE, &drop_extra);
+      stmt->target(), BreakAndContinueScope::CONTINUE,
+      &outer_scope, &drop_extra);
+  HValue* context = environment()->context();
   Drop(drop_extra);
+  int context_pop_count = inner_scope->ContextChainLength(outer_scope);
+  if (context_pop_count > 0) {
+    while (context_pop_count-- > 0) {
+      HInstruction* context_instruction = Add<HLoadNamedField>(
+          context, static_cast<HValue*>(NULL),
+          HObjectAccess::ForContextSlot(Context::PREVIOUS_INDEX));
+      context = context_instruction;
+    }
+    HInstruction* instr = Add<HStoreFrameContext>(context);
+    if (instr->HasObservableSideEffects()) {
+      AddSimulate(stmt->target()->EntryId(), REMOVABLE_SIMULATE);
+    }
+    environment()->BindContext(context);
+  }
+
   Goto(continue_block);
   set_current_block(NULL);
 }
@@ -4375,10 +4435,28 @@ void HOptimizedGraphBuilder::VisitBreakStatement(BreakStatement* stmt) {
   ASSERT(!HasStackOverflow());
   ASSERT(current_block() != NULL);
   ASSERT(current_block()->HasPredecessor());
+  Scope* outer_scope = NULL;
+  Scope* inner_scope = scope();
   int drop_extra = 0;
   HBasicBlock* break_block = break_scope()->Get(
-      stmt->target(), BreakAndContinueScope::BREAK, &drop_extra);
+      stmt->target(), BreakAndContinueScope::BREAK,
+      &outer_scope, &drop_extra);
+  HValue* context = environment()->context();
   Drop(drop_extra);
+  int context_pop_count = inner_scope->ContextChainLength(outer_scope);
+  if (context_pop_count > 0) {
+    while (context_pop_count-- > 0) {
+      HInstruction* context_instruction = Add<HLoadNamedField>(
+          context, static_cast<HValue*>(NULL),
+          HObjectAccess::ForContextSlot(Context::PREVIOUS_INDEX));
+      context = context_instruction;
+    }
+    HInstruction* instr = Add<HStoreFrameContext>(context);
+    if (instr->HasObservableSideEffects()) {
+      AddSimulate(stmt->target()->ExitId(), REMOVABLE_SIMULATE);
+    }
+    environment()->BindContext(context);
+  }
   Goto(break_block);
   set_current_block(NULL);
 }
@@ -4533,7 +4611,7 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
   // translating the clause bodies.
   HBasicBlock* fall_through_block = NULL;
 
-  BreakAndContinueInfo break_info(stmt);
+  BreakAndContinueInfo break_info(stmt, scope());
   { BreakAndContinueScope push(&break_info, this);
     for (int i = 0; i < clause_count; ++i) {
       CaseClause* clause = clauses->at(i);
@@ -4580,9 +4658,7 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
 
 
 void HOptimizedGraphBuilder::VisitLoopBody(IterationStatement* stmt,
-                                           HBasicBlock* loop_entry,
-                                           BreakAndContinueInfo* break_info) {
-  BreakAndContinueScope push(break_info, this);
+                                           HBasicBlock* loop_entry) {
   Add<HSimulate>(stmt->StackCheckId());
   HStackCheck* stack_check =
       HStackCheck::cast(Add<HStackCheck>(HStackCheck::kBackwardsBranch));
@@ -4599,8 +4675,11 @@ void HOptimizedGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) {
   ASSERT(current_block() != NULL);
   HBasicBlock* loop_entry = BuildLoopEntry(stmt);
 
-  BreakAndContinueInfo break_info(stmt);
-  CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
+  BreakAndContinueInfo break_info(stmt, scope());
+  {
+    BreakAndContinueScope push(&break_info, this);
+    CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry));
+  }
   HBasicBlock* body_exit =
       JoinContinue(stmt, current_block(), break_info.continue_block());
   HBasicBlock* loop_successor = NULL;
@@ -4661,9 +4740,10 @@ void HOptimizedGraphBuilder::VisitWhileStatement(WhileStatement* stmt) {
     }
   }
 
-  BreakAndContinueInfo break_info(stmt);
+  BreakAndContinueInfo break_info(stmt, scope());
   if (current_block() != NULL) {
-    CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
+    BreakAndContinueScope push(&break_info, this);
+    CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry));
   }
   HBasicBlock* body_exit =
       JoinContinue(stmt, current_block(), break_info.continue_block());
@@ -4702,9 +4782,10 @@ void HOptimizedGraphBuilder::VisitForStatement(ForStatement* stmt) {
     }
   }
 
-  BreakAndContinueInfo break_info(stmt);
+  BreakAndContinueInfo break_info(stmt, scope());
   if (current_block() != NULL) {
-    CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
+    BreakAndContinueScope push(&break_info, this);
+    CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry));
   }
   HBasicBlock* body_exit =
       JoinContinue(stmt, current_block(), break_info.continue_block());
@@ -4803,8 +4884,11 @@ void HOptimizedGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
 
   Bind(each_var, key);
 
-  BreakAndContinueInfo break_info(stmt, 5);
-  CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
+  BreakAndContinueInfo break_info(stmt, scope(), 5);
+  {
+    BreakAndContinueScope push(&break_info, this);
+    CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry));
+  }
 
   HBasicBlock* body_exit =
       JoinContinue(stmt, current_block(), break_info.continue_block());
@@ -4949,7 +5033,7 @@ HOptimizedGraphBuilder::GlobalPropertyAccess
 HValue* HOptimizedGraphBuilder::BuildContextChainWalk(Variable* var) {
   ASSERT(var->IsContextSlot());
   HValue* context = environment()->context();
-  int length = current_info()->scope()->ContextChainLength(var->scope());
+  int length = scope()->ContextChainLength(var->scope());
   while (length-- > 0) {
     context = Add<HLoadNamedField>(
         context, static_cast<HValue*>(NULL),
@@ -5033,7 +5117,21 @@ void HOptimizedGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
 
     case Variable::CONTEXT: {
       HValue* context = BuildContextChainWalk(variable);
-      HLoadContextSlot* instr = new(zone()) HLoadContextSlot(context, variable);
+      HLoadContextSlot::Mode mode;
+      switch (variable->mode()) {
+        case LET:
+        case CONST:
+          mode = HLoadContextSlot::kCheckDeoptimize;
+          break;
+        case CONST_LEGACY:
+          mode = HLoadContextSlot::kCheckReturnUndefined;
+          break;
+        default:
+          mode = HLoadContextSlot::kNoCheck;
+          break;
+      }
+      HLoadContextSlot* instr =
+          new(zone()) HLoadContextSlot(context, variable->index(), mode);
       return ast_context()->ReturnInstruction(instr, expr->id());
     }
 
@@ -7462,7 +7560,8 @@ bool HOptimizedGraphBuilder::TryInline(Handle<JSFunction> target,
   Add<HSimulate>(BailoutId::None());
 
   current_block()->UpdateEnvironment(inner_env);
-
+  Scope* saved_scope = scope();
+  set_scope(target_info.scope());
   HEnterInlined* enter_inlined =
       Add<HEnterInlined>(return_id, target, arguments_count, function,
                          function_state()->inlining_kind(),
@@ -7472,6 +7571,7 @@ bool HOptimizedGraphBuilder::TryInline(Handle<JSFunction> target,
 
   VisitDeclarations(target_info.scope()->declarations());
   VisitStatements(function->body());
+  set_scope(saved_scope);
   if (HasStackOverflow()) {
     // Bail out if the inline function did, as we cannot residualize a call
     // instead.
@@ -11432,7 +11532,9 @@ HEnvironment::HEnvironment(HEnvironment* outer,
       push_count_(0),
       ast_id_(BailoutId::None()),
       zone_(zone) {
-  Initialize(scope->num_parameters() + 1, scope->num_stack_slots(), 0);
+  Scope* declaration_scope = scope->DeclarationScope();
+  Initialize(declaration_scope->num_parameters() + 1,
+             declaration_scope->num_stack_slots(), 0);
 }
 
 
index acf1d15..818569b 100644 (file)
@@ -1042,10 +1042,14 @@ class HGraphBuilder {
       : info_(info),
         graph_(NULL),
         current_block_(NULL),
+        scope_(info->scope()),
         position_(HSourcePosition::Unknown()),
         start_position_(0) {}
   virtual ~HGraphBuilder() {}
 
+  Scope* scope() const { return scope_; }
+  void set_scope(Scope* scope) { scope_ = scope; }
+
   HBasicBlock* current_block() const { return current_block_; }
   void set_current_block(HBasicBlock* block) { current_block_ = block; }
   HEnvironment* environment() const {
@@ -1870,6 +1874,7 @@ class HGraphBuilder {
   CompilationInfo* info_;
   HGraph* graph_;
   HBasicBlock* current_block_;
+  Scope* scope_;
   HSourcePosition position_;
   int start_position_;
 };
@@ -1997,10 +2002,12 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
   class BreakAndContinueInfo V8_FINAL BASE_EMBEDDED {
    public:
     explicit BreakAndContinueInfo(BreakableStatement* target,
+                                  Scope* scope,
                                   int drop_extra = 0)
         : target_(target),
           break_block_(NULL),
           continue_block_(NULL),
+          scope_(scope),
           drop_extra_(drop_extra) {
     }
 
@@ -2009,12 +2016,14 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
     void set_break_block(HBasicBlock* block) { break_block_ = block; }
     HBasicBlock* continue_block() { return continue_block_; }
     void set_continue_block(HBasicBlock* block) { continue_block_ = block; }
+    Scope* scope() { return scope_; }
     int drop_extra() { return drop_extra_; }
 
    private:
     BreakableStatement* target_;
     HBasicBlock* break_block_;
     HBasicBlock* continue_block_;
+    Scope* scope_;
     int drop_extra_;
   };
 
@@ -2036,7 +2045,8 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
 
     // Search the break stack for a break or continue target.
     enum BreakType { BREAK, CONTINUE };
-    HBasicBlock* Get(BreakableStatement* stmt, BreakType type, int* drop_extra);
+    HBasicBlock* Get(BreakableStatement* stmt, BreakType type,
+                     Scope** scope, int* drop_extra);
 
    private:
     BreakAndContinueInfo* info_;
@@ -2146,8 +2156,7 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
 
   bool PreProcessOsrEntry(IterationStatement* statement);
   void VisitLoopBody(IterationStatement* stmt,
-                     HBasicBlock* loop_entry,
-                     BreakAndContinueInfo* break_info);
+                     HBasicBlock* loop_entry);
 
   // Create a back edge in the flow graph.  body_exit is the predecessor
   // block and loop_entry is the successor block.  loop_successor is the
index 543d10e..ee008a0 100644 (file)
@@ -5644,6 +5644,21 @@ void LCodeGen::DoLoadFieldByIndex(LLoadFieldByIndex* instr) {
 }
 
 
+void LCodeGen::DoStoreFrameContext(LStoreFrameContext* instr) {
+  Register context = ToRegister(instr->context());
+  __ mov(Operand(ebp, StandardFrameConstants::kContextOffset), context);
+}
+
+
+void LCodeGen::DoAllocateBlockContext(LAllocateBlockContext* instr) {
+  Handle<ScopeInfo> scope_info = instr->scope_info();
+  __ Push(scope_info);
+  __ push(ToRegister(instr->function()));
+  CallRuntime(Runtime::kHiddenPushBlockContext, 2, instr);
+  RecordSafepoint(Safepoint::kNoLazyDeopt);
+}
+
+
 #undef __
 
 } }  // namespace v8::internal
index 3cddd51..44b4ea5 100644 (file)
@@ -2654,6 +2654,22 @@ LInstruction* LChunkBuilder::DoLoadFieldByIndex(HLoadFieldByIndex* instr) {
 }
 
 
+LInstruction* LChunkBuilder::DoStoreFrameContext(HStoreFrameContext* instr) {
+  LOperand* context = UseRegisterAtStart(instr->context());
+  return new(zone()) LStoreFrameContext(context);
+}
+
+
+LInstruction* LChunkBuilder::DoAllocateBlockContext(
+    HAllocateBlockContext* instr) {
+  LOperand* context = UseFixed(instr->context(), esi);
+  LOperand* function = UseRegisterAtStart(instr->function());
+  LAllocateBlockContext* result =
+      new(zone()) LAllocateBlockContext(context, function);
+  return MarkAsCall(DefineFixed(result, esi), instr);
+}
+
+
 } }  // namespace v8::internal
 
 #endif  // V8_TARGET_ARCH_IA32
index e92922b..56b53e0 100644 (file)
@@ -20,6 +20,7 @@ class LCodeGen;
 #define LITHIUM_CONCRETE_INSTRUCTION_LIST(V)    \
   V(AccessArgumentsAt)                          \
   V(AddI)                                       \
+  V(AllocateBlockContext)                       \
   V(Allocate)                                   \
   V(ApplyArguments)                             \
   V(ArgumentsElements)                          \
@@ -137,6 +138,7 @@ class LCodeGen;
   V(StackCheck)                                 \
   V(StoreCodeEntry)                             \
   V(StoreContextSlot)                           \
+  V(StoreFrameContext)                          \
   V(StoreGlobalCell)                            \
   V(StoreKeyed)                                 \
   V(StoreKeyedGeneric)                          \
@@ -2649,6 +2651,35 @@ class LLoadFieldByIndex V8_FINAL : public LTemplateInstruction<1, 2, 0> {
 };
 
 
+class LStoreFrameContext: public LTemplateInstruction<0, 1, 0> {
+ public:
+  explicit LStoreFrameContext(LOperand* context) {
+    inputs_[0] = context;
+  }
+
+  LOperand* context() { return inputs_[0]; }
+
+  DECLARE_CONCRETE_INSTRUCTION(StoreFrameContext, "store-frame-context")
+};
+
+
+class LAllocateBlockContext: public LTemplateInstruction<1, 2, 0> {
+ public:
+  LAllocateBlockContext(LOperand* context, LOperand* function) {
+    inputs_[0] = context;
+    inputs_[1] = function;
+  }
+
+  LOperand* context() { return inputs_[0]; }
+  LOperand* function() { return inputs_[1]; }
+
+  Handle<ScopeInfo> scope_info() { return hydrogen()->scope_info(); }
+
+  DECLARE_CONCRETE_INSTRUCTION(AllocateBlockContext, "allocate-block-context")
+  DECLARE_HYDROGEN_ACCESSOR(AllocateBlockContext)
+};
+
+
 class LChunkBuilder;
 class LPlatformChunk V8_FINAL : public LChunk {
  public:
index 369a451..f40d798 100644 (file)
@@ -2948,7 +2948,7 @@ Statement* Parser::DesugarLetBindingsInForStatement(
       Expression* const1 = factory()->NewLiteral(smi1, RelocInfo::kNoPosition);
       VariableProxy* flag_proxy = factory()->NewVariableProxy(flag);
       compare = factory()->NewCompareOperation(
-          Token::EQ, flag_proxy, const1, RelocInfo::kNoPosition);
+          Token::EQ, flag_proxy, const1, pos);
     }
     Statement* clear_flag = NULL;
     // Make statement: flag = 0.
@@ -2971,7 +2971,7 @@ Statement* Parser::DesugarLetBindingsInForStatement(
     BreakableStatement* t = LookupBreakTarget(Handle<String>(), CHECK_OK);
     Statement* stop = factory()->NewBreakStatement(t, RelocInfo::kNoPosition);
     Statement* if_not_cond_break = factory()->NewIfStatement(
-        cond, empty, stop, RelocInfo::kNoPosition);
+        cond, empty, stop, cond->position());
     inner_block->AddStatement(if_not_cond_break, zone());
   }
 
index 029070a..4ebb33b 100644 (file)
@@ -5685,6 +5685,21 @@ void LCodeGen::DoLoadFieldByIndex(LLoadFieldByIndex* instr) {
 }
 
 
+void LCodeGen::DoStoreFrameContext(LStoreFrameContext* instr) {
+  Register context = ToRegister(instr->context());
+  __ movp(Operand(rbp, StandardFrameConstants::kContextOffset), context);
+}
+
+
+void LCodeGen::DoAllocateBlockContext(LAllocateBlockContext* instr) {
+  Handle<ScopeInfo> scope_info = instr->scope_info();
+  __ Push(scope_info);
+  __ Push(ToRegister(instr->function()));
+  CallRuntime(Runtime::kHiddenPushBlockContext, 2, instr);
+  RecordSafepoint(Safepoint::kNoLazyDeopt);
+}
+
+
 #undef __
 
 } }  // namespace v8::internal
index b01d542..d44d37d 100644 (file)
@@ -2583,6 +2583,22 @@ LInstruction* LChunkBuilder::DoLoadFieldByIndex(HLoadFieldByIndex* instr) {
 }
 
 
+LInstruction* LChunkBuilder::DoStoreFrameContext(HStoreFrameContext* instr) {
+  LOperand* context = UseRegisterAtStart(instr->context());
+  return new(zone()) LStoreFrameContext(context);
+}
+
+
+LInstruction* LChunkBuilder::DoAllocateBlockContext(
+    HAllocateBlockContext* instr) {
+  LOperand* context = UseFixed(instr->context(), rsi);
+  LOperand* function = UseRegisterAtStart(instr->function());
+  LAllocateBlockContext* result =
+      new(zone()) LAllocateBlockContext(context, function);
+  return MarkAsCall(DefineFixed(result, rsi), instr);
+}
+
+
 } }  // namespace v8::internal
 
 #endif  // V8_TARGET_ARCH_X64
index 25d2d99..f8387ec 100644 (file)
@@ -21,6 +21,7 @@ class LCodeGen;
   V(AccessArgumentsAt)                          \
   V(AddI)                                       \
   V(Allocate)                                   \
+  V(AllocateBlockContext)                       \
   V(ApplyArguments)                             \
   V(ArgumentsElements)                          \
   V(ArgumentsLength)                            \
@@ -137,6 +138,7 @@ class LCodeGen;
   V(StackCheck)                                 \
   V(StoreCodeEntry)                             \
   V(StoreContextSlot)                           \
+  V(StoreFrameContext)                          \
   V(StoreGlobalCell)                            \
   V(StoreKeyed)                                 \
   V(StoreKeyedGeneric)                          \
@@ -2627,6 +2629,35 @@ class LLoadFieldByIndex V8_FINAL : public LTemplateInstruction<1, 2, 0> {
 };
 
 
+class LStoreFrameContext: public LTemplateInstruction<0, 1, 0> {
+ public:
+  explicit LStoreFrameContext(LOperand* context) {
+    inputs_[0] = context;
+  }
+
+  LOperand* context() { return inputs_[0]; }
+
+  DECLARE_CONCRETE_INSTRUCTION(StoreFrameContext, "store-frame-context")
+};
+
+
+class LAllocateBlockContext: public LTemplateInstruction<1, 2, 0> {
+ public:
+  LAllocateBlockContext(LOperand* context, LOperand* function) {
+    inputs_[0] = context;
+    inputs_[1] = function;
+  }
+
+  LOperand* context() { return inputs_[0]; }
+  LOperand* function() { return inputs_[1]; }
+
+  Handle<ScopeInfo> scope_info() { return hydrogen()->scope_info(); }
+
+  DECLARE_CONCRETE_INSTRUCTION(AllocateBlockContext, "allocate-block-context")
+  DECLARE_HYDROGEN_ACCESSOR(AllocateBlockContext)
+};
+
+
 class LChunkBuilder;
 class LPlatformChunk V8_FINAL : public LChunk {
  public:
index 5888fd2..37fde75 100644 (file)
@@ -32,7 +32,8 @@
 
 // Check that the following functions are optimizable.
 var functions = [ f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14,
-                  f15, f16, f17, f18, f19, f20, f21, f22, f23 ];
+                  f15, f16, f17, f18, f19, f20, f21, f22, f23, f24, f25, f26,
+                  f27, f28, f29, f30, f31, f32, f33];
 
 for (var i = 0; i < functions.length; ++i) {
   var func = functions[i];
@@ -156,6 +157,184 @@ function f23() {
   (function() { x; });
 }
 
+function f24() {
+  let x = 1;
+  {
+    let x = 2;
+    {
+      let x = 3;
+      assertEquals(3, x);
+    }
+    assertEquals(2, x);
+  }
+  assertEquals(1, x);
+}
+
+function f25() {
+  {
+    let x = 2;
+    L: {
+      let x = 3;
+      assertEquals(3, x);
+      break L;
+      assertTrue(false);
+    }
+    assertEquals(2, x);
+  }
+  assertTrue(true);
+}
+
+function f26() {
+  {
+    let x = 1;
+    L: {
+      let x = 2;
+      {
+        let x = 3;
+        assertEquals(3, x);
+        break L;
+        assertTrue(false);
+      }
+      assertTrue(false);
+    }
+    assertEquals(1, x);
+  }
+}
+
+
+function f27() {
+  do {
+    let x = 4;
+    assertEquals(4,x);
+    {
+      let x = 5;
+      assertEquals(5, x);
+      continue;
+      assertTrue(false);
+    }
+  } while (false);
+}
+
+function f28() {
+  label: for (var i = 0; i < 10; ++i) {
+    let x = 'middle' + i;
+    for (var j = 0; j < 10; ++j) {
+      let x = 'inner' + j;
+      continue label;
+    }
+  }
+}
+
+function f29() {
+  // Verify that the context is correctly set in the stack frame after exiting
+  // from with.
+
+  let x = 'outer';
+  label: {
+    let x = 'inner';
+    break label;
+  }
+  f();  // The context could be restored from the stack after the call.
+  assertEquals('outer', x);
+
+  function f() {
+    assertEquals('outer', x);
+  };
+}
+
+function f30() {
+  let x = 'outer';
+  for (var i = 0; i < 10; ++i) {
+    let x = 'inner';
+    continue;
+  }
+  f();
+  assertEquals('outer', x);
+
+  function f() {
+    assertEquals('outer', x);
+  };
+}
+
+function f31() {
+  {
+    let x = 'outer';
+    label: for (var i = 0; assertEquals('outer', x), i < 10; ++i) {
+      let x = 'middle' + i;
+      {
+        let x = 'inner' + j;
+        continue label;
+      }
+    }
+    assertEquals('outer', x);
+  }
+}
+
+var c = true;
+
+function f32() {
+  {
+    let x = 'outer';
+    L: {
+      {
+        let x = 'inner';
+        if (c) {
+          break L;
+        }
+      }
+      foo();
+    }
+  }
+
+  function foo() {
+    return 'bar';
+  }
+}
+
+function f33() {
+  {
+    let x = 'outer';
+    L: {
+      {
+        let x = 'inner';
+        if (c) {
+          break L;
+        }
+        foo();
+      }
+    }
+  }
+
+  function foo() {
+    return 'bar';
+  }
+}
+
+function TestThrow() {
+  function f() {
+    let x = 'outer';
+    {
+      let x = 'inner';
+      throw x;
+    }
+  }
+  for (var i = 0; i < 5; i++) {
+    try {
+      f();
+    } catch (e) {
+      assertEquals('inner', e);
+    }
+  }
+  %OptimizeFunctionOnNextCall(f);
+  try {
+    f();
+  } catch (e) {
+    assertEquals('inner', e);
+  }
+  assertOptimized(f);
+}
+
+TestThrow();
 
 // Test that temporal dead zone semantics for function and block scoped
 // let bindings are handled by the optimizing compiler.
@@ -208,9 +387,59 @@ function TestFunctionContext(s) {
   }
 }
 
+function TestBlockLocal(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 TestBlockContext(s) {
+  'use strict';
+  var func = eval("(function baz(){ { " + s + "; (function() { x; }); } })");
+  print("Testing:");
+  print(func);
+  for (var i = 0; i < 5; ++i) {
+    print(i);
+    try {
+      func();
+      assertUnreachable();
+    } catch (e) {
+      assertInstanceof(e, ReferenceError);
+    }
+  }
+  print("optimize");
+  %OptimizeFunctionOnNextCall(func);
+  try {
+    print("call");
+    func();
+    assertUnreachable();
+  } catch (e) {
+    print("catch");
+    assertInstanceof(e, ReferenceError);
+  }
+}
+
 function TestAll(s) {
   TestFunctionLocal(s);
   TestFunctionContext(s);
+  TestBlockLocal(s);
+  TestBlockContext(s);
 }
 
 // Use before initialization in declaration statement.
@@ -229,34 +458,28 @@ TestAll('x++; let x;');
 TestAll('let y = x; const x = 1;');
 
 
-function f(x, b) {
-  let y = (b ? y : x) + 42;
+function f(x) {
+  let y = x + 42;
   return y;
 }
 
-function g(x, b) {
+function g(x) {
   {
-    let y = (b ? y : x) + 42;
+    let y = x + 42;
     return y;
   }
 }
 
 for (var i=0; i<10; i++) {
-  f(i, false);
-  g(i, false);
+  f(i);
+  g(i);
 }
 
 %OptimizeFunctionOnNextCall(f);
 %OptimizeFunctionOnNextCall(g);
 
-try {
-  f(42, true);
-} catch (e) {
-  assertInstanceof(e, ReferenceError);
-}
+f(12);
+g(12);
 
-try {
-  g(42, true);
-} catch (e) {
-  assertInstanceof(e, ReferenceError);
-}
+assertTrue(%GetOptimizationStatus(f) != 2);
+assertTrue(%GetOptimizationStatus(g) != 2);
index 31194d9..001d9fb 100644 (file)
@@ -28,7 +28,6 @@
 // Flags: --allow-natives-syntax --harmony-scoping
 // Test functionality of block scopes.
 
-// TODO(ES6): properly activate extended mode
 "use strict";
 
 // Hoisting of var declarations.
@@ -40,8 +39,10 @@ function f1() {
   assertEquals(1, x)
   assertEquals(undefined, y)
 }
+for (var j = 0; j < 5; ++j) f1();
+%OptimizeFunctionOnNextCall(f1);
 f1();
-
+assertTrue(%GetOptimizationStatus(f1) != 2);
 
 // Dynamic lookup in and through block contexts.
 function f2(one) {
@@ -59,8 +60,8 @@ function f2(one) {
     assertEquals(6, eval('v'));
   }
 }
-f2(1);
 
+f2(1);
 
 // Lookup in and through block contexts.
 function f3(one) {
@@ -76,10 +77,13 @@ function f3(one) {
     assertEquals(4, z);
     assertEquals(5, u);
     assertEquals(6, v);
-
   }
 }
+for (var j = 0; j < 5; ++j) f3(1);
+%OptimizeFunctionOnNextCall(f3);
 f3(1);
+assertTrue(%GetOptimizationStatus(f3) != 2);
+
 
 
 // Dynamic lookup from closure.