-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[LLVM] Refactor Autoupgrade function attributes from Module attributes. #84494
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
; 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/main.s -o %t/main.o | ||
; RUN: ld.lld -O2 --lto=thin --entry=main %t/main.o %t/foo.o -o %t/exe | ||
; RUN: llvm-objdump -d %t/exe | FileCheck %s | ||
|
||
|
||
; CHECK-LABEL: <main>: | ||
; CHECK-NEXT: pacibsp | ||
; CHECK-NEXT: mov w0, #0x22 | ||
; 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} | ||
|
||
;--- 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(); | ||
|
||
define i32 @main() { | ||
entry: | ||
%1 = call i32 @foo() | ||
ret i32 %1 | ||
} | ||
|
||
!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} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5178,6 +5178,53 @@ void llvm::UpgradeFunctionAttributes(Function &F) { | |
Arg.removeAttrs(AttributeFuncs::typeIncompatible(Arg.getType())); | ||
} | ||
|
||
// Check if the module attribute is present and not zero. | ||
static bool isModuleAttributeSet(Module &M, const StringRef &ModAttr) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently, this function treats "Attribute X is set with value 0" the same as "Attribute X is unset" for a given module. This behavior causes functions within modules with no attributes to be annotated with the |
||
const auto *Attr = | ||
mdconst::extract_or_null<ConstantInt>(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; | ||
|
||
StringRef SignTypeValue = "none"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since adding the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Answering my own question, it looks like uses of this function attribute fall back to the module's attribute if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dhoekwater I have a few changes that changes this behaviour to set flag only when enabled so if all dependency merged then the module flag won't be used anymore for function attribute so here could set only the positive flags. |
||
if (isModuleAttributeSet(M, "sign-return-address-all")) | ||
SignTypeValue = "all"; | ||
else if (isModuleAttributeSet(M, "sign-return-address")) | ||
SignTypeValue = "non-leaf"; | ||
|
||
StringRef BTEValue = | ||
isModuleAttributeSet(M, "branch-target-enforcement") ? "true" : "false"; | ||
StringRef BPPLValue = | ||
isModuleAttributeSet(M, "branch-protection-pauth-lr") ? "true" : "false"; | ||
StringRef GCSValue = | ||
isModuleAttributeSet(M, "guarded-control-stack") ? "true" : "false"; | ||
StringRef SignKeyValue = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like |
||
isModuleAttributeSet(M, "sign-return-address-with-bkey") ? "b_key" | ||
: "a_key"; | ||
|
||
for (Function &F : M.getFunctionList()) { | ||
if (F.isDeclaration()) | ||
continue; | ||
|
||
SetFunctionAttrIfNotSet(F, "sign-return-address", SignTypeValue); | ||
SetFunctionAttrIfNotSet(F, "branch-target-enforcement", BTEValue); | ||
SetFunctionAttrIfNotSet(F, "branch-protection-pauth-lr", BPPLValue); | ||
SetFunctionAttrIfNotSet(F, "guarded-control-stack", GCSValue); | ||
SetFunctionAttrIfNotSet(F, "sign-return-address-key", SignKeyValue); | ||
} | ||
} | ||
|
||
static bool isOldLoopArgument(Metadata *MD) { | ||
auto *T = dyn_cast_or_null<MDTuple>(MD); | ||
if (!T) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
; 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-lto -exported-symbol main \ | ||
; RUN: -exported-symbol foo \ | ||
; RUN: -filetype=obj \ | ||
; RUN: %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(); | ||
|
||
define i32 @main() { | ||
entry: | ||
%add = call i32 @foo() | ||
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: <foo>: | ||
; CHECK-DUMP: pacibsp | ||
; CHECK-DUMP: mov w0, #0x2a | ||
; CHECK-DUMP: autibsp | ||
; CHECK-DUMP: ret | ||
; CHECK-DUMP: <main>: | ||
; CHECK-DUMP-NOT: paciasp | ||
; CHECK-DUMP: str x30, | ||
; CHECK-DUMP: bl 0x14 <main+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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems unneeded. You can add ThinLTO tests to llvm/test/ThinLTO
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added the test here because I'd use
ld.lld
as it invokes the IRMover differently thanllvm-lto
.and under
llvm/test/ThinLTO
ld.lld
is not available.llvm-lto merges modules to an empty module, while ld.lld merges modules into the first module.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Drive by comment: To mimic the ThinLTO behavior of lld use llvm-lto2 not llvm-lto - the former uses the same LTO API (linker resolution-based) whereas the latter uses the legacy one.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @MaskRay and @teresajohnson !
Moved the test, cheers. (in #86212 )