Chromium Code Reviews| Index: runtime/vm/flow_graph_builder.cc |
| =================================================================== |
| --- runtime/vm/flow_graph_builder.cc (revision 43415) |
| +++ runtime/vm/flow_graph_builder.cc (working copy) |
| @@ -1074,13 +1074,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); |
| @@ -1108,10 +1108,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()); |
| @@ -1481,35 +1480,97 @@ |
| } |
| +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, |
| 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))); |
| - // Restore the saved continuation context. |
| + LocalVariable* temp_result_var = |
| + EnterTempLocalScope(Bind(BuildLoadLocal(*continuation_result))); |
| + LocalVariable* temp_error_var = |
| + EnterTempLocalScope(Bind(BuildLoadLocal(*continuation_error))); |
| + |
| + // 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) { |
| @@ -3734,6 +3795,7 @@ |
| // label: SourceLabel } |
| void EffectGraphVisitor::VisitSequenceNode(SequenceNode* node) { |
| LocalScope* scope = node->scope(); |
| + const Function& function = owner()->parsed_function().function(); |
|
srdjan
2015/02/04 19:29:12
owner()->function();
hausner
2015/02/04 22:09:24
Done.
|
| const intptr_t num_context_variables = |
| (scope != NULL) ? scope->num_context_variables() : 0; |
| const bool is_top_level_sequence = |
| @@ -3766,7 +3828,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; |
| @@ -3804,7 +3865,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. |
| @@ -3821,7 +3881,6 @@ |
| } |
| if (FLAG_enable_type_checks && is_top_level_sequence) { |
| - const Function& function = owner()->function(); |
| const int num_params = function.NumParameters(); |
| int pos = 0; |
| if (function.IsConstructor()) { |
| @@ -3850,10 +3909,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); |
| @@ -3876,31 +3937,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); |
| for (intptr_t i = 0; i < num_await_states; i++) { |
| check_jump_count = new(I) ComparisonNode( |
| Scanner::kNoSourcePos, |
| @@ -3913,11 +3971,26 @@ |
| EffectGraphVisitor for_true(owner()); |
| EffectGraphVisitor for_false(owner()); |
| - for_true.BuildAwaitJump(old_context, |
| - continuation_result, |
| - continuation_error, |
| - (*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); |
| + for_true.BuildAwaitJump(old_context, |
| + result_param, |
| + error_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) { |