This fixes PR56982 by properly modeling the control-flow of setjmp. It basically behaves as a non-local goto target so this patch treats it so - it makes it start a basic-block and get abnormal edges from possible sources of non-local gotos. The patch also fixes the bug that longjmp is marked as "leaf".
Bootstrapped and tested on x86_64-unknown-linux-gnu, ok for trunk? What about release branches (after it had some time to settle on trunk of course)? Thanks, Richard. 2013-04-17 Richard Biener <rguent...@suse.de> PR tree-optimization/56982 * builtins.def (BUILT_IN_LONGJMP): longjmp is not a leaf function. * gimplify.c (gimplify_call_expr): Notice special calls. (gimplify_modify_expr): Likewise. * tree-cfg.c (make_abnormal_goto_edges): Handle setjmp-like abnormal control flow receivers. (call_can_make_abnormal_goto): Handle cfun->calls_setjmp in the same way as cfun->has_nonlocal_labels. (gimple_purge_dead_abnormal_call_edges): Likewise. (stmt_starts_bb_p): Make setjmp-like abnormal control flow receivers start a basic-block. * gcc.c-torture/execute/pr56982.c: New testcase. Index: gcc/gimplify.c =================================================================== *** gcc/gimplify.c (revision 198021) --- gcc/gimplify.c (working copy) *************** gimplify_call_expr (tree *expr_p, gimple *** 2729,2734 **** --- 2729,2735 ---- gimple_stmt_iterator gsi; call = gimple_build_call_from_tree (*expr_p); gimple_call_set_fntype (call, TREE_TYPE (fnptrtype)); + notice_special_calls (call); gimplify_seq_add_stmt (pre_p, call); gsi = gsi_last (*pre_p); fold_stmt (&gsi); *************** gimplify_modify_expr (tree *expr_p, gimp *** 4968,4973 **** --- 4969,4975 ---- STRIP_USELESS_TYPE_CONVERSION (CALL_EXPR_FN (*from_p)); assign = gimple_build_call_from_tree (*from_p); gimple_call_set_fntype (assign, TREE_TYPE (fnptrtype)); + notice_special_calls (assign); if (!gimple_call_noreturn_p (assign)) gimple_call_set_lhs (assign, *to_p); } Index: gcc/tree-cfg.c =================================================================== *** gcc/tree-cfg.c (revision 198021) --- gcc/tree-cfg.c (working copy) *************** make_abnormal_goto_edges (basic_block bb *** 967,991 **** gimple_stmt_iterator gsi; FOR_EACH_BB (target_bb) ! for (gsi = gsi_start_bb (target_bb); !gsi_end_p (gsi); gsi_next (&gsi)) ! { ! gimple label_stmt = gsi_stmt (gsi); ! tree target; ! if (gimple_code (label_stmt) != GIMPLE_LABEL) ! break; ! target = gimple_label_label (label_stmt); ! /* Make an edge to every label block that has been marked as a ! potential target for a computed goto or a non-local goto. */ ! if ((FORCED_LABEL (target) && !for_call) ! || (DECL_NONLOCAL (target) && for_call)) ! { make_edge (bb, target_bb, EDGE_ABNORMAL); ! break; ! } ! } } /* Create edges for a goto statement at block BB. */ --- 971,1005 ---- gimple_stmt_iterator gsi; FOR_EACH_BB (target_bb) ! { ! for (gsi = gsi_start_bb (target_bb); !gsi_end_p (gsi); gsi_next (&gsi)) ! { ! gimple label_stmt = gsi_stmt (gsi); ! tree target; ! if (gimple_code (label_stmt) != GIMPLE_LABEL) ! break; ! target = gimple_label_label (label_stmt); ! /* Make an edge to every label block that has been marked as a ! potential target for a computed goto or a non-local goto. */ ! if ((FORCED_LABEL (target) && !for_call) ! || (DECL_NONLOCAL (target) && for_call)) ! { ! make_edge (bb, target_bb, EDGE_ABNORMAL); ! break; ! } ! } ! if (!gsi_end_p (gsi)) ! { ! /* Make an edge to every setjmp-like call. */ ! gimple call_stmt = gsi_stmt (gsi); ! if (is_gimple_call (call_stmt) ! && (gimple_call_flags (call_stmt) & ECF_RETURNS_TWICE)) make_edge (bb, target_bb, EDGE_ABNORMAL); ! } ! } } /* Create edges for a goto statement at block BB. */ *************** call_can_make_abnormal_goto (gimple t) *** 2147,2153 **** { /* If the function has no non-local labels, then a call cannot make an abnormal transfer of control. */ ! if (!cfun->has_nonlocal_label) return false; /* Likewise if the call has no side effects. */ --- 2161,2168 ---- { /* If the function has no non-local labels, then a call 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. */ *************** stmt_starts_bb_p (gimple stmt, gimple pr *** 2302,2307 **** --- 2317,2327 ---- else return true; } + else if (gimple_code (stmt) == GIMPLE_CALL + && gimple_call_flags (stmt) & ECF_RETURNS_TWICE) + /* setjmp acts similar to a nonlocal GOTO target and thus should + start a new block. */ + return true; return false; } *************** gimple_purge_dead_abnormal_call_edges (b *** 7532,7538 **** edge_iterator ei; gimple stmt = last_stmt (bb); ! if (!cfun->has_nonlocal_label) return false; if (stmt && stmt_can_make_abnormal_goto (stmt)) --- 7568,7575 ---- 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)) Index: gcc/builtins.def =================================================================== *** gcc/builtins.def (revision 198021) --- gcc/builtins.def (working copy) *************** DEF_GCC_BUILTIN (BUILT_IN_ISLESSG *** 715,721 **** DEF_GCC_BUILTIN (BUILT_IN_ISUNORDERED, "isunordered", BT_FN_INT_VAR, ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF) DEF_LIB_BUILTIN (BUILT_IN_LABS, "labs", BT_FN_LONG_LONG, ATTR_CONST_NOTHROW_LEAF_LIST) DEF_C99_BUILTIN (BUILT_IN_LLABS, "llabs", BT_FN_LONGLONG_LONGLONG, ATTR_CONST_NOTHROW_LEAF_LIST) ! DEF_GCC_BUILTIN (BUILT_IN_LONGJMP, "longjmp", BT_FN_VOID_PTR_INT, ATTR_NORETURN_NOTHROW_LEAF_LIST) /* [trans-mem]: Adjust BUILT_IN_TM_MALLOC if BUILT_IN_MALLOC is changed. */ DEF_LIB_BUILTIN (BUILT_IN_MALLOC, "malloc", BT_FN_PTR_SIZE, ATTR_MALLOC_NOTHROW_LEAF_LIST) DEF_GCC_BUILTIN (BUILT_IN_NEXT_ARG, "next_arg", BT_FN_PTR_VAR, ATTR_LEAF_LIST) --- 715,721 ---- DEF_GCC_BUILTIN (BUILT_IN_ISUNORDERED, "isunordered", BT_FN_INT_VAR, ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF) DEF_LIB_BUILTIN (BUILT_IN_LABS, "labs", BT_FN_LONG_LONG, ATTR_CONST_NOTHROW_LEAF_LIST) DEF_C99_BUILTIN (BUILT_IN_LLABS, "llabs", BT_FN_LONGLONG_LONGLONG, ATTR_CONST_NOTHROW_LEAF_LIST) ! DEF_GCC_BUILTIN (BUILT_IN_LONGJMP, "longjmp", BT_FN_VOID_PTR_INT, ATTR_NORETURN_NOTHROW_LIST) /* [trans-mem]: Adjust BUILT_IN_TM_MALLOC if BUILT_IN_MALLOC is changed. */ DEF_LIB_BUILTIN (BUILT_IN_MALLOC, "malloc", BT_FN_PTR_SIZE, ATTR_MALLOC_NOTHROW_LEAF_LIST) DEF_GCC_BUILTIN (BUILT_IN_NEXT_ARG, "next_arg", BT_FN_PTR_VAR, ATTR_LEAF_LIST) Index: gcc/testsuite/gcc.c-torture/execute/pr56982.c =================================================================== *** gcc/testsuite/gcc.c-torture/execute/pr56982.c (revision 0) --- gcc/testsuite/gcc.c-torture/execute/pr56982.c (working copy) *************** *** 0 **** --- 1,43 ---- + #include <stdlib.h> + #include <setjmp.h> + + static sigjmp_buf env; + void *stderr; + void baz (void) + { + __asm__ volatile ("" : : : "memory"); + } + + static inline int g(int x) + { + if (x) + { + baz(); + return 0; + } + else + { + baz(); + return 1; + } + } + + int f(int *e) + { + if (*e) + return 1; + + int x = setjmp(env); + int n = g(x); + if (n == 0) + exit(0); + if (x) + abort(); + longjmp(env, 42); + } + + int main(int argc, char** argv) + { + int v = 0; + return f(&v); + }