diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 8ceecff28cbc6..c0070b071e7a8 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1171,22 +1171,29 @@ void CodeGenModule::Release() { "tag-stack-memory-buildattr", 1); if (T.isARM() || T.isThumb() || T.isAArch64()) { + // Previously 1 is used and meant for the backed to derive the function + // attribute form it. 2 now means function attributes already set for all + // functions in this module, so no need to propagate those from the module + // flag. Value is only used in case of LTO module merge because the backend + // will see all required function attribute set already. Value is used + // before modules got merged. Any posive value means the feature is active + // and required binary markings need to be emit accordingly. if (LangOpts.BranchTargetEnforcement) getModule().addModuleFlag(llvm::Module::Min, "branch-target-enforcement", - 1); + 2); if (LangOpts.BranchProtectionPAuthLR) getModule().addModuleFlag(llvm::Module::Min, "branch-protection-pauth-lr", - 1); + 2); if (LangOpts.GuardedControlStack) - getModule().addModuleFlag(llvm::Module::Min, "guarded-control-stack", 1); + getModule().addModuleFlag(llvm::Module::Min, "guarded-control-stack", 2); if (LangOpts.hasSignReturnAddress()) - getModule().addModuleFlag(llvm::Module::Min, "sign-return-address", 1); + getModule().addModuleFlag(llvm::Module::Min, "sign-return-address", 2); if (LangOpts.isSignReturnAddressScopeAll()) getModule().addModuleFlag(llvm::Module::Min, "sign-return-address-all", - 1); + 2); if (!LangOpts.isSignReturnAddressWithAKey()) getModule().addModuleFlag(llvm::Module::Min, - "sign-return-address-with-bkey", 1); + "sign-return-address-with-bkey", 2); } if (CodeGenOpts.StackClashProtector) diff --git a/clang/test/CodeGen/aarch64-sign-return-address.c b/clang/test/CodeGen/aarch64-sign-return-address.c index 8bc54b1a56c38..35c56889e0707 100644 --- a/clang/test/CodeGen/aarch64-sign-return-address.c +++ b/clang/test/CodeGen/aarch64-sign-return-address.c @@ -22,17 +22,17 @@ // NONE-NOT: !"branch-target-enforcement" // ALL-NOT: !"branch-target-enforcement" // PART-NOT: !"branch-target-enforcement" -// BTE: !{i32 8, !"branch-target-enforcement", i32 1} +// BTE: !{i32 8, !"branch-target-enforcement", i32 2} // B-KEY-NOT: !"branch-target-enforcement" // NONE-NOT: !"sign-return-address" -// ALL: !{i32 8, !"sign-return-address", i32 1} -// PART: !{i32 8, !"sign-return-address", i32 1} +// ALL: !{i32 8, !"sign-return-address", i32 2} +// PART: !{i32 8, !"sign-return-address", i32 2} // BTE-NOT: !"sign-return-address" -// B-KEY: !{i32 8, !"sign-return-address", i32 1} +// B-KEY: !{i32 8, !"sign-return-address", i32 2} // NONE-NOT: !"sign-return-address-all" -// ALL: !{i32 8, !"sign-return-address-all", i32 1} +// ALL: !{i32 8, !"sign-return-address-all", i32 2} // PART-NOT: !"sign-return-address-all" // BTE-NOT: !"sign-return-address-all" // B-KEY-NOT: !"sign-return-address-all" @@ -41,6 +41,6 @@ // ALL-NOT: !"sign-return-address-with-bkey" // PART-NOT: !"sign-return-address-with-bkey" // BTE-NOT: !"sign-return-address-with-bkey" -// B-KEY: !{i32 8, !"sign-return-address-with-bkey", i32 1} +// B-KEY: !{i32 8, !"sign-return-address-with-bkey", i32 2} void foo() {} diff --git a/clang/test/CodeGen/arm-branch-protection-attr-2.c b/clang/test/CodeGen/arm-branch-protection-attr-2.c index 1f3c00873043e..741c0026c4d05 100644 --- a/clang/test/CodeGen/arm-branch-protection-attr-2.c +++ b/clang/test/CodeGen/arm-branch-protection-attr-2.c @@ -18,16 +18,16 @@ // NONE-NOT: !"branch-target-enforcement" // PART-NOT: !"branch-target-enforcement" // ALL-NOT: !"branch-target-enforcement" -// BTE: !{i32 8, !"branch-target-enforcement", i32 1} +// BTE: !{i32 8, !"branch-target-enforcement", i32 2} // NONE-NOT: !"sign-return-address" -// PART: !{i32 8, !"sign-return-address", i32 1} -// ALL: !{i32 8, !"sign-return-address", i32 1} +// PART: !{i32 8, !"sign-return-address", i32 2} +// ALL: !{i32 8, !"sign-return-address", i32 2} // BTE-NOT: !"sign-return-address" // NONE-NOT: !"sign-return-address-all", i32 0} // PART-NOT: !"sign-return-address-all", i32 0} -// ALL: !{i32 8, !"sign-return-address-all", i32 1} +// ALL: !{i32 8, !"sign-return-address-all", i32 2} // BTE-NOT: !"sign-return-address-all", i32 0} void foo() {} diff --git a/clang/test/Frontend/arm-ignore-branch-protection-option.c b/clang/test/Frontend/arm-ignore-branch-protection-option.c index 99a2accef3ae2..45bdb37f5ed1a 100644 --- a/clang/test/Frontend/arm-ignore-branch-protection-option.c +++ b/clang/test/Frontend/arm-ignore-branch-protection-option.c @@ -15,4 +15,4 @@ __attribute__((target("arch=cortex-m0"))) void f() {} // CHECK-NOT: attributes { {{.*}} "branch-target-enforcement" /// Check that there are branch protection module attributes despite the warning. -// CHECK: !{i32 8, !"branch-target-enforcement", i32 1} +// CHECK: !{i32 8, !"branch-target-enforcement", i32 2} diff --git a/lld/test/ELF/lto/aarch64_inline.ll b/lld/test/ELF/lto/aarch64_inline.ll new file mode 100644 index 0000000000000..c8b6d7a81ac42 --- /dev/null +++ b/lld/test/ELF/lto/aarch64_inline.ll @@ -0,0 +1,71 @@ +; REQUIRES: aarch64 +;; Test verifies inlining happens cross module when module flags are upgraded +;; by the thin-lto linker/IRMover. +;; Regression test for #82763 + +; RUN: split-file %s %t +; RUN: opt -thinlto-bc -thinlto-split-lto-unit -unified-lto %t/foo.s -o %t/foo.o +; RUN: opt -thinlto-bc -thinlto-split-lto-unit -unified-lto %t/bar.s -o %t/bar.o +; RUN: opt -thinlto-bc -thinlto-split-lto-unit -unified-lto %t/main.s -o %t/main.o +; RUN: ld.lld -O2 --lto=thin --entry=main %t/main.o %t/foo.o %t/bar.o -o %t/exe +; RUN: llvm-objdump -d %t/exe | FileCheck %s + + +; CHECK-LABEL:
: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: mov w0, #0x23 +; CHECK-NEXT: autibsp +; CHECK-NEXT: ret + +;--- foo.s +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "aarch64-unknown-linux-gnu" + +define dso_local noundef i32 @foo() local_unnamed_addr #0 { +entry: + ret i32 34 +} + +attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) } +!llvm.module.flags = !{!0, !1, !2, !3 } +!0 = !{i32 8, !"branch-target-enforcement", i32 1} +!1 = !{i32 8, !"sign-return-address", i32 1} +!2 = !{i32 8, !"sign-return-address-all", i32 1} +!3 = !{i32 8, !"sign-return-address-with-bkey", i32 1} + +;--- bar.s +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "aarch64-unknown-linux-gnu" + +define dso_local noundef i32 @bar() local_unnamed_addr #0 { +entry: + ret i32 1 +} + +attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) "branch-target-enforcement"="true" "sign-return-address"="all" "sign-return-address-key"="b_key" } +!llvm.module.flags = !{!0, !1, !2, !3 } +!0 = !{i32 8, !"branch-target-enforcement", i32 2} +!1 = !{i32 8, !"sign-return-address", i32 2} +!2 = !{i32 8, !"sign-return-address-all", i32 2} +!3 = !{i32 8, !"sign-return-address-with-bkey", i32 2} + +;--- main.s +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "aarch64-unknown-linux-gnu" + +declare i32 @foo(); +declare i32 @bar(); + +define i32 @main() { +entry: + %1 = call i32 @foo() + %2 = call i32 @bar() + %3 = add i32 %1, %2 + ret i32 %3 +} + +!llvm.module.flags = !{!0, !1, !2, !3 } +!0 = !{i32 8, !"branch-target-enforcement", i32 1} +!1 = !{i32 8, !"sign-return-address", i32 1} +!2 = !{i32 8, !"sign-return-address-all", i32 1} +!3 = !{i32 8, !"sign-return-address-with-bkey", i32 1} diff --git a/llvm/include/llvm/IR/AutoUpgrade.h b/llvm/include/llvm/IR/AutoUpgrade.h index 152f781ffa9b3..1ef32bcb121be 100644 --- a/llvm/include/llvm/IR/AutoUpgrade.h +++ b/llvm/include/llvm/IR/AutoUpgrade.h @@ -88,6 +88,9 @@ namespace llvm { /// info. Return true if module is modified. bool UpgradeDebugInfo(Module &M); + /// Copies module attributes to the functions in the module. + void CopyModuleAttrToFunctions(Module &M); + /// Check whether a string looks like an old loop attachment tag. inline bool mayBeOldLoopAttachmentTag(StringRef Name) { return Name.starts_with("llvm.vectorizer."); diff --git a/llvm/lib/IR/AutoUpgrade.cpp b/llvm/lib/IR/AutoUpgrade.cpp index be0abb4b71dae..3c97c60ec044d 100644 --- a/llvm/lib/IR/AutoUpgrade.cpp +++ b/llvm/lib/IR/AutoUpgrade.cpp @@ -5178,6 +5178,58 @@ void llvm::UpgradeFunctionAttributes(Function &F) { Arg.removeAttrs(AttributeFuncs::typeIncompatible(Arg.getType())); } +// Check if the module attribute is present and set to one. +static bool isModuleAttributeOne(Module &M, const StringRef &ModAttr) { + const auto *Attr = + mdconst::extract_or_null(M.getModuleFlag(ModAttr)); + return Attr && Attr->isOne(); +} + +// Check if the function attribute is not present and set it. +static void SetFunctionAttrIfNotSet(Function &F, StringRef FnAttrName, + StringRef Value) { + if (!F.hasFnAttribute(FnAttrName)) + F.addFnAttr(FnAttrName, Value); +} + +void llvm::CopyModuleAttrToFunctions(Module &M) { + Triple T(M.getTargetTriple()); + if (!T.isThumb() && !T.isARM() && !T.isAArch64()) + return; + + bool BTE = isModuleAttributeOne(M, "branch-target-enforcement"); + bool BPPLR = isModuleAttributeOne(M, "branch-protection-pauth-lr"); + bool GCS = isModuleAttributeOne(M, "guarded-control-stack"); + bool SRA = isModuleAttributeOne(M, "sign-return-address"); + + if (!BTE && !BPPLR && !GCS && !SRA) + return; + + StringRef SignTypeValue = "non-leaf"; + if (SRA && isModuleAttributeOne(M, "sign-return-address-all")) + SignTypeValue = "all"; + + StringRef SignKeyValue = "a_key"; + if (SRA && isModuleAttributeOne(M, "sign-return-address-with-bkey")) + SignKeyValue = "b_key"; + + for (Function &F : M.getFunctionList()) { + if (F.isDeclaration()) + continue; + + if (SRA) { + SetFunctionAttrIfNotSet(F, "sign-return-address", SignTypeValue); + SetFunctionAttrIfNotSet(F, "sign-return-address-key", SignKeyValue); + } + if (BTE) + SetFunctionAttrIfNotSet(F, "branch-target-enforcement", "true"); + if (BPPLR) + SetFunctionAttrIfNotSet(F, "branch-protection-pauth-lr", "true"); + if (GCS) + SetFunctionAttrIfNotSet(F, "guarded-control-stack", "true"); + } +} + static bool isOldLoopArgument(Metadata *MD) { auto *T = dyn_cast_or_null(MD); if (!T) diff --git a/llvm/lib/Linker/IRMover.cpp b/llvm/lib/Linker/IRMover.cpp index a7e6db82e5c23..b019cd201313d 100644 --- a/llvm/lib/Linker/IRMover.cpp +++ b/llvm/lib/Linker/IRMover.cpp @@ -1623,6 +1623,11 @@ Error IRLinker::run() { // Loop over all of the linked values to compute type mappings. computeTypeMapping(); + // Convert module level attributes to function level attributes because + // after merging modules the attributes might change and would have different + // effect on the functions as the original module would have. + CopyModuleAttrToFunctions(*SrcM); + std::reverse(Worklist.begin(), Worklist.end()); while (!Worklist.empty()) { GlobalValue *GV = Worklist.back(); @@ -1787,6 +1792,11 @@ IRMover::IRMover(Module &M) : Composite(M) { for (const auto *MD : StructTypes.getVisitedMetadata()) { SharedMDs[MD].reset(const_cast(MD)); } + + // Convert module level attributes to function level attributes because + // after merging modules the attributes might change and would have different + // effect on the functions as the original module would have. + CopyModuleAttrToFunctions(M); } Error IRMover::move(std::unique_ptr Src, diff --git a/llvm/test/LTO/AArch64/Inputs/bar.ll b/llvm/test/LTO/AArch64/Inputs/bar.ll new file mode 100644 index 0000000000000..1f0712660ebdc --- /dev/null +++ b/llvm/test/LTO/AArch64/Inputs/bar.ll @@ -0,0 +1,22 @@ +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "aarch64-unknown-linux-gnu" + +define dso_local void @bar() #0 { +entry: + ret void +} + +define dso_local void @baz() #1 { +entry: + ret void +} + +attributes #0 = { noinline nounwind optnone uwtable } +attributes #1 = { noinline nounwind optnone uwtable "branch-target-enforcement"="true" } + +!llvm.module.flags = !{!0, !1, !2, !3} + +!0 = !{i32 8, !"branch-target-enforcement", i32 2} +!1 = !{i32 8, !"sign-return-address", i32 2} +!2 = !{i32 8, !"sign-return-address-all", i32 2} +!3 = !{i32 8, !"sign-return-address-with-bkey", i32 2} diff --git a/llvm/test/LTO/AArch64/link-branch-target-enforcement.ll b/llvm/test/LTO/AArch64/link-branch-target-enforcement.ll index ccf8cf67ede6d..8313d812e189a 100644 --- a/llvm/test/LTO/AArch64/link-branch-target-enforcement.ll +++ b/llvm/test/LTO/AArch64/link-branch-target-enforcement.ll @@ -30,8 +30,11 @@ entry: ; CHECK-NOT: linking module flags 'branch-target-enforcement': IDs have conflicting values in ; CHECK-DUMP:
: +; CHECK-DUMP: paciasp +; CHECK-DUMP: str ; CHECK-DUMP: bl 0x8 ; CHECK-DUMP: : +; CHECK-DUMP: pacibsp ; `main` doesn't support BTI while `foo` does, so in the binary ; we should see only PAC which is supported by both. diff --git a/llvm/test/LTO/AArch64/link-sign-return-address.ll b/llvm/test/LTO/AArch64/link-sign-return-address.ll new file mode 100644 index 0000000000000..d5ddb7e3155a1 --- /dev/null +++ b/llvm/test/LTO/AArch64/link-sign-return-address.ll @@ -0,0 +1,56 @@ +; Testcase to check that module with different sign return address can +; be mixed. +; +; RUN: llvm-as %s -o %t1.bc +; RUN: llvm-as %p/Inputs/foo.ll -o %t2.bc +; RUN: llvm-as %p/Inputs/bar.ll -o %t3.bc +; RUN: llvm-lto -exported-symbol main \ +; RUN: -exported-symbol foo \ +; RUN: -filetype=obj \ +; RUN: %t3.bc %t2.bc %t1.bc \ +; RUN: -o %t1.exe 2>&1 +; RUN: llvm-objdump -d %t1.exe | FileCheck --check-prefix=CHECK-DUMP %s +; RUN: llvm-readelf -n %t1.exe | FileCheck --allow-empty --check-prefix=CHECK-PROP %s + +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "aarch64-unknown-linux-gnu" + +declare i32 @foo(); +declare void @baz(); +declare void @bar(); + +define i32 @main() { +entry: + %add = call i32 @foo() + call void @bar() + call void @baz() + ret i32 %add +} + +!llvm.module.flags = !{!0, !1, !2, !3 } +!0 = !{i32 8, !"branch-target-enforcement", i32 0} +!1 = !{i32 8, !"sign-return-address", i32 0} +!2 = !{i32 8, !"sign-return-address-all", i32 0} +!3 = !{i32 8, !"sign-return-address-with-bkey", i32 0} + + +; CHECK-DUMP: : +; CHECK-DUMP: ret +; CHECK-DUMP: : +; CHECK-DUMP: bti c +; CHECK-DUMP: ret +; CHECK-DUMP: : +; CHECK-DUMP: pacibsp +; CHECK-DUMP: mov w0, #0x2a +; CHECK-DUMP: autibsp +; CHECK-DUMP: ret +; CHECK-DUMP:
: +; CHECK-DUMP-NOT: paciasp +; CHECK-DUMP: str x30, +; CHECK-DUMP: bl 0x20 +; CHECK-DUMP: bl 0x0 +; CHECK-DUMP: bl 0x4 + +; `main` doesn't support PAC sign-return-address while `foo` does, so in the binary +; we should not see anything. +; CHECK-PROP-NOT: Properties: aarch64 feature: PAC \ No newline at end of file diff --git a/llvm/test/Linker/link-arm-and-thumb.ll b/llvm/test/Linker/link-arm-and-thumb.ll index a90f2128e4430..b5984bf557947 100644 --- a/llvm/test/Linker/link-arm-and-thumb.ll +++ b/llvm/test/Linker/link-arm-and-thumb.ll @@ -13,11 +13,11 @@ entry: ret i32 %add } -; CHECK: define i32 @main() { +; CHECK: define i32 @main() ; CHECK: define i32 @foo(i32 %a, i32 %b) [[ARM_ATTRS:#[0-9]+]] ; CHECK: define i32 @bar(i32 %a, i32 %b) [[THUMB_ATTRS:#[0-9]+]] -; CHECK: attributes [[ARM_ATTRS]] = { "target-features"="-thumb-mode" } -; CHECK: attributes [[THUMB_ATTRS]] = { "target-features"="+thumb-mode" } +; CHECK: attributes [[ARM_ATTRS]] = {{{.*}}"target-features"="-thumb-mode" } +; CHECK: attributes [[THUMB_ATTRS]] = {{{.*}}"target-features"="+thumb-mode" } ; STDERR-NOT: warning: Linking two modules of different target triples: