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