From ee8108b71c7e63392ba9814d859aa06f47acf5e5 Mon Sep 17 00:00:00 2001 From: mstarzinger Date: Wed, 24 Feb 2016 03:05:12 -0800 Subject: [PATCH] [fullcodegen] Implement control flow across do-expressions. This implements proper handling of local control flow (i.e. break and continue) that spans the boundary of a do-expression. We can no longer determine the number of operands to be dropped from the nesting of statements alone, instead we use the new precise operand stack depth tracking. R=jarin@chromium.org TEST=mjsunit/harmony/do-expressions-control BUG=v8:4488 LOG=n Review URL: https://codereview.chromium.org/1724753002 Cr-Commit-Position: refs/heads/master@{#34246} --- src/full-codegen/arm/full-codegen-arm.cc | 16 +-- src/full-codegen/arm64/full-codegen-arm64.cc | 16 +-- src/full-codegen/full-codegen.cc | 70 +++++------ src/full-codegen/full-codegen.h | 68 +++-------- src/full-codegen/ia32/full-codegen-ia32.cc | 18 +-- src/full-codegen/mips/full-codegen-mips.cc | 16 +-- .../mips64/full-codegen-mips64.cc | 16 +-- src/full-codegen/x64/full-codegen-x64.cc | 18 +-- .../mjsunit/harmony/do-expressions-control.js | 109 ++++++++++++++++++ 9 files changed, 210 insertions(+), 137 deletions(-) create mode 100644 test/mjsunit/harmony/do-expressions-control.js diff --git a/src/full-codegen/arm/full-codegen-arm.cc b/src/full-codegen/arm/full-codegen-arm.cc index 45a60e5377..dde076b74f 100644 --- a/src/full-codegen/arm/full-codegen-arm.cc +++ b/src/full-codegen/arm/full-codegen-arm.cc @@ -994,14 +994,14 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { FeedbackVectorSlot slot = stmt->ForInFeedbackSlot(); - Label loop, exit; - ForIn loop_statement(this, stmt); - increment_loop_depth(); - // Get the object to enumerate over. SetExpressionAsStatementPosition(stmt->enumerable()); VisitForAccumulatorValue(stmt->enumerable()); - OperandStackDepthIncrement(ForIn::kElementCount); + OperandStackDepthIncrement(5); + + Label loop, exit; + Iteration loop_statement(this, stmt); + increment_loop_depth(); // If the object is null or undefined, skip over the loop, otherwise convert // it to a JS receiver. See ECMA-262 version 5, section 12.6.4. @@ -1888,7 +1888,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) { case Yield::kSuspend: // Pop value from top-of-stack slot; box result into result register. EmitCreateIteratorResult(false); - __ push(result_register()); + PushOperand(result_register()); // Fall through. case Yield::kInitial: { Label suspend, continuation, post_runtime, resume; @@ -1907,6 +1907,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) { EmitUnwindAndReturn(); __ bind(&suspend); + OperandStackDepthIncrement(1); // Not popped on this path. VisitForAccumulatorValue(expr->generator_object()); DCHECK(continuation.pos() > 0 && Smi::IsValid(continuation.pos())); __ mov(r1, Operand(Smi::FromInt(continuation.pos()))); @@ -1932,7 +1933,6 @@ void FullCodeGenerator::VisitYield(Yield* expr) { case Yield::kFinal: { // Pop value from top-of-stack slot, box result into result register. - OperandStackDepthDecrement(1); EmitCreateIteratorResult(true); EmitUnwindAndReturn(); break; @@ -2081,7 +2081,7 @@ void FullCodeGenerator::EmitCreateIteratorResult(bool done) { __ bind(&done_allocate); __ LoadNativeContextSlot(Context::ITERATOR_RESULT_MAP_INDEX, r1); - __ pop(r2); + PopOperand(r2); __ LoadRoot(r3, done ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex); __ LoadRoot(r4, Heap::kEmptyFixedArrayRootIndex); diff --git a/src/full-codegen/arm64/full-codegen-arm64.cc b/src/full-codegen/arm64/full-codegen-arm64.cc index 169cb07da8..ad5cd8a1d1 100644 --- a/src/full-codegen/arm64/full-codegen-arm64.cc +++ b/src/full-codegen/arm64/full-codegen-arm64.cc @@ -997,14 +997,14 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { // TODO(all): This visitor probably needs better comments and a revisit. - Label loop, exit; - ForIn loop_statement(this, stmt); - increment_loop_depth(); - // Get the object to enumerate over. SetExpressionAsStatementPosition(stmt->enumerable()); VisitForAccumulatorValue(stmt->enumerable()); - OperandStackDepthIncrement(ForIn::kElementCount); + OperandStackDepthIncrement(5); + + Label loop, exit; + Iteration loop_statement(this, stmt); + increment_loop_depth(); // If the object is null or undefined, skip over the loop, otherwise convert // it to a JS receiver. See ECMA-262 version 5, section 12.6.4. @@ -3874,7 +3874,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) { case Yield::kSuspend: // Pop value from top-of-stack slot; box result into result register. EmitCreateIteratorResult(false); - __ Push(result_register()); + PushOperand(result_register()); // Fall through. case Yield::kInitial: { Label suspend, continuation, post_runtime, resume; @@ -3896,6 +3896,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) { EmitUnwindAndReturn(); __ Bind(&suspend); + OperandStackDepthIncrement(1); // Not popped on this path. VisitForAccumulatorValue(expr->generator_object()); DCHECK((continuation.pos() > 0) && Smi::IsValid(continuation.pos())); __ Mov(x1, Smi::FromInt(continuation.pos())); @@ -3921,7 +3922,6 @@ void FullCodeGenerator::VisitYield(Yield* expr) { case Yield::kFinal: { // Pop value from top-of-stack slot, box result into result register. - OperandStackDepthDecrement(1); EmitCreateIteratorResult(true); EmitUnwindAndReturn(); break; @@ -4079,7 +4079,7 @@ void FullCodeGenerator::EmitCreateIteratorResult(bool done) { Register empty_fixed_array = x4; Register untagged_result = x5; __ LoadNativeContextSlot(Context::ITERATOR_RESULT_MAP_INDEX, map_reg); - __ Pop(result_value); + PopOperand(result_value); __ LoadRoot(boolean_done, done ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex); __ LoadRoot(empty_fixed_array, Heap::kEmptyFixedArrayRootIndex); diff --git a/src/full-codegen/full-codegen.cc b/src/full-codegen/full-codegen.cc index 8255089f7e..4d292be501 100644 --- a/src/full-codegen/full-codegen.cc +++ b/src/full-codegen/full-codegen.cc @@ -913,7 +913,6 @@ void FullCodeGenerator::VisitIfStatement(IfStatement* stmt) { void FullCodeGenerator::EmitContinue(Statement* target) { NestedStatement* current = nesting_stack_; - int stack_depth = 0; int context_length = 0; // When continuing, we clobber the unpredictable value in the accumulator // with one that's safe for GC. If we hit an exit from the try block of @@ -923,15 +922,17 @@ void FullCodeGenerator::EmitContinue(Statement* target) { while (!current->IsContinueTarget(target)) { if (current->IsTryFinally()) { Comment cmnt(masm(), "[ Deferred continue through finally"); - current->Exit(&stack_depth, &context_length); - DCHECK_EQ(0, stack_depth); - DCHECK_EQ(0, context_length); + current->Exit(&context_length); + DCHECK_EQ(-1, context_length); current->AsTryFinally()->deferred_commands()->RecordContinue(target); return; } - current = current->Exit(&stack_depth, &context_length); + current = current->Exit(&context_length); } - __ Drop(stack_depth); + int stack_depth = current->GetStackDepthAtTarget(); + int stack_drop = operand_stack_depth_ - stack_depth; + DCHECK_GE(stack_drop, 0); + __ Drop(stack_drop); if (context_length > 0) { while (context_length > 0) { LoadContextField(context_register(), Context::PREVIOUS_INDEX); @@ -952,7 +953,6 @@ void FullCodeGenerator::VisitContinueStatement(ContinueStatement* stmt) { void FullCodeGenerator::EmitBreak(Statement* target) { NestedStatement* current = nesting_stack_; - int stack_depth = 0; int context_length = 0; // When breaking, we clobber the unpredictable value in the accumulator // with one that's safe for GC. If we hit an exit from the try block of @@ -962,15 +962,17 @@ void FullCodeGenerator::EmitBreak(Statement* target) { while (!current->IsBreakTarget(target)) { if (current->IsTryFinally()) { Comment cmnt(masm(), "[ Deferred break through finally"); - current->Exit(&stack_depth, &context_length); - DCHECK_EQ(0, stack_depth); - DCHECK_EQ(0, context_length); + current->Exit(&context_length); + DCHECK_EQ(-1, context_length); current->AsTryFinally()->deferred_commands()->RecordBreak(target); return; } - current = current->Exit(&stack_depth, &context_length); + current = current->Exit(&context_length); } - __ Drop(stack_depth); + int stack_depth = current->GetStackDepthAtTarget(); + int stack_drop = operand_stack_depth_ - stack_depth; + DCHECK_GE(stack_drop, 0); + __ Drop(stack_drop); if (context_length > 0) { while (context_length > 0) { LoadContextField(context_register(), Context::PREVIOUS_INDEX); @@ -991,20 +993,17 @@ void FullCodeGenerator::VisitBreakStatement(BreakStatement* stmt) { void FullCodeGenerator::EmitUnwindAndReturn() { NestedStatement* current = nesting_stack_; - int stack_depth = 0; int context_length = 0; while (current != NULL) { if (current->IsTryFinally()) { Comment cmnt(masm(), "[ Deferred return through finally"); - current->Exit(&stack_depth, &context_length); - DCHECK_EQ(0, stack_depth); - DCHECK_EQ(0, context_length); + current->Exit(&context_length); + DCHECK_EQ(-1, context_length); current->AsTryFinally()->deferred_commands()->RecordReturn(); return; } - current = current->Exit(&stack_depth, &context_length); + current = current->Exit(&context_length); } - __ Drop(stack_depth); EmitReturnSequence(); } @@ -1281,7 +1280,8 @@ void FullCodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) { try_catch_depth_++; int handler_index = NewHandlerTableEntry(); EnterTryBlock(handler_index, &handler_entry); - { TryCatch try_body(this); + { + Comment cmnt_try(masm(), "[ Try block"); Visit(stmt->try_block()); } ExitTryBlock(handler_index); @@ -1322,7 +1322,7 @@ void FullCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) { // Exception handler code. This code is only executed when an exception // is thrown. Record the continuation and jump to the finally block. { - Comment cmt_handler(masm(), "[ Finally handler"); + Comment cmnt_handler(masm(), "[ Finally handler"); deferred.RecordThrow(); } @@ -1331,6 +1331,7 @@ void FullCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) { int handler_index = NewHandlerTableEntry(); EnterTryBlock(handler_index, &handler_entry); { + Comment cmnt_try(masm(), "[ Try block"); TryFinally try_body(this, &deferred); Visit(stmt->try_block()); } @@ -1345,15 +1346,14 @@ void FullCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) { // Finally block implementation. __ bind(&finally_entry); - Comment cmnt_finally(masm(), "[ Finally block"); - OperandStackDepthIncrement(2); // Token and accumulator are on stack. - EnterFinallyBlock(); { - Finally finally_body(this); + Comment cmnt_finally(masm(), "[ Finally block"); + OperandStackDepthIncrement(2); // Token and accumulator are on stack. + EnterFinallyBlock(); Visit(stmt->finally_block()); + ExitFinallyBlock(); + OperandStackDepthDecrement(2); // Token and accumulator were on stack. } - ExitFinallyBlock(); - OperandStackDepthDecrement(2); // Token and accumulator were on stack. { Comment cmnt_deferred(masm(), "[ Post-finally dispatch"); @@ -1599,28 +1599,32 @@ void FullCodeGenerator::VisitRewritableExpression(RewritableExpression* expr) { Visit(expr->expression()); } - FullCodeGenerator::NestedStatement* FullCodeGenerator::TryFinally::Exit( - int* stack_depth, int* context_length) { + int* context_length) { // The macros used here must preserve the result register. + // Calculate how many operands to drop to get down to handler block. + int stack_drop = codegen_->operand_stack_depth_ - GetStackDepthAtTarget(); + DCHECK_GE(stack_drop, 0); + // Because the handler block contains the context of the finally // code, we can restore it directly from there for the finally code // rather than iteratively unwinding contexts via their previous // links. if (*context_length > 0) { - __ Drop(*stack_depth); // Down to the handler block. + __ Drop(stack_drop); // Down to the handler block. // Restore the context to its dedicated register and the stack. - STATIC_ASSERT(TryFinally::kElementCount == 1); + STATIC_ASSERT(TryBlockConstant::kElementCount == 1); __ Pop(codegen_->context_register()); codegen_->StoreToFrameField(StandardFrameConstants::kContextOffset, codegen_->context_register()); } else { // Down to the handler block and also drop context. - __ Drop(*stack_depth + kElementCount); + __ Drop(stack_drop + TryBlockConstant::kElementCount); } - *stack_depth = 0; - *context_length = 0; + + // The caller will ignore outputs. + *context_length = -1; return previous_; } diff --git a/src/full-codegen/full-codegen.h b/src/full-codegen/full-codegen.h index 6ab02313bb..e811e25cf8 100644 --- a/src/full-codegen/full-codegen.h +++ b/src/full-codegen/full-codegen.h @@ -108,7 +108,9 @@ class FullCodeGenerator: public AstVisitor { class NestedStatement BASE_EMBEDDED { public: - explicit NestedStatement(FullCodeGenerator* codegen) : codegen_(codegen) { + explicit NestedStatement(FullCodeGenerator* codegen) + : codegen_(codegen), + stack_depth_at_target_(codegen->operand_stack_depth_) { // Link into codegen's nesting stack. previous_ = codegen->nesting_stack_; codegen->nesting_stack_ = this; @@ -130,18 +132,20 @@ class FullCodeGenerator: public AstVisitor { // Notify the statement that we are exiting it via break, continue, or // return and give it a chance to generate cleanup code. Return the // next outer statement in the nesting stack. We accumulate in - // *stack_depth the amount to drop the stack and in *context_length the - // number of context chain links to unwind as we traverse the nesting - // stack from an exit to its target. - virtual NestedStatement* Exit(int* stack_depth, int* context_length) { - return previous_; - } + // {*context_length} the number of context chain links to unwind as we + // traverse the nesting stack from an exit to its target. + virtual NestedStatement* Exit(int* context_length) { return previous_; } + + // Determine the expected operand stack depth when this statement is being + // used as the target of an exit. The caller will drop to this depth. + int GetStackDepthAtTarget() { return stack_depth_at_target_; } protected: MacroAssembler* masm() { return codegen_->masm(); } FullCodeGenerator* codegen_; NestedStatement* previous_; + int stack_depth_at_target_; private: DISALLOW_COPY_AND_ASSIGN(NestedStatement); @@ -192,7 +196,7 @@ class FullCodeGenerator: public AstVisitor { : Breakable(codegen, block) { } - NestedStatement* Exit(int* stack_depth, int* context_length) override { + NestedStatement* Exit(int* context_length) override { auto block_scope = statement()->AsBlock()->scope(); if (block_scope != nullptr) { if (block_scope->ContextLocalCount() > 0) ++(*context_length); @@ -201,19 +205,6 @@ class FullCodeGenerator: public AstVisitor { } }; - // The try block of a try/catch statement. - class TryCatch : public NestedStatement { - public: - static const int kElementCount = TryBlockConstant::kElementCount; - - explicit TryCatch(FullCodeGenerator* codegen) : NestedStatement(codegen) {} - - NestedStatement* Exit(int* stack_depth, int* context_length) override { - *stack_depth += kElementCount; - return previous_; - } - }; - class DeferredCommands { public: enum Command { kReturn, kThrow, kBreak, kContinue }; @@ -254,12 +245,10 @@ class FullCodeGenerator: public AstVisitor { // The try block of a try/finally statement. class TryFinally : public NestedStatement { public: - static const int kElementCount = TryBlockConstant::kElementCount; - TryFinally(FullCodeGenerator* codegen, DeferredCommands* commands) : NestedStatement(codegen), deferred_commands_(commands) {} - NestedStatement* Exit(int* stack_depth, int* context_length) override; + NestedStatement* Exit(int* context_length) override; bool IsTryFinally() override { return true; } TryFinally* AsTryFinally() override { return this; } @@ -270,35 +259,6 @@ class FullCodeGenerator: public AstVisitor { DeferredCommands* deferred_commands_; }; - // The finally block of a try/finally statement. - class Finally : public NestedStatement { - public: - static const int kElementCount = 3; - - explicit Finally(FullCodeGenerator* codegen) : NestedStatement(codegen) {} - - NestedStatement* Exit(int* stack_depth, int* context_length) override { - *stack_depth += kElementCount; - return previous_; - } - }; - - // The body of a for/in loop. - class ForIn : public Iteration { - public: - static const int kElementCount = 5; - - ForIn(FullCodeGenerator* codegen, ForInStatement* statement) - : Iteration(codegen, statement) { - } - - NestedStatement* Exit(int* stack_depth, int* context_length) override { - *stack_depth += kElementCount; - return previous_; - } - }; - - // The body of a with or catch. class WithOrCatch : public NestedStatement { public: @@ -306,7 +266,7 @@ class FullCodeGenerator: public AstVisitor { : NestedStatement(codegen) { } - NestedStatement* Exit(int* stack_depth, int* context_length) override { + NestedStatement* Exit(int* context_length) override { ++(*context_length); return previous_; } diff --git a/src/full-codegen/ia32/full-codegen-ia32.cc b/src/full-codegen/ia32/full-codegen-ia32.cc index 1f913af43f..13639a7e10 100644 --- a/src/full-codegen/ia32/full-codegen-ia32.cc +++ b/src/full-codegen/ia32/full-codegen-ia32.cc @@ -934,14 +934,14 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { FeedbackVectorSlot slot = stmt->ForInFeedbackSlot(); - Label loop, exit; - ForIn loop_statement(this, stmt); - increment_loop_depth(); - // Get the object to enumerate over. SetExpressionAsStatementPosition(stmt->enumerable()); VisitForAccumulatorValue(stmt->enumerable()); - OperandStackDepthIncrement(ForIn::kElementCount); + OperandStackDepthIncrement(5); + + Label loop, exit; + Iteration loop_statement(this, stmt); + increment_loop_depth(); // If the object is null or undefined, skip over the loop, otherwise convert // it to a JS receiver. See ECMA-262 version 5, section 12.6.4. @@ -1089,8 +1089,7 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { // Remove the pointers stored on the stack. __ bind(loop_statement.break_label()); - __ add(esp, Immediate(5 * kPointerSize)); - OperandStackDepthDecrement(ForIn::kElementCount); + DropOperands(5); // Exit and decrement the loop depth. PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS); @@ -1805,7 +1804,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) { case Yield::kSuspend: // Pop value from top-of-stack slot; box result into result register. EmitCreateIteratorResult(false); - __ push(result_register()); + PushOperand(result_register()); // Fall through. case Yield::kInitial: { Label suspend, continuation, post_runtime, resume; @@ -1824,6 +1823,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) { EmitUnwindAndReturn(); __ bind(&suspend); + OperandStackDepthIncrement(1); // Not popped on this path. VisitForAccumulatorValue(expr->generator_object()); DCHECK(continuation.pos() > 0 && Smi::IsValid(continuation.pos())); __ mov(FieldOperand(eax, JSGeneratorObject::kContinuationOffset), @@ -1850,7 +1850,6 @@ void FullCodeGenerator::VisitYield(Yield* expr) { case Yield::kFinal: { // Pop value from top-of-stack slot, box result into result register. - OperandStackDepthDecrement(1); EmitCreateIteratorResult(true); EmitUnwindAndReturn(); break; @@ -1991,6 +1990,7 @@ void FullCodeGenerator::EmitCreateIteratorResult(bool done) { __ mov(FieldOperand(eax, JSIteratorResult::kDoneOffset), isolate()->factory()->ToBoolean(done)); STATIC_ASSERT(JSIteratorResult::kSize == 5 * kPointerSize); + OperandStackDepthDecrement(1); } diff --git a/src/full-codegen/mips/full-codegen-mips.cc b/src/full-codegen/mips/full-codegen-mips.cc index 17f847c682..6f053506b0 100644 --- a/src/full-codegen/mips/full-codegen-mips.cc +++ b/src/full-codegen/mips/full-codegen-mips.cc @@ -990,15 +990,15 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { FeedbackVectorSlot slot = stmt->ForInFeedbackSlot(); - Label loop, exit; - ForIn loop_statement(this, stmt); - increment_loop_depth(); - // Get the object to enumerate over. SetExpressionAsStatementPosition(stmt->enumerable()); VisitForAccumulatorValue(stmt->enumerable()); __ mov(a0, result_register()); - OperandStackDepthIncrement(ForIn::kElementCount); + OperandStackDepthIncrement(5); + + Label loop, exit; + Iteration loop_statement(this, stmt); + increment_loop_depth(); // If the object is null or undefined, skip over the loop, otherwise convert // it to a JS receiver. See ECMA-262 version 5, section 12.6.4. @@ -1886,7 +1886,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) { case Yield::kSuspend: // Pop value from top-of-stack slot; box result into result register. EmitCreateIteratorResult(false); - __ push(result_register()); + PushOperand(result_register()); // Fall through. case Yield::kInitial: { Label suspend, continuation, post_runtime, resume; @@ -1905,6 +1905,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) { EmitUnwindAndReturn(); __ bind(&suspend); + OperandStackDepthIncrement(1); // Not popped on this path. VisitForAccumulatorValue(expr->generator_object()); DCHECK(continuation.pos() > 0 && Smi::IsValid(continuation.pos())); __ li(a1, Operand(Smi::FromInt(continuation.pos()))); @@ -1929,7 +1930,6 @@ void FullCodeGenerator::VisitYield(Yield* expr) { case Yield::kFinal: { // Pop value from top-of-stack slot, box result into result register. - OperandStackDepthDecrement(1); EmitCreateIteratorResult(true); EmitUnwindAndReturn(); break; @@ -2079,7 +2079,7 @@ void FullCodeGenerator::EmitCreateIteratorResult(bool done) { __ bind(&done_allocate); __ LoadNativeContextSlot(Context::ITERATOR_RESULT_MAP_INDEX, a1); - __ pop(a2); + PopOperand(a2); __ LoadRoot(a3, done ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex); __ LoadRoot(t0, Heap::kEmptyFixedArrayRootIndex); diff --git a/src/full-codegen/mips64/full-codegen-mips64.cc b/src/full-codegen/mips64/full-codegen-mips64.cc index 70ae080d33..f51ec6641c 100644 --- a/src/full-codegen/mips64/full-codegen-mips64.cc +++ b/src/full-codegen/mips64/full-codegen-mips64.cc @@ -990,16 +990,16 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { FeedbackVectorSlot slot = stmt->ForInFeedbackSlot(); - Label loop, exit; - ForIn loop_statement(this, stmt); - increment_loop_depth(); - // Get the object to enumerate over. If the object is null or undefined, skip // over the loop. See ECMA-262 version 5, section 12.6.4. SetExpressionAsStatementPosition(stmt->enumerable()); VisitForAccumulatorValue(stmt->enumerable()); __ mov(a0, result_register()); - OperandStackDepthIncrement(ForIn::kElementCount); + OperandStackDepthIncrement(5); + + Label loop, exit; + Iteration loop_statement(this, stmt); + increment_loop_depth(); // If the object is null or undefined, skip over the loop, otherwise convert // it to a JS receiver. See ECMA-262 version 5, section 12.6.4. @@ -1888,7 +1888,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) { case Yield::kSuspend: // Pop value from top-of-stack slot; box result into result register. EmitCreateIteratorResult(false); - __ push(result_register()); + PushOperand(result_register()); // Fall through. case Yield::kInitial: { Label suspend, continuation, post_runtime, resume; @@ -1907,6 +1907,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) { EmitUnwindAndReturn(); __ bind(&suspend); + OperandStackDepthIncrement(1); // Not popped on this path. VisitForAccumulatorValue(expr->generator_object()); DCHECK(continuation.pos() > 0 && Smi::IsValid(continuation.pos())); __ li(a1, Operand(Smi::FromInt(continuation.pos()))); @@ -1931,7 +1932,6 @@ void FullCodeGenerator::VisitYield(Yield* expr) { case Yield::kFinal: { // Pop value from top-of-stack slot, box result into result register. - OperandStackDepthDecrement(1); EmitCreateIteratorResult(true); EmitUnwindAndReturn(); break; @@ -2083,7 +2083,7 @@ void FullCodeGenerator::EmitCreateIteratorResult(bool done) { __ bind(&done_allocate); __ LoadNativeContextSlot(Context::ITERATOR_RESULT_MAP_INDEX, a1); - __ pop(a2); + PopOperand(a2); __ LoadRoot(a3, done ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex); __ LoadRoot(a4, Heap::kEmptyFixedArrayRootIndex); diff --git a/src/full-codegen/x64/full-codegen-x64.cc b/src/full-codegen/x64/full-codegen-x64.cc index 39862bc4d3..7bd7d4960d 100644 --- a/src/full-codegen/x64/full-codegen-x64.cc +++ b/src/full-codegen/x64/full-codegen-x64.cc @@ -947,14 +947,14 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { FeedbackVectorSlot slot = stmt->ForInFeedbackSlot(); - Label loop, exit; - ForIn loop_statement(this, stmt); - increment_loop_depth(); - // Get the object to enumerate over. SetExpressionAsStatementPosition(stmt->enumerable()); VisitForAccumulatorValue(stmt->enumerable()); - OperandStackDepthIncrement(ForIn::kElementCount); + OperandStackDepthIncrement(5); + + Label loop, exit; + Iteration loop_statement(this, stmt); + increment_loop_depth(); // If the object is null or undefined, skip over the loop, otherwise convert // it to a JS receiver. See ECMA-262 version 5, section 12.6.4. @@ -1115,8 +1115,7 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { // Remove the pointers stored on the stack. __ bind(loop_statement.break_label()); - __ addp(rsp, Immediate(5 * kPointerSize)); - OperandStackDepthDecrement(ForIn::kElementCount); + DropOperands(5); // Exit and decrement the loop depth. PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS); @@ -1828,7 +1827,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) { case Yield::kSuspend: // Pop value from top-of-stack slot; box result into result register. EmitCreateIteratorResult(false); - __ Push(result_register()); + PushOperand(result_register()); // Fall through. case Yield::kInitial: { Label suspend, continuation, post_runtime, resume; @@ -1847,6 +1846,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) { EmitUnwindAndReturn(); __ bind(&suspend); + OperandStackDepthIncrement(1); // Not popped on this path. VisitForAccumulatorValue(expr->generator_object()); DCHECK(continuation.pos() > 0 && Smi::IsValid(continuation.pos())); __ Move(FieldOperand(rax, JSGeneratorObject::kContinuationOffset), @@ -1874,7 +1874,6 @@ void FullCodeGenerator::VisitYield(Yield* expr) { case Yield::kFinal: { // Pop value from top-of-stack slot, box result into result register. - OperandStackDepthDecrement(1); EmitCreateIteratorResult(true); EmitUnwindAndReturn(); break; @@ -2014,6 +2013,7 @@ void FullCodeGenerator::EmitCreateIteratorResult(bool done) { __ LoadRoot(FieldOperand(rax, JSIteratorResult::kDoneOffset), done ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex); STATIC_ASSERT(JSIteratorResult::kSize == 5 * kPointerSize); + OperandStackDepthDecrement(1); } diff --git a/test/mjsunit/harmony/do-expressions-control.js b/test/mjsunit/harmony/do-expressions-control.js new file mode 100644 index 0000000000..12c54295cc --- /dev/null +++ b/test/mjsunit/harmony/do-expressions-control.js @@ -0,0 +1,109 @@ +// Copyright 2016 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --allow-natives-syntax --harmony-do-expressions + +(function TestDoForInDoBreak() { + function f(o, i) { + var a = "result@" + do { + var r = "("; + for (var x in o) { + var b = "end@" + do { + if (x == i) { break } else { r += o[x]; x } + } + } + r + ")"; + } + return a + "," + b; + } + assertEquals("result@(3),end@0", f([3], 2)); + assertEquals("result@(35),end@1", f([3,5], 2)); + assertEquals("result@(35),end@1", f([3,5,7], 2)); + assertEquals("result@(35),end@1", f([3,5,7,9], 2)); + %OptimizeFunctionOnNextCall(f); + assertEquals("result@(3),end@0", f([3], 2)); + assertEquals("result@(35),end@1", f([3,5], 2)); + assertEquals("result@(35),end@1", f([3,5,7], 2)); + assertEquals("result@(35),end@1", f([3,5,7,9], 2)); +})(); + +(function TestDoForInDoContinue() { + function f(o, i) { + var a = "result@" + do { + var r = "(" + for (var x in o) { + var b = "end@" + do { + if (x == i) { continue } else { r += o[x]; x } + } + } + r + ")" + } + return a + "," + b + } + assertEquals("result@(3),end@0", f([3], 2)); + assertEquals("result@(35),end@1", f([3,5], 2)); + assertEquals("result@(35),end@1", f([3,5,7], 2)); + assertEquals("result@(359),end@3", f([3,5,7,9], 2)); + %OptimizeFunctionOnNextCall(f); + assertEquals("result@(3),end@0", f([3], 2)); + assertEquals("result@(35),end@1", f([3,5], 2)); + assertEquals("result@(35),end@1", f([3,5,7], 2)); + assertEquals("result@(359),end@3", f([3,5,7,9], 2)); +})(); + +(function TestDoForNestedWithTargetLabels() { + function f(mode) { + var loop = true; + var head = "<"; + var tail = ">"; + var middle = + "1" + do { loop1: for(; loop; head += "A") { + "2" + do { loop2: for(; loop; head += "B") { + "3" + do { loop3: for(; loop; head += "C") { + "4" + do { loop4: for(; loop; head += "D") { + "5" + do { loop5: for(; loop; head += "E") { + "6" + do { loop6: for(; loop; head += "F") { + loop = false; + switch (mode) { + case "b1": break loop1; + case "b2": break loop2; + case "b3": break loop3; + case "b4": break loop4; + case "b5": break loop5; + case "b6": break loop6; + case "c1": continue loop1; + case "c2": continue loop2; + case "c3": continue loop3; + case "c4": continue loop4; + case "c5": continue loop5; + case "c6": continue loop6; + default: "7"; + } + }} + }} + }} + }} + }} + }} + return head + middle + tail; + } + function test() { + assertEquals( "<1undefined>", f("b1")); + assertEquals( "", f("c1")); + assertEquals( "", f("b2")); + assertEquals( "", f("c2")); + assertEquals( "", f("b3")); + assertEquals( "", f("c3")); + assertEquals( "", f("b4")); + assertEquals( "", f("c4")); + assertEquals( "", f("b5")); + assertEquals( "", f("c5")); + assertEquals( "", f("b6")); + assertEquals("", f("c6")); + assertEquals("", f("xx")); + } + test(); + %OptimizeFunctionOnNextCall(f); + test(); +})();