https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126568
Bug ID: 126568
Summary: [17 Regression] Missed handling of exceptions in
factor_out_conditional_operation
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Keywords: wrong-code
Severity: normal
Priority: P3
Component: tree-optimization
Assignee: unassigned at gcc dot gnu.org
Reporter: ktkachov at gcc dot gnu.org
Target Milestone: ---
/* factor_out_conditional_operation (gcc/tree-ssa-phiopt.cc:329) removes the
two arm statements with gsi_remove (&gsi, true) and builds the replacement
with maybe_push_res_to_seq. Neither step transfers the EH region of the
removed statements to the new one, and there is no
maybe_clean_or_replace_eh_stmt / add_stmt_to_eh_lp anywhere in
tree-ssa-phiopt.cc.
The shape filter in execute_over_cond_phis only requires single_succ_p on
the arm blocks. That excludes lp_nr > 0 (a landing pad makes an EDGE_EH
successor) but NOT lp_nr < 0, which is what record_stmt_eh_region stores
for an ERT_MUST_NOT_THROW region. So the two divisions of a `noexcept'
body under -fnon-call-exceptions sit in single-successor blocks and reach
the transform.
Before phiopt the divisions carry lp_nr < 0, so convert_to_eh_region_ranges
gives them action -2, "no call-site entry", which is how the personality
routine is told to run std::terminate. After phiopt the merged division
has lp_nr == 0, gets an ordinary action-0 call-site entry (or no LSDA at
all), and the exception unwinds straight out of the noexcept function.
g++ -O2 -fnon-call-exceptions -fexceptions, x86_64 (needs a division that
actually traps; AArch64 sdiv does not fault on a zero divisor).
Correct: std::terminate. Broken: the throw escapes divsel and main catches
it. */
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <exception>
static void sigfpe_handler (int) { throw 1; }
static void my_terminate () { printf ("TERMINATE\n"); exit (0); }
volatile int zero = 0;
/* Opaque possibly-throwing call. It opens an EH range in main that also
covers the call to divsel, so an escaping exception is observable. */
__attribute__((noipa)) void maythrow (void) { if (zero) throw 2; }
__attribute__((noipa))
int divsel (int c, int a, int b, int d) noexcept
{
int x;
if (c)
x = a / d;
else
x = b / d;
return x;
}
int main (void)
{
std::set_terminate (my_terminate);
struct sigaction sa;
sa.sa_handler = sigfpe_handler;
sigemptyset (&sa.sa_mask);
sa.sa_flags = SA_NODEFER;
sigaction (SIGFPE, &sa, NULL);
try
{
maythrow ();
int r = divsel (1, 4, 8, 0);
printf ("NO TRAP r=%d\n", r);
__builtin_abort ();
}
catch (int)
{
printf ("ESCAPED noexcept, caught in main\n");
__builtin_abort ();
}
return 0;
}
This ends with SIGSEGV on x86_64 with -O2 -fnon-call-exceptions -fexceptions