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, igraph::InstanceOpInterface a,
776 igraph::InstanceOpInterface b) {
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();
825 if (
auto aInst = dyn_cast<igraph::InstanceOpInterface>(a))
826 if (
auto bInst = dyn_cast<igraph::InstanceOpInterface>(b))
827 if (isa_and_nonnull<firrtl::FIRRTLDialect>(a->getDialect()) &&
828 isa_and_nonnull<firrtl::FIRRTLDialect>(b->getDialect()) &&
829 failed(
check(diag, aInst, bInst)))
833 if (a->getNumResults() != b->getNumResults()) {
834 diag.attachNote(a->getLoc())
835 <<
"operations have different number of results";
836 diag.attachNote(b->getLoc()) <<
"second operation here";
839 for (
auto resultPair : llvm::zip(a->getResults(), b->getResults())) {
840 auto &aValue = std::get<0>(resultPair);
841 auto &bValue = std::get<1>(resultPair);
842 if (failed(
check(diag,
"operation result", a, aValue.getType(), b,
845 data.map.map(aValue, bValue);
849 if (a->getNumOperands() != b->getNumOperands()) {
850 diag.attachNote(a->getLoc())
851 <<
"operations have different number of operands";
852 diag.attachNote(b->getLoc()) <<
"second operation here";
855 for (
auto operandPair : llvm::zip(a->getOperands(), b->getOperands())) {
856 auto &aValue = std::get<0>(operandPair);
857 auto &bValue = std::get<1>(operandPair);
858 if (bValue !=
data.map.lookup(aValue)) {
859 diag.attachNote(a->getLoc())
860 <<
"operations use different operands, first operand is '"
865 diag.attachNote(b->getLoc())
866 <<
"second operand is '"
870 <<
"', but should have been '"
881 if (a->getNumRegions() != b->getNumRegions()) {
882 diag.attachNote(a->getLoc())
883 <<
"operations have different number of regions";
884 diag.attachNote(b->getLoc()) <<
"second operation here";
887 for (
auto regionPair : llvm::zip(a->getRegions(), b->getRegions())) {
888 auto &aRegion = std::get<0>(regionPair);
889 auto &bRegion = std::get<1>(regionPair);
890 if (failed(
check(diag,
data, a, aRegion, b, bRegion)))
895 if (failed(
check(diag,
data, a, a->getAttrDictionary(), b,
896 b->getAttrDictionary())))
902 void check(InFlightDiagnostic &diag, Operation *a, Operation *b) {
907 diag.attachNote(a->getLoc()) <<
"module marked NoDedup";
911 diag.attachNote(b->getLoc()) <<
"module marked NoDedup";
914 auto aSymbol = cast<mlir::SymbolOpInterface>(a);
915 auto bSymbol = cast<mlir::SymbolOpInterface>(b);
917 diag.attachNote(a->getLoc())
919 << (aSymbol.isPrivate() ?
"private but not discardable" :
"public");
920 diag.attachNote(b->getLoc())
922 << (bSymbol.isPrivate() ?
"private but not discardable" :
"public");
927 auto bGroup = dyn_cast_or_null<StringAttr>(
929 if (aGroup != bGroup) {
931 diag.attachNote(b->getLoc())
932 <<
"module is in dedup group '" << bGroup.str() <<
"'";
934 diag.attachNote(b->getLoc()) <<
"module is not part of a dedup group";
937 diag.attachNote(a->getLoc())
938 <<
"module is in dedup group '" << aGroup.str() <<
"'";
940 diag.attachNote(a->getLoc()) <<
"module is not part of a dedup group";
946 diag.attachNote(a->getLoc()) <<
"first module here";
947 diag.attachNote(b->getLoc()) <<
"second module here";
973 llvm::SmallSetVector<Location, 4> decomposedLocs;
975 unsigned seenFIR = 0;
976 for (
auto loc : {to, from}) {
979 if (
auto fusedLoc = dyn_cast<FusedLoc>(loc)) {
982 for (
auto loc : fusedLoc.getLocations()) {
983 if (FileLineColLoc fileLoc = dyn_cast<FileLineColLoc>(loc)) {
984 if (fileLoc.getFilename().strref().ends_with(
".fir")) {
990 decomposedLocs.insert(loc);
996 if (FileLineColLoc fileLoc = dyn_cast<FileLineColLoc>(loc)) {
997 if (fileLoc.getFilename().strref().ends_with(
".fir")) {
1004 if (!isa<UnknownLoc>(loc))
1005 decomposedLocs.insert(loc);
1008 auto locs = decomposedLocs.getArrayRef();
1013 return UnknownLoc::get(
context);
1014 if (locs.size() == 1)
1015 return locs.front();
1017 return FusedLoc::get(
context, locs);
1032 for (
auto nla : circuit.getOps<hw::HierPathOp>())
1033 nlaCache[nla.getNamepathAttr()] = nla.getSymNameAttr();
1040 void dedup(FModuleLike toModule, FModuleLike fromModule) {
1046 SmallVector<Attribute> newLocs;
1047 for (
auto [toLoc, fromLoc] : llvm::zip(toModule.getPortLocations(),
1048 fromModule.getPortLocations())) {
1049 if (toLoc == fromLoc)
1050 newLocs.push_back(toLoc);
1053 cast<LocationAttr>(fromLoc)));
1055 toModule->setAttr(
"portLocations", ArrayAttr::get(
context, newLocs));
1058 mergeOps(renameMap, toModule, toModule, fromModule, fromModule);
1064 if (
auto to = dyn_cast<FModuleOp>(*toModule))
1068 fromModule.getModuleNameAttr());
1079 for (
unsigned i = 0, e =
getNumPorts(module); i < e; ++i)
1082 module->walk([&](Operation *op) { recordAnnotations(op); });
1088 return moduleNamespaces.try_emplace(module, cast<FModuleLike>(module))
1096 if (
auto nlaRef = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal"))
1097 targetMap[nlaRef.getAttr()].insert(target);
1106 auto mem = dyn_cast<MemOp>(op);
1111 for (
unsigned i = 0, e = mem->getNumResults(); i < e; ++i)
1120 instanceGraph[::cast<igraph::ModuleOpInterface>(fromModule)];
1121 auto *toNode = instanceGraph[toModule];
1122 auto toModuleRef = FlatSymbolRefAttr::get(toModule.getModuleNameAttr());
1123 for (
auto *oldInstRec : llvm::make_early_inc_range(fromNode->uses())) {
1124 auto inst = oldInstRec->getInstance();
1125 if (
auto instOp = dyn_cast<InstanceOp>(*inst)) {
1126 instOp.setModuleNameAttr(toModuleRef);
1127 instOp.setPortNamesAttr(toModule.getPortNamesAttr());
1128 }
else if (
auto objectOp = dyn_cast<ObjectOp>(*inst)) {
1129 auto classLike = cast<ClassLike>(*toNode->getModule());
1130 ClassType classType = detail::getInstanceTypeForClassLike(classLike);
1131 objectOp.getResult().setType(classType);
1132 }
else if (
auto instanceChoiceOp = dyn_cast<InstanceChoiceOp>(*inst)) {
1133 auto fromModuleName = fromNode->getModule().getModuleNameAttr();
1134 SmallVector<Attribute> newModules;
1135 for (
auto module : instanceChoiceOp.getReferencedModuleNamesAttr()) {
1136 auto moduleName = cast<StringAttr>(module);
1137 if (moduleName == fromModuleName)
1138 newModules.push_back(toModuleRef);
1140 newModules.push_back(FlatSymbolRefAttr::get(moduleName));
1142 instanceChoiceOp.setModuleNamesAttr(
1143 ArrayAttr::get(
context, newModules));
1144 instanceChoiceOp.setPortNamesAttr(toModule.getPortNamesAttr());
1146 oldInstRec->getParent()->addInstance(inst, toNode);
1147 oldInstRec->erase();
1149 instanceGraph.erase(fromNode);
1150 fromModule->erase();
1158 SmallVector<FlatSymbolRefAttr>
1159 createNLAs(Operation *fromModule, ArrayRef<Attribute> baseNamepath,
1160 SymbolTable::Visibility vis = SymbolTable::Visibility::Private) {
1163 SmallVector<Attribute> namepath = {
nullptr};
1164 namepath.append(baseNamepath.begin(), baseNamepath.end());
1166 auto loc = fromModule->getLoc();
1167 auto *fromNode = instanceGraph[cast<igraph::ModuleOpInterface>(fromModule)];
1168 SmallVector<FlatSymbolRefAttr> nlas;
1169 for (
auto *instanceRecord : fromNode->uses()) {
1170 auto parent = cast<FModuleOp>(*instanceRecord->getParent()->getModule());
1171 auto inst = instanceRecord->getInstance();
1173 auto arrayAttr = ArrayAttr::get(
context, namepath);
1175 auto &cacheEntry = nlaCache[arrayAttr];
1177 auto builder = OpBuilder::atBlockBegin(nlaBlock);
1178 auto nla = hw::HierPathOp::create(builder, loc,
"nla", arrayAttr);
1180 symbolTable.insert(nla);
1182 cacheEntry = nla.getNameAttr();
1183 nla.setVisibility(vis);
1184 nlaTable->addNLA(nla);
1186 auto nlaRef = FlatSymbolRefAttr::get(cast<StringAttr>(cacheEntry));
1187 nlas.push_back(nlaRef);
1195 SmallVector<FlatSymbolRefAttr>
1197 SymbolTable::Visibility vis = SymbolTable::Visibility::Private) {
1198 return createNLAs(fromModule, FlatSymbolRefAttr::get(toModuleName), vis);
1205 Annotation anno, ArrayRef<NamedAttribute> attributes,
1206 unsigned nonLocalIndex,
1207 SmallVectorImpl<Annotation> &newAnnotations) {
1208 SmallVector<NamedAttribute> mutableAttributes(attributes.begin(),
1210 for (
auto &nla : nlas) {
1212 mutableAttributes[nonLocalIndex].setValue(nla);
1213 auto dict = DictionaryAttr::getWithSorted(
context, mutableAttributes);
1216 newAnnotations.push_back(anno);
1225 targetMap.erase(nla.getNameAttr());
1226 nlaTable->erase(nla);
1227 nlaCache.erase(nla.getNamepathAttr());
1228 symbolTable.erase(nla);
1234 FModuleOp fromModule) {
1235 auto toName = toModule.getNameAttr();
1236 auto fromName = fromModule.getNameAttr();
1239 auto moduleNLAs = nlaTable->lookup(fromModule.getNameAttr()).vec();
1241 nlaTable->renameModuleAndInnerRef(toName, fromName, renameMap);
1244 for (
auto nla : moduleNLAs) {
1245 auto elements = nla.getNamepath().getValue();
1247 if (nla.root() != toName)
1250 SmallVector<Attribute> namepath(elements.begin(), elements.end());
1251 auto nlaRefs = createNLAs(fromModule, namepath, nla.getVisibility());
1253 auto &set = targetMap[nla.getSymNameAttr()];
1254 SmallVector<AnnoTarget> targets(set.begin(), set.end());
1256 for (
auto target : targets) {
1259 SmallVector<Annotation> newAnnotations;
1260 for (
auto anno : target.getAnnotations()) {
1262 auto [it, found] = mlir::impl::findAttrSorted(
1263 anno.begin(), anno.end(), nonLocalString);
1266 if (!found || cast<FlatSymbolRefAttr>(it->getValue()).getAttr() !=
1267 nla.getSymNameAttr()) {
1268 newAnnotations.push_back(anno);
1271 auto nonLocalIndex = std::distance(anno.begin(), it);
1273 cloneAnnotation(nlaRefs, anno,
1274 ArrayRef<NamedAttribute>(anno.begin(), anno.end()),
1275 nonLocalIndex, newAnnotations);
1280 target.setAnnotations(annotations);
1282 for (
auto nla : nlaRefs)
1283 targetMap[nla.getAttr()].insert(target);
1295 FModuleOp fromModule) {
1296 addAnnotationContext(renameMap, toModule, toModule);
1297 addAnnotationContext(renameMap, toModule, fromModule);
1303 StringAttr fromName) {
1304 nlaTable->renameModuleAndInnerRef(toName, fromName, renameMap);
1312 SmallVectorImpl<Annotation> &newAnnotations) {
1315 SmallVector<NamedAttribute> attributes;
1316 int nonLocalIndex = -1;
1317 for (
const auto &val : llvm::enumerate(anno)) {
1318 auto attr = val.value();
1320 auto compare = attr.getName().compare(nonLocalString);
1321 assert(compare != 0 &&
"should not pass non-local annotations here");
1325 nonLocalIndex = val.index();
1326 attributes.push_back(NamedAttribute(nonLocalString, nonLocalString));
1331 attributes.push_back(attr);
1333 if (nonLocalIndex == -1) {
1335 nonLocalIndex = attributes.size();
1336 attributes.push_back(NamedAttribute(nonLocalString, nonLocalString));
1339 attributes.append(anno.
begin() + nonLocalIndex, anno.
end());
1343 auto nlaRefs = createNLAs(toModuleName, fromModule);
1344 for (
auto nla : nlaRefs)
1345 targetMap[nla.getAttr()].insert(to);
1348 cloneAnnotation(nlaRefs, anno, attributes, nonLocalIndex, newAnnotations);
1354 SmallVectorImpl<Annotation> &newAnnotations,
1355 SmallPtrSetImpl<Attribute> &dontTouches) {
1356 for (
auto anno : annos) {
1357 if (anno.isClass(dontTouchAnnoClass)) {
1360 anno.removeMember(
"circt.nonlocal");
1361 auto [it, inserted] = dontTouches.insert(anno.getAttr());
1363 newAnnotations.push_back(anno);
1368 if (
auto nla = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
1369 newAnnotations.push_back(anno);
1370 targetMap[nla.getAttr()].insert(to);
1374 makeAnnotationNonLocal(toModule.getModuleNameAttr(), to, fromModule, anno,
1385 SmallVector<Annotation> newAnnotations;
1389 llvm::SmallPtrSet<Attribute, 4> dontTouches;
1393 copyAnnotations(toModule, to, toModule, toAnnos, newAnnotations,
1395 copyAnnotations(toModule, to, fromModule, fromAnnos, newAnnotations,
1399 if (!newAnnotations.empty())
1405 FModuleLike fromModule, Operation *from) {
1411 if (toModule == to) {
1413 for (
unsigned i = 0, e =
getNumPorts(toModule); i < e; ++i)
1418 }
else if (
auto toMem = dyn_cast<MemOp>(to)) {
1420 auto fromMem = cast<MemOp>(from);
1421 for (
unsigned i = 0, e = toMem.getNumResults(); i < e; ++i)
1430 hw::InnerSymAttr toSym,
1431 hw::InnerSymAttr fromSym) {
1432 if (fromSym && !fromSym.getProps().empty()) {
1433 auto &isn = getNamespace(toModule);
1435 SmallVector<hw::InnerSymPropertiesAttr> newProps;
1438 llvm::append_range(newProps, toSym);
1440 for (
auto fromProp : fromSym) {
1441 hw::InnerSymPropertiesAttr newProp;
1442 auto *it = llvm::find_if(newProps, [&](
auto p) {
1443 return p.getFieldID() == fromProp.getFieldID();
1445 if (it != newProps.end()) {
1450 if (fromProp.getSymVisibility().getValue() ==
"public" &&
1451 newProp.getSymVisibility().getValue() !=
"public") {
1452 *it = hw::InnerSymPropertiesAttr::get(
context, newProp.getName(),
1453 newProp.getFieldID(),
1454 fromProp.getSymVisibility());
1458 auto newName = isn.newName(fromProp.getName().getValue());
1459 newProp = hw::InnerSymPropertiesAttr::get(
1460 context, StringAttr::get(
context, newName), fromProp.getFieldID(),
1461 fromProp.getSymVisibility());
1462 newProps.push_back(newProp);
1464 renameMap[fromProp.getName()] = newProp.getName();
1467 llvm::sort(newProps, [](
auto &p,
auto &q) {
1468 return p.getFieldID() < q.getFieldID();
1471 return hw::InnerSymAttr::get(
context, newProps);
1473 return hw::InnerSymAttr();
1480 Operation *to, FModuleLike fromModule,
1484 if (
auto fromInnerSym = dyn_cast<hw::InnerSymbolOpInterface>(from)) {
1485 auto toInnerSym = cast<hw::InnerSymbolOpInterface>(to);
1486 if (
auto newSymAttr = mergeInnerSymbols(renameMap, toModule,
1487 toInnerSym.getInnerSymAttr(),
1488 fromInnerSym.getInnerSymAttr()))
1489 toInnerSym.setInnerSymbolAttr(newSymAttr);
1493 auto fromPortSyms = from->getAttrOfType<ArrayAttr>(
"portSymbols");
1494 if (!fromPortSyms || fromPortSyms.empty())
1497 auto portCount = fromPortSyms.size();
1498 auto toPortSyms = to->getAttrOfType<ArrayAttr>(
"portSymbols");
1502 SmallVector<Attribute> newPortSyms;
1503 if (toPortSyms.empty())
1504 newPortSyms.assign(portCount, hw::InnerSymAttr());
1506 newPortSyms.assign(toPortSyms.begin(), toPortSyms.end());
1508 for (
unsigned portNo = 0; portNo < portCount; ++portNo) {
1509 if (
auto newPortSym = mergeInnerSymbols(
1510 renameMap, toModule,
1511 llvm::cast_if_present<hw::InnerSymAttr>(newPortSyms[portNo]),
1512 cast<hw::InnerSymAttr>(fromPortSyms[portNo]))) {
1513 newPortSyms[portNo] = newPortSym;
1518 FModuleLike::fixupPortSymsArray(newPortSyms, toModule.getContext());
1519 cast<FModuleLike>(to).setPortSymbols(newPortSyms);
1525 FModuleLike fromModule, Operation *from) {
1527 if (to->getLoc() != from->getLoc())
1531 for (
auto regions : llvm::zip(to->getRegions(), from->getRegions()))
1532 mergeRegions(renameMap, toModule, std::get<0>(regions), fromModule,
1533 std::get<1>(regions));
1536 recordSymRenames(renameMap, toModule, to, fromModule, from);
1539 mergeAnnotations(toModule, to, fromModule, from);
1544 FModuleLike fromModule, Block &fromBlock) {
1546 for (
auto [toArg, fromArg] :
1547 llvm::zip(toBlock.getArguments(), fromBlock.getArguments()))
1548 if (toArg.getLoc() != fromArg.getLoc())
1551 for (
auto ops : llvm::zip(toBlock, fromBlock))
1552 mergeOps(renameMap, toModule, &std::get<0>(ops), fromModule,
1558 Region &toRegion, FModuleLike fromModule,
1559 Region &fromRegion) {
1560 for (
auto blocks : llvm::zip(toRegion, fromRegion))
1561 mergeBlocks(renameMap, toModule, std::get<0>(blocks), fromModule,
1562 std::get<1>(blocks));
1576 DenseMap<Attribute, llvm::SmallDenseSet<AnnoTarget>>
targetMap;
1597static void fixupConnect(ImplicitLocOpBuilder &builder, Value dst, Value src) {
1599 auto dstType = dst.getType();
1600 auto srcType = src.getType();
1601 if (dstType == srcType) {
1607 auto dstBundle = type_cast<BundleType>(dstType);
1608 auto srcBundle = type_cast<BundleType>(srcType);
1609 for (
unsigned i = 0; i < dstBundle.getNumElements(); ++i) {
1610 auto dstField = SubfieldOp::create(builder, dst, i);
1611 auto srcField = SubfieldOp::create(builder, src, i);
1612 if (dstBundle.getElement(i).isFlip) {
1613 std::swap(srcBundle, dstBundle);
1614 std::swap(srcField, dstField);
1624 const DenseMap<Attribute, StringAttr> &dedupMap) {
1630 if (
auto instOp = dyn_cast<InstanceOp>(op)) {
1631 ImplicitLocOpBuilder builder(instOp.getLoc(), instOp->getContext());
1632 builder.setInsertionPointAfter(instOp);
1633 auto module = instanceGraph.lookup(instOp.getModuleNameAttr().getAttr())
1634 ->getModule<FModuleLike>();
1635 for (
auto [index, result] : llvm::enumerate(instOp.getResults())) {
1636 auto newType =
module.getPortType(index);
1637 auto oldType = result.getType();
1639 if (newType == oldType)
1641 LLVM_DEBUG(llvm::dbgs()
1642 <<
"- Updating instance port \"" << instOp.getInstanceName()
1643 <<
"." << instOp.getPortName(index) <<
"\" from " << oldType
1644 <<
" to " << newType <<
"\n");
1648 auto wire = WireOp::create(builder, oldType, instOp.getPortName(index))
1650 result.replaceAllUsesWith(wire);
1651 result.setType(newType);
1652 if (instOp.getPortDirection(index) == Direction::Out)
1661 mlir::AttrTypeReplacer replacer;
1662 replacer.addReplacement([&](FlatSymbolRefAttr symRef) {
1663 auto oldName = symRef.getAttr();
1664 auto newName = dedupMap.lookup(oldName);
1665 if (newName && newName != oldName) {
1666 auto newSymRef = FlatSymbolRefAttr::get(newName);
1667 LLVM_DEBUG(llvm::dbgs()
1668 <<
"- Updating " << symRef <<
" to " << newSymRef <<
" in "
1669 << op->getName() <<
" at " << op->getLoc() <<
"\n");
1676 op->setAttrs(cast<DictionaryAttr>(replacer.replace(op->getAttrDictionary())));
1679 for (
auto ®ion : op->getRegions())
1680 for (
auto &block : region)
1681 for (
auto arg : block.getArguments())
1682 arg.setType(replacer.replace(arg.getType()));
1685 for (
auto result : op->getResults())
1686 result.setType(replacer.replace(result.getType()));
1693 const DenseMap<Operation *, ModuleInfoRef> &moduleToModuleInfo,
1694 const DenseMap<Attribute, StringAttr> &dedupMap) {
1695 for (
auto *node : instanceGraph) {
1698 auto module = node->getModule<FModuleLike>();
1699 auto it = moduleToModuleInfo.find(module);
1700 if (it == moduleToModuleInfo.end())
1704 auto &ops = it->second.info->symbolSensitiveOps;
1707 LLVM_DEBUG(llvm::dbgs()
1708 <<
"- Updating " << ops.size() <<
" symbol-sensitive ops in "
1709 << module.getNameAttr() <<
"\n");
1710 for (
auto *op : ops)
1720class DedupPass :
public circt::firrtl::impl::DedupBase<DedupPass> {
1721 using DedupBase::DedupBase;
1723 void runOnOperation()
override {
1724 auto *
context = &getContext();
1725 auto circuit = getOperation();
1726 auto &instanceGraph = getAnalysis<InstanceGraph>();
1727 auto *nlaTable = &getAnalysis<NLATable>();
1728 auto &symbolTable = getAnalysis<SymbolTable>();
1729 Deduper deduper(instanceGraph, symbolTable, nlaTable, circuit);
1731 auto anythingChanged =
false;
1733 llvm::dbgs() <<
"\n";
1734 debugHeader(Twine(
"Dedup circuit \"") + circuit.getName() +
"\"")
1739 auto noDedupClass = StringAttr::get(
context, noDedupAnnoClass);
1742 auto dedupGroupClass = StringAttr::get(
context, dedupGroupAnnoClass);
1745 DenseMap<ModuleInfoRef, Operation *> moduleInfoToModule;
1746 DenseMap<Operation *, ModuleInfoRef> moduleToModuleInfo;
1751 DenseMap<Attribute, StringAttr> dedupMap;
1756 SmallVector<FModuleLike, 0> modules;
1758 if (
auto mod = dyn_cast<FModuleLike>(*node.getModule()))
1759 modules.push_back(mod);
1761 LLVM_DEBUG(llvm::dbgs() <<
"Found " << modules.size() <<
" modules\n");
1763 SmallVector<std::optional<ModuleInfo>> moduleInfos(modules.size());
1767 auto dedupGroupAttrName = StringAttr::get(
context,
"firrtl.dedup_group");
1773 for (
auto module : modules) {
1774 llvm::SmallSetVector<StringAttr, 1> groups;
1776 module, [&groups, dedupGroupClass](
Annotation annotation) {
1779 groups.insert(annotation.
getMember<StringAttr>(
"group"));
1782 if (groups.size() > 1) {
1783 module.emitError("module belongs to multiple dedup groups: ") << groups;
1784 return signalPassFailure();
1786 assert(!module->hasAttr(dedupGroupAttrName) &&
1787 "unexpected existing use of temporary dedup group attribute");
1788 if (!groups.empty())
1789 module->setDiscardableAttr(dedupGroupAttrName, groups.front());
1793 LLVM_DEBUG(llvm::dbgs() <<
"Computing module information\n");
1794 auto result = mlir::failableParallelForEach(
1795 context, llvm::seq(modules.size()), [&](
unsigned idx) {
1796 auto module = modules[idx];
1798 if (AnnotationSet::hasAnnotation(module, noDedupClass))
1802 if (auto ext = dyn_cast<FExtModuleOp>(*module);
1803 ext && !ext.getDefname().has_value())
1807 if (isa<ClassOp>(*module) && !dedupClasses)
1810 StructuralHasher hasher(hasherConstants);
1812 moduleInfos[idx] = hasher.getModuleInfo(module);
1818 auto &os = llvm::dbgs();
1819 for (
auto [module, info] :
llvm::zip(modules, moduleInfos)) {
1822 os << llvm::format_bytes(
info->structuralHash, std::nullopt, 32, 32);
1824 os <<
"--------------------------------";
1825 os <<
"--------------------------------";
1827 os <<
" for " <<
module.getModuleNameAttr() << "\n";
1831 if (result.failed())
1832 return signalPassFailure();
1834 LLVM_DEBUG(llvm::dbgs() <<
"Update modules\n");
1835 for (
auto [i, module] :
llvm::enumerate(modules)) {
1836 auto moduleName =
module.getModuleNameAttr();
1837 auto &maybeModuleInfo = moduleInfos[i];
1839 if (!maybeModuleInfo) {
1844 dedupMap[moduleName] = moduleName;
1848 auto &moduleInfo = maybeModuleInfo.value();
1849 moduleToModuleInfo.try_emplace(module, &moduleInfo);
1852 for (
auto &referredModule : moduleInfo.referredModuleNames)
1853 referredModule = dedupMap[referredModule];
1856 auto it = moduleInfoToModule.find(&moduleInfo);
1857 if (it != moduleInfoToModule.end()) {
1858 auto original = cast<FModuleLike>(it->second);
1859 auto originalName = original.getModuleNameAttr();
1865 dedupMap[moduleName] = moduleName;
1870 for (
auto &[_, dedupedName] : dedupMap)
1871 if (dedupedName == originalName)
1872 dedupedName = moduleName;
1875 it->second =
module;
1877 std::swap(originalName, moduleName);
1878 std::swap(original, module);
1882 LLVM_DEBUG(llvm::dbgs() <<
"- Replace " << moduleName <<
" with "
1883 << originalName <<
"\n");
1884 dedupMap[moduleName] = originalName;
1885 deduper.dedup(original, module);
1887 anythingChanged =
true;
1891 deduper.record(module);
1893 dedupMap[moduleName] = moduleName;
1895 moduleInfoToModule[&moduleInfo] =
module;
1903 auto failed =
false;
1905 auto parseModule = [&](Attribute path) -> StringAttr {
1908 auto [_, rhs] = cast<StringAttr>(path).getValue().split(
'|');
1909 return StringAttr::get(
context, rhs);
1914 auto getLead = [&](StringAttr module) -> StringAttr {
1915 auto it = dedupMap.find(module);
1916 if (it == dedupMap.end()) {
1917 auto diag = emitError(circuit.getLoc(),
1918 "MustDeduplicateAnnotation references module ")
1919 <<
module << " which does not exist";
1926 LLVM_DEBUG(llvm::dbgs() <<
"Update annotations\n");
1928 if (!annotation.
isClass(mustDeduplicateAnnoClass))
1930 auto modules = annotation.
getMember<ArrayAttr>(
"modules");
1932 emitError(circuit.getLoc(),
1933 "MustDeduplicateAnnotation missing \"modules\" member");
1938 if (modules.empty())
1941 auto firstModule = parseModule(modules[0]);
1942 auto firstLead = getLead(firstModule);
1946 for (
auto attr : modules.getValue().drop_front()) {
1947 auto nextModule = parseModule(attr);
1948 auto nextLead = getLead(nextModule);
1951 if (firstLead != nextLead) {
1952 auto diag = emitError(circuit.getLoc(),
"module ")
1953 << nextModule <<
" not deduplicated with " << firstModule;
1956 equiv.check(diag, a, b);
1964 return signalPassFailure();
1967 for (
auto module : circuit.getOps<FModuleLike>())
1968 module->removeDiscardableAttr(dedupGroupAttrName);
1974 markAnalysesPreserved<NLATable>();
1975 if (!anythingChanged)
1976 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.
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 ModuleInfoRef getTombstoneKey()
static ModuleInfoRef getEmptyKey()
static unsigned getHashValue(const ModuleInfoRef &ref)