Chromium Code Reviews| Index: src/interpreter/bytecode-generator.cc |
| diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc |
| index 0dabea2ed31db0db4b2b42282162ce19e958f4ab..b1b8517b1917c3611991b9a95f69ffa70d394df6 100644 |
| --- a/src/interpreter/bytecode-generator.cc |
| +++ b/src/interpreter/bytecode-generator.cc |
| @@ -565,8 +565,10 @@ BytecodeGenerator::BytecodeGenerator(Isolate* isolate, Zone* zone) |
| execution_context_(nullptr), |
| execution_result_(nullptr), |
| register_allocator_(nullptr), |
| + generator_resume_points_(0, zone), |
| try_catch_nesting_level_(0), |
| - try_finally_nesting_level_(0) { |
| + try_finally_nesting_level_(0), |
| + generator_yields_seen_(0) { |
| InitializeAstVisitor(isolate); |
| } |
| @@ -586,6 +588,10 @@ Handle<BytecodeArray> BytecodeGenerator::MakeBytecode(CompilationInfo* info) { |
| // Initialize control scope. |
| ControlScopeForTopLevel control(this); |
| + if (IsGeneratorFunction(info->literal()->kind())) { |
| + VisitGeneratorPrologue(); |
| + } |
| + |
| // Build function context only if there are context allocated variables. |
| if (scope()->NeedsContext()) { |
| // Push a new inner context scope for the function. |
| @@ -634,6 +640,40 @@ void BytecodeGenerator::MakeBytecodeBody() { |
| VisitStatements(info()->literal()->body()); |
| } |
| +void BytecodeGenerator::VisitGeneratorPrologue() { |
| + generator_resume_points_.clear(); |
| + generator_resume_points_.resize(info()->literal()->yield_count()); |
| + |
| + BytecodeLabel regular_call; |
| + builder() |
| + ->LoadAccumulatorWithRegister(Register::new_target()) |
| + .JumpIfUndefined(®ular_call); |
| + |
| + // This is a resume call. Restore registers and perform state dispatch. |
| + // (The current context has already been restored by the trampoline.) |
| + { |
| + RegisterAllocationScope register_scope(this); |
| + Register state = register_allocator()->NewRegister(); |
| + builder() |
| + ->CallRuntime(Runtime::kResumeIgnitionGenerator, Register::new_target(), |
| + 1) |
| + .StoreAccumulatorInRegister(state); |
| + |
| + // TODO(neis): Optimize this by using a proper jump table. |
| + for (size_t i = 0; i < generator_resume_points_.size(); ++i) { |
| + builder() |
| + ->LoadLiteral(Smi::FromInt(static_cast<int>(i))) |
| + .CompareOperation(Token::Value::EQ_STRICT, state) |
| + .JumpIfTrue(&(generator_resume_points_[i])); |
| + } |
| + builder()->Illegal(); // Should never get here. |
| + } |
| + |
| + builder()->Bind(®ular_call); |
| + // This is a regular call. Fall through to the ordinary function prologue, |
| + // after which we will run into the generator object creation and the initial |
| + // yield (both inserted by the parser). |
| +} |
| void BytecodeGenerator::VisitBlock(Block* stmt) { |
| // Visit declarations and statements. |
| @@ -2229,16 +2269,86 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) { |
| execution_result()->SetResultInAccumulator(); |
| } |
| +void BytecodeGenerator::VisitYield(Yield* expr) { |
| + int id = generator_yields_seen_++; |
| + |
| + builder()->SetExpressionPosition(expr); |
| + Register value = VisitForRegisterValue(expr->expression()); |
| + |
| + register_allocator()->PrepareForConsecutiveAllocations(2); |
| + Register generator = register_allocator()->NextConsecutiveRegister(); |
| + Register state = register_allocator()->NextConsecutiveRegister(); |
| + |
| + // Save context, registers, and state. Then return. |
| + VisitForRegisterValue(expr->generator_object(), generator); |
| + builder() |
| + ->LoadLiteral(Smi::FromInt(id)) |
| + .StoreAccumulatorInRegister(state) |
| + .CallRuntime(Runtime::kSuspendIgnitionGenerator, generator, 2) |
| + .LoadAccumulatorWithRegister(value) |
| + .Return(); // Hard return (ignore any finally blocks). |
| + |
| + builder()->Bind(&(generator_resume_points_[id])); |
| + // Upon resume, we continue here. |
| + |
| + { |
| + RegisterAllocationScope register_scope(this); |
| + |
| + Register input = register_allocator()->NewRegister(); |
| + builder() |
| + ->CallRuntime(Runtime::kGeneratorGetInput, generator, 1) |
| + .StoreAccumulatorInRegister(input); |
| + |
| + Register resume_mode = register_allocator()->NewRegister(); |
| + builder() |
| + ->CallRuntime(Runtime::kGeneratorGetResumeMode, generator, 1) |
| + .StoreAccumulatorInRegister(resume_mode); |
| + |
| + // Now dispatch on resume mode. |
| + |
| + BytecodeLabel resume_with_next; |
| + BytecodeLabel resume_with_return; |
| + BytecodeLabel resume_with_throw; |
| -void BytecodeGenerator::VisitYield(Yield* expr) { UNIMPLEMENTED(); } |
| + builder() |
| + ->LoadLiteral(Smi::FromInt(JSGeneratorObject::kNext)) |
| + .CompareOperation(Token::EQ_STRICT, resume_mode) |
| + .JumpIfTrue(&resume_with_next) |
| + .LoadLiteral(Smi::FromInt(JSGeneratorObject::kThrow)) |
| + .CompareOperation(Token::EQ_STRICT, resume_mode) |
| + .JumpIfTrue(&resume_with_throw) |
| + .Jump(&resume_with_return); |
|
Jarin
2016/04/18 13:55:06
Out of curiosity, do you even need the Jump?
neis
2016/04/18 14:07:52
Shhh :)
|
| + |
| + builder()->Bind(&resume_with_return); |
| + { |
| + register_allocator()->PrepareForConsecutiveAllocations(2); |
| + Register value = register_allocator()->NextConsecutiveRegister(); |
| + Register done = register_allocator()->NextConsecutiveRegister(); |
| + builder() |
| + ->MoveRegister(input, value) |
| + .LoadTrue() |
| + .StoreAccumulatorInRegister(done) |
| + .CallRuntime(Runtime::kCreateIterResultObject, value, 2); |
| + execution_control()->ReturnAccumulator(); |
| + } |
| + builder()->Bind(&resume_with_throw); |
| + builder() |
| + ->LoadAccumulatorWithRegister(input) |
| + .Throw(); |
| + |
| + builder()->Bind(&resume_with_next); |
| + builder()->LoadAccumulatorWithRegister(input); |
| + } |
| + execution_result()->SetResultInAccumulator(); |
| +} |
| void BytecodeGenerator::VisitThrow(Throw* expr) { |
| VisitForAccumulatorValue(expr->exception()); |
| builder()->SetExpressionPosition(expr); |
| builder()->Throw(); |
| - // Throw statments are modeled as expression instead of statments. These are |
| - // converted from assignment statements in Rewriter::ReWrite pass. An |
| + // Throw statements are modeled as expressions instead of statements. These |
| + // are converted from assignment statements in Rewriter::ReWrite pass. An |
| // assignment statement expects a value in the accumulator. This is a hack to |
| // avoid DCHECK fails assert accumulator has been set. |
| execution_result()->SetResultInAccumulator(); |