The following fixes a bug in gimple_purge_dead_abnormal_call_edges which happily removes a EDGE_FALLTHRU|EDGE_ABNORMAL edge. The CFG builder via make_edge generally seems to merge edge flags and edges for edges between the same basic-blocks, so any abnormal call edges we insert may shadow the edge from the normal return.
The simplest fix is to just clear the EDGE_ABNORMAL flag in this case. A similar bug is in remove_fallthru_edge. Finally we get more decent test coverage for the non-local goto code. Bootstrap and regtest pending on x86_64-unknown-linux-gnu. Comments? Richard. 2013-05-03 Richard Biener <rguent...@suse.de> PR middle-end/57147 * tree-cfg.c (gimple_purge_dead_abnormal_call_edges): If the edge is also fallthru, preserve it and just clear the abnormal flag. * tree-cfgcleanup.c (remove_fallthru_edge): If the edge is also complex, preserve that and just clear the fallthru flag. * gcc.dg/torture/pr57147.c: New testcase. Index: gcc/tree-cfg.c =================================================================== *** gcc/tree-cfg.c (revision 198572) --- gcc/tree-cfg.c (working copy) *************** gimple_purge_dead_abnormal_call_edges (b *** 7628,7634 **** { if (e->flags & EDGE_ABNORMAL) { ! remove_edge_and_dominated_blocks (e); changed = true; } else --- 7648,7657 ---- { if (e->flags & EDGE_ABNORMAL) { ! if (e->flags & EDGE_FALLTHRU) ! e->flags &= ~EDGE_ABNORMAL; ! else ! remove_edge_and_dominated_blocks (e); changed = true; } else Index: gcc/tree-cfgcleanup.c =================================================================== *** gcc/tree-cfgcleanup.c (revision 198572) --- gcc/tree-cfgcleanup.c (working copy) *************** remove_fallthru_edge (vec<edge, va_gc> * *** 57,63 **** FOR_EACH_EDGE (e, ei, ev) if ((e->flags & EDGE_FALLTHRU) != 0) { ! remove_edge_and_dominated_blocks (e); return true; } return false; --- 57,66 ---- FOR_EACH_EDGE (e, ei, ev) if ((e->flags & EDGE_FALLTHRU) != 0) { ! if (e->flags & EDGE_COMPLEX) ! e->flags &= ~EDGE_FALLTHRU; ! else ! remove_edge_and_dominated_blocks (e); return true; } return false; Index: gcc/testsuite/gcc.dg/torture/pr57147-1.c =================================================================== --- gcc/testsuite/gcc.dg/torture/pr57147-1.c (revision 0) +++ gcc/testsuite/gcc.dg/torture/pr57147-1.c (working copy) @@ -0,0 +1,20 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-optimized" } */ + +struct __jmp_buf_tag {}; +typedef struct __jmp_buf_tag jmp_buf[1]; +extern int _setjmp (struct __jmp_buf_tag __env[1]); + +jmp_buf g_return_jmp_buf; + +void SetNaClSwitchExpectations (void) +{ +} +void TestSyscall(void) +{ + SetNaClSwitchExpectations(); + _setjmp (g_return_jmp_buf); +} + +/* { dg-final { scan-tree-dump-not "builtin_unreachable" "optimized" } } */ +/* { dg-final { cleanup-tree-dump "optimized" } } */ Index: gcc/testsuite/gcc.dg/torture/pr57147-2.c =================================================================== --- gcc/testsuite/gcc.dg/torture/pr57147-2.c (revision 0) +++ gcc/testsuite/gcc.dg/torture/pr57147-2.c (working copy) @@ -0,0 +1,21 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-optimized" } */ + +struct __jmp_buf_tag {}; +typedef struct __jmp_buf_tag jmp_buf[1]; +extern int _setjmp (struct __jmp_buf_tag __env[1]); + +jmp_buf g_return_jmp_buf; + +void SetNaClSwitchExpectations (void) +{ + __builtin_longjmp (g_return_jmp_buf, 1); +} +void TestSyscall(void) +{ + SetNaClSwitchExpectations(); + _setjmp (g_return_jmp_buf); +} + +/* { dg-final { scan-tree-dump "setjmp" "optimized" } } */ +/* { dg-final { cleanup-tree-dump "optimized" } } */