coroutines: Adjust outlined function names [PR95520].
authorIain Sandoe <iain@sandoe.co.uk>
Thu, 8 Jul 2021 08:42:49 +0000 (09:42 +0100)
committerIain Sandoe <iain@sandoe.co.uk>
Mon, 19 Jul 2021 20:10:29 +0000 (21:10 +0100)
The mechanism used to date for uniquing the coroutine helper
functions (actor, destroy) was over-complicating things and
leading to the noted PR and also difficulties in setting
breakpoints on these functions (so this will help PR99215 as
well).

This implementation delegates the adjustment to the mangling
to write_encoding() which necessitates some book-keeping so
that it is possible to determine which of the coroutine
helper names is to be mangled.

Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
PR c++/95520 - [coroutines] __builtin_FUNCTION() returns mangled .actor instead of original function name

PR c++/95520

gcc/cp/ChangeLog:

* coroutines.cc (struct coroutine_info): Add fields for
actor and destroy function decls.
(to_ramp): New.
(coro_get_ramp_function): New.
(coro_get_actor_function): New.
(coro_get_destroy_function): New.
(act_des_fn): Set up mapping between ramp, actor and
destroy functions.
(morph_fn_to_coro): Adjust interface to the builder for
helper function decls.
* cp-tree.h (DECL_ACTOR_FN, DECL_DESTROY_FN, DECL_RAMP_FN,
JOIN_STR): New.
* mangle.c (write_encoding): Handle coroutine helpers.
(write_unqualified_name): Handle lambda coroutine helpers.

gcc/testsuite/ChangeLog:

* g++.dg/coroutines/pr95520.C: New test.

gcc/cp/coroutines.cc
gcc/cp/cp-tree.h
gcc/cp/mangle.c
gcc/testsuite/g++.dg/coroutines/pr95520.C [new file with mode: 0644]

index 712a5c0..47c79e5 100644 (file)
@@ -82,11 +82,13 @@ static bool coro_promise_type_found_p (tree, location_t);
 struct GTY((for_user)) coroutine_info
 {
   tree function_decl; /* The original function decl.  */
-  tree promise_type; /* The cached promise type for this function.  */
-  tree handle_type;  /* The cached coroutine handle for this function.  */
-  tree self_h_proxy; /* A handle instance that is used as the proxy for the
-                       one that will eventually be allocated in the coroutine
-                       frame.  */
+  tree actor_decl;    /* The synthesized actor function.  */
+  tree destroy_decl;  /* The synthesized destroy function.  */
+  tree promise_type;  /* The cached promise type for this function.  */
+  tree handle_type;   /* The cached coroutine handle for this function.  */
+  tree self_h_proxy;  /* A handle instance that is used as the proxy for the
+                        one that will eventually be allocated in the coroutine
+                        frame.  */
   tree promise_proxy; /* Likewise, a proxy promise instance.  */
   tree return_void;   /* The expression for p.return_void() if it exists.  */
   location_t first_coro_keyword; /* The location of the keyword that made this
@@ -526,6 +528,46 @@ coro_promise_type_found_p (tree fndecl, location_t loc)
   return true;
 }
 
+/* Map from actor or destroyer to ramp.  */
+static GTY(()) hash_map<tree, tree> *to_ramp;
+
+/* Given a tree that is an actor or destroy, find the ramp function.  */
+
+tree
+coro_get_ramp_function (tree decl)
+{
+  if (!to_ramp)
+    return NULL_TREE;
+  tree *p = to_ramp->get (decl);
+  if (p)
+    return *p;
+  return NULL_TREE;
+}
+
+/* Given the DECL for a ramp function (the user's original declaration) return
+   the actor function if it has been defined.  */
+
+tree
+coro_get_actor_function (tree decl)
+{
+  if (coroutine_info *info = get_coroutine_info (decl))
+    return info->actor_decl;
+
+  return NULL_TREE;
+}
+
+/* Given the DECL for a ramp function (the user's original declaration) return
+   the destroy function if it has been defined.  */
+
+tree
+coro_get_destroy_function (tree decl)
+{
+  if (coroutine_info *info = get_coroutine_info (decl))
+    return info->destroy_decl;
+
+  return NULL_TREE;
+}
+
 /* These functions assumes that the caller has verified that the state for
    the decl has been initialized, we try to minimize work here.  */
 
@@ -3979,15 +4021,23 @@ register_local_var_uses (tree *stmt, int *do_subtree, void *d)
   return NULL_TREE;
 }
 
