From: Jan Hubicka Date: Sun, 3 Nov 2019 16:37:45 +0000 (+0100) Subject: ipa-fnsummary.c (ipa_call_context::duplicate_from): New member function. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=ac6f2e594886e2209446114023ecdff96b0bd7c4;p=gcc.git 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 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index c2498907314..fdac1b2ca09 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,29 @@ +2019-11-02 Jan Hubicka + + * 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 * ipa-fnsummary.c (inline_read_section): Set vector size diff --git a/gcc/ipa-fnsummary.c b/gcc/ipa-fnsummary.c index 96b50cc445f..eaf904cd401 100644 --- a/gcc/ipa-fnsummary.c +++ b/gcc/ipa-fnsummary.c @@ -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. diff --git a/gcc/ipa-fnsummary.h b/gcc/ipa-fnsummary.h index 7638b450216..91488a99881 100644 --- a/gcc/ipa-fnsummary.h +++ b/gcc/ipa-fnsummary.h @@ -295,11 +295,21 @@ public: vec known_contexts, vec known_aggs, vec 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; diff --git a/gcc/ipa-inline-analysis.c b/gcc/ipa-inline-analysis.c index 5c00c0d1f44..436310596ac 100644 --- a/gcc/ipa-inline-analysis.c +++ b/gcc/ipa-inline-analysis.c @@ -53,6 +53,48 @@ along with GCC; see the file COPYING3. If not see /* Cached node/edge growths. */ call_summary *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_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 (symtab, false); + node_context_cache + = new fast_function_summary (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 known_contexts; vec 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. */ diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c index 05bc8e70677..dacec7d401b 100644 --- a/gcc/ipa-inline.c +++ b/gcc/ipa-inline.c @@ -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 (symtab, false); + initialize_growth_caches (); if (dump_file) fprintf (dump_file, diff --git a/gcc/ipa-inline.h b/gcc/ipa-inline.h index 18c8e1eebd0..7675b9e08fa 100644 --- a/gcc/ipa-inline.h +++ b/gcc/ipa-inline.h @@ -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 */ diff --git a/gcc/ipa-predicate.h b/gcc/ipa-predicate.h index 4121218ac04..2509b1e5162 100644 --- a/gcc/ipa-predicate.h +++ b/gcc/ipa-predicate.h @@ -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 *conditions; diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c index e20467a4bce..5491aee2fc6 100644 --- a/gcc/ipa-prop.c +++ b/gcc/ipa-prop.c @@ -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" diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h index 07a7eea9249..121d0f647b1 100644 --- a/gcc/ipa-prop.h +++ b/gcc/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 *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;