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