-/* Build, return FUNCTION_DECL node with its coroutine frame pointer argument
-   for either actor or destroy functions.  */
+/* Build, return FUNCTION_DECL node based on ORIG with a type FN_TYPE which has
+   a single argument of type CORO_FRAME_PTR.  Build the actor function if
+   ACTOR_P is true, otherwise the destroy. */
 
 static tree
-act_des_fn (tree orig, tree fn_type, tree coro_frame_ptr, const char* name)
+coro_build_actor_or_destroy_function (tree orig, tree fn_type,
+                                     tree coro_frame_ptr, bool actor_p)
 {
-  tree fn_name = get_fn_local_identifier (orig, name);
   location_t loc = DECL_SOURCE_LOCATION (orig);
-  tree fn = build_lang_decl (FUNCTION_DECL, fn_name, fn_type);
+  tree fn
+    = build_lang_decl (FUNCTION_DECL, copy_node (DECL_NAME (orig)), fn_type);
+
+  /* Allow for locating the ramp (original) function from this one.  */
+  if (!to_ramp)
+    to_ramp = hash_map<tree, tree>::create_ggc (10);
+  to_ramp->put (fn, orig);
+
   DECL_CONTEXT (fn) = DECL_CONTEXT (orig);
   DECL_SOURCE_LOCATION (fn) = loc;
   DECL_ARTIFICIAL (fn) = true;
@@ -4021,6 +4071,17 @@ act_des_fn (tree orig, tree fn_type, tree coro_frame_ptr, const char* name)
   /* This is a coroutine component.  */
   DECL_COROUTINE_P (fn) = 1;
 
+  /* Set up a means to find out if a decl is one of the helpers and, if so,
+     which one.  */
+  if (coroutine_info *info = get_coroutine_info (orig))
+    {
+      gcc_checking_assert ((actor_p && info->actor_decl == NULL_TREE)
+                          || info->destroy_decl == NULL_TREE);
+      if (actor_p)
+       info->actor_decl = fn;
+      else
+       info->destroy_decl = fn;
+    }
   return fn;
 }
 
@@ -4329,8 +4390,10 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
   tree act_des_fn_ptr = build_pointer_type (act_des_fn_type);
 
   /* Declare the actor and destroyer function.  */
-  tree actor = act_des_fn (orig, act_des_fn_type, coro_frame_ptr, "actor");
-  tree destroy = act_des_fn (orig, act_des_fn_type, coro_frame_ptr, "destroy");
+  tree actor = coro_build_actor_or_destroy_function (orig, act_des_fn_type,
+                                                    coro_frame_ptr, true);
+  tree destroy = coro_build_actor_or_destroy_function (orig, act_des_fn_type,
+                                                      coro_frame_ptr, false);
 
   /* Construct the wrapped function body; we will analyze this to determine
      the requirements for the coroutine frame.  */
index f4bcab5..ddf8f43 100644 (file)
@@ -5166,6 +5166,21 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
 #define DECL_COROUTINE_P(NODE) \
   (LANG_DECL_FN_CHECK (DECL_COMMON_CHECK (NODE))->coroutine_p)
 
+/* For a FUNCTION_DECL of a coroutine, this holds the ACTOR helper function
+   decl.  */
+#define DECL_ACTOR_FN(NODE) \
+  (coro_get_actor_function ((NODE)))
+
+/* For a FUNCTION_DECL of a coroutine, this holds the DESTROY helper function
+  decl.  */
+#define DECL_DESTROY_FN(NODE) \
+  (coro_get_destroy_function ((NODE)))
+
+/* For a FUNCTION_DECL of a coroutine helper (ACTOR or DESTROY), this points
+   back to the original (ramp) function.  */
+#define DECL_RAMP_FN(NODE) \
+  (coro_get_ramp_function (NODE))
+
 /* True for an OMP_ATOMIC that has dependent parameters.  These are stored
    as an expr in operand 1, and integer_zero_node or clauses in operand 0.  */
 #define OMP_ATOMIC_DEPENDENT_P(NODE) \
