CIRCT  20.0.0git
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 
83 namespace circt {
84 namespace calyx {
85 #define GEN_PASS_DEF_REMOVECOMBGROUPS
86 #include "circt/Dialect/Calyx/CalyxPasses.h.inc"
87 } // namespace calyx
88 } // namespace circt
89 
90 using namespace circt;
91 using namespace calyx;
92 using namespace mlir;
93 
94 namespace {
95 
96 static 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.
107 template <typename TOp>
108 static 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 
116 using CombResRegMapping = DenseMap<Value, RegisterOp>;
117 
118 struct 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 
211 struct 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 
269 void 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 
296 std::unique_ptr<mlir::Pass> circt::calyx::createRemoveCombGroupsPass() {
297  return std::make_unique<RemoveCombGroupsPass>();
298 }
assert(baseType &&"element must be base type")
def create(data_type, value)
Definition: hw.py:433
std::unique_ptr< mlir::Pass > createRemoveCombGroupsPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Definition: seq.py:21