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());
161 WithColor::error() << '\n';
166 Expected
<std::unique_ptr
<remarks::RemarkParser
>> MaybeParser
=
167 remarks::createRemarkParser(*Format
, (*Buf
)->getBuffer());
169 handleAllErrors(MaybeParser
.takeError(), [&](const ErrorInfoBase
&PE
) {
170 PE
.log(WithColor::error());
174 remarks::RemarkParser
&Parser
= **MaybeParser
;
177 Expected
<std::unique_ptr
<remarks::Remark
>> MaybeRemark
= Parser
.next();
179 Error E
= MaybeRemark
.takeError();
180 if (E
.isA
<remarks::EndOfFileError
>()) {
182 consumeError(std::move(E
));
185 handleAllErrors(MaybeRemark
.takeError(), [&](const ErrorInfoBase
&PE
) {
186 PE
.log(WithColor::error());
191 const remarks::Remark
&Remark
= **MaybeRemark
;
193 bool Transformed
= Remark
.RemarkType
== remarks::Type::Passed
;
195 int VectorizationFactor
= 1;
196 int InterleaveCount
= 1;
199 for (const remarks::Argument
&Arg
: Remark
.Args
) {
200 if (Arg
.Key
== "VectorizationFactor")
201 Arg
.Val
.getAsInteger(10, VectorizationFactor
);
202 else if (Arg
.Key
== "InterleaveCount")
203 Arg
.Val
.getAsInteger(10, InterleaveCount
);
204 else if (Arg
.Key
== "UnrollCount")
205 Arg
.Val
.getAsInteger(10, UnrollCount
);
208 const Optional
<remarks::RemarkLocation
> &Loc
= Remark
.Loc
;
212 StringRef File
= Loc
->SourceFilePath
;
213 unsigned Line
= Loc
->SourceLine
;
214 unsigned Column
= Loc
->SourceColumn
;
216 // We track information on both actual and potential transformations. This
217 // way, if there are multiple possible things on a line that are, or could
218 // have been transformed, we can indicate that explicitly in the output.
219 auto UpdateLLII
= [Transformed
](OptReportLocationItemInfo
&LLII
) {
220 LLII
.Analyzed
= true;
222 LLII
.Transformed
= true;
225 if (Remark
.PassName
== "inline") {
226 auto &LI
= LocationInfo
[File
][Line
][Remark
.FunctionName
][Column
];
227 UpdateLLII(LI
.Inlined
);
228 } else if (Remark
.PassName
== "loop-unroll") {
229 auto &LI
= LocationInfo
[File
][Line
][Remark
.FunctionName
][Column
];
230 LI
.UnrollCount
= UnrollCount
;
231 UpdateLLII(LI
.Unrolled
);
232 } else if (Remark
.PassName
== "loop-vectorize") {
233 auto &LI
= LocationInfo
[File
][Line
][Remark
.FunctionName
][Column
];
234 LI
.VectorizationFactor
= VectorizationFactor
;
235 LI
.InterleaveCount
= InterleaveCount
;
236 UpdateLLII(LI
.Vectorized
);
243 static bool writeReport(LocationInfoTy
&LocationInfo
) {
245 llvm::raw_fd_ostream
OS(OutputFileName
, EC
, llvm::sys::fs::OF_Text
);
247 WithColor::error() << "Can't open file " << OutputFileName
<< ": "
248 << EC
.message() << "\n";
252 bool FirstFile
= true;
253 for (auto &FI
: LocationInfo
) {
254 SmallString
<128> FileName(FI
.first
);
255 if (!InputRelDir
.empty())
256 sys::fs::make_absolute(InputRelDir
, FileName
);
258 const auto &FileInfo
= FI
.second
;
260 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> Buf
=
261 MemoryBuffer::getFile(FileName
);
262 if (std::error_code EC
= Buf
.getError()) {
263 WithColor::error() << "Can't open file " << FileName
<< ": "
264 << EC
.message() << "\n";
273 OS
<< "< " << FileName
<< "\n";
275 // Figure out how many characters we need for the vectorization factors
277 OptReportLocationInfo MaxLI
;
278 for (auto &FLI
: FileInfo
)
279 for (auto &FI
: FLI
.second
)
280 for (auto &LI
: FI
.second
)
283 bool NothingInlined
= !MaxLI
.Inlined
.Transformed
;
284 bool NothingUnrolled
= !MaxLI
.Unrolled
.Transformed
;
285 bool NothingVectorized
= !MaxLI
.Vectorized
.Transformed
;
287 unsigned VFDigits
= llvm::utostr(MaxLI
.VectorizationFactor
).size();
288 unsigned ICDigits
= llvm::utostr(MaxLI
.InterleaveCount
).size();
289 unsigned UCDigits
= llvm::utostr(MaxLI
.UnrollCount
).size();
291 // Figure out how many characters we need for the line numbers.
292 int64_t NumLines
= 0;
293 for (line_iterator
LI(*Buf
.get(), false); LI
!= line_iterator(); ++LI
)
296 unsigned LNDigits
= llvm::utostr(NumLines
).size();
298 for (line_iterator
LI(*Buf
.get(), false); LI
!= line_iterator(); ++LI
) {
299 int64_t L
= LI
.line_number();
300 auto LII
= FileInfo
.find(L
);
302 auto PrintLine
= [&](bool PrintFuncName
,
303 const std::set
<std::string
> &FuncNameSet
) {
304 OptReportLocationInfo LLI
;
306 std::map
<int, OptReportLocationInfo
> ColsInfo
;
307 unsigned InlinedCols
= 0, UnrolledCols
= 0, VectorizedCols
= 0;
309 if (LII
!= FileInfo
.end() && !FuncNameSet
.empty()) {
310 const auto &LineInfo
= LII
->second
;
312 for (auto &CI
: LineInfo
.find(*FuncNameSet
.begin())->second
) {
314 ColsInfo
[Col
] = CI
.second
;
315 InlinedCols
+= CI
.second
.Inlined
.Analyzed
;
316 UnrolledCols
+= CI
.second
.Unrolled
.Analyzed
;
317 VectorizedCols
+= CI
.second
.Vectorized
.Analyzed
;
325 bool FirstFunc
= true;
326 for (const auto &FuncName
: FuncNameSet
) {
332 bool Printed
= false;
336 itaniumDemangle(FuncName
.c_str(), nullptr, nullptr, &Status
);
337 if (Demangled
&& Status
== 0) {
343 std::free(Demangled
);
353 // We try to keep the output as concise as possible. If only one thing on
354 // a given line could have been inlined, vectorized, etc. then we can put
355 // the marker on the source line itself. If there are multiple options
356 // then we want to distinguish them by placing the marker for each
357 // transformation on a separate line following the source line. When we
358 // do this, we use a '^' character to point to the appropriate column in
361 std::string
USpaces(Succinct
? 0 : UCDigits
, ' ');
362 std::string
VSpaces(Succinct
? 0 : VFDigits
+ ICDigits
+ 1, ' ');
364 auto UStr
= [UCDigits
](OptReportLocationInfo
&LLI
) {
366 raw_string_ostream
RS(R
);
369 RS
<< LLI
.UnrollCount
;
370 RS
<< std::string(UCDigits
- RS
.str().size(), ' ');
376 auto VStr
= [VFDigits
,
377 ICDigits
](OptReportLocationInfo
&LLI
) -> std::string
{
379 raw_string_ostream
RS(R
);
382 RS
<< LLI
.VectorizationFactor
<< "," << LLI
.InterleaveCount
;
383 RS
<< std::string(VFDigits
+ ICDigits
+ 1 - RS
.str().size(), ' ');
389 OS
<< llvm::format_decimal(L
, LNDigits
) << " ";
390 OS
<< (LLI
.Inlined
.Transformed
&& InlinedCols
< 2 ? "I" :
391 (NothingInlined
? "" : " "));
392 OS
<< (LLI
.Unrolled
.Transformed
&& UnrolledCols
< 2 ?
393 "U" + UStr(LLI
) : (NothingUnrolled
? "" : " " + USpaces
));
394 OS
<< (LLI
.Vectorized
.Transformed
&& VectorizedCols
< 2 ?
395 "V" + VStr(LLI
) : (NothingVectorized
? "" : " " + VSpaces
));
397 OS
<< " | " << *LI
<< "\n";
399 for (auto &J
: ColsInfo
) {
400 if ((J
.second
.Inlined
.Transformed
&& InlinedCols
> 1) ||
401 (J
.second
.Unrolled
.Transformed
&& UnrolledCols
> 1) ||
402 (J
.second
.Vectorized
.Transformed
&& VectorizedCols
> 1)) {
403 OS
<< std::string(LNDigits
+ 1, ' ');
404 OS
<< (J
.second
.Inlined
.Transformed
&&
405 InlinedCols
> 1 ? "I" : (NothingInlined
? "" : " "));
406 OS
<< (J
.second
.Unrolled
.Transformed
&&
407 UnrolledCols
> 1 ? "U" + UStr(J
.second
) :
408 (NothingUnrolled
? "" : " " + USpaces
));
409 OS
<< (J
.second
.Vectorized
.Transformed
&&
410 VectorizedCols
> 1 ? "V" + VStr(J
.second
) :
411 (NothingVectorized
? "" : " " + VSpaces
));
413 OS
<< " | " << std::string(J
.first
- 1, ' ') << "^\n";
418 // We need to figure out if the optimizations for this line were the same
419 // in each function context. If not, then we want to group the similar
420 // function contexts together and display each group separately. If
421 // they're all the same, then we only display the line once without any
422 // additional markings.
423 std::map
<std::map
<int, OptReportLocationInfo
>,
424 std::set
<std::string
>> UniqueLIs
;
426 OptReportLocationInfo AllLI
;
427 if (LII
!= FileInfo
.end()) {
428 const auto &FuncLineInfo
= LII
->second
;
429 for (const auto &FLII
: FuncLineInfo
) {
430 UniqueLIs
[FLII
.second
].insert(FLII
.first
);
432 for (const auto &OI
: FLII
.second
)
437 bool NothingHappened
= !AllLI
.Inlined
.Transformed
&&
438 !AllLI
.Unrolled
.Transformed
&&
439 !AllLI
.Vectorized
.Transformed
;
440 if (UniqueLIs
.size() > 1 && !NothingHappened
) {
442 for (const auto &FSLI
: UniqueLIs
)
443 PrintLine(true, FSLI
.second
);
445 } else if (UniqueLIs
.size() == 1) {
446 PrintLine(false, UniqueLIs
.begin()->second
);
448 PrintLine(false, std::set
<std::string
>());
456 int main(int argc
, const char **argv
) {
457 InitLLVM
X(argc
, argv
);
459 cl::HideUnrelatedOptions(OptReportCategory
);
460 cl::ParseCommandLineOptions(
462 "A tool to generate an optimization report from YAML optimization"
465 LocationInfoTy LocationInfo
;
466 if (!readLocationInfo(LocationInfo
))
468 if (!writeReport(LocationInfo
))