| 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());
 | 
| 
 |