Index: llvm/docs/CommandGuide/llvm-remarkutil.rst =================================================================== --- llvm/docs/CommandGuide/llvm-remarkutil.rst +++ 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:`size_subcommand` - Output function sizes from asm-printer remarks. .. _bitstream2yaml_subcommand: @@ -48,3 +49,24 @@ Takes a YAML remark file as input, and reserializes that file in the bitstream format. + +.. _size_subcommand: + +size +~~~~ + +.. program:: llvm-remarkutil size + +USAGE: :program:`llvm-remarkutil` size --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. + +CSV format is as follows: + +:: + Function,InstructionCount + foo,123 Index: llvm/test/tools/llvm-remarkutil/Inputs/instruction-count.yaml =================================================================== --- /dev/null +++ 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' +... Index: llvm/test/tools/llvm-remarkutil/Inputs/made-up-fake-remarks.yaml =================================================================== --- /dev/null +++ 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' Index: llvm/test/tools/llvm-remarkutil/broken-bitstream-remark.test =================================================================== --- llvm/test/tools/llvm-remarkutil/broken-bitstream-remark.test +++ 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 size --parser=bitstream %p/Inputs/broken-remark -o - 2>&1 | FileCheck %s CHECK: error: Unknown magic number: expecting RMRK, got --- . Index: llvm/test/tools/llvm-remarkutil/broken-yaml-remark.test =================================================================== --- llvm/test/tools/llvm-remarkutil/broken-yaml-remark.test +++ 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 size --parser=yaml %p/Inputs/broken-remark -o - 2>&1 | FileCheck %s CHECK: error: Type, Pass, Name or Function missing Index: llvm/test/tools/llvm-remarkutil/empty-file.test =================================================================== --- llvm/test/tools/llvm-remarkutil/empty-file.test +++ 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 size --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 size --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 Index: llvm/test/tools/llvm-remarkutil/size-instruction-count.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-remarkutil/size-instruction-count.test @@ -0,0 +1,7 @@ +RUN: llvm-remarkutil size --parser=yaml %p/Inputs/instruction-count.yaml | FileCheck %s +RUN: llvm-remarkutil yaml2bitstream %p/Inputs/instruction-count.yaml | llvm-remarkutil size --parser=bitstream | FileCheck %s + +; CHECK-LABEL: Function,InstructionCount +; CHECK: func1,1 +; CHECK: func2,2 +; CHECK: func3,3 Index: llvm/test/tools/llvm-remarkutil/size-no-instruction-count.yaml =================================================================== --- /dev/null +++ llvm/test/tools/llvm-remarkutil/size-no-instruction-count.yaml @@ -0,0 +1,5 @@ +RUN: llvm-remarkutil size --parser=yaml %p/Inputs/made-up-fake-remarks.yaml | FileCheck %s +RUN: llvm-remarkutil yaml2bitstream %p/Inputs/made-up-fake-remarks.yaml | llvm-remarkutil size --parser=bitstream | FileCheck %s + +; CHECK-LABEL: Function,InstructionCount +; CHECK-EMPTY Index: llvm/tools/llvm-remarkutil/RemarkUtil.cpp =================================================================== --- llvm/tools/llvm-remarkutil/RemarkUtil.cpp +++ llvm/tools/llvm-remarkutil/RemarkUtil.cpp @@ -35,12 +35,13 @@ static cl::SubCommand Bitstream2YAML("bitstream2yaml", "Convert bitstream remarks to YAML remarks"); +static cl::SubCommand + Size("size", "Function size 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 +53,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 +61,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 size { +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::Size)); +INPUT_OUTPUT_COMMAND_LINE_OPTIONS(subopts::Size) +} // namespace size + /// \returns A MemoryBuffer for the input file on success, and an Error /// otherwise. static Expected> @@ -75,14 +85,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 +101,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 +149,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 +179,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 +210,49 @@ } } // namespace bitstream2yaml +namespace size { +/// Outputs all instruction count remarks in the file as a CSV. +/// \returns Error::success() on success, and an Error otherwise. +static Error trySize() { + // 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 size + /// 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 +261,8 @@ return bitstream2yaml::tryBitstream2YAML(); if (subopts::YAML2Bitstream) return yaml2bitstream::tryYAML2Bitstream(); + if (subopts::Size) + return size::trySize(); return make_error( "Please specify a subcommand. (See -help for options)", inconvertibleErrorCode());