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 {
PassTiming__anon38759e540111::PassTiming25   PassTiming(TimingScope &timingScope) : rootScope(timingScope) {}
PassTiming__anon38759e540111::PassTiming26   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 
runBeforePipeline__anon38759e540111::PassTiming55   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 
runAfterPipeline__anon38759e540111::PassTiming76   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 
runBeforePass__anon38759e540111::PassTiming86   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 
runAfterPass__anon38759e540111::PassTiming106   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 
runAfterPassFailed__anon38759e540111::PassTiming115   void runAfterPassFailed(Pass *pass, Operation *op) override {
116     runAfterPass(pass, op);
117   }
118 
119   //===--------------------------------------------------------------------===//
120   // Analysis
121   //===--------------------------------------------------------------------===//
122 
runBeforeAnalysis__anon38759e540111::PassTiming123   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 
runAfterAnalysis__anon38759e540111::PassTiming131   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.
enableTiming(TimingScope & timingScope)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.
enableTiming(std::unique_ptr<TimingManager> tm)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.
enableTiming()161 void PassManager::enableTiming() {
162   auto tm = std::make_unique<DefaultTimingManager>();
163   tm->setEnabled(true);
164   enableTiming(std::move(tm));
165 }
166