+2019-11-02 Jan Hubicka <hubicka@ucw.cz>
+
+ * ipa-fnsummary.c (ipa_call_context::duplicate_from): New
+ member function.
+ (ipa_call_context::release): Add ALL parameter.
+ (ipa_call_context::equal_to): New member function.
+ * ipa-fnsummary.h (ipa_call_context): Add empty constructor;
+ duplicate_form, release, equal_to and exists_p member functoins.
+ * ipa-inline-analysis.c (node_context_cache_entry): New
+ class.
+ (node_context_summary): Likewise.
+ (node_context_cache, node_context_cache_hit, node_context_cache_miss,
+ node_context_clear): New static vars.
+ (initialize_growth_caches): New function.
+ (free_growth_caches): Also delete node_context_cache; output stats.
+ (do_estimate_edge_time): Cache contexts.
+ (reset_node_cache): New function.
+ * ipa-inline.c (reset_edge_caches): Reset also node cache.
+ (inline_small_functions): Initialize growth caches.
+ * ipa-inline.h (reset_node_cache, initialize_growth_caches):
+ Declare.
+ * ipa-predicate.h (inline_param_summary::equal_to): New.
+ * ipa-prop.c (ipa_agg_jf_item::equal_to): New.
+ * ipa-prop.h (ipa_agg_jf_item): Declare equal_to member function.
+ (ipa_agg_jump_function): Implement equal_to member function.
+
2019-11-02 Jan Hubicka <hubicka@ucw.cz>
* ipa-fnsummary.c (inline_read_section): Set vector size
{
}
-/* Release memory used by known_vals/contexts/aggs vectors. */
+void
+ipa_call_context::duplicate_from (const ipa_call_context &ctx)
+{
+ m_node = ctx.m_node;
+ m_possible_truths = ctx.m_possible_truths;
+ m_nonspec_possible_truths = ctx.m_nonspec_possible_truths;
+
+ if (ctx.m_inline_param_summary.exists ())
+ m_inline_param_summary = ctx.m_inline_param_summary.copy ();
+ else
+ m_inline_param_summary = vNULL;
+ if (ctx.m_known_vals.exists ())
+ m_known_vals = ctx.m_known_vals.copy ();
+ else
+ m_known_vals = vNULL;
+ if (ctx.m_known_contexts.exists ())
+ m_known_contexts = ctx.m_known_contexts.copy ();
+ else
+ m_known_contexts = vNULL;
+ if (ctx.m_known_aggs.exists ())
+ m_known_aggs = ctx.m_known_aggs.copy ();
+ else
+ m_known_aggs = vNULL;
+}
+
+/* Release memory used by known_vals/contexts/aggs vectors.
+ If ALL is true release also inline_param_summary.
+ This happens when context was previously duplciated to be stored
+ into cache. */
void
-ipa_call_context::release ()
+ipa_call_context::release (bool all)
{
+ /* See if context is initialized at first place. */
+ if (!m_node)
+ return;
m_known_vals.release ();
m_known_contexts.release ();
m_known_aggs.release ();
+ if (all)
+ m_inline_param_summary.release ();
+}
+
+/* Return true if CTX describes the same call context as THIS. */
+
+bool
+ipa_call_context::equal_to (const ipa_call_context &ctx)
+{
+ if (m_node != ctx.m_node
+ || m_possible_truths != ctx.m_possible_truths
+ || m_nonspec_possible_truths != ctx.m_nonspec_possible_truths)
+ return false;
+ if (m_inline_param_summary.exists () != ctx.m_inline_param_summary.exists ()
+ || m_known_vals.exists () != ctx.m_known_vals.exists()
+ || m_known_contexts.exists () != ctx.m_known_contexts.exists ()
+ || m_known_aggs.exists () != ctx.m_known_aggs.exists ())
+ return false;
+ if (m_inline_param_summary.exists ())
+ {
+ if (m_inline_param_summary.length () != ctx.m_inline_param_summary.length ())
+ return false;
+ for (unsigned int i = 0; i < m_inline_param_summary.length (); i++)
+ if (!m_inline_param_summary[i].equal_to (ctx.m_inline_param_summary[i]))
+ return false;
+ }
+ if (m_known_vals.exists ())
+ {
+ if (m_known_vals.length () != ctx.m_known_vals.length ())
+ return false;
+ for (unsigned int i = 0; i < m_known_vals.length (); i++)
+ {
+ tree t1 = m_known_vals[i];
+ tree t2 = ctx.m_known_vals[i];
+
+ if (t1 != t2
+ && (!t1 || !t2 || !operand_equal_p (m_known_vals[i],
+ ctx.m_known_vals[i], 0)))
+ return false;
+ }
+ }
+ if (m_known_contexts.exists ())
+ {
+ if (m_known_contexts.length () != ctx.m_known_contexts.length ())
+ return false;
+ for (unsigned int i = 0; i < m_known_contexts.length (); i++)
+ if (!m_known_contexts[i].equal_to (ctx.m_known_contexts[i]))
+ return false;
+ }
+ if (m_known_aggs.exists ())
+ {
+ if (m_known_aggs.length () != ctx.m_known_aggs.length ())
+ return false;
+ for (unsigned int i = 0; i < m_known_aggs.length (); i++)
+ if (!m_known_aggs[i]->equal_to (*ctx.m_known_aggs[i]))
+ return false;
+ }
+ return true;
}
/* Estimate size and time needed to execute call in the given context.
vec<ipa_polymorphic_call_context> known_contexts,
vec<ipa_agg_jump_function_p> known_aggs,
vec<inline_param_summary> m_inline_param_summary);
+ ipa_call_context ()
+ : m_node(NULL)
+ {
+ }
void estimate_size_and_time (int *ret_size, int *ret_min_size,
sreal *ret_time,
sreal *ret_nonspecialized_time,
ipa_hints *ret_hints);
- void release ();
+ void duplicate_from (const ipa_call_context &ctx);
+ void release (bool all = false);
+ bool equal_to (const ipa_call_context &);
+ bool exists_p ()
+ {
+ return m_node != NULL;
+ }
private:
/* Called function. */
cgraph_node *m_node;
/* Cached node/edge growths. */
call_summary<edge_growth_cache_entry *> *edge_growth_cache = NULL;
+/* The context cache remembers estimated time/size and hints for given
+ ipa_call_context of a call. */
+class node_context_cache_entry
+{
+public:
+ ipa_call_context ctx;
+ sreal time, nonspec_time;
+ int size;
+ ipa_hints hints;
+
+ node_context_cache_entry ()
+ : ctx ()
+ {
+ }
+ ~node_context_cache_entry ()
+ {
+ ctx.release ();
+ }
+};
+
+/* At the moment we implement primitive single entry LRU cache. */
+class node_context_summary
+{
+public:
+ node_context_cache_entry entry;
+
+ node_context_summary ()
+ : entry ()
+ {
+ }
+ ~node_context_summary ()
+ {
+ }
+};
+
+/* Summary holding the context cache. */
+static fast_function_summary <node_context_summary *, va_heap>
+ *node_context_cache = NULL;
+/* Statistics about the context cache effectivity. */
+static long node_context_cache_hit, node_context_cache_miss,
+ node_context_cache_clear;
+
/* Give initial reasons why inlining would fail on EDGE. This gets either
nullified or usually overwritten by more precise reasons later. */
== CIF_FINAL_ERROR);
}
+/* Allocate edge growth caches. */
+
+void
+initialize_growth_caches ()
+{
+ edge_growth_cache
+ = new call_summary<edge_growth_cache_entry *> (symtab, false);
+ node_context_cache
+ = new fast_function_summary<node_context_summary *, va_heap> (symtab);
+}
/* Free growth caches. */
free_growth_caches (void)
{
delete edge_growth_cache;
+ delete node_context_cache;
edge_growth_cache = NULL;
+ node_context_cache = NULL;
+ if (dump_file)
+ fprintf (dump_file, "node context cache: %li hits, %li misses,"
+ " %li initializations\n",
+ node_context_cache_hit, node_context_cache_miss,
+ node_context_cache_clear);
+ node_context_cache_hit = 0;
+ node_context_cache_miss = 0;
+ node_context_cache_clear = 0;
}
/* Return hints derrived from EDGE. */
vec<ipa_polymorphic_call_context> known_contexts;
vec<ipa_agg_jump_function_p> known_aggs;
class ipa_call_summary *es = ipa_call_summaries->get (edge);
- int min_size;
+ int min_size = -1;
callee = edge->callee->ultimate_alias_target ();
&known_contexts, &known_aggs);
ipa_call_context ctx (callee, clause, nonspec_clause, known_vals,
known_contexts, known_aggs, es->param);
- ctx.estimate_size_and_time (&size, &min_size,
- &time, &nonspec_time, &hints);
+ if (node_context_cache != NULL)
+ {
+ node_context_summary *e = node_context_cache->get_create (callee);
+ if (e->entry.ctx.equal_to (ctx))
+ {
+ node_context_cache_hit++;
+ size = e->entry.size;
+ time = e->entry.time;
+ nonspec_time = e->entry.nonspec_time;
+ hints = e->entry.hints;
+ }
+ else
+ {
+ if (e->entry.ctx.exists_p ())
+ node_context_cache_miss++;
+ else
+ node_context_cache_clear++;
+ e->entry.ctx.release (true);
+ e->entry.ctx = ctx;
+ ctx.estimate_size_and_time (&size, &min_size,
+ &time, &nonspec_time, &hints);
+ e->entry.size = size;
+ e->entry.time = time;
+ e->entry.nonspec_time = nonspec_time;
+ e->entry.hints = hints;
+ e->entry.ctx.duplicate_from (ctx);
+ }
+ }
+ else
+ ctx.estimate_size_and_time (&size, &min_size,
+ &time, &nonspec_time, &hints);
/* When we have profile feedback, we can quite safely identify hot
edges and for those we disable size limits. Don't do that when
/* When caching, update the cache entry. */
if (edge_growth_cache != NULL)
{
- ipa_fn_summaries->get (edge->callee->function_symbol ())->min_size
- = min_size;
+ if (min_size >= 0)
+ ipa_fn_summaries->get (edge->callee->function_symbol ())->min_size
+ = min_size;
edge_growth_cache_entry *entry
= edge_growth_cache->get_create (edge);
entry->time = time;
return time;
}
+/* Reset cache for NODE.
+ This must be done each time NODE body is modified. */
+void
+reset_node_cache (struct cgraph_node *node)
+{
+ if (node_context_cache)
+ node_context_cache->remove (node);
+}
/* Return estimated callee growth after inlining EDGE.
Only to be called via estimate_edge_size. */
if (where->inlined_to)
where = where->inlined_to;
+ reset_node_cache (where);
+
if (edge_growth_cache != NULL)
for (edge = where->callers; edge; edge = edge->next_caller)
if (edge->inline_failed)
max_count = max_count.max (edge->count.ipa ());
}
ipa_free_postorder_info ();
- edge_growth_cache
- = new call_summary<edge_growth_cache_entry *> (symtab, false);
+ initialize_growth_caches ();
if (dump_file)
fprintf (dump_file,
int do_estimate_edge_size (struct cgraph_edge *edge);
sreal do_estimate_edge_time (struct cgraph_edge *edge);
ipa_hints do_estimate_edge_hints (struct cgraph_edge *edge);
+void reset_node_cache (struct cgraph_node *node);
+void initialize_growth_caches ();
void free_growth_caches (void);
/* In ipa-inline.c */
Value 0 is reserved for compile time invariants. */
int change_prob;
+ bool equal_to (const inline_param_summary &other)
+ {
+ return change_prob == other.change_prob;
+ }
};
typedef vec<condition, va_gc> *conditions;
return TODO_update_ssa_only_virtuals;
}
+
+/* Return true if OTHER describes same agg item. */
+bool
+ipa_agg_jf_item::equal_to (const ipa_agg_jf_item &other)
+{
+ return offset == other.offset
+ && operand_equal_p (value, other.value, 0);
+}
#include "gt-ipa-prop.h"
/* The known constant or type if this is a clobber. */
tree value;
+
+ /* Return true if OTHER describes same agg item. */
+ bool equal_to (const ipa_agg_jf_item &other);
};
vec<ipa_agg_jf_item, va_gc> *items;
/* True if the data was passed by reference (as opposed to by value). */
bool by_ref;
+
+ /* Return true if OTHER describes same agg items. */
+ bool equal_to (const ipa_agg_jump_function &other)
+ {
+ if (by_ref != other.by_ref)
+ return false;
+ if (items != NULL && other.items == NULL)
+ return false;
+ if (!items)
+ return other.items == NULL;
+ if (items->length () != other.items->length ())
+ return false;
+ for (unsigned int i = 0; i < items->length (); i++)
+ if (!(*items)[i].equal_to ((*other.items)[i]))
+ return false;
+ return true;
+ }
};
typedef struct ipa_agg_jump_function *ipa_agg_jump_function_p;