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");
103 auto skipToMergePoint = [&](
Block *block) -> std::pair<Block *, ValueRange> {
105 while (
auto branchOp = dyn_cast<cf::BranchOp>(block->getTerminator())) {
106 if (llvm::any_of(block->without_terminator(),
107 [](
auto &op) { return !isMemoryEffectFree(&op); }))
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};
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");
131 auto areValuesEquivalent = [](std::tuple<Value, Value> values) {
132 auto [a, b] = values;
135 auto *opA = a.getDefiningOp();
136 auto *opB = b.getDefiningOp();
139 return OperationEquivalence::isEquivalentTo(
140 opA, opB, OperationEquivalence::IgnoreLocations);
145 if (!llvm::all_of(llvm::zip(entryMergeArgs, waitMergeArgs),
146 areValuesEquivalent)) {
147 LLVM_DEBUG(llvm::dbgs() <<
"Skipping process " << processOp.getLoc()
148 <<
": control from entry and wait converges with "
149 "different block arguments\n");
154 Liveness liveness(processOp);
155 for (
auto value : liveness.getLiveOut(waitOp->getBlock())) {
156 if (value.getParentRegion()->isProperAncestor(&processOp.getBody()))
159 llvm::dbgs() <<
"Skipping process " << processOp.getLoc() <<
": value ";
160 value.print(llvm::dbgs(), OpPrintingFlags().skipRegions());
161 llvm::dbgs() <<
" live across wait\n";
170void Lowering::markObservedValues() {
171 SmallVector<Value> worklist;
172 auto markObserved = [&](Value value) {
173 if (observedValues.insert(value).second)
174 worklist.push_back(value);
177 for (
auto value : waitOp.getObserved())
178 if (value.getParentRegion()->isProperAncestor(&processOp.getBody()))
181 while (!worklist.empty()) {
182 auto value = worklist.pop_back_val();
183 auto *op = value.getDefiningOp();
189 if (
auto probeOp = dyn_cast<PrbOp>(op))
190 markObserved(probeOp.getSignal());
196 for (
auto operand : op->getOperands())
197 markObserved(operand);
203bool Lowering::allOperandsObserved() {
206 SmallPtrSet<Region *, 4> properAncestors;
207 for (
auto *region = processOp->getParentRegion(); region;
208 region = region->getParentRegion())
209 properAncestors.insert(region);
212 auto walkResult = processOp.walk([&](Operation *op) {
213 for (
auto operand : op->getOperands()) {
215 if (!properAncestors.count(operand.getParentRegion()))
219 if (isObserved(operand))
224 llvm::dbgs() <<
"Skipping process " << processOp.getLoc()
225 <<
": unobserved value ";
226 operand.print(llvm::dbgs(), OpPrintingFlags().skipRegions());
227 llvm::dbgs() <<
"\n";
229 return WalkResult::interrupt();
231 return WalkResult::advance();
233 return !walkResult.wasInterrupted();
238bool Lowering::isObserved(Value value) {
240 if (observedValues.contains(value))
246 auto *defOp = value.getDefiningOp();
252 SmallDenseSet<Operation *> seenOps;
253 SmallVector<Operation *> worklist;
254 seenOps.insert(defOp);
255 worklist.push_back(defOp);
256 while (!worklist.empty()) {
257 auto *op = worklist.pop_back_val();
260 if (op->getNumRegions() != 0)
266 for (
auto operand : op->getOperands()) {
267 if (observedValues.contains(operand))
269 if (isa<hw::InOutType>(operand.getType()))
271 auto *defOp = operand.getDefiningOp();
272 if (!defOp || !isMemoryEffectFree(defOp))
274 if (seenOps.insert(defOp).second)
275 worklist.push_back(defOp);
281 observedValues.insert(value);
286struct LowerProcessesPass
287 :
public llhd::impl::LowerProcessesPassBase<LowerProcessesPass> {
288 void runOnOperation()
override;
292void LowerProcessesPass::runOnOperation() {
293 SmallVector<ProcessOp> processOps(getOperation().getOps<ProcessOp>());
294 for (
auto processOp : processOps)
295 Lowering(processOp).lower();
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.