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