diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -1050,6 +1050,10 @@ Fn->addFnAttr("packed-stack"); } + if (CGM.getCodeGenOpts().WarnStackSize != UINT_MAX) + Fn->addFnAttr("warn-stack-size", + std::to_string(CGM.getCodeGenOpts().WarnStackSize)); + if (RetTy->isVoidType()) { // Void type; nothing to return. ReturnValue = Address::invalid(); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -787,8 +787,6 @@ getCodeGenOpts().StackProtectorGuardOffset); if (getCodeGenOpts().StackAlignment) getModule().setOverrideStackAlignment(getCodeGenOpts().StackAlignment); - if (getCodeGenOpts().WarnStackSize != UINT_MAX) - getModule().setWarnStackSize(getCodeGenOpts().WarnStackSize); getTargetCodeGenInfo().emitTargetMetadata(*this, MangledDeclNames); diff --git a/clang/test/Frontend/fwarn-stack-size.c b/clang/test/Frontend/fwarn-stack-size.c new file mode 100644 --- /dev/null +++ b/clang/test/Frontend/fwarn-stack-size.c @@ -0,0 +1,4 @@ +// RUN: %clang_cc1 -fwarn-stack-size=42 -emit-llvm -o - %s | FileCheck %s +void foo(void) {} +// CHECK: define {{.*}} @foo() [[ATTR:#[0-9]+]] { +// CHECK: attributes [[ATTR]] = {{.*}} "warn-stack-size"="42" diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -2048,6 +2048,11 @@ function does not satisfy this contract, the behavior is undefined. This attribute does not apply transitively to callees, but does apply to call sites within the function. Note that `willreturn` implies `mustprogress`. +``"warn-stack-size"=""`` + This attribute sets a threshold to emit diagnostics for once the frame size + is known should the frame size exceed the specified value. It takes one + required integer value, which should be greater than or equal to 0, and + less than `UINT_MAX`. ``vscale_range([, ])`` This attribute indicates the minimum and maximum vscale value for the given function. A value of 0 means unbounded. If the optional max value is omitted diff --git a/llvm/include/llvm/IR/Module.h b/llvm/include/llvm/IR/Module.h --- a/llvm/include/llvm/IR/Module.h +++ b/llvm/include/llvm/IR/Module.h @@ -913,10 +913,6 @@ unsigned getOverrideStackAlignment() const; void setOverrideStackAlignment(unsigned Align); - /// Get/set the stack frame size threshold to warn on. - unsigned getWarnStackSize() const; - void setWarnStackSize(unsigned Threshold); - /// @name Utility functions for querying and setting the build SDK version /// @{ diff --git a/llvm/lib/CodeGen/PrologEpilogInserter.cpp b/llvm/lib/CodeGen/PrologEpilogInserter.cpp --- a/llvm/lib/CodeGen/PrologEpilogInserter.cpp +++ b/llvm/lib/CodeGen/PrologEpilogInserter.cpp @@ -274,7 +274,16 @@ MachineFrameInfo &MFI = MF.getFrameInfo(); uint64_t StackSize = MFI.getStackSize(); - unsigned Threshold = MF.getFunction().getParent()->getWarnStackSize(); + unsigned Threshold = UINT_MAX; + if (MF.getFunction().hasFnAttribute("warn-stack-size")) { + bool failed = MF.getFunction() + .getFnAttribute("warn-stack-size") + .getValueAsString() + .getAsInteger(10, Threshold); + // Verifier should have caught this. + assert(!failed && "Invalid warn-stack-size fn attr value"); + (void)failed; + } if (StackSize > Threshold) { DiagnosticInfoStackSize DiagStackSize(F, StackSize); F.getContext().diagnose(DiagStackSize); diff --git a/llvm/lib/IR/Module.cpp b/llvm/lib/IR/Module.cpp --- a/llvm/lib/IR/Module.cpp +++ b/llvm/lib/IR/Module.cpp @@ -732,17 +732,6 @@ addModuleFlag(ModFlagBehavior::Error, "override-stack-alignment", Align); } -unsigned Module::getWarnStackSize() const { - Metadata *MD = getModuleFlag("warn-stack-size"); - if (auto *CI = mdconst::dyn_extract_or_null(MD)) - return CI->getZExtValue(); - return UINT_MAX; -} - -void Module::setWarnStackSize(unsigned Threshold) { - addModuleFlag(ModFlagBehavior::Error, "warn-stack-size", Threshold); -} - void Module::setSDKVersion(const VersionTuple &V) { SmallVector Entries; Entries.push_back(V.getMajor()); diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -543,6 +543,8 @@ void verifyAttributeTypes(AttributeSet Attrs, bool IsFunction, const Value *V); void verifyParameterAttrs(AttributeSet Attrs, Type *Ty, const Value *V); + void checkUnsignedBaseTenFuncAttr(AttributeList Attrs, StringRef Attr, + const Value *V); void verifyFunctionAttrs(FunctionType *FT, AttributeList Attrs, const Value *V, bool IsIntrinsic); void verifyFunctionMetadata(ArrayRef> MDs); @@ -1888,6 +1890,17 @@ } } +void Verifier::checkUnsignedBaseTenFuncAttr(AttributeList Attrs, StringRef Attr, + const Value *V) { + if (Attrs.hasFnAttribute(Attr)) { + StringRef S = Attrs.getAttribute(AttributeList::FunctionIndex, Attr) + .getValueAsString(); + unsigned N; + if (S.getAsInteger(10, N)) + CheckFailed("\"" + Attr + "\" takes an unsigned integer: " + S, V); + } +} + // Check parameter attributes against a function type. // The value V is printed in error messages. void Verifier::verifyFunctionAttrs(FunctionType *FT, AttributeList Attrs, @@ -2087,26 +2100,9 @@ CheckFailed("invalid value for 'frame-pointer' attribute: " + FP, V); } - if (Attrs.hasFnAttribute("patchable-function-prefix")) { - StringRef S = Attrs - .getAttribute(AttributeList::FunctionIndex, - "patchable-function-prefix") - .getValueAsString(); - unsigned N; - if (S.getAsInteger(10, N)) - CheckFailed( - "\"patchable-function-prefix\" takes an unsigned integer: " + S, V); - } - if (Attrs.hasFnAttribute("patchable-function-entry")) { - StringRef S = Attrs - .getAttribute(AttributeList::FunctionIndex, - "patchable-function-entry") - .getValueAsString(); - unsigned N; - if (S.getAsInteger(10, N)) - CheckFailed( - "\"patchable-function-entry\" takes an unsigned integer: " + S, V); - } + checkUnsignedBaseTenFuncAttr(Attrs, "patchable-function-prefix", V); + checkUnsignedBaseTenFuncAttr(Attrs, "patchable-function-entry", V); + checkUnsignedBaseTenFuncAttr(Attrs, "warn-stack-size", V); } void Verifier::verifyFunctionMetadata( diff --git a/llvm/test/CodeGen/ARM/warn-stack.ll b/llvm/test/CodeGen/ARM/warn-stack.ll --- a/llvm/test/CodeGen/ARM/warn-stack.ll +++ b/llvm/test/CodeGen/ARM/warn-stack.ll @@ -4,7 +4,7 @@ ; ; CHECK-NOT: nowarn -define void @nowarn() nounwind ssp "frame-pointer"="all" { +define void @nowarn() nounwind ssp "frame-pointer"="all" "warn-stack-size"="80" { entry: %buffer = alloca [12 x i8], align 1 %arraydecay = getelementptr inbounds [12 x i8], [12 x i8]* %buffer, i64 0, i64 0 @@ -13,7 +13,7 @@ } ; CHECK: warning: stack size limit exceeded (92) in warn -define void @warn() nounwind ssp "frame-pointer"="all" { +define void @warn() nounwind ssp "frame-pointer"="all" "warn-stack-size"="80" { entry: %buffer = alloca [80 x i8], align 1 %arraydecay = getelementptr inbounds [80 x i8], [80 x i8]* %buffer, i64 0, i64 0 @@ -22,6 +22,3 @@ } declare void @doit(i8*) - -!llvm.module.flags = !{!0} -!0 = !{i32 1, !"warn-stack-size", i32 80} diff --git a/llvm/test/CodeGen/X86/warn-stack.ll b/llvm/test/CodeGen/X86/warn-stack.ll --- a/llvm/test/CodeGen/X86/warn-stack.ll +++ b/llvm/test/CodeGen/X86/warn-stack.ll @@ -4,7 +4,7 @@ ; ; CHECK-NOT: nowarn -define void @nowarn() nounwind ssp { +define void @nowarn() nounwind ssp "warn-stack-size"="80" { entry: %buffer = alloca [12 x i8], align 1 %arraydecay = getelementptr inbounds [12 x i8], [12 x i8]* %buffer, i64 0, i64 0 @@ -13,7 +13,7 @@ } ; CHECK: warning: stack size limit exceeded (88) in warn -define void @warn() nounwind ssp { +define void @warn() nounwind ssp "warn-stack-size"="80" { entry: %buffer = alloca [80 x i8], align 1 %arraydecay = getelementptr inbounds [80 x i8], [80 x i8]* %buffer, i64 0, i64 0 @@ -22,6 +22,3 @@ } declare void @doit(i8*) - -!llvm.module.flags = !{!0} -!0 = !{i32 1, !"warn-stack-size", i32 80} diff --git a/llvm/test/Linker/warn-stack-frame.ll b/llvm/test/Linker/warn-stack-frame.ll deleted file mode 100644 --- a/llvm/test/Linker/warn-stack-frame.ll +++ /dev/null @@ -1,16 +0,0 @@ -; RUN: split-file %s %t -; RUN: llvm-link %t/main.ll %t/match.ll -; RUN: not llvm-link %t/main.ll %t/mismatch.ll 2>&1 | \ -; RUN: FileCheck --check-prefix=CHECK-MISMATCH %s - -; CHECK-MISMATCH: error: linking module flags 'warn-stack-size': IDs have conflicting values - -;--- main.ll -!llvm.module.flags = !{!0} -!0 = !{i32 1, !"warn-stack-size", i32 80} -;--- match.ll -!llvm.module.flags = !{!0} -!0 = !{i32 1, !"warn-stack-size", i32 80} -;--- mismatch.ll -!llvm.module.flags = !{!0} -!0 = !{i32 1, !"warn-stack-size", i32 81} diff --git a/llvm/test/Transforms/Inline/warn-stack-size.ll b/llvm/test/Transforms/Inline/warn-stack-size.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Inline/warn-stack-size.ll @@ -0,0 +1,24 @@ +; RUN: opt -passes=inline -S %s | FileCheck %s + +; The caller should NOT inherit the callee's "warn-stack-size" value. +define void @foo() "warn-stack-size"="42" { ret void } +; CHECK: @foo( +; CHECK-NEXT: ret void +; +define void @bar() "warn-stack-size"="7" { +; CHECK: @bar() [[ATTR0:#[0-9]+]] { +; CHECK-NEXT: ret void +; + call void @foo() + ret void +} +define void @baz() "warn-stack-size"="99" { +; CHECK: @baz() [[ATTR1:#[0-9]+]] { +; CHECK-NEXT: ret void +; + call void @bar() + ret void +} + +; CHECK: attributes [[ATTR0]] = {{.*}} "warn-stack-size"="7" +; CHECK: attributes [[ATTR1]] = {{.*}} "warn-stack-size"="99" diff --git a/llvm/test/Verifier/invalid-warn-stack-size.ll b/llvm/test/Verifier/invalid-warn-stack-size.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Verifier/invalid-warn-stack-size.ll @@ -0,0 +1,10 @@ +; RUN: not opt -passes=verify %s -disable-output 2>&1 | FileCheck %s +define void @foo() "warn-stack-size"="42" { ret void } +define void @bar() "warn-stack-size"="-1" { ret void } +define void @baz() "warn-stack-size"="999999999999999999999" { ret void } +define void @qux() "warn-stack-size"="a lot lol" { ret void } + +; CHECK-NOT: "warn-stack-size" takes an unsigned integer: 42 +; CHECK: "warn-stack-size" takes an unsigned integer: -1 +; CHECK: "warn-stack-size" takes an unsigned integer: 999999999999999999999 +; CHECK: "warn-stack-size" takes an unsigned integer: a lot lol