1 | //===- PassStatistics.cpp -------------------------------------------------===// |
2 | // |
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 |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "PassDetail.h" |
10 | #include "mlir/Pass/PassManager.h" |
11 | #include "llvm/ADT/StringExtras.h" |
12 | #include "llvm/Support/Format.h" |
13 | |
14 | using namespace mlir; |
15 | using namespace mlir::detail; |
16 | |
17 | constexpr StringLiteral kPassStatsDescription = |
18 | "... Pass statistics report ..." ; |
19 | |
20 | namespace { |
21 | /// Information pertaining to a specific statistic. |
22 | struct Statistic { |
23 | const char *name, *desc; |
24 | uint64_t value; |
25 | }; |
26 | } // namespace |
27 | |
28 | /// Utility to print a pass entry in the statistics output. |
29 | static void printPassEntry(raw_ostream &os, unsigned indent, StringRef pass, |
30 | MutableArrayRef<Statistic> stats = std::nullopt) { |
31 | os.indent(NumSpaces: indent) << pass << "\n" ; |
32 | if (stats.empty()) |
33 | return; |
34 | |
35 | // Make sure to sort the statistics by name. |
36 | llvm::array_pod_sort( |
37 | Start: stats.begin(), End: stats.end(), Compare: [](const auto *lhs, const auto *rhs) { |
38 | return StringRef{lhs->name}.compare(RHS: StringRef{rhs->name}); |
39 | }); |
40 | |
41 | // Collect the largest name and value length from each of the statistics. |
42 | size_t largestName = 0, largestValue = 0; |
43 | for (auto &stat : stats) { |
44 | largestName = std::max(a: largestName, b: (size_t)strlen(s: stat.name)); |
45 | largestValue = |
46 | std::max(a: largestValue, b: (size_t)llvm::utostr(X: stat.value).size()); |
47 | } |
48 | |
49 | // Print each of the statistics. |
50 | for (auto &stat : stats) { |
51 | os.indent(NumSpaces: indent + 2) << llvm::format(Fmt: "(S) %*u %-*s - %s\n" , Vals: largestValue, |
52 | Vals: stat.value, Vals: largestName, Vals: stat.name, |
53 | Vals: stat.desc); |
54 | } |
55 | } |
56 | |
57 | /// Print the statistics results in a list form, where each pass is sorted by |
58 | /// name. |
59 | static void printResultsAsList(raw_ostream &os, OpPassManager &pm) { |
60 | llvm::StringMap<std::vector<Statistic>> mergedStats; |
61 | std::function<void(Pass *)> addStats = [&](Pass *pass) { |
62 | auto *adaptor = dyn_cast<OpToOpPassAdaptor>(Val: pass); |
63 | |
64 | // If this is not an adaptor, add the stats to the list if there are any. |
65 | if (!adaptor) { |
66 | #if LLVM_ENABLE_STATS |
67 | auto statistics = pass->getStatistics(); |
68 | if (statistics.empty()) |
69 | return; |
70 | |
71 | auto &passEntry = mergedStats[pass->getName()]; |
72 | if (passEntry.empty()) { |
73 | for (Pass::Statistic *it : pass->getStatistics()) |
74 | passEntry.push_back(x: {.name: it->getName(), .desc: it->getDesc(), .value: it->getValue()}); |
75 | } else { |
76 | for (auto [idx, statistic] : llvm::enumerate(First: pass->getStatistics())) |
77 | passEntry[idx].value += statistic->getValue(); |
78 | } |
79 | #endif |
80 | return; |
81 | } |
82 | |
83 | // Otherwise, recursively add each of the children. |
84 | for (auto &mgr : adaptor->getPassManagers()) |
85 | for (Pass &pass : mgr.getPasses()) |
86 | addStats(&pass); |
87 | }; |
88 | for (Pass &pass : pm.getPasses()) |
89 | addStats(&pass); |
90 | |
91 | // Sort the statistics by pass name and then by record name. |
92 | auto passAndStatistics = |
93 | llvm::to_vector<16>(Range: llvm::make_pointer_range(Range&: mergedStats)); |
94 | llvm::array_pod_sort(Start: passAndStatistics.begin(), End: passAndStatistics.end(), |
95 | Compare: [](const decltype(passAndStatistics)::value_type *lhs, |
96 | const decltype(passAndStatistics)::value_type *rhs) { |
97 | return (*lhs)->getKey().compare(RHS: (*rhs)->getKey()); |
98 | }); |
99 | |
100 | // Print the timing information sequentially. |
101 | for (auto &statData : passAndStatistics) |
102 | printPassEntry(os, /*indent=*/2, pass: statData->first(), stats: statData->second); |
103 | } |
104 | |
105 | /// Print the results in pipeline mode that mirrors the internal pass manager |
106 | /// structure. |
107 | static void printResultsAsPipeline(raw_ostream &os, OpPassManager &pm) { |
108 | #if LLVM_ENABLE_STATS |
109 | std::function<void(unsigned, Pass *)> printPass = [&](unsigned indent, |
110 | Pass *pass) { |
111 | if (auto *adaptor = dyn_cast<OpToOpPassAdaptor>(Val: pass)) { |
112 | // If this adaptor has more than one internal pipeline, print an entry for |
113 | // it. |
114 | auto mgrs = adaptor->getPassManagers(); |
115 | if (mgrs.size() > 1) { |
116 | printPassEntry(os, indent, pass: adaptor->getAdaptorName()); |
117 | indent += 2; |
118 | } |
119 | |
120 | // Print each of the children passes. |
121 | for (OpPassManager &mgr : mgrs) { |
122 | auto name = ("'" + mgr.getOpAnchorName() + "' Pipeline" ).str(); |
123 | printPassEntry(os, indent, pass: name); |
124 | for (Pass &pass : mgr.getPasses()) |
125 | printPass(indent + 2, &pass); |
126 | } |
127 | return; |
128 | } |
129 | |
130 | // Otherwise, we print the statistics for this pass. |
131 | std::vector<Statistic> stats; |
132 | for (Pass::Statistic *stat : pass->getStatistics()) |
133 | stats.push_back(x: {.name: stat->getName(), .desc: stat->getDesc(), .value: stat->getValue()}); |
134 | printPassEntry(os, indent, pass: pass->getName(), stats); |
135 | }; |
136 | for (Pass &pass : pm.getPasses()) |
137 | printPass(/*indent=*/0, &pass); |
138 | #endif |
139 | } |
140 | |
141 | static void printStatistics(OpPassManager &pm, PassDisplayMode displayMode) { |
142 | auto os = llvm::CreateInfoOutputFile(); |
143 | |
144 | // Print the stats header. |
145 | *os << "===" << std::string(73, '-') << "===\n" ; |
146 | // Figure out how many spaces for the description name. |
147 | unsigned padding = (80 - kPassStatsDescription.size()) / 2; |
148 | os->indent(NumSpaces: padding) << kPassStatsDescription << '\n'; |
149 | *os << "===" << std::string(73, '-') << "===\n" ; |
150 | |
151 | // Defer to a specialized printer for each display mode. |
152 | switch (displayMode) { |
153 | case PassDisplayMode::List: |
154 | printResultsAsList(os&: *os, pm); |
155 | break; |
156 | case PassDisplayMode::Pipeline: |
157 | printResultsAsPipeline(os&: *os, pm); |
158 | break; |
159 | } |
160 | *os << "\n" ; |
161 | os->flush(); |
162 | } |
163 | |
164 | //===----------------------------------------------------------------------===// |
165 | // PassStatistics |
166 | //===----------------------------------------------------------------------===// |
167 | |
168 | Pass::Statistic::Statistic(Pass *owner, const char *name, |
169 | const char *description) |
170 | : llvm::Statistic{/*DebugType=*/"" , name, description} { |
171 | #if LLVM_ENABLE_STATS |
172 | // Always set the 'initialized' bit to true so that this statistic isn't |
173 | // placed in the static registry. |
174 | // TODO: This is sort of hack as `llvm::Statistic`s can't be setup to avoid |
175 | // automatic registration with the global registry. We should either add |
176 | // support for this in LLVM, or just write our own statistics classes. |
177 | Initialized = true; |
178 | #endif |
179 | |
180 | // Register this statistic with the parent. |
181 | owner->statistics.push_back(x: this); |
182 | } |
183 | |
184 | auto Pass::Statistic::operator=(unsigned value) -> Statistic & { |
185 | llvm::Statistic::operator=(Val: value); |
186 | return *this; |
187 | } |
188 | |
189 | //===----------------------------------------------------------------------===// |
190 | // PassManager |
191 | //===----------------------------------------------------------------------===// |
192 | |
193 | /// Merge the pass statistics of this class into 'other'. |
194 | void OpPassManager::mergeStatisticsInto(OpPassManager &other) { |
195 | auto passes = getPasses(), otherPasses = other.getPasses(); |
196 | |
197 | for (auto passPair : llvm::zip(t&: passes, u&: otherPasses)) { |
198 | Pass &pass = std::get<0>(t&: passPair), &otherPass = std::get<1>(t&: passPair); |
199 | |
200 | // If this is an adaptor, then recursively merge the pass managers. |
201 | if (auto *adaptorPass = dyn_cast<OpToOpPassAdaptor>(Val: &pass)) { |
202 | auto *otherAdaptorPass = cast<OpToOpPassAdaptor>(Val: &otherPass); |
203 | for (auto mgrs : llvm::zip(t: adaptorPass->getPassManagers(), |
204 | u: otherAdaptorPass->getPassManagers())) |
205 | std::get<0>(t&: mgrs).mergeStatisticsInto(other&: std::get<1>(t&: mgrs)); |
206 | continue; |
207 | } |
208 | // Otherwise, merge the statistics for the current pass. |
209 | assert(pass.statistics.size() == otherPass.statistics.size()); |
210 | for (unsigned i = 0, e = pass.statistics.size(); i != e; ++i) { |
211 | assert(pass.statistics[i]->getName() == |
212 | StringRef(otherPass.statistics[i]->getName())); |
213 | *otherPass.statistics[i] += *pass.statistics[i]; |
214 | *pass.statistics[i] = 0; |
215 | } |
216 | } |
217 | } |
218 | |
219 | /// Prepare the statistics of passes within the given pass manager for |
220 | /// consumption(e.g. dumping). |
221 | static void prepareStatistics(OpPassManager &pm) { |
222 | for (Pass &pass : pm.getPasses()) { |
223 | OpToOpPassAdaptor *adaptor = dyn_cast<OpToOpPassAdaptor>(Val: &pass); |
224 | if (!adaptor) |
225 | continue; |
226 | MutableArrayRef<OpPassManager> nestedPms = adaptor->getPassManagers(); |
227 | |
228 | // Merge the statistics from the async pass managers into the main nested |
229 | // pass managers. Prepare recursively before merging. |
230 | for (auto &asyncPM : adaptor->getParallelPassManagers()) { |
231 | for (unsigned i = 0, e = asyncPM.size(); i != e; ++i) { |
232 | prepareStatistics(pm&: asyncPM[i]); |
233 | asyncPM[i].mergeStatisticsInto(other&: nestedPms[i]); |
234 | } |
235 | } |
236 | |
237 | // Prepare the statistics of each of the nested passes. |
238 | for (OpPassManager &nestedPM : nestedPms) |
239 | prepareStatistics(pm&: nestedPM); |
240 | } |
241 | } |
242 | |
243 | /// Dump the statistics of the passes within this pass manager. |
244 | void PassManager::dumpStatistics() { |
245 | prepareStatistics(pm&: *this); |
246 | printStatistics(pm&: *this, displayMode: *passStatisticsMode); |
247 | } |
248 | |
249 | /// Dump the statistics for each pass after running. |
250 | void PassManager::enableStatistics(PassDisplayMode displayMode) { |
251 | passStatisticsMode = displayMode; |
252 | } |
253 | |