struct reachable_info;
static enum reachable_code reachable_next_level (struct eh_region *, tree,
- struct reachable_info *);
+ struct reachable_info *, bool);
static int action_record_eq (const void *, const void *);
static hashval_t action_record_hash (const void *);
rc = RNL_NOT_CAUGHT;
for (; region; region = region->outer)
{
- rc = reachable_next_level (region, type_thrown, NULL);
+ rc = reachable_next_level (region, type_thrown, NULL, false);
if (rc != RNL_NOT_CAUGHT)
break;
}
if (! info)
return;
- info->saw_any_handlers = true;
-
if (crtl->eh.built_landing_pads)
info->callback (lp_region, info->callback_data);
else
static enum reachable_code
reachable_next_level (struct eh_region *region, tree type_thrown,
- struct reachable_info *info)
+ struct reachable_info *info,
+ bool maybe_resx)
{
switch (region->type)
{
case ERT_MUST_NOT_THROW:
/* Here we end our search, since no exceptions may propagate.
- If we've touched down at some landing pad previous, then the
- explicit function call we generated may be used. Otherwise
- the call is made by the runtime.
+
+ Local landing pads of ERT_MUST_NOT_THROW instructions are reachable
+ only via locally handled RESX instructions.
- Before inlining, do not perform this optimization. We may
- inline a subroutine that contains handlers, and that will
- change the value of saw_any_handlers. */
+ When we inline a function call, we can bring in new handlers. In order
+ to avoid ERT_MUST_NOT_THROW landing pads from being deleted as unreachable
+ assume that such handlers exists prior for any inlinable call prior
+ inlining decisions are fixed. */
- if ((info && info->saw_any_handlers) || !cfun->after_inlining)
+ if (maybe_resx)
{
add_reachable_handler (info, region, region);
return RNL_CAUGHT;
/* Invoke CALLBACK on each region reachable from REGION_NUMBER. */
void
-foreach_reachable_handler (int region_number, bool is_resx,
+foreach_reachable_handler (int region_number, bool is_resx, bool inlinable_call,
void (*callback) (struct eh_region *, void *),
void *callback_data)
{
while (region)
{
- if (reachable_next_level (region, type_thrown, &info) >= RNL_CAUGHT)
+ if (reachable_next_level (region, type_thrown, &info,
+ inlinable_call || is_resx) >= RNL_CAUGHT)
break;
/* If we have processed one cleanup, there is no point in
processing any more of them. Each cleanup will have an edge
region_number = INTVAL (XEXP (note, 0));
}
- foreach_reachable_handler (region_number, is_resx,
+ foreach_reachable_handler (region_number, is_resx, false,
(crtl->eh.built_landing_pads
? arh_to_landing_pad
: arh_to_label),
within the function. */
bool
-can_throw_internal_1 (int region_number, bool is_resx)
+can_throw_internal_1 (int region_number, bool is_resx, bool inlinable_call)
{
struct eh_region *region;
tree type_thrown;
regions, which also do not require processing internally. */
for (; region; region = region->outer)
{
- enum reachable_code how = reachable_next_level (region, type_thrown, 0);
+ enum reachable_code how = reachable_next_level (region, type_thrown, 0,
+ inlinable_call || is_resx);
if (how == RNL_BLOCKED)
return false;
if (how != RNL_NOT_CAUGHT)
if (JUMP_P (insn)
&& GET_CODE (PATTERN (insn)) == RESX
&& XINT (PATTERN (insn), 0) > 0)
- return can_throw_internal_1 (XINT (PATTERN (insn), 0), true);
+ return can_throw_internal_1 (XINT (PATTERN (insn), 0), true, false);
if (NONJUMP_INSN_P (insn)
&& GET_CODE (PATTERN (insn)) == SEQUENCE)
if (!note || INTVAL (XEXP (note, 0)) <= 0)
return false;
- return can_throw_internal_1 (INTVAL (XEXP (note, 0)), false);
+ return can_throw_internal_1 (INTVAL (XEXP (note, 0)), false, false);
}
/* Determine if the given INSN can throw an exception that is
visible outside the function. */
bool
-can_throw_external_1 (int region_number, bool is_resx)
+can_throw_external_1 (int region_number, bool is_resx, bool inlinable_call)
{
struct eh_region *region;
tree type_thrown;
/* If the exception is caught or blocked by any containing region,
then it is not seen by any calling function. */
for (; region ; region = region->outer)
- if (reachable_next_level (region, type_thrown, NULL) >= RNL_CAUGHT)
+ if (reachable_next_level (region, type_thrown, NULL,
+ inlinable_call || is_resx) >= RNL_CAUGHT)
return false;
return true;
if (JUMP_P (insn)
&& GET_CODE (PATTERN (insn)) == RESX
&& XINT (PATTERN (insn), 0) > 0)
- return can_throw_external_1 (XINT (PATTERN (insn), 0), true);
+ return can_throw_external_1 (XINT (PATTERN (insn), 0), true, false);
if (NONJUMP_INSN_P (insn)
&& GET_CODE (PATTERN (insn)) == SEQUENCE)
if (INTVAL (XEXP (note, 0)) <= 0)
return false;
- return can_throw_external_1 (INTVAL (XEXP (note, 0)), false);
+ return can_throw_external_1 (INTVAL (XEXP (note, 0)), false, false);
}
/* Set TREE_NOTHROW and crtl->all_throwers_are_sibcalls. */
make_edge (src, dst, EDGE_ABNORMAL | EDGE_EH);
}
+/* See if STMT is call that might be inlined. */
+
+static bool
+inlinable_call_p (gimple stmt)
+{
+ tree decl;
+ if (gimple_code (stmt) != GIMPLE_CALL)
+ return false;
+ if (cfun->after_inlining)
+ return false;
+ /* Indirect calls can be propagated to direct call
+ and inlined. */
+ decl = gimple_call_fndecl (stmt);
+ if (!decl)
+ return true;
+ if (cgraph_function_flags_ready
+ && cgraph_function_body_availability (cgraph_node (decl))
+ < AVAIL_OVERWRITABLE)
+ return false;
+ return !DECL_UNINLINABLE (decl);
+}
+
void
make_eh_edges (gimple stmt)
{
int region_nr;
bool is_resx;
+ bool inlinable = false;
if (gimple_code (stmt) == GIMPLE_RESX)
{
if (region_nr < 0)
return;
is_resx = false;
+ inlinable = inlinable_call_p (stmt);
}
- foreach_reachable_handler (region_nr, is_resx, make_eh_edge, stmt);
+ foreach_reachable_handler (region_nr, is_resx, inlinable, make_eh_edge, stmt);
}
static bool mark_eh_edge_found_error;
basic_block bb = gimple_bb (stmt);
edge_iterator ei;
edge e;
+ bool inlinable = false;
FOR_EACH_EDGE (e, ei, bb->succs)
gcc_assert (!e->aux);
error ("BB %i last statement has incorrectly set region", bb->index);
return true;
}
+ inlinable = inlinable_call_p (stmt);
is_resx = false;
}
- foreach_reachable_handler (region_nr, is_resx, mark_eh_edge, stmt);
+ foreach_reachable_handler (region_nr, is_resx, inlinable, mark_eh_edge, stmt);
FOR_EACH_EDGE (e, ei, bb->succs)
{
if ((e->flags & EDGE_EH) && !e->aux)
{
int region_nr;
bool is_resx = false;
+ bool inlinable_call = false;
if (gimple_code (stmt) == GIMPLE_RESX)
{
is_resx = true;
}
else
- region_nr = lookup_stmt_eh_region (stmt);
+ {
+ region_nr = lookup_stmt_eh_region (stmt);
+ inlinable_call = inlinable_call_p (stmt);
+ }
if (region_nr < 0)
return false;
- return can_throw_internal_1 (region_nr, is_resx);
+ return can_throw_internal_1 (region_nr, is_resx, inlinable_call);
}