The inline diff was line-wrapped in transit. Please use the attached copy of the same patch. There are no code changes.
From: Oleksandr Kulkov <[email protected]> Date: Thu, 08 Oct 2026 20:54:00 +0200 Subject: [PATCH] ipa: Allow inlining expanded target_clones [PR95796]
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))
