aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Jambor <mjambor@suse.cz>2025-03-14 16:07:01 +0100
committerMartin Jambor <jamborm@gcc.gnu.org>2025-03-14 16:13:28 +0100
commit075ec330307c5b1fe5ed166a633c718c06b01437 (patch)
tree6bc130a86039a1a72a9eb89d29e260c62e92dba2
parentab0133cdba5dbcc29655593377e80586f7080472 (diff)
downloadgcc-075ec330307c5b1fe5ed166a633c718c06b01437.zip
gcc-075ec330307c5b1fe5ed166a633c718c06b01437.tar.gz
gcc-075ec330307c5b1fe5ed166a633c718c06b01437.tar.bz2
ipa: Do not modify cgraph edges from thunk clones during inlining (PR116572)
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. gcc/ChangeLog: 2025-03-13 Martin Jambor <mjambor@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 <mjambor@suse.cz> PR ipa/116572 * g++.dg/ipa/pr116572.C: New test.
-rw-r--r--gcc/cgraph.cc7
-rw-r--r--gcc/testsuite/g++.dg/ipa/pr116572.C37
2 files changed, 42 insertions, 2 deletions
diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
index d0b19ad..6ae6a97 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 0000000..909568e
--- /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("");
+}