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-exception 6 // 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 <chrono> 15 #include <optional> 16 17 using namespace mlir; 18 using namespace mlir::detail; 19 20 //===----------------------------------------------------------------------===// 21 // PassTiming 22 //===----------------------------------------------------------------------===// 23 24 namespace { 25 struct PassTiming : public PassInstrumentation { 26 PassTiming(TimingScope &timingScope) : rootScope(timingScope) {} 27 PassTiming(std::unique_ptr<TimingManager> tm) 28 : ownedTimingManager(std::move(tm)), 29 ownedTimingScope(ownedTimingManager->getRootScope()), 30 rootScope(ownedTimingScope) {} 31 32 /// If a pass can spawn additional work on other threads, it records the 33 /// index to its currently active timer here. Passes that run on a 34 /// newly-forked thread will check this list to find the active timer of the 35 /// parent thread into which the new thread should be nested. 36 DenseMap<PipelineParentInfo, unsigned> parentTimerIndices; 37 38 /// The timing manager owned by this instrumentation (in case timing was 39 /// enabled by the user on the pass manager without providing an external 40 /// timing manager). This *must* appear before the `ownedTimingScope` to 41 /// ensure the timing manager is destroyed *after* the scope, since the latter 42 /// may hold a timer that points into the former. 43 std::unique_ptr<TimingManager> ownedTimingManager; 44 TimingScope ownedTimingScope; 45 46 /// A stack of the currently active timing scopes per thread. 47 DenseMap<uint64_t, SmallVector<TimingScope, 4>> activeThreadTimers; 48 49 /// The root timing scope into which timing is reported. 50 TimingScope &rootScope; 51 52 //===--------------------------------------------------------------------===// 53 // Pipeline 54 //===--------------------------------------------------------------------===// 55 56 void runBeforePipeline(std::optional<OperationName> name, 57 const PipelineParentInfo &parentInfo) override { 58 auto tid = llvm::get_threadid(); 59 auto &activeTimers = activeThreadTimers[tid]; 60 61 // Find the parent scope, either using the parent info or the root scope 62 // (e.g. in the case of the top-level pipeline). 63 TimingScope *parentScope; 64 auto it = parentTimerIndices.find(parentInfo); 65 if (it != parentTimerIndices.end()) 66 parentScope = &activeThreadTimers[parentInfo.parentThreadID][it->second]; 67 else 68 parentScope = &rootScope; 69 70 // Use nullptr to anchor op-agnostic pipelines, otherwise use the name of 71 // the operation. 72 const void *timerId = name ? name->getAsOpaquePointer() : nullptr; 73 activeTimers.push_back(parentScope->nest(timerId, [name] { 74 return ("'" + (name ? name->getStringRef() : "any") + "' Pipeline").str(); 75 })); 76 } 77 78 void runAfterPipeline(std::optional<OperationName>, 79 const PipelineParentInfo &) override { 80 auto &activeTimers = activeThreadTimers[llvm::get_threadid()]; 81 assert(!activeTimers.empty() && "expected active timer"); 82 activeTimers.pop_back(); 83 } 84 85 //===--------------------------------------------------------------------===// 86 // Pass 87 //===--------------------------------------------------------------------===// 88 89 void runBeforePass(Pass *pass, Operation *) override { 90 auto tid = llvm::get_threadid(); 91 auto &activeTimers = activeThreadTimers[tid]; 92 auto &parentScope = activeTimers.empty() ? rootScope : activeTimers.back(); 93 94 if (auto *adaptor = dyn_cast<OpToOpPassAdaptor>(pass)) { 95 parentTimerIndices[{tid, pass}] = activeTimers.size(); 96 auto scope = 97 parentScope.nest(pass->getThreadingSiblingOrThis(), 98 [adaptor]() { return adaptor->getAdaptorName(); }); 99 if (adaptor->getPassManagers().size() <= 1) 100 scope.hide(); 101 activeTimers.push_back(std::move(scope)); 102 } else { 103 activeTimers.push_back( 104 parentScope.nest(pass->getThreadingSiblingOrThis(), 105 [pass]() { return std::string(pass->getName()); })); 106 } 107 } 108 109 void runAfterPass(Pass *pass, Operation *) override { 110 auto tid = llvm::get_threadid(); 111 if (isa<OpToOpPassAdaptor>(pass)) 112 parentTimerIndices.erase({tid, pass}); 113 auto &activeTimers = activeThreadTimers[tid]; 114 assert(!activeTimers.empty() && "expected active timer"); 115 activeTimers.pop_back(); 116 } 117 118 void runAfterPassFailed(Pass *pass, Operation *op) override { 119 runAfterPass(pass, op); 120 } 121 122 //===--------------------------------------------------------------------===// 123 // Analysis 124 //===--------------------------------------------------------------------===// 125 126 void runBeforeAnalysis(StringRef name, TypeID id, Operation *) override { 127 auto tid = llvm::get_threadid(); 128 auto &activeTimers = activeThreadTimers[tid]; 129 auto &parentScope = activeTimers.empty() ? rootScope : activeTimers.back(); 130 activeTimers.push_back(parentScope.nest( 131 id.getAsOpaquePointer(), [name] { return "(A) " + name.str(); })); 132 } 133 134 void runAfterAnalysis(StringRef, TypeID, Operation *) override { 135 auto &activeTimers = activeThreadTimers[llvm::get_threadid()]; 136 assert(!activeTimers.empty() && "expected active timer"); 137 activeTimers.pop_back(); 138 } 139 }; 140 } // namespace 141 142 //===----------------------------------------------------------------------===// 143 // PassManager 144 //===----------------------------------------------------------------------===// 145 146 /// Add an instrumentation to time the execution of passes and the computation 147 /// of analyses. 148 void PassManager::enableTiming(TimingScope &timingScope) { 149 if (!timingScope) 150 return; 151 addInstrumentation(std::make_unique<PassTiming>(timingScope)); 152 } 153 154 /// Add an instrumentation to time the execution of passes and the computation 155 /// of analyses. 156 void PassManager::enableTiming(std::unique_ptr<TimingManager> tm) { 157 if (!tm->getRootTimer()) 158 return; // no need to keep the timing manager around if it's disabled 159 addInstrumentation(std::make_unique<PassTiming>(std::move(tm))); 160 } 161 162 /// Add an instrumentation to time the execution of passes and the computation 163 /// of analyses. 164 void PassManager::enableTiming() { 165 auto tm = std::make_unique<DefaultTimingManager>(); 166 tm->setEnabled(true); 167 enableTiming(std::move(tm)); 168 } 169