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