CIRCT  18.0.0git
Dedup.cpp
Go to the documentation of this file.
1 //===- Dedup.cpp - FIRRTL module deduping -----------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file implements FIRRTL module deduplication.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "PassDetails.h"
24 #include "circt/Support/LLVM.h"
25 #include "mlir/IR/IRMapping.h"
26 #include "mlir/IR/ImplicitLocOpBuilder.h"
27 #include "mlir/IR/Threading.h"
28 #include "mlir/Support/LogicalResult.h"
29 #include "llvm/ADT/DenseMap.h"
30 #include "llvm/ADT/DenseMapInfo.h"
31 #include "llvm/ADT/DepthFirstIterator.h"
32 #include "llvm/ADT/PostOrderIterator.h"
33 #include "llvm/ADT/SmallPtrSet.h"
34 #include "llvm/ADT/TypeSwitch.h"
35 #include "llvm/Support/Format.h"
36 #include "llvm/Support/SHA256.h"
37 
38 using namespace circt;
39 using namespace firrtl;
40 using hw::InnerRefAttr;
41 
42 //===----------------------------------------------------------------------===//
43 // Hashing
44 //===----------------------------------------------------------------------===//
45 
46 llvm::raw_ostream &printHex(llvm::raw_ostream &stream,
47  ArrayRef<uint8_t> bytes) {
48  // Print the hash on a single line.
49  return stream << format_bytes(bytes, std::nullopt, 32) << "\n";
50 }
51 
52 llvm::raw_ostream &printHash(llvm::raw_ostream &stream, llvm::SHA256 &data) {
53  return printHex(stream, data.result());
54 }
55 
56 llvm::raw_ostream &printHash(llvm::raw_ostream &stream, std::string data) {
57  ArrayRef<uint8_t> bytes(reinterpret_cast<const uint8_t *>(data.c_str()),
58  data.length());
59  return printHex(stream, bytes);
60 }
61 
62 // This struct contains information to determine module module uniqueness. A
63 // first element is a structural hash of the module, and the second element is
64 // an array which tracks module names encountered in the walk. Since module
65 // names could be replaced during dedup, it's necessary to keep names up-to-date
66 // before actually combining them into structural hashes.
67 struct ModuleInfo {
68  // SHA256 hash.
69  std::array<uint8_t, 32> structuralHash;
70  // Module names referred by instance op in the module.
71  mlir::ArrayAttr referredModuleNames;
72 };
73 
74 struct SymbolTarget {
75  uint64_t index;
76  uint64_t fieldID;
77 };
78 
79 /// This struct contains constant string attributes shared across different
80 /// threads.
82  explicit StructuralHasherSharedConstants(MLIRContext *context) {
83  portTypesAttr = StringAttr::get(context, "portTypes");
84  moduleNameAttr = StringAttr::get(context, "moduleName");
85  innerSymAttr = StringAttr::get(context, "inner_sym");
86  portSymsAttr = StringAttr::get(context, "portSyms");
87  nonessentialAttributes.insert(StringAttr::get(context, "annotations"));
88  nonessentialAttributes.insert(StringAttr::get(context, "name"));
89  nonessentialAttributes.insert(StringAttr::get(context, "portAnnotations"));
90  nonessentialAttributes.insert(StringAttr::get(context, "portNames"));
91  nonessentialAttributes.insert(StringAttr::get(context, "portLocations"));
92  nonessentialAttributes.insert(StringAttr::get(context, "sym_name"));
93  };
94 
95  // This is a cached "portTypes" string attr.
96  StringAttr portTypesAttr;
97 
98  // This is a cached "moduleName" string attr.
99  StringAttr moduleNameAttr;
100 
101  // This is a cached "inner_sym" string attr.
102  StringAttr innerSymAttr;
103 
104  // This is a cached "portSyms" string attr.
105  StringAttr portSymsAttr;
106 
107  // This is a set of every attribute we should ignore.
108  DenseSet<Attribute> nonessentialAttributes;
109 };
110 
113  : constants(constants){};
114 
115  std::pair<std::array<uint8_t, 32>, SmallVector<StringAttr>>
116  getHashAndModuleNames(FModuleLike module, StringAttr group) {
117  update(&(*module));
118  if (group)
119  sha.update(group.str());
120  auto hash = sha.final();
121  return {hash, referredModuleNames};
122  }
123 
124 private:
125  void update(const void *pointer) {
126  auto *addr = reinterpret_cast<const uint8_t *>(&pointer);
127  sha.update(ArrayRef<uint8_t>(addr, sizeof pointer));
128  }
129 
130  void update(size_t value) {
131  auto *addr = reinterpret_cast<const uint8_t *>(&value);
132  sha.update(ArrayRef<uint8_t>(addr, sizeof value));
133  }
134 
135  void update(TypeID typeID) { update(typeID.getAsOpaquePointer()); }
136 
137  // NOLINTNEXTLINE(misc-no-recursion)
138  void update(BundleType type) {
139  update(type.getTypeID());
140  for (auto &element : type.getElements()) {
141  update(element.isFlip);
142  update(element.type);
143  }
144  }
145 
146  // NOLINTNEXTLINE(misc-no-recursion)
147  void update(Type type) {
148  if (auto bundle = type_dyn_cast<BundleType>(type))
149  return update(bundle);
150  update(type.getAsOpaquePointer());
151  }
152 
153  void record(void *address) {
154  auto size = indices.size();
155  indices[address] = size;
156  }
157 
158  void update(BlockArgument arg) { record(arg.getAsOpaquePointer()); }
159 
160  void update(OpResult result) {
161  record(result.getAsOpaquePointer());
162  update(result.getType());
163  }
164 
165  void update(OpOperand &operand) {
166  // We hash the value's index as it apears in the block.
167  auto it = indices.find(operand.get().getAsOpaquePointer());
168  assert(it != indices.end() && "op should have been previously hashed");
169  update(it->second);
170  }
171 
172  void update(Operation *op, hw::InnerSymAttr attr) {
173  for (auto props : attr)
174  innerSymTargets[props.getName()] =
175  SymbolTarget{indices[op], props.getFieldID()};
176  }
177 
178  void update(Value value, hw::InnerSymAttr attr) {
179  for (auto props : attr)
180  innerSymTargets[props.getName()] =
181  SymbolTarget{indices[value.getAsOpaquePointer()], props.getFieldID()};
182  }
183 
184  void update(const SymbolTarget &target) {
185  update(target.index);
186  update(target.fieldID);
187  }
188 
189  void update(InnerRefAttr attr) {
190  // We hash the value's index as it apears in the block.
191  auto it = innerSymTargets.find(attr.getName());
192  assert(it != innerSymTargets.end() &&
193  "inner symbol should have been previously hashed");
194  update(attr.getTypeID());
195  update(it->second);
196  }
197 
198  /// Hash the top level attribute dictionary of the operation. This function
199  /// has special handling for inner symbols, ports, and referenced modules.
200  void update(Operation *op, DictionaryAttr dict) {
201  for (auto namedAttr : dict) {
202  auto name = namedAttr.getName();
203  auto value = namedAttr.getValue();
204  // Skip names and annotations.
205  if (constants.nonessentialAttributes.contains(name))
206  continue;
207 
208  // Hash the port types.
209  if (name == constants.portTypesAttr) {
210  auto portTypes = cast<ArrayAttr>(value).getAsValueRange<TypeAttr>();
211  for (auto type : portTypes)
212  update(type);
213  continue;
214  }
215 
216  // Special case the InnerSymbols to ignore the symbol names.
217  if (name == constants.portSymsAttr) {
218  if (op->getNumRegions() != 1)
219  continue;
220  auto &region = op->getRegion(0);
221  if (region.getBlocks().empty())
222  continue;
223  auto *block = &region.front();
224  auto syms = cast<ArrayAttr>(value).getAsRange<hw::InnerSymAttr>();
225  if (syms.empty())
226  continue;
227  for (auto [arg, sym] : llvm::zip_equal(block->getArguments(), syms))
228  update(arg, sym);
229  continue;
230  }
231  if (name == constants.innerSymAttr) {
232  auto innerSym = cast<hw::InnerSymAttr>(value);
233  update(op, innerSym);
234  continue;
235  }
236 
237  // For instance op, don't use `moduleName` attributes since they might be
238  // replaced by dedup. Record the names and lazily combine their hashes.
239  // It is assumed that module names are hashed only through instance ops;
240  // it could cause suboptimal results if there was other operation that
241  // refers to module names through essential attributes.
242  if (isa<InstanceOp>(op) && name == constants.moduleNameAttr) {
243  referredModuleNames.push_back(cast<FlatSymbolRefAttr>(value).getAttr());
244  continue;
245  }
246 
247  // Hash the interned pointer.
248  update(name.getAsOpaquePointer());
249 
250  // If this is an symbol reference, we need to perform name erasure.
251  if (auto innerRef = dyn_cast<hw::InnerRefAttr>(value))
252  update(innerRef);
253  else
254  update(value.getAsOpaquePointer());
255  }
256  }
257 
258  void update(Block &block) {
259  // Hash the block arguments.
260  for (auto arg : block.getArguments())
261  update(arg);
262  // Hash the operations in the block.
263  for (auto &op : block)
264  update(&op);
265  }
266 
267  void update(mlir::OperationName name) {
268  // Operation names are interned.
269  update(name.getAsOpaquePointer());
270  }
271 
272  // NOLINTNEXTLINE(misc-no-recursion)
273  void update(Operation *op) {
274  record(op);
275  update(op->getName());
276  update(op, op->getAttrDictionary());
277  // Hash the operands.
278  for (auto &operand : op->getOpOperands())
279  update(operand);
280  // Hash the regions. We need to make sure an empty region doesn't hash the
281  // same as no region, so we include the number of regions.
282  update(op->getNumRegions());
283  for (auto &region : op->getRegions())
284  for (auto &block : region.getBlocks())
285  update(block);
286  // Record any op results.
287  for (auto result : op->getResults())
288  update(result);
289  }
290 
291  // Every operation and value is assigned a unique id based on their order of
292  // appearance
293  DenseMap<void *, unsigned> indices;
294 
295  // Every value is assigned a unique id based on their order of appearance.
296  DenseMap<StringAttr, SymbolTarget> innerSymTargets;
297 
298  // This keeps track of module names in the order of the appearance.
299  SmallVector<mlir::StringAttr> referredModuleNames;
300 
301  // String constants.
303 
304  // This is the actual running hash calculation. This is a stateful element
305  // that should be reinitialized after each hash is produced.
306  llvm::SHA256 sha;
307 };
308 
309 //===----------------------------------------------------------------------===//
310 // Equivalence
311 //===----------------------------------------------------------------------===//
312 
313 /// This class is for reporting differences between two modules which should
314 /// have been deduplicated.
315 struct Equivalence {
316  Equivalence(MLIRContext *context, InstanceGraph &instanceGraph)
317  : instanceGraph(instanceGraph) {
318  noDedupClass = StringAttr::get(context, noDedupAnnoClass);
319  dedupGroupClass = StringAttr::get(context, dedupGroupAnnoClass);
320  portDirectionsAttr = StringAttr::get(context, "portDirections");
321  nonessentialAttributes.insert(StringAttr::get(context, "annotations"));
322  nonessentialAttributes.insert(StringAttr::get(context, "name"));
323  nonessentialAttributes.insert(StringAttr::get(context, "portAnnotations"));
324  nonessentialAttributes.insert(StringAttr::get(context, "portNames"));
325  nonessentialAttributes.insert(StringAttr::get(context, "portTypes"));
326  nonessentialAttributes.insert(StringAttr::get(context, "portSyms"));
327  nonessentialAttributes.insert(StringAttr::get(context, "portLocations"));
328  nonessentialAttributes.insert(StringAttr::get(context, "sym_name"));
329  nonessentialAttributes.insert(StringAttr::get(context, "inner_sym"));
330  }
331 
332  struct ModuleData {
333  ModuleData(const hw::InnerSymbolTable &a, const hw::InnerSymbolTable &b)
334  : a(a), b(b) {}
335  IRMapping map;
336  const hw::InnerSymbolTable &a;
337  const hw::InnerSymbolTable &b;
338  };
339 
340  std::string prettyPrint(Attribute attr) {
341  SmallString<64> buffer;
342  llvm::raw_svector_ostream os(buffer);
343  if (auto integerAttr = dyn_cast<IntegerAttr>(attr)) {
344  os << "0x";
345  if (integerAttr.getType().isSignlessInteger())
346  integerAttr.getValue().toStringUnsigned(buffer, /*radix=*/16);
347  else
348  integerAttr.getAPSInt().toString(buffer, /*radix=*/16);
349 
350  } else
351  os << attr;
352  return std::string(buffer);
353  }
354 
355  // NOLINTNEXTLINE(misc-no-recursion)
356  LogicalResult check(InFlightDiagnostic &diag, const Twine &message,
357  Operation *a, BundleType aType, Operation *b,
358  BundleType bType) {
359  if (aType.getNumElements() != bType.getNumElements()) {
360  diag.attachNote(a->getLoc())
361  << message << " bundle type has different number of elements";
362  diag.attachNote(b->getLoc()) << "second operation here";
363  return failure();
364  }
365 
366  for (auto elementPair :
367  llvm::zip(aType.getElements(), bType.getElements())) {
368  auto aElement = std::get<0>(elementPair);
369  auto bElement = std::get<1>(elementPair);
370  if (aElement.isFlip != bElement.isFlip) {
371  diag.attachNote(a->getLoc()) << message << " bundle element "
372  << aElement.name << " flip does not match";
373  diag.attachNote(b->getLoc()) << "second operation here";
374  return failure();
375  }
376 
377  if (failed(check(diag,
378  "bundle element \'" + aElement.name.getValue() + "'", a,
379  aElement.type, b, bElement.type)))
380  return failure();
381  }
382  return success();
383  }
384 
385  LogicalResult check(InFlightDiagnostic &diag, const Twine &message,
386  Operation *a, Type aType, Operation *b, Type bType) {
387  if (aType == bType)
388  return success();
389  if (auto aBundleType = type_dyn_cast<BundleType>(aType))
390  if (auto bBundleType = type_dyn_cast<BundleType>(bType))
391  return check(diag, message, a, aBundleType, b, bBundleType);
392  if (type_isa<RefType>(aType) && type_isa<RefType>(bType) &&
393  aType != bType) {
394  diag.attachNote(a->getLoc())
395  << message << ", has a RefType with a different base type "
396  << type_cast<RefType>(aType).getType()
397  << " in the same position of the two modules marked as 'must dedup'. "
398  "(This may be due to Grand Central Taps or Views being different "
399  "between the two modules.)";
400  diag.attachNote(b->getLoc())
401  << "the second module has a different base type "
402  << type_cast<RefType>(bType).getType();
403  return failure();
404  }
405  diag.attachNote(a->getLoc())
406  << message << " types don't match, first type is " << aType;
407  diag.attachNote(b->getLoc()) << "second type is " << bType;
408  return failure();
409  }
410 
411  LogicalResult check(InFlightDiagnostic &diag, ModuleData &data, Operation *a,
412  Block &aBlock, Operation *b, Block &bBlock) {
413 
414  // Block argument types.
415  auto portNames = a->getAttrOfType<ArrayAttr>("portNames");
416  auto portNo = 0;
417  auto emitMissingPort = [&](Value existsVal, Operation *opExists,
418  Operation *opDoesNotExist) {
419  StringRef portName;
420  auto portNames = opExists->getAttrOfType<ArrayAttr>("portNames");
421  if (portNames)
422  if (auto portNameAttr = dyn_cast<StringAttr>(portNames[portNo]))
423  portName = portNameAttr.getValue();
424  if (type_isa<RefType>(existsVal.getType())) {
425  diag.attachNote(opExists->getLoc())
426  << " contains a RefType port named '" + portName +
427  "' that only exists in one of the modules (can be due to "
428  "difference in Grand Central Tap or View of two modules "
429  "marked with must dedup)";
430  diag.attachNote(opDoesNotExist->getLoc())
431  << "second module to be deduped that does not have the RefType "
432  "port";
433  } else {
434  diag.attachNote(opExists->getLoc())
435  << "port '" + portName + "' only exists in one of the modules";
436  diag.attachNote(opDoesNotExist->getLoc())
437  << "second module to be deduped that does not have the port";
438  }
439  return failure();
440  };
441 
442  for (auto argPair :
443  llvm::zip_longest(aBlock.getArguments(), bBlock.getArguments())) {
444  auto &aArg = std::get<0>(argPair);
445  auto &bArg = std::get<1>(argPair);
446  if (aArg.has_value() && bArg.has_value()) {
447  // TODO: we should print the port number if there are no port names, but
448  // there are always port names ;).
449  StringRef portName;
450  if (portNames) {
451  if (auto portNameAttr = dyn_cast<StringAttr>(portNames[portNo]))
452  portName = portNameAttr.getValue();
453  }
454  // Assumption here that block arguments correspond to ports.
455  if (failed(check(diag, "module port '" + portName + "'", a,
456  aArg->getType(), b, bArg->getType())))
457  return failure();
458  data.map.map(aArg.value(), bArg.value());
459  portNo++;
460  continue;
461  }
462  if (!aArg.has_value())
463  std::swap(a, b);
464  return emitMissingPort(aArg.has_value() ? aArg.value() : bArg.value(), a,
465  b);
466  }
467 
468  // Blocks operations.
469  auto aIt = aBlock.begin();
470  auto aEnd = aBlock.end();
471  auto bIt = bBlock.begin();
472  auto bEnd = bBlock.end();
473  while (aIt != aEnd && bIt != bEnd)
474  if (failed(check(diag, data, &*aIt++, &*bIt++)))
475  return failure();
476  if (aIt != aEnd) {
477  diag.attachNote(aIt->getLoc()) << "first block has more operations";
478  diag.attachNote(b->getLoc()) << "second block here";
479  return failure();
480  }
481  if (bIt != bEnd) {
482  diag.attachNote(bIt->getLoc()) << "second block has more operations";
483  diag.attachNote(a->getLoc()) << "first block here";
484  return failure();
485  }
486  return success();
487  }
488 
489  LogicalResult check(InFlightDiagnostic &diag, ModuleData &data, Operation *a,
490  Region &aRegion, Operation *b, Region &bRegion) {
491  auto aIt = aRegion.begin();
492  auto aEnd = aRegion.end();
493  auto bIt = bRegion.begin();
494  auto bEnd = bRegion.end();
495 
496  // Region blocks.
497  while (aIt != aEnd && bIt != bEnd)
498  if (failed(check(diag, data, a, *aIt++, b, *bIt++)))
499  return failure();
500  if (aIt != aEnd || bIt != bEnd) {
501  diag.attachNote(a->getLoc())
502  << "operation regions have different number of blocks";
503  diag.attachNote(b->getLoc()) << "second operation here";
504  return failure();
505  }
506  return success();
507  }
508 
509  LogicalResult check(InFlightDiagnostic &diag, Operation *a, IntegerAttr aAttr,
510  Operation *b, IntegerAttr bAttr) {
511  if (aAttr == bAttr)
512  return success();
513  auto aDirections = direction::unpackAttribute(aAttr);
514  auto bDirections = direction::unpackAttribute(bAttr);
515  auto portNames = a->getAttrOfType<ArrayAttr>("portNames");
516  for (unsigned i = 0, e = aDirections.size(); i < e; ++i) {
517  auto aDirection = aDirections[i];
518  auto bDirection = bDirections[i];
519  if (aDirection != bDirection) {
520  auto &note = diag.attachNote(a->getLoc()) << "module port ";
521  if (portNames)
522  note << "'" << cast<StringAttr>(portNames[i]).getValue() << "'";
523  else
524  note << i;
525  note << " directions don't match, first direction is '"
526  << direction::toString(aDirection) << "'";
527  diag.attachNote(b->getLoc()) << "second direction is '"
528  << direction::toString(bDirection) << "'";
529  return failure();
530  }
531  }
532  return success();
533  }
534 
535  LogicalResult check(InFlightDiagnostic &diag, ModuleData &data, Operation *a,
536  DictionaryAttr aDict, Operation *b,
537  DictionaryAttr bDict) {
538  // Fast path.
539  if (aDict == bDict)
540  return success();
541 
542  DenseSet<Attribute> seenAttrs;
543  for (auto namedAttr : aDict) {
544  auto attrName = namedAttr.getName();
545  if (nonessentialAttributes.contains(attrName))
546  continue;
547 
548  auto aAttr = namedAttr.getValue();
549  auto bAttr = bDict.get(attrName);
550  if (!bAttr) {
551  diag.attachNote(a->getLoc())
552  << "second operation is missing attribute " << attrName;
553  diag.attachNote(b->getLoc()) << "second operation here";
554  return diag;
555  }
556 
557  if (isa<hw::InnerRefAttr>(aAttr) && isa<hw::InnerRefAttr>(bAttr)) {
558  auto bRef = cast<hw::InnerRefAttr>(bAttr);
559  auto aRef = cast<hw::InnerRefAttr>(aAttr);
560  // See if they are pointing at the same operation or port.
561  auto aTarget = data.a.lookup(aRef.getName());
562  auto bTarget = data.b.lookup(bRef.getName());
563  if (!aTarget || !bTarget)
564  diag.attachNote(a->getLoc())
565  << "malformed ir, possibly violating use-before-def";
566  auto error = [&]() {
567  diag.attachNote(a->getLoc())
568  << "operations have different targets, first operation has "
569  << aTarget;
570  diag.attachNote(b->getLoc()) << "second operation has " << bTarget;
571  return failure();
572  };
573  if (aTarget.isPort()) {
574  // If they are targeting ports, make sure its the same port number.
575  if (!bTarget.isPort() || aTarget.getPort() != bTarget.getPort())
576  return error();
577  } else {
578  // Otherwise make sure that they are targeting the same operation.
579  if (!bTarget.isOpOnly() ||
580  aTarget.getOp() != data.map.lookup(bTarget.getOp()))
581  return error();
582  }
583  if (aTarget.getField() != bTarget.getField())
584  return error();
585  } else if (attrName == portDirectionsAttr) {
586  // Special handling for the port directions attribute for better
587  // error messages.
588  if (failed(check(diag, a, cast<IntegerAttr>(aAttr), b,
589  cast<IntegerAttr>(bAttr))))
590  return failure();
591  } else if (aAttr != bAttr) {
592  diag.attachNote(a->getLoc())
593  << "first operation has attribute '" << attrName.getValue()
594  << "' with value " << prettyPrint(aAttr);
595  diag.attachNote(b->getLoc())
596  << "second operation has value " << prettyPrint(bAttr);
597  return failure();
598  }
599  seenAttrs.insert(attrName);
600  }
601  if (aDict.getValue().size() != bDict.getValue().size()) {
602  for (auto namedAttr : bDict) {
603  auto attrName = namedAttr.getName();
604  // Skip the attribute if we don't care about this particular one or it
605  // is one that is known to be in both dictionaries.
606  if (nonessentialAttributes.contains(attrName) ||
607  seenAttrs.contains(attrName))
608  continue;
609  // We have found an attribute that is only in the second operation.
610  diag.attachNote(a->getLoc())
611  << "first operation is missing attribute " << attrName;
612  diag.attachNote(b->getLoc()) << "second operation here";
613  return failure();
614  }
615  }
616  return success();
617  }
618 
619  // NOLINTNEXTLINE(misc-no-recursion)
620  LogicalResult check(InFlightDiagnostic &diag, InstanceOp a, InstanceOp b) {
621  auto aName = a.getModuleNameAttr().getAttr();
622  auto bName = b.getModuleNameAttr().getAttr();
623  // If the modules instantiate are different we will want to know why the
624  // sub module did not dedupliate. This code recursively checks the child
625  // module.
626  if (aName != bName) {
627  auto aModule = instanceGraph.getReferencedModule(a);
628  auto bModule = instanceGraph.getReferencedModule(b);
629  // Create a new error for the submodule.
630  diag.attachNote(std::nullopt)
631  << "in instance " << a.getNameAttr() << " of " << aName
632  << ", and instance " << b.getNameAttr() << " of " << bName;
633  check(diag, aModule, bModule);
634  return failure();
635  }
636  return success();
637  }
638 
639  // NOLINTNEXTLINE(misc-no-recursion)
640  LogicalResult check(InFlightDiagnostic &diag, ModuleData &data, Operation *a,
641  Operation *b) {
642  // Operation name.
643  if (a->getName() != b->getName()) {
644  diag.attachNote(a->getLoc()) << "first operation is a " << a->getName();
645  diag.attachNote(b->getLoc()) << "second operation is a " << b->getName();
646  return failure();
647  }
648 
649  // If its an instance operaiton, perform some checking and possibly
650  // recurse.
651  if (auto aInst = dyn_cast<InstanceOp>(a)) {
652  auto bInst = cast<InstanceOp>(b);
653  if (failed(check(diag, aInst, bInst)))
654  return failure();
655  }
656 
657  // Operation results.
658  if (a->getNumResults() != b->getNumResults()) {
659  diag.attachNote(a->getLoc())
660  << "operations have different number of results";
661  diag.attachNote(b->getLoc()) << "second operation here";
662  return failure();
663  }
664  for (auto resultPair : llvm::zip(a->getResults(), b->getResults())) {
665  auto &aValue = std::get<0>(resultPair);
666  auto &bValue = std::get<1>(resultPair);
667  if (failed(check(diag, "operation result", a, aValue.getType(), b,
668  bValue.getType())))
669  return failure();
670  data.map.map(aValue, bValue);
671  }
672 
673  // Operations operands.
674  if (a->getNumOperands() != b->getNumOperands()) {
675  diag.attachNote(a->getLoc())
676  << "operations have different number of operands";
677  diag.attachNote(b->getLoc()) << "second operation here";
678  return failure();
679  }
680  for (auto operandPair : llvm::zip(a->getOperands(), b->getOperands())) {
681  auto &aValue = std::get<0>(operandPair);
682  auto &bValue = std::get<1>(operandPair);
683  if (bValue != data.map.lookup(aValue)) {
684  diag.attachNote(a->getLoc())
685  << "operations use different operands, first operand is '"
686  << getFieldName(
687  getFieldRefFromValue(aValue, /*lookThroughCasts=*/true))
688  .first
689  << "'";
690  diag.attachNote(b->getLoc())
691  << "second operand is '"
692  << getFieldName(
693  getFieldRefFromValue(bValue, /*lookThroughCasts=*/true))
694  .first
695  << "', but should have been '"
696  << getFieldName(getFieldRefFromValue(data.map.lookup(aValue),
697  /*lookThroughCasts=*/true))
698  .first
699  << "'";
700  return failure();
701  }
702  }
703  data.map.map(a, b);
704 
705  // Operation regions.
706  if (a->getNumRegions() != b->getNumRegions()) {
707  diag.attachNote(a->getLoc())
708  << "operations have different number of regions";
709  diag.attachNote(b->getLoc()) << "second operation here";
710  return failure();
711  }
712  for (auto regionPair : llvm::zip(a->getRegions(), b->getRegions())) {
713  auto &aRegion = std::get<0>(regionPair);
714  auto &bRegion = std::get<1>(regionPair);
715  if (failed(check(diag, data, a, aRegion, b, bRegion)))
716  return failure();
717  }
718 
719  // Operation attributes.
720  if (failed(check(diag, data, a, a->getAttrDictionary(), b,
721  b->getAttrDictionary())))
722  return failure();
723  return success();
724  }
725 
726  // NOLINTNEXTLINE(misc-no-recursion)
727  void check(InFlightDiagnostic &diag, Operation *a, Operation *b) {
728  hw::InnerSymbolTable aTable(a);
729  hw::InnerSymbolTable bTable(b);
730  ModuleData data(aTable, bTable);
731  AnnotationSet aAnnos(a);
732  AnnotationSet bAnnos(b);
733  if (aAnnos.hasAnnotation(noDedupClass)) {
734  diag.attachNote(a->getLoc()) << "module marked NoDedup";
735  return;
736  }
737  if (bAnnos.hasAnnotation(noDedupClass)) {
738  diag.attachNote(b->getLoc()) << "module marked NoDedup";
739  return;
740  }
741  auto aGroup = aAnnos.hasAnnotation(dedupGroupClass)
742  ? aAnnos.getAnnotation(dedupGroupClass)
743  .getMember<StringAttr>("group")
744  : StringAttr();
745  auto bGroup = bAnnos.hasAnnotation(dedupGroupClass)
746  ? bAnnos.getAnnotation(dedupGroupClass)
747  .getMember<StringAttr>("group")
748  : StringAttr();
749  if (aGroup != bGroup) {
750  if (bGroup) {
751  diag.attachNote(b->getLoc())
752  << "module is in dedup group '" << bGroup.str() << "'";
753  } else {
754  diag.attachNote(b->getLoc()) << "module is not part of a dedup group";
755  }
756  if (aGroup) {
757  diag.attachNote(a->getLoc())
758  << "module is in dedup group '" << aGroup.str() << "'";
759  } else {
760  diag.attachNote(a->getLoc()) << "module is not part of a dedup group";
761  }
762  return;
763  }
764  if (failed(check(diag, data, a, b)))
765  return;
766  diag.attachNote(a->getLoc()) << "first module here";
767  diag.attachNote(b->getLoc()) << "second module here";
768  }
769 
770  // This is a cached "portDirections" string attr.
771  StringAttr portDirectionsAttr;
772  // This is a cached "NoDedup" annotation class string attr.
773  StringAttr noDedupClass;
774  // This is a cached "DedupGroup" annotation class string attr.
775  StringAttr dedupGroupClass;
776  // This is a set of every attribute we should ignore.
777  DenseSet<Attribute> nonessentialAttributes;
779 };
780 
781 //===----------------------------------------------------------------------===//
782 // Deduplication
783 //===----------------------------------------------------------------------===//
784 
785 // Custom location merging. This only keeps track of 8 annotations from ".fir"
786 // files, and however many annotations come from "real" sources. When
787 // deduplicating, modules tend not to have scala source locators, so we wind
788 // up fusing source locators for a module from every copy being deduped. There
789 // is little value in this (all the modules are identical by definition).
790 static Location mergeLoc(MLIRContext *context, Location to, Location from) {
791  // Unique the set of locations to be fused.
792  llvm::SmallSetVector<Location, 4> decomposedLocs;
793  // only track 8 "fir" locations
794  unsigned seenFIR = 0;
795  for (auto loc : {to, from}) {
796  // If the location is a fused location we decompose it if it has no
797  // metadata or the metadata is the same as the top level metadata.
798  if (auto fusedLoc = dyn_cast<FusedLoc>(loc)) {
799  // UnknownLoc's have already been removed from FusedLocs so we can
800  // simply add all of the internal locations.
801  for (auto loc : fusedLoc.getLocations()) {
802  if (FileLineColLoc fileLoc = dyn_cast<FileLineColLoc>(loc)) {
803  if (fileLoc.getFilename().strref().endswith(".fir")) {
804  ++seenFIR;
805  if (seenFIR > 8)
806  continue;
807  }
808  }
809  decomposedLocs.insert(loc);
810  }
811  continue;
812  }
813 
814  // Might need to skip this fir.
815  if (FileLineColLoc fileLoc = dyn_cast<FileLineColLoc>(loc)) {
816  if (fileLoc.getFilename().strref().endswith(".fir")) {
817  ++seenFIR;
818  if (seenFIR > 8)
819  continue;
820  }
821  }
822  // Otherwise, only add known locations to the set.
823  if (!isa<UnknownLoc>(loc))
824  decomposedLocs.insert(loc);
825  }
826 
827  auto locs = decomposedLocs.getArrayRef();
828 
829  // Handle the simple cases of less than two locations. Ensure the metadata (if
830  // provided) is not dropped.
831  if (locs.empty())
832  return UnknownLoc::get(context);
833  if (locs.size() == 1)
834  return locs.front();
835 
836  return FusedLoc::get(context, locs);
837 }
838 
839 struct Deduper {
840 
841  using RenameMap = DenseMap<StringAttr, StringAttr>;
842 
843  Deduper(InstanceGraph &instanceGraph, SymbolTable &symbolTable,
844  NLATable *nlaTable, CircuitOp circuit)
845  : context(circuit->getContext()), instanceGraph(instanceGraph),
846  symbolTable(symbolTable), nlaTable(nlaTable),
847  nlaBlock(circuit.getBodyBlock()),
848  nonLocalString(StringAttr::get(context, "circt.nonlocal")),
849  classString(StringAttr::get(context, "class")) {
850  // Populate the NLA cache.
851  for (auto nla : circuit.getOps<hw::HierPathOp>())
852  nlaCache[nla.getNamepathAttr()] = nla.getSymNameAttr();
853  }
854 
855  /// Remove the "fromModule", and replace all references to it with the
856  /// "toModule". Modules should be deduplicated in a bottom-up order. Any
857  /// module which is not deduplicated needs to be recorded with the `record`
858  /// call.
859  void dedup(FModuleLike toModule, FModuleLike fromModule) {
860  // A map of operation (e.g. wires, nodes) names which are changed, which is
861  // used to update NLAs that reference the "fromModule".
862  RenameMap renameMap;
863 
864  // Merge the port locations.
865  SmallVector<Attribute> newLocs;
866  for (auto [toLoc, fromLoc] : llvm::zip(toModule.getPortLocations(),
867  fromModule.getPortLocations())) {
868  if (toLoc == fromLoc)
869  newLocs.push_back(toLoc);
870  else
871  newLocs.push_back(mergeLoc(context, cast<LocationAttr>(toLoc),
872  cast<LocationAttr>(fromLoc)));
873  }
874  toModule->setAttr("portLocations", ArrayAttr::get(context, newLocs));
875 
876  // Merge the two modules.
877  mergeOps(renameMap, toModule, toModule, fromModule, fromModule);
878 
879  // Rewrite NLAs pathing through these modules to refer to the to module. It
880  // is safe to do this at this point because NLAs cannot be one element long.
881  // This means that all NLAs which require more context cannot be targetting
882  // something in the module it self.
883  if (auto to = dyn_cast<FModuleOp>(*toModule))
884  rewriteModuleNLAs(renameMap, to, cast<FModuleOp>(*fromModule));
885  else
886  rewriteExtModuleNLAs(renameMap, toModule.getModuleNameAttr(),
887  fromModule.getModuleNameAttr());
888 
889  replaceInstances(toModule, fromModule);
890  }
891 
892  /// Record the usages of any NLA's in this module, so that we may update the
893  /// annotation if the parent module is deduped with another module.
894  void record(FModuleLike module) {
895  // Record any annotations on the module.
896  recordAnnotations(module);
897  // Record port annotations.
898  for (unsigned i = 0, e = getNumPorts(module); i < e; ++i)
899  recordAnnotations(PortAnnoTarget(module, i));
900  // Record any annotations in the module body.
901  module->walk([&](Operation *op) { recordAnnotations(op); });
902  }
903 
904 private:
905  /// Get a cached namespace for a module.
906  hw::InnerSymbolNamespace &getNamespace(Operation *module) {
907  return moduleNamespaces.try_emplace(module, cast<FModuleLike>(module))
908  .first->second;
909  }
910 
911  /// For a specific annotation target, record all the unique NLAs which
912  /// target it in the `targetMap`.
914  for (auto anno : target.getAnnotations())
915  if (auto nlaRef = anno.getMember<FlatSymbolRefAttr>("circt.nonlocal"))
916  targetMap[nlaRef.getAttr()].insert(target);
917  }
918 
919  /// Record all targets which use an NLA.
920  void recordAnnotations(Operation *op) {
921  // Record annotations.
922  recordAnnotations(OpAnnoTarget(op));
923 
924  // Record port annotations only if this is a mem operation.
925  auto mem = dyn_cast<MemOp>(op);
926  if (!mem)
927  return;
928 
929  // Record port annotations.
930  for (unsigned i = 0, e = mem->getNumResults(); i < e; ++i)
931  recordAnnotations(PortAnnoTarget(mem, i));
932  }
933 
934  /// This deletes and replaces all instances of the "fromModule" with instances
935  /// of the "toModule".
936  void replaceInstances(FModuleLike toModule, Operation *fromModule) {
937  // Replace all instances of the other module.
938  auto *fromNode =
939  instanceGraph[::cast<igraph::ModuleOpInterface>(fromModule)];
940  auto *toNode = instanceGraph[toModule];
941  auto toModuleRef = FlatSymbolRefAttr::get(toModule.getModuleNameAttr());
942  for (auto *oldInstRec : llvm::make_early_inc_range(fromNode->uses())) {
943  auto inst = ::cast<InstanceOp>(*oldInstRec->getInstance());
944  inst.setModuleNameAttr(toModuleRef);
945  inst.setPortNamesAttr(toModule.getPortNamesAttr());
946  oldInstRec->getParent()->addInstance(inst, toNode);
947  oldInstRec->erase();
948  }
949  instanceGraph.erase(fromNode);
950  fromModule->erase();
951  }
952 
953  /// Look up the instantiations of the `from` module and create an NLA for each
954  /// one, appending the baseNamepath to each NLA. This is used to add more
955  /// context to an already existing NLA. The `fromModule` is used to indicate
956  /// which module the annotation is coming from before the merge, and will be
957  /// used to create the namepaths.
958  SmallVector<FlatSymbolRefAttr>
959  createNLAs(Operation *fromModule, ArrayRef<Attribute> baseNamepath,
960  SymbolTable::Visibility vis = SymbolTable::Visibility::Private) {
961  // Create an attribute array with a placeholder in the first element, where
962  // the root refence of the NLA will be inserted.
963  SmallVector<Attribute> namepath = {nullptr};
964  namepath.append(baseNamepath.begin(), baseNamepath.end());
965 
966  auto loc = fromModule->getLoc();
967  auto *fromNode = instanceGraph[cast<igraph::ModuleOpInterface>(fromModule)];
968  SmallVector<FlatSymbolRefAttr> nlas;
969  for (auto *instanceRecord : fromNode->uses()) {
970  auto parent = cast<FModuleOp>(*instanceRecord->getParent()->getModule());
971  auto inst = instanceRecord->getInstance();
972  namepath[0] = OpAnnoTarget(inst).getNLAReference(getNamespace(parent));
973  auto arrayAttr = ArrayAttr::get(context, namepath);
974  // Check the NLA cache to see if we already have this NLA.
975  auto &cacheEntry = nlaCache[arrayAttr];
976  if (!cacheEntry) {
977  auto nla = OpBuilder::atBlockBegin(nlaBlock).create<hw::HierPathOp>(
978  loc, "nla", arrayAttr);
979  // Insert it into the symbol table to get a unique name.
980  symbolTable.insert(nla);
981  // Store it in the cache.
982  cacheEntry = nla.getNameAttr();
983  nla.setVisibility(vis);
984  nlaTable->addNLA(nla);
985  }
986  auto nlaRef = FlatSymbolRefAttr::get(cast<StringAttr>(cacheEntry));
987  nlas.push_back(nlaRef);
988  }
989  return nlas;
990  }
991 
992  /// Look up the instantiations of this module and create an NLA for each one.
993  /// This returns an array of symbol references which can be used to reference
994  /// the NLAs.
995  SmallVector<FlatSymbolRefAttr>
996  createNLAs(StringAttr toModuleName, FModuleLike fromModule,
997  SymbolTable::Visibility vis = SymbolTable::Visibility::Private) {
998  return createNLAs(fromModule, FlatSymbolRefAttr::get(toModuleName), vis);
999  }
1000 
1001  /// Clone the annotation for each NLA in a list. The attribute list should
1002  /// have a placeholder for the "circt.nonlocal" field, and `nonLocalIndex`
1003  /// should be the index of this field.
1004  void cloneAnnotation(SmallVectorImpl<FlatSymbolRefAttr> &nlas,
1005  Annotation anno, ArrayRef<NamedAttribute> attributes,
1006  unsigned nonLocalIndex,
1007  SmallVectorImpl<Annotation> &newAnnotations) {
1008  SmallVector<NamedAttribute> mutableAttributes(attributes.begin(),
1009  attributes.end());
1010  for (auto &nla : nlas) {
1011  // Add the new annotation.
1012  mutableAttributes[nonLocalIndex].setValue(nla);
1013  auto dict = DictionaryAttr::getWithSorted(context, mutableAttributes);
1014  // The original annotation records if its a subannotation.
1015  anno.setDict(dict);
1016  newAnnotations.push_back(anno);
1017  }
1018  }
1019 
1020  /// This erases the NLA op, and removes the NLA from every module's NLA map,
1021  /// but it does not delete the NLA reference from the target operation's
1022  /// annotations.
1023  void eraseNLA(hw::HierPathOp nla) {
1024  // Erase the NLA from the leaf module's nlaMap.
1025  targetMap.erase(nla.getNameAttr());
1026  nlaTable->erase(nla);
1027  nlaCache.erase(nla.getNamepathAttr());
1028  symbolTable.erase(nla);
1029  }
1030 
1031  /// Process all NLAs referencing the "from" module to point to the "to"
1032  /// module. This is used after merging two modules together.
1033  void addAnnotationContext(RenameMap &renameMap, FModuleOp toModule,
1034  FModuleOp fromModule) {
1035  auto toName = toModule.getNameAttr();
1036  auto fromName = fromModule.getNameAttr();
1037  // Create a copy of the current NLAs. We will be pushing and removing
1038  // NLAs from this op as we go.
1039  auto moduleNLAs = nlaTable->lookup(fromModule.getNameAttr()).vec();
1040  // Change the NLA to target the toModule.
1041  nlaTable->renameModuleAndInnerRef(toName, fromName, renameMap);
1042  // Now we walk the NLA searching for ones that require more context to be
1043  // added.
1044  for (auto nla : moduleNLAs) {
1045  auto elements = nla.getNamepath().getValue();
1046  // If we don't need to add more context, we're done here.
1047  if (nla.root() != toName)
1048  continue;
1049  // Create the replacement NLAs.
1050  SmallVector<Attribute> namepath(elements.begin(), elements.end());
1051  auto nlaRefs = createNLAs(fromModule, namepath, nla.getVisibility());
1052  // Copy out the targets, because we will be updating the map.
1053  auto &set = targetMap[nla.getSymNameAttr()];
1054  SmallVector<AnnoTarget> targets(set.begin(), set.end());
1055  // Replace the uses of the old NLA with the new NLAs.
1056  for (auto target : targets) {
1057  // We have to clone any annotation which uses the old NLA for each new
1058  // NLA. This array collects the new set of annotations.
1059  SmallVector<Annotation> newAnnotations;
1060  for (auto anno : target.getAnnotations()) {
1061  // Find the non-local field of the annotation.
1062  auto [it, found] = mlir::impl::findAttrSorted(
1063  anno.begin(), anno.end(), nonLocalString);
1064  // If this annotation doesn't use the target NLA, copy it with no
1065  // changes.
1066  if (!found || cast<FlatSymbolRefAttr>(it->getValue()).getAttr() !=
1067  nla.getSymNameAttr()) {
1068  newAnnotations.push_back(anno);
1069  continue;
1070  }
1071  auto nonLocalIndex = std::distance(anno.begin(), it);
1072  // Clone the annotation and add it to the list of new annotations.
1073  cloneAnnotation(nlaRefs, anno,
1074  ArrayRef<NamedAttribute>(anno.begin(), anno.end()),
1075  nonLocalIndex, newAnnotations);
1076  }
1077 
1078  // Apply the new annotations to the operation.
1079  AnnotationSet annotations(newAnnotations, context);
1080  target.setAnnotations(annotations);
1081  // Record that target uses the NLA.
1082  for (auto nla : nlaRefs)
1083  targetMap[nla.getAttr()].insert(target);
1084  }
1085 
1086  // Erase the old NLA and remove it from all breadcrumbs.
1087  eraseNLA(nla);
1088  }
1089  }
1090 
1091  /// Process all the NLAs that the two modules participate in, replacing
1092  /// references to the "from" module with references to the "to" module, and
1093  /// adding more context if necessary.
1094  void rewriteModuleNLAs(RenameMap &renameMap, FModuleOp toModule,
1095  FModuleOp fromModule) {
1096  addAnnotationContext(renameMap, toModule, toModule);
1097  addAnnotationContext(renameMap, toModule, fromModule);
1098  }
1099 
1100  // Update all NLAs which the "from" external module participates in to the
1101  // "toName".
1102  void rewriteExtModuleNLAs(RenameMap &renameMap, StringAttr toName,
1103  StringAttr fromName) {
1104  nlaTable->renameModuleAndInnerRef(toName, fromName, renameMap);
1105  }
1106 
1107  /// Take an annotation, and update it to be a non-local annotation. If the
1108  /// annotation is already non-local and has enough context, it will be skipped
1109  /// for now. Return true if the annotation was made non-local.
1110  bool makeAnnotationNonLocal(StringAttr toModuleName, AnnoTarget to,
1111  FModuleLike fromModule, Annotation anno,
1112  SmallVectorImpl<Annotation> &newAnnotations) {
1113  // Start constructing a new annotation, pushing a "circt.nonLocal" field
1114  // into the correct spot if its not already a non-local annotation.
1115  SmallVector<NamedAttribute> attributes;
1116  int nonLocalIndex = -1;
1117  for (const auto &val : llvm::enumerate(anno)) {
1118  auto attr = val.value();
1119  // Is this field "circt.nonlocal"?
1120  auto compare = attr.getName().compare(nonLocalString);
1121  assert(compare != 0 && "should not pass non-local annotations here");
1122  if (compare == 1) {
1123  // This annotation definitely does not have "circt.nonlocal" field. Push
1124  // an empty place holder for the non-local annotation.
1125  nonLocalIndex = val.index();
1126  attributes.push_back(NamedAttribute(nonLocalString, nonLocalString));
1127  break;
1128  }
1129  // Otherwise push the current attribute and keep searching for the
1130  // "circt.nonlocal" field.
1131  attributes.push_back(attr);
1132  }
1133  if (nonLocalIndex == -1) {
1134  // Push an empty "circt.nonlocal" field to the last slot.
1135  nonLocalIndex = attributes.size();
1136  attributes.push_back(NamedAttribute(nonLocalString, nonLocalString));
1137  } else {
1138  // Copy the remaining annotation fields in.
1139  attributes.append(anno.begin() + nonLocalIndex, anno.end());
1140  }
1141 
1142  // Construct the NLAs if we don't have any yet.
1143  auto nlaRefs = createNLAs(toModuleName, fromModule);
1144  for (auto nla : nlaRefs)
1145  targetMap[nla.getAttr()].insert(to);
1146 
1147  // Clone the annotation for each new NLA.
1148  cloneAnnotation(nlaRefs, anno, attributes, nonLocalIndex, newAnnotations);
1149  return true;
1150  }
1151 
1152  void copyAnnotations(FModuleLike toModule, AnnoTarget to,
1153  FModuleLike fromModule, AnnotationSet annos,
1154  SmallVectorImpl<Annotation> &newAnnotations,
1155  SmallPtrSetImpl<Attribute> &dontTouches) {
1156  for (auto anno : annos) {
1157  if (anno.isClass(dontTouchAnnoClass)) {
1158  // Remove the nonlocal field of the annotation if it has one, since this
1159  // is a sticky annotation.
1160  anno.removeMember("circt.nonlocal");
1161  auto [it, inserted] = dontTouches.insert(anno.getAttr());
1162  if (inserted)
1163  newAnnotations.push_back(anno);
1164  continue;
1165  }
1166  // If the annotation is already non-local, we add it as is. It is already
1167  // added to the target map.
1168  if (auto nla = anno.getMember<FlatSymbolRefAttr>("circt.nonlocal")) {
1169  newAnnotations.push_back(anno);
1170  targetMap[nla.getAttr()].insert(to);
1171  continue;
1172  }
1173  // Otherwise make the annotation non-local and add it to the set.
1174  makeAnnotationNonLocal(toModule.getModuleNameAttr(), to, fromModule, anno,
1175  newAnnotations);
1176  }
1177  }
1178 
1179  /// Merge the annotations of a specific target, either a operation or a port
1180  /// on an operation.
1181  void mergeAnnotations(FModuleLike toModule, AnnoTarget to,
1182  AnnotationSet toAnnos, FModuleLike fromModule,
1183  AnnoTarget from, AnnotationSet fromAnnos) {
1184  // This is a list of all the annotations which will be added to `to`.
1185  SmallVector<Annotation> newAnnotations;
1186 
1187  // We have special case handling of DontTouch to prevent it from being
1188  // turned into a non-local annotation, and to remove duplicates.
1189  llvm::SmallPtrSet<Attribute, 4> dontTouches;
1190 
1191  // Iterate the annotations, transforming most annotations into non-local
1192  // ones.
1193  copyAnnotations(toModule, to, toModule, toAnnos, newAnnotations,
1194  dontTouches);
1195  copyAnnotations(toModule, to, fromModule, fromAnnos, newAnnotations,
1196  dontTouches);
1197 
1198  // Copy over all the new annotations.
1199  if (!newAnnotations.empty())
1200  to.setAnnotations(AnnotationSet(newAnnotations, context));
1201  }
1202 
1203  /// Merge all annotations and port annotations on two operations.
1204  void mergeAnnotations(FModuleLike toModule, Operation *to,
1205  FModuleLike fromModule, Operation *from) {
1206  // Merge op annotations.
1207  mergeAnnotations(toModule, OpAnnoTarget(to), AnnotationSet(to), fromModule,
1208  OpAnnoTarget(from), AnnotationSet(from));
1209 
1210  // Merge port annotations.
1211  if (toModule == to) {
1212  // Merge module port annotations.
1213  for (unsigned i = 0, e = getNumPorts(toModule); i < e; ++i)
1214  mergeAnnotations(toModule, PortAnnoTarget(toModule, i),
1215  AnnotationSet::forPort(toModule, i), fromModule,
1216  PortAnnoTarget(fromModule, i),
1217  AnnotationSet::forPort(fromModule, i));
1218  } else if (auto toMem = dyn_cast<MemOp>(to)) {
1219  // Merge memory port annotations.
1220  auto fromMem = cast<MemOp>(from);
1221  for (unsigned i = 0, e = toMem.getNumResults(); i < e; ++i)
1222  mergeAnnotations(toModule, PortAnnoTarget(toMem, i),
1223  AnnotationSet::forPort(toMem, i), fromModule,
1224  PortAnnoTarget(fromMem, i),
1225  AnnotationSet::forPort(fromMem, i));
1226  }
1227  }
1228 
1229  // Record the symbol name change of the operation or any of its ports when
1230  // merging two operations. The renamed symbols are used to update the
1231  // target of any NLAs. This will add symbols to the "to" operation if needed.
1232  void recordSymRenames(RenameMap &renameMap, FModuleLike toModule,
1233  Operation *to, FModuleLike fromModule,
1234  Operation *from) {
1235  // If the "from" operation has an inner_sym, we need to make sure the
1236  // "to" operation also has an `inner_sym` and then record the renaming.
1237  if (auto fromSym = getInnerSymName(from)) {
1238  auto toSym =
1239  getOrAddInnerSym(to, [&](auto _) -> hw::InnerSymbolNamespace & {
1240  return getNamespace(toModule);
1241  });
1242  renameMap[fromSym] = toSym;
1243  }
1244 
1245  // If there are no port symbols on the "from" operation, we are done here.
1246  auto fromPortSyms = from->getAttrOfType<ArrayAttr>("portSyms");
1247  if (!fromPortSyms || fromPortSyms.empty())
1248  return;
1249  // We have to map each "fromPort" to each "toPort".
1250  auto &moduleNamespace = getNamespace(toModule);
1251  auto portCount = fromPortSyms.size();
1252  auto portNames = to->getAttrOfType<ArrayAttr>("portNames");
1253  auto toPortSyms = to->getAttrOfType<ArrayAttr>("portSyms");
1254 
1255  // Create an array of new port symbols for the "to" operation, copy in the
1256  // old symbols if it has any, create an empty symbol array if it doesn't.
1257  SmallVector<Attribute> newPortSyms;
1258  if (toPortSyms.empty())
1259  newPortSyms.assign(portCount, hw::InnerSymAttr());
1260  else
1261  newPortSyms.assign(toPortSyms.begin(), toPortSyms.end());
1262 
1263  for (unsigned portNo = 0; portNo < portCount; ++portNo) {
1264  // If this fromPort doesn't have a symbol, move on to the next one.
1265  if (!fromPortSyms[portNo])
1266  continue;
1267  auto fromSym = fromPortSyms[portNo].cast<hw::InnerSymAttr>();
1268 
1269  // If this toPort doesn't have a symbol, assign one.
1270  hw::InnerSymAttr toSym;
1271  if (!newPortSyms[portNo]) {
1272  // Get a reasonable base name for the port.
1273  StringRef symName = "inner_sym";
1274  if (portNames)
1275  symName = cast<StringAttr>(portNames[portNo]).getValue();
1276  // Create the symbol and store it into the array.
1277  toSym = hw::InnerSymAttr::get(
1278  StringAttr::get(context, moduleNamespace.newName(symName)));
1279  newPortSyms[portNo] = toSym;
1280  } else
1281  toSym = newPortSyms[portNo].cast<hw::InnerSymAttr>();
1282 
1283  // Record the renaming.
1284  renameMap[fromSym.getSymName()] = toSym.getSymName();
1285  }
1286 
1287  // Commit the new symbol attribute.
1288  cast<FModuleLike>(to).setPortSymbols(newPortSyms);
1289  }
1290 
1291  /// Recursively merge two operations.
1292  // NOLINTNEXTLINE(misc-no-recursion)
1293  void mergeOps(RenameMap &renameMap, FModuleLike toModule, Operation *to,
1294  FModuleLike fromModule, Operation *from) {
1295  // Merge the operation locations.
1296  if (to->getLoc() != from->getLoc())
1297  to->setLoc(mergeLoc(context, to->getLoc(), from->getLoc()));
1298 
1299  // Recurse into any regions.
1300  for (auto regions : llvm::zip(to->getRegions(), from->getRegions()))
1301  mergeRegions(renameMap, toModule, std::get<0>(regions), fromModule,
1302  std::get<1>(regions));
1303 
1304  // Record any inner_sym renamings that happened.
1305  recordSymRenames(renameMap, toModule, to, fromModule, from);
1306 
1307  // Merge the annotations.
1308  mergeAnnotations(toModule, to, fromModule, from);
1309  }
1310 
1311  /// Recursively merge two blocks.
1312  void mergeBlocks(RenameMap &renameMap, FModuleLike toModule, Block &toBlock,
1313  FModuleLike fromModule, Block &fromBlock) {
1314  // Merge the block locations.
1315  for (auto [toArg, fromArg] :
1316  llvm::zip(toBlock.getArguments(), fromBlock.getArguments()))
1317  if (toArg.getLoc() != fromArg.getLoc())
1318  toArg.setLoc(mergeLoc(context, toArg.getLoc(), fromArg.getLoc()));
1319 
1320  for (auto ops : llvm::zip(toBlock, fromBlock))
1321  mergeOps(renameMap, toModule, &std::get<0>(ops), fromModule,
1322  &std::get<1>(ops));
1323  }
1324 
1325  // Recursively merge two regions.
1326  void mergeRegions(RenameMap &renameMap, FModuleLike toModule,
1327  Region &toRegion, FModuleLike fromModule,
1328  Region &fromRegion) {
1329  for (auto blocks : llvm::zip(toRegion, fromRegion))
1330  mergeBlocks(renameMap, toModule, std::get<0>(blocks), fromModule,
1331  std::get<1>(blocks));
1332  }
1333 
1334  MLIRContext *context;
1336  SymbolTable &symbolTable;
1337 
1338  /// Cached nla table analysis.
1339  NLATable *nlaTable = nullptr;
1340 
1341  /// We insert all NLAs to the beginning of this block.
1342  Block *nlaBlock;
1343 
1344  // This maps an NLA to the operations and ports that uses it.
1345  DenseMap<Attribute, llvm::SmallDenseSet<AnnoTarget>> targetMap;
1346 
1347  // This is a cache to avoid creating duplicate NLAs. This maps the ArrayAtr
1348  // of the NLA's path to the name of the NLA which contains it.
1349  DenseMap<Attribute, Attribute> nlaCache;
1350 
1351  // Cached attributes for faster comparisons and attribute building.
1352  StringAttr nonLocalString;
1353  StringAttr classString;
1354 
1355  /// A module namespace cache.
1356  DenseMap<Operation *, hw::InnerSymbolNamespace> moduleNamespaces;
1357 };
1358 
1359 //===----------------------------------------------------------------------===//
1360 // Fixup
1361 //===----------------------------------------------------------------------===//
1362 
1363 /// This fixes up connects when the field names of a bundle type changes. It
1364 /// finds all fields which were previously bulk connected and legalizes it
1365 /// into a connect for each field.
1366 void fixupConnect(ImplicitLocOpBuilder &builder, Value dst, Value src) {
1367  // If the types already match we can emit a connect.
1368  auto dstType = dst.getType();
1369  auto srcType = src.getType();
1370  if (dstType == srcType) {
1371  emitConnect(builder, dst, src);
1372  return;
1373  }
1374  // It must be a bundle type and the field name has changed. We have to
1375  // manually decompose the bulk connect into a connect for each field.
1376  auto dstBundle = type_cast<BundleType>(dstType);
1377  auto srcBundle = type_cast<BundleType>(srcType);
1378  for (unsigned i = 0; i < dstBundle.getNumElements(); ++i) {
1379  auto dstField = builder.create<SubfieldOp>(dst, i);
1380  auto srcField = builder.create<SubfieldOp>(src, i);
1381  if (dstBundle.getElement(i).isFlip) {
1382  std::swap(srcBundle, dstBundle);
1383  std::swap(srcField, dstField);
1384  }
1385  fixupConnect(builder, dstField, srcField);
1386  }
1387 }
1388 
1389 /// This is the root method to fixup module references when a module changes.
1390 /// It matches all the results of "to" module with the results of the "from"
1391 /// module.
1392 void fixupAllModules(InstanceGraph &instanceGraph) {
1393  for (auto *node : instanceGraph) {
1394  auto module = cast<FModuleLike>(*node->getModule());
1395  for (auto *instRec : node->uses()) {
1396  auto inst = instRec->getInstance<InstanceOp>();
1397  // Only handle module instantiations for now.
1398  if (!inst)
1399  continue;
1400  ImplicitLocOpBuilder builder(inst.getLoc(), inst->getContext());
1401  builder.setInsertionPointAfter(inst);
1402  for (unsigned i = 0, e = getNumPorts(module); i < e; ++i) {
1403  auto result = inst.getResult(i);
1404  auto newType = module.getPortType(i);
1405  auto oldType = result.getType();
1406  // If the type has not changed, we don't have to fix up anything.
1407  if (newType == oldType)
1408  continue;
1409  // If the type changed we transform it back to the old type with an
1410  // intermediate wire.
1411  auto wire =
1412  builder.create<WireOp>(oldType, inst.getPortName(i)).getResult();
1413  result.replaceAllUsesWith(wire);
1414  result.setType(newType);
1415  if (inst.getPortDirection(i) == Direction::Out)
1416  fixupConnect(builder, wire, result);
1417  else
1418  fixupConnect(builder, result, wire);
1419  }
1420  }
1421  }
1422 }
1423 
1424 namespace llvm {
1425 /// A DenseMapInfo implementation for `ModuleInfo` that is a pair of
1426 /// llvm::SHA256 hashes, which are represented as std::array<uint8_t, 32>, and
1427 /// an array of string attributes. This allows us to create a DenseMap with
1428 /// `ModuleInfo` as keys.
1429 template <>
1430 struct DenseMapInfo<ModuleInfo> {
1431  static inline ModuleInfo getEmptyKey() {
1432  std::array<uint8_t, 32> key;
1433  std::fill(key.begin(), key.end(), ~0);
1434  return {key, DenseMapInfo<mlir::ArrayAttr>::getEmptyKey()};
1435  }
1436 
1437  static inline ModuleInfo getTombstoneKey() {
1438  std::array<uint8_t, 32> key;
1439  std::fill(key.begin(), key.end(), ~0 - 1);
1440  return {key, DenseMapInfo<mlir::ArrayAttr>::getTombstoneKey()};
1441  }
1442 
1443  static unsigned getHashValue(const ModuleInfo &val) {
1444  // We assume SHA256 is already a good hash and just truncate down to the
1445  // number of bytes we need for DenseMap.
1446  unsigned hash;
1447  std::memcpy(&hash, val.structuralHash.data(), sizeof(unsigned));
1448 
1449  // Combine module names.
1450  return llvm::hash_combine(hash, val.referredModuleNames);
1451  }
1452 
1453  static bool isEqual(const ModuleInfo &lhs, const ModuleInfo &rhs) {
1454  return lhs.structuralHash == rhs.structuralHash &&
1455  lhs.referredModuleNames == rhs.referredModuleNames;
1456  }
1457 };
1458 } // namespace llvm
1459 
1460 //===----------------------------------------------------------------------===//
1461 // DedupPass
1462 //===----------------------------------------------------------------------===//
1463 
1464 namespace {
1465 class DedupPass : public DedupBase<DedupPass> {
1466  void runOnOperation() override {
1467  auto *context = &getContext();
1468  auto circuit = getOperation();
1469  auto &instanceGraph = getAnalysis<InstanceGraph>();
1470  auto *nlaTable = &getAnalysis<NLATable>();
1471  auto &symbolTable = getAnalysis<SymbolTable>();
1472  Deduper deduper(instanceGraph, symbolTable, nlaTable, circuit);
1473  Equivalence equiv(context, instanceGraph);
1474  auto anythingChanged = false;
1475 
1476  // Modules annotated with this should not be considered for deduplication.
1477  auto noDedupClass = StringAttr::get(context, noDedupAnnoClass);
1478 
1479  // Only modules within the same group may be deduplicated.
1480  auto dedupGroupClass = StringAttr::get(context, dedupGroupAnnoClass);
1481 
1482  // A map of all the module moduleInfo that we have calculated so far.
1483  llvm::DenseMap<ModuleInfo, Operation *> moduleInfoToModule;
1484 
1485  // We track the name of the module that each module is deduped into, so that
1486  // we can make sure all modules which are marked "must dedup" with each
1487  // other were all deduped to the same module.
1488  DenseMap<Attribute, StringAttr> dedupMap;
1489 
1490  // We must iterate the modules from the bottom up so that we can properly
1491  // deduplicate the modules. We copy the list of modules into a vector first
1492  // to avoid iterator invalidation while we mutate the instance graph.
1493  SmallVector<FModuleLike, 0> modules(
1494  llvm::map_range(llvm::post_order(&instanceGraph), [](auto *node) {
1495  return cast<FModuleLike>(*node->getModule());
1496  }));
1497 
1498  SmallVector<std::optional<
1499  std::pair<std::array<uint8_t, 32>, SmallVector<StringAttr>>>>
1500  hashesAndModuleNames(modules.size());
1501  StructuralHasherSharedConstants hasherConstants(&getContext());
1502 
1503  // Calculate module information parallelly.
1504  auto result = mlir::failableParallelForEach(
1505  context, llvm::seq(modules.size()), [&](unsigned idx) {
1506  auto module = modules[idx];
1507  AnnotationSet annotations(module);
1508  // If the module is marked with NoDedup, just skip it.
1509  if (annotations.hasAnnotation(noDedupClass))
1510  return success();
1511 
1512  // If the module has input RefType ports, also skip it.
1513  if (llvm::any_of(module.getPorts(), [&](PortInfo port) {
1514  return type_isa<RefType>(port.type) && port.isInput();
1515  }))
1516  return success();
1517 
1518  // Only dedup extmodule's with defname.
1519  if (auto ext = dyn_cast<FExtModuleOp>(*module);
1520  ext && !ext.getDefname().has_value())
1521  return success();
1522 
1523  // If module has symbol (name) that must be preserved even if unused,
1524  // skip it. All symbol uses must be supported, which is not true if
1525  // non-private.
1526  if (!module.isPrivate() || !module.canDiscardOnUseEmpty()) {
1527  return success();
1528  }
1529 
1530  // Explicitly skip class-like modules. This is presently unreachable
1531  // due to above and current implementation but check anyway as dedup
1532  // code does not handle these or object operations.
1533  if (isa<ClassLike>(*module)) {
1534  return success();
1535  }
1536 
1537  llvm::SmallSetVector<StringAttr, 1> groups;
1538  for (auto annotation : annotations) {
1539  if (annotation.getClass() == dedupGroupClass)
1540  groups.insert(annotation.getMember<StringAttr>("group"));
1541  }
1542  if (groups.size() > 1) {
1543  module.emitError("module belongs to multiple dedup groups: ")
1544  << groups;
1545  return failure();
1546  }
1547  auto dedupGroup = groups.empty() ? StringAttr() : groups.front();
1548 
1549  StructuralHasher hasher(hasherConstants);
1550  // Calculate the hash of the module and referred module names.
1551  hashesAndModuleNames[idx] =
1552  hasher.getHashAndModuleNames(module, dedupGroup);
1553  return success();
1554  });
1555 
1556  if (result.failed())
1557  return signalPassFailure();
1558 
1559  for (auto [i, module] : llvm::enumerate(modules)) {
1560  auto moduleName = module.getModuleNameAttr();
1561  auto &hashAndModuleNamesOpt = hashesAndModuleNames[i];
1562  // If the hash was not calculated, we need to skip it.
1563  if (!hashAndModuleNamesOpt) {
1564  // We record it in the dedup map to help detect errors when the user
1565  // marks the module as both NoDedup and MustDedup. We do not record this
1566  // module in the hasher to make sure no other module dedups "into" this
1567  // one.
1568  dedupMap[moduleName] = moduleName;
1569  continue;
1570  }
1571 
1572  // Replace module names referred in the module with new names.
1573  SmallVector<mlir::Attribute> names;
1574  for (auto oldModuleName : hashAndModuleNamesOpt->second) {
1575  auto newModuleName = dedupMap[oldModuleName];
1576  names.push_back(newModuleName);
1577  }
1578 
1579  // Create a module info to use it as a key.
1580  ModuleInfo moduleInfo{hashAndModuleNamesOpt->first,
1581  mlir::ArrayAttr::get(module.getContext(), names)};
1582 
1583  // Check if there a module with the same hash.
1584  auto it = moduleInfoToModule.find(moduleInfo);
1585  if (it != moduleInfoToModule.end()) {
1586  auto original = cast<FModuleLike>(it->second);
1587  // Record the group ID of the other module.
1588  dedupMap[moduleName] = original.getModuleNameAttr();
1589  deduper.dedup(original, module);
1590  ++erasedModules;
1591  anythingChanged = true;
1592  continue;
1593  }
1594  // Any module not deduplicated must be recorded.
1595  deduper.record(module);
1596  // Add the module to a new dedup group.
1597  dedupMap[moduleName] = moduleName;
1598  // Record the module info.
1599  moduleInfoToModule[moduleInfo] = module;
1600  }
1601 
1602  // This part verifies that all modules marked by "MustDedup" have been
1603  // properly deduped with each other. For this check to succeed, all modules
1604  // have to been deduped to the same module. It is possible that a module was
1605  // deduped with the wrong thing.
1606 
1607  auto failed = false;
1608  // This parses the module name out of a target string.
1609  auto parseModule = [&](Attribute path) -> StringAttr {
1610  // Each module is listed as a target "~Circuit|Module" which we have to
1611  // parse.
1612  auto [_, rhs] = cast<StringAttr>(path).getValue().split('|');
1613  return StringAttr::get(context, rhs);
1614  };
1615  // This gets the name of the module which the current module was deduped
1616  // with. If the named module isn't in the map, then we didn't encounter it
1617  // in the circuit.
1618  auto getLead = [&](StringAttr module) -> StringAttr {
1619  auto it = dedupMap.find(module);
1620  if (it == dedupMap.end()) {
1621  auto diag = emitError(circuit.getLoc(),
1622  "MustDeduplicateAnnotation references module ")
1623  << module << " which does not exist";
1624  failed = true;
1625  return 0;
1626  }
1627  return it->second;
1628  };
1629 
1630  AnnotationSet::removeAnnotations(circuit, [&](Annotation annotation) {
1631  // If we have already failed, don't process any more annotations.
1632  if (failed)
1633  return false;
1634  if (!annotation.isClass(mustDedupAnnoClass))
1635  return false;
1636  auto modules = annotation.getMember<ArrayAttr>("modules");
1637  if (!modules) {
1638  emitError(circuit.getLoc(),
1639  "MustDeduplicateAnnotation missing \"modules\" member");
1640  failed = true;
1641  return false;
1642  }
1643  // Empty module list has nothing to process.
1644  if (modules.size() == 0)
1645  return true;
1646  // Get the first element.
1647  auto firstModule = parseModule(modules[0]);
1648  auto firstLead = getLead(firstModule);
1649  if (failed)
1650  return false;
1651  // Verify that the remaining elements are all the same as the first.
1652  for (auto attr : modules.getValue().drop_front()) {
1653  auto nextModule = parseModule(attr);
1654  auto nextLead = getLead(nextModule);
1655  if (failed)
1656  return false;
1657  if (firstLead != nextLead) {
1658  auto diag = emitError(circuit.getLoc(), "module ")
1659  << nextModule << " not deduplicated with " << firstModule;
1660  auto a = instanceGraph.lookup(firstLead)->getModule();
1661  auto b = instanceGraph.lookup(nextLead)->getModule();
1662  equiv.check(diag, a, b);
1663  failed = true;
1664  return false;
1665  }
1666  }
1667  return true;
1668  });
1669  if (failed)
1670  return signalPassFailure();
1671 
1672  for (auto module : circuit.getOps<FModuleOp>())
1673  AnnotationSet::removeAnnotations(module, dedupGroupClass);
1674 
1675  // Walk all the modules and fixup the instance operation to return the
1676  // correct type. We delay this fixup until the end because doing it early
1677  // can block the deduplication of the parent modules.
1678  fixupAllModules(instanceGraph);
1679 
1680  markAnalysesPreserved<NLATable>();
1681  if (!anythingChanged)
1682  markAllAnalysesPreserved();
1683  }
1684 };
1685 } // end anonymous namespace
1686 
1687 std::unique_ptr<mlir::Pass> circt::firrtl::createDedupPass() {
1688  return std::make_unique<DedupPass>();
1689 }
lowerAnnotationsNoRefTypePorts FirtoolPreserveValuesMode value
Definition: Firtool.cpp:95
assert(baseType &&"element must be base type")
static Attribute getAttr(ArrayRef< NamedAttribute > attrs, StringRef name)
Get an attribute by name from a list of named attributes.
Definition: FIRRTLOps.cpp:3429
static Location mergeLoc(MLIRContext *context, Location to, Location from)
Definition: Dedup.cpp:790
llvm::raw_ostream & printHex(llvm::raw_ostream &stream, ArrayRef< uint8_t > bytes)
Definition: Dedup.cpp:46
void fixupConnect(ImplicitLocOpBuilder &builder, Value dst, Value src)
This fixes up connects when the field names of a bundle type changes.
Definition: Dedup.cpp:1366
llvm::raw_ostream & printHash(llvm::raw_ostream &stream, llvm::SHA256 &data)
Definition: Dedup.cpp:52
void fixupAllModules(InstanceGraph &instanceGraph)
This is the root method to fixup module references when a module changes.
Definition: Dedup.cpp:1392
static void mergeRegions(Region *region1, Region *region2)
Definition: HWCleanup.cpp:70
Builder builder
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.
Annotation getAnnotation(StringRef className) const
If this annotation set has an annotation with the specified class name, return it.
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.
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.
Definition: NLATable.h:29
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:53
StringRef toString(Direction direction)
SmallVector< Direction > unpackAttribute(IntegerAttr directions)
Turn a packed representation of port attributes into a vector that can be worked with.
FieldRef getFieldRefFromValue(Value value, bool lookThroughCasts=false)
Get the FieldRef from a value.
constexpr const char * mustDedupAnnoClass
std::pair< hw::InnerSymAttr, StringAttr > getOrAddInnerSym(MLIRContext *context, hw::InnerSymAttr attr, uint64_t fieldID, llvm::function_ref< hw::InnerSymbolNamespace &()> getNamespace)
Ensure that the the InnerSymAttr has a symbol on the field specified.
constexpr const char * noDedupAnnoClass
size_t getNumPorts(Operation *op)
Return the number of ports in a module-like thing (modules, memories, etc)
Definition: FIRRTLOps.cpp:271
constexpr const char * dedupGroupAnnoClass
std::unique_ptr< mlir::Pass > createDedupPass()
Definition: Dedup.cpp:1687
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.
Definition: FIRRTLUtils.cpp:23
StringAttr getInnerSymName(Operation *op)
Return the StringAttr for the inner_sym name, if it exists.
Definition: FIRRTLOps.h:108
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
Definition: DebugAnalysis.h:21
MLIRContext * context
Definition: Dedup.cpp:1334
Block * nlaBlock
We insert all NLAs to the beginning of this block.
Definition: Dedup.cpp:1342
void recordAnnotations(Operation *op)
Record all targets which use an NLA.
Definition: Dedup.cpp:920
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...
Definition: Dedup.cpp:1023
void mergeAnnotations(FModuleLike toModule, Operation *to, FModuleLike fromModule, Operation *from)
Merge all annotations and port annotations on two operations.
Definition: Dedup.cpp:1204
void replaceInstances(FModuleLike toModule, Operation *fromModule)
This deletes and replaces all instances of the "fromModule" with instances of the "toModule".
Definition: Dedup.cpp:936
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.
Definition: Dedup.cpp:996
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...
Definition: Dedup.cpp:894
void rewriteExtModuleNLAs(RenameMap &renameMap, StringAttr toName, StringAttr fromName)
Definition: Dedup.cpp:1102
void mergeRegions(RenameMap &renameMap, FModuleLike toModule, Region &toRegion, FModuleLike fromModule, Region &fromRegion)
Definition: Dedup.cpp:1326
void dedup(FModuleLike toModule, FModuleLike fromModule)
Remove the "fromModule", and replace all references to it with the "toModule".
Definition: Dedup.cpp:859
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...
Definition: Dedup.cpp:1094
void recordAnnotations(AnnoTarget target)
For a specific annotation target, record all the unique NLAs which target it in the targetMap.
Definition: Dedup.cpp:913
void cloneAnnotation(SmallVectorImpl< FlatSymbolRefAttr > &nlas, Annotation anno, ArrayRef< NamedAttribute > attributes, unsigned nonLocalIndex, SmallVectorImpl< Annotation > &newAnnotations)
Clone the annotation for each NLA in a list.
Definition: Dedup.cpp:1004
void recordSymRenames(RenameMap &renameMap, FModuleLike toModule, Operation *to, FModuleLike fromModule, Operation *from)
Definition: Dedup.cpp:1232
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.
Definition: Dedup.cpp:1181
StringAttr nonLocalString
Definition: Dedup.cpp:1352
SymbolTable & symbolTable
Definition: Dedup.cpp:1336
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...
Definition: Dedup.cpp:959
void mergeOps(RenameMap &renameMap, FModuleLike toModule, Operation *to, FModuleLike fromModule, Operation *from)
Recursively merge two operations.
Definition: Dedup.cpp:1293
hw::InnerSymbolNamespace & getNamespace(Operation *module)
Get a cached namespace for a module.
Definition: Dedup.cpp:906
DenseMap< Operation *, hw::InnerSymbolNamespace > moduleNamespaces
A module namespace cache.
Definition: Dedup.cpp:1356
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.
Definition: Dedup.cpp:1110
InstanceGraph & instanceGraph
Definition: Dedup.cpp:1335
void mergeBlocks(RenameMap &renameMap, FModuleLike toModule, Block &toBlock, FModuleLike fromModule, Block &fromBlock)
Recursively merge two blocks.
Definition: Dedup.cpp:1312
DenseMap< Attribute, llvm::SmallDenseSet< AnnoTarget > > targetMap
Definition: Dedup.cpp:1345
StringAttr classString
Definition: Dedup.cpp:1353
void copyAnnotations(FModuleLike toModule, AnnoTarget to, FModuleLike fromModule, AnnotationSet annos, SmallVectorImpl< Annotation > &newAnnotations, SmallPtrSetImpl< Attribute > &dontTouches)
Definition: Dedup.cpp:1152
Deduper(InstanceGraph &instanceGraph, SymbolTable &symbolTable, NLATable *nlaTable, CircuitOp circuit)
Definition: Dedup.cpp:843
void addAnnotationContext(RenameMap &renameMap, FModuleOp toModule, FModuleOp fromModule)
Process all NLAs referencing the "from" module to point to the "to" module.
Definition: Dedup.cpp:1033
DenseMap< StringAttr, StringAttr > RenameMap
Definition: Dedup.cpp:841
DenseMap< Attribute, Attribute > nlaCache
Definition: Dedup.cpp:1349
const hw::InnerSymbolTable & a
Definition: Dedup.cpp:336
ModuleData(const hw::InnerSymbolTable &a, const hw::InnerSymbolTable &b)
Definition: Dedup.cpp:333
const hw::InnerSymbolTable & b
Definition: Dedup.cpp:337
This class is for reporting differences between two modules which should have been deduplicated.
Definition: Dedup.cpp:315
LogicalResult check(InFlightDiagnostic &diag, InstanceOp a, InstanceOp b)
Definition: Dedup.cpp:620
DenseSet< Attribute > nonessentialAttributes
Definition: Dedup.cpp:777
std::string prettyPrint(Attribute attr)
Definition: Dedup.cpp:340
LogicalResult check(InFlightDiagnostic &diag, Operation *a, IntegerAttr aAttr, Operation *b, IntegerAttr bAttr)
Definition: Dedup.cpp:509
LogicalResult check(InFlightDiagnostic &diag, ModuleData &data, Operation *a, Block &aBlock, Operation *b, Block &bBlock)
Definition: Dedup.cpp:411
LogicalResult check(InFlightDiagnostic &diag, const Twine &message, Operation *a, Type aType, Operation *b, Type bType)
Definition: Dedup.cpp:385
StringAttr noDedupClass
Definition: Dedup.cpp:773
LogicalResult check(InFlightDiagnostic &diag, ModuleData &data, Operation *a, DictionaryAttr aDict, Operation *b, DictionaryAttr bDict)
Definition: Dedup.cpp:535
LogicalResult check(InFlightDiagnostic &diag, ModuleData &data, Operation *a, Region &aRegion, Operation *b, Region &bRegion)
Definition: Dedup.cpp:489
StringAttr portDirectionsAttr
Definition: Dedup.cpp:771
LogicalResult check(InFlightDiagnostic &diag, const Twine &message, Operation *a, BundleType aType, Operation *b, BundleType bType)
Definition: Dedup.cpp:356
StringAttr dedupGroupClass
Definition: Dedup.cpp:775
LogicalResult check(InFlightDiagnostic &diag, ModuleData &data, Operation *a, Operation *b)
Definition: Dedup.cpp:640
Equivalence(MLIRContext *context, InstanceGraph &instanceGraph)
Definition: Dedup.cpp:316
InstanceGraph & instanceGraph
Definition: Dedup.cpp:778
void check(InFlightDiagnostic &diag, Operation *a, Operation *b)
Definition: Dedup.cpp:727
std::array< uint8_t, 32 > structuralHash
Definition: Dedup.cpp:69
mlir::ArrayAttr referredModuleNames
Definition: Dedup.cpp:71
This struct contains constant string attributes shared across different threads.
Definition: Dedup.cpp:81
DenseSet< Attribute > nonessentialAttributes
Definition: Dedup.cpp:108
StructuralHasherSharedConstants(MLIRContext *context)
Definition: Dedup.cpp:82
void update(Operation *op, DictionaryAttr dict)
Hash the top level attribute dictionary of the operation.
Definition: Dedup.cpp:200
void update(BlockArgument arg)
Definition: Dedup.cpp:158
std::pair< std::array< uint8_t, 32 >, SmallVector< StringAttr > > getHashAndModuleNames(FModuleLike module, StringAttr group)
Definition: Dedup.cpp:116
void update(Type type)
Definition: Dedup.cpp:147
void update(const void *pointer)
Definition: Dedup.cpp:125
void update(InnerRefAttr attr)
Definition: Dedup.cpp:189
void update(Operation *op)
Definition: Dedup.cpp:273
void record(void *address)
Definition: Dedup.cpp:153
void update(const SymbolTarget &target)
Definition: Dedup.cpp:184
llvm::SHA256 sha
Definition: Dedup.cpp:306
void update(Operation *op, hw::InnerSymAttr attr)
Definition: Dedup.cpp:172
DenseMap< StringAttr, SymbolTarget > innerSymTargets
Definition: Dedup.cpp:296
void update(size_t value)
Definition: Dedup.cpp:130
SmallVector< mlir::StringAttr > referredModuleNames
Definition: Dedup.cpp:299
void update(Block &block)
Definition: Dedup.cpp:258
void update(BundleType type)
Definition: Dedup.cpp:138
void update(OpResult result)
Definition: Dedup.cpp:160
void update(OpOperand &operand)
Definition: Dedup.cpp:165
StructuralHasher(const StructuralHasherSharedConstants &constants)
Definition: Dedup.cpp:112
void update(TypeID typeID)
Definition: Dedup.cpp:135
const StructuralHasherSharedConstants & constants
Definition: Dedup.cpp:302
void update(Value value, hw::InnerSymAttr attr)
Definition: Dedup.cpp:178
DenseMap< void *, unsigned > indices
Definition: Dedup.cpp:293
void update(mlir::OperationName name)
Definition: Dedup.cpp:267
uint64_t fieldID
Definition: Dedup.cpp:76
uint64_t index
Definition: Dedup.cpp:75
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 ModuleInfo getEmptyKey()
Definition: Dedup.cpp:1431
static ModuleInfo getTombstoneKey()
Definition: Dedup.cpp:1437
static unsigned getHashValue(const ModuleInfo &val)
Definition: Dedup.cpp:1443
static bool isEqual(const ModuleInfo &lhs, const ModuleInfo &rhs)
Definition: Dedup.cpp:1453