1e46ba31cSRiver Riddle //===- PassTiming.cpp -----------------------------------------------------===//
2e46ba31cSRiver Riddle //
3e46ba31cSRiver Riddle // Copyright 2019 The MLIR Authors.
4e46ba31cSRiver Riddle //
5e46ba31cSRiver Riddle // Licensed under the Apache License, Version 2.0 (the "License");
6e46ba31cSRiver Riddle // you may not use this file except in compliance with the License.
7e46ba31cSRiver Riddle // You may obtain a copy of the License at
8e46ba31cSRiver Riddle //
9e46ba31cSRiver Riddle //   http://www.apache.org/licenses/LICENSE-2.0
10e46ba31cSRiver Riddle //
11e46ba31cSRiver Riddle // Unless required by applicable law or agreed to in writing, software
12e46ba31cSRiver Riddle // distributed under the License is distributed on an "AS IS" BASIS,
13e46ba31cSRiver Riddle // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14e46ba31cSRiver Riddle // See the License for the specific language governing permissions and
15e46ba31cSRiver Riddle // limitations under the License.
16e46ba31cSRiver Riddle // =============================================================================
17e46ba31cSRiver Riddle 
18e46ba31cSRiver Riddle #include "PassDetail.h"
19*6558f80cSRiver Riddle #include "mlir/Pass/PassManager.h"
20*6558f80cSRiver Riddle #include "llvm/ADT/MapVector.h"
21e46ba31cSRiver Riddle #include "llvm/ADT/SmallVector.h"
22e46ba31cSRiver Riddle #include "llvm/ADT/Statistic.h"
23e46ba31cSRiver Riddle #include "llvm/Support/Format.h"
24e46ba31cSRiver Riddle #include "llvm/Support/FormatVariadic.h"
25e46ba31cSRiver Riddle #include "llvm/Support/Timer.h"
26e46ba31cSRiver Riddle 
27e46ba31cSRiver Riddle using namespace mlir;
28e46ba31cSRiver Riddle using namespace mlir::detail;
29e46ba31cSRiver Riddle 
30e46ba31cSRiver Riddle constexpr llvm::StringLiteral kPassTimingDescription =
31e46ba31cSRiver Riddle     "... Pass execution timing report ...";
32e46ba31cSRiver Riddle 
33e46ba31cSRiver Riddle /// Utility function to return if a pass refers to an adaptor pass. Adaptor
34e46ba31cSRiver Riddle /// passes are those that internally execute a pipeline, such as the
35e46ba31cSRiver Riddle /// ModuleToFunctionPassAdaptor.
36e46ba31cSRiver Riddle static bool isAdaptorPass(Pass *pass) {
37e46ba31cSRiver Riddle   return isa<ModuleToFunctionPassAdaptor>(pass);
38e46ba31cSRiver Riddle }
39e46ba31cSRiver Riddle 
40*6558f80cSRiver Riddle namespace {
41*6558f80cSRiver Riddle struct PassTiming : public PassInstrumentation {
42*6558f80cSRiver Riddle   PassTiming(PassTimingDisplayMode displayMode) : displayMode(displayMode) {}
43*6558f80cSRiver Riddle   ~PassTiming() { print(); }
44e46ba31cSRiver Riddle 
45*6558f80cSRiver Riddle   /// Setup the instrumentation hooks.
46*6558f80cSRiver Riddle   void runBeforePass(Pass *pass, const llvm::Any &) override {
47*6558f80cSRiver Riddle     startPassTimer(pass);
48*6558f80cSRiver Riddle   }
49*6558f80cSRiver Riddle   void runAfterPass(Pass *pass, const llvm::Any &) override {
50*6558f80cSRiver Riddle     stopPassTimer(pass);
51*6558f80cSRiver Riddle   }
52*6558f80cSRiver Riddle   void runAfterPassFailed(Pass *pass, const llvm::Any &) override {
53*6558f80cSRiver Riddle     stopPassTimer(pass);
54*6558f80cSRiver Riddle   }
55*6558f80cSRiver Riddle   void runBeforeAnalysis(llvm::StringRef name, AnalysisID *id,
56*6558f80cSRiver Riddle                          const llvm::Any &) override {
57*6558f80cSRiver Riddle     startAnalysisTimer(name, id);
58*6558f80cSRiver Riddle   }
59*6558f80cSRiver Riddle   void runAfterAnalysis(llvm::StringRef name, AnalysisID *id,
60*6558f80cSRiver Riddle                         const llvm::Any &) override {
61*6558f80cSRiver Riddle     stopAnalysisTimer(name, id);
62*6558f80cSRiver Riddle   }
63*6558f80cSRiver Riddle 
64*6558f80cSRiver Riddle   /// Print and clear the timing results.
65*6558f80cSRiver Riddle   void print();
66*6558f80cSRiver Riddle 
67*6558f80cSRiver Riddle   /// Start a new timer for the given pass.
68*6558f80cSRiver Riddle   void startPassTimer(Pass *pass);
69*6558f80cSRiver Riddle 
70*6558f80cSRiver Riddle   /// Stop a timer for the given pass.
71*6558f80cSRiver Riddle   void stopPassTimer(Pass *pass);
72*6558f80cSRiver Riddle 
73*6558f80cSRiver Riddle   /// Start a new timer for the given analysis.
74*6558f80cSRiver Riddle   void startAnalysisTimer(llvm::StringRef name, AnalysisID *id);
75*6558f80cSRiver Riddle 
76*6558f80cSRiver Riddle   /// Stop a timer for the given analysis.
77*6558f80cSRiver Riddle   void stopAnalysisTimer(llvm::StringRef name, AnalysisID *id);
78*6558f80cSRiver Riddle 
79*6558f80cSRiver Riddle   /// Print the timing result in list mode.
80*6558f80cSRiver Riddle   void printResultsAsList(llvm::raw_ostream &os);
81*6558f80cSRiver Riddle 
82*6558f80cSRiver Riddle   /// Print the timing result in pipeline mode.
83*6558f80cSRiver Riddle   void printResultsAsPipeline(llvm::raw_ostream &os);
84*6558f80cSRiver Riddle 
85*6558f80cSRiver Riddle   /// Mapping between pass and a respective timer.
86*6558f80cSRiver Riddle   llvm::MapVector<Pass *, std::unique_ptr<llvm::Timer>> passTimers;
87*6558f80cSRiver Riddle 
88*6558f80cSRiver Riddle   /// Mapping between [analysis id, pass] and a respective timer.
89*6558f80cSRiver Riddle   llvm::DenseMap<std::pair<AnalysisID *, Pass *>, std::unique_ptr<llvm::Timer>>
90*6558f80cSRiver Riddle       analysisTimers;
91*6558f80cSRiver Riddle 
92*6558f80cSRiver Riddle   /// A pointer to the currently active pass, or null.
93*6558f80cSRiver Riddle   Pass *activePass = nullptr;
94*6558f80cSRiver Riddle 
95*6558f80cSRiver Riddle   /// The display mode to use when printing the timing results.
96*6558f80cSRiver Riddle   PassTimingDisplayMode displayMode;
97*6558f80cSRiver Riddle };
98*6558f80cSRiver Riddle } // end anonymous namespace
99e46ba31cSRiver Riddle 
100e46ba31cSRiver Riddle /// Print out the current timing information.
101e46ba31cSRiver Riddle void PassTiming::print() {
102e46ba31cSRiver Riddle   // Don't print anything if there is no timing data.
103e46ba31cSRiver Riddle   if (passTimers.empty() && analysisTimers.empty())
104e46ba31cSRiver Riddle     return;
105e46ba31cSRiver Riddle 
106e46ba31cSRiver Riddle   switch (displayMode) {
107*6558f80cSRiver Riddle   case PassTimingDisplayMode::List:
108e46ba31cSRiver Riddle     printResultsAsList(*llvm::CreateInfoOutputFile());
109e46ba31cSRiver Riddle     break;
110*6558f80cSRiver Riddle   case PassTimingDisplayMode::Pipeline:
111e46ba31cSRiver Riddle     printResultsAsPipeline(*llvm::CreateInfoOutputFile());
112e46ba31cSRiver Riddle     break;
113e46ba31cSRiver Riddle   }
114e46ba31cSRiver Riddle 
115e46ba31cSRiver Riddle   // Reset and clear the timers.
116e46ba31cSRiver Riddle   for (auto &timerPair : passTimers)
117e46ba31cSRiver Riddle     timerPair.second->clear();
118e46ba31cSRiver Riddle   for (auto &timerPair : analysisTimers)
119e46ba31cSRiver Riddle     timerPair.second->clear();
120e46ba31cSRiver Riddle   passTimers.clear();
121e46ba31cSRiver Riddle   analysisTimers.clear();
122e46ba31cSRiver Riddle }
123e46ba31cSRiver Riddle 
124e46ba31cSRiver Riddle /// Start a new timer for the given pass.
125e46ba31cSRiver Riddle void PassTiming::startPassTimer(Pass *pass) {
126e46ba31cSRiver Riddle   std::unique_ptr<llvm::Timer> &timer = passTimers[pass];
127e46ba31cSRiver Riddle 
128e46ba31cSRiver Riddle   /// If the timer doesn't exist then create a new one.
129e46ba31cSRiver Riddle   if (!timer) {
130e46ba31cSRiver Riddle     auto passName = pass->getName();
131e46ba31cSRiver Riddle     timer.reset(new llvm::Timer(passName, passName));
132e46ba31cSRiver Riddle   }
133e46ba31cSRiver Riddle 
134e46ba31cSRiver Riddle   activePass = pass;
135e46ba31cSRiver Riddle   timer->startTimer();
136e46ba31cSRiver Riddle }
137e46ba31cSRiver Riddle 
138e46ba31cSRiver Riddle /// Stop a timer for the given pass.
139e46ba31cSRiver Riddle void PassTiming::stopPassTimer(Pass *pass) {
140e46ba31cSRiver Riddle   std::unique_ptr<llvm::Timer> &timer = passTimers[pass];
141e46ba31cSRiver Riddle   assert(timer && "expected valid timer to stop");
142e46ba31cSRiver Riddle   timer->stopTimer();
143e46ba31cSRiver Riddle   activePass = nullptr;
144e46ba31cSRiver Riddle }
145e46ba31cSRiver Riddle 
146e46ba31cSRiver Riddle /// Start a new timer for the given analysis.
147e46ba31cSRiver Riddle void PassTiming::startAnalysisTimer(llvm::StringRef name, AnalysisID *id) {
148e46ba31cSRiver Riddle   auto &timer = analysisTimers[std::make_pair(id, activePass)];
149e46ba31cSRiver Riddle 
150e46ba31cSRiver Riddle   /// If the timer doesn't exist then create a new one.
151e46ba31cSRiver Riddle   if (!timer)
152e46ba31cSRiver Riddle     timer.reset(new llvm::Timer(name, Twine("(A) " + name).str()));
153e46ba31cSRiver Riddle   timer->startTimer();
154e46ba31cSRiver Riddle }
155e46ba31cSRiver Riddle 
156e46ba31cSRiver Riddle /// Stop a timer for the given analysis.
157e46ba31cSRiver Riddle void PassTiming::stopAnalysisTimer(llvm::StringRef name, AnalysisID *id) {
158e46ba31cSRiver Riddle   auto &timer = analysisTimers[std::make_pair(id, activePass)];
159e46ba31cSRiver Riddle   assert(timer && "expected a valid timer to stop");
160e46ba31cSRiver Riddle   timer->stopTimer();
161e46ba31cSRiver Riddle }
162e46ba31cSRiver Riddle 
163e46ba31cSRiver Riddle /// Print the timing result in list mode.
164e46ba31cSRiver Riddle void PassTiming::printResultsAsList(llvm::raw_ostream &os) {
165e46ba31cSRiver Riddle   // Build a map of timer records uniqued by the timer name.
166e46ba31cSRiver Riddle   llvm::StringMap<llvm::TimeRecord> records;
167e46ba31cSRiver Riddle   auto addTimer = [&](llvm::Timer *timer) {
168e46ba31cSRiver Riddle     auto it = records.try_emplace(timer->getName(), timer->getTotalTime());
169e46ba31cSRiver Riddle     if (!it.second)
170e46ba31cSRiver Riddle       it.first->second += timer->getTotalTime();
171e46ba31cSRiver Riddle   };
172e46ba31cSRiver Riddle 
173e46ba31cSRiver Riddle   // Add all non-adaptor classes to the time records.
174e46ba31cSRiver Riddle   for (auto &timerPair : passTimers)
175e46ba31cSRiver Riddle     if (!isAdaptorPass(timerPair.first))
176e46ba31cSRiver Riddle       addTimer(timerPair.second.get());
177e46ba31cSRiver Riddle 
178e46ba31cSRiver Riddle   // Add the analysis timers.
179e46ba31cSRiver Riddle   for (auto &timerPair : analysisTimers)
180e46ba31cSRiver Riddle     addTimer(timerPair.second.get());
181e46ba31cSRiver Riddle 
182e46ba31cSRiver Riddle   // Create a timer group for the records and print it out.
183e46ba31cSRiver Riddle   llvm::TimerGroup timerGroup("pass", kPassTimingDescription, records);
184e46ba31cSRiver Riddle   timerGroup.print(os);
185e46ba31cSRiver Riddle }
186e46ba31cSRiver Riddle 
187e46ba31cSRiver Riddle /// Utility to print the heading information for the pipeline timer.
188e46ba31cSRiver Riddle /// Note: This is a replication of the header generated by llvm::TimerGroup.
189e46ba31cSRiver Riddle static void printPipelineTimerHeader(llvm::raw_ostream &os,
190e46ba31cSRiver Riddle                                      llvm::TimeRecord &pipelineTotal) {
191e46ba31cSRiver Riddle   os << "===" << std::string(73, '-') << "===\n";
192e46ba31cSRiver Riddle   // Figure out how many spaces to description name.
193e46ba31cSRiver Riddle   unsigned Padding = (80 - kPassTimingDescription.size()) / 2;
194e46ba31cSRiver Riddle   os.indent(Padding) << kPassTimingDescription << '\n';
195e46ba31cSRiver Riddle   os << "===" << std::string(73, '-') << "===\n";
196e46ba31cSRiver Riddle 
197e46ba31cSRiver Riddle   // Print the total time.
198e46ba31cSRiver Riddle   os << llvm::format(
199e46ba31cSRiver Riddle       "  Total Execution Time: %5.4f seconds (%5.4f wall clock)\n\n",
200e46ba31cSRiver Riddle       pipelineTotal.getProcessTime(), pipelineTotal.getWallTime());
201e46ba31cSRiver Riddle 
202e46ba31cSRiver Riddle   // Add the headers for each time section.
203e46ba31cSRiver Riddle   if (pipelineTotal.getUserTime())
204e46ba31cSRiver Riddle     os << "   ---User Time---";
205e46ba31cSRiver Riddle   if (pipelineTotal.getSystemTime())
206e46ba31cSRiver Riddle     os << "   --System Time--";
207e46ba31cSRiver Riddle   if (pipelineTotal.getProcessTime())
208e46ba31cSRiver Riddle     os << "   --User+System--";
209e46ba31cSRiver Riddle   os << "   ---Wall Time---";
210e46ba31cSRiver Riddle   if (pipelineTotal.getMemUsed())
211e46ba31cSRiver Riddle     os << "  ---Mem---";
212e46ba31cSRiver Riddle   os << "  --- Name ---\n";
213e46ba31cSRiver Riddle }
214e46ba31cSRiver Riddle 
215e46ba31cSRiver Riddle /// Print the timing result in pipeline mode.
216e46ba31cSRiver Riddle void PassTiming::printResultsAsPipeline(llvm::raw_ostream &os) {
217e46ba31cSRiver Riddle   // Collect the total time information for each of the non-adaptor passes.
218e46ba31cSRiver Riddle   llvm::TimeRecord pipelineTotal;
219e46ba31cSRiver Riddle   for (auto &timerPair : passTimers)
220e46ba31cSRiver Riddle     if (!isAdaptorPass(timerPair.first))
221e46ba31cSRiver Riddle       pipelineTotal += timerPair.second->getTotalTime();
222e46ba31cSRiver Riddle 
223e46ba31cSRiver Riddle   // Collect the analysis time records for each pass.
224e46ba31cSRiver Riddle   llvm::DenseMap<Pass *, std::vector<llvm::Timer *>> passAnalyses;
225e46ba31cSRiver Riddle   for (auto &timerPair : analysisTimers) {
226e46ba31cSRiver Riddle     passAnalyses[timerPair.first.second].push_back(timerPair.second.get());
227e46ba31cSRiver Riddle     pipelineTotal += timerPair.second->getTotalTime();
228e46ba31cSRiver Riddle   }
229e46ba31cSRiver Riddle   // Sort each of the analysis timers.
230e46ba31cSRiver Riddle   for (auto &analysisPair : passAnalyses) {
231e46ba31cSRiver Riddle     llvm::array_pod_sort(
232e46ba31cSRiver Riddle         analysisPair.second.begin(), analysisPair.second.end(),
233e46ba31cSRiver Riddle         [](llvm::Timer *const *lhsTimer, llvm::Timer *const *rhsTimer) -> int {
234e46ba31cSRiver Riddle           return (*lhsTimer)->getDescription().compare(
235e46ba31cSRiver Riddle               (*rhsTimer)->getDescription());
236e46ba31cSRiver Riddle         });
237e46ba31cSRiver Riddle   }
238e46ba31cSRiver Riddle 
239e46ba31cSRiver Riddle   // Print out timing header.
240e46ba31cSRiver Riddle   printPipelineTimerHeader(os, pipelineTotal);
241e46ba31cSRiver Riddle 
242e46ba31cSRiver Riddle   // Print the formatted timing record.
243e46ba31cSRiver Riddle   unsigned currentIndent = 0;
244e46ba31cSRiver Riddle   auto printTimer = [&](llvm::StringRef name, llvm::TimeRecord timeRecord) {
245e46ba31cSRiver Riddle     timeRecord.print(pipelineTotal, os);
246e46ba31cSRiver Riddle     os.indent(currentIndent) << name;
247e46ba31cSRiver Riddle     os << "\n";
248e46ba31cSRiver Riddle   };
249e46ba31cSRiver Riddle 
250e46ba31cSRiver Riddle   // Utility to print the timing information for a pass and its analyses.
251e46ba31cSRiver Riddle   auto printPassTimer = [&](Pass *pass, llvm::Timer *passTimer) {
252e46ba31cSRiver Riddle     printTimer(passTimer->getDescription(), passTimer->getTotalTime());
253e46ba31cSRiver Riddle 
254e46ba31cSRiver Riddle     // Print the computed analyses for this pass.
255e46ba31cSRiver Riddle     currentIndent += 2;
256e46ba31cSRiver Riddle     for (llvm::Timer *timer : passAnalyses[pass])
257e46ba31cSRiver Riddle       printTimer(timer->getDescription(), timer->getTotalTime());
258e46ba31cSRiver Riddle     currentIndent -= 2;
259e46ba31cSRiver Riddle   };
260e46ba31cSRiver Riddle 
261e46ba31cSRiver Riddle   // Print the total execution time.
262e46ba31cSRiver Riddle   for (auto it = passTimers.begin(), e = passTimers.end(); it != e;) {
263e46ba31cSRiver Riddle     // Handle a ModuleToFunctionAdaptor pass.
264e46ba31cSRiver Riddle     if (isa<ModuleToFunctionPassAdaptor>(it->first)) {
265e46ba31cSRiver Riddle       // Print the time for this adaptor as the accumulation of each of the
266e46ba31cSRiver Riddle       // nested function passes.
267e46ba31cSRiver Riddle       llvm::TimeRecord total;
268e46ba31cSRiver Riddle       for (auto fpIt = ++it; fpIt != e && isa<FunctionPassBase>(fpIt->first);) {
269e46ba31cSRiver Riddle         total += fpIt->second->getTotalTime();
270e46ba31cSRiver Riddle         for (llvm::Timer *timer : passAnalyses[(fpIt++)->first])
271e46ba31cSRiver Riddle           total += timer->getTotalTime();
272e46ba31cSRiver Riddle       }
273e46ba31cSRiver Riddle       printTimer("Function Pipeline", total);
274e46ba31cSRiver Riddle 
275e46ba31cSRiver Riddle       // Update the indent and print the time for each of the function passes
276e46ba31cSRiver Riddle       // within the pipeline.
277e46ba31cSRiver Riddle       currentIndent += 2;
278e46ba31cSRiver Riddle       for (; it != e && isa<FunctionPassBase>(it->first); ++it)
279e46ba31cSRiver Riddle         printPassTimer(it->first, it->second.get());
280e46ba31cSRiver Riddle       currentIndent -= 2;
281e46ba31cSRiver Riddle       continue;
282e46ba31cSRiver Riddle     }
283e46ba31cSRiver Riddle 
284e46ba31cSRiver Riddle     // Otherwise, we print the pass timer directly.
285e46ba31cSRiver Riddle     printPassTimer(it->first, it->second.get());
286e46ba31cSRiver Riddle     ++it;
287e46ba31cSRiver Riddle   }
288e46ba31cSRiver Riddle 
289e46ba31cSRiver Riddle   printTimer("Total\n", pipelineTotal);
290e46ba31cSRiver Riddle   os.flush();
291e46ba31cSRiver Riddle }
292*6558f80cSRiver Riddle 
293*6558f80cSRiver Riddle //===----------------------------------------------------------------------===//
294*6558f80cSRiver Riddle // PassManager
295*6558f80cSRiver Riddle //===----------------------------------------------------------------------===//
296*6558f80cSRiver Riddle 
297*6558f80cSRiver Riddle /// Add an instrumentation to time the execution of passes and the computation
298*6558f80cSRiver Riddle /// of analyses.
299*6558f80cSRiver Riddle void PassManager::enableTiming(PassTimingDisplayMode displayMode) {
300*6558f80cSRiver Riddle   // Check if pass timing is already enabled.
301*6558f80cSRiver Riddle   if (passTiming)
302*6558f80cSRiver Riddle     return;
303*6558f80cSRiver Riddle   addInstrumentation(new PassTiming(displayMode));
304*6558f80cSRiver Riddle   passTiming = true;
305*6558f80cSRiver Riddle }
306