From: Iain Sandoe Date: Tue, 30 Jun 2020 06:26:51 +0000 (+0100) Subject: coroutines: Collect the function body rewrite code. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=9252a208f485eed2757d601657facfa0aee6cd21;p=gcc.git coroutines: Collect the function body rewrite code. The standard describes a rewrite of the body of the user-authored function (which wraps it in a try-catch block and provides the initial and final suspend expressions). The exact arrangement of this was still in flux right up until the DIS and as a consequence was a bit of a moving target. The net result was a fragmented implementation of the parts of the rewrite which is now impeding progress in fixing other issues. This patch collates the rewrite action into a single function and carries this out earlier. gcc/cp/ChangeLog: * coroutines.cc (expand_one_await_expression): Remove code dealing with initial suspend. (build_actor_fn): Remove code special-casing initial and final suspend. Handle the final suspend and marking of the coroutine as done. (coro_rewrite_function_body): New. (bind_expr_find_in_subtree): Remove. (coro_body_contains_bind_expr_p): Remove. (morph_fn_to_coro): Split the rewrite of the original function into coro_rewrite_function_body and call it. --- diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index 54f9cb3b4e4..ba4ac682f11 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -1573,8 +1573,6 @@ expand_one_await_expression (tree *stmt, tree *await_expr, void *d) tree awaiter_calls = TREE_OPERAND (saved_co_await, 3); tree source = TREE_OPERAND (saved_co_await, 4); - bool is_initial = - (source && TREE_INT_CST_LOW (source) == (int) INITIAL_SUSPEND_POINT); bool is_final = (source && TREE_INT_CST_LOW (source) == (int) FINAL_SUSPEND_POINT); bool needs_dtor = TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (var)); @@ -1724,16 +1722,6 @@ expand_one_await_expression (tree *stmt, tree *await_expr, void *d) resume_label = build_stmt (loc, LABEL_EXPR, resume_label); append_to_statement_list (resume_label, &stmt_list); - if (is_initial) - { - /* Note that we are about to execute the await_resume() for the initial - await expression. */ - r = build2_loc (loc, MODIFY_EXPR, boolean_type_node, data->i_a_r_c, - boolean_true_node); - r = coro_build_cvt_void_expr_stmt (r, loc); - append_to_statement_list (r, &stmt_list); - } - /* This will produce the value (if one is provided) from the co_await expression. */ tree resume_call = TREE_VEC_ELT (awaiter_calls, 2); /* await_resume(). */ @@ -2102,19 +2090,16 @@ static void build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, tree orig, hash_map *param_uses, hash_map *local_var_uses, - vec *param_dtor_list, tree initial_await, - tree final_await, unsigned body_count, tree frame_size) + vec *param_dtor_list, + tree fs_label, tree resume_fn_field, + unsigned body_count, tree frame_size) { verify_stmt_tree (fnbody); /* Some things we inherit from the original function. */ - tree coro_frame_ptr = build_pointer_type (coro_frame_type); tree handle_type = get_coroutine_handle_type (orig); tree self_h_proxy = get_coroutine_self_handle_proxy (orig); tree promise_type = get_coroutine_promise_type (orig); tree promise_proxy = get_coroutine_promise_proxy (orig); - tree act_des_fn_type - = build_function_type_list (void_type_node, coro_frame_ptr, NULL_TREE); - tree act_des_fn_ptr = build_pointer_type (act_des_fn_type); /* One param, the coro frame pointer. */ tree actor_fp = DECL_ARGUMENTS (actor); @@ -2145,21 +2130,15 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, DECL_CONTEXT (continuation) = actor; BIND_EXPR_VARS (actor_bind) = continuation; - /* Update the block associated with the outer scope of the orig fn. */ + /* Link in the block associated with the outer scope of the re-written + function body. */ tree first = expr_first (fnbody); - if (first && TREE_CODE (first) == BIND_EXPR) - { - /* We will discard this, since it's connected to the original scope - nest. */ - tree block = BIND_EXPR_BLOCK (first); - if (block) /* For this to be missing is probably a bug. */ - { - gcc_assert (BLOCK_SUPERCONTEXT (block) == NULL_TREE); - gcc_assert (BLOCK_CHAIN (block) == NULL_TREE); - BLOCK_SUPERCONTEXT (block) = top_block; - BLOCK_SUBBLOCKS (top_block) = block; - } - } + gcc_checking_assert (first && TREE_CODE (first) == BIND_EXPR); + tree block = BIND_EXPR_BLOCK (first); + gcc_checking_assert (BLOCK_SUPERCONTEXT (block) == NULL_TREE); + gcc_checking_assert (BLOCK_CHAIN (block) == NULL_TREE); + BLOCK_SUPERCONTEXT (block) = top_block; + BLOCK_SUBBLOCKS (top_block) = block; add_stmt (actor_bind); tree actor_body = push_stmt_list (); @@ -2236,7 +2215,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, add_stmt (b); short unsigned lab_num = 3; - for (unsigned destr_pt = 0; destr_pt < body_count + 2; destr_pt++) + for (unsigned destr_pt = 0; destr_pt < body_count; destr_pt++) { tree l_num = build_int_cst (short_unsigned_type_node, lab_num); b = build_case_label (l_num, NULL_TREE, @@ -2272,8 +2251,10 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, add_stmt (b); lab_num = 2; - /* The final resume should be made to hit the default (trap, UB) entry. */ - for (unsigned resu_pt = 0; resu_pt < body_count + 1; resu_pt++) + /* The final resume should be made to hit the default (trap, UB) entry + although it will be unreachable via the normal entry point, since that + is set to NULL on reaching final suspend. */ + for (unsigned resu_pt = 0; resu_pt < body_count; resu_pt++) { tree l_num = build_int_cst (short_unsigned_type_node, lab_num); b = build_case_label (l_num, NULL_TREE, @@ -2326,55 +2307,19 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, await_xform_data xform = {actor, actor_frame, promise_proxy, ap, self_h_proxy, ash}; - /* Get a reference to the initial suspend var in the frame. */ - transform_await_expr (initial_await, &xform); - tree initial_await_stmt = coro_build_expr_stmt (initial_await, loc); - - /* co_return branches to the final_suspend label, so declare that now. */ - tree fs_label = create_named_label_with_ctx (loc, "final.suspend", actor); - /* Expand co_returns in the saved function body */ fnbody = expand_co_returns (&fnbody, promise_proxy, ap, fs_label); - /* Specific behaviour to treat exceptions thrown by the await_resume () - of the initial suspend expression. In order to implement this, we - need to treat the initial_suspend expression as if it were part of the - user-authored function body. This only applies if exceptions are - enabled. */ - if (flag_exceptions) - { - tree outer = fnbody; - if (TREE_CODE (outer) == BIND_EXPR) - outer = BIND_EXPR_BODY (outer); - gcc_checking_assert (TREE_CODE (outer) == TRY_BLOCK); - tree sl = TRY_STMTS (outer); - if (TREE_CODE (sl) == STATEMENT_LIST) - { - tree_stmt_iterator si = tsi_start (sl); - tsi_link_before (&si, initial_await_stmt, TSI_NEW_STMT); - } - else - { - tree new_try = NULL_TREE; - append_to_statement_list (initial_await_stmt, &new_try); - append_to_statement_list (sl, &new_try); - TRY_STMTS (outer) = new_try; - } - } - else - add_stmt (initial_await_stmt); - /* Transform the await expressions in the function body. Only do each await tree once! */ hash_set pset; cp_walk_tree (&fnbody, transform_await_wrapper, &xform, &pset); - /* Add in our function body with the co_returns rewritten to final form. */ - add_stmt (fnbody); - - /* Final suspend starts here. */ - r = build_stmt (loc, LABEL_EXPR, fs_label); - add_stmt (r); + /* Now replace the promise proxy with its real value. */ + proxy_replace p_data; + p_data.from = promise_proxy; + p_data.to = ap; + cp_walk_tree (&fnbody, replace_proxy, &p_data, NULL); /* Set the actor pointer to null, so that 'done' will work. Resume from here is UB anyway - although a 'ready' await will @@ -2384,15 +2329,12 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, /*protect=*/1, /*want_type=*/0, tf_warning_or_error); tree res_x = build_class_member_access_expr (actor_frame, resume_m, NULL_TREE, false, tf_warning_or_error); - r = build1 (CONVERT_EXPR, act_des_fn_ptr, integer_zero_node); - r = build2 (INIT_EXPR, act_des_fn_ptr, res_x, r); - r = coro_build_cvt_void_expr_stmt (r, loc); - add_stmt (r); + p_data.from = resume_fn_field; + p_data.to = res_x; + cp_walk_tree (&fnbody, replace_proxy, &p_data, NULL); - /* Get a reference to the final suspend var in the frame. */ - transform_await_expr (final_await, &xform); - r = coro_build_expr_stmt (final_await, loc); - add_stmt (r); + /* Add in our function body with the co_returns rewritten to final form. */ + add_stmt (fnbody); /* now do the tail of the function. */ tree del_promise_label @@ -2559,20 +2501,10 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, = build_class_member_access_expr (actor_frame, res_idx_m, NULL_TREE, false, tf_warning_or_error); - /* Boolean value to flag that the initial suspend expression's - await_resume () has been called, and therefore we are in the user's - function body for the purposes of handing exceptions. */ - tree i_a_r_c_m - = lookup_member (coro_frame_type, get_identifier ("__i_a_r_c"), - /*protect=*/1, /*want_type=*/0, tf_warning_or_error); - tree i_a_r_c - = build_class_member_access_expr (actor_frame, i_a_r_c_m, NULL_TREE, - false, tf_warning_or_error); - /* We've now rewritten the tree and added the initial and final co_awaits. Now pass over the tree and expand the co_awaits. */ - coro_aw_data data = {actor, actor_fp, resume_pt_number, i_a_r_c, + coro_aw_data data = {actor, actor_fp, resume_pt_number, NULL_TREE, ash, del_promise_label, ret_label, continue_label, continuation, 2}; cp_walk_tree (&actor_body, await_statement_expander, &data, NULL); @@ -3641,23 +3573,165 @@ act_des_fn (tree orig, tree fn_type, tree coro_frame_ptr, const char* name) return fn; } -/* Return a bind expression if we see one, else NULL_TREE. */ +/* Re-write the body as per [dcl.fct.def.coroutine] / 5. */ + static tree -bind_expr_find_in_subtree (tree *stmt, int *, void *) +coro_rewrite_function_body (location_t fn_start, tree fnbody, + tree orig, tree resume_fn_ptr_type, + tree& resume_fn_field, tree& fs_label) { - if (TREE_CODE (*stmt) == BIND_EXPR) - return *stmt; - return NULL_TREE; -} + /* This will be our new outer scope. */ + tree update_body = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL); + tree top_block = make_node (BLOCK); + BIND_EXPR_BLOCK (update_body) = top_block; + BIND_EXPR_BODY (update_body) = push_stmt_list (); -/* Return the first bind expression that the sub-tree given by STMT - contains. */ + /* If the function has a top level bind expression, then connect that + after first making sure we give it a new block. */ + tree first = expr_first (fnbody); + if (first && TREE_CODE (first) == BIND_EXPR) + { + tree block = BIND_EXPR_BLOCK (first); + gcc_checking_assert (block); + gcc_checking_assert (BLOCK_SUPERCONTEXT (block) == NULL_TREE); + gcc_checking_assert (BLOCK_CHAIN (block) == NULL_TREE); + /* Replace the top block to avoid issues with locations for args + appearing to be in a non-existent place. */ + tree replace_blk = make_node (BLOCK); + BLOCK_VARS (replace_blk) = BLOCK_VARS (block); + BLOCK_SUBBLOCKS (replace_blk) = BLOCK_SUBBLOCKS (block); + for (tree b = BLOCK_SUBBLOCKS (replace_blk); b; b = BLOCK_CHAIN (b)) + BLOCK_SUPERCONTEXT (b) = replace_blk; + BIND_EXPR_BLOCK (first) = replace_blk; + /* The top block has one child, so far, and we have now got a + superblock. */ + BLOCK_SUPERCONTEXT (block) = top_block; + BLOCK_SUBBLOCKS (top_block) = block; + } -static tree -coro_body_contains_bind_expr_p (tree *stmt) -{ - hash_set visited; - return cp_walk_tree (stmt, bind_expr_find_in_subtree, NULL, &visited); + /* Wrap the function body in a try {} catch (...) {} block, if exceptions + are enabled. */ + tree promise = get_coroutine_promise_proxy (orig); + tree var_list = NULL_TREE; + tree initial_await = build_init_or_final_await (fn_start, false); + + /* [stmt.return.coroutine] / 3 + If p.return_void() is a valid expression, flowing off the end of a + coroutine is equivalent to a co_return with no operand; otherwise + flowing off the end of a coroutine results in undefined behavior. */ + tree return_void + = get_coroutine_return_void_expr (current_function_decl, fn_start, false); + + if (flag_exceptions) + { + /* Build promise.unhandled_exception(); */ + tree ueh + = coro_build_promise_expression (current_function_decl, promise, + coro_unhandled_exception_identifier, + fn_start, NULL, /*musthave=*/true); + /* Create and initialize the initial-await-resume-called variable per + [dcl.fct.def.coroutine] / 5.3. */ + tree i_a_r_c = build_lang_decl (VAR_DECL, get_identifier ("__i_a_r_c"), + boolean_type_node); + DECL_ARTIFICIAL (i_a_r_c) = true; + DECL_CHAIN (i_a_r_c) = var_list; + var_list = i_a_r_c; + DECL_INITIAL (i_a_r_c) = boolean_false_node; + add_decl_expr (i_a_r_c); + /* Start the try-catch. */ + tree tcb = build_stmt (fn_start, TRY_BLOCK, NULL_TREE, NULL_TREE); + add_stmt (tcb); + TRY_STMTS (tcb) = push_stmt_list (); + if (initial_await != error_mark_node) + { + /* Build a compound expression that sets the + initial-await-resume-called variable true and then calls the + initial suspend expression await resume. */ + tree vec = TREE_OPERAND (initial_await, 3); + tree aw_r = TREE_VEC_ELT (vec, 2); + tree update = build2 (MODIFY_EXPR, boolean_type_node, i_a_r_c, + boolean_true_node); + aw_r = cp_build_compound_expr (update, aw_r, tf_warning_or_error); + TREE_VEC_ELT (vec, 2) = aw_r; + } + /* Add the initial await to the start of the user-authored function. */ + finish_expr_stmt (initial_await); + /* Append the original function body. */ + add_stmt (fnbody); + if (return_void) + add_stmt (return_void); + TRY_STMTS (tcb) = pop_stmt_list (TRY_STMTS (tcb)); + TRY_HANDLERS (tcb) = push_stmt_list (); + /* Mimic what the parser does for the catch. */ + tree handler = begin_handler (); + finish_handler_parms (NULL_TREE, handler); /* catch (...) */ + + /* Get the initial await resume called value. */ + tree not_iarc_if = begin_if_stmt (); + tree not_iarc = build1_loc (fn_start, TRUTH_NOT_EXPR, + boolean_type_node, i_a_r_c); + finish_if_stmt_cond (not_iarc, not_iarc_if); + /* If the initial await resume called value is false, rethrow... */ + tree rethrow = build_throw (fn_start, NULL_TREE); + TREE_NO_WARNING (rethrow) = true; + finish_expr_stmt (rethrow); + finish_then_clause (not_iarc_if); + tree iarc_scope = IF_SCOPE (not_iarc_if); + IF_SCOPE (not_iarc_if) = NULL; + not_iarc_if = do_poplevel (iarc_scope); + add_stmt (not_iarc_if); + /* ... else call the promise unhandled exception method. */ + ueh = maybe_cleanup_point_expr_void (ueh); + add_stmt (ueh); + finish_handler (handler); + TRY_HANDLERS (tcb) = pop_stmt_list (TRY_HANDLERS (tcb)); + } + else + { + if (pedantic) + { + /* We still try to look for the promise method and warn if it's not + present. */ + tree ueh_meth + = lookup_promise_method (orig, coro_unhandled_exception_identifier, + fn_start, /*musthave=*/false); + if (!ueh_meth || ueh_meth == error_mark_node) + warning_at (fn_start, 0, "no member named %qE in %qT", + coro_unhandled_exception_identifier, + get_coroutine_promise_type (orig)); + } + /* Else we don't check and don't care if the method is missing.. + just add the initial suspend, function and return. */ + finish_expr_stmt (initial_await); + /* Append the original function body. */ + add_stmt (fnbody); + if (return_void) + add_stmt (return_void); + } + + /* co_return branches to the final_suspend label, so declare that now. */ + fs_label + = create_named_label_with_ctx (fn_start, "final.suspend", NULL_TREE); + add_stmt (build_stmt (fn_start, LABEL_EXPR, fs_label)); + + /* Before entering the final suspend point, we signal that this point has + been reached by setting the resume function pointer to zero (this is + what the 'done()' builtin tests) as per the current ABI. */ + resume_fn_field + = build_lang_decl (VAR_DECL, get_identifier ("resume.fn.ptr.proxy"), + resume_fn_ptr_type); + DECL_ARTIFICIAL (resume_fn_field) = true; + tree zero_resume + = build1 (CONVERT_EXPR, resume_fn_ptr_type, integer_zero_node); + zero_resume + = build2 (INIT_EXPR, resume_fn_ptr_type, resume_fn_field, zero_resume); + finish_expr_stmt (zero_resume); + finish_expr_stmt (build_init_or_final_await (fn_start, true)); + BIND_EXPR_BODY (update_body) = pop_stmt_list (BIND_EXPR_BODY (update_body)); + BIND_EXPR_VARS (update_body) = nreverse (var_list); + BLOCK_VARS (top_block) = BIND_EXPR_VARS (update_body); + + return update_body; } /* Here we: @@ -3734,55 +3808,32 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) return false; } - /* So, we've tied off the original body. Now start the replacement. + /* So, we've tied off the original user-authored body in fn_body. + + Start the replacement synthesized ramp body as newbody. If we encounter a fatal error we might return a now-empty body. - TODO: determine if it would help to restore the original. - determine if looking for more errors in coro_function_valid_p() - and stashing types is a better solution. */ + + Note, the returned ramp body is not 'popped', to be compatible with + the way that decl.c handles regular functions, the scope pop is done + in the caller. */ tree newbody = push_stmt_list (); DECL_SAVED_TREE (orig) = newbody; /* If our original body is noexcept, then that's what we apply to our - generated functions. Remember that we're NOEXCEPT and fish out the - contained list (we tied off to the top level already). */ + generated ramp, transfer any MUST_NOT_THOW_EXPR to that. */ bool is_noexcept = TREE_CODE (body_start) == MUST_NOT_THROW_EXPR; if (is_noexcept) { - /* Simplified abstract from begin_eh_spec_block, since we already - know the outcome. */ - fnbody = TREE_OPERAND (body_start, 0); /* Stash the original... */ - add_stmt (body_start); /* ... and start the new. */ + /* The function body we will continue with is the single operand to + the must-not-throw. */ + fnbody = TREE_OPERAND (body_start, 0); + /* Transfer the must-not-throw to the ramp body. */ + add_stmt (body_start); + /* Re-start the ramp as must-not-throw. */ TREE_OPERAND (body_start, 0) = push_stmt_list (); } - /* We can be presented with a function that currently has no outer bind - expression. We will insert bind scopes in expanding await expressions, - and therefore need a top level to the tree, so synthesize an outer bind - expression and scope. */ - tree check_bind = expr_first (fnbody); - if (check_bind && TREE_CODE (check_bind) != BIND_EXPR) - { - tree update_body = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL); - tree blk = make_node (BLOCK); - gcc_checking_assert (!coro_body_contains_bind_expr_p (&fnbody)); - BIND_EXPR_BLOCK (update_body) = blk; - if (TREE_CODE (fnbody) == STATEMENT_LIST) - BIND_EXPR_BODY (update_body) = fnbody; - else - { - tree tlist = NULL_TREE; - append_to_statement_list_force (fnbody, &tlist); - TREE_SIDE_EFFECTS (tlist) = TREE_SIDE_EFFECTS (fnbody); - BIND_EXPR_BODY (update_body) = tlist; - } - tree new_body_list = NULL_TREE; - TREE_SIDE_EFFECTS (update_body) = true; - append_to_statement_list (update_body, &new_body_list); - TREE_SIDE_EFFECTS (new_body_list) = true; - fnbody = new_body_list; - } - /* Create the coro frame type, as far as it can be known at this stage. 1. Types we already know. */ @@ -3792,35 +3843,6 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) /* 2. Types we need to define or look up. */ - /* We need to know, and inspect, each suspend point in the function - in several places. It's convenient to place this map out of line - since it's used from tree walk callbacks. */ - suspend_points = new hash_map; - - /* Initial and final suspend types are special in that the co_awaits for - them are synthetic. We need to find the type for each awaiter from - the coroutine promise. */ - tree initial_await = build_init_or_final_await (fn_start, false); - if (initial_await == error_mark_node) - { - /* Suppress warnings about the missing return value. */ - TREE_NO_WARNING (orig) = true; - return false; - } - /* The type of the frame var for this is the type of its temp proxy. */ - tree initial_suspend_type = TREE_TYPE (TREE_OPERAND (initial_await, 1)); - - tree final_await = build_init_or_final_await (fn_start, true); - if (final_await == error_mark_node) - { - /* Suppress warnings about the missing return value. */ - TREE_NO_WARNING (orig) = true; - return false; - } - - /* The type of the frame var for this is the type of its temp proxy. */ - tree final_suspend_type = TREE_TYPE (TREE_OPERAND (final_await, 1)); - tree fr_name = get_fn_local_identifier (orig, "frame"); tree coro_frame_type = xref_tag (record_type, fr_name, ts_current, false); DECL_CONTEXT (TYPE_NAME (coro_frame_type)) = current_scope (); @@ -3833,20 +3855,27 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) tree actor = act_des_fn (orig, act_des_fn_type, coro_frame_ptr, "actor"); tree destroy = act_des_fn (orig, act_des_fn_type, coro_frame_ptr, "destroy"); + /* Construct the wrapped function body; we will analyze this to determine + the requirements for the coroutine frame. */ + + tree resume_fn_field = NULL_TREE; + tree fs_label = NULL_TREE; + fnbody = coro_rewrite_function_body (fn_start, fnbody, orig, act_des_fn_ptr, + resume_fn_field, fs_label); /* Build our dummy coro frame layout. */ coro_frame_type = begin_class_definition (coro_frame_type); tree field_list = NULL_TREE; tree resume_name - = coro_make_frame_entry (&field_list, "__resume", act_des_fn_ptr, fn_start); - tree destroy_name = coro_make_frame_entry (&field_list, "__destroy", - act_des_fn_ptr, fn_start); + = coro_make_frame_entry (&field_list, "__resume", + act_des_fn_ptr, fn_start); + tree destroy_name + = coro_make_frame_entry (&field_list, "__destroy", + act_des_fn_ptr, fn_start); tree promise_name = coro_make_frame_entry (&field_list, "__p", promise_type, fn_start); tree fnf_name = coro_make_frame_entry (&field_list, "__frame_needs_free", boolean_type_node, fn_start); - tree iarc_name = coro_make_frame_entry (&field_list, "__i_a_r_c", - boolean_type_node, fn_start); tree resume_idx_name = coro_make_frame_entry (&field_list, "__resume_at", short_unsigned_type_node, fn_start); @@ -3928,11 +3957,10 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) cp_walk_tree (&fnbody, register_param_uses, ¶m_data, NULL); } - /* Initial suspend is mandated. */ - tree init_susp_name = coro_make_frame_entry (&field_list, "__aw_s.is", - initial_suspend_type, fn_start); - - register_await_info (initial_await, initial_suspend_type, init_susp_name); + /* We need to know, and inspect, each suspend point in the function + in several places. It's convenient to place this map out of line + since it's used from tree walk callbacks. */ + suspend_points = new hash_map; /* Now insert the data for any body await points, at this time we also need to promote any temporaries that are captured by reference (to regular @@ -3945,12 +3973,6 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) body_aw_points.to_replace = make_tree_vector (); cp_walk_tree (&fnbody, await_statement_walker, &body_aw_points, NULL); - /* Final suspend is mandated. */ - tree fin_susp_name = coro_make_frame_entry (&field_list, "__aw_s.fs", - final_suspend_type, fn_start); - - register_await_info (final_await, final_suspend_type, fin_susp_name); - /* 4. Now make space for local vars, this is conservative again, and we would expect to delete unused entries later. */ hash_map local_var_uses; @@ -4455,17 +4477,6 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) r = coro_build_cvt_void_expr_stmt (r, fn_start); add_stmt (r); - /* Initialize 'initial-await-resume-called' as per - [dcl.fct.def.coroutine] / 5.3 */ - tree i_a_r_c_m - = lookup_member (coro_frame_type, iarc_name, 1, 0, tf_warning_or_error); - tree i_a_r_c = build_class_member_access_expr (deref_fp, i_a_r_c_m, - NULL_TREE, false, - tf_warning_or_error); - r = build2 (INIT_EXPR, boolean_type_node, i_a_r_c, boolean_false_node); - r = coro_build_cvt_void_expr_stmt (r, fn_start); - add_stmt (r); - /* So .. call the actor .. */ r = build_call_expr_loc (fn_start, actor, 1, coro_fp); r = maybe_cleanup_point_expr_void (r); @@ -4535,133 +4546,6 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) BIND_EXPR_BODY (ramp_bind) = pop_stmt_list (ramp_body); TREE_SIDE_EFFECTS (ramp_bind) = true; - /* We know the "real" promise and have a frame layout with a slot for each - suspend point, so we can build an actor function (which contains the - functionality for both 'resume' and 'destroy'). - - wrap the function body in a try {} catch (...) {} block, if exceptions - are enabled. */ - - /* First make a new block for the body - that will be embedded in the - re-written function. */ - tree first = expr_first (fnbody); - bool orig_fn_has_outer_bind = false; - tree replace_blk = NULL_TREE; - if (first && TREE_CODE (first) == BIND_EXPR) - { - orig_fn_has_outer_bind = true; - tree block = BIND_EXPR_BLOCK (first); - replace_blk = make_node (BLOCK); - if (block) /* missing block is probably an error. */ - { - gcc_assert (BLOCK_SUPERCONTEXT (block) == NULL_TREE); - gcc_assert (BLOCK_CHAIN (block) == NULL_TREE); - BLOCK_VARS (replace_blk) = BLOCK_VARS (block); - BLOCK_SUBBLOCKS (replace_blk) = BLOCK_SUBBLOCKS (block); - for (tree b = BLOCK_SUBBLOCKS (replace_blk); b; b = BLOCK_CHAIN (b)) - BLOCK_SUPERCONTEXT (b) = replace_blk; - } - BIND_EXPR_BLOCK (first) = replace_blk; - } - - /* actor's version of the promise. */ - tree actor_frame = build1_loc (fn_start, INDIRECT_REF, coro_frame_type, - DECL_ARGUMENTS (actor)); - tree ap_m = lookup_member (coro_frame_type, get_identifier ("__p"), 1, 0, - tf_warning_or_error); - tree ap = build_class_member_access_expr (actor_frame, ap_m, NULL_TREE, - false, tf_warning_or_error); - - /* Now we've built the promise etc, process fnbody for co_returns. - We want the call to return_void () below and it has no params so - we can create it once here. - Calls to return_value () will have to be checked and created as - required. */ - - tree return_void - = coro_build_promise_expression (current_function_decl, ap, - coro_return_void_identifier, - fn_start, NULL, /*musthave=*/false); - - /* [stmt.return.coroutine] (2.2 : 3) if p.return_void() is a valid - expression, flowing off the end of a coroutine is equivalent to - co_return; otherwise UB. - We just inject the call to p.return_void() here, and fall through to - the final_suspend: label (eliding the goto). If the function body has - a co_return, then this statement will be unreachable and DCEd. */ - if (return_void != NULL_TREE) - { - tree append = push_stmt_list (); - add_stmt (fnbody); - add_stmt (return_void); - fnbody = pop_stmt_list(append); - } - - if (flag_exceptions) - { - tree ueh - = coro_build_promise_expression (current_function_decl, ap, - coro_unhandled_exception_identifier, - fn_start, NULL, /*musthave=*/true); - - /* The try block is just the original function, there's no real - need to call any function to do this. */ - fnbody = build_stmt (fn_start, TRY_BLOCK, fnbody, NULL_TREE); - TRY_HANDLERS (fnbody) = push_stmt_list (); - /* Mimic what the parser does for the catch. */ - tree handler = begin_handler (); - finish_handler_parms (NULL_TREE, handler); /* catch (...) */ - - /* Get the initial await resume called value. */ - tree i_a_r_c = build_class_member_access_expr (actor_frame, i_a_r_c_m, - NULL_TREE, false, - tf_warning_or_error); - tree not_iarc_if = begin_if_stmt (); - tree not_iarc = build1_loc (fn_start, TRUTH_NOT_EXPR, - boolean_type_node, i_a_r_c); - finish_if_stmt_cond (not_iarc, not_iarc_if); - /* If the initial await resume called value is false, rethrow... */ - tree rethrow = build_throw (fn_start, NULL_TREE); - TREE_NO_WARNING (rethrow) = true; - finish_expr_stmt (rethrow); - finish_then_clause (not_iarc_if); - tree iarc_scope = IF_SCOPE (not_iarc_if); - IF_SCOPE (not_iarc_if) = NULL; - not_iarc_if = do_poplevel (iarc_scope); - add_stmt (not_iarc_if); - /* ... else call the promise unhandled exception method. */ - ueh = maybe_cleanup_point_expr_void (ueh); - add_stmt (ueh); - finish_handler (handler); - TRY_HANDLERS (fnbody) = pop_stmt_list (TRY_HANDLERS (fnbody)); - /* If the function starts with a BIND_EXPR, then we need to create - one here to contain the try-catch and to link up the scopes. */ - if (orig_fn_has_outer_bind) - { - fnbody = build3 (BIND_EXPR, void_type_node, NULL, fnbody, NULL); - /* Make and connect the scope blocks. */ - tree tcb_block = make_node (BLOCK); - /* .. and connect it here. */ - BLOCK_SUPERCONTEXT (replace_blk) = tcb_block; - BLOCK_SUBBLOCKS (tcb_block) = replace_blk; - BIND_EXPR_BLOCK (fnbody) = tcb_block; - TREE_SIDE_EFFECTS (fnbody) = true; - } - } - else if (pedantic) - { - /* We still try to look for the promise method and warn if it's not - present. */ - tree ueh_meth - = lookup_promise_method (orig, coro_unhandled_exception_identifier, - fn_start, /*musthave=*/false); - if (!ueh_meth || ueh_meth == error_mark_node) - warning_at (fn_start, 0, "no member named %qE in %qT", - coro_unhandled_exception_identifier, - get_coroutine_promise_type (orig)); - } - /* Else we don't check and don't care if the method is missing. */ - /* Start to build the final functions. We push_deferring_access_checks to avoid these routines being seen as @@ -4671,7 +4555,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) /* Build the actor... */ build_actor_fn (fn_start, coro_frame_type, actor, fnbody, orig, param_uses, - &local_var_uses, param_dtor_list, initial_await, final_await, + &local_var_uses, param_dtor_list, fs_label, resume_fn_field, body_aw_points.await_number, frame_size); /* Destroyer ... */