CIRCT 24.0.0git
Loading...
Searching...
No Matches
HierarchicalRunner.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements the HierarchicalRunner pass which runs a pass pipeline
10// on specific hierarchichy.
11//
12//===----------------------------------------------------------------------===//
13
17#include "mlir/IR/Operation.h"
18#include "mlir/IR/Threading.h"
19#include "mlir/Pass/AnalysisManager.h"
20#include "mlir/Pass/Pass.h"
21#include "mlir/Pass/PassManager.h"
22#include "llvm/ADT/SmallVector.h"
23
24using namespace circt;
25using namespace mlir;
26
27namespace circt {
28#define GEN_PASS_DEF_HIERARCHICALRUNNER
29#include "circt/Transforms/Passes.h.inc"
30} // namespace circt
31
32namespace {
33struct HierarchicalRunnerPass
34 : public circt::impl::HierarchicalRunnerBase<HierarchicalRunnerPass> {
35 using circt::impl::HierarchicalRunnerBase<
36 HierarchicalRunnerPass>::HierarchicalRunnerBase;
37 void runOnOperation() override;
38 HierarchicalRunnerPass(const std::string &topName,
39 llvm::function_ref<void(OpPassManager &)> populateFunc,
40 bool includeBoundInstances) {
41 this->topName = topName;
42 this->includeBoundInstances = includeBoundInstances;
43
44 // Populate the pipeline and set the text format to the option string.
45 populateFunc(dynamicPM);
46 llvm::raw_string_ostream os(pipelineStr);
47 dynamicPM.printAsTextualPipeline(os);
48 }
49
50 LogicalResult initializeOptions(
51 StringRef options,
52 function_ref<LogicalResult(const Twine &)> errorHandler) override {
53 if (failed(
54 HierarchicalRunnerBase::initializeOptions(options, errorHandler)))
55 return failure();
56
57 if (failed(parsePassPipeline(pipelineStr, dynamicPM)))
58 return errorHandler("Failed to parse composite pass pipeline");
59
60 return success();
61 }
62
63 void getDependentDialects(DialectRegistry &registry) const override {
64 dynamicPM.getDependentDialects(registry);
65 }
66
67private:
68 OpPassManager dynamicPM;
69};
70} // namespace
71
72void HierarchicalRunnerPass::runOnOperation() {
73 auto &instanceGraph = getAnalysis<circt::igraph::InstanceGraph>();
74
75 auto name = mlir::StringAttr::get(getOperation()->getContext(), topName);
76 auto *top = instanceGraph.lookupOrNull(name);
77 if (!top) {
78 mlir::emitError(mlir::UnknownLoc::get(&getContext()))
79 << "top module not found in instance graph " << topName;
80 return signalPassFailure();
81 }
82
83 SmallVector<igraph::InstanceGraphNode *> worklist;
84 llvm::SetVector<Operation *> visited;
85 worklist.push_back(top);
86
87 auto am = getAnalysisManager();
88 while (!worklist.empty()) {
89 auto *node = worklist.pop_back_val();
90 assert(node && "node should not be null");
91 auto op = node->getModule();
92 if (!isa_and_nonnull<hw::HWModuleOp>(op) || !visited.insert(op))
93 continue;
94
95 // Ensure an analysis manager has been constructed for each of the nodes.
96 // This prevents thread races when running the nested pipelines.
97 am.nest(op);
98
99 for (auto *child : *node) {
100 auto childOp = child->getInstance();
101 if (!childOp ||
102 (!includeBoundInstances && childOp->hasAttr("doNotPrint")))
103 continue;
104
105 worklist.push_back(child->getTarget());
106 }
107 }
108
109 // We must maintain a fixed pool of pass managers which is at least as large
110 // as the maximum parallelism of the failableParallelForEach below.
111 // Note: The number of pass managers here needs to remain constant
112 // to prevent issues with pass instrumentations that rely on having the same
113 // pass manager for the main thread.
114 size_t numThreads = getContext().getNumThreads();
115
116 llvm::SmallVector<OpPassManager> pipelines(numThreads, dynamicPM);
117
118 // An atomic failure variable for the async executors.
119 std::vector<std::atomic<bool>> activePMs(pipelines.size());
120 llvm::fill(activePMs, false);
121 auto result = mlir::failableParallelForEach(
122 &getContext(), visited, [&](Operation *node) -> LogicalResult {
123 // Find a pass manager for this operation.
124 auto it = llvm::find_if(activePMs, [](std::atomic<bool> &isActive) {
125 bool expectedInactive = false;
126 return isActive.compare_exchange_strong(expectedInactive, true);
127 });
128 assert(it != activePMs.end() &&
129 "could not find inactive pass manager for thread");
130 unsigned pmIndex = it - activePMs.begin();
131 auto result = runPipeline(pipelines[pmIndex], node);
132 // Reset the active bit for this pass manager.
133 activePMs[pmIndex].store(false);
134 return result;
135 });
136 if (failed(result))
137 return signalPassFailure();
138}
139
140std::unique_ptr<mlir::Pass> circt::createHierarchicalRunner(
141 const std::string &topName,
142 llvm::function_ref<void(mlir::OpPassManager &)> pipeline,
143 bool includeBoundInstances) {
144 return std::make_unique<HierarchicalRunnerPass>(topName, pipeline,
145 includeBoundInstances);
146}
assert(baseType &&"element must be base type")
false return failure()
Parse an optional keyword or string and set instance into 'result'.
return success()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createHierarchicalRunner(const std::string &topName, llvm::function_ref< void(mlir::OpPassManager &)> pipeline, bool includeBoundInstances=false)