Chromium Code Reviews| Index: runtime/vm/flow_graph_builder.cc |
| diff --git a/runtime/vm/flow_graph_builder.cc b/runtime/vm/flow_graph_builder.cc |
| index aae3203a5d2c00beefa8c56363d163d22f45e81e..319d205a8a88a387f2c7178b9b5bdf7fcb9eb49c 100644 |
| --- a/runtime/vm/flow_graph_builder.cc |
| +++ b/runtime/vm/flow_graph_builder.cc |
| @@ -260,7 +260,10 @@ FlowGraphBuilder::FlowGraphBuilder( |
| args_pushed_(0), |
| nesting_stack_(NULL), |
| osr_id_(osr_id), |
| - is_optimizing_(is_optimizing) { } |
| + is_optimizing_(is_optimizing), |
| + jump_cnt_(0), |
| + await_joins_(new(I) ZoneGrowableArray<JoinEntryInstr*>()), |
| + await_levels_(new(I) ZoneGrowableArray<intptr_t>()) { } |
| void FlowGraphBuilder::AddCatchEntry(CatchBlockEntryInstr* entry) { |
| @@ -1038,15 +1041,6 @@ void EffectGraphVisitor::VisitReturnNode(ReturnNode* node) { |
| } |
| } |
| - intptr_t current_context_level = owner()->context_level(); |
| - ASSERT(current_context_level >= 0); |
| - if (owner()->parsed_function()->saved_entry_context_var() != NULL) { |
| - // CTX on entry was saved, but not linked as context parent. |
| - BuildRestoreContext(*owner()->parsed_function()->saved_entry_context_var()); |
| - } else { |
| - UnchainContexts(current_context_level); |
| - } |
| - |
| // Async functions contain two types of return statements: |
| // 1) Returns that should complete the completer once all finally blocks have |
| // been inlined (call: :async_completer.complete(return_value)). These |
| @@ -1056,7 +1050,8 @@ void EffectGraphVisitor::VisitReturnNode(ReturnNode* node) { |
| // |
| // We distinguish those kinds of nodes via is_regular_return(). |
| // |
| - if (function.is_async_closure() && node->is_regular_return()) { |
| + if (function.is_async_closure() && |
| + (node->return_type() == ReturnNode::kRegular)) { |
| // Temporary store the computed return value. |
| Do(BuildStoreExprTemp(return_value)); |
| @@ -1083,6 +1078,16 @@ void EffectGraphVisitor::VisitReturnNode(ReturnNode* node) { |
| return_value = BuildNullValue(); |
| } |
| + intptr_t current_context_level = owner()->context_level(); |
| + ASSERT(current_context_level >= 0); |
| + if (owner()->parsed_function()->saved_entry_context_var() != NULL) { |
| + // CTX on entry was saved, but not linked as context parent. |
| + BuildRestoreContext(*owner()->parsed_function()->saved_entry_context_var()); |
| + } else { |
| + UnchainContexts(current_context_level); |
| + } |
| + |
| + |
| AddReturnExit(node->token_pos(), return_value); |
| } |
| @@ -1444,6 +1449,58 @@ AssertAssignableInstr* EffectGraphVisitor::BuildAssertAssignable( |
| } |
| +void EffectGraphVisitor::BuildAwaitJump(LocalScope* lookup_scope, |
| + 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. |
| + // * Append a Goto to the target's join. |
| + LocalVariable* old_ctx = lookup_scope->LookupVariable( |
| + Symbols::AwaitContextVar(), false); |
| + LocalVariable* continuation_result = lookup_scope->LookupVariable( |
| + Symbols::AsyncOperationParam(), false); |
| + ASSERT(continuation_result != NULL && continuation_result->is_captured()); |
|
srdjan
2014/08/26 15:54:33
Add parentheses
Michael Lippautz (Google)
2014/08/26 16:45:48
Done.
|
| + ASSERT(old_ctx != NULL && old_ctx->is_captured()); |
|
srdjan
2014/08/26 15:54:33
ditto
Michael Lippautz (Google)
2014/08/26 16:45:48
Done.
|
| + // Before resoring the continuation context we need to temporary save the |
|
srdjan
2014/08/26 15:54:33
s/resoring/restoring/
Michael Lippautz (Google)
2014/08/26 16:45:48
Done.
|
| + // current continuation result. |
| + Value* continuation_result_value = Bind(BuildLoadLocal(*continuation_result)); |
| + Do(BuildStoreExprTemp(continuation_result_value)); |
| + |
| + // Restore the saved continuation context. |
| + BuildRestoreContext(*old_ctx); |
| + |
| + // Pass over the continuation result. |
| + Value* saved_continuation_result = Bind(BuildLoadExprTemp()); |
| + // 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. |
| + LocalVariable* tmp_var = EnterTempLocalScope(saved_continuation_result); |
| + intptr_t delta = old_ctx_level - |
| + continuation_result->owner()->context_level(); |
| + ASSERT(delta >= 0); |
| + Value* context = Bind(new(I) CurrentContextInstr()); |
| + while (delta-- > 0) { |
| + context = Bind(new(I) LoadFieldInstr( |
| + context, Context::parent_offset(), Type::ZoneHandle(I, Type::null()), |
| + Scanner::kNoSourcePos)); |
| + } |
| + Value* tmp_val = Bind(new(I) LoadLocalInstr(*tmp_var)); |
| + StoreInstanceFieldInstr* store = new(I) StoreInstanceFieldInstr( |
| + Context::variable_offset(continuation_result->index()), |
| + context, |
| + tmp_val, |
| + kEmitStoreBarrier, |
| + Scanner::kNoSourcePos); |
| + Do(store); |
| + Do(ExitTempLocalScope(tmp_var)); |
| + |
| + // Goto saved join. |
| + Goto(target); |
| +} |
| + |
| + |
| // Used for type casts and to test assignments. |
| Value* EffectGraphVisitor::BuildAssignableValue(intptr_t token_pos, |
| Value* value, |
| @@ -2120,6 +2177,47 @@ void EffectGraphVisitor::VisitAwaitNode(AwaitNode* node) { |
| } |
| +void EffectGraphVisitor::VisitAwaitMarkerNode(AwaitMarkerNode* node) { |
| + if (node->type() == AwaitMarkerNode::kNewContinuationState) { |
| + // We need to create a new await state which involves: |
| + // * Increase the jump counter. Sanity check against the list of targets. |
| + // * Save the current context for resuming. |
| + ASSERT(node->scope() != NULL); |
| + LocalVariable* jump_var = node->scope()->LookupVariable( |
| + Symbols::AwaitJumpVar(), false); |
| + LocalVariable* ctx_var = node->scope()->LookupVariable( |
| + Symbols::AwaitContextVar(), false); |
| + ASSERT(jump_var != NULL && jump_var->is_captured()); |
| + ASSERT(ctx_var != NULL && ctx_var->is_captured()); |
|
srdjan
2014/08/26 15:54:33
parentheses
Michael Lippautz (Google)
2014/08/26 16:45:48
Done.
|
| + const intptr_t jump_cnt = owner()->next_await_counter(); |
| + ASSERT(jump_cnt >= 0); |
| + // Sanity check that we always add a JoinEntryInstr before adding a new |
| + // state. |
| + ASSERT(jump_cnt == owner()->await_joins()->length()); |
| + // Store the counter in :await_jump_var. |
| + Value* jump_val = Bind(new (I) ConstantInstr( |
| + Smi::ZoneHandle(I, Smi::New(jump_cnt)))); |
| + Do(BuildStoreLocal(*jump_var, jump_val)); |
| + // Save the current context for resuming. |
| + BuildSaveContext(*ctx_var); |
| + owner()->await_levels()->Add(owner()->context_level()); |
| + return; |
| + } |
| + if (node->type() == AwaitMarkerNode::kTargetForContinuation) { |
| + // We need to create a new await target which involves: |
| + // * Append a join that is also added to the list that will later result in |
| + // a preamble. |
| + JoinEntryInstr* const join = new(I) JoinEntryInstr( |
| + owner()->AllocateBlockId(), owner()->try_index()); |
| + owner()->await_joins()->Add(join); |
| + Goto(join); |
| + exit_ = join; |
| + return; |
| + } |
| + UNREACHABLE(); |
| +} |
| + |
| + |
| intptr_t EffectGraphVisitor::GetCurrentTempLocalIndex() const { |
| return kFirstLocalSlotFromFp |
| - owner()->num_stack_locals() |
| @@ -3626,6 +3724,22 @@ void EffectGraphVisitor::VisitSequenceNode(SequenceNode* node) { |
| } |
| } |
| + // 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. |
| + GotoInstr* preamble_start = NULL; |
| + if ((node == owner()->parsed_function()->node_sequence()) && |
| + (owner()->parsed_function()->function().is_async_closure())) { |
| + JoinEntryInstr* preamble_end = new(I) JoinEntryInstr( |
| + owner()->AllocateBlockId(), owner()->try_index()); |
| + ASSERT(exit() != NULL); |
| + exit()->Goto(preamble_end); |
| + ASSERT(exit()->next()->IsGoto()); |
| + preamble_start = exit()->next()->AsGoto(); |
| + ASSERT(preamble_start->IsGoto()); |
| + exit_ = preamble_end; |
| + } |
| + |
| intptr_t i = 0; |
| while (is_open() && (i < node->length())) { |
| EffectGraphVisitor for_effect(owner()); |
| @@ -3637,6 +3751,62 @@ void EffectGraphVisitor::VisitSequenceNode(SequenceNode* node) { |
| } |
| } |
| + // 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 ((node == owner()->parsed_function()->node_sequence()) && |
| + (owner()->parsed_function()->function().is_async_closure())) { |
| + 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_cnt = new(I) LoadLocalNode( |
| + Scanner::kNoSourcePos, jump_var); |
| + ComparisonNode* check_jump_cnt; |
| + const intptr_t num_await_states = owner()->await_joins()->length(); |
| + for (intptr_t i = 0; i < num_await_states; i++) { |
| + check_jump_cnt = new(I) ComparisonNode( |
| + Scanner::kNoSourcePos, |
| + Token::kEQ, |
| + load_jump_cnt, |
| + new(I) LiteralNode( |
| + Scanner::kNoSourcePos, Smi::ZoneHandle(I, Smi::New(i)))); |
| + TestGraphVisitor for_test(owner(), Scanner::kNoSourcePos); |
| + check_jump_cnt->Visit(&for_test); |
| + EffectGraphVisitor for_true(owner()); |
| + EffectGraphVisitor for_false(owner()); |
| + |
| + for_true.BuildAwaitJump(top_scope, |
| + (*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()); |
| + for_test.entry()->set_previous(preamble_start->previous()); |
| + } |
| + if (i == (num_await_states - 1)) { |
| + // Link up preamble end. |
| + if (exit_ == NULL) { |
| + exit_ = preamble_start; |
| + } else { |
| + exit_->LinkTo(preamble_start); |
| + } |
| + } |
| + } |
| + entry_ = saved_entry; |
| + exit_ = saved_exit; |
| + } |
| + |
| if (is_open()) { |
| if (MustSaveRestoreContext(node)) { |
| BuildRestoreContext( |