]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
* cgraph.c (cgraph_turn_edge_to_speculative): Return newly
authorhubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 13 Aug 2013 12:21:16 +0000 (12:21 +0000)
committerhubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 13 Aug 2013 12:21:16 +0000 (12:21 +0000)
introduced edge; fix typo in sanity check.
(cgraph_resolve_speculation): Export; improve diagnostic.
(cgraph_redirect_edge_call_stmt_to_callee): Better diagnostic; cancel
speculation at type mismatch.
* cgraph.h (cgraph_turn_edge_to_speculative): Update.
(cgraph_resolve_speculation): Declare.
(symtab_can_be_discarded): New function.
* value-prof.c (gimple_ic_transform): Remove actual transform code.
* ipa-inline-transform.c (speculation_removed): New global var.
(clone_inlined_nodes): See if speculation can be removed.
(inline_call): If speculations was removed, we growths may not match.
* ipa-inline.c (can_inline_edge_p): Add DISREGARD_LIMITS parameter.
(speculation_useful_p): New function.
(resolve_noninline_speculation): New function.
(inline_small_functions): Resolve useless speculations.
* ipa-inline.h (speculation_useful_p): Declare
* ipa.c (can_replace_by_local_alias): Simplify.
(ipa_profile): Produce speculative calls in non-lto, too;
add simple cost model; produce local aliases.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@201683 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/ChangeLog
gcc/cgraph.c
gcc/cgraph.h
gcc/ipa-inline-transform.c
gcc/ipa-inline.c
gcc/ipa-inline.h
gcc/ipa.c
gcc/value-prof.c

index 0e8e6e64d8ad7f29c5ba55a1dc43fc041f20cca8..508a14a943883c46770bfd0eae93fb0716666e2d 100644 (file)
@@ -1,3 +1,26 @@
+2013-08-13  Jan Hubicka  <jh@suse.cz>
+
+       * cgraph.c (cgraph_turn_edge_to_speculative): Return newly
+       introduced edge; fix typo in sanity check.
+       (cgraph_resolve_speculation): Export; improve diagnostic.
+       (cgraph_redirect_edge_call_stmt_to_callee): Better diagnostic; cancel
+       speculation at type mismatch.
+       * cgraph.h (cgraph_turn_edge_to_speculative): Update.
+       (cgraph_resolve_speculation): Declare.
+       (symtab_can_be_discarded): New function.
+       * value-prof.c (gimple_ic_transform): Remove actual transform code.
+       * ipa-inline-transform.c (speculation_removed): New global var.
+       (clone_inlined_nodes): See if speculation can be removed.
+       (inline_call): If speculations was removed, we growths may not match.
+       * ipa-inline.c (can_inline_edge_p): Add DISREGARD_LIMITS parameter.
+       (speculation_useful_p): New function.
+       (resolve_noninline_speculation): New function.
+       (inline_small_functions): Resolve useless speculations.
+       * ipa-inline.h (speculation_useful_p): Declare
+       * ipa.c (can_replace_by_local_alias): Simplify.
+       (ipa_profile): Produce speculative calls in non-lto, too;
+       add simple cost model; produce local aliases.
+
 2013-08-13  David Malcolm  <dmalcolm@redhat.com>
 
        * config/i386/t-i386 (i386.o): Rename stray PIPELINE_H to
index 50d13ab4f848a1a851622c47f27ec2de5d993dab..a939ae834848571941c3160c50292ae36e99afaf 100644 (file)
@@ -1040,9 +1040,11 @@ cgraph_set_edge_callee (struct cgraph_edge *e, struct cgraph_node *n)
 
    At this time the function just creates the direct call,
    the referencd representing the if conditional and attaches
-   them all to the orginal indirect call statement.  */
+   them all to the orginal indirect call statement.  
 
-void
+   Return direct edge created.  */
+
+struct cgraph_edge *
 cgraph_turn_edge_to_speculative (struct cgraph_edge *e,
                                 struct cgraph_node *n2,
                                 gcov_type direct_count,
@@ -1073,6 +1075,7 @@ cgraph_turn_edge_to_speculative (struct cgraph_edge *e,
                              IPA_REF_ADDR, e->call_stmt);
   ref->lto_stmt_uid = e->lto_stmt_uid;
   ref->speculative = e->speculative;
+  return e2;
 }
 
 /* Speculative call consist of three components:
@@ -1107,7 +1110,7 @@ cgraph_speculative_call_info (struct cgraph_edge *e,
       if (e2->call_stmt)
        {
          e = cgraph_edge (e->caller, e2->call_stmt);
-         gcc_assert (!e->speculative && !e->indirect_unknown_callee);
+         gcc_assert (e->speculative && !e->indirect_unknown_callee);
        }
       else
        for (e = e->caller->callees; 
@@ -1147,7 +1150,7 @@ cgraph_redirect_edge_callee (struct cgraph_edge *e, struct cgraph_node *n)
    Remove the speculative call sequence and return edge representing the call.
    It is up to caller to redirect the call as appropriate. */
 
