]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
re PR lto/45375 ([meta-bug] Issues with building Mozilla (i.e. Firefox) with LTO)
authorJan Hubicka <jh@suse.cz>
Sun, 19 Aug 2012 05:55:20 +0000 (07:55 +0200)
committerJan Hubicka <hubicka@gcc.gnu.org>
Sun, 19 Aug 2012 05:55:20 +0000 (05:55 +0000)
PR lto/45375
* ipa-inline.c (want_inline_small_function_p): Bypass
inline limits for hinted functions.
(edge_badness): Dump hints; decrease badness for hinted funcitons.
* ipa-inline.h (enum inline_hints_vals): New enum.
(inline_hints): New type.
(edge_growth_cache_entry): Add hints.
(dump_inline_summary): Update.
(dump_inline_hints): Declare.
(do_estimate_edge_hints): Declare.
(estimate_edge_hints): New inline function.
(reset_edge_growth_cache): Update.
* predict.c (cgraph_maybe_hot_edge_p): Do not ice on indirect edges.
* ipa-inline-analysis.c (dump_inline_hints): New function.
(estimate_edge_devirt_benefit): Return true when function should be
hinted.
(estimate_calls_size_and_time): New hints argument; set it when
devritualization happens.
(estimate_node_size_and_time): New hints argument.
(do_estimate_edge_time): Cache hints.
(do_estimate_edge_growth): Update.
(do_estimate_edge_hints): New function

From-SVN: r190509

gcc/ChangeLog
gcc/ipa-inline-analysis.c
gcc/ipa-inline.c
gcc/ipa-inline.h
gcc/predict.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/ipa/iinline-1.c

index 1f313e9b7d89da753046002be15df2b51c579739..6dabed6c468422083db23ba19e098085ce4f6dba 100644 (file)
@@ -1,3 +1,28 @@
+2012-08-18  Jan Hubicka  <jh@suse.cz>
+
+       PR lto/45375
+       * ipa-inline.c (want_inline_small_function_p): Bypass
+       inline limits for hinted functions.
+       (edge_badness): Dump hints; decrease badness for hinted funcitons.
+       * ipa-inline.h (enum inline_hints_vals): New enum.
+       (inline_hints): New type.
+       (edge_growth_cache_entry): Add hints.
+       (dump_inline_summary): Update.
+       (dump_inline_hints): Declare.
+       (do_estimate_edge_hints): Declare.
+       (estimate_edge_hints): New inline function.
+       (reset_edge_growth_cache): Update.
+       * predict.c (cgraph_maybe_hot_edge_p): Do not ice on indirect edges.
+       * ipa-inline-analysis.c (dump_inline_hints): New function.
+       (estimate_edge_devirt_benefit): Return true when function should be
+       hinted.
+       (estimate_calls_size_and_time): New hints argument; set it when
+       devritualization happens.
+       (estimate_node_size_and_time): New hints argument.
+       (do_estimate_edge_time): Cache hints.
+       (do_estimate_edge_growth): Update.      
+       (do_estimate_edge_hints): New function
+
 2012-08-18  John David Anglin  <dave.anglin@nrc-cnrc.gc.ca>
 
        PR middle-end/53823
index 9f96ca8a3baa71abe1efa39de4a213ff81cd08e8..c30e81a88f150762925193248feeda102bea1e2c 100644 (file)
@@ -615,6 +615,22 @@ dump_predicate (FILE *f, conditions conds, struct predicate *pred)
 }
 
 
+/* 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
@@ -2302,7 +2318,7 @@ estimate_edge_size_and_time (struct cgraph_edge *e, int *size, int *time,
 /* 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,
@@ -2311,14 +2327,16 @@ estimate_edge_devirt_benefit (struct cgraph_edge *ie,
 {
   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)
@@ -2328,40 +2346,11 @@ estimate_edge_devirt_benefit (struct cgraph_edge *ie,
               * 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;
 }
 
 
@@ -2371,6 +2360,7 @@ estimate_edge_devirt_benefit (struct cgraph_edge *ie,
 
 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,
@@ -2389,7 +2379,7 @@ estimate_calls_size_and_time (struct cgraph_node *node, int *size, int *time,
              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);
        }
@@ -2400,8 +2390,11 @@ estimate_calls_size_and_time (struct cgraph_node *node, int *size, int *time,
       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;
        }
     }
 }
@@ -2418,12 +2411,14 @@ estimate_node_size_and_time (struct cgraph_node *node,
                             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
@@ -2467,7 +2462,7 @@ estimate_node_size_and_time (struct cgraph_node *node,
   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;
@@ -2480,6 +2475,8 @@ estimate_node_size_and_time (struct cgraph_node *node,
     *ret_time = time;
   if (ret_size)
     *ret_size = size;
+  if (ret_hints)
+    *ret_hints = hints;
   return;
 }
 
@@ -2499,7 +2496,7 @@ estimate_ipcp_clone_size_and_time (struct cgraph_node *node,
 
   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);
 }
 
@@ -2871,7 +2868,7 @@ inline_update_overall_summary (struct cgraph_node *node)
   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;
@@ -2890,6 +2887,7 @@ do_estimate_edge_time (struct cgraph_edge *edge)
 {
   int time;
   int size;
+  inline_hints hints;
   gcov_type ret;
   struct cgraph_node *callee;
   clause_t clause;
@@ -2905,7 +2903,7 @@ do_estimate_edge_time (struct cgraph_edge *edge)
                                &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);
@@ -2929,6 +2927,8 @@ do_estimate_edge_time (struct cgraph_edge *edge)
       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;
 }
@@ -2967,7 +2967,7 @@ do_estimate_edge_growth (struct cgraph_edge *edge)
                                &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);
@@ -2976,6 +2976,47 @@ do_estimate_edge_growth (struct cgraph_edge *edge)
 }
 
 
+/* 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
index 5215049bc05a2fe0dc2f3fc973967cb2c3ab1222..55d9a521c32d943500f71b61d7d1b716a8822d90 100644 (file)
@@ -472,11 +472,15 @@ want_inline_small_function_p (struct cgraph_edge *e, bool report)
   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;
@@ -523,8 +527,14 @@ want_inline_small_function_p (struct cgraph_edge *e, bool report)
           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;
@@ -743,21 +753,25 @@ edge_badness (struct cgraph_edge *edge, bool dump)
   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.  */
