public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH 0/8] coroutines: Use DECL_VALUE_EXPRs to assist in debug [PR99215]
@ 2021-09-01 10:51 Iain Sandoe
  2021-09-01 10:52 ` [PATCH 1/8] coroutines : Use DECL_VALUE_EXPR instead of rewriting vars Iain Sandoe
  0 siblings, 1 reply; 28+ messages in thread
From: Iain Sandoe @ 2021-09-01 10:51 UTC (permalink / raw)
  To: GCC Patches

Hi,

As discussed with Jason at WG21 in Prague - last year, this amends the
way in which the coroutine frame copies of variables are represented in
the front end lowering.  Instead of an explicit pointer->field we now
apply a DECL_VALUE_EXPR to each instead (where that value is the
pointer->field).

Although this was initially targeted at improving the debug experience
in fact it simplifies and shortens the code, and makes subsequent bug-
fixes easier to implement.  Therefore, I'm posting this series first.

In addition to the change applied to local variables:

  - there is an implementation of a mechanism to make the original
    function arguments debug-visible.  These are actually copied into
    the frame by the ramp function, so we create proxy vars for them 
    and attach a DECL_VALUE_EXPR for the frame copy.

  - It is likely that someone debugging a coroutine will want to inspect
    parts of the implementation state (especially items such as the
    suspend index and the promise object).  Although these are not a
    direct part of the user's source code, we expose them as synthetic
    variables in the implementation name-space _Coro_xxxxxx.

  Patches to follow:
  coroutines: Use DECL_VALUE_EXPR instead of rewriting vars.
  coroutines: Add a helper for creating local vars.
  coroutines: Support for debugging implementation state.
  coroutines: Make some of the artificial names more debugger-friendly.
  coroutines: Define and populate accessors for debug state.
  coroutines: Convert implementation variables to debug-friendly form.
  coroutines: Make proxy vars for the function arg copies.
  coroutines: Make the continue handle visible to debug.

 gcc/cp/coroutines.cc | 701 +++++++++++++++++++------------------------
 1 file changed, 304 insertions(+), 397 deletions(-)

This has been tested quite a bit over some months on x86_64-darwin and
on x86_64, powerpc64-linux,
OK for master?
backports?

thanks,
Iain

***
I've tested with GDB and LLDB that we now have visibility of the coro
state, local variables and function parameters.  However the debug
experience is still a little "jumpy" in respect of stepping through the
code - this is because synthesized expressions default to using
'input_location' as their location - which is typically pointing to the
closing brace of the function.  So I think there is more that can
be done for PR99215, but it's a different kind of problem from the one
being solved with the DECL_VALUE_EXPRs.


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH 1/8] coroutines : Use DECL_VALUE_EXPR instead of rewriting  vars.
  2021-09-01 10:51 [PATCH 0/8] coroutines: Use DECL_VALUE_EXPRs to assist in debug [PR99215] Iain Sandoe
@ 2021-09-01 10:52 ` Iain Sandoe
  2021-09-01 10:53   ` [PATCH 2/8] coroutines: Add a helper for creating local vars Iain Sandoe
  2021-09-02 21:51   ` [PATCH 1/8] coroutines : Use DECL_VALUE_EXPR instead of rewriting vars Jason Merrill
  0 siblings, 2 replies; 28+ messages in thread
From: Iain Sandoe @ 2021-09-01 10:52 UTC (permalink / raw)
  To: GCC Patches

Hi,

Variables that need to persist over suspension expressions
must be preserved by being copied into the coroutine frame.

The initial implementations do this manually in the transform
code.  However, that has various disadvantages - including
that the debug connections are lost between the original var
and the frame copy.

The revised implementation makes use of DECL_VALUE_EXPRs to
contain the frame offset expressions, so that the original
var names are preserved in the code.

This process is also applied to the function parms which are
always copied to the frame.  In this case the decls need to be
copied since they are used in two different contexts during
the re-write (in the building of the ramp function, and in
the actor function itself).

This will assist in improvement of debugging (PR 99215).

Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>

gcc/cp/ChangeLog:

	* coroutines.cc (transform_local_var_uses): Record
	frame offset expressions as DECL_VALUE_EXPRs instead of
	rewriting them.
---
 gcc/cp/coroutines.cc | 105 +++----------------------------------------
 1 file changed, 5 insertions(+), 100 deletions(-)

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index ceb3d3be75e..2d68098f242 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -1974,8 +1974,7 @@ transform_local_var_uses (tree *stmt, int *do_subtree, void *d)
   local_vars_transform *lvd = (local_vars_transform *) d;
 
   /* For each var in this bind expr (that has a frame id, which means it was
-     accessed), build a frame reference for each and then walk the bind expr
-     statements, substituting the frame ref for the original var.  */
+     accessed), build a frame reference and add it as the DECL_VALUE_EXPR.  */
 
   if (TREE_CODE (*stmt) == BIND_EXPR)
     {
@@ -1991,13 +1990,9 @@ transform_local_var_uses (tree *stmt, int *do_subtree, void *d)
 	  /* Re-write the variable's context to be in the actor func.  */
 	  DECL_CONTEXT (lvar) = lvd->context;
 
-	/* For capture proxies, this could include the decl value expr.  */
-	if (local_var.is_lambda_capture || local_var.has_value_expr_p)
-	  {
-	    tree ve = DECL_VALUE_EXPR (lvar);
-	    cp_walk_tree (&ve, transform_local_var_uses, d, NULL);
+	  /* For capture proxies, this could include the decl value expr.  */
+	  if (local_var.is_lambda_capture || local_var.has_value_expr_p)
 	    continue; /* No frame entry for this.  */
-	  }
 
 	  /* TODO: implement selective generation of fields when vars are
 	     known not-used.  */
@@ -2011,103 +2006,13 @@ transform_local_var_uses (tree *stmt, int *do_subtree, void *d)
 	  tree fld_idx = build3_loc (lvd->loc, COMPONENT_REF, TREE_TYPE (lvar),
 				     lvd->actor_frame, fld_ref, NULL_TREE);
 	  local_var.field_idx = fld_idx;
-	}
-      /* FIXME: we should be able to do this in the loop above, but (at least
-	 for range for) there are cases where the DECL_INITIAL contains
-	 forward references.
-	 So, now we've built the revised var in the frame, substitute uses of
-	 it in initializers and the bind expr body.  */
-      for (lvar = BIND_EXPR_VARS (*stmt); lvar != NULL;
-	   lvar = DECL_CHAIN (lvar))
-	{
-	  /* we need to walk some of the decl trees, which might contain
-	     references to vars replaced at a higher level.  */
-	  cp_walk_tree (&DECL_INITIAL (lvar), transform_local_var_uses, d,
-			NULL);
-	  cp_walk_tree (&DECL_SIZE (lvar), transform_local_var_uses, d, NULL);
-	  cp_walk_tree (&DECL_SIZE_UNIT (lvar), transform_local_var_uses, d,
-			NULL);
+	  SET_DECL_VALUE_EXPR (lvar, fld_idx);
+	  DECL_HAS_VALUE_EXPR_P (lvar) = true;
 	}
       cp_walk_tree (&BIND_EXPR_BODY (*stmt), transform_local_var_uses, d, NULL);
-
-      /* Now we have processed and removed references to the original vars,
-	 we can drop those from the bind - leaving capture proxies alone.  */
-      for (tree *pvar = &BIND_EXPR_VARS (*stmt); *pvar != NULL;)
-	{
-	  bool existed;
-	  local_var_info &local_var
-	    = lvd->local_var_uses->get_or_insert (*pvar, &existed);
-	  gcc_checking_assert (existed);
-
-	  /* Leave lambda closure captures alone, we replace the *this
-	     pointer with the frame version and let the normal process
-	     deal with the rest.
-	     Likewise, variables with their value found elsewhere.
-	     Skip past unused ones too.  */
-	  if (local_var.is_lambda_capture
-	     || local_var.has_value_expr_p
-	     || local_var.field_id == NULL_TREE)
-	    {
-	      pvar = &DECL_CHAIN (*pvar);
-	      continue;
-	    }
-
-	  /* Discard this one, we replaced it.  */
-	  *pvar = DECL_CHAIN (*pvar);
-	}
-
       *do_subtree = 0; /* We've done the body already.  */
       return NULL_TREE;
     }
-
-  tree var_decl = *stmt;
-  /* Look inside cleanups, we don't want to wrap a statement list in a
-     cleanup.  */
-  bool needs_cleanup = true;
-  if (TREE_CODE (var_decl) == CLEANUP_POINT_EXPR)
-    var_decl = TREE_OPERAND (var_decl, 0);
-  else
-    needs_cleanup = false;
-
-  /* Look inside the decl_expr for the actual var.  */
-  bool decl_expr_p = TREE_CODE (var_decl) == DECL_EXPR;
-  if (decl_expr_p && TREE_CODE (DECL_EXPR_DECL (var_decl)) == VAR_DECL)
-    var_decl = DECL_EXPR_DECL (var_decl);
-  else if (TREE_CODE (var_decl) != VAR_DECL)
-    return NULL_TREE;
-
-  /* VAR_DECLs that are not recorded can belong to the proxies we've placed
-     for the promise and coroutine handle(s), to global vars or to compiler
-     temporaries.  Skip past these, we will handle them later.  */
-  local_var_info *local_var_i = lvd->local_var_uses->get (var_decl);
-
-  if (local_var_i == NULL)
-    return NULL_TREE;
-
-  if (local_var_i->is_lambda_capture
-      || local_var_i->is_static
-      || local_var_i->has_value_expr_p)
-    return NULL_TREE;
-
-  /* This is our revised 'local' i.e. a frame slot.  */
-  tree revised = local_var_i->field_idx;
-  gcc_checking_assert (DECL_CONTEXT (var_decl) == lvd->context);
-
-  if (decl_expr_p && DECL_INITIAL (var_decl))
-    {
-      location_t loc = DECL_SOURCE_LOCATION (var_decl);
-      tree r
-	= cp_build_modify_expr (loc, revised, INIT_EXPR,
-				DECL_INITIAL (var_decl), tf_warning_or_error);
-      if (needs_cleanup)
-	r = coro_build_cvt_void_expr_stmt (r, EXPR_LOCATION (*stmt));
-      *stmt = r;
-    }
-  else
-    *stmt = revised;
-
-  if (decl_expr_p)
-    *do_subtree = 0; /* We've accounted for the nested use.  */
   return NULL_TREE;
 }
 
-- 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH 2/8] coroutines: Add a helper for creating local vars.
  2021-09-01 10:52 ` [PATCH 1/8] coroutines : Use DECL_VALUE_EXPR instead of rewriting vars Iain Sandoe
@ 2021-09-01 10:53   ` Iain Sandoe
  2021-09-01 10:53     ` PATCH 3/8] coroutines: Support for debugging implementation state Iain Sandoe
  2021-09-02 21:52     ` [PATCH 2/8] coroutines: Add a helper for creating local vars Jason Merrill
  2021-09-02 21:51   ` [PATCH 1/8] coroutines : Use DECL_VALUE_EXPR instead of rewriting vars Jason Merrill
  1 sibling, 2 replies; 28+ messages in thread
From: Iain Sandoe @ 2021-09-01 10:53 UTC (permalink / raw)
  To: GCC Patches


This is primarily code factoring, but we take this opportunity
to rename some of the implementation variables (which we intend
to expose to debugging) so that they are in the implementation
namespace.

Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>

gcc/cp/ChangeLog:

	* coroutines.cc (coro_build_artificial_var): New.
	(build_actor_fn): Use var builder, rename vars to use
	implementation namespace.
	(coro_rewrite_function_body): Likewise.
	(morph_fn_to_coro): Likewise.
---
 gcc/cp/coroutines.cc | 69 +++++++++++++++++++++++++++-----------------
 1 file changed, 43 insertions(+), 26 deletions(-)

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index 2d68098f242..b8501032969 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -1474,6 +1474,29 @@ coro_build_cvt_void_expr_stmt (tree expr, location_t loc)
   return coro_build_expr_stmt (t, loc);
 }
 
+/* Helpers to build an artificial var, with location LOC, NAME and TYPE, in
+   CTX, and with initializer INIT.  */
+
+static tree
+coro_build_artificial_var (location_t loc, tree name, tree type, tree ctx,
+			   tree init)
+{
+  tree res = build_lang_decl (VAR_DECL, name, type);
+  DECL_SOURCE_LOCATION (res) = loc;
+  DECL_CONTEXT (res) = ctx;
+  DECL_ARTIFICIAL (res) = true;
+  DECL_INITIAL (res) = init;
+  return res;
+}
+
+static tree
+coro_build_artificial_var (location_t loc, const char *name, tree type,
+			   tree ctx, tree init)
+{
+  return coro_build_artificial_var (loc, get_identifier (name),
+				    type, ctx, init);
+}
+
 /* Helpers for label creation:
    1. Create a named label in the specified context.  */
 
@@ -2113,12 +2136,10 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
   tree top_block = make_node (BLOCK);
   BIND_EXPR_BLOCK (actor_bind) = top_block;
 
-  tree continuation = build_lang_decl (VAR_DECL,
-				       get_identifier ("actor.continue"),
-				       void_coro_handle_type);
-  DECL_ARTIFICIAL (continuation) = 1;
-  DECL_IGNORED_P (continuation) = 1;
-  DECL_CONTEXT (continuation) = actor;
+  tree continuation = coro_build_artificial_var (loc, "_Coro_actor_continue",
+						 void_coro_handle_type, actor,
+						 NULL_TREE);
+  
   BIND_EXPR_VARS (actor_bind) = continuation;
 
   /* Link in the block associated with the outer scope of the re-written
@@ -4069,12 +4090,11 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
 					 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;
+      tree i_a_r_c = coro_build_artificial_var (fn_start, "_Coro_i_a_r_c",
+						boolean_type_node, orig,
+						boolean_false_node);
       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);
@@ -4459,8 +4479,10 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
   add_stmt (ramp_bind);
   tree ramp_body = push_stmt_list ();
 
-  tree coro_fp = build_lang_decl (VAR_DECL, get_identifier ("coro.frameptr"),
-				  coro_frame_ptr);
+  tree zeroinit = build1_loc (fn_start, CONVERT_EXPR,
+			      coro_frame_ptr, integer_zero_node);
+  tree coro_fp = coro_build_artificial_var (fn_start, "_Coro_frameptr",
+					    coro_frame_ptr, orig, zeroinit);
   tree varlist = coro_fp;
 
   /* To signal that we need to cleanup copied function args.  */
@@ -4478,21 +4500,19 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
 
   /* Signal that we need to clean up the promise object on exception.  */
   tree coro_promise_live
-   = build_lang_decl (VAR_DECL, get_identifier ("coro.promise.live"),
-		      boolean_type_node);
-  DECL_ARTIFICIAL (coro_promise_live) = true;
+    = coro_build_artificial_var (fn_start, "_Coro_promise_live",
+				 boolean_type_node, orig, boolean_false_node);
   DECL_CHAIN (coro_promise_live) = varlist;
   varlist = coro_promise_live;
-  DECL_INITIAL (coro_promise_live) = boolean_false_node;
+
   /* When the get-return-object is in the RETURN slot, we need to arrange for
      cleanup on exception.  */
   tree coro_gro_live
-   = build_lang_decl (VAR_DECL, get_identifier ("coro.gro.live"),
-		      boolean_type_node);
-  DECL_ARTIFICIAL (coro_gro_live) = true;
+    = coro_build_artificial_var (fn_start, "_Coro_gro_live",
+				 boolean_type_node, orig, boolean_false_node);
+
   DECL_CHAIN (coro_gro_live) = varlist;
   varlist = coro_gro_live;
-  DECL_INITIAL (coro_gro_live) = boolean_false_node;
 
   /* Collected the scope vars we need ... only one for now. */
   BIND_EXPR_VARS (ramp_bind) = nreverse (varlist);
@@ -4508,8 +4528,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
   /* The decl_expr for the coro frame pointer, initialize to zero so that we
      can pass it to the IFN_CO_FRAME (since there's no way to pass a type,
      directly apparently).  This avoids a "used uninitialized" warning.  */
-  tree zeroinit = build1 (CONVERT_EXPR, coro_frame_ptr, integer_zero_node);
-  DECL_INITIAL (coro_fp) = zeroinit;
+
   add_decl_expr (coro_fp);
   if (flag_exceptions && DECL_ARGUMENTS (orig))
     for (tree arg = DECL_ARGUMENTS (orig); arg != NULL;
@@ -4969,10 +4988,8 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
     {
       /* ... or ... Construct an object that will be used as the single
 	param to the CTOR for the return object.  */
-      gro = build_lang_decl (VAR_DECL, get_identifier ("coro.gro"), gro_type);
-      DECL_CONTEXT (gro) = current_scope ();
-      DECL_ARTIFICIAL (gro) = true;
-      DECL_IGNORED_P (gro) = true;
+      gro = coro_build_artificial_var (fn_start, "_Coro_gro", gro_type, orig,
+				       NULL_TREE);
       add_decl_expr (gro);
       gro_bind_vars = gro;
       r = cp_build_modify_expr (input_location, gro, INIT_EXPR, get_ro,
-- 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* PATCH 3/8] coroutines: Support for debugging implementation state.
  2021-09-01 10:53   ` [PATCH 2/8] coroutines: Add a helper for creating local vars Iain Sandoe
@ 2021-09-01 10:53     ` Iain Sandoe
  2021-09-01 10:54       ` [PATCH 4/8] coroutines: Make some of the artificial names more debugger-friendly Iain Sandoe
  2021-09-03 13:31       ` PATCH 3/8] coroutines: Support for debugging implementation state Jason Merrill
  2021-09-02 21:52     ` [PATCH 2/8] coroutines: Add a helper for creating local vars Jason Merrill
  1 sibling, 2 replies; 28+ messages in thread
From: Iain Sandoe @ 2021-09-01 10:53 UTC (permalink / raw)
  To: GCC Patches


Some of the state that is associated with the implementation
is of interest to a user debugging a coroutine.  In particular
items such as the suspend point, promise object, and current
suspend point.

These variables live in the coroutine frame, but we can inject
proxies for them into the outermost bind expression of the
coroutine.  Such variables are automatically moved into the
coroutine frame (if they need to persist across a suspend
expression).  PLacing the proxies thus allows the user to
inspect them by name in the debugger.

To implement this, we ensure that (at the outermost scope) the
frame entries are not mangled (coroutine frame variables are
usually mangled with scope nesting information so that they do
not clash).  We can safely avoid doing this for the outermost
scope so that we can map frame entries directly to the variables.

This is partial contribution to debug support (PR 99215).

Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>

gcc/cp/ChangeLog:

	* coroutines.cc (register_local_var_uses): Do not mangle
	frame entries for the outermost scope.  Record the outer
	scope as nesting depth 0.
---
 gcc/cp/coroutines.cc | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index b8501032969..a12714ea67e 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -3885,8 +3885,6 @@ register_local_var_uses (tree *stmt, int *do_subtree, void *d)
 
   if (TREE_CODE (*stmt) == BIND_EXPR)
     {
-      lvd->bind_indx++;
-      lvd->nest_depth++;
       tree lvar;
       for (lvar = BIND_EXPR_VARS (*stmt); lvar != NULL;
 	   lvar = DECL_CHAIN (lvar))
@@ -3925,11 +3923,17 @@ register_local_var_uses (tree *stmt, int *do_subtree, void *d)
 	    continue;
 
 	  /* Make names depth+index unique, so that we can support nested
-	     scopes with identically named locals.  */
+	     scopes with identically named locals and still be able to
+	     identify them in the coroutine frame.  */
 	  tree lvname = DECL_NAME (lvar);
 	  char *buf;
-	  if (lvname != NULL_TREE)
-	    buf = xasprintf ("__%s.%u.%u", IDENTIFIER_POINTER (lvname),
+	  /* The outermost bind scope contains the artificial variables that
+	     we inject to implement the coro state machine.  We want to be able
+	     to inspect these in debugging.  */
+	  if (lvname != NULL_TREE && lvd->nest_depth == 0)
+	    buf = xasprintf ("%s", IDENTIFIER_POINTER (lvname));
+	  else if (lvname != NULL_TREE)
+	    buf = xasprintf ("%s_%u_%u", IDENTIFIER_POINTER (lvname),
 			     lvd->nest_depth, lvd->bind_indx);
 	  else
 	    buf = xasprintf ("_D%u.%u.%u", DECL_UID (lvar), lvd->nest_depth,
@@ -3942,6 +3946,8 @@ register_local_var_uses (tree *stmt, int *do_subtree, void *d)
 	  /* We don't walk any of the local var sub-trees, they won't contain
 	     any bind exprs.  */
 	}
+      lvd->bind_indx++;
+      lvd->nest_depth++;
       cp_walk_tree (&BIND_EXPR_BODY (*stmt), register_local_var_uses, d, NULL);
       *do_subtree = 0; /* We've done this.  */
       lvd->nest_depth--;
-- 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH 4/8] coroutines: Make some of the artificial names more  debugger-friendly.
  2021-09-01 10:53     ` PATCH 3/8] coroutines: Support for debugging implementation state Iain Sandoe
@ 2021-09-01 10:54       ` Iain Sandoe
  2021-09-01 10:54         ` [PATCH 5/8] coroutines: Define and populate accessors for debug state Iain Sandoe
  2021-09-03 13:35         ` [PATCH 4/8] coroutines: Make some of the artificial names more debugger-friendly Jason Merrill
  2021-09-03 13:31       ` PATCH 3/8] coroutines: Support for debugging implementation state Jason Merrill
  1 sibling, 2 replies; 28+ messages in thread
From: Iain Sandoe @ 2021-09-01 10:54 UTC (permalink / raw)
  To: GCC Patches


Some of the compiler-generated entries are of interest to a
user debugging - keep variables in the implementation namespace
but avoid using periods as separators (which is not compatible
with visible symbols for some assemblers).

Partial improvement to debugging (PR 99215).

Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>

gcc/cp/ChangeLog:

	* coroutines.cc (coro_promise_type_found_p): Rename variable
	to make it suitable for debugging.
	(build_actor_fn): Likewise.
	(build_destroy_fn): Likewise.
	(register_local_var_uses): Likewise.
	(coro_rewrite_function_body): Likewise.
	(morph_fn_to_coro): Likewise.
---
 gcc/cp/coroutines.cc | 59 ++++++++++++++++++++++----------------------
 1 file changed, 30 insertions(+), 29 deletions(-)

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index a12714ea67e..081e1a46c63 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -513,12 +513,12 @@ coro_promise_type_found_p (tree fndecl, location_t loc)
       /* Build a proxy for a handle to "self" as the param to
 	 await_suspend() calls.  */
       coro_info->self_h_proxy
-	= build_lang_decl (VAR_DECL, get_identifier ("self_h.proxy"),
+	= build_lang_decl (VAR_DECL, get_identifier ("_Coro_self_handle"),
 			   coro_info->handle_type);
 
       /* Build a proxy for the promise so that we can perform lookups.  */
       coro_info->promise_proxy
-	= build_lang_decl (VAR_DECL, get_identifier ("promise.proxy"),
+	= build_lang_decl (VAR_DECL, get_identifier ("_Coro_promise"),
 			   coro_info->promise_type);
 
       /* Note where we first saw a coroutine keyword.  */
@@ -2198,7 +2198,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
     = {actor, actor_frame, coro_frame_type, loc, local_var_uses};
   cp_walk_tree (&fnbody, transform_local_var_uses, &xform_vars_data, NULL);
 
-  tree resume_idx_name = get_identifier ("__resume_at");
+  tree resume_idx_name = get_identifier ("_Coro_resume_index");
   tree rat_field = lookup_member (coro_frame_type, resume_idx_name, 1, 0,
 				  tf_warning_or_error);
   tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, actor_frame,
@@ -2303,13 +2303,13 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
   add_stmt (r);
 
   /* actor's version of the promise.  */
-  tree ap_m = lookup_member (coro_frame_type, get_identifier ("__p"), 1, 0,
+  tree ap_m = lookup_member (coro_frame_type, get_identifier ("_Coro_promise"), 1, 0,
 			     tf_warning_or_error);
   tree ap = build_class_member_access_expr (actor_frame, ap_m, NULL_TREE, false,
 					    tf_warning_or_error);
 
   /* actor's coroutine 'self handle'.  */
-  tree ash_m = lookup_member (coro_frame_type, get_identifier ("__self_h"), 1,
+  tree ash_m = lookup_member (coro_frame_type, get_identifier ("_Coro_self_handle"), 1,
 			      0, tf_warning_or_error);
   tree ash = build_class_member_access_expr (actor_frame, ash_m, NULL_TREE,
 					     false, tf_warning_or_error);
@@ -2343,12 +2343,12 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
   p_data.to = ap;
   cp_walk_tree (&fnbody, replace_proxy, &p_data, NULL);
 
-  /* The rewrite of the function adds code to set the __resume field to
+  /* The rewrite of the function adds code to set the resume_fn field to
      nullptr when the coroutine is done and also the index to zero when
      calling an unhandled exception.  These are represented by two proxies
      in the function, so rewrite them to the proper frame access.  */
   tree resume_m
-    = lookup_member (coro_frame_type, get_identifier ("__resume"),
+    = lookup_member (coro_frame_type, get_identifier ("_Coro_resume_fn"),
 		     /*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);
@@ -2381,7 +2381,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
   /* Here deallocate the frame (if we allocated it), which we will have at
      present.  */
   tree fnf_m
-    = lookup_member (coro_frame_type, get_identifier ("__frame_needs_free"), 1,
+    = lookup_member (coro_frame_type, get_identifier ("_Coro_frame_needs_free"), 1,
 		     0, tf_warning_or_error);
   tree fnf2_x = build_class_member_access_expr (actor_frame, fnf_m, NULL_TREE,
 						false, tf_warning_or_error);
@@ -2504,7 +2504,7 @@ build_destroy_fn (location_t loc, tree coro_frame_type, tree destroy,
 
   tree destr_frame = build1 (INDIRECT_REF, coro_frame_type, destr_fp);
 
-  tree resume_idx_name = get_identifier ("__resume_at");
+  tree resume_idx_name = get_identifier ("_Coro_resume_index");
   tree rat_field = lookup_member (coro_frame_type, resume_idx_name, 1, 0,
 				  tf_warning_or_error);
   tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, destr_frame,
@@ -3927,6 +3927,7 @@ register_local_var_uses (tree *stmt, int *do_subtree, void *d)
 	     identify them in the coroutine frame.  */
 	  tree lvname = DECL_NAME (lvar);
 	  char *buf;
+
 	  /* The outermost bind scope contains the artificial variables that
 	     we inject to implement the coro state machine.  We want to be able
 	     to inspect these in debugging.  */
@@ -3936,7 +3937,7 @@ register_local_var_uses (tree *stmt, int *do_subtree, void *d)
 	    buf = xasprintf ("%s_%u_%u", IDENTIFIER_POINTER (lvname),
 			     lvd->nest_depth, lvd->bind_indx);
 	  else
-	    buf = xasprintf ("_D%u.%u.%u", DECL_UID (lvar), lvd->nest_depth,
+	    buf = xasprintf ("_D%u_%u_%u", DECL_UID (lvar), lvd->nest_depth,
 			     lvd->bind_indx);
 	  /* TODO: Figure out if we should build a local type that has any
 	     excess alignment or size from the original decl.  */
@@ -4214,15 +4215,15 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
  declare a dummy coro frame.
  struct _R_frame {
   using handle_type = coro::coroutine_handle<coro1::promise_type>;
-  void (*__resume)(_R_frame *);
-  void (*__destroy)(_R_frame *);
-  coro1::promise_type __p;
-  bool frame_needs_free; free the coro frame mem if set.
-  bool i_a_r_c; [dcl.fct.def.coroutine] / 5.3
-  short __resume_at;
-  handle_type self_handle;
-  (maybe) parameter copies.
-  (maybe) local variables saved (including awaitables)
+  void (*_Coro_resume_fn)(_R_frame *);
+  void (*_Coro_destroy_fn)(_R_frame *);
+  coro1::promise_type _Coro_promise;
+  bool _Coro_frame_needs_free; free the coro frame mem if set.
+  bool _Coro_i_a_r_c; [dcl.fct.def.coroutine] / 5.3
+  short _Coro_resume_index;
+  handle_type _Coro_self_handle;
+  parameter copies (were required).
+  local variables saved (including awaitables)
   (maybe) trailing space.
  };  */
 
@@ -4314,7 +4315,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
 
   /* 2. Types we need to define or look up.  */
 
-  tree fr_name = get_fn_local_identifier (orig, "frame");
+  tree fr_name = get_fn_local_identifier (orig, "Frame");
   tree coro_frame_type = xref_tag (record_type, fr_name);
   DECL_CONTEXT (TYPE_NAME (coro_frame_type)) = current_scope ();
   tree coro_frame_ptr = build_pointer_type (coro_frame_type);
@@ -4342,22 +4343,22 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
 
   tree field_list = NULL_TREE;
   tree resume_name
-    = coro_make_frame_entry (&field_list, "__resume",
+    = coro_make_frame_entry (&field_list, "_Coro_resume_fn",
 			     act_des_fn_ptr, fn_start);
   tree destroy_name
-    = coro_make_frame_entry (&field_list, "__destroy",
+    = coro_make_frame_entry (&field_list, "_Coro_destroy_fn",
 			     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",
+    = coro_make_frame_entry (&field_list, "_Coro_promise", promise_type, fn_start);
+  tree fnf_name = coro_make_frame_entry (&field_list, "_Coro_frame_needs_free",
 					 boolean_type_node, fn_start);
   tree resume_idx_name
-    = coro_make_frame_entry (&field_list, "__resume_at",
+    = coro_make_frame_entry (&field_list, "_Coro_resume_index",
 			     short_unsigned_type_node, fn_start);
 
   /* We need a handle to this coroutine, which is passed to every
      await_suspend().  There's no point in creating it over and over.  */
-  (void) coro_make_frame_entry (&field_list, "__self_h", handle_type, fn_start);
+  (void) coro_make_frame_entry (&field_list, "_Coro_self_handle", handle_type, fn_start);
 
   /* Now add in fields for function params (if there are any).
      We do not attempt elision of copies at this stage, we do analyze the
@@ -4415,14 +4416,14 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
 	  if (DECL_NAME (arg))
 	    {
 	      tree pname = DECL_NAME (arg);
-	      buf = xasprintf ("__parm.%s", IDENTIFIER_POINTER (pname));
+	      buf = xasprintf ("_P_%s", IDENTIFIER_POINTER (pname));
 	    }
 	  else
-	    buf = xasprintf ("__unnamed_parm.%d", no_name_parm++);
+	    buf = xasprintf ("_P_unnamed_%d", no_name_parm++);
 
 	  if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (parm.frame_type))
 	    {
-	      char *gbuf = xasprintf ("%s.live", buf);
+	      char *gbuf = xasprintf ("%s_live", buf);
 	      parm.guard_var
 		= build_lang_decl (VAR_DECL, get_identifier (gbuf),
 				   boolean_type_node);
-- 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH 5/8] coroutines: Define and populate accessors for debug  state.
  2021-09-01 10:54       ` [PATCH 4/8] coroutines: Make some of the artificial names more debugger-friendly Iain Sandoe
@ 2021-09-01 10:54         ` Iain Sandoe
  2021-09-01 10:55           ` [PATCH 6/8] coroutines: Convert implementation variables to debug-friendly form Iain Sandoe
  2021-09-03 13:39           ` [PATCH 5/8] coroutines: Define and populate accessors for debug state Jason Merrill
  2021-09-03 13:35         ` [PATCH 4/8] coroutines: Make some of the artificial names more debugger-friendly Jason Merrill
  1 sibling, 2 replies; 28+ messages in thread
From: Iain Sandoe @ 2021-09-01 10:54 UTC (permalink / raw)
  To: GCC Patches


This is an efficiency measure and repeats the pattern used for
other identifiers used in the coroutine implementation.

In support of debugging, the user might well need to look at some
of the variables that the implementation manipulates in lowering
the coroutines.  The defines the identifiers for these and populates
them on demand (avoiding repeated identifier calls).

Contributory to debug support (PR 99215)

Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>

gcc/cp/ChangeLog:

	* coroutines.cc: Add identifiers for implementation
	variables that we want to expose to debug.
	(coro_init_identifiers): Initialize implementation names.
	(coro_promise_type_found_p): Use pre-built identifiers.
	(build_actor_fn): Likewise.
	(build_destroy_fn): Likewise.
---
 gcc/cp/coroutines.cc | 32 ++++++++++++++++++++++++--------
 1 file changed, 24 insertions(+), 8 deletions(-)

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index 081e1a46c63..3b46aac4dc5 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -215,7 +215,17 @@ static GTY(()) tree coro_await_ready_identifier;
 static GTY(()) tree coro_await_suspend_identifier;
 static GTY(()) tree coro_await_resume_identifier;
 
-/* Create the identifiers used by the coroutines library interfaces.  */
+/* Accessors for the coroutine frame state used by the implementation.  */
+
+static GTY(()) tree coro_resume_fn_field;
+static GTY(()) tree coro_destroy_fn_field;
+static GTY(()) tree coro_promise_field;
+static GTY(()) tree coro_frame_needs_free_field;
+static GTY(()) tree coro_resume_index_field;
+static GTY(()) tree coro_self_handle_field;
+
+/* Create the identifiers used by the coroutines library interfaces and
+   the implementation frame state.  */
 
 static void
 coro_init_identifiers ()
@@ -241,6 +251,14 @@ coro_init_identifiers ()
   coro_await_ready_identifier = get_identifier ("await_ready");
   coro_await_suspend_identifier = get_identifier ("await_suspend");
   coro_await_resume_identifier = get_identifier ("await_resume");
+
+  /* Coroutine state frame field accessors.  */
+  coro_resume_fn_field = get_identifier ("_Coro_resume_fn");
+  coro_destroy_fn_field = get_identifier ("_Coro_destroy_fn");
+  coro_promise_field = get_identifier ("_Coro_promise");
+  coro_frame_needs_free_field = get_identifier ("_Coro_frame_needs_free");
+  coro_resume_index_field = get_identifier ("_Coro_resume_index");
+  coro_self_handle_field = get_identifier ("_Coro_self_handle");
 }
 
 /* Trees we only need to set up once.  */
@@ -513,12 +531,12 @@ coro_promise_type_found_p (tree fndecl, location_t loc)
       /* Build a proxy for a handle to "self" as the param to
 	 await_suspend() calls.  */
       coro_info->self_h_proxy
-	= build_lang_decl (VAR_DECL, get_identifier ("_Coro_self_handle"),
+	= build_lang_decl (VAR_DECL, coro_self_handle_field,
 			   coro_info->handle_type);
 
       /* Build a proxy for the promise so that we can perform lookups.  */
       coro_info->promise_proxy
-	= build_lang_decl (VAR_DECL, get_identifier ("_Coro_promise"),
+	= build_lang_decl (VAR_DECL, coro_promise_field,
 			   coro_info->promise_type);
 
       /* Note where we first saw a coroutine keyword.  */
@@ -2198,8 +2216,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
     = {actor, actor_frame, coro_frame_type, loc, local_var_uses};
   cp_walk_tree (&fnbody, transform_local_var_uses, &xform_vars_data, NULL);
 
-  tree resume_idx_name = get_identifier ("_Coro_resume_index");
-  tree rat_field = lookup_member (coro_frame_type, resume_idx_name, 1, 0,
+  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field, 1, 0,
 				  tf_warning_or_error);
   tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, actor_frame,
 		     rat_field, NULL_TREE);
@@ -2462,7 +2479,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
 
   /* We will need to know which resume point number should be encoded.  */
   tree res_idx_m
-    = lookup_member (coro_frame_type, resume_idx_name,
+    = lookup_member (coro_frame_type, coro_resume_index_field,
 		     /*protect=*/1, /*want_type=*/0, tf_warning_or_error);
   tree resume_pt_number
     = build_class_member_access_expr (actor_frame, res_idx_m, NULL_TREE, false,
@@ -2504,8 +2521,7 @@ build_destroy_fn (location_t loc, tree coro_frame_type, tree destroy,
 
   tree destr_frame = build1 (INDIRECT_REF, coro_frame_type, destr_fp);
 
-  tree resume_idx_name = get_identifier ("_Coro_resume_index");
-  tree rat_field = lookup_member (coro_frame_type, resume_idx_name, 1, 0,
+  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field, 1, 0,
 				  tf_warning_or_error);
   tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, destr_frame,
 		     rat_field, NULL_TREE);
-- 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH 6/8] coroutines: Convert implementation variables to  debug-friendly form.
  2021-09-01 10:54         ` [PATCH 5/8] coroutines: Define and populate accessors for debug state Iain Sandoe
@ 2021-09-01 10:55           ` Iain Sandoe
  2021-09-01 10:56             ` [PATCH 7/8] coroutines: Make proxy vars for the function arg copies Iain Sandoe
  2021-09-03 13:52             ` [PATCH 6/8] coroutines: Convert implementation variables to debug-friendly form Jason Merrill
  2021-09-03 13:39           ` [PATCH 5/8] coroutines: Define and populate accessors for debug state Jason Merrill
  1 sibling, 2 replies; 28+ messages in thread
From: Iain Sandoe @ 2021-09-01 10:55 UTC (permalink / raw)
  To: GCC Patches


The user might well wish to inspect some of the state that represents
the implementation of the coroutine machine.

In particular:
  The promise object.
  The function pointers for the resumer and destroyer.
  The current resume index (suspend point).
  The handle that represent this coroutine 'self handle'.
  Whether the coroutine frame is allocated and needs to be freed.

These variables are given names that can be 'well-known' and advertised
in debug documentation - they are placed in the implementation namespace
and all begin with _Coro_.

Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>

gcc/cp/ChangeLog:

	* coroutines.cc (transform_await_expr): Use debug-friendly
	names for coroutine implementation.
	(build_actor_fn): Likewise.
	(build_destroy_fn): Likewise.
	(coro_rewrite_function_body): Likewise.
	(morph_fn_to_coro): Likewise.
---
 gcc/cp/coroutines.cc | 214 +++++++++++++++++++------------------------
 1 file changed, 94 insertions(+), 120 deletions(-)

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index 3b46aac4dc5..aacf352f1c9 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -1906,7 +1906,6 @@ transform_await_expr (tree await_expr, await_xform_data *xform)
   /* So, on entry, we have:
      in : CO_AWAIT_EXPR (a, e_proxy, o, awr_call_vector, mode)
 	  We no longer need a [it had diagnostic value, maybe?]
-	  We need to replace the promise proxy in all elements
 	  We need to replace the e_proxy in the awr_call.  */
 
   tree coro_frame_type = TREE_TYPE (xform->actor_frame);
@@ -1932,16 +1931,6 @@ transform_await_expr (tree await_expr, await_xform_data *xform)
       TREE_OPERAND (await_expr, 1) = as;
     }
 
-  /* Now do the self_handle.  */
-  data.from = xform->self_h_proxy;
-  data.to = xform->real_self_h;
-  cp_walk_tree (&await_expr, replace_proxy, &data, NULL);
-
-  /* Now do the promise.  */
-  data.from = xform->promise_proxy;
-  data.to = xform->real_promise;
-  cp_walk_tree (&await_expr, replace_proxy, &data, NULL);
-
   return await_expr;
 }
 
@@ -2128,10 +2117,9 @@ coro_get_frame_dtor (tree coro_fp, tree orig, tree frame_size,
 
 static void
 build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
-		tree orig, hash_map<tree, param_info> *param_uses,
-		hash_map<tree, local_var_info> *local_var_uses,
-		vec<tree, va_gc> *param_dtor_list, tree resume_fn_field,
-		tree resume_idx_field, unsigned body_count, tree frame_size)
+		tree orig, hash_map<tree, local_var_info> *local_var_uses,
+		vec<tree, va_gc> *param_dtor_list,
+		tree resume_idx_var, unsigned body_count, tree frame_size)
 {
   verify_stmt_tree (fnbody);
   /* Some things we inherit from the original function.  */
@@ -2216,8 +2204,8 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
     = {actor, actor_frame, coro_frame_type, loc, local_var_uses};
   cp_walk_tree (&fnbody, transform_local_var_uses, &xform_vars_data, NULL);
 
-  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field, 1, 0,
-				  tf_warning_or_error);
+  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field,
+				  1, 0, tf_warning_or_error);
   tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, actor_frame,
 		     rat_field, NULL_TREE);
 
@@ -2319,14 +2307,8 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
   tree r = build_stmt (loc, LABEL_EXPR, actor_begin_label);
   add_stmt (r);
 
-  /* actor's version of the promise.  */
-  tree ap_m = lookup_member (coro_frame_type, get_identifier ("_Coro_promise"), 1, 0,
-			     tf_warning_or_error);
-  tree ap = build_class_member_access_expr (actor_frame, ap_m, NULL_TREE, false,
-					    tf_warning_or_error);
-
   /* actor's coroutine 'self handle'.  */
-  tree ash_m = lookup_member (coro_frame_type, get_identifier ("_Coro_self_handle"), 1,
+  tree ash_m = lookup_member (coro_frame_type, coro_self_handle_field, 1,
 			      0, tf_warning_or_error);
   tree ash = build_class_member_access_expr (actor_frame, ash_m, NULL_TREE,
 					     false, tf_warning_or_error);
@@ -2347,36 +2329,13 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
      decide where to put things.  */
 
   await_xform_data xform
-    = {actor, actor_frame, promise_proxy, ap, self_h_proxy, ash};
+    = {actor, actor_frame, NULL_TREE, NULL_TREE, self_h_proxy, ash};
 
   /* Transform the await expressions in the function body.  Only do each
      await tree once!  */
   hash_set<tree> pset;
   cp_walk_tree (&fnbody, transform_await_wrapper, &xform, &pset);
 
-  /* 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);
-
-  /* The rewrite of the function adds code to set the resume_fn field to
-     nullptr when the coroutine is done and also the index to zero when
-     calling an unhandled exception.  These are represented by two proxies
-     in the function, so rewrite them to the proper frame access.  */
-  tree resume_m
-    = lookup_member (coro_frame_type, get_identifier ("_Coro_resume_fn"),
-		     /*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);
-  p_data.from = resume_fn_field;
-  p_data.to = res_x;
-  cp_walk_tree (&fnbody, replace_proxy, &p_data, NULL);
-
-  p_data.from = resume_idx_field;
-  p_data.to = rat;
-  cp_walk_tree (&fnbody, replace_proxy, &p_data, NULL);
-
   /* Add in our function body with the co_returns rewritten to final form.  */
   add_stmt (fnbody);
 
@@ -2385,7 +2344,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
   add_stmt (r);
 
   /* Destructors for the things we built explicitly.  */
-  r = build_special_member_call (ap, complete_dtor_identifier, NULL,
+  r = build_special_member_call (promise_proxy, complete_dtor_identifier, NULL,
 				 promise_type, LOOKUP_NORMAL,
 				 tf_warning_or_error);
   add_stmt (r);
@@ -2398,7 +2357,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
   /* Here deallocate the frame (if we allocated it), which we will have at
      present.  */
   tree fnf_m
-    = lookup_member (coro_frame_type, get_identifier ("_Coro_frame_needs_free"), 1,
+    = lookup_member (coro_frame_type, coro_frame_needs_free_field, 1,
 		     0, tf_warning_or_error);
   tree fnf2_x = build_class_member_access_expr (actor_frame, fnf_m, NULL_TREE,
 						false, tf_warning_or_error);
@@ -2477,18 +2436,10 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
   gcc_checking_assert (maybe_cleanup_point_expr_void (r) == r);
   add_stmt (r);
 
-  /* We will need to know which resume point number should be encoded.  */
-  tree res_idx_m
-    = lookup_member (coro_frame_type, coro_resume_index_field,
-		     /*protect=*/1, /*want_type=*/0, tf_warning_or_error);
-  tree resume_pt_number
-    = build_class_member_access_expr (actor_frame, res_idx_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, NULL_TREE,
+  coro_aw_data data = {actor, actor_fp, resume_idx_var, NULL_TREE,
 		       ash, del_promise_label, ret_label,
 		       continue_label, continuation, 2};
   cp_walk_tree (&actor_body, await_statement_expander, &data, NULL);
@@ -2502,7 +2453,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
 }
 
 /* The prototype 'destroy' function :
-   frame->__resume_at |= 1;
+   frame->__Coro_resume_index |= 1;
    actor (frame);  */
 
 static void
@@ -2521,10 +2472,10 @@ build_destroy_fn (location_t loc, tree coro_frame_type, tree destroy,
 
   tree destr_frame = build1 (INDIRECT_REF, coro_frame_type, destr_fp);
 
-  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field, 1, 0,
-				  tf_warning_or_error);
-  tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, destr_frame,
-		     rat_field, NULL_TREE);
+  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field,
+				  1, 0, tf_warning_or_error);
+  tree rat = build3 (COMPONENT_REF, short_unsigned_type_node,
+			 destr_frame, rat_field, NULL_TREE);
 
   /* _resume_at |= 1 */
   tree dstr_idx = build2 (BIT_IOR_EXPR, short_unsigned_type_node, rat,
@@ -4040,8 +3991,8 @@ coro_build_actor_or_destroy_function (tree orig, tree fn_type,
 
 static tree
 coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
-			    tree resume_fn_ptr_type, tree& resume_fn_field,
-			    tree& resume_idx_field, tree& fs_label)
+			    tree resume_fn_ptr_type,
+			    tree& resume_idx_var, tree& fs_label)
 {
   /* This will be our new outer scope.  */
   tree update_body = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL);
@@ -4074,7 +4025,6 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
 
   /* 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);
 
@@ -4085,24 +4035,61 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
   tree return_void
     = get_coroutine_return_void_expr (current_function_decl, fn_start, false);
 
+  /* The pointer to the resume function.  */
+  tree resume_fn_ptr
+    = coro_build_artificial_var (fn_start, coro_resume_fn_field,
+				 resume_fn_ptr_type, orig, NULL_TREE);
+  DECL_CHAIN (resume_fn_ptr) = var_list;
+  var_list = resume_fn_ptr;
+  add_decl_expr (resume_fn_ptr);
+
   /* We will need to be able to set the resume function pointer to nullptr
      to signal that the coroutine is 'done'.  */
-  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);
-  /* Likewise, the resume index needs to be reset.  */
-  resume_idx_field
-    = build_lang_decl (VAR_DECL, get_identifier ("resume.index.proxy"),
-		       short_unsigned_type_node);
-  DECL_ARTIFICIAL (resume_idx_field) = true;
-  tree zero_resume_idx = build_int_cst (short_unsigned_type_node, 0);
-  zero_resume_idx = build2 (INIT_EXPR, short_unsigned_type_node,
-			    resume_idx_field, zero_resume_idx);
+
+  /* The pointer to the destroy function.  */
+  tree var = coro_build_artificial_var (fn_start, coro_destroy_fn_field,
+					resume_fn_ptr_type, orig, NULL_TREE);
+  DECL_CHAIN (var) = var_list;
+  var_list = var;
+  add_decl_expr (var);
+
+  /* The promise was created on demand when parsing we now link it into
+      our scope.  */
+  tree promise = get_coroutine_promise_proxy (orig);
+  DECL_CONTEXT (promise) = orig;
+  DECL_SOURCE_LOCATION (promise) = fn_start;
+  DECL_CHAIN (promise) = var_list;
+  var_list = promise;
+  add_decl_expr (promise);
+
+  /* We need a handle to this coroutine, which is passed to every
+     await_suspend().  This was created on demand when parsing we now link it
+     into our scope.  */
+  var = get_coroutine_self_handle_proxy (orig);
+  DECL_CONTEXT (var) = orig;
+  DECL_SOURCE_LOCATION (var) = fn_start;
+  DECL_CHAIN (var) = var_list;
+  var_list = var;
+  add_decl_expr (var);
+
+
+  /* We create a resume index, this is initialized in the ramp.  */
+  resume_idx_var
+    = coro_build_artificial_var (fn_start, coro_resume_index_field,
+				 short_unsigned_type_node, orig, NULL_TREE);
+  DECL_CHAIN (resume_idx_var) = var_list;
+  var_list = resume_idx_var;
+  add_decl_expr (resume_idx_var);
+
+  /* If the coroutine has a frame that needs to be freed, this will be set by
+     the ramp.  */
+  var = coro_build_artificial_var (fn_start, coro_frame_needs_free_field,
+				   boolean_type_node, orig, NULL_TREE);
+  DECL_CHAIN (var) = var_list;
+  var_list = var;
+  add_decl_expr (var);
 
   if (flag_exceptions)
     {
@@ -4166,10 +4153,14 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
 	 If the unhandled exception method returns, then we continue
 	 to the final await expression (which duplicates the clearing of
 	 the field). */
-      finish_expr_stmt (zero_resume);
-      finish_expr_stmt (zero_resume_idx);
-      ueh = maybe_cleanup_point_expr_void (ueh);
-      add_stmt (ueh);
+      tree r = build2 (MODIFY_EXPR, resume_fn_ptr_type, resume_fn_ptr,
+		       zero_resume);
+      finish_expr_stmt (r);
+      tree short_zero = build_int_cst (short_unsigned_type_node, 0);
+      r = build2 (MODIFY_EXPR, short_unsigned_type_node, resume_idx_var,
+		  short_zero);
+      finish_expr_stmt (r);
+      finish_expr_stmt (ueh);
       finish_handler (handler);
       TRY_HANDLERS (tcb) = pop_stmt_list (TRY_HANDLERS (tcb));
     }
@@ -4204,6 +4195,8 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
   /* 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.  */
+  zero_resume = build2 (MODIFY_EXPR, resume_fn_ptr_type, resume_fn_ptr,
+			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));
@@ -4348,33 +4341,16 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
   /* Construct the wrapped function body; we will analyze this to determine
      the requirements for the coroutine frame.  */
 
-  tree resume_fn_field = NULL_TREE;
-  tree resume_idx_field = NULL_TREE;
+  tree resume_idx_var = NULL_TREE;
   tree fs_label = NULL_TREE;
   fnbody = coro_rewrite_function_body (fn_start, fnbody, orig,
-				       act_des_fn_ptr, resume_fn_field,
-				       resume_idx_field, fs_label);
+				       act_des_fn_ptr,
+				       resume_idx_var, fs_label);
   /* Build our dummy coro frame layout.  */
   coro_frame_type = begin_class_definition (coro_frame_type);
 
+  /* The fields for the coro frame.  */
   tree field_list = NULL_TREE;
-  tree resume_name
-    = coro_make_frame_entry (&field_list, "_Coro_resume_fn",
-			     act_des_fn_ptr, fn_start);
-  tree destroy_name
-    = coro_make_frame_entry (&field_list, "_Coro_destroy_fn",
-			     act_des_fn_ptr, fn_start);
-  tree promise_name
-    = coro_make_frame_entry (&field_list, "_Coro_promise", promise_type, fn_start);
-  tree fnf_name = coro_make_frame_entry (&field_list, "_Coro_frame_needs_free",
-					 boolean_type_node, fn_start);
-  tree resume_idx_name
-    = coro_make_frame_entry (&field_list, "_Coro_resume_index",
-			     short_unsigned_type_node, fn_start);
-
-  /* We need a handle to this coroutine, which is passed to every
-     await_suspend().  There's no point in creating it over and over.  */
-  (void) coro_make_frame_entry (&field_list, "_Coro_self_handle", handle_type, fn_start);
 
   /* Now add in fields for function params (if there are any).
      We do not attempt elision of copies at this stage, we do analyze the
@@ -4776,8 +4752,8 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
 
   /* 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 (coro_frame_type, fnf_name, 1, 0, tf_warning_or_error);
+  tree fnf_m = lookup_member (coro_frame_type, coro_frame_needs_free_field,
+			      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 = build2 (INIT_EXPR, boolean_type_node, fnf_x, boolean_true_node);
@@ -4788,24 +4764,22 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
 
   tree actor_addr = build1 (ADDR_EXPR, act_des_fn_ptr, actor);
   tree resume_m
-    = lookup_member (coro_frame_type, resume_name,
+    = lookup_member (coro_frame_type, coro_resume_fn_field,
 		     /*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 = build2_loc (fn_start, INIT_EXPR, act_des_fn_ptr, resume_x, actor_addr);
-  r = coro_build_cvt_void_expr_stmt (r, fn_start);
-  add_stmt (r);
+  finish_expr_stmt (r);
 
   tree destroy_addr = build1 (ADDR_EXPR, act_des_fn_ptr, destroy);
   tree destroy_m
-    = lookup_member (coro_frame_type, destroy_name,
+    = lookup_member (coro_frame_type, coro_destroy_fn_field,
 		     /*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 = build2_loc (fn_start, INIT_EXPR, act_des_fn_ptr, destroy_x, destroy_addr);
-  r = coro_build_cvt_void_expr_stmt (r, fn_start);
-  add_stmt (r);
+  finish_expr_stmt (r);
 
   /* [dcl.fct.def.coroutine] /13
      When a coroutine is invoked, a copy is created for each coroutine
@@ -4896,7 +4870,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
 
   /* Set up the promise.  */
   tree promise_m
-    = lookup_member (coro_frame_type, promise_name,
+    = lookup_member (coro_frame_type, coro_promise_field,
 		     /*protect=*/1, /*want_type=*/0, tf_warning_or_error);
 
   tree p = build_class_member_access_expr (deref_fp, promise_m, NULL_TREE,
@@ -5042,9 +5016,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
 			      boolean_type_node);
       finish_expr_stmt (r);
     }
-  /* Initialize the resume_idx_name to 0, meaning "not started".  */
+  /* Initialize the resume_idx_var to 0, meaning "not started".  */
   tree resume_idx_m
-    = lookup_member (coro_frame_type, resume_idx_name,
+    = lookup_member (coro_frame_type, coro_resume_index_field,
 		     /*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,
@@ -5187,9 +5161,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
   push_deferring_access_checks (dk_no_check);
 
   /* Build the actor...  */
-  build_actor_fn (fn_start, coro_frame_type, actor, fnbody, orig, param_uses,
-		  &local_var_uses, param_dtor_list, resume_fn_field,
-		  resume_idx_field, body_aw_points.await_number, frame_size);
+  build_actor_fn (fn_start, coro_frame_type, actor, fnbody, orig,
+		  &local_var_uses, param_dtor_list,
+		  resume_idx_var, body_aw_points.await_number, frame_size);
 
   /* Destroyer ... */
   build_destroy_fn (fn_start, coro_frame_type, destroy, actor);
-- 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH 7/8] coroutines: Make proxy vars for the function arg copies.
  2021-09-01 10:55           ` [PATCH 6/8] coroutines: Convert implementation variables to debug-friendly form Iain Sandoe
@ 2021-09-01 10:56             ` Iain Sandoe
  2021-09-01 10:56               ` [PATCH 8/8] coroutines: Make the continue handle visible to debug Iain Sandoe
  2021-09-03 14:07               ` [PATCH 7/8] coroutines: Make proxy vars for the function arg copies Jason Merrill
  2021-09-03 13:52             ` [PATCH 6/8] coroutines: Convert implementation variables to debug-friendly form Jason Merrill
  1 sibling, 2 replies; 28+ messages in thread
From: Iain Sandoe @ 2021-09-01 10:56 UTC (permalink / raw)
  To: GCC Patches


This adds top level proxy variables for the coroutine frame
copies of the original function args.  These are then available
in the debugger to refer to the frame copies.  We rewrite the
function body to use the copies, since the original parms will
no longer be in scope when the coroutine is running.

Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>

gcc/cp/ChangeLog:

	* coroutines.cc (struct param_info): Add copy_var.
	(build_actor_fn): Use simplified param references.
	(register_param_uses): Likewise.
	(rewrite_param_uses): Likewise.
	(analyze_fn_parms): New function.
	(coro_rewrite_function_body): Add proxies for the fn
	parameters to the outer bind scope of the rewritten code.
	(morph_fn_to_coro): Use simplified version of param ref.
---
 gcc/cp/coroutines.cc | 247 ++++++++++++++++++++-----------------------
 1 file changed, 117 insertions(+), 130 deletions(-)

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index aacf352f1c9..395e5c488e5 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -1964,6 +1964,7 @@ transform_await_wrapper (tree *stmt, int *do_subtree, void *d)
 struct param_info
 {
   tree field_id;     /* The name of the copy in the coroutine frame.  */
+  tree copy_var;     /* The local var proxy for the frame copy.  */
   vec<tree *> *body_uses; /* Worklist of uses, void if there are none.  */
   tree frame_type;   /* The type used to represent this parm in the frame.  */
   tree orig_type;    /* The original type of the parm (not as passed).  */
@@ -2169,36 +2170,6 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
   /* Declare the continuation handle.  */
   add_decl_expr (continuation);
 
-  /* Re-write param references in the body, no code should be generated
-     here.  */
-  if (DECL_ARGUMENTS (orig))
-    {
-      tree arg;
-      for (arg = DECL_ARGUMENTS (orig); arg != NULL; arg = DECL_CHAIN (arg))
-	{
-	  bool existed;
-	  param_info &parm = param_uses->get_or_insert (arg, &existed);
-	  if (!parm.body_uses)
-	    continue; /* Wasn't used in the original function body.  */
-
-	  tree fld_ref = lookup_member (coro_frame_type, parm.field_id,
-					/*protect=*/1, /*want_type=*/0,
-					tf_warning_or_error);
-	  tree fld_idx = build3_loc (loc, COMPONENT_REF, parm.frame_type,
-				     actor_frame, fld_ref, NULL_TREE);
-
-	  /* We keep these in the frame as a regular pointer, so convert that
-	   back to the type expected.  */
-	  if (parm.pt_ref)
-	    fld_idx = build1_loc (loc, CONVERT_EXPR, TREE_TYPE (arg), fld_idx);
-
-	  int i;
-	  tree *puse;
-	  FOR_EACH_VEC_ELT (*parm.body_uses, i, puse)
-	    *puse = fld_idx;
-	}
-    }
-
   /* Re-write local vars, similarly.  */
   local_vars_transform xform_vars_data
     = {actor, actor_frame, coro_frame_type, loc, local_var_uses};
@@ -3772,11 +3743,11 @@ struct param_frame_data
   bool param_seen;
 };
 
-/* A tree-walk callback that records the use of parameters (to allow for
-   optimizations where handling unused parameters may be omitted).  */
+/* A tree walk callback that rewrites each parm use to the local variable
+   that represents its copy in the frame.  */
 
 static tree
-register_param_uses (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
+rewrite_param_uses (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
 {
   param_frame_data *data = (param_frame_data *) d;
 
@@ -3784,7 +3755,7 @@ register_param_uses (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
   if (TREE_CODE (*stmt) == VAR_DECL && DECL_HAS_VALUE_EXPR_P (*stmt))
     {
       tree t = DECL_VALUE_EXPR (*stmt);
-      return cp_walk_tree (&t, register_param_uses, d, NULL);
+      return cp_walk_tree (&t, rewrite_param_uses, d, NULL);
     }
 
   if (TREE_CODE (*stmt) != PARM_DECL)
@@ -3798,16 +3769,87 @@ register_param_uses (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
   param_info &parm = data->param_uses->get_or_insert (*stmt, &existed);
   gcc_checking_assert (existed);
 
-  if (!parm.body_uses)
+  *stmt = parm.copy_var;
+  return NULL_TREE;
+}
+
+/* Build up a set of info that determines how each param copy will be
+   handled.  */
+
+static hash_map<tree, param_info> *analyze_fn_parms (tree orig)
+{
+  if (!DECL_ARGUMENTS (orig))
+    return NULL;
+
+  hash_map<tree, param_info> *param_uses = new hash_map<tree, param_info>;
+
+  /* Build a hash map with an entry for each param.
+     The key is the param tree.
+     Then we have an entry for the frame field name.
+     Then a cache for the field ref when we come to use it.
+     Then a tree list of the uses.
+     The second two entries start out empty - and only get populated
+     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))
     {
-      vec_alloc (parm.body_uses, 4);
-      parm.body_uses->quick_push (stmt);
-      data->param_seen = true;
+      bool existed;
+      param_info &parm = param_uses->get_or_insert (arg, &existed);
+      gcc_checking_assert (!existed);
+      parm.body_uses = NULL;
+      tree actual_type = TREE_TYPE (arg);
+      actual_type = complete_type_or_else (actual_type, orig);
+      if (actual_type == NULL_TREE)
+	actual_type = error_mark_node;
+      parm.orig_type = actual_type;
+      parm.by_ref = parm.pt_ref = parm.rv_ref =  false;
+      if (TREE_CODE (actual_type) == REFERENCE_TYPE)
+	{
+	  /* If the user passes by reference, then we will save the
+	     pointer to the original.  As noted in
+	     [dcl.fct.def.coroutine] / 13, if the lifetime of the
+	     referenced item ends and then the coroutine is resumed,
+	     we have UB; well, the user asked for it.  */
+	  if (TYPE_REF_IS_RVALUE (actual_type))
+		parm.rv_ref = true;
+	  else
+		parm.pt_ref = true;
+	}
+      else if (TYPE_REF_P (DECL_ARG_TYPE (arg)))
+	parm.by_ref = true;
+
+      parm.frame_type = actual_type;
+
+      parm.this_ptr = is_this_parameter (arg);
+      parm.lambda_cobj = lambda_p && DECL_NAME (arg) == closure_identifier;
+
+      tree name = DECL_NAME (arg);
+      if (!name)
+	{
+	  char *buf = xasprintf ("_Coro_unnamed_parm_%d", no_name_parm++);
+	  name = get_identifier (buf);
+	  free (buf);
+	}
+      parm.field_id = name;
+
+      if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (parm.frame_type))
+	{
+	  char *buf = xasprintf ("_Coro_%s_live", IDENTIFIER_POINTER (name));
+	  parm.guard_var = build_lang_decl (VAR_DECL, get_identifier (buf),
+					    boolean_type_node);
+	  free (buf);
+	  DECL_ARTIFICIAL (parm.guard_var) = true;
+	  DECL_CONTEXT (parm.guard_var) = orig;
+	  DECL_INITIAL (parm.guard_var) = boolean_false_node;
+	  parm.trivial_dtor = false;
+	}
+      else
+	parm.trivial_dtor = true;
     }
-  else
-    parm.body_uses->safe_push (stmt);
 
-  return NULL_TREE;
+  return param_uses;
 }
 
 /* Small helper for the repetitive task of adding a new field to the coro
@@ -3991,6 +4033,7 @@ coro_build_actor_or_destroy_function (tree orig, tree fn_type,
 
 static tree
 coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
+			    hash_map<tree, param_info> *param_uses,
 			    tree resume_fn_ptr_type,
 			    tree& resume_idx_var, tree& fs_label)
 {
@@ -4074,6 +4117,36 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
   var_list = var;
   add_decl_expr (var);
 
+  /* If we have function parms, then these will be copied to the coroutine
+     frame.  Create a local variable to point to each of them so that we can
+     see them in the debugger.  */
+
+  if (param_uses)
+    {
+      gcc_checking_assert (DECL_ARGUMENTS (orig));
+      /* Add a local var for each parm.  */
+      for (tree arg = DECL_ARGUMENTS (orig); arg != NULL;
+	   arg = DECL_CHAIN (arg))
+	{
+	  param_info *parm_i = param_uses->get (arg);
+	  gcc_checking_assert (parm_i);
+	  parm_i->copy_var
+	    = build_lang_decl (VAR_DECL, parm_i->field_id, TREE_TYPE (arg));
+	  DECL_SOURCE_LOCATION (parm_i->copy_var) = DECL_SOURCE_LOCATION (arg);
+	  DECL_CONTEXT (parm_i->copy_var) = orig;
+	  DECL_ARTIFICIAL (parm_i->copy_var) = true;
+	  DECL_CHAIN (parm_i->copy_var) = var_list;
+	  var_list = parm_i->copy_var;
+	  add_decl_expr (parm_i->copy_var);
+      	}
+
+      /* Now replace all uses of the parms in the function body with the local
+	 vars.  */
+      hash_set<tree *> visited;
+      param_frame_data param_data = {NULL, param_uses,
+				     &visited, fn_start, false};
+      cp_walk_tree (&fnbody, rewrite_param_uses, &param_data, NULL);
+    }
 
   /* We create a resume index, this is initialized in the ramp.  */
   resume_idx_var
@@ -4343,7 +4416,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
 
   tree resume_idx_var = NULL_TREE;
   tree fs_label = NULL_TREE;
-  fnbody = coro_rewrite_function_body (fn_start, fnbody, orig,
+  hash_map<tree, param_info> *param_uses = analyze_fn_parms (orig);
+
+  fnbody = coro_rewrite_function_body (fn_start, fnbody, orig, param_uses,
 				       act_des_fn_ptr,
 				       resume_idx_var, fs_label);
   /* Build our dummy coro frame layout.  */
@@ -4352,94 +4427,6 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
   /* The fields for the coro frame.  */
   tree field_list = NULL_TREE;
 
-  /* Now add in fields for function params (if there are any).
-     We do not attempt elision of copies at this stage, we do analyze the
-     uses and build worklists to replace those when the state machine is
-     lowered.  */
-
-  hash_map<tree, param_info> *param_uses = NULL;
-  if (DECL_ARGUMENTS (orig))
-    {
-      /* Build a hash map with an entry for each param.
-	  The key is the param tree.
-	  Then we have an entry for the frame field name.
-	  Then a cache for the field ref when we come to use it.
-	  Then a tree list of the uses.
-	  The second two entries start out empty - and only get populated
-	  when we see uses.  */
-      param_uses = new hash_map<tree, param_info>;
-      bool lambda_p = LAMBDA_FUNCTION_P (orig);
-
-      unsigned no_name_parm = 0;
-      for (tree arg = DECL_ARGUMENTS (orig); arg != NULL;
-	   arg = DECL_CHAIN (arg))
-	{
-	  bool existed;
-	  param_info &parm = param_uses->get_or_insert (arg, &existed);
-	  gcc_checking_assert (!existed);
-	  parm.body_uses = NULL;
-	  tree actual_type = TREE_TYPE (arg);
-	  actual_type = complete_type_or_else (actual_type, orig);
-	  if (actual_type == NULL_TREE)
-	    actual_type = error_mark_node;
-	  parm.orig_type = actual_type;
-	  parm.by_ref = parm.pt_ref = parm.rv_ref =  false;
-	  if (TREE_CODE (actual_type) == REFERENCE_TYPE)
-	    {
-	      /* If the user passes by reference, then we will save the
-		 pointer to the original.  As noted in
-		 [dcl.fct.def.coroutine] / 13, if the lifetime of the
-		 referenced item ends and then the coroutine is resumed,
-		 we have UB; well, the user asked for it.  */
-	      if (TYPE_REF_IS_RVALUE (actual_type))
-		parm.rv_ref = true;
-	      else
-		parm.pt_ref = true;
-	    }
-	  else if (TYPE_REF_P (DECL_ARG_TYPE (arg)))
-	    parm.by_ref = true;
-
-	  parm.frame_type = actual_type;
-
-	  parm.this_ptr = is_this_parameter (arg);
-	  parm.lambda_cobj = lambda_p && DECL_NAME (arg) == closure_identifier;
-
-	  char *buf;
-	  if (DECL_NAME (arg))
-	    {
-	      tree pname = DECL_NAME (arg);
-	      buf = xasprintf ("_P_%s", IDENTIFIER_POINTER (pname));
-	    }
-	  else
-	    buf = xasprintf ("_P_unnamed_%d", no_name_parm++);
-
-	  if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (parm.frame_type))
-	    {
-	      char *gbuf = xasprintf ("%s_live", buf);
-	      parm.guard_var
-		= build_lang_decl (VAR_DECL, get_identifier (gbuf),
-				   boolean_type_node);
-	      free (gbuf);
-	      DECL_ARTIFICIAL (parm.guard_var) = true;
-	      DECL_INITIAL (parm.guard_var) = boolean_false_node;
-	      parm.trivial_dtor = false;
-	    }
-	  else
-	    parm.trivial_dtor = true;
-	  parm.field_id = coro_make_frame_entry
-	    (&field_list, buf, actual_type, DECL_SOURCE_LOCATION (arg));
-	  free (buf);
-	}
-
-      /* We want to record every instance of param's use, so don't include
-	 a 'visited' hash_set on the tree walk, but only record a containing
-	 expression once.  */
-      hash_set<tree *> visited;
-      param_frame_data param_data
-	= {&field_list, param_uses, &visited, fn_start, false};
-      cp_walk_tree (&fnbody, register_param_uses, &param_data, NULL);
-    }
-
   /* 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.  */
-- 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH 8/8] coroutines: Make the continue handle visible to debug.
  2021-09-01 10:56             ` [PATCH 7/8] coroutines: Make proxy vars for the function arg copies Iain Sandoe
@ 2021-09-01 10:56               ` Iain Sandoe
  2021-09-03 14:25                 ` Jason Merrill
  2021-09-03 14:07               ` [PATCH 7/8] coroutines: Make proxy vars for the function arg copies Jason Merrill
  1 sibling, 1 reply; 28+ messages in thread
From: Iain Sandoe @ 2021-09-01 10:56 UTC (permalink / raw)
  To: GCC Patches


When we have a suspend method that returns a coroutine handle
we transfer (hopefully symmetrically, i.e. with a tailcall) to
that new coroutine instead of returning to our resumer.

This adds the variable to the outer block for the actor function
which means that '_Coro_actor_continue' is visible to debug.

Contributory to PR 99215.

Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>

gcc/cp/ChangeLog:

	* coroutines.cc (build_actor_fn): Make _Coro_actor_continue
	visible to debug.
---
 gcc/cp/coroutines.cc | 1 +
 1 file changed, 1 insertion(+)

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index 395e5c488e5..b32c5dc5e55 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -2148,6 +2148,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
 						 NULL_TREE);
   
   BIND_EXPR_VARS (actor_bind) = continuation;
+  BLOCK_VARS (top_block) = BIND_EXPR_VARS (actor_bind) ;
 
   /* Link in the block associated with the outer scope of the re-written
      function body.  */
-- 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/8] coroutines : Use DECL_VALUE_EXPR instead of rewriting vars.
  2021-09-01 10:52 ` [PATCH 1/8] coroutines : Use DECL_VALUE_EXPR instead of rewriting vars Iain Sandoe
  2021-09-01 10:53   ` [PATCH 2/8] coroutines: Add a helper for creating local vars Iain Sandoe
@ 2021-09-02 21:51   ` Jason Merrill
  1 sibling, 0 replies; 28+ messages in thread
From: Jason Merrill @ 2021-09-02 21:51 UTC (permalink / raw)
  To: Iain Sandoe, GCC Patches

On 9/1/21 6:52 AM, Iain Sandoe wrote:
> Hi,
> 
> Variables that need to persist over suspension expressions
> must be preserved by being copied into the coroutine frame.
> 
> The initial implementations do this manually in the transform
> code.  However, that has various disadvantages - including
> that the debug connections are lost between the original var
> and the frame copy.
> 
> The revised implementation makes use of DECL_VALUE_EXPRs to
> contain the frame offset expressions, so that the original
> var names are preserved in the code.
> 
> This process is also applied to the function parms which are
> always copied to the frame.  In this case the decls need to be
> copied since they are used in two different contexts during
> the re-write (in the building of the ramp function, and in
> the actor function itself).
> 
> This will assist in improvement of debugging (PR 99215).

OK.

> Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
> 
> gcc/cp/ChangeLog:
> 
> 	* coroutines.cc (transform_local_var_uses): Record
> 	frame offset expressions as DECL_VALUE_EXPRs instead of
> 	rewriting them.
> ---
>   gcc/cp/coroutines.cc | 105 +++----------------------------------------
>   1 file changed, 5 insertions(+), 100 deletions(-)
> 
> diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
> index ceb3d3be75e..2d68098f242 100644
> --- a/gcc/cp/coroutines.cc
> +++ b/gcc/cp/coroutines.cc
> @@ -1974,8 +1974,7 @@ transform_local_var_uses (tree *stmt, int *do_subtree, void *d)
>     local_vars_transform *lvd = (local_vars_transform *) d;
>   
>     /* For each var in this bind expr (that has a frame id, which means it was
> -     accessed), build a frame reference for each and then walk the bind expr
> -     statements, substituting the frame ref for the original var.  */
> +     accessed), build a frame reference and add it as the DECL_VALUE_EXPR.  */
>   
>     if (TREE_CODE (*stmt) == BIND_EXPR)
>       {
> @@ -1991,13 +1990,9 @@ transform_local_var_uses (tree *stmt, int *do_subtree, void *d)
>   	  /* Re-write the variable's context to be in the actor func.  */
>   	  DECL_CONTEXT (lvar) = lvd->context;
>   
> -	/* For capture proxies, this could include the decl value expr.  */
> -	if (local_var.is_lambda_capture || local_var.has_value_expr_p)
> -	  {
> -	    tree ve = DECL_VALUE_EXPR (lvar);
> -	    cp_walk_tree (&ve, transform_local_var_uses, d, NULL);
> +	  /* For capture proxies, this could include the decl value expr.  */
> +	  if (local_var.is_lambda_capture || local_var.has_value_expr_p)
>   	    continue; /* No frame entry for this.  */
> -	  }
>   
>   	  /* TODO: implement selective generation of fields when vars are
>   	     known not-used.  */
> @@ -2011,103 +2006,13 @@ transform_local_var_uses (tree *stmt, int *do_subtree, void *d)
>   	  tree fld_idx = build3_loc (lvd->loc, COMPONENT_REF, TREE_TYPE (lvar),
>   				     lvd->actor_frame, fld_ref, NULL_TREE);
>   	  local_var.field_idx = fld_idx;
> -	}
> -      /* FIXME: we should be able to do this in the loop above, but (at least
> -	 for range for) there are cases where the DECL_INITIAL contains
> -	 forward references.
> -	 So, now we've built the revised var in the frame, substitute uses of
> -	 it in initializers and the bind expr body.  */
> -      for (lvar = BIND_EXPR_VARS (*stmt); lvar != NULL;
> -	   lvar = DECL_CHAIN (lvar))
> -	{
> -	  /* we need to walk some of the decl trees, which might contain
> -	     references to vars replaced at a higher level.  */
> -	  cp_walk_tree (&DECL_INITIAL (lvar), transform_local_var_uses, d,
> -			NULL);
> -	  cp_walk_tree (&DECL_SIZE (lvar), transform_local_var_uses, d, NULL);
> -	  cp_walk_tree (&DECL_SIZE_UNIT (lvar), transform_local_var_uses, d,
> -			NULL);
> +	  SET_DECL_VALUE_EXPR (lvar, fld_idx);
> +	  DECL_HAS_VALUE_EXPR_P (lvar) = true;
>   	}
>         cp_walk_tree (&BIND_EXPR_BODY (*stmt), transform_local_var_uses, d, NULL);
> -
> -      /* Now we have processed and removed references to the original vars,
> -	 we can drop those from the bind - leaving capture proxies alone.  */
> -      for (tree *pvar = &BIND_EXPR_VARS (*stmt); *pvar != NULL;)
> -	{
> -	  bool existed;
> -	  local_var_info &local_var
> -	    = lvd->local_var_uses->get_or_insert (*pvar, &existed);
> -	  gcc_checking_assert (existed);
> -
> -	  /* Leave lambda closure captures alone, we replace the *this
> -	     pointer with the frame version and let the normal process
> -	     deal with the rest.
> -	     Likewise, variables with their value found elsewhere.
> -	     Skip past unused ones too.  */
> -	  if (local_var.is_lambda_capture
> -	     || local_var.has_value_expr_p
> -	     || local_var.field_id == NULL_TREE)
> -	    {
> -	      pvar = &DECL_CHAIN (*pvar);
> -	      continue;
> -	    }
> -
> -	  /* Discard this one, we replaced it.  */
> -	  *pvar = DECL_CHAIN (*pvar);
> -	}
> -
>         *do_subtree = 0; /* We've done the body already.  */
>         return NULL_TREE;
>       }
> -
> -  tree var_decl = *stmt;
> -  /* Look inside cleanups, we don't want to wrap a statement list in a
> -     cleanup.  */
> -  bool needs_cleanup = true;
> -  if (TREE_CODE (var_decl) == CLEANUP_POINT_EXPR)
> -    var_decl = TREE_OPERAND (var_decl, 0);
> -  else
> -    needs_cleanup = false;
> -
> -  /* Look inside the decl_expr for the actual var.  */
> -  bool decl_expr_p = TREE_CODE (var_decl) == DECL_EXPR;
> -  if (decl_expr_p && TREE_CODE (DECL_EXPR_DECL (var_decl)) == VAR_DECL)
> -    var_decl = DECL_EXPR_DECL (var_decl);
> -  else if (TREE_CODE (var_decl) != VAR_DECL)
> -    return NULL_TREE;
> -
> -  /* VAR_DECLs that are not recorded can belong to the proxies we've placed
> -     for the promise and coroutine handle(s), to global vars or to compiler
> -     temporaries.  Skip past these, we will handle them later.  */
> -  local_var_info *local_var_i = lvd->local_var_uses->get (var_decl);
> -
> -  if (local_var_i == NULL)
> -    return NULL_TREE;
> -
> -  if (local_var_i->is_lambda_capture
> -      || local_var_i->is_static
> -      || local_var_i->has_value_expr_p)
> -    return NULL_TREE;
> -
> -  /* This is our revised 'local' i.e. a frame slot.  */
> -  tree revised = local_var_i->field_idx;
> -  gcc_checking_assert (DECL_CONTEXT (var_decl) == lvd->context);
> -
> -  if (decl_expr_p && DECL_INITIAL (var_decl))
> -    {
> -      location_t loc = DECL_SOURCE_LOCATION (var_decl);
> -      tree r
> -	= cp_build_modify_expr (loc, revised, INIT_EXPR,
> -				DECL_INITIAL (var_decl), tf_warning_or_error);
> -      if (needs_cleanup)
> -	r = coro_build_cvt_void_expr_stmt (r, EXPR_LOCATION (*stmt));
> -      *stmt = r;
> -    }
> -  else
> -    *stmt = revised;
> -
> -  if (decl_expr_p)
> -    *do_subtree = 0; /* We've accounted for the nested use.  */
>     return NULL_TREE;
>   }
>   
> 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 2/8] coroutines: Add a helper for creating local vars.
  2021-09-01 10:53   ` [PATCH 2/8] coroutines: Add a helper for creating local vars Iain Sandoe
  2021-09-01 10:53     ` PATCH 3/8] coroutines: Support for debugging implementation state Iain Sandoe
@ 2021-09-02 21:52     ` Jason Merrill
  1 sibling, 0 replies; 28+ messages in thread
From: Jason Merrill @ 2021-09-02 21:52 UTC (permalink / raw)
  To: Iain Sandoe, GCC Patches

On 9/1/21 6:53 AM, Iain Sandoe wrote:
> 
> This is primarily code factoring, but we take this opportunity
> to rename some of the implementation variables (which we intend
> to expose to debugging) so that they are in the implementation
> namespace.
> 
> Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
> 
> gcc/cp/ChangeLog:
> 
> 	* coroutines.cc (coro_build_artificial_var): New.
> 	(build_actor_fn): Use var builder, rename vars to use
> 	implementation namespace.
> 	(coro_rewrite_function_body): Likewise.
> 	(morph_fn_to_coro): Likewise.
> ---
>   gcc/cp/coroutines.cc | 69 +++++++++++++++++++++++++++-----------------
>   1 file changed, 43 insertions(+), 26 deletions(-)
> 
> diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
> index 2d68098f242..b8501032969 100644
> --- a/gcc/cp/coroutines.cc
> +++ b/gcc/cp/coroutines.cc
> @@ -1474,6 +1474,29 @@ coro_build_cvt_void_expr_stmt (tree expr, location_t loc)
>     return coro_build_expr_stmt (t, loc);
>   }
>   
> +/* Helpers to build an artificial var, with location LOC, NAME and TYPE, in
> +   CTX, and with initializer INIT.  */
> +
> +static tree
> +coro_build_artificial_var (location_t loc, tree name, tree type, tree ctx,
> +			   tree init)
> +{
> +  tree res = build_lang_decl (VAR_DECL, name, type);
> +  DECL_SOURCE_LOCATION (res) = loc;
> +  DECL_CONTEXT (res) = ctx;
> +  DECL_ARTIFICIAL (res) = true;
> +  DECL_INITIAL (res) = init;
> +  return res;
> +}
> +
> +static tree
> +coro_build_artificial_var (location_t loc, const char *name, tree type,
> +			   tree ctx, tree init)
> +{
> +  return coro_build_artificial_var (loc, get_identifier (name),
> +				    type, ctx, init);
> +}
> +
>   /* Helpers for label creation:
>      1. Create a named label in the specified context.  */
>   
> @@ -2113,12 +2136,10 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>     tree top_block = make_node (BLOCK);
>     BIND_EXPR_BLOCK (actor_bind) = top_block;
>   
> -  tree continuation = build_lang_decl (VAR_DECL,
> -				       get_identifier ("actor.continue"),
> -				       void_coro_handle_type);
> -  DECL_ARTIFICIAL (continuation) = 1;
> -  DECL_IGNORED_P (continuation) = 1;
> -  DECL_CONTEXT (continuation) = actor;
> +  tree continuation = coro_build_artificial_var (loc, "_Coro_actor_continue",
> +						 void_coro_handle_type, actor,
> +						 NULL_TREE);
> +
>     BIND_EXPR_VARS (actor_bind) = continuation;
>   
>     /* Link in the block associated with the outer scope of the re-written
> @@ -4069,12 +4090,11 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>   					 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;
> +      tree i_a_r_c = coro_build_artificial_var (fn_start, "_Coro_i_a_r_c",

I might use a bit longer name, since the user doesn't have the comment 
above to explain the acronym.  :)

OK either way.

> +						boolean_type_node, orig,
> +						boolean_false_node);
>         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);
> @@ -4459,8 +4479,10 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>     add_stmt (ramp_bind);
>     tree ramp_body = push_stmt_list ();
>   
> -  tree coro_fp = build_lang_decl (VAR_DECL, get_identifier ("coro.frameptr"),
> -				  coro_frame_ptr);
> +  tree zeroinit = build1_loc (fn_start, CONVERT_EXPR,
> +			      coro_frame_ptr, integer_zero_node);
> +  tree coro_fp = coro_build_artificial_var (fn_start, "_Coro_frameptr",
> +					    coro_frame_ptr, orig, zeroinit);
>     tree varlist = coro_fp;
>   
>     /* To signal that we need to cleanup copied function args.  */
> @@ -4478,21 +4500,19 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>   
>     /* Signal that we need to clean up the promise object on exception.  */
>     tree coro_promise_live
> -   = build_lang_decl (VAR_DECL, get_identifier ("coro.promise.live"),
> -		      boolean_type_node);
> -  DECL_ARTIFICIAL (coro_promise_live) = true;
> +    = coro_build_artificial_var (fn_start, "_Coro_promise_live",
> +				 boolean_type_node, orig, boolean_false_node);
>     DECL_CHAIN (coro_promise_live) = varlist;
>     varlist = coro_promise_live;
> -  DECL_INITIAL (coro_promise_live) = boolean_false_node;
> +
>     /* When the get-return-object is in the RETURN slot, we need to arrange for
>        cleanup on exception.  */
>     tree coro_gro_live
> -   = build_lang_decl (VAR_DECL, get_identifier ("coro.gro.live"),
> -		      boolean_type_node);
> -  DECL_ARTIFICIAL (coro_gro_live) = true;
> +    = coro_build_artificial_var (fn_start, "_Coro_gro_live",
> +				 boolean_type_node, orig, boolean_false_node);
> +
>     DECL_CHAIN (coro_gro_live) = varlist;
>     varlist = coro_gro_live;
> -  DECL_INITIAL (coro_gro_live) = boolean_false_node;
>   
>     /* Collected the scope vars we need ... only one for now. */
>     BIND_EXPR_VARS (ramp_bind) = nreverse (varlist);
> @@ -4508,8 +4528,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>     /* The decl_expr for the coro frame pointer, initialize to zero so that we
>        can pass it to the IFN_CO_FRAME (since there's no way to pass a type,
>        directly apparently).  This avoids a "used uninitialized" warning.  */
> -  tree zeroinit = build1 (CONVERT_EXPR, coro_frame_ptr, integer_zero_node);
> -  DECL_INITIAL (coro_fp) = zeroinit;
> +
>     add_decl_expr (coro_fp);
>     if (flag_exceptions && DECL_ARGUMENTS (orig))
>       for (tree arg = DECL_ARGUMENTS (orig); arg != NULL;
> @@ -4969,10 +4988,8 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>       {
>         /* ... or ... Construct an object that will be used as the single
>   	param to the CTOR for the return object.  */
> -      gro = build_lang_decl (VAR_DECL, get_identifier ("coro.gro"), gro_type);
> -      DECL_CONTEXT (gro) = current_scope ();
> -      DECL_ARTIFICIAL (gro) = true;
> -      DECL_IGNORED_P (gro) = true;
> +      gro = coro_build_artificial_var (fn_start, "_Coro_gro", gro_type, orig,
> +				       NULL_TREE);
>         add_decl_expr (gro);
>         gro_bind_vars = gro;
>         r = cp_build_modify_expr (input_location, gro, INIT_EXPR, get_ro,
> 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: PATCH 3/8] coroutines: Support for debugging implementation state.
  2021-09-01 10:53     ` PATCH 3/8] coroutines: Support for debugging implementation state Iain Sandoe
  2021-09-01 10:54       ` [PATCH 4/8] coroutines: Make some of the artificial names more debugger-friendly Iain Sandoe
@ 2021-09-03 13:31       ` Jason Merrill
  1 sibling, 0 replies; 28+ messages in thread
From: Jason Merrill @ 2021-09-03 13:31 UTC (permalink / raw)
  To: Iain Sandoe, GCC Patches

On 9/1/21 6:53 AM, Iain Sandoe wrote:
> 
> Some of the state that is associated with the implementation
> is of interest to a user debugging a coroutine.  In particular
> items such as the suspend point, promise object, and current
> suspend point.
> 
> These variables live in the coroutine frame, but we can inject
> proxies for them into the outermost bind expression of the
> coroutine.  Such variables are automatically moved into the
> coroutine frame (if they need to persist across a suspend
> expression).  PLacing the proxies thus allows the user to
> inspect them by name in the debugger.
> 
> To implement this, we ensure that (at the outermost scope) the
> frame entries are not mangled (coroutine frame variables are
> usually mangled with scope nesting information so that they do
> not clash).  We can safely avoid doing this for the outermost
> scope so that we can map frame entries directly to the variables.
> 
> This is partial contribution to debug support (PR 99215).

OK.

> Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
> 
> gcc/cp/ChangeLog:
> 
> 	* coroutines.cc (register_local_var_uses): Do not mangle
> 	frame entries for the outermost scope.  Record the outer
> 	scope as nesting depth 0.
> ---
>   gcc/cp/coroutines.cc | 16 +++++++++++-----
>   1 file changed, 11 insertions(+), 5 deletions(-)
> 
> diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
> index b8501032969..a12714ea67e 100644
> --- a/gcc/cp/coroutines.cc
> +++ b/gcc/cp/coroutines.cc
> @@ -3885,8 +3885,6 @@ register_local_var_uses (tree *stmt, int *do_subtree, void *d)
>   
>     if (TREE_CODE (*stmt) == BIND_EXPR)
>       {
> -      lvd->bind_indx++;
> -      lvd->nest_depth++;
>         tree lvar;
>         for (lvar = BIND_EXPR_VARS (*stmt); lvar != NULL;
>   	   lvar = DECL_CHAIN (lvar))
> @@ -3925,11 +3923,17 @@ register_local_var_uses (tree *stmt, int *do_subtree, void *d)
>   	    continue;
>   
>   	  /* Make names depth+index unique, so that we can support nested
> -	     scopes with identically named locals.  */
> +	     scopes with identically named locals and still be able to
> +	     identify them in the coroutine frame.  */
>   	  tree lvname = DECL_NAME (lvar);
>   	  char *buf;
> -	  if (lvname != NULL_TREE)
> -	    buf = xasprintf ("__%s.%u.%u", IDENTIFIER_POINTER (lvname),
> +	  /* The outermost bind scope contains the artificial variables that
> +	     we inject to implement the coro state machine.  We want to be able
> +	     to inspect these in debugging.  */
> +	  if (lvname != NULL_TREE && lvd->nest_depth == 0)
> +	    buf = xasprintf ("%s", IDENTIFIER_POINTER (lvname));
> +	  else if (lvname != NULL_TREE)
> +	    buf = xasprintf ("%s_%u_%u", IDENTIFIER_POINTER (lvname),
>   			     lvd->nest_depth, lvd->bind_indx);
>   	  else
>   	    buf = xasprintf ("_D%u.%u.%u", DECL_UID (lvar), lvd->nest_depth,
> @@ -3942,6 +3946,8 @@ register_local_var_uses (tree *stmt, int *do_subtree, void *d)
>   	  /* We don't walk any of the local var sub-trees, they won't contain
>   	     any bind exprs.  */
>   	}
> +      lvd->bind_indx++;
> +      lvd->nest_depth++;
>         cp_walk_tree (&BIND_EXPR_BODY (*stmt), register_local_var_uses, d, NULL);
>         *do_subtree = 0; /* We've done this.  */
>         lvd->nest_depth--;
> 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 4/8] coroutines: Make some of the artificial names more debugger-friendly.
  2021-09-01 10:54       ` [PATCH 4/8] coroutines: Make some of the artificial names more debugger-friendly Iain Sandoe
  2021-09-01 10:54         ` [PATCH 5/8] coroutines: Define and populate accessors for debug state Iain Sandoe
@ 2021-09-03 13:35         ` Jason Merrill
  1 sibling, 0 replies; 28+ messages in thread
From: Jason Merrill @ 2021-09-03 13:35 UTC (permalink / raw)
  To: Iain Sandoe, GCC Patches

On 9/1/21 6:54 AM, Iain Sandoe wrote:
> 
> Some of the compiler-generated entries are of interest to a
> user debugging - keep variables in the implementation namespace
> but avoid using periods as separators (which is not compatible
> with visible symbols for some assemblers).
> 
> Partial improvement to debugging (PR 99215).

OK.

> Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
> 
> gcc/cp/ChangeLog:
> 
> 	* coroutines.cc (coro_promise_type_found_p): Rename variable
> 	to make it suitable for debugging.
> 	(build_actor_fn): Likewise.
> 	(build_destroy_fn): Likewise.
> 	(register_local_var_uses): Likewise.
> 	(coro_rewrite_function_body): Likewise.
> 	(morph_fn_to_coro): Likewise.
> ---
>   gcc/cp/coroutines.cc | 59 ++++++++++++++++++++++----------------------
>   1 file changed, 30 insertions(+), 29 deletions(-)
> 
> diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
> index a12714ea67e..081e1a46c63 100644
> --- a/gcc/cp/coroutines.cc
> +++ b/gcc/cp/coroutines.cc
> @@ -513,12 +513,12 @@ coro_promise_type_found_p (tree fndecl, location_t loc)
>         /* Build a proxy for a handle to "self" as the param to
>   	 await_suspend() calls.  */
>         coro_info->self_h_proxy
> -	= build_lang_decl (VAR_DECL, get_identifier ("self_h.proxy"),
> +	= build_lang_decl (VAR_DECL, get_identifier ("_Coro_self_handle"),
>   			   coro_info->handle_type);
>   
>         /* Build a proxy for the promise so that we can perform lookups.  */
>         coro_info->promise_proxy
> -	= build_lang_decl (VAR_DECL, get_identifier ("promise.proxy"),
> +	= build_lang_decl (VAR_DECL, get_identifier ("_Coro_promise"),
>   			   coro_info->promise_type);
>   
>         /* Note where we first saw a coroutine keyword.  */
> @@ -2198,7 +2198,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>       = {actor, actor_frame, coro_frame_type, loc, local_var_uses};
>     cp_walk_tree (&fnbody, transform_local_var_uses, &xform_vars_data, NULL);
>   
> -  tree resume_idx_name = get_identifier ("__resume_at");
> +  tree resume_idx_name = get_identifier ("_Coro_resume_index");
>     tree rat_field = lookup_member (coro_frame_type, resume_idx_name, 1, 0,
>   				  tf_warning_or_error);
>     tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, actor_frame,
> @@ -2303,13 +2303,13 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>     add_stmt (r);
>   
>     /* actor's version of the promise.  */
> -  tree ap_m = lookup_member (coro_frame_type, get_identifier ("__p"), 1, 0,
> +  tree ap_m = lookup_member (coro_frame_type, get_identifier ("_Coro_promise"), 1, 0,
>   			     tf_warning_or_error);
>     tree ap = build_class_member_access_expr (actor_frame, ap_m, NULL_TREE, false,
>   					    tf_warning_or_error);
>   
>     /* actor's coroutine 'self handle'.  */
> -  tree ash_m = lookup_member (coro_frame_type, get_identifier ("__self_h"), 1,
> +  tree ash_m = lookup_member (coro_frame_type, get_identifier ("_Coro_self_handle"), 1,
>   			      0, tf_warning_or_error);
>     tree ash = build_class_member_access_expr (actor_frame, ash_m, NULL_TREE,
>   					     false, tf_warning_or_error);
> @@ -2343,12 +2343,12 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>     p_data.to = ap;
>     cp_walk_tree (&fnbody, replace_proxy, &p_data, NULL);
>   
> -  /* The rewrite of the function adds code to set the __resume field to
> +  /* The rewrite of the function adds code to set the resume_fn field to
>        nullptr when the coroutine is done and also the index to zero when
>        calling an unhandled exception.  These are represented by two proxies
>        in the function, so rewrite them to the proper frame access.  */
>     tree resume_m
> -    = lookup_member (coro_frame_type, get_identifier ("__resume"),
> +    = lookup_member (coro_frame_type, get_identifier ("_Coro_resume_fn"),
>   		     /*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);
> @@ -2381,7 +2381,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>     /* Here deallocate the frame (if we allocated it), which we will have at
>        present.  */
>     tree fnf_m
> -    = lookup_member (coro_frame_type, get_identifier ("__frame_needs_free"), 1,
> +    = lookup_member (coro_frame_type, get_identifier ("_Coro_frame_needs_free"), 1,
>   		     0, tf_warning_or_error);
>     tree fnf2_x = build_class_member_access_expr (actor_frame, fnf_m, NULL_TREE,
>   						false, tf_warning_or_error);
> @@ -2504,7 +2504,7 @@ build_destroy_fn (location_t loc, tree coro_frame_type, tree destroy,
>   
>     tree destr_frame = build1 (INDIRECT_REF, coro_frame_type, destr_fp);
>   
> -  tree resume_idx_name = get_identifier ("__resume_at");
> +  tree resume_idx_name = get_identifier ("_Coro_resume_index");
>     tree rat_field = lookup_member (coro_frame_type, resume_idx_name, 1, 0,
>   				  tf_warning_or_error);
>     tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, destr_frame,
> @@ -3927,6 +3927,7 @@ register_local_var_uses (tree *stmt, int *do_subtree, void *d)
>   	     identify them in the coroutine frame.  */
>   	  tree lvname = DECL_NAME (lvar);
>   	  char *buf;
> +
>   	  /* The outermost bind scope contains the artificial variables that
>   	     we inject to implement the coro state machine.  We want to be able
>   	     to inspect these in debugging.  */
> @@ -3936,7 +3937,7 @@ register_local_var_uses (tree *stmt, int *do_subtree, void *d)
>   	    buf = xasprintf ("%s_%u_%u", IDENTIFIER_POINTER (lvname),
>   			     lvd->nest_depth, lvd->bind_indx);
>   	  else
> -	    buf = xasprintf ("_D%u.%u.%u", DECL_UID (lvar), lvd->nest_depth,
> +	    buf = xasprintf ("_D%u_%u_%u", DECL_UID (lvar), lvd->nest_depth,
>   			     lvd->bind_indx);
>   	  /* TODO: Figure out if we should build a local type that has any
>   	     excess alignment or size from the original decl.  */
> @@ -4214,15 +4215,15 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>    declare a dummy coro frame.
>    struct _R_frame {
>     using handle_type = coro::coroutine_handle<coro1::promise_type>;
> -  void (*__resume)(_R_frame *);
> -  void (*__destroy)(_R_frame *);
> -  coro1::promise_type __p;
> -  bool frame_needs_free; free the coro frame mem if set.
> -  bool i_a_r_c; [dcl.fct.def.coroutine] / 5.3
> -  short __resume_at;
> -  handle_type self_handle;
> -  (maybe) parameter copies.
> -  (maybe) local variables saved (including awaitables)
> +  void (*_Coro_resume_fn)(_R_frame *);
> +  void (*_Coro_destroy_fn)(_R_frame *);
> +  coro1::promise_type _Coro_promise;
> +  bool _Coro_frame_needs_free; free the coro frame mem if set.
> +  bool _Coro_i_a_r_c; [dcl.fct.def.coroutine] / 5.3
> +  short _Coro_resume_index;
> +  handle_type _Coro_self_handle;
> +  parameter copies (were required).
> +  local variables saved (including awaitables)
>     (maybe) trailing space.
>    };  */
>   
> @@ -4314,7 +4315,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>   
>     /* 2. Types we need to define or look up.  */
>   
> -  tree fr_name = get_fn_local_identifier (orig, "frame");
> +  tree fr_name = get_fn_local_identifier (orig, "Frame");
>     tree coro_frame_type = xref_tag (record_type, fr_name);
>     DECL_CONTEXT (TYPE_NAME (coro_frame_type)) = current_scope ();
>     tree coro_frame_ptr = build_pointer_type (coro_frame_type);
> @@ -4342,22 +4343,22 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>   
>     tree field_list = NULL_TREE;
>     tree resume_name
> -    = coro_make_frame_entry (&field_list, "__resume",
> +    = coro_make_frame_entry (&field_list, "_Coro_resume_fn",
>   			     act_des_fn_ptr, fn_start);
>     tree destroy_name
> -    = coro_make_frame_entry (&field_list, "__destroy",
> +    = coro_make_frame_entry (&field_list, "_Coro_destroy_fn",
>   			     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",
> +    = coro_make_frame_entry (&field_list, "_Coro_promise", promise_type, fn_start);
> +  tree fnf_name = coro_make_frame_entry (&field_list, "_Coro_frame_needs_free",
>   					 boolean_type_node, fn_start);
>     tree resume_idx_name
> -    = coro_make_frame_entry (&field_list, "__resume_at",
> +    = coro_make_frame_entry (&field_list, "_Coro_resume_index",
>   			     short_unsigned_type_node, fn_start);
>   
>     /* We need a handle to this coroutine, which is passed to every
>        await_suspend().  There's no point in creating it over and over.  */
> -  (void) coro_make_frame_entry (&field_list, "__self_h", handle_type, fn_start);
> +  (void) coro_make_frame_entry (&field_list, "_Coro_self_handle", handle_type, fn_start);
>   
>     /* Now add in fields for function params (if there are any).
>        We do not attempt elision of copies at this stage, we do analyze the
> @@ -4415,14 +4416,14 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>   	  if (DECL_NAME (arg))
>   	    {
>   	      tree pname = DECL_NAME (arg);
> -	      buf = xasprintf ("__parm.%s", IDENTIFIER_POINTER (pname));
> +	      buf = xasprintf ("_P_%s", IDENTIFIER_POINTER (pname));
>   	    }
>   	  else
> -	    buf = xasprintf ("__unnamed_parm.%d", no_name_parm++);
> +	    buf = xasprintf ("_P_unnamed_%d", no_name_parm++);
>   
>   	  if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (parm.frame_type))
>   	    {
> -	      char *gbuf = xasprintf ("%s.live", buf);
> +	      char *gbuf = xasprintf ("%s_live", buf);
>   	      parm.guard_var
>   		= build_lang_decl (VAR_DECL, get_identifier (gbuf),
>   				   boolean_type_node);
> 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 5/8] coroutines: Define and populate accessors for debug state.
  2021-09-01 10:54         ` [PATCH 5/8] coroutines: Define and populate accessors for debug state Iain Sandoe
  2021-09-01 10:55           ` [PATCH 6/8] coroutines: Convert implementation variables to debug-friendly form Iain Sandoe
@ 2021-09-03 13:39           ` Jason Merrill
  2021-09-03 13:41             ` Iain Sandoe
  2021-09-03 13:42             ` Iain Sandoe
  1 sibling, 2 replies; 28+ messages in thread
From: Jason Merrill @ 2021-09-03 13:39 UTC (permalink / raw)
  To: Iain Sandoe, GCC Patches

On 9/1/21 6:54 AM, Iain Sandoe wrote:
> 
> This is an efficiency measure and repeats the pattern used for
> other identifiers used in the coroutine implementation.
> 
> In support of debugging, the user might well need to look at some
> of the variables that the implementation manipulates in lowering
> the coroutines.  The defines the identifiers for these and populates
> them on demand (avoiding repeated identifier calls).
> 
> Contributory to debug support (PR 99215)
> 
> Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
> 
> gcc/cp/ChangeLog:
> 
> 	* coroutines.cc: Add identifiers for implementation
> 	variables that we want to expose to debug.
> 	(coro_init_identifiers): Initialize implementation names.
> 	(coro_promise_type_found_p): Use pre-built identifiers.
> 	(build_actor_fn): Likewise.
> 	(build_destroy_fn): Likewise.
> ---
>   gcc/cp/coroutines.cc | 32 ++++++++++++++++++++++++--------
>   1 file changed, 24 insertions(+), 8 deletions(-)
> 
> diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
> index 081e1a46c63..3b46aac4dc5 100644
> --- a/gcc/cp/coroutines.cc
> +++ b/gcc/cp/coroutines.cc
> @@ -215,7 +215,17 @@ static GTY(()) tree coro_await_ready_identifier;
>   static GTY(()) tree coro_await_suspend_identifier;
>   static GTY(()) tree coro_await_resume_identifier;
>   
> -/* Create the identifiers used by the coroutines library interfaces.  */
> +/* Accessors for the coroutine frame state used by the implementation.  */
> +
> +static GTY(()) tree coro_resume_fn_field;
> +static GTY(()) tree coro_destroy_fn_field;
> +static GTY(()) tree coro_promise_field;
> +static GTY(()) tree coro_frame_needs_free_field;
> +static GTY(()) tree coro_resume_index_field;
> +static GTY(()) tree coro_self_handle_field;

Since these are identifiers, not FIELD_DECLs, calling them *_field seems 
misleading.

> +/* Create the identifiers used by the coroutines library interfaces and
> +   the implementation frame state.  */
>   
>   static void
>   coro_init_identifiers ()
> @@ -241,6 +251,14 @@ coro_init_identifiers ()
>     coro_await_ready_identifier = get_identifier ("await_ready");
>     coro_await_suspend_identifier = get_identifier ("await_suspend");
>     coro_await_resume_identifier = get_identifier ("await_resume");
> +
> +  /* Coroutine state frame field accessors.  */
> +  coro_resume_fn_field = get_identifier ("_Coro_resume_fn");
> +  coro_destroy_fn_field = get_identifier ("_Coro_destroy_fn");
> +  coro_promise_field = get_identifier ("_Coro_promise");
> +  coro_frame_needs_free_field = get_identifier ("_Coro_frame_needs_free");
> +  coro_resume_index_field = get_identifier ("_Coro_resume_index");
> +  coro_self_handle_field = get_identifier ("_Coro_self_handle");
>   }
>   
>   /* Trees we only need to set up once.  */
> @@ -513,12 +531,12 @@ coro_promise_type_found_p (tree fndecl, location_t loc)
>         /* Build a proxy for a handle to "self" as the param to
>   	 await_suspend() calls.  */
>         coro_info->self_h_proxy
> -	= build_lang_decl (VAR_DECL, get_identifier ("_Coro_self_handle"),
> +	= build_lang_decl (VAR_DECL, coro_self_handle_field,
>   			   coro_info->handle_type);
>   
>         /* Build a proxy for the promise so that we can perform lookups.  */
>         coro_info->promise_proxy
> -	= build_lang_decl (VAR_DECL, get_identifier ("_Coro_promise"),
> +	= build_lang_decl (VAR_DECL, coro_promise_field,
>   			   coro_info->promise_type);
>   
>         /* Note where we first saw a coroutine keyword.  */
> @@ -2198,8 +2216,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>       = {actor, actor_frame, coro_frame_type, loc, local_var_uses};
>     cp_walk_tree (&fnbody, transform_local_var_uses, &xform_vars_data, NULL);
>   
> -  tree resume_idx_name = get_identifier ("_Coro_resume_index");
> -  tree rat_field = lookup_member (coro_frame_type, resume_idx_name, 1, 0,
> +  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field, 1, 0,
>   				  tf_warning_or_error);
>     tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, actor_frame,
>   		     rat_field, NULL_TREE);
> @@ -2462,7 +2479,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>   
>     /* We will need to know which resume point number should be encoded.  */
>     tree res_idx_m
> -    = lookup_member (coro_frame_type, resume_idx_name,
> +    = lookup_member (coro_frame_type, coro_resume_index_field,
>   		     /*protect=*/1, /*want_type=*/0, tf_warning_or_error);
>     tree resume_pt_number
>       = build_class_member_access_expr (actor_frame, res_idx_m, NULL_TREE, false,
> @@ -2504,8 +2521,7 @@ build_destroy_fn (location_t loc, tree coro_frame_type, tree destroy,
>   
>     tree destr_frame = build1 (INDIRECT_REF, coro_frame_type, destr_fp);
>   
> -  tree resume_idx_name = get_identifier ("_Coro_resume_index");
> -  tree rat_field = lookup_member (coro_frame_type, resume_idx_name, 1, 0,
> +  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field, 1, 0,
>   				  tf_warning_or_error);
>     tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, destr_frame,
>   		     rat_field, NULL_TREE);
> 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 5/8] coroutines: Define and populate accessors for debug state.
  2021-09-03 13:39           ` [PATCH 5/8] coroutines: Define and populate accessors for debug state Jason Merrill
@ 2021-09-03 13:41             ` Iain Sandoe
  2021-09-03 13:42             ` Iain Sandoe
  1 sibling, 0 replies; 28+ messages in thread
From: Iain Sandoe @ 2021-09-03 13:41 UTC (permalink / raw)
  To: Jason Merrill; +Cc: GCC Patches



> On 3 Sep 2021, at 14:39, Jason Merrill <jason@redhat.com> wrote:
> 
> On 9/1/21 6:54 AM, Iain Sandoe wrote:
>> This is an efficiency measure and repeats the pattern used for
>> other identifiers used in the coroutine implementation.
>> In support of debugging, the user might well need to look at some
>> of the variables that the implementation manipulates in lowering
>> the coroutines.  The defines the identifiers for these and populates
>> them on demand (avoiding repeated identifier calls).
>> Contributory to debug support (PR 99215)
>> Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
>> gcc/cp/ChangeLog:
>> 	* coroutines.cc: Add identifiers for implementation
>> 	variables that we want to expose to debug.
>> 	(coro_init_identifiers): Initialize implementation names.
>> 	(coro_promise_type_found_p): Use pre-built identifiers.
>> 	(build_actor_fn): Likewise.
>> 	(build_destroy_fn): Likewise.
>> ---
>>  gcc/cp/coroutines.cc | 32 ++++++++++++++++++++++++--------
>>  1 file changed, 24 insertions(+), 8 deletions(-)
>> diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
>> index 081e1a46c63..3b46aac4dc5 100644
>> --- a/gcc/cp/coroutines.cc
>> +++ b/gcc/cp/coroutines.cc
>> @@ -215,7 +215,17 @@ static GTY(()) tree coro_await_ready_identifier;
>>  static GTY(()) tree coro_await_suspend_identifier;
>>  static GTY(()) tree coro_await_resume_identifier;
>>  -/* Create the identifiers used by the coroutines library interfaces.  */
>> +/* Accessors for the coroutine frame state used by the implementation.  */
>> +
>> +static GTY(()) tree coro_resume_fn_field;
>> +static GTY(()) tree coro_destroy_fn_field;
>> +static GTY(()) tree coro_promise_field;
>> +static GTY(()) tree coro_frame_needs_free_field;
>> +static GTY(()) tree coro_resume_index_field;
>> +static GTY(()) tree coro_self_handle_field;
> 
> Since these are identifiers, not FIELD_DECLs, calling them *_field seems misleading.

> 
>> +/* Create the identifiers used by the coroutines library interfaces and
>> +   the implementation frame state.  */
>>    static void
>>  coro_init_identifiers ()
>> @@ -241,6 +251,14 @@ coro_init_identifiers ()
>>    coro_await_ready_identifier = get_identifier ("await_ready");
>>    coro_await_suspend_identifier = get_identifier ("await_suspend");
>>    coro_await_resume_identifier = get_identifier ("await_resume");
>> +
>> +  /* Coroutine state frame field accessors.  */
>> +  coro_resume_fn_field = get_identifier ("_Coro_resume_fn");
>> +  coro_destroy_fn_field = get_identifier ("_Coro_destroy_fn");
>> +  coro_promise_field = get_identifier ("_Coro_promise");
>> +  coro_frame_needs_free_field = get_identifier ("_Coro_frame_needs_free");
>> +  coro_resume_index_field = get_identifier ("_Coro_resume_index");
>> +  coro_self_handle_field = get_identifier ("_Coro_self_handle");
>>  }
>>    /* Trees we only need to set up once.  */
>> @@ -513,12 +531,12 @@ coro_promise_type_found_p (tree fndecl, location_t loc)
>>        /* Build a proxy for a handle to "self" as the param to
>>  	 await_suspend() calls.  */
>>        coro_info->self_h_proxy
>> -	= build_lang_decl (VAR_DECL, get_identifier ("_Coro_self_handle"),
>> +	= build_lang_decl (VAR_DECL, coro_self_handle_field,
>>  			   coro_info->handle_type);
>>          /* Build a proxy for the promise so that we can perform lookups.  */
>>        coro_info->promise_proxy
>> -	= build_lang_decl (VAR_DECL, get_identifier ("_Coro_promise"),
>> +	= build_lang_decl (VAR_DECL, coro_promise_field,
>>  			   coro_info->promise_type);
>>          /* Note where we first saw a coroutine keyword.  */
>> @@ -2198,8 +2216,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>      = {actor, actor_frame, coro_frame_type, loc, local_var_uses};
>>    cp_walk_tree (&fnbody, transform_local_var_uses, &xform_vars_data, NULL);
>>  -  tree resume_idx_name = get_identifier ("_Coro_resume_index");
>> -  tree rat_field = lookup_member (coro_frame_type, resume_idx_name, 1, 0,
>> +  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field, 1, 0,
>>  				  tf_warning_or_error);
>>    tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, actor_frame,
>>  		     rat_field, NULL_TREE);
>> @@ -2462,7 +2479,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>      /* We will need to know which resume point number should be encoded.  */
>>    tree res_idx_m
>> -    = lookup_member (coro_frame_type, resume_idx_name,
>> +    = lookup_member (coro_frame_type, coro_resume_index_field,
>>  		     /*protect=*/1, /*want_type=*/0, tf_warning_or_error);
>>    tree resume_pt_number
>>      = build_class_member_access_expr (actor_frame, res_idx_m, NULL_TREE, false,
>> @@ -2504,8 +2521,7 @@ build_destroy_fn (location_t loc, tree coro_frame_type, tree destroy,
>>      tree destr_frame = build1 (INDIRECT_REF, coro_frame_type, destr_fp);
>>  -  tree resume_idx_name = get_identifier ("_Coro_resume_index");
>> -  tree rat_field = lookup_member (coro_frame_type, resume_idx_name, 1, 0,
>> +  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field, 1, 0,
>>  				  tf_warning_or_error);
>>    tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, destr_frame,
>>  		     rat_field, NULL_TREE);


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 5/8] coroutines: Define and populate accessors for debug state.
  2021-09-03 13:39           ` [PATCH 5/8] coroutines: Define and populate accessors for debug state Jason Merrill
  2021-09-03 13:41             ` Iain Sandoe
@ 2021-09-03 13:42             ` Iain Sandoe
  2021-09-03 13:44               ` Jason Merrill
  1 sibling, 1 reply; 28+ messages in thread
From: Iain Sandoe @ 2021-09-03 13:42 UTC (permalink / raw)
  To: Jason Merrill; +Cc: GCC Patches



> On 3 Sep 2021, at 14:39, Jason Merrill <jason@redhat.com> wrote:
> 
> On 9/1/21 6:54 AM, Iain Sandoe wrote:
>> This is an efficiency measure and repeats the pattern used for
>> other identifiers used in the coroutine implementation.
>> In support of debugging, the user might well need to look at some
>> of the variables that the implementation manipulates in lowering
>> the coroutines.  The defines the identifiers for these and populates
>> them on demand (avoiding repeated identifier calls).
>> Contributory to debug support (PR 99215)
>> Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
>> gcc/cp/ChangeLog:
>> 	* coroutines.cc: Add identifiers for implementation
>> 	variables that we want to expose to debug.
>> 	(coro_init_identifiers): Initialize implementation names.
>> 	(coro_promise_type_found_p): Use pre-built identifiers.
>> 	(build_actor_fn): Likewise.
>> 	(build_destroy_fn): Likewise.
>> ---
>>  gcc/cp/coroutines.cc | 32 ++++++++++++++++++++++++--------
>>  1 file changed, 24 insertions(+), 8 deletions(-)
>> diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
>> index 081e1a46c63..3b46aac4dc5 100644
>> --- a/gcc/cp/coroutines.cc
>> +++ b/gcc/cp/coroutines.cc
>> @@ -215,7 +215,17 @@ static GTY(()) tree coro_await_ready_identifier;
>>  static GTY(()) tree coro_await_suspend_identifier;
>>  static GTY(()) tree coro_await_resume_identifier;
>>  -/* Create the identifiers used by the coroutines library interfaces.  */
>> +/* Accessors for the coroutine frame state used by the implementation.  */
>> +
>> +static GTY(()) tree coro_resume_fn_field;
>> +static GTY(()) tree coro_destroy_fn_field;
>> +static GTY(()) tree coro_promise_field;
>> +static GTY(()) tree coro_frame_needs_free_field;
>> +static GTY(()) tree coro_resume_index_field;
>> +static GTY(()) tree coro_self_handle_field;
> 
> Since these are identifiers, not FIELD_DECLs, calling them *_field seems misleading.

I could append _id or _name .. they were just getting long already.
(they are names of fields, so that would not be misleading)

Iain


> 
>> +/* Create the identifiers used by the coroutines library interfaces and
>> +   the implementation frame state.  */
>>    static void
>>  coro_init_identifiers ()
>> @@ -241,6 +251,14 @@ coro_init_identifiers ()
>>    coro_await_ready_identifier = get_identifier ("await_ready");
>>    coro_await_suspend_identifier = get_identifier ("await_suspend");
>>    coro_await_resume_identifier = get_identifier ("await_resume");
>> +
>> +  /* Coroutine state frame field accessors.  */
>> +  coro_resume_fn_field = get_identifier ("_Coro_resume_fn");
>> +  coro_destroy_fn_field = get_identifier ("_Coro_destroy_fn");
>> +  coro_promise_field = get_identifier ("_Coro_promise");
>> +  coro_frame_needs_free_field = get_identifier ("_Coro_frame_needs_free");
>> +  coro_resume_index_field = get_identifier ("_Coro_resume_index");
>> +  coro_self_handle_field = get_identifier ("_Coro_self_handle");
>>  }
>>    /* Trees we only need to set up once.  */
>> @@ -513,12 +531,12 @@ coro_promise_type_found_p (tree fndecl, location_t loc)
>>        /* Build a proxy for a handle to "self" as the param to
>>  	 await_suspend() calls.  */
>>        coro_info->self_h_proxy
>> -	= build_lang_decl (VAR_DECL, get_identifier ("_Coro_self_handle"),
>> +	= build_lang_decl (VAR_DECL, coro_self_handle_field,
>>  			   coro_info->handle_type);
>>          /* Build a proxy for the promise so that we can perform lookups.  */
>>        coro_info->promise_proxy
>> -	= build_lang_decl (VAR_DECL, get_identifier ("_Coro_promise"),
>> +	= build_lang_decl (VAR_DECL, coro_promise_field,
>>  			   coro_info->promise_type);
>>          /* Note where we first saw a coroutine keyword.  */
>> @@ -2198,8 +2216,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>      = {actor, actor_frame, coro_frame_type, loc, local_var_uses};
>>    cp_walk_tree (&fnbody, transform_local_var_uses, &xform_vars_data, NULL);
>>  -  tree resume_idx_name = get_identifier ("_Coro_resume_index");
>> -  tree rat_field = lookup_member (coro_frame_type, resume_idx_name, 1, 0,
>> +  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field, 1, 0,
>>  				  tf_warning_or_error);
>>    tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, actor_frame,
>>  		     rat_field, NULL_TREE);
>> @@ -2462,7 +2479,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>      /* We will need to know which resume point number should be encoded.  */
>>    tree res_idx_m
>> -    = lookup_member (coro_frame_type, resume_idx_name,
>> +    = lookup_member (coro_frame_type, coro_resume_index_field,
>>  		     /*protect=*/1, /*want_type=*/0, tf_warning_or_error);
>>    tree resume_pt_number
>>      = build_class_member_access_expr (actor_frame, res_idx_m, NULL_TREE, false,
>> @@ -2504,8 +2521,7 @@ build_destroy_fn (location_t loc, tree coro_frame_type, tree destroy,
>>      tree destr_frame = build1 (INDIRECT_REF, coro_frame_type, destr_fp);
>>  -  tree resume_idx_name = get_identifier ("_Coro_resume_index");
>> -  tree rat_field = lookup_member (coro_frame_type, resume_idx_name, 1, 0,
>> +  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field, 1, 0,
>>  				  tf_warning_or_error);
>>    tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, destr_frame,
>>  		     rat_field, NULL_TREE);


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 5/8] coroutines: Define and populate accessors for debug state.
  2021-09-03 13:42             ` Iain Sandoe
@ 2021-09-03 13:44               ` Jason Merrill
  0 siblings, 0 replies; 28+ messages in thread
From: Jason Merrill @ 2021-09-03 13:44 UTC (permalink / raw)
  To: Iain Sandoe; +Cc: GCC Patches

On 9/3/21 9:42 AM, Iain Sandoe wrote:
> 
> 
>> On 3 Sep 2021, at 14:39, Jason Merrill <jason@redhat.com> wrote:
>>
>> On 9/1/21 6:54 AM, Iain Sandoe wrote:
>>> This is an efficiency measure and repeats the pattern used for
>>> other identifiers used in the coroutine implementation.
>>> In support of debugging, the user might well need to look at some
>>> of the variables that the implementation manipulates in lowering
>>> the coroutines.  The defines the identifiers for these and populates
>>> them on demand (avoiding repeated identifier calls).
>>> Contributory to debug support (PR 99215)
>>> Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
>>> gcc/cp/ChangeLog:
>>> 	* coroutines.cc: Add identifiers for implementation
>>> 	variables that we want to expose to debug.
>>> 	(coro_init_identifiers): Initialize implementation names.
>>> 	(coro_promise_type_found_p): Use pre-built identifiers.
>>> 	(build_actor_fn): Likewise.
>>> 	(build_destroy_fn): Likewise.
>>> ---
>>>   gcc/cp/coroutines.cc | 32 ++++++++++++++++++++++++--------
>>>   1 file changed, 24 insertions(+), 8 deletions(-)
>>> diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
>>> index 081e1a46c63..3b46aac4dc5 100644
>>> --- a/gcc/cp/coroutines.cc
>>> +++ b/gcc/cp/coroutines.cc
>>> @@ -215,7 +215,17 @@ static GTY(()) tree coro_await_ready_identifier;
>>>   static GTY(()) tree coro_await_suspend_identifier;
>>>   static GTY(()) tree coro_await_resume_identifier;
>>>   -/* Create the identifiers used by the coroutines library interfaces.  */
>>> +/* Accessors for the coroutine frame state used by the implementation.  */
>>> +
>>> +static GTY(()) tree coro_resume_fn_field;
>>> +static GTY(()) tree coro_destroy_fn_field;
>>> +static GTY(()) tree coro_promise_field;
>>> +static GTY(()) tree coro_frame_needs_free_field;
>>> +static GTY(()) tree coro_resume_index_field;
>>> +static GTY(()) tree coro_self_handle_field;
>>
>> Since these are identifiers, not FIELD_DECLs, calling them *_field seems misleading.
> 
> I could append _id or _name .. they were just getting long already.
> (they are names of fields, so that would not be misleading)

_id works for me, either with or without the _field.

Jason

>>
>>> +/* Create the identifiers used by the coroutines library interfaces and
>>> +   the implementation frame state.  */
>>>     static void
>>>   coro_init_identifiers ()
>>> @@ -241,6 +251,14 @@ coro_init_identifiers ()
>>>     coro_await_ready_identifier = get_identifier ("await_ready");
>>>     coro_await_suspend_identifier = get_identifier ("await_suspend");
>>>     coro_await_resume_identifier = get_identifier ("await_resume");
>>> +
>>> +  /* Coroutine state frame field accessors.  */
>>> +  coro_resume_fn_field = get_identifier ("_Coro_resume_fn");
>>> +  coro_destroy_fn_field = get_identifier ("_Coro_destroy_fn");
>>> +  coro_promise_field = get_identifier ("_Coro_promise");
>>> +  coro_frame_needs_free_field = get_identifier ("_Coro_frame_needs_free");
>>> +  coro_resume_index_field = get_identifier ("_Coro_resume_index");
>>> +  coro_self_handle_field = get_identifier ("_Coro_self_handle");
>>>   }
>>>     /* Trees we only need to set up once.  */
>>> @@ -513,12 +531,12 @@ coro_promise_type_found_p (tree fndecl, location_t loc)
>>>         /* Build a proxy for a handle to "self" as the param to
>>>   	 await_suspend() calls.  */
>>>         coro_info->self_h_proxy
>>> -	= build_lang_decl (VAR_DECL, get_identifier ("_Coro_self_handle"),
>>> +	= build_lang_decl (VAR_DECL, coro_self_handle_field,
>>>   			   coro_info->handle_type);
>>>           /* Build a proxy for the promise so that we can perform lookups.  */
>>>         coro_info->promise_proxy
>>> -	= build_lang_decl (VAR_DECL, get_identifier ("_Coro_promise"),
>>> +	= build_lang_decl (VAR_DECL, coro_promise_field,
>>>   			   coro_info->promise_type);
>>>           /* Note where we first saw a coroutine keyword.  */
>>> @@ -2198,8 +2216,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>>       = {actor, actor_frame, coro_frame_type, loc, local_var_uses};
>>>     cp_walk_tree (&fnbody, transform_local_var_uses, &xform_vars_data, NULL);
>>>   -  tree resume_idx_name = get_identifier ("_Coro_resume_index");
>>> -  tree rat_field = lookup_member (coro_frame_type, resume_idx_name, 1, 0,
>>> +  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field, 1, 0,
>>>   				  tf_warning_or_error);
>>>     tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, actor_frame,
>>>   		     rat_field, NULL_TREE);
>>> @@ -2462,7 +2479,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>>       /* We will need to know which resume point number should be encoded.  */
>>>     tree res_idx_m
>>> -    = lookup_member (coro_frame_type, resume_idx_name,
>>> +    = lookup_member (coro_frame_type, coro_resume_index_field,
>>>   		     /*protect=*/1, /*want_type=*/0, tf_warning_or_error);
>>>     tree resume_pt_number
>>>       = build_class_member_access_expr (actor_frame, res_idx_m, NULL_TREE, false,
>>> @@ -2504,8 +2521,7 @@ build_destroy_fn (location_t loc, tree coro_frame_type, tree destroy,
>>>       tree destr_frame = build1 (INDIRECT_REF, coro_frame_type, destr_fp);
>>>   -  tree resume_idx_name = get_identifier ("_Coro_resume_index");
>>> -  tree rat_field = lookup_member (coro_frame_type, resume_idx_name, 1, 0,
>>> +  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field, 1, 0,
>>>   				  tf_warning_or_error);
>>>     tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, destr_frame,
>>>   		     rat_field, NULL_TREE);
> 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 6/8] coroutines: Convert implementation variables to debug-friendly form.
  2021-09-01 10:55           ` [PATCH 6/8] coroutines: Convert implementation variables to debug-friendly form Iain Sandoe
  2021-09-01 10:56             ` [PATCH 7/8] coroutines: Make proxy vars for the function arg copies Iain Sandoe
@ 2021-09-03 13:52             ` Jason Merrill
  2021-09-03 13:56               ` Iain Sandoe
  1 sibling, 1 reply; 28+ messages in thread
From: Jason Merrill @ 2021-09-03 13:52 UTC (permalink / raw)
  To: Iain Sandoe, GCC Patches

On 9/1/21 6:55 AM, Iain Sandoe wrote:
> 
> The user might well wish to inspect some of the state that represents
> the implementation of the coroutine machine.
> 
> In particular:
>    The promise object.
>    The function pointers for the resumer and destroyer.
>    The current resume index (suspend point).
>    The handle that represent this coroutine 'self handle'.
>    Whether the coroutine frame is allocated and needs to be freed.
> 
> These variables are given names that can be 'well-known' and advertised
> in debug documentation - they are placed in the implementation namespace
> and all begin with _Coro_.

> Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
> 
> gcc/cp/ChangeLog:
> 
> 	* coroutines.cc (transform_await_expr): Use debug-friendly
> 	names for coroutine implementation.
> 	(build_actor_fn): Likewise.
> 	(build_destroy_fn): Likewise.
> 	(coro_rewrite_function_body): Likewise.
> 	(morph_fn_to_coro): Likewise.

Hmm, this patch doesn't seem to match the description and ChangeLog 
entry other than in the names of the functions changed.

> ---
>   gcc/cp/coroutines.cc | 214 +++++++++++++++++++------------------------
>   1 file changed, 94 insertions(+), 120 deletions(-)
> 
> diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
> index 3b46aac4dc5..aacf352f1c9 100644
> --- a/gcc/cp/coroutines.cc
> +++ b/gcc/cp/coroutines.cc
> @@ -1906,7 +1906,6 @@ transform_await_expr (tree await_expr, await_xform_data *xform)
>     /* So, on entry, we have:
>        in : CO_AWAIT_EXPR (a, e_proxy, o, awr_call_vector, mode)
>   	  We no longer need a [it had diagnostic value, maybe?]
> -	  We need to replace the promise proxy in all elements
>   	  We need to replace the e_proxy in the awr_call.  */
>   
>     tree coro_frame_type = TREE_TYPE (xform->actor_frame);
> @@ -1932,16 +1931,6 @@ transform_await_expr (tree await_expr, await_xform_data *xform)
>         TREE_OPERAND (await_expr, 1) = as;
>       }
>   
> -  /* Now do the self_handle.  */
> -  data.from = xform->self_h_proxy;
> -  data.to = xform->real_self_h;
> -  cp_walk_tree (&await_expr, replace_proxy, &data, NULL);
> -
> -  /* Now do the promise.  */
> -  data.from = xform->promise_proxy;
> -  data.to = xform->real_promise;
> -  cp_walk_tree (&await_expr, replace_proxy, &data, NULL);
> -
>     return await_expr;
>   }
>   
> @@ -2128,10 +2117,9 @@ coro_get_frame_dtor (tree coro_fp, tree orig, tree frame_size,
>   
>   static void
>   build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
> -		tree orig, hash_map<tree, param_info> *param_uses,
> -		hash_map<tree, local_var_info> *local_var_uses,
> -		vec<tree, va_gc> *param_dtor_list, tree resume_fn_field,
> -		tree resume_idx_field, unsigned body_count, tree frame_size)
> +		tree orig, hash_map<tree, local_var_info> *local_var_uses,
> +		vec<tree, va_gc> *param_dtor_list,
> +		tree resume_idx_var, unsigned body_count, tree frame_size)
>   {
>     verify_stmt_tree (fnbody);
>     /* Some things we inherit from the original function.  */
> @@ -2216,8 +2204,8 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>       = {actor, actor_frame, coro_frame_type, loc, local_var_uses};
>     cp_walk_tree (&fnbody, transform_local_var_uses, &xform_vars_data, NULL);
>   
> -  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field, 1, 0,
> -				  tf_warning_or_error);
> +  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field,
> +				  1, 0, tf_warning_or_error);
>     tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, actor_frame,
>   		     rat_field, NULL_TREE);
>   
> @@ -2319,14 +2307,8 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>     tree r = build_stmt (loc, LABEL_EXPR, actor_begin_label);
>     add_stmt (r);
>   
> -  /* actor's version of the promise.  */
> -  tree ap_m = lookup_member (coro_frame_type, get_identifier ("_Coro_promise"), 1, 0,
> -			     tf_warning_or_error);
> -  tree ap = build_class_member_access_expr (actor_frame, ap_m, NULL_TREE, false,
> -					    tf_warning_or_error);
> -
>     /* actor's coroutine 'self handle'.  */
> -  tree ash_m = lookup_member (coro_frame_type, get_identifier ("_Coro_self_handle"), 1,
> +  tree ash_m = lookup_member (coro_frame_type, coro_self_handle_field, 1,
>   			      0, tf_warning_or_error);
>     tree ash = build_class_member_access_expr (actor_frame, ash_m, NULL_TREE,
>   					     false, tf_warning_or_error);
> @@ -2347,36 +2329,13 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>        decide where to put things.  */
>   
>     await_xform_data xform
> -    = {actor, actor_frame, promise_proxy, ap, self_h_proxy, ash};
> +    = {actor, actor_frame, NULL_TREE, NULL_TREE, self_h_proxy, ash};
>   
>     /* Transform the await expressions in the function body.  Only do each
>        await tree once!  */
>     hash_set<tree> pset;
>     cp_walk_tree (&fnbody, transform_await_wrapper, &xform, &pset);
>   
> -  /* 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);
> -
> -  /* The rewrite of the function adds code to set the resume_fn field to
> -     nullptr when the coroutine is done and also the index to zero when
> -     calling an unhandled exception.  These are represented by two proxies
> -     in the function, so rewrite them to the proper frame access.  */
> -  tree resume_m
> -    = lookup_member (coro_frame_type, get_identifier ("_Coro_resume_fn"),
> -		     /*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);
> -  p_data.from = resume_fn_field;
> -  p_data.to = res_x;
> -  cp_walk_tree (&fnbody, replace_proxy, &p_data, NULL);
> -
> -  p_data.from = resume_idx_field;
> -  p_data.to = rat;
> -  cp_walk_tree (&fnbody, replace_proxy, &p_data, NULL);
> -
>     /* Add in our function body with the co_returns rewritten to final form.  */
>     add_stmt (fnbody);
>   
> @@ -2385,7 +2344,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>     add_stmt (r);
>   
>     /* Destructors for the things we built explicitly.  */
> -  r = build_special_member_call (ap, complete_dtor_identifier, NULL,
> +  r = build_special_member_call (promise_proxy, complete_dtor_identifier, NULL,
>   				 promise_type, LOOKUP_NORMAL,
>   				 tf_warning_or_error);
>     add_stmt (r);
> @@ -2398,7 +2357,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>     /* Here deallocate the frame (if we allocated it), which we will have at
>        present.  */
>     tree fnf_m
> -    = lookup_member (coro_frame_type, get_identifier ("_Coro_frame_needs_free"), 1,
> +    = lookup_member (coro_frame_type, coro_frame_needs_free_field, 1,
>   		     0, tf_warning_or_error);
>     tree fnf2_x = build_class_member_access_expr (actor_frame, fnf_m, NULL_TREE,
>   						false, tf_warning_or_error);
> @@ -2477,18 +2436,10 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>     gcc_checking_assert (maybe_cleanup_point_expr_void (r) == r);
>     add_stmt (r);
>   
> -  /* We will need to know which resume point number should be encoded.  */
> -  tree res_idx_m
> -    = lookup_member (coro_frame_type, coro_resume_index_field,
> -		     /*protect=*/1, /*want_type=*/0, tf_warning_or_error);
> -  tree resume_pt_number
> -    = build_class_member_access_expr (actor_frame, res_idx_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, NULL_TREE,
> +  coro_aw_data data = {actor, actor_fp, resume_idx_var, NULL_TREE,
>   		       ash, del_promise_label, ret_label,
>   		       continue_label, continuation, 2};
>     cp_walk_tree (&actor_body, await_statement_expander, &data, NULL);
> @@ -2502,7 +2453,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>   }
>   
>   /* The prototype 'destroy' function :
> -   frame->__resume_at |= 1;
> +   frame->__Coro_resume_index |= 1;
>      actor (frame);  */
>   
>   static void
> @@ -2521,10 +2472,10 @@ build_destroy_fn (location_t loc, tree coro_frame_type, tree destroy,
>   
>     tree destr_frame = build1 (INDIRECT_REF, coro_frame_type, destr_fp);
>   
> -  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field, 1, 0,
> -				  tf_warning_or_error);
> -  tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, destr_frame,
> -		     rat_field, NULL_TREE);
> +  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field,
> +				  1, 0, tf_warning_or_error);
> +  tree rat = build3 (COMPONENT_REF, short_unsigned_type_node,
> +			 destr_frame, rat_field, NULL_TREE);
>   
>     /* _resume_at |= 1 */
>     tree dstr_idx = build2 (BIT_IOR_EXPR, short_unsigned_type_node, rat,
> @@ -4040,8 +3991,8 @@ coro_build_actor_or_destroy_function (tree orig, tree fn_type,
>   
>   static tree
>   coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
> -			    tree resume_fn_ptr_type, tree& resume_fn_field,
> -			    tree& resume_idx_field, tree& fs_label)
> +			    tree resume_fn_ptr_type,
> +			    tree& resume_idx_var, tree& fs_label)
>   {
>     /* This will be our new outer scope.  */
>     tree update_body = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL);
> @@ -4074,7 +4025,6 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>   
>     /* 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);
>   
> @@ -4085,24 +4035,61 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>     tree return_void
>       = get_coroutine_return_void_expr (current_function_decl, fn_start, false);
>   
> +  /* The pointer to the resume function.  */
> +  tree resume_fn_ptr
> +    = coro_build_artificial_var (fn_start, coro_resume_fn_field,
> +				 resume_fn_ptr_type, orig, NULL_TREE);
> +  DECL_CHAIN (resume_fn_ptr) = var_list;
> +  var_list = resume_fn_ptr;
> +  add_decl_expr (resume_fn_ptr);
> +
>     /* We will need to be able to set the resume function pointer to nullptr
>        to signal that the coroutine is 'done'.  */
> -  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);
> -  /* Likewise, the resume index needs to be reset.  */
> -  resume_idx_field
> -    = build_lang_decl (VAR_DECL, get_identifier ("resume.index.proxy"),
> -		       short_unsigned_type_node);
> -  DECL_ARTIFICIAL (resume_idx_field) = true;
> -  tree zero_resume_idx = build_int_cst (short_unsigned_type_node, 0);
> -  zero_resume_idx = build2 (INIT_EXPR, short_unsigned_type_node,
> -			    resume_idx_field, zero_resume_idx);
> +
> +  /* The pointer to the destroy function.  */
> +  tree var = coro_build_artificial_var (fn_start, coro_destroy_fn_field,
> +					resume_fn_ptr_type, orig, NULL_TREE);
> +  DECL_CHAIN (var) = var_list;
> +  var_list = var;
> +  add_decl_expr (var);
> +
> +  /* The promise was created on demand when parsing we now link it into
> +      our scope.  */
> +  tree promise = get_coroutine_promise_proxy (orig);
> +  DECL_CONTEXT (promise) = orig;
> +  DECL_SOURCE_LOCATION (promise) = fn_start;
> +  DECL_CHAIN (promise) = var_list;
> +  var_list = promise;
> +  add_decl_expr (promise);
> +
> +  /* We need a handle to this coroutine, which is passed to every
> +     await_suspend().  This was created on demand when parsing we now link it
> +     into our scope.  */
> +  var = get_coroutine_self_handle_proxy (orig);
> +  DECL_CONTEXT (var) = orig;
> +  DECL_SOURCE_LOCATION (var) = fn_start;
> +  DECL_CHAIN (var) = var_list;
> +  var_list = var;
> +  add_decl_expr (var);
> +
> +
> +  /* We create a resume index, this is initialized in the ramp.  */
> +  resume_idx_var
> +    = coro_build_artificial_var (fn_start, coro_resume_index_field,
> +				 short_unsigned_type_node, orig, NULL_TREE);
> +  DECL_CHAIN (resume_idx_var) = var_list;
> +  var_list = resume_idx_var;
> +  add_decl_expr (resume_idx_var);
> +
> +  /* If the coroutine has a frame that needs to be freed, this will be set by
> +     the ramp.  */
> +  var = coro_build_artificial_var (fn_start, coro_frame_needs_free_field,
> +				   boolean_type_node, orig, NULL_TREE);
> +  DECL_CHAIN (var) = var_list;
> +  var_list = var;
> +  add_decl_expr (var);
>   
>     if (flag_exceptions)
>       {
> @@ -4166,10 +4153,14 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>   	 If the unhandled exception method returns, then we continue
>   	 to the final await expression (which duplicates the clearing of
>   	 the field). */
> -      finish_expr_stmt (zero_resume);
> -      finish_expr_stmt (zero_resume_idx);
> -      ueh = maybe_cleanup_point_expr_void (ueh);
> -      add_stmt (ueh);
> +      tree r = build2 (MODIFY_EXPR, resume_fn_ptr_type, resume_fn_ptr,
> +		       zero_resume);
> +      finish_expr_stmt (r);
> +      tree short_zero = build_int_cst (short_unsigned_type_node, 0);
> +      r = build2 (MODIFY_EXPR, short_unsigned_type_node, resume_idx_var,
> +		  short_zero);
> +      finish_expr_stmt (r);
> +      finish_expr_stmt (ueh);
>         finish_handler (handler);
>         TRY_HANDLERS (tcb) = pop_stmt_list (TRY_HANDLERS (tcb));
>       }
> @@ -4204,6 +4195,8 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>     /* 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.  */
> +  zero_resume = build2 (MODIFY_EXPR, resume_fn_ptr_type, resume_fn_ptr,
> +			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));
> @@ -4348,33 +4341,16 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>     /* Construct the wrapped function body; we will analyze this to determine
>        the requirements for the coroutine frame.  */
>   
> -  tree resume_fn_field = NULL_TREE;
> -  tree resume_idx_field = NULL_TREE;
> +  tree resume_idx_var = NULL_TREE;
>     tree fs_label = NULL_TREE;
>     fnbody = coro_rewrite_function_body (fn_start, fnbody, orig,
> -				       act_des_fn_ptr, resume_fn_field,
> -				       resume_idx_field, fs_label);
> +				       act_des_fn_ptr,
> +				       resume_idx_var, fs_label);
>     /* Build our dummy coro frame layout.  */
>     coro_frame_type = begin_class_definition (coro_frame_type);
>   
> +  /* The fields for the coro frame.  */
>     tree field_list = NULL_TREE;
> -  tree resume_name
> -    = coro_make_frame_entry (&field_list, "_Coro_resume_fn",
> -			     act_des_fn_ptr, fn_start);
> -  tree destroy_name
> -    = coro_make_frame_entry (&field_list, "_Coro_destroy_fn",
> -			     act_des_fn_ptr, fn_start);
> -  tree promise_name
> -    = coro_make_frame_entry (&field_list, "_Coro_promise", promise_type, fn_start);
> -  tree fnf_name = coro_make_frame_entry (&field_list, "_Coro_frame_needs_free",
> -					 boolean_type_node, fn_start);
> -  tree resume_idx_name
> -    = coro_make_frame_entry (&field_list, "_Coro_resume_index",
> -			     short_unsigned_type_node, fn_start);
> -
> -  /* We need a handle to this coroutine, which is passed to every
> -     await_suspend().  There's no point in creating it over and over.  */
> -  (void) coro_make_frame_entry (&field_list, "_Coro_self_handle", handle_type, fn_start);
>   
>     /* Now add in fields for function params (if there are any).
>        We do not attempt elision of copies at this stage, we do analyze the
> @@ -4776,8 +4752,8 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>   
>     /* 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 (coro_frame_type, fnf_name, 1, 0, tf_warning_or_error);
> +  tree fnf_m = lookup_member (coro_frame_type, coro_frame_needs_free_field,
> +			      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 = build2 (INIT_EXPR, boolean_type_node, fnf_x, boolean_true_node);
> @@ -4788,24 +4764,22 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>   
>     tree actor_addr = build1 (ADDR_EXPR, act_des_fn_ptr, actor);
>     tree resume_m
> -    = lookup_member (coro_frame_type, resume_name,
> +    = lookup_member (coro_frame_type, coro_resume_fn_field,
>   		     /*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 = build2_loc (fn_start, INIT_EXPR, act_des_fn_ptr, resume_x, actor_addr);
> -  r = coro_build_cvt_void_expr_stmt (r, fn_start);
> -  add_stmt (r);
> +  finish_expr_stmt (r);
>   
>     tree destroy_addr = build1 (ADDR_EXPR, act_des_fn_ptr, destroy);
>     tree destroy_m
> -    = lookup_member (coro_frame_type, destroy_name,
> +    = lookup_member (coro_frame_type, coro_destroy_fn_field,
>   		     /*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 = build2_loc (fn_start, INIT_EXPR, act_des_fn_ptr, destroy_x, destroy_addr);
> -  r = coro_build_cvt_void_expr_stmt (r, fn_start);
> -  add_stmt (r);
> +  finish_expr_stmt (r);
>   
>     /* [dcl.fct.def.coroutine] /13
>        When a coroutine is invoked, a copy is created for each coroutine
> @@ -4896,7 +4870,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>   
>     /* Set up the promise.  */
>     tree promise_m
> -    = lookup_member (coro_frame_type, promise_name,
> +    = lookup_member (coro_frame_type, coro_promise_field,
>   		     /*protect=*/1, /*want_type=*/0, tf_warning_or_error);
>   
>     tree p = build_class_member_access_expr (deref_fp, promise_m, NULL_TREE,
> @@ -5042,9 +5016,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>   			      boolean_type_node);
>         finish_expr_stmt (r);
>       }
> -  /* Initialize the resume_idx_name to 0, meaning "not started".  */
> +  /* Initialize the resume_idx_var to 0, meaning "not started".  */
>     tree resume_idx_m
> -    = lookup_member (coro_frame_type, resume_idx_name,
> +    = lookup_member (coro_frame_type, coro_resume_index_field,
>   		     /*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,
> @@ -5187,9 +5161,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>     push_deferring_access_checks (dk_no_check);
>   
>     /* Build the actor...  */
> -  build_actor_fn (fn_start, coro_frame_type, actor, fnbody, orig, param_uses,
> -		  &local_var_uses, param_dtor_list, resume_fn_field,
> -		  resume_idx_field, body_aw_points.await_number, frame_size);
> +  build_actor_fn (fn_start, coro_frame_type, actor, fnbody, orig,
> +		  &local_var_uses, param_dtor_list,
> +		  resume_idx_var, body_aw_points.await_number, frame_size);
>   
>     /* Destroyer ... */
>     build_destroy_fn (fn_start, coro_frame_type, destroy, actor);
> 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 6/8] coroutines: Convert implementation variables to debug-friendly form.
  2021-09-03 13:52             ` [PATCH 6/8] coroutines: Convert implementation variables to debug-friendly form Jason Merrill
@ 2021-09-03 13:56               ` Iain Sandoe
  2021-09-03 14:12                 ` Jason Merrill
  0 siblings, 1 reply; 28+ messages in thread
From: Iain Sandoe @ 2021-09-03 13:56 UTC (permalink / raw)
  To: Jason Merrill; +Cc: GCC Patches



> On 3 Sep 2021, at 14:52, Jason Merrill <jason@redhat.com> wrote:
> 
> On 9/1/21 6:55 AM, Iain Sandoe wrote:
>> The user might well wish to inspect some of the state that represents
>> the implementation of the coroutine machine.
>> In particular:
>>   The promise object.
>>   The function pointers for the resumer and destroyer.
>>   The current resume index (suspend point).
>>   The handle that represent this coroutine 'self handle'.
>>   Whether the coroutine frame is allocated and needs to be freed.
>> These variables are given names that can be 'well-known' and advertised
>> in debug documentation - they are placed in the implementation namespace
>> and all begin with _Coro_.
> 
>> Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
>> gcc/cp/ChangeLog:
>> 	* coroutines.cc (transform_await_expr): Use debug-friendly
>> 	names for coroutine implementation.
>> 	(build_actor_fn): Likewise.
>> 	(build_destroy_fn): Likewise.
>> 	(coro_rewrite_function_body): Likewise.
>> 	(morph_fn_to_coro): Likewise.
> 
> Hmm, this patch doesn't seem to match the description and ChangeLog entry other than in the names of the functions changed.

with 20:20 hindsight I should have squashed the (several) patches related to the implementation symbols, 

I’ll redo the description - essentially, this is just making use of the simplification available because we now have pre-defined values for the field names.

>> ---
>>  gcc/cp/coroutines.cc | 214 +++++++++++++++++++------------------------
>>  1 file changed, 94 insertions(+), 120 deletions(-)
>> diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
>> index 3b46aac4dc5..aacf352f1c9 100644
>> --- a/gcc/cp/coroutines.cc
>> +++ b/gcc/cp/coroutines.cc
>> @@ -1906,7 +1906,6 @@ transform_await_expr (tree await_expr, await_xform_data *xform)
>>    /* So, on entry, we have:
>>       in : CO_AWAIT_EXPR (a, e_proxy, o, awr_call_vector, mode)
>>  	  We no longer need a [it had diagnostic value, maybe?]
>> -	  We need to replace the promise proxy in all elements
>>  	  We need to replace the e_proxy in the awr_call.  */
>>      tree coro_frame_type = TREE_TYPE (xform->actor_frame);
>> @@ -1932,16 +1931,6 @@ transform_await_expr (tree await_expr, await_xform_data *xform)
>>        TREE_OPERAND (await_expr, 1) = as;
>>      }
>>  -  /* Now do the self_handle.  */
>> -  data.from = xform->self_h_proxy;
>> -  data.to = xform->real_self_h;
>> -  cp_walk_tree (&await_expr, replace_proxy, &data, NULL);
>> -
>> -  /* Now do the promise.  */
>> -  data.from = xform->promise_proxy;
>> -  data.to = xform->real_promise;
>> -  cp_walk_tree (&await_expr, replace_proxy, &data, NULL);
>> -
>>    return await_expr;
>>  }
>>  @@ -2128,10 +2117,9 @@ coro_get_frame_dtor (tree coro_fp, tree orig, tree frame_size,
>>    static void
>>  build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>> -		tree orig, hash_map<tree, param_info> *param_uses,
>> -		hash_map<tree, local_var_info> *local_var_uses,
>> -		vec<tree, va_gc> *param_dtor_list, tree resume_fn_field,
>> -		tree resume_idx_field, unsigned body_count, tree frame_size)
>> +		tree orig, hash_map<tree, local_var_info> *local_var_uses,
>> +		vec<tree, va_gc> *param_dtor_list,
>> +		tree resume_idx_var, unsigned body_count, tree frame_size)
>>  {
>>    verify_stmt_tree (fnbody);
>>    /* Some things we inherit from the original function.  */
>> @@ -2216,8 +2204,8 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>      = {actor, actor_frame, coro_frame_type, loc, local_var_uses};
>>    cp_walk_tree (&fnbody, transform_local_var_uses, &xform_vars_data, NULL);
>>  -  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field, 1, 0,
>> -				  tf_warning_or_error);
>> +  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field,
>> +				  1, 0, tf_warning_or_error);
>>    tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, actor_frame,
>>  		     rat_field, NULL_TREE);
>>  @@ -2319,14 +2307,8 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>    tree r = build_stmt (loc, LABEL_EXPR, actor_begin_label);
>>    add_stmt (r);
>>  -  /* actor's version of the promise.  */
>> -  tree ap_m = lookup_member (coro_frame_type, get_identifier ("_Coro_promise"), 1, 0,
>> -			     tf_warning_or_error);
>> -  tree ap = build_class_member_access_expr (actor_frame, ap_m, NULL_TREE, false,
>> -					    tf_warning_or_error);
>> -
>>    /* actor's coroutine 'self handle'.  */
>> -  tree ash_m = lookup_member (coro_frame_type, get_identifier ("_Coro_self_handle"), 1,
>> +  tree ash_m = lookup_member (coro_frame_type, coro_self_handle_field, 1,
>>  			      0, tf_warning_or_error);
>>    tree ash = build_class_member_access_expr (actor_frame, ash_m, NULL_TREE,
>>  					     false, tf_warning_or_error);
>> @@ -2347,36 +2329,13 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>       decide where to put things.  */
>>      await_xform_data xform
>> -    = {actor, actor_frame, promise_proxy, ap, self_h_proxy, ash};
>> +    = {actor, actor_frame, NULL_TREE, NULL_TREE, self_h_proxy, ash};
>>      /* Transform the await expressions in the function body.  Only do each
>>       await tree once!  */
>>    hash_set<tree> pset;
>>    cp_walk_tree (&fnbody, transform_await_wrapper, &xform, &pset);
>>  -  /* 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);
>> -
>> -  /* The rewrite of the function adds code to set the resume_fn field to
>> -     nullptr when the coroutine is done and also the index to zero when
>> -     calling an unhandled exception.  These are represented by two proxies
>> -     in the function, so rewrite them to the proper frame access.  */
>> -  tree resume_m
>> -    = lookup_member (coro_frame_type, get_identifier ("_Coro_resume_fn"),
>> -		     /*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);
>> -  p_data.from = resume_fn_field;
>> -  p_data.to = res_x;
>> -  cp_walk_tree (&fnbody, replace_proxy, &p_data, NULL);
>> -
>> -  p_data.from = resume_idx_field;
>> -  p_data.to = rat;
>> -  cp_walk_tree (&fnbody, replace_proxy, &p_data, NULL);
>> -
>>    /* Add in our function body with the co_returns rewritten to final form.  */
>>    add_stmt (fnbody);
>>  @@ -2385,7 +2344,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>    add_stmt (r);
>>      /* Destructors for the things we built explicitly.  */
>> -  r = build_special_member_call (ap, complete_dtor_identifier, NULL,
>> +  r = build_special_member_call (promise_proxy, complete_dtor_identifier, NULL,
>>  				 promise_type, LOOKUP_NORMAL,
>>  				 tf_warning_or_error);
>>    add_stmt (r);
>> @@ -2398,7 +2357,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>    /* Here deallocate the frame (if we allocated it), which we will have at
>>       present.  */
>>    tree fnf_m
>> -    = lookup_member (coro_frame_type, get_identifier ("_Coro_frame_needs_free"), 1,
>> +    = lookup_member (coro_frame_type, coro_frame_needs_free_field, 1,
>>  		     0, tf_warning_or_error);
>>    tree fnf2_x = build_class_member_access_expr (actor_frame, fnf_m, NULL_TREE,
>>  						false, tf_warning_or_error);
>> @@ -2477,18 +2436,10 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>    gcc_checking_assert (maybe_cleanup_point_expr_void (r) == r);
>>    add_stmt (r);
>>  -  /* We will need to know which resume point number should be encoded.  */
>> -  tree res_idx_m
>> -    = lookup_member (coro_frame_type, coro_resume_index_field,
>> -		     /*protect=*/1, /*want_type=*/0, tf_warning_or_error);
>> -  tree resume_pt_number
>> -    = build_class_member_access_expr (actor_frame, res_idx_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, NULL_TREE,
>> +  coro_aw_data data = {actor, actor_fp, resume_idx_var, NULL_TREE,
>>  		       ash, del_promise_label, ret_label,
>>  		       continue_label, continuation, 2};
>>    cp_walk_tree (&actor_body, await_statement_expander, &data, NULL);
>> @@ -2502,7 +2453,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>  }
>>    /* The prototype 'destroy' function :
>> -   frame->__resume_at |= 1;
>> +   frame->__Coro_resume_index |= 1;
>>     actor (frame);  */
>>    static void
>> @@ -2521,10 +2472,10 @@ build_destroy_fn (location_t loc, tree coro_frame_type, tree destroy,
>>      tree destr_frame = build1 (INDIRECT_REF, coro_frame_type, destr_fp);
>>  -  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field, 1, 0,
>> -				  tf_warning_or_error);
>> -  tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, destr_frame,
>> -		     rat_field, NULL_TREE);
>> +  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field,
>> +				  1, 0, tf_warning_or_error);
>> +  tree rat = build3 (COMPONENT_REF, short_unsigned_type_node,
>> +			 destr_frame, rat_field, NULL_TREE);
>>      /* _resume_at |= 1 */
>>    tree dstr_idx = build2 (BIT_IOR_EXPR, short_unsigned_type_node, rat,
>> @@ -4040,8 +3991,8 @@ coro_build_actor_or_destroy_function (tree orig, tree fn_type,
>>    static tree
>>  coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>> -			    tree resume_fn_ptr_type, tree& resume_fn_field,
>> -			    tree& resume_idx_field, tree& fs_label)
>> +			    tree resume_fn_ptr_type,
>> +			    tree& resume_idx_var, tree& fs_label)
>>  {
>>    /* This will be our new outer scope.  */
>>    tree update_body = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL);
>> @@ -4074,7 +4025,6 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>>      /* 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);
>>  @@ -4085,24 +4035,61 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>>    tree return_void
>>      = get_coroutine_return_void_expr (current_function_decl, fn_start, false);
>>  +  /* The pointer to the resume function.  */
>> +  tree resume_fn_ptr
>> +    = coro_build_artificial_var (fn_start, coro_resume_fn_field,
>> +				 resume_fn_ptr_type, orig, NULL_TREE);
>> +  DECL_CHAIN (resume_fn_ptr) = var_list;
>> +  var_list = resume_fn_ptr;
>> +  add_decl_expr (resume_fn_ptr);
>> +
>>    /* We will need to be able to set the resume function pointer to nullptr
>>       to signal that the coroutine is 'done'.  */
>> -  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);
>> -  /* Likewise, the resume index needs to be reset.  */
>> -  resume_idx_field
>> -    = build_lang_decl (VAR_DECL, get_identifier ("resume.index.proxy"),
>> -		       short_unsigned_type_node);
>> -  DECL_ARTIFICIAL (resume_idx_field) = true;
>> -  tree zero_resume_idx = build_int_cst (short_unsigned_type_node, 0);
>> -  zero_resume_idx = build2 (INIT_EXPR, short_unsigned_type_node,
>> -			    resume_idx_field, zero_resume_idx);
>> +
>> +  /* The pointer to the destroy function.  */
>> +  tree var = coro_build_artificial_var (fn_start, coro_destroy_fn_field,
>> +					resume_fn_ptr_type, orig, NULL_TREE);
>> +  DECL_CHAIN (var) = var_list;
>> +  var_list = var;
>> +  add_decl_expr (var);
>> +
>> +  /* The promise was created on demand when parsing we now link it into
>> +      our scope.  */
>> +  tree promise = get_coroutine_promise_proxy (orig);
>> +  DECL_CONTEXT (promise) = orig;
>> +  DECL_SOURCE_LOCATION (promise) = fn_start;
>> +  DECL_CHAIN (promise) = var_list;
>> +  var_list = promise;
>> +  add_decl_expr (promise);
>> +
>> +  /* We need a handle to this coroutine, which is passed to every
>> +     await_suspend().  This was created on demand when parsing we now link it
>> +     into our scope.  */
>> +  var = get_coroutine_self_handle_proxy (orig);
>> +  DECL_CONTEXT (var) = orig;
>> +  DECL_SOURCE_LOCATION (var) = fn_start;
>> +  DECL_CHAIN (var) = var_list;
>> +  var_list = var;
>> +  add_decl_expr (var);
>> +
>> +
>> +  /* We create a resume index, this is initialized in the ramp.  */
>> +  resume_idx_var
>> +    = coro_build_artificial_var (fn_start, coro_resume_index_field,
>> +				 short_unsigned_type_node, orig, NULL_TREE);
>> +  DECL_CHAIN (resume_idx_var) = var_list;
>> +  var_list = resume_idx_var;
>> +  add_decl_expr (resume_idx_var);
>> +
>> +  /* If the coroutine has a frame that needs to be freed, this will be set by
>> +     the ramp.  */
>> +  var = coro_build_artificial_var (fn_start, coro_frame_needs_free_field,
>> +				   boolean_type_node, orig, NULL_TREE);
>> +  DECL_CHAIN (var) = var_list;
>> +  var_list = var;
>> +  add_decl_expr (var);
>>      if (flag_exceptions)
>>      {
>> @@ -4166,10 +4153,14 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>>  	 If the unhandled exception method returns, then we continue
>>  	 to the final await expression (which duplicates the clearing of
>>  	 the field). */
>> -      finish_expr_stmt (zero_resume);
>> -      finish_expr_stmt (zero_resume_idx);
>> -      ueh = maybe_cleanup_point_expr_void (ueh);
>> -      add_stmt (ueh);
>> +      tree r = build2 (MODIFY_EXPR, resume_fn_ptr_type, resume_fn_ptr,
>> +		       zero_resume);
>> +      finish_expr_stmt (r);
>> +      tree short_zero = build_int_cst (short_unsigned_type_node, 0);
>> +      r = build2 (MODIFY_EXPR, short_unsigned_type_node, resume_idx_var,
>> +		  short_zero);
>> +      finish_expr_stmt (r);
>> +      finish_expr_stmt (ueh);
>>        finish_handler (handler);
>>        TRY_HANDLERS (tcb) = pop_stmt_list (TRY_HANDLERS (tcb));
>>      }
>> @@ -4204,6 +4195,8 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>>    /* 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.  */
>> +  zero_resume = build2 (MODIFY_EXPR, resume_fn_ptr_type, resume_fn_ptr,
>> +			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));
>> @@ -4348,33 +4341,16 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>>    /* Construct the wrapped function body; we will analyze this to determine
>>       the requirements for the coroutine frame.  */
>>  -  tree resume_fn_field = NULL_TREE;
>> -  tree resume_idx_field = NULL_TREE;
>> +  tree resume_idx_var = NULL_TREE;
>>    tree fs_label = NULL_TREE;
>>    fnbody = coro_rewrite_function_body (fn_start, fnbody, orig,
>> -				       act_des_fn_ptr, resume_fn_field,
>> -				       resume_idx_field, fs_label);
>> +				       act_des_fn_ptr,
>> +				       resume_idx_var, fs_label);
>>    /* Build our dummy coro frame layout.  */
>>    coro_frame_type = begin_class_definition (coro_frame_type);
>>  +  /* The fields for the coro frame.  */
>>    tree field_list = NULL_TREE;
>> -  tree resume_name
>> -    = coro_make_frame_entry (&field_list, "_Coro_resume_fn",
>> -			     act_des_fn_ptr, fn_start);
>> -  tree destroy_name
>> -    = coro_make_frame_entry (&field_list, "_Coro_destroy_fn",
>> -			     act_des_fn_ptr, fn_start);
>> -  tree promise_name
>> -    = coro_make_frame_entry (&field_list, "_Coro_promise", promise_type, fn_start);
>> -  tree fnf_name = coro_make_frame_entry (&field_list, "_Coro_frame_needs_free",
>> -					 boolean_type_node, fn_start);
>> -  tree resume_idx_name
>> -    = coro_make_frame_entry (&field_list, "_Coro_resume_index",
>> -			     short_unsigned_type_node, fn_start);
>> -
>> -  /* We need a handle to this coroutine, which is passed to every
>> -     await_suspend().  There's no point in creating it over and over.  */
>> -  (void) coro_make_frame_entry (&field_list, "_Coro_self_handle", handle_type, fn_start);
>>      /* Now add in fields for function params (if there are any).
>>       We do not attempt elision of copies at this stage, we do analyze the
>> @@ -4776,8 +4752,8 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>>      /* 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 (coro_frame_type, fnf_name, 1, 0, tf_warning_or_error);
>> +  tree fnf_m = lookup_member (coro_frame_type, coro_frame_needs_free_field,
>> +			      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 = build2 (INIT_EXPR, boolean_type_node, fnf_x, boolean_true_node);
>> @@ -4788,24 +4764,22 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>>      tree actor_addr = build1 (ADDR_EXPR, act_des_fn_ptr, actor);
>>    tree resume_m
>> -    = lookup_member (coro_frame_type, resume_name,
>> +    = lookup_member (coro_frame_type, coro_resume_fn_field,
>>  		     /*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 = build2_loc (fn_start, INIT_EXPR, act_des_fn_ptr, resume_x, actor_addr);
>> -  r = coro_build_cvt_void_expr_stmt (r, fn_start);
>> -  add_stmt (r);
>> +  finish_expr_stmt (r);
>>      tree destroy_addr = build1 (ADDR_EXPR, act_des_fn_ptr, destroy);
>>    tree destroy_m
>> -    = lookup_member (coro_frame_type, destroy_name,
>> +    = lookup_member (coro_frame_type, coro_destroy_fn_field,
>>  		     /*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 = build2_loc (fn_start, INIT_EXPR, act_des_fn_ptr, destroy_x, destroy_addr);
>> -  r = coro_build_cvt_void_expr_stmt (r, fn_start);
>> -  add_stmt (r);
>> +  finish_expr_stmt (r);
>>      /* [dcl.fct.def.coroutine] /13
>>       When a coroutine is invoked, a copy is created for each coroutine
>> @@ -4896,7 +4870,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>>      /* Set up the promise.  */
>>    tree promise_m
>> -    = lookup_member (coro_frame_type, promise_name,
>> +    = lookup_member (coro_frame_type, coro_promise_field,
>>  		     /*protect=*/1, /*want_type=*/0, tf_warning_or_error);
>>      tree p = build_class_member_access_expr (deref_fp, promise_m, NULL_TREE,
>> @@ -5042,9 +5016,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>>  			      boolean_type_node);
>>        finish_expr_stmt (r);
>>      }
>> -  /* Initialize the resume_idx_name to 0, meaning "not started".  */
>> +  /* Initialize the resume_idx_var to 0, meaning "not started".  */
>>    tree resume_idx_m
>> -    = lookup_member (coro_frame_type, resume_idx_name,
>> +    = lookup_member (coro_frame_type, coro_resume_index_field,
>>  		     /*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,
>> @@ -5187,9 +5161,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>>    push_deferring_access_checks (dk_no_check);
>>      /* Build the actor...  */
>> -  build_actor_fn (fn_start, coro_frame_type, actor, fnbody, orig, param_uses,
>> -		  &local_var_uses, param_dtor_list, resume_fn_field,
>> -		  resume_idx_field, body_aw_points.await_number, frame_size);
>> +  build_actor_fn (fn_start, coro_frame_type, actor, fnbody, orig,
>> +		  &local_var_uses, param_dtor_list,
>> +		  resume_idx_var, body_aw_points.await_number, frame_size);
>>      /* Destroyer ... */
>>    build_destroy_fn (fn_start, coro_frame_type, destroy, actor);
> 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 7/8] coroutines: Make proxy vars for the function arg copies.
  2021-09-01 10:56             ` [PATCH 7/8] coroutines: Make proxy vars for the function arg copies Iain Sandoe
  2021-09-01 10:56               ` [PATCH 8/8] coroutines: Make the continue handle visible to debug Iain Sandoe
@ 2021-09-03 14:07               ` Jason Merrill
  2021-09-03 14:23                 ` Iain Sandoe
  1 sibling, 1 reply; 28+ messages in thread
From: Jason Merrill @ 2021-09-03 14:07 UTC (permalink / raw)
  To: Iain Sandoe, GCC Patches

On 9/1/21 6:56 AM, Iain Sandoe wrote:
> 
> This adds top level proxy variables for the coroutine frame
> copies of the original function args.  These are then available
> in the debugger to refer to the frame copies.  We rewrite the
> function body to use the copies, since the original parms will
> no longer be in scope when the coroutine is running.
> 
> Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
> 
> gcc/cp/ChangeLog:
> 
> 	* coroutines.cc (struct param_info): Add copy_var.
> 	(build_actor_fn): Use simplified param references.
> 	(register_param_uses): Likewise.
> 	(rewrite_param_uses): Likewise.
> 	(analyze_fn_parms): New function.
> 	(coro_rewrite_function_body): Add proxies for the fn
> 	parameters to the outer bind scope of the rewritten code.
> 	(morph_fn_to_coro): Use simplified version of param ref.
> ---
>   gcc/cp/coroutines.cc | 247 ++++++++++++++++++++-----------------------
>   1 file changed, 117 insertions(+), 130 deletions(-)
> 
> diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
> index aacf352f1c9..395e5c488e5 100644
> --- a/gcc/cp/coroutines.cc
> +++ b/gcc/cp/coroutines.cc
> @@ -1964,6 +1964,7 @@ transform_await_wrapper (tree *stmt, int *do_subtree, void *d)
>   struct param_info
>   {
>     tree field_id;     /* The name of the copy in the coroutine frame.  */
> +  tree copy_var;     /* The local var proxy for the frame copy.  */
>     vec<tree *> *body_uses; /* Worklist of uses, void if there are none.  */
>     tree frame_type;   /* The type used to represent this parm in the frame.  */
>     tree orig_type;    /* The original type of the parm (not as passed).  */
> @@ -2169,36 +2170,6 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>     /* Declare the continuation handle.  */
>     add_decl_expr (continuation);
>   
> -  /* Re-write param references in the body, no code should be generated
> -     here.  */
> -  if (DECL_ARGUMENTS (orig))
> -    {
> -      tree arg;
> -      for (arg = DECL_ARGUMENTS (orig); arg != NULL; arg = DECL_CHAIN (arg))
> -	{
> -	  bool existed;
> -	  param_info &parm = param_uses->get_or_insert (arg, &existed);
> -	  if (!parm.body_uses)
> -	    continue; /* Wasn't used in the original function body.  */
> -
> -	  tree fld_ref = lookup_member (coro_frame_type, parm.field_id,
> -					/*protect=*/1, /*want_type=*/0,
> -					tf_warning_or_error);
> -	  tree fld_idx = build3_loc (loc, COMPONENT_REF, parm.frame_type,
> -				     actor_frame, fld_ref, NULL_TREE);
> -
> -	  /* We keep these in the frame as a regular pointer, so convert that
> -	   back to the type expected.  */
> -	  if (parm.pt_ref)
> -	    fld_idx = build1_loc (loc, CONVERT_EXPR, TREE_TYPE (arg), fld_idx);
> -
> -	  int i;
> -	  tree *puse;
> -	  FOR_EACH_VEC_ELT (*parm.body_uses, i, puse)
> -	    *puse = fld_idx;
> -	}
> -    }
> -
>     /* Re-write local vars, similarly.  */
>     local_vars_transform xform_vars_data
>       = {actor, actor_frame, coro_frame_type, loc, local_var_uses};
> @@ -3772,11 +3743,11 @@ struct param_frame_data
>     bool param_seen;
>   };
>   
> -/* A tree-walk callback that records the use of parameters (to allow for
> -   optimizations where handling unused parameters may be omitted).  */
> +/* A tree walk callback that rewrites each parm use to the local variable
> +   that represents its copy in the frame.  */
>   
>   static tree
> -register_param_uses (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
> +rewrite_param_uses (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
>   {
>     param_frame_data *data = (param_frame_data *) d;
>   
> @@ -3784,7 +3755,7 @@ register_param_uses (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
>     if (TREE_CODE (*stmt) == VAR_DECL && DECL_HAS_VALUE_EXPR_P (*stmt))
>       {
>         tree t = DECL_VALUE_EXPR (*stmt);
> -      return cp_walk_tree (&t, register_param_uses, d, NULL);
> +      return cp_walk_tree (&t, rewrite_param_uses, d, NULL);
>       }
>   
>     if (TREE_CODE (*stmt) != PARM_DECL)
> @@ -3798,16 +3769,87 @@ register_param_uses (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
>     param_info &parm = data->param_uses->get_or_insert (*stmt, &existed);
>     gcc_checking_assert (existed);
>   
> -  if (!parm.body_uses)
> +  *stmt = parm.copy_var;
> +  return NULL_TREE;
> +}
> +
> +/* Build up a set of info that determines how each param copy will be
> +   handled.  */
> +
> +static hash_map<tree, param_info> *analyze_fn_parms (tree orig)

Function name should be on a new line.

> +{
> +  if (!DECL_ARGUMENTS (orig))
> +    return NULL;
> +
> +  hash_map<tree, param_info> *param_uses = new hash_map<tree, param_info>;
> +
> +  /* Build a hash map with an entry for each param.
> +     The key is the param tree.
> +     Then we have an entry for the frame field name.
> +     Then a cache for the field ref when we come to use it.
> +     Then a tree list of the uses.
> +     The second two entries start out empty - and only get populated
> +     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))
>       {
> -      vec_alloc (parm.body_uses, 4);
> -      parm.body_uses->quick_push (stmt);
> -      data->param_seen = true;
> +      bool existed;
> +      param_info &parm = param_uses->get_or_insert (arg, &existed);
> +      gcc_checking_assert (!existed);
> +      parm.body_uses = NULL;
> +      tree actual_type = TREE_TYPE (arg);
> +      actual_type = complete_type_or_else (actual_type, orig);
> +      if (actual_type == NULL_TREE)
> +	actual_type = error_mark_node;
> +      parm.orig_type = actual_type;
> +      parm.by_ref = parm.pt_ref = parm.rv_ref =  false;
> +      if (TREE_CODE (actual_type) == REFERENCE_TYPE)
> +	{
> +	  /* If the user passes by reference, then we will save the
> +	     pointer to the original.  As noted in
> +	     [dcl.fct.def.coroutine] / 13, if the lifetime of the
> +	     referenced item ends and then the coroutine is resumed,
> +	     we have UB; well, the user asked for it.  */
> +	  if (TYPE_REF_IS_RVALUE (actual_type))
> +		parm.rv_ref = true;
> +	  else
> +		parm.pt_ref = true;
> +	}
> +      else if (TYPE_REF_P (DECL_ARG_TYPE (arg)))
> +	parm.by_ref = true;
> +
> +      parm.frame_type = actual_type;
> +
> +      parm.this_ptr = is_this_parameter (arg);
> +      parm.lambda_cobj = lambda_p && DECL_NAME (arg) == closure_identifier;
> +
> +      tree name = DECL_NAME (arg);
> +      if (!name)
> +	{
> +	  char *buf = xasprintf ("_Coro_unnamed_parm_%d", no_name_parm++);
> +	  name = get_identifier (buf);
> +	  free (buf);
> +	}
> +      parm.field_id = name;
> +
> +      if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (parm.frame_type))
> +	{
> +	  char *buf = xasprintf ("_Coro_%s_live", IDENTIFIER_POINTER (name));
> +	  parm.guard_var = build_lang_decl (VAR_DECL, get_identifier (buf),
> +					    boolean_type_node);
> +	  free (buf);
> +	  DECL_ARTIFICIAL (parm.guard_var) = true;
> +	  DECL_CONTEXT (parm.guard_var) = orig;
> +	  DECL_INITIAL (parm.guard_var) = boolean_false_node;
> +	  parm.trivial_dtor = false;
> +	}
> +      else
> +	parm.trivial_dtor = true;
>       }
> -  else
> -    parm.body_uses->safe_push (stmt);
>   
> -  return NULL_TREE;
> +  return param_uses;
>   }
>   
>   /* Small helper for the repetitive task of adding a new field to the coro
> @@ -3991,6 +4033,7 @@ coro_build_actor_or_destroy_function (tree orig, tree fn_type,
>   
>   static tree
>   coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
> +			    hash_map<tree, param_info> *param_uses,
>   			    tree resume_fn_ptr_type,
>   			    tree& resume_idx_var, tree& fs_label)
>   {
> @@ -4074,6 +4117,36 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>     var_list = var;
>     add_decl_expr (var);
>   
> +  /* If we have function parms, then these will be copied to the coroutine
> +     frame.  Create a local variable to point to each of them so that we can
> +     see them in the debugger.  */
> +
> +  if (param_uses)
> +    {
> +      gcc_checking_assert (DECL_ARGUMENTS (orig));
> +      /* Add a local var for each parm.  */
> +      for (tree arg = DECL_ARGUMENTS (orig); arg != NULL;
> +	   arg = DECL_CHAIN (arg))
> +	{
> +	  param_info *parm_i = param_uses->get (arg);
> +	  gcc_checking_assert (parm_i);
> +	  parm_i->copy_var
> +	    = build_lang_decl (VAR_DECL, parm_i->field_id, TREE_TYPE (arg));
> +	  DECL_SOURCE_LOCATION (parm_i->copy_var) = DECL_SOURCE_LOCATION (arg);
> +	  DECL_CONTEXT (parm_i->copy_var) = orig;
> +	  DECL_ARTIFICIAL (parm_i->copy_var) = true;
> +	  DECL_CHAIN (parm_i->copy_var) = var_list;
> +	  var_list = parm_i->copy_var;
> +	  add_decl_expr (parm_i->copy_var);

Are these getting DECL_VALUE_EXPR somewhere?

> +      	}
> +
> +      /* Now replace all uses of the parms in the function body with the local
> +	 vars.  */

I think the following old comment still applies to how 'visited' is 
used, and should be adapted here as well:

>> -      /* We want to record every instance of param's use, so don't include
>> -	 a 'visited' hash_set on the tree walk, but only record a containing
>> -	 expression once.  */

> +      hash_set<tree *> visited;
> +      param_frame_data param_data = {NULL, param_uses,
> +				     &visited, fn_start, false};
> +      cp_walk_tree (&fnbody, rewrite_param_uses, &param_data, NULL);
> +    }
>   
>     /* We create a resume index, this is initialized in the ramp.  */
>     resume_idx_var
> @@ -4343,7 +4416,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>   
>     tree resume_idx_var = NULL_TREE;
>     tree fs_label = NULL_TREE;
> -  fnbody = coro_rewrite_function_body (fn_start, fnbody, orig,
> +  hash_map<tree, param_info> *param_uses = analyze_fn_parms (orig);
> +
> +  fnbody = coro_rewrite_function_body (fn_start, fnbody, orig, param_uses,
>   				       act_des_fn_ptr,
>   				       resume_idx_var, fs_label);
>     /* Build our dummy coro frame layout.  */
> @@ -4352,94 +4427,6 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>     /* The fields for the coro frame.  */
>     tree field_list = NULL_TREE;
>   
> -  /* Now add in fields for function params (if there are any).
> -     We do not attempt elision of copies at this stage, we do analyze the
> -     uses and build worklists to replace those when the state machine is
> -     lowered.  */
> -
> -  hash_map<tree, param_info> *param_uses = NULL;
> -  if (DECL_ARGUMENTS (orig))
> -    {
> -      /* Build a hash map with an entry for each param.
> -	  The key is the param tree.
> -	  Then we have an entry for the frame field name.
> -	  Then a cache for the field ref when we come to use it.
> -	  Then a tree list of the uses.
> -	  The second two entries start out empty - and only get populated
> -	  when we see uses.  */
> -      param_uses = new hash_map<tree, param_info>;
> -      bool lambda_p = LAMBDA_FUNCTION_P (orig);
> -
> -      unsigned no_name_parm = 0;
> -      for (tree arg = DECL_ARGUMENTS (orig); arg != NULL;
> -	   arg = DECL_CHAIN (arg))
> -	{
> -	  bool existed;
> -	  param_info &parm = param_uses->get_or_insert (arg, &existed);
> -	  gcc_checking_assert (!existed);
> -	  parm.body_uses = NULL;
> -	  tree actual_type = TREE_TYPE (arg);
> -	  actual_type = complete_type_or_else (actual_type, orig);
> -	  if (actual_type == NULL_TREE)
> -	    actual_type = error_mark_node;
> -	  parm.orig_type = actual_type;
> -	  parm.by_ref = parm.pt_ref = parm.rv_ref =  false;
> -	  if (TREE_CODE (actual_type) == REFERENCE_TYPE)
> -	    {
> -	      /* If the user passes by reference, then we will save the
> -		 pointer to the original.  As noted in
> -		 [dcl.fct.def.coroutine] / 13, if the lifetime of the
> -		 referenced item ends and then the coroutine is resumed,
> -		 we have UB; well, the user asked for it.  */
> -	      if (TYPE_REF_IS_RVALUE (actual_type))
> -		parm.rv_ref = true;
> -	      else
> -		parm.pt_ref = true;
> -	    }
> -	  else if (TYPE_REF_P (DECL_ARG_TYPE (arg)))
> -	    parm.by_ref = true;
> -
> -	  parm.frame_type = actual_type;
> -
> -	  parm.this_ptr = is_this_parameter (arg);
> -	  parm.lambda_cobj = lambda_p && DECL_NAME (arg) == closure_identifier;
> -
> -	  char *buf;
> -	  if (DECL_NAME (arg))
> -	    {
> -	      tree pname = DECL_NAME (arg);
> -	      buf = xasprintf ("_P_%s", IDENTIFIER_POINTER (pname));
> -	    }
> -	  else
> -	    buf = xasprintf ("_P_unnamed_%d", no_name_parm++);
> -
> -	  if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (parm.frame_type))
> -	    {
> -	      char *gbuf = xasprintf ("%s_live", buf);
> -	      parm.guard_var
> -		= build_lang_decl (VAR_DECL, get_identifier (gbuf),
> -				   boolean_type_node);
> -	      free (gbuf);
> -	      DECL_ARTIFICIAL (parm.guard_var) = true;
> -	      DECL_INITIAL (parm.guard_var) = boolean_false_node;
> -	      parm.trivial_dtor = false;
> -	    }
> -	  else
> -	    parm.trivial_dtor = true;
> -	  parm.field_id = coro_make_frame_entry
> -	    (&field_list, buf, actual_type, DECL_SOURCE_LOCATION (arg));
> -	  free (buf);
> -	}
> -
> -      /* We want to record every instance of param's use, so don't include
> -	 a 'visited' hash_set on the tree walk, but only record a containing
> -	 expression once.  */
> -      hash_set<tree *> visited;
> -      param_frame_data param_data
> -	= {&field_list, param_uses, &visited, fn_start, false};
> -      cp_walk_tree (&fnbody, register_param_uses, &param_data, NULL);
> -    }
> -
>     /* 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.  */
> 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 6/8] coroutines: Convert implementation variables to debug-friendly form.
  2021-09-03 13:56               ` Iain Sandoe
@ 2021-09-03 14:12                 ` Jason Merrill
  2021-09-03 14:21                   ` Iain Sandoe
  0 siblings, 1 reply; 28+ messages in thread
From: Jason Merrill @ 2021-09-03 14:12 UTC (permalink / raw)
  To: Iain Sandoe; +Cc: GCC Patches

On 9/3/21 9:56 AM, Iain Sandoe wrote:
> 
> 
>> On 3 Sep 2021, at 14:52, Jason Merrill <jason@redhat.com> wrote:
>>
>> On 9/1/21 6:55 AM, Iain Sandoe wrote:
>>> The user might well wish to inspect some of the state that represents
>>> the implementation of the coroutine machine.
>>> In particular:
>>>    The promise object.
>>>    The function pointers for the resumer and destroyer.
>>>    The current resume index (suspend point).
>>>    The handle that represent this coroutine 'self handle'.
>>>    Whether the coroutine frame is allocated and needs to be freed.
>>> These variables are given names that can be 'well-known' and advertised
>>> in debug documentation - they are placed in the implementation namespace
>>> and all begin with _Coro_.
>>
>>> Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
>>> gcc/cp/ChangeLog:
>>> 	* coroutines.cc (transform_await_expr): Use debug-friendly
>>> 	names for coroutine implementation.
>>> 	(build_actor_fn): Likewise.
>>> 	(build_destroy_fn): Likewise.
>>> 	(coro_rewrite_function_body): Likewise.
>>> 	(morph_fn_to_coro): Likewise.
>>
>> Hmm, this patch doesn't seem to match the description and ChangeLog entry other than in the names of the functions changed.
> 
> with 20:20 hindsight I should have squashed the (several) patches related to the implementation symbols,
> 
> I’ll redo the description - essentially, this is just making use of the simplification available because we now have pre-defined values for the field names.

I can see how that describes a few lines in this patch, but not for 
instance the change to transform_await_expr, which seems to have nothing 
to do with names?

But yes, moving the changed lines that just use the variables from the 
previous patch into that commit sounds good.  I use rebase -i for that 
sort of thing all the time.

>>> ---
>>>   gcc/cp/coroutines.cc | 214 +++++++++++++++++++------------------------
>>>   1 file changed, 94 insertions(+), 120 deletions(-)
>>> diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
>>> index 3b46aac4dc5..aacf352f1c9 100644
>>> --- a/gcc/cp/coroutines.cc
>>> +++ b/gcc/cp/coroutines.cc
>>> @@ -1906,7 +1906,6 @@ transform_await_expr (tree await_expr, await_xform_data *xform)
>>>     /* So, on entry, we have:
>>>        in : CO_AWAIT_EXPR (a, e_proxy, o, awr_call_vector, mode)
>>>   	  We no longer need a [it had diagnostic value, maybe?]
>>> -	  We need to replace the promise proxy in all elements
>>>   	  We need to replace the e_proxy in the awr_call.  */
>>>       tree coro_frame_type = TREE_TYPE (xform->actor_frame);
>>> @@ -1932,16 +1931,6 @@ transform_await_expr (tree await_expr, await_xform_data *xform)
>>>         TREE_OPERAND (await_expr, 1) = as;
>>>       }
>>>   -  /* Now do the self_handle.  */
>>> -  data.from = xform->self_h_proxy;
>>> -  data.to = xform->real_self_h;
>>> -  cp_walk_tree (&await_expr, replace_proxy, &data, NULL);
>>> -
>>> -  /* Now do the promise.  */
>>> -  data.from = xform->promise_proxy;
>>> -  data.to = xform->real_promise;
>>> -  cp_walk_tree (&await_expr, replace_proxy, &data, NULL);
>>> -
>>>     return await_expr;
>>>   }
>>>   @@ -2128,10 +2117,9 @@ coro_get_frame_dtor (tree coro_fp, tree orig, tree frame_size,
>>>     static void
>>>   build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>> -		tree orig, hash_map<tree, param_info> *param_uses,
>>> -		hash_map<tree, local_var_info> *local_var_uses,
>>> -		vec<tree, va_gc> *param_dtor_list, tree resume_fn_field,
>>> -		tree resume_idx_field, unsigned body_count, tree frame_size)
>>> +		tree orig, hash_map<tree, local_var_info> *local_var_uses,
>>> +		vec<tree, va_gc> *param_dtor_list,
>>> +		tree resume_idx_var, unsigned body_count, tree frame_size)
>>>   {
>>>     verify_stmt_tree (fnbody);
>>>     /* Some things we inherit from the original function.  */
>>> @@ -2216,8 +2204,8 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>>       = {actor, actor_frame, coro_frame_type, loc, local_var_uses};
>>>     cp_walk_tree (&fnbody, transform_local_var_uses, &xform_vars_data, NULL);
>>>   -  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field, 1, 0,
>>> -				  tf_warning_or_error);
>>> +  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field,
>>> +				  1, 0, tf_warning_or_error);
>>>     tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, actor_frame,
>>>   		     rat_field, NULL_TREE);
>>>   @@ -2319,14 +2307,8 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>>     tree r = build_stmt (loc, LABEL_EXPR, actor_begin_label);
>>>     add_stmt (r);
>>>   -  /* actor's version of the promise.  */
>>> -  tree ap_m = lookup_member (coro_frame_type, get_identifier ("_Coro_promise"), 1, 0,
>>> -			     tf_warning_or_error);
>>> -  tree ap = build_class_member_access_expr (actor_frame, ap_m, NULL_TREE, false,
>>> -					    tf_warning_or_error);
>>> -
>>>     /* actor's coroutine 'self handle'.  */
>>> -  tree ash_m = lookup_member (coro_frame_type, get_identifier ("_Coro_self_handle"), 1,
>>> +  tree ash_m = lookup_member (coro_frame_type, coro_self_handle_field, 1,
>>>   			      0, tf_warning_or_error);
>>>     tree ash = build_class_member_access_expr (actor_frame, ash_m, NULL_TREE,
>>>   					     false, tf_warning_or_error);
>>> @@ -2347,36 +2329,13 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>>        decide where to put things.  */
>>>       await_xform_data xform
>>> -    = {actor, actor_frame, promise_proxy, ap, self_h_proxy, ash};
>>> +    = {actor, actor_frame, NULL_TREE, NULL_TREE, self_h_proxy, ash};
>>>       /* Transform the await expressions in the function body.  Only do each
>>>        await tree once!  */
>>>     hash_set<tree> pset;
>>>     cp_walk_tree (&fnbody, transform_await_wrapper, &xform, &pset);
>>>   -  /* 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);
>>> -
>>> -  /* The rewrite of the function adds code to set the resume_fn field to
>>> -     nullptr when the coroutine is done and also the index to zero when
>>> -     calling an unhandled exception.  These are represented by two proxies
>>> -     in the function, so rewrite them to the proper frame access.  */
>>> -  tree resume_m
>>> -    = lookup_member (coro_frame_type, get_identifier ("_Coro_resume_fn"),
>>> -		     /*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);
>>> -  p_data.from = resume_fn_field;
>>> -  p_data.to = res_x;
>>> -  cp_walk_tree (&fnbody, replace_proxy, &p_data, NULL);
>>> -
>>> -  p_data.from = resume_idx_field;
>>> -  p_data.to = rat;
>>> -  cp_walk_tree (&fnbody, replace_proxy, &p_data, NULL);
>>> -
>>>     /* Add in our function body with the co_returns rewritten to final form.  */
>>>     add_stmt (fnbody);
>>>   @@ -2385,7 +2344,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>>     add_stmt (r);
>>>       /* Destructors for the things we built explicitly.  */
>>> -  r = build_special_member_call (ap, complete_dtor_identifier, NULL,
>>> +  r = build_special_member_call (promise_proxy, complete_dtor_identifier, NULL,
>>>   				 promise_type, LOOKUP_NORMAL,
>>>   				 tf_warning_or_error);
>>>     add_stmt (r);
>>> @@ -2398,7 +2357,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>>     /* Here deallocate the frame (if we allocated it), which we will have at
>>>        present.  */
>>>     tree fnf_m
>>> -    = lookup_member (coro_frame_type, get_identifier ("_Coro_frame_needs_free"), 1,
>>> +    = lookup_member (coro_frame_type, coro_frame_needs_free_field, 1,
>>>   		     0, tf_warning_or_error);
>>>     tree fnf2_x = build_class_member_access_expr (actor_frame, fnf_m, NULL_TREE,
>>>   						false, tf_warning_or_error);
>>> @@ -2477,18 +2436,10 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>>     gcc_checking_assert (maybe_cleanup_point_expr_void (r) == r);
>>>     add_stmt (r);
>>>   -  /* We will need to know which resume point number should be encoded.  */
>>> -  tree res_idx_m
>>> -    = lookup_member (coro_frame_type, coro_resume_index_field,
>>> -		     /*protect=*/1, /*want_type=*/0, tf_warning_or_error);
>>> -  tree resume_pt_number
>>> -    = build_class_member_access_expr (actor_frame, res_idx_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, NULL_TREE,
>>> +  coro_aw_data data = {actor, actor_fp, resume_idx_var, NULL_TREE,
>>>   		       ash, del_promise_label, ret_label,
>>>   		       continue_label, continuation, 2};
>>>     cp_walk_tree (&actor_body, await_statement_expander, &data, NULL);
>>> @@ -2502,7 +2453,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>>   }
>>>     /* The prototype 'destroy' function :
>>> -   frame->__resume_at |= 1;
>>> +   frame->__Coro_resume_index |= 1;
>>>      actor (frame);  */
>>>     static void
>>> @@ -2521,10 +2472,10 @@ build_destroy_fn (location_t loc, tree coro_frame_type, tree destroy,
>>>       tree destr_frame = build1 (INDIRECT_REF, coro_frame_type, destr_fp);
>>>   -  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field, 1, 0,
>>> -				  tf_warning_or_error);
>>> -  tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, destr_frame,
>>> -		     rat_field, NULL_TREE);
>>> +  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field,
>>> +				  1, 0, tf_warning_or_error);
>>> +  tree rat = build3 (COMPONENT_REF, short_unsigned_type_node,
>>> +			 destr_frame, rat_field, NULL_TREE);
>>>       /* _resume_at |= 1 */
>>>     tree dstr_idx = build2 (BIT_IOR_EXPR, short_unsigned_type_node, rat,
>>> @@ -4040,8 +3991,8 @@ coro_build_actor_or_destroy_function (tree orig, tree fn_type,
>>>     static tree
>>>   coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>>> -			    tree resume_fn_ptr_type, tree& resume_fn_field,
>>> -			    tree& resume_idx_field, tree& fs_label)
>>> +			    tree resume_fn_ptr_type,
>>> +			    tree& resume_idx_var, tree& fs_label)
>>>   {
>>>     /* This will be our new outer scope.  */
>>>     tree update_body = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL);
>>> @@ -4074,7 +4025,6 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>>>       /* 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);
>>>   @@ -4085,24 +4035,61 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>>>     tree return_void
>>>       = get_coroutine_return_void_expr (current_function_decl, fn_start, false);
>>>   +  /* The pointer to the resume function.  */
>>> +  tree resume_fn_ptr
>>> +    = coro_build_artificial_var (fn_start, coro_resume_fn_field,
>>> +				 resume_fn_ptr_type, orig, NULL_TREE);
>>> +  DECL_CHAIN (resume_fn_ptr) = var_list;
>>> +  var_list = resume_fn_ptr;
>>> +  add_decl_expr (resume_fn_ptr);
>>> +
>>>     /* We will need to be able to set the resume function pointer to nullptr
>>>        to signal that the coroutine is 'done'.  */
>>> -  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);
>>> -  /* Likewise, the resume index needs to be reset.  */
>>> -  resume_idx_field
>>> -    = build_lang_decl (VAR_DECL, get_identifier ("resume.index.proxy"),
>>> -		       short_unsigned_type_node);
>>> -  DECL_ARTIFICIAL (resume_idx_field) = true;
>>> -  tree zero_resume_idx = build_int_cst (short_unsigned_type_node, 0);
>>> -  zero_resume_idx = build2 (INIT_EXPR, short_unsigned_type_node,
>>> -			    resume_idx_field, zero_resume_idx);
>>> +
>>> +  /* The pointer to the destroy function.  */
>>> +  tree var = coro_build_artificial_var (fn_start, coro_destroy_fn_field,
>>> +					resume_fn_ptr_type, orig, NULL_TREE);
>>> +  DECL_CHAIN (var) = var_list;
>>> +  var_list = var;
>>> +  add_decl_expr (var);
>>> +
>>> +  /* The promise was created on demand when parsing we now link it into
>>> +      our scope.  */
>>> +  tree promise = get_coroutine_promise_proxy (orig);
>>> +  DECL_CONTEXT (promise) = orig;
>>> +  DECL_SOURCE_LOCATION (promise) = fn_start;
>>> +  DECL_CHAIN (promise) = var_list;
>>> +  var_list = promise;
>>> +  add_decl_expr (promise);
>>> +
>>> +  /* We need a handle to this coroutine, which is passed to every
>>> +     await_suspend().  This was created on demand when parsing we now link it
>>> +     into our scope.  */
>>> +  var = get_coroutine_self_handle_proxy (orig);
>>> +  DECL_CONTEXT (var) = orig;
>>> +  DECL_SOURCE_LOCATION (var) = fn_start;
>>> +  DECL_CHAIN (var) = var_list;
>>> +  var_list = var;
>>> +  add_decl_expr (var);
>>> +
>>> +
>>> +  /* We create a resume index, this is initialized in the ramp.  */
>>> +  resume_idx_var
>>> +    = coro_build_artificial_var (fn_start, coro_resume_index_field,
>>> +				 short_unsigned_type_node, orig, NULL_TREE);
>>> +  DECL_CHAIN (resume_idx_var) = var_list;
>>> +  var_list = resume_idx_var;
>>> +  add_decl_expr (resume_idx_var);
>>> +
>>> +  /* If the coroutine has a frame that needs to be freed, this will be set by
>>> +     the ramp.  */
>>> +  var = coro_build_artificial_var (fn_start, coro_frame_needs_free_field,
>>> +				   boolean_type_node, orig, NULL_TREE);
>>> +  DECL_CHAIN (var) = var_list;
>>> +  var_list = var;
>>> +  add_decl_expr (var);
>>>       if (flag_exceptions)
>>>       {
>>> @@ -4166,10 +4153,14 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>>>   	 If the unhandled exception method returns, then we continue
>>>   	 to the final await expression (which duplicates the clearing of
>>>   	 the field). */
>>> -      finish_expr_stmt (zero_resume);
>>> -      finish_expr_stmt (zero_resume_idx);
>>> -      ueh = maybe_cleanup_point_expr_void (ueh);
>>> -      add_stmt (ueh);
>>> +      tree r = build2 (MODIFY_EXPR, resume_fn_ptr_type, resume_fn_ptr,
>>> +		       zero_resume);
>>> +      finish_expr_stmt (r);
>>> +      tree short_zero = build_int_cst (short_unsigned_type_node, 0);
>>> +      r = build2 (MODIFY_EXPR, short_unsigned_type_node, resume_idx_var,
>>> +		  short_zero);
>>> +      finish_expr_stmt (r);
>>> +      finish_expr_stmt (ueh);
>>>         finish_handler (handler);
>>>         TRY_HANDLERS (tcb) = pop_stmt_list (TRY_HANDLERS (tcb));
>>>       }
>>> @@ -4204,6 +4195,8 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>>>     /* 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.  */
>>> +  zero_resume = build2 (MODIFY_EXPR, resume_fn_ptr_type, resume_fn_ptr,
>>> +			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));
>>> @@ -4348,33 +4341,16 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>>>     /* Construct the wrapped function body; we will analyze this to determine
>>>        the requirements for the coroutine frame.  */
>>>   -  tree resume_fn_field = NULL_TREE;
>>> -  tree resume_idx_field = NULL_TREE;
>>> +  tree resume_idx_var = NULL_TREE;
>>>     tree fs_label = NULL_TREE;
>>>     fnbody = coro_rewrite_function_body (fn_start, fnbody, orig,
>>> -				       act_des_fn_ptr, resume_fn_field,
>>> -				       resume_idx_field, fs_label);
>>> +				       act_des_fn_ptr,
>>> +				       resume_idx_var, fs_label);
>>>     /* Build our dummy coro frame layout.  */
>>>     coro_frame_type = begin_class_definition (coro_frame_type);
>>>   +  /* The fields for the coro frame.  */
>>>     tree field_list = NULL_TREE;
>>> -  tree resume_name
>>> -    = coro_make_frame_entry (&field_list, "_Coro_resume_fn",
>>> -			     act_des_fn_ptr, fn_start);
>>> -  tree destroy_name
>>> -    = coro_make_frame_entry (&field_list, "_Coro_destroy_fn",
>>> -			     act_des_fn_ptr, fn_start);
>>> -  tree promise_name
>>> -    = coro_make_frame_entry (&field_list, "_Coro_promise", promise_type, fn_start);
>>> -  tree fnf_name = coro_make_frame_entry (&field_list, "_Coro_frame_needs_free",
>>> -					 boolean_type_node, fn_start);
>>> -  tree resume_idx_name
>>> -    = coro_make_frame_entry (&field_list, "_Coro_resume_index",
>>> -			     short_unsigned_type_node, fn_start);
>>> -
>>> -  /* We need a handle to this coroutine, which is passed to every
>>> -     await_suspend().  There's no point in creating it over and over.  */
>>> -  (void) coro_make_frame_entry (&field_list, "_Coro_self_handle", handle_type, fn_start);
>>>       /* Now add in fields for function params (if there are any).
>>>        We do not attempt elision of copies at this stage, we do analyze the
>>> @@ -4776,8 +4752,8 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>>>       /* 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 (coro_frame_type, fnf_name, 1, 0, tf_warning_or_error);
>>> +  tree fnf_m = lookup_member (coro_frame_type, coro_frame_needs_free_field,
>>> +			      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 = build2 (INIT_EXPR, boolean_type_node, fnf_x, boolean_true_node);
>>> @@ -4788,24 +4764,22 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>>>       tree actor_addr = build1 (ADDR_EXPR, act_des_fn_ptr, actor);
>>>     tree resume_m
>>> -    = lookup_member (coro_frame_type, resume_name,
>>> +    = lookup_member (coro_frame_type, coro_resume_fn_field,
>>>   		     /*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 = build2_loc (fn_start, INIT_EXPR, act_des_fn_ptr, resume_x, actor_addr);
>>> -  r = coro_build_cvt_void_expr_stmt (r, fn_start);
>>> -  add_stmt (r);
>>> +  finish_expr_stmt (r);
>>>       tree destroy_addr = build1 (ADDR_EXPR, act_des_fn_ptr, destroy);
>>>     tree destroy_m
>>> -    = lookup_member (coro_frame_type, destroy_name,
>>> +    = lookup_member (coro_frame_type, coro_destroy_fn_field,
>>>   		     /*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 = build2_loc (fn_start, INIT_EXPR, act_des_fn_ptr, destroy_x, destroy_addr);
>>> -  r = coro_build_cvt_void_expr_stmt (r, fn_start);
>>> -  add_stmt (r);
>>> +  finish_expr_stmt (r);
>>>       /* [dcl.fct.def.coroutine] /13
>>>        When a coroutine is invoked, a copy is created for each coroutine
>>> @@ -4896,7 +4870,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>>>       /* Set up the promise.  */
>>>     tree promise_m
>>> -    = lookup_member (coro_frame_type, promise_name,
>>> +    = lookup_member (coro_frame_type, coro_promise_field,
>>>   		     /*protect=*/1, /*want_type=*/0, tf_warning_or_error);
>>>       tree p = build_class_member_access_expr (deref_fp, promise_m, NULL_TREE,
>>> @@ -5042,9 +5016,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>>>   			      boolean_type_node);
>>>         finish_expr_stmt (r);
>>>       }
>>> -  /* Initialize the resume_idx_name to 0, meaning "not started".  */
>>> +  /* Initialize the resume_idx_var to 0, meaning "not started".  */
>>>     tree resume_idx_m
>>> -    = lookup_member (coro_frame_type, resume_idx_name,
>>> +    = lookup_member (coro_frame_type, coro_resume_index_field,
>>>   		     /*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,
>>> @@ -5187,9 +5161,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>>>     push_deferring_access_checks (dk_no_check);
>>>       /* Build the actor...  */
>>> -  build_actor_fn (fn_start, coro_frame_type, actor, fnbody, orig, param_uses,
>>> -		  &local_var_uses, param_dtor_list, resume_fn_field,
>>> -		  resume_idx_field, body_aw_points.await_number, frame_size);
>>> +  build_actor_fn (fn_start, coro_frame_type, actor, fnbody, orig,
>>> +		  &local_var_uses, param_dtor_list,
>>> +		  resume_idx_var, body_aw_points.await_number, frame_size);
>>>       /* Destroyer ... */
>>>     build_destroy_fn (fn_start, coro_frame_type, destroy, actor);
>>
> 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 6/8] coroutines: Convert implementation variables to debug-friendly form.
  2021-09-03 14:12                 ` Jason Merrill
@ 2021-09-03 14:21                   ` Iain Sandoe
  2021-09-05 19:47                     ` Iain Sandoe
  0 siblings, 1 reply; 28+ messages in thread
From: Iain Sandoe @ 2021-09-03 14:21 UTC (permalink / raw)
  To: Jason Merrill; +Cc: GCC Patches



> On 3 Sep 2021, at 15:12, Jason Merrill <jason@redhat.com> wrote:
> 
> On 9/3/21 9:56 AM, Iain Sandoe wrote:
>>> On 3 Sep 2021, at 14:52, Jason Merrill <jason@redhat.com> wrote:
>>> 
>>> On 9/1/21 6:55 AM, Iain Sandoe wrote:
>>>> The user might well wish to inspect some of the state that represents
>>>> the implementation of the coroutine machine.
>>>> In particular:
>>>>   The promise object.
>>>>   The function pointers for the resumer and destroyer.
>>>>   The current resume index (suspend point).
>>>>   The handle that represent this coroutine 'self handle'.
>>>>   Whether the coroutine frame is allocated and needs to be freed.
>>>> These variables are given names that can be 'well-known' and advertised
>>>> in debug documentation - they are placed in the implementation namespace
>>>> and all begin with _Coro_.
>>> 
>>>> Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
>>>> gcc/cp/ChangeLog:
>>>> 	* coroutines.cc (transform_await_expr): Use debug-friendly
>>>> 	names for coroutine implementation.
>>>> 	(build_actor_fn): Likewise.
>>>> 	(build_destroy_fn): Likewise.
>>>> 	(coro_rewrite_function_body): Likewise.
>>>> 	(morph_fn_to_coro): Likewise.
>>> 
>>> Hmm, this patch doesn't seem to match the description and ChangeLog entry other than in the names of the functions changed.
>> with 20:20 hindsight I should have squashed the (several) patches related to the implementation symbols,
>> I’ll redo the description - essentially, this is just making use of the simplification available because we now have pre-defined values for the field names.
> 
> I can see how that describes a few lines in this patch, but not for instance the change to transform_await_expr, which seems to have nothing to do with names?

it’s indirect indeed - but the changes we’ve made to the variable handling mean that we no longer need to rewrite the
proxy vars into their frame->offset; that is handled by the DECL_VALUE_EXPR (as for user’s vars ) - the change to transform_await_expr is removing the now defunct substitution (and poorly described, sorry).

> But yes, moving the changed lines that just use the variables from the previous patch into that commit sounds good.  I use rebase -i for that sort of thing all the time.

yeah, me too -  I realised too late that this series could have had more squashing - if it would make things easier I could do that for the patches related to implemenation variables .. - which would include patch 8 (but not patch 7 which is related to parms only)


>>>> ---
>>>>  gcc/cp/coroutines.cc | 214 +++++++++++++++++++------------------------
>>>>  1 file changed, 94 insertions(+), 120 deletions(-)
>>>> diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
>>>> index 3b46aac4dc5..aacf352f1c9 100644
>>>> --- a/gcc/cp/coroutines.cc
>>>> +++ b/gcc/cp/coroutines.cc
>>>> @@ -1906,7 +1906,6 @@ transform_await_expr (tree await_expr, await_xform_data *xform)
>>>>    /* So, on entry, we have:
>>>>       in : CO_AWAIT_EXPR (a, e_proxy, o, awr_call_vector, mode)
>>>>  	  We no longer need a [it had diagnostic value, maybe?]
>>>> -	  We need to replace the promise proxy in all elements
>>>>  	  We need to replace the e_proxy in the awr_call.  */
>>>>      tree coro_frame_type = TREE_TYPE (xform->actor_frame);
>>>> @@ -1932,16 +1931,6 @@ transform_await_expr (tree await_expr, await_xform_data *xform)
>>>>        TREE_OPERAND (await_expr, 1) = as;
>>>>      }
>>>>  -  /* Now do the self_handle.  */
>>>> -  data.from = xform->self_h_proxy;
>>>> -  data.to = xform->real_self_h;
>>>> -  cp_walk_tree (&await_expr, replace_proxy, &data, NULL);
>>>> -
>>>> -  /* Now do the promise.  */
>>>> -  data.from = xform->promise_proxy;
>>>> -  data.to = xform->real_promise;
>>>> -  cp_walk_tree (&await_expr, replace_proxy, &data, NULL);
>>>> -
>>>>    return await_expr;
>>>>  }
>>>>  @@ -2128,10 +2117,9 @@ coro_get_frame_dtor (tree coro_fp, tree orig, tree frame_size,
>>>>    static void
>>>>  build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>>> -		tree orig, hash_map<tree, param_info> *param_uses,
>>>> -		hash_map<tree, local_var_info> *local_var_uses,
>>>> -		vec<tree, va_gc> *param_dtor_list, tree resume_fn_field,
>>>> -		tree resume_idx_field, unsigned body_count, tree frame_size)
>>>> +		tree orig, hash_map<tree, local_var_info> *local_var_uses,
>>>> +		vec<tree, va_gc> *param_dtor_list,
>>>> +		tree resume_idx_var, unsigned body_count, tree frame_size)
>>>>  {
>>>>    verify_stmt_tree (fnbody);
>>>>    /* Some things we inherit from the original function.  */
>>>> @@ -2216,8 +2204,8 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>>>      = {actor, actor_frame, coro_frame_type, loc, local_var_uses};
>>>>    cp_walk_tree (&fnbody, transform_local_var_uses, &xform_vars_data, NULL);
>>>>  -  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field, 1, 0,
>>>> -				  tf_warning_or_error);
>>>> +  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field,
>>>> +				  1, 0, tf_warning_or_error);
>>>>    tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, actor_frame,
>>>>  		     rat_field, NULL_TREE);
>>>>  @@ -2319,14 +2307,8 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>>>    tree r = build_stmt (loc, LABEL_EXPR, actor_begin_label);
>>>>    add_stmt (r);
>>>>  -  /* actor's version of the promise.  */
>>>> -  tree ap_m = lookup_member (coro_frame_type, get_identifier ("_Coro_promise"), 1, 0,
>>>> -			     tf_warning_or_error);
>>>> -  tree ap = build_class_member_access_expr (actor_frame, ap_m, NULL_TREE, false,
>>>> -					    tf_warning_or_error);
>>>> -
>>>>    /* actor's coroutine 'self handle'.  */
>>>> -  tree ash_m = lookup_member (coro_frame_type, get_identifier ("_Coro_self_handle"), 1,
>>>> +  tree ash_m = lookup_member (coro_frame_type, coro_self_handle_field, 1,
>>>>  			      0, tf_warning_or_error);
>>>>    tree ash = build_class_member_access_expr (actor_frame, ash_m, NULL_TREE,
>>>>  					     false, tf_warning_or_error);
>>>> @@ -2347,36 +2329,13 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>>>       decide where to put things.  */
>>>>      await_xform_data xform
>>>> -    = {actor, actor_frame, promise_proxy, ap, self_h_proxy, ash};
>>>> +    = {actor, actor_frame, NULL_TREE, NULL_TREE, self_h_proxy, ash};
>>>>      /* Transform the await expressions in the function body.  Only do each
>>>>       await tree once!  */
>>>>    hash_set<tree> pset;
>>>>    cp_walk_tree (&fnbody, transform_await_wrapper, &xform, &pset);
>>>>  -  /* 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);
>>>> -
>>>> -  /* The rewrite of the function adds code to set the resume_fn field to
>>>> -     nullptr when the coroutine is done and also the index to zero when
>>>> -     calling an unhandled exception.  These are represented by two proxies
>>>> -     in the function, so rewrite them to the proper frame access.  */
>>>> -  tree resume_m
>>>> -    = lookup_member (coro_frame_type, get_identifier ("_Coro_resume_fn"),
>>>> -		     /*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);
>>>> -  p_data.from = resume_fn_field;
>>>> -  p_data.to = res_x;
>>>> -  cp_walk_tree (&fnbody, replace_proxy, &p_data, NULL);
>>>> -
>>>> -  p_data.from = resume_idx_field;
>>>> -  p_data.to = rat;
>>>> -  cp_walk_tree (&fnbody, replace_proxy, &p_data, NULL);
>>>> -
>>>>    /* Add in our function body with the co_returns rewritten to final form.  */
>>>>    add_stmt (fnbody);
>>>>  @@ -2385,7 +2344,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>>>    add_stmt (r);
>>>>      /* Destructors for the things we built explicitly.  */
>>>> -  r = build_special_member_call (ap, complete_dtor_identifier, NULL,
>>>> +  r = build_special_member_call (promise_proxy, complete_dtor_identifier, NULL,
>>>>  				 promise_type, LOOKUP_NORMAL,
>>>>  				 tf_warning_or_error);
>>>>    add_stmt (r);
>>>> @@ -2398,7 +2357,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>>>    /* Here deallocate the frame (if we allocated it), which we will have at
>>>>       present.  */
>>>>    tree fnf_m
>>>> -    = lookup_member (coro_frame_type, get_identifier ("_Coro_frame_needs_free"), 1,
>>>> +    = lookup_member (coro_frame_type, coro_frame_needs_free_field, 1,
>>>>  		     0, tf_warning_or_error);
>>>>    tree fnf2_x = build_class_member_access_expr (actor_frame, fnf_m, NULL_TREE,
>>>>  						false, tf_warning_or_error);
>>>> @@ -2477,18 +2436,10 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>>>    gcc_checking_assert (maybe_cleanup_point_expr_void (r) == r);
>>>>    add_stmt (r);
>>>>  -  /* We will need to know which resume point number should be encoded.  */
>>>> -  tree res_idx_m
>>>> -    = lookup_member (coro_frame_type, coro_resume_index_field,
>>>> -		     /*protect=*/1, /*want_type=*/0, tf_warning_or_error);
>>>> -  tree resume_pt_number
>>>> -    = build_class_member_access_expr (actor_frame, res_idx_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, NULL_TREE,
>>>> +  coro_aw_data data = {actor, actor_fp, resume_idx_var, NULL_TREE,
>>>>  		       ash, del_promise_label, ret_label,
>>>>  		       continue_label, continuation, 2};
>>>>    cp_walk_tree (&actor_body, await_statement_expander, &data, NULL);
>>>> @@ -2502,7 +2453,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>>>  }
>>>>    /* The prototype 'destroy' function :
>>>> -   frame->__resume_at |= 1;
>>>> +   frame->__Coro_resume_index |= 1;
>>>>     actor (frame);  */
>>>>    static void
>>>> @@ -2521,10 +2472,10 @@ build_destroy_fn (location_t loc, tree coro_frame_type, tree destroy,
>>>>      tree destr_frame = build1 (INDIRECT_REF, coro_frame_type, destr_fp);
>>>>  -  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field, 1, 0,
>>>> -				  tf_warning_or_error);
>>>> -  tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, destr_frame,
>>>> -		     rat_field, NULL_TREE);
>>>> +  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_field,
>>>> +				  1, 0, tf_warning_or_error);
>>>> +  tree rat = build3 (COMPONENT_REF, short_unsigned_type_node,
>>>> +			 destr_frame, rat_field, NULL_TREE);
>>>>      /* _resume_at |= 1 */
>>>>    tree dstr_idx = build2 (BIT_IOR_EXPR, short_unsigned_type_node, rat,
>>>> @@ -4040,8 +3991,8 @@ coro_build_actor_or_destroy_function (tree orig, tree fn_type,
>>>>    static tree
>>>>  coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>>>> -			    tree resume_fn_ptr_type, tree& resume_fn_field,
>>>> -			    tree& resume_idx_field, tree& fs_label)
>>>> +			    tree resume_fn_ptr_type,
>>>> +			    tree& resume_idx_var, tree& fs_label)
>>>>  {
>>>>    /* This will be our new outer scope.  */
>>>>    tree update_body = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL);
>>>> @@ -4074,7 +4025,6 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>>>>      /* 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);
>>>>  @@ -4085,24 +4035,61 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>>>>    tree return_void
>>>>      = get_coroutine_return_void_expr (current_function_decl, fn_start, false);
>>>>  +  /* The pointer to the resume function.  */
>>>> +  tree resume_fn_ptr
>>>> +    = coro_build_artificial_var (fn_start, coro_resume_fn_field,
>>>> +				 resume_fn_ptr_type, orig, NULL_TREE);
>>>> +  DECL_CHAIN (resume_fn_ptr) = var_list;
>>>> +  var_list = resume_fn_ptr;
>>>> +  add_decl_expr (resume_fn_ptr);
>>>> +
>>>>    /* We will need to be able to set the resume function pointer to nullptr
>>>>       to signal that the coroutine is 'done'.  */
>>>> -  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);
>>>> -  /* Likewise, the resume index needs to be reset.  */
>>>> -  resume_idx_field
>>>> -    = build_lang_decl (VAR_DECL, get_identifier ("resume.index.proxy"),
>>>> -		       short_unsigned_type_node);
>>>> -  DECL_ARTIFICIAL (resume_idx_field) = true;
>>>> -  tree zero_resume_idx = build_int_cst (short_unsigned_type_node, 0);
>>>> -  zero_resume_idx = build2 (INIT_EXPR, short_unsigned_type_node,
>>>> -			    resume_idx_field, zero_resume_idx);
>>>> +
>>>> +  /* The pointer to the destroy function.  */
>>>> +  tree var = coro_build_artificial_var (fn_start, coro_destroy_fn_field,
>>>> +					resume_fn_ptr_type, orig, NULL_TREE);
>>>> +  DECL_CHAIN (var) = var_list;
>>>> +  var_list = var;
>>>> +  add_decl_expr (var);
>>>> +
>>>> +  /* The promise was created on demand when parsing we now link it into
>>>> +      our scope.  */
>>>> +  tree promise = get_coroutine_promise_proxy (orig);
>>>> +  DECL_CONTEXT (promise) = orig;
>>>> +  DECL_SOURCE_LOCATION (promise) = fn_start;
>>>> +  DECL_CHAIN (promise) = var_list;
>>>> +  var_list = promise;
>>>> +  add_decl_expr (promise);
>>>> +
>>>> +  /* We need a handle to this coroutine, which is passed to every
>>>> +     await_suspend().  This was created on demand when parsing we now link it
>>>> +     into our scope.  */
>>>> +  var = get_coroutine_self_handle_proxy (orig);
>>>> +  DECL_CONTEXT (var) = orig;
>>>> +  DECL_SOURCE_LOCATION (var) = fn_start;
>>>> +  DECL_CHAIN (var) = var_list;
>>>> +  var_list = var;
>>>> +  add_decl_expr (var);
>>>> +
>>>> +
>>>> +  /* We create a resume index, this is initialized in the ramp.  */
>>>> +  resume_idx_var
>>>> +    = coro_build_artificial_var (fn_start, coro_resume_index_field,
>>>> +				 short_unsigned_type_node, orig, NULL_TREE);
>>>> +  DECL_CHAIN (resume_idx_var) = var_list;
>>>> +  var_list = resume_idx_var;
>>>> +  add_decl_expr (resume_idx_var);
>>>> +
>>>> +  /* If the coroutine has a frame that needs to be freed, this will be set by
>>>> +     the ramp.  */
>>>> +  var = coro_build_artificial_var (fn_start, coro_frame_needs_free_field,
>>>> +				   boolean_type_node, orig, NULL_TREE);
>>>> +  DECL_CHAIN (var) = var_list;
>>>> +  var_list = var;
>>>> +  add_decl_expr (var);
>>>>      if (flag_exceptions)
>>>>      {
>>>> @@ -4166,10 +4153,14 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>>>>  	 If the unhandled exception method returns, then we continue
>>>>  	 to the final await expression (which duplicates the clearing of
>>>>  	 the field). */
>>>> -      finish_expr_stmt (zero_resume);
>>>> -      finish_expr_stmt (zero_resume_idx);
>>>> -      ueh = maybe_cleanup_point_expr_void (ueh);
>>>> -      add_stmt (ueh);
>>>> +      tree r = build2 (MODIFY_EXPR, resume_fn_ptr_type, resume_fn_ptr,
>>>> +		       zero_resume);
>>>> +      finish_expr_stmt (r);
>>>> +      tree short_zero = build_int_cst (short_unsigned_type_node, 0);
>>>> +      r = build2 (MODIFY_EXPR, short_unsigned_type_node, resume_idx_var,
>>>> +		  short_zero);
>>>> +      finish_expr_stmt (r);
>>>> +      finish_expr_stmt (ueh);
>>>>        finish_handler (handler);
>>>>        TRY_HANDLERS (tcb) = pop_stmt_list (TRY_HANDLERS (tcb));
>>>>      }
>>>> @@ -4204,6 +4195,8 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>>>>    /* 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.  */
>>>> +  zero_resume = build2 (MODIFY_EXPR, resume_fn_ptr_type, resume_fn_ptr,
>>>> +			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));
>>>> @@ -4348,33 +4341,16 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>>>>    /* Construct the wrapped function body; we will analyze this to determine
>>>>       the requirements for the coroutine frame.  */
>>>>  -  tree resume_fn_field = NULL_TREE;
>>>> -  tree resume_idx_field = NULL_TREE;
>>>> +  tree resume_idx_var = NULL_TREE;
>>>>    tree fs_label = NULL_TREE;
>>>>    fnbody = coro_rewrite_function_body (fn_start, fnbody, orig,
>>>> -				       act_des_fn_ptr, resume_fn_field,
>>>> -				       resume_idx_field, fs_label);
>>>> +				       act_des_fn_ptr,
>>>> +				       resume_idx_var, fs_label);
>>>>    /* Build our dummy coro frame layout.  */
>>>>    coro_frame_type = begin_class_definition (coro_frame_type);
>>>>  +  /* The fields for the coro frame.  */
>>>>    tree field_list = NULL_TREE;
>>>> -  tree resume_name
>>>> -    = coro_make_frame_entry (&field_list, "_Coro_resume_fn",
>>>> -			     act_des_fn_ptr, fn_start);
>>>> -  tree destroy_name
>>>> -    = coro_make_frame_entry (&field_list, "_Coro_destroy_fn",
>>>> -			     act_des_fn_ptr, fn_start);
>>>> -  tree promise_name
>>>> -    = coro_make_frame_entry (&field_list, "_Coro_promise", promise_type, fn_start);
>>>> -  tree fnf_name = coro_make_frame_entry (&field_list, "_Coro_frame_needs_free",
>>>> -					 boolean_type_node, fn_start);
>>>> -  tree resume_idx_name
>>>> -    = coro_make_frame_entry (&field_list, "_Coro_resume_index",
>>>> -			     short_unsigned_type_node, fn_start);
>>>> -
>>>> -  /* We need a handle to this coroutine, which is passed to every
>>>> -     await_suspend().  There's no point in creating it over and over.  */
>>>> -  (void) coro_make_frame_entry (&field_list, "_Coro_self_handle", handle_type, fn_start);
>>>>      /* Now add in fields for function params (if there are any).
>>>>       We do not attempt elision of copies at this stage, we do analyze the
>>>> @@ -4776,8 +4752,8 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>>>>      /* 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 (coro_frame_type, fnf_name, 1, 0, tf_warning_or_error);
>>>> +  tree fnf_m = lookup_member (coro_frame_type, coro_frame_needs_free_field,
>>>> +			      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 = build2 (INIT_EXPR, boolean_type_node, fnf_x, boolean_true_node);
>>>> @@ -4788,24 +4764,22 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>>>>      tree actor_addr = build1 (ADDR_EXPR, act_des_fn_ptr, actor);
>>>>    tree resume_m
>>>> -    = lookup_member (coro_frame_type, resume_name,
>>>> +    = lookup_member (coro_frame_type, coro_resume_fn_field,
>>>>  		     /*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 = build2_loc (fn_start, INIT_EXPR, act_des_fn_ptr, resume_x, actor_addr);
>>>> -  r = coro_build_cvt_void_expr_stmt (r, fn_start);
>>>> -  add_stmt (r);
>>>> +  finish_expr_stmt (r);
>>>>      tree destroy_addr = build1 (ADDR_EXPR, act_des_fn_ptr, destroy);
>>>>    tree destroy_m
>>>> -    = lookup_member (coro_frame_type, destroy_name,
>>>> +    = lookup_member (coro_frame_type, coro_destroy_fn_field,
>>>>  		     /*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 = build2_loc (fn_start, INIT_EXPR, act_des_fn_ptr, destroy_x, destroy_addr);
>>>> -  r = coro_build_cvt_void_expr_stmt (r, fn_start);
>>>> -  add_stmt (r);
>>>> +  finish_expr_stmt (r);
>>>>      /* [dcl.fct.def.coroutine] /13
>>>>       When a coroutine is invoked, a copy is created for each coroutine
>>>> @@ -4896,7 +4870,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>>>>      /* Set up the promise.  */
>>>>    tree promise_m
>>>> -    = lookup_member (coro_frame_type, promise_name,
>>>> +    = lookup_member (coro_frame_type, coro_promise_field,
>>>>  		     /*protect=*/1, /*want_type=*/0, tf_warning_or_error);
>>>>      tree p = build_class_member_access_expr (deref_fp, promise_m, NULL_TREE,
>>>> @@ -5042,9 +5016,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>>>>  			      boolean_type_node);
>>>>        finish_expr_stmt (r);
>>>>      }
>>>> -  /* Initialize the resume_idx_name to 0, meaning "not started".  */
>>>> +  /* Initialize the resume_idx_var to 0, meaning "not started".  */
>>>>    tree resume_idx_m
>>>> -    = lookup_member (coro_frame_type, resume_idx_name,
>>>> +    = lookup_member (coro_frame_type, coro_resume_index_field,
>>>>  		     /*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,
>>>> @@ -5187,9 +5161,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>>>>    push_deferring_access_checks (dk_no_check);
>>>>      /* Build the actor...  */
>>>> -  build_actor_fn (fn_start, coro_frame_type, actor, fnbody, orig, param_uses,
>>>> -		  &local_var_uses, param_dtor_list, resume_fn_field,
>>>> -		  resume_idx_field, body_aw_points.await_number, frame_size);
>>>> +  build_actor_fn (fn_start, coro_frame_type, actor, fnbody, orig,
>>>> +		  &local_var_uses, param_dtor_list,
>>>> +		  resume_idx_var, body_aw_points.await_number, frame_size);
>>>>      /* Destroyer ... */
>>>>    build_destroy_fn (fn_start, coro_frame_type, destroy, actor);


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 7/8] coroutines: Make proxy vars for the function arg copies.
  2021-09-03 14:07               ` [PATCH 7/8] coroutines: Make proxy vars for the function arg copies Jason Merrill
@ 2021-09-03 14:23                 ` Iain Sandoe
  2021-09-05 19:50                   ` [PATCH 7/8 v2] " Iain Sandoe
  0 siblings, 1 reply; 28+ messages in thread
From: Iain Sandoe @ 2021-09-03 14:23 UTC (permalink / raw)
  To: Jason Merrill; +Cc: GCC Patches



> On 3 Sep 2021, at 15:07, Jason Merrill via Gcc-patches <gcc-patches@gcc.gnu.org> wrote:
> 
> On 9/1/21 6:56 AM, Iain Sandoe wrote:
>> This adds top level proxy variables for the coroutine frame
>> copies of the original function args.  These are then available
>> in the debugger to refer to the frame copies.  We rewrite the
>> function body to use the copies, since the original parms will
>> no longer be in scope when the coroutine is running.
>> Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
>> gcc/cp/ChangeLog:
>> 	* coroutines.cc (struct param_info): Add copy_var.
>> 	(build_actor_fn): Use simplified param references.
>> 	(register_param_uses): Likewise.
>> 	(rewrite_param_uses): Likewise.
>> 	(analyze_fn_parms): New function.
>> 	(coro_rewrite_function_body): Add proxies for the fn
>> 	parameters to the outer bind scope of the rewritten code.
>> 	(morph_fn_to_coro): Use simplified version of param ref.
>> ---
>>  gcc/cp/coroutines.cc | 247 ++++++++++++++++++++-----------------------
>>  1 file changed, 117 insertions(+), 130 deletions(-)
>> diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
>> index aacf352f1c9..395e5c488e5 100644
>> --- a/gcc/cp/coroutines.cc
>> +++ b/gcc/cp/coroutines.cc
>> @@ -1964,6 +1964,7 @@ transform_await_wrapper (tree *stmt, int *do_subtree, void *d)
>>  struct param_info
>>  {
>>    tree field_id;     /* The name of the copy in the coroutine frame.  */
>> +  tree copy_var;     /* The local var proxy for the frame copy.  */
>>    vec<tree *> *body_uses; /* Worklist of uses, void if there are none.  */
>>    tree frame_type;   /* The type used to represent this parm in the frame.  */
>>    tree orig_type;    /* The original type of the parm (not as passed).  */
>> @@ -2169,36 +2170,6 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>>    /* Declare the continuation handle.  */
>>    add_decl_expr (continuation);
>>  -  /* Re-write param references in the body, no code should be generated
>> -     here.  */
>> -  if (DECL_ARGUMENTS (orig))
>> -    {
>> -      tree arg;
>> -      for (arg = DECL_ARGUMENTS (orig); arg != NULL; arg = DECL_CHAIN (arg))
>> -	{
>> -	  bool existed;
>> -	  param_info &parm = param_uses->get_or_insert (arg, &existed);
>> -	  if (!parm.body_uses)
>> -	    continue; /* Wasn't used in the original function body.  */
>> -
>> -	  tree fld_ref = lookup_member (coro_frame_type, parm.field_id,
>> -					/*protect=*/1, /*want_type=*/0,
>> -					tf_warning_or_error);
>> -	  tree fld_idx = build3_loc (loc, COMPONENT_REF, parm.frame_type,
>> -				     actor_frame, fld_ref, NULL_TREE);
>> -
>> -	  /* We keep these in the frame as a regular pointer, so convert that
>> -	   back to the type expected.  */
>> -	  if (parm.pt_ref)
>> -	    fld_idx = build1_loc (loc, CONVERT_EXPR, TREE_TYPE (arg), fld_idx);
>> -
>> -	  int i;
>> -	  tree *puse;
>> -	  FOR_EACH_VEC_ELT (*parm.body_uses, i, puse)
>> -	    *puse = fld_idx;
>> -	}
>> -    }
>> -
>>    /* Re-write local vars, similarly.  */
>>    local_vars_transform xform_vars_data
>>      = {actor, actor_frame, coro_frame_type, loc, local_var_uses};
>> @@ -3772,11 +3743,11 @@ struct param_frame_data
>>    bool param_seen;
>>  };
>>  -/* A tree-walk callback that records the use of parameters (to allow for
>> -   optimizations where handling unused parameters may be omitted).  */
>> +/* A tree walk callback that rewrites each parm use to the local variable
>> +   that represents its copy in the frame.  */
>>    static tree
>> -register_param_uses (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
>> +rewrite_param_uses (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
>>  {
>>    param_frame_data *data = (param_frame_data *) d;
>>  @@ -3784,7 +3755,7 @@ register_param_uses (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
>>    if (TREE_CODE (*stmt) == VAR_DECL && DECL_HAS_VALUE_EXPR_P (*stmt))
>>      {
>>        tree t = DECL_VALUE_EXPR (*stmt);
>> -      return cp_walk_tree (&t, register_param_uses, d, NULL);
>> +      return cp_walk_tree (&t, rewrite_param_uses, d, NULL);
>>      }
>>      if (TREE_CODE (*stmt) != PARM_DECL)
>> @@ -3798,16 +3769,87 @@ register_param_uses (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
>>    param_info &parm = data->param_uses->get_or_insert (*stmt, &existed);
>>    gcc_checking_assert (existed);
>>  -  if (!parm.body_uses)
>> +  *stmt = parm.copy_var;
>> +  return NULL_TREE;
>> +}
>> +
>> +/* Build up a set of info that determines how each param copy will be
>> +   handled.  */
>> +
>> +static hash_map<tree, param_info> *analyze_fn_parms (tree orig)
> 
> Function name should be on a new line.

oops.
> 
>> +{
>> +  if (!DECL_ARGUMENTS (orig))
>> +    return NULL;
>> +
>> +  hash_map<tree, param_info> *param_uses = new hash_map<tree, param_info>;
>> +
>> +  /* Build a hash map with an entry for each param.
>> +     The key is the param tree.
>> +     Then we have an entry for the frame field name.
>> +     Then a cache for the field ref when we come to use it.
>> +     Then a tree list of the uses.
>> +     The second two entries start out empty - and only get populated
>> +     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))
>>      {
>> -      vec_alloc (parm.body_uses, 4);
>> -      parm.body_uses->quick_push (stmt);
>> -      data->param_seen = true;
>> +      bool existed;
>> +      param_info &parm = param_uses->get_or_insert (arg, &existed);
>> +      gcc_checking_assert (!existed);
>> +      parm.body_uses = NULL;
>> +      tree actual_type = TREE_TYPE (arg);
>> +      actual_type = complete_type_or_else (actual_type, orig);
>> +      if (actual_type == NULL_TREE)
>> +	actual_type = error_mark_node;
>> +      parm.orig_type = actual_type;
>> +      parm.by_ref = parm.pt_ref = parm.rv_ref =  false;
>> +      if (TREE_CODE (actual_type) == REFERENCE_TYPE)
>> +	{
>> +	  /* If the user passes by reference, then we will save the
>> +	     pointer to the original.  As noted in
>> +	     [dcl.fct.def.coroutine] / 13, if the lifetime of the
>> +	     referenced item ends and then the coroutine is resumed,
>> +	     we have UB; well, the user asked for it.  */
>> +	  if (TYPE_REF_IS_RVALUE (actual_type))
>> +		parm.rv_ref = true;
>> +	  else
>> +		parm.pt_ref = true;
>> +	}
>> +      else if (TYPE_REF_P (DECL_ARG_TYPE (arg)))
>> +	parm.by_ref = true;
>> +
>> +      parm.frame_type = actual_type;
>> +
>> +      parm.this_ptr = is_this_parameter (arg);
>> +      parm.lambda_cobj = lambda_p && DECL_NAME (arg) == closure_identifier;
>> +
>> +      tree name = DECL_NAME (arg);
>> +      if (!name)
>> +	{
>> +	  char *buf = xasprintf ("_Coro_unnamed_parm_%d", no_name_parm++);
>> +	  name = get_identifier (buf);
>> +	  free (buf);
>> +	}
>> +      parm.field_id = name;
>> +
>> +      if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (parm.frame_type))
>> +	{
>> +	  char *buf = xasprintf ("_Coro_%s_live", IDENTIFIER_POINTER (name));
>> +	  parm.guard_var = build_lang_decl (VAR_DECL, get_identifier (buf),
>> +					    boolean_type_node);
>> +	  free (buf);
>> +	  DECL_ARTIFICIAL (parm.guard_var) = true;
>> +	  DECL_CONTEXT (parm.guard_var) = orig;
>> +	  DECL_INITIAL (parm.guard_var) = boolean_false_node;
>> +	  parm.trivial_dtor = false;
>> +	}
>> +      else
>> +	parm.trivial_dtor = true;
>>      }
>> -  else
>> -    parm.body_uses->safe_push (stmt);
>>  -  return NULL_TREE;
>> +  return param_uses;
>>  }
>>    /* Small helper for the repetitive task of adding a new field to the coro
>> @@ -3991,6 +4033,7 @@ coro_build_actor_or_destroy_function (tree orig, tree fn_type,
>>    static tree
>>  coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>> +			    hash_map<tree, param_info> *param_uses,
>>  			    tree resume_fn_ptr_type,
>>  			    tree& resume_idx_var, tree& fs_label)
>>  {
>> @@ -4074,6 +4117,36 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>>    var_list = var;
>>    add_decl_expr (var);
>>  +  /* If we have function parms, then these will be copied to the coroutine
>> +     frame.  Create a local variable to point to each of them so that we can
>> +     see them in the debugger.  */
>> +
>> +  if (param_uses)
>> +    {
>> +      gcc_checking_assert (DECL_ARGUMENTS (orig));
>> +      /* Add a local var for each parm.  */
>> +      for (tree arg = DECL_ARGUMENTS (orig); arg != NULL;
>> +	   arg = DECL_CHAIN (arg))
>> +	{
>> +	  param_info *parm_i = param_uses->get (arg);
>> +	  gcc_checking_assert (parm_i);
>> +	  parm_i->copy_var
>> +	    = build_lang_decl (VAR_DECL, parm_i->field_id, TREE_TYPE (arg));
>> +	  DECL_SOURCE_LOCATION (parm_i->copy_var) = DECL_SOURCE_LOCATION (arg);
>> +	  DECL_CONTEXT (parm_i->copy_var) = orig;
>> +	  DECL_ARTIFICIAL (parm_i->copy_var) = true;
>> +	  DECL_CHAIN (parm_i->copy_var) = var_list;
>> +	  var_list = parm_i->copy_var;
>> +	  add_decl_expr (parm_i->copy_var);
> 
> Are these getting DECL_VALUE_EXPR somewhere?

Yes - all variables in the outermost bind expression will be allocated a frame slot, and gain a DECL_VALUE_EXPR to point to it.

>> +      	}
>> +
>> +      /* Now replace all uses of the parms in the function body with the local
>> +	 vars.  */
> 
> I think the following old comment still applies to how 'visited' is used, and should be adapted here as well:

Yes, will do.
> 
>>> -      /* We want to record every instance of param's use, so don't include
>>> -	 a 'visited' hash_set on the tree walk, but only record a containing
>>> -	 expression once.  */
> 
>> +      hash_set<tree *> visited;
>> +      param_frame_data param_data = {NULL, param_uses,
>> +				     &visited, fn_start, false};
>> +      cp_walk_tree (&fnbody, rewrite_param_uses, &param_data, NULL);
>> +    }
>>      /* We create a resume index, this is initialized in the ramp.  */
>>    resume_idx_var
>> @@ -4343,7 +4416,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>>      tree resume_idx_var = NULL_TREE;
>>    tree fs_label = NULL_TREE;
>> -  fnbody = coro_rewrite_function_body (fn_start, fnbody, orig,
>> +  hash_map<tree, param_info> *param_uses = analyze_fn_parms (orig);
>> +
>> +  fnbody = coro_rewrite_function_body (fn_start, fnbody, orig, param_uses,
>>  				       act_des_fn_ptr,
>>  				       resume_idx_var, fs_label);
>>    /* Build our dummy coro frame layout.  */
>> @@ -4352,94 +4427,6 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>>    /* The fields for the coro frame.  */
>>    tree field_list = NULL_TREE;
>>  -  /* Now add in fields for function params (if there are any).
>> -     We do not attempt elision of copies at this stage, we do analyze the
>> -     uses and build worklists to replace those when the state machine is
>> -     lowered.  */
>> -
>> -  hash_map<tree, param_info> *param_uses = NULL;
>> -  if (DECL_ARGUMENTS (orig))
>> -    {
>> -      /* Build a hash map with an entry for each param.
>> -	  The key is the param tree.
>> -	  Then we have an entry for the frame field name.
>> -	  Then a cache for the field ref when we come to use it.
>> -	  Then a tree list of the uses.
>> -	  The second two entries start out empty - and only get populated
>> -	  when we see uses.  */
>> -      param_uses = new hash_map<tree, param_info>;
>> -      bool lambda_p = LAMBDA_FUNCTION_P (orig);
>> -
>> -      unsigned no_name_parm = 0;
>> -      for (tree arg = DECL_ARGUMENTS (orig); arg != NULL;
>> -	   arg = DECL_CHAIN (arg))
>> -	{
>> -	  bool existed;
>> -	  param_info &parm = param_uses->get_or_insert (arg, &existed);
>> -	  gcc_checking_assert (!existed);
>> -	  parm.body_uses = NULL;
>> -	  tree actual_type = TREE_TYPE (arg);
>> -	  actual_type = complete_type_or_else (actual_type, orig);
>> -	  if (actual_type == NULL_TREE)
>> -	    actual_type = error_mark_node;
>> -	  parm.orig_type = actual_type;
>> -	  parm.by_ref = parm.pt_ref = parm.rv_ref =  false;
>> -	  if (TREE_CODE (actual_type) == REFERENCE_TYPE)
>> -	    {
>> -	      /* If the user passes by reference, then we will save the
>> -		 pointer to the original.  As noted in
>> -		 [dcl.fct.def.coroutine] / 13, if the lifetime of the
>> -		 referenced item ends and then the coroutine is resumed,
>> -		 we have UB; well, the user asked for it.  */
>> -	      if (TYPE_REF_IS_RVALUE (actual_type))
>> -		parm.rv_ref = true;
>> -	      else
>> -		parm.pt_ref = true;
>> -	    }
>> -	  else if (TYPE_REF_P (DECL_ARG_TYPE (arg)))
>> -	    parm.by_ref = true;
>> -
>> -	  parm.frame_type = actual_type;
>> -
>> -	  parm.this_ptr = is_this_parameter (arg);
>> -	  parm.lambda_cobj = lambda_p && DECL_NAME (arg) == closure_identifier;
>> -
>> -	  char *buf;
>> -	  if (DECL_NAME (arg))
>> -	    {
>> -	      tree pname = DECL_NAME (arg);
>> -	      buf = xasprintf ("_P_%s", IDENTIFIER_POINTER (pname));
>> -	    }
>> -	  else
>> -	    buf = xasprintf ("_P_unnamed_%d", no_name_parm++);
>> -
>> -	  if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (parm.frame_type))
>> -	    {
>> -	      char *gbuf = xasprintf ("%s_live", buf);
>> -	      parm.guard_var
>> -		= build_lang_decl (VAR_DECL, get_identifier (gbuf),
>> -				   boolean_type_node);
>> -	      free (gbuf);
>> -	      DECL_ARTIFICIAL (parm.guard_var) = true;
>> -	      DECL_INITIAL (parm.guard_var) = boolean_false_node;
>> -	      parm.trivial_dtor = false;
>> -	    }
>> -	  else
>> -	    parm.trivial_dtor = true;
>> -	  parm.field_id = coro_make_frame_entry
>> -	    (&field_list, buf, actual_type, DECL_SOURCE_LOCATION (arg));
>> -	  free (buf);
>> -	}
>> -
>> -      /* We want to record every instance of param's use, so don't include
>> -	 a 'visited' hash_set on the tree walk, but only record a containing
>> -	 expression once.  */
>> -      hash_set<tree *> visited;
>> -      param_frame_data param_data
>> -	= {&field_list, param_uses, &visited, fn_start, false};
>> -      cp_walk_tree (&fnbody, register_param_uses, &param_data, NULL);
>> -    }
>> -
>>    /* 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.  */


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 8/8] coroutines: Make the continue handle visible to debug.
  2021-09-01 10:56               ` [PATCH 8/8] coroutines: Make the continue handle visible to debug Iain Sandoe
@ 2021-09-03 14:25                 ` Jason Merrill
  0 siblings, 0 replies; 28+ messages in thread
From: Jason Merrill @ 2021-09-03 14:25 UTC (permalink / raw)
  To: Iain Sandoe, GCC Patches

On 9/1/21 6:56 AM, Iain Sandoe wrote:
> 
> When we have a suspend method that returns a coroutine handle
> we transfer (hopefully symmetrically, i.e. with a tailcall) to
> that new coroutine instead of returning to our resumer.
> 
> This adds the variable to the outer block for the actor function
> which means that '_Coro_actor_continue' is visible to debug.
> 
> Contributory to PR 99215.

OK.

> Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
> 
> gcc/cp/ChangeLog:
> 
> 	* coroutines.cc (build_actor_fn): Make _Coro_actor_continue
> 	visible to debug.
> ---
>   gcc/cp/coroutines.cc | 1 +
>   1 file changed, 1 insertion(+)
> 
> diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
> index 395e5c488e5..b32c5dc5e55 100644
> --- a/gcc/cp/coroutines.cc
> +++ b/gcc/cp/coroutines.cc
> @@ -2148,6 +2148,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>   						 NULL_TREE);
>     
>     BIND_EXPR_VARS (actor_bind) = continuation;
> +  BLOCK_VARS (top_block) = BIND_EXPR_VARS (actor_bind) ;
>   
>     /* Link in the block associated with the outer scope of the re-written
>        function body.  */
> 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 6/8] coroutines: Convert implementation variables to debug-friendly form.
  2021-09-03 14:21                   ` Iain Sandoe
@ 2021-09-05 19:47                     ` Iain Sandoe
  2021-09-07 17:23                       ` Jason Merrill
  0 siblings, 1 reply; 28+ messages in thread
From: Iain Sandoe @ 2021-09-05 19:47 UTC (permalink / raw)
  To: Jason Merrill; +Cc: GCC Patches

Hello Jason,

The patch below is a squashed version of:

(approved) [PATCH 4/8] coroutines: Make some of the artificial names more  debugger-friendly. 
                  [PATCH 5/8] coroutines: Define and populate accessors for debug  state.
                  [PATCH 6/8] coroutines: Convert implementation variables to  debug-friendly form.
(approved) [PATCH 8/8] coroutines: Make the continue handle visible to debug.


[PATCH 6/8] coroutines: Convert implementation variables to debug-friendly form.

> On 3 Sep 2021, at 15:21, Iain Sandoe <iain@sandoe.co.uk> wrote:
>> On 3 Sep 2021, at 15:12, Jason Merrill <jason@redhat.com> wrote:
>> 
>> On 9/3/21 9:56 AM, Iain Sandoe wrote:
>>>> On 3 Sep 2021, at 14:52, Jason Merrill <jason@redhat.com> wrote:
>>>> 
>>>> On 9/1/21 6:55 AM, Iain Sandoe wrote:

>>>>> 	(morph_fn_to_coro): Likewise.
>>>> 
>>>> Hmm, this patch doesn't seem to match the description and ChangeLog entry other than in the names of the functions changed.
>>> with 20:20 hindsight I should have squashed the (several) patches related to the implementation symbols,
>>> I’ll redo the description - essentially, this is just making use of the simplification available because we now have pre-defined values for the field names.
>> 
>> I can see how that describes a few lines in this patch, but not for instance the change to transform_await_expr, which seems to have nothing to do with names?
> 
> it’s indirect indeed - but the changes we’ve made to the variable handling mean that we no longer need to rewrite the
> proxy vars into their frame->offset; that is handled by the DECL_VALUE_EXPR (as for user’s vars ) - the change to transform_await_expr is removing the now defunct substitution (and poorly described, sorry).

now stated explicitly in the comments and in the commit log.
> 
>> But yes, moving the changed lines that just use the variables from the previous patch into that commit sounds good.  I use rebase -i for that sort of thing all the time.
> 
> yeah, me too -  I realised too late that this series could have had more squashing - if it would make things easier I could do that for the patches related to implemenation variables .. - which would include patch 8 (but not patch 7 which is related to parms only)

squashing the series should make the changes clearer.

[PATCH 5/8] coroutines: Define and populate accessors for debug  state.

>>>> +static GTY(()) tree coro_frame_needs_free_field;
>>>> +static GTY(()) tree coro_resume_index_field;
>>>> +static GTY(()) tree coro_self_handle_field;
>>> 
>>> Since these are identifiers, not FIELD_DECLs, calling them *_field seems misleading.
>> I could append _id or _name .. they were just getting long already.
>> (they are names of fields, so that would not be misleading)
> 
> _id works for me, either with or without the _field.
> 

Done

OK now?
thanks
Iain

======

[PATCH] coroutines: Expose implementation state to the debugger.

In the process of transforming a coroutine into the separate representation
as the ramp function and a state machine, we generate some variables that
are of interest to a user during debugging.  Any variable that is persistent
for the execution of the coroutine is placed into the coroutine frame.

In particular:
  The promise object.
  The function pointers for the resumer and destroyer.
  The current resume index (suspend point).
  The handle that represents this coroutine 'self handle'.
  Any handle provided for a continuation coroutine.
  Whether the coroutine frame is allocated and needs to be freed.

Visibility of some of these has already been requested by end users.

This patch ensures that such variables have names that are usable in a
debugger, but are in the reserved namespace for the implementation (they
all begin with _Coro_).  The identifiers are generated lazily when the
first coroutine is encountered.

We place the variables into the outermost bind expression and then add a
DECL_VALUE_EXPR to each that points to the frame entry.

These changes simplify the handling of the variables in the body of the
function (in particular, the use of the DECL_VALUE_EXPR means that we now
no longer need to rewrite proxies for the promise and coroutine handles into
the frame->offset form).

Partial improvement to debugging (PR c++/99215).

Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>

gcc/cp/ChangeLog:

	* coroutines.cc (coro_resume_fn_id, coro_destroy_fn_id,
	coro_promise_id, coro_frame_needs_free_id, coro_resume_index_id,
	coro_self_handle_id, coro_actor_continue_id,
	coro_frame_i_a_r_c_id): New.
	(coro_init_identifiers): Initialize new name identifiers.
	(coro_promise_type_found_p): Use pre-built identifiers.
	(struct await_xform_data): Remove unused fields.
	(transform_await_expr): Delete code that is now unused.
	(build_actor_fn): Simplify interface, use pre-built identifiers and
	remove transforms that are no longer needed.
	(build_destroy_fn): Use revised field names.
	(register_local_var_uses): Use pre-built identifiers.
	(coro_rewrite_function_body): Simplify interface, use pre-built
	identifiers.  Generate proxy vars in the outer bind expr scope for the
	implementation state that we wish to expose.
	(morph_fn_to_coro): Adjust comments for new variable names, use pre-
	built identifiers.  Remove unused code to generate frame entries for
	the implementation state.  Adjust call for build_actor_fn.
---
 gcc/cp/coroutines.cc | 285 +++++++++++++++++++++----------------------
 1 file changed, 137 insertions(+), 148 deletions(-)

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index 9ab2be04266..cd774b78ae0 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -215,7 +215,19 @@ static GTY(()) tree coro_await_ready_identifier;
 static GTY(()) tree coro_await_suspend_identifier;
 static GTY(()) tree coro_await_resume_identifier;
 
-/* Create the identifiers used by the coroutines library interfaces.  */
+/* Accessors for the coroutine frame state used by the implementation.  */
+
+static GTY(()) tree coro_resume_fn_id;
+static GTY(()) tree coro_destroy_fn_id;
+static GTY(()) tree coro_promise_id;
+static GTY(()) tree coro_frame_needs_free_id;
+static GTY(()) tree coro_resume_index_id;
+static GTY(()) tree coro_self_handle_id;
+static GTY(()) tree coro_actor_continue_id;
+static GTY(()) tree coro_frame_i_a_r_c_id;
+
+/* Create the identifiers used by the coroutines library interfaces and
+   the implementation frame state.  */
 
 static void
 coro_init_identifiers ()
@@ -241,6 +253,16 @@ coro_init_identifiers ()
   coro_await_ready_identifier = get_identifier ("await_ready");
   coro_await_suspend_identifier = get_identifier ("await_suspend");
   coro_await_resume_identifier = get_identifier ("await_resume");
+
+  /* Coroutine state frame field accessors.  */
+  coro_resume_fn_id = get_identifier ("_Coro_resume_fn");
+  coro_destroy_fn_id = get_identifier ("_Coro_destroy_fn");
+  coro_promise_id = get_identifier ("_Coro_promise");
+  coro_frame_needs_free_id = get_identifier ("_Coro_frame_needs_free");
+  coro_frame_i_a_r_c_id = get_identifier ("_Coro_initial_await_resume_called");
+  coro_resume_index_id = get_identifier ("_Coro_resume_index");
+  coro_self_handle_id = get_identifier ("_Coro_self_handle");
+  coro_actor_continue_id = get_identifier ("_Coro_actor_continue");
 }
 
 /* Trees we only need to set up once.  */
@@ -513,12 +535,12 @@ coro_promise_type_found_p (tree fndecl, location_t loc)
       /* Build a proxy for a handle to "self" as the param to
 	 await_suspend() calls.  */
       coro_info->self_h_proxy
-	= build_lang_decl (VAR_DECL, get_identifier ("self_h.proxy"),
+	= build_lang_decl (VAR_DECL, coro_self_handle_id,
 			   coro_info->handle_type);
 
       /* Build a proxy for the promise so that we can perform lookups.  */
       coro_info->promise_proxy
-	= build_lang_decl (VAR_DECL, get_identifier ("promise.proxy"),
+	= build_lang_decl (VAR_DECL, coro_promise_id,
 			   coro_info->promise_type);
 
       /* Note where we first saw a coroutine keyword.  */
@@ -1864,10 +1886,6 @@ struct await_xform_data
 {
   tree actor_fn;   /* Decl for context.  */
   tree actor_frame;
-  tree promise_proxy;
-  tree real_promise;
-  tree self_h_proxy;
-  tree real_self_h;
 };
 
 /* When we built the await expressions, we didn't know the coro frame
@@ -1888,7 +1906,6 @@ transform_await_expr (tree await_expr, await_xform_data *xform)
   /* So, on entry, we have:
      in : CO_AWAIT_EXPR (a, e_proxy, o, awr_call_vector, mode)
 	  We no longer need a [it had diagnostic value, maybe?]
-	  We need to replace the promise proxy in all elements
 	  We need to replace the e_proxy in the awr_call.  */
 
   tree coro_frame_type = TREE_TYPE (xform->actor_frame);
@@ -1914,16 +1931,6 @@ transform_await_expr (tree await_expr, await_xform_data *xform)
       TREE_OPERAND (await_expr, 1) = as;
     }
 
-  /* Now do the self_handle.  */
-  data.from = xform->self_h_proxy;
-  data.to = xform->real_self_h;
-  cp_walk_tree (&await_expr, replace_proxy, &data, NULL);
-
-  /* Now do the promise.  */
-  data.from = xform->promise_proxy;
-  data.to = xform->real_promise;
-  cp_walk_tree (&await_expr, replace_proxy, &data, NULL);
-
   return await_expr;
 }
 
@@ -2110,15 +2117,13 @@ coro_get_frame_dtor (tree coro_fp, tree orig, tree frame_size,
 
 static void
 build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
-		tree orig, hash_map<tree, param_info> *param_uses,
-		hash_map<tree, local_var_info> *local_var_uses,
-		vec<tree, va_gc> *param_dtor_list, tree resume_fn_field,
-		tree resume_idx_field, unsigned body_count, tree frame_size)
+		tree orig, hash_map<tree, local_var_info> *local_var_uses,
+		vec<tree, va_gc> *param_dtor_list,
+		tree resume_idx_var, unsigned body_count, tree frame_size)
 {
   verify_stmt_tree (fnbody);
   /* Some things we inherit from the original function.  */
   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);
 
@@ -2136,11 +2141,12 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
   tree top_block = make_node (BLOCK);
   BIND_EXPR_BLOCK (actor_bind) = top_block;
 
-  tree continuation = coro_build_artificial_var (loc, "_Coro_actor_continue",
+  tree continuation = coro_build_artificial_var (loc, coro_actor_continue_id,
 						 void_coro_handle_type, actor,
 						 NULL_TREE);
 
   BIND_EXPR_VARS (actor_bind) = continuation;
+  BLOCK_VARS (top_block) = BIND_EXPR_VARS (actor_bind) ;
 
   /* Link in the block associated with the outer scope of the re-written
      function body.  */
@@ -2198,9 +2204,8 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
     = {actor, actor_frame, coro_frame_type, loc, local_var_uses};
   cp_walk_tree (&fnbody, transform_local_var_uses, &xform_vars_data, NULL);
 
-  tree resume_idx_name = get_identifier ("__resume_at");
-  tree rat_field = lookup_member (coro_frame_type, resume_idx_name, 1, 0,
-				  tf_warning_or_error);
+  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_id,
+				  1, 0, tf_warning_or_error);
   tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, actor_frame,
 		     rat_field, NULL_TREE);
 
@@ -2302,14 +2307,8 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
   tree r = build_stmt (loc, LABEL_EXPR, actor_begin_label);
   add_stmt (r);
 
-  /* actor's version of the promise.  */
-  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);
-
   /* actor's coroutine 'self handle'.  */
-  tree ash_m = lookup_member (coro_frame_type, get_identifier ("__self_h"), 1,
+  tree ash_m = lookup_member (coro_frame_type, coro_self_handle_id, 1,
 			      0, tf_warning_or_error);
   tree ash = build_class_member_access_expr (actor_frame, ash_m, NULL_TREE,
 					     false, tf_warning_or_error);
@@ -2329,37 +2328,13 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
   /* Now we know the real promise, and enough about the frame layout to
      decide where to put things.  */
 
-  await_xform_data xform
-    = {actor, actor_frame, promise_proxy, ap, self_h_proxy, ash};
+  await_xform_data xform = {actor, actor_frame};
 
   /* Transform the await expressions in the function body.  Only do each
      await tree once!  */
   hash_set<tree> pset;
   cp_walk_tree (&fnbody, transform_await_wrapper, &xform, &pset);
 
-  /* 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);
-
-  /* The rewrite of the function adds code to set the __resume field to
-     nullptr when the coroutine is done and also the index to zero when
-     calling an unhandled exception.  These are represented by two proxies
-     in the function, so rewrite them to the proper frame access.  */
-  tree resume_m
-    = lookup_member (coro_frame_type, get_identifier ("__resume"),
-		     /*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);
-  p_data.from = resume_fn_field;
-  p_data.to = res_x;
-  cp_walk_tree (&fnbody, replace_proxy, &p_data, NULL);
-
-  p_data.from = resume_idx_field;
-  p_data.to = rat;
-  cp_walk_tree (&fnbody, replace_proxy, &p_data, NULL);
-
   /* Add in our function body with the co_returns rewritten to final form.  */
   add_stmt (fnbody);
 
@@ -2368,7 +2343,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
   add_stmt (r);
 
   /* Destructors for the things we built explicitly.  */
-  r = build_special_member_call (ap, complete_dtor_identifier, NULL,
+  r = build_special_member_call (promise_proxy, complete_dtor_identifier, NULL,
 				 promise_type, LOOKUP_NORMAL,
 				 tf_warning_or_error);
   add_stmt (r);
@@ -2381,7 +2356,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
   /* Here deallocate the frame (if we allocated it), which we will have at
      present.  */
   tree fnf_m
-    = lookup_member (coro_frame_type, get_identifier ("__frame_needs_free"), 1,
+    = lookup_member (coro_frame_type, coro_frame_needs_free_id, 1,
 		     0, tf_warning_or_error);
   tree fnf2_x = build_class_member_access_expr (actor_frame, fnf_m, NULL_TREE,
 						false, tf_warning_or_error);
@@ -2460,18 +2435,10 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
   gcc_checking_assert (maybe_cleanup_point_expr_void (r) == r);
   add_stmt (r);
 
-  /* We will need to know which resume point number should be encoded.  */
-  tree res_idx_m
-    = lookup_member (coro_frame_type, resume_idx_name,
-		     /*protect=*/1, /*want_type=*/0, tf_warning_or_error);
-  tree resume_pt_number
-    = build_class_member_access_expr (actor_frame, res_idx_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, NULL_TREE,
+  coro_aw_data data = {actor, actor_fp, resume_idx_var, NULL_TREE,
 		       ash, del_promise_label, ret_label,
 		       continue_label, continuation, 2};
   cp_walk_tree (&actor_body, await_statement_expander, &data, NULL);
@@ -2485,7 +2452,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
 }
 
 /* The prototype 'destroy' function :
-   frame->__resume_at |= 1;
+   frame->__Coro_resume_index |= 1;
    actor (frame);  */
 
 static void
@@ -2504,11 +2471,10 @@ build_destroy_fn (location_t loc, tree coro_frame_type, tree destroy,
 
   tree destr_frame = build1 (INDIRECT_REF, coro_frame_type, destr_fp);
 
-  tree resume_idx_name = get_identifier ("__resume_at");
-  tree rat_field = lookup_member (coro_frame_type, resume_idx_name, 1, 0,
-				  tf_warning_or_error);
-  tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, destr_frame,
-		     rat_field, NULL_TREE);
+  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_id,
+				  1, 0, tf_warning_or_error);
+  tree rat = build3 (COMPONENT_REF, short_unsigned_type_node,
+			 destr_frame, rat_field, NULL_TREE);
 
   /* _resume_at |= 1 */
   tree dstr_idx = build2 (BIT_IOR_EXPR, short_unsigned_type_node, rat,
@@ -3927,6 +3893,7 @@ register_local_var_uses (tree *stmt, int *do_subtree, void *d)
 	     identify them in the coroutine frame.  */
 	  tree lvname = DECL_NAME (lvar);
 	  char *buf;
+
 	  /* The outermost bind scope contains the artificial variables that
 	     we inject to implement the coro state machine.  We want to be able
 	     to inspect these in debugging.  */
@@ -3936,7 +3903,7 @@ register_local_var_uses (tree *stmt, int *do_subtree, void *d)
 	    buf = xasprintf ("%s_%u_%u", IDENTIFIER_POINTER (lvname),
 			     lvd->nest_depth, lvd->bind_indx);
 	  else
-	    buf = xasprintf ("_D%u.%u.%u", DECL_UID (lvar), lvd->nest_depth,
+	    buf = xasprintf ("_D%u_%u_%u", DECL_UID (lvar), lvd->nest_depth,
 			     lvd->bind_indx);
 	  /* TODO: Figure out if we should build a local type that has any
 	     excess alignment or size from the original decl.  */
@@ -4023,8 +3990,8 @@ coro_build_actor_or_destroy_function (tree orig, tree fn_type,
 
 static tree
 coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
-			    tree resume_fn_ptr_type, tree& resume_fn_field,
-			    tree& resume_idx_field, tree& fs_label)
+			    tree resume_fn_ptr_type,
+			    tree& resume_idx_var, tree& fs_label)
 {
   /* This will be our new outer scope.  */
   tree update_body = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL);
@@ -4057,7 +4024,6 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
 
   /* 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);
 
@@ -4068,24 +4034,61 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
   tree return_void
     = get_coroutine_return_void_expr (current_function_decl, fn_start, false);
 
+  /* The pointer to the resume function.  */
+  tree resume_fn_ptr
+    = coro_build_artificial_var (fn_start, coro_resume_fn_id,
+				 resume_fn_ptr_type, orig, NULL_TREE);
+  DECL_CHAIN (resume_fn_ptr) = var_list;
+  var_list = resume_fn_ptr;
+  add_decl_expr (resume_fn_ptr);
+
   /* We will need to be able to set the resume function pointer to nullptr
      to signal that the coroutine is 'done'.  */
-  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);
-  /* Likewise, the resume index needs to be reset.  */
-  resume_idx_field
-    = build_lang_decl (VAR_DECL, get_identifier ("resume.index.proxy"),
-		       short_unsigned_type_node);
-  DECL_ARTIFICIAL (resume_idx_field) = true;
-  tree zero_resume_idx = build_int_cst (short_unsigned_type_node, 0);
-  zero_resume_idx = build2 (INIT_EXPR, short_unsigned_type_node,
-			    resume_idx_field, zero_resume_idx);
+
+  /* The pointer to the destroy function.  */
+  tree var = coro_build_artificial_var (fn_start, coro_destroy_fn_id,
+					resume_fn_ptr_type, orig, NULL_TREE);
+  DECL_CHAIN (var) = var_list;
+  var_list = var;
+  add_decl_expr (var);
+
+  /* The promise was created on demand when parsing we now link it into
+      our scope.  */
+  tree promise = get_coroutine_promise_proxy (orig);
+  DECL_CONTEXT (promise) = orig;
+  DECL_SOURCE_LOCATION (promise) = fn_start;
+  DECL_CHAIN (promise) = var_list;
+  var_list = promise;
+  add_decl_expr (promise);
+
+  /* We need a handle to this coroutine, which is passed to every
+     await_suspend().  This was created on demand when parsing we now link it
+     into our scope.  */
+  var = get_coroutine_self_handle_proxy (orig);
+  DECL_CONTEXT (var) = orig;
+  DECL_SOURCE_LOCATION (var) = fn_start;
+  DECL_CHAIN (var) = var_list;
+  var_list = var;
+  add_decl_expr (var);
+
+
+  /* We create a resume index, this is initialized in the ramp.  */
+  resume_idx_var
+    = coro_build_artificial_var (fn_start, coro_resume_index_id,
+				 short_unsigned_type_node, orig, NULL_TREE);
+  DECL_CHAIN (resume_idx_var) = var_list;
+  var_list = resume_idx_var;
+  add_decl_expr (resume_idx_var);
+
+  /* If the coroutine has a frame that needs to be freed, this will be set by
+     the ramp.  */
+  var = coro_build_artificial_var (fn_start, coro_frame_needs_free_id,
+				   boolean_type_node, orig, NULL_TREE);
+  DECL_CHAIN (var) = var_list;
+  var_list = var;
+  add_decl_expr (var);
 
   if (flag_exceptions)
     {
@@ -4097,8 +4100,7 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
       /* Create and initialize the initial-await-resume-called variable per
 	 [dcl.fct.def.coroutine] / 5.3.  */
       tree i_a_r_c
-	= coro_build_artificial_var (fn_start,
-				     "_Coro_initial_await_resume_called",
+	= coro_build_artificial_var (fn_start, coro_frame_i_a_r_c_id,
 				     boolean_type_node, orig,
 				     boolean_false_node);
       DECL_CHAIN (i_a_r_c) = var_list;
@@ -4151,10 +4153,14 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
 	 If the unhandled exception method returns, then we continue
 	 to the final await expression (which duplicates the clearing of
 	 the field). */
-      finish_expr_stmt (zero_resume);
-      finish_expr_stmt (zero_resume_idx);
-      ueh = maybe_cleanup_point_expr_void (ueh);
-      add_stmt (ueh);
+      tree r = build2 (MODIFY_EXPR, resume_fn_ptr_type, resume_fn_ptr,
+		       zero_resume);
+      finish_expr_stmt (r);
+      tree short_zero = build_int_cst (short_unsigned_type_node, 0);
+      r = build2 (MODIFY_EXPR, short_unsigned_type_node, resume_idx_var,
+		  short_zero);
+      finish_expr_stmt (r);
+      finish_expr_stmt (ueh);
       finish_handler (handler);
       TRY_HANDLERS (tcb) = pop_stmt_list (TRY_HANDLERS (tcb));
     }
@@ -4189,6 +4195,8 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
   /* 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.  */
+  zero_resume = build2 (MODIFY_EXPR, resume_fn_ptr_type, resume_fn_ptr,
+			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));
@@ -4216,15 +4224,15 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
  declare a dummy coro frame.
  struct _R_frame {
   using handle_type = coro::coroutine_handle<coro1::promise_type>;
-  void (*__resume)(_R_frame *);
-  void (*__destroy)(_R_frame *);
-  coro1::promise_type __p;
-  bool frame_needs_free; free the coro frame mem if set.
-  bool i_a_r_c; [dcl.fct.def.coroutine] / 5.3
-  short __resume_at;
-  handle_type self_handle;
-  (maybe) parameter copies.
-  (maybe) local variables saved (including awaitables)
+  void (*_Coro_resume_fn)(_R_frame *);
+  void (*_Coro_destroy_fn)(_R_frame *);
+  coro1::promise_type _Coro_promise;
+  bool _Coro_frame_needs_free; free the coro frame mem if set.
+  bool _Coro_i_a_r_c; [dcl.fct.def.coroutine] / 5.3
+  short _Coro_resume_index;
+  handle_type _Coro_self_handle;
+  parameter copies (were required).
+  local variables saved (including awaitables)
   (maybe) trailing space.
  };  */
 
@@ -4316,7 +4324,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
 
   /* 2. Types we need to define or look up.  */
 
-  tree fr_name = get_fn_local_identifier (orig, "frame");
+  tree fr_name = get_fn_local_identifier (orig, "Frame");
   tree coro_frame_type = xref_tag (record_type, fr_name);
   DECL_CONTEXT (TYPE_NAME (coro_frame_type)) = current_scope ();
   tree coro_frame_ptr = build_pointer_type (coro_frame_type);
@@ -4333,33 +4341,16 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
   /* Construct the wrapped function body; we will analyze this to determine
      the requirements for the coroutine frame.  */
 
-  tree resume_fn_field = NULL_TREE;
-  tree resume_idx_field = NULL_TREE;
+  tree resume_idx_var = NULL_TREE;
   tree fs_label = NULL_TREE;
   fnbody = coro_rewrite_function_body (fn_start, fnbody, orig,
-				       act_des_fn_ptr, resume_fn_field,
-				       resume_idx_field, fs_label);
+				       act_des_fn_ptr,
+				       resume_idx_var, fs_label);
   /* Build our dummy coro frame layout.  */
   coro_frame_type = begin_class_definition (coro_frame_type);
 
+  /* The fields for the coro frame.  */
   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);
-  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 resume_idx_name
-    = coro_make_frame_entry (&field_list, "__resume_at",
-			     short_unsigned_type_node, fn_start);
-
-  /* We need a handle to this coroutine, which is passed to every
-     await_suspend().  There's no point in creating it over and over.  */
-  (void) coro_make_frame_entry (&field_list, "__self_h", handle_type, fn_start);
 
   /* Now add in fields for function params (if there are any).
      We do not attempt elision of copies at this stage, we do analyze the
@@ -4417,14 +4408,14 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
 	  if (DECL_NAME (arg))
 	    {
 	      tree pname = DECL_NAME (arg);
-	      buf = xasprintf ("__parm.%s", IDENTIFIER_POINTER (pname));
+	      buf = xasprintf ("_P_%s", IDENTIFIER_POINTER (pname));
 	    }
 	  else
-	    buf = xasprintf ("__unnamed_parm.%d", no_name_parm++);
+	    buf = xasprintf ("_P_unnamed_%d", no_name_parm++);
 
 	  if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (parm.frame_type))
 	    {
-	      char *gbuf = xasprintf ("%s.live", buf);
+	      char *gbuf = xasprintf ("%s_live", buf);
 	      parm.guard_var
 		= build_lang_decl (VAR_DECL, get_identifier (gbuf),
 				   boolean_type_node);
@@ -4761,8 +4752,8 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
 
   /* 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 (coro_frame_type, fnf_name, 1, 0, tf_warning_or_error);
+  tree fnf_m = lookup_member (coro_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 = build2 (INIT_EXPR, boolean_type_node, fnf_x, boolean_true_node);
@@ -4773,24 +4764,22 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
 
   tree actor_addr = build1 (ADDR_EXPR, act_des_fn_ptr, actor);
   tree resume_m
-    = lookup_member (coro_frame_type, resume_name,
+    = lookup_member (coro_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 = build2_loc (fn_start, INIT_EXPR, act_des_fn_ptr, resume_x, actor_addr);
-  r = coro_build_cvt_void_expr_stmt (r, fn_start);
-  add_stmt (r);
+  finish_expr_stmt (r);
 
   tree destroy_addr = build1 (ADDR_EXPR, act_des_fn_ptr, destroy);
   tree destroy_m
-    = lookup_member (coro_frame_type, destroy_name,
+    = lookup_member (coro_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 = build2_loc (fn_start, INIT_EXPR, act_des_fn_ptr, destroy_x, destroy_addr);
-  r = coro_build_cvt_void_expr_stmt (r, fn_start);
-  add_stmt (r);
+  finish_expr_stmt (r);
 
   /* [dcl.fct.def.coroutine] /13
      When a coroutine is invoked, a copy is created for each coroutine
@@ -4881,7 +4870,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
 
   /* Set up the promise.  */
   tree promise_m
-    = lookup_member (coro_frame_type, promise_name,
+    = lookup_member (coro_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,
@@ -5027,9 +5016,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
 			      boolean_type_node);
       finish_expr_stmt (r);
     }
-  /* Initialize the resume_idx_name to 0, meaning "not started".  */
+  /* Initialize the resume_idx_var to 0, meaning "not started".  */
   tree resume_idx_m
-    = lookup_member (coro_frame_type, resume_idx_name,
+    = lookup_member (coro_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,
@@ -5172,9 +5161,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
   push_deferring_access_checks (dk_no_check);
 
   /* Build the actor...  */
-  build_actor_fn (fn_start, coro_frame_type, actor, fnbody, orig, param_uses,
-		  &local_var_uses, param_dtor_list, resume_fn_field,
-		  resume_idx_field, body_aw_points.await_number, frame_size);
+  build_actor_fn (fn_start, coro_frame_type, actor, fnbody, orig,
+		  &local_var_uses, param_dtor_list,
+		  resume_idx_var, body_aw_points.await_number, frame_size);
 
   /* Destroyer ... */
   build_destroy_fn (fn_start, coro_frame_type, destroy, actor);
-- 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH 7/8 v2] coroutines: Make proxy vars for the function arg copies.
  2021-09-03 14:23                 ` Iain Sandoe
@ 2021-09-05 19:50                   ` Iain Sandoe
  2021-09-07 17:26                     ` Jason Merrill
  0 siblings, 1 reply; 28+ messages in thread
From: Iain Sandoe @ 2021-09-05 19:50 UTC (permalink / raw)
  To: Jason Merrill; +Cc: GCC Patches

Hello Jason,

> On 3 Sep 2021, at 15:23, Iain Sandoe <iain@sandoe.co.uk> wrote:
>> On 3 Sep 2021, at 15:07, Jason Merrill via Gcc-patches <gcc-patches@gcc.gnu.org> wrote:
>> 
>> On 9/1/21 6:56 AM, Iain Sandoe wrote:
>>> This adds top level proxy variables for the coroutine frame

>>> +	  add_decl_expr (parm_i->copy_var);
>> 
>> Are these getting DECL_VALUE_EXPR somewhere?
> 
> Yes - all variables in the outermost bind expression will be allocated a frame slot, and gain a DECL_VALUE_EXPR to point to it.

Amended comments to say this.
> 
>>> +      	}
>>> +
>>> +      /* Now replace all uses of the parms in the function body with the local
>>> +	 vars.  */
>> 
>> I think the following old comment still applies to how 'visited' is used, and should be adapted here as well:
> 
> Yes, will do.
done

OK now?
thanks
Iain

[PATCH] coroutines: Make proxy vars for the function arg copies.

This adds top level proxy variables for the coroutine frame
copies of the original function args.  These are then available
in the debugger to refer to the frame copies.  We rewrite the
function body to use the copies, since the original parms will
no longer be in scope when the coroutine is running.

Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>

gcc/cp/ChangeLog:

	* coroutines.cc (struct param_info): Add copy_var.
	(build_actor_fn): Use simplified param references.
	(register_param_uses): Likewise.
	(rewrite_param_uses): Likewise.
	(analyze_fn_parms): New function.
	(coro_rewrite_function_body): Add proxies for the fn
	parameters to the outer bind scope of the rewritten code.
	(morph_fn_to_coro): Use simplified version of param ref.
---
 gcc/cp/coroutines.cc | 251 +++++++++++++++++++++----------------------
 1 file changed, 121 insertions(+), 130 deletions(-)

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index cd774b78ae0..d2cc2e73c89 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -1964,6 +1964,7 @@ transform_await_wrapper (tree *stmt, int *do_subtree, void *d)
 struct param_info
 {
   tree field_id;     /* The name of the copy in the coroutine frame.  */
+  tree copy_var;     /* The local var proxy for the frame copy.  */
   vec<tree *> *body_uses; /* Worklist of uses, void if there are none.  */
   tree frame_type;   /* The type used to represent this parm in the frame.  */
   tree orig_type;    /* The original type of the parm (not as passed).  */
@@ -2169,36 +2170,6 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
   /* Declare the continuation handle.  */
   add_decl_expr (continuation);
 
-  /* Re-write param references in the body, no code should be generated
-     here.  */
-  if (DECL_ARGUMENTS (orig))
-    {
-      tree arg;
-      for (arg = DECL_ARGUMENTS (orig); arg != NULL; arg = DECL_CHAIN (arg))
-	{
-	  bool existed;
-	  param_info &parm = param_uses->get_or_insert (arg, &existed);
-	  if (!parm.body_uses)
-	    continue; /* Wasn't used in the original function body.  */
-
-	  tree fld_ref = lookup_member (coro_frame_type, parm.field_id,
-					/*protect=*/1, /*want_type=*/0,
-					tf_warning_or_error);
-	  tree fld_idx = build3_loc (loc, COMPONENT_REF, parm.frame_type,
-				     actor_frame, fld_ref, NULL_TREE);
-
-	  /* We keep these in the frame as a regular pointer, so convert that
-	   back to the type expected.  */
-	  if (parm.pt_ref)
-	    fld_idx = build1_loc (loc, CONVERT_EXPR, TREE_TYPE (arg), fld_idx);
-
-	  int i;
-	  tree *puse;
-	  FOR_EACH_VEC_ELT (*parm.body_uses, i, puse)
-	    *puse = fld_idx;
-	}
-    }
-
   /* Re-write local vars, similarly.  */
   local_vars_transform xform_vars_data
     = {actor, actor_frame, coro_frame_type, loc, local_var_uses};
@@ -3771,11 +3742,11 @@ struct param_frame_data
   bool param_seen;
 };
 
-/* A tree-walk callback that records the use of parameters (to allow for
-   optimizations where handling unused parameters may be omitted).  */
+/* A tree walk callback that rewrites each parm use to the local variable
+   that represents its copy in the frame.  */
 
 static tree
-register_param_uses (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
+rewrite_param_uses (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
 {
   param_frame_data *data = (param_frame_data *) d;
 
@@ -3783,7 +3754,7 @@ register_param_uses (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
   if (TREE_CODE (*stmt) == VAR_DECL && DECL_HAS_VALUE_EXPR_P (*stmt))
     {
       tree t = DECL_VALUE_EXPR (*stmt);
-      return cp_walk_tree (&t, register_param_uses, d, NULL);
+      return cp_walk_tree (&t, rewrite_param_uses, d, NULL);
     }
 
   if (TREE_CODE (*stmt) != PARM_DECL)
@@ -3797,16 +3768,88 @@ register_param_uses (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
   param_info &parm = data->param_uses->get_or_insert (*stmt, &existed);
   gcc_checking_assert (existed);
 
-  if (!parm.body_uses)
+  *stmt = parm.copy_var;
+  return NULL_TREE;
+}
+
+/* Build up a set of info that determines how each param copy will be
+   handled.  */
+
+static hash_map<tree, param_info> *
+analyze_fn_parms (tree orig)
+{
+  if (!DECL_ARGUMENTS (orig))
+    return NULL;
+
+  hash_map<tree, param_info> *param_uses = new hash_map<tree, param_info>;
+
+  /* Build a hash map with an entry for each param.
+     The key is the param tree.
+     Then we have an entry for the frame field name.
+     Then a cache for the field ref when we come to use it.
+     Then a tree list of the uses.
+     The second two entries start out empty - and only get populated
+     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))
     {
-      vec_alloc (parm.body_uses, 4);
-      parm.body_uses->quick_push (stmt);
-      data->param_seen = true;
+      bool existed;
+      param_info &parm = param_uses->get_or_insert (arg, &existed);
+      gcc_checking_assert (!existed);
+      parm.body_uses = NULL;
+      tree actual_type = TREE_TYPE (arg);
+      actual_type = complete_type_or_else (actual_type, orig);
+      if (actual_type == NULL_TREE)
+	actual_type = error_mark_node;
+      parm.orig_type = actual_type;
+      parm.by_ref = parm.pt_ref = parm.rv_ref =  false;
+      if (TREE_CODE (actual_type) == REFERENCE_TYPE)
+	{
+	  /* If the user passes by reference, then we will save the
+	     pointer to the original.  As noted in
+	     [dcl.fct.def.coroutine] / 13, if the lifetime of the
+	     referenced item ends and then the coroutine is resumed,
+	     we have UB; well, the user asked for it.  */
+	  if (TYPE_REF_IS_RVALUE (actual_type))
+		parm.rv_ref = true;
+	  else
+		parm.pt_ref = true;
+	}
+      else if (TYPE_REF_P (DECL_ARG_TYPE (arg)))
+	parm.by_ref = true;
+
+      parm.frame_type = actual_type;
+
+      parm.this_ptr = is_this_parameter (arg);
+      parm.lambda_cobj = lambda_p && DECL_NAME (arg) == closure_identifier;
+
+      tree name = DECL_NAME (arg);
+      if (!name)
+	{
+	  char *buf = xasprintf ("_Coro_unnamed_parm_%d", no_name_parm++);
+	  name = get_identifier (buf);
+	  free (buf);
+	}
+      parm.field_id = name;
+
+      if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (parm.frame_type))
+	{
+	  char *buf = xasprintf ("_Coro_%s_live", IDENTIFIER_POINTER (name));
+	  parm.guard_var = build_lang_decl (VAR_DECL, get_identifier (buf),
+					    boolean_type_node);
+	  free (buf);
+	  DECL_ARTIFICIAL (parm.guard_var) = true;
+	  DECL_CONTEXT (parm.guard_var) = orig;
+	  DECL_INITIAL (parm.guard_var) = boolean_false_node;
+	  parm.trivial_dtor = false;
+	}
+      else
+	parm.trivial_dtor = true;
     }
-  else
-    parm.body_uses->safe_push (stmt);
 
-  return NULL_TREE;
+  return param_uses;
 }
 
 /* Small helper for the repetitive task of adding a new field to the coro
@@ -3990,6 +4033,7 @@ coro_build_actor_or_destroy_function (tree orig, tree fn_type,
 
 static tree
 coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
+			    hash_map<tree, param_info> *param_uses,
 			    tree resume_fn_ptr_type,
 			    tree& resume_idx_var, tree& fs_label)
 {
@@ -4073,6 +4117,39 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
   var_list = var;
   add_decl_expr (var);
 
+  /* If we have function parms, then these will be copied to the coroutine
+     frame.  Create a local (proxy) variable for each parm, since the original
+     parms will be out of scope once the ramp has finished. The proxy vars will
+     get DECL_VALUE_EXPRs pointing to the frame copies, so that we can interact
+     with them in the debugger.  */
+  if (param_uses)
+    {
+      gcc_checking_assert (DECL_ARGUMENTS (orig));
+      /* Add a local var for each parm.  */
+      for (tree arg = DECL_ARGUMENTS (orig); arg != NULL;
+	   arg = DECL_CHAIN (arg))
+	{
+	  param_info *parm_i = param_uses->get (arg);
+	  gcc_checking_assert (parm_i);
+	  parm_i->copy_var
+	    = build_lang_decl (VAR_DECL, parm_i->field_id, TREE_TYPE (arg));
+	  DECL_SOURCE_LOCATION (parm_i->copy_var) = DECL_SOURCE_LOCATION (arg);
+	  DECL_CONTEXT (parm_i->copy_var) = orig;
+	  DECL_ARTIFICIAL (parm_i->copy_var) = true;
+	  DECL_CHAIN (parm_i->copy_var) = var_list;
+	  var_list = parm_i->copy_var;
+	  add_decl_expr (parm_i->copy_var);
+      	}
+
+      /* Now replace all uses of the parms in the function body with the proxy
+	 vars.  We want to this to apply to every instance of param's use, so
+	 don't include a 'visited' hash_set on the tree walk, however we will
+	 arrange to visit each containing expression only once.  */
+      hash_set<tree *> visited;
+      param_frame_data param_data = {NULL, param_uses,
+				     &visited, fn_start, false};
+      cp_walk_tree (&fnbody, rewrite_param_uses, &param_data, NULL);
+    }
 
   /* We create a resume index, this is initialized in the ramp.  */
   resume_idx_var
@@ -4343,7 +4420,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
 
   tree resume_idx_var = NULL_TREE;
   tree fs_label = NULL_TREE;
-  fnbody = coro_rewrite_function_body (fn_start, fnbody, orig,
+  hash_map<tree, param_info> *param_uses = analyze_fn_parms (orig);
+
+  fnbody = coro_rewrite_function_body (fn_start, fnbody, orig, param_uses,
 				       act_des_fn_ptr,
 				       resume_idx_var, fs_label);
   /* Build our dummy coro frame layout.  */
@@ -4352,94 +4431,6 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
   /* The fields for the coro frame.  */
   tree field_list = NULL_TREE;
 
-  /* Now add in fields for function params (if there are any).
-     We do not attempt elision of copies at this stage, we do analyze the
-     uses and build worklists to replace those when the state machine is
-     lowered.  */
-
-  hash_map<tree, param_info> *param_uses = NULL;
-  if (DECL_ARGUMENTS (orig))
-    {
-      /* Build a hash map with an entry for each param.
-	  The key is the param tree.
-	  Then we have an entry for the frame field name.
-	  Then a cache for the field ref when we come to use it.
-	  Then a tree list of the uses.
-	  The second two entries start out empty - and only get populated
-	  when we see uses.  */
-      param_uses = new hash_map<tree, param_info>;
-      bool lambda_p = LAMBDA_FUNCTION_P (orig);
-
-      unsigned no_name_parm = 0;
-      for (tree arg = DECL_ARGUMENTS (orig); arg != NULL;
-	   arg = DECL_CHAIN (arg))
-	{
-	  bool existed;
-	  param_info &parm = param_uses->get_or_insert (arg, &existed);
-	  gcc_checking_assert (!existed);
-	  parm.body_uses = NULL;
-	  tree actual_type = TREE_TYPE (arg);
-	  actual_type = complete_type_or_else (actual_type, orig);
-	  if (actual_type == NULL_TREE)
-	    actual_type = error_mark_node;
-	  parm.orig_type = actual_type;
-	  parm.by_ref = parm.pt_ref = parm.rv_ref =  false;
-	  if (TREE_CODE (actual_type) == REFERENCE_TYPE)
-	    {
-	      /* If the user passes by reference, then we will save the
-		 pointer to the original.  As noted in
-		 [dcl.fct.def.coroutine] / 13, if the lifetime of the
-		 referenced item ends and then the coroutine is resumed,
-		 we have UB; well, the user asked for it.  */
-	      if (TYPE_REF_IS_RVALUE (actual_type))
-		parm.rv_ref = true;
-	      else
-		parm.pt_ref = true;
-	    }
-	  else if (TYPE_REF_P (DECL_ARG_TYPE (arg)))
-	    parm.by_ref = true;
-
-	  parm.frame_type = actual_type;
-
-	  parm.this_ptr = is_this_parameter (arg);
-	  parm.lambda_cobj = lambda_p && DECL_NAME (arg) == closure_identifier;
-
-	  char *buf;
-	  if (DECL_NAME (arg))
-	    {
-	      tree pname = DECL_NAME (arg);
-	      buf = xasprintf ("_P_%s", IDENTIFIER_POINTER (pname));
-	    }
-	  else
-	    buf = xasprintf ("_P_unnamed_%d", no_name_parm++);
-
-	  if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (parm.frame_type))
-	    {
-	      char *gbuf = xasprintf ("%s_live", buf);
-	      parm.guard_var
-		= build_lang_decl (VAR_DECL, get_identifier (gbuf),
-				   boolean_type_node);
-	      free (gbuf);
-	      DECL_ARTIFICIAL (parm.guard_var) = true;
-	      DECL_INITIAL (parm.guard_var) = boolean_false_node;
-	      parm.trivial_dtor = false;
-	    }
-	  else
-	    parm.trivial_dtor = true;
-	  parm.field_id = coro_make_frame_entry
-	    (&field_list, buf, actual_type, DECL_SOURCE_LOCATION (arg));
-	  free (buf);
-	}
-
-      /* We want to record every instance of param's use, so don't include
-	 a 'visited' hash_set on the tree walk, but only record a containing
-	 expression once.  */
-      hash_set<tree *> visited;
-      param_frame_data param_data
-	= {&field_list, param_uses, &visited, fn_start, false};
-      cp_walk_tree (&fnbody, register_param_uses, &param_data, NULL);
-    }
-
   /* 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.  */
-- 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 6/8] coroutines: Convert implementation variables to debug-friendly form.
  2021-09-05 19:47                     ` Iain Sandoe
@ 2021-09-07 17:23                       ` Jason Merrill
  0 siblings, 0 replies; 28+ messages in thread
From: Jason Merrill @ 2021-09-07 17:23 UTC (permalink / raw)
  To: Iain Sandoe; +Cc: GCC Patches

On 9/5/21 3:47 PM, Iain Sandoe wrote:
> Hello Jason,
> 
> The patch below is a squashed version of:
> 
> (approved) [PATCH 4/8] coroutines: Make some of the artificial names more  debugger-friendly.
>                    [PATCH 5/8] coroutines: Define and populate accessors for debug  state.
>                    [PATCH 6/8] coroutines: Convert implementation variables to  debug-friendly form.
> (approved) [PATCH 8/8] coroutines: Make the continue handle visible to debug.
> 
> 
> [PATCH 6/8] coroutines: Convert implementation variables to debug-friendly form.
> 
>> On 3 Sep 2021, at 15:21, Iain Sandoe <iain@sandoe.co.uk> wrote:
>>> On 3 Sep 2021, at 15:12, Jason Merrill <jason@redhat.com> wrote:
>>>
>>> On 9/3/21 9:56 AM, Iain Sandoe wrote:
>>>>> On 3 Sep 2021, at 14:52, Jason Merrill <jason@redhat.com> wrote:
>>>>>
>>>>> On 9/1/21 6:55 AM, Iain Sandoe wrote:
> 
>>>>>> 	(morph_fn_to_coro): Likewise.
>>>>>
>>>>> Hmm, this patch doesn't seem to match the description and ChangeLog entry other than in the names of the functions changed.
>>>> with 20:20 hindsight I should have squashed the (several) patches related to the implementation symbols,
>>>> I’ll redo the description - essentially, this is just making use of the simplification available because we now have pre-defined values for the field names.
>>>
>>> I can see how that describes a few lines in this patch, but not for instance the change to transform_await_expr, which seems to have nothing to do with names?
>>
>> it’s indirect indeed - but the changes we’ve made to the variable handling mean that we no longer need to rewrite the
>> proxy vars into their frame->offset; that is handled by the DECL_VALUE_EXPR (as for user’s vars ) - the change to transform_await_expr is removing the now defunct substitution (and poorly described, sorry).
> 
> now stated explicitly in the comments and in the commit log.

So that sounds like it goes with patch 1, rather than 4/5?  But it's 
fine as is.

>>> But yes, moving the changed lines that just use the variables from the previous patch into that commit sounds good.  I use rebase -i for that sort of thing all the time.
>>
>> yeah, me too -  I realised too late that this series could have had more squashing - if it would make things easier I could do that for the patches related to implemenation variables .. - which would include patch 8 (but not patch 7 which is related to parms only)
> 
> squashing the series should make the changes clearer.
> 
> [PATCH 5/8] coroutines: Define and populate accessors for debug  state.
> 
>>>>> +static GTY(()) tree coro_frame_needs_free_field;
>>>>> +static GTY(()) tree coro_resume_index_field;
>>>>> +static GTY(()) tree coro_self_handle_field;
>>>>
>>>> Since these are identifiers, not FIELD_DECLs, calling them *_field seems misleading.
>>> I could append _id or _name .. they were just getting long already.
>>> (they are names of fields, so that would not be misleading)
>>
>> _id works for me, either with or without the _field.
>>
> 
> Done
> 
> OK now?

OK, thanks.

> thanks
> Iain
> 
> ======
> 
> [PATCH] coroutines: Expose implementation state to the debugger.
> 
> In the process of transforming a coroutine into the separate representation
> as the ramp function and a state machine, we generate some variables that
> are of interest to a user during debugging.  Any variable that is persistent
> for the execution of the coroutine is placed into the coroutine frame.
> 
> In particular:
>    The promise object.
>    The function pointers for the resumer and destroyer.
>    The current resume index (suspend point).
>    The handle that represents this coroutine 'self handle'.
>    Any handle provided for a continuation coroutine.
>    Whether the coroutine frame is allocated and needs to be freed.
> 
> Visibility of some of these has already been requested by end users.
> 
> This patch ensures that such variables have names that are usable in a
> debugger, but are in the reserved namespace for the implementation (they
> all begin with _Coro_).  The identifiers are generated lazily when the
> first coroutine is encountered.
> 
> We place the variables into the outermost bind expression and then add a
> DECL_VALUE_EXPR to each that points to the frame entry.
> 
> These changes simplify the handling of the variables in the body of the
> function (in particular, the use of the DECL_VALUE_EXPR means that we now
> no longer need to rewrite proxies for the promise and coroutine handles into
> the frame->offset form).
> 
> Partial improvement to debugging (PR c++/99215).
> 
> Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
> 
> gcc/cp/ChangeLog:
> 
> 	* coroutines.cc (coro_resume_fn_id, coro_destroy_fn_id,
> 	coro_promise_id, coro_frame_needs_free_id, coro_resume_index_id,
> 	coro_self_handle_id, coro_actor_continue_id,
> 	coro_frame_i_a_r_c_id): New.
> 	(coro_init_identifiers): Initialize new name identifiers.
> 	(coro_promise_type_found_p): Use pre-built identifiers.
> 	(struct await_xform_data): Remove unused fields.
> 	(transform_await_expr): Delete code that is now unused.
> 	(build_actor_fn): Simplify interface, use pre-built identifiers and
> 	remove transforms that are no longer needed.
> 	(build_destroy_fn): Use revised field names.
> 	(register_local_var_uses): Use pre-built identifiers.
> 	(coro_rewrite_function_body): Simplify interface, use pre-built
> 	identifiers.  Generate proxy vars in the outer bind expr scope for the
> 	implementation state that we wish to expose.
> 	(morph_fn_to_coro): Adjust comments for new variable names, use pre-
> 	built identifiers.  Remove unused code to generate frame entries for
> 	the implementation state.  Adjust call for build_actor_fn.
> ---
>   gcc/cp/coroutines.cc | 285 +++++++++++++++++++++----------------------
>   1 file changed, 137 insertions(+), 148 deletions(-)
> 
> diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
> index 9ab2be04266..cd774b78ae0 100644
> --- a/gcc/cp/coroutines.cc
> +++ b/gcc/cp/coroutines.cc
> @@ -215,7 +215,19 @@ static GTY(()) tree coro_await_ready_identifier;
>   static GTY(()) tree coro_await_suspend_identifier;
>   static GTY(()) tree coro_await_resume_identifier;
>   
> -/* Create the identifiers used by the coroutines library interfaces.  */
> +/* Accessors for the coroutine frame state used by the implementation.  */
> +
> +static GTY(()) tree coro_resume_fn_id;
> +static GTY(()) tree coro_destroy_fn_id;
> +static GTY(()) tree coro_promise_id;
> +static GTY(()) tree coro_frame_needs_free_id;
> +static GTY(()) tree coro_resume_index_id;
> +static GTY(()) tree coro_self_handle_id;
> +static GTY(()) tree coro_actor_continue_id;
> +static GTY(()) tree coro_frame_i_a_r_c_id;
> +
> +/* Create the identifiers used by the coroutines library interfaces and
> +   the implementation frame state.  */
>   
>   static void
>   coro_init_identifiers ()
> @@ -241,6 +253,16 @@ coro_init_identifiers ()
>     coro_await_ready_identifier = get_identifier ("await_ready");
>     coro_await_suspend_identifier = get_identifier ("await_suspend");
>     coro_await_resume_identifier = get_identifier ("await_resume");
> +
> +  /* Coroutine state frame field accessors.  */
> +  coro_resume_fn_id = get_identifier ("_Coro_resume_fn");
> +  coro_destroy_fn_id = get_identifier ("_Coro_destroy_fn");
> +  coro_promise_id = get_identifier ("_Coro_promise");
> +  coro_frame_needs_free_id = get_identifier ("_Coro_frame_needs_free");
> +  coro_frame_i_a_r_c_id = get_identifier ("_Coro_initial_await_resume_called");
> +  coro_resume_index_id = get_identifier ("_Coro_resume_index");
> +  coro_self_handle_id = get_identifier ("_Coro_self_handle");
> +  coro_actor_continue_id = get_identifier ("_Coro_actor_continue");
>   }
>   
>   /* Trees we only need to set up once.  */
> @@ -513,12 +535,12 @@ coro_promise_type_found_p (tree fndecl, location_t loc)
>         /* Build a proxy for a handle to "self" as the param to
>   	 await_suspend() calls.  */
>         coro_info->self_h_proxy
> -	= build_lang_decl (VAR_DECL, get_identifier ("self_h.proxy"),
> +	= build_lang_decl (VAR_DECL, coro_self_handle_id,
>   			   coro_info->handle_type);
>   
>         /* Build a proxy for the promise so that we can perform lookups.  */
>         coro_info->promise_proxy
> -	= build_lang_decl (VAR_DECL, get_identifier ("promise.proxy"),
> +	= build_lang_decl (VAR_DECL, coro_promise_id,
>   			   coro_info->promise_type);
>   
>         /* Note where we first saw a coroutine keyword.  */
> @@ -1864,10 +1886,6 @@ struct await_xform_data
>   {
>     tree actor_fn;   /* Decl for context.  */
>     tree actor_frame;
> -  tree promise_proxy;
> -  tree real_promise;
> -  tree self_h_proxy;
> -  tree real_self_h;
>   };
>   
>   /* When we built the await expressions, we didn't know the coro frame
> @@ -1888,7 +1906,6 @@ transform_await_expr (tree await_expr, await_xform_data *xform)
>     /* So, on entry, we have:
>        in : CO_AWAIT_EXPR (a, e_proxy, o, awr_call_vector, mode)
>   	  We no longer need a [it had diagnostic value, maybe?]
> -	  We need to replace the promise proxy in all elements
>   	  We need to replace the e_proxy in the awr_call.  */
>   
>     tree coro_frame_type = TREE_TYPE (xform->actor_frame);
> @@ -1914,16 +1931,6 @@ transform_await_expr (tree await_expr, await_xform_data *xform)
>         TREE_OPERAND (await_expr, 1) = as;
>       }
>   
> -  /* Now do the self_handle.  */
> -  data.from = xform->self_h_proxy;
> -  data.to = xform->real_self_h;
> -  cp_walk_tree (&await_expr, replace_proxy, &data, NULL);
> -
> -  /* Now do the promise.  */
> -  data.from = xform->promise_proxy;
> -  data.to = xform->real_promise;
> -  cp_walk_tree (&await_expr, replace_proxy, &data, NULL);
> -
>     return await_expr;
>   }
>   
> @@ -2110,15 +2117,13 @@ coro_get_frame_dtor (tree coro_fp, tree orig, tree frame_size,
>   
>   static void
>   build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
> -		tree orig, hash_map<tree, param_info> *param_uses,
> -		hash_map<tree, local_var_info> *local_var_uses,
> -		vec<tree, va_gc> *param_dtor_list, tree resume_fn_field,
> -		tree resume_idx_field, unsigned body_count, tree frame_size)
> +		tree orig, hash_map<tree, local_var_info> *local_var_uses,
> +		vec<tree, va_gc> *param_dtor_list,
> +		tree resume_idx_var, unsigned body_count, tree frame_size)
>   {
>     verify_stmt_tree (fnbody);
>     /* Some things we inherit from the original function.  */
>     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);
>   
> @@ -2136,11 +2141,12 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>     tree top_block = make_node (BLOCK);
>     BIND_EXPR_BLOCK (actor_bind) = top_block;
>   
> -  tree continuation = coro_build_artificial_var (loc, "_Coro_actor_continue",
> +  tree continuation = coro_build_artificial_var (loc, coro_actor_continue_id,
>   						 void_coro_handle_type, actor,
>   						 NULL_TREE);
>   
>     BIND_EXPR_VARS (actor_bind) = continuation;
> +  BLOCK_VARS (top_block) = BIND_EXPR_VARS (actor_bind) ;
>   
>     /* Link in the block associated with the outer scope of the re-written
>        function body.  */
> @@ -2198,9 +2204,8 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>       = {actor, actor_frame, coro_frame_type, loc, local_var_uses};
>     cp_walk_tree (&fnbody, transform_local_var_uses, &xform_vars_data, NULL);
>   
> -  tree resume_idx_name = get_identifier ("__resume_at");
> -  tree rat_field = lookup_member (coro_frame_type, resume_idx_name, 1, 0,
> -				  tf_warning_or_error);
> +  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_id,
> +				  1, 0, tf_warning_or_error);
>     tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, actor_frame,
>   		     rat_field, NULL_TREE);
>   
> @@ -2302,14 +2307,8 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>     tree r = build_stmt (loc, LABEL_EXPR, actor_begin_label);
>     add_stmt (r);
>   
> -  /* actor's version of the promise.  */
> -  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);
> -
>     /* actor's coroutine 'self handle'.  */
> -  tree ash_m = lookup_member (coro_frame_type, get_identifier ("__self_h"), 1,
> +  tree ash_m = lookup_member (coro_frame_type, coro_self_handle_id, 1,
>   			      0, tf_warning_or_error);
>     tree ash = build_class_member_access_expr (actor_frame, ash_m, NULL_TREE,
>   					     false, tf_warning_or_error);
> @@ -2329,37 +2328,13 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>     /* Now we know the real promise, and enough about the frame layout to
>        decide where to put things.  */
>   
> -  await_xform_data xform
> -    = {actor, actor_frame, promise_proxy, ap, self_h_proxy, ash};
> +  await_xform_data xform = {actor, actor_frame};
>   
>     /* Transform the await expressions in the function body.  Only do each
>        await tree once!  */
>     hash_set<tree> pset;
>     cp_walk_tree (&fnbody, transform_await_wrapper, &xform, &pset);
>   
> -  /* 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);
> -
> -  /* The rewrite of the function adds code to set the __resume field to
> -     nullptr when the coroutine is done and also the index to zero when
> -     calling an unhandled exception.  These are represented by two proxies
> -     in the function, so rewrite them to the proper frame access.  */
> -  tree resume_m
> -    = lookup_member (coro_frame_type, get_identifier ("__resume"),
> -		     /*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);
> -  p_data.from = resume_fn_field;
> -  p_data.to = res_x;
> -  cp_walk_tree (&fnbody, replace_proxy, &p_data, NULL);
> -
> -  p_data.from = resume_idx_field;
> -  p_data.to = rat;
> -  cp_walk_tree (&fnbody, replace_proxy, &p_data, NULL);
> -
>     /* Add in our function body with the co_returns rewritten to final form.  */
>     add_stmt (fnbody);
>   
> @@ -2368,7 +2343,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>     add_stmt (r);
>   
>     /* Destructors for the things we built explicitly.  */
> -  r = build_special_member_call (ap, complete_dtor_identifier, NULL,
> +  r = build_special_member_call (promise_proxy, complete_dtor_identifier, NULL,
>   				 promise_type, LOOKUP_NORMAL,
>   				 tf_warning_or_error);
>     add_stmt (r);
> @@ -2381,7 +2356,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>     /* Here deallocate the frame (if we allocated it), which we will have at
>        present.  */
>     tree fnf_m
> -    = lookup_member (coro_frame_type, get_identifier ("__frame_needs_free"), 1,
> +    = lookup_member (coro_frame_type, coro_frame_needs_free_id, 1,
>   		     0, tf_warning_or_error);
>     tree fnf2_x = build_class_member_access_expr (actor_frame, fnf_m, NULL_TREE,
>   						false, tf_warning_or_error);
> @@ -2460,18 +2435,10 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>     gcc_checking_assert (maybe_cleanup_point_expr_void (r) == r);
>     add_stmt (r);
>   
> -  /* We will need to know which resume point number should be encoded.  */
> -  tree res_idx_m
> -    = lookup_member (coro_frame_type, resume_idx_name,
> -		     /*protect=*/1, /*want_type=*/0, tf_warning_or_error);
> -  tree resume_pt_number
> -    = build_class_member_access_expr (actor_frame, res_idx_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, NULL_TREE,
> +  coro_aw_data data = {actor, actor_fp, resume_idx_var, NULL_TREE,
>   		       ash, del_promise_label, ret_label,
>   		       continue_label, continuation, 2};
>     cp_walk_tree (&actor_body, await_statement_expander, &data, NULL);
> @@ -2485,7 +2452,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>   }
>   
>   /* The prototype 'destroy' function :
> -   frame->__resume_at |= 1;
> +   frame->__Coro_resume_index |= 1;
>      actor (frame);  */
>   
>   static void
> @@ -2504,11 +2471,10 @@ build_destroy_fn (location_t loc, tree coro_frame_type, tree destroy,
>   
>     tree destr_frame = build1 (INDIRECT_REF, coro_frame_type, destr_fp);
>   
> -  tree resume_idx_name = get_identifier ("__resume_at");
> -  tree rat_field = lookup_member (coro_frame_type, resume_idx_name, 1, 0,
> -				  tf_warning_or_error);
> -  tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, destr_frame,
> -		     rat_field, NULL_TREE);
> +  tree rat_field = lookup_member (coro_frame_type, coro_resume_index_id,
> +				  1, 0, tf_warning_or_error);
> +  tree rat = build3 (COMPONENT_REF, short_unsigned_type_node,
> +			 destr_frame, rat_field, NULL_TREE);
>   
>     /* _resume_at |= 1 */
>     tree dstr_idx = build2 (BIT_IOR_EXPR, short_unsigned_type_node, rat,
> @@ -3927,6 +3893,7 @@ register_local_var_uses (tree *stmt, int *do_subtree, void *d)
>   	     identify them in the coroutine frame.  */
>   	  tree lvname = DECL_NAME (lvar);
>   	  char *buf;
> +
>   	  /* The outermost bind scope contains the artificial variables that
>   	     we inject to implement the coro state machine.  We want to be able
>   	     to inspect these in debugging.  */
> @@ -3936,7 +3903,7 @@ register_local_var_uses (tree *stmt, int *do_subtree, void *d)
>   	    buf = xasprintf ("%s_%u_%u", IDENTIFIER_POINTER (lvname),
>   			     lvd->nest_depth, lvd->bind_indx);
>   	  else
> -	    buf = xasprintf ("_D%u.%u.%u", DECL_UID (lvar), lvd->nest_depth,
> +	    buf = xasprintf ("_D%u_%u_%u", DECL_UID (lvar), lvd->nest_depth,
>   			     lvd->bind_indx);
>   	  /* TODO: Figure out if we should build a local type that has any
>   	     excess alignment or size from the original decl.  */
> @@ -4023,8 +3990,8 @@ coro_build_actor_or_destroy_function (tree orig, tree fn_type,
>   
>   static tree
>   coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
> -			    tree resume_fn_ptr_type, tree& resume_fn_field,
> -			    tree& resume_idx_field, tree& fs_label)
> +			    tree resume_fn_ptr_type,
> +			    tree& resume_idx_var, tree& fs_label)
>   {
>     /* This will be our new outer scope.  */
>     tree update_body = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL);
> @@ -4057,7 +4024,6 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>   
>     /* 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);
>   
> @@ -4068,24 +4034,61 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>     tree return_void
>       = get_coroutine_return_void_expr (current_function_decl, fn_start, false);
>   
> +  /* The pointer to the resume function.  */
> +  tree resume_fn_ptr
> +    = coro_build_artificial_var (fn_start, coro_resume_fn_id,
> +				 resume_fn_ptr_type, orig, NULL_TREE);
> +  DECL_CHAIN (resume_fn_ptr) = var_list;
> +  var_list = resume_fn_ptr;
> +  add_decl_expr (resume_fn_ptr);
> +
>     /* We will need to be able to set the resume function pointer to nullptr
>        to signal that the coroutine is 'done'.  */
> -  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);
> -  /* Likewise, the resume index needs to be reset.  */
> -  resume_idx_field
> -    = build_lang_decl (VAR_DECL, get_identifier ("resume.index.proxy"),
> -		       short_unsigned_type_node);
> -  DECL_ARTIFICIAL (resume_idx_field) = true;
> -  tree zero_resume_idx = build_int_cst (short_unsigned_type_node, 0);
> -  zero_resume_idx = build2 (INIT_EXPR, short_unsigned_type_node,
> -			    resume_idx_field, zero_resume_idx);
> +
> +  /* The pointer to the destroy function.  */
> +  tree var = coro_build_artificial_var (fn_start, coro_destroy_fn_id,
> +					resume_fn_ptr_type, orig, NULL_TREE);
> +  DECL_CHAIN (var) = var_list;
> +  var_list = var;
> +  add_decl_expr (var);
> +
> +  /* The promise was created on demand when parsing we now link it into
> +      our scope.  */
> +  tree promise = get_coroutine_promise_proxy (orig);
> +  DECL_CONTEXT (promise) = orig;
> +  DECL_SOURCE_LOCATION (promise) = fn_start;
> +  DECL_CHAIN (promise) = var_list;
> +  var_list = promise;
> +  add_decl_expr (promise);
> +
> +  /* We need a handle to this coroutine, which is passed to every
> +     await_suspend().  This was created on demand when parsing we now link it
> +     into our scope.  */
> +  var = get_coroutine_self_handle_proxy (orig);
> +  DECL_CONTEXT (var) = orig;
> +  DECL_SOURCE_LOCATION (var) = fn_start;
> +  DECL_CHAIN (var) = var_list;
> +  var_list = var;
> +  add_decl_expr (var);
> +
> +
> +  /* We create a resume index, this is initialized in the ramp.  */
> +  resume_idx_var
> +    = coro_build_artificial_var (fn_start, coro_resume_index_id,
> +				 short_unsigned_type_node, orig, NULL_TREE);
> +  DECL_CHAIN (resume_idx_var) = var_list;
> +  var_list = resume_idx_var;
> +  add_decl_expr (resume_idx_var);
> +
> +  /* If the coroutine has a frame that needs to be freed, this will be set by
> +     the ramp.  */
> +  var = coro_build_artificial_var (fn_start, coro_frame_needs_free_id,
> +				   boolean_type_node, orig, NULL_TREE);
> +  DECL_CHAIN (var) = var_list;
> +  var_list = var;
> +  add_decl_expr (var);
>   
>     if (flag_exceptions)
>       {
> @@ -4097,8 +4100,7 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>         /* Create and initialize the initial-await-resume-called variable per
>   	 [dcl.fct.def.coroutine] / 5.3.  */
>         tree i_a_r_c
> -	= coro_build_artificial_var (fn_start,
> -				     "_Coro_initial_await_resume_called",
> +	= coro_build_artificial_var (fn_start, coro_frame_i_a_r_c_id,
>   				     boolean_type_node, orig,
>   				     boolean_false_node);
>         DECL_CHAIN (i_a_r_c) = var_list;
> @@ -4151,10 +4153,14 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>   	 If the unhandled exception method returns, then we continue
>   	 to the final await expression (which duplicates the clearing of
>   	 the field). */
> -      finish_expr_stmt (zero_resume);
> -      finish_expr_stmt (zero_resume_idx);
> -      ueh = maybe_cleanup_point_expr_void (ueh);
> -      add_stmt (ueh);
> +      tree r = build2 (MODIFY_EXPR, resume_fn_ptr_type, resume_fn_ptr,
> +		       zero_resume);
> +      finish_expr_stmt (r);
> +      tree short_zero = build_int_cst (short_unsigned_type_node, 0);
> +      r = build2 (MODIFY_EXPR, short_unsigned_type_node, resume_idx_var,
> +		  short_zero);
> +      finish_expr_stmt (r);
> +      finish_expr_stmt (ueh);
>         finish_handler (handler);
>         TRY_HANDLERS (tcb) = pop_stmt_list (TRY_HANDLERS (tcb));
>       }
> @@ -4189,6 +4195,8 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>     /* 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.  */
> +  zero_resume = build2 (MODIFY_EXPR, resume_fn_ptr_type, resume_fn_ptr,
> +			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));
> @@ -4216,15 +4224,15 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>    declare a dummy coro frame.
>    struct _R_frame {
>     using handle_type = coro::coroutine_handle<coro1::promise_type>;
> -  void (*__resume)(_R_frame *);
> -  void (*__destroy)(_R_frame *);
> -  coro1::promise_type __p;
> -  bool frame_needs_free; free the coro frame mem if set.
> -  bool i_a_r_c; [dcl.fct.def.coroutine] / 5.3
> -  short __resume_at;
> -  handle_type self_handle;
> -  (maybe) parameter copies.
> -  (maybe) local variables saved (including awaitables)
> +  void (*_Coro_resume_fn)(_R_frame *);
> +  void (*_Coro_destroy_fn)(_R_frame *);
> +  coro1::promise_type _Coro_promise;
> +  bool _Coro_frame_needs_free; free the coro frame mem if set.
> +  bool _Coro_i_a_r_c; [dcl.fct.def.coroutine] / 5.3
> +  short _Coro_resume_index;
> +  handle_type _Coro_self_handle;
> +  parameter copies (were required).
> +  local variables saved (including awaitables)
>     (maybe) trailing space.
>    };  */
>   
> @@ -4316,7 +4324,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>   
>     /* 2. Types we need to define or look up.  */
>   
> -  tree fr_name = get_fn_local_identifier (orig, "frame");
> +  tree fr_name = get_fn_local_identifier (orig, "Frame");
>     tree coro_frame_type = xref_tag (record_type, fr_name);
>     DECL_CONTEXT (TYPE_NAME (coro_frame_type)) = current_scope ();
>     tree coro_frame_ptr = build_pointer_type (coro_frame_type);
> @@ -4333,33 +4341,16 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>     /* Construct the wrapped function body; we will analyze this to determine
>        the requirements for the coroutine frame.  */
>   
> -  tree resume_fn_field = NULL_TREE;
> -  tree resume_idx_field = NULL_TREE;
> +  tree resume_idx_var = NULL_TREE;
>     tree fs_label = NULL_TREE;
>     fnbody = coro_rewrite_function_body (fn_start, fnbody, orig,
> -				       act_des_fn_ptr, resume_fn_field,
> -				       resume_idx_field, fs_label);
> +				       act_des_fn_ptr,
> +				       resume_idx_var, fs_label);
>     /* Build our dummy coro frame layout.  */
>     coro_frame_type = begin_class_definition (coro_frame_type);
>   
> +  /* The fields for the coro frame.  */
>     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);
> -  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 resume_idx_name
> -    = coro_make_frame_entry (&field_list, "__resume_at",
> -			     short_unsigned_type_node, fn_start);
> -
> -  /* We need a handle to this coroutine, which is passed to every
> -     await_suspend().  There's no point in creating it over and over.  */
> -  (void) coro_make_frame_entry (&field_list, "__self_h", handle_type, fn_start);
>   
>     /* Now add in fields for function params (if there are any).
>        We do not attempt elision of copies at this stage, we do analyze the
> @@ -4417,14 +4408,14 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>   	  if (DECL_NAME (arg))
>   	    {
>   	      tree pname = DECL_NAME (arg);
> -	      buf = xasprintf ("__parm.%s", IDENTIFIER_POINTER (pname));
> +	      buf = xasprintf ("_P_%s", IDENTIFIER_POINTER (pname));
>   	    }
>   	  else
> -	    buf = xasprintf ("__unnamed_parm.%d", no_name_parm++);
> +	    buf = xasprintf ("_P_unnamed_%d", no_name_parm++);
>   
>   	  if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (parm.frame_type))
>   	    {
> -	      char *gbuf = xasprintf ("%s.live", buf);
> +	      char *gbuf = xasprintf ("%s_live", buf);
>   	      parm.guard_var
>   		= build_lang_decl (VAR_DECL, get_identifier (gbuf),
>   				   boolean_type_node);
> @@ -4761,8 +4752,8 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>   
>     /* 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 (coro_frame_type, fnf_name, 1, 0, tf_warning_or_error);
> +  tree fnf_m = lookup_member (coro_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 = build2 (INIT_EXPR, boolean_type_node, fnf_x, boolean_true_node);
> @@ -4773,24 +4764,22 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>   
>     tree actor_addr = build1 (ADDR_EXPR, act_des_fn_ptr, actor);
>     tree resume_m
> -    = lookup_member (coro_frame_type, resume_name,
> +    = lookup_member (coro_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 = build2_loc (fn_start, INIT_EXPR, act_des_fn_ptr, resume_x, actor_addr);
> -  r = coro_build_cvt_void_expr_stmt (r, fn_start);
> -  add_stmt (r);
> +  finish_expr_stmt (r);
>   
>     tree destroy_addr = build1 (ADDR_EXPR, act_des_fn_ptr, destroy);
>     tree destroy_m
> -    = lookup_member (coro_frame_type, destroy_name,
> +    = lookup_member (coro_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 = build2_loc (fn_start, INIT_EXPR, act_des_fn_ptr, destroy_x, destroy_addr);
> -  r = coro_build_cvt_void_expr_stmt (r, fn_start);
> -  add_stmt (r);
> +  finish_expr_stmt (r);
>   
>     /* [dcl.fct.def.coroutine] /13
>        When a coroutine is invoked, a copy is created for each coroutine
> @@ -4881,7 +4870,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>   
>     /* Set up the promise.  */
>     tree promise_m
> -    = lookup_member (coro_frame_type, promise_name,
> +    = lookup_member (coro_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,
> @@ -5027,9 +5016,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>   			      boolean_type_node);
>         finish_expr_stmt (r);
>       }
> -  /* Initialize the resume_idx_name to 0, meaning "not started".  */
> +  /* Initialize the resume_idx_var to 0, meaning "not started".  */
>     tree resume_idx_m
> -    = lookup_member (coro_frame_type, resume_idx_name,
> +    = lookup_member (coro_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,
> @@ -5172,9 +5161,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>     push_deferring_access_checks (dk_no_check);
>   
>     /* Build the actor...  */
> -  build_actor_fn (fn_start, coro_frame_type, actor, fnbody, orig, param_uses,
> -		  &local_var_uses, param_dtor_list, resume_fn_field,
> -		  resume_idx_field, body_aw_points.await_number, frame_size);
> +  build_actor_fn (fn_start, coro_frame_type, actor, fnbody, orig,
> +		  &local_var_uses, param_dtor_list,
> +		  resume_idx_var, body_aw_points.await_number, frame_size);
>   
>     /* Destroyer ... */
>     build_destroy_fn (fn_start, coro_frame_type, destroy, actor);
> 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 7/8 v2] coroutines: Make proxy vars for the function arg copies.
  2021-09-05 19:50                   ` [PATCH 7/8 v2] " Iain Sandoe
@ 2021-09-07 17:26                     ` Jason Merrill
  0 siblings, 0 replies; 28+ messages in thread
From: Jason Merrill @ 2021-09-07 17:26 UTC (permalink / raw)
  To: Iain Sandoe; +Cc: GCC Patches

On 9/5/21 3:50 PM, Iain Sandoe wrote:
> Hello Jason,
> 
>> On 3 Sep 2021, at 15:23, Iain Sandoe <iain@sandoe.co.uk> wrote:
>>> On 3 Sep 2021, at 15:07, Jason Merrill via Gcc-patches <gcc-patches@gcc.gnu.org> wrote:
>>>
>>> On 9/1/21 6:56 AM, Iain Sandoe wrote:
>>>> This adds top level proxy variables for the coroutine frame
> 
>>>> +	  add_decl_expr (parm_i->copy_var);
>>>
>>> Are these getting DECL_VALUE_EXPR somewhere?
>>
>> Yes - all variables in the outermost bind expression will be allocated a frame slot, and gain a DECL_VALUE_EXPR to point to it.
> 
> Amended comments to say this.
>>
>>>> +      	}
>>>> +
>>>> +      /* Now replace all uses of the parms in the function body with the local
>>>> +	 vars.  */
>>>
>>> I think the following old comment still applies to how 'visited' is used, and should be adapted here as well:
>>
>> Yes, will do.
> done
> 
> OK now?

OK.

> thanks
> Iain
> 
> [PATCH] coroutines: Make proxy vars for the function arg copies.
> 
> This adds top level proxy variables for the coroutine frame
> copies of the original function args.  These are then available
> in the debugger to refer to the frame copies.  We rewrite the
> function body to use the copies, since the original parms will
> no longer be in scope when the coroutine is running.
> 
> Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
> 
> gcc/cp/ChangeLog:
> 
> 	* coroutines.cc (struct param_info): Add copy_var.
> 	(build_actor_fn): Use simplified param references.
> 	(register_param_uses): Likewise.
> 	(rewrite_param_uses): Likewise.
> 	(analyze_fn_parms): New function.
> 	(coro_rewrite_function_body): Add proxies for the fn
> 	parameters to the outer bind scope of the rewritten code.
> 	(morph_fn_to_coro): Use simplified version of param ref.
> ---
>   gcc/cp/coroutines.cc | 251 +++++++++++++++++++++----------------------
>   1 file changed, 121 insertions(+), 130 deletions(-)
> 
> diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
> index cd774b78ae0..d2cc2e73c89 100644
> --- a/gcc/cp/coroutines.cc
> +++ b/gcc/cp/coroutines.cc
> @@ -1964,6 +1964,7 @@ transform_await_wrapper (tree *stmt, int *do_subtree, void *d)
>   struct param_info
>   {
>     tree field_id;     /* The name of the copy in the coroutine frame.  */
> +  tree copy_var;     /* The local var proxy for the frame copy.  */
>     vec<tree *> *body_uses; /* Worklist of uses, void if there are none.  */
>     tree frame_type;   /* The type used to represent this parm in the frame.  */
>     tree orig_type;    /* The original type of the parm (not as passed).  */
> @@ -2169,36 +2170,6 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>     /* Declare the continuation handle.  */
>     add_decl_expr (continuation);
>   
> -  /* Re-write param references in the body, no code should be generated
> -     here.  */
> -  if (DECL_ARGUMENTS (orig))
> -    {
> -      tree arg;
> -      for (arg = DECL_ARGUMENTS (orig); arg != NULL; arg = DECL_CHAIN (arg))
> -	{
> -	  bool existed;
> -	  param_info &parm = param_uses->get_or_insert (arg, &existed);
> -	  if (!parm.body_uses)
> -	    continue; /* Wasn't used in the original function body.  */
> -
> -	  tree fld_ref = lookup_member (coro_frame_type, parm.field_id,
> -					/*protect=*/1, /*want_type=*/0,
> -					tf_warning_or_error);
> -	  tree fld_idx = build3_loc (loc, COMPONENT_REF, parm.frame_type,
> -				     actor_frame, fld_ref, NULL_TREE);
> -
> -	  /* We keep these in the frame as a regular pointer, so convert that
> -	   back to the type expected.  */
> -	  if (parm.pt_ref)
> -	    fld_idx = build1_loc (loc, CONVERT_EXPR, TREE_TYPE (arg), fld_idx);
> -
> -	  int i;
> -	  tree *puse;
> -	  FOR_EACH_VEC_ELT (*parm.body_uses, i, puse)
> -	    *puse = fld_idx;
> -	}
> -    }
> -
>     /* Re-write local vars, similarly.  */
>     local_vars_transform xform_vars_data
>       = {actor, actor_frame, coro_frame_type, loc, local_var_uses};
> @@ -3771,11 +3742,11 @@ struct param_frame_data
>     bool param_seen;
>   };
>   
> -/* A tree-walk callback that records the use of parameters (to allow for
> -   optimizations where handling unused parameters may be omitted).  */
> +/* A tree walk callback that rewrites each parm use to the local variable
> +   that represents its copy in the frame.  */
>   
>   static tree
> -register_param_uses (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
> +rewrite_param_uses (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
>   {
>     param_frame_data *data = (param_frame_data *) d;
>   
> @@ -3783,7 +3754,7 @@ register_param_uses (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
>     if (TREE_CODE (*stmt) == VAR_DECL && DECL_HAS_VALUE_EXPR_P (*stmt))
>       {
>         tree t = DECL_VALUE_EXPR (*stmt);
> -      return cp_walk_tree (&t, register_param_uses, d, NULL);
> +      return cp_walk_tree (&t, rewrite_param_uses, d, NULL);
>       }
>   
>     if (TREE_CODE (*stmt) != PARM_DECL)
> @@ -3797,16 +3768,88 @@ register_param_uses (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
>     param_info &parm = data->param_uses->get_or_insert (*stmt, &existed);
>     gcc_checking_assert (existed);
>   
> -  if (!parm.body_uses)
> +  *stmt = parm.copy_var;
> +  return NULL_TREE;
> +}
> +
> +/* Build up a set of info that determines how each param copy will be
> +   handled.  */
> +
> +static hash_map<tree, param_info> *
> +analyze_fn_parms (tree orig)
> +{
> +  if (!DECL_ARGUMENTS (orig))
> +    return NULL;
> +
> +  hash_map<tree, param_info> *param_uses = new hash_map<tree, param_info>;
> +
> +  /* Build a hash map with an entry for each param.
> +     The key is the param tree.
> +     Then we have an entry for the frame field name.
> +     Then a cache for the field ref when we come to use it.
> +     Then a tree list of the uses.
> +     The second two entries start out empty - and only get populated
> +     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))
>       {
> -      vec_alloc (parm.body_uses, 4);
> -      parm.body_uses->quick_push (stmt);
> -      data->param_seen = true;
> +      bool existed;
> +      param_info &parm = param_uses->get_or_insert (arg, &existed);
> +      gcc_checking_assert (!existed);
> +      parm.body_uses = NULL;
> +      tree actual_type = TREE_TYPE (arg);
> +      actual_type = complete_type_or_else (actual_type, orig);
> +      if (actual_type == NULL_TREE)
> +	actual_type = error_mark_node;
> +      parm.orig_type = actual_type;
> +      parm.by_ref = parm.pt_ref = parm.rv_ref =  false;
> +      if (TREE_CODE (actual_type) == REFERENCE_TYPE)
> +	{
> +	  /* If the user passes by reference, then we will save the
> +	     pointer to the original.  As noted in
> +	     [dcl.fct.def.coroutine] / 13, if the lifetime of the
> +	     referenced item ends and then the coroutine is resumed,
> +	     we have UB; well, the user asked for it.  */
> +	  if (TYPE_REF_IS_RVALUE (actual_type))
> +		parm.rv_ref = true;
> +	  else
> +		parm.pt_ref = true;
> +	}
> +      else if (TYPE_REF_P (DECL_ARG_TYPE (arg)))
> +	parm.by_ref = true;
> +
> +      parm.frame_type = actual_type;
> +
> +      parm.this_ptr = is_this_parameter (arg);
> +      parm.lambda_cobj = lambda_p && DECL_NAME (arg) == closure_identifier;
> +
> +      tree name = DECL_NAME (arg);
> +      if (!name)
> +	{
> +	  char *buf = xasprintf ("_Coro_unnamed_parm_%d", no_name_parm++);
> +	  name = get_identifier (buf);
> +	  free (buf);
> +	}
> +      parm.field_id = name;
> +
> +      if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (parm.frame_type))
> +	{
> +	  char *buf = xasprintf ("_Coro_%s_live", IDENTIFIER_POINTER (name));
> +	  parm.guard_var = build_lang_decl (VAR_DECL, get_identifier (buf),
> +					    boolean_type_node);
> +	  free (buf);
> +	  DECL_ARTIFICIAL (parm.guard_var) = true;
> +	  DECL_CONTEXT (parm.guard_var) = orig;
> +	  DECL_INITIAL (parm.guard_var) = boolean_false_node;
> +	  parm.trivial_dtor = false;
> +	}
> +      else
> +	parm.trivial_dtor = true;
>       }
> -  else
> -    parm.body_uses->safe_push (stmt);
>   
> -  return NULL_TREE;
> +  return param_uses;
>   }
>   
>   /* Small helper for the repetitive task of adding a new field to the coro
> @@ -3990,6 +4033,7 @@ coro_build_actor_or_destroy_function (tree orig, tree fn_type,
>   
>   static tree
>   coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
> +			    hash_map<tree, param_info> *param_uses,
>   			    tree resume_fn_ptr_type,
>   			    tree& resume_idx_var, tree& fs_label)
>   {
> @@ -4073,6 +4117,39 @@ coro_rewrite_function_body (location_t fn_start, tree fnbody, tree orig,
>     var_list = var;
>     add_decl_expr (var);
>   
> +  /* If we have function parms, then these will be copied to the coroutine
> +     frame.  Create a local (proxy) variable for each parm, since the original
> +     parms will be out of scope once the ramp has finished. The proxy vars will
> +     get DECL_VALUE_EXPRs pointing to the frame copies, so that we can interact
> +     with them in the debugger.  */
> +  if (param_uses)
> +    {
> +      gcc_checking_assert (DECL_ARGUMENTS (orig));
> +      /* Add a local var for each parm.  */
> +      for (tree arg = DECL_ARGUMENTS (orig); arg != NULL;
> +	   arg = DECL_CHAIN (arg))
> +	{
> +	  param_info *parm_i = param_uses->get (arg);
> +	  gcc_checking_assert (parm_i);
> +	  parm_i->copy_var
> +	    = build_lang_decl (VAR_DECL, parm_i->field_id, TREE_TYPE (arg));
> +	  DECL_SOURCE_LOCATION (parm_i->copy_var) = DECL_SOURCE_LOCATION (arg);
> +	  DECL_CONTEXT (parm_i->copy_var) = orig;
> +	  DECL_ARTIFICIAL (parm_i->copy_var) = true;
> +	  DECL_CHAIN (parm_i->copy_var) = var_list;
> +	  var_list = parm_i->copy_var;
> +	  add_decl_expr (parm_i->copy_var);
> +      	}
> +
> +      /* Now replace all uses of the parms in the function body with the proxy
> +	 vars.  We want to this to apply to every instance of param's use, so
> +	 don't include a 'visited' hash_set on the tree walk, however we will
> +	 arrange to visit each containing expression only once.  */
> +      hash_set<tree *> visited;
> +      param_frame_data param_data = {NULL, param_uses,
> +				     &visited, fn_start, false};
> +      cp_walk_tree (&fnbody, rewrite_param_uses, &param_data, NULL);
> +    }
>   
>     /* We create a resume index, this is initialized in the ramp.  */
>     resume_idx_var
> @@ -4343,7 +4420,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>   
>     tree resume_idx_var = NULL_TREE;
>     tree fs_label = NULL_TREE;
> -  fnbody = coro_rewrite_function_body (fn_start, fnbody, orig,
> +  hash_map<tree, param_info> *param_uses = analyze_fn_parms (orig);
> +
> +  fnbody = coro_rewrite_function_body (fn_start, fnbody, orig, param_uses,
>   				       act_des_fn_ptr,
>   				       resume_idx_var, fs_label);
>     /* Build our dummy coro frame layout.  */
> @@ -4352,94 +4431,6 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
>     /* The fields for the coro frame.  */
>     tree field_list = NULL_TREE;
>   
> -  /* Now add in fields for function params (if there are any).
> -     We do not attempt elision of copies at this stage, we do analyze the
> -     uses and build worklists to replace those when the state machine is
> -     lowered.  */
> -
> -  hash_map<tree, param_info> *param_uses = NULL;
> -  if (DECL_ARGUMENTS (orig))
> -    {
> -      /* Build a hash map with an entry for each param.
> -	  The key is the param tree.
> -	  Then we have an entry for the frame field name.
> -	  Then a cache for the field ref when we come to use it.
> -	  Then a tree list of the uses.
> -	  The second two entries start out empty - and only get populated
> -	  when we see uses.  */
> -      param_uses = new hash_map<tree, param_info>;
> -      bool lambda_p = LAMBDA_FUNCTION_P (orig);
> -
> -      unsigned no_name_parm = 0;
> -      for (tree arg = DECL_ARGUMENTS (orig); arg != NULL;
> -	   arg = DECL_CHAIN (arg))
> -	{
> -	  bool existed;
> -	  param_info &parm = param_uses->get_or_insert (arg, &existed);
> -	  gcc_checking_assert (!existed);
> -	  parm.body_uses = NULL;
> -	  tree actual_type = TREE_TYPE (arg);
> -	  actual_type = complete_type_or_else (actual_type, orig);
> -	  if (actual_type == NULL_TREE)
> -	    actual_type = error_mark_node;
> -	  parm.orig_type = actual_type;
> -	  parm.by_ref = parm.pt_ref = parm.rv_ref =  false;
> -	  if (TREE_CODE (actual_type) == REFERENCE_TYPE)
> -	    {
> -	      /* If the user passes by reference, then we will save the
> -		 pointer to the original.  As noted in
> -		 [dcl.fct.def.coroutine] / 13, if the lifetime of the
> -		 referenced item ends and then the coroutine is resumed,
> -		 we have UB; well, the user asked for it.  */
> -	      if (TYPE_REF_IS_RVALUE (actual_type))
> -		parm.rv_ref = true;
> -	      else
> -		parm.pt_ref = true;
> -	    }
> -	  else if (TYPE_REF_P (DECL_ARG_TYPE (arg)))
> -	    parm.by_ref = true;
> -
> -	  parm.frame_type = actual_type;
> -
> -	  parm.this_ptr = is_this_parameter (arg);
> -	  parm.lambda_cobj = lambda_p && DECL_NAME (arg) == closure_identifier;
> -
> -	  char *buf;
> -	  if (DECL_NAME (arg))
> -	    {
> -	      tree pname = DECL_NAME (arg);
> -	      buf = xasprintf ("_P_%s", IDENTIFIER_POINTER (pname));
> -	    }
> -	  else
> -	    buf = xasprintf ("_P_unnamed_%d", no_name_parm++);
> -
> -	  if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (parm.frame_type))
> -	    {
> -	      char *gbuf = xasprintf ("%s_live", buf);
> -	      parm.guard_var
> -		= build_lang_decl (VAR_DECL, get_identifier (gbuf),
> -				   boolean_type_node);
> -	      free (gbuf);
> -	      DECL_ARTIFICIAL (parm.guard_var) = true;
> -	      DECL_INITIAL (parm.guard_var) = boolean_false_node;
> -	      parm.trivial_dtor = false;
> -	    }
> -	  else
> -	    parm.trivial_dtor = true;
> -	  parm.field_id = coro_make_frame_entry
> -	    (&field_list, buf, actual_type, DECL_SOURCE_LOCATION (arg));
> -	  free (buf);
> -	}
> -
> -      /* We want to record every instance of param's use, so don't include
> -	 a 'visited' hash_set on the tree walk, but only record a containing
> -	 expression once.  */
> -      hash_set<tree *> visited;
> -      param_frame_data param_data
> -	= {&field_list, param_uses, &visited, fn_start, false};
> -      cp_walk_tree (&fnbody, register_param_uses, &param_data, NULL);
> -    }
> -
>     /* 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.  */
> 


^ permalink raw reply	[flat|nested] 28+ messages in thread

end of thread, other threads:[~2021-09-07 17:26 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-09-01 10:51 [PATCH 0/8] coroutines: Use DECL_VALUE_EXPRs to assist in debug [PR99215] Iain Sandoe
2021-09-01 10:52 ` [PATCH 1/8] coroutines : Use DECL_VALUE_EXPR instead of rewriting vars Iain Sandoe
2021-09-01 10:53   ` [PATCH 2/8] coroutines: Add a helper for creating local vars Iain Sandoe
2021-09-01 10:53     ` PATCH 3/8] coroutines: Support for debugging implementation state Iain Sandoe
2021-09-01 10:54       ` [PATCH 4/8] coroutines: Make some of the artificial names more debugger-friendly Iain Sandoe
2021-09-01 10:54         ` [PATCH 5/8] coroutines: Define and populate accessors for debug state Iain Sandoe
2021-09-01 10:55           ` [PATCH 6/8] coroutines: Convert implementation variables to debug-friendly form Iain Sandoe
2021-09-01 10:56             ` [PATCH 7/8] coroutines: Make proxy vars for the function arg copies Iain Sandoe
2021-09-01 10:56               ` [PATCH 8/8] coroutines: Make the continue handle visible to debug Iain Sandoe
2021-09-03 14:25                 ` Jason Merrill
2021-09-03 14:07               ` [PATCH 7/8] coroutines: Make proxy vars for the function arg copies Jason Merrill
2021-09-03 14:23                 ` Iain Sandoe
2021-09-05 19:50                   ` [PATCH 7/8 v2] " Iain Sandoe
2021-09-07 17:26                     ` Jason Merrill
2021-09-03 13:52             ` [PATCH 6/8] coroutines: Convert implementation variables to debug-friendly form Jason Merrill
2021-09-03 13:56               ` Iain Sandoe
2021-09-03 14:12                 ` Jason Merrill
2021-09-03 14:21                   ` Iain Sandoe
2021-09-05 19:47                     ` Iain Sandoe
2021-09-07 17:23                       ` Jason Merrill
2021-09-03 13:39           ` [PATCH 5/8] coroutines: Define and populate accessors for debug state Jason Merrill
2021-09-03 13:41             ` Iain Sandoe
2021-09-03 13:42             ` Iain Sandoe
2021-09-03 13:44               ` Jason Merrill
2021-09-03 13:35         ` [PATCH 4/8] coroutines: Make some of the artificial names more debugger-friendly Jason Merrill
2021-09-03 13:31       ` PATCH 3/8] coroutines: Support for debugging implementation state Jason Merrill
2021-09-02 21:52     ` [PATCH 2/8] coroutines: Add a helper for creating local vars Jason Merrill
2021-09-02 21:51   ` [PATCH 1/8] coroutines : Use DECL_VALUE_EXPR instead of rewriting vars Jason Merrill

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).