CIRCT 20.0.0git
Loading...
Searching...
No Matches
KanagawaCleanSelfdrivers.cpp
Go to the documentation of this file.
1//===- KanagawaCleanSelfdrivers.cpp ---------------------------------------===//
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
11#include "mlir/Pass/Pass.h"
12
19#include "llvm/Support/Debug.h"
20
21#include "mlir/Transforms/DialectConversion.h"
22#include "llvm/ADT/TypeSwitch.h"
23
24#define DEBUG_TYPE "kanagawa-clean-selfdrivers"
25
26namespace circt {
27namespace kanagawa {
28#define GEN_PASS_DEF_KANAGAWACLEANSELFDRIVERS
29#include "circt/Dialect/Kanagawa/KanagawaPasses.h.inc"
30} // namespace kanagawa
31} // namespace circt
32
33using namespace circt;
34using namespace kanagawa;
35using namespace circt::igraph;
36
37// Returns true if the given input port is self-driven, i.e. there exists
38// a PortWriteOp that writes to it.
39static bool isSelfDriven(InputPortOp op) {
40 return llvm::any_of(op->getUsers(), [&](Operation *user) {
41 auto writer = dyn_cast<PortWriteOp>(user);
42 return writer && writer.getPort() == op.getPort();
43 });
44}
45
46namespace {
47
48// Rewrites cases where an input port is being read in the instantiating module.
49// Replaces the input port read by the assignment value of the input port.
50static LogicalResult replaceReadsOfWrites(ContainerOp containerOp) {
51 // Partition out all of the get_port's wrt. their target port symbol.
52 struct PortAccesses {
53 GetPortOp getAsInput;
54 GetPortOp getAsOutput;
55 llvm::SmallVector<PortReadOp> reads;
56 PortWriteOp writer;
57 };
58 llvm::DenseMap</*instance*/ Value,
59 /*portName*/ llvm::DenseMap<StringAttr, PortAccesses>>
60 instancePortAccessMap;
61
62 for (auto getPortOp : containerOp.getOps<GetPortOp>()) {
63 PortAccesses &portAccesses =
64 instancePortAccessMap[getPortOp.getInstance()]
65 [getPortOp.getPortSymbolAttr().getAttr()];
66 if (getPortOp.getDirection() == Direction::Input) {
67 if (portAccesses.getAsInput)
68 return containerOp.emitError(
69 "multiple input get_ports - please CSE the input IR");
70 portAccesses.getAsInput = getPortOp;
71 for (auto *user : getPortOp->getUsers()) {
72 if (auto writer = dyn_cast<PortWriteOp>(user)) {
73 if (portAccesses.writer)
74 return getPortOp.emitError(
75 "multiple writers of the same input port");
76 portAccesses.writer = writer;
77 }
78 }
79 } else {
80 if (portAccesses.getAsOutput)
81 return containerOp.emitError(
82 "multiple get_port as output - please CSE the input IR");
83 portAccesses.getAsOutput = getPortOp;
84
85 for (auto *user : getPortOp->getUsers()) {
86 if (auto reader = dyn_cast<PortReadOp>(user))
87 portAccesses.reads.push_back(reader);
88 }
89 }
90 }
91
92 for (auto &[instance, portAccessMap] : instancePortAccessMap) {
93 for (auto &[portName, portAccesses] : portAccessMap) {
94 // If the port is not written to, nothing to do.
95 if (!portAccesses.writer)
96 continue;
97
98 // if the port is not read, nothing to do.
99 if (!portAccesses.getAsOutput)
100 continue;
101
102 // If the input port is self-driven, we need to replace all reads of the
103 // input port with the value that is being written to it.
104 LLVM_DEBUG(llvm::dbgs() << "Writer is: " << portAccesses.writer << "\n";);
105 for (auto reader : portAccesses.reads) {
106 LLVM_DEBUG(llvm::dbgs() << "Replacing: " << reader << "\n";);
107 reader.replaceAllUsesWith(portAccesses.writer.getValue());
108 reader.erase();
109 }
110 portAccesses.getAsOutput.erase();
111 }
112 }
113
114 return success();
115}
116
117struct InputPortOpConversionPattern : public OpConversionPattern<InputPortOp> {
118 InputPortOpConversionPattern(MLIRContext *context, InstanceGraph &ig)
119 : OpConversionPattern<InputPortOp>(context), ig(ig) {}
120
121 LogicalResult
122 matchAndRewrite(InputPortOp op, OpAdaptor adaptor,
123 ConversionPatternRewriter &rewriter) const override {
124 // Locate the readers and writers of the port.
125 PortWriteOp writer = nullptr;
126 llvm::SmallVector<PortReadOp> readers;
127 for (auto *user : op->getUsers()) {
128 auto res = llvm::TypeSwitch<Operation *, LogicalResult>(user)
129 .Case<PortWriteOp>([&](auto op) {
130 if (writer)
131 return rewriter.notifyMatchFailure(
132 user, "found multiple drivers of the self-driven "
133 "input port");
134 writer = op;
135 return success();
136 })
137 .Case<PortReadOp>([&](auto op) {
138 readers.push_back(op);
139 return success();
140 })
141 .Default([&](auto) {
142 return rewriter.notifyMatchFailure(
143 user, "unhandled user of the self-driven "
144 "input port");
145 });
146
147 if (failed(res))
148 return failure();
149 }
150
151 // Create a `hw.wire` to ensure that the input port name is maintained.
152 auto wire = rewriter.create<hw::WireOp>(op.getLoc(), writer.getValue(),
153 op.getInnerSymAttrName());
154
155 // Replace all reads of the input port with the wire.
156 for (auto reader : readers)
157 rewriter.replaceOp(reader, wire);
158
159 // Since Kanagawa allows for input ports to be read from outside the
160 // container, we need to check the instance graph to see whether this is the
161 // case. If so, we need to add an output port to the container and connect
162 // it to the assigned value.
163 auto parentModuleOp = dyn_cast<ModuleOpInterface>(op->getParentOp());
164 if (parentModuleOp) {
165 InstanceGraphNode *node = ig.lookup(parentModuleOp);
166 bool anyOutsideReads = llvm::any_of(node->uses(), [&](auto use) {
167 Block *userBlock = use->getInstance()->getBlock();
168 for (auto getPortOp : userBlock->getOps<GetPortOp>()) {
169 if (getPortOp.getPortSymbol() == *op.getInnerName()) {
170 return true;
171 }
172 }
173 return false;
174 });
175
176 if (anyOutsideReads) {
177 auto outputPort = rewriter.create<OutputPortOp>(
178 op.getLoc(), op.getInnerSym(), op.getType(), op.getNameAttr());
179 rewriter.create<PortWriteOp>(op.getLoc(), outputPort, wire);
180 }
181 }
182
183 // Finally, erase the writer and input port.
184 rewriter.eraseOp(op);
185 rewriter.eraseOp(writer);
186 return success();
187 }
188
189protected:
190 InstanceGraph &ig;
191};
192
193struct CleanSelfdriversPass
194 : public circt::kanagawa::impl::KanagawaCleanSelfdriversBase<
195 CleanSelfdriversPass> {
196 void runOnOperation() override;
197
198 LogicalResult cleanInstanceSide();
199 LogicalResult cleanContainerSide();
200};
201} // anonymous namespace
202
203LogicalResult CleanSelfdriversPass::cleanInstanceSide() {
204 for (ContainerOp containerOp : getOperation().getOps<ContainerOp>())
205 if (failed(replaceReadsOfWrites(containerOp)))
206 return failure();
207
208 return success();
209}
210
211LogicalResult CleanSelfdriversPass::cleanContainerSide() {
212 auto *ctx = &getContext();
213 ConversionTarget target(*ctx);
214 target.addLegalDialect<KanagawaDialect>();
215 target.addLegalOp<hw::WireOp>();
216 target.addDynamicallyLegalOp<InputPortOp>(
217 [](InputPortOp op) { return !isSelfDriven(op); });
218
219 auto &ig = getAnalysis<InstanceGraph>();
220 RewritePatternSet patterns(ctx);
221 patterns.add<InputPortOpConversionPattern>(ctx, ig);
222
223 if (failed(
224 applyPartialConversion(getOperation(), target, std::move(patterns))))
225 return failure();
226
227 return success();
228}
229
230void CleanSelfdriversPass::runOnOperation() {
231 if (failed(cleanInstanceSide()) || failed(cleanContainerSide()))
232 return signalPassFailure();
233}
234
236 return std::make_unique<CleanSelfdriversPass>();
237}
static bool isSelfDriven(InputPortOp op)
HW-specific instance graph with a virtual entry node linking to all publicly visible modules.
This is a Node in the InstanceGraph.
llvm::iterator_range< UseIterator > uses()
std::unique_ptr< mlir::Pass > createCleanSelfdriversPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.