@@ -849,6 +863,8 @@ edge_badness (struct cgraph_edge *edge, bool dump)
          if (dump)
            fprintf (dump_file, "Badness overflow\n");
        }
+      if (hints & INLINE_HINT_indirect_call)
+       badness /= 8;
       if (dump)
        {
          fprintf (dump_file,
index d6909afbcd686a7dbc20111c798b19369f763690..fca99e629b0046e85cfb732ad8e3bc90a9561c19 100644 (file)
@@ -42,6 +42,13 @@ typedef struct GTY(()) condition
     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);
 
@@ -158,6 +165,7 @@ extern VEC(inline_edge_summary_t,heap) *inline_edge_summary_vec;
 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);
@@ -168,7 +176,8 @@ extern VEC(edge_growth_cache_entry,heap) *edge_growth_cache;
 /* 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);
@@ -185,6 +194,7 @@ void inline_merge_summary (struct cgraph_edge *edge);
 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);
@@ -257,6 +267,22 @@ estimate_edge_time (struct cgraph_edge *edge)
 }
 
 
+/* 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
@@ -273,7 +299,7 @@ reset_edge_growth_cache (struct cgraph_edge *edge)
 {
   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);
     }
 }
index c884d384540d074d5a4cba29a5d8a45cddc073e1..e1a064d6cde2323dfdd31884527d43d050f45be2 100644 (file)
@@ -165,10 +165,12 @@ cgraph_maybe_hot_edge_p (struct cgraph_edge *edge)
          <= profile_info->sum_max / PARAM_VALUE (HOT_BB_COUNT_FRACTION)))
     return false;
   if (edge->caller->frequency == NODE_FREQUENCY_UNLIKELY_EXECUTED
-      || edge->callee->frequency == NODE_FREQUENCY_UNLIKELY_EXECUTED)
+      || (edge->callee
+         && edge->callee->frequency == NODE_FREQUENCY_UNLIKELY_EXECUTED))
     return false;
   if (edge->caller->frequency > NODE_FREQUENCY_UNLIKELY_EXECUTED
-      && edge->callee->frequency <= NODE_FREQUENCY_EXECUTED_ONCE)
+      && (edge->callee
+         && edge->callee->frequency <= NODE_FREQUENCY_EXECUTED_ONCE))
     return false;
   if (optimize_size)
     return false;
index e8a48c9c6d8f05c4eb3eeae833bb7eca1e2cc97a..378e778f56a85684549d3595707699fd6cf95266 100644 (file)
@@ -1,3 +1,7 @@
+2012-08-18  Jan Hubicka  <jh@suse.cz>
+
+       * gcc.dg/ipa/iinline-1.c: Update testcase to test inline hints.
+
 2012-08-18  Mikael Morin  <mikael@gcc.gnu.org>
 
        PR fortran/39290
index 617c484994b4d0bf25e9392b9dd1ef573c4559a5..860b3e5720436f0a2ca0d209530dcb4e5c112e4f 100644 (file)
@@ -1,7 +1,7 @@
 /* Verify that simple indirect calls are inlined even without early
    inlining..  */
 /* { dg-do compile } */
-/* { dg-options "-O3 -c -fdump-ipa-inline -fno-early-inlining"  } */
+/* { dg-options "-O3 -c -fdump-ipa-inline-details -fno-early-inlining -fno-ipa-cp"  } */
 
 extern void non_existent(int);
 
@@ -22,5 +22,6 @@ int test (void)
   return 0;
 }
 
+/* { dg-final { scan-ipa-dump "indirect_call"  "inline"  } } */
 /* { dg-final { scan-ipa-dump "hooray\[^\\n\]*inline copy in test"  "inline"  } } */
 /* { dg-final { cleanup-ipa-dump "inline" } } */