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