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