15 #include "mlir/IR/Builders.h"
16 #include "mlir/Pass/Pass.h"
17 #include "mlir/Transforms/DialectConversion.h"
18 #include "llvm/ADT/TypeSwitch.h"
22 #define GEN_PASS_DEF_KANAGAWATUNNELING
23 #include "circt/Dialect/Kanagawa/KanagawaPasses.h.inc"
28 using namespace circt;
38 mlir::StringAttr portName;
43 PortRefType getType() {
44 return cast<PortRefType>(getPortOp.getPort().getType());
46 Type getInnerType() {
return getType().getPortType(); }
47 Direction getRequestedDirection() {
return getType().getDirection(); }
52 Tunneler(
const KanagawaTunnelingOptions &options, PathOp op,
58 using PortRefMapping = llvm::MapVector<PortInfo *, Value>;
67 llvm::ArrayRef<PathStepAttr> path,
68 PortRefMapping &mapping);
75 Value portForwardIfNeeded(PortOpInterface actualPort,
PortInfo &portInfo);
82 llvm::ArrayRef<PathStepAttr> path,
83 PortRefMapping &portMapping);
88 FlatSymbolRefAttr tunnelInto,
89 llvm::ArrayRef<PathStepAttr> path,
90 PortRefMapping &portMapping);
93 void genPortNames(llvm::SmallVectorImpl<PortInfo> &portInfos);
96 ConversionPatternRewriter &rewriter;
98 const KanagawaTunnelingOptions &options;
99 mlir::StringAttr pathName;
100 llvm::SmallVector<PathStepAttr> path;
103 llvm::SmallVector<PortInfo> portInfos;
108 FlatSymbolRefAttr targetName;
111 Tunneler::Tunneler(
const KanagawaTunnelingOptions &options, PathOp op,
113 : op(op), rewriter(rewriter), ig(ig), options(options) {
114 llvm::copy(op.getPathAsRange(), std::back_inserter(path));
116 "empty paths should never occur - illegal for kanagawa.path ops");
117 target = path.back();
118 targetName = target.getChild();
121 void Tunneler::genPortNames(llvm::SmallVectorImpl<PortInfo> &portInfos) {
122 std::string pathName;
123 llvm::raw_string_ostream ss(pathName);
125 op.getPathAsRange(), ss,
126 [&](PathStepAttr step) {
127 if (step.getDirection() == PathDirection::Parent)
130 ss << step.getChild().getValue();
138 ? options.writeSuffix
139 : options.readSuffix;
140 pi.portName = rewriter.getStringAttr(pathName +
"_" +
141 pi.portName.getValue() + suffix);
145 LogicalResult Tunneler::go() {
147 for (
auto *user : op.getResult().getUsers()) {
148 auto getPortOp = dyn_cast<GetPortOp>(user);
150 return user->emitOpError() <<
"unknown user of a PathOp result - "
151 "tunneling only supports kanagawa.get_port";
153 PortInfo{getPortOp.getPortSymbolAttr().getAttr(), getPortOp});
155 genPortNames(portInfos);
158 ig.
lookup(cast<ModuleOpInterface>(op.getOperation()->getParentOp()));
160 PortRefMapping mapping;
161 if (failed(tunnelDispatch(currentContainer, path, mapping)))
166 auto *it = mapping.find(&pi);
167 assert(it != mapping.end() &&
168 "expected to find a portref mapping for all get_port ops");
169 rewriter.replaceOp(pi.getPortOp, it->second);
173 rewriter.eraseOp(op);
179 llvm::ArrayRef<PathStepAttr> path,
180 PortRefMapping &mapping) {
181 PathStepAttr currentStep = path.front();
182 PortRefMapping targetValue;
183 path = path.drop_front();
184 if (currentStep.getDirection() == PathDirection::Parent) {
185 LogicalResult upRes = tunnelUp(currentContainer, path, mapping);
189 FlatSymbolRefAttr tunnelInto = currentStep.getChild();
190 LogicalResult downRes =
191 tunnelDown(currentContainer, tunnelInto, path, mapping);
198 Value Tunneler::portForwardIfNeeded(PortOpInterface actualPort,
201 cast<PortRefType>(actualPort.getPort().getType()).getDirection();
202 Direction requestedDir = portInfo.getRequestedDirection();
205 if (actualDir == requestedDir)
206 return actualPort.getPort();
209 OpBuilder::InsertionGuard guard(rewriter);
210 rewriter.setInsertionPointAfter(actualPort);
217 auto wireOp = rewriter.create<InputWireOp>(
218 op.getLoc(), rewriter.getStringAttr(*actualPort.getInnerName() +
".wr"),
219 portInfo.getInnerType());
221 rewriter.create<PortWriteOp>(op.getLoc(), actualPort.getPort(),
223 return wireOp.getPort();
231 rewriter.create<PortReadOp>(op.getLoc(), actualPort.getPort());
232 auto wireOp = rewriter.create<OutputWireOp>(
235 rewriter.getStringAttr(*actualPort.getInnerName() +
".rd")),
236 inputValue, rewriter.getStringAttr(actualPort.getNameHint() +
".rd"));
237 return wireOp.getPort();
243 static FailureOr<ContainerInstanceOp> locateInstanceIn(Operation *parentOp,
244 FlatSymbolRefAttr name) {
245 if (parentOp->hasTrait<OpTrait::SymbolTable>()) {
246 auto *tunnelInstanceOp = SymbolTable::lookupSymbolIn(parentOp, name);
247 if (!tunnelInstanceOp)
249 return cast<ContainerInstanceOp>(tunnelInstanceOp);
253 for (
auto instanceOp : parentOp->getRegion(0).getOps<ContainerInstanceOp>()) {
254 if (instanceOp.getInnerSym().getSymName() == name.getValue())
263 FlatSymbolRefAttr tunnelInto,
264 llvm::ArrayRef<PathStepAttr> path,
265 PortRefMapping &portMapping) {
267 Operation *parentOp = currentContainer->
getModule().getOperation();
268 auto parentSymbolOp = dyn_cast<hw::InnerSymbolOpInterface>(parentOp);
269 assert(parentSymbolOp &&
"expected current container to be a symbol op");
270 FailureOr<ContainerInstanceOp> locateRes =
271 locateInstanceIn(parentOp, tunnelInto);
272 if (failed(locateRes))
273 return op->emitOpError()
274 <<
"expected an instance named " << tunnelInto <<
" in @"
275 << parentSymbolOp.getInnerSymAttr().getSymName().getValue()
276 <<
" but found none";
277 ContainerInstanceOp tunnelInstance = *locateRes;
282 rewriter.setInsertionPointAfter(tunnelInstance);
284 auto targetGetPortOp =
285 rewriter.create<GetPortOp>(op.getLoc(), pi.getType(), tunnelInstance,
286 pi.getPortOp.getPortSymbol());
287 portMapping[&pi] = targetGetPortOp.getResult();
294 auto *tunnelScopeNode =
295 ig.
lookup(tunnelInstance.getTargetNameAttr().getName());
296 auto tunnelScope = tunnelScopeNode->
getModule<ScopeOpInterface>();
298 rewriter.setInsertionPointToEnd(tunnelScope.getBodyBlock());
299 llvm::DenseMap<StringAttr, OutputPortOp> outputPortOps;
301 outputPortOps[pi.portName] = rewriter.create<OutputPortOp>(
307 PortRefMapping childMapping;
308 if (failed(tunnelDispatch(tunnelScopeNode, path, childMapping)))
311 for (
auto [pi, res] : childMapping) {
315 rewriter.setInsertionPointToEnd(tunnelScope.getBodyBlock());
316 rewriter.create<PortWriteOp>(op.getLoc(), outputPortOps[portInfo.portName],
321 rewriter.setInsertionPointAfter(tunnelInstance);
322 auto getPortOp = rewriter.create<GetPortOp>(
323 op.getLoc(), tunnelInstance, portInfo.portName, portInfo.getType(),
326 rewriter.create<PortReadOp>(op.getLoc(), getPortOp).getResult();
334 llvm::ArrayRef<PathStepAttr> path,
335 PortRefMapping &portMapping) {
336 auto scopeOp = currentContainer->
getModule<ScopeOpInterface>();
337 if (currentContainer->
noUses())
338 return op->emitOpError()
339 <<
"cannot tunnel up from " << scopeOp.getScopeName()
340 <<
" because it has no uses";
342 for (
auto *use : currentContainer->
uses()) {
344 ig.
lookup(use->getParent()->getModule());
345 auto parentScope = parentScopeNode->
getModule<ScopeOpInterface>();
346 PortRefMapping targetPortMapping;
351 StringRef targetPortName = pi.getPortOp.getPortSymbol();
352 PortOpInterface portLikeOp = parentScope.lookupPort(targetPortName);
354 return op->emitOpError()
355 <<
"expected a port named " << targetPortName <<
" in "
356 << parentScope.getScopeName() <<
" but found none";
359 targetPortMapping[&pi] = portForwardIfNeeded(portLikeOp, pi);
364 if (failed(tunnelDispatch(parentScopeNode, path, targetPortMapping)))
368 auto instance = use->getInstance<ContainerInstanceOp>();
369 rewriter.setInsertionPointAfter(instance);
371 auto getPortOp = rewriter.create<GetPortOp>(
373 rewriter.create<PortWriteOp>(op.getLoc(), getPortOp,
374 targetPortMapping[&pi]);
379 rewriter.setInsertionPointToEnd(scopeOp.getBodyBlock());
381 auto inputPort = rewriter.create<InputPortOp>(
387 rewriter.create<PortReadOp>(op.getLoc(), inputPort.getResult())
396 TunnelingConversionPattern(MLIRContext *context,
InstanceGraph &ig,
397 KanagawaTunnelingOptions options)
399 options(std::move(options)) {}
402 matchAndRewrite(PathOp op, OpAdaptor adaptor,
403 ConversionPatternRewriter &rewriter)
const override {
404 return Tunneler(options, op, rewriter, ig).go();
409 KanagawaTunnelingOptions options;
413 :
public circt::kanagawa::impl::KanagawaTunnelingBase<TunnelingPass> {
414 using KanagawaTunnelingBase<TunnelingPass>::KanagawaTunnelingBase;
415 void runOnOperation()
override;
420 void TunnelingPass::runOnOperation() {
421 auto &ig = getAnalysis<InstanceGraph>();
422 auto *ctx = &getContext();
423 ConversionTarget target(*ctx);
424 target.addIllegalOp<PathOp>();
425 target.addLegalOp<InputPortOp, OutputPortOp, PortReadOp, PortWriteOp,
426 GetPortOp, InputWireOp, OutputWireOp>();
429 patterns.add<TunnelingConversionPattern>(
430 ctx, ig, KanagawaTunnelingOptions{readSuffix, writeSuffix});
433 applyPartialConversion(getOperation(), target, std::move(
patterns))))
437 std::unique_ptr<Pass>
439 return std::make_unique<TunnelingPass>(options);
assert(baseType &&"element must be base type")
HW-specific instance graph with a virtual entry node linking to all publicly visible modules.
This is a Node in the InstanceGraph.
bool noUses()
Return true if there are no more instances of this module.
auto getModule()
Get the module that this node is tracking.
llvm::iterator_range< UseIterator > uses()
InstanceGraphNode * lookup(ModuleOpInterface op)
Look up an InstanceGraphNode for a module.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Direction
The direction of a Component or Cell port.
std::unique_ptr< mlir::Pass > createTunnelingPass(const KanagawaTunnelingOptions &={})
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
This holds the name, type, direction of a module's ports.