168 lines · cpp
1//===- PassTiming.cpp -----------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "PassDetail.h"10#include "mlir/Pass/PassManager.h"11#include "llvm/ADT/SmallVector.h"12#include "llvm/Support/Threading.h"13 14#include <optional>15 16using namespace mlir;17using namespace mlir::detail;18 19//===----------------------------------------------------------------------===//20// PassTiming21//===----------------------------------------------------------------------===//22 23namespace {24struct PassTiming : public PassInstrumentation {25 PassTiming(TimingScope &timingScope) : rootScope(timingScope) {}26 PassTiming(std::unique_ptr<TimingManager> tm)27 : ownedTimingManager(std::move(tm)),28 ownedTimingScope(ownedTimingManager->getRootScope()),29 rootScope(ownedTimingScope) {}30 31 /// If a pass can spawn additional work on other threads, it records the32 /// index to its currently active timer here. Passes that run on a33 /// newly-forked thread will check this list to find the active timer of the34 /// parent thread into which the new thread should be nested.35 DenseMap<PipelineParentInfo, unsigned> parentTimerIndices;36 37 /// The timing manager owned by this instrumentation (in case timing was38 /// enabled by the user on the pass manager without providing an external39 /// timing manager). This *must* appear before the `ownedTimingScope` to40 /// ensure the timing manager is destroyed *after* the scope, since the latter41 /// may hold a timer that points into the former.42 std::unique_ptr<TimingManager> ownedTimingManager;43 TimingScope ownedTimingScope;44 45 /// A stack of the currently active timing scopes per thread.46 DenseMap<uint64_t, SmallVector<TimingScope, 4>> activeThreadTimers;47 48 /// The root timing scope into which timing is reported.49 TimingScope &rootScope;50 51 //===--------------------------------------------------------------------===//52 // Pipeline53 //===--------------------------------------------------------------------===//54 55 void runBeforePipeline(std::optional<OperationName> name,56 const PipelineParentInfo &parentInfo) override {57 auto tid = llvm::get_threadid();58 auto &activeTimers = activeThreadTimers[tid];59 60 // Find the parent scope, either using the parent info or the root scope61 // (e.g. in the case of the top-level pipeline).62 TimingScope *parentScope;63 auto it = parentTimerIndices.find(parentInfo);64 if (it != parentTimerIndices.end())65 parentScope = &activeThreadTimers[parentInfo.parentThreadID][it->second];66 else67 parentScope = &rootScope;68 69 // Use nullptr to anchor op-agnostic pipelines, otherwise use the name of70 // the operation.71 const void *timerId = name ? name->getAsOpaquePointer() : nullptr;72 activeTimers.push_back(parentScope->nest(timerId, [name] {73 return ("'" + (name ? name->getStringRef() : "any") + "' Pipeline").str();74 }));75 }76 77 void runAfterPipeline(std::optional<OperationName>,78 const PipelineParentInfo &) override {79 auto &activeTimers = activeThreadTimers[llvm::get_threadid()];80 assert(!activeTimers.empty() && "expected active timer");81 activeTimers.pop_back();82 }83 84 //===--------------------------------------------------------------------===//85 // Pass86 //===--------------------------------------------------------------------===//87 88 void runBeforePass(Pass *pass, Operation *) override {89 auto tid = llvm::get_threadid();90 auto &activeTimers = activeThreadTimers[tid];91 auto &parentScope = activeTimers.empty() ? rootScope : activeTimers.back();92 93 if (auto *adaptor = dyn_cast<OpToOpPassAdaptor>(pass)) {94 parentTimerIndices[{tid, pass}] = activeTimers.size();95 auto scope =96 parentScope.nest(pass->getThreadingSiblingOrThis(),97 [adaptor]() { return adaptor->getAdaptorName(); });98 if (adaptor->getPassManagers().size() <= 1)99 scope.hide();100 activeTimers.push_back(std::move(scope));101 } else {102 activeTimers.push_back(103 parentScope.nest(pass->getThreadingSiblingOrThis(),104 [pass]() { return std::string(pass->getName()); }));105 }106 }107 108 void runAfterPass(Pass *pass, Operation *) override {109 auto tid = llvm::get_threadid();110 if (isa<OpToOpPassAdaptor>(pass))111 parentTimerIndices.erase({tid, pass});112 auto &activeTimers = activeThreadTimers[tid];113 assert(!activeTimers.empty() && "expected active timer");114 activeTimers.pop_back();115 }116 117 void runAfterPassFailed(Pass *pass, Operation *op) override {118 runAfterPass(pass, op);119 }120 121 //===--------------------------------------------------------------------===//122 // Analysis123 //===--------------------------------------------------------------------===//124 125 void runBeforeAnalysis(StringRef name, TypeID id, Operation *) override {126 auto tid = llvm::get_threadid();127 auto &activeTimers = activeThreadTimers[tid];128 auto &parentScope = activeTimers.empty() ? rootScope : activeTimers.back();129 activeTimers.push_back(parentScope.nest(130 id.getAsOpaquePointer(), [name] { return "(A) " + name.str(); }));131 }132 133 void runAfterAnalysis(StringRef, TypeID, Operation *) override {134 auto &activeTimers = activeThreadTimers[llvm::get_threadid()];135 assert(!activeTimers.empty() && "expected active timer");136 activeTimers.pop_back();137 }138};139} // namespace140 141//===----------------------------------------------------------------------===//142// PassManager143//===----------------------------------------------------------------------===//144 145/// Add an instrumentation to time the execution of passes and the computation146/// of analyses.147void PassManager::enableTiming(TimingScope &timingScope) {148 if (!timingScope)149 return;150 addInstrumentation(std::make_unique<PassTiming>(timingScope));151}152 153/// Add an instrumentation to time the execution of passes and the computation154/// of analyses.155void PassManager::enableTiming(std::unique_ptr<TimingManager> tm) {156 if (!tm->getRootTimer())157 return; // no need to keep the timing manager around if it's disabled158 addInstrumentation(std::make_unique<PassTiming>(std::move(tm)));159}160 161/// Add an instrumentation to time the execution of passes and the computation162/// of analyses.163void PassManager::enableTiming() {164 auto tm = std::make_unique<DefaultTimingManager>();165 tm->setEnabled(true);166 enableTiming(std::move(tm));167}168