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;
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");
322 static bool isEqual(
const ResetSignal &lhs,
const ResetSignal &rhs) {
331 case ResetKind::Async:
332 return os <<
"async";
333 case ResetKind::Sync:
443struct InferResetsPass
444 :
public circt::firrtl::impl::InferResetsBase<InferResetsPass> {
445 void runOnOperation()
override;
446 void runOnOperationInner();
449 using InferResetsBase::InferResetsBase;
450 InferResetsPass(
const InferResetsPass &other) : InferResetsBase(other) {}
455 void traceResets(CircuitOp circuit);
456 void traceResets(FInstanceLike inst);
457 void traceResets(Value dst, Value src, Location loc);
458 void traceResets(Value value);
459 void traceResets(Type dstType, Value dst,
unsigned dstID, Type srcType,
460 Value src,
unsigned srcID, Location loc);
462 LogicalResult inferAndUpdateResets();
463 FailureOr<ResetKind> inferReset(ResetNetwork net);
464 LogicalResult updateReset(ResetNetwork net, ResetKind kind);
470 LogicalResult collectAnnos(CircuitOp circuit);
476 FailureOr<std::optional<Value>> collectAnnos(FModuleOp module);
478 LogicalResult buildDomains(CircuitOp circuit);
479 void buildDomains(FModuleOp module,
const InstancePath &instPath,
481 unsigned indent = 0);
483 LogicalResult determineImpl();
484 LogicalResult determineImpl(FModuleOp module, ResetDomain &domain);
486 LogicalResult implementFullReset();
487 LogicalResult implementFullReset(FModuleOp module, ResetDomain &domain);
488 void implementFullReset(Operation *op, FModuleOp module, Value actualReset);
491 void implementFullReset(FInstanceLike inst, StringAttr moduleName,
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;
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, InstanceChoiceOp, MemOp>([&](
auto op) {
704 string += op.getName();
706 string += op.getPortName(cast<OpResult>(value).getResultNumber());
709 .Case<WireOp, NodeOp, RegOp, RegResetOp>([&](
auto op) {
710 string += op.getName();
713 .Default([](
auto) {
return false; });
717 SmallString<64> name;
722 auto type = value.getType();
725 if (
auto bundleType = type_dyn_cast<BundleType>(type)) {
728 auto &element = bundleType.getElements()[index];
731 string += element.name.getValue();
734 localID = localID -
getFieldID(bundleType, index);
735 }
else if (
auto vecType = type_dyn_cast<FVectorType>(type)) {
738 type = vecType.getElementType();
745 llvm_unreachable(
"unsupported type");
757 return TypeSwitch<Type, bool>(type)
759 return type.getRecursiveTypeProperties().hasUninferredReset;
761 .Default([](
auto) {
return false; });
768void InferResetsPass::traceResets(CircuitOp circuit) {
770 llvm::dbgs() <<
"\n";
771 debugHeader(
"Tracing uninferred resets") <<
"\n\n";
774 SmallVector<std::pair<FModuleOp, SmallVector<Operation *>>> moduleToOps;
776 for (
auto module : circuit.getOps<FModuleOp>())
777 moduleToOps.push_back({module, {}});
780 getAnalysis<hw::InnerSymbolTableCollection>()};
782 mlir::parallelForEach(circuit.getContext(), moduleToOps, [](
auto &e) {
783 e.first.walk([&](Operation *op) {
787 op->getResultTypes(),
788 [](mlir::Type type) { return typeContainsReset(type); }) ||
789 llvm::any_of(op->getOperandTypes(), typeContainsReset))
790 e.second.push_back(op);
794 for (
auto &[_, ops] : moduleToOps)
795 for (auto *op : ops) {
796 TypeSwitch<Operation *>(op)
797 .Case<FConnectLike>([&](
auto op) {
798 traceResets(op.getDest(), op.getSrc(), op.getLoc());
800 .Case<FInstanceLike>([&](
auto op) { traceResets(op); })
801 .Case<RefSendOp>([&](
auto op) {
803 traceResets(op.getType().getType(), op.getResult(), 0,
804 op.getBase().getType().getPassiveType(), op.getBase(),
807 .Case<RefResolveOp>([&](
auto op) {
809 traceResets(op.getType(), op.getResult(), 0,
810 op.getRef().getType().getType(), op.getRef(), 0,
813 .Case<Forceable>([&](Forceable op) {
814 if (
auto node = dyn_cast<NodeOp>(op.getOperation()))
815 traceResets(node.getResult(), node.getInput(), node.getLoc());
817 if (op.isForceable())
818 traceResets(op.getDataType(), op.getData(), 0, op.getDataType(),
819 op.getDataRef(), 0, op.getLoc());
821 .Case<RWProbeOp>([&](RWProbeOp op) {
822 auto ist = irn.lookup(op.getTarget());
825 auto baseType = op.getType().getType();
826 traceResets(baseType, op.getResult(), 0, baseType.getPassiveType(),
827 ref.getValue(), ref.getFieldID(), op.getLoc());
829 .Case<UninferredResetCastOp, ConstCastOp, RefCastOp,
830 UnsafeDomainCastOp>([&](
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(FInstanceLike inst) {
908 LLVM_DEBUG(llvm::dbgs() <<
"Visiting instance " << inst.getInstanceName()
910 auto moduleNames = inst.getReferencedModuleNamesAttr();
911 for (
auto moduleName : moduleNames.getAsRange<StringAttr>()) {
912 auto *node = instanceGraph->lookup(moduleName);
913 auto module = dyn_cast<FModuleOp>(*node->getModule());
918 for (
const auto &it :
llvm::enumerate(inst->getResults())) {
919 Value dstPort =
module.getArgument(it.index());
920 Value srcPort = it.value();
921 if (module.getPortDirection(it.index()) == Direction::Out)
922 std::swap(dstPort, srcPort);
923 traceResets(dstPort, srcPort, it.value().getLoc());
930void InferResetsPass::traceResets(Value dst, Value src, Location loc) {
932 traceResets(dst.getType(), dst, 0, src.getType(), src, 0, loc);
937void InferResetsPass::traceResets(Type dstType, Value dst,
unsigned dstID,
938 Type srcType, Value src,
unsigned srcID,
940 if (
auto dstBundle = type_dyn_cast<BundleType>(dstType)) {
941 auto srcBundle = type_cast<BundleType>(srcType);
942 for (
unsigned dstIdx = 0, e = dstBundle.getNumElements(); dstIdx < e;
944 auto dstField = dstBundle.getElements()[dstIdx].name;
945 auto srcIdx = srcBundle.getElementIndex(dstField);
948 auto &dstElt = dstBundle.getElements()[dstIdx];
949 auto &srcElt = srcBundle.getElements()[*srcIdx];
951 traceResets(srcElt.type, src, srcID +
getFieldID(srcBundle, *srcIdx),
952 dstElt.type, dst, dstID +
getFieldID(dstBundle, dstIdx),
955 traceResets(dstElt.type, dst, dstID +
getFieldID(dstBundle, dstIdx),
956 srcElt.type, src, srcID +
getFieldID(srcBundle, *srcIdx),
963 if (
auto dstVector = type_dyn_cast<FVectorType>(dstType)) {
964 auto srcVector = type_cast<FVectorType>(srcType);
965 auto srcElType = srcVector.getElementType();
966 auto dstElType = dstVector.getElementType();
979 traceResets(dstElType, dst, dstID +
getFieldID(dstVector), srcElType, src,
985 if (
auto dstRef = type_dyn_cast<RefType>(dstType)) {
986 auto srcRef = type_cast<RefType>(srcType);
987 return traceResets(dstRef.getType(), dst, dstID, srcRef.getType(), src,
992 auto dstBase = type_dyn_cast<FIRRTLBaseType>(dstType);
993 auto srcBase = type_dyn_cast<FIRRTLBaseType>(srcType);
994 if (!dstBase || !srcBase)
996 if (!type_isa<ResetType>(dstBase) && !type_isa<ResetType>(srcBase))
1001 LLVM_DEBUG(llvm::dbgs() <<
"Visiting driver '" << dstField <<
"' = '"
1002 << srcField <<
"' (" << dstType <<
" = " << srcType
1008 ResetSignal dstLeader =
1009 *resetClasses.findLeader(resetClasses.insert({dstField, dstBase}));
1010 ResetSignal srcLeader =
1011 *resetClasses.findLeader(resetClasses.insert({srcField, srcBase}));
1014 ResetSignal unionLeader = *resetClasses.unionSets(dstLeader, srcLeader);
1015 assert(unionLeader == dstLeader || unionLeader == srcLeader);
1020 if (dstLeader != srcLeader) {
1021 auto &unionDrives = resetDrives[unionLeader];
1022 auto mergedDrivesIt =
1023 resetDrives.find(unionLeader == dstLeader ? srcLeader : dstLeader);
1024 if (mergedDrivesIt != resetDrives.end()) {
1025 unionDrives.append(mergedDrivesIt->second);
1026 resetDrives.erase(mergedDrivesIt);
1032 resetDrives[unionLeader].push_back(
1033 {{dstField, dstBase}, {srcField, srcBase}, loc});
1040LogicalResult InferResetsPass::inferAndUpdateResets() {
1042 llvm::dbgs() <<
"\n";
1045 for (
const auto &it : resetClasses) {
1046 if (!it->isLeader())
1048 ResetNetwork net = resetClasses.members(*it);
1051 auto kind = inferReset(net);
1056 if (failed(updateReset(net, *kind)))
1062FailureOr<ResetKind> InferResetsPass::inferReset(ResetNetwork net) {
1063 LLVM_DEBUG(llvm::dbgs() <<
"Inferring reset network with "
1064 << std::distance(net.begin(), net.end())
1068 unsigned asyncDrives = 0;
1069 unsigned syncDrives = 0;
1070 unsigned invalidDrives = 0;
1071 for (ResetSignal signal : net) {
1073 if (type_isa<AsyncResetType>(signal.type))
1075 else if (type_isa<UIntType>(signal.type))
1078 isa_and_nonnull<InvalidValueOp>(
1079 signal.field.getValue().getDefiningOp()))
1082 LLVM_DEBUG(llvm::dbgs() <<
"- Found " << asyncDrives <<
" async, "
1083 << syncDrives <<
" sync, " << invalidDrives
1084 <<
" invalid drives\n");
1087 if (asyncDrives == 0 && syncDrives == 0 && invalidDrives == 0) {
1088 ResetSignal root = guessRoot(net);
1089 auto diag = mlir::emitError(root.field.getValue().getLoc())
1090 <<
"reset network never driven with concrete type";
1091 for (ResetSignal signal : net)
1092 diag.attachNote(signal.field.
getLoc()) <<
"here: ";
1097 if (asyncDrives > 0 && syncDrives > 0) {
1098 ResetSignal root = guessRoot(net);
1099 bool majorityAsync = asyncDrives >= syncDrives;
1100 auto diag = mlir::emitError(root.field.getValue().getLoc())
1102 SmallString<32> fieldName;
1104 diag <<
" \"" << fieldName <<
"\"";
1105 diag <<
" simultaneously connected to async and sync resets";
1106 diag.attachNote(root.field.getValue().getLoc())
1107 <<
"majority of connections to this reset are "
1108 << (majorityAsync ?
"async" :
"sync");
1109 for (
auto &drive : getResetDrives(net)) {
1110 if ((type_isa<AsyncResetType>(drive.dst.type) && !majorityAsync) ||
1111 (type_isa<AsyncResetType>(drive.src.type) && !majorityAsync) ||
1112 (type_isa<UIntType>(drive.dst.type) && majorityAsync) ||
1113 (type_isa<UIntType>(drive.src.type) && majorityAsync))
1114 diag.attachNote(drive.loc)
1115 << (type_isa<AsyncResetType>(drive.src.type) ?
"async" :
"sync")
1124 auto kind = (asyncDrives ? ResetKind::Async : ResetKind::Sync);
1125 LLVM_DEBUG(llvm::dbgs() <<
"- Inferred as " << kind <<
"\n");
1133LogicalResult InferResetsPass::updateReset(ResetNetwork net, ResetKind kind) {
1134 LLVM_DEBUG(llvm::dbgs() <<
"Updating reset network with "
1135 << std::distance(net.begin(), net.end())
1136 <<
" nodes to " << kind <<
"\n");
1140 if (kind == ResetKind::Async)
1141 resetType = AsyncResetType::get(&getContext());
1143 resetType = UIntType::get(&getContext(), 1);
1149 SmallDenseSet<Operation *> moduleWorklist;
1150 SmallDenseSet<std::pair<Operation *, Operation *>> extmoduleWorklist;
1151 for (
auto signal : net) {
1152 Value value = signal.field.getValue();
1153 if (!isa<BlockArgument>(value) &&
1154 !isa_and_nonnull<WireOp, RegOp, RegResetOp, FInstanceLike,
1155 InvalidValueOp, ConstCastOp, RefCastOp,
1156 UninferredResetCastOp, RWProbeOp, AsResetPrimOp>(
1157 value.getDefiningOp()))
1159 if (updateReset(signal.field, resetType)) {
1160 for (
auto *user : value.getUsers())
1161 worklist.insert(user);
1162 if (
auto blockArg = dyn_cast<BlockArgument>(value)) {
1163 moduleWorklist.insert(blockArg.getOwner()->getParentOp());
1167 TypeSwitch<Operation *>(value.getDefiningOp())
1168 .Case<FInstanceLike>([&](FInstanceLike op) {
1169 for (
auto moduleName : op.getReferencedModuleNamesAttr()) {
1170 auto *node = instanceGraph->lookup(cast<StringAttr>(moduleName));
1171 if (
auto refModule = dyn_cast<FExtModuleOp>(*node->getModule()))
1172 extmoduleWorklist.insert({refModule, op.getOperation()});
1175 .Case<UninferredResetCastOp>([&](
auto op) {
1176 op.replaceAllUsesWith(op.getInput());
1179 .Case<AsResetPrimOp>([&](
auto op) {
1182 Value result = op.getInput();
1183 if (type_isa<AsyncResetType>(resetType)) {
1184 ImplicitLocOpBuilder builder(op.getLoc(), op);
1185 result = AsAsyncResetPrimOp::create(builder, op.getInput());
1187 op.replaceAllUsesWith(result);
1197 while (!worklist.empty()) {
1198 auto *wop = worklist.pop_back_val();
1199 SmallVector<Type, 2> types;
1200 if (
auto op = dyn_cast<InferTypeOpInterface>(wop)) {
1202 SmallVector<Type, 2> types;
1203 if (failed(op.inferReturnTypes(op->getContext(), op->getLoc(),
1204 op->getOperands(), op->getAttrDictionary(),
1205 op->getPropertiesStorage(),
1206 op->getRegions(), types)))
1211 for (
auto it :
llvm::zip(op->getResults(), types)) {
1212 auto newType = std::get<1>(it);
1213 if (std::get<0>(it).getType() == newType)
1215 std::get<0>(it).setType(newType);
1216 for (
auto *user : std::
get<0>(it).getUsers())
1217 worklist.insert(user);
1219 LLVM_DEBUG(llvm::dbgs() <<
"- Inferred " << *op <<
"\n");
1220 }
else if (
auto uop = dyn_cast<UninferredResetCastOp>(wop)) {
1221 for (
auto *user : uop.getResult().getUsers())
1222 worklist.insert(user);
1223 uop.replaceAllUsesWith(uop.getInput());
1224 LLVM_DEBUG(llvm::dbgs() <<
"- Inferred " << uop <<
"\n");
1230 for (
auto *op : moduleWorklist) {
1231 auto module = dyn_cast<FModuleOp>(op);
1235 SmallVector<Attribute> argTypes;
1236 argTypes.reserve(module.getNumPorts());
1237 for (
auto arg : module.getArguments())
1238 argTypes.push_back(TypeAttr::
get(arg.getType()));
1240 module.setPortTypesAttr(ArrayAttr::get(op->getContext(), argTypes));
1241 LLVM_DEBUG(llvm::dbgs()
1242 <<
"- Updated type of module '" << module.getName() <<
"'\n");
1246 for (
auto [mod, instOp] : extmoduleWorklist) {
1247 auto module = cast<FExtModuleOp>(mod);
1249 SmallVector<Attribute> types;
1250 for (
auto type : instOp->getResultTypes())
1251 types.push_back(TypeAttr::
get(type));
1253 module.setPortTypesAttr(ArrayAttr::get(module->getContext(), types));
1254 LLVM_DEBUG(llvm::dbgs()
1255 <<
"- Updated type of extmodule '" << module.getName() <<
"'\n");
1265 if (oldType.isGround()) {
1271 if (
auto bundleType = type_dyn_cast<BundleType>(oldType)) {
1273 SmallVector<BundleType::BundleElement> fields(bundleType.begin(),
1276 fields[index].type, fieldID -
getFieldID(bundleType, index), fieldType);
1277 return BundleType::get(oldType.getContext(), fields, bundleType.
isConst());
1281 if (
auto vectorType = type_dyn_cast<FVectorType>(oldType)) {
1282 auto newType =
updateType(vectorType.getElementType(),
1283 fieldID -
getFieldID(vectorType), fieldType);
1284 return FVectorType::get(newType, vectorType.getNumElements(),
1285 vectorType.isConst());
1288 llvm_unreachable(
"unknown aggregate type");
1295 auto oldType = type_cast<FIRRTLType>(field.
getValue().getType());
1301 if (oldType == newType)
1303 LLVM_DEBUG(llvm::dbgs() <<
"- Updating '" << field <<
"' from " << oldType
1304 <<
" to " << newType <<
"\n");
1313LogicalResult InferResetsPass::collectAnnos(CircuitOp circuit) {
1315 llvm::dbgs() <<
"\n";
1316 debugHeader(
"Gather reset annotations") <<
"\n\n";
1318 SmallVector<std::pair<FModuleOp, std::optional<Value>>> results;
1319 for (
auto module : circuit.getOps<FModuleOp>())
1320 results.push_back({module, {}});
1322 if (failed(mlir::failableParallelForEach(
1323 circuit.getContext(), results, [&](
auto &moduleAndResult) {
1324 auto result = collectAnnos(moduleAndResult.first);
1327 moduleAndResult.second = *result;
1332 for (
auto [module, reset] : results)
1333 if (reset.has_value())
1334 annotatedResets.insert({module, *reset});
1338FailureOr<std::optional<Value>>
1339InferResetsPass::collectAnnos(FModuleOp module) {
1340 bool anyFailed =
false;
1345 bool ignore =
false;
1347 if (anno.
isClass(excludeFromFullResetAnnoClass)) {
1349 conflictingAnnos.insert({anno, module.getLoc()});
1352 if (anno.
isClass(fullResetAnnoClass)) {
1354 module.emitError("''FullResetAnnotation' cannot target module; must
"
1355 "target port or wire/node instead
");
1363 // Consume any reset annotations on module ports.
1365 // Helper for checking annotations and determining the reset
1366 auto checkAnnotations = [&](Annotation anno, Value arg) {
1367 if (anno.isClass(fullResetAnnoClass)) {
1368 ResetKind expectedResetKind;
1369 if (auto rt = anno.getMember<StringAttr>("resetType
")) {
1371 expectedResetKind = ResetKind::Sync;
1372 } else if (rt == "async
") {
1373 expectedResetKind = ResetKind::Async;
1375 mlir::emitError(arg.getLoc(),
1376 "'FullResetAnnotation' requires resetType ==
'sync' "
1377 "|
'async', but got resetType ==
")
1383 mlir::emitError(arg.getLoc(),
1384 "'FullResetAnnotation' requires resetType ==
"
1385 "'sync' |
'async', but got no resetType
");
1389 // Check that the type is well-formed
1390 bool isAsync = expectedResetKind == ResetKind::Async;
1391 bool validUint = false;
1392 if (auto uintT = dyn_cast<UIntType>(arg.getType()))
1393 validUint = uintT.getWidth() == 1;
1394 if ((isAsync && !isa<AsyncResetType>(arg.getType())) ||
1395 (!isAsync && !validUint)) {
1396 auto kind = resetKindToStringRef(expectedResetKind);
1397 mlir::emitError(arg.getLoc(),
1398 "'FullResetAnnotation' with resetType ==
'")
1399 << kind << "' must target
" << kind << " reset, but targets
"
1406 conflictingAnnos.insert({anno, reset.getLoc()});
1410 if (anno.isClass(excludeFromFullResetAnnoClass)) {
1412 mlir::emitError(arg.getLoc(),
1413 "'ExcludeFromFullResetAnnotation' cannot
"
1414 "target port/wire/node; must target
module instead");
1422 Value arg =
module.getArgument(argNum);
1423 return checkAnnotations(anno, arg);
1429 module.getBody().walk([&](Operation *op) {
1431 if (!isa<WireOp, NodeOp>(op)) {
1432 if (AnnotationSet::hasAnnotation(op, fullResetAnnoClass,
1433 excludeFromFullResetAnnoClass)) {
1436 "reset annotations must target module, port, or wire/node");
1444 auto arg = op->getResult(0);
1445 return checkAnnotations(anno, arg);
1454 if (!ignore && !reset) {
1455 LLVM_DEBUG(llvm::dbgs()
1456 <<
"No reset annotation for " << module.getName() <<
"\n");
1457 return std::optional<Value>();
1461 if (conflictingAnnos.size() > 1) {
1462 auto diag =
module.emitError("multiple reset annotations on module '")
1463 << module.getName() << "'";
1464 for (
auto &annoAndLoc : conflictingAnnos)
1465 diag.attachNote(annoAndLoc.second)
1466 <<
"conflicting " << annoAndLoc.first.getClassAttr() <<
":";
1472 llvm::dbgs() <<
"Annotated reset for " <<
module.getName() << ": ";
1474 llvm::dbgs() <<
"no domain\n";
1475 else if (
auto arg = dyn_cast<BlockArgument>(reset))
1476 llvm::dbgs() <<
"port " <<
module.getPortName(arg.getArgNumber()) << "\n";
1478 llvm::dbgs() <<
"wire "
1479 << reset.getDefiningOp()->getAttrOfType<StringAttr>(
"name")
1485 return std::optional<Value>(reset);
1497LogicalResult InferResetsPass::buildDomains(CircuitOp circuit) {
1499 llvm::dbgs() <<
"\n";
1500 debugHeader(
"Build full reset domains") <<
"\n\n";
1504 auto &instGraph = getAnalysis<InstanceGraph>();
1510 dyn_cast_or_null<FModuleOp>(node.
getModule().getOperation()))
1511 buildDomains(module,
InstancePath{}, Value{}, instGraph);
1515 bool anyFailed =
false;
1516 for (
auto &it : domains) {
1517 auto module = cast<FModuleOp>(it.first);
1518 auto &domainConflicts = it.second;
1519 if (domainConflicts.size() <= 1)
1523 SmallDenseSet<Value> printedDomainResets;
1524 auto diag =
module.emitError("module '")
1526 << "' instantiated in different reset domains";
1527 for (
auto &it : domainConflicts) {
1528 ResetDomain &domain = it.first;
1529 const auto &path = it.second;
1530 auto inst = path.leaf();
1531 auto loc = path.empty() ?
module.getLoc() : inst.getLoc();
1532 auto ¬e = diag.attachNote(loc);
1536 note <<
"root instance";
1538 note <<
"instance '";
1541 [&](InstanceOpInterface inst) { note << inst.getInstanceName(); },
1542 [&]() { note <<
"/"; });
1548 if (domain.rootReset) {
1550 note <<
" reset domain rooted at '" << nameAndModule.first.getValue()
1551 <<
"' of module '" << nameAndModule.second.getName() <<
"'";
1554 if (printedDomainResets.insert(domain.rootReset).second) {
1555 diag.attachNote(domain.rootReset.getLoc())
1556 <<
"reset domain '" << nameAndModule.first.getValue()
1557 <<
"' of module '" << nameAndModule.second.getName()
1558 <<
"' declared here:";
1561 note <<
" no reset domain";
1564 return failure(anyFailed);
1567void InferResetsPass::buildDomains(FModuleOp module,
1572 llvm::dbgs().indent(indent * 2) <<
"Visiting ";
1573 if (instPath.
empty())
1574 llvm::dbgs() <<
"$root";
1576 llvm::dbgs() << instPath.
leaf().getInstanceName();
1577 llvm::dbgs() <<
" (" <<
module.getName() << ")\n";
1582 auto it = annotatedResets.find(module);
1583 if (it != annotatedResets.end()) {
1586 if (
auto localReset = it->second)
1587 domain = ResetDomain(localReset);
1588 domain.isTop =
true;
1589 }
else if (parentReset) {
1591 domain = ResetDomain(parentReset);
1598 auto &entries = domains[module];
1599 if (domain.rootReset)
1600 if (llvm::all_of(entries,
1601 [&](
const auto &entry) {
return entry.first != domain; }))
1602 entries.push_back({domain, instPath});
1605 for (
auto *record : *instGraph[module]) {
1606 auto submodule = dyn_cast<FModuleOp>(*record->getTarget()->getModule());
1610 instancePathCache->appendInstance(instPath, record->getInstance());
1611 buildDomains(submodule, childPath, domain.rootReset, instGraph, indent + 1);
1616LogicalResult InferResetsPass::determineImpl() {
1617 auto anyFailed =
false;
1619 llvm::dbgs() <<
"\n";
1620 debugHeader(
"Determine implementation") <<
"\n\n";
1622 for (
auto &it : domains) {
1623 auto module = cast<FModuleOp>(it.first);
1624 auto &entries = it.second;
1626 if (entries.empty())
1628 auto &domain = entries.back().first;
1629 if (failed(determineImpl(module, domain)))
1632 return failure(anyFailed);
1650LogicalResult InferResetsPass::determineImpl(FModuleOp module,
1651 ResetDomain &domain) {
1655 LLVM_DEBUG(llvm::dbgs() <<
"Planning reset for " << module.getName() <<
"\n");
1660 LLVM_DEBUG(llvm::dbgs()
1661 <<
"- Rooting at local value " << domain.resetName <<
"\n");
1662 domain.localReset = domain.rootReset;
1663 if (
auto blockArg = dyn_cast<BlockArgument>(domain.rootReset))
1664 domain.existingPort = blockArg.getArgNumber();
1670 auto neededName = domain.resetName;
1671 auto neededType = domain.resetType;
1672 LLVM_DEBUG(llvm::dbgs() <<
"- Looking for existing port " << neededName
1674 auto portNames =
module.getPortNames();
1675 auto *portIt = llvm::find(portNames, neededName);
1678 if (portIt == portNames.end()) {
1679 LLVM_DEBUG(llvm::dbgs() <<
"- Creating new port " << neededName <<
"\n");
1680 domain.resetName = neededName;
1684 LLVM_DEBUG(llvm::dbgs() <<
"- Reusing existing port " << neededName <<
"\n");
1687 auto portNo = std::distance(portNames.begin(), portIt);
1688 auto portType =
module.getPortType(portNo);
1689 if (portType != neededType) {
1690 auto diag = emitError(module.getPortLocation(portNo),
"module '")
1691 <<
module.getName() << "' is in reset domain requiring port '"
1692 << domain.resetName.getValue() << "' to have type "
1693 << domain.resetType << ", but has type " << portType;
1694 diag.attachNote(domain.rootReset.getLoc()) <<
"reset domain rooted here";
1699 domain.existingPort = portNo;
1700 domain.localReset =
module.getArgument(portNo);
1709LogicalResult InferResetsPass::implementFullReset() {
1711 llvm::dbgs() <<
"\n";
1714 for (
auto &it : domains) {
1715 auto module = cast<FModuleOp>(it.first);
1716 auto &entries = it.second;
1720 if (!entries.empty())
1721 domain = entries.back().first;
1722 if (failed(implementFullReset(module, domain)))
1733LogicalResult InferResetsPass::implementFullReset(FModuleOp module,
1734 ResetDomain &domain) {
1739 SmallVector<FInstanceLike> instances;
1740 module.walk([&](FInstanceLike instOp) { instances.push_back(instOp); });
1742 if (!instances.empty())
1743 llvm::dbgs() <<
"Tie off instances in " << module.getName() <<
"\n";
1745 for (
auto instOp : instances)
1746 implementFullReset(instOp, module, Value());
1750 LLVM_DEBUG(llvm::dbgs() <<
"Implementing full reset for " << module.getName()
1754 auto *
context =
module.getContext();
1756 annotations.addAnnotations(DictionaryAttr::get(
1758 StringAttr::get(
context, fullResetAnnoClass))));
1759 annotations.applyToOperation(module);
1762 auto actualReset = domain.localReset;
1763 if (!domain.localReset) {
1764 PortInfo portInfo{domain.resetName,
1768 domain.rootReset.getLoc()};
1769 module.insertPorts({{0, portInfo}});
1770 actualReset =
module.getArgument(0);
1771 LLVM_DEBUG(llvm::dbgs() <<
"- Inserted port " << domain.resetName <<
"\n");
1775 llvm::dbgs() <<
"- Using ";
1776 if (
auto blockArg = dyn_cast<BlockArgument>(actualReset))
1777 llvm::dbgs() <<
"port #" << blockArg.getArgNumber() <<
" ";
1779 llvm::dbgs() <<
"wire/node ";
1785 SmallVector<Operation *> opsToUpdate;
1786 module.walk([&](Operation *op) {
1787 if (isa<FInstanceLike, RegOp, RegResetOp>(op))
1788 opsToUpdate.push_back(op);
1795 if (!isa<BlockArgument>(actualReset)) {
1796 mlir::DominanceInfo dom(module);
1801 auto *resetOp = actualReset.getDefiningOp();
1802 if (!opsToUpdate.empty() && !dom.dominates(resetOp, opsToUpdate[0])) {
1803 LLVM_DEBUG(llvm::dbgs()
1804 <<
"- Reset doesn't dominate all uses, needs to be moved\n");
1808 auto nodeOp = dyn_cast<NodeOp>(resetOp);
1809 if (nodeOp && !dom.dominates(nodeOp.getInput(), opsToUpdate[0])) {
1810 LLVM_DEBUG(llvm::dbgs()
1811 <<
"- Promoting node to wire for move: " << nodeOp <<
"\n");
1812 auto builder = ImplicitLocOpBuilder::atBlockBegin(nodeOp.getLoc(),
1813 nodeOp->getBlock());
1814 auto wireOp = WireOp::create(
1815 builder, nodeOp.getResult().getType(), nodeOp.getNameAttr(),
1816 nodeOp.getNameKindAttr(), nodeOp.getAnnotationsAttr(),
1817 nodeOp.getInnerSymAttr(), nodeOp.getForceableAttr());
1819 nodeOp->replaceAllUsesWith(wireOp);
1820 nodeOp->removeAttr(nodeOp.getInnerSymAttrName());
1824 nodeOp.setNameKind(NameKindEnum::DroppableName);
1825 nodeOp.setAnnotationsAttr(ArrayAttr::get(builder.getContext(), {}));
1826 builder.setInsertionPointAfter(nodeOp);
1827 emitConnect(builder, wireOp.getResult(), nodeOp.getResult());
1829 actualReset = wireOp.getResult();
1830 domain.localReset = wireOp.getResult();
1835 Block *targetBlock = dom.findNearestCommonDominator(
1836 resetOp->getBlock(), opsToUpdate[0]->getBlock());
1838 if (targetBlock != resetOp->getBlock())
1839 llvm::dbgs() <<
"- Needs to be moved to different block\n";
1848 auto getParentInBlock = [](Operation *op,
Block *block) {
1849 while (op && op->getBlock() != block)
1850 op = op->getParentOp();
1853 auto *resetOpInTarget = getParentInBlock(resetOp, targetBlock);
1854 auto *firstOpInTarget = getParentInBlock(opsToUpdate[0], targetBlock);
1860 if (resetOpInTarget->isBeforeInBlock(firstOpInTarget))
1861 resetOp->moveBefore(resetOpInTarget);
1863 resetOp->moveBefore(firstOpInTarget);
1868 for (
auto *op : opsToUpdate)
1869 implementFullReset(op, module, actualReset);
1876void InferResetsPass::implementFullReset(FInstanceLike inst,
1877 StringAttr moduleName,
1878 Value actualReset) {
1882 auto *node = instanceGraph->lookup(moduleName);
1883 auto refModule = dyn_cast<FModuleOp>(*node->
getModule());
1886 auto *domainIt = domains.find(refModule);
1887 if (domainIt == domains.end() || domainIt->second.empty())
1889 auto &domain = domainIt->second.back().first;
1890 assert(domain &&
"null domains should not be listed");
1892 ImplicitLocOpBuilder builder(inst.getLoc(), inst);
1894 LLVM_DEBUG(llvm::dbgs() << (actualReset ?
"- Update " :
"- Tie-off ")
1900 if (!domain.localReset) {
1901 LLVM_DEBUG(llvm::dbgs() <<
" - Adding new result as reset\n");
1902 auto newInstOp = inst.cloneWithInsertedPortsAndReplaceUses(
1904 {domain.resetName, domain.resetType, Direction::In}}});
1905 instReset = newInstOp->getResult(0);
1906 instanceGraph->replaceInstance(inst, newInstOp);
1909 }
else if (domain.existingPort.has_value()) {
1910 auto idx = *domain.existingPort;
1911 instReset = inst->getResult(idx);
1912 LLVM_DEBUG(llvm::dbgs() <<
" - Using result #" << idx <<
" as reset\n");
1921 builder.setInsertionPointAfter(inst);
1928 LLVM_DEBUG(llvm::dbgs() <<
" - Tying off reset to constant 0\n");
1929 if (type_isa<AsyncResetType>(domain.resetType))
1930 actualReset = SpecialConstantOp::create(builder, domain.resetType,
false);
1932 actualReset = ConstantOp::create(
1933 builder, UIntType::get(builder.getContext(), 1), APInt(1, 0));
1937 assert(instReset && actualReset);
1944void InferResetsPass::implementFullReset(Operation *op, FModuleOp module,
1945 Value actualReset) {
1946 ImplicitLocOpBuilder builder(op->getLoc(), op);
1949 if (
auto instOp = dyn_cast<FInstanceLike>(op))
1950 return implementFullReset(
1951 instOp, cast<StringAttr>(instOp.getReferencedModuleNamesAttr()[0]),
1959 if (
auto regOp = dyn_cast<RegOp>(op)) {
1960 LLVM_DEBUG(llvm::dbgs() <<
"- Adding full reset to " << regOp <<
"\n");
1962 auto newRegOp = RegResetOp::create(
1963 builder, regOp.getResult().getType(), regOp.getClockVal(), actualReset,
1964 zero, regOp.getNameAttr(), regOp.getNameKindAttr(),
1965 regOp.getAnnotations(), regOp.getInnerSymAttr(),
1966 regOp.getForceableAttr());
1967 regOp.getResult().replaceAllUsesWith(newRegOp.getResult());
1968 if (regOp.getForceable())
1969 regOp.getRef().replaceAllUsesWith(newRegOp.getRef());
1975 if (
auto regOp = dyn_cast<RegResetOp>(op)) {
1978 if (type_isa<AsyncResetType>(regOp.getResetSignal().getType()) ||
1979 type_isa<UIntType>(actualReset.getType())) {
1980 LLVM_DEBUG(llvm::dbgs() <<
"- Skipping (has reset) " << regOp <<
"\n");
1983 if (failed(regOp.verifyInvariants()))
1984 signalPassFailure();
1987 LLVM_DEBUG(llvm::dbgs() <<
"- Updating reset of " << regOp <<
"\n");
1989 auto reset = regOp.getResetSignal();
1990 auto value = regOp.getResetValue();
1996 builder.setInsertionPointAfterValue(regOp.getResult());
1997 auto mux = MuxPrimOp::create(builder, reset, value, regOp.getResult());
2001 builder.setInsertionPoint(regOp);
2003 regOp.getResetSignalMutable().assign(actualReset);
2004 regOp.getResetValueMutable().assign(zero);
2008LogicalResult InferResetsPass::verifyNoAbstractReset() {
2009 bool hasAbstractResetPorts =
false;
2010 for (FModuleLike module :
2011 getOperation().
getBodyBlock()->getOps<FModuleLike>()) {
2012 for (
PortInfo port : module.getPorts()) {
2013 if (getBaseOfType<ResetType>(port.type)) {
2014 auto diag = emitError(port.loc)
2015 <<
"a port \"" << port.getName()
2016 <<
"\" with abstract reset type was unable to be "
2017 "inferred by InferResets (is this a top-level port?)";
2018 diag.attachNote(module->getLoc())
2019 <<
"the module with this uninferred reset port was defined here";
2020 hasAbstractResetPorts =
true;
2025 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 bool isEqual(const ResetSignal &lhs, const ResetSignal &rhs)
static unsigned getHashValue(const ResetSignal &x)