summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog47
-rw-r--r--gcc/builtins.c132
-rw-r--r--gcc/builtins.def5
-rw-r--r--gcc/doc/install.texi6
-rw-r--r--gcc/doc/tm.texi18
-rw-r--r--gcc/gimple-low.c191
-rw-r--r--gcc/testsuite/ChangeLog7
-rw-r--r--gcc/testsuite/gcc.dg/non-local-goto-1.c56
-rw-r--r--gcc/testsuite/gcc.dg/non-local-goto-2.c57
-rw-r--r--gcc/testsuite/gcc.dg/setjmp-3.c38
-rw-r--r--gcc/testsuite/gcc.dg/setjmp-4.c40
-rw-r--r--gcc/tree-cfg.c170
-rw-r--r--gcc/tree-flow.h3
-rw-r--r--gcc/tree-inline.c34
-rw-r--r--gcc/tree-optimize.c56
-rw-r--r--gcc/tree.c20
-rw-r--r--gcc/unwind-sjlj.c8
17 files changed, 708 insertions, 180 deletions
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 <ebotcazou@adacore.com>
+
+ * builtins.c (expand_builtin_setjmp): Delete.
+ (expand_builtin) <BUILT_IN_SETJMP>: Mark as unreachable.
+ <BUILT_IN_SETJMP_SETUP>: New case.
+ <BUILT_IN_SETJMP_DISPATCHER>: Likewise.
+ <BUILT_IN_SETJMP_RECEIVER>: 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) <CALL_EXPR>: Invoke lower_builtin_setjmp if the callee
+ is __builtin_setjmp and set calls_builtin_setjmp to true as well.
+ <MODIFY_EXPR>: 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) <CALL_EXPR>: Use specific predicate
+ to detect calls that can go to non-local labels. Use specific
+ helper to create the abnormal edges associated with them.
+ <MODIFY_EXPR>: 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 <geoffk@apple.com>
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, &<D1847>);
+ D.1844 = 0;
+ goto <D1846>;
+ <D1847>:;
+ __builtin_setjmp_receiver (&<D1847>);
+ D.1844 = 1;
+ <D1846>:;
+ if (D.1844 == 0) goto <D1848>; else goto <D1849>;
+
+ [...]
+
+ __builtin_setjmp_setup (&buf, &<D2847>);
+ D.2844 = 0;
+ goto <D2846>;
+ <D2847>:;
+ __builtin_setjmp_receiver (&<D2847>);
+ D.2844 = 1;
+ <D2846>:;
+ if (D.2844 == 0) goto <D2848>; else goto <D2849>;
+
+ [...]
+
+ <D3850>:;
+ return;
+ <D3853>: [non-local];
+ setjmpvar.0 = __builtin_setjmp_dispatcher (&<D3853>);
+ 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,5 +1,12 @@
2006-09-28 Eric Botcazou <ebotcazou@adacore.com>
+ * 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 <ebotcazou@adacore.com>
+
* gnat.dg/self_aggregate_with_pointer.adb: New test.
2006-09-27 Steven G. Kargl <kargls@gcc.gnu.org>
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 <setjmp.h>
+
+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 <setjmp.h>
+
+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 t)
+{
+ 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
-simple_goto_p (tree expr)
+tree_can_make_abnormal_goto (tree t)
{
- return (TREE_CODE (expr) == GOTO_EXPR
- && TREE_CODE (GOTO_DESTINATION (expr)) == LABEL_DECL);
+ 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);
- else
+ if (bsi_end_p (bsi))
{
- tree stmt = bsi_stmt (stmt_bsi);
- bsi_remove (&stmt_bsi, false);
bsi_insert_after (&bsi, stmt, BSI_NEW_STMT);
+ purge_dead_abnormal_edges = true;
}
+ else
+ {
+ 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