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