forked from OSchip/llvm-project
[llvm-remarkutil] Add an option to print out function sizes
This adds an `instruction-count` command to llvm-remarkutil. ``` llvm-remarkutil instruction-count --parser=<bitstream|yaml> <file> ``` This will, for now, only print out asm-printer `InstructionCount` remarks. Frequently I need to find out things like "what are the top 10 largest functions" in a given project. This makes it so we can find that information quickly and easily from any format of remarks. I chose a CSV because I usually want to stick these into a spreadsheet, and the data is two-dimensional. In the future, we may want to change this to another format if we add more complicated data. Differential Revision: https://reviews.llvm.org/D134765
This commit is contained in:
parent
704b2e162c
commit
a4591a61df
|
@ -19,6 +19,7 @@ Subcommands
|
|||
|
||||
* :ref:`bitstream2yaml_subcommand` - Reserialize bitstream remarks to YAML.
|
||||
* :ref:`yaml2bitstream_subcommand` - Reserialize YAML remarks to bitstream.
|
||||
* :ref:`instruction-count_subcommand` - Output function instruction counts.
|
||||
|
||||
.. _bitstream2yaml_subcommand:
|
||||
|
||||
|
@ -48,3 +49,26 @@ Summary
|
|||
|
||||
Takes a YAML remark file as input, and reserializes that file in the bitstream
|
||||
format.
|
||||
|
||||
.. _instruction-count_subcommand:
|
||||
|
||||
instruction-count
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. program:: llvm-remarkutil instruction-count
|
||||
|
||||
USAGE: :program:`llvm-remarkutil` instruction-count <input file> --parser=<bitstream|yaml> -o <output file>
|
||||
|
||||
Summary
|
||||
^^^^^^^
|
||||
|
||||
Outputs instruction count remarks for every function. Instruction count remarks
|
||||
encode the number of instructions in a function at assembly printing time.
|
||||
|
||||
Instruction count remarks require asm-printer remarks.
|
||||
|
||||
CSV format is as follows:
|
||||
|
||||
::
|
||||
Function,InstructionCount
|
||||
foo,123
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
--- !Analysis
|
||||
Pass: asm-printer
|
||||
Name: InstructionCount
|
||||
Function: func1
|
||||
Args:
|
||||
- NumInstructions: '1'
|
||||
- String: ' instructions in function'
|
||||
...
|
||||
--- !Analysis
|
||||
Pass: asm-printer
|
||||
Name: InstructionCount
|
||||
Function: func2
|
||||
Args:
|
||||
- NumInstructions: '2'
|
||||
- String: ' instructions in function'
|
||||
...
|
||||
--- !Analysis
|
||||
Pass: asm-printer
|
||||
Name: InstructionCount
|
||||
Function: func3
|
||||
Args:
|
||||
- NumInstructions: '3'
|
||||
- String: ' instructions in function'
|
||||
...
|
|
@ -0,0 +1,12 @@
|
|||
--- !Analysis
|
||||
Pass: foo0
|
||||
Name: bar0
|
||||
Function: baz0
|
||||
Args:
|
||||
- Arg: 'arg'
|
||||
--- !Analysis
|
||||
Pass: foo1
|
||||
Name: bar1
|
||||
Function: baz1
|
||||
Args:
|
||||
- Arg: 'arg'
|
|
@ -1,2 +1,3 @@
|
|||
RUN: not llvm-remarkutil bitstream2yaml %p/Inputs/broken-remark -o - 2>&1 | FileCheck %s
|
||||
RUN: not llvm-remarkutil instruction-count --parser=bitstream %p/Inputs/broken-remark -o - 2>&1 | FileCheck %s
|
||||
CHECK: error: Unknown magic number: expecting RMRK, got --- .
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
RUN: not llvm-remarkutil yaml2bitstream %p/Inputs/broken-remark -o - 2>&1 | FileCheck %s
|
||||
RUN: not llvm-remarkutil instruction-count --parser=yaml %p/Inputs/broken-remark -o - 2>&1 | FileCheck %s
|
||||
CHECK: error: Type, Pass, Name or Function missing
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
RUN: not llvm-remarkutil yaml2bitstream %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --check-prefix=YAML2BITSTREAM
|
||||
RUN: not llvm-remarkutil yaml2bitstream %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --check-prefix=YAMLPARSER
|
||||
RUN: not llvm-remarkutil instruction-count --parser=yaml %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --check-prefix=YAMLPARSER
|
||||
RUN: llvm-remarkutil bitstream2yaml %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --allow-empty --check-prefix=BITSTREAM2YAML
|
||||
RUN: llvm-remarkutil instruction-count --parser=bitstream %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --allow-empty --check-prefix=SIZEBITSTREAM
|
||||
|
||||
; YAML2BITSTREAM: error: document root is not of mapping type.
|
||||
; YAMLPARSER: error: document root is not of mapping type.
|
||||
|
||||
; An empty bitstream file is valid.
|
||||
; BITSTREAM2YAML-NOT: error
|
||||
|
||||
; SIZEBITSTREAM-LABEL: Function,InstructionCount
|
||||
; SIZEBITSTREAM-EMPTY
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
RUN: llvm-remarkutil instruction-count --parser=yaml %p/Inputs/instruction-count.yaml | FileCheck %s
|
||||
RUN: llvm-remarkutil yaml2bitstream %p/Inputs/instruction-count.yaml | llvm-remarkutil instruction-count --parser=bitstream | FileCheck %s
|
||||
|
||||
; CHECK-LABEL: Function,InstructionCount
|
||||
; CHECK: func1,1
|
||||
; CHECK: func2,2
|
||||
; CHECK: func3,3
|
|
@ -0,0 +1,5 @@
|
|||
RUN: llvm-remarkutil instruction-count --parser=yaml %p/Inputs/made-up-fake-remarks.yaml | FileCheck %s
|
||||
RUN: llvm-remarkutil yaml2bitstream %p/Inputs/made-up-fake-remarks.yaml | llvm-remarkutil instruction-count --parser=bitstream | FileCheck %s
|
||||
|
||||
; CHECK-LABEL: Function,InstructionCount
|
||||
; CHECK-EMPTY
|
|
@ -35,12 +35,14 @@ static cl::SubCommand
|
|||
static cl::SubCommand
|
||||
Bitstream2YAML("bitstream2yaml",
|
||||
"Convert bitstream remarks to YAML remarks");
|
||||
static cl::SubCommand InstructionCount(
|
||||
"instruction-count",
|
||||
"Function instruction count information (requires asm-printer remarks)");
|
||||
} // namespace subopts
|
||||
|
||||
// Conversions have the same command line options. AFAIK there is no way to
|
||||
// reuse them, so to avoid duplication, let's just stick this in a hideous
|
||||
// macro.
|
||||
#define CONVERSION_COMMAND_LINE_OPTIONS(SUBOPT) \
|
||||
// Keep input + output help + names consistent across the various modes via a
|
||||
// hideous macro.
|
||||
#define INPUT_OUTPUT_COMMAND_LINE_OPTIONS(SUBOPT) \
|
||||
static cl::opt<std::string> InputFileName( \
|
||||
cl::Positional, cl::cat(RemarkUtilCategory), cl::init("-"), \
|
||||
cl::desc("<input file>"), cl::sub(SUBOPT)); \
|
||||
|
@ -52,7 +54,7 @@ namespace yaml2bitstream {
|
|||
static constexpr Format InputFormat = Format::YAML;
|
||||
/// Remark format to output.
|
||||
static constexpr Format OutputFormat = Format::Bitstream;
|
||||
CONVERSION_COMMAND_LINE_OPTIONS(subopts::YAML2Bitstream)
|
||||
INPUT_OUTPUT_COMMAND_LINE_OPTIONS(subopts::YAML2Bitstream)
|
||||
} // namespace yaml2bitstream
|
||||
|
||||
namespace bitstream2yaml {
|
||||
|
@ -60,9 +62,18 @@ namespace bitstream2yaml {
|
|||
static constexpr Format InputFormat = Format::Bitstream;
|
||||
/// Remark format to output.
|
||||
static constexpr Format OutputFormat = Format::YAML;
|
||||
CONVERSION_COMMAND_LINE_OPTIONS(subopts::Bitstream2YAML)
|
||||
INPUT_OUTPUT_COMMAND_LINE_OPTIONS(subopts::Bitstream2YAML)
|
||||
} // namespace bitstream2yaml
|
||||
|
||||
namespace instructioncount {
|
||||
static cl::opt<Format> InputFormat(
|
||||
"parser", cl::desc("Input remark format to parse"),
|
||||
cl::values(clEnumValN(Format::YAML, "yaml", "YAML"),
|
||||
clEnumValN(Format::Bitstream, "bitstream", "Bitstream")),
|
||||
cl::sub(subopts::InstructionCount));
|
||||
INPUT_OUTPUT_COMMAND_LINE_OPTIONS(subopts::InstructionCount)
|
||||
} // namespace instructioncount
|
||||
|
||||
/// \returns A MemoryBuffer for the input file on success, and an Error
|
||||
/// otherwise.
|
||||
static Expected<std::unique_ptr<MemoryBuffer>>
|
||||
|
@ -75,14 +86,15 @@ getInputMemoryBuffer(StringRef InputFileName) {
|
|||
return std::move(*MaybeBuf);
|
||||
}
|
||||
|
||||
/// \returns A ToolOutputFile which can be used for writing remarks on success,
|
||||
/// and an Error otherwise.
|
||||
/// \returns A ToolOutputFile which can be used for outputting the results of
|
||||
/// some tool mode.
|
||||
/// \p OutputFileName is the desired destination.
|
||||
/// \p Flags controls whether or not the file is opened for writing in text
|
||||
/// mode, as a binary, etc. See sys::fs::OpenFlags for more detail.
|
||||
static Expected<std::unique_ptr<ToolOutputFile>>
|
||||
getOutputFile(StringRef OutputFileName, Format OutputFormat) {
|
||||
getOutputFileWithFlags(StringRef OutputFileName, sys::fs::OpenFlags Flags) {
|
||||
if (OutputFileName == "")
|
||||
OutputFileName = "-";
|
||||
auto Flags = OutputFormat == Format::YAML ? sys::fs::OF_TextWithCRLF
|
||||
: sys::fs::OF_None;
|
||||
std::error_code ErrorCode;
|
||||
auto OF = std::make_unique<ToolOutputFile>(OutputFileName, ErrorCode, Flags);
|
||||
if (ErrorCode)
|
||||
|
@ -90,6 +102,19 @@ getOutputFile(StringRef OutputFileName, Format OutputFormat) {
|
|||
return std::move(OF);
|
||||
}
|
||||
|
||||
/// \returns A ToolOutputFile which can be used for writing remarks on success,
|
||||
/// and an Error otherwise.
|
||||
/// \p OutputFileName is the desired destination.
|
||||
/// \p OutputFormat
|
||||
static Expected<std::unique_ptr<ToolOutputFile>>
|
||||
getOutputFileForRemarks(StringRef OutputFileName, Format OutputFormat) {
|
||||
assert((OutputFormat == Format::YAML || OutputFormat == Format::Bitstream) &&
|
||||
"Expected one of YAML or Bitstream!");
|
||||
return getOutputFileWithFlags(OutputFileName, OutputFormat == Format::YAML
|
||||
? sys::fs::OF_TextWithCRLF
|
||||
: sys::fs::OF_None);
|
||||
}
|
||||
|
||||
namespace yaml2bitstream {
|
||||
/// Parses all remarks in the input YAML file.
|
||||
/// \p [out] ParsedRemarks - Filled with remarks parsed from the input file.
|
||||
|
@ -125,7 +150,7 @@ tryParseRemarksFromYAMLFile(std::vector<std::unique_ptr<Remark>> &ParsedRemarks,
|
|||
static Error tryReserializeYAML2Bitstream(
|
||||
const std::vector<std::unique_ptr<Remark>> &ParsedRemarks,
|
||||
StringTable &StrTab) {
|
||||
auto MaybeOF = getOutputFile(OutputFileName, OutputFormat);
|
||||
auto MaybeOF = getOutputFileForRemarks(OutputFileName, OutputFormat);
|
||||
if (!MaybeOF)
|
||||
return MaybeOF.takeError();
|
||||
auto OF = std::move(*MaybeOF);
|
||||
|
@ -155,7 +180,7 @@ namespace bitstream2yaml {
|
|||
/// \returns An Error if reserialization fails, or Error::success() on success.
|
||||
static Error tryBitstream2YAML() {
|
||||
// Create the serializer.
|
||||
auto MaybeOF = getOutputFile(OutputFileName, OutputFormat);
|
||||
auto MaybeOF = getOutputFileForRemarks(OutputFileName, OutputFormat);
|
||||
if (!MaybeOF)
|
||||
return MaybeOF.takeError();
|
||||
auto OF = std::move(*MaybeOF);
|
||||
|
@ -186,6 +211,49 @@ static Error tryBitstream2YAML() {
|
|||
}
|
||||
} // namespace bitstream2yaml
|
||||
|
||||
namespace instructioncount {
|
||||
/// Outputs all instruction count remarks in the file as a CSV.
|
||||
/// \returns Error::success() on success, and an Error otherwise.
|
||||
static Error tryInstructionCount() {
|
||||
// Create the output buffer.
|
||||
auto MaybeOF = getOutputFileWithFlags(OutputFileName,
|
||||
/*Flags = */ sys::fs::OF_TextWithCRLF);
|
||||
if (!MaybeOF)
|
||||
return MaybeOF.takeError();
|
||||
auto OF = std::move(*MaybeOF);
|
||||
// Create a parser for the user-specified input format.
|
||||
auto MaybeBuf = getInputMemoryBuffer(InputFileName);
|
||||
if (!MaybeBuf)
|
||||
return MaybeBuf.takeError();
|
||||
auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer());
|
||||
if (!MaybeParser)
|
||||
return MaybeParser.takeError();
|
||||
// Emit CSV header.
|
||||
OF->os() << "Function,InstructionCount\n";
|
||||
// Parse all remarks. Whenever we see an instruction count remark, output
|
||||
// the file name and the number of instructions.
|
||||
auto &Parser = **MaybeParser;
|
||||
auto MaybeRemark = Parser.next();
|
||||
for (; MaybeRemark; MaybeRemark = Parser.next()) {
|
||||
auto &Remark = **MaybeRemark;
|
||||
if (Remark.RemarkName != "InstructionCount")
|
||||
continue;
|
||||
auto *InstrCountArg = find_if(Remark.Args, [](const Argument &Arg) {
|
||||
return Arg.Key == "NumInstructions";
|
||||
});
|
||||
assert(InstrCountArg != Remark.Args.end() &&
|
||||
"Expected instruction count remarks to have a NumInstructions key?");
|
||||
OF->os() << Remark.FunctionName << "," << InstrCountArg->Val << "\n";
|
||||
}
|
||||
auto E = MaybeRemark.takeError();
|
||||
if (!E.isA<EndOfFileError>())
|
||||
return E;
|
||||
consumeError(std::move(E));
|
||||
OF->keep();
|
||||
return Error::success();
|
||||
}
|
||||
} // namespace instructioncount
|
||||
|
||||
/// Handle user-specified suboptions (e.g. yaml2bitstream, bitstream2yaml).
|
||||
/// \returns An Error if the specified suboption fails or if no suboption was
|
||||
/// specified. Otherwise, Error::success().
|
||||
|
@ -194,6 +262,8 @@ static Error handleSuboptions() {
|
|||
return bitstream2yaml::tryBitstream2YAML();
|
||||
if (subopts::YAML2Bitstream)
|
||||
return yaml2bitstream::tryYAML2Bitstream();
|
||||
if (subopts::InstructionCount)
|
||||
return instructioncount::tryInstructionCount();
|
||||
return make_error<StringError>(
|
||||
"Please specify a subcommand. (See -help for options)",
|
||||
inconvertibleErrorCode());
|
||||
|
|
Loading…
Reference in New Issue