}
+/* Dump inline hints. */
+void
+dump_inline_hints (FILE *f, inline_hints hints)
+{
+ if (!hints)
+ return;
+ fprintf (f, "inline hints:");
+ if (hints & INLINE_HINT_indirect_call)
+ {
+ hints &= ~INLINE_HINT_indirect_call;
+ fprintf (f, " indirect_call");
+ }
+ gcc_assert (!hints);
+}
+
+
/* Record SIZE and TIME under condition PRED into the inline summary. */
static void
/* Estimate benefit devirtualizing indirect edge IE, provided KNOWN_VALS and
KNOWN_BINFOS. */
-static void
+static bool
estimate_edge_devirt_benefit (struct cgraph_edge *ie,
int *size, int *time, int prob,
VEC (tree, heap) *known_vals,
{
tree target;
int time_diff, size_diff;
+ struct cgraph_node *callee;
+ struct inline_summary *isummary;
if (!known_vals && !known_binfos)
- return;
+ return false;
target = ipa_get_indirect_edge_target (ie, known_vals, known_binfos,
known_aggs);
if (!target)
- return;
+ return false;
/* Account for difference in cost between indirect and direct calls. */
size_diff = ((eni_size_weights.indirect_call_cost - eni_size_weights.call_cost)
* INLINE_TIME_SCALE * prob / REG_BR_PROB_BASE);
*time -= time_diff;
- /* TODO: This code is trying to benefit indirect calls that will be inlined later.
- The logic however do not belong into local size/time estimates and can not be
- done here, or the accounting of changes will get wrong and we result with
- negative function body sizes. We need to introduce infrastructure for independent
- benefits to the inliner. */
-#if 0
- struct cgraph_node *callee;
- struct inline_summary *isummary;
- int edge_size, edge_time, time_diff, size_diff;
-
callee = cgraph_get_node (target);
if (!callee || !callee->analyzed)
- return;
+ return false;
isummary = inline_summary (callee);
- if (!isummary->inlinable)
- return;
-
- estimate_edge_size_and_time (ie, &edge_size, &edge_time, prob);
-
- /* Count benefit only from functions that definitely will be inlined
- if additional context from NODE's caller were available.
-
- We just account overall size change by inlining. TODO:
- we really need to add sort of benefit metrics for these kind of
- cases. */
- if (edge_size - size_diff >= isummary->size * INLINE_SIZE_SCALE)
- {
- /* Subtract size and time that we added for edge IE. */
- *size -= edge_size - size_diff;
-
- /* Account inlined call. */
- *size += isummary->size * INLINE_SIZE_SCALE;
- }
-#endif
+ return isummary->inlinable;
}
static void
estimate_calls_size_and_time (struct cgraph_node *node, int *size, int *time,
+ inline_hints *hints,
clause_t possible_truths,
VEC (tree, heap) *known_vals,
VEC (tree, heap) *known_binfos,
estimate_edge_size_and_time (e, size, time, REG_BR_PROB_BASE);
}
else
- estimate_calls_size_and_time (e->callee, size, time,
+ estimate_calls_size_and_time (e->callee, size, time, hints,
possible_truths,
known_vals, known_binfos, known_aggs);
}
if (!es->predicate || evaluate_predicate (es->predicate, possible_truths))
{
estimate_edge_size_and_time (e, size, time, REG_BR_PROB_BASE);
- estimate_edge_devirt_benefit (e, size, time, REG_BR_PROB_BASE,
- known_vals, known_binfos, known_aggs);
+ if (estimate_edge_devirt_benefit (e, size, time, REG_BR_PROB_BASE,
+ known_vals, known_binfos, known_aggs)
+ && hints
+ && cgraph_maybe_hot_edge_p (e))
+ *hints |= INLINE_HINT_indirect_call;
}
}
}
VEC (tree, heap) *known_binfos,
VEC (ipa_agg_jump_function_p, heap) *known_aggs,
int *ret_size, int *ret_time,
+ inline_hints *ret_hints,
VEC (inline_param_summary_t, heap)
*inline_param_summary)
{
struct inline_summary *info = inline_summary (node);
size_time_entry *e;
int size = 0, time = 0;
+ inline_hints hints = 0;
int i;
if (dump_file
if (time > MAX_TIME * INLINE_TIME_SCALE)
time = MAX_TIME * INLINE_TIME_SCALE;
- estimate_calls_size_and_time (node, &size, &time, possible_truths,
+ estimate_calls_size_and_time (node, &size, &time, &hints, possible_truths,
known_vals, known_binfos, known_aggs);
time = (time + INLINE_TIME_SCALE / 2) / INLINE_TIME_SCALE;
size = (size + INLINE_SIZE_SCALE / 2) / INLINE_SIZE_SCALE;
*ret_time = time;
if (ret_size)
*ret_size = size;
+ if (ret_hints)
+ *ret_hints = hints;
return;
}
clause = evaluate_conditions_for_known_args (node, false, known_vals, NULL);
estimate_node_size_and_time (node, clause, known_vals, known_binfos, NULL,
- ret_size, ret_time,
+ ret_size, ret_time, NULL,
NULL);
}
info->time = 0;
for (i = 0; VEC_iterate (size_time_entry, info->entry, i, e); i++)
info->size += e->size, info->time += e->time;
- estimate_calls_size_and_time (node, &info->size, &info->time,
+ estimate_calls_size_and_time (node, &info->size, &info->time, NULL,
~(clause_t)(1 << predicate_false_condition),
NULL, NULL, NULL);
info->time = (info->time + INLINE_TIME_SCALE / 2) / INLINE_TIME_SCALE;
{
int time;
int size;
+ inline_hints hints;
gcov_type ret;
struct cgraph_node *callee;
clause_t clause;
&clause, &known_vals, &known_binfos,
&known_aggs);
estimate_node_size_and_time (callee, clause, known_vals, known_binfos,
- known_aggs, &size, &time, es->param);
+ known_aggs, &size, &time, &hints, es->param);
VEC_free (tree, heap, known_vals);
VEC_free (tree, heap, known_binfos);
VEC_free (ipa_agg_jump_function_p, heap, known_aggs);
gcc_checking_assert (es->call_stmt_size);
VEC_index (edge_growth_cache_entry, edge_growth_cache, edge->uid).size
= ret_size + (ret_size >= 0);
+ VEC_index (edge_growth_cache_entry, edge_growth_cache, edge->uid).hints
+ = hints + 1;
}
return ret;
}
&clause, &known_vals, &known_binfos,
&known_aggs);
estimate_node_size_and_time (callee, clause, known_vals, known_binfos,
- known_aggs, &size, NULL, NULL);
+ known_aggs, &size, NULL, NULL, NULL);
VEC_free (tree, heap, known_vals);
VEC_free (tree, heap, known_binfos);
VEC_free (ipa_agg_jump_function_p, heap, known_aggs);
}
+/* Estimate the growth of the caller when inlining EDGE.
+ Only to be called via estimate_edge_size. */
+
+inline_hints
+do_estimate_edge_hints (struct cgraph_edge *edge)
+{
+ inline_hints hints;
+ struct cgraph_node *callee;
+ clause_t clause;
+ VEC (tree, heap) *known_vals;
+ VEC (tree, heap) *known_binfos;
+ VEC (ipa_agg_jump_function_p, heap) *known_aggs;
+
+ /* When we do caching, use do_estimate_edge_time to populate the entry. */
+
+ if (edge_growth_cache)
+ {
+ do_estimate_edge_time (edge);
+ hints = VEC_index (edge_growth_cache_entry,
+ edge_growth_cache,
+ edge->uid).hints;
+ gcc_checking_assert (hints);
+ return hints - 1;
+ }
+
+ callee = cgraph_function_or_thunk_node (edge->callee, NULL);
+
+ /* Early inliner runs without caching, go ahead and do the dirty work. */
+ gcc_checking_assert (edge->inline_failed);
+ evaluate_properties_for_edge (edge, true,
+ &clause, &known_vals, &known_binfos,
+ &known_aggs);
+ estimate_node_size_and_time (callee, clause, known_vals, known_binfos,
+ known_aggs, NULL, NULL, &hints, NULL);
+ VEC_free (tree, heap, known_vals);
+ VEC_free (tree, heap, known_binfos);
+ VEC_free (ipa_agg_jump_function_p, heap, known_aggs);
+ return hints;
+}
+
+
/* Estimate self time of the function NODE after inlining EDGE. */
int
else
{
int growth = estimate_edge_growth (e);
+ inline_hints hints = estimate_edge_hints (e);
if (growth <= 0)
;
+ /* Apply MAX_INLINE_INSNS_SINGLE limit. Do not do so when
+ hints suggests that inlining given function is very profitable. */
else if (DECL_DECLARED_INLINE_P (callee->symbol.decl)
- && growth >= MAX_INLINE_INSNS_SINGLE)
+ && growth >= MAX_INLINE_INSNS_SINGLE
+ && !(hints & INLINE_HINT_indirect_call))
{
e->inline_failed = CIF_MAX_INLINE_INSNS_SINGLE_LIMIT;
want_inline = false;
e->inline_failed = CIF_NOT_DECLARED_INLINED;
want_inline = false;
}
+ /* Apply MAX_INLINE_INSNS_AUTO limit for functions not declared inline
+ Upgrade it to MAX_INLINE_INSNS_SINGLE when hints suggests that
+ inlining given function is very profitable. */
else if (!DECL_DECLARED_INLINE_P (callee->symbol.decl)
- && growth >= MAX_INLINE_INSNS_AUTO)
+ && growth >= ((hints & INLINE_HINT_indirect_call)
+ ? MAX (MAX_INLINE_INSNS_AUTO,
+ MAX_INLINE_INSNS_SINGLE)
+ : MAX_INLINE_INSNS_AUTO))
{
e->inline_failed = CIF_MAX_INLINE_INSNS_AUTO_LIMIT;
want_inline = false;
struct cgraph_node *callee = cgraph_function_or_thunk_node (edge->callee,
NULL);
struct inline_summary *callee_info = inline_summary (callee);
+ inline_hints hints;
if (DECL_DISREGARD_INLINE_LIMITS (callee->symbol.decl))
return INT_MIN;
growth = estimate_edge_growth (edge);
time_growth = estimate_edge_time (edge);
+ hints = estimate_edge_hints (edge);
if (dump)
{
fprintf (dump_file, " Badness calculation for %s -> %s\n",
xstrdup (cgraph_node_name (edge->caller)),
xstrdup (cgraph_node_name (callee)));
- fprintf (dump_file, " size growth %i, time growth %i\n",
+ fprintf (dump_file, " size growth %i, time growth %i ",
growth,
time_growth);
+ dump_inline_hints (dump_file, hints);
+ fprintf (dump_file, "\n");
}
/* Always prefer inlining saving code size. */
if (dump)
fprintf (dump_file, "Badness overflow\n");
}
+ if (hints & INLINE_HINT_indirect_call)
+ badness /= 8;
if (dump)
{
fprintf (dump_file,
unsigned by_ref : 1;
} condition;
+/* Inline hints are reasons why inline heuristics should preffer inlining given function.
+ They are represtented as bitmap of the following values. */
+enum inline_hints_vals {
+ INLINE_HINT_indirect_call = 1
+};
+typedef int inline_hints;
+
DEF_VEC_O (condition);
DEF_VEC_ALLOC_O (condition, gc);
typedef struct edge_growth_cache_entry
{
int time, size;
+ inline_hints hints;
} edge_growth_cache_entry;
DEF_VEC_O(edge_growth_cache_entry);
DEF_VEC_ALLOC_O(edge_growth_cache_entry,heap);
/* In ipa-inline-analysis.c */
void debug_inline_summary (struct cgraph_node *);
void dump_inline_summaries (FILE *f);
-void dump_inline_summary (FILE * f, struct cgraph_node *node);
+void dump_inline_summary (FILE *f, struct cgraph_node *node);
+void dump_inline_hints (FILE *f, inline_hints);
void inline_generate_summary (void);
void inline_read_summary (void);
void inline_write_summary (void);
void inline_update_overall_summary (struct cgraph_node *node);
int do_estimate_edge_growth (struct cgraph_edge *edge);
int do_estimate_edge_time (struct cgraph_edge *edge);
+inline_hints do_estimate_edge_hints (struct cgraph_edge *edge);
void initialize_growth_caches (void);
void free_growth_caches (void);
void compute_inline_parameters (struct cgraph_node *, bool);
}
+/* Return estimated callee runtime increase after inlning
+ EDGE. */
+
+static inline inline_hints
+estimate_edge_hints (struct cgraph_edge *edge)
+{
+ inline_hints ret;
+ if ((int)VEC_length (edge_growth_cache_entry, edge_growth_cache) <= edge->uid
+ || !(ret = VEC_index (edge_growth_cache_entry,
+ edge_growth_cache,
+ edge->uid).hints))
+ return do_estimate_edge_time (edge);
+ return ret - 1;
+}
+
+
/* Reset cached value for NODE. */
static inline void
{
if ((int)VEC_length (edge_growth_cache_entry, edge_growth_cache) > edge->uid)
{
- struct edge_growth_cache_entry zero = {0, 0};
+ struct edge_growth_cache_entry zero = {0, 0, 0};
VEC_replace (edge_growth_cache_entry, edge_growth_cache, edge->uid, zero);
}
}