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);
111inline bool operator==(
const ResetDomain &a,
const ResetDomain &b) {
…}
115inline bool operator!=(
const ResetDomain &a,
const ResetDomain &b) {
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 builder.create<AsClockPrimOp>(nullBit());
137 .Case<AsyncResetType>([&](
auto type) {
138 return builder.create<AsAsyncResetPrimOp>(nullBit());
140 .Case<SIntType, UIntType>([&](
auto type) {
141 return builder.create<ConstantOp>(
142 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 builder.create<FEnumCreateOp>(type, element.name, value);
153 auto value = builder.create<ConstantOp>(
154 UIntType::get(builder.getContext(), type.getBitWidth(),
156 APInt::getZero(type.getBitWidth()));
157 return builder.create<BitCastOp>(type, value);
159 .Case<BundleType>([&](
auto type) {
160 auto wireOp = builder.create<WireOp>(type);
161 for (
unsigned i = 0, e = type.getNumElements(); i < e; ++i) {
162 auto fieldType = type.getElementTypePreservingConst(i);
165 builder.create<SubfieldOp>(fieldType, wireOp.getResult(), i);
168 return wireOp.getResult();
170 .Case<FVectorType>([&](
auto type) {
171 auto wireOp = builder.create<WireOp>(type);
173 builder, type.getElementTypePreservingConst(), cache);
174 for (
unsigned i = 0, e = type.getNumElements(); i < e; ++i) {
175 auto acc = builder.create<SubindexOp>(zero.getType(),
176 wireOp.getResult(), i);
179 return wireOp.getResult();
181 .Case<ResetType, AnalogType>(
182 [&](
auto type) {
return builder.create<InvalidValueOp>(type); })
184 llvm_unreachable(
"switch handles all types");
187 cache.insert({type, value});
204 Value reset, Value resetValue) {
208 bool resetValueUsed =
false;
210 for (
auto &use : target.getUses()) {
211 Operation *useOp = use.getOwner();
212 builder.setInsertionPoint(useOp);
213 TypeSwitch<Operation *>(useOp)
216 .Case<ConnectOp, MatchingConnectOp>([&](
auto op) {
217 if (op.getDest() != target)
219 LLVM_DEBUG(llvm::dbgs() <<
" - Insert mux into " << op <<
"\n");
221 builder.create<MuxPrimOp>(reset, resetValue, op.getSrc());
222 op.getSrcMutable().assign(muxOp);
223 resetValueUsed =
true;
226 .Case<SubfieldOp>([&](
auto op) {
228 builder.create<SubfieldOp>(resetValue, op.getFieldIndexAttr());
230 resetValueUsed =
true;
232 resetSubValue.erase();
235 .Case<SubindexOp>([&](
auto op) {
237 builder.create<SubindexOp>(resetValue, op.getIndexAttr());
239 resetValueUsed =
true;
241 resetSubValue.erase();
244 .Case<SubaccessOp>([&](
auto op) {
245 if (op.getInput() != target)
248 builder.create<SubaccessOp>(resetValue, op.getIndex());
250 resetValueUsed =
true;
252 resetSubValue.erase();
255 return resetValueUsed;
270 bool operator<(
const ResetSignal &other)
const {
return field < other.field; }
271 bool operator==(
const ResetSignal &other)
const {
272 return field == other.field;
274 bool operator!=(
const ResetSignal &other)
const {
return !(*
this == other); }
294using ResetDrives = SmallVector<ResetDrive, 1>;
297using ResetNetwork = llvm::iterator_range<
298 llvm::EquivalenceClasses<ResetSignal>::member_iterator>;
301enum class ResetKind { Async, Sync };
303static StringRef resetKindToStringRef(
const ResetKind &kind) {
305 case ResetKind::Async:
307 case ResetKind::Sync:
310 llvm_unreachable(
"unhandled reset kind");
326 static bool isEqual(
const ResetSignal &lhs,
const ResetSignal &rhs) {
326 static bool isEqual(
const ResetSignal &lhs,
const ResetSignal &rhs) {
…}
335 case ResetKind::Async:
336 return os <<
"async";
337 case ResetKind::Sync:
447struct InferResetsPass
448 :
public circt::firrtl::impl::InferResetsBase<InferResetsPass> {
449 void runOnOperation()
override;
450 void runOnOperationInner();
453 using InferResetsBase::InferResetsBase;
454 InferResetsPass(
const InferResetsPass &other) : InferResetsBase(other) {}
459 void traceResets(CircuitOp circuit);
460 void traceResets(InstanceOp inst);
461 void traceResets(Value dst, Value src, Location loc);
462 void traceResets(Value value);
463 void traceResets(Type dstType, Value dst,
unsigned dstID, Type srcType,
464 Value src,
unsigned srcID, Location loc);
466 LogicalResult inferAndUpdateResets();
467 FailureOr<ResetKind> inferReset(ResetNetwork net);
468 LogicalResult updateReset(ResetNetwork net, ResetKind kind);
474 LogicalResult collectAnnos(CircuitOp circuit);
480 FailureOr<std::optional<Value>> collectAnnos(FModuleOp module);
482 LogicalResult buildDomains(CircuitOp circuit);
483 void buildDomains(FModuleOp module,
const InstancePath &instPath,
485 unsigned indent = 0);
487 LogicalResult determineImpl();
488 LogicalResult determineImpl(FModuleOp module, ResetDomain &domain);
490 LogicalResult implementFullReset();
491 LogicalResult implementFullReset(FModuleOp module, ResetDomain &domain);
492 void implementFullReset(Operation *op, FModuleOp module, Value actualReset);
494 LogicalResult verifyNoAbstractReset();
500 ResetNetwork getResetNetwork(ResetSignal signal) {
501 return llvm::make_range(resetClasses.findLeader(signal),
502 resetClasses.member_end());
506 ResetDrives &getResetDrives(ResetNetwork net) {
507 return resetDrives[*net.begin()];
512 ResetSignal guessRoot(ResetNetwork net);
513 ResetSignal guessRoot(ResetSignal signal) {
514 return guessRoot(getResetNetwork(signal));
521 llvm::EquivalenceClasses<ResetSignal> resetClasses;
524 DenseMap<ResetSignal, ResetDrives> resetDrives;
529 DenseMap<Operation *, Value> annotatedResets;
533 MapVector<FModuleOp, SmallVector<std::pair<ResetDomain, InstancePath>, 1>>
540 std::unique_ptr<InstancePathCache> instancePathCache;
544void InferResetsPass::runOnOperation() {
545 runOnOperationInner();
546 resetClasses = llvm::EquivalenceClasses<ResetSignal>();
548 annotatedResets.clear();
550 instancePathCache.reset(
nullptr);
551 markAnalysesPreserved<InstanceGraph>();
554void InferResetsPass::runOnOperationInner() {
555 instanceGraph = &getAnalysis<InstanceGraph>();
556 instancePathCache = std::make_unique<InstancePathCache>(*instanceGraph);
559 traceResets(getOperation());
562 if (failed(inferAndUpdateResets()))
563 return signalPassFailure();
566 if (failed(collectAnnos(getOperation())))
567 return signalPassFailure();
570 if (failed(buildDomains(getOperation())))
571 return signalPassFailure();
574 if (failed(determineImpl()))
575 return signalPassFailure();
578 if (failed(implementFullReset()))
579 return signalPassFailure();
582 if (failed(verifyNoAbstractReset()))
583 return signalPassFailure();
586ResetSignal InferResetsPass::guessRoot(ResetNetwork net) {
587 ResetDrives &drives = getResetDrives(net);
588 ResetSignal bestSignal = *net.begin();
589 unsigned bestNumDrives = -1;
591 for (
auto signal : net) {
593 if (isa_and_nonnull<InvalidValueOp>(
594 signal.field.getValue().getDefiningOp()))
599 unsigned numDrives = 0;
600 for (
auto &drive : drives)
601 if (drive.dst == signal)
607 if (numDrives < bestNumDrives) {
608 bestNumDrives = numDrives;
627 .
Case<BundleType>([](
auto type) {
629 for (
auto e : type.getElements())
634 [](
auto type) {
return getMaxFieldID(type.getElementType()) + 1; })
635 .Default([](
auto) {
return 0; });
639 assert(index < type.getNumElements());
641 for (
unsigned i = 0; i < index; ++i)
649 assert(type.getNumElements() &&
"Bundle must have >0 fields");
651 for (
const auto &e : llvm::enumerate(type.getElements())) {
653 if (fieldID < numSubfields)
655 fieldID -= numSubfields;
657 assert(
false &&
"field id outside bundle");
663 if (oldType.isGround()) {
669 if (
auto bundleType = type_dyn_cast<BundleType>(oldType)) {
677 if (
auto vectorType = type_dyn_cast<FVectorType>(oldType)) {
678 if (vectorType.getNumElements() == 0)
695 if (
auto arg = dyn_cast<BlockArgument>(value)) {
696 auto module = cast<FModuleOp>(arg.getOwner()->getParentOp());
697 string +=
module.getPortName(arg.getArgNumber());
701 auto *op = value.getDefiningOp();
702 return TypeSwitch<Operation *, bool>(op)
703 .Case<InstanceOp, MemOp>([&](
auto op) {
704 string += op.getName();
707 op.getPortName(cast<OpResult>(value).getResultNumber()).getValue();
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 = builder.create<InvalidValueOp>(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>(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());
1175 while (!worklist.empty()) {
1176 auto *wop = worklist.pop_back_val();
1177 SmallVector<Type, 2> types;
1178 if (
auto op = dyn_cast<InferTypeOpInterface>(wop)) {
1180 SmallVector<Type, 2> types;
1181 if (failed(op.inferReturnTypes(op->getContext(), op->getLoc(),
1182 op->getOperands(), op->getAttrDictionary(),
1183 op->getPropertiesStorage(),
1184 op->getRegions(), types)))
1189 for (
auto it :
llvm::zip(op->getResults(), types)) {
1190 auto newType = std::get<1>(it);
1191 if (std::get<0>(it).getType() == newType)
1193 std::get<0>(it).setType(newType);
1194 for (
auto *user : std::
get<0>(it).getUsers())
1195 worklist.insert(user);
1197 LLVM_DEBUG(llvm::dbgs() <<
"- Inferred " << *op <<
"\n");
1198 }
else if (
auto uop = dyn_cast<UninferredResetCastOp>(wop)) {
1199 for (
auto *user : uop.getResult().getUsers())
1200 worklist.insert(user);
1201 uop.replaceAllUsesWith(uop.getInput());
1202 LLVM_DEBUG(llvm::dbgs() <<
"- Inferred " << uop <<
"\n");
1208 for (
auto *op : moduleWorklist) {
1209 auto module = dyn_cast<FModuleOp>(op);
1213 SmallVector<Attribute> argTypes;
1214 argTypes.reserve(module.getNumPorts());
1215 for (
auto arg : module.getArguments())
1216 argTypes.push_back(TypeAttr::
get(arg.getType()));
1218 module.setPortTypesAttr(ArrayAttr::get(op->getContext(), argTypes));
1219 LLVM_DEBUG(llvm::dbgs()
1220 <<
"- Updated type of module '" << module.getName() <<
"'\n");
1224 for (
auto pair : extmoduleWorklist) {
1225 auto module = cast<FExtModuleOp>(pair.first);
1226 auto instOp = cast<InstanceOp>(pair.second);
1228 SmallVector<Attribute> types;
1229 for (
auto type : instOp.getResultTypes())
1230 types.push_back(TypeAttr::
get(type));
1232 module.setPortTypesAttr(ArrayAttr::get(module->getContext(), types));
1233 LLVM_DEBUG(llvm::dbgs()
1234 <<
"- Updated type of extmodule '" << module.getName() <<
"'\n");
1244 if (oldType.isGround()) {
1250 if (
auto bundleType = type_dyn_cast<BundleType>(oldType)) {
1252 SmallVector<BundleType::BundleElement> fields(bundleType.begin(),
1255 fields[index].type, fieldID -
getFieldID(bundleType, index), fieldType);
1256 return BundleType::get(oldType.getContext(), fields, bundleType.
isConst());
1260 if (
auto vectorType = type_dyn_cast<FVectorType>(oldType)) {
1261 auto newType =
updateType(vectorType.getElementType(),
1262 fieldID -
getFieldID(vectorType), fieldType);
1263 return FVectorType::get(newType, vectorType.getNumElements(),
1264 vectorType.isConst());
1267 llvm_unreachable(
"unknown aggregate type");
1274 auto oldType = type_cast<FIRRTLType>(field.
getValue().getType());
1280 if (oldType == newType)
1282 LLVM_DEBUG(llvm::dbgs() <<
"- Updating '" << field <<
"' from " << oldType
1283 <<
" to " << newType <<
"\n");
1292LogicalResult InferResetsPass::collectAnnos(CircuitOp circuit) {
1294 llvm::dbgs() <<
"\n";
1295 debugHeader(
"Gather reset annotations") <<
"\n\n";
1297 SmallVector<std::pair<FModuleOp, std::optional<Value>>> results;
1298 for (
auto module : circuit.getOps<FModuleOp>())
1299 results.push_back({module, {}});
1301 if (failed(mlir::failableParallelForEach(
1302 circuit.getContext(), results, [&](
auto &moduleAndResult) {
1303 auto result = collectAnnos(moduleAndResult.first);
1306 moduleAndResult.second = *result;
1311 for (
auto [module, reset] : results)
1312 if (reset.has_value())
1313 annotatedResets.insert({module, *reset});
1317FailureOr<std::optional<Value>>
1318InferResetsPass::collectAnnos(FModuleOp module) {
1319 bool anyFailed =
false;
1320 SmallSetVector<std::pair<Annotation, Location>, 4> conflictingAnnos;
1324 bool ignore =
false;
1328 conflictingAnnos.insert({anno, module.getLoc()});
1333 module.emitError("''FullResetAnnotation' cannot target module; must
"
1334 "target port or wire/node instead
");
1342 // Consume any reset annotations on module ports.
1344 // Helper for checking annotations and determining the reset
1345 auto checkAnnotations = [&](Annotation anno, Value arg) {
1346 if (anno.isClass(fullResetAnnoClass)) {
1347 ResetKind expectedResetKind;
1348 if (auto rt = anno.getMember<StringAttr>("resetType
")) {
1350 expectedResetKind = ResetKind::Sync;
1351 } else if (rt == "async
") {
1352 expectedResetKind = ResetKind::Async;
1354 mlir::emitError(arg.getLoc(),
1355 "'FullResetAnnotation' requires resetType ==
'sync' "
1356 "|
'async', but got resetType ==
")
1362 mlir::emitError(arg.getLoc(),
1363 "'FullResetAnnotation' requires resetType ==
"
1364 "'sync' |
'async', but got no resetType
");
1368 // Check that the type is well-formed
1369 bool isAsync = expectedResetKind == ResetKind::Async;
1370 bool validUint = false;
1371 if (auto uintT = dyn_cast<UIntType>(arg.getType()))
1372 validUint = uintT.getWidth() == 1;
1373 if ((isAsync && !isa<AsyncResetType>(arg.getType())) ||
1374 (!isAsync && !validUint)) {
1375 auto kind = resetKindToStringRef(expectedResetKind);
1376 mlir::emitError(arg.getLoc(),
1377 "'FullResetAnnotation' with resetType ==
'")
1378 << kind << "' must target
" << kind << " reset, but targets
"
1385 conflictingAnnos.insert({anno, reset.getLoc()});
1389 if (anno.isClass(excludeFromFullResetAnnoClass)) {
1391 mlir::emitError(arg.getLoc(),
1392 "'ExcludeFromFullResetAnnotation' cannot
"
1393 "target port/wire/node; must target
module instead");
1401 Value arg =
module.getArgument(argNum);
1402 return checkAnnotations(anno, arg);
1408 module.getBody().walk([&](Operation *op) {
1410 if (!isa<WireOp, NodeOp>(op)) {
1411 if (AnnotationSet::hasAnnotation(op, fullResetAnnoClass,
1412 excludeFromFullResetAnnoClass)) {
1415 "reset annotations must target module, port, or wire/node");
1423 auto arg = op->getResult(0);
1424 return checkAnnotations(anno, arg);
1433 if (!ignore && !reset) {
1434 LLVM_DEBUG(llvm::dbgs()
1435 <<
"No reset annotation for " << module.getName() <<
"\n");
1436 return std::optional<Value>();
1440 if (conflictingAnnos.size() > 1) {
1441 auto diag =
module.emitError("multiple reset annotations on module '")
1442 << module.getName() << "'";
1443 for (
auto &annoAndLoc : conflictingAnnos)
1444 diag.attachNote(annoAndLoc.second)
1445 <<
"conflicting " << annoAndLoc.first.getClassAttr() <<
":";
1451 llvm::dbgs() <<
"Annotated reset for " <<
module.getName() << ": ";
1453 llvm::dbgs() <<
"no domain\n";
1454 else if (
auto arg = dyn_cast<BlockArgument>(reset))
1455 llvm::dbgs() <<
"port " <<
module.getPortName(arg.getArgNumber()) << "\n";
1457 llvm::dbgs() <<
"wire "
1458 << reset.getDefiningOp()->getAttrOfType<StringAttr>(
"name")
1464 return std::optional<Value>(reset);
1476LogicalResult InferResetsPass::buildDomains(CircuitOp circuit) {
1478 llvm::dbgs() <<
"\n";
1479 debugHeader(
"Build full reset domains") <<
"\n\n";
1483 auto &instGraph = getAnalysis<InstanceGraph>();
1484 auto module = dyn_cast<FModuleOp>(*instGraph.getTopLevelNode()->getModule());
1486 LLVM_DEBUG(llvm::dbgs()
1487 <<
"Skipping circuit because main module is no `firrtl.module`");
1490 buildDomains(module,
InstancePath{}, Value{}, instGraph);
1493 bool anyFailed =
false;
1494 for (
auto &it : domains) {
1495 auto module = cast<FModuleOp>(it.first);
1496 auto &domainConflicts = it.second;
1497 if (domainConflicts.size() <= 1)
1501 SmallDenseSet<Value> printedDomainResets;
1502 auto diag =
module.emitError("module '")
1504 << "' instantiated in different reset domains";
1505 for (
auto &it : domainConflicts) {
1506 ResetDomain &domain = it.first;
1507 const auto &path = it.second;
1508 auto inst = path.leaf();
1509 auto loc = path.empty() ?
module.getLoc() : inst.getLoc();
1510 auto ¬e = diag.attachNote(loc);
1514 note <<
"root instance";
1516 note <<
"instance '";
1519 [&](InstanceOpInterface inst) { note << inst.getInstanceName(); },
1520 [&]() { note <<
"/"; });
1526 if (domain.rootReset) {
1528 note <<
" reset domain rooted at '" << nameAndModule.first.getValue()
1529 <<
"' of module '" << nameAndModule.second.getName() <<
"'";
1532 if (printedDomainResets.insert(domain.rootReset).second) {
1533 diag.attachNote(domain.rootReset.getLoc())
1534 <<
"reset domain '" << nameAndModule.first.getValue()
1535 <<
"' of module '" << nameAndModule.second.getName()
1536 <<
"' declared here:";
1539 note <<
" no reset domain";
1542 return failure(anyFailed);
1545void InferResetsPass::buildDomains(FModuleOp module,
1550 llvm::dbgs().indent(indent * 2) <<
"Visiting ";
1551 if (instPath.
empty())
1552 llvm::dbgs() <<
"$root";
1554 llvm::dbgs() << instPath.
leaf().getInstanceName();
1555 llvm::dbgs() <<
" (" <<
module.getName() << ")\n";
1560 auto it = annotatedResets.find(module);
1561 if (it != annotatedResets.end()) {
1564 if (
auto localReset = it->second)
1565 domain = ResetDomain(localReset);
1566 domain.isTop =
true;
1567 }
else if (parentReset) {
1569 domain = ResetDomain(parentReset);
1575 auto &entries = domains[module];
1576 if (llvm::all_of(entries,
1577 [&](
const auto &entry) {
return entry.first != domain; }))
1578 entries.push_back({domain, instPath});
1581 for (
auto *record : *instGraph[module]) {
1582 auto submodule = dyn_cast<FModuleOp>(*record->getTarget()->getModule());
1586 instancePathCache->appendInstance(instPath, record->getInstance());
1587 buildDomains(submodule, childPath, domain.rootReset, instGraph, indent + 1);
1592LogicalResult InferResetsPass::determineImpl() {
1593 auto anyFailed =
false;
1595 llvm::dbgs() <<
"\n";
1596 debugHeader(
"Determine implementation") <<
"\n\n";
1598 for (
auto &it : domains) {
1599 auto module = cast<FModuleOp>(it.first);
1600 auto &domain = it.second.back().first;
1601 if (failed(determineImpl(module, domain)))
1604 return failure(anyFailed);
1622LogicalResult InferResetsPass::determineImpl(FModuleOp module,
1623 ResetDomain &domain) {
1627 LLVM_DEBUG(llvm::dbgs() <<
"Planning reset for " << module.getName() <<
"\n");
1632 LLVM_DEBUG(llvm::dbgs()
1633 <<
"- Rooting at local value " << domain.resetName <<
"\n");
1634 domain.localReset = domain.rootReset;
1635 if (
auto blockArg = dyn_cast<BlockArgument>(domain.rootReset))
1636 domain.existingPort = blockArg.getArgNumber();
1642 auto neededName = domain.resetName;
1643 auto neededType = domain.resetType;
1644 LLVM_DEBUG(llvm::dbgs() <<
"- Looking for existing port " << neededName
1646 auto portNames =
module.getPortNames();
1647 auto *portIt = llvm::find(portNames, neededName);
1650 if (portIt == portNames.end()) {
1651 LLVM_DEBUG(llvm::dbgs() <<
"- Creating new port " << neededName <<
"\n");
1652 domain.resetName = neededName;
1656 LLVM_DEBUG(llvm::dbgs() <<
"- Reusing existing port " << neededName <<
"\n");
1659 auto portNo = std::distance(portNames.begin(), portIt);
1660 auto portType =
module.getPortType(portNo);
1661 if (portType != neededType) {
1662 auto diag = emitError(module.getPortLocation(portNo),
"module '")
1663 <<
module.getName() << "' is in reset domain requiring port '"
1664 << domain.resetName.getValue() << "' to have type "
1665 << domain.resetType << ", but has type " << portType;
1666 diag.attachNote(domain.rootReset.getLoc()) <<
"reset domain rooted here";
1671 domain.existingPort = portNo;
1672 domain.localReset =
module.getArgument(portNo);
1681LogicalResult InferResetsPass::implementFullReset() {
1683 llvm::dbgs() <<
"\n";
1686 for (
auto &it : domains)
1687 if (failed(implementFullReset(cast<FModuleOp>(it.first),
1688 it.second.back().first)))
1698LogicalResult InferResetsPass::implementFullReset(FModuleOp module,
1699 ResetDomain &domain) {
1700 LLVM_DEBUG(llvm::dbgs() <<
"Implementing full reset for " << module.getName()
1705 LLVM_DEBUG(llvm::dbgs()
1706 <<
"- Skipping because module explicitly has no domain\n");
1711 auto *context =
module.getContext();
1713 annotations.addAnnotations(DictionaryAttr::get(
1714 context, NamedAttribute(StringAttr::get(context,
"class"),
1716 annotations.applyToOperation(module);
1719 auto actualReset = domain.localReset;
1720 if (!domain.localReset) {
1721 PortInfo portInfo{domain.resetName,
1725 domain.rootReset.getLoc()};
1726 module.insertPorts({{0, portInfo}});
1727 actualReset =
module.getArgument(0);
1728 LLVM_DEBUG(llvm::dbgs() <<
"- Inserted port " << domain.resetName <<
"\n");
1732 llvm::dbgs() <<
"- Using ";
1733 if (
auto blockArg = dyn_cast<BlockArgument>(actualReset))
1734 llvm::dbgs() <<
"port #" << blockArg.getArgNumber() <<
" ";
1736 llvm::dbgs() <<
"wire/node ";
1742 SmallVector<Operation *> opsToUpdate;
1743 module.walk([&](Operation *op) {
1744 if (isa<InstanceOp, RegOp, RegResetOp>(op))
1745 opsToUpdate.push_back(op);
1752 if (!isa<BlockArgument>(actualReset)) {
1753 mlir::DominanceInfo dom(module);
1758 auto *resetOp = actualReset.getDefiningOp();
1759 if (!opsToUpdate.empty() && !dom.dominates(resetOp, opsToUpdate[0])) {
1760 LLVM_DEBUG(llvm::dbgs()
1761 <<
"- Reset doesn't dominate all uses, needs to be moved\n");
1765 auto nodeOp = dyn_cast<NodeOp>(resetOp);
1766 if (nodeOp && !dom.dominates(nodeOp.getInput(), opsToUpdate[0])) {
1767 LLVM_DEBUG(llvm::dbgs()
1768 <<
"- Promoting node to wire for move: " << nodeOp <<
"\n");
1769 auto builder = ImplicitLocOpBuilder::atBlockBegin(nodeOp.getLoc(),
1770 nodeOp->getBlock());
1771 auto wireOp = builder.create<WireOp>(
1772 nodeOp.getResult().getType(), nodeOp.getNameAttr(),
1773 nodeOp.getNameKindAttr(), nodeOp.getAnnotationsAttr(),
1774 nodeOp.getInnerSymAttr(), nodeOp.getForceableAttr());
1776 nodeOp->replaceAllUsesWith(wireOp);
1777 nodeOp->removeAttr(nodeOp.getInnerSymAttrName());
1781 nodeOp.setNameKind(NameKindEnum::DroppableName);
1782 nodeOp.setAnnotationsAttr(ArrayAttr::get(builder.getContext(), {}));
1783 builder.setInsertionPointAfter(nodeOp);
1784 emitConnect(builder, wireOp.getResult(), nodeOp.getResult());
1786 actualReset = wireOp.getResult();
1787 domain.localReset = wireOp.getResult();
1792 Block *targetBlock = dom.findNearestCommonDominator(
1793 resetOp->getBlock(), opsToUpdate[0]->getBlock());
1795 if (targetBlock != resetOp->getBlock())
1796 llvm::dbgs() <<
"- Needs to be moved to different block\n";
1805 auto getParentInBlock = [](Operation *op,
Block *block) {
1806 while (op && op->getBlock() != block)
1807 op = op->getParentOp();
1810 auto *resetOpInTarget = getParentInBlock(resetOp, targetBlock);
1811 auto *firstOpInTarget = getParentInBlock(opsToUpdate[0], targetBlock);
1817 if (resetOpInTarget->isBeforeInBlock(firstOpInTarget))
1818 resetOp->moveBefore(resetOpInTarget);
1820 resetOp->moveBefore(firstOpInTarget);
1825 for (
auto *op : opsToUpdate)
1826 implementFullReset(op, module, actualReset);
1833void InferResetsPass::implementFullReset(Operation *op, FModuleOp module,
1834 Value actualReset) {
1835 ImplicitLocOpBuilder builder(op->getLoc(), op);
1838 if (
auto instOp = dyn_cast<InstanceOp>(op)) {
1842 auto refModule = instOp.getReferencedModule<FModuleOp>(*instanceGraph);
1845 auto domainIt = domains.find(refModule);
1846 if (domainIt == domains.end())
1848 auto &domain = domainIt->second.back().first;
1851 LLVM_DEBUG(llvm::dbgs()
1852 <<
"- Update instance '" << instOp.getName() <<
"'\n");
1856 if (!domain.localReset) {
1857 LLVM_DEBUG(llvm::dbgs() <<
" - Adding new result as reset\n");
1859 auto newInstOp = instOp.cloneAndInsertPorts(
1861 {domain.resetName, type_cast<FIRRTLBaseType>(actualReset.getType()),
1863 instReset = newInstOp.getResult(0);
1866 instOp.replaceAllUsesWith(newInstOp.getResults().drop_front());
1867 instanceGraph->replaceInstance(instOp, newInstOp);
1870 }
else if (domain.existingPort.has_value()) {
1871 auto idx = *domain.existingPort;
1872 instReset = instOp.getResult(idx);
1873 LLVM_DEBUG(llvm::dbgs() <<
" - Using result #" << idx <<
" as reset\n");
1883 assert(instReset && actualReset);
1884 builder.setInsertionPointAfter(instOp);
1890 if (
auto regOp = dyn_cast<RegOp>(op)) {
1891 LLVM_DEBUG(llvm::dbgs() <<
"- Adding full reset to " << regOp <<
"\n");
1893 auto newRegOp = builder.create<RegResetOp>(
1894 regOp.getResult().getType(), regOp.getClockVal(), actualReset, zero,
1895 regOp.getNameAttr(), regOp.getNameKindAttr(), regOp.getAnnotations(),
1896 regOp.getInnerSymAttr(), regOp.getForceableAttr());
1897 regOp.getResult().replaceAllUsesWith(newRegOp.getResult());
1898 if (regOp.getForceable())
1899 regOp.getRef().replaceAllUsesWith(newRegOp.getRef());
1905 if (
auto regOp = dyn_cast<RegResetOp>(op)) {
1908 if (type_isa<AsyncResetType>(regOp.getResetSignal().getType()) ||
1909 type_isa<UIntType>(actualReset.getType())) {
1910 LLVM_DEBUG(llvm::dbgs() <<
"- Skipping (has reset) " << regOp <<
"\n");
1913 if (failed(regOp.verifyInvariants()))
1914 signalPassFailure();
1917 LLVM_DEBUG(llvm::dbgs() <<
"- Updating reset of " << regOp <<
"\n");
1919 auto reset = regOp.getResetSignal();
1920 auto value = regOp.getResetValue();
1926 builder.setInsertionPointAfterValue(regOp.getResult());
1927 auto mux = builder.create<MuxPrimOp>(reset, value, regOp.getResult());
1931 builder.setInsertionPoint(regOp);
1933 regOp.getResetSignalMutable().assign(actualReset);
1934 regOp.getResetValueMutable().assign(zero);
1938LogicalResult InferResetsPass::verifyNoAbstractReset() {
1939 bool hasAbstractResetPorts =
false;
1940 for (FModuleLike module :
1941 getOperation().
getBodyBlock()->getOps<FModuleLike>()) {
1942 for (
PortInfo port : module.getPorts()) {
1943 if (getBaseOfType<ResetType>(port.type)) {
1944 auto diag = emitError(port.loc)
1945 <<
"a port \"" << port.getName()
1946 <<
"\" with abstract reset type was unable to be "
1947 "inferred by InferResets (is this a top-level port?)";
1948 diag.attachNote(module->getLoc())
1949 <<
"the module with this uninferred reset port was defined here";
1950 hasAbstractResetPorts =
true;
1955 if (hasAbstractResetPorts)
assert(baseType &&"element must be base type")
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.
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.
constexpr const char * excludeFromFullResetAnnoClass
Annotation that marks a module as not belonging to any reset domain.
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.
constexpr const char * fullResetAnnoClass
Annotation that marks a reset (port or wire) and domain.
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(llvm::StringRef str, int 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)