diff --git a/llvm/tools/llvm-remarkutil/CMakeLists.txt b/llvm/tools/llvm-remarkutil/CMakeLists.txt --- a/llvm/tools/llvm-remarkutil/CMakeLists.txt +++ b/llvm/tools/llvm-remarkutil/CMakeLists.txt @@ -5,5 +5,9 @@ ) add_llvm_tool(llvm-remarkutil + RemarkConvert.cpp + RemarkCount.cpp RemarkUtil.cpp + RemarkUtilHelpers.cpp + RemarkUtilRegistry.cpp ) diff --git a/llvm/tools/llvm-remarkutil/RemarkConvert.cpp b/llvm/tools/llvm-remarkutil/RemarkConvert.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-remarkutil/RemarkConvert.cpp @@ -0,0 +1,161 @@ +//===- RemarkConvert.cpp -------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Convert remarks from bitstream to yaml and the other way around. +// +//===----------------------------------------------------------------------===// + +#include "RemarkUtilHelpers.h" +#include "RemarkUtilRegistry.h" +#include "llvm-c/Remarks.h" +#include "llvm/ADT/MapVector.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Remarks/Remark.h" +#include "llvm/Remarks/RemarkFormat.h" +#include "llvm/Remarks/RemarkParser.h" +#include "llvm/Remarks/YAMLRemarkSerializer.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/WithColor.h" + +using namespace llvm; +using namespace remarks; +using namespace llvm::remarkutil; + +extern ExitOnError ExitOnErr; +static cl::SubCommand + YAML2Bitstream("yaml2bitstream", + "Convert YAML remarks to bitstream remarks"); +static cl::SubCommand + Bitstream2YAML("bitstream2yaml", + "Convert bitstream remarks to YAML remarks"); + +namespace yaml2bitstream { +/// Remark format to parse. +static constexpr Format InputFormat = Format::YAML; +/// Remark format to output. +static constexpr Format OutputFormat = Format::Bitstream; +INPUT_OUTPUT_COMMAND_LINE_OPTIONS(YAML2Bitstream) +} // namespace yaml2bitstream + +namespace bitstream2yaml { +/// Remark format to parse. +static constexpr Format InputFormat = Format::Bitstream; +/// Remark format to output. +static constexpr Format OutputFormat = Format::YAML; +INPUT_OUTPUT_COMMAND_LINE_OPTIONS(Bitstream2YAML) +} // namespace bitstream2yaml + +namespace yaml2bitstream { +/// Parses all remarks in the input YAML file. +/// \p [out] ParsedRemarks - Filled with remarks parsed from the input file. +/// \p [out] StrTab - A string table populated for later remark serialization. +/// \returns Error::success() if all remarks were successfully parsed, and an +/// Error otherwise. +static Error +tryParseRemarksFromYAMLFile(std::vector> &ParsedRemarks, + StringTable &StrTab) { + auto MaybeBuf = getInputMemoryBuffer(InputFileName); + if (!MaybeBuf) + return MaybeBuf.takeError(); + auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer()); + if (!MaybeParser) + return MaybeParser.takeError(); + auto &Parser = **MaybeParser; + auto MaybeRemark = Parser.next(); + for (; MaybeRemark; MaybeRemark = Parser.next()) { + StrTab.internalize(**MaybeRemark); + ParsedRemarks.push_back(std::move(*MaybeRemark)); + } + auto E = MaybeRemark.takeError(); + if (!E.isA()) + return E; + consumeError(std::move(E)); + return Error::success(); +} + +/// Reserialize a list of parsed YAML remarks into bitstream remarks. +/// \p ParsedRemarks - A list of remarks. +/// \p StrTab - The string table for the remarks. +/// \returns Error::success() on success. +static Error tryReserializeYAML2Bitstream( + const std::vector> &ParsedRemarks, + StringTable &StrTab) { + auto MaybeOF = getOutputFileForRemarks(OutputFileName, OutputFormat); + if (!MaybeOF) + return MaybeOF.takeError(); + auto OF = std::move(*MaybeOF); + auto MaybeSerializer = createRemarkSerializer( + OutputFormat, SerializerMode::Standalone, OF->os(), std::move(StrTab)); + if (!MaybeSerializer) + return MaybeSerializer.takeError(); + auto Serializer = std::move(*MaybeSerializer); + for (const auto &Remark : ParsedRemarks) + Serializer->emit(*Remark); + OF->keep(); + return Error::success(); +} + +/// Parse YAML remarks and reserialize as bitstream remarks. +/// \returns Error::success() on success, and an Error otherwise. +static Error tryYAML2Bitstream() { + StringTable StrTab; + std::vector> ParsedRemarks; + ExitOnErr(tryParseRemarksFromYAMLFile(ParsedRemarks, StrTab)); + return tryReserializeYAML2Bitstream(ParsedRemarks, StrTab); +} +} // namespace yaml2bitstream + +namespace bitstream2yaml { +/// Parse bitstream remarks and reserialize as YAML remarks. +/// \returns An Error if reserialization fails, or Error::success() on success. +static Error tryBitstream2YAML() { + // Create the serializer. + auto MaybeOF = getOutputFileForRemarks(OutputFileName, OutputFormat); + if (!MaybeOF) + return MaybeOF.takeError(); + auto OF = std::move(*MaybeOF); + auto MaybeSerializer = createRemarkSerializer( + OutputFormat, SerializerMode::Standalone, OF->os()); + if (!MaybeSerializer) + return MaybeSerializer.takeError(); + + // Create the parser. + auto MaybeBuf = getInputMemoryBuffer(InputFileName); + if (!MaybeBuf) + return MaybeBuf.takeError(); + auto Serializer = std::move(*MaybeSerializer); + auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer()); + if (!MaybeParser) + return MaybeParser.takeError(); + auto &Parser = **MaybeParser; + + // Parse + reserialize all remarks. + auto MaybeRemark = Parser.next(); + for (; MaybeRemark; MaybeRemark = Parser.next()) + Serializer->emit(**MaybeRemark); + auto E = MaybeRemark.takeError(); + if (!E.isA()) + return E; + consumeError(std::move(E)); + return Error::success(); +} +} // namespace bitstream2yaml + +static CommandRegistration Bitstream2YamlReg(&Bitstream2YAML, + bitstream2yaml::tryBitstream2YAML); +static CommandRegistration Yaml2Bitstream(&YAML2Bitstream, + yaml2bitstream::tryYAML2Bitstream); diff --git a/llvm/tools/llvm-remarkutil/RemarkCount.cpp b/llvm/tools/llvm-remarkutil/RemarkCount.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-remarkutil/RemarkCount.cpp @@ -0,0 +1,177 @@ +//===- RemarkCount.cpp ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Count remarks using `instruction-count` for asm-printer remarks and +// `annotation-count` for annotation-remarks +// +//===----------------------------------------------------------------------===// +#include "RemarkUtilHelpers.h" +#include "RemarkUtilRegistry.h" +#include "llvm-c/Remarks.h" +#include "llvm/ADT/MapVector.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Remarks/Remark.h" +#include "llvm/Remarks/RemarkFormat.h" +#include "llvm/Remarks/RemarkParser.h" +#include "llvm/Remarks/YAMLRemarkSerializer.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/WithColor.h" + +using namespace llvm; +using namespace remarks; +using namespace llvm::remarkutil; + +static cl::SubCommand InstructionCount( + "instruction-count", + "Function instruction count information (requires asm-printer remarks)"); +static cl::SubCommand + AnnotationCount("annotation-count", + "Collect count information from annotation remarks (uses " + "AnnotationRemarksPass)"); + +namespace instructioncount { +INPUT_FORMAT_COMMAND_LINE_OPTIONS(InstructionCount) +INPUT_OUTPUT_COMMAND_LINE_OPTIONS(InstructionCount) +DEBUG_LOC_INFO_COMMAND_LINE_OPTIONS(InstructionCount) +} // namespace instructioncount + +namespace annotationcount { +INPUT_FORMAT_COMMAND_LINE_OPTIONS(AnnotationCount) +static cl::opt AnnotationTypeToCollect( + "annotation-type", cl::desc("annotation-type remark to collect count for"), + cl::sub(AnnotationCount)); +INPUT_OUTPUT_COMMAND_LINE_OPTIONS(AnnotationCount) +DEBUG_LOC_INFO_COMMAND_LINE_OPTIONS(AnnotationCount) +} // namespace annotationcount + +static bool shouldSkipRemark(bool UseDebugLoc, Remark &Remark) { + return UseDebugLoc && !Remark.Loc.has_value(); +} + +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. + if (UseDebugLoc) + OF->os() << "Source,"; + 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; + if (shouldSkipRemark(UseDebugLoc, Remark)) + 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?"); + if (UseDebugLoc) { + std::string Loc = Remark.Loc->SourceFilePath.str() + ":" + + std::to_string(Remark.Loc->SourceLine) + +":" + + std::to_string(Remark.Loc->SourceColumn); + OF->os() << Loc << ","; + } + 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 + +namespace annotationcount { +static Error tryAnnotationCount() { + // 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. + if (UseDebugLoc) + OF->os() << "Source,"; + OF->os() << "Function,Count\n"; + // Parse all remarks. When we see the specified remark collect the count + // information. + auto &Parser = **MaybeParser; + auto MaybeRemark = Parser.next(); + for (; MaybeRemark; MaybeRemark = Parser.next()) { + auto &Remark = **MaybeRemark; + if (Remark.RemarkName != "AnnotationSummary") + continue; + if (shouldSkipRemark(UseDebugLoc, Remark)) + continue; + auto *RemarkNameArg = find_if(Remark.Args, [](const Argument &Arg) { + return Arg.Key == "type" && Arg.Val == AnnotationTypeToCollect; + }); + if (RemarkNameArg == Remark.Args.end()) + continue; + auto *CountArg = find_if( + Remark.Args, [](const Argument &Arg) { return Arg.Key == "count"; }); + assert(CountArg != Remark.Args.end() && + "Expected annotation-type remark to have a count key?"); + if (UseDebugLoc) { + std::string Loc = Remark.Loc->SourceFilePath.str() + ":" + + std::to_string(Remark.Loc->SourceLine) + +":" + + std::to_string(Remark.Loc->SourceColumn); + OF->os() << Loc << ","; + } + OF->os() << Remark.FunctionName << "," << CountArg->Val << "\n"; + } + auto E = MaybeRemark.takeError(); + if (!E.isA()) + return E; + consumeError(std::move(E)); + OF->keep(); + return Error::success(); +} +} // namespace annotationcount + +static CommandRegistration + InstructionCountReg(&InstructionCount, + instructioncount::tryInstructionCount); +static CommandRegistration Yaml2Bitstream(&AnnotationCount, + annotationcount::tryAnnotationCount); 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 @@ -1,4 +1,4 @@ -//===--------- llvm-remarkutil/RemarkUtil.cpp -----------===// +//===--------- llvm-remarkutil/RemarkUtil.cpp -----------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -8,370 +8,34 @@ /// Utility for remark files. //===----------------------------------------------------------------------===// -#include "llvm-c/Remarks.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Remarks/Remark.h" -#include "llvm/Remarks/RemarkFormat.h" -#include "llvm/Remarks/RemarkParser.h" -#include "llvm/Remarks/YAMLRemarkSerializer.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Compiler.h" -#include "llvm/Support/Error.h" -#include "llvm/Support/FileSystem.h" +#include "RemarkUtilRegistry.h" #include "llvm/Support/InitLLVM.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/ToolOutputFile.h" -#include "llvm/Support/WithColor.h" using namespace llvm; -using namespace remarks; - -static ExitOnError ExitOnErr; -static cl::OptionCategory RemarkUtilCategory("llvm-remarkutil options"); -namespace subopts { -static cl::SubCommand - YAML2Bitstream("yaml2bitstream", - "Convert YAML remarks to bitstream remarks"); -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)"); -static cl::SubCommand - AnnotationCount("annotation-count", - "Collect count information from annotation remarks (uses " - "AnnotationRemarksPass)"); -} // namespace subopts - -// 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)); \ - static cl::opt OutputFileName( \ - "o", cl::init("-"), cl::cat(RemarkUtilCategory), cl::desc("Output"), \ - cl::value_desc("filename"), cl::sub(SUBOPT)); - -// Keep Input format and names consistent accross the modes via a macro. -#define INPUT_FORMAT_COMMAND_LINE_OPTIONS(SUBOPT) \ - 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(SUBOPT)); - -#define DEBUG_LOC_INFO_COMMAND_LINE_OPTIONS(SUBOPT) \ - static cl::opt UseDebugLoc( \ - "use-debug-loc", \ - cl::desc( \ - "Add debug loc information when generating tables for " \ - "functions. The loc is represented as (path:line number:column " \ - "number)"), \ - cl::init(false), cl::sub(SUBOPT)); -namespace yaml2bitstream { -/// Remark format to parse. -static constexpr Format InputFormat = Format::YAML; -/// Remark format to output. -static constexpr Format OutputFormat = Format::Bitstream; -INPUT_OUTPUT_COMMAND_LINE_OPTIONS(subopts::YAML2Bitstream) -} // namespace yaml2bitstream - -namespace bitstream2yaml { -/// Remark format to parse. -static constexpr Format InputFormat = Format::Bitstream; -/// Remark format to output. -static constexpr Format OutputFormat = Format::YAML; -INPUT_OUTPUT_COMMAND_LINE_OPTIONS(subopts::Bitstream2YAML) -} // namespace bitstream2yaml - -namespace instructioncount { -INPUT_FORMAT_COMMAND_LINE_OPTIONS(subopts::InstructionCount) -INPUT_OUTPUT_COMMAND_LINE_OPTIONS(subopts::InstructionCount) -DEBUG_LOC_INFO_COMMAND_LINE_OPTIONS(subopts::InstructionCount) -} // namespace instructioncount - -namespace annotationcount { -INPUT_FORMAT_COMMAND_LINE_OPTIONS(subopts::AnnotationCount) -static cl::opt AnnotationTypeToCollect( - "annotation-type", cl::desc("annotation-type remark to collect count for"), - cl::sub(subopts::AnnotationCount)); -INPUT_OUTPUT_COMMAND_LINE_OPTIONS(subopts::AnnotationCount) -DEBUG_LOC_INFO_COMMAND_LINE_OPTIONS(subopts::AnnotationCount) -} // namespace annotationcount - -/// \returns A MemoryBuffer for the input file on success, and an Error -/// otherwise. -static Expected> -getInputMemoryBuffer(StringRef InputFileName) { - auto MaybeBuf = MemoryBuffer::getFileOrSTDIN(InputFileName); - if (auto ErrorCode = MaybeBuf.getError()) - return createStringError(ErrorCode, - Twine("Cannot open file '" + InputFileName + - "': " + ErrorCode.message())); - return std::move(*MaybeBuf); -} - -/// \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> -getOutputFileWithFlags(StringRef OutputFileName, sys::fs::OpenFlags Flags) { - if (OutputFileName == "") - OutputFileName = "-"; - std::error_code ErrorCode; - auto OF = std::make_unique(OutputFileName, ErrorCode, Flags); - if (ErrorCode) - return errorCodeToError(ErrorCode); - 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); -} - -static bool shouldSkipRemark(bool UseDebugLoc, Remark &Remark) { - return UseDebugLoc && !Remark.Loc.has_value(); -} - -namespace yaml2bitstream { -/// Parses all remarks in the input YAML file. -/// \p [out] ParsedRemarks - Filled with remarks parsed from the input file. -/// \p [out] StrTab - A string table populated for later remark serialization. -/// \returns Error::success() if all remarks were successfully parsed, and an -/// Error otherwise. -static Error -tryParseRemarksFromYAMLFile(std::vector> &ParsedRemarks, - StringTable &StrTab) { - auto MaybeBuf = getInputMemoryBuffer(InputFileName); - if (!MaybeBuf) - return MaybeBuf.takeError(); - auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer()); - if (!MaybeParser) - return MaybeParser.takeError(); - auto &Parser = **MaybeParser; - auto MaybeRemark = Parser.next(); - for (; MaybeRemark; MaybeRemark = Parser.next()) { - StrTab.internalize(**MaybeRemark); - ParsedRemarks.push_back(std::move(*MaybeRemark)); - } - auto E = MaybeRemark.takeError(); - if (!E.isA()) - return E; - consumeError(std::move(E)); - return Error::success(); -} - -/// Reserialize a list of parsed YAML remarks into bitstream remarks. -/// \p ParsedRemarks - A list of remarks. -/// \p StrTab - The string table for the remarks. -/// \returns Error::success() on success. -static Error tryReserializeYAML2Bitstream( - const std::vector> &ParsedRemarks, - StringTable &StrTab) { - auto MaybeOF = getOutputFileForRemarks(OutputFileName, OutputFormat); - if (!MaybeOF) - return MaybeOF.takeError(); - auto OF = std::move(*MaybeOF); - auto MaybeSerializer = createRemarkSerializer( - OutputFormat, SerializerMode::Standalone, OF->os(), std::move(StrTab)); - if (!MaybeSerializer) - return MaybeSerializer.takeError(); - auto Serializer = std::move(*MaybeSerializer); - for (const auto &Remark : ParsedRemarks) - Serializer->emit(*Remark); - OF->keep(); - return Error::success(); -} - -/// Parse YAML remarks and reserialize as bitstream remarks. -/// \returns Error::success() on success, and an Error otherwise. -static Error tryYAML2Bitstream() { - StringTable StrTab; - std::vector> ParsedRemarks; - ExitOnErr(tryParseRemarksFromYAMLFile(ParsedRemarks, StrTab)); - return tryReserializeYAML2Bitstream(ParsedRemarks, StrTab); -} -} // namespace yaml2bitstream - -namespace bitstream2yaml { -/// Parse bitstream remarks and reserialize as YAML remarks. -/// \returns An Error if reserialization fails, or Error::success() on success. -static Error tryBitstream2YAML() { - // Create the serializer. - auto MaybeOF = getOutputFileForRemarks(OutputFileName, OutputFormat); - if (!MaybeOF) - return MaybeOF.takeError(); - auto OF = std::move(*MaybeOF); - auto MaybeSerializer = createRemarkSerializer( - OutputFormat, SerializerMode::Standalone, OF->os()); - if (!MaybeSerializer) - return MaybeSerializer.takeError(); - - // Create the parser. - auto MaybeBuf = getInputMemoryBuffer(InputFileName); - if (!MaybeBuf) - return MaybeBuf.takeError(); - auto Serializer = std::move(*MaybeSerializer); - auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer()); - if (!MaybeParser) - return MaybeParser.takeError(); - auto &Parser = **MaybeParser; - - // Parse + reserialize all remarks. - auto MaybeRemark = Parser.next(); - for (; MaybeRemark; MaybeRemark = Parser.next()) - Serializer->emit(**MaybeRemark); - auto E = MaybeRemark.takeError(); - if (!E.isA()) - return E; - consumeError(std::move(E)); - return Error::success(); -} -} // 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. - if (UseDebugLoc) - OF->os() << "Source,"; - 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; - if (shouldSkipRemark(UseDebugLoc, Remark)) - 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?"); - if (UseDebugLoc) { - std::string Loc = Remark.Loc->SourceFilePath.str() + ":" + - std::to_string(Remark.Loc->SourceLine) + +":" + - std::to_string(Remark.Loc->SourceColumn); - OF->os() << Loc << ","; - } - 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 - -namespace annotationcount { -static Error tryAnnotationCount() { - // 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. - if (UseDebugLoc) - OF->os() << "Source,"; - OF->os() << "Function,Count\n"; - // Parse all remarks. When we see the specified remark collect the count - // information. - auto &Parser = **MaybeParser; - auto MaybeRemark = Parser.next(); - for (; MaybeRemark; MaybeRemark = Parser.next()) { - auto &Remark = **MaybeRemark; - if (Remark.RemarkName != "AnnotationSummary") - continue; - if (shouldSkipRemark(UseDebugLoc, Remark)) - continue; - auto *RemarkNameArg = find_if(Remark.Args, [](const Argument &Arg) { - return Arg.Key == "type" && Arg.Val == AnnotationTypeToCollect; - }); - if (RemarkNameArg == Remark.Args.end()) - continue; - auto *CountArg = find_if( - Remark.Args, [](const Argument &Arg) { return Arg.Key == "count"; }); - assert(CountArg != Remark.Args.end() && - "Expected annotation-type remark to have a count key?"); - if (UseDebugLoc) { - std::string Loc = Remark.Loc->SourceFilePath.str() + ":" + - std::to_string(Remark.Loc->SourceLine) + +":" + - std::to_string(Remark.Loc->SourceColumn); - OF->os() << Loc << ","; +using namespace llvm::remarkutil; +ExitOnError ExitOnErr; + +static Error handleSubOptions() { + for (auto *SC : cl::getRegisteredSubcommands()) { + if (*SC) { + // If no subcommand was provided, we need to explicitly check if this is + // the top-level subcommand. + if (SC == &cl::SubCommand::getTopLevel()) + break; + if (auto C = dispatch(SC)) { + return C(); + } } - OF->os() << Remark.FunctionName << "," << CountArg->Val << "\n"; } - auto E = MaybeRemark.takeError(); - if (!E.isA()) - return E; - consumeError(std::move(E)); - OF->keep(); - return Error::success(); -} - -} // namespace annotationcount -/// 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(). -static Error handleSuboptions() { - if (subopts::Bitstream2YAML) - return bitstream2yaml::tryBitstream2YAML(); - if (subopts::YAML2Bitstream) - return yaml2bitstream::tryYAML2Bitstream(); - if (subopts::InstructionCount) - return instructioncount::tryInstructionCount(); - if (subopts::AnnotationCount) - return annotationcount::tryAnnotationCount(); return make_error( "Please specify a subcommand. (See -help for options)", inconvertibleErrorCode()); } -int main(int argc, const char **argv) { +int main(int argc, char *argv[]) { InitLLVM X(argc, argv); - cl::HideUnrelatedOptions(RemarkUtilCategory); cl::ParseCommandLineOptions(argc, argv, "Remark file utilities\n"); ExitOnErr.setBanner(std::string(argv[0]) + ": error: "); - ExitOnErr(handleSuboptions()); -} + ExitOnErr(handleSubOptions()); +} \ No newline at end of file diff --git a/llvm/tools/llvm-remarkutil/RemarkUtilHelpers.h b/llvm/tools/llvm-remarkutil/RemarkUtilHelpers.h new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-remarkutil/RemarkUtilHelpers.h @@ -0,0 +1,59 @@ +//===- RemarkUtilHelpers.h ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Helpers for remark utilites +// +//===----------------------------------------------------------------------===// +#include "llvm-c/Remarks.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Remarks/Remark.h" +#include "llvm/Remarks/RemarkFormat.h" +#include "llvm/Remarks/RemarkParser.h" +#include "llvm/Remarks/YAMLRemarkSerializer.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/ToolOutputFile.h" + +// 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::init("-"), \ + cl::desc(""), \ + cl::sub(SUBOPT)); \ + static cl::opt OutputFileName( \ + "o", cl::init("-"), cl::desc("Output"), cl::value_desc("filename"), \ + cl::sub(SUBOPT)); + +// Keep Input format and names consistent accross the modes via a macro. +#define INPUT_FORMAT_COMMAND_LINE_OPTIONS(SUBOPT) \ + 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(SUBOPT)); + +#define DEBUG_LOC_INFO_COMMAND_LINE_OPTIONS(SUBOPT) \ + static cl::opt UseDebugLoc( \ + "use-debug-loc", \ + cl::desc( \ + "Add debug loc information when generating tables for " \ + "functions. The loc is represented as (path:line number:column " \ + "number)"), \ + cl::init(false), cl::sub(SUBOPT)); + +namespace llvm { +namespace remarks { +Expected> +getInputMemoryBuffer(StringRef InputFileName); +Expected> +getOutputFileWithFlags(StringRef OutputFileName, sys::fs::OpenFlags Flags); +Expected> +getOutputFileForRemarks(StringRef OutputFileName, Format OutputFormat); +} // namespace remarks +} // namespace llvm \ No newline at end of file diff --git a/llvm/tools/llvm-remarkutil/RemarkUtilHelpers.cpp b/llvm/tools/llvm-remarkutil/RemarkUtilHelpers.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-remarkutil/RemarkUtilHelpers.cpp @@ -0,0 +1,57 @@ +//===- RemarkUtilHelpers.cpp ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Helpers for remark utilites +// +//===----------------------------------------------------------------------===// +#include "RemarkUtilHelpers.h" + +namespace llvm { +namespace remarks { +/// \returns A MemoryBuffer for the input file on success, and an Error +/// otherwise. +Expected> +getInputMemoryBuffer(StringRef InputFileName) { + auto MaybeBuf = MemoryBuffer::getFileOrSTDIN(InputFileName); + if (auto ErrorCode = MaybeBuf.getError()) + return createStringError(ErrorCode, + Twine("Cannot open file '" + InputFileName + + "': " + ErrorCode.message())); + return std::move(*MaybeBuf); +} + +/// \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. +Expected> +getOutputFileWithFlags(StringRef OutputFileName, sys::fs::OpenFlags Flags) { + if (OutputFileName == "") + OutputFileName = "-"; + std::error_code ErrorCode; + auto OF = std::make_unique(OutputFileName, ErrorCode, Flags); + if (ErrorCode) + return errorCodeToError(ErrorCode); + 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 +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 remarks +} // namespace llvm \ No newline at end of file diff --git a/llvm/tools/llvm-remarkutil/RemarkUtilRegistry.h b/llvm/tools/llvm-remarkutil/RemarkUtilRegistry.h new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-remarkutil/RemarkUtilRegistry.h @@ -0,0 +1,40 @@ +//===- RemarkUtilRegistry.h: Implement a command registry. ----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implement a simple subcommand registry. +// +//===----------------------------------------------------------------------===// +#ifndef TOOLS_LLVM_REMARKUTIL_REGISTRY_H +#define TOOLS_LLVM_REMARKUTIL_REGISTRY_H + +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace remarkutil { + +// Use |CommandRegistration| as a global initialiser that registers a function +// and associates it with |SC|. This requires that a command has not been +// registered to a given |SC|. +// +// Usage: +// +// // At namespace scope. +// static CommandRegistration Unused(&MySubCommand, [] { ... }); +// +struct CommandRegistration { + CommandRegistration(cl::SubCommand *SC, std::function Command); +}; + +// Requires that |SC| is not null and has an associated function to it. +std::function dispatch(cl::SubCommand *SC); + +} // namespace remarkutil +} // namespace llvm + +#endif // TOOLS_LLVM_REMARKUTIL_REGISTRY_H diff --git a/llvm/tools/llvm-remarkutil/RemarkUtilRegistry.cpp b/llvm/tools/llvm-remarkutil/RemarkUtilRegistry.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-remarkutil/RemarkUtilRegistry.cpp @@ -0,0 +1,41 @@ +//===- RemarkUtilRegistry.cpp: Implement a command registry. --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implement a simple subcommand registry. +// +//===----------------------------------------------------------------------===// +#include "RemarkUtilRegistry.h" +#include + +namespace llvm { +namespace remarkutil { + +using HandlerType = std::function; + +static std::unordered_map &getCommands() { + static std::unordered_map Commands; + return Commands; +} + +CommandRegistration::CommandRegistration(cl::SubCommand *SC, + HandlerType Command) { + assert(getCommands().count(SC) == 0 && + "Attempting to overwrite a command handler"); + assert(Command && "Attempting to register an empty std::function"); + getCommands()[SC] = Command; +} + +HandlerType dispatch(cl::SubCommand *SC) { + auto It = getCommands().find(SC); + assert(It != getCommands().end() && + "Attempting to dispatch on un-registered SubCommand."); + return It->second; +} + +} // namespace remarkutil +} // namespace llvm