12#include "mlir/Analysis/Liveness.h"
13#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
14#include "mlir/Dialect/SCF/IR/SCF.h"
15#include "mlir/Transforms/RegionUtils.h"
16#include "llvm/Support/Debug.h"
18#define DEBUG_TYPE "llhd-lower-processes"
22#define GEN_PASS_DEF_LOWERPROCESSESPASS
23#include "circt/Dialect/LLHD/Transforms/LLHDPasses.h.inc"
30using llvm::SmallDenseSet;
31using llvm::SmallSetVector;
35 Lowering(ProcessOp processOp) : processOp(processOp) {}
37 bool matchControlFlow();
38 void markObservedValues();
39 bool allOperandsObserved();
40 bool isObserved(Value value);
44 SmallDenseSet<Value> observedValues;
48void Lowering::lower() {
50 if (!matchControlFlow())
53 if (!allOperandsObserved())
55 LLVM_DEBUG(llvm::dbgs() <<
"Lowering process " << processOp.getLoc() <<
"\n");
58 OpBuilder builder(processOp);
59 auto executeOp = builder.create<scf::ExecuteRegionOp>(
60 processOp.getLoc(), processOp.getResultTypes());
61 executeOp.getRegion().takeBody(processOp.getBody());
62 processOp.replaceAllUsesWith(executeOp);
67 builder.setInsertionPoint(waitOp);
68 builder.create<scf::YieldOp>(waitOp.getLoc(), waitOp.getYieldOperands());
73 IRRewriter rewriter(builder);
74 (void)simplifyRegions(rewriter, executeOp->getRegions());
79bool Lowering::matchControlFlow() {
82 for (
auto &block : processOp.getBody()) {
83 if (
auto op = dyn_cast<WaitOp>(block.getTerminator())) {
85 LLVM_DEBUG(llvm::dbgs() <<
"Skipping process " << processOp.getLoc()
86 <<
": multiple wait ops\n");
93 LLVM_DEBUG(llvm::dbgs() <<
"Skipping process " << processOp.getLoc()
97 if (!waitOp.getDestOperands().empty()) {
98 LLVM_DEBUG(llvm::dbgs() <<
"Skipping process " << processOp.getLoc()
99 <<
": wait op has destination operands\n");
104 auto skipToMergePoint = [&](
Block *block) -> std::pair<Block *, ValueRange> {
106 while (
auto branchOp = dyn_cast<cf::BranchOp>(block->getTerminator())) {
107 if (!block->without_terminator().empty())
109 block = branchOp.getDest();
110 operands = branchOp.getDestOperands();
111 if (std::distance(block->pred_begin(), block->pred_end()) > 1)
113 if (!operands.empty())
116 return {block, operands};
121 auto &entry = processOp.getBody().front();
122 auto [entryMergeBlock, entryMergeArgs] = skipToMergePoint(&entry);
123 auto [waitMergeBlock, waitMergeArgs] = skipToMergePoint(waitOp.getDest());
124 if (entryMergeBlock != waitMergeBlock) {
125 LLVM_DEBUG(llvm::dbgs()
126 <<
"Skipping process " << processOp.getLoc()
127 <<
": control from entry and wait does not converge\n");
130 if (entryMergeArgs != waitMergeArgs) {
131 LLVM_DEBUG(llvm::dbgs() <<
"Skipping process " << processOp.getLoc()
132 <<
": control from entry and wait converges with "
133 "different block arguments\n");
138 Liveness liveness(processOp);
139 for (
auto value : liveness.getLiveOut(waitOp->getBlock())) {
140 if (value.getParentRegion()->isProperAncestor(&processOp.getBody()))
143 llvm::dbgs() <<
"Skipping process " << processOp.getLoc() <<
": value ";
144 value.print(llvm::dbgs(), OpPrintingFlags().skipRegions());
145 llvm::dbgs() <<
" live across wait\n";
154void Lowering::markObservedValues() {
155 SmallVector<Value> worklist;
156 auto markObserved = [&](Value value) {
157 if (observedValues.insert(value).second)
158 worklist.push_back(value);
161 for (
auto value : waitOp.getObserved())
162 if (value.getParentRegion()->isProperAncestor(&processOp.getBody()))
165 while (!worklist.empty()) {
166 auto value = worklist.pop_back_val();
167 auto *op = value.getDefiningOp();
173 if (
auto probeOp = dyn_cast<PrbOp>(op))
174 markObserved(probeOp.getSignal());
180 for (
auto operand : op->getOperands())
181 markObserved(operand);
187bool Lowering::allOperandsObserved() {
190 SmallPtrSet<Region *, 4> properAncestors;
191 for (
auto *region = processOp->getParentRegion(); region;
192 region = region->getParentRegion())
193 properAncestors.insert(region);
196 auto walkResult = processOp.walk([&](Operation *op) {
197 for (
auto operand : op->getOperands()) {
199 if (!properAncestors.count(operand.getParentRegion()))
203 if (isObserved(operand))
208 llvm::dbgs() <<
"Skipping process " << processOp.getLoc()
209 <<
": unobserved value ";
210 operand.print(llvm::dbgs(), OpPrintingFlags().skipRegions());
211 llvm::dbgs() <<
"\n";
213 return WalkResult::interrupt();
215 return WalkResult::advance();
217 return !walkResult.wasInterrupted();
222bool Lowering::isObserved(Value value) {
224 if (observedValues.contains(value))
230 auto *defOp = value.getDefiningOp();
236 SmallDenseSet<Operation *> seenOps;
237 SmallVector<Operation *> worklist;
238 seenOps.insert(defOp);
239 worklist.push_back(defOp);
240 while (!worklist.empty()) {
241 auto *op = worklist.pop_back_val();
244 if (op->getNumRegions() != 0)
250 for (
auto operand : op->getOperands()) {
251 if (observedValues.contains(operand))
253 if (isa<hw::InOutType>(operand.getType()))
255 auto *defOp = operand.getDefiningOp();
256 if (!defOp || !isMemoryEffectFree(defOp))
258 if (seenOps.insert(defOp).second)
259 worklist.push_back(defOp);
265 observedValues.insert(value);
270struct LowerProcessesPass
271 :
public llhd::impl::LowerProcessesPassBase<LowerProcessesPass> {
272 void runOnOperation()
override;
276void LowerProcessesPass::runOnOperation() {
277 SmallVector<ProcessOp> processOps(getOperation().getOps<ProcessOp>());
278 for (
auto processOp : processOps)
279 Lowering(processOp).lower();
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.