23#include "mlir/IR/Dominance.h"
24#include "mlir/IR/ImplicitLocOpBuilder.h"
25#include "mlir/IR/Threading.h"
26#include "mlir/Pass/Pass.h"
27#include "llvm/ADT/EquivalenceClasses.h"
28#include "llvm/ADT/SetVector.h"
29#include "llvm/ADT/TypeSwitch.h"
30#include "llvm/Support/Debug.h"
32#define DEBUG_TYPE "infer-resets"
36#define GEN_PASS_DEF_INFERRESETS
37#include "circt/Dialect/FIRRTL/Passes.h.inc"
41using circt::igraph::InstanceOpInterface;
44using llvm::BumpPtrAllocator;
46using llvm::SmallDenseSet;
47using llvm::SmallSetVector;
49using mlir::InferTypeOpInterface;
52using namespace firrtl;
61 if (
auto arg = dyn_cast<BlockArgument>(reset)) {
62 auto module = cast<FModuleOp>(arg.getParentRegion()->getParentOp());
63 return {
module.getPortNameAttr(arg.getArgNumber()), module};
65 auto *op = reset.getDefiningOp();
66 return {op->getAttrOfType<StringAttr>(
"name"),
67 op->getParentOfType<FModuleOp>()};
96 std::optional<unsigned> existingPort;
99 ResetDomain() =
default;
102 ResetDomain(Value rootReset)
103 : rootReset(rootReset), resetName(
getResetName(rootReset)),
104 resetType(rootReset.getType()) {}
107 explicit operator bool()
const {
return static_cast<bool>(rootReset); }
111inline bool operator==(
const ResetDomain &a,
const ResetDomain &b) {
112 return (a.isTop == b.isTop && a.resetName == b.resetName &&
113 a.resetType == b.resetType);
115inline bool operator!=(
const ResetDomain &a,
const ResetDomain &b) {
124 auto it = cache.find(type);
125 if (it != cache.end())
127 auto nullBit = [&]() {
129 builder, UIntType::get(builder.getContext(), 1,
true),
134 .
Case<ClockType>([&](
auto type) {
135 return AsClockPrimOp::create(builder, nullBit());
137 .Case<AsyncResetType>([&](
auto type) {
138 return AsAsyncResetPrimOp::create(builder, nullBit());
140 .Case<SIntType, UIntType>([&](
auto type) {
141 return ConstantOp::create(
142 builder, type, APInt::getZero(type.getWidth().value_or(1)));
144 .Case<FEnumType>([&](
auto type) -> Value {
147 if (type.getNumElements() != 0 &&
148 type.getElement(0).value.getValue().isZero()) {
149 const auto &element = type.getElement(0);
151 return FEnumCreateOp::create(builder, type, element.name, value);
153 auto value = ConstantOp::create(builder,
154 UIntType::get(builder.getContext(),
157 APInt::getZero(type.getBitWidth()));
158 return BitCastOp::create(builder, type, value);
160 .Case<BundleType>([&](
auto type) {
161 auto wireOp = WireOp::create(builder, type);
162 for (
unsigned i = 0, e = type.getNumElements(); i < e; ++i) {
163 auto fieldType = type.getElementTypePreservingConst(i);
166 SubfieldOp::create(builder, fieldType, wireOp.getResult(), i);
169 return wireOp.getResult();
171 .Case<FVectorType>([&](
auto type) {
172 auto wireOp = WireOp::create(builder, type);
174 builder, type.getElementTypePreservingConst(), cache);
175 for (
unsigned i = 0, e = type.getNumElements(); i < e; ++i) {
176 auto acc = SubindexOp::create(builder, zero.getType(),
177 wireOp.getResult(), i);
180 return wireOp.getResult();
182 .Case<ResetType, AnalogType>(
183 [&](
auto type) {
return InvalidValueOp::create(builder, type); })
185 llvm_unreachable(
"switch handles all types");
188 cache.insert({type, value});
205 Value reset, Value resetValue) {
209 bool resetValueUsed =
false;
211 for (
auto &use : target.getUses()) {
212 Operation *useOp = use.getOwner();
213 builder.setInsertionPoint(useOp);
214 TypeSwitch<Operation *>(useOp)
217 .Case<ConnectOp, MatchingConnectOp>([&](
auto op) {
218 if (op.getDest() != target)
220 LLVM_DEBUG(llvm::dbgs() <<
" - Insert mux into " << op <<
"\n");
222 MuxPrimOp::create(builder, reset, resetValue, op.getSrc());
223 op.getSrcMutable().assign(muxOp);
224 resetValueUsed =
true;
227 .Case<SubfieldOp>([&](
auto op) {
229 SubfieldOp::create(builder, resetValue, op.getFieldIndexAttr());
231 resetValueUsed =
true;
233 resetSubValue.erase();
236 .Case<SubindexOp>([&](
auto op) {
238 SubindexOp::create(builder, resetValue, op.getIndexAttr());
240 resetValueUsed =
true;
242 resetSubValue.erase();
245 .Case<SubaccessOp>([&](
auto op) {
246 if (op.getInput() != target)
249 SubaccessOp::create(builder, resetValue, op.getIndex());
251 resetValueUsed =
true;
253 resetSubValue.erase();
256 return resetValueUsed;
271 bool operator<(
const ResetSignal &other)
const {
return field < other.field; }
272 bool operator==(
const ResetSignal &other)
const {
273 return field == other.field;
275 bool operator!=(
const ResetSignal &other)
const {
return !(*
this == other); }
295using ResetDrives = SmallVector<ResetDrive, 1>;
298using ResetNetwork = llvm::iterator_range<
299 llvm::EquivalenceClasses<ResetSignal>::member_iterator>;
302enum class ResetKind { Async, Sync };
304static StringRef resetKindToStringRef(
const ResetKind &kind) {
306 case ResetKind::Async:
308 case ResetKind::Sync:
311 llvm_unreachable(
"unhandled reset kind");
327 static bool isEqual(
const ResetSignal &lhs,
const ResetSignal &rhs) {
336 case ResetKind::Async:
337 return os <<
"async";
338 case ResetKind::Sync:
448struct InferResetsPass
449 :
public circt::firrtl::impl::InferResetsBase<InferResetsPass> {
450 void runOnOperation()
override;
451 void runOnOperationInner();
454 using InferResetsBase::InferResetsBase;
455 InferResetsPass(
const InferResetsPass &other) : InferResetsBase(other) {}
460 void traceResets(CircuitOp circuit);
461 void traceResets(InstanceOp inst);
462 void traceResets(Value dst, Value src, Location loc);
463 void traceResets(Value value);
464 void traceResets(Type dstType, Value dst,
unsigned dstID, Type srcType,
465 Value src,
unsigned srcID, Location loc);
467 LogicalResult inferAndUpdateResets();
468 FailureOr<ResetKind> inferReset(ResetNetwork net);
469 LogicalResult updateReset(ResetNetwork net, ResetKind kind);
475 LogicalResult collectAnnos(CircuitOp circuit);
481 FailureOr<std::optional<Value>> collectAnnos(FModuleOp module);
483 LogicalResult buildDomains(CircuitOp circuit);
484 void buildDomains(FModuleOp module,
const InstancePath &instPath,
486 unsigned indent = 0);
488 LogicalResult determineImpl();
489 LogicalResult determineImpl(FModuleOp module, ResetDomain &domain);
491 LogicalResult implementFullReset();
492 LogicalResult implementFullReset(FModuleOp module, ResetDomain &domain);
493 void implementFullReset(Operation *op, FModuleOp module, Value actualReset);
495 LogicalResult verifyNoAbstractReset();
501 ResetNetwork getResetNetwork(ResetSignal signal) {
502 return llvm::make_range(resetClasses.findLeader(signal),
503 resetClasses.member_end());
507 ResetDrives &getResetDrives(ResetNetwork net) {
508 return resetDrives[*net.begin()];
513 ResetSignal guessRoot(ResetNetwork net);
514 ResetSignal guessRoot(ResetSignal signal) {
515 return guessRoot(getResetNetwork(signal));
522 llvm::EquivalenceClasses<ResetSignal> resetClasses;
525 DenseMap<ResetSignal, ResetDrives> resetDrives;
530 DenseMap<Operation *, Value> annotatedResets;
534 MapVector<FModuleOp, SmallVector<std::pair<ResetDomain, InstancePath>, 1>>
541 std::unique_ptr<InstancePathCache> instancePathCache;
545void InferResetsPass::runOnOperation() {
546 runOnOperationInner();
547 resetClasses = llvm::EquivalenceClasses<ResetSignal>();
549 annotatedResets.clear();
551 instancePathCache.reset(
nullptr);
552 markAnalysesPreserved<InstanceGraph>();
555void InferResetsPass::runOnOperationInner() {
556 instanceGraph = &getAnalysis<InstanceGraph>();
557 instancePathCache = std::make_unique<InstancePathCache>(*instanceGraph);
560 traceResets(getOperation());
563 if (failed(inferAndUpdateResets()))
564 return signalPassFailure();
567 if (failed(collectAnnos(getOperation())))
568 return signalPassFailure();
571 if (failed(buildDomains(getOperation())))
572 return signalPassFailure();
575 if (failed(determineImpl()))
576 return signalPassFailure();
579 if (failed(implementFullReset()))
580 return signalPassFailure();
583 if (failed(verifyNoAbstractReset()))
584 return signalPassFailure();
587ResetSignal InferResetsPass::guessRoot(ResetNetwork net) {
588 ResetDrives &drives = getResetDrives(net);
589 ResetSignal bestSignal = *net.begin();
590 unsigned bestNumDrives = -1;
592 for (
auto signal : net) {
594 if (isa_and_nonnull<InvalidValueOp>(
595 signal.field.getValue().getDefiningOp()))
600 unsigned numDrives = 0;
601 for (
auto &drive : drives)
602 if (drive.dst == signal)
608 if (numDrives < bestNumDrives) {
609 bestNumDrives = numDrives;
628 .
Case<BundleType>([](
auto type) {
630 for (
auto e : type.getElements())
635 [](
auto type) {
return getMaxFieldID(type.getElementType()) + 1; })
636 .Default([](
auto) {
return 0; });
640 assert(index < type.getNumElements());
642 for (
unsigned i = 0; i < index; ++i)
650 assert(type.getNumElements() &&
"Bundle must have >0 fields");
652 for (
const auto &e : llvm::enumerate(type.getElements())) {
654 if (fieldID < numSubfields)
656 fieldID -= numSubfields;
658 assert(
false &&
"field id outside bundle");
664 if (oldType.isGround()) {
670 if (
auto bundleType = type_dyn_cast<BundleType>(oldType)) {
678 if (
auto vectorType = type_dyn_cast<FVectorType>(oldType)) {
679 if (vectorType.getNumElements() == 0)
696 if (
auto arg = dyn_cast<BlockArgument>(value)) {
697 auto module = cast<FModuleOp>(arg.getOwner()->getParentOp());
698 string +=
module.getPortName(arg.getArgNumber());
702 auto *op = value.getDefiningOp();
703 return TypeSwitch<Operation *, bool>(op)
704 .Case<InstanceOp, MemOp>([&](
auto op) {
705 string += op.getName();
707 string += op.getPortName(cast<OpResult>(value).getResultNumber());
710 .Case<WireOp, NodeOp, RegOp, RegResetOp>([&](
auto op) {
711 string += op.getName();
714 .Default([](
auto) {
return false; });
718 SmallString<64> name;
723 auto type = value.getType();
726 if (
auto bundleType = type_dyn_cast<BundleType>(type)) {
729 auto &element = bundleType.getElements()[index];
732 string += element.name.getValue();
735 localID = localID -
getFieldID(bundleType, index);
736 }
else if (
auto vecType = type_dyn_cast<FVectorType>(type)) {
739 type = vecType.getElementType();
746 llvm_unreachable(
"unsupported type");
758 return TypeSwitch<Type, bool>(type)
760 return type.getRecursiveTypeProperties().hasUninferredReset;
762 .Default([](
auto) {
return false; });
769void InferResetsPass::traceResets(CircuitOp circuit) {
771 llvm::dbgs() <<
"\n";
772 debugHeader(
"Tracing uninferred resets") <<
"\n\n";
775 SmallVector<std::pair<FModuleOp, SmallVector<Operation *>>> moduleToOps;
777 for (
auto module : circuit.getOps<FModuleOp>())
778 moduleToOps.push_back({module, {}});
781 getAnalysis<hw::InnerSymbolTableCollection>()};
783 mlir::parallelForEach(circuit.getContext(), moduleToOps, [](
auto &e) {
784 e.first.walk([&](Operation *op) {
788 op->getResultTypes(),
789 [](mlir::Type type) { return typeContainsReset(type); }) ||
790 llvm::any_of(op->getOperandTypes(), typeContainsReset))
791 e.second.push_back(op);
795 for (
auto &[_, ops] : moduleToOps)
796 for (auto *op : ops) {
797 TypeSwitch<Operation *>(op)
798 .Case<FConnectLike>([&](
auto op) {
799 traceResets(op.getDest(), op.getSrc(), op.getLoc());
801 .Case<InstanceOp>([&](
auto op) { traceResets(op); })
802 .Case<RefSendOp>([&](
auto op) {
804 traceResets(op.getType().getType(), op.getResult(), 0,
805 op.getBase().getType().getPassiveType(), op.getBase(),
808 .Case<RefResolveOp>([&](
auto op) {
810 traceResets(op.getType(), op.getResult(), 0,
811 op.getRef().getType().getType(), op.getRef(), 0,
814 .Case<Forceable>([&](Forceable op) {
815 if (
auto node = dyn_cast<NodeOp>(op.getOperation()))
816 traceResets(node.getResult(), node.getInput(), node.getLoc());
818 if (op.isForceable())
819 traceResets(op.getDataType(), op.getData(), 0, op.getDataType(),
820 op.getDataRef(), 0, op.getLoc());
822 .Case<RWProbeOp>([&](RWProbeOp op) {
823 auto ist = irn.lookup(op.getTarget());
826 auto baseType = op.getType().getType();
827 traceResets(baseType, op.getResult(), 0, baseType.getPassiveType(),
828 ref.getValue(), ref.getFieldID(), op.getLoc());
830 .Case<UninferredResetCastOp, ConstCastOp, RefCastOp>([&](
auto op) {
831 traceResets(op.getResult(), op.getInput(), op.getLoc());
833 .Case<InvalidValueOp>([&](
auto op) {
842 auto type = op.getType();
845 LLVM_DEBUG(llvm::dbgs() <<
"Uniquify " << op <<
"\n");
846 ImplicitLocOpBuilder builder(op->getLoc(), op);
848 llvm::make_early_inc_range(
llvm::drop_begin(op->getUses()))) {
854 auto newOp = InvalidValueOp::create(builder, type);
859 .Case<SubfieldOp>([&](
auto op) {
862 BundleType bundleType = op.getInput().getType();
863 auto index = op.getFieldIndex();
864 traceResets(op.getType(), op.getResult(), 0,
865 bundleType.getElements()[index].type, op.getInput(),
869 .Case<SubindexOp, SubaccessOp>([&](
auto op) {
882 FVectorType vectorType = op.getInput().getType();
883 traceResets(op.getType(), op.getResult(), 0,
884 vectorType.getElementType(), op.getInput(),
888 .Case<RefSubOp>([&](RefSubOp op) {
890 auto aggType = op.getInput().getType().getType();
891 uint64_t fieldID = TypeSwitch<FIRRTLBaseType, uint64_t>(aggType)
892 .Case<FVectorType>([](
auto type) {
895 .Case<BundleType>([&](
auto type) {
898 traceResets(op.getType(), op.getResult(), 0,
899 op.getResult().getType(), op.getInput(), fieldID,
907void InferResetsPass::traceResets(InstanceOp inst) {
909 auto module = inst.getReferencedModule<FModuleOp>(*instanceGraph);
912 LLVM_DEBUG(llvm::dbgs() <<
"Visiting instance " << inst.getName() <<
"\n");
915 for (
const auto &it :
llvm::enumerate(inst.getResults())) {
916 auto dir =
module.getPortDirection(it.index());
917 Value dstPort =
module.getArgument(it.index());
918 Value srcPort = it.value();
919 if (dir == Direction::Out)
920 std::swap(dstPort, srcPort);
921 traceResets(dstPort, srcPort, it.value().getLoc());
927void InferResetsPass::traceResets(Value dst, Value src, Location loc) {
929 traceResets(dst.getType(), dst, 0, src.getType(), src, 0, loc);
934void InferResetsPass::traceResets(Type dstType, Value dst,
unsigned dstID,
935 Type srcType, Value src,
unsigned srcID,
937 if (
auto dstBundle = type_dyn_cast<BundleType>(dstType)) {
938 auto srcBundle = type_cast<BundleType>(srcType);
939 for (
unsigned dstIdx = 0, e = dstBundle.getNumElements(); dstIdx < e;
941 auto dstField = dstBundle.getElements()[dstIdx].name;
942 auto srcIdx = srcBundle.getElementIndex(dstField);
945 auto &dstElt = dstBundle.getElements()[dstIdx];
946 auto &srcElt = srcBundle.getElements()[*srcIdx];
948 traceResets(srcElt.type, src, srcID +
getFieldID(srcBundle, *srcIdx),
949 dstElt.type, dst, dstID +
getFieldID(dstBundle, dstIdx),
952 traceResets(dstElt.type, dst, dstID +
getFieldID(dstBundle, dstIdx),
953 srcElt.type, src, srcID +
getFieldID(srcBundle, *srcIdx),
960 if (
auto dstVector = type_dyn_cast<FVectorType>(dstType)) {
961 auto srcVector = type_cast<FVectorType>(srcType);
962 auto srcElType = srcVector.getElementType();
963 auto dstElType = dstVector.getElementType();
976 traceResets(dstElType, dst, dstID +
getFieldID(dstVector), srcElType, src,
982 if (
auto dstRef = type_dyn_cast<RefType>(dstType)) {
983 auto srcRef = type_cast<RefType>(srcType);
984 return traceResets(dstRef.getType(), dst, dstID, srcRef.getType(), src,
989 auto dstBase = type_dyn_cast<FIRRTLBaseType>(dstType);
990 auto srcBase = type_dyn_cast<FIRRTLBaseType>(srcType);
991 if (!dstBase || !srcBase)
993 if (!type_isa<ResetType>(dstBase) && !type_isa<ResetType>(srcBase))
998 LLVM_DEBUG(llvm::dbgs() <<
"Visiting driver '" << dstField <<
"' = '"
999 << srcField <<
"' (" << dstType <<
" = " << srcType
1005 ResetSignal dstLeader =
1006 *resetClasses.findLeader(resetClasses.insert({dstField, dstBase}));
1007 ResetSignal srcLeader =
1008 *resetClasses.findLeader(resetClasses.insert({srcField, srcBase}));
1011 ResetSignal unionLeader = *resetClasses.unionSets(dstLeader, srcLeader);
1012 assert(unionLeader == dstLeader || unionLeader == srcLeader);
1017 if (dstLeader != srcLeader) {
1018 auto &unionDrives = resetDrives[unionLeader];
1019 auto mergedDrivesIt =
1020 resetDrives.find(unionLeader == dstLeader ? srcLeader : dstLeader);
1021 if (mergedDrivesIt != resetDrives.end()) {
1022 unionDrives.append(mergedDrivesIt->second);
1023 resetDrives.erase(mergedDrivesIt);
1029 resetDrives[unionLeader].push_back(
1030 {{dstField, dstBase}, {srcField, srcBase}, loc});
1037LogicalResult InferResetsPass::inferAndUpdateResets() {
1039 llvm::dbgs() <<
"\n";
1042 for (
const auto &it : resetClasses) {
1043 if (!it->isLeader())
1045 ResetNetwork net = resetClasses.members(*it);
1048 auto kind = inferReset(net);
1053 if (failed(updateReset(net, *kind)))
1059FailureOr<ResetKind> InferResetsPass::inferReset(ResetNetwork net) {
1060 LLVM_DEBUG(llvm::dbgs() <<
"Inferring reset network with "
1061 << std::distance(net.begin(), net.end())
1065 unsigned asyncDrives = 0;
1066 unsigned syncDrives = 0;
1067 unsigned invalidDrives = 0;
1068 for (ResetSignal signal : net) {
1070 if (type_isa<AsyncResetType>(signal.type))
1072 else if (type_isa<UIntType>(signal.type))
1075 isa_and_nonnull<InvalidValueOp>(
1076 signal.field.getValue().getDefiningOp()))
1079 LLVM_DEBUG(llvm::dbgs() <<
"- Found " << asyncDrives <<
" async, "
1080 << syncDrives <<
" sync, " << invalidDrives
1081 <<
" invalid drives\n");
1084 if (asyncDrives == 0 && syncDrives == 0 && invalidDrives == 0) {
1085 ResetSignal root = guessRoot(net);
1086 auto diag = mlir::emitError(root.field.getValue().getLoc())
1087 <<
"reset network never driven with concrete type";
1088 for (ResetSignal signal : net)
1089 diag.attachNote(signal.field.
getLoc()) <<
"here: ";
1094 if (asyncDrives > 0 && syncDrives > 0) {
1095 ResetSignal root = guessRoot(net);
1096 bool majorityAsync = asyncDrives >= syncDrives;
1097 auto diag = mlir::emitError(root.field.getValue().getLoc())
1099 SmallString<32> fieldName;
1101 diag <<
" \"" << fieldName <<
"\"";
1102 diag <<
" simultaneously connected to async and sync resets";
1103 diag.attachNote(root.field.getValue().getLoc())
1104 <<
"majority of connections to this reset are "
1105 << (majorityAsync ?
"async" :
"sync");
1106 for (
auto &drive : getResetDrives(net)) {
1107 if ((type_isa<AsyncResetType>(drive.dst.type) && !majorityAsync) ||
1108 (type_isa<AsyncResetType>(drive.src.type) && !majorityAsync) ||
1109 (type_isa<UIntType>(drive.dst.type) && majorityAsync) ||
1110 (type_isa<UIntType>(drive.src.type) && majorityAsync))
1111 diag.attachNote(drive.loc)
1112 << (type_isa<AsyncResetType>(drive.src.type) ?
"async" :
"sync")
1121 auto kind = (asyncDrives ? ResetKind::Async : ResetKind::Sync);
1122 LLVM_DEBUG(llvm::dbgs() <<
"- Inferred as " << kind <<
"\n");
1130LogicalResult InferResetsPass::updateReset(ResetNetwork net, ResetKind kind) {
1131 LLVM_DEBUG(llvm::dbgs() <<
"Updating reset network with "
1132 << std::distance(net.begin(), net.end())
1133 <<
" nodes to " << kind <<
"\n");
1137 if (kind == ResetKind::Async)
1138 resetType = AsyncResetType::get(&getContext());
1140 resetType = UIntType::get(&getContext(), 1);
1145 SmallSetVector<Operation *, 16> worklist;
1146 SmallDenseSet<Operation *> moduleWorklist;
1147 SmallDenseSet<std::pair<Operation *, Operation *>> extmoduleWorklist;
1148 for (
auto signal : net) {
1149 Value value = signal.field.getValue();
1150 if (!isa<BlockArgument>(value) &&
1151 !isa_and_nonnull<WireOp, RegOp, RegResetOp, InstanceOp, InvalidValueOp,
1152 ConstCastOp, RefCastOp, UninferredResetCastOp,
1153 RWProbeOp, AsResetPrimOp>(value.getDefiningOp()))
1155 if (updateReset(signal.field, resetType)) {
1156 for (
auto user : value.getUsers())
1157 worklist.insert(user);
1158 if (
auto blockArg = dyn_cast<BlockArgument>(value))
1159 moduleWorklist.insert(blockArg.getOwner()->getParentOp());
1160 else if (
auto instOp = value.getDefiningOp<InstanceOp>()) {
1161 if (
auto extmodule =
1162 instOp.getReferencedModule<FExtModuleOp>(*instanceGraph))
1163 extmoduleWorklist.insert({extmodule, instOp});
1164 }
else if (
auto uncast = value.getDefiningOp<UninferredResetCastOp>()) {
1165 uncast.replaceAllUsesWith(uncast.getInput());
1167 }
else if (
auto asResetOp = value.getDefiningOp<AsResetPrimOp>()) {
1170 Value result = asResetOp.getInput();
1171 if (type_isa<AsyncResetType>(resetType)) {
1172 ImplicitLocOpBuilder builder(asResetOp.getLoc(), asResetOp);
1173 result = AsAsyncResetPrimOp::create(builder, asResetOp.getInput());
1175 asResetOp.replaceAllUsesWith(result);
1185 while (!worklist.empty()) {
1186 auto *wop = worklist.pop_back_val();
1187 SmallVector<Type, 2> types;
1188 if (
auto op = dyn_cast<InferTypeOpInterface>(wop)) {
1190 SmallVector<Type, 2> types;
1191 if (failed(op.inferReturnTypes(op->getContext(), op->getLoc(),
1192 op->getOperands(), op->getAttrDictionary(),
1193 op->getPropertiesStorage(),
1194 op->getRegions(), types)))
1199 for (
auto it :
llvm::zip(op->getResults(), types)) {
1200 auto newType = std::get<1>(it);
1201 if (std::get<0>(it).getType() == newType)
1203 std::get<0>(it).setType(newType);
1204 for (
auto *user : std::
get<0>(it).getUsers())
1205 worklist.insert(user);
1207 LLVM_DEBUG(llvm::dbgs() <<
"- Inferred " << *op <<
"\n");
1208 }
else if (
auto uop = dyn_cast<UninferredResetCastOp>(wop)) {
1209 for (
auto *user : uop.getResult().getUsers())
1210 worklist.insert(user);
1211 uop.replaceAllUsesWith(uop.getInput());
1212 LLVM_DEBUG(llvm::dbgs() <<
"- Inferred " << uop <<
"\n");
1218 for (
auto *op : moduleWorklist) {
1219 auto module = dyn_cast<FModuleOp>(op);
1223 SmallVector<Attribute> argTypes;
1224 argTypes.reserve(module.getNumPorts());
1225 for (
auto arg : module.getArguments())
1226 argTypes.push_back(TypeAttr::
get(arg.getType()));
1228 module.setPortTypesAttr(ArrayAttr::get(op->getContext(), argTypes));
1229 LLVM_DEBUG(llvm::dbgs()
1230 <<
"- Updated type of module '" << module.getName() <<
"'\n");
1234 for (
auto pair : extmoduleWorklist) {
1235 auto module = cast<FExtModuleOp>(pair.first);
1236 auto instOp = cast<InstanceOp>(pair.second);
1238 SmallVector<Attribute> types;
1239 for (
auto type : instOp.getResultTypes())
1240 types.push_back(TypeAttr::
get(type));
1242 module.setPortTypesAttr(ArrayAttr::get(module->getContext(), types));
1243 LLVM_DEBUG(llvm::dbgs()
1244 <<
"- Updated type of extmodule '" << module.getName() <<
"'\n");
1254 if (oldType.isGround()) {
1260 if (
auto bundleType = type_dyn_cast<BundleType>(oldType)) {
1262 SmallVector<BundleType::BundleElement> fields(bundleType.begin(),
1265 fields[index].type, fieldID -
getFieldID(bundleType, index), fieldType);
1266 return BundleType::get(oldType.getContext(), fields, bundleType.
isConst());
1270 if (
auto vectorType = type_dyn_cast<FVectorType>(oldType)) {
1271 auto newType =
updateType(vectorType.getElementType(),
1272 fieldID -
getFieldID(vectorType), fieldType);
1273 return FVectorType::get(newType, vectorType.getNumElements(),
1274 vectorType.isConst());
1277 llvm_unreachable(
"unknown aggregate type");
1284 auto oldType = type_cast<FIRRTLType>(field.
getValue().getType());
1290 if (oldType == newType)
1292 LLVM_DEBUG(llvm::dbgs() <<
"- Updating '" << field <<
"' from " << oldType
1293 <<
" to " << newType <<
"\n");
1302LogicalResult InferResetsPass::collectAnnos(CircuitOp circuit) {
1304 llvm::dbgs() <<
"\n";
1305 debugHeader(
"Gather reset annotations") <<
"\n\n";
1307 SmallVector<std::pair<FModuleOp, std::optional<Value>>> results;
1308 for (
auto module : circuit.getOps<FModuleOp>())
1309 results.push_back({module, {}});
1311 if (failed(mlir::failableParallelForEach(
1312 circuit.getContext(), results, [&](
auto &moduleAndResult) {
1313 auto result = collectAnnos(moduleAndResult.first);
1316 moduleAndResult.second = *result;
1321 for (
auto [module, reset] : results)
1322 if (reset.has_value())
1323 annotatedResets.insert({module, *reset});
1327FailureOr<std::optional<Value>>
1328InferResetsPass::collectAnnos(FModuleOp module) {
1329 bool anyFailed =
false;
1330 SmallSetVector<std::pair<Annotation, Location>, 4> conflictingAnnos;
1334 bool ignore =
false;
1336 if (anno.
isClass(excludeFromFullResetAnnoClass)) {
1338 conflictingAnnos.insert({anno, module.getLoc()});
1341 if (anno.
isClass(fullResetAnnoClass)) {
1343 module.emitError("''FullResetAnnotation' cannot target module; must
"
1344 "target port or wire/node instead
");
1352 // Consume any reset annotations on module ports.
1354 // Helper for checking annotations and determining the reset
1355 auto checkAnnotations = [&](Annotation anno, Value arg) {
1356 if (anno.isClass(fullResetAnnoClass)) {
1357 ResetKind expectedResetKind;
1358 if (auto rt = anno.getMember<StringAttr>("resetType
")) {
1360 expectedResetKind = ResetKind::Sync;
1361 } else if (rt == "async
") {
1362 expectedResetKind = ResetKind::Async;
1364 mlir::emitError(arg.getLoc(),
1365 "'FullResetAnnotation' requires resetType ==
'sync' "
1366 "|
'async', but got resetType ==
")
1372 mlir::emitError(arg.getLoc(),
1373 "'FullResetAnnotation' requires resetType ==
"
1374 "'sync' |
'async', but got no resetType
");
1378 // Check that the type is well-formed
1379 bool isAsync = expectedResetKind == ResetKind::Async;
1380 bool validUint = false;
1381 if (auto uintT = dyn_cast<UIntType>(arg.getType()))
1382 validUint = uintT.getWidth() == 1;
1383 if ((isAsync && !isa<AsyncResetType>(arg.getType())) ||
1384 (!isAsync && !validUint)) {
1385 auto kind = resetKindToStringRef(expectedResetKind);
1386 mlir::emitError(arg.getLoc(),
1387 "'FullResetAnnotation' with resetType ==
'")
1388 << kind << "' must target
" << kind << " reset, but targets
"
1395 conflictingAnnos.insert({anno, reset.getLoc()});
1399 if (anno.isClass(excludeFromFullResetAnnoClass)) {
1401 mlir::emitError(arg.getLoc(),
1402 "'ExcludeFromFullResetAnnotation' cannot
"
1403 "target port/wire/node; must target
module instead");
1411 Value arg =
module.getArgument(argNum);
1412 return checkAnnotations(anno, arg);
1418 module.getBody().walk([&](Operation *op) {
1420 if (!isa<WireOp, NodeOp>(op)) {
1421 if (AnnotationSet::hasAnnotation(op, fullResetAnnoClass,
1422 excludeFromFullResetAnnoClass)) {
1425 "reset annotations must target module, port, or wire/node");
1433 auto arg = op->getResult(0);
1434 return checkAnnotations(anno, arg);
1443 if (!ignore && !reset) {
1444 LLVM_DEBUG(llvm::dbgs()
1445 <<
"No reset annotation for " << module.getName() <<
"\n");
1446 return std::optional<Value>();
1450 if (conflictingAnnos.size() > 1) {
1451 auto diag =
module.emitError("multiple reset annotations on module '")
1452 << module.getName() << "'";
1453 for (
auto &annoAndLoc : conflictingAnnos)
1454 diag.attachNote(annoAndLoc.second)
1455 <<
"conflicting " << annoAndLoc.first.getClassAttr() <<
":";
1461 llvm::dbgs() <<
"Annotated reset for " <<
module.getName() << ": ";
1463 llvm::dbgs() <<
"no domain\n";
1464 else if (
auto arg = dyn_cast<BlockArgument>(reset))
1465 llvm::dbgs() <<
"port " <<
module.getPortName(arg.getArgNumber()) << "\n";
1467 llvm::dbgs() <<
"wire "
1468 << reset.getDefiningOp()->getAttrOfType<StringAttr>(
"name")
1474 return std::optional<Value>(reset);
1486LogicalResult InferResetsPass::buildDomains(CircuitOp circuit) {
1488 llvm::dbgs() <<
"\n";
1489 debugHeader(
"Build full reset domains") <<
"\n\n";
1493 auto &instGraph = getAnalysis<InstanceGraph>();
1499 dyn_cast_or_null<FModuleOp>(node.
getModule().getOperation()))
1500 buildDomains(module,
InstancePath{}, Value{}, instGraph);
1504 bool anyFailed =
false;
1505 for (
auto &it : domains) {
1506 auto module = cast<FModuleOp>(it.first);
1507 auto &domainConflicts = it.second;
1508 if (domainConflicts.size() <= 1)
1512 SmallDenseSet<Value> printedDomainResets;
1513 auto diag =
module.emitError("module '")
1515 << "' instantiated in different reset domains";
1516 for (
auto &it : domainConflicts) {
1517 ResetDomain &domain = it.first;
1518 const auto &path = it.second;
1519 auto inst = path.leaf();
1520 auto loc = path.empty() ?
module.getLoc() : inst.getLoc();
1521 auto ¬e = diag.attachNote(loc);
1525 note <<
"root instance";
1527 note <<
"instance '";
1530 [&](InstanceOpInterface inst) { note << inst.getInstanceName(); },
1531 [&]() { note <<
"/"; });
1537 if (domain.rootReset) {
1539 note <<
" reset domain rooted at '" << nameAndModule.first.getValue()
1540 <<
"' of module '" << nameAndModule.second.getName() <<
"'";
1543 if (printedDomainResets.insert(domain.rootReset).second) {
1544 diag.attachNote(domain.rootReset.getLoc())
1545 <<
"reset domain '" << nameAndModule.first.getValue()
1546 <<
"' of module '" << nameAndModule.second.getName()
1547 <<
"' declared here:";
1550 note <<
" no reset domain";
1553 return failure(anyFailed);
1556void InferResetsPass::buildDomains(FModuleOp module,
1561 llvm::dbgs().indent(indent * 2) <<
"Visiting ";
1562 if (instPath.
empty())
1563 llvm::dbgs() <<
"$root";
1565 llvm::dbgs() << instPath.
leaf().getInstanceName();
1566 llvm::dbgs() <<
" (" <<
module.getName() << ")\n";
1571 auto it = annotatedResets.find(module);
1572 if (it != annotatedResets.end()) {
1575 if (
auto localReset = it->second)
1576 domain = ResetDomain(localReset);
1577 domain.isTop =
true;
1578 }
else if (parentReset) {
1580 domain = ResetDomain(parentReset);
1586 auto &entries = domains[module];
1587 if (llvm::all_of(entries,
1588 [&](
const auto &entry) {
return entry.first != domain; }))
1589 entries.push_back({domain, instPath});
1592 for (
auto *record : *instGraph[module]) {
1593 auto submodule = dyn_cast<FModuleOp>(*record->getTarget()->getModule());
1597 instancePathCache->appendInstance(instPath, record->getInstance());
1598 buildDomains(submodule, childPath, domain.rootReset, instGraph, indent + 1);
1603LogicalResult InferResetsPass::determineImpl() {
1604 auto anyFailed =
false;
1606 llvm::dbgs() <<
"\n";
1607 debugHeader(
"Determine implementation") <<
"\n\n";
1609 for (
auto &it : domains) {
1610 auto module = cast<FModuleOp>(it.first);
1611 auto &domain = it.second.back().first;
1612 if (failed(determineImpl(module, domain)))
1615 return failure(anyFailed);
1633LogicalResult InferResetsPass::determineImpl(FModuleOp module,
1634 ResetDomain &domain) {
1638 LLVM_DEBUG(llvm::dbgs() <<
"Planning reset for " << module.getName() <<
"\n");
1643 LLVM_DEBUG(llvm::dbgs()
1644 <<
"- Rooting at local value " << domain.resetName <<
"\n");
1645 domain.localReset = domain.rootReset;
1646 if (
auto blockArg = dyn_cast<BlockArgument>(domain.rootReset))
1647 domain.existingPort = blockArg.getArgNumber();
1653 auto neededName = domain.resetName;
1654 auto neededType = domain.resetType;
1655 LLVM_DEBUG(llvm::dbgs() <<
"- Looking for existing port " << neededName
1657 auto portNames =
module.getPortNames();
1658 auto *portIt = llvm::find(portNames, neededName);
1661 if (portIt == portNames.end()) {
1662 LLVM_DEBUG(llvm::dbgs() <<
"- Creating new port " << neededName <<
"\n");
1663 domain.resetName = neededName;
1667 LLVM_DEBUG(llvm::dbgs() <<
"- Reusing existing port " << neededName <<
"\n");
1670 auto portNo = std::distance(portNames.begin(), portIt);
1671 auto portType =
module.getPortType(portNo);
1672 if (portType != neededType) {
1673 auto diag = emitError(module.getPortLocation(portNo),
"module '")
1674 <<
module.getName() << "' is in reset domain requiring port '"
1675 << domain.resetName.getValue() << "' to have type "
1676 << domain.resetType << ", but has type " << portType;
1677 diag.attachNote(domain.rootReset.getLoc()) <<
"reset domain rooted here";
1682 domain.existingPort = portNo;
1683 domain.localReset =
module.getArgument(portNo);
1692LogicalResult InferResetsPass::implementFullReset() {
1694 llvm::dbgs() <<
"\n";
1697 for (
auto &it : domains)
1698 if (failed(implementFullReset(cast<FModuleOp>(it.first),
1699 it.second.back().first)))
1709LogicalResult InferResetsPass::implementFullReset(FModuleOp module,
1710 ResetDomain &domain) {
1711 LLVM_DEBUG(llvm::dbgs() <<
"Implementing full reset for " << module.getName()
1716 LLVM_DEBUG(llvm::dbgs()
1717 <<
"- Skipping because module explicitly has no domain\n");
1722 auto *
context =
module.getContext();
1724 annotations.addAnnotations(DictionaryAttr::get(
1726 StringAttr::get(
context, fullResetAnnoClass))));
1727 annotations.applyToOperation(module);
1730 auto actualReset = domain.localReset;
1731 if (!domain.localReset) {
1732 PortInfo portInfo{domain.resetName,
1736 domain.rootReset.getLoc()};
1737 module.insertPorts({{0, portInfo}});
1738 actualReset =
module.getArgument(0);
1739 LLVM_DEBUG(llvm::dbgs() <<
"- Inserted port " << domain.resetName <<
"\n");
1743 llvm::dbgs() <<
"- Using ";
1744 if (
auto blockArg = dyn_cast<BlockArgument>(actualReset))
1745 llvm::dbgs() <<
"port #" << blockArg.getArgNumber() <<
" ";
1747 llvm::dbgs() <<
"wire/node ";
1753 SmallVector<Operation *> opsToUpdate;
1754 module.walk([&](Operation *op) {
1755 if (isa<InstanceOp, RegOp, RegResetOp>(op))
1756 opsToUpdate.push_back(op);
1763 if (!isa<BlockArgument>(actualReset)) {
1764 mlir::DominanceInfo dom(module);
1769 auto *resetOp = actualReset.getDefiningOp();
1770 if (!opsToUpdate.empty() && !dom.dominates(resetOp, opsToUpdate[0])) {
1771 LLVM_DEBUG(llvm::dbgs()
1772 <<
"- Reset doesn't dominate all uses, needs to be moved\n");
1776 auto nodeOp = dyn_cast<NodeOp>(resetOp);
1777 if (nodeOp && !dom.dominates(nodeOp.getInput(), opsToUpdate[0])) {
1778 LLVM_DEBUG(llvm::dbgs()
1779 <<
"- Promoting node to wire for move: " << nodeOp <<
"\n");
1780 auto builder = ImplicitLocOpBuilder::atBlockBegin(nodeOp.getLoc(),
1781 nodeOp->getBlock());
1782 auto wireOp = WireOp::create(
1783 builder, nodeOp.getResult().getType(), nodeOp.getNameAttr(),
1784 nodeOp.getNameKindAttr(), nodeOp.getAnnotationsAttr(),
1785 nodeOp.getInnerSymAttr(), nodeOp.getForceableAttr());
1787 nodeOp->replaceAllUsesWith(wireOp);
1788 nodeOp->removeAttr(nodeOp.getInnerSymAttrName());
1792 nodeOp.setNameKind(NameKindEnum::DroppableName);
1793 nodeOp.setAnnotationsAttr(ArrayAttr::get(builder.getContext(), {}));
1794 builder.setInsertionPointAfter(nodeOp);
1795 emitConnect(builder, wireOp.getResult(), nodeOp.getResult());
1797 actualReset = wireOp.getResult();
1798 domain.localReset = wireOp.getResult();
1803 Block *targetBlock = dom.findNearestCommonDominator(
1804 resetOp->getBlock(), opsToUpdate[0]->getBlock());
1806 if (targetBlock != resetOp->getBlock())
1807 llvm::dbgs() <<
"- Needs to be moved to different block\n";
1816 auto getParentInBlock = [](Operation *op,
Block *block) {
1817 while (op && op->getBlock() != block)
1818 op = op->getParentOp();
1821 auto *resetOpInTarget = getParentInBlock(resetOp, targetBlock);
1822 auto *firstOpInTarget = getParentInBlock(opsToUpdate[0], targetBlock);
1828 if (resetOpInTarget->isBeforeInBlock(firstOpInTarget))
1829 resetOp->moveBefore(resetOpInTarget);
1831 resetOp->moveBefore(firstOpInTarget);
1836 for (
auto *op : opsToUpdate)
1837 implementFullReset(op, module, actualReset);
1844void InferResetsPass::implementFullReset(Operation *op, FModuleOp module,
1845 Value actualReset) {
1846 ImplicitLocOpBuilder builder(op->getLoc(), op);
1849 if (
auto instOp = dyn_cast<InstanceOp>(op)) {
1853 auto refModule = instOp.getReferencedModule<FModuleOp>(*instanceGraph);
1856 auto domainIt = domains.find(refModule);
1857 if (domainIt == domains.end())
1859 auto &domain = domainIt->second.back().first;
1862 LLVM_DEBUG(llvm::dbgs()
1863 <<
"- Update instance '" << instOp.getName() <<
"'\n");
1867 if (!domain.localReset) {
1868 LLVM_DEBUG(llvm::dbgs() <<
" - Adding new result as reset\n");
1869 auto newInstOp = instOp.cloneWithInsertedPortsAndReplaceUses(
1871 {domain.resetName, type_cast<FIRRTLBaseType>(actualReset.getType()),
1873 instReset = newInstOp.getResult(0);
1874 instanceGraph->replaceInstance(instOp, newInstOp);
1877 }
else if (domain.existingPort.has_value()) {
1878 auto idx = *domain.existingPort;
1879 instReset = instOp.getResult(idx);
1880 LLVM_DEBUG(llvm::dbgs() <<
" - Using result #" << idx <<
" as reset\n");
1890 assert(instReset && actualReset);
1891 builder.setInsertionPointAfter(instOp);
1897 if (
auto regOp = dyn_cast<RegOp>(op)) {
1898 LLVM_DEBUG(llvm::dbgs() <<
"- Adding full reset to " << regOp <<
"\n");
1900 auto newRegOp = RegResetOp::create(
1901 builder, regOp.getResult().getType(), regOp.getClockVal(), actualReset,
1902 zero, regOp.getNameAttr(), regOp.getNameKindAttr(),
1903 regOp.getAnnotations(), regOp.getInnerSymAttr(),
1904 regOp.getForceableAttr());
1905 regOp.getResult().replaceAllUsesWith(newRegOp.getResult());
1906 if (regOp.getForceable())
1907 regOp.getRef().replaceAllUsesWith(newRegOp.getRef());
1913 if (
auto regOp = dyn_cast<RegResetOp>(op)) {
1916 if (type_isa<AsyncResetType>(regOp.getResetSignal().getType()) ||
1917 type_isa<UIntType>(actualReset.getType())) {
1918 LLVM_DEBUG(llvm::dbgs() <<
"- Skipping (has reset) " << regOp <<
"\n");
1921 if (failed(regOp.verifyInvariants()))
1922 signalPassFailure();
1925 LLVM_DEBUG(llvm::dbgs() <<
"- Updating reset of " << regOp <<
"\n");
1927 auto reset = regOp.getResetSignal();
1928 auto value = regOp.getResetValue();
1934 builder.setInsertionPointAfterValue(regOp.getResult());
1935 auto mux = MuxPrimOp::create(builder, reset, value, regOp.getResult());
1939 builder.setInsertionPoint(regOp);
1941 regOp.getResetSignalMutable().assign(actualReset);
1942 regOp.getResetValueMutable().assign(zero);
1946LogicalResult InferResetsPass::verifyNoAbstractReset() {
1947 bool hasAbstractResetPorts =
false;
1948 for (FModuleLike module :
1949 getOperation().
getBodyBlock()->getOps<FModuleLike>()) {
1950 for (
PortInfo port : module.getPorts()) {
1951 if (getBaseOfType<ResetType>(port.type)) {
1952 auto diag = emitError(port.loc)
1953 <<
"a port \"" << port.getName()
1954 <<
"\" with abstract reset type was unable to be "
1955 "inferred by InferResets (is this a top-level port?)";
1956 diag.attachNote(module->getLoc())
1957 <<
"the module with this uninferred reset port was defined here";
1958 hasAbstractResetPorts =
true;
1963 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.
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.
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)