CIRCT  19.0.0git
FIRRTLReductions.cpp
Go to the documentation of this file.
1 //===- FIRRTLReductions.cpp - Reduction patterns for the FIRRTL dialect ---===//
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 
17 #include "mlir/IR/ImplicitLocOpBuilder.h"
18 #include "llvm/ADT/APSInt.h"
19 #include "llvm/ADT/SmallSet.h"
20 #include "llvm/Support/Debug.h"
21 
22 #define DEBUG_TYPE "firrtl-reductions"
23 
24 using namespace mlir;
25 using namespace circt;
26 
27 //===----------------------------------------------------------------------===//
28 // Utilities
29 //===----------------------------------------------------------------------===//
30 
31 namespace detail {
32 /// A utility doing lazy construction of `SymbolTable`s and `SymbolUserMap`s,
33 /// which is handy for reductions that need to look up a lot of symbols.
34 struct SymbolCache {
35  SymbolTable &getSymbolTable(Operation *op) {
36  return tables.getSymbolTable(op);
37  }
38  SymbolTable &getNearestSymbolTable(Operation *op) {
39  return getSymbolTable(SymbolTable::getNearestSymbolTable(op));
40  }
41 
42  SymbolUserMap &getSymbolUserMap(Operation *op) {
43  auto it = userMaps.find(op);
44  if (it != userMaps.end())
45  return it->second;
46  return userMaps.insert({op, SymbolUserMap(tables, op)}).first->second;
47  }
48  SymbolUserMap &getNearestSymbolUserMap(Operation *op) {
49  return getSymbolUserMap(SymbolTable::getNearestSymbolTable(op));
50  }
51 
52  void clear() {
53  tables = SymbolTableCollection();
54  userMaps.clear();
55  }
56 
57 private:
58  SymbolTableCollection tables;
60 };
61 } // namespace detail
62 
63 /// Utility to easily get the instantiated firrtl::FModuleOp or an empty
64 /// optional in case another type of module is instantiated.
65 static std::optional<firrtl::FModuleOp>
66 findInstantiatedModule(firrtl::InstanceOp instOp,
67  ::detail::SymbolCache &symbols) {
68  auto *tableOp = SymbolTable::getNearestSymbolTable(instOp);
69  auto moduleOp = dyn_cast<firrtl::FModuleOp>(
70  instOp.getReferencedOperation(symbols.getSymbolTable(tableOp)));
71  return moduleOp ? std::optional(moduleOp) : std::nullopt;
72 }
73 
74 /// Utility to track the transitive size of modules.
76  void clear() { moduleSizes.clear(); }
77 
78  uint64_t getModuleSize(Operation *module, ::detail::SymbolCache &symbols) {
79  if (auto it = moduleSizes.find(module); it != moduleSizes.end())
80  return it->second;
81  uint64_t size = 1;
82  module->walk([&](Operation *op) {
83  size += 1;
84  if (auto instOp = dyn_cast<firrtl::InstanceOp>(op))
85  if (auto instModule = findInstantiatedModule(instOp, symbols))
86  size += getModuleSize(*instModule, symbols);
87  });
88  moduleSizes.insert({module, size});
89  return size;
90  }
91 
92 private:
93  llvm::DenseMap<Operation *, uint64_t> moduleSizes;
94 };
95 
96 /// Check that all connections to a value are invalids.
97 static bool onlyInvalidated(Value arg) {
98  return llvm::all_of(arg.getUses(), [](OpOperand &use) {
99  auto *op = use.getOwner();
100  if (!isa<firrtl::ConnectOp, firrtl::StrictConnectOp>(op))
101  return false;
102  if (use.getOperandNumber() != 0)
103  return false;
104  if (!op->getOperand(1).getDefiningOp<firrtl::InvalidValueOp>())
105  return false;
106  return true;
107  });
108 }
109 
110 /// A tracker for track NLAs affected by a reduction. Performs the necessary
111 /// cleanup steps in order to maintain IR validity after the reduction has
112 /// applied. For example, removing an instance that forms part of an NLA path
113 /// requires that NLA to be removed as well.
114 struct NLARemover {
115  /// Clear the set of marked NLAs. Call this before attempting a reduction.
116  void clear() { nlasToRemove.clear(); }
117 
118  /// Remove all marked annotations. Call this after applying a reduction in
119  /// order to validate the IR.
120  void remove(mlir::ModuleOp module) {
121  unsigned numRemoved = 0;
122  (void)numRemoved;
123  for (Operation &rootOp : *module.getBody()) {
124  if (!isa<firrtl::CircuitOp>(&rootOp))
125  continue;
126  SymbolTable symbolTable(&rootOp);
127  for (auto sym : nlasToRemove) {
128  if (auto *op = symbolTable.lookup(sym)) {
129  ++numRemoved;
130  op->erase();
131  }
132  }
133  }
134  LLVM_DEBUG({
135  unsigned numLost = nlasToRemove.size() - numRemoved;
136  if (numRemoved > 0 || numLost > 0) {
137  llvm::dbgs() << "Removed " << numRemoved << " NLAs";
138  if (numLost > 0)
139  llvm::dbgs() << " (" << numLost << " no longer there)";
140  llvm::dbgs() << "\n";
141  }
142  });
143  }
144 
145  /// Mark all NLAs referenced in the given annotation as to be removed. This
146  /// can be an entire array or dictionary of annotations, and the function will
147  /// descend into child annotations appropriately.
148  void markNLAsInAnnotation(Attribute anno) {
149  if (auto dict = dyn_cast<DictionaryAttr>(anno)) {
150  if (auto field = dict.getAs<FlatSymbolRefAttr>("circt.nonlocal"))
151  nlasToRemove.insert(field.getAttr());
152  for (auto namedAttr : dict)
153  markNLAsInAnnotation(namedAttr.getValue());
154  } else if (auto array = dyn_cast<ArrayAttr>(anno)) {
155  for (auto attr : array)
156  markNLAsInAnnotation(attr);
157  }
158  }
159 
160  /// Mark all NLAs referenced in an operation. Also traverses all nested
161  /// operations. Call this before removing an operation, to mark any associated
162  /// NLAs as to be removed as well.
163  void markNLAsInOperation(Operation *op) {
164  op->walk([&](Operation *op) {
165  if (auto annos = op->getAttrOfType<ArrayAttr>("annotations"))
166  markNLAsInAnnotation(annos);
167  });
168  }
169 
170  /// The set of NLAs to remove, identified by their symbol.
171  llvm::DenseSet<StringAttr> nlasToRemove;
172 };
173 
174 //===----------------------------------------------------------------------===//
175 // Reduction patterns
176 //===----------------------------------------------------------------------===//
177 
178 /// A sample reduction pattern that maps `firrtl.module` to `firrtl.extmodule`.
179 struct FIRRTLModuleExternalizer : public OpReduction<firrtl::FModuleOp> {
180  void beforeReduction(mlir::ModuleOp op) override {
181  nlaRemover.clear();
182  symbols.clear();
183  moduleSizes.clear();
184  }
185  void afterReduction(mlir::ModuleOp op) override { nlaRemover.remove(op); }
186 
187  uint64_t match(firrtl::FModuleOp module) override {
188  return moduleSizes.getModuleSize(module, symbols);
189  }
190 
191  LogicalResult rewrite(firrtl::FModuleOp module) override {
192  nlaRemover.markNLAsInOperation(module);
193  OpBuilder builder(module);
194  builder.create<firrtl::FExtModuleOp>(
195  module->getLoc(),
196  module->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()),
197  module.getConventionAttr(), module.getPorts(), StringRef(),
198  module.getAnnotationsAttr());
199  module->erase();
200  return success();
201  }
202 
203  std::string getName() const override { return "firrtl-module-externalizer"; }
204 
208 };
209 
210 /// Invalidate all the leaf fields of a value with a given flippedness by
211 /// connecting an invalid value to them. This is useful for ensuring that all
212 /// output ports of an instance or memory (including those nested in bundles)
213 /// are properly invalidated.
214 static void invalidateOutputs(ImplicitLocOpBuilder &builder, Value value,
215  SmallDenseMap<Type, Value, 8> &invalidCache,
216  bool flip = false) {
217  auto type = value.getType().dyn_cast<firrtl::FIRRTLType>();
218  if (!type)
219  return;
220 
221  // Descend into bundles by creating subfield ops.
222  if (auto bundleType = type.dyn_cast<firrtl::BundleType>()) {
223  for (auto element : llvm::enumerate(bundleType.getElements())) {
224  auto subfield =
225  builder.createOrFold<firrtl::SubfieldOp>(value, element.index());
226  invalidateOutputs(builder, subfield, invalidCache,
227  flip ^ element.value().isFlip);
228  if (subfield.use_empty())
229  subfield.getDefiningOp()->erase();
230  }
231  return;
232  }
233 
234  // Descend into vectors by creating subindex ops.
235  if (auto vectorType = type.dyn_cast<firrtl::FVectorType>()) {
236  for (unsigned i = 0, e = vectorType.getNumElements(); i != e; ++i) {
237  auto subindex = builder.createOrFold<firrtl::SubindexOp>(value, i);
238  invalidateOutputs(builder, subindex, invalidCache, flip);
239  if (subindex.use_empty())
240  subindex.getDefiningOp()->erase();
241  }
242  return;
243  }
244 
245  // Only drive outputs.
246  if (flip)
247  return;
248  Value invalid = invalidCache.lookup(type);
249  if (!invalid) {
250  invalid = builder.create<firrtl::InvalidValueOp>(type);
251  invalidCache.insert({type, invalid});
252  }
253  builder.create<firrtl::ConnectOp>(value, invalid);
254 }
255 
256 /// Connect a value to every leave of a destination value.
257 static void connectToLeafs(ImplicitLocOpBuilder &builder, Value dest,
258  Value value) {
259  auto type = dest.getType().dyn_cast<firrtl::FIRRTLBaseType>();
260  if (!type)
261  return;
262  if (auto bundleType = type.dyn_cast<firrtl::BundleType>()) {
263  for (auto element : llvm::enumerate(bundleType.getElements()))
265  builder.create<firrtl::SubfieldOp>(dest, element.index()),
266  value);
267  return;
268  }
269  if (auto vectorType = type.dyn_cast<firrtl::FVectorType>()) {
270  for (unsigned i = 0, e = vectorType.getNumElements(); i != e; ++i)
271  connectToLeafs(builder, builder.create<firrtl::SubindexOp>(dest, i),
272  value);
273  return;
274  }
275  auto valueType = value.getType().dyn_cast<firrtl::FIRRTLBaseType>();
276  if (!valueType)
277  return;
278  auto destWidth = type.getBitWidthOrSentinel();
279  auto valueWidth = valueType ? valueType.getBitWidthOrSentinel() : -1;
280  if (destWidth >= 0 && valueWidth >= 0 && destWidth < valueWidth)
281  value = builder.create<firrtl::HeadPrimOp>(value, destWidth);
282  if (!isa<firrtl::UIntType>(type)) {
283  if (isa<firrtl::SIntType>(type))
284  value = builder.create<firrtl::AsSIntPrimOp>(value);
285  else
286  return;
287  }
288  builder.create<firrtl::ConnectOp>(dest, value);
289 }
290 
291 /// Reduce all leaf fields of a value through an XOR tree.
292 static void reduceXor(ImplicitLocOpBuilder &builder, Value &into, Value value) {
293  auto type = value.getType().dyn_cast<firrtl::FIRRTLType>();
294  if (!type)
295  return;
296  if (auto bundleType = type.dyn_cast<firrtl::BundleType>()) {
297  for (auto element : llvm::enumerate(bundleType.getElements()))
298  reduceXor(
299  builder, into,
300  builder.createOrFold<firrtl::SubfieldOp>(value, element.index()));
301  return;
302  }
303  if (auto vectorType = type.dyn_cast<firrtl::FVectorType>()) {
304  for (unsigned i = 0, e = vectorType.getNumElements(); i != e; ++i)
305  reduceXor(builder, into,
306  builder.createOrFold<firrtl::SubindexOp>(value, i));
307  return;
308  }
309  if (!isa<firrtl::UIntType>(type)) {
310  if (isa<firrtl::SIntType>(type))
311  value = builder.create<firrtl::AsUIntPrimOp>(value);
312  else
313  return;
314  }
315  into = into ? builder.createOrFold<firrtl::XorPrimOp>(into, value) : value;
316 }
317 
318 /// A sample reduction pattern that maps `firrtl.instance` to a set of
319 /// invalidated wires. This often shortcuts a long iterative process of connect
320 /// invalidation, module externalization, and wire stripping
321 struct InstanceStubber : public OpReduction<firrtl::InstanceOp> {
322  void beforeReduction(mlir::ModuleOp op) override {
323  erasedInsts.clear();
324  erasedModules.clear();
325  symbols.clear();
326  nlaRemover.clear();
327  moduleSizes.clear();
328  }
329  void afterReduction(mlir::ModuleOp op) override {
330  // Look into deleted modules to find additional instances that are no longer
331  // instantiated anywhere.
332  SmallVector<Operation *> worklist;
333  auto deadInsts = erasedInsts;
334  for (auto *op : erasedModules)
335  worklist.push_back(op);
336  while (!worklist.empty()) {
337  auto *op = worklist.pop_back_val();
338  auto *tableOp = SymbolTable::getNearestSymbolTable(op);
339  op->walk([&](firrtl::InstanceOp instOp) {
340  auto moduleOp = cast<firrtl::FModuleLike>(
341  instOp.getReferencedOperation(symbols.getSymbolTable(tableOp)));
342  deadInsts.insert(instOp);
343  if (llvm::all_of(
344  symbols.getSymbolUserMap(tableOp).getUsers(moduleOp),
345  [&](Operation *user) { return deadInsts.contains(user); })) {
346  LLVM_DEBUG(llvm::dbgs() << "- Removing transitively unused module `"
347  << moduleOp.getModuleName() << "`\n");
348  erasedModules.insert(moduleOp);
349  worklist.push_back(moduleOp);
350  }
351  });
352  }
353 
354  for (auto *op : erasedInsts)
355  op->erase();
356  for (auto *op : erasedModules)
357  op->erase();
358  nlaRemover.remove(op);
359  }
360 
361  uint64_t match(firrtl::InstanceOp instOp) override {
362  if (auto fmoduleOp = findInstantiatedModule(instOp, symbols))
363  return moduleSizes.getModuleSize(*fmoduleOp, symbols);
364  return 0;
365  }
366 
367  LogicalResult rewrite(firrtl::InstanceOp instOp) override {
368  LLVM_DEBUG(llvm::dbgs()
369  << "Stubbing instance `" << instOp.getName() << "`\n");
370  ImplicitLocOpBuilder builder(instOp.getLoc(), instOp);
371  SmallDenseMap<Type, Value, 8> invalidCache;
372  for (unsigned i = 0, e = instOp.getNumResults(); i != e; ++i) {
373  auto result = instOp.getResult(i);
374  auto name = builder.getStringAttr(Twine(instOp.getName()) + "_" +
375  instOp.getPortNameStr(i));
376  auto wire =
377  builder
378  .create<firrtl::WireOp>(result.getType(), name,
379  firrtl::NameKindEnum::DroppableName,
380  instOp.getPortAnnotation(i), StringAttr{})
381  .getResult();
382  invalidateOutputs(builder, wire, invalidCache,
383  instOp.getPortDirection(i) == firrtl::Direction::In);
384  result.replaceAllUsesWith(wire);
385  }
386  auto *tableOp = SymbolTable::getNearestSymbolTable(instOp);
387  auto moduleOp = cast<firrtl::FModuleLike>(
388  instOp.getReferencedOperation(symbols.getSymbolTable(tableOp)));
389  nlaRemover.markNLAsInOperation(instOp);
390  erasedInsts.insert(instOp);
391  if (llvm::all_of(
392  symbols.getSymbolUserMap(tableOp).getUsers(moduleOp),
393  [&](Operation *user) { return erasedInsts.contains(user); })) {
394  LLVM_DEBUG(llvm::dbgs() << "- Removing now unused module `"
395  << moduleOp.getModuleName() << "`\n");
396  erasedModules.insert(moduleOp);
397  }
398  return success();
399  }
400 
401  std::string getName() const override { return "instance-stubber"; }
402  bool acceptSizeIncrease() const override { return true; }
403 
406  llvm::DenseSet<Operation *> erasedInsts;
407  llvm::DenseSet<Operation *> erasedModules;
409 };
410 
411 /// A sample reduction pattern that maps `firrtl.mem` to a set of invalidated
412 /// wires.
413 struct MemoryStubber : public OpReduction<firrtl::MemOp> {
414  void beforeReduction(mlir::ModuleOp op) override { nlaRemover.clear(); }
415  void afterReduction(mlir::ModuleOp op) override { nlaRemover.remove(op); }
416  LogicalResult rewrite(firrtl::MemOp memOp) override {
417  LLVM_DEBUG(llvm::dbgs() << "Stubbing memory `" << memOp.getName() << "`\n");
418  ImplicitLocOpBuilder builder(memOp.getLoc(), memOp);
419  SmallDenseMap<Type, Value, 8> invalidCache;
420  Value xorInputs;
421  SmallVector<Value> outputs;
422  for (unsigned i = 0, e = memOp.getNumResults(); i != e; ++i) {
423  auto result = memOp.getResult(i);
424  auto name = builder.getStringAttr(Twine(memOp.getName()) + "_" +
425  memOp.getPortNameStr(i));
426  auto wire =
427  builder
428  .create<firrtl::WireOp>(result.getType(), name,
429  firrtl::NameKindEnum::DroppableName,
430  memOp.getPortAnnotation(i), StringAttr{})
431  .getResult();
432  invalidateOutputs(builder, wire, invalidCache, true);
433  result.replaceAllUsesWith(wire);
434 
435  // Isolate the input and output data fields of the port.
436  Value input, output;
437  switch (memOp.getPortKind(i)) {
438  case firrtl::MemOp::PortKind::Read:
439  output = builder.createOrFold<firrtl::SubfieldOp>(wire, 3);
440  break;
441  case firrtl::MemOp::PortKind::Write:
442  input = builder.createOrFold<firrtl::SubfieldOp>(wire, 3);
443  break;
444  case firrtl::MemOp::PortKind::ReadWrite:
445  input = builder.createOrFold<firrtl::SubfieldOp>(wire, 5);
446  output = builder.createOrFold<firrtl::SubfieldOp>(wire, 3);
447  break;
448  case firrtl::MemOp::PortKind::Debug:
449  output = wire;
450  break;
451  }
452 
453  if (!isa<firrtl::RefType>(result.getType())) {
454  // Reduce all input ports to a single one through an XOR tree.
455  unsigned numFields =
456  wire.getType().cast<firrtl::BundleType>().getNumElements();
457  for (unsigned i = 0; i != numFields; ++i) {
458  if (i != 2 && i != 3 && i != 5)
459  reduceXor(builder, xorInputs,
460  builder.createOrFold<firrtl::SubfieldOp>(wire, i));
461  }
462  if (input)
463  reduceXor(builder, xorInputs, input);
464  }
465 
466  // Track the output port to hook it up to the XORd input later.
467  if (output)
468  outputs.push_back(output);
469  }
470 
471  // Hook up the outputs.
472  for (auto output : outputs)
473  connectToLeafs(builder, output, xorInputs);
474 
475  nlaRemover.markNLAsInOperation(memOp);
476  memOp->erase();
477  return success();
478  }
479  std::string getName() const override { return "memory-stubber"; }
480  bool acceptSizeIncrease() const override { return true; }
482 };
483 
484 /// Check whether an operation interacts with flows in any way, which can make
485 /// replacement and operand forwarding harder in some cases.
486 static bool isFlowSensitiveOp(Operation *op) {
487  return isa<firrtl::WireOp, firrtl::RegOp, firrtl::RegResetOp,
488  firrtl::InstanceOp, firrtl::SubfieldOp, firrtl::SubindexOp,
489  firrtl::SubaccessOp>(op);
490 }
491 
492 /// A sample reduction pattern that replaces all uses of an operation with one
493 /// of its operands. This can help pruning large parts of the expression tree
494 /// rapidly.
495 template <unsigned OpNum>
497  uint64_t match(Operation *op) override {
498  if (op->getNumResults() != 1 || OpNum >= op->getNumOperands())
499  return 0;
500  if (isFlowSensitiveOp(op))
501  return 0;
502  auto resultTy =
503  op->getResult(0).getType().dyn_cast<firrtl::FIRRTLBaseType>();
504  auto opTy =
505  op->getOperand(OpNum).getType().dyn_cast<firrtl::FIRRTLBaseType>();
506  return resultTy && opTy &&
507  resultTy.getWidthlessType() == opTy.getWidthlessType() &&
508  (resultTy.getBitWidthOrSentinel() == -1) ==
509  (opTy.getBitWidthOrSentinel() == -1) &&
510  isa<firrtl::UIntType, firrtl::SIntType>(resultTy);
511  }
512  LogicalResult rewrite(Operation *op) override {
513  assert(match(op));
514  ImplicitLocOpBuilder builder(op->getLoc(), op);
515  auto result = op->getResult(0);
516  auto operand = op->getOperand(OpNum);
517  auto resultTy = result.getType().cast<firrtl::FIRRTLBaseType>();
518  auto operandTy = operand.getType().cast<firrtl::FIRRTLBaseType>();
519  auto resultWidth = resultTy.getBitWidthOrSentinel();
520  auto operandWidth = operandTy.getBitWidthOrSentinel();
521  Value newOp;
522  if (resultWidth < operandWidth)
523  newOp =
524  builder.createOrFold<firrtl::BitsPrimOp>(operand, resultWidth - 1, 0);
525  else if (resultWidth > operandWidth)
526  newOp = builder.createOrFold<firrtl::PadPrimOp>(operand, resultWidth);
527  else
528  newOp = operand;
529  LLVM_DEBUG(llvm::dbgs() << "Forwarding " << newOp << " in " << *op << "\n");
530  result.replaceAllUsesWith(newOp);
531  reduce::pruneUnusedOps(op, *this);
532  return success();
533  }
534  std::string getName() const override {
535  return ("firrtl-operand" + Twine(OpNum) + "-forwarder").str();
536  }
537 };
538 
539 /// A sample reduction pattern that replaces FIRRTL operations with a constant
540 /// zero of their type.
542  uint64_t match(Operation *op) override {
543  if (op->getNumResults() != 1 || op->getNumOperands() == 0)
544  return 0;
545  if (isFlowSensitiveOp(op))
546  return 0;
547  auto type = op->getResult(0).getType().dyn_cast<firrtl::FIRRTLBaseType>();
548  return isa_and_nonnull<firrtl::UIntType, firrtl::SIntType>(type);
549  }
550  LogicalResult rewrite(Operation *op) override {
551  assert(match(op));
552  OpBuilder builder(op);
553  auto type = op->getResult(0).getType().cast<firrtl::FIRRTLBaseType>();
554  auto width = type.getBitWidthOrSentinel();
555  if (width == -1)
556  width = 64;
557  auto newOp = builder.create<firrtl::ConstantOp>(
558  op->getLoc(), type, APSInt(width, isa<firrtl::UIntType>(type)));
559  op->replaceAllUsesWith(newOp);
560  reduce::pruneUnusedOps(op, *this);
561  return success();
562  }
563  std::string getName() const override { return "firrtl-constantifier"; }
564 };
565 
566 /// A sample reduction pattern that replaces the right-hand-side of
567 /// `firrtl.connect` and `firrtl.strictconnect` operations with a
568 /// `firrtl.invalidvalue`. This removes uses from the fanin cone to these
569 /// connects and creates opportunities for reduction in DCE/CSE.
570 struct ConnectInvalidator : public Reduction {
571  uint64_t match(Operation *op) override {
572  if (!isa<firrtl::ConnectOp, firrtl::StrictConnectOp>(op))
573  return 0;
574  auto type = op->getOperand(1).getType().dyn_cast<firrtl::FIRRTLBaseType>();
575  return type && type.isPassive() &&
576  !op->getOperand(1).getDefiningOp<firrtl::InvalidValueOp>();
577  }
578  LogicalResult rewrite(Operation *op) override {
579  assert(match(op));
580  auto rhs = op->getOperand(1);
581  OpBuilder builder(op);
582  auto invOp =
583  builder.create<firrtl::InvalidValueOp>(rhs.getLoc(), rhs.getType());
584  auto *rhsOp = rhs.getDefiningOp();
585  op->setOperand(1, invOp);
586  if (rhsOp)
587  reduce::pruneUnusedOps(rhsOp, *this);
588  return success();
589  }
590  std::string getName() const override { return "connect-invalidator"; }
591  bool acceptSizeIncrease() const override { return true; }
592 };
593 
594 /// A sample reduction pattern that removes FIRRTL annotations from ports and
595 /// operations.
596 struct AnnotationRemover : public Reduction {
597  void beforeReduction(mlir::ModuleOp op) override { nlaRemover.clear(); }
598  void afterReduction(mlir::ModuleOp op) override { nlaRemover.remove(op); }
599  uint64_t match(Operation *op) override {
600  return op->hasAttr("annotations") || op->hasAttr("portAnnotations");
601  }
602  LogicalResult rewrite(Operation *op) override {
603  auto emptyArray = ArrayAttr::get(op->getContext(), {});
604  if (auto annos = op->getAttr("annotations")) {
605  nlaRemover.markNLAsInAnnotation(annos);
606  op->setAttr("annotations", emptyArray);
607  }
608  if (auto annos = op->getAttr("portAnnotations")) {
609  nlaRemover.markNLAsInAnnotation(annos);
610  auto attr = emptyArray;
611  if (isa<firrtl::InstanceOp>(op))
612  attr = ArrayAttr::get(
613  op->getContext(),
614  SmallVector<Attribute>(op->getNumResults(), emptyArray));
615  op->setAttr("portAnnotations", attr);
616  }
617  return success();
618  }
619  std::string getName() const override { return "annotation-remover"; }
621 };
622 
623 /// A sample reduction pattern that removes ports from the root `firrtl.module`
624 /// if the port is not used or just invalidated.
625 struct RootPortPruner : public OpReduction<firrtl::FModuleOp> {
626  uint64_t match(firrtl::FModuleOp module) override {
627  auto circuit = module->getParentOfType<firrtl::CircuitOp>();
628  if (!circuit)
629  return 0;
630  return circuit.getNameAttr() == module.getNameAttr();
631  }
632  LogicalResult rewrite(firrtl::FModuleOp module) override {
633  assert(match(module));
634  size_t numPorts = module.getNumPorts();
635  llvm::BitVector dropPorts(numPorts);
636  for (unsigned i = 0; i != numPorts; ++i) {
637  if (onlyInvalidated(module.getArgument(i))) {
638  dropPorts.set(i);
639  for (auto *user :
640  llvm::make_early_inc_range(module.getArgument(i).getUsers()))
641  user->erase();
642  }
643  }
644  module.erasePorts(dropPorts);
645  return success();
646  }
647  std::string getName() const override { return "root-port-pruner"; }
648 };
649 
650 /// A sample reduction pattern that replaces instances of `firrtl.extmodule`
651 /// with wires.
652 struct ExtmoduleInstanceRemover : public OpReduction<firrtl::InstanceOp> {
653  void beforeReduction(mlir::ModuleOp op) override {
654  symbols.clear();
655  nlaRemover.clear();
656  }
657  void afterReduction(mlir::ModuleOp op) override { nlaRemover.remove(op); }
658 
659  uint64_t match(firrtl::InstanceOp instOp) override {
660  return isa<firrtl::FExtModuleOp>(
661  instOp.getReferencedOperation(symbols.getNearestSymbolTable(instOp)));
662  }
663  LogicalResult rewrite(firrtl::InstanceOp instOp) override {
664  auto portInfo =
665  cast<firrtl::FModuleLike>(instOp.getReferencedOperation(
666  symbols.getNearestSymbolTable(instOp)))
667  .getPorts();
668  ImplicitLocOpBuilder builder(instOp.getLoc(), instOp);
669  SmallVector<Value> replacementWires;
670  for (firrtl::PortInfo info : portInfo) {
671  auto wire =
672  builder
673  .create<firrtl::WireOp>(
674  info.type,
675  (Twine(instOp.getName()) + "_" + info.getName()).str())
676  .getResult();
677  if (info.isOutput()) {
678  auto inv = builder.create<firrtl::InvalidValueOp>(info.type);
679  builder.create<firrtl::ConnectOp>(wire, inv);
680  }
681  replacementWires.push_back(wire);
682  }
683  nlaRemover.markNLAsInOperation(instOp);
684  instOp.replaceAllUsesWith(std::move(replacementWires));
685  instOp->erase();
686  return success();
687  }
688  std::string getName() const override { return "extmodule-instance-remover"; }
689  bool acceptSizeIncrease() const override { return true; }
690 
693 };
694 
695 /// A sample reduction pattern that pushes connected values through wires.
696 struct ConnectForwarder : public Reduction {
697  void beforeReduction(mlir::ModuleOp op) override { opsToErase.clear(); }
698  void afterReduction(mlir::ModuleOp op) override {
699  for (auto *op : opsToErase)
700  op->dropAllReferences();
701  for (auto *op : opsToErase)
702  op->erase();
703  }
704 
705  uint64_t match(Operation *op) override {
706  if (!isa<firrtl::FConnectLike>(op))
707  return 0;
708  auto dest = op->getOperand(0);
709  auto src = op->getOperand(1);
710  auto *destOp = dest.getDefiningOp();
711  auto *srcOp = src.getDefiningOp();
712  if (dest == src)
713  return 0;
714 
715  // Ensure that the destination is something we should be able to forward
716  // through.
717  if (!isa_and_nonnull<firrtl::WireOp>(destOp))
718  return 0;
719 
720  // Ensure that the destination is connected to only once, and all uses of
721  // the connection occur after the definition of the source.
722  unsigned numConnects = 0;
723  for (auto &use : dest.getUses()) {
724  auto *op = use.getOwner();
725  if (use.getOperandNumber() == 0 && isa<firrtl::FConnectLike>(op)) {
726  if (++numConnects > 1)
727  return 0;
728  continue;
729  }
730  if (srcOp && !srcOp->isBeforeInBlock(op))
731  return 0;
732  }
733 
734  return 1;
735  }
736 
737  LogicalResult rewrite(Operation *op) override {
738  auto dest = op->getOperand(0);
739  dest.replaceAllUsesWith(op->getOperand(1));
740  opsToErase.insert(dest.getDefiningOp());
741  opsToErase.insert(op);
742  return success();
743  }
744 
745  std::string getName() const override { return "connect-forwarder"; }
746 
747  llvm::DenseSet<Operation *> opsToErase;
748 };
749 
750 /// A sample reduction pattern that replaces a single-use wire and register with
751 /// an operand of the source value of the connection.
752 template <unsigned OpNum>
754  uint64_t match(Operation *op) override {
755  if (!isa<firrtl::ConnectOp, firrtl::StrictConnectOp>(op))
756  return 0;
757  auto dest = op->getOperand(0);
758  auto *destOp = dest.getDefiningOp();
759 
760  // Ensure that the destination is used only once.
761  if (!destOp || !destOp->hasOneUse() ||
762  !isa<firrtl::WireOp, firrtl::RegOp, firrtl::RegResetOp>(destOp))
763  return 0;
764 
765  auto *srcOp = op->getOperand(1).getDefiningOp();
766  if (!srcOp || OpNum >= srcOp->getNumOperands())
767  return 0;
768 
769  auto resultTy = dest.getType().dyn_cast<firrtl::FIRRTLBaseType>();
770  auto opTy =
771  srcOp->getOperand(OpNum).getType().dyn_cast<firrtl::FIRRTLBaseType>();
772 
773  return resultTy && opTy &&
774  resultTy.getWidthlessType() == opTy.getWidthlessType() &&
775  ((resultTy.getBitWidthOrSentinel() == -1) ==
776  (opTy.getBitWidthOrSentinel() == -1)) &&
777  isa<firrtl::UIntType, firrtl::SIntType>(resultTy);
778  }
779 
780  LogicalResult rewrite(Operation *op) override {
781  auto *destOp = op->getOperand(0).getDefiningOp();
782  auto *srcOp = op->getOperand(1).getDefiningOp();
783  auto forwardedOperand = srcOp->getOperand(OpNum);
784  ImplicitLocOpBuilder builder(destOp->getLoc(), destOp);
785  Value newDest;
786  if (auto wire = dyn_cast<firrtl::WireOp>(destOp))
787  newDest = builder
788  .create<firrtl::WireOp>(forwardedOperand.getType(),
789  wire.getName())
790  .getResult();
791  else {
792  auto regName = destOp->getAttrOfType<StringAttr>("name");
793  // We can promote the register into a wire but we wouldn't do here because
794  // the error might be caused by the register.
795  auto clock = destOp->getOperand(0);
796  newDest = builder
797  .create<firrtl::RegOp>(forwardedOperand.getType(), clock,
798  regName ? regName.str() : "")
799  .getResult();
800  }
801 
802  // Create new connection between a new wire and the forwarded operand.
803  builder.setInsertionPointAfter(op);
804  if (isa<firrtl::ConnectOp>(op))
805  builder.create<firrtl::ConnectOp>(newDest, forwardedOperand);
806  else
807  builder.create<firrtl::StrictConnectOp>(newDest, forwardedOperand);
808 
809  // Remove the old connection and destination. We don't have to replace them
810  // because destination has only one use.
811  op->erase();
812  destOp->erase();
813  reduce::pruneUnusedOps(srcOp, *this);
814 
815  return success();
816  }
817  std::string getName() const override {
818  return ("connect-source-operand-" + Twine(OpNum) + "-forwarder").str();
819  }
820 };
821 
822 /// A sample reduction pattern that tries to remove aggregate wires by replacing
823 /// all subaccesses with new independent wires. This can disentangle large
824 /// unused wires that are otherwise difficult to collect due to the subaccesses.
825 struct DetachSubaccesses : public Reduction {
826  void beforeReduction(mlir::ModuleOp op) override { opsToErase.clear(); }
827  void afterReduction(mlir::ModuleOp op) override {
828  for (auto *op : opsToErase)
829  op->dropAllReferences();
830  for (auto *op : opsToErase)
831  op->erase();
832  }
833  uint64_t match(Operation *op) override {
834  // Only applies to wires and registers that are purely used in subaccess
835  // operations.
836  return isa<firrtl::WireOp, firrtl::RegOp, firrtl::RegResetOp>(op) &&
837  llvm::all_of(op->getUses(), [](auto &use) {
838  return use.getOperandNumber() == 0 &&
839  isa<firrtl::SubfieldOp, firrtl::SubindexOp,
840  firrtl::SubaccessOp>(use.getOwner());
841  });
842  }
843  LogicalResult rewrite(Operation *op) override {
844  assert(match(op));
845  OpBuilder builder(op);
846  bool isWire = isa<firrtl::WireOp>(op);
847  Value invalidClock;
848  if (!isWire)
849  invalidClock = builder.create<firrtl::InvalidValueOp>(
850  op->getLoc(), firrtl::ClockType::get(op->getContext()));
851  for (Operation *user : llvm::make_early_inc_range(op->getUsers())) {
852  builder.setInsertionPoint(user);
853  auto type = user->getResult(0).getType();
854  Operation *replOp;
855  if (isWire)
856  replOp = builder.create<firrtl::WireOp>(user->getLoc(), type);
857  else
858  replOp =
859  builder.create<firrtl::RegOp>(user->getLoc(), type, invalidClock);
860  user->replaceAllUsesWith(replOp);
861  opsToErase.insert(user);
862  }
863  opsToErase.insert(op);
864  return success();
865  }
866  std::string getName() const override { return "detach-subaccesses"; }
867  llvm::DenseSet<Operation *> opsToErase;
868 };
869 
870 /// This reduction removes symbols on node ops. Name preservation creates a lot
871 /// of nodes ops with symbols to keep name information but it also prevents
872 /// normal canonicalizations.
873 struct NodeSymbolRemover : public OpReduction<firrtl::NodeOp> {
874 
875  uint64_t match(firrtl::NodeOp nodeOp) override {
876  return nodeOp.getInnerSym() &&
877  !nodeOp.getInnerSym()->getSymName().getValue().empty();
878  }
879 
880  LogicalResult rewrite(firrtl::NodeOp nodeOp) override {
881  nodeOp.removeInnerSymAttr();
882  return success();
883  }
884 
885  std::string getName() const override { return "node-symbol-remover"; }
886 };
887 
888 /// A sample reduction pattern that eagerly inlines instances.
889 struct EagerInliner : public OpReduction<firrtl::InstanceOp> {
890  void beforeReduction(mlir::ModuleOp op) override {
891  symbols.clear();
892  nlaRemover.clear();
893  }
894  void afterReduction(mlir::ModuleOp op) override { nlaRemover.remove(op); }
895 
896  uint64_t match(firrtl::InstanceOp instOp) override {
897  auto *tableOp = SymbolTable::getNearestSymbolTable(instOp);
898  auto *moduleOp =
899  instOp.getReferencedOperation(symbols.getSymbolTable(tableOp));
900  if (!isa<firrtl::FModuleOp>(moduleOp))
901  return 0;
902  return symbols.getSymbolUserMap(tableOp).getUsers(moduleOp).size() == 1;
903  }
904 
905  LogicalResult rewrite(firrtl::InstanceOp instOp) override {
906  LLVM_DEBUG(llvm::dbgs()
907  << "Inlining instance `" << instOp.getName() << "`\n");
908  SmallVector<Value> argReplacements;
909  ImplicitLocOpBuilder builder(instOp.getLoc(), instOp);
910  for (unsigned i = 0, e = instOp.getNumResults(); i != e; ++i) {
911  auto result = instOp.getResult(i);
912  auto name = builder.getStringAttr(Twine(instOp.getName()) + "_" +
913  instOp.getPortNameStr(i));
914  auto wire =
915  builder
916  .create<firrtl::WireOp>(result.getType(), name,
917  firrtl::NameKindEnum::DroppableName,
918  instOp.getPortAnnotation(i), StringAttr{})
919  .getResult();
920  result.replaceAllUsesWith(wire);
921  argReplacements.push_back(wire);
922  }
923  auto *tableOp = SymbolTable::getNearestSymbolTable(instOp);
924  auto moduleOp = cast<firrtl::FModuleOp>(
925  instOp.getReferencedOperation(symbols.getSymbolTable(tableOp)));
926  for (auto &op : llvm::make_early_inc_range(*moduleOp.getBodyBlock())) {
927  op.remove();
928  builder.insert(&op);
929  for (auto &operand : op.getOpOperands())
930  if (auto blockArg = dyn_cast<BlockArgument>(operand.get()))
931  operand.set(argReplacements[blockArg.getArgNumber()]);
932  }
933  nlaRemover.markNLAsInOperation(instOp);
934  instOp->erase();
935  moduleOp->erase();
936  return success();
937  }
938 
939  std::string getName() const override { return "eager-inliner"; }
940  bool acceptSizeIncrease() const override { return true; }
941 
944 };
945 
946 /// Psuedo-reduction that sanitizes the names of things inside modules. This is
947 /// not an actual reduction, but often removes extraneous information that has
948 /// no bearing on the actual reduction (and would likely be removed before
949 /// sharing the reduction). This makes the following changes:
950 ///
951 /// - All wires are renamed to "wire"
952 /// - All registers are renamed to "reg"
953 /// - All nodes are renamed to "node"
954 /// - All memories are renamed to "mem"
955 /// - All verification messages and labels are dropped
956 ///
958  uint64_t match(Operation *op) override {
959  // Only match operations with names.
960  return isa<firrtl::WireOp, firrtl::RegOp, firrtl::RegResetOp,
961  firrtl::NodeOp, firrtl::MemOp, chirrtl::CombMemOp,
962  chirrtl::SeqMemOp, firrtl::AssertOp, firrtl::AssumeOp,
963  firrtl::CoverOp>(op);
964  }
965  LogicalResult rewrite(Operation *op) override {
966  TypeSwitch<Operation *, void>(op)
967  .Case<firrtl::WireOp>([](auto op) { op.setName("wire"); })
968  .Case<firrtl::RegOp, firrtl::RegResetOp>(
969  [](auto op) { op.setName("reg"); })
970  .Case<firrtl::NodeOp>([](auto op) { op.setName("node"); })
971  .Case<firrtl::MemOp, chirrtl::CombMemOp, chirrtl::SeqMemOp>(
972  [](auto op) { op.setName("mem"); })
973  .Case<firrtl::AssertOp, firrtl::AssumeOp, firrtl::CoverOp>([](auto op) {
974  op->setAttr("message", StringAttr::get(op.getContext(), ""));
975  op->setAttr("name", StringAttr::get(op.getContext(), ""));
976  });
977  return success();
978  }
979 
980  std::string getName() const override {
981  return "module-internal-name-sanitizer";
982  }
983 
984  bool acceptSizeIncrease() const override { return true; }
985 
986  bool isOneShot() const override { return true; }
987 };
988 
989 /// Psuedo-reduction that sanitizes module, instance, and port names. This
990 /// makes the following changes:
991 ///
992 /// - All modules are given metasyntactic names ("Foo", "Bar", etc.)
993 /// - All instances are renamed to match the new module name
994 /// - All module ports are renamed in the following way:
995 /// - All clocks are reanemd to "clk"
996 /// - All resets are renamed to "rst"
997 /// - All references are renamed to "ref"
998 /// - Anything else is renamed to "port"
999 ///
1000 struct ModuleNameSanitizer : OpReduction<firrtl::CircuitOp> {
1001 
1002  const char *names[48] = {
1003  "Foo", "Bar", "Baz", "Qux", "Quux", "Quuux", "Quuuux",
1004  "Quz", "Corge", "Grault", "Bazola", "Ztesch", "Thud", "Grunt",
1005  "Bletch", "Fum", "Fred", "Jim", "Sheila", "Barney", "Flarp",
1006  "Zxc", "Spqr", "Wombat", "Shme", "Bongo", "Spam", "Eggs",
1007  "Snork", "Zot", "Blarg", "Wibble", "Toto", "Titi", "Tata",
1008  "Tutu", "Pippo", "Pluto", "Paperino", "Aap", "Noot", "Mies",
1009  "Oogle", "Foogle", "Boogle", "Zork", "Gork", "Bork"};
1010 
1011  size_t nameIndex = 0;
1012 
1013  const char *getName() {
1014  if (nameIndex >= 48)
1015  nameIndex = 0;
1016  return names[nameIndex++];
1017  };
1018 
1019  size_t portNameIndex = 0;
1020 
1021  char getPortName() {
1022  if (portNameIndex >= 26)
1023  portNameIndex = 0;
1024  return 'a' + portNameIndex++;
1025  }
1026 
1027  void beforeReduction(mlir::ModuleOp op) override { nameIndex = 0; }
1028 
1029  LogicalResult rewrite(firrtl::CircuitOp circuitOp) override {
1030 
1031  firrtl::InstanceGraph iGraph(circuitOp);
1032 
1033  auto *circuitName = getName();
1034  iGraph.getTopLevelModule().setName(circuitName);
1035  circuitOp.setName(circuitName);
1036 
1037  for (auto *node : iGraph) {
1038  auto module = node->getModule<firrtl::FModuleLike>();
1039 
1040  bool shouldReplacePorts = false;
1041  SmallVector<Attribute> newNames;
1042  if (auto fmodule = dyn_cast<firrtl::FModuleOp>(*module)) {
1043  portNameIndex = 0;
1044  // TODO: The namespace should be unnecessary. However, some FIRRTL
1045  // passes expect that port names are unique.
1046  circt::Namespace ns;
1047  auto oldPorts = fmodule.getPorts();
1048  shouldReplacePorts = !oldPorts.empty();
1049  for (unsigned i = 0, e = fmodule.getNumPorts(); i != e; ++i) {
1050  auto port = oldPorts[i];
1051  auto newName = firrtl::FIRRTLTypeSwitch<Type, StringRef>(port.type)
1052  .Case<firrtl::ClockType>(
1053  [&](auto a) { return ns.newName("clk"); })
1054  .Case<firrtl::ResetType, firrtl::AsyncResetType>(
1055  [&](auto a) { return ns.newName("rst"); })
1056  .Case<firrtl::RefType>(
1057  [&](auto a) { return ns.newName("ref"); })
1058  .Default([&](auto a) {
1059  return ns.newName(Twine(getPortName()));
1060  });
1061  newNames.push_back(StringAttr::get(circuitOp.getContext(), newName));
1062  }
1063  fmodule->setAttr("portNames",
1064  ArrayAttr::get(fmodule.getContext(), newNames));
1065  }
1066 
1067  if (module == iGraph.getTopLevelModule())
1068  continue;
1069  auto newName = StringAttr::get(circuitOp.getContext(), getName());
1070  module.setName(newName);
1071  for (auto *use : node->uses()) {
1072  auto instanceOp = dyn_cast<firrtl::InstanceOp>(*use->getInstance());
1073  instanceOp.setModuleName(newName);
1074  instanceOp.setName(newName);
1075  if (shouldReplacePorts)
1076  instanceOp.setPortNamesAttr(
1077  ArrayAttr::get(circuitOp.getContext(), newNames));
1078  }
1079  }
1080 
1081  circuitOp->dump();
1082 
1083  return success();
1084  }
1085 
1086  std::string getName() const override { return "module-name-sanitizer"; }
1087 
1088  bool acceptSizeIncrease() const override { return true; }
1089 
1090  bool isOneShot() const override { return true; }
1091 };
1092 
1093 //===----------------------------------------------------------------------===//
1094 // Reduction Registration
1095 //===----------------------------------------------------------------------===//
1096 
1097 void firrtl::FIRRTLReducePatternDialectInterface::populateReducePatterns(
1099  // Gather a list of reduction patterns that we should try. Ideally these are
1100  // assigned reasonable benefit indicators (higher benefit patterns are
1101  // prioritized). For example, things that can knock out entire modules while
1102  // being cheap should be tried first (and thus have higher benefit), before
1103  // trying to tweak operands of individual arithmetic ops.
1104  patterns.add<PassReduction, 30>(
1105  getContext(), firrtl::createDropNamesPass(PreserveValues::None), false,
1106  true);
1107  patterns.add<PassReduction, 29>(getContext(),
1108  firrtl::createLowerCHIRRTLPass(), true, true);
1109  patterns.add<PassReduction, 28>(getContext(), firrtl::createInferWidthsPass(),
1110  true, true);
1111  patterns.add<PassReduction, 27>(getContext(), firrtl::createInferResetsPass(),
1112  true, true);
1114  patterns.add<InstanceStubber, 25>();
1115  patterns.add<MemoryStubber, 24>();
1116  patterns.add<EagerInliner, 23>();
1117  patterns.add<PassReduction, 22>(
1118  getContext(), firrtl::createLowerFIRRTLTypesPass(), true, true);
1119  patterns.add<PassReduction, 21>(getContext(), firrtl::createExpandWhensPass(),
1120  true, true);
1121  patterns.add<PassReduction, 20>(getContext(), firrtl::createInlinerPass());
1122  patterns.add<PassReduction, 18>(getContext(),
1124  patterns.add<PassReduction, 17>(
1125  getContext(),
1126  firrtl::createRemoveUnusedPortsPass(/*ignoreDontTouch=*/true));
1127  patterns.add<NodeSymbolRemover, 15>();
1128  patterns.add<ConnectForwarder, 14>();
1129  patterns.add<ConnectInvalidator, 13>();
1130  patterns.add<FIRRTLConstantifier, 12>();
1134  patterns.add<DetachSubaccesses, 7>();
1135  patterns.add<AnnotationRemover, 6>();
1136  patterns.add<RootPortPruner, 5>();
1142  patterns.add<ModuleNameSanitizer, 0>();
1143 }
1144 
1146  mlir::DialectRegistry &registry) {
1147  registry.addExtension(+[](MLIRContext *ctx, FIRRTLDialect *dialect) {
1148  dialect->addInterfaces<FIRRTLReducePatternDialectInterface>();
1149  });
1150 }
assert(baseType &&"element must be base type")
static bool isFlowSensitiveOp(Operation *op)
Check whether an operation interacts with flows in any way, which can make replacement and operand fo...
static std::optional< firrtl::FModuleOp > findInstantiatedModule(firrtl::InstanceOp instOp, ::detail::SymbolCache &symbols)
Utility to easily get the instantiated firrtl::FModuleOp or an empty optional in case another type of...
static void connectToLeafs(ImplicitLocOpBuilder &builder, Value dest, Value value)
Connect a value to every leave of a destination value.
static void invalidateOutputs(ImplicitLocOpBuilder &builder, Value value, SmallDenseMap< Type, Value, 8 > &invalidCache, bool flip=false)
Invalidate all the leaf fields of a value with a given flippedness by connecting an invalid value to ...
static bool onlyInvalidated(Value arg)
Check that all connections to a value are invalids.
static void reduceXor(ImplicitLocOpBuilder &builder, Value &into, Value value)
Reduce all leaf fields of a value through an XOR tree.
int32_t width
Definition: FIRRTL.cpp:36
llvm::SmallVector< StringAttr > outputs
Builder builder
A namespace that is used to store existing names and generate new names in some scope within the IR.
Definition: Namespace.h:29
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
Definition: Namespace.h:63
int32_t getBitWidthOrSentinel()
If this is an IntType, AnalogType, or sugar type for a single bit (Clock, Reset, etc) then return the...
bool isPassive() const
Return true if this is a "passive" type - one that contains no "flip" types recursively within itself...
Definition: FIRRTLTypes.h:152
FIRRTLBaseType getWidthlessType()
Return this type with widths of all ground types removed.
This class implements the same functionality as TypeSwitch except that it uses firrtl::type_dyn_cast ...
Definition: FIRRTLTypes.h:518
FIRRTLTypeSwitch< T, ResultT > & Case(CallableT &&caseFn)
Add a case on the given type.
Definition: FIRRTLTypes.h:528
This graph tracks modules and where they are instantiated.
FModuleLike getTopLevelModule()
Get the module corresponding to the top-level module of a circuit.
void registerReducePatternDialectInterface(mlir::DialectRegistry &registry)
Register the Arc Reduction pattern dialect interface to the given registry.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
Direction flip(Direction direction)
Flip a port direction.
std::unique_ptr< mlir::Pass > createLowerFIRRTLTypesPass(PreserveAggregate::PreserveMode mode=PreserveAggregate::None, PreserveAggregate::PreserveMode memoryMode=PreserveAggregate::None)
This is the pass constructor.
std::unique_ptr< mlir::Pass > createIMConstPropPass()
std::unique_ptr< mlir::Pass > createRemoveUnusedPortsPass(bool ignoreDontTouch=false)
std::unique_ptr< mlir::Pass > createInlinerPass()
std::unique_ptr< mlir::Pass > createDropNamesPass(PreserveValues::PreserveMode mode=PreserveValues::None)
Definition: DropName.cpp:92
std::unique_ptr< mlir::Pass > createLowerCHIRRTLPass()
std::unique_ptr< mlir::Pass > createExpandWhensPass()
std::unique_ptr< mlir::Pass > createInferWidthsPass()
std::unique_ptr< mlir::Pass > createInferResetsPass()
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
void pruneUnusedOps(Operation *initialOp, Reduction &reduction)
Starting at the given op, traverse through it and its operands and erase operations that have no more...
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
A sample reduction pattern that removes FIRRTL annotations from ports and operations.
void afterReduction(mlir::ModuleOp op) override
Called after the reduction has been applied to a subset of operations.
uint64_t match(Operation *op) override
Check if the reduction can apply to a specific operation.
LogicalResult rewrite(Operation *op) override
Apply the reduction to a specific operation.
void beforeReduction(mlir::ModuleOp op) override
Called before the reduction is applied to a new subset of operations.
std::string getName() const override
Return a human-readable name for this reduction pattern.
A sample reduction pattern that pushes connected values through wires.
uint64_t match(Operation *op) override
Check if the reduction can apply to a specific operation.
void beforeReduction(mlir::ModuleOp op) override
Called before the reduction is applied to a new subset of operations.
void afterReduction(mlir::ModuleOp op) override
Called after the reduction has been applied to a subset of operations.
LogicalResult rewrite(Operation *op) override
Apply the reduction to a specific operation.
llvm::DenseSet< Operation * > opsToErase
std::string getName() const override
Return a human-readable name for this reduction pattern.
A sample reduction pattern that replaces the right-hand-side of firrtl.connect and firrtl....
LogicalResult rewrite(Operation *op) override
Apply the reduction to a specific operation.
bool acceptSizeIncrease() const override
Return true if the tool should accept the transformation this reduction performs on the module even i...
std::string getName() const override
Return a human-readable name for this reduction pattern.
uint64_t match(Operation *op) override
Check if the reduction can apply to a specific operation.
A sample reduction pattern that replaces a single-use wire and register with an operand of the source...
uint64_t match(Operation *op) override
Check if the reduction can apply to a specific operation.
LogicalResult rewrite(Operation *op) override
Apply the reduction to a specific operation.
std::string getName() const override
Return a human-readable name for this reduction pattern.
A sample reduction pattern that tries to remove aggregate wires by replacing all subaccesses with new...
void afterReduction(mlir::ModuleOp op) override
Called after the reduction has been applied to a subset of operations.
std::string getName() const override
Return a human-readable name for this reduction pattern.
uint64_t match(Operation *op) override
Check if the reduction can apply to a specific operation.
LogicalResult rewrite(Operation *op) override
Apply the reduction to a specific operation.
void beforeReduction(mlir::ModuleOp op) override
Called before the reduction is applied to a new subset of operations.
llvm::DenseSet< Operation * > opsToErase
A sample reduction pattern that eagerly inlines instances.
uint64_t match(firrtl::InstanceOp instOp) override
::detail::SymbolCache symbols
LogicalResult rewrite(firrtl::InstanceOp instOp) override
NLARemover nlaRemover
std::string getName() const override
Return a human-readable name for this reduction pattern.
bool acceptSizeIncrease() const override
Return true if the tool should accept the transformation this reduction performs on the module even i...
void beforeReduction(mlir::ModuleOp op) override
Called before the reduction is applied to a new subset of operations.
void afterReduction(mlir::ModuleOp op) override
Called after the reduction has been applied to a subset of operations.
A sample reduction pattern that replaces instances of firrtl.extmodule with wires.
::detail::SymbolCache symbols
LogicalResult rewrite(firrtl::InstanceOp instOp) override
std::string getName() const override
Return a human-readable name for this reduction pattern.
bool acceptSizeIncrease() const override
Return true if the tool should accept the transformation this reduction performs on the module even i...
void beforeReduction(mlir::ModuleOp op) override
Called before the reduction is applied to a new subset of operations.
uint64_t match(firrtl::InstanceOp instOp) override
void afterReduction(mlir::ModuleOp op) override
Called after the reduction has been applied to a subset of operations.
A sample reduction pattern that replaces FIRRTL operations with a constant zero of their type.
LogicalResult rewrite(Operation *op) override
Apply the reduction to a specific operation.
uint64_t match(Operation *op) override
Check if the reduction can apply to a specific operation.
std::string getName() const override
Return a human-readable name for this reduction pattern.
A sample reduction pattern that maps firrtl.module to firrtl.extmodule.
std::string getName() const override
Return a human-readable name for this reduction pattern.
LogicalResult rewrite(firrtl::FModuleOp module) override
::detail::SymbolCache symbols
void beforeReduction(mlir::ModuleOp op) override
Called before the reduction is applied to a new subset of operations.
void afterReduction(mlir::ModuleOp op) override
Called after the reduction has been applied to a subset of operations.
uint64_t match(firrtl::FModuleOp module) override
A sample reduction pattern that replaces all uses of an operation with one of its operands.
std::string getName() const override
Return a human-readable name for this reduction pattern.
uint64_t match(Operation *op) override
Check if the reduction can apply to a specific operation.
LogicalResult rewrite(Operation *op) override
Apply the reduction to a specific operation.
A sample reduction pattern that maps firrtl.instance to a set of invalidated wires.
void afterReduction(mlir::ModuleOp op) override
Called after the reduction has been applied to a subset of operations.
void beforeReduction(mlir::ModuleOp op) override
Called before the reduction is applied to a new subset of operations.
llvm::DenseSet< Operation * > erasedModules
LogicalResult rewrite(firrtl::InstanceOp instOp) override
llvm::DenseSet< Operation * > erasedInsts
uint64_t match(firrtl::InstanceOp instOp) override
ModuleSizeCache moduleSizes
bool acceptSizeIncrease() const override
Return true if the tool should accept the transformation this reduction performs on the module even i...
std::string getName() const override
Return a human-readable name for this reduction pattern.
::detail::SymbolCache symbols
A sample reduction pattern that maps firrtl.mem to a set of invalidated wires.
std::string getName() const override
Return a human-readable name for this reduction pattern.
bool acceptSizeIncrease() const override
Return true if the tool should accept the transformation this reduction performs on the module even i...
void beforeReduction(mlir::ModuleOp op) override
Called before the reduction is applied to a new subset of operations.
LogicalResult rewrite(firrtl::MemOp memOp) override
NLARemover nlaRemover
void afterReduction(mlir::ModuleOp op) override
Called after the reduction has been applied to a subset of operations.
Psuedo-reduction that sanitizes the names of things inside modules.
bool acceptSizeIncrease() const override
Return true if the tool should accept the transformation this reduction performs on the module even i...
uint64_t match(Operation *op) override
Check if the reduction can apply to a specific operation.
LogicalResult rewrite(Operation *op) override
Apply the reduction to a specific operation.
std::string getName() const override
Return a human-readable name for this reduction pattern.
bool isOneShot() const override
Return true if the tool should not try to reapply this reduction after it has been successful.
Psuedo-reduction that sanitizes module, instance, and port names.
std::string getName() const override
Return a human-readable name for this reduction pattern.
void beforeReduction(mlir::ModuleOp op) override
Called before the reduction is applied to a new subset of operations.
LogicalResult rewrite(firrtl::CircuitOp circuitOp) override
bool isOneShot() const override
Return true if the tool should not try to reapply this reduction after it has been successful.
bool acceptSizeIncrease() const override
Return true if the tool should accept the transformation this reduction performs on the module even i...
Utility to track the transitive size of modules.
llvm::DenseMap< Operation *, uint64_t > moduleSizes
uint64_t getModuleSize(Operation *module, ::detail::SymbolCache &symbols)
A tracker for track NLAs affected by a reduction.
void remove(mlir::ModuleOp module)
Remove all marked annotations.
void clear()
Clear the set of marked NLAs. Call this before attempting a reduction.
llvm::DenseSet< StringAttr > nlasToRemove
The set of NLAs to remove, identified by their symbol.
void markNLAsInAnnotation(Attribute anno)
Mark all NLAs referenced in the given annotation as to be removed.
void markNLAsInOperation(Operation *op)
Mark all NLAs referenced in an operation.
This reduction removes symbols on node ops.
std::string getName() const override
Return a human-readable name for this reduction pattern.
uint64_t match(firrtl::NodeOp nodeOp) override
LogicalResult rewrite(firrtl::NodeOp nodeOp) override
A sample reduction pattern that removes ports from the root firrtl.module if the port is not used or ...
std::string getName() const override
Return a human-readable name for this reduction pattern.
LogicalResult rewrite(firrtl::FModuleOp module) override
uint64_t match(firrtl::FModuleOp module) override
A reduction pattern that applies an mlir::Pass.
Definition: Reduction.h:99
An abstract reduction pattern.
Definition: Reduction.h:24
A dialect interface to provide reduction patterns to a reducer tool.
This holds the name and type that describes the module's ports.
A utility doing lazy construction of SymbolTables and SymbolUserMaps, which is handy for reductions t...
SymbolTable & getSymbolTable(Operation *op)
SymbolTableCollection tables
SymbolTable & getNearestSymbolTable(Operation *op)
SymbolUserMap & getSymbolUserMap(Operation *op)
SymbolUserMap & getNearestSymbolUserMap(Operation *op)
SmallDenseMap< Operation *, SymbolUserMap, 2 > userMaps