blob: 425aeae35e059cc02b5345c3870ed9e7a4d8d351 [file] [log] [blame]
//===- PassStatistics.cpp -------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "PassDetail.h"
#include "mlir/Pass/PassManager.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Format.h"
using namespace mlir;
using namespace mlir::detail;
constexpr StringLiteral kPassStatsDescription =
"... Pass statistics report ...";
namespace {
/// Information pertaining to a specific statistic.
struct Statistic {
const char *name, *desc;
unsigned value;
};
} // end anonymous namespace
/// Utility to print a pass entry in the statistics output.
static void printPassEntry(raw_ostream &os, unsigned indent, StringRef pass,
MutableArrayRef<Statistic> stats = llvm::None) {
os.indent(indent) << pass << "\n";
if (stats.empty())
return;
// Make sure to sort the statistics by name.
llvm::array_pod_sort(stats.begin(), stats.end(),
[](const auto *lhs, const auto *rhs) {
return llvm::array_pod_sort_comparator<const char *>(
&lhs->name, &rhs->name);
});
// Collect the largest name and value length from each of the statistics.
size_t largestName = 0, largestValue = 0;
for (auto &stat : stats) {
largestName = std::max(largestName, (size_t)strlen(stat.name));
largestValue =
std::max(largestValue, (size_t)llvm::utostr(stat.value).size());
}
// Print each of the statistics.
for (auto &stat : stats) {
os.indent(indent + 2) << llvm::format("(S) %*u %-*s - %s\n", largestValue,
stat.value, largestName, stat.name,
stat.desc);
}
}
/// Print the statistics results in a list form, where each pass is sorted by
/// name.
static void printResultsAsList(raw_ostream &os, OpPassManager &pm) {
llvm::StringMap<std::vector<Statistic>> mergedStats;
std::function<void(Pass *)> addStats = [&](Pass *pass) {
auto *adaptor = dyn_cast<OpToOpPassAdaptor>(pass);
// If this is not an adaptor, add the stats to the list if there are any.
if (!adaptor) {
#if LLVM_ENABLE_STATS
auto statistics = pass->getStatistics();
if (statistics.empty())
return;
auto &passEntry = mergedStats[pass->getName()];
if (passEntry.empty()) {
for (Pass::Statistic *it : pass->getStatistics())
passEntry.push_back({it->getName(), it->getDesc(), it->getValue()});
} else {
for (auto &it : llvm::enumerate(pass->getStatistics()))
passEntry[it.index()].value += it.value()->getValue();
}
#endif
return;
}
// Otherwise, recursively add each of the children.
for (auto &mgr : adaptor->getPassManagers())
for (Pass &pass : mgr.getPasses())
addStats(&pass);
};
for (Pass &pass : pm.getPasses())
addStats(&pass);
// Sort the statistics by pass name and then by record name.
std::vector<std::pair<StringRef, std::vector<Statistic>>> passAndStatistics;
for (auto &passIt : mergedStats)
passAndStatistics.push_back({passIt.first(), std::move(passIt.second)});
llvm::sort(passAndStatistics, [](const auto &lhs, const auto &rhs) {
return lhs.first.compare(rhs.first) < 0;
});
// Print the timing information sequentially.
for (auto &statData : passAndStatistics)
printPassEntry(os, /*indent=*/2, statData.first, statData.second);
}
/// Print the results in pipeline mode that mirrors the internal pass manager
/// structure.
static void printResultsAsPipeline(raw_ostream &os, OpPassManager &pm) {
#if LLVM_ENABLE_STATS
std::function<void(unsigned, Pass *)> printPass = [&](unsigned indent,
Pass *pass) {
if (auto *adaptor = dyn_cast<OpToOpPassAdaptor>(pass)) {
// If this adaptor has more than one internal pipeline, print an entry for
// it.
auto mgrs = adaptor->getPassManagers();
if (mgrs.size() > 1) {
printPassEntry(os, indent, adaptor->getAdaptorName());
indent += 2;
}
// Print each of the children passes.
for (OpPassManager &mgr : mgrs) {
auto name = ("'" + mgr.getOpName() + "' Pipeline").str();
printPassEntry(os, indent, name);
for (Pass &pass : mgr.getPasses())
printPass(indent + 2, &pass);
}
return;
}
// Otherwise, we print the statistics for this pass.
std::vector<Statistic> stats;
for (Pass::Statistic *stat : pass->getStatistics())
stats.push_back({stat->getName(), stat->getDesc(), stat->getValue()});
printPassEntry(os, indent, pass->getName(), stats);
};
for (Pass &pass : pm.getPasses())
printPass(/*indent=*/0, &pass);
#endif
}
static void printStatistics(OpPassManager &pm, PassDisplayMode displayMode) {
auto os = llvm::CreateInfoOutputFile();
// Print the stats header.
*os << "===" << std::string(73, '-') << "===\n";
// Figure out how many spaces for the description name.
unsigned padding = (80 - kPassStatsDescription.size()) / 2;
os->indent(padding) << kPassStatsDescription << '\n';
*os << "===" << std::string(73, '-') << "===\n";
// Defer to a specialized printer for each display mode.
switch (displayMode) {
case PassDisplayMode::List:
printResultsAsList(*os, pm);
break;
case PassDisplayMode::Pipeline:
printResultsAsPipeline(*os, pm);
break;
}
*os << "\n";
os->flush();
}
//===----------------------------------------------------------------------===//
// PassStatistics
//===----------------------------------------------------------------------===//
Pass::Statistic::Statistic(Pass *owner, const char *name,
const char *description)
: llvm::Statistic{/*DebugType=*/"", name, description} {
#if LLVM_ENABLE_STATS
// Always set the 'initialized' bit to true so that this statistic isn't
// placed in the static registry.
// TODO: This is sort of hack as `llvm::Statistic`s can't be setup to avoid
// automatic registration with the global registry. We should either add
// support for this in LLVM, or just write our own statistics classes.
Initialized = true;
#endif
// Register this statistic with the parent.
owner->statistics.push_back(this);
}
auto Pass::Statistic::operator=(unsigned value) -> Statistic & {
llvm::Statistic::operator=(value);
return *this;
}
//===----------------------------------------------------------------------===//
// PassManager
//===----------------------------------------------------------------------===//
/// Merge the pass statistics of this class into 'other'.
void OpPassManager::mergeStatisticsInto(OpPassManager &other) {
auto passes = getPasses(), otherPasses = other.getPasses();
for (auto passPair : llvm::zip(passes, otherPasses)) {
Pass &pass = std::get<0>(passPair), &otherPass = std::get<1>(passPair);
// If this is an adaptor, then recursively merge the pass managers.
if (auto *adaptorPass = dyn_cast<OpToOpPassAdaptor>(&pass)) {
auto *otherAdaptorPass = cast<OpToOpPassAdaptor>(&otherPass);
for (auto mgrs : llvm::zip(adaptorPass->getPassManagers(),
otherAdaptorPass->getPassManagers()))
std::get<0>(mgrs).mergeStatisticsInto(std::get<1>(mgrs));
continue;
}
// Otherwise, merge the statistics for the current pass.
assert(pass.statistics.size() == otherPass.statistics.size());
for (unsigned i = 0, e = pass.statistics.size(); i != e; ++i) {
assert(pass.statistics[i]->getName() ==
StringRef(otherPass.statistics[i]->getName()));
*otherPass.statistics[i] += *pass.statistics[i];
*pass.statistics[i] = 0;
}
}
}
/// Prepare the statistics of passes within the given pass manager for
/// consumption(e.g. dumping).
static void prepareStatistics(OpPassManager &pm) {
for (Pass &pass : pm.getPasses()) {
OpToOpPassAdaptor *adaptor = dyn_cast<OpToOpPassAdaptor>(&pass);
if (!adaptor)
continue;
MutableArrayRef<OpPassManager> nestedPms = adaptor->getPassManagers();
// Merge the statistics from the async pass managers into the main nested
// pass managers.
for (auto &asyncPM : adaptor->getParallelPassManagers()) {
for (unsigned i = 0, e = asyncPM.size(); i != e; ++i)
asyncPM[i].mergeStatisticsInto(nestedPms[i]);
}
// Prepare the statistics of each of the nested passes.
for (OpPassManager &nestedPM : nestedPms)
prepareStatistics(nestedPM);
}
}
/// Dump the statistics of the passes within this pass manager.
void PassManager::dumpStatistics() {
prepareStatistics(*this);
printStatistics(*this, *passStatisticsMode);
}
/// Dump the statistics for each pass after running.
void PassManager::enableStatistics(PassDisplayMode displayMode) {
passStatisticsMode = displayMode;
}