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/ADT/StringExtras.h"
18 #include "llvm/Demangle/Demangle.h"
19 #include "llvm/Remarks/Remark.h"
20 #include "llvm/Remarks/RemarkFormat.h"
21 #include "llvm/Remarks/RemarkParser.h"
22 #include "llvm/Support/CommandLine.h"
23 #include "llvm/Support/Error.h"
24 #include "llvm/Support/ErrorOr.h"
25 #include "llvm/Support/FileSystem.h"
26 #include "llvm/Support/Format.h"
27 #include "llvm/Support/InitLLVM.h"
28 #include "llvm/Support/LineIterator.h"
29 #include "llvm/Support/MemoryBuffer.h"
30 #include "llvm/Support/Path.h"
31 #include "llvm/Support/Program.h"
32 #include "llvm/Support/WithColor.h"
33 #include "llvm/Support/raw_ostream.h"
41 // Mark all our options with this category, everything else (except for -version
42 // and -help) will be hidden.
43 static cl::OptionCategory
44 OptReportCategory("llvm-opt-report options");
46 static cl::opt
<std::string
>
47 InputFileName(cl::Positional
, cl::desc("<input>"), cl::init("-"),
48 cl::cat(OptReportCategory
));
50 static cl::opt
<std::string
>
51 OutputFileName("o", cl::desc("Output file"), cl::init("-"),
52 cl::cat(OptReportCategory
));
54 static cl::opt
<std::string
>
55 InputRelDir("r", cl::desc("Root for relative input paths"), cl::init(""),
56 cl::cat(OptReportCategory
));
59 Succinct("s", cl::desc("Don't include vectorization factors, etc."),
60 cl::init(false), cl::cat(OptReportCategory
));
63 NoDemangle("no-demangle", cl::desc("Don't demangle function names"),
64 cl::init(false), cl::cat(OptReportCategory
));
66 static cl::opt
<std::string
> ParserFormat("format",
67 cl::desc("The format of the remarks."),
69 cl::cat(OptReportCategory
));
72 // For each location in the source file, the common per-transformation state
74 struct OptReportLocationItemInfo
{
75 bool Analyzed
= false;
76 bool Transformed
= false;
78 OptReportLocationItemInfo
&operator |= (
79 const OptReportLocationItemInfo
&RHS
) {
80 Analyzed
|= RHS
.Analyzed
;
81 Transformed
|= RHS
.Transformed
;
86 bool operator < (const OptReportLocationItemInfo
&RHS
) const {
87 if (Analyzed
< RHS
.Analyzed
)
89 else if (Analyzed
> RHS
.Analyzed
)
91 else if (Transformed
< RHS
.Transformed
)
97 // The per-location information collected for producing an optimization report.
98 struct OptReportLocationInfo
{
99 OptReportLocationItemInfo Inlined
;
100 OptReportLocationItemInfo Unrolled
;
101 OptReportLocationItemInfo Vectorized
;
103 int VectorizationFactor
= 1;
104 int InterleaveCount
= 1;
107 OptReportLocationInfo
&operator |= (const OptReportLocationInfo
&RHS
) {
108 Inlined
|= RHS
.Inlined
;
109 Unrolled
|= RHS
.Unrolled
;
110 Vectorized
|= RHS
.Vectorized
;
112 VectorizationFactor
=
113 std::max(VectorizationFactor
, RHS
.VectorizationFactor
);
114 InterleaveCount
= std::max(InterleaveCount
, RHS
.InterleaveCount
);
115 UnrollCount
= std::max(UnrollCount
, RHS
.UnrollCount
);
120 bool operator < (const OptReportLocationInfo
&RHS
) const {
121 if (Inlined
< RHS
.Inlined
)
123 else if (RHS
.Inlined
< Inlined
)
125 else if (Unrolled
< RHS
.Unrolled
)
127 else if (RHS
.Unrolled
< Unrolled
)
129 else if (Vectorized
< RHS
.Vectorized
)
131 else if (RHS
.Vectorized
< Vectorized
|| Succinct
)
133 else if (VectorizationFactor
< RHS
.VectorizationFactor
)
135 else if (VectorizationFactor
> RHS
.VectorizationFactor
)
137 else if (InterleaveCount
< RHS
.InterleaveCount
)
139 else if (InterleaveCount
> RHS
.InterleaveCount
)
141 else if (UnrollCount
< RHS
.UnrollCount
)
147 typedef std::map
<std::string
, std::map
<int, std::map
<std::string
, std::map
<int,
148 OptReportLocationInfo
>>>> LocationInfoTy
;
149 } // anonymous namespace
151 static bool readLocationInfo(LocationInfoTy
&LocationInfo
) {
152 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> Buf
=
153 MemoryBuffer::getFile(InputFileName
.c_str());
154 if (std::error_code EC
= Buf
.getError()) {
155 WithColor::error() << "Can't open file " << InputFileName
<< ": "
156 << EC
.message() << "\n";
160 Expected
<remarks::Format
> Format
= remarks::parseFormat(ParserFormat
);
162 handleAllErrors(Format
.takeError(), [&](const ErrorInfoBase
&PE
) {
163 PE
.log(WithColor::error());
169 Expected
<std::unique_ptr
<remarks::RemarkParser
>> MaybeParser
=
170 remarks::createRemarkParserFromMeta(*Format
, (*Buf
)->getBuffer());
172 handleAllErrors(MaybeParser
.takeError(), [&](const ErrorInfoBase
&PE
) {
173 PE
.log(WithColor::error());
178 remarks::RemarkParser
&Parser
= **MaybeParser
;
181 Expected
<std::unique_ptr
<remarks::Remark
>> MaybeRemark
= Parser
.next();
183 Error E
= MaybeRemark
.takeError();
184 if (E
.isA
<remarks::EndOfFileError
>()) {
186 consumeError(std::move(E
));
189 handleAllErrors(std::move(E
), [&](const ErrorInfoBase
&PE
) {
190 PE
.log(WithColor::error());
196 const remarks::Remark
&Remark
= **MaybeRemark
;
198 bool Transformed
= Remark
.RemarkType
== remarks::Type::Passed
;
200 int VectorizationFactor
= 1;
201 int InterleaveCount
= 1;
204 for (const remarks::Argument
&Arg
: Remark
.Args
) {
205 if (Arg
.Key
== "VectorizationFactor")
206 Arg
.Val
.getAsInteger(10, VectorizationFactor
);
207 else if (Arg
.Key
== "InterleaveCount")
208 Arg
.Val
.getAsInteger(10, InterleaveCount
);
209 else if (Arg
.Key
== "UnrollCount")
210 Arg
.Val
.getAsInteger(10, UnrollCount
);
213 const std::optional
<remarks::RemarkLocation
> &Loc
= Remark
.Loc
;
217 StringRef File
= Loc
->SourceFilePath
;
218 unsigned Line
= Loc
->SourceLine
;
219 unsigned Column
= Loc
->SourceColumn
;
221 // We track information on both actual and potential transformations. This
222 // way, if there are multiple possible things on a line that are, or could
223 // have been transformed, we can indicate that explicitly in the output.
224 auto UpdateLLII
= [Transformed
](OptReportLocationItemInfo
&LLII
) {
225 LLII
.Analyzed
= true;
227 LLII
.Transformed
= true;
230 if (Remark
.PassName
== "inline") {
231 auto &LI
= LocationInfo
[std::string(File
)][Line
]
232 [std::string(Remark
.FunctionName
)][Column
];
233 UpdateLLII(LI
.Inlined
);
234 } else if (Remark
.PassName
== "loop-unroll") {
235 auto &LI
= LocationInfo
[std::string(File
)][Line
]
236 [std::string(Remark
.FunctionName
)][Column
];
237 LI
.UnrollCount
= UnrollCount
;
238 UpdateLLII(LI
.Unrolled
);
239 } else if (Remark
.PassName
== "loop-vectorize") {
240 auto &LI
= LocationInfo
[std::string(File
)][Line
]
241 [std::string(Remark
.FunctionName
)][Column
];
242 LI
.VectorizationFactor
= VectorizationFactor
;
243 LI
.InterleaveCount
= InterleaveCount
;
244 UpdateLLII(LI
.Vectorized
);
251 static bool writeReport(LocationInfoTy
&LocationInfo
) {
253 llvm::raw_fd_ostream
OS(OutputFileName
, EC
, llvm::sys::fs::OF_TextWithCRLF
);
255 WithColor::error() << "Can't open file " << OutputFileName
<< ": "
256 << EC
.message() << "\n";
260 bool FirstFile
= true;
261 for (auto &FI
: LocationInfo
) {
262 SmallString
<128> FileName(FI
.first
);
263 if (!InputRelDir
.empty())
264 sys::fs::make_absolute(InputRelDir
, FileName
);
266 const auto &FileInfo
= FI
.second
;
268 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> Buf
=
269 MemoryBuffer::getFile(FileName
);
270 if (std::error_code EC
= Buf
.getError()) {
271 WithColor::error() << "Can't open file " << FileName
<< ": "
272 << EC
.message() << "\n";
281 OS
<< "< " << FileName
<< "\n";
283 // Figure out how many characters we need for the vectorization factors
285 OptReportLocationInfo MaxLI
;
286 for (auto &FLI
: FileInfo
)
287 for (auto &FI
: FLI
.second
)
288 for (auto &LI
: FI
.second
)
291 bool NothingInlined
= !MaxLI
.Inlined
.Transformed
;
292 bool NothingUnrolled
= !MaxLI
.Unrolled
.Transformed
;
293 bool NothingVectorized
= !MaxLI
.Vectorized
.Transformed
;
295 unsigned VFDigits
= llvm::utostr(MaxLI
.VectorizationFactor
).size();
296 unsigned ICDigits
= llvm::utostr(MaxLI
.InterleaveCount
).size();
297 unsigned UCDigits
= llvm::utostr(MaxLI
.UnrollCount
).size();
299 // Figure out how many characters we need for the line numbers.
300 int64_t NumLines
= 0;
301 for (line_iterator
LI(*Buf
.get(), false); LI
!= line_iterator(); ++LI
)
304 unsigned LNDigits
= llvm::utostr(NumLines
).size();
306 for (line_iterator
LI(*Buf
.get(), false); LI
!= line_iterator(); ++LI
) {
307 int64_t L
= LI
.line_number();
308 auto LII
= FileInfo
.find(L
);
310 auto PrintLine
= [&](bool PrintFuncName
,
311 const std::set
<std::string
> &FuncNameSet
) {
312 OptReportLocationInfo LLI
;
314 std::map
<int, OptReportLocationInfo
> ColsInfo
;
315 unsigned InlinedCols
= 0, UnrolledCols
= 0, VectorizedCols
= 0;
317 if (LII
!= FileInfo
.end() && !FuncNameSet
.empty()) {
318 const auto &LineInfo
= LII
->second
;
320 for (auto &CI
: LineInfo
.find(*FuncNameSet
.begin())->second
) {
322 ColsInfo
[Col
] = CI
.second
;
323 InlinedCols
+= CI
.second
.Inlined
.Analyzed
;
324 UnrolledCols
+= CI
.second
.Unrolled
.Analyzed
;
325 VectorizedCols
+= CI
.second
.Vectorized
.Analyzed
;
333 bool FirstFunc
= true;
334 for (const auto &FuncName
: FuncNameSet
) {
340 bool Printed
= false;
342 if (char *Demangled
= itaniumDemangle(FuncName
)) {
345 std::free(Demangled
);
356 // We try to keep the output as concise as possible. If only one thing on
357 // a given line could have been inlined, vectorized, etc. then we can put
358 // the marker on the source line itself. If there are multiple options
359 // then we want to distinguish them by placing the marker for each
360 // transformation on a separate line following the source line. When we
361 // do this, we use a '^' character to point to the appropriate column in
364 std::string
USpaces(Succinct
? 0 : UCDigits
, ' ');
365 std::string
VSpaces(Succinct
? 0 : VFDigits
+ ICDigits
+ 1, ' ');
367 auto UStr
= [UCDigits
](OptReportLocationInfo
&LLI
) {
369 raw_string_ostream
RS(R
);
372 RS
<< LLI
.UnrollCount
;
373 RS
<< std::string(UCDigits
- R
.size(), ' ');
379 auto VStr
= [VFDigits
,
380 ICDigits
](OptReportLocationInfo
&LLI
) -> std::string
{
382 raw_string_ostream
RS(R
);
385 RS
<< LLI
.VectorizationFactor
<< "," << LLI
.InterleaveCount
;
386 RS
<< std::string(VFDigits
+ ICDigits
+ 1 - R
.size(), ' ');
392 OS
<< llvm::format_decimal(L
, LNDigits
) << " ";
393 OS
<< (LLI
.Inlined
.Transformed
&& InlinedCols
< 2 ? "I" :
394 (NothingInlined
? "" : " "));
395 OS
<< (LLI
.Unrolled
.Transformed
&& UnrolledCols
< 2 ?
396 "U" + UStr(LLI
) : (NothingUnrolled
? "" : " " + USpaces
));
397 OS
<< (LLI
.Vectorized
.Transformed
&& VectorizedCols
< 2 ?
398 "V" + VStr(LLI
) : (NothingVectorized
? "" : " " + VSpaces
));
400 OS
<< " | " << *LI
<< "\n";
402 for (auto &J
: ColsInfo
) {
403 if ((J
.second
.Inlined
.Transformed
&& InlinedCols
> 1) ||
404 (J
.second
.Unrolled
.Transformed
&& UnrolledCols
> 1) ||
405 (J
.second
.Vectorized
.Transformed
&& VectorizedCols
> 1)) {
406 OS
<< std::string(LNDigits
+ 1, ' ');
407 OS
<< (J
.second
.Inlined
.Transformed
&&
408 InlinedCols
> 1 ? "I" : (NothingInlined
? "" : " "));
409 OS
<< (J
.second
.Unrolled
.Transformed
&&
410 UnrolledCols
> 1 ? "U" + UStr(J
.second
) :
411 (NothingUnrolled
? "" : " " + USpaces
));
412 OS
<< (J
.second
.Vectorized
.Transformed
&&
413 VectorizedCols
> 1 ? "V" + VStr(J
.second
) :
414 (NothingVectorized
? "" : " " + VSpaces
));
416 OS
<< " | " << std::string(J
.first
- 1, ' ') << "^\n";
421 // We need to figure out if the optimizations for this line were the same
422 // in each function context. If not, then we want to group the similar
423 // function contexts together and display each group separately. If
424 // they're all the same, then we only display the line once without any
425 // additional markings.
426 std::map
<std::map
<int, OptReportLocationInfo
>,
427 std::set
<std::string
>> UniqueLIs
;
429 OptReportLocationInfo AllLI
;
430 if (LII
!= FileInfo
.end()) {
431 const auto &FuncLineInfo
= LII
->second
;
432 for (const auto &FLII
: FuncLineInfo
) {
433 UniqueLIs
[FLII
.second
].insert(FLII
.first
);
435 for (const auto &OI
: FLII
.second
)
440 bool NothingHappened
= !AllLI
.Inlined
.Transformed
&&
441 !AllLI
.Unrolled
.Transformed
&&
442 !AllLI
.Vectorized
.Transformed
;
443 if (UniqueLIs
.size() > 1 && !NothingHappened
) {
445 for (const auto &FSLI
: UniqueLIs
)
446 PrintLine(true, FSLI
.second
);
448 } else if (UniqueLIs
.size() == 1) {
449 PrintLine(false, UniqueLIs
.begin()->second
);
451 PrintLine(false, std::set
<std::string
>());
459 int main(int argc
, const char **argv
) {
460 InitLLVM
X(argc
, argv
);
462 cl::HideUnrelatedOptions(OptReportCategory
);
463 cl::ParseCommandLineOptions(
465 "A tool to generate an optimization report from YAML optimization"
468 LocationInfoTy LocationInfo
;
469 if (!readLocationInfo(LocationInfo
))
471 if (!writeReport(LocationInfo
))