16#include "llvm/ADT/SmallPtrSet.h"
17#include "llvm/ADT/StringMap.h"
18#include "llvm/Support/raw_ostream.h"
22#define GEN_PASS_DEF_RESOLVEXMRREF
23#include "circt/Dialect/Arc/ArcPasses.h.inc"
32 if (c ==
'\\' || c ==
'|' || c ==
':')
40 for (
unsigned i = index, e = pathArray.size(); i < e; ++i) {
41 auto ref = cast<hw::InnerRefAttr>(pathArray[i]);
51 unsigned index, Type targetType) {
57 llvm::raw_string_ostream os(key);
64 for (Operation *parent = op->getParentOp(); parent;
65 parent = parent->getParentOp())
66 if (isa<sv::IfDefOp>(parent))
72 ArrayAttr pathArray, Type targetType) {
78 llvm::raw_string_ostream os(key);
85struct ResolveXMRRefPass
86 :
public arc::impl::ResolveXMRRefBase<ResolveXMRRefPass> {
87 using Base = arc::impl::ResolveXMRRefBase<ResolveXMRRefPass>;
90 llvm::StringMap<unsigned> borePortByKey;
91 llvm::StringMap<unsigned> captureInputPortByKey;
92 llvm::StringMap<unsigned> nextBoreOrdinalByModule;
93 bool bindContextDiagnosedFailure =
false;
95 void runOnOperation()
override;
96 Value boreRecursively(ArrayAttr pathArray,
unsigned index,
97 hw::HWModuleOp currentMod, SymbolTable &symTable,
98 StringAttr targetSym, Type targetType);
99 Value getInstancePortValue(hw::InstanceOp inst,
unsigned portIdx,
100 mlir::Operation *moduleOp);
101 std::string createUniqueBoredPortName(hw::HWModuleOp mod,
102 StringAttr targetSym);
103 std::string createUniqueCapturePortName(hw::HWModuleOp mod,
104 StringAttr targetSym);
105 enum class XMRUseMode { ReadOnly, Unsupported };
106 XMRUseMode classifyXMRUse(sv::XMRRefOp xmrRefOp);
107 Value resolveViaBindContext(sv::XMRRefOp xmrRefOp, hw::HierPathOp pathOp,
108 hw::HWModuleOp payloadMod, SymbolTable &symTable,
113ResolveXMRRefPass::XMRUseMode
114ResolveXMRRefPass::classifyXMRUse(sv::XMRRefOp xmrRefOp) {
115 bool hasRead =
false;
116 for (Operation *user : xmrRefOp->getUsers()) {
117 if (isa<sv::ReadInOutOp>(user)) {
123 <<
"unsupported sv.xmr.ref use by '" << user->getName()
124 <<
"'; only read-only uses (sv.read_inout) are supported";
125 return XMRUseMode::Unsupported;
129 xmrRefOp.emitError(
"sv.xmr.ref has no read uses; write/other uses are not "
131 return XMRUseMode::Unsupported;
134 return XMRUseMode::ReadOnly;
137std::string ResolveXMRRefPass::createUniqueBoredPortName(hw::HWModuleOp mod,
138 StringAttr targetSym) {
139 auto moduleName = mod.getModuleName();
140 unsigned &ordinal = nextBoreOrdinalByModule[moduleName];
143 std::string candidate =
"xmr_bored_" + targetSym.getValue().str() +
"_" +
144 std::to_string(ordinal++);
147 for (
unsigned i = 0, e = mod.getNumPorts(); i < e; ++i) {
148 if (mod.getPort(i).
name.getValue() == candidate) {
159ResolveXMRRefPass::createUniqueCapturePortName(hw::HWModuleOp mod,
160 StringAttr targetSym) {
161 auto moduleName = mod.getModuleName();
162 unsigned &ordinal = nextBoreOrdinalByModule[moduleName];
165 std::string candidate =
"xmr_capture_" + targetSym.getValue().str() +
"_" +
166 std::to_string(ordinal++);
169 for (
unsigned i = 0, e = mod.getNumPorts(); i < e; ++i) {
170 if (mod.getPort(i).
name.getValue() == candidate) {
180Value ResolveXMRRefPass::resolveViaBindContext(sv::XMRRefOp xmrRefOp,
181 hw::HierPathOp pathOp,
182 hw::HWModuleOp payloadMod,
183 SymbolTable &symTable,
185 bindContextDiagnosedFailure =
false;
186 auto pathArray = pathOp.getNamepath();
187 auto root = dyn_cast<hw::InnerRefAttr>(pathArray[0]);
191 SmallVector<hw::InstanceOp> bindInstances;
192 bool hasConditionalBind =
false;
193 auto module = getOperation();
194 module.walk([&](sv::BindOp bindOp) {
195 auto boundInst = bindOp.getReferencedInstance(nullptr);
199 if (boundInst.getModuleName() != payloadMod.getModuleName())
202 if (bindOp.getInstance().getModule() != root.getModule())
206 hasConditionalBind =
true;
210 bindInstances.push_back(boundInst);
213 if (hasConditionalBind) {
214 xmrRefOp.emitError(
"cannot resolve XMR through conditionally guarded "
215 "sv.bind; only unconditional sv.bind is supported");
216 bindContextDiagnosedFailure =
true;
220 if (bindInstances.empty())
223 if (bindInstances.size() != 1) {
224 xmrRefOp.emitError(
"bind-context XMR requires a unique unconditional "
226 bindContextDiagnosedFailure =
true;
230 auto hostModule = bindInstances.front()->getParentOfType<hw::HWModuleOp>();
231 if (!hostModule || hostModule.getModuleName() != root.getModule()) {
232 xmrRefOp.emitError(
"bind host module mismatch while resolving XMR path");
233 bindContextDiagnosedFailure =
true;
237 Value hostValue = boreRecursively(pathArray, 0, hostModule, symTable,
238 pathOp.ref(), targetType);
242 llvm::SmallPtrSet<Operation *, 8> bindInstSet;
243 for (
auto inst : bindInstances)
244 bindInstSet.insert(inst.getOperation());
246 auto uses = SymbolTable::getSymbolUses(payloadMod, module);
250 for (
auto use : *uses) {
251 auto userInst = dyn_cast<hw::InstanceOp>(use.getUser());
254 if (!bindInstSet.contains(userInst.getOperation())) {
255 xmrRefOp.emitError(
"payload module has non-bind instances; refusing to "
256 "resolve bind-context XMR ambiguously");
257 bindContextDiagnosedFailure =
true;
262 std::string captureKey =
buildCaptureKey(payloadMod, pathArray, targetType);
263 unsigned inputPortIdx;
264 auto captureIt = captureInputPortByKey.find(captureKey);
265 if (captureIt == captureInputPortByKey.end()) {
266 std::string portName =
267 createUniqueCapturePortName(payloadMod, pathOp.ref());
269 OpBuilder b(payloadMod.getContext());
271 payloadMod.appendInput(b.getStringAttr(portName), targetType);
272 inputPortIdx = appended.second.getArgNumber();
273 captureInputPortByKey[captureKey] = inputPortIdx;
275 SmallVector<hw::InstanceOp> instances;
276 instances.reserve(bindInstSet.size());
277 for (
auto use : *uses)
278 if (
auto userInst = dyn_cast<hw::InstanceOp>(use.getUser()))
279 instances.push_back(userInst);
281 for (
auto userInst : instances) {
282 SmallVector<Value> operands(userInst.getOperands());
283 if (!bindInstSet.contains(userInst.getOperation())) {
284 xmrRefOp.emitError(
"payload module has non-bind instances; refusing to "
285 "resolve bind-context XMR ambiguously");
286 bindContextDiagnosedFailure =
true;
289 operands.push_back(hostValue);
291 OpBuilder ib(userInst);
292 auto newInst = hw::InstanceOp::create(
293 ib, userInst.getLoc(), payloadMod, userInst.getInstanceNameAttr(),
294 operands, userInst.getParameters(), userInst.getInnerSymAttr());
296 for (
unsigned i = 0; i < userInst.getNumResults(); ++i)
297 userInst.getResult(i).replaceAllUsesWith(newInst.getResult(i));
299 if (userInst.getDoNotPrint())
300 newInst.setDoNotPrintAttr(UnitAttr::get(newInst.getContext()));
305 return payloadMod.getBodyBlock()->getArgument(inputPortIdx);
307 inputPortIdx = captureIt->second;
309 auto bindInst = bindInstances.front();
310 if (inputPortIdx >= bindInst.getNumOperands()) {
311 xmrRefOp.emitError(
"bind instance is missing required capture input "
312 "operand for previously materialized XMR");
313 bindContextDiagnosedFailure =
true;
317 return bindInst.getOperand(inputPortIdx);
321void ResolveXMRRefPass::runOnOperation() {
322 auto module = getOperation();
323 SymbolTable symTable(module);
324 borePortByKey.clear();
325 captureInputPortByKey.clear();
326 nextBoreOrdinalByModule.clear();
329 SmallVector<Operation *> opsToErase;
331 module.walk([&](sv::XMRRefOp xmrRefOp) {
335 if (classifyXMRUse(xmrRefOp) != XMRUseMode::ReadOnly) {
340 auto pathOp = xmrRefOp.getReferencedPath(
nullptr);
342 xmrRefOp.emitError(
"unable to resolve path for XMR reference");
347 StringAttr leafModName = pathOp.leafMod();
348 Operation *leafMod = symTable.lookup(leafModName);
350 xmrRefOp.emitError(
"leaf module not found in symbol table");
355 OpBuilder builder(xmrRefOp);
356 Value resolvedValue =
nullptr;
358 Type targetType = sv::getHandleElementType(xmrRefOp.getType());
360 ArrayAttr pathArray = pathOp.getNamepath();
361 auto currentModule = xmrRefOp->getParentOfType<hw::HWModuleOp>();
363 resolvedValue = boreRecursively(pathArray, 0, currentModule, symTable,
364 pathOp.ref(), targetType);
366 if (!resolvedValue && pathOp.isComponent()) {
367 resolvedValue = resolveViaBindContext(xmrRefOp, pathOp, currentModule,
368 symTable, targetType);
369 if (!resolvedValue && bindContextDiagnosedFailure) {
375 if (!resolvedValue) {
376 if (isa<hw::HWModuleExternOp>(leafMod)) {
377 xmrRefOp.emitError(
"unable to resolve XMR into internal blackbox "
378 "symbol; rerun with "
379 "--arc-resolve-xmr=lower-blackbox-internal-to-zero "
380 "to force zero-lowering");
384 if (pathOp.isComponent()) {
385 xmrRefOp.emitError(
"unable to resolve component path");
389 xmrRefOp.emitError(
"unsupported XMR reference type");
395 for (OpOperand &use :
396 llvm::make_early_inc_range(xmrRefOp.getResult().getUses())) {
397 if (
auto readOp = dyn_cast<sv::ReadInOutOp>(use.getOwner())) {
398 readOp.getResult().replaceAllUsesWith(resolvedValue);
399 opsToErase.push_back(readOp);
402 opsToErase.push_back(xmrRefOp);
407 return signalPassFailure();
409 module.walk([&](hw::HierPathOp pathOp) {
410 if (pathOp->use_empty()) {
411 opsToErase.push_back(pathOp);
415 for (Operation *op : opsToErase)
419Value ResolveXMRRefPass::boreRecursively(ArrayAttr pathArray,
unsigned index,
420 hw::HWModuleOp currentMod,
421 SymbolTable &symTable,
422 StringAttr targetSym,
424 auto innerRef = cast<hw::InnerRefAttr>(pathArray[index]);
425 StringAttr symName = innerRef.getName();
427 if (index == pathArray.size() - 1) {
428 hw::InnerSymbolTable ist(currentMod);
429 auto target = ist.lookup(symName);
433 if (target.isPort()) {
434 unsigned pIdx = target.getPort();
435 if (currentMod.getPort(pIdx).isOutput()) {
437 cast<hw::OutputOp>(currentMod.getBodyBlock()->getTerminator());
439 for (
unsigned i = 0; i < pIdx; ++i)
440 if (currentMod.getPort(i).isOutput())
442 return outOp.getOperand(oIdx);
445 for (
unsigned i = 0; i < pIdx; ++i)
446 if (!currentMod.getPort(i).isOutput())
448 return currentMod.getBodyBlock()->getArgument(aIdx);
450 return target.getOp()->getNumResults() > 0 ? target.getOp()->getResult(0)
454 hw::InnerSymbolTable currentIST(currentMod);
456 dyn_cast_or_null<hw::InstanceOp>(currentIST.lookup(symName).getOp());
460 Operation *childModOp = symTable.lookup(instOp.getModuleNameAttr().getAttr());
464 auto nextRef = cast<hw::InnerRefAttr>(pathArray[index + 1]);
465 StringAttr nextSym = nextRef.getName();
467 hw::InnerSymbolTable childIST(childModOp);
468 auto nextTarget = childIST.lookup(nextSym);
470 if (nextTarget && nextTarget.isPort()) {
471 return getInstancePortValue(instOp, nextTarget.getPort(), childModOp);
474 if (isa<hw::HWModuleExternOp>(childModOp)) {
475 if (lowerBlackBoxInternalToZero) {
476 instOp.emitWarning() <<
"XMR target '" << nextSym
477 <<
"' is internal to blackbox. Lowering to 0.";
484 auto childMod = cast<hw::HWModuleOp>(childModOp);
485 std::string boreKey =
486 buildBoreKey(childMod, pathArray, index + 1, targetType);
487 if (
auto it = borePortByKey.find(boreKey); it != borePortByKey.end()) {
488 if (it->second >= instOp.getNumResults())
490 return instOp.getResult(it->second);
493 Value childVal = boreRecursively(pathArray, index + 1, childMod, symTable,
494 targetSym, targetType);
498 std::string portName = createUniqueBoredPortName(childMod, targetSym);
500 OpBuilder mb(childMod.getContext());
501 hw::PortInfo newPort;
502 newPort.
name = mb.getStringAttr(portName);
503 newPort.
dir = hw::ModulePort::Direction::Output;
504 newPort.
type = targetType;
506 SmallVector<std::pair<unsigned, hw::PortInfo>> newOutputs;
507 newOutputs.push_back({childMod.getNumOutputPorts(), newPort});
508 childMod.modifyPorts({}, newOutputs, {}, {});
510 auto outOp = cast<hw::OutputOp>(childMod.getBodyBlock()->getTerminator());
511 outOp->insertOperands(outOp->getNumOperands(), childVal);
512 unsigned resIdx = childMod.getNumOutputPorts() - 1;
513 borePortByKey[boreKey] = resIdx;
515 auto top = getOperation();
516 auto uses = SymbolTable::getSymbolUses(childMod, top);
520 SmallVector<hw::InstanceOp> instances;
521 for (
auto use : *uses)
522 if (
auto userInst = dyn_cast<hw::InstanceOp>(use.getUser()))
523 instances.push_back(userInst);
526 for (
auto userInst : instances) {
527 SmallVector<Value> operands(userInst.getOperands());
528 OpBuilder b(userInst);
529 auto newInst = hw::InstanceOp::create(
530 b, userInst.getLoc(), childMod, userInst.getInstanceNameAttr(),
531 operands, userInst.getParameters(), userInst.getInnerSymAttr());
533 for (
unsigned i = 0; i < userInst.getNumResults(); ++i)
534 userInst.getResult(i).replaceAllUsesWith(newInst.getResult(i));
536 if (userInst.getDoNotPrint())
537 newInst.setDoNotPrintAttr(UnitAttr::get(newInst.getContext()));
539 if (userInst == instOp)
540 boredValue = newInst.getResult(resIdx);
548Value ResolveXMRRefPass::getInstancePortValue(hw::InstanceOp inst,
550 mlir::Operation *moduleOp) {
552 unsigned inOrInoutIdx = 0;
554 for (
unsigned i = 0; i < portIdx; ++i) {
556 if (
auto mod = dyn_cast<hw::HWModuleOp>(moduleOp))
557 port = mod.getPort(i);
559 port = cast<hw::HWModuleExternOp>(moduleOp).getPort(i);
567 hw::PortInfo targetPort;
568 if (
auto mod = dyn_cast<hw::HWModuleOp>(moduleOp))
569 targetPort = mod.getPort(portIdx);
571 targetPort = cast<hw::HWModuleExternOp>(moduleOp).getPort(portIdx);
574 if (outIdx < inst.getNumResults())
575 return inst.getResult(outIdx);
577 if (inOrInoutIdx < inst.getNumOperands())
578 return inst.getOperand(inOrInoutIdx);
static void appendEscaped(std::string &out, StringRef text)
static std::string buildPathSuffixKey(ArrayAttr pathArray, unsigned index)
static std::string buildCaptureKey(hw::HWModuleOp payloadMod, ArrayAttr pathArray, Type targetType)
static std::string buildBoreKey(hw::HWModuleOp childMod, ArrayAttr pathArray, unsigned index, Type targetType)
static bool isConditionallyGuarded(Operation *op)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.