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/Transforms/LLHDPasses.h.inc"
29using llvm::SmallDenseSet;
30using llvm::SmallSetVector;
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()) {
82 if (
auto op = dyn_cast<WaitOp>(block.getTerminator())) {
84 LLVM_DEBUG(llvm::dbgs() <<
"Skipping process " << processOp.getLoc()
85 <<
": multiple wait ops\n");
92 LLVM_DEBUG(llvm::dbgs() <<
"Skipping process " << processOp.getLoc()
96 if (!waitOp.getDestOperands().empty()) {
97 LLVM_DEBUG(llvm::dbgs() <<
"Skipping process " << processOp.getLoc()
98 <<
": wait op has destination operands\n");
101 if (waitOp.getDelay()) {
102 LLVM_DEBUG(llvm::dbgs() <<
"Skipping process " << processOp.getLoc()
103 <<
": wait op has delay\n");
108 auto skipToMergePoint = [&](
Block *block) -> std::pair<Block *, ValueRange> {
110 while (
auto branchOp = dyn_cast<cf::BranchOp>(block->getTerminator())) {
111 if (llvm::any_of(block->without_terminator(),
112 [](
auto &op) { return !isMemoryEffectFree(&op); }))
114 block = branchOp.getDest();
115 operands = branchOp.getDestOperands();
116 if (std::distance(block->pred_begin(), block->pred_end()) > 1)
118 if (!operands.empty())
121 return {block, operands};
125 auto &entry = processOp.getBody().front();
126 auto [entryMergeBlock, entryMergeArgs] = skipToMergePoint(&entry);
127 auto [waitMergeBlock, waitMergeArgs] = skipToMergePoint(waitOp.getDest());
128 if (entryMergeBlock != waitMergeBlock) {
129 LLVM_DEBUG(llvm::dbgs()
130 <<
"Skipping process " << processOp.getLoc()
131 <<
": control from entry and wait does not converge\n");
136 auto areValuesEquivalent = [](std::tuple<Value, Value> values) {
137 auto [a, b] = values;
140 auto *opA = a.getDefiningOp();
141 auto *opB = b.getDefiningOp();
144 return OperationEquivalence::isEquivalentTo(
145 opA, opB, OperationEquivalence::IgnoreLocations);
150 if (!llvm::all_of(llvm::zip(entryMergeArgs, waitMergeArgs),
151 areValuesEquivalent)) {
152 LLVM_DEBUG(llvm::dbgs() <<
"Skipping process " << processOp.getLoc()
153 <<
": control from entry and wait converges with "
154 "different block arguments\n");
159 Liveness liveness(processOp);
160 for (
auto value : liveness.getLiveOut(waitOp->getBlock())) {
161 if (value.getParentRegion()->isProperAncestor(&processOp.getBody()))
164 llvm::dbgs() <<
"Skipping process " << processOp.getLoc() <<
": value ";
165 value.print(llvm::dbgs(), OpPrintingFlags().skipRegions());
166 llvm::dbgs() <<
" live across wait\n";
175void Lowering::markObservedValues() {
176 SmallVector<Value> worklist;
177 auto markObserved = [&](Value value) {
178 if (observedValues.insert(value).second)
179 worklist.push_back(value);
182 for (
auto value : waitOp.getObserved())
183 if (value.getParentRegion()->isProperAncestor(&processOp.getBody()))
186 while (!worklist.empty()) {
187 auto value = worklist.pop_back_val();
188 auto *op = value.getDefiningOp();
194 if (
auto probeOp = dyn_cast<ProbeOp>(op))
195 markObserved(probeOp.getSignal());
201 for (
auto operand : op->getOperands())
202 markObserved(operand);
208bool Lowering::allOperandsObserved() {
211 SmallPtrSet<Region *, 4> properAncestors;
212 for (
auto *region = processOp->getParentRegion(); region;
213 region = region->getParentRegion())
214 properAncestors.insert(region);
217 auto walkResult = processOp.walk([&](Operation *op) {
218 for (
auto operand : op->getOperands()) {
220 if (!properAncestors.count(operand.getParentRegion()))
224 if (isObserved(operand))
229 llvm::dbgs() <<
"Skipping process " << processOp.getLoc()
230 <<
": unobserved value ";
231 operand.print(llvm::dbgs(), OpPrintingFlags().skipRegions());
232 llvm::dbgs() <<
"\n";
234 return WalkResult::interrupt();
236 return WalkResult::advance();
238 return !walkResult.wasInterrupted();
243bool Lowering::isObserved(Value value) {
245 if (observedValues.contains(value))
251 auto *defOp = value.getDefiningOp();
257 SmallDenseSet<Operation *> seenOps;
258 SmallVector<Operation *> worklist;
259 seenOps.insert(defOp);
260 worklist.push_back(defOp);
261 while (!worklist.empty()) {
262 auto *op = worklist.pop_back_val();
265 if (op->getNumRegions() != 0)
271 for (
auto operand : op->getOperands()) {
272 if (observedValues.contains(operand))
274 if (isa<RefType>(operand.getType()))
276 auto *defOp = operand.getDefiningOp();
277 if (!defOp || !isMemoryEffectFree(defOp))
279 if (seenOps.insert(defOp).second)
280 worklist.push_back(defOp);
286 observedValues.insert(value);
291struct LowerProcessesPass
292 :
public llhd::impl::LowerProcessesPassBase<LowerProcessesPass> {
293 void runOnOperation()
override;
297void LowerProcessesPass::runOnOperation() {
298 SmallVector<ProcessOp> processOps(getOperation().getOps<ProcessOp>());
299 for (
auto processOp : processOps)
300 Lowering(processOp).lower();
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.