https://github.com/mocusez created 
https://github.com/llvm/llvm-project/pull/218210

Function template specializations are not returned as separate results when 
looking up their primary template, so visit target_clones specializations 
directly. Emit multiversion bodies before the final replacement sweeps so 
constructor and other symbol replacements discovered during clone generation 
are applied.

Fixes #116501

Constraint: Other multiversion kinds remain template-restricted
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep multiversion body emission before the replacement sweeps unless 
all late replacement producers are handled another way
Tested: Full check-clang with isolated valid /dev/null (48,447 passed, zero 
unexpected failures)
Tested: DuckDB Release 
build(https://github.com/mocusez/duckdb/tree/fmv_autovec_pr) 577/577 and 
4,426/4,426 tests with 983,782 assertions
Tested: LLVM issue #116501 default/popcnt example compiled, linked, dispatched, 
and ran
Not-tested: Runtime dispatch on non-x86 hardware

>From 7c7a5952de2f49c1cf98920a37c6573fd3e42643 Mon Sep 17 00:00:00 2001
From: MocusEZ <[email protected]>
Date: Sun, 23 Aug 2026 06:39:16 +0000
Subject: [PATCH] [clang] Support target_clones on function templates

Function template specializations are not returned as separate results when 
looking up their primary template, so visit target_clones specializations 
directly. Emit multiversion bodies before the final replacement sweeps so 
constructor and other symbol replacements discovered during clone generation 
are applied.

Fixes #116501

Constraint: Other multiversion kinds remain template-restricted
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep multiversion body emission before the replacement sweeps unless 
all late replacement producers are handled another way
Tested: Full check-clang with isolated valid /dev/null (48,447 passed, zero 
unexpected failures)
Tested: All 15 target_clones tests across x86, AArch64, RISC-V, and PowerPC
Tested: DuckDB Release build 577/577 and 4,426/4,426 tests with 983,782 
assertions
Tested: LLVM issue #116501 default/popcnt example compiled, linked, dispatched, 
and ran
Not-tested: Runtime dispatch on non-x86 hardware
---
 clang/docs/ReleaseNotes.md                    |  3 +
 clang/include/clang/Basic/AttrDocs.td         |  3 +
 clang/lib/AST/ASTContext.cpp                  | 10 +-
 clang/lib/CodeGen/CodeGenModule.cpp           |  3 +-
 clang/lib/Sema/SemaDecl.cpp                   |  2 +-
 .../attr-target-clones-templates.cpp          | 98 +++++++++++++++++++
 clang/test/SemaCXX/attr-target-clones.cpp     | 29 +++++-
 7 files changed, 142 insertions(+), 6 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/attr-target-clones-templates.cpp

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index fcd58e38261bb..eb1f794c63965 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -232,6 +232,9 @@ features cannot lower the translation-unit ABI level;
 
 ### Attribute Changes in Clang
 
+- The `target_clones` attribute now supports function templates. Each
+  specialization is multiversioned and dispatched independently. (#GH116501)
+
 - Clang now properly propagates attributes on class and variable templates to 
their redeclarations, which will result in redeclarations not interfering with 
diagnostics. (#GH209812)
 
 ### Improvements to Clang's diagnostics
diff --git a/clang/include/clang/Basic/AttrDocs.td 
b/clang/include/clang/Basic/AttrDocs.td
index 3052dd6c77ab1..6e1a5ebfcd1fd 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -3397,6 +3397,9 @@ generation options. Additionally, these versions will be 
resolved at runtime
 based on the priority of their attribute options. All `target_clone` functions
 are considered multiversioned functions.
 
+In C++, the attribute may be applied to a function template. Each 
specialization
+of the template is multiversioned and dispatched independently.
+
 For AArch64 target:
 The attribute contains comma-separated strings of target features joined by "+"
 sign. For example:
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 03a28ea295887..1a1413940f215 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -13467,7 +13467,15 @@ void ASTContext::forEachMultiversionedFunctionVersion(
     const FunctionDecl *FD,
     llvm::function_ref<void(FunctionDecl *)> Pred) const {
   assert(FD->isMultiVersion() && "Only valid for multiversioned functions");
-  llvm::SmallDenseSet<const FunctionDecl*, 4> SeenDecls;
+  // Function template specializations do not appear as separate results when
+  // looking up the name of their primary template. The target_clones attribute
+  // stores every version on the specialization itself, so visit it directly.
+  if (FD->isTargetClonesMultiVersion() &&
+      FD->isFunctionTemplateSpecialization()) {
+    Pred(const_cast<FunctionDecl *>(FD)->getMostRecentDecl());
+    return;
+  }
+  llvm::SmallDenseSet<const FunctionDecl *, 4> SeenDecls;
   FD = FD->getMostRecentDecl();
   // FIXME: The order of traversal here matters and depends on the order of
   // lookup results, which happens to be (mostly) oldest-to-newest, but we
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp 
b/clang/lib/CodeGen/CodeGenModule.cpp
index 435f67542268a..eb00611a2e6fc 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -1162,9 +1162,10 @@ void CodeGenModule::Release() {
   DeferredDecls.insert_range(EmittedDeferredDecls);
   EmittedDeferredDecls.clear();
   EmitVTablesOpportunistically();
+  // Multiversion bodies can add deferred definitions and replacements.
+  emitMultiVersionFunctions();
   applyGlobalValReplacements();
   applyReplacements();
-  emitMultiVersionFunctions();
   emitPFPFieldsWithEvaluatedOffset();
   emitGlobalDeleteForwardingBodies();
 
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 5055ef3d1cbb1..bf01230f1f098 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -11816,7 +11816,7 @@ static bool CheckMultiVersionAdditionalRules(Sema &S, 
const FunctionDecl *OldFD,
                               << static_cast<unsigned>(MVKind)),
       PartialDiagnosticAt(NewFD->getLocation(),
                           S.PDiag(diag::err_multiversion_diff)),
-      /*TemplatesSupported=*/false,
+      /*TemplatesSupported=*/MVKind == MultiVersionKind::TargetClones,
       /*ConstexprSupported=*/!IsCPUSpecificCPUDispatchMVKind,
       /*CLinkageMayDiffer=*/false);
 }
diff --git a/clang/test/CodeGenCXX/attr-target-clones-templates.cpp 
b/clang/test/CodeGenCXX/attr-target-clones-templates.cpp
new file mode 100644
index 0000000000000..127f1f65e3495
--- /dev/null
+++ b/clang/test/CodeGenCXX/attr-target-clones-templates.cpp
@@ -0,0 +1,98 @@
+// RUN: %clang_cc1 -std=c++14 -triple x86_64-linux-gnu -emit-llvm %s -o - | 
FileCheck %s
+
+struct InlineDependency {
+  InlineDependency(int &value) : value(value) {}
+  int &value;
+};
+
+template <typename T>
+T __attribute__((target_clones("sse4.2", "default"))) templated(T value) {
+  return value;
+}
+
+int call_int(int value) {
+  return templated(value);
+}
+
+float call_float(float value) {
+  return templated(value);
+}
+
+int (*address_int())(int) {
+  return &templated<int>;
+}
+
+template double templated<double>(double);
+
+template <typename T>
+T __attribute__((target_clones("sse4.2", "default")))
+with_inline_dependency(T &value) {
+  InlineDependency dependency(value);
+  return dependency.value;
+}
+
+int call_with_inline_dependency(int &value) {
+  return with_inline_dependency(value);
+}
+
+// CHECK-DAG: $_Z9templatedIiET_S0_.resolver = comdat any
+
+// CHECK-DAG: $_Z9templatedIfET_S0_.resolver = comdat any
+// CHECK-DAG: $_Z9templatedIdET_S0_.resolver = comdat any
+// CHECK-DAG: $_Z9templatedIiET_S0_.sse4.2.0 = comdat any
+// CHECK-DAG: $_Z9templatedIiET_S0_.default.1 = comdat any
+// CHECK-DAG: $_Z9templatedIfET_S0_.sse4.2.0 = comdat any
+// CHECK-DAG: $_Z9templatedIfET_S0_.default.1 = comdat any
+// CHECK-DAG: $_Z9templatedIdET_S0_.sse4.2.0 = comdat any
+// CHECK-DAG: $_Z9templatedIdET_S0_.default.1 = comdat any
+// CHECK-DAG: $_Z22with_inline_dependencyIiET_RS0_.resolver = comdat any
+// CHECK-DAG: $_Z22with_inline_dependencyIiET_RS0_.sse4.2.0 = comdat any
+// CHECK-DAG: $_Z22with_inline_dependencyIiET_RS0_.default.1 = comdat any
+
+// CHECK-DAG: @_Z9templatedIiET_S0_.ifunc = weak_odr alias i32 (i32), ptr 
@_Z9templatedIiET_S0_
+// CHECK-DAG: @_Z9templatedIfET_S0_.ifunc = weak_odr alias float (float), ptr 
@_Z9templatedIfET_S0_
+// CHECK-DAG: @_Z9templatedIdET_S0_.ifunc = weak_odr alias double (double), 
ptr @_Z9templatedIdET_S0_
+// CHECK-DAG: @_Z9templatedIiET_S0_ = weak_odr ifunc i32 (i32), ptr 
@_Z9templatedIiET_S0_.resolver
+// CHECK-DAG: @_Z9templatedIfET_S0_ = weak_odr ifunc float (float), ptr 
@_Z9templatedIfET_S0_.resolver
+// CHECK-DAG: @_Z9templatedIdET_S0_ = weak_odr ifunc double (double), ptr 
@_Z9templatedIdET_S0_.resolver
+
+// CHECK-DAG: @_Z22with_inline_dependencyIiET_RS0_.ifunc = weak_odr alias i32 
(ptr), ptr @_Z22with_inline_dependencyIiET_RS0_
+// CHECK-DAG: @_Z22with_inline_dependencyIiET_RS0_ = weak_odr ifunc i32 (ptr), 
ptr @_Z22with_inline_dependencyIiET_RS0_.resolver
+// CHECK-LABEL: define dso_local noundef i32 @_Z8call_inti(
+// CHECK: call noundef i32 @_Z9templatedIiET_S0_(i32 noundef
+
+// CHECK-LABEL: define weak_odr ptr @_Z9templatedIiET_S0_.resolver() {{.*}} 
comdat
+// CHECK: ret ptr @_Z9templatedIiET_S0_.sse4.2.0
+// CHECK: ret ptr @_Z9templatedIiET_S0_.default.1
+
+// CHECK-LABEL: define dso_local noundef float @_Z10call_floatf(
+// CHECK: call noundef float @_Z9templatedIfET_S0_(float noundef
+
+// CHECK-LABEL: define weak_odr ptr @_Z9templatedIfET_S0_.resolver() {{.*}} 
comdat
+// CHECK: ret ptr @_Z9templatedIfET_S0_.sse4.2.0
+// CHECK: ret ptr @_Z9templatedIfET_S0_.default.1
+
+// CHECK-LABEL: define dso_local noundef ptr @_Z11address_intv(
+// CHECK: ret ptr @_Z9templatedIiET_S0_
+
+// CHECK-LABEL: define weak_odr noundef double @_Z9templatedIdET_S0_.sse4.2.0(
+// CHECK-LABEL: define weak_odr noundef double @_Z9templatedIdET_S0_.default.1(
+// CHECK-LABEL: define weak_odr ptr @_Z9templatedIdET_S0_.resolver() {{.*}} 
comdat
+// CHECK: ret ptr @_Z9templatedIdET_S0_.sse4.2.0
+// CHECK: ret ptr @_Z9templatedIdET_S0_.default.1
+
+// CHECK-LABEL: define dso_local noundef i32 
@_Z27call_with_inline_dependencyRi(
+// CHECK: call noundef i32 @_Z22with_inline_dependencyIiET_RS0_(
+
+// CHECK-LABEL: define weak_odr ptr 
@_Z22with_inline_dependencyIiET_RS0_.resolver() {{.*}} comdat
+// CHECK: ret ptr @_Z22with_inline_dependencyIiET_RS0_.sse4.2.0
+// CHECK: ret ptr @_Z22with_inline_dependencyIiET_RS0_.default.1
+
+// CHECK-LABEL: define linkonce_odr noundef i32 @_Z9templatedIiET_S0_.sse4.2.0(
+// CHECK-LABEL: define linkonce_odr noundef i32 
@_Z9templatedIiET_S0_.default.1(
+// CHECK-LABEL: define linkonce_odr noundef float 
@_Z9templatedIfET_S0_.sse4.2.0(
+// CHECK-LABEL: define linkonce_odr noundef float 
@_Z9templatedIfET_S0_.default.1(
+// CHECK-LABEL: define linkonce_odr noundef i32 
@_Z22with_inline_dependencyIiET_RS0_.sse4.2.0(
+// CHECK-LABEL: define linkonce_odr void @_ZN16InlineDependencyC1ERi(
+// CHECK-LABEL: define linkonce_odr noundef i32 
@_Z22with_inline_dependencyIiET_RS0_.default.1(
+// CHECK-LABEL: define linkonce_odr void @_ZN16InlineDependencyC2ERi(
diff --git a/clang/test/SemaCXX/attr-target-clones.cpp 
b/clang/test/SemaCXX/attr-target-clones.cpp
index 68c9a4ff48ed9..2eb2c816649e1 100644
--- a/clang/test/SemaCXX/attr-target-clones.cpp
+++ b/clang/test/SemaCXX/attr-target-clones.cpp
@@ -1,8 +1,31 @@
 // RUN: %clang_cc1 -triple x86_64-linux-gnu  -fsyntax-only -verify 
-fexceptions -fcxx-exceptions %s -std=c++14
 
-// expected-error@+2 {{attribute 'target_clones' multiversioned functions do 
not yet support function templates}}
-template<typename T, typename U>
-int __attribute__((target_clones("sse4.2", "default"))) foo(){ return 1;}
+template <typename T>
+T __attribute__((target_clones("sse4.2", "default"))) templated(T value) {
+  return value;
+}
+
+int use_templated() {
+  int (*ptr)(int) = &templated<int>;
+  return ptr(1) + templated(2.0f);
+}
+
+template double templated<double>(double);
+
+struct HasMemberTemplate {
+  template <typename T>
+  T __attribute__((target_clones("sse4.2", "default"))) member(T value) {
+    return value;
+  }
+};
+
+int use_member_template(HasMemberTemplate &object) {
+  return object.member(1);
+}
+
+// expected-error@+2 {{attribute 'target_clones' multiversioned functions do 
not yet support deduced return types}}
+template <typename T>
+auto __attribute__((target_clones("sse4.2", "default"))) undeduced(T);
 
 void uses_lambda() {
   // expected-error@+1 {{attribute 'target_clones' multiversioned functions do 
not yet support lambdas}}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to