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:
River Riddle 2019-12-05 11:52:58 -08:00 committed by A. Unique TensorFlower
parent 4d61a79db4
commit 33a64540ad
11 changed files with 500 additions and 33 deletions

View File

@ -319,10 +319,10 @@ program has been run through the passes. This provides several benefits:
## Pass Registration ## Pass Registration
Briefly shown in the example definitions of the various Briefly shown in the example definitions of the various pass types is the
pass types is the `PassRegistration` class. This is a utility to `PassRegistration` class. This is a utility to register derived pass classes so
register derived pass classes so that they may be created, and inspected, by that they may be created, and inspected, by utilities like mlir-opt. Registering
utilities like mlir-opt. Registering a pass class takes the form: a pass class takes the form:
```c++ ```c++
static PassRegistration<MyPass> pass("command-line-arg", "description"); 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"); 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 ## Pass Instrumentation
MLIR provides a customizable framework to instrument pass execution and analysis MLIR provides a customizable framework to instrument pass execution and analysis

View File

@ -23,6 +23,7 @@
#include "mlir/Pass/PassRegistry.h" #include "mlir/Pass/PassRegistry.h"
#include "mlir/Support/LogicalResult.h" #include "mlir/Support/LogicalResult.h"
#include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/Statistic.h"
namespace mlir { namespace mlir {
namespace detail { namespace detail {
@ -76,6 +77,28 @@ public:
/// pass to be to be round-trippable to the textual format. /// pass to be to be round-trippable to the textual format.
virtual void printAsTextualPipeline(raw_ostream &os); 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: protected:
explicit Pass(const PassID *passID, explicit Pass(const PassID *passID,
llvm::Optional<StringRef> opName = llvm::None) llvm::Optional<StringRef> opName = llvm::None)
@ -125,6 +148,9 @@ private:
/// The current execution state for the pass. /// The current execution state for the pass.
llvm::Optional<detail::PassExecutionState> passState; llvm::Optional<detail::PassExecutionState> passState;
/// The set of statistics held by this pass.
std::vector<Statistic *> statistics;
/// Allow access to 'clone' and 'run'. /// Allow access to 'clone' and 'run'.
friend class OpPassManager; friend class OpPassManager;
}; };

View File

@ -21,6 +21,9 @@
#include "mlir/Support/LogicalResult.h" #include "mlir/Support/LogicalResult.h"
#include "llvm/ADT/Optional.h" #include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/iterator.h"
#include <vector>
namespace llvm { namespace llvm {
class Any; class Any;
@ -54,6 +57,13 @@ public:
~OpPassManager(); ~OpPassManager();
OpPassManager &operator=(const OpPassManager &rhs); 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. /// Run the held passes over the given operation.
LogicalResult run(Operation *op, AnalysisManager am); LogicalResult run(Operation *op, AnalysisManager am);
@ -93,6 +103,9 @@ public:
/// the correctness of per-pass overrides of Pass::printAsTextualPipeline. /// the correctness of per-pass overrides of Pass::printAsTextualPipeline.
void printAsTextualPipeline(raw_ostream &os); void printAsTextualPipeline(raw_ostream &os);
/// Merge the pass statistics of this class into 'other'.
void mergeStatisticsInto(OpPassManager &other);
private: private:
OpPassManager(OperationName name, bool disableThreads, bool verifyPasses); OpPassManager(OperationName name, bool disableThreads, bool verifyPasses);
@ -107,10 +120,10 @@ private:
// PassManager // PassManager
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
/// An enum describing the different display modes for the pass timing /// An enum describing the different display modes for the information within
/// information within the pass manager. /// the pass manager.
enum class PassTimingDisplayMode { enum class PassDisplayMode {
// In this mode the results are displayed in a list sorted by total time, // In this mode the results are displayed in a list sorted by total,
// with each pass/analysis instance aggregated into one unique result. // with each pass/analysis instance aggregated into one unique result.
List, List,
@ -162,13 +175,23 @@ public:
/// Note: Timing should be enabled after all other instrumentations to avoid /// Note: Timing should be enabled after all other instrumentations to avoid
/// any potential "ghost" timing from other instrumentations being /// any potential "ghost" timing from other instrumentations being
/// unintentionally included in the timing results. /// unintentionally included in the timing results.
void enableTiming( void enableTiming(PassDisplayMode displayMode = PassDisplayMode::Pipeline);
PassTimingDisplayMode displayMode = PassTimingDisplayMode::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: private:
/// Dump the statistics of the passes within this pass manager.
void dumpStatistics();
/// Flag that specifies if pass timing is enabled. /// Flag that specifies if pass timing is enabled.
bool passTiming : 1; bool passTiming : 1;
/// Flag that specifies if pass statistics should be dumped.
Optional<PassDisplayMode> passStatisticsMode;
/// A manager for pass instrumentations. /// A manager for pass instrumentations.
std::unique_ptr<PassInstrumentor> instrumentor; std::unique_ptr<PassInstrumentor> instrumentor;

View File

@ -216,6 +216,11 @@ OpPassManager &OpPassManager::operator=(const OpPassManager &rhs) {
OpPassManager::~OpPassManager() {} 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. /// Run all of the passes in this manager over the current operation.
LogicalResult OpPassManager::run(Operation *op, AnalysisManager am) { LogicalResult OpPassManager::run(Operation *op, AnalysisManager am) {
// Run each of the held passes. // 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) OpToOpPassAdaptor::OpToOpPassAdaptor(OpPassManager &&mgr)
: OpToOpPassAdaptorBase(std::move(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 // If reproducer generation is enabled, run the pass manager with crash
// handling enabled. // handling enabled.
if (crashReproducerFileName) LogicalResult result =
return runWithCrashRecovery(*this, am, module, *crashReproducerFileName); crashReproducerFileName
return OpPassManager::run(module, am); ? 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. /// Disable support for multi-threading within the pass manager.

View File

@ -48,6 +48,9 @@ public:
/// Returns the pass managers held by this adaptor. /// Returns the pass managers held by this adaptor.
MutableArrayRef<OpPassManager> getPassManagers() { return mgrs; } MutableArrayRef<OpPassManager> getPassManagers() { return mgrs; }
/// Returns the adaptor pass name.
std::string getName();
protected: protected:
// A set of adaptors to run. // A set of adaptors to run.
SmallVector<OpPassManager, 1> mgrs; SmallVector<OpPassManager, 1> mgrs;
@ -75,6 +78,11 @@ public:
/// Run the held pipeline over all operations. /// Run the held pipeline over all operations.
void runOnOperation() override; void runOnOperation() override;
/// Return the async pass managers held by this parallel adaptor.
MutableArrayRef<SmallVector<OpPassManager, 1>> getParallelPassManagers() {
return asyncExecutors;
}
private: private:
// A set of executors, cloned from the main executor, that run asynchronously // A set of executors, cloned from the main executor, that run asynchronously
// on different threads. // on different threads.

View File

@ -69,14 +69,30 @@ struct PassManagerOptions {
llvm::cl::opt<bool> passTiming{ llvm::cl::opt<bool> passTiming{
"pass-timing", "pass-timing",
llvm::cl::desc("Display the execution times of each pass")}; llvm::cl::desc("Display the execution times of each pass")};
llvm::cl::opt<PassTimingDisplayMode> passTimingDisplayMode{ llvm::cl::opt<PassDisplayMode> passTimingDisplayMode{
"pass-timing-display", "pass-timing-display",
llvm::cl::desc("Display method for pass timing data"), llvm::cl::desc("Display method for pass timing data"),
llvm::cl::init(PassTimingDisplayMode::Pipeline), llvm::cl::init(PassDisplayMode::Pipeline),
llvm::cl::values( llvm::cl::values(
clEnumValN(PassTimingDisplayMode::List, "list", clEnumValN(PassDisplayMode::List, "list",
"display the results in a list sorted by total time"), "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"))}; "display the results with a nested pipeline view"))};
/// Add a pass timing instrumentation if enabled by 'pass-timing' flags. /// Add a pass timing instrumentation if enabled by 'pass-timing' flags.
@ -146,6 +162,10 @@ void mlir::applyPassManagerCLOptions(PassManager &pm) {
if ((*options)->disableThreads) if ((*options)->disableThreads)
pm.disableMultithreading(); pm.disableMultithreading();
// Enable statistics dumping.
if ((*options)->passStatistics)
pm.enableStatistics((*options)->passStatisticsDisplayMode);
// Add the IR printing instrumentation. // Add the IR printing instrumentation.
(*options)->addPrinterInstrumentation(pm); (*options)->addPrinterInstrumentation(pm);

View File

@ -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;
}

View File

@ -169,7 +169,7 @@ struct Timer {
}; };
struct PassTiming : public PassInstrumentation { struct PassTiming : public PassInstrumentation {
PassTiming(PassTimingDisplayMode displayMode) : displayMode(displayMode) {} PassTiming(PassDisplayMode displayMode) : displayMode(displayMode) {}
~PassTiming() override { print(); } ~PassTiming() override { print(); }
/// Setup the instrumentation hooks. /// Setup the instrumentation hooks.
@ -242,7 +242,7 @@ struct PassTiming : public PassInstrumentation {
DenseMap<uint64_t, SmallVector<Timer *, 4>> activeThreadTimers; DenseMap<uint64_t, SmallVector<Timer *, 4>> activeThreadTimers;
/// The display mode to use when printing the timing results. /// 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 /// 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. /// 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 auto kind = isAdaptorPass(pass) ? TimerKind::PipelineCollection
: TimerKind::PassOrAnalysis; : TimerKind::PassOrAnalysis;
Timer *timer = getTimer(pass, kind, [pass]() -> std::string { Timer *timer = getTimer(pass, kind, [pass]() -> std::string {
if (auto *adaptor = getAdaptorPassBase(pass)) { if (auto *adaptor = getAdaptorPassBase(pass))
std::string name = "Pipeline Collection : ["; return adaptor->getName();
llvm::raw_string_ostream os(name);
interleaveComma(adaptor->getPassManagers(), os, [&](OpPassManager &pm) {
os << '\'' << pm.getOpName() << '\'';
});
os << ']';
return os.str();
}
return pass->getName(); return pass->getName();
}); });
@ -345,8 +338,8 @@ void PassTiming::runAfterAnalysis(llvm::StringRef, AnalysisID *, Operation *) {
static void printTimerHeader(llvm::raw_ostream &os, TimeRecord total) { static void printTimerHeader(llvm::raw_ostream &os, TimeRecord total) {
os << "===" << std::string(73, '-') << "===\n"; os << "===" << std::string(73, '-') << "===\n";
// Figure out how many spaces to description name. // Figure out how many spaces to description name.
unsigned Padding = (80 - kPassTimingDescription.size()) / 2; unsigned padding = (80 - kPassTimingDescription.size()) / 2;
os.indent(Padding) << kPassTimingDescription << '\n'; os.indent(padding) << kPassTimingDescription << '\n';
os << "===" << std::string(73, '-') << "===\n"; os << "===" << std::string(73, '-') << "===\n";
// Print the total time followed by the section headers. // 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. // Defer to a specialized printer for each display mode.
switch (displayMode) { switch (displayMode) {
case PassTimingDisplayMode::List: case PassDisplayMode::List:
printResultsAsList(*os, rootTimer.get(), totalTime); printResultsAsList(*os, rootTimer.get(), totalTime);
break; break;
case PassTimingDisplayMode::Pipeline: case PassDisplayMode::Pipeline:
printResultsAsPipeline(*os, rootTimer.get(), totalTime); printResultsAsPipeline(*os, rootTimer.get(), totalTime);
break; 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 /// Add an instrumentation to time the execution of passes and the computation
/// of analyses. /// of analyses.
void PassManager::enableTiming(PassTimingDisplayMode displayMode) { void PassManager::enableTiming(PassDisplayMode displayMode) {
// Check if pass timing is already enabled. // Check if pass timing is already enabled.
if (passTiming) if (passTiming)
return; return;

View File

@ -124,6 +124,10 @@ struct CSE : public OperationPass<CSE> {
private: private:
/// Operations marked as dead and to be erased. /// Operations marked as dead and to be erased.
std::vector<Operation *> opsToErase; 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 } // 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 the operation is already trivially dead just add it to the erase list.
if (op->use_empty()) { if (op->use_empty()) {
opsToErase.push_back(op); opsToErase.push_back(op);
++numDCE;
return success(); return success();
} }
@ -160,6 +165,8 @@ LogicalResult CSE::simplifyOperation(ScopedMapTy &knownValues, Operation *op) {
!op->getLoc().isa<UnknownLoc>()) { !op->getLoc().isa<UnknownLoc>()) {
existing->setLoc(op->getLoc()); existing->setLoc(op->getLoc());
} }
++numCSE;
return success(); return success();
} }

View File

@ -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
}

View File

@ -74,6 +74,18 @@ public:
class TestCrashRecoveryPass : public OperationPass<TestCrashRecoveryPass> { class TestCrashRecoveryPass : public OperationPass<TestCrashRecoveryPass> {
void runOnOperation() final { abort(); } 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 } // end anonymous namespace
static void testNestedPipeline(OpPassManager &pm) { static void testNestedPipeline(OpPassManager &pm) {
@ -106,6 +118,9 @@ static PassRegistration<TestCrashRecoveryPass>
unusedCrashP("test-pass-crash", unusedCrashP("test-pass-crash",
"Test a pass in the pass manager that always crashes"); "Test a pass in the pass manager that always crashes");
static PassRegistration<TestStatisticPass> unusedStatP("test-stats-pass",
"Test pass statistics");
static PassPipelineRegistration<> static PassPipelineRegistration<>
unused("test-pm-nested-pipeline", unused("test-pm-nested-pipeline",
"Test a nested pipeline in the pass manager", testNestedPipeline); "Test a nested pipeline in the pass manager", testNestedPipeline);