CIRCT 20.0.0git
Loading...
Searching...
No Matches
ProcessLoweringPass.cpp
Go to the documentation of this file.
1//===- ProcessLoweringPass.cpp - Implement Process Lowering Pass ----------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Implement Pass to transform combinational processes to entities.
10//
11//===----------------------------------------------------------------------===//
12
15#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
16#include "mlir/IR/PatternMatch.h"
17#include "mlir/IR/Visitors.h"
18#include "mlir/Pass/Pass.h"
19#include "llvm/Support/Debug.h"
20
21#define DEBUG_TYPE "llhd-process-lowering"
22
23namespace circt {
24namespace llhd {
25#define GEN_PASS_DEF_PROCESSLOWERING
26#include "circt/Dialect/LLHD/Transforms/LLHDPasses.h.inc"
27} // namespace llhd
28} // namespace circt
29
30using namespace mlir;
31using namespace circt;
32
33namespace {
34
35struct ProcessLoweringPass
36 : public circt::llhd::impl::ProcessLoweringBase<ProcessLoweringPass> {
37 void runOnOperation() override;
38};
39
40static LogicalResult isProcValidToLower(llhd::ProcessOp op) {
41 if (op.getBody().getBlocks().size() != 2) {
42 LLVM_DEBUG({
43 llvm::dbgs() << "process-lowering only supports processes with "
44 "two basic blocks where the first "
45 "contains a 'cf.br' terminator and the second one is "
46 "terminated by a 'llhd.wait' operation\n";
47 });
48 return failure();
49 }
50
51 Block &first = op.getBody().front();
52 Block &last = op.getBody().back();
53
54 if (!last.getArguments().empty()) {
55 LLVM_DEBUG({
56 llvm::dbgs() << "the second block (containing the "
57 "llhd.wait) is not allowed to have arguments\n";
58 });
59 return failure();
60 }
61
62 if (!isa<cf::BranchOp>(first.getTerminator())) {
63 LLVM_DEBUG({
64 llvm::dbgs() << "the first block has to "
65 "be terminated by a cf.br operation\n";
66 });
67 return failure();
68 }
69
70 if (auto wait = dyn_cast<llhd::WaitOp>(last.getTerminator())) {
71 // No optional time argument is allowed
72 if (wait.getTime()) {
73 LLVM_DEBUG({
74 llvm::dbgs() << "llhd.wait terminators with optional time "
75 "argument cannot be lowered to structural LLHD\n";
76 });
77 return failure();
78 }
79
80 SmallVector<Value> observedSignals;
81 for (Value obs : wait.getObserved())
82 if (auto prb = obs.getDefiningOp<llhd::PrbOp>())
83 if (!op.getBody().isAncestor(prb->getParentRegion()))
84 observedSignals.push_back(prb.getSignal());
85
86 // Every probed signal has to occur in the observed signals list in
87 // the wait instruction
88 WalkResult result = op.walk([&](Operation *operation) -> WalkResult {
89 // TODO: value does not need to be observed if all values this value is
90 // a combinatorial result of are observed.
91 for (Value operand : operation->getOperands()) {
92 if (op.getBody().isAncestor(operand.getParentRegion()))
93 continue;
94 if (llvm::is_contained(wait.getObserved(), operand))
95 continue;
96 if (llvm::is_contained(observedSignals, operand))
97 continue;
98 if (auto *defOp = operand.getDefiningOp();
99 defOp && defOp->hasTrait<OpTrait::ConstantLike>())
100 continue;
101 if (auto bitcastOp = operand.getDefiningOp<hw::BitcastOp>())
102 if (auto *defOp = bitcastOp.getInput().getDefiningOp();
103 defOp && defOp->hasTrait<OpTrait::ConstantLike>())
104 continue;
105
106 LLVM_DEBUG({
107 llvm::dbgs() << "the wait terminator is required to "
108 "have values used in the process as arguments\n";
109 });
110 return failure();
111 }
112
113 return WalkResult::advance();
114 });
115
116 return failure(result.wasInterrupted());
117 }
118
119 LLVM_DEBUG({
120 llvm::dbgs() << "the second block must be "
121 "terminated by llhd.wait\n";
122 });
123 return failure();
124}
125
126void ProcessLoweringPass::runOnOperation() {
127 ModuleOp module = getOperation();
128
129 module.walk([](llhd::ProcessOp op) {
130 LLVM_DEBUG({ llvm::dbgs() << "\n=== Process\n"; });
131 // Check invariants
132 if (failed(isProcValidToLower(op)))
133 return;
134
135 // In the case that wait is used to suspend the process, we need to merge
136 // the two blocks as we needed the second block to have a target for wait
137 // (the entry block cannot be targeted).
138 if (op.getBody().getBlocks().size() == 2) {
139 Block &first = op.getBody().front();
140 Block &second = op.getBody().back();
141 // Delete the BranchOp operation in the entry block
142 first.getTerminator()->erase();
143 // Move operations of second block in entry block.
144 first.getOperations().splice(first.end(), second.getOperations());
145 // Drop all references to the second block and delete it.
146 second.dropAllReferences();
147 second.dropAllDefinedValueUses();
148 second.erase();
149 }
150
151 // Remove the remaining llhd.halt or llhd.wait terminator
152 op.getBody().front().getTerminator()->erase();
153
154 IRRewriter rewriter(op);
155 rewriter.inlineBlockBefore(&op.getBody().front(), op);
156 op.erase();
157
158 LLVM_DEBUG({ llvm::dbgs() << "Process lowered successfully!\n"; });
159 });
160}
161} // namespace
static bool isAncestor(Block *block, Block *other)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.