-static struct cgraph_edge *
+struct cgraph_edge *
 cgraph_resolve_speculation (struct cgraph_edge *edge, tree callee_decl)
 {
   struct cgraph_edge *e2;
@@ -1159,12 +1162,21 @@ cgraph_resolve_speculation (struct cgraph_edge *edge, tree callee_decl)
     {
       if (dump_file)
        {
-         fprintf (dump_file, "Speculative indirect call %s/%i => %s/%i has "
-                  "turned out to have contradicitng known target ",
-                  xstrdup (cgraph_node_name (edge->caller)), edge->caller->symbol.order,
-                  xstrdup (cgraph_node_name (e2->callee)), e2->callee->symbol.order);
-         print_generic_expr (dump_file, callee_decl, 0);
-          fprintf (dump_file, "\n");
+         if (callee_decl)
+           {
+             fprintf (dump_file, "Speculative indirect call %s/%i => %s/%i has "
+                      "turned out to have contradicting known target ",
+                      xstrdup (cgraph_node_name (edge->caller)), edge->caller->symbol.order,
+                      xstrdup (cgraph_node_name (e2->callee)), e2->callee->symbol.order);
+             print_generic_expr (dump_file, callee_decl, 0);
+             fprintf (dump_file, "\n");
+           }
+         else
+           {
+             fprintf (dump_file, "Removing speculative call %s/%i => %s/%i\n",
+                      xstrdup (cgraph_node_name (edge->caller)), edge->caller->symbol.order,
+                      xstrdup (cgraph_node_name (e2->callee)), e2->callee->symbol.order);
+           }
        }
     }
   else
@@ -1264,12 +1276,24 @@ cgraph_redirect_edge_call_stmt_to_callee (struct cgraph_edge *e)
       cgraph_speculative_call_info (e, e, e2, ref);
       if (gimple_call_fndecl (e->call_stmt))
        e = cgraph_resolve_speculation (e, gimple_call_fndecl (e->call_stmt));
-      else
+      if (!gimple_check_call_matching_types (e->call_stmt, e->callee->symbol.decl,
+                                            true))
        {
+         e = cgraph_resolve_speculation (e, NULL);
          if (dump_file)
-           fprintf (dump_file, "Expanding speculative call of %s/%i -> %s/%i\n",
+           fprintf (dump_file, "Not expanding speculative call of %s/%i -> %s/%i\n"
+                    "Type mismatch.\n",
                     xstrdup (cgraph_node_name (e->caller)), e->caller->symbol.order,
                     xstrdup (cgraph_node_name (e->callee)), e->callee->symbol.order);
+       }
+      else
+       {
+         if (dump_file)
+           fprintf (dump_file, "Expanding speculative call of %s/%i -> %s/%i count:"
+                    HOST_WIDEST_INT_PRINT_DEC"\n",
+                    xstrdup (cgraph_node_name (e->caller)), e->caller->symbol.order,
+                    xstrdup (cgraph_node_name (e->callee)), e->callee->symbol.order,
+                    (HOST_WIDEST_INT)e->count);
          gcc_assert (e2->speculative);
          push_cfun (DECL_STRUCT_FUNCTION (e->caller->symbol.decl));
          new_stmt = gimple_ic (e->call_stmt, cgraph (ref->referred),
index e430533338302f366f571228f4323c8644455c2c..6c25bf283f9de1de349575111d3ab8d87d1f448b 100644 (file)
@@ -726,7 +726,7 @@ bool cgraph_propagate_frequency (struct cgraph_node *node);
 struct cgraph_node * cgraph_function_node (struct cgraph_node *,
                                           enum availability *avail = NULL);
 bool cgraph_get_body (struct cgraph_node *node);
-void
+struct cgraph_edge *
 cgraph_turn_edge_to_speculative (struct cgraph_edge *,
                                 struct cgraph_node *,
                                 gcov_type, int);
@@ -783,6 +783,7 @@ struct cgraph_node *cgraph_function_versioning (struct cgraph_node *,
                                                basic_block, const char *);
 void tree_function_versioning (tree, tree, vec<ipa_replace_map_p, va_gc> *,
                               bool, bitmap, bool, bitmap, basic_block);
+struct cgraph_edge *cgraph_resolve_speculation (struct cgraph_edge *, tree);
 
 /* In cgraphbuild.c  */
 unsigned int rebuild_cgraph_edges (void);
@@ -1398,4 +1399,16 @@ symtab_real_symbol_p (symtab_node node)
     return false;
   return true;
 }
+
+/* Return true if NODE can be discarded by linker from the binary.  */
+
+static inline bool
+symtab_can_be_discarded (symtab_node node)
+{
+  return (DECL_EXTERNAL (node->symbol.decl)
+         || (DECL_ONE_ONLY (node->symbol.decl)
+             && node->symbol.resolution != LDPR_PREVAILING_DEF
+             && node->symbol.resolution != LDPR_PREVAILING_DEF_IRONLY
+             && node->symbol.resolution != LDPR_PREVAILING_DEF_IRONLY_EXP));
+}
 #endif  /* GCC_CGRAPH_H  */
index 54b113ac0001d49d8a667e2975bff9155aeb844d..8ead336de64ef8b5ef7f10ff6baec82c877e9f8c 100644 (file)
@@ -46,6 +46,7 @@ along with GCC; see the file COPYING3.  If not see
 
 int ncalls_inlined;
 int nfunctions_inlined;
+bool speculation_removed;
 
 /* Scale frequency of NODE edges by FREQ_SCALE.  */
 
@@ -134,6 +135,7 @@ clone_inlined_nodes (struct cgraph_edge *e, bool duplicate,
                     bool update_original, int *overall_size)
 {
   struct cgraph_node *inlining_into;
+  struct cgraph_edge *next;
 
   if (e->caller->global.inlined_to)
     inlining_into = e->caller->global.inlined_to;
@@ -186,9 +188,17 @@ clone_inlined_nodes (struct cgraph_edge *e, bool duplicate,
   e->callee->global.inlined_to = inlining_into;
 
   /* Recursively clone all bodies.  */
-  for (e = e->callee->callees; e; e = e->next_callee)
-    if (!e->inline_failed)
-      clone_inlined_nodes (e, duplicate, update_original, overall_size);
+  for (e = e->callee->callees; e; e = next)
+    {
+      next = e->next_callee;
+      if (!e->inline_failed)
+        clone_inlined_nodes (e, duplicate, update_original, overall_size);
+      if (e->speculative && !speculation_useful_p (e, true))
+       {
+         cgraph_resolve_speculation (e, NULL);
+         speculation_removed = true;
+       }
+    }
 }
 
 
@@ -218,6 +228,7 @@ inline_call (struct cgraph_edge *e, bool update_original,
   bool predicated = inline_edge_summary (e)->predicate != NULL;
 #endif
 
+  speculation_removed = false;
   /* Don't inline inlined edges.  */
   gcc_assert (e->inline_failed);
   /* Don't even think of inlining inline clone.  */
@@ -267,6 +278,7 @@ inline_call (struct cgraph_edge *e, bool update_original,
      error due to INLINE_SIZE_SCALE roudoff errors.  */
   gcc_assert (!update_overall_summary || !overall_size || new_edges_found
              || abs (estimated_growth - (new_size - old_size)) <= 1
+             || speculation_removed
              /* FIXME: a hack.  Edges with false predicate are accounted
                 wrong, we should remove them from callgraph.  */
              || predicated);
index 033b314b34a33ada58d00c36fe884078bfe3f27d..a9eb1ad75a6487cb059013abd93d8e484e964488 100644 (file)
@@ -229,10 +229,13 @@ report_inline_failed_reason (struct cgraph_edge *e)
    We check whether inlining is possible at all and whether
    caller growth limits allow doing so.  
 
-   if REPORT is true, output reason to the dump file.  */
+   if REPORT is true, output reason to the dump file.  
+
+   if DISREGARD_LIMITES is true, ignore size limits.*/
 
 static bool
-can_inline_edge_p (struct cgraph_edge *e, bool report)
+can_inline_edge_p (struct cgraph_edge *e, bool report,
+                  bool disregard_limits = false)
 {
   bool inlinable = true;
   enum availability avail;
@@ -309,6 +312,7 @@ can_inline_edge_p (struct cgraph_edge *e, bool report)
     }
   /* Check if caller growth allows the inlining.  */
   else if (!DECL_DISREGARD_INLINE_LIMITS (callee->symbol.decl)
+          && !disregard_limits
           && !lookup_attribute ("flatten",
                                 DECL_ATTRIBUTES
                                   (e->caller->global.inlined_to
@@ -1400,6 +1404,79 @@ heap_edge_removal_hook (struct cgraph_edge *e, void *data)
     }
 }
 
+/* Return true if speculation of edge E seems useful.
+   If ANTICIPATE_INLINING is true, be conservative and hope that E
+   may get inlined.  */
+
+bool
+speculation_useful_p (struct cgraph_edge *e, bool anticipate_inlining)
+{
+  enum availability avail;
+  struct cgraph_node *target = cgraph_function_or_thunk_node (e->callee, &avail);
+  struct cgraph_edge *direct, *indirect;
+  struct ipa_ref *ref;
+
+  gcc_assert (e->speculative && !e->indirect_unknown_callee);
+
+  if (!cgraph_maybe_hot_edge_p (e))
+    return false;
+
+  /* See if IP optimizations found something potentially useful about the
+     function.  For now we look only for CONST/PURE flags.  Almost everything
+     else we propagate is useless.  */
+  if (avail >= AVAIL_AVAILABLE)
+    {
+      int ecf_flags = flags_from_decl_or_type (target->symbol.decl);
+      if (ecf_flags & ECF_CONST)
+        {
+          cgraph_speculative_call_info (e, direct, indirect, ref);
+         if (!(indirect->indirect_info->ecf_flags & ECF_CONST))
+           return true;
+        }
+      else if (ecf_flags & ECF_PURE)
+        {
+          cgraph_speculative_call_info (e, direct, indirect, ref);
+         if (!(indirect->indirect_info->ecf_flags & ECF_PURE))
+           return true;
+        }
+    }
+  /* If we did not managed to inline the function nor redirect
+     to an ipa-cp clone (that are seen by having local flag set),
+     it is probably pointless to inline it unless hardware is missing
+     indirect call predictor.  */
+  if (!anticipate_inlining && e->inline_failed && !target->local.local)
+    return false;
+  /* For overwritable targets there is not much to do.  */
+  if (e->inline_failed && !can_inline_edge_p (e, false, true))
+    return false;
+  /* OK, speculation seems interesting.  */
+  return true;
+}
+
+/* We know that EDGE is not going to be inlined.
+   See if we can remove speculation.  */
+
+static void
+resolve_noninline_speculation (fibheap_t edge_heap, struct cgraph_edge *edge)
+{
+  if (edge->speculative && !speculation_useful_p (edge, false))
+    {
+      struct cgraph_node *node = edge->caller;
+      struct cgraph_node *where = node->global.inlined_to
+                                 ? node->global.inlined_to : node;
+      bitmap updated_nodes = BITMAP_ALLOC (NULL);
+
+      cgraph_resolve_speculation (edge, NULL);
+      reset_node_growth_cache (where);
+      reset_edge_caches (where);
+      inline_update_overall_summary (where);
+      update_caller_keys (edge_heap, where,
+                         updated_nodes, NULL);
+      reset_node_growth_cache (where);
+      BITMAP_FREE (updated_nodes);
+    }
+}
+
 /* We use greedy algorithm for inlining of small functions:
    All inline candidates are put into prioritized heap ordered in
    increasing badness.
@@ -1478,14 +1555,19 @@ inline_small_functions (void)
   /* Populate the heeap with all edges we might inline.  */
 
   FOR_EACH_DEFINED_FUNCTION (node)
-    if (!node->global.inlined_to)
-      {
-       if (dump_file)
-         fprintf (dump_file, "Enqueueing calls of %s/%i.\n",
-                  cgraph_node_name (node), node->symbol.order);
+    {
+      bool update = false;
+      struct cgraph_edge *next;
 
-       for (edge = node->callers; edge; edge = edge->next_caller)
+      if (dump_file)
+       fprintf (dump_file, "Enqueueing calls in %s/%i.\n",
+                cgraph_node_name (node), node->symbol.order);
+
+      for (edge = node->callees; edge; edge = next)
+       {
+         next = edge->next_callee;
          if (edge->inline_failed
+             && !edge->aux
              && can_inline_edge_p (edge, true)
              && want_inline_small_function_p (edge, true)
              && edge->inline_failed)
@@ -1493,7 +1575,24 @@ inline_small_functions (void)
              gcc_assert (!edge->aux);
              update_edge_key (edge_heap, edge);
            }
-      }
+         if (edge->speculative && !speculation_useful_p (edge, edge->aux != NULL))
+           {
+             cgraph_resolve_speculation (edge, NULL);
+             update = true;
+           }
+       }
+      if (update)
+       {
+         struct cgraph_node *where = node->global.inlined_to
+                                     ? node->global.inlined_to : node;
+         inline_update_overall_summary (where);
+          reset_node_growth_cache (where);
+         reset_edge_caches (where);
+          update_caller_keys (edge_heap, where,
+                             updated_nodes, NULL);
+          bitmap_clear (updated_nodes);
+       }
+    }
 
   gcc_assert (in_lto_p
              || !max_count
@@ -1534,7 +1633,10 @@ inline_small_functions (void)
        }
 
       if (!can_inline_edge_p (edge, true))
-       continue;
+       {
+         resolve_noninline_speculation (edge_heap, edge);
+         continue;
+       }
       
       callee = cgraph_function_or_thunk_node (edge->callee, NULL);
       growth = estimate_edge_growth (edge);
@@ -1568,11 +1670,15 @@ inline_small_functions (void)
        {
          edge->inline_failed = CIF_INLINE_UNIT_GROWTH_LIMIT;
          report_inline_failed_reason (edge);
+         resolve_noninline_speculation (edge_heap, edge);
          continue;
        }
 
       if (!want_inline_small_function_p (edge, true))
-       continue;
+       {
+         resolve_noninline_speculation (edge_heap, edge);
+         continue;
+       }
 
       /* Heuristics for inlining small functions works poorly for
         recursive calls where we do efect similar to loop unrolling.
@@ -1588,6 +1694,7 @@ inline_small_functions (void)
                                   ? &new_indirect_edges : NULL))
            {
              edge->inline_failed = CIF_RECURSIVE_INLINING;
+             resolve_noninline_speculation (edge_heap, edge);
              continue;
            }
          reset_edge_caches (where);
@@ -1596,6 +1703,7 @@ inline_small_functions (void)
          if (flag_indirect_inlining)
            add_new_edges_to_heap (edge_heap, new_indirect_edges);
           update_callee_keys (edge_heap, where, updated_nodes);
+         bitmap_clear (updated_nodes);
        }
       else
        {
@@ -1621,6 +1729,7 @@ inline_small_functions (void)
              edge->inline_failed
                = (DECL_DISREGARD_INLINE_LIMITS (edge->callee->symbol.decl)
                   ? CIF_RECURSIVE_INLINING : CIF_UNSPECIFIED);
+             resolve_noninline_speculation (edge_heap, edge);
              continue;
            }
          else if (depth && dump_file)
@@ -1773,6 +1882,7 @@ ipa_inline (void)
   struct cgraph_node **order =
     XCNEWVEC (struct cgraph_node *, cgraph_n_nodes);
   int i;
+  int cold;
 
   if (in_lto_p && optimize)
     ipa_update_after_lto_read ();
@@ -1820,66 +1930,83 @@ ipa_inline (void)
      code size will shrink because the out-of-line copy is eliminated. 
      We do this regardless on the callee size as long as function growth limits
      are met.  */
-  if (flag_inline_functions_called_once)
+  if (dump_file)
+    fprintf (dump_file,
+            "\nDeciding on functions to be inlined into all callers and removing useless speculations:\n");
+
+  /* Inlining one function called once has good chance of preventing
+     inlining other function into the same callee.  Ideally we should
+     work in priority order, but probably inlining hot functions first
+     is good cut without the extra pain of maintaining the queue.
+
+     ??? this is not really fitting the bill perfectly: inlining function
+     into callee often leads to better optimization of callee due to
+     increased context for optimization.
+     For example if main() function calls a function that outputs help
+     and then function that does the main optmization, we should inline
+     the second with priority even if both calls are cold by themselves.
+
+     We probably want to implement new predicate replacing our use of
+     maybe_hot_edge interpreted as maybe_hot_edge || callee is known
+     to be hot.  */
+  for (cold = 0; cold <= 1; cold ++)
     {
-      int cold;
-      if (dump_file)
-       fprintf (dump_file,
-                "\nDeciding on functions to be inlined into all callers:\n");
-
-      /* Inlining one function called once has good chance of preventing
-        inlining other function into the same callee.  Ideally we should
-        work in priority order, but probably inlining hot functions first
-        is good cut without the extra pain of maintaining the queue.
-
-        ??? this is not really fitting the bill perfectly: inlining function
-        into callee often leads to better optimization of callee due to
-        increased context for optimization.
-        For example if main() function calls a function that outputs help
-        and then function that does the main optmization, we should inline
-        the second with priority even if both calls are cold by themselves.
-
-        We probably want to implement new predicate replacing our use of
-        maybe_hot_edge interpreted as maybe_hot_edge || callee is known
-        to be hot.  */
-      for (cold = 0; cold <= 1; cold ++)
+      FOR_EACH_DEFINED_FUNCTION (node)
        {
-         FOR_EACH_DEFINED_FUNCTION (node)
+         struct cgraph_edge *edge, *next;
+         bool update=false;
+
+         for (edge = node->callees; edge; edge = next)
            {
-             if (want_inline_function_to_all_callers_p (node, cold))
+             next = edge->next_callee;
+             if (edge->speculative && !speculation_useful_p (edge, false))
                {
-                 int num_calls = 0;
-                 struct cgraph_edge *e;
-                 for (e = node->callers; e; e = e->next_caller)
-                   num_calls++;
-                 while (node->callers && !node->global.inlined_to)
+                 cgraph_resolve_speculation (edge, NULL);
+                 update = true;
+               }
+           }
+         if (update)
+           {
+             struct cgraph_node *where = node->global.inlined_to
+                                         ? node->global.inlined_to : node;
+              reset_node_growth_cache (where);
+             reset_edge_caches (where);
+             inline_update_overall_summary (where);
+           }
+         if (flag_inline_functions_called_once
+             && want_inline_function_to_all_callers_p (node, cold))
+           {
+             int num_calls = 0;
+             struct cgraph_edge *e;
+             for (e = node->callers; e; e = e->next_caller)
+               num_calls++;
+             while (node->callers && !node->global.inlined_to)
+               {
+                 struct cgraph_node *caller = node->callers->caller;
+
+                 if (dump_file)
                    {
-                     struct cgraph_node *caller = node->callers->caller;
+                     fprintf (dump_file,
+                              "\nInlining %s size %i.\n",
+                              cgraph_node_name (node),
+                              inline_summary (node)->size);
+                     fprintf (dump_file,
+                              " Called once from %s %i insns.\n",
+                              cgraph_node_name (node->callers->caller),
+                              inline_summary (node->callers->caller)->size);
+                   }
 
+                 inline_call (node->callers, true, NULL, NULL, true);
+                 if (dump_file)
+                   fprintf (dump_file,
+                            " Inlined into %s which now has %i size\n",
+                            cgraph_node_name (caller),
+                            inline_summary (caller)->size);
+                 if (!num_calls--)
+                   {
                      if (dump_file)
-                       {
-                         fprintf (dump_file,
-                                  "\nInlining %s size %i.\n",
-                                  cgraph_node_name (node),
-                                  inline_summary (node)->size);
-                         fprintf (dump_file,
-                                  " Called once from %s %i insns.\n",
-                                  cgraph_node_name (node->callers->caller),
-                                  inline_summary (node->callers->caller)->size);
-                       }
-
-                     inline_call (node->callers, true, NULL, NULL, true);
-                     if (dump_file)
-                       fprintf (dump_file,
-                                " Inlined into %s which now has %i size\n",
-                                cgraph_node_name (caller),
-                                inline_summary (caller)->size);
-                     if (!num_calls--)
-                       {
-                         if (dump_file)
-                           fprintf (dump_file, "New calls found; giving up.\n");
-                         break;
-                       }
+                       fprintf (dump_file, "New calls found; giving up.\n");
+                     break;
                    }
                }
            }
index b34cb526baded23504924438bc9cdae7d9a94a37..000d1abc968982b7a1574fe625d97721d897e4c8 100644 (file)
@@ -226,6 +226,7 @@ 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);
+bool speculation_useful_p (struct cgraph_edge *e, bool anticipate_inlining);
 
 /* In ipa-inline-transform.c  */
 bool inline_call (struct cgraph_edge *, bool, vec<cgraph_edge_p> *, int *, bool);
index c870a6f067849073b7819a232924c482e2b36e9f..1578aed70e8ae71d24f167b669e2f2a1ed664cd3 100644 (file)
--- a/gcc/ipa.c
+++ b/gcc/ipa.c
@@ -768,11 +768,7 @@ bool
 can_replace_by_local_alias (symtab_node node)
 {
   return (symtab_node_availability (node) > AVAIL_OVERWRITABLE
-         && !DECL_EXTERNAL (node->symbol.decl)
-         && (!DECL_ONE_ONLY (node->symbol.decl)
-             || node->symbol.resolution == LDPR_PREVAILING_DEF
-             || node->symbol.resolution == LDPR_PREVAILING_DEF_IRONLY
-             || node->symbol.resolution == LDPR_PREVAILING_DEF_IRONLY_EXP));
+         && !symtab_can_be_discarded (node));
 }
 
 /* Mark visibility of all functions.
@@ -1407,53 +1403,9 @@ ipa_profile (void)
   bool something_changed = false;
   int i;
   gcov_type overall_time = 0, cutoff = 0, cumulated = 0, overall_size = 0;
-
-  /* Produce speculative calls: we saved common traget from porfiling into
-     e->common_target_id.  Now, at link time, we can look up corresponding
-     function node and produce speculative call.  */
-  if (in_lto_p)
-    {
-      struct cgraph_edge *e;
-      struct cgraph_node *n,*n2;
-
-      init_node_map (false);
-      FOR_EACH_DEFINED_FUNCTION (n)
-       {
-         bool update = false;
-
-         for (e = n->indirect_calls; e; e = e->next_callee)
-           if (e->indirect_info->common_target_id)
-             {
-               n2 = find_func_by_profile_id (e->indirect_info->common_target_id);
-               if (n2)
-                 {
-                   if (dump_file)
-                     {
-                       fprintf (dump_file, "Indirect call -> direct call from"
-                                " other module %s/%i => %s/%i, prob %3.2f\n",
-                                xstrdup (cgraph_node_name (n)), n->symbol.order,
-                                xstrdup (cgraph_node_name (n2)), n2->symbol.order,
-                                e->indirect_info->common_target_probability
-                                / (float)REG_BR_PROB_BASE);
-                     }
-                   cgraph_turn_edge_to_speculative
-                     (e, n2,
-                      apply_scale (e->count,
-                                   e->indirect_info->common_target_probability),
-                      apply_scale (e->frequency,
-                                   e->indirect_info->common_target_probability));
-                   update = true;
-                 }
-               else
-                 if (dump_file)
-                   fprintf (dump_file, "Function with profile-id %i not found.\n",
-                            e->indirect_info->common_target_id);
-              }
-            if (update)
-              inline_update_overall_summary (n);
-          }
-       del_node_map ();
-    }
+  struct cgraph_node *n,*n2;
+  int nindirect = 0, ncommon = 0, nunknown = 0, nuseless = 0, nconverted = 0;
+  bool node_map_initialized = false;
 
   if (dump_file)
     dump_histogram (dump_file, histogram);
@@ -1523,6 +1475,106 @@ ipa_profile (void)
   histogram.release();
   free_alloc_pool (histogram_pool);
 
+  /* Produce speculative calls: we saved common traget from porfiling into
+     e->common_target_id.  Now, at link time, we can look up corresponding
+     function node and produce speculative call.  */
+
+  FOR_EACH_DEFINED_FUNCTION (n)
+    {
+      bool update = false;
+
+      for (e = n->indirect_calls; e; e = e->next_callee)
+       {
+         if (n->count)
+           nindirect++;
+         if (e->indirect_info->common_target_id)
+           {
+             if (!node_map_initialized)
+               init_node_map (false);
+             node_map_initialized = true;
+             ncommon++;
+             n2 = find_func_by_profile_id (e->indirect_info->common_target_id);
+             if (n2)
+               {
+                 if (dump_file)
+                   {
+                     fprintf (dump_file, "Indirect call -> direct call from"
+                              " other module %s/%i => %s/%i, prob %3.2f\n",
+                              xstrdup (cgraph_node_name (n)), n->symbol.order,
+                              xstrdup (cgraph_node_name (n2)), n2->symbol.order,
+                              e->indirect_info->common_target_probability
+                              / (float)REG_BR_PROB_BASE);
+                   }
+                 if (e->indirect_info->common_target_probability
+                     < REG_BR_PROB_BASE / 2)
+                   {
+                     nuseless++;
+                     if (dump_file)
+                       fprintf (dump_file,
+                                "Not speculating: probability is too low.\n");
+                   }
+                 else if (!cgraph_maybe_hot_edge_p (e))
+                   {
+                     nuseless++;
+                     if (dump_file)
+                       fprintf (dump_file,
+                                "Not speculating: call is cold.\n");
+                   }
+                 else if (cgraph_function_body_availability (n2)
+                          <= AVAIL_OVERWRITABLE
+                          && symtab_can_be_discarded ((symtab_node) n2))
+                   {
+                     nuseless++;
+                     if (dump_file)
+                       fprintf (dump_file,
+                                "Not speculating: target is overwritable "
+                                "and can be discarded.\n");
+                   }
+                 else
+                   {
+                     /* Target may be overwritable, but profile says that
+                        control flow goes to this particular implementation
+                        of N2.  Speculate on the local alias to allow inlining.
+                      */
+                     if (!symtab_can_be_discarded ((symtab_node) n2))
+                       n2 = cgraph (symtab_nonoverwritable_alias ((symtab_node)n2));
+                     nconverted++;
+                     cgraph_turn_edge_to_speculative
+                       (e, n2,
+                        apply_scale (e->count,
+                                     e->indirect_info->common_target_probability),
+                        apply_scale (e->frequency,
+                                     e->indirect_info->common_target_probability));
+                     update = true;
+                   }
+               }
+             else
+               {
+                 if (dump_file)
+                   fprintf (dump_file, "Function with profile-id %i not found.\n",
+                            e->indirect_info->common_target_id);
+                 nunknown++;
+               }
+           }
+        }
+       if (update)
+        inline_update_overall_summary (n);
+     }
+  if (node_map_initialized)
+    del_node_map ();
+  if (dump_file && nindirect)
+    fprintf (dump_file,
+            "%i indirect calls trained.\n"
+            "%i (%3.2f%%) have common target.\n"
+            "%i (%3.2f%%) targets was not found.\n"
+            "%i (%3.2f%%) speculations seems useless.\n"
+            "%i (%3.2f%%) speculations produced.\n",
+            nindirect,
+            ncommon, ncommon * 100.0 / nindirect,
+            nunknown, nunknown * 100.0 / nindirect,
+            nuseless, nuseless * 100.0 / nindirect,
+            nconverted, nconverted * 100.0 / nindirect);
+
   order_pos = ipa_reverse_postorder (order);
   for (i = order_pos - 1; i >= 0; i--)
     {
index f110277f22d9b0083df9323880d1eb314ec57f7a..8aa9fcda905f5c5580c552d0849cd821118c0031 100644 (file)
@@ -1431,8 +1431,6 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
   gimple stmt = gsi_stmt (*gsi);
   histogram_value histogram;
   gcov_type val, count, all, bb_all;
-  gcov_type prob;
-  gimple modify;
   struct cgraph_node *direct_call;
 
   if (gimple_code (stmt) != GIMPLE_CALL)
@@ -1452,12 +1450,6 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
   count = histogram->hvalue.counters [1];
   all = histogram->hvalue.counters [2];
 
-  if (4 * count <= 3 * all)
-    {
-      gimple_remove_histogram_value (cfun, stmt, histogram);
-      return false;
-    }
-
   bb_all = gimple_bb (stmt)->count;
   /* The order of CHECK_COUNTER calls is important -
      since check_counter can correct the third parameter
@@ -1469,10 +1461,9 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
       return false;
     }
 
-  if (all > 0)
-    prob = GCOV_COMPUTE_SCALE (count, all);
-  else
-    prob = 0;
+  if (4 * count <= 3 * all)
+    return false;
+
   direct_call = find_func_by_profile_id ((int)val);
 
   if (direct_call == NULL)
@@ -1488,12 +1479,21 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
        }
       return false;
     }
-  gimple_remove_histogram_value (cfun, stmt, histogram);
 
   if (!check_ic_target (stmt, direct_call))
-    return false;
-
-  modify = gimple_ic (stmt, direct_call, prob, count, all);
+    {
+      if (dump_file)
+       {
+         fprintf (dump_file, "Indirect call -> direct call ");
+         print_generic_expr (dump_file, gimple_call_fn (stmt), TDF_SLIM);
+         fprintf (dump_file, "=> ");
+         print_generic_expr (dump_file, direct_call->symbol.decl, TDF_SLIM);
+         fprintf (dump_file, " transformation skipped because of type mismatch");
+         print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
+       }
+      gimple_remove_histogram_value (cfun, stmt, histogram);
+      return false;
+    }
 
   if (dump_file)
     {
@@ -1501,10 +1501,8 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
       print_generic_expr (dump_file, gimple_call_fn (stmt), TDF_SLIM);
       fprintf (dump_file, "=> ");
       print_generic_expr (dump_file, direct_call->symbol.decl, TDF_SLIM);
-      fprintf (dump_file, " transformation on insn ");
+      fprintf (dump_file, " transformation on insn postponned to ipa-profile");
       print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
-      fprintf (dump_file, " to ");
-      print_gimple_stmt (dump_file, modify, 0, TDF_SLIM);
       fprintf (dump_file, "hist->count "HOST_WIDEST_INT_PRINT_DEC
               " hist->all "HOST_WIDEST_INT_PRINT_DEC"\n", count, all);
     }