10b57cec5SDimitry Andric //===- PassTimingInfo.cpp - LLVM Pass Timing Implementation ---------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This file implements the LLVM Pass Timing infrastructure for both 100b57cec5SDimitry Andric // new and legacy pass managers. 110b57cec5SDimitry Andric // 120b57cec5SDimitry Andric // PassTimingInfo Class - This class is used to calculate information about the 130b57cec5SDimitry Andric // amount of time each pass takes to execute. This only happens when 140b57cec5SDimitry Andric // -time-passes is enabled on the command line. 150b57cec5SDimitry Andric // 160b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 170b57cec5SDimitry Andric 180b57cec5SDimitry Andric #include "llvm/IR/PassTimingInfo.h" 190b57cec5SDimitry Andric #include "llvm/ADT/Statistic.h" 200b57cec5SDimitry Andric #include "llvm/IR/PassInstrumentation.h" 210b57cec5SDimitry Andric #include "llvm/Pass.h" 220b57cec5SDimitry Andric #include "llvm/Support/CommandLine.h" 230b57cec5SDimitry Andric #include "llvm/Support/Debug.h" 240b57cec5SDimitry Andric #include "llvm/Support/FormatVariadic.h" 250b57cec5SDimitry Andric #include "llvm/Support/ManagedStatic.h" 260b57cec5SDimitry Andric #include "llvm/Support/Mutex.h" 27e8d8bef9SDimitry Andric #include "llvm/Support/TypeName.h" 280b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 290b57cec5SDimitry Andric #include <string> 300b57cec5SDimitry Andric 310b57cec5SDimitry Andric using namespace llvm; 320b57cec5SDimitry Andric 330b57cec5SDimitry Andric #define DEBUG_TYPE "time-passes" 340b57cec5SDimitry Andric 350b57cec5SDimitry Andric namespace llvm { 360b57cec5SDimitry Andric 370b57cec5SDimitry Andric bool TimePassesIsEnabled = false; 38e8d8bef9SDimitry Andric bool TimePassesPerRun = false; 390b57cec5SDimitry Andric 400b57cec5SDimitry Andric static cl::opt<bool, true> EnableTiming( 410b57cec5SDimitry Andric "time-passes", cl::location(TimePassesIsEnabled), cl::Hidden, 420b57cec5SDimitry Andric cl::desc("Time each pass, printing elapsed time for each on exit")); 430b57cec5SDimitry Andric 44e8d8bef9SDimitry Andric static cl::opt<bool, true> EnableTimingPerRun( 45e8d8bef9SDimitry Andric "time-passes-per-run", cl::location(TimePassesPerRun), cl::Hidden, 46e8d8bef9SDimitry Andric cl::desc("Time each pass run, printing elapsed time for each run on exit"), 47e8d8bef9SDimitry Andric cl::callback([](const bool &) { TimePassesIsEnabled = true; })); 48e8d8bef9SDimitry Andric 490b57cec5SDimitry Andric namespace { 500b57cec5SDimitry Andric namespace legacy { 510b57cec5SDimitry Andric 520b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 530b57cec5SDimitry Andric // Legacy pass manager's PassTimingInfo implementation 540b57cec5SDimitry Andric 550b57cec5SDimitry Andric /// Provides an interface for collecting pass timing information. 560b57cec5SDimitry Andric /// 570b57cec5SDimitry Andric /// It was intended to be generic but now we decided to split 580b57cec5SDimitry Andric /// interfaces completely. This is now exclusively for legacy-pass-manager use. 590b57cec5SDimitry Andric class PassTimingInfo { 600b57cec5SDimitry Andric public: 610b57cec5SDimitry Andric using PassInstanceID = void *; 620b57cec5SDimitry Andric 630b57cec5SDimitry Andric private: 640b57cec5SDimitry Andric StringMap<unsigned> PassIDCountMap; ///< Map that counts instances of passes 650b57cec5SDimitry Andric DenseMap<PassInstanceID, std::unique_ptr<Timer>> TimingData; ///< timers for pass instances 660b57cec5SDimitry Andric TimerGroup TG; 670b57cec5SDimitry Andric 680b57cec5SDimitry Andric public: 690b57cec5SDimitry Andric /// Default constructor for yet-inactive timeinfo. 700b57cec5SDimitry Andric /// Use \p init() to activate it. 710b57cec5SDimitry Andric PassTimingInfo(); 720b57cec5SDimitry Andric 730b57cec5SDimitry Andric /// Print out timing information and release timers. 740b57cec5SDimitry Andric ~PassTimingInfo(); 750b57cec5SDimitry Andric 760b57cec5SDimitry Andric /// Initializes the static \p TheTimeInfo member to a non-null value when 770b57cec5SDimitry Andric /// -time-passes is enabled. Leaves it null otherwise. 780b57cec5SDimitry Andric /// 790b57cec5SDimitry Andric /// This method may be called multiple times. 800b57cec5SDimitry Andric static void init(); 810b57cec5SDimitry Andric 820b57cec5SDimitry Andric /// Prints out timing information and then resets the timers. 830b57cec5SDimitry Andric /// By default it uses the stream created by CreateInfoOutputFile(). 840b57cec5SDimitry Andric void print(raw_ostream *OutStream = nullptr); 850b57cec5SDimitry Andric 860b57cec5SDimitry Andric /// Returns the timer for the specified pass if it exists. 870b57cec5SDimitry Andric Timer *getPassTimer(Pass *, PassInstanceID); 880b57cec5SDimitry Andric 890b57cec5SDimitry Andric static PassTimingInfo *TheTimeInfo; 900b57cec5SDimitry Andric 910b57cec5SDimitry Andric private: 920b57cec5SDimitry Andric Timer *newPassTimer(StringRef PassID, StringRef PassDesc); 930b57cec5SDimitry Andric }; 940b57cec5SDimitry Andric 950b57cec5SDimitry Andric static ManagedStatic<sys::SmartMutex<true>> TimingInfoMutex; 960b57cec5SDimitry Andric 97bdd1243dSDimitry Andric PassTimingInfo::PassTimingInfo() : TG("pass", "Pass execution timing report") {} 980b57cec5SDimitry Andric 990b57cec5SDimitry Andric PassTimingInfo::~PassTimingInfo() { 1000b57cec5SDimitry Andric // Deleting the timers accumulates their info into the TG member. 1010b57cec5SDimitry Andric // Then TG member is (implicitly) deleted, actually printing the report. 1020b57cec5SDimitry Andric TimingData.clear(); 1030b57cec5SDimitry Andric } 1040b57cec5SDimitry Andric 1050b57cec5SDimitry Andric void PassTimingInfo::init() { 1060b57cec5SDimitry Andric if (!TimePassesIsEnabled || TheTimeInfo) 1070b57cec5SDimitry Andric return; 1080b57cec5SDimitry Andric 1090b57cec5SDimitry Andric // Constructed the first time this is called, iff -time-passes is enabled. 1100b57cec5SDimitry Andric // This guarantees that the object will be constructed after static globals, 1110b57cec5SDimitry Andric // thus it will be destroyed before them. 1120b57cec5SDimitry Andric static ManagedStatic<PassTimingInfo> TTI; 1130b57cec5SDimitry Andric TheTimeInfo = &*TTI; 1140b57cec5SDimitry Andric } 1150b57cec5SDimitry Andric 1160b57cec5SDimitry Andric /// Prints out timing information and then resets the timers. 1170b57cec5SDimitry Andric void PassTimingInfo::print(raw_ostream *OutStream) { 1180b57cec5SDimitry Andric TG.print(OutStream ? *OutStream : *CreateInfoOutputFile(), true); 1190b57cec5SDimitry Andric } 1200b57cec5SDimitry Andric 1210b57cec5SDimitry Andric Timer *PassTimingInfo::newPassTimer(StringRef PassID, StringRef PassDesc) { 1220b57cec5SDimitry Andric unsigned &num = PassIDCountMap[PassID]; 1230b57cec5SDimitry Andric num++; 1240b57cec5SDimitry Andric // Appending description with a pass-instance number for all but the first one 1250b57cec5SDimitry Andric std::string PassDescNumbered = 1260b57cec5SDimitry Andric num <= 1 ? PassDesc.str() : formatv("{0} #{1}", PassDesc, num).str(); 1270b57cec5SDimitry Andric return new Timer(PassID, PassDescNumbered, TG); 1280b57cec5SDimitry Andric } 1290b57cec5SDimitry Andric 1300b57cec5SDimitry Andric Timer *PassTimingInfo::getPassTimer(Pass *P, PassInstanceID Pass) { 1310b57cec5SDimitry Andric if (P->getAsPMDataManager()) 1320b57cec5SDimitry Andric return nullptr; 1330b57cec5SDimitry Andric 1340b57cec5SDimitry Andric init(); 1350b57cec5SDimitry Andric sys::SmartScopedLock<true> Lock(*TimingInfoMutex); 1360b57cec5SDimitry Andric std::unique_ptr<Timer> &T = TimingData[Pass]; 1370b57cec5SDimitry Andric 1380b57cec5SDimitry Andric if (!T) { 1390b57cec5SDimitry Andric StringRef PassName = P->getPassName(); 1400b57cec5SDimitry Andric StringRef PassArgument; 1410b57cec5SDimitry Andric if (const PassInfo *PI = Pass::lookupPassInfo(P->getPassID())) 1420b57cec5SDimitry Andric PassArgument = PI->getPassArgument(); 1430b57cec5SDimitry Andric T.reset(newPassTimer(PassArgument.empty() ? PassName : PassArgument, PassName)); 1440b57cec5SDimitry Andric } 1450b57cec5SDimitry Andric return T.get(); 1460b57cec5SDimitry Andric } 1470b57cec5SDimitry Andric 1480b57cec5SDimitry Andric PassTimingInfo *PassTimingInfo::TheTimeInfo; 1490b57cec5SDimitry Andric } // namespace legacy 1500b57cec5SDimitry Andric } // namespace 1510b57cec5SDimitry Andric 1520b57cec5SDimitry Andric Timer *getPassTimer(Pass *P) { 1530b57cec5SDimitry Andric legacy::PassTimingInfo::init(); 1540b57cec5SDimitry Andric if (legacy::PassTimingInfo::TheTimeInfo) 1550b57cec5SDimitry Andric return legacy::PassTimingInfo::TheTimeInfo->getPassTimer(P, P); 1560b57cec5SDimitry Andric return nullptr; 1570b57cec5SDimitry Andric } 1580b57cec5SDimitry Andric 1590b57cec5SDimitry Andric /// If timing is enabled, report the times collected up to now and then reset 1600b57cec5SDimitry Andric /// them. 1610b57cec5SDimitry Andric void reportAndResetTimings(raw_ostream *OutStream) { 1620b57cec5SDimitry Andric if (legacy::PassTimingInfo::TheTimeInfo) 1630b57cec5SDimitry Andric legacy::PassTimingInfo::TheTimeInfo->print(OutStream); 1640b57cec5SDimitry Andric } 1650b57cec5SDimitry Andric 1660b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 1670b57cec5SDimitry Andric // Pass timing handling for the New Pass Manager 1680b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 1690b57cec5SDimitry Andric 1700b57cec5SDimitry Andric /// Returns the timer for the specified pass invocation of \p PassID. 1710b57cec5SDimitry Andric /// Each time it creates a new timer. 172bdd1243dSDimitry Andric Timer &TimePassesHandler::getPassTimer(StringRef PassID, bool IsPass) { 173bdd1243dSDimitry Andric TimerGroup &TG = IsPass ? PassTG : AnalysisTG; 174e8d8bef9SDimitry Andric if (!PerRun) { 175e8d8bef9SDimitry Andric TimerVector &Timers = TimingData[PassID]; 176e8d8bef9SDimitry Andric if (Timers.size() == 0) 177e8d8bef9SDimitry Andric Timers.emplace_back(new Timer(PassID, PassID, TG)); 178e8d8bef9SDimitry Andric return *Timers.front(); 179e8d8bef9SDimitry Andric } 180e8d8bef9SDimitry Andric 1815ffd83dbSDimitry Andric // Take a vector of Timers created for this \p PassID and append 1825ffd83dbSDimitry Andric // one more timer to it. 1835ffd83dbSDimitry Andric TimerVector &Timers = TimingData[PassID]; 1845ffd83dbSDimitry Andric unsigned Count = Timers.size() + 1; 1850b57cec5SDimitry Andric 1860b57cec5SDimitry Andric std::string FullDesc = formatv("{0} #{1}", PassID, Count).str(); 1870b57cec5SDimitry Andric 1880b57cec5SDimitry Andric Timer *T = new Timer(PassID, FullDesc, TG); 1895ffd83dbSDimitry Andric Timers.emplace_back(T); 1904824e7fdSDimitry Andric assert(Count == Timers.size() && "Timers vector not adjusted correctly."); 1915ffd83dbSDimitry Andric 1925ffd83dbSDimitry Andric return *T; 1930b57cec5SDimitry Andric } 1940b57cec5SDimitry Andric 195e8d8bef9SDimitry Andric TimePassesHandler::TimePassesHandler(bool Enabled, bool PerRun) 196bdd1243dSDimitry Andric : PassTG("pass", "Pass execution timing report"), 197bdd1243dSDimitry Andric AnalysisTG("analysis", "Analysis execution timing report"), 198bdd1243dSDimitry Andric Enabled(Enabled), PerRun(PerRun) {} 199e8d8bef9SDimitry Andric 200e8d8bef9SDimitry Andric TimePassesHandler::TimePassesHandler() 201e8d8bef9SDimitry Andric : TimePassesHandler(TimePassesIsEnabled, TimePassesPerRun) {} 2020b57cec5SDimitry Andric 2030b57cec5SDimitry Andric void TimePassesHandler::setOutStream(raw_ostream &Out) { 2040b57cec5SDimitry Andric OutStream = &Out; 2050b57cec5SDimitry Andric } 2060b57cec5SDimitry Andric 2070b57cec5SDimitry Andric void TimePassesHandler::print() { 2080b57cec5SDimitry Andric if (!Enabled) 2090b57cec5SDimitry Andric return; 210bdd1243dSDimitry Andric std::unique_ptr<raw_ostream> MaybeCreated; 211bdd1243dSDimitry Andric raw_ostream *OS = OutStream; 212bdd1243dSDimitry Andric if (OutStream) { 213bdd1243dSDimitry Andric OS = OutStream; 214bdd1243dSDimitry Andric } else { 215bdd1243dSDimitry Andric MaybeCreated = CreateInfoOutputFile(); 216bdd1243dSDimitry Andric OS = &*MaybeCreated; 217bdd1243dSDimitry Andric } 218bdd1243dSDimitry Andric PassTG.print(*OS, true); 219bdd1243dSDimitry Andric AnalysisTG.print(*OS, true); 2200b57cec5SDimitry Andric } 2210b57cec5SDimitry Andric 2220b57cec5SDimitry Andric LLVM_DUMP_METHOD void TimePassesHandler::dump() const { 2230b57cec5SDimitry Andric dbgs() << "Dumping timers for " << getTypeName<TimePassesHandler>() 2240b57cec5SDimitry Andric << ":\n\tRunning:\n"; 2250b57cec5SDimitry Andric for (auto &I : TimingData) { 2265ffd83dbSDimitry Andric StringRef PassID = I.getKey(); 2275ffd83dbSDimitry Andric const TimerVector& MyTimers = I.getValue(); 2285ffd83dbSDimitry Andric for (unsigned idx = 0; idx < MyTimers.size(); idx++) { 2295ffd83dbSDimitry Andric const Timer* MyTimer = MyTimers[idx].get(); 2305ffd83dbSDimitry Andric if (MyTimer && MyTimer->isRunning()) 2315ffd83dbSDimitry Andric dbgs() << "\tTimer " << MyTimer << " for pass " << PassID << "(" << idx << ")\n"; 2325ffd83dbSDimitry Andric } 2330b57cec5SDimitry Andric } 2340b57cec5SDimitry Andric dbgs() << "\tTriggered:\n"; 2350b57cec5SDimitry Andric for (auto &I : TimingData) { 2365ffd83dbSDimitry Andric StringRef PassID = I.getKey(); 2375ffd83dbSDimitry Andric const TimerVector& MyTimers = I.getValue(); 2385ffd83dbSDimitry Andric for (unsigned idx = 0; idx < MyTimers.size(); idx++) { 2395ffd83dbSDimitry Andric const Timer* MyTimer = MyTimers[idx].get(); 2405ffd83dbSDimitry Andric if (MyTimer && MyTimer->hasTriggered() && !MyTimer->isRunning()) 2415ffd83dbSDimitry Andric dbgs() << "\tTimer " << MyTimer << " for pass " << PassID << "(" << idx << ")\n"; 2425ffd83dbSDimitry Andric } 2430b57cec5SDimitry Andric } 2440b57cec5SDimitry Andric } 2450b57cec5SDimitry Andric 246bdd1243dSDimitry Andric static bool shouldIgnorePass(StringRef PassID) { 247bdd1243dSDimitry Andric return isSpecialPass(PassID, 248bdd1243dSDimitry Andric {"PassManager", "PassAdaptor", "AnalysisManagerProxy", 249bdd1243dSDimitry Andric "ModuleInlinerWrapperPass", "DevirtSCCRepeatedPass"}); 250bdd1243dSDimitry Andric } 251bdd1243dSDimitry Andric 252bdd1243dSDimitry Andric void TimePassesHandler::startPassTimer(StringRef PassID) { 253bdd1243dSDimitry Andric if (shouldIgnorePass(PassID)) 254bdd1243dSDimitry Andric return; 255*5f757f3fSDimitry Andric // Stop the previous pass timer to prevent double counting when a 256*5f757f3fSDimitry Andric // pass requests another pass. 257*5f757f3fSDimitry Andric if (!PassActiveTimerStack.empty()) { 258*5f757f3fSDimitry Andric assert(PassActiveTimerStack.back()->isRunning()); 259*5f757f3fSDimitry Andric PassActiveTimerStack.back()->stopTimer(); 260*5f757f3fSDimitry Andric } 261bdd1243dSDimitry Andric Timer &MyTimer = getPassTimer(PassID, /*IsPass*/ true); 262*5f757f3fSDimitry Andric PassActiveTimerStack.push_back(&MyTimer); 263bdd1243dSDimitry Andric assert(!MyTimer.isRunning()); 264bdd1243dSDimitry Andric MyTimer.startTimer(); 265bdd1243dSDimitry Andric } 266bdd1243dSDimitry Andric 267bdd1243dSDimitry Andric void TimePassesHandler::stopPassTimer(StringRef PassID) { 268bdd1243dSDimitry Andric if (shouldIgnorePass(PassID)) 269bdd1243dSDimitry Andric return; 270*5f757f3fSDimitry Andric assert(!PassActiveTimerStack.empty() && "empty stack in popTimer"); 271*5f757f3fSDimitry Andric Timer *MyTimer = PassActiveTimerStack.pop_back_val(); 272*5f757f3fSDimitry Andric assert(MyTimer && "timer should be present"); 273*5f757f3fSDimitry Andric assert(MyTimer->isRunning()); 274*5f757f3fSDimitry Andric MyTimer->stopTimer(); 275*5f757f3fSDimitry Andric 276*5f757f3fSDimitry Andric // Restart the previously stopped timer. 277*5f757f3fSDimitry Andric if (!PassActiveTimerStack.empty()) { 278*5f757f3fSDimitry Andric assert(!PassActiveTimerStack.back()->isRunning()); 279*5f757f3fSDimitry Andric PassActiveTimerStack.back()->startTimer(); 280*5f757f3fSDimitry Andric } 281bdd1243dSDimitry Andric } 282bdd1243dSDimitry Andric 283bdd1243dSDimitry Andric void TimePassesHandler::startAnalysisTimer(StringRef PassID) { 284bdd1243dSDimitry Andric // Stop the previous analysis timer to prevent double counting when an 285bdd1243dSDimitry Andric // analysis requests another analysis. 286bdd1243dSDimitry Andric if (!AnalysisActiveTimerStack.empty()) { 287bdd1243dSDimitry Andric assert(AnalysisActiveTimerStack.back()->isRunning()); 288bdd1243dSDimitry Andric AnalysisActiveTimerStack.back()->stopTimer(); 289bdd1243dSDimitry Andric } 290bdd1243dSDimitry Andric 291bdd1243dSDimitry Andric Timer &MyTimer = getPassTimer(PassID, /*IsPass*/ false); 292bdd1243dSDimitry Andric AnalysisActiveTimerStack.push_back(&MyTimer); 2930b57cec5SDimitry Andric if (!MyTimer.isRunning()) 2940b57cec5SDimitry Andric MyTimer.startTimer(); 2950b57cec5SDimitry Andric } 2960b57cec5SDimitry Andric 297bdd1243dSDimitry Andric void TimePassesHandler::stopAnalysisTimer(StringRef PassID) { 298bdd1243dSDimitry Andric assert(!AnalysisActiveTimerStack.empty() && "empty stack in popTimer"); 299bdd1243dSDimitry Andric Timer *MyTimer = AnalysisActiveTimerStack.pop_back_val(); 3000b57cec5SDimitry Andric assert(MyTimer && "timer should be present"); 3010b57cec5SDimitry Andric if (MyTimer->isRunning()) 3020b57cec5SDimitry Andric MyTimer->stopTimer(); 303bdd1243dSDimitry Andric 304bdd1243dSDimitry Andric // Restart the previously stopped timer. 305bdd1243dSDimitry Andric if (!AnalysisActiveTimerStack.empty()) { 306bdd1243dSDimitry Andric assert(!AnalysisActiveTimerStack.back()->isRunning()); 307bdd1243dSDimitry Andric AnalysisActiveTimerStack.back()->startTimer(); 3080b57cec5SDimitry Andric } 3090b57cec5SDimitry Andric } 3100b57cec5SDimitry Andric 3110b57cec5SDimitry Andric void TimePassesHandler::registerCallbacks(PassInstrumentationCallbacks &PIC) { 3120b57cec5SDimitry Andric if (!Enabled) 3130b57cec5SDimitry Andric return; 3140b57cec5SDimitry Andric 315e8d8bef9SDimitry Andric PIC.registerBeforeNonSkippedPassCallback( 316bdd1243dSDimitry Andric [this](StringRef P, Any) { this->startPassTimer(P); }); 3170b57cec5SDimitry Andric PIC.registerAfterPassCallback( 318e8d8bef9SDimitry Andric [this](StringRef P, Any, const PreservedAnalyses &) { 319bdd1243dSDimitry Andric this->stopPassTimer(P); 320e8d8bef9SDimitry Andric }); 3210b57cec5SDimitry Andric PIC.registerAfterPassInvalidatedCallback( 322e8d8bef9SDimitry Andric [this](StringRef P, const PreservedAnalyses &) { 323bdd1243dSDimitry Andric this->stopPassTimer(P); 324e8d8bef9SDimitry Andric }); 3250b57cec5SDimitry Andric PIC.registerBeforeAnalysisCallback( 326bdd1243dSDimitry Andric [this](StringRef P, Any) { this->startAnalysisTimer(P); }); 3270b57cec5SDimitry Andric PIC.registerAfterAnalysisCallback( 328bdd1243dSDimitry Andric [this](StringRef P, Any) { this->stopAnalysisTimer(P); }); 3290b57cec5SDimitry Andric } 3300b57cec5SDimitry Andric 3310b57cec5SDimitry Andric } // namespace llvm 332