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"
34using 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();
50static 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()];
66 if (getPortOp.getDirection() == Direction::Input) {
67 if (portAccesses.getAsInput)
68 return portAccesses.getAsInput.emitError(
"multiple input get_ports")
69 .attachNote(getPortOp.getLoc())
70 <<
"redundant get_port here";
71 portAccesses.getAsInput = getPortOp;
72 for (
auto *user : getPortOp->getUsers()) {
73 if (
auto writer = dyn_cast<PortWriteOp>(user)) {
74 if (portAccesses.writer)
75 return getPortOp.emitError(
76 "multiple writers of the same input port");
77 portAccesses.writer = writer;
81 if (portAccesses.getAsOutput)
82 return portAccesses.getAsOutput.emitError(
"multiple get_port as output")
83 .attachNote(getPortOp.getLoc())
84 <<
"redundant get_port here";
85 portAccesses.getAsOutput = getPortOp;
87 for (
auto *user : getPortOp->getUsers()) {
88 if (
auto reader = dyn_cast<PortReadOp>(user))
89 portAccesses.reads.push_back(reader);
94 for (
auto &[instance, portAccessMap] : instancePortAccessMap) {
95 for (
auto &[portName, portAccesses] : portAccessMap) {
97 if (!portAccesses.writer)
101 if (!portAccesses.getAsOutput)
106 LLVM_DEBUG(llvm::dbgs() <<
"Writer is: " << portAccesses.writer <<
"\n";);
107 for (
auto reader : portAccesses.reads) {
108 LLVM_DEBUG(llvm::dbgs() <<
"Replacing: " << reader <<
"\n";);
109 reader.replaceAllUsesWith(portAccesses.writer.getValue());
112 portAccesses.getAsOutput.erase();
120 InputPortOpConversionPattern(MLIRContext *context,
InstanceGraph &ig)
124 matchAndRewrite(InputPortOp op, OpAdaptor adaptor,
125 ConversionPatternRewriter &rewriter)
const override {
127 PortWriteOp writer =
nullptr;
128 llvm::SmallVector<PortReadOp> readers;
129 for (
auto *user : op->getUsers()) {
130 auto res = llvm::TypeSwitch<Operation *, LogicalResult>(user)
131 .Case<PortWriteOp>([&](
auto op) {
133 return rewriter.notifyMatchFailure(
134 user,
"found multiple drivers of the self-driven "
139 .Case<PortReadOp>([&](
auto op) {
140 readers.push_back(op);
144 return rewriter.notifyMatchFailure(
145 user,
"unhandled user of the self-driven "
154 auto wire = rewriter.create<hw::WireOp>(op.getLoc(), writer.getValue(),
155 op.getInnerSymAttrName());
158 for (
auto reader : readers)
159 rewriter.replaceOp(reader, wire);
165 auto parentModuleOp = dyn_cast<ModuleOpInterface>(op->getParentOp());
166 if (parentModuleOp) {
168 bool anyOutsideReads = llvm::any_of(node->
uses(), [&](
auto use) {
169 Block *userBlock = use->getInstance()->getBlock();
170 for (auto getPortOp : userBlock->getOps<GetPortOp>()) {
171 if (getPortOp.getPortSymbol() == *op.getInnerName()) {
178 if (anyOutsideReads) {
179 auto outputPort = rewriter.create<OutputPortOp>(
180 op.getLoc(), op.getInnerSym(), op.getType(), op.getNameAttr());
181 rewriter.create<PortWriteOp>(op.getLoc(), outputPort, wire);
186 rewriter.eraseOp(op);
187 rewriter.eraseOp(writer);
195struct CleanSelfdriversPass
196 :
public circt::kanagawa::impl::KanagawaCleanSelfdriversBase<
197 CleanSelfdriversPass> {
198 void runOnOperation()
override;
200 LogicalResult cleanInstanceSide();
201 LogicalResult cleanContainerSide();
205LogicalResult CleanSelfdriversPass::cleanInstanceSide() {
206 for (ContainerOp containerOp : getOperation().getOps<ContainerOp>())
207 if (failed(replaceReadsOfWrites(containerOp)))
213LogicalResult CleanSelfdriversPass::cleanContainerSide() {
214 auto *ctx = &getContext();
215 ConversionTarget target(*ctx);
216 target.addLegalDialect<KanagawaDialect>();
217 target.addLegalOp<hw::WireOp>();
218 target.addDynamicallyLegalOp<InputPortOp>(
221 auto &ig = getAnalysis<InstanceGraph>();
223 patterns.add<InputPortOpConversionPattern>(ctx, ig);
226 applyPartialConversion(getOperation(), target, std::move(
patterns))))
232void CleanSelfdriversPass::runOnOperation() {
233 if (failed(cleanInstanceSide()) || failed(cleanContainerSide()))
234 return signalPassFailure();
238 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.