CIRCT 23.0.0git
Loading...
Searching...
No Matches
Mem2Reg.cpp
Go to the documentation of this file.
1//===- Mem2Reg.cpp - Promote signal/memory slots to values ----------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
14#include "mlir/Analysis/Liveness.h"
15#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
16#include "mlir/IR/Dominance.h"
17#include "llvm/ADT/ArrayRef.h"
18#include "llvm/Support/Debug.h"
19#include "llvm/Support/GenericIteratedDominanceFrontier.h"
20
21#define DEBUG_TYPE "llhd-mem2reg"
22
23namespace circt {
24namespace llhd {
25#define GEN_PASS_DEF_MEM2REGPASS
26#include "circt/Dialect/LLHD/LLHDPasses.h.inc"
27} // namespace llhd
28} // namespace circt
29
30using namespace mlir;
31using namespace circt;
32using namespace llhd;
33using llvm::ArrayRef;
34using llvm::PointerIntPair;
35using llvm::SmallDenseSet;
37using llvm::SpecificBumpPtrAllocator;
38
39/// Check whether a value is defined by `llhd.constant_time <0ns, 0d, 1e>`.
40static bool isEpsilonDelay(Value value) {
41 if (auto timeOp = value.getDefiningOp<ConstantTimeOp>()) {
42 auto t = timeOp.getValue();
43 return t.getTime() == 0 && t.getDelta() == 0 && t.getEpsilon() == 1;
44 }
45 return false;
46}
47
48/// Check whether a value is defined by `llhd.constant_time <0ns, 1d, 0e>`.
49static bool isDeltaDelay(Value value) {
50 if (auto timeOp = value.getDefiningOp<ConstantTimeOp>()) {
51 auto t = timeOp.getValue();
52 return t.getTime() == 0 && t.getDelta() == 1 && t.getEpsilon() == 0;
53 }
54 return false;
55}
56
57/// Check whether an operation is a `llhd.drive` with an epsilon delay. This
58/// corresponds to a blocking assignment in Verilog.
59static bool isBlockingDrive(Operation *op) {
60 if (auto driveOp = dyn_cast<DriveOp>(op))
61 return isEpsilonDelay(driveOp.getTime());
62 return false;
63}
64
65/// Check whether an operation is a `llhd.drive` with a delta delay. This
66/// corresponds to a non-blocking assignment in Verilog.
67static bool isDeltaDrive(Operation *op) {
68 if (auto driveOp = dyn_cast<DriveOp>(op))
69 return isDeltaDelay(driveOp.getTime());
70 return false;
71}
72
73//===----------------------------------------------------------------------===//
74// Reaching Definitions and Placeholders
75//===----------------------------------------------------------------------===//
76
77namespace {
78/// Information about whether a definition is driven back onto its signal. For
79/// example, probes provide a definition for their signal that does not have to
80/// be driven back onto the signal. Drives on the other hand provide a
81/// definition that eventually must be driven onto the signal.
82struct DriveCondition {
83 static DriveCondition never() { return ConditionAndMode(Value{}, Never); }
84 static DriveCondition always() { return ConditionAndMode(Value{}, Always); }
85 static DriveCondition conditional(Value condition = {}) {
86 return ConditionAndMode(condition, Conditional);
87 }
88
89 bool isNever() const { return conditionAndMode.getInt() == Never; }
90 bool isAlways() const { return conditionAndMode.getInt() == Always; }
91 bool isConditional() const {
92 return conditionAndMode.getInt() == Conditional;
93 }
94
95 Value getCondition() const { return conditionAndMode.getPointer(); }
96 void setCondition(Value condition) { conditionAndMode.setPointer(condition); }
97
98 bool operator==(const DriveCondition &other) const {
99 return conditionAndMode == other.conditionAndMode;
100 }
101 bool operator!=(const DriveCondition &other) const {
102 return conditionAndMode != other.conditionAndMode;
103 }
104
105private:
106 enum {
107 Never,
108 Always,
109 Conditional,
110 };
111 typedef PointerIntPair<Value, 2> ConditionAndMode;
112 ConditionAndMode conditionAndMode;
113
114 DriveCondition(ConditionAndMode conditionAndMode)
115 : conditionAndMode(conditionAndMode) {}
116 friend DenseMapInfo<DriveCondition>;
117};
118
119/// A definition for a memory slot that may not yet have a concrete SSA value.
120/// These are created for blocks which need to merge distinct definitions for
121/// the same slot coming from its predecssors, as a standin before block
122/// arguments are created. They are also created for drives, where a concrete
123/// value is already available in the form of the driven value.
124struct Def {
125 Block *block;
126 Type type;
127 Value value;
128 DriveCondition condition;
129 bool valueIsPlaceholder = false;
130 bool conditionIsPlaceholder = false;
131
132 Def(Value value, DriveCondition condition)
133 : block(value.getParentBlock()), type(value.getType()), value(value),
134 condition(condition) {}
135 Def(Block *block, Type type, DriveCondition condition)
136 : block(block), type(type), condition(condition) {}
137
138 Value getValueOrPlaceholder();
139 Value getConditionOrPlaceholder();
140};
141} // namespace
142
143/// Return the SSA value for this definition if it already has one, or create
144/// a placeholder value if no value exists yet.
145Value Def::getValueOrPlaceholder() {
146 if (!value) {
147 auto builder = OpBuilder::atBlockBegin(block);
148 value = UnrealizedConversionCastOp::create(builder, builder.getUnknownLoc(),
149 type, ValueRange{})
150 .getResult(0);
151 valueIsPlaceholder = true;
152 }
153 return value;
154}
155
156/// Return the drive condition for this definition. Creates a constant false or
157/// true SSA value if the drive mode is "never" or "always", respectively. If
158/// the mode is "conditional", return the its condition value if it already has
159/// one, or create a placeholder value if no value exists yet.
160Value Def::getConditionOrPlaceholder() {
161 if (!condition.getCondition()) {
162 auto builder = OpBuilder::atBlockBegin(block);
163 Value value;
164 if (condition.isNever()) {
165 value = hw::ConstantOp::create(builder, builder.getUnknownLoc(),
166 builder.getI1Type(), 0);
167 } else if (condition.isAlways()) {
168 value = hw::ConstantOp::create(builder, builder.getUnknownLoc(),
169 builder.getI1Type(), 1);
170 } else {
171 value =
172 UnrealizedConversionCastOp::create(builder, builder.getUnknownLoc(),
173 builder.getI1Type(), ValueRange{})
174 .getResult(0);
175 conditionIsPlaceholder = true;
176 }
177 condition.setCondition(value);
178 }
179 return condition.getCondition();
180}
181
182// Allow `DriveCondition` to be used as hash map key.
183template <>
184struct llvm::DenseMapInfo<DriveCondition> {
191 static unsigned getHashValue(DriveCondition d) {
193 d.conditionAndMode);
194 }
195 static bool isEqual(DriveCondition lhs, DriveCondition rhs) {
196 return lhs == rhs;
197 }
198};
199
200//===----------------------------------------------------------------------===//
201// Lattice to Propagate Needed and Reaching Definitions
202//===----------------------------------------------------------------------===//
203
204/// The slot a reaching definition specifies a value for, alongside a bit
205/// indicating whether the definition is from a delayed drive or a blocking
206/// drive.
207using DefSlot = PointerIntPair<Value, 1>;
208static DefSlot blockingSlot(Value slot) { return {slot, 0}; }
209static DefSlot delayedSlot(Value slot) { return {slot, 1}; }
210static Value getSlot(DefSlot slot) { return slot.getPointer(); }
211static bool isDelayed(DefSlot slot) { return slot.getInt(); }
212static Type getStoredType(Value slot) {
213 return cast<RefType>(slot.getType()).getNestedType();
214}
215static Type getStoredType(DefSlot slot) {
216 return getStoredType(slot.getPointer());
217}
218static Location getLoc(DefSlot slot) { return slot.getPointer().getLoc(); }
219
220namespace {
221
222struct LatticeNode;
223struct BlockExit;
224struct ProbeNode;
225struct DriveNode;
226struct SignalNode;
227
228/// Lattice state between two adjacent lattice nodes for a single slot.
229///
230/// `needed` records whether a definition of the slot has to be available at
231/// this program point, computed by the backward pass. The two reaching-def
232/// pointers carry the slot's definitions for the blocking and delayed
233/// flavors, computed by the forward pass; either may be null when the slot
234/// has no definition reaching this point.
235struct LatticeValue {
236 LatticeNode *nodeBefore = nullptr;
237 LatticeNode *nodeAfter = nullptr;
238 bool needed = false;
239 Def *blockingReachingDef = nullptr;
240 Def *delayedReachingDef = nullptr;
241
242 /// Return the reaching def for the blocking/delayed flavor of this slot.
243 Def *getReachingDef(bool delayed) const {
244 return delayed ? delayedReachingDef : blockingReachingDef;
245 }
246 void setReachingDef(bool delayed, Def *def) {
247 (delayed ? delayedReachingDef : blockingReachingDef) = def;
248 }
249};
250
251struct LatticeNode {
252 enum class Kind { BlockEntry, BlockExit, Probe, Drive, Signal };
253 const Kind kind;
254 /// Dirty flag to prevent duplicate pushes to worklist.
255 bool dirty = false;
256 LatticeNode(Kind kind) : kind(kind) {}
257};
258
259struct BlockEntry : public LatticeNode {
260 Block *block;
261 LatticeValue *valueAfter;
262 SmallVector<BlockExit *, 2> predecessors;
263 /// Probe op inserted at the start of this block for the slot being
264 /// promoted, if a definition is needed here but not provided by all
265 /// predecessors.
266 Def *insertedProbe = nullptr;
267 /// Merge definitions created for this block entry when predecessors
268 /// disagree on the reaching def. One per flavor, mirroring `LatticeValue`.
269 Def *blockingMerged = nullptr;
270 Def *delayedMerged = nullptr;
271
272 BlockEntry(Block *block, LatticeValue *valueAfter)
273 : LatticeNode(Kind::BlockEntry), block(block), valueAfter(valueAfter) {
274 assert(!valueAfter->nodeBefore);
275 valueAfter->nodeBefore = this;
276 }
277
278 Def *getMerged(bool delayed) const {
279 return delayed ? delayedMerged : blockingMerged;
280 }
281 void setMerged(bool delayed, Def *def) {
282 (delayed ? delayedMerged : blockingMerged) = def;
283 }
284
285 static bool classof(const LatticeNode *n) {
286 return n->kind == Kind::BlockEntry;
287 }
288};
289
290struct BlockExit : public LatticeNode {
291 Block *block;
292 LatticeValue *valueBefore;
293 SmallVector<BlockEntry *, 2> successors;
294 Operation *terminator;
295 bool suspends;
296
297 BlockExit(Block *block, LatticeValue *valueBefore)
298 : LatticeNode(Kind::BlockExit), block(block), valueBefore(valueBefore),
299 terminator(block->getTerminator()),
300 suspends(isa<HaltOp, WaitOp>(terminator)) {
301 assert(!valueBefore->nodeAfter);
302 valueBefore->nodeAfter = this;
303 }
304
305 static bool classof(const LatticeNode *n) {
306 return n->kind == Kind::BlockExit;
307 }
308};
309
310struct OpNode : public LatticeNode {
311 Operation *op;
312 LatticeValue *valueBefore;
313 LatticeValue *valueAfter;
314
315 OpNode(Kind kind, Operation *op, LatticeValue *valueBefore,
316 LatticeValue *valueAfter)
317 : LatticeNode(kind), op(op), valueBefore(valueBefore),
318 valueAfter(valueAfter) {
319 assert(!valueBefore->nodeAfter);
320 assert(!valueAfter->nodeBefore);
321 valueBefore->nodeAfter = this;
322 valueAfter->nodeBefore = this;
323 }
324
325 static bool classof(const LatticeNode *n) {
326 return isa<ProbeNode, DriveNode, SignalNode>(n);
327 }
328};
329
330struct ProbeNode : public OpNode {
331 Value slot;
332
333 ProbeNode(ProbeOp op, Value slot, LatticeValue *valueBefore,
334 LatticeValue *valueAfter)
335 : OpNode(Kind::Probe, op, valueBefore, valueAfter), slot(slot) {}
336
337 static bool classof(const LatticeNode *n) { return n->kind == Kind::Probe; }
338};
339
340struct DriveNode : public OpNode {
341 DefSlot slot;
342 Def *def;
343
344 DriveNode(DriveOp op, Value slot, Def *def, LatticeValue *valueBefore,
345 LatticeValue *valueAfter)
346 : OpNode(Kind::Drive, op, valueBefore, valueAfter),
347 slot(isDeltaDrive(op) ? delayedSlot(slot) : blockingSlot(slot)),
348 def(def) {
350 }
351
352 /// Returns true if the op does not drive an entire slot, but only a part of a
353 /// slot through a projection.
354 bool drivesProjection() const { return op->getOperand(0) != getSlot(slot); }
355
356 /// Return the drive op.
357 DriveOp getDriveOp() const { return cast<DriveOp>(op); }
358
359 static bool classof(const LatticeNode *n) { return n->kind == Kind::Drive; }
360};
361
362struct SignalNode : public OpNode {
363 Def *def;
364
365 SignalNode(SignalOp op, Def *def, LatticeValue *valueBefore,
366 LatticeValue *valueAfter)
367 : OpNode(Kind::Signal, op, valueBefore, valueAfter), def(def) {}
368
369 SignalOp getSignalOp() const { return cast<SignalOp>(op); }
370 DefSlot getSlot() const { return blockingSlot(getSignalOp()); }
371
372 static bool classof(const LatticeNode *n) { return n->kind == Kind::Signal; }
373};
374
375/// A lattice of block entry and exit nodes, nodes for relevant operations such
376/// as probes and drives, and values flowing between the nodes.
377struct Lattice {
378 /// Create a new value on the lattice.
379 LatticeValue *createValue() {
380 auto *value = new (valueAllocator.Allocate()) LatticeValue();
381 values.push_back(value);
382 return value;
383 }
384
385 /// Create a new node on the lattice.
386 template <class T, typename... Args>
387 T *createNode(Args... args) {
388 auto *node =
389 new (getAllocator<T>().Allocate()) T(std::forward<Args>(args)...);
390 nodes.push_back(node);
391 return node;
392 }
393
394 /// Create a new reaching definition.
395 template <typename... Args>
396 Def *createDef(Args... args) {
397 auto *def = new (defAllocator.Allocate()) Def(std::forward<Args>(args)...);
398 defs.push_back(def);
399 return def;
400 }
401
402 /// Create a new reaching definition for an existing value in the IR.
403 Def *createDef(Value value, DriveCondition mode) {
404 auto &slot = defsForValues[{value, mode}];
405 if (!slot) {
406 slot = new (defAllocator.Allocate()) Def(value, mode);
407 defs.push_back(slot);
408 }
409 return slot;
410 }
411
412#ifndef NDEBUG
413 void dump(llvm::raw_ostream &os = llvm::dbgs());
414#endif
415
416 /// All nodes in the lattice.
417 std::vector<LatticeNode *> nodes;
418 /// All values in the lattice.
419 std::vector<LatticeValue *> values;
420 /// All reaching defs in the lattice.
421 std::vector<Def *> defs;
422 /// The reaching defs for concrete values in the IR. This map is used to
423 /// create a single def for the same SSA value to allow for pointer equality
424 /// comparisons.
425 DenseMap<std::pair<Value, DriveCondition>, Def *> defsForValues;
426
427private:
428 SpecificBumpPtrAllocator<LatticeValue> valueAllocator;
429 SpecificBumpPtrAllocator<Def> defAllocator;
430 SpecificBumpPtrAllocator<BlockEntry> blockEntryAllocator;
431 SpecificBumpPtrAllocator<BlockExit> blockExitAllocator;
432 SpecificBumpPtrAllocator<ProbeNode> probeAllocator;
433 SpecificBumpPtrAllocator<DriveNode> driveAllocator;
434 SpecificBumpPtrAllocator<SignalNode> signalAllocator;
435
436 // Helper function to get the correct allocator given a lattice node class.
437 template <class T>
438 SpecificBumpPtrAllocator<T> &getAllocator();
439};
440
441// Specializations for the `getAllocator` template that map node types to the
442// correct allocator.
443template <>
444SpecificBumpPtrAllocator<BlockEntry> &Lattice::getAllocator() {
445 return blockEntryAllocator;
446}
447template <>
448SpecificBumpPtrAllocator<BlockExit> &Lattice::getAllocator() {
449 return blockExitAllocator;
450}
451template <>
452SpecificBumpPtrAllocator<ProbeNode> &Lattice::getAllocator() {
453 return probeAllocator;
454}
455template <>
456SpecificBumpPtrAllocator<DriveNode> &Lattice::getAllocator() {
457 return driveAllocator;
458}
459template <>
460SpecificBumpPtrAllocator<SignalNode> &Lattice::getAllocator() {
461 return signalAllocator;
462}
463
464} // namespace
465
466#ifndef NDEBUG
467/// Print the lattice in human-readable form. Useful for debugging.
468void Lattice::dump(llvm::raw_ostream &os) {
469 // Helper functions to quickly come up with unique names for things.
473
474 auto blockName = [&](Block *block) {
475 unsigned id = blockNames.insert({block, blockNames.size()}).first->second;
476 return std::string("bb") + llvm::utostr(id);
477 };
478
479 auto memName = [&](DefSlot value) {
480 unsigned id =
481 memNames.insert({getSlot(value), memNames.size()}).first->second;
482 return std::string("mem") + llvm::utostr(id) +
483 (isDelayed(value) ? "#" : "");
484 };
485
486 auto defName = [&](Def *def) {
487 unsigned id = defNames.insert({def, defNames.size()}).first->second;
488 return std::string("def") + llvm::utostr(id);
489 };
490
491 // Ensure the blocks are named in the order they were created.
492 for (auto *node : nodes)
493 if (auto *entry = dyn_cast<BlockEntry>(node))
494 blockName(entry->block);
495
496 // Iterate over all block entry nodes.
497 os << "lattice {\n";
498 for (auto *node : nodes) {
499 auto *entry = dyn_cast<BlockEntry>(node);
500 if (!entry)
501 continue;
502
503 // Print the opening braces and predecessors for the block.
504 os << " " << blockName(entry->block) << ":";
505 if (entry->predecessors.empty()) {
506 os << " // no predecessors";
507 } else {
508 os << " // from";
509 for (auto *node : entry->predecessors)
510 os << " " << blockName(node->block);
511 }
512 os << "\n";
513
514 // Print all nodes following the block entry, up until the block exit.
515 auto *value = entry->valueAfter;
516 while (true) {
517 // Print the needed-def flag at this lattice point.
518 if (value->needed)
519 os << " -> need\n";
520 auto printDef = [&](bool delayed, Def *def) {
521 if (!def)
522 return;
523 os << " -> def (" << (delayed ? "delayed" : "blocking")
524 << ")=" << defName(def);
525 if (def->condition.isNever())
526 os << "[N]";
527 else if (def->condition.isAlways())
528 os << "[A]";
529 else
530 os << "[C]";
531 os << "\n";
532 };
533 printDef(false, value->blockingReachingDef);
534 printDef(true, value->delayedReachingDef);
535 if (isa<BlockExit>(value->nodeAfter))
536 break;
537
538 // Print the node.
539 if (auto *node = dyn_cast<ProbeNode>(value->nodeAfter))
540 os << " probe " << memName(blockingSlot(node->slot)) << "\n";
541 else if (auto *node = dyn_cast<DriveNode>(value->nodeAfter))
542 os << " drive " << memName(node->slot) << "\n";
543 else if (auto *node = dyn_cast<SignalNode>(value->nodeAfter))
544 os << " signal " << memName(node->getSlot()) << "\n";
545 else
546 os << " unknown\n";
547
548 // Advance to the next node.
549 value = cast<OpNode>(value->nodeAfter)->valueAfter;
550 }
551
552 // Print the closing braces and successors for the block.
553 auto *exit = cast<BlockExit>(value->nodeAfter);
554 if (isa<WaitOp>(exit->terminator))
555 os << " wait";
556 else if (exit->successors.empty())
557 os << " halt";
558 else
559 os << " goto";
560 for (auto *node : exit->successors)
561 os << " " << blockName(node->block);
562 if (exit->suspends)
563 os << " // suspends";
564 os << "\n";
565 }
566
567 // Dump the memories.
568 for (auto [mem, id] : memNames)
569 os << " mem" << id << ": " << mem << "\n";
570
571 os << "}\n";
572}
573#endif
574
575//===----------------------------------------------------------------------===//
576// Projection Utilities
577//===----------------------------------------------------------------------===//
578
579namespace {
580/// A single projection operation, together with the concrete value of the
581/// signal being projected into. This helper is useful to unpack a stack of
582/// projections to get the targeted value, change it, and then pack the stack
583/// back up into an updated value.
584struct Projection {
585 /// The projection operation.
586 Operation *op;
587 /// The value being projected into. This is not the op's target signal, but
588 /// rather the value of the op's target signal.
589 Value into;
590};
591} // namespace
592
593/// A stack of projection operations.
594using ProjectionStack = SmallVector<Projection>;
595
596/// Collect the `llhd.sig.*` projection ops between `fromSignal` and `toSlot`.
597/// The `fromSignal` value must be derived from `toSlot` through only
598/// `llhd.sig.*` operations. The result is a stack; the op producing
599/// `fromSignal` appears first in the vector, while the final op projecting into
600/// `toSlot` appears last in the vector.
601static ProjectionStack getProjections(Value fromSignal, Value toSlot) {
602 ProjectionStack stack;
603 while (fromSignal != toSlot) {
604 auto *op = cast<OpResult>(fromSignal).getOwner();
605 stack.push_back({op, Value()});
606 fromSignal = op->getOperand(0);
607 }
608 return stack;
609}
610
611/// Resolve a stack of projections by taking a value and descending into its
612/// subelements until the final value targeted by the projection stack remains,
613/// which is then returned. Also updates the `into` fields of the projections in
614/// the stack to represent the concrete value of intermediate projections. This
615/// allows a later `packProjections` to reconstruct the root value with the
616/// field targeted by the projection updated to a different value.
617static Value unpackProjections(OpBuilder &builder, Value value,
618 ProjectionStack &projections) {
619 for (auto &projection : llvm::reverse(projections)) {
620 projection.into = value;
621 value = TypeSwitch<Operation *, Value>(projection.op)
622 .Case<SigArrayGetOp>([&](auto op) {
623 return builder.createOrFold<hw::ArrayGetOp>(
624 op.getLoc(), value, op.getIndex());
625 })
626 .Case<SigStructExtractOp>([&](auto op) {
627 // SigStructExtractOp is used for both struct and union
628 // member access; use the appropriate HW extract op.
629 if (isa<hw::UnionType>(value.getType()))
630 return builder.createOrFold<hw::UnionExtractOp>(
631 op.getLoc(), value, op.getFieldAttr());
632 return builder.createOrFold<hw::StructExtractOp>(
633 op.getLoc(), value, op.getFieldAttr());
634 })
635 .Case<SigExtractOp>([&](auto op) {
636 auto type = cast<RefType>(op.getType()).getNestedType();
637 auto width = type.getIntOrFloatBitWidth();
638 return comb::createDynamicExtract(builder, op.getLoc(), value,
639 op.getLowBit(), width);
640 });
641 }
642 return value;
643}
644
645/// Undo a stack of projections by taking the value of the projected field and
646/// injecting it into the surrounding aggregate value that the projection
647/// targets. This requires the `into` fields to be set to the concrete value of
648/// the intermediate projections.
649///
650/// Example:
651/// ```
652/// auto projections = getProjections(...);
653/// auto fieldValue = unpackProjections(..., aggregateValue, projections);
654/// fieldValue = update(fieldValue);
655/// aggregateValue = packProjections(..., fieldValue, projections);
656/// ```
657static Value packProjections(OpBuilder &builder, Value value,
658 const ProjectionStack &projections) {
659 for (auto projection : projections) {
660 value = TypeSwitch<Operation *, Value>(projection.op)
661 .Case<SigArrayGetOp>([&](auto op) {
662 return builder.createOrFold<hw::ArrayInjectOp>(
663 op.getLoc(), projection.into, op.getIndex(), value);
664 })
665 .Case<SigStructExtractOp>([&](auto op) {
666 // For unions, creating a new value from a single field
667 // replaces the entire union (all fields share storage).
668 if (auto unionTy =
669 dyn_cast<hw::UnionType>(projection.into.getType()))
670 return builder.createOrFold<hw::UnionCreateOp>(
671 op.getLoc(), unionTy, op.getFieldAttr(), value);
672 return builder.createOrFold<hw::StructInjectOp>(
673 op.getLoc(), projection.into, op.getFieldAttr(), value);
674 })
675 .Case<SigExtractOp>([&](auto op) {
676 return comb::createDynamicInject(builder, op.getLoc(),
677 projection.into,
678 op.getLowBit(), value);
679 });
680 }
681 return value;
682}
683
684//===----------------------------------------------------------------------===//
685// Drive/Probe to SSA Value Promotion
686//===----------------------------------------------------------------------===//
687
688namespace {
689/// The main promoter forwarding drives to probes within a region.
690struct Promoter {
691 Promoter(Region &region) : region(region) {}
692 LogicalResult promote();
693
694 void findPromotableSlots();
695 Value resolveSlot(Value projectionOrSlot);
696 void populateSlotOps();
697
698 void captureAcrossWait();
699 void captureAcrossWait(Value value, ArrayRef<WaitOp> waitOps,
700 Liveness &liveness, DominanceInfo &dominance);
701
702 void constructLattice();
703 void propagateBackward();
704 void propagateBackward(LatticeNode *node);
705 void propagateForward();
706 void propagateForward(bool optimisticMerges, DominanceInfo &dominance);
707 void propagateForward(LatticeNode *node, bool optimisticMerges,
708 DominanceInfo &dominance);
709 void markDirty(LatticeNode *node);
710
711 void insertProbeBlocks();
712 void insertProbes();
713 void insertProbes(BlockEntry *node);
714
715 void insertDriveBlocks();
716 void insertDrives();
717 void insertDrives(BlockExit *node);
718 void insertDrives(DriveNode *node);
719
720 void resolveDefinitions();
721 void resolveDefinitions(ProbeNode *node);
722 void resolveDefinitions(DriveNode *node);
723 void resolveDefinitionValue(DriveNode *node);
724 void resolveDefinitionCondition(DriveNode *node);
725
726 void insertBlockArgs();
727 bool insertBlockArgs(BlockEntry *node);
728 void replaceValueWith(Value oldValue, Value newValue);
729
730 /// Remove a local `llhd.sig` op if all of its remaining users are drives or
731 /// projections.
732 void removeUnusedLocalSignal(SignalOp signalOp);
733
734 /// Run the lattice analysis and transformation pipeline for `currentSlot`.
735 void promoteSlot();
736
737 /// The region we are promoting in.
738 Region &region;
739
740 /// The slots we are promoting. Mostly `llhd.sig` ops in practice. This
741 /// establishes a deterministic order for slot allocations, such that
742 /// everything else in the pass can operate using unordered maps and sets.
743 SmallVector<Value> slots;
744 /// A mapping from projections to the root slot they are projecting into.
745 SmallDenseMap<Value, Value> projections;
746 /// A set of all promotable signal SSA values. This is the union of `slots`
747 /// and `projections` of those slots.
748 SmallDenseSet<Value> promotable;
749
750 /// The slot currently being analyzed and rewritten. The lattice and all
751 /// per-slot methods operate relative to this value.
752 Value currentSlot;
753
754 /// One `llhd.constant_time` op per block, shared by every drive the pass
755 /// inserts at that block's terminator regardless of which slot triggered
756 /// the insertion.
757 DenseMap<Block *, ConstantTimeOp> blockingTimeCache;
758 DenseMap<Block *, ConstantTimeOp> delayedTimeCache;
759
760 /// Lattice for the slot currently being promoted. Holds the needed-def
761 /// flag and the pair of reaching-def pointers at every program point, plus
762 /// the bump allocators that own the nodes/values/defs.
763 std::optional<Lattice> lattice;
764 /// A worklist of lattice nodes used within calls to `propagate*`.
765 SmallVector<LatticeNode *> dirtyNodes;
766
767 /// Helper to clean up unused ops.
768 UnusedOpPruner pruner;
769
770 /// Maps slot values to all ops that refer to them. The operation list is
771 /// laid out in the same order as a loop over blocks in the region.
772 DenseMap<Value, SmallVector<Operation *>> slotOps;
773};
774} // namespace
775
776LogicalResult Promoter::promote() {
777 if (region.empty())
778 return success();
779
780 findPromotableSlots();
781 captureAcrossWait();
782
783 // If there are no promotable slots we can still return after ensuring any
784 // values live across waits were captured as block arguments above. This
785 // keeps the pass semantics while allowing wait capturing to run
786 // independently of slot promotion.
787 if (slots.empty())
788 return success();
789
790 // Run the lattice analysis and rewrite once per slot. Each iteration gets
791 // its own fresh lattice with O(1)-sized state per program point, so the
792 // total cost is linear in the number of slots times the number of ops that
793 // contribute to that slot.
794 populateSlotOps();
795 for (auto slot : slots) {
796 currentSlot = slot;
797 promoteSlot();
798 }
799 currentSlot = {};
800
801 // Erase operations that have become unused.
802 pruner.eraseNow();
803
804 return success();
805}
806
807/// Run the lattice analysis and transformation pipeline for `currentSlot`.
808void Promoter::promoteSlot() {
809 assert(currentSlot && "currentSlot must be set before promoteSlot()");
810 lattice.emplace();
811 dirtyNodes.clear();
812
813 LLVM_DEBUG(llvm::dbgs() << "Promoting slot " << currentSlot << "\n");
814
815 constructLattice();
816 LLVM_DEBUG({
817 llvm::dbgs() << "Initial lattice:\n";
818 lattice->dump();
819 });
820
821 // Propagate the needed-def flag backward across the lattice.
822 propagateBackward();
823 LLVM_DEBUG({
824 llvm::dbgs() << "After backward propagation:\n";
825 lattice->dump();
826 });
827
828 // Insert probes wherever a def is needed for the first time.
829 insertProbeBlocks();
830 insertProbes();
831 LLVM_DEBUG({
832 llvm::dbgs() << "After probe insertion:\n";
833 lattice->dump();
834 });
835
836 // Propagate the reaching-def pointers forward across the lattice.
837 propagateForward();
838 LLVM_DEBUG({
839 llvm::dbgs() << "After forward propagation:\n";
840 lattice->dump();
841 });
842
843 // Resolve definitions.
844 resolveDefinitions();
845
846 // Insert drives wherever a reaching def can no longer propagate.
847 insertDriveBlocks();
848 insertDrives();
849 LLVM_DEBUG({
850 llvm::dbgs() << "After def resolution and drive insertion:\n";
851 lattice->dump();
852 });
853
854 // Insert the necessary block arguments.
855 insertBlockArgs();
856
857 // If this slot is a region-local signal declaration, try to drop it when
858 // no probes remain.
859 if (auto signalOp = currentSlot.getDefiningOp<SignalOp>())
860 if (signalOp->getParentRegion() == &region)
861 removeUnusedLocalSignal(signalOp);
862
863 // Release the lattice so the bump allocators free their slabs before the
864 // next slot runs.
865 lattice.reset();
866}
867
868/// Identify any promotable slots probed or driven under the current region.
869void Promoter::findPromotableSlots() {
870 SmallPtrSet<Value, 8> seenSlots;
871 SmallPtrSet<Operation *, 8> checkedUsers;
872 SmallVector<Operation *, 8> userWorklist;
873
874 region.walk([&](Operation *op) {
875 for (auto operand : op->getOperands()) {
876 if (!seenSlots.insert(operand).second)
877 continue;
878
879 // We can only promote probes and drives on a locally-defined signal.
880 // Other signals, such as the ones brought into a module through a port,
881 // have an unknown aliasing relationship with the other ports.
882 if (!operand.getDefiningOp<llhd::SignalOp>())
883 continue;
884
885 // Ensure the slot is not used in any way we cannot reason about.
886 bool hasProjection = false;
887 bool hasBlockingDrive = false;
888 bool hasDeltaDrive = false;
889 auto checkUser = [&](Operation *user) -> bool {
890 // We don't support nested probes and drives.
891 if (region.isProperAncestor(user->getParentRegion()))
892 return false;
893 // Ignore uses outside of the region.
894 if (user->getParentRegion() != &region)
895 return true;
896 // Projection operations are okay, as long as nested projections
897 // stay in the same block. Cross-block nested projections would break
898 // during promotion because the projection chain gets severed when
899 // Mem2Reg rewrites signal references into SSA block arguments.
900 if (isa<SigArrayGetOp, SigExtractOp, SigStructExtractOp>(user)) {
901 hasProjection = true;
902 for (auto *projectionUser : user->getUsers()) {
903 if (isa<SigArrayGetOp, SigExtractOp, SigStructExtractOp>(
904 projectionUser) &&
905 projectionUser->getBlock() != user->getBlock())
906 return false;
907 hasBlockingDrive |= isBlockingDrive(projectionUser);
908 hasDeltaDrive |= isDeltaDrive(projectionUser);
909 if (checkedUsers.insert(projectionUser).second)
910 userWorklist.push_back(projectionUser);
911 }
912 projections.insert({user->getResult(0), operand});
913 return true;
914 }
915 hasBlockingDrive |= isBlockingDrive(user);
916 hasDeltaDrive |= isDeltaDrive(user);
917 return isa<ProbeOp>(user) || isBlockingDrive(user) ||
918 isDeltaDrive(user);
919 };
920 checkedUsers.clear();
921 if (!llvm::all_of(operand.getUsers(), [&](auto *user) {
922 auto allOk = true;
923 if (checkedUsers.insert(user).second)
924 userWorklist.push_back(user);
925 while (!userWorklist.empty() && allOk)
926 allOk &= checkUser(userWorklist.pop_back_val());
927 userWorklist.clear();
928 return allOk;
929 }))
930 continue;
931
932 // Don't promote slots that have projections and a mix of blocking and
933 // delta drives. A blocking drive erases the delayed reaching definition,
934 // which leaves delta projection drives without a reaching definition.
935 if (hasProjection && hasBlockingDrive && hasDeltaDrive)
936 continue;
937
938 // Mem2Reg needs to be able to materialize integer constants for promoted
939 // slots, which requires the bit width to fit in an IntegerType. Skip
940 // signals that are too wide.
941 auto bitWidth = hw::getBitWidth(getStoredType(operand));
942 if (bitWidth < 0 ||
943 static_cast<unsigned>(bitWidth) > IntegerType::kMaxWidth)
944 continue;
945
946 slots.push_back(operand);
947 }
948 });
949
950 // Populate `promotable` with the slots and projections we are promoting.
951 promotable.insert(slots.begin(), slots.end());
952 for (auto [projection, slot] : llvm::make_early_inc_range(projections))
953 if (!promotable.contains(slot))
954 projections.erase(projection);
955 for (auto [projection, slot] : projections)
956 promotable.insert(projection);
957
958 LLVM_DEBUG(llvm::dbgs() << "Found " << slots.size() << " promotable slots, "
959 << promotable.size() << " promotable values\n");
960}
961
962/// Resolve SSA values in `projection` to the `slot` they are projecting into.
963/// Simply returns the given value if it is not in `projections`.
964Value Promoter::resolveSlot(Value projectionOrSlot) {
965 if (auto slot = projections.lookup(projectionOrSlot))
966 return slot;
967 return projectionOrSlot;
968}
969
970/// Explicitly capture any probes that are live across an `llhd.wait` as block
971/// arguments and destination operand of that wait. This ensures that replacing
972/// the probe with a reaching definition later on will capture the value of the
973/// reaching definition before the wait.
974void Promoter::captureAcrossWait() {
975 if (region.hasOneBlock())
976 return;
977
978 SmallVector<WaitOp> waitOps;
979 for (auto &block : region)
980 if (auto waitOp = dyn_cast<WaitOp>(block.getTerminator()))
981 waitOps.push_back(waitOp);
982
983 DominanceInfo dominance(region.getParentOp());
984 Liveness liveness(region.getParentOp());
985
986 llvm::DenseSet<Value> alreadyCaptured;
987
988 auto isDefinedInRegion = [&](Value v) {
989 return v.getParentRegion() == &region;
990 };
991
992 for (auto waitOp : waitOps) {
993 Block *waitBlock = waitOp->getBlock();
994 const auto &liveOutValues = liveness.getLiveOut(waitBlock);
995
996 for (Value v : liveOutValues) {
997 if (!isDefinedInRegion(v))
998 continue;
999
1000 if (!alreadyCaptured.insert(v).second)
1001 continue;
1002
1003 captureAcrossWait(v, waitOps, liveness, dominance);
1004 }
1005 }
1006}
1007
1008/// Add a probe as block argument to a list of wait ops and update uses of the
1009/// probe to use the added block arguments as appropriate. This may insert
1010/// additional block arguments in case the probe and added block arguments both
1011/// reach the same block.
1012void Promoter::captureAcrossWait(Value value, ArrayRef<WaitOp> waitOps,
1013 Liveness &liveness, DominanceInfo &dominance) {
1014 LLVM_DEBUG({
1015 llvm::dbgs() << "Capture " << value << "\n";
1016 for (auto waitOp : waitOps)
1017 llvm::dbgs() << "- Across " << waitOp << "\n";
1018 });
1019
1020 // Calculate the merge points for this probe once it gets promoted to block
1021 // arguments across the wait ops.
1022 auto &domTree = dominance.getDomTree(&region);
1023 llvm::IDFCalculatorBase<Block, false> idfCalculator(domTree);
1024
1025 // Calculate the set of blocks which will define this probe as a distinct
1026 // value.
1027 SmallPtrSet<Block *, 4> definingBlocks;
1028 definingBlocks.insert(value.getParentBlock());
1029 for (auto waitOp : waitOps)
1030 definingBlocks.insert(waitOp.getDest());
1031 idfCalculator.setDefiningBlocks(definingBlocks);
1032
1033 // Calculate where the probe is live.
1034 SmallPtrSet<Block *, 16> liveInBlocks;
1035 for (auto &block : region)
1036 if (liveness.getLiveness(&block)->isLiveIn(value))
1037 liveInBlocks.insert(&block);
1038 idfCalculator.setLiveInBlocks(liveInBlocks);
1039
1040 // Calculate the merge points where we will have to insert block arguments for
1041 // this probe.
1042 SmallVector<Block *> mergePointsVec;
1043 idfCalculator.calculate(mergePointsVec);
1044 SmallPtrSet<Block *, 16> mergePoints(mergePointsVec.begin(),
1045 mergePointsVec.end());
1046 for (auto waitOp : waitOps)
1047 mergePoints.insert(waitOp.getDest());
1048 LLVM_DEBUG(llvm::dbgs() << "- " << mergePoints.size() << " merge points\n");
1049
1050 // Perform a depth-first search starting at the block containing the probe,
1051 // which dominates all its uses. When we encounter a block that is a merge
1052 // point, insert a block argument.
1053 struct WorklistItem {
1054 DominanceInfoNode *domNode;
1055 Value reachingDef;
1056 };
1057 SmallVector<WorklistItem> worklist;
1058 worklist.push_back({domTree.getNode(value.getParentBlock()), value});
1059
1060 while (!worklist.empty()) {
1061 auto item = worklist.pop_back_val();
1062 auto *block = item.domNode->getBlock();
1063
1064 // If this block is a merge point, insert a block argument for the probe.
1065 if (mergePoints.contains(block))
1066 item.reachingDef = block->addArgument(value.getType(), value.getLoc());
1067
1068 // Replace any uses of the probe in this block with the current reaching
1069 // definition.
1070 for (auto &op : *block)
1071 op.replaceUsesOfWith(value, item.reachingDef);
1072
1073 // If the terminator of this block branches to a merge point, add the
1074 // current reaching definition as a destination operand.
1075 if (auto branchOp = dyn_cast<BranchOpInterface>(block->getTerminator())) {
1076 for (auto &blockOperand : branchOp->getBlockOperands())
1077 if (mergePoints.contains(blockOperand.get()))
1078 branchOp.getSuccessorOperands(blockOperand.getOperandNumber())
1079 .append(item.reachingDef);
1080 } else if (auto waitOp = dyn_cast<WaitOp>(block->getTerminator())) {
1081 if (mergePoints.contains(waitOp.getDest()))
1082 waitOp.getDestOperandsMutable().append(item.reachingDef);
1083 }
1084
1085 for (auto *child : item.domNode->children())
1086 worklist.push_back({child, item.reachingDef});
1087 }
1088}
1089
1090//===----------------------------------------------------------------------===//
1091// Lattice Construction and Propagation
1092//===----------------------------------------------------------------------===//
1093
1094/// Preprocess all ops in the current region to populate `slotOps`.
1095///
1096/// This avoids a repeated linear walk of the region in every call to
1097/// `constructLattice`.
1098void Promoter::populateSlotOps() {
1099 for (auto &block : region) {
1100 for (auto &op : block.without_terminator()) {
1101 Value slot = TypeSwitch<Operation *, Value>(&op)
1102 .Case<ProbeOp, DriveOp>([&](auto op) {
1103 if (promotable.contains(op.getSignal()))
1104 return resolveSlot(op.getSignal());
1105 return Value();
1106 })
1107 .Case([&](SignalOp op) { return op.getResult(); })
1108 .Default([&](Operation *) { return Value(); });
1109 if (slot)
1110 slotOps[slot].push_back(&op);
1111 }
1112 }
1113}
1114
1115/// Populate the lattice with nodes and values corresponding to the blocks and
1116/// to the operations in the region that touch `currentSlot`. Ops that touch
1117/// other slots are skipped entirely — they're transparent to this slot's
1118/// analysis.
1119void Promoter::constructLattice() {
1120 assert(currentSlot && "constructLattice requires currentSlot");
1121
1122 // Get all operations that correspond to the current slot in the region. These
1123 // are arranged in the same order as a region walk.
1124 ArrayRef<Operation *> currentSlotOps = slotOps[currentSlot];
1125
1126 // Create entry nodes for each block.
1128 for (auto &block : region) {
1129 auto *entry =
1130 lattice->createNode<BlockEntry>(&block, lattice->createValue());
1131 blockEntries.insert({&block, entry});
1132 }
1133
1134 // Create nodes for each operation that touches `currentSlot`.
1135 for (auto &block : region) {
1136 auto *valueBefore = blockEntries.lookup(&block)->valueAfter;
1137
1138 // Handle operations. All operations within this block will exist in the
1139 // prefix of currentSlotOps.
1140 ArrayRef<Operation *> blockOps = currentSlotOps.take_while(
1141 [&](Operation *op) { return op->getBlock() == &block; });
1142 currentSlotOps = currentSlotOps.drop_front(blockOps.size());
1143
1144 for (Operation *op : blockOps) {
1145 // Handle probes.
1146 if (auto probeOp = dyn_cast<ProbeOp>(op)) {
1147 if (!promotable.contains(probeOp.getSignal()))
1148 continue;
1149 if (resolveSlot(probeOp.getSignal()) != currentSlot)
1150 continue;
1151 auto *node = lattice->createNode<ProbeNode>(
1152 probeOp, currentSlot, valueBefore, lattice->createValue());
1153 valueBefore = node->valueAfter;
1154 continue;
1155 }
1156
1157 // Handle drives.
1158 if (auto driveOp = dyn_cast<DriveOp>(op)) {
1159 if (!isBlockingDrive(op) && !isDeltaDrive(op))
1160 continue;
1161 if (!promotable.contains(driveOp.getSignal()))
1162 continue;
1163 if (resolveSlot(driveOp.getSignal()) != currentSlot)
1164 continue;
1165 auto condition = DriveCondition::always();
1166 if (auto enable = driveOp.getEnable())
1167 condition = DriveCondition::conditional(enable);
1168 // Drives that target a slot directly provide the driven value as a
1169 // definition for the slot. Drives to projections have their value
1170 // calculated later on.
1171 auto *def =
1172 driveOp.getSignal() == currentSlot
1173 ? lattice->createDef(driveOp.getValue(), condition)
1174 : lattice->createDef(driveOp->getBlock(),
1175 getStoredType(currentSlot), condition);
1176 auto *node = lattice->createNode<DriveNode>(
1177 driveOp, currentSlot, def, valueBefore, lattice->createValue());
1178 valueBefore = node->valueAfter;
1179 continue;
1180 }
1181
1182 // Handle local signals. Only the current slot's own SignalOp, if it
1183 // lives inside this region, contributes a SignalNode.
1184 if (auto signalOp = dyn_cast<SignalOp>(op)) {
1185 if (signalOp.getResult() != currentSlot)
1186 continue;
1187 auto *def =
1188 lattice->createDef(signalOp.getInit(), DriveCondition::never());
1189 auto *node = lattice->createNode<SignalNode>(signalOp, def, valueBefore,
1190 lattice->createValue());
1191 valueBefore = node->valueAfter;
1192 continue;
1193 }
1194 }
1195
1196 // Create the exit node for the block.
1197 auto *exit = lattice->createNode<BlockExit>(&block, valueBefore);
1198 for (auto *otherBlock : exit->terminator->getSuccessors()) {
1199 auto *otherEntry = blockEntries.lookup(otherBlock);
1200 exit->successors.push_back(otherEntry);
1201 otherEntry->predecessors.push_back(exit);
1202 }
1203 }
1204}
1205
1206/// Propagate the lattice values backwards against control flow until a fixed
1207/// point is reached.
1208void Promoter::propagateBackward() {
1209 for (auto *node : lattice->nodes)
1210 propagateBackward(node);
1211 SmallVector<LatticeNode *> nodes;
1212 while (!dirtyNodes.empty()) {
1213 std::swap(dirtyNodes, nodes);
1214 for (auto *node : nodes) {
1215 node->dirty = false;
1216 propagateBackward(node);
1217 }
1218 nodes.clear();
1219 }
1220}
1221
1222/// Propagate the lattice value after a node backward to the value before a
1223/// node, updating the `needed` flag at the incoming value.
1224void Promoter::propagateBackward(LatticeNode *node) {
1225 auto update = [&](LatticeValue *value, bool needed) {
1226 if (value->needed != needed) {
1227 value->needed = needed;
1228 markDirty(value->nodeBefore);
1229 }
1230 };
1231
1232 // Probes need a definition for the probed slot to be available.
1233 if (auto *probe = dyn_cast<ProbeNode>(node)) {
1234 update(probe->valueBefore, true);
1235 return;
1236 }
1237
1238 // Blocking unconditional drives to an entire slot kill the need for a
1239 // definition to be available, since they provide a definition themselves.
1240 // Conditional drives propagate the need for a definition, since they have to
1241 // forward an incoming reaching def in case they are disabled. Drives to
1242 // projections need a definition to be available such that they can partially
1243 // update it.
1244 if (auto *drive = dyn_cast<DriveNode>(node)) {
1245 bool needed = drive->valueAfter->needed;
1246 if (drive->drivesProjection())
1247 needed = true;
1248 else if (!isDelayed(drive->slot) && !drive->getDriveOp().getEnable())
1249 needed = false;
1250 update(drive->valueBefore, needed);
1251 return;
1252 }
1253
1254 // Local signal declarations kill the need for a definition to be available,
1255 // since the op is the first time a signal becomes available and the op
1256 // provides an initial value as a definition.
1257 if (isa<SignalNode>(node)) {
1258 auto *signal = cast<SignalNode>(node);
1259 update(signal->valueBefore, false);
1260 return;
1261 }
1262
1263 // Block entries simply trigger updates to all their predecessors.
1264 if (auto *entry = dyn_cast<BlockEntry>(node)) {
1265 for (auto *predecessor : entry->predecessors)
1266 markDirty(predecessor);
1267 return;
1268 }
1269
1270 // Block exits merge any needed definitions from their successors.
1271 if (auto *exit = dyn_cast<BlockExit>(node)) {
1272 if (exit->suspends)
1273 return;
1274 bool needed = false;
1275 for (auto *successor : exit->successors)
1276 needed |= successor->valueAfter->needed;
1277 update(exit->valueBefore, needed);
1278 return;
1279 }
1280
1281 assert(false && "unhandled node in backward propagation");
1282}
1283
1284void Promoter::propagateForward() {
1285 DominanceInfo dominance(region.getParentOp());
1286 propagateForward(true, dominance);
1287 propagateForward(false, dominance);
1288}
1289
1290/// Propagate the lattice values forwards along with control flow until a fixed
1291/// point is reached. If `optimisticMerges` is true, block entry points
1292/// propagate definitions from their predecessors into the block without
1293/// creating a merge definition even if the definition is not available in all
1294/// predecessors. This is overly optimistic, but initially helps definitions
1295/// propagate through loop structures. If `optimisticMerges` is false, block
1296/// entry points create merge definitions for definitions that are not available
1297/// in all predecessors.
1298void Promoter::propagateForward(bool optimisticMerges,
1299 DominanceInfo &dominance) {
1300 for (auto *node : lattice->nodes)
1301 propagateForward(node, optimisticMerges, dominance);
1302 SmallVector<LatticeNode *> nodes;
1303 while (!dirtyNodes.empty()) {
1304 std::swap(dirtyNodes, nodes);
1305 for (auto *node : nodes) {
1306 node->dirty = false;
1307 propagateForward(node, optimisticMerges, dominance);
1308 }
1309 nodes.clear();
1310 }
1311}
1312
1313/// Propagate the lattice value before a node forward to the value after a
1314/// node, updating the blocking and delayed reaching-def pointers at the
1315/// outgoing value.
1316void Promoter::propagateForward(LatticeNode *node, bool optimisticMerges,
1317 DominanceInfo &dominance) {
1318 auto update = [&](LatticeValue *value, Def *blocking, Def *delayed) {
1319 if (value->blockingReachingDef != blocking ||
1320 value->delayedReachingDef != delayed) {
1321 value->blockingReachingDef = blocking;
1322 value->delayedReachingDef = delayed;
1323 markDirty(value->nodeAfter);
1324 }
1325 };
1326
1327 // Probes simply propagate any reaching defs.
1328 if (auto *probe = dyn_cast<ProbeNode>(node)) {
1329 update(probe->valueAfter, probe->valueBefore->blockingReachingDef,
1330 probe->valueBefore->delayedReachingDef);
1331 return;
1332 }
1333
1334 // Drives propagate the driven value as a reaching def. Blocking drives kill
1335 // earlier non-blocking drives. This reflects Verilog and VHDL behaviour,
1336 // where a drive sequence like
1337 //
1338 // a <= #10ns 42; // A
1339 // a <= 43; // B
1340 // a = 44; // C
1341 //
1342 // would see (B) override (A), because it happens earlier, and (C) override
1343 // (B), because it in turn happens earlier.
1344 if (auto *drive = dyn_cast<DriveNode>(node)) {
1345 Def *blocking = drive->valueBefore->blockingReachingDef;
1346 Def *delayed = drive->valueBefore->delayedReachingDef;
1347 bool driveDelayed = isDelayed(drive->slot);
1348
1349 // If the drive is conditional or driving a projection of a slot, merge any
1350 // incoming reaching def into the reaching def created by the drive. This is
1351 // necessary since a conditional drive following an unconditional drive may
1352 // have to insert a multiplexer to forward to subsequent probes.
1353 if (drive->drivesProjection() || drive->getDriveOp().getEnable()) {
1354 Def *inDef = driveDelayed ? delayed : blocking;
1355 if (inDef) {
1356 if (drive->def->value != inDef->value)
1357 drive->def->value = {};
1358 if (inDef->condition.isAlways() || drive->def->condition.isAlways())
1359 drive->def->condition = DriveCondition::always();
1360 else if (drive->def->condition != inDef->condition)
1361 drive->def->condition = DriveCondition::conditional();
1362 }
1363 }
1364
1365 if (driveDelayed) {
1366 delayed = drive->def;
1367 } else {
1368 blocking = drive->def;
1369 // A blocking drive kills any pending delta drive at the same slot.
1370 delayed = nullptr;
1371 }
1372 update(drive->valueAfter, blocking, delayed);
1373 return;
1374 }
1375
1376 // Signals propagate their initial value as a reaching def. They also kill
1377 // any earlier delayed definition for the same slot.
1378 if (auto *signal = dyn_cast<SignalNode>(node)) {
1379 update(signal->valueAfter, signal->def, nullptr);
1380 return;
1381 }
1382
1383 // Block entry points merge reaching defs from their predecessors, creating
1384 // new merge defs where predecessors disagree.
1385 if (auto *entry = dyn_cast<BlockEntry>(node)) {
1386 // Inserted probes supersede anything coming from predecessors, for both
1387 // the blocking and delayed flavors.
1388 if (entry->insertedProbe) {
1389 update(entry->valueAfter, entry->insertedProbe, entry->insertedProbe);
1390 return;
1391 }
1392
1393 // Do not propagate reaching defs past a block whose defining op does not
1394 // dominate this entry, or whose predecessor set contains any suspending
1395 // block.
1396 Block *slotBlock = currentSlot.getDefiningOp()->getBlock();
1397 bool slotDominates = dominance.dominates(slotBlock, entry->block);
1398 bool anyPredSuspends =
1399 llvm::any_of(entry->predecessors, [](auto *p) { return p->suspends; });
1400
1401 auto mergeFlavor = [&](bool delayed) -> Def * {
1402 if (!slotDominates || anyPredSuspends)
1403 return nullptr;
1404
1405 // Bail early if no predecessor provides a def for this flavor.
1406 if (llvm::all_of(entry->predecessors, [&](auto *p) {
1407 return !p->valueBefore->getReachingDef(delayed);
1408 }))
1409 return nullptr;
1410
1411 // Walk predecessors to determine whether all agree on the same def and
1412 // the same drive condition.
1413 Def *common = nullptr;
1414 DriveCondition cond = DriveCondition::never();
1415 bool first = true;
1416 for (auto *pred : entry->predecessors) {
1417 Def *predDef = pred->valueBefore->getReachingDef(delayed);
1418 if (!predDef && optimisticMerges)
1419 continue;
1420 DriveCondition predCond =
1421 predDef ? predDef->condition : DriveCondition::never();
1422 if (first) {
1423 common = predDef;
1424 cond = predCond;
1425 first = false;
1426 continue;
1427 }
1428 if (common != predDef)
1429 common = nullptr;
1430 if (cond != predCond)
1431 cond = DriveCondition::conditional();
1432 }
1433
1434 if (first)
1435 return nullptr;
1436
1437 // Once a merge def exists at this entry, keep returning it. The value
1438 // here can otherwise flip between the cached `merged` (when
1439 // predecessors disagree) and `common` (when they happen to coincide on
1440 // a non-null def via back-edge propagation), preventing the fixpoint
1441 // loop from converging on cyclic CFGs.
1442 Def *&merged = delayed ? entry->delayedMerged : entry->blockingMerged;
1443 if (merged) {
1444 merged->condition = cond;
1445 return merged;
1446 }
1447
1448 // If all predecessors agree on the same concrete def, use it as-is.
1449 if (common)
1450 return common;
1451
1452 // Otherwise create a merge def for this disagreement.
1453 auto slot =
1454 delayed ? delayedSlot(currentSlot) : blockingSlot(currentSlot);
1455 merged = lattice->createDef(entry->block, getStoredType(slot), cond);
1456 return merged;
1457 };
1458
1459 Def *newBlocking = mergeFlavor(/*delayed=*/false);
1460 Def *newDelayed = mergeFlavor(/*delayed=*/true);
1461 update(entry->valueAfter, newBlocking, newDelayed);
1462 return;
1463 }
1464
1465 // Block exits simply trigger updates to all their successors.
1466 if (auto *exit = dyn_cast<BlockExit>(node)) {
1467 for (auto *successor : exit->successors)
1468 markDirty(successor);
1469 return;
1470 }
1471
1472 assert(false && "unhandled node in forward propagation");
1473}
1474
1475/// Mark a lattice node to be updated during propagation.
1476void Promoter::markDirty(LatticeNode *node) {
1477 assert(node);
1478 if (node->dirty)
1479 return;
1480 node->dirty = true;
1481 dirtyNodes.push_back(node);
1482}
1483
1484//===----------------------------------------------------------------------===//
1485// Drive/Probe Insertion
1486//===----------------------------------------------------------------------===//
1487
1488/// Insert additional probe blocks where needed. This can happen if a definition
1489/// is needed in a block which has a suspending and non-suspending predecessor.
1490/// In that case we would like to insert probes in the predecessor blocks, but
1491/// cannot do so because of the suspending predecessor.
1492void Promoter::insertProbeBlocks() {
1493 // Find predecessor/successor edges where the current slot is needed at the
1494 // successor but not carried past one of the successor's predecessors. An
1495 // extra probe block is inserted on such edges.
1496 SmallDenseSet<std::pair<BlockExit *, BlockEntry *>, 1> worklist;
1497 for (auto *node : lattice->nodes) {
1498 auto *entry = dyn_cast<BlockEntry>(node);
1499 if (!entry || !entry->valueAfter->needed)
1500 continue;
1501 unsigned numIncoming = 0;
1502 for (auto *predecessor : entry->predecessors)
1503 if (predecessor->valueBefore->needed)
1504 ++numIncoming;
1505 if (numIncoming == 0 || numIncoming == entry->predecessors.size())
1506 continue;
1507 for (auto *predecessor : entry->predecessors)
1508 if (!predecessor->valueBefore->needed)
1509 worklist.insert({predecessor, entry});
1510 }
1511
1512 // Insert probe blocks after all blocks we have identified.
1513 for (auto [predecessor, successor] : worklist) {
1514 LLVM_DEBUG(llvm::dbgs() << "- Inserting probe block towards " << successor
1515 << " after " << *predecessor->terminator << "\n");
1516 OpBuilder builder(predecessor->terminator);
1517 auto *newBlock = builder.createBlock(successor->block);
1518 for (auto oldArg : successor->block->getArguments())
1519 newBlock->addArgument(oldArg.getType(), oldArg.getLoc());
1520 cf::BranchOp::create(builder, predecessor->terminator->getLoc(),
1521 successor->block, newBlock->getArguments());
1522 for (auto &blockOp : predecessor->terminator->getBlockOperands())
1523 if (blockOp.get() == successor->block)
1524 blockOp.set(newBlock);
1525
1526 // Create new nodes in the lattice for the added block.
1527 auto *value = lattice->createValue();
1528 value->needed = successor->valueAfter->needed;
1529 auto *newEntry = lattice->createNode<BlockEntry>(newBlock, value);
1530 auto *newExit = lattice->createNode<BlockExit>(newBlock, value);
1531 newEntry->predecessors.push_back(predecessor);
1532 newExit->successors.push_back(successor);
1533 llvm::replace(successor->predecessors, predecessor, newExit);
1534 llvm::replace(predecessor->successors, successor, newEntry);
1535 }
1536}
1537
1538/// Insert probes wherever a definition is needed for the first time. This is
1539/// the case in the entry block, after any suspensions, and after operations
1540/// that have unknown effects on memory slots.
1541void Promoter::insertProbes() {
1542 for (auto *node : lattice->nodes) {
1543 if (auto *entry = dyn_cast<BlockEntry>(node))
1544 insertProbes(entry);
1545 }
1546}
1547
1548/// Insert a probe at the beginning of the block for the current slot, if it
1549/// is needed here but not already provided by all predecessors.
1550void Promoter::insertProbes(BlockEntry *node) {
1551 if (!node->valueAfter->needed)
1552 return;
1553 if (!node->predecessors.empty() &&
1554 llvm::all_of(node->predecessors, [](auto *predecessor) {
1555 return predecessor->valueBefore->needed;
1556 }))
1557 return;
1558 LLVM_DEBUG(llvm::dbgs() << "- Inserting probe for " << currentSlot
1559 << " in block " << node->block << "\n");
1560 auto builder = OpBuilder::atBlockBegin(node->block);
1561 // If the slot is defined in this same block, insert after its defining op.
1562 if (Operation *op = currentSlot.getDefiningOp())
1563 if (op->getBlock() == node->block)
1564 builder.setInsertionPointAfterValue(currentSlot);
1565 auto value = ProbeOp::create(builder, currentSlot.getLoc(), currentSlot);
1566 auto *def = lattice->createDef(value, DriveCondition::never());
1567 node->insertedProbe = def;
1568}
1569
1570/// Insert additional drive blocks where needed. This can happen if a definition
1571/// continues into some of a block's successors, but not all of them.
1572void Promoter::insertDriveBlocks() {
1573 // Find exit/successor edges where the current slot reaches the exit with a
1574 // non-never drive condition but isn't propagated through all successors.
1575 // Test the blocking and delayed flavors independently.
1576 auto partialAt = [](LatticeValue *before, LatticeValue *after, bool delayed,
1577 unsigned totalSuccessors) {
1578 Def *def = before->getReachingDef(delayed);
1579 if (!def || def->condition.isNever())
1580 return false;
1581 (void)totalSuccessors;
1582 return !after->getReachingDef(delayed);
1583 };
1584
1585 SmallDenseSet<std::pair<BlockExit *, BlockEntry *>, 1> worklist;
1586 for (auto *node : lattice->nodes) {
1587 auto *exit = dyn_cast<BlockExit>(node);
1588 if (!exit)
1589 continue;
1590 // A slot is "partial" on the (exit, successor) edge if the reaching def
1591 // at the exit exists and has a usable condition, but doesn't flow into
1592 // that particular successor. Compute per flavor.
1593 for (bool delayed : {false, true}) {
1594 Def *def = exit->valueBefore->getReachingDef(delayed);
1595 if (!def || def->condition.isNever())
1596 continue;
1597 unsigned numContinues = 0;
1598 for (auto *successor : exit->successors)
1599 if (successor->valueAfter->getReachingDef(delayed))
1600 ++numContinues;
1601 if (numContinues == 0 || numContinues == exit->successors.size())
1602 continue;
1603 for (auto *successor : exit->successors)
1604 if (partialAt(exit->valueBefore, successor->valueAfter, delayed,
1605 exit->successors.size()))
1606 worklist.insert({exit, successor});
1607 }
1608 }
1609
1610 // Insert drive blocks before all blocks we have identified.
1611 for (auto [predecessor, successor] : worklist) {
1612 LLVM_DEBUG(llvm::dbgs() << "- Inserting drive block towards " << successor
1613 << " after " << *predecessor->terminator << "\n");
1614 OpBuilder builder(predecessor->terminator);
1615 auto *newBlock = builder.createBlock(successor->block);
1616 for (auto oldArg : successor->block->getArguments())
1617 newBlock->addArgument(oldArg.getType(), oldArg.getLoc());
1618 cf::BranchOp::create(builder, predecessor->terminator->getLoc(),
1619 successor->block, newBlock->getArguments());
1620 for (auto &blockOp : predecessor->terminator->getBlockOperands())
1621 if (blockOp.get() == successor->block)
1622 blockOp.set(newBlock);
1623
1624 // Create new nodes in the lattice for the added block. The new block
1625 // carries the same needed flag as its successor and the same reaching
1626 // defs as its predecessor.
1627 auto *value = lattice->createValue();
1628 value->needed = successor->valueAfter->needed;
1629 value->blockingReachingDef = predecessor->valueBefore->blockingReachingDef;
1630 value->delayedReachingDef = predecessor->valueBefore->delayedReachingDef;
1631 auto *newEntry = lattice->createNode<BlockEntry>(newBlock, value);
1632 auto *newExit = lattice->createNode<BlockExit>(newBlock, value);
1633 newEntry->predecessors.push_back(predecessor);
1634 newExit->successors.push_back(successor);
1635 llvm::replace(successor->predecessors, predecessor, newExit);
1636 llvm::replace(predecessor->successors, successor, newEntry);
1637 }
1638}
1639
1640/// Insert drives wherever a reaching definition can no longer propagate. This
1641/// is the before any suspensions and before operations that have unknown
1642/// effects on memory slots.
1643void Promoter::insertDrives() {
1644 for (auto *node : lattice->nodes) {
1645 if (auto *exit = dyn_cast<BlockExit>(node))
1646 insertDrives(exit);
1647 else if (auto *drive = dyn_cast<DriveNode>(node))
1648 insertDrives(drive);
1649 }
1650}
1651
1652/// Insert drives at block terminators for definitions that do not propagate
1653/// into successors.
1654void Promoter::insertDrives(BlockExit *node) {
1655 auto builder = OpBuilder::atBlockTerminator(node->block);
1656
1657 // Reuse the cached `llhd.constant_time` op for this block, or create one
1658 // before the terminator on first use. Cached across slots so we end up
1659 // with one constant-time op per block exit, not one per promoted slot.
1660 auto getTime = [&](bool delta) -> ConstantTimeOp {
1661 auto &cached = (delta ? delayedTimeCache : blockingTimeCache)[node->block];
1662 if (!cached)
1663 cached = ConstantTimeOp::create(builder, node->terminator->getLoc(), 0,
1664 "ns", delta ? 1 : 0, delta ? 0 : 1);
1665 return cached;
1666 };
1667
1668 auto insertDriveForSlot = [&](bool delayed) {
1669 Def *reachingDef = node->valueBefore->getReachingDef(delayed);
1670 if (!reachingDef || reachingDef->condition.isNever())
1671 return;
1672 if (!node->suspends && !node->successors.empty() &&
1673 llvm::all_of(node->successors, [&](auto *successor) {
1674 return successor->valueAfter->getReachingDef(delayed) != nullptr;
1675 }))
1676 return;
1677 LLVM_DEBUG(llvm::dbgs() << "- Inserting drive for " << currentSlot << " "
1678 << (delayed ? "(delayed)" : "(blocking)")
1679 << " before " << *node->terminator << "\n");
1680 auto time = getTime(delayed);
1681 auto value = reachingDef->getValueOrPlaceholder();
1682 auto enable = reachingDef->condition.isConditional()
1683 ? reachingDef->getConditionOrPlaceholder()
1684 : Value{};
1685 DriveOp::create(builder, currentSlot.getLoc(), currentSlot, value, time,
1686 enable);
1687 };
1688
1689 insertDriveForSlot(/*delayed=*/false);
1690 insertDriveForSlot(/*delayed=*/true);
1691}
1692
1693/// Remove drives to the current slot. These have been replaced with new
1694/// drives at block exits.
1695void Promoter::insertDrives(DriveNode *node) {
1696 LLVM_DEBUG(llvm::dbgs() << "- Removing drive " << *node->op << "\n");
1697 pruner.eraseNow(node->op);
1698 node->op = nullptr;
1699}
1700
1701//===----------------------------------------------------------------------===//
1702// Drive-to-Probe Forwarding
1703//===----------------------------------------------------------------------===//
1704
1705/// Forward definitions throughout the IR.
1706void Promoter::resolveDefinitions() {
1707 for (auto *node : lattice->nodes) {
1708 if (auto *probe = dyn_cast<ProbeNode>(node))
1709 resolveDefinitions(probe);
1710 else if (auto *drive = dyn_cast<DriveNode>(node))
1711 resolveDefinitions(drive);
1712 }
1713}
1714
1715/// Replace probes with the corresponding reaching definition.
1716void Promoter::resolveDefinitions(ProbeNode *node) {
1717 Def *def = node->valueBefore->blockingReachingDef;
1718 assert(def && "no definition reaches probe");
1719
1720 // Gather any projections between the probe and the underlying slot.
1721 auto projections = getProjections(node->op->getOperand(0), node->slot);
1722
1723 // Extract the projected value from the aggregate.
1724 auto builder = OpBuilder(node->op);
1725 auto value = def->getValueOrPlaceholder();
1726 value = unpackProjections(builder, value, projections);
1727
1728 // Replace the probe result with the projected value.
1729 LLVM_DEBUG(llvm::dbgs() << "- Replacing " << *node->op << " with " << value
1730 << "\n");
1731 replaceValueWith(node->op->getResult(0), value);
1732 pruner.eraseNow(node->op);
1733 node->op = nullptr;
1734}
1735
1736/// Mutate reaching definitions for conditional drives or drives that target
1737/// projections of a slot.
1738void Promoter::resolveDefinitions(DriveNode *node) {
1739 // Check whether the drive already has a value and condition set for its
1740 // reaching def. This is the case for drives to an entire slot. Conditional
1741 // drives and drives to a projection will have no value set initially, in
1742 // which case either the value is null or it is a placeholder.
1743 if (!node->def->value || node->def->valueIsPlaceholder)
1744 resolveDefinitionValue(node);
1745 if (node->def->condition.isConditional() &&
1746 (!node->def->condition.getCondition() ||
1747 node->def->conditionIsPlaceholder))
1748 resolveDefinitionCondition(node);
1749}
1750
1751void Promoter::resolveDefinitionValue(DriveNode *node) {
1752 // Get the slot definition reaching the drive. This is the value the drive has
1753 // to update.
1754 Def *inDef = node->valueBefore->getReachingDef(isDelayed(node->slot));
1755 assert(inDef && "no definition reaches drive");
1756 auto driveOp = node->getDriveOp();
1757 LLVM_DEBUG(llvm::dbgs() << "- Injecting value for " << driveOp << "\n");
1758
1759 // Gather any projections between the drive and the underlying slot.
1760 auto projections = getProjections(driveOp.getSignal(), getSlot(node->slot));
1761
1762 // Extract the current value from the aggregate.
1763 auto builder = OpBuilder(driveOp);
1764 auto value = inDef->getValueOrPlaceholder();
1765 value = unpackProjections(builder, value, projections);
1766
1767 // Update the value according to the drive.
1768 pruner.eraseLaterIfUnused(value);
1769 if (!driveOp.getEnable())
1770 value = driveOp.getValue();
1771 else
1772 value = builder.createOrFold<comb::MuxOp>(
1773 driveOp.getLoc(), driveOp.getEnable(), driveOp.getValue(), value);
1774
1775 // Inject the updated value into the aggregate.
1776 value = packProjections(builder, value, projections);
1777
1778 // Track the updated slot value in the drive's reaching def.
1779 if (node->def->valueIsPlaceholder) {
1780 auto *placeholder = node->def->value.getDefiningOp();
1781 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1782 "placeholder replaced but valueIsPlaceholder still set");
1783 replaceValueWith(placeholder->getResult(0), value);
1784 pruner.eraseNow(placeholder);
1785 node->def->valueIsPlaceholder = false;
1786 }
1787 node->def->value = value;
1788}
1789
1790void Promoter::resolveDefinitionCondition(DriveNode *node) {
1791 // Get the slot definition reaching the drive. This contains the condition the
1792 // drive has to update.
1793 Def *inDef = node->valueBefore->getReachingDef(isDelayed(node->slot));
1794 assert(inDef && "no definition reaches drive");
1795 auto driveOp = node->getDriveOp();
1796 LLVM_DEBUG(llvm::dbgs() << "- Mutating condition for " << driveOp << "\n");
1797 OpBuilder builder(driveOp);
1798
1799 // Adjust the drive condition by combining the incoming condition with the
1800 // enable of this drive op.
1801 Value condition;
1802 if (inDef->condition.isNever())
1803 condition = driveOp.getEnable();
1804 else
1805 condition =
1806 builder.createOrFold<comb::OrOp>(driveOp.getLoc(), driveOp.getEnable(),
1807 inDef->getConditionOrPlaceholder());
1808
1809 // Track the updated drive condition in the drive's reaching def.
1810 if (node->def->conditionIsPlaceholder) {
1811 auto *placeholder = node->def->condition.getCondition().getDefiningOp();
1812 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1813 "placeholder replaced but conditionIsPlaceholder still set");
1814 replaceValueWith(placeholder->getResult(0), condition);
1815 pruner.eraseNow(placeholder);
1816 node->def->conditionIsPlaceholder = false;
1817 }
1818 node->def->condition.setCondition(condition);
1819}
1820
1821//===----------------------------------------------------------------------===//
1822// Block Argument Insertion
1823//===----------------------------------------------------------------------===//
1824
1825/// Insert block arguments into the IR.
1826void Promoter::insertBlockArgs() {
1827 bool anyArgsInserted = true;
1828 while (anyArgsInserted) {
1829 anyArgsInserted = false;
1830 for (auto *node : lattice->nodes)
1831 if (auto *entry = dyn_cast<BlockEntry>(node))
1832 anyArgsInserted |= insertBlockArgs(entry);
1833 }
1834}
1835
1836/// Insert block arguments for any merging definitions for which a placeholder
1837/// value has been created. Also insert corresponding successor operands to any
1838/// ops branching here. Returns true if any arguments were inserted.
1839///
1840/// This function may create additional placeholders in predecessor blocks.
1841/// Creating block arguments in a later block may uncover additional arguments
1842/// to be inserted in a previous one. Therefore this function must be called
1843/// until no more block arguments are inserted.
1844bool Promoter::insertBlockArgs(BlockEntry *node) {
1845 // Determine which merge defs for the current slot still have unresolved
1846 // placeholders. Check the blocking flavor first, then the delayed flavor,
1847 // so block argument ordering is deterministic per slot (and the
1848 // slot-iteration order in promote() gives deterministic ordering across
1849 // slots).
1850 enum class Which { Value, Condition };
1851 SmallVector<std::pair<DefSlot, Which>> neededSlots;
1852 auto addNeededSlot = [&](DefSlot slot) {
1853 auto *def = node->getMerged(isDelayed(slot));
1854 if (!def)
1855 return;
1856 if (!node->valueAfter->getReachingDef(isDelayed(slot)))
1857 return;
1858 if (def->valueIsPlaceholder)
1859 neededSlots.push_back({slot, Which::Value});
1860 if (def->conditionIsPlaceholder)
1861 neededSlots.push_back({slot, Which::Condition});
1862 };
1863 addNeededSlot(blockingSlot(currentSlot));
1864 addNeededSlot(delayedSlot(currentSlot));
1865 if (neededSlots.empty())
1866 return false;
1867 LLVM_DEBUG(llvm::dbgs() << "- Adding " << neededSlots.size()
1868 << " args to block " << node->block << "\n");
1869
1870 // Add the block arguments.
1871 for (auto [slot, which] : neededSlots) {
1872 auto *def = node->getMerged(isDelayed(slot));
1873 assert(def);
1874 switch (which) {
1875 case Which::Value: {
1876 // Create an argument for the definition's value and replace any
1877 // placeholder we might have created earlier.
1878 auto *placeholder = def->value.getDefiningOp();
1879 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1880 "placeholder replaced but valueIsPlaceholder still set");
1881 auto arg = node->block->addArgument(getStoredType(slot), getLoc(slot));
1882 replaceValueWith(placeholder->getResult(0), arg);
1883 pruner.eraseNow(placeholder);
1884 def->value = arg;
1885 def->valueIsPlaceholder = false;
1886 break;
1887 }
1888 case Which::Condition: {
1889 // If the definition's drive mode is conditional, create an argument for
1890 // the drive condition and replace any placeholder we might have created
1891 // earlier.
1892 auto *placeholder = def->condition.getCondition().getDefiningOp();
1893 assert(isa_and_nonnull<UnrealizedConversionCastOp>(placeholder) &&
1894 "placeholder replaced but conditionIsPlaceholder still set");
1895 auto conditionArg = node->block->addArgument(
1896 IntegerType::get(region.getContext(), 1), getLoc(slot));
1897 replaceValueWith(placeholder->getResult(0), conditionArg);
1898 pruner.eraseNow(placeholder);
1899 def->condition.setCondition(conditionArg);
1900 def->conditionIsPlaceholder = false;
1901 break;
1902 }
1903 }
1904 }
1905
1906 // Add successor operands to the predecessor terminators.
1907 for (auto *predecessor : node->predecessors) {
1908 // Collect the interesting reaching definitions in the predecessor.
1909 SmallVector<Value> args;
1910 for (auto [slot, which] : neededSlots) {
1911 Def *def = predecessor->valueBefore->getReachingDef(isDelayed(slot));
1912 auto builder = OpBuilder::atBlockTerminator(predecessor->block);
1913 switch (which) {
1914 case Which::Value:
1915 if (def) {
1916 args.push_back(def->getValueOrPlaceholder());
1917 } else {
1918 auto type = getStoredType(slot);
1919 auto flatType = builder.getIntegerType(hw::getBitWidth(type));
1920 Value value =
1921 hw::ConstantOp::create(builder, getLoc(slot), flatType, 0);
1922 if (type != flatType)
1923 value = hw::BitcastOp::create(builder, getLoc(slot), type, value);
1924 args.push_back(value);
1925 }
1926 break;
1927 case Which::Condition:
1928 if (def) {
1929 args.push_back(def->getConditionOrPlaceholder());
1930 } else {
1931 args.push_back(hw::ConstantOp::create(builder, getLoc(slot),
1932 builder.getI1Type(), 0));
1933 }
1934 break;
1935 }
1936 }
1937
1938 // Add the reaching definitions to the branch op.
1939 auto branchOp = cast<BranchOpInterface>(predecessor->terminator);
1940 for (auto &blockOperand : branchOp->getBlockOperands())
1941 if (blockOperand.get() == node->block)
1942 branchOp.getSuccessorOperands(blockOperand.getOperandNumber())
1943 .append(args);
1944 }
1945
1946 return true;
1947}
1948
1949/// Replace all uses of an old value with a new value in the IR, and update all
1950/// mentions of the old value in the lattice to the new value.
1951void Promoter::replaceValueWith(Value oldValue, Value newValue) {
1952 oldValue.replaceAllUsesWith(newValue);
1953 for (auto *def : lattice->defs) {
1954 if (def->value == oldValue)
1955 def->value = newValue;
1956 if (def->condition.isConditional() &&
1957 def->condition.getCondition() == oldValue)
1958 def->condition.setCondition(newValue);
1959 }
1960}
1961
1962//===----------------------------------------------------------------------===//
1963// Cleanup
1964//===----------------------------------------------------------------------===//
1965
1966/// Remove a local signal if it is only used by projection ops and drives, but
1967/// never probed. Since the signal is local and cannot be observed in any other
1968/// way, we can safely remove it along with any projection ops and drives.
1969void Promoter::removeUnusedLocalSignal(SignalOp signalOp) {
1970 // Check if the signal is only ever projected into and driven, but never
1971 // probed.
1973 SmallVector<Operation *> worklist;
1974 worklist.push_back(signalOp);
1975 while (!worklist.empty()) {
1976 auto *op = worklist.pop_back_val();
1977 if (!isa<SignalOp, DriveOp, SigArrayGetOp, SigArraySliceOp, SigExtractOp,
1978 SigStructExtractOp>(op))
1979 return;
1980 for (auto *user : op->getUsers())
1981 if (users.insert(user))
1982 worklist.push_back(user);
1983 }
1984
1985 // If we get here, the local signal is never probed. This means we can safely
1986 // remove it.
1987 LLVM_DEBUG(llvm::dbgs() << "- Removing local signal " << *signalOp << "\n");
1988 for (auto *op : llvm::reverse(users))
1989 pruner.eraseNow(op);
1990}
1991
1992//===----------------------------------------------------------------------===//
1993// Pass Infrastructure
1994//===----------------------------------------------------------------------===//
1995
1996namespace {
1997struct Mem2RegPass : public llhd::impl::Mem2RegPassBase<Mem2RegPass> {
1998 void runOnOperation() override;
1999};
2000} // namespace
2001
2002void Mem2RegPass::runOnOperation() {
2003 SmallVector<Region *> regions;
2004 getOperation()->walk<WalkOrder::PreOrder>([&](Operation *op) {
2005 if (isa<ProcessOp, FinalOp, CombinationalOp>(op)) {
2006 auto &region = op->getRegion(0);
2007 if (!region.empty())
2008 regions.push_back(&region);
2009 return WalkResult::skip();
2010 }
2011 return WalkResult::advance();
2012 });
2013 for (auto *region : regions)
2014 if (failed(Promoter(*region).promote()))
2015 return signalPassFailure();
2016}
static bool isAlways(Attribute attr, bool expected)
Definition ArcFolds.cpp:22
assert(baseType &&"element must be base type")
static void dump(DIModule &module, raw_indented_ostream &os)
static bool isDelayed(DefSlot slot)
Definition Mem2Reg.cpp:211
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...
Definition Mem2Reg.cpp:657
static Location getLoc(DefSlot slot)
Definition Mem2Reg.cpp:218
static bool isDeltaDrive(Operation *op)
Check whether an operation is a llhd.drive with a delta delay.
Definition Mem2Reg.cpp:67
static ProjectionStack getProjections(Value fromSignal, Value toSlot)
Collect the llhd.sig.
Definition Mem2Reg.cpp:601
static bool isDeltaDelay(Value value)
Check whether a value is defined by llhd.constant_time <0ns, 1d, 0e>.
Definition Mem2Reg.cpp:49
static DefSlot blockingSlot(Value slot)
Definition Mem2Reg.cpp:208
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 ...
Definition Mem2Reg.cpp:617
static DefSlot delayedSlot(Value slot)
Definition Mem2Reg.cpp:209
static Value getSlot(DefSlot slot)
Definition Mem2Reg.cpp:210
SmallVector< Projection > ProjectionStack
A stack of projection operations.
Definition Mem2Reg.cpp:594
static bool isBlockingDrive(Operation *op)
Check whether an operation is a llhd.drive with an epsilon delay.
Definition Mem2Reg.cpp:59
static Type getStoredType(Value slot)
Definition Mem2Reg.cpp:212
static bool isEpsilonDelay(Value value)
Check whether a value is defined by llhd.constant_time <0ns, 0d, 1e>.
Definition Mem2Reg.cpp:40
PointerIntPair< Value, 1 > DefSlot
The slot a reaching definition specifies a value for, alongside a bit indicating whether the definiti...
Definition Mem2Reg.cpp:207
static StringAttr append(StringAttr base, const Twine &suffix)
Return a attribute with the specified suffix appended.
create(data_type, value)
Definition hw.py:441
create(data_type, value)
Definition hw.py:433
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition CalyxOps.cpp:56
static bool operator==(const ModulePort &a, const ModulePort &b)
Definition HWTypes.h:35
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
bool operator!=(uint64_t a, const FVInt &b)
Definition FVInt.h:685
Utility that tracks operations that have potentially become unused and allows them to be cleaned up a...
static DriveCondition getEmptyKey()
Definition Mem2Reg.cpp:185
static DriveCondition getTombstoneKey()
Definition Mem2Reg.cpp:188
static bool isEqual(DriveCondition lhs, DriveCondition rhs)
Definition Mem2Reg.cpp:195
static unsigned getHashValue(DriveCondition d)
Definition Mem2Reg.cpp:191