11 #include "mlir/Pass/Pass.h"
19 #include "llvm/Support/Debug.h"
21 #include "mlir/Transforms/DialectConversion.h"
22 #include "llvm/ADT/TypeSwitch.h"
24 #define DEBUG_TYPE "kanagawa-clean-selfdrivers"
28 #define GEN_PASS_DEF_KANAGAWACLEANSELFDRIVERS
29 #include "circt/Dialect/Kanagawa/KanagawaPasses.h.inc"
33 using namespace circt;
34 using namespace kanagawa;
40 return llvm::any_of(op->getUsers(), [&](Operation *user) {
41 auto writer = dyn_cast<PortWriteOp>(user);
42 return writer && writer.getPort() == op.getPort();
50 static LogicalResult replaceReadsOfWrites(ContainerOp containerOp) {
54 GetPortOp getAsOutput;
55 llvm::SmallVector<PortReadOp> reads;
58 llvm::DenseMap< Value,
59 llvm::DenseMap<StringAttr, PortAccesses>>
60 instancePortAccessMap;
62 for (
auto getPortOp : containerOp.getOps<GetPortOp>()) {
63 PortAccesses &portAccesses =
64 instancePortAccessMap[getPortOp.getInstance()]
65 [getPortOp.getPortSymbolAttr().getAttr()];
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;
80 if (portAccesses.getAsOutput)
81 return containerOp.emitError(
82 "multiple get_port as output - please CSE the input IR");
83 portAccesses.getAsOutput = getPortOp;
85 for (
auto *user : getPortOp->getUsers()) {
86 if (
auto reader = dyn_cast<PortReadOp>(user))
87 portAccesses.reads.push_back(reader);
92 for (
auto &[instance, portAccessMap] : instancePortAccessMap) {
93 for (
auto &[portName, portAccesses] : portAccessMap) {
95 if (!portAccesses.writer)
99 if (!portAccesses.getAsOutput)
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());
110 portAccesses.getAsOutput.erase();
118 InputPortOpConversionPattern(MLIRContext *context,
InstanceGraph &ig)
122 matchAndRewrite(InputPortOp op, OpAdaptor adaptor,
123 ConversionPatternRewriter &rewriter)
const override {
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) {
131 return rewriter.notifyMatchFailure(
132 user,
"found multiple drivers of the self-driven "
137 .Case<PortReadOp>([&](
auto op) {
138 readers.push_back(op);
142 return rewriter.notifyMatchFailure(
143 user,
"unhandled user of the self-driven "
152 auto wire = rewriter.create<hw::WireOp>(op.getLoc(), writer.getValue(),
153 op.getInnerSymAttrName());
156 for (
auto reader : readers)
157 rewriter.replaceOp(reader, wire);
163 auto parentModuleOp = dyn_cast<ModuleOpInterface>(op->getParentOp());
164 if (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()) {
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);
184 rewriter.eraseOp(op);
185 rewriter.eraseOp(writer);
193 struct CleanSelfdriversPass
194 :
public circt::kanagawa::impl::KanagawaCleanSelfdriversBase<
195 CleanSelfdriversPass> {
196 void runOnOperation()
override;
198 LogicalResult cleanInstanceSide();
199 LogicalResult cleanContainerSide();
203 LogicalResult CleanSelfdriversPass::cleanInstanceSide() {
204 for (ContainerOp containerOp : getOperation().getOps<ContainerOp>())
205 if (failed(replaceReadsOfWrites(containerOp)))
211 LogicalResult CleanSelfdriversPass::cleanContainerSide() {
212 auto *ctx = &getContext();
213 ConversionTarget target(*ctx);
214 target.addLegalDialect<KanagawaDialect>();
215 target.addLegalOp<hw::WireOp>();
216 target.addDynamicallyLegalOp<InputPortOp>(
219 auto &ig = getAnalysis<InstanceGraph>();
221 patterns.add<InputPortOpConversionPattern>(ctx, ig);
224 applyPartialConversion(getOperation(), target, std::move(
patterns))))
230 void CleanSelfdriversPass::runOnOperation() {
231 if (failed(cleanInstanceSide()) || failed(cleanContainerSide()))
232 return signalPassFailure();
236 return std::make_unique<CleanSelfdriversPass>();
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.