Hi Jason, gentle ping for this one.
> On 29 Aug 2024, at 20:10, Iain Sandoe <iains....@gmail.com> wrote: > > Hi Jason, > >>> - char *buf = xasprintf ("_Coro_unnamed_parm_%d", no_name_parm++); >>> + char *buf = xasprintf ("anon%d", parm_num); > >> Why the reduction in verbosity here? > I was getting ahead of myself; not intended in this patch. Fixed. > >>> + p = pushdecl_outermost_localscope (p); >>> + add_decl_expr (p); > >> Why outermost_localscope but adding the DECL_EXPR in the same list as the >> others that use plain pushdecl? > > A pasto. fixed. > >> Incidentally, it seems like most uses of _build_art are followed by pushdecl >> and add_decl_expr, maybe another function could combine them? Say, >> ...push_artificial_var...? Or _declare_? > > Done,as attached - sorry I was not very inspired to think of a shorter name. > OK now (assuming a re-test passes)? > thanks > Iain > > --- 8< --- > > Now that we have separated the codegen of the ramp, actor and > destroy functions, we no longer need to manage the scopes for > variables manually. > > This introduces a helper function that allows us to build a > local var with a DECL_VALUE_EXPR that relates to the coroutine > state frame entry. > > This fixes a latent issue where we would generate guard vars > when exceptions were disabled. > > gcc/cp/ChangeLog: > > * coroutines.cc (coro_build_artificial_var_with_dve): New. > (coro_build_and_push_artificial_var): New. > (coro_build_and_push_artificial_var_with_dve): New. > (analyze_fn_parms): Ensure that frame entries cannot clash > with local variables. > (build_coroutine_frame_delete_expr): Amend comment. > (cp_coroutine_transform::build_ramp_function): Rework to > avoid manual management of variables and scopes. > > Signed-off-by: Iain Sandoe <i...@sandoe.co.uk> > --- > gcc/cp/coroutines.cc | 302 +++++++++++++++++++++++-------------------- > 1 file changed, 159 insertions(+), 143 deletions(-) > > diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc > index 20bda5520c0..b6f98108889 100644 > --- a/gcc/cp/coroutines.cc > +++ b/gcc/cp/coroutines.cc > @@ -1723,6 +1723,8 @@ coro_build_artificial_var (location_t loc, tree name, > tree type, tree ctx, > return res; > } > > +/* As above, except allowing the name to be a string. */ > + > static tree > coro_build_artificial_var (location_t loc, const char *name, tree type, > tree ctx, tree init) > @@ -1731,6 +1733,48 @@ coro_build_artificial_var (location_t loc, const char > *name, tree type, > type, ctx, init); > } > > +/* As for coro_build_artificial_var, except that we push this decl into > + the current binding scope and add the decl_expr. */ > + > +static tree > +coro_build_and_push_artificial_var (location_t loc, const char *name, > + tree type, tree ctx, tree init) > +{ > + tree v = coro_build_artificial_var (loc, name, type, ctx, init); > + v = pushdecl (v); > + add_decl_expr (v); > + return v; > +} > + > +/* Build a var NAME of TYPE in CTX and with INIT; add a DECL_VALUE_EXPR that > + refers to BASE.FIELD. */ > + > +static tree > +coro_build_artificial_var_with_dve (location_t loc, tree name, tree type, > + tree ctx, tree init, tree base, tree field) > +{ > + tree v = coro_build_artificial_var (loc, name, type, ctx, init); > + tree dve = coro_build_frame_access_expr (base, field, true, > + tf_warning_or_error); > + SET_DECL_VALUE_EXPR (v, dve); > + DECL_HAS_VALUE_EXPR_P (v) = true; > + return v; > +} > + > +/* As for coro_build_artificial_var_with_dve, but push into the current > binding > + scope and add the decl_expr. */ > + > +static tree > +coro_build_and_push_artificial_var_with_dve (location_t loc, tree name, > + tree type, tree ctx, tree init, > + tree base, tree field) > +{ > + tree v = coro_build_artificial_var_with_dve (loc, name, type, ctx, init, > + base, field); > + v = pushdecl (v); > + add_decl_expr (v); > + return v; > +} > > /* 2. Create a named label in the specified context. */ > > @@ -3841,8 +3885,10 @@ analyze_fn_parms (tree orig, hash_map<tree, > param_info> *param_uses) > when we see uses. */ > bool lambda_p = LAMBDA_FUNCTION_P (orig); > > - unsigned no_name_parm = 0; > - for (tree arg = DECL_ARGUMENTS (orig); arg != NULL; arg = DECL_CHAIN (arg)) > + /* Count the param copies from 1 as per the std. */ > + unsigned parm_num = 1; > + for (tree arg = DECL_ARGUMENTS (orig); arg != NULL; > + ++parm_num, arg = DECL_CHAIN (arg)) > { > bool existed; > param_info &parm = param_uses->get_or_insert (arg, &existed); > @@ -3877,16 +3923,16 @@ analyze_fn_parms (tree orig, hash_map<tree, > param_info> *param_uses) > tree name = DECL_NAME (arg); > if (!name) > { > - char *buf = xasprintf ("_Coro_unnamed_parm_%d", no_name_parm++); > + char *buf = xasprintf ("_Coro_q%u___unnamed", parm_num); > name = get_identifier (buf); > free (buf); > } > parm.field_id = name; > - > if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (parm.frame_type)) > { > - char *buf = xasprintf ("%s%s_live", DECL_NAME (arg) ? "_Coro_" : "", > - IDENTIFIER_POINTER (name)); > + char *buf = xasprintf ("_Coro_q%u_%s_live", parm_num, > + DECL_NAME (arg) ? IDENTIFIER_POINTER (name) > + : "__unnamed"); > parm.guard_var > = coro_build_artificial_var (UNKNOWN_LOCATION, get_identifier (buf), > boolean_type_node, orig, > @@ -4569,7 +4615,7 @@ build_coroutine_frame_delete_expr (tree coro_fp, tree > frame_size, > removed, and use it as the declaration of the ramp which both replaces the > user's written function at call sites, and is responsible for starting > the coroutine it defined. > - returns NULL_TREE on error or an expression for the frame size. > + returns false on error. > > We should arrive here with the state of the compiler as if we had just > executed start_preparsed_function(). */ > @@ -4628,9 +4674,7 @@ cp_coroutine_transform::build_ramp_function () > > frame_size = TYPE_SIZE_UNIT (frame_type); > > - /* Make a var to represent the frame pointer early. Initialize to zero so > - that we can pass it to the IFN_CO_FRAME (to give that access to the > frame > - type). */ > + /* Make a var to represent the frame pointer early. */ > tree coro_fp = coro_build_artificial_var (loc, "_Coro_frameptr", > frame_ptr_type, orig_fn_decl, > NULL_TREE); > @@ -4660,66 +4704,51 @@ cp_coroutine_transform::build_ramp_function () > > /* So now construct the Ramp: */ > > - tree ramp_fnbody = begin_function_body (); > - /* Now build the ramp function pieces. */ > - tree ramp_bind = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL); > - add_stmt (ramp_bind); > - tree ramp_outer_bind = push_stmt_list (); > - tree varlist = coro_fp; > - > - /* To signal that we need to cleanup copied function args. */ > - if (flag_exceptions && DECL_ARGUMENTS (orig_fn_decl)) > - for (tree arg = DECL_ARGUMENTS (orig_fn_decl); arg != NULL; > - arg = DECL_CHAIN (arg)) > - { > - param_info *parm_i = param_uses.get (arg); > - gcc_checking_assert (parm_i); > - if (parm_i->trivial_dtor) > - continue; > - DECL_CHAIN (parm_i->guard_var) = varlist; > - varlist = parm_i->guard_var; > - } > - > - /* Signal that we need to clean up the promise object on exception. */ > - tree coro_promise_live > - = coro_build_artificial_var (loc, "_Coro_promise_live", > boolean_type_node, > - orig_fn_decl, boolean_false_node); > - DECL_CHAIN (coro_promise_live) = varlist; > - varlist = coro_promise_live; > - > - /* When the get-return-object is in the RETURN slot, we need to arrange for > - cleanup on exception. */ > - tree coro_gro_live > - = coro_build_artificial_var (loc, "_Coro_gro_live", boolean_type_node, > - orig_fn_decl, boolean_false_node); > - > - DECL_CHAIN (coro_gro_live) = varlist; > - varlist = coro_gro_live; > - > - /* Collected the scope vars we need ... only one for now. */ > - BIND_EXPR_VARS (ramp_bind) = nreverse (varlist); > - > - /* We're now going to create a new top level scope block for the ramp > - function. */ > - tree top_block = make_node (BLOCK); > + tree ramp_fnbody = begin_compound_stmt (BCS_FN_BODY); > + coro_fp = pushdecl (coro_fp); > + add_decl_expr (coro_fp); > > - BIND_EXPR_BLOCK (ramp_bind) = top_block; > - BLOCK_VARS (top_block) = BIND_EXPR_VARS (ramp_bind); > - BLOCK_SUBBLOCKS (top_block) = NULL_TREE; > - current_binding_level->blocks = top_block; > + tree coro_promise_live = NULL_TREE; > + tree coro_gro_live = NULL_TREE; > + if (flag_exceptions) > + { > + /* Signal that we need to clean up the promise object on exception. */ > + coro_promise_live > + = coro_build_and_push_artificial_var (loc, "_Coro_promise_live", > + boolean_type_node, orig_fn_decl, > + boolean_false_node); > + > + /* When the get-return-object is in the RETURN slot, we need to arrange > + for cleanup on exception. */ > + coro_gro_live > + = coro_build_and_push_artificial_var (loc, "_Coro_gro_live", > + boolean_type_node, orig_fn_decl, > + boolean_false_node); > + > + /* To signal that we need to cleanup copied function args. */ > + if (DECL_ARGUMENTS (orig_fn_decl)) > + for (tree arg = DECL_ARGUMENTS (orig_fn_decl); arg != NULL; > + arg = DECL_CHAIN (arg)) > + { > + param_info *parm_i = param_uses.get (arg); > + if (parm_i->trivial_dtor) > + continue; > + parm_i->guard_var = pushdecl (parm_i->guard_var); > + add_decl_expr (parm_i->guard_var); > + } > + } > > - add_decl_expr (coro_fp); > - if (flag_exceptions && DECL_ARGUMENTS (orig_fn_decl)) > - for (tree arg = DECL_ARGUMENTS (orig_fn_decl); arg != NULL; > - arg = DECL_CHAIN (arg)) > - { > - param_info *parm_i = param_uses.get (arg); > - if (parm_i->trivial_dtor) > - continue; > - add_decl_expr (parm_i->guard_var); > - } > - add_decl_expr (coro_promise_live); > - add_decl_expr (coro_gro_live); > + /* deref the frame pointer, to use in member access code. */ > + tree deref_fp > + = cp_build_indirect_ref (loc, coro_fp, RO_UNARY_STAR, > + tf_warning_or_error); > + tree frame_needs_free > + = coro_build_and_push_artificial_var_with_dve (loc, > + coro_frame_needs_free_id, > + boolean_type_node, > + orig_fn_decl, NULL_TREE, > + deref_fp, > + coro_frame_needs_free_id); > > /* Build the frame. */ > > @@ -4764,52 +4793,57 @@ cp_coroutine_transform::build_ramp_function () > finish_if_stmt (if_stmt); > } > > + /* For now, once allocation has succeeded we always assume that this needs > + destruction, there's no impl. for frame allocation elision. */ > + r = cp_build_init_expr (frame_needs_free, boolean_true_node); > + finish_expr_stmt (r); > + > + /* Set up the promise. */ > + tree p > + = coro_build_and_push_artificial_var_with_dve (loc, coro_promise_id, > + promise_type, orig_fn_decl, > + NULL_TREE, deref_fp, > + coro_promise_id); > + > /* Up to now any exception thrown will propagate directly to the caller. > This is OK since the only source of such exceptions would be in > allocation > of the coroutine frame, and therefore the ramp will not have initialized > any further state. From here, we will track state that needs explicit > destruction in the case that promise or g.r.o setup fails or an exception > is thrown from the initial suspend expression. */ > - tree ramp_cleanup = NULL_TREE; > + tree ramp_try_block = NULL_TREE; > + tree ramp_try_stmts = NULL_TREE; > + tree iarc_x = NULL_TREE; > if (flag_exceptions) > { > - ramp_cleanup = build_stmt (loc, TRY_BLOCK, NULL, NULL); > - add_stmt (ramp_cleanup); > - TRY_STMTS (ramp_cleanup) = push_stmt_list (); > + iarc_x > + = coro_build_and_push_artificial_var_with_dve (loc, > + coro_frame_i_a_r_c_id, > + boolean_type_node, > + orig_fn_decl, NULL_TREE, > + deref_fp, > + coro_frame_i_a_r_c_id); > + ramp_try_block = begin_try_block (); > + ramp_try_stmts = begin_compound_stmt (BCS_TRY_BLOCK); > } > > - /* deref the frame pointer, to use in member access code. */ > - tree deref_fp = build_x_arrow (loc, coro_fp, tf_warning_or_error); > - > - /* For now, once allocation has succeeded we always assume that this needs > - destruction, there's no impl. for frame allocation elision. */ > - tree fnf_m = lookup_member (frame_type, coro_frame_needs_free_id, > - 1, 0,tf_warning_or_error); > - tree fnf_x = build_class_member_access_expr (deref_fp, fnf_m, NULL_TREE, > - false, tf_warning_or_error); > - r = cp_build_init_expr (fnf_x, boolean_true_node); > - finish_expr_stmt (r); > - > /* Put the resumer and destroyer functions in. */ > > tree actor_addr = build1 (ADDR_EXPR, act_des_fn_ptr_type, resumer); > - tree resume_m > - = lookup_member (frame_type, coro_resume_fn_id, > - /*protect=*/1, /*want_type=*/0, tf_warning_or_error); > - tree resume_x = build_class_member_access_expr (deref_fp, resume_m, > NULL_TREE, > - false, tf_warning_or_error); > - r = cp_build_init_expr (loc, resume_x, actor_addr); > - finish_expr_stmt (r); > + tree actor_ptr > + = coro_build_and_push_artificial_var_with_dve (loc, coro_resume_fn_id, > + act_des_fn_ptr_type, > + orig_fn_decl, > + actor_addr, deref_fp, > + coro_resume_fn_id); > > tree destroy_addr = build1 (ADDR_EXPR, act_des_fn_ptr_type, destroyer); > - tree destroy_m > - = lookup_member (frame_type, coro_destroy_fn_id, > - /*protect=*/1, /*want_type=*/0, tf_warning_or_error); > - tree destroy_x > - = build_class_member_access_expr (deref_fp, destroy_m, NULL_TREE, false, > - tf_warning_or_error); > - r = cp_build_init_expr (loc, destroy_x, destroy_addr); > - finish_expr_stmt (r); > + tree destroy_ptr > + = coro_build_and_push_artificial_var_with_dve (loc, coro_destroy_fn_id, > + act_des_fn_ptr_type, > + orig_fn_decl, > + destroy_addr, deref_fp, > + coro_destroy_fn_id); > > /* [dcl.fct.def.coroutine] /13 > When a coroutine is invoked, a copy is created for each coroutine > @@ -4885,14 +4919,6 @@ cp_coroutine_transform::build_ramp_function () > } > } > > - /* Set up the promise. */ > - tree promise_m > - = lookup_member (frame_type, coro_promise_id, > - /*protect=*/1, /*want_type=*/0, tf_warning_or_error); > - > - tree p = build_class_member_access_expr (deref_fp, promise_m, NULL_TREE, > - false, tf_warning_or_error); > - > tree promise_dtor = NULL_TREE; > if (type_build_ctor_call (promise_type)) > { > @@ -4920,11 +4946,11 @@ cp_coroutine_transform::build_ramp_function () > tf_warning_or_error); > > finish_expr_stmt (r); > - > - r = build_modify_expr (loc, coro_promise_live, boolean_type_node, > - INIT_EXPR, loc, boolean_true_node, > - boolean_type_node); > - finish_expr_stmt (r); > + if (flag_exceptions) > + { > + r = cp_build_init_expr (coro_promise_live, boolean_true_node); > + finish_expr_stmt (r); > + } > > promise_dtor = cxx_maybe_build_cleanup (p, tf_warning_or_error); > } > @@ -4936,10 +4962,7 @@ cp_coroutine_transform::build_ramp_function () > > /* Without a return object we haven't got much clue what's going on. */ > if (!get_ro || get_ro == error_mark_node) > - { > - BIND_EXPR_BODY (ramp_bind) = pop_stmt_list (ramp_outer_bind); > - return false; > - } > + return false; > > /* Check for a bad get return object type. > [dcl.fct.def.coroutine] / 7 requires: > @@ -4954,15 +4977,15 @@ cp_coroutine_transform::build_ramp_function () > } > > /* Initialize the resume_idx_var to 0, meaning "not started". */ > - tree resume_idx_m > - = lookup_member (frame_type, coro_resume_index_id, > - /*protect=*/1, /*want_type=*/0, tf_warning_or_error); > - tree resume_idx > - = build_class_member_access_expr (deref_fp, resume_idx_m, NULL_TREE, > false, > - tf_warning_or_error); > - r = build_int_cst (short_unsigned_type_node, 0); > - r = cp_build_init_expr (loc, resume_idx, r); > - finish_expr_stmt (r); > + tree resume_idx = coro_build_and_push_artificial_var_with_dve > + (loc, coro_resume_index_id, short_unsigned_type_node, orig_fn_decl, > + build_zero_cst (short_unsigned_type_node), deref_fp, > coro_resume_index_id); > + > + if (flag_exceptions && iarc_x) > + { > + r = cp_build_init_expr (iarc_x, boolean_false_node); > + finish_expr_stmt (r); > + } > > /* Used for return objects in the RESULT slot. */ > tree ret_val_dtor = NULL_TREE; > @@ -5003,15 +5026,13 @@ cp_coroutine_transform::build_ramp_function () > of the initial suspend expression), then we need to clean up the > return value. */ > ret_val_dtor = cxx_maybe_build_cleanup (DECL_RESULT (orig_fn_decl), > - tf_warning_or_error); > + tf_warning_or_error); > > /* If we have a live g.r.o in the return slot, then signal this for > exception > cleanup. */ > - if (ret_val_dtor) > + if (flag_exceptions && ret_val_dtor) > { > - r = build_modify_expr (loc, coro_gro_live, boolean_type_node, > - INIT_EXPR, loc, boolean_true_node, > - boolean_type_node); > + r = cp_build_init_expr (coro_gro_live, boolean_true_node); > finish_expr_stmt (r); > } > > @@ -5031,7 +5052,8 @@ cp_coroutine_transform::build_ramp_function () > > if (flag_exceptions) > { > - TRY_HANDLERS (ramp_cleanup) = push_stmt_list (); > + finish_compound_stmt (ramp_try_stmts); > + finish_try_block (ramp_try_block); > tree handler = begin_handler (); > finish_handler_parms (NULL_TREE, handler); /* catch (...) */ > > @@ -5047,13 +5069,9 @@ cp_coroutine_transform::build_ramp_function () > > /* Before initial resume is called, the responsibility for cleanup on > exception falls to the ramp. After that, the coroutine body code > - should do the cleanup. */ > - tree iarc_m = lookup_member (frame_type, coro_frame_i_a_r_c_id, > - 1, 0, tf_warning_or_error); > - tree iarc_x > - = build_class_member_access_expr (deref_fp, iarc_m, NULL_TREE, > - /*preserve_reference*/false, > - tf_warning_or_error); > + should do the cleanup. This is signalled by the flag > + 'initial_await_resume_called'. */ > + > tree not_iarc > = build1_loc (loc, TRUTH_NOT_EXPR, boolean_type_node, iarc_x); > tree cleanup_if = begin_if_stmt (); > @@ -5095,7 +5113,7 @@ cp_coroutine_transform::build_ramp_function () > > /* No delete the frame if required. */ > tree fnf_if = begin_if_stmt (); > - finish_if_stmt_cond (fnf_x, fnf_if); > + finish_if_stmt_cond (frame_needs_free, fnf_if); > finish_expr_stmt (delete_frame_call); > finish_then_clause (fnf_if); > finish_if_stmt (fnf_if); > @@ -5108,11 +5126,9 @@ cp_coroutine_transform::build_ramp_function () > suppress_warning (rethrow); > finish_expr_stmt (rethrow); > finish_handler (handler); > - TRY_HANDLERS (ramp_cleanup) = pop_stmt_list (TRY_HANDLERS > (ramp_cleanup)); > + finish_handler_sequence (ramp_try_block); > } > - > - BIND_EXPR_BODY (ramp_bind) = pop_stmt_list (ramp_outer_bind); > - finish_function_body (ramp_fnbody); > + finish_compound_stmt (ramp_fnbody); > return true; > } > > -- > 2.39.2 (Apple Git-143) >