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 "bundle element \'" + aElement.name.getValue() +
"'", a,
520 aElement.type, b, bElement.type)))
526 LogicalResult
check(InFlightDiagnostic &diag,
const Twine &message,
527 Operation *a, Type aType, Operation *b, Type bType) {
530 if (
auto aBundleType = type_dyn_cast<BundleType>(aType))
531 if (
auto bBundleType = type_dyn_cast<BundleType>(bType))
532 return check(diag, message, a, aBundleType, b, bBundleType);
533 if (type_isa<RefType>(aType) && type_isa<RefType>(bType) &&
535 diag.attachNote(a->getLoc())
536 << message <<
", has a RefType with a different base type "
537 << type_cast<RefType>(aType).getType()
538 <<
" in the same position of the two modules marked as 'must dedup'. "
539 "(This may be due to Grand Central Taps or Views being different "
540 "between the two modules.)";
541 diag.attachNote(b->getLoc())
542 <<
"the second module has a different base type "
543 << type_cast<RefType>(bType).getType();
546 diag.attachNote(a->getLoc())
547 << message <<
" types don't match, first type is " << aType;
548 diag.attachNote(b->getLoc()) <<
"second type is " << bType;
553 Block &aBlock, Operation *b, Block &bBlock) {
556 auto portNames = a->getAttrOfType<ArrayAttr>(
"portNames");
558 auto emitMissingPort = [&](Value existsVal, Operation *opExists,
559 Operation *opDoesNotExist) {
561 auto portNames = opExists->getAttrOfType<ArrayAttr>(
"portNames");
563 if (
auto portNameAttr = dyn_cast<StringAttr>(portNames[portNo]))
564 portName = portNameAttr.getValue();
565 if (type_isa<RefType>(existsVal.getType())) {
566 diag.attachNote(opExists->getLoc())
567 <<
" contains a RefType port named '" + portName +
568 "' that only exists in one of the modules (can be due to "
569 "difference in Grand Central Tap or View of two modules "
570 "marked with must dedup)";
571 diag.attachNote(opDoesNotExist->getLoc())
572 <<
"second module to be deduped that does not have the RefType "
575 diag.attachNote(opExists->getLoc())
576 <<
"port '" + portName +
"' only exists in one of the modules";
577 diag.attachNote(opDoesNotExist->getLoc())
578 <<
"second module to be deduped that does not have the port";
584 llvm::zip_longest(aBlock.getArguments(), bBlock.getArguments())) {
585 auto &aArg = std::get<0>(argPair);
586 auto &bArg = std::get<1>(argPair);
587 if (aArg.has_value() && bArg.has_value()) {
592 if (
auto portNameAttr = dyn_cast<StringAttr>(portNames[portNo]))
593 portName = portNameAttr.getValue();
596 if (failed(
check(diag,
"module port '" + portName +
"'", a,
597 aArg->getType(), b, bArg->getType())))
599 data.map.map(aArg.value(), bArg.value());
603 if (!aArg.has_value())
605 return emitMissingPort(aArg.has_value() ? aArg.value() : bArg.value(), a,
610 auto aIt = aBlock.begin();
611 auto aEnd = aBlock.end();
612 auto bIt = bBlock.begin();
613 auto bEnd = bBlock.end();
614 while (aIt != aEnd && bIt != bEnd)
615 if (failed(
check(diag,
data, &*aIt++, &*bIt++)))
618 diag.attachNote(aIt->getLoc()) <<
"first block has more operations";
619 diag.attachNote(b->getLoc()) <<
"second block here";
623 diag.attachNote(bIt->getLoc()) <<
"second block has more operations";
624 diag.attachNote(a->getLoc()) <<
"first block here";
631 Region &aRegion, Operation *b, Region &bRegion) {
632 auto aIt = aRegion.begin();
633 auto aEnd = aRegion.end();
634 auto bIt = bRegion.begin();
635 auto bEnd = bRegion.end();
638 while (aIt != aEnd && bIt != bEnd)
639 if (failed(
check(diag,
data, a, *aIt++, b, *bIt++)))
641 if (aIt != aEnd || bIt != bEnd) {
642 diag.attachNote(a->getLoc())
643 <<
"operation regions have different number of blocks";
644 diag.attachNote(b->getLoc()) <<
"second operation here";
650 LogicalResult
check(InFlightDiagnostic &diag, Operation *a,
651 mlir::DenseBoolArrayAttr aAttr, Operation *b,
652 mlir::DenseBoolArrayAttr bAttr) {
655 auto portNames = a->getAttrOfType<ArrayAttr>(
"portNames");
656 for (
unsigned i = 0, e = aAttr.size(); i < e; ++i) {
657 auto aDirection = aAttr[i];
658 auto bDirection = bAttr[i];
659 if (aDirection != bDirection) {
660 auto ¬e = diag.attachNote(a->getLoc()) <<
"module port ";
662 note <<
"'" << cast<StringAttr>(portNames[i]).getValue() <<
"'";
665 note <<
" directions don't match, first direction is '"
667 diag.attachNote(b->getLoc()) <<
"second direction is '"
676 DictionaryAttr aDict, Operation *b,
677 DictionaryAttr bDict) {
682 DenseSet<Attribute> seenAttrs;
683 for (
auto namedAttr : aDict) {
684 auto attrName = namedAttr.getName();
688 auto aAttr = namedAttr.getValue();
689 auto bAttr = bDict.get(attrName);
691 diag.attachNote(a->getLoc())
692 <<
"second operation is missing attribute " << attrName;
693 diag.attachNote(b->getLoc()) <<
"second operation here";
697 if (isa<hw::InnerRefAttr>(aAttr) && isa<hw::InnerRefAttr>(bAttr)) {
698 auto bRef = cast<hw::InnerRefAttr>(bAttr);
699 auto aRef = cast<hw::InnerRefAttr>(aAttr);
701 auto aTarget =
data.a.lookup(aRef.getName());
702 auto bTarget =
data.b.lookup(bRef.getName());
703 if (!aTarget || !bTarget)
704 diag.attachNote(a->getLoc())
705 <<
"malformed ir, possibly violating use-before-def";
707 diag.attachNote(a->getLoc())
708 <<
"operations have different targets, first operation has "
710 diag.attachNote(b->getLoc()) <<
"second operation has " << bTarget;
713 if (aTarget.isPort()) {
715 if (!bTarget.isPort() || aTarget.getPort() != bTarget.getPort())
719 if (!bTarget.isOpOnly() ||
720 aTarget.getOp() !=
data.map.lookup(bTarget.getOp()))
723 if (aTarget.getField() != bTarget.getField())
728 if (failed(
check(diag, a, cast<mlir::DenseBoolArrayAttr>(aAttr), b,
729 cast<mlir::DenseBoolArrayAttr>(bAttr))))
731 }
else if (isa<DistinctAttr>(aAttr) && isa<DistinctAttr>(bAttr)) {
734 }
else if (aAttr != bAttr) {
735 diag.attachNote(a->getLoc())
736 <<
"first operation has attribute '" << attrName.getValue()
738 diag.attachNote(b->getLoc())
739 <<
"second operation has value " <<
prettyPrint(bAttr);
742 seenAttrs.insert(attrName);
744 if (aDict.getValue().size() != bDict.getValue().size()) {
745 for (
auto namedAttr : bDict) {
746 auto attrName = namedAttr.getName();
750 seenAttrs.contains(attrName))
753 diag.attachNote(a->getLoc())
754 <<
"first operation is missing attribute " << attrName;
755 diag.attachNote(b->getLoc()) <<
"second operation here";
763 LogicalResult
check(InFlightDiagnostic &diag, FInstanceLike a,
765 auto aName = a.getReferencedModuleNameAttr();
766 auto bName = b.getReferencedModuleNameAttr();
776 diag.attachNote(std::nullopt)
777 <<
"in instance " << a.getInstanceNameAttr() <<
" of " << aName
778 <<
", and instance " << b.getInstanceNameAttr() <<
" of " << bName;
779 check(diag, aModule, bModule);
787 if (a->getName() != b->getName()) {
788 diag.attachNote(a->getLoc()) <<
"first operation is a " << a->getName();
789 diag.attachNote(b->getLoc()) <<
"second operation is a " << b->getName();
795 if (
auto aInst = dyn_cast<FInstanceLike>(a)) {
796 auto bInst = cast<FInstanceLike>(b);
797 if (failed(
check(diag, aInst, bInst)))
802 if (a->getNumResults() != b->getNumResults()) {
803 diag.attachNote(a->getLoc())
804 <<
"operations have different number of results";
805 diag.attachNote(b->getLoc()) <<
"second operation here";
808 for (
auto resultPair : llvm::zip(a->getResults(), b->getResults())) {
809 auto &aValue = std::get<0>(resultPair);
810 auto &bValue = std::get<1>(resultPair);
811 if (failed(
check(diag,
"operation result", a, aValue.getType(), b,
814 data.map.map(aValue, bValue);
818 if (a->getNumOperands() != b->getNumOperands()) {
819 diag.attachNote(a->getLoc())
820 <<
"operations have different number of operands";
821 diag.attachNote(b->getLoc()) <<
"second operation here";
824 for (
auto operandPair : llvm::zip(a->getOperands(), b->getOperands())) {
825 auto &aValue = std::get<0>(operandPair);
826 auto &bValue = std::get<1>(operandPair);
827 if (bValue !=
data.map.lookup(aValue)) {
828 diag.attachNote(a->getLoc())
829 <<
"operations use different operands, first operand is '"
834 diag.attachNote(b->getLoc())
835 <<
"second operand is '"
839 <<
"', but should have been '"
850 if (a->getNumRegions() != b->getNumRegions()) {
851 diag.attachNote(a->getLoc())
852 <<
"operations have different number of regions";
853 diag.attachNote(b->getLoc()) <<
"second operation here";
856 for (
auto regionPair : llvm::zip(a->getRegions(), b->getRegions())) {
857 auto &aRegion = std::get<0>(regionPair);
858 auto &bRegion = std::get<1>(regionPair);
859 if (failed(
check(diag,
data, a, aRegion, b, bRegion)))
864 if (failed(
check(diag,
data, a, a->getAttrDictionary(), b,
865 b->getAttrDictionary())))
871 void check(InFlightDiagnostic &diag, Operation *a, Operation *b) {
876 diag.attachNote(a->getLoc()) <<
"module marked NoDedup";
880 diag.attachNote(b->getLoc()) <<
"module marked NoDedup";
883 auto aSymbol = cast<mlir::SymbolOpInterface>(a);
884 auto bSymbol = cast<mlir::SymbolOpInterface>(b);
886 diag.attachNote(a->getLoc())
888 << (aSymbol.isPrivate() ?
"private but not discardable" :
"public");
889 diag.attachNote(b->getLoc())
891 << (bSymbol.isPrivate() ?
"private but not discardable" :
"public");
896 auto bGroup = dyn_cast_or_null<StringAttr>(
898 if (aGroup != bGroup) {
900 diag.attachNote(b->getLoc())
901 <<
"module is in dedup group '" << bGroup.str() <<
"'";
903 diag.attachNote(b->getLoc()) <<
"module is not part of a dedup group";
906 diag.attachNote(a->getLoc())
907 <<
"module is in dedup group '" << aGroup.str() <<
"'";
909 diag.attachNote(a->getLoc()) <<
"module is not part of a dedup group";
915 diag.attachNote(a->getLoc()) <<
"first module here";
916 diag.attachNote(b->getLoc()) <<
"second module here";
940static Location
mergeLoc(MLIRContext *context, Location to, Location from) {
942 llvm::SmallSetVector<Location, 4> decomposedLocs;
944 unsigned seenFIR = 0;
945 for (
auto loc : {to, from}) {
948 if (
auto fusedLoc = dyn_cast<FusedLoc>(loc)) {
951 for (
auto loc : fusedLoc.getLocations()) {
952 if (FileLineColLoc fileLoc = dyn_cast<FileLineColLoc>(loc)) {
953 if (fileLoc.getFilename().strref().ends_with(
".fir")) {
959 decomposedLocs.insert(loc);
965 if (FileLineColLoc fileLoc = dyn_cast<FileLineColLoc>(loc)) {
966 if (fileLoc.getFilename().strref().ends_with(
".fir")) {
973 if (!isa<UnknownLoc>(loc))
974 decomposedLocs.insert(loc);
977 auto locs = decomposedLocs.getArrayRef();
982 return UnknownLoc::get(context);
983 if (locs.size() == 1)
986 return FusedLoc::get(context, locs);
1001 for (
auto nla : circuit.getOps<hw::HierPathOp>())
1002 nlaCache[nla.getNamepathAttr()] = nla.getSymNameAttr();
1009 void dedup(FModuleLike toModule, FModuleLike fromModule) {
1015 SmallVector<Attribute> newLocs;
1016 for (
auto [toLoc, fromLoc] : llvm::zip(toModule.getPortLocations(),
1017 fromModule.getPortLocations())) {
1018 if (toLoc == fromLoc)
1019 newLocs.push_back(toLoc);
1022 cast<LocationAttr>(fromLoc)));
1024 toModule->setAttr(
"portLocations", ArrayAttr::get(
context, newLocs));
1027 mergeOps(renameMap, toModule, toModule, fromModule, fromModule);
1033 if (
auto to = dyn_cast<FModuleOp>(*toModule))
1037 fromModule.getModuleNameAttr());
1048 for (
unsigned i = 0, e =
getNumPorts(module); i < e; ++i)
1051 module->walk([&](Operation *op) { recordAnnotations(op); });
1057 return moduleNamespaces.try_emplace(module, cast<FModuleLike>(module))
1065 if (
auto nlaRef = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal"))
1066 targetMap[nlaRef.getAttr()].insert(target);
1075 auto mem = dyn_cast<MemOp>(op);
1080 for (
unsigned i = 0, e = mem->getNumResults(); i < e; ++i)
1089 instanceGraph[::cast<igraph::ModuleOpInterface>(fromModule)];
1090 auto *toNode = instanceGraph[toModule];
1091 auto toModuleRef = FlatSymbolRefAttr::get(toModule.getModuleNameAttr());
1092 for (
auto *oldInstRec : llvm::make_early_inc_range(fromNode->uses())) {
1093 auto inst = oldInstRec->getInstance();
1094 if (
auto instOp = dyn_cast<InstanceOp>(*inst)) {
1095 instOp.setModuleNameAttr(toModuleRef);
1096 instOp.setPortNamesAttr(toModule.getPortNamesAttr());
1097 }
else if (
auto objectOp = dyn_cast<ObjectOp>(*inst)) {
1098 auto classLike = cast<ClassLike>(*toNode->getModule());
1099 ClassType classType = detail::getInstanceTypeForClassLike(classLike);
1100 objectOp.getResult().setType(classType);
1102 oldInstRec->getParent()->addInstance(inst, toNode);
1103 oldInstRec->erase();
1105 instanceGraph.erase(fromNode);
1106 fromModule->erase();
1114 SmallVector<FlatSymbolRefAttr>
1115 createNLAs(Operation *fromModule, ArrayRef<Attribute> baseNamepath,
1116 SymbolTable::Visibility vis = SymbolTable::Visibility::Private) {
1119 SmallVector<Attribute> namepath = {
nullptr};
1120 namepath.append(baseNamepath.begin(), baseNamepath.end());
1122 auto loc = fromModule->getLoc();
1123 auto *fromNode = instanceGraph[cast<igraph::ModuleOpInterface>(fromModule)];
1124 SmallVector<FlatSymbolRefAttr> nlas;
1125 for (
auto *instanceRecord : fromNode->uses()) {
1126 auto parent = cast<FModuleOp>(*instanceRecord->getParent()->getModule());
1127 auto inst = instanceRecord->getInstance();
1129 auto arrayAttr = ArrayAttr::get(context, namepath);
1131 auto &cacheEntry = nlaCache[arrayAttr];
1133 auto builder = OpBuilder::atBlockBegin(nlaBlock);
1134 auto nla = hw::HierPathOp::create(builder, loc,
"nla", arrayAttr);
1136 symbolTable.insert(nla);
1138 cacheEntry = nla.getNameAttr();
1139 nla.setVisibility(vis);
1140 nlaTable->addNLA(nla);
1142 auto nlaRef = FlatSymbolRefAttr::get(cast<StringAttr>(cacheEntry));
1143 nlas.push_back(nlaRef);
1151 SmallVector<FlatSymbolRefAttr>
1153 SymbolTable::Visibility vis = SymbolTable::Visibility::Private) {
1154 return createNLAs(fromModule, FlatSymbolRefAttr::get(toModuleName), vis);
1161 Annotation anno, ArrayRef<NamedAttribute> attributes,
1162 unsigned nonLocalIndex,
1163 SmallVectorImpl<Annotation> &newAnnotations) {
1164 SmallVector<NamedAttribute> mutableAttributes(attributes.begin(),
1166 for (
auto &nla : nlas) {
1168 mutableAttributes[nonLocalIndex].setValue(nla);
1169 auto dict = DictionaryAttr::getWithSorted(context, mutableAttributes);
1172 newAnnotations.push_back(anno);
1181 targetMap.erase(nla.getNameAttr());
1182 nlaTable->erase(nla);
1183 nlaCache.erase(nla.getNamepathAttr());
1184 symbolTable.erase(nla);
1190 FModuleOp fromModule) {
1191 auto toName = toModule.getNameAttr();
1192 auto fromName = fromModule.getNameAttr();
1195 auto moduleNLAs = nlaTable->lookup(fromModule.getNameAttr()).vec();
1197 nlaTable->renameModuleAndInnerRef(toName, fromName, renameMap);
1200 for (
auto nla : moduleNLAs) {
1201 auto elements = nla.getNamepath().getValue();
1203 if (nla.root() != toName)
1206 SmallVector<Attribute> namepath(elements.begin(), elements.end());
1207 auto nlaRefs = createNLAs(fromModule, namepath, nla.getVisibility());
1209 auto &set = targetMap[nla.getSymNameAttr()];
1210 SmallVector<AnnoTarget> targets(set.begin(), set.end());
1212 for (
auto target : targets) {
1215 SmallVector<Annotation> newAnnotations;
1216 for (
auto anno : target.getAnnotations()) {
1218 auto [it, found] = mlir::impl::findAttrSorted(
1219 anno.begin(), anno.end(), nonLocalString);
1222 if (!found || cast<FlatSymbolRefAttr>(it->getValue()).getAttr() !=
1223 nla.getSymNameAttr()) {
1224 newAnnotations.push_back(anno);
1227 auto nonLocalIndex = std::distance(anno.begin(), it);
1229 cloneAnnotation(nlaRefs, anno,
1230 ArrayRef<NamedAttribute>(anno.begin(), anno.end()),
1231 nonLocalIndex, newAnnotations);
1236 target.setAnnotations(annotations);
1238 for (
auto nla : nlaRefs)
1239 targetMap[nla.getAttr()].insert(target);
1251 FModuleOp fromModule) {
1252 addAnnotationContext(renameMap, toModule, toModule);
1253 addAnnotationContext(renameMap, toModule, fromModule);
1259 StringAttr fromName) {
1260 nlaTable->renameModuleAndInnerRef(toName, fromName, renameMap);
1268 SmallVectorImpl<Annotation> &newAnnotations) {
1271 SmallVector<NamedAttribute> attributes;
1272 int nonLocalIndex = -1;
1273 for (
const auto &val : llvm::enumerate(anno)) {
1274 auto attr = val.value();
1276 auto compare = attr.getName().compare(nonLocalString);
1277 assert(compare != 0 &&
"should not pass non-local annotations here");
1281 nonLocalIndex = val.index();
1282 attributes.push_back(NamedAttribute(nonLocalString, nonLocalString));
1287 attributes.push_back(attr);
1289 if (nonLocalIndex == -1) {
1291 nonLocalIndex = attributes.size();
1292 attributes.push_back(NamedAttribute(nonLocalString, nonLocalString));
1295 attributes.append(anno.
begin() + nonLocalIndex, anno.
end());
1299 auto nlaRefs = createNLAs(toModuleName, fromModule);
1300 for (
auto nla : nlaRefs)
1301 targetMap[nla.getAttr()].insert(to);
1304 cloneAnnotation(nlaRefs, anno, attributes, nonLocalIndex, newAnnotations);
1310 SmallVectorImpl<Annotation> &newAnnotations,
1311 SmallPtrSetImpl<Attribute> &dontTouches) {
1312 for (
auto anno : annos) {
1316 anno.removeMember(
"circt.nonlocal");
1317 auto [it, inserted] = dontTouches.insert(anno.getAttr());
1319 newAnnotations.push_back(anno);
1324 if (
auto nla = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
1325 newAnnotations.push_back(anno);
1326 targetMap[nla.getAttr()].insert(to);
1330 makeAnnotationNonLocal(toModule.getModuleNameAttr(), to, fromModule, anno,
1341 SmallVector<Annotation> newAnnotations;
1345 llvm::SmallPtrSet<Attribute, 4> dontTouches;
1349 copyAnnotations(toModule, to, toModule, toAnnos, newAnnotations,
1351 copyAnnotations(toModule, to, fromModule, fromAnnos, newAnnotations,
1355 if (!newAnnotations.empty())
1361 FModuleLike fromModule, Operation *from) {
1367 if (toModule == to) {
1369 for (
unsigned i = 0, e =
getNumPorts(toModule); i < e; ++i)
1374 }
else if (
auto toMem = dyn_cast<MemOp>(to)) {
1376 auto fromMem = cast<MemOp>(from);
1377 for (
unsigned i = 0, e = toMem.getNumResults(); i < e; ++i)
1386 hw::InnerSymAttr toSym,
1387 hw::InnerSymAttr fromSym) {
1388 if (fromSym && !fromSym.getProps().empty()) {
1389 auto &isn = getNamespace(toModule);
1391 SmallVector<hw::InnerSymPropertiesAttr> newProps;
1394 llvm::append_range(newProps, toSym);
1396 for (
auto fromProp : fromSym) {
1397 hw::InnerSymPropertiesAttr newProp;
1398 auto *it = llvm::find_if(newProps, [&](
auto p) {
1399 return p.getFieldID() == fromProp.getFieldID();
1401 if (it != newProps.end()) {
1406 if (fromProp.getSymVisibility().getValue() ==
"public" &&
1407 newProp.getSymVisibility().getValue() !=
"public") {
1408 *it = hw::InnerSymPropertiesAttr::get(context, newProp.getName(),
1409 newProp.getFieldID(),
1410 fromProp.getSymVisibility());
1414 auto newName = isn.newName(fromProp.getName().getValue());
1415 newProp = hw::InnerSymPropertiesAttr::get(
1416 context, StringAttr::get(context, newName), fromProp.getFieldID(),
1417 fromProp.getSymVisibility());
1418 newProps.push_back(newProp);
1420 renameMap[fromProp.getName()] = newProp.getName();
1423 llvm::sort(newProps, [](
auto &p,
auto &q) {
1424 return p.getFieldID() < q.getFieldID();
1427 return hw::InnerSymAttr::get(context, newProps);
1429 return hw::InnerSymAttr();
1436 Operation *to, FModuleLike fromModule,
1440 if (
auto fromInnerSym = dyn_cast<hw::InnerSymbolOpInterface>(from)) {
1441 auto toInnerSym = cast<hw::InnerSymbolOpInterface>(to);
1442 if (
auto newSymAttr = mergeInnerSymbols(renameMap, toModule,
1443 toInnerSym.getInnerSymAttr(),
1444 fromInnerSym.getInnerSymAttr()))
1445 toInnerSym.setInnerSymbolAttr(newSymAttr);
1449 auto fromPortSyms = from->getAttrOfType<ArrayAttr>(
"portSymbols");
1450 if (!fromPortSyms || fromPortSyms.empty())
1453 auto portCount = fromPortSyms.size();
1454 auto toPortSyms = to->getAttrOfType<ArrayAttr>(
"portSymbols");
1458 SmallVector<Attribute> newPortSyms;
1459 if (toPortSyms.empty())
1460 newPortSyms.assign(portCount, hw::InnerSymAttr());
1462 newPortSyms.assign(toPortSyms.begin(), toPortSyms.end());
1464 for (
unsigned portNo = 0; portNo < portCount; ++portNo) {
1465 if (
auto newPortSym = mergeInnerSymbols(
1466 renameMap, toModule,
1467 llvm::cast_if_present<hw::InnerSymAttr>(newPortSyms[portNo]),
1468 cast<hw::InnerSymAttr>(fromPortSyms[portNo]))) {
1469 newPortSyms[portNo] = newPortSym;
1474 FModuleLike::fixupPortSymsArray(newPortSyms, toModule.getContext());
1475 cast<FModuleLike>(to).setPortSymbols(newPortSyms);
1481 FModuleLike fromModule, Operation *from) {
1483 if (to->getLoc() != from->getLoc())
1484 to->setLoc(
mergeLoc(context, to->getLoc(), from->getLoc()));
1487 for (
auto regions : llvm::zip(to->getRegions(), from->getRegions()))
1488 mergeRegions(renameMap, toModule, std::get<0>(regions), fromModule,
1489 std::get<1>(regions));
1492 recordSymRenames(renameMap, toModule, to, fromModule, from);
1495 mergeAnnotations(toModule, to, fromModule, from);
1500 FModuleLike fromModule, Block &fromBlock) {
1502 for (
auto [toArg, fromArg] :
1503 llvm::zip(toBlock.getArguments(), fromBlock.getArguments()))
1504 if (toArg.getLoc() != fromArg.getLoc())
1505 toArg.setLoc(
mergeLoc(context, toArg.getLoc(), fromArg.getLoc()));
1507 for (
auto ops : llvm::zip(toBlock, fromBlock))
1508 mergeOps(renameMap, toModule, &std::get<0>(ops), fromModule,
1514 Region &toRegion, FModuleLike fromModule,
1515 Region &fromRegion) {
1516 for (
auto blocks : llvm::zip(toRegion, fromRegion))
1517 mergeBlocks(renameMap, toModule, std::get<0>(blocks), fromModule,
1518 std::get<1>(blocks));
1532 DenseMap<Attribute, llvm::SmallDenseSet<AnnoTarget>>
targetMap;
1553static void fixupConnect(ImplicitLocOpBuilder &builder, Value dst, Value src) {
1555 auto dstType = dst.getType();
1556 auto srcType = src.getType();
1557 if (dstType == srcType) {
1563 auto dstBundle = type_cast<BundleType>(dstType);
1564 auto srcBundle = type_cast<BundleType>(srcType);
1565 for (
unsigned i = 0; i < dstBundle.getNumElements(); ++i) {
1566 auto dstField = SubfieldOp::create(builder, dst, i);
1567 auto srcField = SubfieldOp::create(builder, src, i);
1568 if (dstBundle.getElement(i).isFlip) {
1569 std::swap(srcBundle, dstBundle);
1570 std::swap(srcField, dstField);
1580 const DenseMap<Attribute, StringAttr> &dedupMap) {
1586 if (
auto instOp = dyn_cast<InstanceOp>(op)) {
1587 ImplicitLocOpBuilder builder(instOp.getLoc(), instOp->getContext());
1588 builder.setInsertionPointAfter(instOp);
1589 auto module = instanceGraph.lookup(instOp.getModuleNameAttr().getAttr())
1590 ->getModule<FModuleLike>();
1591 for (
auto [index, result] : llvm::enumerate(instOp.getResults())) {
1592 auto newType =
module.getPortType(index);
1593 auto oldType = result.getType();
1595 if (newType == oldType)
1597 LLVM_DEBUG(llvm::dbgs()
1598 <<
"- Updating instance port \"" << instOp.getInstanceName()
1599 <<
"." << instOp.getPortName(index).getValue() <<
"\" from "
1600 << oldType <<
" to " << newType <<
"\n");
1604 auto wire = WireOp::create(builder, oldType, instOp.getPortName(index))
1606 result.replaceAllUsesWith(wire);
1607 result.setType(newType);
1608 if (instOp.getPortDirection(index) == Direction::Out)
1617 mlir::AttrTypeReplacer replacer;
1618 replacer.addReplacement([&](FlatSymbolRefAttr symRef) {
1619 auto oldName = symRef.getAttr();
1620 auto newName = dedupMap.lookup(oldName);
1621 if (newName && newName != oldName) {
1622 auto newSymRef = FlatSymbolRefAttr::get(newName);
1623 LLVM_DEBUG(llvm::dbgs()
1624 <<
"- Updating " << symRef <<
" to " << newSymRef <<
" in "
1625 << op->getName() <<
" at " << op->getLoc() <<
"\n");
1632 op->setAttrs(cast<DictionaryAttr>(replacer.replace(op->getAttrDictionary())));
1635 for (
auto ®ion : op->getRegions())
1636 for (
auto &block : region)
1637 for (
auto arg : block.getArguments())
1638 arg.setType(replacer.replace(arg.getType()));
1641 for (
auto result : op->getResults())
1642 result.setType(replacer.replace(result.getType()));
1649 const DenseMap<Operation *, ModuleInfoRef> &moduleToModuleInfo,
1650 const DenseMap<Attribute, StringAttr> &dedupMap) {
1651 for (
auto *node : instanceGraph) {
1654 auto module = node->getModule<FModuleLike>();
1655 auto it = moduleToModuleInfo.find(module);
1656 if (it == moduleToModuleInfo.end())
1660 auto &ops = it->second.info->symbolSensitiveOps;
1663 LLVM_DEBUG(llvm::dbgs()
1664 <<
"- Updating " << ops.size() <<
" symbol-sensitive ops in "
1665 << module.getNameAttr() <<
"\n");
1666 for (
auto *op : ops)
1676class DedupPass :
public circt::firrtl::impl::DedupBase<DedupPass> {
1677 void runOnOperation()
override {
1678 auto *context = &getContext();
1679 auto circuit = getOperation();
1680 auto &instanceGraph = getAnalysis<InstanceGraph>();
1681 auto *nlaTable = &getAnalysis<NLATable>();
1682 auto &symbolTable = getAnalysis<SymbolTable>();
1683 Deduper deduper(instanceGraph, symbolTable, nlaTable, circuit);
1685 auto anythingChanged =
false;
1687 llvm::dbgs() <<
"\n";
1688 debugHeader(Twine(
"Dedup circuit \"") + circuit.getName() +
"\"")
1699 DenseMap<ModuleInfoRef, Operation *> moduleInfoToModule;
1700 DenseMap<Operation *, ModuleInfoRef> moduleToModuleInfo;
1705 DenseMap<Attribute, StringAttr> dedupMap;
1710 SmallVector<FModuleLike, 0> modules;
1712 if (
auto mod = dyn_cast<FModuleLike>(*node.getModule()))
1713 modules.push_back(mod);
1715 LLVM_DEBUG(llvm::dbgs() <<
"Found " << modules.size() <<
" modules\n");
1717 SmallVector<std::optional<ModuleInfo>> moduleInfos(modules.size());
1721 auto dedupGroupAttrName = StringAttr::get(context,
"firrtl.dedup_group");
1727 for (
auto module : modules) {
1728 llvm::SmallSetVector<StringAttr, 1> groups;
1730 module, [&groups, dedupGroupClass](
Annotation annotation) {
1733 groups.insert(annotation.
getMember<StringAttr>(
"group"));
1736 if (groups.size() > 1) {
1737 module.emitError("module belongs to multiple dedup groups: ") << groups;
1738 return signalPassFailure();
1740 assert(!module->hasAttr(dedupGroupAttrName) &&
1741 "unexpected existing use of temporary dedup group attribute");
1742 if (!groups.empty())
1743 module->setDiscardableAttr(dedupGroupAttrName, groups.front());
1747 LLVM_DEBUG(llvm::dbgs() <<
"Computing module information\n");
1748 auto result = mlir::failableParallelForEach(
1749 context, llvm::seq(modules.size()), [&](
unsigned idx) {
1750 auto module = modules[idx];
1752 if (AnnotationSet::hasAnnotation(module, noDedupClass))
1756 if (auto ext = dyn_cast<FExtModuleOp>(*module);
1757 ext && !ext.getDefname().has_value())
1760 StructuralHasher hasher(hasherConstants);
1762 moduleInfos[idx] = hasher.getModuleInfo(module);
1768 auto &os = llvm::dbgs();
1769 for (
auto [module, info] :
llvm::zip(modules, moduleInfos)) {
1772 os << llvm::format_bytes(
info->structuralHash, std::nullopt, 32, 32);
1774 os <<
"--------------------------------";
1775 os <<
"--------------------------------";
1777 os <<
" for " <<
module.getModuleNameAttr() << "\n";
1781 if (result.failed())
1782 return signalPassFailure();
1784 LLVM_DEBUG(llvm::dbgs() <<
"Update modules\n");
1785 for (
auto [i, module] :
llvm::enumerate(modules)) {
1786 auto moduleName =
module.getModuleNameAttr();
1787 auto &maybeModuleInfo = moduleInfos[i];
1789 if (!maybeModuleInfo) {
1794 dedupMap[moduleName] = moduleName;
1798 auto &moduleInfo = maybeModuleInfo.value();
1799 moduleToModuleInfo.try_emplace(module, &moduleInfo);
1802 for (
auto &referredModule : moduleInfo.referredModuleNames)
1803 referredModule = dedupMap[referredModule];
1806 auto it = moduleInfoToModule.find(&moduleInfo);
1807 if (it != moduleInfoToModule.end()) {
1808 auto original = cast<FModuleLike>(it->second);
1809 auto originalName = original.getModuleNameAttr();
1818 for (
auto &[originalName, dedupedName] : dedupMap)
1819 if (dedupedName == originalName)
1820 dedupedName = moduleName;
1823 it->second =
module;
1825 std::swap(originalName, moduleName);
1826 std::swap(original, module);
1830 LLVM_DEBUG(llvm::dbgs() <<
"- Replace " << moduleName <<
" with "
1831 << originalName <<
"\n");
1832 dedupMap[moduleName] = originalName;
1833 deduper.dedup(original, module);
1835 anythingChanged =
true;
1839 deduper.record(module);
1841 dedupMap[moduleName] = moduleName;
1843 moduleInfoToModule[&moduleInfo] =
module;
1851 auto failed =
false;
1853 auto parseModule = [&](Attribute path) -> StringAttr {
1856 auto [_, rhs] = cast<StringAttr>(path).getValue().split(
'|');
1857 return StringAttr::get(context, rhs);
1862 auto getLead = [&](StringAttr module) -> StringAttr {
1863 auto it = dedupMap.find(module);
1864 if (it == dedupMap.end()) {
1865 auto diag = emitError(circuit.getLoc(),
1866 "MustDeduplicateAnnotation references module ")
1867 <<
module << " which does not exist";
1874 LLVM_DEBUG(llvm::dbgs() <<
"Update annotations\n");
1878 auto modules = annotation.
getMember<ArrayAttr>(
"modules");
1880 emitError(circuit.getLoc(),
1881 "MustDeduplicateAnnotation missing \"modules\" member");
1886 if (modules.empty())
1889 auto firstModule = parseModule(modules[0]);
1890 auto firstLead = getLead(firstModule);
1894 for (
auto attr : modules.getValue().drop_front()) {
1895 auto nextModule = parseModule(attr);
1896 auto nextLead = getLead(nextModule);
1899 if (firstLead != nextLead) {
1900 auto diag = emitError(circuit.getLoc(),
"module ")
1901 << nextModule <<
" not deduplicated with " << firstModule;
1904 equiv.check(diag, a, b);
1912 return signalPassFailure();
1915 for (
auto module : circuit.getOps<FModuleLike>())
1916 module->removeDiscardableAttr(dedupGroupAttrName);
1922 markAnalysesPreserved<NLATable>();
1923 if (!anythingChanged)
1924 markAllAnalysesPreserved();
assert(baseType &&"element must be base type")
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)