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;
59 return symbol.isPrivate() &&
60 (symbol.canDiscardOnUseEmpty() || isa<ClassLike>(*symbol));
67 llvm::raw_ostream &
printHex(llvm::raw_ostream &stream,
68 ArrayRef<uint8_t> bytes) {
70 return stream << format_bytes(bytes, std::nullopt, 32) <<
"\n";
73 llvm::raw_ostream &
printHash(llvm::raw_ostream &stream, llvm::SHA256 &data) {
77 llvm::raw_ostream &
printHash(llvm::raw_ostream &stream, std::string data) {
78 ArrayRef<uint8_t> bytes(
reinterpret_cast<const uint8_t *
>(
data.c_str()),
118 nonessentialAttributes.insert(
StringAttr::get(context,
"annotations"));
120 nonessentialAttributes.insert(
StringAttr::get(context,
"portAnnotations"));
122 nonessentialAttributes.insert(
StringAttr::get(context,
"portLocations"));
147 : constants(constants){};
149 std::pair<std::array<uint8_t, 32>, SmallVector<StringAttr>>
152 auto hash = sha.final();
153 return {hash, referredModuleNames};
158 auto *
addr =
reinterpret_cast<const uint8_t *
>(&pointer);
159 sha.update(ArrayRef<uint8_t>(
addr,
sizeof pointer));
163 auto *
addr =
reinterpret_cast<const uint8_t *
>(&value);
164 sha.update(ArrayRef<uint8_t>(
addr,
sizeof value));
171 update(type.getTypeID());
172 for (
auto &element : type.getElements()) {
173 update(element.isFlip);
174 update(element.type);
180 if (
auto bundle = type_dyn_cast<BundleType>(type))
181 return update(bundle);
182 update(type.getAsOpaquePointer());
186 auto size = indices.size();
187 assert(!indices.contains(address));
188 indices[address] = size;
193 if (
auto arg = dyn_cast<BlockArgument>(val))
194 return {indices.at(arg.getOwner()), arg.getArgNumber()};
195 auto result = cast<OpResult>(val);
196 return {indices.at(result.getOwner()), result.getResultNumber()};
203 if (
auto objectOp = dyn_cast<ObjectOp>(result.getOwner())) {
204 referredModuleNames.push_back(objectOp.getType().getNameAttr().getAttr());
208 update(result.getType());
218 void update(Operation *op, hw::InnerSymAttr attr) {
219 for (
auto props : attr)
220 innerSymTargets[props.getName()] =
224 void update(Value value, hw::InnerSymAttr attr) {
225 for (
auto props : attr)
226 innerSymTargets[props.getName()] =
231 update(target.
index);
237 auto it = innerSymTargets.find(attr.getName());
238 assert(it != innerSymTargets.end() &&
239 "inner symbol should have been previously hashed");
240 update(attr.getTypeID());
246 void update(Operation *op, DictionaryAttr dict) {
247 for (
auto namedAttr : dict) {
248 auto name = namedAttr.getName();
249 auto value = namedAttr.getValue();
252 bool isClassPortNames =
253 isa<ClassLike>(op) && name == constants.portNamesAttr;
254 if (constants.nonessentialAttributes.contains(name) && !isClassPortNames)
258 if (name == constants.portTypesAttr) {
259 auto portTypes = cast<ArrayAttr>(value).getAsValueRange<TypeAttr>();
260 for (
auto type : portTypes)
266 if (name == constants.portSymsAttr) {
267 if (op->getNumRegions() != 1)
269 auto ®ion = op->getRegion(0);
270 if (region.getBlocks().empty())
272 auto *block = ®ion.front();
273 auto syms = cast<ArrayAttr>(value).getAsRange<hw::InnerSymAttr>();
276 for (
auto [arg, sym] : llvm::zip_equal(block->getArguments(), syms))
280 if (name == constants.innerSymAttr) {
281 auto innerSym = cast<hw::InnerSymAttr>(value);
282 update(op, innerSym);
291 if (isa<InstanceOp>(op) && name == constants.moduleNameAttr) {
292 referredModuleNames.push_back(cast<FlatSymbolRefAttr>(value).
getAttr());
297 update(name.getAsOpaquePointer());
301 if (isa<DistinctAttr>(value))
305 if (
auto innerRef = dyn_cast<hw::InnerRefAttr>(value))
308 update(value.getAsOpaquePointer());
314 update(name.getAsOpaquePointer());
320 update(op->getName());
323 for (
auto &operand : op->getOpOperands())
327 for (
auto ®ion : op->getRegions())
328 for (
auto &block : region.getBlocks())
333 update(op, op->getAttrDictionary());
337 update(op->getNumRegions());
338 for (
auto ®ion : op->getRegions()) {
339 update(region.getBlocks().size());
340 for (
auto &block : region.getBlocks()) {
341 update(indices.at(&block));
342 for (
auto argType : block.getArgumentTypes())
344 for (
auto &op : block)
350 for (
auto result : op->getResults())
380 : instanceGraph(instanceGraph) {
384 nonessentialAttributes.insert(
StringAttr::get(context,
"annotations"));
386 nonessentialAttributes.insert(
StringAttr::get(context,
"portAnnotations"));
390 nonessentialAttributes.insert(
StringAttr::get(context,
"portLocations"));
396 ModuleData(
const hw::InnerSymbolTable &a,
const hw::InnerSymbolTable &b)
399 const hw::InnerSymbolTable &
a;
400 const hw::InnerSymbolTable &
b;
404 SmallString<64> buffer;
405 llvm::raw_svector_ostream os(buffer);
406 if (
auto integerAttr = dyn_cast<IntegerAttr>(attr)) {
408 if (integerAttr.getType().isSignlessInteger())
409 integerAttr.getValue().toStringUnsigned(buffer, 16);
411 integerAttr.getAPSInt().toString(buffer, 16);
415 return std::string(buffer);
419 LogicalResult
check(InFlightDiagnostic &diag,
const Twine &message,
420 Operation *a, BundleType aType, Operation *b,
422 if (aType.getNumElements() != bType.getNumElements()) {
423 diag.attachNote(a->getLoc())
424 << message <<
" bundle type has different number of elements";
425 diag.attachNote(b->getLoc()) <<
"second operation here";
429 for (
auto elementPair :
430 llvm::zip(aType.getElements(), bType.getElements())) {
431 auto aElement = std::get<0>(elementPair);
432 auto bElement = std::get<1>(elementPair);
433 if (aElement.isFlip != bElement.isFlip) {
434 diag.attachNote(a->getLoc()) << message <<
" bundle element "
435 << aElement.name <<
" flip does not match";
436 diag.attachNote(b->getLoc()) <<
"second operation here";
440 if (failed(check(diag,
441 "bundle element \'" + aElement.name.getValue() +
"'", a,
442 aElement.type, b, bElement.type)))
448 LogicalResult
check(InFlightDiagnostic &diag,
const Twine &message,
449 Operation *a, Type aType, Operation *b, Type bType) {
452 if (
auto aBundleType = type_dyn_cast<BundleType>(aType))
453 if (
auto bBundleType = type_dyn_cast<BundleType>(bType))
454 return check(diag, message, a, aBundleType, b, bBundleType);
455 if (type_isa<RefType>(aType) && type_isa<RefType>(bType) &&
457 diag.attachNote(a->getLoc())
458 << message <<
", has a RefType with a different base type "
459 << type_cast<RefType>(aType).getType()
460 <<
" in the same position of the two modules marked as 'must dedup'. "
461 "(This may be due to Grand Central Taps or Views being different "
462 "between the two modules.)";
463 diag.attachNote(b->getLoc())
464 <<
"the second module has a different base type "
465 << type_cast<RefType>(bType).getType();
468 diag.attachNote(a->getLoc())
469 << message <<
" types don't match, first type is " << aType;
470 diag.attachNote(b->getLoc()) <<
"second type is " << bType;
475 Block &aBlock, Operation *b, Block &bBlock) {
478 auto portNames = a->getAttrOfType<ArrayAttr>(
"portNames");
480 auto emitMissingPort = [&](Value existsVal, Operation *opExists,
481 Operation *opDoesNotExist) {
483 auto portNames = opExists->getAttrOfType<ArrayAttr>(
"portNames");
485 if (
auto portNameAttr = dyn_cast<StringAttr>(portNames[portNo]))
486 portName = portNameAttr.getValue();
487 if (type_isa<RefType>(existsVal.getType())) {
488 diag.attachNote(opExists->getLoc())
489 <<
" contains a RefType port named '" + portName +
490 "' that only exists in one of the modules (can be due to "
491 "difference in Grand Central Tap or View of two modules "
492 "marked with must dedup)";
493 diag.attachNote(opDoesNotExist->getLoc())
494 <<
"second module to be deduped that does not have the RefType "
497 diag.attachNote(opExists->getLoc())
498 <<
"port '" + portName +
"' only exists in one of the modules";
499 diag.attachNote(opDoesNotExist->getLoc())
500 <<
"second module to be deduped that does not have the port";
506 llvm::zip_longest(aBlock.getArguments(), bBlock.getArguments())) {
507 auto &aArg = std::get<0>(argPair);
508 auto &bArg = std::get<1>(argPair);
509 if (aArg.has_value() && bArg.has_value()) {
514 if (
auto portNameAttr = dyn_cast<StringAttr>(portNames[portNo]))
515 portName = portNameAttr.getValue();
518 if (failed(check(diag,
"module port '" + portName +
"'", a,
519 aArg->getType(), b, bArg->getType())))
521 data.map.map(aArg.value(), bArg.value());
525 if (!aArg.has_value())
527 return emitMissingPort(aArg.has_value() ? aArg.value() : bArg.value(), a,
532 auto aIt = aBlock.begin();
533 auto aEnd = aBlock.end();
534 auto bIt = bBlock.begin();
535 auto bEnd = bBlock.end();
536 while (aIt != aEnd && bIt != bEnd)
537 if (failed(check(diag,
data, &*aIt++, &*bIt++)))
540 diag.attachNote(aIt->getLoc()) <<
"first block has more operations";
541 diag.attachNote(b->getLoc()) <<
"second block here";
545 diag.attachNote(bIt->getLoc()) <<
"second block has more operations";
546 diag.attachNote(a->getLoc()) <<
"first block here";
553 Region &aRegion, Operation *b, Region &bRegion) {
554 auto aIt = aRegion.begin();
555 auto aEnd = aRegion.end();
556 auto bIt = bRegion.begin();
557 auto bEnd = bRegion.end();
560 while (aIt != aEnd && bIt != bEnd)
561 if (failed(check(diag,
data, a, *aIt++, b, *bIt++)))
563 if (aIt != aEnd || bIt != bEnd) {
564 diag.attachNote(a->getLoc())
565 <<
"operation regions have different number of blocks";
566 diag.attachNote(b->getLoc()) <<
"second operation here";
572 LogicalResult
check(InFlightDiagnostic &diag, Operation *a,
573 mlir::DenseBoolArrayAttr aAttr, Operation *b,
574 mlir::DenseBoolArrayAttr bAttr) {
577 auto portNames = a->getAttrOfType<ArrayAttr>(
"portNames");
578 for (
unsigned i = 0, e = aAttr.size(); i < e; ++i) {
579 auto aDirection = aAttr[i];
580 auto bDirection = bAttr[i];
581 if (aDirection != bDirection) {
582 auto ¬e = diag.attachNote(a->getLoc()) <<
"module port ";
584 note <<
"'" << cast<StringAttr>(portNames[i]).getValue() <<
"'";
587 note <<
" directions don't match, first direction is '"
589 diag.attachNote(b->getLoc()) <<
"second direction is '"
598 DictionaryAttr aDict, Operation *b,
599 DictionaryAttr bDict) {
604 DenseSet<Attribute> seenAttrs;
605 for (
auto namedAttr : aDict) {
606 auto attrName = namedAttr.getName();
607 if (nonessentialAttributes.contains(attrName))
610 auto aAttr = namedAttr.getValue();
611 auto bAttr = bDict.get(attrName);
613 diag.attachNote(a->getLoc())
614 <<
"second operation is missing attribute " << attrName;
615 diag.attachNote(b->getLoc()) <<
"second operation here";
619 if (isa<hw::InnerRefAttr>(aAttr) && isa<hw::InnerRefAttr>(bAttr)) {
620 auto bRef = cast<hw::InnerRefAttr>(bAttr);
621 auto aRef = cast<hw::InnerRefAttr>(aAttr);
623 auto aTarget =
data.a.lookup(aRef.getName());
624 auto bTarget =
data.b.lookup(bRef.getName());
625 if (!aTarget || !bTarget)
626 diag.attachNote(a->getLoc())
627 <<
"malformed ir, possibly violating use-before-def";
629 diag.attachNote(a->getLoc())
630 <<
"operations have different targets, first operation has "
632 diag.attachNote(b->getLoc()) <<
"second operation has " << bTarget;
635 if (aTarget.isPort()) {
637 if (!bTarget.isPort() || aTarget.getPort() != bTarget.getPort())
641 if (!bTarget.isOpOnly() ||
642 aTarget.getOp() !=
data.map.lookup(bTarget.getOp()))
645 if (aTarget.getField() != bTarget.getField())
647 }
else if (attrName == portDirectionsAttr) {
650 if (failed(check(diag, a, cast<mlir::DenseBoolArrayAttr>(aAttr), b,
651 cast<mlir::DenseBoolArrayAttr>(bAttr))))
653 }
else if (isa<DistinctAttr>(aAttr) && isa<DistinctAttr>(bAttr)) {
656 }
else if (aAttr != bAttr) {
657 diag.attachNote(a->getLoc())
658 <<
"first operation has attribute '" << attrName.getValue()
659 <<
"' with value " << prettyPrint(aAttr);
660 diag.attachNote(b->getLoc())
661 <<
"second operation has value " << prettyPrint(bAttr);
664 seenAttrs.insert(attrName);
666 if (aDict.getValue().size() != bDict.getValue().size()) {
667 for (
auto namedAttr : bDict) {
668 auto attrName = namedAttr.getName();
671 if (nonessentialAttributes.contains(attrName) ||
672 seenAttrs.contains(attrName))
675 diag.attachNote(a->getLoc())
676 <<
"first operation is missing attribute " << attrName;
677 diag.attachNote(b->getLoc()) <<
"second operation here";
685 LogicalResult
check(InFlightDiagnostic &diag, FInstanceLike a,
687 auto aName = a.getReferencedModuleNameAttr();
688 auto bName = b.getReferencedModuleNameAttr();
695 auto aModule = instanceGraph.lookup(aName)->getModule();
696 auto bModule = instanceGraph.lookup(bName)->getModule();
698 diag.attachNote(std::nullopt)
699 <<
"in instance " << a.getInstanceNameAttr() <<
" of " << aName
700 <<
", and instance " << b.getInstanceNameAttr() <<
" of " << bName;
701 check(diag, aModule, bModule);
709 if (a->getName() != b->getName()) {
710 diag.attachNote(a->getLoc()) <<
"first operation is a " << a->getName();
711 diag.attachNote(b->getLoc()) <<
"second operation is a " << b->getName();
717 if (
auto aInst = dyn_cast<FInstanceLike>(a)) {
718 auto bInst = cast<FInstanceLike>(b);
719 if (failed(check(diag, aInst, bInst)))
724 if (a->getNumResults() != b->getNumResults()) {
725 diag.attachNote(a->getLoc())
726 <<
"operations have different number of results";
727 diag.attachNote(b->getLoc()) <<
"second operation here";
730 for (
auto resultPair : llvm::zip(a->getResults(), b->getResults())) {
731 auto &aValue = std::get<0>(resultPair);
732 auto &bValue = std::get<1>(resultPair);
733 if (failed(check(diag,
"operation result", a, aValue.getType(), b,
736 data.map.map(aValue, bValue);
740 if (a->getNumOperands() != b->getNumOperands()) {
741 diag.attachNote(a->getLoc())
742 <<
"operations have different number of operands";
743 diag.attachNote(b->getLoc()) <<
"second operation here";
746 for (
auto operandPair : llvm::zip(a->getOperands(), b->getOperands())) {
747 auto &aValue = std::get<0>(operandPair);
748 auto &bValue = std::get<1>(operandPair);
749 if (bValue !=
data.map.lookup(aValue)) {
750 diag.attachNote(a->getLoc())
751 <<
"operations use different operands, first operand is '"
756 diag.attachNote(b->getLoc())
757 <<
"second operand is '"
761 <<
"', but should have been '"
772 if (a->getNumRegions() != b->getNumRegions()) {
773 diag.attachNote(a->getLoc())
774 <<
"operations have different number of regions";
775 diag.attachNote(b->getLoc()) <<
"second operation here";
778 for (
auto regionPair : llvm::zip(a->getRegions(), b->getRegions())) {
779 auto &aRegion = std::get<0>(regionPair);
780 auto &bRegion = std::get<1>(regionPair);
781 if (failed(check(diag,
data, a, aRegion, b, bRegion)))
786 if (failed(check(diag,
data, a, a->getAttrDictionary(), b,
787 b->getAttrDictionary())))
793 void check(InFlightDiagnostic &diag, Operation *a, Operation *b) {
794 hw::InnerSymbolTable aTable(a);
795 hw::InnerSymbolTable bTable(b);
798 diag.attachNote(a->getLoc()) <<
"module marked NoDedup";
802 diag.attachNote(b->getLoc()) <<
"module marked NoDedup";
805 auto aSymbol = cast<mlir::SymbolOpInterface>(a);
806 auto bSymbol = cast<mlir::SymbolOpInterface>(b);
808 diag.attachNote(a->getLoc())
810 << (aSymbol.isPrivate() ?
"private but not discardable" :
"public");
814 diag.attachNote(b->getLoc())
816 << (bSymbol.isPrivate() ?
"private but not discardable" :
"public");
820 dyn_cast_or_null<StringAttr>(a->getDiscardableAttr(dedupGroupAttrName));
821 auto bGroup = dyn_cast_or_null<StringAttr>(
822 b->getAttrOfType<StringAttr>(dedupGroupAttrName));
823 if (aGroup != bGroup) {
825 diag.attachNote(b->getLoc())
826 <<
"module is in dedup group '" << bGroup.str() <<
"'";
828 diag.attachNote(b->getLoc()) <<
"module is not part of a dedup group";
831 diag.attachNote(a->getLoc())
832 <<
"module is in dedup group '" << aGroup.str() <<
"'";
834 diag.attachNote(a->getLoc()) <<
"module is not part of a dedup group";
838 if (failed(check(diag,
data, a, b)))
840 diag.attachNote(a->getLoc()) <<
"first module here";
841 diag.attachNote(b->getLoc()) <<
"second module here";
865 static Location
mergeLoc(MLIRContext *context, Location to, Location from) {
867 llvm::SmallSetVector<Location, 4> decomposedLocs;
869 unsigned seenFIR = 0;
870 for (
auto loc : {to, from}) {
873 if (
auto fusedLoc = dyn_cast<FusedLoc>(loc)) {
876 for (
auto loc : fusedLoc.getLocations()) {
877 if (FileLineColLoc fileLoc = dyn_cast<FileLineColLoc>(loc)) {
878 if (fileLoc.getFilename().strref().ends_with(
".fir")) {
884 decomposedLocs.insert(loc);
890 if (FileLineColLoc fileLoc = dyn_cast<FileLineColLoc>(loc)) {
891 if (fileLoc.getFilename().strref().ends_with(
".fir")) {
898 if (!isa<UnknownLoc>(loc))
899 decomposedLocs.insert(loc);
902 auto locs = decomposedLocs.getArrayRef();
908 if (locs.size() == 1)
919 NLATable *nlaTable, CircuitOp circuit)
920 : context(circuit->getContext()), instanceGraph(instanceGraph),
921 symbolTable(symbolTable), nlaTable(nlaTable),
923 nonLocalString(StringAttr::
get(context,
"circt.nonlocal")),
924 classString(StringAttr::
get(context,
"class")) {
926 for (
auto nla : circuit.getOps<hw::HierPathOp>())
927 nlaCache[nla.getNamepathAttr()] = nla.getSymNameAttr();
934 void dedup(FModuleLike toModule, FModuleLike fromModule) {
940 SmallVector<Attribute> newLocs;
941 for (
auto [toLoc, fromLoc] : llvm::zip(toModule.getPortLocations(),
942 fromModule.getPortLocations())) {
943 if (toLoc == fromLoc)
944 newLocs.push_back(toLoc);
946 newLocs.push_back(
mergeLoc(context, cast<LocationAttr>(toLoc),
947 cast<LocationAttr>(fromLoc)));
949 toModule->setAttr(
"portLocations",
ArrayAttr::get(context, newLocs));
952 mergeOps(renameMap, toModule, toModule, fromModule, fromModule);
958 if (
auto to = dyn_cast<FModuleOp>(*toModule))
959 rewriteModuleNLAs(renameMap, to, cast<FModuleOp>(*fromModule));
961 rewriteExtModuleNLAs(renameMap, toModule.getModuleNameAttr(),
962 fromModule.getModuleNameAttr());
964 replaceInstances(toModule, fromModule);
971 recordAnnotations(module);
973 for (
unsigned i = 0, e =
getNumPorts(module); i < e; ++i)
976 module->walk([&](Operation *op) { recordAnnotations(op); });
982 return moduleNamespaces.try_emplace(module, cast<FModuleLike>(module))
990 if (
auto nlaRef = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal"))
991 targetMap[nlaRef.getAttr()].insert(target);
1000 auto mem = dyn_cast<MemOp>(op);
1005 for (
unsigned i = 0, e = mem->getNumResults(); i < e; ++i)
1014 instanceGraph[::cast<igraph::ModuleOpInterface>(fromModule)];
1015 auto *toNode = instanceGraph[toModule];
1017 for (
auto *oldInstRec : llvm::make_early_inc_range(fromNode->uses())) {
1018 auto inst = oldInstRec->getInstance();
1019 if (
auto instOp = dyn_cast<InstanceOp>(*inst)) {
1020 instOp.setModuleNameAttr(toModuleRef);
1021 instOp.setPortNamesAttr(toModule.getPortNamesAttr());
1022 }
else if (
auto objectOp = dyn_cast<ObjectOp>(*inst)) {
1023 auto classLike = cast<ClassLike>(*toNode->getModule());
1025 objectOp.getResult().setType(classType);
1027 oldInstRec->getParent()->addInstance(inst, toNode);
1028 oldInstRec->erase();
1030 instanceGraph.erase(fromNode);
1031 fromModule->erase();
1039 SmallVector<FlatSymbolRefAttr>
1040 createNLAs(Operation *fromModule, ArrayRef<Attribute> baseNamepath,
1041 SymbolTable::Visibility vis = SymbolTable::Visibility::Private) {
1044 SmallVector<Attribute> namepath = {
nullptr};
1045 namepath.append(baseNamepath.begin(), baseNamepath.end());
1047 auto loc = fromModule->getLoc();
1048 auto *fromNode = instanceGraph[cast<igraph::ModuleOpInterface>(fromModule)];
1049 SmallVector<FlatSymbolRefAttr> nlas;
1050 for (
auto *instanceRecord : fromNode->uses()) {
1051 auto parent = cast<FModuleOp>(*instanceRecord->getParent()->getModule());
1052 auto inst = instanceRecord->getInstance();
1056 auto &cacheEntry = nlaCache[arrayAttr];
1058 auto nla = OpBuilder::atBlockBegin(nlaBlock).create<hw::HierPathOp>(
1059 loc,
"nla", arrayAttr);
1061 symbolTable.insert(nla);
1063 cacheEntry = nla.getNameAttr();
1064 nla.setVisibility(vis);
1065 nlaTable->addNLA(nla);
1068 nlas.push_back(nlaRef);
1076 SmallVector<FlatSymbolRefAttr>
1078 SymbolTable::Visibility vis = SymbolTable::Visibility::Private) {
1086 Annotation anno, ArrayRef<NamedAttribute> attributes,
1087 unsigned nonLocalIndex,
1088 SmallVectorImpl<Annotation> &newAnnotations) {
1089 SmallVector<NamedAttribute> mutableAttributes(attributes.begin(),
1091 for (
auto &nla : nlas) {
1093 mutableAttributes[nonLocalIndex].setValue(nla);
1094 auto dict = DictionaryAttr::getWithSorted(context, mutableAttributes);
1097 newAnnotations.push_back(anno);
1106 targetMap.erase(nla.getNameAttr());
1107 nlaTable->erase(nla);
1108 nlaCache.erase(nla.getNamepathAttr());
1109 symbolTable.erase(nla);
1115 FModuleOp fromModule) {
1116 auto toName = toModule.getNameAttr();
1117 auto fromName = fromModule.getNameAttr();
1120 auto moduleNLAs = nlaTable->lookup(fromModule.getNameAttr()).vec();
1122 nlaTable->renameModuleAndInnerRef(toName, fromName, renameMap);
1125 for (
auto nla : moduleNLAs) {
1126 auto elements = nla.getNamepath().getValue();
1128 if (nla.root() != toName)
1131 SmallVector<Attribute> namepath(elements.begin(), elements.end());
1132 auto nlaRefs = createNLAs(fromModule, namepath, nla.getVisibility());
1134 auto &set = targetMap[nla.getSymNameAttr()];
1135 SmallVector<AnnoTarget> targets(set.begin(), set.end());
1137 for (
auto target : targets) {
1140 SmallVector<Annotation> newAnnotations;
1141 for (
auto anno : target.getAnnotations()) {
1143 auto [it, found] = mlir::impl::findAttrSorted(
1144 anno.begin(), anno.end(), nonLocalString);
1147 if (!found || cast<FlatSymbolRefAttr>(it->getValue()).getAttr() !=
1148 nla.getSymNameAttr()) {
1149 newAnnotations.push_back(anno);
1152 auto nonLocalIndex = std::distance(anno.begin(), it);
1154 cloneAnnotation(nlaRefs, anno,
1155 ArrayRef<NamedAttribute>(anno.begin(), anno.end()),
1156 nonLocalIndex, newAnnotations);
1161 target.setAnnotations(annotations);
1163 for (
auto nla : nlaRefs)
1164 targetMap[nla.getAttr()].insert(target);
1176 FModuleOp fromModule) {
1177 addAnnotationContext(renameMap, toModule, toModule);
1178 addAnnotationContext(renameMap, toModule, fromModule);
1184 StringAttr fromName) {
1185 nlaTable->renameModuleAndInnerRef(toName, fromName, renameMap);
1193 SmallVectorImpl<Annotation> &newAnnotations) {
1196 SmallVector<NamedAttribute> attributes;
1197 int nonLocalIndex = -1;
1198 for (
const auto &val : llvm::enumerate(anno)) {
1199 auto attr = val.value();
1201 auto compare = attr.getName().compare(nonLocalString);
1202 assert(compare != 0 &&
"should not pass non-local annotations here");
1206 nonLocalIndex = val.index();
1207 attributes.push_back(NamedAttribute(nonLocalString, nonLocalString));
1212 attributes.push_back(attr);
1214 if (nonLocalIndex == -1) {
1216 nonLocalIndex = attributes.size();
1217 attributes.push_back(NamedAttribute(nonLocalString, nonLocalString));
1220 attributes.append(anno.
begin() + nonLocalIndex, anno.
end());
1224 auto nlaRefs = createNLAs(toModuleName, fromModule);
1225 for (
auto nla : nlaRefs)
1226 targetMap[nla.getAttr()].insert(to);
1229 cloneAnnotation(nlaRefs, anno, attributes, nonLocalIndex, newAnnotations);
1235 SmallVectorImpl<Annotation> &newAnnotations,
1236 SmallPtrSetImpl<Attribute> &dontTouches) {
1237 for (
auto anno : annos) {
1241 anno.removeMember(
"circt.nonlocal");
1242 auto [it, inserted] = dontTouches.insert(anno.getAttr());
1244 newAnnotations.push_back(anno);
1249 if (
auto nla = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
1250 newAnnotations.push_back(anno);
1251 targetMap[nla.getAttr()].insert(to);
1255 makeAnnotationNonLocal(toModule.getModuleNameAttr(), to, fromModule, anno,
1266 SmallVector<Annotation> newAnnotations;
1270 llvm::SmallPtrSet<Attribute, 4> dontTouches;
1274 copyAnnotations(toModule, to, toModule, toAnnos, newAnnotations,
1276 copyAnnotations(toModule, to, fromModule, fromAnnos, newAnnotations,
1280 if (!newAnnotations.empty())
1286 FModuleLike fromModule, Operation *from) {
1292 if (toModule == to) {
1294 for (
unsigned i = 0, e =
getNumPorts(toModule); i < e; ++i)
1299 }
else if (
auto toMem = dyn_cast<MemOp>(to)) {
1301 auto fromMem = cast<MemOp>(from);
1302 for (
unsigned i = 0, e = toMem.getNumResults(); i < e; ++i)
1314 Operation *to, FModuleLike fromModule,
1321 return getNamespace(toModule);
1323 renameMap[fromSym] = toSym;
1327 auto fromPortSyms = from->getAttrOfType<ArrayAttr>(
"portSyms");
1328 if (!fromPortSyms || fromPortSyms.empty())
1331 auto &moduleNamespace = getNamespace(toModule);
1332 auto portCount = fromPortSyms.size();
1333 auto portNames = to->getAttrOfType<ArrayAttr>(
"portNames");
1334 auto toPortSyms = to->getAttrOfType<ArrayAttr>(
"portSyms");
1338 SmallVector<Attribute> newPortSyms;
1339 if (toPortSyms.empty())
1340 newPortSyms.assign(portCount, hw::InnerSymAttr());
1342 newPortSyms.assign(toPortSyms.begin(), toPortSyms.end());
1344 for (
unsigned portNo = 0; portNo < portCount; ++portNo) {
1346 if (!fromPortSyms[portNo])
1348 auto fromSym = cast<hw::InnerSymAttr>(fromPortSyms[portNo]);
1351 hw::InnerSymAttr toSym;
1352 if (!newPortSyms[portNo]) {
1354 StringRef symName =
"inner_sym";
1356 symName = cast<StringAttr>(portNames[portNo]).getValue();
1360 newPortSyms[portNo] = toSym;
1362 toSym = cast<hw::InnerSymAttr>(newPortSyms[portNo]);
1365 renameMap[fromSym.getSymName()] = toSym.getSymName();
1369 cast<FModuleLike>(to).setPortSymbols(newPortSyms);
1375 FModuleLike fromModule, Operation *from) {
1377 if (to->getLoc() != from->getLoc())
1378 to->setLoc(
mergeLoc(context, to->getLoc(), from->getLoc()));
1381 for (
auto regions : llvm::zip(to->getRegions(), from->getRegions()))
1382 mergeRegions(renameMap, toModule, std::get<0>(regions), fromModule,
1383 std::get<1>(regions));
1386 recordSymRenames(renameMap, toModule, to, fromModule, from);
1389 mergeAnnotations(toModule, to, fromModule, from);
1394 FModuleLike fromModule, Block &fromBlock) {
1396 for (
auto [toArg, fromArg] :
1397 llvm::zip(toBlock.getArguments(), fromBlock.getArguments()))
1398 if (toArg.getLoc() != fromArg.getLoc())
1399 toArg.setLoc(
mergeLoc(context, toArg.getLoc(), fromArg.getLoc()));
1401 for (
auto ops : llvm::zip(toBlock, fromBlock))
1402 mergeOps(renameMap, toModule, &std::get<0>(ops), fromModule,
1408 Region &toRegion, FModuleLike fromModule,
1409 Region &fromRegion) {
1410 for (
auto blocks : llvm::zip(toRegion, fromRegion))
1411 mergeBlocks(renameMap, toModule, std::get<0>(blocks), fromModule,
1412 std::get<1>(blocks));
1426 DenseMap<Attribute, llvm::SmallDenseSet<AnnoTarget>>
targetMap;
1450 SmallVector<Attribute> newPortTypes;
1451 bool anyDifferences =
false;
1454 for (
size_t i = 0, e = classOp.getNumPorts(); i < e; ++i) {
1457 auto portClassType = dyn_cast<ClassType>(classOp.getPortType(i));
1458 if (!portClassType) {
1459 newPortTypes.push_back(classOp.getPortTypeAttr(i));
1464 Type newPortClassType;
1465 BlockArgument portArg = classOp.getArgument(i);
1466 for (
auto &use : portArg.getUses()) {
1467 if (
auto propassign = dyn_cast<PropAssignOp>(use.getOwner())) {
1468 Type sourceType = propassign.getSrc().getType();
1469 if (propassign.getDest() == use.get() && sourceType != portClassType) {
1471 if (newPortClassType) {
1472 assert(newPortClassType == sourceType &&
1473 "expected all references to be of the same type");
1477 newPortClassType = sourceType;
1484 if (!newPortClassType) {
1485 newPortTypes.push_back(classOp.getPortTypeAttr(i));
1491 classOp.getArgument(i).setType(newPortClassType);
1493 anyDifferences =
true;
1498 classOp.setPortTypes(newPortTypes);
1500 return anyDifferences;
1507 objectOp.getResult().setType(newClassType);
1515 auto dstType = dst.getType();
1516 auto srcType = src.getType();
1517 if (dstType == srcType) {
1523 auto dstBundle = type_cast<BundleType>(dstType);
1524 auto srcBundle = type_cast<BundleType>(srcType);
1525 for (
unsigned i = 0; i < dstBundle.getNumElements(); ++i) {
1526 auto dstField = builder.create<SubfieldOp>(dst, i);
1527 auto srcField = builder.create<SubfieldOp>(src, i);
1528 if (dstBundle.getElement(i).isFlip) {
1529 std::swap(srcBundle, dstBundle);
1530 std::swap(srcField, dstField);
1540 for (
auto *node : instanceGraph) {
1541 auto module = cast<FModuleLike>(*node->getModule());
1544 bool shouldFixupObjects =
false;
1545 auto classOp = dyn_cast<ClassOp>(module.getOperation());
1549 for (
auto *instRec : node->uses()) {
1552 if (shouldFixupObjects) {
1554 classOp.getInstanceType());
1559 auto inst = instRec->getInstance<InstanceOp>();
1563 ImplicitLocOpBuilder builder(inst.getLoc(), inst->getContext());
1564 builder.setInsertionPointAfter(inst);
1565 for (
size_t i = 0, e =
getNumPorts(module); i < e; ++i) {
1566 auto result = inst.getResult(i);
1567 auto newType = module.getPortType(i);
1568 auto oldType = result.getType();
1570 if (newType == oldType)
1575 builder.create<WireOp>(oldType, inst.getPortName(i)).getResult();
1576 result.replaceAllUsesWith(wire);
1577 result.setType(newType);
1593 struct DenseMapInfo<ModuleInfo> {
1595 std::array<uint8_t, 32> key;
1596 std::fill(key.begin(), key.end(), ~0);
1597 return {key, DenseMapInfo<mlir::ArrayAttr>::getEmptyKey()};
1601 std::array<uint8_t, 32> key;
1602 std::fill(key.begin(), key.end(), ~0 - 1);
1603 return {key, DenseMapInfo<mlir::ArrayAttr>::getTombstoneKey()};
1610 std::memcpy(&hash, val.structuralHash.data(),
sizeof(
unsigned));
1616 static bool isEqual(
const ModuleInfo &lhs,
const ModuleInfo &rhs) {
1617 return lhs.structuralHash == rhs.structuralHash &&
1618 lhs.referredModuleNames == rhs.referredModuleNames;
1628 class DedupPass :
public circt::firrtl::impl::DedupBase<DedupPass> {
1629 void runOnOperation()
override {
1630 auto *context = &getContext();
1631 auto circuit = getOperation();
1632 auto &instanceGraph = getAnalysis<InstanceGraph>();
1633 auto *nlaTable = &getAnalysis<NLATable>();
1634 auto &symbolTable = getAnalysis<SymbolTable>();
1635 Deduper deduper(instanceGraph, symbolTable, nlaTable, circuit);
1637 auto anythingChanged =
false;
1646 llvm::DenseMap<ModuleInfo, Operation *> moduleInfoToModule;
1651 DenseMap<Attribute, StringAttr> dedupMap;
1656 SmallVector<FModuleLike, 0> modules(
1657 llvm::map_range(llvm::post_order(&instanceGraph), [](
auto *node) {
1658 return cast<FModuleLike>(*node->getModule());
1661 SmallVector<std::optional<
1662 std::pair<std::array<uint8_t, 32>, SmallVector<StringAttr>>>>
1663 hashesAndModuleNames(modules.size());
1667 auto dedupGroupAttrName =
StringAttr::get(context,
"firrtl.dedup_group");
1673 for (
auto module : modules) {
1674 llvm::SmallSetVector<StringAttr, 1> groups;
1676 module, [&groups, dedupGroupClass](
Annotation annotation) {
1679 groups.insert(annotation.
getMember<StringAttr>(
"group"));
1682 if (groups.size() > 1) {
1683 module.emitError(
"module belongs to multiple dedup groups: ") << groups;
1684 return signalPassFailure();
1686 assert(!module->hasAttr(dedupGroupAttrName) &&
1687 "unexpected existing use of temporary dedup group attribute");
1688 if (!groups.empty())
1689 module->setDiscardableAttr(dedupGroupAttrName, groups.front());
1693 auto result = mlir::failableParallelForEach(
1694 context, llvm::seq(modules.size()), [&](
unsigned idx) {
1695 auto module = modules[idx];
1697 if (AnnotationSet::hasAnnotation(module, noDedupClass))
1701 if (auto ext = dyn_cast<FExtModuleOp>(*module);
1702 ext && !ext.getDefname().has_value())
1705 if (!checkVisibility(module))
1708 StructuralHasher hasher(hasherConstants);
1710 hashesAndModuleNames[idx] = hasher.getHashAndModuleNames(module);
1714 if (result.failed())
1715 return signalPassFailure();
1717 for (
auto [i, module] : llvm::enumerate(modules)) {
1718 auto moduleName = module.getModuleNameAttr();
1719 auto &hashAndModuleNamesOpt = hashesAndModuleNames[i];
1721 if (!hashAndModuleNamesOpt) {
1726 dedupMap[moduleName] = moduleName;
1731 SmallVector<mlir::Attribute> names;
1732 for (
auto oldModuleName : hashAndModuleNamesOpt->second) {
1733 auto newModuleName = dedupMap[oldModuleName];
1734 names.push_back(newModuleName);
1738 ModuleInfo moduleInfo{hashAndModuleNamesOpt->first,
1742 auto it = moduleInfoToModule.find(moduleInfo);
1743 if (it != moduleInfoToModule.end()) {
1744 auto original = cast<FModuleLike>(it->second);
1746 dedupMap[moduleName] = original.getModuleNameAttr();
1747 deduper.dedup(original, module);
1749 anythingChanged =
true;
1753 deduper.record(module);
1755 dedupMap[moduleName] = moduleName;
1757 moduleInfoToModule[moduleInfo] = module;
1765 auto failed =
false;
1767 auto parseModule = [&](Attribute path) -> StringAttr {
1770 auto [_, rhs] = cast<StringAttr>(path).getValue().split(
'|');
1776 auto getLead = [&](StringAttr module) -> StringAttr {
1777 auto it = dedupMap.find(module);
1778 if (it == dedupMap.end()) {
1779 auto diag = emitError(circuit.getLoc(),
1780 "MustDeduplicateAnnotation references module ")
1781 << module <<
" which does not exist";
1791 auto modules = annotation.
getMember<ArrayAttr>(
"modules");
1793 emitError(circuit.getLoc(),
1794 "MustDeduplicateAnnotation missing \"modules\" member");
1799 if (modules.empty())
1802 auto firstModule = parseModule(modules[0]);
1803 auto firstLead = getLead(firstModule);
1807 for (
auto attr : modules.getValue().drop_front()) {
1808 auto nextModule = parseModule(attr);
1809 auto nextLead = getLead(nextModule);
1812 if (firstLead != nextLead) {
1813 auto diag = emitError(circuit.getLoc(),
"module ")
1814 << nextModule <<
" not deduplicated with " << firstModule;
1815 auto a = instanceGraph.lookup(firstLead)->getModule();
1816 auto b = instanceGraph.lookup(nextLead)->getModule();
1817 equiv.check(diag, a, b);
1825 return signalPassFailure();
1828 for (
auto module : circuit.getOps<FModuleLike>())
1829 module->removeDiscardableAttr(dedupGroupAttrName);
1836 markAnalysesPreserved<NLATable>();
1837 if (!anythingChanged)
1838 markAllAnalysesPreserved();
1844 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.
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::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.
ValueId getId(Value val)
Get the unique id for the specified value.
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(BundleType type)
void update(OpResult result)
void update(OpOperand &operand)
StructuralHasher(const StructuralHasherSharedConstants &constants)
void update(ValueId index)
void update(TypeID typeID)
const StructuralHasherSharedConstants & constants
void update(Value value, hw::InnerSymAttr attr)
DenseMap< void *, unsigned > indices
void update(mlir::OperationName name)
Unique identifier for a value.
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)