ipa-fnsummary.c (ipa_call_context::duplicate_from): New member function.
authorJan Hubicka <hubicka@ucw.cz>
Sun, 3 Nov 2019 16:37:45 +0000 (17:37 +0100)
committerJan Hubicka <hubicka@gcc.gnu.org>
Sun, 3 Nov 2019 16:37:45 +0000 (16:37 +0000)
* 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

gcc/ChangeLog
gcc/ipa-fnsummary.c
gcc/ipa-fnsummary.h
gcc/ipa-inline-analysis.c
gcc/ipa-inline.c
gcc/ipa-inline.h
gcc/ipa-predicate.h
gcc/ipa-prop.c
gcc/ipa-prop.h

index c249890731442f0c625bf251cc6bb61ab41be474..fdac1b2ca09ef575ad40599ec8f0e19a2057730b 100644 (file)
@@ -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
index 96b50cc445f6258699723a19ea033d7cce489cfc..eaf904cd4017462ab45d041a672c899d90b4359a 100644 (file)
@@ -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.
index 7638b4502162a5c6df99307e88ad185f07263188..91488a998814e069672396d7bbedc035a08bb325 100644 (file)
@@ -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;
index 5c00c0d1f44ffbf50199b2edd81618184b4403c5..436310596ac475f9944424ddea2b329bb0d78675 100644 (file)
@@ -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.  */
index 05bc8e70677fe243d6e567cfa1d62837ed805551..dacec7d401b728cbeb3ebd99a3f169c409fb4bdf 100644 (file)
@@ -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,
index 18c8e1eebd0cbcd0099cdf100501dbcc143fb0f3..7675b9e08fa8585b3105550d782c8f262a1067a6 100644 (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  */
index 4121218ac041c631cc9751e9458b8fead6f37a27..2509b1e516217ac616b989f1343dcbce792a2e7a 100644 (file)
@@ -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;
index e20467a4bcef6c0013ed865be9f41eb3eec62a93..5491aee2fc6090819f86b029b10d1f8b9157112b 100644 (file)
@@ -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"
index 07a7eea9249110b347bef11f11b50d7502a4ab22..121d0f647b11a16f9b91442bb894edada26bb65c 100644 (file)
@@ -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;