xref: /freebsd/contrib/llvm-project/llvm/lib/IR/PassTimingInfo.cpp (revision 0b57cec536236d46e3dba9bd041533462f33dbb7)
1*0b57cec5SDimitry Andric //===- PassTimingInfo.cpp - LLVM Pass Timing Implementation ---------------===//
2*0b57cec5SDimitry Andric //
3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0b57cec5SDimitry Andric //
7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8*0b57cec5SDimitry Andric //
9*0b57cec5SDimitry Andric // This file implements the LLVM Pass Timing infrastructure for both
10*0b57cec5SDimitry Andric // new and legacy pass managers.
11*0b57cec5SDimitry Andric //
12*0b57cec5SDimitry Andric // PassTimingInfo Class - This class is used to calculate information about the
13*0b57cec5SDimitry Andric // amount of time each pass takes to execute.  This only happens when
14*0b57cec5SDimitry Andric // -time-passes is enabled on the command line.
15*0b57cec5SDimitry Andric //
16*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
17*0b57cec5SDimitry Andric 
18*0b57cec5SDimitry Andric #include "llvm/IR/PassTimingInfo.h"
19*0b57cec5SDimitry Andric #include "llvm/ADT/DenseMap.h"
20*0b57cec5SDimitry Andric #include "llvm/ADT/Statistic.h"
21*0b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
22*0b57cec5SDimitry Andric #include "llvm/IR/PassInstrumentation.h"
23*0b57cec5SDimitry Andric #include "llvm/Pass.h"
24*0b57cec5SDimitry Andric #include "llvm/Support/CommandLine.h"
25*0b57cec5SDimitry Andric #include "llvm/Support/Debug.h"
26*0b57cec5SDimitry Andric #include "llvm/Support/FormatVariadic.h"
27*0b57cec5SDimitry Andric #include "llvm/Support/ManagedStatic.h"
28*0b57cec5SDimitry Andric #include "llvm/Support/Mutex.h"
29*0b57cec5SDimitry Andric #include "llvm/Support/Timer.h"
30*0b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
31*0b57cec5SDimitry Andric #include <memory>
32*0b57cec5SDimitry Andric #include <string>
33*0b57cec5SDimitry Andric 
34*0b57cec5SDimitry Andric using namespace llvm;
35*0b57cec5SDimitry Andric 
36*0b57cec5SDimitry Andric #define DEBUG_TYPE "time-passes"
37*0b57cec5SDimitry Andric 
38*0b57cec5SDimitry Andric namespace llvm {
39*0b57cec5SDimitry Andric 
40*0b57cec5SDimitry Andric bool TimePassesIsEnabled = false;
41*0b57cec5SDimitry Andric 
42*0b57cec5SDimitry Andric static cl::opt<bool, true> EnableTiming(
43*0b57cec5SDimitry Andric     "time-passes", cl::location(TimePassesIsEnabled), cl::Hidden,
44*0b57cec5SDimitry Andric     cl::desc("Time each pass, printing elapsed time for each on exit"));
45*0b57cec5SDimitry Andric 
46*0b57cec5SDimitry Andric namespace {
47*0b57cec5SDimitry Andric namespace legacy {
48*0b57cec5SDimitry Andric 
49*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
50*0b57cec5SDimitry Andric // Legacy pass manager's PassTimingInfo implementation
51*0b57cec5SDimitry Andric 
52*0b57cec5SDimitry Andric /// Provides an interface for collecting pass timing information.
53*0b57cec5SDimitry Andric ///
54*0b57cec5SDimitry Andric /// It was intended to be generic but now we decided to split
55*0b57cec5SDimitry Andric /// interfaces completely. This is now exclusively for legacy-pass-manager use.
56*0b57cec5SDimitry Andric class PassTimingInfo {
57*0b57cec5SDimitry Andric public:
58*0b57cec5SDimitry Andric   using PassInstanceID = void *;
59*0b57cec5SDimitry Andric 
60*0b57cec5SDimitry Andric private:
61*0b57cec5SDimitry Andric   StringMap<unsigned> PassIDCountMap; ///< Map that counts instances of passes
62*0b57cec5SDimitry Andric   DenseMap<PassInstanceID, std::unique_ptr<Timer>> TimingData; ///< timers for pass instances
63*0b57cec5SDimitry Andric   TimerGroup TG;
64*0b57cec5SDimitry Andric 
65*0b57cec5SDimitry Andric public:
66*0b57cec5SDimitry Andric   /// Default constructor for yet-inactive timeinfo.
67*0b57cec5SDimitry Andric   /// Use \p init() to activate it.
68*0b57cec5SDimitry Andric   PassTimingInfo();
69*0b57cec5SDimitry Andric 
70*0b57cec5SDimitry Andric   /// Print out timing information and release timers.
71*0b57cec5SDimitry Andric   ~PassTimingInfo();
72*0b57cec5SDimitry Andric 
73*0b57cec5SDimitry Andric   /// Initializes the static \p TheTimeInfo member to a non-null value when
74*0b57cec5SDimitry Andric   /// -time-passes is enabled. Leaves it null otherwise.
75*0b57cec5SDimitry Andric   ///
76*0b57cec5SDimitry Andric   /// This method may be called multiple times.
77*0b57cec5SDimitry Andric   static void init();
78*0b57cec5SDimitry Andric 
79*0b57cec5SDimitry Andric   /// Prints out timing information and then resets the timers.
80*0b57cec5SDimitry Andric   /// By default it uses the stream created by CreateInfoOutputFile().
81*0b57cec5SDimitry Andric   void print(raw_ostream *OutStream = nullptr);
82*0b57cec5SDimitry Andric 
83*0b57cec5SDimitry Andric   /// Returns the timer for the specified pass if it exists.
84*0b57cec5SDimitry Andric   Timer *getPassTimer(Pass *, PassInstanceID);
85*0b57cec5SDimitry Andric 
86*0b57cec5SDimitry Andric   static PassTimingInfo *TheTimeInfo;
87*0b57cec5SDimitry Andric 
88*0b57cec5SDimitry Andric private:
89*0b57cec5SDimitry Andric   Timer *newPassTimer(StringRef PassID, StringRef PassDesc);
90*0b57cec5SDimitry Andric };
91*0b57cec5SDimitry Andric 
92*0b57cec5SDimitry Andric static ManagedStatic<sys::SmartMutex<true>> TimingInfoMutex;
93*0b57cec5SDimitry Andric 
94*0b57cec5SDimitry Andric PassTimingInfo::PassTimingInfo()
95*0b57cec5SDimitry Andric     : TG("pass", "... Pass execution timing report ...") {}
96*0b57cec5SDimitry Andric 
97*0b57cec5SDimitry Andric PassTimingInfo::~PassTimingInfo() {
98*0b57cec5SDimitry Andric   // Deleting the timers accumulates their info into the TG member.
99*0b57cec5SDimitry Andric   // Then TG member is (implicitly) deleted, actually printing the report.
100*0b57cec5SDimitry Andric   TimingData.clear();
101*0b57cec5SDimitry Andric }
102*0b57cec5SDimitry Andric 
103*0b57cec5SDimitry Andric void PassTimingInfo::init() {
104*0b57cec5SDimitry Andric   if (!TimePassesIsEnabled || TheTimeInfo)
105*0b57cec5SDimitry Andric     return;
106*0b57cec5SDimitry Andric 
107*0b57cec5SDimitry Andric   // Constructed the first time this is called, iff -time-passes is enabled.
108*0b57cec5SDimitry Andric   // This guarantees that the object will be constructed after static globals,
109*0b57cec5SDimitry Andric   // thus it will be destroyed before them.
110*0b57cec5SDimitry Andric   static ManagedStatic<PassTimingInfo> TTI;
111*0b57cec5SDimitry Andric   TheTimeInfo = &*TTI;
112*0b57cec5SDimitry Andric }
113*0b57cec5SDimitry Andric 
114*0b57cec5SDimitry Andric /// Prints out timing information and then resets the timers.
115*0b57cec5SDimitry Andric void PassTimingInfo::print(raw_ostream *OutStream) {
116*0b57cec5SDimitry Andric   TG.print(OutStream ? *OutStream : *CreateInfoOutputFile(), true);
117*0b57cec5SDimitry Andric }
118*0b57cec5SDimitry Andric 
119*0b57cec5SDimitry Andric Timer *PassTimingInfo::newPassTimer(StringRef PassID, StringRef PassDesc) {
120*0b57cec5SDimitry Andric   unsigned &num = PassIDCountMap[PassID];
121*0b57cec5SDimitry Andric   num++;
122*0b57cec5SDimitry Andric   // Appending description with a pass-instance number for all but the first one
123*0b57cec5SDimitry Andric   std::string PassDescNumbered =
124*0b57cec5SDimitry Andric       num <= 1 ? PassDesc.str() : formatv("{0} #{1}", PassDesc, num).str();
125*0b57cec5SDimitry Andric   return new Timer(PassID, PassDescNumbered, TG);
126*0b57cec5SDimitry Andric }
127*0b57cec5SDimitry Andric 
128*0b57cec5SDimitry Andric Timer *PassTimingInfo::getPassTimer(Pass *P, PassInstanceID Pass) {
129*0b57cec5SDimitry Andric   if (P->getAsPMDataManager())
130*0b57cec5SDimitry Andric     return nullptr;
131*0b57cec5SDimitry Andric 
132*0b57cec5SDimitry Andric   init();
133*0b57cec5SDimitry Andric   sys::SmartScopedLock<true> Lock(*TimingInfoMutex);
134*0b57cec5SDimitry Andric   std::unique_ptr<Timer> &T = TimingData[Pass];
135*0b57cec5SDimitry Andric 
136*0b57cec5SDimitry Andric   if (!T) {
137*0b57cec5SDimitry Andric     StringRef PassName = P->getPassName();
138*0b57cec5SDimitry Andric     StringRef PassArgument;
139*0b57cec5SDimitry Andric     if (const PassInfo *PI = Pass::lookupPassInfo(P->getPassID()))
140*0b57cec5SDimitry Andric       PassArgument = PI->getPassArgument();
141*0b57cec5SDimitry Andric     T.reset(newPassTimer(PassArgument.empty() ? PassName : PassArgument, PassName));
142*0b57cec5SDimitry Andric   }
143*0b57cec5SDimitry Andric   return T.get();
144*0b57cec5SDimitry Andric }
145*0b57cec5SDimitry Andric 
146*0b57cec5SDimitry Andric PassTimingInfo *PassTimingInfo::TheTimeInfo;
147*0b57cec5SDimitry Andric } // namespace legacy
148*0b57cec5SDimitry Andric } // namespace
149*0b57cec5SDimitry Andric 
150*0b57cec5SDimitry Andric Timer *getPassTimer(Pass *P) {
151*0b57cec5SDimitry Andric   legacy::PassTimingInfo::init();
152*0b57cec5SDimitry Andric   if (legacy::PassTimingInfo::TheTimeInfo)
153*0b57cec5SDimitry Andric     return legacy::PassTimingInfo::TheTimeInfo->getPassTimer(P, P);
154*0b57cec5SDimitry Andric   return nullptr;
155*0b57cec5SDimitry Andric }
156*0b57cec5SDimitry Andric 
157*0b57cec5SDimitry Andric /// If timing is enabled, report the times collected up to now and then reset
158*0b57cec5SDimitry Andric /// them.
159*0b57cec5SDimitry Andric void reportAndResetTimings(raw_ostream *OutStream) {
160*0b57cec5SDimitry Andric   if (legacy::PassTimingInfo::TheTimeInfo)
161*0b57cec5SDimitry Andric     legacy::PassTimingInfo::TheTimeInfo->print(OutStream);
162*0b57cec5SDimitry Andric }
163*0b57cec5SDimitry Andric 
164*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
165*0b57cec5SDimitry Andric // Pass timing handling for the New Pass Manager
166*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
167*0b57cec5SDimitry Andric 
168*0b57cec5SDimitry Andric /// Returns the timer for the specified pass invocation of \p PassID.
169*0b57cec5SDimitry Andric /// Each time it creates a new timer.
170*0b57cec5SDimitry Andric Timer &TimePassesHandler::getPassTimer(StringRef PassID) {
171*0b57cec5SDimitry Andric   // Bump counts for each request of the timer.
172*0b57cec5SDimitry Andric   unsigned Count = nextPassID(PassID);
173*0b57cec5SDimitry Andric 
174*0b57cec5SDimitry Andric   // Unconditionally appending description with a pass-invocation number.
175*0b57cec5SDimitry Andric   std::string FullDesc = formatv("{0} #{1}", PassID, Count).str();
176*0b57cec5SDimitry Andric 
177*0b57cec5SDimitry Andric   PassInvocationID UID{PassID, Count};
178*0b57cec5SDimitry Andric   Timer *T = new Timer(PassID, FullDesc, TG);
179*0b57cec5SDimitry Andric   auto Pair = TimingData.try_emplace(UID, T);
180*0b57cec5SDimitry Andric   assert(Pair.second && "should always create a new timer");
181*0b57cec5SDimitry Andric   return *(Pair.first->second.get());
182*0b57cec5SDimitry Andric }
183*0b57cec5SDimitry Andric 
184*0b57cec5SDimitry Andric TimePassesHandler::TimePassesHandler(bool Enabled)
185*0b57cec5SDimitry Andric     : TG("pass", "... Pass execution timing report ..."), Enabled(Enabled) {}
186*0b57cec5SDimitry Andric 
187*0b57cec5SDimitry Andric void TimePassesHandler::setOutStream(raw_ostream &Out) {
188*0b57cec5SDimitry Andric   OutStream = &Out;
189*0b57cec5SDimitry Andric }
190*0b57cec5SDimitry Andric 
191*0b57cec5SDimitry Andric void TimePassesHandler::print() {
192*0b57cec5SDimitry Andric   if (!Enabled)
193*0b57cec5SDimitry Andric     return;
194*0b57cec5SDimitry Andric   TG.print(OutStream ? *OutStream : *CreateInfoOutputFile(), true);
195*0b57cec5SDimitry Andric }
196*0b57cec5SDimitry Andric 
197*0b57cec5SDimitry Andric LLVM_DUMP_METHOD void TimePassesHandler::dump() const {
198*0b57cec5SDimitry Andric   dbgs() << "Dumping timers for " << getTypeName<TimePassesHandler>()
199*0b57cec5SDimitry Andric          << ":\n\tRunning:\n";
200*0b57cec5SDimitry Andric   for (auto &I : TimingData) {
201*0b57cec5SDimitry Andric     const Timer *MyTimer = I.second.get();
202*0b57cec5SDimitry Andric     if (!MyTimer || MyTimer->isRunning())
203*0b57cec5SDimitry Andric       dbgs() << "\tTimer " << MyTimer << " for pass " << I.first.first << "("
204*0b57cec5SDimitry Andric              << I.first.second << ")\n";
205*0b57cec5SDimitry Andric   }
206*0b57cec5SDimitry Andric   dbgs() << "\tTriggered:\n";
207*0b57cec5SDimitry Andric   for (auto &I : TimingData) {
208*0b57cec5SDimitry Andric     const Timer *MyTimer = I.second.get();
209*0b57cec5SDimitry Andric     if (!MyTimer || (MyTimer->hasTriggered() && !MyTimer->isRunning()))
210*0b57cec5SDimitry Andric       dbgs() << "\tTimer " << MyTimer << " for pass " << I.first.first << "("
211*0b57cec5SDimitry Andric              << I.first.second << ")\n";
212*0b57cec5SDimitry Andric   }
213*0b57cec5SDimitry Andric }
214*0b57cec5SDimitry Andric 
215*0b57cec5SDimitry Andric void TimePassesHandler::startTimer(StringRef PassID) {
216*0b57cec5SDimitry Andric   Timer &MyTimer = getPassTimer(PassID);
217*0b57cec5SDimitry Andric   TimerStack.push_back(&MyTimer);
218*0b57cec5SDimitry Andric   if (!MyTimer.isRunning())
219*0b57cec5SDimitry Andric     MyTimer.startTimer();
220*0b57cec5SDimitry Andric }
221*0b57cec5SDimitry Andric 
222*0b57cec5SDimitry Andric void TimePassesHandler::stopTimer(StringRef PassID) {
223*0b57cec5SDimitry Andric   assert(TimerStack.size() > 0 && "empty stack in popTimer");
224*0b57cec5SDimitry Andric   Timer *MyTimer = TimerStack.pop_back_val();
225*0b57cec5SDimitry Andric   assert(MyTimer && "timer should be present");
226*0b57cec5SDimitry Andric   if (MyTimer->isRunning())
227*0b57cec5SDimitry Andric     MyTimer->stopTimer();
228*0b57cec5SDimitry Andric }
229*0b57cec5SDimitry Andric 
230*0b57cec5SDimitry Andric static bool matchPassManager(StringRef PassID) {
231*0b57cec5SDimitry Andric   size_t prefix_pos = PassID.find('<');
232*0b57cec5SDimitry Andric   if (prefix_pos == StringRef::npos)
233*0b57cec5SDimitry Andric     return false;
234*0b57cec5SDimitry Andric   StringRef Prefix = PassID.substr(0, prefix_pos);
235*0b57cec5SDimitry Andric   return Prefix.endswith("PassManager") || Prefix.endswith("PassAdaptor") ||
236*0b57cec5SDimitry Andric          Prefix.endswith("AnalysisManagerProxy");
237*0b57cec5SDimitry Andric }
238*0b57cec5SDimitry Andric 
239*0b57cec5SDimitry Andric bool TimePassesHandler::runBeforePass(StringRef PassID) {
240*0b57cec5SDimitry Andric   if (matchPassManager(PassID))
241*0b57cec5SDimitry Andric     return true;
242*0b57cec5SDimitry Andric 
243*0b57cec5SDimitry Andric   startTimer(PassID);
244*0b57cec5SDimitry Andric 
245*0b57cec5SDimitry Andric   LLVM_DEBUG(dbgs() << "after runBeforePass(" << PassID << ")\n");
246*0b57cec5SDimitry Andric   LLVM_DEBUG(dump());
247*0b57cec5SDimitry Andric 
248*0b57cec5SDimitry Andric   // we are not going to skip this pass, thus return true.
249*0b57cec5SDimitry Andric   return true;
250*0b57cec5SDimitry Andric }
251*0b57cec5SDimitry Andric 
252*0b57cec5SDimitry Andric void TimePassesHandler::runAfterPass(StringRef PassID) {
253*0b57cec5SDimitry Andric   if (matchPassManager(PassID))
254*0b57cec5SDimitry Andric     return;
255*0b57cec5SDimitry Andric 
256*0b57cec5SDimitry Andric   stopTimer(PassID);
257*0b57cec5SDimitry Andric 
258*0b57cec5SDimitry Andric   LLVM_DEBUG(dbgs() << "after runAfterPass(" << PassID << ")\n");
259*0b57cec5SDimitry Andric   LLVM_DEBUG(dump());
260*0b57cec5SDimitry Andric }
261*0b57cec5SDimitry Andric 
262*0b57cec5SDimitry Andric void TimePassesHandler::registerCallbacks(PassInstrumentationCallbacks &PIC) {
263*0b57cec5SDimitry Andric   if (!Enabled)
264*0b57cec5SDimitry Andric     return;
265*0b57cec5SDimitry Andric 
266*0b57cec5SDimitry Andric   PIC.registerBeforePassCallback(
267*0b57cec5SDimitry Andric       [this](StringRef P, Any) { return this->runBeforePass(P); });
268*0b57cec5SDimitry Andric   PIC.registerAfterPassCallback(
269*0b57cec5SDimitry Andric       [this](StringRef P, Any) { this->runAfterPass(P); });
270*0b57cec5SDimitry Andric   PIC.registerAfterPassInvalidatedCallback(
271*0b57cec5SDimitry Andric       [this](StringRef P) { this->runAfterPass(P); });
272*0b57cec5SDimitry Andric   PIC.registerBeforeAnalysisCallback(
273*0b57cec5SDimitry Andric       [this](StringRef P, Any) { this->runBeforePass(P); });
274*0b57cec5SDimitry Andric   PIC.registerAfterAnalysisCallback(
275*0b57cec5SDimitry Andric       [this](StringRef P, Any) { this->runAfterPass(P); });
276*0b57cec5SDimitry Andric }
277*0b57cec5SDimitry Andric 
278*0b57cec5SDimitry Andric } // namespace llvm
279