diff --git a/gcc/ChangeLog b/gcc/ChangeLog index c4161a0b3b1..3f96481599e 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,50 @@ +2006-09-28 Eric Botcazou + + * builtins.c (expand_builtin_setjmp): Delete. + (expand_builtin) : Mark as unreachable. + : New case. + : Likewise. + : Likewise. + * builtins.def (BUILT_IN_SETJMP_SETUP): New built-in stub. + (BUILT_IN_SETJMP_DISPATCHER): Likewise. + (BUILT_IN_SETJMP_RECEIVER): Likewise. + * gimple-low.c (struct lower_data): New field calls_builtin_setjmp. + (lower_function_body): Initialize it to false. If it is set to true + at the end of the processing, emit the setjmp dispatcher. + (lower_stmt) : Invoke lower_builtin_setjmp if the callee + is __builtin_setjmp and set calls_builtin_setjmp to true as well. + : Fall through to above case if there is a CALL_EXPR + on the rhs of the assignment. + (lower_builtin_setjmp): New function. + * tree.c (build_common_builtin_nodes): Build BUILT_IN_SETJMP_SETUP, + BUILT_IN_SETJMP_DISPATCHER and BUILT_IN_SETJMP_RECEIVER nodes. + * tree-cfg.c (make_exit_edges) : Use specific predicate + to detect calls that can go to non-local labels. Use specific + helper to create the abnormal edges associated with them. + : Likewise. + (make_abnormal_goto_edges): New function extracted from... + (make_goto_expr_edges): ...here. Call it for computed gotos. + (simple_goto_p): Minor tweak. + (tree_can_make_abnormal_goto): New predicate. + (tree_redirect_edge_and_branch): Return zero on all abnormal edges. + (tree_purge_dead_abnormal_call_edges): New function. + * tree-flow.h (tree_can_make_abnormal_goto): Declare. + (tree_purge_dead_abnormal_call_edges): Likewise. + (make_abnormal_goto_edges): Likewise. + * tree-inline.c (expand_call_inline): Simplify statement frobbing. + Purge all dead abnormal edges if the call was in the last statement. + * tree-optimize.c (has_abnormal_outgoing_edge_p): New predicate. + (execute_fixup_cfg): If there are non-local labels in the function, + scan the basic blocks and split them at calls that can go to non-local + labels or add missing abnormal call edges. Write down the CFG in the + dump file. + (pass_fixup_cfg): Remove TODO_dump_func flag. + * unwind-sjlj.c: Poison setjmp. + * doc/install.texi (enable-sjlj-exceptions): Use more general wording. + * doc/tm.texi (DWARF2_UNWIND_INFO): Likewise. + (TARGET_UNWIND_TABLES_DEFAULT): Fix typo. + (DONT_USE_BUILTIN_SETJMP): Document it. + 2006-09-28 Geoffrey Keating PR target/28617 diff --git a/gcc/builtins.c b/gcc/builtins.c index 4684a5482ad..8fb58ca052d 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -81,7 +81,6 @@ static int apply_result_size (void); #if defined (HAVE_untyped_call) || defined (HAVE_untyped_return) static rtx result_vector (int, rtx); #endif -static rtx expand_builtin_setjmp (tree, rtx); static void expand_builtin_update_setjmp_buf (rtx); static void expand_builtin_prefetch (tree); static rtx expand_builtin_apply_args (void); @@ -608,8 +607,8 @@ expand_builtin_return_addr (enum built_in_function fndecl_code, int count) static HOST_WIDE_INT setjmp_alias_set = -1; /* Construct the leading half of a __builtin_setjmp call. Control will - return to RECEIVER_LABEL. This is used directly by sjlj exception - handling code. */ + return to RECEIVER_LABEL. This is also called directly by the SJLJ + exception handling code. */ void expand_builtin_setjmp_setup (rtx buf_addr, rtx receiver_label) @@ -660,8 +659,8 @@ expand_builtin_setjmp_setup (rtx buf_addr, rtx receiver_label) current_function_has_nonlocal_label = 1; } -/* Construct the trailing part of a __builtin_setjmp call. - This is used directly by sjlj exception handling code. */ +/* Construct the trailing part of a __builtin_setjmp call. This is + also called directly by the SJLJ exception handling code. */ void expand_builtin_setjmp_receiver (rtx receiver_label ATTRIBUTE_UNUSED) @@ -729,73 +728,10 @@ expand_builtin_setjmp_receiver (rtx receiver_label ATTRIBUTE_UNUSED) emit_insn (gen_rtx_ASM_INPUT (VOIDmode, "")); } -/* __builtin_setjmp is passed a pointer to an array of five words (not - all will be used on all machines). It operates similarly to the C - library function of the same name, but is more efficient. Much of - the code below (and for longjmp) is copied from the handling of - non-local gotos. - - NOTE: This is intended for use by GNAT and the exception handling - scheme in the compiler and will only work in the method used by - them. */ - -static rtx -expand_builtin_setjmp (tree arglist, rtx target) -{ - rtx buf_addr, next_lab, cont_lab; - - if (!validate_arglist (arglist, POINTER_TYPE, VOID_TYPE)) - return NULL_RTX; - - if (target == 0 || !REG_P (target) - || REGNO (target) < FIRST_PSEUDO_REGISTER) - target = gen_reg_rtx (TYPE_MODE (integer_type_node)); - - buf_addr = expand_normal (TREE_VALUE (arglist)); - - next_lab = gen_label_rtx (); - cont_lab = gen_label_rtx (); - - expand_builtin_setjmp_setup (buf_addr, next_lab); - - /* Set TARGET to zero and branch to the continue label. Use emit_jump to - ensure that pending stack adjustments are flushed. */ - emit_move_insn (target, const0_rtx); - emit_jump (cont_lab); - - emit_label (next_lab); - - /* Because setjmp and longjmp are not represented in the CFG, a cfgcleanup - may find that the basic block starting with NEXT_LAB is unreachable. - The whole block, along with NEXT_LAB, would be removed (see PR26983). - Make sure that never happens. */ - LABEL_PRESERVE_P (next_lab) = 1; - - expand_builtin_setjmp_receiver (next_lab); - - /* Set TARGET to one. */ - emit_move_insn (target, const1_rtx); - emit_label (cont_lab); - - /* Tell flow about the strange goings on. Putting `next_lab' on - `nonlocal_goto_handler_labels' to indicates that function - calls may traverse the arc back to this label. */ - - current_function_has_nonlocal_label = 1; - nonlocal_goto_handler_labels - = gen_rtx_EXPR_LIST (VOIDmode, next_lab, nonlocal_goto_handler_labels); - - return target; -} - /* __builtin_longjmp is passed a pointer to an array of five words (not all will be used on all machines). It operates similarly to the C library function of the same name, but is more efficient. Much of - the code below is copied from the handling of non-local gotos. - - NOTE: This is intended for use by GNAT and the exception handling - scheme in the compiler and will only work in the method used by - them. */ + the code below is copied from the handling of non-local gotos. */ static void expand_builtin_longjmp (rtx buf_addr, rtx value) @@ -6077,18 +6013,63 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, break; case BUILT_IN_SETJMP: - target = expand_builtin_setjmp (arglist, target); - if (target) - return target; + /* This should have been lowered to the builtins below. */ + gcc_unreachable (); + + case BUILT_IN_SETJMP_SETUP: + /* __builtin_setjmp_setup is passed a pointer to an array of five words + and the receiver label. */ + if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)) + { + rtx buf_addr = expand_expr (TREE_VALUE (arglist), subtarget, + VOIDmode, EXPAND_NORMAL); + tree label = TREE_OPERAND (TREE_VALUE (TREE_CHAIN (arglist)), 0); + rtx label_r = label_rtx (label); + + /* This is copied from the handling of non-local gotos. */ + expand_builtin_setjmp_setup (buf_addr, label_r); + nonlocal_goto_handler_labels + = gen_rtx_EXPR_LIST (VOIDmode, label_r, + nonlocal_goto_handler_labels); + /* ??? Do not let expand_label treat us as such since we would + not want to be both on the list of non-local labels and on + the list of forced labels. */ + FORCED_LABEL (label) = 0; + return const0_rtx; + } + break; + + case BUILT_IN_SETJMP_DISPATCHER: + /* __builtin_setjmp_dispatcher is passed the dispatcher label. */ + if (validate_arglist (arglist, POINTER_TYPE, VOID_TYPE)) + { + tree label = TREE_OPERAND (TREE_VALUE (arglist), 0); + rtx label_r = label_rtx (label); + + /* Remove the dispatcher label from the list of non-local labels + since the receiver labels have been added to it above. */ + remove_node_from_expr_list (label_r, &nonlocal_goto_handler_labels); + return const0_rtx; + } + break; + + case BUILT_IN_SETJMP_RECEIVER: + /* __builtin_setjmp_receiver is passed the receiver label. */ + if (validate_arglist (arglist, POINTER_TYPE, VOID_TYPE)) + { + tree label = TREE_OPERAND (TREE_VALUE (arglist), 0); + rtx label_r = label_rtx (label); + + expand_builtin_setjmp_receiver (label_r); + return const0_rtx; + } break; /* __builtin_longjmp is passed a pointer to an array of five words. It's similar to the C library longjmp function but works with __builtin_setjmp above. */ case BUILT_IN_LONGJMP: - if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) - break; - else + if (validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) { rtx buf_addr = expand_expr (TREE_VALUE (arglist), subtarget, VOIDmode, EXPAND_NORMAL); @@ -6103,6 +6084,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, expand_builtin_longjmp (buf_addr, value); return const0_rtx; } + break; case BUILT_IN_NONLOCAL_GOTO: target = expand_builtin_nonlocal_goto (arglist); diff --git a/gcc/builtins.def b/gcc/builtins.def index 2dcbd39d6d5..0e73cf440a4 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -690,6 +690,11 @@ DEF_BUILTIN_STUB (BUILT_IN_INIT_TRAMPOLINE, "__builtin_init_trampoline") DEF_BUILTIN_STUB (BUILT_IN_ADJUST_TRAMPOLINE, "__builtin_adjust_trampoline") DEF_BUILTIN_STUB (BUILT_IN_NONLOCAL_GOTO, "__builtin_nonlocal_goto") +/* Implementing __builtin_setjmp. */ +DEF_BUILTIN_STUB (BUILT_IN_SETJMP_SETUP, "__builtin_setjmp_setup") +DEF_BUILTIN_STUB (BUILT_IN_SETJMP_DISPATCHER, "__builtin_setjmp_dispatcher") +DEF_BUILTIN_STUB (BUILT_IN_SETJMP_RECEIVER, "__builtin_setjmp_receiver") + /* Implementing variable sized local variables. */ DEF_BUILTIN_STUB (BUILT_IN_STACK_SAVE, "__builtin_stack_save") DEF_BUILTIN_STUB (BUILT_IN_STACK_RESTORE, "__builtin_stack_restore") diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi index 66ec7ca9bf4..1925b5577cb 100644 --- a/gcc/doc/install.texi +++ b/gcc/doc/install.texi @@ -1456,9 +1456,9 @@ file to compile into a @file{.class} file. Search for libiconv in @file{DIR/include} and @file{DIR/lib}. @item --enable-sjlj-exceptions -Force use of @code{builtin_setjmp} for exceptions. @samp{configure} -ordinarily picks the correct value based on the platform. Only use -this option if you are sure you need a different setting. +Force use of the @code{setjmp}/@code{longjmp}-based scheme for exceptions. +@samp{configure} ordinarily picks the correct value based on the platform. +Only use this option if you are sure you need a different setting. @item --with-system-zlib Use installed @samp{zlib} rather than that included with GCC@. diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index 44f1aaa59cd..c724a49a40e 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -7924,8 +7924,7 @@ Define this macro to 0 if your target supports DWARF 2 frame unwind information, but it does not yet work with exception handling. Otherwise, if your target supports this information (if it defines @samp{INCOMING_RETURN_ADDR_RTX} and either @samp{UNALIGNED_INT_ASM_OP} -or @samp{OBJECT_FORMAT_ELF}), GCC will provide a default definition of -1. +or @samp{OBJECT_FORMAT_ELF}), GCC will provide a default definition of 1. If @code{TARGET_UNWIND_INFO} is defined, the target specific unwinder will be used in all cases. Defining this macro will enable the generation @@ -7933,7 +7932,8 @@ of DWARF 2 frame debugging information. If @code{TARGET_UNWIND_INFO} is not defined, and this macro is defined to 1, the DWARF 2 unwinder will be the default exception handling mechanism; -otherwise, @code{setjmp}/@code{longjmp} will be used by default. +otherwise, the @code{setjmp}/@code{longjmp}-based scheme will be used by +default. @end defmac @defmac TARGET_UNWIND_INFO @@ -7941,7 +7941,7 @@ Define this macro if your target has ABI specified unwind tables. Usually these will be output by @code{TARGET_UNWIND_EMIT}. @end defmac -@deftypevar {Target Hook} bool TARGET_UNWID_TABLES_DEFAULT +@deftypevar {Target Hook} bool TARGET_UNWIND_TABLES_DEFAULT This variable should be set to @code{true} if the target ABI requires unwinding tables even when exceptions are not used. @end deftypevar @@ -7949,8 +7949,14 @@ tables even when exceptions are not used. @defmac MUST_USE_SJLJ_EXCEPTIONS This macro need only be defined if @code{DWARF2_UNWIND_INFO} is runtime-variable. In that case, @file{except.h} cannot correctly -determine the corresponding definition of -@code{MUST_USE_SJLJ_EXCEPTIONS}, so the target must provide it directly. +determine the corresponding definition of @code{MUST_USE_SJLJ_EXCEPTIONS}, +so the target must provide it directly. +@end defmac + +@defmac DONT_USE_BUILTIN_SETJMP +Define this macro to 1 if the @code{setjmp}/@code{longjmp}-based scheme +should use the @code{setjmp}/@code{longjmp} functions from the C library +instead of the @code{__builtin_setjmp}/@code{__builtin_longjmp} machinery. @end defmac @defmac DWARF_CIE_DATA_ALIGNMENT diff --git a/gcc/gimple-low.c b/gcc/gimple-low.c index ff6b8b27f30..c6a0312a2be 100644 --- a/gcc/gimple-low.c +++ b/gcc/gimple-low.c @@ -49,14 +49,18 @@ struct lower_data /* A TREE_LIST of label and return statements to be moved to the end of the function. */ tree return_statements; + + /* True if the function calls __builtin_setjmp. */ + bool calls_builtin_setjmp; }; static void lower_stmt (tree_stmt_iterator *, struct lower_data *); static void lower_bind_expr (tree_stmt_iterator *, struct lower_data *); static void lower_cond_expr (tree_stmt_iterator *, struct lower_data *); static void lower_return_expr (tree_stmt_iterator *, struct lower_data *); +static void lower_builtin_setjmp (tree_stmt_iterator *); -/* Lowers the body of current_function_decl. */ +/* Lower the body of current_function_decl. */ static unsigned int lower_function_body (void) @@ -113,6 +117,35 @@ lower_function_body (void) tsi_link_after (&i, x, TSI_CONTINUE_LINKING); } + /* If the function calls __builtin_setjmp, we need to emit the computed + goto that will serve as the unique dispatcher for all the receivers. */ + if (data.calls_builtin_setjmp) + { + tree disp_label, disp_var, arg; + + /* Build 'DISP_LABEL:' and insert. */ + disp_label = create_artificial_label (); + /* This mark will create forward edges from every call site. */ + DECL_NONLOCAL (disp_label) = 1; + current_function_has_nonlocal_label = 1; + x = build1 (LABEL_EXPR, void_type_node, disp_label); + tsi_link_after (&i, x, TSI_CONTINUE_LINKING); + + /* Build 'DISP_VAR = __builtin_setjmp_dispatcher (DISP_LABEL);' + and insert. */ + disp_var = create_tmp_var (ptr_type_node, "setjmpvar"); + t = build_addr (disp_label, current_function_decl); + arg = tree_cons (NULL, t, NULL); + t = implicit_built_in_decls[BUILT_IN_SETJMP_DISPATCHER]; + t = build_function_call_expr (t,arg); + x = build2 (MODIFY_EXPR, void_type_node, disp_var, t); + + /* Build 'goto DISP_VAR;' and insert. */ + tsi_link_after (&i, x, TSI_CONTINUE_LINKING); + x = build1 (GOTO_EXPR, void_type_node, disp_var); + tsi_link_after (&i, x, TSI_CONTINUE_LINKING); + } + gcc_assert (data.block == DECL_INITIAL (current_function_decl)); BLOCK_SUBBLOCKS (data.block) = blocks_nreverse (BLOCK_SUBBLOCKS (data.block)); @@ -139,7 +172,7 @@ struct tree_opt_pass pass_lower_cf = }; -/* Lowers the EXPR. Unlike gimplification the statements are not relowered +/* Lower the EXPR. Unlike gimplification the statements are not relowered when they are changed -- if this has to be done, the lowering routine must do it explicitly. DATA is passed through the recursion. */ @@ -171,7 +204,7 @@ lower_omp_directive (tree_stmt_iterator *tsi, struct lower_data *data) } -/* Lowers statement TSI. DATA is passed through the recursion. */ +/* Lower statement TSI. DATA is passed through the recursion. */ static void lower_stmt (tree_stmt_iterator *tsi, struct lower_data *data) @@ -207,8 +240,6 @@ lower_stmt (tree_stmt_iterator *tsi, struct lower_data *data) case NOP_EXPR: case ASM_EXPR: - case MODIFY_EXPR: - case CALL_EXPR: case GOTO_EXPR: case LABEL_EXPR: case SWITCH_EXPR: @@ -223,6 +254,27 @@ lower_stmt (tree_stmt_iterator *tsi, struct lower_data *data) case OMP_CONTINUE: break; + case MODIFY_EXPR: + if (TREE_CODE (TREE_OPERAND (stmt, 1)) == CALL_EXPR) + stmt = TREE_OPERAND (stmt, 1); + else + break; + /* FALLTHRU */ + + case CALL_EXPR: + { + tree decl = get_callee_fndecl (stmt); + if (decl + && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL + && DECL_FUNCTION_CODE (decl) == BUILT_IN_SETJMP) + { + data->calls_builtin_setjmp = true; + lower_builtin_setjmp (tsi); + return; + } + } + break; + case OMP_PARALLEL: lower_omp_directive (tsi, data); return; @@ -234,7 +286,7 @@ lower_stmt (tree_stmt_iterator *tsi, struct lower_data *data) tsi_next (tsi); } -/* Lowers a bind_expr TSI. DATA is passed through the recursion. */ +/* Lower a bind_expr TSI. DATA is passed through the recursion. */ static void lower_bind_expr (tree_stmt_iterator *tsi, struct lower_data *data) @@ -403,7 +455,7 @@ block_may_fallthru (tree block) } } -/* Lowers a cond_expr TSI. DATA is passed through the recursion. */ +/* Lower a cond_expr TSI. DATA is passed through the recursion. */ static void lower_cond_expr (tree_stmt_iterator *tsi, struct lower_data *data) @@ -498,6 +550,8 @@ lower_cond_expr (tree_stmt_iterator *tsi, struct lower_data *data) tsi_next (tsi); } +/* Lower a return_expr TSI. DATA is passed through the recursion. */ + static void lower_return_expr (tree_stmt_iterator *tsi, struct lower_data *data) { @@ -534,6 +588,129 @@ lower_return_expr (tree_stmt_iterator *tsi, struct lower_data *data) tsi_link_before (tsi, t, TSI_SAME_STMT); tsi_delink (tsi); } + +/* Lower a __builtin_setjmp TSI. + + __builtin_setjmp is passed a pointer to an array of five words (not + all will be used on all machines). It operates similarly to the C + library function of the same name, but is more efficient. + + It is lowered into 3 other builtins, namely __builtin_setjmp_setup, + __builtin_setjmp_dispatcher and __builtin_setjmp_receiver, but with + __builtin_setjmp_dispatcher shared among all the instances; that's + why it is only emitted at the end by lower_function_body. + + After full lowering, the body of the function should look like: + + { + void * setjmpvar.0; + int D.1844; + int D.2844; + + [...] + + __builtin_setjmp_setup (&buf, &); + D.1844 = 0; + goto ; + :; + __builtin_setjmp_receiver (&); + D.1844 = 1; + :; + if (D.1844 == 0) goto ; else goto ; + + [...] + + __builtin_setjmp_setup (&buf, &); + D.2844 = 0; + goto ; + :; + __builtin_setjmp_receiver (&); + D.2844 = 1; + :; + if (D.2844 == 0) goto ; else goto ; + + [...] + + :; + return; + : [non-local]; + setjmpvar.0 = __builtin_setjmp_dispatcher (&); + goto setjmpvar.0; + } + + The dispatcher block will be both the unique destination of all the + abnormal call edges and the unique source of all the abnormal edges + to the receivers, thus keeping the complexity explosion localized. */ + +static void +lower_builtin_setjmp (tree_stmt_iterator *tsi) +{ + tree stmt = tsi_stmt (*tsi); + tree cont_label = create_artificial_label (); + tree next_label = create_artificial_label (); + tree dest, t, arg; + + /* 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; + + if (TREE_CODE (stmt) == MODIFY_EXPR) + { + dest = TREE_OPERAND (stmt, 0); + stmt = TREE_OPERAND (stmt, 1); + } + else + dest = NULL_TREE; + + /* Build '__builtin_setjmp_setup (BUF, NEXT_LABEL)' and insert. */ + t = build_addr (next_label, current_function_decl); + arg = tree_cons (NULL, t, NULL); + t = TREE_VALUE (TREE_OPERAND (stmt, 1)); + arg = tree_cons (NULL, t, arg); + t = implicit_built_in_decls[BUILT_IN_SETJMP_SETUP]; + t = build_function_call_expr (t, arg); + SET_EXPR_LOCUS (t, EXPR_LOCUS (stmt)); + tsi_link_before (tsi, t, TSI_SAME_STMT); + + /* Build 'DEST = 0' and insert. */ + if (dest) + { + t = build2 (MODIFY_EXPR, void_type_node, dest, integer_zero_node); + SET_EXPR_LOCUS (t, EXPR_LOCUS (stmt)); + tsi_link_before (tsi, t, TSI_SAME_STMT); + } + + /* Build 'goto CONT_LABEL' and insert. */ + t = build1 (GOTO_EXPR, void_type_node, cont_label); + tsi_link_before (tsi, t, TSI_SAME_STMT); + + /* Build 'NEXT_LABEL:' and insert. */ + t = build1 (LABEL_EXPR, void_type_node, next_label); + tsi_link_before (tsi, t, TSI_SAME_STMT); + + /* Build '__builtin_setjmp_receiver (NEXT_LABEL)' and insert. */ + t = build_addr (next_label, current_function_decl); + arg = tree_cons (NULL, t, NULL); + t = implicit_built_in_decls[BUILT_IN_SETJMP_RECEIVER]; + t = build_function_call_expr (t, arg); + SET_EXPR_LOCUS (t, EXPR_LOCUS (stmt)); + tsi_link_before (tsi, t, TSI_SAME_STMT); + + /* Build 'DEST = 1' and insert. */ + if (dest) + { + t = build2 (MODIFY_EXPR, void_type_node, dest, integer_one_node); + SET_EXPR_LOCUS (t, EXPR_LOCUS (stmt)); + tsi_link_before (tsi, t, TSI_SAME_STMT); + } + + /* Build 'CONT_LABEL:' and insert. */ + t = build1 (LABEL_EXPR, void_type_node, cont_label); + tsi_link_before (tsi, t, TSI_SAME_STMT); + + /* Remove the call to __builtin_setjmp. */ + tsi_delink (tsi); +} /* Record the variables in VARS into function FN. */ diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 112f6fe64a1..d59cd1b011e 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2006-09-28 Eric Botcazou + + * gcc.dg/non-local-goto-1.c: New test. + * gcc.dg/non-local-goto-2.c: Likewise. + * gcc.dg/setjmp-3.c: Likewise. + * gcc.dg/setjmp-4.c: Likewise. + 2006-09-28 Eric Botcazou * gnat.dg/self_aggregate_with_pointer.adb: New test. diff --git a/gcc/testsuite/gcc.dg/non-local-goto-1.c b/gcc/testsuite/gcc.dg/non-local-goto-1.c new file mode 100644 index 00000000000..2bace076fad --- /dev/null +++ b/gcc/testsuite/gcc.dg/non-local-goto-1.c @@ -0,0 +1,56 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ + +extern void abort (void); + +int global; + +static foo(void) __attribute__((noinline)); + +static foo(void) +{ + global = 1; +} + +static bar(void) +{ + foo (); +} + +int execute(int cmd) +{ + __label__ start; + + void raise(void) + { + goto start; + } + + int last; + + bar (); + + last = 0; + +start: + + if (last == 0) + while (1) + { + last = 1; + raise (); + } + + if (last == 0) + return 0; + else + return cmd; +} + +int main(void) +{ + if (execute (1) == 0) + abort (); + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/non-local-goto-2.c b/gcc/testsuite/gcc.dg/non-local-goto-2.c new file mode 100644 index 00000000000..24ed650a9e9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/non-local-goto-2.c @@ -0,0 +1,57 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ + +extern void abort (void); + +int global; + +static foo(void) __attribute__((noinline)); + +static foo(void) +{ + global = 1; +} + +static bar(void) +{ + foo (); + global = 0; +} + +int execute(int cmd) +{ + __label__ start; + + void raise(void) + { + goto start; + } + + int last; + + bar (); + + last = 0; + +start: + + if (last == 0) + while (1) + { + last = 1; + raise (); + } + + if (last == 0) + return 0; + else + return cmd; +} + +int main(void) +{ + if (execute (1) == 0) + abort (); + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/setjmp-3.c b/gcc/testsuite/gcc.dg/setjmp-3.c new file mode 100644 index 00000000000..94dc5906206 --- /dev/null +++ b/gcc/testsuite/gcc.dg/setjmp-3.c @@ -0,0 +1,38 @@ +/* { dg-do run } */ +/* { dg-options "-O" } */ + +#include + +extern void abort (void); + +jmp_buf buf; + +void raise(void) +{ + __builtin_longjmp (buf, 1); +} + +int execute(int cmd) +{ + int last = 0; + + if (__builtin_setjmp (buf) == 0) + while (1) + { + last = 1; + raise (); + } + + if (last == 0) + return 0; + else + return cmd; +} + +int main(void) +{ + if (execute (1) == 0) + abort (); + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/setjmp-4.c b/gcc/testsuite/gcc.dg/setjmp-4.c new file mode 100644 index 00000000000..b4a917b8907 --- /dev/null +++ b/gcc/testsuite/gcc.dg/setjmp-4.c @@ -0,0 +1,40 @@ +/* { dg-do run } */ +/* { dg-options "-O" } */ + +#include + +extern void abort (void); + +jmp_buf buf; + +void raise(void) +{ + __builtin_longjmp (buf, 1); +} + +int execute(int cmd) +{ + int last = 0; + + __builtin_setjmp (buf); + + if (last == 0) + while (1) + { + last = 1; + raise (); + } + + if (last == 0) + return 0; + else + return cmd; +} + +int main(void) +{ + if (execute (1) == 0) + abort (); + + return 0; +} diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c index 67d049130e8..46ee1b1cf17 100644 --- a/gcc/tree-cfg.c +++ b/gcc/tree-cfg.c @@ -489,9 +489,8 @@ make_edges (void) /* If this function receives a nonlocal goto, then we need to make edges from this call site to all the nonlocal goto handlers. */ - if (TREE_SIDE_EFFECTS (last) - && current_function_has_nonlocal_label) - make_goto_expr_edges (bb); + if (tree_can_make_abnormal_goto (last)) + make_abnormal_goto_edges (bb, true); /* If this statement has reachable exception handlers, then create abnormal edges to them. */ @@ -507,10 +506,8 @@ make_edges (void) /* A MODIFY_EXPR may have a CALL_EXPR on its RHS and the CALL_EXPR may have an abnormal edge. Search the RHS for this case and create any required edges. */ - tree op = get_call_expr_in (last); - if (op && TREE_SIDE_EFFECTS (op) - && current_function_has_nonlocal_label) - make_goto_expr_edges (bb); + if (tree_can_make_abnormal_goto (last)) + make_abnormal_goto_edges (bb, true); make_eh_edges (last); } @@ -836,76 +833,60 @@ label_to_block_fn (struct function *ifun, tree dest) return VEC_index (basic_block, ifun->cfg->x_label_to_block_map, uid); } +/* Create edges for an abnormal goto statement at block BB. If FOR_CALL + is true, the source statement is a CALL_EXPR instead of a GOTO_EXPR. */ + +void +make_abnormal_goto_edges (basic_block bb, bool for_call) +{ + basic_block target_bb; + block_stmt_iterator bsi; + + FOR_EACH_BB (target_bb) + for (bsi = bsi_start (target_bb); !bsi_end_p (bsi); bsi_next (&bsi)) + { + tree target = bsi_stmt (bsi); + + if (TREE_CODE (target) != LABEL_EXPR) + break; + + target = LABEL_EXPR_LABEL (target); + + /* 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. */ static void make_goto_expr_edges (basic_block bb) { - tree goto_t; - basic_block target_bb; - bool for_call; block_stmt_iterator last = bsi_last (bb); + tree goto_t = bsi_stmt (last); - goto_t = bsi_stmt (last); - - /* If the last statement is not a GOTO (i.e., it is a RETURN_EXPR, - CALL_EXPR or MODIFY_EXPR), then the edge is an abnormal edge resulting - from a nonlocal goto. */ - if (TREE_CODE (goto_t) != GOTO_EXPR) - for_call = true; - else + /* A simple GOTO creates normal edges. */ + if (simple_goto_p (goto_t)) { tree dest = GOTO_DESTINATION (goto_t); - for_call = false; - - /* A GOTO to a local label creates normal edges. */ - if (simple_goto_p (goto_t)) - { - edge e = make_edge (bb, label_to_block (dest), EDGE_FALLTHRU); + edge e = make_edge (bb, label_to_block (dest), EDGE_FALLTHRU); #ifdef USE_MAPPED_LOCATION - e->goto_locus = EXPR_LOCATION (goto_t); + e->goto_locus = EXPR_LOCATION (goto_t); #else - e->goto_locus = EXPR_LOCUS (goto_t); + e->goto_locus = EXPR_LOCUS (goto_t); #endif - bsi_remove (&last, true); - return; - } - - /* Nothing more to do for nonlocal gotos. */ - if (TREE_CODE (dest) == LABEL_DECL) - return; - - /* Computed gotos remain. */ + bsi_remove (&last, true); + return; } - /* Look for the block starting with the destination label. In the - case of a computed goto, make an edge to any label block we find - in the CFG. */ - FOR_EACH_BB (target_bb) - { - block_stmt_iterator bsi; - - for (bsi = bsi_start (target_bb); !bsi_end_p (bsi); bsi_next (&bsi)) - { - tree target = bsi_stmt (bsi); - - if (TREE_CODE (target) != LABEL_EXPR) - break; - - if ( - /* Computed GOTOs. Make an edge to every label block that has - been marked as a potential target for a computed goto. */ - (FORCED_LABEL (LABEL_EXPR_LABEL (target)) && !for_call) - /* Nonlocal GOTO target. Make an edge to every label block - that has been marked as a potential target for a nonlocal - goto. */ - || (DECL_NONLOCAL (LABEL_EXPR_LABEL (target)) && for_call)) - { - make_edge (bb, target_bb, EDGE_ABNORMAL); - break; - } - } - } + /* A computed GOTO creates abnormal edges. */ + make_abnormal_goto_edges (bb, false); } @@ -2517,13 +2498,31 @@ computed_goto_p (tree t) } -/* Checks whether EXPR is a simple local goto. */ +/* Return true if T is a simple local goto. */ bool -simple_goto_p (tree expr) +simple_goto_p (tree t) { - return (TREE_CODE (expr) == GOTO_EXPR - && TREE_CODE (GOTO_DESTINATION (expr)) == LABEL_DECL); + return (TREE_CODE (t) == GOTO_EXPR + && TREE_CODE (GOTO_DESTINATION (t)) == LABEL_DECL); +} + + +/* Return true if T can make an abnormal transfer of control flow. + Transfers of control flow associated with EH are excluded. */ + +bool +tree_can_make_abnormal_goto (tree t) +{ + if (computed_goto_p (t)) + return true; + if (TREE_CODE (t) == MODIFY_EXPR) + t = TREE_OPERAND (t, 1); + if (TREE_CODE (t) == WITH_SIZE_EXPR) + t = TREE_OPERAND (t, 0); + if (TREE_CODE (t) == CALL_EXPR) + return TREE_SIDE_EFFECTS (t) && current_function_has_nonlocal_label; + return false; } @@ -4072,7 +4071,7 @@ tree_redirect_edge_and_branch (edge e, basic_block dest) edge ret; tree label, stmt; - if (e->flags & (EDGE_ABNORMAL_CALL | EDGE_EH)) + if (e->flags & EDGE_ABNORMAL) return NULL; if (e->src != ENTRY_BLOCK_PTR @@ -5374,6 +5373,41 @@ tree_flow_call_edges_add (sbitmap blocks) return blocks_split; } +/* Purge dead abnormal call edges from basic block BB. */ + +bool +tree_purge_dead_abnormal_call_edges (basic_block bb) +{ + bool changed = tree_purge_dead_eh_edges (bb); + + if (current_function_has_nonlocal_label) + { + tree stmt = last_stmt (bb); + edge_iterator ei; + edge e; + + if (!(stmt && tree_can_make_abnormal_goto (stmt))) + for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei)); ) + { + if (e->flags & EDGE_ABNORMAL) + { + remove_edge (e); + changed = true; + } + else + ei_next (&ei); + } + + /* See tree_purge_dead_eh_edges below. */ + if (changed) + free_dominance_info (CDI_DOMINATORS); + } + + return changed; +} + +/* Purge dead EH edges from basic block BB. */ + bool tree_purge_dead_eh_edges (basic_block bb) { diff --git a/gcc/tree-flow.h b/gcc/tree-flow.h index e8fa099f65d..f70eb774f35 100644 --- a/gcc/tree-flow.h +++ b/gcc/tree-flow.h @@ -564,6 +564,7 @@ extern bool is_ctrl_stmt (tree); extern bool is_ctrl_altering_stmt (tree); extern bool computed_goto_p (tree); extern bool simple_goto_p (tree); +extern bool tree_can_make_abnormal_goto (tree); extern basic_block single_noncomplex_succ (basic_block bb); extern void tree_dump_bb (basic_block, FILE *, int); extern void debug_tree_bb (basic_block); @@ -596,6 +597,7 @@ extern bool tree_duplicate_sese_region (edge, edge, basic_block *, unsigned, basic_block *); extern void add_phi_args_after_copy_bb (basic_block); extern void add_phi_args_after_copy (basic_block *, unsigned); +extern bool tree_purge_dead_abnormal_call_edges (basic_block); extern bool tree_purge_dead_eh_edges (basic_block); extern bool tree_purge_all_dead_eh_edges (bitmap); extern tree gimplify_val (block_stmt_iterator *, tree, tree); @@ -607,6 +609,7 @@ extern tree gimplify_build3 (block_stmt_iterator *, enum tree_code, tree, tree, tree, tree); extern void init_empty_tree_cfg (void); extern void fold_cond_expr_cond (void); +extern void make_abnormal_goto_edges (basic_block, bool); extern void replace_uses_by (tree, tree); extern void start_recording_case_labels (void); extern void end_recording_case_labels (void); diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c index baca8340486..61b1dab954d 100644 --- a/gcc/tree-inline.c +++ b/gcc/tree-inline.c @@ -1930,6 +1930,7 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data) edge e; block_stmt_iterator bsi, stmt_bsi; bool successfully_inlined = FALSE; + bool purge_dead_abnormal_edges; tree t_step; tree var; @@ -2024,30 +2025,36 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data) #endif /* We will be inlining this callee. */ - id->eh_region = lookup_stmt_eh_region (stmt); /* Split the block holding the CALL_EXPR. */ - e = split_block (bb, stmt); bb = e->src; return_block = e->dest; remove_edge (e); - /* split_block splits before the statement, work around this by moving - the call into the first half_bb. Not pretty, but seems easier than - doing the CFG manipulation by hand when the CALL_EXPR is in the last - statement in BB. */ + /* split_block splits after the statement; work around this by + moving the call into the second block manually. Not pretty, + but seems easier than doing the CFG manipulation by hand + when the CALL_EXPR is in the last statement of BB. */ stmt_bsi = bsi_last (bb); + bsi_remove (&stmt_bsi, false); + + /* If the CALL_EXPR was in the last statement of BB, it may have + been the source of abnormal edges. In this case, schedule + the removal of dead abnormal edges. */ bsi = bsi_start (return_block); - if (!bsi_end_p (bsi)) - bsi_move_before (&stmt_bsi, &bsi); + if (bsi_end_p (bsi)) + { + bsi_insert_after (&bsi, stmt, BSI_NEW_STMT); + purge_dead_abnormal_edges = true; + } else { - tree stmt = bsi_stmt (stmt_bsi); - bsi_remove (&stmt_bsi, false); - bsi_insert_after (&bsi, stmt, BSI_NEW_STMT); + bsi_insert_before (&bsi, stmt, BSI_NEW_STMT); + purge_dead_abnormal_edges = false; } + stmt_bsi = bsi_start (return_block); /* Build a block containing code to initialize the arguments, the @@ -2147,9 +2154,8 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data) tsi_delink() will leave the iterator in a sane state. */ bsi_remove (&stmt_bsi, true); - bsi_next (&bsi); - if (bsi_end_p (bsi)) - tree_purge_dead_eh_edges (return_block); + if (purge_dead_abnormal_edges) + tree_purge_dead_abnormal_call_edges (return_block); /* If the value of the new expression is ignored, that's OK. We don't warn about this for CALL_EXPRs, so we shouldn't warn about diff --git a/gcc/tree-optimize.c b/gcc/tree-optimize.c index 3f4471a468c..50dd22b451f 100644 --- a/gcc/tree-optimize.c +++ b/gcc/tree-optimize.c @@ -237,9 +237,26 @@ struct tree_opt_pass pass_free_cfg_annotations = 0, /* todo_flags_finish */ 0 /* letter */ }; -/* Pass: fixup_cfg - IPA passes or compilation of earlier functions might've - changed some properties - such as marked functions nothrow. Remove now - redundant edges and basic blocks. */ + +/* Return true if BB has at least one abnormal outgoing edge. */ + +static inline bool +has_abnormal_outgoing_edge_p (basic_block bb) +{ + edge e; + edge_iterator ei; + + FOR_EACH_EDGE (e, ei, bb->succs) + if (e->flags & EDGE_ABNORMAL) + return true; + + return false; +} + +/* Pass: fixup_cfg. IPA passes, compilation of earlier functions or inlining + might have changed some properties, such as marked functions nothrow or + added calls that can potentially go to non-local labels. Remove redundant + edges and basic blocks, and create new ones if necessary. */ static unsigned int execute_fixup_cfg (void) @@ -262,8 +279,37 @@ execute_fixup_cfg (void) } tree_purge_dead_eh_edges (bb); } - + + if (current_function_has_nonlocal_label) + FOR_EACH_BB (bb) + { + for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi)) + { + tree stmt = bsi_stmt (bsi); + if (tree_can_make_abnormal_goto (stmt)) + { + if (stmt == bsi_stmt (bsi_last (bb))) + { + if (!has_abnormal_outgoing_edge_p (bb)) + make_abnormal_goto_edges (bb, true); + } + else + { + edge e = split_block (bb, stmt); + bb = e->src; + make_abnormal_goto_edges (bb, true); + } + break; + } + } + } + cleanup_tree_cfg (); + + /* Dump a textual representation of the flowgraph. */ + if (dump_file) + dump_tree_cfg (dump_file, dump_flags); + return 0; } @@ -280,7 +326,7 @@ struct tree_opt_pass pass_fixup_cfg = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ - TODO_dump_func, /* todo_flags_finish */ + 0, /* todo_flags_finish */ 0 /* letter */ }; diff --git a/gcc/tree.c b/gcc/tree.c index cfbfe1462b6..e838ba1d632 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -6714,6 +6714,26 @@ build_common_builtin_nodes (void) "__builtin_nonlocal_goto", ECF_NORETURN | ECF_NOTHROW); + tmp = tree_cons (NULL_TREE, ptr_type_node, void_list_node); + tmp = tree_cons (NULL_TREE, ptr_type_node, tmp); + ftype = build_function_type (void_type_node, tmp); + local_define_builtin ("__builtin_setjmp_setup", ftype, + BUILT_IN_SETJMP_SETUP, + "__builtin_setjmp_setup", ECF_NOTHROW); + + tmp = tree_cons (NULL_TREE, ptr_type_node, void_list_node); + ftype = build_function_type (ptr_type_node, tmp); + local_define_builtin ("__builtin_setjmp_dispatcher", ftype, + BUILT_IN_SETJMP_DISPATCHER, + "__builtin_setjmp_dispatcher", + ECF_PURE | ECF_NOTHROW); + + tmp = tree_cons (NULL_TREE, ptr_type_node, void_list_node); + ftype = build_function_type (void_type_node, tmp); + local_define_builtin ("__builtin_setjmp_receiver", ftype, + BUILT_IN_SETJMP_RECEIVER, + "__builtin_setjmp_receiver", ECF_NOTHROW); + ftype = build_function_type (ptr_type_node, void_list_node); local_define_builtin ("__builtin_stack_save", ftype, BUILT_IN_STACK_SAVE, "__builtin_stack_save", ECF_NOTHROW); diff --git a/gcc/unwind-sjlj.c b/gcc/unwind-sjlj.c index 72c363c7af1..e1a62c9e025 100644 --- a/gcc/unwind-sjlj.c +++ b/gcc/unwind-sjlj.c @@ -1,5 +1,5 @@ /* SJLJ exception handling and frame unwind runtime interface routines. - Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 + Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2006 Free Software Foundation, Inc. This file is part of GCC. @@ -45,10 +45,14 @@ typedef void *jmp_buf[JMP_BUF_SIZE]; extern void longjmp(jmp_buf, int) __attribute__((noreturn)); #endif #else -#define setjmp __builtin_setjmp #define longjmp __builtin_longjmp #endif +/* The setjmp side is dealt with in the except.c file. */ +#undef setjmp +#define setjmp setjmp_should_not_be_used_in_this_file + + /* This structure is allocated on the stack of the target function. This must match the definition created in except.c:init_eh. */ struct SjLj_Function_Context