CIRCT  19.0.0git
IbisCleanSelfdrivers.cpp
Go to the documentation of this file.
1 //===- IbisCleanSelfdrivers.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 
13 #include "circt/Dialect/HW/HWOps.h"
19 #include "llvm/Support/Debug.h"
20 
21 #include "mlir/Transforms/DialectConversion.h"
22 #include "llvm/ADT/TypeSwitch.h"
23 
24 #define DEBUG_TYPE "ibis-clean-selfdrivers"
25 
26 namespace circt {
27 namespace ibis {
28 #define GEN_PASS_DEF_IBISCLEANSELFDRIVERS
29 #include "circt/Dialect/Ibis/IbisPasses.h.inc"
30 } // namespace ibis
31 } // namespace circt
32 
33 using namespace circt;
34 using namespace ibis;
35 using 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.
39 static 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 
46 namespace {
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.
50 static 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 
117 struct 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 Ibis allows for input ports to be read from outside the container,
160  // we need to check the instance graph to see whether this is the case.
161  // If so, we need to add an output port to the container and connect it to
162  // 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 
189 protected:
190  InstanceGraph &ig;
191 };
192 
193 struct CleanSelfdriversPass
194  : public circt::ibis::impl::IbisCleanSelfdriversBase<CleanSelfdriversPass> {
195  void runOnOperation() override;
196 
197  LogicalResult cleanInstanceSide();
198  LogicalResult cleanContainerSide();
199 };
200 } // anonymous namespace
201 
202 LogicalResult CleanSelfdriversPass::cleanInstanceSide() {
203  for (ContainerOp containerOp : getOperation().getOps<ContainerOp>())
204  if (failed(replaceReadsOfWrites(containerOp)))
205  return failure();
206 
207  return success();
208 }
209 
210 LogicalResult CleanSelfdriversPass::cleanContainerSide() {
211  auto *ctx = &getContext();
212  ConversionTarget target(*ctx);
213  target.addLegalDialect<IbisDialect>();
214  target.addLegalOp<hw::WireOp>();
215  target.addDynamicallyLegalOp<InputPortOp>(
216  [](InputPortOp op) { return !isSelfDriven(op); });
217 
218  auto &ig = getAnalysis<InstanceGraph>();
219  RewritePatternSet patterns(ctx);
220  patterns.add<InputPortOpConversionPattern>(ctx, ig);
221 
222  if (failed(
223  applyPartialConversion(getOperation(), target, std::move(patterns))))
224  return failure();
225 
226  return success();
227 }
228 
229 void CleanSelfdriversPass::runOnOperation() {
230  if (failed(cleanInstanceSide()) || failed(cleanContainerSide()))
231  return signalPassFailure();
232 }
233 
235  return std::make_unique<CleanSelfdriversPass>();
236 }
@ Input
Definition: HW.h:35
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.
Definition: DebugAnalysis.h:21