]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
Improve ipa-cp devirtualization costing
authorJan Hubicka <jh@suse.cz>
Fri, 19 Sep 2025 17:05:34 +0000 (19:05 +0200)
committerJan Hubicka <jh@suse.cz>
Fri, 19 Sep 2025 17:05:34 +0000 (19:05 +0200)
This patch fixed devirtualization time benefit of ipa-cp which should be scaled
by edge frequency but it is not.  The cost model is still not correct for speculative
calls, since it does not take into account the fact that code size will shrink if
speculation is removed.
I also made cgraph_edge::make_direct to not ICE when there are multiple speculations
to same target. This can happen in combination of devirtualization and ICF in some
rare cases.

gcc/ChangeLog:

* cgraph.cc (cgraph_edge::make_direct): Do not ICE when there are
multiple speculations to comptaible targets
(cgraph_edge::combined_sreal_frequency): New member function.
* cgraph.h (cgraph_edge::combined_sreal_frequency): Declare.
* ipa-cp.cc (devirtualization_time_bonus): Return sreal; consider
profile.
(estimate_local_effects): Likewise.

gcc/cgraph.cc
gcc/cgraph.h
gcc/ipa-cp.cc

index 32071a84bacce916dd12646314da98a57e4973ba..e5902a0fdcd0fbbe08911f368b15c4c03827b5bf 100644 (file)
@@ -1466,13 +1466,11 @@ cgraph_edge::make_direct (cgraph_edge *edge, cgraph_node *callee)
          /* Compare ref not direct->callee.  Direct edge is possibly
             inlined or redirected.  */
          if (!direct->speculative_call_target_ref ()
-              ->referred->semantically_equivalent_p (callee))
+              ->referred->semantically_equivalent_p (callee)
+             || found)
            edge = direct->resolve_speculation (direct, NULL);
          else
-           {
-             gcc_checking_assert (!found);
-             found = direct;
-           }
+           found = direct;
        }
 
       /* On successful speculation just remove the indirect edge and
@@ -4422,6 +4420,25 @@ cgraph_edge::sreal_frequency ()
                               : caller->count);
 }
 
+/* Expected frequency of executions within the function.
+   If edge is speculative, sum all its indirect targets.  */
+
+sreal
+cgraph_edge::combined_sreal_frequency ()
+{
+  if (!speculative)
+    return sreal_frequency ();
+  cgraph_edge *e = this;
+  if (e->callee)
+    e = e->speculative_call_indirect_edge ();
+  sreal sum = e->sreal_frequency ();
+  for (e = e->first_speculative_call_target ();
+       e;
+       e = e->next_speculative_call_target ())
+    sum += e->sreal_frequency ();
+  return sum;
+}
+
 
 /* During LTO stream in this can be used to check whether call can possibly
    be internal to the current translation unit.  */
index deca564a8e37b9feef018d21a63c3a73647bda51..d668a8182729a985a5aa7f4955ed9f24902b5665 100644 (file)
@@ -1991,6 +1991,10 @@ public:
 
   /* Expected frequency of executions within the function.  */
   sreal sreal_frequency ();
+
+  /* Expected frequency of executions within the function.
+     If edge is speculative, sum all its indirect targets.  */
+  sreal combined_sreal_frequency ();
 private:
   /* Unique id of the edge.  */
   int m_uid;
index 4e03c36610676ff2cd9e7ac75bddc1c3ebe0a6ac..f1623c147f47c8d7266c9ca8b31143b44ba24799 100644 (file)
@@ -3289,14 +3289,17 @@ ipa_get_indirect_edge_target (struct cgraph_edge *ie,
 }
 
 /* Calculate devirtualization time bonus for NODE, assuming we know information
-   about arguments stored in AVALS.  */
+   about arguments stored in AVALS.
 
-static int
+   FIXME: This function will also consider devirtualization of calls that are
+   known to be dead in the clone.  */
+
+static sreal
 devirtualization_time_bonus (struct cgraph_node *node,
                             ipa_auto_call_arg_values *avals)
 {
   struct cgraph_edge *ie;
-  int res = 0;
+  sreal res = 0;
 
   for (ie = node->indirect_calls; ie; ie = ie->next_callee)
     {
@@ -3314,7 +3317,7 @@ devirtualization_time_bonus (struct cgraph_node *node,
        continue;
 
       /* Only bare minimum benefit for clearly un-inlineable targets.  */
-      res += 1;
+      int savings = 1;
       callee = cgraph_node::get (target);
       if (!callee || !callee->definition)
        continue;
@@ -3331,12 +3334,13 @@ devirtualization_time_bonus (struct cgraph_node *node,
       int max_inline_insns_auto
        = opt_for_fn (callee->decl, param_max_inline_insns_auto);
       if (size <= max_inline_insns_auto / 4)
-       res += 31 / ((int)speculative + 1);
+       savings += 31 / ((int)speculative + 1);
       else if (size <= max_inline_insns_auto / 2)
-       res += 15 / ((int)speculative + 1);
+       savings += 15 / ((int)speculative + 1);
       else if (size <= max_inline_insns_auto
               || DECL_DECLARED_INLINE_P (callee->decl))
-       res += 7 / ((int)speculative + 1);
+       savings += 7 / ((int)speculative + 1);
+      res = res + ie->combined_sreal_frequency () * (sreal) savings;
     }
 
   return res;
@@ -3624,8 +3628,8 @@ estimate_local_effects (struct cgraph_node *node)
   ipa_auto_call_arg_values avals;
   always_const = gather_context_independent_values (info, &avals, true,
                                                    &removable_params_cost);
-  int devirt_bonus = devirtualization_time_bonus (node, &avals);
-  if (always_const || devirt_bonus
+  sreal devirt_bonus = devirtualization_time_bonus (node, &avals);
+  if (always_const || devirt_bonus > 0
       || (removable_params_cost && clone_for_param_removal_p (node)))
     {
       struct caller_statistics stats;