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) {
598 if (isa<hw::UnionType>(value.getType()))
599 return builder.createOrFold<hw::UnionExtractOp>(
600 op.getLoc(), value, op.getFieldAttr());
602 op.getLoc(), value, op.getFieldAttr());
604 .Case<SigExtractOp>([&](
auto op) {
605 auto type = cast<RefType>(op.getType()).getNestedType();
606 auto width = type.getIntOrFloatBitWidth();
607 return comb::createDynamicExtract(builder, op.getLoc(), value,
608 op.getLowBit(), width);
628 for (
auto projection : projections) {
629 value = TypeSwitch<Operation *, Value>(projection.op)
630 .Case<SigArrayGetOp>([&](
auto op) {
631 return builder.createOrFold<hw::ArrayInjectOp>(
632 op.getLoc(), projection.into, op.getIndex(), value);
634 .Case<SigStructExtractOp>([&](
auto op) {
638 dyn_cast<hw::UnionType>(projection.into.getType()))
639 return builder.createOrFold<hw::UnionCreateOp>(
640 op.getLoc(), unionTy, op.getFieldAttr(), value);
641 return builder.createOrFold<hw::StructInjectOp>(
642 op.getLoc(), projection.into, op.getFieldAttr(), value);
644 .Case<SigExtractOp>([&](
auto op) {
645 return comb::createDynamicInject(builder, op.getLoc(),
647 op.getLowBit(), value);
660 Promoter(Region ®ion) : region(region) {}
661 LogicalResult promote();
663 void findPromotableSlots();
664 Value resolveSlot(Value projectionOrSlot);
666 void captureAcrossWait();
667 void captureAcrossWait(Value value, ArrayRef<WaitOp> waitOps,
668 Liveness &liveness, DominanceInfo &dominance);
670 void constructLattice();
671 void propagateBackward();
672 void propagateBackward(LatticeNode *node);
673 void propagateForward();
674 void propagateForward(
bool optimisticMerges, DominanceInfo &dominance);
675 void propagateForward(LatticeNode *node,
bool optimisticMerges,
676 DominanceInfo &dominance);
677 void markDirty(LatticeNode *node);
679 void insertProbeBlocks();
681 void insertProbes(BlockEntry *node);
683 void insertDriveBlocks();
685 void insertDrives(BlockExit *node);
686 void insertDrives(DriveNode *node);
688 void resolveDefinitions();
689 void resolveDefinitions(ProbeNode *node);
690 void resolveDefinitions(DriveNode *node);
691 void resolveDefinitionValue(DriveNode *node);
692 void resolveDefinitionCondition(DriveNode *node);
694 void insertBlockArgs();
695 bool insertBlockArgs(BlockEntry *node);
696 void replaceValueWith(Value oldValue, Value newValue);
698 void removeUnusedLocalSignals();
699 void removeUnusedLocalSignal(SignalNode *signal);
707 SmallVector<Value> slots;
712 SmallDenseSet<Value> promotable;
718 SmallPtrSet<LatticeNode *, 4> dirtyNodes;
725LogicalResult Promoter::promote() {
729 findPromotableSlots();
741 llvm::dbgs() <<
"Initial lattice:\n";
748 llvm::dbgs() <<
"After backward propagation:\n";
756 llvm::dbgs() <<
"After probe insertion:\n";
763 llvm::dbgs() <<
"After forward propagation:\n";
768 resolveDefinitions();
774 llvm::dbgs() <<
"After def resolution and drive insertion:\n";
782 removeUnusedLocalSignals();
791void Promoter::findPromotableSlots() {
792 SmallPtrSet<Value, 8> seenSlots;
793 SmallPtrSet<Operation *, 8> checkedUsers;
794 SmallVector<Operation *, 8> userWorklist;
796 region.walk([&](Operation *op) {
797 for (
auto operand : op->getOperands()) {
798 if (!seenSlots.insert(operand).second)
804 if (!operand.getDefiningOp<llhd::SignalOp>())
808 bool hasProjection =
false;
809 bool hasBlockingDrive =
false;
810 bool hasDeltaDrive =
false;
811 auto checkUser = [&](Operation *user) ->
bool {
813 if (region.isProperAncestor(user->getParentRegion()))
816 if (user->getParentRegion() != ®ion)
822 if (isa<SigArrayGetOp, SigExtractOp, SigStructExtractOp>(user)) {
823 hasProjection =
true;
824 for (
auto *projectionUser : user->getUsers()) {
825 if (isa<SigArrayGetOp, SigExtractOp, SigStructExtractOp>(
827 projectionUser->getBlock() != user->getBlock())
831 if (checkedUsers.insert(projectionUser).second)
832 userWorklist.push_back(projectionUser);
834 projections.insert({user->getResult(0), operand});
842 checkedUsers.clear();
843 if (!llvm::all_of(operand.getUsers(), [&](
auto *user) {
845 if (checkedUsers.insert(user).second)
846 userWorklist.push_back(user);
847 while (!userWorklist.empty() && allOk)
848 allOk &= checkUser(userWorklist.pop_back_val());
849 userWorklist.clear();
857 if (hasProjection && hasBlockingDrive && hasDeltaDrive)
865 static_cast<unsigned>(bitWidth) > IntegerType::kMaxWidth)
868 slots.push_back(operand);
873 promotable.insert(slots.begin(), slots.end());
874 for (
auto [projection, slot] :
llvm::make_early_inc_range(projections))
875 if (!promotable.contains(slot))
876 projections.erase(projection);
877 for (
auto [projection, slot] : projections)
878 promotable.insert(projection);
880 LLVM_DEBUG(llvm::dbgs() <<
"Found " << slots.size() <<
" promotable slots, "
881 << promotable.size() <<
" promotable values\n");
886Value Promoter::resolveSlot(Value projectionOrSlot) {
887 if (
auto slot = projections.lookup(projectionOrSlot))
889 return projectionOrSlot;
896void Promoter::captureAcrossWait() {
897 if (region.hasOneBlock())
900 SmallVector<WaitOp> waitOps;
901 for (
auto &block : region)
902 if (auto waitOp = dyn_cast<WaitOp>(block.getTerminator()))
903 waitOps.push_back(waitOp);
905 DominanceInfo dominance(region.getParentOp());
906 Liveness liveness(region.getParentOp());
908 llvm::DenseSet<Value> alreadyCaptured;
910 auto isDefinedInRegion = [&](Value v) {
911 return v.getParentRegion() == ®ion;
914 for (
auto waitOp : waitOps) {
915 Block *waitBlock = waitOp->getBlock();
916 const auto &liveOutValues = liveness.getLiveOut(waitBlock);
918 for (Value v : liveOutValues) {
919 if (!isDefinedInRegion(v))
922 if (!alreadyCaptured.insert(v).second)
925 captureAcrossWait(v, waitOps, liveness, dominance);
934void Promoter::captureAcrossWait(Value value, ArrayRef<WaitOp> waitOps,
935 Liveness &liveness, DominanceInfo &dominance) {
937 llvm::dbgs() <<
"Capture " << value <<
"\n";
938 for (
auto waitOp : waitOps)
939 llvm::dbgs() <<
"- Across " << waitOp <<
"\n";
944 auto &domTree = dominance.getDomTree(®ion);
945 llvm::IDFCalculatorBase<Block, false> idfCalculator(domTree);
949 SmallPtrSet<Block *, 4> definingBlocks;
950 definingBlocks.insert(value.getParentBlock());
951 for (
auto waitOp : waitOps)
952 definingBlocks.insert(waitOp.getDest());
953 idfCalculator.setDefiningBlocks(definingBlocks);
956 SmallPtrSet<Block *, 16> liveInBlocks;
957 for (
auto &block : region)
958 if (liveness.getLiveness(&block)->isLiveIn(value))
959 liveInBlocks.insert(&block);
960 idfCalculator.setLiveInBlocks(liveInBlocks);
964 SmallVector<Block *> mergePointsVec;
965 idfCalculator.calculate(mergePointsVec);
966 SmallPtrSet<Block *, 16> mergePoints(mergePointsVec.begin(),
967 mergePointsVec.end());
968 for (
auto waitOp : waitOps)
969 mergePoints.insert(waitOp.getDest());
970 LLVM_DEBUG(llvm::dbgs() <<
"- " << mergePoints.size() <<
" merge points\n");
975 struct WorklistItem {
976 DominanceInfoNode *domNode;
979 SmallVector<WorklistItem> worklist;
980 worklist.push_back({domTree.getNode(value.getParentBlock()), value});
982 while (!worklist.empty()) {
983 auto item = worklist.pop_back_val();
984 auto *block = item.domNode->getBlock();
987 if (mergePoints.contains(block))
988 item.reachingDef = block->addArgument(value.getType(), value.getLoc());
992 for (
auto &op : *block)
993 op.replaceUsesOfWith(value, item.reachingDef);
997 if (
auto branchOp = dyn_cast<BranchOpInterface>(block->getTerminator())) {
998 for (
auto &blockOperand : branchOp->getBlockOperands())
999 if (mergePoints.contains(blockOperand.
get()))
1000 branchOp.getSuccessorOperands(blockOperand.getOperandNumber())
1001 .
append(item.reachingDef);
1002 }
else if (
auto waitOp = dyn_cast<WaitOp>(block->getTerminator())) {
1003 if (mergePoints.contains(waitOp.getDest()))
1004 waitOp.getDestOperandsMutable().append(item.reachingDef);
1007 for (
auto *child : item.domNode->children())
1008 worklist.push_back({child, item.reachingDef});
1018void Promoter::constructLattice() {
1021 for (
auto &block : region) {
1022 auto *entry = lattice.createNode<BlockEntry>(&block, lattice.createValue());
1023 blockEntries.insert({&block, entry});
1027 for (
auto &block : region) {
1028 auto *valueBefore = blockEntries.lookup(&block)->valueAfter;
1031 for (
auto &op : block.without_terminator()) {
1033 if (
auto probeOp = dyn_cast<ProbeOp>(op)) {
1034 if (!promotable.contains(probeOp.getSignal()))
1036 auto *node = lattice.createNode<ProbeNode>(
1037 probeOp, resolveSlot(probeOp.getSignal()), valueBefore,
1038 lattice.createValue());
1039 valueBefore = node->valueAfter;
1044 if (
auto driveOp = dyn_cast<DriveOp>(op)) {
1047 if (!promotable.contains(driveOp.getSignal()))
1049 auto condition = DriveCondition::always();
1050 if (
auto enable = driveOp.getEnable())
1051 condition = DriveCondition::conditional(enable);
1055 auto slot = resolveSlot(driveOp.getSignal());
1056 auto *def = driveOp.getSignal() == slot
1057 ? lattice.createDef(driveOp.getValue(), condition)
1058 : lattice.createDef(driveOp->getBlock(),
1060 auto *node = lattice.createNode<DriveNode>(
1061 driveOp, slot, def, valueBefore, lattice.createValue());
1062 valueBefore = node->valueAfter;
1067 if (
auto signalOp = dyn_cast<SignalOp>(op)) {
1068 if (!promotable.contains(signalOp))
1071 lattice.createDef(signalOp.getInit(), DriveCondition::never());
1072 auto *node = lattice.createNode<SignalNode>(signalOp, def, valueBefore,
1073 lattice.createValue());
1074 valueBefore = node->valueAfter;
1080 auto *exit = lattice.createNode<BlockExit>(&block, valueBefore);
1081 for (
auto *otherBlock : exit->terminator->getSuccessors()) {
1082 auto *otherEntry = blockEntries.lookup(otherBlock);
1083 exit->successors.push_back(otherEntry);
1084 otherEntry->predecessors.push_back(exit);
1091void Promoter::propagateBackward() {
1092 for (
auto *node : lattice.nodes)
1093 propagateBackward(node);
1094 while (!dirtyNodes.empty()) {
1095 auto *node = *dirtyNodes.begin();
1096 dirtyNodes.erase(node);
1097 propagateBackward(node);
1103void Promoter::propagateBackward(LatticeNode *node) {
1104 auto update = [&](LatticeValue *value,
auto &neededDefs) {
1105 if (value->neededDefs != neededDefs) {
1106 value->neededDefs = neededDefs;
1107 markDirty(value->nodeBefore);
1112 if (
auto *probe = dyn_cast<ProbeNode>(node)) {
1113 auto needed = probe->valueAfter->neededDefs;
1114 needed.insert(probe->slot);
1115 update(probe->valueBefore, needed);
1125 if (
auto *drive = dyn_cast<DriveNode>(node)) {
1126 auto needed = drive->valueAfter->neededDefs;
1127 if (drive->drivesProjection())
1128 needed.insert(
getSlot(drive->slot));
1129 else if (!
isDelayed(drive->slot) && !drive->getDriveOp().getEnable())
1130 needed.erase(
getSlot(drive->slot));
1131 update(drive->valueBefore, needed);
1138 if (
auto *signal = dyn_cast<SignalNode>(node)) {
1139 auto needed = signal->valueAfter->neededDefs;
1140 needed.erase(signal->getSignalOp());
1141 update(signal->valueBefore, needed);
1146 if (
auto *entry = dyn_cast<BlockEntry>(node)) {
1147 for (
auto *predecessor : entry->predecessors)
1148 markDirty(predecessor);
1153 if (
auto *exit = dyn_cast<BlockExit>(node)) {
1156 SmallDenseSet<Value, 1> needed;
1157 for (
auto *successors : exit->successors)
1158 needed.insert(successors->valueAfter->neededDefs.begin(),
1159 successors->valueAfter->neededDefs.
end());
1160 update(exit->valueBefore, needed);
1164 assert(
false &&
"unhandled node in backward propagation");
1167void Promoter::propagateForward() {
1168 DominanceInfo dominance(region.getParentOp());
1169 propagateForward(
true, dominance);
1170 propagateForward(
false, dominance);
1181void Promoter::propagateForward(
bool optimisticMerges,
1182 DominanceInfo &dominance) {
1183 for (
auto *node : lattice.nodes)
1184 propagateForward(node, optimisticMerges, dominance);
1185 while (!dirtyNodes.empty()) {
1186 auto *node = *dirtyNodes.begin();
1187 dirtyNodes.erase(node);
1188 propagateForward(node, optimisticMerges, dominance);
1193void Promoter::propagateForward(LatticeNode *node,
bool optimisticMerges,
1194 DominanceInfo &dominance) {
1195 auto update = [&](LatticeValue *value,
auto &reachingDefs) {
1196 if (value->reachingDefs != reachingDefs) {
1197 value->reachingDefs = reachingDefs;
1198 markDirty(value->nodeAfter);
1203 if (
auto *probe = dyn_cast<ProbeNode>(node)) {
1204 update(probe->valueAfter, probe->valueBefore->reachingDefs);
1218 if (
auto *drive = dyn_cast<DriveNode>(node)) {
1219 auto reaching = drive->valueBefore->reachingDefs;
1225 if (drive->drivesProjection() || drive->getDriveOp().getEnable()) {
1226 auto *inDef = reaching.lookup(drive->slot);
1228 if (drive->def->value != inDef->value)
1229 drive->def->value = {};
1230 if (inDef->condition.isAlways() || drive->def->condition.isAlways())
1231 drive->def->condition = DriveCondition::always();
1232 else if (drive->def->condition != inDef->condition)
1233 drive->def->condition = DriveCondition::conditional();
1237 reaching[drive->slot] = drive->def;
1240 update(drive->valueAfter, reaching);
1247 if (
auto *signal = dyn_cast<SignalNode>(node)) {
1248 auto reaching = signal->valueBefore->reachingDefs;
1249 reaching[signal->getSlot()] = signal->def;
1250 reaching.erase(
delayedSlot(signal->getSignalOp()));
1251 update(signal->valueAfter, reaching);
1257 if (
auto *entry = dyn_cast<BlockEntry>(node)) {
1260 for (
auto [slot, insertedProbe] : entry->insertedProbes) {
1268 for (
auto *predecessor : entry->predecessors) {
1269 if (predecessor->suspends)
1273 for (
auto &defEntry : predecessor->valueBefore->reachingDefs) {
1274 auto slot =
getSlot(defEntry.getFirst());
1275 auto *slotBlock = slot.getDefiningOp()->getBlock();
1276 if (!dominance.dominates(slotBlock, entry->block))
1278 reachingDefs.insert(defEntry);
1282 for (
auto pair : reachingDefs) {
1284 Def *reachingDef = pair.second;
1285 DriveCondition reachingDefCondition = reachingDef->condition;
1288 if (reaching.contains(slot))
1298 if (llvm::any_of(entry->predecessors, [&](
auto *predecessor) {
1299 return predecessor->suspends;
1303 for (
auto *predecessor : entry->predecessors) {
1304 auto otherDef = predecessor->valueBefore->reachingDefs.lookup(slot);
1305 if (!otherDef && optimisticMerges)
1309 if (reachingDef != otherDef)
1310 reachingDef =
nullptr;
1314 otherDef ? otherDef->condition : DriveCondition::never();
1315 if (reachingDefCondition != condition)
1316 reachingDefCondition = DriveCondition::conditional();
1322 reachingDef = entry->mergedDefs.lookup(slot);
1324 reachingDef = lattice.createDef(entry->block,
getStoredType(slot),
1325 reachingDefCondition);
1326 entry->mergedDefs.insert({slot, reachingDef});
1328 reachingDef->condition = reachingDefCondition;
1330 reaching.insert({slot, reachingDef});
1333 update(entry->valueAfter, reaching);
1338 if (
auto *exit = dyn_cast<BlockExit>(node)) {
1339 for (
auto *successor : exit->successors)
1340 markDirty(successor);
1344 assert(
false &&
"unhandled node in forward propagation");
1348void Promoter::markDirty(LatticeNode *node) {
1350 dirtyNodes.insert(node);
1361void Promoter::insertProbeBlocks() {
1365 SmallDenseSet<std::pair<BlockExit *, BlockEntry *>, 1> worklist;
1366 for (
auto *node : lattice.nodes) {
1367 if (
auto *entry = dyn_cast<BlockEntry>(node)) {
1368 SmallVector<Value> partialSlots;
1369 for (
auto slot : entry->valueAfter->neededDefs) {
1370 unsigned numIncoming = 0;
1371 for (
auto *predecessor : entry->predecessors)
1372 if (predecessor->valueBefore->neededDefs.contains(slot))
1374 if (numIncoming != 0 && numIncoming != entry->predecessors.size())
1375 partialSlots.push_back(slot);
1377 for (
auto *predecessor : entry->predecessors)
1378 if (
llvm::any_of(partialSlots, [&](auto slot) {
1379 return !predecessor->valueBefore->neededDefs.contains(slot);
1381 worklist.insert({predecessor, entry});
1386 for (
auto [predecessor, successor] : worklist) {
1387 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting probe block towards " << successor
1388 <<
" after " << *predecessor->terminator <<
"\n");
1389 OpBuilder builder(predecessor->terminator);
1390 auto *newBlock = builder.createBlock(successor->block);
1391 for (
auto oldArg : successor->block->getArguments())
1392 newBlock->addArgument(oldArg.getType(), oldArg.
getLoc());
1393 cf::BranchOp::create(builder, predecessor->terminator->getLoc(),
1394 successor->block, newBlock->getArguments());
1395 for (
auto &blockOp : predecessor->terminator->getBlockOperands())
1396 if (blockOp.
get() == successor->block)
1397 blockOp.set(newBlock);
1400 auto *value = lattice.createValue();
1401 value->neededDefs = successor->valueAfter->neededDefs;
1402 auto *newEntry = lattice.createNode<BlockEntry>(newBlock, value);
1403 auto *newExit = lattice.createNode<BlockExit>(newBlock, value);
1404 newEntry->predecessors.push_back(predecessor);
1405 newExit->successors.push_back(successor);
1406 llvm::replace(successor->predecessors, predecessor, newExit);
1407 llvm::replace(predecessor->successors, successor, newEntry);
1414void Promoter::insertProbes() {
1415 for (
auto *node : lattice.nodes) {
1416 if (
auto *entry = dyn_cast<BlockEntry>(node))
1417 insertProbes(entry);
1423void Promoter::insertProbes(BlockEntry *node) {
1424 auto builder = OpBuilder::atBlockBegin(node->block);
1425 for (
auto neededDef : slots) {
1426 if (!node->valueAfter->neededDefs.contains(neededDef))
1428 if (!node->predecessors.empty() &&
1429 llvm::all_of(node->predecessors, [&](
auto *predecessor) {
1430 return predecessor->valueBefore->neededDefs.contains(neededDef);
1433 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting probe for " << neededDef
1434 <<
" in block " << node->block <<
"\n");
1435 OpBuilder::InsertionGuard guard(builder);
1437 if (Operation *op = neededDef.getDefiningOp()) {
1438 if (op->getBlock() == node->block)
1439 builder.setInsertionPointAfterValue(neededDef);
1441 auto value = ProbeOp::create(builder, neededDef.getLoc(), neededDef);
1442 auto *def = lattice.createDef(value, DriveCondition::never());
1443 node->insertedProbes.push_back({neededDef, def});
1449void Promoter::insertDriveBlocks() {
1453 SmallDenseSet<std::pair<BlockExit *, BlockEntry *>, 1> worklist;
1454 for (
auto *node : lattice.nodes) {
1455 if (
auto *exit = dyn_cast<BlockExit>(node)) {
1456 SmallVector<DefSlot> partialSlots;
1457 for (
auto [slot, reachingDef] : exit->valueBefore->reachingDefs) {
1458 if (reachingDef->condition.isNever())
1460 unsigned numContinues = 0;
1461 for (
auto *successor : exit->successors)
1462 if (successor->valueAfter->reachingDefs.contains(slot))
1464 if (numContinues != 0 && numContinues != exit->successors.size())
1465 partialSlots.push_back(slot);
1467 for (
auto *successor : exit->successors)
1468 if (
llvm::any_of(partialSlots, [&](auto slot) {
1469 return !successor->valueAfter->reachingDefs.contains(slot);
1471 worklist.insert({exit, successor});
1476 for (
auto [predecessor, successor] : worklist) {
1477 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting drive block towards " << successor
1478 <<
" after " << *predecessor->terminator <<
"\n");
1479 OpBuilder builder(predecessor->terminator);
1480 auto *newBlock = builder.createBlock(successor->block);
1481 for (
auto oldArg : successor->block->getArguments())
1482 newBlock->addArgument(oldArg.getType(), oldArg.
getLoc());
1483 cf::BranchOp::create(builder, predecessor->terminator->getLoc(),
1484 successor->block, newBlock->getArguments());
1485 for (
auto &blockOp : predecessor->terminator->getBlockOperands())
1486 if (blockOp.
get() == successor->block)
1487 blockOp.set(newBlock);
1490 auto *value = lattice.createValue();
1491 value->neededDefs = successor->valueAfter->neededDefs;
1492 value->reachingDefs = predecessor->valueBefore->reachingDefs;
1493 auto *newEntry = lattice.createNode<BlockEntry>(newBlock, value);
1494 auto *newExit = lattice.createNode<BlockExit>(newBlock, value);
1495 newEntry->predecessors.push_back(predecessor);
1496 newExit->successors.push_back(successor);
1497 llvm::replace(successor->predecessors, predecessor, newExit);
1498 llvm::replace(predecessor->successors, successor, newEntry);
1505void Promoter::insertDrives() {
1506 for (
auto *node : lattice.nodes) {
1507 if (
auto *exit = dyn_cast<BlockExit>(node))
1509 else if (
auto *drive = dyn_cast<DriveNode>(node))
1510 insertDrives(drive);
1516void Promoter::insertDrives(BlockExit *node) {
1517 auto builder = OpBuilder::atBlockTerminator(node->block);
1519 ConstantTimeOp epsilonTime;
1520 ConstantTimeOp deltaTime;
1521 auto getTime = [&](
bool delta) {
1524 deltaTime = ConstantTimeOp::create(builder, node->terminator->getLoc(),
1529 epsilonTime = ConstantTimeOp::create(builder, node->terminator->getLoc(),
1534 auto insertDriveForSlot = [&](
DefSlot slot) {
1535 auto reachingDef = node->valueBefore->reachingDefs.lookup(slot);
1536 if (!reachingDef || reachingDef->condition.isNever())
1538 if (!node->suspends && !node->successors.empty() &&
1539 llvm::all_of(node->successors, [&](
auto *successor) {
1540 return successor->valueAfter->reachingDefs.contains(slot);
1543 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting drive for " <<
getSlot(slot) <<
" "
1544 << (
isDelayed(slot) ?
"(delayed)" :
"(blocking)")
1545 <<
" before " << *node->terminator <<
"\n");
1547 auto value = reachingDef->getValueOrPlaceholder();
1548 auto enable = reachingDef->condition.isConditional()
1549 ? reachingDef->getConditionOrPlaceholder()
1551 DriveOp::create(builder,
getLoc(slot),
getSlot(slot), value, time, enable);
1554 for (
auto slot : slots)
1556 for (
auto slot : slots)
1562void Promoter::insertDrives(DriveNode *node) {
1563 if (!promotable.contains(
getSlot(node->slot)))
1565 LLVM_DEBUG(llvm::dbgs() <<
"- Removing drive " << *node->op <<
"\n");
1566 pruner.eraseNow(node->op);
1575void Promoter::resolveDefinitions() {
1576 for (
auto *node : lattice.nodes) {
1577 if (
auto *probe = dyn_cast<ProbeNode>(node))
1578 resolveDefinitions(probe);
1579 else if (
auto *drive = dyn_cast<DriveNode>(node))
1580 resolveDefinitions(drive);
1585void Promoter::resolveDefinitions(ProbeNode *node) {
1586 if (!promotable.contains(node->slot))
1588 auto *def = node->valueBefore->reachingDefs.lookup(
blockingSlot(node->slot));
1589 assert(def &&
"no definition reaches probe");
1592 auto projections =
getProjections(node->op->getOperand(0), node->slot);
1595 auto builder = OpBuilder(node->op);
1596 auto value = def->getValueOrPlaceholder();
1600 LLVM_DEBUG(llvm::dbgs() <<
"- Replacing " << *node->op <<
" with " << value
1602 replaceValueWith(node->op->getResult(0), value);
1603 pruner.eraseNow(node->op);
1609void Promoter::resolveDefinitions(DriveNode *node) {
1610 if (!promotable.contains(
getSlot(node->slot)))
1617 if (!node->def->value || node->def->valueIsPlaceholder)
1618 resolveDefinitionValue(node);
1619 if (node->def->condition.isConditional() &&
1620 (!node->def->condition.getCondition() ||
1621 node->def->conditionIsPlaceholder))
1622 resolveDefinitionCondition(node);
1625void Promoter::resolveDefinitionValue(DriveNode *node) {
1628 auto *inDef = node->valueBefore->reachingDefs.lookup(node->slot);
1629 assert(inDef &&
"no definition reaches drive");
1630 auto driveOp = node->getDriveOp();
1631 LLVM_DEBUG(llvm::dbgs() <<
"- Injecting value for " << driveOp <<
"\n");
1637 auto builder = OpBuilder(driveOp);
1638 auto value = inDef->getValueOrPlaceholder();
1642 pruner.eraseLaterIfUnused(value);
1643 if (!driveOp.getEnable())
1644 value = driveOp.getValue();
1647 driveOp.getLoc(), driveOp.getEnable(), driveOp.getValue(), value);
1653 if (node->def->valueIsPlaceholder) {
1654 auto *placeholder = node->def->value.getDefiningOp();
1655 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1656 "placeholder replaced but valueIsPlaceholder still set");
1657 replaceValueWith(placeholder->getResult(0), value);
1658 pruner.eraseNow(placeholder);
1659 node->def->valueIsPlaceholder =
false;
1661 node->def->value = value;
1664void Promoter::resolveDefinitionCondition(DriveNode *node) {
1667 auto *inDef = node->valueBefore->reachingDefs.lookup(node->slot);
1668 assert(inDef &&
"no definition reaches drive");
1669 auto driveOp = node->getDriveOp();
1670 LLVM_DEBUG(llvm::dbgs() <<
"- Mutating condition for " << driveOp <<
"\n");
1671 OpBuilder builder(driveOp);
1676 if (inDef->condition.isNever())
1677 condition = driveOp.getEnable();
1680 builder.createOrFold<
comb::OrOp>(driveOp.getLoc(), driveOp.getEnable(),
1681 inDef->getConditionOrPlaceholder());
1684 if (node->def->conditionIsPlaceholder) {
1685 auto *placeholder = node->def->condition.getCondition().getDefiningOp();
1686 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1687 "placeholder replaced but conditionIsPlaceholder still set");
1688 replaceValueWith(placeholder->getResult(0), condition);
1689 pruner.eraseNow(placeholder);
1690 node->def->conditionIsPlaceholder =
false;
1692 node->def->condition.setCondition(condition);
1700void Promoter::insertBlockArgs() {
1701 bool anyArgsInserted =
true;
1702 while (anyArgsInserted) {
1703 anyArgsInserted =
false;
1704 for (
auto *node : lattice.nodes)
1705 if (auto *entry = dyn_cast<BlockEntry>(node))
1706 anyArgsInserted |= insertBlockArgs(entry);
1718bool Promoter::insertBlockArgs(BlockEntry *node) {
1724 enum class Which { Value, Condition };
1725 SmallVector<std::pair<DefSlot, Which>> neededSlots;
1726 auto addNeededSlot = [&](
DefSlot slot) {
1727 if (
auto *def = node->mergedDefs.lookup(slot)) {
1728 if (node->valueAfter->reachingDefs.contains(slot)) {
1729 if (def->valueIsPlaceholder)
1730 neededSlots.push_back({slot, Which::Value});
1731 if (def->conditionIsPlaceholder)
1732 neededSlots.push_back({slot, Which::Condition});
1736 for (
auto slot : slots) {
1740 if (neededSlots.empty())
1742 LLVM_DEBUG(llvm::dbgs() <<
"- Adding " << neededSlots.size()
1743 <<
" args to block " << node->block <<
"\n");
1746 for (
auto [slot, which] : neededSlots) {
1747 auto *def = node->mergedDefs.lookup(slot);
1750 case Which::Value: {
1753 auto *placeholder = def->value.getDefiningOp();
1754 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1755 "placeholder replaced but valueIsPlaceholder still set");
1757 replaceValueWith(placeholder->getResult(0), arg);
1758 pruner.eraseNow(placeholder);
1760 def->valueIsPlaceholder =
false;
1763 case Which::Condition: {
1767 auto *placeholder = def->condition.getCondition().getDefiningOp();
1768 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1769 "placeholder replaced but conditionIsPlaceholder still set");
1770 auto conditionArg = node->block->addArgument(
1771 IntegerType::get(region.getContext(), 1),
getLoc(slot));
1772 replaceValueWith(placeholder->getResult(0), conditionArg);
1773 pruner.eraseNow(placeholder);
1774 def->condition.setCondition(conditionArg);
1775 def->conditionIsPlaceholder =
false;
1782 for (
auto *predecessor : node->predecessors) {
1784 SmallVector<Value> args;
1785 for (
auto [slot, which] : neededSlots) {
1786 auto *def = predecessor->valueBefore->reachingDefs.lookup(slot);
1787 auto builder = OpBuilder::atBlockTerminator(predecessor->block);
1791 args.push_back(def->getValueOrPlaceholder());
1794 auto flatType = builder.getIntegerType(hw::getBitWidth(type));
1797 if (type != flatType)
1799 args.push_back(value);
1802 case Which::Condition:
1804 args.push_back(def->getConditionOrPlaceholder());
1807 builder.getI1Type(), 0));
1814 auto branchOp = cast<BranchOpInterface>(predecessor->terminator);
1815 for (
auto &blockOperand : branchOp->getBlockOperands())
1816 if (blockOperand.
get() == node->block)
1817 branchOp.getSuccessorOperands(blockOperand.getOperandNumber())
1826void Promoter::replaceValueWith(Value oldValue, Value newValue) {
1827 oldValue.replaceAllUsesWith(newValue);
1828 for (
auto *def : lattice.defs) {
1829 if (def->value == oldValue)
1830 def->value = newValue;
1831 if (def->condition.isConditional() &&
1832 def->condition.getCondition() == oldValue)
1833 def->condition.setCondition(newValue);
1842void Promoter::removeUnusedLocalSignals() {
1843 for (
auto *node : lattice.nodes)
1844 if (auto *signal = dyn_cast<SignalNode>(node))
1845 removeUnusedLocalSignal(signal);
1851void Promoter::removeUnusedLocalSignal(SignalNode *signal) {
1854 SmallSetVector<Operation *, 8> users;
1855 SmallVector<Operation *> worklist;
1856 worklist.push_back(signal->op);
1857 while (!worklist.empty()) {
1858 auto *op = worklist.pop_back_val();
1859 if (!isa<SignalOp, DriveOp, SigArrayGetOp, SigArraySliceOp, SigExtractOp,
1860 SigStructExtractOp>(op))
1862 for (
auto *user : op->getUsers())
1863 if (users.insert(user))
1864 worklist.push_back(user);
1869 LLVM_DEBUG(llvm::dbgs() <<
"- Removing local signal " << *signal->op <<
"\n");
1870 for (
auto *op :
llvm::reverse(users))
1871 pruner.eraseNow(op);
1879struct Mem2RegPass :
public llhd::impl::Mem2RegPassBase<Mem2RegPass> {
1880 void runOnOperation()
override;
1884void Mem2RegPass::runOnOperation() {
1885 SmallVector<Region *> regions;
1886 getOperation()->walk<WalkOrder::PreOrder>([&](Operation *op) {
1887 if (isa<ProcessOp, FinalOp, CombinationalOp>(op)) {
1888 auto ®ion = op->getRegion(0);
1889 if (!region.empty())
1890 regions.push_back(®ion);
1891 return WalkResult::skip();
1893 return WalkResult::advance();
1895 for (
auto *region : regions)
1896 if (failed(Promoter(*region).promote()))
1897 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)