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"
11e46ba31cSRiver Riddle #include "llvm/ADT/SmallVector.h"
123a845be7SRiver Riddle #include "llvm/Support/Threading.h"
1333f908c4SFabian Schuiki 
148e7b683dSRiver Riddle #include <chrono>
15e46ba31cSRiver Riddle 
16e46ba31cSRiver Riddle using namespace mlir;
17e46ba31cSRiver Riddle using namespace mlir::detail;
18e46ba31cSRiver Riddle 
1933f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
2033f908c4SFabian Schuiki // PassTiming
2133f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
22e46ba31cSRiver Riddle 
236558f80cSRiver Riddle namespace {
246558f80cSRiver Riddle struct PassTiming : public PassInstrumentation {
PassTiming__anon2e0843fe0111::PassTiming2533f908c4SFabian Schuiki   PassTiming(TimingScope &timingScope) : rootScope(timingScope) {}
PassTiming__anon2e0843fe0111::PassTiming2633f908c4SFabian Schuiki   PassTiming(std::unique_ptr<TimingManager> tm)
2733f908c4SFabian Schuiki       : ownedTimingManager(std::move(tm)),
2833f908c4SFabian Schuiki         ownedTimingScope(ownedTimingManager->getRootScope()),
2933f908c4SFabian Schuiki         rootScope(ownedTimingScope) {}
30e46ba31cSRiver Riddle 
3133f908c4SFabian Schuiki   /// If a pass can spawn additional work on other threads, it records the
3233f908c4SFabian Schuiki   /// index to its currently active timer here. Passes that run on a
3333f908c4SFabian Schuiki   /// newly-forked thread will check this list to find the active timer of the
3433f908c4SFabian Schuiki   /// parent thread into which the new thread should be nested.
3533f908c4SFabian Schuiki   DenseMap<PipelineParentInfo, unsigned> parentTimerIndices;
3633f908c4SFabian Schuiki 
3733f908c4SFabian Schuiki   /// A stack of the currently active timing scopes per thread.
3833f908c4SFabian Schuiki   DenseMap<uint64_t, SmallVector<TimingScope, 4>> activeThreadTimers;
3933f908c4SFabian Schuiki 
4033f908c4SFabian Schuiki   /// The timing manager owned by this instrumentation (in case timing was
4133f908c4SFabian Schuiki   /// enabled by the user on the pass manager without providing an external
4233f908c4SFabian Schuiki   /// timing manager). This *must* appear before the `ownedTimingScope` to
4333f908c4SFabian Schuiki   /// ensure the timing manager is destroyed *after* the scope, since the latter
4433f908c4SFabian Schuiki   /// may hold a timer that points into the former.
4533f908c4SFabian Schuiki   std::unique_ptr<TimingManager> ownedTimingManager;
4633f908c4SFabian Schuiki   TimingScope ownedTimingScope;
4733f908c4SFabian Schuiki 
4833f908c4SFabian Schuiki   /// The root timing scope into which timing is reported.
4933f908c4SFabian Schuiki   TimingScope &rootScope;
5033f908c4SFabian Schuiki 
5133f908c4SFabian Schuiki   //===--------------------------------------------------------------------===//
5233f908c4SFabian Schuiki   // Pipeline
5333f908c4SFabian Schuiki   //===--------------------------------------------------------------------===//
5433f908c4SFabian Schuiki 
runBeforePipeline__anon2e0843fe0111::PassTiming55*c2fb9c29SRiver Riddle   void runBeforePipeline(Optional<OperationName> name,
5633f908c4SFabian Schuiki                          const PipelineParentInfo &parentInfo) override {
57120509a6SRiver Riddle     auto tid = llvm::get_threadid();
58120509a6SRiver Riddle     auto &activeTimers = activeThreadTimers[tid];
596558f80cSRiver Riddle 
6033f908c4SFabian Schuiki     TimingScope *parentScope;
613a845be7SRiver Riddle     if (activeTimers.empty()) {
6233f908c4SFabian Schuiki       auto it = parentTimerIndices.find(parentInfo);
6333f908c4SFabian Schuiki       if (it != parentTimerIndices.end())
6433f908c4SFabian Schuiki         parentScope =
6533f908c4SFabian Schuiki             &activeThreadTimers[parentInfo.parentThreadID][it->second];
6633f908c4SFabian Schuiki       else
6733f908c4SFabian Schuiki         parentScope = &rootScope;
68120509a6SRiver Riddle     } else {
6933f908c4SFabian Schuiki       parentScope = &activeTimers.back();
7033f908c4SFabian Schuiki     }
71*c2fb9c29SRiver Riddle 
72*c2fb9c29SRiver Riddle     // Use nullptr to anchor op-agnostic pipelines, otherwise use the name of
73*c2fb9c29SRiver Riddle     // the operation.
74*c2fb9c29SRiver Riddle     const void *timerId = name ? name->getAsOpaquePointer() : nullptr;
75*c2fb9c29SRiver Riddle     activeTimers.push_back(parentScope->nest(timerId, [name] {
76*c2fb9c29SRiver Riddle       return ("'" + (name ? name->getStringRef() : "any") + "' Pipeline").str();
7733f908c4SFabian Schuiki     }));
788e7b683dSRiver Riddle   }
796558f80cSRiver Riddle 
runAfterPipeline__anon2e0843fe0111::PassTiming80*c2fb9c29SRiver Riddle   void runAfterPipeline(Optional<OperationName>,
81*c2fb9c29SRiver Riddle                         const PipelineParentInfo &) override {
8233f908c4SFabian Schuiki     auto &activeTimers = activeThreadTimers[llvm::get_threadid()];
8333f908c4SFabian Schuiki     assert(!activeTimers.empty() && "expected active timer");
8433f908c4SFabian Schuiki     activeTimers.pop_back();
853a845be7SRiver Riddle   }
866558f80cSRiver Riddle 
8733f908c4SFabian Schuiki   //===--------------------------------------------------------------------===//
8833f908c4SFabian Schuiki   // Pass
8933f908c4SFabian Schuiki   //===--------------------------------------------------------------------===//
903a845be7SRiver Riddle 
runBeforePass__anon2e0843fe0111::PassTiming9133f908c4SFabian Schuiki   void runBeforePass(Pass *pass, Operation *) override {
92120509a6SRiver Riddle     auto tid = llvm::get_threadid();
93120509a6SRiver Riddle     auto &activeTimers = activeThreadTimers[tid];
9433f908c4SFabian Schuiki     auto &parentScope = activeTimers.empty() ? rootScope : activeTimers.back();
9533f908c4SFabian Schuiki 
9633f908c4SFabian Schuiki     if (auto *adaptor = dyn_cast<OpToOpPassAdaptor>(pass)) {
9733f908c4SFabian Schuiki       parentTimerIndices[{tid, pass}] = activeTimers.size();
9833f908c4SFabian Schuiki       auto scope =
9933f908c4SFabian Schuiki           parentScope.nest(pass->getThreadingSiblingOrThis(),
10033f908c4SFabian Schuiki                            [adaptor]() { return adaptor->getAdaptorName(); });
10133f908c4SFabian Schuiki       if (adaptor->getPassManagers().size() <= 1)
10233f908c4SFabian Schuiki         scope.hide();
10333f908c4SFabian Schuiki       activeTimers.push_back(std::move(scope));
10433f908c4SFabian Schuiki     } else {
10533f908c4SFabian Schuiki       activeTimers.push_back(
10633f908c4SFabian Schuiki           parentScope.nest(pass->getThreadingSiblingOrThis(),
10733f908c4SFabian Schuiki                            [pass]() { return std::string(pass->getName()); }));
10833f908c4SFabian Schuiki     }
10933f908c4SFabian Schuiki   }
11033f908c4SFabian Schuiki 
runAfterPass__anon2e0843fe0111::PassTiming11133f908c4SFabian Schuiki   void runAfterPass(Pass *pass, Operation *) override {
11233f908c4SFabian Schuiki     auto tid = llvm::get_threadid();
11333f908c4SFabian Schuiki     if (isa<OpToOpPassAdaptor>(pass))
11433f908c4SFabian Schuiki       parentTimerIndices.erase({tid, pass});
11533f908c4SFabian Schuiki     auto &activeTimers = activeThreadTimers[tid];
116120509a6SRiver Riddle     assert(!activeTimers.empty() && "expected active timer");
117120509a6SRiver Riddle     activeTimers.pop_back();
118120509a6SRiver Riddle   }
119120509a6SRiver Riddle 
runAfterPassFailed__anon2e0843fe0111::PassTiming12033f908c4SFabian Schuiki   void runAfterPassFailed(Pass *pass, Operation *op) override {
12133f908c4SFabian Schuiki     runAfterPass(pass, op);
122e46ba31cSRiver Riddle   }
123e46ba31cSRiver Riddle 
12433f908c4SFabian Schuiki   //===--------------------------------------------------------------------===//
12533f908c4SFabian Schuiki   // Analysis
12633f908c4SFabian Schuiki   //===--------------------------------------------------------------------===//
12733f908c4SFabian Schuiki 
runBeforeAnalysis__anon2e0843fe0111::PassTiming12833f908c4SFabian Schuiki   void runBeforeAnalysis(StringRef name, TypeID id, Operation *) override {
12933f908c4SFabian Schuiki     auto tid = llvm::get_threadid();
13033f908c4SFabian Schuiki     auto &activeTimers = activeThreadTimers[tid];
13133f908c4SFabian Schuiki     auto &parentScope = activeTimers.empty() ? rootScope : activeTimers.back();
13233f908c4SFabian Schuiki     activeTimers.push_back(parentScope.nest(
13333f908c4SFabian Schuiki         id.getAsOpaquePointer(), [name] { return "(A) " + name.str(); }));
134e46ba31cSRiver Riddle   }
135e46ba31cSRiver Riddle 
runAfterAnalysis__anon2e0843fe0111::PassTiming13633f908c4SFabian Schuiki   void runAfterAnalysis(StringRef, TypeID, Operation *) override {
13733f908c4SFabian Schuiki     auto &activeTimers = activeThreadTimers[llvm::get_threadid()];
13833f908c4SFabian Schuiki     assert(!activeTimers.empty() && "expected active timer");
13933f908c4SFabian Schuiki     activeTimers.pop_back();
1403a845be7SRiver Riddle   }
141f65a3f7cSMehdi Amini };
14233f908c4SFabian Schuiki } // namespace
143f65a3f7cSMehdi Amini 
1446558f80cSRiver Riddle //===----------------------------------------------------------------------===//
1456558f80cSRiver Riddle // PassManager
1466558f80cSRiver Riddle //===----------------------------------------------------------------------===//
1476558f80cSRiver Riddle 
1486558f80cSRiver Riddle /// Add an instrumentation to time the execution of passes and the computation
1496558f80cSRiver Riddle /// of analyses.
enableTiming(TimingScope & timingScope)15033f908c4SFabian Schuiki void PassManager::enableTiming(TimingScope &timingScope) {
15133f908c4SFabian Schuiki   if (!timingScope)
1526558f80cSRiver Riddle     return;
15333f908c4SFabian Schuiki   addInstrumentation(std::make_unique<PassTiming>(timingScope));
15433f908c4SFabian Schuiki }
15533f908c4SFabian Schuiki 
15633f908c4SFabian Schuiki /// Add an instrumentation to time the execution of passes and the computation
15733f908c4SFabian Schuiki /// of analyses.
enableTiming(std::unique_ptr<TimingManager> tm)15833f908c4SFabian Schuiki void PassManager::enableTiming(std::unique_ptr<TimingManager> tm) {
15933f908c4SFabian Schuiki   if (!tm->getRootTimer())
16033f908c4SFabian Schuiki     return; // no need to keep the timing manager around if it's disabled
16133f908c4SFabian Schuiki   addInstrumentation(std::make_unique<PassTiming>(std::move(tm)));
16233f908c4SFabian Schuiki }
16333f908c4SFabian Schuiki 
16433f908c4SFabian Schuiki /// Add an instrumentation to time the execution of passes and the computation
16533f908c4SFabian Schuiki /// of analyses.
enableTiming()16633f908c4SFabian Schuiki void PassManager::enableTiming() {
16733f908c4SFabian Schuiki   auto tm = std::make_unique<DefaultTimingManager>();
16833f908c4SFabian Schuiki   tm->setEnabled(true);
16933f908c4SFabian Schuiki   enableTiming(std::move(tm));
1706558f80cSRiver Riddle }
171