Support GCC's -fstack-usage flag

This patch adds support for GCC's -fstack-usage flag. With this flag, a stack
usage file (i.e., .su file) is generated for each input source file. The format
of the stack usage file is also similar to what is used by GCC. For each
function defined in the source file, a line with the following information is
produced in the .su file.

<source_file>:<line_number>:<function_name> <size_in_byte> <static/dynamic>

"Static" means that the function's frame size is static and the size info is an
accurate reflection of the frame size. While "dynamic" means the function's
frame size can only be determined at run-time because the function manipulates
the stack dynamically (e.g., due to variable size objects). The size info only
reflects the size of the fixed size frame objects in this case and therefore is
not a reliable measure of the total frame size.

Reviewed By: MaskRay

Differential Revision: https://reviews.llvm.org/D100509
This commit is contained in:
Pengxuan Zheng 2021-04-14 13:54:47 -07:00
parent c012a388a1
commit c9b36a041f
11 changed files with 101 additions and 0 deletions

View File

@ -70,6 +70,10 @@ New Compiler Flags
- ``-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
-------------------------

View File

@ -372,6 +372,11 @@ public:
/// coverage pass should actually not be instrumented.
std::vector<std::string> 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;

View File

@ -2709,6 +2709,12 @@ defm stack_size_section : BoolFOption<"stack-size-section",
CodeGenOpts<"StackSizeSection">, DefaultFalse,
PosFlag<SetTrue, [CC1Option], "Emit section containing metadata on function stack sizes">,
NegFlag<SetFalse>>;
def fstack_usage : Flag<["-"], "fstack-usage">, Group<f_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<CodeGenOpts<"StackUsageOutput">>;
defm unique_basic_block_section_names : BoolFOption<"unique-basic-block-section-names",
CodeGenOpts<"UniqueBasicBlockSectionNames">, DefaultFalse,

View File

@ -567,6 +567,7 @@ static bool initTargetOptions(DiagnosticsEngine &Diags,
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;

View File

@ -5492,6 +5492,18 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
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());

View File

@ -1931,6 +1931,9 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
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)

View File

@ -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;
}

View File

@ -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; }

View File

@ -182,6 +182,9 @@ private:
/// 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<raw_fd_ostream> StackUsageStream;
static char ID;
protected:
@ -358,6 +361,8 @@ public:
void emitStackSizeSection(const MachineFunction &MF);
void emitStackUsage(const MachineFunction &MF);
void emitBBAddrMapSection(const MachineFunction &MF);
void emitPseudoProbe(const MachineInstr &MI);

View File

@ -342,6 +342,11 @@ namespace llvm {
/// 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

View File

@ -1185,6 +1185,37 @@ void AsmPrinter::emitStackSizeSection(const MachineFunction &MF) {
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<raw_fd_ostream>(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 @@ void AsmPrinter::emitFunctionBody() {
// Emit section containing stack size metadata.
emitStackSizeSection(*MF);
// Emit .su file containing function stack size information.
emitStackUsage(*MF);
emitPatchableFunctionEntries();
if (isVerbose())