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:
parent
a40e443fe8
commit
4f6c213142
17 changed files with 708 additions and 180 deletions
|
@ -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
|
||||
|
|
132
gcc/builtins.c
132
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);
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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@.
|
||||
|
|
|
@ -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
|
||||
|
|
191
gcc/gimple-low.c
191
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. */
|
||||
|
|
|
@ -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.
|
||||
|
|
56
gcc/testsuite/gcc.dg/non-local-goto-1.c
Normal file
56
gcc/testsuite/gcc.dg/non-local-goto-1.c
Normal 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;
|
||||
}
|
57
gcc/testsuite/gcc.dg/non-local-goto-2.c
Normal file
57
gcc/testsuite/gcc.dg/non-local-goto-2.c
Normal 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;
|
||||
}
|
38
gcc/testsuite/gcc.dg/setjmp-3.c
Normal file
38
gcc/testsuite/gcc.dg/setjmp-3.c
Normal 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;
|
||||
}
|
40
gcc/testsuite/gcc.dg/setjmp-4.c
Normal file
40
gcc/testsuite/gcc.dg/setjmp-4.c
Normal 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;
|
||||
}
|
170
gcc/tree-cfg.c
170
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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
};
|
||||
|
||||
|
|
20
gcc/tree.c
20
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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue