[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}
This commit is contained in:
mstarzinger 2016-02-24 03:05:12 -08:00 committed by Commit bot
parent 1c1b70c98d
commit ee8108b71c
9 changed files with 210 additions and 137 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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( "<A1undefined>", f("c1"));
assertEquals( "<A12undefined>", f("b2"));
assertEquals( "<BA12undefined>", f("c2"));
assertEquals( "<BA123undefined>", f("b3"));
assertEquals( "<CBA123undefined>", f("c3"));
assertEquals( "<CBA1234undefined>", f("b4"));
assertEquals( "<DCBA1234undefined>", f("c4"));
assertEquals( "<DCBA12345undefined>", f("b5"));
assertEquals( "<EDCBA12345undefined>", f("c5"));
assertEquals( "<EDCBA123456undefined>", f("b6"));
assertEquals("<FEDCBA123456undefined>", f("c6"));
assertEquals("<FEDCBA1234567>", f("xx"));
}
test();
%OptimizeFunctionOnNextCall(f);
test();
})();