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

Unified Diff: runtime/vm/flow_graph_builder.cc

Issue 484933003: Await it! (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: rebase Created 6 years, 4 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 | « runtime/vm/flow_graph_builder.h ('k') | runtime/vm/parser.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: runtime/vm/flow_graph_builder.cc
diff --git a/runtime/vm/flow_graph_builder.cc b/runtime/vm/flow_graph_builder.cc
index dfdec06e3ed8bcbed4058c2280507412c95d1944..40cdf4cdd57b522dd747ff1c23f3edf4030509e1 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());
+ ASSERT((old_ctx != NULL) && old_ctx->is_captured());
+ // Before restoring the continuation context we need to temporary save the
+ // 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->marker_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());
+ 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->marker_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()
@@ -3638,6 +3736,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());
@@ -3649,6 +3763,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(
« no previous file with comments | « runtime/vm/flow_graph_builder.h ('k') | runtime/vm/parser.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698