26#include "mlir/IR/IRMapping.h"
27#include "mlir/IR/Threading.h"
28#include "mlir/Pass/Pass.h"
29#include "llvm/ADT/DenseMap.h"
30#include "llvm/ADT/DenseMapInfo.h"
31#include "llvm/ADT/Hashing.h"
32#include "llvm/ADT/PostOrderIterator.h"
33#include "llvm/ADT/SmallPtrSet.h"
34#include "llvm/Support/Debug.h"
35#include "llvm/Support/Format.h"
36#include "llvm/Support/SHA256.h"
38#define DEBUG_TYPE "firrtl-dedup"
42#define GEN_PASS_DEF_DEDUP
43#include "circt/Dialect/FIRRTL/Passes.h.inc"
48using namespace firrtl;
49using hw::InnerRefAttr;
58 if (!symbol.isPrivate())
63 if (isa<ClassLike>(*symbol))
68 if (!symbol.canDiscardOnUseEmpty())
136 : constants(constants) {}
154 for (
auto [index, innerSym] : llvm::enumerate(module.getPortSymbols())) {
155 for (
auto prop : cast<hw::InnerSymAttr>(innerSym))
159 size_t index =
module.getNumPorts();
160 module.walk([&](hw::InnerSymbolOpInterface innerSymOp) {
161 if (auto innerSym = innerSymOp.getInnerSymAttr()) {
162 for (auto prop : innerSym)
163 innerSymIDTable[prop.getName()] = std::pair(index, prop.getFieldID());
171 auto [it, inserted] = idTable.try_emplace(
object, nextID);
179 auto it = idTable.find(
object);
180 if (it == idTable.end())
182 auto id = it->second;
188 return innerSymIDTable.at(name);
192 auto value = operand.get();
193 if (
auto result = dyn_cast<OpResult>(value)) {
194 auto *op = result.getOwner();
196 update(result.getResultNumber());
199 if (
auto argument = dyn_cast<BlockArgument>(value)) {
200 auto *block = argument.getOwner();
201 update(getID(block));
202 update(argument.getArgNumber());
205 llvm_unreachable(
"Unknown value type");
209 auto *
addr =
reinterpret_cast<const uint8_t *
>(&pointer);
210 sha.update(ArrayRef<uint8_t>(
addr,
sizeof pointer));
214 auto *
addr =
reinterpret_cast<const uint8_t *
>(&value);
215 sha.update(ArrayRef<uint8_t>(
addr,
sizeof value));
218 template <
typename T,
typename U>
219 void update(
const std::pair<T, U> &pair) {
228 update(type.getTypeID());
229 for (
auto &element : type.getElements()) {
230 update(element.isFlip);
231 update(element.type);
237 update(type.getTypeID());
241 hasSeenSymbol =
true;
242 referredModuleNames.push_back(type.getNameAttr().getAttr());
243 for (
auto &element : type.getElements()) {
244 update(element.name.getAsOpaquePointer());
245 update(element.type);
246 update(
static_cast<unsigned>(element.direction));
252 if (
auto bundle = type_dyn_cast<BundleType>(type))
253 return update(bundle);
254 if (
auto klass = type_dyn_cast<ClassType>(type))
255 return update(klass);
256 update(type.getAsOpaquePointer());
263 if (
auto objectOp = dyn_cast<ObjectOp>(result.getOwner())) {
264 hasSeenSymbol =
true;
265 referredModuleNames.push_back(objectOp.getType().getNameAttr().getAttr());
269 update(result.getType());
274 void update(Operation *op, DictionaryAttr dict) {
275 for (
auto namedAttr : dict) {
276 auto name = namedAttr.getName();
277 auto value = namedAttr.getValue();
281 value.walk([&](FlatSymbolRefAttr) { hasSeenSymbol =
true; });
285 bool isClassPortNames =
286 isa<ClassLike>(op) && name == constants.portNamesAttr;
287 if (constants.nonessentialAttributes.contains(name) && !isClassPortNames)
291 update(name.getAsOpaquePointer());
294 if (name == constants.portTypesAttr) {
295 auto portTypes = cast<ArrayAttr>(value).getAsValueRange<TypeAttr>();
296 for (
auto type : portTypes)
306 if (isa<InstanceOp>(op) && name == constants.moduleNameAttr) {
307 referredModuleNames.push_back(cast<FlatSymbolRefAttr>(value).getAttr());
311 if (isa<InstanceChoiceOp>(op) && name == constants.moduleNamesAttr) {
312 for (
auto module : cast<ArrayAttr>(value))
313 referredModuleNames.push_back(
314 cast<FlatSymbolRefAttr>(module).getAttr());
320 if (isa<DistinctAttr>(value))
324 if (
auto innerRef = dyn_cast<hw::InnerRefAttr>(value)) {
325 update(getInnerSymID(innerRef.getName()));
331 update(value.getAsOpaquePointer());
337 update(name.getAsOpaquePointer());
342 for (
auto &op : llvm::reverse(*block))
344 for (
auto type : block->getArgumentTypes())
346 update(finalizeID(block));
353 for (
auto &block : llvm::reverse(region->getBlocks()))
363 update(op->getNumRegions());
364 for (
auto ®ion : reverse(op->getRegions()))
367 update(op->getName());
370 for (
auto &operand : op->getOpOperands())
375 hasSeenSymbol =
false;
376 update(op, op->getAttrDictionary());
379 for (
auto result : op->getResults())
385 symbolSensitiveOps.push_back(op);
388 update(finalizeID(op));
415 bool hasSeenSymbol =
false;
446 return llvm::hash_combine(
452 auto *
empty = getEmptyKey().info;
453 auto *tombstone = getTombstoneKey().info;
455 rhs.
info == tombstone)
493 SmallString<64> buffer;
494 llvm::raw_svector_ostream os(buffer);
495 if (
auto integerAttr = dyn_cast<IntegerAttr>(attr)) {
497 if (integerAttr.getType().isSignlessInteger())
498 integerAttr.getValue().toStringUnsigned(buffer, 16);
500 integerAttr.getAPSInt().toString(buffer, 16);
504 return std::string(buffer);
508 LogicalResult
check(InFlightDiagnostic &diag,
const Twine &message,
509 Operation *a, BundleType aType, Operation *b,
511 if (aType.getNumElements() != bType.getNumElements()) {
512 diag.attachNote(a->getLoc())
513 << message <<
" bundle type has different number of elements";
514 diag.attachNote(b->getLoc()) <<
"second operation here";
518 for (
auto elementPair :
519 llvm::zip(aType.getElements(), bType.getElements())) {
520 auto aElement = std::get<0>(elementPair);
521 auto bElement = std::get<1>(elementPair);
522 if (aElement.isFlip != bElement.isFlip) {
523 diag.attachNote(a->getLoc()) << message <<
" bundle element "
524 << aElement.name <<
" flip does not match";
525 diag.attachNote(b->getLoc()) <<
"second operation here";
529 if (failed(
check(diag,
530 message +
" -> bundle element \'" +
531 aElement.name.getValue() +
"'",
532 a, aElement.type, b, bElement.type)))
538 LogicalResult
check(InFlightDiagnostic &diag,
const Twine &message,
539 Operation *a, Type aType, Operation *b, Type bType) {
542 if (
auto aBundleType = type_dyn_cast<BundleType>(aType))
543 if (
auto bBundleType = type_dyn_cast<BundleType>(bType))
544 return check(diag, message, a, aBundleType, b, bBundleType);
545 if (type_isa<RefType>(aType) && type_isa<RefType>(bType) &&
547 diag.attachNote(a->getLoc())
548 << message <<
", has a RefType with a different base type "
549 << type_cast<RefType>(aType).getType()
550 <<
" in the same position of the two modules marked as 'must dedup'. "
551 "(This may be due to Grand Central Taps or Views being different "
552 "between the two modules.)";
553 diag.attachNote(b->getLoc())
554 <<
"the second module has a different base type "
555 << type_cast<RefType>(bType).getType();
558 diag.attachNote(a->getLoc())
559 << message <<
" types don't match, first type is " << aType;
560 diag.attachNote(b->getLoc()) <<
"second type is " << bType;
565 Block &aBlock, Operation *b, Block &bBlock) {
568 auto portNames = a->getAttrOfType<ArrayAttr>(
"portNames");
570 auto emitMissingPort = [&](Value existsVal, Operation *opExists,
571 Operation *opDoesNotExist) {
573 auto portNames = opExists->getAttrOfType<ArrayAttr>(
"portNames");
575 if (
auto portNameAttr = dyn_cast<StringAttr>(portNames[portNo]))
576 portName = portNameAttr.getValue();
577 if (type_isa<RefType>(existsVal.getType())) {
578 diag.attachNote(opExists->getLoc())
579 <<
" contains a RefType port named '" + portName +
580 "' that only exists in one of the modules (can be due to "
581 "difference in Grand Central Tap or View of two modules "
582 "marked with must dedup)";
583 diag.attachNote(opDoesNotExist->getLoc())
584 <<
"second module to be deduped that does not have the RefType "
587 diag.attachNote(opExists->getLoc())
588 <<
"port '" + portName +
"' only exists in one of the modules";
589 diag.attachNote(opDoesNotExist->getLoc())
590 <<
"second module to be deduped that does not have the port";
596 llvm::zip_longest(aBlock.getArguments(), bBlock.getArguments())) {
597 auto &aArg = std::get<0>(argPair);
598 auto &bArg = std::get<1>(argPair);
599 if (aArg.has_value() && bArg.has_value()) {
604 if (
auto portNameAttr = dyn_cast<StringAttr>(portNames[portNo]))
605 portName = portNameAttr.getValue();
608 if (failed(
check(diag,
"module port '" + portName +
"'", a,
609 aArg->getType(), b, bArg->getType())))
611 data.map.map(aArg.value(), bArg.value());
615 if (!aArg.has_value())
617 return emitMissingPort(aArg.has_value() ? aArg.value() : bArg.value(), a,
622 auto aIt = aBlock.begin();
623 auto aEnd = aBlock.end();
624 auto bIt = bBlock.begin();
625 auto bEnd = bBlock.end();
626 while (aIt != aEnd && bIt != bEnd)
627 if (failed(
check(diag,
data, &*aIt++, &*bIt++)))
630 diag.attachNote(aIt->getLoc()) <<
"first block has more operations";
631 diag.attachNote(b->getLoc()) <<
"second block here";
635 diag.attachNote(bIt->getLoc()) <<
"second block has more operations";
636 diag.attachNote(a->getLoc()) <<
"first block here";
643 Region &aRegion, Operation *b, Region &bRegion) {
644 auto aIt = aRegion.begin();
645 auto aEnd = aRegion.end();
646 auto bIt = bRegion.begin();
647 auto bEnd = bRegion.end();
650 while (aIt != aEnd && bIt != bEnd)
651 if (failed(
check(diag,
data, a, *aIt++, b, *bIt++)))
653 if (aIt != aEnd || bIt != bEnd) {
654 diag.attachNote(a->getLoc())
655 <<
"operation regions have different number of blocks";
656 diag.attachNote(b->getLoc()) <<
"second operation here";
662 LogicalResult
check(InFlightDiagnostic &diag, Operation *a,
663 mlir::DenseBoolArrayAttr aAttr, Operation *b,
664 mlir::DenseBoolArrayAttr bAttr) {
667 auto portNames = a->getAttrOfType<ArrayAttr>(
"portNames");
668 for (
unsigned i = 0, e = aAttr.size(); i < e; ++i) {
669 auto aDirection = aAttr[i];
670 auto bDirection = bAttr[i];
671 if (aDirection != bDirection) {
672 auto ¬e = diag.attachNote(a->getLoc()) <<
"module port ";
674 note <<
"'" << cast<StringAttr>(portNames[i]).getValue() <<
"'";
677 note <<
" directions don't match, first direction is '"
679 diag.attachNote(b->getLoc()) <<
"second direction is '"
688 DictionaryAttr aDict, Operation *b,
689 DictionaryAttr bDict) {
694 DenseSet<Attribute> seenAttrs;
695 for (
auto namedAttr : aDict) {
696 auto attrName = namedAttr.getName();
700 auto aAttr = namedAttr.getValue();
701 auto bAttr = bDict.get(attrName);
703 diag.attachNote(a->getLoc())
704 <<
"second operation is missing attribute " << attrName;
705 diag.attachNote(b->getLoc()) <<
"second operation here";
709 if (isa<hw::InnerRefAttr>(aAttr) && isa<hw::InnerRefAttr>(bAttr)) {
710 auto bRef = cast<hw::InnerRefAttr>(bAttr);
711 auto aRef = cast<hw::InnerRefAttr>(aAttr);
713 auto aTarget =
data.a.lookup(aRef.getName());
714 auto bTarget =
data.b.lookup(bRef.getName());
715 if (!aTarget || !bTarget)
716 diag.attachNote(a->getLoc())
717 <<
"malformed ir, possibly violating use-before-def";
719 diag.attachNote(a->getLoc())
720 <<
"operations have different targets, first operation has "
722 diag.attachNote(b->getLoc()) <<
"second operation has " << bTarget;
725 if (aTarget.isPort()) {
727 if (!bTarget.isPort() || aTarget.getPort() != bTarget.getPort())
731 if (!bTarget.isOpOnly() ||
732 data.map.lookupOrNull(aTarget.getOp()) != bTarget.getOp())
735 if (aTarget.getField() != bTarget.getField())
740 if (failed(
check(diag, a, cast<mlir::DenseBoolArrayAttr>(aAttr), b,
741 cast<mlir::DenseBoolArrayAttr>(bAttr))))
743 }
else if (isa<DistinctAttr>(aAttr) && isa<DistinctAttr>(bAttr)) {
746 }
else if (aAttr != bAttr) {
747 diag.attachNote(a->getLoc())
748 <<
"first operation has attribute '" << attrName.getValue()
750 diag.attachNote(b->getLoc())
751 <<
"second operation has value " <<
prettyPrint(bAttr);
754 seenAttrs.insert(attrName);
756 if (aDict.getValue().size() != bDict.getValue().size()) {
757 for (
auto namedAttr : bDict) {
758 auto attrName = namedAttr.getName();
762 seenAttrs.contains(attrName))
765 diag.attachNote(a->getLoc())
766 <<
"first operation is missing attribute " << attrName;
767 diag.attachNote(b->getLoc()) <<
"second operation here";
775 LogicalResult
check(InFlightDiagnostic &diag, FInstanceLike a,
779 auto aNames = a.getReferencedModuleNamesAttr();
780 auto bNames = b.getReferencedModuleNamesAttr();
781 if (aNames == bNames)
784 if (aNames.size() != bNames.size()) {
785 diag.attachNote(a->getLoc())
786 <<
"an instance has a different number of referenced "
787 "modules: first instance has "
788 << aNames.size() <<
" modules";
789 diag.attachNote(b->getLoc())
790 <<
"second instance has " << bNames.size() <<
" modules";
794 for (
auto [aName, bName] : llvm::zip(aNames.getAsRange<StringAttr>(),
795 bNames.getAsRange<StringAttr>())) {
804 diag.attachNote(std::nullopt)
805 <<
"in instance " << a.getInstanceNameAttr() <<
" of " << aName
806 <<
", and instance " << b.getInstanceNameAttr() <<
" of " << bName;
807 check(diag, aModule, bModule);
816 if (a->getName() != b->getName()) {
817 diag.attachNote(a->getLoc()) <<
"first operation is a " << a->getName();
818 diag.attachNote(b->getLoc()) <<
"second operation is a " << b->getName();
824 if (
auto aInst = dyn_cast<FInstanceLike>(a)) {
825 auto bInst = cast<FInstanceLike>(b);
826 if (failed(
check(diag, aInst, bInst)))
831 if (a->getNumResults() != b->getNumResults()) {
832 diag.attachNote(a->getLoc())
833 <<
"operations have different number of results";
834 diag.attachNote(b->getLoc()) <<
"second operation here";
837 for (
auto resultPair : llvm::zip(a->getResults(), b->getResults())) {
838 auto &aValue = std::get<0>(resultPair);
839 auto &bValue = std::get<1>(resultPair);
840 if (failed(
check(diag,
"operation result", a, aValue.getType(), b,
843 data.map.map(aValue, bValue);
847 if (a->getNumOperands() != b->getNumOperands()) {
848 diag.attachNote(a->getLoc())
849 <<
"operations have different number of operands";
850 diag.attachNote(b->getLoc()) <<
"second operation here";
853 for (
auto operandPair : llvm::zip(a->getOperands(), b->getOperands())) {
854 auto &aValue = std::get<0>(operandPair);
855 auto &bValue = std::get<1>(operandPair);
856 if (bValue !=
data.map.lookup(aValue)) {
857 diag.attachNote(a->getLoc())
858 <<
"operations use different operands, first operand is '"
863 diag.attachNote(b->getLoc())
864 <<
"second operand is '"
868 <<
"', but should have been '"
879 if (a->getNumRegions() != b->getNumRegions()) {
880 diag.attachNote(a->getLoc())
881 <<
"operations have different number of regions";
882 diag.attachNote(b->getLoc()) <<
"second operation here";
885 for (
auto regionPair : llvm::zip(a->getRegions(), b->getRegions())) {
886 auto &aRegion = std::get<0>(regionPair);
887 auto &bRegion = std::get<1>(regionPair);
888 if (failed(
check(diag,
data, a, aRegion, b, bRegion)))
893 if (failed(
check(diag,
data, a, a->getAttrDictionary(), b,
894 b->getAttrDictionary())))
900 void check(InFlightDiagnostic &diag, Operation *a, Operation *b) {
905 diag.attachNote(a->getLoc()) <<
"module marked NoDedup";
909 diag.attachNote(b->getLoc()) <<
"module marked NoDedup";
912 auto aSymbol = cast<mlir::SymbolOpInterface>(a);
913 auto bSymbol = cast<mlir::SymbolOpInterface>(b);
915 diag.attachNote(a->getLoc())
917 << (aSymbol.isPrivate() ?
"private but not discardable" :
"public");
918 diag.attachNote(b->getLoc())
920 << (bSymbol.isPrivate() ?
"private but not discardable" :
"public");
925 auto bGroup = dyn_cast_or_null<StringAttr>(
927 if (aGroup != bGroup) {
929 diag.attachNote(b->getLoc())
930 <<
"module is in dedup group '" << bGroup.str() <<
"'";
932 diag.attachNote(b->getLoc()) <<
"module is not part of a dedup group";
935 diag.attachNote(a->getLoc())
936 <<
"module is in dedup group '" << aGroup.str() <<
"'";
938 diag.attachNote(a->getLoc()) <<
"module is not part of a dedup group";
944 diag.attachNote(a->getLoc()) <<
"first module here";
945 diag.attachNote(b->getLoc()) <<
"second module here";
971 llvm::SmallSetVector<Location, 4> decomposedLocs;
973 unsigned seenFIR = 0;
974 for (
auto loc : {to, from}) {
977 if (
auto fusedLoc = dyn_cast<FusedLoc>(loc)) {
980 for (
auto loc : fusedLoc.getLocations()) {
981 if (FileLineColLoc fileLoc = dyn_cast<FileLineColLoc>(loc)) {
982 if (fileLoc.getFilename().strref().ends_with(
".fir")) {
988 decomposedLocs.insert(loc);
994 if (FileLineColLoc fileLoc = dyn_cast<FileLineColLoc>(loc)) {
995 if (fileLoc.getFilename().strref().ends_with(
".fir")) {
1002 if (!isa<UnknownLoc>(loc))
1003 decomposedLocs.insert(loc);
1006 auto locs = decomposedLocs.getArrayRef();
1011 return UnknownLoc::get(
context);
1012 if (locs.size() == 1)
1013 return locs.front();
1015 return FusedLoc::get(
context, locs);
1030 for (
auto nla : circuit.getOps<hw::HierPathOp>())
1031 nlaCache[nla.getNamepathAttr()] = nla.getSymNameAttr();
1038 void dedup(FModuleLike toModule, FModuleLike fromModule) {
1044 SmallVector<Attribute> newLocs;
1045 for (
auto [toLoc, fromLoc] : llvm::zip(toModule.getPortLocations(),
1046 fromModule.getPortLocations())) {
1047 if (toLoc == fromLoc)
1048 newLocs.push_back(toLoc);
1051 cast<LocationAttr>(fromLoc)));
1053 toModule->setAttr(
"portLocations", ArrayAttr::get(
context, newLocs));
1056 mergeOps(renameMap, toModule, toModule, fromModule, fromModule);
1062 if (
auto to = dyn_cast<FModuleOp>(*toModule))
1066 fromModule.getModuleNameAttr());
1077 for (
unsigned i = 0, e =
getNumPorts(module); i < e; ++i)
1080 module->walk([&](Operation *op) { recordAnnotations(op); });
1086 return moduleNamespaces.try_emplace(module, cast<FModuleLike>(module))
1094 if (
auto nlaRef = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal"))
1095 targetMap[nlaRef.getAttr()].insert(target);
1104 auto mem = dyn_cast<MemOp>(op);
1109 for (
unsigned i = 0, e = mem->getNumResults(); i < e; ++i)
1118 instanceGraph[::cast<igraph::ModuleOpInterface>(fromModule)];
1119 auto *toNode = instanceGraph[toModule];
1120 auto toModuleRef = FlatSymbolRefAttr::get(toModule.getModuleNameAttr());
1121 for (
auto *oldInstRec : llvm::make_early_inc_range(fromNode->uses())) {
1122 auto inst = oldInstRec->getInstance();
1123 if (
auto instOp = dyn_cast<InstanceOp>(*inst)) {
1124 instOp.setModuleNameAttr(toModuleRef);
1125 instOp.setPortNamesAttr(toModule.getPortNamesAttr());
1126 }
else if (
auto objectOp = dyn_cast<ObjectOp>(*inst)) {
1127 auto classLike = cast<ClassLike>(*toNode->getModule());
1128 ClassType classType = detail::getInstanceTypeForClassLike(classLike);
1129 objectOp.getResult().setType(classType);
1130 }
else if (
auto instanceChoiceOp = dyn_cast<InstanceChoiceOp>(*inst)) {
1131 auto fromModuleName = fromNode->getModule().getModuleNameAttr();
1132 SmallVector<Attribute> newModules;
1133 for (
auto module : instanceChoiceOp.getReferencedModuleNamesAttr()) {
1134 auto moduleName = cast<StringAttr>(module);
1135 if (moduleName == fromModuleName)
1136 newModules.push_back(toModuleRef);
1138 newModules.push_back(FlatSymbolRefAttr::get(moduleName));
1140 instanceChoiceOp.setModuleNamesAttr(
1141 ArrayAttr::get(
context, newModules));
1142 instanceChoiceOp.setPortNamesAttr(toModule.getPortNamesAttr());
1144 oldInstRec->getParent()->addInstance(inst, toNode);
1145 oldInstRec->erase();
1147 instanceGraph.erase(fromNode);
1148 fromModule->erase();
1156 SmallVector<FlatSymbolRefAttr>
1157 createNLAs(Operation *fromModule, ArrayRef<Attribute> baseNamepath,
1158 SymbolTable::Visibility vis = SymbolTable::Visibility::Private) {
1161 SmallVector<Attribute> namepath = {
nullptr};
1162 namepath.append(baseNamepath.begin(), baseNamepath.end());
1164 auto loc = fromModule->getLoc();
1165 auto *fromNode = instanceGraph[cast<igraph::ModuleOpInterface>(fromModule)];
1166 SmallVector<FlatSymbolRefAttr> nlas;
1167 for (
auto *instanceRecord : fromNode->uses()) {
1168 auto parent = cast<FModuleOp>(*instanceRecord->getParent()->getModule());
1169 auto inst = instanceRecord->getInstance();
1171 auto arrayAttr = ArrayAttr::get(
context, namepath);
1173 auto &cacheEntry = nlaCache[arrayAttr];
1175 auto builder = OpBuilder::atBlockBegin(nlaBlock);
1176 auto nla = hw::HierPathOp::create(builder, loc,
"nla", arrayAttr);
1178 symbolTable.insert(nla);
1180 cacheEntry = nla.getNameAttr();
1181 nla.setVisibility(vis);
1182 nlaTable->addNLA(nla);
1184 auto nlaRef = FlatSymbolRefAttr::get(cast<StringAttr>(cacheEntry));
1185 nlas.push_back(nlaRef);
1193 SmallVector<FlatSymbolRefAttr>
1195 SymbolTable::Visibility vis = SymbolTable::Visibility::Private) {
1196 return createNLAs(fromModule, FlatSymbolRefAttr::get(toModuleName), vis);
1203 Annotation anno, ArrayRef<NamedAttribute> attributes,
1204 unsigned nonLocalIndex,
1205 SmallVectorImpl<Annotation> &newAnnotations) {
1206 SmallVector<NamedAttribute> mutableAttributes(attributes.begin(),
1208 for (
auto &nla : nlas) {
1210 mutableAttributes[nonLocalIndex].setValue(nla);
1211 auto dict = DictionaryAttr::getWithSorted(
context, mutableAttributes);
1214 newAnnotations.push_back(anno);
1223 targetMap.erase(nla.getNameAttr());
1224 nlaTable->erase(nla);
1225 nlaCache.erase(nla.getNamepathAttr());
1226 symbolTable.erase(nla);
1232 FModuleOp fromModule) {
1233 auto toName = toModule.getNameAttr();
1234 auto fromName = fromModule.getNameAttr();
1237 auto moduleNLAs = nlaTable->lookup(fromModule.getNameAttr()).vec();
1239 nlaTable->renameModuleAndInnerRef(toName, fromName, renameMap);
1242 for (
auto nla : moduleNLAs) {
1243 auto elements = nla.getNamepath().getValue();
1245 if (nla.root() != toName)
1248 SmallVector<Attribute> namepath(elements.begin(), elements.end());
1249 auto nlaRefs = createNLAs(fromModule, namepath, nla.getVisibility());
1251 auto &set = targetMap[nla.getSymNameAttr()];
1252 SmallVector<AnnoTarget> targets(set.begin(), set.end());
1254 for (
auto target : targets) {
1257 SmallVector<Annotation> newAnnotations;
1258 for (
auto anno : target.getAnnotations()) {
1260 auto [it, found] = mlir::impl::findAttrSorted(
1261 anno.begin(), anno.end(), nonLocalString);
1264 if (!found || cast<FlatSymbolRefAttr>(it->getValue()).getAttr() !=
1265 nla.getSymNameAttr()) {
1266 newAnnotations.push_back(anno);
1269 auto nonLocalIndex = std::distance(anno.begin(), it);
1271 cloneAnnotation(nlaRefs, anno,
1272 ArrayRef<NamedAttribute>(anno.begin(), anno.end()),
1273 nonLocalIndex, newAnnotations);
1278 target.setAnnotations(annotations);
1280 for (
auto nla : nlaRefs)
1281 targetMap[nla.getAttr()].insert(target);
1293 FModuleOp fromModule) {
1294 addAnnotationContext(renameMap, toModule, toModule);
1295 addAnnotationContext(renameMap, toModule, fromModule);
1301 StringAttr fromName) {
1302 nlaTable->renameModuleAndInnerRef(toName, fromName, renameMap);
1310 SmallVectorImpl<Annotation> &newAnnotations) {
1313 SmallVector<NamedAttribute> attributes;
1314 int nonLocalIndex = -1;
1315 for (
const auto &val : llvm::enumerate(anno)) {
1316 auto attr = val.value();
1318 auto compare = attr.getName().compare(nonLocalString);
1319 assert(compare != 0 &&
"should not pass non-local annotations here");
1323 nonLocalIndex = val.index();
1324 attributes.push_back(NamedAttribute(nonLocalString, nonLocalString));
1329 attributes.push_back(attr);
1331 if (nonLocalIndex == -1) {
1333 nonLocalIndex = attributes.size();
1334 attributes.push_back(NamedAttribute(nonLocalString, nonLocalString));
1337 attributes.append(anno.
begin() + nonLocalIndex, anno.
end());
1341 auto nlaRefs = createNLAs(toModuleName, fromModule);
1342 for (
auto nla : nlaRefs)
1343 targetMap[nla.getAttr()].insert(to);
1346 cloneAnnotation(nlaRefs, anno, attributes, nonLocalIndex, newAnnotations);
1352 SmallVectorImpl<Annotation> &newAnnotations,
1353 SmallPtrSetImpl<Attribute> &dontTouches) {
1354 for (
auto anno : annos) {
1355 if (anno.isClass(dontTouchAnnoClass)) {
1358 anno.removeMember(
"circt.nonlocal");
1359 auto [it, inserted] = dontTouches.insert(anno.getAttr());
1361 newAnnotations.push_back(anno);
1366 if (
auto nla = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
1367 newAnnotations.push_back(anno);
1368 targetMap[nla.getAttr()].insert(to);
1372 makeAnnotationNonLocal(toModule.getModuleNameAttr(), to, fromModule, anno,
1383 SmallVector<Annotation> newAnnotations;
1387 llvm::SmallPtrSet<Attribute, 4> dontTouches;
1391 copyAnnotations(toModule, to, toModule, toAnnos, newAnnotations,
1393 copyAnnotations(toModule, to, fromModule, fromAnnos, newAnnotations,
1397 if (!newAnnotations.empty())
1403 FModuleLike fromModule, Operation *from) {
1409 if (toModule == to) {
1411 for (
unsigned i = 0, e =
getNumPorts(toModule); i < e; ++i)
1416 }
else if (
auto toMem = dyn_cast<MemOp>(to)) {
1418 auto fromMem = cast<MemOp>(from);
1419 for (
unsigned i = 0, e = toMem.getNumResults(); i < e; ++i)
1428 hw::InnerSymAttr toSym,
1429 hw::InnerSymAttr fromSym) {
1430 if (fromSym && !fromSym.getProps().empty()) {
1431 auto &isn = getNamespace(toModule);
1433 SmallVector<hw::InnerSymPropertiesAttr> newProps;
1436 llvm::append_range(newProps, toSym);
1438 for (
auto fromProp : fromSym) {
1439 hw::InnerSymPropertiesAttr newProp;
1440 auto *it = llvm::find_if(newProps, [&](
auto p) {
1441 return p.getFieldID() == fromProp.getFieldID();
1443 if (it != newProps.end()) {
1448 if (fromProp.getSymVisibility().getValue() ==
"public" &&
1449 newProp.getSymVisibility().getValue() !=
"public") {
1450 *it = hw::InnerSymPropertiesAttr::get(
context, newProp.getName(),
1451 newProp.getFieldID(),
1452 fromProp.getSymVisibility());
1456 auto newName = isn.newName(fromProp.getName().getValue());
1457 newProp = hw::InnerSymPropertiesAttr::get(
1458 context, StringAttr::get(
context, newName), fromProp.getFieldID(),
1459 fromProp.getSymVisibility());
1460 newProps.push_back(newProp);
1462 renameMap[fromProp.getName()] = newProp.getName();
1465 llvm::sort(newProps, [](
auto &p,
auto &q) {
1466 return p.getFieldID() < q.getFieldID();
1469 return hw::InnerSymAttr::get(
context, newProps);
1471 return hw::InnerSymAttr();
1478 Operation *to, FModuleLike fromModule,
1482 if (
auto fromInnerSym = dyn_cast<hw::InnerSymbolOpInterface>(from)) {
1483 auto toInnerSym = cast<hw::InnerSymbolOpInterface>(to);
1484 if (
auto newSymAttr = mergeInnerSymbols(renameMap, toModule,
1485 toInnerSym.getInnerSymAttr(),
1486 fromInnerSym.getInnerSymAttr()))
1487 toInnerSym.setInnerSymbolAttr(newSymAttr);
1491 auto fromPortSyms = from->getAttrOfType<ArrayAttr>(
"portSymbols");
1492 if (!fromPortSyms || fromPortSyms.empty())
1495 auto portCount = fromPortSyms.size();
1496 auto toPortSyms = to->getAttrOfType<ArrayAttr>(
"portSymbols");
1500 SmallVector<Attribute> newPortSyms;
1501 if (toPortSyms.empty())
1502 newPortSyms.assign(portCount, hw::InnerSymAttr());
1504 newPortSyms.assign(toPortSyms.begin(), toPortSyms.end());
1506 for (
unsigned portNo = 0; portNo < portCount; ++portNo) {
1507 if (
auto newPortSym = mergeInnerSymbols(
1508 renameMap, toModule,
1509 llvm::cast_if_present<hw::InnerSymAttr>(newPortSyms[portNo]),
1510 cast<hw::InnerSymAttr>(fromPortSyms[portNo]))) {
1511 newPortSyms[portNo] = newPortSym;
1516 FModuleLike::fixupPortSymsArray(newPortSyms, toModule.getContext());
1517 cast<FModuleLike>(to).setPortSymbols(newPortSyms);
1523 FModuleLike fromModule, Operation *from) {
1525 if (to->getLoc() != from->getLoc())
1529 for (
auto regions : llvm::zip(to->getRegions(), from->getRegions()))
1530 mergeRegions(renameMap, toModule, std::get<0>(regions), fromModule,
1531 std::get<1>(regions));
1534 recordSymRenames(renameMap, toModule, to, fromModule, from);
1537 mergeAnnotations(toModule, to, fromModule, from);
1542 FModuleLike fromModule, Block &fromBlock) {
1544 for (
auto [toArg, fromArg] :
1545 llvm::zip(toBlock.getArguments(), fromBlock.getArguments()))
1546 if (toArg.getLoc() != fromArg.getLoc())
1549 for (
auto ops : llvm::zip(toBlock, fromBlock))
1550 mergeOps(renameMap, toModule, &std::get<0>(ops), fromModule,
1556 Region &toRegion, FModuleLike fromModule,
1557 Region &fromRegion) {
1558 for (
auto blocks : llvm::zip(toRegion, fromRegion))
1559 mergeBlocks(renameMap, toModule, std::get<0>(blocks), fromModule,
1560 std::get<1>(blocks));
1574 DenseMap<Attribute, llvm::SmallDenseSet<AnnoTarget>>
targetMap;
1595static void fixupConnect(ImplicitLocOpBuilder &builder, Value dst, Value src) {
1597 auto dstType = dst.getType();
1598 auto srcType = src.getType();
1599 if (dstType == srcType) {
1605 auto dstBundle = type_cast<BundleType>(dstType);
1606 auto srcBundle = type_cast<BundleType>(srcType);
1607 for (
unsigned i = 0; i < dstBundle.getNumElements(); ++i) {
1608 auto dstField = SubfieldOp::create(builder, dst, i);
1609 auto srcField = SubfieldOp::create(builder, src, i);
1610 if (dstBundle.getElement(i).isFlip) {
1611 std::swap(srcBundle, dstBundle);
1612 std::swap(srcField, dstField);
1622 const DenseMap<Attribute, StringAttr> &dedupMap) {
1628 if (
auto instOp = dyn_cast<InstanceOp>(op)) {
1629 ImplicitLocOpBuilder builder(instOp.getLoc(), instOp->getContext());
1630 builder.setInsertionPointAfter(instOp);
1631 auto module = instanceGraph.lookup(instOp.getModuleNameAttr().getAttr())
1632 ->getModule<FModuleLike>();
1633 for (
auto [index, result] : llvm::enumerate(instOp.getResults())) {
1634 auto newType =
module.getPortType(index);
1635 auto oldType = result.getType();
1637 if (newType == oldType)
1639 LLVM_DEBUG(llvm::dbgs()
1640 <<
"- Updating instance port \"" << instOp.getInstanceName()
1641 <<
"." << instOp.getPortName(index) <<
"\" from " << oldType
1642 <<
" to " << newType <<
"\n");
1646 auto wire = WireOp::create(builder, oldType, instOp.getPortName(index))
1648 result.replaceAllUsesWith(wire);
1649 result.setType(newType);
1650 if (instOp.getPortDirection(index) == Direction::Out)
1659 mlir::AttrTypeReplacer replacer;
1660 replacer.addReplacement([&](FlatSymbolRefAttr symRef) {
1661 auto oldName = symRef.getAttr();
1662 auto newName = dedupMap.lookup(oldName);
1663 if (newName && newName != oldName) {
1664 auto newSymRef = FlatSymbolRefAttr::get(newName);
1665 LLVM_DEBUG(llvm::dbgs()
1666 <<
"- Updating " << symRef <<
" to " << newSymRef <<
" in "
1667 << op->getName() <<
" at " << op->getLoc() <<
"\n");
1674 op->setAttrs(cast<DictionaryAttr>(replacer.replace(op->getAttrDictionary())));
1677 for (
auto ®ion : op->getRegions())
1678 for (
auto &block : region)
1679 for (
auto arg : block.getArguments())
1680 arg.setType(replacer.replace(arg.getType()));
1683 for (
auto result : op->getResults())
1684 result.setType(replacer.replace(result.getType()));
1691 const DenseMap<Operation *, ModuleInfoRef> &moduleToModuleInfo,
1692 const DenseMap<Attribute, StringAttr> &dedupMap) {
1693 for (
auto *node : instanceGraph) {
1696 auto module = node->getModule<FModuleLike>();
1697 auto it = moduleToModuleInfo.find(module);
1698 if (it == moduleToModuleInfo.end())
1702 auto &ops = it->second.info->symbolSensitiveOps;
1705 LLVM_DEBUG(llvm::dbgs()
1706 <<
"- Updating " << ops.size() <<
" symbol-sensitive ops in "
1707 << module.getNameAttr() <<
"\n");
1708 for (
auto *op : ops)
1718class DedupPass :
public circt::firrtl::impl::DedupBase<DedupPass> {
1719 using DedupBase::DedupBase;
1721 void runOnOperation()
override {
1722 auto *
context = &getContext();
1723 auto circuit = getOperation();
1724 auto &instanceGraph = getAnalysis<InstanceGraph>();
1725 auto *nlaTable = &getAnalysis<NLATable>();
1726 auto &symbolTable = getAnalysis<SymbolTable>();
1727 Deduper deduper(instanceGraph, symbolTable, nlaTable, circuit);
1729 auto anythingChanged =
false;
1731 llvm::dbgs() <<
"\n";
1732 debugHeader(Twine(
"Dedup circuit \"") + circuit.getName() +
"\"")
1737 auto noDedupClass = StringAttr::get(
context, noDedupAnnoClass);
1740 auto dedupGroupClass = StringAttr::get(
context, dedupGroupAnnoClass);
1743 DenseMap<ModuleInfoRef, Operation *> moduleInfoToModule;
1744 DenseMap<Operation *, ModuleInfoRef> moduleToModuleInfo;
1749 DenseMap<Attribute, StringAttr> dedupMap;
1754 SmallVector<FModuleLike, 0> modules;
1756 if (
auto mod = dyn_cast<FModuleLike>(*node.getModule()))
1757 modules.push_back(mod);
1759 LLVM_DEBUG(llvm::dbgs() <<
"Found " << modules.size() <<
" modules\n");
1761 SmallVector<std::optional<ModuleInfo>> moduleInfos(modules.size());
1765 auto dedupGroupAttrName = StringAttr::get(
context,
"firrtl.dedup_group");
1771 for (
auto module : modules) {
1772 llvm::SmallSetVector<StringAttr, 1> groups;
1774 module, [&groups, dedupGroupClass](
Annotation annotation) {
1777 groups.insert(annotation.
getMember<StringAttr>(
"group"));
1780 if (groups.size() > 1) {
1781 module.emitError("module belongs to multiple dedup groups: ") << groups;
1782 return signalPassFailure();
1784 assert(!module->hasAttr(dedupGroupAttrName) &&
1785 "unexpected existing use of temporary dedup group attribute");
1786 if (!groups.empty())
1787 module->setDiscardableAttr(dedupGroupAttrName, groups.front());
1791 LLVM_DEBUG(llvm::dbgs() <<
"Computing module information\n");
1792 auto result = mlir::failableParallelForEach(
1793 context, llvm::seq(modules.size()), [&](
unsigned idx) {
1794 auto module = modules[idx];
1796 if (AnnotationSet::hasAnnotation(module, noDedupClass))
1800 if (auto ext = dyn_cast<FExtModuleOp>(*module);
1801 ext && !ext.getDefname().has_value())
1805 if (isa<ClassOp>(*module) && !dedupClasses)
1808 StructuralHasher hasher(hasherConstants);
1810 moduleInfos[idx] = hasher.getModuleInfo(module);
1816 auto &os = llvm::dbgs();
1817 for (
auto [module, info] :
llvm::zip(modules, moduleInfos)) {
1820 os << llvm::format_bytes(
info->structuralHash, std::nullopt, 32, 32);
1822 os <<
"--------------------------------";
1823 os <<
"--------------------------------";
1825 os <<
" for " <<
module.getModuleNameAttr() << "\n";
1829 if (result.failed())
1830 return signalPassFailure();
1832 LLVM_DEBUG(llvm::dbgs() <<
"Update modules\n");
1833 for (
auto [i, module] :
llvm::enumerate(modules)) {
1834 auto moduleName =
module.getModuleNameAttr();
1835 auto &maybeModuleInfo = moduleInfos[i];
1837 if (!maybeModuleInfo) {
1842 dedupMap[moduleName] = moduleName;
1846 auto &moduleInfo = maybeModuleInfo.value();
1847 moduleToModuleInfo.try_emplace(module, &moduleInfo);
1850 for (
auto &referredModule : moduleInfo.referredModuleNames)
1851 referredModule = dedupMap[referredModule];
1854 auto it = moduleInfoToModule.find(&moduleInfo);
1855 if (it != moduleInfoToModule.end()) {
1856 auto original = cast<FModuleLike>(it->second);
1857 auto originalName = original.getModuleNameAttr();
1863 dedupMap[moduleName] = moduleName;
1868 for (
auto &[_, dedupedName] : dedupMap)
1869 if (dedupedName == originalName)
1870 dedupedName = moduleName;
1873 it->second =
module;
1875 std::swap(originalName, moduleName);
1876 std::swap(original, module);
1880 LLVM_DEBUG(llvm::dbgs() <<
"- Replace " << moduleName <<
" with "
1881 << originalName <<
"\n");
1882 dedupMap[moduleName] = originalName;
1883 deduper.dedup(original, module);
1885 anythingChanged =
true;
1889 deduper.record(module);
1891 dedupMap[moduleName] = moduleName;
1893 moduleInfoToModule[&moduleInfo] =
module;
1901 auto failed =
false;
1903 auto parseModule = [&](Attribute path) -> StringAttr {
1906 auto [_, rhs] = cast<StringAttr>(path).getValue().split(
'|');
1907 return StringAttr::get(
context, rhs);
1912 auto getLead = [&](StringAttr module) -> StringAttr {
1913 auto it = dedupMap.find(module);
1914 if (it == dedupMap.end()) {
1915 auto diag = emitError(circuit.getLoc(),
1916 "MustDeduplicateAnnotation references module ")
1917 <<
module << " which does not exist";
1924 LLVM_DEBUG(llvm::dbgs() <<
"Update annotations\n");
1926 if (!annotation.
isClass(mustDeduplicateAnnoClass))
1928 auto modules = annotation.
getMember<ArrayAttr>(
"modules");
1930 emitError(circuit.getLoc(),
1931 "MustDeduplicateAnnotation missing \"modules\" member");
1936 if (modules.empty())
1939 auto firstModule = parseModule(modules[0]);
1940 auto firstLead = getLead(firstModule);
1944 for (
auto attr : modules.getValue().drop_front()) {
1945 auto nextModule = parseModule(attr);
1946 auto nextLead = getLead(nextModule);
1949 if (firstLead != nextLead) {
1950 auto diag = emitError(circuit.getLoc(),
"module ")
1951 << nextModule <<
" not deduplicated with " << firstModule;
1954 equiv.check(diag, a, b);
1962 return signalPassFailure();
1965 for (
auto module : circuit.getOps<FModuleLike>())
1966 module->removeDiscardableAttr(dedupGroupAttrName);
1972 markAnalysesPreserved<NLATable>();
1973 if (!anythingChanged)
1974 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.
size_t getNumPorts(Operation *op)
Return the number of ports in a module-like thing (modules, memories, etc)
std::pair< std::string, bool > getFieldName(const FieldRef &fieldRef, bool nameSafe=false)
Get a string identifier representing the FieldRef.
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 moduleNamesAttr
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)