1*bdd1243dSDimitry Andric //===--------- llvm-remarkutil/RemarkUtil.cpp -----------===// 2*bdd1243dSDimitry Andric // 3*bdd1243dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*bdd1243dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*bdd1243dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*bdd1243dSDimitry Andric // 7*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 8*bdd1243dSDimitry Andric /// Utility for remark files. 9*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 10*bdd1243dSDimitry Andric 11*bdd1243dSDimitry Andric #include "llvm-c/Remarks.h" 12*bdd1243dSDimitry Andric #include "llvm/ADT/StringRef.h" 13*bdd1243dSDimitry Andric #include "llvm/Remarks/Remark.h" 14*bdd1243dSDimitry Andric #include "llvm/Remarks/RemarkFormat.h" 15*bdd1243dSDimitry Andric #include "llvm/Remarks/RemarkParser.h" 16*bdd1243dSDimitry Andric #include "llvm/Remarks/YAMLRemarkSerializer.h" 17*bdd1243dSDimitry Andric #include "llvm/Support/CommandLine.h" 18*bdd1243dSDimitry Andric #include "llvm/Support/Compiler.h" 19*bdd1243dSDimitry Andric #include "llvm/Support/Error.h" 20*bdd1243dSDimitry Andric #include "llvm/Support/FileSystem.h" 21*bdd1243dSDimitry Andric #include "llvm/Support/InitLLVM.h" 22*bdd1243dSDimitry Andric #include "llvm/Support/MemoryBuffer.h" 23*bdd1243dSDimitry Andric #include "llvm/Support/ToolOutputFile.h" 24*bdd1243dSDimitry Andric #include "llvm/Support/WithColor.h" 25*bdd1243dSDimitry Andric 26*bdd1243dSDimitry Andric using namespace llvm; 27*bdd1243dSDimitry Andric using namespace remarks; 28*bdd1243dSDimitry Andric 29*bdd1243dSDimitry Andric static ExitOnError ExitOnErr; 30*bdd1243dSDimitry Andric static cl::OptionCategory RemarkUtilCategory("llvm-remarkutil options"); 31*bdd1243dSDimitry Andric namespace subopts { 32*bdd1243dSDimitry Andric static cl::SubCommand 33*bdd1243dSDimitry Andric YAML2Bitstream("yaml2bitstream", 34*bdd1243dSDimitry Andric "Convert YAML remarks to bitstream remarks"); 35*bdd1243dSDimitry Andric static cl::SubCommand 36*bdd1243dSDimitry Andric Bitstream2YAML("bitstream2yaml", 37*bdd1243dSDimitry Andric "Convert bitstream remarks to YAML remarks"); 38*bdd1243dSDimitry Andric static cl::SubCommand InstructionCount( 39*bdd1243dSDimitry Andric "instruction-count", 40*bdd1243dSDimitry Andric "Function instruction count information (requires asm-printer remarks)"); 41*bdd1243dSDimitry Andric } // namespace subopts 42*bdd1243dSDimitry Andric 43*bdd1243dSDimitry Andric // Keep input + output help + names consistent across the various modes via a 44*bdd1243dSDimitry Andric // hideous macro. 45*bdd1243dSDimitry Andric #define INPUT_OUTPUT_COMMAND_LINE_OPTIONS(SUBOPT) \ 46*bdd1243dSDimitry Andric static cl::opt<std::string> InputFileName( \ 47*bdd1243dSDimitry Andric cl::Positional, cl::cat(RemarkUtilCategory), cl::init("-"), \ 48*bdd1243dSDimitry Andric cl::desc("<input file>"), cl::sub(SUBOPT)); \ 49*bdd1243dSDimitry Andric static cl::opt<std::string> OutputFileName( \ 50*bdd1243dSDimitry Andric "o", cl::init("-"), cl::cat(RemarkUtilCategory), cl::desc("Output"), \ 51*bdd1243dSDimitry Andric cl::value_desc("filename"), cl::sub(SUBOPT)); 52*bdd1243dSDimitry Andric namespace yaml2bitstream { 53*bdd1243dSDimitry Andric /// Remark format to parse. 54*bdd1243dSDimitry Andric static constexpr Format InputFormat = Format::YAML; 55*bdd1243dSDimitry Andric /// Remark format to output. 56*bdd1243dSDimitry Andric static constexpr Format OutputFormat = Format::Bitstream; 57*bdd1243dSDimitry Andric INPUT_OUTPUT_COMMAND_LINE_OPTIONS(subopts::YAML2Bitstream) 58*bdd1243dSDimitry Andric } // namespace yaml2bitstream 59*bdd1243dSDimitry Andric 60*bdd1243dSDimitry Andric namespace bitstream2yaml { 61*bdd1243dSDimitry Andric /// Remark format to parse. 62*bdd1243dSDimitry Andric static constexpr Format InputFormat = Format::Bitstream; 63*bdd1243dSDimitry Andric /// Remark format to output. 64*bdd1243dSDimitry Andric static constexpr Format OutputFormat = Format::YAML; 65*bdd1243dSDimitry Andric INPUT_OUTPUT_COMMAND_LINE_OPTIONS(subopts::Bitstream2YAML) 66*bdd1243dSDimitry Andric } // namespace bitstream2yaml 67*bdd1243dSDimitry Andric 68*bdd1243dSDimitry Andric namespace instructioncount { 69*bdd1243dSDimitry Andric static cl::opt<Format> InputFormat( 70*bdd1243dSDimitry Andric "parser", cl::desc("Input remark format to parse"), 71*bdd1243dSDimitry Andric cl::values(clEnumValN(Format::YAML, "yaml", "YAML"), 72*bdd1243dSDimitry Andric clEnumValN(Format::Bitstream, "bitstream", "Bitstream")), 73*bdd1243dSDimitry Andric cl::sub(subopts::InstructionCount)); 74*bdd1243dSDimitry Andric INPUT_OUTPUT_COMMAND_LINE_OPTIONS(subopts::InstructionCount) 75*bdd1243dSDimitry Andric } // namespace instructioncount 76*bdd1243dSDimitry Andric 77*bdd1243dSDimitry Andric /// \returns A MemoryBuffer for the input file on success, and an Error 78*bdd1243dSDimitry Andric /// otherwise. 79*bdd1243dSDimitry Andric static Expected<std::unique_ptr<MemoryBuffer>> 80*bdd1243dSDimitry Andric getInputMemoryBuffer(StringRef InputFileName) { 81*bdd1243dSDimitry Andric auto MaybeBuf = MemoryBuffer::getFileOrSTDIN(InputFileName); 82*bdd1243dSDimitry Andric if (auto ErrorCode = MaybeBuf.getError()) 83*bdd1243dSDimitry Andric return createStringError(ErrorCode, 84*bdd1243dSDimitry Andric Twine("Cannot open file '" + InputFileName + 85*bdd1243dSDimitry Andric "': " + ErrorCode.message())); 86*bdd1243dSDimitry Andric return std::move(*MaybeBuf); 87*bdd1243dSDimitry Andric } 88*bdd1243dSDimitry Andric 89*bdd1243dSDimitry Andric /// \returns A ToolOutputFile which can be used for outputting the results of 90*bdd1243dSDimitry Andric /// some tool mode. 91*bdd1243dSDimitry Andric /// \p OutputFileName is the desired destination. 92*bdd1243dSDimitry Andric /// \p Flags controls whether or not the file is opened for writing in text 93*bdd1243dSDimitry Andric /// mode, as a binary, etc. See sys::fs::OpenFlags for more detail. 94*bdd1243dSDimitry Andric static Expected<std::unique_ptr<ToolOutputFile>> 95*bdd1243dSDimitry Andric getOutputFileWithFlags(StringRef OutputFileName, sys::fs::OpenFlags Flags) { 96*bdd1243dSDimitry Andric if (OutputFileName == "") 97*bdd1243dSDimitry Andric OutputFileName = "-"; 98*bdd1243dSDimitry Andric std::error_code ErrorCode; 99*bdd1243dSDimitry Andric auto OF = std::make_unique<ToolOutputFile>(OutputFileName, ErrorCode, Flags); 100*bdd1243dSDimitry Andric if (ErrorCode) 101*bdd1243dSDimitry Andric return errorCodeToError(ErrorCode); 102*bdd1243dSDimitry Andric return std::move(OF); 103*bdd1243dSDimitry Andric } 104*bdd1243dSDimitry Andric 105*bdd1243dSDimitry Andric /// \returns A ToolOutputFile which can be used for writing remarks on success, 106*bdd1243dSDimitry Andric /// and an Error otherwise. 107*bdd1243dSDimitry Andric /// \p OutputFileName is the desired destination. 108*bdd1243dSDimitry Andric /// \p OutputFormat 109*bdd1243dSDimitry Andric static Expected<std::unique_ptr<ToolOutputFile>> 110*bdd1243dSDimitry Andric getOutputFileForRemarks(StringRef OutputFileName, Format OutputFormat) { 111*bdd1243dSDimitry Andric assert((OutputFormat == Format::YAML || OutputFormat == Format::Bitstream) && 112*bdd1243dSDimitry Andric "Expected one of YAML or Bitstream!"); 113*bdd1243dSDimitry Andric return getOutputFileWithFlags(OutputFileName, OutputFormat == Format::YAML 114*bdd1243dSDimitry Andric ? sys::fs::OF_TextWithCRLF 115*bdd1243dSDimitry Andric : sys::fs::OF_None); 116*bdd1243dSDimitry Andric } 117*bdd1243dSDimitry Andric 118*bdd1243dSDimitry Andric namespace yaml2bitstream { 119*bdd1243dSDimitry Andric /// Parses all remarks in the input YAML file. 120*bdd1243dSDimitry Andric /// \p [out] ParsedRemarks - Filled with remarks parsed from the input file. 121*bdd1243dSDimitry Andric /// \p [out] StrTab - A string table populated for later remark serialization. 122*bdd1243dSDimitry Andric /// \returns Error::success() if all remarks were successfully parsed, and an 123*bdd1243dSDimitry Andric /// Error otherwise. 124*bdd1243dSDimitry Andric static Error 125*bdd1243dSDimitry Andric tryParseRemarksFromYAMLFile(std::vector<std::unique_ptr<Remark>> &ParsedRemarks, 126*bdd1243dSDimitry Andric StringTable &StrTab) { 127*bdd1243dSDimitry Andric auto MaybeBuf = getInputMemoryBuffer(InputFileName); 128*bdd1243dSDimitry Andric if (!MaybeBuf) 129*bdd1243dSDimitry Andric return MaybeBuf.takeError(); 130*bdd1243dSDimitry Andric auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer()); 131*bdd1243dSDimitry Andric if (!MaybeParser) 132*bdd1243dSDimitry Andric return MaybeParser.takeError(); 133*bdd1243dSDimitry Andric auto &Parser = **MaybeParser; 134*bdd1243dSDimitry Andric auto MaybeRemark = Parser.next(); 135*bdd1243dSDimitry Andric for (; MaybeRemark; MaybeRemark = Parser.next()) { 136*bdd1243dSDimitry Andric StrTab.internalize(**MaybeRemark); 137*bdd1243dSDimitry Andric ParsedRemarks.push_back(std::move(*MaybeRemark)); 138*bdd1243dSDimitry Andric } 139*bdd1243dSDimitry Andric auto E = MaybeRemark.takeError(); 140*bdd1243dSDimitry Andric if (!E.isA<EndOfFileError>()) 141*bdd1243dSDimitry Andric return E; 142*bdd1243dSDimitry Andric consumeError(std::move(E)); 143*bdd1243dSDimitry Andric return Error::success(); 144*bdd1243dSDimitry Andric } 145*bdd1243dSDimitry Andric 146*bdd1243dSDimitry Andric /// Reserialize a list of parsed YAML remarks into bitstream remarks. 147*bdd1243dSDimitry Andric /// \p ParsedRemarks - A list of remarks. 148*bdd1243dSDimitry Andric /// \p StrTab - The string table for the remarks. 149*bdd1243dSDimitry Andric /// \returns Error::success() on success. 150*bdd1243dSDimitry Andric static Error tryReserializeYAML2Bitstream( 151*bdd1243dSDimitry Andric const std::vector<std::unique_ptr<Remark>> &ParsedRemarks, 152*bdd1243dSDimitry Andric StringTable &StrTab) { 153*bdd1243dSDimitry Andric auto MaybeOF = getOutputFileForRemarks(OutputFileName, OutputFormat); 154*bdd1243dSDimitry Andric if (!MaybeOF) 155*bdd1243dSDimitry Andric return MaybeOF.takeError(); 156*bdd1243dSDimitry Andric auto OF = std::move(*MaybeOF); 157*bdd1243dSDimitry Andric auto MaybeSerializer = createRemarkSerializer( 158*bdd1243dSDimitry Andric OutputFormat, SerializerMode::Standalone, OF->os(), std::move(StrTab)); 159*bdd1243dSDimitry Andric if (!MaybeSerializer) 160*bdd1243dSDimitry Andric return MaybeSerializer.takeError(); 161*bdd1243dSDimitry Andric auto Serializer = std::move(*MaybeSerializer); 162*bdd1243dSDimitry Andric for (const auto &Remark : ParsedRemarks) 163*bdd1243dSDimitry Andric Serializer->emit(*Remark); 164*bdd1243dSDimitry Andric OF->keep(); 165*bdd1243dSDimitry Andric return Error::success(); 166*bdd1243dSDimitry Andric } 167*bdd1243dSDimitry Andric 168*bdd1243dSDimitry Andric /// Parse YAML remarks and reserialize as bitstream remarks. 169*bdd1243dSDimitry Andric /// \returns Error::success() on success, and an Error otherwise. 170*bdd1243dSDimitry Andric static Error tryYAML2Bitstream() { 171*bdd1243dSDimitry Andric StringTable StrTab; 172*bdd1243dSDimitry Andric std::vector<std::unique_ptr<Remark>> ParsedRemarks; 173*bdd1243dSDimitry Andric ExitOnErr(tryParseRemarksFromYAMLFile(ParsedRemarks, StrTab)); 174*bdd1243dSDimitry Andric return tryReserializeYAML2Bitstream(ParsedRemarks, StrTab); 175*bdd1243dSDimitry Andric } 176*bdd1243dSDimitry Andric } // namespace yaml2bitstream 177*bdd1243dSDimitry Andric 178*bdd1243dSDimitry Andric namespace bitstream2yaml { 179*bdd1243dSDimitry Andric /// Parse bitstream remarks and reserialize as YAML remarks. 180*bdd1243dSDimitry Andric /// \returns An Error if reserialization fails, or Error::success() on success. 181*bdd1243dSDimitry Andric static Error tryBitstream2YAML() { 182*bdd1243dSDimitry Andric // Create the serializer. 183*bdd1243dSDimitry Andric auto MaybeOF = getOutputFileForRemarks(OutputFileName, OutputFormat); 184*bdd1243dSDimitry Andric if (!MaybeOF) 185*bdd1243dSDimitry Andric return MaybeOF.takeError(); 186*bdd1243dSDimitry Andric auto OF = std::move(*MaybeOF); 187*bdd1243dSDimitry Andric auto MaybeSerializer = createRemarkSerializer( 188*bdd1243dSDimitry Andric OutputFormat, SerializerMode::Standalone, OF->os()); 189*bdd1243dSDimitry Andric if (!MaybeSerializer) 190*bdd1243dSDimitry Andric return MaybeSerializer.takeError(); 191*bdd1243dSDimitry Andric 192*bdd1243dSDimitry Andric // Create the parser. 193*bdd1243dSDimitry Andric auto MaybeBuf = getInputMemoryBuffer(InputFileName); 194*bdd1243dSDimitry Andric if (!MaybeBuf) 195*bdd1243dSDimitry Andric return MaybeBuf.takeError(); 196*bdd1243dSDimitry Andric auto Serializer = std::move(*MaybeSerializer); 197*bdd1243dSDimitry Andric auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer()); 198*bdd1243dSDimitry Andric if (!MaybeParser) 199*bdd1243dSDimitry Andric return MaybeParser.takeError(); 200*bdd1243dSDimitry Andric auto &Parser = **MaybeParser; 201*bdd1243dSDimitry Andric 202*bdd1243dSDimitry Andric // Parse + reserialize all remarks. 203*bdd1243dSDimitry Andric auto MaybeRemark = Parser.next(); 204*bdd1243dSDimitry Andric for (; MaybeRemark; MaybeRemark = Parser.next()) 205*bdd1243dSDimitry Andric Serializer->emit(**MaybeRemark); 206*bdd1243dSDimitry Andric auto E = MaybeRemark.takeError(); 207*bdd1243dSDimitry Andric if (!E.isA<EndOfFileError>()) 208*bdd1243dSDimitry Andric return E; 209*bdd1243dSDimitry Andric consumeError(std::move(E)); 210*bdd1243dSDimitry Andric return Error::success(); 211*bdd1243dSDimitry Andric } 212*bdd1243dSDimitry Andric } // namespace bitstream2yaml 213*bdd1243dSDimitry Andric 214*bdd1243dSDimitry Andric namespace instructioncount { 215*bdd1243dSDimitry Andric /// Outputs all instruction count remarks in the file as a CSV. 216*bdd1243dSDimitry Andric /// \returns Error::success() on success, and an Error otherwise. 217*bdd1243dSDimitry Andric static Error tryInstructionCount() { 218*bdd1243dSDimitry Andric // Create the output buffer. 219*bdd1243dSDimitry Andric auto MaybeOF = getOutputFileWithFlags(OutputFileName, 220*bdd1243dSDimitry Andric /*Flags = */ sys::fs::OF_TextWithCRLF); 221*bdd1243dSDimitry Andric if (!MaybeOF) 222*bdd1243dSDimitry Andric return MaybeOF.takeError(); 223*bdd1243dSDimitry Andric auto OF = std::move(*MaybeOF); 224*bdd1243dSDimitry Andric // Create a parser for the user-specified input format. 225*bdd1243dSDimitry Andric auto MaybeBuf = getInputMemoryBuffer(InputFileName); 226*bdd1243dSDimitry Andric if (!MaybeBuf) 227*bdd1243dSDimitry Andric return MaybeBuf.takeError(); 228*bdd1243dSDimitry Andric auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer()); 229*bdd1243dSDimitry Andric if (!MaybeParser) 230*bdd1243dSDimitry Andric return MaybeParser.takeError(); 231*bdd1243dSDimitry Andric // Emit CSV header. 232*bdd1243dSDimitry Andric OF->os() << "Function,InstructionCount\n"; 233*bdd1243dSDimitry Andric // Parse all remarks. Whenever we see an instruction count remark, output 234*bdd1243dSDimitry Andric // the file name and the number of instructions. 235*bdd1243dSDimitry Andric auto &Parser = **MaybeParser; 236*bdd1243dSDimitry Andric auto MaybeRemark = Parser.next(); 237*bdd1243dSDimitry Andric for (; MaybeRemark; MaybeRemark = Parser.next()) { 238*bdd1243dSDimitry Andric auto &Remark = **MaybeRemark; 239*bdd1243dSDimitry Andric if (Remark.RemarkName != "InstructionCount") 240*bdd1243dSDimitry Andric continue; 241*bdd1243dSDimitry Andric auto *InstrCountArg = find_if(Remark.Args, [](const Argument &Arg) { 242*bdd1243dSDimitry Andric return Arg.Key == "NumInstructions"; 243*bdd1243dSDimitry Andric }); 244*bdd1243dSDimitry Andric assert(InstrCountArg != Remark.Args.end() && 245*bdd1243dSDimitry Andric "Expected instruction count remarks to have a NumInstructions key?"); 246*bdd1243dSDimitry Andric OF->os() << Remark.FunctionName << "," << InstrCountArg->Val << "\n"; 247*bdd1243dSDimitry Andric } 248*bdd1243dSDimitry Andric auto E = MaybeRemark.takeError(); 249*bdd1243dSDimitry Andric if (!E.isA<EndOfFileError>()) 250*bdd1243dSDimitry Andric return E; 251*bdd1243dSDimitry Andric consumeError(std::move(E)); 252*bdd1243dSDimitry Andric OF->keep(); 253*bdd1243dSDimitry Andric return Error::success(); 254*bdd1243dSDimitry Andric } 255*bdd1243dSDimitry Andric } // namespace instructioncount 256*bdd1243dSDimitry Andric 257*bdd1243dSDimitry Andric /// Handle user-specified suboptions (e.g. yaml2bitstream, bitstream2yaml). 258*bdd1243dSDimitry Andric /// \returns An Error if the specified suboption fails or if no suboption was 259*bdd1243dSDimitry Andric /// specified. Otherwise, Error::success(). 260*bdd1243dSDimitry Andric static Error handleSuboptions() { 261*bdd1243dSDimitry Andric if (subopts::Bitstream2YAML) 262*bdd1243dSDimitry Andric return bitstream2yaml::tryBitstream2YAML(); 263*bdd1243dSDimitry Andric if (subopts::YAML2Bitstream) 264*bdd1243dSDimitry Andric return yaml2bitstream::tryYAML2Bitstream(); 265*bdd1243dSDimitry Andric if (subopts::InstructionCount) 266*bdd1243dSDimitry Andric return instructioncount::tryInstructionCount(); 267*bdd1243dSDimitry Andric return make_error<StringError>( 268*bdd1243dSDimitry Andric "Please specify a subcommand. (See -help for options)", 269*bdd1243dSDimitry Andric inconvertibleErrorCode()); 270*bdd1243dSDimitry Andric } 271*bdd1243dSDimitry Andric 272*bdd1243dSDimitry Andric int main(int argc, const char **argv) { 273*bdd1243dSDimitry Andric InitLLVM X(argc, argv); 274*bdd1243dSDimitry Andric cl::HideUnrelatedOptions(RemarkUtilCategory); 275*bdd1243dSDimitry Andric cl::ParseCommandLineOptions(argc, argv, "Remark file utilities\n"); 276*bdd1243dSDimitry Andric ExitOnErr.setBanner(std::string(argv[0]) + ": error: "); 277*bdd1243dSDimitry Andric ExitOnErr(handleSuboptions()); 278*bdd1243dSDimitry Andric } 279