Hi,

In PR 116572 we hit an assert that a thunk which does not have a body
looks like it has one.  It does not, but the call_stmt of its outgoing
edge points to a statement, which should not.  In fact it has several
outgoing call graph edges, which cannot be.  The problem is that the
code updating the edges to reflect inlining into the master clone (an
ex-thunk, unlike the clone, which is still an unexpanded thunk) is
being updated during inling into the master clone.  This patch simply
makes the code to skip unexpanded thunk clones.

The patch has passed bootstrap, LTO-bootstrap and testing on x86_64.  It
has been approved by Honza in person so I will commit it a few moments.
I also plan to backport it to gcc-14 in a few weeks.

Thanks,

Martin


gcc/ChangeLog:

2025-03-13  Martin Jambor  <mjam...@suse.cz>

        PR ipa/116572
        * cgraph.cc (cgraph_update_edges_for_call_stmt): Do not update
        edges of clones that are unexpanded thunk.  Assert that the node
        passed as the parameter is not an unexpanded thunk.

gcc/testsuite/ChangeLog:

2025-03-13  Martin Jambor  <mjam...@suse.cz>

        PR ipa/116572
        * g++.dg/ipa/pr116572.C: New test.
---
 gcc/cgraph.cc                       |  7 ++++--
 gcc/testsuite/g++.dg/ipa/pr116572.C | 37 +++++++++++++++++++++++++++++
 2 files changed, 42 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ipa/pr116572.C

diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
index d0b19ad850e..6ae6a97f6f5 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -1708,12 +1708,15 @@ cgraph_update_edges_for_call_stmt (gimple *old_stmt, 
tree old_decl,
   cgraph_node *node;
 
   gcc_checking_assert (orig);
+  gcc_assert (!orig->thunk);
   cgraph_update_edges_for_call_stmt_node (orig, old_stmt, old_decl, new_stmt);
   if (orig->clones)
     for (node = orig->clones; node != orig;)
       {
-       cgraph_update_edges_for_call_stmt_node (node, old_stmt, old_decl,
-                                               new_stmt);
+       /* Do not attempt to adjust bodies of yet unexpanded thunks.  */
+       if (!node->thunk)
+         cgraph_update_edges_for_call_stmt_node (node, old_stmt, old_decl,
+                                                 new_stmt);
        if (node->clones)
          node = node->clones;
        else if (node->next_sibling_clone)
diff --git a/gcc/testsuite/g++.dg/ipa/pr116572.C 
b/gcc/testsuite/g++.dg/ipa/pr116572.C
new file mode 100644
index 00000000000..909568e1c72
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ipa/pr116572.C
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-std=c++20 -O3 -fsanitize=undefined" } */
+
+long v;
+template <class> struct A;
+template <typename C, typename = A<C>, typename = C>
+class B;
+template <>
+struct A<char>
+{
+  static int foo(char *s, const char *t, long n) { return __builtin_memcmp(s, 
t, n); }
+};
+template <typename C, typename, typename>
+struct B {
+  long b;
+  B(const C *);
+  C *bar() const;
+  constexpr unsigned long baz(const C *, unsigned long, unsigned long) const 
noexcept;
+  void baz() { C c; baz(&c, 0, v); }
+};
+template <typename C, typename D, typename E>
+constexpr unsigned long
+B<C, D, E>::baz(const C *s, unsigned long, unsigned long n) const noexcept
+{
+  C *x = bar(); if (!x) return b; D::foo(x, s, n); return 0;
+}
+namespace {
+struct F { virtual ~F() {} };
+struct F2 { virtual void foo(B<char>) const; };
+struct F3 : F, F2 { void foo(B<char> s) const { s.baz(); } } f;
+}
+int
+main()
+{
+  F *p;
+  dynamic_cast<F2 *>(p)->foo("");
+}
-- 
2.48.1

Reply via email to