15#include "llvm/ADT/SmallPtrSet.h"
16#include "llvm/ADT/StringMap.h"
17#include "llvm/Support/raw_ostream.h"
21#define GEN_PASS_DEF_RESOLVEXMRREF
22#include "circt/Dialect/Arc/ArcPasses.h.inc"
31 if (c ==
'\\' || c ==
'|' || c ==
':')
39 for (
unsigned i = index, e = pathArray.size(); i < e; ++i) {
40 auto ref = cast<hw::InnerRefAttr>(pathArray[i]);
50 unsigned index, Type targetType) {
56 llvm::raw_string_ostream os(key);
63 for (Operation *parent = op->getParentOp(); parent;
64 parent = parent->getParentOp())
65 if (isa<sv::IfDefOp>(parent))
71 ArrayAttr pathArray, Type targetType) {
77 llvm::raw_string_ostream os(key);
84struct ResolveXMRRefPass
85 :
public arc::impl::ResolveXMRRefBase<ResolveXMRRefPass> {
86 using Base = arc::impl::ResolveXMRRefBase<ResolveXMRRefPass>;
89 llvm::StringMap<unsigned> borePortByKey;
90 llvm::StringMap<unsigned> captureInputPortByKey;
91 llvm::StringMap<unsigned> nextBoreOrdinalByModule;
92 bool bindContextDiagnosedFailure =
false;
94 void runOnOperation()
override;
95 Value boreRecursively(ArrayAttr pathArray,
unsigned index,
97 StringAttr targetSym, Type targetType);
98 Value getInstancePortValue(hw::InstanceOp inst,
unsigned portIdx,
99 mlir::Operation *moduleOp);
101 StringAttr targetSym);
103 StringAttr targetSym);
104 enum class XMRUseMode { ReadOnly, Unsupported };
105 XMRUseMode classifyXMRUse(sv::XMRRefOp xmrRefOp);
106 Value resolveViaBindContext(sv::XMRRefOp xmrRefOp, hw::HierPathOp pathOp,
112ResolveXMRRefPass::XMRUseMode
113ResolveXMRRefPass::classifyXMRUse(sv::XMRRefOp xmrRefOp) {
114 bool hasRead =
false;
115 for (Operation *user : xmrRefOp->getUsers()) {
116 if (isa<sv::ReadInOutOp>(user)) {
122 <<
"unsupported sv.xmr.ref use by '" << user->getName()
123 <<
"'; only read-only uses (sv.read_inout) are supported";
124 return XMRUseMode::Unsupported;
128 xmrRefOp.emitError(
"sv.xmr.ref has no read uses; write/other uses are not "
130 return XMRUseMode::Unsupported;
133 return XMRUseMode::ReadOnly;
136std::string ResolveXMRRefPass::createUniqueBoredPortName(
hw::HWModuleOp mod,
137 StringAttr targetSym) {
138 auto moduleName = mod.getModuleName();
139 unsigned &ordinal = nextBoreOrdinalByModule[moduleName];
142 std::string candidate =
"xmr_bored_" + targetSym.getValue().str() +
"_" +
143 std::to_string(ordinal++);
146 for (
unsigned i = 0, e = mod.getNumPorts(); i < e; ++i) {
147 if (mod.getPort(i).
name.getValue() == candidate) {
159 StringAttr targetSym) {
160 auto moduleName = mod.getModuleName();
161 unsigned &ordinal = nextBoreOrdinalByModule[moduleName];
164 std::string candidate =
"xmr_capture_" + targetSym.getValue().str() +
"_" +
165 std::to_string(ordinal++);
168 for (
unsigned i = 0, e = mod.getNumPorts(); i < e; ++i) {
169 if (mod.getPort(i).
name.getValue() == candidate) {
179Value ResolveXMRRefPass::resolveViaBindContext(sv::XMRRefOp xmrRefOp,
180 hw::HierPathOp pathOp,
182 SymbolTable &symTable,
184 bindContextDiagnosedFailure =
false;
185 auto pathArray = pathOp.getNamepath();
186 auto root = dyn_cast<hw::InnerRefAttr>(pathArray[0]);
190 SmallVector<hw::InstanceOp> bindInstances;
191 bool hasConditionalBind =
false;
192 auto module = getOperation();
193 module.walk([&](sv::BindOp bindOp) {
194 auto boundInst = bindOp.getReferencedInstance(nullptr);
198 if (boundInst.getModuleName() != payloadMod.getModuleName())
201 if (bindOp.getInstance().getModule() != root.getModule())
205 hasConditionalBind =
true;
209 bindInstances.push_back(boundInst);
212 if (hasConditionalBind) {
213 xmrRefOp.emitError(
"cannot resolve XMR through conditionally guarded "
214 "sv.bind; only unconditional sv.bind is supported");
215 bindContextDiagnosedFailure =
true;
219 if (bindInstances.empty())
222 if (bindInstances.size() != 1) {
223 xmrRefOp.emitError(
"bind-context XMR requires a unique unconditional "
225 bindContextDiagnosedFailure =
true;
229 auto hostModule = bindInstances.front()->getParentOfType<
hw::HWModuleOp>();
230 if (!hostModule || hostModule.getModuleName() != root.getModule()) {
231 xmrRefOp.emitError(
"bind host module mismatch while resolving XMR path");
232 bindContextDiagnosedFailure =
true;
236 Value hostValue = boreRecursively(pathArray, 0, hostModule, symTable,
237 pathOp.ref(), targetType);
241 llvm::SmallPtrSet<Operation *, 8> bindInstSet;
242 for (
auto inst : bindInstances)
243 bindInstSet.insert(inst.getOperation());
245 auto uses = SymbolTable::getSymbolUses(payloadMod, module);
249 for (
auto use : *uses) {
250 auto userInst = dyn_cast<hw::InstanceOp>(use.getUser());
253 if (!bindInstSet.contains(userInst.getOperation())) {
254 xmrRefOp.emitError(
"payload module has non-bind instances; refusing to "
255 "resolve bind-context XMR ambiguously");
256 bindContextDiagnosedFailure =
true;
261 std::string captureKey =
buildCaptureKey(payloadMod, pathArray, targetType);
262 unsigned inputPortIdx;
263 auto captureIt = captureInputPortByKey.find(captureKey);
264 if (captureIt == captureInputPortByKey.end()) {
265 std::string portName =
266 createUniqueCapturePortName(payloadMod, pathOp.ref());
268 OpBuilder
b(payloadMod.getContext());
270 payloadMod.appendInput(
b.getStringAttr(portName), targetType);
271 inputPortIdx = appended.second.getArgNumber();
272 captureInputPortByKey[captureKey] = inputPortIdx;
274 SmallVector<hw::InstanceOp> instances;
275 instances.reserve(bindInstSet.size());
276 for (
auto use : *uses)
277 if (auto userInst = dyn_cast<
hw::InstanceOp>(use.getUser()))
278 instances.push_back(userInst);
280 for (
auto userInst : instances) {
281 SmallVector<Value> operands(userInst.getOperands());
282 if (!bindInstSet.contains(userInst.getOperation())) {
283 xmrRefOp.emitError(
"payload module has non-bind instances; refusing to "
284 "resolve bind-context XMR ambiguously");
285 bindContextDiagnosedFailure =
true;
288 operands.push_back(hostValue);
290 OpBuilder ib(userInst);
291 auto newInst = hw::InstanceOp::create(
292 ib, userInst.getLoc(), payloadMod, userInst.getInstanceNameAttr(),
293 operands, userInst.getParameters(), userInst.getInnerSymAttr());
295 for (
unsigned i = 0; i < userInst.getNumResults(); ++i)
296 userInst.getResult(i).replaceAllUsesWith(newInst.getResult(i));
298 if (userInst.getDoNotPrint())
299 newInst.setDoNotPrintAttr(UnitAttr::get(newInst.getContext()));
304 return payloadMod.getBodyBlock()->getArgument(inputPortIdx);
306 inputPortIdx = captureIt->second;
308 auto bindInst = bindInstances.front();
309 if (inputPortIdx >= bindInst.getNumOperands()) {
310 xmrRefOp.emitError(
"bind instance is missing required capture input "
311 "operand for previously materialized XMR");
312 bindContextDiagnosedFailure =
true;
316 return bindInst.getOperand(inputPortIdx);
320void ResolveXMRRefPass::runOnOperation() {
321 auto module = getOperation();
322 SymbolTable symTable(module);
323 borePortByKey.clear();
324 captureInputPortByKey.clear();
325 nextBoreOrdinalByModule.clear();
328 SmallVector<Operation *> opsToErase;
330 module.walk([&](sv::XMRRefOp xmrRefOp) {
334 if (classifyXMRUse(xmrRefOp) != XMRUseMode::ReadOnly) {
339 auto pathOp = xmrRefOp.getReferencedPath(
nullptr);
341 xmrRefOp.emitError(
"unable to resolve path for XMR reference");
346 StringAttr leafModName = pathOp.leafMod();
347 Operation *leafMod = symTable.lookup(leafModName);
349 xmrRefOp.emitError(
"leaf module not found in symbol table");
354 OpBuilder builder(xmrRefOp);
355 Value resolvedValue =
nullptr;
357 Type targetType = cast<hw::InOutType>(xmrRefOp.getType()).getElementType();
359 ArrayAttr pathArray = pathOp.getNamepath();
362 resolvedValue = boreRecursively(pathArray, 0, currentModule, symTable,
363 pathOp.ref(), targetType);
365 if (!resolvedValue && pathOp.isComponent()) {
366 resolvedValue = resolveViaBindContext(xmrRefOp, pathOp, currentModule,
367 symTable, targetType);
368 if (!resolvedValue && bindContextDiagnosedFailure) {
374 if (!resolvedValue) {
375 if (isa<hw::HWModuleExternOp>(leafMod)) {
376 xmrRefOp.emitError(
"unable to resolve XMR into internal blackbox "
377 "symbol; rerun with "
378 "--arc-resolve-xmr=lower-blackbox-internal-to-zero "
379 "to force zero-lowering");
383 if (pathOp.isComponent()) {
384 xmrRefOp.emitError(
"unable to resolve component path");
388 xmrRefOp.emitError(
"unsupported XMR reference type");
394 for (OpOperand &use :
395 llvm::make_early_inc_range(xmrRefOp.getResult().getUses())) {
396 if (
auto readOp = dyn_cast<sv::ReadInOutOp>(use.getOwner())) {
397 readOp.getResult().replaceAllUsesWith(resolvedValue);
398 opsToErase.push_back(readOp);
401 opsToErase.push_back(xmrRefOp);
406 return signalPassFailure();
408 module.walk([&](hw::HierPathOp pathOp) {
409 if (pathOp->use_empty()) {
410 opsToErase.push_back(pathOp);
414 for (Operation *op : opsToErase)
418Value ResolveXMRRefPass::boreRecursively(ArrayAttr pathArray,
unsigned index,
420 SymbolTable &symTable,
421 StringAttr targetSym,
423 auto innerRef = cast<hw::InnerRefAttr>(pathArray[index]);
424 StringAttr symName = innerRef.getName();
426 if (index == pathArray.size() - 1) {
428 auto target = ist.lookup(symName);
432 if (target.isPort()) {
433 unsigned pIdx = target.getPort();
434 if (currentMod.getPort(pIdx).isOutput()) {
436 cast<hw::OutputOp>(currentMod.getBodyBlock()->getTerminator());
438 for (
unsigned i = 0; i < pIdx; ++i)
439 if (currentMod.getPort(i).isOutput())
441 return outOp.getOperand(oIdx);
444 for (
unsigned i = 0; i < pIdx; ++i)
445 if (!currentMod.getPort(i).isOutput())
447 return currentMod.getBodyBlock()->getArgument(aIdx);
449 return target.getOp()->getNumResults() > 0 ? target.getOp()->getResult(0)
455 dyn_cast_or_null<hw::InstanceOp>(currentIST.lookup(symName).getOp());
459 Operation *childModOp = symTable.lookup(instOp.getModuleNameAttr().getAttr());
463 auto nextRef = cast<hw::InnerRefAttr>(pathArray[index + 1]);
464 StringAttr nextSym = nextRef.getName();
467 auto nextTarget = childIST.lookup(nextSym);
469 if (nextTarget && nextTarget.isPort()) {
470 return getInstancePortValue(instOp, nextTarget.getPort(), childModOp);
473 if (isa<hw::HWModuleExternOp>(childModOp)) {
474 if (lowerBlackBoxInternalToZero) {
475 instOp.emitWarning() <<
"XMR target '" << nextSym
476 <<
"' is internal to blackbox. Lowering to 0.";
483 auto childMod = cast<hw::HWModuleOp>(childModOp);
484 std::string boreKey =
485 buildBoreKey(childMod, pathArray, index + 1, targetType);
486 if (
auto it = borePortByKey.find(boreKey); it != borePortByKey.end()) {
487 if (it->second >= instOp.getNumResults())
489 return instOp.getResult(it->second);
492 Value childVal = boreRecursively(pathArray, index + 1, childMod, symTable,
493 targetSym, targetType);
497 std::string portName = createUniqueBoredPortName(childMod, targetSym);
499 OpBuilder mb(childMod.getContext());
501 newPort.
name = mb.getStringAttr(portName);
502 newPort.
dir = hw::ModulePort::Direction::Output;
503 newPort.
type = targetType;
505 SmallVector<std::pair<unsigned, hw::PortInfo>> newOutputs;
506 newOutputs.push_back({childMod.getNumOutputPorts(), newPort});
507 childMod.modifyPorts({}, newOutputs, {}, {});
509 auto outOp = cast<hw::OutputOp>(childMod.getBodyBlock()->getTerminator());
510 outOp->insertOperands(outOp->getNumOperands(), childVal);
511 unsigned resIdx = childMod.getNumOutputPorts() - 1;
512 borePortByKey[boreKey] = resIdx;
514 auto top = getOperation();
515 auto uses = SymbolTable::getSymbolUses(childMod, top);
519 SmallVector<hw::InstanceOp> instances;
520 for (
auto use : *uses)
521 if (auto userInst = dyn_cast<
hw::InstanceOp>(use.getUser()))
522 instances.push_back(userInst);
525 for (
auto userInst : instances) {
526 SmallVector<Value> operands(userInst.getOperands());
527 OpBuilder
b(userInst);
528 auto newInst = hw::InstanceOp::create(
529 b, userInst.getLoc(), childMod, userInst.getInstanceNameAttr(),
530 operands, userInst.getParameters(), userInst.getInnerSymAttr());
532 for (
unsigned i = 0; i < userInst.getNumResults(); ++i)
533 userInst.getResult(i).replaceAllUsesWith(newInst.getResult(i));
535 if (userInst.getDoNotPrint())
536 newInst.setDoNotPrintAttr(UnitAttr::get(newInst.getContext()));
538 if (userInst == instOp)
539 boredValue = newInst.getResult(resIdx);
547Value ResolveXMRRefPass::getInstancePortValue(hw::InstanceOp inst,
549 mlir::Operation *moduleOp) {
551 unsigned inOrInoutIdx = 0;
553 for (
unsigned i = 0; i < portIdx; ++i) {
555 if (
auto mod = dyn_cast<hw::HWModuleOp>(moduleOp))
556 port = mod.getPort(i);
558 port = cast<hw::HWModuleExternOp>(moduleOp).getPort(i);
567 if (
auto mod = dyn_cast<hw::HWModuleOp>(moduleOp))
568 targetPort = mod.getPort(portIdx);
570 targetPort = cast<hw::HWModuleExternOp>(moduleOp).getPort(portIdx);
573 if (outIdx < inst.getNumResults())
574 return inst.getResult(outIdx);
576 if (inOrInoutIdx < inst.getNumOperands())
577 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)
A table of inner symbols and their resolutions.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
This holds the name, type, direction of a module's ports.