Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1128)

Unified Diff: src/interpreter/bytecode-generator.cc

Issue 2664083002: [ignition] desugar async functions/generators/modules in BytecodeGenerator
Patch Set: make it a little easier to read Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/interpreter/bytecode-generator.h ('k') | src/parsing/parser.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/interpreter/bytecode-generator.cc
diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc
index c28d4687d8a8cf5b742626739d8cbeb50ee9dc54..e8f5e442209d61ba9d5696f6f4855a74f6cc39f1 100644
--- a/src/interpreter/bytecode-generator.cc
+++ b/src/interpreter/bytecode-generator.cc
@@ -601,6 +601,7 @@ BytecodeGenerator::BytecodeGenerator(CompilationInfo* info)
generator_resume_points_(info->literal()->yield_count(), info->zone()),
generator_state_(),
loop_depth_(0),
+ catch_prediction_(HandlerTable::UNCAUGHT),
home_object_symbol_(info->isolate()->factory()->home_object_symbol()),
iterator_symbol_(info->isolate()->factory()->iterator_symbol()),
prototype_string_(info->isolate()->factory()->prototype_string()),
@@ -696,14 +697,6 @@ void BytecodeGenerator::GenerateBytecode(uintptr_t stack_limit) {
GenerateBytecodeBody();
}
- // In generator functions, we may not have visited every yield in the AST
- // since we skip some obviously dead code. Hence the generated bytecode may
- // contain jumps to unbound labels (resume points that will never be used).
- // We bind these now.
- for (auto& label : generator_resume_points_) {
- if (!label.is_bound()) builder()->Bind(&label);
- }
-
// Emit an implicit return instruction in case control flow can fall off the
// end of the function without an explicit return being present on all paths.
if (builder()->RequiresImplicitReturn()) {
@@ -739,10 +732,220 @@ void BytecodeGenerator::GenerateBytecodeBody() {
// Perform a stack-check before the body.
builder()->StackCheck(info()->literal()->start_position());
+ // Build assignment to variable <function name> if function is a named
+ // expression and the variable is used.
+ if (info()->literal()->is_named_expression()) {
+ Variable* function_var = scope()->function_var();
+ if (function_var != nullptr && !function_var->IsUnallocated()) {
+ builder()->LoadAccumulatorWithRegister(Register::function_closure());
+ BuildVariableAssignment(function_var, Token::INIT,
+ FeedbackVectorSlot::Invalid(),
+ HoleCheckMode::kElided);
+ }
+ }
rmcilroy 2017/02/06 22:27:50 This change doesn't seem related to the generator
caitp 2017/02/06 22:48:23 well, I would love to do it in a separate CL, but
rmcilroy 2017/02/07 17:24:41 OK I can live with this change in this CL, thanks
caitp 2017/02/07 18:13:08 Actually, I brought this up in the meeting today,
+
+ if (IsAsyncFunction(info()->literal()->kind())) {
+ return GenerateBytecodeBodyForAsyncFunction();
+ }
+ if (IsGeneratorFunction(info()->literal()->kind())) {
+ return GenerateBytecodeBodyForGenerator();
+ }
+ if (IsModule(info()->literal()->kind())) {
+ return GenerateBytecodeBodyForModule();
+ }
rmcilroy 2017/02/06 22:27:50 Thanks for splitting the logic out of GenerateByte
caitp 2017/02/06 22:48:23 I'm not entirely sure how this would work due to h
rmcilroy 2017/02/07 17:24:41 Yeah your right regarding RAII ControlScopes. I'd
+
+ Block* parameter_init_block = info()->literal()->parameter_init_block();
// Visit statements in the function body.
+ if (parameter_init_block != nullptr) {
+ VisitBlock(parameter_init_block);
+ }
rmcilroy 2017/02/06 22:27:50 Any reason for this change? Could we just do this
caitp 2017/02/06 22:48:24 Async functions need to incorporate the parameter
rmcilroy 2017/02/07 17:24:41 I see. Is it necessary that the parameter initiali
VisitStatements(info()->literal()->body());
}
+void BytecodeGenerator::BuildAllocateAndStoreJSGeneratorObject(
+ BuildJSGeneratorObject tag) {
+ RegisterAllocationScope register_scope(this);
+ RegisterList args = register_allocator()->NewRegisterList(2);
+ builder()->MoveRegister(Register::function_closure(), args[0]);
+
+ int yield_position = info()->literal()->position();
rmcilroy 2017/02/06 22:27:50 Could you pass through literal as an argument rath
caitp 2017/02/06 22:48:23 For the cases that would use this, aren't they alw
caitp 2017/02/06 23:29:36 nvm, thought this was specifically about yield_pos
rmcilroy 2017/02/07 17:24:41 Yeah I was meaning for all the uses in the new fun
+ builder()->SetExpressionPosition(yield_position);
+
+ if (IsArrowFunction(info()->literal()->kind())) {
+ // Lexical `this`
+ builder()->LoadUndefined().StoreAccumulatorInRegister(args[1]);
+ } else {
+ // Receiver parameter
+ builder()->MoveRegister(builder()->Parameter(0), args[1]);
+ }
+ builder()->CallRuntime(Runtime::kCreateJSGeneratorObject, args);
+
+ Variable* var_generator = scope()->generator_object_var();
+ DCHECK_NOT_NULL(var_generator);
+
+ switch (tag) {
+ case BuildJSGeneratorObject::kInitialYield: {
+ Register generator = args[0];
+ builder()->StoreAccumulatorInRegister(generator);
+ BuildVariableAssignment(var_generator, Token::INIT,
+ FeedbackVectorSlot::Invalid(),
+ HoleCheckMode::kElided);
+ BuildYield(0, generator, generator);
+ BuildYieldResumePoint(0, Yield::kOnExceptionThrow, generator,
+ yield_position);
+ break;
+ }
+ case BuildJSGeneratorObject::kNoInitialYield: {
+ BuildVariableAssignment(var_generator, Token::INIT,
+ FeedbackVectorSlot::Invalid(),
+ HoleCheckMode::kElided);
+ break;
+ }
+ }
+}
+
+void BytecodeGenerator::BuildAllocateAndStoreJSPromise() {
+ Variable* var_promise = scope()->promise_var();
+ DCHECK_NOT_NULL(var_promise);
+
+ RegisterAllocationScope register_scope(this);
+ RegisterList args = register_allocator()->NewRegisterList(1);
+ builder()->LoadUndefined().StoreAccumulatorInRegister(args[0]).CallJSRuntime(
+ Context::ASYNC_FUNCTION_PROMISE_CREATE_INDEX, args);
+ BuildVariableAssignment(var_promise, Token::INIT,
+ FeedbackVectorSlot::Invalid(),
+ HoleCheckMode::kElided);
+}
+
+void BytecodeGenerator::BindUnboundGeneratorResumePoints() {
+ DCHECK(IsResumableFunction(info()->literal()->kind()));
+ for (auto& label : generator_resume_points_) {
+ if (!label.is_bound()) builder()->Bind(&label);
+ }
+}
+
+void BytecodeGenerator::GenerateBytecodeBodyForGenerator() {
+ DCHECK(IsGeneratorFunction(info()->literal()->kind()));
+
+ // Desugar parameters before generator is allocated.
+ Block* const parameter_init_block = info()->literal()->parameter_init_block();
+ if (parameter_init_block != nullptr) {
+ // Initialize non-simple parameters before initial yield.
+ VisitBlock(parameter_init_block);
+ }
+
+ BuildTryFinally(
+ [=]() { // Try ...
+ BuildAllocateAndStoreJSGeneratorObject(
+ BuildJSGeneratorObject::kInitialYield);
+ VisitStatements(info()->literal()->body());
+ },
+ [=]() { // Finally ...
+ Variable* var_generator = scope()->generator_object_var();
+ DCHECK_NOT_NULL(var_generator);
+
+ BuildVariableLoad(var_generator, FeedbackVectorSlot::Invalid(),
+ HoleCheckMode::kElided);
+ RegisterAllocationScope register_scope(this);
+ Register generator = register_allocator()->NewRegister();
+ builder()->StoreAccumulatorInRegister(generator).CallRuntime(
+ Runtime::kInlineGeneratorClose, generator);
+ });
+
+ // In generator functions, we may not have visited every yield in the AST
+ // since we skip some obviously dead code. Hence the generated bytecode may
+ // contain jumps to unbound labels (resume points that will never be used).
+ // We bind these now.
+ BindUnboundGeneratorResumePoints();
+
+ if (builder()->RequiresImplicitReturn()) {
rmcilroy 2017/02/06 22:27:50 From here to the end shouldn't be necessary (it's
caitp 2017/02/06 22:48:24 The one in GenerateBytecode uses BuildReturn(), wh
rmcilroy 2017/02/07 17:24:41 Let's make that change in the followup CL which ch
+ // Unreachable, but required for BytecodeArrayBuilder
+ builder()->LoadUndefined().Return();
+ }
+ DCHECK(!builder()->RequiresImplicitReturn());
+}
+
+void BytecodeGenerator::GenerateBytecodeBodyForModule() {
+ DCHECK(IsModule(info()->literal()->kind()));
+
+ // Modules do not have non-simple parameters
+ DCHECK_NULL(info()->literal()->parameter_init_block());
+
+ BuildAllocateAndStoreJSGeneratorObject(BuildJSGeneratorObject::kInitialYield);
+ VisitStatements(info()->literal()->body());
+
+ // In modules, we may not have visited every yield in the AST
+ // since we skip some obviously dead code. Hence the generated bytecode may
+ // contain jumps to unbound labels (resume points that will never be used).
+ // We bind these now.
+ BindUnboundGeneratorResumePoints();
+}
+
+void BytecodeGenerator::GenerateBytecodeBodyForAsyncFunction() {
+ DCHECK(IsAsyncFunction(info()->literal()->kind()));
+
+ BuildAllocateAndStoreJSPromise();
+
+ BuildTryCatch(
+ HandlerTable::ASYNC_AWAIT,
+ [=]() {
rmcilroy 2017/02/06 22:27:49 I don't like these lambdas, and the function point
caitp 2017/02/06 22:48:24 I'll give that a try.
+ // Try ...
+ Block* const parameter_init_block =
+ info()->literal()->parameter_init_block();
+ if (parameter_init_block != nullptr) {
+ // Initialize non-simple parameters if necessary, reject Promise in
+ // case of exception
+ VisitBlock(parameter_init_block);
+ }
+
+ BuildAllocateAndStoreJSGeneratorObject(
+ BuildJSGeneratorObject::kNoInitialYield);
+
+ // Finish generating function body
+ VisitStatements(info()->literal()->body());
+ },
+ [=](Register) {
+ // Catch ...
+ // Caught exception is used to reject Promise without emitting a debug
+ // event.
+ RegisterAllocationScope register_scope(this);
+ RegisterList args = register_allocator()->NewRegisterList(4);
+ Register promise = args[1];
+ Register exception = args[2];
+
+ builder()
+ ->StoreAccumulatorInRegister(exception)
+ .LoadTheHole()
+ .SetPendingMessage();
+
+ Variable* var_promise = scope()->promise_var();
+ DCHECK_NOT_NULL(var_promise);
+ BuildVariableLoad(var_promise, FeedbackVectorSlot::Invalid(),
+ HoleCheckMode::kElided);
+ builder()
+ ->StoreAccumulatorInRegister(promise)
+ .LoadUndefined()
+ .StoreAccumulatorInRegister(args[0])
+ .LoadFalse()
+ .StoreAccumulatorInRegister(args[3])
+ .CallJSRuntime(Context::PROMISE_INTERNAL_REJECT_INDEX, args)
+ .LoadAccumulatorWithRegister(promise)
+ .Return();
+ });
+
+ // In async functions, we may not have visited every yield in the AST
+ // since we skip some obviously dead code. Hence the generated bytecode may
+ // contain jumps to unbound labels (resume points that will never be used).
+ // We bind these now.
+ BindUnboundGeneratorResumePoints();
+
+ if (builder()->RequiresImplicitReturn()) {
rmcilroy 2017/02/06 22:27:50 Ditto
+ // Unreachable, but required for BytecodeArrayBuilder
+ builder()->LoadUndefined().Return();
+ }
+ DCHECK(!builder()->RequiresImplicitReturn());
+}
+
void BytecodeGenerator::BuildIndexedJump(Register index, size_t start_index,
size_t size,
ZoneVector<BytecodeLabel>& targets) {
@@ -1334,8 +1537,11 @@ void BytecodeGenerator::VisitForOfStatement(ForOfStatement* stmt) {
loop_builder.EndLoop();
}
-void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
- TryCatchBuilder try_control_builder(builder(), stmt->catch_prediction());
+void BytecodeGenerator::BuildTryCatch(
+ HandlerTable::CatchPrediction catch_prediction,
+ std::function<void()> build_try,
+ std::function<void(Register)> build_catch) {
+ TryCatchBuilder try_control_builder(builder(), catch_prediction);
// Preserve the context in a dedicated register, so that it can be restored
// when the handler is entered by the stack-unwinding machinery.
@@ -1348,29 +1554,43 @@ void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
try_control_builder.BeginTry(context);
{
ControlScopeForTryCatch scope(this, &try_control_builder);
- Visit(stmt->try_block());
+ HandlerTable::CatchPrediction last_catch_prediction = catch_prediction_;
+ if (catch_prediction != HandlerTable::UNCAUGHT) {
+ catch_prediction_ = catch_prediction;
+ }
+ build_try();
+ catch_prediction_ = last_catch_prediction;
}
try_control_builder.EndTry();
- // Create a catch scope that binds the exception.
- BuildNewLocalCatchContext(stmt->variable(), stmt->scope());
- builder()->StoreAccumulatorInRegister(context);
+ // Evaluate the catch-block.
+ build_catch(context);
+ try_control_builder.EndCatch();
+}
- // If requested, clear message object as we enter the catch block.
- if (stmt->clear_pending_message()) {
- builder()->LoadTheHole().SetPendingMessage();
- }
+void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
+ BuildTryCatch(stmt->catch_prediction(), [=]() { Visit(stmt->try_block()); },
+ [=](Register context) {
+ // Create a catch scope that binds the exception.
+ BuildNewLocalCatchContext(stmt->variable(), stmt->scope());
+ builder()->StoreAccumulatorInRegister(context);
- // Load the catch context into the accumulator.
- builder()->LoadAccumulatorWithRegister(context);
+ // I requested, clear message object as we enter the catch
+ // block.
+ if (stmt->clear_pending_message()) {
+ builder()->LoadTheHole().SetPendingMessage();
+ }
- // Evaluate the catch-block.
- VisitInScope(stmt->catch_block(), stmt->scope());
- try_control_builder.EndCatch();
+ // Load the catch context into the accumulator.
+ builder()->LoadAccumulatorWithRegister(context);
+
+ VisitInScope(stmt->catch_block(), stmt->scope());
+ });
}
-void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
- TryFinallyBuilder try_control_builder(builder(), stmt->catch_prediction());
+void BytecodeGenerator::BuildTryFinally(std::function<void()> build_try,
+ std::function<void()> build_finally) {
+ TryFinallyBuilder try_control_builder(builder(), catch_prediction_);
// We keep a record of all paths that enter the finally-block to be able to
// dispatch to the correct continuation point after the statements in the
@@ -1402,7 +1622,7 @@ void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
try_control_builder.BeginTry(context);
{
ControlScopeForTryFinally scope(this, &try_control_builder, &commands);
- Visit(stmt->try_block());
+ build_try();
}
try_control_builder.EndTry();
@@ -1421,7 +1641,7 @@ void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
message);
// Evaluate the finally-block.
- Visit(stmt->finally_block());
+ build_finally();
try_control_builder.EndFinally();
// Pending message object is restored on exit.
@@ -1431,6 +1651,11 @@ void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
commands.ApplyDeferredCommands();
}
+void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
+ BuildTryFinally([=]() { Visit(stmt->try_block()); },
+ [=]() { Visit(stmt->finally_block()); });
+}
+
void BytecodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) {
builder()->SetStatementPosition(stmt);
builder()->Debugger();
@@ -2287,20 +2512,31 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) {
}
}
-void BytecodeGenerator::VisitYield(Yield* expr) {
- builder()->SetExpressionPosition(expr);
- Register value = VisitForRegisterValue(expr->expression());
-
- Register generator = VisitForRegisterValue(expr->generator_object());
+void BytecodeGenerator::BuildYield(int yield_id, Register generator,
+ Register value) {
+ DCHECK_GE(yield_id, 0);
+ DCHECK_LT(yield_id, generator_resume_points_.size());
+ DCHECK(generator.is_valid());
+ DCHECK(value.is_valid());
// Save context, registers, and state. Then return.
builder()
- ->LoadLiteral(Smi::FromInt(expr->yield_id()))
+ ->LoadLiteral(Smi::FromInt(yield_id))
.SuspendGenerator(generator)
.LoadAccumulatorWithRegister(value)
.Return(); // Hard return (ignore any finally blocks).
+}
- builder()->Bind(&(generator_resume_points_[expr->yield_id()]));
+void BytecodeGenerator::BuildYieldResumePoint(int yield_id,
+ Yield::OnException on_exception,
+ Register generator,
+ int position) {
+ DCHECK_GE(yield_id, 0);
+ DCHECK_LT(yield_id, generator_resume_points_.size());
+ DCHECK(generator.is_valid());
+
+ DCHECK(!generator_resume_points_[yield_id].is_bound());
+ builder()->Bind(&(generator_resume_points_[yield_id]));
// Upon resume, we continue here.
{
@@ -2328,6 +2564,7 @@ void BytecodeGenerator::VisitYield(Yield* expr) {
BytecodeLabel resume_with_return;
BytecodeLabel resume_with_throw;
+ // TODO(caitp): Don't generate `resume_with_return` block for non-generators
builder()
->LoadLiteral(Smi::FromInt(JSGeneratorObject::kNext))
.CompareOperation(Token::EQ_STRICT, resume_mode)
@@ -2349,9 +2586,9 @@ void BytecodeGenerator::VisitYield(Yield* expr) {
}
builder()->Bind(&resume_with_throw);
- builder()->SetExpressionPosition(expr);
+ builder()->SetExpressionPosition(position);
builder()->LoadAccumulatorWithRegister(input);
- if (expr->rethrow_on_exception()) {
+ if (on_exception == Yield::kOnExceptionRethrow) {
builder()->ReThrow();
} else {
builder()->Throw();
@@ -2362,6 +2599,17 @@ void BytecodeGenerator::VisitYield(Yield* expr) {
}
}
+void BytecodeGenerator::VisitYield(Yield* expr) {
+ Register generator = VisitForRegisterValue(expr->generator_object());
+
+ builder()->SetExpressionPosition(expr);
+ Register value = VisitForRegisterValue(expr->expression());
+
+ BuildYield(expr->yield_id(), generator, value);
+ BuildYieldResumePoint(expr->yield_id(), expr->on_exception(), generator,
+ expr->position());
+}
+
void BytecodeGenerator::VisitThrow(Throw* expr) {
VisitForAccumulatorValue(expr->exception());
builder()->SetExpressionPosition(expr);
« no previous file with comments | « src/interpreter/bytecode-generator.h ('k') | src/parsing/parser.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698