25#include "mlir/IR/IRMapping.h" 
   26#include "mlir/IR/Threading.h" 
   27#include "mlir/Pass/Pass.h" 
   28#include "llvm/ADT/DenseMap.h" 
   29#include "llvm/ADT/DenseMapInfo.h" 
   30#include "llvm/ADT/Hashing.h" 
   31#include "llvm/ADT/PostOrderIterator.h" 
   32#include "llvm/ADT/SmallPtrSet.h" 
   33#include "llvm/Support/Debug.h" 
   34#include "llvm/Support/Format.h" 
   35#include "llvm/Support/SHA256.h" 
   37#define DEBUG_TYPE "firrtl-dedup" 
   41#define GEN_PASS_DEF_DEDUP 
   42#include "circt/Dialect/FIRRTL/Passes.h.inc" 
   47using namespace firrtl;
 
   48using hw::InnerRefAttr;
 
   57  if (!symbol.isPrivate())
 
   62  if (isa<ClassLike>(*symbol))
 
   67  if (!symbol.canDiscardOnUseEmpty())
 
 
  132      : constants(constants) {}
 
 
  150    for (
auto [index, innerSym] : llvm::enumerate(module.getPortSymbols())) {
 
  151      for (
auto prop : cast<hw::InnerSymAttr>(innerSym))
 
  155    size_t index = 
module.getNumPorts();
 
  156    module.walk([&](hw::InnerSymbolOpInterface innerSymOp) {
 
  157      if (auto innerSym = innerSymOp.getInnerSymAttr()) {
 
  158        for (auto prop : innerSym)
 
  159          innerSymIDTable[prop.getName()] = std::pair(index, prop.getFieldID());
 
 
  167    auto [it, inserted] = idTable.try_emplace(
object, nextID);
 
 
  175    auto it = idTable.find(
object);
 
  176    if (it == idTable.end())
 
  178    auto id = it->second;
 
 
  184    return innerSymIDTable.at(name);
 
 
  188    auto value = operand.get();
 
  189    if (
auto result = dyn_cast<OpResult>(value)) {
 
  190      auto *op = result.getOwner();
 
  192      update(result.getResultNumber());
 
  195    if (
auto argument = dyn_cast<BlockArgument>(value)) {
 
  196      auto *block = argument.getOwner();
 
  197      update(getID(block));
 
  198      update(argument.getArgNumber());
 
  201    llvm_unreachable(
"Unknown value type");
 
 
  205    auto *
addr = 
reinterpret_cast<const uint8_t *
>(&pointer);
 
  206    sha.update(ArrayRef<uint8_t>(
addr, 
sizeof pointer));
 
 
  210    auto *
addr = 
reinterpret_cast<const uint8_t *
>(&value);
 
  211    sha.update(ArrayRef<uint8_t>(
addr, 
sizeof value));
 
 
  214  template <
typename T, 
typename U>
 
  215  void update(
const std::pair<T, U> &pair) {
 
 
  224    update(type.getTypeID());
 
  225    for (
auto &element : type.getElements()) {
 
  226      update(element.isFlip);
 
  227      update(element.type);
 
 
  233    update(type.getTypeID());
 
  237    hasSeenSymbol = 
true;
 
  238    referredModuleNames.push_back(type.getNameAttr().getAttr());
 
  239    for (
auto &element : type.getElements()) {
 
  240      update(element.name.getAsOpaquePointer());
 
  241      update(element.type);
 
  242      update(
static_cast<unsigned>(element.direction));
 
 
  248    if (
auto bundle = type_dyn_cast<BundleType>(type))
 
  249      return update(bundle);
 
  250    if (
auto klass = type_dyn_cast<ClassType>(type))
 
  251      return update(klass);
 
  252    update(type.getAsOpaquePointer());
 
 
  259    if (
auto objectOp = dyn_cast<ObjectOp>(result.getOwner())) {
 
  260      hasSeenSymbol = 
true;
 
  261      referredModuleNames.push_back(objectOp.getType().getNameAttr().getAttr());
 
  265    update(result.getType());
 
 
  270  void update(Operation *op, DictionaryAttr dict) {
 
  271    for (
auto namedAttr : dict) {
 
  272      auto name = namedAttr.getName();
 
  273      auto value = namedAttr.getValue();
 
  277      value.walk([&](FlatSymbolRefAttr) { hasSeenSymbol = 
true; });
 
  281      bool isClassPortNames =
 
  282          isa<ClassLike>(op) && name == constants.portNamesAttr;
 
  283      if (constants.nonessentialAttributes.contains(name) && !isClassPortNames)
 
  287      update(name.getAsOpaquePointer());
 
  290      if (name == constants.portTypesAttr) {
 
  291        auto portTypes = cast<ArrayAttr>(value).getAsValueRange<TypeAttr>();
 
  292        for (
auto type : portTypes)
 
  302      if (isa<InstanceOp>(op) && name == constants.moduleNameAttr) {
 
  303        referredModuleNames.push_back(cast<FlatSymbolRefAttr>(value).getAttr());
 
  309      if (isa<DistinctAttr>(value))
 
  313      if (
auto innerRef = dyn_cast<hw::InnerRefAttr>(value)) {
 
  314        update(getInnerSymID(innerRef.getName()));
 
  320      update(value.getAsOpaquePointer());
 
 
  326    update(name.getAsOpaquePointer());
 
 
  331    for (
auto &op : llvm::reverse(*block))
 
  333    for (
auto type : block->getArgumentTypes())
 
  335    update(finalizeID(block));
 
 
  342    for (
auto &block : llvm::reverse(region->getBlocks()))
 
 
  352    update(op->getNumRegions());
 
  353    for (
auto ®ion : reverse(op->getRegions()))
 
  356    update(op->getName());
 
  359    for (
auto &operand : op->getOpOperands())
 
  364    hasSeenSymbol = 
false;
 
  365    update(op, op->getAttrDictionary());
 
  368    for (
auto result : op->getResults())
 
  374      symbolSensitiveOps.push_back(op);
 
  377    update(finalizeID(op));
 
 
  404  bool hasSeenSymbol = 
false;
 
 
  435    return llvm::hash_combine(
 
 
  441    auto *
empty = getEmptyKey().info;
 
  442    auto *tombstone = getTombstoneKey().info;
 
  444        rhs.
info == tombstone)
 
 
 
  482    SmallString<64> buffer;
 
  483    llvm::raw_svector_ostream os(buffer);
 
  484    if (
auto integerAttr = dyn_cast<IntegerAttr>(attr)) {
 
  486      if (integerAttr.getType().isSignlessInteger())
 
  487        integerAttr.getValue().toStringUnsigned(buffer, 16);
 
  489        integerAttr.getAPSInt().toString(buffer, 16);
 
  493    return std::string(buffer);
 
 
  497  LogicalResult 
check(InFlightDiagnostic &diag, 
const Twine &message,
 
  498                      Operation *a, BundleType aType, Operation *b,
 
  500    if (aType.getNumElements() != bType.getNumElements()) {
 
  501      diag.attachNote(a->getLoc())
 
  502          << message << 
" bundle type has different number of elements";
 
  503      diag.attachNote(b->getLoc()) << 
"second operation here";
 
  507    for (
auto elementPair :
 
  508         llvm::zip(aType.getElements(), bType.getElements())) {
 
  509      auto aElement = std::get<0>(elementPair);
 
  510      auto bElement = std::get<1>(elementPair);
 
  511      if (aElement.isFlip != bElement.isFlip) {
 
  512        diag.attachNote(a->getLoc()) << message << 
" bundle element " 
  513                                     << aElement.name << 
" flip does not match";
 
  514        diag.attachNote(b->getLoc()) << 
"second operation here";
 
  518      if (failed(
check(diag,
 
  519                       "bundle element \'" + aElement.name.getValue() + 
"'", a,
 
  520                       aElement.type, b, bElement.type)))
 
 
  526  LogicalResult 
check(InFlightDiagnostic &diag, 
const Twine &message,
 
  527                      Operation *a, Type aType, Operation *b, Type bType) {
 
  530    if (
auto aBundleType = type_dyn_cast<BundleType>(aType))
 
  531      if (
auto bBundleType = type_dyn_cast<BundleType>(bType))
 
  532        return check(diag, message, a, aBundleType, b, bBundleType);
 
  533    if (type_isa<RefType>(aType) && type_isa<RefType>(bType) &&
 
  535      diag.attachNote(a->getLoc())
 
  536          << message << 
", has a RefType with a different base type " 
  537          << type_cast<RefType>(aType).getType()
 
  538          << 
" in the same position of the two modules marked as 'must dedup'. " 
  539             "(This may be due to Grand Central Taps or Views being different " 
  540             "between the two modules.)";
 
  541      diag.attachNote(b->getLoc())
 
  542          << 
"the second module has a different base type " 
  543          << type_cast<RefType>(bType).getType();
 
  546    diag.attachNote(a->getLoc())
 
  547        << message << 
" types don't match, first type is " << aType;
 
  548    diag.attachNote(b->getLoc()) << 
"second type is " << bType;
 
 
  553                      Block &aBlock, Operation *b, Block &bBlock) {
 
  556    auto portNames = a->getAttrOfType<ArrayAttr>(
"portNames");
 
  558    auto emitMissingPort = [&](Value existsVal, Operation *opExists,
 
  559                               Operation *opDoesNotExist) {
 
  561      auto portNames = opExists->getAttrOfType<ArrayAttr>(
"portNames");
 
  563        if (
auto portNameAttr = dyn_cast<StringAttr>(portNames[portNo]))
 
  564          portName = portNameAttr.getValue();
 
  565      if (type_isa<RefType>(existsVal.getType())) {
 
  566        diag.attachNote(opExists->getLoc())
 
  567            << 
" contains a RefType port named '" + portName +
 
  568                   "' that only exists in one of the modules (can be due to " 
  569                   "difference in Grand Central Tap or View of two modules " 
  570                   "marked with must dedup)";
 
  571        diag.attachNote(opDoesNotExist->getLoc())
 
  572            << 
"second module to be deduped that does not have the RefType " 
  575        diag.attachNote(opExists->getLoc())
 
  576            << 
"port '" + portName + 
"' only exists in one of the modules";
 
  577        diag.attachNote(opDoesNotExist->getLoc())
 
  578            << 
"second module to be deduped that does not have the port";
 
  584         llvm::zip_longest(aBlock.getArguments(), bBlock.getArguments())) {
 
  585      auto &aArg = std::get<0>(argPair);
 
  586      auto &bArg = std::get<1>(argPair);
 
  587      if (aArg.has_value() && bArg.has_value()) {
 
  592          if (
auto portNameAttr = dyn_cast<StringAttr>(portNames[portNo]))
 
  593            portName = portNameAttr.getValue();
 
  596        if (failed(
check(diag, 
"module port '" + portName + 
"'", a,
 
  597                         aArg->getType(), b, bArg->getType())))
 
  599        data.map.map(aArg.value(), bArg.value());
 
  603      if (!aArg.has_value())
 
  605      return emitMissingPort(aArg.has_value() ? aArg.value() : bArg.value(), a,
 
  610    auto aIt = aBlock.begin();
 
  611    auto aEnd = aBlock.end();
 
  612    auto bIt = bBlock.begin();
 
  613    auto bEnd = bBlock.end();
 
  614    while (aIt != aEnd && bIt != bEnd)
 
  615      if (failed(
check(diag, 
data, &*aIt++, &*bIt++)))
 
  618      diag.attachNote(aIt->getLoc()) << 
"first block has more operations";
 
  619      diag.attachNote(b->getLoc()) << 
"second block here";
 
  623      diag.attachNote(bIt->getLoc()) << 
"second block has more operations";
 
  624      diag.attachNote(a->getLoc()) << 
"first block here";
 
 
  631                      Region &aRegion, Operation *b, Region &bRegion) {
 
  632    auto aIt = aRegion.begin();
 
  633    auto aEnd = aRegion.end();
 
  634    auto bIt = bRegion.begin();
 
  635    auto bEnd = bRegion.end();
 
  638    while (aIt != aEnd && bIt != bEnd)
 
  639      if (failed(
check(diag, 
data, a, *aIt++, b, *bIt++)))
 
  641    if (aIt != aEnd || bIt != bEnd) {
 
  642      diag.attachNote(a->getLoc())
 
  643          << 
"operation regions have different number of blocks";
 
  644      diag.attachNote(b->getLoc()) << 
"second operation here";
 
 
  650  LogicalResult 
check(InFlightDiagnostic &diag, Operation *a,
 
  651                      mlir::DenseBoolArrayAttr aAttr, Operation *b,
 
  652                      mlir::DenseBoolArrayAttr bAttr) {
 
  655    auto portNames = a->getAttrOfType<ArrayAttr>(
"portNames");
 
  656    for (
unsigned i = 0, e = aAttr.size(); i < e; ++i) {
 
  657      auto aDirection = aAttr[i];
 
  658      auto bDirection = bAttr[i];
 
  659      if (aDirection != bDirection) {
 
  660        auto ¬e = diag.attachNote(a->getLoc()) << 
"module port ";
 
  662          note << 
"'" << cast<StringAttr>(portNames[i]).getValue() << 
"'";
 
  665        note << 
" directions don't match, first direction is '" 
  667        diag.attachNote(b->getLoc()) << 
"second direction is '" 
 
  676                      DictionaryAttr aDict, Operation *b,
 
  677                      DictionaryAttr bDict) {
 
  682    DenseSet<Attribute> seenAttrs;
 
  683    for (
auto namedAttr : aDict) {
 
  684      auto attrName = namedAttr.getName();
 
  688      auto aAttr = namedAttr.getValue();
 
  689      auto bAttr = bDict.get(attrName);
 
  691        diag.attachNote(a->getLoc())
 
  692            << 
"second operation is missing attribute " << attrName;
 
  693        diag.attachNote(b->getLoc()) << 
"second operation here";
 
  697      if (isa<hw::InnerRefAttr>(aAttr) && isa<hw::InnerRefAttr>(bAttr)) {
 
  698        auto bRef = cast<hw::InnerRefAttr>(bAttr);
 
  699        auto aRef = cast<hw::InnerRefAttr>(aAttr);
 
  701        auto aTarget = 
data.a.lookup(aRef.getName());
 
  702        auto bTarget = 
data.b.lookup(bRef.getName());
 
  703        if (!aTarget || !bTarget)
 
  704          diag.attachNote(a->getLoc())
 
  705              << 
"malformed ir, possibly violating use-before-def";
 
  707          diag.attachNote(a->getLoc())
 
  708              << 
"operations have different targets, first operation has " 
  710          diag.attachNote(b->getLoc()) << 
"second operation has " << bTarget;
 
  713        if (aTarget.isPort()) {
 
  715          if (!bTarget.isPort() || aTarget.getPort() != bTarget.getPort())
 
  719          if (!bTarget.isOpOnly() ||
 
  720              data.map.lookupOrNull(aTarget.getOp()) != bTarget.getOp())
 
  723        if (aTarget.getField() != bTarget.getField())
 
  728        if (failed(
check(diag, a, cast<mlir::DenseBoolArrayAttr>(aAttr), b,
 
  729                         cast<mlir::DenseBoolArrayAttr>(bAttr))))
 
  731      } 
else if (isa<DistinctAttr>(aAttr) && isa<DistinctAttr>(bAttr)) {
 
  734      } 
else if (aAttr != bAttr) {
 
  735        diag.attachNote(a->getLoc())
 
  736            << 
"first operation has attribute '" << attrName.getValue()
 
  738        diag.attachNote(b->getLoc())
 
  739            << 
"second operation has value " << 
prettyPrint(bAttr);
 
  742      seenAttrs.insert(attrName);
 
  744    if (aDict.getValue().size() != bDict.getValue().size()) {
 
  745      for (
auto namedAttr : bDict) {
 
  746        auto attrName = namedAttr.getName();
 
  750            seenAttrs.contains(attrName))
 
  753        diag.attachNote(a->getLoc())
 
  754            << 
"first operation is missing attribute " << attrName;
 
  755        diag.attachNote(b->getLoc()) << 
"second operation here";
 
 
  763  LogicalResult 
check(InFlightDiagnostic &diag, FInstanceLike a,
 
  765    auto aName = a.getReferencedModuleNameAttr();
 
  766    auto bName = b.getReferencedModuleNameAttr();
 
  776    diag.attachNote(std::nullopt)
 
  777        << 
"in instance " << a.getInstanceNameAttr() << 
" of " << aName
 
  778        << 
", and instance " << b.getInstanceNameAttr() << 
" of " << bName;
 
  779    check(diag, aModule, bModule);
 
 
  787    if (a->getName() != b->getName()) {
 
  788      diag.attachNote(a->getLoc()) << 
"first operation is a " << a->getName();
 
  789      diag.attachNote(b->getLoc()) << 
"second operation is a " << b->getName();
 
  795    if (
auto aInst = dyn_cast<FInstanceLike>(a)) {
 
  796      auto bInst = cast<FInstanceLike>(b);
 
  797      if (failed(
check(diag, aInst, bInst)))
 
  802    if (a->getNumResults() != b->getNumResults()) {
 
  803      diag.attachNote(a->getLoc())
 
  804          << 
"operations have different number of results";
 
  805      diag.attachNote(b->getLoc()) << 
"second operation here";
 
  808    for (
auto resultPair : llvm::zip(a->getResults(), b->getResults())) {
 
  809      auto &aValue = std::get<0>(resultPair);
 
  810      auto &bValue = std::get<1>(resultPair);
 
  811      if (failed(
check(diag, 
"operation result", a, aValue.getType(), b,
 
  814      data.map.map(aValue, bValue);
 
  818    if (a->getNumOperands() != b->getNumOperands()) {
 
  819      diag.attachNote(a->getLoc())
 
  820          << 
"operations have different number of operands";
 
  821      diag.attachNote(b->getLoc()) << 
"second operation here";
 
  824    for (
auto operandPair : llvm::zip(a->getOperands(), b->getOperands())) {
 
  825      auto &aValue = std::get<0>(operandPair);
 
  826      auto &bValue = std::get<1>(operandPair);
 
  827      if (bValue != 
data.map.lookup(aValue)) {
 
  828        diag.attachNote(a->getLoc())
 
  829            << 
"operations use different operands, first operand is '" 
  834        diag.attachNote(b->getLoc())
 
  835            << 
"second operand is '" 
  839            << 
"', but should have been '" 
  850    if (a->getNumRegions() != b->getNumRegions()) {
 
  851      diag.attachNote(a->getLoc())
 
  852          << 
"operations have different number of regions";
 
  853      diag.attachNote(b->getLoc()) << 
"second operation here";
 
  856    for (
auto regionPair : llvm::zip(a->getRegions(), b->getRegions())) {
 
  857      auto &aRegion = std::get<0>(regionPair);
 
  858      auto &bRegion = std::get<1>(regionPair);
 
  859      if (failed(
check(diag, 
data, a, aRegion, b, bRegion)))
 
  864    if (failed(
check(diag, 
data, a, a->getAttrDictionary(), b,
 
  865                     b->getAttrDictionary())))
 
 
  871  void check(InFlightDiagnostic &diag, Operation *a, Operation *b) {
 
  876      diag.attachNote(a->getLoc()) << 
"module marked NoDedup";
 
  880      diag.attachNote(b->getLoc()) << 
"module marked NoDedup";
 
  883    auto aSymbol = cast<mlir::SymbolOpInterface>(a);
 
  884    auto bSymbol = cast<mlir::SymbolOpInterface>(b);
 
  886      diag.attachNote(a->getLoc())
 
  888          << (aSymbol.isPrivate() ? 
"private but not discardable" : 
"public");
 
  889      diag.attachNote(b->getLoc())
 
  891          << (bSymbol.isPrivate() ? 
"private but not discardable" : 
"public");
 
  896    auto bGroup = dyn_cast_or_null<StringAttr>(
 
  898    if (aGroup != bGroup) {
 
  900        diag.attachNote(b->getLoc())
 
  901            << 
"module is in dedup group '" << bGroup.str() << 
"'";
 
  903        diag.attachNote(b->getLoc()) << 
"module is not part of a dedup group";
 
  906        diag.attachNote(a->getLoc())
 
  907            << 
"module is in dedup group '" << aGroup.str() << 
"'";
 
  909        diag.attachNote(a->getLoc()) << 
"module is not part of a dedup group";
 
  915    diag.attachNote(a->getLoc()) << 
"first module here";
 
  916    diag.attachNote(b->getLoc()) << 
"second module here";
 
 
 
  940static Location 
mergeLoc(MLIRContext *context, Location to, Location from) {
 
  942  llvm::SmallSetVector<Location, 4> decomposedLocs;
 
  944  unsigned seenFIR = 0;
 
  945  for (
auto loc : {to, from}) {
 
  948    if (
auto fusedLoc = dyn_cast<FusedLoc>(loc)) {
 
  951      for (
auto loc : fusedLoc.getLocations()) {
 
  952        if (FileLineColLoc fileLoc = dyn_cast<FileLineColLoc>(loc)) {
 
  953          if (fileLoc.getFilename().strref().ends_with(
".fir")) {
 
  959        decomposedLocs.insert(loc);
 
  965    if (FileLineColLoc fileLoc = dyn_cast<FileLineColLoc>(loc)) {
 
  966      if (fileLoc.getFilename().strref().ends_with(
".fir")) {
 
  973    if (!isa<UnknownLoc>(loc))
 
  974      decomposedLocs.insert(loc);
 
  977  auto locs = decomposedLocs.getArrayRef();
 
  982    return UnknownLoc::get(context);
 
  983  if (locs.size() == 1)
 
  986  return FusedLoc::get(context, locs);
 
 
 1001    for (
auto nla : circuit.getOps<hw::HierPathOp>())
 
 1002      nlaCache[nla.getNamepathAttr()] = nla.getSymNameAttr();
 
 
 1009  void dedup(FModuleLike toModule, FModuleLike fromModule) {
 
 1015    SmallVector<Attribute> newLocs;
 
 1016    for (
auto [toLoc, fromLoc] : llvm::zip(toModule.getPortLocations(),
 
 1017                                           fromModule.getPortLocations())) {
 
 1018      if (toLoc == fromLoc)
 
 1019        newLocs.push_back(toLoc);
 
 1022                                   cast<LocationAttr>(fromLoc)));
 
 1024    toModule->setAttr(
"portLocations", ArrayAttr::get(
context, newLocs));
 
 1027    mergeOps(renameMap, toModule, toModule, fromModule, fromModule);
 
 1033    if (
auto to = dyn_cast<FModuleOp>(*toModule))
 
 1037                           fromModule.getModuleNameAttr());
 
 
 1048    for (
unsigned i = 0, e = 
getNumPorts(module); i < e; ++i)
 
 1051    module->walk([&](Operation *op) { recordAnnotations(op); });
 
 
 1057    return moduleNamespaces.try_emplace(module, cast<FModuleLike>(module))
 
 
 1065      if (
auto nlaRef = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal"))
 
 1066        targetMap[nlaRef.getAttr()].insert(target);
 
 
 1075    auto mem = dyn_cast<MemOp>(op);
 
 1080    for (
unsigned i = 0, e = mem->getNumResults(); i < e; ++i)
 
 
 1089        instanceGraph[::cast<igraph::ModuleOpInterface>(fromModule)];
 
 1090    auto *toNode = instanceGraph[toModule];
 
 1091    auto toModuleRef = FlatSymbolRefAttr::get(toModule.getModuleNameAttr());
 
 1092    for (
auto *oldInstRec : llvm::make_early_inc_range(fromNode->uses())) {
 
 1093      auto inst = oldInstRec->getInstance();
 
 1094      if (
auto instOp = dyn_cast<InstanceOp>(*inst)) {
 
 1095        instOp.setModuleNameAttr(toModuleRef);
 
 1096        instOp.setPortNamesAttr(toModule.getPortNamesAttr());
 
 1097      } 
else if (
auto objectOp = dyn_cast<ObjectOp>(*inst)) {
 
 1098        auto classLike = cast<ClassLike>(*toNode->getModule());
 
 1099        ClassType classType = detail::getInstanceTypeForClassLike(classLike);
 
 1100        objectOp.getResult().setType(classType);
 
 1102      oldInstRec->getParent()->addInstance(inst, toNode);
 
 1103      oldInstRec->erase();
 
 1105    instanceGraph.erase(fromNode);
 
 1106    fromModule->erase();
 
 
 1114  SmallVector<FlatSymbolRefAttr>
 
 1115  createNLAs(Operation *fromModule, ArrayRef<Attribute> baseNamepath,
 
 1116             SymbolTable::Visibility vis = SymbolTable::Visibility::Private) {
 
 1119    SmallVector<Attribute> namepath = {
nullptr};
 
 1120    namepath.append(baseNamepath.begin(), baseNamepath.end());
 
 1122    auto loc = fromModule->getLoc();
 
 1123    auto *fromNode = instanceGraph[cast<igraph::ModuleOpInterface>(fromModule)];
 
 1124    SmallVector<FlatSymbolRefAttr> nlas;
 
 1125    for (
auto *instanceRecord : fromNode->uses()) {
 
 1126      auto parent = cast<FModuleOp>(*instanceRecord->getParent()->getModule());
 
 1127      auto inst = instanceRecord->getInstance();
 
 1129      auto arrayAttr = ArrayAttr::get(context, namepath);
 
 1131      auto &cacheEntry = nlaCache[arrayAttr];
 
 1133        auto builder = OpBuilder::atBlockBegin(nlaBlock);
 
 1134        auto nla = hw::HierPathOp::create(builder, loc, 
"nla", arrayAttr);
 
 1136        symbolTable.insert(nla);
 
 1138        cacheEntry = nla.getNameAttr();
 
 1139        nla.setVisibility(vis);
 
 1140        nlaTable->addNLA(nla);
 
 1142      auto nlaRef = FlatSymbolRefAttr::get(cast<StringAttr>(cacheEntry));
 
 1143      nlas.push_back(nlaRef);
 
 
 1151  SmallVector<FlatSymbolRefAttr>
 
 1153             SymbolTable::Visibility vis = SymbolTable::Visibility::Private) {
 
 1154    return createNLAs(fromModule, FlatSymbolRefAttr::get(toModuleName), vis);
 
 
 1161                       Annotation anno, ArrayRef<NamedAttribute> attributes,
 
 1162                       unsigned nonLocalIndex,
 
 1163                       SmallVectorImpl<Annotation> &newAnnotations) {
 
 1164    SmallVector<NamedAttribute> mutableAttributes(attributes.begin(),
 
 1166    for (
auto &nla : nlas) {
 
 1168      mutableAttributes[nonLocalIndex].setValue(nla);
 
 1169      auto dict = DictionaryAttr::getWithSorted(context, mutableAttributes);
 
 1172      newAnnotations.push_back(anno);
 
 
 1181    targetMap.erase(nla.getNameAttr());
 
 1182    nlaTable->erase(nla);
 
 1183    nlaCache.erase(nla.getNamepathAttr());
 
 1184    symbolTable.erase(nla);
 
 
 1190                            FModuleOp fromModule) {
 
 1191    auto toName = toModule.getNameAttr();
 
 1192    auto fromName = fromModule.getNameAttr();
 
 1195    auto moduleNLAs = nlaTable->lookup(fromModule.getNameAttr()).vec();
 
 1197    nlaTable->renameModuleAndInnerRef(toName, fromName, renameMap);
 
 1200    for (
auto nla : moduleNLAs) {
 
 1201      auto elements = nla.getNamepath().getValue();
 
 1203      if (nla.root() != toName)
 
 1206      SmallVector<Attribute> namepath(elements.begin(), elements.end());
 
 1207      auto nlaRefs = createNLAs(fromModule, namepath, nla.getVisibility());
 
 1209      auto &set = targetMap[nla.getSymNameAttr()];
 
 1210      SmallVector<AnnoTarget> targets(set.begin(), set.end());
 
 1212      for (
auto target : targets) {
 
 1215        SmallVector<Annotation> newAnnotations;
 
 1216        for (
auto anno : target.getAnnotations()) {
 
 1218          auto [it, found] = mlir::impl::findAttrSorted(
 
 1219              anno.begin(), anno.end(), nonLocalString);
 
 1222          if (!found || cast<FlatSymbolRefAttr>(it->getValue()).getAttr() !=
 
 1223                            nla.getSymNameAttr()) {
 
 1224            newAnnotations.push_back(anno);
 
 1227          auto nonLocalIndex = std::distance(anno.begin(), it);
 
 1229          cloneAnnotation(nlaRefs, anno,
 
 1230                          ArrayRef<NamedAttribute>(anno.begin(), anno.end()),
 
 1231                          nonLocalIndex, newAnnotations);
 
 1236        target.setAnnotations(annotations);
 
 1238        for (
auto nla : nlaRefs)
 
 1239          targetMap[nla.getAttr()].insert(target);
 
 
 1251                         FModuleOp fromModule) {
 
 1252    addAnnotationContext(renameMap, toModule, toModule);
 
 1253    addAnnotationContext(renameMap, toModule, fromModule);
 
 
 1259                            StringAttr fromName) {
 
 1260    nlaTable->renameModuleAndInnerRef(toName, fromName, renameMap);
 
 
 1268                              SmallVectorImpl<Annotation> &newAnnotations) {
 
 1271    SmallVector<NamedAttribute> attributes;
 
 1272    int nonLocalIndex = -1;
 
 1273    for (
const auto &val : llvm::enumerate(anno)) {
 
 1274      auto attr = val.value();
 
 1276      auto compare = attr.getName().compare(nonLocalString);
 
 1277      assert(compare != 0 && 
"should not pass non-local annotations here");
 
 1281        nonLocalIndex = val.index();
 
 1282        attributes.push_back(NamedAttribute(nonLocalString, nonLocalString));
 
 1287      attributes.push_back(attr);
 
 1289    if (nonLocalIndex == -1) {
 
 1291      nonLocalIndex = attributes.size();
 
 1292      attributes.push_back(NamedAttribute(nonLocalString, nonLocalString));
 
 1295      attributes.append(anno.
begin() + nonLocalIndex, anno.
end());
 
 1299    auto nlaRefs = createNLAs(toModuleName, fromModule);
 
 1300    for (
auto nla : nlaRefs)
 
 1301      targetMap[nla.getAttr()].insert(to);
 
 1304    cloneAnnotation(nlaRefs, anno, attributes, nonLocalIndex, newAnnotations);
 
 
 1310                       SmallVectorImpl<Annotation> &newAnnotations,
 
 1311                       SmallPtrSetImpl<Attribute> &dontTouches) {
 
 1312    for (
auto anno : annos) {
 
 1316        anno.removeMember(
"circt.nonlocal");
 
 1317        auto [it, inserted] = dontTouches.insert(anno.getAttr());
 
 1319          newAnnotations.push_back(anno);
 
 1324      if (
auto nla = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
 
 1325        newAnnotations.push_back(anno);
 
 1326        targetMap[nla.getAttr()].insert(to);
 
 1330      makeAnnotationNonLocal(toModule.getModuleNameAttr(), to, fromModule, anno,
 
 
 1341    SmallVector<Annotation> newAnnotations;
 
 1345    llvm::SmallPtrSet<Attribute, 4> dontTouches;
 
 1349    copyAnnotations(toModule, to, toModule, toAnnos, newAnnotations,
 
 1351    copyAnnotations(toModule, to, fromModule, fromAnnos, newAnnotations,
 
 1355    if (!newAnnotations.empty())
 
 
 1361                        FModuleLike fromModule, Operation *from) {
 
 1367    if (toModule == to) {
 
 1369      for (
unsigned i = 0, e = 
getNumPorts(toModule); i < e; ++i)
 
 1374    } 
else if (
auto toMem = dyn_cast<MemOp>(to)) {
 
 1376      auto fromMem = cast<MemOp>(from);
 
 1377      for (
unsigned i = 0, e = toMem.getNumResults(); i < e; ++i)
 
 
 1386                                     hw::InnerSymAttr toSym,
 
 1387                                     hw::InnerSymAttr fromSym) {
 
 1388    if (fromSym && !fromSym.getProps().empty()) {
 
 1389      auto &isn = getNamespace(toModule);
 
 1391      SmallVector<hw::InnerSymPropertiesAttr> newProps;
 
 1394        llvm::append_range(newProps, toSym);
 
 1396      for (
auto fromProp : fromSym) {
 
 1397        hw::InnerSymPropertiesAttr newProp;
 
 1398        auto *it = llvm::find_if(newProps, [&](
auto p) {
 
 1399          return p.getFieldID() == fromProp.getFieldID();
 
 1401        if (it != newProps.end()) {
 
 1406          if (fromProp.getSymVisibility().getValue() == 
"public" &&
 
 1407              newProp.getSymVisibility().getValue() != 
"public") {
 
 1408            *it = hw::InnerSymPropertiesAttr::get(context, newProp.getName(),
 
 1409                                                  newProp.getFieldID(),
 
 1410                                                  fromProp.getSymVisibility());
 
 1414          auto newName = isn.newName(fromProp.getName().getValue());
 
 1415          newProp = hw::InnerSymPropertiesAttr::get(
 
 1416              context, StringAttr::get(context, newName), fromProp.getFieldID(),
 
 1417              fromProp.getSymVisibility());
 
 1418          newProps.push_back(newProp);
 
 1420        renameMap[fromProp.getName()] = newProp.getName();
 
 1423      llvm::sort(newProps, [](
auto &p, 
auto &q) {
 
 1424        return p.getFieldID() < q.getFieldID();
 
 1427      return hw::InnerSymAttr::get(context, newProps);
 
 1429    return hw::InnerSymAttr();
 
 
 1436                        Operation *to, FModuleLike fromModule,
 
 1440    if (
auto fromInnerSym = dyn_cast<hw::InnerSymbolOpInterface>(from)) {
 
 1441      auto toInnerSym = cast<hw::InnerSymbolOpInterface>(to);
 
 1442      if (
auto newSymAttr = mergeInnerSymbols(renameMap, toModule,
 
 1443                                              toInnerSym.getInnerSymAttr(),
 
 1444                                              fromInnerSym.getInnerSymAttr()))
 
 1445        toInnerSym.setInnerSymbolAttr(newSymAttr);
 
 1449    auto fromPortSyms = from->getAttrOfType<ArrayAttr>(
"portSymbols");
 
 1450    if (!fromPortSyms || fromPortSyms.empty())
 
 1453    auto portCount = fromPortSyms.size();
 
 1454    auto toPortSyms = to->getAttrOfType<ArrayAttr>(
"portSymbols");
 
 1458    SmallVector<Attribute> newPortSyms;
 
 1459    if (toPortSyms.empty())
 
 1460      newPortSyms.assign(portCount, hw::InnerSymAttr());
 
 1462      newPortSyms.assign(toPortSyms.begin(), toPortSyms.end());
 
 1464    for (
unsigned portNo = 0; portNo < portCount; ++portNo) {
 
 1465      if (
auto newPortSym = mergeInnerSymbols(
 
 1466              renameMap, toModule,
 
 1467              llvm::cast_if_present<hw::InnerSymAttr>(newPortSyms[portNo]),
 
 1468              cast<hw::InnerSymAttr>(fromPortSyms[portNo]))) {
 
 1469        newPortSyms[portNo] = newPortSym;
 
 1474    FModuleLike::fixupPortSymsArray(newPortSyms, toModule.getContext());
 
 1475    cast<FModuleLike>(to).setPortSymbols(newPortSyms);
 
 
 1481                FModuleLike fromModule, Operation *from) {
 
 1483    if (to->getLoc() != from->getLoc())
 
 1484      to->setLoc(
mergeLoc(context, to->getLoc(), from->getLoc()));
 
 1487    for (
auto regions : llvm::zip(to->getRegions(), from->getRegions()))
 
 1488      mergeRegions(renameMap, toModule, std::get<0>(regions), fromModule,
 
 1489                   std::get<1>(regions));
 
 1492    recordSymRenames(renameMap, toModule, to, fromModule, from);
 
 1495    mergeAnnotations(toModule, to, fromModule, from);
 
 
 1500                   FModuleLike fromModule, Block &fromBlock) {
 
 1502    for (
auto [toArg, fromArg] :
 
 1503         llvm::zip(toBlock.getArguments(), fromBlock.getArguments()))
 
 1504      if (toArg.getLoc() != fromArg.getLoc())
 
 1505        toArg.setLoc(
mergeLoc(context, toArg.getLoc(), fromArg.getLoc()));
 
 1507    for (
auto ops : llvm::zip(toBlock, fromBlock))
 
 1508      mergeOps(renameMap, toModule, &std::get<0>(ops), fromModule,
 
 
 1514                    Region &toRegion, FModuleLike fromModule,
 
 1515                    Region &fromRegion) {
 
 1516    for (
auto blocks : llvm::zip(toRegion, fromRegion))
 
 1517      mergeBlocks(renameMap, toModule, std::get<0>(blocks), fromModule,
 
 1518                  std::get<1>(blocks));
 
 
 1532  DenseMap<Attribute, llvm::SmallDenseSet<AnnoTarget>> 
targetMap;
 
 
 1553static void fixupConnect(ImplicitLocOpBuilder &builder, Value dst, Value src) {
 
 1555  auto dstType = dst.getType();
 
 1556  auto srcType = src.getType();
 
 1557  if (dstType == srcType) {
 
 1563  auto dstBundle = type_cast<BundleType>(dstType);
 
 1564  auto srcBundle = type_cast<BundleType>(srcType);
 
 1565  for (
unsigned i = 0; i < dstBundle.getNumElements(); ++i) {
 
 1566    auto dstField = SubfieldOp::create(builder, dst, i);
 
 1567    auto srcField = SubfieldOp::create(builder, src, i);
 
 1568    if (dstBundle.getElement(i).isFlip) {
 
 1569      std::swap(srcBundle, dstBundle);
 
 1570      std::swap(srcField, dstField);
 
 
 1580                       const DenseMap<Attribute, StringAttr> &dedupMap) {
 
 1586  if (
auto instOp = dyn_cast<InstanceOp>(op)) {
 
 1587    ImplicitLocOpBuilder builder(instOp.getLoc(), instOp->getContext());
 
 1588    builder.setInsertionPointAfter(instOp);
 
 1589    auto module = instanceGraph.lookup(instOp.getModuleNameAttr().getAttr())
 
 1590                      ->getModule<FModuleLike>();
 
 1591    for (
auto [index, result] : llvm::enumerate(instOp.getResults())) {
 
 1592      auto newType = 
module.getPortType(index);
 
 1593      auto oldType = result.getType();
 
 1595      if (newType == oldType)
 
 1597      LLVM_DEBUG(llvm::dbgs()
 
 1598                 << 
"- Updating instance port \"" << instOp.getInstanceName()
 
 1599                 << 
"." << instOp.getPortName(index) << 
"\" from " << oldType
 
 1600                 << 
" to " << newType << 
"\n");
 
 1604      auto wire = WireOp::create(builder, oldType, instOp.getPortName(index))
 
 1606      result.replaceAllUsesWith(wire);
 
 1607      result.setType(newType);
 
 1608      if (instOp.getPortDirection(index) == Direction::Out)
 
 1617  mlir::AttrTypeReplacer replacer;
 
 1618  replacer.addReplacement([&](FlatSymbolRefAttr symRef) {
 
 1619    auto oldName = symRef.getAttr();
 
 1620    auto newName = dedupMap.lookup(oldName);
 
 1621    if (newName && newName != oldName) {
 
 1622      auto newSymRef = FlatSymbolRefAttr::get(newName);
 
 1623      LLVM_DEBUG(llvm::dbgs()
 
 1624                 << 
"- Updating " << symRef << 
" to " << newSymRef << 
" in " 
 1625                 << op->getName() << 
" at " << op->getLoc() << 
"\n");
 
 1632  op->setAttrs(cast<DictionaryAttr>(replacer.replace(op->getAttrDictionary())));
 
 1635  for (
auto ®ion : op->getRegions())
 
 1636    for (
auto &block : region)
 
 1637      for (
auto arg : block.getArguments())
 
 1638        arg.setType(replacer.replace(arg.getType()));
 
 1641  for (
auto result : op->getResults())
 
 1642    result.setType(replacer.replace(result.getType()));
 
 
 1649    const DenseMap<Operation *, ModuleInfoRef> &moduleToModuleInfo,
 
 1650    const DenseMap<Attribute, StringAttr> &dedupMap) {
 
 1651  for (
auto *node : instanceGraph) {
 
 1654    auto module = node->getModule<FModuleLike>();
 
 1655    auto it = moduleToModuleInfo.find(module);
 
 1656    if (it == moduleToModuleInfo.end())
 
 1660    auto &ops = it->second.info->symbolSensitiveOps;
 
 1663    LLVM_DEBUG(llvm::dbgs()
 
 1664               << 
"- Updating " << ops.size() << 
" symbol-sensitive ops in " 
 1665               << module.getNameAttr() << 
"\n");
 
 1666    for (
auto *op : ops)
 
 
 1676class DedupPass : 
public circt::firrtl::impl::DedupBase<DedupPass> {
 
 1677  using DedupBase::DedupBase;
 
 1679  void runOnOperation()
 override {
 
 1680    auto *context = &getContext();
 
 1681    auto circuit = getOperation();
 
 1682    auto &instanceGraph = getAnalysis<InstanceGraph>();
 
 1683    auto *nlaTable = &getAnalysis<NLATable>();
 
 1684    auto &symbolTable = getAnalysis<SymbolTable>();
 
 1685    Deduper deduper(instanceGraph, symbolTable, nlaTable, circuit);
 
 1687    auto anythingChanged = 
false;
 
 1689      llvm::dbgs() << 
"\n";
 
 1690      debugHeader(Twine(
"Dedup circuit \"") + circuit.getName() + 
"\"")
 
 1701    DenseMap<ModuleInfoRef, Operation *> moduleInfoToModule;
 
 1702    DenseMap<Operation *, ModuleInfoRef> moduleToModuleInfo;
 
 1707    DenseMap<Attribute, StringAttr> dedupMap;
 
 1712    SmallVector<FModuleLike, 0> modules;
 
 1714      if (
auto mod = dyn_cast<FModuleLike>(*node.getModule()))
 
 1715        modules.push_back(mod);
 
 1717    LLVM_DEBUG(llvm::dbgs() << 
"Found " << modules.size() << 
" modules\n");
 
 1719    SmallVector<std::optional<ModuleInfo>> moduleInfos(modules.size());
 
 1723    auto dedupGroupAttrName = StringAttr::get(context, 
"firrtl.dedup_group");
 
 1729    for (
auto module : modules) {
 
 1730      llvm::SmallSetVector<StringAttr, 1> groups;
 
 1732          module, [&groups, dedupGroupClass](
Annotation annotation) {
 
 1735            groups.insert(annotation.
getMember<StringAttr>(
"group"));
 
 1738      if (groups.size() > 1) {
 
 1739        module.emitError("module belongs to multiple dedup groups: ") << groups;
 
 1740        return signalPassFailure();
 
 1742      assert(!module->hasAttr(dedupGroupAttrName) &&
 
 1743             "unexpected existing use of temporary dedup group attribute");
 
 1744      if (!groups.empty())
 
 1745        module->setDiscardableAttr(dedupGroupAttrName, groups.front());
 
 1749    LLVM_DEBUG(llvm::dbgs() << 
"Computing module information\n");
 
 1750    auto result = mlir::failableParallelForEach(
 
 1751        context, llvm::seq(modules.size()), [&](
unsigned idx) {
 
 1752          auto module = modules[idx];
 
 1754          if (AnnotationSet::hasAnnotation(module, noDedupClass))
 
 1758          if (auto ext = dyn_cast<FExtModuleOp>(*module);
 
 1759              ext && !ext.getDefname().has_value())
 
 1763          if (isa<ClassOp>(*module) && !dedupClasses)
 
 1766          StructuralHasher hasher(hasherConstants);
 
 1768          moduleInfos[idx] = hasher.getModuleInfo(module);
 
 1774      auto &os = llvm::dbgs();
 
 1775      for (
auto [module, info] : 
llvm::zip(modules, moduleInfos)) {
 
 1778          os << llvm::format_bytes(
info->structuralHash, std::nullopt, 32, 32);
 
 1780          os << 
"--------------------------------";
 
 1781          os << 
"--------------------------------";
 
 1783        os << 
" for " << 
module.getModuleNameAttr() << "\n";
 
 1787    if (result.failed())
 
 1788      return signalPassFailure();
 
 1790    LLVM_DEBUG(llvm::dbgs() << 
"Update modules\n");
 
 1791    for (
auto [i, module] : 
llvm::enumerate(modules)) {
 
 1792      auto moduleName = 
module.getModuleNameAttr();
 
 1793      auto &maybeModuleInfo = moduleInfos[i];
 
 1795      if (!maybeModuleInfo) {
 
 1800        dedupMap[moduleName] = moduleName;
 
 1804      auto &moduleInfo = maybeModuleInfo.value();
 
 1805      moduleToModuleInfo.try_emplace(module, &moduleInfo);
 
 1808      for (
auto &referredModule : moduleInfo.referredModuleNames)
 
 1809        referredModule = dedupMap[referredModule];
 
 1812      auto it = moduleInfoToModule.find(&moduleInfo);
 
 1813      if (it != moduleInfoToModule.end()) {
 
 1814        auto original = cast<FModuleLike>(it->second);
 
 1815        auto originalName = original.getModuleNameAttr();
 
 1824          for (
auto &[originalName, dedupedName] : dedupMap)
 
 1825            if (dedupedName == originalName)
 
 1826              dedupedName = moduleName;
 
 1829          it->second = 
module;
 
 1831          std::swap(originalName, moduleName);
 
 1832          std::swap(original, module);
 
 1836        LLVM_DEBUG(llvm::dbgs() << 
"- Replace " << moduleName << 
" with " 
 1837                                << originalName << 
"\n");
 
 1838        dedupMap[moduleName] = originalName;
 
 1839        deduper.dedup(original, module);
 
 1841        anythingChanged = 
true;
 
 1845      deduper.record(module);
 
 1847      dedupMap[moduleName] = moduleName;
 
 1849      moduleInfoToModule[&moduleInfo] = 
module;
 
 1857    auto failed = 
false;
 
 1859    auto parseModule = [&](Attribute path) -> StringAttr {
 
 1862      auto [_, rhs] = cast<StringAttr>(path).getValue().split(
'|');
 
 1863      return StringAttr::get(context, rhs);
 
 1868    auto getLead = [&](StringAttr module) -> StringAttr {
 
 1869      auto it = dedupMap.find(module);
 
 1870      if (it == dedupMap.end()) {
 
 1871        auto diag = emitError(circuit.getLoc(),
 
 1872                              "MustDeduplicateAnnotation references module ")
 
 1873                    << 
module << " which does not exist";
 
 1880    LLVM_DEBUG(llvm::dbgs() << 
"Update annotations\n");
 
 1884      auto modules = annotation.
getMember<ArrayAttr>(
"modules");
 
 1886        emitError(circuit.getLoc(),
 
 1887                  "MustDeduplicateAnnotation missing \"modules\" member");
 
 1892      if (modules.empty())
 
 1895      auto firstModule = parseModule(modules[0]);
 
 1896      auto firstLead = getLead(firstModule);
 
 1900      for (
auto attr : modules.getValue().drop_front()) {
 
 1901        auto nextModule = parseModule(attr);
 
 1902        auto nextLead = getLead(nextModule);
 
 1905        if (firstLead != nextLead) {
 
 1906          auto diag = emitError(circuit.getLoc(), 
"module ")
 
 1907                      << nextModule << 
" not deduplicated with " << firstModule;
 
 1910          equiv.check(diag, a, b);
 
 1918      return signalPassFailure();
 
 1921    for (
auto module : circuit.getOps<FModuleLike>())
 
 1922      module->removeDiscardableAttr(dedupGroupAttrName);
 
 1928    markAnalysesPreserved<NLATable>();
 
 1929    if (!anythingChanged)
 
 1930      markAllAnalysesPreserved();
 
assert(baseType &&"element must be base type")
static void mergeRegions(Region *region1, Region *region2)
static Block * getBodyBlock(FModuleLike mod)
static InstancePath empty
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
bool hasAnnotation(StringRef className) const
Return true if we have an annotation with the specified class name.
static AnnotationSet forPort(FModuleLike op, size_t portNo)
Get an annotation set for the specified port.
This class provides a read-only projection of an annotation.
void setDict(DictionaryAttr dict)
Set the data dictionary of this attribute.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
StringAttr getClassAttr() const
Return the 'class' that this annotation is representing.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
This graph tracks modules and where they are instantiated.
This table tracks nlas and what modules participate in them.
A table of inner symbols and their resolutions.
auto getModule()
Get the module that this node is tracking.
decltype(auto) walkPostOrder(Fn &&fn)
Perform a post-order walk across the modules.
InstanceGraphNode * lookup(ModuleOpInterface op)
Look up an InstanceGraphNode for a module.
static StringRef toString(Direction direction)
FieldRef getFieldRefFromValue(Value value, bool lookThroughCasts=false)
Get the FieldRef from a value.
constexpr const char * mustDedupAnnoClass
constexpr const char * noDedupAnnoClass
size_t getNumPorts(Operation *op)
Return the number of ports in a module-like thing (modules, memories, etc)
constexpr const char * dedupGroupAnnoClass
std::pair< std::string, bool > getFieldName(const FieldRef &fieldRef, bool nameSafe=false)
Get a string identifier representing the FieldRef.
constexpr const char * dontTouchAnnoClass
void emitConnect(OpBuilder &builder, Location loc, Value lhs, Value rhs)
Emit a connect between two values.
static bool operator==(const ModulePort &a, const ModulePort &b)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
llvm::raw_ostream & debugHeader(const llvm::Twine &str, unsigned width=80)
Write a "header"-like string to the debug stream with a certain width.
SmallVector< FlatSymbolRefAttr > createNLAs(Operation *fromModule, ArrayRef< Attribute > baseNamepath, SymbolTable::Visibility vis=SymbolTable::Visibility::Private)
Look up the instantiations of the from module and create an NLA for each one, appending the baseNamep...
Block * nlaBlock
We insert all NLAs to the beginning of this block.
void recordAnnotations(Operation *op)
Record all targets which use an NLA.
void eraseNLA(hw::HierPathOp nla)
This erases the NLA op, and removes the NLA from every module's NLA map, but it does not delete the N...
void mergeAnnotations(FModuleLike toModule, Operation *to, FModuleLike fromModule, Operation *from)
Merge all annotations and port annotations on two operations.
void replaceInstances(FModuleLike toModule, Operation *fromModule)
This deletes and replaces all instances of the "fromModule" with instances of the "toModule".
void record(FModuleLike module)
Record the usages of any NLA's in this module, so that we may update the annotation if the parent mod...
void rewriteExtModuleNLAs(RenameMap &renameMap, StringAttr toName, StringAttr fromName)
void mergeRegions(RenameMap &renameMap, FModuleLike toModule, Region &toRegion, FModuleLike fromModule, Region &fromRegion)
void dedup(FModuleLike toModule, FModuleLike fromModule)
Remove the "fromModule", and replace all references to it with the "toModule".
void rewriteModuleNLAs(RenameMap &renameMap, FModuleOp toModule, FModuleOp fromModule)
Process all the NLAs that the two modules participate in, replacing references to the "from" module w...
SmallVector< FlatSymbolRefAttr > createNLAs(StringAttr toModuleName, FModuleLike fromModule, SymbolTable::Visibility vis=SymbolTable::Visibility::Private)
Look up the instantiations of this module and create an NLA for each one.
void recordAnnotations(AnnoTarget target)
For a specific annotation target, record all the unique NLAs which target it in the targetMap.
NLATable * nlaTable
Cached nla table analysis.
hw::InnerSymAttr mergeInnerSymbols(RenameMap &renameMap, FModuleLike toModule, hw::InnerSymAttr toSym, hw::InnerSymAttr fromSym)
void cloneAnnotation(SmallVectorImpl< FlatSymbolRefAttr > &nlas, Annotation anno, ArrayRef< NamedAttribute > attributes, unsigned nonLocalIndex, SmallVectorImpl< Annotation > &newAnnotations)
Clone the annotation for each NLA in a list.
void recordSymRenames(RenameMap &renameMap, FModuleLike toModule, Operation *to, FModuleLike fromModule, Operation *from)
void mergeAnnotations(FModuleLike toModule, AnnoTarget to, AnnotationSet toAnnos, FModuleLike fromModule, AnnoTarget from, AnnotationSet fromAnnos)
Merge the annotations of a specific target, either a operation or a port on an operation.
StringAttr nonLocalString
hw::InnerSymbolNamespace & getNamespace(Operation *module)
Get a cached namespace for a module.
SymbolTable & symbolTable
void mergeOps(RenameMap &renameMap, FModuleLike toModule, Operation *to, FModuleLike fromModule, Operation *from)
Recursively merge two operations.
DenseMap< Operation *, hw::InnerSymbolNamespace > moduleNamespaces
A module namespace cache.
bool makeAnnotationNonLocal(StringAttr toModuleName, AnnoTarget to, FModuleLike fromModule, Annotation anno, SmallVectorImpl< Annotation > &newAnnotations)
Take an annotation, and update it to be a non-local annotation.
InstanceGraph & instanceGraph
void mergeBlocks(RenameMap &renameMap, FModuleLike toModule, Block &toBlock, FModuleLike fromModule, Block &fromBlock)
Recursively merge two blocks.
DenseMap< Attribute, llvm::SmallDenseSet< AnnoTarget > > targetMap
void copyAnnotations(FModuleLike toModule, AnnoTarget to, FModuleLike fromModule, AnnotationSet annos, SmallVectorImpl< Annotation > &newAnnotations, SmallPtrSetImpl< Attribute > &dontTouches)
Deduper(InstanceGraph &instanceGraph, SymbolTable &symbolTable, NLATable *nlaTable, CircuitOp circuit)
void addAnnotationContext(RenameMap &renameMap, FModuleOp toModule, FModuleOp fromModule)
Process all NLAs referencing the "from" module to point to the "to" module.
DenseMap< StringAttr, StringAttr > RenameMap
DenseMap< Attribute, Attribute > nlaCache
const hw::InnerSymbolTable & a
ModuleData(const hw::InnerSymbolTable &a, const hw::InnerSymbolTable &b)
const hw::InnerSymbolTable & b
This class is for reporting differences between two modules which should have been deduplicated.
DenseSet< Attribute > nonessentialAttributes
std::string prettyPrint(Attribute attr)
LogicalResult check(InFlightDiagnostic &diag, FInstanceLike a, FInstanceLike b)
LogicalResult check(InFlightDiagnostic &diag, ModuleData &data, Operation *a, Block &aBlock, Operation *b, Block &bBlock)
LogicalResult check(InFlightDiagnostic &diag, const Twine &message, Operation *a, Type aType, Operation *b, Type bType)
StringAttr dedupGroupAttrName
LogicalResult check(InFlightDiagnostic &diag, ModuleData &data, Operation *a, DictionaryAttr aDict, Operation *b, DictionaryAttr bDict)
LogicalResult check(InFlightDiagnostic &diag, ModuleData &data, Operation *a, Region &aRegion, Operation *b, Region &bRegion)
StringAttr portDirectionsAttr
LogicalResult check(InFlightDiagnostic &diag, const Twine &message, Operation *a, BundleType aType, Operation *b, BundleType bType)
LogicalResult check(InFlightDiagnostic &diag, ModuleData &data, Operation *a, Operation *b)
Equivalence(MLIRContext *context, InstanceGraph &instanceGraph)
LogicalResult check(InFlightDiagnostic &diag, Operation *a, mlir::DenseBoolArrayAttr aAttr, Operation *b, mlir::DenseBoolArrayAttr bAttr)
InstanceGraph & instanceGraph
void check(InFlightDiagnostic &diag, Operation *a, Operation *b)
A reference to a ModuleInfo that compares and hashes like it.
ModuleInfoRef(ModuleInfo *info)
std::vector< Operation * > symbolSensitiveOps
std::vector< StringAttr > referredModuleNames
std::array< uint8_t, 32 > structuralHash
This struct contains constant string attributes shared across different threads.
StringAttr moduleNameAttr
DenseSet< Attribute > nonessentialAttributes
StructuralHasherSharedConstants(MLIRContext *context)
void populateInnerSymIDTable(FModuleLike module)
Find all the ports and operations which may define an inner symbol operations and give each a unique ...
void update(Operation *op, DictionaryAttr dict)
Hash the top level attribute dictionary of the operation.
void update(const void *pointer)
void update(ClassType type)
DenseMap< void *, unsigned > idTable
void update(const std::pair< T, U > &pair)
void update(Operation *op)
DenseMap< StringAttr, std::pair< size_t, size_t > > innerSymIDTable
ModuleInfo getModuleInfo(FModuleLike module)
void update(size_t value)
void update(BundleType type)
unsigned getID(void *object)
void update(OpResult result)
void update(OpOperand &operand)
StructuralHasher(const StructuralHasherSharedConstants &constants)
std::vector< Operation * > symbolSensitiveOps
void update(Region *region)
void update(Block *block)
std::vector< StringAttr > referredModuleNames
void update(TypeID typeID)
const StructuralHasherSharedConstants & constants
std::pair< size_t, size_t > getInnerSymID(StringAttr name)
unsigned finalizeID(void *object)
void update(mlir::OperationName name)
An annotation target is used to keep track of something that is targeted by an Annotation.
AnnotationSet getAnnotations() const
Get the annotations associated with the target.
void setAnnotations(AnnotationSet annotations) const
Set the annotations associated with the target.
This represents an annotation targeting a specific operation.
Attribute getNLAReference(hw::InnerSymbolNamespace &moduleNamespace) const
This represents an annotation targeting a specific port of a module, memory, or instance.
static bool isEqual(const ModuleInfoRef &lhs, const ModuleInfoRef &rhs)
static ModuleInfoRef getTombstoneKey()
static ModuleInfoRef getEmptyKey()
static unsigned getHashValue(const ModuleInfoRef &ref)