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