CIRCT  19.0.0git
SCFToCalyx.cpp
Go to the documentation of this file.
1 //===- SCFToCalyx.cpp - SCF to Calyx pass entry point -----------*- 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 // This is the main SCF to Calyx conversion pass implementation.
10 //
11 //===----------------------------------------------------------------------===//
12 
14 #include "../PassDetail.h"
19 #include "circt/Dialect/HW/HWOps.h"
20 #include "mlir/Conversion/LLVMCommon/ConversionTarget.h"
21 #include "mlir/Conversion/LLVMCommon/Pattern.h"
22 #include "mlir/Dialect/Arith/IR/Arith.h"
23 #include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
24 #include "mlir/Dialect/Func/IR/FuncOps.h"
25 #include "mlir/Dialect/MemRef/IR/MemRef.h"
26 #include "mlir/Dialect/SCF/IR/SCF.h"
27 #include "mlir/IR/AsmState.h"
28 #include "mlir/IR/Matchers.h"
29 #include "mlir/Support/LogicalResult.h"
30 #include "mlir/Transforms/GreedyPatternRewriteDriver.h"
31 #include "llvm/ADT/TypeSwitch.h"
32 
33 #include <variant>
34 
35 using namespace llvm;
36 using namespace mlir;
37 using namespace mlir::arith;
38 using namespace mlir::cf;
39 using namespace mlir::func;
40 
41 namespace circt {
42 class ComponentLoweringStateInterface;
43 namespace scftocalyx {
44 
45 //===----------------------------------------------------------------------===//
46 // Utility types
47 //===----------------------------------------------------------------------===//
48 
49 class ScfWhileOp : public calyx::WhileOpInterface<scf::WhileOp> {
50 public:
51  explicit ScfWhileOp(scf::WhileOp op)
52  : calyx::WhileOpInterface<scf::WhileOp>(op) {}
53 
54  Block::BlockArgListType getBodyArgs() override {
55  return getOperation().getAfterArguments();
56  }
57 
58  Block *getBodyBlock() override { return &getOperation().getAfter().front(); }
59 
60  Block *getConditionBlock() override {
61  return &getOperation().getBefore().front();
62  }
63 
64  Value getConditionValue() override {
65  return getOperation().getConditionOp().getOperand(0);
66  }
67 
68  std::optional<int64_t> getBound() override { return std::nullopt; }
69 };
70 
71 class ScfForOp : public calyx::RepeatOpInterface<scf::ForOp> {
72 public:
73  explicit ScfForOp(scf::ForOp op) : calyx::RepeatOpInterface<scf::ForOp>(op) {}
74 
75  Block::BlockArgListType getBodyArgs() override {
76  return getOperation().getRegion().getArguments();
77  }
78 
79  Block *getBodyBlock() override {
80  return &getOperation().getRegion().getBlocks().front();
81  }
82 
83  std::optional<int64_t> getBound() override {
84  return constantTripCount(getOperation().getLowerBound(),
85  getOperation().getUpperBound(),
86  getOperation().getStep());
87  }
88 };
89 
90 //===----------------------------------------------------------------------===//
91 // Lowering state classes
92 //===----------------------------------------------------------------------===//
93 
95  /// While operation to schedule.
97 };
98 
100  /// For operation to schedule.
102  /// Bound
103  uint64_t bound;
104 };
105 
107  /// Instance for invoking.
108  calyx::InstanceOp instanceOp;
109  // CallOp for getting the arguments.
110  func::CallOp callOp;
111 };
112 
113 /// A variant of types representing scheduleable operations.
114 using Scheduleable = std::variant<calyx::GroupOp, WhileScheduleable,
116 
118  : calyx::LoopLoweringStateInterface<ScfWhileOp> {
119 public:
120  SmallVector<calyx::GroupOp> getWhileLoopInitGroups(ScfWhileOp op) {
121  return getLoopInitGroups(std::move(op));
122  }
124  OpBuilder &builder, ScfWhileOp op, calyx::ComponentOp componentOp,
125  Twine uniqueSuffix, MutableArrayRef<OpOperand> ops) {
126  return buildLoopIterArgAssignments(builder, std::move(op), componentOp,
127  uniqueSuffix, ops);
128  }
129  void addWhileLoopIterReg(ScfWhileOp op, calyx::RegisterOp reg, unsigned idx) {
130  return addLoopIterReg(std::move(op), reg, idx);
131  }
132  const DenseMap<unsigned, calyx::RegisterOp> &
134  return getLoopIterRegs(std::move(op));
135  }
136  void setWhileLoopLatchGroup(ScfWhileOp op, calyx::GroupOp group) {
137  return setLoopLatchGroup(std::move(op), group);
138  }
139  calyx::GroupOp getWhileLoopLatchGroup(ScfWhileOp op) {
140  return getLoopLatchGroup(std::move(op));
141  }
143  SmallVector<calyx::GroupOp> groups) {
144  return setLoopInitGroups(std::move(op), std::move(groups));
145  }
146 };
147 
150 public:
151  SmallVector<calyx::GroupOp> getForLoopInitGroups(ScfForOp op) {
152  return getLoopInitGroups(std::move(op));
153  }
155  OpBuilder &builder, ScfForOp op, calyx::ComponentOp componentOp,
156  Twine uniqueSuffix, MutableArrayRef<OpOperand> ops) {
157  return buildLoopIterArgAssignments(builder, std::move(op), componentOp,
158  uniqueSuffix, ops);
159  }
160  void addForLoopIterReg(ScfForOp op, calyx::RegisterOp reg, unsigned idx) {
161  return addLoopIterReg(std::move(op), reg, idx);
162  }
163  const DenseMap<unsigned, calyx::RegisterOp> &getForLoopIterRegs(ScfForOp op) {
164  return getLoopIterRegs(std::move(op));
165  }
166  calyx::RegisterOp getForLoopIterReg(ScfForOp op, unsigned idx) {
167  return getLoopIterReg(std::move(op), idx);
168  }
169  void setForLoopLatchGroup(ScfForOp op, calyx::GroupOp group) {
170  return setLoopLatchGroup(std::move(op), group);
171  }
172  calyx::GroupOp getForLoopLatchGroup(ScfForOp op) {
173  return getLoopLatchGroup(std::move(op));
174  }
175  void setForLoopInitGroups(ScfForOp op, SmallVector<calyx::GroupOp> groups) {
176  return setLoopInitGroups(std::move(op), std::move(groups));
177  }
178 };
179 
180 /// Handles the current state of lowering of a Calyx component. It is mainly
181 /// used as a key/value store for recording information during partial lowering,
182 /// which is required at later lowering passes.
186  public calyx::SchedulerInterface<Scheduleable> {
187 public:
188  ComponentLoweringState(calyx::ComponentOp component)
189  : calyx::ComponentLoweringStateInterface(component) {}
190 };
191 
192 //===----------------------------------------------------------------------===//
193 // Conversion patterns
194 //===----------------------------------------------------------------------===//
195 
196 /// Iterate through the operations of a source function and instantiate
197 /// components or primitives based on the type of the operations.
199  using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
200 
201  LogicalResult
203  PatternRewriter &rewriter) const override {
204  /// We walk the operations of the funcOp to ensure that all def's have
205  /// been visited before their uses.
206  bool opBuiltSuccessfully = true;
207  funcOp.walk([&](Operation *_op) {
208  opBuiltSuccessfully &=
209  TypeSwitch<mlir::Operation *, bool>(_op)
210  .template Case<arith::ConstantOp, ReturnOp, BranchOpInterface,
211  /// SCF
212  scf::YieldOp, scf::WhileOp, scf::ForOp,
213  /// memref
214  memref::AllocOp, memref::AllocaOp, memref::LoadOp,
215  memref::StoreOp,
216  /// standard arithmetic
217  AddIOp, SubIOp, CmpIOp, ShLIOp, ShRUIOp, ShRSIOp,
218  AndIOp, XOrIOp, OrIOp, ExtUIOp, ExtSIOp, TruncIOp,
219  MulIOp, DivUIOp, DivSIOp, RemUIOp, RemSIOp,
220  SelectOp, IndexCastOp, CallOp>(
221  [&](auto op) { return buildOp(rewriter, op).succeeded(); })
222  .template Case<FuncOp, scf::ConditionOp>([&](auto) {
223  /// Skip: these special cases will be handled separately.
224  return true;
225  })
226  .Default([&](auto op) {
227  op->emitError() << "Unhandled operation during BuildOpGroups()";
228  return false;
229  });
230 
231  return opBuiltSuccessfully ? WalkResult::advance()
232  : WalkResult::interrupt();
233  });
234 
235  return success(opBuiltSuccessfully);
236  }
237 
238 private:
239  /// Op builder specializations.
240  LogicalResult buildOp(PatternRewriter &rewriter, scf::YieldOp yieldOp) const;
241  LogicalResult buildOp(PatternRewriter &rewriter,
242  BranchOpInterface brOp) const;
243  LogicalResult buildOp(PatternRewriter &rewriter,
244  arith::ConstantOp constOp) const;
245  LogicalResult buildOp(PatternRewriter &rewriter, SelectOp op) const;
246  LogicalResult buildOp(PatternRewriter &rewriter, AddIOp op) const;
247  LogicalResult buildOp(PatternRewriter &rewriter, SubIOp op) const;
248  LogicalResult buildOp(PatternRewriter &rewriter, MulIOp op) const;
249  LogicalResult buildOp(PatternRewriter &rewriter, DivUIOp op) const;
250  LogicalResult buildOp(PatternRewriter &rewriter, DivSIOp op) const;
251  LogicalResult buildOp(PatternRewriter &rewriter, RemUIOp op) const;
252  LogicalResult buildOp(PatternRewriter &rewriter, RemSIOp op) const;
253  LogicalResult buildOp(PatternRewriter &rewriter, ShRUIOp op) const;
254  LogicalResult buildOp(PatternRewriter &rewriter, ShRSIOp op) const;
255  LogicalResult buildOp(PatternRewriter &rewriter, ShLIOp op) const;
256  LogicalResult buildOp(PatternRewriter &rewriter, AndIOp op) const;
257  LogicalResult buildOp(PatternRewriter &rewriter, OrIOp op) const;
258  LogicalResult buildOp(PatternRewriter &rewriter, XOrIOp op) const;
259  LogicalResult buildOp(PatternRewriter &rewriter, CmpIOp op) const;
260  LogicalResult buildOp(PatternRewriter &rewriter, TruncIOp op) const;
261  LogicalResult buildOp(PatternRewriter &rewriter, ExtUIOp op) const;
262  LogicalResult buildOp(PatternRewriter &rewriter, ExtSIOp op) const;
263  LogicalResult buildOp(PatternRewriter &rewriter, ReturnOp op) const;
264  LogicalResult buildOp(PatternRewriter &rewriter, IndexCastOp op) const;
265  LogicalResult buildOp(PatternRewriter &rewriter, memref::AllocOp op) const;
266  LogicalResult buildOp(PatternRewriter &rewriter, memref::AllocaOp op) const;
267  LogicalResult buildOp(PatternRewriter &rewriter, memref::LoadOp op) const;
268  LogicalResult buildOp(PatternRewriter &rewriter, memref::StoreOp op) const;
269  LogicalResult buildOp(PatternRewriter &rewriter, scf::WhileOp whileOp) const;
270  LogicalResult buildOp(PatternRewriter &rewriter, scf::ForOp forOp) const;
271  LogicalResult buildOp(PatternRewriter &rewriter, CallOp callOp) const;
272 
273  /// buildLibraryOp will build a TCalyxLibOp inside a TGroupOp based on the
274  /// source operation TSrcOp.
275  template <typename TGroupOp, typename TCalyxLibOp, typename TSrcOp>
276  LogicalResult buildLibraryOp(PatternRewriter &rewriter, TSrcOp op,
277  TypeRange srcTypes, TypeRange dstTypes) const {
278  SmallVector<Type> types;
279  llvm::append_range(types, srcTypes);
280  llvm::append_range(types, dstTypes);
281 
282  auto calyxOp =
283  getState<ComponentLoweringState>().getNewLibraryOpInstance<TCalyxLibOp>(
284  rewriter, op.getLoc(), types);
285 
286  auto directions = calyxOp.portDirections();
287  SmallVector<Value, 4> opInputPorts;
288  SmallVector<Value, 4> opOutputPorts;
289  for (auto dir : enumerate(directions)) {
290  if (dir.value() == calyx::Direction::Input)
291  opInputPorts.push_back(calyxOp.getResult(dir.index()));
292  else
293  opOutputPorts.push_back(calyxOp.getResult(dir.index()));
294  }
295  assert(
296  opInputPorts.size() == op->getNumOperands() &&
297  opOutputPorts.size() == op->getNumResults() &&
298  "Expected an equal number of in/out ports in the Calyx library op with "
299  "respect to the number of operands/results of the source operation.");
300 
301  /// Create assignments to the inputs of the library op.
302  auto group = createGroupForOp<TGroupOp>(rewriter, op);
303  rewriter.setInsertionPointToEnd(group.getBodyBlock());
304  for (auto dstOp : enumerate(opInputPorts))
305  rewriter.create<calyx::AssignOp>(op.getLoc(), dstOp.value(),
306  op->getOperand(dstOp.index()));
307 
308  /// Replace the result values of the source operator with the new operator.
309  for (auto res : enumerate(opOutputPorts)) {
310  getState<ComponentLoweringState>().registerEvaluatingGroup(res.value(),
311  group);
312  op->getResult(res.index()).replaceAllUsesWith(res.value());
313  }
314  return success();
315  }
316 
317  /// buildLibraryOp which provides in- and output types based on the operands
318  /// and results of the op argument.
319  template <typename TGroupOp, typename TCalyxLibOp, typename TSrcOp>
320  LogicalResult buildLibraryOp(PatternRewriter &rewriter, TSrcOp op) const {
321  return buildLibraryOp<TGroupOp, TCalyxLibOp, TSrcOp>(
322  rewriter, op, op.getOperandTypes(), op->getResultTypes());
323  }
324 
325  /// Creates a group named by the basic block which the input op resides in.
326  template <typename TGroupOp>
327  TGroupOp createGroupForOp(PatternRewriter &rewriter, Operation *op) const {
328  Block *block = op->getBlock();
329  auto groupName = getState<ComponentLoweringState>().getUniqueName(
330  loweringState().blockName(block));
331  return calyx::createGroup<TGroupOp>(
332  rewriter, getState<ComponentLoweringState>().getComponentOp(),
333  op->getLoc(), groupName);
334  }
335 
336  /// buildLibraryBinaryPipeOp will build a TCalyxLibBinaryPipeOp, to
337  /// deal with MulIOp, DivUIOp and RemUIOp.
338  template <typename TOpType, typename TSrcOp>
339  LogicalResult buildLibraryBinaryPipeOp(PatternRewriter &rewriter, TSrcOp op,
340  TOpType opPipe, Value out) const {
341  StringRef opName = TSrcOp::getOperationName().split(".").second;
342  Location loc = op.getLoc();
343  Type width = op.getResult().getType();
344  // Pass the result from the Operation to the Calyx primitive.
345  op.getResult().replaceAllUsesWith(out);
346  auto reg = createRegister(
347  op.getLoc(), rewriter, getComponent(), width.getIntOrFloatBitWidth(),
348  getState<ComponentLoweringState>().getUniqueName(opName));
349  // Operation pipelines are not combinational, so a GroupOp is required.
350  auto group = createGroupForOp<calyx::GroupOp>(rewriter, op);
351  OpBuilder builder(group->getRegion(0));
352  getState<ComponentLoweringState>().addBlockScheduleable(op->getBlock(),
353  group);
354 
355  rewriter.setInsertionPointToEnd(group.getBodyBlock());
356  rewriter.create<calyx::AssignOp>(loc, opPipe.getLeft(), op.getLhs());
357  rewriter.create<calyx::AssignOp>(loc, opPipe.getRight(), op.getRhs());
358  // Write the output to this register.
359  rewriter.create<calyx::AssignOp>(loc, reg.getIn(), out);
360  // The write enable port is high when the pipeline is done.
361  rewriter.create<calyx::AssignOp>(loc, reg.getWriteEn(), opPipe.getDone());
362  // Set pipelineOp to high as long as its done signal is not high.
363  // This prevents the pipelineOP from executing for the cycle that we write
364  // to register. To get !(pipelineOp.done) we do 1 xor pipelineOp.done
365  hw::ConstantOp c1 = createConstant(loc, rewriter, getComponent(), 1, 1);
366  rewriter.create<calyx::AssignOp>(
367  loc, opPipe.getGo(), c1,
368  comb::createOrFoldNot(group.getLoc(), opPipe.getDone(), builder));
369  // The group is done when the register write is complete.
370  rewriter.create<calyx::GroupDoneOp>(loc, reg.getDone());
371 
372  // Register the values for the pipeline.
373  getState<ComponentLoweringState>().registerEvaluatingGroup(out, group);
374  getState<ComponentLoweringState>().registerEvaluatingGroup(opPipe.getLeft(),
375  group);
376  getState<ComponentLoweringState>().registerEvaluatingGroup(
377  opPipe.getRight(), group);
378 
379  return success();
380  }
381 
382  /// Creates assignments within the provided group to the address ports of the
383  /// memoryOp based on the provided addressValues.
384  void assignAddressPorts(PatternRewriter &rewriter, Location loc,
385  calyx::GroupInterface group,
386  calyx::MemoryInterface memoryInterface,
387  Operation::operand_range addressValues) const {
388  IRRewriter::InsertionGuard guard(rewriter);
389  rewriter.setInsertionPointToEnd(group.getBody());
390  auto addrPorts = memoryInterface.addrPorts();
391  if (addressValues.empty()) {
392  assert(
393  addrPorts.size() == 1 &&
394  "We expected a 1 dimensional memory of size 1 because there were no "
395  "address assignment values");
396  // Assign to address 1'd0 in memory.
397  rewriter.create<calyx::AssignOp>(
398  loc, addrPorts[0],
399  createConstant(loc, rewriter, getComponent(), 1, 0));
400  } else {
401  assert(addrPorts.size() == addressValues.size() &&
402  "Mismatch between number of address ports of the provided memory "
403  "and address assignment values");
404  for (auto address : enumerate(addressValues))
405  rewriter.create<calyx::AssignOp>(loc, addrPorts[address.index()],
406  address.value());
407  }
408  }
409 };
410 
411 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
412  memref::LoadOp loadOp) const {
413  Value memref = loadOp.getMemref();
414  auto memoryInterface =
415  getState<ComponentLoweringState>().getMemoryInterface(memref);
416  auto group = createGroupForOp<calyx::GroupOp>(rewriter, loadOp);
417  assignAddressPorts(rewriter, loadOp.getLoc(), group, memoryInterface,
418  loadOp.getIndices());
419 
420  rewriter.setInsertionPointToEnd(group.getBodyBlock());
421 
422  bool needReg = true;
423  Value res;
424  Value regWriteEn =
425  createConstant(loadOp.getLoc(), rewriter, getComponent(), 1, 1);
426  if (memoryInterface.readEnOpt().has_value()) {
427  auto oneI1 =
428  calyx::createConstant(loadOp.getLoc(), rewriter, getComponent(), 1, 1);
429  rewriter.create<calyx::AssignOp>(loadOp.getLoc(), memoryInterface.readEn(),
430  oneI1);
431  regWriteEn = memoryInterface.done();
432  if (calyx::noStoresToMemory(memref) &&
433  calyx::singleLoadFromMemory(memref)) {
434  // Single load from memory; we do not need to write the output to a
435  // register. The readData value will be held until readEn is asserted
436  // again
437  needReg = false;
438  rewriter.create<calyx::GroupDoneOp>(loadOp.getLoc(),
439  memoryInterface.done());
440  // We refrain from replacing the loadOp result with
441  // memoryInterface.readData, since multiple loadOp's need to be converted
442  // to a single memory's ReadData. If this replacement is done now, we lose
443  // the link between which SSA memref::LoadOp values map to which groups
444  // for loading a value from the Calyx memory. At this point of lowering,
445  // we keep the memref::LoadOp SSA value, and do value replacement _after_
446  // control has been generated (see LateSSAReplacement). This is *vital*
447  // for things such as calyx::InlineCombGroups to be able to properly track
448  // which memory assignment groups belong to which accesses.
449  res = loadOp.getResult();
450  }
451  } else if (memoryInterface.contentEnOpt().has_value()) {
452  auto oneI1 =
453  calyx::createConstant(loadOp.getLoc(), rewriter, getComponent(), 1, 1);
454  auto zeroI1 =
455  calyx::createConstant(loadOp.getLoc(), rewriter, getComponent(), 1, 0);
456  rewriter.create<calyx::AssignOp>(loadOp.getLoc(),
457  memoryInterface.contentEn(), oneI1);
458  rewriter.create<calyx::AssignOp>(loadOp.getLoc(), memoryInterface.writeEn(),
459  zeroI1);
460  regWriteEn = memoryInterface.done();
461  if (calyx::noStoresToMemory(memref) &&
462  calyx::singleLoadFromMemory(memref)) {
463  // Single load from memory; we do not need to write the output to a
464  // register. The readData value will be held until contentEn is asserted
465  // again
466  needReg = false;
467  rewriter.create<calyx::GroupDoneOp>(loadOp.getLoc(),
468  memoryInterface.done());
469  // We refrain from replacing the loadOp result with
470  // memoryInterface.readData, since multiple loadOp's need to be converted
471  // to a single memory's ReadData. If this replacement is done now, we lose
472  // the link between which SSA memref::LoadOp values map to which groups
473  // for loading a value from the Calyx memory. At this point of lowering,
474  // we keep the memref::LoadOp SSA value, and do value replacement _after_
475  // control has been generated (see LateSSAReplacement). This is *vital*
476  // for things such as calyx::InlineCombGroups to be able to properly track
477  // which memory assignment groups belong to which accesses.
478  res = loadOp.getResult();
479  }
480  }
481 
482  if (needReg) {
483  // Multiple loads from the same memory; In this case, we _may_ have a
484  // structural hazard in the design we generate. To get around this, we
485  // conservatively place a register in front of each load operation, and
486  // replace all uses of the loaded value with the register output. Reading
487  // for sequential memories will cause a read to take at least 2 cycles,
488  // but it will usually be better because combinational reads on memories
489  // can significantly decrease the maximum achievable frequency.
490  auto reg = createRegister(
491  loadOp.getLoc(), rewriter, getComponent(),
492  loadOp.getMemRefType().getElementTypeBitWidth(),
493  getState<ComponentLoweringState>().getUniqueName("load"));
494  rewriter.setInsertionPointToEnd(group.getBodyBlock());
495  rewriter.create<calyx::AssignOp>(loadOp.getLoc(), reg.getIn(),
496  memoryInterface.readData());
497  rewriter.create<calyx::AssignOp>(loadOp.getLoc(), reg.getWriteEn(),
498  regWriteEn);
499  rewriter.create<calyx::GroupDoneOp>(loadOp.getLoc(), reg.getDone());
500  loadOp.getResult().replaceAllUsesWith(reg.getOut());
501  res = reg.getOut();
502  }
503 
504  getState<ComponentLoweringState>().registerEvaluatingGroup(res, group);
505  getState<ComponentLoweringState>().addBlockScheduleable(loadOp->getBlock(),
506  group);
507  return success();
508 }
509 
510 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
511  memref::StoreOp storeOp) const {
512  auto memoryInterface = getState<ComponentLoweringState>().getMemoryInterface(
513  storeOp.getMemref());
514  auto group = createGroupForOp<calyx::GroupOp>(rewriter, storeOp);
515 
516  // This is a sequential group, so register it as being scheduleable for the
517  // block.
518  getState<ComponentLoweringState>().addBlockScheduleable(storeOp->getBlock(),
519  group);
520  assignAddressPorts(rewriter, storeOp.getLoc(), group, memoryInterface,
521  storeOp.getIndices());
522  rewriter.setInsertionPointToEnd(group.getBodyBlock());
523  rewriter.create<calyx::AssignOp>(
524  storeOp.getLoc(), memoryInterface.writeData(), storeOp.getValueToStore());
525  rewriter.create<calyx::AssignOp>(
526  storeOp.getLoc(), memoryInterface.writeEn(),
527  createConstant(storeOp.getLoc(), rewriter, getComponent(), 1, 1));
528  if (memoryInterface.contentEnOpt().has_value()) {
529  // If memory has content enable, it must be asserted when writing
530  rewriter.create<calyx::AssignOp>(
531  storeOp.getLoc(), memoryInterface.contentEn(),
532  createConstant(storeOp.getLoc(), rewriter, getComponent(), 1, 1));
533  }
534  rewriter.create<calyx::GroupDoneOp>(storeOp.getLoc(), memoryInterface.done());
535 
536  return success();
537 }
538 
539 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
540  MulIOp mul) const {
541  Location loc = mul.getLoc();
542  Type width = mul.getResult().getType(), one = rewriter.getI1Type();
543  auto mulPipe =
544  getState<ComponentLoweringState>()
545  .getNewLibraryOpInstance<calyx::MultPipeLibOp>(
546  rewriter, loc, {one, one, one, width, width, width, one});
547  return buildLibraryBinaryPipeOp<calyx::MultPipeLibOp>(
548  rewriter, mul, mulPipe,
549  /*out=*/mulPipe.getOut());
550 }
551 
552 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
553  DivUIOp div) const {
554  Location loc = div.getLoc();
555  Type width = div.getResult().getType(), one = rewriter.getI1Type();
556  auto divPipe =
557  getState<ComponentLoweringState>()
558  .getNewLibraryOpInstance<calyx::DivUPipeLibOp>(
559  rewriter, loc, {one, one, one, width, width, width, one});
560  return buildLibraryBinaryPipeOp<calyx::DivUPipeLibOp>(
561  rewriter, div, divPipe,
562  /*out=*/divPipe.getOut());
563 }
564 
565 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
566  DivSIOp div) const {
567  Location loc = div.getLoc();
568  Type width = div.getResult().getType(), one = rewriter.getI1Type();
569  auto divPipe =
570  getState<ComponentLoweringState>()
571  .getNewLibraryOpInstance<calyx::DivSPipeLibOp>(
572  rewriter, loc, {one, one, one, width, width, width, one});
573  return buildLibraryBinaryPipeOp<calyx::DivSPipeLibOp>(
574  rewriter, div, divPipe,
575  /*out=*/divPipe.getOut());
576 }
577 
578 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
579  RemUIOp rem) const {
580  Location loc = rem.getLoc();
581  Type width = rem.getResult().getType(), one = rewriter.getI1Type();
582  auto remPipe =
583  getState<ComponentLoweringState>()
584  .getNewLibraryOpInstance<calyx::RemUPipeLibOp>(
585  rewriter, loc, {one, one, one, width, width, width, one});
586  return buildLibraryBinaryPipeOp<calyx::RemUPipeLibOp>(
587  rewriter, rem, remPipe,
588  /*out=*/remPipe.getOut());
589 }
590 
591 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
592  RemSIOp rem) const {
593  Location loc = rem.getLoc();
594  Type width = rem.getResult().getType(), one = rewriter.getI1Type();
595  auto remPipe =
596  getState<ComponentLoweringState>()
597  .getNewLibraryOpInstance<calyx::RemSPipeLibOp>(
598  rewriter, loc, {one, one, one, width, width, width, one});
599  return buildLibraryBinaryPipeOp<calyx::RemSPipeLibOp>(
600  rewriter, rem, remPipe,
601  /*out=*/remPipe.getOut());
602 }
603 
604 template <typename TAllocOp>
605 static LogicalResult buildAllocOp(ComponentLoweringState &componentState,
606  PatternRewriter &rewriter, TAllocOp allocOp) {
607  rewriter.setInsertionPointToStart(
608  componentState.getComponentOp().getBodyBlock());
609  MemRefType memtype = allocOp.getType();
610  SmallVector<int64_t> addrSizes;
611  SmallVector<int64_t> sizes;
612  for (int64_t dim : memtype.getShape()) {
613  sizes.push_back(dim);
614  addrSizes.push_back(calyx::handleZeroWidth(dim));
615  }
616  // If memref has no size (e.g., memref<i32>) create a 1 dimensional memory of
617  // size 1.
618  if (sizes.empty() && addrSizes.empty()) {
619  sizes.push_back(1);
620  addrSizes.push_back(1);
621  }
622  auto memoryOp = rewriter.create<calyx::SeqMemoryOp>(
623  allocOp.getLoc(), componentState.getUniqueName("mem"),
624  memtype.getElementType().getIntOrFloatBitWidth(), sizes, addrSizes);
625  // Externalize memories by default. This makes it easier for the native
626  // compiler to provide initialized memories.
627  memoryOp->setAttr("external",
628  IntegerAttr::get(rewriter.getI1Type(), llvm::APInt(1, 1)));
629  componentState.registerMemoryInterface(allocOp.getResult(),
630  calyx::MemoryInterface(memoryOp));
631  return success();
632 }
633 
634 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
635  memref::AllocOp allocOp) const {
636  return buildAllocOp(getState<ComponentLoweringState>(), rewriter, allocOp);
637 }
638 
639 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
640  memref::AllocaOp allocOp) const {
641  return buildAllocOp(getState<ComponentLoweringState>(), rewriter, allocOp);
642 }
643 
644 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
645  scf::YieldOp yieldOp) const {
646  if (yieldOp.getOperands().empty()) {
647  // If yield operands are empty, we assume we have a for loop.
648  auto forOp = dyn_cast<scf::ForOp>(yieldOp->getParentOp());
649  assert(forOp && "Empty yieldOps should only be located within ForOps");
650  ScfForOp forOpInterface(forOp);
651 
652  // Get the ForLoop's Induction Register.
653  auto inductionReg =
654  getState<ComponentLoweringState>().getForLoopIterReg(forOpInterface, 0);
655 
656  Type regWidth = inductionReg.getOut().getType();
657  // Adder should have same width as the inductionReg.
658  SmallVector<Type> types(3, regWidth);
659  auto addOp = getState<ComponentLoweringState>()
660  .getNewLibraryOpInstance<calyx::AddLibOp>(
661  rewriter, forOp.getLoc(), types);
662 
663  auto directions = addOp.portDirections();
664  // For an add operation, we expect two input ports and one output port
665  SmallVector<Value, 2> opInputPorts;
666  Value opOutputPort;
667  for (auto dir : enumerate(directions)) {
668  switch (dir.value()) {
670  opInputPorts.push_back(addOp.getResult(dir.index()));
671  break;
672  }
674  opOutputPort = addOp.getResult(dir.index());
675  break;
676  }
677  }
678  }
679 
680  // "Latch Group" increments inductionReg by forLoop's step value.
681  calyx::ComponentOp componentOp =
682  getState<ComponentLoweringState>().getComponentOp();
683  SmallVector<StringRef, 4> groupIdentifier = {
684  "incr", getState<ComponentLoweringState>().getUniqueName(forOp),
685  "induction", "var"};
686  auto groupOp = calyx::createGroup<calyx::GroupOp>(
687  rewriter, componentOp, forOp.getLoc(),
688  llvm::join(groupIdentifier, "_"));
689  rewriter.setInsertionPointToEnd(groupOp.getBodyBlock());
690 
691  // Assign inductionReg.out to the left port of the adder.
692  Value leftOp = opInputPorts.front();
693  rewriter.create<calyx::AssignOp>(forOp.getLoc(), leftOp,
694  inductionReg.getOut());
695  // Assign forOp.getConstantStep to the right port of the adder.
696  Value rightOp = opInputPorts.back();
697  rewriter.create<calyx::AssignOp>(
698  forOp.getLoc(), rightOp,
699  createConstant(forOp->getLoc(), rewriter, componentOp,
700  regWidth.getIntOrFloatBitWidth(),
701  forOp.getConstantStep().value().getSExtValue()));
702  // Assign adder's output port to inductionReg.
703  buildAssignmentsForRegisterWrite(rewriter, groupOp, componentOp,
704  inductionReg, opOutputPort);
705  // Set group as For Loop's "latch" group.
706  getState<ComponentLoweringState>().setForLoopLatchGroup(forOpInterface,
707  groupOp);
708  getState<ComponentLoweringState>().registerEvaluatingGroup(opOutputPort,
709  groupOp);
710  return success();
711  }
712  // If yieldOp for a for loop is not empty, then we do not transform for loop.
713  if (dyn_cast<scf::ForOp>(yieldOp->getParentOp())) {
714  return yieldOp.getOperation()->emitError()
715  << "Currently do not support non-empty yield operations inside for "
716  "loops. Run --scf-for-to-while before running --scf-to-calyx.";
717  }
718 
719  auto whileOp = dyn_cast<scf::WhileOp>(yieldOp->getParentOp());
720  if (!whileOp) {
721  return yieldOp.getOperation()->emitError()
722  << "Currently only support yield operations inside for and while "
723  "loops.";
724  }
725  ScfWhileOp whileOpInterface(whileOp);
726 
727  auto assignGroup =
728  getState<ComponentLoweringState>().buildWhileLoopIterArgAssignments(
729  rewriter, whileOpInterface,
730  getState<ComponentLoweringState>().getComponentOp(),
731  getState<ComponentLoweringState>().getUniqueName(whileOp) + "_latch",
732  yieldOp->getOpOperands());
733  getState<ComponentLoweringState>().setWhileLoopLatchGroup(whileOpInterface,
734  assignGroup);
735  return success();
736 }
737 
738 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
739  BranchOpInterface brOp) const {
740  /// Branch argument passing group creation
741  /// Branch operands are passed through registers. In BuildBasicBlockRegs we
742  /// created registers for all branch arguments of each block. We now
743  /// create groups for assigning values to these registers.
744  Block *srcBlock = brOp->getBlock();
745  for (auto succBlock : enumerate(brOp->getSuccessors())) {
746  auto succOperands = brOp.getSuccessorOperands(succBlock.index());
747  if (succOperands.empty())
748  continue;
749  // Create operand passing group
750  std::string groupName = loweringState().blockName(srcBlock) + "_to_" +
751  loweringState().blockName(succBlock.value());
752  auto groupOp = calyx::createGroup<calyx::GroupOp>(rewriter, getComponent(),
753  brOp.getLoc(), groupName);
754  // Fetch block argument registers associated with the basic block
755  auto dstBlockArgRegs =
756  getState<ComponentLoweringState>().getBlockArgRegs(succBlock.value());
757  // Create register assignment for each block argument
758  for (auto arg : enumerate(succOperands.getForwardedOperands())) {
759  auto reg = dstBlockArgRegs[arg.index()];
761  rewriter, groupOp,
762  getState<ComponentLoweringState>().getComponentOp(), reg,
763  arg.value());
764  }
765  /// Register the group as a block argument group, to be executed
766  /// when entering the successor block from this block (srcBlock).
767  getState<ComponentLoweringState>().addBlockArgGroup(
768  srcBlock, succBlock.value(), groupOp);
769  }
770  return success();
771 }
772 
773 /// For each return statement, we create a new group for assigning to the
774 /// previously created return value registers.
775 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
776  ReturnOp retOp) const {
777  if (retOp.getNumOperands() == 0)
778  return success();
779 
780  std::string groupName =
781  getState<ComponentLoweringState>().getUniqueName("ret_assign");
782  auto groupOp = calyx::createGroup<calyx::GroupOp>(rewriter, getComponent(),
783  retOp.getLoc(), groupName);
784  for (auto op : enumerate(retOp.getOperands())) {
785  auto reg = getState<ComponentLoweringState>().getReturnReg(op.index());
787  rewriter, groupOp, getState<ComponentLoweringState>().getComponentOp(),
788  reg, op.value());
789  }
790  /// Schedule group for execution for when executing the return op block.
791  getState<ComponentLoweringState>().addBlockScheduleable(retOp->getBlock(),
792  groupOp);
793  return success();
794 }
795 
796 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
797  arith::ConstantOp constOp) const {
798  /// Move constant operations to the compOp body as hw::ConstantOp's.
799  APInt value;
800  calyx::matchConstantOp(constOp, value);
801  auto hwConstOp = rewriter.replaceOpWithNewOp<hw::ConstantOp>(constOp, value);
802  hwConstOp->moveAfter(getComponent().getBodyBlock(),
803  getComponent().getBodyBlock()->begin());
804  return success();
805 }
806 
807 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
808  AddIOp op) const {
809  return buildLibraryOp<calyx::CombGroupOp, calyx::AddLibOp>(rewriter, op);
810 }
811 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
812  SubIOp op) const {
813  return buildLibraryOp<calyx::CombGroupOp, calyx::SubLibOp>(rewriter, op);
814 }
815 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
816  ShRUIOp op) const {
817  return buildLibraryOp<calyx::CombGroupOp, calyx::RshLibOp>(rewriter, op);
818 }
819 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
820  ShRSIOp op) const {
821  return buildLibraryOp<calyx::CombGroupOp, calyx::SrshLibOp>(rewriter, op);
822 }
823 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
824  ShLIOp op) const {
825  return buildLibraryOp<calyx::CombGroupOp, calyx::LshLibOp>(rewriter, op);
826 }
827 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
828  AndIOp op) const {
829  return buildLibraryOp<calyx::CombGroupOp, calyx::AndLibOp>(rewriter, op);
830 }
831 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
832  OrIOp op) const {
833  return buildLibraryOp<calyx::CombGroupOp, calyx::OrLibOp>(rewriter, op);
834 }
835 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
836  XOrIOp op) const {
837  return buildLibraryOp<calyx::CombGroupOp, calyx::XorLibOp>(rewriter, op);
838 }
839 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
840  SelectOp op) const {
841  return buildLibraryOp<calyx::CombGroupOp, calyx::MuxLibOp>(rewriter, op);
842 }
843 
844 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
845  CmpIOp op) const {
846  switch (op.getPredicate()) {
847  case CmpIPredicate::eq:
848  return buildLibraryOp<calyx::CombGroupOp, calyx::EqLibOp>(rewriter, op);
849  case CmpIPredicate::ne:
850  return buildLibraryOp<calyx::CombGroupOp, calyx::NeqLibOp>(rewriter, op);
851  case CmpIPredicate::uge:
852  return buildLibraryOp<calyx::CombGroupOp, calyx::GeLibOp>(rewriter, op);
853  case CmpIPredicate::ult:
854  return buildLibraryOp<calyx::CombGroupOp, calyx::LtLibOp>(rewriter, op);
855  case CmpIPredicate::ugt:
856  return buildLibraryOp<calyx::CombGroupOp, calyx::GtLibOp>(rewriter, op);
857  case CmpIPredicate::ule:
858  return buildLibraryOp<calyx::CombGroupOp, calyx::LeLibOp>(rewriter, op);
859  case CmpIPredicate::sge:
860  return buildLibraryOp<calyx::CombGroupOp, calyx::SgeLibOp>(rewriter, op);
861  case CmpIPredicate::slt:
862  return buildLibraryOp<calyx::CombGroupOp, calyx::SltLibOp>(rewriter, op);
863  case CmpIPredicate::sgt:
864  return buildLibraryOp<calyx::CombGroupOp, calyx::SgtLibOp>(rewriter, op);
865  case CmpIPredicate::sle:
866  return buildLibraryOp<calyx::CombGroupOp, calyx::SleLibOp>(rewriter, op);
867  }
868  llvm_unreachable("unsupported comparison predicate");
869 }
870 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
871  TruncIOp op) const {
872  return buildLibraryOp<calyx::CombGroupOp, calyx::SliceLibOp>(
873  rewriter, op, {op.getOperand().getType()}, {op.getType()});
874 }
875 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
876  ExtUIOp op) const {
877  return buildLibraryOp<calyx::CombGroupOp, calyx::PadLibOp>(
878  rewriter, op, {op.getOperand().getType()}, {op.getType()});
879 }
880 
881 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
882  ExtSIOp op) const {
883  return buildLibraryOp<calyx::CombGroupOp, calyx::ExtSILibOp>(
884  rewriter, op, {op.getOperand().getType()}, {op.getType()});
885 }
886 
887 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
888  IndexCastOp op) const {
889  Type sourceType = calyx::convIndexType(rewriter, op.getOperand().getType());
890  Type targetType = calyx::convIndexType(rewriter, op.getResult().getType());
891  unsigned targetBits = targetType.getIntOrFloatBitWidth();
892  unsigned sourceBits = sourceType.getIntOrFloatBitWidth();
893  LogicalResult res = success();
894 
895  if (targetBits == sourceBits) {
896  /// Drop the index cast and replace uses of the target value with the source
897  /// value.
898  op.getResult().replaceAllUsesWith(op.getOperand());
899  } else {
900  /// pad/slice the source operand.
901  if (sourceBits > targetBits)
902  res = buildLibraryOp<calyx::CombGroupOp, calyx::SliceLibOp>(
903  rewriter, op, {sourceType}, {targetType});
904  else
905  res = buildLibraryOp<calyx::CombGroupOp, calyx::PadLibOp>(
906  rewriter, op, {sourceType}, {targetType});
907  }
908  rewriter.eraseOp(op);
909  return res;
910 }
911 
912 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
913  scf::WhileOp whileOp) const {
914  // Only need to add the whileOp to the BlockSchedulables scheduler interface.
915  // Everything else was handled in the `BuildWhileGroups` pattern.
916  ScfWhileOp scfWhileOp(whileOp);
917  getState<ComponentLoweringState>().addBlockScheduleable(
918  whileOp.getOperation()->getBlock(), WhileScheduleable{scfWhileOp});
919  return success();
920 }
921 
922 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
923  scf::ForOp forOp) const {
924  // Only need to add the forOp to the BlockSchedulables scheduler interface.
925  // Everything else was handled in the `BuildForGroups` pattern.
926  ScfForOp scfForOp(forOp);
927  // If we cannot compute the trip count of the for loop, then we should
928  // emit an error saying to use --scf-for-to-while
929  std::optional<uint64_t> bound = scfForOp.getBound();
930  if (!bound.has_value()) {
931  return scfForOp.getOperation()->emitError()
932  << "Loop bound not statically known. Should "
933  "transform into while loop using `--scf-for-to-while` before "
934  "running --lower-scf-to-calyx.";
935  }
936  getState<ComponentLoweringState>().addBlockScheduleable(
937  forOp.getOperation()->getBlock(), ForScheduleable{
938  scfForOp,
939  bound.value(),
940  });
941  return success();
942 }
943 
944 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
945  CallOp callOp) const {
946  std::string instanceName = calyx::getInstanceName(callOp);
947  calyx::InstanceOp instanceOp =
948  getState<ComponentLoweringState>().getInstance(instanceName);
949  SmallVector<Value, 4> outputPorts;
950  auto portInfos = instanceOp.getReferencedComponent().getPortInfo();
951  for (auto [idx, portInfo] : enumerate(portInfos)) {
952  if (portInfo.direction == calyx::Direction::Output)
953  outputPorts.push_back(instanceOp.getResult(idx));
954  }
955 
956  // Replacing a CallOp results in the out port of the instance.
957  for (auto [idx, result] : llvm::enumerate(callOp.getResults()))
958  rewriter.replaceAllUsesWith(result, outputPorts[idx]);
959 
960  // CallScheduleanle requires an instance, while CallOp can be used to get the
961  // input ports.
962  getState<ComponentLoweringState>().addBlockScheduleable(
963  callOp.getOperation()->getBlock(), CallScheduleable{instanceOp, callOp});
964  return success();
965 }
966 
967 /// Inlines Calyx ExecuteRegionOp operations within their parent blocks.
968 /// An execution region op (ERO) is inlined by:
969 /// i : add a sink basic block for all yield operations inside the
970 /// ERO to jump to
971 /// ii : Rewrite scf.yield calls inside the ERO to branch to the sink block
972 /// iii: inline the ERO region
973 /// TODO(#1850) evaluate the usefulness of this lowering pattern.
975  : public OpRewritePattern<scf::ExecuteRegionOp> {
976  using OpRewritePattern::OpRewritePattern;
977 
978  LogicalResult matchAndRewrite(scf::ExecuteRegionOp execOp,
979  PatternRewriter &rewriter) const override {
980  /// Determine type of "yield" operations inside the ERO.
981  TypeRange yieldTypes = execOp.getResultTypes();
982 
983  /// Create sink basic block and rewrite uses of yield results to sink block
984  /// arguments.
985  rewriter.setInsertionPointAfter(execOp);
986  auto *sinkBlock = rewriter.splitBlock(
987  execOp->getBlock(),
988  execOp.getOperation()->getIterator()->getNextNode()->getIterator());
989  sinkBlock->addArguments(
990  yieldTypes,
991  SmallVector<Location, 4>(yieldTypes.size(), rewriter.getUnknownLoc()));
992  for (auto res : enumerate(execOp.getResults()))
993  res.value().replaceAllUsesWith(sinkBlock->getArgument(res.index()));
994 
995  /// Rewrite yield calls as branches.
996  for (auto yieldOp :
997  make_early_inc_range(execOp.getRegion().getOps<scf::YieldOp>())) {
998  rewriter.setInsertionPointAfter(yieldOp);
999  rewriter.replaceOpWithNewOp<BranchOp>(yieldOp, sinkBlock,
1000  yieldOp.getOperands());
1001  }
1002 
1003  /// Inline the regionOp.
1004  auto *preBlock = execOp->getBlock();
1005  auto *execOpEntryBlock = &execOp.getRegion().front();
1006  auto *postBlock = execOp->getBlock()->splitBlock(execOp);
1007  rewriter.inlineRegionBefore(execOp.getRegion(), postBlock);
1008  rewriter.mergeBlocks(postBlock, preBlock);
1009  rewriter.eraseOp(execOp);
1010 
1011  /// Finally, erase the unused entry block of the execOp region.
1012  rewriter.mergeBlocks(execOpEntryBlock, preBlock);
1013 
1014  return success();
1015  }
1016 };
1017 
1018 /// Creates a new Calyx component for each FuncOp in the program.
1020  using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
1021 
1022  LogicalResult
1024  PatternRewriter &rewriter) const override {
1025  /// Maintain a mapping between funcOp input arguments and the port index
1026  /// which the argument will eventually map to.
1027  DenseMap<Value, unsigned> funcOpArgRewrites;
1028 
1029  /// Maintain a mapping between funcOp output indexes and the component
1030  /// output port index which the return value will eventually map to.
1031  DenseMap<unsigned, unsigned> funcOpResultMapping;
1032 
1033  /// Maintain a mapping between an external memory argument (identified by a
1034  /// memref) and eventual component input- and output port indices that will
1035  /// map to the memory ports. The pair denotes the start index of the memory
1036  /// ports in the in- and output ports of the component. Ports are expected
1037  /// to be ordered in the same manner as they are added by
1038  /// calyx::appendPortsForExternalMemref.
1039  DenseMap<Value, std::pair<unsigned, unsigned>> extMemoryCompPortIndices;
1040 
1041  /// Create I/O ports. Maintain separate in/out port vectors to determine
1042  /// which port index each function argument will eventually map to.
1043  SmallVector<calyx::PortInfo> inPorts, outPorts;
1044  FunctionType funcType = funcOp.getFunctionType();
1045  unsigned extMemCounter = 0;
1046  for (auto arg : enumerate(funcOp.getArguments())) {
1047  if (arg.value().getType().isa<MemRefType>()) {
1048  /// External memories
1049  auto memName =
1050  "ext_mem" + std::to_string(extMemoryCompPortIndices.size());
1051  extMemoryCompPortIndices[arg.value()] = {inPorts.size(),
1052  outPorts.size()};
1053  calyx::appendPortsForExternalMemref(rewriter, memName, arg.value(),
1054  extMemCounter++, inPorts, outPorts);
1055  } else {
1056  /// Single-port arguments
1057  std::string inName;
1058  if (auto portNameAttr = funcOp.getArgAttrOfType<StringAttr>(
1059  arg.index(), scfToCalyx::sPortNameAttr))
1060  inName = portNameAttr.str();
1061  else
1062  inName = "in" + std::to_string(arg.index());
1063  funcOpArgRewrites[arg.value()] = inPorts.size();
1064  inPorts.push_back(calyx::PortInfo{
1065  rewriter.getStringAttr(inName),
1066  calyx::convIndexType(rewriter, arg.value().getType()),
1068  DictionaryAttr::get(rewriter.getContext(), {})});
1069  }
1070  }
1071  for (auto res : enumerate(funcType.getResults())) {
1072  std::string resName;
1073  if (auto portNameAttr = funcOp.getResultAttrOfType<StringAttr>(
1074  res.index(), scfToCalyx::sPortNameAttr))
1075  resName = portNameAttr.str();
1076  else
1077  resName = "out" + std::to_string(res.index());
1078  funcOpResultMapping[res.index()] = outPorts.size();
1079  outPorts.push_back(calyx::PortInfo{
1080  rewriter.getStringAttr(resName),
1081  calyx::convIndexType(rewriter, res.value()), calyx::Direction::Output,
1082  DictionaryAttr::get(rewriter.getContext(), {})});
1083  }
1084 
1085  /// We've now recorded all necessary indices. Merge in- and output ports
1086  /// and add the required mandatory component ports.
1087  auto ports = inPorts;
1088  llvm::append_range(ports, outPorts);
1089  calyx::addMandatoryComponentPorts(rewriter, ports);
1090 
1091  /// Create a calyx::ComponentOp corresponding to the to-be-lowered function.
1092  auto compOp = rewriter.create<calyx::ComponentOp>(
1093  funcOp.getLoc(), rewriter.getStringAttr(funcOp.getSymName()), ports);
1094 
1095  std::string funcName = "func_" + funcOp.getSymName().str();
1096  rewriter.modifyOpInPlace(funcOp, [&]() { funcOp.setSymName(funcName); });
1097 
1098  /// Mark this component as the toplevel.
1099  compOp->setAttr("toplevel", rewriter.getUnitAttr());
1100 
1101  /// Store the function-to-component mapping.
1102  functionMapping[funcOp] = compOp;
1103  auto *compState = loweringState().getState<ComponentLoweringState>(compOp);
1104  compState->setFuncOpResultMapping(funcOpResultMapping);
1105 
1106  /// Rewrite funcOp SSA argument values to the CompOp arguments.
1107  for (auto &mapping : funcOpArgRewrites)
1108  mapping.getFirst().replaceAllUsesWith(
1109  compOp.getArgument(mapping.getSecond()));
1110 
1111  /// Register external memories
1112  for (auto extMemPortIndices : extMemoryCompPortIndices) {
1113  /// Create a mapping for the in- and output ports using the Calyx memory
1114  /// port structure.
1115  calyx::MemoryPortsImpl extMemPorts;
1116  unsigned inPortsIt = extMemPortIndices.getSecond().first;
1117  unsigned outPortsIt = extMemPortIndices.getSecond().second +
1118  compOp.getInputPortInfo().size();
1119  extMemPorts.readData = compOp.getArgument(inPortsIt++);
1120  extMemPorts.done = compOp.getArgument(inPortsIt);
1121  extMemPorts.writeData = compOp.getArgument(outPortsIt++);
1122  unsigned nAddresses = extMemPortIndices.getFirst()
1123  .getType()
1124  .cast<MemRefType>()
1125  .getShape()
1126  .size();
1127  for (unsigned j = 0; j < nAddresses; ++j)
1128  extMemPorts.addrPorts.push_back(compOp.getArgument(outPortsIt++));
1129  extMemPorts.writeEn = compOp.getArgument(outPortsIt);
1130 
1131  /// Register the external memory ports as a memory interface within the
1132  /// component.
1133  compState->registerMemoryInterface(extMemPortIndices.getFirst(),
1134  calyx::MemoryInterface(extMemPorts));
1135  }
1136 
1137  return success();
1138  }
1139 };
1140 
1141 /// In BuildWhileGroups, a register is created for each iteration argumenet of
1142 /// the while op. These registers are then written to on the while op
1143 /// terminating yield operation alongside before executing the whileOp in the
1144 /// schedule, to set the initial values of the argument registers.
1146  using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
1147 
1148  LogicalResult
1150  PatternRewriter &rewriter) const override {
1151  LogicalResult res = success();
1152  funcOp.walk([&](Operation *op) {
1153  // Only work on ops that support the ScfWhileOp.
1154  if (!isa<scf::WhileOp>(op))
1155  return WalkResult::advance();
1156 
1157  auto scfWhileOp = cast<scf::WhileOp>(op);
1158  ScfWhileOp whileOp(scfWhileOp);
1159 
1160  getState<ComponentLoweringState>().setUniqueName(whileOp.getOperation(),
1161  "while");
1162 
1163  /// Check for do-while loops.
1164  /// TODO(mortbopet) can we support these? for now, do not support loops
1165  /// where iterargs are changed in the 'before' region. scf.WhileOp also
1166  /// has support for different types of iter_args and return args which we
1167  /// also do not support; iter_args and while return values are placed in
1168  /// the same registers.
1169  for (auto barg :
1170  enumerate(scfWhileOp.getBefore().front().getArguments())) {
1171  auto condOp = scfWhileOp.getConditionOp().getArgs()[barg.index()];
1172  if (barg.value() != condOp) {
1173  res = whileOp.getOperation()->emitError()
1174  << loweringState().irName(barg.value())
1175  << " != " << loweringState().irName(condOp)
1176  << "do-while loops not supported; expected iter-args to "
1177  "remain untransformed in the 'before' region of the "
1178  "scf.while op.";
1179  return WalkResult::interrupt();
1180  }
1181  }
1182 
1183  /// Create iteration argument registers.
1184  /// The iteration argument registers will be referenced:
1185  /// - In the "before" part of the while loop, calculating the conditional,
1186  /// - In the "after" part of the while loop,
1187  /// - Outside the while loop, rewriting the while loop return values.
1188  for (auto arg : enumerate(whileOp.getBodyArgs())) {
1189  std::string name = getState<ComponentLoweringState>()
1190  .getUniqueName(whileOp.getOperation())
1191  .str() +
1192  "_arg" + std::to_string(arg.index());
1193  auto reg =
1194  createRegister(arg.value().getLoc(), rewriter, getComponent(),
1195  arg.value().getType().getIntOrFloatBitWidth(), name);
1196  getState<ComponentLoweringState>().addWhileLoopIterReg(whileOp, reg,
1197  arg.index());
1198  arg.value().replaceAllUsesWith(reg.getOut());
1199 
1200  /// Also replace uses in the "before" region of the while loop
1201  whileOp.getConditionBlock()
1202  ->getArgument(arg.index())
1203  .replaceAllUsesWith(reg.getOut());
1204  }
1205 
1206  /// Create iter args initial value assignment group(s), one per register.
1207  SmallVector<calyx::GroupOp> initGroups;
1208  auto numOperands = whileOp.getOperation()->getNumOperands();
1209  for (size_t i = 0; i < numOperands; ++i) {
1210  auto initGroupOp =
1211  getState<ComponentLoweringState>().buildWhileLoopIterArgAssignments(
1212  rewriter, whileOp,
1213  getState<ComponentLoweringState>().getComponentOp(),
1214  getState<ComponentLoweringState>().getUniqueName(
1215  whileOp.getOperation()) +
1216  "_init_" + std::to_string(i),
1217  whileOp.getOperation()->getOpOperand(i));
1218  initGroups.push_back(initGroupOp);
1219  }
1220 
1221  getState<ComponentLoweringState>().setWhileLoopInitGroups(whileOp,
1222  initGroups);
1223 
1224  return WalkResult::advance();
1225  });
1226  return res;
1227  }
1228 };
1229 
1230 /// In BuildForGroups, a register is created for the iteration argument of
1231 /// the for op. This register is then initialized to the lowerBound of the for
1232 /// loop in a group that executes the for loop.
1234  using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
1235 
1236  LogicalResult
1238  PatternRewriter &rewriter) const override {
1239  LogicalResult res = success();
1240  funcOp.walk([&](Operation *op) {
1241  // Only work on ops that support the ScfForOp.
1242  if (!isa<scf::ForOp>(op))
1243  return WalkResult::advance();
1244 
1245  auto scfForOp = cast<scf::ForOp>(op);
1246  ScfForOp forOp(scfForOp);
1247 
1248  getState<ComponentLoweringState>().setUniqueName(forOp.getOperation(),
1249  "for");
1250 
1251  // Create a register for the InductionVar, and set that Register as the
1252  // only IterReg for the For Loop
1253  auto inductionVar = forOp.getOperation().getInductionVar();
1254  SmallVector<std::string, 3> inductionVarIdentifiers = {
1255  getState<ComponentLoweringState>()
1256  .getUniqueName(forOp.getOperation())
1257  .str(),
1258  "induction", "var"};
1259  std::string name = llvm::join(inductionVarIdentifiers, "_");
1260  auto reg =
1261  createRegister(inductionVar.getLoc(), rewriter, getComponent(),
1262  inductionVar.getType().getIntOrFloatBitWidth(), name);
1263  getState<ComponentLoweringState>().addForLoopIterReg(forOp, reg, 0);
1264  inductionVar.replaceAllUsesWith(reg.getOut());
1265 
1266  // Create InitGroup that sets the InductionVar to LowerBound
1267  calyx::ComponentOp componentOp =
1268  getState<ComponentLoweringState>().getComponentOp();
1269  SmallVector<calyx::GroupOp> initGroups;
1270  SmallVector<std::string, 4> groupIdentifiers = {
1271  "init",
1272  getState<ComponentLoweringState>()
1273  .getUniqueName(forOp.getOperation())
1274  .str(),
1275  "induction", "var"};
1276  std::string groupName = llvm::join(groupIdentifiers, "_");
1277  auto groupOp = calyx::createGroup<calyx::GroupOp>(
1278  rewriter, componentOp, forOp.getLoc(), groupName);
1279  buildAssignmentsForRegisterWrite(rewriter, groupOp, componentOp, reg,
1280  forOp.getOperation().getLowerBound());
1281  initGroups.push_back(groupOp);
1282  getState<ComponentLoweringState>().setForLoopInitGroups(forOp,
1283  initGroups);
1284 
1285  return WalkResult::advance();
1286  });
1287  return res;
1288  }
1289 };
1290 
1291 /// Builds a control schedule by traversing the CFG of the function and
1292 /// associating this with the previously created groups.
1293 /// For simplicity, the generated control flow is expanded for all possible
1294 /// paths in the input DAG. This elaborated control flow is later reduced in
1295 /// the runControlFlowSimplification passes.
1297  using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
1298 
1299  LogicalResult
1301  PatternRewriter &rewriter) const override {
1302  auto *entryBlock = &funcOp.getBlocks().front();
1303  rewriter.setInsertionPointToStart(
1304  getComponent().getControlOp().getBodyBlock());
1305  auto topLevelSeqOp = rewriter.create<calyx::SeqOp>(funcOp.getLoc());
1306  DenseSet<Block *> path;
1307  return buildCFGControl(path, rewriter, topLevelSeqOp.getBodyBlock(),
1308  nullptr, entryBlock);
1309  }
1310 
1311 private:
1312  /// Sequentially schedules the groups that registered themselves with
1313  /// 'block'.
1314  LogicalResult scheduleBasicBlock(PatternRewriter &rewriter,
1315  const DenseSet<Block *> &path,
1316  mlir::Block *parentCtrlBlock,
1317  mlir::Block *block) const {
1318  auto compBlockScheduleables =
1319  getState<ComponentLoweringState>().getBlockScheduleables(block);
1320  auto loc = block->front().getLoc();
1321 
1322  if (compBlockScheduleables.size() > 1) {
1323  auto seqOp = rewriter.create<calyx::SeqOp>(loc);
1324  parentCtrlBlock = seqOp.getBodyBlock();
1325  }
1326 
1327  for (auto &group : compBlockScheduleables) {
1328  rewriter.setInsertionPointToEnd(parentCtrlBlock);
1329  if (auto groupPtr = std::get_if<calyx::GroupOp>(&group); groupPtr) {
1330  rewriter.create<calyx::EnableOp>(groupPtr->getLoc(),
1331  groupPtr->getSymName());
1332  } else if (auto whileSchedPtr = std::get_if<WhileScheduleable>(&group);
1333  whileSchedPtr) {
1334  auto &whileOp = whileSchedPtr->whileOp;
1335 
1336  auto whileCtrlOp = buildWhileCtrlOp(
1337  whileOp,
1338  getState<ComponentLoweringState>().getWhileLoopInitGroups(whileOp),
1339  rewriter);
1340  rewriter.setInsertionPointToEnd(whileCtrlOp.getBodyBlock());
1341  auto whileBodyOp =
1342  rewriter.create<calyx::SeqOp>(whileOp.getOperation()->getLoc());
1343  auto *whileBodyOpBlock = whileBodyOp.getBodyBlock();
1344 
1345  /// Only schedule the 'after' block. The 'before' block is
1346  /// implicitly scheduled when evaluating the while condition.
1347  LogicalResult res = buildCFGControl(path, rewriter, whileBodyOpBlock,
1348  block, whileOp.getBodyBlock());
1349 
1350  // Insert loop-latch at the end of the while group
1351  rewriter.setInsertionPointToEnd(whileBodyOpBlock);
1352  calyx::GroupOp whileLatchGroup =
1353  getState<ComponentLoweringState>().getWhileLoopLatchGroup(whileOp);
1354  rewriter.create<calyx::EnableOp>(whileLatchGroup.getLoc(),
1355  whileLatchGroup.getName());
1356 
1357  if (res.failed())
1358  return res;
1359  } else if (auto *forSchedPtr = std::get_if<ForScheduleable>(&group);
1360  forSchedPtr) {
1361  auto forOp = forSchedPtr->forOp;
1362 
1363  auto forCtrlOp = buildForCtrlOp(
1364  forOp,
1365  getState<ComponentLoweringState>().getForLoopInitGroups(forOp),
1366  forSchedPtr->bound, rewriter);
1367  rewriter.setInsertionPointToEnd(forCtrlOp.getBodyBlock());
1368  auto forBodyOp =
1369  rewriter.create<calyx::SeqOp>(forOp.getOperation()->getLoc());
1370  auto *forBodyOpBlock = forBodyOp.getBodyBlock();
1371 
1372  // Schedule the body of the for loop.
1373  LogicalResult res = buildCFGControl(path, rewriter, forBodyOpBlock,
1374  block, forOp.getBodyBlock());
1375 
1376  // Insert loop-latch at the end of the while group.
1377  rewriter.setInsertionPointToEnd(forBodyOpBlock);
1378  calyx::GroupOp forLatchGroup =
1379  getState<ComponentLoweringState>().getForLoopLatchGroup(forOp);
1380  rewriter.create<calyx::EnableOp>(forLatchGroup.getLoc(),
1381  forLatchGroup.getName());
1382  if (res.failed())
1383  return res;
1384  } else if (auto *callSchedPtr = std::get_if<CallScheduleable>(&group)) {
1385  auto instanceOp = callSchedPtr->instanceOp;
1386  OpBuilder::InsertionGuard g(rewriter);
1387  auto callBody = rewriter.create<calyx::SeqOp>(instanceOp.getLoc());
1388  rewriter.setInsertionPointToStart(callBody.getBodyBlock());
1389  std::string initGroupName = "init_" + instanceOp.getSymName().str();
1390  rewriter.create<calyx::EnableOp>(instanceOp.getLoc(), initGroupName);
1391  SmallVector<Value, 4> instancePorts;
1392  auto inputPorts = callSchedPtr->callOp.getOperands();
1393  llvm::copy(instanceOp.getResults().take_front(inputPorts.size()),
1394  std::back_inserter(instancePorts));
1395  rewriter.create<calyx::InvokeOp>(
1396  instanceOp.getLoc(), instanceOp.getSymName(), instancePorts,
1397  inputPorts, ArrayAttr::get(rewriter.getContext(), {}),
1398  ArrayAttr::get(rewriter.getContext(), {}));
1399  } else
1400  llvm_unreachable("Unknown scheduleable");
1401  }
1402  return success();
1403  }
1404 
1405  /// Schedules a block by inserting a branch argument assignment block (if any)
1406  /// before recursing into the scheduling of the block innards.
1407  /// Blocks 'from' and 'to' refer to blocks in the source program.
1408  /// parentCtrlBlock refers to the control block wherein control operations are
1409  /// to be inserted.
1410  LogicalResult schedulePath(PatternRewriter &rewriter,
1411  const DenseSet<Block *> &path, Location loc,
1412  Block *from, Block *to,
1413  Block *parentCtrlBlock) const {
1414  /// Schedule any registered block arguments to be executed before the body
1415  /// of the branch.
1416  rewriter.setInsertionPointToEnd(parentCtrlBlock);
1417  auto preSeqOp = rewriter.create<calyx::SeqOp>(loc);
1418  rewriter.setInsertionPointToEnd(preSeqOp.getBodyBlock());
1419  for (auto barg :
1420  getState<ComponentLoweringState>().getBlockArgGroups(from, to))
1421  rewriter.create<calyx::EnableOp>(barg.getLoc(), barg.getSymName());
1422 
1423  return buildCFGControl(path, rewriter, parentCtrlBlock, from, to);
1424  }
1425 
1426  LogicalResult buildCFGControl(DenseSet<Block *> path,
1427  PatternRewriter &rewriter,
1428  mlir::Block *parentCtrlBlock,
1429  mlir::Block *preBlock,
1430  mlir::Block *block) const {
1431  if (path.count(block) != 0)
1432  return preBlock->getTerminator()->emitError()
1433  << "CFG backedge detected. Loops must be raised to 'scf.while' or "
1434  "'scf.for' operations.";
1435 
1436  rewriter.setInsertionPointToEnd(parentCtrlBlock);
1437  LogicalResult bbSchedResult =
1438  scheduleBasicBlock(rewriter, path, parentCtrlBlock, block);
1439  if (bbSchedResult.failed())
1440  return bbSchedResult;
1441 
1442  path.insert(block);
1443  auto successors = block->getSuccessors();
1444  auto nSuccessors = successors.size();
1445  if (nSuccessors > 0) {
1446  auto brOp = dyn_cast<BranchOpInterface>(block->getTerminator());
1447  assert(brOp);
1448  if (nSuccessors > 1) {
1449  /// TODO(mortbopet): we could choose to support ie. std.switch, but it
1450  /// would probably be easier to just require it to be lowered
1451  /// beforehand.
1452  assert(nSuccessors == 2 &&
1453  "only conditional branches supported for now...");
1454  /// Wrap each branch inside an if/else.
1455  auto cond = brOp->getOperand(0);
1456  auto condGroup = getState<ComponentLoweringState>()
1457  .getEvaluatingGroup<calyx::CombGroupOp>(cond);
1458  auto symbolAttr = FlatSymbolRefAttr::get(
1459  StringAttr::get(getContext(), condGroup.getSymName()));
1460 
1461  auto ifOp = rewriter.create<calyx::IfOp>(
1462  brOp->getLoc(), cond, symbolAttr, /*initializeElseBody=*/true);
1463  rewriter.setInsertionPointToStart(ifOp.getThenBody());
1464  auto thenSeqOp = rewriter.create<calyx::SeqOp>(brOp.getLoc());
1465  rewriter.setInsertionPointToStart(ifOp.getElseBody());
1466  auto elseSeqOp = rewriter.create<calyx::SeqOp>(brOp.getLoc());
1467 
1468  bool trueBrSchedSuccess =
1469  schedulePath(rewriter, path, brOp.getLoc(), block, successors[0],
1470  thenSeqOp.getBodyBlock())
1471  .succeeded();
1472  bool falseBrSchedSuccess = true;
1473  if (trueBrSchedSuccess) {
1474  falseBrSchedSuccess =
1475  schedulePath(rewriter, path, brOp.getLoc(), block, successors[1],
1476  elseSeqOp.getBodyBlock())
1477  .succeeded();
1478  }
1479 
1480  return success(trueBrSchedSuccess && falseBrSchedSuccess);
1481  } else {
1482  /// Schedule sequentially within the current parent control block.
1483  return schedulePath(rewriter, path, brOp.getLoc(), block,
1484  successors.front(), parentCtrlBlock);
1485  }
1486  }
1487  return success();
1488  }
1489 
1490  // Insert a Par of initGroups at Location loc. Used as helper for
1491  // `buildWhileCtrlOp` and `buildForCtrlOp`.
1492  void
1493  insertParInitGroups(PatternRewriter &rewriter, Location loc,
1494  const SmallVector<calyx::GroupOp> &initGroups) const {
1495  PatternRewriter::InsertionGuard g(rewriter);
1496  auto parOp = rewriter.create<calyx::ParOp>(loc);
1497  rewriter.setInsertionPointToStart(parOp.getBodyBlock());
1498  for (calyx::GroupOp group : initGroups)
1499  rewriter.create<calyx::EnableOp>(group.getLoc(), group.getName());
1500  }
1501 
1502  calyx::WhileOp buildWhileCtrlOp(ScfWhileOp whileOp,
1503  SmallVector<calyx::GroupOp> initGroups,
1504  PatternRewriter &rewriter) const {
1505  Location loc = whileOp.getLoc();
1506  /// Insert while iter arg initialization group(s). Emit a
1507  /// parallel group to assign one or more registers all at once.
1508  insertParInitGroups(rewriter, loc, initGroups);
1509 
1510  /// Insert the while op itself.
1511  auto cond = whileOp.getConditionValue();
1512  auto condGroup = getState<ComponentLoweringState>()
1513  .getEvaluatingGroup<calyx::CombGroupOp>(cond);
1514  auto symbolAttr = FlatSymbolRefAttr::get(
1515  StringAttr::get(getContext(), condGroup.getSymName()));
1516  return rewriter.create<calyx::WhileOp>(loc, cond, symbolAttr);
1517  }
1518 
1519  calyx::RepeatOp buildForCtrlOp(ScfForOp forOp,
1520  SmallVector<calyx::GroupOp> const &initGroups,
1521  uint64_t bound,
1522  PatternRewriter &rewriter) const {
1523  Location loc = forOp.getLoc();
1524  // Insert for iter arg initialization group(s). Emit a
1525  // parallel group to assign one or more registers all at once.
1526  insertParInitGroups(rewriter, loc, initGroups);
1527 
1528  // Insert the repeatOp that corresponds to the For loop.
1529  return rewriter.create<calyx::RepeatOp>(loc, bound);
1530  }
1531 };
1532 
1533 /// LateSSAReplacement contains various functions for replacing SSA values that
1534 /// were not replaced during op construction.
1536  using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
1537 
1538  LogicalResult partiallyLowerFuncToComp(FuncOp funcOp,
1539  PatternRewriter &) const override {
1540  funcOp.walk([&](scf::WhileOp op) {
1541  /// The yielded values returned from the while op will be present in the
1542  /// iterargs registers post execution of the loop.
1543  /// This is done now, as opposed to during BuildWhileGroups since if the
1544  /// results of the whileOp were replaced before
1545  /// BuildOpGroups/BuildControl, the whileOp would get dead-code
1546  /// eliminated.
1547  ScfWhileOp whileOp(op);
1548  for (auto res :
1549  getState<ComponentLoweringState>().getWhileLoopIterRegs(whileOp))
1550  whileOp.getOperation()->getResults()[res.first].replaceAllUsesWith(
1551  res.second.getOut());
1552  });
1553 
1554  funcOp.walk([&](memref::LoadOp loadOp) {
1555  if (calyx::singleLoadFromMemory(loadOp)) {
1556  /// In buildOpGroups we did not replace loadOp's results, to ensure a
1557  /// link between evaluating groups (which fix the input addresses of a
1558  /// memory op) and a readData result. Now, we may replace these SSA
1559  /// values with their memoryOp readData output.
1560  loadOp.getResult().replaceAllUsesWith(
1561  getState<ComponentLoweringState>()
1562  .getMemoryInterface(loadOp.getMemref())
1563  .readData());
1564  }
1565  });
1566 
1567  return success();
1568  }
1569 };
1570 
1571 /// Erases FuncOp operations.
1573  using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
1574 
1575  LogicalResult matchAndRewrite(FuncOp funcOp,
1576  PatternRewriter &rewriter) const override {
1577  rewriter.eraseOp(funcOp);
1578  return success();
1579  }
1580 
1581  LogicalResult
1583  PatternRewriter &rewriter) const override {
1584  return success();
1585  }
1586 };
1587 
1588 //===----------------------------------------------------------------------===//
1589 // Pass driver
1590 //===----------------------------------------------------------------------===//
1591 class SCFToCalyxPass : public SCFToCalyxBase<SCFToCalyxPass> {
1592 public:
1594  : SCFToCalyxBase<SCFToCalyxPass>(), partialPatternRes(success()) {}
1595  void runOnOperation() override;
1596 
1597  LogicalResult setTopLevelFunction(mlir::ModuleOp moduleOp,
1598  std::string &topLevelFunction) {
1599  if (!topLevelFunctionOpt.empty()) {
1600  if (SymbolTable::lookupSymbolIn(moduleOp, topLevelFunctionOpt) ==
1601  nullptr) {
1602  moduleOp.emitError() << "Top level function '" << topLevelFunctionOpt
1603  << "' not found in module.";
1604  return failure();
1605  }
1606  topLevelFunction = topLevelFunctionOpt;
1607  } else {
1608  /// No top level function set; infer top level if the module only contains
1609  /// a single function, else, throw error.
1610  auto funcOps = moduleOp.getOps<FuncOp>();
1611  if (std::distance(funcOps.begin(), funcOps.end()) == 1)
1612  topLevelFunction = (*funcOps.begin()).getSymName().str();
1613  else {
1614  moduleOp.emitError()
1615  << "Module contains multiple functions, but no top level "
1616  "function was set. Please see --top-level-function";
1617  return failure();
1618  }
1619  }
1620  return success();
1621  }
1622 
1624  enum class Strategy { Once, Greedy };
1625  RewritePatternSet pattern;
1627  };
1628 
1629  //// Labels the entry point of a Calyx program.
1630  /// Furthermore, this function performs validation on the input function,
1631  /// to ensure that we've implemented the capabilities necessary to convert
1632  /// it.
1633  LogicalResult labelEntryPoint(StringRef topLevelFunction) {
1634  // Program legalization - the partial conversion driver will not run
1635  // unless some pattern is provided - provide a dummy pattern.
1636  struct DummyPattern : public OpRewritePattern<mlir::ModuleOp> {
1637  using OpRewritePattern::OpRewritePattern;
1638  LogicalResult matchAndRewrite(mlir::ModuleOp,
1639  PatternRewriter &) const override {
1640  return failure();
1641  }
1642  };
1643 
1644  ConversionTarget target(getContext());
1645  target.addLegalDialect<calyx::CalyxDialect>();
1646  target.addLegalDialect<scf::SCFDialect>();
1647  target.addIllegalDialect<hw::HWDialect>();
1648  target.addIllegalDialect<comb::CombDialect>();
1649 
1650  // Only accept std operations which we've added lowerings for
1651  target.addIllegalDialect<FuncDialect>();
1652  target.addIllegalDialect<ArithDialect>();
1653  target.addLegalOp<AddIOp, SelectOp, SubIOp, CmpIOp, ShLIOp, ShRUIOp,
1654  ShRSIOp, AndIOp, XOrIOp, OrIOp, ExtUIOp, TruncIOp,
1655  CondBranchOp, BranchOp, MulIOp, DivUIOp, DivSIOp, RemUIOp,
1656  RemSIOp, ReturnOp, arith::ConstantOp, IndexCastOp, FuncOp,
1657  ExtSIOp, CallOp>();
1658 
1659  RewritePatternSet legalizePatterns(&getContext());
1660  legalizePatterns.add<DummyPattern>(&getContext());
1661  DenseSet<Operation *> legalizedOps;
1662  if (applyPartialConversion(getOperation(), target,
1663  std::move(legalizePatterns))
1664  .failed())
1665  return failure();
1666 
1667  // Program conversion
1668  return calyx::applyModuleOpConversion(getOperation(), topLevelFunction);
1669  }
1670 
1671  /// 'Once' patterns are expected to take an additional LogicalResult&
1672  /// argument, to forward their result state (greedyPatternRewriteDriver
1673  /// results are skipped for Once patterns).
1674  template <typename TPattern, typename... PatternArgs>
1675  void addOncePattern(SmallVectorImpl<LoweringPattern> &patterns,
1676  PatternArgs &&...args) {
1677  RewritePatternSet ps(&getContext());
1678  ps.add<TPattern>(&getContext(), partialPatternRes, args...);
1679  patterns.push_back(
1680  LoweringPattern{std::move(ps), LoweringPattern::Strategy::Once});
1681  }
1682 
1683  template <typename TPattern, typename... PatternArgs>
1684  void addGreedyPattern(SmallVectorImpl<LoweringPattern> &patterns,
1685  PatternArgs &&...args) {
1686  RewritePatternSet ps(&getContext());
1687  ps.add<TPattern>(&getContext(), args...);
1688  patterns.push_back(
1689  LoweringPattern{std::move(ps), LoweringPattern::Strategy::Greedy});
1690  }
1691 
1692  LogicalResult runPartialPattern(RewritePatternSet &pattern, bool runOnce) {
1693  assert(pattern.getNativePatterns().size() == 1 &&
1694  "Should only apply 1 partial lowering pattern at once");
1695 
1696  // During component creation, the function body is inlined into the
1697  // component body for further processing. However, proper control flow
1698  // will only be established later in the conversion process, so ensure
1699  // that rewriter optimizations (especially DCE) are disabled.
1700  GreedyRewriteConfig config;
1701  config.enableRegionSimplification = false;
1702  if (runOnce)
1703  config.maxIterations = 1;
1704 
1705  /// Can't return applyPatternsAndFoldGreedily. Root isn't
1706  /// necessarily erased so it will always return failed(). Instead,
1707  /// forward the 'succeeded' value from PartialLoweringPatternBase.
1708  (void)applyPatternsAndFoldGreedily(getOperation(), std::move(pattern),
1709  config);
1710  return partialPatternRes;
1711  }
1712 
1713 private:
1714  LogicalResult partialPatternRes;
1715  std::shared_ptr<calyx::CalyxLoweringState> loweringState = nullptr;
1716 };
1717 
1718 void SCFToCalyxPass::runOnOperation() {
1719  // Clear internal state. See https://github.com/llvm/circt/issues/3235
1720  loweringState.reset();
1721  partialPatternRes = LogicalResult::failure();
1722 
1723  std::string topLevelFunction;
1724  if (failed(setTopLevelFunction(getOperation(), topLevelFunction))) {
1725  signalPassFailure();
1726  return;
1727  }
1728 
1729  /// Start conversion
1730  if (failed(labelEntryPoint(topLevelFunction))) {
1731  signalPassFailure();
1732  return;
1733  }
1734  loweringState = std::make_shared<calyx::CalyxLoweringState>(getOperation(),
1735  topLevelFunction);
1736 
1737  /// --------------------------------------------------------------------------
1738  /// If you are a developer, it may be helpful to add a
1739  /// 'getOperation()->dump()' call after the execution of each stage to
1740  /// view the transformations that's going on.
1741  /// --------------------------------------------------------------------------
1742 
1743  /// A mapping is maintained between a function operation and its corresponding
1744  /// Calyx component.
1745  DenseMap<FuncOp, calyx::ComponentOp> funcMap;
1746  SmallVector<LoweringPattern, 8> loweringPatterns;
1747  calyx::PatternApplicationState patternState;
1748 
1749  /// Creates a new Calyx component for each FuncOp in the inpurt module.
1750  addOncePattern<FuncOpConversion>(loweringPatterns, patternState, funcMap,
1751  *loweringState);
1752 
1753  /// This pass inlines scf.ExecuteRegionOp's by adding control-flow.
1754  addGreedyPattern<InlineExecuteRegionOpPattern>(loweringPatterns);
1755 
1756  /// This pattern converts all index typed values to an i32 integer.
1757  addOncePattern<calyx::ConvertIndexTypes>(loweringPatterns, patternState,
1758  funcMap, *loweringState);
1759 
1760  /// This pattern creates registers for all basic-block arguments.
1761  addOncePattern<calyx::BuildBasicBlockRegs>(loweringPatterns, patternState,
1762  funcMap, *loweringState);
1763 
1764  addOncePattern<calyx::BuildCallInstance>(loweringPatterns, patternState,
1765  funcMap, *loweringState);
1766 
1767  /// This pattern creates registers for the function return values.
1768  addOncePattern<calyx::BuildReturnRegs>(loweringPatterns, patternState,
1769  funcMap, *loweringState);
1770 
1771  /// This pattern creates registers for iteration arguments of scf.while
1772  /// operations. Additionally, creates a group for assigning the initial
1773  /// value of the iteration argument registers.
1774  addOncePattern<BuildWhileGroups>(loweringPatterns, patternState, funcMap,
1775  *loweringState);
1776 
1777  /// This pattern creates registers for iteration arguments of scf.for
1778  /// operations. Additionally, creates a group for assigning the initial
1779  /// value of the iteration argument registers.
1780  addOncePattern<BuildForGroups>(loweringPatterns, patternState, funcMap,
1781  *loweringState);
1782 
1783  /// This pattern converts operations within basic blocks to Calyx library
1784  /// operators. Combinational operations are assigned inside a
1785  /// calyx::CombGroupOp, and sequential inside calyx::GroupOps.
1786  /// Sequential groups are registered with the Block* of which the operation
1787  /// originated from. This is used during control schedule generation. By
1788  /// having a distinct group for each operation, groups are analogous to SSA
1789  /// values in the source program.
1790  addOncePattern<BuildOpGroups>(loweringPatterns, patternState, funcMap,
1791  *loweringState);
1792 
1793  /// This pattern traverses the CFG of the program and generates a control
1794  /// schedule based on the calyx::GroupOp's which were registered for each
1795  /// basic block in the source function.
1796  addOncePattern<BuildControl>(loweringPatterns, patternState, funcMap,
1797  *loweringState);
1798 
1799  /// This pass recursively inlines use-def chains of combinational logic (from
1800  /// non-stateful groups) into groups referenced in the control schedule.
1801  addOncePattern<calyx::InlineCombGroups>(loweringPatterns, patternState,
1802  *loweringState);
1803 
1804  /// This pattern performs various SSA replacements that must be done
1805  /// after control generation.
1806  addOncePattern<LateSSAReplacement>(loweringPatterns, patternState, funcMap,
1807  *loweringState);
1808 
1809  /// Eliminate any unused combinational groups. This is done before
1810  /// calyx::RewriteMemoryAccesses to avoid inferring slice components for
1811  /// groups that will be removed.
1812  addGreedyPattern<calyx::EliminateUnusedCombGroups>(loweringPatterns);
1813 
1814  /// This pattern rewrites accesses to memories which are too wide due to
1815  /// index types being converted to a fixed-width integer type.
1816  addOncePattern<calyx::RewriteMemoryAccesses>(loweringPatterns, patternState,
1817  *loweringState);
1818 
1819  /// This pattern removes the source FuncOp which has now been converted into
1820  /// a Calyx component.
1821  addOncePattern<CleanupFuncOps>(loweringPatterns, patternState, funcMap,
1822  *loweringState);
1823 
1824  /// Sequentially apply each lowering pattern.
1825  for (auto &pat : loweringPatterns) {
1826  LogicalResult partialPatternRes = runPartialPattern(
1827  pat.pattern,
1828  /*runOnce=*/pat.strategy == LoweringPattern::Strategy::Once);
1829  if (succeeded(partialPatternRes))
1830  continue;
1831  signalPassFailure();
1832  return;
1833  }
1834 
1835  //===----------------------------------------------------------------------===//
1836  // Cleanup patterns
1837  //===----------------------------------------------------------------------===//
1838  RewritePatternSet cleanupPatterns(&getContext());
1839  cleanupPatterns.add<calyx::MultipleGroupDonePattern,
1840  calyx::NonTerminatingGroupDonePattern>(&getContext());
1841  if (failed(applyPatternsAndFoldGreedily(getOperation(),
1842  std::move(cleanupPatterns)))) {
1843  signalPassFailure();
1844  return;
1845  }
1846 
1847  if (ciderSourceLocationMetadata) {
1848  // Debugging information for the Cider debugger.
1849  // Reference: https://docs.calyxir.org/debug/cider.html
1850  SmallVector<Attribute, 16> sourceLocations;
1851  getOperation()->walk([&](calyx::ComponentOp component) {
1852  return getCiderSourceLocationMetadata(component, sourceLocations);
1853  });
1854 
1855  MLIRContext *context = getOperation()->getContext();
1856  getOperation()->setAttr("calyx.metadata",
1857  ArrayAttr::get(context, sourceLocations));
1858  }
1859 }
1860 
1861 } // namespace scftocalyx
1862 
1863 //===----------------------------------------------------------------------===//
1864 // Pass initialization
1865 //===----------------------------------------------------------------------===//
1866 
1867 std::unique_ptr<OperationPass<ModuleOp>> createSCFToCalyxPass() {
1868  return std::make_unique<scftocalyx::SCFToCalyxPass>();
1869 }
1870 
1871 } // namespace circt
assert(baseType &&"element must be base type")
int32_t width
Definition: FIRRTL.cpp:36
@ Input
Definition: HW.h:35
@ Output
Definition: HW.h:35
Builder builder
void setFuncOpResultMapping(const DenseMap< unsigned, unsigned > &mapping)
Assign a mapping between the source funcOp result indices and the corresponding output port indices o...
std::string getUniqueName(StringRef prefix)
Returns a unique name within compOp with the provided prefix.
void registerMemoryInterface(Value memref, const calyx::MemoryInterface &memoryInterface)
Registers a memory interface as being associated with a memory identified by 'memref'.
calyx::ComponentOp getComponentOp()
Returns the calyx::ComponentOp associated with this lowering state.
FuncOpPartialLoweringPatterns are patterns which intend to match on FuncOps and then perform their ow...
Holds common utilities used for scheduling when lowering to Calyx.
Builds a control schedule by traversing the CFG of the function and associating this with the previou...
calyx::RepeatOp buildForCtrlOp(ScfForOp forOp, SmallVector< calyx::GroupOp > const &initGroups, uint64_t bound, PatternRewriter &rewriter) const
LogicalResult partiallyLowerFuncToComp(FuncOp funcOp, PatternRewriter &rewriter) const override
LogicalResult schedulePath(PatternRewriter &rewriter, const DenseSet< Block * > &path, Location loc, Block *from, Block *to, Block *parentCtrlBlock) const
Schedules a block by inserting a branch argument assignment block (if any) before recursing into the ...
calyx::WhileOp buildWhileCtrlOp(ScfWhileOp whileOp, SmallVector< calyx::GroupOp > initGroups, PatternRewriter &rewriter) const
LogicalResult scheduleBasicBlock(PatternRewriter &rewriter, const DenseSet< Block * > &path, mlir::Block *parentCtrlBlock, mlir::Block *block) const
Sequentially schedules the groups that registered themselves with 'block'.
LogicalResult buildCFGControl(DenseSet< Block * > path, PatternRewriter &rewriter, mlir::Block *parentCtrlBlock, mlir::Block *preBlock, mlir::Block *block) const
void insertParInitGroups(PatternRewriter &rewriter, Location loc, const SmallVector< calyx::GroupOp > &initGroups) const
In BuildForGroups, a register is created for the iteration argument of the for op.
LogicalResult partiallyLowerFuncToComp(FuncOp funcOp, PatternRewriter &rewriter) const override
Iterate through the operations of a source function and instantiate components or primitives based on...
Definition: SCFToCalyx.cpp:198
TGroupOp createGroupForOp(PatternRewriter &rewriter, Operation *op) const
Creates a group named by the basic block which the input op resides in.
Definition: SCFToCalyx.cpp:327
LogicalResult buildLibraryOp(PatternRewriter &rewriter, TSrcOp op) const
buildLibraryOp which provides in- and output types based on the operands and results of the op argume...
Definition: SCFToCalyx.cpp:320
void assignAddressPorts(PatternRewriter &rewriter, Location loc, calyx::GroupInterface group, calyx::MemoryInterface memoryInterface, Operation::operand_range addressValues) const
Creates assignments within the provided group to the address ports of the memoryOp based on the provi...
Definition: SCFToCalyx.cpp:384
LogicalResult buildLibraryOp(PatternRewriter &rewriter, TSrcOp op, TypeRange srcTypes, TypeRange dstTypes) const
buildLibraryOp will build a TCalyxLibOp inside a TGroupOp based on the source operation TSrcOp.
Definition: SCFToCalyx.cpp:276
LogicalResult partiallyLowerFuncToComp(FuncOp funcOp, PatternRewriter &rewriter) const override
Definition: SCFToCalyx.cpp:202
LogicalResult buildLibraryBinaryPipeOp(PatternRewriter &rewriter, TSrcOp op, TOpType opPipe, Value out) const
buildLibraryBinaryPipeOp will build a TCalyxLibBinaryPipeOp, to deal with MulIOp, DivUIOp and RemUIOp...
Definition: SCFToCalyx.cpp:339
In BuildWhileGroups, a register is created for each iteration argumenet of the while op.
LogicalResult partiallyLowerFuncToComp(FuncOp funcOp, PatternRewriter &rewriter) const override
Erases FuncOp operations.
LogicalResult matchAndRewrite(FuncOp funcOp, PatternRewriter &rewriter) const override
LogicalResult partiallyLowerFuncToComp(FuncOp funcOp, PatternRewriter &rewriter) const override
Handles the current state of lowering of a Calyx component.
Definition: SCFToCalyx.cpp:186
ComponentLoweringState(calyx::ComponentOp component)
Definition: SCFToCalyx.cpp:188
void setForLoopInitGroups(ScfForOp op, SmallVector< calyx::GroupOp > groups)
Definition: SCFToCalyx.cpp:175
calyx::GroupOp buildForLoopIterArgAssignments(OpBuilder &builder, ScfForOp op, calyx::ComponentOp componentOp, Twine uniqueSuffix, MutableArrayRef< OpOperand > ops)
Definition: SCFToCalyx.cpp:154
void setForLoopLatchGroup(ScfForOp op, calyx::GroupOp group)
Definition: SCFToCalyx.cpp:169
SmallVector< calyx::GroupOp > getForLoopInitGroups(ScfForOp op)
Definition: SCFToCalyx.cpp:151
void addForLoopIterReg(ScfForOp op, calyx::RegisterOp reg, unsigned idx)
Definition: SCFToCalyx.cpp:160
calyx::GroupOp getForLoopLatchGroup(ScfForOp op)
Definition: SCFToCalyx.cpp:172
calyx::RegisterOp getForLoopIterReg(ScfForOp op, unsigned idx)
Definition: SCFToCalyx.cpp:166
const DenseMap< unsigned, calyx::RegisterOp > & getForLoopIterRegs(ScfForOp op)
Definition: SCFToCalyx.cpp:163
Inlines Calyx ExecuteRegionOp operations within their parent blocks.
Definition: SCFToCalyx.cpp:975
LogicalResult matchAndRewrite(scf::ExecuteRegionOp execOp, PatternRewriter &rewriter) const override
Definition: SCFToCalyx.cpp:978
LateSSAReplacement contains various functions for replacing SSA values that were not replaced during ...
LogicalResult partiallyLowerFuncToComp(FuncOp funcOp, PatternRewriter &) const override
LogicalResult labelEntryPoint(StringRef topLevelFunction)
Labels the entry point of a Calyx program.
LogicalResult runPartialPattern(RewritePatternSet &pattern, bool runOnce)
LogicalResult setTopLevelFunction(mlir::ModuleOp moduleOp, std::string &topLevelFunction)
void addGreedyPattern(SmallVectorImpl< LoweringPattern > &patterns, PatternArgs &&...args)
void addOncePattern(SmallVectorImpl< LoweringPattern > &patterns, PatternArgs &&...args)
'Once' patterns are expected to take an additional LogicalResult& argument, to forward their result s...
std::optional< int64_t > getBound() override
Definition: SCFToCalyx.cpp:83
Block * getBodyBlock() override
Definition: SCFToCalyx.cpp:79
Block::BlockArgListType getBodyArgs() override
Definition: SCFToCalyx.cpp:75
ScfWhileOp(scf::WhileOp op)
Definition: SCFToCalyx.cpp:51
Block::BlockArgListType getBodyArgs() override
Definition: SCFToCalyx.cpp:54
Block * getConditionBlock() override
Definition: SCFToCalyx.cpp:60
std::optional< int64_t > getBound() override
Definition: SCFToCalyx.cpp:68
Block * getBodyBlock() override
Definition: SCFToCalyx.cpp:58
Value getConditionValue() override
Definition: SCFToCalyx.cpp:64
calyx::GroupOp buildWhileLoopIterArgAssignments(OpBuilder &builder, ScfWhileOp op, calyx::ComponentOp componentOp, Twine uniqueSuffix, MutableArrayRef< OpOperand > ops)
Definition: SCFToCalyx.cpp:123
void setWhileLoopInitGroups(ScfWhileOp op, SmallVector< calyx::GroupOp > groups)
Definition: SCFToCalyx.cpp:142
SmallVector< calyx::GroupOp > getWhileLoopInitGroups(ScfWhileOp op)
Definition: SCFToCalyx.cpp:120
void addWhileLoopIterReg(ScfWhileOp op, calyx::RegisterOp reg, unsigned idx)
Definition: SCFToCalyx.cpp:129
const DenseMap< unsigned, calyx::RegisterOp > & getWhileLoopIterRegs(ScfWhileOp op)
Definition: SCFToCalyx.cpp:133
void setWhileLoopLatchGroup(ScfWhileOp op, calyx::GroupOp group)
Definition: SCFToCalyx.cpp:136
calyx::GroupOp getWhileLoopLatchGroup(ScfWhileOp op)
Definition: SCFToCalyx.cpp:139
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
void addMandatoryComponentPorts(PatternRewriter &rewriter, SmallVectorImpl< calyx::PortInfo > &ports)
void appendPortsForExternalMemref(PatternRewriter &rewriter, StringRef memName, Value memref, unsigned memoryID, SmallVectorImpl< calyx::PortInfo > &inPorts, SmallVectorImpl< calyx::PortInfo > &outPorts)
void buildAssignmentsForRegisterWrite(OpBuilder &builder, calyx::GroupOp groupOp, calyx::ComponentOp componentOp, calyx::RegisterOp &reg, Value inputValue)
Creates register assignment operations within the provided groupOp.
DenseMap< const mlir::RewritePattern *, SmallPtrSet< Operation *, 16 > > PatternApplicationState
Extra state that is passed to all PartialLoweringPatterns so they can record when they have run on an...
Type convIndexType(OpBuilder &builder, Type type)
LogicalResult applyModuleOpConversion(mlir::ModuleOp, StringRef topLevelFunction)
Helper to update the top-level ModuleOp to set the entrypoing function.
WalkResult getCiderSourceLocationMetadata(calyx::ComponentOp component, SmallVectorImpl< Attribute > &sourceLocations)
bool matchConstantOp(Operation *op, APInt &value)
unsigned handleZeroWidth(int64_t dim)
hw::ConstantOp createConstant(Location loc, OpBuilder &builder, ComponentOp component, size_t width, size_t value)
A helper function to create constants in the HW dialect.
calyx::RegisterOp createRegister(Location loc, OpBuilder &builder, ComponentOp component, size_t width, Twine prefix)
Creates a RegisterOp, with input and output port bit widths defined by width.
bool noStoresToMemory(Value memoryReference)
bool singleLoadFromMemory(Value memoryReference)
std::string getInstanceName(mlir::func::CallOp callOp)
A helper function to get the instance name.
Value createOrFoldNot(Location loc, Value value, OpBuilder &builder, bool twoState=false)
Create a `‘Not’' gate on a value.
Definition: CombOps.cpp:48
static constexpr std::string_view sPortNameAttr
Definition: SCFToCalyx.h:26
std::variant< calyx::GroupOp, WhileScheduleable, ForScheduleable, CallScheduleable > Scheduleable
A variant of types representing scheduleable operations.
Definition: SCFToCalyx.cpp:115
static LogicalResult buildAllocOp(ComponentLoweringState &componentState, PatternRewriter &rewriter, TAllocOp allocOp)
Definition: SCFToCalyx.cpp:605
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
std::unique_ptr< OperationPass< ModuleOp > > createSCFToCalyxPass()
Create an SCF to Calyx conversion pass.
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Definition: seq.py:20
std::optional< Value > done
std::optional< Value > writeEn
std::optional< Value > writeData
std::optional< Value > readData
SmallVector< Value > addrPorts
When building groups which contain accesses to multiple sequential components, a group_done op is cre...
GroupDoneOp's are terminator operations and should therefore be the last operator in a group.
This holds information about the port for either a Component or Cell.
Definition: CalyxOps.h:85
calyx::InstanceOp instanceOp
Instance for invoking.
Definition: SCFToCalyx.cpp:108
ScfForOp forOp
For operation to schedule.
Definition: SCFToCalyx.cpp:101
Creates a new Calyx component for each FuncOp in the program.
LogicalResult partiallyLowerFuncToComp(FuncOp funcOp, PatternRewriter &rewriter) const override
ScfWhileOp whileOp
While operation to schedule.
Definition: SCFToCalyx.cpp:96