CIRCT 23.0.0git
Loading...
Searching...
No Matches
ResolveXMRRef.cpp
Go to the documentation of this file.
1//===- ResolveXmrRef.cpp --------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
15#include "llvm/ADT/SmallPtrSet.h"
16#include "llvm/ADT/StringMap.h"
17#include "llvm/Support/raw_ostream.h"
18
19namespace circt {
20namespace arc {
21#define GEN_PASS_DEF_RESOLVEXMRREF
22#include "circt/Dialect/Arc/ArcPasses.h.inc"
23} // namespace arc
24} // namespace circt
25
26using namespace circt;
27using namespace arc;
28
29static void appendEscaped(std::string &out, StringRef text) {
30 for (char c : text) {
31 if (c == '\\' || c == '|' || c == ':')
32 out.push_back('\\');
33 out.push_back(c);
34 }
35}
36
37static std::string buildPathSuffixKey(ArrayAttr pathArray, unsigned index) {
38 std::string key;
39 for (unsigned i = index, e = pathArray.size(); i < e; ++i) {
40 auto ref = cast<hw::InnerRefAttr>(pathArray[i]);
41 appendEscaped(key, ref.getModule().getValue());
42 key.push_back(':');
43 appendEscaped(key, ref.getName().getValue());
44 key.push_back('|');
45 }
46 return key;
47}
48
49static std::string buildBoreKey(hw::HWModuleOp childMod, ArrayAttr pathArray,
50 unsigned index, Type targetType) {
51 std::string key;
52 appendEscaped(key, childMod.getModuleName());
53 key.push_back('|');
54 key.append(buildPathSuffixKey(pathArray, index));
55 key.push_back('|');
56 llvm::raw_string_ostream os(key);
57 targetType.print(os);
58 os.flush();
59 return key;
60}
61
62static bool isConditionallyGuarded(Operation *op) {
63 for (Operation *parent = op->getParentOp(); parent;
64 parent = parent->getParentOp())
65 if (isa<sv::IfDefOp>(parent))
66 return true;
67 return false;
68}
69
70static std::string buildCaptureKey(hw::HWModuleOp payloadMod,
71 ArrayAttr pathArray, Type targetType) {
72 std::string key;
73 appendEscaped(key, payloadMod.getModuleName());
74 key.push_back('|');
75 key.append(buildPathSuffixKey(pathArray, 0));
76 key.push_back('|');
77 llvm::raw_string_ostream os(key);
78 targetType.print(os);
79 os.flush();
80 return key;
81}
82
83namespace {
84struct ResolveXMRRefPass
85 : public arc::impl::ResolveXMRRefBase<ResolveXMRRefPass> {
86 using Base = arc::impl::ResolveXMRRefBase<ResolveXMRRefPass>;
87 using Base::Base;
88
89 llvm::StringMap<unsigned> borePortByKey;
90 llvm::StringMap<unsigned> captureInputPortByKey;
91 llvm::StringMap<unsigned> nextBoreOrdinalByModule;
92 bool bindContextDiagnosedFailure = false;
93
94 void runOnOperation() override;
95 Value boreRecursively(ArrayAttr pathArray, unsigned index,
96 hw::HWModuleOp currentMod, SymbolTable &symTable,
97 StringAttr targetSym, Type targetType);
98 Value getInstancePortValue(hw::InstanceOp inst, unsigned portIdx,
99 mlir::Operation *moduleOp);
100 std::string createUniqueBoredPortName(hw::HWModuleOp mod,
101 StringAttr targetSym);
102 std::string createUniqueCapturePortName(hw::HWModuleOp mod,
103 StringAttr targetSym);
104 enum class XMRUseMode { ReadOnly, Unsupported };
105 XMRUseMode classifyXMRUse(sv::XMRRefOp xmrRefOp);
106 Value resolveViaBindContext(sv::XMRRefOp xmrRefOp, hw::HierPathOp pathOp,
107 hw::HWModuleOp payloadMod, SymbolTable &symTable,
108 Type targetType);
109};
110} // namespace
111
112ResolveXMRRefPass::XMRUseMode
113ResolveXMRRefPass::classifyXMRUse(sv::XMRRefOp xmrRefOp) {
114 bool hasRead = false;
115 for (Operation *user : xmrRefOp->getUsers()) {
116 if (isa<sv::ReadInOutOp>(user)) {
117 hasRead = true;
118 continue;
119 }
120
121 xmrRefOp.emitError()
122 << "unsupported sv.xmr.ref use by '" << user->getName()
123 << "'; only read-only uses (sv.read_inout) are supported";
124 return XMRUseMode::Unsupported;
125 }
126
127 if (!hasRead) {
128 xmrRefOp.emitError("sv.xmr.ref has no read uses; write/other uses are not "
129 "supported yet");
130 return XMRUseMode::Unsupported;
131 }
132
133 return XMRUseMode::ReadOnly;
134}
135
136std::string ResolveXMRRefPass::createUniqueBoredPortName(hw::HWModuleOp mod,
137 StringAttr targetSym) {
138 auto moduleName = mod.getModuleName();
139 unsigned &ordinal = nextBoreOrdinalByModule[moduleName];
140
141 while (true) {
142 std::string candidate = "xmr_bored_" + targetSym.getValue().str() + "_" +
143 std::to_string(ordinal++);
144
145 bool exists = false;
146 for (unsigned i = 0, e = mod.getNumPorts(); i < e; ++i) {
147 if (mod.getPort(i).name.getValue() == candidate) {
148 exists = true;
149 break;
150 }
151 }
152 if (!exists)
153 return candidate;
154 }
155}
156
157std::string
158ResolveXMRRefPass::createUniqueCapturePortName(hw::HWModuleOp mod,
159 StringAttr targetSym) {
160 auto moduleName = mod.getModuleName();
161 unsigned &ordinal = nextBoreOrdinalByModule[moduleName];
162
163 while (true) {
164 std::string candidate = "xmr_capture_" + targetSym.getValue().str() + "_" +
165 std::to_string(ordinal++);
166
167 bool exists = false;
168 for (unsigned i = 0, e = mod.getNumPorts(); i < e; ++i) {
169 if (mod.getPort(i).name.getValue() == candidate) {
170 exists = true;
171 break;
172 }
173 }
174 if (!exists)
175 return candidate;
176 }
177}
178
179Value ResolveXMRRefPass::resolveViaBindContext(sv::XMRRefOp xmrRefOp,
180 hw::HierPathOp pathOp,
181 hw::HWModuleOp payloadMod,
182 SymbolTable &symTable,
183 Type targetType) {
184 bindContextDiagnosedFailure = false;
185 auto pathArray = pathOp.getNamepath();
186 auto root = dyn_cast<hw::InnerRefAttr>(pathArray[0]);
187 if (!root)
188 return nullptr;
189
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);
195 if (!boundInst)
196 return;
197
198 if (boundInst.getModuleName() != payloadMod.getModuleName())
199 return;
200
201 if (bindOp.getInstance().getModule() != root.getModule())
202 return;
203
204 if (isConditionallyGuarded(bindOp)) {
205 hasConditionalBind = true;
206 return;
207 }
208
209 bindInstances.push_back(boundInst);
210 });
211
212 if (hasConditionalBind) {
213 xmrRefOp.emitError("cannot resolve XMR through conditionally guarded "
214 "sv.bind; only unconditional sv.bind is supported");
215 bindContextDiagnosedFailure = true;
216 return nullptr;
217 }
218
219 if (bindInstances.empty())
220 return nullptr;
221
222 if (bindInstances.size() != 1) {
223 xmrRefOp.emitError("bind-context XMR requires a unique unconditional "
224 "sv.bind instance");
225 bindContextDiagnosedFailure = true;
226 return nullptr;
227 }
228
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;
233 return nullptr;
234 }
235
236 Value hostValue = boreRecursively(pathArray, 0, hostModule, symTable,
237 pathOp.ref(), targetType);
238 if (!hostValue)
239 return nullptr;
240
241 llvm::SmallPtrSet<Operation *, 8> bindInstSet;
242 for (auto inst : bindInstances)
243 bindInstSet.insert(inst.getOperation());
244
245 auto uses = SymbolTable::getSymbolUses(payloadMod, module);
246 if (!uses)
247 return nullptr;
248
249 for (auto use : *uses) {
250 auto userInst = dyn_cast<hw::InstanceOp>(use.getUser());
251 if (!userInst)
252 continue;
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;
257 return nullptr;
258 }
259 }
260
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());
267
268 OpBuilder b(payloadMod.getContext());
269 auto appended =
270 payloadMod.appendInput(b.getStringAttr(portName), targetType);
271 inputPortIdx = appended.second.getArgNumber();
272 captureInputPortByKey[captureKey] = inputPortIdx;
273
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);
279
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;
286 return nullptr;
287 }
288 operands.push_back(hostValue);
289
290 OpBuilder ib(userInst);
291 auto newInst = hw::InstanceOp::create(
292 ib, userInst.getLoc(), payloadMod, userInst.getInstanceNameAttr(),
293 operands, userInst.getParameters(), userInst.getInnerSymAttr());
294
295 for (unsigned i = 0; i < userInst.getNumResults(); ++i)
296 userInst.getResult(i).replaceAllUsesWith(newInst.getResult(i));
297
298 if (userInst.getDoNotPrint())
299 newInst.setDoNotPrintAttr(UnitAttr::get(newInst.getContext()));
300
301 userInst.erase();
302 }
303
304 return payloadMod.getBodyBlock()->getArgument(inputPortIdx);
305 } else {
306 inputPortIdx = captureIt->second;
307
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;
313 return nullptr;
314 }
315
316 return bindInst.getOperand(inputPortIdx);
317 }
318}
319
320void ResolveXMRRefPass::runOnOperation() {
321 auto module = getOperation();
322 SymbolTable symTable(module);
323 borePortByKey.clear();
324 captureInputPortByKey.clear();
325 nextBoreOrdinalByModule.clear();
326 bool failed = false;
327
328 SmallVector<Operation *> opsToErase;
329
330 module.walk([&](sv::XMRRefOp xmrRefOp) {
331 if (failed)
332 return;
333
334 if (classifyXMRUse(xmrRefOp) != XMRUseMode::ReadOnly) {
335 failed = true;
336 return;
337 }
338
339 auto pathOp = xmrRefOp.getReferencedPath(nullptr);
340 if (!pathOp) {
341 xmrRefOp.emitError("unable to resolve path for XMR reference");
342 failed = true;
343 return;
344 }
345
346 StringAttr leafModName = pathOp.leafMod();
347 Operation *leafMod = symTable.lookup(leafModName);
348 if (!leafMod) {
349 xmrRefOp.emitError("leaf module not found in symbol table");
350 failed = true;
351 return;
352 }
353
354 OpBuilder builder(xmrRefOp);
355 Value resolvedValue = nullptr;
356
357 Type targetType = cast<hw::InOutType>(xmrRefOp.getType()).getElementType();
358
359 ArrayAttr pathArray = pathOp.getNamepath();
360 auto currentModule = xmrRefOp->getParentOfType<hw::HWModuleOp>();
361
362 resolvedValue = boreRecursively(pathArray, 0, currentModule, symTable,
363 pathOp.ref(), targetType);
364
365 if (!resolvedValue && pathOp.isComponent()) {
366 resolvedValue = resolveViaBindContext(xmrRefOp, pathOp, currentModule,
367 symTable, targetType);
368 if (!resolvedValue && bindContextDiagnosedFailure) {
369 failed = true;
370 return;
371 }
372 }
373
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");
380 failed = true;
381 return;
382 }
383 if (pathOp.isComponent()) {
384 xmrRefOp.emitError("unable to resolve component path");
385 failed = true;
386 return;
387 }
388 xmrRefOp.emitError("unsupported XMR reference type");
389 failed = true;
390 return;
391 }
392
393 if (resolvedValue) {
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);
399 }
400 }
401 opsToErase.push_back(xmrRefOp);
402 }
403 });
404
405 if (failed)
406 return signalPassFailure();
407
408 module.walk([&](hw::HierPathOp pathOp) {
409 if (pathOp->use_empty()) {
410 opsToErase.push_back(pathOp);
411 }
412 });
413
414 for (Operation *op : opsToErase)
415 op->erase();
416}
417
418Value ResolveXMRRefPass::boreRecursively(ArrayAttr pathArray, unsigned index,
419 hw::HWModuleOp currentMod,
420 SymbolTable &symTable,
421 StringAttr targetSym,
422 Type targetType) {
423 auto innerRef = cast<hw::InnerRefAttr>(pathArray[index]);
424 StringAttr symName = innerRef.getName();
425
426 if (index == pathArray.size() - 1) {
427 hw::InnerSymbolTable ist(currentMod);
428 auto target = ist.lookup(symName);
429 if (!target)
430 return nullptr;
431
432 if (target.isPort()) {
433 unsigned pIdx = target.getPort();
434 if (currentMod.getPort(pIdx).isOutput()) {
435 auto outOp =
436 cast<hw::OutputOp>(currentMod.getBodyBlock()->getTerminator());
437 unsigned oIdx = 0;
438 for (unsigned i = 0; i < pIdx; ++i)
439 if (currentMod.getPort(i).isOutput())
440 oIdx++;
441 return outOp.getOperand(oIdx);
442 }
443 unsigned aIdx = 0;
444 for (unsigned i = 0; i < pIdx; ++i)
445 if (!currentMod.getPort(i).isOutput())
446 aIdx++;
447 return currentMod.getBodyBlock()->getArgument(aIdx);
448 }
449 return target.getOp()->getNumResults() > 0 ? target.getOp()->getResult(0)
450 : nullptr;
451 }
452
453 hw::InnerSymbolTable currentIST(currentMod);
454 auto instOp =
455 dyn_cast_or_null<hw::InstanceOp>(currentIST.lookup(symName).getOp());
456 if (!instOp)
457 return nullptr;
458
459 Operation *childModOp = symTable.lookup(instOp.getModuleNameAttr().getAttr());
460 if (!childModOp)
461 return nullptr;
462
463 auto nextRef = cast<hw::InnerRefAttr>(pathArray[index + 1]);
464 StringAttr nextSym = nextRef.getName();
465
466 hw::InnerSymbolTable childIST(childModOp);
467 auto nextTarget = childIST.lookup(nextSym);
468
469 if (nextTarget && nextTarget.isPort()) {
470 return getInstancePortValue(instOp, nextTarget.getPort(), childModOp);
471 }
472
473 if (isa<hw::HWModuleExternOp>(childModOp)) {
474 if (lowerBlackBoxInternalToZero) {
475 instOp.emitWarning() << "XMR target '" << nextSym
476 << "' is internal to blackbox. Lowering to 0.";
477 OpBuilder b(instOp);
478 return hw::ConstantOp::create(b, instOp.getLoc(), targetType, 0);
479 }
480 return nullptr;
481 }
482
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())
488 return nullptr;
489 return instOp.getResult(it->second);
490 }
491
492 Value childVal = boreRecursively(pathArray, index + 1, childMod, symTable,
493 targetSym, targetType);
494 if (!childVal)
495 return nullptr;
496
497 std::string portName = createUniqueBoredPortName(childMod, targetSym);
498
499 OpBuilder mb(childMod.getContext());
500 hw::PortInfo newPort;
501 newPort.name = mb.getStringAttr(portName);
502 newPort.dir = hw::ModulePort::Direction::Output;
503 newPort.type = targetType;
504
505 SmallVector<std::pair<unsigned, hw::PortInfo>> newOutputs;
506 newOutputs.push_back({childMod.getNumOutputPorts(), newPort});
507 childMod.modifyPorts({}, newOutputs, {}, {});
508
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;
513
514 auto top = getOperation();
515 auto uses = SymbolTable::getSymbolUses(childMod, top);
516 if (!uses)
517 return nullptr;
518
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);
523
524 Value boredValue;
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());
531
532 for (unsigned i = 0; i < userInst.getNumResults(); ++i)
533 userInst.getResult(i).replaceAllUsesWith(newInst.getResult(i));
534
535 if (userInst.getDoNotPrint())
536 newInst.setDoNotPrintAttr(UnitAttr::get(newInst.getContext()));
537
538 if (userInst == instOp)
539 boredValue = newInst.getResult(resIdx);
540
541 userInst.erase();
542 }
543
544 return boredValue;
545}
546
547Value ResolveXMRRefPass::getInstancePortValue(hw::InstanceOp inst,
548 unsigned portIdx,
549 mlir::Operation *moduleOp) {
550 unsigned outIdx = 0;
551 unsigned inOrInoutIdx = 0;
552
553 for (unsigned i = 0; i < portIdx; ++i) {
554 hw::PortInfo port;
555 if (auto mod = dyn_cast<hw::HWModuleOp>(moduleOp))
556 port = mod.getPort(i);
557 else
558 port = cast<hw::HWModuleExternOp>(moduleOp).getPort(i);
559
560 if (port.isOutput())
561 outIdx++;
562 else
563 inOrInoutIdx++;
564 }
565
566 hw::PortInfo targetPort;
567 if (auto mod = dyn_cast<hw::HWModuleOp>(moduleOp))
568 targetPort = mod.getPort(portIdx);
569 else
570 targetPort = cast<hw::HWModuleExternOp>(moduleOp).getPort(portIdx);
571
572 if (targetPort.isOutput()) {
573 if (outIdx < inst.getNumResults())
574 return inst.getResult(outIdx);
575 } else {
576 if (inOrInoutIdx < inst.getNumOperands())
577 return inst.getOperand(inOrInoutIdx);
578 }
579
580 return nullptr;
581}
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.
create(data_type, value)
Definition hw.py:433
name(self)
Definition hw.py:329
Definition arc.py:1
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition hw.py:1
mlir::Type type
Definition HWTypes.h:31
mlir::StringAttr name
Definition HWTypes.h:30
This holds the name, type, direction of a module's ports.