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