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