Chromium Code Reviews| Index: src/interpreter/bytecode-generator.cc |
| diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc |
| index 0227f13b146454a347e623159b6606ce2325bc14..a488fc04b1272032a21a20d7aa7e57e338e8570e 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), |
| + resume_points_(0, zone), |
| try_catch_nesting_level_(0), |
| - try_finally_nesting_level_(0) { |
| + try_finally_nesting_level_(0), |
| + yield_count_(0) { |
| InitializeAstVisitor(isolate); |
| } |
| @@ -586,6 +588,10 @@ Handle<BytecodeArray> BytecodeGenerator::MakeBytecode(CompilationInfo* info) { |
| // Initialize control scope. |
| ControlScopeForTopLevel control(this); |
| + if (IsGeneratorFunction(info->literal()->kind())) { |
| + BuildGeneratorPrologue(); |
| + } |
| + |
| // 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::BuildGeneratorPrologue() { |
| + resume_points_.clear(); |
|
rmcilroy
2016/04/15 13:17:46
Do we need the clear? it should be empty when we r
neis
2016/04/18 08:04:17
MakeBytecode could be called several times on the
rmcilroy
2016/04/18 10:01:57
No it's only ever used for a single compile info.
neis
2016/04/18 14:07:52
Ok. After your change I can probably initialize it
|
| + 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 < resume_points_.size(); ++i) { |
| + builder() |
| + ->LoadLiteral(Smi::FromInt(static_cast<int>(i))) |
| + .CompareOperation(Token::Value::EQ_STRICT, state) |
| + .JumpIfTrue(&(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,83 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) { |
| execution_result()->SetResultInAccumulator(); |
| } |
| +void BytecodeGenerator::VisitYield(Yield* expr) { |
| + int id = yield_count_++; |
|
rmcilroy
2016/04/15 13:17:46
Rather than doing this id naming implicitly, would
neis
2016/04/18 08:04:17
I don't think that would be safer and I don't see
rmcilroy
2016/04/18 10:01:57
OK. I'm fine with this for this CL, but maybe it's
neis
2016/04/18 14:07:52
OK. Also, I probably need to move them into array
|
| -void BytecodeGenerator::VisitYield(Yield* expr) { UNIMPLEMENTED(); } |
| + builder()->SetExpressionPosition(expr); |
| + Register value = VisitForRegisterValue(expr->expression()); |
|
rmcilroy
2016/04/15 13:17:46
nit - maybe scope the pre-yield, and post-resume p
neis
2016/04/18 08:04:17
Thanks, I didn't think of that. I added a scope f
|
| + |
| + 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(&(resume_points_[id])); |
| + // Upon resume, we continue here. |
| + |
| + 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) |
|
rmcilroy
2016/04/15 13:17:46
Maybe you could have a runtime function which retu
neis
2016/04/18 08:04:17
Let's keep it as is for now. I think we want to g
rmcilroy
2016/04/18 10:01:57
Acknowledged.
|
| + .StoreAccumulatorInRegister(resume_mode); |
| + |
| + // Dispatch on resume mode. |
| + { |
| + BytecodeLabel resume_with_next; |
| + BytecodeLabel resume_with_return; |
| + BytecodeLabel resume_with_throw; |
| + |
| + builder() |
| + ->LoadLiteral(Smi::FromInt(JSGeneratorObject::kReturn)) |
| + .CompareOperation(Token::EQ_STRICT, resume_mode) |
| + .JumpIfTrue(&resume_with_return) |
| + .LoadLiteral(Smi::FromInt(JSGeneratorObject::kThrow)) |
| + .CompareOperation(Token::EQ_STRICT, resume_mode) |
| + .JumpIfTrue(&resume_with_throw) |
| + .Jump(&resume_with_next); |
|
rmcilroy
2016/04/15 13:17:46
(micro optimization) - presumably JSGeneratorObjec
neis
2016/04/18 08:04:17
Done.
|
| + |
| + 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 |
|
rmcilroy
2016/04/15 13:17:46
Thanks!
|
| // assignment statement expects a value in the accumulator. This is a hack to |
| // avoid DCHECK fails assert accumulator has been set. |
| execution_result()->SetResultInAccumulator(); |