22 #include "mlir/IR/Threading.h"
23 #include "mlir/Pass/Pass.h"
24 #include "llvm/ADT/APSInt.h"
25 #include "llvm/ADT/TinyPtrVector.h"
26 #include "llvm/Support/Debug.h"
27 #include "llvm/Support/ScopedPrinter.h"
31 #define GEN_PASS_DEF_IMCONSTPROP
32 #include "circt/Dialect/FIRRTL/Passes.h.inc"
36 using namespace circt;
37 using namespace firrtl;
39 #define DEBUG_TYPE "IMCP"
43 return isa<WireOp, RegResetOp, RegOp>(op);
48 return isa<SubindexOp, SubaccessOp, SubfieldOp, OpenSubfieldOp,
49 OpenSubindexOp, RefSubOp>(op);
55 return isa<NodeOp, RefResolveOp, RefSendOp>(op);
64 if (type_isa<RefType>(op->getResult(0).getType()))
98 LatticeValue() : valueAndTag(nullptr, Kind::Unknown) {}
100 LatticeValue(IntegerAttr attr)
101 : valueAndTag(attr, Kind::Constant) {}
102 LatticeValue(StringAttr attr)
103 : valueAndTag(attr, Kind::Constant) {}
105 static LatticeValue getOverdefined() {
107 result.markOverdefined();
111 bool isUnknown()
const {
return valueAndTag.getInt() == Kind::Unknown; }
112 bool isConstant()
const {
return valueAndTag.getInt() == Kind::Constant; }
113 bool isOverdefined()
const {
114 return valueAndTag.getInt() == Kind::Overdefined;
118 void markOverdefined() {
119 valueAndTag.setPointerAndInt(
nullptr, Kind::Overdefined);
123 void markConstant(IntegerAttr value) {
124 valueAndTag.setPointerAndInt(value, Kind::Constant);
129 Attribute getValue()
const {
return valueAndTag.getPointer(); }
139 bool mergeIn(LatticeValue rhs) {
141 if (isOverdefined() || rhs.isUnknown())
146 valueAndTag = rhs.valueAndTag;
153 if (valueAndTag != rhs.valueAndTag) {
160 bool operator==(
const LatticeValue &other)
const {
161 return valueAndTag == other.valueAndTag;
163 bool operator!=(
const LatticeValue &other)
const {
164 return valueAndTag != other.valueAndTag;
170 llvm::PointerIntPair<Attribute, 2, Kind> valueAndTag;
176 const LatticeValue &lattice) {
177 if (lattice.isUnknown())
178 return os <<
"<Unknown>";
179 if (lattice.isOverdefined())
180 return os <<
"<Overdefined>";
181 return os <<
"<" << lattice.getConstant() <<
">";
185 struct IMConstPropPass
186 :
public circt::firrtl::impl::IMConstPropBase<IMConstPropPass> {
188 void runOnOperation()
override;
189 void rewriteModuleBody(FModuleOp module);
192 bool isBlockExecutable(Block *block)
const {
193 return executableBlocks.count(block);
196 bool isOverdefined(
FieldRef value)
const {
197 auto it = latticeValues.find(value);
198 return it != latticeValues.end() && it->second.isOverdefined();
203 void markOverdefined(Value value) {
204 FieldRef fieldRef = getOrCacheFieldRefFromValue(value);
205 auto firrtlType = type_dyn_cast<FIRRTLType>(value.getType());
206 if (!firrtlType || type_isa<PropertyType>(firrtlType)) {
207 markOverdefined(fieldRef);
218 void markOverdefined(
FieldRef value) {
219 auto &entry = latticeValues[value];
220 if (!entry.isOverdefined()) {
223 <<
"Setting overdefined : (" <<
getFieldName(value).first <<
")\n";
225 entry.markOverdefined();
226 changedLatticeValueWorklist.push_back(value);
233 void mergeLatticeValue(
FieldRef value, LatticeValue &valueEntry,
234 LatticeValue source) {
235 if (valueEntry.mergeIn(source)) {
238 <<
"Changed to " << valueEntry <<
" : (" << value <<
")\n";
240 changedLatticeValueWorklist.push_back(value);
244 void mergeLatticeValue(
FieldRef value, LatticeValue source) {
246 if (source.isUnknown())
248 mergeLatticeValue(value, latticeValues[value], source);
254 auto it = latticeValues.find(from);
255 if (it == latticeValues.end())
257 mergeLatticeValue(result, it->second);
260 void mergeLatticeValue(Value result, Value from) {
261 FieldRef fieldRefFrom = getOrCacheFieldRefFromValue(from);
262 FieldRef fieldRefResult = getOrCacheFieldRefFromValue(result);
263 if (!type_isa<FIRRTLType>(result.getType()))
264 return mergeLatticeValue(fieldRefResult, fieldRefFrom);
266 if (type_isa<PropertyType>(result.getType()))
267 return mergeLatticeValue(fieldRefResult, fieldRefFrom);
269 [&](uint64_t fieldID,
auto,
auto) {
270 mergeLatticeValue(fieldRefResult.getSubField(fieldID),
271 fieldRefFrom.getSubField(fieldID));
280 void setLatticeValue(
FieldRef value, LatticeValue source) {
282 if (source.isUnknown())
286 auto &valueEntry = latticeValues[value];
287 if (valueEntry != source) {
288 changedLatticeValueWorklist.push_back(value);
296 FieldRef getOrCacheFieldRefFromValue(Value value) {
297 if (!value.getDefiningOp() || !
isAggregate(value.getDefiningOp()))
299 auto &fieldRef = valueToFieldRef[value];
309 bool allowTruncation =
false);
312 void markBlockExecutable(Block *block);
313 void markWireOp(WireOp wireOrReg);
314 void markMemOp(MemOp mem);
316 void markInvalidValueOp(InvalidValueOp invalid);
317 void markAggregateConstantOp(AggregateConstantOp constant);
318 void markInstanceOp(InstanceOp instance);
319 void markObjectOp(ObjectOp
object);
320 template <
typename OpTy>
321 void markConstantValueOp(OpTy op);
324 void visitRefSend(RefSendOp send,
FieldRef changedFieldRef);
325 void visitRefResolve(RefResolveOp resolve,
FieldRef changedFieldRef);
326 void mergeOnlyChangedLatticeValue(Value dest, Value src,
328 void visitNode(NodeOp node,
FieldRef changedFieldRef);
329 void visitOperation(Operation *op,
FieldRef changedFieldRef);
336 DenseMap<FieldRef, LatticeValue> latticeValues;
339 SmallPtrSet<Block *, 16> executableBlocks;
343 SmallVector<FieldRef, 64> changedLatticeValueWorklist;
346 DenseMap<FieldRef, llvm::TinyPtrVector<Operation *>> fieldRefToUsers;
350 llvm::DenseMap<Value, FieldRef> valueToFieldRef;
354 DenseMap<BlockArgument, llvm::TinyPtrVector<Value>>
355 resultPortToInstanceResultMapping;
359 llvm::ScopedPrinter logger{llvm::dbgs()};
365 void IMConstPropPass::runOnOperation() {
366 auto circuit = getOperation();
368 { logger.startLine() <<
"IMConstProp : " << circuit.getName() <<
"\n"; });
370 instanceGraph = &getAnalysis<InstanceGraph>();
373 for (
auto module : circuit.getBodyBlock()->getOps<FModuleOp>()) {
374 if (module.isPublic()) {
375 markBlockExecutable(module.getBodyBlock());
376 for (
auto port : module.getBodyBlock()->getArguments())
377 markOverdefined(port);
382 while (!changedLatticeValueWorklist.empty()) {
383 FieldRef changedFieldRef = changedLatticeValueWorklist.pop_back_val();
384 for (Operation *user : fieldRefToUsers[changedFieldRef]) {
385 if (isBlockExecutable(user->getBlock()))
386 visitOperation(user, changedFieldRef);
391 mlir::parallelForEach(circuit.getContext(),
392 circuit.getBodyBlock()->getOps<FModuleOp>(),
393 [&](
auto op) { rewriteModuleBody(op); });
396 instanceGraph =
nullptr;
397 latticeValues.clear();
398 executableBlocks.clear();
399 assert(changedLatticeValueWorklist.empty());
400 fieldRefToUsers.clear();
401 valueToFieldRef.clear();
402 resultPortToInstanceResultMapping.clear();
408 LatticeValue IMConstPropPass::getExtendedLatticeValue(
FieldRef value,
410 bool allowTruncation) {
412 auto it = latticeValues.find(value);
413 if (it == latticeValues.end())
414 return LatticeValue();
416 auto result = it->second;
418 if (result.isUnknown() || result.isOverdefined())
422 if (isa<PropertyType>(destType))
425 auto constant = result.getConstant();
428 auto intAttr = dyn_cast<IntegerAttr>(constant);
429 assert(intAttr &&
"unsupported lattice attribute kind");
434 if (
auto boolAttr = dyn_cast<BoolAttr>(intAttr))
440 return LatticeValue::getOverdefined();
443 auto resultConstant = intAttr.getAPSInt();
444 auto destWidth = baseType.getBitWidthOrSentinel();
446 return LatticeValue::getOverdefined();
447 if (resultConstant.getBitWidth() == (
unsigned)destWidth)
452 return LatticeValue(
IntegerAttr::get(destType.getContext(), resultConstant));
459 void IMConstPropPass::markBlockExecutable(Block *block) {
460 if (!executableBlocks.insert(block).second)
465 for (
auto ba : block->getArguments())
469 for (
auto &op : *block) {
471 TypeSwitch<Operation *>(&op)
472 .Case<RegOp, RegResetOp>(
473 [&](
auto reg) { markOverdefined(op.getResult(0)); })
474 .Case<WireOp>([&](
auto wire) { markWireOp(wire); })
475 .Case<ConstantOp, SpecialConstantOp, StringConstantOp,
476 FIntegerConstantOp, BoolConstantOp>(
477 [&](
auto constOp) { markConstantValueOp(constOp); })
478 .Case<AggregateConstantOp>(
479 [&](
auto aggConstOp) { markAggregateConstantOp(aggConstOp); })
480 .Case<InvalidValueOp>(
481 [&](
auto invalid) { markInvalidValueOp(invalid); })
482 .Case<InstanceOp>([&](
auto instance) { markInstanceOp(instance); })
483 .Case<ObjectOp>([&](
auto obj) { markObjectOp(obj); })
484 .Case<MemOp>([&](
auto mem) { markMemOp(mem); })
485 .Default([&](
auto _) {
486 if (isa<mlir::UnrealizedConversionCastOp, VerbatimExprOp,
487 VerbatimWireOp, SubaccessOp>(op) ||
488 op.getNumOperands() == 0) {
492 for (
auto result : op.getResults())
493 markOverdefined(result);
505 bool hasAggregateOperand =
506 llvm::any_of(op.getOperandTypes(), [](Type type) {
507 return type_isa<FVectorType, BundleType>(type);
510 for (
auto result : op.getResults())
511 if (hasAggregateOperand ||
512 type_isa<FVectorType, BundleType>(result.getType()))
513 markOverdefined(result);
520 for (
auto operand : op.getOperands()) {
521 auto fieldRef = getOrCacheFieldRefFromValue(operand);
522 auto firrtlType = type_dyn_cast<FIRRTLType>(operand.getType());
526 if (type_isa<PropertyType>(firrtlType)) {
527 fieldRefToUsers[fieldRef].push_back(&op);
531 fieldRefToUsers[fieldRef.
getSubField(fieldID)].push_back(&op);
539 void IMConstPropPass::markWireOp(WireOp wire) {
540 auto type = type_dyn_cast<FIRRTLType>(wire.getResult().getType());
541 if (!type ||
hasDontTouch(wire.getResult()) || wire.isForceable()) {
542 for (
auto result : wire.getResults())
543 markOverdefined(result);
550 void IMConstPropPass::markMemOp(MemOp mem) {
551 for (
auto result : mem.getResults())
552 markOverdefined(result);
555 template <
typename OpTy>
556 void IMConstPropPass::markConstantValueOp(OpTy op) {
557 mergeLatticeValue(getOrCacheFieldRefFromValue(op),
558 LatticeValue(op.getValueAttr()));
561 void IMConstPropPass::markAggregateConstantOp(AggregateConstantOp constant) {
562 walkGroundTypes(constant.getType(), [&](uint64_t fieldID,
auto,
auto) {
563 mergeLatticeValue(FieldRef(constant, fieldID),
564 LatticeValue(cast<IntegerAttr>(
565 constant.getAttributeFromFieldID(fieldID))));
569 void IMConstPropPass::markInvalidValueOp(InvalidValueOp invalid) {
570 markOverdefined(invalid.getResult());
575 void IMConstPropPass::markInstanceOp(InstanceOp instance) {
577 Operation *op = instance.getReferencedModule(*instanceGraph);
581 if (!isa<FModuleOp>(op)) {
582 auto module = dyn_cast<FModuleLike>(op);
583 for (
size_t resultNo = 0, e = instance.getNumResults(); resultNo != e;
585 auto portVal = instance.getResult(resultNo);
591 markOverdefined(portVal);
597 auto fModule = cast<FModuleOp>(op);
598 markBlockExecutable(fModule.getBodyBlock());
602 for (
size_t resultNo = 0, e = instance.getNumResults(); resultNo != e;
604 auto instancePortVal = instance.getResult(resultNo);
612 BlockArgument modulePortVal = fModule.getArgument(resultNo);
614 resultPortToInstanceResultMapping[modulePortVal].push_back(instancePortVal);
618 mergeLatticeValue(instancePortVal, modulePortVal);
622 void IMConstPropPass::markObjectOp(ObjectOp obj) {
624 markOverdefined(obj);
627 static std::optional<uint64_t>
630 assert(!type_isa<RefType>(connectionType));
641 void IMConstPropPass::mergeOnlyChangedLatticeValue(Value dest, Value src,
645 auto destType = dest.getType();
646 if (
auto refType = type_dyn_cast<RefType>(destType))
647 destType = refType.getType();
649 if (!isa<FIRRTLType>(destType)) {
652 markOverdefined(src);
653 return markOverdefined(dest);
656 auto fieldRefSrc = getOrCacheFieldRefFromValue(src);
657 auto fieldRefDest = getOrCacheFieldRefFromValue(dest);
661 if (
auto srcOffset =
getFieldIDOffset(changedFieldRef, destType, fieldRefSrc))
662 mergeLatticeValue(fieldRefDest.getSubField(*srcOffset),
663 fieldRefSrc.getSubField(*srcOffset));
667 if (
auto destOffset =
669 mergeLatticeValue(fieldRefDest.getSubField(*destOffset),
670 fieldRefSrc.getSubField(*destOffset));
673 void IMConstPropPass::visitConnectLike(FConnectLike
connect,
676 auto destType =
connect.getDest().getType();
677 if (
auto refType = type_dyn_cast<RefType>(destType))
678 destType = refType.getType();
681 if (!isa<FIRRTLType>(destType)) {
682 markOverdefined(
connect.getSrc());
683 return markOverdefined(
connect.getDest());
686 auto fieldRefSrc = getOrCacheFieldRefFromValue(
connect.getSrc());
687 auto fieldRefDest = getOrCacheFieldRefFromValue(
connect.getDest());
688 if (
auto subaccess = fieldRefDest.getValue().getDefiningOp<SubaccessOp>()) {
692 Value parent = subaccess.getInput();
693 while (parent.getDefiningOp() &&
694 parent.getDefiningOp()->getNumOperands() > 0)
695 parent = parent.getDefiningOp()->getOperand(0);
696 return markOverdefined(parent);
699 auto propagateElementLattice = [&](uint64_t fieldID,
FIRRTLType destType) {
700 auto fieldRefDestConnected = fieldRefDest.getSubField(fieldID);
701 assert(!firrtl::type_isa<FIRRTLBaseType>(destType) ||
702 firrtl::type_cast<FIRRTLBaseType>(destType).isGround());
706 getExtendedLatticeValue(fieldRefSrc.getSubField(fieldID), destType);
707 if (srcValue.isUnknown())
712 if (
auto blockArg = dyn_cast<BlockArgument>(fieldRefDest.getValue())) {
713 for (
auto userOfResultPort : resultPortToInstanceResultMapping[blockArg])
715 FieldRef(userOfResultPort, fieldRefDestConnected.getFieldID()),
718 return mergeLatticeValue(fieldRefDestConnected, srcValue);
721 auto dest = cast<mlir::OpResult>(fieldRefDest.getValue());
726 return mergeLatticeValue(fieldRefDestConnected, srcValue);
730 if (
auto instance = dest.getDefiningOp<InstanceOp>()) {
732 mergeLatticeValue(fieldRefDestConnected, srcValue);
733 auto mod = instance.getReferencedModule<FModuleOp>(*instanceGraph);
737 BlockArgument modulePortVal = mod.getArgument(dest.getResultNumber());
739 return mergeLatticeValue(
740 FieldRef(modulePortVal, fieldRefDestConnected.getFieldID()),
746 if (dest.getDefiningOp<MemOp>())
750 if (isa_and_nonnull<ObjectSubfieldOp>(dest.getDefiningOp()))
753 connect.emitError(
"connectlike operation unhandled by IMConstProp")
754 .attachNote(
connect.getDest().getLoc())
755 <<
"connect destination is here";
758 if (
auto srcOffset =
getFieldIDOffset(changedFieldRef, destType, fieldRefSrc))
759 propagateElementLattice(
761 firrtl::type_cast<FIRRTLType>(
764 if (
auto relativeDest =
766 propagateElementLattice(
768 firrtl::type_cast<FIRRTLType>(
772 void IMConstPropPass::visitRefSend(RefSendOp send,
FieldRef changedFieldRef) {
774 return mergeOnlyChangedLatticeValue(send.getResult(), send.getBase(),
778 void IMConstPropPass::visitRefResolve(RefResolveOp resolve,
782 return mergeOnlyChangedLatticeValue(resolve.getResult(), resolve.getRef(),
786 void IMConstPropPass::visitNode(NodeOp node,
FieldRef changedFieldRef) {
787 if (
hasDontTouch(node.getResult()) || node.isForceable()) {
788 for (
auto result : node.getResults())
789 markOverdefined(result);
793 return mergeOnlyChangedLatticeValue(node.getResult(), node.getInput(),
803 void IMConstPropPass::visitOperation(Operation *op,
FieldRef changedField) {
805 if (
auto connectLikeOp = dyn_cast<FConnectLike>(op))
806 return visitConnectLike(connectLikeOp, changedField);
807 if (
auto sendOp = dyn_cast<RefSendOp>(op))
808 return visitRefSend(sendOp, changedField);
809 if (
auto resolveOp = dyn_cast<RefResolveOp>(op))
810 return visitRefResolve(resolveOp, changedField);
811 if (
auto nodeOp = dyn_cast<NodeOp>(op))
812 return visitNode(nodeOp, changedField);
823 auto isOverdefinedFn = [&](Value value) {
824 return isOverdefined(getOrCacheFieldRefFromValue(value));
826 if (llvm::all_of(op->getResults(), isOverdefinedFn))
831 if (op->getNumOperands() > 128) {
832 for (
auto value : op->getResults())
833 markOverdefined(value);
839 SmallVector<Attribute, 8> operandConstants;
840 operandConstants.reserve(op->getNumOperands());
841 bool hasUnknown =
false;
842 for (Value operand : op->getOperands()) {
844 auto &operandLattice = latticeValues[getOrCacheFieldRefFromValue(operand)];
849 if (operandLattice.isUnknown())
854 if (operandLattice.isConstant())
855 operandConstants.push_back(operandLattice.getValue());
857 operandConstants.push_back({});
862 SmallVector<OpFoldResult, 8> foldResults;
863 foldResults.reserve(op->getNumResults());
864 if (failed(op->fold(operandConstants, foldResults))) {
866 logger.startLine() <<
"Folding Failed operation : '" << op->getName()
872 for (
auto value : op->getResults())
873 markOverdefined(value);
878 logger.getOStream() <<
"\n";
879 logger.startLine() <<
"Folding operation : '" << op->getName() <<
"\n";
881 logger.getOStream() <<
"( ";
882 for (
auto cst : operandConstants)
884 logger.getOStream() <<
"{} ";
886 logger.getOStream() << cst <<
" ";
888 logger.getOStream() <<
") -> { ";
890 for (
auto &r : foldResults) {
891 logger.getOStream() << r <<
" ";
894 logger.getOStream() <<
"}\n";
901 if (foldResults.empty())
902 return visitOperation(op, changedField);
905 assert(foldResults.size() == op->getNumResults() &&
"invalid result size");
906 for (
unsigned i = 0, e = foldResults.size(); i != e; ++i) {
908 LatticeValue resultLattice;
909 OpFoldResult foldResult = foldResults[i];
910 if (Attribute foldAttr = dyn_cast<Attribute>(foldResult)) {
911 if (
auto intAttr = dyn_cast<IntegerAttr>(foldAttr))
912 resultLattice = LatticeValue(intAttr);
913 else if (
auto strAttr = dyn_cast<StringAttr>(foldAttr))
914 resultLattice = LatticeValue(strAttr);
916 resultLattice = LatticeValue::getOverdefined();
919 latticeValues[getOrCacheFieldRefFromValue(foldResult.get<Value>())];
922 mergeLatticeValue(getOrCacheFieldRefFromValue(op->getResult(i)),
927 void IMConstPropPass::rewriteModuleBody(FModuleOp module) {
928 auto *body = module.getBodyBlock();
930 if (!executableBlocks.count(body))
933 auto builder = OpBuilder::atBlockBegin(body);
937 auto cursor = builder.create<firrtl::ConstantOp>(module.getLoc(), APSInt(1));
938 builder.setInsertionPoint(cursor);
941 DenseMap<std::pair<Attribute, Type>, Operation *> constPool;
943 std::function<Value(Attribute, Type, Location)> getConst =
944 [&](Attribute constantValue, Type type, Location loc) -> Value {
945 auto constIt = constPool.find({constantValue, type});
946 if (constIt != constPool.end()) {
947 auto *cst = constIt->second;
949 cst->setLoc(builder.getFusedLoc({cst->getLoc(), loc}));
950 return cst->getResult(0);
952 OpBuilder::InsertionGuard x(builder);
953 builder.setInsertionPoint(cursor);
958 if (
auto refType = type_dyn_cast<RefType>(type)) {
959 assert(!type_cast<RefType>(type).getForceable() &&
960 "Attempting to materialize rwprobe of constant, shouldn't happen");
961 auto inner = getConst(constantValue, refType.getType(), loc);
963 cst = builder.create<RefSendOp>(loc, inner);
965 cst = module->getDialect()->materializeConstant(builder, constantValue,
967 assert(cst &&
"all FIRRTL constants can be materialized");
968 constPool.insert({{constantValue, type}, cst});
969 return cst->getResult(0);
974 auto replaceValueIfPossible = [&](Value value) ->
bool {
978 auto replaceIfNotConnect = [&value](Value replacement) {
979 value.replaceUsesWithIf(replacement, [](OpOperand &operand) {
980 return !isa<FConnectLike>(operand.getOwner()) ||
981 operand.getOperandNumber() != 0;
987 if (it == latticeValues.end() || it->second.isOverdefined() ||
988 it->second.isUnknown())
994 if (!type_isa<FIRRTLBaseType, RefType, FIntegerType, StringType, BoolType>(
999 getConst(it->second.getValue(), value.getType(), value.
getLoc());
1001 replaceIfNotConnect(cstValue);
1006 for (
auto &port : body->getArguments())
1007 replaceValueIfPossible(port);
1015 bool aboveCursor =
false;
1016 for (
auto &op : llvm::make_early_inc_range(llvm::reverse(*body))) {
1017 auto dropIfDead = [&](Operation &op,
const Twine &debugPrefix) {
1018 if (op.use_empty() &&
1021 { logger.getOStream() << debugPrefix <<
" : " << op <<
"\n"; });
1031 dropIfDead(op,
"Trivially dead materialized constant");
1035 if (&op == cursor) {
1042 if (
auto connect = dyn_cast<FConnectLike>(op)) {
1043 if (
auto *destOp =
connect.getDest().getDefiningOp()) {
1044 auto fieldRef = getOrCacheFieldRefFromValue(
connect.getDest());
1050 auto type = type_dyn_cast<FIRRTLType>(
connect.getDest().getType());
1053 auto baseType = type_dyn_cast<FIRRTLBaseType>(type);
1054 if (baseType && !baseType.isGround())
1066 if (op.getNumResults() != 1 && !isa<InstanceOp>(op))
1070 if (dropIfDead(op,
"Trivially dead"))
1075 if (op.hasTrait<mlir::OpTrait::ConstantLike>())
1079 builder.setInsertionPoint(&op);
1080 bool foldedAny =
false;
1081 for (
auto result : op.getResults())
1082 foldedAny |= replaceValueIfPossible(result);
1088 if (foldedAny && dropIfDead(op,
"Made dead"))
1094 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)