]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
ipa/124700: Fix rebuild_references for callback functions
authorJosef Melcr <josef.melcr@suse.com>
Mon, 13 Apr 2026 07:44:34 +0000 (09:44 +0200)
committerJosef Melcr <josef.melcr@suse.com>
Mon, 13 Apr 2026 08:01:59 +0000 (10:01 +0200)
This patch deals with the following situation:

template<typename T>
void foo () {
  GOMP_parallel (foo._omp_fn, ...);
}

Before callback edges were implemented, if foo got put into a COMDAT
group and subsequently virtually cloned (by ipa-cp, for example), then
the OpenMP kernel wouldn't be put into the same COMDAT group, since it
would be referred to by functions both inside and outside said COMDAT
group.

With callback edges, the kernel may be cloned as well.  If both foo and
foo._omp_fn get cloned, foo's clone will be redirected to the cloned
kernel.  This allows us to put foo._omp_fn into the same COMDAT group as
foo via ipa-comdat.  As the kernel is artificial, it becomes
comdat-local.

However, this redirection is not immediate, leading to the situation in
the PR, where the clones are properly redirected in the call graph, but
the corresponding gimple call is not yet updated.  If one calls
rebuild_references during this time on cloned foo, the reference that
gets rebuilt will still point to the old kernel, which is now in a
COMDAT group, thus creating a reference to a comdat-local symbol outside
of its COMDAT group, leading to a checking ICE.  This patch remedies
that by checking for this case and building a reference to the correct
kernel.

PR ipa/124700

gcc/ChangeLog:

* cgraph.cc (cgraph_node::is_clone_of): New method, determines
whether a node is a descendant of another in the clone tree.
* cgraph.h (struct cgraph_node): Add decl of is_clone_of.
* cgraphbuild.cc (mark_address): Fix reference creation for
cloned callback functions.

gcc/testsuite/ChangeLog:

* g++.dg/gomp/pr124700.C: New test.

Signed-off-by: Josef Melcr <josef.melcr@suse.com>
gcc/cgraph.cc
gcc/cgraph.h
gcc/cgraphbuild.cc
gcc/testsuite/g++.dg/gomp/pr124700.C [new file with mode: 0644]

index 90635fb5a89da78a305347f1380ada6707b4ed6d..353e8b498aa6d172b3702291964b3c045cd35d3c 100644 (file)
@@ -3626,6 +3626,16 @@ cgraph_node::only_called_directly_p (void)
                                       NULL, true);
 }
 
+/* Returns TRUE iff THIS is a descendant of N in the clone tree.  */
+
+bool
+cgraph_node::is_clone_of (cgraph_node *n) const
+{
+  for (cgraph_node *walker = clone_of; walker; walker = walker->clone_of)
+    if (walker == n)
+      return true;
+  return false;
+}
 
 /* Collect all callers of NODE.  Worker for collect_callers_of_node.  */
 
index 39209b539577ac5caf8e5199d39295ddf685d65c..938292cb21aa78fe7073fe49ec8c0ae012d51e1e 100644 (file)
@@ -1294,6 +1294,9 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node
      it is not used in any other non-standard way.  */
   bool only_called_directly_p (void);
 
+  /* Returns TRUE iff THIS is a descendant of N in the clone tree.  */
+  bool is_clone_of (cgraph_node *n) const;
+
   /* Turn profile to global0.  Walk into inlined functions.  */
   void make_profile_local ();
 
index e50adb955cb0210a49ef02dc44aeb73f8009c5ee..e33a414310bc77dcda71176ccda6a6009caef392 100644 (file)
@@ -214,9 +214,27 @@ mark_address (gimple *stmt, tree addr, tree, void *data)
   addr = get_base_address (addr);
   if (TREE_CODE (addr) == FUNCTION_DECL)
     {
+      cgraph_node *caller = (cgraph_node *) data;
       cgraph_node *node = cgraph_node::get_create (addr);
+      /* If NODE was cloned and the caller is a callback-dispatching function,
+        the gimple call might not be updated yet.  Check whether that's the
+        case and if so, replace NODE with the correct callee.  */
+      cgraph_edge *e = caller->get_edge (stmt);
+      if (e && e->has_callback)
+       {
+         for (cgraph_edge *cbe = e->first_callback_edge ();
+              cbe;
+              cbe = cbe->next_callback_edge ())
+           {
+             if (cbe->callee->is_clone_of (node))
+               {
+                 node = cbe->callee;
+                 break;
+               }
+           }
+       }
       node->mark_address_taken ();
-      ((symtab_node *)data)->create_reference (node, IPA_REF_ADDR, stmt);
+      caller->create_reference (node, IPA_REF_ADDR, stmt);
     }
   else if (addr && VAR_P (addr)
           && (TREE_STATIC (addr) || DECL_EXTERNAL (addr)))
diff --git a/gcc/testsuite/g++.dg/gomp/pr124700.C b/gcc/testsuite/g++.dg/gomp/pr124700.C
new file mode 100644 (file)
index 0000000..b95754a
--- /dev/null
@@ -0,0 +1,60 @@
+/* { dg-do compile } */
+/* { dg-options "-O3" } */
+
+int a (int);
+struct b
+{
+  b ();
+  b (b &);
+  ~b ();
+};
+template <typename c> b d (c);
+int ab;
+void ae (int);
+struct e
+{
+  b g;
+  template <typename c> e operator<< (c h)
+  {
+    d (h);
+    return *this;
+  }
+  b f;
+};
+template <int = 0> struct i
+{
+  long ah;
+  int j ()
+  {
+    e () << "" << aj << 1 << "" << ah << "" << ak << "";
+    return ah;
+  }
+  char aj;
+  int ak;
+};
+i<> k;
+template <typename>
+int
+ao (bool h)
+{
+#pragma omp parallel
+  {
+    int as;
+    for (; as;)
+      {
+       int au = k.j ();
+       ae (au);
+       if (h)
+         b ();
+      }
+  }
+  return a (ab);
+}
+int l = ao<short> (true);
+void
+az ()
+{
+  ao<short> (true);
+  __builtin_unreachable ();
+}
+template int ao<short> (bool);