15#include "mlir/Analysis/Liveness.h"
16#include "mlir/Dialect/Arith/IR/Arith.h"
17#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
18#include "mlir/IR/Dominance.h"
19#include "llvm/ADT/ArrayRef.h"
20#include "llvm/Support/Debug.h"
21#include "llvm/Support/GenericIteratedDominanceFrontier.h"
23#define DEBUG_TYPE "llhd-mem2reg"
27#define GEN_PASS_DEF_MEM2REGPASS
28#include "circt/Dialect/LLHD/LLHDPasses.h.inc"
36using llvm::PointerIntPair;
37using llvm::SmallDenseSet;
39using llvm::SpecificBumpPtrAllocator;
48 if (
auto driveOp = dyn_cast<DriveOp>(op)) {
49 if (
auto timeOp = driveOp.getTime().getDefiningOp<ConstantTimeOp>()) {
50 auto t = timeOp.getValue();
51 return t.getTime() == 0 && t.getDelta() == 1;
66struct DriveCondition {
67 static DriveCondition never() {
return ConditionAndMode(Value{}, Never); }
68 static DriveCondition always() {
return ConditionAndMode(Value{}, Always); }
69 static DriveCondition conditional(Value condition = {}) {
70 return ConditionAndMode(condition, Conditional);
73 bool isNever()
const {
return conditionAndMode.getInt() == Never; }
74 bool isAlways()
const {
return conditionAndMode.getInt() == Always; }
75 bool isConditional()
const {
76 return conditionAndMode.getInt() == Conditional;
79 Value getCondition()
const {
return conditionAndMode.getPointer(); }
80 void setCondition(Value condition) { conditionAndMode.setPointer(condition); }
82 bool operator==(
const DriveCondition &other)
const {
83 return conditionAndMode == other.conditionAndMode;
85 bool operator!=(
const DriveCondition &other)
const {
86 return conditionAndMode != other.conditionAndMode;
95 typedef PointerIntPair<Value, 2> ConditionAndMode;
96 ConditionAndMode conditionAndMode;
98 DriveCondition(ConditionAndMode conditionAndMode)
99 : conditionAndMode(conditionAndMode) {}
100 friend DenseMapInfo<DriveCondition>;
112 DriveCondition condition;
113 bool valueIsPlaceholder =
false;
114 bool conditionIsPlaceholder =
false;
116 Def(Value value, DriveCondition condition)
117 : block(value.getParentBlock()), type(value.getType()), value(value),
118 condition(condition) {}
119 Def(Block *block, Type type, DriveCondition condition)
120 : block(block), type(type), condition(condition) {}
122 Value getValueOrPlaceholder();
123 Value getConditionOrPlaceholder();
129Value Def::getValueOrPlaceholder() {
131 auto builder = OpBuilder::atBlockBegin(block);
132 value = UnrealizedConversionCastOp::create(builder, builder.getUnknownLoc(),
135 valueIsPlaceholder =
true;
144Value Def::getConditionOrPlaceholder() {
145 if (!condition.getCondition()) {
146 auto builder = OpBuilder::atBlockBegin(block);
148 if (condition.isNever()) {
150 builder.getI1Type(), 0);
151 }
else if (condition.isAlways()) {
153 builder.getI1Type(), 1);
156 UnrealizedConversionCastOp::create(builder, builder.getUnknownLoc(),
157 builder.getI1Type(), ValueRange{})
159 conditionIsPlaceholder =
true;
161 condition.setCondition(value);
163 return condition.getCondition();
173 static bool isEqual(DriveCondition lhs, DriveCondition rhs) {
196 if (
auto intTy = dyn_cast<IntegerType>(type))
197 return intTy.getWidth();
198 if (
auto floatTy = dyn_cast<FloatType>(type))
199 return floatTy.getWidth();
204 return static_cast<unsigned>(bitWidth);
209 return bitWidth && *bitWidth <= IntegerType::kMaxWidth;
213 if (isa<FloatType>(type))
214 return arith::ConstantOp::create(builder, loc, builder.getZeroAttr(type));
217 assert(bitWidth &&
"cannot create zero value for unpromotable slot type");
218 auto flatType = builder.getIntegerType(*bitWidth);
220 if (type != flatType)
241 LatticeNode *nodeBefore =
nullptr;
242 LatticeNode *nodeAfter =
nullptr;
244 Def *blockingReachingDef =
nullptr;
245 Def *delayedReachingDef =
nullptr;
248 Def *getReachingDef(
bool delayed)
const {
249 return delayed ? delayedReachingDef : blockingReachingDef;
251 void setReachingDef(
bool delayed, Def *def) {
252 (delayed ? delayedReachingDef : blockingReachingDef) = def;
257 enum class Kind { BlockEntry, BlockExit, Probe, Drive, Slot };
261 LatticeNode(Kind kind) : kind(kind) {}
264struct BlockEntry :
public LatticeNode {
266 LatticeValue *valueAfter;
267 SmallVector<BlockExit *, 2> predecessors;
271 Def *insertedProbe =
nullptr;
274 Def *blockingMerged =
nullptr;
275 Def *delayedMerged =
nullptr;
277 BlockEntry(Block *block, LatticeValue *valueAfter)
278 : LatticeNode(Kind::BlockEntry), block(block), valueAfter(valueAfter) {
279 assert(!valueAfter->nodeBefore);
280 valueAfter->nodeBefore =
this;
283 Def *getMerged(
bool delayed)
const {
284 return delayed ? delayedMerged : blockingMerged;
286 void setMerged(
bool delayed, Def *def) {
287 (delayed ? delayedMerged : blockingMerged) = def;
290 static bool classof(
const LatticeNode *n) {
291 return n->kind == Kind::BlockEntry;
295struct BlockExit :
public LatticeNode {
297 LatticeValue *valueBefore;
298 SmallVector<BlockEntry *, 2> successors;
299 Operation *terminator;
302 BlockExit(Block *block, LatticeValue *valueBefore)
303 : LatticeNode(Kind::BlockExit), block(block), valueBefore(valueBefore),
304 terminator(block->getTerminator()),
305 suspends(isa<HaltOp, WaitOp>(terminator)) {
306 assert(!valueBefore->nodeAfter);
307 valueBefore->nodeAfter =
this;
310 static bool classof(
const LatticeNode *n) {
311 return n->kind == Kind::BlockExit;
315struct OpNode :
public LatticeNode {
317 LatticeValue *valueBefore;
318 LatticeValue *valueAfter;
320 OpNode(Kind kind, Operation *op, LatticeValue *valueBefore,
321 LatticeValue *valueAfter)
322 : LatticeNode(kind), op(op), valueBefore(valueBefore),
323 valueAfter(valueAfter) {
324 assert(!valueBefore->nodeAfter);
325 assert(!valueAfter->nodeBefore);
326 valueBefore->nodeAfter =
this;
327 valueAfter->nodeBefore =
this;
330 static bool classof(
const LatticeNode *n) {
331 return isa<ProbeNode, DriveNode, SlotNode>(n);
335struct ProbeNode :
public OpNode {
338 ProbeNode(ProbeOp op, Value slot, LatticeValue *valueBefore,
339 LatticeValue *valueAfter)
340 : OpNode(Kind::Probe, op, valueBefore, valueAfter), slot(slot) {}
342 static bool classof(
const LatticeNode *n) {
return n->kind == Kind::Probe; }
345struct DriveNode :
public OpNode {
349 DriveNode(DriveLikeOp op, Value slot, Def *def, LatticeValue *valueBefore,
350 LatticeValue *valueAfter)
351 : OpNode(Kind::Drive, op, valueBefore, valueAfter),
359 bool drivesProjection()
const {
return op->getOperand(0) !=
getSlot(slot); }
362 DriveLikeOp getDriveOp()
const {
return cast<DriveLikeOp>(op); }
364 static bool classof(
const LatticeNode *n) {
return n->kind == Kind::Drive; }
367struct SlotNode :
public OpNode {
372 SlotNode(Operation *op, Def *def, LatticeValue *valueBefore,
373 LatticeValue *valueAfter)
374 : OpNode(Kind::Slot, op, valueBefore, valueAfter), def(def) {}
376 bool isSignal()
const {
return isa<SignalOp>(op); }
379 static bool classof(
const LatticeNode *n) {
return n->kind == Kind::Slot; }
386 LatticeValue *createValue() {
387 auto *value =
new (valueAllocator.Allocate()) LatticeValue();
388 values.push_back(value);
393 template <
class T,
typename... Args>
394 T *createNode(Args... args) {
396 new (getAllocator<T>().Allocate()) T(std::forward<Args>(args)...);
397 nodes.push_back(node);
402 template <
typename... Args>
403 Def *createDef(Args... args) {
404 auto *def =
new (defAllocator.Allocate()) Def(std::forward<Args>(args)...);
410 Def *createDef(Value value, DriveCondition mode) {
411 auto &slot = defsForValues[{value, mode}];
413 slot =
new (defAllocator.Allocate()) Def(value, mode);
414 defs.push_back(slot);
420 void dump(llvm::raw_ostream &os = llvm::dbgs());
424 std::vector<LatticeNode *> nodes;
426 std::vector<LatticeValue *> values;
428 std::vector<Def *> defs;
432 DenseMap<std::pair<Value, DriveCondition>, Def *> defsForValues;
435 SpecificBumpPtrAllocator<LatticeValue> valueAllocator;
436 SpecificBumpPtrAllocator<Def> defAllocator;
437 SpecificBumpPtrAllocator<BlockEntry> blockEntryAllocator;
438 SpecificBumpPtrAllocator<BlockExit> blockExitAllocator;
439 SpecificBumpPtrAllocator<ProbeNode> probeAllocator;
440 SpecificBumpPtrAllocator<DriveNode> driveAllocator;
441 SpecificBumpPtrAllocator<SlotNode> slotAllocator;
445 SpecificBumpPtrAllocator<T> &getAllocator();
451SpecificBumpPtrAllocator<BlockEntry> &Lattice::getAllocator() {
452 return blockEntryAllocator;
455SpecificBumpPtrAllocator<BlockExit> &Lattice::getAllocator() {
456 return blockExitAllocator;
459SpecificBumpPtrAllocator<ProbeNode> &Lattice::getAllocator() {
460 return probeAllocator;
463SpecificBumpPtrAllocator<DriveNode> &Lattice::getAllocator() {
464 return driveAllocator;
467SpecificBumpPtrAllocator<SlotNode> &Lattice::getAllocator() {
468 return slotAllocator;
475void Lattice::dump(llvm::raw_ostream &os) {
477 llvm::MapVector<Block *, unsigned> blockNames;
478 llvm::MapVector<Value, unsigned> memNames;
479 llvm::MapVector<Def *, unsigned> defNames;
481 auto blockName = [&](
Block *block) {
482 unsigned id = blockNames.insert({block, blockNames.size()}).first->second;
483 return std::string(
"bb") + llvm::utostr(
id);
486 auto memName = [&](
DefSlot value) {
488 memNames.insert({
getSlot(value), memNames.size()}).first->second;
489 return std::string(
"mem") + llvm::utostr(
id) +
493 auto defName = [&](Def *def) {
494 unsigned id = defNames.insert({def, defNames.size()}).first->second;
495 return std::string(
"def") + llvm::utostr(
id);
499 for (
auto *node : nodes)
500 if (
auto *entry = dyn_cast<BlockEntry>(node))
501 blockName(entry->block);
505 for (
auto *node : nodes) {
506 auto *entry = dyn_cast<BlockEntry>(node);
511 os <<
" " << blockName(entry->block) <<
":";
512 if (entry->predecessors.empty()) {
513 os <<
" // no predecessors";
516 for (
auto *node : entry->predecessors)
517 os <<
" " << blockName(node->block);
522 auto *value = entry->valueAfter;
527 auto printDef = [&](
bool delayed, Def *def) {
530 os <<
" -> def (" << (delayed ?
"delayed" :
"blocking")
531 <<
")=" << defName(def);
532 if (def->condition.isNever())
534 else if (def->condition.isAlways())
540 printDef(
false, value->blockingReachingDef);
541 printDef(
true, value->delayedReachingDef);
542 if (isa<BlockExit>(value->nodeAfter))
546 if (
auto *node = dyn_cast<ProbeNode>(value->nodeAfter))
547 os <<
" probe " << memName(
blockingSlot(node->slot)) <<
"\n";
548 else if (
auto *node = dyn_cast<DriveNode>(value->nodeAfter))
549 os <<
" drive " << memName(node->slot) <<
"\n";
550 else if (
auto *node = dyn_cast<SlotNode>(value->nodeAfter))
551 os <<
" slot " << memName(node->getSlot()) <<
"\n";
556 value = cast<OpNode>(value->nodeAfter)->valueAfter;
560 auto *exit = cast<BlockExit>(value->nodeAfter);
561 if (isa<WaitOp>(exit->terminator))
563 else if (exit->successors.empty())
567 for (
auto *node : exit->successors)
568 os <<
" " << blockName(node->block);
570 os <<
" // suspends";
575 for (
auto [mem,
id] : memNames)
576 os <<
" mem" <<
id <<
": " << mem <<
"\n";
614 while (fromSignal != toSlot) {
615 auto *op = cast<OpResult>(fromSignal).getOwner();
616 stack.push_back({op, Value(), {}});
617 fromSignal = op->getOperand(0);
630 for (
auto &projection : llvm::reverse(projections)) {
631 projection.into = value;
632 value = TypeSwitch<Operation *, Value>(projection.op)
633 .Case<SigArrayGetOp>([&](
auto op) {
635 op.getLoc(), value, op.getIndex());
637 .Case<SigStructExtractOp>([&](
auto op) {
640 if (isa<hw::UnionType>(value.getType()))
641 return builder.createOrFold<hw::UnionExtractOp>(
642 op.getLoc(), value, op.getFieldAttr());
644 op.getLoc(), value, op.getFieldAttr());
646 .Case<SigExtractOp>([&](
auto op) {
647 auto type = cast<RefType>(op.getType()).getNestedType();
648 auto width = type.getIntOrFloatBitWidth();
650 op.getLowBit(), width);
670 for (
auto projection : projections) {
671 value = TypeSwitch<Operation *, Value>(projection.op)
672 .Case<SigArrayGetOp>([&](
auto op) {
673 return builder.createOrFold<hw::ArrayInjectOp>(
674 op.getLoc(), projection.into, op.getIndex(), value);
676 .Case<SigStructExtractOp>([&](
auto op) {
680 dyn_cast<hw::UnionType>(projection.into.getType()))
681 return builder.createOrFold<hw::UnionCreateOp>(
682 op.getLoc(), unionTy, op.getFieldAttr(), value);
683 return builder.createOrFold<hw::StructInjectOp>(
684 op.getLoc(), projection.into, op.getFieldAttr(), value);
686 .Case<SigExtractOp>([&](
auto op) {
689 op.getLowBit(), value);
704struct PromotionTree {
706 using Access = Operation *;
711 Node(Operation *op) : op(op) {}
715 return llvm::concat<Access>(directAccesses, foldedAccesses);
717 bool hasInRegionAccess(
const Region ®ion) {
718 return llvm::any_of(accesses(), [&](Access &access) {
719 return access && access->getParentRegion() == ®ion;
722 bool hasInRegionDeltaDrive(
const Region ®ion) {
723 return llvm::any_of(accesses(), [&](Access &access) {
724 return access->getParentRegion() == ®ion &&
isDeltaDrive(access);
727 bool hasInRegionBlockingDrive(
const Region ®ion) {
728 return llvm::any_of(accesses(), [&](Access &access) {
729 return access->getParentRegion() == ®ion &&
isBlockingDrive(access);
732 bool hasInRegionDrive(
const Region ®ion) {
733 return llvm::any_of(accesses(), [&](Access &access) {
734 return access->getParentRegion() == ®ion && isa<DriveLikeOp>(access);
737 bool hasInRegionProjectedDrive(
const Region ®ion) {
738 return llvm::any_of(foldedAccesses, [&](Access &access) {
739 return access->getParentRegion() == ®ion && isa<DriveLikeOp>(access);
742 bool hasProjection()
const {
return !foldedProjections.empty(); }
747 SmallVector<Access> directAccesses;
754 SmallVector<Access> foldedAccesses;
757 SmallVector<Operation *> foldedProjections;
771 Node *buildFrom(Operation *signal, Region ®ion);
773 Node *makeNode(Operation *op) {
774 nodes.emplace_back(std::make_unique<Node>(op));
775 return nodes.back().get();
779 SmallVector<std::unique_ptr<Node>, 8> nodes;
782PromotionTree::Node *PromotionTree::buildFrom(Operation *signal,
785 assert(isa<SignalOp>(signal));
786 Node *rootNode = makeNode(signal);
789 using WorklistItem = std::tuple<Operation *, Node *, ProjectionStack>;
790 SmallVector<WorklistItem, 4> worklist = {{signal, rootNode, {}}};
791 while (!worklist.empty()) {
792 auto [curOp, curNode, relativePath] = worklist.pop_back_val();
795 SmallVector<Access, 4> accesses;
796 SmallVector<Projection, 4> projections;
797 for (Operation *user : curOp->getUsers()) {
799 if (region.isProperAncestor(user->getParentRegion()))
802 bool inRegion = user->getParentRegion() == ®ion;
804 if (isa<ProbeOp>(user)) {
806 curNode->isSlot |= inRegion;
807 accesses.push_back(user);
808 }
else if (isa<DriveLikeOp>(user)) {
813 curNode->isSlot |= inRegion;
814 accesses.push_back(user);
815 }
else if (isa<SigExtractOp, SigArrayGetOp, SigStructExtractOp>(user)) {
823 projections.push_back({user, user->getResult(0), *range});
833 if (!curNode->isSlot) {
834 auto overlaps = [&](
const Projection &p1,
const Projection &p2) {
837 curNode->isSlot |= llvm::any_of(projections, [&](Projection &proj) {
838 return llvm::any_of(projections, [&](Projection &sibling) {
839 return overlaps(proj, sibling);
845 if (relativePath.size() == 0)
846 curNode->directAccesses.append(accesses);
848 curNode->foldedAccesses.append(accesses);
851 for (
auto &proj : projections) {
852 if (curNode->isSlot) {
854 curNode->foldedProjections.push_back(proj.op);
856 auto path = relativePath;
857 path.push_back(proj);
858 worklist.push_back({proj.op, curNode, path});
861 auto *projNode = makeNode(proj.op);
864 worklist.push_back({proj.op, projNode, {}});
880 Promoter(Region ®ion) : region(region) {}
881 LogicalResult promote();
883 void findPromotableSlots();
884 Value resolveSlot(Value projectionOrSlot);
885 void populateSlotOps();
887 void computeLiveOutWait();
888 void captureAcrossWait();
889 void captureAcrossWait(Value value, ArrayRef<WaitOp> waitOps,
890 Liveness &liveness, DominanceInfo &dominance);
892 void constructLattice();
893 void propagateBackward();
894 void propagateBackward(LatticeNode *node);
895 void propagateForward();
896 void propagateForward(
bool optimisticMerges, DominanceInfo &dominance);
897 void propagateForward(LatticeNode *node,
bool optimisticMerges,
898 DominanceInfo &dominance);
899 void markDirty(LatticeNode *node);
901 void insertProbeBlocks();
903 void insertProbes(BlockEntry *node);
904 void insertProbes(SlotNode *node);
905 void hoistSlot(Operation *slotOp);
907 void insertDriveBlocks();
909 void insertDrives(BlockExit *node);
910 void insertDrives(DriveNode *node);
912 void resolveDefinitions();
913 void resolveDefinitions(ProbeNode *node);
914 void resolveDefinitions(DriveNode *node);
915 void resolveDefinitionValue(DriveNode *node);
916 void resolveDefinitionCondition(DriveNode *node);
918 void insertBlockArgs();
919 bool insertBlockArgs(BlockEntry *node);
920 void replaceValueWith(Value oldValue, Value newValue);
924 void removeUnusedLocalSignal(SignalOp signalOp);
933 SmallVector<WaitOp> waitOps;
935 llvm::SetVector<Value> liveOutWait;
937 std::unique_ptr<DominanceInfo> dominance;
938 std::unique_ptr<Liveness> liveness;
943 SmallVector<Value> slots;
945 SmallDenseMap<Value, Value> projections;
948 SmallDenseSet<Value> promotable;
957 DenseMap<Block *, ConstantTimeOp> delayedTimeCache;
962 std::optional<Lattice> lattice;
964 SmallVector<LatticeNode *> dirtyNodes;
967 UnusedOpPruner pruner;
971 DenseMap<Value, SmallVector<Operation *>> slotOps;
973 SmallSetVector<Operation *, 4> localSignals;
977LogicalResult Promoter::promote() {
981 computeLiveOutWait();
982 findPromotableSlots();
997 for (
auto slot : slots) {
1004 for (
auto *signalOp : localSignals)
1005 removeUnusedLocalSignal(cast<SignalOp>(signalOp));
1014void Promoter::promoteSlot() {
1015 assert(currentSlot &&
"currentSlot must be set before promoteSlot()");
1019 LLVM_DEBUG(llvm::dbgs() <<
"Promoting slot " << currentSlot <<
"\n");
1023 llvm::dbgs() <<
"Initial lattice:\n";
1028 propagateBackward();
1030 llvm::dbgs() <<
"After backward propagation:\n";
1035 insertProbeBlocks();
1038 llvm::dbgs() <<
"After probe insertion:\n";
1045 llvm::dbgs() <<
"After forward propagation:\n";
1050 resolveDefinitions();
1053 insertDriveBlocks();
1056 llvm::dbgs() <<
"After def resolution and drive insertion:\n";
1066 if (signalOp->getParentRegion() == ®ion)
1067 localSignals.insert(signalOp);
1075void Promoter::findPromotableSlots() {
1076 SmallPtrSet<Value, 8> seenSlots;
1077 SmallPtrSet<Operation *, 8> seenSignals;
1080 PromotionTree ptree;
1081 region.walk([&](Operation *op) {
1082 for (
auto operand : op->getOperands()) {
1083 if (!seenSlots.insert(operand).second)
1091 if (!seenSignals.insert(root).second)
1094 unsigned prevSize = ptree.nodes.size();
1095 auto *rootNode = ptree.buildFrom(root, region);
1098 ptree.nodes.pop_back_n(ptree.nodes.size() - prevSize);
1104 for (
auto &node : ptree.nodes) {
1106 if (!node->isSlot || !node->hasInRegionAccess(region))
1112 if (node->hasProjection() && node->hasInRegionBlockingDrive(region) &&
1113 node->hasInRegionDeltaDrive(region))
1118 Value slotVal = node->op->getResult(0);
1126 if (llvm::any_of(node->foldedProjections, [&](Operation *proj) {
1127 return liveOutWait.contains(proj->getOperand(0));
1132 slots.push_back(slotVal);
1133 for (
auto *proj : node->foldedProjections)
1134 projections[proj->getResult(0)] = slotVal;
1137 promotable.insert(slots.begin(), slots.end());
1138 projections.remove_if([&](
auto elem) {
1139 auto [projection, slot] = elem;
1140 return !promotable.contains(slot);
1142 for (
auto [projection, slot] : projections)
1143 promotable.insert(projection);
1145 LLVM_DEBUG(llvm::dbgs() <<
"Found " << slots.size() <<
" promotable slots, "
1146 << promotable.size() <<
" promotable values\n");
1151Value Promoter::resolveSlot(Value projectionOrSlot) {
1152 if (
auto slot = projections.lookup(projectionOrSlot))
1154 return projectionOrSlot;
1160void Promoter::computeLiveOutWait() {
1161 if (region.hasOneBlock())
1164 for (
auto &block : region)
1165 if (
auto waitOp = dyn_cast<WaitOp>(block.getTerminator()))
1166 waitOps.push_back(waitOp);
1168 if (waitOps.empty())
1171 dominance = std::make_unique<DominanceInfo>(region.getParentOp());
1172 liveness = std::make_unique<Liveness>(region.getParentOp());
1174 auto isDefinedInRegion = [&](Value v) {
1175 return v.getParentRegion() == ®ion;
1178 for (
auto waitOp : waitOps) {
1179 Block *waitBlock = waitOp->getBlock();
1180 const auto &liveOutValues = liveness->getLiveOut(waitBlock);
1182 for (Value v : liveOutValues) {
1183 if (!isDefinedInRegion(v))
1185 liveOutWait.insert(v);
1194void Promoter::captureAcrossWait() {
1195 if (!dominance || !liveness)
1197 for (Value v : liveOutWait)
1198 captureAcrossWait(v, waitOps, *liveness, *dominance);
1205void Promoter::captureAcrossWait(Value value, ArrayRef<WaitOp> waitOps,
1206 Liveness &liveness, DominanceInfo &dominance) {
1213 if (
auto *defOp = value.getDefiningOp())
1214 if (defOp->hasTrait<OpTrait::ConstantLike>())
1217 SmallVector<WaitOp> capturedWaitOps;
1218 for (
auto waitOp : waitOps) {
1219 auto *waitBlock = waitOp->getBlock();
1220 auto *blockLiveness = liveness.getLiveness(waitBlock);
1221 if (!blockLiveness || !blockLiveness->isLiveOut(value))
1223 if (!dominance.dominates(value, waitOp.getOperation()))
1225 capturedWaitOps.push_back(waitOp);
1227 if (capturedWaitOps.empty())
1231 llvm::dbgs() <<
"Capture " << value <<
"\n";
1232 for (
auto waitOp : capturedWaitOps)
1233 llvm::dbgs() <<
"- Across " << waitOp <<
"\n";
1238 auto &domTree = dominance.getDomTree(®ion);
1239 llvm::IDFCalculatorBase<Block, false> idfCalculator(domTree);
1243 SmallPtrSet<Block *, 4> definingBlocks;
1244 definingBlocks.insert(value.getParentBlock());
1245 for (
auto waitOp : capturedWaitOps)
1246 definingBlocks.insert(waitOp.getDest());
1247 idfCalculator.setDefiningBlocks(definingBlocks);
1250 SmallPtrSet<Block *, 16> liveInBlocks;
1251 for (
auto &block : region)
1252 if (liveness.getLiveness(&block)->isLiveIn(value))
1253 liveInBlocks.insert(&block);
1254 idfCalculator.setLiveInBlocks(liveInBlocks);
1258 SmallVector<Block *> mergePointsVec;
1259 idfCalculator.calculate(mergePointsVec);
1260 SmallPtrSet<Block *, 16> mergePoints(mergePointsVec.begin(),
1261 mergePointsVec.end());
1262 for (
auto waitOp : capturedWaitOps)
1263 mergePoints.insert(waitOp.getDest());
1264 LLVM_DEBUG(llvm::dbgs() <<
"- " << mergePoints.size() <<
" merge points\n");
1269 struct WorklistItem {
1270 DominanceInfoNode *domNode;
1273 SmallVector<WorklistItem> worklist;
1274 worklist.push_back({domTree.getNode(value.getParentBlock()), value});
1276 while (!worklist.empty()) {
1277 auto item = worklist.pop_back_val();
1278 auto *block = item.domNode->getBlock();
1281 if (mergePoints.contains(block))
1282 item.reachingDef = block->addArgument(value.getType(), value.getLoc());
1286 for (
auto &op : *block)
1287 op.replaceUsesOfWith(value, item.reachingDef);
1291 if (
auto branchOp = dyn_cast<BranchOpInterface>(block->getTerminator())) {
1292 for (
auto &blockOperand : branchOp->getBlockOperands())
1293 if (mergePoints.contains(blockOperand.get()))
1294 branchOp.getSuccessorOperands(blockOperand.getOperandNumber())
1295 .append(item.reachingDef);
1296 }
else if (
auto waitOp = dyn_cast<WaitOp>(block->getTerminator())) {
1297 if (mergePoints.contains(waitOp.getDest()))
1298 waitOp.getDestOperandsMutable().append(item.reachingDef);
1301 for (
auto *child : item.domNode->children())
1302 worklist.push_back({child, item.reachingDef});
1314void Promoter::populateSlotOps() {
1315 for (
auto &block : region) {
1316 for (
auto &op : block.without_terminator()) {
1317 Value slot = TypeSwitch<Operation *, Value>(&op)
1318 .Case([&](ProbeOp op) {
1319 if (promotable.contains(op.getSignal()))
1320 return resolveSlot(op.getSignal());
1323 .Case([&](DriveLikeOp op) {
1324 if (promotable.contains(op.getDriven()))
1325 return resolveSlot(op.getDriven());
1328 .Case<SigExtractOp, SigArrayGetOp, SigStructExtractOp>(
1329 [&](Operation *op) -> Value {
1330 if (llvm::is_contained(slots, op->getResult(0)))
1331 return op->getResult(0);
1334 .Case([&](SignalOp op) {
return op.getResult(); })
1335 .Default([&](Operation *) {
return Value(); });
1337 slotOps[slot].push_back(&op);
1346void Promoter::constructLattice() {
1347 assert(currentSlot &&
"constructLattice requires currentSlot");
1351 ArrayRef<Operation *> currentSlotOps = slotOps[currentSlot];
1354 SmallDenseMap<Block *, BlockEntry *, 8> blockEntries;
1355 for (
auto &block : region) {
1357 lattice->createNode<BlockEntry>(&block, lattice->createValue());
1358 blockEntries.insert({&block, entry});
1362 for (
auto &block : region) {
1363 auto *valueBefore = blockEntries.lookup(&block)->valueAfter;
1367 ArrayRef<Operation *> blockOps = currentSlotOps.take_while(
1368 [&](Operation *op) {
return op->getBlock() == █ });
1369 currentSlotOps = currentSlotOps.drop_front(blockOps.size());
1371 for (Operation *op : blockOps) {
1373 if (
auto probeOp = dyn_cast<ProbeOp>(op)) {
1374 if (!promotable.contains(probeOp.getSignal()))
1376 if (resolveSlot(probeOp.getSignal()) != currentSlot)
1378 auto *node = lattice->createNode<ProbeNode>(
1379 probeOp, currentSlot, valueBefore, lattice->createValue());
1380 valueBefore = node->valueAfter;
1385 if (
auto driveOp = dyn_cast<DriveLikeOp>(op)) {
1388 auto signal = driveOp.getDriven();
1389 if (!promotable.contains(signal))
1391 if (resolveSlot(signal) != currentSlot)
1393 auto condition = DriveCondition::always();
1394 if (
auto enable = driveOp.getEnable())
1395 condition = DriveCondition::conditional(enable);
1400 signal == currentSlot
1401 ? lattice->createDef(driveOp.getValue(), condition)
1402 : lattice->createDef(driveOp->getBlock(),
1404 auto *node = lattice->createNode<DriveNode>(
1405 driveOp, currentSlot, def, valueBefore, lattice->createValue());
1406 valueBefore = node->valueAfter;
1411 if (isa<SignalOp, SigExtractOp, SigArrayGetOp, SigStructExtractOp>(op)) {
1412 if (op->getResult(0) != currentSlot)
1416 if (
auto signalOp = dyn_cast<SignalOp>(op))
1417 if (
auto init = signalOp.getInit())
1418 def = lattice->createDef(init, DriveCondition::never());
1419 auto *node = lattice->createNode<SlotNode>(op, def, valueBefore,
1420 lattice->createValue());
1421 valueBefore = node->valueAfter;
1427 auto *exit = lattice->createNode<BlockExit>(&block, valueBefore);
1428 for (
auto *otherBlock : exit->terminator->getSuccessors()) {
1429 auto *otherEntry = blockEntries.lookup(otherBlock);
1430 exit->successors.push_back(otherEntry);
1431 otherEntry->predecessors.push_back(exit);
1438void Promoter::propagateBackward() {
1439 for (
auto *node : lattice->nodes)
1440 propagateBackward(node);
1441 SmallVector<LatticeNode *> nodes;
1442 while (!dirtyNodes.empty()) {
1443 std::swap(dirtyNodes, nodes);
1444 for (
auto *node : nodes) {
1445 node->dirty =
false;
1446 propagateBackward(node);
1454void Promoter::propagateBackward(LatticeNode *node) {
1455 auto update = [&](LatticeValue *value,
bool needed) {
1456 if (value->needed != needed) {
1457 value->needed = needed;
1458 markDirty(value->nodeBefore);
1463 if (
auto *probe = dyn_cast<ProbeNode>(node)) {
1464 update(probe->valueBefore,
true);
1474 if (
auto *drive = dyn_cast<DriveNode>(node)) {
1475 bool needed = drive->valueAfter->needed;
1476 if (drive->drivesProjection())
1478 else if (!
isDelayed(drive->slot) && !drive->getDriveOp().getEnable())
1480 update(drive->valueBefore, needed);
1490 if (
auto *slot = dyn_cast<SlotNode>(node)) {
1491 update(slot->valueBefore,
false);
1496 if (
auto *entry = dyn_cast<BlockEntry>(node)) {
1497 for (
auto *predecessor : entry->predecessors)
1498 markDirty(predecessor);
1503 if (
auto *exit = dyn_cast<BlockExit>(node)) {
1506 bool needed =
false;
1507 for (
auto *successor : exit->successors)
1508 needed |= successor->valueAfter->needed;
1509 update(exit->valueBefore, needed);
1513 assert(
false &&
"unhandled node in backward propagation");
1516void Promoter::propagateForward() {
1517 DominanceInfo dominance(region.getParentOp());
1518 propagateForward(
true, dominance);
1519 propagateForward(
false, dominance);
1530void Promoter::propagateForward(
bool optimisticMerges,
1531 DominanceInfo &dominance) {
1532 for (
auto *node : lattice->nodes)
1533 propagateForward(node, optimisticMerges, dominance);
1534 SmallVector<LatticeNode *> nodes;
1535 while (!dirtyNodes.empty()) {
1536 std::swap(dirtyNodes, nodes);
1537 for (
auto *node : nodes) {
1538 node->dirty =
false;
1539 propagateForward(node, optimisticMerges, dominance);
1548void Promoter::propagateForward(LatticeNode *node,
bool optimisticMerges,
1549 DominanceInfo &dominance) {
1550 auto update = [&](LatticeValue *value, Def *blocking, Def *delayed) {
1551 if (value->blockingReachingDef != blocking ||
1552 value->delayedReachingDef != delayed) {
1553 value->blockingReachingDef = blocking;
1554 value->delayedReachingDef = delayed;
1555 markDirty(value->nodeAfter);
1560 if (
auto *probe = dyn_cast<ProbeNode>(node)) {
1561 update(probe->valueAfter, probe->valueBefore->blockingReachingDef,
1562 probe->valueBefore->delayedReachingDef);
1576 if (
auto *drive = dyn_cast<DriveNode>(node)) {
1577 Def *blocking = drive->valueBefore->blockingReachingDef;
1578 Def *delayed = drive->valueBefore->delayedReachingDef;
1579 bool driveDelayed =
isDelayed(drive->slot);
1585 if (drive->drivesProjection() || drive->getDriveOp().getEnable()) {
1586 Def *inDef = driveDelayed ? delayed : blocking;
1588 if (drive->def->value != inDef->value)
1589 drive->def->value = {};
1590 if (inDef->condition.isAlways() || drive->def->condition.isAlways())
1591 drive->def->condition = DriveCondition::always();
1592 else if (drive->def->condition != inDef->condition)
1593 drive->def->condition = DriveCondition::conditional();
1598 delayed = drive->def;
1600 blocking = drive->def;
1604 update(drive->valueAfter, blocking, delayed);
1611 if (
auto *slot = dyn_cast<SlotNode>(node)) {
1612 update(slot->valueAfter, slot->def, slot->isSignal() ? slot->def :
nullptr);
1618 if (
auto *entry = dyn_cast<BlockEntry>(node)) {
1621 if (entry->insertedProbe) {
1622 update(entry->valueAfter, entry->insertedProbe, entry->insertedProbe);
1629 Block *slotBlock = currentSlot.getDefiningOp()->getBlock();
1630 bool slotDominates = dominance.dominates(slotBlock, entry->block);
1631 bool anyPredSuspends =
1632 llvm::any_of(entry->predecessors, [](
auto *p) { return p->suspends; });
1634 auto mergeFlavor = [&](
bool delayed) -> Def * {
1635 if (!slotDominates || anyPredSuspends)
1639 if (llvm::all_of(entry->predecessors, [&](
auto *p) {
1640 return !p->valueBefore->getReachingDef(delayed);
1646 Def *common =
nullptr;
1647 DriveCondition cond = DriveCondition::never();
1649 for (
auto *pred : entry->predecessors) {
1650 Def *predDef = pred->valueBefore->getReachingDef(delayed);
1651 if (!predDef && optimisticMerges)
1653 DriveCondition predCond =
1654 predDef ? predDef->condition : DriveCondition::never();
1661 if (common != predDef)
1663 if (cond != predCond)
1664 cond = DriveCondition::conditional();
1675 Def *&merged = delayed ? entry->delayedMerged : entry->blockingMerged;
1677 merged->condition = cond;
1688 merged = lattice->createDef(entry->block,
getStoredType(slot), cond);
1692 Def *newBlocking = mergeFlavor(
false);
1693 Def *newDelayed = mergeFlavor(
true);
1694 update(entry->valueAfter, newBlocking, newDelayed);
1699 if (
auto *exit = dyn_cast<BlockExit>(node)) {
1700 for (
auto *successor : exit->successors)
1701 markDirty(successor);
1705 assert(
false &&
"unhandled node in forward propagation");
1709void Promoter::markDirty(LatticeNode *node) {
1714 dirtyNodes.push_back(node);
1725void Promoter::insertProbeBlocks() {
1729 SmallDenseSet<std::pair<BlockExit *, BlockEntry *>, 1> worklist;
1730 for (
auto *node : lattice->nodes) {
1731 auto *entry = dyn_cast<BlockEntry>(node);
1732 if (!entry || !entry->valueAfter->needed)
1734 unsigned numIncoming = 0;
1735 for (
auto *predecessor : entry->predecessors)
1736 if (predecessor->valueBefore->needed)
1738 if (numIncoming == 0 || numIncoming == entry->predecessors.size())
1740 for (
auto *predecessor : entry->predecessors)
1741 if (!predecessor->valueBefore->needed)
1742 worklist.insert({predecessor, entry});
1746 for (
auto [predecessor, successor] : worklist) {
1747 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting probe block towards " << successor
1748 <<
" after " << *predecessor->terminator <<
"\n");
1749 OpBuilder builder(predecessor->terminator);
1750 auto *newBlock = builder.createBlock(successor->block);
1751 for (
auto oldArg : successor->block->getArguments())
1752 newBlock->addArgument(oldArg.getType(), oldArg.getLoc());
1753 cf::BranchOp::create(builder, predecessor->terminator->getLoc(),
1754 successor->block, newBlock->getArguments());
1755 for (
auto &blockOp : predecessor->terminator->getBlockOperands())
1756 if (blockOp.get() == successor->block)
1757 blockOp.set(newBlock);
1760 auto *value = lattice->createValue();
1761 value->needed = successor->valueAfter->needed;
1762 auto *newEntry = lattice->createNode<BlockEntry>(newBlock, value);
1763 auto *newExit = lattice->createNode<BlockExit>(newBlock, value);
1764 newEntry->predecessors.push_back(predecessor);
1765 newExit->successors.push_back(successor);
1766 llvm::replace(successor->predecessors, predecessor, newExit);
1767 llvm::replace(predecessor->successors, successor, newEntry);
1774void Promoter::insertProbes() {
1775 for (
auto *node : lattice->nodes) {
1776 if (
auto *entry = dyn_cast<BlockEntry>(node))
1777 insertProbes(entry);
1778 else if (
auto *slot = dyn_cast<SlotNode>(node))
1786void Promoter::insertProbes(SlotNode *node) {
1787 auto *op = node->op;
1789 if (!node->def && node->valueAfter->needed) {
1790 OpBuilder builder(op);
1791 builder.setInsertionPointAfter(op);
1792 auto value = ProbeOp::create(builder, currentSlot.getLoc(), currentSlot);
1793 node->def = lattice->createDef(value, DriveCondition::never());
1795 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting probe for " << currentSlot
1796 <<
" after " << *op <<
"\n");
1809void Promoter::hoistSlot(Operation *slotOp) {
1810 auto isInRegion = [&](Value value) {
1811 return region.isAncestor(value.getParentRegion());
1817 SmallVector<Operation *> toMove;
1818 auto *curOp = slotOp;
1822 toMove.push_back(curOp);
1823 for (
auto index : curOp->getOperands().drop_front()) {
1824 if (!isInRegion(index))
1826 auto *indexOp = index.getDefiningOp();
1827 if (!indexOp || !indexOp->hasTrait<OpTrait::ConstantLike>())
1829 toMove.push_back(indexOp);
1831 auto input = curOp->getOperand(0);
1832 if (!isInRegion(input))
1834 curOp = input.getDefiningOp();
1841 for (
auto *op : llvm::reverse(toMove)) {
1842 if (!region.isAncestor(op->getParentRegion()))
1844 LLVM_DEBUG(llvm::dbgs() <<
"- Hoisting " << *op <<
"\n");
1845 op->moveBefore(region.getParentOp());
1851void Promoter::insertProbes(BlockEntry *node) {
1852 if (!node->valueAfter->needed)
1854 if (!node->predecessors.empty() &&
1855 llvm::all_of(node->predecessors, [](
auto *predecessor) {
1856 return predecessor->valueBefore->needed;
1859 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting probe for " << currentSlot
1860 <<
" in block " << node->block <<
"\n");
1861 auto builder = OpBuilder::atBlockBegin(node->block);
1863 if (Operation *op = currentSlot.getDefiningOp())
1864 if (op->getBlock() == node->block)
1865 builder.setInsertionPointAfterValue(currentSlot);
1866 auto value = ProbeOp::create(builder, currentSlot.getLoc(), currentSlot);
1867 auto *def = lattice->createDef(value, DriveCondition::never());
1868 node->insertedProbe = def;
1873void Promoter::insertDriveBlocks() {
1877 auto partialAt = [](LatticeValue *before, LatticeValue *after,
bool delayed,
1878 unsigned totalSuccessors) {
1879 Def *def = before->getReachingDef(delayed);
1880 if (!def || def->condition.isNever())
1882 (void)totalSuccessors;
1883 return !after->getReachingDef(delayed);
1886 SmallDenseSet<std::pair<BlockExit *, BlockEntry *>, 1> worklist;
1887 for (
auto *node : lattice->nodes) {
1888 auto *exit = dyn_cast<BlockExit>(node);
1894 for (
bool delayed : {
false,
true}) {
1895 Def *def = exit->valueBefore->getReachingDef(delayed);
1896 if (!def || def->condition.isNever())
1898 unsigned numContinues = 0;
1899 for (
auto *successor : exit->successors)
1900 if (successor->valueAfter->getReachingDef(delayed))
1902 if (numContinues == 0 || numContinues == exit->successors.size())
1904 for (
auto *successor : exit->successors)
1905 if (partialAt(exit->valueBefore, successor->valueAfter, delayed,
1906 exit->successors.size()))
1907 worklist.insert({exit, successor});
1912 for (
auto [predecessor, successor] : worklist) {
1913 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting drive block towards " << successor
1914 <<
" after " << *predecessor->terminator <<
"\n");
1915 OpBuilder builder(predecessor->terminator);
1916 auto *newBlock = builder.createBlock(successor->block);
1917 for (
auto oldArg : successor->block->getArguments())
1918 newBlock->addArgument(oldArg.getType(), oldArg.getLoc());
1919 cf::BranchOp::create(builder, predecessor->terminator->getLoc(),
1920 successor->block, newBlock->getArguments());
1921 for (
auto &blockOp : predecessor->terminator->getBlockOperands())
1922 if (blockOp.get() == successor->block)
1923 blockOp.set(newBlock);
1928 auto *value = lattice->createValue();
1929 value->needed = successor->valueAfter->needed;
1930 value->blockingReachingDef = predecessor->valueBefore->blockingReachingDef;
1931 value->delayedReachingDef = predecessor->valueBefore->delayedReachingDef;
1932 auto *newEntry = lattice->createNode<BlockEntry>(newBlock, value);
1933 auto *newExit = lattice->createNode<BlockExit>(newBlock, value);
1934 newEntry->predecessors.push_back(predecessor);
1935 newExit->successors.push_back(successor);
1936 llvm::replace(successor->predecessors, predecessor, newExit);
1937 llvm::replace(predecessor->successors, successor, newEntry);
1944void Promoter::insertDrives() {
1945 for (
auto *node : lattice->nodes) {
1946 if (
auto *exit = dyn_cast<BlockExit>(node))
1948 else if (
auto *drive = dyn_cast<DriveNode>(node))
1949 insertDrives(drive);
1955void Promoter::insertDrives(BlockExit *node) {
1956 auto builder = OpBuilder::atBlockTerminator(node->block);
1961 auto getDeltaTime = [&]() -> ConstantTimeOp {
1962 auto &cached = delayedTimeCache[node->block];
1964 cached = ConstantTimeOp::create(builder, node->terminator->getLoc(), 0,
1969 auto insertDriveForSlot = [&](
bool delayed) {
1970 Def *reachingDef = node->valueBefore->getReachingDef(delayed);
1971 if (!reachingDef || reachingDef->condition.isNever())
1973 if (!node->suspends && !node->successors.empty() &&
1974 llvm::all_of(node->successors, [&](
auto *successor) {
1975 return successor->valueAfter->getReachingDef(delayed) != nullptr;
1978 LLVM_DEBUG(llvm::dbgs() <<
"- Inserting drive for " << currentSlot <<
" "
1979 << (delayed ?
"(delayed)" :
"(blocking)")
1980 <<
" before " << *node->terminator <<
"\n");
1981 auto value = reachingDef->getValueOrPlaceholder();
1982 auto enable = reachingDef->condition.isConditional()
1983 ? reachingDef->getConditionOrPlaceholder()
1986 DriveOp::create(builder, currentSlot.getLoc(), currentSlot, value,
1987 getDeltaTime(), enable);
1989 AssignOp::create(builder, currentSlot.getLoc(), currentSlot, value,
1993 insertDriveForSlot(
false);
1994 insertDriveForSlot(
true);
1999void Promoter::insertDrives(DriveNode *node) {
2000 LLVM_DEBUG(llvm::dbgs() <<
"- Removing drive " << *node->op <<
"\n");
2010void Promoter::resolveDefinitions() {
2011 for (
auto *node : lattice->nodes) {
2012 if (
auto *probe = dyn_cast<ProbeNode>(node))
2013 resolveDefinitions(probe);
2014 else if (
auto *drive = dyn_cast<DriveNode>(node))
2015 resolveDefinitions(drive);
2020void Promoter::resolveDefinitions(ProbeNode *node) {
2021 Def *def = node->valueBefore->blockingReachingDef;
2022 assert(def &&
"no definition reaches probe");
2025 auto projections =
getProjections(node->op->getOperand(0), node->slot);
2028 auto builder = OpBuilder(node->op);
2029 auto value = def->getValueOrPlaceholder();
2033 LLVM_DEBUG(llvm::dbgs() <<
"- Replacing " << *node->op <<
" with " << value
2035 replaceValueWith(node->op->getResult(0), value);
2042void Promoter::resolveDefinitions(DriveNode *node) {
2047 if (!node->def->value || node->def->valueIsPlaceholder)
2048 resolveDefinitionValue(node);
2049 if (node->def->condition.isConditional() &&
2050 (!node->def->condition.getCondition() ||
2051 node->def->conditionIsPlaceholder))
2052 resolveDefinitionCondition(node);
2055void Promoter::resolveDefinitionValue(DriveNode *node) {
2058 Def *inDef = node->valueBefore->getReachingDef(
isDelayed(node->slot));
2059 assert(inDef &&
"no definition reaches drive");
2060 auto driveOp = node->getDriveOp();
2061 LLVM_DEBUG(llvm::dbgs() <<
"- Injecting value for " << driveOp <<
"\n");
2067 auto builder = OpBuilder(driveOp);
2068 auto value = inDef->getValueOrPlaceholder();
2073 if (!driveOp.getEnable())
2074 value = driveOp.getValue();
2076 value = builder.createOrFold<comb::MuxOp>(
2077 driveOp.getLoc(), driveOp.getEnable(), driveOp.getValue(), value);
2083 if (node->def->valueIsPlaceholder) {
2084 auto *placeholder = node->def->value.getDefiningOp();
2085 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
2086 "placeholder replaced but valueIsPlaceholder still set");
2087 replaceValueWith(placeholder->getResult(0), value);
2089 node->def->valueIsPlaceholder =
false;
2091 node->def->value = value;
2094void Promoter::resolveDefinitionCondition(DriveNode *node) {
2097 Def *inDef = node->valueBefore->getReachingDef(
isDelayed(node->slot));
2098 assert(inDef &&
"no definition reaches drive");
2099 auto driveOp = node->getDriveOp();
2100 LLVM_DEBUG(llvm::dbgs() <<
"- Mutating condition for " << driveOp <<
"\n");
2101 OpBuilder builder(driveOp);
2106 if (inDef->condition.isNever())
2107 condition = driveOp.getEnable();
2110 builder.createOrFold<comb::OrOp>(driveOp.getLoc(), driveOp.getEnable(),
2111 inDef->getConditionOrPlaceholder());
2114 if (node->def->conditionIsPlaceholder) {
2115 auto *placeholder = node->def->condition.getCondition().getDefiningOp();
2116 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
2117 "placeholder replaced but conditionIsPlaceholder still set");
2118 replaceValueWith(placeholder->getResult(0), condition);
2120 node->def->conditionIsPlaceholder =
false;
2122 node->def->condition.setCondition(condition);
2130void Promoter::insertBlockArgs() {
2131 bool anyArgsInserted =
true;
2132 while (anyArgsInserted) {
2133 anyArgsInserted =
false;
2134 for (
auto *node : lattice->nodes)
2135 if (
auto *entry = dyn_cast<BlockEntry>(node))
2136 anyArgsInserted |= insertBlockArgs(entry);
2148bool Promoter::insertBlockArgs(BlockEntry *node) {
2154 enum class Which { Value, Condition };
2155 SmallVector<std::pair<DefSlot, Which>> neededSlots;
2156 auto addNeededSlot = [&](
DefSlot slot) {
2157 auto *def = node->getMerged(
isDelayed(slot));
2160 if (!node->valueAfter->getReachingDef(
isDelayed(slot)))
2162 if (def->valueIsPlaceholder)
2163 neededSlots.push_back({slot, Which::Value});
2164 if (def->conditionIsPlaceholder)
2165 neededSlots.push_back({slot, Which::Condition});
2169 if (neededSlots.empty())
2171 LLVM_DEBUG(llvm::dbgs() <<
"- Adding " << neededSlots.size()
2172 <<
" args to block " << node->block <<
"\n");
2175 for (
auto [slot, which] : neededSlots) {
2176 auto *def = node->getMerged(
isDelayed(slot));
2179 case Which::Value: {
2182 auto *placeholder = def->value.getDefiningOp();
2183 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
2184 "placeholder replaced but valueIsPlaceholder still set");
2186 replaceValueWith(placeholder->getResult(0), arg);
2189 def->valueIsPlaceholder =
false;
2192 case Which::Condition: {
2196 auto *placeholder = def->condition.getCondition().getDefiningOp();
2197 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
2198 "placeholder replaced but conditionIsPlaceholder still set");
2199 auto conditionArg = node->block->addArgument(
2200 IntegerType::get(region.getContext(), 1),
getLoc(slot));
2201 replaceValueWith(placeholder->getResult(0), conditionArg);
2203 def->condition.setCondition(conditionArg);
2204 def->conditionIsPlaceholder =
false;
2211 SmallPtrSet<BlockExit *, 4> updatedPredecessors;
2212 for (
auto *predecessor : node->predecessors) {
2220 if (!updatedPredecessors.insert(predecessor).second)
2224 SmallVector<Value> args;
2225 for (
auto [slot, which] : neededSlots) {
2226 Def *def = predecessor->valueBefore->getReachingDef(
isDelayed(slot));
2227 auto builder = OpBuilder::atBlockTerminator(predecessor->block);
2231 args.push_back(def->getValueOrPlaceholder());
2237 case Which::Condition:
2239 args.push_back(def->getConditionOrPlaceholder());
2242 builder.getI1Type(), 0));
2251 if (
auto branchOp = dyn_cast<BranchOpInterface>(predecessor->terminator)) {
2252 SmallVector<unsigned> successorOperandNumbers;
2253 for (
auto &blockOperand : branchOp->getBlockOperands())
2254 if (blockOperand.get() == node->block)
2255 successorOperandNumbers.push_back(blockOperand.getOperandNumber());
2256 for (
auto operandNumber : successorOperandNumbers)
2257 branchOp.getSuccessorOperands(operandNumber).append(args);
2260 if (
auto waitOp = dyn_cast<WaitOp>(predecessor->terminator)) {
2261 if (waitOp.getDest() == node->block)
2262 waitOp.getDestOperandsMutable().append(args);
2266 "mem2reg predecessor terminator has no successor operands");
2274void Promoter::replaceValueWith(Value oldValue, Value newValue) {
2275 oldValue.replaceAllUsesWith(newValue);
2276 for (
auto *def : lattice->defs) {
2277 if (def->value == oldValue)
2278 def->value = newValue;
2279 if (def->condition.isConditional() &&
2280 def->condition.getCondition() == oldValue)
2281 def->condition.setCondition(newValue);
2292void Promoter::removeUnusedLocalSignal(SignalOp signalOp) {
2295 SmallSetVector<Operation *, 8> users;
2296 SmallVector<Operation *> worklist;
2297 worklist.push_back(signalOp);
2298 while (!worklist.empty()) {
2299 auto *op = worklist.pop_back_val();
2300 if (!isa<SignalOp, DriveLikeOp, SigArrayGetOp, SigArraySliceOp,
2301 SigExtractOp, SigStructExtractOp>(op))
2303 for (
auto *user : op->getUsers())
2304 if (users.insert(user))
2305 worklist.push_back(user);
2310 LLVM_DEBUG(llvm::dbgs() <<
"- Removing local signal " << *signalOp <<
"\n");
2311 for (
auto *op : llvm::reverse(users))
2320struct Mem2RegPass :
public llhd::impl::Mem2RegPassBase<Mem2RegPass> {
2321 void runOnOperation()
override;
2325void Mem2RegPass::runOnOperation() {
2326 SmallVector<Region *> regions;
2327 getOperation()->walk<WalkOrder::PreOrder>([&](Operation *op) {
2328 if (isa<ProcessOp, FinalOp, CombinationalOp, CoroutineOp>(op)) {
2329 auto ®ion = op->getRegion(0);
2330 if (!region.empty())
2331 regions.push_back(®ion);
2332 return WalkResult::skip();
2334 return WalkResult::advance();
2336 for (
auto *region : regions)
2337 if (failed(Promoter(*region).promote()))
2338 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 Value createZeroValue(ImplicitLocOpBuilder &builder, FIRRTLBaseType type, SmallDenseMap< FIRRTLBaseType, Value > &cache)
Construct a zero value of the given type using the given builder.
static std::optional< unsigned > getPromotableSlotBitWidth(Type type)
static bool isDelayed(DefSlot slot)
static bool isPromotableSlotType(Type type)
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 ProjectionStack getProjections(Value fromSignal, Value toSlot)
Collect the llhd.sig.
static bool isDeltaDrive(const Operation *op)
Check whether an operation is a llhd.drv with a delta delay.
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 bool isBlockingDrive(const Operation *op)
Check whether an operation is an llhd.assign.
static DefSlot delayedSlot(Value slot)
static Value getSlot(DefSlot slot)
static Type getStoredType(DefSlot slot)
SmallVector< Projection > ProjectionStack
A stack of projection operations.
PointerIntPair< Value, 1 > DefSlot
The slot a reaching definition specifies a value for, alongside a bit indicating whether the definiti...
Value createDynamicInject(OpBuilder &builder, Location loc, Value value, Value offset, Value replacement, bool twoState=false)
Replace a range of bits in an integer at a dynamic offset, and return the updated integer value.
Value createDynamicExtract(OpBuilder &builder, Location loc, Value value, Value offset, unsigned width)
Extract a range of bits from an integer at a dynamic offset.
static bool operator==(const ModulePort &a, const ModulePort &b)
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
std::optional< ProjectionRange > getProjectionRange(Operation *op)
Calculate a projection's range.
std::variant< ConstProjectionRange, DynProjectionRange > ProjectionRange
Portion of the signal operand that is selected by a projection.
Operation * getRootSignal(Value sig)
Given a signal or a projection, walk back the projection chain until you find the root signal declara...
bool isProjection(const Operation *op)
Check if an op is one of the supported projection types.
bool isDynamic(const ProjectionRange &range)
Helper to check if a projection range is dynamic or not.
bool isProvablyDisjoint(const ProjectionRange &a, const ProjectionRange &b)
Check whether two projections provably describe disjoint storage.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
bool operator!=(uint64_t a, const FVInt &b)
void eraseLaterIfUnused(Operation *op)
Mark an op the be erased later if it is unused at that point.
void eraseNow(Operation *op)
Erase an operation immediately, and remove it from the set of ops to be removed later.
static bool isEqual(DriveCondition lhs, DriveCondition rhs)
static unsigned getHashValue(DriveCondition d)