14#include "mlir/Analysis/Liveness.h"
15#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
16#include "mlir/IR/Dominance.h"
17#include "llvm/Support/Debug.h"
18#include "llvm/Support/GenericIteratedDominanceFrontier.h"
20#define DEBUG_TYPE "llhd-mem2reg"
24#define GEN_PASS_DEF_MEM2REGPASS
25#include "circt/Dialect/LLHD/LLHDPasses.h.inc"
32using llvm::PointerIntPair;
33using llvm::SmallDenseSet;
35using llvm::SpecificBumpPtrAllocator;
39 if (
auto timeOp = value.getDefiningOp<ConstantTimeOp>()) {
40 auto t = timeOp.getValue();
41 return t.getTime() == 0 && t.getDelta() == 0 && t.getEpsilon() == 1;
48 if (
auto timeOp = value.getDefiningOp<ConstantTimeOp>()) {
49 auto t = timeOp.getValue();
50 return t.getTime() == 0 && t.getDelta() == 1 && t.getEpsilon() == 0;
58 if (
auto driveOp = dyn_cast<DriveOp>(op))
66 if (
auto driveOp = dyn_cast<DriveOp>(op))
80struct DriveCondition {
81 static DriveCondition never() {
return ConditionAndMode(Value{},
Never); }
82 static DriveCondition always() {
return ConditionAndMode(Value{}, Always); }
83 static DriveCondition conditional(Value condition = {}) {
84 return ConditionAndMode(condition, Conditional);
87 bool isNever()
const {
return conditionAndMode.getInt() ==
Never; }
88 bool isAlways()
const {
return conditionAndMode.getInt() == Always; }
89 bool isConditional()
const {
90 return conditionAndMode.getInt() == Conditional;
93 Value getCondition()
const {
return conditionAndMode.getPointer(); }
94 void setCondition(Value condition) { conditionAndMode.setPointer(condition); }
96 bool operator==(
const DriveCondition &other)
const {
97 return conditionAndMode == other.conditionAndMode;
99 bool operator!=(
const DriveCondition &other)
const {
100 return conditionAndMode != other.conditionAndMode;
109 typedef PointerIntPair<Value, 2> ConditionAndMode;
110 ConditionAndMode conditionAndMode;
112 DriveCondition(ConditionAndMode conditionAndMode)
113 : conditionAndMode(conditionAndMode) {}
114 friend DenseMapInfo<DriveCondition>;
126 DriveCondition condition;
127 bool valueIsPlaceholder =
false;
128 bool conditionIsPlaceholder =
false;
130 Def(Value value, DriveCondition condition)
131 : block(value.getParentBlock()), type(value.getType()), value(value),
132 condition(condition) {}
133 Def(Block *block, Type type, DriveCondition condition)
134 : block(block), type(type), condition(condition) {}
136 Value getValueOrPlaceholder();
137 Value getConditionOrPlaceholder();
143Value Def::getValueOrPlaceholder() {
145 auto builder = OpBuilder::atBlockBegin(block);
146 value = UnrealizedConversionCastOp::create(builder, builder.getUnknownLoc(),
149 valueIsPlaceholder =
true;
158Value Def::getConditionOrPlaceholder() {
159 if (!condition.getCondition()) {
160 auto builder = OpBuilder::atBlockBegin(block);
162 if (condition.isNever()) {
164 builder.getI1Type(), 0);
165 }
else if (condition.isAlways()) {
167 builder.getI1Type(), 1);
170 UnrealizedConversionCastOp::create(builder, builder.getUnknownLoc(),
171 builder.getI1Type(), ValueRange{})
173 conditionIsPlaceholder =
true;
175 condition.setCondition(value);
177 return condition.getCondition();
193 static bool isEqual(DriveCondition lhs, DriveCondition rhs) {
211 return cast<RefType>(slot.getType()).getNestedType();
227 LatticeNode *nodeBefore =
nullptr;
228 LatticeNode *nodeAfter =
nullptr;
229 SmallDenseSet<Value, 1> neededDefs;
234 enum class Kind { BlockEntry, BlockExit, Probe, Drive, Signal };
238 LatticeNode(Kind kind) : kind(kind) {}
241struct BlockEntry :
public LatticeNode {
243 LatticeValue *valueAfter;
244 SmallVector<BlockExit *, 2> predecessors;
245 SmallVector<std::pair<Value, Def *>, 0> insertedProbes;
248 BlockEntry(Block *block, LatticeValue *valueAfter)
249 : LatticeNode(Kind::BlockEntry), block(block), valueAfter(valueAfter) {
250 assert(!valueAfter->nodeBefore);
251 valueAfter->nodeBefore =
this;
254 static bool classof(
const LatticeNode *n) {
255 return n->kind == Kind::BlockEntry;
259struct BlockExit :
public LatticeNode {
261 LatticeValue *valueBefore;
262 SmallVector<BlockEntry *, 2> successors;
263 Operation *terminator;
266 BlockExit(Block *block, LatticeValue *valueBefore)
267 : LatticeNode(Kind::BlockExit), block(block), valueBefore(valueBefore),
268 terminator(block->getTerminator()),
269 suspends(isa<HaltOp, WaitOp>(terminator)) {
270 assert(!valueBefore->nodeAfter);
271 valueBefore->nodeAfter =
this;
274 static bool classof(
const LatticeNode *n) {
275 return n->kind == Kind::BlockExit;
279struct OpNode :
public LatticeNode {
281 LatticeValue *valueBefore;
282 LatticeValue *valueAfter;
284 OpNode(Kind kind, Operation *op, LatticeValue *valueBefore,
285 LatticeValue *valueAfter)
286 : LatticeNode(kind), op(op), valueBefore(valueBefore),
287 valueAfter(valueAfter) {
288 assert(!valueBefore->nodeAfter);
289 assert(!valueAfter->nodeBefore);
290 valueBefore->nodeAfter =
this;
291 valueAfter->nodeBefore =
this;
294 static bool classof(
const LatticeNode *n) {
295 return isa<ProbeNode, DriveNode, SignalNode>(n);
299struct ProbeNode :
public OpNode {
302 ProbeNode(ProbeOp op, Value slot, LatticeValue *valueBefore,
303 LatticeValue *valueAfter)
304 : OpNode(Kind::Probe, op, valueBefore, valueAfter), slot(slot) {}
306 static bool classof(
const LatticeNode *n) {
return n->kind == Kind::Probe; }
309struct DriveNode :
public OpNode {
313 DriveNode(DriveOp op, Value slot, Def *def, LatticeValue *valueBefore,
314 LatticeValue *valueAfter)
315 : OpNode(Kind::Drive, op, valueBefore, valueAfter),
323 bool drivesProjection()
const {
return op->getOperand(0) !=
getSlot(slot); }
326 DriveOp getDriveOp()
const {
return cast<DriveOp>(op); }
328 static bool classof(
const LatticeNode *n) {
return n->kind == Kind::Drive; }
331struct SignalNode :
public OpNode {
334 SignalNode(SignalOp op, Def *def, LatticeValue *valueBefore,
335 LatticeValue *valueAfter)
336 : OpNode(Kind::Signal, op, valueBefore, valueAfter), def(def) {}
338 SignalOp getSignalOp()
const {
return cast<SignalOp>(op); }
341 static bool classof(
const LatticeNode *n) {
return n->kind == Kind::Signal; }
348 LatticeValue *createValue() {
349 auto *value =
new (valueAllocator.Allocate()) LatticeValue();
350 values.push_back(value);
355 template <
class T,
typename... Args>
356 T *createNode(Args... args) {
358 new (getAllocator<T>().Allocate()) T(std::forward<Args>(args)...);
359 nodes.push_back(node);
364 template <
typename... Args>
365 Def *createDef(Args... args) {
366 auto *def =
new (defAllocator.Allocate()) Def(std::forward<Args>(args)...);
372 Def *createDef(Value value, DriveCondition mode) {
373 auto &slot = defsForValues[{value, mode}];
375 slot =
new (defAllocator.Allocate()) Def(value, mode);
376 defs.push_back(slot);
382 void dump(llvm::raw_ostream &os = llvm::dbgs());
386 std::vector<LatticeNode *> nodes;
388 std::vector<LatticeValue *> values;
390 std::vector<Def *> defs;
394 DenseMap<std::pair<Value, DriveCondition>, Def *> defsForValues;
397 SpecificBumpPtrAllocator<LatticeValue> valueAllocator;
398 SpecificBumpPtrAllocator<Def> defAllocator;
399 SpecificBumpPtrAllocator<BlockEntry> blockEntryAllocator;
400 SpecificBumpPtrAllocator<BlockExit> blockExitAllocator;
401 SpecificBumpPtrAllocator<ProbeNode> probeAllocator;
402 SpecificBumpPtrAllocator<DriveNode> driveAllocator;
403 SpecificBumpPtrAllocator<SignalNode> signalAllocator;
407 SpecificBumpPtrAllocator<T> &getAllocator();
413SpecificBumpPtrAllocator<BlockEntry> &Lattice::getAllocator() {
414 return blockEntryAllocator;
417SpecificBumpPtrAllocator<BlockExit> &Lattice::getAllocator() {
418 return blockExitAllocator;
421SpecificBumpPtrAllocator<ProbeNode> &Lattice::getAllocator() {
422 return probeAllocator;
425SpecificBumpPtrAllocator<DriveNode> &Lattice::getAllocator() {
426 return driveAllocator;
429SpecificBumpPtrAllocator<SignalNode> &Lattice::getAllocator() {
430 return signalAllocator;
437void Lattice::dump(llvm::raw_ostream &os) {
443 auto blockName = [&](
Block *block) {
444 unsigned id = blockNames.insert({block, blockNames.size()}).first->second;
445 return std::string(
"bb") + llvm::utostr(
id);
448 auto memName = [&](
DefSlot value) {
450 memNames.insert({
getSlot(value), memNames.size()}).first->second;
451 return std::string(
"mem") + llvm::utostr(
id) +
455 auto defName = [&](Def *def) {
456 unsigned id = defNames.insert({def, defNames.size()}).first->second;
457 return std::string(
"def") + llvm::utostr(
id);
461 for (
auto *node : nodes)
462 if (auto *entry = dyn_cast<BlockEntry>(node))
463 blockName(entry->block);
467 for (
auto *node : nodes) {
468 auto *entry = dyn_cast<BlockEntry>(node);
473 os <<
" " << blockName(entry->block) <<
":";
474 if (entry->predecessors.empty()) {
475 os <<
" // no predecessors";
478 for (
auto *node : entry->predecessors)
479 os <<
" " << blockName(node->block);
484 auto *value = entry->valueAfter;
487 if (!value->neededDefs.empty()) {
489 for (
auto mem : value->neededDefs)
493 if (!value->reachingDefs.empty()) {
495 for (
auto [mem, def] : value->reachingDefs) {
496 os <<
" " << memName(mem) <<
"=" << defName(def);
497 if (def->condition.isNever())
499 else if (def->condition.isAlways())
506 if (isa<BlockExit>(value->nodeAfter))
510 if (
auto *node = dyn_cast<ProbeNode>(value->nodeAfter))
511 os <<
" probe " << memName(
blockingSlot(node->slot)) <<
"\n";
512 else if (
auto *node = dyn_cast<DriveNode>(value->nodeAfter))
513 os <<
" drive " << memName(node->slot) <<
"\n";
514 else if (
auto *node = dyn_cast<SignalNode>(value->nodeAfter))
515 os <<
" signal " << memName(node->getSlot()) <<
"\n";
520 value = cast<OpNode>(value->nodeAfter)->valueAfter;
524 auto *exit = cast<BlockExit>(value->nodeAfter);
525 if (isa<WaitOp>(exit->terminator))
527 else if (exit->successors.empty())
531 for (
auto *node : exit->successors)
532 os <<
" " << blockName(node->block);
534 os <<
" // suspends";
539 for (
auto [mem,
id] : memNames)
540 os <<
" mem" << id <<
": " << mem <<
"\n";
574 while (fromSignal != toSlot) {
575 auto *op = cast<OpResult>(fromSignal).getOwner();
576 stack.push_back({op, Value()});
577 fromSignal = op->getOperand(0);
590 for (
auto &projection : llvm::reverse(projections)) {
591 projection.into = value;
592 value = TypeSwitch<Operation *, Value>(projection.op)
593 .Case<SigArrayGetOp>([&](
auto op) {
595 op.getLoc(), value, op.getIndex());
597 .Case<SigStructExtractOp>([&](
auto op) {
600 if (isa<hw::UnionType>(value.getType()))
601 return builder.createOrFold<hw::UnionExtractOp>(
602 op.getLoc(), value, op.getFieldAttr());
604 op.getLoc(), value, op.getFieldAttr());
606 .Case<SigExtractOp>([&](
auto op) {
607 auto type = cast<RefType>(op.getType()).getNestedType();
608 auto width = type.getIntOrFloatBitWidth();
609 return comb::createDynamicExtract(builder, op.getLoc(), value,
610 op.getLowBit(), width);
630 for (
auto projection : projections) {
631 value = TypeSwitch<Operation *, Value>(projection.op)
632 .Case<SigArrayGetOp>([&](
auto op) {
633 return builder.createOrFold<hw::ArrayInjectOp>(
634 op.getLoc(), projection.into, op.getIndex(), value);
636 .Case<SigStructExtractOp>([&](
auto op) {
640 dyn_cast<hw::UnionType>(projection.into.getType()))
641 return builder.createOrFold<hw::UnionCreateOp>(
642 op.getLoc(), unionTy, op.getFieldAttr(), value);
643 return builder.createOrFold<hw::StructInjectOp>(
644 op.getLoc(), projection.into, op.getFieldAttr(), value);
646 .Case<SigExtractOp>([&](
auto op) {
647 return comb::createDynamicInject(builder, op.getLoc(),
649 op.getLowBit(), value);
662 Promoter(Region ®ion) : region(region) {}
663 LogicalResult promote();
665 void findPromotableSlots();
666 Value resolveSlot(Value projectionOrSlot);
668 void captureAcrossWait();
669 void captureAcrossWait(Value value, ArrayRef<WaitOp> waitOps,
670 Liveness &liveness, DominanceInfo &dominance);
672 void constructLattice();
673 void propagateBackward();
674 void propagateBackward(LatticeNode *node);
675 void propagateForward();
676 void propagateForward(
bool optimisticMerges, DominanceInfo &dominance);
677 void propagateForward(LatticeNode *node,
bool optimisticMerges,
678 DominanceInfo &dominance);
679 void markDirty(LatticeNode *node);
681 void insertProbeBlocks();
683 void insertProbes(BlockEntry *node);
685 void insertDriveBlocks();
687 void insertDrives(BlockExit *node);
688 void insertDrives(DriveNode *node);
690 void resolveDefinitions();
691 void resolveDefinitions(ProbeNode *node);
692 void resolveDefinitions(DriveNode *node);
693 void resolveDefinitionValue(DriveNode *node);
694 void resolveDefinitionCondition(DriveNode *node);
696 void insertBlockArgs();
697 bool insertBlockArgs(BlockEntry *node);
698 void replaceValueWith(Value oldValue, Value newValue);
700 void removeUnusedLocalSignals();
701 void removeUnusedLocalSignal(SignalNode *signal);
709 SmallVector<Value> slots;
714 SmallDenseSet<Value> promotable;
720 SmallVector<LatticeNode *> dirtyNodes;
727LogicalResult Promoter::promote() {
731 findPromotableSlots();
743 llvm::dbgs() <<
"Initial lattice:\n";
750 llvm::dbgs() <<
"After backward propagation:\n";
758 llvm::dbgs() <<
"After probe insertion:\n";
765 llvm::dbgs() <<
"After forward propagation:\n";
770 resolveDefinitions();
776 llvm::dbgs() <<
"After def resolution and drive insertion:\n";
784 removeUnusedLocalSignals();
793void Promoter::findPromotableSlots() {
794 SmallPtrSet<Value, 8> seenSlots;
795 SmallPtrSet<Operation *, 8> checkedUsers;
796 SmallVector<Operation *, 8> userWorklist;
798 region.walk([&](Operation *op) {
799 for (
auto operand : op->getOperands()) {
800 if (!seenSlots.insert(operand).second)
806 if (!operand.getDefiningOp<llhd::SignalOp>())
810 bool hasProjection =
false;
811 bool hasBlockingDrive =
false;
812 bool hasDeltaDrive =
false;
813 auto checkUser = [&](Operation *user) ->
bool {
815 if (region.isProperAncestor(user->getParentRegion()))
818 if (user->getParentRegion() != ®ion)
824 if (isa<SigArrayGetOp, SigExtractOp, SigStructExtractOp>(user)) {
825 hasProjection =
true;
826 for (
auto *projectionUser : user->getUsers()) {
827 if (isa<SigArrayGetOp, SigExtractOp, SigStructExtractOp>(
829 projectionUser->getBlock() != user->getBlock())
833 if (checkedUsers.insert(projectionUser).second)
834 userWorklist.push_back(projectionUser);
836 projections.insert({user->getResult(0), operand});
844 checkedUsers.clear();
845 if (!llvm::all_of(operand.getUsers(), [&](
auto *user) {
847 if (checkedUsers.insert(user).second)
848 userWorklist.push_back(user);
849 while (!userWorklist.empty() && allOk)
850 allOk &= checkUser(userWorklist.pop_back_val());
851 userWorklist.clear();
859 if (hasProjection && hasBlockingDrive && hasDeltaDrive)
867 static_cast<unsigned>(bitWidth) > IntegerType::kMaxWidth)
870 slots.push_back(operand);
875 promotable.insert(slots.begin(), slots.end());
876 for (
auto [projection, slot] :
llvm::make_early_inc_range(projections))
877 if (!promotable.contains(slot))
878 projections.erase(projection);
879 for (
auto [projection, slot] : projections)
880 promotable.insert(projection);
882 LLVM_DEBUG(llvm::dbgs() <<
"Found " << slots.size() <<
" promotable slots, "
883 << promotable.size() <<
" promotable values\n");
888Value Promoter::resolveSlot(Value projectionOrSlot) {
889 if (
auto slot = projections.lookup(projectionOrSlot))
891 return projectionOrSlot;
898void Promoter::captureAcrossWait() {
899 if (region.hasOneBlock())
902 SmallVector<WaitOp> waitOps;
903 for (
auto &block : region)
904 if (auto waitOp = dyn_cast<WaitOp>(block.getTerminator()))
905 waitOps.push_back(waitOp);
907 DominanceInfo dominance(region.getParentOp());
908 Liveness liveness(region.getParentOp());
910 llvm::DenseSet<Value> alreadyCaptured;
912 auto isDefinedInRegion = [&](Value v) {
913 return v.getParentRegion() == ®ion;
916 for (
auto waitOp : waitOps) {
917 Block *waitBlock = waitOp->getBlock();
918 const auto &liveOutValues = liveness.getLiveOut(waitBlock);
920 for (Value v : liveOutValues) {
921 if (!isDefinedInRegion(v))
924 if (!alreadyCaptured.insert(v).second)
927 captureAcrossWait(v, waitOps, liveness, dominance);
936void Promoter::captureAcrossWait(Value value, ArrayRef<WaitOp> waitOps,
937 Liveness &liveness, DominanceInfo &dominance) {
939 llvm::dbgs() <<
"Capture " << value <<
"\n";
940 for (
auto waitOp : waitOps)
941 llvm::dbgs() <<
"- Across " << waitOp <<
"\n";
946 auto &domTree = dominance.getDomTree(®ion);
947 llvm::IDFCalculatorBase<Block, false> idfCalculator(domTree);
951 SmallPtrSet<Block *, 4> definingBlocks;
952 definingBlocks.insert(value.getParentBlock());
953 for (
auto waitOp : waitOps)
954 definingBlocks.insert(waitOp.getDest());
955 idfCalculator.setDefiningBlocks(definingBlocks);
958 SmallPtrSet<Block *, 16> liveInBlocks;
959 for (
auto &block : region)
960 if (liveness.getLiveness(&block)->isLiveIn(value))
961 liveInBlocks.insert(&block);
962 idfCalculator.setLiveInBlocks(liveInBlocks);
966 SmallVector<Block *> mergePointsVec;
967 idfCalculator.calculate(mergePointsVec);
968 SmallPtrSet<Block *, 16> mergePoints(mergePointsVec.begin(),
969 mergePointsVec.end());
970 for (
auto waitOp : waitOps)
971 mergePoints.insert(waitOp.getDest());
972 LLVM_DEBUG(llvm::dbgs() <<
"- " << mergePoints.size() <<
" merge points\n");
977 struct WorklistItem {
978 DominanceInfoNode *domNode;
981 SmallVector<WorklistItem> worklist;
982 worklist.push_back({domTree.getNode(value.getParentBlock()), value});
984 while (!worklist.empty()) {
985 auto item = worklist.pop_back_val();
986 auto *block = item.domNode->getBlock();
989 if (mergePoints.contains(block))
990 item.reachingDef = block->addArgument(value.getType(), value.getLoc());
994 for (
auto &op : *block)
995 op.replaceUsesOfWith(value, item.reachingDef);
999 if (
auto branchOp = dyn_cast<BranchOpInterface>(block->getTerminator())) {
1000 for (
auto &blockOperand : branchOp->getBlockOperands())
1001 if (mergePoints.contains(blockOperand.
get()))
1002 branchOp.getSuccessorOperands(blockOperand.getOperandNumber())
1003 .
append(item.reachingDef);
1004 }
else if (
auto waitOp = dyn_cast<WaitOp>(block->getTerminator())) {
1005 if (mergePoints.contains(waitOp.getDest()))
1006 waitOp.getDestOperandsMutable().append(item.reachingDef);
1009 for (
auto *child : item.domNode->children())
1010 worklist.push_back({child, item.reachingDef});
1020void Promoter::constructLattice() {
1023 for (
auto &block : region) {
1024 auto *entry = lattice.createNode<BlockEntry>(&block, lattice.createValue());
1025 blockEntries.insert({&block, entry});
1029 for (
auto &block : region) {
1030 auto *valueBefore = blockEntries.lookup(&block)->valueAfter;
1033 for (
auto &op : block.without_terminator()) {
1035 if (
auto probeOp = dyn_cast<ProbeOp>(op)) {
1036 if (!promotable.contains(probeOp.getSignal()))
1038 auto *node = lattice.createNode<ProbeNode>(
1039 probeOp, resolveSlot(probeOp.getSignal()), valueBefore,
1040 lattice.createValue());
1041 valueBefore = node->valueAfter;
1046 if (
auto driveOp = dyn_cast<DriveOp>(op)) {
1049 if (!promotable.contains(driveOp.getSignal()))
1051 auto condition = DriveCondition::always();
1052 if (
auto enable = driveOp.getEnable())
1053 condition = DriveCondition::conditional(enable);
1057 auto slot = resolveSlot(driveOp.getSignal());
1058 auto *def = driveOp.getSignal() == slot
1059 ? lattice.createDef(driveOp.getValue(), condition)
1060 : lattice.createDef(driveOp->getBlock(),
1062 auto *node = lattice.createNode<DriveNode>(
1063 driveOp, slot, def, valueBefore, lattice.createValue());
1064 valueBefore = node->valueAfter;
1069 if (
auto signalOp = dyn_cast<SignalOp>(op)) {
1070 if (!promotable.contains(signalOp))
1073 lattice.createDef(signalOp.getInit(), DriveCondition::never());
1074 auto *node = lattice.createNode<SignalNode>(signalOp, def, valueBefore,
1075 lattice.createValue());
1076 valueBefore = node->valueAfter;
1082 auto *exit = lattice.createNode<BlockExit>(&block, valueBefore);
1083 for (
auto *otherBlock : exit->terminator->getSuccessors()) {
1084 auto *otherEntry = blockEntries.lookup(otherBlock);
1085 exit->successors.push_back(otherEntry);
1086 otherEntry->predecessors.push_back(exit);
1093void Promoter::propagateBackward() {
1094 for (
auto *node : lattice.nodes)
1095 propagateBackward(node);
1096 SmallVector<LatticeNode *> nodes;
1097 while (!dirtyNodes.empty()) {
1098 std::swap(dirtyNodes, nodes);
1099 for (
auto *node : nodes) {
1100 node->dirty =
false;
1101 propagateBackward(node);
1109void Promoter::propagateBackward(LatticeNode *node) {
1110 auto update = [&](LatticeValue *value,
auto &neededDefs) {
1111 if (value->neededDefs != neededDefs) {
1112 value->neededDefs = neededDefs;
1113 markDirty(value->nodeBefore);
1118 if (
auto *probe = dyn_cast<ProbeNode>(node)) {
1119 auto needed = probe->valueAfter->neededDefs;
1120 needed.insert(probe->slot);
1121 update(probe->valueBefore, needed);
1131 if (
auto *drive = dyn_cast<DriveNode>(node)) {
1132 auto needed = drive->valueAfter->neededDefs;
1133 if (drive->drivesProjection())
1134 needed.insert(
getSlot(drive->slot));
1135 else if (!
isDelayed(drive->slot) && !drive->getDriveOp().getEnable())
1136 needed.erase(
getSlot(drive->slot));
1137 update(drive->valueBefore, needed);
1144 if (
auto *signal = dyn_cast<SignalNode>(node)) {
1145 auto needed = signal->valueAfter->neededDefs;
1146 needed.erase(signal->getSignalOp());
1147 update(signal->valueBefore, needed);
1152 if (
auto *entry = dyn_cast<BlockEntry>(node)) {
1153 for (
auto *predecessor : entry->predecessors)
1154 markDirty(predecessor);
1159 if (
auto *exit = dyn_cast<BlockExit>(node)) {
1162 SmallDenseSet<Value, 1> needed;
1163 for (
auto *successors : exit->successors)
1164 needed.insert(successors->valueAfter->neededDefs.begin(),
1165 successors->valueAfter->neededDefs.
end());
1166 update(exit->valueBefore, needed);
1170 assert(
false &&
"unhandled node in backward propagation");
1173void Promoter::propagateForward() {
1174 DominanceInfo dominance(region.getParentOp());
1175 propagateForward(
true, dominance);
1176 propagateForward(
false, dominance);
1187void Promoter::propagateForward(
bool optimisticMerges,
1188 DominanceInfo &dominance) {
1189 for (
auto *node : lattice.nodes)
1190 propagateForward(node, optimisticMerges, dominance);
1191 SmallVector<LatticeNode *> nodes;
1192 while (!dirtyNodes.empty()) {
1193 std::swap(dirtyNodes, nodes);
1194 for (
auto *node : nodes) {
1195 node->dirty =
false;
1196 propagateForward(node, optimisticMerges, dominance);
1203void Promoter::propagateForward(LatticeNode *node,
bool optimisticMerges,
1204 DominanceInfo &dominance) {
1205 auto update = [&](LatticeValue *value,
auto &reachingDefs) {
1206 if (value->reachingDefs != reachingDefs) {
1207 value->reachingDefs = reachingDefs;
1208 markDirty(value->nodeAfter);
1213 if (
auto *probe = dyn_cast<ProbeNode>(node)) {
1214 update(probe->valueAfter, probe->valueBefore->reachingDefs);
1228 if (
auto *drive = dyn_cast<DriveNode>(node)) {
1229 auto reaching = drive->valueBefore->reachingDefs;
1235 if (drive->drivesProjection() || drive->getDriveOp().getEnable()) {
1236 auto *inDef = reaching.lookup(drive->slot);
1238 if (drive->def->value != inDef->value)
1239 drive->def->value = {};
1240 if (inDef->condition.isAlways() || drive->def->condition.isAlways())
1241 drive->def->condition = DriveCondition::always();
1242 else if (drive->def->condition != inDef->condition)
1243 drive->def->condition = DriveCondition::conditional();
1247 reaching[drive->slot] = drive->def;
1250 update(drive->valueAfter, reaching);
1257 if (
auto *signal = dyn_cast<SignalNode>(node)) {
1258 auto reaching = signal->valueBefore->reachingDefs;
1259 reaching[signal->getSlot()] = signal->def;
1260 reaching.erase(
delayedSlot(signal->getSignalOp()));
1261 update(signal->valueAfter, reaching);
1267 if (
auto *entry = dyn_cast<BlockEntry>(node)) {
1270 for (
auto [slot, insertedProbe] : entry->insertedProbes) {
1278 for (
auto *predecessor : entry->predecessors) {
1279 if (predecessor->suspends)
1283 for (
auto &defEntry : predecessor->valueBefore->reachingDefs) {
1284 auto slot =
getSlot(defEntry.getFirst());
1285 auto *slotBlock = slot.getDefiningOp()->getBlock();
1286 if (!dominance.dominates(slotBlock, entry->block))
1288 reachingDefs.insert(defEntry);
1292 for (
auto pair : reachingDefs) {
1294 Def *reachingDef = pair.second;
1295 DriveCondition reachingDefCondition = reachingDef->condition;
1298 if (reaching.contains(slot))
1308 if (llvm::any_of(entry->predecessors, [&](
auto *predecessor) {
1309 return predecessor->suspends;
1313 for (
auto *predecessor : entry->predecessors) {
1314 auto otherDef = predecessor->valueBefore->reachingDefs.lookup(slot);
1315 if (!otherDef && optimisticMerges)
1319 if (reachingDef != otherDef)
1320 reachingDef =
nullptr;
1324 otherDef ? otherDef->condition : DriveCondition::never();
1325 if (reachingDefCondition != condition)
1326 reachingDefCondition = DriveCondition::conditional();
1332 reachingDef = entry->mergedDefs.lookup(slot);
1334 reachingDef = lattice.createDef(entry->block,
getStoredType(slot),
1335 reachingDefCondition);
1336 entry->mergedDefs.insert({slot, reachingDef});
1338 reachingDef->condition = reachingDefCondition;
1340 reaching.insert({slot, reachingDef});
1343 update(entry->valueAfter, reaching);
1348 if (
auto *exit = dyn_cast<BlockExit>(node)) {
1349 for (
auto *successor : exit->successors)
1350 markDirty(successor);
1354 assert(
false &&
"unhandled node in forward propagation");
1358void Promoter::markDirty(LatticeNode *node) {
1363 dirtyNodes.push_back(node);
1374void Promoter::insertProbeBlocks() {
1378 SmallDenseSet<std::pair<BlockExit *, BlockEntry *>, 1> worklist;
1379 for (
auto *node : lattice.nodes) {
1380 if (
auto *entry = dyn_cast<BlockEntry>(node)) {
1381 SmallVector<Value> partialSlots;
1382 for (
auto slot : entry->valueAfter->neededDefs) {
1383 unsigned numIncoming = 0;
1384 for (
auto *predecessor : entry->predecessors)
1385 if (predecessor->valueBefore->neededDefs.contains(slot))
1387 if (numIncoming != 0 && numIncoming != entry->predecessors.size())
1388 partialSlots.push_back(slot);
1390 for (
auto *predecessor : entry->predecessors)
1391 if (
llvm::any_of(partialSlots, [&](auto slot) {
1392 return !predecessor->valueBefore->neededDefs.contains(slot);
1394 worklist.insert({predecessor, entry});
1399 for (
auto [predecessor, successor] : worklist) {
1400 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting probe block towards " << successor
1401 <<
" after " << *predecessor->terminator <<
"\n");
1402 OpBuilder builder(predecessor->terminator);
1403 auto *newBlock = builder.createBlock(successor->block);
1404 for (
auto oldArg : successor->block->getArguments())
1405 newBlock->addArgument(oldArg.getType(), oldArg.
getLoc());
1406 cf::BranchOp::create(builder, predecessor->terminator->getLoc(),
1407 successor->block, newBlock->getArguments());
1408 for (
auto &blockOp : predecessor->terminator->getBlockOperands())
1409 if (blockOp.
get() == successor->block)
1410 blockOp.set(newBlock);
1413 auto *value = lattice.createValue();
1414 value->neededDefs = successor->valueAfter->neededDefs;
1415 auto *newEntry = lattice.createNode<BlockEntry>(newBlock, value);
1416 auto *newExit = lattice.createNode<BlockExit>(newBlock, value);
1417 newEntry->predecessors.push_back(predecessor);
1418 newExit->successors.push_back(successor);
1419 llvm::replace(successor->predecessors, predecessor, newExit);
1420 llvm::replace(predecessor->successors, successor, newEntry);
1427void Promoter::insertProbes() {
1428 for (
auto *node : lattice.nodes) {
1429 if (
auto *entry = dyn_cast<BlockEntry>(node))
1430 insertProbes(entry);
1436void Promoter::insertProbes(BlockEntry *node) {
1437 auto builder = OpBuilder::atBlockBegin(node->block);
1438 for (
auto neededDef : slots) {
1439 if (!node->valueAfter->neededDefs.contains(neededDef))
1441 if (!node->predecessors.empty() &&
1442 llvm::all_of(node->predecessors, [&](
auto *predecessor) {
1443 return predecessor->valueBefore->neededDefs.contains(neededDef);
1446 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting probe for " << neededDef
1447 <<
" in block " << node->block <<
"\n");
1448 OpBuilder::InsertionGuard guard(builder);
1450 if (Operation *op = neededDef.getDefiningOp()) {
1451 if (op->getBlock() == node->block)
1452 builder.setInsertionPointAfterValue(neededDef);
1454 auto value = ProbeOp::create(builder, neededDef.getLoc(), neededDef);
1455 auto *def = lattice.createDef(value, DriveCondition::never());
1456 node->insertedProbes.push_back({neededDef, def});
1462void Promoter::insertDriveBlocks() {
1466 SmallDenseSet<std::pair<BlockExit *, BlockEntry *>, 1> worklist;
1467 for (
auto *node : lattice.nodes) {
1468 if (
auto *exit = dyn_cast<BlockExit>(node)) {
1469 SmallVector<DefSlot> partialSlots;
1470 for (
auto [slot, reachingDef] : exit->valueBefore->reachingDefs) {
1471 if (reachingDef->condition.isNever())
1473 unsigned numContinues = 0;
1474 for (
auto *successor : exit->successors)
1475 if (successor->valueAfter->reachingDefs.contains(slot))
1477 if (numContinues != 0 && numContinues != exit->successors.size())
1478 partialSlots.push_back(slot);
1480 for (
auto *successor : exit->successors)
1481 if (
llvm::any_of(partialSlots, [&](auto slot) {
1482 return !successor->valueAfter->reachingDefs.contains(slot);
1484 worklist.insert({exit, successor});
1489 for (
auto [predecessor, successor] : worklist) {
1490 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting drive block towards " << successor
1491 <<
" after " << *predecessor->terminator <<
"\n");
1492 OpBuilder builder(predecessor->terminator);
1493 auto *newBlock = builder.createBlock(successor->block);
1494 for (
auto oldArg : successor->block->getArguments())
1495 newBlock->addArgument(oldArg.getType(), oldArg.
getLoc());
1496 cf::BranchOp::create(builder, predecessor->terminator->getLoc(),
1497 successor->block, newBlock->getArguments());
1498 for (
auto &blockOp : predecessor->terminator->getBlockOperands())
1499 if (blockOp.
get() == successor->block)
1500 blockOp.set(newBlock);
1503 auto *value = lattice.createValue();
1504 value->neededDefs = successor->valueAfter->neededDefs;
1505 value->reachingDefs = predecessor->valueBefore->reachingDefs;
1506 auto *newEntry = lattice.createNode<BlockEntry>(newBlock, value);
1507 auto *newExit = lattice.createNode<BlockExit>(newBlock, value);
1508 newEntry->predecessors.push_back(predecessor);
1509 newExit->successors.push_back(successor);
1510 llvm::replace(successor->predecessors, predecessor, newExit);
1511 llvm::replace(predecessor->successors, successor, newEntry);
1518void Promoter::insertDrives() {
1519 for (
auto *node : lattice.nodes) {
1520 if (
auto *exit = dyn_cast<BlockExit>(node))
1522 else if (
auto *drive = dyn_cast<DriveNode>(node))
1523 insertDrives(drive);
1529void Promoter::insertDrives(BlockExit *node) {
1530 auto builder = OpBuilder::atBlockTerminator(node->block);
1532 ConstantTimeOp epsilonTime;
1533 ConstantTimeOp deltaTime;
1534 auto getTime = [&](
bool delta) {
1537 deltaTime = ConstantTimeOp::create(builder, node->terminator->getLoc(),
1542 epsilonTime = ConstantTimeOp::create(builder, node->terminator->getLoc(),
1547 auto insertDriveForSlot = [&](
DefSlot slot) {
1548 auto reachingDef = node->valueBefore->reachingDefs.lookup(slot);
1549 if (!reachingDef || reachingDef->condition.isNever())
1551 if (!node->suspends && !node->successors.empty() &&
1552 llvm::all_of(node->successors, [&](
auto *successor) {
1553 return successor->valueAfter->reachingDefs.contains(slot);
1556 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting drive for " <<
getSlot(slot) <<
" "
1557 << (
isDelayed(slot) ?
"(delayed)" :
"(blocking)")
1558 <<
" before " << *node->terminator <<
"\n");
1560 auto value = reachingDef->getValueOrPlaceholder();
1561 auto enable = reachingDef->condition.isConditional()
1562 ? reachingDef->getConditionOrPlaceholder()
1564 DriveOp::create(builder,
getLoc(slot),
getSlot(slot), value, time, enable);
1567 for (
auto slot : slots)
1569 for (
auto slot : slots)
1575void Promoter::insertDrives(DriveNode *node) {
1576 if (!promotable.contains(
getSlot(node->slot)))
1578 LLVM_DEBUG(llvm::dbgs() <<
"- Removing drive " << *node->op <<
"\n");
1579 pruner.eraseNow(node->op);
1588void Promoter::resolveDefinitions() {
1589 for (
auto *node : lattice.nodes) {
1590 if (
auto *probe = dyn_cast<ProbeNode>(node))
1591 resolveDefinitions(probe);
1592 else if (
auto *drive = dyn_cast<DriveNode>(node))
1593 resolveDefinitions(drive);
1598void Promoter::resolveDefinitions(ProbeNode *node) {
1599 if (!promotable.contains(node->slot))
1601 auto *def = node->valueBefore->reachingDefs.lookup(
blockingSlot(node->slot));
1602 assert(def &&
"no definition reaches probe");
1605 auto projections =
getProjections(node->op->getOperand(0), node->slot);
1608 auto builder = OpBuilder(node->op);
1609 auto value = def->getValueOrPlaceholder();
1613 LLVM_DEBUG(llvm::dbgs() <<
"- Replacing " << *node->op <<
" with " << value
1615 replaceValueWith(node->op->getResult(0), value);
1616 pruner.eraseNow(node->op);
1622void Promoter::resolveDefinitions(DriveNode *node) {
1623 if (!promotable.contains(
getSlot(node->slot)))
1630 if (!node->def->value || node->def->valueIsPlaceholder)
1631 resolveDefinitionValue(node);
1632 if (node->def->condition.isConditional() &&
1633 (!node->def->condition.getCondition() ||
1634 node->def->conditionIsPlaceholder))
1635 resolveDefinitionCondition(node);
1638void Promoter::resolveDefinitionValue(DriveNode *node) {
1641 auto *inDef = node->valueBefore->reachingDefs.lookup(node->slot);
1642 assert(inDef &&
"no definition reaches drive");
1643 auto driveOp = node->getDriveOp();
1644 LLVM_DEBUG(llvm::dbgs() <<
"- Injecting value for " << driveOp <<
"\n");
1650 auto builder = OpBuilder(driveOp);
1651 auto value = inDef->getValueOrPlaceholder();
1655 pruner.eraseLaterIfUnused(value);
1656 if (!driveOp.getEnable())
1657 value = driveOp.getValue();
1660 driveOp.getLoc(), driveOp.getEnable(), driveOp.getValue(), value);
1666 if (node->def->valueIsPlaceholder) {
1667 auto *placeholder = node->def->value.getDefiningOp();
1668 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1669 "placeholder replaced but valueIsPlaceholder still set");
1670 replaceValueWith(placeholder->getResult(0), value);
1671 pruner.eraseNow(placeholder);
1672 node->def->valueIsPlaceholder =
false;
1674 node->def->value = value;
1677void Promoter::resolveDefinitionCondition(DriveNode *node) {
1680 auto *inDef = node->valueBefore->reachingDefs.lookup(node->slot);
1681 assert(inDef &&
"no definition reaches drive");
1682 auto driveOp = node->getDriveOp();
1683 LLVM_DEBUG(llvm::dbgs() <<
"- Mutating condition for " << driveOp <<
"\n");
1684 OpBuilder builder(driveOp);
1689 if (inDef->condition.isNever())
1690 condition = driveOp.getEnable();
1693 builder.createOrFold<
comb::OrOp>(driveOp.getLoc(), driveOp.getEnable(),
1694 inDef->getConditionOrPlaceholder());
1697 if (node->def->conditionIsPlaceholder) {
1698 auto *placeholder = node->def->condition.getCondition().getDefiningOp();
1699 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1700 "placeholder replaced but conditionIsPlaceholder still set");
1701 replaceValueWith(placeholder->getResult(0), condition);
1702 pruner.eraseNow(placeholder);
1703 node->def->conditionIsPlaceholder =
false;
1705 node->def->condition.setCondition(condition);
1713void Promoter::insertBlockArgs() {
1714 bool anyArgsInserted =
true;
1715 while (anyArgsInserted) {
1716 anyArgsInserted =
false;
1717 for (
auto *node : lattice.nodes)
1718 if (auto *entry = dyn_cast<BlockEntry>(node))
1719 anyArgsInserted |= insertBlockArgs(entry);
1731bool Promoter::insertBlockArgs(BlockEntry *node) {
1737 enum class Which { Value, Condition };
1738 SmallVector<std::pair<DefSlot, Which>> neededSlots;
1739 auto addNeededSlot = [&](
DefSlot slot) {
1740 if (
auto *def = node->mergedDefs.lookup(slot)) {
1741 if (node->valueAfter->reachingDefs.contains(slot)) {
1742 if (def->valueIsPlaceholder)
1743 neededSlots.push_back({slot, Which::Value});
1744 if (def->conditionIsPlaceholder)
1745 neededSlots.push_back({slot, Which::Condition});
1749 for (
auto slot : slots) {
1753 if (neededSlots.empty())
1755 LLVM_DEBUG(llvm::dbgs() <<
"- Adding " << neededSlots.size()
1756 <<
" args to block " << node->block <<
"\n");
1759 for (
auto [slot, which] : neededSlots) {
1760 auto *def = node->mergedDefs.lookup(slot);
1763 case Which::Value: {
1766 auto *placeholder = def->value.getDefiningOp();
1767 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1768 "placeholder replaced but valueIsPlaceholder still set");
1770 replaceValueWith(placeholder->getResult(0), arg);
1771 pruner.eraseNow(placeholder);
1773 def->valueIsPlaceholder =
false;
1776 case Which::Condition: {
1780 auto *placeholder = def->condition.getCondition().getDefiningOp();
1781 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1782 "placeholder replaced but conditionIsPlaceholder still set");
1783 auto conditionArg = node->block->addArgument(
1784 IntegerType::get(region.getContext(), 1),
getLoc(slot));
1785 replaceValueWith(placeholder->getResult(0), conditionArg);
1786 pruner.eraseNow(placeholder);
1787 def->condition.setCondition(conditionArg);
1788 def->conditionIsPlaceholder =
false;
1795 for (
auto *predecessor : node->predecessors) {
1797 SmallVector<Value> args;
1798 for (
auto [slot, which] : neededSlots) {
1799 auto *def = predecessor->valueBefore->reachingDefs.lookup(slot);
1800 auto builder = OpBuilder::atBlockTerminator(predecessor->block);
1804 args.push_back(def->getValueOrPlaceholder());
1807 auto flatType = builder.getIntegerType(hw::getBitWidth(type));
1810 if (type != flatType)
1812 args.push_back(value);
1815 case Which::Condition:
1817 args.push_back(def->getConditionOrPlaceholder());
1820 builder.getI1Type(), 0));
1827 auto branchOp = cast<BranchOpInterface>(predecessor->terminator);
1828 for (
auto &blockOperand : branchOp->getBlockOperands())
1829 if (blockOperand.
get() == node->block)
1830 branchOp.getSuccessorOperands(blockOperand.getOperandNumber())
1839void Promoter::replaceValueWith(Value oldValue, Value newValue) {
1840 oldValue.replaceAllUsesWith(newValue);
1841 for (
auto *def : lattice.defs) {
1842 if (def->value == oldValue)
1843 def->value = newValue;
1844 if (def->condition.isConditional() &&
1845 def->condition.getCondition() == oldValue)
1846 def->condition.setCondition(newValue);
1855void Promoter::removeUnusedLocalSignals() {
1856 for (
auto *node : lattice.nodes)
1857 if (auto *signal = dyn_cast<SignalNode>(node))
1858 removeUnusedLocalSignal(signal);
1864void Promoter::removeUnusedLocalSignal(SignalNode *signal) {
1868 SmallVector<Operation *> worklist;
1869 worklist.push_back(signal->op);
1870 while (!worklist.empty()) {
1871 auto *op = worklist.pop_back_val();
1872 if (!isa<SignalOp, DriveOp, SigArrayGetOp, SigArraySliceOp, SigExtractOp,
1873 SigStructExtractOp>(op))
1875 for (
auto *user : op->getUsers())
1876 if (users.insert(user))
1877 worklist.push_back(user);
1882 LLVM_DEBUG(llvm::dbgs() <<
"- Removing local signal " << *signal->op <<
"\n");
1883 for (
auto *op :
llvm::reverse(users))
1884 pruner.eraseNow(op);
1892struct Mem2RegPass :
public llhd::impl::Mem2RegPassBase<Mem2RegPass> {
1893 void runOnOperation()
override;
1897void Mem2RegPass::runOnOperation() {
1898 SmallVector<Region *> regions;
1899 getOperation()->walk<WalkOrder::PreOrder>([&](Operation *op) {
1900 if (isa<ProcessOp, FinalOp, CombinationalOp>(op)) {
1901 auto ®ion = op->getRegion(0);
1902 if (!region.empty())
1903 regions.push_back(®ion);
1904 return WalkResult::skip();
1906 return WalkResult::advance();
1908 for (
auto *region : regions)
1909 if (failed(Promoter(*region).promote()))
1910 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)