builtins.c (expand_builtin_setjmp): Delete.

* 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.

From-SVN: r117298
This commit is contained in:
Eric Botcazou 2006-09-29 06:32:58 +00:00 committed by Eric Botcazou
parent a40e443fe8
commit 4f6c213142
17 changed files with 708 additions and 180 deletions

View File

@ -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

View File

@ -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);

View File

@ -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")

View File

@ -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@.

View File

@ -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

View File

@ -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. */

View File

@ -1,3 +1,10 @@
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.

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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)
{

View File

@ -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);

View File

@ -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

View File

@ -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 */
};

View File

@ -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);

View File

@ -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