CIRCT 20.0.0git
Loading...
Searching...
No Matches
RemoveCombGroups.cpp
Go to the documentation of this file.
1//===- RemoveCombGroups.cpp - Remove Comb Groups Pass -----------*- C++ -*-===//
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// Contains the definitions of the Remove Comb Groups pass.
10//
11//===----------------------------------------------------------------------===//
12
13/// Transforms combinational groups, which have a constant done condition,
14/// into proper groups by registering the values read from the ports of cells
15/// used within the combinational group.
16///
17/// It also transforms (invoke,if,while)-with into semantically equivalent
18/// control programs that first enable a group that calculates and registers the
19/// ports defined by the combinational group execute the respective cond group
20/// and then execute the control operator.
21///
22/// # Example
23/// ```
24/// group comb_cond<"static"=0> {
25/// lt.right = 32'd10;
26/// lt.left = 32'd1;
27/// eq.right = r.out;
28/// eq.left = x.out;
29/// comb_cond[done] = 1'd1;
30/// }
31/// control {
32/// invoke comp(left = lt.out, ..)(..) with comb_cond;
33/// if lt.out with comb_cond {
34/// ...
35/// }
36/// while eq.out with comb_cond {
37/// ...
38/// }
39/// }
40/// ```
41/// into:
42/// ```
43/// group comb_cond<"static"=1> {
44/// lt.right = 32'd10;
45/// lt.left = 32'd1;
46/// eq.right = r.out;
47/// eq.left = x.out;
48/// lt_reg.in = lt.out
49/// lt_reg.write_en = 1'd1;
50/// eq_reg.in = eq.out;
51/// eq_reg.write_en = 1'd1;
52/// comb_cond[done] = lt_reg.done & eq_reg.done ? 1'd1;
53/// }
54/// control {
55/// seq {
56/// comb_cond;
57/// invoke comp(left = lt_reg.out, ..)(..);
58/// }
59/// seq {
60/// comb_cond;
61/// if lt_reg.out {
62/// ...
63/// }
64/// }
65/// seq {
66/// comb_cond;
67/// while eq_reg.out {
68/// ...
69/// comb_cond;
70/// }
71/// }
72/// }
73/// ```
74
77#include "circt/Support/LLVM.h"
78#include "mlir/IR/BuiltinTypes.h"
79#include "mlir/IR/OperationSupport.h"
80#include "mlir/Transforms/DialectConversion.h"
81#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
82
83namespace circt {
84namespace calyx {
85#define GEN_PASS_DEF_REMOVECOMBGROUPS
86#include "circt/Dialect/Calyx/CalyxPasses.h.inc"
87} // namespace calyx
88} // namespace circt
89
90using namespace circt;
91using namespace calyx;
92using namespace mlir;
93
94namespace {
95
96static calyx::RegisterOp createReg(ComponentOp component,
97 PatternRewriter &rewriter, Location loc,
98 Twine prefix, size_t width) {
99 IRRewriter::InsertionGuard guard(rewriter);
100 rewriter.setInsertionPointToStart(component.getBodyBlock());
101 return rewriter.create<calyx::RegisterOp>(loc, (prefix + "_reg").str(),
102 width);
103}
104
105// Wraps the provided 'op' inside a newly created TOp operation, and
106// returns the TOp operation.
107template <typename TOp>
108static TOp wrapInsideOp(OpBuilder &builder, Operation *op) {
109 OpBuilder::InsertionGuard g(builder);
110 builder.setInsertionPoint(op);
111 auto newOp = builder.create<TOp>(op->getLoc());
112 op->moveBefore(newOp.getBodyBlock(), newOp.getBodyBlock()->begin());
113 return newOp;
114}
115
116using CombResRegMapping = DenseMap<Value, RegisterOp>;
117
118struct RemoveCombGroupsPattern : public OpRewritePattern<calyx::CombGroupOp> {
119 using OpRewritePattern::OpRewritePattern;
120
121 RemoveCombGroupsPattern(MLIRContext *ctx, CombResRegMapping *mapping)
122 : OpRewritePattern(ctx), combResRegMapping(mapping) {}
123
124 LogicalResult matchAndRewrite(calyx::CombGroupOp combGroup,
125 PatternRewriter &rewriter) const override {
126
127 auto component = combGroup->getParentOfType<ComponentOp>();
128 auto group = rewriter.create<calyx::GroupOp>(combGroup.getLoc(),
129 combGroup.getName());
130 rewriter.mergeBlocks(combGroup.getBodyBlock(), group.getBodyBlock());
131 rewriter.replaceOp(combGroup, group);
132
133 // Determine which cell results are read from the control schedule.
134 SetVector<Operation *> cellsAssigned;
135 for (auto op : group.getOps<calyx::AssignOp>()) {
136 auto defOp = dyn_cast<CellInterface>(op.getDest().getDefiningOp());
137 assert(defOp && "expected some assignment to a cell");
138 cellsAssigned.insert(defOp);
139 }
140
141 rewriter.setInsertionPointToStart(group.getBodyBlock());
142 auto oneConstant = rewriter.create<hw::ConstantOp>(
143 group.getLoc(), APInt(1, 1, /*isSigned=*/true, /*implicitTrunc=*/true));
144
145 // Maintain the set of cell results which have already been assigned to
146 // its register within this group.
147 SetVector<Value> alreadyAssignedResults;
148
149 // Collect register done signals. These are needed for generating the
150 // GroupDone signal.
151 SetVector<Value> registerDoneSigs;
152
153 // 1. Iterate over the cells assigned within the combinational group.
154 // 2. For any use of a cell result within the controls schedule.
155 // 3. Ensure that the cell result has a register.
156 // 4. Ensure that the cell result has been written to its register in this
157 // group.
158 // We do not replace uses of the combinational results now, since the
159 // following code relies on a checking cell result value use in the
160 // control schedule, which needs to remain even when two combinational
161 // groups assign to the same cell.
162 for (auto *cellOp : cellsAssigned) {
163 auto cell = dyn_cast<CellInterface>(cellOp);
164 for (auto combRes : cell.getOutputPorts()) {
165 for (auto &use : llvm::make_early_inc_range(combRes.getUses())) {
166 if (use.getOwner()->getParentOfType<calyx::ControlOp>()) {
167 auto combResReg = combResRegMapping->find(combRes);
168 if (combResReg == combResRegMapping->end()) {
169 // First time a registered variant of this result is needed.
170 auto reg = createReg(component, rewriter, combRes.getLoc(),
171 cell.instanceName(),
172 combRes.getType().getIntOrFloatBitWidth());
173 auto it = combResRegMapping->insert({combRes, reg});
174 combResReg = it.first;
175 }
176
177 // Assign the cell result register - a register should only be
178 // assigned once within a group.
179 if (!alreadyAssignedResults.contains(combRes)) {
180 rewriter.create<AssignOp>(combRes.getLoc(),
181 combResReg->second.getIn(), combRes);
182 rewriter.create<AssignOp>(combRes.getLoc(),
183 combResReg->second.getWriteEn(),
184 oneConstant);
185 alreadyAssignedResults.insert(combRes);
186 }
187
188 registerDoneSigs.insert(combResReg->second.getDone());
189 }
190 }
191 }
192 }
193
194 // Create a group done op with the complex &[regDone] expression as a
195 // guard.
196 assert(!registerDoneSigs.empty() &&
197 "No registers assigned in the combinational group?");
198 rewriter.setInsertionPointToEnd(group.getBodyBlock());
199 rewriter.create<calyx::GroupDoneOp>(
200 group.getLoc(),
201 rewriter.create<hw::ConstantOp>(group.getLoc(), APInt(1, 1)),
202 rewriter.create<comb::AndOp>(combGroup.getLoc(), rewriter.getI1Type(),
203 registerDoneSigs.takeVector()));
204
205 return success();
206 }
207
208 mutable CombResRegMapping *combResRegMapping;
209};
210
211struct RemoveCombGroupsPass
212 : public circt::calyx::impl::RemoveCombGroupsBase<RemoveCombGroupsPass> {
213 void runOnOperation() override;
214
215 /// Removes 'with' groups from an operation and instead schedules the group
216 /// right before the oop.
217 void rewriteIfWithCombGroup(OpBuilder &builder) {
218 OpBuilder::InsertionGuard guard(builder);
219 getOperation().walk([&](IfOp ifOp) {
220 if (!ifOp.getGroupName())
221 return;
222 auto groupName = ifOp.getGroupName();
223 // Ensure that we're inside a sequential control composition.
224 wrapInsideOp<SeqOp>(builder, ifOp);
225 builder.setInsertionPoint(ifOp);
226 builder.create<EnableOp>(ifOp.getLoc(), groupName.value());
227 ifOp.removeGroupNameAttr();
228 });
229 }
230
231 void rewriteWhileWithCombGroup(OpBuilder &builder) {
232 OpBuilder::InsertionGuard guard(builder);
233 getOperation().walk([&](WhileOp whileOp) {
234 if (!whileOp.getGroupName())
235 return;
236 auto groupName = whileOp.getGroupName().value();
237 // Ensure that we're inside a sequential control composition.
238 wrapInsideOp<SeqOp>(builder, whileOp);
239 builder.setInsertionPoint(whileOp);
240 builder.create<EnableOp>(whileOp.getLoc(), groupName);
241 whileOp.removeGroupNameAttr();
242
243 // Also schedule the group at the end of the while body.
244 auto &curWhileBodyOp = whileOp.getBodyBlock()->front();
245 builder.setInsertionPointToStart(whileOp.getBodyBlock());
246 auto newSeqBody = builder.create<SeqOp>(curWhileBodyOp.getLoc());
247 builder.setInsertionPointToStart(newSeqBody.getBodyBlock());
248 auto condEnable =
249 builder.create<EnableOp>(curWhileBodyOp.getLoc(), groupName);
250 curWhileBodyOp.moveBefore(condEnable);
251 });
252 }
253
254 void rewriteCellResults() {
255 for (auto &&[res, reg] : combResToReg) {
256 for (auto &use : llvm::make_early_inc_range(res.getUses())) {
257 if (use.getOwner()->getParentOfType<calyx::ControlOp>()) {
258 use.set(reg.getOut());
259 }
260 }
261 }
262 }
263
264 CombResRegMapping combResToReg;
265};
266
267} // end anonymous namespace
268
269void RemoveCombGroupsPass::runOnOperation() {
270 ConversionTarget target(getContext());
271 target.addLegalDialect<calyx::CalyxDialect>();
272 target.addLegalDialect<hw::HWDialect>();
273 target.addLegalDialect<comb::CombDialect>();
274 target.addIllegalOp<calyx::CombGroupOp>();
275
276 RewritePatternSet patterns(&getContext());
277
278 // Maintain a mapping from combinational result SSA values to the registered
279 // version of that combinational unit. This is used to avoid duplicating
280 // registers when cells are used across different groups.
281 patterns.add<RemoveCombGroupsPattern>(&getContext(), &combResToReg);
282
283 if (applyPartialConversion(getOperation(), target, std::move(patterns))
284 .failed())
285 signalPassFailure();
286
287 // Rewrite uses of the cell results to their registered variants.
288 rewriteCellResults();
289
290 // Rewrite 'with' uses of the previously combinational groups.
291 OpBuilder builder(&getContext());
292 rewriteIfWithCombGroup(builder);
293 rewriteWhileWithCombGroup(builder);
294}
295
296std::unique_ptr<mlir::Pass> circt::calyx::createRemoveCombGroupsPass() {
297 return std::make_unique<RemoveCombGroupsPass>();
298}
assert(baseType &&"element must be base type")
create(data_type, value)
Definition hw.py:433
std::unique_ptr< mlir::Pass > createRemoveCombGroupsPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Definition seq.py:21