1 //===- PassTimingInfo.cpp - LLVM Pass Timing Implementation ---------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file implements the LLVM Pass Timing infrastructure for both
10 // new and legacy pass managers.
12 // PassTimingInfo Class - This class is used to calculate information about the
13 // amount of time each pass takes to execute. This only happens when
14 // -time-passes is enabled on the command line.
16 //===----------------------------------------------------------------------===//
18 #include "llvm/IR/PassTimingInfo.h"
19 #include "llvm/ADT/Statistic.h"
20 #include "llvm/IR/PassInstrumentation.h"
21 #include "llvm/Pass.h"
22 #include "llvm/Support/CommandLine.h"
23 #include "llvm/Support/Debug.h"
24 #include "llvm/Support/FormatVariadic.h"
25 #include "llvm/Support/ManagedStatic.h"
26 #include "llvm/Support/Mutex.h"
27 #include "llvm/Support/TypeName.h"
28 #include "llvm/Support/raw_ostream.h"
33 #define DEBUG_TYPE "time-passes"
37 bool TimePassesIsEnabled
= false;
38 bool TimePassesPerRun
= false;
40 static cl::opt
<bool, true> EnableTiming(
41 "time-passes", cl::location(TimePassesIsEnabled
), cl::Hidden
,
42 cl::desc("Time each pass, printing elapsed time for each on exit"));
44 static cl::opt
<bool, true> EnableTimingPerRun(
45 "time-passes-per-run", cl::location(TimePassesPerRun
), cl::Hidden
,
46 cl::desc("Time each pass run, printing elapsed time for each run on exit"),
47 cl::callback([](const bool &) { TimePassesIsEnabled
= true; }));
52 //===----------------------------------------------------------------------===//
53 // Legacy pass manager's PassTimingInfo implementation
55 /// Provides an interface for collecting pass timing information.
57 /// It was intended to be generic but now we decided to split
58 /// interfaces completely. This is now exclusively for legacy-pass-manager use.
59 class PassTimingInfo
{
61 using PassInstanceID
= void *;
64 StringMap
<unsigned> PassIDCountMap
; ///< Map that counts instances of passes
65 DenseMap
<PassInstanceID
, std::unique_ptr
<Timer
>> TimingData
; ///< timers for pass instances
69 /// Default constructor for yet-inactive timeinfo.
70 /// Use \p init() to activate it.
73 /// Print out timing information and release timers.
76 /// Initializes the static \p TheTimeInfo member to a non-null value when
77 /// -time-passes is enabled. Leaves it null otherwise.
79 /// This method may be called multiple times.
82 /// Prints out timing information and then resets the timers.
83 /// By default it uses the stream created by CreateInfoOutputFile().
84 void print(raw_ostream
*OutStream
= nullptr);
86 /// Returns the timer for the specified pass if it exists.
87 Timer
*getPassTimer(Pass
*, PassInstanceID
);
89 static PassTimingInfo
*TheTimeInfo
;
92 Timer
*newPassTimer(StringRef PassID
, StringRef PassDesc
);
95 static ManagedStatic
<sys::SmartMutex
<true>> TimingInfoMutex
;
97 PassTimingInfo::PassTimingInfo() : TG("pass", "Pass execution timing report") {}
99 PassTimingInfo::~PassTimingInfo() {
100 // Deleting the timers accumulates their info into the TG member.
101 // Then TG member is (implicitly) deleted, actually printing the report.
105 void PassTimingInfo::init() {
106 if (!TimePassesIsEnabled
|| TheTimeInfo
)
109 // Constructed the first time this is called, iff -time-passes is enabled.
110 // This guarantees that the object will be constructed after static globals,
111 // thus it will be destroyed before them.
112 static ManagedStatic
<PassTimingInfo
> TTI
;
116 /// Prints out timing information and then resets the timers.
117 void PassTimingInfo::print(raw_ostream
*OutStream
) {
118 TG
.print(OutStream
? *OutStream
: *CreateInfoOutputFile(), true);
121 Timer
*PassTimingInfo::newPassTimer(StringRef PassID
, StringRef PassDesc
) {
122 unsigned &num
= PassIDCountMap
[PassID
];
124 // Appending description with a pass-instance number for all but the first one
125 std::string PassDescNumbered
=
126 num
<= 1 ? PassDesc
.str() : formatv("{0} #{1}", PassDesc
, num
).str();
127 return new Timer(PassID
, PassDescNumbered
, TG
);
130 Timer
*PassTimingInfo::getPassTimer(Pass
*P
, PassInstanceID Pass
) {
131 if (P
->getAsPMDataManager())
135 sys::SmartScopedLock
<true> Lock(*TimingInfoMutex
);
136 std::unique_ptr
<Timer
> &T
= TimingData
[Pass
];
139 StringRef PassName
= P
->getPassName();
140 StringRef PassArgument
;
141 if (const PassInfo
*PI
= Pass::lookupPassInfo(P
->getPassID()))
142 PassArgument
= PI
->getPassArgument();
143 T
.reset(newPassTimer(PassArgument
.empty() ? PassName
: PassArgument
, PassName
));
148 PassTimingInfo
*PassTimingInfo::TheTimeInfo
;
149 } // namespace legacy
152 Timer
*getPassTimer(Pass
*P
) {
153 legacy::PassTimingInfo::init();
154 if (legacy::PassTimingInfo::TheTimeInfo
)
155 return legacy::PassTimingInfo::TheTimeInfo
->getPassTimer(P
, P
);
159 /// If timing is enabled, report the times collected up to now and then reset
161 void reportAndResetTimings(raw_ostream
*OutStream
) {
162 if (legacy::PassTimingInfo::TheTimeInfo
)
163 legacy::PassTimingInfo::TheTimeInfo
->print(OutStream
);
166 //===----------------------------------------------------------------------===//
167 // Pass timing handling for the New Pass Manager
168 //===----------------------------------------------------------------------===//
170 /// Returns the timer for the specified pass invocation of \p PassID.
171 /// Each time it creates a new timer.
172 Timer
&TimePassesHandler::getPassTimer(StringRef PassID
, bool IsPass
) {
173 TimerGroup
&TG
= IsPass
? PassTG
: AnalysisTG
;
175 TimerVector
&Timers
= TimingData
[PassID
];
176 if (Timers
.size() == 0)
177 Timers
.emplace_back(new Timer(PassID
, PassID
, TG
));
178 return *Timers
.front();
181 // Take a vector of Timers created for this \p PassID and append
182 // one more timer to it.
183 TimerVector
&Timers
= TimingData
[PassID
];
184 unsigned Count
= Timers
.size() + 1;
186 std::string FullDesc
= formatv("{0} #{1}", PassID
, Count
).str();
188 Timer
*T
= new Timer(PassID
, FullDesc
, TG
);
189 Timers
.emplace_back(T
);
190 assert(Count
== Timers
.size() && "Timers vector not adjusted correctly.");
195 TimePassesHandler::TimePassesHandler(bool Enabled
, bool PerRun
)
196 : PassTG("pass", "Pass execution timing report"),
197 AnalysisTG("analysis", "Analysis execution timing report"),
198 Enabled(Enabled
), PerRun(PerRun
) {}
200 TimePassesHandler::TimePassesHandler()
201 : TimePassesHandler(TimePassesIsEnabled
, TimePassesPerRun
) {}
203 void TimePassesHandler::setOutStream(raw_ostream
&Out
) {
207 void TimePassesHandler::print() {
210 std::unique_ptr
<raw_ostream
> MaybeCreated
;
211 raw_ostream
*OS
= OutStream
;
215 MaybeCreated
= CreateInfoOutputFile();
218 PassTG
.print(*OS
, true);
219 AnalysisTG
.print(*OS
, true);
222 LLVM_DUMP_METHOD
void TimePassesHandler::dump() const {
223 dbgs() << "Dumping timers for " << getTypeName
<TimePassesHandler
>()
224 << ":\n\tRunning:\n";
225 for (auto &I
: TimingData
) {
226 StringRef PassID
= I
.getKey();
227 const TimerVector
& MyTimers
= I
.getValue();
228 for (unsigned idx
= 0; idx
< MyTimers
.size(); idx
++) {
229 const Timer
* MyTimer
= MyTimers
[idx
].get();
230 if (MyTimer
&& MyTimer
->isRunning())
231 dbgs() << "\tTimer " << MyTimer
<< " for pass " << PassID
<< "(" << idx
<< ")\n";
234 dbgs() << "\tTriggered:\n";
235 for (auto &I
: TimingData
) {
236 StringRef PassID
= I
.getKey();
237 const TimerVector
& MyTimers
= I
.getValue();
238 for (unsigned idx
= 0; idx
< MyTimers
.size(); idx
++) {
239 const Timer
* MyTimer
= MyTimers
[idx
].get();
240 if (MyTimer
&& MyTimer
->hasTriggered() && !MyTimer
->isRunning())
241 dbgs() << "\tTimer " << MyTimer
<< " for pass " << PassID
<< "(" << idx
<< ")\n";
246 static bool shouldIgnorePass(StringRef PassID
) {
247 return isSpecialPass(PassID
,
248 {"PassManager", "PassAdaptor", "AnalysisManagerProxy",
249 "ModuleInlinerWrapperPass", "DevirtSCCRepeatedPass"});
252 void TimePassesHandler::startPassTimer(StringRef PassID
) {
253 if (shouldIgnorePass(PassID
))
255 // Stop the previous pass timer to prevent double counting when a
256 // pass requests another pass.
257 if (!PassActiveTimerStack
.empty()) {
258 assert(PassActiveTimerStack
.back()->isRunning());
259 PassActiveTimerStack
.back()->stopTimer();
261 Timer
&MyTimer
= getPassTimer(PassID
, /*IsPass*/ true);
262 PassActiveTimerStack
.push_back(&MyTimer
);
263 assert(!MyTimer
.isRunning());
264 MyTimer
.startTimer();
267 void TimePassesHandler::stopPassTimer(StringRef PassID
) {
268 if (shouldIgnorePass(PassID
))
270 assert(!PassActiveTimerStack
.empty() && "empty stack in popTimer");
271 Timer
*MyTimer
= PassActiveTimerStack
.pop_back_val();
272 assert(MyTimer
&& "timer should be present");
273 assert(MyTimer
->isRunning());
274 MyTimer
->stopTimer();
276 // Restart the previously stopped timer.
277 if (!PassActiveTimerStack
.empty()) {
278 assert(!PassActiveTimerStack
.back()->isRunning());
279 PassActiveTimerStack
.back()->startTimer();
283 void TimePassesHandler::startAnalysisTimer(StringRef PassID
) {
284 // Stop the previous analysis timer to prevent double counting when an
285 // analysis requests another analysis.
286 if (!AnalysisActiveTimerStack
.empty()) {
287 assert(AnalysisActiveTimerStack
.back()->isRunning());
288 AnalysisActiveTimerStack
.back()->stopTimer();
291 Timer
&MyTimer
= getPassTimer(PassID
, /*IsPass*/ false);
292 AnalysisActiveTimerStack
.push_back(&MyTimer
);
293 if (!MyTimer
.isRunning())
294 MyTimer
.startTimer();
297 void TimePassesHandler::stopAnalysisTimer(StringRef PassID
) {
298 assert(!AnalysisActiveTimerStack
.empty() && "empty stack in popTimer");
299 Timer
*MyTimer
= AnalysisActiveTimerStack
.pop_back_val();
300 assert(MyTimer
&& "timer should be present");
301 if (MyTimer
->isRunning())
302 MyTimer
->stopTimer();
304 // Restart the previously stopped timer.
305 if (!AnalysisActiveTimerStack
.empty()) {
306 assert(!AnalysisActiveTimerStack
.back()->isRunning());
307 AnalysisActiveTimerStack
.back()->startTimer();
311 void TimePassesHandler::registerCallbacks(PassInstrumentationCallbacks
&PIC
) {
315 PIC
.registerBeforeNonSkippedPassCallback(
316 [this](StringRef P
, Any
) { this->startPassTimer(P
); });
317 PIC
.registerAfterPassCallback(
318 [this](StringRef P
, Any
, const PreservedAnalyses
&) {
319 this->stopPassTimer(P
);
321 PIC
.registerAfterPassInvalidatedCallback(
322 [this](StringRef P
, const PreservedAnalyses
&) {
323 this->stopPassTimer(P
);
325 PIC
.registerBeforeAnalysisCallback(
326 [this](StringRef P
, Any
) { this->startAnalysisTimer(P
); });
327 PIC
.registerAfterAnalysisCallback(
328 [this](StringRef P
, Any
) { this->stopAnalysisTimer(P
); });