public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] coroutines: Wrap awaiting if/swich in a BIND_EXPR, if needed [PR106188]
@ 2022-09-03 20:57 Arsen Arsenović
  2022-09-04 10:35 ` Iain Sandoe
  0 siblings, 1 reply; 7+ messages in thread
From: Arsen Arsenović @ 2022-09-03 20:57 UTC (permalink / raw)
  To: gcc-patches; +Cc: jason, nathan, iain, Arsen Arsenović

In the edge case of a coroutine not containing any locals, the ifcd/swch
temporaries would get added to the coroutine frame, corrupting its
layout. We can prevent this by ensuring that in cases where the scope of
the if/switch is the same as the coroutine frame one, we insert a
wrapping BIND_EXPR and try again.

PR c++/106188 - [coroutines] Incorrect frame layout after transforming conditional statement without top-level bind expression

PR c++/106713 - [11/12/13 Regression] Coroutine regression in GCC 11.3.0: if (co_await ...) crashes with a jump to ud2 since r12-3529-g70ee703c479081ac

gcc/cp/ChangeLog:
	PR c++/106188
	PR c++/106713
	* coroutines.cc (struct susp_frame_data): Add fn_body for
	  informing the await_statement_walker of the coroutine body.
	(maybe_add_bind): New function.
	(await_statement_walker): Call maybe_add_bind when necessary.
	(morph_fn_to_coro): Pass fnbody into susp_frame_data.

gcc/testsuite/ChangeLog:

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

Signed-off-by: Arsen Arsenović <arsen@aarsen.me>
---
 gcc/cp/coroutines.cc                       | 45 ++++++++++++++++++++--
 gcc/testsuite/g++.dg/coroutines/pr106188.C | 35 +++++++++++++++++
 2 files changed, 77 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/coroutines/pr106188.C

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index edb3b706ddc..2e88fb99d7d 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -2586,6 +2586,7 @@ struct susp_frame_data
   bool captures_temporary;	 /* This expr captures temps by ref.  */
   bool needs_truth_if_exp;	 /* We must expand a truth_if expression.  */
   bool has_awaiter_init;	 /* We must handle initializing an awaiter.  */
+  tree *fn_body;		 /* Original function body */
 };
 
 /* If this is an await expression, then count it (both uniquely within the
@@ -3326,6 +3327,33 @@ add_var_to_bind (tree& bind, tree var_type,
   return newvar;
 }
 
+/* Helper to ensure we have at least one bind to add vars to. */
+static bool
+maybe_add_bind(susp_frame_data *awpts, tree *stmt, location_t sloc)
+{
+  /* bind_stack is already asserted to be nonempty */
+  tree &bind = awpts->bind_stack->last();
+  gcc_checking_assert(TREE_CODE (*awpts->fn_body) == BIND_EXPR);
+  if (BIND_EXPR_VARS (bind) != BIND_EXPR_VARS (*awpts->fn_body))
+    {
+      /* Alright, we have a unique (enough) scope, bail early. */
+      return false;
+    }
+
+  /* In this case, we'd be pushing variables to the start of the coroutine
+   * unless we add a new BIND_EXPR here.
+   */
+  tree ifsw_bind = build3_loc (sloc, BIND_EXPR, void_type_node,
+			     NULL, NULL, NULL);
+  BIND_EXPR_BODY (ifsw_bind) = *stmt;
+  *stmt = ifsw_bind;
+  /* We don't push this BIND_EXPR to the walkers bind stack, it will be handled
+   * by a call to cp_walk_tree, since we start the next walk off from this
+   * bind node.
+   */
+  return true;
+}
+
 /* Helper to build and add if (!cond) break;  */
 
 static void
@@ -3456,6 +3484,12 @@ await_statement_walker (tree *stmt, int *do_subtree, void *d)
 	      return NULL_TREE; /* Nothing special to do here.  */
 
 	    gcc_checking_assert (!awpts->bind_stack->is_empty());
+	    location_t sloc = EXPR_LOCATION (IF_COND (if_stmt));
+	    if (maybe_add_bind (awpts, stmt, sloc))
+              {
+		*do_subtree = false; /* Done inside maybe_add_bind. */
+		return cp_walk_tree (stmt, await_statement_walker, awpts, NULL);
+              }
 	    tree& bind_expr = awpts->bind_stack->last ();
 	    tree newvar = add_var_to_bind (bind_expr, boolean_type_node,
 					   "ifcd", awpts->cond_number++);
@@ -3464,7 +3498,6 @@ await_statement_walker (tree *stmt, int *do_subtree, void *d)
 	    if (TREE_CODE (cond_inner) == CLEANUP_POINT_EXPR)
 	      cond_inner = TREE_OPERAND (cond_inner, 0);
 	    add_decl_expr (newvar);
-	    location_t sloc = EXPR_LOCATION (IF_COND (if_stmt));
 	    /* We want to initialize the new variable with the expression
 	       that contains the await(s) and potentially also needs to
 	       have truth_if expressions expanded.  */
@@ -3640,6 +3673,12 @@ await_statement_walker (tree *stmt, int *do_subtree, void *d)
 	      return NULL_TREE; /* Nothing special to do here.  */
 
 	    gcc_checking_assert (!awpts->bind_stack->is_empty());
+	    location_t sloc = EXPR_LOCATION (SWITCH_STMT_COND (sw_stmt));
+	    if (maybe_add_bind (awpts, stmt, sloc))
+              {
+		*do_subtree = false; /* Done inside maybe_add_bind. */
+		return cp_walk_tree (stmt, await_statement_walker, awpts, NULL);
+              }
 	    /* Build a variable to hold the condition, this will be
 		   included in the frame as a local var.  */
 	    tree& bind_expr = awpts->bind_stack->last ();
@@ -3652,7 +3691,6 @@ await_statement_walker (tree *stmt, int *do_subtree, void *d)
 	    tree cond_inner = SWITCH_STMT_COND (sw_stmt);
 	    if (TREE_CODE (cond_inner) == CLEANUP_POINT_EXPR)
 	      cond_inner = TREE_OPERAND (cond_inner, 0);
-	    location_t sloc = EXPR_LOCATION (SWITCH_STMT_COND (sw_stmt));
 	    tree new_s = build2_loc (sloc, INIT_EXPR, sw_type, newvar,
 				     cond_inner);
 	    finish_expr_stmt (new_s);
@@ -4477,7 +4515,8 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
      vars) they will get added to the coro frame along with other locals.  */
   susp_frame_data body_aw_points
     = {&field_list, handle_type, fs_label, NULL, NULL, 0, 0,
-       hash_set<tree> (), NULL, NULL, 0, false, false, false};
+       hash_set<tree> (), NULL, NULL, 0, false, false, false,
+       &fnbody};
   body_aw_points.block_stack = make_tree_vector ();
   body_aw_points.bind_stack = make_tree_vector ();
   body_aw_points.to_replace = make_tree_vector ();
diff --git a/gcc/testsuite/g++.dg/coroutines/pr106188.C b/gcc/testsuite/g++.dg/coroutines/pr106188.C
new file mode 100644
index 00000000000..1de3d4cceca
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/pr106188.C
@@ -0,0 +1,35 @@
+//  { dg-additional-options "-std=c++20 -w" }
+//  { dg-do run }
+// test case from pr106188, w/o workaround
+#include <coroutine>
+
+struct task {
+  struct promise_type {
+    task get_return_object() { return task{}; }
+    void return_void() {}
+    void unhandled_exception() {}
+    auto initial_suspend() noexcept { return std::suspend_never{}; }
+    auto final_suspend() noexcept { return std::suspend_never{}; }
+  };
+};
+
+struct suspend_and_resume {
+  bool await_ready() const { return false; }
+  void await_suspend(std::coroutine_handle<> h) { h.resume(); }
+  void await_resume() {}
+};
+
+task f() {
+  if (co_await suspend_and_resume{}, false) {}
+}
+
+task g() {
+  switch (co_await suspend_and_resume{}, 0) {
+    default: break;
+  }
+}
+
+int main() {
+  f();
+  g();
+}
-- 
2.35.1


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

end of thread, other threads:[~2022-09-07 15:28 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-09-03 20:57 [PATCH] coroutines: Wrap awaiting if/swich in a BIND_EXPR, if needed [PR106188] Arsen Arsenović
2022-09-04 10:35 ` Iain Sandoe
2022-09-04 19:04   ` [PATCH v2] coroutines: Ensure there's a top level bind when rewriting [PR106188] Arsen Arsenović
2022-09-04 22:09     ` Iain Sandoe
2022-09-05 11:15       ` Arsen Arsenović
2022-09-07 15:13     ` Jason Merrill
2022-09-07 15:28       ` Arsen Arsenović

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).