Hi,
This patch makes inliner to not subtract inlined function profile from the
offline copy in cases where profile is clearly not consistent.  As a result we
do not drop the offline version to likely never executed profile.  This helps
in cases the profile got lost, i.e. by comdat function merging and also for
auto-fdo.

Bootstrapped/regtested x86-64-linux, comitted.
gcc/ChangeLog:

        * ipa-inline-transform.cc (clone_inlined_nodes): Add KEEP_OFFLINE_COPY
        parameter.
        (inline_call): Sanity check profile and if it is clearly broken do
        not subtract profile from original function.
        * ipa-inline.cc (recursive_inlining): Update.
        * ipa-inline.h (clone_inlined_nodes): Update.

diff --git a/gcc/ipa-inline-transform.cc b/gcc/ipa-inline-transform.cc
index da9c9076e5f..a2048549665 100644
--- a/gcc/ipa-inline-transform.cc
+++ b/gcc/ipa-inline-transform.cc
@@ -142,12 +142,14 @@ master_clone_with_noninline_clones_p (struct cgraph_node 
*node)
    DUPLICATE is used for bookkeeping on whether we are actually creating new
    clones or re-using node originally representing out-of-line function call.
    By default the offline copy is removed, when it appears dead after inlining.
-   UPDATE_ORIGINAL prevents this transformation.
+   KEEP_OFFLINE_COPY prevents this transformation.
+   If UPDATE_ORIGINAL is set, clones profile is subtracted from the offline 
version.
    If OVERALL_SIZE is non-NULL, the size is updated to reflect the
    transformation.  */
 
 void
 clone_inlined_nodes (struct cgraph_edge *e, bool duplicate,
+                    bool keep_offline_copy,
                     bool update_original, int *overall_size)
 {
   struct cgraph_node *inlining_into;
@@ -167,7 +169,7 @@ clone_inlined_nodes (struct cgraph_edge *e, bool duplicate,
       if (!e->callee->callers->next_caller
          /* Recursive inlining never wants the master clone to
             be overwritten.  */
-         && update_original
+         && !keep_offline_copy
          && can_remove_node_now_p (e->callee, e)
          /* We cannot overwrite a master clone with non-inline clones
             until after these clones are materialized.  */
@@ -228,7 +230,8 @@ clone_inlined_nodes (struct cgraph_edge *e, bool duplicate,
     {
       next = e->next_callee;
       if (!e->inline_failed)
-        clone_inlined_nodes (e, duplicate, update_original, overall_size);
+        clone_inlined_nodes (e, duplicate, keep_offline_copy,
+                            update_original, overall_size);
     }
 }
 
@@ -306,7 +309,8 @@ mark_all_inlined_calls_cdtor (cgraph_node *node)
 
 
 /* Mark edge E as inlined and update callgraph accordingly.  UPDATE_ORIGINAL
-   specify whether profile of original function should be updated.  If any new
+   specify whether profile of original function should be updated and whether
+   offline copy should be removed if unnecesary.  If any new
    indirect edges are discovered in the process, add them to NEW_EDGES, unless
    it is NULL. If UPDATE_OVERALL_SUMMARY is false, do not bother to recompute 
overall
    size of caller after inlining. Caller is required to eventually do it via
@@ -328,6 +332,7 @@ inline_call (struct cgraph_edge *e, bool update_original,
   bool comdat_local = e->callee->comdat_local_p ();
   struct cgraph_node *callee = e->callee->ultimate_alias_target ();
   bool new_edges_found = false;
+  bool keep_offline_copy = !update_original;
 
   int estimated_growth = 0;
   if (! update_overall_summary)
@@ -379,6 +384,29 @@ inline_call (struct cgraph_edge *e, bool update_original,
          fprintf (dump_file, "\n");
        }
     }
+  /* Do sanity checking of the profile and in case of inconsistencies do not
+     update profile of original.  This reduces the chances that inlining
+     turns callee cold while in reality it is still hot.  */
+  if (!(callee->count.ipa ().force_nonzero () == callee->count.ipa ()))
+    {
+      if (dump_file)
+       fprintf (dump_file, "Callee count is 0; not updating callee profile\n");
+      update_original = false;
+    }
+  else if (e->count.ipa ().quality () == AFDO
+          && !(e->count.ipa ().force_nonzero () == e->count.ipa ()))
+    {
+      if (dump_file)
+       fprintf (dump_file, "Edge count is AFDO 0; not updating callee 
profile\n");
+      update_original = false;
+    }
+  if (e->count.ipa () > callee->count.ipa ().apply_scale (9, 8))
+    {
+      if (dump_file)
+       fprintf (dump_file, "Calee count is too small (profile is 
inconsistent);"
+                " not updating callee profile\n");
+      update_original = false;
+    }
   if (to->thunk)
     {
       struct cgraph_node *target = to->callees->callee;
@@ -530,7 +558,8 @@ inline_call (struct cgraph_edge *e, bool update_original,
        }
     }
 
-  clone_inlined_nodes (e, true, update_original, overall_size);
+  clone_inlined_nodes (e, true, keep_offline_copy,
+                      update_original, overall_size);
 
   gcc_assert (curr->callee->inlined_to == to);
 
diff --git a/gcc/ipa-inline.cc b/gcc/ipa-inline.cc
index 0cf97a80687..b71ebbe60fd 100644
--- a/gcc/ipa-inline.cc
+++ b/gcc/ipa-inline.cc
@@ -1860,7 +1860,7 @@ recursive_inlining (struct cgraph_edge *edge,
            false, vNULL, true, NULL, NULL, NULL);
          for (e = master_clone->callees; e; e = e->next_callee)
            if (!e->inline_failed)
-             clone_inlined_nodes (e, true, false, NULL);
+             clone_inlined_nodes (e, true, true, false, NULL);
          curr->redirect_callee (master_clone);
          if (edge_growth_cache != NULL)
            edge_growth_cache->remove (curr);
diff --git a/gcc/ipa-inline.h b/gcc/ipa-inline.h
index 8940cb90102..7d2f881e0ff 100644
--- a/gcc/ipa-inline.h
+++ b/gcc/ipa-inline.h
@@ -61,7 +61,7 @@ bool inline_account_function_p (struct cgraph_node *node);
 bool inline_call (struct cgraph_edge *, bool, vec<cgraph_edge *> *, int *, 
bool,
                  bool *callee_removed = NULL);
 unsigned int inline_transform (struct cgraph_node *);
-void clone_inlined_nodes (struct cgraph_edge *e, bool, bool, int *);
+void clone_inlined_nodes (struct cgraph_edge *e, bool, bool, bool, int *);
 
 extern int ncalls_inlined;
 extern int nfunctions_inlined;

Reply via email to