22 #include "mlir/IR/Threading.h"
23 #include "llvm/ADT/APSInt.h"
24 #include "llvm/ADT/TinyPtrVector.h"
25 #include "llvm/Support/Debug.h"
26 #include "llvm/Support/ScopedPrinter.h"
28 using namespace circt;
29 using namespace firrtl;
31 #define DEBUG_TYPE "IMCP"
35 return isa<WireOp, RegResetOp, RegOp>(op);
40 return isa<SubindexOp, SubaccessOp, SubfieldOp, OpenSubfieldOp,
41 OpenSubindexOp, RefSubOp>(op);
47 return isa<NodeOp, RefResolveOp, RefSendOp>(op);
56 if (type_isa<RefType>(op->getResult(0).getType()))
90 LatticeValue() : valueAndTag(nullptr, Kind::Unknown) {}
92 LatticeValue(IntegerAttr attr)
93 : valueAndTag(attr, Kind::Constant) {}
94 LatticeValue(StringAttr attr)
95 : valueAndTag(attr, Kind::Constant) {}
97 static LatticeValue getOverdefined() {
99 result.markOverdefined();
103 bool isUnknown()
const {
return valueAndTag.getInt() == Kind::Unknown; }
104 bool isConstant()
const {
return valueAndTag.getInt() == Kind::Constant; }
105 bool isOverdefined()
const {
106 return valueAndTag.getInt() == Kind::Overdefined;
110 void markOverdefined() {
111 valueAndTag.setPointerAndInt(
nullptr, Kind::Overdefined);
115 void markConstant(IntegerAttr value) {
116 valueAndTag.setPointerAndInt(value, Kind::Constant);
121 Attribute getValue()
const {
return valueAndTag.getPointer(); }
131 bool mergeIn(LatticeValue rhs) {
133 if (isOverdefined() || rhs.isUnknown())
138 valueAndTag = rhs.valueAndTag;
145 if (valueAndTag != rhs.valueAndTag) {
152 bool operator==(
const LatticeValue &other)
const {
153 return valueAndTag == other.valueAndTag;
155 bool operator!=(
const LatticeValue &other)
const {
156 return valueAndTag != other.valueAndTag;
162 llvm::PointerIntPair<Attribute, 2, Kind> valueAndTag;
168 const LatticeValue &lattice) {
169 if (lattice.isUnknown())
170 return os <<
"<Unknown>";
171 if (lattice.isOverdefined())
172 return os <<
"<Overdefined>";
173 return os <<
"<" << lattice.getConstant() <<
">";
177 struct IMConstPropPass :
public IMConstPropBase<IMConstPropPass> {
179 void runOnOperation()
override;
180 void rewriteModuleBody(FModuleOp module);
183 bool isBlockExecutable(Block *block)
const {
184 return executableBlocks.count(block);
187 bool isOverdefined(
FieldRef value)
const {
188 auto it = latticeValues.find(value);
189 return it != latticeValues.end() && it->second.isOverdefined();
194 void markOverdefined(Value value) {
195 FieldRef fieldRef = getOrCacheFieldRefFromValue(value);
196 auto firrtlType = type_dyn_cast<FIRRTLType>(value.getType());
197 if (!firrtlType || type_isa<PropertyType>(firrtlType)) {
198 markOverdefined(fieldRef);
209 void markOverdefined(
FieldRef value) {
210 auto &entry = latticeValues[value];
211 if (!entry.isOverdefined()) {
214 <<
"Setting overdefined : (" <<
getFieldName(value).first <<
")\n";
216 entry.markOverdefined();
217 changedLatticeValueWorklist.push_back(value);
224 void mergeLatticeValue(
FieldRef value, LatticeValue &valueEntry,
225 LatticeValue source) {
226 if (valueEntry.mergeIn(source)) {
229 <<
"Changed to " << valueEntry <<
" : (" << value <<
")\n";
231 changedLatticeValueWorklist.push_back(value);
235 void mergeLatticeValue(
FieldRef value, LatticeValue source) {
237 if (source.isUnknown())
239 mergeLatticeValue(value, latticeValues[value], source);
245 auto it = latticeValues.find(from);
246 if (it == latticeValues.end())
248 mergeLatticeValue(result, it->second);
251 void mergeLatticeValue(Value result, Value from) {
252 FieldRef fieldRefFrom = getOrCacheFieldRefFromValue(from);
253 FieldRef fieldRefResult = getOrCacheFieldRefFromValue(result);
254 if (!type_isa<FIRRTLType>(result.getType()))
255 return mergeLatticeValue(fieldRefResult, fieldRefFrom);
257 if (type_isa<PropertyType>(result.getType()))
258 return mergeLatticeValue(fieldRefResult, fieldRefFrom);
260 [&](uint64_t fieldID,
auto,
auto) {
261 mergeLatticeValue(fieldRefResult.getSubField(fieldID),
262 fieldRefFrom.getSubField(fieldID));
271 void setLatticeValue(
FieldRef value, LatticeValue source) {
273 if (source.isUnknown())
277 auto &valueEntry = latticeValues[value];
278 if (valueEntry != source) {
279 changedLatticeValueWorklist.push_back(value);
287 FieldRef getOrCacheFieldRefFromValue(Value value) {
288 if (!value.getDefiningOp() || !
isAggregate(value.getDefiningOp()))
290 auto &fieldRef = valueToFieldRef[value];
300 bool allowTruncation =
false);
303 void markBlockExecutable(Block *block);
304 void markWireOp(WireOp wireOrReg);
305 void markMemOp(MemOp mem);
307 void markInvalidValueOp(InvalidValueOp invalid);
308 void markAggregateConstantOp(AggregateConstantOp constant);
309 void markInstanceOp(InstanceOp instance);
310 void markObjectOp(ObjectOp
object);
311 template <
typename OpTy>
312 void markConstantValueOp(OpTy op);
315 void visitRefSend(RefSendOp send,
FieldRef changedFieldRef);
316 void visitRefResolve(RefResolveOp resolve,
FieldRef changedFieldRef);
317 void mergeOnlyChangedLatticeValue(Value dest, Value src,
319 void visitNode(NodeOp node,
FieldRef changedFieldRef);
320 void visitOperation(Operation *op,
FieldRef changedFieldRef);
327 DenseMap<FieldRef, LatticeValue> latticeValues;
330 SmallPtrSet<Block *, 16> executableBlocks;
334 SmallVector<FieldRef, 64> changedLatticeValueWorklist;
337 DenseMap<FieldRef, llvm::TinyPtrVector<Operation *>> fieldRefToUsers;
341 llvm::DenseMap<Value, FieldRef> valueToFieldRef;
345 DenseMap<BlockArgument, llvm::TinyPtrVector<Value>>
346 resultPortToInstanceResultMapping;
350 llvm::ScopedPrinter logger{llvm::dbgs()};
356 void IMConstPropPass::runOnOperation() {
357 auto circuit = getOperation();
359 { logger.startLine() <<
"IMConstProp : " << circuit.getName() <<
"\n"; });
361 instanceGraph = &getAnalysis<InstanceGraph>();
364 for (
auto module : circuit.getBodyBlock()->getOps<FModuleOp>()) {
365 if (module.isPublic()) {
366 markBlockExecutable(module.getBodyBlock());
367 for (
auto port : module.getBodyBlock()->getArguments())
368 markOverdefined(port);
373 while (!changedLatticeValueWorklist.empty()) {
374 FieldRef changedFieldRef = changedLatticeValueWorklist.pop_back_val();
375 for (Operation *user : fieldRefToUsers[changedFieldRef]) {
376 if (isBlockExecutable(user->getBlock()))
377 visitOperation(user, changedFieldRef);
382 mlir::parallelForEach(circuit.getContext(),
383 circuit.getBodyBlock()->getOps<FModuleOp>(),
384 [&](
auto op) { rewriteModuleBody(op); });
387 instanceGraph =
nullptr;
388 latticeValues.clear();
389 executableBlocks.clear();
390 assert(changedLatticeValueWorklist.empty());
391 fieldRefToUsers.clear();
392 valueToFieldRef.clear();
393 resultPortToInstanceResultMapping.clear();
399 LatticeValue IMConstPropPass::getExtendedLatticeValue(
FieldRef value,
401 bool allowTruncation) {
403 auto it = latticeValues.find(value);
404 if (it == latticeValues.end())
405 return LatticeValue();
407 auto result = it->second;
409 if (result.isUnknown() || result.isOverdefined())
413 if (isa<PropertyType>(destType))
416 auto constant = result.getConstant();
419 auto intAttr = dyn_cast<IntegerAttr>(constant);
420 assert(intAttr &&
"unsupported lattice attribute kind");
425 if (
auto boolAttr = dyn_cast<BoolAttr>(intAttr))
431 return LatticeValue::getOverdefined();
434 auto resultConstant = intAttr.getAPSInt();
435 auto destWidth = baseType.getBitWidthOrSentinel();
437 return LatticeValue::getOverdefined();
438 if (resultConstant.getBitWidth() == (
unsigned)destWidth)
443 return LatticeValue(
IntegerAttr::get(destType.getContext(), resultConstant));
450 void IMConstPropPass::markBlockExecutable(Block *block) {
451 if (!executableBlocks.insert(block).second)
456 for (
auto ba : block->getArguments())
460 for (
auto &op : *block) {
462 TypeSwitch<Operation *>(&op)
463 .Case<RegOp, RegResetOp>(
464 [&](
auto reg) { markOverdefined(op.getResult(0)); })
465 .Case<WireOp>([&](
auto wire) { markWireOp(wire); })
466 .Case<ConstantOp, SpecialConstantOp, StringConstantOp,
467 FIntegerConstantOp, BoolConstantOp>(
468 [&](
auto constOp) { markConstantValueOp(constOp); })
469 .Case<AggregateConstantOp>(
470 [&](
auto aggConstOp) { markAggregateConstantOp(aggConstOp); })
471 .Case<InvalidValueOp>(
472 [&](
auto invalid) { markInvalidValueOp(invalid); })
473 .Case<InstanceOp>([&](
auto instance) { markInstanceOp(instance); })
474 .Case<ObjectOp>([&](
auto obj) { markObjectOp(obj); })
475 .Case<MemOp>([&](
auto mem) { markMemOp(mem); })
476 .Default([&](
auto _) {
477 if (isa<mlir::UnrealizedConversionCastOp, VerbatimExprOp,
478 VerbatimWireOp, SubaccessOp>(op) ||
479 op.getNumOperands() == 0) {
483 for (
auto result : op.getResults())
484 markOverdefined(result);
496 bool hasAggregateOperand =
497 llvm::any_of(op.getOperandTypes(), [](Type type) {
498 return type_isa<FVectorType, BundleType>(type);
501 for (
auto result : op.getResults())
502 if (hasAggregateOperand ||
503 type_isa<FVectorType, BundleType>(result.getType()))
504 markOverdefined(result);
511 for (
auto operand : op.getOperands()) {
512 auto fieldRef = getOrCacheFieldRefFromValue(operand);
513 auto firrtlType = type_dyn_cast<FIRRTLType>(operand.getType());
517 if (type_isa<PropertyType>(firrtlType)) {
518 fieldRefToUsers[fieldRef].push_back(&op);
522 fieldRefToUsers[fieldRef.
getSubField(fieldID)].push_back(&op);
530 void IMConstPropPass::markWireOp(WireOp wire) {
531 auto type = type_dyn_cast<FIRRTLType>(wire.getResult().getType());
532 if (!type ||
hasDontTouch(wire.getResult()) || wire.isForceable()) {
533 for (
auto result : wire.getResults())
534 markOverdefined(result);
541 void IMConstPropPass::markMemOp(MemOp mem) {
542 for (
auto result : mem.getResults())
543 markOverdefined(result);
546 template <
typename OpTy>
547 void IMConstPropPass::markConstantValueOp(OpTy op) {
548 mergeLatticeValue(getOrCacheFieldRefFromValue(op),
549 LatticeValue(op.getValueAttr()));
552 void IMConstPropPass::markAggregateConstantOp(AggregateConstantOp constant) {
553 walkGroundTypes(constant.getType(), [&](uint64_t fieldID,
auto,
auto) {
554 mergeLatticeValue(FieldRef(constant, fieldID),
555 LatticeValue(cast<IntegerAttr>(
556 constant.getAttributeFromFieldID(fieldID))));
560 void IMConstPropPass::markInvalidValueOp(InvalidValueOp invalid) {
561 markOverdefined(invalid.getResult());
566 void IMConstPropPass::markInstanceOp(InstanceOp instance) {
568 Operation *op = instance.getReferencedModule(*instanceGraph);
572 if (!isa<FModuleOp>(op)) {
573 auto module = dyn_cast<FModuleLike>(op);
574 for (
size_t resultNo = 0, e = instance.getNumResults(); resultNo != e;
576 auto portVal = instance.getResult(resultNo);
582 markOverdefined(portVal);
588 auto fModule = cast<FModuleOp>(op);
589 markBlockExecutable(fModule.getBodyBlock());
593 for (
size_t resultNo = 0, e = instance.getNumResults(); resultNo != e;
595 auto instancePortVal = instance.getResult(resultNo);
603 BlockArgument modulePortVal = fModule.getArgument(resultNo);
605 resultPortToInstanceResultMapping[modulePortVal].push_back(instancePortVal);
609 mergeLatticeValue(instancePortVal, modulePortVal);
613 void IMConstPropPass::markObjectOp(ObjectOp obj) {
615 markOverdefined(obj);
618 static std::optional<uint64_t>
621 assert(!type_isa<RefType>(connectionType));
632 void IMConstPropPass::mergeOnlyChangedLatticeValue(Value dest, Value src,
636 auto destType = dest.getType();
637 if (
auto refType = type_dyn_cast<RefType>(destType))
638 destType = refType.getType();
640 if (!isa<FIRRTLType>(destType)) {
643 markOverdefined(src);
644 return markOverdefined(dest);
647 auto fieldRefSrc = getOrCacheFieldRefFromValue(src);
648 auto fieldRefDest = getOrCacheFieldRefFromValue(dest);
652 if (
auto srcOffset =
getFieldIDOffset(changedFieldRef, destType, fieldRefSrc))
653 mergeLatticeValue(fieldRefDest.getSubField(*srcOffset),
654 fieldRefSrc.getSubField(*srcOffset));
658 if (
auto destOffset =
660 mergeLatticeValue(fieldRefDest.getSubField(*destOffset),
661 fieldRefSrc.getSubField(*destOffset));
664 void IMConstPropPass::visitConnectLike(FConnectLike
connect,
667 auto destType =
connect.getDest().getType();
668 if (
auto refType = type_dyn_cast<RefType>(destType))
669 destType = refType.getType();
672 if (!isa<FIRRTLType>(destType)) {
673 markOverdefined(
connect.getSrc());
674 return markOverdefined(
connect.getDest());
677 auto fieldRefSrc = getOrCacheFieldRefFromValue(
connect.getSrc());
678 auto fieldRefDest = getOrCacheFieldRefFromValue(
connect.getDest());
679 if (
auto subaccess = fieldRefDest.getValue().getDefiningOp<SubaccessOp>()) {
683 Value parent = subaccess.getInput();
684 while (parent.getDefiningOp() &&
685 parent.getDefiningOp()->getNumOperands() > 0)
686 parent = parent.getDefiningOp()->getOperand(0);
687 return markOverdefined(parent);
690 auto propagateElementLattice = [&](uint64_t fieldID,
FIRRTLType destType) {
691 auto fieldRefDestConnected = fieldRefDest.getSubField(fieldID);
692 assert(!firrtl::type_isa<FIRRTLBaseType>(destType) ||
693 firrtl::type_cast<FIRRTLBaseType>(destType).isGround());
697 getExtendedLatticeValue(fieldRefSrc.getSubField(fieldID), destType);
698 if (srcValue.isUnknown())
703 if (
auto blockArg = dyn_cast<BlockArgument>(fieldRefDest.getValue())) {
704 for (
auto userOfResultPort : resultPortToInstanceResultMapping[blockArg])
706 FieldRef(userOfResultPort, fieldRefDestConnected.getFieldID()),
709 return mergeLatticeValue(fieldRefDestConnected, srcValue);
712 auto dest = cast<mlir::OpResult>(fieldRefDest.getValue());
717 return mergeLatticeValue(fieldRefDestConnected, srcValue);
721 if (
auto instance = dest.getDefiningOp<InstanceOp>()) {
723 mergeLatticeValue(fieldRefDestConnected, srcValue);
724 auto mod = instance.getReferencedModule<FModuleOp>(*instanceGraph);
728 BlockArgument modulePortVal = mod.getArgument(dest.getResultNumber());
730 return mergeLatticeValue(
731 FieldRef(modulePortVal, fieldRefDestConnected.getFieldID()),
737 if (dest.getDefiningOp<MemOp>())
741 if (isa_and_nonnull<ObjectSubfieldOp>(dest.getDefiningOp()))
744 connect.emitError(
"connectlike operation unhandled by IMConstProp")
745 .attachNote(
connect.getDest().getLoc())
746 <<
"connect destination is here";
749 if (
auto srcOffset =
getFieldIDOffset(changedFieldRef, destType, fieldRefSrc))
750 propagateElementLattice(
752 firrtl::type_cast<FIRRTLType>(
755 if (
auto relativeDest =
757 propagateElementLattice(
759 firrtl::type_cast<FIRRTLType>(
763 void IMConstPropPass::visitRefSend(RefSendOp send,
FieldRef changedFieldRef) {
765 return mergeOnlyChangedLatticeValue(send.getResult(), send.getBase(),
769 void IMConstPropPass::visitRefResolve(RefResolveOp resolve,
773 return mergeOnlyChangedLatticeValue(resolve.getResult(), resolve.getRef(),
777 void IMConstPropPass::visitNode(NodeOp node,
FieldRef changedFieldRef) {
778 if (
hasDontTouch(node.getResult()) || node.isForceable()) {
779 for (
auto result : node.getResults())
780 markOverdefined(result);
784 return mergeOnlyChangedLatticeValue(node.getResult(), node.getInput(),
794 void IMConstPropPass::visitOperation(Operation *op,
FieldRef changedField) {
796 if (
auto connectLikeOp = dyn_cast<FConnectLike>(op))
797 return visitConnectLike(connectLikeOp, changedField);
798 if (
auto sendOp = dyn_cast<RefSendOp>(op))
799 return visitRefSend(sendOp, changedField);
800 if (
auto resolveOp = dyn_cast<RefResolveOp>(op))
801 return visitRefResolve(resolveOp, changedField);
802 if (
auto nodeOp = dyn_cast<NodeOp>(op))
803 return visitNode(nodeOp, changedField);
814 auto isOverdefinedFn = [&](Value value) {
815 return isOverdefined(getOrCacheFieldRefFromValue(value));
817 if (llvm::all_of(op->getResults(), isOverdefinedFn))
822 if (op->getNumOperands() > 128) {
823 for (
auto value : op->getResults())
824 markOverdefined(value);
830 SmallVector<Attribute, 8> operandConstants;
831 operandConstants.reserve(op->getNumOperands());
832 bool hasUnknown =
false;
833 for (Value operand : op->getOperands()) {
835 auto &operandLattice = latticeValues[getOrCacheFieldRefFromValue(operand)];
840 if (operandLattice.isUnknown())
845 if (operandLattice.isConstant())
846 operandConstants.push_back(operandLattice.getValue());
848 operandConstants.push_back({});
853 SmallVector<OpFoldResult, 8> foldResults;
854 foldResults.reserve(op->getNumResults());
855 if (failed(op->fold(operandConstants, foldResults))) {
857 logger.startLine() <<
"Folding Failed operation : '" << op->getName()
863 for (
auto value : op->getResults())
864 markOverdefined(value);
869 logger.getOStream() <<
"\n";
870 logger.startLine() <<
"Folding operation : '" << op->getName() <<
"\n";
872 logger.getOStream() <<
"( ";
873 for (
auto cst : operandConstants)
875 logger.getOStream() <<
"{} ";
877 logger.getOStream() << cst <<
" ";
879 logger.getOStream() <<
") -> { ";
881 for (
auto &r : foldResults) {
882 logger.getOStream() << r <<
" ";
885 logger.getOStream() <<
"}\n";
892 if (foldResults.empty())
893 return visitOperation(op, changedField);
896 assert(foldResults.size() == op->getNumResults() &&
"invalid result size");
897 for (
unsigned i = 0, e = foldResults.size(); i != e; ++i) {
899 LatticeValue resultLattice;
900 OpFoldResult foldResult = foldResults[i];
901 if (Attribute foldAttr = dyn_cast<Attribute>(foldResult)) {
902 if (
auto intAttr = dyn_cast<IntegerAttr>(foldAttr))
903 resultLattice = LatticeValue(intAttr);
904 else if (
auto strAttr = dyn_cast<StringAttr>(foldAttr))
905 resultLattice = LatticeValue(strAttr);
907 resultLattice = LatticeValue::getOverdefined();
910 latticeValues[getOrCacheFieldRefFromValue(foldResult.get<Value>())];
913 mergeLatticeValue(getOrCacheFieldRefFromValue(op->getResult(i)),
918 void IMConstPropPass::rewriteModuleBody(FModuleOp module) {
919 auto *body = module.getBodyBlock();
921 if (!executableBlocks.count(body))
924 auto builder = OpBuilder::atBlockBegin(body);
928 auto cursor =
builder.create<firrtl::ConstantOp>(module.getLoc(), APSInt(1));
929 builder.setInsertionPoint(cursor);
932 DenseMap<std::pair<Attribute, Type>, Operation *> constPool;
934 std::function<Value(Attribute, Type, Location)> getConst =
935 [&](Attribute constantValue, Type type, Location loc) -> Value {
936 auto constIt = constPool.find({constantValue, type});
937 if (constIt != constPool.end()) {
938 auto *cst = constIt->second;
940 cst->setLoc(
builder.getFusedLoc({cst->getLoc(), loc}));
941 return cst->getResult(0);
943 OpBuilder::InsertionGuard x(
builder);
944 builder.setInsertionPoint(cursor);
949 if (
auto refType = type_dyn_cast<RefType>(type)) {
950 assert(!type_cast<RefType>(type).getForceable() &&
951 "Attempting to materialize rwprobe of constant, shouldn't happen");
952 auto inner = getConst(constantValue, refType.getType(), loc);
954 cst =
builder.create<RefSendOp>(loc, inner);
956 cst = module->getDialect()->materializeConstant(
builder, constantValue,
958 assert(cst &&
"all FIRRTL constants can be materialized");
959 constPool.insert({{constantValue, type}, cst});
960 return cst->getResult(0);
965 auto replaceValueIfPossible = [&](Value value) ->
bool {
969 auto replaceIfNotConnect = [&value](Value replacement) {
970 value.replaceUsesWithIf(replacement, [](OpOperand &operand) {
971 return !isa<FConnectLike>(operand.getOwner()) ||
972 operand.getOperandNumber() != 0;
978 if (it == latticeValues.end() || it->second.isOverdefined() ||
979 it->second.isUnknown())
985 if (!type_isa<FIRRTLBaseType, RefType, FIntegerType, StringType, BoolType>(
990 getConst(it->second.getValue(), value.getType(), value.
getLoc());
992 replaceIfNotConnect(cstValue);
997 for (
auto &port : body->getArguments())
998 replaceValueIfPossible(port);
1006 bool aboveCursor =
false;
1007 for (
auto &op : llvm::make_early_inc_range(llvm::reverse(*body))) {
1008 auto dropIfDead = [&](Operation &op,
const Twine &debugPrefix) {
1009 if (op.use_empty() &&
1012 { logger.getOStream() << debugPrefix <<
" : " << op <<
"\n"; });
1022 dropIfDead(op,
"Trivially dead materialized constant");
1026 if (&op == cursor) {
1033 if (
auto connect = dyn_cast<FConnectLike>(op)) {
1034 if (
auto *destOp =
connect.getDest().getDefiningOp()) {
1035 auto fieldRef = getOrCacheFieldRefFromValue(
connect.getDest());
1041 auto type = type_dyn_cast<FIRRTLType>(
connect.getDest().getType());
1044 auto baseType = type_dyn_cast<FIRRTLBaseType>(type);
1045 if (baseType && !baseType.isGround())
1057 if (op.getNumResults() != 1 && !isa<InstanceOp>(op))
1061 if (dropIfDead(op,
"Trivially dead"))
1066 if (op.hasTrait<mlir::OpTrait::ConstantLike>())
1070 builder.setInsertionPoint(&op);
1071 bool foldedAny =
false;
1072 for (
auto result : op.getResults())
1073 foldedAny |= replaceValueIfPossible(result);
1079 if (foldedAny && dropIfDead(op,
"Made dead"))
1085 return std::make_unique<IMConstPropPass>();
assert(baseType &&"element must be base type")
static std::optional< APSInt > getConstant(Attribute operand)
Determine the value of a constant operand for the sake of constant folding.
static bool isNodeLike(Operation *op)
static std::optional< uint64_t > getFieldIDOffset(FieldRef changedFieldRef, Type connectionType, FieldRef connectedValueFieldRef)
static bool isWireOrReg(Operation *op)
Return true if this is a wire or register.
static bool isAggregate(Operation *op)
Return true if this is an aggregate indexer.
static bool isDeletableWireOrRegOrNode(Operation *op)
Return true if this is a wire or register we're allowed to delete.
bool operator!=(const ResetDomain &a, const ResetDomain &b)
bool operator==(const ResetDomain &a, const ResetDomain &b)
This class represents a reference to a specific field or element of an aggregate value.
FieldRef getSubField(unsigned subFieldID) const
Get a reference to a subfield.
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.
Location getLoc() const
Get the location associated with the value of this field ref.
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool canBeDeleted() const
Check if every annotation can be deleted.
This graph tracks modules and where they are instantiated.
def connect(destination, source)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
FieldRef getFieldRefFromValue(Value value, bool lookThroughCasts=false)
Get the FieldRef from a value.
void walkGroundTypes(FIRRTLType firrtlType, llvm::function_ref< void(uint64_t, FIRRTLBaseType, bool)> fn)
Walk leaf ground types in the firrtlType and apply the function fn.
bool isConstant(Operation *op)
Return true if the specified operation has a constant value.
std::unique_ptr< mlir::Pass > createIMConstPropPass()
bool hasDontTouch(Value value)
Check whether a block argument ("port") or the operation defining a value has a DontTouch annotation,...
T & operator<<(T &os, FIRVersion version)
bool hasDroppableName(Operation *op)
Return true if the name is droppable.
std::pair< std::string, bool > getFieldName(const FieldRef &fieldRef, bool nameSafe=false)
Get a string identifier representing the FieldRef.
::mlir::Type getFinalTypeByFieldID(Type type, uint64_t fieldID)
uint64_t getMaxFieldID(Type)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
APSInt extOrTruncZeroWidth(APSInt value, unsigned width)
A safe version of APSInt::extOrTrunc that will NOT assert on zero-width signed APSInts.
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)