25#include "mlir/Analysis/TopologicalSortUtils.h"
26#include "mlir/IR/Dominance.h"
27#include "mlir/IR/ImplicitLocOpBuilder.h"
28#include "mlir/IR/Matchers.h"
29#include "llvm/ADT/APSInt.h"
30#include "llvm/ADT/DenseMap.h"
31#include "llvm/ADT/SmallSet.h"
32#include "llvm/Support/Debug.h"
34#define DEBUG_TYPE "firrtl-reductions"
39using llvm::SmallDenseSet;
53 return tables->getSymbolTable(op);
63 return userMaps.insert({op, SymbolUserMap(*
tables, op)}).first->second;
70 tables = std::make_unique<SymbolTableCollection>();
75 std::unique_ptr<SymbolTableCollection>
tables;
82static std::optional<firrtl::FModuleOp>
85 auto *tableOp = SymbolTable::getNearestSymbolTable(instOp);
86 auto moduleOp = dyn_cast<firrtl::FModuleOp>(
88 return moduleOp ? std::optional(moduleOp) : std::nullopt;
99 module->walk([&](Operation *op) {
101 if (
auto instOp = dyn_cast<firrtl::InstanceOp>(op))
115 return llvm::all_of(arg.getUses(), [](OpOperand &use) {
116 auto *op = use.getOwner();
117 if (!isa<firrtl::ConnectOp, firrtl::MatchingConnectOp>(op))
119 if (use.getOperandNumber() != 0)
121 if (!op->getOperand(1).getDefiningOp<firrtl::InvalidValueOp>())
138 unsigned numRemoved = 0;
140 SymbolTableCollection symbolTables;
141 for (Operation &rootOp : *module.getBody()) {
142 if (!isa<firrtl::CircuitOp>(&rootOp))
144 SymbolUserMap symbolUserMap(symbolTables, &rootOp);
145 auto &symbolTable = symbolTables.getSymbolTable(&rootOp);
147 if (
auto *op = symbolTable.lookup(sym)) {
148 if (symbolUserMap.useEmpty(op)) {
157 if (numRemoved > 0 || numLost > 0) {
158 llvm::dbgs() <<
"Removed " << numRemoved <<
" NLAs";
160 llvm::dbgs() <<
" (" << numLost <<
" no longer there)";
161 llvm::dbgs() <<
"\n";
170 if (
auto dict = dyn_cast<DictionaryAttr>(anno)) {
171 if (
auto field = dict.getAs<FlatSymbolRefAttr>(
"circt.nonlocal"))
173 for (
auto namedAttr : dict)
175 }
else if (
auto array = dyn_cast<ArrayAttr>(anno)) {
176 for (
auto attr : array)
185 op->walk([&](Operation *op) {
186 if (
auto annos = op->getAttrOfType<ArrayAttr>(
"annotations"))
202struct FIRRTLModuleExternalizer :
public OpReduction<FModuleOp> {
203 void beforeReduction(mlir::ModuleOp op)
override {
209 void afterReduction(mlir::ModuleOp op)
override { nlaRemover.remove(op); }
211 uint64_t match(FModuleOp module)
override {
212 if (innerSymUses.hasInnerRef(module))
214 return moduleSizes.getModuleSize(module, symbols);
217 LogicalResult rewrite(FModuleOp module)
override {
220 layers.insert_range(module.getLayersAttr().getAsRange<SymbolRefAttr>());
221 for (
auto attr : module.getPortTypes()) {
222 auto type = cast<TypeAttr>(attr).getValue();
224 if (
auto layer = refType.getLayer())
225 layers.insert(layer);
227 SmallVector<Attribute, 4> layersArray;
228 layersArray.reserve(layers.size());
229 for (
auto layer : layers)
230 layersArray.push_back(layer);
232 nlaRemover.markNLAsInOperation(module);
233 OpBuilder builder(module);
235 FExtModuleOp::create(builder, module->getLoc(), module.getSymNameAttr(),
236 module.getConventionAttr(), module.getPorts(),
237 builder.getArrayAttr(layersArray), StringRef(),
238 module.getAnnotationsAttr());
239 SymbolTable::setSymbolVisibility(extmodule,
240 SymbolTable::getSymbolVisibility(module));
245 std::string getName()
const override {
return "firrtl-module-externalizer"; }
264static void invalidateOutputs(ImplicitLocOpBuilder &builder, Value value,
273 assert(!
flip &&
"input probes are not allowed");
275 auto underlyingType = refType.getType();
277 if (!refType.getForceable()) {
280 auto targetWire = WireOp::create(builder, underlyingType);
281 auto refSend = builder.create<RefSendOp>(targetWire.getResult());
282 builder.create<RefDefineOp>(value, refSend.getResult());
285 auto invalid = InvalidValueOp::create(builder, underlyingType);
286 MatchingConnectOp::create(builder, targetWire.getResult(), invalid);
292 WireOp::create(builder, underlyingType,
293 "", NameKindEnum::DroppableName,
294 ArrayRef<Attribute>{},
299 auto targetWire = forceableWire.getResult();
300 auto forceableRef = forceableWire.getDataRef();
302 builder.create<RefDefineOp>(value, forceableRef);
305 auto invalid = InvalidValueOp::create(builder, underlyingType);
306 MatchingConnectOp::create(builder, targetWire, invalid);
312 for (
auto element : llvm::enumerate(bundleType.getElements())) {
313 auto subfield = builder.createOrFold<SubfieldOp>(value, element.index());
314 invalidateOutputs(builder, subfield, tieOffCache,
315 flip ^ element.value().isFlip);
316 if (subfield.use_empty())
317 subfield.getDefiningOp()->erase();
324 for (
unsigned i = 0, e = vectorType.getNumElements(); i != e; ++i) {
325 auto subindex = builder.createOrFold<SubindexOp>(value, i);
326 invalidateOutputs(builder, subindex, tieOffCache,
flip);
327 if (subindex.use_empty())
328 subindex.getDefiningOp()->erase();
339 auto invalid = InvalidValueOp::create(builder, baseType);
340 ConnectOp::create(builder, value, invalid);
346 auto unknown = tieOffCache.
getUnknown(propType);
347 builder.create<PropAssignOp>(value, unknown);
352static void connectToLeafs(ImplicitLocOpBuilder &builder, Value dest,
354 auto type = dyn_cast<firrtl::FIRRTLBaseType>(dest.getType());
357 if (
auto bundleType = dyn_cast<firrtl::BundleType>(type)) {
358 for (
auto element : llvm::enumerate(bundleType.getElements()))
359 connectToLeafs(builder,
360 firrtl::SubfieldOp::create(builder, dest, element.index()),
364 if (
auto vectorType = dyn_cast<firrtl::FVectorType>(type)) {
365 for (
unsigned i = 0, e = vectorType.getNumElements(); i != e; ++i)
366 connectToLeafs(builder, firrtl::SubindexOp::create(builder, dest, i),
370 auto valueType = dyn_cast<firrtl::FIRRTLBaseType>(value.getType());
373 auto destWidth = type.getBitWidthOrSentinel();
374 auto valueWidth = valueType ? valueType.getBitWidthOrSentinel() : -1;
375 if (destWidth >= 0 && valueWidth >= 0 && destWidth < valueWidth)
376 value = firrtl::HeadPrimOp::create(builder, value, destWidth);
377 if (!isa<firrtl::UIntType>(type)) {
378 if (isa<firrtl::SIntType>(type))
379 value = firrtl::AsSIntPrimOp::create(builder, value);
383 firrtl::ConnectOp::create(builder, dest, value);
387static void reduceXor(ImplicitLocOpBuilder &builder, Value &into, Value value) {
388 auto type = dyn_cast<firrtl::FIRRTLType>(value.getType());
391 if (
auto bundleType = dyn_cast<firrtl::BundleType>(type)) {
392 for (
auto element : llvm::enumerate(bundleType.getElements()))
395 builder.createOrFold<firrtl::SubfieldOp>(value, element.index()));
398 if (
auto vectorType = dyn_cast<firrtl::FVectorType>(type)) {
399 for (
unsigned i = 0, e = vectorType.getNumElements(); i != e; ++i)
400 reduceXor(builder, into,
401 builder.createOrFold<firrtl::SubindexOp>(value, i));
404 if (!isa<firrtl::UIntType>(type)) {
405 if (isa<firrtl::SIntType>(type))
406 value = firrtl::AsUIntPrimOp::create(builder, value);
410 into = into ? builder.createOrFold<firrtl::XorPrimOp>(into, value) : value;
416struct InstanceStubber :
public OpReduction<firrtl::InstanceOp> {
417 void beforeReduction(mlir::ModuleOp op)
override {
419 erasedModules.clear();
424 void afterReduction(mlir::ModuleOp op)
override {
427 SmallVector<Operation *> worklist;
428 auto deadInsts = erasedInsts;
429 for (
auto *op : erasedModules)
430 worklist.push_back(op);
431 while (!worklist.empty()) {
432 auto *op = worklist.pop_back_val();
433 auto *tableOp = SymbolTable::getNearestSymbolTable(op);
434 op->walk([&](firrtl::InstanceOp instOp) {
435 auto moduleOp = cast<firrtl::FModuleLike>(
436 instOp.getReferencedOperation(symbols.getSymbolTable(tableOp)));
437 deadInsts.insert(instOp);
439 symbols.getSymbolUserMap(tableOp).getUsers(moduleOp),
440 [&](Operation *user) { return deadInsts.contains(user); })) {
441 LLVM_DEBUG(llvm::dbgs() <<
"- Removing transitively unused module `"
442 << moduleOp.getModuleName() <<
"`\n");
443 erasedModules.insert(moduleOp);
444 worklist.push_back(moduleOp);
449 for (
auto *op : erasedInsts)
451 for (
auto *op : erasedModules)
453 nlaRemover.remove(op);
456 uint64_t match(firrtl::InstanceOp instOp)
override {
458 return moduleSizes.getModuleSize(*fmoduleOp, symbols);
462 LogicalResult rewrite(firrtl::InstanceOp instOp)
override {
463 LLVM_DEBUG(llvm::dbgs()
464 <<
"Stubbing instance `" << instOp.getName() <<
"`\n");
465 ImplicitLocOpBuilder builder(instOp.getLoc(), instOp);
466 TieOffCache tieOffCache(builder);
467 for (
unsigned i = 0, e = instOp.getNumResults(); i != e; ++i) {
468 auto result = instOp.getResult(i);
469 auto name = builder.getStringAttr(Twine(instOp.getName()) +
"_" +
470 instOp.getPortName(i));
472 firrtl::WireOp::create(builder, result.getType(), name,
473 firrtl::NameKindEnum::DroppableName,
474 instOp.getPortAnnotation(i), StringAttr{})
476 invalidateOutputs(builder, wire, tieOffCache,
477 instOp.getPortDirection(i) == firrtl::Direction::In);
478 result.replaceAllUsesWith(wire);
480 auto *tableOp = SymbolTable::getNearestSymbolTable(instOp);
481 auto moduleOp = cast<firrtl::FModuleLike>(
482 instOp.getReferencedOperation(symbols.getSymbolTable(tableOp)));
483 nlaRemover.markNLAsInOperation(instOp);
484 erasedInsts.insert(instOp);
486 symbols.getSymbolUserMap(tableOp).getUsers(moduleOp),
487 [&](Operation *user) { return erasedInsts.contains(user); })) {
488 LLVM_DEBUG(llvm::dbgs() <<
"- Removing now unused module `"
489 << moduleOp.getModuleName() <<
"`\n");
490 erasedModules.insert(moduleOp);
495 std::string
getName()
const override {
return "instance-stubber"; }
496 bool acceptSizeIncrease()
const override {
return true; }
498 ::detail::SymbolCache symbols;
499 NLARemover nlaRemover;
500 llvm::DenseSet<Operation *> erasedInsts;
501 llvm::DenseSet<Operation *> erasedModules;
502 ModuleSizeCache moduleSizes;
507struct MemoryStubber :
public OpReduction<firrtl::MemOp> {
508 void beforeReduction(mlir::ModuleOp op)
override { nlaRemover.clear(); }
509 void afterReduction(mlir::ModuleOp op)
override { nlaRemover.remove(op); }
510 LogicalResult rewrite(firrtl::MemOp memOp)
override {
511 LLVM_DEBUG(llvm::dbgs() <<
"Stubbing memory `" << memOp.getName() <<
"`\n");
512 ImplicitLocOpBuilder builder(memOp.getLoc(), memOp);
513 TieOffCache tieOffCache(builder);
515 SmallVector<Value> outputs;
516 for (
unsigned i = 0, e = memOp.getNumResults(); i != e; ++i) {
517 auto result = memOp.getResult(i);
518 auto name = builder.getStringAttr(Twine(memOp.getName()) +
"_" +
519 memOp.getPortName(i));
521 firrtl::WireOp::create(builder, result.getType(), name,
522 firrtl::NameKindEnum::DroppableName,
523 memOp.getPortAnnotation(i), StringAttr{})
525 invalidateOutputs(builder, wire, tieOffCache,
true);
526 result.replaceAllUsesWith(wire);
530 switch (memOp.getPortKind(i)) {
531 case firrtl::MemOp::PortKind::Read:
532 output = builder.createOrFold<firrtl::SubfieldOp>(wire, 3);
534 case firrtl::MemOp::PortKind::Write:
535 input = builder.createOrFold<firrtl::SubfieldOp>(wire, 3);
537 case firrtl::MemOp::PortKind::ReadWrite:
538 input = builder.createOrFold<firrtl::SubfieldOp>(wire, 5);
539 output = builder.createOrFold<firrtl::SubfieldOp>(wire, 3);
541 case firrtl::MemOp::PortKind::Debug:
546 if (!isa<firrtl::RefType>(result.getType())) {
549 cast<firrtl::BundleType>(wire.getType()).getNumElements();
550 for (
unsigned i = 0; i != numFields; ++i) {
551 if (i != 2 && i != 3 && i != 5)
552 reduceXor(builder, xorInputs,
553 builder.createOrFold<firrtl::SubfieldOp>(wire, i));
556 reduceXor(builder, xorInputs, input);
561 outputs.push_back(output);
565 for (
auto output : outputs)
566 connectToLeafs(builder, output, xorInputs);
568 nlaRemover.markNLAsInOperation(memOp);
572 std::string
getName()
const override {
return "memory-stubber"; }
573 bool acceptSizeIncrease()
const override {
return true; }
574 NLARemover nlaRemover;
579static bool isFlowSensitiveOp(Operation *op) {
580 return isa<WireOp, RegOp, RegResetOp, InstanceOp, SubfieldOp, SubindexOp,
581 SubaccessOp, ObjectSubfieldOp>(op);
587template <
unsigned OpNum>
588struct FIRRTLOperandForwarder :
public Reduction {
589 uint64_t match(Operation *op)
override {
590 if (op->getNumResults() != 1 || OpNum >= op->getNumOperands())
592 if (isFlowSensitiveOp(op))
595 dyn_cast<firrtl::FIRRTLBaseType>(op->getResult(0).getType());
597 dyn_cast<firrtl::FIRRTLBaseType>(op->getOperand(OpNum).getType());
598 return resultTy && opTy &&
599 resultTy.getWidthlessType() == opTy.getWidthlessType() &&
600 (resultTy.getBitWidthOrSentinel() == -1) ==
601 (opTy.getBitWidthOrSentinel() == -1) &&
602 isa<firrtl::UIntType, firrtl::SIntType>(resultTy);
604 LogicalResult rewrite(Operation *op)
override {
606 ImplicitLocOpBuilder builder(op->getLoc(), op);
607 auto result = op->getResult(0);
608 auto operand = op->getOperand(OpNum);
609 auto resultTy = cast<firrtl::FIRRTLBaseType>(result.getType());
610 auto operandTy = cast<firrtl::FIRRTLBaseType>(operand.getType());
611 auto resultWidth = resultTy.getBitWidthOrSentinel();
612 auto operandWidth = operandTy.getBitWidthOrSentinel();
614 if (resultWidth < operandWidth)
616 builder.createOrFold<firrtl::BitsPrimOp>(operand, resultWidth - 1, 0);
617 else if (resultWidth > operandWidth)
618 newOp = builder.createOrFold<firrtl::PadPrimOp>(operand, resultWidth);
621 LLVM_DEBUG(llvm::dbgs() <<
"Forwarding " << newOp <<
" in " << *op <<
"\n");
622 result.replaceAllUsesWith(newOp);
626 std::string
getName()
const override {
627 return (
"firrtl-operand" + Twine(OpNum) +
"-forwarder").str();
634 void beforeReduction(mlir::ModuleOp op)
override {
638 anyrefCastDummy.clear();
639 op.walk<WalkOrder::PreOrder>([&](CircuitOp circuitOp) {
640 for (
auto classOp : circuitOp.getOps<ClassOp>()) {
641 if (classOp.getArguments().empty() && classOp.getBodyBlock()->empty()) {
642 anyrefCastDummy.insert({circuitOp, classOp});
643 anyrefCastDummyNames[circuitOp].insert(classOp.getNameAttr());
646 return WalkResult::skip();
650 uint64_t match(Operation *op)
override {
651 if (op->hasTrait<OpTrait::ConstantLike>()) {
653 if (!matchPattern(op, m_Constant(&attr)))
655 if (
auto intAttr = dyn_cast<IntegerAttr>(attr))
656 if (intAttr.getValue().isZero())
658 if (
auto strAttr = dyn_cast<StringAttr>(attr))
661 if (
auto floatAttr = dyn_cast<FloatAttr>(attr))
662 if (floatAttr.getValue().isZero())
665 if (
auto listOp = dyn_cast<ListCreateOp>(op))
666 if (listOp.getElements().empty())
668 if (
auto pathOp = dyn_cast<UnresolvedPathOp>(op))
669 if (pathOp.getTarget().empty())
673 if (
auto anyrefCastOp = dyn_cast<ObjectAnyRefCastOp>(op)) {
674 auto circuitOp = anyrefCastOp->getParentOfType<CircuitOp>();
676 anyrefCastOp.getInput().getType().getNameAttr().getAttr();
677 if (anyrefCastDummyNames[circuitOp].contains(className))
681 if (op->getNumResults() != 1)
683 if (op->hasAttr(
"inner_sym"))
685 if (isFlowSensitiveOp(op))
687 return isa<UIntType, SIntType, StringType, FIntegerType, BoolType,
688 DoubleType, ListType, PathType, AnyRefType>(
689 op->getResult(0).getType());
692 LogicalResult rewrite(Operation *op)
override {
693 OpBuilder builder(op);
694 auto type = op->getResult(0).getType();
697 if (isa<UIntType, SIntType>(type)) {
698 auto width = cast<FIRRTLBaseType>(type).getBitWidthOrSentinel();
701 auto newOp = ConstantOp::create(builder, op->getLoc(), type,
702 APSInt(width, isa<UIntType>(type)));
703 op->replaceAllUsesWith(newOp);
709 if (isa<StringType>(type)) {
710 auto attr = builder.getStringAttr(
"");
711 auto newOp = StringConstantOp::create(builder, op->getLoc(), attr);
712 op->replaceAllUsesWith(newOp);
718 if (isa<FIntegerType>(type)) {
719 auto attr = builder.getIntegerAttr(builder.getIntegerType(64,
true), 0);
720 auto newOp = FIntegerConstantOp::create(builder, op->getLoc(), attr);
721 op->replaceAllUsesWith(newOp);
727 if (isa<BoolType>(type)) {
728 auto attr = builder.getBoolAttr(
false);
729 auto newOp = BoolConstantOp::create(builder, op->getLoc(), attr);
730 op->replaceAllUsesWith(newOp);
736 if (isa<DoubleType>(type)) {
737 auto attr = builder.getFloatAttr(builder.getF64Type(), 0.0);
738 auto newOp = DoubleConstantOp::create(builder, op->getLoc(), attr);
739 op->replaceAllUsesWith(newOp);
745 if (isa<ListType>(type)) {
747 ListCreateOp::create(builder, op->getLoc(), type, ValueRange{});
748 op->replaceAllUsesWith(newOp);
754 if (isa<PathType>(type)) {
755 auto newOp = UnresolvedPathOp::create(builder, op->getLoc(),
"");
756 op->replaceAllUsesWith(newOp);
762 if (isa<AnyRefType>(type)) {
763 auto circuitOp = op->getParentOfType<CircuitOp>();
764 auto &dummy = anyrefCastDummy[circuitOp];
766 OpBuilder::InsertionGuard guard(builder);
767 builder.setInsertionPointToStart(circuitOp.getBodyBlock());
768 auto &symbolTable = symbols.getNearestSymbolTable(op);
769 dummy = ClassOp::create(builder, op->getLoc(),
"Dummy", {}, {});
770 symbolTable.insert(dummy);
771 anyrefCastDummyNames[circuitOp].insert(dummy.getNameAttr());
773 auto objectOp = ObjectOp::create(builder, op->getLoc(), dummy,
"dummy");
775 ObjectAnyRefCastOp::create(builder, op->getLoc(), objectOp);
776 op->replaceAllUsesWith(anyrefOp);
784 std::string
getName()
const override {
return "firrtl-constantifier"; }
785 bool acceptSizeIncrease()
const override {
return true; }
787 ::detail::SymbolCache symbols;
788 SmallDenseMap<CircuitOp, ClassOp, 2> anyrefCastDummy;
789 SmallDenseMap<CircuitOp, DenseSet<StringAttr>, 2> anyrefCastDummyNames;
796struct ConnectInvalidator :
public Reduction {
797 uint64_t match(Operation *op)
override {
798 if (!isa<FConnectLike>(op))
800 if (
auto *srcOp = op->getOperand(1).getDefiningOp())
801 if (srcOp->hasTrait<OpTrait::ConstantLike>() ||
802 isa<InvalidValueOp>(srcOp))
804 auto type = dyn_cast<FIRRTLBaseType>(op->getOperand(1).getType());
805 return type && type.isPassive();
807 LogicalResult rewrite(Operation *op)
override {
809 auto rhs = op->getOperand(1);
810 OpBuilder builder(op);
811 auto invOp = InvalidValueOp::create(builder, rhs.getLoc(), rhs.getType());
812 auto *rhsOp = rhs.getDefiningOp();
813 op->setOperand(1, invOp);
818 std::string
getName()
const override {
return "connect-invalidator"; }
819 bool acceptSizeIncrease()
const override {
return true; }
825struct AnnotationRemover :
public Reduction {
826 void beforeReduction(mlir::ModuleOp op)
override { nlaRemover.clear(); }
827 void afterReduction(mlir::ModuleOp op)
override { nlaRemover.remove(op); }
829 void matches(Operation *op,
830 llvm::function_ref<
void(uint64_t, uint64_t)> addMatch)
override {
831 uint64_t matchId = 0;
834 if (
auto annos = op->getAttrOfType<ArrayAttr>(
"annotations"))
835 for (
unsigned i = 0; i < annos.size(); ++i)
836 addMatch(1, matchId++);
839 if (
auto portAnnos = op->getAttrOfType<ArrayAttr>(
"portAnnotations"))
840 for (
auto portAnnoArray : portAnnos)
841 if (
auto portAnnoArrayAttr = dyn_cast<ArrayAttr>(portAnnoArray))
842 for (
unsigned i = 0; i < portAnnoArrayAttr.size(); ++i)
843 addMatch(1, matchId++);
846 LogicalResult rewriteMatches(Operation *op,
847 ArrayRef<uint64_t> matches)
override {
849 llvm::SmallDenseSet<uint64_t, 4> matchesSet(matches.begin(), matches.end());
852 uint64_t matchId = 0;
853 auto processAnnotations =
854 [&](ArrayRef<Attribute> annotations) -> ArrayAttr {
855 SmallVector<Attribute> newAnnotations;
856 for (
auto anno : annotations) {
857 if (!matchesSet.contains(matchId)) {
858 newAnnotations.push_back(anno);
861 nlaRemover.markNLAsInAnnotation(anno);
865 return ArrayAttr::get(op->getContext(), newAnnotations);
869 if (
auto annos = op->getAttrOfType<ArrayAttr>(
"annotations")) {
870 op->setAttr(
"annotations", processAnnotations(annos.getValue()));
874 if (
auto portAnnos = op->getAttrOfType<ArrayAttr>(
"portAnnotations")) {
875 SmallVector<Attribute> newPortAnnos;
876 for (
auto portAnnoArrayAttr : portAnnos.getAsRange<ArrayAttr>()) {
877 newPortAnnos.push_back(
878 processAnnotations(portAnnoArrayAttr.getValue()));
880 op->setAttr(
"portAnnotations",
881 ArrayAttr::get(op->getContext(), newPortAnnos));
887 std::string
getName()
const override {
return "annotation-remover"; }
888 NLARemover nlaRemover;
894struct SimplifyResets :
public OpReduction<CircuitOp> {
895 uint64_t match(CircuitOp circuit)
override {
896 uint64_t numResets = 0;
897 AttrTypeWalker walker;
898 walker.addWalk([&](ResetType type) { ++numResets; });
900 circuit.walk([&](Operation *op) {
901 for (
auto result : op->getResults())
902 walker.walk(result.getType());
904 for (
auto ®ion : op->getRegions())
905 for (
auto &block : region)
906 for (
auto arg : block.getArguments())
907 walker.walk(arg.getType());
909 walker.walk(op->getAttrDictionary());
915 LogicalResult rewrite(CircuitOp circuit)
override {
916 auto uint1Type = UIntType::get(circuit->getContext(), 1,
false);
917 auto constUint1Type = UIntType::get(circuit->getContext(), 1,
true);
919 AttrTypeReplacer replacer;
920 replacer.addReplacement([&](ResetType type) {
921 return type.isConst() ? constUint1Type : uint1Type;
923 replacer.recursivelyReplaceElementsIn(circuit,
true,
928 circuit.walk([&](Operation *op) {
931 return anno.
isClass(fullResetAnnoClass, excludeFromFullResetAnnoClass,
932 fullAsyncResetAnnoClass,
933 ignoreFullAsyncResetAnnoClass);
937 if (
auto module = dyn_cast<FModuleLike>(op)) {
940 return anno.
isClass(fullResetAnnoClass, excludeFromFullResetAnnoClass,
941 fullAsyncResetAnnoClass,
942 ignoreFullAsyncResetAnnoClass);
950 std::string
getName()
const override {
return "firrtl-simplify-resets"; }
951 bool acceptSizeIncrease()
const override {
return true; }
956struct RootPortPruner :
public OpReduction<firrtl::FModuleOp> {
957 void matches(firrtl::FModuleOp module,
958 llvm::function_ref<
void(uint64_t, uint64_t)> addMatch)
override {
959 auto circuit =
module->getParentOfType<firrtl::CircuitOp>();
960 if (!circuit || circuit.getNameAttr() != module.getNameAttr())
964 size_t numPorts =
module.getNumPorts();
965 for (
unsigned i = 0; i != numPorts; ++i) {
971 LogicalResult rewriteMatches(firrtl::FModuleOp module,
972 ArrayRef<uint64_t> matches)
override {
974 llvm::BitVector dropPorts(module.getNumPorts());
975 for (
auto portIdx : matches)
976 dropPorts.set(portIdx);
979 for (
auto portIdx : matches) {
981 llvm::make_early_inc_range(module.getArgument(portIdx).getUsers()))
986 module.erasePorts(dropPorts);
990 std::string
getName()
const override {
return "root-port-pruner"; }
996struct RootExtmodulePortPruner :
public OpReduction<firrtl::FExtModuleOp> {
997 void matches(firrtl::FExtModuleOp module,
998 llvm::function_ref<
void(uint64_t, uint64_t)> addMatch)
override {
999 auto circuit =
module->getParentOfType<firrtl::CircuitOp>();
1000 if (!circuit || circuit.getNameAttr() != module.getNameAttr())
1005 size_t numPorts =
module.getNumPorts();
1006 for (
unsigned i = 0; i != numPorts; ++i)
1010 LogicalResult rewriteMatches(firrtl::FExtModuleOp module,
1011 ArrayRef<uint64_t> matches)
override {
1012 if (matches.empty())
1016 llvm::BitVector dropPorts(module.getNumPorts());
1017 for (
auto portIdx : matches)
1018 dropPorts.set(portIdx);
1021 module.erasePorts(dropPorts);
1025 std::string
getName()
const override {
return "root-extmodule-port-pruner"; }
1030struct ExtmoduleInstanceRemover :
public OpReduction<firrtl::InstanceOp> {
1031 void beforeReduction(mlir::ModuleOp op)
override {
1035 void afterReduction(mlir::ModuleOp op)
override { nlaRemover.remove(op); }
1037 uint64_t match(firrtl::InstanceOp instOp)
override {
1038 return isa<firrtl::FExtModuleOp>(
1039 instOp.getReferencedOperation(symbols.getNearestSymbolTable(instOp)));
1041 LogicalResult rewrite(firrtl::InstanceOp instOp)
override {
1043 cast<firrtl::FModuleLike>(instOp.getReferencedOperation(
1044 symbols.getNearestSymbolTable(instOp)))
1046 ImplicitLocOpBuilder builder(instOp.getLoc(), instOp);
1047 TieOffCache tieOffCache(builder);
1048 SmallVector<Value> replacementWires;
1049 for (firrtl::PortInfo info : portInfo) {
1050 auto wire = firrtl::WireOp::create(
1052 (Twine(instOp.getName()) +
"_" +
info.getName()).str())
1054 if (
info.isOutput()) {
1056 if (
auto baseType = dyn_cast<firrtl::FIRRTLBaseType>(
info.type)) {
1057 auto inv = InvalidValueOp::create(builder, baseType);
1058 firrtl::ConnectOp::create(builder, wire, inv);
1059 }
else if (
auto propType = dyn_cast<firrtl::PropertyType>(
info.type)) {
1060 auto unknown = tieOffCache.
getUnknown(propType);
1061 builder.create<firrtl::PropAssignOp>(wire, unknown);
1064 replacementWires.push_back(wire);
1066 nlaRemover.markNLAsInOperation(instOp);
1067 instOp.replaceAllUsesWith(std::move(replacementWires));
1071 std::string
getName()
const override {
return "extmodule-instance-remover"; }
1072 bool acceptSizeIncrease()
const override {
return true; }
1074 ::detail::SymbolCache symbols;
1075 NLARemover nlaRemover;
1083struct PortPrunerHelpers {
1085 template <
typename ModuleOpType>
1086 static void computeUnusedInstancePorts(ModuleOpType module,
1087 ArrayRef<Operation *> users,
1088 llvm::BitVector &portsToRemove) {
1089 auto ports =
module.getPorts();
1090 for (
size_t portIdx = 0; portIdx < ports.size(); ++portIdx) {
1091 bool portUsed =
false;
1092 for (
auto *user : users) {
1093 if (
auto instOp = dyn_cast<firrtl::InstanceOp>(user)) {
1094 auto result = instOp.getResult(portIdx);
1095 if (!result.use_empty()) {
1102 portsToRemove.set(portIdx);
1108 updateInstancesAndErasePorts(Operation *module, ArrayRef<Operation *> users,
1109 const llvm::BitVector &portsToRemove) {
1111 SmallVector<firrtl::InstanceOp> instancesToUpdate;
1112 for (
auto *user : users) {
1113 if (
auto instOp = dyn_cast<firrtl::InstanceOp>(user))
1114 instancesToUpdate.push_back(instOp);
1117 for (
auto instOp : instancesToUpdate) {
1118 auto newInst = instOp.cloneWithErasedPorts(portsToRemove);
1121 size_t newResultIdx = 0;
1122 for (
size_t oldResultIdx = 0; oldResultIdx < instOp.getNumResults();
1124 if (portsToRemove[oldResultIdx]) {
1126 assert(instOp.getResult(oldResultIdx).use_empty() &&
1127 "removing port with uses");
1130 instOp.getResult(oldResultIdx)
1131 .replaceAllUsesWith(newInst->getResult(newResultIdx));
1142struct ModulePortPruner :
public OpReduction<firrtl::FModuleOp> {
1143 void beforeReduction(mlir::ModuleOp op)
override {
1147 void afterReduction(mlir::ModuleOp op)
override { nlaRemover.remove(op); }
1149 void matches(firrtl::FModuleOp module,
1150 llvm::function_ref<
void(uint64_t, uint64_t)> addMatch)
override {
1151 auto *tableOp = SymbolTable::getNearestSymbolTable(module);
1152 auto &userMap = symbols.getSymbolUserMap(tableOp);
1153 auto ports =
module.getPorts();
1154 auto users = userMap.getUsers(module);
1158 llvm::BitVector portsToRemove(ports.size());
1162 PortPrunerHelpers::computeUnusedInstancePorts(module, users,
1166 portsToRemove.set();
1171 for (
size_t portIdx = 0; portIdx < ports.size(); ++portIdx) {
1172 if (!portsToRemove[portIdx])
1174 if (!module.getArgument(portIdx).use_empty())
1175 portsToRemove.reset(portIdx);
1179 for (
size_t portIdx = 0; portIdx < ports.size(); ++portIdx)
1180 if (portsToRemove[portIdx])
1181 addMatch(1, portIdx);
1184 LogicalResult rewriteMatches(firrtl::FModuleOp module,
1185 ArrayRef<uint64_t> matches)
override {
1186 if (matches.empty())
1190 llvm::BitVector portsToRemove(module.getNumPorts());
1191 for (
auto portIdx : matches)
1192 portsToRemove.set(portIdx);
1195 auto *tableOp = SymbolTable::getNearestSymbolTable(module);
1196 auto &userMap = symbols.getSymbolUserMap(tableOp);
1197 auto users = userMap.getUsers(module);
1200 PortPrunerHelpers::updateInstancesAndErasePorts(module, users,
1205 module.erasePorts(portsToRemove);
1210 std::string
getName()
const override {
return "module-port-pruner"; }
1212 ::detail::SymbolCache symbols;
1213 NLARemover nlaRemover;
1217struct ExtmodulePortPruner :
public OpReduction<firrtl::FExtModuleOp> {
1218 void beforeReduction(mlir::ModuleOp op)
override {
1222 void afterReduction(mlir::ModuleOp op)
override { nlaRemover.remove(op); }
1224 void matches(firrtl::FExtModuleOp module,
1225 llvm::function_ref<
void(uint64_t, uint64_t)> addMatch)
override {
1226 auto *tableOp = SymbolTable::getNearestSymbolTable(module);
1227 auto &userMap = symbols.getSymbolUserMap(tableOp);
1228 auto ports =
module.getPorts();
1229 auto users = userMap.getUsers(module);
1232 llvm::BitVector portsToRemove(ports.size());
1234 if (users.empty()) {
1236 portsToRemove.set();
1240 PortPrunerHelpers::computeUnusedInstancePorts(module, users,
1245 for (
size_t portIdx = 0; portIdx < ports.size(); ++portIdx)
1246 if (portsToRemove[portIdx])
1247 addMatch(1, portIdx);
1250 LogicalResult rewriteMatches(firrtl::FExtModuleOp module,
1251 ArrayRef<uint64_t> matches)
override {
1252 if (matches.empty())
1256 llvm::BitVector portsToRemove(module.getNumPorts());
1257 for (
auto portIdx : matches)
1258 portsToRemove.set(portIdx);
1261 auto *tableOp = SymbolTable::getNearestSymbolTable(module);
1262 auto &userMap = symbols.getSymbolUserMap(tableOp);
1263 auto users = userMap.getUsers(module);
1266 PortPrunerHelpers::updateInstancesAndErasePorts(module, users,
1270 module.erasePorts(portsToRemove);
1275 std::string
getName()
const override {
return "extmodule-port-pruner"; }
1277 ::detail::SymbolCache symbols;
1278 NLARemover nlaRemover;
1282struct ConnectForwarder :
public Reduction {
1283 void beforeReduction(mlir::ModuleOp op)
override {
1284 domInfo = std::make_unique<DominanceInfo>(op);
1287 uint64_t match(Operation *op)
override {
1288 if (!isa<firrtl::FConnectLike>(op))
1290 auto dest = op->getOperand(0);
1291 auto src = op->getOperand(1);
1292 auto *destOp = dest.getDefiningOp();
1293 auto *srcOp = src.getDefiningOp();
1299 if (!isa_and_nonnull<firrtl::WireOp, firrtl::RegOp, firrtl::RegResetOp>(
1305 unsigned numConnects = 0;
1306 for (
auto &use : dest.getUses()) {
1307 auto *op = use.getOwner();
1308 if (use.getOperandNumber() == 0 && isa<firrtl::FConnectLike>(op)) {
1309 if (++numConnects > 1)
1316 !domInfo->properlyDominates(srcOp, op,
false))
1323 LogicalResult rewrite(Operation *op)
override {
1324 auto dst = op->getOperand(0);
1325 auto src = op->getOperand(1);
1326 dst.replaceAllUsesExcept(src, op);
1328 SmallVector<Operation *> worklist(
1329 {dst.getDefiningOp(), src.getDefiningOp()});
1334 std::string
getName()
const override {
return "connect-forwarder"; }
1337 std::unique_ptr<DominanceInfo> domInfo;
1342template <
unsigned OpNum>
1343struct ConnectSourceOperandForwarder :
public Reduction {
1344 uint64_t match(Operation *op)
override {
1345 if (!isa<firrtl::ConnectOp, firrtl::MatchingConnectOp>(op))
1347 auto dest = op->getOperand(0);
1348 auto *destOp = dest.getDefiningOp();
1351 if (!destOp || !destOp->hasOneUse() ||
1352 !isa<firrtl::WireOp, firrtl::RegOp, firrtl::RegResetOp>(destOp))
1355 auto *srcOp = op->getOperand(1).getDefiningOp();
1356 if (!srcOp || OpNum >= srcOp->getNumOperands())
1359 auto resultTy = dyn_cast<firrtl::FIRRTLBaseType>(dest.getType());
1361 dyn_cast<firrtl::FIRRTLBaseType>(srcOp->getOperand(OpNum).getType());
1363 return resultTy && opTy &&
1364 resultTy.getWidthlessType() == opTy.getWidthlessType() &&
1365 ((resultTy.getBitWidthOrSentinel() == -1) ==
1366 (opTy.getBitWidthOrSentinel() == -1)) &&
1367 isa<firrtl::UIntType, firrtl::SIntType>(resultTy);
1370 LogicalResult rewrite(Operation *op)
override {
1371 auto *destOp = op->getOperand(0).getDefiningOp();
1372 auto *srcOp = op->getOperand(1).getDefiningOp();
1373 auto forwardedOperand = srcOp->getOperand(OpNum);
1374 ImplicitLocOpBuilder builder(destOp->getLoc(), destOp);
1376 if (
auto wire = dyn_cast<firrtl::WireOp>(destOp))
1377 newDest = firrtl::WireOp::create(builder, forwardedOperand.getType(),
1381 auto regName = destOp->getAttrOfType<StringAttr>(
"name");
1384 auto clock = destOp->getOperand(0);
1385 newDest = firrtl::RegOp::create(builder, forwardedOperand.getType(),
1386 clock, regName ? regName.str() :
"")
1391 builder.setInsertionPointAfter(op);
1392 if (isa<firrtl::ConnectOp>(op))
1393 firrtl::ConnectOp::create(builder, newDest, forwardedOperand);
1395 firrtl::MatchingConnectOp::create(builder, newDest, forwardedOperand);
1405 std::string
getName()
const override {
1406 return (
"connect-source-operand-" + Twine(OpNum) +
"-forwarder").str();
1413struct DetachSubaccesses :
public Reduction {
1414 void beforeReduction(mlir::ModuleOp op)
override { opsToErase.clear(); }
1415 void afterReduction(mlir::ModuleOp op)
override {
1416 for (
auto *op : opsToErase)
1417 op->dropAllReferences();
1418 for (
auto *op : opsToErase)
1421 uint64_t match(Operation *op)
override {
1424 return isa<firrtl::WireOp, firrtl::RegOp, firrtl::RegResetOp>(op) &&
1425 llvm::all_of(op->getUses(), [](
auto &use) {
1426 return use.getOperandNumber() == 0 &&
1427 isa<firrtl::SubfieldOp, firrtl::SubindexOp,
1428 firrtl::SubaccessOp>(use.getOwner());
1431 LogicalResult rewrite(Operation *op)
override {
1433 OpBuilder builder(op);
1434 bool isWire = isa<firrtl::WireOp>(op);
1437 invalidClock = firrtl::InvalidValueOp::create(
1438 builder, op->getLoc(), firrtl::ClockType::get(op->getContext()));
1439 for (Operation *user : llvm::make_early_inc_range(op->getUsers())) {
1440 builder.setInsertionPoint(user);
1441 auto type = user->getResult(0).getType();
1444 replOp = firrtl::WireOp::create(builder, user->getLoc(), type);
1447 firrtl::RegOp::create(builder, user->getLoc(), type, invalidClock);
1448 user->replaceAllUsesWith(replOp);
1449 opsToErase.insert(user);
1451 opsToErase.insert(op);
1454 std::string
getName()
const override {
return "detach-subaccesses"; }
1455 llvm::DenseSet<Operation *> opsToErase;
1461struct NodeSymbolRemover :
public Reduction {
1462 void beforeReduction(mlir::ModuleOp op)
override {
1463 innerSymUses = reduce::InnerSymbolUses(op);
1466 uint64_t match(Operation *op)
override {
1468 auto sym = op->getAttrOfType<hw::InnerSymAttr>(
"inner_sym");
1469 if (!sym || sym.empty())
1473 if (innerSymUses.hasInnerRef(op))
1478 LogicalResult rewrite(Operation *op)
override {
1479 op->removeAttr(
"inner_sym");
1483 std::string
getName()
const override {
return "node-symbol-remover"; }
1484 bool acceptSizeIncrease()
const override {
return true; }
1486 reduce::InnerSymbolUses innerSymUses;
1492hasInnerSymbolCollision(Operation *referencedOp, Operation *parentOp,
1501 LogicalResult walkResult = targetTable.
walkSymbols(
1504 if (parentTable.lookup(name)) {
1512 return failed(walkResult);
1516struct EagerInliner :
public OpReduction<InstanceOp> {
1517 void beforeReduction(mlir::ModuleOp op)
override {
1521 for (
auto circuitOp : op.getOps<CircuitOp>())
1522 nlaTables.insert({circuitOp, std::make_unique<NLATable>(circuitOp)});
1523 innerSymTables = std::make_unique<hw::InnerSymbolTableCollection>();
1525 void afterReduction(mlir::ModuleOp op)
override {
1526 nlaRemover.remove(op);
1528 innerSymTables.reset();
1531 uint64_t match(InstanceOp instOp)
override {
1532 auto *tableOp = SymbolTable::getNearestSymbolTable(instOp);
1534 instOp.getReferencedOperation(symbols.getSymbolTable(tableOp));
1537 if (!isa<FModuleOp>(moduleOp))
1541 auto circuitOp = instOp->getParentOfType<CircuitOp>();
1544 auto it = nlaTables.find(circuitOp);
1545 if (it == nlaTables.end() || !it->second)
1547 DenseSet<hw::HierPathOp> nlas;
1548 it->second->getInstanceNLAs(instOp, nlas);
1554 auto parentOp = instOp->getParentOfType<FModuleLike>();
1555 if (hasInnerSymbolCollision(moduleOp, parentOp, *innerSymTables))
1561 LogicalResult rewrite(InstanceOp instOp)
override {
1562 auto *tableOp = SymbolTable::getNearestSymbolTable(instOp);
1563 auto moduleOp = cast<FModuleOp>(
1564 instOp.getReferencedOperation(symbols.getSymbolTable(tableOp)));
1566 (symbols.getSymbolUserMap(tableOp).getUsers(moduleOp).size() == 1);
1567 auto clonedModuleOp = isLastUse ? moduleOp : moduleOp.clone();
1570 IRRewriter rewriter(instOp);
1571 SmallVector<Value> argWires;
1572 for (
unsigned i = 0, e = instOp.getNumResults(); i != e; ++i) {
1573 auto result = instOp.getResult(i);
1574 auto name = rewriter.getStringAttr(Twine(instOp.getName()) +
"_" +
1575 instOp.getPortName(i));
1576 auto wire = WireOp::create(rewriter, instOp.getLoc(), result.getType(),
1577 name, NameKindEnum::DroppableName,
1578 instOp.getPortAnnotation(i), StringAttr{})
1580 result.replaceAllUsesWith(wire);
1581 argWires.push_back(wire);
1585 rewriter.inlineBlockBefore(clonedModuleOp.getBodyBlock(), instOp, argWires);
1589 nlaRemover.markNLAsInOperation(instOp);
1591 nlaRemover.markNLAsInOperation(moduleOp);
1594 clonedModuleOp.erase();
1598 std::string
getName()
const override {
return "firrtl-eager-inliner"; }
1599 bool acceptSizeIncrease()
const override {
return true; }
1601 ::detail::SymbolCache symbols;
1602 NLARemover nlaRemover;
1603 DenseMap<CircuitOp, std::unique_ptr<NLATable>> nlaTables;
1604 std::unique_ptr<hw::InnerSymbolTableCollection> innerSymTables;
1608struct ObjectInliner :
public OpReduction<ObjectOp> {
1609 void beforeReduction(mlir::ModuleOp op)
override {
1610 blocksToSort.clear();
1613 innerSymTables = std::make_unique<hw::InnerSymbolTableCollection>();
1615 void afterReduction(mlir::ModuleOp op)
override {
1616 for (
auto *block : blocksToSort)
1617 mlir::sortTopologically(block);
1618 blocksToSort.clear();
1619 nlaRemover.remove(op);
1620 innerSymTables.reset();
1623 uint64_t match(ObjectOp objOp)
override {
1624 auto *tableOp = SymbolTable::getNearestSymbolTable(objOp);
1626 objOp.getReferencedOperation(symbols.getSymbolTable(tableOp));
1629 if (!isa<ClassOp>(classOp))
1634 auto parentOp = objOp->getParentOfType<FModuleLike>();
1635 if (hasInnerSymbolCollision(classOp, parentOp, *innerSymTables))
1639 for (
auto *user : objOp.getResult().getUsers())
1640 if (!isa<ObjectSubfieldOp>(user))
1646 LogicalResult rewrite(ObjectOp objOp)
override {
1647 auto *tableOp = SymbolTable::getNearestSymbolTable(objOp);
1648 auto classOp = cast<ClassOp>(
1649 objOp.getReferencedOperation(symbols.getSymbolTable(tableOp)));
1650 auto clonedClassOp = classOp.clone();
1653 IRRewriter rewriter(objOp);
1654 SmallVector<Value> portWires;
1655 auto classType = objOp.getType();
1658 for (
unsigned i = 0, e = classType.getNumElements(); i != e; ++i) {
1659 auto element = classType.getElement(i);
1660 auto name = rewriter.getStringAttr(Twine(objOp.getName()) +
"_" +
1661 element.name.getValue());
1662 auto wire = WireOp::create(rewriter, objOp.getLoc(), element.type, name,
1663 NameKindEnum::DroppableName,
1664 rewriter.getArrayAttr({}), StringAttr{})
1666 portWires.push_back(wire);
1670 SmallVector<ObjectSubfieldOp> subfieldOps;
1671 for (
auto *user : objOp.getResult().getUsers()) {
1672 auto subfieldOp = cast<ObjectSubfieldOp>(user);
1673 subfieldOps.push_back(subfieldOp);
1674 auto index = subfieldOp.getIndex();
1675 subfieldOp.getResult().replaceAllUsesWith(portWires[index]);
1679 rewriter.inlineBlockBefore(clonedClassOp.getBodyBlock(), objOp, portWires);
1685 SmallVector<FConnectLike> connectsToErase;
1686 for (
auto portWire : portWires) {
1690 for (
auto *user : portWire.getUsers()) {
1691 if (
auto connect = dyn_cast<FConnectLike>(user)) {
1692 if (
connect.getDest() == portWire) {
1694 connectsToErase.push_back(connect);
1704 portWire.replaceAllUsesWith(value);
1705 for (
auto connect : connectsToErase)
1707 if (portWire.use_empty())
1708 portWire.getDefiningOp()->erase();
1709 connectsToErase.clear();
1713 nlaRemover.markNLAsInOperation(objOp);
1718 blocksToSort.insert(objOp->getBlock());
1721 for (
auto subfieldOp : subfieldOps)
1724 clonedClassOp.erase();
1728 std::string
getName()
const override {
return "firrtl-object-inliner"; }
1729 bool acceptSizeIncrease()
const override {
return true; }
1731 SetVector<Block *> blocksToSort;
1732 ::detail::SymbolCache symbols;
1733 NLARemover nlaRemover;
1734 std::unique_ptr<hw::InnerSymbolTableCollection> innerSymTables;
1739struct ResetDisconnector :
public OpReduction<RegResetOp> {
1740 uint64_t match(RegResetOp op)
override {
return 1; }
1742 LogicalResult rewrite(RegResetOp regResetOp)
override {
1743 ImplicitLocOpBuilder builder(regResetOp.getLoc(), regResetOp);
1744 auto regOp = RegOp::create(
1745 builder, regResetOp.getResult().getType(), regResetOp.getClockVal(),
1746 regResetOp.getNameAttr(), regResetOp.getNameKindAttr(),
1747 regResetOp.getAnnotationsAttr(), regResetOp.getInnerSymAttr(),
1748 regResetOp.getForceableAttr(), regResetOp.getInitialAttr());
1750 regResetOp.getResult().replaceAllUsesWith(regOp.getResult());
1751 if (regResetOp.getForceable())
1752 regResetOp.getRef().replaceAllUsesWith(regOp.getRef());
1758 std::string
getName()
const override {
return "reset-disconnector"; }
1772struct ModuleInternalNameSanitizer :
public Reduction {
1773 uint64_t
match(Operation *op)
override {
1775 return isa<firrtl::WireOp, firrtl::RegOp, firrtl::RegResetOp,
1776 firrtl::NodeOp, firrtl::MemOp, chirrtl::CombMemOp,
1777 chirrtl::SeqMemOp, firrtl::AssertOp, firrtl::AssumeOp,
1778 firrtl::CoverOp>(op);
1780 LogicalResult
rewrite(Operation *op)
override {
1781 TypeSwitch<Operation *, void>(op)
1782 .Case<firrtl::WireOp>([](
auto op) { op.setName(
"wire"); })
1783 .Case<firrtl::RegOp, firrtl::RegResetOp>(
1784 [](
auto op) { op.setName(
"reg"); })
1785 .Case<firrtl::NodeOp>([](
auto op) { op.setName(
"node"); })
1786 .Case<firrtl::MemOp, chirrtl::CombMemOp, chirrtl::SeqMemOp>(
1787 [](
auto op) { op.setName(
"mem"); })
1788 .Case<firrtl::AssertOp, firrtl::AssumeOp, firrtl::CoverOp>([](
auto op) {
1789 op->setAttr(
"message", StringAttr::get(op.getContext(),
""));
1790 op->setAttr(
"name", StringAttr::get(op.getContext(),
""));
1795 std::string
getName()
const override {
1796 return "module-internal-name-sanitizer";
1801 bool isOneShot()
const override {
return true; }
1815struct ModuleNameSanitizer :
OpReduction<firrtl::CircuitOp> {
1817 reduce::MetasyntacticNameGenerator nameGenerator;
1818 size_t portNameIndex = 0;
1821 if (portNameIndex >= 26)
1823 return 'a' + portNameIndex++;
1826 void beforeReduction(mlir::ModuleOp op)
override { nameGenerator.reset(); }
1828 LogicalResult
rewrite(firrtl::CircuitOp circuitOp)
override {
1831 firrtl::InstanceGraph iGraph(circuitOp);
1832 NLATable nlaTable(circuitOp);
1833 SymbolTable symTable(circuitOp);
1834 CircuitNamespace ns(circuitOp);
1837 auto renameModule = [&](firrtl::FModuleLike mod,
1838 StringAttr newName) -> LogicalResult {
1839 StringAttr oldName = mod.getModuleNameAttr();
1840 if (failed(symTable.rename(mod, newName)))
1842 nlaTable.renameModule(oldName, newName);
1848 auto topModule = iGraph.getTopLevelModule();
1849 auto *ctx = circuitOp.getContext();
1851 topModule.getModuleName())) {
1852 auto newTopName = StringAttr::get(ctx, nameGenerator.getNextName(ns));
1853 if (failed(renameModule(topModule, newTopName)))
1855 circuitOp.setName(newTopName.getValue());
1858 for (
auto *node : iGraph) {
1859 auto module = node->getModule<firrtl::FModuleLike>();
1861 bool shouldReplacePorts =
false;
1862 SmallVector<Attribute> newPortNames;
1863 if (
auto fmodule = dyn_cast<firrtl::FModuleOp>(*module)) {
1867 circt::Namespace ns;
1868 auto oldPorts = fmodule.getPorts();
1869 shouldReplacePorts = !oldPorts.empty();
1870 for (
unsigned i = 0, e = fmodule.getNumPorts(); i != e; ++i) {
1871 auto port = oldPorts[i];
1872 auto newName = firrtl::FIRRTLTypeSwitch<Type, StringRef>(port.type)
1873 .Case<firrtl::ClockType>(
1874 [&](
auto a) {
return ns.
newName(
"clk"); })
1875 .Case<firrtl::ResetType, firrtl::AsyncResetType>(
1876 [&](
auto a) {
return ns.
newName(
"rst"); })
1877 .Case<firrtl::RefType>(
1878 [&](
auto a) {
return ns.
newName(
"ref"); })
1879 .Default([&](
auto a) {
1882 newPortNames.push_back(StringAttr::get(ctx, newName));
1884 fmodule->setAttr(
"portNames",
1885 ArrayAttr::get(fmodule.getContext(), newPortNames));
1888 if (module == iGraph.getTopLevelModule())
1892 module.getModuleName()))
1894 auto newName = StringAttr::get(ctx, nameGenerator.getNextName(ns));
1895 if (failed(renameModule(module, newName)))
1897 for (
auto *use : node->uses()) {
1898 auto useOp = use->getInstance();
1899 if (
auto instanceOp = dyn_cast<firrtl::InstanceOp>(*useOp)) {
1903 instanceOp.setName(newName);
1904 if (shouldReplacePorts)
1905 instanceOp.setPortNamesAttr(ArrayAttr::get(ctx, newPortNames));
1906 }
else if (
auto instanceChoiceOp =
1907 dyn_cast<firrtl::InstanceChoiceOp>(*useOp)) {
1908 if (instanceChoiceOp.getDefaultTargetAttr().getAttr() == newName)
1909 instanceChoiceOp.setName(newName);
1910 if (shouldReplacePorts)
1911 instanceChoiceOp.setPortNamesAttr(
1912 ArrayAttr::get(ctx, newPortNames));
1913 }
else if (
auto objectOp = dyn_cast<firrtl::ObjectOp>(*useOp)) {
1917 auto oldClassType = objectOp.getType();
1918 auto newClassType = firrtl::ClassType::get(
1919 ctx, FlatSymbolRefAttr::get(newName), oldClassType.getElements());
1920 objectOp.getResult().setType(newClassType);
1921 objectOp.setName(newName);
1929 std::string
getName()
const override {
return "module-name-sanitizer"; }
1933 bool isOneShot()
const override {
return true; }
1952struct ModuleSwapper :
public OpReduction<InstanceOp> {
1954 using PortSignature = SmallVector<std::pair<Type, Direction>>;
1955 struct CircuitState {
1956 DenseMap<PortSignature, SmallVector<FModuleLike, 4>> moduleTypeGroups;
1957 DenseMap<StringAttr, FModuleLike> instanceToCanonicalModule;
1958 std::unique_ptr<NLATable> nlaTable;
1961 void beforeReduction(mlir::ModuleOp op)
override {
1964 moduleSizes.clear();
1965 circuitStates.clear();
1968 op.walk<WalkOrder::PreOrder>([&](CircuitOp circuitOp) {
1969 auto &state = circuitStates[circuitOp];
1970 state.nlaTable = std::make_unique<NLATable>(circuitOp);
1971 buildModuleTypeGroups(circuitOp, state);
1972 return WalkResult::skip();
1975 void afterReduction(mlir::ModuleOp op)
override { nlaRemover.remove(op); }
1981 PortSignature getModulePortSignature(FModuleLike module) {
1982 PortSignature signature;
1983 signature.reserve(module.getNumPorts());
1984 for (
unsigned i = 0, e = module.getNumPorts(); i < e; ++i)
1985 signature.emplace_back(module.getPortType(i), module.getPortDirection(i));
1990 void buildModuleTypeGroups(CircuitOp circuitOp, CircuitState &state) {
1992 for (
auto module : circuitOp.getBodyBlock()->getOps<FModuleLike>()) {
1993 auto signature = getModulePortSignature(module);
1994 state.moduleTypeGroups[signature].push_back(module);
1998 for (
auto &[signature, modules] : state.moduleTypeGroups) {
1999 if (modules.size() <= 1)
2002 FModuleLike smallestModule =
nullptr;
2003 uint64_t smallestSize = std::numeric_limits<uint64_t>::max();
2005 for (
auto module : modules) {
2006 uint64_t size = moduleSizes.getModuleSize(module, symbols);
2007 if (size < smallestSize) {
2008 smallestSize = size;
2009 smallestModule =
module;
2014 for (
auto module : modules) {
2015 if (module != smallestModule) {
2016 state.instanceToCanonicalModule[
module.getModuleNameAttr()] =
2023 uint64_t match(InstanceOp instOp)
override {
2025 auto circuitOp = instOp->getParentOfType<CircuitOp>();
2027 const auto &state = circuitStates.at(circuitOp);
2030 DenseSet<hw::HierPathOp> nlas;
2031 state.nlaTable->getInstanceNLAs(instOp, nlas);
2036 auto moduleName = instOp.getModuleNameAttr().getAttr();
2037 auto canonicalModule = state.instanceToCanonicalModule.lookup(moduleName);
2038 if (!canonicalModule)
2042 auto currentModule = cast<FModuleLike>(
2043 instOp.getReferencedOperation(symbols.getNearestSymbolTable(instOp)));
2044 uint64_t currentSize = moduleSizes.getModuleSize(currentModule, symbols);
2045 uint64_t canonicalSize =
2046 moduleSizes.getModuleSize(canonicalModule, symbols);
2047 return currentSize > canonicalSize ? currentSize - canonicalSize : 1;
2050 LogicalResult rewrite(InstanceOp instOp)
override {
2052 auto circuitOp = instOp->getParentOfType<CircuitOp>();
2054 const auto &state = circuitStates.at(circuitOp);
2057 auto canonicalModule = state.instanceToCanonicalModule.at(
2058 instOp.getModuleNameAttr().getAttr());
2059 auto canonicalName = canonicalModule.getModuleNameAttr();
2060 instOp.setModuleNameAttr(FlatSymbolRefAttr::get(canonicalName));
2063 instOp.setPortNamesAttr(canonicalModule.getPortNamesAttr());
2068 std::string
getName()
const override {
return "firrtl-module-swapper"; }
2069 bool acceptSizeIncrease()
const override {
return true; }
2072 ::detail::SymbolCache symbols;
2073 NLARemover nlaRemover;
2074 ModuleSizeCache moduleSizes;
2077 DenseMap<CircuitOp, CircuitState> circuitStates;
2095struct ForceDedup :
public OpReduction<CircuitOp> {
2096 void beforeReduction(mlir::ModuleOp op)
override {
2099 modulesToErase.clear();
2100 moduleSizes.clear();
2102 void afterReduction(mlir::ModuleOp op)
override {
2103 nlaRemover.remove(op);
2104 for (
auto mod : modulesToErase)
2109 void matches(CircuitOp circuitOp,
2110 llvm::function_ref<
void(uint64_t, uint64_t)> addMatch)
override {
2111 auto &symbolTable = symbols.getNearestSymbolTable(circuitOp);
2112 auto annotations = AnnotationSet(circuitOp);
2113 for (
auto [annoIdx, anno] : llvm::enumerate(annotations)) {
2114 if (!anno.
isClass(mustDeduplicateAnnoClass))
2117 auto modulesAttr = anno.
getMember<ArrayAttr>(
"modules");
2118 if (!modulesAttr || modulesAttr.size() < 2)
2124 uint64_t totalSize = 0;
2125 ArrayAttr portTypes;
2126 DenseBoolArrayAttr portDirections;
2127 bool allSame =
true;
2128 for (
auto moduleName : modulesAttr.getAsRange<StringAttr>()) {
2134 auto mod = symbolTable.lookup<FModuleLike>(target->module);
2139 totalSize += moduleSizes.getModuleSize(mod, symbols);
2141 portTypes = mod.getPortTypesAttr();
2142 portDirections = mod.getPortDirectionsAttr();
2143 }
else if (portTypes != mod.getPortTypesAttr() ||
2144 portDirections != mod.getPortDirectionsAttr()) {
2154 addMatch(totalSize, annoIdx);
2158 LogicalResult rewriteMatches(CircuitOp circuitOp,
2159 ArrayRef<uint64_t> matches)
override {
2160 auto *
context = circuitOp->getContext();
2161 NLATable nlaTable(circuitOp);
2162 hw::InnerSymbolTableCollection innerSymTables;
2163 auto annotations = AnnotationSet(circuitOp);
2164 SmallVector<Annotation> newAnnotations;
2166 for (
auto [annoIdx, anno] : llvm::enumerate(annotations)) {
2168 if (!llvm::is_contained(matches, annoIdx)) {
2169 newAnnotations.push_back(anno);
2172 auto modulesAttr = anno.
getMember<ArrayAttr>(
"modules");
2173 assert(anno.
isClass(mustDeduplicateAnnoClass) && modulesAttr &&
2174 modulesAttr.size() >= 2);
2177 SmallVector<StringAttr> moduleNames;
2178 for (
auto moduleRef : modulesAttr.getAsRange<StringAttr>()) {
2180 auto refStr = moduleRef.getValue();
2181 auto pipePos = refStr.find(
'|');
2182 if (pipePos != StringRef::npos && pipePos + 1 < refStr.size()) {
2183 auto moduleName = refStr.substr(pipePos + 1);
2184 moduleNames.push_back(StringAttr::get(
context, moduleName));
2189 if (moduleNames.size() < 2)
2194 replaceModuleReferences(circuitOp, moduleNames, nlaTable, innerSymTables);
2195 nlaRemover.markNLAsInAnnotation(anno.
getAttr());
2197 if (newAnnotations.size() == annotations.size())
2201 AnnotationSet newAnnoSet(newAnnotations,
context);
2202 newAnnoSet.applyToOperation(circuitOp);
2206 std::string
getName()
const override {
return "firrtl-force-dedup"; }
2207 bool acceptSizeIncrease()
const override {
return true; }
2212 void replaceModuleReferences(CircuitOp circuitOp,
2213 ArrayRef<StringAttr> moduleNames,
2215 hw::InnerSymbolTableCollection &innerSymTables) {
2216 auto *tableOp = SymbolTable::getNearestSymbolTable(circuitOp);
2217 auto &symbolTable = symbols.getSymbolTable(tableOp);
2218 auto &symbolUserMap = symbols.getSymbolUserMap(tableOp);
2219 auto *
context = circuitOp->getContext();
2220 auto innerRefs = hw::InnerRefNamespace{symbolTable, innerSymTables};
2223 FModuleLike canonicalModule;
2224 SmallVector<FModuleLike> modulesToReplace;
2225 for (
auto name : moduleNames) {
2226 if (
auto mod = symbolTable.lookup<FModuleLike>(name)) {
2227 if (!canonicalModule)
2228 canonicalModule = mod;
2230 modulesToReplace.push_back(mod);
2233 if (modulesToReplace.empty())
2237 auto canonicalName = canonicalModule.getModuleNameAttr();
2238 auto canonicalRef = FlatSymbolRefAttr::get(canonicalName);
2239 for (
auto moduleName : moduleNames) {
2240 if (moduleName == canonicalName)
2242 auto *symbolOp = symbolTable.lookup(moduleName);
2245 for (
auto *user : symbolUserMap.getUsers(symbolOp)) {
2246 auto instOp = dyn_cast<InstanceOp>(user);
2247 if (!instOp || instOp.getModuleNameAttr().getAttr() != moduleName)
2249 instOp.setModuleNameAttr(canonicalRef);
2250 instOp.setPortNamesAttr(canonicalModule.getPortNamesAttr());
2256 for (
auto oldMod : modulesToReplace) {
2257 SmallVector<hw::HierPathOp> nlaOps(
2258 nlaTable.
lookup(oldMod.getModuleNameAttr()));
2259 for (
auto nlaOp : nlaOps) {
2260 nlaTable.
erase(nlaOp);
2261 StringAttr oldModName = oldMod.getModuleNameAttr();
2262 StringAttr newModName = canonicalName;
2263 SmallVector<Attribute, 4> newPath;
2264 for (
auto nameRef : nlaOp.getNamepath()) {
2265 if (
auto ref = dyn_cast<hw::InnerRefAttr>(nameRef)) {
2266 if (ref.getModule() == oldModName) {
2267 auto oldInst = innerRefs.lookupOp<FInstanceLike>(ref);
2268 ref = hw::InnerRefAttr::get(newModName, ref.getName());
2269 auto newInst = innerRefs.lookupOp<FInstanceLike>(ref);
2270 if (oldInst && newInst) {
2273 auto oldModNames = oldInst.getReferencedModuleNamesAttr();
2274 auto newModNames = newInst.getReferencedModuleNamesAttr();
2275 if (!oldModNames.empty() && !newModNames.empty()) {
2276 oldModName = cast<StringAttr>(oldModNames[0]);
2277 newModName = cast<StringAttr>(newModNames[0]);
2281 newPath.push_back(ref);
2282 }
else if (cast<FlatSymbolRefAttr>(nameRef).getAttr() == oldModName) {
2283 newPath.push_back(FlatSymbolRefAttr::get(newModName));
2285 newPath.push_back(nameRef);
2288 nlaOp.setNamepathAttr(ArrayAttr::get(
context, newPath));
2294 for (
auto module : modulesToReplace) {
2295 nlaRemover.markNLAsInOperation(module);
2296 modulesToErase.insert(module);
2300 ::detail::SymbolCache symbols;
2301 NLARemover nlaRemover;
2302 SetVector<FModuleLike> modulesToErase;
2303 ModuleSizeCache moduleSizes;
2322struct MustDedupChildren :
public OpReduction<CircuitOp> {
2323 void beforeReduction(mlir::ModuleOp op)
override {
2327 void afterReduction(mlir::ModuleOp op)
override { nlaRemover.remove(op); }
2331 void matches(CircuitOp circuitOp,
2332 llvm::function_ref<
void(uint64_t, uint64_t)> addMatch)
override {
2333 auto annotations = AnnotationSet(circuitOp);
2334 uint64_t matchId = 0;
2336 DenseSet<StringRef> modulesAlreadyInMustDedup;
2337 for (
auto [annoIdx, anno] : llvm::enumerate(annotations))
2338 if (anno.
isClass(mustDeduplicateAnnoClass))
2339 if (
auto modulesAttr = anno.
getMember<ArrayAttr>(
"modules"))
2340 for (
auto moduleRef : modulesAttr.getAsRange<StringAttr>())
2342 modulesAlreadyInMustDedup.insert(target->module);
2344 for (
auto [annoIdx, anno] : llvm::enumerate(annotations)) {
2345 if (!anno.
isClass(mustDeduplicateAnnoClass))
2348 auto modulesAttr = anno.
getMember<ArrayAttr>(
"modules");
2349 if (!modulesAttr || modulesAttr.size() < 2)
2353 processInstanceGroups(
2354 circuitOp, modulesAttr, [&](ArrayRef<FInstanceLike> instanceGroup) {
2358 SmallDenseSet<StringAttr, 4> moduleTargets;
2359 for (
auto instOp : instanceGroup) {
2360 auto moduleNames = instOp.getReferencedModuleNamesAttr();
2361 for (
auto moduleName : moduleNames)
2362 moduleTargets.insert(cast<StringAttr>(moduleName));
2364 if (moduleTargets.size() < 2)
2369 if (llvm::any_of(instanceGroup, [&](FInstanceLike inst) {
2370 auto moduleNames = inst.getReferencedModuleNames();
2371 return llvm::any_of(moduleNames, [&](StringRef moduleName) {
2372 return modulesAlreadyInMustDedup.contains(moduleName);
2377 addMatch(1, matchId - 1);
2382 LogicalResult rewriteMatches(CircuitOp circuitOp,
2383 ArrayRef<uint64_t> matches)
override {
2384 auto *
context = circuitOp->getContext();
2385 auto annotations = AnnotationSet(circuitOp);
2386 SmallVector<Annotation> newAnnotations;
2387 uint64_t matchId = 0;
2389 for (
auto [annoIdx, anno] : llvm::enumerate(annotations)) {
2390 if (!anno.
isClass(mustDeduplicateAnnoClass)) {
2391 newAnnotations.push_back(anno);
2395 auto modulesAttr = anno.
getMember<ArrayAttr>(
"modules");
2396 if (!modulesAttr || modulesAttr.size() < 2) {
2397 newAnnotations.push_back(anno);
2401 processInstanceGroups(
2402 circuitOp, modulesAttr, [&](ArrayRef<FInstanceLike> instanceGroup) {
2404 if (!llvm::is_contained(matches, matchId++))
2408 SmallSetVector<StringAttr, 4> moduleTargets;
2409 for (
auto instOp : instanceGroup) {
2410 auto moduleNames = instOp.getReferencedModuleNames();
2411 for (
auto moduleName : moduleNames) {
2412 auto target = TokenAnnoTarget();
2413 target.circuit = circuitOp.getName();
2414 target.module = moduleName;
2415 moduleTargets.insert(target.toStringAttr(
context));
2420 SmallVector<NamedAttribute> newAnnoAttrs;
2421 newAnnoAttrs.emplace_back(
2422 StringAttr::get(
context,
"class"),
2423 StringAttr::get(
context, mustDeduplicateAnnoClass));
2424 newAnnoAttrs.emplace_back(
2425 StringAttr::get(
context,
"modules"),
2427 SmallVector<Attribute>(moduleTargets.begin(),
2428 moduleTargets.end())));
2430 auto newAnnoDict = DictionaryAttr::get(
context, newAnnoAttrs);
2431 newAnnotations.emplace_back(newAnnoDict);
2435 newAnnotations.push_back(anno);
2439 AnnotationSet newAnnoSet(newAnnotations,
context);
2440 newAnnoSet.applyToOperation(circuitOp);
2444 std::string
getName()
const override {
return "must-dedup-children"; }
2445 bool acceptSizeIncrease()
const override {
return true; }
2452 void processInstanceGroups(
2453 CircuitOp circuitOp, ArrayAttr modulesAttr,
2454 llvm::function_ref<
void(ArrayRef<FInstanceLike>)> callback) {
2455 auto &symbolTable = symbols.getSymbolTable(circuitOp);
2458 SmallVector<FModuleLike> modules;
2459 for (
auto moduleRef : modulesAttr.getAsRange<StringAttr>())
2461 if (
auto mod = symbolTable.lookup<FModuleLike>(target->module))
2462 modules.push_back(mod);
2465 if (modules.size() < 2)
2472 struct InstanceGroup {
2473 SmallVector<FInstanceLike> instances;
2474 bool nameIsUnique =
true;
2476 MapVector<StringAttr, InstanceGroup> instanceGroups;
2477 for (
auto module : modules) {
2478 SmallDenseMap<StringAttr, unsigned> nameCounts;
2479 module.walk([&](FInstanceLike instOp) {
2480 if (isa<ObjectOp>(instOp.getOperation()))
2482 auto name = instOp.getInstanceNameAttr();
2483 auto &group = instanceGroups[name];
2484 if (nameCounts[name]++ > 1)
2485 group.nameIsUnique =
false;
2486 group.instances.push_back(instOp);
2492 for (
auto &[name, group] : instanceGroups)
2493 if (group.nameIsUnique && group.instances.size() >= 2)
2494 callback(group.instances);
2501struct LayerDisable :
public OpReduction<CircuitOp> {
2502 LayerDisable(MLIRContext *
context) {
2503 pm = std::make_unique<mlir::PassManager>(
2504 context,
"builtin.module", mlir::OpPassManager::Nesting::Explicit);
2505 pm->nest<firrtl::CircuitOp>().addPass(firrtl::createSpecializeLayers());
2508 void beforeReduction(mlir::ModuleOp op)
override { symbolRefAttrMap.clear(); }
2510 void afterReduction(mlir::ModuleOp op)
override { (void)pm->run(op); };
2512 void matches(CircuitOp circuitOp,
2513 llvm::function_ref<
void(uint64_t, uint64_t)> addMatch)
override {
2514 uint64_t matchId = 0;
2516 SmallVector<FlatSymbolRefAttr> nestedRefs;
2517 std::function<void(StringAttr, LayerOp)> addLayer = [&](StringAttr rootRef,
2520 rootRef = layerOp.getSymNameAttr();
2522 nestedRefs.push_back(FlatSymbolRefAttr::get(layerOp));
2524 symbolRefAttrMap[matchId] = SymbolRefAttr::get(rootRef, nestedRefs);
2525 addMatch(1, matchId++);
2527 for (
auto nestedLayerOp : layerOp.getOps<LayerOp>())
2528 addLayer(rootRef, nestedLayerOp);
2530 if (!nestedRefs.empty())
2531 nestedRefs.pop_back();
2534 for (
auto layerOp : circuitOp.getOps<LayerOp>())
2535 addLayer({}, layerOp);
2538 LogicalResult rewriteMatches(CircuitOp circuitOp,
2539 ArrayRef<uint64_t> matches)
override {
2540 SmallVector<Attribute> disableLayers;
2541 if (
auto existingDisables = circuitOp.getDisableLayersAttr()) {
2542 auto disableRange = existingDisables.getAsRange<Attribute>();
2543 disableLayers.append(disableRange.begin(), disableRange.end());
2545 for (
auto match : matches)
2546 disableLayers.push_back(symbolRefAttrMap.at(match));
2548 circuitOp.setDisableLayersAttr(
2549 ArrayAttr::get(circuitOp.getContext(), disableLayers));
2554 std::string
getName()
const override {
return "firrtl-layer-disable"; }
2556 std::unique_ptr<mlir::PassManager> pm;
2557 DenseMap<uint64_t, SymbolRefAttr> symbolRefAttrMap;
2562struct LayerEnableRemover :
public OpReduction<FModuleOp> {
2563 void matches(FModuleOp moduleOp,
2564 llvm::function_ref<
void(uint64_t, uint64_t)> addMatch)
override {
2566 auto layers = moduleOp.getLayersAttr();
2567 for (
size_t i = 0, e = layers.size(); i != e; ++i)
2571 LogicalResult rewriteMatches(FModuleOp moduleOp,
2572 ArrayRef<uint64_t> matches)
override {
2574 llvm::SmallDenseSet<uint64_t, 4> removed(matches.begin(), matches.end());
2575 SmallVector<Attribute> newLayers;
2576 auto layers = moduleOp.getLayersAttr();
2577 for (
size_t i = 0, e = layers.size(); i != e; ++i)
2578 if (!removed.contains(i))
2579 newLayers.push_back(layers[i]);
2581 moduleOp.setLayersAttr(ArrayAttr::get(moduleOp.getContext(), newLayers));
2585 std::string
getName()
const override {
return "firrtl-layer-enable-remover"; }
2595 llvm::function_ref<
void(uint64_t, uint64_t)> addMatch)
override {
2597 auto elements = listOp.getElements();
2598 for (
size_t i = 0; i < elements.size(); ++i)
2603 ArrayRef<uint64_t>
matches)
override {
2605 llvm::SmallDenseSet<uint64_t, 4> matchesSet(
matches.begin(),
matches.end());
2608 SmallVector<Value> newElements;
2609 auto elements = listOp.getElements();
2610 for (
size_t i = 0; i < elements.size(); ++i) {
2611 if (!matchesSet.contains(i))
2612 newElements.push_back(elements[i]);
2616 OpBuilder builder(listOp);
2617 auto newListOp = ListCreateOp::create(builder, listOp.getLoc(),
2618 listOp.getType(), newElements);
2619 listOp.getResult().replaceAllUsesWith(newListOp.getResult());
2626 return "firrtl-list-create-element-remover";
2632 uint64_t
match(FModuleOp module)
override {
2633 return module.getConvention() != Convention::Internal;
2636 LogicalResult
rewrite(FModuleOp module)
override {
2637 module.setConvention(Convention::Internal);
2641 std::string
getName()
const override {
return "module-convention-remover"; }
2648 uint64_t
match(FExtModuleOp extmodule)
override {
2649 return extmodule.getConvention() != Convention::Internal;
2652 LogicalResult
rewrite(FExtModuleOp extmodule)
override {
2653 extmodule.setConvention(Convention::Internal);
2658 return "extmodule-convention-remover";
2670 return "firrtl-imdeadcodeelim-remove-ports";
2685 patterns.add<SimplifyResets, 35>();
2687 patterns.add<MustDedupChildren, 33>();
2688 patterns.add<AnnotationRemover, 32>();
2690 patterns.add<LayerDisable, 30>(getContext());
2691 patterns.add<LayerEnableRemover, 30>();
2697 firrtl::createLowerCHIRRTLPass(),
true,
true);
2702 patterns.add<FIRRTLModuleExternalizer, 25>();
2703 patterns.add<InstanceStubber, 24>();
2708 firrtl::createLowerFIRRTLTypes(),
true,
true);
2714 patterns.add<NodeSymbolRemover, 16>();
2716 patterns.add<ConnectForwarder, 14>();
2717 patterns.add<ConnectInvalidator, 13>();
2719 patterns.add<FIRRTLOperandForwarder<0>, 11>();
2720 patterns.add<FIRRTLOperandForwarder<1>, 10>();
2721 patterns.add<FIRRTLOperandForwarder<2>, 9>();
2723 patterns.add<ResetDisconnector, 8>();
2724 patterns.add<DetachSubaccesses, 7>();
2725 patterns.add<ModulePortPruner, 7>();
2726 patterns.add<ExtmodulePortPruner, 6>();
2728 patterns.add<RootExtmodulePortPruner, 5>();
2729 patterns.add<ExtmoduleInstanceRemover, 4>();
2730 patterns.add<ConnectSourceOperandForwarder<0>, 3>();
2731 patterns.add<ConnectSourceOperandForwarder<1>, 2>();
2732 patterns.add<ConnectSourceOperandForwarder<2>, 1>();
2740 mlir::DialectRegistry ®istry) {
2741 registry.addExtension(+[](MLIRContext *ctx, FIRRTLDialect *dialect) {
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static bool onlyInvalidated(Value arg)
Check that all connections to a value are invalids.
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...
false return failure()
Parse an optional keyword or string and set instance into 'result'.
if(parseOptionalKeywordOrOptionalString(parser, result.rawName, found)) return failure()
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
static bool removePortAnnotations(Operation *module, llvm::function_ref< bool(unsigned, Annotation)> predicate)
Remove all port annotations from a module or extmodule for which predicate returns true.
Attribute getAttr() const
Get the underlying 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.
ArrayRef< hw::HierPathOp > lookup(Operation *op)
Lookup all NLAs an operation participates in.
void addNLA(hw::HierPathOp nla)
Insert a new NLA.
void erase(hw::HierPathOp nlaOp, SymbolTable *symbolTable=nullptr)
Remove the NLA from the analysis.
Helper class to cache tie-off values for different FIRRTL types.
Value getUnknown(PropertyType type)
Get or create an UnknownValueOp for the given property type.
The target of an inner symbol, the entity the symbol is a handle for.
This class represents a collection of InnerSymbolTable's.
InnerSymbolTable & getInnerSymbolTable(Operation *op)
Get or create the InnerSymbolTable for the specified operation.
static RetTy walkSymbols(Operation *op, FuncTy &&callback)
Walk the given IST operation and invoke the callback for all encountered inner symbols.
connect(destination, source)
@ None
Don't explicitly preserve any named values.
void registerReducePatternDialectInterface(mlir::DialectRegistry ®istry)
Register the FIRRTL Reduction pattern dialect interface to the given registry.
SmallSet< SymbolRefAttr, 4, LayerSetCompare > LayerSet
BaseTy type_dyn_cast(Type type)
std::optional< TokenAnnoTarget > tokenizePath(StringRef origTarget)
Parse a FIRRTL annotation path into its constituent parts.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
ModulePort::Direction flip(ModulePort::Direction direction)
Flip a port direction.
void pruneUnusedOps(SmallVectorImpl< Operation * > &worklist, Reduction &reduction)
Starting from an initial worklist of operations, traverse through it and its operands and erase opera...
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Reduction that removes the convention attribute from external modules.
bool isOneShot() const override
Return true if the tool should not try to reapply this reduction after it has been successful.
uint64_t match(FExtModuleOp extmodule) override
std::string getName() const override
Return a human-readable name for this reduction pattern.
LogicalResult rewrite(FExtModuleOp extmodule) override
bool acceptSizeIncrease() const override
Return true if the tool should accept the transformation this reduction performs on the module even i...
IMDCEPortReduction(MLIRContext *context)
std::string getName() const override
Return a human-readable name for this reduction pattern.
A reduction pattern that removes elements from FIRRTL list create operations.
LogicalResult rewriteMatches(ListCreateOp listOp, ArrayRef< uint64_t > matches) override
void matches(ListCreateOp listOp, llvm::function_ref< void(uint64_t, uint64_t)> addMatch) override
std::string getName() const override
Return a human-readable name for this reduction pattern.
Reduction that removes the convention attribute from regular modules.
uint64_t match(FModuleOp module) 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...
LogicalResult rewrite(FModuleOp module) override
bool isOneShot() const override
Return true if the tool should not try to reapply this reduction after it has been successful.
Pseudo-reduction that sanitizes the names of operations 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.
Pseudo-reduction that sanitizes module and port names.
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.
bool acceptSizeIncrease() const override
Return true if the tool should accept the transformation this reduction performs on the module even i...
LogicalResult rewrite(mlir::ModuleOp moduleOp) override
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.
A reduction pattern for a specific operation.
A reduction pattern that applies an mlir::Pass.
MLIRContext *const context
PassReduction(MLIRContext *context, std::unique_ptr< Pass > pass, bool canIncreaseSize=false, bool oneShot=false)
An abstract reduction pattern.
virtual void beforeReduction(mlir::ModuleOp)
Called before the reduction is applied to a new subset of operations.
A dialect interface to provide reduction patterns to a reducer tool.
void populateReducePatterns(circt::ReducePatternSet &patterns) const override
A helper struct that scans a root operation and all its nested operations for InnerRefAttrs.
A utility doing lazy construction of SymbolTables and SymbolUserMaps, which is handy for reductions t...
std::unique_ptr< SymbolTableCollection > tables
SymbolUserMap & getSymbolUserMap(Operation *op)
SymbolUserMap & getNearestSymbolUserMap(Operation *op)
SymbolTable & getNearestSymbolTable(Operation *op)
SmallDenseMap< Operation *, SymbolUserMap, 2 > userMaps
SymbolTable & getSymbolTable(Operation *op)