25#include "mlir/IR/IRMapping.h"
26#include "mlir/IR/Threading.h"
27#include "mlir/Pass/Pass.h"
28#include "llvm/ADT/DenseMap.h"
29#include "llvm/ADT/DenseMapInfo.h"
30#include "llvm/ADT/Hashing.h"
31#include "llvm/ADT/PostOrderIterator.h"
32#include "llvm/ADT/SmallPtrSet.h"
33#include "llvm/Support/Debug.h"
34#include "llvm/Support/Format.h"
35#include "llvm/Support/SHA256.h"
37#define DEBUG_TYPE "firrtl-dedup"
41#define GEN_PASS_DEF_DEDUP
42#include "circt/Dialect/FIRRTL/Passes.h.inc"
47using namespace firrtl;
48using hw::InnerRefAttr;
57 if (!symbol.isPrivate())
62 if (isa<ClassLike>(*symbol))
67 if (!symbol.canDiscardOnUseEmpty())
132 : constants(constants) {}
150 for (
auto [index, innerSym] : llvm::enumerate(module.getPortSymbols())) {
151 for (
auto prop : cast<hw::InnerSymAttr>(innerSym))
155 size_t index =
module.getNumPorts();
156 module.walk([&](hw::InnerSymbolOpInterface innerSymOp) {
157 if (auto innerSym = innerSymOp.getInnerSymAttr()) {
158 for (auto prop : innerSym)
159 innerSymIDTable[prop.getName()] = std::pair(index, prop.getFieldID());
167 auto [it, inserted] = idTable.try_emplace(
object, nextID);
175 auto it = idTable.find(
object);
176 if (it == idTable.end())
178 auto id = it->second;
184 return innerSymIDTable.at(name);
188 auto value = operand.get();
189 if (
auto result = dyn_cast<OpResult>(value)) {
190 auto *op = result.getOwner();
192 update(result.getResultNumber());
195 if (
auto argument = dyn_cast<BlockArgument>(value)) {
196 auto *block = argument.getOwner();
197 update(getID(block));
198 update(argument.getArgNumber());
201 llvm_unreachable(
"Unknown value type");
205 auto *
addr =
reinterpret_cast<const uint8_t *
>(&pointer);
206 sha.update(ArrayRef<uint8_t>(
addr,
sizeof pointer));
210 auto *
addr =
reinterpret_cast<const uint8_t *
>(&value);
211 sha.update(ArrayRef<uint8_t>(
addr,
sizeof value));
214 template <
typename T,
typename U>
215 void update(
const std::pair<T, U> &pair) {
224 update(type.getTypeID());
225 for (
auto &element : type.getElements()) {
226 update(element.isFlip);
227 update(element.type);
233 update(type.getTypeID());
237 hasSeenSymbol =
true;
238 referredModuleNames.push_back(type.getNameAttr().getAttr());
239 for (
auto &element : type.getElements()) {
240 update(element.name.getAsOpaquePointer());
241 update(element.type);
242 update(
static_cast<unsigned>(element.direction));
248 if (
auto bundle = type_dyn_cast<BundleType>(type))
249 return update(bundle);
250 if (
auto klass = type_dyn_cast<ClassType>(type))
251 return update(klass);
252 update(type.getAsOpaquePointer());
259 if (
auto objectOp = dyn_cast<ObjectOp>(result.getOwner())) {
260 hasSeenSymbol =
true;
261 referredModuleNames.push_back(objectOp.getType().getNameAttr().getAttr());
265 update(result.getType());
270 void update(Operation *op, DictionaryAttr dict) {
271 for (
auto namedAttr : dict) {
272 auto name = namedAttr.getName();
273 auto value = namedAttr.getValue();
277 value.walk([&](FlatSymbolRefAttr) { hasSeenSymbol =
true; });
281 bool isClassPortNames =
282 isa<ClassLike>(op) && name == constants.portNamesAttr;
283 if (constants.nonessentialAttributes.contains(name) && !isClassPortNames)
287 update(name.getAsOpaquePointer());
290 if (name == constants.portTypesAttr) {
291 auto portTypes = cast<ArrayAttr>(value).getAsValueRange<TypeAttr>();
292 for (
auto type : portTypes)
302 if (isa<InstanceOp>(op) && name == constants.moduleNameAttr) {
303 referredModuleNames.push_back(cast<FlatSymbolRefAttr>(value).getAttr());
309 if (isa<DistinctAttr>(value))
313 if (
auto innerRef = dyn_cast<hw::InnerRefAttr>(value)) {
314 update(getInnerSymID(innerRef.getName()));
320 update(value.getAsOpaquePointer());
326 update(name.getAsOpaquePointer());
331 for (
auto &op : llvm::reverse(*block))
333 for (
auto type : block->getArgumentTypes())
335 update(finalizeID(block));
342 for (
auto &block : llvm::reverse(region->getBlocks()))
352 update(op->getNumRegions());
353 for (
auto ®ion : reverse(op->getRegions()))
356 update(op->getName());
359 for (
auto &operand : op->getOpOperands())
364 hasSeenSymbol =
false;
365 update(op, op->getAttrDictionary());
368 for (
auto result : op->getResults())
374 symbolSensitiveOps.push_back(op);
377 update(finalizeID(op));
404 bool hasSeenSymbol =
false;
435 return llvm::hash_combine(
441 auto *
empty = getEmptyKey().info;
442 auto *tombstone = getTombstoneKey().info;
444 rhs.
info == tombstone)
482 SmallString<64> buffer;
483 llvm::raw_svector_ostream os(buffer);
484 if (
auto integerAttr = dyn_cast<IntegerAttr>(attr)) {
486 if (integerAttr.getType().isSignlessInteger())
487 integerAttr.getValue().toStringUnsigned(buffer, 16);
489 integerAttr.getAPSInt().toString(buffer, 16);
493 return std::string(buffer);
497 LogicalResult
check(InFlightDiagnostic &diag,
const Twine &message,
498 Operation *a, BundleType aType, Operation *b,
500 if (aType.getNumElements() != bType.getNumElements()) {
501 diag.attachNote(a->getLoc())
502 << message <<
" bundle type has different number of elements";
503 diag.attachNote(b->getLoc()) <<
"second operation here";
507 for (
auto elementPair :
508 llvm::zip(aType.getElements(), bType.getElements())) {
509 auto aElement = std::get<0>(elementPair);
510 auto bElement = std::get<1>(elementPair);
511 if (aElement.isFlip != bElement.isFlip) {
512 diag.attachNote(a->getLoc()) << message <<
" bundle element "
513 << aElement.name <<
" flip does not match";
514 diag.attachNote(b->getLoc()) <<
"second operation here";
518 if (failed(
check(diag,
519 message +
" -> bundle element \'" +
520 aElement.name.getValue() +
"'",
521 a, aElement.type, b, bElement.type)))
527 LogicalResult
check(InFlightDiagnostic &diag,
const Twine &message,
528 Operation *a, Type aType, Operation *b, Type bType) {
531 if (
auto aBundleType = type_dyn_cast<BundleType>(aType))
532 if (
auto bBundleType = type_dyn_cast<BundleType>(bType))
533 return check(diag, message, a, aBundleType, b, bBundleType);
534 if (type_isa<RefType>(aType) && type_isa<RefType>(bType) &&
536 diag.attachNote(a->getLoc())
537 << message <<
", has a RefType with a different base type "
538 << type_cast<RefType>(aType).getType()
539 <<
" in the same position of the two modules marked as 'must dedup'. "
540 "(This may be due to Grand Central Taps or Views being different "
541 "between the two modules.)";
542 diag.attachNote(b->getLoc())
543 <<
"the second module has a different base type "
544 << type_cast<RefType>(bType).getType();
547 diag.attachNote(a->getLoc())
548 << message <<
" types don't match, first type is " << aType;
549 diag.attachNote(b->getLoc()) <<
"second type is " << bType;
554 Block &aBlock, Operation *b, Block &bBlock) {
557 auto portNames = a->getAttrOfType<ArrayAttr>(
"portNames");
559 auto emitMissingPort = [&](Value existsVal, Operation *opExists,
560 Operation *opDoesNotExist) {
562 auto portNames = opExists->getAttrOfType<ArrayAttr>(
"portNames");
564 if (
auto portNameAttr = dyn_cast<StringAttr>(portNames[portNo]))
565 portName = portNameAttr.getValue();
566 if (type_isa<RefType>(existsVal.getType())) {
567 diag.attachNote(opExists->getLoc())
568 <<
" contains a RefType port named '" + portName +
569 "' that only exists in one of the modules (can be due to "
570 "difference in Grand Central Tap or View of two modules "
571 "marked with must dedup)";
572 diag.attachNote(opDoesNotExist->getLoc())
573 <<
"second module to be deduped that does not have the RefType "
576 diag.attachNote(opExists->getLoc())
577 <<
"port '" + portName +
"' only exists in one of the modules";
578 diag.attachNote(opDoesNotExist->getLoc())
579 <<
"second module to be deduped that does not have the port";
585 llvm::zip_longest(aBlock.getArguments(), bBlock.getArguments())) {
586 auto &aArg = std::get<0>(argPair);
587 auto &bArg = std::get<1>(argPair);
588 if (aArg.has_value() && bArg.has_value()) {
593 if (
auto portNameAttr = dyn_cast<StringAttr>(portNames[portNo]))
594 portName = portNameAttr.getValue();
597 if (failed(
check(diag,
"module port '" + portName +
"'", a,
598 aArg->getType(), b, bArg->getType())))
600 data.map.map(aArg.value(), bArg.value());
604 if (!aArg.has_value())
606 return emitMissingPort(aArg.has_value() ? aArg.value() : bArg.value(), a,
611 auto aIt = aBlock.begin();
612 auto aEnd = aBlock.end();
613 auto bIt = bBlock.begin();
614 auto bEnd = bBlock.end();
615 while (aIt != aEnd && bIt != bEnd)
616 if (failed(
check(diag,
data, &*aIt++, &*bIt++)))
619 diag.attachNote(aIt->getLoc()) <<
"first block has more operations";
620 diag.attachNote(b->getLoc()) <<
"second block here";
624 diag.attachNote(bIt->getLoc()) <<
"second block has more operations";
625 diag.attachNote(a->getLoc()) <<
"first block here";
632 Region &aRegion, Operation *b, Region &bRegion) {
633 auto aIt = aRegion.begin();
634 auto aEnd = aRegion.end();
635 auto bIt = bRegion.begin();
636 auto bEnd = bRegion.end();
639 while (aIt != aEnd && bIt != bEnd)
640 if (failed(
check(diag,
data, a, *aIt++, b, *bIt++)))
642 if (aIt != aEnd || bIt != bEnd) {
643 diag.attachNote(a->getLoc())
644 <<
"operation regions have different number of blocks";
645 diag.attachNote(b->getLoc()) <<
"second operation here";
651 LogicalResult
check(InFlightDiagnostic &diag, Operation *a,
652 mlir::DenseBoolArrayAttr aAttr, Operation *b,
653 mlir::DenseBoolArrayAttr bAttr) {
656 auto portNames = a->getAttrOfType<ArrayAttr>(
"portNames");
657 for (
unsigned i = 0, e = aAttr.size(); i < e; ++i) {
658 auto aDirection = aAttr[i];
659 auto bDirection = bAttr[i];
660 if (aDirection != bDirection) {
661 auto ¬e = diag.attachNote(a->getLoc()) <<
"module port ";
663 note <<
"'" << cast<StringAttr>(portNames[i]).getValue() <<
"'";
666 note <<
" directions don't match, first direction is '"
668 diag.attachNote(b->getLoc()) <<
"second direction is '"
677 DictionaryAttr aDict, Operation *b,
678 DictionaryAttr bDict) {
683 DenseSet<Attribute> seenAttrs;
684 for (
auto namedAttr : aDict) {
685 auto attrName = namedAttr.getName();
689 auto aAttr = namedAttr.getValue();
690 auto bAttr = bDict.get(attrName);
692 diag.attachNote(a->getLoc())
693 <<
"second operation is missing attribute " << attrName;
694 diag.attachNote(b->getLoc()) <<
"second operation here";
698 if (isa<hw::InnerRefAttr>(aAttr) && isa<hw::InnerRefAttr>(bAttr)) {
699 auto bRef = cast<hw::InnerRefAttr>(bAttr);
700 auto aRef = cast<hw::InnerRefAttr>(aAttr);
702 auto aTarget =
data.a.lookup(aRef.getName());
703 auto bTarget =
data.b.lookup(bRef.getName());
704 if (!aTarget || !bTarget)
705 diag.attachNote(a->getLoc())
706 <<
"malformed ir, possibly violating use-before-def";
708 diag.attachNote(a->getLoc())
709 <<
"operations have different targets, first operation has "
711 diag.attachNote(b->getLoc()) <<
"second operation has " << bTarget;
714 if (aTarget.isPort()) {
716 if (!bTarget.isPort() || aTarget.getPort() != bTarget.getPort())
720 if (!bTarget.isOpOnly() ||
721 data.map.lookupOrNull(aTarget.getOp()) != bTarget.getOp())
724 if (aTarget.getField() != bTarget.getField())
729 if (failed(
check(diag, a, cast<mlir::DenseBoolArrayAttr>(aAttr), b,
730 cast<mlir::DenseBoolArrayAttr>(bAttr))))
732 }
else if (isa<DistinctAttr>(aAttr) && isa<DistinctAttr>(bAttr)) {
735 }
else if (aAttr != bAttr) {
736 diag.attachNote(a->getLoc())
737 <<
"first operation has attribute '" << attrName.getValue()
739 diag.attachNote(b->getLoc())
740 <<
"second operation has value " <<
prettyPrint(bAttr);
743 seenAttrs.insert(attrName);
745 if (aDict.getValue().size() != bDict.getValue().size()) {
746 for (
auto namedAttr : bDict) {
747 auto attrName = namedAttr.getName();
751 seenAttrs.contains(attrName))
754 diag.attachNote(a->getLoc())
755 <<
"first operation is missing attribute " << attrName;
756 diag.attachNote(b->getLoc()) <<
"second operation here";
764 LogicalResult
check(InFlightDiagnostic &diag, FInstanceLike a,
766 auto aName = a.getReferencedModuleNameAttr();
767 auto bName = b.getReferencedModuleNameAttr();
777 diag.attachNote(std::nullopt)
778 <<
"in instance " << a.getInstanceNameAttr() <<
" of " << aName
779 <<
", and instance " << b.getInstanceNameAttr() <<
" of " << bName;
780 check(diag, aModule, bModule);
788 if (a->getName() != b->getName()) {
789 diag.attachNote(a->getLoc()) <<
"first operation is a " << a->getName();
790 diag.attachNote(b->getLoc()) <<
"second operation is a " << b->getName();
796 if (
auto aInst = dyn_cast<FInstanceLike>(a)) {
797 auto bInst = cast<FInstanceLike>(b);
798 if (failed(
check(diag, aInst, bInst)))
803 if (a->getNumResults() != b->getNumResults()) {
804 diag.attachNote(a->getLoc())
805 <<
"operations have different number of results";
806 diag.attachNote(b->getLoc()) <<
"second operation here";
809 for (
auto resultPair : llvm::zip(a->getResults(), b->getResults())) {
810 auto &aValue = std::get<0>(resultPair);
811 auto &bValue = std::get<1>(resultPair);
812 if (failed(
check(diag,
"operation result", a, aValue.getType(), b,
815 data.map.map(aValue, bValue);
819 if (a->getNumOperands() != b->getNumOperands()) {
820 diag.attachNote(a->getLoc())
821 <<
"operations have different number of operands";
822 diag.attachNote(b->getLoc()) <<
"second operation here";
825 for (
auto operandPair : llvm::zip(a->getOperands(), b->getOperands())) {
826 auto &aValue = std::get<0>(operandPair);
827 auto &bValue = std::get<1>(operandPair);
828 if (bValue !=
data.map.lookup(aValue)) {
829 diag.attachNote(a->getLoc())
830 <<
"operations use different operands, first operand is '"
835 diag.attachNote(b->getLoc())
836 <<
"second operand is '"
840 <<
"', but should have been '"
851 if (a->getNumRegions() != b->getNumRegions()) {
852 diag.attachNote(a->getLoc())
853 <<
"operations have different number of regions";
854 diag.attachNote(b->getLoc()) <<
"second operation here";
857 for (
auto regionPair : llvm::zip(a->getRegions(), b->getRegions())) {
858 auto &aRegion = std::get<0>(regionPair);
859 auto &bRegion = std::get<1>(regionPair);
860 if (failed(
check(diag,
data, a, aRegion, b, bRegion)))
865 if (failed(
check(diag,
data, a, a->getAttrDictionary(), b,
866 b->getAttrDictionary())))
872 void check(InFlightDiagnostic &diag, Operation *a, Operation *b) {
877 diag.attachNote(a->getLoc()) <<
"module marked NoDedup";
881 diag.attachNote(b->getLoc()) <<
"module marked NoDedup";
884 auto aSymbol = cast<mlir::SymbolOpInterface>(a);
885 auto bSymbol = cast<mlir::SymbolOpInterface>(b);
887 diag.attachNote(a->getLoc())
889 << (aSymbol.isPrivate() ?
"private but not discardable" :
"public");
890 diag.attachNote(b->getLoc())
892 << (bSymbol.isPrivate() ?
"private but not discardable" :
"public");
897 auto bGroup = dyn_cast_or_null<StringAttr>(
899 if (aGroup != bGroup) {
901 diag.attachNote(b->getLoc())
902 <<
"module is in dedup group '" << bGroup.str() <<
"'";
904 diag.attachNote(b->getLoc()) <<
"module is not part of a dedup group";
907 diag.attachNote(a->getLoc())
908 <<
"module is in dedup group '" << aGroup.str() <<
"'";
910 diag.attachNote(a->getLoc()) <<
"module is not part of a dedup group";
916 diag.attachNote(a->getLoc()) <<
"first module here";
917 diag.attachNote(b->getLoc()) <<
"second module here";
943 llvm::SmallSetVector<Location, 4> decomposedLocs;
945 unsigned seenFIR = 0;
946 for (
auto loc : {to, from}) {
949 if (
auto fusedLoc = dyn_cast<FusedLoc>(loc)) {
952 for (
auto loc : fusedLoc.getLocations()) {
953 if (FileLineColLoc fileLoc = dyn_cast<FileLineColLoc>(loc)) {
954 if (fileLoc.getFilename().strref().ends_with(
".fir")) {
960 decomposedLocs.insert(loc);
966 if (FileLineColLoc fileLoc = dyn_cast<FileLineColLoc>(loc)) {
967 if (fileLoc.getFilename().strref().ends_with(
".fir")) {
974 if (!isa<UnknownLoc>(loc))
975 decomposedLocs.insert(loc);
978 auto locs = decomposedLocs.getArrayRef();
983 return UnknownLoc::get(
context);
984 if (locs.size() == 1)
987 return FusedLoc::get(
context, locs);
1002 for (
auto nla : circuit.getOps<hw::HierPathOp>())
1003 nlaCache[nla.getNamepathAttr()] = nla.getSymNameAttr();
1010 void dedup(FModuleLike toModule, FModuleLike fromModule) {
1016 SmallVector<Attribute> newLocs;
1017 for (
auto [toLoc, fromLoc] : llvm::zip(toModule.getPortLocations(),
1018 fromModule.getPortLocations())) {
1019 if (toLoc == fromLoc)
1020 newLocs.push_back(toLoc);
1023 cast<LocationAttr>(fromLoc)));
1025 toModule->setAttr(
"portLocations", ArrayAttr::get(
context, newLocs));
1028 mergeOps(renameMap, toModule, toModule, fromModule, fromModule);
1034 if (
auto to = dyn_cast<FModuleOp>(*toModule))
1038 fromModule.getModuleNameAttr());
1049 for (
unsigned i = 0, e =
getNumPorts(module); i < e; ++i)
1052 module->walk([&](Operation *op) { recordAnnotations(op); });
1058 return moduleNamespaces.try_emplace(module, cast<FModuleLike>(module))
1066 if (
auto nlaRef = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal"))
1067 targetMap[nlaRef.getAttr()].insert(target);
1076 auto mem = dyn_cast<MemOp>(op);
1081 for (
unsigned i = 0, e = mem->getNumResults(); i < e; ++i)
1090 instanceGraph[::cast<igraph::ModuleOpInterface>(fromModule)];
1091 auto *toNode = instanceGraph[toModule];
1092 auto toModuleRef = FlatSymbolRefAttr::get(toModule.getModuleNameAttr());
1093 for (
auto *oldInstRec : llvm::make_early_inc_range(fromNode->uses())) {
1094 auto inst = oldInstRec->getInstance();
1095 if (
auto instOp = dyn_cast<InstanceOp>(*inst)) {
1096 instOp.setModuleNameAttr(toModuleRef);
1097 instOp.setPortNamesAttr(toModule.getPortNamesAttr());
1098 }
else if (
auto objectOp = dyn_cast<ObjectOp>(*inst)) {
1099 auto classLike = cast<ClassLike>(*toNode->getModule());
1100 ClassType classType = detail::getInstanceTypeForClassLike(classLike);
1101 objectOp.getResult().setType(classType);
1103 oldInstRec->getParent()->addInstance(inst, toNode);
1104 oldInstRec->erase();
1106 instanceGraph.erase(fromNode);
1107 fromModule->erase();
1115 SmallVector<FlatSymbolRefAttr>
1116 createNLAs(Operation *fromModule, ArrayRef<Attribute> baseNamepath,
1117 SymbolTable::Visibility vis = SymbolTable::Visibility::Private) {
1120 SmallVector<Attribute> namepath = {
nullptr};
1121 namepath.append(baseNamepath.begin(), baseNamepath.end());
1123 auto loc = fromModule->getLoc();
1124 auto *fromNode = instanceGraph[cast<igraph::ModuleOpInterface>(fromModule)];
1125 SmallVector<FlatSymbolRefAttr> nlas;
1126 for (
auto *instanceRecord : fromNode->uses()) {
1127 auto parent = cast<FModuleOp>(*instanceRecord->getParent()->getModule());
1128 auto inst = instanceRecord->getInstance();
1130 auto arrayAttr = ArrayAttr::get(
context, namepath);
1132 auto &cacheEntry = nlaCache[arrayAttr];
1134 auto builder = OpBuilder::atBlockBegin(nlaBlock);
1135 auto nla = hw::HierPathOp::create(builder, loc,
"nla", arrayAttr);
1137 symbolTable.insert(nla);
1139 cacheEntry = nla.getNameAttr();
1140 nla.setVisibility(vis);
1141 nlaTable->addNLA(nla);
1143 auto nlaRef = FlatSymbolRefAttr::get(cast<StringAttr>(cacheEntry));
1144 nlas.push_back(nlaRef);
1152 SmallVector<FlatSymbolRefAttr>
1154 SymbolTable::Visibility vis = SymbolTable::Visibility::Private) {
1155 return createNLAs(fromModule, FlatSymbolRefAttr::get(toModuleName), vis);
1162 Annotation anno, ArrayRef<NamedAttribute> attributes,
1163 unsigned nonLocalIndex,
1164 SmallVectorImpl<Annotation> &newAnnotations) {
1165 SmallVector<NamedAttribute> mutableAttributes(attributes.begin(),
1167 for (
auto &nla : nlas) {
1169 mutableAttributes[nonLocalIndex].setValue(nla);
1170 auto dict = DictionaryAttr::getWithSorted(
context, mutableAttributes);
1173 newAnnotations.push_back(anno);
1182 targetMap.erase(nla.getNameAttr());
1183 nlaTable->erase(nla);
1184 nlaCache.erase(nla.getNamepathAttr());
1185 symbolTable.erase(nla);
1191 FModuleOp fromModule) {
1192 auto toName = toModule.getNameAttr();
1193 auto fromName = fromModule.getNameAttr();
1196 auto moduleNLAs = nlaTable->lookup(fromModule.getNameAttr()).vec();
1198 nlaTable->renameModuleAndInnerRef(toName, fromName, renameMap);
1201 for (
auto nla : moduleNLAs) {
1202 auto elements = nla.getNamepath().getValue();
1204 if (nla.root() != toName)
1207 SmallVector<Attribute> namepath(elements.begin(), elements.end());
1208 auto nlaRefs = createNLAs(fromModule, namepath, nla.getVisibility());
1210 auto &set = targetMap[nla.getSymNameAttr()];
1211 SmallVector<AnnoTarget> targets(set.begin(), set.end());
1213 for (
auto target : targets) {
1216 SmallVector<Annotation> newAnnotations;
1217 for (
auto anno : target.getAnnotations()) {
1219 auto [it, found] = mlir::impl::findAttrSorted(
1220 anno.begin(), anno.end(), nonLocalString);
1223 if (!found || cast<FlatSymbolRefAttr>(it->getValue()).getAttr() !=
1224 nla.getSymNameAttr()) {
1225 newAnnotations.push_back(anno);
1228 auto nonLocalIndex = std::distance(anno.begin(), it);
1230 cloneAnnotation(nlaRefs, anno,
1231 ArrayRef<NamedAttribute>(anno.begin(), anno.end()),
1232 nonLocalIndex, newAnnotations);
1237 target.setAnnotations(annotations);
1239 for (
auto nla : nlaRefs)
1240 targetMap[nla.getAttr()].insert(target);
1252 FModuleOp fromModule) {
1253 addAnnotationContext(renameMap, toModule, toModule);
1254 addAnnotationContext(renameMap, toModule, fromModule);
1260 StringAttr fromName) {
1261 nlaTable->renameModuleAndInnerRef(toName, fromName, renameMap);
1269 SmallVectorImpl<Annotation> &newAnnotations) {
1272 SmallVector<NamedAttribute> attributes;
1273 int nonLocalIndex = -1;
1274 for (
const auto &val : llvm::enumerate(anno)) {
1275 auto attr = val.value();
1277 auto compare = attr.getName().compare(nonLocalString);
1278 assert(compare != 0 &&
"should not pass non-local annotations here");
1282 nonLocalIndex = val.index();
1283 attributes.push_back(NamedAttribute(nonLocalString, nonLocalString));
1288 attributes.push_back(attr);
1290 if (nonLocalIndex == -1) {
1292 nonLocalIndex = attributes.size();
1293 attributes.push_back(NamedAttribute(nonLocalString, nonLocalString));
1296 attributes.append(anno.
begin() + nonLocalIndex, anno.
end());
1300 auto nlaRefs = createNLAs(toModuleName, fromModule);
1301 for (
auto nla : nlaRefs)
1302 targetMap[nla.getAttr()].insert(to);
1305 cloneAnnotation(nlaRefs, anno, attributes, nonLocalIndex, newAnnotations);
1311 SmallVectorImpl<Annotation> &newAnnotations,
1312 SmallPtrSetImpl<Attribute> &dontTouches) {
1313 for (
auto anno : annos) {
1317 anno.removeMember(
"circt.nonlocal");
1318 auto [it, inserted] = dontTouches.insert(anno.getAttr());
1320 newAnnotations.push_back(anno);
1325 if (
auto nla = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
1326 newAnnotations.push_back(anno);
1327 targetMap[nla.getAttr()].insert(to);
1331 makeAnnotationNonLocal(toModule.getModuleNameAttr(), to, fromModule, anno,
1342 SmallVector<Annotation> newAnnotations;
1346 llvm::SmallPtrSet<Attribute, 4> dontTouches;
1350 copyAnnotations(toModule, to, toModule, toAnnos, newAnnotations,
1352 copyAnnotations(toModule, to, fromModule, fromAnnos, newAnnotations,
1356 if (!newAnnotations.empty())
1362 FModuleLike fromModule, Operation *from) {
1368 if (toModule == to) {
1370 for (
unsigned i = 0, e =
getNumPorts(toModule); i < e; ++i)
1375 }
else if (
auto toMem = dyn_cast<MemOp>(to)) {
1377 auto fromMem = cast<MemOp>(from);
1378 for (
unsigned i = 0, e = toMem.getNumResults(); i < e; ++i)
1387 hw::InnerSymAttr toSym,
1388 hw::InnerSymAttr fromSym) {
1389 if (fromSym && !fromSym.getProps().empty()) {
1390 auto &isn = getNamespace(toModule);
1392 SmallVector<hw::InnerSymPropertiesAttr> newProps;
1395 llvm::append_range(newProps, toSym);
1397 for (
auto fromProp : fromSym) {
1398 hw::InnerSymPropertiesAttr newProp;
1399 auto *it = llvm::find_if(newProps, [&](
auto p) {
1400 return p.getFieldID() == fromProp.getFieldID();
1402 if (it != newProps.end()) {
1407 if (fromProp.getSymVisibility().getValue() ==
"public" &&
1408 newProp.getSymVisibility().getValue() !=
"public") {
1409 *it = hw::InnerSymPropertiesAttr::get(
context, newProp.getName(),
1410 newProp.getFieldID(),
1411 fromProp.getSymVisibility());
1415 auto newName = isn.newName(fromProp.getName().getValue());
1416 newProp = hw::InnerSymPropertiesAttr::get(
1417 context, StringAttr::get(
context, newName), fromProp.getFieldID(),
1418 fromProp.getSymVisibility());
1419 newProps.push_back(newProp);
1421 renameMap[fromProp.getName()] = newProp.getName();
1424 llvm::sort(newProps, [](
auto &p,
auto &q) {
1425 return p.getFieldID() < q.getFieldID();
1428 return hw::InnerSymAttr::get(
context, newProps);
1430 return hw::InnerSymAttr();
1437 Operation *to, FModuleLike fromModule,
1441 if (
auto fromInnerSym = dyn_cast<hw::InnerSymbolOpInterface>(from)) {
1442 auto toInnerSym = cast<hw::InnerSymbolOpInterface>(to);
1443 if (
auto newSymAttr = mergeInnerSymbols(renameMap, toModule,
1444 toInnerSym.getInnerSymAttr(),
1445 fromInnerSym.getInnerSymAttr()))
1446 toInnerSym.setInnerSymbolAttr(newSymAttr);
1450 auto fromPortSyms = from->getAttrOfType<ArrayAttr>(
"portSymbols");
1451 if (!fromPortSyms || fromPortSyms.empty())
1454 auto portCount = fromPortSyms.size();
1455 auto toPortSyms = to->getAttrOfType<ArrayAttr>(
"portSymbols");
1459 SmallVector<Attribute> newPortSyms;
1460 if (toPortSyms.empty())
1461 newPortSyms.assign(portCount, hw::InnerSymAttr());
1463 newPortSyms.assign(toPortSyms.begin(), toPortSyms.end());
1465 for (
unsigned portNo = 0; portNo < portCount; ++portNo) {
1466 if (
auto newPortSym = mergeInnerSymbols(
1467 renameMap, toModule,
1468 llvm::cast_if_present<hw::InnerSymAttr>(newPortSyms[portNo]),
1469 cast<hw::InnerSymAttr>(fromPortSyms[portNo]))) {
1470 newPortSyms[portNo] = newPortSym;
1475 FModuleLike::fixupPortSymsArray(newPortSyms, toModule.getContext());
1476 cast<FModuleLike>(to).setPortSymbols(newPortSyms);
1482 FModuleLike fromModule, Operation *from) {
1484 if (to->getLoc() != from->getLoc())
1488 for (
auto regions : llvm::zip(to->getRegions(), from->getRegions()))
1489 mergeRegions(renameMap, toModule, std::get<0>(regions), fromModule,
1490 std::get<1>(regions));
1493 recordSymRenames(renameMap, toModule, to, fromModule, from);
1496 mergeAnnotations(toModule, to, fromModule, from);
1501 FModuleLike fromModule, Block &fromBlock) {
1503 for (
auto [toArg, fromArg] :
1504 llvm::zip(toBlock.getArguments(), fromBlock.getArguments()))
1505 if (toArg.getLoc() != fromArg.getLoc())
1508 for (
auto ops : llvm::zip(toBlock, fromBlock))
1509 mergeOps(renameMap, toModule, &std::get<0>(ops), fromModule,
1515 Region &toRegion, FModuleLike fromModule,
1516 Region &fromRegion) {
1517 for (
auto blocks : llvm::zip(toRegion, fromRegion))
1518 mergeBlocks(renameMap, toModule, std::get<0>(blocks), fromModule,
1519 std::get<1>(blocks));
1533 DenseMap<Attribute, llvm::SmallDenseSet<AnnoTarget>>
targetMap;
1554static void fixupConnect(ImplicitLocOpBuilder &builder, Value dst, Value src) {
1556 auto dstType = dst.getType();
1557 auto srcType = src.getType();
1558 if (dstType == srcType) {
1564 auto dstBundle = type_cast<BundleType>(dstType);
1565 auto srcBundle = type_cast<BundleType>(srcType);
1566 for (
unsigned i = 0; i < dstBundle.getNumElements(); ++i) {
1567 auto dstField = SubfieldOp::create(builder, dst, i);
1568 auto srcField = SubfieldOp::create(builder, src, i);
1569 if (dstBundle.getElement(i).isFlip) {
1570 std::swap(srcBundle, dstBundle);
1571 std::swap(srcField, dstField);
1581 const DenseMap<Attribute, StringAttr> &dedupMap) {
1587 if (
auto instOp = dyn_cast<InstanceOp>(op)) {
1588 ImplicitLocOpBuilder builder(instOp.getLoc(), instOp->getContext());
1589 builder.setInsertionPointAfter(instOp);
1590 auto module = instanceGraph.lookup(instOp.getModuleNameAttr().getAttr())
1591 ->getModule<FModuleLike>();
1592 for (
auto [index, result] : llvm::enumerate(instOp.getResults())) {
1593 auto newType =
module.getPortType(index);
1594 auto oldType = result.getType();
1596 if (newType == oldType)
1598 LLVM_DEBUG(llvm::dbgs()
1599 <<
"- Updating instance port \"" << instOp.getInstanceName()
1600 <<
"." << instOp.getPortName(index) <<
"\" from " << oldType
1601 <<
" to " << newType <<
"\n");
1605 auto wire = WireOp::create(builder, oldType, instOp.getPortName(index))
1607 result.replaceAllUsesWith(wire);
1608 result.setType(newType);
1609 if (instOp.getPortDirection(index) == Direction::Out)
1618 mlir::AttrTypeReplacer replacer;
1619 replacer.addReplacement([&](FlatSymbolRefAttr symRef) {
1620 auto oldName = symRef.getAttr();
1621 auto newName = dedupMap.lookup(oldName);
1622 if (newName && newName != oldName) {
1623 auto newSymRef = FlatSymbolRefAttr::get(newName);
1624 LLVM_DEBUG(llvm::dbgs()
1625 <<
"- Updating " << symRef <<
" to " << newSymRef <<
" in "
1626 << op->getName() <<
" at " << op->getLoc() <<
"\n");
1633 op->setAttrs(cast<DictionaryAttr>(replacer.replace(op->getAttrDictionary())));
1636 for (
auto ®ion : op->getRegions())
1637 for (
auto &block : region)
1638 for (
auto arg : block.getArguments())
1639 arg.setType(replacer.replace(arg.getType()));
1642 for (
auto result : op->getResults())
1643 result.setType(replacer.replace(result.getType()));
1650 const DenseMap<Operation *, ModuleInfoRef> &moduleToModuleInfo,
1651 const DenseMap<Attribute, StringAttr> &dedupMap) {
1652 for (
auto *node : instanceGraph) {
1655 auto module = node->getModule<FModuleLike>();
1656 auto it = moduleToModuleInfo.find(module);
1657 if (it == moduleToModuleInfo.end())
1661 auto &ops = it->second.info->symbolSensitiveOps;
1664 LLVM_DEBUG(llvm::dbgs()
1665 <<
"- Updating " << ops.size() <<
" symbol-sensitive ops in "
1666 << module.getNameAttr() <<
"\n");
1667 for (
auto *op : ops)
1677class DedupPass :
public circt::firrtl::impl::DedupBase<DedupPass> {
1678 using DedupBase::DedupBase;
1680 void runOnOperation()
override {
1681 auto *
context = &getContext();
1682 auto circuit = getOperation();
1683 auto &instanceGraph = getAnalysis<InstanceGraph>();
1684 auto *nlaTable = &getAnalysis<NLATable>();
1685 auto &symbolTable = getAnalysis<SymbolTable>();
1686 Deduper deduper(instanceGraph, symbolTable, nlaTable, circuit);
1688 auto anythingChanged =
false;
1690 llvm::dbgs() <<
"\n";
1691 debugHeader(Twine(
"Dedup circuit \"") + circuit.getName() +
"\"")
1702 DenseMap<ModuleInfoRef, Operation *> moduleInfoToModule;
1703 DenseMap<Operation *, ModuleInfoRef> moduleToModuleInfo;
1708 DenseMap<Attribute, StringAttr> dedupMap;
1713 SmallVector<FModuleLike, 0> modules;
1715 if (
auto mod = dyn_cast<FModuleLike>(*node.getModule()))
1716 modules.push_back(mod);
1718 LLVM_DEBUG(llvm::dbgs() <<
"Found " << modules.size() <<
" modules\n");
1720 SmallVector<std::optional<ModuleInfo>> moduleInfos(modules.size());
1724 auto dedupGroupAttrName = StringAttr::get(
context,
"firrtl.dedup_group");
1730 for (
auto module : modules) {
1731 llvm::SmallSetVector<StringAttr, 1> groups;
1733 module, [&groups, dedupGroupClass](
Annotation annotation) {
1736 groups.insert(annotation.
getMember<StringAttr>(
"group"));
1739 if (groups.size() > 1) {
1740 module.emitError("module belongs to multiple dedup groups: ") << groups;
1741 return signalPassFailure();
1743 assert(!module->hasAttr(dedupGroupAttrName) &&
1744 "unexpected existing use of temporary dedup group attribute");
1745 if (!groups.empty())
1746 module->setDiscardableAttr(dedupGroupAttrName, groups.front());
1750 LLVM_DEBUG(llvm::dbgs() <<
"Computing module information\n");
1751 auto result = mlir::failableParallelForEach(
1752 context, llvm::seq(modules.size()), [&](
unsigned idx) {
1753 auto module = modules[idx];
1755 if (AnnotationSet::hasAnnotation(module, noDedupClass))
1759 if (auto ext = dyn_cast<FExtModuleOp>(*module);
1760 ext && !ext.getDefname().has_value())
1764 if (isa<ClassOp>(*module) && !dedupClasses)
1767 StructuralHasher hasher(hasherConstants);
1769 moduleInfos[idx] = hasher.getModuleInfo(module);
1775 auto &os = llvm::dbgs();
1776 for (
auto [module, info] :
llvm::zip(modules, moduleInfos)) {
1779 os << llvm::format_bytes(
info->structuralHash, std::nullopt, 32, 32);
1781 os <<
"--------------------------------";
1782 os <<
"--------------------------------";
1784 os <<
" for " <<
module.getModuleNameAttr() << "\n";
1788 if (result.failed())
1789 return signalPassFailure();
1791 LLVM_DEBUG(llvm::dbgs() <<
"Update modules\n");
1792 for (
auto [i, module] :
llvm::enumerate(modules)) {
1793 auto moduleName =
module.getModuleNameAttr();
1794 auto &maybeModuleInfo = moduleInfos[i];
1796 if (!maybeModuleInfo) {
1801 dedupMap[moduleName] = moduleName;
1805 auto &moduleInfo = maybeModuleInfo.value();
1806 moduleToModuleInfo.try_emplace(module, &moduleInfo);
1809 for (
auto &referredModule : moduleInfo.referredModuleNames)
1810 referredModule = dedupMap[referredModule];
1813 auto it = moduleInfoToModule.find(&moduleInfo);
1814 if (it != moduleInfoToModule.end()) {
1815 auto original = cast<FModuleLike>(it->second);
1816 auto originalName = original.getModuleNameAttr();
1825 for (
auto &[_, dedupedName] : dedupMap)
1826 if (dedupedName == originalName)
1827 dedupedName = moduleName;
1830 it->second =
module;
1832 std::swap(originalName, moduleName);
1833 std::swap(original, module);
1837 LLVM_DEBUG(llvm::dbgs() <<
"- Replace " << moduleName <<
" with "
1838 << originalName <<
"\n");
1839 dedupMap[moduleName] = originalName;
1840 deduper.dedup(original, module);
1842 anythingChanged =
true;
1846 deduper.record(module);
1848 dedupMap[moduleName] = moduleName;
1850 moduleInfoToModule[&moduleInfo] =
module;
1858 auto failed =
false;
1860 auto parseModule = [&](Attribute path) -> StringAttr {
1863 auto [_, rhs] = cast<StringAttr>(path).getValue().split(
'|');
1864 return StringAttr::get(
context, rhs);
1869 auto getLead = [&](StringAttr module) -> StringAttr {
1870 auto it = dedupMap.find(module);
1871 if (it == dedupMap.end()) {
1872 auto diag = emitError(circuit.getLoc(),
1873 "MustDeduplicateAnnotation references module ")
1874 <<
module << " which does not exist";
1881 LLVM_DEBUG(llvm::dbgs() <<
"Update annotations\n");
1885 auto modules = annotation.
getMember<ArrayAttr>(
"modules");
1887 emitError(circuit.getLoc(),
1888 "MustDeduplicateAnnotation missing \"modules\" member");
1893 if (modules.empty())
1896 auto firstModule = parseModule(modules[0]);
1897 auto firstLead = getLead(firstModule);
1901 for (
auto attr : modules.getValue().drop_front()) {
1902 auto nextModule = parseModule(attr);
1903 auto nextLead = getLead(nextModule);
1906 if (firstLead != nextLead) {
1907 auto diag = emitError(circuit.getLoc(),
"module ")
1908 << nextModule <<
" not deduplicated with " << firstModule;
1911 equiv.check(diag, a, b);
1919 return signalPassFailure();
1922 for (
auto module : circuit.getOps<FModuleLike>())
1923 module->removeDiscardableAttr(dedupGroupAttrName);
1929 markAnalysesPreserved<NLATable>();
1930 if (!anythingChanged)
1931 markAllAnalysesPreserved();
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static void mergeRegions(Region *region1, Region *region2)
static Block * getBodyBlock(FModuleLike mod)
static InstancePath empty
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.
A table of inner symbols and their resolutions.
auto getModule()
Get the module that this node is tracking.
decltype(auto) walkPostOrder(Fn &&fn)
Perform a post-order walk across the modules.
InstanceGraphNode * lookup(ModuleOpInterface op)
Look up an InstanceGraphNode for a module.
static StringRef toString(Direction direction)
FieldRef getFieldRefFromValue(Value value, bool lookThroughCasts=false)
Get the FieldRef from a value.
constexpr const char * mustDedupAnnoClass
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::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.
static bool operator==(const ModulePort &a, const ModulePort &b)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
llvm::raw_ostream & debugHeader(const llvm::Twine &str, unsigned width=80)
Write a "header"-like string to the debug stream with a certain width.
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...
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".
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...
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 recordAnnotations(AnnoTarget target)
For a specific annotation target, record all the unique NLAs which target it in the targetMap.
NLATable * nlaTable
Cached nla table analysis.
hw::InnerSymAttr mergeInnerSymbols(RenameMap &renameMap, FModuleLike toModule, hw::InnerSymAttr toSym, hw::InnerSymAttr fromSym)
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
hw::InnerSymbolNamespace & getNamespace(Operation *module)
Get a cached namespace for a module.
SymbolTable & symbolTable
void mergeOps(RenameMap &renameMap, FModuleLike toModule, Operation *to, FModuleLike fromModule, Operation *from)
Recursively merge two operations.
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)
A reference to a ModuleInfo that compares and hashes like it.
ModuleInfoRef(ModuleInfo *info)
std::vector< Operation * > symbolSensitiveOps
std::vector< StringAttr > referredModuleNames
std::array< uint8_t, 32 > structuralHash
This struct contains constant string attributes shared across different threads.
StringAttr moduleNameAttr
DenseSet< Attribute > nonessentialAttributes
StructuralHasherSharedConstants(MLIRContext *context)
void populateInnerSymIDTable(FModuleLike module)
Find all the ports and operations which may define an inner symbol operations and give each a unique ...
void update(Operation *op, DictionaryAttr dict)
Hash the top level attribute dictionary of the operation.
void update(const void *pointer)
void update(ClassType type)
DenseMap< void *, unsigned > idTable
void update(const std::pair< T, U > &pair)
void update(Operation *op)
DenseMap< StringAttr, std::pair< size_t, size_t > > innerSymIDTable
ModuleInfo getModuleInfo(FModuleLike module)
void update(size_t value)
void update(BundleType type)
unsigned getID(void *object)
void update(OpResult result)
void update(OpOperand &operand)
StructuralHasher(const StructuralHasherSharedConstants &constants)
std::vector< Operation * > symbolSensitiveOps
void update(Region *region)
void update(Block *block)
std::vector< StringAttr > referredModuleNames
void update(TypeID typeID)
const StructuralHasherSharedConstants & constants
std::pair< size_t, size_t > getInnerSymID(StringAttr name)
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 bool isEqual(const ModuleInfoRef &lhs, const ModuleInfoRef &rhs)
static ModuleInfoRef getTombstoneKey()
static ModuleInfoRef getEmptyKey()
static unsigned getHashValue(const ModuleInfoRef &ref)