25 #include "mlir/IR/IRMapping.h"
26 #include "mlir/IR/ImplicitLocOpBuilder.h"
27 #include "mlir/IR/Threading.h"
28 #include "mlir/Support/LogicalResult.h"
29 #include "llvm/ADT/DenseMap.h"
30 #include "llvm/ADT/DenseMapInfo.h"
31 #include "llvm/ADT/DepthFirstIterator.h"
32 #include "llvm/ADT/PostOrderIterator.h"
33 #include "llvm/ADT/SmallPtrSet.h"
34 #include "llvm/ADT/TypeSwitch.h"
35 #include "llvm/Support/Format.h"
36 #include "llvm/Support/SHA256.h"
38 using namespace circt;
39 using namespace firrtl;
40 using hw::InnerRefAttr;
46 llvm::raw_ostream &
printHex(llvm::raw_ostream &stream,
47 ArrayRef<uint8_t> bytes) {
49 return stream << format_bytes(bytes, std::nullopt, 32) <<
"\n";
52 llvm::raw_ostream &
printHash(llvm::raw_ostream &stream, llvm::SHA256 &data) {
56 llvm::raw_ostream &
printHash(llvm::raw_ostream &stream, std::string data) {
57 ArrayRef<uint8_t> bytes(
reinterpret_cast<const uint8_t *
>(
data.c_str()),
90 nonessentialAttributes.insert(
StringAttr::get(context,
"portAnnotations"));
92 nonessentialAttributes.insert(
StringAttr::get(context,
"portLocations"));
117 : constants(constants){};
119 std::pair<std::array<uint8_t, 32>, SmallVector<StringAttr>>
123 sha.update(group.str());
124 auto hash = sha.final();
125 return {hash, referredModuleNames};
130 auto *
addr =
reinterpret_cast<const uint8_t *
>(&pointer);
131 sha.update(ArrayRef<uint8_t>(
addr,
sizeof pointer));
135 auto *
addr =
reinterpret_cast<const uint8_t *
>(&value);
136 sha.update(ArrayRef<uint8_t>(
addr,
sizeof value));
143 update(type.getTypeID());
144 for (
auto &element : type.getElements()) {
145 update(element.isFlip);
146 update(element.type);
152 if (
auto bundle = type_dyn_cast<BundleType>(type))
153 return update(bundle);
154 update(type.getAsOpaquePointer());
158 auto size = indices.size();
159 indices[address] = size;
162 void update(BlockArgument arg) { record(arg.getAsOpaquePointer()); }
165 record(result.getAsOpaquePointer());
170 if (
auto objectOp = dyn_cast<ObjectOp>(result.getOwner())) {
171 referredModuleNames.push_back(objectOp.getType().getNameAttr().getAttr());
175 update(result.getType());
180 auto it = indices.find(operand.get().getAsOpaquePointer());
181 assert(it != indices.end() &&
"op should have been previously hashed");
185 void update(Operation *op, hw::InnerSymAttr attr) {
186 for (
auto props : attr)
187 innerSymTargets[props.getName()] =
191 void update(Value value, hw::InnerSymAttr attr) {
192 for (
auto props : attr)
193 innerSymTargets[props.getName()] =
194 SymbolTarget{indices[value.getAsOpaquePointer()], props.getFieldID()};
198 update(target.
index);
204 auto it = innerSymTargets.find(attr.getName());
205 assert(it != innerSymTargets.end() &&
206 "inner symbol should have been previously hashed");
207 update(attr.getTypeID());
213 void update(Operation *op, DictionaryAttr dict) {
214 for (
auto namedAttr : dict) {
215 auto name = namedAttr.getName();
216 auto value = namedAttr.getValue();
219 bool isClassPortNames =
220 isa<ClassLike>(op) && name == constants.portNamesAttr;
221 if (constants.nonessentialAttributes.contains(name) && !isClassPortNames)
225 if (name == constants.portTypesAttr) {
226 auto portTypes = cast<ArrayAttr>(value).getAsValueRange<TypeAttr>();
227 for (
auto type : portTypes)
233 if (name == constants.portSymsAttr) {
234 if (op->getNumRegions() != 1)
236 auto ®ion = op->getRegion(0);
237 if (region.getBlocks().empty())
239 auto *block = ®ion.front();
240 auto syms = cast<ArrayAttr>(value).getAsRange<hw::InnerSymAttr>();
243 for (
auto [arg, sym] : llvm::zip_equal(block->getArguments(), syms))
247 if (name == constants.innerSymAttr) {
248 auto innerSym = cast<hw::InnerSymAttr>(value);
249 update(op, innerSym);
258 if (isa<InstanceOp>(op) && name == constants.moduleNameAttr) {
259 referredModuleNames.push_back(cast<FlatSymbolRefAttr>(value).
getAttr());
264 update(name.getAsOpaquePointer());
268 if (isa<DistinctAttr>(value))
272 if (
auto innerRef = dyn_cast<hw::InnerRefAttr>(value))
275 update(value.getAsOpaquePointer());
281 for (
auto arg : block.getArguments())
284 for (
auto &op : block)
290 update(name.getAsOpaquePointer());
296 update(op->getName());
297 update(op, op->getAttrDictionary());
299 for (
auto &operand : op->getOpOperands())
303 update(op->getNumRegions());
304 for (
auto ®ion : op->getRegions())
305 for (
auto &block : region.getBlocks())
308 for (
auto result : op->getResults())
338 : instanceGraph(instanceGraph) {
342 nonessentialAttributes.insert(
StringAttr::get(context,
"annotations"));
344 nonessentialAttributes.insert(
StringAttr::get(context,
"portAnnotations"));
348 nonessentialAttributes.insert(
StringAttr::get(context,
"portLocations"));
354 ModuleData(
const hw::InnerSymbolTable &a,
const hw::InnerSymbolTable &b)
357 const hw::InnerSymbolTable &
a;
358 const hw::InnerSymbolTable &
b;
362 SmallString<64> buffer;
363 llvm::raw_svector_ostream os(buffer);
364 if (
auto integerAttr = dyn_cast<IntegerAttr>(attr)) {
366 if (integerAttr.getType().isSignlessInteger())
367 integerAttr.getValue().toStringUnsigned(buffer, 16);
369 integerAttr.getAPSInt().toString(buffer, 16);
373 return std::string(buffer);
377 LogicalResult
check(InFlightDiagnostic &diag,
const Twine &message,
378 Operation *a, BundleType aType, Operation *b,
380 if (aType.getNumElements() != bType.getNumElements()) {
381 diag.attachNote(a->getLoc())
382 << message <<
" bundle type has different number of elements";
383 diag.attachNote(b->getLoc()) <<
"second operation here";
387 for (
auto elementPair :
388 llvm::zip(aType.getElements(), bType.getElements())) {
389 auto aElement = std::get<0>(elementPair);
390 auto bElement = std::get<1>(elementPair);
391 if (aElement.isFlip != bElement.isFlip) {
392 diag.attachNote(a->getLoc()) << message <<
" bundle element "
393 << aElement.name <<
" flip does not match";
394 diag.attachNote(b->getLoc()) <<
"second operation here";
398 if (failed(check(diag,
399 "bundle element \'" + aElement.name.getValue() +
"'", a,
400 aElement.type, b, bElement.type)))
406 LogicalResult
check(InFlightDiagnostic &diag,
const Twine &message,
407 Operation *a, Type aType, Operation *b, Type bType) {
410 if (
auto aBundleType = type_dyn_cast<BundleType>(aType))
411 if (
auto bBundleType = type_dyn_cast<BundleType>(bType))
412 return check(diag, message, a, aBundleType, b, bBundleType);
413 if (type_isa<RefType>(aType) && type_isa<RefType>(bType) &&
415 diag.attachNote(a->getLoc())
416 << message <<
", has a RefType with a different base type "
417 << type_cast<RefType>(aType).getType()
418 <<
" in the same position of the two modules marked as 'must dedup'. "
419 "(This may be due to Grand Central Taps or Views being different "
420 "between the two modules.)";
421 diag.attachNote(b->getLoc())
422 <<
"the second module has a different base type "
423 << type_cast<RefType>(bType).getType();
426 diag.attachNote(a->getLoc())
427 << message <<
" types don't match, first type is " << aType;
428 diag.attachNote(b->getLoc()) <<
"second type is " << bType;
433 Block &aBlock, Operation *b, Block &bBlock) {
436 auto portNames = a->getAttrOfType<ArrayAttr>(
"portNames");
438 auto emitMissingPort = [&](Value existsVal, Operation *opExists,
439 Operation *opDoesNotExist) {
441 auto portNames = opExists->getAttrOfType<ArrayAttr>(
"portNames");
443 if (
auto portNameAttr = dyn_cast<StringAttr>(portNames[portNo]))
444 portName = portNameAttr.getValue();
445 if (type_isa<RefType>(existsVal.getType())) {
446 diag.attachNote(opExists->getLoc())
447 <<
" contains a RefType port named '" + portName +
448 "' that only exists in one of the modules (can be due to "
449 "difference in Grand Central Tap or View of two modules "
450 "marked with must dedup)";
451 diag.attachNote(opDoesNotExist->getLoc())
452 <<
"second module to be deduped that does not have the RefType "
455 diag.attachNote(opExists->getLoc())
456 <<
"port '" + portName +
"' only exists in one of the modules";
457 diag.attachNote(opDoesNotExist->getLoc())
458 <<
"second module to be deduped that does not have the port";
464 llvm::zip_longest(aBlock.getArguments(), bBlock.getArguments())) {
465 auto &aArg = std::get<0>(argPair);
466 auto &bArg = std::get<1>(argPair);
467 if (aArg.has_value() && bArg.has_value()) {
472 if (
auto portNameAttr = dyn_cast<StringAttr>(portNames[portNo]))
473 portName = portNameAttr.getValue();
476 if (failed(check(diag,
"module port '" + portName +
"'", a,
477 aArg->getType(), b, bArg->getType())))
479 data.map.map(aArg.value(), bArg.value());
483 if (!aArg.has_value())
485 return emitMissingPort(aArg.has_value() ? aArg.value() : bArg.value(), a,
490 auto aIt = aBlock.begin();
491 auto aEnd = aBlock.end();
492 auto bIt = bBlock.begin();
493 auto bEnd = bBlock.end();
494 while (aIt != aEnd && bIt != bEnd)
495 if (failed(check(diag,
data, &*aIt++, &*bIt++)))
498 diag.attachNote(aIt->getLoc()) <<
"first block has more operations";
499 diag.attachNote(b->getLoc()) <<
"second block here";
503 diag.attachNote(bIt->getLoc()) <<
"second block has more operations";
504 diag.attachNote(a->getLoc()) <<
"first block here";
511 Region &aRegion, Operation *b, Region &bRegion) {
512 auto aIt = aRegion.begin();
513 auto aEnd = aRegion.end();
514 auto bIt = bRegion.begin();
515 auto bEnd = bRegion.end();
518 while (aIt != aEnd && bIt != bEnd)
519 if (failed(check(diag,
data, a, *aIt++, b, *bIt++)))
521 if (aIt != aEnd || bIt != bEnd) {
522 diag.attachNote(a->getLoc())
523 <<
"operation regions have different number of blocks";
524 diag.attachNote(b->getLoc()) <<
"second operation here";
530 LogicalResult
check(InFlightDiagnostic &diag, Operation *a,
531 mlir::DenseBoolArrayAttr aAttr, Operation *b,
532 mlir::DenseBoolArrayAttr bAttr) {
535 auto portNames = a->getAttrOfType<ArrayAttr>(
"portNames");
536 for (
unsigned i = 0, e = aAttr.size(); i < e; ++i) {
537 auto aDirection = aAttr[i];
538 auto bDirection = bAttr[i];
539 if (aDirection != bDirection) {
540 auto ¬e = diag.attachNote(a->getLoc()) <<
"module port ";
542 note <<
"'" << cast<StringAttr>(portNames[i]).getValue() <<
"'";
545 note <<
" directions don't match, first direction is '"
547 diag.attachNote(b->getLoc()) <<
"second direction is '"
556 DictionaryAttr aDict, Operation *b,
557 DictionaryAttr bDict) {
562 DenseSet<Attribute> seenAttrs;
563 for (
auto namedAttr : aDict) {
564 auto attrName = namedAttr.getName();
565 if (nonessentialAttributes.contains(attrName))
568 auto aAttr = namedAttr.getValue();
569 auto bAttr = bDict.get(attrName);
571 diag.attachNote(a->getLoc())
572 <<
"second operation is missing attribute " << attrName;
573 diag.attachNote(b->getLoc()) <<
"second operation here";
577 if (isa<hw::InnerRefAttr>(aAttr) && isa<hw::InnerRefAttr>(bAttr)) {
578 auto bRef = cast<hw::InnerRefAttr>(bAttr);
579 auto aRef = cast<hw::InnerRefAttr>(aAttr);
581 auto aTarget =
data.a.lookup(aRef.getName());
582 auto bTarget =
data.b.lookup(bRef.getName());
583 if (!aTarget || !bTarget)
584 diag.attachNote(a->getLoc())
585 <<
"malformed ir, possibly violating use-before-def";
587 diag.attachNote(a->getLoc())
588 <<
"operations have different targets, first operation has "
590 diag.attachNote(b->getLoc()) <<
"second operation has " << bTarget;
593 if (aTarget.isPort()) {
595 if (!bTarget.isPort() || aTarget.getPort() != bTarget.getPort())
599 if (!bTarget.isOpOnly() ||
600 aTarget.getOp() !=
data.map.lookup(bTarget.getOp()))
603 if (aTarget.getField() != bTarget.getField())
605 }
else if (attrName == portDirectionsAttr) {
608 if (failed(check(diag, a, cast<mlir::DenseBoolArrayAttr>(aAttr), b,
609 cast<mlir::DenseBoolArrayAttr>(bAttr))))
611 }
else if (isa<DistinctAttr>(aAttr) && isa<DistinctAttr>(bAttr)) {
614 }
else if (aAttr != bAttr) {
615 diag.attachNote(a->getLoc())
616 <<
"first operation has attribute '" << attrName.getValue()
617 <<
"' with value " << prettyPrint(aAttr);
618 diag.attachNote(b->getLoc())
619 <<
"second operation has value " << prettyPrint(bAttr);
622 seenAttrs.insert(attrName);
624 if (aDict.getValue().size() != bDict.getValue().size()) {
625 for (
auto namedAttr : bDict) {
626 auto attrName = namedAttr.getName();
629 if (nonessentialAttributes.contains(attrName) ||
630 seenAttrs.contains(attrName))
633 diag.attachNote(a->getLoc())
634 <<
"first operation is missing attribute " << attrName;
635 diag.attachNote(b->getLoc()) <<
"second operation here";
643 LogicalResult
check(InFlightDiagnostic &diag, FInstanceLike a,
645 auto aName = a.getReferencedModuleNameAttr();
646 auto bName = b.getReferencedModuleNameAttr();
653 auto aModule = instanceGraph.lookup(aName)->getModule();
654 auto bModule = instanceGraph.lookup(bName)->getModule();
656 diag.attachNote(std::nullopt)
657 <<
"in instance " << a.getInstanceNameAttr() <<
" of " << aName
658 <<
", and instance " << b.getInstanceNameAttr() <<
" of " << bName;
659 check(diag, aModule, bModule);
667 if (a->getName() != b->getName()) {
668 diag.attachNote(a->getLoc()) <<
"first operation is a " << a->getName();
669 diag.attachNote(b->getLoc()) <<
"second operation is a " << b->getName();
675 if (
auto aInst = dyn_cast<FInstanceLike>(a)) {
676 auto bInst = cast<FInstanceLike>(b);
677 if (failed(check(diag, aInst, bInst)))
682 if (a->getNumResults() != b->getNumResults()) {
683 diag.attachNote(a->getLoc())
684 <<
"operations have different number of results";
685 diag.attachNote(b->getLoc()) <<
"second operation here";
688 for (
auto resultPair : llvm::zip(a->getResults(), b->getResults())) {
689 auto &aValue = std::get<0>(resultPair);
690 auto &bValue = std::get<1>(resultPair);
691 if (failed(check(diag,
"operation result", a, aValue.getType(), b,
694 data.map.map(aValue, bValue);
698 if (a->getNumOperands() != b->getNumOperands()) {
699 diag.attachNote(a->getLoc())
700 <<
"operations have different number of operands";
701 diag.attachNote(b->getLoc()) <<
"second operation here";
704 for (
auto operandPair : llvm::zip(a->getOperands(), b->getOperands())) {
705 auto &aValue = std::get<0>(operandPair);
706 auto &bValue = std::get<1>(operandPair);
707 if (bValue !=
data.map.lookup(aValue)) {
708 diag.attachNote(a->getLoc())
709 <<
"operations use different operands, first operand is '"
714 diag.attachNote(b->getLoc())
715 <<
"second operand is '"
719 <<
"', but should have been '"
730 if (a->getNumRegions() != b->getNumRegions()) {
731 diag.attachNote(a->getLoc())
732 <<
"operations have different number of regions";
733 diag.attachNote(b->getLoc()) <<
"second operation here";
736 for (
auto regionPair : llvm::zip(a->getRegions(), b->getRegions())) {
737 auto &aRegion = std::get<0>(regionPair);
738 auto &bRegion = std::get<1>(regionPair);
739 if (failed(check(diag,
data, a, aRegion, b, bRegion)))
744 if (failed(check(diag,
data, a, a->getAttrDictionary(), b,
745 b->getAttrDictionary())))
751 void check(InFlightDiagnostic &diag, Operation *a, Operation *b) {
752 hw::InnerSymbolTable aTable(a);
753 hw::InnerSymbolTable bTable(b);
758 diag.attachNote(a->getLoc()) <<
"module marked NoDedup";
762 diag.attachNote(b->getLoc()) <<
"module marked NoDedup";
773 if (aGroup != bGroup) {
775 diag.attachNote(b->getLoc())
776 <<
"module is in dedup group '" << bGroup.str() <<
"'";
778 diag.attachNote(b->getLoc()) <<
"module is not part of a dedup group";
781 diag.attachNote(a->getLoc())
782 <<
"module is in dedup group '" << aGroup.str() <<
"'";
784 diag.attachNote(a->getLoc()) <<
"module is not part of a dedup group";
788 if (failed(check(diag,
data, a, b)))
790 diag.attachNote(a->getLoc()) <<
"first module here";
791 diag.attachNote(b->getLoc()) <<
"second module here";
814 static Location
mergeLoc(MLIRContext *context, Location to, Location from) {
816 llvm::SmallSetVector<Location, 4> decomposedLocs;
818 unsigned seenFIR = 0;
819 for (
auto loc : {to, from}) {
822 if (
auto fusedLoc = dyn_cast<FusedLoc>(loc)) {
825 for (
auto loc : fusedLoc.getLocations()) {
826 if (FileLineColLoc fileLoc = dyn_cast<FileLineColLoc>(loc)) {
827 if (fileLoc.getFilename().strref().ends_with(
".fir")) {
833 decomposedLocs.insert(loc);
839 if (FileLineColLoc fileLoc = dyn_cast<FileLineColLoc>(loc)) {
840 if (fileLoc.getFilename().strref().ends_with(
".fir")) {
847 if (!isa<UnknownLoc>(loc))
848 decomposedLocs.insert(loc);
851 auto locs = decomposedLocs.getArrayRef();
857 if (locs.size() == 1)
868 NLATable *nlaTable, CircuitOp circuit)
869 : context(circuit->getContext()), instanceGraph(instanceGraph),
870 symbolTable(symbolTable), nlaTable(nlaTable),
871 nlaBlock(circuit.getBodyBlock()),
872 nonLocalString(StringAttr::
get(context,
"circt.nonlocal")),
873 classString(StringAttr::
get(context,
"class")) {
875 for (
auto nla : circuit.getOps<hw::HierPathOp>())
876 nlaCache[nla.getNamepathAttr()] = nla.getSymNameAttr();
883 void dedup(FModuleLike toModule, FModuleLike fromModule) {
889 SmallVector<Attribute> newLocs;
890 for (
auto [toLoc, fromLoc] : llvm::zip(toModule.getPortLocations(),
891 fromModule.getPortLocations())) {
892 if (toLoc == fromLoc)
893 newLocs.push_back(toLoc);
895 newLocs.push_back(
mergeLoc(context, cast<LocationAttr>(toLoc),
896 cast<LocationAttr>(fromLoc)));
898 toModule->setAttr(
"portLocations",
ArrayAttr::get(context, newLocs));
901 mergeOps(renameMap, toModule, toModule, fromModule, fromModule);
907 if (
auto to = dyn_cast<FModuleOp>(*toModule))
908 rewriteModuleNLAs(renameMap, to, cast<FModuleOp>(*fromModule));
910 rewriteExtModuleNLAs(renameMap, toModule.getModuleNameAttr(),
911 fromModule.getModuleNameAttr());
913 replaceInstances(toModule, fromModule);
920 recordAnnotations(module);
922 for (
unsigned i = 0, e =
getNumPorts(module); i < e; ++i)
925 module->walk([&](Operation *op) { recordAnnotations(op); });
931 return moduleNamespaces.try_emplace(module, cast<FModuleLike>(module))
939 if (
auto nlaRef = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal"))
940 targetMap[nlaRef.getAttr()].insert(target);
949 auto mem = dyn_cast<MemOp>(op);
954 for (
unsigned i = 0, e = mem->getNumResults(); i < e; ++i)
963 instanceGraph[::cast<igraph::ModuleOpInterface>(fromModule)];
964 auto *toNode = instanceGraph[toModule];
966 for (
auto *oldInstRec : llvm::make_early_inc_range(fromNode->uses())) {
967 auto inst = oldInstRec->getInstance();
968 if (
auto instOp = dyn_cast<InstanceOp>(*inst)) {
969 instOp.setModuleNameAttr(toModuleRef);
970 instOp.setPortNamesAttr(toModule.getPortNamesAttr());
971 }
else if (
auto objectOp = dyn_cast<ObjectOp>(*inst)) {
972 auto classLike = cast<ClassLike>(*toNode->getModule());
974 objectOp.getResult().setType(classType);
976 oldInstRec->getParent()->addInstance(inst, toNode);
979 instanceGraph.erase(fromNode);
988 SmallVector<FlatSymbolRefAttr>
989 createNLAs(Operation *fromModule, ArrayRef<Attribute> baseNamepath,
990 SymbolTable::Visibility vis = SymbolTable::Visibility::Private) {
993 SmallVector<Attribute> namepath = {
nullptr};
994 namepath.append(baseNamepath.begin(), baseNamepath.end());
996 auto loc = fromModule->getLoc();
997 auto *fromNode = instanceGraph[cast<igraph::ModuleOpInterface>(fromModule)];
998 SmallVector<FlatSymbolRefAttr> nlas;
999 for (
auto *instanceRecord : fromNode->uses()) {
1000 auto parent = cast<FModuleOp>(*instanceRecord->getParent()->getModule());
1001 auto inst = instanceRecord->getInstance();
1005 auto &cacheEntry = nlaCache[arrayAttr];
1007 auto nla = OpBuilder::atBlockBegin(nlaBlock).create<hw::HierPathOp>(
1008 loc,
"nla", arrayAttr);
1010 symbolTable.insert(nla);
1012 cacheEntry = nla.getNameAttr();
1013 nla.setVisibility(vis);
1014 nlaTable->addNLA(nla);
1017 nlas.push_back(nlaRef);
1025 SmallVector<FlatSymbolRefAttr>
1027 SymbolTable::Visibility vis = SymbolTable::Visibility::Private) {
1035 Annotation anno, ArrayRef<NamedAttribute> attributes,
1036 unsigned nonLocalIndex,
1037 SmallVectorImpl<Annotation> &newAnnotations) {
1038 SmallVector<NamedAttribute> mutableAttributes(attributes.begin(),
1040 for (
auto &nla : nlas) {
1042 mutableAttributes[nonLocalIndex].setValue(nla);
1043 auto dict = DictionaryAttr::getWithSorted(context, mutableAttributes);
1046 newAnnotations.push_back(anno);
1055 targetMap.erase(nla.getNameAttr());
1056 nlaTable->erase(nla);
1057 nlaCache.erase(nla.getNamepathAttr());
1058 symbolTable.erase(nla);
1064 FModuleOp fromModule) {
1065 auto toName = toModule.getNameAttr();
1066 auto fromName = fromModule.getNameAttr();
1069 auto moduleNLAs = nlaTable->lookup(fromModule.getNameAttr()).vec();
1071 nlaTable->renameModuleAndInnerRef(toName, fromName, renameMap);
1074 for (
auto nla : moduleNLAs) {
1075 auto elements = nla.getNamepath().getValue();
1077 if (nla.root() != toName)
1080 SmallVector<Attribute> namepath(elements.begin(), elements.end());
1081 auto nlaRefs = createNLAs(fromModule, namepath, nla.getVisibility());
1083 auto &set = targetMap[nla.getSymNameAttr()];
1084 SmallVector<AnnoTarget> targets(set.begin(), set.end());
1086 for (
auto target : targets) {
1089 SmallVector<Annotation> newAnnotations;
1090 for (
auto anno : target.getAnnotations()) {
1092 auto [it, found] = mlir::impl::findAttrSorted(
1093 anno.begin(), anno.end(), nonLocalString);
1096 if (!found || cast<FlatSymbolRefAttr>(it->getValue()).getAttr() !=
1097 nla.getSymNameAttr()) {
1098 newAnnotations.push_back(anno);
1101 auto nonLocalIndex = std::distance(anno.begin(), it);
1103 cloneAnnotation(nlaRefs, anno,
1104 ArrayRef<NamedAttribute>(anno.begin(), anno.end()),
1105 nonLocalIndex, newAnnotations);
1110 target.setAnnotations(annotations);
1112 for (
auto nla : nlaRefs)
1113 targetMap[nla.getAttr()].insert(target);
1125 FModuleOp fromModule) {
1126 addAnnotationContext(renameMap, toModule, toModule);
1127 addAnnotationContext(renameMap, toModule, fromModule);
1133 StringAttr fromName) {
1134 nlaTable->renameModuleAndInnerRef(toName, fromName, renameMap);
1142 SmallVectorImpl<Annotation> &newAnnotations) {
1145 SmallVector<NamedAttribute> attributes;
1146 int nonLocalIndex = -1;
1147 for (
const auto &val : llvm::enumerate(anno)) {
1148 auto attr = val.value();
1150 auto compare = attr.getName().compare(nonLocalString);
1151 assert(compare != 0 &&
"should not pass non-local annotations here");
1155 nonLocalIndex = val.index();
1156 attributes.push_back(NamedAttribute(nonLocalString, nonLocalString));
1161 attributes.push_back(attr);
1163 if (nonLocalIndex == -1) {
1165 nonLocalIndex = attributes.size();
1166 attributes.push_back(NamedAttribute(nonLocalString, nonLocalString));
1169 attributes.append(anno.
begin() + nonLocalIndex, anno.
end());
1173 auto nlaRefs = createNLAs(toModuleName, fromModule);
1174 for (
auto nla : nlaRefs)
1175 targetMap[nla.getAttr()].insert(to);
1178 cloneAnnotation(nlaRefs, anno, attributes, nonLocalIndex, newAnnotations);
1184 SmallVectorImpl<Annotation> &newAnnotations,
1185 SmallPtrSetImpl<Attribute> &dontTouches) {
1186 for (
auto anno : annos) {
1190 anno.removeMember(
"circt.nonlocal");
1191 auto [it, inserted] = dontTouches.insert(anno.getAttr());
1193 newAnnotations.push_back(anno);
1198 if (
auto nla = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
1199 newAnnotations.push_back(anno);
1200 targetMap[nla.getAttr()].insert(to);
1204 makeAnnotationNonLocal(toModule.getModuleNameAttr(), to, fromModule, anno,
1215 SmallVector<Annotation> newAnnotations;
1219 llvm::SmallPtrSet<Attribute, 4> dontTouches;
1223 copyAnnotations(toModule, to, toModule, toAnnos, newAnnotations,
1225 copyAnnotations(toModule, to, fromModule, fromAnnos, newAnnotations,
1229 if (!newAnnotations.empty())
1235 FModuleLike fromModule, Operation *from) {
1241 if (toModule == to) {
1243 for (
unsigned i = 0, e =
getNumPorts(toModule); i < e; ++i)
1248 }
else if (
auto toMem = dyn_cast<MemOp>(to)) {
1250 auto fromMem = cast<MemOp>(from);
1251 for (
unsigned i = 0, e = toMem.getNumResults(); i < e; ++i)
1263 Operation *to, FModuleLike fromModule,
1270 return getNamespace(toModule);
1272 renameMap[fromSym] = toSym;
1276 auto fromPortSyms = from->getAttrOfType<ArrayAttr>(
"portSyms");
1277 if (!fromPortSyms || fromPortSyms.empty())
1280 auto &moduleNamespace = getNamespace(toModule);
1281 auto portCount = fromPortSyms.size();
1282 auto portNames = to->getAttrOfType<ArrayAttr>(
"portNames");
1283 auto toPortSyms = to->getAttrOfType<ArrayAttr>(
"portSyms");
1287 SmallVector<Attribute> newPortSyms;
1288 if (toPortSyms.empty())
1289 newPortSyms.assign(portCount, hw::InnerSymAttr());
1291 newPortSyms.assign(toPortSyms.begin(), toPortSyms.end());
1293 for (
unsigned portNo = 0; portNo < portCount; ++portNo) {
1295 if (!fromPortSyms[portNo])
1297 auto fromSym = cast<hw::InnerSymAttr>(fromPortSyms[portNo]);
1300 hw::InnerSymAttr toSym;
1301 if (!newPortSyms[portNo]) {
1303 StringRef symName =
"inner_sym";
1305 symName = cast<StringAttr>(portNames[portNo]).getValue();
1309 newPortSyms[portNo] = toSym;
1311 toSym = cast<hw::InnerSymAttr>(newPortSyms[portNo]);
1314 renameMap[fromSym.getSymName()] = toSym.getSymName();
1318 cast<FModuleLike>(to).setPortSymbols(newPortSyms);
1324 FModuleLike fromModule, Operation *from) {
1326 if (to->getLoc() != from->getLoc())
1327 to->setLoc(
mergeLoc(context, to->getLoc(), from->getLoc()));
1330 for (
auto regions : llvm::zip(to->getRegions(), from->getRegions()))
1331 mergeRegions(renameMap, toModule, std::get<0>(regions), fromModule,
1332 std::get<1>(regions));
1335 recordSymRenames(renameMap, toModule, to, fromModule, from);
1338 mergeAnnotations(toModule, to, fromModule, from);
1343 FModuleLike fromModule, Block &fromBlock) {
1345 for (
auto [toArg, fromArg] :
1346 llvm::zip(toBlock.getArguments(), fromBlock.getArguments()))
1347 if (toArg.getLoc() != fromArg.getLoc())
1348 toArg.setLoc(
mergeLoc(context, toArg.getLoc(), fromArg.getLoc()));
1350 for (
auto ops : llvm::zip(toBlock, fromBlock))
1351 mergeOps(renameMap, toModule, &std::get<0>(ops), fromModule,
1357 Region &toRegion, FModuleLike fromModule,
1358 Region &fromRegion) {
1359 for (
auto blocks : llvm::zip(toRegion, fromRegion))
1360 mergeBlocks(renameMap, toModule, std::get<0>(blocks), fromModule,
1361 std::get<1>(blocks));
1375 DenseMap<Attribute, llvm::SmallDenseSet<AnnoTarget>>
targetMap;
1399 SmallVector<Attribute> newPortTypes;
1400 bool anyDifferences =
false;
1403 for (
size_t i = 0, e = classOp.getNumPorts(); i < e; ++i) {
1406 auto portClassType = dyn_cast<ClassType>(classOp.getPortType(i));
1407 if (!portClassType) {
1408 newPortTypes.push_back(classOp.getPortTypeAttr(i));
1413 Type newPortClassType;
1414 BlockArgument portArg = classOp.getArgument(i);
1415 for (
auto &use : portArg.getUses()) {
1416 if (
auto propassign = dyn_cast<PropAssignOp>(use.getOwner())) {
1417 Type sourceType = propassign.getSrc().getType();
1418 if (propassign.getDest() == use.get() && sourceType != portClassType) {
1420 if (newPortClassType) {
1421 assert(newPortClassType == sourceType &&
1422 "expected all references to be of the same type");
1426 newPortClassType = sourceType;
1433 if (!newPortClassType) {
1434 newPortTypes.push_back(classOp.getPortTypeAttr(i));
1440 classOp.getArgument(i).setType(newPortClassType);
1442 anyDifferences =
true;
1447 classOp.setPortTypes(newPortTypes);
1449 return anyDifferences;
1456 objectOp.getResult().setType(newClassType);
1464 auto dstType = dst.getType();
1465 auto srcType = src.getType();
1466 if (dstType == srcType) {
1472 auto dstBundle = type_cast<BundleType>(dstType);
1473 auto srcBundle = type_cast<BundleType>(srcType);
1474 for (
unsigned i = 0; i < dstBundle.getNumElements(); ++i) {
1475 auto dstField =
builder.create<SubfieldOp>(dst, i);
1476 auto srcField =
builder.create<SubfieldOp>(src, i);
1477 if (dstBundle.getElement(i).isFlip) {
1478 std::swap(srcBundle, dstBundle);
1479 std::swap(srcField, dstField);
1489 for (
auto *node : instanceGraph) {
1490 auto module = cast<FModuleLike>(*node->getModule());
1493 bool shouldFixupObjects =
false;
1494 auto classOp = dyn_cast<ClassOp>(module.getOperation());
1498 for (
auto *instRec : node->uses()) {
1501 if (shouldFixupObjects) {
1503 classOp.getInstanceType());
1508 auto inst = instRec->getInstance<InstanceOp>();
1512 ImplicitLocOpBuilder
builder(inst.getLoc(), inst->getContext());
1513 builder.setInsertionPointAfter(inst);
1514 for (
size_t i = 0, e =
getNumPorts(module); i < e; ++i) {
1515 auto result = inst.getResult(i);
1516 auto newType = module.getPortType(i);
1517 auto oldType = result.getType();
1519 if (newType == oldType)
1524 builder.create<WireOp>(oldType, inst.getPortName(i)).getResult();
1525 result.replaceAllUsesWith(wire);
1526 result.setType(newType);
1542 struct DenseMapInfo<ModuleInfo> {
1544 std::array<uint8_t, 32> key;
1545 std::fill(key.begin(), key.end(), ~0);
1546 return {key, DenseMapInfo<mlir::ArrayAttr>::getEmptyKey()};
1550 std::array<uint8_t, 32> key;
1551 std::fill(key.begin(), key.end(), ~0 - 1);
1552 return {key, DenseMapInfo<mlir::ArrayAttr>::getTombstoneKey()};
1559 std::memcpy(&hash, val.structuralHash.data(),
sizeof(
unsigned));
1562 return llvm::hash_combine(hash, val.referredModuleNames);
1565 static bool isEqual(
const ModuleInfo &lhs,
const ModuleInfo &rhs) {
1566 return lhs.structuralHash == rhs.structuralHash &&
1567 lhs.referredModuleNames == rhs.referredModuleNames;
1577 class DedupPass :
public DedupBase<DedupPass> {
1578 void runOnOperation()
override {
1579 auto *context = &getContext();
1580 auto circuit = getOperation();
1581 auto &instanceGraph = getAnalysis<InstanceGraph>();
1582 auto *nlaTable = &getAnalysis<NLATable>();
1583 auto &symbolTable = getAnalysis<SymbolTable>();
1584 Deduper deduper(instanceGraph, symbolTable, nlaTable, circuit);
1586 auto anythingChanged =
false;
1595 llvm::DenseMap<ModuleInfo, Operation *> moduleInfoToModule;
1600 DenseMap<Attribute, StringAttr> dedupMap;
1605 SmallVector<FModuleLike, 0> modules(
1606 llvm::map_range(llvm::post_order(&instanceGraph), [](
auto *node) {
1607 return cast<FModuleLike>(*node->getModule());
1610 SmallVector<std::optional<
1611 std::pair<std::array<uint8_t, 32>, SmallVector<StringAttr>>>>
1612 hashesAndModuleNames(modules.size());
1616 auto result = mlir::failableParallelForEach(
1617 context, llvm::seq(modules.size()), [&](
unsigned idx) {
1618 auto module = modules[idx];
1619 AnnotationSet annotations(module);
1621 if (annotations.hasAnnotation(noDedupClass))
1625 if (llvm::any_of(module.getPorts(), [&](PortInfo port) {
1626 return type_isa<RefType>(port.type) && port.isInput();
1631 if (
auto ext = dyn_cast<FExtModuleOp>(*module);
1632 ext && !ext.getDefname().has_value())
1638 if (!module.isPrivate() ||
1639 (!module.canDiscardOnUseEmpty() && !isa<ClassLike>(*module))) {
1643 llvm::SmallSetVector<StringAttr, 1> groups;
1644 for (
auto annotation : annotations) {
1645 if (annotation.getClass() == dedupGroupClass)
1646 groups.insert(annotation.getMember<StringAttr>(
"group"));
1648 if (groups.size() > 1) {
1649 module.emitError(
"module belongs to multiple dedup groups: ")
1653 auto dedupGroup = groups.empty() ? StringAttr() : groups.front();
1657 hashesAndModuleNames[idx] =
1658 hasher.getHashAndModuleNames(module, dedupGroup);
1662 if (result.failed())
1663 return signalPassFailure();
1665 for (
auto [i, module] : llvm::enumerate(modules)) {
1666 auto moduleName = module.getModuleNameAttr();
1667 auto &hashAndModuleNamesOpt = hashesAndModuleNames[i];
1669 if (!hashAndModuleNamesOpt) {
1674 dedupMap[moduleName] = moduleName;
1679 SmallVector<mlir::Attribute> names;
1680 for (
auto oldModuleName : hashAndModuleNamesOpt->second) {
1681 auto newModuleName = dedupMap[oldModuleName];
1682 names.push_back(newModuleName);
1686 ModuleInfo moduleInfo{hashAndModuleNamesOpt->first,
1690 auto it = moduleInfoToModule.find(moduleInfo);
1691 if (it != moduleInfoToModule.end()) {
1692 auto original = cast<FModuleLike>(it->second);
1694 dedupMap[moduleName] = original.getModuleNameAttr();
1695 deduper.dedup(original, module);
1697 anythingChanged =
true;
1701 deduper.record(module);
1703 dedupMap[moduleName] = moduleName;
1705 moduleInfoToModule[moduleInfo] = module;
1713 auto failed =
false;
1715 auto parseModule = [&](Attribute path) -> StringAttr {
1718 auto [_, rhs] = cast<StringAttr>(path).getValue().split(
'|');
1724 auto getLead = [&](StringAttr module) -> StringAttr {
1725 auto it = dedupMap.find(module);
1726 if (it == dedupMap.end()) {
1727 auto diag = emitError(circuit.getLoc(),
1728 "MustDeduplicateAnnotation references module ")
1729 << module <<
" which does not exist";
1742 auto modules = annotation.
getMember<ArrayAttr>(
"modules");
1744 emitError(circuit.getLoc(),
1745 "MustDeduplicateAnnotation missing \"modules\" member");
1750 if (modules.size() == 0)
1753 auto firstModule = parseModule(modules[0]);
1754 auto firstLead = getLead(firstModule);
1758 for (
auto attr : modules.getValue().drop_front()) {
1759 auto nextModule = parseModule(attr);
1760 auto nextLead = getLead(nextModule);
1763 if (firstLead != nextLead) {
1764 auto diag = emitError(circuit.getLoc(),
"module ")
1765 << nextModule <<
" not deduplicated with " << firstModule;
1766 auto a = instanceGraph.lookup(firstLead)->getModule();
1767 auto b = instanceGraph.lookup(nextLead)->getModule();
1768 equiv.check(diag, a, b);
1776 return signalPassFailure();
1778 for (
auto module : circuit.getOps<FModuleOp>())
1786 markAnalysesPreserved<NLATable>();
1787 if (!anythingChanged)
1788 markAllAnalysesPreserved();
1794 return std::make_unique<DedupPass>();
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.
static void mergeRegions(Region *region1, Region *region2)
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
bool hasAnnotation(StringRef className) const
Return true if we have an annotation with the specified class name.
static AnnotationSet forPort(FModuleLike op, size_t portNo)
Get an annotation set for the specified port.
Annotation getAnnotation(StringRef className) const
If this annotation set has an annotation with the specified class name, return it.
This class provides a read-only projection of an annotation.
void setDict(DictionaryAttr dict)
Set the data dictionary of this attribute.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
This graph tracks modules and where they are instantiated.
This table tracks nlas and what modules participate in them.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
ClassType getInstanceTypeForClassLike(ClassLike classOp)
static StringRef toString(Direction direction)
FieldRef getFieldRefFromValue(Value value, bool lookThroughCasts=false)
Get the FieldRef from a value.
constexpr const char * mustDedupAnnoClass
std::pair< hw::InnerSymAttr, StringAttr > getOrAddInnerSym(MLIRContext *context, hw::InnerSymAttr attr, uint64_t fieldID, llvm::function_ref< hw::InnerSymbolNamespace &()> getNamespace)
Ensure that the the InnerSymAttr has a symbol on the field specified.
constexpr const char * noDedupAnnoClass
size_t getNumPorts(Operation *op)
Return the number of ports in a module-like thing (modules, memories, etc)
constexpr const char * dedupGroupAnnoClass
std::unique_ptr< mlir::Pass > createDedupPass()
std::pair< std::string, bool > getFieldName(const FieldRef &fieldRef, bool nameSafe=false)
Get a string identifier representing the FieldRef.
constexpr const char * dontTouchAnnoClass
void emitConnect(OpBuilder &builder, Location loc, Value lhs, Value rhs)
Emit a connect between two values.
StringAttr getInnerSymName(Operation *op)
Return the StringAttr for the inner_sym name, if it exists.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Block * nlaBlock
We insert all NLAs to the beginning of this block.
void recordAnnotations(Operation *op)
Record all targets which use an NLA.
void eraseNLA(hw::HierPathOp nla)
This erases the NLA op, and removes the NLA from every module's NLA map, but it does not delete the N...
void mergeAnnotations(FModuleLike toModule, Operation *to, FModuleLike fromModule, Operation *from)
Merge all annotations and port annotations on two operations.
void replaceInstances(FModuleLike toModule, Operation *fromModule)
This deletes and replaces all instances of the "fromModule" with instances of the "toModule".
SmallVector< FlatSymbolRefAttr > createNLAs(StringAttr toModuleName, FModuleLike fromModule, SymbolTable::Visibility vis=SymbolTable::Visibility::Private)
Look up the instantiations of this module and create an NLA for each one.
void record(FModuleLike module)
Record the usages of any NLA's in this module, so that we may update the annotation if the parent mod...
void rewriteExtModuleNLAs(RenameMap &renameMap, StringAttr toName, StringAttr fromName)
void mergeRegions(RenameMap &renameMap, FModuleLike toModule, Region &toRegion, FModuleLike fromModule, Region &fromRegion)
void dedup(FModuleLike toModule, FModuleLike fromModule)
Remove the "fromModule", and replace all references to it with the "toModule".
void rewriteModuleNLAs(RenameMap &renameMap, FModuleOp toModule, FModuleOp fromModule)
Process all the NLAs that the two modules participate in, replacing references to the "from" module w...
void recordAnnotations(AnnoTarget target)
For a specific annotation target, record all the unique NLAs which target it in the targetMap.
void cloneAnnotation(SmallVectorImpl< FlatSymbolRefAttr > &nlas, Annotation anno, ArrayRef< NamedAttribute > attributes, unsigned nonLocalIndex, SmallVectorImpl< Annotation > &newAnnotations)
Clone the annotation for each NLA in a list.
void recordSymRenames(RenameMap &renameMap, FModuleLike toModule, Operation *to, FModuleLike fromModule, Operation *from)
void mergeAnnotations(FModuleLike toModule, AnnoTarget to, AnnotationSet toAnnos, FModuleLike fromModule, AnnoTarget from, AnnotationSet fromAnnos)
Merge the annotations of a specific target, either a operation or a port on an operation.
StringAttr nonLocalString
SymbolTable & symbolTable
SmallVector< FlatSymbolRefAttr > createNLAs(Operation *fromModule, ArrayRef< Attribute > baseNamepath, SymbolTable::Visibility vis=SymbolTable::Visibility::Private)
Look up the instantiations of the from module and create an NLA for each one, appending the baseNamep...
void mergeOps(RenameMap &renameMap, FModuleLike toModule, Operation *to, FModuleLike fromModule, Operation *from)
Recursively merge two operations.
hw::InnerSymbolNamespace & getNamespace(Operation *module)
Get a cached namespace for a module.
DenseMap< Operation *, hw::InnerSymbolNamespace > moduleNamespaces
A module namespace cache.
bool makeAnnotationNonLocal(StringAttr toModuleName, AnnoTarget to, FModuleLike fromModule, Annotation anno, SmallVectorImpl< Annotation > &newAnnotations)
Take an annotation, and update it to be a non-local annotation.
InstanceGraph & instanceGraph
void mergeBlocks(RenameMap &renameMap, FModuleLike toModule, Block &toBlock, FModuleLike fromModule, Block &fromBlock)
Recursively merge two blocks.
DenseMap< Attribute, llvm::SmallDenseSet< AnnoTarget > > targetMap
void copyAnnotations(FModuleLike toModule, AnnoTarget to, FModuleLike fromModule, AnnotationSet annos, SmallVectorImpl< Annotation > &newAnnotations, SmallPtrSetImpl< Attribute > &dontTouches)
Deduper(InstanceGraph &instanceGraph, SymbolTable &symbolTable, NLATable *nlaTable, CircuitOp circuit)
void addAnnotationContext(RenameMap &renameMap, FModuleOp toModule, FModuleOp fromModule)
Process all NLAs referencing the "from" module to point to the "to" module.
DenseMap< StringAttr, StringAttr > RenameMap
DenseMap< Attribute, Attribute > nlaCache
const hw::InnerSymbolTable & a
ModuleData(const hw::InnerSymbolTable &a, const hw::InnerSymbolTable &b)
const hw::InnerSymbolTable & b
This class is for reporting differences between two modules which should have been deduplicated.
DenseSet< Attribute > nonessentialAttributes
std::string prettyPrint(Attribute attr)
LogicalResult check(InFlightDiagnostic &diag, FInstanceLike a, FInstanceLike b)
LogicalResult check(InFlightDiagnostic &diag, ModuleData &data, Operation *a, Block &aBlock, Operation *b, Block &bBlock)
LogicalResult check(InFlightDiagnostic &diag, const Twine &message, Operation *a, Type aType, Operation *b, Type bType)
LogicalResult check(InFlightDiagnostic &diag, ModuleData &data, Operation *a, DictionaryAttr aDict, Operation *b, DictionaryAttr bDict)
LogicalResult check(InFlightDiagnostic &diag, ModuleData &data, Operation *a, Region &aRegion, Operation *b, Region &bRegion)
StringAttr portDirectionsAttr
LogicalResult check(InFlightDiagnostic &diag, const Twine &message, Operation *a, BundleType aType, Operation *b, BundleType bType)
StringAttr dedupGroupClass
LogicalResult check(InFlightDiagnostic &diag, ModuleData &data, Operation *a, Operation *b)
Equivalence(MLIRContext *context, InstanceGraph &instanceGraph)
LogicalResult check(InFlightDiagnostic &diag, Operation *a, mlir::DenseBoolArrayAttr aAttr, Operation *b, mlir::DenseBoolArrayAttr bAttr)
InstanceGraph & instanceGraph
void check(InFlightDiagnostic &diag, Operation *a, Operation *b)
std::array< uint8_t, 32 > structuralHash
mlir::ArrayAttr referredModuleNames
This struct contains constant string attributes shared across different threads.
StringAttr moduleNameAttr
DenseSet< Attribute > nonessentialAttributes
StructuralHasherSharedConstants(MLIRContext *context)
void update(Operation *op, DictionaryAttr dict)
Hash the top level attribute dictionary of the operation.
void update(BlockArgument arg)
std::pair< std::array< uint8_t, 32 >, SmallVector< StringAttr > > getHashAndModuleNames(FModuleLike module, StringAttr group)
void update(const void *pointer)
void update(InnerRefAttr attr)
void update(Operation *op)
void record(void *address)
void update(const SymbolTarget &target)
void update(Operation *op, hw::InnerSymAttr attr)
DenseMap< StringAttr, SymbolTarget > innerSymTargets
void update(size_t value)
SmallVector< mlir::StringAttr > referredModuleNames
void update(Block &block)
void update(BundleType type)
void update(OpResult result)
void update(OpOperand &operand)
StructuralHasher(const StructuralHasherSharedConstants &constants)
void update(TypeID typeID)
const StructuralHasherSharedConstants & constants
void update(Value value, hw::InnerSymAttr attr)
DenseMap< void *, unsigned > indices
void update(mlir::OperationName name)
An annotation target is used to keep track of something that is targeted by an Annotation.
AnnotationSet getAnnotations() const
Get the annotations associated with the target.
void setAnnotations(AnnotationSet annotations) const
Set the annotations associated with the target.
This represents an annotation targeting a specific operation.
Attribute getNLAReference(hw::InnerSymbolNamespace &moduleNamespace) const
This represents an annotation targeting a specific port of a module, memory, or instance.
static ModuleInfo getEmptyKey()
static ModuleInfo getTombstoneKey()
static unsigned getHashValue(const ModuleInfo &val)
static bool isEqual(const ModuleInfo &lhs, const ModuleInfo &rhs)