[clang] [AArch64][FMV] Enable PAuth and BTI hardening of resolver functions (PR #141573)

Anatoly Trosinenko via cfe-commits cfe-commits at lists.llvm.org
Wed Sep 17 05:54:12 PDT 2025


https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/141573

>From 83d2b3affe1a438bf260356c3b581fb613da5cd4 Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Mon, 26 May 2025 22:28:55 +0300
Subject: [PATCH 1/8] [AArch64][FMV] Enable PAuth and BTI hardening of resolver
 functions

---
 clang/lib/CodeGen/CodeGenModule.cpp           |  9 +++++++
 .../CodeGen/ptrauth-resolver-attributes.c     | 25 +++++++++++++++++++
 2 files changed, 34 insertions(+)
 create mode 100644 clang/test/CodeGen/ptrauth-resolver-attributes.c

diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 0ebab141b187d..b1d8aa2e7eb8f 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -4831,6 +4831,13 @@ llvm::Constant *CodeGenModule::GetOrCreateMultiVersionResolver(GlobalDecl GD) {
   if (FD->isTargetMultiVersion() || FD->isTargetClonesMultiVersion())
     AddDeferredMultiVersionResolverToEmit(GD);
 
+  auto SetResolverAttrs = [&](llvm::Function &Resolver) {
+    TargetInfo::BranchProtectionInfo BPI(getLangOpts());
+    TargetCodeGenInfo::setBranchProtectionFnAttributes(BPI, Resolver);
+    TargetCodeGenInfo::setPointerAuthFnAttributes(CodeGenOpts.PointerAuth,
+                                                  Resolver);
+  };
+
   // For cpu_specific, don't create an ifunc yet because we don't know if the
   // cpu_dispatch will be emitted in this translation unit.
   if (ShouldReturnIFunc) {
@@ -4845,6 +4852,7 @@ llvm::Constant *CodeGenModule::GetOrCreateMultiVersionResolver(GlobalDecl GD) {
                                   "", Resolver, &getModule());
     GIF->setName(ResolverName);
     SetCommonAttributes(FD, GIF);
+    SetResolverAttrs(cast<llvm::Function>(*Resolver));
     if (ResolverGV)
       replaceDeclarationWith(ResolverGV, GIF);
     return GIF;
@@ -4855,6 +4863,7 @@ llvm::Constant *CodeGenModule::GetOrCreateMultiVersionResolver(GlobalDecl GD) {
   assert(isa<llvm::GlobalValue>(Resolver) && !ResolverGV &&
          "Resolver should be created for the first time");
   SetCommonAttributes(FD, cast<llvm::GlobalValue>(Resolver));
+  SetResolverAttrs(cast<llvm::Function>(*Resolver));
   return Resolver;
 }
 
diff --git a/clang/test/CodeGen/ptrauth-resolver-attributes.c b/clang/test/CodeGen/ptrauth-resolver-attributes.c
new file mode 100644
index 0000000000000..8bdedd2c549be
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-resolver-attributes.c
@@ -0,0 +1,25 @@
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -mbranch-target-enforce -msign-return-address=all      -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,BTI-SIGNRA %s
+// RUN: %clang_cc1 -triple arm64-apple-ios   -mbranch-target-enforce -msign-return-address=all      -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,BTI-SIGNRA %s
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-returns -fptrauth-auth-traps -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,PAUTHTEST %s
+// RUN: %clang_cc1 -triple arm64-apple-ios   -fptrauth-calls -fptrauth-returns -fptrauth-auth-traps -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,PAUTHTEST %s
+
+// Check that resolver functions generated by clang have the correct attributes.
+
+int __attribute__((target_clones("crc", "default"))) ftc(void) { return 0; }
+
+int __attribute__((target_version("crc")))     fmv(void) { return 0; }
+int __attribute__((target_version("default"))) fmv(void) { return 0; }
+
+// CHECK: define{{.*}} i32 @ftc._Mcrc() #0
+// CHECK: define{{.*}} ptr @ftc.resolver() #1
+// CHECK: define{{.*}} i32 @fmv._Mcrc() #0
+// CHECK: define{{.*}} i32 @fmv.default() #2
+// CHECK: define{{.*}} i32 @ftc.default() #2
+// CHECK: define{{.*}} ptr @fmv.resolver() #1
+
+// BTI-SIGNRA: attributes #0 = { {{.*}}"branch-target-enforcement" {{.*}}"sign-return-address"="all" "sign-return-address-key"="a_key"{{.*}} }
+// BTI-SIGNRA: attributes #1 = { {{.*}}"branch-target-enforcement" {{.*}}"sign-return-address"="all" "sign-return-address-key"="a_key"{{.*}} }
+// BTI-SIGNRA: attributes #2 = { {{.*}}"branch-target-enforcement" {{.*}}"sign-return-address"="all" "sign-return-address-key"="a_key"{{.*}} }
+// PAUTHTEST: attributes #0 = { {{.*}}"ptrauth-auth-traps" "ptrauth-calls" "ptrauth-returns"{{.*}} }
+// PAUTHTEST: attributes #1 = { {{.*}}"ptrauth-auth-traps" "ptrauth-calls" "ptrauth-returns"{{.*}} }
+// PAUTHTEST: attributes #2 = { {{.*}}"ptrauth-auth-traps" "ptrauth-calls" "ptrauth-returns"{{.*}} }

>From 55f830c846f8aea4373bf2c13378365ea85fa342 Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Wed, 4 Jun 2025 16:34:47 +0300
Subject: [PATCH 2/8] Use setTargetAttributes function

---
 clang/lib/CodeGen/CodeGenModule.cpp | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index b1d8aa2e7eb8f..52c557a6f61bb 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -4831,11 +4831,11 @@ llvm::Constant *CodeGenModule::GetOrCreateMultiVersionResolver(GlobalDecl GD) {
   if (FD->isTargetMultiVersion() || FD->isTargetClonesMultiVersion())
     AddDeferredMultiVersionResolverToEmit(GD);
 
-  auto SetResolverAttrs = [&](llvm::Function &Resolver) {
-    TargetInfo::BranchProtectionInfo BPI(getLangOpts());
-    TargetCodeGenInfo::setBranchProtectionFnAttributes(BPI, Resolver);
-    TargetCodeGenInfo::setPointerAuthFnAttributes(CodeGenOpts.PointerAuth,
-                                                  Resolver);
+  auto SetResolverAttrs = [&](llvm::Function *Resolver) {
+    // Set the default target-specific attributes, such as PAC and BTI ones on
+    // AArch64. Not passing Decl to prevent setting unrelated attributes,
+    // as Resolver can be shared by multiple declarations.
+    getTargetCodeGenInfo().setTargetAttributes(/*D=*/nullptr, Resolver, *this);
   };
 
   // For cpu_specific, don't create an ifunc yet because we don't know if the
@@ -4852,7 +4852,7 @@ llvm::Constant *CodeGenModule::GetOrCreateMultiVersionResolver(GlobalDecl GD) {
                                   "", Resolver, &getModule());
     GIF->setName(ResolverName);
     SetCommonAttributes(FD, GIF);
-    SetResolverAttrs(cast<llvm::Function>(*Resolver));
+    SetResolverAttrs(cast<llvm::Function>(Resolver));
     if (ResolverGV)
       replaceDeclarationWith(ResolverGV, GIF);
     return GIF;
@@ -4863,7 +4863,7 @@ llvm::Constant *CodeGenModule::GetOrCreateMultiVersionResolver(GlobalDecl GD) {
   assert(isa<llvm::GlobalValue>(Resolver) && !ResolverGV &&
          "Resolver should be created for the first time");
   SetCommonAttributes(FD, cast<llvm::GlobalValue>(Resolver));
-  SetResolverAttrs(cast<llvm::Function>(*Resolver));
+  SetResolverAttrs(cast<llvm::Function>(Resolver));
   return Resolver;
 }
 

>From 7d2b171f1bb7128e39e006cf9d924455fcb0a39a Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Wed, 4 Jun 2025 17:36:20 +0300
Subject: [PATCH 3/8] test: accept functions and attribute groups in any order

---
 .../CodeGen/ptrauth-resolver-attributes.c     | 24 +++++++++----------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/clang/test/CodeGen/ptrauth-resolver-attributes.c b/clang/test/CodeGen/ptrauth-resolver-attributes.c
index 8bdedd2c549be..a1239604226a0 100644
--- a/clang/test/CodeGen/ptrauth-resolver-attributes.c
+++ b/clang/test/CodeGen/ptrauth-resolver-attributes.c
@@ -10,16 +10,16 @@ int __attribute__((target_clones("crc", "default"))) ftc(void) { return 0; }
 int __attribute__((target_version("crc")))     fmv(void) { return 0; }
 int __attribute__((target_version("default"))) fmv(void) { return 0; }
 
-// CHECK: define{{.*}} i32 @ftc._Mcrc() #0
-// CHECK: define{{.*}} ptr @ftc.resolver() #1
-// CHECK: define{{.*}} i32 @fmv._Mcrc() #0
-// CHECK: define{{.*}} i32 @fmv.default() #2
-// CHECK: define{{.*}} i32 @ftc.default() #2
-// CHECK: define{{.*}} ptr @fmv.resolver() #1
+// CHECK-DAG: define{{.*}} i32 @ftc._Mcrc() #[[ATTR_CRC:[0-9]+]]
+// CHECK-DAG: define{{.*}} i32 @ftc.default() #[[ATTR_DEFAULT:[0-9]+]]
+// CHECK-DAG: define{{.*}} ptr @ftc.resolver() #[[ATTR_RESOLVER:[0-9]+]]
+// CHECK-DAG: define{{.*}} i32 @fmv._Mcrc() #[[ATTR_CRC]]
+// CHECK-DAG: define{{.*}} i32 @fmv.default() #[[ATTR_DEFAULT]]
+// CHECK-DAG: define{{.*}} ptr @fmv.resolver() #[[ATTR_RESOLVER]]
 
-// BTI-SIGNRA: attributes #0 = { {{.*}}"branch-target-enforcement" {{.*}}"sign-return-address"="all" "sign-return-address-key"="a_key"{{.*}} }
-// BTI-SIGNRA: attributes #1 = { {{.*}}"branch-target-enforcement" {{.*}}"sign-return-address"="all" "sign-return-address-key"="a_key"{{.*}} }
-// BTI-SIGNRA: attributes #2 = { {{.*}}"branch-target-enforcement" {{.*}}"sign-return-address"="all" "sign-return-address-key"="a_key"{{.*}} }
-// PAUTHTEST: attributes #0 = { {{.*}}"ptrauth-auth-traps" "ptrauth-calls" "ptrauth-returns"{{.*}} }
-// PAUTHTEST: attributes #1 = { {{.*}}"ptrauth-auth-traps" "ptrauth-calls" "ptrauth-returns"{{.*}} }
-// PAUTHTEST: attributes #2 = { {{.*}}"ptrauth-auth-traps" "ptrauth-calls" "ptrauth-returns"{{.*}} }
+// BTI-SIGNRA-DAG: attributes #[[ATTR_CRC]]      = { {{.*}}"branch-target-enforcement" {{.*}}"sign-return-address"="all" "sign-return-address-key"="a_key"{{.*}} }
+// BTI-SIGNRA-DAG: attributes #[[ATTR_RESOLVER]] = { {{.*}}"branch-target-enforcement" {{.*}}"sign-return-address"="all" "sign-return-address-key"="a_key"{{.*}} }
+// BTI-SIGNRA-DAG: attributes #[[ATTR_DEFAULT]]  = { {{.*}}"branch-target-enforcement" {{.*}}"sign-return-address"="all" "sign-return-address-key"="a_key"{{.*}} }
+// PAUTHTEST-DAG:  attributes #[[ATTR_CRC]]      = { {{.*}}"ptrauth-auth-traps" "ptrauth-calls" "ptrauth-returns"{{.*}} }
+// PAUTHTEST-DAG:  attributes #[[ATTR_RESOLVER]] = { {{.*}}"ptrauth-auth-traps" "ptrauth-calls" "ptrauth-returns"{{.*}} }
+// PAUTHTEST-DAG:  attributes #[[ATTR_DEFAULT]]  = { {{.*}}"ptrauth-auth-traps" "ptrauth-calls" "ptrauth-returns"{{.*}} }

>From 4e8a8804453c0e4fa8f2ff7668b7e62f95b75351 Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Mon, 14 Jul 2025 21:33:40 +0300
Subject: [PATCH 4/8] Move new test file to AArch64/ subdirectory

---
 clang/test/CodeGen/{ => AArch64}/ptrauth-resolver-attributes.c | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename clang/test/CodeGen/{ => AArch64}/ptrauth-resolver-attributes.c (100%)

diff --git a/clang/test/CodeGen/ptrauth-resolver-attributes.c b/clang/test/CodeGen/AArch64/ptrauth-resolver-attributes.c
similarity index 100%
rename from clang/test/CodeGen/ptrauth-resolver-attributes.c
rename to clang/test/CodeGen/AArch64/ptrauth-resolver-attributes.c

>From fcf7cd6f69f51396f746a8b3a120d5f63bd43899 Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Fri, 5 Sep 2025 00:11:23 +0300
Subject: [PATCH 5/8] [WIP] Set common attributes on the resolver functions

---
 clang/lib/CodeGen/CodeGenModule.cpp           |  5 +++
 .../AArch64/ptrauth-resolver-attributes.c     | 36 +++++++++++++------
 2 files changed, 31 insertions(+), 10 deletions(-)

diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 52c557a6f61bb..9d48b1fd6e462 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -4611,6 +4611,8 @@ void CodeGenModule::emitMultiVersionFunctions() {
     llvm::Function *ResolverFunc = cast<llvm::Function>(ResolverConstant);
 
     ResolverFunc->setLinkage(getMultiversionLinkage(*this, GD));
+    setGlobalVisibility(ResolverFunc, /*D=*/nullptr);
+    setDSOLocal(ResolverFunc);
 
     if (!ResolverFunc->hasLocalLinkage() && supportsCOMDAT())
       ResolverFunc->setComdat(
@@ -4675,6 +4677,9 @@ void CodeGenModule::emitCPUDispatchDefinition(GlobalDecl GD) {
   auto *ResolverFunc = cast<llvm::Function>(GetOrCreateLLVMFunction(
       ResolverName, ResolverType, ResolverGD, /*ForVTable=*/false));
   ResolverFunc->setLinkage(getMultiversionLinkage(*this, GD));
+  setGlobalVisibility(ResolverFunc, /*D=*/nullptr);
+  setDSOLocal(ResolverFunc);
+
   if (supportsCOMDAT())
     ResolverFunc->setComdat(
         getModule().getOrInsertComdat(ResolverFunc->getName()));
diff --git a/clang/test/CodeGen/AArch64/ptrauth-resolver-attributes.c b/clang/test/CodeGen/AArch64/ptrauth-resolver-attributes.c
index a1239604226a0..edb30cf491fda 100644
--- a/clang/test/CodeGen/AArch64/ptrauth-resolver-attributes.c
+++ b/clang/test/CodeGen/AArch64/ptrauth-resolver-attributes.c
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 -triple aarch64-linux-gnu -mbranch-target-enforce -msign-return-address=all      -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,BTI-SIGNRA %s
-// RUN: %clang_cc1 -triple arm64-apple-ios   -mbranch-target-enforce -msign-return-address=all      -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,BTI-SIGNRA %s
-// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-returns -fptrauth-auth-traps -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,PAUTHTEST %s
-// RUN: %clang_cc1 -triple arm64-apple-ios   -fptrauth-calls -fptrauth-returns -fptrauth-auth-traps -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,PAUTHTEST %s
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -mbranch-target-enforce -msign-return-address=all      -emit-llvm %s -o - | FileCheck -DLINKVIS="dso_local " --check-prefixes=CHECK,BTI-SIGNRA %s
+// RUN: %clang_cc1 -triple arm64-apple-ios   -mbranch-target-enforce -msign-return-address=all      -emit-llvm %s -o - | FileCheck -DLINKVIS=""           --check-prefixes=CHECK,BTI-SIGNRA %s
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-returns -fptrauth-auth-traps -emit-llvm %s -o - | FileCheck -DLINKVIS="dso_local " --check-prefixes=CHECK,PAUTHTEST %s
+// RUN: %clang_cc1 -triple arm64-apple-ios   -fptrauth-calls -fptrauth-returns -fptrauth-auth-traps -emit-llvm %s -o - | FileCheck -DLINKVIS=""           --check-prefixes=CHECK,PAUTHTEST %s
 
 // Check that resolver functions generated by clang have the correct attributes.
 
@@ -10,12 +10,28 @@ int __attribute__((target_clones("crc", "default"))) ftc(void) { return 0; }
 int __attribute__((target_version("crc")))     fmv(void) { return 0; }
 int __attribute__((target_version("default"))) fmv(void) { return 0; }
 
-// CHECK-DAG: define{{.*}} i32 @ftc._Mcrc() #[[ATTR_CRC:[0-9]+]]
-// CHECK-DAG: define{{.*}} i32 @ftc.default() #[[ATTR_DEFAULT:[0-9]+]]
-// CHECK-DAG: define{{.*}} ptr @ftc.resolver() #[[ATTR_RESOLVER:[0-9]+]]
-// CHECK-DAG: define{{.*}} i32 @fmv._Mcrc() #[[ATTR_CRC]]
-// CHECK-DAG: define{{.*}} i32 @fmv.default() #[[ATTR_DEFAULT]]
-// CHECK-DAG: define{{.*}} ptr @fmv.resolver() #[[ATTR_RESOLVER]]
+static int __attribute__((target_clones("crc", "default"))) static_ftc(void) { return 0; }
+
+static int __attribute__((target_version("crc")))     static_fmv(void) { return 0; }
+static int __attribute__((target_version("default"))) static_fmv(void) { return 0; }
+
+// Force emission of static_* functions.
+void *get_ptr1(void) { return static_ftc; }
+void *get_ptr2(void) { return static_fmv; }
+
+// CHECK-DAG: define [[LINKVIS]]i32 @ftc._Mcrc() #[[ATTR_CRC:[0-9]+]]
+// CHECK-DAG: define [[LINKVIS]]i32 @ftc.default() #[[ATTR_DEFAULT:[0-9]+]]
+// CHECK-DAG: define weak_odr ptr @ftc.resolver() #[[ATTR_RESOLVER:[0-9]+]]
+// CHECK-DAG: define [[LINKVIS]]i32 @fmv._Mcrc() #[[ATTR_CRC]]
+// CHECK-DAG: define [[LINKVIS]]i32 @fmv.default() #[[ATTR_DEFAULT]]
+// CHECK-DAG: define weak_odr ptr @fmv.resolver() #[[ATTR_RESOLVER]]
+
+// CHECK-DAG: define internal i32 @static_ftc._Mcrc() #[[ATTR_CRC:[0-9]+]]
+// CHECK-DAG: define internal i32 @static_ftc.default() #[[ATTR_DEFAULT:[0-9]+]]
+// CHECK-DAG: define internal ptr @static_ftc.resolver() #[[ATTR_RESOLVER:[0-9]+]]
+// CHECK-DAG: define internal i32 @static_fmv._Mcrc() #[[ATTR_CRC]]
+// CHECK-DAG: define internal i32 @static_fmv.default() #[[ATTR_DEFAULT]]
+// CHECK-DAG: define internal ptr @static_fmv.resolver() #[[ATTR_RESOLVER]]
 
 // BTI-SIGNRA-DAG: attributes #[[ATTR_CRC]]      = { {{.*}}"branch-target-enforcement" {{.*}}"sign-return-address"="all" "sign-return-address-key"="a_key"{{.*}} }
 // BTI-SIGNRA-DAG: attributes #[[ATTR_RESOLVER]] = { {{.*}}"branch-target-enforcement" {{.*}}"sign-return-address"="all" "sign-return-address-key"="a_key"{{.*}} }

>From e1c1e232e2c6aec5a349a5318dc6e68b916a91e7 Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Fri, 5 Sep 2025 20:05:30 +0300
Subject: [PATCH 6/8] Introduce setMultiVersionResolverAttributes function

---
 clang/lib/CodeGen/CodeGenModule.cpp           | 38 +++++++-----
 clang/lib/CodeGen/CodeGenModule.h             |  9 +++
 .../CodeGen/AArch64/fmv-resolver-emission.c   |  2 +-
 clang/test/CodeGen/AArch64/ptrauth-fmv.c      | 42 +++++++++++++
 .../AArch64/ptrauth-resolver-attributes.c     | 41 ------------
 .../CodeGen/AArch64/resolver-attributes.c     | 62 +++++++++++++++++++
 clang/test/CodeGen/attr-target-clones.c       |  4 +-
 clang/test/CodeGen/attr-target-mv.c           |  6 +-
 8 files changed, 141 insertions(+), 63 deletions(-)
 create mode 100644 clang/test/CodeGen/AArch64/ptrauth-fmv.c
 delete mode 100644 clang/test/CodeGen/AArch64/ptrauth-resolver-attributes.c
 create mode 100644 clang/test/CodeGen/AArch64/resolver-attributes.c

diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 9d48b1fd6e462..8c5af9e75e33f 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -4610,10 +4610,6 @@ void CodeGenModule::emitMultiVersionFunctions() {
     }
     llvm::Function *ResolverFunc = cast<llvm::Function>(ResolverConstant);
 
-    ResolverFunc->setLinkage(getMultiversionLinkage(*this, GD));
-    setGlobalVisibility(ResolverFunc, /*D=*/nullptr);
-    setDSOLocal(ResolverFunc);
-
     if (!ResolverFunc->hasLocalLinkage() && supportsCOMDAT())
       ResolverFunc->setComdat(
           getModule().getOrInsertComdat(ResolverFunc->getName()));
@@ -4626,6 +4622,7 @@ void CodeGenModule::emitMultiVersionFunctions() {
         });
     CodeGenFunction CGF(*this);
     CGF.EmitMultiVersionResolver(ResolverFunc, Options);
+    setMultiVersionResolverAttributes(ResolverFunc, GD);
   }
 
   // Ensure that any additions to the deferred decls list caused by emitting a
@@ -4676,9 +4673,6 @@ void CodeGenModule::emitCPUDispatchDefinition(GlobalDecl GD) {
 
   auto *ResolverFunc = cast<llvm::Function>(GetOrCreateLLVMFunction(
       ResolverName, ResolverType, ResolverGD, /*ForVTable=*/false));
-  ResolverFunc->setLinkage(getMultiversionLinkage(*this, GD));
-  setGlobalVisibility(ResolverFunc, /*D=*/nullptr);
-  setDSOLocal(ResolverFunc);
 
   if (supportsCOMDAT())
     ResolverFunc->setComdat(
@@ -4745,6 +4739,7 @@ void CodeGenModule::emitCPUDispatchDefinition(GlobalDecl GD) {
 
   CodeGenFunction CGF(*this);
   CGF.EmitMultiVersionResolver(ResolverFunc, Options);
+  setMultiVersionResolverAttributes(ResolverFunc, GD);
 
   if (getTarget().supportsIFunc()) {
     llvm::GlobalValue::LinkageTypes Linkage = getMultiversionLinkage(*this, GD);
@@ -4836,13 +4831,6 @@ llvm::Constant *CodeGenModule::GetOrCreateMultiVersionResolver(GlobalDecl GD) {
   if (FD->isTargetMultiVersion() || FD->isTargetClonesMultiVersion())
     AddDeferredMultiVersionResolverToEmit(GD);
 
-  auto SetResolverAttrs = [&](llvm::Function *Resolver) {
-    // Set the default target-specific attributes, such as PAC and BTI ones on
-    // AArch64. Not passing Decl to prevent setting unrelated attributes,
-    // as Resolver can be shared by multiple declarations.
-    getTargetCodeGenInfo().setTargetAttributes(/*D=*/nullptr, Resolver, *this);
-  };
-
   // For cpu_specific, don't create an ifunc yet because we don't know if the
   // cpu_dispatch will be emitted in this translation unit.
   if (ShouldReturnIFunc) {
@@ -4857,7 +4845,6 @@ llvm::Constant *CodeGenModule::GetOrCreateMultiVersionResolver(GlobalDecl GD) {
                                   "", Resolver, &getModule());
     GIF->setName(ResolverName);
     SetCommonAttributes(FD, GIF);
-    SetResolverAttrs(cast<llvm::Function>(Resolver));
     if (ResolverGV)
       replaceDeclarationWith(ResolverGV, GIF);
     return GIF;
@@ -4868,10 +4855,29 @@ llvm::Constant *CodeGenModule::GetOrCreateMultiVersionResolver(GlobalDecl GD) {
   assert(isa<llvm::GlobalValue>(Resolver) && !ResolverGV &&
          "Resolver should be created for the first time");
   SetCommonAttributes(FD, cast<llvm::GlobalValue>(Resolver));
-  SetResolverAttrs(cast<llvm::Function>(Resolver));
   return Resolver;
 }
 
+void CodeGenModule::setMultiVersionResolverAttributes(llvm::Function *Resolver,
+                                                      GlobalDecl GD) {
+  const NamedDecl *D = dyn_cast_or_null<NamedDecl>(GD.getDecl());
+  Resolver->setLinkage(getMultiversionLinkage(*this, GD));
+
+  // Function body has to be emitted before calling setGlobalVisibility
+  // for Resolver to be considered as definition.
+  setGlobalVisibility(Resolver, D);
+
+  setDSOLocal(Resolver);
+
+  // Set the default target-specific attributes, such as PAC and BTI ones on
+  // AArch64. Not passing Decl to prevent setting unrelated attributes,
+  // as Resolver can be shared by multiple declarations.
+  // FIXME Some targets may require a non-null D to set some attributes
+  //       (such as "stackrealign" on X86, even when it is requested via
+  //       "-mstackrealign" command line option).
+  getTargetCodeGenInfo().setTargetAttributes(/*D=*/nullptr, Resolver, *this);
+}
+
 bool CodeGenModule::shouldDropDLLAttribute(const Decl *D,
                                            const llvm::GlobalValue *GV) const {
   auto SC = GV->getDLLStorageClass();
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index 8b1ac2d976c5e..3971b296b3f80 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1851,6 +1851,15 @@ class CodeGenModule : public CodeGenTypeCache {
   // that feature and for a regular function (llvm::GlobalValue) otherwise.
   llvm::Constant *GetOrCreateMultiVersionResolver(GlobalDecl GD);
 
+  // Set attributes to a resolver function generated by Clang.
+  // GD is either the cpu_dispatch declaration or an arbitrarily chosen
+  // function declaration that triggered the implicit generation of this
+  // resolver function.
+  //
+  /// NOTE: This should only be called for definitions.
+  void setMultiVersionResolverAttributes(llvm::Function *Resolver,
+                                         GlobalDecl GD);
+
   // In scenarios where a function is not known to be a multiversion function
   // until a later declaration, it is sometimes necessary to change the
   // previously created mangled name to align with requirements of whatever
diff --git a/clang/test/CodeGen/AArch64/fmv-resolver-emission.c b/clang/test/CodeGen/AArch64/fmv-resolver-emission.c
index 591625d4d0da1..193ee9556cab0 100644
--- a/clang/test/CodeGen/AArch64/fmv-resolver-emission.c
+++ b/clang/test/CodeGen/AArch64/fmv-resolver-emission.c
@@ -328,7 +328,7 @@ __attribute__((target_clones("aes"))) void clones_without_default(void) {}
 // CHECK-NEXT:    ret void
 //
 //
-// CHECK-LABEL: define {{[^@]+}}@internal_func.resolver() {
+// CHECK-LABEL: define {{[^@]+}}@internal_func.resolver() comdat {
 // CHECK-NEXT:  resolver_entry:
 // CHECK-NEXT:    call void @__init_cpu_features_resolver()
 // CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
diff --git a/clang/test/CodeGen/AArch64/ptrauth-fmv.c b/clang/test/CodeGen/AArch64/ptrauth-fmv.c
new file mode 100644
index 0000000000000..3b60ea7412f1b
--- /dev/null
+++ b/clang/test/CodeGen/AArch64/ptrauth-fmv.c
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -mbranch-target-enforce -msign-return-address=all      -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,BTI-SIGNRA %s
+// RUN: %clang_cc1 -triple arm64-apple-ios   -mbranch-target-enforce -msign-return-address=all      -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,BTI-SIGNRA %s
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-returns -fptrauth-auth-traps -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,PAUTHTEST  %s
+// RUN: %clang_cc1 -triple arm64-apple-ios   -fptrauth-calls -fptrauth-returns -fptrauth-auth-traps -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,PAUTHTEST  %s
+
+// Check that both multi-versioned functions themselves and corresponding
+// resolvers generated by Clang have the correct PAC/BTI attributes.
+
+int __attribute__((target_clones("crc", "default"))) global_target_clones(void) { return 0; }
+
+int __attribute__((target_version("crc")))     global_target_version(void) { return 0; }
+int __attribute__((target_version("default"))) global_target_version(void) { return 0; }
+
+static int __attribute__((target_clones("crc", "default"))) static_target_clones(void) { return 0; }
+
+static int __attribute__((target_version("crc")))     static_target_version(void) { return 0; }
+static int __attribute__((target_version("default"))) static_target_version(void) { return 0; }
+
+// Force emission of static_* functions.
+void *get_ptr1(void) { return static_target_clones; }
+void *get_ptr2(void) { return static_target_version; }
+
+// CHECK-DAG: define{{( dso_local)?}} i32 @global_target_clones._Mcrc()    #[[ATTR_CRC:[0-9]+]]
+// CHECK-DAG: define{{( dso_local)?}} i32 @global_target_clones.default()  #[[ATTR_DEFAULT:[0-9]+]]
+// CHECK-DAG: define weak_odr         ptr @global_target_clones.resolver() #[[ATTR_RESOLVER:[0-9]+]]
+// CHECK-DAG: define{{( dso_local)?}} i32 @global_target_version._Mcrc()    #[[ATTR_CRC]]
+// CHECK-DAG: define{{( dso_local)?}} i32 @global_target_version.default()  #[[ATTR_DEFAULT]]
+// CHECK-DAG: define weak_odr         ptr @global_target_version.resolver() #[[ATTR_RESOLVER]]
+
+// CHECK-DAG: define internal i32 @static_target_clones._Mcrc()    #[[ATTR_CRC:[0-9]+]]
+// CHECK-DAG: define internal i32 @static_target_clones.default()  #[[ATTR_DEFAULT:[0-9]+]]
+// CHECK-DAG: define internal ptr @static_target_clones.resolver() #[[ATTR_RESOLVER:[0-9]+]]
+// CHECK-DAG: define internal i32 @static_target_version._Mcrc()    #[[ATTR_CRC]]
+// CHECK-DAG: define internal i32 @static_target_version.default()  #[[ATTR_DEFAULT]]
+// CHECK-DAG: define internal ptr @static_target_version.resolver() #[[ATTR_RESOLVER]]
+
+// BTI-SIGNRA-DAG: attributes #[[ATTR_CRC]]      = { {{.*}}"branch-target-enforcement" {{.*}}"sign-return-address"="all" "sign-return-address-key"="a_key"{{.*}} }
+// BTI-SIGNRA-DAG: attributes #[[ATTR_RESOLVER]] = { {{.*}}"branch-target-enforcement" {{.*}}"sign-return-address"="all" "sign-return-address-key"="a_key"{{.*}} }
+// BTI-SIGNRA-DAG: attributes #[[ATTR_DEFAULT]]  = { {{.*}}"branch-target-enforcement" {{.*}}"sign-return-address"="all" "sign-return-address-key"="a_key"{{.*}} }
+// PAUTHTEST-DAG:  attributes #[[ATTR_CRC]]      = { {{.*}}"ptrauth-auth-traps" "ptrauth-calls" "ptrauth-returns"{{.*}} }
+// PAUTHTEST-DAG:  attributes #[[ATTR_RESOLVER]] = { {{.*}}"ptrauth-auth-traps" "ptrauth-calls" "ptrauth-returns"{{.*}} }
+// PAUTHTEST-DAG:  attributes #[[ATTR_DEFAULT]]  = { {{.*}}"ptrauth-auth-traps" "ptrauth-calls" "ptrauth-returns"{{.*}} }
diff --git a/clang/test/CodeGen/AArch64/ptrauth-resolver-attributes.c b/clang/test/CodeGen/AArch64/ptrauth-resolver-attributes.c
deleted file mode 100644
index edb30cf491fda..0000000000000
--- a/clang/test/CodeGen/AArch64/ptrauth-resolver-attributes.c
+++ /dev/null
@@ -1,41 +0,0 @@
-// RUN: %clang_cc1 -triple aarch64-linux-gnu -mbranch-target-enforce -msign-return-address=all      -emit-llvm %s -o - | FileCheck -DLINKVIS="dso_local " --check-prefixes=CHECK,BTI-SIGNRA %s
-// RUN: %clang_cc1 -triple arm64-apple-ios   -mbranch-target-enforce -msign-return-address=all      -emit-llvm %s -o - | FileCheck -DLINKVIS=""           --check-prefixes=CHECK,BTI-SIGNRA %s
-// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-returns -fptrauth-auth-traps -emit-llvm %s -o - | FileCheck -DLINKVIS="dso_local " --check-prefixes=CHECK,PAUTHTEST %s
-// RUN: %clang_cc1 -triple arm64-apple-ios   -fptrauth-calls -fptrauth-returns -fptrauth-auth-traps -emit-llvm %s -o - | FileCheck -DLINKVIS=""           --check-prefixes=CHECK,PAUTHTEST %s
-
-// Check that resolver functions generated by clang have the correct attributes.
-
-int __attribute__((target_clones("crc", "default"))) ftc(void) { return 0; }
-
-int __attribute__((target_version("crc")))     fmv(void) { return 0; }
-int __attribute__((target_version("default"))) fmv(void) { return 0; }
-
-static int __attribute__((target_clones("crc", "default"))) static_ftc(void) { return 0; }
-
-static int __attribute__((target_version("crc")))     static_fmv(void) { return 0; }
-static int __attribute__((target_version("default"))) static_fmv(void) { return 0; }
-
-// Force emission of static_* functions.
-void *get_ptr1(void) { return static_ftc; }
-void *get_ptr2(void) { return static_fmv; }
-
-// CHECK-DAG: define [[LINKVIS]]i32 @ftc._Mcrc() #[[ATTR_CRC:[0-9]+]]
-// CHECK-DAG: define [[LINKVIS]]i32 @ftc.default() #[[ATTR_DEFAULT:[0-9]+]]
-// CHECK-DAG: define weak_odr ptr @ftc.resolver() #[[ATTR_RESOLVER:[0-9]+]]
-// CHECK-DAG: define [[LINKVIS]]i32 @fmv._Mcrc() #[[ATTR_CRC]]
-// CHECK-DAG: define [[LINKVIS]]i32 @fmv.default() #[[ATTR_DEFAULT]]
-// CHECK-DAG: define weak_odr ptr @fmv.resolver() #[[ATTR_RESOLVER]]
-
-// CHECK-DAG: define internal i32 @static_ftc._Mcrc() #[[ATTR_CRC:[0-9]+]]
-// CHECK-DAG: define internal i32 @static_ftc.default() #[[ATTR_DEFAULT:[0-9]+]]
-// CHECK-DAG: define internal ptr @static_ftc.resolver() #[[ATTR_RESOLVER:[0-9]+]]
-// CHECK-DAG: define internal i32 @static_fmv._Mcrc() #[[ATTR_CRC]]
-// CHECK-DAG: define internal i32 @static_fmv.default() #[[ATTR_DEFAULT]]
-// CHECK-DAG: define internal ptr @static_fmv.resolver() #[[ATTR_RESOLVER]]
-
-// BTI-SIGNRA-DAG: attributes #[[ATTR_CRC]]      = { {{.*}}"branch-target-enforcement" {{.*}}"sign-return-address"="all" "sign-return-address-key"="a_key"{{.*}} }
-// BTI-SIGNRA-DAG: attributes #[[ATTR_RESOLVER]] = { {{.*}}"branch-target-enforcement" {{.*}}"sign-return-address"="all" "sign-return-address-key"="a_key"{{.*}} }
-// BTI-SIGNRA-DAG: attributes #[[ATTR_DEFAULT]]  = { {{.*}}"branch-target-enforcement" {{.*}}"sign-return-address"="all" "sign-return-address-key"="a_key"{{.*}} }
-// PAUTHTEST-DAG:  attributes #[[ATTR_CRC]]      = { {{.*}}"ptrauth-auth-traps" "ptrauth-calls" "ptrauth-returns"{{.*}} }
-// PAUTHTEST-DAG:  attributes #[[ATTR_RESOLVER]] = { {{.*}}"ptrauth-auth-traps" "ptrauth-calls" "ptrauth-returns"{{.*}} }
-// PAUTHTEST-DAG:  attributes #[[ATTR_DEFAULT]]  = { {{.*}}"ptrauth-auth-traps" "ptrauth-calls" "ptrauth-returns"{{.*}} }
diff --git a/clang/test/CodeGen/AArch64/resolver-attributes.c b/clang/test/CodeGen/AArch64/resolver-attributes.c
new file mode 100644
index 0000000000000..a1263ded46565
--- /dev/null
+++ b/clang/test/CodeGen/AArch64/resolver-attributes.c
@@ -0,0 +1,62 @@
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -mbranch-target-enforce    -emit-llvm %s -o - | FileCheck --check-prefixes=BTI,ELF    %s
+// RUN: %clang_cc1 -triple arm64-apple-ios   -mbranch-target-enforce    -emit-llvm %s -o - | FileCheck --check-prefixes=BTI        %s
+// RUN: %clang_cc1 -triple aarch64-linux-gnu                            -emit-llvm %s -o - | FileCheck --check-prefixes=NOBTI,ELF  %s
+// RUN: %clang_cc1 -triple arm64-apple-ios                              -emit-llvm %s -o - | FileCheck --check-prefixes=NOBTI      %s
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -fvisibility=hidden        -emit-llvm %s -o - | FileCheck --check-prefixes=HIDDEN,ELF %s
+// RUN: %clang_cc1 -triple arm64-apple-ios   -fvisibility=hidden        -emit-llvm %s -o - | FileCheck --check-prefixes=HIDDEN     %s
+
+// Check that the resolver functions generated by Clang have the correct attributes.
+// In these test cases, branch-target-enforcement is used as an example of
+// target-specific attribute that has to be set on every function by default.
+
+// FIXME: `cpu_specific`/`cpu_dispatch` and `target` attributes cannot be
+//        tested on AArch64.
+
+__attribute__((target_clones("crc", "default")))
+int global_target_clones(void) { return 0; }
+
+__attribute__((target_version("crc")))     int global_target_version(void) { return 0; }
+__attribute__((target_version("default"))) int global_target_version(void) { return 0; }
+
+__attribute__((target_clones("crc", "default")))
+static int static_target_clones(void) { return 0; }
+
+__attribute__((target_version("crc")))     static int  static_target_version(void) { return 0; }
+__attribute__((target_version("default"))) static int  static_target_version(void) { return 0; }
+
+// Force emission of static_* functions.
+void *get_ptr1(void) { return static_target_clones; }
+void *get_ptr2(void) { return static_target_version; }
+
+#ifdef __ELF__
+// Make sure target-specific attributes can be overriden as needed for
+// non-autogenerated resolver functions.
+// Note that since there is only a single definition of ifunc_resolver, it
+// is not itself a multi-versioned function, even though it has target(...)
+// attribute.
+int ifunc_func(void) { return 0; }
+__attribute__((target("branch-protection=bti"))) void *ifunc_resolver(void) { return ifunc_func; }
+__attribute__((ifunc("ifunc_resolver"))) int ifunc(void);
+#endif
+
+// ELF: define{{.*}} ptr @ifunc_resolver() #[[ATTR_IFUNC_RESOLVER:[0-9]+]]
+
+// BTI: define weak_odr ptr @global_target_clones.resolver()  #[[ATTR_RESOLVER:[0-9]+]]
+// BTI: define weak_odr ptr @global_target_version.resolver() #[[ATTR_RESOLVER]]
+// BTI: define internal ptr @static_target_clones.resolver()  #[[ATTR_RESOLVER]]
+// BTI: define internal ptr @static_target_version.resolver() #[[ATTR_RESOLVER]]
+
+// In NOBTI case, no attribute groups are assigned to the resolver functions:
+// NOBTI: define weak_odr ptr @global_target_clones.resolver(){{[^#]*}} {
+// NOBTI: define weak_odr ptr @global_target_version.resolver(){{[^#]*}} {
+// NOBTI: define internal ptr @static_target_clones.resolver(){{[^#]*}} {
+// NOBTI: define internal ptr @static_target_version.resolver(){{[^#]*}} {
+
+// HIDDEN: define weak_odr hidden ptr @global_target_clones.resolver(){{[^#]*}}
+// HIDDEN: define weak_odr hidden ptr @global_target_version.resolver(){{[^#]*}}
+// HIDDEN: define internal ptr @static_target_clones.resolver(){{[^#]*}}
+// HIDDEN: define internal ptr @static_target_version.resolver(){{[^#]*}}
+
+// ELF:       attributes #[[ATTR_IFUNC_RESOLVER]] = { {{.*}}"branch-target-enforcement"{{.*}} }
+
+// BTI:       attributes #[[ATTR_RESOLVER]] = { {{.*}}"branch-target-enforcement"{{.*}} }
diff --git a/clang/test/CodeGen/attr-target-clones.c b/clang/test/CodeGen/attr-target-clones.c
index 3256db061f9a2..e11daa67192dc 100644
--- a/clang/test/CodeGen/attr-target-clones.c
+++ b/clang/test/CodeGen/attr-target-clones.c
@@ -44,9 +44,9 @@
 static int __attribute__((target_clones("sse4.2, default"))) internal(void) { return 0; }
 int use(void) { return internal(); }
 /// Internal linkage resolvers do not use comdat.
-// LINUX: define internal ptr @internal.resolver() {
+// LINUX: define internal ptr @internal.resolver() comdat {
 // DARWIN: define internal ptr @internal.resolver() {
-// WINDOWS: define internal i32 @internal() {
+// WINDOWS: define internal i32 @internal() comdat {
 
 int __attribute__((target_clones("sse4.2, default"))) foo(void) { return 0; }
 // LINUX: define {{.*}}i32 @foo.sse4.2.0()
diff --git a/clang/test/CodeGen/attr-target-mv.c b/clang/test/CodeGen/attr-target-mv.c
index 07f47d93cd29c..6699326ef834f 100644
--- a/clang/test/CodeGen/attr-target-mv.c
+++ b/clang/test/CodeGen/attr-target-mv.c
@@ -283,9 +283,9 @@ void calls_pr50025c(void) { pr50025c(); }
 // WINDOWS: call i32 @foo
 
 /// Internal linkage resolvers do not use comdat.
-// ITANIUM: define internal ptr @foo_internal.resolver() {
-
-// WINDOWS: define internal i32 @foo_internal.resolver() {
+// LINUX:   define internal ptr @foo_internal.resolver() comdat {
+// DARWIN:  define internal ptr @foo_internal.resolver() {
+// WINDOWS: define internal i32 @foo_internal.resolver() comdat {
 
 // ITANIUM: define{{.*}} i32 @bar2()
 // ITANIUM: call i32 @foo_inline.ifunc()

>From 4eafbe4fafca25266ad208c0e71ad25749b2f9fa Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Wed, 17 Sep 2025 14:44:05 +0300
Subject: [PATCH 7/8] Fix setting comdats on resolver functions

---
 clang/lib/CodeGen/CodeGenModule.cpp                | 8 ++++----
 clang/test/CodeGen/AArch64/fmv-resolver-emission.c | 2 +-
 clang/test/CodeGen/attr-target-clones.c            | 4 ++--
 clang/test/CodeGen/attr-target-mv.c                | 6 +++---
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 8c5af9e75e33f..0b660e3daaf81 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -4610,10 +4610,6 @@ void CodeGenModule::emitMultiVersionFunctions() {
     }
     llvm::Function *ResolverFunc = cast<llvm::Function>(ResolverConstant);
 
-    if (!ResolverFunc->hasLocalLinkage() && supportsCOMDAT())
-      ResolverFunc->setComdat(
-          getModule().getOrInsertComdat(ResolverFunc->getName()));
-
     const TargetInfo &TI = getTarget();
     llvm::stable_sort(
         Options, [&TI](const CodeGenFunction::FMVResolverOption &LHS,
@@ -4622,7 +4618,11 @@ void CodeGenModule::emitMultiVersionFunctions() {
         });
     CodeGenFunction CGF(*this);
     CGF.EmitMultiVersionResolver(ResolverFunc, Options);
+
     setMultiVersionResolverAttributes(ResolverFunc, GD);
+    if (!ResolverFunc->hasLocalLinkage() && supportsCOMDAT())
+      ResolverFunc->setComdat(
+          getModule().getOrInsertComdat(ResolverFunc->getName()));
   }
 
   // Ensure that any additions to the deferred decls list caused by emitting a
diff --git a/clang/test/CodeGen/AArch64/fmv-resolver-emission.c b/clang/test/CodeGen/AArch64/fmv-resolver-emission.c
index 193ee9556cab0..591625d4d0da1 100644
--- a/clang/test/CodeGen/AArch64/fmv-resolver-emission.c
+++ b/clang/test/CodeGen/AArch64/fmv-resolver-emission.c
@@ -328,7 +328,7 @@ __attribute__((target_clones("aes"))) void clones_without_default(void) {}
 // CHECK-NEXT:    ret void
 //
 //
-// CHECK-LABEL: define {{[^@]+}}@internal_func.resolver() comdat {
+// CHECK-LABEL: define {{[^@]+}}@internal_func.resolver() {
 // CHECK-NEXT:  resolver_entry:
 // CHECK-NEXT:    call void @__init_cpu_features_resolver()
 // CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
diff --git a/clang/test/CodeGen/attr-target-clones.c b/clang/test/CodeGen/attr-target-clones.c
index e11daa67192dc..3256db061f9a2 100644
--- a/clang/test/CodeGen/attr-target-clones.c
+++ b/clang/test/CodeGen/attr-target-clones.c
@@ -44,9 +44,9 @@
 static int __attribute__((target_clones("sse4.2, default"))) internal(void) { return 0; }
 int use(void) { return internal(); }
 /// Internal linkage resolvers do not use comdat.
-// LINUX: define internal ptr @internal.resolver() comdat {
+// LINUX: define internal ptr @internal.resolver() {
 // DARWIN: define internal ptr @internal.resolver() {
-// WINDOWS: define internal i32 @internal() comdat {
+// WINDOWS: define internal i32 @internal() {
 
 int __attribute__((target_clones("sse4.2, default"))) foo(void) { return 0; }
 // LINUX: define {{.*}}i32 @foo.sse4.2.0()
diff --git a/clang/test/CodeGen/attr-target-mv.c b/clang/test/CodeGen/attr-target-mv.c
index 6699326ef834f..07f47d93cd29c 100644
--- a/clang/test/CodeGen/attr-target-mv.c
+++ b/clang/test/CodeGen/attr-target-mv.c
@@ -283,9 +283,9 @@ void calls_pr50025c(void) { pr50025c(); }
 // WINDOWS: call i32 @foo
 
 /// Internal linkage resolvers do not use comdat.
-// LINUX:   define internal ptr @foo_internal.resolver() comdat {
-// DARWIN:  define internal ptr @foo_internal.resolver() {
-// WINDOWS: define internal i32 @foo_internal.resolver() comdat {
+// ITANIUM: define internal ptr @foo_internal.resolver() {
+
+// WINDOWS: define internal i32 @foo_internal.resolver() {
 
 // ITANIUM: define{{.*}} i32 @bar2()
 // ITANIUM: call i32 @foo_inline.ifunc()

>From f25b5c55965b06ca58df234b551fe3c6eca279c5 Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Wed, 17 Sep 2025 15:51:47 +0300
Subject: [PATCH 8/8] resolver-attributes.c: the only expected variability for
 NOBTI and HIDDEN is optional comdat

---
 .../test/CodeGen/AArch64/resolver-attributes.c | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/clang/test/CodeGen/AArch64/resolver-attributes.c b/clang/test/CodeGen/AArch64/resolver-attributes.c
index a1263ded46565..6e4497cdc8611 100644
--- a/clang/test/CodeGen/AArch64/resolver-attributes.c
+++ b/clang/test/CodeGen/AArch64/resolver-attributes.c
@@ -47,15 +47,15 @@ __attribute__((ifunc("ifunc_resolver"))) int ifunc(void);
 // BTI: define internal ptr @static_target_version.resolver() #[[ATTR_RESOLVER]]
 
 // In NOBTI case, no attribute groups are assigned to the resolver functions:
-// NOBTI: define weak_odr ptr @global_target_clones.resolver(){{[^#]*}} {
-// NOBTI: define weak_odr ptr @global_target_version.resolver(){{[^#]*}} {
-// NOBTI: define internal ptr @static_target_clones.resolver(){{[^#]*}} {
-// NOBTI: define internal ptr @static_target_version.resolver(){{[^#]*}} {
-
-// HIDDEN: define weak_odr hidden ptr @global_target_clones.resolver(){{[^#]*}}
-// HIDDEN: define weak_odr hidden ptr @global_target_version.resolver(){{[^#]*}}
-// HIDDEN: define internal ptr @static_target_clones.resolver(){{[^#]*}}
-// HIDDEN: define internal ptr @static_target_version.resolver(){{[^#]*}}
+// NOBTI: define weak_odr ptr @global_target_clones.resolver(){{( comdat)?}} {
+// NOBTI: define weak_odr ptr @global_target_version.resolver(){{( comdat)?}} {
+// NOBTI: define internal ptr @static_target_clones.resolver() {
+// NOBTI: define internal ptr @static_target_version.resolver() {
+
+// HIDDEN: define weak_odr hidden ptr @global_target_clones.resolver(){{( comdat)?}} {
+// HIDDEN: define weak_odr hidden ptr @global_target_version.resolver(){{( comdat)?}} {
+// HIDDEN: define internal ptr @static_target_clones.resolver() {
+// HIDDEN: define internal ptr @static_target_version.resolver() {
 
 // ELF:       attributes #[[ATTR_IFUNC_RESOLVER]] = { {{.*}}"branch-target-enforcement"{{.*}} }
 



More information about the cfe-commits mailing list