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>([&](
auto op) {
830 traceResets(op.getResult(), op.getInput(), op.getLoc());
832 .Case<InvalidValueOp>([&](
auto op) {
841 auto type = op.getType();
844 LLVM_DEBUG(llvm::dbgs() <<
"Uniquify " << op <<
"\n");
845 ImplicitLocOpBuilder builder(op->getLoc(), op);
847 llvm::make_early_inc_range(
llvm::drop_begin(op->getUses()))) {
853 auto newOp = InvalidValueOp::create(builder, type);
858 .Case<SubfieldOp>([&](
auto op) {
861 BundleType bundleType = op.getInput().getType();
862 auto index = op.getFieldIndex();
863 traceResets(op.getType(), op.getResult(), 0,
864 bundleType.getElements()[index].type, op.getInput(),
868 .Case<SubindexOp, SubaccessOp>([&](
auto op) {
881 FVectorType vectorType = op.getInput().getType();
882 traceResets(op.getType(), op.getResult(), 0,
883 vectorType.getElementType(), op.getInput(),
887 .Case<RefSubOp>([&](RefSubOp op) {
889 auto aggType = op.getInput().getType().getType();
890 uint64_t fieldID = TypeSwitch<FIRRTLBaseType, uint64_t>(aggType)
891 .Case<FVectorType>([](
auto type) {
894 .Case<BundleType>([&](
auto type) {
897 traceResets(op.getType(), op.getResult(), 0,
898 op.getResult().getType(), op.getInput(), fieldID,
906void InferResetsPass::traceResets(FInstanceLike inst) {
907 LLVM_DEBUG(llvm::dbgs() <<
"Visiting instance " << inst.getInstanceName()
909 auto moduleNames = inst.getReferencedModuleNamesAttr();
910 for (
auto moduleName : moduleNames.getAsRange<StringAttr>()) {
911 auto *node = instanceGraph->lookup(moduleName);
912 auto module = dyn_cast<FModuleOp>(*node->getModule());
917 for (
const auto &it :
llvm::enumerate(inst->getResults())) {
918 Value dstPort =
module.getArgument(it.index());
919 Value srcPort = it.value();
920 if (module.getPortDirection(it.index()) == Direction::Out)
921 std::swap(dstPort, srcPort);
922 traceResets(dstPort, srcPort, it.value().getLoc());
929void InferResetsPass::traceResets(Value dst, Value src, Location loc) {
931 traceResets(dst.getType(), dst, 0, src.getType(), src, 0, loc);
936void InferResetsPass::traceResets(Type dstType, Value dst,
unsigned dstID,
937 Type srcType, Value src,
unsigned srcID,
939 if (
auto dstBundle = type_dyn_cast<BundleType>(dstType)) {
940 auto srcBundle = type_cast<BundleType>(srcType);
941 for (
unsigned dstIdx = 0, e = dstBundle.getNumElements(); dstIdx < e;
943 auto dstField = dstBundle.getElements()[dstIdx].name;
944 auto srcIdx = srcBundle.getElementIndex(dstField);
947 auto &dstElt = dstBundle.getElements()[dstIdx];
948 auto &srcElt = srcBundle.getElements()[*srcIdx];
950 traceResets(srcElt.type, src, srcID +
getFieldID(srcBundle, *srcIdx),
951 dstElt.type, dst, dstID +
getFieldID(dstBundle, dstIdx),
954 traceResets(dstElt.type, dst, dstID +
getFieldID(dstBundle, dstIdx),
955 srcElt.type, src, srcID +
getFieldID(srcBundle, *srcIdx),
962 if (
auto dstVector = type_dyn_cast<FVectorType>(dstType)) {
963 auto srcVector = type_cast<FVectorType>(srcType);
964 auto srcElType = srcVector.getElementType();
965 auto dstElType = dstVector.getElementType();
978 traceResets(dstElType, dst, dstID +
getFieldID(dstVector), srcElType, src,
984 if (
auto dstRef = type_dyn_cast<RefType>(dstType)) {
985 auto srcRef = type_cast<RefType>(srcType);
986 return traceResets(dstRef.getType(), dst, dstID, srcRef.getType(), src,
991 auto dstBase = type_dyn_cast<FIRRTLBaseType>(dstType);
992 auto srcBase = type_dyn_cast<FIRRTLBaseType>(srcType);
993 if (!dstBase || !srcBase)
995 if (!type_isa<ResetType>(dstBase) && !type_isa<ResetType>(srcBase))
1000 LLVM_DEBUG(llvm::dbgs() <<
"Visiting driver '" << dstField <<
"' = '"
1001 << srcField <<
"' (" << dstType <<
" = " << srcType
1007 ResetSignal dstLeader =
1008 *resetClasses.findLeader(resetClasses.insert({dstField, dstBase}));
1009 ResetSignal srcLeader =
1010 *resetClasses.findLeader(resetClasses.insert({srcField, srcBase}));
1013 ResetSignal unionLeader = *resetClasses.unionSets(dstLeader, srcLeader);
1014 assert(unionLeader == dstLeader || unionLeader == srcLeader);
1019 if (dstLeader != srcLeader) {
1020 auto &unionDrives = resetDrives[unionLeader];
1021 auto mergedDrivesIt =
1022 resetDrives.find(unionLeader == dstLeader ? srcLeader : dstLeader);
1023 if (mergedDrivesIt != resetDrives.end()) {
1024 unionDrives.append(mergedDrivesIt->second);
1025 resetDrives.erase(mergedDrivesIt);
1031 resetDrives[unionLeader].push_back(
1032 {{dstField, dstBase}, {srcField, srcBase}, loc});
1039LogicalResult InferResetsPass::inferAndUpdateResets() {
1041 llvm::dbgs() <<
"\n";
1044 for (
const auto &it : resetClasses) {
1045 if (!it->isLeader())
1047 ResetNetwork net = resetClasses.members(*it);
1050 auto kind = inferReset(net);
1055 if (failed(updateReset(net, *kind)))
1061FailureOr<ResetKind> InferResetsPass::inferReset(ResetNetwork net) {
1062 LLVM_DEBUG(llvm::dbgs() <<
"Inferring reset network with "
1063 << std::distance(net.begin(), net.end())
1067 unsigned asyncDrives = 0;
1068 unsigned syncDrives = 0;
1069 unsigned invalidDrives = 0;
1070 for (ResetSignal signal : net) {
1072 if (type_isa<AsyncResetType>(signal.type))
1074 else if (type_isa<UIntType>(signal.type))
1077 isa_and_nonnull<InvalidValueOp>(
1078 signal.field.getValue().getDefiningOp()))
1081 LLVM_DEBUG(llvm::dbgs() <<
"- Found " << asyncDrives <<
" async, "
1082 << syncDrives <<
" sync, " << invalidDrives
1083 <<
" invalid drives\n");
1086 if (asyncDrives == 0 && syncDrives == 0 && invalidDrives == 0) {
1087 ResetSignal root = guessRoot(net);
1088 auto diag = mlir::emitError(root.field.getValue().getLoc())
1089 <<
"reset network never driven with concrete type";
1090 for (ResetSignal signal : net)
1091 diag.attachNote(signal.field.
getLoc()) <<
"here: ";
1096 if (asyncDrives > 0 && syncDrives > 0) {
1097 ResetSignal root = guessRoot(net);
1098 bool majorityAsync = asyncDrives >= syncDrives;
1099 auto diag = mlir::emitError(root.field.getValue().getLoc())
1101 SmallString<32> fieldName;
1103 diag <<
" \"" << fieldName <<
"\"";
1104 diag <<
" simultaneously connected to async and sync resets";
1105 diag.attachNote(root.field.getValue().getLoc())
1106 <<
"majority of connections to this reset are "
1107 << (majorityAsync ?
"async" :
"sync");
1108 for (
auto &drive : getResetDrives(net)) {
1109 if ((type_isa<AsyncResetType>(drive.dst.type) && !majorityAsync) ||
1110 (type_isa<AsyncResetType>(drive.src.type) && !majorityAsync) ||
1111 (type_isa<UIntType>(drive.dst.type) && majorityAsync) ||
1112 (type_isa<UIntType>(drive.src.type) && majorityAsync))
1113 diag.attachNote(drive.loc)
1114 << (type_isa<AsyncResetType>(drive.src.type) ?
"async" :
"sync")
1123 auto kind = (asyncDrives ? ResetKind::Async : ResetKind::Sync);
1124 LLVM_DEBUG(llvm::dbgs() <<
"- Inferred as " << kind <<
"\n");
1132LogicalResult InferResetsPass::updateReset(ResetNetwork net, ResetKind kind) {
1133 LLVM_DEBUG(llvm::dbgs() <<
"Updating reset network with "
1134 << std::distance(net.begin(), net.end())
1135 <<
" nodes to " << kind <<
"\n");
1139 if (kind == ResetKind::Async)
1140 resetType = AsyncResetType::get(&getContext());
1142 resetType = UIntType::get(&getContext(), 1);
1148 SmallDenseSet<Operation *> moduleWorklist;
1149 SmallDenseSet<std::pair<Operation *, Operation *>> extmoduleWorklist;
1150 for (
auto signal : net) {
1151 Value value = signal.field.getValue();
1152 if (!isa<BlockArgument>(value) &&
1153 !isa_and_nonnull<WireOp, RegOp, RegResetOp, FInstanceLike,
1154 InvalidValueOp, ConstCastOp, RefCastOp,
1155 UninferredResetCastOp, RWProbeOp, AsResetPrimOp>(
1156 value.getDefiningOp()))
1158 if (updateReset(signal.field, resetType)) {
1159 for (
auto *user : value.getUsers())
1160 worklist.insert(user);
1161 if (
auto blockArg = dyn_cast<BlockArgument>(value)) {
1162 moduleWorklist.insert(blockArg.getOwner()->getParentOp());
1166 TypeSwitch<Operation *>(value.getDefiningOp())
1167 .Case<FInstanceLike>([&](FInstanceLike op) {
1168 for (
auto moduleName : op.getReferencedModuleNamesAttr()) {
1169 auto *node = instanceGraph->lookup(cast<StringAttr>(moduleName));
1170 if (
auto refModule = dyn_cast<FExtModuleOp>(*node->getModule()))
1171 extmoduleWorklist.insert({refModule, op.getOperation()});
1174 .Case<UninferredResetCastOp>([&](
auto op) {
1175 op.replaceAllUsesWith(op.getInput());
1178 .Case<AsResetPrimOp>([&](
auto op) {
1181 Value result = op.getInput();
1182 if (type_isa<AsyncResetType>(resetType)) {
1183 ImplicitLocOpBuilder builder(op.getLoc(), op);
1184 result = AsAsyncResetPrimOp::create(builder, op.getInput());
1186 op.replaceAllUsesWith(result);
1196 while (!worklist.empty()) {
1197 auto *wop = worklist.pop_back_val();
1198 SmallVector<Type, 2> types;
1199 if (
auto op = dyn_cast<InferTypeOpInterface>(wop)) {
1201 SmallVector<Type, 2> types;
1202 if (failed(op.inferReturnTypes(op->getContext(), op->getLoc(),
1203 op->getOperands(), op->getAttrDictionary(),
1204 op->getPropertiesStorage(),
1205 op->getRegions(), types)))
1210 for (
auto it :
llvm::zip(op->getResults(), types)) {
1211 auto newType = std::get<1>(it);
1212 if (std::get<0>(it).getType() == newType)
1214 std::get<0>(it).setType(newType);
1215 for (
auto *user : std::
get<0>(it).getUsers())
1216 worklist.insert(user);
1218 LLVM_DEBUG(llvm::dbgs() <<
"- Inferred " << *op <<
"\n");
1219 }
else if (
auto uop = dyn_cast<UninferredResetCastOp>(wop)) {
1220 for (
auto *user : uop.getResult().getUsers())
1221 worklist.insert(user);
1222 uop.replaceAllUsesWith(uop.getInput());
1223 LLVM_DEBUG(llvm::dbgs() <<
"- Inferred " << uop <<
"\n");
1229 for (
auto *op : moduleWorklist) {
1230 auto module = dyn_cast<FModuleOp>(op);
1234 SmallVector<Attribute> argTypes;
1235 argTypes.reserve(module.getNumPorts());
1236 for (
auto arg : module.getArguments())
1237 argTypes.push_back(TypeAttr::
get(arg.getType()));
1239 module.setPortTypesAttr(ArrayAttr::get(op->getContext(), argTypes));
1240 LLVM_DEBUG(llvm::dbgs()
1241 <<
"- Updated type of module '" << module.getName() <<
"'\n");
1245 for (
auto [mod, instOp] : extmoduleWorklist) {
1246 auto module = cast<FExtModuleOp>(mod);
1248 SmallVector<Attribute> types;
1249 for (
auto type : instOp->getResultTypes())
1250 types.push_back(TypeAttr::
get(type));
1252 module.setPortTypesAttr(ArrayAttr::get(module->getContext(), types));
1253 LLVM_DEBUG(llvm::dbgs()
1254 <<
"- Updated type of extmodule '" << module.getName() <<
"'\n");
1264 if (oldType.isGround()) {
1270 if (
auto bundleType = type_dyn_cast<BundleType>(oldType)) {
1272 SmallVector<BundleType::BundleElement> fields(bundleType.begin(),
1275 fields[index].type, fieldID -
getFieldID(bundleType, index), fieldType);
1276 return BundleType::get(oldType.getContext(), fields, bundleType.
isConst());
1280 if (
auto vectorType = type_dyn_cast<FVectorType>(oldType)) {
1281 auto newType =
updateType(vectorType.getElementType(),
1282 fieldID -
getFieldID(vectorType), fieldType);
1283 return FVectorType::get(newType, vectorType.getNumElements(),
1284 vectorType.isConst());
1287 llvm_unreachable(
"unknown aggregate type");
1294 auto oldType = type_cast<FIRRTLType>(field.
getValue().getType());
1300 if (oldType == newType)
1302 LLVM_DEBUG(llvm::dbgs() <<
"- Updating '" << field <<
"' from " << oldType
1303 <<
" to " << newType <<
"\n");
1312LogicalResult InferResetsPass::collectAnnos(CircuitOp circuit) {
1314 llvm::dbgs() <<
"\n";
1315 debugHeader(
"Gather reset annotations") <<
"\n\n";
1317 SmallVector<std::pair<FModuleOp, std::optional<Value>>> results;
1318 for (
auto module : circuit.getOps<FModuleOp>())
1319 results.push_back({module, {}});
1321 if (failed(mlir::failableParallelForEach(
1322 circuit.getContext(), results, [&](
auto &moduleAndResult) {
1323 auto result = collectAnnos(moduleAndResult.first);
1326 moduleAndResult.second = *result;
1331 for (
auto [module, reset] : results)
1332 if (reset.has_value())
1333 annotatedResets.insert({module, *reset});
1337FailureOr<std::optional<Value>>
1338InferResetsPass::collectAnnos(FModuleOp module) {
1339 bool anyFailed =
false;
1344 bool ignore =
false;
1346 if (anno.
isClass(excludeFromFullResetAnnoClass)) {
1348 conflictingAnnos.insert({anno, module.getLoc()});
1351 if (anno.
isClass(fullResetAnnoClass)) {
1353 module.emitError("''FullResetAnnotation' cannot target module; must
"
1354 "target port or wire/node instead
");
1362 // Consume any reset annotations on module ports.
1364 // Helper for checking annotations and determining the reset
1365 auto checkAnnotations = [&](Annotation anno, Value arg) {
1366 if (anno.isClass(fullResetAnnoClass)) {
1367 ResetKind expectedResetKind;
1368 if (auto rt = anno.getMember<StringAttr>("resetType
")) {
1370 expectedResetKind = ResetKind::Sync;
1371 } else if (rt == "async
") {
1372 expectedResetKind = ResetKind::Async;
1374 mlir::emitError(arg.getLoc(),
1375 "'FullResetAnnotation' requires resetType ==
'sync' "
1376 "|
'async', but got resetType ==
")
1382 mlir::emitError(arg.getLoc(),
1383 "'FullResetAnnotation' requires resetType ==
"
1384 "'sync' |
'async', but got no resetType
");
1388 // Check that the type is well-formed
1389 bool isAsync = expectedResetKind == ResetKind::Async;
1390 bool validUint = false;
1391 if (auto uintT = dyn_cast<UIntType>(arg.getType()))
1392 validUint = uintT.getWidth() == 1;
1393 if ((isAsync && !isa<AsyncResetType>(arg.getType())) ||
1394 (!isAsync && !validUint)) {
1395 auto kind = resetKindToStringRef(expectedResetKind);
1396 mlir::emitError(arg.getLoc(),
1397 "'FullResetAnnotation' with resetType ==
'")
1398 << kind << "' must target
" << kind << " reset, but targets
"
1405 conflictingAnnos.insert({anno, reset.getLoc()});
1409 if (anno.isClass(excludeFromFullResetAnnoClass)) {
1411 mlir::emitError(arg.getLoc(),
1412 "'ExcludeFromFullResetAnnotation' cannot
"
1413 "target port/wire/node; must target
module instead");
1421 Value arg =
module.getArgument(argNum);
1422 return checkAnnotations(anno, arg);
1428 module.getBody().walk([&](Operation *op) {
1430 if (!isa<WireOp, NodeOp>(op)) {
1431 if (AnnotationSet::hasAnnotation(op, fullResetAnnoClass,
1432 excludeFromFullResetAnnoClass)) {
1435 "reset annotations must target module, port, or wire/node");
1443 auto arg = op->getResult(0);
1444 return checkAnnotations(anno, arg);
1453 if (!ignore && !reset) {
1454 LLVM_DEBUG(llvm::dbgs()
1455 <<
"No reset annotation for " << module.getName() <<
"\n");
1456 return std::optional<Value>();
1460 if (conflictingAnnos.size() > 1) {
1461 auto diag =
module.emitError("multiple reset annotations on module '")
1462 << module.getName() << "'";
1463 for (
auto &annoAndLoc : conflictingAnnos)
1464 diag.attachNote(annoAndLoc.second)
1465 <<
"conflicting " << annoAndLoc.first.getClassAttr() <<
":";
1471 llvm::dbgs() <<
"Annotated reset for " <<
module.getName() << ": ";
1473 llvm::dbgs() <<
"no domain\n";
1474 else if (
auto arg = dyn_cast<BlockArgument>(reset))
1475 llvm::dbgs() <<
"port " <<
module.getPortName(arg.getArgNumber()) << "\n";
1477 llvm::dbgs() <<
"wire "
1478 << reset.getDefiningOp()->getAttrOfType<StringAttr>(
"name")
1484 return std::optional<Value>(reset);
1496LogicalResult InferResetsPass::buildDomains(CircuitOp circuit) {
1498 llvm::dbgs() <<
"\n";
1499 debugHeader(
"Build full reset domains") <<
"\n\n";
1503 auto &instGraph = getAnalysis<InstanceGraph>();
1509 dyn_cast_or_null<FModuleOp>(node.
getModule().getOperation()))
1510 buildDomains(module,
InstancePath{}, Value{}, instGraph);
1514 bool anyFailed =
false;
1515 for (
auto &it : domains) {
1516 auto module = cast<FModuleOp>(it.first);
1517 auto &domainConflicts = it.second;
1518 if (domainConflicts.size() <= 1)
1522 SmallDenseSet<Value> printedDomainResets;
1523 auto diag =
module.emitError("module '")
1525 << "' instantiated in different reset domains";
1526 for (
auto &it : domainConflicts) {
1527 ResetDomain &domain = it.first;
1528 const auto &path = it.second;
1529 auto inst = path.leaf();
1530 auto loc = path.empty() ?
module.getLoc() : inst.getLoc();
1531 auto ¬e = diag.attachNote(loc);
1535 note <<
"root instance";
1537 note <<
"instance '";
1540 [&](InstanceOpInterface inst) { note << inst.getInstanceName(); },
1541 [&]() { note <<
"/"; });
1547 if (domain.rootReset) {
1549 note <<
" reset domain rooted at '" << nameAndModule.first.getValue()
1550 <<
"' of module '" << nameAndModule.second.getName() <<
"'";
1553 if (printedDomainResets.insert(domain.rootReset).second) {
1554 diag.attachNote(domain.rootReset.getLoc())
1555 <<
"reset domain '" << nameAndModule.first.getValue()
1556 <<
"' of module '" << nameAndModule.second.getName()
1557 <<
"' declared here:";
1560 note <<
" no reset domain";
1563 return failure(anyFailed);
1566void InferResetsPass::buildDomains(FModuleOp module,
1571 llvm::dbgs().indent(indent * 2) <<
"Visiting ";
1572 if (instPath.
empty())
1573 llvm::dbgs() <<
"$root";
1575 llvm::dbgs() << instPath.
leaf().getInstanceName();
1576 llvm::dbgs() <<
" (" <<
module.getName() << ")\n";
1581 auto it = annotatedResets.find(module);
1582 if (it != annotatedResets.end()) {
1585 if (
auto localReset = it->second)
1586 domain = ResetDomain(localReset);
1587 domain.isTop =
true;
1588 }
else if (parentReset) {
1590 domain = ResetDomain(parentReset);
1597 auto &entries = domains[module];
1598 if (domain.rootReset)
1599 if (llvm::all_of(entries,
1600 [&](
const auto &entry) {
return entry.first != domain; }))
1601 entries.push_back({domain, instPath});
1604 for (
auto *record : *instGraph[module]) {
1605 auto submodule = dyn_cast<FModuleOp>(*record->getTarget()->getModule());
1609 instancePathCache->appendInstance(instPath, record->getInstance());
1610 buildDomains(submodule, childPath, domain.rootReset, instGraph, indent + 1);
1615LogicalResult InferResetsPass::determineImpl() {
1616 auto anyFailed =
false;
1618 llvm::dbgs() <<
"\n";
1619 debugHeader(
"Determine implementation") <<
"\n\n";
1621 for (
auto &it : domains) {
1622 auto module = cast<FModuleOp>(it.first);
1623 auto &entries = it.second;
1625 if (entries.empty())
1627 auto &domain = entries.back().first;
1628 if (failed(determineImpl(module, domain)))
1631 return failure(anyFailed);
1649LogicalResult InferResetsPass::determineImpl(FModuleOp module,
1650 ResetDomain &domain) {
1654 LLVM_DEBUG(llvm::dbgs() <<
"Planning reset for " << module.getName() <<
"\n");
1659 LLVM_DEBUG(llvm::dbgs()
1660 <<
"- Rooting at local value " << domain.resetName <<
"\n");
1661 domain.localReset = domain.rootReset;
1662 if (
auto blockArg = dyn_cast<BlockArgument>(domain.rootReset))
1663 domain.existingPort = blockArg.getArgNumber();
1669 auto neededName = domain.resetName;
1670 auto neededType = domain.resetType;
1671 LLVM_DEBUG(llvm::dbgs() <<
"- Looking for existing port " << neededName
1673 auto portNames =
module.getPortNames();
1674 auto *portIt = llvm::find(portNames, neededName);
1677 if (portIt == portNames.end()) {
1678 LLVM_DEBUG(llvm::dbgs() <<
"- Creating new port " << neededName <<
"\n");
1679 domain.resetName = neededName;
1683 LLVM_DEBUG(llvm::dbgs() <<
"- Reusing existing port " << neededName <<
"\n");
1686 auto portNo = std::distance(portNames.begin(), portIt);
1687 auto portType =
module.getPortType(portNo);
1688 if (portType != neededType) {
1689 auto diag = emitError(module.getPortLocation(portNo),
"module '")
1690 <<
module.getName() << "' is in reset domain requiring port '"
1691 << domain.resetName.getValue() << "' to have type "
1692 << domain.resetType << ", but has type " << portType;
1693 diag.attachNote(domain.rootReset.getLoc()) <<
"reset domain rooted here";
1698 domain.existingPort = portNo;
1699 domain.localReset =
module.getArgument(portNo);
1708LogicalResult InferResetsPass::implementFullReset() {
1710 llvm::dbgs() <<
"\n";
1713 for (
auto &it : domains) {
1714 auto module = cast<FModuleOp>(it.first);
1715 auto &entries = it.second;
1719 if (!entries.empty())
1720 domain = entries.back().first;
1721 if (failed(implementFullReset(module, domain)))
1732LogicalResult InferResetsPass::implementFullReset(FModuleOp module,
1733 ResetDomain &domain) {
1738 SmallVector<FInstanceLike> instances;
1739 module.walk([&](FInstanceLike instOp) { instances.push_back(instOp); });
1741 if (!instances.empty())
1742 llvm::dbgs() <<
"Tie off instances in " << module.getName() <<
"\n";
1744 for (
auto instOp : instances)
1745 implementFullReset(instOp, module, Value());
1749 LLVM_DEBUG(llvm::dbgs() <<
"Implementing full reset for " << module.getName()
1753 auto *
context =
module.getContext();
1755 annotations.addAnnotations(DictionaryAttr::get(
1757 StringAttr::get(
context, fullResetAnnoClass))));
1758 annotations.applyToOperation(module);
1761 auto actualReset = domain.localReset;
1762 if (!domain.localReset) {
1763 PortInfo portInfo{domain.resetName,
1767 domain.rootReset.getLoc()};
1768 module.insertPorts({{0, portInfo}});
1769 actualReset =
module.getArgument(0);
1770 LLVM_DEBUG(llvm::dbgs() <<
"- Inserted port " << domain.resetName <<
"\n");
1774 llvm::dbgs() <<
"- Using ";
1775 if (
auto blockArg = dyn_cast<BlockArgument>(actualReset))
1776 llvm::dbgs() <<
"port #" << blockArg.getArgNumber() <<
" ";
1778 llvm::dbgs() <<
"wire/node ";
1784 SmallVector<Operation *> opsToUpdate;
1785 module.walk([&](Operation *op) {
1786 if (isa<FInstanceLike, RegOp, RegResetOp>(op))
1787 opsToUpdate.push_back(op);
1794 if (!isa<BlockArgument>(actualReset)) {
1795 mlir::DominanceInfo dom(module);
1800 auto *resetOp = actualReset.getDefiningOp();
1801 if (!opsToUpdate.empty() && !dom.dominates(resetOp, opsToUpdate[0])) {
1802 LLVM_DEBUG(llvm::dbgs()
1803 <<
"- Reset doesn't dominate all uses, needs to be moved\n");
1807 auto nodeOp = dyn_cast<NodeOp>(resetOp);
1808 if (nodeOp && !dom.dominates(nodeOp.getInput(), opsToUpdate[0])) {
1809 LLVM_DEBUG(llvm::dbgs()
1810 <<
"- Promoting node to wire for move: " << nodeOp <<
"\n");
1811 auto builder = ImplicitLocOpBuilder::atBlockBegin(nodeOp.getLoc(),
1812 nodeOp->getBlock());
1813 auto wireOp = WireOp::create(
1814 builder, nodeOp.getResult().getType(), nodeOp.getNameAttr(),
1815 nodeOp.getNameKindAttr(), nodeOp.getAnnotationsAttr(),
1816 nodeOp.getInnerSymAttr(), nodeOp.getForceableAttr());
1818 nodeOp->replaceAllUsesWith(wireOp);
1819 nodeOp->removeAttr(nodeOp.getInnerSymAttrName());
1823 nodeOp.setNameKind(NameKindEnum::DroppableName);
1824 nodeOp.setAnnotationsAttr(ArrayAttr::get(builder.getContext(), {}));
1825 builder.setInsertionPointAfter(nodeOp);
1826 emitConnect(builder, wireOp.getResult(), nodeOp.getResult());
1828 actualReset = wireOp.getResult();
1829 domain.localReset = wireOp.getResult();
1834 Block *targetBlock = dom.findNearestCommonDominator(
1835 resetOp->getBlock(), opsToUpdate[0]->getBlock());
1837 if (targetBlock != resetOp->getBlock())
1838 llvm::dbgs() <<
"- Needs to be moved to different block\n";
1847 auto getParentInBlock = [](Operation *op,
Block *block) {
1848 while (op && op->getBlock() != block)
1849 op = op->getParentOp();
1852 auto *resetOpInTarget = getParentInBlock(resetOp, targetBlock);
1853 auto *firstOpInTarget = getParentInBlock(opsToUpdate[0], targetBlock);
1859 if (resetOpInTarget->isBeforeInBlock(firstOpInTarget))
1860 resetOp->moveBefore(resetOpInTarget);
1862 resetOp->moveBefore(firstOpInTarget);
1867 for (
auto *op : opsToUpdate)
1868 implementFullReset(op, module, actualReset);
1875void InferResetsPass::implementFullReset(FInstanceLike inst,
1876 StringAttr moduleName,
1877 Value actualReset) {
1881 auto *node = instanceGraph->lookup(moduleName);
1882 auto refModule = dyn_cast<FModuleOp>(*node->
getModule());
1885 auto *domainIt = domains.find(refModule);
1886 if (domainIt == domains.end() || domainIt->second.empty())
1888 auto &domain = domainIt->second.back().first;
1889 assert(domain &&
"null domains should not be listed");
1891 ImplicitLocOpBuilder builder(inst.getLoc(), inst);
1893 LLVM_DEBUG(llvm::dbgs() << (actualReset ?
"- Update " :
"- Tie-off ")
1899 if (!domain.localReset) {
1900 LLVM_DEBUG(llvm::dbgs() <<
" - Adding new result as reset\n");
1901 auto newInstOp = inst.cloneWithInsertedPortsAndReplaceUses(
1903 {domain.resetName, domain.resetType, Direction::In}}});
1904 instReset = newInstOp->getResult(0);
1905 instanceGraph->replaceInstance(inst, newInstOp);
1908 }
else if (domain.existingPort.has_value()) {
1909 auto idx = *domain.existingPort;
1910 instReset = inst->getResult(idx);
1911 LLVM_DEBUG(llvm::dbgs() <<
" - Using result #" << idx <<
" as reset\n");
1920 builder.setInsertionPointAfter(inst);
1927 LLVM_DEBUG(llvm::dbgs() <<
" - Tying off reset to constant 0\n");
1928 if (type_isa<AsyncResetType>(domain.resetType))
1929 actualReset = SpecialConstantOp::create(builder, domain.resetType,
false);
1931 actualReset = ConstantOp::create(
1932 builder, UIntType::get(builder.getContext(), 1), APInt(1, 0));
1936 assert(instReset && actualReset);
1943void InferResetsPass::implementFullReset(Operation *op, FModuleOp module,
1944 Value actualReset) {
1945 ImplicitLocOpBuilder builder(op->getLoc(), op);
1948 if (
auto instOp = dyn_cast<FInstanceLike>(op))
1949 return implementFullReset(
1950 instOp, cast<StringAttr>(instOp.getReferencedModuleNamesAttr()[0]),
1958 if (
auto regOp = dyn_cast<RegOp>(op)) {
1959 LLVM_DEBUG(llvm::dbgs() <<
"- Adding full reset to " << regOp <<
"\n");
1961 auto newRegOp = RegResetOp::create(
1962 builder, regOp.getResult().getType(), regOp.getClockVal(), actualReset,
1963 zero, regOp.getNameAttr(), regOp.getNameKindAttr(),
1964 regOp.getAnnotations(), regOp.getInnerSymAttr(),
1965 regOp.getForceableAttr());
1966 regOp.getResult().replaceAllUsesWith(newRegOp.getResult());
1967 if (regOp.getForceable())
1968 regOp.getRef().replaceAllUsesWith(newRegOp.getRef());
1974 if (
auto regOp = dyn_cast<RegResetOp>(op)) {
1977 if (type_isa<AsyncResetType>(regOp.getResetSignal().getType()) ||
1978 type_isa<UIntType>(actualReset.getType())) {
1979 LLVM_DEBUG(llvm::dbgs() <<
"- Skipping (has reset) " << regOp <<
"\n");
1982 if (failed(regOp.verifyInvariants()))
1983 signalPassFailure();
1986 LLVM_DEBUG(llvm::dbgs() <<
"- Updating reset of " << regOp <<
"\n");
1988 auto reset = regOp.getResetSignal();
1989 auto value = regOp.getResetValue();
1995 builder.setInsertionPointAfterValue(regOp.getResult());
1996 auto mux = MuxPrimOp::create(builder, reset, value, regOp.getResult());
2000 builder.setInsertionPoint(regOp);
2002 regOp.getResetSignalMutable().assign(actualReset);
2003 regOp.getResetValueMutable().assign(zero);
2007LogicalResult InferResetsPass::verifyNoAbstractReset() {
2008 bool hasAbstractResetPorts =
false;
2009 for (FModuleLike module :
2010 getOperation().
getBodyBlock()->getOps<FModuleLike>()) {
2011 for (
PortInfo port : module.getPorts()) {
2012 if (getBaseOfType<ResetType>(port.type)) {
2013 auto diag = emitError(port.loc)
2014 <<
"a port \"" << port.getName()
2015 <<
"\" with abstract reset type was unable to be "
2016 "inferred by InferResets (is this a top-level port?)";
2017 diag.attachNote(module->getLoc())
2018 <<
"the module with this uninferred reset port was defined here";
2019 hasAbstractResetPorts =
true;
2024 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)