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/Hashing.h"
33 #include "llvm/ADT/PostOrderIterator.h"
34 #include "llvm/ADT/SmallPtrSet.h"
35 #include "llvm/ADT/TypeSwitch.h"
36 #include "llvm/Support/Format.h"
37 #include "llvm/Support/SHA256.h"
41 #define GEN_PASS_DEF_DEDUP
42 #include "circt/Dialect/FIRRTL/Passes.h.inc"
46 using namespace circt;
47 using namespace firrtl;
48 using hw::InnerRefAttr;
57 if (!symbol.isPrivate())
62 if (isa<ClassLike>(*symbol))
67 if (!symbol.canDiscardOnUseEmpty())
77 llvm::raw_ostream &
printHex(llvm::raw_ostream &stream,
78 ArrayRef<uint8_t> bytes) {
80 return stream << format_bytes(bytes, std::nullopt, 32) <<
"\n";
83 llvm::raw_ostream &
printHash(llvm::raw_ostream &stream, llvm::SHA256 &data) {
87 llvm::raw_ostream &
printHash(llvm::raw_ostream &stream, std::string data) {
88 ArrayRef<uint8_t> bytes(
reinterpret_cast<const uint8_t *
>(
data.c_str()),
105 static bool operator==(
const ModuleInfo &lhs,
const ModuleInfo &rhs) {
106 return lhs.structuralHash == rhs.structuralHash &&
107 lhs.referredModuleNames == rhs.referredModuleNames;
119 nonessentialAttributes.insert(
StringAttr::get(context,
"annotations"));
122 nonessentialAttributes.insert(
StringAttr::get(context,
"portAnnotations"));
124 nonessentialAttributes.insert(
StringAttr::get(context,
"portLocations"));
126 nonessentialAttributes.insert(
StringAttr::get(context,
"sym_visibility"));
150 : constants(constants) {}
154 return {sha.final(), std::move(referredModuleNames)};
160 auto [it, inserted] = idTable.try_emplace(
object, nextID);
168 auto it = idTable.find(
object);
169 if (it == idTable.end())
171 auto id = it->second;
177 auto [it, inserted] = innerSymIDTable.try_emplace(name, nextInnerSymID);
184 auto value = operand.get();
185 if (
auto result = dyn_cast<OpResult>(value)) {
186 auto *op = result.getOwner();
188 update(result.getResultNumber());
191 if (
auto argument = dyn_cast<BlockArgument>(value)) {
192 auto *block = argument.getOwner();
193 update(getID(block));
194 update(argument.getArgNumber());
197 llvm_unreachable(
"Unknown value type");
201 auto *
addr =
reinterpret_cast<const uint8_t *
>(&pointer);
202 sha.update(ArrayRef<uint8_t>(
addr,
sizeof pointer));
206 auto *
addr =
reinterpret_cast<const uint8_t *
>(&value);
207 sha.update(ArrayRef<uint8_t>(
addr,
sizeof value));
214 update(type.getTypeID());
215 for (
auto &element : type.getElements()) {
216 update(element.isFlip);
217 update(element.type);
223 if (
auto bundle = type_dyn_cast<BundleType>(type))
224 return update(bundle);
225 update(type.getAsOpaquePointer());
232 if (
auto objectOp = dyn_cast<ObjectOp>(result.getOwner())) {
233 referredModuleNames.push_back(objectOp.getType().getNameAttr().getAttr());
237 update(result.getType());
242 void update(Operation *op, DictionaryAttr dict) {
243 for (
auto namedAttr : dict) {
244 auto name = namedAttr.getName();
245 auto value = namedAttr.getValue();
248 bool isClassPortNames =
249 isa<ClassLike>(op) && name == constants.portNamesAttr;
250 if (constants.nonessentialAttributes.contains(name) && !isClassPortNames)
254 update(name.getAsOpaquePointer());
257 if (name == constants.portTypesAttr) {
258 auto portTypes = cast<ArrayAttr>(value).getAsValueRange<TypeAttr>();
259 for (
auto type : portTypes)
265 if (name == constants.portSymbolsAttr) {
266 if (op->getNumRegions() != 1)
268 auto ®ion = op->getRegion(0);
269 if (region.getBlocks().empty())
271 for (
auto sym : cast<ArrayAttr>(value).getAsRange<hw::InnerSymAttr>()) {
272 for (
auto property : sym) {
273 update(property.getFieldID());
274 update(getInnerSymID(property.getName()));
280 if (name == constants.innerSymAttr) {
281 auto innerSym = cast<hw::InnerSymAttr>(value);
282 for (
auto property : innerSym) {
283 update(property.getFieldID());
284 update(getInnerSymID(property.getName()));
294 if (isa<InstanceOp>(op) && name == constants.moduleNameAttr) {
295 referredModuleNames.push_back(cast<FlatSymbolRefAttr>(value).getAttr());
301 if (isa<DistinctAttr>(value))
305 if (
auto innerRef = dyn_cast<hw::InnerRefAttr>(value))
306 update(getInnerSymID(innerRef.getName()));
308 update(value.getAsOpaquePointer());
314 update(name.getAsOpaquePointer());
319 for (
auto &op : llvm::reverse(*block))
321 for (
auto type : block->getArgumentTypes())
323 update(finalizeID(block));
330 for (
auto &block : llvm::reverse(region->getBlocks()))
340 update(op->getNumRegions());
341 for (
auto ®ion : reverse(op->getRegions()))
344 update(op->getName());
347 for (
auto &operand : op->getOpOperands())
352 update(op, op->getAttrDictionary());
355 for (
auto result : op->getResults())
359 update(finalizeID(op));
370 unsigned nextInnerSymID = 0;
394 : instanceGraph(instanceGraph) {
398 nonessentialAttributes.insert(
StringAttr::get(context,
"annotations"));
400 nonessentialAttributes.insert(
StringAttr::get(context,
"portAnnotations"));
403 nonessentialAttributes.insert(
StringAttr::get(context,
"portSymbols"));
404 nonessentialAttributes.insert(
StringAttr::get(context,
"portLocations"));
410 ModuleData(
const hw::InnerSymbolTable &a,
const hw::InnerSymbolTable &b)
413 const hw::InnerSymbolTable &
a;
414 const hw::InnerSymbolTable &
b;
418 SmallString<64> buffer;
419 llvm::raw_svector_ostream os(buffer);
420 if (
auto integerAttr = dyn_cast<IntegerAttr>(attr)) {
422 if (integerAttr.getType().isSignlessInteger())
423 integerAttr.getValue().toStringUnsigned(buffer, 16);
425 integerAttr.getAPSInt().toString(buffer, 16);
429 return std::string(buffer);
433 LogicalResult
check(InFlightDiagnostic &diag,
const Twine &message,
434 Operation *a, BundleType aType, Operation *b,
436 if (aType.getNumElements() != bType.getNumElements()) {
437 diag.attachNote(a->getLoc())
438 << message <<
" bundle type has different number of elements";
439 diag.attachNote(b->getLoc()) <<
"second operation here";
443 for (
auto elementPair :
444 llvm::zip(aType.getElements(), bType.getElements())) {
445 auto aElement = std::get<0>(elementPair);
446 auto bElement = std::get<1>(elementPair);
447 if (aElement.isFlip != bElement.isFlip) {
448 diag.attachNote(a->getLoc()) << message <<
" bundle element "
449 << aElement.name <<
" flip does not match";
450 diag.attachNote(b->getLoc()) <<
"second operation here";
454 if (failed(check(diag,
455 "bundle element \'" + aElement.name.getValue() +
"'", a,
456 aElement.type, b, bElement.type)))
462 LogicalResult
check(InFlightDiagnostic &diag,
const Twine &message,
463 Operation *a, Type aType, Operation *b, Type bType) {
466 if (
auto aBundleType = type_dyn_cast<BundleType>(aType))
467 if (
auto bBundleType = type_dyn_cast<BundleType>(bType))
468 return check(diag, message, a, aBundleType, b, bBundleType);
469 if (type_isa<RefType>(aType) && type_isa<RefType>(bType) &&
471 diag.attachNote(a->getLoc())
472 << message <<
", has a RefType with a different base type "
473 << type_cast<RefType>(aType).getType()
474 <<
" in the same position of the two modules marked as 'must dedup'. "
475 "(This may be due to Grand Central Taps or Views being different "
476 "between the two modules.)";
477 diag.attachNote(b->getLoc())
478 <<
"the second module has a different base type "
479 << type_cast<RefType>(bType).getType();
482 diag.attachNote(a->getLoc())
483 << message <<
" types don't match, first type is " << aType;
484 diag.attachNote(b->getLoc()) <<
"second type is " << bType;
489 Block &aBlock, Operation *b, Block &bBlock) {
492 auto portNames = a->getAttrOfType<ArrayAttr>(
"portNames");
494 auto emitMissingPort = [&](Value existsVal, Operation *opExists,
495 Operation *opDoesNotExist) {
497 auto portNames = opExists->getAttrOfType<ArrayAttr>(
"portNames");
499 if (
auto portNameAttr = dyn_cast<StringAttr>(portNames[portNo]))
500 portName = portNameAttr.getValue();
501 if (type_isa<RefType>(existsVal.getType())) {
502 diag.attachNote(opExists->getLoc())
503 <<
" contains a RefType port named '" + portName +
504 "' that only exists in one of the modules (can be due to "
505 "difference in Grand Central Tap or View of two modules "
506 "marked with must dedup)";
507 diag.attachNote(opDoesNotExist->getLoc())
508 <<
"second module to be deduped that does not have the RefType "
511 diag.attachNote(opExists->getLoc())
512 <<
"port '" + portName +
"' only exists in one of the modules";
513 diag.attachNote(opDoesNotExist->getLoc())
514 <<
"second module to be deduped that does not have the port";
520 llvm::zip_longest(aBlock.getArguments(), bBlock.getArguments())) {
521 auto &aArg = std::get<0>(argPair);
522 auto &bArg = std::get<1>(argPair);
523 if (aArg.has_value() && bArg.has_value()) {
528 if (
auto portNameAttr = dyn_cast<StringAttr>(portNames[portNo]))
529 portName = portNameAttr.getValue();
532 if (failed(check(diag,
"module port '" + portName +
"'", a,
533 aArg->getType(), b, bArg->getType())))
535 data.map.map(aArg.value(), bArg.value());
539 if (!aArg.has_value())
541 return emitMissingPort(aArg.has_value() ? aArg.value() : bArg.value(), a,
546 auto aIt = aBlock.begin();
547 auto aEnd = aBlock.end();
548 auto bIt = bBlock.begin();
549 auto bEnd = bBlock.end();
550 while (aIt != aEnd && bIt != bEnd)
551 if (failed(check(diag,
data, &*aIt++, &*bIt++)))
554 diag.attachNote(aIt->getLoc()) <<
"first block has more operations";
555 diag.attachNote(b->getLoc()) <<
"second block here";
559 diag.attachNote(bIt->getLoc()) <<
"second block has more operations";
560 diag.attachNote(a->getLoc()) <<
"first block here";
567 Region &aRegion, Operation *b, Region &bRegion) {
568 auto aIt = aRegion.begin();
569 auto aEnd = aRegion.end();
570 auto bIt = bRegion.begin();
571 auto bEnd = bRegion.end();
574 while (aIt != aEnd && bIt != bEnd)
575 if (failed(check(diag,
data, a, *aIt++, b, *bIt++)))
577 if (aIt != aEnd || bIt != bEnd) {
578 diag.attachNote(a->getLoc())
579 <<
"operation regions have different number of blocks";
580 diag.attachNote(b->getLoc()) <<
"second operation here";
586 LogicalResult
check(InFlightDiagnostic &diag, Operation *a,
587 mlir::DenseBoolArrayAttr aAttr, Operation *b,
588 mlir::DenseBoolArrayAttr bAttr) {
591 auto portNames = a->getAttrOfType<ArrayAttr>(
"portNames");
592 for (
unsigned i = 0, e = aAttr.size(); i < e; ++i) {
593 auto aDirection = aAttr[i];
594 auto bDirection = bAttr[i];
595 if (aDirection != bDirection) {
596 auto ¬e = diag.attachNote(a->getLoc()) <<
"module port ";
598 note <<
"'" << cast<StringAttr>(portNames[i]).getValue() <<
"'";
601 note <<
" directions don't match, first direction is '"
603 diag.attachNote(b->getLoc()) <<
"second direction is '"
612 DictionaryAttr aDict, Operation *b,
613 DictionaryAttr bDict) {
618 DenseSet<Attribute> seenAttrs;
619 for (
auto namedAttr : aDict) {
620 auto attrName = namedAttr.getName();
621 if (nonessentialAttributes.contains(attrName))
624 auto aAttr = namedAttr.getValue();
625 auto bAttr = bDict.get(attrName);
627 diag.attachNote(a->getLoc())
628 <<
"second operation is missing attribute " << attrName;
629 diag.attachNote(b->getLoc()) <<
"second operation here";
633 if (isa<hw::InnerRefAttr>(aAttr) && isa<hw::InnerRefAttr>(bAttr)) {
634 auto bRef = cast<hw::InnerRefAttr>(bAttr);
635 auto aRef = cast<hw::InnerRefAttr>(aAttr);
637 auto aTarget =
data.a.lookup(aRef.getName());
638 auto bTarget =
data.b.lookup(bRef.getName());
639 if (!aTarget || !bTarget)
640 diag.attachNote(a->getLoc())
641 <<
"malformed ir, possibly violating use-before-def";
643 diag.attachNote(a->getLoc())
644 <<
"operations have different targets, first operation has "
646 diag.attachNote(b->getLoc()) <<
"second operation has " << bTarget;
649 if (aTarget.isPort()) {
651 if (!bTarget.isPort() || aTarget.getPort() != bTarget.getPort())
655 if (!bTarget.isOpOnly() ||
656 aTarget.getOp() !=
data.map.lookup(bTarget.getOp()))
659 if (aTarget.getField() != bTarget.getField())
661 }
else if (attrName == portDirectionsAttr) {
664 if (failed(check(diag, a, cast<mlir::DenseBoolArrayAttr>(aAttr), b,
665 cast<mlir::DenseBoolArrayAttr>(bAttr))))
667 }
else if (isa<DistinctAttr>(aAttr) && isa<DistinctAttr>(bAttr)) {
670 }
else if (aAttr != bAttr) {
671 diag.attachNote(a->getLoc())
672 <<
"first operation has attribute '" << attrName.getValue()
673 <<
"' with value " << prettyPrint(aAttr);
674 diag.attachNote(b->getLoc())
675 <<
"second operation has value " << prettyPrint(bAttr);
678 seenAttrs.insert(attrName);
680 if (aDict.getValue().size() != bDict.getValue().size()) {
681 for (
auto namedAttr : bDict) {
682 auto attrName = namedAttr.getName();
685 if (nonessentialAttributes.contains(attrName) ||
686 seenAttrs.contains(attrName))
689 diag.attachNote(a->getLoc())
690 <<
"first operation is missing attribute " << attrName;
691 diag.attachNote(b->getLoc()) <<
"second operation here";
699 LogicalResult
check(InFlightDiagnostic &diag, FInstanceLike a,
701 auto aName = a.getReferencedModuleNameAttr();
702 auto bName = b.getReferencedModuleNameAttr();
709 auto aModule = instanceGraph.lookup(aName)->getModule();
710 auto bModule = instanceGraph.lookup(bName)->getModule();
712 diag.attachNote(std::nullopt)
713 <<
"in instance " << a.getInstanceNameAttr() <<
" of " << aName
714 <<
", and instance " << b.getInstanceNameAttr() <<
" of " << bName;
715 check(diag, aModule, bModule);
723 if (a->getName() != b->getName()) {
724 diag.attachNote(a->getLoc()) <<
"first operation is a " << a->getName();
725 diag.attachNote(b->getLoc()) <<
"second operation is a " << b->getName();
731 if (
auto aInst = dyn_cast<FInstanceLike>(a)) {
732 auto bInst = cast<FInstanceLike>(b);
733 if (failed(check(diag, aInst, bInst)))
738 if (a->getNumResults() != b->getNumResults()) {
739 diag.attachNote(a->getLoc())
740 <<
"operations have different number of results";
741 diag.attachNote(b->getLoc()) <<
"second operation here";
744 for (
auto resultPair : llvm::zip(a->getResults(), b->getResults())) {
745 auto &aValue = std::get<0>(resultPair);
746 auto &bValue = std::get<1>(resultPair);
747 if (failed(check(diag,
"operation result", a, aValue.getType(), b,
750 data.map.map(aValue, bValue);
754 if (a->getNumOperands() != b->getNumOperands()) {
755 diag.attachNote(a->getLoc())
756 <<
"operations have different number of operands";
757 diag.attachNote(b->getLoc()) <<
"second operation here";
760 for (
auto operandPair : llvm::zip(a->getOperands(), b->getOperands())) {
761 auto &aValue = std::get<0>(operandPair);
762 auto &bValue = std::get<1>(operandPair);
763 if (bValue !=
data.map.lookup(aValue)) {
764 diag.attachNote(a->getLoc())
765 <<
"operations use different operands, first operand is '"
770 diag.attachNote(b->getLoc())
771 <<
"second operand is '"
775 <<
"', but should have been '"
786 if (a->getNumRegions() != b->getNumRegions()) {
787 diag.attachNote(a->getLoc())
788 <<
"operations have different number of regions";
789 diag.attachNote(b->getLoc()) <<
"second operation here";
792 for (
auto regionPair : llvm::zip(a->getRegions(), b->getRegions())) {
793 auto &aRegion = std::get<0>(regionPair);
794 auto &bRegion = std::get<1>(regionPair);
795 if (failed(check(diag,
data, a, aRegion, b, bRegion)))
800 if (failed(check(diag,
data, a, a->getAttrDictionary(), b,
801 b->getAttrDictionary())))
807 void check(InFlightDiagnostic &diag, Operation *a, Operation *b) {
808 hw::InnerSymbolTable aTable(a);
809 hw::InnerSymbolTable bTable(b);
812 diag.attachNote(a->getLoc()) <<
"module marked NoDedup";
816 diag.attachNote(b->getLoc()) <<
"module marked NoDedup";
819 auto aSymbol = cast<mlir::SymbolOpInterface>(a);
820 auto bSymbol = cast<mlir::SymbolOpInterface>(b);
822 diag.attachNote(a->getLoc())
824 << (aSymbol.isPrivate() ?
"private but not discardable" :
"public");
828 diag.attachNote(b->getLoc())
830 << (bSymbol.isPrivate() ?
"private but not discardable" :
"public");
834 dyn_cast_or_null<StringAttr>(a->getDiscardableAttr(dedupGroupAttrName));
835 auto bGroup = dyn_cast_or_null<StringAttr>(
836 b->getAttrOfType<StringAttr>(dedupGroupAttrName));
837 if (aGroup != bGroup) {
839 diag.attachNote(b->getLoc())
840 <<
"module is in dedup group '" << bGroup.str() <<
"'";
842 diag.attachNote(b->getLoc()) <<
"module is not part of a dedup group";
845 diag.attachNote(a->getLoc())
846 <<
"module is in dedup group '" << aGroup.str() <<
"'";
848 diag.attachNote(a->getLoc()) <<
"module is not part of a dedup group";
852 if (failed(check(diag,
data, a, b)))
854 diag.attachNote(a->getLoc()) <<
"first module here";
855 diag.attachNote(b->getLoc()) <<
"second module here";
879 static Location
mergeLoc(MLIRContext *context, Location to, Location from) {
881 llvm::SmallSetVector<Location, 4> decomposedLocs;
883 unsigned seenFIR = 0;
884 for (
auto loc : {to, from}) {
887 if (
auto fusedLoc = dyn_cast<FusedLoc>(loc)) {
890 for (
auto loc : fusedLoc.getLocations()) {
891 if (FileLineColLoc fileLoc = dyn_cast<FileLineColLoc>(loc)) {
892 if (fileLoc.getFilename().strref().ends_with(
".fir")) {
898 decomposedLocs.insert(loc);
904 if (FileLineColLoc fileLoc = dyn_cast<FileLineColLoc>(loc)) {
905 if (fileLoc.getFilename().strref().ends_with(
".fir")) {
912 if (!isa<UnknownLoc>(loc))
913 decomposedLocs.insert(loc);
916 auto locs = decomposedLocs.getArrayRef();
922 if (locs.size() == 1)
933 NLATable *nlaTable, CircuitOp circuit)
934 : context(circuit->getContext()), instanceGraph(instanceGraph),
935 symbolTable(symbolTable), nlaTable(nlaTable),
937 nonLocalString(StringAttr::
get(context,
"circt.nonlocal")),
938 classString(StringAttr::
get(context,
"class")) {
940 for (
auto nla : circuit.getOps<hw::HierPathOp>())
941 nlaCache[nla.getNamepathAttr()] = nla.getSymNameAttr();
948 void dedup(FModuleLike toModule, FModuleLike fromModule) {
954 SmallVector<Attribute> newLocs;
955 for (
auto [toLoc, fromLoc] : llvm::zip(toModule.getPortLocations(),
956 fromModule.getPortLocations())) {
957 if (toLoc == fromLoc)
958 newLocs.push_back(toLoc);
960 newLocs.push_back(
mergeLoc(context, cast<LocationAttr>(toLoc),
961 cast<LocationAttr>(fromLoc)));
963 toModule->setAttr(
"portLocations",
ArrayAttr::get(context, newLocs));
966 mergeOps(renameMap, toModule, toModule, fromModule, fromModule);
972 if (
auto to = dyn_cast<FModuleOp>(*toModule))
973 rewriteModuleNLAs(renameMap, to, cast<FModuleOp>(*fromModule));
975 rewriteExtModuleNLAs(renameMap, toModule.getModuleNameAttr(),
976 fromModule.getModuleNameAttr());
978 replaceInstances(toModule, fromModule);
985 recordAnnotations(module);
987 for (
unsigned i = 0, e =
getNumPorts(module); i < e; ++i)
990 module->walk([&](Operation *op) { recordAnnotations(op); });
996 return moduleNamespaces.try_emplace(module, cast<FModuleLike>(module))
1004 if (
auto nlaRef = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal"))
1005 targetMap[nlaRef.getAttr()].insert(target);
1014 auto mem = dyn_cast<MemOp>(op);
1019 for (
unsigned i = 0, e = mem->getNumResults(); i < e; ++i)
1028 instanceGraph[::cast<igraph::ModuleOpInterface>(fromModule)];
1029 auto *toNode = instanceGraph[toModule];
1031 for (
auto *oldInstRec : llvm::make_early_inc_range(fromNode->uses())) {
1032 auto inst = oldInstRec->getInstance();
1033 if (
auto instOp = dyn_cast<InstanceOp>(*inst)) {
1034 instOp.setModuleNameAttr(toModuleRef);
1035 instOp.setPortNamesAttr(toModule.getPortNamesAttr());
1036 }
else if (
auto objectOp = dyn_cast<ObjectOp>(*inst)) {
1037 auto classLike = cast<ClassLike>(*toNode->getModule());
1039 objectOp.getResult().setType(classType);
1041 oldInstRec->getParent()->addInstance(inst, toNode);
1042 oldInstRec->erase();
1044 instanceGraph.erase(fromNode);
1045 fromModule->erase();
1053 SmallVector<FlatSymbolRefAttr>
1054 createNLAs(Operation *fromModule, ArrayRef<Attribute> baseNamepath,
1055 SymbolTable::Visibility vis = SymbolTable::Visibility::Private) {
1058 SmallVector<Attribute> namepath = {
nullptr};
1059 namepath.append(baseNamepath.begin(), baseNamepath.end());
1061 auto loc = fromModule->getLoc();
1062 auto *fromNode = instanceGraph[cast<igraph::ModuleOpInterface>(fromModule)];
1063 SmallVector<FlatSymbolRefAttr> nlas;
1064 for (
auto *instanceRecord : fromNode->uses()) {
1065 auto parent = cast<FModuleOp>(*instanceRecord->getParent()->getModule());
1066 auto inst = instanceRecord->getInstance();
1070 auto &cacheEntry = nlaCache[arrayAttr];
1072 auto nla = OpBuilder::atBlockBegin(nlaBlock).create<hw::HierPathOp>(
1073 loc,
"nla", arrayAttr);
1075 symbolTable.insert(nla);
1077 cacheEntry = nla.getNameAttr();
1078 nla.setVisibility(vis);
1079 nlaTable->addNLA(nla);
1082 nlas.push_back(nlaRef);
1090 SmallVector<FlatSymbolRefAttr>
1092 SymbolTable::Visibility vis = SymbolTable::Visibility::Private) {
1100 Annotation anno, ArrayRef<NamedAttribute> attributes,
1101 unsigned nonLocalIndex,
1102 SmallVectorImpl<Annotation> &newAnnotations) {
1103 SmallVector<NamedAttribute> mutableAttributes(attributes.begin(),
1105 for (
auto &nla : nlas) {
1107 mutableAttributes[nonLocalIndex].setValue(nla);
1108 auto dict = DictionaryAttr::getWithSorted(context, mutableAttributes);
1111 newAnnotations.push_back(anno);
1120 targetMap.erase(nla.getNameAttr());
1121 nlaTable->erase(nla);
1122 nlaCache.erase(nla.getNamepathAttr());
1123 symbolTable.erase(nla);
1129 FModuleOp fromModule) {
1130 auto toName = toModule.getNameAttr();
1131 auto fromName = fromModule.getNameAttr();
1134 auto moduleNLAs = nlaTable->lookup(fromModule.getNameAttr()).vec();
1136 nlaTable->renameModuleAndInnerRef(toName, fromName, renameMap);
1139 for (
auto nla : moduleNLAs) {
1140 auto elements = nla.getNamepath().getValue();
1142 if (nla.root() != toName)
1145 SmallVector<Attribute> namepath(elements.begin(), elements.end());
1146 auto nlaRefs = createNLAs(fromModule, namepath, nla.getVisibility());
1148 auto &set = targetMap[nla.getSymNameAttr()];
1149 SmallVector<AnnoTarget> targets(set.begin(), set.end());
1151 for (
auto target : targets) {
1154 SmallVector<Annotation> newAnnotations;
1155 for (
auto anno : target.getAnnotations()) {
1157 auto [it, found] = mlir::impl::findAttrSorted(
1158 anno.begin(), anno.end(), nonLocalString);
1161 if (!found || cast<FlatSymbolRefAttr>(it->getValue()).getAttr() !=
1162 nla.getSymNameAttr()) {
1163 newAnnotations.push_back(anno);
1166 auto nonLocalIndex = std::distance(anno.begin(), it);
1168 cloneAnnotation(nlaRefs, anno,
1169 ArrayRef<NamedAttribute>(anno.begin(), anno.end()),
1170 nonLocalIndex, newAnnotations);
1175 target.setAnnotations(annotations);
1177 for (
auto nla : nlaRefs)
1178 targetMap[nla.getAttr()].insert(target);
1190 FModuleOp fromModule) {
1191 addAnnotationContext(renameMap, toModule, toModule);
1192 addAnnotationContext(renameMap, toModule, fromModule);
1198 StringAttr fromName) {
1199 nlaTable->renameModuleAndInnerRef(toName, fromName, renameMap);
1207 SmallVectorImpl<Annotation> &newAnnotations) {
1210 SmallVector<NamedAttribute> attributes;
1211 int nonLocalIndex = -1;
1212 for (
const auto &val : llvm::enumerate(anno)) {
1213 auto attr = val.value();
1215 auto compare = attr.getName().compare(nonLocalString);
1216 assert(compare != 0 &&
"should not pass non-local annotations here");
1220 nonLocalIndex = val.index();
1221 attributes.push_back(NamedAttribute(nonLocalString, nonLocalString));
1226 attributes.push_back(attr);
1228 if (nonLocalIndex == -1) {
1230 nonLocalIndex = attributes.size();
1231 attributes.push_back(NamedAttribute(nonLocalString, nonLocalString));
1234 attributes.append(anno.
begin() + nonLocalIndex, anno.
end());
1238 auto nlaRefs = createNLAs(toModuleName, fromModule);
1239 for (
auto nla : nlaRefs)
1240 targetMap[nla.getAttr()].insert(to);
1243 cloneAnnotation(nlaRefs, anno, attributes, nonLocalIndex, newAnnotations);
1249 SmallVectorImpl<Annotation> &newAnnotations,
1250 SmallPtrSetImpl<Attribute> &dontTouches) {
1251 for (
auto anno : annos) {
1255 anno.removeMember(
"circt.nonlocal");
1256 auto [it, inserted] = dontTouches.insert(anno.getAttr());
1258 newAnnotations.push_back(anno);
1263 if (
auto nla = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
1264 newAnnotations.push_back(anno);
1265 targetMap[nla.getAttr()].insert(to);
1269 makeAnnotationNonLocal(toModule.getModuleNameAttr(), to, fromModule, anno,
1280 SmallVector<Annotation> newAnnotations;
1284 llvm::SmallPtrSet<Attribute, 4> dontTouches;
1288 copyAnnotations(toModule, to, toModule, toAnnos, newAnnotations,
1290 copyAnnotations(toModule, to, fromModule, fromAnnos, newAnnotations,
1294 if (!newAnnotations.empty())
1300 FModuleLike fromModule, Operation *from) {
1306 if (toModule == to) {
1308 for (
unsigned i = 0, e =
getNumPorts(toModule); i < e; ++i)
1313 }
else if (
auto toMem = dyn_cast<MemOp>(to)) {
1315 auto fromMem = cast<MemOp>(from);
1316 for (
unsigned i = 0, e = toMem.getNumResults(); i < e; ++i)
1328 Operation *to, FModuleLike fromModule,
1335 return getNamespace(toModule);
1337 renameMap[fromSym] = toSym;
1341 auto fromPortSyms = from->getAttrOfType<ArrayAttr>(
"portSymbols");
1342 if (!fromPortSyms || fromPortSyms.empty())
1345 auto &moduleNamespace = getNamespace(toModule);
1346 auto portCount = fromPortSyms.size();
1347 auto portNames = to->getAttrOfType<ArrayAttr>(
"portNames");
1348 auto toPortSyms = to->getAttrOfType<ArrayAttr>(
"portSymbols");
1352 SmallVector<Attribute> newPortSyms;
1353 if (toPortSyms.empty())
1354 newPortSyms.assign(portCount, hw::InnerSymAttr());
1356 newPortSyms.assign(toPortSyms.begin(), toPortSyms.end());
1358 for (
unsigned portNo = 0; portNo < portCount; ++portNo) {
1360 if (!fromPortSyms[portNo])
1362 auto fromSym = cast<hw::InnerSymAttr>(fromPortSyms[portNo]);
1365 hw::InnerSymAttr toSym;
1366 if (!newPortSyms[portNo]) {
1368 StringRef symName =
"inner_sym";
1370 symName = cast<StringAttr>(portNames[portNo]).getValue();
1374 newPortSyms[portNo] = toSym;
1376 toSym = cast<hw::InnerSymAttr>(newPortSyms[portNo]);
1379 renameMap[fromSym.getSymName()] = toSym.getSymName();
1383 FModuleLike::fixupPortSymsArray(newPortSyms, toModule.getContext());
1384 cast<FModuleLike>(to).setPortSymbols(newPortSyms);
1390 FModuleLike fromModule, Operation *from) {
1392 if (to->getLoc() != from->getLoc())
1393 to->setLoc(
mergeLoc(context, to->getLoc(), from->getLoc()));
1396 for (
auto regions : llvm::zip(to->getRegions(), from->getRegions()))
1397 mergeRegions(renameMap, toModule, std::get<0>(regions), fromModule,
1398 std::get<1>(regions));
1401 recordSymRenames(renameMap, toModule, to, fromModule, from);
1404 mergeAnnotations(toModule, to, fromModule, from);
1409 FModuleLike fromModule, Block &fromBlock) {
1411 for (
auto [toArg, fromArg] :
1412 llvm::zip(toBlock.getArguments(), fromBlock.getArguments()))
1413 if (toArg.getLoc() != fromArg.getLoc())
1414 toArg.setLoc(
mergeLoc(context, toArg.getLoc(), fromArg.getLoc()));
1416 for (
auto ops : llvm::zip(toBlock, fromBlock))
1417 mergeOps(renameMap, toModule, &std::get<0>(ops), fromModule,
1423 Region &toRegion, FModuleLike fromModule,
1424 Region &fromRegion) {
1425 for (
auto blocks : llvm::zip(toRegion, fromRegion))
1426 mergeBlocks(renameMap, toModule, std::get<0>(blocks), fromModule,
1427 std::get<1>(blocks));
1441 DenseMap<Attribute, llvm::SmallDenseSet<AnnoTarget>>
targetMap;
1465 SmallVector<Attribute> newPortTypes;
1466 bool anyDifferences =
false;
1469 for (
size_t i = 0, e = classOp.getNumPorts(); i < e; ++i) {
1472 auto portClassType = dyn_cast<ClassType>(classOp.getPortType(i));
1473 if (!portClassType) {
1474 newPortTypes.push_back(classOp.getPortTypeAttr(i));
1479 Type newPortClassType;
1480 BlockArgument portArg = classOp.getArgument(i);
1481 for (
auto &use : portArg.getUses()) {
1482 if (
auto propassign = dyn_cast<PropAssignOp>(use.getOwner())) {
1483 Type sourceType = propassign.getSrc().getType();
1484 if (propassign.getDest() == use.get() && sourceType != portClassType) {
1486 if (newPortClassType) {
1487 assert(newPortClassType == sourceType &&
1488 "expected all references to be of the same type");
1492 newPortClassType = sourceType;
1499 if (!newPortClassType) {
1500 newPortTypes.push_back(classOp.getPortTypeAttr(i));
1506 classOp.getArgument(i).setType(newPortClassType);
1508 anyDifferences =
true;
1513 classOp.setPortTypes(newPortTypes);
1515 return anyDifferences;
1522 objectOp.getResult().setType(newClassType);
1530 auto dstType = dst.getType();
1531 auto srcType = src.getType();
1532 if (dstType == srcType) {
1538 auto dstBundle = type_cast<BundleType>(dstType);
1539 auto srcBundle = type_cast<BundleType>(srcType);
1540 for (
unsigned i = 0; i < dstBundle.getNumElements(); ++i) {
1541 auto dstField = builder.create<SubfieldOp>(dst, i);
1542 auto srcField = builder.create<SubfieldOp>(src, i);
1543 if (dstBundle.getElement(i).isFlip) {
1544 std::swap(srcBundle, dstBundle);
1545 std::swap(srcField, dstField);
1555 for (
auto *node : instanceGraph) {
1556 auto module = cast<FModuleLike>(*node->getModule());
1559 bool shouldFixupObjects =
false;
1560 auto classOp = dyn_cast<ClassOp>(module.getOperation());
1564 for (
auto *instRec : node->uses()) {
1567 if (shouldFixupObjects) {
1569 classOp.getInstanceType());
1574 auto inst = instRec->getInstance<InstanceOp>();
1578 ImplicitLocOpBuilder builder(inst.getLoc(), inst->getContext());
1579 builder.setInsertionPointAfter(inst);
1580 for (
size_t i = 0, e =
getNumPorts(module); i < e; ++i) {
1581 auto result = inst.getResult(i);
1582 auto newType = module.getPortType(i);
1583 auto oldType = result.getType();
1585 if (newType == oldType)
1590 builder.create<WireOp>(oldType, inst.getPortName(i)).getResult();
1591 result.replaceAllUsesWith(wire);
1592 result.setType(newType);
1610 std::array<uint8_t, 32> key;
1611 std::fill(key.begin(), key.end(), ~0);
1616 std::array<uint8_t, 32> key;
1617 std::fill(key.begin(), key.end(), ~0 - 1);
1625 std::memcpy(&hash, val.structuralHash.data(),
sizeof(
unsigned));
1629 hash, llvm::hash_combine_range(val.referredModuleNames.begin(),
1630 val.referredModuleNames.end()));
1633 static bool isEqual(
const ModuleInfo &lhs,
const ModuleInfo &rhs) {
1644 class DedupPass :
public circt::firrtl::impl::DedupBase<DedupPass> {
1645 void runOnOperation()
override {
1646 auto *context = &getContext();
1647 auto circuit = getOperation();
1648 auto &instanceGraph = getAnalysis<InstanceGraph>();
1649 auto *nlaTable = &getAnalysis<NLATable>();
1650 auto &symbolTable = getAnalysis<SymbolTable>();
1651 Deduper deduper(instanceGraph, symbolTable, nlaTable, circuit);
1653 auto anythingChanged =
false;
1662 llvm::DenseMap<ModuleInfo, Operation *> moduleInfoToModule;
1667 DenseMap<Attribute, StringAttr> dedupMap;
1672 SmallVector<FModuleLike, 0> modules(
1673 llvm::map_range(llvm::post_order(&instanceGraph), [](
auto *node) {
1674 return cast<FModuleLike>(*node->getModule());
1677 SmallVector<std::optional<ModuleInfo>> moduleInfos(modules.size());
1681 auto dedupGroupAttrName =
StringAttr::get(context,
"firrtl.dedup_group");
1687 for (
auto module : modules) {
1688 llvm::SmallSetVector<StringAttr, 1> groups;
1690 module, [&groups, dedupGroupClass](
Annotation annotation) {
1693 groups.insert(annotation.
getMember<StringAttr>(
"group"));
1696 if (groups.size() > 1) {
1697 module.emitError(
"module belongs to multiple dedup groups: ") << groups;
1698 return signalPassFailure();
1700 assert(!module->hasAttr(dedupGroupAttrName) &&
1701 "unexpected existing use of temporary dedup group attribute");
1702 if (!groups.empty())
1703 module->setDiscardableAttr(dedupGroupAttrName, groups.front());
1707 auto result = mlir::failableParallelForEach(
1708 context, llvm::seq(modules.size()), [&](
unsigned idx) {
1709 auto module = modules[idx];
1711 if (AnnotationSet::hasAnnotation(module, noDedupClass))
1715 if (auto ext = dyn_cast<FExtModuleOp>(*module);
1716 ext && !ext.getDefname().has_value())
1719 StructuralHasher hasher(hasherConstants);
1721 moduleInfos[idx] = hasher.getModuleInfo(module);
1725 if (result.failed())
1726 return signalPassFailure();
1728 for (
auto [i, module] : llvm::enumerate(modules)) {
1729 auto moduleName = module.getModuleNameAttr();
1730 auto &maybeModuleInfo = moduleInfos[i];
1732 if (!maybeModuleInfo) {
1737 dedupMap[moduleName] = moduleName;
1741 auto &moduleInfo = maybeModuleInfo.value();
1744 for (
auto &referredModule : moduleInfo.referredModuleNames)
1745 referredModule = dedupMap[referredModule];
1748 auto it = moduleInfoToModule.find(moduleInfo);
1749 if (it != moduleInfoToModule.end()) {
1750 auto original = cast<FModuleLike>(it->second);
1751 auto originalName = original.getModuleNameAttr();
1760 for (
auto &[originalName, dedupedName] : dedupMap)
1761 if (dedupedName == originalName)
1762 dedupedName = moduleName;
1765 it->second = module;
1767 std::swap(originalName, moduleName);
1768 std::swap(original, module);
1772 dedupMap[moduleName] = originalName;
1773 deduper.dedup(original, module);
1775 anythingChanged =
true;
1779 deduper.record(module);
1781 dedupMap[moduleName] = moduleName;
1783 moduleInfoToModule[std::move(moduleInfo)] = module;
1791 auto failed =
false;
1793 auto parseModule = [&](Attribute path) -> StringAttr {
1796 auto [_, rhs] = cast<StringAttr>(path).getValue().split(
'|');
1802 auto getLead = [&](StringAttr module) -> StringAttr {
1803 auto it = dedupMap.find(module);
1804 if (it == dedupMap.end()) {
1805 auto diag = emitError(circuit.getLoc(),
1806 "MustDeduplicateAnnotation references module ")
1807 << module <<
" which does not exist";
1817 auto modules = annotation.
getMember<ArrayAttr>(
"modules");
1819 emitError(circuit.getLoc(),
1820 "MustDeduplicateAnnotation missing \"modules\" member");
1825 if (modules.empty())
1828 auto firstModule = parseModule(modules[0]);
1829 auto firstLead = getLead(firstModule);
1833 for (
auto attr : modules.getValue().drop_front()) {
1834 auto nextModule = parseModule(attr);
1835 auto nextLead = getLead(nextModule);
1838 if (firstLead != nextLead) {
1839 auto diag = emitError(circuit.getLoc(),
"module ")
1840 << nextModule <<
" not deduplicated with " << firstModule;
1841 auto a = instanceGraph.lookup(firstLead)->getModule();
1842 auto b = instanceGraph.lookup(nextLead)->getModule();
1843 equiv.check(diag, a, b);
1851 return signalPassFailure();
1854 for (
auto module : circuit.getOps<FModuleLike>())
1855 module->removeDiscardableAttr(dedupGroupAttrName);
1862 markAnalysesPreserved<NLATable>();
1863 if (!anythingChanged)
1864 markAllAnalysesPreserved();
1870 return std::make_unique<DedupPass>();
assert(baseType &&"element must be base type")
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.
bool operator==(uint64_t a, const FVInt &b)
size_t hash_combine(size_t h1, size_t h2)
C++'s stdlib doesn't have a hash_combine function. This is a simple one.
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::vector< StringAttr > referredModuleNames
std::array< uint8_t, 32 > structuralHash
This struct contains constant string attributes shared across different threads.
StringAttr portSymbolsAttr
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(const void *pointer)
DenseMap< void *, unsigned > idTable
void update(Operation *op)
ModuleInfo getModuleInfo(FModuleLike module)
void update(size_t value)
unsigned getInnerSymID(StringAttr name)
void update(BundleType type)
unsigned getID(void *object)
void update(OpResult result)
void update(OpOperand &operand)
StructuralHasher(const StructuralHasherSharedConstants &constants)
void update(Region *region)
void update(Block *block)
std::vector< StringAttr > referredModuleNames
DenseMap< StringAttr, unsigned > innerSymIDTable
void update(TypeID typeID)
const StructuralHasherSharedConstants & constants
unsigned finalizeID(void *object)
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)