1e46ba31cSRiver Riddle //===- PassTiming.cpp -----------------------------------------------------===//
2e46ba31cSRiver Riddle //
330857107SMehdi Amini // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
456222a06SMehdi Amini // See https://llvm.org/LICENSE.txt for license information.
556222a06SMehdi Amini // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e46ba31cSRiver Riddle //
756222a06SMehdi Amini //===----------------------------------------------------------------------===//
8e46ba31cSRiver Riddle 
9e46ba31cSRiver Riddle #include "PassDetail.h"
106558f80cSRiver Riddle #include "mlir/Pass/PassManager.h"
116558f80cSRiver Riddle #include "llvm/ADT/MapVector.h"
12c900d499SChristian Sigg #include "llvm/ADT/STLExtras.h"
13e46ba31cSRiver Riddle #include "llvm/ADT/SmallVector.h"
14e46ba31cSRiver Riddle #include "llvm/ADT/Statistic.h"
15e46ba31cSRiver Riddle #include "llvm/Support/Format.h"
16e46ba31cSRiver Riddle #include "llvm/Support/FormatVariadic.h"
173a845be7SRiver Riddle #include "llvm/Support/Threading.h"
188e7b683dSRiver Riddle #include <chrono>
19e46ba31cSRiver Riddle 
20e46ba31cSRiver Riddle using namespace mlir;
21e46ba31cSRiver Riddle using namespace mlir::detail;
22e46ba31cSRiver Riddle 
234562e389SRiver Riddle constexpr StringLiteral kPassTimingDescription =
24e46ba31cSRiver Riddle     "... Pass execution timing report ...";
25e46ba31cSRiver Riddle 
266558f80cSRiver Riddle namespace {
273a845be7SRiver Riddle /// Simple record class to record timing information.
283a845be7SRiver Riddle struct TimeRecord {
293a845be7SRiver Riddle   TimeRecord(double wall = 0.0, double user = 0.0) : wall(wall), user(user) {}
303a845be7SRiver Riddle 
313a845be7SRiver Riddle   TimeRecord &operator+=(const TimeRecord &other) {
323a845be7SRiver Riddle     wall += other.wall;
333a845be7SRiver Riddle     user += other.user;
343a845be7SRiver Riddle     return *this;
353a845be7SRiver Riddle   }
363a845be7SRiver Riddle 
373a845be7SRiver Riddle   /// Print the current time record to 'os', with a breakdown showing
383a845be7SRiver Riddle   /// contributions to the give 'total' time record.
393a845be7SRiver Riddle   void print(raw_ostream &os, const TimeRecord &total) {
403a845be7SRiver Riddle     if (total.user != total.wall)
413a845be7SRiver Riddle       os << llvm::format("  %7.4f (%5.1f%%)  ", user,
423a845be7SRiver Riddle                          100.0 * user / total.user);
433a845be7SRiver Riddle     os << llvm::format("  %7.4f (%5.1f%%)  ", wall, 100.0 * wall / total.wall);
443a845be7SRiver Riddle   }
453a845be7SRiver Riddle 
463a845be7SRiver Riddle   double wall, user;
473a845be7SRiver Riddle };
483a845be7SRiver Riddle 
49120509a6SRiver Riddle /// An enumeration of the different types of timers.
50120509a6SRiver Riddle enum class TimerKind {
51120509a6SRiver Riddle   /// This timer represents an ordered collection of pass timers, corresponding
52120509a6SRiver Riddle   /// to a pass pipeline.
53120509a6SRiver Riddle   Pipeline,
54120509a6SRiver Riddle 
55120509a6SRiver Riddle   /// This timer represents a collection of pipeline timers.
56120509a6SRiver Riddle   PipelineCollection,
57120509a6SRiver Riddle 
58120509a6SRiver Riddle   /// This timer represents an analysis or pass timer.
59120509a6SRiver Riddle   PassOrAnalysis
60120509a6SRiver Riddle };
61120509a6SRiver Riddle 
628e7b683dSRiver Riddle struct Timer {
63120509a6SRiver Riddle   explicit Timer(std::string &&name, TimerKind kind)
64120509a6SRiver Riddle       : name(std::move(name)), kind(kind) {}
658e7b683dSRiver Riddle 
668e7b683dSRiver Riddle   /// Start the timer.
678e7b683dSRiver Riddle   void start() { startTime = std::chrono::system_clock::now(); }
688e7b683dSRiver Riddle 
698e7b683dSRiver Riddle   /// Stop the timer.
703a845be7SRiver Riddle   void stop() {
713a845be7SRiver Riddle     auto newTime = std::chrono::system_clock::now() - startTime;
723a845be7SRiver Riddle     wallTime += newTime;
733a845be7SRiver Riddle     userTime += newTime;
743a845be7SRiver Riddle   }
758e7b683dSRiver Riddle 
768e7b683dSRiver Riddle   /// Get or create a child timer with the provided name and id.
77120509a6SRiver Riddle   Timer *getChildTimer(const void *id, TimerKind kind,
788e7b683dSRiver Riddle                        std::function<std::string()> &&nameBuilder) {
798e7b683dSRiver Riddle     auto &child = children[id];
808e7b683dSRiver Riddle     if (!child)
81c900d499SChristian Sigg       child = std::make_unique<Timer>(nameBuilder(), kind);
828e7b683dSRiver Riddle     return child.get();
838e7b683dSRiver Riddle   }
848e7b683dSRiver Riddle 
858e7b683dSRiver Riddle   /// Returns the total time for this timer in seconds.
863a845be7SRiver Riddle   TimeRecord getTotalTime() {
871c649d57SRiver Riddle     // If this is a pass or analysis timer, use the recorded time directly.
881c649d57SRiver Riddle     if (kind == TimerKind::PassOrAnalysis) {
893a845be7SRiver Riddle       return TimeRecord(
903a845be7SRiver Riddle           std::chrono::duration_cast<std::chrono::duration<double>>(wallTime)
913a845be7SRiver Riddle               .count(),
923a845be7SRiver Riddle           std::chrono::duration_cast<std::chrono::duration<double>>(userTime)
933a845be7SRiver Riddle               .count());
948e7b683dSRiver Riddle     }
958e7b683dSRiver Riddle 
968bfedb3cSKazuaki Ishizaki     // Otherwise, accumulate the timing from each of the children.
973a845be7SRiver Riddle     TimeRecord totalTime;
988e7b683dSRiver Riddle     for (auto &child : children)
998e7b683dSRiver Riddle       totalTime += child.second->getTotalTime();
1008e7b683dSRiver Riddle     return totalTime;
1018e7b683dSRiver Riddle   }
1028e7b683dSRiver Riddle 
1033a845be7SRiver Riddle   /// A map of unique identifiers to child timers.
1043a845be7SRiver Riddle   using ChildrenMap = llvm::MapVector<const void *, std::unique_ptr<Timer>>;
1053a845be7SRiver Riddle 
1063a845be7SRiver Riddle   /// Merge the timing data from 'other' into this timer.
1073a845be7SRiver Riddle   void merge(Timer &&other) {
1083a845be7SRiver Riddle     if (wallTime < other.wallTime)
1093a845be7SRiver Riddle       wallTime = other.wallTime;
1103a845be7SRiver Riddle     userTime += other.userTime;
111120509a6SRiver Riddle     mergeChildren(std::move(other.children));
1123a845be7SRiver Riddle   }
1133a845be7SRiver Riddle 
1148bfedb3cSKazuaki Ishizaki   /// Merge the timer children in 'otherChildren' with the children of this
115120509a6SRiver Riddle   /// timer.
116120509a6SRiver Riddle   void mergeChildren(ChildrenMap &&otherChildren) {
1173a845be7SRiver Riddle     // Check for an empty children list.
1183a845be7SRiver Riddle     if (children.empty()) {
1193a845be7SRiver Riddle       children = std::move(otherChildren);
1203a845be7SRiver Riddle       return;
1213a845be7SRiver Riddle     }
1223a845be7SRiver Riddle 
123120509a6SRiver Riddle     // Pipeline merges are handled separately as the children are merged
1248bfedb3cSKazuaki Ishizaki     // lexicographically.
125120509a6SRiver Riddle     if (kind == TimerKind::Pipeline) {
1263a845be7SRiver Riddle       assert(children.size() == otherChildren.size() &&
127120509a6SRiver Riddle              "pipeline merge requires the same number of children");
128120509a6SRiver Riddle       for (auto it : llvm::zip(children, otherChildren))
129120509a6SRiver Riddle         std::get<0>(it).second->merge(std::move(*std::get<1>(it).second));
1303a845be7SRiver Riddle       return;
1313a845be7SRiver Riddle     }
1323a845be7SRiver Riddle 
133120509a6SRiver Riddle     // Otherwise, we merge children based upon their timer key.
134120509a6SRiver Riddle     for (auto &otherChild : otherChildren)
135120509a6SRiver Riddle       mergeChild(std::move(otherChild));
1363a845be7SRiver Riddle   }
137120509a6SRiver Riddle 
138120509a6SRiver Riddle   /// Merge in the given child timer and id into this timer.
139120509a6SRiver Riddle   void mergeChild(ChildrenMap::value_type &&childIt) {
140120509a6SRiver Riddle     auto &child = children[childIt.first];
141120509a6SRiver Riddle     if (!child)
142120509a6SRiver Riddle       child = std::move(childIt.second);
143120509a6SRiver Riddle     else
144120509a6SRiver Riddle       child->merge(std::move(*childIt.second));
1453a845be7SRiver Riddle   }
1463a845be7SRiver Riddle 
1478e7b683dSRiver Riddle   /// Raw timing information.
148ff05e9daSRiver Riddle   std::chrono::time_point<std::chrono::system_clock> startTime;
1493a845be7SRiver Riddle   std::chrono::nanoseconds wallTime = std::chrono::nanoseconds(0);
1503a845be7SRiver Riddle   std::chrono::nanoseconds userTime = std::chrono::nanoseconds(0);
1518e7b683dSRiver Riddle 
1528e7b683dSRiver Riddle   /// A map of unique identifiers to child timers.
1533a845be7SRiver Riddle   ChildrenMap children;
1548e7b683dSRiver Riddle 
1558e7b683dSRiver Riddle   /// A descriptive name for this timer.
1568e7b683dSRiver Riddle   std::string name;
157120509a6SRiver Riddle 
158120509a6SRiver Riddle   /// The type of timer this instance represents.
159120509a6SRiver Riddle   TimerKind kind;
1608e7b683dSRiver Riddle };
1618e7b683dSRiver Riddle 
1626558f80cSRiver Riddle struct PassTiming : public PassInstrumentation {
163*f65a3f7cSMehdi Amini   PassTiming(std::unique_ptr<PassManager::PassTimingConfig> config)
164*f65a3f7cSMehdi Amini       : config(std::move(config)) {}
165c900d499SChristian Sigg   ~PassTiming() override { print(); }
166e46ba31cSRiver Riddle 
1676558f80cSRiver Riddle   /// Setup the instrumentation hooks.
168120509a6SRiver Riddle   void runBeforePipeline(const OperationName &name,
1691c649d57SRiver Riddle                          const PipelineParentInfo &parentInfo) override;
170120509a6SRiver Riddle   void runAfterPipeline(const OperationName &name,
1711c649d57SRiver Riddle                         const PipelineParentInfo &parentInfo) override;
1724fb971a9SRiver Riddle   void runBeforePass(Pass *pass, Operation *) override { startPassTimer(pass); }
1734fb971a9SRiver Riddle   void runAfterPass(Pass *pass, Operation *) override;
1744fb971a9SRiver Riddle   void runAfterPassFailed(Pass *pass, Operation *op) override {
1754fb971a9SRiver Riddle     runAfterPass(pass, op);
1766558f80cSRiver Riddle   }
177a517191aSRiver Riddle   void runBeforeAnalysis(StringRef name, TypeID id, Operation *) override {
1786558f80cSRiver Riddle     startAnalysisTimer(name, id);
1796558f80cSRiver Riddle   }
180a517191aSRiver Riddle   void runAfterAnalysis(StringRef, TypeID, Operation *) override;
1816558f80cSRiver Riddle 
1826558f80cSRiver Riddle   /// Print and clear the timing results.
1836558f80cSRiver Riddle   void print();
1846558f80cSRiver Riddle 
1856558f80cSRiver Riddle   /// Start a new timer for the given pass.
1866558f80cSRiver Riddle   void startPassTimer(Pass *pass);
1876558f80cSRiver Riddle 
1886558f80cSRiver Riddle   /// Start a new timer for the given analysis.
189a517191aSRiver Riddle   void startAnalysisTimer(StringRef name, TypeID id);
1906558f80cSRiver Riddle 
191120509a6SRiver Riddle   /// Pop the last active timer for the current thread.
192120509a6SRiver Riddle   Timer *popLastActiveTimer() {
193120509a6SRiver Riddle     auto tid = llvm::get_threadid();
194120509a6SRiver Riddle     auto &activeTimers = activeThreadTimers[tid];
195120509a6SRiver Riddle     assert(!activeTimers.empty() && "expected active timer");
196120509a6SRiver Riddle     return activeTimers.pop_back_val();
197120509a6SRiver Riddle   }
1986558f80cSRiver Riddle 
1996558f80cSRiver Riddle   /// Print the timing result in list mode.
2003a845be7SRiver Riddle   void printResultsAsList(raw_ostream &os, Timer *root, TimeRecord totalTime);
2016558f80cSRiver Riddle 
2026558f80cSRiver Riddle   /// Print the timing result in pipeline mode.
2033a845be7SRiver Riddle   void printResultsAsPipeline(raw_ostream &os, Timer *root,
2043a845be7SRiver Riddle                               TimeRecord totalTime);
2056558f80cSRiver Riddle 
2068e7b683dSRiver Riddle   /// Returns a timer for the provided identifier and name.
207120509a6SRiver Riddle   Timer *getTimer(const void *id, TimerKind kind,
208120509a6SRiver Riddle                   std::function<std::string()> &&nameBuilder) {
2093a845be7SRiver Riddle     auto tid = llvm::get_threadid();
2103a845be7SRiver Riddle 
2113a845be7SRiver Riddle     // If there is no active timer then add to the root timer.
2123a845be7SRiver Riddle     auto &activeTimers = activeThreadTimers[tid];
213120509a6SRiver Riddle     Timer *parentTimer;
2143a845be7SRiver Riddle     if (activeTimers.empty()) {
2153a845be7SRiver Riddle       auto &rootTimer = rootTimers[tid];
2163a845be7SRiver Riddle       if (!rootTimer)
217c900d499SChristian Sigg         rootTimer = std::make_unique<Timer>("root", TimerKind::Pipeline);
218120509a6SRiver Riddle       parentTimer = rootTimer.get();
219120509a6SRiver Riddle     } else {
220120509a6SRiver Riddle       // Otherwise, add this to the active timer.
221120509a6SRiver Riddle       parentTimer = activeTimers.back();
2228e7b683dSRiver Riddle     }
2236558f80cSRiver Riddle 
224120509a6SRiver Riddle     auto timer = parentTimer->getChildTimer(id, kind, std::move(nameBuilder));
2253a845be7SRiver Riddle     activeTimers.push_back(timer);
2263a845be7SRiver Riddle     return timer;
2273a845be7SRiver Riddle   }
2286558f80cSRiver Riddle 
2293a845be7SRiver Riddle   /// The root top level timers for each thread.
2303a845be7SRiver Riddle   DenseMap<uint64_t, std::unique_ptr<Timer>> rootTimers;
2313a845be7SRiver Riddle 
2323a845be7SRiver Riddle   /// A stack of the currently active pass timers per thread.
2333a845be7SRiver Riddle   DenseMap<uint64_t, SmallVector<Timer *, 4>> activeThreadTimers;
2346558f80cSRiver Riddle 
235*f65a3f7cSMehdi Amini   /// The configuration object to use when printing the timing results.
236*f65a3f7cSMehdi Amini   std::unique_ptr<PassManager::PassTimingConfig> config;
237120509a6SRiver Riddle 
238120509a6SRiver Riddle   /// A mapping of pipeline timers that need to be merged into the parent
2391c649d57SRiver Riddle   /// collection. The timers are mapped to the parent info to merge into.
2401c649d57SRiver Riddle   DenseMap<PipelineParentInfo, SmallVector<Timer::ChildrenMap::value_type, 4>>
241120509a6SRiver Riddle       pipelinesToMerge;
2426558f80cSRiver Riddle };
2436558f80cSRiver Riddle } // end anonymous namespace
244e46ba31cSRiver Riddle 
245120509a6SRiver Riddle void PassTiming::runBeforePipeline(const OperationName &name,
2461c649d57SRiver Riddle                                    const PipelineParentInfo &parentInfo) {
2475aacce3dSKazuaki Ishizaki   // We don't actually want to time the pipelines, they gather their total
248120509a6SRiver Riddle   // from their held passes.
249120509a6SRiver Riddle   getTimer(name.getAsOpaquePointer(), TimerKind::Pipeline,
250120509a6SRiver Riddle            [&] { return ("'" + name.getStringRef() + "' Pipeline").str(); });
251120509a6SRiver Riddle }
252120509a6SRiver Riddle 
253120509a6SRiver Riddle void PassTiming::runAfterPipeline(const OperationName &name,
2541c649d57SRiver Riddle                                   const PipelineParentInfo &parentInfo) {
255120509a6SRiver Riddle   // Pop the timer for the pipeline.
256120509a6SRiver Riddle   auto tid = llvm::get_threadid();
257120509a6SRiver Riddle   auto &activeTimers = activeThreadTimers[tid];
258120509a6SRiver Riddle   assert(!activeTimers.empty() && "expected active timer");
259120509a6SRiver Riddle   activeTimers.pop_back();
260120509a6SRiver Riddle 
261120509a6SRiver Riddle   // If the current thread is the same as the parent, there is nothing left to
262120509a6SRiver Riddle   // do.
2631c649d57SRiver Riddle   if (tid == parentInfo.parentThreadID)
264120509a6SRiver Riddle     return;
265120509a6SRiver Riddle 
266120509a6SRiver Riddle   // Otherwise, mark the pipeline timer for merging into the correct parent
267120509a6SRiver Riddle   // thread.
268120509a6SRiver Riddle   assert(activeTimers.empty() && "expected parent timer to be root");
269120509a6SRiver Riddle   auto *parentTimer = rootTimers[tid].get();
270120509a6SRiver Riddle   assert(parentTimer->children.size() == 1 &&
271120509a6SRiver Riddle          parentTimer->children.count(name.getAsOpaquePointer()) &&
272120509a6SRiver Riddle          "expected a single pipeline timer");
2731c649d57SRiver Riddle   pipelinesToMerge[parentInfo].push_back(
274120509a6SRiver Riddle       std::move(*parentTimer->children.begin()));
275120509a6SRiver Riddle   rootTimers.erase(tid);
276120509a6SRiver Riddle }
277120509a6SRiver Riddle 
278e46ba31cSRiver Riddle /// Start a new timer for the given pass.
279e46ba31cSRiver Riddle void PassTiming::startPassTimer(Pass *pass) {
280120509a6SRiver Riddle   auto kind = isAdaptorPass(pass) ? TimerKind::PipelineCollection
281120509a6SRiver Riddle                                   : TimerKind::PassOrAnalysis;
282120509a6SRiver Riddle   Timer *timer = getTimer(pass, kind, [pass]() -> std::string {
28333a64540SRiver Riddle     if (auto *adaptor = getAdaptorPassBase(pass))
28433a64540SRiver Riddle       return adaptor->getName();
285adcd0268SBenjamin Kramer     return std::string(pass->getName());
2868e7b683dSRiver Riddle   });
287e46ba31cSRiver Riddle 
2888e7b683dSRiver Riddle   // We don't actually want to time the adaptor passes, they gather their total
2898e7b683dSRiver Riddle   // from their held passes.
2908e7b683dSRiver Riddle   if (!isAdaptorPass(pass))
2918e7b683dSRiver Riddle     timer->start();
292e46ba31cSRiver Riddle }
293e46ba31cSRiver Riddle 
294e46ba31cSRiver Riddle /// Start a new timer for the given analysis.
295a517191aSRiver Riddle void PassTiming::startAnalysisTimer(StringRef name, TypeID id) {
296a517191aSRiver Riddle   Timer *timer = getTimer(id.getAsOpaquePointer(), TimerKind::PassOrAnalysis,
297120509a6SRiver Riddle                           [name] { return "(A) " + name.str(); });
2988e7b683dSRiver Riddle   timer->start();
299e46ba31cSRiver Riddle }
300e46ba31cSRiver Riddle 
3018e7b683dSRiver Riddle /// Stop a pass timer.
3024fb971a9SRiver Riddle void PassTiming::runAfterPass(Pass *pass, Operation *) {
303120509a6SRiver Riddle   Timer *timer = popLastActiveTimer();
3048e7b683dSRiver Riddle 
3055c036e68SRiver Riddle   // If this is an OpToOpPassAdaptorParallel, then we need to merge in the
306120509a6SRiver Riddle   // timing data for the pipelines running on other threads.
3075c036e68SRiver Riddle   if (isa<OpToOpPassAdaptorParallel>(pass)) {
3081c649d57SRiver Riddle     auto toMerge = pipelinesToMerge.find({llvm::get_threadid(), pass});
309120509a6SRiver Riddle     if (toMerge != pipelinesToMerge.end()) {
310120509a6SRiver Riddle       for (auto &it : toMerge->second)
311120509a6SRiver Riddle         timer->mergeChild(std::move(it));
312120509a6SRiver Riddle       pipelinesToMerge.erase(toMerge);
3133a845be7SRiver Riddle     }
3143a845be7SRiver Riddle     return;
3153a845be7SRiver Riddle   }
3163a845be7SRiver Riddle 
31784a6182dSKazuaki Ishizaki   // Adaptor passes aren't timed directly, so we don't need to stop their
3188e7b683dSRiver Riddle   // timers.
3198e7b683dSRiver Riddle   if (!isAdaptorPass(pass))
3208e7b683dSRiver Riddle     timer->stop();
321e46ba31cSRiver Riddle }
322e46ba31cSRiver Riddle 
3238e7b683dSRiver Riddle /// Stop a timer.
324a517191aSRiver Riddle void PassTiming::runAfterAnalysis(StringRef, TypeID, Operation *) {
325120509a6SRiver Riddle   popLastActiveTimer()->stop();
326e46ba31cSRiver Riddle }
327e46ba31cSRiver Riddle 
3288e7b683dSRiver Riddle /// Utility to print the timer heading information.
3294562e389SRiver Riddle static void printTimerHeader(raw_ostream &os, TimeRecord total) {
330e46ba31cSRiver Riddle   os << "===" << std::string(73, '-') << "===\n";
331e46ba31cSRiver Riddle   // Figure out how many spaces to description name.
33233a64540SRiver Riddle   unsigned padding = (80 - kPassTimingDescription.size()) / 2;
33333a64540SRiver Riddle   os.indent(padding) << kPassTimingDescription << '\n';
334e46ba31cSRiver Riddle   os << "===" << std::string(73, '-') << "===\n";
335e46ba31cSRiver Riddle 
3368e7b683dSRiver Riddle   // Print the total time followed by the section headers.
3373a845be7SRiver Riddle   os << llvm::format("  Total Execution Time: %5.4f seconds\n\n", total.wall);
3383a845be7SRiver Riddle   if (total.user != total.wall)
3393a845be7SRiver Riddle     os << "   ---User Time---";
3408e7b683dSRiver Riddle   os << "   ---Wall Time---  --- Name ---\n";
3418e7b683dSRiver Riddle }
342e46ba31cSRiver Riddle 
3438e7b683dSRiver Riddle /// Utility to print a single line entry in the timer output.
3448e7b683dSRiver Riddle static void printTimeEntry(raw_ostream &os, unsigned indent, StringRef name,
3453a845be7SRiver Riddle                            TimeRecord time, TimeRecord totalTime) {
3463a845be7SRiver Riddle   time.print(os, totalTime);
3478e7b683dSRiver Riddle   os.indent(indent) << name << "\n";
3488e7b683dSRiver Riddle }
3498e7b683dSRiver Riddle 
3508e7b683dSRiver Riddle /// Print out the current timing information.
3518e7b683dSRiver Riddle void PassTiming::print() {
3528e7b683dSRiver Riddle   // Don't print anything if there is no timing data.
3533a845be7SRiver Riddle   if (rootTimers.empty())
3548e7b683dSRiver Riddle     return;
3553a845be7SRiver Riddle 
3563a845be7SRiver Riddle   assert(rootTimers.size() == 1 && "expected one remaining root timer");
3578e7b683dSRiver Riddle 
358*f65a3f7cSMehdi Amini   auto printCallback = [&](raw_ostream &os) {
359*f65a3f7cSMehdi Amini     auto &rootTimer = rootTimers.begin()->second;
3608e7b683dSRiver Riddle     // Print the timer header.
3613a845be7SRiver Riddle     TimeRecord totalTime = rootTimer->getTotalTime();
362*f65a3f7cSMehdi Amini     printTimerHeader(os, totalTime);
3638e7b683dSRiver Riddle     // Defer to a specialized printer for each display mode.
364*f65a3f7cSMehdi Amini     switch (config->getDisplayMode()) {
36533a64540SRiver Riddle     case PassDisplayMode::List:
366*f65a3f7cSMehdi Amini       printResultsAsList(os, rootTimer.get(), totalTime);
3678e7b683dSRiver Riddle       break;
36833a64540SRiver Riddle     case PassDisplayMode::Pipeline:
369*f65a3f7cSMehdi Amini       printResultsAsPipeline(os, rootTimer.get(), totalTime);
3708e7b683dSRiver Riddle       break;
3718e7b683dSRiver Riddle     }
372*f65a3f7cSMehdi Amini     printTimeEntry(os, 0, "Total", totalTime, totalTime);
373*f65a3f7cSMehdi Amini     os.flush();
3748e7b683dSRiver Riddle 
3753a845be7SRiver Riddle     // Reset root timers.
3763a845be7SRiver Riddle     rootTimers.clear();
3773a845be7SRiver Riddle     activeThreadTimers.clear();
378*f65a3f7cSMehdi Amini   };
379*f65a3f7cSMehdi Amini 
380*f65a3f7cSMehdi Amini   config->printTiming(printCallback);
381*f65a3f7cSMehdi Amini }
382*f65a3f7cSMehdi Amini 
383*f65a3f7cSMehdi Amini // The default implementation for printTiming uses
384*f65a3f7cSMehdi Amini // `llvm::CreateInfoOutputFile()` as stream, it can be overridden by clients
385*f65a3f7cSMehdi Amini // to customize the output.
386*f65a3f7cSMehdi Amini void PassManager::PassTimingConfig::printTiming(PrintCallbackFn printCallback) {
387*f65a3f7cSMehdi Amini   printCallback(*llvm::CreateInfoOutputFile());
3888e7b683dSRiver Riddle }
3898e7b683dSRiver Riddle 
3908e7b683dSRiver Riddle /// Print the timing result in list mode.
3913a845be7SRiver Riddle void PassTiming::printResultsAsList(raw_ostream &os, Timer *root,
3923a845be7SRiver Riddle                                     TimeRecord totalTime) {
3933a845be7SRiver Riddle   llvm::StringMap<TimeRecord> mergedTimings;
3948e7b683dSRiver Riddle 
3958e7b683dSRiver Riddle   std::function<void(Timer *)> addTimer = [&](Timer *timer) {
396120509a6SRiver Riddle     // Only add timing information for passes and analyses.
397120509a6SRiver Riddle     if (timer->kind == TimerKind::PassOrAnalysis)
3988e7b683dSRiver Riddle       mergedTimings[timer->name] += timer->getTotalTime();
3998e7b683dSRiver Riddle     for (auto &children : timer->children)
4008e7b683dSRiver Riddle       addTimer(children.second.get());
4018e7b683dSRiver Riddle   };
4028e7b683dSRiver Riddle 
4038e7b683dSRiver Riddle   // Add each of the top level timers.
4043a845be7SRiver Riddle   for (auto &topLevelTimer : root->children)
4058e7b683dSRiver Riddle     addTimer(topLevelTimer.second.get());
4068e7b683dSRiver Riddle 
4073a845be7SRiver Riddle   // Sort the timing information by wall time.
4083a845be7SRiver Riddle   std::vector<std::pair<StringRef, TimeRecord>> timerNameAndTime;
4098e7b683dSRiver Riddle   for (auto &it : mergedTimings)
4108e7b683dSRiver Riddle     timerNameAndTime.emplace_back(it.first(), it.second);
4118e7b683dSRiver Riddle   llvm::array_pod_sort(timerNameAndTime.begin(), timerNameAndTime.end(),
4123a845be7SRiver Riddle                        [](const std::pair<StringRef, TimeRecord> *lhs,
4133a845be7SRiver Riddle                           const std::pair<StringRef, TimeRecord> *rhs) {
4148e7b683dSRiver Riddle                          return llvm::array_pod_sort_comparator<double>(
4153a845be7SRiver Riddle                              &rhs->second.wall, &lhs->second.wall);
4168e7b683dSRiver Riddle                        });
4178e7b683dSRiver Riddle 
4188e7b683dSRiver Riddle   // Print the timing information sequentially.
4198e7b683dSRiver Riddle   for (auto &timeData : timerNameAndTime)
4208e7b683dSRiver Riddle     printTimeEntry(os, 0, timeData.first, timeData.second, totalTime);
421e46ba31cSRiver Riddle }
422e46ba31cSRiver Riddle 
423e46ba31cSRiver Riddle /// Print the timing result in pipeline mode.
4243a845be7SRiver Riddle void PassTiming::printResultsAsPipeline(raw_ostream &os, Timer *root,
4253a845be7SRiver Riddle                                         TimeRecord totalTime) {
4268e7b683dSRiver Riddle   std::function<void(unsigned, Timer *)> printTimer = [&](unsigned indent,
4278e7b683dSRiver Riddle                                                           Timer *timer) {
428120509a6SRiver Riddle     // If this is a timer for a pipeline collection and the collection only has
429120509a6SRiver Riddle     // one pipeline child, then only print the child.
430120509a6SRiver Riddle     if (timer->kind == TimerKind::PipelineCollection &&
431120509a6SRiver Riddle         timer->children.size() == 1)
432120509a6SRiver Riddle       return printTimer(indent, timer->children.begin()->second.get());
433120509a6SRiver Riddle 
4348e7b683dSRiver Riddle     printTimeEntry(os, indent, timer->name, timer->getTotalTime(), totalTime);
435120509a6SRiver Riddle 
436120509a6SRiver Riddle     // If this timer is a pipeline, then print the children in-order.
437120509a6SRiver Riddle     if (timer->kind == TimerKind::Pipeline) {
438120509a6SRiver Riddle       for (auto &child : timer->children)
439120509a6SRiver Riddle         printTimer(indent + 2, child.second.get());
440120509a6SRiver Riddle       return;
441120509a6SRiver Riddle     }
442120509a6SRiver Riddle 
443120509a6SRiver Riddle     // Otherwise, sort the children by name to give a deterministic ordering
444120509a6SRiver Riddle     // when emitting the time.
445120509a6SRiver Riddle     SmallVector<Timer *, 4> children;
446120509a6SRiver Riddle     children.reserve(timer->children.size());
447120509a6SRiver Riddle     for (auto &child : timer->children)
448120509a6SRiver Riddle       children.push_back(child.second.get());
449120509a6SRiver Riddle     llvm::array_pod_sort(children.begin(), children.end(),
450120509a6SRiver Riddle                          [](Timer *const *lhs, Timer *const *rhs) {
451120509a6SRiver Riddle                            return (*lhs)->name.compare((*rhs)->name);
452120509a6SRiver Riddle                          });
453120509a6SRiver Riddle     for (auto &child : children)
454120509a6SRiver Riddle       printTimer(indent + 2, child);
455e46ba31cSRiver Riddle   };
456e46ba31cSRiver Riddle 
4578e7b683dSRiver Riddle   // Print each of the top level timers.
4583a845be7SRiver Riddle   for (auto &topLevelTimer : root->children)
4598e7b683dSRiver Riddle     printTimer(0, topLevelTimer.second.get());
460e46ba31cSRiver Riddle }
4616558f80cSRiver Riddle 
462*f65a3f7cSMehdi Amini // Out-of-line as key function.
463*f65a3f7cSMehdi Amini PassManager::PassTimingConfig::~PassTimingConfig() {}
464*f65a3f7cSMehdi Amini 
4656558f80cSRiver Riddle //===----------------------------------------------------------------------===//
4666558f80cSRiver Riddle // PassManager
4676558f80cSRiver Riddle //===----------------------------------------------------------------------===//
4686558f80cSRiver Riddle 
4696558f80cSRiver Riddle /// Add an instrumentation to time the execution of passes and the computation
4706558f80cSRiver Riddle /// of analyses.
471*f65a3f7cSMehdi Amini void PassManager::enableTiming(std::unique_ptr<PassTimingConfig> config) {
4726558f80cSRiver Riddle   // Check if pass timing is already enabled.
4736558f80cSRiver Riddle   if (passTiming)
4746558f80cSRiver Riddle     return;
475*f65a3f7cSMehdi Amini   if (!config)
476*f65a3f7cSMehdi Amini     config = std::make_unique<PassManager::PassTimingConfig>();
477*f65a3f7cSMehdi Amini   addInstrumentation(std::make_unique<PassTiming>(std::move(config)));
4786558f80cSRiver Riddle   passTiming = true;
4796558f80cSRiver Riddle }
480