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