xref: /freebsd-src/contrib/llvm-project/llvm/tools/llvm-remarkutil/RemarkUtil.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
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