return max_inline_insns_auto;
}
+enum can_inline_edge_by_limits_flags
+{
+ /* True if we are early inlining. */
+ CAN_INLINE_EARLY = 1,
+ /* Ignore size limits. */
+ CAN_INLINE_DISREGARD_LIMITS = 2,
+ /* Force size limits (ignore always_inline). This is used for
+ recrusive inlining where always_inline may lead to inline bombs
+ and technically it is non-sential anyway. */
+ CAN_INLINE_FORCE_LIMITS = 4,
+ /* Report decision to dump file. */
+ CAN_INLINE_REPORT = 8,
+};
+
/* Decide if we can inline the edge and possibly update
inline_failed reason.
We check whether inlining is possible at all and whether
- caller growth limits allow doing so.
-
- if REPORT is true, output reason to the dump file.
-
- if DISREGARD_LIMITS is true, ignore size limits. */
+ caller growth limits allow doing so. */
static bool
-can_inline_edge_by_limits_p (struct cgraph_edge *e, bool report,
- bool disregard_limits = false, bool early = false)
+can_inline_edge_by_limits_p (struct cgraph_edge *e, int flags)
{
gcc_checking_assert (e->inline_failed);
if (cgraph_inline_failed_type (e->inline_failed) == CIF_FINAL_ERROR)
{
- if (report)
+ if (flags & CAN_INLINE_REPORT)
report_inline_failed_reason (e);
return false;
}
tree callee_tree
= callee ? DECL_FUNCTION_SPECIFIC_OPTIMIZATION (callee->decl) : NULL;
/* Check if caller growth allows the inlining. */
- if (!DECL_DISREGARD_INLINE_LIMITS (callee->decl)
- && !disregard_limits
- && !lookup_attribute ("flatten",
- DECL_ATTRIBUTES (caller->decl))
+ if (!(flags & CAN_INLINE_DISREGARD_LIMITS)
+ && ((flags & CAN_INLINE_FORCE_LIMITS)
+ || (!DECL_DISREGARD_INLINE_LIMITS (callee->decl)
+ && !lookup_attribute ("flatten",
+ DECL_ATTRIBUTES (caller->decl))))
&& !caller_growth_limits (e))
inlinable = false;
else if (callee->externally_visible
to inline library always_inline functions. See PR65873.
Disable the check for early inlining for now until better solution
is found. */
- if (always_inline && early)
+ if (always_inline && (flags & CAN_INLINE_EARLY))
;
/* There are some options that change IL semantics which means
we cannot inline in these cases for correctness reason.
/* When devirtualization is disabled for callee, it is not safe
to inline it as we possibly mangled the type info.
Allow early inlining of always inlines. */
- || (!early && check_maybe_down (flag_devirtualize)))
+ || (!(flags & CAN_INLINE_EARLY) && check_maybe_down (flag_devirtualize)))
{
e->inline_failed = CIF_OPTIMIZATION_MISMATCH;
inlinable = false;
}
- if (!inlinable && report)
+ if (!inlinable && (flags & CAN_INLINE_REPORT))
report_inline_failed_reason (e);
return inlinable;
}
return false;
if (!can_inline_edge_p (e, true, true)
- || !can_inline_edge_by_limits_p (e, true, false, true))
+ || !can_inline_edge_by_limits_p (e, CAN_INLINE_EARLY | CAN_INLINE_REPORT))
return false;
/* When inlining regular functions into always-inline functions
during early inlining watch for possible inline cycles. */
want_inline = false;
}
}
+ if (!can_inline_edge_by_limits_p (edge, CAN_INLINE_FORCE_LIMITS | CAN_INLINE_REPORT))
+ {
+ reason = "inline limits exceeded for always_inline function";
+ want_inline = false;
+ }
if (!want_inline && dump_enabled_p ())
dump_printf_loc (MSG_MISSED_OPTIMIZATION, edge->call_stmt,
" not inlining recursively: %s\n", reason);
return true;
if (e->recursive_p ())
return true;
- if (!can_inline_edge_by_limits_p (e, true))
+ if (!can_inline_edge_by_limits_p (e, CAN_INLINE_REPORT))
return true;
/* Inlining large functions to large loop depth is often harmful because
of register pressure it implies. */
{
if (can_inline_edge_p (edge, false)
&& want_inline_small_function_p (edge, false)
- && can_inline_edge_by_limits_p (edge, false))
+ && can_inline_edge_by_limits_p (edge, 0))
update_edge_key (heap, edge);
else if (edge->aux)
{
{
if (can_inline_edge_p (e, false)
&& want_inline_small_function_p (e, false)
- && can_inline_edge_by_limits_p (e, false))
+ && can_inline_edge_by_limits_p (e, 0))
{
gcc_checking_assert (check_inlinability || can_inline_edge_p (e, false));
gcc_checking_assert (check_inlinability || e->aux);
struct cgraph_node *cnode, *dest = curr->callee;
if (!can_inline_edge_p (curr, true)
- || !can_inline_edge_by_limits_p (curr, true))
+ || !can_inline_edge_by_limits_p (curr, CAN_INLINE_REPORT | CAN_INLINE_FORCE_LIMITS))
continue;
/* MASTER_CLONE is produced in the case we already started modified
if (edge->inline_failed
&& can_inline_edge_p (edge, true)
&& want_inline_small_function_p (edge, true)
- && can_inline_edge_by_limits_p (edge, true))
+ && can_inline_edge_by_limits_p (edge, CAN_INLINE_REPORT))
{
inline_badness b (edge, edge_badness (edge, false));
edge->aux = heap->insert (b, edge);
return false;
/* For overwritable targets there is not much to do. */
if (!can_inline_edge_p (e, false)
- || !can_inline_edge_by_limits_p (e, false, true))
+ || !can_inline_edge_by_limits_p (e, CAN_INLINE_DISREGARD_LIMITS))
return false;
/* OK, speculation seems interesting. */
return true;
&& !edge->aux
&& can_inline_edge_p (edge, true)
&& want_inline_small_function_p (edge, true)
- && can_inline_edge_by_limits_p (edge, true)
+ && can_inline_edge_by_limits_p (edge, CAN_INLINE_REPORT)
&& edge->inline_failed)
{
gcc_assert (!edge->aux);
}
if (!can_inline_edge_p (edge, true)
- || !can_inline_edge_by_limits_p (edge, true))
+ || !can_inline_edge_by_limits_p (edge, CAN_INLINE_REPORT))
{
resolve_noninline_speculation (&edge_heap, edge);
continue;
{
if (where->inlined_to)
where = where->inlined_to;
+
+ /* Disable always_inline on self recursive functions.
+ This prevents some inlining bombs such as one in PR113291
+ from exploding.
+ It is not enough to stop inlining in self recursive always_inlines
+ since they may grow large enough so always inlining them even
+ with recursin depth 0 is too much.
+
+ All sane uses of always_inline should be handled during
+ early optimizations. */
+ DECL_DISREGARD_INLINE_LIMITS (where->decl) = false;
+
if (!recursive_inlining (edge,
opt_for_fn (edge->caller->decl,
flag_indirect_inlining)
too. */
if (!early
? !can_inline_edge_p (e, true)
- && !can_inline_edge_by_limits_p (e, true)
+ && !can_inline_edge_by_limits_p (e, CAN_INLINE_REPORT)
: !can_early_inline_edge_p (e))
continue;
struct cgraph_node *caller = node->callers->caller;
if (!can_inline_edge_p (node->callers, true)
- || !can_inline_edge_by_limits_p (node->callers, true)
+ || !can_inline_edge_by_limits_p (node->callers, CAN_INLINE_REPORT)
|| node->callers->recursive_p ())
{
if (dump_file)