ipa-fnsummary.c (ipa_call_context::duplicate_from): New member function.
* 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. From-SVN: r277757
This commit is contained in:
parent
360386c7ef
commit
ac6f2e5948
9 changed files with 270 additions and 10 deletions
|
@ -1,3 +1,29 @@
|
|||
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
|
||||
|
|
|
@ -2964,14 +2964,103 @@ ipa_call_context::ipa_call_context (cgraph_node *node,
|
|||
{
|
||||
}
|
||||
|
||||
/* 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.
|
||||
|
|
|
@ -295,11 +295,21 @@ public:
|
|||
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;
|
||||
|
|
|
@ -53,6 +53,48 @@ along with GCC; see the file COPYING3. If not see
|
|||
/* 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. */
|
||||
|
||||
|
@ -77,6 +119,16 @@ initialize_inline_failed (struct cgraph_edge *e)
|
|||
== 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. */
|
||||
|
||||
|
@ -84,7 +136,17 @@ void
|
|||
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. */
|
||||
|
@ -129,7 +191,7 @@ do_estimate_edge_time (struct cgraph_edge *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 ();
|
||||
|
||||
|
@ -139,8 +201,37 @@ do_estimate_edge_time (struct cgraph_edge *edge)
|
|||
&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
|
||||
|
@ -160,8 +251,9 @@ do_estimate_edge_time (struct cgraph_edge *edge)
|
|||
/* 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;
|
||||
|
@ -174,6 +266,14 @@ do_estimate_edge_time (struct cgraph_edge *edge)
|
|||
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. */
|
||||
|
|
|
@ -1368,6 +1368,8 @@ reset_edge_caches (struct cgraph_node *node)
|
|||
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)
|
||||
|
@ -1900,8 +1902,7 @@ inline_small_functions (void)
|
|||
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,
|
||||
|
|
|
@ -48,6 +48,8 @@ bool growth_likely_positive (struct cgraph_node *, int);
|
|||
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 */
|
||||
|
|
|
@ -77,6 +77,10 @@ struct inline_param_summary
|
|||
|
||||
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;
|
||||
|
|
|
@ -5293,4 +5293,12 @@ ipcp_transform_function (struct cgraph_node *node)
|
|||
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"
|
||||
|
|
|
@ -127,6 +127,9 @@ struct GTY(()) ipa_agg_jf_item
|
|||
|
||||
/* 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);
|
||||
};
|
||||
|
||||
|
||||
|
@ -139,6 +142,23 @@ struct GTY(()) ipa_agg_jump_function
|
|||
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;
|
||||
|
|
Loading…
Add table
Reference in a new issue