On 8/29/24 11:21 AM, Iain Sandoe wrote:
This will help with the patches to follow for revising the
frame layout when we have an entry with excess alignment.

Tested on x86_64 darwin/linux powerpc64le linux, OK for trunk?
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.
        (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 | 264 +++++++++++++++++++++----------------------
  1 file changed, 129 insertions(+), 135 deletions(-)

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index 2a1183d70e4..d4d74838eb4 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -1731,6 +1731,20 @@ coro_build_artificial_var (location_t loc, const char 
*name, tree type,
                                    type, ctx, init);
  }
+/* 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;
+}
/* 2. Create a named label in the specified context. */ @@ -3841,8 +3855,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,15 +3893,14 @@ 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 ("anon%d", parm_num);

Why the reduction in verbosity here?

          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_" : "",
+         char *buf = xasprintf ("_Coro_q%u_%s_live", parm_num,
                                 IDENTIFIER_POINTER (name));
          parm.guard_var
            = coro_build_artificial_var (UNKNOWN_LOCATION, get_identifier (buf),
@@ -4569,7 +4584,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 +4643,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 +4673,54 @@ 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;
-      }
+  tree ramp_fnbody = begin_compound_stmt (BCS_FN_BODY);
+  coro_fp = pushdecl (coro_fp);
+  add_decl_expr (coro_fp);
- /* 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;
+  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_artificial_var (loc, "_Coro_promise_live",
+                                    boolean_type_node, orig_fn_decl,
+                                    boolean_false_node);
+      coro_promise_live = pushdecl (coro_promise_live);
+      add_decl_expr (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_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);
-
-  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;
+      coro_gro_live = pushdecl (coro_gro_live);
+      add_decl_expr (coro_gro_live);
+      /* 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_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);
+  frame_needs_free = pushdecl (frame_needs_free);
+  add_decl_expr (frame_needs_free);
/* Build the frame. */ @@ -4764,52 +4765,59 @@ 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_artificial_var_with_dve (loc, coro_promise_id,
+                                              promise_type, orig_fn_decl,
+                                              NULL_TREE, deref_fp,
+                                              coro_promise_id);
+  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?

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_?

Jason

Reply via email to