llvm.org GIT mirror llvm / 6af7eec
Add an llvm-opt-report tool to generate basic source-annotated optimization summaries LLVM now has the ability to record information from optimization remarks in a machine-consumable YAML file for later analysis. This can be enabled in opt (see r282539), and D25225 adds a Clang flag to do the same. This patch adds llvm-opt-report, a tool to generate basic optimization "listing" files (annotated sources with information about what optimizations were performed) from one of these YAML inputs. D19678 proposed to add this capability directly to Clang, but this more-general YAML-based infrastructure was the direction we decided upon in that review thread. For this optimization report, I focused on making the output as succinct as possible while providing information on inlining and loop transformations. The goal here is that the source code should still be easily readable in the report. My primary inspiration here is the reports generated by Cray's tools (http://docs.cray.com/books/S-2496-4101/html-S-2496-4101/z1112823641oswald.html). These reports are highly regarded within the HPC community. Intel's compiler, for example, also has an optimization-report capability (https://software.intel.com/sites/default/files/managed/55/b1/new-compiler-optimization-reports.pdf). $ cat /tmp/v.c void bar(); void foo() { bar(); } void Test(int *res, int *c, int *d, int *p, int n) { int i; #pragma clang loop vectorize(assume_safety) for (i = 0; i < 1600; i++) { res[i] = (p[i] == 0) ? res[i] : res[i] + d[i]; } for (i = 0; i < 16; i++) { res[i] = (p[i] == 0) ? res[i] : res[i] + d[i]; } foo(); foo(); bar(); foo(); } D25225 adds -fsave-optimization-record (and -fsave-optimization-record=filename), and this would be used as follows: $ clang -O3 -o /tmp/v.o -c /tmp/v.c -fsave-optimization-record $ llvm-opt-report /tmp/v.yaml > /tmp/v.lst $ cat /tmp/v.lst < /tmp/v.c 2 | void bar(); 3 | void foo() { bar(); } 4 | 5 | void Test(int *res, int *c, int *d, int *p, int n) { 6 | int i; 7 | 8 | #pragma clang loop vectorize(assume_safety) 9 V4,2 | for (i = 0; i < 1600; i++) { 10 | res[i] = (p[i] == 0) ? res[i] : res[i] + d[i]; 11 | } 12 | 13 U16 | for (i = 0; i < 16; i++) { 14 | res[i] = (p[i] == 0) ? res[i] : res[i] + d[i]; 15 | } 16 | 17 I | foo(); 18 | 19 | foo(); bar(); foo(); I | ^ I | ^ 20 | } Each source line gets a prefix giving the line number, and a few columns for important optimizations: inlining, loop unrolling and loop vectorization. An 'I' is printed next to a line where a function was inlined, a 'U' next to an unrolled loop, and 'V' next to a vectorized loop. These are printed on the relevant code line when that seems unambiguous, or on subsequent lines when multiple potential options exist (messages, both positive and negative, from the same optimization with different column numbers are taken to indicate potential ambiguity). When on subsequent lines, a '^' is output in the relevant column. Annotated source for all relevant input files are put into the listing file (each starting with '<' and then the file name). You can disable having the unrolling/vectorization factors appear by using the -s flag. Differential Revision: https://reviews.llvm.org/D25262 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@283398 91177308-0d34-0410-b5e6-96231b3b80d8 Hal Finkel 2 years ago
7 changed file(s) with 771 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
4848 llvm-mcmarkup
4949 llvm-nm
5050 llvm-objdump
51 llvm-opt-report
5152 llvm-pdbdump
5253 llvm-profdata
5354 llvm-ranlib
0 void bar();
1 void foo() { bar(); }
2
3 #include "or.h"
4
5 void Test(int *res, int *c, int *d, int *p, int n) {
6 int i;
7
8 #pragma clang loop vectorize(assume_safety)
9 for (i = 0; i < 1600; i++) {
10 res[i] = (p[i] == 0) ? res[i] : res[i] + d[i];
11 }
12
13 for (i = 0; i < 16; i++) {
14 res[i] = (p[i] == 0) ? res[i] : res[i] + d[i];
15 }
16
17 foo();
18
19 foo(); bar(); foo();
20 }
21
0 void TestH(int *res, int *c, int *d, int *p, int n) {
1 int i;
2
3 #pragma clang loop vectorize(assume_safety)
4 for (i = 0; i < 1600; i++) {
5 res[i] = (p[i] == 0) ? res[i] : res[i] + d[i];
6 }
7
8 for (i = 0; i < 16; i++) {
9 res[i] = (p[i] == 0) ? res[i] : res[i] + d[i];
10 }
11
12 foo();
13
14 foo(); bar(); foo();
15 }
16
0 --- !Missed
1 Pass: inline
2 Name: NoDefinition
3 DebugLoc: { File: Inputs/or.c, Line: 2, Column: 14 }
4 Function: foo
5 Args:
6 - Callee: bar
7 - String: ' will not be inlined into '
8 - Caller: foo
9 - String: ' because its definition is unavailable'
10 ...
11 --- !Missed
12 Pass: inline
13 Name: NoDefinition
14 DebugLoc: { File: Inputs/or.h, Line: 15, Column: 10 }
15 Function: TestH
16 Args:
17 - Callee: bar
18 - String: ' will not be inlined into '
19 - Caller: TestH
20 - String: ' because its definition is unavailable'
21 ...
22 --- !Analysis
23 Pass: inline
24 Name: CanBeInlined
25 DebugLoc: { File: Inputs/or.h, Line: 13, Column: 3 }
26 Function: TestH
27 Args:
28 - Callee: foo
29 - String: ' can be inlined into '
30 - Caller: TestH
31 - String: ' with cost='
32 - Cost: '30'
33 - String: ' (threshold='
34 - Threshold: '412'
35 - String: ')'
36 ...
37 --- !Passed
38 Pass: inline
39 Name: Inlined
40 DebugLoc: { File: Inputs/or.h, Line: 13, Column: 3 }
41 Function: TestH
42 Args:
43 - Callee: foo
44 - String: ' inlined into '
45 - Caller: TestH
46 ...
47 --- !Analysis
48 Pass: inline
49 Name: CanBeInlined
50 DebugLoc: { File: Inputs/or.h, Line: 15, Column: 3 }
51 Function: TestH
52 Args:
53 - Callee: foo
54 - String: ' can be inlined into '
55 - Caller: TestH
56 - String: ' with cost='
57 - Cost: '30'
58 - String: ' (threshold='
59 - Threshold: '412'
60 - String: ')'
61 ...
62 --- !Passed
63 Pass: inline
64 Name: Inlined
65 DebugLoc: { File: Inputs/or.h, Line: 15, Column: 3 }
66 Function: TestH
67 Args:
68 - Callee: foo
69 - String: ' inlined into '
70 - Caller: TestH
71 ...
72 --- !Analysis
73 Pass: inline
74 Name: CanBeInlined
75 DebugLoc: { File: Inputs/or.h, Line: 15, Column: 17 }
76 Function: TestH
77 Args:
78 - Callee: foo
79 - String: ' can be inlined into '
80 - Caller: TestH
81 - String: ' with cost='
82 - Cost: '30'
83 - String: ' (threshold='
84 - Threshold: '412'
85 - String: ')'
86 ...
87 --- !Passed
88 Pass: inline
89 Name: Inlined
90 DebugLoc: { File: Inputs/or.h, Line: 15, Column: 17 }
91 Function: TestH
92 Args:
93 - Callee: foo
94 - String: ' inlined into '
95 - Caller: TestH
96 ...
97 --- !Passed
98 Pass: loop-unroll
99 Name: FullyUnrolled
100 DebugLoc: { File: Inputs/or.h, Line: 9, Column: 3 }
101 Function: TestH
102 Args:
103 - String: 'completely unrolled loop with '
104 - UnrollCount: '16'
105 - String: ' iterations'
106 ...
107 --- !Missed
108 Pass: inline
109 Name: NoDefinition
110 DebugLoc: { File: Inputs/or.c, Line: 20, Column: 10 }
111 Function: Test
112 Args:
113 - Callee: bar
114 - String: ' will not be inlined into '
115 - Caller: Test
116 - String: ' because its definition is unavailable'
117 ...
118 --- !Analysis
119 Pass: inline
120 Name: CanBeInlined
121 DebugLoc: { File: Inputs/or.c, Line: 18, Column: 3 }
122 Function: Test
123 Args:
124 - Callee: foo
125 - String: ' can be inlined into '
126 - Caller: Test
127 - String: ' with cost='
128 - Cost: '30'
129 - String: ' (threshold='
130 - Threshold: '412'
131 - String: ')'
132 ...
133 --- !Passed
134 Pass: inline
135 Name: Inlined
136 DebugLoc: { File: Inputs/or.c, Line: 18, Column: 3 }
137 Function: Test
138 Args:
139 - Callee: foo
140 - String: ' inlined into '
141 - Caller: Test
142 ...
143 --- !Analysis
144 Pass: inline
145 Name: CanBeInlined
146 DebugLoc: { File: Inputs/or.c, Line: 20, Column: 3 }
147 Function: Test
148 Args:
149 - Callee: foo
150 - String: ' can be inlined into '
151 - Caller: Test
152 - String: ' with cost='
153 - Cost: '30'
154 - String: ' (threshold='
155 - Threshold: '412'
156 - String: ')'
157 ...
158 --- !Passed
159 Pass: inline
160 Name: Inlined
161 DebugLoc: { File: Inputs/or.c, Line: 20, Column: 3 }
162 Function: Test
163 Args:
164 - Callee: foo
165 - String: ' inlined into '
166 - Caller: Test
167 ...
168 --- !Analysis
169 Pass: inline
170 Name: CanBeInlined
171 DebugLoc: { File: Inputs/or.c, Line: 20, Column: 17 }
172 Function: Test
173 Args:
174 - Callee: foo
175 - String: ' can be inlined into '
176 - Caller: Test
177 - String: ' with cost='
178 - Cost: '30'
179 - String: ' (threshold='
180 - Threshold: '412'
181 - String: ')'
182 ...
183 --- !Passed
184 Pass: inline
185 Name: Inlined
186 DebugLoc: { File: Inputs/or.c, Line: 20, Column: 17 }
187 Function: Test
188 Args:
189 - Callee: foo
190 - String: ' inlined into '
191 - Caller: Test
192 ...
193 --- !Passed
194 Pass: loop-unroll
195 Name: FullyUnrolled
196 DebugLoc: { File: Inputs/or.c, Line: 14, Column: 3 }
197 Function: Test
198 Args:
199 - String: 'completely unrolled loop with '
200 - UnrollCount: '16'
201 - String: ' iterations'
202 ...
203 --- !Passed
204 Pass: loop-vectorize
205 Name: Vectorized
206 DebugLoc: { File: Inputs/or.h, Line: 5, Column: 3 }
207 Function: TestH
208 Args:
209 - String: 'vectorized loop (vectorization width: '
210 - VectorizationFactor: '4'
211 - String: ', interleaved count: '
212 - InterleaveCount: '2'
213 - String: ')'
214 ...
215 --- !Passed
216 Pass: loop-vectorize
217 Name: Vectorized
218 DebugLoc: { File: Inputs/or.c, Line: 10, Column: 3 }
219 Function: Test
220 Args:
221 - String: 'vectorized loop (vectorization width: '
222 - VectorizationFactor: '4'
223 - String: ', interleaved count: '
224 - InterleaveCount: '2'
225 - String: ')'
226 ...
0 RUN: llvm-opt-report -r %p %p/Inputs/or.yaml | FileCheck -strict-whitespace %s
1 RUN: llvm-opt-report -s -r %p %p/Inputs/or.yaml | FileCheck -strict-whitespace -check-prefix=CHECK-SUCCINCT %s
2
3 ; CHECK: < {{.*}}/Inputs/or.c
4 ; CHECK-NEXT: 2 | void bar();
5 ; CHECK-NEXT: 3 | void foo() { bar(); }
6 ; CHECK-NEXT: 4 |
7 ; CHECK-NEXT: 5 | #include "or.h"
8 ; CHECK-NEXT: 6 |
9 ; CHECK-NEXT: 7 | void Test(int *res, int *c, int *d, int *p, int n) {
10 ; CHECK-NEXT: 8 | int i;
11 ; CHECK-NEXT: 9 |
12 ; CHECK-NEXT: 10 | #pragma clang loop vectorize(assume_safety)
13 ; CHECK-NEXT: 11 V4,2 | for (i = 0; i < 1600; i++) {
14 ; CHECK-NEXT: 12 | res[i] = (p[i] == 0) ? res[i] : res[i] + d[i];
15 ; CHECK-NEXT: 13 | }
16 ; CHECK-NEXT: 14 |
17 ; CHECK-NEXT: 15 U16 | for (i = 0; i < 16; i++) {
18 ; CHECK-NEXT: 16 | res[i] = (p[i] == 0) ? res[i] : res[i] + d[i];
19 ; CHECK-NEXT: 17 | }
20 ; CHECK-NEXT: 18 |
21 ; CHECK-NEXT: 19 I | foo();
22 ; CHECK-NEXT: 20 |
23 ; CHECK-NEXT: 21 | foo(); bar(); foo();
24 ; CHECK-NEXT: I | ^
25 ; CHECK-NEXT: I | ^
26 ; CHECK-NEXT: 22 | }
27 ; CHECK-NEXT: 23 |
28
29 ; CHECK: < {{.*}}/Inputs/or.h
30 ; CHECK-NEXT: 2 | void TestH(int *res, int *c, int *d, int *p, int n) {
31 ; CHECK-NEXT: 3 | int i;
32 ; CHECK-NEXT: 4 |
33 ; CHECK-NEXT: 5 | #pragma clang loop vectorize(assume_safety)
34 ; CHECK-NEXT: 6 V4,2 | for (i = 0; i < 1600; i++) {
35 ; CHECK-NEXT: 7 | res[i] = (p[i] == 0) ? res[i] : res[i] + d[i];
36 ; CHECK-NEXT: 8 | }
37 ; CHECK-NEXT: 9 |
38 ; CHECK-NEXT: 10 U16 | for (i = 0; i < 16; i++) {
39 ; CHECK-NEXT: 11 | res[i] = (p[i] == 0) ? res[i] : res[i] + d[i];
40 ; CHECK-NEXT: 12 | }
41 ; CHECK-NEXT: 13 |
42 ; CHECK-NEXT: 14 I | foo();
43 ; CHECK-NEXT: 15 |
44 ; CHECK-NEXT: 16 | foo(); bar(); foo();
45 ; CHECK-NEXT: I | ^
46 ; CHECK-NEXT: I | ^
47 ; CHECK-NEXT: 17 | }
48 ; CHECK-NEXT: 18 |
49
50 ; CHECK-SUCCINCT: < {{.*}}/Inputs/or.c
51 CHECK-SUCCINCT-NEXT: 2 | void bar();
52 CHECK-SUCCINCT-NEXT: 3 | void foo() { bar(); }
53 CHECK-SUCCINCT-NEXT: 4 |
54 CHECK-SUCCINCT-NEXT: 5 | #include "or.h"
55 CHECK-SUCCINCT-NEXT: 6 |
56 CHECK-SUCCINCT-NEXT: 7 | void Test(int *res, int *c, int *d, int *p, int n) {
57 CHECK-SUCCINCT-NEXT: 8 | int i;
58 CHECK-SUCCINCT-NEXT: 9 |
59 CHECK-SUCCINCT-NEXT: 10 | #pragma clang loop vectorize(assume_safety)
60 CHECK-SUCCINCT-NEXT: 11 V | for (i = 0; i < 1600; i++) {
61 CHECK-SUCCINCT-NEXT: 12 | res[i] = (p[i] == 0) ? res[i] : res[i] + d[i];
62 CHECK-SUCCINCT-NEXT: 13 | }
63 CHECK-SUCCINCT-NEXT: 14 |
64 CHECK-SUCCINCT-NEXT: 15 U | for (i = 0; i < 16; i++) {
65 CHECK-SUCCINCT-NEXT: 16 | res[i] = (p[i] == 0) ? res[i] : res[i] + d[i];
66 CHECK-SUCCINCT-NEXT: 17 | }
67 CHECK-SUCCINCT-NEXT: 18 |
68 CHECK-SUCCINCT-NEXT: 19 I | foo();
69 CHECK-SUCCINCT-NEXT: 20 |
70 CHECK-SUCCINCT-NEXT: 21 | foo(); bar(); foo();
71 CHECK-SUCCINCT-NEXT: I | ^
72 CHECK-SUCCINCT-NEXT: I | ^
73 CHECK-SUCCINCT-NEXT: 22 | }
74 CHECK-SUCCINCT-NEXT: 23 |
75
76 CHECK-SUCCINCT: < {{.*}}/Inputs/or.h
77 CHECK-SUCCINCT-NEXT: 2 | void TestH(int *res, int *c, int *d, int *p, int n) {
78 CHECK-SUCCINCT-NEXT: 3 | int i;
79 CHECK-SUCCINCT-NEXT: 4 |
80 CHECK-SUCCINCT-NEXT: 5 | #pragma clang loop vectorize(assume_safety)
81 CHECK-SUCCINCT-NEXT: 6 V | for (i = 0; i < 1600; i++) {
82 CHECK-SUCCINCT-NEXT: 7 | res[i] = (p[i] == 0) ? res[i] : res[i] + d[i];
83 CHECK-SUCCINCT-NEXT: 8 | }
84 CHECK-SUCCINCT-NEXT: 9 |
85 CHECK-SUCCINCT-NEXT: 10 U | for (i = 0; i < 16; i++) {
86 CHECK-SUCCINCT-NEXT: 11 | res[i] = (p[i] == 0) ? res[i] : res[i] + d[i];
87 CHECK-SUCCINCT-NEXT: 12 | }
88 CHECK-SUCCINCT-NEXT: 13 |
89 CHECK-SUCCINCT-NEXT: 14 I | foo();
90 CHECK-SUCCINCT-NEXT: 15 |
91 CHECK-SUCCINCT-NEXT: 16 | foo(); bar(); foo();
92 CHECK-SUCCINCT-NEXT: I | ^
93 CHECK-SUCCINCT-NEXT: I | ^
94 CHECK-SUCCINCT-NEXT: 17 | }
95 CHECK-SUCCINCT-NEXT: 18 |
96
0 set(LLVM_LINK_COMPONENTS Core Object Support)
1
2 add_llvm_tool(llvm-opt-report
3 OptReport.cpp
4 )
5
0 //===------------------ llvm-opt-report/OptReport.cpp ---------------------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// \brief This file implements a tool that can parse the YAML optimization
11 /// records and generate an optimization summary annotated source listing
12 /// report.
13 ///
14 //===----------------------------------------------------------------------===//
15
16 #include "llvm/Support/CommandLine.h"
17 #include "llvm/Support/Error.h"
18 #include "llvm/Support/ErrorOr.h"
19 #include "llvm/Support/FileSystem.h"
20 #include "llvm/Support/Format.h"
21 #include "llvm/Support/LineIterator.h"
22 #include "llvm/Support/FileSystem.h"
23 #include "llvm/Support/MemoryBuffer.h"
24 #include "llvm/Support/Path.h"
25 #include "llvm/Support/Program.h"
26 #include "llvm/Support/raw_ostream.h"
27 #include "llvm/Support/Signals.h"
28 #include "llvm/Support/YAMLTraits.h"
29
30 using namespace llvm;
31 using namespace llvm::yaml;
32
33 static cl::opt Help("h", cl::desc("Alias for -help"), cl::Hidden);
34
35 // Mark all our options with this category, everything else (except for -version
36 // and -help) will be hidden.
37 static cl::OptionCategory
38 OptReportCategory("llvm-opt-report options");
39
40 static cl::opt
41 InputFileName(cl::Positional, cl::desc(""), cl::init("-"),
42 cl::cat(OptReportCategory));
43
44 static cl::opt
45 OutputFileName("o", cl::desc("Output file"), cl::init("-"),
46 cl::cat(OptReportCategory));
47
48 static cl::opt
49 InputRelDir("r", cl::desc("Root for relative input paths"), cl::init(""),
50 cl::cat(OptReportCategory));
51
52 static cl::opt
53 Succinct("s", cl::desc("Don't include vectorization factors, etc."),
54 cl::init(false), cl::cat(OptReportCategory));
55
56 namespace {
57 // For each location in the source file, the common per-transformation state
58 // collected.
59 struct OptReportLocationItemInfo {
60 bool Analyzed = false;
61 bool Transformed = false;
62
63 OptReportLocationItemInfo &operator |= (
64 const OptReportLocationItemInfo &RHS) {
65 Analyzed |= RHS.Analyzed;
66 Transformed |= RHS.Transformed;
67
68 return *this;
69 }
70 };
71
72 // The per-location information collected for producing an optimization report.
73 struct OptReportLocationInfo {
74 OptReportLocationItemInfo Inlined;
75 OptReportLocationItemInfo Unrolled;
76 OptReportLocationItemInfo Vectorized;
77
78 int VectorizationFactor = 1;
79 int InterleaveCount = 1;
80 int UnrollCount = 1;
81
82 OptReportLocationInfo &operator |= (const OptReportLocationInfo &RHS) {
83 Inlined |= RHS.Inlined;
84 Unrolled |= RHS.Unrolled;
85 Vectorized |= RHS.Vectorized;
86
87 VectorizationFactor =
88 std::max(VectorizationFactor, RHS.VectorizationFactor);
89 InterleaveCount = std::max(InterleaveCount, RHS.InterleaveCount);
90 UnrollCount = std::max(UnrollCount, RHS.UnrollCount);
91
92 return *this;
93 }
94 };
95
96 typedef std::map
97 OptReportLocationInfo>>> LocationInfoTy;
98 } // anonymous namespace
99
100 static void collectLocationInfo(yaml::Stream &Stream,
101 LocationInfoTy &LocationInfo) {
102 SmallVector Tmp;
103
104 // Note: We're using the YAML parser here directly, instead of using the
105 // YAMLTraits implementation, because the YAMLTraits implementation does not
106 // support a way to handle only a subset of the input keys (it will error out
107 // if there is an input key that you don't map to your class), and
108 // furthermore, it does not provide a way to handle the Args sequence of
109 // key/value pairs, where the order must be captured and the 'String' key
110 // might be repeated.
111 for (auto &Doc : Stream) {
112 auto *Root = dyn_cast(Doc.getRoot());
113 if (!Root)
114 continue;
115
116 bool Transformed = Root->getRawTag() == "!Passed";
117 std::string Pass, File;
118 int Line = 0, Column = 1;
119
120 int VectorizationFactor = 1;
121 int InterleaveCount = 1;
122 int UnrollCount = 1;
123
124 for (auto &RootChild : *Root) {
125 auto *Key = dyn_cast(RootChild.getKey());
126 if (!Key)
127 continue;
128 StringRef KeyName = Key->getValue(Tmp);
129 if (KeyName == "Pass") {
130 auto *Value = dyn_cast(RootChild.getValue());
131 if (!Value)
132 continue;
133 Pass = Value->getValue(Tmp);
134 } else if (KeyName == "DebugLoc") {
135 auto *DebugLoc = dyn_cast(RootChild.getValue());
136 if (!DebugLoc)
137 continue;
138
139 for (auto &DLChild : *DebugLoc) {
140 auto *DLKey = dyn_cast(DLChild.getKey());
141 if (!DLKey)
142 continue;
143 StringRef DLKeyName = DLKey->getValue(Tmp);
144 if (DLKeyName == "File") {
145 auto *Value = dyn_cast(DLChild.getValue());
146 if (!Value)
147 continue;
148 File = Value->getValue(Tmp);
149 } else if (DLKeyName == "Line") {
150 auto *Value = dyn_cast(DLChild.getValue());
151 if (!Value)
152 continue;
153 Value->getValue(Tmp).getAsInteger(10, Line);
154 } else if (DLKeyName == "Column") {
155 auto *Value = dyn_cast(DLChild.getValue());
156 if (!Value)
157 continue;
158 Value->getValue(Tmp).getAsInteger(10, Column);
159 }
160 }
161 } else if (KeyName == "Args") {
162 auto *Args = dyn_cast(RootChild.getValue());
163 if (!Args)
164 continue;
165 for (auto &ArgChild : *Args) {
166 auto *ArgMap = dyn_cast(&ArgChild);
167 if (!ArgMap)
168 continue;
169 for (auto &ArgKV : *ArgMap) {
170 auto *ArgKey = dyn_cast(ArgKV.getKey());
171 if (!ArgKey)
172 continue;
173 StringRef ArgKeyName = ArgKey->getValue(Tmp);
174 if (ArgKeyName == "VectorizationFactor") {
175 auto *Value = dyn_cast(ArgKV.getValue());
176 if (!Value)
177 continue;
178 Value->getValue(Tmp).getAsInteger(10, VectorizationFactor);
179 } else if (ArgKeyName == "InterleaveCount") {
180 auto *Value = dyn_cast(ArgKV.getValue());
181 if (!Value)
182 continue;
183 Value->getValue(Tmp).getAsInteger(10, InterleaveCount);
184 } else if (ArgKeyName == "UnrollCount") {
185 auto *Value = dyn_cast(ArgKV.getValue());
186 if (!Value)
187 continue;
188 Value->getValue(Tmp).getAsInteger(10, UnrollCount);
189 }
190 }
191 }
192 }
193 }
194
195 if (Line < 1 || File.empty())
196 continue;
197
198 // We track information on both actual and potential transformations. This
199 // way, if there are multiple possible things on a line that are, or could
200 // have been transformed, we can indicate that explicitly in the output.
201 auto UpdateLLII = [Transformed, VectorizationFactor,
202 InterleaveCount,
203 UnrollCount](OptReportLocationInfo &LI,
204 OptReportLocationItemInfo &LLII) {
205 LLII.Analyzed = true;
206 if (Transformed) {
207 LLII.Transformed = true;
208
209 LI.VectorizationFactor = VectorizationFactor;
210 LI.InterleaveCount = InterleaveCount;
211 LI.UnrollCount = UnrollCount;
212 }
213 };
214
215 if (Pass == "inline") {
216 auto &LI = LocationInfo[File][Line][Column];
217 UpdateLLII(LI, LI.Inlined);
218 } else if (Pass == "loop-unroll") {
219 auto &LI = LocationInfo[File][Line][Column];
220 UpdateLLII(LI, LI.Unrolled);
221 } else if (Pass == "loop-vectorize") {
222 auto &LI = LocationInfo[File][Line][Column];
223 UpdateLLII(LI, LI.Vectorized);
224 }
225 }
226 }
227
228 static bool readLocationInfo(LocationInfoTy &LocationInfo) {
229 ErrorOr> Buf =
230 MemoryBuffer::getFileOrSTDIN(InputFileName);
231 if (std::error_code EC = Buf.getError()) {
232 errs() << "error: Can't open file " << InputFileName << ": " <<
233 EC.message() << "\n";
234 return false;
235 }
236
237 SourceMgr SM;
238 yaml::Stream Stream(Buf.get()->getBuffer(), SM);
239 collectLocationInfo(Stream, LocationInfo);
240
241 return true;
242 }
243
244 static bool writeReport(LocationInfoTy &LocationInfo) {
245 std::error_code EC;
246 llvm::raw_fd_ostream OS(OutputFileName, EC,
247 llvm::sys::fs::F_Text);
248 if (EC) {
249 errs() << "error: Can't open file " << OutputFileName << ": " <<
250 EC.message() << "\n";
251 return false;
252 }
253
254 bool FirstFile = true;
255 for (auto &FI : LocationInfo) {
256 SmallString<128> FileName(FI.first);
257 if (!InputRelDir.empty()) {
258 if (std::error_code EC = sys::fs::make_absolute(InputRelDir, FileName)) {
259 errs() << "error: Can't resolve file path to " << FileName << ": " <<
260 EC.message() << "\n";
261 return false;
262 }
263 }
264
265 const auto &FileInfo = FI.second;
266
267 ErrorOr> Buf =
268 MemoryBuffer::getFile(FileName);
269 if (std::error_code EC = Buf.getError()) {
270 errs() << "error: Can't open file " << FileName << ": " <<
271 EC.message() << "\n";
272 return false;
273 }
274
275 if (FirstFile)
276 FirstFile = false;
277 else
278 OS << "\n";
279
280 OS << "< " << FileName << "\n";
281
282 // Figure out how many characters we need for the vectorization factors
283 // and similar.
284 OptReportLocationInfo MaxLI;
285 for (auto &FI : FileInfo)
286 for (auto &LI : FI.second)
287 MaxLI |= LI.second;
288
289 unsigned VFDigits = llvm::utostr(MaxLI.VectorizationFactor).size();
290 unsigned ICDigits = llvm::utostr(MaxLI.InterleaveCount).size();
291 unsigned UCDigits = llvm::utostr(MaxLI.UnrollCount).size();
292
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)
296 ++NumLines;
297
298 unsigned LNDigits = llvm::utostr(NumLines).size();
299
300 for (line_iterator LI(*Buf.get(), false); LI != line_iterator(); ++LI) {
301 int64_t L = LI.line_number();
302 OptReportLocationInfo LLI;
303
304 std::map ColsInfo;
305 unsigned InlinedCols = 0, UnrolledCols = 0, VectorizedCols = 0;
306
307 auto LII = FileInfo.find(L);
308 if (LII != FileInfo.end()) {
309 const auto &LineInfo = LII->second;
310
311 for (auto &CI : LineInfo) {
312 int Col = CI.first;
313 ColsInfo[Col] = CI.second;
314 InlinedCols += CI.second.Inlined.Analyzed;
315 UnrolledCols += CI.second.Unrolled.Analyzed;
316 VectorizedCols += CI.second.Vectorized.Analyzed;
317 LLI |= CI.second;
318 }
319 }
320
321 // We try to keep the output as concise as possible. If only one thing on
322 // a given line could have been inlined, vectorized, etc. then we can put
323 // the marker on the source line itself. If there are multiple options
324 // then we want to distinguish them by placing the marker for each
325 // transformation on a separate line following the source line. When we
326 // do this, we use a '^' character to point to the appropriate column in
327 // the source line.
328
329 std::string USpaces(Succinct ? 0 : UCDigits, ' ');
330 std::string VSpaces(Succinct ? 0 : VFDigits + ICDigits + 1, ' ');
331
332 auto UStr = [UCDigits](OptReportLocationInfo &LLI) {
333 std::string R;
334 raw_string_ostream RS(R);
335 if (!Succinct)
336 RS << llvm::format_decimal(LLI.UnrollCount, UCDigits);
337 return RS.str();
338 };
339
340 auto VStr = [VFDigits,
341 ICDigits](OptReportLocationInfo &LLI) -> std::string {
342 std::string R;
343 raw_string_ostream RS(R);
344 if (!Succinct)
345 RS << llvm::format_decimal(LLI.VectorizationFactor, VFDigits) <<
346 "," << llvm::format_decimal(LLI.InterleaveCount, ICDigits);
347 return RS.str();
348 };
349
350 OS << llvm::format_decimal(L + 1, LNDigits) << " ";
351 OS << (LLI.Inlined.Transformed && InlinedCols < 2 ? "I" : " ");
352 OS << (LLI.Unrolled.Transformed && UnrolledCols < 2 ?
353 "U" + UStr(LLI) : " " + USpaces);
354 OS << (LLI.Vectorized.Transformed && VectorizedCols < 2 ?
355 "V" + VStr(LLI) : " " + VSpaces);
356
357 OS << " | " << *LI << "\n";
358
359 for (auto &J : ColsInfo) {
360 if ((J.second.Inlined.Transformed && InlinedCols > 1) ||
361 (J.second.Unrolled.Transformed && UnrolledCols > 1) ||
362 (J.second.Vectorized.Transformed && VectorizedCols > 1)) {
363 OS << std::string(LNDigits + 1, ' ');
364 OS << (J.second.Inlined.Transformed &&
365 InlinedCols > 1 ? "I" : " ");
366 OS << (J.second.Unrolled.Transformed &&
367 UnrolledCols > 1 ? "U" + UStr(J.second) : " " + USpaces);
368 OS << (J.second.Vectorized.Transformed &&
369 VectorizedCols > 1 ? "V" + VStr(J.second) : " " + VSpaces);
370
371 OS << " | " << std::string(J.first - 1, ' ') << "^\n";
372 }
373 }
374 }
375 }
376
377 return true;
378 }
379
380 int main(int argc, const char **argv) {
381 sys::PrintStackTraceOnErrorSignal(argv[0]);
382
383 cl::HideUnrelatedOptions(OptReportCategory);
384 cl::ParseCommandLineOptions(
385 argc, argv,
386 "A tool to generate an optimization report from YAML optimization"
387 " record files.\n");
388
389 if (Help)
390 cl::PrintHelpMessage();
391
392 LocationInfoTy LocationInfo;
393 if (!readLocationInfo(LocationInfo))
394 return 1;
395 if (!writeReport(LocationInfo))
396 return 1;
397
398 return 0;
399 }
400