I am testing the following. There's IMHO also a missed optimization (for CFG-cleanup?) that when a block does not end up in a call outgoing abnormal edges can be purged? In this case it is IPA inlining leaving us with this - the inliner calls gimple_purge_dead_abnormal_call_edges on the return block but both cfun->has_nonlocal_label and cfun->calls_setjmp are false so the function does nothing. This is probably a premature check there?
Anyhow, the fix below is safer at this point (also for backporting), but it's the 2nd spot of this kind I fix so I wonder if we should fix the above for GCC 9? The real issue might be that we are removing the "last" returns_twice call but retain abnormal edges not originating from it (and we'll never cleanup the rest because of those early outs). DCE is the one that recomputes calls_setjmp but does not remove abnormal edges. Other passes might also elide such calls but they leave calls_setjmp as-is. I'm a bit nervous with an idea that DCE can purge all abnormal edges if !cfun->calls_setjmp && !cfun->has_nonlocal_label. Anyway, looks a bit like a can of worms and it's better to not rely on abnormal edges not being present when the block is empty. Richard. 2019-03-14 Richard Biener <rguent...@suse.de> PR tree-optimization/89710 * tree-ssa-loop-ch.c (should_duplicate_loop_header_p): Use safe_dyn_cast. * gcc.dg/torture/pr89710.c: New testcase. Index: gcc/tree-ssa-loop-ch.c =================================================================== --- gcc/tree-ssa-loop-ch.c (revision 269678) +++ gcc/tree-ssa-loop-ch.c (working copy) @@ -100,7 +100,7 @@ should_duplicate_loop_header_p (basic_bl return false; } - gcond *last = dyn_cast <gcond *> (last_stmt (header)); + gcond *last = safe_dyn_cast <gcond *> (last_stmt (header)); if (!last) { if (dump_file && (dump_flags & TDF_DETAILS)) Index: gcc/testsuite/gcc.dg/torture/pr89710.c =================================================================== --- gcc/testsuite/gcc.dg/torture/pr89710.c (nonexistent) +++ gcc/testsuite/gcc.dg/torture/pr89710.c (working copy) @@ -0,0 +1,30 @@ +/* { dg-do compile } */ + +void +gm (int *); + +__attribute__ ((returns_twice)) void +jg (void) +{ +} + +void +eb (void) +{ + int r6 = 0; + + if (r6 != 0) + gm (&r6); +} + +void +gm (int *r6) +{ + jg (); + + for (;;) + { + eb (); + *r6 = 0; + } +} diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c index 0dc94ea41d4..69837328279 100644 --- a/gcc/tree-cfg.c +++ b/gcc/tree-cfg.c @@ -2585,13 +2585,8 @@ debug_cfg_stats (void) static bool call_can_make_abnormal_goto (gimple *t) { - /* If the function has no non-local labels, then a call cannot make an + /* If the call has no side effects, then it cannot make an abnormal transfer of control. */ - if (!cfun->has_nonlocal_label - && !cfun->calls_setjmp) - return false; - - /* Likewise if the call has no side effects. */ if (!gimple_has_side_effects (t)) return false; @@ -8670,10 +8665,6 @@ gimple_purge_dead_abnormal_call_edges (basic_block bb) edge_iterator ei; gimple *stmt = last_stmt (bb); - if (!cfun->has_nonlocal_label - && !cfun->calls_setjmp) - return false; - if (stmt && stmt_can_make_abnormal_goto (stmt)) return false;