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/Support/CommandLine.h"
19 #include "llvm/Support/Error.h"
20 #include "llvm/Support/ErrorOr.h"
21 #include "llvm/Support/FileSystem.h"
22 #include "llvm/Support/Format.h"
23 #include "llvm/Support/InitLLVM.h"
24 #include "llvm/Support/LineIterator.h"
25 #include "llvm/Support/MemoryBuffer.h"
26 #include "llvm/Support/Path.h"
27 #include "llvm/Support/Program.h"
28 #include "llvm/Support/WithColor.h"
29 #include "llvm/Support/YAMLTraits.h"
30 #include "llvm/Support/raw_ostream.h"
36 using namespace llvm::yaml
;
38 static cl::opt
<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden
);
40 // Mark all our options with this category, everything else (except for -version
41 // and -help) will be hidden.
42 static cl::OptionCategory
43 OptReportCategory("llvm-opt-report options");
45 static cl::opt
<std::string
>
46 InputFileName(cl::Positional
, cl::desc("<input>"), cl::init("-"),
47 cl::cat(OptReportCategory
));
49 static cl::opt
<std::string
>
50 OutputFileName("o", cl::desc("Output file"), cl::init("-"),
51 cl::cat(OptReportCategory
));
53 static cl::opt
<std::string
>
54 InputRelDir("r", cl::desc("Root for relative input paths"), cl::init(""),
55 cl::cat(OptReportCategory
));
58 Succinct("s", cl::desc("Don't include vectorization factors, etc."),
59 cl::init(false), cl::cat(OptReportCategory
));
62 NoDemangle("no-demangle", cl::desc("Don't demangle function names"),
63 cl::init(false), cl::cat(OptReportCategory
));
66 // For each location in the source file, the common per-transformation state
68 struct OptReportLocationItemInfo
{
69 bool Analyzed
= false;
70 bool Transformed
= false;
72 OptReportLocationItemInfo
&operator |= (
73 const OptReportLocationItemInfo
&RHS
) {
74 Analyzed
|= RHS
.Analyzed
;
75 Transformed
|= RHS
.Transformed
;
80 bool operator < (const OptReportLocationItemInfo
&RHS
) const {
81 if (Analyzed
< RHS
.Analyzed
)
83 else if (Analyzed
> RHS
.Analyzed
)
85 else if (Transformed
< RHS
.Transformed
)
91 // The per-location information collected for producing an optimization report.
92 struct OptReportLocationInfo
{
93 OptReportLocationItemInfo Inlined
;
94 OptReportLocationItemInfo Unrolled
;
95 OptReportLocationItemInfo Vectorized
;
97 int VectorizationFactor
= 1;
98 int InterleaveCount
= 1;
101 OptReportLocationInfo
&operator |= (const OptReportLocationInfo
&RHS
) {
102 Inlined
|= RHS
.Inlined
;
103 Unrolled
|= RHS
.Unrolled
;
104 Vectorized
|= RHS
.Vectorized
;
106 VectorizationFactor
=
107 std::max(VectorizationFactor
, RHS
.VectorizationFactor
);
108 InterleaveCount
= std::max(InterleaveCount
, RHS
.InterleaveCount
);
109 UnrollCount
= std::max(UnrollCount
, RHS
.UnrollCount
);
114 bool operator < (const OptReportLocationInfo
&RHS
) const {
115 if (Inlined
< RHS
.Inlined
)
117 else if (RHS
.Inlined
< Inlined
)
119 else if (Unrolled
< RHS
.Unrolled
)
121 else if (RHS
.Unrolled
< Unrolled
)
123 else if (Vectorized
< RHS
.Vectorized
)
125 else if (RHS
.Vectorized
< Vectorized
|| Succinct
)
127 else if (VectorizationFactor
< RHS
.VectorizationFactor
)
129 else if (VectorizationFactor
> RHS
.VectorizationFactor
)
131 else if (InterleaveCount
< RHS
.InterleaveCount
)
133 else if (InterleaveCount
> RHS
.InterleaveCount
)
135 else if (UnrollCount
< RHS
.UnrollCount
)
141 typedef std::map
<std::string
, std::map
<int, std::map
<std::string
, std::map
<int,
142 OptReportLocationInfo
>>>> LocationInfoTy
;
143 } // anonymous namespace
145 static bool readLocationInfo(LocationInfoTy
&LocationInfo
) {
146 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> Buf
=
147 MemoryBuffer::getFile(InputFileName
.c_str());
148 if (std::error_code EC
= Buf
.getError()) {
149 WithColor::error() << "Can't open file " << InputFileName
<< ": "
150 << EC
.message() << "\n";
154 StringRef Buffer
= (*Buf
)->getBuffer();
155 LLVMRemarkParserRef Parser
=
156 LLVMRemarkParserCreate(Buffer
.data(), Buffer
.size());
158 LLVMRemarkEntry
*Remark
= nullptr;
159 while ((Remark
= LLVMRemarkParserGetNext(Parser
))) {
161 StringRef(Remark
->RemarkType
.Str
, Remark
->RemarkType
.Len
) == "!Passed";
162 StringRef
Pass(Remark
->PassName
.Str
, Remark
->PassName
.Len
);
163 StringRef
File(Remark
->DebugLoc
.SourceFile
.Str
,
164 Remark
->DebugLoc
.SourceFile
.Len
);
165 StringRef
Function(Remark
->FunctionName
.Str
, Remark
->FunctionName
.Len
);
166 uint32_t Line
= Remark
->DebugLoc
.SourceLineNumber
;
167 uint32_t Column
= Remark
->DebugLoc
.SourceColumnNumber
;
168 ArrayRef
<LLVMRemarkArg
> Args(Remark
->Args
, Remark
->NumArgs
);
170 int VectorizationFactor
= 1;
171 int InterleaveCount
= 1;
174 for (const LLVMRemarkArg
&Arg
: Args
) {
175 StringRef
ArgKeyName(Arg
.Key
.Str
, Arg
.Key
.Len
);
176 StringRef
ArgValue(Arg
.Value
.Str
, Arg
.Value
.Len
);
177 if (ArgKeyName
== "VectorizationFactor")
178 ArgValue
.getAsInteger(10, VectorizationFactor
);
179 else if (ArgKeyName
== "InterleaveCount")
180 ArgValue
.getAsInteger(10, InterleaveCount
);
181 else if (ArgKeyName
== "UnrollCount")
182 ArgValue
.getAsInteger(10, UnrollCount
);
185 if (Line
< 1 || File
.empty())
188 // We track information on both actual and potential transformations. This
189 // way, if there are multiple possible things on a line that are, or could
190 // have been transformed, we can indicate that explicitly in the output.
191 auto UpdateLLII
= [Transformed
](OptReportLocationItemInfo
&LLII
) {
192 LLII
.Analyzed
= true;
194 LLII
.Transformed
= true;
197 if (Pass
== "inline") {
198 auto &LI
= LocationInfo
[File
][Line
][Function
][Column
];
199 UpdateLLII(LI
.Inlined
);
200 } else if (Pass
== "loop-unroll") {
201 auto &LI
= LocationInfo
[File
][Line
][Function
][Column
];
202 LI
.UnrollCount
= UnrollCount
;
203 UpdateLLII(LI
.Unrolled
);
204 } else if (Pass
== "loop-vectorize") {
205 auto &LI
= LocationInfo
[File
][Line
][Function
][Column
];
206 LI
.VectorizationFactor
= VectorizationFactor
;
207 LI
.InterleaveCount
= InterleaveCount
;
208 UpdateLLII(LI
.Vectorized
);
212 bool HasError
= LLVMRemarkParserHasError(Parser
);
214 WithColor::error() << LLVMRemarkParserGetErrorMessage(Parser
) << "\n";
216 LLVMRemarkParserDispose(Parser
);
220 static bool writeReport(LocationInfoTy
&LocationInfo
) {
222 llvm::raw_fd_ostream
OS(OutputFileName
, EC
,
223 llvm::sys::fs::F_Text
);
225 WithColor::error() << "Can't open file " << OutputFileName
<< ": "
226 << EC
.message() << "\n";
230 bool FirstFile
= true;
231 for (auto &FI
: LocationInfo
) {
232 SmallString
<128> FileName(FI
.first
);
233 if (!InputRelDir
.empty())
234 sys::fs::make_absolute(InputRelDir
, FileName
);
236 const auto &FileInfo
= FI
.second
;
238 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> Buf
=
239 MemoryBuffer::getFile(FileName
);
240 if (std::error_code EC
= Buf
.getError()) {
241 WithColor::error() << "Can't open file " << FileName
<< ": "
242 << EC
.message() << "\n";
251 OS
<< "< " << FileName
<< "\n";
253 // Figure out how many characters we need for the vectorization factors
255 OptReportLocationInfo MaxLI
;
256 for (auto &FLI
: FileInfo
)
257 for (auto &FI
: FLI
.second
)
258 for (auto &LI
: FI
.second
)
261 bool NothingInlined
= !MaxLI
.Inlined
.Transformed
;
262 bool NothingUnrolled
= !MaxLI
.Unrolled
.Transformed
;
263 bool NothingVectorized
= !MaxLI
.Vectorized
.Transformed
;
265 unsigned VFDigits
= llvm::utostr(MaxLI
.VectorizationFactor
).size();
266 unsigned ICDigits
= llvm::utostr(MaxLI
.InterleaveCount
).size();
267 unsigned UCDigits
= llvm::utostr(MaxLI
.UnrollCount
).size();
269 // Figure out how many characters we need for the line numbers.
270 int64_t NumLines
= 0;
271 for (line_iterator
LI(*Buf
.get(), false); LI
!= line_iterator(); ++LI
)
274 unsigned LNDigits
= llvm::utostr(NumLines
).size();
276 for (line_iterator
LI(*Buf
.get(), false); LI
!= line_iterator(); ++LI
) {
277 int64_t L
= LI
.line_number();
278 auto LII
= FileInfo
.find(L
);
280 auto PrintLine
= [&](bool PrintFuncName
,
281 const std::set
<std::string
> &FuncNameSet
) {
282 OptReportLocationInfo LLI
;
284 std::map
<int, OptReportLocationInfo
> ColsInfo
;
285 unsigned InlinedCols
= 0, UnrolledCols
= 0, VectorizedCols
= 0;
287 if (LII
!= FileInfo
.end() && !FuncNameSet
.empty()) {
288 const auto &LineInfo
= LII
->second
;
290 for (auto &CI
: LineInfo
.find(*FuncNameSet
.begin())->second
) {
292 ColsInfo
[Col
] = CI
.second
;
293 InlinedCols
+= CI
.second
.Inlined
.Analyzed
;
294 UnrolledCols
+= CI
.second
.Unrolled
.Analyzed
;
295 VectorizedCols
+= CI
.second
.Vectorized
.Analyzed
;
303 bool FirstFunc
= true;
304 for (const auto &FuncName
: FuncNameSet
) {
310 bool Printed
= false;
314 itaniumDemangle(FuncName
.c_str(), nullptr, nullptr, &Status
);
315 if (Demangled
&& Status
== 0) {
321 std::free(Demangled
);
331 // We try to keep the output as concise as possible. If only one thing on
332 // a given line could have been inlined, vectorized, etc. then we can put
333 // the marker on the source line itself. If there are multiple options
334 // then we want to distinguish them by placing the marker for each
335 // transformation on a separate line following the source line. When we
336 // do this, we use a '^' character to point to the appropriate column in
339 std::string
USpaces(Succinct
? 0 : UCDigits
, ' ');
340 std::string
VSpaces(Succinct
? 0 : VFDigits
+ ICDigits
+ 1, ' ');
342 auto UStr
= [UCDigits
](OptReportLocationInfo
&LLI
) {
344 raw_string_ostream
RS(R
);
347 RS
<< LLI
.UnrollCount
;
348 RS
<< std::string(UCDigits
- RS
.str().size(), ' ');
354 auto VStr
= [VFDigits
,
355 ICDigits
](OptReportLocationInfo
&LLI
) -> std::string
{
357 raw_string_ostream
RS(R
);
360 RS
<< LLI
.VectorizationFactor
<< "," << LLI
.InterleaveCount
;
361 RS
<< std::string(VFDigits
+ ICDigits
+ 1 - RS
.str().size(), ' ');
367 OS
<< llvm::format_decimal(L
, LNDigits
) << " ";
368 OS
<< (LLI
.Inlined
.Transformed
&& InlinedCols
< 2 ? "I" :
369 (NothingInlined
? "" : " "));
370 OS
<< (LLI
.Unrolled
.Transformed
&& UnrolledCols
< 2 ?
371 "U" + UStr(LLI
) : (NothingUnrolled
? "" : " " + USpaces
));
372 OS
<< (LLI
.Vectorized
.Transformed
&& VectorizedCols
< 2 ?
373 "V" + VStr(LLI
) : (NothingVectorized
? "" : " " + VSpaces
));
375 OS
<< " | " << *LI
<< "\n";
377 for (auto &J
: ColsInfo
) {
378 if ((J
.second
.Inlined
.Transformed
&& InlinedCols
> 1) ||
379 (J
.second
.Unrolled
.Transformed
&& UnrolledCols
> 1) ||
380 (J
.second
.Vectorized
.Transformed
&& VectorizedCols
> 1)) {
381 OS
<< std::string(LNDigits
+ 1, ' ');
382 OS
<< (J
.second
.Inlined
.Transformed
&&
383 InlinedCols
> 1 ? "I" : (NothingInlined
? "" : " "));
384 OS
<< (J
.second
.Unrolled
.Transformed
&&
385 UnrolledCols
> 1 ? "U" + UStr(J
.second
) :
386 (NothingUnrolled
? "" : " " + USpaces
));
387 OS
<< (J
.second
.Vectorized
.Transformed
&&
388 VectorizedCols
> 1 ? "V" + VStr(J
.second
) :
389 (NothingVectorized
? "" : " " + VSpaces
));
391 OS
<< " | " << std::string(J
.first
- 1, ' ') << "^\n";
396 // We need to figure out if the optimizations for this line were the same
397 // in each function context. If not, then we want to group the similar
398 // function contexts together and display each group separately. If
399 // they're all the same, then we only display the line once without any
400 // additional markings.
401 std::map
<std::map
<int, OptReportLocationInfo
>,
402 std::set
<std::string
>> UniqueLIs
;
404 OptReportLocationInfo AllLI
;
405 if (LII
!= FileInfo
.end()) {
406 const auto &FuncLineInfo
= LII
->second
;
407 for (const auto &FLII
: FuncLineInfo
) {
408 UniqueLIs
[FLII
.second
].insert(FLII
.first
);
410 for (const auto &OI
: FLII
.second
)
415 bool NothingHappened
= !AllLI
.Inlined
.Transformed
&&
416 !AllLI
.Unrolled
.Transformed
&&
417 !AllLI
.Vectorized
.Transformed
;
418 if (UniqueLIs
.size() > 1 && !NothingHappened
) {
420 for (const auto &FSLI
: UniqueLIs
)
421 PrintLine(true, FSLI
.second
);
423 } else if (UniqueLIs
.size() == 1) {
424 PrintLine(false, UniqueLIs
.begin()->second
);
426 PrintLine(false, std::set
<std::string
>());
434 int main(int argc
, const char **argv
) {
435 InitLLVM
X(argc
, argv
);
437 cl::HideUnrelatedOptions(OptReportCategory
);
438 cl::ParseCommandLineOptions(
440 "A tool to generate an optimization report from YAML optimization"
444 cl::PrintHelpMessage();
448 LocationInfoTy LocationInfo
;
449 if (!readLocationInfo(LocationInfo
))
451 if (!writeReport(LocationInfo
))