https://gcc.gnu.org/g:ebd81e6e396d3d50e4ac89404e45f5d2029a186d

commit r17-5258-gebd81e6e396d3d50e4ac89404e45f5d2029a186d
Author: Hugo Sales <[email protected]>
Date:   Fri Oct 16 11:50:48 2026 +0100

    ipa-cp: Check polymorphic contexts before redirecting to a clone [PR127917]
    
    perhaps_add_new_callers redirects a call to an existing specialized
    clone when the call passes the value the clone was made for, and all
    the scalar and aggregate constants the clone assumes.  It does not
    compare polymorphic contexts.
    
    A clone made for one parameter can also assume a dynamic type for
    another parameter, recorded in its known_contexts, and IPA-CP
    devirtualizes the calls in the clone's body for that type.  A call that
    passes a different dynamic type must not be redirected to it, or the
    program runs the wrong virtual method.
    
    Since IPA-CP makes several decision sweeps, this happens with
    self-recursive functions.  In the new test, the first sweeps clone
    Tree::Select (int, Selector &) for a context of THIS, and that clone
    assumes S is a Bad.  A later sweep makes another clone, in which S is
    a plain Selector.  The recursive call in the new clone passes the
    THIS context the old clone was made for, and there are no scalars or
    aggregates to compare, so IPA-CP redirects it to the old clone.  That
    clone calls Bad::Accept directly, and the test aborts.
    
    This patch adds cgraph_edge_brings_all_contexts_for_node, which does
    for polymorphic contexts what cgraph_edge_brings_all_scalars_for_node
    does for scalars, and requires it in perhaps_add_new_callers.  As in
    ipcp_val_replacement_ok_p, a context more precise than the one the
    clone was specialized for is still accepted.
    
    gcc/ChangeLog:
    
            PR ipa/127917
            * ipa-cp.cc (cgraph_edge_brings_all_contexts_for_node): New 
function.
            (perhaps_add_new_callers): Use it.
    
    gcc/testsuite/ChangeLog:
    
            PR ipa/127917
            * g++.dg/ipa/pr127917.C: New test.
    
    Signed-off-by: Hugo Sales <[email protected]>

Diff:
---
 gcc/ipa-cp.cc                       | 41 ++++++++++++++++++++++++++++++++-
 gcc/testsuite/g++.dg/ipa/pr127917.C | 45 +++++++++++++++++++++++++++++++++++++
 2 files changed, 85 insertions(+), 1 deletion(-)

diff --git a/gcc/ipa-cp.cc b/gcc/ipa-cp.cc
index b35e07a49d4a..4ae6c7516ef6 100644
--- a/gcc/ipa-cp.cc
+++ b/gcc/ipa-cp.cc
@@ -5833,6 +5833,44 @@ cgraph_edge_brings_all_agg_vals_for_node (struct 
cgraph_edge *cs,
   return avl.superset_of_p (existing);
 }
 
+/* Determine whether CS also brings all polymorphic contexts that NODE is
+   specialized for.  */
+
+static bool
+cgraph_edge_brings_all_contexts_for_node (struct cgraph_edge *cs,
+                                         struct cgraph_node *node)
+{
+  ipa_node_params *dest_info = ipa_node_params_sum->get (node);
+  unsigned count = dest_info->known_contexts.length ();
+  if (!count)
+    return true;
+
+  ipa_node_params *caller_info = ipa_node_params_sum->get (cs->caller);
+  ipa_edge_args *args = ipa_edge_args_sum->get (cs);
+  for (unsigned i = 0; i < count; i++)
+    {
+      ipa_polymorphic_call_context ctx = dest_info->known_contexts[i];
+      if (ctx.useless_p ())
+       continue;
+
+      if (!args || i >= (unsigned) ipa_get_cs_argument_count (args))
+       return false;
+      ipa_jump_func *jump_func = ipa_get_ith_jump_func (args, i);
+      ipa_polymorphic_call_context edge_ctx
+       = ipa_context_from_jfunc (caller_info, cs, i, jump_func);
+      if (edge_ctx.useless_p ())
+       return false;
+      if (edge_ctx.equal_to (ctx))
+       continue;
+      /* Also accept a context that is more precise than the one NODE has been
+        specialized for.  */
+      ctx.combine_with (edge_ctx);
+      if (!edge_ctx.equal_to (ctx))
+       return false;
+    }
+  return true;
+}
+
 /* Given an original NODE and a VAL for which we have already created a
    specialized clone, look whether there are incoming edges that still lead
    into the old node but now also bring the requested value and also conform to
@@ -5853,7 +5891,8 @@ perhaps_add_new_callers (cgraph_node *node, 
ipcp_value<valtype> *val)
        {
          if (cgraph_edge_brings_value_p (cs, src, node, val)
              && cgraph_edge_brings_all_scalars_for_node (cs, val->spec_node)
-             && cgraph_edge_brings_all_agg_vals_for_node (cs, val->spec_node))
+             && cgraph_edge_brings_all_agg_vals_for_node (cs, val->spec_node)
+             && cgraph_edge_brings_all_contexts_for_node (cs, val->spec_node))
            {
              if (dump_file)
                fprintf (dump_file, " - adding an extra caller %s of %s\n",
diff --git a/gcc/testsuite/g++.dg/ipa/pr127917.C 
b/gcc/testsuite/g++.dg/ipa/pr127917.C
new file mode 100644
index 000000000000..dfa1517f5e92
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ipa/pr127917.C
@@ -0,0 +1,45 @@
+// PR ipa/127917
+// { dg-do run }
+// { dg-options "-O3" }
+
+struct Selector {
+  virtual void Accept () {}
+};
+
+struct Tree {
+  Tree () : depth (2) {}
+  virtual int Select (Selector &s) { Select (depth, s); return 1; }
+  void Select (int d, Selector &s)
+  {
+    if (d)
+      {
+       Select (d - 1, s);
+       Select (d - 1, s);
+      }
+    else
+      s.Accept ();
+  }
+  int depth;
+};
+
+struct Bad : Selector {
+  void Accept () { __builtin_abort (); }
+};
+
+__attribute__((noipa)) void
+perform (Tree &t)
+{
+  Selector ok;
+  if (t.depth != 1 && t.Select (ok))
+    return;
+  Bad bad;
+  t.Select (bad);
+  t.Select (bad);
+}
+
+int
+main ()
+{
+  Tree t;
+  perform (t);
+}

Reply via email to