forked from OSchip/llvm-project
Add support for instance specific pass statistics.
Statistics are a way to keep track of what the compiler is doing and how effective various optimizations are. It is useful to see what optimizations are contributing to making a particular program run faster. Pass-instance specific statistics take this even further as you can see the effect of placing a particular pass at specific places within the pass pipeline, e.g. they could help answer questions like "what happens if I run CSE again here". Statistics can be added to a pass by simply adding members of type 'Pass::Statistics'. This class takes as a constructor arguments: the parent pass pointer, a name, and a description. Statistics can be dumped by the pass manager in a similar manner to how pass timing information is dumped, i.e. via PassManager::enableStatistics programmatically; or -pass-statistics and -pass-statistics-display via the command line pass manager options. Below is an example: struct MyPass : public OperationPass<MyPass> { Statistic testStat{this, "testStat", "A test statistic"}; void runOnOperation() { ... ++testStat; ... } }; $ mlir-opt -pass-pipeline='func(my-pass,my-pass)' foo.mlir -pass-statistics Pipeline Display: ===-------------------------------------------------------------------------=== ... Pass statistics report ... ===-------------------------------------------------------------------------=== 'func' Pipeline MyPass (S) 15 testStat - A test statistic MyPass (S) 6 testStat - A test statistic List Display: ===-------------------------------------------------------------------------=== ... Pass statistics report ... ===-------------------------------------------------------------------------=== MyPass (S) 21 testStat - A test statistic PiperOrigin-RevId: 284022014
This commit is contained in:
parent
4d61a79db4
commit
33a64540ad
|
@ -319,10 +319,10 @@ program has been run through the passes. This provides several benefits:
|
|||
|
||||
## Pass Registration
|
||||
|
||||
Briefly shown in the example definitions of the various
|
||||
pass types is the `PassRegistration` class. This is a utility to
|
||||
register derived pass classes so that they may be created, and inspected, by
|
||||
utilities like mlir-opt. Registering a pass class takes the form:
|
||||
Briefly shown in the example definitions of the various pass types is the
|
||||
`PassRegistration` class. This is a utility to register derived pass classes so
|
||||
that they may be created, and inspected, by utilities like mlir-opt. Registering
|
||||
a pass class takes the form:
|
||||
|
||||
```c++
|
||||
static PassRegistration<MyPass> pass("command-line-arg", "description");
|
||||
|
@ -469,6 +469,76 @@ struct MyPassOptions : public PassOptions<MyPassOptions> {
|
|||
static PassRegistration<MyPass, MyPassOptions> pass("my-pass", "description");
|
||||
```
|
||||
|
||||
## Pass Statistics
|
||||
|
||||
Statistics are a way to keep track of what the compiler is doing and how
|
||||
effective various transformations are. It is often useful to see what effect
|
||||
specific transformations have on a particular program, and how often they
|
||||
trigger. Pass statistics are instance specific which allow for taking this a
|
||||
step further as you are able to see the effect of placing a particular
|
||||
transformation at specific places within the pass pipeline. For example, they
|
||||
help answer questions like `What happens if I run CSE again here?`.
|
||||
|
||||
Statistics can be added to a pass by using the 'Pass::Statistic' class. This
|
||||
class takes as a constructor arguments: the parent pass, a name, and a
|
||||
description. This class acts like an unsigned integer, and may be incremented
|
||||
and updated accordingly. These statistics use the same infrastructure as
|
||||
[`llvm::Statistic`](http://llvm.org/docs/ProgrammersManual.html#the-statistic-class-stats-option)
|
||||
and thus have similar usage constraints. Collected statistics can be dumped by
|
||||
the [pass manager](#pass-manager) programmatically via
|
||||
`PassManager::enableStatistics`; or via `-pass-statistics` and
|
||||
`-pass-statistics-display` on the command line.
|
||||
|
||||
An example is shown below:
|
||||
|
||||
```c++
|
||||
struct MyPass : public OperationPass<MyPass> {
|
||||
Statistic testStat{this, "testStat", "A test statistic"};
|
||||
|
||||
void runOnOperation() {
|
||||
...
|
||||
|
||||
// Update our statistic after some invariant was hit.
|
||||
++testStat;
|
||||
|
||||
...
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
The collected statistics may be aggregated in two types of views:
|
||||
|
||||
A pipeline view that models the structure of the pass manager, this is the
|
||||
default view:
|
||||
|
||||
```shell
|
||||
$ mlir-opt -pass-pipeline='func(my-pass,my-pass)' foo.mlir -pass-statistics
|
||||
|
||||
===-------------------------------------------------------------------------===
|
||||
... Pass statistics report ...
|
||||
===-------------------------------------------------------------------------===
|
||||
'func' Pipeline
|
||||
MyPass
|
||||
(S) 15 testStat - A test statistic
|
||||
VerifierPass
|
||||
MyPass
|
||||
(S) 6 testStat - A test statistic
|
||||
VerifierPass
|
||||
VerifierPass
|
||||
```
|
||||
|
||||
And a list view that aggregates all instances of a specific pass together:
|
||||
|
||||
```shell
|
||||
$ mlir-opt -pass-pipeline='func(my-pass, my-pass)' foo.mlir -pass-statistics -pass-statistics-display=list
|
||||
|
||||
===-------------------------------------------------------------------------===
|
||||
... Pass statistics report ...
|
||||
===-------------------------------------------------------------------------===
|
||||
MyPass
|
||||
(S) 21 testStat - A test statistic
|
||||
```
|
||||
|
||||
## Pass Instrumentation
|
||||
|
||||
MLIR provides a customizable framework to instrument pass execution and analysis
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "mlir/Pass/PassRegistry.h"
|
||||
#include "mlir/Support/LogicalResult.h"
|
||||
#include "llvm/ADT/PointerIntPair.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
|
||||
namespace mlir {
|
||||
namespace detail {
|
||||
|
@ -76,6 +77,28 @@ public:
|
|||
/// pass to be to be round-trippable to the textual format.
|
||||
virtual void printAsTextualPipeline(raw_ostream &os);
|
||||
|
||||
/// This class represents a single pass statistic. This statistic functions
|
||||
/// similarly to an unsigned integer value, and may be updated and incremented
|
||||
/// accordingly. This class can be used to provide additional information
|
||||
/// about the transformations and analyses performed by a pass.
|
||||
class Statistic : public llvm::Statistic {
|
||||
public:
|
||||
/// The statistic is initialized by the pass owner, a name, and a
|
||||
/// description.
|
||||
Statistic(Pass *owner, const char *name, const char *description);
|
||||
|
||||
/// Assign the statistic to the given value.
|
||||
Statistic &operator=(unsigned value);
|
||||
|
||||
private:
|
||||
/// Hide some of the details of llvm::Statistic that we don't use.
|
||||
using llvm::Statistic::getDebugType;
|
||||
};
|
||||
|
||||
/// Returns the main statistics for this pass instance.
|
||||
ArrayRef<Statistic *> getStatistics() const { return statistics; }
|
||||
MutableArrayRef<Statistic *> getStatistics() { return statistics; }
|
||||
|
||||
protected:
|
||||
explicit Pass(const PassID *passID,
|
||||
llvm::Optional<StringRef> opName = llvm::None)
|
||||
|
@ -125,6 +148,9 @@ private:
|
|||
/// The current execution state for the pass.
|
||||
llvm::Optional<detail::PassExecutionState> passState;
|
||||
|
||||
/// The set of statistics held by this pass.
|
||||
std::vector<Statistic *> statistics;
|
||||
|
||||
/// Allow access to 'clone' and 'run'.
|
||||
friend class OpPassManager;
|
||||
};
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
#include "mlir/Support/LogicalResult.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/iterator.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
class Any;
|
||||
|
@ -54,6 +57,13 @@ public:
|
|||
~OpPassManager();
|
||||
OpPassManager &operator=(const OpPassManager &rhs);
|
||||
|
||||
/// Iterator over the passes in this pass manager.
|
||||
using pass_iterator =
|
||||
llvm::pointee_iterator<std::vector<std::unique_ptr<Pass>>::iterator>;
|
||||
pass_iterator begin();
|
||||
pass_iterator end();
|
||||
llvm::iterator_range<pass_iterator> getPasses() { return {begin(), end()}; }
|
||||
|
||||
/// Run the held passes over the given operation.
|
||||
LogicalResult run(Operation *op, AnalysisManager am);
|
||||
|
||||
|
@ -93,6 +103,9 @@ public:
|
|||
/// the correctness of per-pass overrides of Pass::printAsTextualPipeline.
|
||||
void printAsTextualPipeline(raw_ostream &os);
|
||||
|
||||
/// Merge the pass statistics of this class into 'other'.
|
||||
void mergeStatisticsInto(OpPassManager &other);
|
||||
|
||||
private:
|
||||
OpPassManager(OperationName name, bool disableThreads, bool verifyPasses);
|
||||
|
||||
|
@ -107,10 +120,10 @@ private:
|
|||
// PassManager
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// An enum describing the different display modes for the pass timing
|
||||
/// information within the pass manager.
|
||||
enum class PassTimingDisplayMode {
|
||||
// In this mode the results are displayed in a list sorted by total time,
|
||||
/// An enum describing the different display modes for the information within
|
||||
/// the pass manager.
|
||||
enum class PassDisplayMode {
|
||||
// In this mode the results are displayed in a list sorted by total,
|
||||
// with each pass/analysis instance aggregated into one unique result.
|
||||
List,
|
||||
|
||||
|
@ -162,13 +175,23 @@ public:
|
|||
/// Note: Timing should be enabled after all other instrumentations to avoid
|
||||
/// any potential "ghost" timing from other instrumentations being
|
||||
/// unintentionally included in the timing results.
|
||||
void enableTiming(
|
||||
PassTimingDisplayMode displayMode = PassTimingDisplayMode::Pipeline);
|
||||
void enableTiming(PassDisplayMode displayMode = PassDisplayMode::Pipeline);
|
||||
|
||||
/// Prompts the pass manager to print the statistics collected for each of the
|
||||
/// held passes after each call to 'run'.
|
||||
void
|
||||
enableStatistics(PassDisplayMode displayMode = PassDisplayMode::Pipeline);
|
||||
|
||||
private:
|
||||
/// Dump the statistics of the passes within this pass manager.
|
||||
void dumpStatistics();
|
||||
|
||||
/// Flag that specifies if pass timing is enabled.
|
||||
bool passTiming : 1;
|
||||
|
||||
/// Flag that specifies if pass statistics should be dumped.
|
||||
Optional<PassDisplayMode> passStatisticsMode;
|
||||
|
||||
/// A manager for pass instrumentations.
|
||||
std::unique_ptr<PassInstrumentor> instrumentor;
|
||||
|
||||
|
|
|
@ -216,6 +216,11 @@ OpPassManager &OpPassManager::operator=(const OpPassManager &rhs) {
|
|||
|
||||
OpPassManager::~OpPassManager() {}
|
||||
|
||||
OpPassManager::pass_iterator OpPassManager::begin() {
|
||||
return impl->passes.begin();
|
||||
}
|
||||
OpPassManager::pass_iterator OpPassManager::end() { return impl->passes.end(); }
|
||||
|
||||
/// Run all of the passes in this manager over the current operation.
|
||||
LogicalResult OpPassManager::run(Operation *op, AnalysisManager am) {
|
||||
// Run each of the held passes.
|
||||
|
@ -341,6 +346,17 @@ void OpToOpPassAdaptorBase::mergeInto(OpToOpPassAdaptorBase &rhs) {
|
|||
});
|
||||
}
|
||||
|
||||
/// Returns the adaptor pass name.
|
||||
std::string OpToOpPassAdaptorBase::getName() {
|
||||
std::string name = "Pipeline Collection : [";
|
||||
llvm::raw_string_ostream os(name);
|
||||
interleaveComma(getPassManagers(), os, [&](OpPassManager &pm) {
|
||||
os << '\'' << pm.getOpName() << '\'';
|
||||
});
|
||||
os << ']';
|
||||
return os.str();
|
||||
}
|
||||
|
||||
OpToOpPassAdaptor::OpToOpPassAdaptor(OpPassManager &&mgr)
|
||||
: OpToOpPassAdaptorBase(std::move(mgr)) {}
|
||||
|
||||
|
@ -560,9 +576,15 @@ LogicalResult PassManager::run(ModuleOp module) {
|
|||
|
||||
// If reproducer generation is enabled, run the pass manager with crash
|
||||
// handling enabled.
|
||||
if (crashReproducerFileName)
|
||||
return runWithCrashRecovery(*this, am, module, *crashReproducerFileName);
|
||||
return OpPassManager::run(module, am);
|
||||
LogicalResult result =
|
||||
crashReproducerFileName
|
||||
? runWithCrashRecovery(*this, am, module, *crashReproducerFileName)
|
||||
: OpPassManager::run(module, am);
|
||||
|
||||
// Dump all of the pass statistics if necessary.
|
||||
if (passStatisticsMode)
|
||||
dumpStatistics();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Disable support for multi-threading within the pass manager.
|
||||
|
|
|
@ -48,6 +48,9 @@ public:
|
|||
/// Returns the pass managers held by this adaptor.
|
||||
MutableArrayRef<OpPassManager> getPassManagers() { return mgrs; }
|
||||
|
||||
/// Returns the adaptor pass name.
|
||||
std::string getName();
|
||||
|
||||
protected:
|
||||
// A set of adaptors to run.
|
||||
SmallVector<OpPassManager, 1> mgrs;
|
||||
|
@ -75,6 +78,11 @@ public:
|
|||
/// Run the held pipeline over all operations.
|
||||
void runOnOperation() override;
|
||||
|
||||
/// Return the async pass managers held by this parallel adaptor.
|
||||
MutableArrayRef<SmallVector<OpPassManager, 1>> getParallelPassManagers() {
|
||||
return asyncExecutors;
|
||||
}
|
||||
|
||||
private:
|
||||
// A set of executors, cloned from the main executor, that run asynchronously
|
||||
// on different threads.
|
||||
|
|
|
@ -69,14 +69,30 @@ struct PassManagerOptions {
|
|||
llvm::cl::opt<bool> passTiming{
|
||||
"pass-timing",
|
||||
llvm::cl::desc("Display the execution times of each pass")};
|
||||
llvm::cl::opt<PassTimingDisplayMode> passTimingDisplayMode{
|
||||
llvm::cl::opt<PassDisplayMode> passTimingDisplayMode{
|
||||
"pass-timing-display",
|
||||
llvm::cl::desc("Display method for pass timing data"),
|
||||
llvm::cl::init(PassTimingDisplayMode::Pipeline),
|
||||
llvm::cl::init(PassDisplayMode::Pipeline),
|
||||
llvm::cl::values(
|
||||
clEnumValN(PassTimingDisplayMode::List, "list",
|
||||
clEnumValN(PassDisplayMode::List, "list",
|
||||
"display the results in a list sorted by total time"),
|
||||
clEnumValN(PassTimingDisplayMode::Pipeline, "pipeline",
|
||||
clEnumValN(PassDisplayMode::Pipeline, "pipeline",
|
||||
"display the results with a nested pipeline view"))};
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Pass Statistics
|
||||
//===--------------------------------------------------------------------===//
|
||||
llvm::cl::opt<bool> passStatistics{
|
||||
"pass-statistics", llvm::cl::desc("Display the statistics of each pass")};
|
||||
llvm::cl::opt<PassDisplayMode> passStatisticsDisplayMode{
|
||||
"pass-statistics-display",
|
||||
llvm::cl::desc("Display method for pass statistics"),
|
||||
llvm::cl::init(PassDisplayMode::Pipeline),
|
||||
llvm::cl::values(
|
||||
clEnumValN(
|
||||
PassDisplayMode::List, "list",
|
||||
"display the results in a merged list sorted by pass name"),
|
||||
clEnumValN(PassDisplayMode::Pipeline, "pipeline",
|
||||
"display the results with a nested pipeline view"))};
|
||||
|
||||
/// Add a pass timing instrumentation if enabled by 'pass-timing' flags.
|
||||
|
@ -146,6 +162,10 @@ void mlir::applyPassManagerCLOptions(PassManager &pm) {
|
|||
if ((*options)->disableThreads)
|
||||
pm.disableMultithreading();
|
||||
|
||||
// Enable statistics dumping.
|
||||
if ((*options)->passStatistics)
|
||||
pm.enableStatistics((*options)->passStatisticsDisplayMode);
|
||||
|
||||
// Add the IR printing instrumentation.
|
||||
(*options)->addPrinterInstrumentation(pm);
|
||||
|
||||
|
|
|
@ -0,0 +1,258 @@
|
|||
//===- PassStatistics.cpp -------------------------------------------------===//
|
||||
//
|
||||
// Copyright 2019 The MLIR Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
#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 llvm::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 = getAdaptorPassBase(pass);
|
||||
|
||||
// If this is not an adaptor, add the stats to the list if there are any.
|
||||
if (!adaptor) {
|
||||
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();
|
||||
}
|
||||
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) {
|
||||
std::function<void(unsigned, Pass *)> printPass = [&](unsigned indent,
|
||||
Pass *pass) {
|
||||
// Handle the case of an adaptor pass.
|
||||
if (auto *adaptor = getAdaptorPassBase(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->getName());
|
||||
indent += 2;
|
||||
}
|
||||
|
||||
// Print each of the children passes.
|
||||
for (OpPassManager &mgr : mgrs) {
|
||||
auto name = ("'" + mgr.getOpName().getStringRef() + "' 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);
|
||||
}
|
||||
|
||||
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 registartion 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 = getAdaptorPassBase(&pass)) {
|
||||
auto *otherAdaptorPass = getAdaptorPassBase(&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()) {
|
||||
OpToOpPassAdaptorBase *adaptor = getAdaptorPassBase(&pass);
|
||||
if (!adaptor)
|
||||
continue;
|
||||
MutableArrayRef<OpPassManager> nestedPms = adaptor->getPassManagers();
|
||||
|
||||
// If this is a parallel adaptor, merge the statistics from the async
|
||||
// pass managers into the main nested pass managers.
|
||||
if (auto *parallelAdaptor = dyn_cast<OpToOpPassAdaptorParallel>(&pass)) {
|
||||
for (auto &asyncPM : parallelAdaptor->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;
|
||||
}
|
|
@ -169,7 +169,7 @@ struct Timer {
|
|||
};
|
||||
|
||||
struct PassTiming : public PassInstrumentation {
|
||||
PassTiming(PassTimingDisplayMode displayMode) : displayMode(displayMode) {}
|
||||
PassTiming(PassDisplayMode displayMode) : displayMode(displayMode) {}
|
||||
~PassTiming() override { print(); }
|
||||
|
||||
/// Setup the instrumentation hooks.
|
||||
|
@ -242,7 +242,7 @@ struct PassTiming : public PassInstrumentation {
|
|||
DenseMap<uint64_t, SmallVector<Timer *, 4>> activeThreadTimers;
|
||||
|
||||
/// The display mode to use when printing the timing results.
|
||||
PassTimingDisplayMode displayMode;
|
||||
PassDisplayMode displayMode;
|
||||
|
||||
/// A mapping of pipeline timers that need to be merged into the parent
|
||||
/// collection. The timers are mapped to the parent info to merge into.
|
||||
|
@ -289,15 +289,8 @@ void PassTiming::startPassTimer(Pass *pass) {
|
|||
auto kind = isAdaptorPass(pass) ? TimerKind::PipelineCollection
|
||||
: TimerKind::PassOrAnalysis;
|
||||
Timer *timer = getTimer(pass, kind, [pass]() -> std::string {
|
||||
if (auto *adaptor = getAdaptorPassBase(pass)) {
|
||||
std::string name = "Pipeline Collection : [";
|
||||
llvm::raw_string_ostream os(name);
|
||||
interleaveComma(adaptor->getPassManagers(), os, [&](OpPassManager &pm) {
|
||||
os << '\'' << pm.getOpName() << '\'';
|
||||
});
|
||||
os << ']';
|
||||
return os.str();
|
||||
}
|
||||
if (auto *adaptor = getAdaptorPassBase(pass))
|
||||
return adaptor->getName();
|
||||
return pass->getName();
|
||||
});
|
||||
|
||||
|
@ -345,8 +338,8 @@ void PassTiming::runAfterAnalysis(llvm::StringRef, AnalysisID *, Operation *) {
|
|||
static void printTimerHeader(llvm::raw_ostream &os, TimeRecord total) {
|
||||
os << "===" << std::string(73, '-') << "===\n";
|
||||
// Figure out how many spaces to description name.
|
||||
unsigned Padding = (80 - kPassTimingDescription.size()) / 2;
|
||||
os.indent(Padding) << kPassTimingDescription << '\n';
|
||||
unsigned padding = (80 - kPassTimingDescription.size()) / 2;
|
||||
os.indent(padding) << kPassTimingDescription << '\n';
|
||||
os << "===" << std::string(73, '-') << "===\n";
|
||||
|
||||
// Print the total time followed by the section headers.
|
||||
|
@ -379,10 +372,10 @@ void PassTiming::print() {
|
|||
|
||||
// Defer to a specialized printer for each display mode.
|
||||
switch (displayMode) {
|
||||
case PassTimingDisplayMode::List:
|
||||
case PassDisplayMode::List:
|
||||
printResultsAsList(*os, rootTimer.get(), totalTime);
|
||||
break;
|
||||
case PassTimingDisplayMode::Pipeline:
|
||||
case PassDisplayMode::Pipeline:
|
||||
printResultsAsPipeline(*os, rootTimer.get(), totalTime);
|
||||
break;
|
||||
}
|
||||
|
@ -472,7 +465,7 @@ void PassTiming::printResultsAsPipeline(raw_ostream &os, Timer *root,
|
|||
|
||||
/// Add an instrumentation to time the execution of passes and the computation
|
||||
/// of analyses.
|
||||
void PassManager::enableTiming(PassTimingDisplayMode displayMode) {
|
||||
void PassManager::enableTiming(PassDisplayMode displayMode) {
|
||||
// Check if pass timing is already enabled.
|
||||
if (passTiming)
|
||||
return;
|
||||
|
|
|
@ -124,6 +124,10 @@ struct CSE : public OperationPass<CSE> {
|
|||
private:
|
||||
/// Operations marked as dead and to be erased.
|
||||
std::vector<Operation *> opsToErase;
|
||||
|
||||
/// Statistics for CSE.
|
||||
Statistic numCSE{this, "num-cse'd", "Number of operations CSE'd"};
|
||||
Statistic numDCE{this, "num-dce'd", "Number of operations trivially DCE'd"};
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
|
@ -143,6 +147,7 @@ LogicalResult CSE::simplifyOperation(ScopedMapTy &knownValues, Operation *op) {
|
|||
// If the operation is already trivially dead just add it to the erase list.
|
||||
if (op->use_empty()) {
|
||||
opsToErase.push_back(op);
|
||||
++numDCE;
|
||||
return success();
|
||||
}
|
||||
|
||||
|
@ -160,6 +165,8 @@ LogicalResult CSE::simplifyOperation(ScopedMapTy &knownValues, Operation *op) {
|
|||
!op->getLoc().isa<UnknownLoc>()) {
|
||||
existing->setLoc(op->getLoc());
|
||||
}
|
||||
|
||||
++numCSE;
|
||||
return success();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
// RUN: mlir-opt %s -verify-each=true -pass-pipeline='func(test-stats-pass,test-stats-pass)' -pass-statistics -pass-statistics-display=list 2>&1 | FileCheck -check-prefix=LIST %s
|
||||
// RUN: mlir-opt %s -verify-each=true -pass-pipeline='func(test-stats-pass,test-stats-pass)' -pass-statistics -pass-statistics-display=pipeline 2>&1 | FileCheck -check-prefix=PIPELINE %s
|
||||
|
||||
// LIST: Pass statistics report
|
||||
// LIST: TestStatisticPass
|
||||
// LIST-NEXT: (S) 8 num-ops - Number of operations counted
|
||||
// LIST-NOT: Verifier
|
||||
|
||||
// PIPELINE: Pass statistics report
|
||||
// PIPELINE: 'func' Pipeline
|
||||
// PIPELINE-NEXT: TestStatisticPass
|
||||
// PIPELINE-NEXT: (S) 4 num-ops - Number of operations counted
|
||||
// PIPELINE-NEXT: Verifier
|
||||
// PIPELINE-NEXT: TestStatisticPass
|
||||
// PIPELINE-NEXT: (S) 4 num-ops - Number of operations counted
|
||||
// PIPELINE-NEXT: Verifier
|
||||
// PIPELINE-NEXT: Verifier
|
||||
|
||||
func @foo() {
|
||||
return
|
||||
}
|
||||
|
||||
func @bar() {
|
||||
return
|
||||
}
|
|
@ -74,6 +74,18 @@ public:
|
|||
class TestCrashRecoveryPass : public OperationPass<TestCrashRecoveryPass> {
|
||||
void runOnOperation() final { abort(); }
|
||||
};
|
||||
|
||||
/// A test pass that contains a statistic.
|
||||
struct TestStatisticPass : public OperationPass<TestStatisticPass> {
|
||||
TestStatisticPass() = default;
|
||||
TestStatisticPass(const TestStatisticPass &) {}
|
||||
|
||||
Statistic opCount{this, "num-ops", "Number of operations counted"};
|
||||
|
||||
void runOnOperation() final {
|
||||
getOperation()->walk([&](Operation *) { ++opCount; });
|
||||
}
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
static void testNestedPipeline(OpPassManager &pm) {
|
||||
|
@ -106,6 +118,9 @@ static PassRegistration<TestCrashRecoveryPass>
|
|||
unusedCrashP("test-pass-crash",
|
||||
"Test a pass in the pass manager that always crashes");
|
||||
|
||||
static PassRegistration<TestStatisticPass> unusedStatP("test-stats-pass",
|
||||
"Test pass statistics");
|
||||
|
||||
static PassPipelineRegistration<>
|
||||
unused("test-pm-nested-pipeline",
|
||||
"Test a nested pipeline in the pass manager", testNestedPipeline);
|
||||
|
|
Loading…
Reference in New Issue