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>>
getInputMemoryBuffer(StringRef InputFileName)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>>
getOutputFileWithFlags(StringRef OutputFileName,sys::fs::OpenFlags Flags)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>>
getOutputFileForRemarks(StringRef OutputFileName,Format OutputFormat)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
tryParseRemarksFromYAMLFile(std::vector<std::unique_ptr<Remark>> & ParsedRemarks,StringTable & StrTab)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.
tryReserializeYAML2Bitstream(const std::vector<std::unique_ptr<Remark>> & ParsedRemarks,StringTable & StrTab)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.
tryYAML2Bitstream()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.
tryBitstream2YAML()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.
tryInstructionCount()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().
handleSuboptions()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
main(int argc,const char ** argv)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