This fixes the missed inlining reported in PR ipa/95796:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95796
Functions with matching target_clones attributes remain uninlinable
after their versions are generated.

The C-family target_clones attribute handler sets DECL_UNINLINABLE.
Generated versions inherit that flag, preventing inlining even after
matching caller and callee versions have been selected.

Reject inlining while the target_clones attribute is present, without
caching that temporary restriction in DECL_UNINLINABLE. Once version
expansion replaces the attribute, the usual inlining checks apply.

Add C and C++ checks for inlining matching versions and a negative check
for different target lists. Keep the PR95778 call-routing tests focused
on their existing assertions by marking their callees noinline.

Bootstrapped all default languages and regression-tested on
x86_64-pc-linux-gnu with multilib enabled, including make -k check and
check-c++-all. No new unexpected failures compared with unmodified
7d652a985309ac5cd1492a505805bbd2c3781598.

OK for trunk? If approved, please commit this on my behalf.

Assisted-by: OpenAI Codex

gcc/c-family/ChangeLog:

        PR ipa/95796
        * c-attribs.cc (handle_target_clones_attribute): Do not set
        DECL_UNINLINABLE for target_clones.

gcc/ChangeLog:

        PR ipa/95796
        * tree-inline.cc (tree_inlinable_function_p): Reject an unexpanded
        target_clones function without caching the restriction.

gcc/testsuite/ChangeLog:

        PR ipa/95796
        * g++.target/i386/pr95796-1.C: New test.
        * gcc.target/i386/pr95778-1.c (f2): Add noinline.
        * gcc.target/i386/pr95778-2.c (f2): Likewise.
        * gcc.target/i386/pr95796-1.c: New test.
        * gcc.target/i386/pr95796-3.c: New test.
        * gcc.target/i386/tct-ipa-inline-ice.c: Remove obsolete comment.

---
diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index 470054f04..ab7c6f6eb 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -5786,9 +5786,6 @@ handle_target_clones_attribute (tree *node, tree
name, tree ARG_UNUSED (args),
                     "single %<target_clones%> attribute is ignored");
          *no_add_attrs = true;
        }
-      else
-      /* Do not inline functions with multiple clone targets.  */
-       DECL_UNINLINABLE (*node) = 1;
     }
   else
     {
diff --git a/gcc/testsuite/g++.target/i386/pr95796-1.C
b/gcc/testsuite/g++.target/i386/pr95796-1.C
new file mode 100644
index 000000000..8bd554516
--- /dev/null
+++ b/gcc/testsuite/g++.target/i386/pr95796-1.C
@@ -0,0 +1,21 @@
+/* PR ipa/95796 */
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+template <class T>
+__attribute__((target_clones("default", "sse4.2")))
+static T
+helper (T x)
+{
+  return 3 * x + 1;
+}
+
+__attribute__((target_clones("sse4.2", "default")))
+unsigned
+caller (unsigned x)
+{
+  return helper (x);
+}
+
+/* { dg-final { scan-tree-dump-not "= helper" "optimized" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr95778-1.c
b/gcc/testsuite/gcc.target/i386/pr95778-1.c
index 18f838321..e537fd578 100644
--- a/gcc/testsuite/gcc.target/i386/pr95778-1.c
+++ b/gcc/testsuite/gcc.target/i386/pr95778-1.c
@@ -2,7 +2,7 @@
 /* { dg-options "-O3 -fPIC -fno-asynchronous-unwind-tables" } */
 /* { dg-require-ifunc "" } */

-__attribute__((target_clones("default,avx2")))
+__attribute__((noinline, target_clones("default,avx2")))
 static int
 f2(int *p)
 {
diff --git a/gcc/testsuite/gcc.target/i386/pr95778-2.c
b/gcc/testsuite/gcc.target/i386/pr95778-2.c
index 9ef513aab..339086671 100644
--- a/gcc/testsuite/gcc.target/i386/pr95778-2.c
+++ b/gcc/testsuite/gcc.target/i386/pr95778-2.c
@@ -2,7 +2,7 @@
 /* { dg-options "-O3 -fPIC -fno-asynchronous-unwind-tables" } */
 /* { dg-require-ifunc "" } */

-__attribute__((visibility("internal"),target_clones("default,avx2")))
+__attribute__((noinline, visibility("internal"),target_clones("default,avx2")))
 int
 f2(int *p)
 {
diff --git a/gcc/testsuite/gcc.target/i386/pr95796-1.c
b/gcc/testsuite/gcc.target/i386/pr95796-1.c
new file mode 100644
index 000000000..4d62a989d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr95796-1.c
@@ -0,0 +1,22 @@
+/* PR ipa/95796 */
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+__attribute__((target_clones("default", "sse4.2")))
+static unsigned
+helper (unsigned x)
+{
+  return 3 * x + 1;
+}
+
+__attribute__((target_clones("sse4.2", "default")))
+unsigned
+caller (unsigned x)
+{
+  return helper (x);
+}
+
+/* Both callers should inline the matching helper, despite the different
+   order of the target_clones arguments.  */
+/* { dg-final { scan-tree-dump-not "= helper" "optimized" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr95796-3.c
b/gcc/testsuite/gcc.target/i386/pr95796-3.c
new file mode 100644
index 000000000..1c3c45ffd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr95796-3.c
@@ -0,0 +1,23 @@
+/* PR ipa/95796 */
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+__attribute__((target_clones("default", "avx2")))
+static unsigned
+helper (unsigned x)
+{
+  return 3 * x + 1;
+}
+
+__attribute__((target_clones("default", "sse4.2")))
+unsigned
+caller (unsigned x)
+{
+  return helper (x);
+}
+
+/* The caller's target_clones list does not determine which version of the
+   helper the resolver would select.  Do not inline the unexpanded helper
+   during early inlining and thereby bypass the resolver.  */
+/* { dg-final { scan-tree-dump "= helper \\(" "optimized" } } */
diff --git a/gcc/testsuite/gcc.target/i386/tct-ipa-inline-ice.c
b/gcc/testsuite/gcc.target/i386/tct-ipa-inline-ice.c
index b8f77372e..6f390613f 100644
--- a/gcc/testsuite/gcc.target/i386/tct-ipa-inline-ice.c
+++ b/gcc/testsuite/gcc.target/i386/tct-ipa-inline-ice.c
@@ -1,8 +1,5 @@
 /* Test function multiversioning and inlining of a function with a
-   target_clones attribute.  Note, we need to allow inlining of
-   target_clones functions, see
-   "Do not inline functions with multiple clone targets." in c-attribs.cc,
-   otherwise the test would not trigger the ICE.  */
+   target_clones attribute.  */
 /* { dg-do assemble } */
 /* { dg-require-ifunc "" } */
 /* { dg-options "-O3" } */
diff --git a/gcc/tree-inline.cc b/gcc/tree-inline.cc
index 8162b1f50..425221449 100644
--- a/gcc/tree-inline.cc
+++ b/gcc/tree-inline.cc
@@ -4216,6 +4216,12 @@ tree_inlinable_function_p (tree fn)
   bool do_warning;
   tree always_inline;

+  /* Do not inline a target_clones function before its versions have been
+     expanded.  This restriction is temporary: unlike the other reasons below,
+     it must not be cached in DECL_UNINLINABLE and inherited by the clones.  */
+  if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (fn)))
+    return false;
+
   /* If we've already decided this function shouldn't be inlined,
      there's no need to check again.  */
   if (DECL_UNINLINABLE (fn))

Reply via email to