Hi! As described in the PR, __builtin_setjmp_receiver isn't declared to returns_twice, and thus after dce cfun->calls_setjmp might be no longer true. At RTL __builtin_setjmp_receiver is handled as non-local label, so this patch just forces cfun->has_nonlocal_label already in GIMPLE, so that e.g. the inliner still sees stmt_can_make_nonlocal_goto.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2014-01-31 Jakub Jelinek <ja...@redhat.com> PR tree-optimization/60003 * gimple-low.c (lower_builtin_setjmp): Set cfun->has_nonlocal_label. * profile.c (branch_prob): Use gimple_call_builtin_p to check for BUILT_IN_SETJMP_RECEIVER. * tree-inline.c (copy_bb): Call notice_special_calls. * gcc.c-torture/execute/pr60003.c: New test. --- gcc/gimple-low.c.jj 2014-01-29 12:43:25.000000000 +0100 +++ gcc/gimple-low.c 2014-01-31 10:02:38.843026680 +0100 @@ -709,6 +709,12 @@ lower_builtin_setjmp (gimple_stmt_iterat tree dest, t, arg; gimple g; + /* __builtin_setjmp_{setup,receiver} aren't ECF_RETURNS_TWICE and for RTL + these builtins are modelled as non-local label jumps to the label + that is passed to these two builtins, so pretend we have a non-local + label during GIMPLE passes too. See PR60003. */ + cfun->has_nonlocal_label = true; + /* NEXT_LABEL is the label __builtin_longjmp will jump to. Its address is passed to both __builtin_setjmp_setup and __builtin_setjmp_receiver. */ FORCED_LABEL (next_label) = 1; --- gcc/profile.c.jj 2014-01-29 12:43:25.000000000 +0100 +++ gcc/profile.c 2014-01-31 10:18:00.450198256 +0100 @@ -1104,7 +1104,6 @@ branch_prob (void) { gimple_stmt_iterator gsi; gimple first; - tree fndecl; gsi = gsi_start_nondebug_after_labels_bb (bb); gcc_checking_assert (!gsi_end_p (gsi)); @@ -1114,10 +1113,7 @@ branch_prob (void) special and don't expect anything to be inserted before them. */ if (is_gimple_call (first) - && (((fndecl = gimple_call_fndecl (first)) != NULL - && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL - && (DECL_FUNCTION_CODE (fndecl) - == BUILT_IN_SETJMP_RECEIVER)) + && (gimple_call_builtin_p (first, BUILT_IN_SETJMP_RECEIVER) || (gimple_call_flags (first) & ECF_RETURNS_TWICE) || (gimple_call_internal_p (first) && (gimple_call_internal_fn (first) --- gcc/tree-inline.c.jj 2014-01-29 12:43:24.000000000 +0100 +++ gcc/tree-inline.c 2014-01-31 10:19:13.849815593 +0100 @@ -1745,7 +1745,6 @@ copy_bb (copy_body_data *id, basic_block if (is_gimple_call (stmt)) { struct cgraph_edge *edge; - int flags; switch (id->transform_call_graph_edges) { @@ -1868,11 +1867,7 @@ copy_bb (copy_body_data *id, basic_block } } - flags = gimple_call_flags (stmt); - if (flags & ECF_MAY_BE_ALLOCA) - cfun->calls_alloca = true; - if (flags & ECF_RETURNS_TWICE) - cfun->calls_setjmp = true; + notice_special_calls (stmt); } maybe_duplicate_eh_stmt_fn (cfun, stmt, id->src_cfun, orig_stmt, --- gcc/testsuite/gcc.c-torture/execute/pr60003.c.jj 2014-01-31 10:05:15.095205547 +0100 +++ gcc/testsuite/gcc.c-torture/execute/pr60003.c 2014-01-31 10:04:59.000000000 +0100 @@ -0,0 +1,48 @@ +/* PR tree-optimization/60003 */ + +extern void abort (void); + +unsigned long long jmp_buf[5]; + +__attribute__((noinline, noclone)) void +baz (void) +{ + __builtin_longjmp (&jmp_buf, 1); +} + +void +bar (void) +{ + baz (); +} + +__attribute__((noinline, noclone)) int +foo (int x) +{ + int a = 0; + + if (__builtin_setjmp (&jmp_buf) == 0) + { + while (1) + { + a = 1; + bar (); /* OK if baz () instead */ + } + } + else + { + if (a == 0) + return 0; + else + return x; + } +} + +int +main () +{ + if (foo (1) == 0) + abort (); + + return 0; +} Jakub