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/RemarkParser.h"
19 #include "llvm/Support/CommandLine.h"
20 #include "llvm/Support/Error.h"
21 #include "llvm/Support/ErrorOr.h"
22 #include "llvm/Support/FileSystem.h"
23 #include "llvm/Support/Format.h"
24 #include "llvm/Support/InitLLVM.h"
25 #include "llvm/Support/LineIterator.h"
26 #include "llvm/Support/MemoryBuffer.h"
27 #include "llvm/Support/Path.h"
28 #include "llvm/Support/Program.h"
29 #include "llvm/Support/WithColor.h"
30 #include "llvm/Support/YAMLTraits.h"
31 #include "llvm/Support/raw_ostream.h"
37 using namespace llvm::yaml
;
39 // Mark all our options with this category, everything else (except for -version
40 // and -help) will be hidden.
41 static cl::OptionCategory
42 OptReportCategory("llvm-opt-report options");
44 static cl::opt
<std::string
>
45 InputFileName(cl::Positional
, cl::desc("<input>"), cl::init("-"),
46 cl::cat(OptReportCategory
));
48 static cl::opt
<std::string
>
49 OutputFileName("o", cl::desc("Output file"), cl::init("-"),
50 cl::cat(OptReportCategory
));
52 static cl::opt
<std::string
>
53 InputRelDir("r", cl::desc("Root for relative input paths"), cl::init(""),
54 cl::cat(OptReportCategory
));
57 Succinct("s", cl::desc("Don't include vectorization factors, etc."),
58 cl::init(false), cl::cat(OptReportCategory
));
61 NoDemangle("no-demangle", cl::desc("Don't demangle function names"),
62 cl::init(false), cl::cat(OptReportCategory
));
65 // For each location in the source file, the common per-transformation state
67 struct OptReportLocationItemInfo
{
68 bool Analyzed
= false;
69 bool Transformed
= false;
71 OptReportLocationItemInfo
&operator |= (
72 const OptReportLocationItemInfo
&RHS
) {
73 Analyzed
|= RHS
.Analyzed
;
74 Transformed
|= RHS
.Transformed
;
79 bool operator < (const OptReportLocationItemInfo
&RHS
) const {
80 if (Analyzed
< RHS
.Analyzed
)
82 else if (Analyzed
> RHS
.Analyzed
)
84 else if (Transformed
< RHS
.Transformed
)
90 // The per-location information collected for producing an optimization report.
91 struct OptReportLocationInfo
{
92 OptReportLocationItemInfo Inlined
;
93 OptReportLocationItemInfo Unrolled
;
94 OptReportLocationItemInfo Vectorized
;
96 int VectorizationFactor
= 1;
97 int InterleaveCount
= 1;
100 OptReportLocationInfo
&operator |= (const OptReportLocationInfo
&RHS
) {
101 Inlined
|= RHS
.Inlined
;
102 Unrolled
|= RHS
.Unrolled
;
103 Vectorized
|= RHS
.Vectorized
;
105 VectorizationFactor
=
106 std::max(VectorizationFactor
, RHS
.VectorizationFactor
);
107 InterleaveCount
= std::max(InterleaveCount
, RHS
.InterleaveCount
);
108 UnrollCount
= std::max(UnrollCount
, RHS
.UnrollCount
);
113 bool operator < (const OptReportLocationInfo
&RHS
) const {
114 if (Inlined
< RHS
.Inlined
)
116 else if (RHS
.Inlined
< Inlined
)
118 else if (Unrolled
< RHS
.Unrolled
)
120 else if (RHS
.Unrolled
< Unrolled
)
122 else if (Vectorized
< RHS
.Vectorized
)
124 else if (RHS
.Vectorized
< Vectorized
|| Succinct
)
126 else if (VectorizationFactor
< RHS
.VectorizationFactor
)
128 else if (VectorizationFactor
> RHS
.VectorizationFactor
)
130 else if (InterleaveCount
< RHS
.InterleaveCount
)
132 else if (InterleaveCount
> RHS
.InterleaveCount
)
134 else if (UnrollCount
< RHS
.UnrollCount
)
140 typedef std::map
<std::string
, std::map
<int, std::map
<std::string
, std::map
<int,
141 OptReportLocationInfo
>>>> LocationInfoTy
;
142 } // anonymous namespace
144 static bool readLocationInfo(LocationInfoTy
&LocationInfo
) {
145 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> Buf
=
146 MemoryBuffer::getFile(InputFileName
.c_str());
147 if (std::error_code EC
= Buf
.getError()) {
148 WithColor::error() << "Can't open file " << InputFileName
<< ": "
149 << EC
.message() << "\n";
153 remarks::Parser
Parser((*Buf
)->getBuffer());
156 Expected
<const remarks::Remark
*> RemarkOrErr
= Parser
.getNext();
158 handleAllErrors(RemarkOrErr
.takeError(), [&](const ErrorInfoBase
&PE
) {
159 PE
.log(WithColor::error());
163 if (!*RemarkOrErr
) // End of file.
166 const remarks::Remark
&Remark
= **RemarkOrErr
;
168 bool Transformed
= Remark
.RemarkType
== remarks::Type::Passed
;
170 int VectorizationFactor
= 1;
171 int InterleaveCount
= 1;
174 for (const remarks::Argument
&Arg
: Remark
.Args
) {
175 if (Arg
.Key
== "VectorizationFactor")
176 Arg
.Val
.getAsInteger(10, VectorizationFactor
);
177 else if (Arg
.Key
== "InterleaveCount")
178 Arg
.Val
.getAsInteger(10, InterleaveCount
);
179 else if (Arg
.Key
== "UnrollCount")
180 Arg
.Val
.getAsInteger(10, UnrollCount
);
183 const Optional
<remarks::RemarkLocation
> &Loc
= Remark
.Loc
;
187 StringRef File
= Loc
->SourceFilePath
;
188 unsigned Line
= Loc
->SourceLine
;
189 unsigned Column
= Loc
->SourceColumn
;
191 // We track information on both actual and potential transformations. This
192 // way, if there are multiple possible things on a line that are, or could
193 // have been transformed, we can indicate that explicitly in the output.
194 auto UpdateLLII
= [Transformed
](OptReportLocationItemInfo
&LLII
) {
195 LLII
.Analyzed
= true;
197 LLII
.Transformed
= true;
200 if (Remark
.PassName
== "inline") {
201 auto &LI
= LocationInfo
[File
][Line
][Remark
.FunctionName
][Column
];
202 UpdateLLII(LI
.Inlined
);
203 } else if (Remark
.PassName
== "loop-unroll") {
204 auto &LI
= LocationInfo
[File
][Line
][Remark
.FunctionName
][Column
];
205 LI
.UnrollCount
= UnrollCount
;
206 UpdateLLII(LI
.Unrolled
);
207 } else if (Remark
.PassName
== "loop-vectorize") {
208 auto &LI
= LocationInfo
[File
][Line
][Remark
.FunctionName
][Column
];
209 LI
.VectorizationFactor
= VectorizationFactor
;
210 LI
.InterleaveCount
= InterleaveCount
;
211 UpdateLLII(LI
.Vectorized
);
218 static bool writeReport(LocationInfoTy
&LocationInfo
) {
220 llvm::raw_fd_ostream
OS(OutputFileName
, EC
,
221 llvm::sys::fs::F_Text
);
223 WithColor::error() << "Can't open file " << OutputFileName
<< ": "
224 << EC
.message() << "\n";
228 bool FirstFile
= true;
229 for (auto &FI
: LocationInfo
) {
230 SmallString
<128> FileName(FI
.first
);
231 if (!InputRelDir
.empty())
232 sys::fs::make_absolute(InputRelDir
, FileName
);
234 const auto &FileInfo
= FI
.second
;
236 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> Buf
=
237 MemoryBuffer::getFile(FileName
);
238 if (std::error_code EC
= Buf
.getError()) {
239 WithColor::error() << "Can't open file " << FileName
<< ": "
240 << EC
.message() << "\n";
249 OS
<< "< " << FileName
<< "\n";
251 // Figure out how many characters we need for the vectorization factors
253 OptReportLocationInfo MaxLI
;
254 for (auto &FLI
: FileInfo
)
255 for (auto &FI
: FLI
.second
)
256 for (auto &LI
: FI
.second
)
259 bool NothingInlined
= !MaxLI
.Inlined
.Transformed
;
260 bool NothingUnrolled
= !MaxLI
.Unrolled
.Transformed
;
261 bool NothingVectorized
= !MaxLI
.Vectorized
.Transformed
;
263 unsigned VFDigits
= llvm::utostr(MaxLI
.VectorizationFactor
).size();
264 unsigned ICDigits
= llvm::utostr(MaxLI
.InterleaveCount
).size();
265 unsigned UCDigits
= llvm::utostr(MaxLI
.UnrollCount
).size();
267 // Figure out how many characters we need for the line numbers.
268 int64_t NumLines
= 0;
269 for (line_iterator
LI(*Buf
.get(), false); LI
!= line_iterator(); ++LI
)
272 unsigned LNDigits
= llvm::utostr(NumLines
).size();
274 for (line_iterator
LI(*Buf
.get(), false); LI
!= line_iterator(); ++LI
) {
275 int64_t L
= LI
.line_number();
276 auto LII
= FileInfo
.find(L
);
278 auto PrintLine
= [&](bool PrintFuncName
,
279 const std::set
<std::string
> &FuncNameSet
) {
280 OptReportLocationInfo LLI
;
282 std::map
<int, OptReportLocationInfo
> ColsInfo
;
283 unsigned InlinedCols
= 0, UnrolledCols
= 0, VectorizedCols
= 0;
285 if (LII
!= FileInfo
.end() && !FuncNameSet
.empty()) {
286 const auto &LineInfo
= LII
->second
;
288 for (auto &CI
: LineInfo
.find(*FuncNameSet
.begin())->second
) {
290 ColsInfo
[Col
] = CI
.second
;
291 InlinedCols
+= CI
.second
.Inlined
.Analyzed
;
292 UnrolledCols
+= CI
.second
.Unrolled
.Analyzed
;
293 VectorizedCols
+= CI
.second
.Vectorized
.Analyzed
;
301 bool FirstFunc
= true;
302 for (const auto &FuncName
: FuncNameSet
) {
308 bool Printed
= false;
312 itaniumDemangle(FuncName
.c_str(), nullptr, nullptr, &Status
);
313 if (Demangled
&& Status
== 0) {
319 std::free(Demangled
);
329 // We try to keep the output as concise as possible. If only one thing on
330 // a given line could have been inlined, vectorized, etc. then we can put
331 // the marker on the source line itself. If there are multiple options
332 // then we want to distinguish them by placing the marker for each
333 // transformation on a separate line following the source line. When we
334 // do this, we use a '^' character to point to the appropriate column in
337 std::string
USpaces(Succinct
? 0 : UCDigits
, ' ');
338 std::string
VSpaces(Succinct
? 0 : VFDigits
+ ICDigits
+ 1, ' ');
340 auto UStr
= [UCDigits
](OptReportLocationInfo
&LLI
) {
342 raw_string_ostream
RS(R
);
345 RS
<< LLI
.UnrollCount
;
346 RS
<< std::string(UCDigits
- RS
.str().size(), ' ');
352 auto VStr
= [VFDigits
,
353 ICDigits
](OptReportLocationInfo
&LLI
) -> std::string
{
355 raw_string_ostream
RS(R
);
358 RS
<< LLI
.VectorizationFactor
<< "," << LLI
.InterleaveCount
;
359 RS
<< std::string(VFDigits
+ ICDigits
+ 1 - RS
.str().size(), ' ');
365 OS
<< llvm::format_decimal(L
, LNDigits
) << " ";
366 OS
<< (LLI
.Inlined
.Transformed
&& InlinedCols
< 2 ? "I" :
367 (NothingInlined
? "" : " "));
368 OS
<< (LLI
.Unrolled
.Transformed
&& UnrolledCols
< 2 ?
369 "U" + UStr(LLI
) : (NothingUnrolled
? "" : " " + USpaces
));
370 OS
<< (LLI
.Vectorized
.Transformed
&& VectorizedCols
< 2 ?
371 "V" + VStr(LLI
) : (NothingVectorized
? "" : " " + VSpaces
));
373 OS
<< " | " << *LI
<< "\n";
375 for (auto &J
: ColsInfo
) {
376 if ((J
.second
.Inlined
.Transformed
&& InlinedCols
> 1) ||
377 (J
.second
.Unrolled
.Transformed
&& UnrolledCols
> 1) ||
378 (J
.second
.Vectorized
.Transformed
&& VectorizedCols
> 1)) {
379 OS
<< std::string(LNDigits
+ 1, ' ');
380 OS
<< (J
.second
.Inlined
.Transformed
&&
381 InlinedCols
> 1 ? "I" : (NothingInlined
? "" : " "));
382 OS
<< (J
.second
.Unrolled
.Transformed
&&
383 UnrolledCols
> 1 ? "U" + UStr(J
.second
) :
384 (NothingUnrolled
? "" : " " + USpaces
));
385 OS
<< (J
.second
.Vectorized
.Transformed
&&
386 VectorizedCols
> 1 ? "V" + VStr(J
.second
) :
387 (NothingVectorized
? "" : " " + VSpaces
));
389 OS
<< " | " << std::string(J
.first
- 1, ' ') << "^\n";
394 // We need to figure out if the optimizations for this line were the same
395 // in each function context. If not, then we want to group the similar
396 // function contexts together and display each group separately. If
397 // they're all the same, then we only display the line once without any
398 // additional markings.
399 std::map
<std::map
<int, OptReportLocationInfo
>,
400 std::set
<std::string
>> UniqueLIs
;
402 OptReportLocationInfo AllLI
;
403 if (LII
!= FileInfo
.end()) {
404 const auto &FuncLineInfo
= LII
->second
;
405 for (const auto &FLII
: FuncLineInfo
) {
406 UniqueLIs
[FLII
.second
].insert(FLII
.first
);
408 for (const auto &OI
: FLII
.second
)
413 bool NothingHappened
= !AllLI
.Inlined
.Transformed
&&
414 !AllLI
.Unrolled
.Transformed
&&
415 !AllLI
.Vectorized
.Transformed
;
416 if (UniqueLIs
.size() > 1 && !NothingHappened
) {
418 for (const auto &FSLI
: UniqueLIs
)
419 PrintLine(true, FSLI
.second
);
421 } else if (UniqueLIs
.size() == 1) {
422 PrintLine(false, UniqueLIs
.begin()->second
);
424 PrintLine(false, std::set
<std::string
>());
432 int main(int argc
, const char **argv
) {
433 InitLLVM
X(argc
, argv
);
435 cl::HideUnrelatedOptions(OptReportCategory
);
436 cl::ParseCommandLineOptions(
438 "A tool to generate an optimization report from YAML optimization"
441 LocationInfoTy LocationInfo
;
442 if (!readLocationInfo(LocationInfo
))
444 if (!writeReport(LocationInfo
))