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 = builder.create<CombinationalOp>(processOp.getLoc(),
59 processOp.getResultTypes());
60 executeOp.getRegion().takeBody(processOp.getBody());
61 processOp.replaceAllUsesWith(executeOp);
66 builder.setInsertionPoint(waitOp);
67 builder.create<YieldOp>(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");
103 auto skipToMergePoint = [&](
Block *block) -> std::pair<Block *, ValueRange> {
105 while (
auto branchOp = dyn_cast<cf::BranchOp>(block->getTerminator())) {
106 if (!block->without_terminator().empty())
108 block = branchOp.getDest();
109 operands = branchOp.getDestOperands();
110 if (std::distance(block->pred_begin(), block->pred_end()) > 1)
112 if (!operands.empty())
115 return {block, operands};
120 auto &entry = processOp.getBody().front();
121 auto [entryMergeBlock, entryMergeArgs] = skipToMergePoint(&entry);
122 auto [waitMergeBlock, waitMergeArgs] = skipToMergePoint(waitOp.getDest());
123 if (entryMergeBlock != waitMergeBlock) {
124 LLVM_DEBUG(llvm::dbgs()
125 <<
"Skipping process " << processOp.getLoc()
126 <<
": control from entry and wait does not converge\n");
129 if (entryMergeArgs != waitMergeArgs) {
130 LLVM_DEBUG(llvm::dbgs() <<
"Skipping process " << processOp.getLoc()
131 <<
": control from entry and wait converges with "
132 "different block arguments\n");
137 Liveness liveness(processOp);
138 for (
auto value : liveness.getLiveOut(waitOp->getBlock())) {
139 if (value.getParentRegion()->isProperAncestor(&processOp.getBody()))
142 llvm::dbgs() <<
"Skipping process " << processOp.getLoc() <<
": value ";
143 value.print(llvm::dbgs(), OpPrintingFlags().skipRegions());
144 llvm::dbgs() <<
" live across wait\n";
153void Lowering::markObservedValues() {
154 SmallVector<Value> worklist;
155 auto markObserved = [&](Value value) {
156 if (observedValues.insert(value).second)
157 worklist.push_back(value);
160 for (
auto value : waitOp.getObserved())
161 if (value.getParentRegion()->isProperAncestor(&processOp.getBody()))
164 while (!worklist.empty()) {
165 auto value = worklist.pop_back_val();
166 auto *op = value.getDefiningOp();
172 if (
auto probeOp = dyn_cast<PrbOp>(op))
173 markObserved(probeOp.getSignal());
179 for (
auto operand : op->getOperands())
180 markObserved(operand);
186bool Lowering::allOperandsObserved() {
189 SmallPtrSet<Region *, 4> properAncestors;
190 for (
auto *region = processOp->getParentRegion(); region;
191 region = region->getParentRegion())
192 properAncestors.insert(region);
195 auto walkResult = processOp.walk([&](Operation *op) {
196 for (
auto operand : op->getOperands()) {
198 if (!properAncestors.count(operand.getParentRegion()))
202 if (isObserved(operand))
207 llvm::dbgs() <<
"Skipping process " << processOp.getLoc()
208 <<
": unobserved value ";
209 operand.print(llvm::dbgs(), OpPrintingFlags().skipRegions());
210 llvm::dbgs() <<
"\n";
212 return WalkResult::interrupt();
214 return WalkResult::advance();
216 return !walkResult.wasInterrupted();
221bool Lowering::isObserved(Value value) {
223 if (observedValues.contains(value))
229 auto *defOp = value.getDefiningOp();
235 SmallDenseSet<Operation *> seenOps;
236 SmallVector<Operation *> worklist;
237 seenOps.insert(defOp);
238 worklist.push_back(defOp);
239 while (!worklist.empty()) {
240 auto *op = worklist.pop_back_val();
243 if (op->getNumRegions() != 0)
249 for (
auto operand : op->getOperands()) {
250 if (observedValues.contains(operand))
252 if (isa<hw::InOutType>(operand.getType()))
254 auto *defOp = operand.getDefiningOp();
255 if (!defOp || !isMemoryEffectFree(defOp))
257 if (seenOps.insert(defOp).second)
258 worklist.push_back(defOp);
264 observedValues.insert(value);
269struct LowerProcessesPass
270 :
public llhd::impl::LowerProcessesPassBase<LowerProcessesPass> {
271 void runOnOperation()
override;
275void LowerProcessesPass::runOnOperation() {
276 SmallVector<ProcessOp> processOps(getOperation().getOps<ProcessOp>());
277 for (
auto processOp : processOps)
278 Lowering(processOp).lower();
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.