23#include "mlir/IR/Dominance.h"
24#include "mlir/IR/ImplicitLocOpBuilder.h"
25#include "mlir/IR/Threading.h"
26#include "mlir/Pass/Pass.h"
27#include "llvm/ADT/EquivalenceClasses.h"
28#include "llvm/ADT/SetVector.h"
29#include "llvm/ADT/TypeSwitch.h"
30#include "llvm/Support/Debug.h"
32#define DEBUG_TYPE "infer-resets"
36#define GEN_PASS_DEF_INFERRESETS
37#include "circt/Dialect/FIRRTL/Passes.h.inc"
41using circt::igraph::InstanceOpInterface;
44using llvm::BumpPtrAllocator;
46using llvm::SmallDenseSet;
47using llvm::SmallSetVector;
49using mlir::InferTypeOpInterface;
52using namespace firrtl;
61 if (
auto arg = dyn_cast<BlockArgument>(reset)) {
62 auto module = cast<FModuleOp>(arg.getParentRegion()->getParentOp());
63 return {
module.getPortNameAttr(arg.getArgNumber()), module};
65 auto *op = reset.getDefiningOp();
66 return {op->getAttrOfType<StringAttr>(
"name"),
67 op->getParentOfType<FModuleOp>()};
96 std::optional<unsigned> existingPort;
99 ResetDomain() =
default;
102 ResetDomain(Value rootReset)
103 : rootReset(rootReset), resetName(
getResetName(rootReset)),
104 resetType(rootReset.getType()) {}
107 explicit operator bool()
const {
return static_cast<bool>(rootReset); }
111inline bool operator==(
const ResetDomain &a,
const ResetDomain &b) {
112 return (a.isTop == b.isTop && a.resetName == b.resetName &&
113 a.resetType == b.resetType);
115inline bool operator!=(
const ResetDomain &a,
const ResetDomain &b) {
124 auto it = cache.find(type);
125 if (it != cache.end())
127 auto nullBit = [&]() {
129 builder, UIntType::get(builder.getContext(), 1,
true),
134 .
Case<ClockType>([&](
auto type) {
135 return AsClockPrimOp::create(builder, nullBit());
137 .Case<AsyncResetType>([&](
auto type) {
138 return AsAsyncResetPrimOp::create(builder, nullBit());
140 .Case<SIntType, UIntType>([&](
auto type) {
141 return ConstantOp::create(
142 builder, type, APInt::getZero(type.getWidth().value_or(1)));
144 .Case<FEnumType>([&](
auto type) -> Value {
147 if (type.getNumElements() != 0 &&
148 type.getElement(0).value.getValue().isZero()) {
149 const auto &element = type.getElement(0);
151 return FEnumCreateOp::create(builder, type, element.name, value);
153 auto value = ConstantOp::create(builder,
154 UIntType::get(builder.getContext(),
157 APInt::getZero(type.getBitWidth()));
158 return BitCastOp::create(builder, type, value);
160 .Case<BundleType>([&](
auto type) {
161 auto wireOp = WireOp::create(builder, type);
162 for (
unsigned i = 0, e = type.getNumElements(); i < e; ++i) {
163 auto fieldType = type.getElementTypePreservingConst(i);
166 SubfieldOp::create(builder, fieldType, wireOp.getResult(), i);
169 return wireOp.getResult();
171 .Case<FVectorType>([&](
auto type) {
172 auto wireOp = WireOp::create(builder, type);
174 builder, type.getElementTypePreservingConst(), cache);
175 for (
unsigned i = 0, e = type.getNumElements(); i < e; ++i) {
176 auto acc = SubindexOp::create(builder, zero.getType(),
177 wireOp.getResult(), i);
180 return wireOp.getResult();
182 .Case<ResetType, AnalogType>(
183 [&](
auto type) {
return InvalidValueOp::create(builder, type); })
185 llvm_unreachable(
"switch handles all types");
188 cache.insert({type, value});
205 Value reset, Value resetValue) {
209 bool resetValueUsed =
false;
211 for (
auto &use : target.getUses()) {
212 Operation *useOp = use.getOwner();
213 builder.setInsertionPoint(useOp);
214 TypeSwitch<Operation *>(useOp)
217 .Case<ConnectOp, MatchingConnectOp>([&](
auto op) {
218 if (op.getDest() != target)
220 LLVM_DEBUG(llvm::dbgs() <<
" - Insert mux into " << op <<
"\n");
222 MuxPrimOp::create(builder, reset, resetValue, op.getSrc());
223 op.getSrcMutable().assign(muxOp);
224 resetValueUsed =
true;
227 .Case<SubfieldOp>([&](
auto op) {
229 SubfieldOp::create(builder, resetValue, op.getFieldIndexAttr());
231 resetValueUsed =
true;
233 resetSubValue.erase();
236 .Case<SubindexOp>([&](
auto op) {
238 SubindexOp::create(builder, resetValue, op.getIndexAttr());
240 resetValueUsed =
true;
242 resetSubValue.erase();
245 .Case<SubaccessOp>([&](
auto op) {
246 if (op.getInput() != target)
249 SubaccessOp::create(builder, resetValue, op.getIndex());
251 resetValueUsed =
true;
253 resetSubValue.erase();
256 return resetValueUsed;
271 bool operator<(
const ResetSignal &other)
const {
return field < other.field; }
272 bool operator==(
const ResetSignal &other)
const {
273 return field == other.field;
275 bool operator!=(
const ResetSignal &other)
const {
return !(*
this == other); }
295using ResetDrives = SmallVector<ResetDrive, 1>;
298using ResetNetwork = llvm::iterator_range<
299 llvm::EquivalenceClasses<ResetSignal>::member_iterator>;
302enum class ResetKind { Async, Sync };
304static StringRef resetKindToStringRef(
const ResetKind &kind) {
306 case ResetKind::Async:
308 case ResetKind::Sync:
311 llvm_unreachable(
"unhandled reset kind");
327 static bool isEqual(
const ResetSignal &lhs,
const ResetSignal &rhs) {
336 case ResetKind::Async:
337 return os <<
"async";
338 case ResetKind::Sync:
448struct InferResetsPass
449 :
public circt::firrtl::impl::InferResetsBase<InferResetsPass> {
450 void runOnOperation()
override;
451 void runOnOperationInner();
454 using InferResetsBase::InferResetsBase;
455 InferResetsPass(
const InferResetsPass &other) : InferResetsBase(other) {}
460 void traceResets(CircuitOp circuit);
461 void traceResets(InstanceOp inst);
462 void traceResets(Value dst, Value src, Location loc);
463 void traceResets(Value value);
464 void traceResets(Type dstType, Value dst,
unsigned dstID, Type srcType,
465 Value src,
unsigned srcID, Location loc);
467 LogicalResult inferAndUpdateResets();
468 FailureOr<ResetKind> inferReset(ResetNetwork net);
469 LogicalResult updateReset(ResetNetwork net, ResetKind kind);
475 LogicalResult collectAnnos(CircuitOp circuit);
481 FailureOr<std::optional<Value>> collectAnnos(FModuleOp module);
483 LogicalResult buildDomains(CircuitOp circuit);
484 void buildDomains(FModuleOp module,
const InstancePath &instPath,
486 unsigned indent = 0);
488 LogicalResult determineImpl();
489 LogicalResult determineImpl(FModuleOp module, ResetDomain &domain);
491 LogicalResult implementFullReset();
492 LogicalResult implementFullReset(FModuleOp module, ResetDomain &domain);
493 void implementFullReset(Operation *op, FModuleOp module, Value actualReset);
495 LogicalResult verifyNoAbstractReset();
501 ResetNetwork getResetNetwork(ResetSignal signal) {
502 return llvm::make_range(resetClasses.findLeader(signal),
503 resetClasses.member_end());
507 ResetDrives &getResetDrives(ResetNetwork net) {
508 return resetDrives[*net.begin()];
513 ResetSignal guessRoot(ResetNetwork net);
514 ResetSignal guessRoot(ResetSignal signal) {
515 return guessRoot(getResetNetwork(signal));
522 llvm::EquivalenceClasses<ResetSignal> resetClasses;
525 DenseMap<ResetSignal, ResetDrives> resetDrives;
530 DenseMap<Operation *, Value> annotatedResets;
534 MapVector<FModuleOp, SmallVector<std::pair<ResetDomain, InstancePath>, 1>>
541 std::unique_ptr<InstancePathCache> instancePathCache;
545void InferResetsPass::runOnOperation() {
546 runOnOperationInner();
547 resetClasses = llvm::EquivalenceClasses<ResetSignal>();
549 annotatedResets.clear();
551 instancePathCache.reset(
nullptr);
552 markAnalysesPreserved<InstanceGraph>();
555void InferResetsPass::runOnOperationInner() {
556 instanceGraph = &getAnalysis<InstanceGraph>();
557 instancePathCache = std::make_unique<InstancePathCache>(*instanceGraph);
560 traceResets(getOperation());
563 if (failed(inferAndUpdateResets()))
564 return signalPassFailure();
567 if (failed(collectAnnos(getOperation())))
568 return signalPassFailure();
571 if (failed(buildDomains(getOperation())))
572 return signalPassFailure();
575 if (failed(determineImpl()))
576 return signalPassFailure();
579 if (failed(implementFullReset()))
580 return signalPassFailure();
583 if (failed(verifyNoAbstractReset()))
584 return signalPassFailure();
587ResetSignal InferResetsPass::guessRoot(ResetNetwork net) {
588 ResetDrives &drives = getResetDrives(net);
589 ResetSignal bestSignal = *net.begin();
590 unsigned bestNumDrives = -1;
592 for (
auto signal : net) {
594 if (isa_and_nonnull<InvalidValueOp>(
595 signal.field.getValue().getDefiningOp()))
600 unsigned numDrives = 0;
601 for (
auto &drive : drives)
602 if (drive.dst == signal)
608 if (numDrives < bestNumDrives) {
609 bestNumDrives = numDrives;
628 .
Case<BundleType>([](
auto type) {
630 for (
auto e : type.getElements())
635 [](
auto type) {
return getMaxFieldID(type.getElementType()) + 1; })
636 .Default([](
auto) {
return 0; });
640 assert(index < type.getNumElements());
642 for (
unsigned i = 0; i < index; ++i)
650 assert(type.getNumElements() &&
"Bundle must have >0 fields");
652 for (
const auto &e : llvm::enumerate(type.getElements())) {
654 if (fieldID < numSubfields)
656 fieldID -= numSubfields;
658 assert(
false &&
"field id outside bundle");
664 if (oldType.isGround()) {
670 if (
auto bundleType = type_dyn_cast<BundleType>(oldType)) {
678 if (
auto vectorType = type_dyn_cast<FVectorType>(oldType)) {
679 if (vectorType.getNumElements() == 0)
696 if (
auto arg = dyn_cast<BlockArgument>(value)) {
697 auto module = cast<FModuleOp>(arg.getOwner()->getParentOp());
698 string +=
module.getPortName(arg.getArgNumber());
702 auto *op = value.getDefiningOp();
703 return TypeSwitch<Operation *, bool>(op)
704 .Case<InstanceOp, MemOp>([&](
auto op) {
705 string += op.getName();
707 string += op.getPortName(cast<OpResult>(value).getResultNumber());
710 .Case<WireOp, NodeOp, RegOp, RegResetOp>([&](
auto op) {
711 string += op.getName();
714 .Default([](
auto) {
return false; });
718 SmallString<64> name;
723 auto type = value.getType();
726 if (
auto bundleType = type_dyn_cast<BundleType>(type)) {
729 auto &element = bundleType.getElements()[index];
732 string += element.name.getValue();
735 localID = localID -
getFieldID(bundleType, index);
736 }
else if (
auto vecType = type_dyn_cast<FVectorType>(type)) {
739 type = vecType.getElementType();
746 llvm_unreachable(
"unsupported type");
758 return TypeSwitch<Type, bool>(type)
760 return type.getRecursiveTypeProperties().hasUninferredReset;
762 .Default([](
auto) {
return false; });
769void InferResetsPass::traceResets(CircuitOp circuit) {
771 llvm::dbgs() <<
"\n";
772 debugHeader(
"Tracing uninferred resets") <<
"\n\n";
775 SmallVector<std::pair<FModuleOp, SmallVector<Operation *>>> moduleToOps;
777 for (
auto module : circuit.getOps<FModuleOp>())
778 moduleToOps.push_back({module, {}});
781 getAnalysis<hw::InnerSymbolTableCollection>()};
783 mlir::parallelForEach(circuit.getContext(), moduleToOps, [](
auto &e) {
784 e.first.walk([&](Operation *op) {
788 op->getResultTypes(),
789 [](mlir::Type type) { return typeContainsReset(type); }) ||
790 llvm::any_of(op->getOperandTypes(), typeContainsReset))
791 e.second.push_back(op);
795 for (
auto &[_, ops] : moduleToOps)
796 for (auto *op : ops) {
797 TypeSwitch<Operation *>(op)
798 .Case<FConnectLike>([&](
auto op) {
799 traceResets(op.getDest(), op.getSrc(), op.getLoc());
801 .Case<InstanceOp>([&](
auto op) { traceResets(op); })
802 .Case<RefSendOp>([&](
auto op) {
804 traceResets(op.getType().getType(), op.getResult(), 0,
805 op.getBase().getType().getPassiveType(), op.getBase(),
808 .Case<RefResolveOp>([&](
auto op) {
810 traceResets(op.getType(), op.getResult(), 0,
811 op.getRef().getType().getType(), op.getRef(), 0,
814 .Case<Forceable>([&](Forceable op) {
815 if (
auto node = dyn_cast<NodeOp>(op.getOperation()))
816 traceResets(node.getResult(), node.getInput(), node.getLoc());
818 if (op.isForceable())
819 traceResets(op.getDataType(), op.getData(), 0, op.getDataType(),
820 op.getDataRef(), 0, op.getLoc());
822 .Case<RWProbeOp>([&](RWProbeOp op) {
823 auto ist = irn.lookup(op.getTarget());
826 auto baseType = op.getType().getType();
827 traceResets(baseType, op.getResult(), 0, baseType.getPassiveType(),
828 ref.getValue(), ref.getFieldID(), op.getLoc());
830 .Case<UninferredResetCastOp, ConstCastOp, RefCastOp>([&](
auto op) {
831 traceResets(op.getResult(), op.getInput(), op.getLoc());
833 .Case<InvalidValueOp>([&](
auto op) {
842 auto type = op.getType();
845 LLVM_DEBUG(llvm::dbgs() <<
"Uniquify " << op <<
"\n");
846 ImplicitLocOpBuilder builder(op->getLoc(), op);
848 llvm::make_early_inc_range(
llvm::drop_begin(op->getUses()))) {
854 auto newOp = InvalidValueOp::create(builder, type);
859 .Case<SubfieldOp>([&](
auto op) {
862 BundleType bundleType = op.getInput().getType();
863 auto index = op.getFieldIndex();
864 traceResets(op.getType(), op.getResult(), 0,
865 bundleType.getElements()[index].type, op.getInput(),
869 .Case<SubindexOp, SubaccessOp>([&](
auto op) {
882 FVectorType vectorType = op.getInput().getType();
883 traceResets(op.getType(), op.getResult(), 0,
884 vectorType.getElementType(), op.getInput(),
888 .Case<RefSubOp>([&](RefSubOp op) {
890 auto aggType = op.getInput().getType().getType();
891 uint64_t fieldID = TypeSwitch<FIRRTLBaseType, uint64_t>(aggType)
892 .Case<FVectorType>([](
auto type) {
895 .Case<BundleType>([&](
auto type) {
898 traceResets(op.getType(), op.getResult(), 0,
899 op.getResult().getType(), op.getInput(), fieldID,
907void InferResetsPass::traceResets(InstanceOp inst) {
909 auto module = inst.getReferencedModule<FModuleOp>(*instanceGraph);
912 LLVM_DEBUG(llvm::dbgs() <<
"Visiting instance " << inst.getName() <<
"\n");
915 for (
const auto &it :
llvm::enumerate(inst.getResults())) {
916 auto dir =
module.getPortDirection(it.index());
917 Value dstPort =
module.getArgument(it.index());
918 Value srcPort = it.value();
919 if (dir == Direction::Out)
920 std::swap(dstPort, srcPort);
921 traceResets(dstPort, srcPort, it.value().getLoc());
927void InferResetsPass::traceResets(Value dst, Value src, Location loc) {
929 traceResets(dst.getType(), dst, 0, src.getType(), src, 0, loc);
934void InferResetsPass::traceResets(Type dstType, Value dst,
unsigned dstID,
935 Type srcType, Value src,
unsigned srcID,
937 if (
auto dstBundle = type_dyn_cast<BundleType>(dstType)) {
938 auto srcBundle = type_cast<BundleType>(srcType);
939 for (
unsigned dstIdx = 0, e = dstBundle.getNumElements(); dstIdx < e;
941 auto dstField = dstBundle.getElements()[dstIdx].name;
942 auto srcIdx = srcBundle.getElementIndex(dstField);
945 auto &dstElt = dstBundle.getElements()[dstIdx];
946 auto &srcElt = srcBundle.getElements()[*srcIdx];
948 traceResets(srcElt.type, src, srcID +
getFieldID(srcBundle, *srcIdx),
949 dstElt.type, dst, dstID +
getFieldID(dstBundle, dstIdx),
952 traceResets(dstElt.type, dst, dstID +
getFieldID(dstBundle, dstIdx),
953 srcElt.type, src, srcID +
getFieldID(srcBundle, *srcIdx),
960 if (
auto dstVector = type_dyn_cast<FVectorType>(dstType)) {
961 auto srcVector = type_cast<FVectorType>(srcType);
962 auto srcElType = srcVector.getElementType();
963 auto dstElType = dstVector.getElementType();
976 traceResets(dstElType, dst, dstID +
getFieldID(dstVector), srcElType, src,
982 if (
auto dstRef = type_dyn_cast<RefType>(dstType)) {
983 auto srcRef = type_cast<RefType>(srcType);
984 return traceResets(dstRef.getType(), dst, dstID, srcRef.getType(), src,
989 auto dstBase = type_dyn_cast<FIRRTLBaseType>(dstType);
990 auto srcBase = type_dyn_cast<FIRRTLBaseType>(srcType);
991 if (!dstBase || !srcBase)
993 if (!type_isa<ResetType>(dstBase) && !type_isa<ResetType>(srcBase))
998 LLVM_DEBUG(llvm::dbgs() <<
"Visiting driver '" << dstField <<
"' = '"
999 << srcField <<
"' (" << dstType <<
" = " << srcType
1005 ResetSignal dstLeader =
1006 *resetClasses.findLeader(resetClasses.insert({dstField, dstBase}));
1007 ResetSignal srcLeader =
1008 *resetClasses.findLeader(resetClasses.insert({srcField, srcBase}));
1011 ResetSignal unionLeader = *resetClasses.unionSets(dstLeader, srcLeader);
1012 assert(unionLeader == dstLeader || unionLeader == srcLeader);
1017 if (dstLeader != srcLeader) {
1018 auto &unionDrives = resetDrives[unionLeader];
1019 auto mergedDrivesIt =
1020 resetDrives.find(unionLeader == dstLeader ? srcLeader : dstLeader);
1021 if (mergedDrivesIt != resetDrives.end()) {
1022 unionDrives.append(mergedDrivesIt->second);
1023 resetDrives.erase(mergedDrivesIt);
1029 resetDrives[unionLeader].push_back(
1030 {{dstField, dstBase}, {srcField, srcBase}, loc});
1037LogicalResult InferResetsPass::inferAndUpdateResets() {
1039 llvm::dbgs() <<
"\n";
1042 for (
const auto &it : resetClasses) {
1043 if (!it->isLeader())
1045 ResetNetwork net = resetClasses.members(*it);
1048 auto kind = inferReset(net);
1053 if (failed(updateReset(net, *kind)))
1059FailureOr<ResetKind> InferResetsPass::inferReset(ResetNetwork net) {
1060 LLVM_DEBUG(llvm::dbgs() <<
"Inferring reset network with "
1061 << std::distance(net.begin(), net.end())
1065 unsigned asyncDrives = 0;
1066 unsigned syncDrives = 0;
1067 unsigned invalidDrives = 0;
1068 for (ResetSignal signal : net) {
1070 if (type_isa<AsyncResetType>(signal.type))
1072 else if (type_isa<UIntType>(signal.type))
1075 isa_and_nonnull<InvalidValueOp>(
1076 signal.field.getValue().getDefiningOp()))
1079 LLVM_DEBUG(llvm::dbgs() <<
"- Found " << asyncDrives <<
" async, "
1080 << syncDrives <<
" sync, " << invalidDrives
1081 <<
" invalid drives\n");
1084 if (asyncDrives == 0 && syncDrives == 0 && invalidDrives == 0) {
1085 ResetSignal root = guessRoot(net);
1086 auto diag = mlir::emitError(root.field.getValue().getLoc())
1087 <<
"reset network never driven with concrete type";
1088 for (ResetSignal signal : net)
1089 diag.attachNote(signal.field.
getLoc()) <<
"here: ";
1094 if (asyncDrives > 0 && syncDrives > 0) {
1095 ResetSignal root = guessRoot(net);
1096 bool majorityAsync = asyncDrives >= syncDrives;
1097 auto diag = mlir::emitError(root.field.getValue().getLoc())
1099 SmallString<32> fieldName;
1101 diag <<
" \"" << fieldName <<
"\"";
1102 diag <<
" simultaneously connected to async and sync resets";
1103 diag.attachNote(root.field.getValue().getLoc())
1104 <<
"majority of connections to this reset are "
1105 << (majorityAsync ?
"async" :
"sync");
1106 for (
auto &drive : getResetDrives(net)) {
1107 if ((type_isa<AsyncResetType>(drive.dst.type) && !majorityAsync) ||
1108 (type_isa<AsyncResetType>(drive.src.type) && !majorityAsync) ||
1109 (type_isa<UIntType>(drive.dst.type) && majorityAsync) ||
1110 (type_isa<UIntType>(drive.src.type) && majorityAsync))
1111 diag.attachNote(drive.loc)
1112 << (type_isa<AsyncResetType>(drive.src.type) ?
"async" :
"sync")
1121 auto kind = (asyncDrives ? ResetKind::Async : ResetKind::Sync);
1122 LLVM_DEBUG(llvm::dbgs() <<
"- Inferred as " << kind <<
"\n");
1130LogicalResult InferResetsPass::updateReset(ResetNetwork net, ResetKind kind) {
1131 LLVM_DEBUG(llvm::dbgs() <<
"Updating reset network with "
1132 << std::distance(net.begin(), net.end())
1133 <<
" nodes to " << kind <<
"\n");
1137 if (kind == ResetKind::Async)
1138 resetType = AsyncResetType::get(&getContext());
1140 resetType = UIntType::get(&getContext(), 1);
1145 SmallSetVector<Operation *, 16> worklist;
1146 SmallDenseSet<Operation *> moduleWorklist;
1147 SmallDenseSet<std::pair<Operation *, Operation *>> extmoduleWorklist;
1148 for (
auto signal : net) {
1149 Value value = signal.field.getValue();
1150 if (!isa<BlockArgument>(value) &&
1151 !isa_and_nonnull<WireOp, RegOp, RegResetOp, InstanceOp, InvalidValueOp,
1152 ConstCastOp, RefCastOp, UninferredResetCastOp,
1153 RWProbeOp, AsResetPrimOp>(value.getDefiningOp()))
1155 if (updateReset(signal.field, resetType)) {
1156 for (
auto user : value.getUsers())
1157 worklist.insert(user);
1158 if (
auto blockArg = dyn_cast<BlockArgument>(value))
1159 moduleWorklist.insert(blockArg.getOwner()->getParentOp());
1160 else if (
auto instOp = value.getDefiningOp<InstanceOp>()) {
1161 if (
auto extmodule =
1162 instOp.getReferencedModule<FExtModuleOp>(*instanceGraph))
1163 extmoduleWorklist.insert({extmodule, instOp});
1164 }
else if (
auto uncast = value.getDefiningOp<UninferredResetCastOp>()) {
1165 uncast.replaceAllUsesWith(uncast.getInput());
1167 }
else if (
auto asResetOp = value.getDefiningOp<AsResetPrimOp>()) {
1170 Value result = asResetOp.getInput();
1171 if (type_isa<AsyncResetType>(resetType)) {
1172 ImplicitLocOpBuilder builder(asResetOp.getLoc(), asResetOp);
1173 result = AsAsyncResetPrimOp::create(builder, asResetOp.getInput());
1175 asResetOp.replaceAllUsesWith(result);
1185 while (!worklist.empty()) {
1186 auto *wop = worklist.pop_back_val();
1187 SmallVector<Type, 2> types;
1188 if (
auto op = dyn_cast<InferTypeOpInterface>(wop)) {
1190 SmallVector<Type, 2> types;
1191 if (failed(op.inferReturnTypes(op->getContext(), op->getLoc(),
1192 op->getOperands(), op->getAttrDictionary(),
1193 op->getPropertiesStorage(),
1194 op->getRegions(), types)))
1199 for (
auto it :
llvm::zip(op->getResults(), types)) {
1200 auto newType = std::get<1>(it);
1201 if (std::get<0>(it).getType() == newType)
1203 std::get<0>(it).setType(newType);
1204 for (
auto *user : std::
get<0>(it).getUsers())
1205 worklist.insert(user);
1207 LLVM_DEBUG(llvm::dbgs() <<
"- Inferred " << *op <<
"\n");
1208 }
else if (
auto uop = dyn_cast<UninferredResetCastOp>(wop)) {
1209 for (
auto *user : uop.getResult().getUsers())
1210 worklist.insert(user);
1211 uop.replaceAllUsesWith(uop.getInput());
1212 LLVM_DEBUG(llvm::dbgs() <<
"- Inferred " << uop <<
"\n");
1218 for (
auto *op : moduleWorklist) {
1219 auto module = dyn_cast<FModuleOp>(op);
1223 SmallVector<Attribute> argTypes;
1224 argTypes.reserve(module.getNumPorts());
1225 for (
auto arg : module.getArguments())
1226 argTypes.push_back(TypeAttr::
get(arg.getType()));
1228 module.setPortTypesAttr(ArrayAttr::get(op->getContext(), argTypes));
1229 LLVM_DEBUG(llvm::dbgs()
1230 <<
"- Updated type of module '" << module.getName() <<
"'\n");
1234 for (
auto pair : extmoduleWorklist) {
1235 auto module = cast<FExtModuleOp>(pair.first);
1236 auto instOp = cast<InstanceOp>(pair.second);
1238 SmallVector<Attribute> types;
1239 for (
auto type : instOp.getResultTypes())
1240 types.push_back(TypeAttr::
get(type));
1242 module.setPortTypesAttr(ArrayAttr::get(module->getContext(), types));
1243 LLVM_DEBUG(llvm::dbgs()
1244 <<
"- Updated type of extmodule '" << module.getName() <<
"'\n");
1254 if (oldType.isGround()) {
1260 if (
auto bundleType = type_dyn_cast<BundleType>(oldType)) {
1262 SmallVector<BundleType::BundleElement> fields(bundleType.begin(),
1265 fields[index].type, fieldID -
getFieldID(bundleType, index), fieldType);
1266 return BundleType::get(oldType.getContext(), fields, bundleType.
isConst());
1270 if (
auto vectorType = type_dyn_cast<FVectorType>(oldType)) {
1271 auto newType =
updateType(vectorType.getElementType(),
1272 fieldID -
getFieldID(vectorType), fieldType);
1273 return FVectorType::get(newType, vectorType.getNumElements(),
1274 vectorType.isConst());
1277 llvm_unreachable(
"unknown aggregate type");
1284 auto oldType = type_cast<FIRRTLType>(field.
getValue().getType());
1290 if (oldType == newType)
1292 LLVM_DEBUG(llvm::dbgs() <<
"- Updating '" << field <<
"' from " << oldType
1293 <<
" to " << newType <<
"\n");
1302LogicalResult InferResetsPass::collectAnnos(CircuitOp circuit) {
1304 llvm::dbgs() <<
"\n";
1305 debugHeader(
"Gather reset annotations") <<
"\n\n";
1307 SmallVector<std::pair<FModuleOp, std::optional<Value>>> results;
1308 for (
auto module : circuit.getOps<FModuleOp>())
1309 results.push_back({module, {}});
1311 if (failed(mlir::failableParallelForEach(
1312 circuit.getContext(), results, [&](
auto &moduleAndResult) {
1313 auto result = collectAnnos(moduleAndResult.first);
1316 moduleAndResult.second = *result;
1321 for (
auto [module, reset] : results)
1322 if (reset.has_value())
1323 annotatedResets.insert({module, *reset});
1327FailureOr<std::optional<Value>>
1328InferResetsPass::collectAnnos(FModuleOp module) {
1329 bool anyFailed =
false;
1330 SmallSetVector<std::pair<Annotation, Location>, 4> conflictingAnnos;
1334 bool ignore =
false;
1336 if (anno.
isClass(excludeFromFullResetAnnoClass)) {
1338 conflictingAnnos.insert({anno, module.getLoc()});
1341 if (anno.
isClass(fullResetAnnoClass)) {
1343 module.emitError("''FullResetAnnotation' cannot target module; must
"
1344 "target port or wire/node instead
");
1352 // Consume any reset annotations on module ports.
1354 // Helper for checking annotations and determining the reset
1355 auto checkAnnotations = [&](Annotation anno, Value arg) {
1356 if (anno.isClass(fullResetAnnoClass)) {
1357 ResetKind expectedResetKind;
1358 if (auto rt = anno.getMember<StringAttr>("resetType
")) {
1360 expectedResetKind = ResetKind::Sync;
1361 } else if (rt == "async
") {
1362 expectedResetKind = ResetKind::Async;
1364 mlir::emitError(arg.getLoc(),
1365 "'FullResetAnnotation' requires resetType ==
'sync' "
1366 "|
'async', but got resetType ==
")
1372 mlir::emitError(arg.getLoc(),
1373 "'FullResetAnnotation' requires resetType ==
"
1374 "'sync' |
'async', but got no resetType
");
1378 // Check that the type is well-formed
1379 bool isAsync = expectedResetKind == ResetKind::Async;
1380 bool validUint = false;
1381 if (auto uintT = dyn_cast<UIntType>(arg.getType()))
1382 validUint = uintT.getWidth() == 1;
1383 if ((isAsync && !isa<AsyncResetType>(arg.getType())) ||
1384 (!isAsync && !validUint)) {
1385 auto kind = resetKindToStringRef(expectedResetKind);
1386 mlir::emitError(arg.getLoc(),
1387 "'FullResetAnnotation' with resetType ==
'")
1388 << kind << "' must target
" << kind << " reset, but targets
"
1395 conflictingAnnos.insert({anno, reset.getLoc()});
1399 if (anno.isClass(excludeFromFullResetAnnoClass)) {
1401 mlir::emitError(arg.getLoc(),
1402 "'ExcludeFromFullResetAnnotation' cannot
"
1403 "target port/wire/node; must target
module instead");
1411 Value arg =
module.getArgument(argNum);
1412 return checkAnnotations(anno, arg);
1418 module.getBody().walk([&](Operation *op) {
1420 if (!isa<WireOp, NodeOp>(op)) {
1421 if (AnnotationSet::hasAnnotation(op, fullResetAnnoClass,
1422 excludeFromFullResetAnnoClass)) {
1425 "reset annotations must target module, port, or wire/node");
1433 auto arg = op->getResult(0);
1434 return checkAnnotations(anno, arg);
1443 if (!ignore && !reset) {
1444 LLVM_DEBUG(llvm::dbgs()
1445 <<
"No reset annotation for " << module.getName() <<
"\n");
1446 return std::optional<Value>();
1450 if (conflictingAnnos.size() > 1) {
1451 auto diag =
module.emitError("multiple reset annotations on module '")
1452 << module.getName() << "'";
1453 for (
auto &annoAndLoc : conflictingAnnos)
1454 diag.attachNote(annoAndLoc.second)
1455 <<
"conflicting " << annoAndLoc.first.getClassAttr() <<
":";
1461 llvm::dbgs() <<
"Annotated reset for " <<
module.getName() << ": ";
1463 llvm::dbgs() <<
"no domain\n";
1464 else if (
auto arg = dyn_cast<BlockArgument>(reset))
1465 llvm::dbgs() <<
"port " <<
module.getPortName(arg.getArgNumber()) << "\n";
1467 llvm::dbgs() <<
"wire "
1468 << reset.getDefiningOp()->getAttrOfType<StringAttr>(
"name")
1474 return std::optional<Value>(reset);
1486LogicalResult InferResetsPass::buildDomains(CircuitOp circuit) {
1488 llvm::dbgs() <<
"\n";
1489 debugHeader(
"Build full reset domains") <<
"\n\n";
1493 auto &instGraph = getAnalysis<InstanceGraph>();
1499 dyn_cast_or_null<FModuleOp>(node.
getModule().getOperation()))
1500 buildDomains(module,
InstancePath{}, Value{}, instGraph);
1504 bool anyFailed =
false;
1505 for (
auto &it : domains) {
1506 auto module = cast<FModuleOp>(it.first);
1507 auto &domainConflicts = it.second;
1508 if (domainConflicts.size() <= 1)
1512 SmallDenseSet<Value> printedDomainResets;
1513 auto diag =
module.emitError("module '")
1515 << "' instantiated in different reset domains";
1516 for (
auto &it : domainConflicts) {
1517 ResetDomain &domain = it.first;
1518 const auto &path = it.second;
1519 auto inst = path.leaf();
1520 auto loc = path.empty() ?
module.getLoc() : inst.getLoc();
1521 auto ¬e = diag.attachNote(loc);
1525 note <<
"root instance";
1527 note <<
"instance '";
1530 [&](InstanceOpInterface inst) { note << inst.getInstanceName(); },
1531 [&]() { note <<
"/"; });
1537 if (domain.rootReset) {
1539 note <<
" reset domain rooted at '" << nameAndModule.first.getValue()
1540 <<
"' of module '" << nameAndModule.second.getName() <<
"'";
1543 if (printedDomainResets.insert(domain.rootReset).second) {
1544 diag.attachNote(domain.rootReset.getLoc())
1545 <<
"reset domain '" << nameAndModule.first.getValue()
1546 <<
"' of module '" << nameAndModule.second.getName()
1547 <<
"' declared here:";
1550 note <<
" no reset domain";
1553 return failure(anyFailed);
1556void InferResetsPass::buildDomains(FModuleOp module,
1561 llvm::dbgs().indent(indent * 2) <<
"Visiting ";
1562 if (instPath.
empty())
1563 llvm::dbgs() <<
"$root";
1565 llvm::dbgs() << instPath.
leaf().getInstanceName();
1566 llvm::dbgs() <<
" (" <<
module.getName() << ")\n";
1571 auto it = annotatedResets.find(module);
1572 if (it != annotatedResets.end()) {
1575 if (
auto localReset = it->second)
1576 domain = ResetDomain(localReset);
1577 domain.isTop =
true;
1578 }
else if (parentReset) {
1580 domain = ResetDomain(parentReset);
1587 auto &entries = domains[module];
1588 if (domain.rootReset)
1589 if (llvm::all_of(entries,
1590 [&](
const auto &entry) {
return entry.first != domain; }))
1591 entries.push_back({domain, instPath});
1594 for (
auto *record : *instGraph[module]) {
1595 auto submodule = dyn_cast<FModuleOp>(*record->getTarget()->getModule());
1599 instancePathCache->appendInstance(instPath, record->getInstance());
1600 buildDomains(submodule, childPath, domain.rootReset, instGraph, indent + 1);
1605LogicalResult InferResetsPass::determineImpl() {
1606 auto anyFailed =
false;
1608 llvm::dbgs() <<
"\n";
1609 debugHeader(
"Determine implementation") <<
"\n\n";
1611 for (
auto &it : domains) {
1612 auto module = cast<FModuleOp>(it.first);
1613 auto &entries = it.second;
1615 if (entries.empty())
1617 auto &domain = entries.back().first;
1618 if (failed(determineImpl(module, domain)))
1621 return failure(anyFailed);
1639LogicalResult InferResetsPass::determineImpl(FModuleOp module,
1640 ResetDomain &domain) {
1644 LLVM_DEBUG(llvm::dbgs() <<
"Planning reset for " << module.getName() <<
"\n");
1649 LLVM_DEBUG(llvm::dbgs()
1650 <<
"- Rooting at local value " << domain.resetName <<
"\n");
1651 domain.localReset = domain.rootReset;
1652 if (
auto blockArg = dyn_cast<BlockArgument>(domain.rootReset))
1653 domain.existingPort = blockArg.getArgNumber();
1659 auto neededName = domain.resetName;
1660 auto neededType = domain.resetType;
1661 LLVM_DEBUG(llvm::dbgs() <<
"- Looking for existing port " << neededName
1663 auto portNames =
module.getPortNames();
1664 auto *portIt = llvm::find(portNames, neededName);
1667 if (portIt == portNames.end()) {
1668 LLVM_DEBUG(llvm::dbgs() <<
"- Creating new port " << neededName <<
"\n");
1669 domain.resetName = neededName;
1673 LLVM_DEBUG(llvm::dbgs() <<
"- Reusing existing port " << neededName <<
"\n");
1676 auto portNo = std::distance(portNames.begin(), portIt);
1677 auto portType =
module.getPortType(portNo);
1678 if (portType != neededType) {
1679 auto diag = emitError(module.getPortLocation(portNo),
"module '")
1680 <<
module.getName() << "' is in reset domain requiring port '"
1681 << domain.resetName.getValue() << "' to have type "
1682 << domain.resetType << ", but has type " << portType;
1683 diag.attachNote(domain.rootReset.getLoc()) <<
"reset domain rooted here";
1688 domain.existingPort = portNo;
1689 domain.localReset =
module.getArgument(portNo);
1698LogicalResult InferResetsPass::implementFullReset() {
1700 llvm::dbgs() <<
"\n";
1703 for (
auto &it : domains) {
1704 auto module = cast<FModuleOp>(it.first);
1705 auto &entries = it.second;
1709 if (!entries.empty())
1710 domain = entries.back().first;
1711 if (failed(implementFullReset(module, domain)))
1722LogicalResult InferResetsPass::implementFullReset(FModuleOp module,
1723 ResetDomain &domain) {
1728 SmallVector<InstanceOp> instances;
1729 module.walk([&](InstanceOp instOp) { instances.push_back(instOp); });
1731 if (!instances.empty())
1732 llvm::dbgs() <<
"Tie off instances in " << module.getName() <<
"\n";
1734 for (
auto instOp : instances)
1735 implementFullReset(instOp, module, Value());
1739 LLVM_DEBUG(llvm::dbgs() <<
"Implementing full reset for " << module.getName()
1743 auto *
context =
module.getContext();
1745 annotations.addAnnotations(DictionaryAttr::get(
1747 StringAttr::get(
context, fullResetAnnoClass))));
1748 annotations.applyToOperation(module);
1751 auto actualReset = domain.localReset;
1752 if (!domain.localReset) {
1753 PortInfo portInfo{domain.resetName,
1757 domain.rootReset.getLoc()};
1758 module.insertPorts({{0, portInfo}});
1759 actualReset =
module.getArgument(0);
1760 LLVM_DEBUG(llvm::dbgs() <<
"- Inserted port " << domain.resetName <<
"\n");
1764 llvm::dbgs() <<
"- Using ";
1765 if (
auto blockArg = dyn_cast<BlockArgument>(actualReset))
1766 llvm::dbgs() <<
"port #" << blockArg.getArgNumber() <<
" ";
1768 llvm::dbgs() <<
"wire/node ";
1774 SmallVector<Operation *> opsToUpdate;
1775 module.walk([&](Operation *op) {
1776 if (isa<InstanceOp, RegOp, RegResetOp>(op))
1777 opsToUpdate.push_back(op);
1784 if (!isa<BlockArgument>(actualReset)) {
1785 mlir::DominanceInfo dom(module);
1790 auto *resetOp = actualReset.getDefiningOp();
1791 if (!opsToUpdate.empty() && !dom.dominates(resetOp, opsToUpdate[0])) {
1792 LLVM_DEBUG(llvm::dbgs()
1793 <<
"- Reset doesn't dominate all uses, needs to be moved\n");
1797 auto nodeOp = dyn_cast<NodeOp>(resetOp);
1798 if (nodeOp && !dom.dominates(nodeOp.getInput(), opsToUpdate[0])) {
1799 LLVM_DEBUG(llvm::dbgs()
1800 <<
"- Promoting node to wire for move: " << nodeOp <<
"\n");
1801 auto builder = ImplicitLocOpBuilder::atBlockBegin(nodeOp.getLoc(),
1802 nodeOp->getBlock());
1803 auto wireOp = WireOp::create(
1804 builder, nodeOp.getResult().getType(), nodeOp.getNameAttr(),
1805 nodeOp.getNameKindAttr(), nodeOp.getAnnotationsAttr(),
1806 nodeOp.getInnerSymAttr(), nodeOp.getForceableAttr());
1808 nodeOp->replaceAllUsesWith(wireOp);
1809 nodeOp->removeAttr(nodeOp.getInnerSymAttrName());
1813 nodeOp.setNameKind(NameKindEnum::DroppableName);
1814 nodeOp.setAnnotationsAttr(ArrayAttr::get(builder.getContext(), {}));
1815 builder.setInsertionPointAfter(nodeOp);
1816 emitConnect(builder, wireOp.getResult(), nodeOp.getResult());
1818 actualReset = wireOp.getResult();
1819 domain.localReset = wireOp.getResult();
1824 Block *targetBlock = dom.findNearestCommonDominator(
1825 resetOp->getBlock(), opsToUpdate[0]->getBlock());
1827 if (targetBlock != resetOp->getBlock())
1828 llvm::dbgs() <<
"- Needs to be moved to different block\n";
1837 auto getParentInBlock = [](Operation *op,
Block *block) {
1838 while (op && op->getBlock() != block)
1839 op = op->getParentOp();
1842 auto *resetOpInTarget = getParentInBlock(resetOp, targetBlock);
1843 auto *firstOpInTarget = getParentInBlock(opsToUpdate[0], targetBlock);
1849 if (resetOpInTarget->isBeforeInBlock(firstOpInTarget))
1850 resetOp->moveBefore(resetOpInTarget);
1852 resetOp->moveBefore(firstOpInTarget);
1857 for (
auto *op : opsToUpdate)
1858 implementFullReset(op, module, actualReset);
1866void InferResetsPass::implementFullReset(Operation *op, FModuleOp module,
1867 Value actualReset) {
1868 ImplicitLocOpBuilder builder(op->getLoc(), op);
1871 if (
auto instOp = dyn_cast<InstanceOp>(op)) {
1875 auto refModule = instOp.getReferencedModule<FModuleOp>(*instanceGraph);
1878 auto domainIt = domains.find(refModule);
1879 if (domainIt == domains.end() || domainIt->second.empty())
1881 auto &domain = domainIt->second.back().first;
1882 assert(domain &&
"null domains should not be listed");
1883 LLVM_DEBUG(llvm::dbgs()
1884 << (actualReset ?
"- Update instance '" :
"- Tie-off instance '")
1885 << instOp.
getName() <<
"'\n");
1889 if (!domain.localReset) {
1890 LLVM_DEBUG(llvm::dbgs() <<
" - Adding new result as reset\n");
1891 auto newInstOp = instOp.cloneWithInsertedPortsAndReplaceUses(
1893 {domain.resetName, domain.resetType, Direction::In}}});
1894 instReset = newInstOp.getResult(0);
1895 instanceGraph->replaceInstance(instOp, newInstOp);
1898 }
else if (domain.existingPort.has_value()) {
1899 auto idx = *domain.existingPort;
1900 instReset = instOp.getResult(idx);
1901 LLVM_DEBUG(llvm::dbgs() <<
" - Using result #" << idx <<
" as reset\n");
1910 builder.setInsertionPointAfter(instOp);
1917 LLVM_DEBUG(llvm::dbgs() <<
" - Tying off reset to constant 0\n");
1918 if (type_isa<AsyncResetType>(domain.resetType))
1920 SpecialConstantOp::create(builder, domain.resetType,
false);
1922 actualReset = ConstantOp::create(
1923 builder, UIntType::get(builder.getContext(), 1), APInt(1, 0));
1927 assert(instReset && actualReset);
1937 if (
auto regOp = dyn_cast<RegOp>(op)) {
1938 LLVM_DEBUG(llvm::dbgs() <<
"- Adding full reset to " << regOp <<
"\n");
1940 auto newRegOp = RegResetOp::create(
1941 builder, regOp.getResult().getType(), regOp.getClockVal(), actualReset,
1942 zero, regOp.getNameAttr(), regOp.getNameKindAttr(),
1943 regOp.getAnnotations(), regOp.getInnerSymAttr(),
1944 regOp.getForceableAttr());
1945 regOp.getResult().replaceAllUsesWith(newRegOp.getResult());
1946 if (regOp.getForceable())
1947 regOp.getRef().replaceAllUsesWith(newRegOp.getRef());
1953 if (
auto regOp = dyn_cast<RegResetOp>(op)) {
1956 if (type_isa<AsyncResetType>(regOp.getResetSignal().getType()) ||
1957 type_isa<UIntType>(actualReset.getType())) {
1958 LLVM_DEBUG(llvm::dbgs() <<
"- Skipping (has reset) " << regOp <<
"\n");
1961 if (failed(regOp.verifyInvariants()))
1962 signalPassFailure();
1965 LLVM_DEBUG(llvm::dbgs() <<
"- Updating reset of " << regOp <<
"\n");
1967 auto reset = regOp.getResetSignal();
1968 auto value = regOp.getResetValue();
1974 builder.setInsertionPointAfterValue(regOp.getResult());
1975 auto mux = MuxPrimOp::create(builder, reset, value, regOp.getResult());
1979 builder.setInsertionPoint(regOp);
1981 regOp.getResetSignalMutable().assign(actualReset);
1982 regOp.getResetValueMutable().assign(zero);
1986LogicalResult InferResetsPass::verifyNoAbstractReset() {
1987 bool hasAbstractResetPorts =
false;
1988 for (FModuleLike module :
1989 getOperation().
getBodyBlock()->getOps<FModuleLike>()) {
1990 for (
PortInfo port : module.getPorts()) {
1991 if (getBaseOfType<ResetType>(port.type)) {
1992 auto diag = emitError(port.loc)
1993 <<
"a port \"" << port.getName()
1994 <<
"\" with abstract reset type was unable to be "
1995 "inferred by InferResets (is this a top-level port?)";
1996 diag.attachNote(module->getLoc())
1997 <<
"the module with this uninferred reset port was defined here";
1998 hasAbstractResetPorts =
true;
2003 if (hasAbstractResetPorts)
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static Value createZeroValue(ImplicitLocOpBuilder &builder, FIRRTLBaseType type, SmallDenseMap< FIRRTLBaseType, Value > &cache)
Construct a zero value of the given type using the given builder.
static unsigned getFieldID(BundleType type, unsigned index)
static unsigned getIndexForFieldID(BundleType type, unsigned fieldID)
static FIRRTLBaseType updateType(FIRRTLBaseType oldType, unsigned fieldID, FIRRTLBaseType fieldType)
Update the type of a single field within a type.
static bool isUselessVec(FIRRTLBaseType oldType, unsigned fieldID)
static StringAttr getResetName(Value reset)
Return the name of a reset.
static bool insertResetMux(ImplicitLocOpBuilder &builder, Value target, Value reset, Value resetValue)
Helper function that inserts reset multiplexer into all ConnectOps with the given target.
static bool typeContainsReset(Type type)
Check whether a type contains a ResetType.
static bool getDeclName(Value value, SmallString< 32 > &string)
static unsigned getMaxFieldID(FIRRTLBaseType type)
static std::pair< StringAttr, FModuleOp > getResetNameAndModule(Value reset)
Return the name and parent module of a reset.
static Location getLoc(DefSlot slot)
static Block * getBodyBlock(FModuleLike mod)
static InstancePath empty
This class represents a reference to a specific field or element of an aggregate value.
unsigned getFieldID() const
Get the field ID of this FieldRef, which is a unique identifier mapped to a specific field in a bundl...
Value getValue() const
Get the Value which created this location.
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
static bool removePortAnnotations(Operation *module, llvm::function_ref< bool(unsigned, Annotation)> predicate)
Remove all port annotations from a module or extmodule for which predicate returns true.
This class provides a read-only projection of an annotation.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
FIRRTLBaseType getConstType(bool isConst) const
Return a 'const' or non-'const' version of this type.
bool isConst() const
Returns true if this is a 'const' type that can only hold compile-time constant values.
This class implements the same functionality as TypeSwitch except that it uses firrtl::type_dyn_cast ...
FIRRTLTypeSwitch< T, ResultT > & Case(CallableT &&caseFn)
Add a case on the given type.
This graph tracks modules and where they are instantiated.
This is a Node in the InstanceGraph.
bool noUses()
Return true if there are no more instances of this module.
auto getModule()
Get the module that this node is tracking.
An instance path composed of a series of instances.
InstanceOpInterface leaf() const
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
FieldRef getFieldRefForTarget(const hw::InnerSymTarget &ist)
Get FieldRef pointing to the specified inner symbol target, which must be valid.
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
FIRRTLType mapBaseType(FIRRTLType type, function_ref< FIRRTLBaseType(FIRRTLBaseType)> fn)
Return a FIRRTLType with its base type component mutated by the given function.
llvm::raw_ostream & operator<<(llvm::raw_ostream &os, const InstanceInfo::LatticeValue &value)
std::pair< std::string, bool > getFieldName(const FieldRef &fieldRef, bool nameSafe=false)
Get a string identifier representing the FieldRef.
void emitConnect(OpBuilder &builder, Location loc, Value lhs, Value rhs)
Emit a connect between two values.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
static bool operator==(const ModulePort &a, const ModulePort &b)
static llvm::hash_code hash_value(const ModulePort &port)
bool operator<(const DictEntry &entry, const DictEntry &other)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
llvm::raw_ostream & debugHeader(const llvm::Twine &str, unsigned width=80)
Write a "header"-like string to the debug stream with a certain width.
bool operator!=(uint64_t a, const FVInt &b)
This holds the name and type that describes the module's ports.
This class represents the namespace in which InnerRef's can be resolved.
A data structure that caches and provides paths to module instances in the IR.
static ResetSignal getEmptyKey()
static ResetSignal getTombstoneKey()
static bool isEqual(const ResetSignal &lhs, const ResetSignal &rhs)
static unsigned getHashValue(const ResetSignal &x)