[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:
parent
1c1b70c98d
commit
ee8108b71c
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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_;
|
||||
}
|
||||
|
||||
|
@ -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_;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
109
test/mjsunit/harmony/do-expressions-control.js
Normal file
109
test/mjsunit/harmony/do-expressions-control.js
Normal 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();
|
||||
})();
|
Loading…
Reference in New Issue
Block a user