17 #include "mlir/IR/Builders.h"
18 #include "mlir/Transforms/DialectConversion.h"
19 #include "llvm/ADT/TypeSwitch.h"
22 using namespace circt;
28 #define GEN_PASS_DEF_IBISTUNNELING
29 #include "circt/Dialect/Ibis/IbisPasses.h.inc"
35 mlir::StringAttr portName;
40 PortRefType getType() {
41 return cast<PortRefType>(getPortOp.getPort().getType());
43 Type getInnerType() {
return getType().getPortType(); }
44 Direction getRequestedDirection() {
return getType().getDirection(); }
49 Tunneler(
const IbisTunnelingOptions &options, PathOp op,
55 using PortRefMapping = llvm::MapVector<PortInfo *, Value>;
64 llvm::ArrayRef<PathStepAttr> path,
65 PortRefMapping &mapping);
72 Value portForwardIfNeeded(PortOpInterface actualPort,
PortInfo &portInfo);
79 llvm::ArrayRef<PathStepAttr> path,
80 PortRefMapping &portMapping);
85 FlatSymbolRefAttr tunnelInto,
86 llvm::ArrayRef<PathStepAttr> path,
87 PortRefMapping &portMapping);
90 void genPortNames(llvm::SmallVectorImpl<PortInfo> &portInfos);
93 ConversionPatternRewriter &rewriter;
95 const IbisTunnelingOptions &options;
96 mlir::StringAttr pathName;
97 llvm::SmallVector<PathStepAttr> path;
100 llvm::SmallVector<PortInfo> portInfos;
105 FlatSymbolRefAttr targetName;
108 Tunneler::Tunneler(
const IbisTunnelingOptions &options, PathOp op,
110 : op(op), rewriter(rewriter), ig(ig), options(options) {
111 llvm::copy(op.getPathAsRange(), std::back_inserter(path));
113 "empty paths should never occur - illegal for ibis.path ops");
114 target = path.back();
115 targetName = target.getChild();
118 void Tunneler::genPortNames(llvm::SmallVectorImpl<PortInfo> &portInfos) {
119 std::string pathName;
120 llvm::raw_string_ostream ss(pathName);
122 op.getPathAsRange(), ss,
123 [&](PathStepAttr step) {
124 if (step.getDirection() == PathDirection::Parent)
127 ss << step.getChild().getValue();
135 ? options.writeSuffix
136 : options.readSuffix;
137 pi.portName = rewriter.getStringAttr(pathName +
"_" +
138 pi.portName.getValue() + suffix);
142 LogicalResult Tunneler::go() {
144 for (
auto *user : op.getResult().getUsers()) {
145 auto getPortOp = dyn_cast<GetPortOp>(user);
147 return user->emitOpError() <<
"unknown user of a PathOp result - "
148 "tunneling only supports ibis.get_port";
152 genPortNames(portInfos);
155 ig.
lookup(cast<ModuleOpInterface>(op.getOperation()->getParentOp()));
157 PortRefMapping mapping;
158 if (failed(tunnelDispatch(currentContainer, path, mapping)))
163 auto *it = mapping.find(&pi);
164 assert(it != mapping.end() &&
165 "expected to find a portref mapping for all get_port ops");
166 rewriter.replaceOp(pi.getPortOp, it->second);
170 rewriter.eraseOp(op);
176 llvm::ArrayRef<PathStepAttr> path,
177 PortRefMapping &mapping) {
178 PathStepAttr currentStep = path.front();
179 PortRefMapping targetValue;
180 path = path.drop_front();
181 if (currentStep.getDirection() == PathDirection::Parent) {
182 LogicalResult upRes = tunnelUp(currentContainer, path, mapping);
186 FlatSymbolRefAttr tunnelInto = currentStep.getChild();
187 LogicalResult downRes =
188 tunnelDown(currentContainer, tunnelInto, path, mapping);
195 Value Tunneler::portForwardIfNeeded(PortOpInterface actualPort,
198 cast<PortRefType>(actualPort.getPort().getType()).getDirection();
199 Direction requestedDir = portInfo.getRequestedDirection();
202 if (actualDir == requestedDir)
203 return actualPort.getPort();
206 OpBuilder::InsertionGuard guard(rewriter);
207 rewriter.setInsertionPointAfter(actualPort);
214 auto wireOp = rewriter.create<InputWireOp>(
216 rewriter.getStringAttr(actualPort.getPortName().strref() +
".wr"),
217 portInfo.getInnerType());
219 rewriter.create<PortWriteOp>(op.getLoc(), actualPort.getPort(),
221 return wireOp.getPort();
229 rewriter.create<PortReadOp>(op.getLoc(), actualPort.getPort());
230 auto wireOp = rewriter.create<OutputWireOp>(
233 rewriter.getStringAttr(actualPort.getPortName().strref() +
".rd")),
235 return wireOp.getPort();
241 FlatSymbolRefAttr name) {
242 if (parentOp->hasTrait<OpTrait::SymbolTable>()) {
243 auto *tunnelInstanceOp = SymbolTable::lookupSymbolIn(parentOp, name);
244 if (!tunnelInstanceOp)
246 return cast<ContainerInstanceOp>(tunnelInstanceOp);
250 for (
auto instanceOp : parentOp->getRegion(0).getOps<ContainerInstanceOp>()) {
251 if (instanceOp.getInnerSym().getSymName() == name.getValue())
260 FlatSymbolRefAttr tunnelInto,
261 llvm::ArrayRef<PathStepAttr> path,
262 PortRefMapping &portMapping) {
264 Operation *parentOp = currentContainer->
getModule().getOperation();
265 auto parentSymbolOp = dyn_cast<hw::InnerSymbolOpInterface>(parentOp);
266 assert(parentSymbolOp &&
"expected current container to be a symbol op");
268 locateInstanceIn(parentOp, tunnelInto);
269 if (failed(locateRes))
270 return op->emitOpError()
271 <<
"expected an instance named " << tunnelInto <<
" in @"
272 << parentSymbolOp.getInnerSymAttr().getSymName().getValue()
273 <<
" but found none";
274 ContainerInstanceOp tunnelInstance = *locateRes;
279 rewriter.setInsertionPointAfter(tunnelInstance);
281 auto targetGetPortOp =
282 rewriter.create<GetPortOp>(op.getLoc(), pi.getType(), tunnelInstance,
283 pi.getPortOp.getPortSymbol());
284 portMapping[&pi] = targetGetPortOp.getResult();
291 auto *tunnelScopeNode =
292 ig.
lookup(tunnelInstance.getTargetNameAttr().getName());
293 auto tunnelScope = tunnelScopeNode->
getModule<ScopeOpInterface>();
295 rewriter.setInsertionPointToEnd(tunnelScope.getBodyBlock());
296 llvm::DenseMap<StringAttr, OutputPortOp> outputPortOps;
298 outputPortOps[pi.portName] = rewriter.create<OutputPortOp>(
304 PortRefMapping childMapping;
305 if (failed(tunnelDispatch(tunnelScopeNode, path, childMapping)))
308 for (
auto [pi, res] : childMapping) {
312 rewriter.setInsertionPointToEnd(tunnelScope.getBodyBlock());
313 rewriter.create<PortWriteOp>(op.getLoc(), outputPortOps[portInfo.portName],
318 rewriter.setInsertionPointAfter(tunnelInstance);
319 auto getPortOp = rewriter.create<GetPortOp>(
320 op.getLoc(), tunnelInstance, portInfo.portName, portInfo.getType(),
323 rewriter.create<PortReadOp>(op.getLoc(), getPortOp).getResult();
331 llvm::ArrayRef<PathStepAttr> path,
332 PortRefMapping &portMapping) {
333 auto scopeOp = currentContainer->
getModule<ScopeOpInterface>();
334 if (currentContainer->
noUses())
335 return op->emitOpError()
336 <<
"cannot tunnel up from " << scopeOp.getScopeName()
337 <<
" because it has no uses";
339 for (
auto *use : currentContainer->
uses()) {
341 ig.
lookup(use->getParent()->getModule());
342 auto parentScope = parentScopeNode->
getModule<ScopeOpInterface>();
343 PortRefMapping targetPortMapping;
348 StringRef targetPortName = pi.getPortOp.getPortSymbol();
349 PortOpInterface portLikeOp = parentScope.lookupPort(targetPortName);
351 return op->emitOpError()
352 <<
"expected a port named " << targetPortName <<
" in "
353 << parentScope.getScopeName() <<
" but found none";
356 targetPortMapping[&pi] = portForwardIfNeeded(portLikeOp, pi);
361 if (failed(tunnelDispatch(parentScopeNode, path, targetPortMapping)))
365 auto instance = use->getInstance<ContainerInstanceOp>();
366 rewriter.setInsertionPointAfter(instance);
368 auto getPortOp = rewriter.create<GetPortOp>(
370 rewriter.create<PortWriteOp>(op.getLoc(), getPortOp,
371 targetPortMapping[&pi]);
376 rewriter.setInsertionPointToEnd(scopeOp.getBodyBlock());
378 auto inputPort = rewriter.create<InputPortOp>(
384 rewriter.create<PortReadOp>(op.getLoc(), inputPort.getResult())
393 TunnelingConversionPattern(MLIRContext *context,
InstanceGraph &ig,
394 IbisTunnelingOptions options)
396 options(std::move(options)) {}
399 matchAndRewrite(PathOp op, OpAdaptor adaptor,
400 ConversionPatternRewriter &rewriter)
const override {
401 return Tunneler(options, op, rewriter, ig).go();
406 IbisTunnelingOptions options;
409 struct TunnelingPass :
public impl::IbisTunnelingBase<TunnelingPass> {
410 using IbisTunnelingBase<TunnelingPass>::IbisTunnelingBase;
411 void runOnOperation()
override;
416 void TunnelingPass::runOnOperation() {
417 auto &ig = getAnalysis<InstanceGraph>();
418 auto *ctx = &getContext();
419 ConversionTarget target(*ctx);
420 target.addIllegalOp<PathOp>();
421 target.addLegalOp<InputPortOp, OutputPortOp, PortReadOp, PortWriteOp,
422 GetPortOp, InputWireOp, OutputWireOp>();
425 patterns.add<TunnelingConversionPattern>(
426 ctx, ig, IbisTunnelingOptions{readSuffix, writeSuffix});
429 applyPartialConversion(getOperation(), target, std::move(
patterns))))
433 std::unique_ptr<Pass>
435 return std::make_unique<TunnelingPass>(options);
assert(baseType &&"element must be base type")
static Attribute getAttr(ArrayRef< NamedAttribute > attrs, StringRef name)
Get an attribute by name from a list of named attributes.
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 IbisTunnelingOptions &={})
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
This holds the name, type, direction of a module's ports.