14#include "mlir/Analysis/Liveness.h"
15#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
16#include "mlir/IR/Dominance.h"
17#include "llvm/ADT/ArrayRef.h"
18#include "llvm/Support/Debug.h"
19#include "llvm/Support/GenericIteratedDominanceFrontier.h"
21#define DEBUG_TYPE "llhd-mem2reg"
25#define GEN_PASS_DEF_MEM2REGPASS
26#include "circt/Dialect/LLHD/LLHDPasses.h.inc"
34using llvm::PointerIntPair;
35using llvm::SmallDenseSet;
37using llvm::SpecificBumpPtrAllocator;
41 if (
auto timeOp = value.getDefiningOp<ConstantTimeOp>()) {
42 auto t = timeOp.getValue();
43 return t.getTime() == 0 && t.getDelta() == 0 && t.getEpsilon() == 1;
50 if (
auto timeOp = value.getDefiningOp<ConstantTimeOp>()) {
51 auto t = timeOp.getValue();
52 return t.getTime() == 0 && t.getDelta() == 1 && t.getEpsilon() == 0;
60 if (
auto driveOp = dyn_cast<DriveOp>(op))
68 if (
auto driveOp = dyn_cast<DriveOp>(op))
82struct DriveCondition {
83 static DriveCondition never() {
return ConditionAndMode(Value{},
Never); }
84 static DriveCondition always() {
return ConditionAndMode(Value{}, Always); }
85 static DriveCondition conditional(Value condition = {}) {
86 return ConditionAndMode(condition, Conditional);
89 bool isNever()
const {
return conditionAndMode.getInt() ==
Never; }
90 bool isAlways()
const {
return conditionAndMode.getInt() == Always; }
91 bool isConditional()
const {
92 return conditionAndMode.getInt() == Conditional;
95 Value getCondition()
const {
return conditionAndMode.getPointer(); }
96 void setCondition(Value condition) { conditionAndMode.setPointer(condition); }
98 bool operator==(
const DriveCondition &other)
const {
99 return conditionAndMode == other.conditionAndMode;
101 bool operator!=(
const DriveCondition &other)
const {
102 return conditionAndMode != other.conditionAndMode;
111 typedef PointerIntPair<Value, 2> ConditionAndMode;
112 ConditionAndMode conditionAndMode;
114 DriveCondition(ConditionAndMode conditionAndMode)
115 : conditionAndMode(conditionAndMode) {}
116 friend DenseMapInfo<DriveCondition>;
128 DriveCondition condition;
129 bool valueIsPlaceholder =
false;
130 bool conditionIsPlaceholder =
false;
132 Def(Value value, DriveCondition condition)
133 : block(value.getParentBlock()), type(value.getType()), value(value),
134 condition(condition) {}
135 Def(Block *block, Type type, DriveCondition condition)
136 : block(block), type(type), condition(condition) {}
138 Value getValueOrPlaceholder();
139 Value getConditionOrPlaceholder();
145Value Def::getValueOrPlaceholder() {
147 auto builder = OpBuilder::atBlockBegin(block);
148 value = UnrealizedConversionCastOp::create(builder, builder.getUnknownLoc(),
151 valueIsPlaceholder =
true;
160Value Def::getConditionOrPlaceholder() {
161 if (!condition.getCondition()) {
162 auto builder = OpBuilder::atBlockBegin(block);
164 if (condition.isNever()) {
166 builder.getI1Type(), 0);
167 }
else if (condition.isAlways()) {
169 builder.getI1Type(), 1);
172 UnrealizedConversionCastOp::create(builder, builder.getUnknownLoc(),
173 builder.getI1Type(), ValueRange{})
175 conditionIsPlaceholder =
true;
177 condition.setCondition(value);
179 return condition.getCondition();
195 static bool isEqual(DriveCondition lhs, DriveCondition rhs) {
213 return cast<RefType>(slot.getType()).getNestedType();
236 LatticeNode *nodeBefore =
nullptr;
237 LatticeNode *nodeAfter =
nullptr;
239 Def *blockingReachingDef =
nullptr;
240 Def *delayedReachingDef =
nullptr;
243 Def *getReachingDef(
bool delayed)
const {
244 return delayed ? delayedReachingDef : blockingReachingDef;
246 void setReachingDef(
bool delayed, Def *def) {
247 (delayed ? delayedReachingDef : blockingReachingDef) = def;
252 enum class Kind { BlockEntry, BlockExit, Probe, Drive, Signal };
256 LatticeNode(Kind kind) : kind(kind) {}
259struct BlockEntry :
public LatticeNode {
261 LatticeValue *valueAfter;
262 SmallVector<BlockExit *, 2> predecessors;
266 Def *insertedProbe =
nullptr;
269 Def *blockingMerged =
nullptr;
270 Def *delayedMerged =
nullptr;
272 BlockEntry(Block *block, LatticeValue *valueAfter)
273 : LatticeNode(Kind::BlockEntry), block(block), valueAfter(valueAfter) {
274 assert(!valueAfter->nodeBefore);
275 valueAfter->nodeBefore =
this;
278 Def *getMerged(
bool delayed)
const {
279 return delayed ? delayedMerged : blockingMerged;
281 void setMerged(
bool delayed, Def *def) {
282 (delayed ? delayedMerged : blockingMerged) = def;
285 static bool classof(
const LatticeNode *n) {
286 return n->kind == Kind::BlockEntry;
290struct BlockExit :
public LatticeNode {
292 LatticeValue *valueBefore;
293 SmallVector<BlockEntry *, 2> successors;
294 Operation *terminator;
297 BlockExit(Block *block, LatticeValue *valueBefore)
298 : LatticeNode(Kind::BlockExit), block(block), valueBefore(valueBefore),
299 terminator(block->getTerminator()),
300 suspends(isa<HaltOp, WaitOp>(terminator)) {
301 assert(!valueBefore->nodeAfter);
302 valueBefore->nodeAfter =
this;
305 static bool classof(
const LatticeNode *n) {
306 return n->kind == Kind::BlockExit;
310struct OpNode :
public LatticeNode {
312 LatticeValue *valueBefore;
313 LatticeValue *valueAfter;
315 OpNode(Kind kind, Operation *op, LatticeValue *valueBefore,
316 LatticeValue *valueAfter)
317 : LatticeNode(kind), op(op), valueBefore(valueBefore),
318 valueAfter(valueAfter) {
319 assert(!valueBefore->nodeAfter);
320 assert(!valueAfter->nodeBefore);
321 valueBefore->nodeAfter =
this;
322 valueAfter->nodeBefore =
this;
325 static bool classof(
const LatticeNode *n) {
326 return isa<ProbeNode, DriveNode, SignalNode>(n);
330struct ProbeNode :
public OpNode {
333 ProbeNode(ProbeOp op, Value slot, LatticeValue *valueBefore,
334 LatticeValue *valueAfter)
335 : OpNode(Kind::Probe, op, valueBefore, valueAfter), slot(slot) {}
337 static bool classof(
const LatticeNode *n) {
return n->kind == Kind::Probe; }
340struct DriveNode :
public OpNode {
344 DriveNode(DriveOp op, Value slot, Def *def, LatticeValue *valueBefore,
345 LatticeValue *valueAfter)
346 : OpNode(Kind::Drive, op, valueBefore, valueAfter),
354 bool drivesProjection()
const {
return op->getOperand(0) !=
getSlot(slot); }
357 DriveOp getDriveOp()
const {
return cast<DriveOp>(op); }
359 static bool classof(
const LatticeNode *n) {
return n->kind == Kind::Drive; }
362struct SignalNode :
public OpNode {
365 SignalNode(SignalOp op, Def *def, LatticeValue *valueBefore,
366 LatticeValue *valueAfter)
367 : OpNode(Kind::Signal, op, valueBefore, valueAfter), def(def) {}
369 SignalOp getSignalOp()
const {
return cast<SignalOp>(op); }
372 static bool classof(
const LatticeNode *n) {
return n->kind == Kind::Signal; }
379 LatticeValue *createValue() {
380 auto *value =
new (valueAllocator.Allocate()) LatticeValue();
381 values.push_back(value);
386 template <
class T,
typename... Args>
387 T *createNode(Args... args) {
389 new (getAllocator<T>().Allocate()) T(std::forward<Args>(args)...);
390 nodes.push_back(node);
395 template <
typename... Args>
396 Def *createDef(Args... args) {
397 auto *def =
new (defAllocator.Allocate()) Def(std::forward<Args>(args)...);
403 Def *createDef(Value value, DriveCondition mode) {
404 auto &slot = defsForValues[{value, mode}];
406 slot =
new (defAllocator.Allocate()) Def(value, mode);
407 defs.push_back(slot);
413 void dump(llvm::raw_ostream &os = llvm::dbgs());
417 std::vector<LatticeNode *> nodes;
419 std::vector<LatticeValue *> values;
421 std::vector<Def *> defs;
425 DenseMap<std::pair<Value, DriveCondition>, Def *> defsForValues;
428 SpecificBumpPtrAllocator<LatticeValue> valueAllocator;
429 SpecificBumpPtrAllocator<Def> defAllocator;
430 SpecificBumpPtrAllocator<BlockEntry> blockEntryAllocator;
431 SpecificBumpPtrAllocator<BlockExit> blockExitAllocator;
432 SpecificBumpPtrAllocator<ProbeNode> probeAllocator;
433 SpecificBumpPtrAllocator<DriveNode> driveAllocator;
434 SpecificBumpPtrAllocator<SignalNode> signalAllocator;
438 SpecificBumpPtrAllocator<T> &getAllocator();
444SpecificBumpPtrAllocator<BlockEntry> &Lattice::getAllocator() {
445 return blockEntryAllocator;
448SpecificBumpPtrAllocator<BlockExit> &Lattice::getAllocator() {
449 return blockExitAllocator;
452SpecificBumpPtrAllocator<ProbeNode> &Lattice::getAllocator() {
453 return probeAllocator;
456SpecificBumpPtrAllocator<DriveNode> &Lattice::getAllocator() {
457 return driveAllocator;
460SpecificBumpPtrAllocator<SignalNode> &Lattice::getAllocator() {
461 return signalAllocator;
468void Lattice::dump(llvm::raw_ostream &os) {
474 auto blockName = [&](
Block *block) {
475 unsigned id = blockNames.insert({block, blockNames.size()}).first->second;
476 return std::string(
"bb") + llvm::utostr(
id);
479 auto memName = [&](
DefSlot value) {
481 memNames.insert({
getSlot(value), memNames.size()}).first->second;
482 return std::string(
"mem") + llvm::utostr(
id) +
486 auto defName = [&](Def *def) {
487 unsigned id = defNames.insert({def, defNames.size()}).first->second;
488 return std::string(
"def") + llvm::utostr(
id);
492 for (
auto *node : nodes)
493 if (auto *entry = dyn_cast<BlockEntry>(node))
494 blockName(entry->block);
498 for (
auto *node : nodes) {
499 auto *entry = dyn_cast<BlockEntry>(node);
504 os <<
" " << blockName(entry->block) <<
":";
505 if (entry->predecessors.empty()) {
506 os <<
" // no predecessors";
509 for (
auto *node : entry->predecessors)
510 os <<
" " << blockName(node->block);
515 auto *value = entry->valueAfter;
520 auto printDef = [&](
bool delayed, Def *def) {
523 os <<
" -> def (" << (delayed ?
"delayed" :
"blocking")
524 <<
")=" << defName(def);
525 if (def->condition.isNever())
527 else if (def->condition.isAlways())
533 printDef(
false, value->blockingReachingDef);
534 printDef(
true, value->delayedReachingDef);
535 if (isa<BlockExit>(value->nodeAfter))
539 if (
auto *node = dyn_cast<ProbeNode>(value->nodeAfter))
540 os <<
" probe " << memName(
blockingSlot(node->slot)) <<
"\n";
541 else if (
auto *node = dyn_cast<DriveNode>(value->nodeAfter))
542 os <<
" drive " << memName(node->slot) <<
"\n";
543 else if (
auto *node = dyn_cast<SignalNode>(value->nodeAfter))
544 os <<
" signal " << memName(node->getSlot()) <<
"\n";
549 value = cast<OpNode>(value->nodeAfter)->valueAfter;
553 auto *exit = cast<BlockExit>(value->nodeAfter);
554 if (isa<WaitOp>(exit->terminator))
556 else if (exit->successors.empty())
560 for (
auto *node : exit->successors)
561 os <<
" " << blockName(node->block);
563 os <<
" // suspends";
568 for (
auto [mem,
id] : memNames)
569 os <<
" mem" << id <<
": " << mem <<
"\n";
603 while (fromSignal != toSlot) {
604 auto *op = cast<OpResult>(fromSignal).getOwner();
605 stack.push_back({op, Value()});
606 fromSignal = op->getOperand(0);
619 for (
auto &projection : llvm::reverse(projections)) {
620 projection.into = value;
621 value = TypeSwitch<Operation *, Value>(projection.op)
622 .Case<SigArrayGetOp>([&](
auto op) {
624 op.getLoc(), value, op.getIndex());
626 .Case<SigStructExtractOp>([&](
auto op) {
629 if (isa<hw::UnionType>(value.getType()))
630 return builder.createOrFold<hw::UnionExtractOp>(
631 op.getLoc(), value, op.getFieldAttr());
633 op.getLoc(), value, op.getFieldAttr());
635 .Case<SigExtractOp>([&](
auto op) {
636 auto type = cast<RefType>(op.getType()).getNestedType();
637 auto width = type.getIntOrFloatBitWidth();
638 return comb::createDynamicExtract(builder, op.getLoc(), value,
639 op.getLowBit(), width);
659 for (
auto projection : projections) {
660 value = TypeSwitch<Operation *, Value>(projection.op)
661 .Case<SigArrayGetOp>([&](
auto op) {
662 return builder.createOrFold<hw::ArrayInjectOp>(
663 op.getLoc(), projection.into, op.getIndex(), value);
665 .Case<SigStructExtractOp>([&](
auto op) {
669 dyn_cast<hw::UnionType>(projection.into.getType()))
670 return builder.createOrFold<hw::UnionCreateOp>(
671 op.getLoc(), unionTy, op.getFieldAttr(), value);
672 return builder.createOrFold<hw::StructInjectOp>(
673 op.getLoc(), projection.into, op.getFieldAttr(), value);
675 .Case<SigExtractOp>([&](
auto op) {
676 return comb::createDynamicInject(builder, op.getLoc(),
678 op.getLowBit(), value);
691 Promoter(Region ®ion) : region(region) {}
692 LogicalResult promote();
694 void findPromotableSlots();
695 Value resolveSlot(Value projectionOrSlot);
696 void populateSlotOps();
698 void captureAcrossWait();
699 void captureAcrossWait(Value value, ArrayRef<WaitOp> waitOps,
700 Liveness &liveness, DominanceInfo &dominance);
702 void constructLattice();
703 void propagateBackward();
704 void propagateBackward(LatticeNode *node);
705 void propagateForward();
706 void propagateForward(
bool optimisticMerges, DominanceInfo &dominance);
707 void propagateForward(LatticeNode *node,
bool optimisticMerges,
708 DominanceInfo &dominance);
709 void markDirty(LatticeNode *node);
711 void insertProbeBlocks();
713 void insertProbes(BlockEntry *node);
715 void insertDriveBlocks();
717 void insertDrives(BlockExit *node);
718 void insertDrives(DriveNode *node);
720 void resolveDefinitions();
721 void resolveDefinitions(ProbeNode *node);
722 void resolveDefinitions(DriveNode *node);
723 void resolveDefinitionValue(DriveNode *node);
724 void resolveDefinitionCondition(DriveNode *node);
726 void insertBlockArgs();
727 bool insertBlockArgs(BlockEntry *node);
728 void replaceValueWith(Value oldValue, Value newValue);
732 void removeUnusedLocalSignal(SignalOp signalOp);
743 SmallVector<Value> slots;
748 SmallDenseSet<Value> promotable;
757 DenseMap<Block *, ConstantTimeOp> blockingTimeCache;
758 DenseMap<Block *, ConstantTimeOp> delayedTimeCache;
763 std::optional<Lattice> lattice;
765 SmallVector<LatticeNode *> dirtyNodes;
772 DenseMap<Value, SmallVector<Operation *>> slotOps;
776LogicalResult Promoter::promote() {
780 findPromotableSlots();
795 for (
auto slot : slots) {
808void Promoter::promoteSlot() {
809 assert(currentSlot &&
"currentSlot must be set before promoteSlot()");
813 LLVM_DEBUG(llvm::dbgs() <<
"Promoting slot " << currentSlot <<
"\n");
817 llvm::dbgs() <<
"Initial lattice:\n";
824 llvm::dbgs() <<
"After backward propagation:\n";
832 llvm::dbgs() <<
"After probe insertion:\n";
839 llvm::dbgs() <<
"After forward propagation:\n";
844 resolveDefinitions();
850 llvm::dbgs() <<
"After def resolution and drive insertion:\n";
859 if (
auto signalOp = currentSlot.getDefiningOp<SignalOp>())
860 if (signalOp->getParentRegion() == ®ion)
861 removeUnusedLocalSignal(signalOp);
869void Promoter::findPromotableSlots() {
870 SmallPtrSet<Value, 8> seenSlots;
871 SmallPtrSet<Operation *, 8> checkedUsers;
872 SmallVector<Operation *, 8> userWorklist;
874 region.walk([&](Operation *op) {
875 for (
auto operand : op->getOperands()) {
876 if (!seenSlots.insert(operand).second)
882 if (!operand.getDefiningOp<llhd::SignalOp>())
886 bool hasProjection =
false;
887 bool hasBlockingDrive =
false;
888 bool hasDeltaDrive =
false;
889 auto checkUser = [&](Operation *user) ->
bool {
891 if (region.isProperAncestor(user->getParentRegion()))
894 if (user->getParentRegion() != ®ion)
900 if (isa<SigArrayGetOp, SigExtractOp, SigStructExtractOp>(user)) {
901 hasProjection =
true;
902 for (
auto *projectionUser : user->getUsers()) {
903 if (isa<SigArrayGetOp, SigExtractOp, SigStructExtractOp>(
905 projectionUser->getBlock() != user->getBlock())
909 if (checkedUsers.insert(projectionUser).second)
910 userWorklist.push_back(projectionUser);
912 projections.insert({user->getResult(0), operand});
920 checkedUsers.clear();
921 if (!llvm::all_of(operand.getUsers(), [&](
auto *user) {
923 if (checkedUsers.insert(user).second)
924 userWorklist.push_back(user);
925 while (!userWorklist.empty() && allOk)
926 allOk &= checkUser(userWorklist.pop_back_val());
927 userWorklist.clear();
935 if (hasProjection && hasBlockingDrive && hasDeltaDrive)
943 static_cast<unsigned>(bitWidth) > IntegerType::kMaxWidth)
946 slots.push_back(operand);
951 promotable.insert(slots.begin(), slots.end());
952 for (
auto [projection, slot] :
llvm::make_early_inc_range(projections))
953 if (!promotable.contains(slot))
954 projections.erase(projection);
955 for (
auto [projection, slot] : projections)
956 promotable.insert(projection);
958 LLVM_DEBUG(llvm::dbgs() <<
"Found " << slots.size() <<
" promotable slots, "
959 << promotable.size() <<
" promotable values\n");
964Value Promoter::resolveSlot(Value projectionOrSlot) {
965 if (
auto slot = projections.lookup(projectionOrSlot))
967 return projectionOrSlot;
974void Promoter::captureAcrossWait() {
975 if (region.hasOneBlock())
978 SmallVector<WaitOp> waitOps;
979 for (
auto &block : region)
980 if (auto waitOp = dyn_cast<WaitOp>(block.getTerminator()))
981 waitOps.push_back(waitOp);
983 DominanceInfo dominance(region.getParentOp());
984 Liveness liveness(region.getParentOp());
986 llvm::DenseSet<Value> alreadyCaptured;
988 auto isDefinedInRegion = [&](Value v) {
989 return v.getParentRegion() == ®ion;
992 for (
auto waitOp : waitOps) {
993 Block *waitBlock = waitOp->getBlock();
994 const auto &liveOutValues = liveness.getLiveOut(waitBlock);
996 for (Value v : liveOutValues) {
997 if (!isDefinedInRegion(v))
1000 if (!alreadyCaptured.insert(v).second)
1003 captureAcrossWait(v, waitOps, liveness, dominance);
1012void Promoter::captureAcrossWait(Value value, ArrayRef<WaitOp> waitOps,
1013 Liveness &liveness, DominanceInfo &dominance) {
1015 llvm::dbgs() <<
"Capture " << value <<
"\n";
1016 for (
auto waitOp : waitOps)
1017 llvm::dbgs() <<
"- Across " << waitOp <<
"\n";
1022 auto &domTree = dominance.getDomTree(®ion);
1023 llvm::IDFCalculatorBase<Block, false> idfCalculator(domTree);
1027 SmallPtrSet<Block *, 4> definingBlocks;
1028 definingBlocks.insert(value.getParentBlock());
1029 for (
auto waitOp : waitOps)
1030 definingBlocks.insert(waitOp.getDest());
1031 idfCalculator.setDefiningBlocks(definingBlocks);
1034 SmallPtrSet<Block *, 16> liveInBlocks;
1035 for (
auto &block : region)
1036 if (liveness.getLiveness(&block)->isLiveIn(value))
1037 liveInBlocks.insert(&block);
1038 idfCalculator.setLiveInBlocks(liveInBlocks);
1042 SmallVector<Block *> mergePointsVec;
1043 idfCalculator.calculate(mergePointsVec);
1044 SmallPtrSet<Block *, 16> mergePoints(mergePointsVec.begin(),
1045 mergePointsVec.end());
1046 for (
auto waitOp : waitOps)
1047 mergePoints.insert(waitOp.getDest());
1048 LLVM_DEBUG(llvm::dbgs() <<
"- " << mergePoints.size() <<
" merge points\n");
1053 struct WorklistItem {
1054 DominanceInfoNode *domNode;
1057 SmallVector<WorklistItem> worklist;
1058 worklist.push_back({domTree.getNode(value.getParentBlock()), value});
1060 while (!worklist.empty()) {
1061 auto item = worklist.pop_back_val();
1062 auto *block = item.domNode->getBlock();
1065 if (mergePoints.contains(block))
1066 item.reachingDef = block->addArgument(value.getType(), value.getLoc());
1070 for (
auto &op : *block)
1071 op.replaceUsesOfWith(value, item.reachingDef);
1075 if (
auto branchOp = dyn_cast<BranchOpInterface>(block->getTerminator())) {
1076 for (
auto &blockOperand : branchOp->getBlockOperands())
1077 if (mergePoints.contains(blockOperand.
get()))
1078 branchOp.getSuccessorOperands(blockOperand.getOperandNumber())
1079 .
append(item.reachingDef);
1080 }
else if (
auto waitOp = dyn_cast<WaitOp>(block->getTerminator())) {
1081 if (mergePoints.contains(waitOp.getDest()))
1082 waitOp.getDestOperandsMutable().append(item.reachingDef);
1085 for (
auto *child : item.domNode->children())
1086 worklist.push_back({child, item.reachingDef});
1098void Promoter::populateSlotOps() {
1099 for (
auto &block : region) {
1100 for (
auto &op : block.without_terminator()) {
1101 Value slot = TypeSwitch<Operation *, Value>(&op)
1102 .Case<ProbeOp, DriveOp>([&](
auto op) {
1103 if (promotable.contains(op.getSignal()))
1104 return resolveSlot(op.getSignal());
1107 .Case([&](SignalOp op) {
return op.getResult(); })
1108 .Default([&](Operation *) {
return Value(); });
1110 slotOps[slot].push_back(&op);
1119void Promoter::constructLattice() {
1120 assert(currentSlot &&
"constructLattice requires currentSlot");
1124 ArrayRef<Operation *> currentSlotOps = slotOps[currentSlot];
1128 for (
auto &block : region) {
1130 lattice->createNode<BlockEntry>(&block, lattice->createValue());
1131 blockEntries.insert({&block, entry});
1135 for (
auto &block : region) {
1136 auto *valueBefore = blockEntries.lookup(&block)->valueAfter;
1140 ArrayRef<Operation *> blockOps = currentSlotOps.take_while(
1141 [&](Operation *op) {
return op->getBlock() == █ });
1142 currentSlotOps = currentSlotOps.drop_front(blockOps.size());
1144 for (Operation *op : blockOps) {
1146 if (
auto probeOp = dyn_cast<ProbeOp>(op)) {
1147 if (!promotable.contains(probeOp.getSignal()))
1149 if (resolveSlot(probeOp.getSignal()) != currentSlot)
1151 auto *node = lattice->createNode<ProbeNode>(
1152 probeOp, currentSlot, valueBefore, lattice->createValue());
1153 valueBefore = node->valueAfter;
1158 if (
auto driveOp = dyn_cast<DriveOp>(op)) {
1161 if (!promotable.contains(driveOp.getSignal()))
1163 if (resolveSlot(driveOp.getSignal()) != currentSlot)
1165 auto condition = DriveCondition::always();
1166 if (
auto enable = driveOp.getEnable())
1167 condition = DriveCondition::conditional(enable);
1172 driveOp.getSignal() == currentSlot
1173 ? lattice->createDef(driveOp.getValue(), condition)
1174 : lattice->createDef(driveOp->getBlock(),
1176 auto *node = lattice->createNode<DriveNode>(
1177 driveOp, currentSlot, def, valueBefore, lattice->createValue());
1178 valueBefore = node->valueAfter;
1184 if (
auto signalOp = dyn_cast<SignalOp>(op)) {
1185 if (signalOp.getResult() != currentSlot)
1188 lattice->createDef(signalOp.getInit(), DriveCondition::never());
1189 auto *node = lattice->createNode<SignalNode>(signalOp, def, valueBefore,
1190 lattice->createValue());
1191 valueBefore = node->valueAfter;
1197 auto *exit = lattice->createNode<BlockExit>(&block, valueBefore);
1198 for (
auto *otherBlock : exit->terminator->getSuccessors()) {
1199 auto *otherEntry = blockEntries.lookup(otherBlock);
1200 exit->successors.push_back(otherEntry);
1201 otherEntry->predecessors.push_back(exit);
1208void Promoter::propagateBackward() {
1209 for (
auto *node : lattice->nodes)
1210 propagateBackward(node);
1211 SmallVector<LatticeNode *> nodes;
1212 while (!dirtyNodes.empty()) {
1213 std::swap(dirtyNodes, nodes);
1214 for (
auto *node : nodes) {
1215 node->dirty =
false;
1216 propagateBackward(node);
1224void Promoter::propagateBackward(LatticeNode *node) {
1225 auto update = [&](LatticeValue *value,
bool needed) {
1226 if (value->needed != needed) {
1227 value->needed = needed;
1228 markDirty(value->nodeBefore);
1233 if (
auto *probe = dyn_cast<ProbeNode>(node)) {
1234 update(probe->valueBefore,
true);
1244 if (
auto *drive = dyn_cast<DriveNode>(node)) {
1245 bool needed = drive->valueAfter->needed;
1246 if (drive->drivesProjection())
1248 else if (!
isDelayed(drive->slot) && !drive->getDriveOp().getEnable())
1250 update(drive->valueBefore, needed);
1257 if (isa<SignalNode>(node)) {
1258 auto *signal = cast<SignalNode>(node);
1259 update(signal->valueBefore,
false);
1264 if (
auto *entry = dyn_cast<BlockEntry>(node)) {
1265 for (
auto *predecessor : entry->predecessors)
1266 markDirty(predecessor);
1271 if (
auto *exit = dyn_cast<BlockExit>(node)) {
1274 bool needed =
false;
1275 for (
auto *successor : exit->successors)
1276 needed |= successor->valueAfter->needed;
1277 update(exit->valueBefore, needed);
1281 assert(
false &&
"unhandled node in backward propagation");
1284void Promoter::propagateForward() {
1285 DominanceInfo dominance(region.getParentOp());
1286 propagateForward(
true, dominance);
1287 propagateForward(
false, dominance);
1298void Promoter::propagateForward(
bool optimisticMerges,
1299 DominanceInfo &dominance) {
1300 for (
auto *node : lattice->nodes)
1301 propagateForward(node, optimisticMerges, dominance);
1302 SmallVector<LatticeNode *> nodes;
1303 while (!dirtyNodes.empty()) {
1304 std::swap(dirtyNodes, nodes);
1305 for (
auto *node : nodes) {
1306 node->dirty =
false;
1307 propagateForward(node, optimisticMerges, dominance);
1316void Promoter::propagateForward(LatticeNode *node,
bool optimisticMerges,
1317 DominanceInfo &dominance) {
1318 auto update = [&](LatticeValue *value, Def *blocking, Def *delayed) {
1319 if (value->blockingReachingDef != blocking ||
1320 value->delayedReachingDef != delayed) {
1321 value->blockingReachingDef = blocking;
1322 value->delayedReachingDef = delayed;
1323 markDirty(value->nodeAfter);
1328 if (
auto *probe = dyn_cast<ProbeNode>(node)) {
1329 update(probe->valueAfter, probe->valueBefore->blockingReachingDef,
1330 probe->valueBefore->delayedReachingDef);
1344 if (
auto *drive = dyn_cast<DriveNode>(node)) {
1345 Def *blocking = drive->valueBefore->blockingReachingDef;
1346 Def *delayed = drive->valueBefore->delayedReachingDef;
1347 bool driveDelayed =
isDelayed(drive->slot);
1353 if (drive->drivesProjection() || drive->getDriveOp().getEnable()) {
1354 Def *inDef = driveDelayed ? delayed : blocking;
1356 if (drive->def->value != inDef->value)
1357 drive->def->value = {};
1358 if (inDef->condition.isAlways() || drive->def->condition.isAlways())
1359 drive->def->condition = DriveCondition::always();
1360 else if (drive->def->condition != inDef->condition)
1361 drive->def->condition = DriveCondition::conditional();
1366 delayed = drive->def;
1368 blocking = drive->def;
1372 update(drive->valueAfter, blocking, delayed);
1378 if (
auto *signal = dyn_cast<SignalNode>(node)) {
1379 update(signal->valueAfter, signal->def,
nullptr);
1385 if (
auto *entry = dyn_cast<BlockEntry>(node)) {
1388 if (entry->insertedProbe) {
1389 update(entry->valueAfter, entry->insertedProbe, entry->insertedProbe);
1396 Block *slotBlock = currentSlot.getDefiningOp()->getBlock();
1397 bool slotDominates = dominance.dominates(slotBlock, entry->block);
1398 bool anyPredSuspends =
1399 llvm::any_of(entry->predecessors, [](
auto *p) { return p->suspends; });
1401 auto mergeFlavor = [&](
bool delayed) -> Def * {
1402 if (!slotDominates || anyPredSuspends)
1406 if (llvm::all_of(entry->predecessors, [&](
auto *p) {
1407 return !p->valueBefore->getReachingDef(delayed);
1413 Def *common =
nullptr;
1414 DriveCondition cond = DriveCondition::never();
1416 for (
auto *pred : entry->predecessors) {
1417 Def *predDef = pred->valueBefore->getReachingDef(delayed);
1418 if (!predDef && optimisticMerges)
1420 DriveCondition predCond =
1421 predDef ? predDef->condition : DriveCondition::never();
1428 if (common != predDef)
1430 if (cond != predCond)
1431 cond = DriveCondition::conditional();
1442 Def *&merged = delayed ? entry->delayedMerged : entry->blockingMerged;
1444 merged->condition = cond;
1455 merged = lattice->createDef(entry->block,
getStoredType(slot), cond);
1459 Def *newBlocking = mergeFlavor(
false);
1460 Def *newDelayed = mergeFlavor(
true);
1461 update(entry->valueAfter, newBlocking, newDelayed);
1466 if (
auto *exit = dyn_cast<BlockExit>(node)) {
1467 for (
auto *successor : exit->successors)
1468 markDirty(successor);
1472 assert(
false &&
"unhandled node in forward propagation");
1476void Promoter::markDirty(LatticeNode *node) {
1481 dirtyNodes.push_back(node);
1492void Promoter::insertProbeBlocks() {
1496 SmallDenseSet<std::pair<BlockExit *, BlockEntry *>, 1> worklist;
1497 for (
auto *node : lattice->nodes) {
1498 auto *entry = dyn_cast<BlockEntry>(node);
1499 if (!entry || !entry->valueAfter->needed)
1501 unsigned numIncoming = 0;
1502 for (
auto *predecessor : entry->predecessors)
1503 if (predecessor->valueBefore->needed)
1505 if (numIncoming == 0 || numIncoming == entry->predecessors.size())
1507 for (
auto *predecessor : entry->predecessors)
1508 if (!predecessor->valueBefore->needed)
1509 worklist.insert({predecessor, entry});
1513 for (
auto [predecessor, successor] : worklist) {
1514 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting probe block towards " << successor
1515 <<
" after " << *predecessor->terminator <<
"\n");
1516 OpBuilder builder(predecessor->terminator);
1517 auto *newBlock = builder.createBlock(successor->block);
1518 for (
auto oldArg : successor->block->getArguments())
1519 newBlock->addArgument(oldArg.getType(), oldArg.
getLoc());
1520 cf::BranchOp::create(builder, predecessor->terminator->getLoc(),
1521 successor->block, newBlock->getArguments());
1522 for (
auto &blockOp : predecessor->terminator->getBlockOperands())
1523 if (blockOp.
get() == successor->block)
1524 blockOp.set(newBlock);
1527 auto *value = lattice->createValue();
1528 value->needed = successor->valueAfter->needed;
1529 auto *newEntry = lattice->createNode<BlockEntry>(newBlock, value);
1530 auto *newExit = lattice->createNode<BlockExit>(newBlock, value);
1531 newEntry->predecessors.push_back(predecessor);
1532 newExit->successors.push_back(successor);
1533 llvm::replace(successor->predecessors, predecessor, newExit);
1534 llvm::replace(predecessor->successors, successor, newEntry);
1541void Promoter::insertProbes() {
1542 for (
auto *node : lattice->nodes) {
1543 if (
auto *entry = dyn_cast<BlockEntry>(node))
1544 insertProbes(entry);
1550void Promoter::insertProbes(BlockEntry *node) {
1551 if (!node->valueAfter->needed)
1553 if (!node->predecessors.empty() &&
1554 llvm::all_of(node->predecessors, [](
auto *predecessor) {
1555 return predecessor->valueBefore->needed;
1558 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting probe for " << currentSlot
1559 <<
" in block " << node->block <<
"\n");
1560 auto builder = OpBuilder::atBlockBegin(node->block);
1562 if (Operation *op = currentSlot.getDefiningOp())
1563 if (op->getBlock() == node->block)
1564 builder.setInsertionPointAfterValue(currentSlot);
1565 auto value = ProbeOp::create(builder, currentSlot.getLoc(), currentSlot);
1566 auto *def = lattice->createDef(value, DriveCondition::never());
1567 node->insertedProbe = def;
1572void Promoter::insertDriveBlocks() {
1576 auto partialAt = [](LatticeValue *before, LatticeValue *after,
bool delayed,
1577 unsigned totalSuccessors) {
1578 Def *def = before->getReachingDef(delayed);
1579 if (!def || def->condition.isNever())
1581 (void)totalSuccessors;
1582 return !after->getReachingDef(delayed);
1585 SmallDenseSet<std::pair<BlockExit *, BlockEntry *>, 1> worklist;
1586 for (
auto *node : lattice->nodes) {
1587 auto *exit = dyn_cast<BlockExit>(node);
1593 for (
bool delayed : {
false,
true}) {
1594 Def *def = exit->valueBefore->getReachingDef(delayed);
1595 if (!def || def->condition.isNever())
1597 unsigned numContinues = 0;
1598 for (
auto *successor : exit->successors)
1599 if (successor->valueAfter->getReachingDef(delayed))
1601 if (numContinues == 0 || numContinues == exit->successors.size())
1603 for (
auto *successor : exit->successors)
1604 if (partialAt(exit->valueBefore, successor->valueAfter, delayed,
1605 exit->successors.size()))
1606 worklist.insert({exit, successor});
1611 for (
auto [predecessor, successor] : worklist) {
1612 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting drive block towards " << successor
1613 <<
" after " << *predecessor->terminator <<
"\n");
1614 OpBuilder builder(predecessor->terminator);
1615 auto *newBlock = builder.createBlock(successor->block);
1616 for (
auto oldArg : successor->block->getArguments())
1617 newBlock->addArgument(oldArg.getType(), oldArg.
getLoc());
1618 cf::BranchOp::create(builder, predecessor->terminator->getLoc(),
1619 successor->block, newBlock->getArguments());
1620 for (
auto &blockOp : predecessor->terminator->getBlockOperands())
1621 if (blockOp.
get() == successor->block)
1622 blockOp.set(newBlock);
1627 auto *value = lattice->createValue();
1628 value->needed = successor->valueAfter->needed;
1629 value->blockingReachingDef = predecessor->valueBefore->blockingReachingDef;
1630 value->delayedReachingDef = predecessor->valueBefore->delayedReachingDef;
1631 auto *newEntry = lattice->createNode<BlockEntry>(newBlock, value);
1632 auto *newExit = lattice->createNode<BlockExit>(newBlock, value);
1633 newEntry->predecessors.push_back(predecessor);
1634 newExit->successors.push_back(successor);
1635 llvm::replace(successor->predecessors, predecessor, newExit);
1636 llvm::replace(predecessor->successors, successor, newEntry);
1643void Promoter::insertDrives() {
1644 for (
auto *node : lattice->nodes) {
1645 if (
auto *exit = dyn_cast<BlockExit>(node))
1647 else if (
auto *drive = dyn_cast<DriveNode>(node))
1648 insertDrives(drive);
1654void Promoter::insertDrives(BlockExit *node) {
1655 auto builder = OpBuilder::atBlockTerminator(node->block);
1660 auto getTime = [&](
bool delta) -> ConstantTimeOp {
1661 auto &cached = (delta ? delayedTimeCache : blockingTimeCache)[node->block];
1663 cached = ConstantTimeOp::create(builder, node->terminator->getLoc(), 0,
1664 "ns", delta ? 1 : 0, delta ? 0 : 1);
1668 auto insertDriveForSlot = [&](
bool delayed) {
1669 Def *reachingDef = node->valueBefore->getReachingDef(delayed);
1670 if (!reachingDef || reachingDef->condition.isNever())
1672 if (!node->suspends && !node->successors.empty() &&
1673 llvm::all_of(node->successors, [&](
auto *successor) {
1674 return successor->valueAfter->getReachingDef(delayed) != nullptr;
1677 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting drive for " << currentSlot <<
" "
1678 << (delayed ?
"(delayed)" :
"(blocking)")
1679 <<
" before " << *node->terminator <<
"\n");
1680 auto time = getTime(delayed);
1681 auto value = reachingDef->getValueOrPlaceholder();
1682 auto enable = reachingDef->condition.isConditional()
1683 ? reachingDef->getConditionOrPlaceholder()
1685 DriveOp::create(builder, currentSlot.getLoc(), currentSlot, value, time,
1689 insertDriveForSlot(
false);
1690 insertDriveForSlot(
true);
1695void Promoter::insertDrives(DriveNode *node) {
1696 LLVM_DEBUG(llvm::dbgs() <<
"- Removing drive " << *node->op <<
"\n");
1697 pruner.eraseNow(node->op);
1706void Promoter::resolveDefinitions() {
1707 for (
auto *node : lattice->nodes) {
1708 if (
auto *probe = dyn_cast<ProbeNode>(node))
1709 resolveDefinitions(probe);
1710 else if (
auto *drive = dyn_cast<DriveNode>(node))
1711 resolveDefinitions(drive);
1716void Promoter::resolveDefinitions(ProbeNode *node) {
1717 Def *def = node->valueBefore->blockingReachingDef;
1718 assert(def &&
"no definition reaches probe");
1721 auto projections =
getProjections(node->op->getOperand(0), node->slot);
1724 auto builder = OpBuilder(node->op);
1725 auto value = def->getValueOrPlaceholder();
1729 LLVM_DEBUG(llvm::dbgs() <<
"- Replacing " << *node->op <<
" with " << value
1731 replaceValueWith(node->op->getResult(0), value);
1732 pruner.eraseNow(node->op);
1738void Promoter::resolveDefinitions(DriveNode *node) {
1743 if (!node->def->value || node->def->valueIsPlaceholder)
1744 resolveDefinitionValue(node);
1745 if (node->def->condition.isConditional() &&
1746 (!node->def->condition.getCondition() ||
1747 node->def->conditionIsPlaceholder))
1748 resolveDefinitionCondition(node);
1751void Promoter::resolveDefinitionValue(DriveNode *node) {
1754 Def *inDef = node->valueBefore->getReachingDef(
isDelayed(node->slot));
1755 assert(inDef &&
"no definition reaches drive");
1756 auto driveOp = node->getDriveOp();
1757 LLVM_DEBUG(llvm::dbgs() <<
"- Injecting value for " << driveOp <<
"\n");
1763 auto builder = OpBuilder(driveOp);
1764 auto value = inDef->getValueOrPlaceholder();
1768 pruner.eraseLaterIfUnused(value);
1769 if (!driveOp.getEnable())
1770 value = driveOp.getValue();
1773 driveOp.getLoc(), driveOp.getEnable(), driveOp.getValue(), value);
1779 if (node->def->valueIsPlaceholder) {
1780 auto *placeholder = node->def->value.getDefiningOp();
1781 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1782 "placeholder replaced but valueIsPlaceholder still set");
1783 replaceValueWith(placeholder->getResult(0), value);
1784 pruner.eraseNow(placeholder);
1785 node->def->valueIsPlaceholder =
false;
1787 node->def->value = value;
1790void Promoter::resolveDefinitionCondition(DriveNode *node) {
1793 Def *inDef = node->valueBefore->getReachingDef(
isDelayed(node->slot));
1794 assert(inDef &&
"no definition reaches drive");
1795 auto driveOp = node->getDriveOp();
1796 LLVM_DEBUG(llvm::dbgs() <<
"- Mutating condition for " << driveOp <<
"\n");
1797 OpBuilder builder(driveOp);
1802 if (inDef->condition.isNever())
1803 condition = driveOp.getEnable();
1806 builder.createOrFold<
comb::OrOp>(driveOp.getLoc(), driveOp.getEnable(),
1807 inDef->getConditionOrPlaceholder());
1810 if (node->def->conditionIsPlaceholder) {
1811 auto *placeholder = node->def->condition.getCondition().getDefiningOp();
1812 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1813 "placeholder replaced but conditionIsPlaceholder still set");
1814 replaceValueWith(placeholder->getResult(0), condition);
1815 pruner.eraseNow(placeholder);
1816 node->def->conditionIsPlaceholder =
false;
1818 node->def->condition.setCondition(condition);
1826void Promoter::insertBlockArgs() {
1827 bool anyArgsInserted =
true;
1828 while (anyArgsInserted) {
1829 anyArgsInserted =
false;
1830 for (
auto *node : lattice->nodes)
1831 if (auto *entry = dyn_cast<BlockEntry>(node))
1832 anyArgsInserted |= insertBlockArgs(entry);
1844bool Promoter::insertBlockArgs(BlockEntry *node) {
1850 enum class Which { Value, Condition };
1851 SmallVector<std::pair<DefSlot, Which>> neededSlots;
1852 auto addNeededSlot = [&](
DefSlot slot) {
1853 auto *def = node->getMerged(
isDelayed(slot));
1856 if (!node->valueAfter->getReachingDef(
isDelayed(slot)))
1858 if (def->valueIsPlaceholder)
1859 neededSlots.push_back({slot, Which::Value});
1860 if (def->conditionIsPlaceholder)
1861 neededSlots.push_back({slot, Which::Condition});
1865 if (neededSlots.empty())
1867 LLVM_DEBUG(llvm::dbgs() <<
"- Adding " << neededSlots.size()
1868 <<
" args to block " << node->block <<
"\n");
1871 for (
auto [slot, which] : neededSlots) {
1872 auto *def = node->getMerged(
isDelayed(slot));
1875 case Which::Value: {
1878 auto *placeholder = def->value.getDefiningOp();
1879 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1880 "placeholder replaced but valueIsPlaceholder still set");
1882 replaceValueWith(placeholder->getResult(0), arg);
1883 pruner.eraseNow(placeholder);
1885 def->valueIsPlaceholder =
false;
1888 case Which::Condition: {
1892 auto *placeholder = def->condition.getCondition().getDefiningOp();
1893 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1894 "placeholder replaced but conditionIsPlaceholder still set");
1895 auto conditionArg = node->block->addArgument(
1896 IntegerType::get(region.getContext(), 1),
getLoc(slot));
1897 replaceValueWith(placeholder->getResult(0), conditionArg);
1898 pruner.eraseNow(placeholder);
1899 def->condition.setCondition(conditionArg);
1900 def->conditionIsPlaceholder =
false;
1907 for (
auto *predecessor : node->predecessors) {
1909 SmallVector<Value> args;
1910 for (
auto [slot, which] : neededSlots) {
1911 Def *def = predecessor->valueBefore->getReachingDef(
isDelayed(slot));
1912 auto builder = OpBuilder::atBlockTerminator(predecessor->block);
1916 args.push_back(def->getValueOrPlaceholder());
1919 auto flatType = builder.getIntegerType(hw::getBitWidth(type));
1922 if (type != flatType)
1924 args.push_back(value);
1927 case Which::Condition:
1929 args.push_back(def->getConditionOrPlaceholder());
1932 builder.getI1Type(), 0));
1939 auto branchOp = cast<BranchOpInterface>(predecessor->terminator);
1940 for (
auto &blockOperand : branchOp->getBlockOperands())
1941 if (blockOperand.
get() == node->block)
1942 branchOp.getSuccessorOperands(blockOperand.getOperandNumber())
1951void Promoter::replaceValueWith(Value oldValue, Value newValue) {
1952 oldValue.replaceAllUsesWith(newValue);
1953 for (
auto *def : lattice->defs) {
1954 if (def->value == oldValue)
1955 def->value = newValue;
1956 if (def->condition.isConditional() &&
1957 def->condition.getCondition() == oldValue)
1958 def->condition.setCondition(newValue);
1969void Promoter::removeUnusedLocalSignal(SignalOp signalOp) {
1973 SmallVector<Operation *> worklist;
1974 worklist.push_back(signalOp);
1975 while (!worklist.empty()) {
1976 auto *op = worklist.pop_back_val();
1977 if (!isa<SignalOp, DriveOp, SigArrayGetOp, SigArraySliceOp, SigExtractOp,
1978 SigStructExtractOp>(op))
1980 for (
auto *user : op->getUsers())
1981 if (users.insert(user))
1982 worklist.push_back(user);
1987 LLVM_DEBUG(llvm::dbgs() <<
"- Removing local signal " << *signalOp <<
"\n");
1988 for (
auto *op :
llvm::reverse(users))
1989 pruner.eraseNow(op);
1997struct Mem2RegPass :
public llhd::impl::Mem2RegPassBase<Mem2RegPass> {
1998 void runOnOperation()
override;
2002void Mem2RegPass::runOnOperation() {
2003 SmallVector<Region *> regions;
2004 getOperation()->walk<WalkOrder::PreOrder>([&](Operation *op) {
2005 if (isa<ProcessOp, FinalOp, CombinationalOp>(op)) {
2006 auto ®ion = op->getRegion(0);
2007 if (!region.empty())
2008 regions.push_back(®ion);
2009 return WalkResult::skip();
2011 return WalkResult::advance();
2013 for (
auto *region : regions)
2014 if (failed(Promoter(*region).promote()))
2015 return signalPassFailure();
static bool isAlways(Attribute attr, bool expected)
assert(baseType &&"element must be base type")
static void dump(DIModule &module, raw_indented_ostream &os)
static bool isDelayed(DefSlot slot)
static Value packProjections(OpBuilder &builder, Value value, const ProjectionStack &projections)
Undo a stack of projections by taking the value of the projected field and injecting it into the surr...
static Location getLoc(DefSlot slot)
static bool isDeltaDrive(Operation *op)
Check whether an operation is a llhd.drive with a delta delay.
static ProjectionStack getProjections(Value fromSignal, Value toSlot)
Collect the llhd.sig.
static bool isDeltaDelay(Value value)
Check whether a value is defined by llhd.constant_time <0ns, 1d, 0e>.
static DefSlot blockingSlot(Value slot)
static Value unpackProjections(OpBuilder &builder, Value value, ProjectionStack &projections)
Resolve a stack of projections by taking a value and descending into its subelements until the final ...
static DefSlot delayedSlot(Value slot)
static Value getSlot(DefSlot slot)
SmallVector< Projection > ProjectionStack
A stack of projection operations.
static bool isBlockingDrive(Operation *op)
Check whether an operation is a llhd.drive with an epsilon delay.
static Type getStoredType(Value slot)
static bool isEpsilonDelay(Value value)
Check whether a value is defined by llhd.constant_time <0ns, 0d, 1e>.
PointerIntPair< Value, 1 > DefSlot
The slot a reaching definition specifies a value for, alongside a bit indicating whether the definiti...
static StringAttr append(StringAttr base, const Twine &suffix)
Return a attribute with the specified suffix appended.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
static bool operator==(const ModulePort &a, const ModulePort &b)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
bool operator!=(uint64_t a, const FVInt &b)
Utility that tracks operations that have potentially become unused and allows them to be cleaned up a...
static DriveCondition getEmptyKey()
static DriveCondition getTombstoneKey()
static bool isEqual(DriveCondition lhs, DriveCondition rhs)
static unsigned getHashValue(DriveCondition d)