From c8b2fa454ab21abb4529d3254ac6048552f757e5 Mon Sep 17 00:00:00 2001 From: "ulan@chromium.org" Date: Thu, 5 Jun 2014 07:33:01 +0000 Subject: [PATCH] Preliminary support for block contexts in hydrogen. Patch from Steven Keuchel 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 --- src/arm/lithium-arm.cc | 16 ++ src/arm/lithium-arm.h | 31 ++++ src/arm/lithium-codegen-arm.cc | 15 ++ src/arm64/lithium-arm64.cc | 16 ++ src/arm64/lithium-arm64.h | 31 ++++ src/arm64/lithium-codegen-arm64.cc | 17 ++ src/ast.h | 4 + src/full-codegen.cc | 9 +- src/hydrogen-instructions.cc | 9 + src/hydrogen-instructions.h | 69 +++++-- src/hydrogen.cc | 146 ++++++++++++--- src/hydrogen.h | 15 +- src/ia32/lithium-codegen-ia32.cc | 15 ++ src/ia32/lithium-ia32.cc | 16 ++ src/ia32/lithium-ia32.h | 31 ++++ src/parser.cc | 4 +- src/x64/lithium-codegen-x64.cc | 15 ++ src/x64/lithium-x64.cc | 16 ++ src/x64/lithium-x64.h | 31 ++++ test/mjsunit/harmony/block-let-crankshaft.js | 257 +++++++++++++++++++++++++-- test/mjsunit/harmony/block-scoping.js | 12 +- 21 files changed, 710 insertions(+), 65 deletions(-) diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc index 79eb197..93dc830 100644 --- a/src/arm/lithium-arm.cc +++ b/src/arm/lithium-arm.cc @@ -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 diff --git a/src/arm/lithium-arm.h b/src/arm/lithium-arm.h index 2bd07fc..127badc 100644 --- a/src/arm/lithium-arm.h +++ b/src/arm/lithium-arm.h @@ -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 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: diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index 245115f..f852f3d 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -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 scope_info = instr->scope_info(); + __ Push(scope_info); + __ push(ToRegister(instr->function())); + CallRuntime(Runtime::kHiddenPushBlockContext, 2, instr); + RecordSafepoint(Safepoint::kNoLazyDeopt); +} + + #undef __ } } // namespace v8::internal diff --git a/src/arm64/lithium-arm64.cc b/src/arm64/lithium-arm64.cc index bba6530..8446edf 100644 --- a/src/arm64/lithium-arm64.cc +++ b/src/arm64/lithium-arm64.cc @@ -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 diff --git a/src/arm64/lithium-arm64.h b/src/arm64/lithium-arm64.h index 76a569d..18dd927 100644 --- a/src/arm64/lithium-arm64.h +++ b/src/arm64/lithium-arm64.h @@ -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 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) { diff --git a/src/arm64/lithium-codegen-arm64.cc b/src/arm64/lithium-codegen-arm64.cc index a235188..e5e0d1a 100644 --- a/src/arm64/lithium-codegen-arm64.cc +++ b/src/arm64/lithium-codegen-arm64.cc @@ -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 scope_info = instr->scope_info(); + __ Push(scope_info); + __ Push(ToRegister(instr->function())); + CallRuntime(Runtime::kHiddenPushBlockContext, 2, instr); + RecordSafepoint(Safepoint::kNoLazyDeopt); +} + + + } } // namespace v8::internal diff --git a/src/ast.h b/src/ast.h index 35e3a85..0882b25 100644 --- a/src/ast.h +++ b/src/ast.h @@ -441,6 +441,8 @@ class Block V8_FINAL : public BreakableStatement { ZoneList* 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 statements_; bool is_initializer_block_; + const BailoutId decls_id_; Scope* scope_; }; diff --git a/src/full-codegen.cc b/src/full-codegen.cc index 1a60f33..0c82eb3 100644 --- a/src/full-codegen.cc +++ b/src/full-codegen.cc @@ -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); } diff --git a/src/hydrogen-instructions.cc b/src/hydrogen-instructions.cc index 44a6dde..96cd016 100644 --- a/src/hydrogen-instructions.cc +++ b/src/hydrogen-instructions.cc @@ -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; diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index 9e6f0f0..d1f2372 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -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); + HValue* context() { return OperandAt(0); } + HValue* function() { return OperandAt(1); } + Handle 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 scope_info) + : scope_info_(scope_info) { + SetOperandAt(0, context); + SetOperandAt(1, function); + set_representation(Representation::Tagged()); + } + + Handle scope_info_; +}; + + + #undef DECLARE_INSTRUCTION #undef DECLARE_CONCRETE_INSTRUCTION diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 36d62d2..a3f4b0b 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -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(); + } + AddInstruction(function); + // Allocate a block context and store it to the stack frame. + HInstruction* inner_context = Add( + outer_context, function, scope->GetScopeInfo()); + HInstruction* instr = Add(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( + inner_context, static_cast(NULL), + HObjectAccess::ForContextSlot(Context::PREVIOUS_INDEX)); + + HInstruction* instr = Add(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( + context, static_cast(NULL), + HObjectAccess::ForContextSlot(Context::PREVIOUS_INDEX)); + context = context_instruction; + } + HInstruction* instr = Add(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( + context, static_cast(NULL), + HObjectAccess::ForContextSlot(Context::PREVIOUS_INDEX)); + context = context_instruction; + } + HInstruction* instr = Add(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(stmt->StackCheckId()); HStackCheck* stack_check = HStackCheck::cast(Add(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( context, static_cast(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 target, Add(BailoutId::None()); current_block()->UpdateEnvironment(inner_env); - + Scope* saved_scope = scope(); + set_scope(target_info.scope()); HEnterInlined* enter_inlined = Add(return_id, target, arguments_count, function, function_state()->inlining_kind(), @@ -7472,6 +7571,7 @@ bool HOptimizedGraphBuilder::TryInline(Handle 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); } diff --git a/src/hydrogen.h b/src/hydrogen.h index acf1d15..818569b 100644 --- a/src/hydrogen.h +++ b/src/hydrogen.h @@ -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 diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index 543d10e..ee008a0 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -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 scope_info = instr->scope_info(); + __ Push(scope_info); + __ push(ToRegister(instr->function())); + CallRuntime(Runtime::kHiddenPushBlockContext, 2, instr); + RecordSafepoint(Safepoint::kNoLazyDeopt); +} + + #undef __ } } // namespace v8::internal diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc index 3cddd51..44b4ea5 100644 --- a/src/ia32/lithium-ia32.cc +++ b/src/ia32/lithium-ia32.cc @@ -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 diff --git a/src/ia32/lithium-ia32.h b/src/ia32/lithium-ia32.h index e92922b..56b53e0 100644 --- a/src/ia32/lithium-ia32.h +++ b/src/ia32/lithium-ia32.h @@ -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 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: diff --git a/src/parser.cc b/src/parser.cc index 369a451..f40d798 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -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(), 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()); } diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc index 029070a..4ebb33b 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -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 scope_info = instr->scope_info(); + __ Push(scope_info); + __ Push(ToRegister(instr->function())); + CallRuntime(Runtime::kHiddenPushBlockContext, 2, instr); + RecordSafepoint(Safepoint::kNoLazyDeopt); +} + + #undef __ } } // namespace v8::internal diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc index b01d542..d44d37d 100644 --- a/src/x64/lithium-x64.cc +++ b/src/x64/lithium-x64.cc @@ -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 diff --git a/src/x64/lithium-x64.h b/src/x64/lithium-x64.h index 25d2d99..f8387ec 100644 --- a/src/x64/lithium-x64.h +++ b/src/x64/lithium-x64.h @@ -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 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: diff --git a/test/mjsunit/harmony/block-let-crankshaft.js b/test/mjsunit/harmony/block-let-crankshaft.js index 5888fd2..37fde75 100644 --- a/test/mjsunit/harmony/block-let-crankshaft.js +++ b/test/mjsunit/harmony/block-let-crankshaft.js @@ -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); diff --git a/test/mjsunit/harmony/block-scoping.js b/test/mjsunit/harmony/block-scoping.js index 31194d9..001d9fb 100644 --- a/test/mjsunit/harmony/block-scoping.js +++ b/test/mjsunit/harmony/block-scoping.js @@ -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. -- 2.7.4