CIRCT  19.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 
75 #include "PassDetails.h"
78 #include "circt/Support/LLVM.h"
79 #include "mlir/IR/BuiltinTypes.h"
80 #include "mlir/IR/OperationSupport.h"
81 #include "mlir/Transforms/DialectConversion.h"
82 #include "mlir/Transforms/GreedyPatternRewriteDriver.h"
83 
84 using namespace circt;
85 using namespace calyx;
86 using namespace mlir;
87 
88 namespace {
89 
90 static calyx::RegisterOp createReg(ComponentOp component,
91  PatternRewriter &rewriter, Location loc,
92  Twine prefix, size_t width) {
93  IRRewriter::InsertionGuard guard(rewriter);
94  rewriter.setInsertionPointToStart(component.getBodyBlock());
95  return rewriter.create<calyx::RegisterOp>(loc, (prefix + "_reg").str(),
96  width);
97 }
98 
99 // Wraps the provided 'op' inside a newly created TOp operation, and
100 // returns the TOp operation.
101 template <typename TOp>
102 static TOp wrapInsideOp(OpBuilder &builder, Operation *op) {
103  OpBuilder::InsertionGuard g(builder);
104  builder.setInsertionPoint(op);
105  auto newOp = builder.create<TOp>(op->getLoc());
106  op->moveBefore(newOp.getBodyBlock(), newOp.getBodyBlock()->begin());
107  return newOp;
108 }
109 
110 using CombResRegMapping = DenseMap<Value, RegisterOp>;
111 
112 struct RemoveCombGroupsPattern : public OpRewritePattern<calyx::CombGroupOp> {
113  using OpRewritePattern::OpRewritePattern;
114 
115  RemoveCombGroupsPattern(MLIRContext *ctx, CombResRegMapping *mapping)
116  : OpRewritePattern(ctx), combResRegMapping(mapping) {}
117 
118  LogicalResult matchAndRewrite(calyx::CombGroupOp combGroup,
119  PatternRewriter &rewriter) const override {
120 
121  auto component = combGroup->getParentOfType<ComponentOp>();
122  auto group = rewriter.create<calyx::GroupOp>(combGroup.getLoc(),
123  combGroup.getName());
124  rewriter.mergeBlocks(combGroup.getBodyBlock(), group.getBodyBlock());
125  rewriter.replaceOp(combGroup, group);
126 
127  // Determine which cell results are read from the control schedule.
128  SetVector<Operation *> cellsAssigned;
129  for (auto op : group.getOps<calyx::AssignOp>()) {
130  auto defOp = dyn_cast<CellInterface>(op.getDest().getDefiningOp());
131  assert(defOp && "expected some assignment to a cell");
132  cellsAssigned.insert(defOp);
133  }
134 
135  rewriter.setInsertionPointToStart(group.getBodyBlock());
136  auto oneConstant = rewriter.create<hw::ConstantOp>(
137  group.getLoc(), APInt(1, 1, /*isSigned=*/true));
138 
139  // Maintain the set of cell results which have already been assigned to
140  // its register within this group.
141  SetVector<Value> alreadyAssignedResults;
142 
143  // Collect register done signals. These are needed for generating the
144  // GroupDone signal.
145  SetVector<Value> registerDoneSigs;
146 
147  // 1. Iterate over the cells assigned within the combinational group.
148  // 2. For any use of a cell result within the controls schedule.
149  // 3. Ensure that the cell result has a register.
150  // 4. Ensure that the cell result has been written to its register in this
151  // group.
152  // We do not replace uses of the combinational results now, since the
153  // following code relies on a checking cell result value use in the
154  // control schedule, which needs to remain even when two combinational
155  // groups assign to the same cell.
156  for (auto *cellOp : cellsAssigned) {
157  auto cell = dyn_cast<CellInterface>(cellOp);
158  for (auto combRes : cell.getOutputPorts()) {
159  for (auto &use : llvm::make_early_inc_range(combRes.getUses())) {
160  if (use.getOwner()->getParentOfType<calyx::ControlOp>()) {
161  auto combResReg = combResRegMapping->find(combRes);
162  if (combResReg == combResRegMapping->end()) {
163  // First time a registered variant of this result is needed.
164  auto reg = createReg(component, rewriter, combRes.getLoc(),
165  cell.instanceName(),
166  combRes.getType().getIntOrFloatBitWidth());
167  auto it = combResRegMapping->insert({combRes, reg});
168  combResReg = it.first;
169  }
170 
171  // Assign the cell result register - a register should only be
172  // assigned once within a group.
173  if (!alreadyAssignedResults.contains(combRes)) {
174  rewriter.create<AssignOp>(combRes.getLoc(),
175  combResReg->second.getIn(), combRes);
176  rewriter.create<AssignOp>(combRes.getLoc(),
177  combResReg->second.getWriteEn(),
178  oneConstant);
179  alreadyAssignedResults.insert(combRes);
180  }
181 
182  registerDoneSigs.insert(combResReg->second.getDone());
183  }
184  }
185  }
186  }
187 
188  // Create a group done op with the complex &[regDone] expression as a
189  // guard.
190  assert(!registerDoneSigs.empty() &&
191  "No registers assigned in the combinational group?");
192  rewriter.setInsertionPointToEnd(group.getBodyBlock());
193  rewriter.create<calyx::GroupDoneOp>(
194  group.getLoc(),
195  rewriter.create<hw::ConstantOp>(group.getLoc(), APInt(1, 1)),
196  rewriter.create<comb::AndOp>(combGroup.getLoc(), rewriter.getI1Type(),
197  registerDoneSigs.takeVector()));
198 
199  return success();
200  }
201 
202  mutable CombResRegMapping *combResRegMapping;
203 };
204 
205 struct RemoveCombGroupsPass
206  : public RemoveCombGroupsBase<RemoveCombGroupsPass> {
207  void runOnOperation() override;
208 
209  /// Removes 'with' groups from an operation and instead schedules the group
210  /// right before the oop.
211  void rewriteIfWithCombGroup(OpBuilder &builder) {
212  OpBuilder::InsertionGuard guard(builder);
213  getOperation().walk([&](IfOp ifOp) {
214  if (!ifOp.getGroupName())
215  return;
216  auto groupName = ifOp.getGroupName();
217  // Ensure that we're inside a sequential control composition.
218  wrapInsideOp<SeqOp>(builder, ifOp);
219  builder.setInsertionPoint(ifOp);
220  builder.create<EnableOp>(ifOp.getLoc(), groupName.value());
221  ifOp.removeGroupNameAttr();
222  });
223  }
224 
225  void rewriteWhileWithCombGroup(OpBuilder &builder) {
226  OpBuilder::InsertionGuard guard(builder);
227  getOperation().walk([&](WhileOp whileOp) {
228  if (!whileOp.getGroupName())
229  return;
230  auto groupName = whileOp.getGroupName().value();
231  // Ensure that we're inside a sequential control composition.
232  wrapInsideOp<SeqOp>(builder, whileOp);
233  builder.setInsertionPoint(whileOp);
234  builder.create<EnableOp>(whileOp.getLoc(), groupName);
235  whileOp.removeGroupNameAttr();
236 
237  // Also schedule the group at the end of the while body.
238  auto &curWhileBodyOp = whileOp.getBodyBlock()->front();
239  builder.setInsertionPointToStart(whileOp.getBodyBlock());
240  auto newSeqBody = builder.create<SeqOp>(curWhileBodyOp.getLoc());
241  builder.setInsertionPointToStart(newSeqBody.getBodyBlock());
242  auto condEnable =
243  builder.create<EnableOp>(curWhileBodyOp.getLoc(), groupName);
244  curWhileBodyOp.moveBefore(condEnable);
245  });
246  }
247 
248  void rewriteCellResults() {
249  for (auto &&[res, reg] : combResToReg) {
250  for (auto &use : llvm::make_early_inc_range(res.getUses())) {
251  if (use.getOwner()->getParentOfType<calyx::ControlOp>()) {
252  use.set(reg.getOut());
253  }
254  }
255  }
256  }
257 
258  CombResRegMapping combResToReg;
259 };
260 
261 } // end anonymous namespace
262 
263 void RemoveCombGroupsPass::runOnOperation() {
264  ConversionTarget target(getContext());
265  target.addLegalDialect<calyx::CalyxDialect>();
266  target.addLegalDialect<hw::HWDialect>();
267  target.addLegalDialect<comb::CombDialect>();
268  target.addIllegalOp<calyx::CombGroupOp>();
269 
270  RewritePatternSet patterns(&getContext());
271 
272  // Maintain a mapping from combinational result SSA values to the registered
273  // version of that combinational unit. This is used to avoid duplicating
274  // registers when cells are used across different groups.
275  patterns.add<RemoveCombGroupsPattern>(&getContext(), &combResToReg);
276 
277  if (applyPartialConversion(getOperation(), target, std::move(patterns))
278  .failed())
279  signalPassFailure();
280 
281  // Rewrite uses of the cell results to their registered variants.
282  rewriteCellResults();
283 
284  // Rewrite 'with' uses of the previously combinational groups.
285  OpBuilder builder(&getContext());
286  rewriteIfWithCombGroup(builder);
287  rewriteWhileWithCombGroup(builder);
288 }
289 
290 std::unique_ptr<mlir::Pass> circt::calyx::createRemoveCombGroupsPass() {
291  return std::make_unique<RemoveCombGroupsPass>();
292 }
assert(baseType &&"element must be base type")
int32_t width
Definition: FIRRTL.cpp:36
Builder builder
def create(data_type, value)
Definition: hw.py:393
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:20