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