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. 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 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 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().getStringRef() + "' 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 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 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 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'. 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). 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. 238 void PassManager::dumpStatistics() { 239 prepareStatistics(*this); 240 printStatistics(*this, *passStatisticsMode); 241 } 242 243 /// Dump the statistics for each pass after running. 244 void PassManager::enableStatistics(PassDisplayMode displayMode) { 245 passStatisticsMode = displayMode; 246 } 247