| Index: runtime/vm/flow_graph_builder.cc
|
| ===================================================================
|
| --- runtime/vm/flow_graph_builder.cc (revision 43515)
|
| +++ runtime/vm/flow_graph_builder.cc (working copy)
|
| @@ -1075,13 +1075,13 @@
|
| //
|
| // We distinguish those kinds of nodes via is_regular_return().
|
| //
|
| - if (function.is_async_closure() &&
|
| + if (function.IsAsyncClosure() &&
|
| (node->return_type() == ReturnNode::kRegular)) {
|
| // Temporary store the computed return value.
|
| Do(BuildStoreExprTemp(return_value));
|
|
|
| - LocalVariable* rcv_var = node->scope()->LookupVariable(
|
| - Symbols::AsyncCompleter(), false);
|
| + LocalVariable* rcv_var =
|
| + node->scope()->LookupVariable(Symbols::AsyncCompleter(), false);
|
| ASSERT(rcv_var != NULL && rcv_var->is_captured());
|
| ZoneGrowableArray<PushArgumentInstr*>* arguments =
|
| new(I) ZoneGrowableArray<PushArgumentInstr*>(2);
|
| @@ -1109,10 +1109,9 @@
|
| UnchainContexts(current_context_level);
|
| }
|
|
|
| -
|
| AddReturnExit(node->token_pos(), return_value);
|
|
|
| - if (function.is_async_closure() &&
|
| + if ((function.IsAsyncClosure() || function.IsSyncGenClosure()) &&
|
| (node->return_type() == ReturnNode::kContinuationTarget)) {
|
| JoinEntryInstr* const join = new(I) JoinEntryInstr(
|
| owner()->AllocateBlockId(), owner()->try_index());
|
| @@ -1484,6 +1483,62 @@
|
| }
|
|
|
|
|
| +void EffectGraphVisitor::BuildYieldJump(LocalVariable* old_context,
|
| + LocalVariable* iterator_param,
|
| + const intptr_t old_ctx_level,
|
| + JoinEntryInstr* target) {
|
| + // Building a jump consists of the following actions:
|
| + // * Load the generator body's iterator parameter (:iterator)
|
| + // from the current context into a temporary.
|
| + // * Restore the old context from :await_cxt_var.
|
| + // * Copy the iterator saved above into the restored context.
|
| + // * Append a Goto to the target's join.
|
| + ASSERT((iterator_param != NULL) && iterator_param->is_captured());
|
| + ASSERT((old_context != NULL) && old_context->is_captured());
|
| + // Before restoring the context we need to temporarily save the
|
| + // iterator parameter.
|
| + LocalVariable* temp_iterator_var =
|
| + EnterTempLocalScope(Bind(BuildLoadLocal(*iterator_param)));
|
| +
|
| + // Restore the saved continuation context, i.e. the context that was
|
| + // saved into :await_ctx_var before the closure suspended.
|
| + BuildRestoreContext(*old_context);
|
| +
|
| + // Store the continuation result and continuation error values into
|
| + // the restored context.
|
| +
|
| + // FlowGraphBuilder is at top context level, but the continuation
|
| + // target has possibly been recorded in a nested context (old_ctx_level).
|
| + // We need to unroll manually here.
|
| + intptr_t delta =
|
| + old_ctx_level - iterator_param->owner()->context_level();
|
| + ASSERT(delta >= 0);
|
| + Value* context = Bind(BuildCurrentContext());
|
| + while (delta-- > 0) {
|
| + context = Bind(new(I) LoadFieldInstr(
|
| + context, Context::parent_offset(), Type::ZoneHandle(I, Type::null()),
|
| + Scanner::kNoSourcePos));
|
| + }
|
| + LocalVariable* temp_context_var = EnterTempLocalScope(context);
|
| +
|
| + Value* context_val = Bind(new(I) LoadLocalInstr(*temp_context_var));
|
| + Value* store_val = Bind(new(I) LoadLocalInstr(*temp_iterator_var));
|
| + StoreInstanceFieldInstr* store = new(I) StoreInstanceFieldInstr(
|
| + Context::variable_offset(iterator_param->index()),
|
| + context_val,
|
| + store_val,
|
| + kEmitStoreBarrier,
|
| + Scanner::kNoSourcePos);
|
| + Do(store);
|
| +
|
| + Do(ExitTempLocalScope(temp_context_var));
|
| + Do(ExitTempLocalScope(temp_iterator_var));
|
| +
|
| + // Goto saved join.
|
| + Goto(target);
|
| +}
|
| +
|
| +
|
| void EffectGraphVisitor::BuildAwaitJump(LocalVariable* old_context,
|
| LocalVariable* continuation_result,
|
| LocalVariable* continuation_error,
|
| @@ -1491,31 +1546,37 @@
|
| const intptr_t old_ctx_level,
|
| JoinEntryInstr* target) {
|
| // Building a jump consists of the following actions:
|
| - // * Record the current continuation result in a temporary.
|
| - // * Restore the old context.
|
| - // * Overwrite the old context's continuation result with the temporary.
|
| + // * Load the current continuation result parameter (:async_result)
|
| + // and continuation error parameter (:async_error_param) from
|
| + // the current context into temporaries.
|
| + // * Restore the old context from :await_cxt_var.
|
| + // * Copy the result and error parameters saved above into the restored
|
| + // context.
|
| // * Append a Goto to the target's join.
|
| ASSERT((continuation_result != NULL) && continuation_result->is_captured());
|
| ASSERT((continuation_error != NULL) && continuation_error->is_captured());
|
| ASSERT((old_context != NULL) && old_context->is_captured());
|
| // Before restoring the continuation context we need to temporary save the
|
| // result and error parameter.
|
| - LocalVariable* temp_result_var = EnterTempLocalScope(
|
| - Bind(BuildLoadLocal(*continuation_result)));
|
| - LocalVariable* temp_error_var = EnterTempLocalScope(
|
| - Bind(BuildLoadLocal(*continuation_error)));
|
| - LocalVariable* temp_stack_trace_var = EnterTempLocalScope(
|
| - Bind(BuildLoadLocal(*continuation_stack_trace)));
|
| - // Restore the saved continuation context.
|
| + LocalVariable* temp_result_var =
|
| + EnterTempLocalScope(Bind(BuildLoadLocal(*continuation_result)));
|
| + LocalVariable* temp_error_var =
|
| + EnterTempLocalScope(Bind(BuildLoadLocal(*continuation_error)));
|
| + LocalVariable* temp_stack_trace_var =
|
| + EnterTempLocalScope(Bind(BuildLoadLocal(*continuation_stack_trace)));
|
| +
|
| + // Restore the saved continuation context, i.e. the context that was
|
| + // saved into :await_ctx_var before the closure suspended.
|
| BuildRestoreContext(*old_context);
|
|
|
| - // Pass over the continuation result.
|
| + // Store the continuation result and continuation error values into
|
| + // the restored context.
|
|
|
| // FlowGraphBuilder is at top context level, but the await target has possibly
|
| // been recorded in a nested context (old_ctx_level). We need to unroll
|
| // manually here.
|
| - intptr_t delta = old_ctx_level -
|
| - continuation_result->owner()->context_level();
|
| + intptr_t delta =
|
| + old_ctx_level - continuation_result->owner()->context_level();
|
| ASSERT(delta >= 0);
|
| Value* context = Bind(BuildCurrentContext());
|
| while (delta-- > 0) {
|
| @@ -3702,6 +3763,7 @@
|
| // label: SourceLabel }
|
| void EffectGraphVisitor::VisitSequenceNode(SequenceNode* node) {
|
| LocalScope* scope = node->scope();
|
| + const Function& function = owner()->function();
|
| const intptr_t num_context_variables =
|
| (scope != NULL) ? scope->num_context_variables() : 0;
|
| const bool is_top_level_sequence =
|
| @@ -3734,7 +3796,6 @@
|
| // the captured parameters from the frame into the context.
|
| if (is_top_level_sequence) {
|
| ASSERT(scope->context_level() == 1);
|
| - const Function& function = owner()->function();
|
| const int num_params = function.NumParameters();
|
| int param_frame_index = (num_params == function.num_fixed_parameters()) ?
|
| (kParamEndSlotFromFp + num_params) : kFirstLocalSlotFromFp;
|
| @@ -3772,7 +3833,6 @@
|
|
|
| // This check may be deleted if the generated code is leaf.
|
| // Native functions don't need a stack check at entry.
|
| - const Function& function = owner()->function();
|
| if (is_top_level_sequence && !function.is_native()) {
|
| // Always allocate CheckOverflowInstr so that deopt-ids match regardless
|
| // if we inline or not.
|
| @@ -3789,7 +3849,6 @@
|
| }
|
|
|
| if (Isolate::Current()->TypeChecksEnabled() && is_top_level_sequence) {
|
| - const Function& function = owner()->function();
|
| const int num_params = function.NumParameters();
|
| int pos = 0;
|
| if (function.IsConstructor()) {
|
| @@ -3818,10 +3877,12 @@
|
| }
|
|
|
| // Continuation part:
|
| - // If this node sequence is the body of an async closure leave room for a
|
| - // preamble. The preamble is generated after visiting the body.
|
| + // If this node sequence is the body of a function with continuations,
|
| + // leave room for a preamble.
|
| + // The preamble is generated after visiting the body.
|
| GotoInstr* preamble_start = NULL;
|
| - if (is_top_level_sequence && (owner()->function().is_async_closure())) {
|
| + if (is_top_level_sequence &&
|
| + (function.IsAsyncClosure() || function.IsSyncGenClosure())) {
|
| JoinEntryInstr* preamble_end = new(I) JoinEntryInstr(
|
| owner()->AllocateBlockId(), owner()->try_index());
|
| ASSERT(exit() != NULL);
|
| @@ -3844,33 +3905,28 @@
|
| }
|
|
|
| // Continuation part:
|
| - // After generating the CFG for the body we can create the preamble because we
|
| - // know exactly how many continuation states we need.
|
| - if (is_top_level_sequence && (owner()->function().is_async_closure())) {
|
| + // After generating the CFG for the body we can create the preamble
|
| + // because we know exactly how many continuation states we need.
|
| + if (is_top_level_sequence &&
|
| + (function.IsAsyncClosure() || function.IsSyncGenClosure())) {
|
| ASSERT(preamble_start != NULL);
|
| // We are at the top level. Fetch the corresponding scope.
|
| LocalScope* top_scope = node->scope();
|
| LocalVariable* jump_var = top_scope->LookupVariable(
|
| Symbols::AwaitJumpVar(), false);
|
| ASSERT(jump_var != NULL && jump_var->is_captured());
|
| -
|
| Instruction* saved_entry = entry_;
|
| Instruction* saved_exit = exit_;
|
| entry_ = NULL;
|
| exit_ = NULL;
|
|
|
| - LoadLocalNode* load_jump_count = new(I) LoadLocalNode(
|
| - Scanner::kNoSourcePos, jump_var);
|
| + LoadLocalNode* load_jump_count =
|
| + new(I) LoadLocalNode(Scanner::kNoSourcePos, jump_var);
|
| ComparisonNode* check_jump_count;
|
| const intptr_t num_await_states = owner()->await_joins()->length();
|
| +
|
| LocalVariable* old_context = top_scope->LookupVariable(
|
| Symbols::AwaitContextVar(), false);
|
| - LocalVariable* continuation_result = top_scope->LookupVariable(
|
| - Symbols::AsyncOperationParam(), false);
|
| - LocalVariable* continuation_error = top_scope->LookupVariable(
|
| - Symbols::AsyncOperationErrorParam(), false);
|
| - LocalVariable* continuation_stack_trace = top_scope->LookupVariable(
|
| - Symbols::AsyncOperationStackTraceParam(), false);
|
| for (intptr_t i = 0; i < num_await_states; i++) {
|
| check_jump_count = new(I) ComparisonNode(
|
| Scanner::kNoSourcePos,
|
| @@ -3883,14 +3939,32 @@
|
| EffectGraphVisitor for_true(owner());
|
| EffectGraphVisitor for_false(owner());
|
|
|
| - for_true.BuildAwaitJump(old_context,
|
| - continuation_result,
|
| - continuation_error,
|
| - continuation_stack_trace,
|
| - (*owner()->await_levels())[i],
|
| - (*owner()->await_joins())[i]);
|
| + if (function.IsAsyncClosure()) {
|
| + LocalVariable* result_param =
|
| + top_scope->LookupVariable(Symbols::AsyncOperationParam(), false);
|
| + LocalVariable* error_param =
|
| + top_scope->LookupVariable(Symbols::AsyncOperationErrorParam(),
|
| + false);
|
| + LocalVariable* stack_trace_param =
|
| + top_scope->LookupVariable(Symbols::AsyncOperationStackTraceParam(),
|
| + false);
|
| + for_true.BuildAwaitJump(old_context,
|
| + result_param,
|
| + error_param,
|
| + stack_trace_param,
|
| + (*owner()->await_levels())[i],
|
| + (*owner()->await_joins())[i]);
|
| + } else {
|
| + ASSERT(function.IsSyncGenClosure());
|
| + LocalVariable* iterator_param =
|
| + top_scope->LookupVariable(Symbols::IteratorParameter(), false);
|
| + for_true.BuildYieldJump(old_context,
|
| + iterator_param,
|
| + (*owner()->await_levels())[i],
|
| + (*owner()->await_joins())[i]);
|
| + }
|
| +
|
| Join(for_test, for_true, for_false);
|
| -
|
| if (i == 0) {
|
| // Manually link up the preamble start.
|
| preamble_start->previous()->set_next(for_test.entry());
|
|
|