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.
/* 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)
--- /dev/null
+// 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;
+}