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;
438 return llvm::hash_combine(
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, igraph::InstanceOpInterface a,
765 igraph::InstanceOpInterface b) {
768 auto aNames = a.getReferencedModuleNamesAttr();
769 auto bNames = b.getReferencedModuleNamesAttr();
770 if (aNames == bNames)
773 if (aNames.size() != bNames.size()) {
774 diag.attachNote(a->getLoc())
775 <<
"an instance has a different number of referenced "
776 "modules: first instance has "
777 << aNames.size() <<
" modules";
778 diag.attachNote(b->getLoc())
779 <<
"second instance has " << bNames.size() <<
" modules";
783 for (
auto [aName, bName] : llvm::zip(aNames.getAsRange<StringAttr>(),
784 bNames.getAsRange<StringAttr>())) {
793 diag.attachNote(std::nullopt)
794 <<
"in instance " << a.getInstanceNameAttr() <<
" of " << aName
795 <<
", and instance " << b.getInstanceNameAttr() <<
" of " << bName;
796 check(diag, aModule, bModule);
805 if (a->getName() != b->getName()) {
806 diag.attachNote(a->getLoc()) <<
"first operation is a " << a->getName();
807 diag.attachNote(b->getLoc()) <<
"second operation is a " << b->getName();
814 if (
auto aInst = dyn_cast<igraph::InstanceOpInterface>(a))
815 if (
auto bInst = dyn_cast<igraph::InstanceOpInterface>(b))
816 if (isa_and_nonnull<firrtl::FIRRTLDialect>(a->getDialect()) &&
817 isa_and_nonnull<firrtl::FIRRTLDialect>(b->getDialect()) &&
818 failed(
check(diag, aInst, bInst)))
822 if (a->getNumResults() != b->getNumResults()) {
823 diag.attachNote(a->getLoc())
824 <<
"operations have different number of results";
825 diag.attachNote(b->getLoc()) <<
"second operation here";
828 for (
auto resultPair : llvm::zip(a->getResults(), b->getResults())) {
829 auto &aValue = std::get<0>(resultPair);
830 auto &bValue = std::get<1>(resultPair);
831 if (failed(
check(diag,
"operation result", a, aValue.getType(), b,
834 data.map.map(aValue, bValue);
838 if (a->getNumOperands() != b->getNumOperands()) {
839 diag.attachNote(a->getLoc())
840 <<
"operations have different number of operands";
841 diag.attachNote(b->getLoc()) <<
"second operation here";
844 for (
auto operandPair : llvm::zip(a->getOperands(), b->getOperands())) {
845 auto &aValue = std::get<0>(operandPair);
846 auto &bValue = std::get<1>(operandPair);
847 if (bValue !=
data.map.lookup(aValue)) {
848 diag.attachNote(a->getLoc())
849 <<
"operations use different operands, first operand is '"
854 diag.attachNote(b->getLoc())
855 <<
"second operand is '"
859 <<
"', but should have been '"
870 if (a->getNumRegions() != b->getNumRegions()) {
871 diag.attachNote(a->getLoc())
872 <<
"operations have different number of regions";
873 diag.attachNote(b->getLoc()) <<
"second operation here";
876 for (
auto regionPair : llvm::zip(a->getRegions(), b->getRegions())) {
877 auto &aRegion = std::get<0>(regionPair);
878 auto &bRegion = std::get<1>(regionPair);
879 if (failed(
check(diag,
data, a, aRegion, b, bRegion)))
884 if (failed(
check(diag,
data, a, a->getAttrDictionary(), b,
885 b->getAttrDictionary())))
891 void check(InFlightDiagnostic &diag, Operation *a, Operation *b) {
896 diag.attachNote(a->getLoc()) <<
"module marked NoDedup";
900 diag.attachNote(b->getLoc()) <<
"module marked NoDedup";
903 auto aSymbol = cast<mlir::SymbolOpInterface>(a);
904 auto bSymbol = cast<mlir::SymbolOpInterface>(b);
906 diag.attachNote(a->getLoc())
908 << (aSymbol.isPrivate() ?
"private but not discardable" :
"public");
909 diag.attachNote(b->getLoc())
911 << (bSymbol.isPrivate() ?
"private but not discardable" :
"public");
916 auto bGroup = dyn_cast_or_null<StringAttr>(
918 if (aGroup != bGroup) {
920 diag.attachNote(b->getLoc())
921 <<
"module is in dedup group '" << bGroup.str() <<
"'";
923 diag.attachNote(b->getLoc()) <<
"module is not part of a dedup group";
926 diag.attachNote(a->getLoc())
927 <<
"module is in dedup group '" << aGroup.str() <<
"'";
929 diag.attachNote(a->getLoc()) <<
"module is not part of a dedup group";
935 diag.attachNote(a->getLoc()) <<
"first module here";
936 diag.attachNote(b->getLoc()) <<
"second module here";
964 unsigned seenFIR = 0;
965 for (
auto loc : {to, from}) {
968 if (
auto fusedLoc = dyn_cast<FusedLoc>(loc)) {
971 for (
auto loc : fusedLoc.getLocations()) {
972 if (FileLineColLoc fileLoc = dyn_cast<FileLineColLoc>(loc)) {
973 if (fileLoc.getFilename().strref().ends_with(
".fir")) {
979 decomposedLocs.insert(loc);
985 if (FileLineColLoc fileLoc = dyn_cast<FileLineColLoc>(loc)) {
986 if (fileLoc.getFilename().strref().ends_with(
".fir")) {
993 if (!isa<UnknownLoc>(loc))
994 decomposedLocs.insert(loc);
997 auto locs = decomposedLocs.getArrayRef();
1002 return UnknownLoc::get(
context);
1003 if (locs.size() == 1)
1004 return locs.front();
1006 return FusedLoc::get(
context, locs);
1021 for (
auto nla : circuit.getOps<hw::HierPathOp>())
1022 nlaCache[nla.getNamepathAttr()] = nla.getNameAttr();
1029 void dedup(FModuleLike toModule, FModuleLike fromModule) {
1035 SmallVector<Attribute> newLocs;
1036 for (
auto [toLoc, fromLoc] : llvm::zip(toModule.getPortLocations(),
1037 fromModule.getPortLocations())) {
1038 if (toLoc == fromLoc)
1039 newLocs.push_back(toLoc);
1042 cast<LocationAttr>(fromLoc)));
1044 toModule->setAttr(
"portLocations", ArrayAttr::get(
context, newLocs));
1047 mergeOps(renameMap, toModule, toModule, fromModule, fromModule);
1053 if (
auto to = dyn_cast<FModuleOp>(*toModule))
1057 fromModule.getModuleNameAttr());
1068 for (
unsigned i = 0, e =
getNumPorts(module); i < e; ++i)
1071 module->walk([&](Operation *op) { recordAnnotations(op); });
1077 return moduleNamespaces.try_emplace(module, cast<FModuleLike>(module))
1085 if (
auto nlaRef = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal"))
1086 targetMap[nlaRef.getAttr()].insert(target);
1095 auto mem = dyn_cast<MemOp>(op);
1100 for (
unsigned i = 0, e = mem->getNumResults(); i < e; ++i)
1109 instanceGraph[::cast<igraph::ModuleOpInterface>(fromModule)];
1110 auto *toNode = instanceGraph[toModule];
1111 auto toModuleRef = FlatSymbolRefAttr::get(toModule.getModuleNameAttr());
1112 for (
auto *oldInstRec : llvm::make_early_inc_range(fromNode->uses())) {
1113 auto inst = oldInstRec->getInstance();
1114 if (
auto instOp = dyn_cast<InstanceOp>(*inst)) {
1115 instOp.setModuleNameAttr(toModuleRef);
1116 instOp.setPortNamesAttr(toModule.getPortNamesAttr());
1117 }
else if (
auto objectOp = dyn_cast<ObjectOp>(*inst)) {
1118 auto classLike = cast<ClassLike>(*toNode->getModule());
1119 ClassType classType = detail::getInstanceTypeForClassLike(classLike);
1120 objectOp.getResult().setType(classType);
1121 }
else if (
auto instanceChoiceOp = dyn_cast<InstanceChoiceOp>(*inst)) {
1122 auto fromModuleName = fromNode->getModule().getModuleNameAttr();
1123 SmallVector<Attribute> newModules;
1124 for (
auto module : instanceChoiceOp.getReferencedModuleNamesAttr()) {
1125 auto moduleName = cast<StringAttr>(module);
1126 if (moduleName == fromModuleName)
1127 newModules.push_back(toModuleRef);
1129 newModules.push_back(FlatSymbolRefAttr::get(moduleName));
1131 instanceChoiceOp.setModuleNamesAttr(
1132 ArrayAttr::get(
context, newModules));
1133 instanceChoiceOp.setPortNamesAttr(toModule.getPortNamesAttr());
1135 oldInstRec->getParent()->addInstance(inst, toNode);
1136 oldInstRec->erase();
1138 instanceGraph.erase(fromNode);
1139 fromModule->erase();
1147 SmallVector<FlatSymbolRefAttr>
1148 createNLAs(Operation *fromModule, ArrayRef<Attribute> baseNamepath,
1149 SymbolTable::Visibility vis = SymbolTable::Visibility::Private) {
1152 SmallVector<Attribute> namepath = {
nullptr};
1153 namepath.append(baseNamepath.begin(), baseNamepath.end());
1155 auto loc = fromModule->getLoc();
1156 auto *fromNode = instanceGraph[cast<igraph::ModuleOpInterface>(fromModule)];
1157 SmallVector<FlatSymbolRefAttr> nlas;
1158 for (
auto *instanceRecord : fromNode->uses()) {
1159 auto parent = cast<FModuleOp>(*instanceRecord->getParent()->getModule());
1160 auto inst = instanceRecord->getInstance();
1162 auto arrayAttr = ArrayAttr::get(
context, namepath);
1164 auto &cacheEntry = nlaCache[arrayAttr];
1166 auto builder = OpBuilder::atBlockBegin(nlaBlock);
1167 auto nla = hw::HierPathOp::create(builder, loc,
"nla",
1170 symbolTable.insert(nla);
1172 cacheEntry = nla.getNameAttr();
1173 nla.setVisibility(vis);
1174 nlaTable->addNLA(nla);
1176 auto nlaRef = FlatSymbolRefAttr::get(cast<StringAttr>(cacheEntry));
1177 nlas.push_back(nlaRef);
1185 SmallVector<FlatSymbolRefAttr>
1187 SymbolTable::Visibility vis = SymbolTable::Visibility::Private) {
1188 return createNLAs(fromModule, FlatSymbolRefAttr::get(toModuleName), vis);
1195 Annotation anno, ArrayRef<NamedAttribute> attributes,
1196 unsigned nonLocalIndex,
1197 SmallVectorImpl<Annotation> &newAnnotations) {
1198 SmallVector<NamedAttribute> mutableAttributes(attributes.begin(),
1200 for (
auto &nla : nlas) {
1202 mutableAttributes[nonLocalIndex].setValue(nla);
1203 auto dict = DictionaryAttr::getWithSorted(
context, mutableAttributes);
1206 newAnnotations.push_back(anno);
1215 targetMap.erase(nla.getNameAttr());
1216 nlaTable->erase(nla);
1217 nlaCache.erase(nla.getNamepathAttr());
1218 symbolTable.erase(nla);
1224 FModuleOp fromModule) {
1225 auto toName = toModule.getNameAttr();
1226 auto fromName = fromModule.getNameAttr();
1229 auto moduleNLAs = nlaTable->lookup(fromModule.getNameAttr()).vec();
1231 nlaTable->renameModuleAndInnerRef(toName, fromName, renameMap);
1236 auto *fromNode = instanceGraph.lookup(fromName);
1237 std::optional<std::pair<Attribute, StringAttr>> singlePrefixAndName;
1240 for (
auto nla : moduleNLAs) {
1241 auto elements = nla.getNamepath().getValue();
1243 if (nla.root() != toName)
1247 if (fromNode->hasOneUse()) {
1248 if (!singlePrefixAndName) {
1249 auto *instanceRecord = *fromNode->uses().begin();
1251 cast<FModuleOp>(*instanceRecord->getParent()->getModule());
1252 singlePrefixAndName = {
OpAnnoTarget(instanceRecord->getInstance())
1254 parent.getNameAttr()};
1256 SmallVector<Attribute> newNamepath;
1257 newNamepath.reserve(elements.size() + 1);
1258 auto [singlePrefix, singleParentName] = *singlePrefixAndName;
1259 newNamepath.push_back(singlePrefix);
1260 newNamepath.append(elements.begin(), elements.end());
1261 auto newPath = ArrayAttr::get(
context, newNamepath);
1264 if (!nlaCache.count(newPath)) {
1265 nlaCache.erase(nla.getNamepathAttr());
1266 nla.setNamepathAttr(newPath);
1267 nlaCache[newPath] = nla.getNameAttr();
1268 nlaTable->addNLAtoModule(nla, singleParentName);
1274 SmallVector<Attribute> namepath(elements.begin(), elements.end());
1275 auto nlaRefs = createNLAs(fromModule, namepath, nla.getVisibility());
1277 auto &set = targetMap[nla.getNameAttr()];
1278 SmallVector<AnnoTarget> targets(set.begin(), set.end());
1280 for (
auto target : targets) {
1283 SmallVector<Annotation> newAnnotations;
1284 for (
auto anno : target.getAnnotations()) {
1286 auto [it, found] = mlir::impl::findAttrSorted(
1287 anno.begin(), anno.end(), nonLocalString);
1290 if (!found || cast<FlatSymbolRefAttr>(it->getValue()).getAttr() !=
1291 nla.getNameAttr()) {
1292 newAnnotations.push_back(anno);
1295 auto nonLocalIndex = std::distance(anno.begin(), it);
1297 cloneAnnotation(nlaRefs, anno,
1298 ArrayRef<NamedAttribute>(anno.begin(), anno.end()),
1299 nonLocalIndex, newAnnotations);
1304 target.setAnnotations(annotations);
1306 for (
auto nla : nlaRefs)
1307 targetMap[nla.getAttr()].insert(target);
1319 FModuleOp fromModule) {
1320 addAnnotationContext(renameMap, toModule, toModule);
1321 addAnnotationContext(renameMap, toModule, fromModule);
1327 StringAttr fromName) {
1328 nlaTable->renameModuleAndInnerRef(toName, fromName, renameMap);
1336 SmallVectorImpl<Annotation> &newAnnotations) {
1339 SmallVector<NamedAttribute> attributes;
1340 int nonLocalIndex = -1;
1341 for (
const auto &val : llvm::enumerate(anno)) {
1342 auto attr = val.value();
1344 auto compare = attr.getName().compare(nonLocalString);
1345 assert(compare != 0 &&
"should not pass non-local annotations here");
1349 nonLocalIndex = val.index();
1350 attributes.push_back(NamedAttribute(nonLocalString, nonLocalString));
1355 attributes.push_back(attr);
1357 if (nonLocalIndex == -1) {
1359 nonLocalIndex = attributes.size();
1360 attributes.push_back(NamedAttribute(nonLocalString, nonLocalString));
1363 attributes.append(anno.
begin() + nonLocalIndex, anno.
end());
1367 auto nlaRefs = createNLAs(toModuleName, fromModule);
1368 for (
auto nla : nlaRefs)
1369 targetMap[nla.getAttr()].insert(to);
1372 cloneAnnotation(nlaRefs, anno, attributes, nonLocalIndex, newAnnotations);
1378 SmallVectorImpl<Annotation> &newAnnotations,
1379 SmallPtrSetImpl<Attribute> &dontTouches) {
1380 for (
auto anno : annos) {
1381 if (anno.isClass(dontTouchAnnoClass)) {
1384 anno.removeMember(
"circt.nonlocal");
1385 auto [it, inserted] = dontTouches.insert(anno.getAttr());
1387 newAnnotations.push_back(anno);
1392 if (
auto nla = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
1393 newAnnotations.push_back(anno);
1394 targetMap[nla.getAttr()].insert(to);
1398 makeAnnotationNonLocal(toModule.getModuleNameAttr(), to, fromModule, anno,
1409 SmallVector<Annotation> newAnnotations;
1413 llvm::SmallPtrSet<Attribute, 4> dontTouches;
1417 copyAnnotations(toModule, to, toModule, toAnnos, newAnnotations,
1419 copyAnnotations(toModule, to, fromModule, fromAnnos, newAnnotations,
1423 if (!newAnnotations.empty())
1429 FModuleLike fromModule, Operation *from) {
1435 if (toModule == to) {
1437 for (
unsigned i = 0, e =
getNumPorts(toModule); i < e; ++i)
1442 }
else if (
auto toMem = dyn_cast<MemOp>(to)) {
1444 auto fromMem = cast<MemOp>(from);
1445 for (
unsigned i = 0, e = toMem.getNumResults(); i < e; ++i)
1454 hw::InnerSymAttr toSym,
1455 hw::InnerSymAttr fromSym) {
1456 if (fromSym && !fromSym.getProps().empty()) {
1457 auto &isn = getNamespace(toModule);
1459 SmallVector<hw::InnerSymPropertiesAttr> newProps;
1462 llvm::append_range(newProps, toSym);
1464 for (
auto fromProp : fromSym) {
1465 hw::InnerSymPropertiesAttr newProp;
1466 auto *it = llvm::find_if(newProps, [&](
auto p) {
1467 return p.getFieldID() == fromProp.getFieldID();
1469 if (it != newProps.end()) {
1474 if (fromProp.getSymVisibility().getValue() ==
"public" &&
1475 newProp.getSymVisibility().getValue() !=
"public") {
1476 *it = hw::InnerSymPropertiesAttr::get(
context, newProp.getName(),
1477 newProp.getFieldID(),
1478 fromProp.getSymVisibility());
1482 auto newName = isn.newName(fromProp.getName().getValue());
1483 newProp = hw::InnerSymPropertiesAttr::get(
1484 context, StringAttr::get(
context, newName), fromProp.getFieldID(),
1485 fromProp.getSymVisibility());
1486 newProps.push_back(newProp);
1488 renameMap[fromProp.getName()] = newProp.getName();
1491 llvm::sort(newProps, [](
auto &p,
auto &q) {
1492 return p.getFieldID() < q.getFieldID();
1495 return hw::InnerSymAttr::get(
context, newProps);
1497 return hw::InnerSymAttr();
1504 Operation *to, FModuleLike fromModule,
1508 if (
auto fromInnerSym = dyn_cast<hw::InnerSymbolOpInterface>(from)) {
1509 auto toInnerSym = cast<hw::InnerSymbolOpInterface>(to);
1510 if (
auto newSymAttr = mergeInnerSymbols(renameMap, toModule,
1511 toInnerSym.getInnerSymAttr(),
1512 fromInnerSym.getInnerSymAttr()))
1513 toInnerSym.setInnerSymbolAttr(newSymAttr);
1517 auto fromPortSyms = from->getAttrOfType<ArrayAttr>(
"portSymbols");
1518 if (!fromPortSyms || fromPortSyms.empty())
1521 auto portCount = fromPortSyms.size();
1522 auto toPortSyms = to->getAttrOfType<ArrayAttr>(
"portSymbols");
1526 SmallVector<Attribute> newPortSyms;
1527 if (toPortSyms.empty())
1528 newPortSyms.assign(portCount, hw::InnerSymAttr());
1530 newPortSyms.assign(toPortSyms.begin(), toPortSyms.end());
1532 for (
unsigned portNo = 0; portNo < portCount; ++portNo) {
1533 if (
auto newPortSym = mergeInnerSymbols(
1534 renameMap, toModule,
1535 llvm::cast_if_present<hw::InnerSymAttr>(newPortSyms[portNo]),
1536 cast<hw::InnerSymAttr>(fromPortSyms[portNo]))) {
1537 newPortSyms[portNo] = newPortSym;
1542 FModuleLike::fixupPortSymsArray(newPortSyms, toModule.getContext());
1543 cast<FModuleLike>(to).setPortSymbols(newPortSyms);
1549 FModuleLike fromModule, Operation *from) {
1551 if (to->getLoc() != from->getLoc())
1555 for (
auto regions : llvm::zip(to->getRegions(), from->getRegions()))
1556 mergeRegions(renameMap, toModule, std::get<0>(regions), fromModule,
1557 std::get<1>(regions));
1560 recordSymRenames(renameMap, toModule, to, fromModule, from);
1563 mergeAnnotations(toModule, to, fromModule, from);
1568 FModuleLike fromModule, Block &fromBlock) {
1570 for (
auto [toArg, fromArg] :
1571 llvm::zip(toBlock.getArguments(), fromBlock.getArguments()))
1572 if (toArg.getLoc() != fromArg.getLoc())
1575 for (
auto ops : llvm::zip(toBlock, fromBlock))
1576 mergeOps(renameMap, toModule, &std::get<0>(ops), fromModule,
1582 Region &toRegion, FModuleLike fromModule,
1583 Region &fromRegion) {
1584 for (
auto blocks : llvm::zip(toRegion, fromRegion))
1585 mergeBlocks(renameMap, toModule, std::get<0>(blocks), fromModule,
1586 std::get<1>(blocks));
1600 DenseMap<Attribute, llvm::SmallDenseSet<AnnoTarget>>
targetMap;
1621static void fixupConnect(ImplicitLocOpBuilder &builder, Value dst, Value src) {
1623 auto dstType = dst.getType();
1624 auto srcType = src.getType();
1625 if (dstType == srcType) {
1631 auto dstBundle = type_cast<BundleType>(dstType);
1632 auto srcBundle = type_cast<BundleType>(srcType);
1633 for (
unsigned i = 0; i < dstBundle.getNumElements(); ++i) {
1634 auto dstField = SubfieldOp::create(builder, dst, i);
1635 auto srcField = SubfieldOp::create(builder, src, i);
1636 if (dstBundle.getElement(i).isFlip) {
1637 std::swap(srcBundle, dstBundle);
1638 std::swap(srcField, dstField);
1648 const DenseMap<Attribute, StringAttr> &dedupMap) {
1654 if (
auto instOp = dyn_cast<InstanceOp>(op)) {
1655 ImplicitLocOpBuilder builder(instOp.getLoc(), instOp->getContext());
1656 builder.setInsertionPointAfter(instOp);
1657 auto module = instanceGraph.lookup(instOp.getModuleNameAttr().getAttr())
1658 ->getModule<FModuleLike>();
1659 for (
auto [index, result] : llvm::enumerate(instOp.getResults())) {
1660 auto newType =
module.getPortType(index);
1661 auto oldType = result.getType();
1663 if (newType == oldType)
1665 LLVM_DEBUG(llvm::dbgs()
1666 <<
"- Updating instance port \"" << instOp.getInstanceName()
1667 <<
"." << instOp.getPortName(index) <<
"\" from " << oldType
1668 <<
" to " << newType <<
"\n");
1672 auto wire = WireOp::create(builder, oldType, instOp.getPortName(index))
1674 result.replaceAllUsesWith(wire);
1675 result.setType(newType);
1676 if (instOp.getPortDirection(index) == Direction::Out)
1685 mlir::AttrTypeReplacer replacer;
1686 replacer.addReplacement([&](FlatSymbolRefAttr symRef) {
1687 auto oldName = symRef.getAttr();
1688 auto newName = dedupMap.lookup(oldName);
1689 if (newName && newName != oldName) {
1690 auto newSymRef = FlatSymbolRefAttr::get(newName);
1691 LLVM_DEBUG(llvm::dbgs()
1692 <<
"- Updating " << symRef <<
" to " << newSymRef <<
" in "
1693 << op->getName() <<
" at " << op->getLoc() <<
"\n");
1700 op->setAttrs(cast<DictionaryAttr>(replacer.replace(op->getAttrDictionary())));
1703 for (
auto ®ion : op->getRegions())
1704 for (
auto &block : region)
1705 for (
auto arg : block.getArguments())
1706 arg.setType(replacer.replace(arg.getType()));
1709 for (
auto result : op->getResults())
1710 result.setType(replacer.replace(result.getType()));
1717 const DenseMap<Operation *, ModuleInfoRef> &moduleToModuleInfo,
1718 const DenseMap<Attribute, StringAttr> &dedupMap) {
1719 for (
auto *node : instanceGraph) {
1722 auto module = node->getModule<FModuleLike>();
1723 auto it = moduleToModuleInfo.find(module);
1724 if (it == moduleToModuleInfo.end())
1728 auto &ops = it->second.info->symbolSensitiveOps;
1731 LLVM_DEBUG(llvm::dbgs()
1732 <<
"- Updating " << ops.size() <<
" symbol-sensitive ops in "
1733 << module.getModuleNameAttr() <<
"\n");
1734 for (
auto *op : ops)
1744class DedupPass :
public circt::firrtl::impl::DedupBase<DedupPass> {
1745 using DedupBase::DedupBase;
1747 void runOnOperation()
override {
1748 auto *
context = &getContext();
1749 auto circuit = getOperation();
1750 auto &instanceGraph = getAnalysis<InstanceGraph>();
1751 auto *nlaTable = &getAnalysis<NLATable>();
1752 auto &symbolTable = getAnalysis<SymbolTable>();
1753 Deduper deduper(instanceGraph, symbolTable, nlaTable, circuit);
1755 auto anythingChanged =
false;
1757 llvm::dbgs() <<
"\n";
1758 debugHeader(Twine(
"Dedup circuit \"") + circuit.getName() +
"\"")
1763 auto noDedupClass = StringAttr::get(
context, noDedupAnnoClass);
1766 auto dedupGroupClass = StringAttr::get(
context, dedupGroupAnnoClass);
1769 DenseMap<ModuleInfoRef, Operation *> moduleInfoToModule;
1770 DenseMap<Operation *, ModuleInfoRef> moduleToModuleInfo;
1775 DenseMap<Attribute, StringAttr> dedupMap;
1780 SmallVector<FModuleLike, 0> modules;
1782 if (
auto mod = dyn_cast<FModuleLike>(*node.getModule()))
1783 modules.push_back(mod);
1785 LLVM_DEBUG(llvm::dbgs() <<
"Found " << modules.size() <<
" modules\n");
1787 SmallVector<std::optional<ModuleInfo>> moduleInfos(modules.size());
1791 auto dedupGroupAttrName = StringAttr::get(
context,
"firrtl.dedup_group");
1797 for (
auto module : modules) {
1800 module, [&groups, dedupGroupClass](
Annotation annotation) {
1803 groups.insert(annotation.
getMember<StringAttr>(
"group"));
1806 if (groups.size() > 1) {
1807 module.emitError("module belongs to multiple dedup groups: ") << groups;
1808 return signalPassFailure();
1810 assert(!module->hasAttr(dedupGroupAttrName) &&
1811 "unexpected existing use of temporary dedup group attribute");
1812 if (!groups.empty())
1813 module->setDiscardableAttr(dedupGroupAttrName, groups.front());
1817 LLVM_DEBUG(llvm::dbgs() <<
"Computing module information\n");
1818 auto result = mlir::failableParallelForEach(
1819 context, llvm::seq(modules.size()), [&](
unsigned idx) {
1820 auto module = modules[idx];
1822 if (AnnotationSet::hasAnnotation(module, noDedupClass))
1826 if (auto ext = dyn_cast<FExtModuleOp>(*module);
1827 ext && !ext.getDefname().has_value())
1831 if (isa<ClassOp>(*module) && !dedupClasses)
1834 StructuralHasher hasher(hasherConstants);
1836 moduleInfos[idx] = hasher.getModuleInfo(module);
1842 auto &os = llvm::dbgs();
1843 for (
auto [module, info] :
llvm::zip(modules, moduleInfos)) {
1846 os << llvm::format_bytes(
info->structuralHash, std::nullopt, 32, 32);
1848 os <<
"--------------------------------";
1849 os <<
"--------------------------------";
1851 os <<
" for " <<
module.getModuleNameAttr() << "\n";
1855 if (result.failed())
1856 return signalPassFailure();
1858 LLVM_DEBUG(llvm::dbgs() <<
"Update modules\n");
1859 for (
auto [i, module] :
llvm::enumerate(modules)) {
1860 auto moduleName =
module.getModuleNameAttr();
1861 auto &maybeModuleInfo = moduleInfos[i];
1863 if (!maybeModuleInfo) {
1868 dedupMap[moduleName] = moduleName;
1872 auto &moduleInfo = maybeModuleInfo.value();
1873 moduleToModuleInfo.try_emplace(module, &moduleInfo);
1876 for (
auto &referredModule : moduleInfo.referredModuleNames)
1877 referredModule = dedupMap[referredModule];
1880 auto it = moduleInfoToModule.find(&moduleInfo);
1881 if (it != moduleInfoToModule.end()) {
1882 auto original = cast<FModuleLike>(it->second);
1883 auto originalName = original.getModuleNameAttr();
1888 cast<mlir::SymbolOpInterface>(module.getOperation()))) {
1890 dedupMap[moduleName] = moduleName;
1893 cast<mlir::SymbolOpInterface>(original.getOperation())))
1896 for (
auto &[_, dedupedName] : dedupMap)
1897 if (dedupedName == originalName)
1898 dedupedName = moduleName;
1901 it->second =
module;
1903 std::swap(originalName, moduleName);
1904 std::swap(original, module);
1908 LLVM_DEBUG(llvm::dbgs() <<
"- Replace " << moduleName <<
" with "
1909 << originalName <<
"\n");
1910 dedupMap[moduleName] = originalName;
1911 deduper.dedup(original, module);
1913 anythingChanged =
true;
1917 deduper.record(module);
1919 dedupMap[moduleName] = moduleName;
1921 moduleInfoToModule[&moduleInfo] =
module;
1929 auto failed =
false;
1931 auto parseModule = [&](Attribute path) -> StringAttr {
1934 auto [_, rhs] = cast<StringAttr>(path).getValue().split(
'|');
1935 return StringAttr::get(
context, rhs);
1940 auto getLead = [&](StringAttr module) -> StringAttr {
1941 auto it = dedupMap.find(module);
1942 if (it == dedupMap.end()) {
1943 auto diag = emitError(circuit.getLoc(),
1944 "MustDeduplicateAnnotation references module ")
1945 <<
module << " which does not exist";
1952 LLVM_DEBUG(llvm::dbgs() <<
"Update annotations\n");
1954 if (!annotation.
isClass(mustDeduplicateAnnoClass))
1956 auto modules = annotation.
getMember<ArrayAttr>(
"modules");
1958 emitError(circuit.getLoc(),
1959 "MustDeduplicateAnnotation missing \"modules\" member");
1964 if (modules.empty())
1967 auto firstModule = parseModule(modules[0]);
1968 auto firstLead = getLead(firstModule);
1972 for (
auto attr : modules.getValue().drop_front()) {
1973 auto nextModule = parseModule(attr);
1974 auto nextLead = getLead(nextModule);
1977 if (firstLead != nextLead) {
1978 auto diag = emitError(circuit.getLoc(),
"module ")
1979 << nextModule <<
" not deduplicated with " << firstModule;
1982 equiv.check(diag, a, b);
1990 return signalPassFailure();
1993 for (
auto module : circuit.getOps<FModuleLike>())
1994 module->removeDiscardableAttr(dedupGroupAttrName);
2000 markAnalysesPreserved<NLATable>();
2001 if (!anythingChanged)
2002 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)
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)
void emitConnect(OpBuilder &builder, Location loc, Value lhs, Value rhs, bool warnOnTruncation=false)
Emit a connect between two values.
std::pair< std::string, bool > getFieldName(const FieldRef &fieldRef, bool nameSafe=false)
Get a string identifier representing the FieldRef.
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.
LogicalResult check(InFlightDiagnostic &diag, igraph::InstanceOpInterface a, igraph::InstanceOpInterface b)
DenseSet< Attribute > nonessentialAttributes
std::string prettyPrint(Attribute attr)
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 unsigned getHashValue(const ModuleInfoRef &ref)