-
Notifications
You must be signed in to change notification settings - Fork 14.4k
__declspec(noshrinkwrap) support #147397
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
base: main
Are you sure you want to change the base?
__declspec(noshrinkwrap) support #147397
Conversation
@llvm/pr-subscribers-llvm-transforms @llvm/pr-subscribers-llvm-ir Author: Adam Glass (AdamGlass) ChangesProvide a mechanism to disable the shrinkwrapping process on an individual function via __declspec(noshrinkwrap). This is functionality is sometimes necessary in systems and exception handling code-. Other mechanisms for doing this are -enable-shrink-wrap=false and via llvm::TargetFrameLowering::enableShrinkWrapping() -- neither mechanism is particularly flexible or targeted. No example of similar documented functionality has been found. Implementation adds a Clang declspec which drives an LLVM function attribute. Full diff: https://github.com/llvm/llvm-project/pull/147397.diff 12 Files Affected:
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 340f439a45bb9..ba2d121bd7cf7 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -5190,3 +5190,10 @@ def NonString : InheritableAttr {
let Subjects = SubjectList<[Var, Field]>;
let Documentation = [NonStringDocs];
}
+
+def NoShrinkWrapping : InheritableAttr {
+ let Spellings = [Declspec<"noshrinkwrap">];
+ let Subjects = SubjectList<[Function]>;
+ let Documentation = [NoShrinkWrappingDocs];
+ let SimpleHandler = 1;
+}
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 43442f177ab7b..7c0be6d4a3f21 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -9427,3 +9427,11 @@ diagnostics with code like:
__attribute__((nonstring)) char NotAStr[3] = "foo"; // Not diagnosed
}];
}
+
+def NoShrinkWrappingDocs : Documentation {
+ let Category = DocCatFunction;
+ let Content = [{
+ The ``__declspec(noshrinkwrap)`` attribute disables the shrinkwrapping
+ process for this function.
+ }];
+}
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index e6d150f7e13d6..5d7d53cdbf3b9 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -2642,6 +2642,10 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D,
else if (isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPReq))
B.addAttribute(llvm::Attribute::StackProtectReq);
+ if (D && D->hasAttr<NoShrinkWrappingAttr>()) {
+ B.addAttribute(llvm::Attribute::NoShrinkWrap);
+ }
+
if (!D) {
// Non-entry HLSL functions must always be inlined.
if (getLangOpts().HLSL && !F->hasFnAttribute(llvm::Attribute::NoInline))
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index c5d5d03cc99c7..0b8a367238d23 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -7869,6 +7869,10 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_VTablePointerAuthentication:
handleVTablePointerAuthentication(S, D, AL);
break;
+
+ case ParsedAttr::AT_NoShrinkWrapping:
+ handleSimpleAttribute<NoShrinkWrappingAttr>(S, D, AL);
+ break;
}
}
diff --git a/clang/test/CodeGen/declspec-noshrinkwrap.c b/clang/test/CodeGen/declspec-noshrinkwrap.c
new file mode 100644
index 0000000000000..fee321875c2d3
--- /dev/null
+++ b/clang/test/CodeGen/declspec-noshrinkwrap.c
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -fdeclspec -emit-llvm %s -o - | FileCheck %s
+
+// Ensure that declspec results in a llvm function attribute
+__declspec(noshrinkwrap)
+int square(int a) {
+ return a*a;
+}
+
+// CHECK: Function Attrs: {{.*}} noshrinkwrap
+// CHECK-LABEL: @square
+// CHECK: attributes #0 {{.*}} noshrinkwrap
diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
index dc78eb4164acf..520dd4b7e528e 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -799,6 +799,7 @@ enum AttributeKindCodes {
ATTR_KIND_SANITIZE_TYPE = 101,
ATTR_KIND_CAPTURES = 102,
ATTR_KIND_DEAD_ON_RETURN = 103,
+ ATTR_KIND_NO_SHRINKWRAP = 104,
};
enum ComdatSelectionKindCodes {
diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td
index 112853965407c..8212be07ba291 100644
--- a/llvm/include/llvm/IR/Attributes.td
+++ b/llvm/include/llvm/IR/Attributes.td
@@ -402,6 +402,9 @@ def MarkedForWindowsSecureHotPatching
def AllowDirectAccessInHotPatchFunction
: StrBoolAttr<"allow_direct_access_in_hot_patch_function">;
+/// Function should not be shrinkwrapped
+def NoShrinkWrap : EnumAttr<"noshrinkwrap", IntersectPreserve, [FnAttr]>;
+
/// Target-independent string attributes.
def LessPreciseFPMAD : StrBoolAttr<"less-precise-fpmad">;
def NoInfsFPMath : StrBoolAttr<"no-infs-fp-math">;
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index 66ecc69c9874d..3d4cbdf5e7fb0 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2246,6 +2246,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
return Attribute::Captures;
case bitc::ATTR_KIND_DEAD_ON_RETURN:
return Attribute::DeadOnReturn;
+ case bitc::ATTR_KIND_NO_SHRINKWRAP:
+ return Attribute::NoShrinkWrap;
}
}
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index 7e0d81ff4b196..a8db8592d411e 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -946,6 +946,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
return bitc::ATTR_KIND_CAPTURES;
case Attribute::DeadOnReturn:
return bitc::ATTR_KIND_DEAD_ON_RETURN;
+ case Attribute::NoShrinkWrap:
+ return bitc::ATTR_KIND_NO_SHRINKWRAP;
case Attribute::EndAttrKinds:
llvm_unreachable("Can not encode end-attribute kinds marker.");
case Attribute::None:
diff --git a/llvm/lib/CodeGen/ShrinkWrap.cpp b/llvm/lib/CodeGen/ShrinkWrap.cpp
index 41e956caa7b34..ce51baba0a691 100644
--- a/llvm/lib/CodeGen/ShrinkWrap.cpp
+++ b/llvm/lib/CodeGen/ShrinkWrap.cpp
@@ -1013,6 +1013,9 @@ PreservedAnalyses ShrinkWrapPass::run(MachineFunction &MF,
bool ShrinkWrapImpl::isShrinkWrapEnabled(const MachineFunction &MF) {
const TargetFrameLowering *TFI = MF.getSubtarget().getFrameLowering();
+ if (MF.getFunction().hasFnAttribute(Attribute::NoShrinkWrap))
+ return false;
+
switch (EnableShrinkWrapOpt) {
case cl::BOU_UNSET:
return TFI->enableShrinkWrapping(MF) &&
diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
index eacaf42e4e8ba..21bb4877689f9 100644
--- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp
+++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
@@ -981,6 +981,7 @@ Function *CodeExtractor::constructFunctionDeclaration(
case Attribute::MustProgress:
case Attribute::NoProfile:
case Attribute::SkipProfile:
+ case Attribute::NoShrinkWrap:
break;
// These attributes cannot be applied to functions.
case Attribute::Alignment:
diff --git a/llvm/test/CodeGen/AArch64/arm64-shrink-wrapping-noshrinkwrap.ll b/llvm/test/CodeGen/AArch64/arm64-shrink-wrapping-noshrinkwrap.ll
new file mode 100644
index 0000000000000..119da3ac4f2e9
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/arm64-shrink-wrapping-noshrinkwrap.ll
@@ -0,0 +1,106 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -debugify-and-strip-all-safe %s -o - -mtriple=arm64-apple-ios -enable-shrink-wrap=true -disable-post-ra -frame-pointer=non-leaf | FileCheck %s --check-prefix=ENABLE
+; RUN: llc -debugify-and-strip-all-safe %s -o - -enable-shrink-wrap=false -disable-post-ra -frame-pointer=non-leaf | FileCheck %s --check-prefix=DISABLE
+; RUN: llc -debugify-and-strip-all-safe %s -o - -mtriple=arm64-apple-ios -enable-shrink-wrap=true -disable-post-ra -frame-pointer=non-leaf | FileCheck %s --check-prefix=NOSHRINK
+target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
+target triple = "arm64-apple-ios"
+
+; Verify that the attribute 'noshrinkwrap' has the same effect as disabling the
+; shrinkwrapping entirely.
+
+; Known shrinkwrap example: Simple diamond with a call just on one side.
+define i32 @foo(i32 %a, i32 %b) {
+; ENABLE-LABEL: foo:
+; ENABLE: ; %bb.0:
+; ENABLE-NEXT: cmp w0, w1
+; ENABLE-NEXT: b.ge LBB0_2
+; ENABLE-NEXT: ; %bb.1: ; %true
+; ENABLE-NEXT: sub sp, sp, #32
+; ENABLE-NEXT: stp x29, x30, [sp, #16] ; 16-byte Folded Spill
+; ENABLE-NEXT: add x29, sp, #16
+; ENABLE-NEXT: .cfi_def_cfa w29, 16
+; ENABLE-NEXT: .cfi_offset w30, -8
+; ENABLE-NEXT: .cfi_offset w29, -16
+; ENABLE-NEXT: stur w0, [x29, #-4]
+; ENABLE-NEXT: sub x1, x29, #4
+; ENABLE-NEXT: mov w0, wzr
+; ENABLE-NEXT: bl _doSomething
+; ENABLE-NEXT: ldp x29, x30, [sp, #16] ; 16-byte Folded Reload
+; ENABLE-NEXT: add sp, sp, #32
+; ENABLE-NEXT: LBB0_2: ; %false
+; ENABLE-NEXT: ret
+; ENABLE-LABEL: bar
+;
+; DISABLE-LABEL: foo:
+; DISABLE: ; %bb.0:
+; DISABLE-NEXT: sub sp, sp, #32
+; DISABLE-NEXT: stp x29, x30, [sp, #16] ; 16-byte Folded Spill
+; DISABLE-NEXT: add x29, sp, #16
+; DISABLE-NEXT: .cfi_def_cfa w29, 16
+; DISABLE-NEXT: .cfi_offset w30, -8
+; DISABLE-NEXT: .cfi_offset w29, -16
+; DISABLE-NEXT: cmp w0, w1
+; DISABLE-NEXT: b.ge LBB0_2
+; DISABLE-NEXT: ; %bb.1: ; %true
+; DISABLE-NEXT: stur w0, [x29, #-4]
+; DISABLE-NEXT: sub x1, x29, #4
+; DISABLE-NEXT: mov w0, wzr
+; DISABLE-NEXT: bl _doSomething
+; DISABLE-NEXT: LBB0_2: ; %false
+; DISABLE-NEXT: ldp x29, x30, [sp, #16] ; 16-byte Folded Reload
+; DISABLE-NEXT: add sp, sp, #32
+; DISABLE-NEXT: ret
+; DISABLE-LABEL: bar
+ %tmp = alloca i32, align 4
+ %tmp2 = icmp slt i32 %a, %b
+ br i1 %tmp2, label %true, label %false
+
+true:
+ store i32 %a, ptr %tmp, align 4
+ %tmp4 = call i32 @doSomething(i32 0, ptr %tmp)
+ br label %false
+
+false:
+ %tmp.0 = phi i32 [ %tmp4, %true ], [ %a, %0 ]
+ ret i32 %tmp.0
+}
+
+; Same code as above but with 'noshrinkwrap' attribute
+
+define i32 @bar(i32 %a, i32 %b) noshrinkwrap {
+; NOSHRINK-LABEL: bar:
+; NOSHRINK: ; %bb.0:
+; NOSHRINK-NEXT: sub sp, sp, #32
+; NOSHRINK-NEXT: stp x29, x30, [sp, #16] ; 16-byte Folded Spill
+; NOSHRINK-NEXT: add x29, sp, #16
+; NOSHRINK-NEXT: .cfi_def_cfa w29, 16
+; NOSHRINK-NEXT: .cfi_offset w30, -8
+; NOSHRINK-NEXT: .cfi_offset w29, -16
+; NOSHRINK-NEXT: cmp w0, w1
+; NOSHRINK-NEXT: b.ge LBB1_2
+; NOSHRINK-NEXT: ; %bb.1: ; %true
+; NOSHRINK-NEXT: stur w0, [x29, #-4]
+; NOSHRINK-NEXT: sub x1, x29, #4
+; NOSHRINK-NEXT: mov w0, wzr
+; NOSHRINK-NEXT: bl _doSomething
+; NOSHRINK-NEXT: LBB1_2: ; %false
+; NOSHRINK-NEXT: ldp x29, x30, [sp, #16] ; 16-byte Folded Reload
+; NOSHRINK-NEXT: add sp, sp, #32
+; NOSHRINK-NEXT: ret
+ %tmp = alloca i32, align 4
+ %tmp2 = icmp slt i32 %a, %b
+ br i1 %tmp2, label %true, label %false
+
+true:
+ store i32 %a, ptr %tmp, align 4
+ %tmp4 = call i32 @doSomething(i32 0, ptr %tmp)
+ br label %false
+
+false:
+ %tmp.0 = phi i32 [ %tmp4, %true ], [ %a, %0 ]
+ ret i32 %tmp.0
+}
+
+; Function Attrs: optsize
+declare i32 @doSomething(i32, ptr)
+
|
As a practical matter, I don't think LLVM supports shrink-wrapping on Windows at the moment. I mean, I guess it's something someone could theoretically look into at some point, but the interaction with unwind info is complicated. Can we define what it means to "shrink-wrap" in a way that isn't specifically tied to the shrink-wrapping pass? We need to define what output is legal, not how we got there. For example, if the rule is that we aren't allowed to access non-stack memory before adjusting the stack, that could interact with late instruction scheduling. It looks like this isn't an existing MSVC feature? If that's the case, why are we using __declspec syntax, as opposed to modern C++ attribute syntax? |
Change isn't Windows specific -- seems generally useful. You're right that it enshrines the functionality responsible rather than desire 'don't get cute with the prologue'. I'm open to a better name. re: __declspec() -- there is no compatibility requirement for this choice. MSVC has an undocumented mechanism but it uses intrinsics while still having the same behavior as what's proposed here. I'll look at how to enable the modern attribute style instead. thanks! |
This is probably worth an RFC (https://clang.llvm.org/get_involved.html). The "late instruction scheduling" example is something I'm pretty sure can actually trigger in practice; does prolog/epilog lowering need to insert some kind of barrier instruction? And the attribute needs to be documented in LangRef. |
Provide a mechanism to disable the shrinkwrapping process on an individual function via __declspec(noshrinkwrap). This is functionality is sometimes necessary in systems and exception handling code-. Other mechanisms for doing this are -enable-shrink-wrap=false and via llvm::TargetFrameLowering::enableShrinkWrapping() -- neither mechanism is particularly flexible or targeted.
No example of similar documented functionality has been found.
Implementation adds a Clang declspec which drives an LLVM function attribute.
Attribute is consumed by the ShrinkWrap pass.