12#include "mlir/Analysis/Liveness.h"
13#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
14#include "mlir/IR/Dominance.h"
15#include "llvm/Support/Debug.h"
16#include "llvm/Support/GenericIteratedDominanceFrontier.h"
18#define DEBUG_TYPE "llhd-mem2reg"
22#define GEN_PASS_DEF_MEM2REGPASS
23#include "circt/Dialect/LLHD/Transforms/LLHDPasses.h.inc"
30using llvm::PointerIntPair;
31using llvm::SmallDenseSet;
32using llvm::SpecificBumpPtrAllocator;
36 if (
auto timeOp = value.getDefiningOp<ConstantTimeOp>()) {
37 auto t = timeOp.getValue();
38 return t.getTime() == 0 && t.getDelta() == 0 && t.getEpsilon() == 1;
45 if (
auto timeOp = value.getDefiningOp<ConstantTimeOp>()) {
46 auto t = timeOp.getValue();
47 return t.getTime() == 0 && t.getDelta() == 1 && t.getEpsilon() == 0;
55 if (
auto driveOp = dyn_cast<DrvOp>(op))
63 if (
auto driveOp = dyn_cast<DrvOp>(op))
77struct DriveCondition {
78 static DriveCondition never() {
return ConditionAndMode(Value{},
Never); }
79 static DriveCondition always() {
return ConditionAndMode(Value{}, Always); }
80 static DriveCondition conditional(Value condition = {}) {
81 return ConditionAndMode(condition, Conditional);
84 bool isNever()
const {
return conditionAndMode.getInt() ==
Never; }
85 bool isAlways()
const {
return conditionAndMode.getInt() == Always; }
86 bool isConditional()
const {
87 return conditionAndMode.getInt() == Conditional;
90 Value getCondition()
const {
return conditionAndMode.getPointer(); }
91 void setCondition(Value condition) { conditionAndMode.setPointer(condition); }
93 bool operator==(
const DriveCondition &other)
const {
94 return conditionAndMode == other.conditionAndMode;
96 bool operator!=(
const DriveCondition &other)
const {
97 return conditionAndMode != other.conditionAndMode;
106 typedef PointerIntPair<Value, 2> ConditionAndMode;
107 ConditionAndMode conditionAndMode;
109 DriveCondition(ConditionAndMode conditionAndMode)
110 : conditionAndMode(conditionAndMode) {}
111 friend DenseMapInfo<DriveCondition>;
123 DriveCondition condition;
124 bool valueIsPlaceholder =
false;
125 bool conditionIsPlaceholder =
false;
127 Def(Value value, DriveCondition condition)
128 : block(value.getParentBlock()), type(value.getType()), value(value),
129 condition(condition) {}
130 Def(Block *block, Type type, DriveCondition condition)
131 : block(block), type(type), condition(condition) {}
133 Value getValueOrPlaceholder();
134 Value getConditionOrPlaceholder();
140Value Def::getValueOrPlaceholder() {
142 auto builder = OpBuilder::atBlockBegin(block);
144 .create<UnrealizedConversionCastOp>(builder.getUnknownLoc(),
147 valueIsPlaceholder =
true;
156Value Def::getConditionOrPlaceholder() {
157 if (!condition.getCondition()) {
158 auto builder = OpBuilder::atBlockBegin(block);
160 if (condition.isNever()) {
162 builder.getI1Type(), 0);
163 }
else if (condition.isAlways()) {
165 builder.getI1Type(), 1);
168 .
create<UnrealizedConversionCastOp>(builder.getUnknownLoc(),
172 conditionIsPlaceholder =
true;
174 condition.setCondition(value);
176 return condition.getCondition();
192 static bool isEqual(DriveCondition lhs, DriveCondition rhs) {
192 static bool isEqual(DriveCondition lhs, DriveCondition rhs) {
…}
210 return cast<hw::InOutType>(slot.getPointer().getType()).getElementType();
222 LatticeNode *nodeBefore =
nullptr;
223 LatticeNode *nodeAfter =
nullptr;
224 SmallDenseSet<Value, 1> neededDefs;
229 enum class Kind { BlockEntry, BlockExit, Probe, Drive };
231 LatticeNode(Kind kind) : kind(kind) {}
234struct BlockEntry :
public LatticeNode {
236 LatticeValue *valueAfter;
237 SmallVector<BlockExit *, 2> predecessors;
238 SmallVector<std::pair<Value, Def *>, 0> insertedProbes;
241 BlockEntry(Block *block, LatticeValue *valueAfter)
242 : LatticeNode(Kind::BlockEntry), block(block), valueAfter(valueAfter) {
243 assert(!valueAfter->nodeBefore);
244 valueAfter->nodeBefore =
this;
247 static bool classof(
const LatticeNode *n) {
248 return n->kind == Kind::BlockEntry;
252struct BlockExit :
public LatticeNode {
254 LatticeValue *valueBefore;
255 SmallVector<BlockEntry *, 2> successors;
256 Operation *terminator;
259 BlockExit(Block *block, LatticeValue *valueBefore)
260 : LatticeNode(Kind::BlockExit), block(block), valueBefore(valueBefore),
261 terminator(block->getTerminator()),
262 suspends(isa<HaltOp, WaitOp>(terminator)) {
263 assert(!valueBefore->nodeAfter);
264 valueBefore->nodeAfter =
this;
267 static bool classof(
const LatticeNode *n) {
268 return n->kind == Kind::BlockExit;
272struct OpNode :
public LatticeNode {
274 LatticeValue *valueBefore;
275 LatticeValue *valueAfter;
277 OpNode(Kind kind, Operation *op, LatticeValue *valueBefore,
278 LatticeValue *valueAfter)
279 : LatticeNode(kind), op(op), valueBefore(valueBefore),
280 valueAfter(valueAfter) {
281 assert(!valueBefore->nodeAfter);
282 assert(!valueAfter->nodeBefore);
283 valueBefore->nodeAfter =
this;
284 valueAfter->nodeBefore =
this;
287 static bool classof(
const LatticeNode *n) {
288 return isa<ProbeNode, DriveNode>(n);
292struct ProbeNode :
public OpNode {
295 ProbeNode(PrbOp op, LatticeValue *valueBefore, LatticeValue *valueAfter)
296 : OpNode(Kind::Probe, op, valueBefore, valueAfter), slot(op.getSignal()) {
299 static bool classof(
const LatticeNode *n) {
return n->kind == Kind::Probe; }
302struct DriveNode :
public OpNode {
306 DriveNode(DrvOp op, Def *def, LatticeValue *valueBefore,
307 LatticeValue *valueAfter)
308 : OpNode(Kind::Drive, op, valueBefore, valueAfter),
315 static bool classof(
const LatticeNode *n) {
return n->kind == Kind::Drive; }
322 LatticeValue *createValue() {
323 auto *value =
new (valueAllocator.Allocate()) LatticeValue();
324 values.push_back(value);
329 template <
class T,
typename... Args>
330 T *createNode(Args... args) {
332 new (getAllocator<T>().Allocate()) T(std::forward<Args>(args)...);
333 nodes.push_back(node);
338 template <
typename... Args>
339 Def *createDef(Args... args) {
340 auto *def =
new (defAllocator.Allocate()) Def(std::forward<Args>(args)...);
346 Def *createDef(Value value, DriveCondition mode) {
347 auto &slot = defsForValues[{value, mode}];
349 slot =
new (defAllocator.Allocate()) Def(value, mode);
350 defs.push_back(slot);
356 void dump(llvm::raw_ostream &os = llvm::dbgs());
360 std::vector<LatticeNode *> nodes;
362 std::vector<LatticeValue *> values;
364 std::vector<Def *> defs;
368 DenseMap<std::pair<Value, DriveCondition>, Def *> defsForValues;
371 SpecificBumpPtrAllocator<LatticeValue> valueAllocator;
372 SpecificBumpPtrAllocator<Def> defAllocator;
373 SpecificBumpPtrAllocator<BlockEntry> blockEntryAllocator;
374 SpecificBumpPtrAllocator<BlockExit> blockExitAllocator;
375 SpecificBumpPtrAllocator<ProbeNode> probeAllocator;
376 SpecificBumpPtrAllocator<DriveNode> driveAllocator;
380 SpecificBumpPtrAllocator<T> &getAllocator();
386SpecificBumpPtrAllocator<BlockEntry> &Lattice::getAllocator() {
387 return blockEntryAllocator;
390SpecificBumpPtrAllocator<BlockExit> &Lattice::getAllocator() {
391 return blockExitAllocator;
394SpecificBumpPtrAllocator<ProbeNode> &Lattice::getAllocator() {
395 return probeAllocator;
398SpecificBumpPtrAllocator<DriveNode> &Lattice::getAllocator() {
399 return driveAllocator;
406void Lattice::dump(llvm::raw_ostream &os) {
408 llvm::MapVector<Block *, unsigned> blockNames;
409 llvm::MapVector<Value, unsigned> memNames;
410 llvm::MapVector<Def *, unsigned> defNames;
412 auto blockName = [&](
Block *block) {
413 unsigned id = blockNames.insert({block, blockNames.size()}).first->second;
414 return std::string(
"bb") + llvm::utostr(
id);
417 auto memName = [&](
DefSlot value) {
419 memNames.insert({
getSlot(value), memNames.size()}).first->second;
420 return std::string(
"mem") + llvm::utostr(
id) +
424 auto defName = [&](Def *def) {
425 unsigned id = defNames.insert({def, defNames.size()}).first->second;
426 return std::string(
"def") + llvm::utostr(
id);
430 for (
auto *node : nodes)
431 if (auto *entry = dyn_cast<BlockEntry>(node))
432 blockName(entry->block);
436 for (
auto *node : nodes) {
437 auto *entry = dyn_cast<BlockEntry>(node);
442 os <<
" " << blockName(entry->block) <<
":";
443 if (entry->predecessors.empty()) {
444 os <<
" // no predecessors";
447 for (
auto *node : entry->predecessors)
448 os <<
" " << blockName(node->block);
453 auto *value = entry->valueAfter;
456 if (!value->neededDefs.empty()) {
458 for (
auto mem : value->neededDefs)
462 if (!value->reachingDefs.empty()) {
464 for (
auto [mem, def] : value->reachingDefs) {
465 os <<
" " << memName(mem) <<
"=" << defName(def);
466 if (def->condition.isNever())
468 else if (def->condition.isAlways())
475 if (isa<BlockExit>(value->nodeAfter))
479 if (
auto *node = dyn_cast<ProbeNode>(value->nodeAfter))
480 os <<
" probe " << memName(
blockingSlot(node->slot)) <<
"\n";
481 else if (
auto *node = dyn_cast<DriveNode>(value->nodeAfter))
482 os <<
" drive " << memName(node->slot) <<
"\n";
487 value = cast<OpNode>(value->nodeAfter)->valueAfter;
491 auto *exit = cast<BlockExit>(value->nodeAfter);
492 if (isa<WaitOp>(exit->terminator))
494 else if (exit->successors.empty())
498 for (
auto *node : exit->successors)
499 os <<
" " << blockName(node->block);
501 os <<
" // suspends";
506 for (
auto [mem,
id] : memNames)
507 os <<
" mem" << id <<
": " << mem <<
"\n";
520 Promoter(Region ®ion) : region(region) {}
521 LogicalResult promote();
523 void findPromotableSlots();
525 void captureAcrossWait();
526 void captureAcrossWait(PrbOp probeOp, ArrayRef<WaitOp> waitOps,
527 Liveness &liveness, DominanceInfo &dominance);
529 void constructLattice();
530 void propagateBackward();
531 void propagateBackward(LatticeNode *node);
532 void propagateForward(
bool optimisticMerges);
533 void propagateForward(LatticeNode *node,
bool optimisticMerges);
534 void markDirty(LatticeNode *node);
536 void insertProbeBlocks();
538 void insertProbes(BlockEntry *node);
540 void insertDriveBlocks();
542 void insertDrives(BlockExit *node);
543 void insertDrives(DriveNode *node);
545 void resolveDefinitions();
546 void resolveDefinitions(ProbeNode *node);
548 void insertBlockArgs();
549 bool insertBlockArgs(BlockEntry *node);
550 void replaceValueWith(Value oldValue, Value newValue);
558 SmallVector<Value> slots;
566 SmallPtrSet<LatticeNode *, 4> dirtyNodes;
570LogicalResult Promoter::promote() {
574 findPromotableSlots();
582 llvm::dbgs() <<
"Initial lattice:\n";
589 llvm::dbgs() <<
"After backward propagation:\n";
597 llvm::dbgs() <<
"After probe insertion:\n";
602 propagateForward(
true);
603 propagateForward(
false);
605 llvm::dbgs() <<
"After forward propagation:\n";
610 resolveDefinitions();
616 llvm::dbgs() <<
"After def resolution and drive insertion:\n";
627void Promoter::findPromotableSlots() {
628 SmallPtrSet<Value, 8> seenSlots;
629 region.walk([&](Operation *op) {
630 for (
auto operand : op->getOperands()) {
631 if (!seenSlots.insert(operand).second)
635 if (!operand.getDefiningOp<llhd::SignalOp>())
637 if (!llvm::all_of(operand.getUsers(), [&](
auto *user) {
639 if (region.isProperAncestor(user->getParentRegion()))
642 if (user->getParentRegion() != ®ion)
644 return isa<PrbOp>(user) || isBlockingDrive(user) ||
649 slotOrder.insert({operand, slots.size()});
650 slots.push_back(operand);
654 LLVM_DEBUG(llvm::dbgs() <<
"Found " << slots.size() <<
" promotable slots\n");
661void Promoter::captureAcrossWait() {
662 if (region.hasOneBlock())
665 SmallVector<WaitOp> waitOps;
666 for (
auto &block : region)
667 if (auto waitOp = dyn_cast<WaitOp>(block.getTerminator()))
668 waitOps.push_back(waitOp);
670 DominanceInfo dominance(region.getParentOp());
671 Liveness liveness(region.getParentOp());
673 SmallVector<WaitOp> crossingWaitOps;
674 for (
auto &block : region) {
675 for (
auto probeOp : block.getOps<PrbOp>()) {
676 for (
auto waitOp : waitOps)
677 if (liveness.getLiveness(waitOp->getBlock())->
isLiveOut(probeOp))
678 crossingWaitOps.push_back(waitOp);
679 if (!crossingWaitOps.empty()) {
680 captureAcrossWait(probeOp, crossingWaitOps, liveness, dominance);
681 crossingWaitOps.clear();
691void Promoter::captureAcrossWait(PrbOp probeOp, ArrayRef<WaitOp> waitOps,
692 Liveness &liveness, DominanceInfo &dominance) {
694 llvm::dbgs() <<
"Capture " << probeOp <<
"\n";
695 for (
auto waitOp : waitOps)
696 llvm::dbgs() <<
"- Across " << waitOp <<
"\n";
701 auto &domTree = dominance.getDomTree(®ion);
702 llvm::IDFCalculatorBase<Block, false> idfCalculator(domTree);
706 SmallPtrSet<Block *, 4> definingBlocks;
707 definingBlocks.insert(probeOp->getBlock());
708 for (
auto waitOp : waitOps)
709 definingBlocks.insert(waitOp.getDest());
710 idfCalculator.setDefiningBlocks(definingBlocks);
713 SmallPtrSet<Block *, 16> liveInBlocks;
714 for (
auto &block : region)
715 if (liveness.getLiveness(&block)->isLiveIn(probeOp))
716 liveInBlocks.insert(&block);
717 idfCalculator.setLiveInBlocks(liveInBlocks);
721 SmallVector<Block *> mergePointsVec;
722 idfCalculator.calculate(mergePointsVec);
723 SmallPtrSet<Block *, 16> mergePoints(mergePointsVec.begin(),
724 mergePointsVec.end());
725 for (
auto waitOp : waitOps)
726 mergePoints.insert(waitOp.getDest());
727 LLVM_DEBUG(llvm::dbgs() <<
"- " << mergePoints.size() <<
" merge points\n");
732 struct WorklistItem {
733 DominanceInfoNode *domNode;
736 SmallVector<WorklistItem> worklist;
737 worklist.push_back({domTree.getNode(probeOp->getBlock()), probeOp});
739 while (!worklist.empty()) {
740 auto item = worklist.pop_back_val();
741 auto *block = item.domNode->getBlock();
744 if (mergePoints.contains(block))
746 block->addArgument(probeOp.getType(), probeOp.getLoc());
750 for (
auto &op : *block)
751 op.replaceUsesOfWith(probeOp, item.reachingDef);
755 if (
auto branchOp = dyn_cast<BranchOpInterface>(block->getTerminator())) {
756 for (
auto &blockOperand : branchOp->getBlockOperands())
757 if (mergePoints.contains(blockOperand.
get()))
758 branchOp.getSuccessorOperands(blockOperand.getOperandNumber())
759 .
append(item.reachingDef);
760 }
else if (
auto waitOp = dyn_cast<WaitOp>(block->getTerminator())) {
761 if (mergePoints.contains(waitOp.getDest()))
762 waitOp.getDestOperandsMutable().append(item.reachingDef);
765 for (
auto *child : item.domNode->children())
766 worklist.push_back({child, item.reachingDef});
776void Promoter::constructLattice() {
779 for (
auto &block : region) {
780 auto *entry = lattice.createNode<BlockEntry>(&block, lattice.createValue());
781 blockEntries.insert({&block, entry});
785 for (
auto &block : region) {
786 auto *valueBefore = blockEntries.lookup(&block)->valueAfter;
789 for (
auto &op : block.without_terminator()) {
791 if (
auto probeOp = dyn_cast<PrbOp>(op)) {
792 if (!slotOrder.contains(probeOp.getSignal()))
794 auto *node = lattice.createNode<ProbeNode>(probeOp, valueBefore,
795 lattice.createValue());
796 valueBefore = node->valueAfter;
801 if (
auto driveOp = dyn_cast<DrvOp>(op)) {
804 if (!slotOrder.contains(driveOp.getSignal()))
806 auto condition = DriveCondition::always();
807 if (
auto enable = driveOp.getEnable())
808 condition = DriveCondition::conditional(enable);
809 auto *def = lattice.createDef(driveOp.getValue(), condition);
810 auto *node = lattice.createNode<DriveNode>(driveOp, def, valueBefore,
811 lattice.createValue());
812 valueBefore = node->valueAfter;
818 auto *exit = lattice.createNode<BlockExit>(&block, valueBefore);
819 for (
auto *otherBlock : exit->terminator->getSuccessors()) {
820 auto *otherEntry = blockEntries.lookup(otherBlock);
821 exit->successors.push_back(otherEntry);
822 otherEntry->predecessors.push_back(exit);
829void Promoter::propagateBackward() {
830 for (
auto *node : lattice.nodes)
831 propagateBackward(node);
832 while (!dirtyNodes.empty()) {
833 auto *node = *dirtyNodes.begin();
834 dirtyNodes.erase(node);
835 propagateBackward(node);
841void Promoter::propagateBackward(LatticeNode *node) {
842 auto update = [&](LatticeValue *value,
auto &neededDefs) {
843 if (value->neededDefs != neededDefs) {
844 value->neededDefs = neededDefs;
845 markDirty(value->nodeBefore);
850 if (
auto *probe = dyn_cast<ProbeNode>(node)) {
851 auto needed = probe->valueAfter->neededDefs;
852 needed.insert(probe->slot);
853 update(probe->valueBefore, needed);
859 if (
auto *drive = dyn_cast<DriveNode>(node)) {
860 auto needed = drive->valueAfter->neededDefs;
862 needed.erase(
getSlot(drive->slot));
863 update(drive->valueBefore, needed);
868 if (
auto *entry = dyn_cast<BlockEntry>(node)) {
869 for (
auto *predecessor : entry->predecessors)
870 markDirty(predecessor);
875 if (
auto *exit = dyn_cast<BlockExit>(node)) {
878 SmallDenseSet<Value, 1> needed;
879 for (
auto *successors : exit->successors)
880 needed.insert(successors->valueAfter->neededDefs.begin(),
881 successors->valueAfter->neededDefs.
end());
882 update(exit->valueBefore, needed);
886 assert(
false &&
"unhandled node in backward propagation");
897void Promoter::propagateForward(
bool optimisticMerges) {
898 for (
auto *node : lattice.nodes)
899 propagateForward(node, optimisticMerges);
900 while (!dirtyNodes.empty()) {
901 auto *node = *dirtyNodes.begin();
902 dirtyNodes.erase(node);
903 propagateForward(node, optimisticMerges);
908void Promoter::propagateForward(LatticeNode *node,
bool optimisticMerges) {
909 auto update = [&](LatticeValue *value,
auto &reachingDefs) {
910 if (value->reachingDefs != reachingDefs) {
911 value->reachingDefs = reachingDefs;
912 markDirty(value->nodeAfter);
917 if (
auto *probe = dyn_cast<ProbeNode>(node)) {
918 update(probe->valueAfter, probe->valueBefore->reachingDefs);
932 if (
auto *drive = dyn_cast<DriveNode>(node)) {
933 auto reaching = drive->valueBefore->reachingDefs;
934 reaching[drive->slot] = drive->def;
937 update(drive->valueAfter, reaching);
943 if (
auto *entry = dyn_cast<BlockEntry>(node)) {
946 for (
auto [slot, insertedProbe] : entry->insertedProbes)
952 for (
auto *predecessor : entry->predecessors)
953 if (!predecessor->suspends)
954 reachingDefs.insert(predecessor->valueBefore->reachingDefs.begin(),
955 predecessor->valueBefore->reachingDefs.
end());
957 for (
auto pair : reachingDefs) {
959 Def *reachingDef = pair.second;
960 DriveCondition reachingDefCondition = reachingDef->condition;
963 if (reaching.contains(slot))
973 if (llvm::any_of(entry->predecessors, [&](
auto *predecessor) {
974 return predecessor->suspends;
977 for (
auto *predecessor : entry->predecessors) {
978 auto otherDef = predecessor->valueBefore->reachingDefs.lookup(slot);
979 if (!otherDef && optimisticMerges)
983 if (reachingDef != otherDef)
984 reachingDef =
nullptr;
988 otherDef ? otherDef->condition : DriveCondition::never();
989 if (reachingDefCondition != condition)
990 reachingDefCondition = DriveCondition::conditional();
996 reachingDef = entry->mergedDefs.lookup(slot);
998 reachingDef = lattice.createDef(entry->block,
getStoredType(slot),
999 reachingDefCondition);
1000 entry->mergedDefs.insert({slot, reachingDef});
1002 reachingDef->condition = reachingDefCondition;
1004 reaching.insert({slot, reachingDef});
1007 update(entry->valueAfter, reaching);
1012 if (
auto *exit = dyn_cast<BlockExit>(node)) {
1013 for (
auto *successor : exit->successors)
1014 markDirty(successor);
1018 assert(
false &&
"unhandled node in forward propagation");
1022void Promoter::markDirty(LatticeNode *node) {
1024 dirtyNodes.insert(node);
1035void Promoter::insertProbeBlocks() {
1039 SmallDenseSet<std::pair<BlockExit *, BlockEntry *>, 1> worklist;
1040 for (
auto *node : lattice.nodes) {
1041 if (
auto *entry = dyn_cast<BlockEntry>(node)) {
1042 SmallVector<Value> partialSlots;
1043 for (
auto slot : entry->valueAfter->neededDefs) {
1044 unsigned numIncoming = 0;
1045 for (
auto *predecessor : entry->predecessors)
1046 if (predecessor->valueBefore->neededDefs.contains(slot))
1048 if (numIncoming != 0 && numIncoming != entry->predecessors.size())
1049 partialSlots.push_back(slot);
1051 for (
auto *predecessor : entry->predecessors)
1052 if (
llvm::any_of(partialSlots, [&](auto slot) {
1053 return !predecessor->valueBefore->neededDefs.contains(slot);
1055 worklist.insert({predecessor, entry});
1060 for (
auto [predecessor, successor] : worklist) {
1061 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting probe block towards " << successor
1062 <<
" after " << *predecessor->terminator <<
"\n");
1063 OpBuilder builder(predecessor->terminator);
1064 auto *newBlock = builder.createBlock(successor->block);
1065 for (
auto oldArg : successor->block->getArguments())
1066 newBlock->addArgument(oldArg.getType(), oldArg.
getLoc());
1067 builder.create<cf::BranchOp>(predecessor->terminator->getLoc(),
1068 successor->block, newBlock->getArguments());
1069 for (
auto &blockOp : predecessor->terminator->getBlockOperands())
1070 if (blockOp.
get() == successor->block)
1071 blockOp.set(newBlock);
1074 auto *value = lattice.createValue();
1075 value->neededDefs = successor->valueAfter->neededDefs;
1076 auto *newEntry = lattice.createNode<BlockEntry>(newBlock, value);
1077 auto *newExit = lattice.createNode<BlockExit>(newBlock, value);
1078 newEntry->predecessors.push_back(predecessor);
1079 newExit->successors.push_back(successor);
1080 llvm::replace(successor->predecessors, predecessor, newExit);
1081 llvm::replace(predecessor->successors, successor, newEntry);
1088void Promoter::insertProbes() {
1089 for (
auto *node : lattice.nodes) {
1090 if (
auto *entry = dyn_cast<BlockEntry>(node))
1091 insertProbes(entry);
1097void Promoter::insertProbes(BlockEntry *node) {
1098 auto builder = OpBuilder::atBlockBegin(node->block);
1099 for (
auto neededDef : slots) {
1100 if (!node->valueAfter->neededDefs.contains(neededDef))
1102 if (!node->predecessors.empty() &&
1103 llvm::all_of(node->predecessors, [&](
auto *predecessor) {
1104 return predecessor->valueBefore->neededDefs.contains(neededDef);
1107 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting probe for " << neededDef
1108 <<
" in block " << node->block <<
"\n");
1109 auto value = builder.create<PrbOp>(neededDef.getLoc(), neededDef);
1110 auto *def = lattice.createDef(value, DriveCondition::never());
1111 node->insertedProbes.push_back({neededDef, def});
1117void Promoter::insertDriveBlocks() {
1121 SmallDenseSet<std::pair<BlockExit *, BlockEntry *>, 1> worklist;
1122 for (
auto *node : lattice.nodes) {
1123 if (
auto *exit = dyn_cast<BlockExit>(node)) {
1124 SmallVector<DefSlot> partialSlots;
1125 for (
auto [slot, reachingDef] : exit->valueBefore->reachingDefs) {
1126 if (reachingDef->condition.isNever())
1128 unsigned numContinues = 0;
1129 for (
auto *successor : exit->successors)
1130 if (successor->valueAfter->reachingDefs.contains(slot))
1132 if (numContinues != 0 && numContinues != exit->successors.size())
1133 partialSlots.push_back(slot);
1135 for (
auto *successor : exit->successors)
1136 if (
llvm::any_of(partialSlots, [&](auto slot) {
1137 return !successor->valueAfter->reachingDefs.contains(slot);
1139 worklist.insert({exit, successor});
1144 for (
auto [predecessor, successor] : worklist) {
1145 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting drive block towards " << successor
1146 <<
" after " << *predecessor->terminator <<
"\n");
1147 OpBuilder builder(predecessor->terminator);
1148 auto *newBlock = builder.createBlock(successor->block);
1149 for (
auto oldArg : successor->block->getArguments())
1150 newBlock->addArgument(oldArg.getType(), oldArg.
getLoc());
1151 builder.create<cf::BranchOp>(predecessor->terminator->getLoc(),
1152 successor->block, newBlock->getArguments());
1153 for (
auto &blockOp : predecessor->terminator->getBlockOperands())
1154 if (blockOp.
get() == successor->block)
1155 blockOp.set(newBlock);
1158 auto *value = lattice.createValue();
1159 value->neededDefs = successor->valueAfter->neededDefs;
1160 value->reachingDefs = predecessor->valueBefore->reachingDefs;
1161 auto *newEntry = lattice.createNode<BlockEntry>(newBlock, value);
1162 auto *newExit = lattice.createNode<BlockExit>(newBlock, value);
1163 newEntry->predecessors.push_back(predecessor);
1164 newExit->successors.push_back(successor);
1165 llvm::replace(successor->predecessors, predecessor, newExit);
1166 llvm::replace(predecessor->successors, successor, newEntry);
1173void Promoter::insertDrives() {
1174 for (
auto *node : lattice.nodes) {
1175 if (
auto *exit = dyn_cast<BlockExit>(node))
1177 else if (
auto *drive = dyn_cast<DriveNode>(node))
1178 insertDrives(drive);
1184void Promoter::insertDrives(BlockExit *node) {
1185 auto builder = OpBuilder::atBlockTerminator(node->block);
1187 ConstantTimeOp epsilonTime;
1188 ConstantTimeOp deltaTime;
1189 auto getTime = [&](
bool delta) {
1192 deltaTime = builder.create<ConstantTimeOp>(node->terminator->getLoc(),
1197 epsilonTime = builder.create<ConstantTimeOp>(node->terminator->getLoc(),
1202 auto insertDriveForSlot = [&](
DefSlot slot) {
1203 auto reachingDef = node->valueBefore->reachingDefs.lookup(slot);
1204 if (!reachingDef || reachingDef->condition.isNever())
1206 if (!node->suspends && !node->successors.empty() &&
1207 llvm::all_of(node->successors, [&](
auto *successor) {
1208 return successor->valueAfter->reachingDefs.contains(slot);
1211 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting drive for " <<
getSlot(slot) <<
" "
1212 << (
isDelayed(slot) ?
"(delayed)" :
"(blocking)")
1213 <<
" before " << *node->terminator <<
"\n");
1215 auto value = reachingDef->getValueOrPlaceholder();
1216 auto enable = reachingDef->condition.isConditional()
1217 ? reachingDef->getConditionOrPlaceholder()
1219 builder.create<DrvOp>(
getLoc(slot),
getSlot(slot), value, time, enable);
1222 for (
auto slot : slots)
1224 for (
auto slot : slots)
1230void Promoter::insertDrives(DriveNode *node) {
1231 if (!slotOrder.contains(
getSlot(node->slot)))
1233 LLVM_DEBUG(llvm::dbgs() <<
"- Removing drive " << *node->op <<
"\n");
1234 auto *delayOp = cast<DrvOp>(node->op).getTime().getDefiningOp();
1237 if (delayOp && isOpTriviallyDead(delayOp))
1246void Promoter::resolveDefinitions() {
1247 for (
auto *node : lattice.nodes)
1248 if (auto *probe = dyn_cast<ProbeNode>(node))
1249 resolveDefinitions(probe);
1253void Promoter::resolveDefinitions(ProbeNode *node) {
1254 if (!slotOrder.contains(node->slot))
1256 auto *def = node->valueBefore->reachingDefs.lookup(
blockingSlot(node->slot));
1257 assert(def &&
"no definition reaches probe");
1258 LLVM_DEBUG(llvm::dbgs() <<
"- Replacing " << *node->op <<
"\n");
1259 replaceValueWith(node->op->getResult(0), def->getValueOrPlaceholder());
1269void Promoter::insertBlockArgs() {
1270 bool anyArgsInserted =
true;
1271 while (anyArgsInserted) {
1272 anyArgsInserted =
false;
1273 for (
auto *node : lattice.nodes)
1274 if (auto *entry = dyn_cast<BlockEntry>(node))
1275 anyArgsInserted |= insertBlockArgs(entry);
1287bool Promoter::insertBlockArgs(BlockEntry *node) {
1293 enum class Which { Value, Condition };
1294 SmallVector<std::pair<DefSlot, Which>> neededSlots;
1295 auto addNeededSlot = [&](
DefSlot slot) {
1296 if (
auto *def = node->mergedDefs.lookup(slot)) {
1297 if (node->valueAfter->reachingDefs.contains(slot)) {
1298 if (def->valueIsPlaceholder)
1299 neededSlots.push_back({slot, Which::Value});
1300 if (def->conditionIsPlaceholder)
1301 neededSlots.push_back({slot, Which::Condition});
1305 for (
auto slot : slots) {
1309 if (neededSlots.empty())
1311 LLVM_DEBUG(llvm::dbgs() <<
"- Adding " << neededSlots.size()
1312 <<
" args to block " << node->block <<
"\n");
1315 for (
auto [slot, which] : neededSlots) {
1316 auto *def = node->mergedDefs.lookup(slot);
1319 case Which::Value: {
1322 auto *placeholder = def->value.getDefiningOp();
1323 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1324 "placeholder replaced but valueIsPlaceholder still set");
1326 replaceValueWith(placeholder->getResult(0), arg);
1327 placeholder->erase();
1329 def->valueIsPlaceholder =
false;
1332 case Which::Condition: {
1336 auto *placeholder = def->condition.getCondition().getDefiningOp();
1337 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1338 "placeholder replaced but conditionIsPlaceholder still set");
1339 auto conditionArg = node->block->addArgument(
1340 IntegerType::get(region.getContext(), 1),
getLoc(slot));
1341 replaceValueWith(placeholder->getResult(0), conditionArg);
1342 placeholder->erase();
1343 def->condition.setCondition(conditionArg);
1344 def->conditionIsPlaceholder =
false;
1351 for (
auto *predecessor : node->predecessors) {
1353 SmallVector<Value> args;
1354 for (
auto [slot, which] : neededSlots) {
1355 auto *def = predecessor->valueBefore->reachingDefs.lookup(slot);
1356 auto builder = OpBuilder::atBlockTerminator(predecessor->block);
1360 args.push_back(def->getValueOrPlaceholder());
1363 auto flatType = builder.getIntegerType(hw::getBitWidth(type));
1366 if (type != flatType)
1368 args.push_back(value);
1371 case Which::Condition:
1373 args.push_back(def->getConditionOrPlaceholder());
1376 getLoc(slot), builder.getI1Type(), 0));
1383 auto branchOp = cast<BranchOpInterface>(predecessor->terminator);
1384 for (
auto &blockOperand : branchOp->getBlockOperands())
1385 if (blockOperand.
get() == node->block)
1386 branchOp.getSuccessorOperands(blockOperand.getOperandNumber())
1395void Promoter::replaceValueWith(Value oldValue, Value newValue) {
1396 oldValue.replaceAllUsesWith(newValue);
1397 for (
auto *def : lattice.defs) {
1398 if (def->value == oldValue)
1399 def->value = newValue;
1400 if (def->condition.isConditional() &&
1401 def->condition.getCondition() == oldValue)
1402 def->condition.setCondition(newValue);
1411struct Mem2RegPass :
public llhd::impl::Mem2RegPassBase<Mem2RegPass> {
1412 void runOnOperation()
override;
1416void Mem2RegPass::runOnOperation() {
1417 SmallVector<Region *> regions;
1418 getOperation()->walk<WalkOrder::PreOrder>([&](Operation *op) {
1419 if (isa<ProcessOp, FinalOp>(op)) {
1420 auto ®ion = op->getRegion(0);
1421 if (!region.empty())
1422 regions.push_back(®ion);
1423 return WalkResult::skip();
1425 return WalkResult::advance();
1427 for (
auto *region : regions)
1428 if (failed(Promoter(*region).promote()))
1429 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 Location getLoc(DefSlot slot)
static bool isDeltaDrive(Operation *op)
Check whether an operation is a llhd.drive with a delta delay.
static bool isDeltaDelay(Value value)
Check whether a value is defined by llhd.constant_time <0ns, 1d, 0e>.
static DefSlot blockingSlot(Value slot)
static DefSlot delayedSlot(Value slot)
static Value getSlot(DefSlot slot)
static Type getStoredType(DefSlot slot)
static bool isBlockingDrive(Operation *op)
Check whether an operation is a llhd.drive with an epsilon delay.
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)
static DriveCondition getEmptyKey()
static DriveCondition getTombstoneKey()
static bool isEqual(DriveCondition lhs, DriveCondition rhs)
static unsigned getHashValue(DriveCondition d)