diff --git a/mlir/docs/PassManagement.md b/mlir/docs/PassManagement.md --- a/mlir/docs/PassManagement.md +++ b/mlir/docs/PassManagement.md @@ -1065,7 +1065,8 @@ counts the number of times the `DominanceInfo` analysis is computed: ```c++ -struct DominanceCounterInstrumentation : public PassInstrumentation { +struct DominanceCounterInstrumentation + : public PassInstrumentation { /// The cumulative count of how many times dominance has been calculated. unsigned &count; diff --git a/mlir/include/mlir/Pass/Pass.h b/mlir/include/mlir/Pass/Pass.h --- a/mlir/include/mlir/Pass/Pass.h +++ b/mlir/include/mlir/Pass/Pass.h @@ -12,6 +12,7 @@ #include "mlir/Pass/AnalysisManager.h" #include "mlir/Pass/PassRegistry.h" #include "mlir/Support/LogicalResult.h" +#include "mlir/Support/Timing.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/Statistic.h" #include @@ -196,6 +197,16 @@ return passState->pipelineExecutor(pipeline, op); } + /// Create a nested timing scope. This returns a new `TimingScope` with a + /// timer nested within the current pass scope. In this fashion, the time in + /// this scope may be further subdivided in a more fine-grained fashion. + template + TimingScope createTimingScope(Args... args) { + if (auto *pi = getAnalysisManager().getPassInstrumentor()) + return pi->getCurrentTimer().nest(std::forward(args)...); + return TimingScope(); + } + /// A clone method to create a copy of this pass. std::unique_ptr clone() const { auto newInst = clonePass(); diff --git a/mlir/include/mlir/Pass/PassInstrumentation.h b/mlir/include/mlir/Pass/PassInstrumentation.h --- a/mlir/include/mlir/Pass/PassInstrumentation.h +++ b/mlir/include/mlir/Pass/PassInstrumentation.h @@ -17,15 +17,17 @@ class OperationName; class Operation; class Pass; +class Timer; namespace detail { struct PassInstrumentorImpl; } // namespace detail -/// PassInstrumentation provides several entry points into the pass manager -/// infrastructure. Instrumentations should be added directly to a PassManager -/// before running a pipeline. -class PassInstrumentation { +/// PassInstrumentationBase represents the opaque instrumentation base class, +/// with several entry points into the pass manager infrastructure. +/// Instrumentations should be added directly to a PassManager before running a +/// pipeline. +class PassInstrumentationBase { public: /// This struct represents information related to the parent pass of pipeline. /// It includes information that allows for effectively linking pipelines that @@ -39,7 +41,7 @@ Pass *parentPass; }; - virtual ~PassInstrumentation() = 0; + virtual ~PassInstrumentationBase() = 0; /// A callback to run before a pass pipeline is executed. This function takes /// the name of the operation type being operated on, or std::nullopt if the @@ -80,6 +82,29 @@ /// name of the analysis that was computed, its TypeID, as well as the /// current operation being analyzed. virtual void runAfterAnalysis(StringRef name, TypeID id, Operation *op) {} + + /// Return the TypeID of the derived instrumentation. + TypeID getTypeID() const { return typeID; } + +protected: + PassInstrumentationBase(TypeID typeID) : typeID(typeID) {} + +private: + /// The TypeID of the derived class. + TypeID typeID; +}; + +/// This class provides a CRTP base class to simplify defining +/// instrumentations. +template +class PassInstrumentation : public PassInstrumentationBase { +public: + static bool classof(const PassInstrumentationBase *pi) { + return pi->getTypeID() == TypeID::get(); + } + +protected: + PassInstrumentation() : PassInstrumentationBase(TypeID::get()) {} }; /// This class holds a collection of PassInstrumentation objects, and invokes @@ -91,33 +116,37 @@ PassInstrumentor(const PassInstrumentor &) = delete; ~PassInstrumentor(); - /// See PassInstrumentation::runBeforePipeline for details. - void - runBeforePipeline(std::optional name, - const PassInstrumentation::PipelineParentInfo &parentInfo); + /// Return the currently active timer for the current thread. This can be used + /// to nest additional timers under the current instrumentation scope. + Timer getCurrentTimer(); + + /// See PassInstrumentationBase::runBeforePipeline for details. + void runBeforePipeline( + std::optional name, + const PassInstrumentationBase::PipelineParentInfo &parentInfo); - /// See PassInstrumentation::runAfterPipeline for details. - void - runAfterPipeline(std::optional name, - const PassInstrumentation::PipelineParentInfo &parentInfo); + /// See PassInstrumentationBase::runAfterPipeline for details. + void runAfterPipeline( + std::optional name, + const PassInstrumentationBase::PipelineParentInfo &parentInfo); - /// See PassInstrumentation::runBeforePass for details. + /// See PassInstrumentationBase::runBeforePass for details. void runBeforePass(Pass *pass, Operation *op); - /// See PassInstrumentation::runAfterPass for details. + /// See PassInstrumentationBase::runAfterPass for details. void runAfterPass(Pass *pass, Operation *op); - /// See PassInstrumentation::runAfterPassFailed for details. + /// See PassInstrumentationBase::runAfterPassFailed for details. void runAfterPassFailed(Pass *pass, Operation *op); - /// See PassInstrumentation::runBeforeAnalysis for details. + /// See PassInstrumentationBase::runBeforeAnalysis for details. void runBeforeAnalysis(StringRef name, TypeID id, Operation *op); - /// See PassInstrumentation::runAfterAnalysis for details. + /// See PassInstrumentationBase::runAfterAnalysis for details. void runAfterAnalysis(StringRef name, TypeID id, Operation *op); /// Add the given instrumentation to the collection. - void addInstrumentation(std::unique_ptr pi); + void addInstrumentation(std::unique_ptr pi); private: std::unique_ptr impl; @@ -127,8 +156,8 @@ namespace llvm { template <> -struct DenseMapInfo { - using T = mlir::PassInstrumentation::PipelineParentInfo; +struct DenseMapInfo { + using T = mlir::PassInstrumentationBase::PipelineParentInfo; using PairInfo = DenseMapInfo>; static T getEmptyKey() { diff --git a/mlir/include/mlir/Pass/PassManager.h b/mlir/include/mlir/Pass/PassManager.h --- a/mlir/include/mlir/Pass/PassManager.h +++ b/mlir/include/mlir/Pass/PassManager.h @@ -30,7 +30,7 @@ class MLIRContext; class Operation; class Pass; -class PassInstrumentation; +class PassInstrumentationBase; class PassInstrumentor; namespace detail { @@ -274,7 +274,7 @@ //===--------------------------------------------------------------------===// /// Add the provided instrumentation to the pass manager. - void addInstrumentation(std::unique_ptr pi); + void addInstrumentation(std::unique_ptr pi); //===--------------------------------------------------------------------===// // IR Printing diff --git a/mlir/include/mlir/Support/Timing.h b/mlir/include/mlir/Support/Timing.h --- a/mlir/include/mlir/Support/Timing.h +++ b/mlir/include/mlir/Support/Timing.h @@ -315,6 +315,9 @@ /// Hide the timer in timing reports and directly show its children. void hide() { timer.hide(); } + /// Return the underlying timer managed by this scope. + Timer getTimer() { return timer; } + private: /// The wrapped timer. Timer timer; diff --git a/mlir/lib/Pass/IRPrinting.cpp b/mlir/lib/Pass/IRPrinting.cpp --- a/mlir/lib/Pass/IRPrinting.cpp +++ b/mlir/lib/Pass/IRPrinting.cpp @@ -20,8 +20,10 @@ // IRPrinter //===----------------------------------------------------------------------===// -class IRPrinterInstrumentation : public PassInstrumentation { +class IRPrinterInstrumentation + : public PassInstrumentation { public: + MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(IRPrinterInstrumentation) IRPrinterInstrumentation(std::unique_ptr config) : config(std::move(config)) {} diff --git a/mlir/lib/Pass/Pass.cpp b/mlir/lib/Pass/Pass.cpp --- a/mlir/lib/Pass/Pass.cpp +++ b/mlir/lib/Pass/Pass.cpp @@ -12,6 +12,7 @@ #include "mlir/Pass/Pass.h" #include "PassDetail.h" +#include "PassTiming.h" #include "mlir/IR/Diagnostics.h" #include "mlir/IR/Dialect.h" #include "mlir/IR/OpDefinition.h" @@ -433,8 +434,8 @@ // Initialize the pass state with a callback for the pass to dynamically // execute a pipeline on the currently visited operation. PassInstrumentor *pi = am.getPassInstrumentor(); - PassInstrumentation::PipelineParentInfo parentInfo = {llvm::get_threadid(), - pass}; + PassInstrumentationBase::PipelineParentInfo parentInfo = { + llvm::get_threadid(), pass}; auto dynamicPipelineCallback = [&](OpPassManager &pipeline, Operation *root) -> LogicalResult { if (!op->isAncestor(root)) @@ -513,7 +514,7 @@ LogicalResult OpToOpPassAdaptor::runPipeline( OpPassManager &pm, Operation *op, AnalysisManager am, bool verifyPasses, unsigned parentInitGeneration, PassInstrumentor *instrumentor, - const PassInstrumentation::PipelineParentInfo *parentInfo) { + const PassInstrumentationBase::PipelineParentInfo *parentInfo) { assert((!instrumentor || parentInfo) && "expected parent info if instrumentor is provided"); auto scopeExit = llvm::make_scope_exit([&] { @@ -660,8 +661,8 @@ /// Run this pass adaptor synchronously. void OpToOpPassAdaptor::runOnOperationImpl(bool verifyPasses) { auto am = getAnalysisManager(); - PassInstrumentation::PipelineParentInfo parentInfo = {llvm::get_threadid(), - this}; + PassInstrumentationBase::PipelineParentInfo parentInfo = { + llvm::get_threadid(), this}; auto *instrumentor = am.getPassInstrumentor(); for (auto ®ion : getOperation()->getRegions()) { for (auto &block : region) { @@ -734,8 +735,8 @@ } // Get the current thread for this adaptor. - PassInstrumentation::PipelineParentInfo parentInfo = {llvm::get_threadid(), - this}; + PassInstrumentationBase::PipelineParentInfo parentInfo = { + llvm::get_threadid(), this}; auto *instrumentor = am.getPassInstrumentor(); // An atomic failure variable for the async executors. @@ -832,7 +833,8 @@ } /// Add the provided instrumentation to the pass manager. -void PassManager::addInstrumentation(std::unique_ptr pi) { +void PassManager::addInstrumentation( + std::unique_ptr pi) { if (!instrumentor) instrumentor = std::make_unique(); @@ -918,12 +920,12 @@ // PassInstrumentation //===----------------------------------------------------------------------===// -PassInstrumentation::~PassInstrumentation() = default; +PassInstrumentationBase::~PassInstrumentationBase() = default; -void PassInstrumentation::runBeforePipeline( +void PassInstrumentationBase::runBeforePipeline( std::optional name, const PipelineParentInfo &parentInfo) {} -void PassInstrumentation::runAfterPipeline( +void PassInstrumentationBase::runAfterPipeline( std::optional name, const PipelineParentInfo &parentInfo) {} //===----------------------------------------------------------------------===// @@ -937,7 +939,7 @@ llvm::sys::SmartMutex mutex; /// Set of registered instrumentations. - std::vector> instrumentations; + std::vector> instrumentations; }; } // namespace detail } // namespace mlir @@ -945,46 +947,54 @@ PassInstrumentor::PassInstrumentor() : impl(new PassInstrumentorImpl()) {} PassInstrumentor::~PassInstrumentor() = default; -/// See PassInstrumentation::runBeforePipeline for details. +Timer PassInstrumentor::getCurrentTimer() { + llvm::sys::SmartScopedLock instrumentationLock(impl->mutex); + for (auto &instr : impl->instrumentations) + if (auto *timing = dyn_cast(instr.get())) + return timing->getActiveTimer(); + return Timer(); +} + +/// See PassInstrumentationBase::runBeforePipeline for details. void PassInstrumentor::runBeforePipeline( std::optional name, - const PassInstrumentation::PipelineParentInfo &parentInfo) { + const PassInstrumentationBase::PipelineParentInfo &parentInfo) { llvm::sys::SmartScopedLock instrumentationLock(impl->mutex); for (auto &instr : impl->instrumentations) instr->runBeforePipeline(name, parentInfo); } -/// See PassInstrumentation::runAfterPipeline for details. +/// See PassInstrumentationBase::runAfterPipeline for details. void PassInstrumentor::runAfterPipeline( std::optional name, - const PassInstrumentation::PipelineParentInfo &parentInfo) { + const PassInstrumentationBase::PipelineParentInfo &parentInfo) { llvm::sys::SmartScopedLock instrumentationLock(impl->mutex); for (auto &instr : llvm::reverse(impl->instrumentations)) instr->runAfterPipeline(name, parentInfo); } -/// See PassInstrumentation::runBeforePass for details. +/// See PassInstrumentationBase::runBeforePass for details. void PassInstrumentor::runBeforePass(Pass *pass, Operation *op) { llvm::sys::SmartScopedLock instrumentationLock(impl->mutex); for (auto &instr : impl->instrumentations) instr->runBeforePass(pass, op); } -/// See PassInstrumentation::runAfterPass for details. +/// See PassInstrumentationBase::runAfterPass for details. void PassInstrumentor::runAfterPass(Pass *pass, Operation *op) { llvm::sys::SmartScopedLock instrumentationLock(impl->mutex); for (auto &instr : llvm::reverse(impl->instrumentations)) instr->runAfterPass(pass, op); } -/// See PassInstrumentation::runAfterPassFailed for details. +/// See PassInstrumentationBase::runAfterPassFailed for details. void PassInstrumentor::runAfterPassFailed(Pass *pass, Operation *op) { llvm::sys::SmartScopedLock instrumentationLock(impl->mutex); for (auto &instr : llvm::reverse(impl->instrumentations)) instr->runAfterPassFailed(pass, op); } -/// See PassInstrumentation::runBeforeAnalysis for details. +/// See PassInstrumentationBase::runBeforeAnalysis for details. void PassInstrumentor::runBeforeAnalysis(StringRef name, TypeID id, Operation *op) { llvm::sys::SmartScopedLock instrumentationLock(impl->mutex); @@ -992,7 +1002,7 @@ instr->runBeforeAnalysis(name, id, op); } -/// See PassInstrumentation::runAfterAnalysis for details. +/// See PassInstrumentationBase::runAfterAnalysis for details. void PassInstrumentor::runAfterAnalysis(StringRef name, TypeID id, Operation *op) { llvm::sys::SmartScopedLock instrumentationLock(impl->mutex); @@ -1002,7 +1012,7 @@ /// Add the given instrumentation to the collection. void PassInstrumentor::addInstrumentation( - std::unique_ptr pi) { + std::unique_ptr pi) { llvm::sys::SmartScopedLock instrumentationLock(impl->mutex); impl->instrumentations.emplace_back(std::move(pi)); } diff --git a/mlir/lib/Pass/PassCrashRecovery.cpp b/mlir/lib/Pass/PassCrashRecovery.cpp --- a/mlir/lib/Pass/PassCrashRecovery.cpp +++ b/mlir/lib/Pass/PassCrashRecovery.cpp @@ -346,7 +346,10 @@ //===----------------------------------------------------------------------===// namespace { -struct CrashReproducerInstrumentation : public PassInstrumentation { +struct CrashReproducerInstrumentation + : public PassInstrumentation { + MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(CrashReproducerInstrumentation) + CrashReproducerInstrumentation(PassCrashReproducerGenerator &generator) : generator(generator) {} ~CrashReproducerInstrumentation() override = default; diff --git a/mlir/lib/Pass/PassDetail.h b/mlir/lib/Pass/PassDetail.h --- a/mlir/lib/Pass/PassDetail.h +++ b/mlir/lib/Pass/PassDetail.h @@ -76,7 +76,7 @@ static LogicalResult runPipeline( OpPassManager &pm, Operation *op, AnalysisManager am, bool verifyPasses, unsigned parentInitGeneration, PassInstrumentor *instrumentor = nullptr, - const PassInstrumentation::PipelineParentInfo *parentInfo = nullptr); + const PassInstrumentationBase::PipelineParentInfo *parentInfo = nullptr); /// A set of adaptors to run. SmallVector mgrs; diff --git a/mlir/lib/Pass/PassTiming.h b/mlir/lib/Pass/PassTiming.h new file mode 100644 --- /dev/null +++ b/mlir/lib/Pass/PassTiming.h @@ -0,0 +1,81 @@ +//===- PassTiming.h - MLIR Pass Timing --------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LIB_MLIR_PASS_PASSTIMING_H_ +#define LIB_MLIR_PASS_PASSTIMING_H_ + +#include "mlir/Pass/Pass.h" +#include "mlir/Pass/PassManager.h" + +namespace mlir::detail { +/// This class implements a pass instrumentation that records the time spent in +/// each pass and pipeline. +class PassTiming : public PassInstrumentation { +public: + MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(PassTiming) + + PassTiming(TimingScope &timingScope) : rootScope(timingScope) {} + PassTiming(std::unique_ptr tm) + : ownedTimingManager(std::move(tm)), + ownedTimingScope(ownedTimingManager->getRootScope()), + rootScope(ownedTimingScope) {} + + /// Return the currently active timer for the current thread. + Timer getActiveTimer(); + + //===--------------------------------------------------------------------===// + // Pipeline + //===--------------------------------------------------------------------===// + + void runBeforePipeline(std::optional name, + const PipelineParentInfo &parentInfo) override; + + void runAfterPipeline(std::optional, + const PipelineParentInfo &) override; + + //===--------------------------------------------------------------------===// + // Pass + //===--------------------------------------------------------------------===// + + void runBeforePass(Pass *pass, Operation *) override; + void runAfterPass(Pass *pass, Operation *) override; + void runAfterPassFailed(Pass *pass, Operation *op) override { + runAfterPass(pass, op); + } + + //===--------------------------------------------------------------------===// + // Analysis + //===--------------------------------------------------------------------===// + + void runBeforeAnalysis(StringRef name, TypeID id, Operation *) override; + void runAfterAnalysis(StringRef, TypeID, Operation *) override; + +private: + /// If a pass can spawn additional work on other threads, it records the + /// index to its currently active timer here. Passes that run on a + /// newly-forked thread will check this list to find the active timer of the + /// parent thread into which the new thread should be nested. + DenseMap parentTimerIndices; + + /// A stack of the currently active timing scopes per thread. + DenseMap> activeThreadTimers; + + /// The timing manager owned by this instrumentation (in case timing was + /// enabled by the user on the pass manager without providing an external + /// timing manager). This *must* appear before the `ownedTimingScope` to + /// ensure the timing manager is destroyed *after* the scope, since the latter + /// may hold a timer that points into the former. + std::unique_ptr ownedTimingManager; + TimingScope ownedTimingScope; + + /// The root timing scope into which timing is reported. + TimingScope &rootScope; +}; +} // namespace mlir::detail + +#endif // LIB_MLIR_PASS_PASSTIMING_H_ diff --git a/mlir/lib/Pass/PassTiming.cpp b/mlir/lib/Pass/PassTiming.cpp --- a/mlir/lib/Pass/PassTiming.cpp +++ b/mlir/lib/Pass/PassTiming.cpp @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// +#include "PassTiming.h" #include "PassDetail.h" #include "mlir/Pass/PassManager.h" #include "llvm/ADT/SmallVector.h" @@ -21,123 +22,94 @@ // PassTiming //===----------------------------------------------------------------------===// -namespace { -struct PassTiming : public PassInstrumentation { - PassTiming(TimingScope &timingScope) : rootScope(timingScope) {} - PassTiming(std::unique_ptr tm) - : ownedTimingManager(std::move(tm)), - ownedTimingScope(ownedTimingManager->getRootScope()), - rootScope(ownedTimingScope) {} - - /// If a pass can spawn additional work on other threads, it records the - /// index to its currently active timer here. Passes that run on a - /// newly-forked thread will check this list to find the active timer of the - /// parent thread into which the new thread should be nested. - DenseMap parentTimerIndices; - - /// A stack of the currently active timing scopes per thread. - DenseMap> activeThreadTimers; - - /// The timing manager owned by this instrumentation (in case timing was - /// enabled by the user on the pass manager without providing an external - /// timing manager). This *must* appear before the `ownedTimingScope` to - /// ensure the timing manager is destroyed *after* the scope, since the latter - /// may hold a timer that points into the former. - std::unique_ptr ownedTimingManager; - TimingScope ownedTimingScope; - - /// The root timing scope into which timing is reported. - TimingScope &rootScope; - - //===--------------------------------------------------------------------===// - // Pipeline - //===--------------------------------------------------------------------===// - - void runBeforePipeline(std::optional name, - const PipelineParentInfo &parentInfo) override { - auto tid = llvm::get_threadid(); - auto &activeTimers = activeThreadTimers[tid]; - - // Find the parent scope, either using the parent info or the root scope - // (e.g. in the case of the top-level pipeline). - TimingScope *parentScope; - auto it = parentTimerIndices.find(parentInfo); - if (it != parentTimerIndices.end()) - parentScope = &activeThreadTimers[parentInfo.parentThreadID][it->second]; - else - parentScope = &rootScope; - - // Use nullptr to anchor op-agnostic pipelines, otherwise use the name of - // the operation. - const void *timerId = name ? name->getAsOpaquePointer() : nullptr; - activeTimers.push_back(parentScope->nest(timerId, [name] { - return ("'" + (name ? name->getStringRef() : "any") + "' Pipeline").str(); - })); - } +Timer PassTiming::getActiveTimer() { + auto tid = llvm::get_threadid(); + auto &activeTimers = activeThreadTimers[tid]; + if (activeTimers.empty()) + return Timer(); + return activeTimers.back().getTimer(); +} - void runAfterPipeline(std::optional, - const PipelineParentInfo &) override { - auto &activeTimers = activeThreadTimers[llvm::get_threadid()]; - assert(!activeTimers.empty() && "expected active timer"); - activeTimers.pop_back(); - } +//===--------------------------------------------------------------------===// +// Pipeline + +void PassTiming::runBeforePipeline(std::optional name, + const PipelineParentInfo &parentInfo) { + auto tid = llvm::get_threadid(); + auto &activeTimers = activeThreadTimers[tid]; + + // Find the parent scope, either using the parent info or the root scope + // (e.g. in the case of the top-level pipeline). + TimingScope *parentScope; + auto it = parentTimerIndices.find(parentInfo); + if (it != parentTimerIndices.end()) + parentScope = &activeThreadTimers[parentInfo.parentThreadID][it->second]; + else + parentScope = &rootScope; + + // Use nullptr to anchor op-agnostic pipelines, otherwise use the name of + // the operation. + const void *timerId = name ? name->getAsOpaquePointer() : nullptr; + activeTimers.push_back(parentScope->nest(timerId, [name] { + return ("'" + (name ? name->getStringRef() : "any") + "' Pipeline").str(); + })); +} - //===--------------------------------------------------------------------===// - // Pass - //===--------------------------------------------------------------------===// - - void runBeforePass(Pass *pass, Operation *) override { - auto tid = llvm::get_threadid(); - auto &activeTimers = activeThreadTimers[tid]; - auto &parentScope = activeTimers.empty() ? rootScope : activeTimers.back(); - - if (auto *adaptor = dyn_cast(pass)) { - parentTimerIndices[{tid, pass}] = activeTimers.size(); - auto scope = - parentScope.nest(pass->getThreadingSiblingOrThis(), - [adaptor]() { return adaptor->getAdaptorName(); }); - if (adaptor->getPassManagers().size() <= 1) - scope.hide(); - activeTimers.push_back(std::move(scope)); - } else { - activeTimers.push_back( - parentScope.nest(pass->getThreadingSiblingOrThis(), - [pass]() { return std::string(pass->getName()); })); - } - } +void PassTiming::runAfterPipeline(std::optional, + const PipelineParentInfo &) { + auto &activeTimers = activeThreadTimers[llvm::get_threadid()]; + assert(!activeTimers.empty() && "expected active timer"); + activeTimers.pop_back(); +} - void runAfterPass(Pass *pass, Operation *) override { - auto tid = llvm::get_threadid(); - if (isa(pass)) - parentTimerIndices.erase({tid, pass}); - auto &activeTimers = activeThreadTimers[tid]; - assert(!activeTimers.empty() && "expected active timer"); - activeTimers.pop_back(); +//===--------------------------------------------------------------------===// +// Pass + +void PassTiming::runBeforePass(Pass *pass, Operation *) { + auto tid = llvm::get_threadid(); + auto &activeTimers = activeThreadTimers[tid]; + auto &parentScope = activeTimers.empty() ? rootScope : activeTimers.back(); + + if (auto *adaptor = dyn_cast(pass)) { + parentTimerIndices[{tid, pass}] = activeTimers.size(); + auto scope = + parentScope.nest(pass->getThreadingSiblingOrThis(), + [adaptor]() { return adaptor->getAdaptorName(); }); + if (adaptor->getPassManagers().size() <= 1) + scope.hide(); + activeTimers.push_back(std::move(scope)); + } else { + activeTimers.push_back( + parentScope.nest(pass->getThreadingSiblingOrThis(), + [pass]() { return std::string(pass->getName()); })); } +} - void runAfterPassFailed(Pass *pass, Operation *op) override { - runAfterPass(pass, op); - } +void PassTiming::runAfterPass(Pass *pass, Operation *) { + auto tid = llvm::get_threadid(); + if (isa(pass)) + parentTimerIndices.erase({tid, pass}); + auto &activeTimers = activeThreadTimers[tid]; + assert(!activeTimers.empty() && "expected active timer"); + activeTimers.pop_back(); +} - //===--------------------------------------------------------------------===// - // Analysis - //===--------------------------------------------------------------------===// +//===--------------------------------------------------------------------===// +// Analysis - void runBeforeAnalysis(StringRef name, TypeID id, Operation *) override { - auto tid = llvm::get_threadid(); - auto &activeTimers = activeThreadTimers[tid]; - auto &parentScope = activeTimers.empty() ? rootScope : activeTimers.back(); - activeTimers.push_back(parentScope.nest( - id.getAsOpaquePointer(), [name] { return "(A) " + name.str(); })); - } +void PassTiming::runBeforeAnalysis(StringRef name, TypeID id, Operation *) { + auto tid = llvm::get_threadid(); + auto &activeTimers = activeThreadTimers[tid]; + auto &parentScope = activeTimers.empty() ? rootScope : activeTimers.back(); + activeTimers.push_back(parentScope.nest( + id.getAsOpaquePointer(), [name] { return "(A) " + name.str(); })); +} - void runAfterAnalysis(StringRef, TypeID, Operation *) override { - auto &activeTimers = activeThreadTimers[llvm::get_threadid()]; - assert(!activeTimers.empty() && "expected active timer"); - activeTimers.pop_back(); - } -}; -} // namespace +void PassTiming::runAfterAnalysis(StringRef, TypeID, Operation *) { + auto &activeTimers = activeThreadTimers[llvm::get_threadid()]; + assert(!activeTimers.empty() && "expected active timer"); + activeTimers.pop_back(); +} //===----------------------------------------------------------------------===// // PassManager @@ -154,8 +126,9 @@ /// Add an instrumentation to time the execution of passes and the computation /// of analyses. void PassManager::enableTiming(std::unique_ptr tm) { + // If the timing manager isn't active, no need to add it. if (!tm->getRootTimer()) - return; // no need to keep the timing manager around if it's disabled + return; addInstrumentation(std::make_unique(std::move(tm))); } diff --git a/mlir/test/Pass/pass-timing.mlir b/mlir/test/Pass/pass-timing.mlir --- a/mlir/test/Pass/pass-timing.mlir +++ b/mlir/test/Pass/pass-timing.mlir @@ -1,5 +1,5 @@ // RUN: mlir-opt %s -mlir-disable-threading=true -verify-each=true -pass-pipeline='builtin.module(func.func(cse,canonicalize,cse))' -mlir-timing -mlir-timing-display=list 2>&1 | FileCheck -check-prefix=LIST %s -// RUN: mlir-opt %s -mlir-disable-threading=true -verify-each=true -pass-pipeline='builtin.module(func.func(cse,canonicalize,cse))' -mlir-timing -mlir-timing-display=tree 2>&1 | FileCheck -check-prefix=PIPELINE %s +// RUN: mlir-opt %s -mlir-disable-threading=true -verify-each=true -pass-pipeline='builtin.module(func.func(cse,canonicalize,cse,test-timingscope-pass))' -mlir-timing -mlir-timing-display=tree 2>&1 | FileCheck -check-prefix=PIPELINE %s // RUN: mlir-opt %s -mlir-disable-threading=false -verify-each=true -pass-pipeline='builtin.module(func.func(cse,canonicalize,cse))' -mlir-timing -mlir-timing-display=list 2>&1 | FileCheck -check-prefix=MT_LIST %s // RUN: mlir-opt %s -mlir-disable-threading=false -verify-each=true -pass-pipeline='builtin.module(func.func(cse,canonicalize,cse))' -mlir-timing -mlir-timing-display=tree 2>&1 | FileCheck -check-prefix=MT_PIPELINE %s // RUN: mlir-opt %s -mlir-disable-threading=true -verify-each=false -test-pm-nested-pipeline -mlir-timing -mlir-timing-display=tree 2>&1 | FileCheck -check-prefix=NESTED_PIPELINE %s @@ -22,6 +22,8 @@ // PIPELINE-NEXT: Canonicalizer // PIPELINE-NEXT: CSE // PIPELINE-NEXT: (A) DominanceInfo +// PIPELINE-NEXT: TestTimingScopePass +// PIPELINE-NEXT: Test Timing Scope // PIPELINE-NEXT: Output // PIPELINE-NEXT: Rest // PIPELINE-NEXT: Total diff --git a/mlir/test/lib/Pass/TestPassManager.cpp b/mlir/test/lib/Pass/TestPassManager.cpp --- a/mlir/test/lib/Pass/TestPassManager.cpp +++ b/mlir/test/lib/Pass/TestPassManager.cpp @@ -185,6 +185,19 @@ getOperation()->walk([&](Operation *) { ++opCountDuplicate; }); } }; + +/// A test pass that adds a nested timing scope. +struct TestTimingScopePass + : public PassWrapper> { + MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestTimingScopePass) + + TestTimingScopePass() = default; + TestTimingScopePass(const TestStatisticPass &) : PassWrapper() {} + StringRef getArgument() const final { return "test-timingscope-pass"; } + StringRef getDescription() const final { return "Test pass timing scopes"; } + + void runOnOperation() final { createTimingScope("Test Timing Scope"); } +}; } // namespace static void testNestedPipeline(OpPassManager &pm) { @@ -222,6 +235,8 @@ PassRegistration(); + PassRegistration(); + PassPipelineRegistration<>("test-pm-nested-pipeline", "Test a nested pipeline in the pass manager", testNestedPipeline);