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 <[email protected]> Date: Mon, 26 May 2025 22:28:55 +0300 Subject: [PATCH 1/7] [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 <[email protected]> Date: Wed, 4 Jun 2025 16:34:47 +0300 Subject: [PATCH 2/7] 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 <[email protected]> Date: Wed, 4 Jun 2025 17:36:20 +0300 Subject: [PATCH 3/7] 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 <[email protected]> Date: Mon, 14 Jul 2025 21:33:40 +0300 Subject: [PATCH 4/7] 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 <[email protected]> Date: Fri, 5 Sep 2025 00:11:23 +0300 Subject: [PATCH 5/7] [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 <[email protected]> Date: Fri, 5 Sep 2025 20:05:30 +0300 Subject: [PATCH 6/7] 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 <[email protected]> Date: Wed, 17 Sep 2025 14:44:05 +0300 Subject: [PATCH 7/7] 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() _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
