diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -70,6 +70,10 @@ - ``-Wreserved-identifier`` emits warning when user code uses reserved identifiers. +- ``-fstack-usage`` generates an extra .su file per input source file. The .su + file contains frame size information for each function defined in the source + file. + Deprecated Compiler Flags ------------------------- 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 @@ -372,6 +372,11 @@ /// coverage pass should actually not be instrumented. std::vector SanitizeCoverageIgnorelistFiles; + /// Name of the stack usage file (i.e., .su file) if user passes + /// -fstack-usage. If empty, it can be implied that -fstack-usage is not + /// passed on the command line. + 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/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2709,6 +2709,12 @@ CodeGenOpts<"StackSizeSection">, DefaultFalse, PosFlag, NegFlag>; +def fstack_usage : Flag<["-"], "fstack-usage">, Group, + HelpText<"Emit .su file containing information on function stack sizes">; +def stack_usage_file : Separate<["-"], "stack-usage-file">, + Flags<[CC1Option, NoDriverOption]>, + HelpText<"Filename (or -) to write stack usage output to">, + 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,7 @@ Options.ExplicitEmulatedTLS = CodeGenOpts.ExplicitEmulatedTLS; Options.DebuggerTuning = CodeGenOpts.getDebuggerTuning(); Options.EmitStackSizeSection = CodeGenOpts.StackSizeSection; + 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 @@ -5492,6 +5492,18 @@ options::OPT_fno_stack_size_section, RawTriple.isPS4())) CmdArgs.push_back("-fstack-size-section"); + if (Args.hasArg(options::OPT_fstack_usage)) { + CmdArgs.push_back("-stack-usage-file"); + + 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(OutputFilename)); + } else + CmdArgs.push_back( + Args.MakeArgString(Twine(getBaseInputStem(Args, Inputs)) + ".su")); + } + 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 @@ -1931,6 +1931,9 @@ if (UsingSampleProfile) NeedLocTracking = true; + if (!Opts.StackUsageOutput.empty()) + NeedLocTracking = true; + // 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,19 @@ +// REQUIRES: aarch64-registered-target + +// RUN: rm -rf %t && mkdir %t && cd %t +// RUN: %clang_cc1 -triple aarch64-unknown -stack-usage-file b.su -emit-obj %s -o b.o +// RUN: FileCheck %s < b.su + +// CHECK: stack-usage.c:[[#@LINE+1]]:foo {{[0-9]+}} static +int foo() { + char a[8]; + + return 0; +} + +// CHECK: stack-usage.c:[[#@LINE+1]]: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,7 @@ +// RUN: %clang -target aarch64-unknown %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ABSENT +// CHECK-ABSENT-NOT: "-stack-usage-file" + +// RUN: %clang -target aarch64-unknown -fstack-usage %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-PRESENT +// CHECK-PRESENT: "-stack-usage-file" + +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 @@ -182,6 +182,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; + static char ID; protected: @@ -358,6 +361,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 @@ -342,6 +342,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 user passes + /// -fstack-usage. If empty, it can be implied that -fstack-usage is not + /// passed on the command line. + 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 @@ -1185,6 +1185,37 @@ OutStreamer->PopSection(); } +void AsmPrinter::emitStackUsage(const MachineFunction &MF) { + const std::string &OutputFilename = MF.getTarget().Options.StackUsageOutput; + + // OutputFilename empty implies -fstack-usage is not passed. + if (OutputFilename.empty()) + return; + + const MachineFrameInfo &FrameInfo = MF.getFrameInfo(); + uint64_t StackSize = FrameInfo.getStackSize(); + + 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()) @@ -1469,6 +1500,9 @@ // Emit section containing stack size metadata. emitStackSizeSection(*MF); + // Emit .su file containing function stack size information. + emitStackUsage(*MF); + emitPatchableFunctionEntries(); if (isVerbose())