17 #include "llvm/Support/Debug.h"
19 #include "mlir/Transforms/DialectConversion.h"
20 #include "llvm/ADT/TypeSwitch.h"
22 #define DEBUG_TYPE "ibis-clean-selfdrivers"
24 using namespace circt;
31 return llvm::any_of(op->getUsers(), [&](Operation *user) {
32 auto writer = dyn_cast<PortWriteOp>(user);
33 return writer && writer.getPort() == op.getPort();
41 static LogicalResult replaceReadsOfWrites(ContainerOp containerOp) {
45 GetPortOp getAsOutput;
46 llvm::SmallVector<PortReadOp> reads;
49 llvm::DenseMap< Value,
50 llvm::DenseMap<StringAttr, PortAccesses>>
51 instancePortAccessMap;
53 for (
auto getPortOp : containerOp.getOps<GetPortOp>()) {
54 PortAccesses &portAccesses =
55 instancePortAccessMap[getPortOp.getInstance()]
56 [getPortOp.getPortSymbolAttr().getAttr()];
58 if (portAccesses.getAsInput)
59 return containerOp.emitError(
60 "multiple input get_ports - please CSE the input IR");
61 portAccesses.getAsInput = getPortOp;
62 for (
auto *user : getPortOp->getUsers()) {
63 if (
auto writer = dyn_cast<PortWriteOp>(user)) {
64 if (portAccesses.writer)
65 return getPortOp.emitError(
66 "multiple writers of the same input port");
67 portAccesses.writer = writer;
71 if (portAccesses.getAsOutput)
72 return containerOp.emitError(
73 "multiple get_port as output - please CSE the input IR");
74 portAccesses.getAsOutput = getPortOp;
76 for (
auto *user : getPortOp->getUsers()) {
77 if (
auto reader = dyn_cast<PortReadOp>(user))
78 portAccesses.reads.push_back(reader);
83 for (
auto &[instance, portAccessMap] : instancePortAccessMap) {
84 for (
auto &[portName, portAccesses] : portAccessMap) {
86 if (!portAccesses.writer)
90 if (!portAccesses.getAsOutput)
95 LLVM_DEBUG(llvm::dbgs() <<
"Writer is: " << portAccesses.writer <<
"\n";);
96 for (
auto reader : portAccesses.reads) {
97 LLVM_DEBUG(llvm::dbgs() <<
"Replacing: " << reader <<
"\n";);
98 reader.replaceAllUsesWith(portAccesses.writer.getValue());
101 portAccesses.getAsOutput.erase();
109 InputPortOpConversionPattern(MLIRContext *context,
InstanceGraph &ig)
113 matchAndRewrite(InputPortOp op, OpAdaptor adaptor,
114 ConversionPatternRewriter &rewriter)
const override {
116 PortWriteOp writer =
nullptr;
117 llvm::SmallVector<PortReadOp> readers;
118 for (
auto *user : op->getUsers()) {
119 auto res = llvm::TypeSwitch<Operation *, LogicalResult>(user)
120 .Case<PortWriteOp>([&](
auto op) {
122 return rewriter.notifyMatchFailure(
123 user,
"found multiple drivers of the self-driven "
128 .Case<PortReadOp>([&](
auto op) {
129 readers.push_back(op);
133 return rewriter.notifyMatchFailure(
134 user,
"unhandled user of the self-driven "
143 auto wire = rewriter.create<hw::WireOp>(op.getLoc(), writer.getValue(),
144 op.getInnerSymAttrName());
147 for (
auto reader : readers)
148 rewriter.replaceOp(reader, wire);
154 auto parentModuleOp = dyn_cast<ModuleOpInterface>(op->getParentOp());
155 if (parentModuleOp) {
157 bool anyOutsideReads = llvm::any_of(node->
uses(), [&](
auto use) {
158 Block *userBlock = use->getInstance()->getBlock();
159 for (auto getPortOp : userBlock->getOps<GetPortOp>()) {
160 if (getPortOp.getPortSymbol() == op.getPortName())
166 if (anyOutsideReads) {
167 auto outputPort = rewriter.create<OutputPortOp>(
168 op.getLoc(), op.getNameAttr(), op.getInnerSym(), op.getType());
169 rewriter.create<PortWriteOp>(op.getLoc(), outputPort, wire);
174 rewriter.eraseOp(op);
175 rewriter.eraseOp(writer);
183 struct CleanSelfdriversPass
184 :
public IbisCleanSelfdriversBase<CleanSelfdriversPass> {
185 void runOnOperation()
override;
187 LogicalResult cleanInstanceSide();
188 LogicalResult cleanContainerSide();
192 LogicalResult CleanSelfdriversPass::cleanInstanceSide() {
193 for (ContainerOp containerOp : getOperation().getOps<ContainerOp>())
194 if (failed(replaceReadsOfWrites(containerOp)))
200 LogicalResult CleanSelfdriversPass::cleanContainerSide() {
201 auto *ctx = &getContext();
202 ConversionTarget target(*ctx);
203 target.addLegalDialect<IbisDialect>();
204 target.addLegalOp<hw::WireOp>();
205 target.addDynamicallyLegalOp<InputPortOp>(
208 auto &ig = getAnalysis<InstanceGraph>();
210 patterns.add<InputPortOpConversionPattern>(ctx, ig);
213 applyPartialConversion(getOperation(), target, std::move(
patterns))))
219 void CleanSelfdriversPass::runOnOperation() {
220 if (failed(cleanInstanceSide()) || failed(cleanContainerSide()))
221 return signalPassFailure();
225 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.