21#include "mlir/IR/Iterators.h"
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"
37using 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() <<
">";
185struct 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, DomainType>(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, DomainType>(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);
315 void markDPICallIntrinsicOp(DPICallIntrinsicOp dpi);
317 void markInvalidValueOp(InvalidValueOp invalid);
318 void markAggregateConstantOp(AggregateConstantOp constant);
319 void markInstanceOp(InstanceOp instance);
320 void markInstanceChoiceOp(InstanceChoiceOp instance);
321 void markObjectOp(ObjectOp
object);
322 template <
typename OpTy>
323 void markConstantValueOp(OpTy op);
325 void visitConnectLike(FConnectLike connect,
FieldRef changedFieldRef);
326 void visitRefSend(RefSendOp send,
FieldRef changedFieldRef);
328 void mergeOnlyChangedLatticeValue(Value dest, Value src,
330 void visitNode(NodeOp node,
FieldRef changedFieldRef);
331 void visitOperation(Operation *op,
FieldRef changedFieldRef);
338 DenseMap<FieldRef, LatticeValue> latticeValues;
341 SmallPtrSet<Block *, 16> executableBlocks;
345 SmallVector<FieldRef, 64> changedLatticeValueWorklist;
348 DenseMap<FieldRef, llvm::TinyPtrVector<Operation *>> fieldRefToUsers;
352 llvm::DenseMap<Value, FieldRef> valueToFieldRef;
356 DenseMap<BlockArgument, llvm::TinyPtrVector<Value>>
357 resultPortToInstanceResultMapping;
361 llvm::ScopedPrinter logger{llvm::dbgs()};
367void IMConstPropPass::runOnOperation() {
368 auto circuit = getOperation();
370 { logger.startLine() <<
"IMConstProp : " << circuit.getName() <<
"\n"; });
372 instanceGraph = &getAnalysis<InstanceGraph>();
375 for (
auto &op : circuit.getOps()) {
377 if (
auto module = dyn_cast<FModuleOp>(op)) {
378 if (module.isPublic()) {
379 markBlockExecutable(module.getBodyBlock());
381 markOverdefined(port);
388 if (isa<hw::HierPathOp>(op))
395 auto symbolUses = SymbolTable::getSymbolUses(&op);
398 for (
const auto &use : *symbolUses) {
399 if (
auto symRef = dyn_cast<FlatSymbolRefAttr>(use.getSymbolRef())) {
400 if (
auto *igNode = instanceGraph->lookupOrNull(symRef.getAttr())) {
401 if (
auto module = dyn_cast<FModuleOp>(*igNode->getModule())) {
402 LLVM_DEBUG(llvm::dbgs()
403 <<
"Unknown use of " << module.getModuleNameAttr()
404 <<
" in " << op.getName()
405 <<
", marking inputs as overdefined\n");
406 markBlockExecutable(module.getBodyBlock());
408 markOverdefined(port);
416 while (!changedLatticeValueWorklist.empty()) {
417 FieldRef changedFieldRef = changedLatticeValueWorklist.pop_back_val();
418 for (Operation *user : fieldRefToUsers[changedFieldRef]) {
419 if (isBlockExecutable(user->getBlock()))
420 visitOperation(user, changedFieldRef);
425 mlir::parallelForEach(circuit.getContext(),
426 circuit.getBodyBlock()->getOps<FModuleOp>(),
427 [&](
auto op) { rewriteModuleBody(op); });
430 instanceGraph =
nullptr;
431 latticeValues.clear();
432 executableBlocks.clear();
433 assert(changedLatticeValueWorklist.empty());
434 fieldRefToUsers.clear();
435 valueToFieldRef.clear();
436 resultPortToInstanceResultMapping.clear();
442LatticeValue IMConstPropPass::getExtendedLatticeValue(
FieldRef value,
444 bool allowTruncation) {
446 auto it = latticeValues.find(value);
447 if (it == latticeValues.end())
448 return LatticeValue();
450 auto result = it->second;
452 if (result.isUnknown() || result.isOverdefined())
456 if (isa<PropertyType, DomainType>(destType))
459 auto constant = result.getConstant();
462 auto intAttr = dyn_cast<IntegerAttr>(constant);
463 assert(intAttr &&
"unsupported lattice attribute kind");
468 if (
auto boolAttr = dyn_cast<BoolAttr>(intAttr))
474 return LatticeValue::getOverdefined();
477 auto resultConstant = intAttr.getAPSInt();
478 auto destWidth = baseType.getBitWidthOrSentinel();
480 return LatticeValue::getOverdefined();
481 if (resultConstant.getBitWidth() == (
unsigned)destWidth)
486 return LatticeValue(IntegerAttr::get(destType.getContext(), resultConstant));
493void IMConstPropPass::markBlockExecutable(Block *block) {
494 if (!executableBlocks.insert(block).second)
499 for (
auto ba : block->getArguments())
503 for (
auto &op : *block) {
505 TypeSwitch<Operation *>(&op)
506 .Case<RegOp, RegResetOp>(
507 [&](
auto reg) { markOverdefined(op.getResult(0)); })
508 .Case<WireOp>([&](
auto wire) { markWireOp(wire); })
509 .Case<ConstantOp, SpecialConstantOp, StringConstantOp,
510 FIntegerConstantOp, BoolConstantOp>(
511 [&](
auto constOp) { markConstantValueOp(constOp); })
512 .Case<AggregateConstantOp>(
513 [&](
auto aggConstOp) { markAggregateConstantOp(aggConstOp); })
514 .Case<InvalidValueOp>(
515 [&](
auto invalid) { markInvalidValueOp(invalid); })
516 .Case<InstanceOp>([&](
auto instance) { markInstanceOp(instance); })
517 .Case<InstanceChoiceOp>(
518 [&](
auto instance) { markInstanceChoiceOp(instance); })
519 .Case<ObjectOp>([&](
auto obj) { markObjectOp(obj); })
520 .Case<MemOp>([&](
auto mem) { markMemOp(mem); })
522 [&](
auto layer) { markBlockExecutable(layer.getBody(0)); })
523 .Case<DPICallIntrinsicOp>(
524 [&](
auto dpi) { markDPICallIntrinsicOp(dpi); })
525 .Default([&](
auto _) {
526 if (isa<mlir::UnrealizedConversionCastOp, VerbatimExprOp,
527 VerbatimWireOp, SubaccessOp>(op) ||
528 op.getNumOperands() == 0) {
532 for (
auto result : op.getResults())
533 markOverdefined(result);
545 bool hasAggregateOperand =
546 llvm::any_of(op.getOperandTypes(), [](Type type) {
547 return type_isa<FVectorType, BundleType>(type);
550 for (
auto result : op.getResults())
551 if (hasAggregateOperand ||
552 type_isa<FVectorType, BundleType>(result.getType()))
553 markOverdefined(result);
560 for (
auto operand : op.getOperands()) {
561 auto fieldRef = getOrCacheFieldRefFromValue(operand);
562 auto firrtlType = type_dyn_cast<FIRRTLType>(operand.getType());
566 if (type_isa<PropertyType, DomainType>(firrtlType)) {
567 fieldRefToUsers[fieldRef].push_back(&op);
571 fieldRefToUsers[fieldRef.
getSubField(fieldID)].push_back(&op);
579void IMConstPropPass::markWireOp(WireOp wire) {
580 auto type = type_dyn_cast<FIRRTLType>(wire.getResult().getType());
581 if (!type ||
hasDontTouch(wire.getResult()) || wire.isForceable()) {
582 for (
auto result : wire.getResults())
583 markOverdefined(result);
590void IMConstPropPass::markMemOp(MemOp mem) {
591 for (
auto result : mem.getResults())
592 markOverdefined(result);
595void IMConstPropPass::markDPICallIntrinsicOp(DPICallIntrinsicOp dpi) {
596 if (
auto result = dpi.getResult())
597 markOverdefined(result);
600template <
typename OpTy>
601void IMConstPropPass::markConstantValueOp(OpTy op) {
602 mergeLatticeValue(getOrCacheFieldRefFromValue(op),
603 LatticeValue(op.getValueAttr()));
606void IMConstPropPass::markAggregateConstantOp(AggregateConstantOp constant) {
607 walkGroundTypes(constant.getType(), [&](uint64_t fieldID,
auto,
auto) {
608 mergeLatticeValue(FieldRef(constant, fieldID),
609 LatticeValue(cast<IntegerAttr>(
610 constant.getAttributeFromFieldID(fieldID))));
614void IMConstPropPass::markInvalidValueOp(InvalidValueOp invalid) {
615 markOverdefined(invalid.getResult());
620void IMConstPropPass::markInstanceOp(InstanceOp instance) {
622 Operation *op = instance.getReferencedModule(*instanceGraph);
626 if (!isa<FModuleOp>(op)) {
627 auto module = dyn_cast<FModuleLike>(op);
628 for (
size_t resultNo = 0, e = instance.getNumResults(); resultNo != e;
630 auto portVal = instance.getResult(resultNo);
632 if (module.getPortDirection(resultNo) == Direction::In)
636 markOverdefined(portVal);
642 auto fModule = cast<FModuleOp>(op);
643 markBlockExecutable(fModule.getBodyBlock());
647 for (
size_t resultNo = 0, e = instance.getNumResults(); resultNo != e;
649 auto instancePortVal = instance.getResult(resultNo);
652 if (fModule.getPortDirection(resultNo) == Direction::In)
657 BlockArgument modulePortVal = fModule.getArgument(resultNo);
659 resultPortToInstanceResultMapping[modulePortVal].push_back(instancePortVal);
663 mergeLatticeValue(instancePortVal, modulePortVal);
667void IMConstPropPass::markObjectOp(ObjectOp obj) {
669 markOverdefined(obj);
672void IMConstPropPass::markInstanceChoiceOp(InstanceChoiceOp instance) {
676 for (
auto result : instance.getResults())
677 markOverdefined(result);
681 for (
auto moduleName : instance.getModuleNamesAttr()) {
683 instanceGraph->lookup(cast<FlatSymbolRefAttr>(moduleName).getAttr())
686 if (
auto fModule = dyn_cast<FModuleOp>(op)) {
687 markBlockExecutable(fModule.getBodyBlock());
689 for (
auto port : fModule.
getBodyBlock()->getArguments())
690 markOverdefined(port);
695static std::optional<uint64_t>
698 assert(!type_isa<RefType>(connectionType));
709void IMConstPropPass::mergeOnlyChangedLatticeValue(Value dest, Value src,
713 auto destType = dest.getType();
714 if (
auto refType = type_dyn_cast<RefType>(destType))
715 destType = refType.getType();
717 if (!isa<FIRRTLType>(destType)) {
720 markOverdefined(src);
721 return markOverdefined(dest);
724 auto fieldRefSrc = getOrCacheFieldRefFromValue(src);
725 auto fieldRefDest = getOrCacheFieldRefFromValue(dest);
729 if (
auto srcOffset =
getFieldIDOffset(changedFieldRef, destType, fieldRefSrc))
730 mergeLatticeValue(fieldRefDest.getSubField(*srcOffset),
731 fieldRefSrc.getSubField(*srcOffset));
735 if (
auto destOffset =
737 mergeLatticeValue(fieldRefDest.getSubField(*destOffset),
738 fieldRefSrc.getSubField(*destOffset));
741void IMConstPropPass::visitConnectLike(FConnectLike connect,
744 auto destType =
connect.getDest().getType();
745 if (
auto refType = type_dyn_cast<RefType>(destType))
746 destType = refType.getType();
749 if (!isa<FIRRTLType>(destType)) {
750 markOverdefined(
connect.getSrc());
751 return markOverdefined(
connect.getDest());
754 auto fieldRefSrc = getOrCacheFieldRefFromValue(
connect.getSrc());
755 auto fieldRefDest = getOrCacheFieldRefFromValue(
connect.getDest());
756 if (
auto subaccess = fieldRefDest.getValue().getDefiningOp<SubaccessOp>()) {
760 Value parent = subaccess.getInput();
761 while (parent.getDefiningOp() &&
762 parent.getDefiningOp()->getNumOperands() > 0)
763 parent = parent.getDefiningOp()->getOperand(0);
764 return markOverdefined(parent);
767 auto propagateElementLattice = [&](uint64_t fieldID,
FIRRTLType destType) {
768 auto fieldRefDestConnected = fieldRefDest.getSubField(fieldID);
769 assert(!firrtl::type_isa<FIRRTLBaseType>(destType) ||
770 firrtl::type_cast<FIRRTLBaseType>(destType).isGround());
774 getExtendedLatticeValue(fieldRefSrc.getSubField(fieldID), destType);
775 if (srcValue.isUnknown())
780 if (
auto blockArg = dyn_cast<BlockArgument>(fieldRefDest.getValue())) {
781 for (
auto userOfResultPort : resultPortToInstanceResultMapping[blockArg])
786 return mergeLatticeValue(fieldRefDestConnected, srcValue);
789 auto dest = cast<mlir::OpResult>(fieldRefDest.getValue());
794 return mergeLatticeValue(fieldRefDestConnected, srcValue);
798 if (
auto instance = dest.getDefiningOp<InstanceOp>()) {
800 mergeLatticeValue(fieldRefDestConnected, srcValue);
801 auto mod = instance.getReferencedModule<FModuleOp>(*instanceGraph);
805 BlockArgument modulePortVal = mod.getArgument(dest.getResultNumber());
807 return mergeLatticeValue(
808 FieldRef(modulePortVal, fieldRefDestConnected.getFieldID()),
813 if (isa_and_nonnull<InstanceChoiceOp, MemOp, ObjectSubfieldOp>(
814 dest.getDefiningOp()))
817 connect.emitError(
"connectlike operation unhandled by IMConstProp")
818 .attachNote(
connect.getDest().getLoc())
819 <<
"connect destination is here";
822 if (
auto srcOffset =
getFieldIDOffset(changedFieldRef, destType, fieldRefSrc))
823 propagateElementLattice(
825 firrtl::type_cast<FIRRTLType>(
828 if (
auto relativeDest =
830 propagateElementLattice(
832 firrtl::type_cast<FIRRTLType>(
836void IMConstPropPass::visitRefSend(RefSendOp send,
FieldRef changedFieldRef) {
838 return mergeOnlyChangedLatticeValue(send.getResult(), send.getBase(),
842void IMConstPropPass::visitRefResolve(RefResolveOp
resolve,
846 return mergeOnlyChangedLatticeValue(
resolve.getResult(),
resolve.getRef(),
850void IMConstPropPass::visitNode(NodeOp node,
FieldRef changedFieldRef) {
851 if (
hasDontTouch(node.getResult()) || node.isForceable()) {
852 for (
auto result : node.getResults())
853 markOverdefined(result);
857 return mergeOnlyChangedLatticeValue(node.getResult(), node.getInput(),
867void IMConstPropPass::visitOperation(Operation *op,
FieldRef changedField) {
869 if (
auto connectLikeOp = dyn_cast<FConnectLike>(op))
870 return visitConnectLike(connectLikeOp, changedField);
871 if (
auto sendOp = dyn_cast<RefSendOp>(op))
872 return visitRefSend(sendOp, changedField);
873 if (
auto resolveOp = dyn_cast<RefResolveOp>(op))
874 return visitRefResolve(resolveOp, changedField);
875 if (
auto nodeOp = dyn_cast<NodeOp>(op))
876 return visitNode(nodeOp, changedField);
887 auto isOverdefinedFn = [&](Value value) {
888 return isOverdefined(getOrCacheFieldRefFromValue(value));
890 if (llvm::all_of(op->getResults(), isOverdefinedFn))
895 if (op->getNumOperands() > 128) {
896 for (
auto value : op->getResults())
897 markOverdefined(value);
903 SmallVector<Attribute, 8> operandConstants;
904 operandConstants.reserve(op->getNumOperands());
905 bool hasUnknown =
false;
906 for (Value operand : op->getOperands()) {
908 auto &operandLattice = latticeValues[getOrCacheFieldRefFromValue(operand)];
913 if (operandLattice.isUnknown())
918 if (operandLattice.isConstant())
919 operandConstants.push_back(operandLattice.getValue());
921 operandConstants.push_back({});
926 SmallVector<OpFoldResult, 8> foldResults;
927 foldResults.reserve(op->getNumResults());
928 if (failed(op->fold(operandConstants, foldResults))) {
930 logger.startLine() <<
"Folding Failed operation : '" << op->getName()
936 for (
auto value : op->getResults())
937 markOverdefined(value);
942 logger.getOStream() <<
"\n";
943 logger.startLine() <<
"Folding operation : '" << op->getName() <<
"\n";
945 logger.getOStream() <<
"( ";
946 for (
auto cst : operandConstants)
948 logger.getOStream() <<
"{} ";
950 logger.getOStream() << cst <<
" ";
952 logger.getOStream() <<
") -> { ";
954 for (
auto &r : foldResults) {
955 logger.getOStream() << r <<
" ";
958 logger.getOStream() <<
"}\n";
965 if (foldResults.empty())
966 return visitOperation(op, changedField);
969 assert(foldResults.size() == op->getNumResults() &&
"invalid result size");
970 for (
unsigned i = 0, e = foldResults.size(); i != e; ++i) {
972 LatticeValue resultLattice;
973 OpFoldResult foldResult = foldResults[i];
974 if (Attribute foldAttr = dyn_cast<Attribute>(foldResult)) {
975 if (
auto intAttr = dyn_cast<IntegerAttr>(foldAttr))
976 resultLattice = LatticeValue(intAttr);
977 else if (
auto strAttr = dyn_cast<StringAttr>(foldAttr))
978 resultLattice = LatticeValue(strAttr);
980 resultLattice = LatticeValue::getOverdefined();
983 latticeValues[getOrCacheFieldRefFromValue(cast<Value>(foldResult))];
986 mergeLatticeValue(getOrCacheFieldRefFromValue(op->getResult(i)),
991void IMConstPropPass::rewriteModuleBody(FModuleOp module) {
992 auto *body =
module.getBodyBlock();
994 if (!executableBlocks.count(body))
997 auto builder = OpBuilder::atBlockBegin(body);
1001 auto cursor = firrtl::ConstantOp::create(builder, module.getLoc(), APSInt(1));
1002 builder.setInsertionPoint(cursor);
1005 DenseMap<std::pair<Attribute, Type>, Operation *> constPool;
1007 std::function<Value(Attribute, Type, Location)> getConst =
1008 [&](Attribute constantValue, Type type, Location loc) -> Value {
1009 auto constIt = constPool.find({constantValue, type});
1010 if (constIt != constPool.end()) {
1011 auto *cst = constIt->second;
1013 cst->setLoc(builder.getFusedLoc({cst->getLoc(), loc}));
1014 return cst->getResult(0);
1016 OpBuilder::InsertionGuard x(builder);
1017 builder.setInsertionPoint(cursor);
1022 if (
auto refType = type_dyn_cast<RefType>(type)) {
1023 assert(!type_cast<RefType>(type).getForceable() &&
1024 "Attempting to materialize rwprobe of constant, shouldn't happen");
1025 auto inner = getConst(constantValue, refType.getType(), loc);
1027 cst = RefSendOp::create(builder, loc, inner);
1029 cst =
module->getDialect()->materializeConstant(builder, constantValue,
1031 assert(cst &&
"all FIRRTL constants can be materialized");
1032 constPool.insert({{constantValue, type}, cst});
1033 return cst->getResult(0);
1038 auto replaceValueIfPossible = [&](Value value) ->
bool {
1042 auto replaceIfNotConnect = [&value](Value replacement) {
1043 value.replaceUsesWithIf(replacement, [](OpOperand &operand) {
1044 return !isa<FConnectLike>(operand.getOwner()) ||
1045 operand.getOperandNumber() != 0;
1051 if (it == latticeValues.end() || it->second.isOverdefined() ||
1052 it->second.isUnknown())
1058 if (!type_isa<FIRRTLBaseType, RefType, FIntegerType, StringType, BoolType>(
1063 getConst(it->second.getValue(), value.getType(), value.
getLoc());
1065 replaceIfNotConnect(cstValue);
1070 for (
auto &port : body->getArguments())
1071 replaceValueIfPossible(port);
1079 bool aboveCursor =
false;
1080 module.walk<mlir::WalkOrder::PostOrder, mlir::ReverseIterator>(
1081 [&](Operation *op) {
1082 auto dropIfDead = [&](Operation *op, const Twine &debugPrefix) {
1083 if (op->use_empty() &&
1084 (wouldOpBeTriviallyDead(op) || isDeletableWireOrRegOrNode(op))) {
1086 { logger.getOStream() << debugPrefix << " : " << *op << "\n"; });
1096 dropIfDead(op,
"Trivially dead materialized constant");
1097 return WalkResult::advance();
1103 return WalkResult::advance();
1107 if (
auto connect = dyn_cast<FConnectLike>(op)) {
1108 if (
auto *destOp =
connect.getDest().getDefiningOp()) {
1109 auto fieldRef = getOrCacheFieldRefFromValue(
connect.getDest());
1115 auto type = type_dyn_cast<FIRRTLType>(
connect.getDest().getType());
1117 return WalkResult::advance();
1118 auto baseType = type_dyn_cast<FIRRTLBaseType>(type);
1119 if (baseType && !baseType.isGround())
1120 return WalkResult::advance();
1122 !isOverdefined(fieldRef)) {
1127 return WalkResult::advance();
1132 if (op->getNumResults() != 1 && !isa<InstanceOp>(op))
1133 return WalkResult::advance();
1136 if (dropIfDead(op,
"Trivially dead"))
1137 return WalkResult::advance();
1141 if (op->hasTrait<mlir::OpTrait::ConstantLike>())
1142 return WalkResult::advance();
1145 builder.setInsertionPoint(op);
1146 bool foldedAny =
false;
1147 for (
auto result : op->getResults())
1148 foldedAny |= replaceValueIfPossible(result);
1154 if (foldedAny && dropIfDead(op,
"Made dead"))
1155 return WalkResult::advance();
1157 return WalkResult::advance();
assert(baseType &&"element must be base type")
static mlir::Operation * resolve(Context &context, mlir::SymbolRefAttr sym)
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 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 std::optional< uint64_t > getFieldIDOffset(FieldRef changedFieldRef, Type connectionType, FieldRef connectedValueFieldRef)
static bool isDeletableWireOrRegOrNode(Operation *op)
Return true if this is a wire or register we're allowed to delete.
static unsigned getFieldID(BundleType type, unsigned index)
static Block * getBodyBlock(FModuleLike mod)
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...
This graph tracks modules and where they are instantiated.
connect(destination, source)
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.
bool hasDontTouch(Value value)
Check whether a block argument ("port") or the operation defining a value has a DontTouch annotation,...
llvm::raw_ostream & operator<<(llvm::raw_ostream &os, const InstanceInfo::LatticeValue &value)
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)
static bool operator==(const ModulePort &a, const ModulePort &b)
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.
bool operator!=(uint64_t a, const FVInt &b)
reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)