24#include "mlir/IR/Dominance.h"
25#include "mlir/IR/ImplicitLocOpBuilder.h"
26#include "mlir/IR/Threading.h"
27#include "mlir/Pass/Pass.h"
28#include "llvm/ADT/EquivalenceClasses.h"
29#include "llvm/ADT/SetVector.h"
30#include "llvm/ADT/TypeSwitch.h"
31#include "llvm/Support/Debug.h"
33#define DEBUG_TYPE "infer-resets"
37#define GEN_PASS_DEF_INFERRESETS
38#include "circt/Dialect/FIRRTL/Passes.h.inc"
42using circt::igraph::InstanceOpInterface;
45using llvm::BumpPtrAllocator;
47using llvm::SmallDenseSet;
48using llvm::SmallSetVector;
50using mlir::InferTypeOpInterface;
53using namespace firrtl;
62 if (
auto arg = dyn_cast<BlockArgument>(reset)) {
63 auto module = cast<FModuleOp>(arg.getParentRegion()->getParentOp());
64 return {
module.getPortNameAttr(arg.getArgNumber()), module};
66 auto *op = reset.getDefiningOp();
67 return {op->getAttrOfType<StringAttr>(
"name"),
68 op->getParentOfType<FModuleOp>()};
97 std::optional<unsigned> existingPort;
100 ResetDomain() =
default;
103 ResetDomain(Value rootReset)
104 : rootReset(rootReset), resetName(
getResetName(rootReset)),
105 resetType(rootReset.getType()) {}
108 explicit operator bool()
const {
return static_cast<bool>(rootReset); }
112inline bool operator==(
const ResetDomain &a,
const ResetDomain &b) {
113 return (a.isTop == b.isTop && a.resetName == b.resetName &&
114 a.resetType == b.resetType);
116inline bool operator!=(
const ResetDomain &a,
const ResetDomain &b) {
125 auto it = cache.find(type);
126 if (it != cache.end())
128 auto nullBit = [&]() {
130 builder, UIntType::get(builder.getContext(), 1,
true),
135 .
Case<ClockType>([&](
auto type) {
136 return AsClockPrimOp::create(builder, nullBit());
138 .Case<AsyncResetType>([&](
auto type) {
139 return AsAsyncResetPrimOp::create(builder, nullBit());
141 .Case<SIntType, UIntType>([&](
auto type) {
142 return ConstantOp::create(
143 builder, type, APInt::getZero(type.getWidth().value_or(1)));
145 .Case<FEnumType>([&](
auto type) -> Value {
148 if (type.getNumElements() != 0 &&
149 type.getElement(0).value.getValue().isZero()) {
150 const auto &element = type.getElement(0);
152 return FEnumCreateOp::create(builder, type, element.name, value);
154 auto value = ConstantOp::create(builder,
155 UIntType::get(builder.getContext(),
158 APInt::getZero(type.getBitWidth()));
159 return BitCastOp::create(builder, type, value);
161 .Case<BundleType>([&](
auto type) {
162 auto wireOp = WireOp::create(builder, type);
163 for (
unsigned i = 0, e = type.getNumElements(); i < e; ++i) {
164 auto fieldType = type.getElementTypePreservingConst(i);
167 SubfieldOp::create(builder, fieldType, wireOp.getResult(), i);
170 return wireOp.getResult();
172 .Case<FVectorType>([&](
auto type) {
173 auto wireOp = WireOp::create(builder, type);
175 builder, type.getElementTypePreservingConst(), cache);
176 for (
unsigned i = 0, e = type.getNumElements(); i < e; ++i) {
177 auto acc = SubindexOp::create(builder, zero.getType(),
178 wireOp.getResult(), i);
181 return wireOp.getResult();
183 .Case<ResetType, AnalogType>(
184 [&](
auto type) {
return InvalidValueOp::create(builder, type); })
186 llvm_unreachable(
"switch handles all types");
189 cache.insert({type, value});
206 Value reset, Value resetValue) {
210 bool resetValueUsed =
false;
212 for (
auto &use : target.getUses()) {
213 Operation *useOp = use.getOwner();
214 builder.setInsertionPoint(useOp);
215 TypeSwitch<Operation *>(useOp)
218 .Case<ConnectOp, MatchingConnectOp>([&](
auto op) {
219 if (op.getDest() != target)
221 LLVM_DEBUG(llvm::dbgs() <<
" - Insert mux into " << op <<
"\n");
223 MuxPrimOp::create(builder, reset, resetValue, op.getSrc());
224 op.getSrcMutable().assign(muxOp);
225 resetValueUsed =
true;
228 .Case<SubfieldOp>([&](
auto op) {
230 SubfieldOp::create(builder, resetValue, op.getFieldIndexAttr());
232 resetValueUsed =
true;
234 resetSubValue.erase();
237 .Case<SubindexOp>([&](
auto op) {
239 SubindexOp::create(builder, resetValue, op.getIndexAttr());
241 resetValueUsed =
true;
243 resetSubValue.erase();
246 .Case<SubaccessOp>([&](
auto op) {
247 if (op.getInput() != target)
250 SubaccessOp::create(builder, resetValue, op.getIndex());
252 resetValueUsed =
true;
254 resetSubValue.erase();
257 return resetValueUsed;
272 bool operator<(
const ResetSignal &other)
const {
return field < other.field; }
273 bool operator==(
const ResetSignal &other)
const {
274 return field == other.field;
276 bool operator!=(
const ResetSignal &other)
const {
return !(*
this == other); }
296using ResetDrives = SmallVector<ResetDrive, 1>;
299using ResetNetwork = llvm::iterator_range<
300 llvm::EquivalenceClasses<ResetSignal>::member_iterator>;
303enum class ResetKind { Async, Sync };
305static StringRef resetKindToStringRef(
const ResetKind &kind) {
307 case ResetKind::Async:
309 case ResetKind::Sync:
312 llvm_unreachable(
"unhandled reset kind");
328 static bool isEqual(
const ResetSignal &lhs,
const ResetSignal &rhs) {
337 case ResetKind::Async:
338 return os <<
"async";
339 case ResetKind::Sync:
449struct InferResetsPass
450 :
public circt::firrtl::impl::InferResetsBase<InferResetsPass> {
451 void runOnOperation()
override;
452 void runOnOperationInner();
455 using InferResetsBase::InferResetsBase;
456 InferResetsPass(
const InferResetsPass &other) : InferResetsBase(other) {}
461 void traceResets(CircuitOp circuit);
462 void traceResets(FInstanceLike inst);
463 void traceResets(Value dst, Value src, Location loc);
464 void traceResets(Value value);
465 void traceResets(Type dstType, Value dst,
unsigned dstID, Type srcType,
466 Value src,
unsigned srcID, Location loc);
468 LogicalResult inferAndUpdateResets();
469 FailureOr<ResetKind> inferReset(ResetNetwork net);
470 LogicalResult updateReset(ResetNetwork net, ResetKind kind);
476 LogicalResult collectAnnos(CircuitOp circuit);
482 FailureOr<std::optional<Value>> collectAnnos(FModuleOp module);
484 LogicalResult buildDomains(CircuitOp circuit);
485 void buildDomains(FModuleOp module,
const InstancePath &instPath,
487 unsigned indent = 0);
489 LogicalResult determineImpl();
490 LogicalResult determineImpl(FModuleOp module, ResetDomain &domain);
492 LogicalResult implementFullReset();
493 LogicalResult implementFullReset(FModuleOp module, ResetDomain &domain);
494 void implementFullReset(Operation *op, FModuleOp module, Value actualReset);
497 void implementFullReset(FInstanceLike inst, StringAttr moduleName,
500 LogicalResult verifyNoAbstractReset();
506 ResetNetwork getResetNetwork(ResetSignal signal) {
507 return llvm::make_range(resetClasses.findLeader(signal),
508 resetClasses.member_end());
512 ResetDrives &getResetDrives(ResetNetwork net) {
513 return resetDrives[*net.begin()];
518 ResetSignal guessRoot(ResetNetwork net);
519 ResetSignal guessRoot(ResetSignal signal) {
520 return guessRoot(getResetNetwork(signal));
527 llvm::EquivalenceClasses<ResetSignal> resetClasses;
530 DenseMap<ResetSignal, ResetDrives> resetDrives;
535 DenseMap<Operation *, Value> annotatedResets;
539 MapVector<FModuleOp, SmallVector<std::pair<ResetDomain, InstancePath>, 1>>
546 std::unique_ptr<InstancePathCache> instancePathCache;
550void InferResetsPass::runOnOperation() {
551 runOnOperationInner();
552 resetClasses = llvm::EquivalenceClasses<ResetSignal>();
554 annotatedResets.clear();
556 instancePathCache.reset(
nullptr);
557 markAnalysesPreserved<InstanceGraph>();
560void InferResetsPass::runOnOperationInner() {
561 instanceGraph = &getAnalysis<InstanceGraph>();
562 instancePathCache = std::make_unique<InstancePathCache>(*instanceGraph);
565 traceResets(getOperation());
568 if (failed(inferAndUpdateResets()))
569 return signalPassFailure();
572 if (failed(collectAnnos(getOperation())))
573 return signalPassFailure();
576 if (failed(buildDomains(getOperation())))
577 return signalPassFailure();
580 if (failed(determineImpl()))
581 return signalPassFailure();
584 if (failed(implementFullReset()))
585 return signalPassFailure();
588 if (failed(verifyNoAbstractReset()))
589 return signalPassFailure();
592ResetSignal InferResetsPass::guessRoot(ResetNetwork net) {
593 ResetDrives &drives = getResetDrives(net);
594 ResetSignal bestSignal = *net.begin();
595 unsigned bestNumDrives = -1;
597 for (
auto signal : net) {
599 if (isa_and_nonnull<InvalidValueOp>(
600 signal.field.getValue().getDefiningOp()))
605 unsigned numDrives = 0;
606 for (
auto &drive : drives)
607 if (drive.dst == signal)
613 if (numDrives < bestNumDrives) {
614 bestNumDrives = numDrives;
633 .
Case<BundleType>([](
auto type) {
635 for (
auto e : type.getElements())
640 [](
auto type) {
return getMaxFieldID(type.getElementType()) + 1; })
641 .Default([](
auto) {
return 0; });
645 assert(index < type.getNumElements());
647 for (
unsigned i = 0; i < index; ++i)
655 assert(type.getNumElements() &&
"Bundle must have >0 fields");
657 for (
const auto &e : llvm::enumerate(type.getElements())) {
659 if (fieldID < numSubfields)
661 fieldID -= numSubfields;
663 assert(
false &&
"field id outside bundle");
669 if (oldType.isGround()) {
675 if (
auto bundleType = type_dyn_cast<BundleType>(oldType)) {
683 if (
auto vectorType = type_dyn_cast<FVectorType>(oldType)) {
684 if (vectorType.getNumElements() == 0)
701 if (
auto arg = dyn_cast<BlockArgument>(value)) {
702 auto module = cast<FModuleOp>(arg.getOwner()->getParentOp());
703 string +=
module.getPortName(arg.getArgNumber());
707 auto *op = value.getDefiningOp();
708 return TypeSwitch<Operation *, bool>(op)
709 .Case<InstanceOp, InstanceChoiceOp, MemOp>([&](
auto op) {
710 string += op.getName();
712 string += op.getPortName(cast<OpResult>(value).getResultNumber());
715 .Case<WireOp, NodeOp, RegOp, RegResetOp>([&](
auto op) {
716 string += op.getName();
719 .Default([](
auto) {
return false; });
723 SmallString<64> name;
728 auto type = value.getType();
731 if (
auto bundleType = type_dyn_cast<BundleType>(type)) {
734 auto &element = bundleType.getElements()[index];
737 string += element.name.getValue();
740 localID = localID -
getFieldID(bundleType, index);
741 }
else if (
auto vecType = type_dyn_cast<FVectorType>(type)) {
744 type = vecType.getElementType();
751 llvm_unreachable(
"unsupported type");
763 return TypeSwitch<Type, bool>(type)
765 return type.getRecursiveTypeProperties().hasUninferredReset;
767 .Default([](
auto) {
return false; });
774void InferResetsPass::traceResets(CircuitOp circuit) {
776 llvm::dbgs() <<
"\n";
777 debugHeader(
"Tracing uninferred resets") <<
"\n\n";
780 SmallVector<std::pair<FModuleOp, SmallVector<Operation *>>> moduleToOps;
782 for (
auto module : circuit.getOps<FModuleOp>())
783 moduleToOps.push_back({module, {}});
786 getAnalysis<hw::InnerSymbolTableCollection>()};
788 mlir::parallelForEach(circuit.getContext(), moduleToOps, [](
auto &e) {
789 e.first.walk([&](Operation *op) {
793 op->getResultTypes(),
794 [](mlir::Type type) { return typeContainsReset(type); }) ||
795 llvm::any_of(op->getOperandTypes(), typeContainsReset))
796 e.second.push_back(op);
800 for (
auto &[_, ops] : moduleToOps)
801 for (auto *op : ops) {
802 TypeSwitch<Operation *>(op)
803 .Case<FConnectLike>([&](
auto op) {
804 traceResets(op.getDest(), op.getSrc(), op.getLoc());
806 .Case<FInstanceLike>([&](
auto op) { traceResets(op); })
807 .Case<RefSendOp>([&](
auto op) {
809 traceResets(op.getType().getType(), op.getResult(), 0,
810 op.getBase().getType().getPassiveType(), op.getBase(),
813 .Case<RefResolveOp>([&](
auto op) {
815 traceResets(op.getType(), op.getResult(), 0,
816 op.getRef().getType().getType(), op.getRef(), 0,
819 .Case<Forceable>([&](Forceable op) {
820 if (
auto node = dyn_cast<NodeOp>(op.getOperation()))
821 traceResets(node.getResult(), node.getInput(), node.getLoc());
823 if (op.isForceable())
824 traceResets(op.getDataType(), op.getData(), 0, op.getDataType(),
825 op.getDataRef(), 0, op.getLoc());
827 .Case<RWProbeOp>([&](RWProbeOp op) {
828 auto ist = irn.lookup(op.getTarget());
831 auto baseType = op.getType().getType();
832 traceResets(baseType, op.getResult(), 0, baseType.getPassiveType(),
833 ref.getValue(), ref.getFieldID(), op.getLoc());
835 .Case<UninferredResetCastOp, ConstCastOp, RefCastOp>([&](
auto op) {
836 traceResets(op.getResult(), op.getInput(), op.getLoc());
838 .Case<InvalidValueOp>([&](
auto op) {
847 auto type = op.getType();
850 LLVM_DEBUG(llvm::dbgs() <<
"Uniquify " << op <<
"\n");
851 ImplicitLocOpBuilder builder(op->getLoc(), op);
853 llvm::make_early_inc_range(
llvm::drop_begin(op->getUses()))) {
859 auto newOp = InvalidValueOp::create(builder, type);
864 .Case<SubfieldOp>([&](
auto op) {
867 BundleType bundleType = op.getInput().getType();
868 auto index = op.getFieldIndex();
869 traceResets(op.getType(), op.getResult(), 0,
870 bundleType.getElements()[index].type, op.getInput(),
874 .Case<SubindexOp, SubaccessOp>([&](
auto op) {
887 FVectorType vectorType = op.getInput().getType();
888 traceResets(op.getType(), op.getResult(), 0,
889 vectorType.getElementType(), op.getInput(),
893 .Case<RefSubOp>([&](RefSubOp op) {
895 auto aggType = op.getInput().getType().getType();
896 uint64_t fieldID = TypeSwitch<FIRRTLBaseType, uint64_t>(aggType)
897 .Case<FVectorType>([](
auto type) {
900 .Case<BundleType>([&](
auto type) {
903 traceResets(op.getType(), op.getResult(), 0,
904 op.getResult().getType(), op.getInput(), fieldID,
912void InferResetsPass::traceResets(FInstanceLike inst) {
913 LLVM_DEBUG(llvm::dbgs() <<
"Visiting instance " << inst.getInstanceName()
915 auto moduleNames = inst.getReferencedModuleNamesAttr();
916 for (
auto moduleName : moduleNames.getAsRange<StringAttr>()) {
917 auto *node = instanceGraph->lookup(moduleName);
918 auto module = dyn_cast<FModuleOp>(*node->getModule());
923 for (
const auto &it :
llvm::enumerate(inst->getResults())) {
924 Value dstPort =
module.getArgument(it.index());
925 Value srcPort = it.value();
926 if (module.getPortDirection(it.index()) == Direction::Out)
927 std::swap(dstPort, srcPort);
928 traceResets(dstPort, srcPort, it.value().getLoc());
935void InferResetsPass::traceResets(Value dst, Value src, Location loc) {
937 traceResets(dst.getType(), dst, 0, src.getType(), src, 0, loc);
942void InferResetsPass::traceResets(Type dstType, Value dst,
unsigned dstID,
943 Type srcType, Value src,
unsigned srcID,
945 if (
auto dstBundle = type_dyn_cast<BundleType>(dstType)) {
946 auto srcBundle = type_cast<BundleType>(srcType);
947 for (
unsigned dstIdx = 0, e = dstBundle.getNumElements(); dstIdx < e;
949 auto dstField = dstBundle.getElements()[dstIdx].name;
950 auto srcIdx = srcBundle.getElementIndex(dstField);
953 auto &dstElt = dstBundle.getElements()[dstIdx];
954 auto &srcElt = srcBundle.getElements()[*srcIdx];
956 traceResets(srcElt.type, src, srcID +
getFieldID(srcBundle, *srcIdx),
957 dstElt.type, dst, dstID +
getFieldID(dstBundle, dstIdx),
960 traceResets(dstElt.type, dst, dstID +
getFieldID(dstBundle, dstIdx),
961 srcElt.type, src, srcID +
getFieldID(srcBundle, *srcIdx),
968 if (
auto dstVector = type_dyn_cast<FVectorType>(dstType)) {
969 auto srcVector = type_cast<FVectorType>(srcType);
970 auto srcElType = srcVector.getElementType();
971 auto dstElType = dstVector.getElementType();
984 traceResets(dstElType, dst, dstID +
getFieldID(dstVector), srcElType, src,
990 if (
auto dstRef = type_dyn_cast<RefType>(dstType)) {
991 auto srcRef = type_cast<RefType>(srcType);
992 return traceResets(dstRef.getType(), dst, dstID, srcRef.getType(), src,
997 auto dstBase = type_dyn_cast<FIRRTLBaseType>(dstType);
998 auto srcBase = type_dyn_cast<FIRRTLBaseType>(srcType);
999 if (!dstBase || !srcBase)
1001 if (!type_isa<ResetType>(dstBase) && !type_isa<ResetType>(srcBase))
1006 LLVM_DEBUG(llvm::dbgs() <<
"Visiting driver '" << dstField <<
"' = '"
1007 << srcField <<
"' (" << dstType <<
" = " << srcType
1013 ResetSignal dstLeader =
1014 *resetClasses.findLeader(resetClasses.insert({dstField, dstBase}));
1015 ResetSignal srcLeader =
1016 *resetClasses.findLeader(resetClasses.insert({srcField, srcBase}));
1019 ResetSignal unionLeader = *resetClasses.unionSets(dstLeader, srcLeader);
1020 assert(unionLeader == dstLeader || unionLeader == srcLeader);
1025 if (dstLeader != srcLeader) {
1026 auto &unionDrives = resetDrives[unionLeader];
1027 auto mergedDrivesIt =
1028 resetDrives.find(unionLeader == dstLeader ? srcLeader : dstLeader);
1029 if (mergedDrivesIt != resetDrives.end()) {
1030 unionDrives.append(mergedDrivesIt->second);
1031 resetDrives.erase(mergedDrivesIt);
1037 resetDrives[unionLeader].push_back(
1038 {{dstField, dstBase}, {srcField, srcBase}, loc});
1045LogicalResult InferResetsPass::inferAndUpdateResets() {
1047 llvm::dbgs() <<
"\n";
1050 for (
const auto &it : resetClasses) {
1051 if (!it->isLeader())
1053 ResetNetwork net = resetClasses.members(*it);
1056 auto kind = inferReset(net);
1061 if (failed(updateReset(net, *kind)))
1067FailureOr<ResetKind> InferResetsPass::inferReset(ResetNetwork net) {
1068 LLVM_DEBUG(llvm::dbgs() <<
"Inferring reset network with "
1069 << std::distance(net.begin(), net.end())
1073 unsigned asyncDrives = 0;
1074 unsigned syncDrives = 0;
1075 unsigned invalidDrives = 0;
1076 for (ResetSignal signal : net) {
1078 if (type_isa<AsyncResetType>(signal.type))
1080 else if (type_isa<UIntType>(signal.type))
1083 isa_and_nonnull<InvalidValueOp>(
1084 signal.field.getValue().getDefiningOp()))
1087 LLVM_DEBUG(llvm::dbgs() <<
"- Found " << asyncDrives <<
" async, "
1088 << syncDrives <<
" sync, " << invalidDrives
1089 <<
" invalid drives\n");
1092 if (asyncDrives == 0 && syncDrives == 0 && invalidDrives == 0) {
1093 ResetSignal root = guessRoot(net);
1094 auto diag = mlir::emitError(root.field.getValue().getLoc())
1095 <<
"reset network never driven with concrete type";
1096 for (ResetSignal signal : net)
1097 diag.attachNote(signal.field.
getLoc()) <<
"here: ";
1102 if (asyncDrives > 0 && syncDrives > 0) {
1103 ResetSignal root = guessRoot(net);
1104 bool majorityAsync = asyncDrives >= syncDrives;
1105 auto diag = mlir::emitError(root.field.getValue().getLoc())
1107 SmallString<32> fieldName;
1109 diag <<
" \"" << fieldName <<
"\"";
1110 diag <<
" simultaneously connected to async and sync resets";
1111 diag.attachNote(root.field.getValue().getLoc())
1112 <<
"majority of connections to this reset are "
1113 << (majorityAsync ?
"async" :
"sync");
1114 for (
auto &drive : getResetDrives(net)) {
1115 if ((type_isa<AsyncResetType>(drive.dst.type) && !majorityAsync) ||
1116 (type_isa<AsyncResetType>(drive.src.type) && !majorityAsync) ||
1117 (type_isa<UIntType>(drive.dst.type) && majorityAsync) ||
1118 (type_isa<UIntType>(drive.src.type) && majorityAsync))
1119 diag.attachNote(drive.loc)
1120 << (type_isa<AsyncResetType>(drive.src.type) ?
"async" :
"sync")
1129 auto kind = (asyncDrives ? ResetKind::Async : ResetKind::Sync);
1130 LLVM_DEBUG(llvm::dbgs() <<
"- Inferred as " << kind <<
"\n");
1138LogicalResult InferResetsPass::updateReset(ResetNetwork net, ResetKind kind) {
1139 LLVM_DEBUG(llvm::dbgs() <<
"Updating reset network with "
1140 << std::distance(net.begin(), net.end())
1141 <<
" nodes to " << kind <<
"\n");
1145 if (kind == ResetKind::Async)
1146 resetType = AsyncResetType::get(&getContext());
1148 resetType = UIntType::get(&getContext(), 1);
1153 SmallSetVector<Operation *, 16> worklist;
1154 SmallDenseSet<Operation *> moduleWorklist;
1155 SmallDenseSet<std::pair<Operation *, Operation *>> extmoduleWorklist;
1156 for (
auto signal : net) {
1157 Value value = signal.field.getValue();
1158 if (!isa<BlockArgument>(value) &&
1159 !isa_and_nonnull<WireOp, RegOp, RegResetOp, FInstanceLike,
1160 InvalidValueOp, ConstCastOp, RefCastOp,
1161 UninferredResetCastOp, RWProbeOp, AsResetPrimOp>(
1162 value.getDefiningOp()))
1164 if (updateReset(signal.field, resetType)) {
1165 for (
auto *user : value.getUsers())
1166 worklist.insert(user);
1167 if (
auto blockArg = dyn_cast<BlockArgument>(value)) {
1168 moduleWorklist.insert(blockArg.getOwner()->getParentOp());
1172 TypeSwitch<Operation *>(value.getDefiningOp())
1173 .Case<FInstanceLike>([&](FInstanceLike op) {
1174 for (
auto moduleName : op.getReferencedModuleNamesAttr()) {
1175 auto *node = instanceGraph->lookup(cast<StringAttr>(moduleName));
1176 if (
auto refModule = dyn_cast<FExtModuleOp>(*node->getModule()))
1177 extmoduleWorklist.insert({refModule, op.getOperation()});
1180 .Case<UninferredResetCastOp>([&](
auto op) {
1181 op.replaceAllUsesWith(op.getInput());
1184 .Case<AsResetPrimOp>([&](
auto op) {
1187 Value result = op.getInput();
1188 if (type_isa<AsyncResetType>(resetType)) {
1189 ImplicitLocOpBuilder builder(op.getLoc(), op);
1190 result = AsAsyncResetPrimOp::create(builder, op.getInput());
1192 op.replaceAllUsesWith(result);
1202 while (!worklist.empty()) {
1203 auto *wop = worklist.pop_back_val();
1204 SmallVector<Type, 2> types;
1205 if (
auto op = dyn_cast<InferTypeOpInterface>(wop)) {
1207 SmallVector<Type, 2> types;
1208 if (failed(op.inferReturnTypes(op->getContext(), op->getLoc(),
1209 op->getOperands(), op->getAttrDictionary(),
1210 op->getPropertiesStorage(),
1211 op->getRegions(), types)))
1216 for (
auto it :
llvm::zip(op->getResults(), types)) {
1217 auto newType = std::get<1>(it);
1218 if (std::get<0>(it).getType() == newType)
1220 std::get<0>(it).setType(newType);
1221 for (
auto *user : std::
get<0>(it).getUsers())
1222 worklist.insert(user);
1224 LLVM_DEBUG(llvm::dbgs() <<
"- Inferred " << *op <<
"\n");
1225 }
else if (
auto uop = dyn_cast<UninferredResetCastOp>(wop)) {
1226 for (
auto *user : uop.getResult().getUsers())
1227 worklist.insert(user);
1228 uop.replaceAllUsesWith(uop.getInput());
1229 LLVM_DEBUG(llvm::dbgs() <<
"- Inferred " << uop <<
"\n");
1235 for (
auto *op : moduleWorklist) {
1236 auto module = dyn_cast<FModuleOp>(op);
1240 SmallVector<Attribute> argTypes;
1241 argTypes.reserve(module.getNumPorts());
1242 for (
auto arg : module.getArguments())
1243 argTypes.push_back(TypeAttr::
get(arg.getType()));
1245 module.setPortTypesAttr(ArrayAttr::get(op->getContext(), argTypes));
1246 LLVM_DEBUG(llvm::dbgs()
1247 <<
"- Updated type of module '" << module.getName() <<
"'\n");
1251 for (
auto [mod, instOp] : extmoduleWorklist) {
1252 auto module = cast<FExtModuleOp>(mod);
1254 SmallVector<Attribute> types;
1255 for (
auto type : instOp->getResultTypes())
1256 types.push_back(TypeAttr::
get(type));
1258 module.setPortTypesAttr(ArrayAttr::get(module->getContext(), types));
1259 LLVM_DEBUG(llvm::dbgs()
1260 <<
"- Updated type of extmodule '" << module.getName() <<
"'\n");
1270 if (oldType.isGround()) {
1276 if (
auto bundleType = type_dyn_cast<BundleType>(oldType)) {
1278 SmallVector<BundleType::BundleElement> fields(bundleType.begin(),
1281 fields[index].type, fieldID -
getFieldID(bundleType, index), fieldType);
1282 return BundleType::get(oldType.getContext(), fields, bundleType.
isConst());
1286 if (
auto vectorType = type_dyn_cast<FVectorType>(oldType)) {
1287 auto newType =
updateType(vectorType.getElementType(),
1288 fieldID -
getFieldID(vectorType), fieldType);
1289 return FVectorType::get(newType, vectorType.getNumElements(),
1290 vectorType.isConst());
1293 llvm_unreachable(
"unknown aggregate type");
1300 auto oldType = type_cast<FIRRTLType>(field.
getValue().getType());
1306 if (oldType == newType)
1308 LLVM_DEBUG(llvm::dbgs() <<
"- Updating '" << field <<
"' from " << oldType
1309 <<
" to " << newType <<
"\n");
1318LogicalResult InferResetsPass::collectAnnos(CircuitOp circuit) {
1320 llvm::dbgs() <<
"\n";
1321 debugHeader(
"Gather reset annotations") <<
"\n\n";
1323 SmallVector<std::pair<FModuleOp, std::optional<Value>>> results;
1324 for (
auto module : circuit.getOps<FModuleOp>())
1325 results.push_back({module, {}});
1327 if (failed(mlir::failableParallelForEach(
1328 circuit.getContext(), results, [&](
auto &moduleAndResult) {
1329 auto result = collectAnnos(moduleAndResult.first);
1332 moduleAndResult.second = *result;
1337 for (
auto [module, reset] : results)
1338 if (reset.has_value())
1339 annotatedResets.insert({module, *reset});
1343FailureOr<std::optional<Value>>
1344InferResetsPass::collectAnnos(FModuleOp module) {
1345 bool anyFailed =
false;
1346 SmallSetVector<std::pair<Annotation, Location>, 4> conflictingAnnos;
1350 bool ignore =
false;
1352 if (anno.
isClass(excludeFromFullResetAnnoClass)) {
1354 conflictingAnnos.insert({anno, module.getLoc()});
1357 if (anno.
isClass(fullResetAnnoClass)) {
1359 module.emitError("''FullResetAnnotation' cannot target module; must
"
1360 "target port or wire/node instead
");
1368 // Consume any reset annotations on module ports.
1370 // Helper for checking annotations and determining the reset
1371 auto checkAnnotations = [&](Annotation anno, Value arg) {
1372 if (anno.isClass(fullResetAnnoClass)) {
1373 ResetKind expectedResetKind;
1374 if (auto rt = anno.getMember<StringAttr>("resetType
")) {
1376 expectedResetKind = ResetKind::Sync;
1377 } else if (rt == "async
") {
1378 expectedResetKind = ResetKind::Async;
1380 mlir::emitError(arg.getLoc(),
1381 "'FullResetAnnotation' requires resetType ==
'sync' "
1382 "|
'async', but got resetType ==
")
1388 mlir::emitError(arg.getLoc(),
1389 "'FullResetAnnotation' requires resetType ==
"
1390 "'sync' |
'async', but got no resetType
");
1394 // Check that the type is well-formed
1395 bool isAsync = expectedResetKind == ResetKind::Async;
1396 bool validUint = false;
1397 if (auto uintT = dyn_cast<UIntType>(arg.getType()))
1398 validUint = uintT.getWidth() == 1;
1399 if ((isAsync && !isa<AsyncResetType>(arg.getType())) ||
1400 (!isAsync && !validUint)) {
1401 auto kind = resetKindToStringRef(expectedResetKind);
1402 mlir::emitError(arg.getLoc(),
1403 "'FullResetAnnotation' with resetType ==
'")
1404 << kind << "' must target
" << kind << " reset, but targets
"
1411 conflictingAnnos.insert({anno, reset.getLoc()});
1415 if (anno.isClass(excludeFromFullResetAnnoClass)) {
1417 mlir::emitError(arg.getLoc(),
1418 "'ExcludeFromFullResetAnnotation' cannot
"
1419 "target port/wire/node; must target
module instead");
1427 Value arg =
module.getArgument(argNum);
1428 return checkAnnotations(anno, arg);
1434 module.getBody().walk([&](Operation *op) {
1436 if (!isa<WireOp, NodeOp>(op)) {
1437 if (AnnotationSet::hasAnnotation(op, fullResetAnnoClass,
1438 excludeFromFullResetAnnoClass)) {
1441 "reset annotations must target module, port, or wire/node");
1449 auto arg = op->getResult(0);
1450 return checkAnnotations(anno, arg);
1459 if (!ignore && !reset) {
1460 LLVM_DEBUG(llvm::dbgs()
1461 <<
"No reset annotation for " << module.getName() <<
"\n");
1462 return std::optional<Value>();
1466 if (conflictingAnnos.size() > 1) {
1467 auto diag =
module.emitError("multiple reset annotations on module '")
1468 << module.getName() << "'";
1469 for (
auto &annoAndLoc : conflictingAnnos)
1470 diag.attachNote(annoAndLoc.second)
1471 <<
"conflicting " << annoAndLoc.first.getClassAttr() <<
":";
1477 llvm::dbgs() <<
"Annotated reset for " <<
module.getName() << ": ";
1479 llvm::dbgs() <<
"no domain\n";
1480 else if (
auto arg = dyn_cast<BlockArgument>(reset))
1481 llvm::dbgs() <<
"port " <<
module.getPortName(arg.getArgNumber()) << "\n";
1483 llvm::dbgs() <<
"wire "
1484 << reset.getDefiningOp()->getAttrOfType<StringAttr>(
"name")
1490 return std::optional<Value>(reset);
1502LogicalResult InferResetsPass::buildDomains(CircuitOp circuit) {
1504 llvm::dbgs() <<
"\n";
1505 debugHeader(
"Build full reset domains") <<
"\n\n";
1509 auto &instGraph = getAnalysis<InstanceGraph>();
1515 dyn_cast_or_null<FModuleOp>(node.
getModule().getOperation()))
1516 buildDomains(module,
InstancePath{}, Value{}, instGraph);
1520 bool anyFailed =
false;
1521 for (
auto &it : domains) {
1522 auto module = cast<FModuleOp>(it.first);
1523 auto &domainConflicts = it.second;
1524 if (domainConflicts.size() <= 1)
1528 SmallDenseSet<Value> printedDomainResets;
1529 auto diag =
module.emitError("module '")
1531 << "' instantiated in different reset domains";
1532 for (
auto &it : domainConflicts) {
1533 ResetDomain &domain = it.first;
1534 const auto &path = it.second;
1535 auto inst = path.leaf();
1536 auto loc = path.empty() ?
module.getLoc() : inst.getLoc();
1537 auto ¬e = diag.attachNote(loc);
1541 note <<
"root instance";
1543 note <<
"instance '";
1546 [&](InstanceOpInterface inst) { note << inst.getInstanceName(); },
1547 [&]() { note <<
"/"; });
1553 if (domain.rootReset) {
1555 note <<
" reset domain rooted at '" << nameAndModule.first.getValue()
1556 <<
"' of module '" << nameAndModule.second.getName() <<
"'";
1559 if (printedDomainResets.insert(domain.rootReset).second) {
1560 diag.attachNote(domain.rootReset.getLoc())
1561 <<
"reset domain '" << nameAndModule.first.getValue()
1562 <<
"' of module '" << nameAndModule.second.getName()
1563 <<
"' declared here:";
1566 note <<
" no reset domain";
1569 return failure(anyFailed);
1572void InferResetsPass::buildDomains(FModuleOp module,
1577 llvm::dbgs().indent(indent * 2) <<
"Visiting ";
1578 if (instPath.
empty())
1579 llvm::dbgs() <<
"$root";
1581 llvm::dbgs() << instPath.
leaf().getInstanceName();
1582 llvm::dbgs() <<
" (" <<
module.getName() << ")\n";
1587 auto it = annotatedResets.find(module);
1588 if (it != annotatedResets.end()) {
1591 if (
auto localReset = it->second)
1592 domain = ResetDomain(localReset);
1593 domain.isTop =
true;
1594 }
else if (parentReset) {
1596 domain = ResetDomain(parentReset);
1603 auto &entries = domains[module];
1604 if (domain.rootReset)
1605 if (llvm::all_of(entries,
1606 [&](
const auto &entry) {
return entry.first != domain; }))
1607 entries.push_back({domain, instPath});
1610 for (
auto *record : *instGraph[module]) {
1611 auto submodule = dyn_cast<FModuleOp>(*record->getTarget()->getModule());
1615 instancePathCache->appendInstance(instPath, record->getInstance());
1616 buildDomains(submodule, childPath, domain.rootReset, instGraph, indent + 1);
1621LogicalResult InferResetsPass::determineImpl() {
1622 auto anyFailed =
false;
1624 llvm::dbgs() <<
"\n";
1625 debugHeader(
"Determine implementation") <<
"\n\n";
1627 for (
auto &it : domains) {
1628 auto module = cast<FModuleOp>(it.first);
1629 auto &entries = it.second;
1631 if (entries.empty())
1633 auto &domain = entries.back().first;
1634 if (failed(determineImpl(module, domain)))
1637 return failure(anyFailed);
1655LogicalResult InferResetsPass::determineImpl(FModuleOp module,
1656 ResetDomain &domain) {
1660 LLVM_DEBUG(llvm::dbgs() <<
"Planning reset for " << module.getName() <<
"\n");
1665 LLVM_DEBUG(llvm::dbgs()
1666 <<
"- Rooting at local value " << domain.resetName <<
"\n");
1667 domain.localReset = domain.rootReset;
1668 if (
auto blockArg = dyn_cast<BlockArgument>(domain.rootReset))
1669 domain.existingPort = blockArg.getArgNumber();
1675 auto neededName = domain.resetName;
1676 auto neededType = domain.resetType;
1677 LLVM_DEBUG(llvm::dbgs() <<
"- Looking for existing port " << neededName
1679 auto portNames =
module.getPortNames();
1680 auto *portIt = llvm::find(portNames, neededName);
1683 if (portIt == portNames.end()) {
1684 LLVM_DEBUG(llvm::dbgs() <<
"- Creating new port " << neededName <<
"\n");
1685 domain.resetName = neededName;
1689 LLVM_DEBUG(llvm::dbgs() <<
"- Reusing existing port " << neededName <<
"\n");
1692 auto portNo = std::distance(portNames.begin(), portIt);
1693 auto portType =
module.getPortType(portNo);
1694 if (portType != neededType) {
1695 auto diag = emitError(module.getPortLocation(portNo),
"module '")
1696 <<
module.getName() << "' is in reset domain requiring port '"
1697 << domain.resetName.getValue() << "' to have type "
1698 << domain.resetType << ", but has type " << portType;
1699 diag.attachNote(domain.rootReset.getLoc()) <<
"reset domain rooted here";
1704 domain.existingPort = portNo;
1705 domain.localReset =
module.getArgument(portNo);
1714LogicalResult InferResetsPass::implementFullReset() {
1716 llvm::dbgs() <<
"\n";
1719 for (
auto &it : domains) {
1720 auto module = cast<FModuleOp>(it.first);
1721 auto &entries = it.second;
1725 if (!entries.empty())
1726 domain = entries.back().first;
1727 if (failed(implementFullReset(module, domain)))
1738LogicalResult InferResetsPass::implementFullReset(FModuleOp module,
1739 ResetDomain &domain) {
1744 SmallVector<FInstanceLike> instances;
1745 module.walk([&](FInstanceLike instOp) { instances.push_back(instOp); });
1747 if (!instances.empty())
1748 llvm::dbgs() <<
"Tie off instances in " << module.getName() <<
"\n";
1750 for (
auto instOp : instances)
1751 implementFullReset(instOp, module, Value());
1755 LLVM_DEBUG(llvm::dbgs() <<
"Implementing full reset for " << module.getName()
1759 auto *
context =
module.getContext();
1761 annotations.addAnnotations(DictionaryAttr::get(
1763 StringAttr::get(
context, fullResetAnnoClass))));
1764 annotations.applyToOperation(module);
1767 auto actualReset = domain.localReset;
1768 if (!domain.localReset) {
1769 PortInfo portInfo{domain.resetName,
1773 domain.rootReset.getLoc()};
1774 module.insertPorts({{0, portInfo}});
1775 actualReset =
module.getArgument(0);
1776 LLVM_DEBUG(llvm::dbgs() <<
"- Inserted port " << domain.resetName <<
"\n");
1780 llvm::dbgs() <<
"- Using ";
1781 if (
auto blockArg = dyn_cast<BlockArgument>(actualReset))
1782 llvm::dbgs() <<
"port #" << blockArg.getArgNumber() <<
" ";
1784 llvm::dbgs() <<
"wire/node ";
1790 SmallVector<Operation *> opsToUpdate;
1791 module.walk([&](Operation *op) {
1792 if (isa<FInstanceLike, RegOp, RegResetOp>(op))
1793 opsToUpdate.push_back(op);
1800 if (!isa<BlockArgument>(actualReset)) {
1801 mlir::DominanceInfo dom(module);
1806 auto *resetOp = actualReset.getDefiningOp();
1807 if (!opsToUpdate.empty() && !dom.dominates(resetOp, opsToUpdate[0])) {
1808 LLVM_DEBUG(llvm::dbgs()
1809 <<
"- Reset doesn't dominate all uses, needs to be moved\n");
1813 auto nodeOp = dyn_cast<NodeOp>(resetOp);
1814 if (nodeOp && !dom.dominates(nodeOp.getInput(), opsToUpdate[0])) {
1815 LLVM_DEBUG(llvm::dbgs()
1816 <<
"- Promoting node to wire for move: " << nodeOp <<
"\n");
1817 auto builder = ImplicitLocOpBuilder::atBlockBegin(nodeOp.getLoc(),
1818 nodeOp->getBlock());
1819 auto wireOp = WireOp::create(
1820 builder, nodeOp.getResult().getType(), nodeOp.getNameAttr(),
1821 nodeOp.getNameKindAttr(), nodeOp.getAnnotationsAttr(),
1822 nodeOp.getInnerSymAttr(), nodeOp.getForceableAttr());
1824 nodeOp->replaceAllUsesWith(wireOp);
1825 nodeOp->removeAttr(nodeOp.getInnerSymAttrName());
1829 nodeOp.setNameKind(NameKindEnum::DroppableName);
1830 nodeOp.setAnnotationsAttr(ArrayAttr::get(builder.getContext(), {}));
1831 builder.setInsertionPointAfter(nodeOp);
1832 emitConnect(builder, wireOp.getResult(), nodeOp.getResult());
1834 actualReset = wireOp.getResult();
1835 domain.localReset = wireOp.getResult();
1840 Block *targetBlock = dom.findNearestCommonDominator(
1841 resetOp->getBlock(), opsToUpdate[0]->getBlock());
1843 if (targetBlock != resetOp->getBlock())
1844 llvm::dbgs() <<
"- Needs to be moved to different block\n";
1853 auto getParentInBlock = [](Operation *op,
Block *block) {
1854 while (op && op->getBlock() != block)
1855 op = op->getParentOp();
1858 auto *resetOpInTarget = getParentInBlock(resetOp, targetBlock);
1859 auto *firstOpInTarget = getParentInBlock(opsToUpdate[0], targetBlock);
1865 if (resetOpInTarget->isBeforeInBlock(firstOpInTarget))
1866 resetOp->moveBefore(resetOpInTarget);
1868 resetOp->moveBefore(firstOpInTarget);
1873 for (
auto *op : opsToUpdate)
1874 implementFullReset(op, module, actualReset);
1881void InferResetsPass::implementFullReset(FInstanceLike inst,
1882 StringAttr moduleName,
1883 Value actualReset) {
1887 auto *node = instanceGraph->lookup(moduleName);
1888 auto refModule = dyn_cast<FModuleOp>(*node->
getModule());
1891 auto *domainIt = domains.find(refModule);
1892 if (domainIt == domains.end() || domainIt->second.empty())
1894 auto &domain = domainIt->second.back().first;
1895 assert(domain &&
"null domains should not be listed");
1897 ImplicitLocOpBuilder builder(inst.getLoc(), inst);
1899 LLVM_DEBUG(llvm::dbgs() << (actualReset ?
"- Update " :
"- Tie-off ")
1905 if (!domain.localReset) {
1906 LLVM_DEBUG(llvm::dbgs() <<
" - Adding new result as reset\n");
1907 auto newInstOp = inst.cloneWithInsertedPortsAndReplaceUses(
1909 {domain.resetName, domain.resetType, Direction::In}}});
1910 instReset = newInstOp->getResult(0);
1911 instanceGraph->replaceInstance(inst, newInstOp);
1914 }
else if (domain.existingPort.has_value()) {
1915 auto idx = *domain.existingPort;
1916 instReset = inst->getResult(idx);
1917 LLVM_DEBUG(llvm::dbgs() <<
" - Using result #" << idx <<
" as reset\n");
1926 builder.setInsertionPointAfter(inst);
1933 LLVM_DEBUG(llvm::dbgs() <<
" - Tying off reset to constant 0\n");
1934 if (type_isa<AsyncResetType>(domain.resetType))
1935 actualReset = SpecialConstantOp::create(builder, domain.resetType,
false);
1937 actualReset = ConstantOp::create(
1938 builder, UIntType::get(builder.getContext(), 1), APInt(1, 0));
1942 assert(instReset && actualReset);
1949void InferResetsPass::implementFullReset(Operation *op, FModuleOp module,
1950 Value actualReset) {
1951 ImplicitLocOpBuilder builder(op->getLoc(), op);
1954 if (
auto instOp = dyn_cast<FInstanceLike>(op))
1955 return implementFullReset(
1956 instOp, cast<StringAttr>(instOp.getReferencedModuleNamesAttr()[0]),
1964 if (
auto regOp = dyn_cast<RegOp>(op)) {
1965 LLVM_DEBUG(llvm::dbgs() <<
"- Adding full reset to " << regOp <<
"\n");
1967 auto newRegOp = RegResetOp::create(
1968 builder, regOp.getResult().getType(), regOp.getClockVal(), actualReset,
1969 zero, regOp.getNameAttr(), regOp.getNameKindAttr(),
1970 regOp.getAnnotations(), regOp.getInnerSymAttr(),
1971 regOp.getForceableAttr());
1972 regOp.getResult().replaceAllUsesWith(newRegOp.getResult());
1973 if (regOp.getForceable())
1974 regOp.getRef().replaceAllUsesWith(newRegOp.getRef());
1980 if (
auto regOp = dyn_cast<RegResetOp>(op)) {
1983 if (type_isa<AsyncResetType>(regOp.getResetSignal().getType()) ||
1984 type_isa<UIntType>(actualReset.getType())) {
1985 LLVM_DEBUG(llvm::dbgs() <<
"- Skipping (has reset) " << regOp <<
"\n");
1988 if (failed(regOp.verifyInvariants()))
1989 signalPassFailure();
1992 LLVM_DEBUG(llvm::dbgs() <<
"- Updating reset of " << regOp <<
"\n");
1994 auto reset = regOp.getResetSignal();
1995 auto value = regOp.getResetValue();
2001 builder.setInsertionPointAfterValue(regOp.getResult());
2002 auto mux = MuxPrimOp::create(builder, reset, value, regOp.getResult());
2006 builder.setInsertionPoint(regOp);
2008 regOp.getResetSignalMutable().assign(actualReset);
2009 regOp.getResetValueMutable().assign(zero);
2013LogicalResult InferResetsPass::verifyNoAbstractReset() {
2014 bool hasAbstractResetPorts =
false;
2015 for (FModuleLike module :
2016 getOperation().
getBodyBlock()->getOps<FModuleLike>()) {
2017 for (
PortInfo port : module.getPorts()) {
2018 if (getBaseOfType<ResetType>(port.type)) {
2019 auto diag = emitError(port.loc)
2020 <<
"a port \"" << port.getName()
2021 <<
"\" with abstract reset type was unable to be "
2022 "inferred by InferResets (is this a top-level port?)";
2023 diag.attachNote(module->getLoc())
2024 <<
"the module with this uninferred reset port was defined here";
2025 hasAbstractResetPorts =
true;
2030 if (hasAbstractResetPorts)
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static Value createZeroValue(ImplicitLocOpBuilder &builder, FIRRTLBaseType type, SmallDenseMap< FIRRTLBaseType, Value > &cache)
Construct a zero value of the given type using the given builder.
static unsigned getFieldID(BundleType type, unsigned index)
static unsigned getIndexForFieldID(BundleType type, unsigned fieldID)
static FIRRTLBaseType updateType(FIRRTLBaseType oldType, unsigned fieldID, FIRRTLBaseType fieldType)
Update the type of a single field within a type.
static bool isUselessVec(FIRRTLBaseType oldType, unsigned fieldID)
static StringAttr getResetName(Value reset)
Return the name of a reset.
static bool insertResetMux(ImplicitLocOpBuilder &builder, Value target, Value reset, Value resetValue)
Helper function that inserts reset multiplexer into all ConnectOps with the given target.
static bool typeContainsReset(Type type)
Check whether a type contains a ResetType.
static bool getDeclName(Value value, SmallString< 32 > &string)
static unsigned getMaxFieldID(FIRRTLBaseType type)
static std::pair< StringAttr, FModuleOp > getResetNameAndModule(Value reset)
Return the name and parent module of a reset.
static Location getLoc(DefSlot slot)
static Block * getBodyBlock(FModuleLike mod)
static InstancePath empty
This class represents a reference to a specific field or element of an aggregate value.
unsigned getFieldID() const
Get the field ID of this FieldRef, which is a unique identifier mapped to a specific field in a bundl...
Value getValue() const
Get the Value which created this location.
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
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.
This class provides a read-only projection of an annotation.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
FIRRTLBaseType getConstType(bool isConst) const
Return a 'const' or non-'const' version of this type.
bool isConst() const
Returns true if this is a 'const' type that can only hold compile-time constant values.
This class implements the same functionality as TypeSwitch except that it uses firrtl::type_dyn_cast ...
FIRRTLTypeSwitch< T, ResultT > & Case(CallableT &&caseFn)
Add a case on the given type.
This graph tracks modules and where they are instantiated.
This is a Node in the InstanceGraph.
bool noUses()
Return true if there are no more instances of this module.
auto getModule()
Get the module that this node is tracking.
An instance path composed of a series of instances.
InstanceOpInterface leaf() const
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
std::string getInstanceName(mlir::func::CallOp callOp)
A helper function to get the instance name.
FieldRef getFieldRefForTarget(const hw::InnerSymTarget &ist)
Get FieldRef pointing to the specified inner symbol target, which must be valid.
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
FIRRTLType mapBaseType(FIRRTLType type, function_ref< FIRRTLBaseType(FIRRTLBaseType)> fn)
Return a FIRRTLType with its base type component mutated by the given function.
llvm::raw_ostream & operator<<(llvm::raw_ostream &os, const InstanceInfo::LatticeValue &value)
std::pair< std::string, bool > getFieldName(const FieldRef &fieldRef, bool nameSafe=false)
Get a string identifier representing the FieldRef.
void emitConnect(OpBuilder &builder, Location loc, Value lhs, Value rhs)
Emit a connect between two values.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
static bool operator==(const ModulePort &a, const ModulePort &b)
static llvm::hash_code hash_value(const ModulePort &port)
bool operator<(const DictEntry &entry, const DictEntry &other)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
llvm::raw_ostream & debugHeader(const llvm::Twine &str, unsigned width=80)
Write a "header"-like string to the debug stream with a certain width.
bool operator!=(uint64_t a, const FVInt &b)
This holds the name and type that describes the module's ports.
This class represents the namespace in which InnerRef's can be resolved.
A data structure that caches and provides paths to module instances in the IR.
static ResetSignal getEmptyKey()
static ResetSignal getTombstoneKey()
static bool isEqual(const ResetSignal &lhs, const ResetSignal &rhs)
static unsigned getHashValue(const ResetSignal &x)