diff mbox series

coros: mark .CO_YIELD as LEAF [PR106973]

Message ID 20240903181009.448675-1-arsen@aarsen.me
State New
Headers show
Series coros: mark .CO_YIELD as LEAF [PR106973] | expand

Commit Message

Arsen Arsenović Sept. 3, 2024, 5:57 p.m. UTC
Tested on x86_64-pc-linux-gnu.  OK for trunk?
---------- >8 ----------
We rely on .CO_YIELD calls being followed by an assignment (optionally)
and then a switch/if in the same basic block.  This implies that a
.CO_YIELD can never end a block.  However, since a call to .CO_YIELD is
still a call, if the function containing it calls setjmp, GCC thinks
that the .CO_YIELD can introduce abnormal control flow, and generates an
edge for the call.

We know this is not the case; .CO_YIELD calls get removed quite early on
and have no effect, and result in no other calls, so .CO_YIELD can be
considered a leaf function, preventing generating an edge when calling
it.

PR c++/106973 - coroutine generator and setjmp

	PR c++/106973

gcc/ChangeLog:

	* internal-fn.def (CO_YIELD): Mark as ECF_LEAF.

gcc/testsuite/ChangeLog:

	* g++.dg/coroutines/pr106973.C: New test.
---
 gcc/internal-fn.def                        |  2 +-
 gcc/testsuite/g++.dg/coroutines/pr106973.C | 22 ++++++++++++++++++++++
 2 files changed, 23 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/g++.dg/coroutines/pr106973.C

Comments

Richard Biener Sept. 4, 2024, 7:36 a.m. UTC | #1
On Tue, Sep 3, 2024 at 8:11 PM Arsen Arsenović <arsen@aarsen.me> wrote:
>
> Tested on x86_64-pc-linux-gnu.  OK for trunk?

OK

> ---------- >8 ----------
> We rely on .CO_YIELD calls being followed by an assignment (optionally)
> and then a switch/if in the same basic block.  This implies that a
> .CO_YIELD can never end a block.  However, since a call to .CO_YIELD is
> still a call, if the function containing it calls setjmp, GCC thinks
> that the .CO_YIELD can introduce abnormal control flow, and generates an
> edge for the call.
>
> We know this is not the case; .CO_YIELD calls get removed quite early on
> and have no effect, and result in no other calls, so .CO_YIELD can be
> considered a leaf function, preventing generating an edge when calling
> it.
>
> PR c++/106973 - coroutine generator and setjmp
>
>         PR c++/106973
>
> gcc/ChangeLog:
>
>         * internal-fn.def (CO_YIELD): Mark as ECF_LEAF.
>
> gcc/testsuite/ChangeLog:
>
>         * g++.dg/coroutines/pr106973.C: New test.
> ---
>  gcc/internal-fn.def                        |  2 +-
>  gcc/testsuite/g++.dg/coroutines/pr106973.C | 22 ++++++++++++++++++++++
>  2 files changed, 23 insertions(+), 1 deletion(-)
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/pr106973.C
>
> diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
> index 75b527b1ab0b..23b4ab02b300 100644
> --- a/gcc/internal-fn.def
> +++ b/gcc/internal-fn.def
> @@ -569,7 +569,7 @@ DEF_INTERNAL_FN (DIVMOD, ECF_CONST | ECF_LEAF, NULL)
>
>  /* For coroutines.  */
>  DEF_INTERNAL_FN (CO_ACTOR, ECF_NOTHROW | ECF_LEAF, NULL)
> -DEF_INTERNAL_FN (CO_YIELD, ECF_NOTHROW, NULL)
> +DEF_INTERNAL_FN (CO_YIELD, ECF_NOTHROW | ECF_LEAF, NULL)
>  DEF_INTERNAL_FN (CO_SUSPN, ECF_NOTHROW, NULL)
>  DEF_INTERNAL_FN (CO_FRAME, ECF_PURE | ECF_NOTHROW | ECF_LEAF, NULL)
>
> diff --git a/gcc/testsuite/g++.dg/coroutines/pr106973.C b/gcc/testsuite/g++.dg/coroutines/pr106973.C
> new file mode 100644
> index 000000000000..6db6cbc7711a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/pr106973.C
> @@ -0,0 +1,22 @@
> +// https://gcc.gnu.org/PR106973
> +// { dg-require-effective-target indirect_jumps }
> +#include <coroutine>
> +#include <setjmp.h>
> +
> +struct generator;
> +struct generator_promise {
> +  generator get_return_object();
> +  std::suspend_always initial_suspend();
> +  std::suspend_always final_suspend() noexcept;
> +  std::suspend_always yield_value(int);
> +  void unhandled_exception();
> +};
> +
> +struct generator {
> +  using promise_type = generator_promise;
> +};
> +jmp_buf foo_env;
> +generator foo() {
> +  setjmp(foo_env);
> +  co_yield 1;
> +}
> --
> 2.46.0
>
diff mbox series

Patch

diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index 75b527b1ab0b..23b4ab02b300 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -569,7 +569,7 @@  DEF_INTERNAL_FN (DIVMOD, ECF_CONST | ECF_LEAF, NULL)
 
 /* For coroutines.  */
 DEF_INTERNAL_FN (CO_ACTOR, ECF_NOTHROW | ECF_LEAF, NULL)
-DEF_INTERNAL_FN (CO_YIELD, ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (CO_YIELD, ECF_NOTHROW | ECF_LEAF, NULL)
 DEF_INTERNAL_FN (CO_SUSPN, ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (CO_FRAME, ECF_PURE | ECF_NOTHROW | ECF_LEAF, NULL)
 
diff --git a/gcc/testsuite/g++.dg/coroutines/pr106973.C b/gcc/testsuite/g++.dg/coroutines/pr106973.C
new file mode 100644
index 000000000000..6db6cbc7711a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/pr106973.C
@@ -0,0 +1,22 @@ 
+// https://gcc.gnu.org/PR106973
+// { dg-require-effective-target indirect_jumps }
+#include <coroutine>
+#include <setjmp.h>
+
+struct generator;
+struct generator_promise {
+  generator get_return_object();
+  std::suspend_always initial_suspend();
+  std::suspend_always final_suspend() noexcept;
+  std::suspend_always yield_value(int);
+  void unhandled_exception();
+};
+
+struct generator {
+  using promise_type = generator_promise;
+};
+jmp_buf foo_env;
+generator foo() {
+  setjmp(foo_env);
+  co_yield 1;
+}