1 //===------------------ llvm-opt-report/OptReport.cpp ---------------------===//
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
7 //===----------------------------------------------------------------------===//
10 /// This file implements a tool that can parse the YAML optimization
11 /// records and generate an optimization summary annotated source listing
14 //===----------------------------------------------------------------------===//
16 #include "llvm-c/Remarks.h"
17 #include "llvm/Demangle/Demangle.h"
18 #include "llvm/Remarks/RemarkFormat.h"
19 #include "llvm/Remarks/RemarkParser.h"
20 #include "llvm/Support/CommandLine.h"
21 #include "llvm/Support/Error.h"
22 #include "llvm/Support/ErrorOr.h"
23 #include "llvm/Support/FileSystem.h"
24 #include "llvm/Support/Format.h"
25 #include "llvm/Support/InitLLVM.h"
26 #include "llvm/Support/LineIterator.h"
27 #include "llvm/Support/MemoryBuffer.h"
28 #include "llvm/Support/Path.h"
29 #include "llvm/Support/Program.h"
30 #include "llvm/Support/WithColor.h"
31 #include "llvm/Support/raw_ostream.h"
38 // Mark all our options with this category, everything else (except for -version
39 // and -help) will be hidden.
40 static cl::OptionCategory
41 OptReportCategory("llvm-opt-report options");
43 static cl::opt
<std::string
>
44 InputFileName(cl::Positional
, cl::desc("<input>"), cl::init("-"),
45 cl::cat(OptReportCategory
));
47 static cl::opt
<std::string
>
48 OutputFileName("o", cl::desc("Output file"), cl::init("-"),
49 cl::cat(OptReportCategory
));
51 static cl::opt
<std::string
>
52 InputRelDir("r", cl::desc("Root for relative input paths"), cl::init(""),
53 cl::cat(OptReportCategory
));
56 Succinct("s", cl::desc("Don't include vectorization factors, etc."),
57 cl::init(false), cl::cat(OptReportCategory
));
60 NoDemangle("no-demangle", cl::desc("Don't demangle function names"),
61 cl::init(false), cl::cat(OptReportCategory
));
63 static cl::opt
<std::string
> ParserFormat("format",
64 cl::desc("The format of the remarks."),
66 cl::cat(OptReportCategory
));
69 // For each location in the source file, the common per-transformation state
71 struct OptReportLocationItemInfo
{
72 bool Analyzed
= false;
73 bool Transformed
= false;
75 OptReportLocationItemInfo
&operator |= (
76 const OptReportLocationItemInfo
&RHS
) {
77 Analyzed
|= RHS
.Analyzed
;
78 Transformed
|= RHS
.Transformed
;
83 bool operator < (const OptReportLocationItemInfo
&RHS
) const {
84 if (Analyzed
< RHS
.Analyzed
)
86 else if (Analyzed
> RHS
.Analyzed
)
88 else if (Transformed
< RHS
.Transformed
)
94 // The per-location information collected for producing an optimization report.
95 struct OptReportLocationInfo
{
96 OptReportLocationItemInfo Inlined
;
97 OptReportLocationItemInfo Unrolled
;
98 OptReportLocationItemInfo Vectorized
;
100 int VectorizationFactor
= 1;
101 int InterleaveCount
= 1;
104 OptReportLocationInfo
&operator |= (const OptReportLocationInfo
&RHS
) {
105 Inlined
|= RHS
.Inlined
;
106 Unrolled
|= RHS
.Unrolled
;
107 Vectorized
|= RHS
.Vectorized
;
109 VectorizationFactor
=
110 std::max(VectorizationFactor
, RHS
.VectorizationFactor
);
111 InterleaveCount
= std::max(InterleaveCount
, RHS
.InterleaveCount
);
112 UnrollCount
= std::max(UnrollCount
, RHS
.UnrollCount
);
117 bool operator < (const OptReportLocationInfo
&RHS
) const {
118 if (Inlined
< RHS
.Inlined
)
120 else if (RHS
.Inlined
< Inlined
)
122 else if (Unrolled
< RHS
.Unrolled
)
124 else if (RHS
.Unrolled
< Unrolled
)
126 else if (Vectorized
< RHS
.Vectorized
)
128 else if (RHS
.Vectorized
< Vectorized
|| Succinct
)
130 else if (VectorizationFactor
< RHS
.VectorizationFactor
)
132 else if (VectorizationFactor
> RHS
.VectorizationFactor
)
134 else if (InterleaveCount
< RHS
.InterleaveCount
)
136 else if (InterleaveCount
> RHS
.InterleaveCount
)
138 else if (UnrollCount
< RHS
.UnrollCount
)
144 typedef std::map
<std::string
, std::map
<int, std::map
<std::string
, std::map
<int,
145 OptReportLocationInfo
>>>> LocationInfoTy
;
146 } // anonymous namespace
148 static bool readLocationInfo(LocationInfoTy
&LocationInfo
) {
149 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> Buf
=
150 MemoryBuffer::getFile(InputFileName
.c_str());
151 if (std::error_code EC
= Buf
.getError()) {
152 WithColor::error() << "Can't open file " << InputFileName
<< ": "
153 << EC
.message() << "\n";
157 Expected
<remarks::Format
> Format
= remarks::parseFormat(ParserFormat
);
159 handleAllErrors(Format
.takeError(), [&](const ErrorInfoBase
&PE
) {
160 PE
.log(WithColor::error());
166 Expected
<std::unique_ptr
<remarks::RemarkParser
>> MaybeParser
=
167 remarks::createRemarkParserFromMeta(*Format
, (*Buf
)->getBuffer());
169 handleAllErrors(MaybeParser
.takeError(), [&](const ErrorInfoBase
&PE
) {
170 PE
.log(WithColor::error());
175 remarks::RemarkParser
&Parser
= **MaybeParser
;
178 Expected
<std::unique_ptr
<remarks::Remark
>> MaybeRemark
= Parser
.next();
180 Error E
= MaybeRemark
.takeError();
181 if (E
.isA
<remarks::EndOfFileError
>()) {
183 consumeError(std::move(E
));
186 handleAllErrors(std::move(E
), [&](const ErrorInfoBase
&PE
) {
187 PE
.log(WithColor::error());
193 const remarks::Remark
&Remark
= **MaybeRemark
;
195 bool Transformed
= Remark
.RemarkType
== remarks::Type::Passed
;
197 int VectorizationFactor
= 1;
198 int InterleaveCount
= 1;
201 for (const remarks::Argument
&Arg
: Remark
.Args
) {
202 if (Arg
.Key
== "VectorizationFactor")
203 Arg
.Val
.getAsInteger(10, VectorizationFactor
);
204 else if (Arg
.Key
== "InterleaveCount")
205 Arg
.Val
.getAsInteger(10, InterleaveCount
);
206 else if (Arg
.Key
== "UnrollCount")
207 Arg
.Val
.getAsInteger(10, UnrollCount
);
210 const Optional
<remarks::RemarkLocation
> &Loc
= Remark
.Loc
;
214 StringRef File
= Loc
->SourceFilePath
;
215 unsigned Line
= Loc
->SourceLine
;
216 unsigned Column
= Loc
->SourceColumn
;
218 // We track information on both actual and potential transformations. This
219 // way, if there are multiple possible things on a line that are, or could
220 // have been transformed, we can indicate that explicitly in the output.
221 auto UpdateLLII
= [Transformed
](OptReportLocationItemInfo
&LLII
) {
222 LLII
.Analyzed
= true;
224 LLII
.Transformed
= true;
227 if (Remark
.PassName
== "inline") {
228 auto &LI
= LocationInfo
[File
][Line
][Remark
.FunctionName
][Column
];
229 UpdateLLII(LI
.Inlined
);
230 } else if (Remark
.PassName
== "loop-unroll") {
231 auto &LI
= LocationInfo
[File
][Line
][Remark
.FunctionName
][Column
];
232 LI
.UnrollCount
= UnrollCount
;
233 UpdateLLII(LI
.Unrolled
);
234 } else if (Remark
.PassName
== "loop-vectorize") {
235 auto &LI
= LocationInfo
[File
][Line
][Remark
.FunctionName
][Column
];
236 LI
.VectorizationFactor
= VectorizationFactor
;
237 LI
.InterleaveCount
= InterleaveCount
;
238 UpdateLLII(LI
.Vectorized
);
245 static bool writeReport(LocationInfoTy
&LocationInfo
) {
247 llvm::raw_fd_ostream
OS(OutputFileName
, EC
, llvm::sys::fs::OF_Text
);
249 WithColor::error() << "Can't open file " << OutputFileName
<< ": "
250 << EC
.message() << "\n";
254 bool FirstFile
= true;
255 for (auto &FI
: LocationInfo
) {
256 SmallString
<128> FileName(FI
.first
);
257 if (!InputRelDir
.empty())
258 sys::fs::make_absolute(InputRelDir
, FileName
);
260 const auto &FileInfo
= FI
.second
;
262 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> Buf
=
263 MemoryBuffer::getFile(FileName
);
264 if (std::error_code EC
= Buf
.getError()) {
265 WithColor::error() << "Can't open file " << FileName
<< ": "
266 << EC
.message() << "\n";
275 OS
<< "< " << FileName
<< "\n";
277 // Figure out how many characters we need for the vectorization factors
279 OptReportLocationInfo MaxLI
;
280 for (auto &FLI
: FileInfo
)
281 for (auto &FI
: FLI
.second
)
282 for (auto &LI
: FI
.second
)
285 bool NothingInlined
= !MaxLI
.Inlined
.Transformed
;
286 bool NothingUnrolled
= !MaxLI
.Unrolled
.Transformed
;
287 bool NothingVectorized
= !MaxLI
.Vectorized
.Transformed
;
289 unsigned VFDigits
= llvm::utostr(MaxLI
.VectorizationFactor
).size();
290 unsigned ICDigits
= llvm::utostr(MaxLI
.InterleaveCount
).size();
291 unsigned UCDigits
= llvm::utostr(MaxLI
.UnrollCount
).size();
293 // Figure out how many characters we need for the line numbers.
294 int64_t NumLines
= 0;
295 for (line_iterator
LI(*Buf
.get(), false); LI
!= line_iterator(); ++LI
)
298 unsigned LNDigits
= llvm::utostr(NumLines
).size();
300 for (line_iterator
LI(*Buf
.get(), false); LI
!= line_iterator(); ++LI
) {
301 int64_t L
= LI
.line_number();
302 auto LII
= FileInfo
.find(L
);
304 auto PrintLine
= [&](bool PrintFuncName
,
305 const std::set
<std::string
> &FuncNameSet
) {
306 OptReportLocationInfo LLI
;
308 std::map
<int, OptReportLocationInfo
> ColsInfo
;
309 unsigned InlinedCols
= 0, UnrolledCols
= 0, VectorizedCols
= 0;
311 if (LII
!= FileInfo
.end() && !FuncNameSet
.empty()) {
312 const auto &LineInfo
= LII
->second
;
314 for (auto &CI
: LineInfo
.find(*FuncNameSet
.begin())->second
) {
316 ColsInfo
[Col
] = CI
.second
;
317 InlinedCols
+= CI
.second
.Inlined
.Analyzed
;
318 UnrolledCols
+= CI
.second
.Unrolled
.Analyzed
;
319 VectorizedCols
+= CI
.second
.Vectorized
.Analyzed
;
327 bool FirstFunc
= true;
328 for (const auto &FuncName
: FuncNameSet
) {
334 bool Printed
= false;
338 itaniumDemangle(FuncName
.c_str(), nullptr, nullptr, &Status
);
339 if (Demangled
&& Status
== 0) {
345 std::free(Demangled
);
355 // We try to keep the output as concise as possible. If only one thing on
356 // a given line could have been inlined, vectorized, etc. then we can put
357 // the marker on the source line itself. If there are multiple options
358 // then we want to distinguish them by placing the marker for each
359 // transformation on a separate line following the source line. When we
360 // do this, we use a '^' character to point to the appropriate column in
363 std::string
USpaces(Succinct
? 0 : UCDigits
, ' ');
364 std::string
VSpaces(Succinct
? 0 : VFDigits
+ ICDigits
+ 1, ' ');
366 auto UStr
= [UCDigits
](OptReportLocationInfo
&LLI
) {
368 raw_string_ostream
RS(R
);
371 RS
<< LLI
.UnrollCount
;
372 RS
<< std::string(UCDigits
- RS
.str().size(), ' ');
378 auto VStr
= [VFDigits
,
379 ICDigits
](OptReportLocationInfo
&LLI
) -> std::string
{
381 raw_string_ostream
RS(R
);
384 RS
<< LLI
.VectorizationFactor
<< "," << LLI
.InterleaveCount
;
385 RS
<< std::string(VFDigits
+ ICDigits
+ 1 - RS
.str().size(), ' ');
391 OS
<< llvm::format_decimal(L
, LNDigits
) << " ";
392 OS
<< (LLI
.Inlined
.Transformed
&& InlinedCols
< 2 ? "I" :
393 (NothingInlined
? "" : " "));
394 OS
<< (LLI
.Unrolled
.Transformed
&& UnrolledCols
< 2 ?
395 "U" + UStr(LLI
) : (NothingUnrolled
? "" : " " + USpaces
));
396 OS
<< (LLI
.Vectorized
.Transformed
&& VectorizedCols
< 2 ?
397 "V" + VStr(LLI
) : (NothingVectorized
? "" : " " + VSpaces
));
399 OS
<< " | " << *LI
<< "\n";
401 for (auto &J
: ColsInfo
) {
402 if ((J
.second
.Inlined
.Transformed
&& InlinedCols
> 1) ||
403 (J
.second
.Unrolled
.Transformed
&& UnrolledCols
> 1) ||
404 (J
.second
.Vectorized
.Transformed
&& VectorizedCols
> 1)) {
405 OS
<< std::string(LNDigits
+ 1, ' ');
406 OS
<< (J
.second
.Inlined
.Transformed
&&
407 InlinedCols
> 1 ? "I" : (NothingInlined
? "" : " "));
408 OS
<< (J
.second
.Unrolled
.Transformed
&&
409 UnrolledCols
> 1 ? "U" + UStr(J
.second
) :
410 (NothingUnrolled
? "" : " " + USpaces
));
411 OS
<< (J
.second
.Vectorized
.Transformed
&&
412 VectorizedCols
> 1 ? "V" + VStr(J
.second
) :
413 (NothingVectorized
? "" : " " + VSpaces
));
415 OS
<< " | " << std::string(J
.first
- 1, ' ') << "^\n";
420 // We need to figure out if the optimizations for this line were the same
421 // in each function context. If not, then we want to group the similar
422 // function contexts together and display each group separately. If
423 // they're all the same, then we only display the line once without any
424 // additional markings.
425 std::map
<std::map
<int, OptReportLocationInfo
>,
426 std::set
<std::string
>> UniqueLIs
;
428 OptReportLocationInfo AllLI
;
429 if (LII
!= FileInfo
.end()) {
430 const auto &FuncLineInfo
= LII
->second
;
431 for (const auto &FLII
: FuncLineInfo
) {
432 UniqueLIs
[FLII
.second
].insert(FLII
.first
);
434 for (const auto &OI
: FLII
.second
)
439 bool NothingHappened
= !AllLI
.Inlined
.Transformed
&&
440 !AllLI
.Unrolled
.Transformed
&&
441 !AllLI
.Vectorized
.Transformed
;
442 if (UniqueLIs
.size() > 1 && !NothingHappened
) {
444 for (const auto &FSLI
: UniqueLIs
)
445 PrintLine(true, FSLI
.second
);
447 } else if (UniqueLIs
.size() == 1) {
448 PrintLine(false, UniqueLIs
.begin()->second
);
450 PrintLine(false, std::set
<std::string
>());
458 int main(int argc
, const char **argv
) {
459 InitLLVM
X(argc
, argv
);
461 cl::HideUnrelatedOptions(OptReportCategory
);
462 cl::ParseCommandLineOptions(
464 "A tool to generate an optimization report from YAML optimization"
467 LocationInfoTy LocationInfo
;
468 if (!readLocationInfo(LocationInfo
))
470 if (!writeReport(LocationInfo
))