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
17using namespace mlir;
18using namespace mlir::detail;
19
20//===----------------------------------------------------------------------===//
21// PassTiming
22//===----------------------------------------------------------------------===//
23
24namespace {
25struct 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 /// A stack of the currently active timing scopes per thread.
39 DenseMap<uint64_t, SmallVector<TimingScope, 4>> activeThreadTimers;
40
41 /// The timing manager owned by this instrumentation (in case timing was
42 /// enabled by the user on the pass manager without providing an external
43 /// timing manager). This *must* appear before the `ownedTimingScope` to
44 /// ensure the timing manager is destroyed *after* the scope, since the latter
45 /// may hold a timer that points into the former.
46 std::unique_ptr<TimingManager> ownedTimingManager;
47 TimingScope ownedTimingScope;
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(Val: 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(Elt: parentScope->nest(args: timerId, args: [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>(Val: pass)) {
95 parentTimerIndices[{.parentThreadID: tid, .parentPass: pass}] = activeTimers.size();
96 auto scope =
97 parentScope.nest(args: pass->getThreadingSiblingOrThis(),
98 args: [adaptor]() { return adaptor->getAdaptorName(); });
99 if (adaptor->getPassManagers().size() <= 1)
100 scope.hide();
101 activeTimers.push_back(Elt: std::move(scope));
102 } else {
103 activeTimers.push_back(
104 Elt: parentScope.nest(args: pass->getThreadingSiblingOrThis(),
105 args: [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>(Val: pass))
112 parentTimerIndices.erase(Val: {.parentThreadID: tid, .parentPass: 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(Elt: parentScope.nest(
131 args: id.getAsOpaquePointer(), args: [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.
148void PassManager::enableTiming(TimingScope &timingScope) {
149 if (!timingScope)
150 return;
151 addInstrumentation(pi: std::make_unique<PassTiming>(args&: timingScope));
152}
153
154/// Add an instrumentation to time the execution of passes and the computation
155/// of analyses.
156void 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(pi: std::make_unique<PassTiming>(args: std::move(tm)));
160}
161
162/// Add an instrumentation to time the execution of passes and the computation
163/// of analyses.
164void PassManager::enableTiming() {
165 auto tm = std::make_unique<DefaultTimingManager>();
166 tm->setEnabled(true);
167 enableTiming(tm: std::move(tm));
168}
169

source code of mlir/lib/Pass/PassTiming.cpp