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
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 std::fill(activePMs.begin(), activePMs.end(), 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")
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)