@@ -5584,6 +5599,7 @@ extern GTY(()) vec<tree, va_gc> *keyed_classes;
 #ifndef NO_DOT_IN_LABEL
 
 #define JOINER '.'
+#define JOIN_STR "."
 
 #define AUTO_TEMP_NAME "_.tmp_"
 #define VFIELD_BASE ".vf"
@@ -5595,6 +5611,7 @@ extern GTY(()) vec<tree, va_gc> *keyed_classes;
 #ifndef NO_DOLLAR_IN_LABEL
 
 #define JOINER '$'
+#define JOIN_STR "$"
 
 #define AUTO_TEMP_NAME "_$tmp_"
 #define VFIELD_BASE "$vf"
@@ -5603,6 +5620,8 @@ extern GTY(()) vec<tree, va_gc> *keyed_classes;
 
 #else /* NO_DOLLAR_IN_LABEL */
 
+#define JOIN_STR "_"
+
 #define VTABLE_NAME "__vt_"
 #define VTABLE_NAME_P(ID_NODE) \
   (!strncmp (IDENTIFIER_POINTER (ID_NODE), VTABLE_NAME, \
@@ -8292,6 +8311,9 @@ extern tree finish_co_yield_expr          (location_t, tree);
 extern tree coro_validate_builtin_call         (tree,
                                                 tsubst_flags_t = tf_warning_or_error);
 extern bool morph_fn_to_coro                   (tree, tree *, tree *);
+extern tree coro_get_actor_function            (tree);
+extern tree coro_get_destroy_function          (tree);
+extern tree coro_get_ramp_function             (tree);
 
 /* Inline bodies.  */
   
index ee14c2d..bf4abba 100644 (file)
@@ -832,6 +832,18 @@ write_encoding (const tree decl)
       write_bare_function_type (fn_type,
                                mangle_return_type_p (decl),
                                d);
+
+      /* If this is a coroutine helper, then append an appropriate string to
+        identify which.  */
+      if (tree ramp = DECL_RAMP_FN (decl))
+       {
+         if (DECL_ACTOR_FN (ramp) == decl)
+           write_string (JOIN_STR "actor");
+         else if (DECL_DESTROY_FN (ramp) == decl)
+           write_string (JOIN_STR "destroy");
+         else
+           gcc_unreachable ();
+       }
     }
 }
 
@@ -1423,9 +1435,12 @@ write_unqualified_name (tree decl)
        }
       else if (DECL_OVERLOADED_OPERATOR_P (decl))
        {
+         tree t;
+         if (!(t = DECL_RAMP_FN (decl)))
+           t = decl;
          const char *mangled_name
-           = (ovl_op_info[DECL_ASSIGNMENT_OPERATOR_P (decl)]
-              [DECL_OVERLOADED_OPERATOR_CODE_RAW (decl)].mangled_name);
+           = (ovl_op_info[DECL_ASSIGNMENT_OPERATOR_P (t)]
+              [DECL_OVERLOADED_OPERATOR_CODE_RAW (t)].mangled_name);
          write_string (mangled_name);
        }
       else if (UDLIT_OPER_P (DECL_NAME (decl)))
diff --git a/gcc/testsuite/g++.dg/coroutines/pr95520.C b/gcc/testsuite/g++.dg/coroutines/pr95520.C
new file mode 100644 (file)
index 0000000..4849b07
--- /dev/null
@@ -0,0 +1,29 @@
+// { dg-do run }
+// { dg-output "coroutine name: MyFoo" }
+#include <coroutine>
+#include <cstdio>
+
+struct pt
+{
+    using handle_t = std::coroutine_handle<pt>;
+    auto get_return_object() noexcept { return handle_t::from_promise(*this); }
+
+    std::suspend_never initial_suspend () const noexcept { return {}; }
+    std::suspend_never final_suspend () const noexcept { return {}; }
+    void return_void() const noexcept {}
+    void unhandled_exception() const noexcept {}
+};
+
+template <> struct std::coroutine_traits<pt::handle_t>
+    { using promise_type = pt; };
+
+static pt::handle_t MyFoo ()
+{ 
+    printf ("coroutine name: %s\n", __builtin_FUNCTION());
+    co_return;
+}
+
+int main()
+{
+    MyFoo ();
+}