diff --git a/llvm/docs/CommandGuide/llvm-remarkutil.rst b/llvm/docs/CommandGuide/llvm-remarkutil.rst --- a/llvm/docs/CommandGuide/llvm-remarkutil.rst +++ b/llvm/docs/CommandGuide/llvm-remarkutil.rst @@ -19,6 +19,7 @@ * :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 @@ 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 --parser= -o + +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 diff --git a/llvm/test/tools/llvm-remarkutil/Inputs/instruction-count.yaml b/llvm/test/tools/llvm-remarkutil/Inputs/instruction-count.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-remarkutil/Inputs/instruction-count.yaml @@ -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' +... diff --git a/llvm/test/tools/llvm-remarkutil/Inputs/made-up-fake-remarks.yaml b/llvm/test/tools/llvm-remarkutil/Inputs/made-up-fake-remarks.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-remarkutil/Inputs/made-up-fake-remarks.yaml @@ -0,0 +1,12 @@ +--- !Analysis +Pass: foo0 +Name: bar0 +Function: baz0 +Args: + - Arg: 'arg' +--- !Analysis +Pass: foo1 +Name: bar1 +Function: baz1 +Args: + - Arg: 'arg' diff --git a/llvm/test/tools/llvm-remarkutil/broken-bitstream-remark.test b/llvm/test/tools/llvm-remarkutil/broken-bitstream-remark.test --- a/llvm/test/tools/llvm-remarkutil/broken-bitstream-remark.test +++ b/llvm/test/tools/llvm-remarkutil/broken-bitstream-remark.test @@ -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 --- . diff --git a/llvm/test/tools/llvm-remarkutil/broken-yaml-remark.test b/llvm/test/tools/llvm-remarkutil/broken-yaml-remark.test --- a/llvm/test/tools/llvm-remarkutil/broken-yaml-remark.test +++ b/llvm/test/tools/llvm-remarkutil/broken-yaml-remark.test @@ -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 diff --git a/llvm/test/tools/llvm-remarkutil/empty-file.test b/llvm/test/tools/llvm-remarkutil/empty-file.test --- a/llvm/test/tools/llvm-remarkutil/empty-file.test +++ b/llvm/test/tools/llvm-remarkutil/empty-file.test @@ -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 diff --git a/llvm/test/tools/llvm-remarkutil/instruction-count.test b/llvm/test/tools/llvm-remarkutil/instruction-count.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-remarkutil/instruction-count.test @@ -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 diff --git a/llvm/test/tools/llvm-remarkutil/no-instruction-count.test b/llvm/test/tools/llvm-remarkutil/no-instruction-count.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-remarkutil/no-instruction-count.test @@ -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 diff --git a/llvm/tools/llvm-remarkutil/RemarkUtil.cpp b/llvm/tools/llvm-remarkutil/RemarkUtil.cpp --- a/llvm/tools/llvm-remarkutil/RemarkUtil.cpp +++ b/llvm/tools/llvm-remarkutil/RemarkUtil.cpp @@ -35,12 +35,14 @@ 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 InputFileName( \ cl::Positional, cl::cat(RemarkUtilCategory), cl::init("-"), \ cl::desc(""), cl::sub(SUBOPT)); \ @@ -52,7 +54,7 @@ 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 @@ 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 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> @@ -75,14 +86,15 @@ 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> -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(OutputFileName, ErrorCode, Flags); if (ErrorCode) @@ -90,6 +102,19 @@ 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> +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 @@ static Error tryReserializeYAML2Bitstream( const std::vector> &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 @@ /// \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 @@ } } // 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()) + 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 @@ return bitstream2yaml::tryBitstream2YAML(); if (subopts::YAML2Bitstream) return yaml2bitstream::tryYAML2Bitstream(); + if (subopts::InstructionCount) + return instructioncount::tryInstructionCount(); return make_error( "Please specify a subcommand. (See -help for options)", inconvertibleErrorCode());