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();
189 static bool isEqual(DriveCondition lhs, DriveCondition rhs) {
207 return cast<RefType>(slot.getType()).getNestedType();
230 LatticeNode *nodeBefore =
nullptr;
231 LatticeNode *nodeAfter =
nullptr;
233 Def *blockingReachingDef =
nullptr;
234 Def *delayedReachingDef =
nullptr;
237 Def *getReachingDef(
bool delayed)
const {
238 return delayed ? delayedReachingDef : blockingReachingDef;
240 void setReachingDef(
bool delayed, Def *def) {
241 (delayed ? delayedReachingDef : blockingReachingDef) = def;
246 enum class Kind { BlockEntry, BlockExit, Probe, Drive, Signal };
250 LatticeNode(Kind kind) : kind(kind) {}
253struct BlockEntry :
public LatticeNode {
255 LatticeValue *valueAfter;
256 SmallVector<BlockExit *, 2> predecessors;
260 Def *insertedProbe =
nullptr;
263 Def *blockingMerged =
nullptr;
264 Def *delayedMerged =
nullptr;
266 BlockEntry(Block *block, LatticeValue *valueAfter)
267 : LatticeNode(Kind::BlockEntry), block(block), valueAfter(valueAfter) {
268 assert(!valueAfter->nodeBefore);
269 valueAfter->nodeBefore =
this;
272 Def *getMerged(
bool delayed)
const {
273 return delayed ? delayedMerged : blockingMerged;
275 void setMerged(
bool delayed, Def *def) {
276 (delayed ? delayedMerged : blockingMerged) = def;
279 static bool classof(
const LatticeNode *n) {
280 return n->kind == Kind::BlockEntry;
284struct BlockExit :
public LatticeNode {
286 LatticeValue *valueBefore;
287 SmallVector<BlockEntry *, 2> successors;
288 Operation *terminator;
291 BlockExit(Block *block, LatticeValue *valueBefore)
292 : LatticeNode(Kind::BlockExit), block(block), valueBefore(valueBefore),
293 terminator(block->getTerminator()),
294 suspends(isa<HaltOp, WaitOp>(terminator)) {
295 assert(!valueBefore->nodeAfter);
296 valueBefore->nodeAfter =
this;
299 static bool classof(
const LatticeNode *n) {
300 return n->kind == Kind::BlockExit;
304struct OpNode :
public LatticeNode {
306 LatticeValue *valueBefore;
307 LatticeValue *valueAfter;
309 OpNode(Kind kind, Operation *op, LatticeValue *valueBefore,
310 LatticeValue *valueAfter)
311 : LatticeNode(kind), op(op), valueBefore(valueBefore),
312 valueAfter(valueAfter) {
313 assert(!valueBefore->nodeAfter);
314 assert(!valueAfter->nodeBefore);
315 valueBefore->nodeAfter =
this;
316 valueAfter->nodeBefore =
this;
319 static bool classof(
const LatticeNode *n) {
320 return isa<ProbeNode, DriveNode, SignalNode>(n);
324struct ProbeNode :
public OpNode {
327 ProbeNode(ProbeOp op, Value slot, LatticeValue *valueBefore,
328 LatticeValue *valueAfter)
329 : OpNode(Kind::Probe, op, valueBefore, valueAfter), slot(slot) {}
331 static bool classof(
const LatticeNode *n) {
return n->kind == Kind::Probe; }
334struct DriveNode :
public OpNode {
338 DriveNode(DriveOp op, Value slot, Def *def, LatticeValue *valueBefore,
339 LatticeValue *valueAfter)
340 : OpNode(Kind::Drive, op, valueBefore, valueAfter),
348 bool drivesProjection()
const {
return op->getOperand(0) !=
getSlot(slot); }
351 DriveOp getDriveOp()
const {
return cast<DriveOp>(op); }
353 static bool classof(
const LatticeNode *n) {
return n->kind == Kind::Drive; }
356struct SignalNode :
public OpNode {
359 SignalNode(SignalOp op, Def *def, LatticeValue *valueBefore,
360 LatticeValue *valueAfter)
361 : OpNode(Kind::Signal, op, valueBefore, valueAfter), def(def) {}
363 SignalOp getSignalOp()
const {
return cast<SignalOp>(op); }
366 static bool classof(
const LatticeNode *n) {
return n->kind == Kind::Signal; }
373 LatticeValue *createValue() {
374 auto *value =
new (valueAllocator.Allocate()) LatticeValue();
375 values.push_back(value);
380 template <
class T,
typename... Args>
381 T *createNode(Args... args) {
383 new (getAllocator<T>().Allocate()) T(std::forward<Args>(args)...);
384 nodes.push_back(node);
389 template <
typename... Args>
390 Def *createDef(Args... args) {
391 auto *def =
new (defAllocator.Allocate()) Def(std::forward<Args>(args)...);
397 Def *createDef(Value value, DriveCondition mode) {
398 auto &slot = defsForValues[{value, mode}];
400 slot =
new (defAllocator.Allocate()) Def(value, mode);
401 defs.push_back(slot);
407 void dump(llvm::raw_ostream &os = llvm::dbgs());
411 std::vector<LatticeNode *> nodes;
413 std::vector<LatticeValue *> values;
415 std::vector<Def *> defs;
419 DenseMap<std::pair<Value, DriveCondition>, Def *> defsForValues;
422 SpecificBumpPtrAllocator<LatticeValue> valueAllocator;
423 SpecificBumpPtrAllocator<Def> defAllocator;
424 SpecificBumpPtrAllocator<BlockEntry> blockEntryAllocator;
425 SpecificBumpPtrAllocator<BlockExit> blockExitAllocator;
426 SpecificBumpPtrAllocator<ProbeNode> probeAllocator;
427 SpecificBumpPtrAllocator<DriveNode> driveAllocator;
428 SpecificBumpPtrAllocator<SignalNode> signalAllocator;
432 SpecificBumpPtrAllocator<T> &getAllocator();
438SpecificBumpPtrAllocator<BlockEntry> &Lattice::getAllocator() {
439 return blockEntryAllocator;
442SpecificBumpPtrAllocator<BlockExit> &Lattice::getAllocator() {
443 return blockExitAllocator;
446SpecificBumpPtrAllocator<ProbeNode> &Lattice::getAllocator() {
447 return probeAllocator;
450SpecificBumpPtrAllocator<DriveNode> &Lattice::getAllocator() {
451 return driveAllocator;
454SpecificBumpPtrAllocator<SignalNode> &Lattice::getAllocator() {
455 return signalAllocator;
462void Lattice::dump(llvm::raw_ostream &os) {
468 auto blockName = [&](
Block *block) {
469 unsigned id = blockNames.insert({block, blockNames.size()}).first->second;
470 return std::string(
"bb") + llvm::utostr(
id);
473 auto memName = [&](
DefSlot value) {
475 memNames.insert({
getSlot(value), memNames.size()}).first->second;
476 return std::string(
"mem") + llvm::utostr(
id) +
480 auto defName = [&](Def *def) {
481 unsigned id = defNames.insert({def, defNames.size()}).first->second;
482 return std::string(
"def") + llvm::utostr(
id);
486 for (
auto *node : nodes)
487 if (auto *entry = dyn_cast<BlockEntry>(node))
488 blockName(entry->block);
492 for (
auto *node : nodes) {
493 auto *entry = dyn_cast<BlockEntry>(node);
498 os <<
" " << blockName(entry->block) <<
":";
499 if (entry->predecessors.empty()) {
500 os <<
" // no predecessors";
503 for (
auto *node : entry->predecessors)
504 os <<
" " << blockName(node->block);
509 auto *value = entry->valueAfter;
514 auto printDef = [&](
bool delayed, Def *def) {
517 os <<
" -> def (" << (delayed ?
"delayed" :
"blocking")
518 <<
")=" << defName(def);
519 if (def->condition.isNever())
521 else if (def->condition.isAlways())
527 printDef(
false, value->blockingReachingDef);
528 printDef(
true, value->delayedReachingDef);
529 if (isa<BlockExit>(value->nodeAfter))
533 if (
auto *node = dyn_cast<ProbeNode>(value->nodeAfter))
534 os <<
" probe " << memName(
blockingSlot(node->slot)) <<
"\n";
535 else if (
auto *node = dyn_cast<DriveNode>(value->nodeAfter))
536 os <<
" drive " << memName(node->slot) <<
"\n";
537 else if (
auto *node = dyn_cast<SignalNode>(value->nodeAfter))
538 os <<
" signal " << memName(node->getSlot()) <<
"\n";
543 value = cast<OpNode>(value->nodeAfter)->valueAfter;
547 auto *exit = cast<BlockExit>(value->nodeAfter);
548 if (isa<WaitOp>(exit->terminator))
550 else if (exit->successors.empty())
554 for (
auto *node : exit->successors)
555 os <<
" " << blockName(node->block);
557 os <<
" // suspends";
562 for (
auto [mem,
id] : memNames)
563 os <<
" mem" << id <<
": " << mem <<
"\n";
597 while (fromSignal != toSlot) {
598 auto *op = cast<OpResult>(fromSignal).getOwner();
599 stack.push_back({op, Value()});
600 fromSignal = op->getOperand(0);
613 for (
auto &projection : llvm::reverse(projections)) {
614 projection.into = value;
615 value = TypeSwitch<Operation *, Value>(projection.op)
616 .Case<SigArrayGetOp>([&](
auto op) {
618 op.getLoc(), value, op.getIndex());
620 .Case<SigStructExtractOp>([&](
auto op) {
623 if (isa<hw::UnionType>(value.getType()))
624 return builder.createOrFold<hw::UnionExtractOp>(
625 op.getLoc(), value, op.getFieldAttr());
627 op.getLoc(), value, op.getFieldAttr());
629 .Case<SigExtractOp>([&](
auto op) {
630 auto type = cast<RefType>(op.getType()).getNestedType();
631 auto width = type.getIntOrFloatBitWidth();
632 return comb::createDynamicExtract(builder, op.getLoc(), value,
633 op.getLowBit(), width);
653 for (
auto projection : projections) {
654 value = TypeSwitch<Operation *, Value>(projection.op)
655 .Case<SigArrayGetOp>([&](
auto op) {
656 return builder.createOrFold<hw::ArrayInjectOp>(
657 op.getLoc(), projection.into, op.getIndex(), value);
659 .Case<SigStructExtractOp>([&](
auto op) {
663 dyn_cast<hw::UnionType>(projection.into.getType()))
664 return builder.createOrFold<hw::UnionCreateOp>(
665 op.getLoc(), unionTy, op.getFieldAttr(), value);
666 return builder.createOrFold<hw::StructInjectOp>(
667 op.getLoc(), projection.into, op.getFieldAttr(), value);
669 .Case<SigExtractOp>([&](
auto op) {
670 return comb::createDynamicInject(builder, op.getLoc(),
672 op.getLowBit(), value);
685 Promoter(Region ®ion) : region(region) {}
686 LogicalResult promote();
688 void findPromotableSlots();
689 Value resolveSlot(Value projectionOrSlot);
690 void populateSlotOps();
692 void captureAcrossWait();
693 void captureAcrossWait(Value value, ArrayRef<WaitOp> waitOps,
694 Liveness &liveness, DominanceInfo &dominance);
696 void constructLattice();
697 void propagateBackward();
698 void propagateBackward(LatticeNode *node);
699 void propagateForward();
700 void propagateForward(
bool optimisticMerges, DominanceInfo &dominance);
701 void propagateForward(LatticeNode *node,
bool optimisticMerges,
702 DominanceInfo &dominance);
703 void markDirty(LatticeNode *node);
705 void insertProbeBlocks();
707 void insertProbes(BlockEntry *node);
709 void insertDriveBlocks();
711 void insertDrives(BlockExit *node);
712 void insertDrives(DriveNode *node);
714 void resolveDefinitions();
715 void resolveDefinitions(ProbeNode *node);
716 void resolveDefinitions(DriveNode *node);
717 void resolveDefinitionValue(DriveNode *node);
718 void resolveDefinitionCondition(DriveNode *node);
720 void insertBlockArgs();
721 bool insertBlockArgs(BlockEntry *node);
722 void replaceValueWith(Value oldValue, Value newValue);
726 void removeUnusedLocalSignal(SignalOp signalOp);
737 SmallVector<Value> slots;
742 SmallDenseSet<Value> promotable;
751 DenseMap<Block *, ConstantTimeOp> blockingTimeCache;
752 DenseMap<Block *, ConstantTimeOp> delayedTimeCache;
757 std::optional<Lattice> lattice;
759 SmallVector<LatticeNode *> dirtyNodes;
766 DenseMap<Value, SmallVector<Operation *>> slotOps;
770LogicalResult Promoter::promote() {
774 findPromotableSlots();
789 for (
auto slot : slots) {
802void Promoter::promoteSlot() {
803 assert(currentSlot &&
"currentSlot must be set before promoteSlot()");
807 LLVM_DEBUG(llvm::dbgs() <<
"Promoting slot " << currentSlot <<
"\n");
811 llvm::dbgs() <<
"Initial lattice:\n";
818 llvm::dbgs() <<
"After backward propagation:\n";
826 llvm::dbgs() <<
"After probe insertion:\n";
833 llvm::dbgs() <<
"After forward propagation:\n";
838 resolveDefinitions();
844 llvm::dbgs() <<
"After def resolution and drive insertion:\n";
853 if (
auto signalOp = currentSlot.getDefiningOp<SignalOp>())
854 if (signalOp->getParentRegion() == ®ion)
855 removeUnusedLocalSignal(signalOp);
863void Promoter::findPromotableSlots() {
864 SmallPtrSet<Value, 8> seenSlots;
865 SmallPtrSet<Operation *, 8> checkedUsers;
866 SmallVector<Operation *, 8> userWorklist;
868 region.walk([&](Operation *op) {
869 for (
auto operand : op->getOperands()) {
870 if (!seenSlots.insert(operand).second)
876 if (!operand.getDefiningOp<llhd::SignalOp>())
880 bool hasProjection =
false;
881 bool hasBlockingDrive =
false;
882 bool hasDeltaDrive =
false;
883 auto checkUser = [&](Operation *user) ->
bool {
885 if (region.isProperAncestor(user->getParentRegion()))
888 if (user->getParentRegion() != ®ion)
894 if (isa<SigArrayGetOp, SigExtractOp, SigStructExtractOp>(user)) {
895 hasProjection =
true;
896 for (
auto *projectionUser : user->getUsers()) {
897 if (isa<SigArrayGetOp, SigExtractOp, SigStructExtractOp>(
899 projectionUser->getBlock() != user->getBlock())
903 if (checkedUsers.insert(projectionUser).second)
904 userWorklist.push_back(projectionUser);
906 projections.insert({user->getResult(0), operand});
914 checkedUsers.clear();
915 if (!llvm::all_of(operand.getUsers(), [&](
auto *user) {
917 if (checkedUsers.insert(user).second)
918 userWorklist.push_back(user);
919 while (!userWorklist.empty() && allOk)
920 allOk &= checkUser(userWorklist.pop_back_val());
921 userWorklist.clear();
929 if (hasProjection && hasBlockingDrive && hasDeltaDrive)
937 static_cast<unsigned>(bitWidth) > IntegerType::kMaxWidth)
940 slots.push_back(operand);
945 promotable.insert(slots.begin(), slots.end());
946 projections.remove_if([&](
auto elem) {
947 auto [projection, slot] = elem;
948 return !promotable.contains(slot);
950 for (
auto [projection, slot] : projections)
951 promotable.insert(projection);
953 LLVM_DEBUG(llvm::dbgs() <<
"Found " << slots.size() <<
" promotable slots, "
954 << promotable.size() <<
" promotable values\n");
959Value Promoter::resolveSlot(Value projectionOrSlot) {
960 if (
auto slot = projections.lookup(projectionOrSlot))
962 return projectionOrSlot;
969void Promoter::captureAcrossWait() {
970 if (region.hasOneBlock())
973 SmallVector<WaitOp> waitOps;
974 for (
auto &block : region)
975 if (auto waitOp = dyn_cast<WaitOp>(block.getTerminator()))
976 waitOps.push_back(waitOp);
978 DominanceInfo dominance(region.getParentOp());
979 Liveness liveness(region.getParentOp());
981 llvm::DenseSet<Value> alreadyCaptured;
983 auto isDefinedInRegion = [&](Value v) {
984 return v.getParentRegion() == ®ion;
987 for (
auto waitOp : waitOps) {
988 Block *waitBlock = waitOp->getBlock();
989 const auto &liveOutValues = liveness.getLiveOut(waitBlock);
991 for (Value v : liveOutValues) {
992 if (!isDefinedInRegion(v))
995 if (!alreadyCaptured.insert(v).second)
998 captureAcrossWait(v, waitOps, liveness, dominance);
1007void Promoter::captureAcrossWait(Value value, ArrayRef<WaitOp> waitOps,
1008 Liveness &liveness, DominanceInfo &dominance) {
1010 llvm::dbgs() <<
"Capture " << value <<
"\n";
1011 for (
auto waitOp : waitOps)
1012 llvm::dbgs() <<
"- Across " << waitOp <<
"\n";
1017 auto &domTree = dominance.getDomTree(®ion);
1018 llvm::IDFCalculatorBase<Block, false> idfCalculator(domTree);
1022 SmallPtrSet<Block *, 4> definingBlocks;
1023 definingBlocks.insert(value.getParentBlock());
1024 for (
auto waitOp : waitOps)
1025 definingBlocks.insert(waitOp.getDest());
1026 idfCalculator.setDefiningBlocks(definingBlocks);
1029 SmallPtrSet<Block *, 16> liveInBlocks;
1030 for (
auto &block : region)
1031 if (liveness.getLiveness(&block)->isLiveIn(value))
1032 liveInBlocks.insert(&block);
1033 idfCalculator.setLiveInBlocks(liveInBlocks);
1037 SmallVector<Block *> mergePointsVec;
1038 idfCalculator.calculate(mergePointsVec);
1039 SmallPtrSet<Block *, 16> mergePoints(mergePointsVec.begin(),
1040 mergePointsVec.end());
1041 for (
auto waitOp : waitOps)
1042 mergePoints.insert(waitOp.getDest());
1043 LLVM_DEBUG(llvm::dbgs() <<
"- " << mergePoints.size() <<
" merge points\n");
1048 struct WorklistItem {
1049 DominanceInfoNode *domNode;
1052 SmallVector<WorklistItem> worklist;
1053 worklist.push_back({domTree.getNode(value.getParentBlock()), value});
1055 while (!worklist.empty()) {
1056 auto item = worklist.pop_back_val();
1057 auto *block = item.domNode->getBlock();
1060 if (mergePoints.contains(block))
1061 item.reachingDef = block->addArgument(value.getType(), value.getLoc());
1065 for (
auto &op : *block)
1066 op.replaceUsesOfWith(value, item.reachingDef);
1070 if (
auto branchOp = dyn_cast<BranchOpInterface>(block->getTerminator())) {
1071 for (
auto &blockOperand : branchOp->getBlockOperands())
1072 if (mergePoints.contains(blockOperand.
get()))
1073 branchOp.getSuccessorOperands(blockOperand.getOperandNumber())
1074 .
append(item.reachingDef);
1075 }
else if (
auto waitOp = dyn_cast<WaitOp>(block->getTerminator())) {
1076 if (mergePoints.contains(waitOp.getDest()))
1077 waitOp.getDestOperandsMutable().append(item.reachingDef);
1080 for (
auto *child : item.domNode->children())
1081 worklist.push_back({child, item.reachingDef});
1093void Promoter::populateSlotOps() {
1094 for (
auto &block : region) {
1095 for (
auto &op : block.without_terminator()) {
1096 Value slot = TypeSwitch<Operation *, Value>(&op)
1097 .Case<ProbeOp, DriveOp>([&](
auto op) {
1098 if (promotable.contains(op.getSignal()))
1099 return resolveSlot(op.getSignal());
1102 .Case([&](SignalOp op) {
return op.getResult(); })
1103 .Default([&](Operation *) {
return Value(); });
1105 slotOps[slot].push_back(&op);
1114void Promoter::constructLattice() {
1115 assert(currentSlot &&
"constructLattice requires currentSlot");
1119 ArrayRef<Operation *> currentSlotOps = slotOps[currentSlot];
1123 for (
auto &block : region) {
1125 lattice->createNode<BlockEntry>(&block, lattice->createValue());
1126 blockEntries.insert({&block, entry});
1130 for (
auto &block : region) {
1131 auto *valueBefore = blockEntries.lookup(&block)->valueAfter;
1135 ArrayRef<Operation *> blockOps = currentSlotOps.take_while(
1136 [&](Operation *op) {
return op->getBlock() == █ });
1137 currentSlotOps = currentSlotOps.drop_front(blockOps.size());
1139 for (Operation *op : blockOps) {
1141 if (
auto probeOp = dyn_cast<ProbeOp>(op)) {
1142 if (!promotable.contains(probeOp.getSignal()))
1144 if (resolveSlot(probeOp.getSignal()) != currentSlot)
1146 auto *node = lattice->createNode<ProbeNode>(
1147 probeOp, currentSlot, valueBefore, lattice->createValue());
1148 valueBefore = node->valueAfter;
1153 if (
auto driveOp = dyn_cast<DriveOp>(op)) {
1156 if (!promotable.contains(driveOp.getSignal()))
1158 if (resolveSlot(driveOp.getSignal()) != currentSlot)
1160 auto condition = DriveCondition::always();
1161 if (
auto enable = driveOp.getEnable())
1162 condition = DriveCondition::conditional(enable);
1167 driveOp.getSignal() == currentSlot
1168 ? lattice->createDef(driveOp.getValue(), condition)
1169 : lattice->createDef(driveOp->getBlock(),
1171 auto *node = lattice->createNode<DriveNode>(
1172 driveOp, currentSlot, def, valueBefore, lattice->createValue());
1173 valueBefore = node->valueAfter;
1179 if (
auto signalOp = dyn_cast<SignalOp>(op)) {
1180 if (signalOp.getResult() != currentSlot)
1183 lattice->createDef(signalOp.getInit(), DriveCondition::never());
1184 auto *node = lattice->createNode<SignalNode>(signalOp, def, valueBefore,
1185 lattice->createValue());
1186 valueBefore = node->valueAfter;
1192 auto *exit = lattice->createNode<BlockExit>(&block, valueBefore);
1193 for (
auto *otherBlock : exit->terminator->getSuccessors()) {
1194 auto *otherEntry = blockEntries.lookup(otherBlock);
1195 exit->successors.push_back(otherEntry);
1196 otherEntry->predecessors.push_back(exit);
1203void Promoter::propagateBackward() {
1204 for (
auto *node : lattice->nodes)
1205 propagateBackward(node);
1206 SmallVector<LatticeNode *> nodes;
1207 while (!dirtyNodes.empty()) {
1208 std::swap(dirtyNodes, nodes);
1209 for (
auto *node : nodes) {
1210 node->dirty =
false;
1211 propagateBackward(node);
1219void Promoter::propagateBackward(LatticeNode *node) {
1220 auto update = [&](LatticeValue *value,
bool needed) {
1221 if (value->needed != needed) {
1222 value->needed = needed;
1223 markDirty(value->nodeBefore);
1228 if (
auto *probe = dyn_cast<ProbeNode>(node)) {
1229 update(probe->valueBefore,
true);
1239 if (
auto *drive = dyn_cast<DriveNode>(node)) {
1240 bool needed = drive->valueAfter->needed;
1241 if (drive->drivesProjection())
1243 else if (!
isDelayed(drive->slot) && !drive->getDriveOp().getEnable())
1245 update(drive->valueBefore, needed);
1252 if (isa<SignalNode>(node)) {
1253 auto *signal = cast<SignalNode>(node);
1254 update(signal->valueBefore,
false);
1259 if (
auto *entry = dyn_cast<BlockEntry>(node)) {
1260 for (
auto *predecessor : entry->predecessors)
1261 markDirty(predecessor);
1266 if (
auto *exit = dyn_cast<BlockExit>(node)) {
1269 bool needed =
false;
1270 for (
auto *successor : exit->successors)
1271 needed |= successor->valueAfter->needed;
1272 update(exit->valueBefore, needed);
1276 assert(
false &&
"unhandled node in backward propagation");
1279void Promoter::propagateForward() {
1280 DominanceInfo dominance(region.getParentOp());
1281 propagateForward(
true, dominance);
1282 propagateForward(
false, dominance);
1293void Promoter::propagateForward(
bool optimisticMerges,
1294 DominanceInfo &dominance) {
1295 for (
auto *node : lattice->nodes)
1296 propagateForward(node, optimisticMerges, dominance);
1297 SmallVector<LatticeNode *> nodes;
1298 while (!dirtyNodes.empty()) {
1299 std::swap(dirtyNodes, nodes);
1300 for (
auto *node : nodes) {
1301 node->dirty =
false;
1302 propagateForward(node, optimisticMerges, dominance);
1311void Promoter::propagateForward(LatticeNode *node,
bool optimisticMerges,
1312 DominanceInfo &dominance) {
1313 auto update = [&](LatticeValue *value, Def *blocking, Def *delayed) {
1314 if (value->blockingReachingDef != blocking ||
1315 value->delayedReachingDef != delayed) {
1316 value->blockingReachingDef = blocking;
1317 value->delayedReachingDef = delayed;
1318 markDirty(value->nodeAfter);
1323 if (
auto *probe = dyn_cast<ProbeNode>(node)) {
1324 update(probe->valueAfter, probe->valueBefore->blockingReachingDef,
1325 probe->valueBefore->delayedReachingDef);
1339 if (
auto *drive = dyn_cast<DriveNode>(node)) {
1340 Def *blocking = drive->valueBefore->blockingReachingDef;
1341 Def *delayed = drive->valueBefore->delayedReachingDef;
1342 bool driveDelayed =
isDelayed(drive->slot);
1348 if (drive->drivesProjection() || drive->getDriveOp().getEnable()) {
1349 Def *inDef = driveDelayed ? delayed : blocking;
1351 if (drive->def->value != inDef->value)
1352 drive->def->value = {};
1353 if (inDef->condition.isAlways() || drive->def->condition.isAlways())
1354 drive->def->condition = DriveCondition::always();
1355 else if (drive->def->condition != inDef->condition)
1356 drive->def->condition = DriveCondition::conditional();
1361 delayed = drive->def;
1363 blocking = drive->def;
1367 update(drive->valueAfter, blocking, delayed);
1373 if (
auto *signal = dyn_cast<SignalNode>(node)) {
1374 update(signal->valueAfter, signal->def,
nullptr);
1380 if (
auto *entry = dyn_cast<BlockEntry>(node)) {
1383 if (entry->insertedProbe) {
1384 update(entry->valueAfter, entry->insertedProbe, entry->insertedProbe);
1391 Block *slotBlock = currentSlot.getDefiningOp()->getBlock();
1392 bool slotDominates = dominance.dominates(slotBlock, entry->block);
1393 bool anyPredSuspends =
1394 llvm::any_of(entry->predecessors, [](
auto *p) { return p->suspends; });
1396 auto mergeFlavor = [&](
bool delayed) -> Def * {
1397 if (!slotDominates || anyPredSuspends)
1401 if (llvm::all_of(entry->predecessors, [&](
auto *p) {
1402 return !p->valueBefore->getReachingDef(delayed);
1408 Def *common =
nullptr;
1409 DriveCondition cond = DriveCondition::never();
1411 for (
auto *pred : entry->predecessors) {
1412 Def *predDef = pred->valueBefore->getReachingDef(delayed);
1413 if (!predDef && optimisticMerges)
1415 DriveCondition predCond =
1416 predDef ? predDef->condition : DriveCondition::never();
1423 if (common != predDef)
1425 if (cond != predCond)
1426 cond = DriveCondition::conditional();
1437 Def *&merged = delayed ? entry->delayedMerged : entry->blockingMerged;
1439 merged->condition = cond;
1450 merged = lattice->createDef(entry->block,
getStoredType(slot), cond);
1454 Def *newBlocking = mergeFlavor(
false);
1455 Def *newDelayed = mergeFlavor(
true);
1456 update(entry->valueAfter, newBlocking, newDelayed);
1461 if (
auto *exit = dyn_cast<BlockExit>(node)) {
1462 for (
auto *successor : exit->successors)
1463 markDirty(successor);
1467 assert(
false &&
"unhandled node in forward propagation");
1471void Promoter::markDirty(LatticeNode *node) {
1476 dirtyNodes.push_back(node);
1487void Promoter::insertProbeBlocks() {
1491 SmallDenseSet<std::pair<BlockExit *, BlockEntry *>, 1> worklist;
1492 for (
auto *node : lattice->nodes) {
1493 auto *entry = dyn_cast<BlockEntry>(node);
1494 if (!entry || !entry->valueAfter->needed)
1496 unsigned numIncoming = 0;
1497 for (
auto *predecessor : entry->predecessors)
1498 if (predecessor->valueBefore->needed)
1500 if (numIncoming == 0 || numIncoming == entry->predecessors.size())
1502 for (
auto *predecessor : entry->predecessors)
1503 if (!predecessor->valueBefore->needed)
1504 worklist.insert({predecessor, entry});
1508 for (
auto [predecessor, successor] : worklist) {
1509 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting probe block towards " << successor
1510 <<
" after " << *predecessor->terminator <<
"\n");
1511 OpBuilder builder(predecessor->terminator);
1512 auto *newBlock = builder.createBlock(successor->block);
1513 for (
auto oldArg : successor->block->getArguments())
1514 newBlock->addArgument(oldArg.getType(), oldArg.
getLoc());
1515 cf::BranchOp::create(builder, predecessor->terminator->getLoc(),
1516 successor->block, newBlock->getArguments());
1517 for (
auto &blockOp : predecessor->terminator->getBlockOperands())
1518 if (blockOp.
get() == successor->block)
1519 blockOp.set(newBlock);
1522 auto *value = lattice->createValue();
1523 value->needed = successor->valueAfter->needed;
1524 auto *newEntry = lattice->createNode<BlockEntry>(newBlock, value);
1525 auto *newExit = lattice->createNode<BlockExit>(newBlock, value);
1526 newEntry->predecessors.push_back(predecessor);
1527 newExit->successors.push_back(successor);
1528 llvm::replace(successor->predecessors, predecessor, newExit);
1529 llvm::replace(predecessor->successors, successor, newEntry);
1536void Promoter::insertProbes() {
1537 for (
auto *node : lattice->nodes) {
1538 if (
auto *entry = dyn_cast<BlockEntry>(node))
1539 insertProbes(entry);
1545void Promoter::insertProbes(BlockEntry *node) {
1546 if (!node->valueAfter->needed)
1548 if (!node->predecessors.empty() &&
1549 llvm::all_of(node->predecessors, [](
auto *predecessor) {
1550 return predecessor->valueBefore->needed;
1553 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting probe for " << currentSlot
1554 <<
" in block " << node->block <<
"\n");
1555 auto builder = OpBuilder::atBlockBegin(node->block);
1557 if (Operation *op = currentSlot.getDefiningOp())
1558 if (op->getBlock() == node->block)
1559 builder.setInsertionPointAfterValue(currentSlot);
1560 auto value = ProbeOp::create(builder, currentSlot.getLoc(), currentSlot);
1561 auto *def = lattice->createDef(value, DriveCondition::never());
1562 node->insertedProbe = def;
1567void Promoter::insertDriveBlocks() {
1571 auto partialAt = [](LatticeValue *before, LatticeValue *after,
bool delayed,
1572 unsigned totalSuccessors) {
1573 Def *def = before->getReachingDef(delayed);
1574 if (!def || def->condition.isNever())
1576 (void)totalSuccessors;
1577 return !after->getReachingDef(delayed);
1580 SmallDenseSet<std::pair<BlockExit *, BlockEntry *>, 1> worklist;
1581 for (
auto *node : lattice->nodes) {
1582 auto *exit = dyn_cast<BlockExit>(node);
1588 for (
bool delayed : {
false,
true}) {
1589 Def *def = exit->valueBefore->getReachingDef(delayed);
1590 if (!def || def->condition.isNever())
1592 unsigned numContinues = 0;
1593 for (
auto *successor : exit->successors)
1594 if (successor->valueAfter->getReachingDef(delayed))
1596 if (numContinues == 0 || numContinues == exit->successors.size())
1598 for (
auto *successor : exit->successors)
1599 if (partialAt(exit->valueBefore, successor->valueAfter, delayed,
1600 exit->successors.size()))
1601 worklist.insert({exit, successor});
1606 for (
auto [predecessor, successor] : worklist) {
1607 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting drive block towards " << successor
1608 <<
" after " << *predecessor->terminator <<
"\n");
1609 OpBuilder builder(predecessor->terminator);
1610 auto *newBlock = builder.createBlock(successor->block);
1611 for (
auto oldArg : successor->block->getArguments())
1612 newBlock->addArgument(oldArg.getType(), oldArg.
getLoc());
1613 cf::BranchOp::create(builder, predecessor->terminator->getLoc(),
1614 successor->block, newBlock->getArguments());
1615 for (
auto &blockOp : predecessor->terminator->getBlockOperands())
1616 if (blockOp.
get() == successor->block)
1617 blockOp.set(newBlock);
1622 auto *value = lattice->createValue();
1623 value->needed = successor->valueAfter->needed;
1624 value->blockingReachingDef = predecessor->valueBefore->blockingReachingDef;
1625 value->delayedReachingDef = predecessor->valueBefore->delayedReachingDef;
1626 auto *newEntry = lattice->createNode<BlockEntry>(newBlock, value);
1627 auto *newExit = lattice->createNode<BlockExit>(newBlock, value);
1628 newEntry->predecessors.push_back(predecessor);
1629 newExit->successors.push_back(successor);
1630 llvm::replace(successor->predecessors, predecessor, newExit);
1631 llvm::replace(predecessor->successors, successor, newEntry);
1638void Promoter::insertDrives() {
1639 for (
auto *node : lattice->nodes) {
1640 if (
auto *exit = dyn_cast<BlockExit>(node))
1642 else if (
auto *drive = dyn_cast<DriveNode>(node))
1643 insertDrives(drive);
1649void Promoter::insertDrives(BlockExit *node) {
1650 auto builder = OpBuilder::atBlockTerminator(node->block);
1655 auto getTime = [&](
bool delta) -> ConstantTimeOp {
1656 auto &cached = (delta ? delayedTimeCache : blockingTimeCache)[node->block];
1658 cached = ConstantTimeOp::create(builder, node->terminator->getLoc(), 0,
1659 "ns", delta ? 1 : 0, delta ? 0 : 1);
1663 auto insertDriveForSlot = [&](
bool delayed) {
1664 Def *reachingDef = node->valueBefore->getReachingDef(delayed);
1665 if (!reachingDef || reachingDef->condition.isNever())
1667 if (!node->suspends && !node->successors.empty() &&
1668 llvm::all_of(node->successors, [&](
auto *successor) {
1669 return successor->valueAfter->getReachingDef(delayed) != nullptr;
1672 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting drive for " << currentSlot <<
" "
1673 << (delayed ?
"(delayed)" :
"(blocking)")
1674 <<
" before " << *node->terminator <<
"\n");
1675 auto time = getTime(delayed);
1676 auto value = reachingDef->getValueOrPlaceholder();
1677 auto enable = reachingDef->condition.isConditional()
1678 ? reachingDef->getConditionOrPlaceholder()
1680 DriveOp::create(builder, currentSlot.getLoc(), currentSlot, value, time,
1684 insertDriveForSlot(
false);
1685 insertDriveForSlot(
true);
1690void Promoter::insertDrives(DriveNode *node) {
1691 LLVM_DEBUG(llvm::dbgs() <<
"- Removing drive " << *node->op <<
"\n");
1692 pruner.eraseNow(node->op);
1701void Promoter::resolveDefinitions() {
1702 for (
auto *node : lattice->nodes) {
1703 if (
auto *probe = dyn_cast<ProbeNode>(node))
1704 resolveDefinitions(probe);
1705 else if (
auto *drive = dyn_cast<DriveNode>(node))
1706 resolveDefinitions(drive);
1711void Promoter::resolveDefinitions(ProbeNode *node) {
1712 Def *def = node->valueBefore->blockingReachingDef;
1713 assert(def &&
"no definition reaches probe");
1716 auto projections =
getProjections(node->op->getOperand(0), node->slot);
1719 auto builder = OpBuilder(node->op);
1720 auto value = def->getValueOrPlaceholder();
1724 LLVM_DEBUG(llvm::dbgs() <<
"- Replacing " << *node->op <<
" with " << value
1726 replaceValueWith(node->op->getResult(0), value);
1727 pruner.eraseNow(node->op);
1733void Promoter::resolveDefinitions(DriveNode *node) {
1738 if (!node->def->value || node->def->valueIsPlaceholder)
1739 resolveDefinitionValue(node);
1740 if (node->def->condition.isConditional() &&
1741 (!node->def->condition.getCondition() ||
1742 node->def->conditionIsPlaceholder))
1743 resolveDefinitionCondition(node);
1746void Promoter::resolveDefinitionValue(DriveNode *node) {
1749 Def *inDef = node->valueBefore->getReachingDef(
isDelayed(node->slot));
1750 assert(inDef &&
"no definition reaches drive");
1751 auto driveOp = node->getDriveOp();
1752 LLVM_DEBUG(llvm::dbgs() <<
"- Injecting value for " << driveOp <<
"\n");
1758 auto builder = OpBuilder(driveOp);
1759 auto value = inDef->getValueOrPlaceholder();
1763 pruner.eraseLaterIfUnused(value);
1764 if (!driveOp.getEnable())
1765 value = driveOp.getValue();
1768 driveOp.getLoc(), driveOp.getEnable(), driveOp.getValue(), value);
1774 if (node->def->valueIsPlaceholder) {
1775 auto *placeholder = node->def->value.getDefiningOp();
1776 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1777 "placeholder replaced but valueIsPlaceholder still set");
1778 replaceValueWith(placeholder->getResult(0), value);
1779 pruner.eraseNow(placeholder);
1780 node->def->valueIsPlaceholder =
false;
1782 node->def->value = value;
1785void Promoter::resolveDefinitionCondition(DriveNode *node) {
1788 Def *inDef = node->valueBefore->getReachingDef(
isDelayed(node->slot));
1789 assert(inDef &&
"no definition reaches drive");
1790 auto driveOp = node->getDriveOp();
1791 LLVM_DEBUG(llvm::dbgs() <<
"- Mutating condition for " << driveOp <<
"\n");
1792 OpBuilder builder(driveOp);
1797 if (inDef->condition.isNever())
1798 condition = driveOp.getEnable();
1801 builder.createOrFold<
comb::OrOp>(driveOp.getLoc(), driveOp.getEnable(),
1802 inDef->getConditionOrPlaceholder());
1805 if (node->def->conditionIsPlaceholder) {
1806 auto *placeholder = node->def->condition.getCondition().getDefiningOp();
1807 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1808 "placeholder replaced but conditionIsPlaceholder still set");
1809 replaceValueWith(placeholder->getResult(0), condition);
1810 pruner.eraseNow(placeholder);
1811 node->def->conditionIsPlaceholder =
false;
1813 node->def->condition.setCondition(condition);
1821void Promoter::insertBlockArgs() {
1822 bool anyArgsInserted =
true;
1823 while (anyArgsInserted) {
1824 anyArgsInserted =
false;
1825 for (
auto *node : lattice->nodes)
1826 if (auto *entry = dyn_cast<BlockEntry>(node))
1827 anyArgsInserted |= insertBlockArgs(entry);
1839bool Promoter::insertBlockArgs(BlockEntry *node) {
1845 enum class Which { Value, Condition };
1846 SmallVector<std::pair<DefSlot, Which>> neededSlots;
1847 auto addNeededSlot = [&](
DefSlot slot) {
1848 auto *def = node->getMerged(
isDelayed(slot));
1851 if (!node->valueAfter->getReachingDef(
isDelayed(slot)))
1853 if (def->valueIsPlaceholder)
1854 neededSlots.push_back({slot, Which::Value});
1855 if (def->conditionIsPlaceholder)
1856 neededSlots.push_back({slot, Which::Condition});
1860 if (neededSlots.empty())
1862 LLVM_DEBUG(llvm::dbgs() <<
"- Adding " << neededSlots.size()
1863 <<
" args to block " << node->block <<
"\n");
1866 for (
auto [slot, which] : neededSlots) {
1867 auto *def = node->getMerged(
isDelayed(slot));
1870 case Which::Value: {
1873 auto *placeholder = def->value.getDefiningOp();
1874 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1875 "placeholder replaced but valueIsPlaceholder still set");
1877 replaceValueWith(placeholder->getResult(0), arg);
1878 pruner.eraseNow(placeholder);
1880 def->valueIsPlaceholder =
false;
1883 case Which::Condition: {
1887 auto *placeholder = def->condition.getCondition().getDefiningOp();
1888 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1889 "placeholder replaced but conditionIsPlaceholder still set");
1890 auto conditionArg = node->block->addArgument(
1891 IntegerType::get(region.getContext(), 1),
getLoc(slot));
1892 replaceValueWith(placeholder->getResult(0), conditionArg);
1893 pruner.eraseNow(placeholder);
1894 def->condition.setCondition(conditionArg);
1895 def->conditionIsPlaceholder =
false;
1902 for (
auto *predecessor : node->predecessors) {
1904 SmallVector<Value> args;
1905 for (
auto [slot, which] : neededSlots) {
1906 Def *def = predecessor->valueBefore->getReachingDef(
isDelayed(slot));
1907 auto builder = OpBuilder::atBlockTerminator(predecessor->block);
1911 args.push_back(def->getValueOrPlaceholder());
1914 auto flatType = builder.getIntegerType(hw::getBitWidth(type));
1917 if (type != flatType)
1919 args.push_back(value);
1922 case Which::Condition:
1924 args.push_back(def->getConditionOrPlaceholder());
1927 builder.getI1Type(), 0));
1934 auto branchOp = cast<BranchOpInterface>(predecessor->terminator);
1935 for (
auto &blockOperand : branchOp->getBlockOperands())
1936 if (blockOperand.
get() == node->block)
1937 branchOp.getSuccessorOperands(blockOperand.getOperandNumber())
1946void Promoter::replaceValueWith(Value oldValue, Value newValue) {
1947 oldValue.replaceAllUsesWith(newValue);
1948 for (
auto *def : lattice->defs) {
1949 if (def->value == oldValue)
1950 def->value = newValue;
1951 if (def->condition.isConditional() &&
1952 def->condition.getCondition() == oldValue)
1953 def->condition.setCondition(newValue);
1964void Promoter::removeUnusedLocalSignal(SignalOp signalOp) {
1968 SmallVector<Operation *> worklist;
1969 worklist.push_back(signalOp);
1970 while (!worklist.empty()) {
1971 auto *op = worklist.pop_back_val();
1972 if (!isa<SignalOp, DriveOp, SigArrayGetOp, SigArraySliceOp, SigExtractOp,
1973 SigStructExtractOp>(op))
1975 for (
auto *user : op->getUsers())
1976 if (users.insert(user))
1977 worklist.push_back(user);
1982 LLVM_DEBUG(llvm::dbgs() <<
"- Removing local signal " << *signalOp <<
"\n");
1983 for (
auto *op :
llvm::reverse(users))
1984 pruner.eraseNow(op);
1992struct Mem2RegPass :
public llhd::impl::Mem2RegPassBase<Mem2RegPass> {
1993 void runOnOperation()
override;
1997void Mem2RegPass::runOnOperation() {
1998 SmallVector<Region *> regions;
1999 getOperation()->walk<WalkOrder::PreOrder>([&](Operation *op) {
2000 if (isa<ProcessOp, FinalOp, CombinationalOp>(op)) {
2001 auto ®ion = op->getRegion(0);
2002 if (!region.empty())
2003 regions.push_back(®ion);
2004 return WalkResult::skip();
2006 return WalkResult::advance();
2008 for (
auto *region : regions)
2009 if (failed(Promoter(*region).promote()))
2010 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 bool isEqual(DriveCondition lhs, DriveCondition rhs)
static unsigned getHashValue(DriveCondition d)