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"
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;
111Tunneler::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();
121void 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();
137 std::string suffix = pi.getRequestedDirection() == Direction::Input
138 ? options.writeSuffix
139 : options.readSuffix;
140 pi.portName = rewriter.getStringAttr(pathName +
"_" +
141 pi.portName.getValue() + suffix);
145LogicalResult 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);
198Value 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);
216 if (requestedDir == Direction::Input) {
217 auto wireOp = InputWireOp::create(
218 rewriter, op.getLoc(),
219 rewriter.getStringAttr(*actualPort.getInnerName() +
".wr"),
220 portInfo.getInnerType());
222 PortWriteOp::create(rewriter, op.getLoc(), actualPort.getPort(),
224 return wireOp.getPort();
232 PortReadOp::create(rewriter, op.getLoc(), actualPort.getPort());
233 auto wireOp = OutputWireOp::create(
234 rewriter, op.getLoc(),
235 hw::InnerSymAttr::get(
236 rewriter.getStringAttr(*actualPort.getInnerName() +
".rd")),
237 inputValue, rewriter.getStringAttr(actualPort.getNameHint() +
".rd"));
238 return wireOp.getPort();
244static FailureOr<ContainerInstanceOp> locateInstanceIn(Operation *parentOp,
245 FlatSymbolRefAttr name) {
246 if (parentOp->hasTrait<OpTrait::SymbolTable>()) {
247 auto *tunnelInstanceOp = SymbolTable::lookupSymbolIn(parentOp, name);
248 if (!tunnelInstanceOp)
250 return cast<ContainerInstanceOp>(tunnelInstanceOp);
254 for (
auto instanceOp : parentOp->getRegion(0).getOps<ContainerInstanceOp>()) {
255 if (instanceOp.getInnerSym().getSymName() == name.getValue())
264 FlatSymbolRefAttr tunnelInto,
265 llvm::ArrayRef<PathStepAttr> path,
266 PortRefMapping &portMapping) {
268 Operation *parentOp = currentContainer->
getModule().getOperation();
269 auto parentSymbolOp = dyn_cast<hw::InnerSymbolOpInterface>(parentOp);
270 assert(parentSymbolOp &&
"expected current container to be a symbol op");
271 FailureOr<ContainerInstanceOp> locateRes =
272 locateInstanceIn(parentOp, tunnelInto);
273 if (failed(locateRes))
274 return op->emitOpError()
275 <<
"expected an instance named " << tunnelInto <<
" in @"
276 << parentSymbolOp.getInnerSymAttr().getSymName().getValue()
277 <<
" but found none";
278 ContainerInstanceOp tunnelInstance = *locateRes;
283 rewriter.setInsertionPointAfter(tunnelInstance);
285 auto targetGetPortOp =
286 GetPortOp::create(rewriter, op.getLoc(), pi.getType(), tunnelInstance,
287 pi.getPortOp.getPortSymbol());
288 portMapping[&pi] = targetGetPortOp.getResult();
295 auto *tunnelScopeNode =
296 ig.
lookup(tunnelInstance.getTargetNameAttr().getName());
297 auto tunnelScope = tunnelScopeNode->
getModule<ScopeOpInterface>();
299 rewriter.setInsertionPointToEnd(tunnelScope.getBodyBlock());
300 llvm::DenseMap<StringAttr, OutputPortOp> outputPortOps;
302 outputPortOps[pi.portName] = OutputPortOp::create(
303 rewriter, op.getLoc(), circt::hw::InnerSymAttr::get(pi.portName),
304 pi.getType(), pi.portName);
308 PortRefMapping childMapping;
309 if (failed(tunnelDispatch(tunnelScopeNode, path, childMapping)))
312 for (
auto [pi, res] : childMapping) {
316 rewriter.setInsertionPointToEnd(tunnelScope.getBodyBlock());
317 PortWriteOp::create(rewriter, op.getLoc(), outputPortOps[portInfo.portName],
322 rewriter.setInsertionPointAfter(tunnelInstance);
323 auto getPortOp = GetPortOp::create(rewriter, op.getLoc(), tunnelInstance,
324 portInfo.portName, portInfo.getType(),
327 PortReadOp::create(rewriter, op.getLoc(), getPortOp).getResult();
335 llvm::ArrayRef<PathStepAttr> path,
336 PortRefMapping &portMapping) {
337 auto scopeOp = currentContainer->
getModule<ScopeOpInterface>();
338 if (currentContainer->
noUses())
339 return op->emitOpError()
340 <<
"cannot tunnel up from " << scopeOp.getScopeName()
341 <<
" because it has no uses";
343 for (
auto *use : currentContainer->uses()) {
345 ig.
lookup(use->getParent()->getModule());
346 auto parentScope = parentScopeNode->
getModule<ScopeOpInterface>();
347 PortRefMapping targetPortMapping;
352 StringRef targetPortName = pi.getPortOp.getPortSymbol();
353 PortOpInterface portLikeOp = parentScope.lookupPort(targetPortName);
355 return op->emitOpError()
356 <<
"expected a port named " << targetPortName <<
" in "
357 << parentScope.getScopeName() <<
" but found none";
360 targetPortMapping[&pi] = portForwardIfNeeded(portLikeOp, pi);
365 if (failed(tunnelDispatch(parentScopeNode, path, targetPortMapping)))
369 auto instance = use->getInstance<ContainerInstanceOp>();
370 rewriter.setInsertionPointAfter(instance);
373 GetPortOp::create(rewriter, op.getLoc(), instance, pi.portName,
374 pi.getType(), Direction::Input);
375 PortWriteOp::create(rewriter, op.getLoc(), getPortOp,
376 targetPortMapping[&pi]);
381 rewriter.setInsertionPointToEnd(scopeOp.getBodyBlock());
383 auto inputPort = InputPortOp::create(rewriter, op.getLoc(),
384 hw::InnerSymAttr::get(pi.portName),
385 pi.getType(), pi.portName);
389 PortReadOp::create(rewriter, op.getLoc(), inputPort.getResult())
398 TunnelingConversionPattern(MLIRContext *context,
InstanceGraph &ig,
399 KanagawaTunnelingOptions options)
401 options(std::move(options)) {}
404 matchAndRewrite(PathOp op, OpAdaptor adaptor,
405 ConversionPatternRewriter &rewriter)
const override {
406 return Tunneler(options, op, rewriter, ig).go();
411 KanagawaTunnelingOptions options;
415 :
public circt::kanagawa::impl::KanagawaTunnelingBase<TunnelingPass> {
416 using KanagawaTunnelingBase<TunnelingPass>::KanagawaTunnelingBase;
417 void runOnOperation()
override;
422void TunnelingPass::runOnOperation() {
423 auto &ig = getAnalysis<InstanceGraph>();
424 auto *ctx = &getContext();
425 ConversionTarget target(*ctx);
426 target.addIllegalOp<PathOp>();
427 target.addLegalOp<InputPortOp, OutputPortOp, PortReadOp, PortWriteOp,
428 GetPortOp, InputWireOp, OutputWireOp>();
431 patterns.add<TunnelingConversionPattern>(
432 ctx, ig, KanagawaTunnelingOptions{readSuffix, writeSuffix});
435 applyPartialConversion(getOperation(), target, std::move(
patterns))))
441 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.
InstanceGraphNode * lookup(ModuleOpInterface op)
Look up an InstanceGraphNode for a module.
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.