12#include "mlir/Analysis/Liveness.h"
13#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
14#include "mlir/Transforms/RegionUtils.h"
15#include "llvm/Support/Debug.h"
17#define DEBUG_TYPE "llhd-lower-processes"
21#define GEN_PASS_DEF_LOWERPROCESSESPASS
22#include "circt/Dialect/LLHD/LLHDPasses.h.inc"
29using llvm::SmallDenseSet;
34 Lowering(ProcessOp processOp) : processOp(processOp) {}
36 bool matchControlFlow();
37 void markObservedValues();
38 bool allOperandsObserved();
39 bool isObserved(Value value);
43 SmallDenseSet<Value> observedValues;
47void Lowering::lower() {
49 if (!matchControlFlow())
52 if (!allOperandsObserved())
54 LLVM_DEBUG(llvm::dbgs() <<
"Lowering process " << processOp.getLoc() <<
"\n");
57 OpBuilder builder(processOp);
58 auto executeOp = CombinationalOp::create(builder, processOp.getLoc(),
59 processOp.getResultTypes());
60 executeOp.getRegion().takeBody(processOp.getBody());
61 processOp.replaceAllUsesWith(executeOp);
66 builder.setInsertionPoint(waitOp);
67 YieldOp::create(builder, waitOp.getLoc(), waitOp.getYieldOperands());
72 IRRewriter rewriter(builder);
73 (void)simplifyRegions(rewriter, executeOp->getRegions());
78bool Lowering::matchControlFlow() {
81 for (
auto &block : processOp.getBody()) {
85 if (isa<HaltOp>(block.getTerminator())) {
86 LLVM_DEBUG(llvm::dbgs() <<
"Skipping process " << processOp.getLoc()
87 <<
": contains halt terminator\n");
90 if (
auto op = dyn_cast<WaitOp>(block.getTerminator())) {
92 LLVM_DEBUG(llvm::dbgs() <<
"Skipping process " << processOp.getLoc()
93 <<
": multiple wait ops\n");
100 LLVM_DEBUG(llvm::dbgs() <<
"Skipping process " << processOp.getLoc()
101 <<
": no wait op\n");
104 if (!waitOp.getDestOperands().empty()) {
105 LLVM_DEBUG(llvm::dbgs() <<
"Skipping process " << processOp.getLoc()
106 <<
": wait op has destination operands\n");
109 if (waitOp.getDelay()) {
110 LLVM_DEBUG(llvm::dbgs() <<
"Skipping process " << processOp.getLoc()
111 <<
": wait op has delay\n");
116 auto skipToMergePoint = [&](
Block *block) -> std::pair<Block *, ValueRange> {
118 while (
auto branchOp = dyn_cast<cf::BranchOp>(block->getTerminator())) {
119 if (llvm::any_of(block->without_terminator(),
120 [](
auto &op) { return !isMemoryEffectFree(&op); }))
122 block = branchOp.getDest();
123 operands = branchOp.getDestOperands();
124 if (std::distance(block->pred_begin(), block->pred_end()) > 1)
126 if (!operands.empty())
129 return {block, operands};
133 auto &entry = processOp.getBody().front();
134 auto [entryMergeBlock, entryMergeArgs] = skipToMergePoint(&entry);
135 auto [waitMergeBlock, waitMergeArgs] = skipToMergePoint(waitOp.getDest());
136 if (entryMergeBlock != waitMergeBlock) {
137 LLVM_DEBUG(llvm::dbgs()
138 <<
"Skipping process " << processOp.getLoc()
139 <<
": control from entry and wait does not converge\n");
144 auto areValuesEquivalent = [](std::tuple<Value, Value> values) {
145 auto [a, b] = values;
148 auto *opA = a.getDefiningOp();
149 auto *opB = b.getDefiningOp();
152 return OperationEquivalence::isEquivalentTo(
153 opA, opB, OperationEquivalence::IgnoreLocations);
158 if (!llvm::all_of(llvm::zip(entryMergeArgs, waitMergeArgs),
159 areValuesEquivalent)) {
160 LLVM_DEBUG(llvm::dbgs() <<
"Skipping process " << processOp.getLoc()
161 <<
": control from entry and wait converges with "
162 "different block arguments\n");
167 Liveness liveness(processOp);
168 for (
auto value : liveness.getLiveOut(waitOp->getBlock())) {
169 if (value.getParentRegion()->isProperAncestor(&processOp.getBody()))
172 llvm::dbgs() <<
"Skipping process " << processOp.getLoc() <<
": value ";
173 value.print(llvm::dbgs(), OpPrintingFlags().skipRegions());
174 llvm::dbgs() <<
" live across wait\n";
183void Lowering::markObservedValues() {
184 SmallVector<Value> worklist;
185 auto markObserved = [&](Value value) {
186 if (observedValues.insert(value).second)
187 worklist.push_back(value);
190 for (
auto value : waitOp.getObserved())
191 if (value.getParentRegion()->isProperAncestor(&processOp.getBody()))
194 while (!worklist.empty()) {
195 auto value = worklist.pop_back_val();
196 auto *op = value.getDefiningOp();
202 if (
auto probeOp = dyn_cast<ProbeOp>(op))
203 markObserved(probeOp.getSignal());
209 for (
auto operand : op->getOperands())
210 markObserved(operand);
216bool Lowering::allOperandsObserved() {
219 SmallPtrSet<Region *, 4> properAncestors;
220 for (
auto *region = processOp->getParentRegion(); region;
221 region = region->getParentRegion())
222 properAncestors.insert(region);
225 auto walkResult = processOp.walk([&](Operation *op) {
226 for (
auto operand : op->getOperands()) {
228 if (!properAncestors.count(operand.getParentRegion()))
232 if (isObserved(operand))
237 llvm::dbgs() <<
"Skipping process " << processOp.getLoc()
238 <<
": unobserved value ";
239 operand.print(llvm::dbgs(), OpPrintingFlags().skipRegions());
240 llvm::dbgs() <<
"\n";
242 return WalkResult::interrupt();
244 return WalkResult::advance();
246 return !walkResult.wasInterrupted();
251bool Lowering::isObserved(Value value) {
253 if (observedValues.contains(value))
259 auto *defOp = value.getDefiningOp();
265 SmallDenseSet<Operation *> seenOps;
266 SmallVector<Operation *> worklist;
267 seenOps.insert(defOp);
268 worklist.push_back(defOp);
269 while (!worklist.empty()) {
270 auto *op = worklist.pop_back_val();
273 if (op->getNumRegions() != 0)
279 for (
auto operand : op->getOperands()) {
280 if (observedValues.contains(operand))
282 if (isa<RefType>(operand.getType()))
284 auto *defOp = operand.getDefiningOp();
285 if (!defOp || !isMemoryEffectFree(defOp))
287 if (seenOps.insert(defOp).second)
288 worklist.push_back(defOp);
294 observedValues.insert(value);
299struct LowerProcessesPass
300 :
public llhd::impl::LowerProcessesPassBase<LowerProcessesPass> {
301 void runOnOperation()
override;
305void LowerProcessesPass::runOnOperation() {
306 SmallVector<ProcessOp> processOps(getOperation().getOps<ProcessOp>());
307 for (
auto processOp : processOps)
308 Lowering(processOp).lower();
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.