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

View file

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

View file

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

View file

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

View file

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

View file

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

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 {
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;

View file

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

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> {
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);