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