24 #include "mlir/IR/IRMapping.h"
25 #include "mlir/IR/ImplicitLocOpBuilder.h"
26 #include "mlir/IR/Threading.h"
27 #include "mlir/Pass/Pass.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"
40 #define GEN_PASS_DEF_DEDUP
41 #include "circt/Dialect/FIRRTL/Passes.h.inc"
45 using namespace circt;
46 using namespace firrtl;
47 using hw::InnerRefAttr;
53 llvm::raw_ostream &
printHex(llvm::raw_ostream &stream,
54 ArrayRef<uint8_t> bytes) {
56 return stream << format_bytes(bytes, std::nullopt, 32) <<
"\n";
59 llvm::raw_ostream &
printHash(llvm::raw_ostream &stream, llvm::SHA256 &data) {
63 llvm::raw_ostream &
printHash(llvm::raw_ostream &stream, std::string data) {
64 ArrayRef<uint8_t> bytes(
reinterpret_cast<const uint8_t *
>(
data.c_str()),
97 nonessentialAttributes.insert(
StringAttr::get(context,
"portAnnotations"));
99 nonessentialAttributes.insert(
StringAttr::get(context,
"portLocations"));
124 : constants(constants){};
126 std::pair<std::array<uint8_t, 32>, SmallVector<StringAttr>>
129 auto hash = sha.final();
130 return {hash, referredModuleNames};
135 auto *
addr =
reinterpret_cast<const uint8_t *
>(&pointer);
136 sha.update(ArrayRef<uint8_t>(
addr,
sizeof pointer));
140 auto *
addr =
reinterpret_cast<const uint8_t *
>(&value);
141 sha.update(ArrayRef<uint8_t>(
addr,
sizeof value));
148 update(type.getTypeID());
149 for (
auto &element : type.getElements()) {
150 update(element.isFlip);
151 update(element.type);
157 if (
auto bundle = type_dyn_cast<BundleType>(type))
158 return update(bundle);
159 update(type.getAsOpaquePointer());
163 auto size = indices.size();
164 indices[address] = size;
167 void update(BlockArgument arg) { record(arg.getAsOpaquePointer()); }
170 record(result.getAsOpaquePointer());
175 if (
auto objectOp = dyn_cast<ObjectOp>(result.getOwner())) {
176 referredModuleNames.push_back(objectOp.getType().getNameAttr().getAttr());
180 update(result.getType());
185 auto it = indices.find(operand.get().getAsOpaquePointer());
186 assert(it != indices.end() &&
"op should have been previously hashed");
190 void update(Operation *op, hw::InnerSymAttr attr) {
191 for (
auto props : attr)
192 innerSymTargets[props.getName()] =
196 void update(Value value, hw::InnerSymAttr attr) {
197 for (
auto props : attr)
198 innerSymTargets[props.getName()] =
199 SymbolTarget{indices[value.getAsOpaquePointer()], props.getFieldID()};
203 update(target.
index);
209 auto it = innerSymTargets.find(attr.getName());
210 assert(it != innerSymTargets.end() &&
211 "inner symbol should have been previously hashed");
212 update(attr.getTypeID());
218 void update(Operation *op, DictionaryAttr dict) {
219 for (
auto namedAttr : dict) {
220 auto name = namedAttr.getName();
221 auto value = namedAttr.getValue();
224 bool isClassPortNames =
225 isa<ClassLike>(op) && name == constants.portNamesAttr;
226 if (constants.nonessentialAttributes.contains(name) && !isClassPortNames)
230 if (name == constants.portTypesAttr) {
231 auto portTypes = cast<ArrayAttr>(value).getAsValueRange<TypeAttr>();
232 for (
auto type : portTypes)
238 if (name == constants.portSymsAttr) {
239 if (op->getNumRegions() != 1)
241 auto ®ion = op->getRegion(0);
242 if (region.getBlocks().empty())
244 auto *block = ®ion.front();
245 auto syms = cast<ArrayAttr>(value).getAsRange<hw::InnerSymAttr>();
248 for (
auto [arg, sym] : llvm::zip_equal(block->getArguments(), syms))
252 if (name == constants.innerSymAttr) {
253 auto innerSym = cast<hw::InnerSymAttr>(value);
254 update(op, innerSym);
263 if (isa<InstanceOp>(op) && name == constants.moduleNameAttr) {
264 referredModuleNames.push_back(cast<FlatSymbolRefAttr>(value).
getAttr());
269 update(name.getAsOpaquePointer());
273 if (isa<DistinctAttr>(value))
277 if (
auto innerRef = dyn_cast<hw::InnerRefAttr>(value))
280 update(value.getAsOpaquePointer());
286 for (
auto arg : block.getArguments())
289 for (
auto &op : block)
295 update(name.getAsOpaquePointer());
301 update(op->getName());
302 update(op, op->getAttrDictionary());
304 for (
auto &operand : op->getOpOperands())
308 update(op->getNumRegions());
309 for (
auto ®ion : op->getRegions())
310 for (
auto &block : region.getBlocks())
313 for (
auto result : op->getResults())
343 : instanceGraph(instanceGraph) {
347 nonessentialAttributes.insert(
StringAttr::get(context,
"annotations"));
349 nonessentialAttributes.insert(
StringAttr::get(context,
"portAnnotations"));
353 nonessentialAttributes.insert(
StringAttr::get(context,
"portLocations"));
359 ModuleData(
const hw::InnerSymbolTable &a,
const hw::InnerSymbolTable &b)
362 const hw::InnerSymbolTable &
a;
363 const hw::InnerSymbolTable &
b;
367 SmallString<64> buffer;
368 llvm::raw_svector_ostream os(buffer);
369 if (
auto integerAttr = dyn_cast<IntegerAttr>(attr)) {
371 if (integerAttr.getType().isSignlessInteger())
372 integerAttr.getValue().toStringUnsigned(buffer, 16);
374 integerAttr.getAPSInt().toString(buffer, 16);
378 return std::string(buffer);
382 LogicalResult
check(InFlightDiagnostic &diag,
const Twine &message,
383 Operation *a, BundleType aType, Operation *b,
385 if (aType.getNumElements() != bType.getNumElements()) {
386 diag.attachNote(a->getLoc())
387 << message <<
" bundle type has different number of elements";
388 diag.attachNote(b->getLoc()) <<
"second operation here";
392 for (
auto elementPair :
393 llvm::zip(aType.getElements(), bType.getElements())) {
394 auto aElement = std::get<0>(elementPair);
395 auto bElement = std::get<1>(elementPair);
396 if (aElement.isFlip != bElement.isFlip) {
397 diag.attachNote(a->getLoc()) << message <<
" bundle element "
398 << aElement.name <<
" flip does not match";
399 diag.attachNote(b->getLoc()) <<
"second operation here";
403 if (failed(check(diag,
404 "bundle element \'" + aElement.name.getValue() +
"'", a,
405 aElement.type, b, bElement.type)))
411 LogicalResult
check(InFlightDiagnostic &diag,
const Twine &message,
412 Operation *a, Type aType, Operation *b, Type bType) {
415 if (
auto aBundleType = type_dyn_cast<BundleType>(aType))
416 if (
auto bBundleType = type_dyn_cast<BundleType>(bType))
417 return check(diag, message, a, aBundleType, b, bBundleType);
418 if (type_isa<RefType>(aType) && type_isa<RefType>(bType) &&
420 diag.attachNote(a->getLoc())
421 << message <<
", has a RefType with a different base type "
422 << type_cast<RefType>(aType).getType()
423 <<
" in the same position of the two modules marked as 'must dedup'. "
424 "(This may be due to Grand Central Taps or Views being different "
425 "between the two modules.)";
426 diag.attachNote(b->getLoc())
427 <<
"the second module has a different base type "
428 << type_cast<RefType>(bType).getType();
431 diag.attachNote(a->getLoc())
432 << message <<
" types don't match, first type is " << aType;
433 diag.attachNote(b->getLoc()) <<
"second type is " << bType;
438 Block &aBlock, Operation *b, Block &bBlock) {
441 auto portNames = a->getAttrOfType<ArrayAttr>(
"portNames");
443 auto emitMissingPort = [&](Value existsVal, Operation *opExists,
444 Operation *opDoesNotExist) {
446 auto portNames = opExists->getAttrOfType<ArrayAttr>(
"portNames");
448 if (
auto portNameAttr = dyn_cast<StringAttr>(portNames[portNo]))
449 portName = portNameAttr.getValue();
450 if (type_isa<RefType>(existsVal.getType())) {
451 diag.attachNote(opExists->getLoc())
452 <<
" contains a RefType port named '" + portName +
453 "' that only exists in one of the modules (can be due to "
454 "difference in Grand Central Tap or View of two modules "
455 "marked with must dedup)";
456 diag.attachNote(opDoesNotExist->getLoc())
457 <<
"second module to be deduped that does not have the RefType "
460 diag.attachNote(opExists->getLoc())
461 <<
"port '" + portName +
"' only exists in one of the modules";
462 diag.attachNote(opDoesNotExist->getLoc())
463 <<
"second module to be deduped that does not have the port";
469 llvm::zip_longest(aBlock.getArguments(), bBlock.getArguments())) {
470 auto &aArg = std::get<0>(argPair);
471 auto &bArg = std::get<1>(argPair);
472 if (aArg.has_value() && bArg.has_value()) {
477 if (
auto portNameAttr = dyn_cast<StringAttr>(portNames[portNo]))
478 portName = portNameAttr.getValue();
481 if (failed(check(diag,
"module port '" + portName +
"'", a,
482 aArg->getType(), b, bArg->getType())))
484 data.map.map(aArg.value(), bArg.value());
488 if (!aArg.has_value())
490 return emitMissingPort(aArg.has_value() ? aArg.value() : bArg.value(), a,
495 auto aIt = aBlock.begin();
496 auto aEnd = aBlock.end();
497 auto bIt = bBlock.begin();
498 auto bEnd = bBlock.end();
499 while (aIt != aEnd && bIt != bEnd)
500 if (failed(check(diag,
data, &*aIt++, &*bIt++)))
503 diag.attachNote(aIt->getLoc()) <<
"first block has more operations";
504 diag.attachNote(b->getLoc()) <<
"second block here";
508 diag.attachNote(bIt->getLoc()) <<
"second block has more operations";
509 diag.attachNote(a->getLoc()) <<
"first block here";
516 Region &aRegion, Operation *b, Region &bRegion) {
517 auto aIt = aRegion.begin();
518 auto aEnd = aRegion.end();
519 auto bIt = bRegion.begin();
520 auto bEnd = bRegion.end();
523 while (aIt != aEnd && bIt != bEnd)
524 if (failed(check(diag,
data, a, *aIt++, b, *bIt++)))
526 if (aIt != aEnd || bIt != bEnd) {
527 diag.attachNote(a->getLoc())
528 <<
"operation regions have different number of blocks";
529 diag.attachNote(b->getLoc()) <<
"second operation here";
535 LogicalResult
check(InFlightDiagnostic &diag, Operation *a,
536 mlir::DenseBoolArrayAttr aAttr, Operation *b,
537 mlir::DenseBoolArrayAttr bAttr) {
540 auto portNames = a->getAttrOfType<ArrayAttr>(
"portNames");
541 for (
unsigned i = 0, e = aAttr.size(); i < e; ++i) {
542 auto aDirection = aAttr[i];
543 auto bDirection = bAttr[i];
544 if (aDirection != bDirection) {
545 auto ¬e = diag.attachNote(a->getLoc()) <<
"module port ";
547 note <<
"'" << cast<StringAttr>(portNames[i]).getValue() <<
"'";
550 note <<
" directions don't match, first direction is '"
552 diag.attachNote(b->getLoc()) <<
"second direction is '"
561 DictionaryAttr aDict, Operation *b,
562 DictionaryAttr bDict) {
567 DenseSet<Attribute> seenAttrs;
568 for (
auto namedAttr : aDict) {
569 auto attrName = namedAttr.getName();
570 if (nonessentialAttributes.contains(attrName))
573 auto aAttr = namedAttr.getValue();
574 auto bAttr = bDict.get(attrName);
576 diag.attachNote(a->getLoc())
577 <<
"second operation is missing attribute " << attrName;
578 diag.attachNote(b->getLoc()) <<
"second operation here";
582 if (isa<hw::InnerRefAttr>(aAttr) && isa<hw::InnerRefAttr>(bAttr)) {
583 auto bRef = cast<hw::InnerRefAttr>(bAttr);
584 auto aRef = cast<hw::InnerRefAttr>(aAttr);
586 auto aTarget =
data.a.lookup(aRef.getName());
587 auto bTarget =
data.b.lookup(bRef.getName());
588 if (!aTarget || !bTarget)
589 diag.attachNote(a->getLoc())
590 <<
"malformed ir, possibly violating use-before-def";
592 diag.attachNote(a->getLoc())
593 <<
"operations have different targets, first operation has "
595 diag.attachNote(b->getLoc()) <<
"second operation has " << bTarget;
598 if (aTarget.isPort()) {
600 if (!bTarget.isPort() || aTarget.getPort() != bTarget.getPort())
604 if (!bTarget.isOpOnly() ||
605 aTarget.getOp() !=
data.map.lookup(bTarget.getOp()))
608 if (aTarget.getField() != bTarget.getField())
610 }
else if (attrName == portDirectionsAttr) {
613 if (failed(check(diag, a, cast<mlir::DenseBoolArrayAttr>(aAttr), b,
614 cast<mlir::DenseBoolArrayAttr>(bAttr))))
616 }
else if (isa<DistinctAttr>(aAttr) && isa<DistinctAttr>(bAttr)) {
619 }
else if (aAttr != bAttr) {
620 diag.attachNote(a->getLoc())
621 <<
"first operation has attribute '" << attrName.getValue()
622 <<
"' with value " << prettyPrint(aAttr);
623 diag.attachNote(b->getLoc())
624 <<
"second operation has value " << prettyPrint(bAttr);
627 seenAttrs.insert(attrName);
629 if (aDict.getValue().size() != bDict.getValue().size()) {
630 for (
auto namedAttr : bDict) {
631 auto attrName = namedAttr.getName();
634 if (nonessentialAttributes.contains(attrName) ||
635 seenAttrs.contains(attrName))
638 diag.attachNote(a->getLoc())
639 <<
"first operation is missing attribute " << attrName;
640 diag.attachNote(b->getLoc()) <<
"second operation here";
648 LogicalResult
check(InFlightDiagnostic &diag, FInstanceLike a,
650 auto aName = a.getReferencedModuleNameAttr();
651 auto bName = b.getReferencedModuleNameAttr();
658 auto aModule = instanceGraph.lookup(aName)->getModule();
659 auto bModule = instanceGraph.lookup(bName)->getModule();
661 diag.attachNote(std::nullopt)
662 <<
"in instance " << a.getInstanceNameAttr() <<
" of " << aName
663 <<
", and instance " << b.getInstanceNameAttr() <<
" of " << bName;
664 check(diag, aModule, bModule);
672 if (a->getName() != b->getName()) {
673 diag.attachNote(a->getLoc()) <<
"first operation is a " << a->getName();
674 diag.attachNote(b->getLoc()) <<
"second operation is a " << b->getName();
680 if (
auto aInst = dyn_cast<FInstanceLike>(a)) {
681 auto bInst = cast<FInstanceLike>(b);
682 if (failed(check(diag, aInst, bInst)))
687 if (a->getNumResults() != b->getNumResults()) {
688 diag.attachNote(a->getLoc())
689 <<
"operations have different number of results";
690 diag.attachNote(b->getLoc()) <<
"second operation here";
693 for (
auto resultPair : llvm::zip(a->getResults(), b->getResults())) {
694 auto &aValue = std::get<0>(resultPair);
695 auto &bValue = std::get<1>(resultPair);
696 if (failed(check(diag,
"operation result", a, aValue.getType(), b,
699 data.map.map(aValue, bValue);
703 if (a->getNumOperands() != b->getNumOperands()) {
704 diag.attachNote(a->getLoc())
705 <<
"operations have different number of operands";
706 diag.attachNote(b->getLoc()) <<
"second operation here";
709 for (
auto operandPair : llvm::zip(a->getOperands(), b->getOperands())) {
710 auto &aValue = std::get<0>(operandPair);
711 auto &bValue = std::get<1>(operandPair);
712 if (bValue !=
data.map.lookup(aValue)) {
713 diag.attachNote(a->getLoc())
714 <<
"operations use different operands, first operand is '"
719 diag.attachNote(b->getLoc())
720 <<
"second operand is '"
724 <<
"', but should have been '"
735 if (a->getNumRegions() != b->getNumRegions()) {
736 diag.attachNote(a->getLoc())
737 <<
"operations have different number of regions";
738 diag.attachNote(b->getLoc()) <<
"second operation here";
741 for (
auto regionPair : llvm::zip(a->getRegions(), b->getRegions())) {
742 auto &aRegion = std::get<0>(regionPair);
743 auto &bRegion = std::get<1>(regionPair);
744 if (failed(check(diag,
data, a, aRegion, b, bRegion)))
749 if (failed(check(diag,
data, a, a->getAttrDictionary(), b,
750 b->getAttrDictionary())))
756 void check(InFlightDiagnostic &diag, Operation *a, Operation *b) {
757 hw::InnerSymbolTable aTable(a);
758 hw::InnerSymbolTable bTable(b);
761 diag.attachNote(a->getLoc()) <<
"module marked NoDedup";
765 diag.attachNote(b->getLoc()) <<
"module marked NoDedup";
769 dyn_cast_or_null<StringAttr>(a->getDiscardableAttr(dedupGroupAttrName));
770 auto bGroup = dyn_cast_or_null<StringAttr>(
771 b->getAttrOfType<StringAttr>(dedupGroupAttrName));
772 if (aGroup != bGroup) {
774 diag.attachNote(b->getLoc())
775 <<
"module is in dedup group '" << bGroup.str() <<
"'";
777 diag.attachNote(b->getLoc()) <<
"module is not part of a dedup group";
780 diag.attachNote(a->getLoc())
781 <<
"module is in dedup group '" << aGroup.str() <<
"'";
783 diag.attachNote(a->getLoc()) <<
"module is not part of a dedup group";
787 if (failed(check(diag,
data, a, b)))
789 diag.attachNote(a->getLoc()) <<
"first module here";
790 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),
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 circt::firrtl::impl::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 dedupGroupAttrName =
StringAttr::get(context,
"firrtl.dedup_group");
1622 for (
auto module : modules) {
1623 llvm::SmallSetVector<StringAttr, 1> groups;
1625 module, [&groups, dedupGroupClass](
Annotation annotation) {
1628 groups.insert(annotation.
getMember<StringAttr>(
"group"));
1631 if (groups.size() > 1) {
1632 module.emitError(
"module belongs to multiple dedup groups: ") << groups;
1633 return signalPassFailure();
1635 assert(!module->hasAttr(dedupGroupAttrName) &&
1636 "unexpected existing use of temporary dedup group attribute");
1637 if (!groups.empty())
1638 module->setDiscardableAttr(dedupGroupAttrName, groups.front());
1642 auto result = mlir::failableParallelForEach(
1643 context, llvm::seq(modules.size()), [&](
unsigned idx) {
1644 auto module = modules[idx];
1646 if (AnnotationSet::hasAnnotation(module, noDedupClass))
1650 if (auto ext = dyn_cast<FExtModuleOp>(*module);
1651 ext && !ext.getDefname().has_value())
1657 if (!module.isPrivate() ||
1658 (!module.canDiscardOnUseEmpty() && !isa<ClassLike>(*module))) {
1668 if (result.failed())
1669 return signalPassFailure();
1671 for (
auto [i, module] : llvm::enumerate(modules)) {
1672 auto moduleName = module.getModuleNameAttr();
1673 auto &hashAndModuleNamesOpt = hashesAndModuleNames[i];
1675 if (!hashAndModuleNamesOpt) {
1680 dedupMap[moduleName] = moduleName;
1685 SmallVector<mlir::Attribute> names;
1686 for (
auto oldModuleName : hashAndModuleNamesOpt->second) {
1687 auto newModuleName = dedupMap[oldModuleName];
1688 names.push_back(newModuleName);
1692 ModuleInfo moduleInfo{hashAndModuleNamesOpt->first,
1696 auto it = moduleInfoToModule.find(moduleInfo);
1697 if (it != moduleInfoToModule.end()) {
1698 auto original = cast<FModuleLike>(it->second);
1700 dedupMap[moduleName] = original.getModuleNameAttr();
1701 deduper.dedup(original, module);
1703 anythingChanged =
true;
1707 deduper.record(module);
1709 dedupMap[moduleName] = moduleName;
1711 moduleInfoToModule[moduleInfo] = module;
1719 auto failed =
false;
1721 auto parseModule = [&](Attribute path) -> StringAttr {
1724 auto [_, rhs] = cast<StringAttr>(path).getValue().split(
'|');
1730 auto getLead = [&](StringAttr module) -> StringAttr {
1731 auto it = dedupMap.find(module);
1732 if (it == dedupMap.end()) {
1733 auto diag = emitError(circuit.getLoc(),
1734 "MustDeduplicateAnnotation references module ")
1735 << module <<
" which does not exist";
1745 auto modules = annotation.
getMember<ArrayAttr>(
"modules");
1747 emitError(circuit.getLoc(),
1748 "MustDeduplicateAnnotation missing \"modules\" member");
1753 if (modules.empty())
1756 auto firstModule = parseModule(modules[0]);
1757 auto firstLead = getLead(firstModule);
1761 for (
auto attr : modules.getValue().drop_front()) {
1762 auto nextModule = parseModule(attr);
1763 auto nextLead = getLead(nextModule);
1766 if (firstLead != nextLead) {
1767 auto diag = emitError(circuit.getLoc(),
"module ")
1768 << nextModule <<
" not deduplicated with " << firstModule;
1769 auto a = instanceGraph.lookup(firstLead)->getModule();
1770 auto b = instanceGraph.lookup(nextLead)->getModule();
1771 equiv.check(diag, a, b);
1779 return signalPassFailure();
1782 for (
auto module : circuit.getOps<FModuleLike>())
1783 module->removeDiscardableAttr(dedupGroupAttrName);
1790 markAnalysesPreserved<NLATable>();
1791 if (!anythingChanged)
1792 markAllAnalysesPreserved();
1798 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)
static Block * getBodyBlock(FModuleLike mod)
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.
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.
StringAttr getClassAttr() const
Return the 'class' that this annotation is representing.
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)
StringAttr dedupGroupAttrName
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)
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)
void update(const void *pointer)
void update(InnerRefAttr attr)
void update(Operation *op)
void record(void *address)
void update(const SymbolTarget &target)
std::pair< std::array< uint8_t, 32 >, SmallVector< StringAttr > > getHashAndModuleNames(FModuleLike module)
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)