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()) { + 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(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) +