diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -389,6 +389,8 @@ /// coverage pass should actually not be instrumented. std::vector SanitizeCoverageBlocklistFiles; + std::string StackUsageOutput; + /// Executable and command-line used to create a given CompilerInvocation. /// Most of the time this will be the full -cc1 command. const char *Argv0 = nullptr; diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -107,6 +107,7 @@ CODEGENOPT(XRayInstrumentFunctions , 1, 0) ///< Set when -fxray-instrument is ///< enabled. CODEGENOPT(StackSizeSection , 1, 0) ///< Set when -fstack-size-section is enabled. +CODEGENOPT(StackUsage , 1, 0) ///< Set when -fstack-usage is enabled. CODEGENOPT(ForceDwarfFrameSection , 1, 0) ///< Set when -fforce-dwarf-frame is ///< enabled. 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 @@ -2650,6 +2650,12 @@ CodeGenOpts<"StackSizeSection">, DefaultFalse, PosFlag, NegFlag>; +def fstack_usage : Flag<["-"], "fstack-usage">, Group, Flags<[CC1Option]>, + HelpText<"Emit .su file containing information on function stack sizes">, + MarshallingInfoFlag>; +def fstack_usage_EQ : Joined<["-"], "fstack-usage=">, Group, + Flags<[CC1Option, NoDriverOption]>, + MarshallingInfoString, [{""}]>; defm unique_basic_block_section_names : BoolFOption<"unique-basic-block-section-names", CodeGenOpts<"UniqueBasicBlockSectionNames">, DefaultFalse, diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -567,6 +567,8 @@ Options.ExplicitEmulatedTLS = CodeGenOpts.ExplicitEmulatedTLS; Options.DebuggerTuning = CodeGenOpts.getDebuggerTuning(); Options.EmitStackSizeSection = CodeGenOpts.StackSizeSection; + Options.EmitStackUsage = CodeGenOpts.StackUsage; + Options.StackUsageOutput = CodeGenOpts.StackUsageOutput; Options.EmitAddrsig = CodeGenOpts.Addrsig; Options.ForceDwarfFrameSection = CodeGenOpts.ForceDwarfFrameSection; Options.EmitCallSiteInfo = CodeGenOpts.EmitCallSiteInfo; 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 @@ -5468,6 +5468,17 @@ options::OPT_fno_stack_size_section, RawTriple.isPS4())) CmdArgs.push_back("-fstack-size-section"); + if (Args.hasArg(options::OPT_fstack_usage)) { + CmdArgs.push_back(Args.MakeArgString("-fstack-usage")); + + if (Arg *OutputOpt = Args.getLastArg(options::OPT_o)) { + SmallString<128> OutputFilename(OutputOpt->getValue()); + llvm::sys::path::replace_extension(OutputFilename, "su"); + CmdArgs.push_back(Args.MakeArgString("-fstack-usage=" + + std::string(OutputFilename.str()))); + } + } + CmdArgs.push_back("-ferror-limit"); if (Arg *A = Args.getLastArg(options::OPT_ferror_limit_EQ)) CmdArgs.push_back(A->getValue()); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1923,6 +1923,12 @@ if (UsingSampleProfile) NeedLocTracking = true; + if (Args.hasArg(OPT_fstack_usage)) + NeedLocTracking = true; + + if (Arg *A = Args.getLastArg(OPT_fstack_usage_EQ)) + Opts.StackUsageOutput = std::string(A->getValue()); + // If the user requested a flag that requires source locations available in // the backend, make sure that the backend tracks source location information. if (NeedLocTracking && Opts.getDebugInfo() == codegenoptions::NoDebugInfo) diff --git a/clang/test/CodeGen/stack-usage.c b/clang/test/CodeGen/stack-usage.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/stack-usage.c @@ -0,0 +1,22 @@ +// REQUIRES: x86-registered-target,aarch64-registered-target,arm-registered-target + +// RUN: %clang_cc1 -triple x86_64-unknown -fstack-usage -fstack-usage=%T/a.su -emit-obj %s -o %T/a.o +// RUN: FileCheck %s < %T/a.su +// RUN: %clang_cc1 -triple aarch64-unknown -fstack-usage -fstack-usage=%T/b.su -emit-obj %s -o %T/b.o +// RUN: FileCheck %s < %T/b.su +// RUN: %clang_cc1 -triple arm-unknown -fstack-usage -fstack-usage=%T/c.su -emit-obj %s -o %T/c.o +// RUN: FileCheck %s < %T/c.su + +// CHECK: stack-usage.c:11:foo {{[0-9]+}} static +int foo() { + char a[8]; + + return 0; +} + +// CHECK: stack-usage.c:18:bar {{[0-9]+}} dynamic +int bar(int len) { + char a[len]; + + return 1; +} diff --git a/clang/test/Driver/stack-usage.c b/clang/test/Driver/stack-usage.c new file mode 100644 --- /dev/null +++ b/clang/test/Driver/stack-usage.c @@ -0,0 +1,11 @@ +// RUN: %clang -target x86_64-unknown %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ABSENT +// RUN: %clang -target aarch64-unknown %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ABSENT +// RUN: %clang -target arm-unknown %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ABSENT +// CHECK-ABSENT-NOT: -fstack-usage + +// RUN: %clang -target x86_64-unknown -fstack-usage %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-PRESENT +// RUN: %clang -target aarch64-unknown -fstack-usage %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-PRESENT +// RUN: %clang -target arm-unknown -fstack-usage %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-PRESENT +// CHECK-PRESENT: -fstack-usage + +int foo() { return 42; } diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h --- a/llvm/include/llvm/CodeGen/AsmPrinter.h +++ b/llvm/include/llvm/CodeGen/AsmPrinter.h @@ -175,6 +175,9 @@ /// Emit comments in assembly output if this is true. bool VerboseAsm; + /// Output stream for the stack usage file (i.e., .su file). + std::unique_ptr StackUsageStream = nullptr; + static char ID; protected: @@ -351,6 +354,8 @@ void emitStackSizeSection(const MachineFunction &MF); + void emitStackUsage(const MachineFunction &MF); + void emitBBAddrMapSection(const MachineFunction &MF); void emitPseudoProbe(const MachineInstr &MI); diff --git a/llvm/include/llvm/Target/TargetOptions.h b/llvm/include/llvm/Target/TargetOptions.h --- a/llvm/include/llvm/Target/TargetOptions.h +++ b/llvm/include/llvm/Target/TargetOptions.h @@ -130,7 +130,7 @@ GuaranteedTailCallOpt(false), StackSymbolOrdering(true), EnableFastISel(false), EnableGlobalISel(false), UseInitArray(false), DisableIntegratedAS(false), RelaxELFRelocations(false), - FunctionSections(false), DataSections(false), + FunctionSections(false), DataSections(false), EmitStackUsage(false), IgnoreXCOFFVisibility(false), XCOFFTracebackTable(true), UniqueSectionNames(true), UniqueBasicBlockSectionNames(false), TrapUnreachable(false), NoTrapAfterNoreturn(false), TLSSize(0), @@ -249,6 +249,9 @@ /// Emit data into separate sections. unsigned DataSections : 1; + /// Emit .su file containing information on function stack sizes. + unsigned EmitStackUsage : 1; + /// Do not emit visibility attribute for xcoff. unsigned IgnoreXCOFFVisibility : 1; @@ -341,6 +344,11 @@ /// Stack protector guard reg to use, e.g. usually fs or gs in X86. std::string StackProtectorGuardReg = "None"; + /// Name of the stack usage file (i.e., .su file). If empty, the name of the + /// source file should be used to name the stack usage file (e.g., foo.su + /// for foo.c) + std::string StackUsageOutput = ""; + /// FloatABIType - This setting is set by -float-abi=xxx option is specfied /// on the command line. This setting may either be Default, Soft, or Hard. /// Default selects the target's default behavior. Soft selects the ABI for diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -1163,6 +1163,42 @@ OutStreamer->PopSection(); } +void AsmPrinter::emitStackUsage(const MachineFunction &MF) { + if (!MF.getTarget().Options.EmitStackUsage) + return; + + const MachineFrameInfo &FrameInfo = MF.getFrameInfo(); + uint64_t StackSize = FrameInfo.getStackSize(); + + auto OutputFilename = MF.getTarget().Options.StackUsageOutput; + if (OutputFilename.empty()) { + SmallString<128> Val = MF.getFunction().getParent()->getName(); + + llvm::sys::path::replace_extension(Val, "su"); + OutputFilename = std::string(Val.str()); + } + + if (StackUsageStream == nullptr) { + std::error_code EC; + StackUsageStream = + std::make_unique(OutputFilename, EC, sys::fs::OF_Text); + if (EC) { + errs() << "Could not open file: " << EC.message(); + return; + } + } + + *StackUsageStream << MF.getFunction().getParent()->getName(); + if (const DISubprogram *DSP = MF.getFunction().getSubprogram()) + *StackUsageStream << ":" << DSP->getLine(); + + *StackUsageStream << ":" << MF.getName() << "\t" << StackSize << "\t"; + if (FrameInfo.hasVarSizedObjects()) + *StackUsageStream << "dynamic\n"; + else + *StackUsageStream << "static\n"; +} + static bool needFuncLabelsForEHOrDebugInfo(const MachineFunction &MF) { MachineModuleInfo &MMI = MF.getMMI(); if (!MF.getLandingPads().empty() || MF.hasEHFunclets() || MMI.hasDebugInfo()) @@ -1447,6 +1483,9 @@ // Emit section containing stack size metadata. emitStackSizeSection(*MF); + // Emit .su file containing function stack size information. + emitStackUsage(*MF); + emitPatchableFunctionEntries(); if (isVerbose())