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;
34using llvm::SmallSetVector;
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 };
236 LatticeNode(Kind kind) : kind(kind) {}
239struct BlockEntry :
public LatticeNode {
241 LatticeValue *valueAfter;
242 SmallVector<BlockExit *, 2> predecessors;
243 SmallVector<std::pair<Value, Def *>, 0> insertedProbes;
246 BlockEntry(Block *block, LatticeValue *valueAfter)
247 : LatticeNode(Kind::BlockEntry), block(block), valueAfter(valueAfter) {
248 assert(!valueAfter->nodeBefore);
249 valueAfter->nodeBefore =
this;
252 static bool classof(
const LatticeNode *n) {
253 return n->kind == Kind::BlockEntry;
257struct BlockExit :
public LatticeNode {
259 LatticeValue *valueBefore;
260 SmallVector<BlockEntry *, 2> successors;
261 Operation *terminator;
264 BlockExit(Block *block, LatticeValue *valueBefore)
265 : LatticeNode(Kind::BlockExit), block(block), valueBefore(valueBefore),
266 terminator(block->getTerminator()),
267 suspends(isa<HaltOp, WaitOp>(terminator)) {
268 assert(!valueBefore->nodeAfter);
269 valueBefore->nodeAfter =
this;
272 static bool classof(
const LatticeNode *n) {
273 return n->kind == Kind::BlockExit;
277struct OpNode :
public LatticeNode {
279 LatticeValue *valueBefore;
280 LatticeValue *valueAfter;
282 OpNode(Kind kind, Operation *op, LatticeValue *valueBefore,
283 LatticeValue *valueAfter)
284 : LatticeNode(kind), op(op), valueBefore(valueBefore),
285 valueAfter(valueAfter) {
286 assert(!valueBefore->nodeAfter);
287 assert(!valueAfter->nodeBefore);
288 valueBefore->nodeAfter =
this;
289 valueAfter->nodeBefore =
this;
292 static bool classof(
const LatticeNode *n) {
293 return isa<ProbeNode, DriveNode, SignalNode>(n);
297struct ProbeNode :
public OpNode {
300 ProbeNode(ProbeOp op, Value slot, LatticeValue *valueBefore,
301 LatticeValue *valueAfter)
302 : OpNode(Kind::Probe, op, valueBefore, valueAfter), slot(slot) {}
304 static bool classof(
const LatticeNode *n) {
return n->kind == Kind::Probe; }
307struct DriveNode :
public OpNode {
311 DriveNode(DriveOp op, Value slot, Def *def, LatticeValue *valueBefore,
312 LatticeValue *valueAfter)
313 : OpNode(Kind::Drive, op, valueBefore, valueAfter),
321 bool drivesProjection()
const {
return op->getOperand(0) !=
getSlot(slot); }
324 DriveOp getDriveOp()
const {
return cast<DriveOp>(op); }
326 static bool classof(
const LatticeNode *n) {
return n->kind == Kind::Drive; }
329struct SignalNode :
public OpNode {
332 SignalNode(SignalOp op, Def *def, LatticeValue *valueBefore,
333 LatticeValue *valueAfter)
334 : OpNode(Kind::Signal, op, valueBefore, valueAfter), def(def) {}
336 SignalOp getSignalOp()
const {
return cast<SignalOp>(op); }
339 static bool classof(
const LatticeNode *n) {
return n->kind == Kind::Signal; }
346 LatticeValue *createValue() {
347 auto *value =
new (valueAllocator.Allocate()) LatticeValue();
348 values.push_back(value);
353 template <
class T,
typename... Args>
354 T *createNode(Args... args) {
356 new (getAllocator<T>().Allocate()) T(std::forward<Args>(args)...);
357 nodes.push_back(node);
362 template <
typename... Args>
363 Def *createDef(Args... args) {
364 auto *def =
new (defAllocator.Allocate()) Def(std::forward<Args>(args)...);
370 Def *createDef(Value value, DriveCondition mode) {
371 auto &slot = defsForValues[{value, mode}];
373 slot =
new (defAllocator.Allocate()) Def(value, mode);
374 defs.push_back(slot);
380 void dump(llvm::raw_ostream &os = llvm::dbgs());
384 std::vector<LatticeNode *> nodes;
386 std::vector<LatticeValue *> values;
388 std::vector<Def *> defs;
392 DenseMap<std::pair<Value, DriveCondition>, Def *> defsForValues;
395 SpecificBumpPtrAllocator<LatticeValue> valueAllocator;
396 SpecificBumpPtrAllocator<Def> defAllocator;
397 SpecificBumpPtrAllocator<BlockEntry> blockEntryAllocator;
398 SpecificBumpPtrAllocator<BlockExit> blockExitAllocator;
399 SpecificBumpPtrAllocator<ProbeNode> probeAllocator;
400 SpecificBumpPtrAllocator<DriveNode> driveAllocator;
401 SpecificBumpPtrAllocator<SignalNode> signalAllocator;
405 SpecificBumpPtrAllocator<T> &getAllocator();
411SpecificBumpPtrAllocator<BlockEntry> &Lattice::getAllocator() {
412 return blockEntryAllocator;
415SpecificBumpPtrAllocator<BlockExit> &Lattice::getAllocator() {
416 return blockExitAllocator;
419SpecificBumpPtrAllocator<ProbeNode> &Lattice::getAllocator() {
420 return probeAllocator;
423SpecificBumpPtrAllocator<DriveNode> &Lattice::getAllocator() {
424 return driveAllocator;
427SpecificBumpPtrAllocator<SignalNode> &Lattice::getAllocator() {
428 return signalAllocator;
435void Lattice::dump(llvm::raw_ostream &os) {
437 llvm::MapVector<Block *, unsigned> blockNames;
438 llvm::MapVector<Value, unsigned> memNames;
439 llvm::MapVector<Def *, unsigned> defNames;
441 auto blockName = [&](
Block *block) {
442 unsigned id = blockNames.insert({block, blockNames.size()}).first->second;
443 return std::string(
"bb") + llvm::utostr(
id);
446 auto memName = [&](
DefSlot value) {
448 memNames.insert({
getSlot(value), memNames.size()}).first->second;
449 return std::string(
"mem") + llvm::utostr(
id) +
453 auto defName = [&](Def *def) {
454 unsigned id = defNames.insert({def, defNames.size()}).first->second;
455 return std::string(
"def") + llvm::utostr(
id);
459 for (
auto *node : nodes)
460 if (auto *entry = dyn_cast<BlockEntry>(node))
461 blockName(entry->block);
465 for (
auto *node : nodes) {
466 auto *entry = dyn_cast<BlockEntry>(node);
471 os <<
" " << blockName(entry->block) <<
":";
472 if (entry->predecessors.empty()) {
473 os <<
" // no predecessors";
476 for (
auto *node : entry->predecessors)
477 os <<
" " << blockName(node->block);
482 auto *value = entry->valueAfter;
485 if (!value->neededDefs.empty()) {
487 for (
auto mem : value->neededDefs)
491 if (!value->reachingDefs.empty()) {
493 for (
auto [mem, def] : value->reachingDefs) {
494 os <<
" " << memName(mem) <<
"=" << defName(def);
495 if (def->condition.isNever())
497 else if (def->condition.isAlways())
504 if (isa<BlockExit>(value->nodeAfter))
508 if (
auto *node = dyn_cast<ProbeNode>(value->nodeAfter))
509 os <<
" probe " << memName(
blockingSlot(node->slot)) <<
"\n";
510 else if (
auto *node = dyn_cast<DriveNode>(value->nodeAfter))
511 os <<
" drive " << memName(node->slot) <<
"\n";
512 else if (
auto *node = dyn_cast<SignalNode>(value->nodeAfter))
513 os <<
" signal " << memName(node->getSlot()) <<
"\n";
518 value = cast<OpNode>(value->nodeAfter)->valueAfter;
522 auto *exit = cast<BlockExit>(value->nodeAfter);
523 if (isa<WaitOp>(exit->terminator))
525 else if (exit->successors.empty())
529 for (
auto *node : exit->successors)
530 os <<
" " << blockName(node->block);
532 os <<
" // suspends";
537 for (
auto [mem,
id] : memNames)
538 os <<
" mem" << id <<
": " << mem <<
"\n";
572 while (fromSignal != toSlot) {
573 auto *op = cast<OpResult>(fromSignal).getOwner();
574 stack.push_back({op, Value()});
575 fromSignal = op->getOperand(0);
588 for (
auto &projection : llvm::reverse(projections)) {
589 projection.into = value;
590 value = TypeSwitch<Operation *, Value>(projection.op)
591 .Case<SigArrayGetOp>([&](
auto op) {
593 op.getLoc(), value, op.getIndex());
595 .Case<SigStructExtractOp>([&](
auto op) {
597 op.getLoc(), value, op.getFieldAttr());
599 .Case<SigExtractOp>([&](
auto op) {
600 auto type = cast<RefType>(op.getType()).getNestedType();
601 auto width = type.getIntOrFloatBitWidth();
602 return comb::createDynamicExtract(builder, op.getLoc(), value,
603 op.getLowBit(), width);
623 for (
auto projection : projections) {
624 value = TypeSwitch<Operation *, Value>(projection.op)
625 .Case<SigArrayGetOp>([&](
auto op) {
626 return builder.createOrFold<hw::ArrayInjectOp>(
627 op.getLoc(), projection.into, op.getIndex(), value);
629 .Case<SigStructExtractOp>([&](
auto op) {
630 return builder.createOrFold<hw::StructInjectOp>(
631 op.getLoc(), projection.into, op.getFieldAttr(), value);
633 .Case<SigExtractOp>([&](
auto op) {
634 return comb::createDynamicInject(builder, op.getLoc(),
636 op.getLowBit(), value);
649 Promoter(Region ®ion) : region(region) {}
650 LogicalResult promote();
652 void findPromotableSlots();
653 Value resolveSlot(Value projectionOrSlot);
655 void captureAcrossWait();
656 void captureAcrossWait(Value value, ArrayRef<WaitOp> waitOps,
657 Liveness &liveness, DominanceInfo &dominance);
659 void constructLattice();
660 void propagateBackward();
661 void propagateBackward(LatticeNode *node);
662 void propagateForward();
663 void propagateForward(
bool optimisticMerges, DominanceInfo &dominance);
664 void propagateForward(LatticeNode *node,
bool optimisticMerges,
665 DominanceInfo &dominance);
666 void markDirty(LatticeNode *node);
668 void insertProbeBlocks();
670 void insertProbes(BlockEntry *node);
672 void insertDriveBlocks();
674 void insertDrives(BlockExit *node);
675 void insertDrives(DriveNode *node);
677 void resolveDefinitions();
678 void resolveDefinitions(ProbeNode *node);
679 void resolveDefinitions(DriveNode *node);
680 void resolveDefinitionValue(DriveNode *node);
681 void resolveDefinitionCondition(DriveNode *node);
683 void insertBlockArgs();
684 bool insertBlockArgs(BlockEntry *node);
685 void replaceValueWith(Value oldValue, Value newValue);
687 void removeUnusedLocalSignals();
688 void removeUnusedLocalSignal(SignalNode *signal);
696 SmallVector<Value> slots;
701 SmallDenseSet<Value> promotable;
707 SmallPtrSet<LatticeNode *, 4> dirtyNodes;
714LogicalResult Promoter::promote() {
718 findPromotableSlots();
730 llvm::dbgs() <<
"Initial lattice:\n";
737 llvm::dbgs() <<
"After backward propagation:\n";
745 llvm::dbgs() <<
"After probe insertion:\n";
752 llvm::dbgs() <<
"After forward propagation:\n";
757 resolveDefinitions();
763 llvm::dbgs() <<
"After def resolution and drive insertion:\n";
771 removeUnusedLocalSignals();
780void Promoter::findPromotableSlots() {
781 SmallPtrSet<Value, 8> seenSlots;
782 SmallPtrSet<Operation *, 8> checkedUsers;
783 SmallVector<Operation *, 8> userWorklist;
785 region.walk([&](Operation *op) {
786 for (
auto operand : op->getOperands()) {
787 if (!seenSlots.insert(operand).second)
793 if (!operand.getDefiningOp<llhd::SignalOp>())
797 auto checkUser = [&](Operation *user) ->
bool {
799 if (region.isProperAncestor(user->getParentRegion()))
802 if (user->getParentRegion() != ®ion)
805 if (isa<SigArrayGetOp, SigExtractOp, SigStructExtractOp>(user)) {
806 for (
auto *projectionUser : user->getUsers())
807 if (checkedUsers.insert(projectionUser).second)
808 userWorklist.push_back(projectionUser);
809 projections.insert({user->getResult(0), operand});
815 checkedUsers.clear();
816 if (!llvm::all_of(operand.getUsers(), [&](
auto *user) {
818 if (checkedUsers.insert(user).second)
819 userWorklist.push_back(user);
820 while (!userWorklist.empty() && allOk)
821 allOk &= checkUser(userWorklist.pop_back_val());
822 userWorklist.clear();
827 slots.push_back(operand);
832 promotable.insert(slots.begin(), slots.end());
833 for (
auto [projection, slot] :
llvm::make_early_inc_range(projections))
834 if (!promotable.contains(slot))
835 projections.erase(projection);
836 for (
auto [projection, slot] : projections)
837 promotable.insert(projection);
839 LLVM_DEBUG(llvm::dbgs() <<
"Found " << slots.size() <<
" promotable slots, "
840 << promotable.size() <<
" promotable values\n");
845Value Promoter::resolveSlot(Value projectionOrSlot) {
846 if (
auto slot = projections.lookup(projectionOrSlot))
848 return projectionOrSlot;
855void Promoter::captureAcrossWait() {
856 if (region.hasOneBlock())
859 SmallVector<WaitOp> waitOps;
860 for (
auto &block : region)
861 if (auto waitOp = dyn_cast<WaitOp>(block.getTerminator()))
862 waitOps.push_back(waitOp);
864 DominanceInfo dominance(region.getParentOp());
865 Liveness liveness(region.getParentOp());
867 llvm::DenseSet<Value> alreadyCaptured;
869 auto isDefinedInRegion = [&](Value v) {
870 return v.getParentRegion() == ®ion;
873 for (
auto waitOp : waitOps) {
874 Block *waitBlock = waitOp->getBlock();
875 const auto &liveOutValues = liveness.getLiveOut(waitBlock);
877 for (Value v : liveOutValues) {
878 if (!isDefinedInRegion(v))
881 if (!alreadyCaptured.insert(v).second)
884 captureAcrossWait(v, waitOps, liveness, dominance);
893void Promoter::captureAcrossWait(Value value, ArrayRef<WaitOp> waitOps,
894 Liveness &liveness, DominanceInfo &dominance) {
896 llvm::dbgs() <<
"Capture " << value <<
"\n";
897 for (
auto waitOp : waitOps)
898 llvm::dbgs() <<
"- Across " << waitOp <<
"\n";
903 auto &domTree = dominance.getDomTree(®ion);
904 llvm::IDFCalculatorBase<Block, false> idfCalculator(domTree);
908 SmallPtrSet<Block *, 4> definingBlocks;
909 definingBlocks.insert(value.getParentBlock());
910 for (
auto waitOp : waitOps)
911 definingBlocks.insert(waitOp.getDest());
912 idfCalculator.setDefiningBlocks(definingBlocks);
915 SmallPtrSet<Block *, 16> liveInBlocks;
916 for (
auto &block : region)
917 if (liveness.getLiveness(&block)->isLiveIn(value))
918 liveInBlocks.insert(&block);
919 idfCalculator.setLiveInBlocks(liveInBlocks);
923 SmallVector<Block *> mergePointsVec;
924 idfCalculator.calculate(mergePointsVec);
925 SmallPtrSet<Block *, 16> mergePoints(mergePointsVec.begin(),
926 mergePointsVec.end());
927 for (
auto waitOp : waitOps)
928 mergePoints.insert(waitOp.getDest());
929 LLVM_DEBUG(llvm::dbgs() <<
"- " << mergePoints.size() <<
" merge points\n");
934 struct WorklistItem {
935 DominanceInfoNode *domNode;
938 SmallVector<WorklistItem> worklist;
939 worklist.push_back({domTree.getNode(value.getParentBlock()), value});
941 while (!worklist.empty()) {
942 auto item = worklist.pop_back_val();
943 auto *block = item.domNode->getBlock();
946 if (mergePoints.contains(block))
947 item.reachingDef = block->addArgument(value.getType(), value.getLoc());
951 for (
auto &op : *block)
952 op.replaceUsesOfWith(value, item.reachingDef);
956 if (
auto branchOp = dyn_cast<BranchOpInterface>(block->getTerminator())) {
957 for (
auto &blockOperand : branchOp->getBlockOperands())
958 if (mergePoints.contains(blockOperand.
get()))
959 branchOp.getSuccessorOperands(blockOperand.getOperandNumber())
960 .
append(item.reachingDef);
961 }
else if (
auto waitOp = dyn_cast<WaitOp>(block->getTerminator())) {
962 if (mergePoints.contains(waitOp.getDest()))
963 waitOp.getDestOperandsMutable().append(item.reachingDef);
966 for (
auto *child : item.domNode->children())
967 worklist.push_back({child, item.reachingDef});
977void Promoter::constructLattice() {
980 for (
auto &block : region) {
981 auto *entry = lattice.createNode<BlockEntry>(&block, lattice.createValue());
982 blockEntries.insert({&block, entry});
986 for (
auto &block : region) {
987 auto *valueBefore = blockEntries.lookup(&block)->valueAfter;
990 for (
auto &op : block.without_terminator()) {
992 if (
auto probeOp = dyn_cast<ProbeOp>(op)) {
993 if (!promotable.contains(probeOp.getSignal()))
995 auto *node = lattice.createNode<ProbeNode>(
996 probeOp, resolveSlot(probeOp.getSignal()), valueBefore,
997 lattice.createValue());
998 valueBefore = node->valueAfter;
1003 if (
auto driveOp = dyn_cast<DriveOp>(op)) {
1006 if (!promotable.contains(driveOp.getSignal()))
1008 auto condition = DriveCondition::always();
1009 if (
auto enable = driveOp.getEnable())
1010 condition = DriveCondition::conditional(enable);
1014 auto slot = resolveSlot(driveOp.getSignal());
1015 auto *def = driveOp.getSignal() == slot
1016 ? lattice.createDef(driveOp.getValue(), condition)
1017 : lattice.createDef(driveOp->getBlock(),
1019 auto *node = lattice.createNode<DriveNode>(
1020 driveOp, slot, def, valueBefore, lattice.createValue());
1021 valueBefore = node->valueAfter;
1026 if (
auto signalOp = dyn_cast<SignalOp>(op)) {
1027 if (!promotable.contains(signalOp))
1030 lattice.createDef(signalOp.getInit(), DriveCondition::never());
1031 auto *node = lattice.createNode<SignalNode>(signalOp, def, valueBefore,
1032 lattice.createValue());
1033 valueBefore = node->valueAfter;
1039 auto *exit = lattice.createNode<BlockExit>(&block, valueBefore);
1040 for (
auto *otherBlock : exit->terminator->getSuccessors()) {
1041 auto *otherEntry = blockEntries.lookup(otherBlock);
1042 exit->successors.push_back(otherEntry);
1043 otherEntry->predecessors.push_back(exit);
1050void Promoter::propagateBackward() {
1051 for (
auto *node : lattice.nodes)
1052 propagateBackward(node);
1053 while (!dirtyNodes.empty()) {
1054 auto *node = *dirtyNodes.begin();
1055 dirtyNodes.erase(node);
1056 propagateBackward(node);
1062void Promoter::propagateBackward(LatticeNode *node) {
1063 auto update = [&](LatticeValue *value,
auto &neededDefs) {
1064 if (value->neededDefs != neededDefs) {
1065 value->neededDefs = neededDefs;
1066 markDirty(value->nodeBefore);
1071 if (
auto *probe = dyn_cast<ProbeNode>(node)) {
1072 auto needed = probe->valueAfter->neededDefs;
1073 needed.insert(probe->slot);
1074 update(probe->valueBefore, needed);
1084 if (
auto *drive = dyn_cast<DriveNode>(node)) {
1085 auto needed = drive->valueAfter->neededDefs;
1086 if (drive->drivesProjection())
1087 needed.insert(
getSlot(drive->slot));
1088 else if (!
isDelayed(drive->slot) && !drive->getDriveOp().getEnable())
1089 needed.erase(
getSlot(drive->slot));
1090 update(drive->valueBefore, needed);
1097 if (
auto *signal = dyn_cast<SignalNode>(node)) {
1098 auto needed = signal->valueAfter->neededDefs;
1099 needed.erase(signal->getSignalOp());
1100 update(signal->valueBefore, needed);
1105 if (
auto *entry = dyn_cast<BlockEntry>(node)) {
1106 for (
auto *predecessor : entry->predecessors)
1107 markDirty(predecessor);
1112 if (
auto *exit = dyn_cast<BlockExit>(node)) {
1115 SmallDenseSet<Value, 1> needed;
1116 for (
auto *successors : exit->successors)
1117 needed.insert(successors->valueAfter->neededDefs.begin(),
1118 successors->valueAfter->neededDefs.
end());
1119 update(exit->valueBefore, needed);
1123 assert(
false &&
"unhandled node in backward propagation");
1126void Promoter::propagateForward() {
1127 DominanceInfo dominance(region.getParentOp());
1128 propagateForward(
true, dominance);
1129 propagateForward(
false, dominance);
1140void Promoter::propagateForward(
bool optimisticMerges,
1141 DominanceInfo &dominance) {
1142 for (
auto *node : lattice.nodes)
1143 propagateForward(node, optimisticMerges, dominance);
1144 while (!dirtyNodes.empty()) {
1145 auto *node = *dirtyNodes.begin();
1146 dirtyNodes.erase(node);
1147 propagateForward(node, optimisticMerges, dominance);
1152void Promoter::propagateForward(LatticeNode *node,
bool optimisticMerges,
1153 DominanceInfo &dominance) {
1154 auto update = [&](LatticeValue *value,
auto &reachingDefs) {
1155 if (value->reachingDefs != reachingDefs) {
1156 value->reachingDefs = reachingDefs;
1157 markDirty(value->nodeAfter);
1162 if (
auto *probe = dyn_cast<ProbeNode>(node)) {
1163 update(probe->valueAfter, probe->valueBefore->reachingDefs);
1177 if (
auto *drive = dyn_cast<DriveNode>(node)) {
1178 auto reaching = drive->valueBefore->reachingDefs;
1184 if (drive->drivesProjection() || drive->getDriveOp().getEnable()) {
1185 auto *inDef = reaching.lookup(drive->slot);
1187 if (drive->def->value != inDef->value)
1188 drive->def->value = {};
1189 if (inDef->condition.isAlways() || drive->def->condition.isAlways())
1190 drive->def->condition = DriveCondition::always();
1191 else if (drive->def->condition != inDef->condition)
1192 drive->def->condition = DriveCondition::conditional();
1196 reaching[drive->slot] = drive->def;
1199 update(drive->valueAfter, reaching);
1206 if (
auto *signal = dyn_cast<SignalNode>(node)) {
1207 auto reaching = signal->valueBefore->reachingDefs;
1208 reaching[signal->getSlot()] = signal->def;
1209 reaching.erase(
delayedSlot(signal->getSignalOp()));
1210 update(signal->valueAfter, reaching);
1216 if (
auto *entry = dyn_cast<BlockEntry>(node)) {
1219 for (
auto [slot, insertedProbe] : entry->insertedProbes) {
1227 for (
auto *predecessor : entry->predecessors) {
1228 if (predecessor->suspends)
1232 for (
auto &defEntry : predecessor->valueBefore->reachingDefs) {
1233 auto slot =
getSlot(defEntry.getFirst());
1234 auto *slotBlock = slot.getDefiningOp()->getBlock();
1235 if (!dominance.dominates(slotBlock, entry->block))
1237 reachingDefs.insert(defEntry);
1241 for (
auto pair : reachingDefs) {
1243 Def *reachingDef = pair.second;
1244 DriveCondition reachingDefCondition = reachingDef->condition;
1247 if (reaching.contains(slot))
1257 if (llvm::any_of(entry->predecessors, [&](
auto *predecessor) {
1258 return predecessor->suspends;
1262 for (
auto *predecessor : entry->predecessors) {
1263 auto otherDef = predecessor->valueBefore->reachingDefs.lookup(slot);
1264 if (!otherDef && optimisticMerges)
1268 if (reachingDef != otherDef)
1269 reachingDef =
nullptr;
1273 otherDef ? otherDef->condition : DriveCondition::never();
1274 if (reachingDefCondition != condition)
1275 reachingDefCondition = DriveCondition::conditional();
1281 reachingDef = entry->mergedDefs.lookup(slot);
1283 reachingDef = lattice.createDef(entry->block,
getStoredType(slot),
1284 reachingDefCondition);
1285 entry->mergedDefs.insert({slot, reachingDef});
1287 reachingDef->condition = reachingDefCondition;
1289 reaching.insert({slot, reachingDef});
1292 update(entry->valueAfter, reaching);
1297 if (
auto *exit = dyn_cast<BlockExit>(node)) {
1298 for (
auto *successor : exit->successors)
1299 markDirty(successor);
1303 assert(
false &&
"unhandled node in forward propagation");
1307void Promoter::markDirty(LatticeNode *node) {
1309 dirtyNodes.insert(node);
1320void Promoter::insertProbeBlocks() {
1324 SmallDenseSet<std::pair<BlockExit *, BlockEntry *>, 1> worklist;
1325 for (
auto *node : lattice.nodes) {
1326 if (
auto *entry = dyn_cast<BlockEntry>(node)) {
1327 SmallVector<Value> partialSlots;
1328 for (
auto slot : entry->valueAfter->neededDefs) {
1329 unsigned numIncoming = 0;
1330 for (
auto *predecessor : entry->predecessors)
1331 if (predecessor->valueBefore->neededDefs.contains(slot))
1333 if (numIncoming != 0 && numIncoming != entry->predecessors.size())
1334 partialSlots.push_back(slot);
1336 for (
auto *predecessor : entry->predecessors)
1337 if (
llvm::any_of(partialSlots, [&](auto slot) {
1338 return !predecessor->valueBefore->neededDefs.contains(slot);
1340 worklist.insert({predecessor, entry});
1345 for (
auto [predecessor, successor] : worklist) {
1346 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting probe block towards " << successor
1347 <<
" after " << *predecessor->terminator <<
"\n");
1348 OpBuilder builder(predecessor->terminator);
1349 auto *newBlock = builder.createBlock(successor->block);
1350 for (
auto oldArg : successor->block->getArguments())
1351 newBlock->addArgument(oldArg.getType(), oldArg.
getLoc());
1352 cf::BranchOp::create(builder, predecessor->terminator->getLoc(),
1353 successor->block, newBlock->getArguments());
1354 for (
auto &blockOp : predecessor->terminator->getBlockOperands())
1355 if (blockOp.
get() == successor->block)
1356 blockOp.set(newBlock);
1359 auto *value = lattice.createValue();
1360 value->neededDefs = successor->valueAfter->neededDefs;
1361 auto *newEntry = lattice.createNode<BlockEntry>(newBlock, value);
1362 auto *newExit = lattice.createNode<BlockExit>(newBlock, value);
1363 newEntry->predecessors.push_back(predecessor);
1364 newExit->successors.push_back(successor);
1365 llvm::replace(successor->predecessors, predecessor, newExit);
1366 llvm::replace(predecessor->successors, successor, newEntry);
1373void Promoter::insertProbes() {
1374 for (
auto *node : lattice.nodes) {
1375 if (
auto *entry = dyn_cast<BlockEntry>(node))
1376 insertProbes(entry);
1382void Promoter::insertProbes(BlockEntry *node) {
1383 auto builder = OpBuilder::atBlockBegin(node->block);
1384 for (
auto neededDef : slots) {
1385 if (!node->valueAfter->neededDefs.contains(neededDef))
1387 if (!node->predecessors.empty() &&
1388 llvm::all_of(node->predecessors, [&](
auto *predecessor) {
1389 return predecessor->valueBefore->neededDefs.contains(neededDef);
1392 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting probe for " << neededDef
1393 <<
" in block " << node->block <<
"\n");
1394 OpBuilder::InsertionGuard guard(builder);
1396 if (Operation *op = neededDef.getDefiningOp()) {
1397 if (op->getBlock() == node->block)
1398 builder.setInsertionPointAfterValue(neededDef);
1400 auto value = ProbeOp::create(builder, neededDef.getLoc(), neededDef);
1401 auto *def = lattice.createDef(value, DriveCondition::never());
1402 node->insertedProbes.push_back({neededDef, def});
1408void Promoter::insertDriveBlocks() {
1412 SmallDenseSet<std::pair<BlockExit *, BlockEntry *>, 1> worklist;
1413 for (
auto *node : lattice.nodes) {
1414 if (
auto *exit = dyn_cast<BlockExit>(node)) {
1415 SmallVector<DefSlot> partialSlots;
1416 for (
auto [slot, reachingDef] : exit->valueBefore->reachingDefs) {
1417 if (reachingDef->condition.isNever())
1419 unsigned numContinues = 0;
1420 for (
auto *successor : exit->successors)
1421 if (successor->valueAfter->reachingDefs.contains(slot))
1423 if (numContinues != 0 && numContinues != exit->successors.size())
1424 partialSlots.push_back(slot);
1426 for (
auto *successor : exit->successors)
1427 if (
llvm::any_of(partialSlots, [&](auto slot) {
1428 return !successor->valueAfter->reachingDefs.contains(slot);
1430 worklist.insert({exit, successor});
1435 for (
auto [predecessor, successor] : worklist) {
1436 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting drive block towards " << successor
1437 <<
" after " << *predecessor->terminator <<
"\n");
1438 OpBuilder builder(predecessor->terminator);
1439 auto *newBlock = builder.createBlock(successor->block);
1440 for (
auto oldArg : successor->block->getArguments())
1441 newBlock->addArgument(oldArg.getType(), oldArg.
getLoc());
1442 cf::BranchOp::create(builder, predecessor->terminator->getLoc(),
1443 successor->block, newBlock->getArguments());
1444 for (
auto &blockOp : predecessor->terminator->getBlockOperands())
1445 if (blockOp.
get() == successor->block)
1446 blockOp.set(newBlock);
1449 auto *value = lattice.createValue();
1450 value->neededDefs = successor->valueAfter->neededDefs;
1451 value->reachingDefs = predecessor->valueBefore->reachingDefs;
1452 auto *newEntry = lattice.createNode<BlockEntry>(newBlock, value);
1453 auto *newExit = lattice.createNode<BlockExit>(newBlock, value);
1454 newEntry->predecessors.push_back(predecessor);
1455 newExit->successors.push_back(successor);
1456 llvm::replace(successor->predecessors, predecessor, newExit);
1457 llvm::replace(predecessor->successors, successor, newEntry);
1464void Promoter::insertDrives() {
1465 for (
auto *node : lattice.nodes) {
1466 if (
auto *exit = dyn_cast<BlockExit>(node))
1468 else if (
auto *drive = dyn_cast<DriveNode>(node))
1469 insertDrives(drive);
1475void Promoter::insertDrives(BlockExit *node) {
1476 auto builder = OpBuilder::atBlockTerminator(node->block);
1478 ConstantTimeOp epsilonTime;
1479 ConstantTimeOp deltaTime;
1480 auto getTime = [&](
bool delta) {
1483 deltaTime = ConstantTimeOp::create(builder, node->terminator->getLoc(),
1488 epsilonTime = ConstantTimeOp::create(builder, node->terminator->getLoc(),
1493 auto insertDriveForSlot = [&](
DefSlot slot) {
1494 auto reachingDef = node->valueBefore->reachingDefs.lookup(slot);
1495 if (!reachingDef || reachingDef->condition.isNever())
1497 if (!node->suspends && !node->successors.empty() &&
1498 llvm::all_of(node->successors, [&](
auto *successor) {
1499 return successor->valueAfter->reachingDefs.contains(slot);
1502 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting drive for " <<
getSlot(slot) <<
" "
1503 << (
isDelayed(slot) ?
"(delayed)" :
"(blocking)")
1504 <<
" before " << *node->terminator <<
"\n");
1506 auto value = reachingDef->getValueOrPlaceholder();
1507 auto enable = reachingDef->condition.isConditional()
1508 ? reachingDef->getConditionOrPlaceholder()
1510 DriveOp::create(builder,
getLoc(slot),
getSlot(slot), value, time, enable);
1513 for (
auto slot : slots)
1515 for (
auto slot : slots)
1521void Promoter::insertDrives(DriveNode *node) {
1522 if (!promotable.contains(
getSlot(node->slot)))
1524 LLVM_DEBUG(llvm::dbgs() <<
"- Removing drive " << *node->op <<
"\n");
1525 pruner.eraseNow(node->op);
1534void Promoter::resolveDefinitions() {
1535 for (
auto *node : lattice.nodes) {
1536 if (
auto *probe = dyn_cast<ProbeNode>(node))
1537 resolveDefinitions(probe);
1538 else if (
auto *drive = dyn_cast<DriveNode>(node))
1539 resolveDefinitions(drive);
1544void Promoter::resolveDefinitions(ProbeNode *node) {
1545 if (!promotable.contains(node->slot))
1547 auto *def = node->valueBefore->reachingDefs.lookup(
blockingSlot(node->slot));
1548 assert(def &&
"no definition reaches probe");
1551 auto projections =
getProjections(node->op->getOperand(0), node->slot);
1554 auto builder = OpBuilder(node->op);
1555 auto value = def->getValueOrPlaceholder();
1559 LLVM_DEBUG(llvm::dbgs() <<
"- Replacing " << *node->op <<
" with " << value
1561 replaceValueWith(node->op->getResult(0), value);
1562 pruner.eraseNow(node->op);
1568void Promoter::resolveDefinitions(DriveNode *node) {
1569 if (!promotable.contains(
getSlot(node->slot)))
1576 if (!node->def->value || node->def->valueIsPlaceholder)
1577 resolveDefinitionValue(node);
1578 if (node->def->condition.isConditional() &&
1579 (!node->def->condition.getCondition() ||
1580 node->def->conditionIsPlaceholder))
1581 resolveDefinitionCondition(node);
1584void Promoter::resolveDefinitionValue(DriveNode *node) {
1587 auto *inDef = node->valueBefore->reachingDefs.lookup(node->slot);
1588 assert(inDef &&
"no definition reaches drive");
1589 auto driveOp = node->getDriveOp();
1590 LLVM_DEBUG(llvm::dbgs() <<
"- Injecting value for " << driveOp <<
"\n");
1596 auto builder = OpBuilder(driveOp);
1597 auto value = inDef->getValueOrPlaceholder();
1601 pruner.eraseLaterIfUnused(value);
1602 if (!driveOp.getEnable())
1603 value = driveOp.getValue();
1606 driveOp.getLoc(), driveOp.getEnable(), driveOp.getValue(), value);
1612 if (node->def->valueIsPlaceholder) {
1613 auto *placeholder = node->def->value.getDefiningOp();
1614 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1615 "placeholder replaced but valueIsPlaceholder still set");
1616 replaceValueWith(placeholder->getResult(0), value);
1617 pruner.eraseNow(placeholder);
1618 node->def->valueIsPlaceholder =
false;
1620 node->def->value = value;
1623void Promoter::resolveDefinitionCondition(DriveNode *node) {
1626 auto *inDef = node->valueBefore->reachingDefs.lookup(node->slot);
1627 assert(inDef &&
"no definition reaches drive");
1628 auto driveOp = node->getDriveOp();
1629 LLVM_DEBUG(llvm::dbgs() <<
"- Mutating condition for " << driveOp <<
"\n");
1630 OpBuilder builder(driveOp);
1635 if (inDef->condition.isNever())
1636 condition = driveOp.getEnable();
1639 builder.createOrFold<
comb::OrOp>(driveOp.getLoc(), driveOp.getEnable(),
1640 inDef->getConditionOrPlaceholder());
1643 if (node->def->conditionIsPlaceholder) {
1644 auto *placeholder = node->def->condition.getCondition().getDefiningOp();
1645 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1646 "placeholder replaced but conditionIsPlaceholder still set");
1647 replaceValueWith(placeholder->getResult(0), condition);
1648 pruner.eraseNow(placeholder);
1649 node->def->conditionIsPlaceholder =
false;
1651 node->def->condition.setCondition(condition);
1659void Promoter::insertBlockArgs() {
1660 bool anyArgsInserted =
true;
1661 while (anyArgsInserted) {
1662 anyArgsInserted =
false;
1663 for (
auto *node : lattice.nodes)
1664 if (auto *entry = dyn_cast<BlockEntry>(node))
1665 anyArgsInserted |= insertBlockArgs(entry);
1677bool Promoter::insertBlockArgs(BlockEntry *node) {
1683 enum class Which { Value, Condition };
1684 SmallVector<std::pair<DefSlot, Which>> neededSlots;
1685 auto addNeededSlot = [&](
DefSlot slot) {
1686 if (
auto *def = node->mergedDefs.lookup(slot)) {
1687 if (node->valueAfter->reachingDefs.contains(slot)) {
1688 if (def->valueIsPlaceholder)
1689 neededSlots.push_back({slot, Which::Value});
1690 if (def->conditionIsPlaceholder)
1691 neededSlots.push_back({slot, Which::Condition});
1695 for (
auto slot : slots) {
1699 if (neededSlots.empty())
1701 LLVM_DEBUG(llvm::dbgs() <<
"- Adding " << neededSlots.size()
1702 <<
" args to block " << node->block <<
"\n");
1705 for (
auto [slot, which] : neededSlots) {
1706 auto *def = node->mergedDefs.lookup(slot);
1709 case Which::Value: {
1712 auto *placeholder = def->value.getDefiningOp();
1713 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1714 "placeholder replaced but valueIsPlaceholder still set");
1716 replaceValueWith(placeholder->getResult(0), arg);
1717 pruner.eraseNow(placeholder);
1719 def->valueIsPlaceholder =
false;
1722 case Which::Condition: {
1726 auto *placeholder = def->condition.getCondition().getDefiningOp();
1727 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1728 "placeholder replaced but conditionIsPlaceholder still set");
1729 auto conditionArg = node->block->addArgument(
1730 IntegerType::get(region.getContext(), 1),
getLoc(slot));
1731 replaceValueWith(placeholder->getResult(0), conditionArg);
1732 pruner.eraseNow(placeholder);
1733 def->condition.setCondition(conditionArg);
1734 def->conditionIsPlaceholder =
false;
1741 for (
auto *predecessor : node->predecessors) {
1743 SmallVector<Value> args;
1744 for (
auto [slot, which] : neededSlots) {
1745 auto *def = predecessor->valueBefore->reachingDefs.lookup(slot);
1746 auto builder = OpBuilder::atBlockTerminator(predecessor->block);
1750 args.push_back(def->getValueOrPlaceholder());
1753 auto flatType = builder.getIntegerType(hw::getBitWidth(type));
1756 if (type != flatType)
1758 args.push_back(value);
1761 case Which::Condition:
1763 args.push_back(def->getConditionOrPlaceholder());
1766 builder.getI1Type(), 0));
1773 auto branchOp = cast<BranchOpInterface>(predecessor->terminator);
1774 for (
auto &blockOperand : branchOp->getBlockOperands())
1775 if (blockOperand.
get() == node->block)
1776 branchOp.getSuccessorOperands(blockOperand.getOperandNumber())
1785void Promoter::replaceValueWith(Value oldValue, Value newValue) {
1786 oldValue.replaceAllUsesWith(newValue);
1787 for (
auto *def : lattice.defs) {
1788 if (def->value == oldValue)
1789 def->value = newValue;
1790 if (def->condition.isConditional() &&
1791 def->condition.getCondition() == oldValue)
1792 def->condition.setCondition(newValue);
1801void Promoter::removeUnusedLocalSignals() {
1802 for (
auto *node : lattice.nodes)
1803 if (auto *signal = dyn_cast<SignalNode>(node))
1804 removeUnusedLocalSignal(signal);
1810void Promoter::removeUnusedLocalSignal(SignalNode *signal) {
1813 SmallSetVector<Operation *, 8> users;
1814 SmallVector<Operation *> worklist;
1815 worklist.push_back(signal->op);
1816 while (!worklist.empty()) {
1817 auto *op = worklist.pop_back_val();
1818 if (!isa<SignalOp, DriveOp, SigArrayGetOp, SigArraySliceOp, SigExtractOp,
1819 SigStructExtractOp>(op))
1821 for (
auto *user : op->getUsers())
1822 if (users.insert(user))
1823 worklist.push_back(user);
1828 LLVM_DEBUG(llvm::dbgs() <<
"- Removing local signal " << *signal->op <<
"\n");
1829 for (
auto *op :
llvm::reverse(users))
1830 pruner.eraseNow(op);
1838struct Mem2RegPass :
public llhd::impl::Mem2RegPassBase<Mem2RegPass> {
1839 void runOnOperation()
override;
1843void Mem2RegPass::runOnOperation() {
1844 SmallVector<Region *> regions;
1845 getOperation()->walk<WalkOrder::PreOrder>([&](Operation *op) {
1846 if (isa<ProcessOp, FinalOp, CombinationalOp>(op)) {
1847 auto ®ion = op->getRegion(0);
1848 if (!region.empty())
1849 regions.push_back(®ion);
1850 return WalkResult::skip();
1852 return WalkResult::advance();
1854 for (
auto *region : regions)
1855 if (failed(Promoter(*region).promote()))
1856 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)