253 lines · cpp
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-exception6//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 14using namespace mlir;15using namespace mlir::detail;16 17constexpr StringLiteral kPassStatsDescription =18 "... Pass statistics report ...";19 20namespace {21/// Information pertaining to a specific statistic.22struct Statistic {23 const char *name, *desc;24 uint64_t value;25};26} // namespace27 28/// Utility to print a pass entry in the statistics output.29static void printPassEntry(raw_ostream &os, unsigned indent, StringRef pass,30 MutableArrayRef<Statistic> stats = {}) {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(37 stats.begin(), stats.end(), [](const auto *lhs, const auto *rhs) {38 return StringRef{lhs->name}.compare(StringRef{rhs->name});39 });40 41 // Collect the largest name and value length from each of the statistics.42 size_t largestName = 0, largestValue = 0;43 for (auto &stat : stats) {44 largestName = std::max(largestName, (size_t)strlen(stat.name));45 largestValue =46 std::max(largestValue, (size_t)llvm::utostr(stat.value).size());47 }48 49 // Print each of the statistics.50 for (auto &stat : stats) {51 os.indent(indent + 2) << llvm::format("(S) %*u %-*s - %s\n", largestValue,52 stat.value, largestName, stat.name,53 stat.desc);54 }55}56 57/// Print the statistics results in a list form, where each pass is sorted by58/// name.59static void printResultsAsList(raw_ostream &os, OpPassManager &pm) {60 llvm::StringMap<std::vector<Statistic>> mergedStats;61 std::function<void(Pass *)> addStats = [&](Pass *pass) {62 auto *adaptor = dyn_cast<OpToOpPassAdaptor>(pass);63 64 // If this is not an adaptor, add the stats to the list if there are any.65 if (!adaptor) {66#if LLVM_ENABLE_STATS67 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 [idx, statistic] : llvm::enumerate(pass->getStatistics()))77 passEntry[idx].value += statistic->getValue();78 }79#endif80 return;81 }82 83 // Otherwise, recursively add each of the children.84 for (auto &mgr : adaptor->getPassManagers())85 for (Pass &pass : mgr.getPasses())86 addStats(&pass);87 };88 for (Pass &pass : pm.getPasses())89 addStats(&pass);90 91 // Sort the statistics by pass name and then by record name.92 auto passAndStatistics =93 llvm::to_vector<16>(llvm::make_pointer_range(mergedStats));94 llvm::array_pod_sort(passAndStatistics.begin(), passAndStatistics.end(),95 [](const decltype(passAndStatistics)::value_type *lhs,96 const decltype(passAndStatistics)::value_type *rhs) {97 return (*lhs)->getKey().compare((*rhs)->getKey());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 manager106/// structure.107static void printResultsAsPipeline(raw_ostream &os, OpPassManager &pm) {108#if LLVM_ENABLE_STATS109 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 for113 // 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.getOpAnchorName() + "' 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#endif139}140 141static 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// PassStatistics166//===----------------------------------------------------------------------===//167 168Pass::Statistic::Statistic(Pass *owner, const char *name,169 const char *description)170 : llvm::Statistic{/*DebugType=*/"", name, description} {171#if LLVM_ENABLE_STATS172 // Always set the 'initialized' bit to true so that this statistic isn't173 // placed in the static registry.174 // TODO: This is sort of hack as `llvm::Statistic`s can't be setup to avoid175 // automatic registration with the global registry. We should either add176 // support for this in LLVM, or just write our own statistics classes.177 Initialized = true;178#endif179 180 // Register this statistic with the parent.181 owner->statistics.push_back(this);182}183 184auto Pass::Statistic::operator=(unsigned value) -> Statistic & {185 llvm::Statistic::operator=(value);186 return *this;187}188 189//===----------------------------------------------------------------------===//190// PassManager191//===----------------------------------------------------------------------===//192 193/// Merge the pass statistics of this class into 'other'.194void 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 for220/// consumption(e.g. dumping).221static 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 nested229 // pass managers. Prepare recursively before merging.230 for (auto &asyncPM : adaptor->getParallelPassManagers()) {231 for (unsigned i = 0, e = asyncPM.size(); i != e; ++i) {232 prepareStatistics(asyncPM[i]);233 asyncPM[i].mergeStatisticsInto(nestedPms[i]);234 }235 }236 237 // Prepare the statistics of each of the nested passes.238 for (OpPassManager &nestedPM : nestedPms)239 prepareStatistics(nestedPM);240 }241}242 243/// Dump the statistics of the passes within this pass manager.244void PassManager::dumpStatistics() {245 prepareStatistics(*this);246 printStatistics(*this, *passStatisticsMode);247}248 249/// Dump the statistics for each pass after running.250void PassManager::enableStatistics(PassDisplayMode displayMode) {251 passStatisticsMode = displayMode;252}253