diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -25,6 +25,15 @@ def err_fe_cannot_link_module : Error<"cannot link module '%0': %1">, DefaultFatal; +def warn_fe_stack_usage : Warning<"stack usage is %0 bytes in %1">, + BackendInfo, InGroup; +def warn_fe_stack_usage_dynam : Warning<"stack usage might be %0 bytes in %1">, + BackendInfo, InGroup; +def warn_fe_backend_stack_usage: Warning<"%0">, + BackendInfo, InGroup; +def err_fe_backend_stack_usage: Error<"%0">, BackendInfo; +def note_fe_backend_stack_usage: Note<"%0">, BackendInfo; + def warn_fe_frame_larger_than : Warning<"stack frame size of %0 bytes in %q1">, BackendInfo, InGroup; def warn_fe_backend_frame_larger_than: Warning<"%0">, diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -1189,6 +1189,7 @@ def BackendInlineAsm : DiagGroup<"inline-asm">; def BackendSourceMgr : DiagGroup<"source-mgr">; def BackendFrameLargerThanEQ : DiagGroup<"frame-larger-than=">; +def BackendStackUsageEQ : DiagGroup<"stack-usage=">; def BackendPlugin : DiagGroup<"backend-plugin">; def RemarkBackendPlugin : DiagGroup<"remark-backend-plugin">; def BackendOptimizationRemark : DiagGroup<"pass">; diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2537,6 +2537,8 @@ def Wlarger_than_ : Joined<["-"], "Wlarger-than-">, Alias; def Wframe_larger_than_EQ : Joined<["-"], "Wframe-larger-than=">, Group, Flags<[NoXarchOption]>; +def Wstack_usage_EQ : Joined<["-"], "Wstack-usage=">, Group, Flags<[NoXarchOption]>; + def : Flag<["-"], "fterminated-vtables">, Alias; defm threadsafe_statics : BoolFOption<"threadsafe-statics", LangOpts<"ThreadsafeStatics">, DefaultTrue, diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp --- a/clang/lib/CodeGen/CodeGenAction.cpp +++ b/clang/lib/CodeGen/CodeGenAction.cpp @@ -567,15 +567,19 @@ // We do not know how to format other severities. return false; - if (const Decl *ND = Gen->GetDeclForMangledName(D.getFunction().getName())) { - // FIXME: Shouldn't need to truncate to uint32_t - Diags.Report(ND->getASTContext().getFullLoc(ND->getLocation()), - diag::warn_fe_frame_larger_than) + const Decl *ND = Gen->GetDeclForMangledName(D.getFunction().getName()); + if (!ND) + return false; + + unsigned DiagID = diag::warn_fe_frame_larger_than; + if (D.printStackUsageInfo()) + DiagID = D.hasVarSizedObjects() ? diag::warn_fe_stack_usage_dynam + : diag::warn_fe_stack_usage; + // FIXME: Shouldn't need to truncate to uint32_t + Diags.Report(ND->getASTContext().getFullLoc(ND->getLocation()), DiagID) << static_cast(D.getStackSize()) << Decl::castToDeclContext(ND); - return true; - } - return false; + return true; } const FullSourceLoc BackendConsumer::getBestLocationFromDebugLoc( @@ -774,7 +778,10 @@ case llvm::DK_StackSize: if (StackSizeDiagHandler(cast(DI))) return; - ComputeDiagID(Severity, backend_frame_larger_than, DiagID); + if (cast(DI).printStackUsageInfo()) + ComputeDiagID(Severity, backend_stack_usage, DiagID); + else + ComputeDiagID(Severity, backend_frame_larger_than, DiagID); break; case DK_Linker: assert(CurLinkModule); diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -4777,10 +4777,16 @@ if (Arg *A = Args.getLastArg(options::OPT_Wframe_larger_than_EQ)) { StringRef v = A->getValue(); CmdArgs.push_back("-mllvm"); - CmdArgs.push_back(Args.MakeArgString("-warn-stack-size=" + v)); + CmdArgs.push_back(Args.MakeArgString("-warn-frame-size=" + v)); A->claim(); } + if (Arg *A = Args.getLastArg(options::OPT_Wstack_usage_EQ)) { + StringRef bytes = A->getValue(); + CmdArgs.push_back("-mllvm"); + CmdArgs.push_back(Args.MakeArgString("-warn-stack-usage=" + bytes)); + } + if (!Args.hasFlag(options::OPT_fjump_tables, options::OPT_fno_jump_tables, true)) CmdArgs.push_back("-fno-jump-tables"); diff --git a/clang/test/Frontend/backend-diagnostic.c b/clang/test/Frontend/backend-diagnostic.c --- a/clang/test/Frontend/backend-diagnostic.c +++ b/clang/test/Frontend/backend-diagnostic.c @@ -4,11 +4,11 @@ // _PROMOTE_: Promote warning to error. // _IGNORE_: Drop backend warning. // -// RUN: not %clang_cc1 %s -mllvm -warn-stack-size=0 -no-integrated-as -S -o - -triple=i386-apple-darwin 2> %t.err +// RUN: not %clang_cc1 %s -mllvm -warn-frame-size=0 -no-integrated-as -S -o - -triple=i386-apple-darwin 2> %t.err // RUN: FileCheck < %t.err %s --check-prefix=REGULAR --check-prefix=ASM -// RUN: not %clang_cc1 %s -mllvm -warn-stack-size=0 -no-integrated-as -S -o - -triple=i386-apple-darwin -Werror=frame-larger-than= 2> %t.err +// RUN: not %clang_cc1 %s -mllvm -warn-frame-size=0 -no-integrated-as -S -o - -triple=i386-apple-darwin -Werror=frame-larger-than= 2> %t.err // RUN: FileCheck < %t.err %s --check-prefix=PROMOTE --check-prefix=ASM -// RUN: not %clang_cc1 %s -mllvm -warn-stack-size=0 -no-integrated-as -S -o - -triple=i386-apple-darwin -Wno-frame-larger-than= 2> %t.err +// RUN: not %clang_cc1 %s -mllvm -warn-frame-size=0 -no-integrated-as -S -o - -triple=i386-apple-darwin -Wno-frame-larger-than= 2> %t.err // RUN: FileCheck < %t.err %s --check-prefix=IGNORE --check-prefix=ASM // // RUN: %clang_cc1 %s -S -o - -triple=i386-apple-darwin -verify -no-integrated-as diff --git a/clang/test/Frontend/backend-stack-usage-diagnostic.c b/clang/test/Frontend/backend-stack-usage-diagnostic.c new file mode 100644 --- /dev/null +++ b/clang/test/Frontend/backend-stack-usage-diagnostic.c @@ -0,0 +1,24 @@ + +// RUN: %clang -Wstack-usage=0 -o /dev/null -c %s 2> %t.err +// RUN: FileCheck < %t.err %s --check-prefix=WARN +// RUN: %clang -Wstack-usage=0 -Wno-stack-usage= -o /dev/null -c %s 2> %t.err +// RUN: FileCheck < %t.err %s --check-prefix=IGNORE --allow-empty +// RUN: %clang -Wstack-usage=0 -w -o /dev/null -c %s 2> %t.err +// RUN: FileCheck < %t.err %s --check-prefix=IGNORE --allow-empty +// RUN: %clang -Wstack-usage=3 -o /dev/null -c %s 2> %t.err +// RUN: FileCheck < %t.err %s --check-prefix=WARN +// RUN: %clang -Wstack-usage=100 -o /dev/null -c %s 2> %t.err +// RUN: FileCheck < %t.err %s --check-prefix=IGNORE --allow-empty + +// WARN: warning: stack usage is {{[0-9]+}} bytes in function 'test_square' +// IGNORE-NOT: stack usage is {{[0-9]+}} bytes in function 'test_square' +int test_square(int num) { + return num * num; +} + +// WARN: warning: stack usage might be {{[0-9]+}} bytes in function 'test_unbounded' +// IGNORE-NOT: stack usage might be {{[0-9]+}} bytes in function 'test_unbounded' +int test_unbounded(int len) { + char a[len]; + return 1; +} diff --git a/clang/test/Misc/backend-stack-frame-diagnostics-fallback.cpp b/clang/test/Misc/backend-stack-frame-diagnostics-fallback.cpp --- a/clang/test/Misc/backend-stack-frame-diagnostics-fallback.cpp +++ b/clang/test/Misc/backend-stack-frame-diagnostics-fallback.cpp @@ -1,5 +1,5 @@ // REQUIRES: x86-registered-target -// RUN: %clang_cc1 %s -mllvm -warn-stack-size=0 -emit-codegen-only -triple=i386-apple-darwin 2>&1 | FileCheck %s +// RUN: %clang_cc1 %s -mllvm -warn-frame-size=0 -emit-codegen-only -triple=i386-apple-darwin 2>&1 | FileCheck %s // TODO: Emit rich diagnostics for thunks and move this into the appropriate test file. // Until then, test that we fall back and display the LLVM backend diagnostic. diff --git a/llvm/include/llvm/CodeGen/MachineFrameInfo.h b/llvm/include/llvm/CodeGen/MachineFrameInfo.h --- a/llvm/include/llvm/CodeGen/MachineFrameInfo.h +++ b/llvm/include/llvm/CodeGen/MachineFrameInfo.h @@ -243,6 +243,10 @@ /// to be allocated on entry to the function. uint64_t StackSize = 0; + /// The FrameSize is the StackSize above, minus any reserved argument space + /// for call sites in the function. + uint64_t FrameSize = 0; + /// The amount that a frame offset needs to be adjusted to /// have the actual offset from the stack/frame pointer. The exact usage of /// this is target-dependent, but it is typically used to adjust between @@ -568,6 +572,14 @@ /// Estimate and return the size of the stack frame. uint64_t estimateStackSize(const MachineFunction &MF) const; + /// Returns the StackSize without the stack space allocated to hold + /// parameters for other functions called from inside the current + /// function + uint64_t getFrameSize() const { return FrameSize; } + + /// Set the size of the frame. + void setFrameSize(uint64_t Size) { FrameSize = Size; } + /// Return the correction for frame offsets. int getOffsetAdjustment() const { return OffsetAdjustment; } diff --git a/llvm/include/llvm/IR/DiagnosticInfo.h b/llvm/include/llvm/IR/DiagnosticInfo.h --- a/llvm/include/llvm/IR/DiagnosticInfo.h +++ b/llvm/include/llvm/IR/DiagnosticInfo.h @@ -216,15 +216,24 @@ class DiagnosticInfoStackSize : public DiagnosticInfoResourceLimit { void anchor() override; + bool HasVarSizedObjects; + bool PrintStackUsageInfo; + public: DiagnosticInfoStackSize(const Function &Fn, uint64_t StackSize, + bool HasVarSizedObjects = false, + bool PrintStackUsageInfo = false, DiagnosticSeverity Severity = DS_Warning, uint64_t StackLimit = 0) : DiagnosticInfoResourceLimit(Fn, "stack size", StackSize, Severity, - DK_StackSize, StackLimit) {} + DK_StackSize, StackLimit), + HasVarSizedObjects(HasVarSizedObjects), + PrintStackUsageInfo(PrintStackUsageInfo) {} uint64_t getStackSize() const { return getResourceSize(); } uint64_t getStackLimit() const { return getResourceLimit(); } + bool hasVarSizedObjects() const { return HasVarSizedObjects; } + bool printStackUsageInfo() const { return PrintStackUsageInfo; } static bool classof(const DiagnosticInfo *DI) { return DI->getKind() == DK_StackSize; 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 @@ -139,9 +139,14 @@ char &llvm::PrologEpilogCodeInserterID = PEI::ID; static cl::opt -WarnStackSize("warn-stack-size", cl::Hidden, cl::init((unsigned)-1), - cl::desc("Warn for stack size bigger than the given" - " number")); + WarnFrameSize("warn-frame-size", cl::Hidden, cl::init((unsigned)-1), + cl::desc("Warn for frame size bigger than the given" + " number")); + +static cl::opt + WarnStackUsage("warn-stack-usage", cl::Hidden, cl::init((unsigned)-1), + cl::desc("Warn for stack size bigger than the given" + " number")); INITIALIZE_PASS_BEGIN(PEI, DEBUG_TYPE, "Prologue/Epilogue Insertion", false, false) @@ -275,11 +280,19 @@ if (TRI->requiresRegisterScavenging(MF) && FrameIndexVirtualScavenging) scavengeFrameVirtualRegs(MF, *RS); - // Warn on stack size when we exceeds the given limit. + // Warn on stack usage size when we exceed the given limit MachineFrameInfo &MFI = MF.getFrameInfo(); uint64_t StackSize = MFI.getStackSize(); - if (WarnStackSize.getNumOccurrences() > 0 && WarnStackSize < StackSize) { - DiagnosticInfoStackSize DiagStackSize(F, StackSize); + if (WarnStackUsage.getNumOccurrences() > 0 && WarnStackUsage <= StackSize) { + DiagnosticInfoStackSize DiagStackUsage( + F, StackSize, MFI.hasVarSizedObjects(), true /*Stack Usage*/); + F.getContext().diagnose(DiagStackUsage); + } + + uint64_t FrameSize = MFI.getFrameSize(); + // Warn on Frame size when we exceed the given limit. + if (WarnFrameSize.getNumOccurrences() > 0 && WarnFrameSize < FrameSize) { + DiagnosticInfoStackSize DiagStackSize(F, FrameSize); F.getContext().diagnose(DiagStackSize); } ORE->emit([&]() { @@ -1048,6 +1061,10 @@ AdjustStackOffset(MFI, SFI, StackGrowsDown, Offset, MaxAlign, Skew); } + int64_t OffsetBeforeMaxCallFrameSize = Offset; + int64_t FrameSize = OffsetBeforeMaxCallFrameSize - LocalAreaOffset; + MFI.setFrameSize(FrameSize); + if (!TFI.targetHandlesStackFrameRounding()) { // If we have reserved argument space for call sites in the function // immediately on entry to the current function, count it as part of the @@ -1073,6 +1090,11 @@ int64_t OffsetBeforeAlignment = Offset; Offset = alignTo(Offset, StackAlign, Skew); + OffsetBeforeMaxCallFrameSize = + alignTo(OffsetBeforeMaxCallFrameSize, StackAlign, Skew); + int64_t FrameSize = OffsetBeforeMaxCallFrameSize - LocalAreaOffset; + MFI.setFrameSize(FrameSize); + // If we have increased the offset to fulfill the alignment constrants, // then the scavenging spill slots may become harder to reach from the // stack pointer, float them so they stay close.