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/Transforms/LLHDPasses.h.inc"
32using llvm::PointerIntPair;
33using llvm::SmallDenseSet;
34using llvm::SpecificBumpPtrAllocator;
38 if (
auto timeOp = value.getDefiningOp<ConstantTimeOp>()) {
39 auto t = timeOp.getValue();
40 return t.getTime() == 0 && t.getDelta() == 0 && t.getEpsilon() == 1;
47 if (
auto timeOp = value.getDefiningOp<ConstantTimeOp>()) {
48 auto t = timeOp.getValue();
49 return t.getTime() == 0 && t.getDelta() == 1 && t.getEpsilon() == 0;
57 if (
auto driveOp = dyn_cast<DrvOp>(op))
65 if (
auto driveOp = dyn_cast<DrvOp>(op))
79struct DriveCondition {
80 static DriveCondition never() {
return ConditionAndMode(Value{},
Never); }
81 static DriveCondition always() {
return ConditionAndMode(Value{}, Always); }
82 static DriveCondition conditional(Value condition = {}) {
83 return ConditionAndMode(condition, Conditional);
86 bool isNever()
const {
return conditionAndMode.getInt() ==
Never; }
87 bool isAlways()
const {
return conditionAndMode.getInt() == Always; }
88 bool isConditional()
const {
89 return conditionAndMode.getInt() == Conditional;
92 Value getCondition()
const {
return conditionAndMode.getPointer(); }
93 void setCondition(Value condition) { conditionAndMode.setPointer(condition); }
95 bool operator==(
const DriveCondition &other)
const {
96 return conditionAndMode == other.conditionAndMode;
98 bool operator!=(
const DriveCondition &other)
const {
99 return conditionAndMode != other.conditionAndMode;
108 typedef PointerIntPair<Value, 2> ConditionAndMode;
109 ConditionAndMode conditionAndMode;
111 DriveCondition(ConditionAndMode conditionAndMode)
112 : conditionAndMode(conditionAndMode) {}
113 friend DenseMapInfo<DriveCondition>;
125 DriveCondition condition;
126 bool valueIsPlaceholder =
false;
127 bool conditionIsPlaceholder =
false;
129 Def(Value value, DriveCondition condition)
130 : block(value.getParentBlock()), type(value.getType()), value(value),
131 condition(condition) {}
132 Def(Block *block, Type type, DriveCondition condition)
133 : block(block), type(type), condition(condition) {}
135 Value getValueOrPlaceholder();
136 Value getConditionOrPlaceholder();
142Value Def::getValueOrPlaceholder() {
144 auto builder = OpBuilder::atBlockBegin(block);
146 .create<UnrealizedConversionCastOp>(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 .
create<UnrealizedConversionCastOp>(builder.getUnknownLoc(),
174 conditionIsPlaceholder =
true;
176 condition.setCondition(value);
178 return condition.getCondition();
194 static bool isEqual(DriveCondition lhs, DriveCondition rhs) {
194 static bool isEqual(DriveCondition lhs, DriveCondition rhs) {
…}
212 return cast<hw::InOutType>(slot.getType()).getElementType();
227 LatticeNode *nodeBefore =
nullptr;
228 LatticeNode *nodeAfter =
nullptr;
229 SmallDenseSet<Value, 1> neededDefs;
234 enum class Kind { BlockEntry, BlockExit, Probe, Drive };
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>(n);
297struct ProbeNode :
public OpNode {
300 ProbeNode(PrbOp 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(DrvOp 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 DrvOp getDriveOp()
const {
return cast<DrvOp>(op); }
326 static bool classof(
const LatticeNode *n) {
return n->kind == Kind::Drive; }
333 LatticeValue *createValue() {
334 auto *value =
new (valueAllocator.Allocate()) LatticeValue();
335 values.push_back(value);
340 template <
class T,
typename... Args>
341 T *createNode(Args... args) {
343 new (getAllocator<T>().Allocate()) T(std::forward<Args>(args)...);
344 nodes.push_back(node);
349 template <
typename... Args>
350 Def *createDef(Args... args) {
351 auto *def =
new (defAllocator.Allocate()) Def(std::forward<Args>(args)...);
357 Def *createDef(Value value, DriveCondition mode) {
358 auto &slot = defsForValues[{value, mode}];
360 slot =
new (defAllocator.Allocate()) Def(value, mode);
361 defs.push_back(slot);
367 void dump(llvm::raw_ostream &os = llvm::dbgs());
371 std::vector<LatticeNode *> nodes;
373 std::vector<LatticeValue *> values;
375 std::vector<Def *> defs;
379 DenseMap<std::pair<Value, DriveCondition>, Def *> defsForValues;
382 SpecificBumpPtrAllocator<LatticeValue> valueAllocator;
383 SpecificBumpPtrAllocator<Def> defAllocator;
384 SpecificBumpPtrAllocator<BlockEntry> blockEntryAllocator;
385 SpecificBumpPtrAllocator<BlockExit> blockExitAllocator;
386 SpecificBumpPtrAllocator<ProbeNode> probeAllocator;
387 SpecificBumpPtrAllocator<DriveNode> driveAllocator;
391 SpecificBumpPtrAllocator<T> &getAllocator();
397SpecificBumpPtrAllocator<BlockEntry> &Lattice::getAllocator() {
398 return blockEntryAllocator;
401SpecificBumpPtrAllocator<BlockExit> &Lattice::getAllocator() {
402 return blockExitAllocator;
405SpecificBumpPtrAllocator<ProbeNode> &Lattice::getAllocator() {
406 return probeAllocator;
409SpecificBumpPtrAllocator<DriveNode> &Lattice::getAllocator() {
410 return driveAllocator;
417void Lattice::dump(llvm::raw_ostream &os) {
419 llvm::MapVector<Block *, unsigned> blockNames;
420 llvm::MapVector<Value, unsigned> memNames;
421 llvm::MapVector<Def *, unsigned> defNames;
423 auto blockName = [&](
Block *block) {
424 unsigned id = blockNames.insert({block, blockNames.size()}).first->second;
425 return std::string(
"bb") + llvm::utostr(
id);
428 auto memName = [&](
DefSlot value) {
430 memNames.insert({
getSlot(value), memNames.size()}).first->second;
431 return std::string(
"mem") + llvm::utostr(
id) +
435 auto defName = [&](Def *def) {
436 unsigned id = defNames.insert({def, defNames.size()}).first->second;
437 return std::string(
"def") + llvm::utostr(
id);
441 for (
auto *node : nodes)
442 if (auto *entry = dyn_cast<BlockEntry>(node))
443 blockName(entry->block);
447 for (
auto *node : nodes) {
448 auto *entry = dyn_cast<BlockEntry>(node);
453 os <<
" " << blockName(entry->block) <<
":";
454 if (entry->predecessors.empty()) {
455 os <<
" // no predecessors";
458 for (
auto *node : entry->predecessors)
459 os <<
" " << blockName(node->block);
464 auto *value = entry->valueAfter;
467 if (!value->neededDefs.empty()) {
469 for (
auto mem : value->neededDefs)
473 if (!value->reachingDefs.empty()) {
475 for (
auto [mem, def] : value->reachingDefs) {
476 os <<
" " << memName(mem) <<
"=" << defName(def);
477 if (def->condition.isNever())
479 else if (def->condition.isAlways())
486 if (isa<BlockExit>(value->nodeAfter))
490 if (
auto *node = dyn_cast<ProbeNode>(value->nodeAfter))
491 os <<
" probe " << memName(
blockingSlot(node->slot)) <<
"\n";
492 else if (
auto *node = dyn_cast<DriveNode>(value->nodeAfter))
493 os <<
" drive " << memName(node->slot) <<
"\n";
498 value = cast<OpNode>(value->nodeAfter)->valueAfter;
502 auto *exit = cast<BlockExit>(value->nodeAfter);
503 if (isa<WaitOp>(exit->terminator))
505 else if (exit->successors.empty())
509 for (
auto *node : exit->successors)
510 os <<
" " << blockName(node->block);
512 os <<
" // suspends";
517 for (
auto [mem,
id] : memNames)
518 os <<
" mem" << id <<
": " << mem <<
"\n";
552 while (fromSignal != toSlot) {
553 auto *op = cast<OpResult>(fromSignal).getOwner();
554 stack.push_back({op, Value()});
555 fromSignal = op->getOperand(0);
568 for (
auto &projection : llvm::reverse(projections)) {
569 projection.into = value;
570 value = TypeSwitch<Operation *, Value>(projection.op)
571 .Case<SigArrayGetOp>([&](
auto op) {
573 op.getLoc(), value, op.getIndex());
593 for (
auto projection : projections) {
594 value = TypeSwitch<Operation *, Value>(projection.op)
595 .Case<SigArrayGetOp>([&](
auto op) {
596 return builder.createOrFold<hw::ArrayInjectOp>(
597 op.getLoc(), projection.into, op.getIndex(), value);
610 Promoter(Region ®ion) : region(region) {}
611 LogicalResult promote();
613 void findPromotableSlots();
614 Value resolveSlot(Value projectionOrSlot);
616 void captureAcrossWait();
617 void captureAcrossWait(PrbOp probeOp, ArrayRef<WaitOp> waitOps,
618 Liveness &liveness, DominanceInfo &dominance);
620 void constructLattice();
621 void propagateBackward();
622 void propagateBackward(LatticeNode *node);
623 void propagateForward(
bool optimisticMerges);
624 void propagateForward(LatticeNode *node,
bool optimisticMerges);
625 void markDirty(LatticeNode *node);
627 void insertProbeBlocks();
629 void insertProbes(BlockEntry *node);
631 void insertDriveBlocks();
633 void insertDrives(BlockExit *node);
634 void insertDrives(DriveNode *node);
636 void resolveDefinitions();
637 void resolveDefinitions(ProbeNode *node);
638 void resolveDefinitions(DriveNode *node);
639 void resolveDefinitionValue(DriveNode *node);
640 void resolveDefinitionCondition(DriveNode *node);
642 void insertBlockArgs();
643 bool insertBlockArgs(BlockEntry *node);
644 void replaceValueWith(Value oldValue, Value newValue);
652 SmallVector<Value> slots;
657 SmallDenseSet<Value> promotable;
663 SmallPtrSet<LatticeNode *, 4> dirtyNodes;
670LogicalResult Promoter::promote() {
674 findPromotableSlots();
682 llvm::dbgs() <<
"Initial lattice:\n";
689 llvm::dbgs() <<
"After backward propagation:\n";
697 llvm::dbgs() <<
"After probe insertion:\n";
702 propagateForward(
true);
703 propagateForward(
false);
705 llvm::dbgs() <<
"After forward propagation:\n";
710 resolveDefinitions();
716 llvm::dbgs() <<
"After def resolution and drive insertion:\n";
730void Promoter::findPromotableSlots() {
731 SmallPtrSet<Value, 8> seenSlots;
732 SmallPtrSet<Operation *, 8> checkedUsers;
733 SmallVector<Operation *, 8> userWorklist;
735 region.walk([&](Operation *op) {
736 for (
auto operand : op->getOperands()) {
737 if (!seenSlots.insert(operand).second)
743 if (!operand.getDefiningOp<llhd::SignalOp>())
747 auto checkUser = [&](Operation *user) ->
bool {
749 if (region.isProperAncestor(user->getParentRegion()))
752 if (user->getParentRegion() != ®ion)
756 if (isa<SigArrayGetOp>(user)) {
757 for (
auto *projectionUser : user->getUsers()) {
758 if (projectionUser->getBlock() != user->getBlock())
760 if (checkedUsers.insert(projectionUser).second)
761 userWorklist.push_back(projectionUser);
763 projections.insert({user->getResult(0), operand});
768 checkedUsers.clear();
769 if (!llvm::all_of(operand.getUsers(), [&](
auto *user) {
771 if (checkedUsers.insert(user).second)
772 userWorklist.push_back(user);
773 while (!userWorklist.empty() && allOk)
774 allOk &= checkUser(userWorklist.pop_back_val());
775 userWorklist.clear();
780 slots.push_back(operand);
785 promotable.insert(slots.begin(), slots.end());
786 for (
auto [projection, slot] :
llvm::make_early_inc_range(projections))
787 if (!promotable.contains(slot))
788 projections.erase(projection);
789 for (
auto [projection, slot] : projections)
790 promotable.insert(projection);
792 LLVM_DEBUG(llvm::dbgs() <<
"Found " << slots.size() <<
" promotable slots, "
793 << promotable.size() <<
" promotable values\n");
798Value Promoter::resolveSlot(Value projectionOrSlot) {
799 if (
auto slot = projections.lookup(projectionOrSlot))
801 return projectionOrSlot;
808void Promoter::captureAcrossWait() {
809 if (region.hasOneBlock())
812 SmallVector<WaitOp> waitOps;
813 for (
auto &block : region)
814 if (auto waitOp = dyn_cast<WaitOp>(block.getTerminator()))
815 waitOps.push_back(waitOp);
817 DominanceInfo dominance(region.getParentOp());
818 Liveness liveness(region.getParentOp());
820 SmallVector<WaitOp> crossingWaitOps;
821 for (
auto &block : region) {
822 for (
auto probeOp : block.getOps<PrbOp>()) {
823 for (
auto waitOp : waitOps)
824 if (liveness.getLiveness(waitOp->getBlock())->
isLiveOut(probeOp))
825 crossingWaitOps.push_back(waitOp);
826 if (!crossingWaitOps.empty()) {
827 captureAcrossWait(probeOp, crossingWaitOps, liveness, dominance);
828 crossingWaitOps.clear();
838void Promoter::captureAcrossWait(PrbOp probeOp, ArrayRef<WaitOp> waitOps,
839 Liveness &liveness, DominanceInfo &dominance) {
841 llvm::dbgs() <<
"Capture " << probeOp <<
"\n";
842 for (
auto waitOp : waitOps)
843 llvm::dbgs() <<
"- Across " << waitOp <<
"\n";
848 auto &domTree = dominance.getDomTree(®ion);
849 llvm::IDFCalculatorBase<Block, false> idfCalculator(domTree);
853 SmallPtrSet<Block *, 4> definingBlocks;
854 definingBlocks.insert(probeOp->getBlock());
855 for (
auto waitOp : waitOps)
856 definingBlocks.insert(waitOp.getDest());
857 idfCalculator.setDefiningBlocks(definingBlocks);
860 SmallPtrSet<Block *, 16> liveInBlocks;
861 for (
auto &block : region)
862 if (liveness.getLiveness(&block)->isLiveIn(probeOp))
863 liveInBlocks.insert(&block);
864 idfCalculator.setLiveInBlocks(liveInBlocks);
868 SmallVector<Block *> mergePointsVec;
869 idfCalculator.calculate(mergePointsVec);
870 SmallPtrSet<Block *, 16> mergePoints(mergePointsVec.begin(),
871 mergePointsVec.end());
872 for (
auto waitOp : waitOps)
873 mergePoints.insert(waitOp.getDest());
874 LLVM_DEBUG(llvm::dbgs() <<
"- " << mergePoints.size() <<
" merge points\n");
879 struct WorklistItem {
880 DominanceInfoNode *domNode;
883 SmallVector<WorklistItem> worklist;
884 worklist.push_back({domTree.getNode(probeOp->getBlock()), probeOp});
886 while (!worklist.empty()) {
887 auto item = worklist.pop_back_val();
888 auto *block = item.domNode->getBlock();
891 if (mergePoints.contains(block))
893 block->addArgument(probeOp.getType(), probeOp.getLoc());
897 for (
auto &op : *block)
898 op.replaceUsesOfWith(probeOp, item.reachingDef);
902 if (
auto branchOp = dyn_cast<BranchOpInterface>(block->getTerminator())) {
903 for (
auto &blockOperand : branchOp->getBlockOperands())
904 if (mergePoints.contains(blockOperand.
get()))
905 branchOp.getSuccessorOperands(blockOperand.getOperandNumber())
906 .
append(item.reachingDef);
907 }
else if (
auto waitOp = dyn_cast<WaitOp>(block->getTerminator())) {
908 if (mergePoints.contains(waitOp.getDest()))
909 waitOp.getDestOperandsMutable().append(item.reachingDef);
912 for (
auto *child : item.domNode->children())
913 worklist.push_back({child, item.reachingDef});
923void Promoter::constructLattice() {
926 for (
auto &block : region) {
927 auto *entry = lattice.createNode<BlockEntry>(&block, lattice.createValue());
928 blockEntries.insert({&block, entry});
932 for (
auto &block : region) {
933 auto *valueBefore = blockEntries.lookup(&block)->valueAfter;
936 for (
auto &op : block.without_terminator()) {
938 if (
auto probeOp = dyn_cast<PrbOp>(op)) {
939 if (!promotable.contains(probeOp.getSignal()))
941 auto *node = lattice.createNode<ProbeNode>(
942 probeOp, resolveSlot(probeOp.getSignal()), valueBefore,
943 lattice.createValue());
944 valueBefore = node->valueAfter;
949 if (
auto driveOp = dyn_cast<DrvOp>(op)) {
952 if (!promotable.contains(driveOp.getSignal()))
954 auto condition = DriveCondition::always();
955 if (
auto enable = driveOp.getEnable())
956 condition = DriveCondition::conditional(enable);
960 auto slot = resolveSlot(driveOp.getSignal());
961 auto *def = driveOp.getSignal() == slot
962 ? lattice.createDef(driveOp.getValue(), condition)
963 : lattice.createDef(driveOp->getBlock(),
965 auto *node = lattice.createNode<DriveNode>(
966 driveOp, slot, def, valueBefore, lattice.createValue());
967 valueBefore = node->valueAfter;
973 auto *exit = lattice.createNode<BlockExit>(&block, valueBefore);
974 for (
auto *otherBlock : exit->terminator->getSuccessors()) {
975 auto *otherEntry = blockEntries.lookup(otherBlock);
976 exit->successors.push_back(otherEntry);
977 otherEntry->predecessors.push_back(exit);
984void Promoter::propagateBackward() {
985 for (
auto *node : lattice.nodes)
986 propagateBackward(node);
987 while (!dirtyNodes.empty()) {
988 auto *node = *dirtyNodes.begin();
989 dirtyNodes.erase(node);
990 propagateBackward(node);
996void Promoter::propagateBackward(LatticeNode *node) {
997 auto update = [&](LatticeValue *value,
auto &neededDefs) {
998 if (value->neededDefs != neededDefs) {
999 value->neededDefs = neededDefs;
1000 markDirty(value->nodeBefore);
1005 if (
auto *probe = dyn_cast<ProbeNode>(node)) {
1006 auto needed = probe->valueAfter->neededDefs;
1007 needed.insert(probe->slot);
1008 update(probe->valueBefore, needed);
1018 if (
auto *drive = dyn_cast<DriveNode>(node)) {
1019 auto needed = drive->valueAfter->neededDefs;
1020 if (drive->drivesProjection())
1021 needed.insert(
getSlot(drive->slot));
1022 else if (!
isDelayed(drive->slot) && !drive->getDriveOp().getEnable())
1023 needed.erase(
getSlot(drive->slot));
1024 update(drive->valueBefore, needed);
1029 if (
auto *entry = dyn_cast<BlockEntry>(node)) {
1030 for (
auto *predecessor : entry->predecessors)
1031 markDirty(predecessor);
1036 if (
auto *exit = dyn_cast<BlockExit>(node)) {
1039 SmallDenseSet<Value, 1> needed;
1040 for (
auto *successors : exit->successors)
1041 needed.insert(successors->valueAfter->neededDefs.begin(),
1042 successors->valueAfter->neededDefs.
end());
1043 update(exit->valueBefore, needed);
1047 assert(
false &&
"unhandled node in backward propagation");
1058void Promoter::propagateForward(
bool optimisticMerges) {
1059 for (
auto *node : lattice.nodes)
1060 propagateForward(node, optimisticMerges);
1061 while (!dirtyNodes.empty()) {
1062 auto *node = *dirtyNodes.begin();
1063 dirtyNodes.erase(node);
1064 propagateForward(node, optimisticMerges);
1069void Promoter::propagateForward(LatticeNode *node,
bool optimisticMerges) {
1070 auto update = [&](LatticeValue *value,
auto &reachingDefs) {
1071 if (value->reachingDefs != reachingDefs) {
1072 value->reachingDefs = reachingDefs;
1073 markDirty(value->nodeAfter);
1078 if (
auto *probe = dyn_cast<ProbeNode>(node)) {
1079 update(probe->valueAfter, probe->valueBefore->reachingDefs);
1093 if (
auto *drive = dyn_cast<DriveNode>(node)) {
1094 auto reaching = drive->valueBefore->reachingDefs;
1100 if (drive->drivesProjection() || drive->getDriveOp().getEnable()) {
1101 auto *inDef = reaching.lookup(drive->slot);
1103 if (drive->def->value != inDef->value)
1104 drive->def->value = {};
1105 if (inDef->condition.isAlways() || drive->def->condition.isAlways())
1106 drive->def->condition = DriveCondition::always();
1107 else if (drive->def->condition != inDef->condition)
1108 drive->def->condition = DriveCondition::conditional();
1112 reaching[drive->slot] = drive->def;
1115 update(drive->valueAfter, reaching);
1121 if (
auto *entry = dyn_cast<BlockEntry>(node)) {
1124 for (
auto [slot, insertedProbe] : entry->insertedProbes) {
1132 for (
auto *predecessor : entry->predecessors)
1133 if (!predecessor->suspends)
1134 reachingDefs.insert(predecessor->valueBefore->reachingDefs.begin(),
1135 predecessor->valueBefore->reachingDefs.
end());
1137 for (
auto pair : reachingDefs) {
1139 Def *reachingDef = pair.second;
1140 DriveCondition reachingDefCondition = reachingDef->condition;
1143 if (reaching.contains(slot))
1153 if (llvm::any_of(entry->predecessors, [&](
auto *predecessor) {
1154 return predecessor->suspends;
1157 for (
auto *predecessor : entry->predecessors) {
1158 auto otherDef = predecessor->valueBefore->reachingDefs.lookup(slot);
1159 if (!otherDef && optimisticMerges)
1163 if (reachingDef != otherDef)
1164 reachingDef =
nullptr;
1168 otherDef ? otherDef->condition : DriveCondition::never();
1169 if (reachingDefCondition != condition)
1170 reachingDefCondition = DriveCondition::conditional();
1176 reachingDef = entry->mergedDefs.lookup(slot);
1178 reachingDef = lattice.createDef(entry->block,
getStoredType(slot),
1179 reachingDefCondition);
1180 entry->mergedDefs.insert({slot, reachingDef});
1182 reachingDef->condition = reachingDefCondition;
1184 reaching.insert({slot, reachingDef});
1187 update(entry->valueAfter, reaching);
1192 if (
auto *exit = dyn_cast<BlockExit>(node)) {
1193 for (
auto *successor : exit->successors)
1194 markDirty(successor);
1198 assert(
false &&
"unhandled node in forward propagation");
1202void Promoter::markDirty(LatticeNode *node) {
1204 dirtyNodes.insert(node);
1215void Promoter::insertProbeBlocks() {
1219 SmallDenseSet<std::pair<BlockExit *, BlockEntry *>, 1> worklist;
1220 for (
auto *node : lattice.nodes) {
1221 if (
auto *entry = dyn_cast<BlockEntry>(node)) {
1222 SmallVector<Value> partialSlots;
1223 for (
auto slot : entry->valueAfter->neededDefs) {
1224 unsigned numIncoming = 0;
1225 for (
auto *predecessor : entry->predecessors)
1226 if (predecessor->valueBefore->neededDefs.contains(slot))
1228 if (numIncoming != 0 && numIncoming != entry->predecessors.size())
1229 partialSlots.push_back(slot);
1231 for (
auto *predecessor : entry->predecessors)
1232 if (
llvm::any_of(partialSlots, [&](auto slot) {
1233 return !predecessor->valueBefore->neededDefs.contains(slot);
1235 worklist.insert({predecessor, entry});
1240 for (
auto [predecessor, successor] : worklist) {
1241 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting probe block towards " << successor
1242 <<
" after " << *predecessor->terminator <<
"\n");
1243 OpBuilder builder(predecessor->terminator);
1244 auto *newBlock = builder.createBlock(successor->block);
1245 for (
auto oldArg : successor->block->getArguments())
1246 newBlock->addArgument(oldArg.getType(), oldArg.
getLoc());
1247 builder.create<cf::BranchOp>(predecessor->terminator->getLoc(),
1248 successor->block, newBlock->getArguments());
1249 for (
auto &blockOp : predecessor->terminator->getBlockOperands())
1250 if (blockOp.
get() == successor->block)
1251 blockOp.set(newBlock);
1254 auto *value = lattice.createValue();
1255 value->neededDefs = successor->valueAfter->neededDefs;
1256 auto *newEntry = lattice.createNode<BlockEntry>(newBlock, value);
1257 auto *newExit = lattice.createNode<BlockExit>(newBlock, value);
1258 newEntry->predecessors.push_back(predecessor);
1259 newExit->successors.push_back(successor);
1260 llvm::replace(successor->predecessors, predecessor, newExit);
1261 llvm::replace(predecessor->successors, successor, newEntry);
1268void Promoter::insertProbes() {
1269 for (
auto *node : lattice.nodes) {
1270 if (
auto *entry = dyn_cast<BlockEntry>(node))
1271 insertProbes(entry);
1277void Promoter::insertProbes(BlockEntry *node) {
1278 auto builder = OpBuilder::atBlockBegin(node->block);
1279 for (
auto neededDef : slots) {
1280 if (!node->valueAfter->neededDefs.contains(neededDef))
1282 if (!node->predecessors.empty() &&
1283 llvm::all_of(node->predecessors, [&](
auto *predecessor) {
1284 return predecessor->valueBefore->neededDefs.contains(neededDef);
1287 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting probe for " << neededDef
1288 <<
" in block " << node->block <<
"\n");
1289 auto value = builder.create<PrbOp>(neededDef.getLoc(), neededDef);
1290 auto *def = lattice.createDef(value, DriveCondition::never());
1291 node->insertedProbes.push_back({neededDef, def});
1297void Promoter::insertDriveBlocks() {
1301 SmallDenseSet<std::pair<BlockExit *, BlockEntry *>, 1> worklist;
1302 for (
auto *node : lattice.nodes) {
1303 if (
auto *exit = dyn_cast<BlockExit>(node)) {
1304 SmallVector<DefSlot> partialSlots;
1305 for (
auto [slot, reachingDef] : exit->valueBefore->reachingDefs) {
1306 if (reachingDef->condition.isNever())
1308 unsigned numContinues = 0;
1309 for (
auto *successor : exit->successors)
1310 if (successor->valueAfter->reachingDefs.contains(slot))
1312 if (numContinues != 0 && numContinues != exit->successors.size())
1313 partialSlots.push_back(slot);
1315 for (
auto *successor : exit->successors)
1316 if (
llvm::any_of(partialSlots, [&](auto slot) {
1317 return !successor->valueAfter->reachingDefs.contains(slot);
1319 worklist.insert({exit, successor});
1324 for (
auto [predecessor, successor] : worklist) {
1325 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting drive block towards " << successor
1326 <<
" after " << *predecessor->terminator <<
"\n");
1327 OpBuilder builder(predecessor->terminator);
1328 auto *newBlock = builder.createBlock(successor->block);
1329 for (
auto oldArg : successor->block->getArguments())
1330 newBlock->addArgument(oldArg.getType(), oldArg.
getLoc());
1331 builder.create<cf::BranchOp>(predecessor->terminator->getLoc(),
1332 successor->block, newBlock->getArguments());
1333 for (
auto &blockOp : predecessor->terminator->getBlockOperands())
1334 if (blockOp.
get() == successor->block)
1335 blockOp.set(newBlock);
1338 auto *value = lattice.createValue();
1339 value->neededDefs = successor->valueAfter->neededDefs;
1340 value->reachingDefs = predecessor->valueBefore->reachingDefs;
1341 auto *newEntry = lattice.createNode<BlockEntry>(newBlock, value);
1342 auto *newExit = lattice.createNode<BlockExit>(newBlock, value);
1343 newEntry->predecessors.push_back(predecessor);
1344 newExit->successors.push_back(successor);
1345 llvm::replace(successor->predecessors, predecessor, newExit);
1346 llvm::replace(predecessor->successors, successor, newEntry);
1353void Promoter::insertDrives() {
1354 for (
auto *node : lattice.nodes) {
1355 if (
auto *exit = dyn_cast<BlockExit>(node))
1357 else if (
auto *drive = dyn_cast<DriveNode>(node))
1358 insertDrives(drive);
1364void Promoter::insertDrives(BlockExit *node) {
1365 auto builder = OpBuilder::atBlockTerminator(node->block);
1367 ConstantTimeOp epsilonTime;
1368 ConstantTimeOp deltaTime;
1369 auto getTime = [&](
bool delta) {
1372 deltaTime = builder.create<ConstantTimeOp>(node->terminator->getLoc(),
1377 epsilonTime = builder.create<ConstantTimeOp>(node->terminator->getLoc(),
1382 auto insertDriveForSlot = [&](
DefSlot slot) {
1383 auto reachingDef = node->valueBefore->reachingDefs.lookup(slot);
1384 if (!reachingDef || reachingDef->condition.isNever())
1386 if (!node->suspends && !node->successors.empty() &&
1387 llvm::all_of(node->successors, [&](
auto *successor) {
1388 return successor->valueAfter->reachingDefs.contains(slot);
1391 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting drive for " <<
getSlot(slot) <<
" "
1392 << (
isDelayed(slot) ?
"(delayed)" :
"(blocking)")
1393 <<
" before " << *node->terminator <<
"\n");
1395 auto value = reachingDef->getValueOrPlaceholder();
1396 auto enable = reachingDef->condition.isConditional()
1397 ? reachingDef->getConditionOrPlaceholder()
1399 builder.create<DrvOp>(
getLoc(slot),
getSlot(slot), value, time, enable);
1402 for (
auto slot : slots)
1404 for (
auto slot : slots)
1410void Promoter::insertDrives(DriveNode *node) {
1411 if (!promotable.contains(
getSlot(node->slot)))
1413 LLVM_DEBUG(llvm::dbgs() <<
"- Removing drive " << *node->op <<
"\n");
1414 pruner.eraseNow(node->op);
1423void Promoter::resolveDefinitions() {
1424 for (
auto *node : lattice.nodes) {
1425 if (
auto *probe = dyn_cast<ProbeNode>(node))
1426 resolveDefinitions(probe);
1427 else if (
auto *drive = dyn_cast<DriveNode>(node))
1428 resolveDefinitions(drive);
1433void Promoter::resolveDefinitions(ProbeNode *node) {
1434 if (!promotable.contains(node->slot))
1436 auto *def = node->valueBefore->reachingDefs.lookup(
blockingSlot(node->slot));
1437 assert(def &&
"no definition reaches probe");
1440 auto projections =
getProjections(node->op->getOperand(0), node->slot);
1443 auto builder = OpBuilder(node->op);
1444 auto value = def->getValueOrPlaceholder();
1448 LLVM_DEBUG(llvm::dbgs() <<
"- Replacing " << *node->op <<
"\n");
1449 replaceValueWith(node->op->getResult(0), value);
1450 pruner.eraseNow(node->op);
1456void Promoter::resolveDefinitions(DriveNode *node) {
1457 if (!promotable.contains(
getSlot(node->slot)))
1464 if (!node->def->value || node->def->valueIsPlaceholder)
1465 resolveDefinitionValue(node);
1466 if (node->def->condition.isConditional() &&
1467 (!node->def->condition.getCondition() ||
1468 node->def->conditionIsPlaceholder))
1469 resolveDefinitionCondition(node);
1472void Promoter::resolveDefinitionValue(DriveNode *node) {
1475 auto *inDef = node->valueBefore->reachingDefs.lookup(node->slot);
1476 assert(inDef &&
"no definition reaches drive");
1477 auto driveOp = node->getDriveOp();
1478 LLVM_DEBUG(llvm::dbgs() <<
"- Injecting value for " << driveOp <<
"\n");
1484 auto builder = OpBuilder(driveOp);
1485 auto value = inDef->getValueOrPlaceholder();
1489 pruner.eraseLaterIfUnused(value);
1490 if (!driveOp.getEnable())
1491 value = driveOp.getValue();
1494 driveOp.getLoc(), driveOp.getEnable(), driveOp.getValue(), value);
1500 if (node->def->valueIsPlaceholder) {
1501 auto *placeholder = node->def->value.getDefiningOp();
1502 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1503 "placeholder replaced but valueIsPlaceholder still set");
1504 replaceValueWith(placeholder->getResult(0), value);
1505 pruner.eraseNow(placeholder);
1506 node->def->valueIsPlaceholder =
false;
1508 node->def->value = value;
1511void Promoter::resolveDefinitionCondition(DriveNode *node) {
1514 auto *inDef = node->valueBefore->reachingDefs.lookup(node->slot);
1515 assert(inDef &&
"no definition reaches drive");
1516 auto driveOp = node->getDriveOp();
1517 LLVM_DEBUG(llvm::dbgs() <<
"- Mutating condition for " << driveOp <<
"\n");
1518 OpBuilder builder(driveOp);
1523 if (inDef->condition.isNever())
1524 condition = driveOp.getEnable();
1527 builder.createOrFold<
comb::OrOp>(driveOp.getLoc(), driveOp.getEnable(),
1528 inDef->getConditionOrPlaceholder());
1531 if (node->def->conditionIsPlaceholder) {
1532 auto *placeholder = node->def->condition.getCondition().getDefiningOp();
1533 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1534 "placeholder replaced but conditionIsPlaceholder still set");
1535 replaceValueWith(placeholder->getResult(0), condition);
1536 pruner.eraseNow(placeholder);
1537 node->def->conditionIsPlaceholder =
false;
1539 node->def->condition.setCondition(condition);
1547void Promoter::insertBlockArgs() {
1548 bool anyArgsInserted =
true;
1549 while (anyArgsInserted) {
1550 anyArgsInserted =
false;
1551 for (
auto *node : lattice.nodes)
1552 if (auto *entry = dyn_cast<BlockEntry>(node))
1553 anyArgsInserted |= insertBlockArgs(entry);
1565bool Promoter::insertBlockArgs(BlockEntry *node) {
1571 enum class Which { Value, Condition };
1572 SmallVector<std::pair<DefSlot, Which>> neededSlots;
1573 auto addNeededSlot = [&](
DefSlot slot) {
1574 if (
auto *def = node->mergedDefs.lookup(slot)) {
1575 if (node->valueAfter->reachingDefs.contains(slot)) {
1576 if (def->valueIsPlaceholder)
1577 neededSlots.push_back({slot, Which::Value});
1578 if (def->conditionIsPlaceholder)
1579 neededSlots.push_back({slot, Which::Condition});
1583 for (
auto slot : slots) {
1587 if (neededSlots.empty())
1589 LLVM_DEBUG(llvm::dbgs() <<
"- Adding " << neededSlots.size()
1590 <<
" args to block " << node->block <<
"\n");
1593 for (
auto [slot, which] : neededSlots) {
1594 auto *def = node->mergedDefs.lookup(slot);
1597 case Which::Value: {
1600 auto *placeholder = def->value.getDefiningOp();
1601 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1602 "placeholder replaced but valueIsPlaceholder still set");
1604 replaceValueWith(placeholder->getResult(0), arg);
1605 pruner.eraseNow(placeholder);
1607 def->valueIsPlaceholder =
false;
1610 case Which::Condition: {
1614 auto *placeholder = def->condition.getCondition().getDefiningOp();
1615 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1616 "placeholder replaced but conditionIsPlaceholder still set");
1617 auto conditionArg = node->block->addArgument(
1618 IntegerType::get(region.getContext(), 1),
getLoc(slot));
1619 replaceValueWith(placeholder->getResult(0), conditionArg);
1620 pruner.eraseNow(placeholder);
1621 def->condition.setCondition(conditionArg);
1622 def->conditionIsPlaceholder =
false;
1629 for (
auto *predecessor : node->predecessors) {
1631 SmallVector<Value> args;
1632 for (
auto [slot, which] : neededSlots) {
1633 auto *def = predecessor->valueBefore->reachingDefs.lookup(slot);
1634 auto builder = OpBuilder::atBlockTerminator(predecessor->block);
1638 args.push_back(def->getValueOrPlaceholder());
1641 auto flatType = builder.getIntegerType(hw::getBitWidth(type));
1644 if (type != flatType)
1646 args.push_back(value);
1649 case Which::Condition:
1651 args.push_back(def->getConditionOrPlaceholder());
1654 getLoc(slot), builder.getI1Type(), 0));
1661 auto branchOp = cast<BranchOpInterface>(predecessor->terminator);
1662 for (
auto &blockOperand : branchOp->getBlockOperands())
1663 if (blockOperand.
get() == node->block)
1664 branchOp.getSuccessorOperands(blockOperand.getOperandNumber())
1673void Promoter::replaceValueWith(Value oldValue, Value newValue) {
1674 oldValue.replaceAllUsesWith(newValue);
1675 for (
auto *def : lattice.defs) {
1676 if (def->value == oldValue)
1677 def->value = newValue;
1678 if (def->condition.isConditional() &&
1679 def->condition.getCondition() == oldValue)
1680 def->condition.setCondition(newValue);
1689struct Mem2RegPass :
public llhd::impl::Mem2RegPassBase<Mem2RegPass> {
1690 void runOnOperation()
override;
1694void Mem2RegPass::runOnOperation() {
1695 SmallVector<Region *> regions;
1696 getOperation()->walk<WalkOrder::PreOrder>([&](Operation *op) {
1697 if (isa<ProcessOp, FinalOp>(op)) {
1698 auto ®ion = op->getRegion(0);
1699 if (!region.empty())
1700 regions.push_back(®ion);
1701 return WalkResult::skip();
1703 return WalkResult::advance();
1705 for (
auto *region : regions)
1706 if (failed(Promoter(*region).promote()))
1707 return signalPassFailure();
static bool isAlways(Attribute attr, bool expected)
static bool isLiveOut(Value val)
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)