CIRCT
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/Arithmetic/IR/Arithmetic.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/IR/AsmState.h"
27 #include "mlir/IR/Matchers.h"
28 #include "mlir/Transforms/GreedyPatternRewriteDriver.h"
29 #include "llvm/ADT/TypeSwitch.h"
30 
31 #include <variant>
32 
33 using namespace llvm;
34 using namespace mlir;
35 using namespace mlir::arith;
36 using namespace mlir::cf;
37 using namespace mlir::func;
38 
39 namespace circt {
40 class ComponentLoweringStateInterface;
41 namespace scftocalyx {
42 
43 //===----------------------------------------------------------------------===//
44 // Utility types
45 //===----------------------------------------------------------------------===//
46 
47 class ScfWhileOp : public calyx::WhileOpInterface<scf::WhileOp> {
48 public:
49  explicit ScfWhileOp(scf::WhileOp op)
50  : calyx::WhileOpInterface<scf::WhileOp>(op) {}
51 
52  Block::BlockArgListType getBodyArgs() override {
53  return getOperation().getAfterArguments();
54  }
55 
56  Block *getBodyBlock() override { return &getOperation().getAfter().front(); }
57 
58  Block *getConditionBlock() override {
59  return &getOperation().getBefore().front();
60  }
61 
62  Value getConditionValue() override {
63  return getOperation().getConditionOp().getOperand(0);
64  }
65 
66  Optional<uint64_t> getBound() override { return None; }
67 };
68 
69 //===----------------------------------------------------------------------===//
70 // Lowering state classes
71 //===----------------------------------------------------------------------===//
72 
74  /// While operation to schedule.
76  /// The group(s) to schedule before the while operation These groups should
77  /// set the initial value(s) of the loop init_args register(s).
78  SmallVector<calyx::GroupOp> initGroups;
79 };
80 
81 /// A variant of types representing scheduleable operations.
82 using Scheduleable = std::variant<calyx::GroupOp, WhileScheduleable>;
83 
84 /// Handles the current state of lowering of a Calyx component. It is mainly
85 /// used as a key/value store for recording information during partial lowering,
86 /// which is required at later lowering passes.
89  public calyx::LoopLoweringStateInterface<ScfWhileOp>,
90  public calyx::SchedulerInterface<Scheduleable> {
91 public:
92  ComponentLoweringState(calyx::ComponentOp component)
93  : calyx::ComponentLoweringStateInterface(component) {}
94 };
95 
96 //===----------------------------------------------------------------------===//
97 // Conversion patterns
98 //===----------------------------------------------------------------------===//
99 
100 /// Iterate through the operations of a source function and instantiate
101 /// components or primitives based on the type of the operations.
103  using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
104 
105  LogicalResult
107  PatternRewriter &rewriter) const override {
108  /// We walk the operations of the funcOp to ensure that all def's have
109  /// been visited before their uses.
110  bool opBuiltSuccessfully = true;
111  funcOp.walk([&](Operation *_op) {
112  opBuiltSuccessfully &=
113  TypeSwitch<mlir::Operation *, bool>(_op)
114  .template Case<arith::ConstantOp, ReturnOp, BranchOpInterface,
115  /// SCF
116  scf::YieldOp,
117  /// memref
118  memref::AllocOp, memref::AllocaOp, memref::LoadOp,
119  memref::StoreOp,
120  /// standard arithmetic
121  AddIOp, SubIOp, CmpIOp, ShLIOp, ShRUIOp, ShRSIOp,
122  AndIOp, XOrIOp, OrIOp, ExtUIOp, ExtSIOp, TruncIOp,
123  MulIOp, DivUIOp, DivSIOp, RemUIOp, RemSIOp,
124  IndexCastOp>(
125  [&](auto op) { return buildOp(rewriter, op).succeeded(); })
126  .template Case<scf::WhileOp, FuncOp, scf::ConditionOp>([&](auto) {
127  /// Skip: these special cases will be handled separately.
128  return true;
129  })
130  .Default([&](auto op) {
131  op->emitError() << "Unhandled operation during BuildOpGroups()";
132  return false;
133  });
134 
135  return opBuiltSuccessfully ? WalkResult::advance()
136  : WalkResult::interrupt();
137  });
138 
139  return success(opBuiltSuccessfully);
140  }
141 
142 private:
143  /// Op builder specializations.
144  LogicalResult buildOp(PatternRewriter &rewriter, scf::YieldOp yieldOp) const;
145  LogicalResult buildOp(PatternRewriter &rewriter,
146  BranchOpInterface brOp) const;
147  LogicalResult buildOp(PatternRewriter &rewriter,
148  arith::ConstantOp constOp) const;
149  LogicalResult buildOp(PatternRewriter &rewriter, AddIOp op) const;
150  LogicalResult buildOp(PatternRewriter &rewriter, SubIOp op) const;
151  LogicalResult buildOp(PatternRewriter &rewriter, MulIOp op) const;
152  LogicalResult buildOp(PatternRewriter &rewriter, DivUIOp op) const;
153  LogicalResult buildOp(PatternRewriter &rewriter, DivSIOp op) const;
154  LogicalResult buildOp(PatternRewriter &rewriter, RemUIOp op) const;
155  LogicalResult buildOp(PatternRewriter &rewriter, RemSIOp op) const;
156  LogicalResult buildOp(PatternRewriter &rewriter, ShRUIOp op) const;
157  LogicalResult buildOp(PatternRewriter &rewriter, ShRSIOp op) const;
158  LogicalResult buildOp(PatternRewriter &rewriter, ShLIOp op) const;
159  LogicalResult buildOp(PatternRewriter &rewriter, AndIOp op) const;
160  LogicalResult buildOp(PatternRewriter &rewriter, OrIOp op) const;
161  LogicalResult buildOp(PatternRewriter &rewriter, XOrIOp op) const;
162  LogicalResult buildOp(PatternRewriter &rewriter, CmpIOp op) const;
163  LogicalResult buildOp(PatternRewriter &rewriter, TruncIOp op) const;
164  LogicalResult buildOp(PatternRewriter &rewriter, ExtUIOp op) const;
165  LogicalResult buildOp(PatternRewriter &rewriter, ExtSIOp op) const;
166  LogicalResult buildOp(PatternRewriter &rewriter, ReturnOp op) const;
167  LogicalResult buildOp(PatternRewriter &rewriter, IndexCastOp op) const;
168  LogicalResult buildOp(PatternRewriter &rewriter, memref::AllocOp op) const;
169  LogicalResult buildOp(PatternRewriter &rewriter, memref::AllocaOp op) const;
170  LogicalResult buildOp(PatternRewriter &rewriter, memref::LoadOp op) const;
171  LogicalResult buildOp(PatternRewriter &rewriter, memref::StoreOp op) const;
172 
173  /// buildLibraryOp will build a TCalyxLibOp inside a TGroupOp based on the
174  /// source operation TSrcOp.
175  template <typename TGroupOp, typename TCalyxLibOp, typename TSrcOp>
176  LogicalResult buildLibraryOp(PatternRewriter &rewriter, TSrcOp op,
177  TypeRange srcTypes, TypeRange dstTypes) const {
178  SmallVector<Type> types;
179  llvm::append_range(types, srcTypes);
180  llvm::append_range(types, dstTypes);
181 
182  auto calyxOp =
183  getState<ComponentLoweringState>().getNewLibraryOpInstance<TCalyxLibOp>(
184  rewriter, op.getLoc(), types);
185 
186  auto directions = calyxOp.portDirections();
187  SmallVector<Value, 4> opInputPorts;
188  SmallVector<Value, 4> opOutputPorts;
189  for (auto dir : enumerate(directions)) {
190  if (dir.value() == calyx::Direction::Input)
191  opInputPorts.push_back(calyxOp.getResult(dir.index()));
192  else
193  opOutputPorts.push_back(calyxOp.getResult(dir.index()));
194  }
195  assert(
196  opInputPorts.size() == op->getNumOperands() &&
197  opOutputPorts.size() == op->getNumResults() &&
198  "Expected an equal number of in/out ports in the Calyx library op with "
199  "respect to the number of operands/results of the source operation.");
200 
201  /// Create assignments to the inputs of the library op.
202  auto group = createGroupForOp<TGroupOp>(rewriter, op);
203  rewriter.setInsertionPointToEnd(group.getBody());
204  for (auto dstOp : enumerate(opInputPorts))
205  rewriter.create<calyx::AssignOp>(op.getLoc(), dstOp.value(),
206  op->getOperand(dstOp.index()));
207 
208  /// Replace the result values of the source operator with the new operator.
209  for (auto res : enumerate(opOutputPorts)) {
210  getState<ComponentLoweringState>().registerEvaluatingGroup(res.value(),
211  group);
212  op->getResult(res.index()).replaceAllUsesWith(res.value());
213  }
214  return success();
215  }
216 
217  /// buildLibraryOp which provides in- and output types based on the operands
218  /// and results of the op argument.
219  template <typename TGroupOp, typename TCalyxLibOp, typename TSrcOp>
220  LogicalResult buildLibraryOp(PatternRewriter &rewriter, TSrcOp op) const {
221  return buildLibraryOp<TGroupOp, TCalyxLibOp, TSrcOp>(
222  rewriter, op, op.getOperandTypes(), op->getResultTypes());
223  }
224 
225  /// Creates a group named by the basic block which the input op resides in.
226  template <typename TGroupOp>
227  TGroupOp createGroupForOp(PatternRewriter &rewriter, Operation *op) const {
228  Block *block = op->getBlock();
229  auto groupName = getState<ComponentLoweringState>().getUniqueName(
230  programState().blockName(block));
231  return calyx::createGroup<TGroupOp>(
232  rewriter, getState<ComponentLoweringState>().getComponentOp(),
233  op->getLoc(), groupName);
234  }
235 
236  /// buildLibraryBinaryPipeOp will build a TCalyxLibBinaryPipeOp, to
237  /// deal with MulIOp, DivUIOp and RemUIOp.
238  template <typename TOpType, typename TSrcOp>
239  LogicalResult buildLibraryBinaryPipeOp(PatternRewriter &rewriter, TSrcOp op,
240  TOpType opPipe, Value out) const {
241  StringRef opName = TSrcOp::getOperationName().split(".").second;
242  Location loc = op.getLoc();
243  Type width = op.getResult().getType();
244  // Pass the result from the Operation to the Calyx primitive.
245  op.getResult().replaceAllUsesWith(out);
246  auto reg = createRegister(
247  op.getLoc(), rewriter, getComponent(), width.getIntOrFloatBitWidth(),
248  getState<ComponentLoweringState>().getUniqueName(opName));
249  // Operation pipelines are not combinational, so a GroupOp is required.
250  auto group = createGroupForOp<calyx::GroupOp>(rewriter, op);
251  getState<ComponentLoweringState>().addBlockScheduleable(op->getBlock(),
252  group);
253 
254  rewriter.setInsertionPointToEnd(group.getBody());
255  rewriter.create<calyx::AssignOp>(loc, opPipe.left(), op.getLhs());
256  rewriter.create<calyx::AssignOp>(loc, opPipe.right(), op.getRhs());
257  // Write the output to this register.
258  rewriter.create<calyx::AssignOp>(loc, reg.in(), out);
259  // The write enable port is high when the pipeline is done.
260  rewriter.create<calyx::AssignOp>(loc, reg.write_en(), opPipe.done());
261  rewriter.create<calyx::AssignOp>(
262  loc, opPipe.go(), createConstant(loc, rewriter, getComponent(), 1, 1));
263  // The group is done when the register write is complete.
264  rewriter.create<calyx::GroupDoneOp>(loc, reg.done());
265 
266  // Register the values for the pipeline.
267  getState<ComponentLoweringState>().registerEvaluatingGroup(out, group);
268  getState<ComponentLoweringState>().registerEvaluatingGroup(opPipe.left(),
269  group);
270  getState<ComponentLoweringState>().registerEvaluatingGroup(opPipe.right(),
271  group);
272 
273  return success();
274  }
275 
276  /// Creates assignments within the provided group to the address ports of the
277  /// memoryOp based on the provided addressValues.
278  void assignAddressPorts(PatternRewriter &rewriter, Location loc,
279  calyx::GroupInterface group,
280  calyx::MemoryInterface memoryInterface,
281  Operation::operand_range addressValues) const {
282  IRRewriter::InsertionGuard guard(rewriter);
283  rewriter.setInsertionPointToEnd(group.getBody());
284  auto addrPorts = memoryInterface.addrPorts();
285  assert(addrPorts.size() == addressValues.size() &&
286  "Mismatch between number of address ports of the provided memory "
287  "and address assignment values");
288  for (auto &address : enumerate(addressValues))
289  rewriter.create<calyx::AssignOp>(loc, addrPorts[address.index()],
290  address.value());
291  }
292 };
293 
294 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
295  memref::LoadOp loadOp) const {
296  Value memref = loadOp.memref();
297  auto memoryInterface =
298  getState<ComponentLoweringState>().getMemoryInterface(memref);
299  if (calyx::noStoresToMemory(memref) && calyx::singleLoadFromMemory(memref)) {
300  // Single load from memory; we do not need to write the
301  // output to a register. This is essentially a "combinational read" under
302  // current Calyx semantics with memory, and thus can be done in a
303  // combinational group. Note that if any stores are done to this memory,
304  // we require that the load and store be in separate non-combinational
305  // groups to avoid reading and writing to the same memory in the same group.
306  auto combGroup = createGroupForOp<calyx::CombGroupOp>(rewriter, loadOp);
307  assignAddressPorts(rewriter, loadOp.getLoc(), combGroup, memoryInterface,
308  loadOp.getIndices());
309 
310  // We refrain from replacing the loadOp result with
311  // memoryInterface.readData, since multiple loadOp's need to be converted
312  // to a single memory's ReadData. If this replacement is done now, we lose
313  // the link between which SSA memref::LoadOp values map to which groups for
314  // loading a value from the Calyx memory. At this point of lowering, we
315  // keep the memref::LoadOp SSA value, and do value replacement _after_
316  // control has been generated (see LateSSAReplacement). This is *vital* for
317  // things such as calyx::InlineCombGroups to be able to properly track which
318  // memory assignment groups belong to which accesses.
319  getState<ComponentLoweringState>().registerEvaluatingGroup(
320  loadOp.getResult(), combGroup);
321  } else {
322  auto group = createGroupForOp<calyx::GroupOp>(rewriter, loadOp);
323  assignAddressPorts(rewriter, loadOp.getLoc(), group, memoryInterface,
324  loadOp.getIndices());
325 
326  // Multiple loads from the same memory; In this case, we _may_ have a
327  // structural hazard in the design we generate. To get around this, we
328  // conservatively place a register in front of each load operation, and
329  // replace all uses of the loaded value with the register output. Proper
330  // handling of this requires the combinational group inliner/scheduler to
331  // be aware of when a combinational expression references multiple loaded
332  // values from the same memory, and then schedule assignments to temporary
333  // registers to get around the structural hazard.
334  auto reg = createRegister(
335  loadOp.getLoc(), rewriter, getComponent(),
336  loadOp.getMemRefType().getElementTypeBitWidth(),
337  getState<ComponentLoweringState>().getUniqueName("load"));
339  rewriter, group, getState<ComponentLoweringState>().getComponentOp(),
340  reg, memoryInterface.readData());
341  loadOp.getResult().replaceAllUsesWith(reg.out());
342  getState<ComponentLoweringState>().addBlockScheduleable(loadOp->getBlock(),
343  group);
344  }
345  return success();
346 }
347 
348 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
349  memref::StoreOp storeOp) const {
350  auto memoryInterface =
351  getState<ComponentLoweringState>().getMemoryInterface(storeOp.memref());
352  auto group = createGroupForOp<calyx::GroupOp>(rewriter, storeOp);
353 
354  // This is a sequential group, so register it as being scheduleable for the
355  // block.
356  getState<ComponentLoweringState>().addBlockScheduleable(storeOp->getBlock(),
357  group);
358  assignAddressPorts(rewriter, storeOp.getLoc(), group, memoryInterface,
359  storeOp.getIndices());
360  rewriter.setInsertionPointToEnd(group.getBody());
361  rewriter.create<calyx::AssignOp>(
362  storeOp.getLoc(), memoryInterface.writeData(), storeOp.getValueToStore());
363  rewriter.create<calyx::AssignOp>(
364  storeOp.getLoc(), memoryInterface.writeEn(),
365  createConstant(storeOp.getLoc(), rewriter, getComponent(), 1, 1));
366  rewriter.create<calyx::GroupDoneOp>(storeOp.getLoc(), memoryInterface.done());
367 
368  return success();
369 }
370 
371 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
372  MulIOp mul) const {
373  Location loc = mul.getLoc();
374  Type width = mul.getResult().getType(), one = rewriter.getI1Type();
375  auto mulPipe =
376  getState<ComponentLoweringState>()
377  .getNewLibraryOpInstance<calyx::MultPipeLibOp>(
378  rewriter, loc, {one, one, one, width, width, width, one});
379  return buildLibraryBinaryPipeOp<calyx::MultPipeLibOp>(rewriter, mul, mulPipe,
380  /*out=*/mulPipe.out());
381 }
382 
383 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
384  DivUIOp div) const {
385  Location loc = div.getLoc();
386  Type width = div.getResult().getType(), one = rewriter.getI1Type();
387  auto divPipe =
388  getState<ComponentLoweringState>()
389  .getNewLibraryOpInstance<calyx::DivUPipeLibOp>(
390  rewriter, loc, {one, one, one, width, width, width, one});
391  return buildLibraryBinaryPipeOp<calyx::DivUPipeLibOp>(rewriter, div, divPipe,
392  /*out=*/divPipe.out());
393 }
394 
395 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
396  DivSIOp div) const {
397  Location loc = div.getLoc();
398  Type width = div.getResult().getType(), one = rewriter.getI1Type();
399  auto divPipe =
400  getState<ComponentLoweringState>()
401  .getNewLibraryOpInstance<calyx::DivSPipeLibOp>(
402  rewriter, loc, {one, one, one, width, width, width, one});
403  return buildLibraryBinaryPipeOp<calyx::DivSPipeLibOp>(rewriter, div, divPipe,
404  /*out=*/divPipe.out());
405 }
406 
407 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
408  RemUIOp rem) const {
409  Location loc = rem.getLoc();
410  Type width = rem.getResult().getType(), one = rewriter.getI1Type();
411  auto remPipe =
412  getState<ComponentLoweringState>()
413  .getNewLibraryOpInstance<calyx::RemUPipeLibOp>(
414  rewriter, loc, {one, one, one, width, width, width, one});
415  return buildLibraryBinaryPipeOp<calyx::RemUPipeLibOp>(rewriter, rem, remPipe,
416  /*out=*/remPipe.out());
417 }
418 
419 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
420  RemSIOp rem) const {
421  Location loc = rem.getLoc();
422  Type width = rem.getResult().getType(), one = rewriter.getI1Type();
423  auto remPipe =
424  getState<ComponentLoweringState>()
425  .getNewLibraryOpInstance<calyx::RemSPipeLibOp>(
426  rewriter, loc, {one, one, one, width, width, width, one});
427  return buildLibraryBinaryPipeOp<calyx::RemSPipeLibOp>(rewriter, rem, remPipe,
428  /*out=*/remPipe.out());
429 }
430 
431 template <typename TAllocOp>
432 static LogicalResult buildAllocOp(ComponentLoweringState &componentState,
433  PatternRewriter &rewriter, TAllocOp allocOp) {
434  rewriter.setInsertionPointToStart(componentState.getComponentOp().getBody());
435  MemRefType memtype = allocOp.getType();
436  SmallVector<int64_t> addrSizes;
437  SmallVector<int64_t> sizes;
438  for (int64_t dim : memtype.getShape()) {
439  sizes.push_back(dim);
440  addrSizes.push_back(calyx::handleZeroWidth(dim));
441  }
442  auto memoryOp = rewriter.create<calyx::MemoryOp>(
443  allocOp.getLoc(), componentState.getUniqueName("mem"),
444  memtype.getElementType().getIntOrFloatBitWidth(), sizes, addrSizes);
445  // Externalize memories by default. This makes it easier for the native
446  // compiler to provide initialized memories.
447  memoryOp->setAttr("external",
448  IntegerAttr::get(rewriter.getI1Type(), llvm::APInt(1, 1)));
449  componentState.registerMemoryInterface(allocOp.getResult(),
450  calyx::MemoryInterface(memoryOp));
451  return success();
452 }
453 
454 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
455  memref::AllocOp allocOp) const {
456  return buildAllocOp(getState<ComponentLoweringState>(), rewriter, allocOp);
457 }
458 
459 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
460  memref::AllocaOp allocOp) const {
461  return buildAllocOp(getState<ComponentLoweringState>(), rewriter, allocOp);
462 }
463 
464 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
465  scf::YieldOp yieldOp) const {
466  if (yieldOp.getOperands().size() == 0)
467  return success();
468  auto whileOp = dyn_cast<scf::WhileOp>(yieldOp->getParentOp());
469  assert(whileOp);
470  ScfWhileOp whileOpInterface(whileOp);
471 
472  auto assignGroup =
473  getState<ComponentLoweringState>().buildLoopIterArgAssignments(
474  rewriter, whileOpInterface,
475  getState<ComponentLoweringState>().getComponentOp(),
476  getState<ComponentLoweringState>().getUniqueName(whileOp) + "_latch",
477  yieldOp->getOpOperands());
478  getState<ComponentLoweringState>().setLoopLatchGroup(whileOpInterface,
479  assignGroup);
480  return success();
481 }
482 
483 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
484  BranchOpInterface brOp) const {
485  /// Branch argument passing group creation
486  /// Branch operands are passed through registers. In BuildBasicBlockRegs we
487  /// created registers for all branch arguments of each block. We now
488  /// create groups for assigning values to these registers.
489  Block *srcBlock = brOp->getBlock();
490  for (auto succBlock : enumerate(brOp->getSuccessors())) {
491  auto succOperands = brOp.getSuccessorOperands(succBlock.index());
492  if (succOperands.empty())
493  continue;
494  // Create operand passing group
495  std::string groupName = programState().blockName(srcBlock) + "_to_" +
496  programState().blockName(succBlock.value());
497  auto groupOp = calyx::createGroup<calyx::GroupOp>(rewriter, getComponent(),
498  brOp.getLoc(), groupName);
499  // Fetch block argument registers associated with the basic block
500  auto dstBlockArgRegs =
501  getState<ComponentLoweringState>().getBlockArgRegs(succBlock.value());
502  // Create register assignment for each block argument
503  for (auto arg : enumerate(succOperands.getForwardedOperands())) {
504  auto reg = dstBlockArgRegs[arg.index()];
506  rewriter, groupOp,
507  getState<ComponentLoweringState>().getComponentOp(), reg,
508  arg.value());
509  }
510  /// Register the group as a block argument group, to be executed
511  /// when entering the successor block from this block (srcBlock).
512  getState<ComponentLoweringState>().addBlockArgGroup(
513  srcBlock, succBlock.value(), groupOp);
514  }
515  return success();
516 }
517 
518 /// For each return statement, we create a new group for assigning to the
519 /// previously created return value registers.
520 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
521  ReturnOp retOp) const {
522  if (retOp.getNumOperands() == 0)
523  return success();
524 
525  std::string groupName =
526  getState<ComponentLoweringState>().getUniqueName("ret_assign");
527  auto groupOp = calyx::createGroup<calyx::GroupOp>(rewriter, getComponent(),
528  retOp.getLoc(), groupName);
529  for (auto op : enumerate(retOp.getOperands())) {
530  auto reg = getState<ComponentLoweringState>().getReturnReg(op.index());
532  rewriter, groupOp, getState<ComponentLoweringState>().getComponentOp(),
533  reg, op.value());
534  }
535  /// Schedule group for execution for when executing the return op block.
536  getState<ComponentLoweringState>().addBlockScheduleable(retOp->getBlock(),
537  groupOp);
538  return success();
539 }
540 
541 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
542  arith::ConstantOp constOp) const {
543  /// Move constant operations to the compOp body as hw::ConstantOp's.
544  APInt value;
545  calyx::matchConstantOp(constOp, value);
546  auto hwConstOp = rewriter.replaceOpWithNewOp<hw::ConstantOp>(constOp, value);
547  hwConstOp->moveAfter(getComponent().getBody(),
548  getComponent().getBody()->begin());
549  return success();
550 }
551 
552 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
553  AddIOp op) const {
554  return buildLibraryOp<calyx::CombGroupOp, calyx::AddLibOp>(rewriter, op);
555 }
556 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
557  SubIOp op) const {
558  return buildLibraryOp<calyx::CombGroupOp, calyx::SubLibOp>(rewriter, op);
559 }
560 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
561  ShRUIOp op) const {
562  return buildLibraryOp<calyx::CombGroupOp, calyx::RshLibOp>(rewriter, op);
563 }
564 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
565  ShRSIOp op) const {
566  return buildLibraryOp<calyx::CombGroupOp, calyx::SrshLibOp>(rewriter, op);
567 }
568 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
569  ShLIOp op) const {
570  return buildLibraryOp<calyx::CombGroupOp, calyx::LshLibOp>(rewriter, op);
571 }
572 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
573  AndIOp op) const {
574  return buildLibraryOp<calyx::CombGroupOp, calyx::AndLibOp>(rewriter, op);
575 }
576 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
577  OrIOp op) const {
578  return buildLibraryOp<calyx::CombGroupOp, calyx::OrLibOp>(rewriter, op);
579 }
580 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
581  XOrIOp op) const {
582  return buildLibraryOp<calyx::CombGroupOp, calyx::XorLibOp>(rewriter, op);
583 }
584 
585 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
586  CmpIOp op) const {
587  switch (op.getPredicate()) {
588  case CmpIPredicate::eq:
589  return buildLibraryOp<calyx::CombGroupOp, calyx::EqLibOp>(rewriter, op);
590  case CmpIPredicate::ne:
591  return buildLibraryOp<calyx::CombGroupOp, calyx::NeqLibOp>(rewriter, op);
592  case CmpIPredicate::uge:
593  return buildLibraryOp<calyx::CombGroupOp, calyx::GeLibOp>(rewriter, op);
594  case CmpIPredicate::ult:
595  return buildLibraryOp<calyx::CombGroupOp, calyx::LtLibOp>(rewriter, op);
596  case CmpIPredicate::ugt:
597  return buildLibraryOp<calyx::CombGroupOp, calyx::GtLibOp>(rewriter, op);
598  case CmpIPredicate::ule:
599  return buildLibraryOp<calyx::CombGroupOp, calyx::LeLibOp>(rewriter, op);
600  case CmpIPredicate::sge:
601  return buildLibraryOp<calyx::CombGroupOp, calyx::SgeLibOp>(rewriter, op);
602  case CmpIPredicate::slt:
603  return buildLibraryOp<calyx::CombGroupOp, calyx::SltLibOp>(rewriter, op);
604  case CmpIPredicate::sgt:
605  return buildLibraryOp<calyx::CombGroupOp, calyx::SgtLibOp>(rewriter, op);
606  case CmpIPredicate::sle:
607  return buildLibraryOp<calyx::CombGroupOp, calyx::SleLibOp>(rewriter, op);
608  }
609  llvm_unreachable("unsupported comparison predicate");
610 }
611 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
612  TruncIOp op) const {
613  return buildLibraryOp<calyx::CombGroupOp, calyx::SliceLibOp>(
614  rewriter, op, {op.getOperand().getType()}, {op.getType()});
615 }
616 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
617  ExtUIOp op) const {
618  return buildLibraryOp<calyx::CombGroupOp, calyx::PadLibOp>(
619  rewriter, op, {op.getOperand().getType()}, {op.getType()});
620 }
621 
622 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
623  ExtSIOp op) const {
624  return buildLibraryOp<calyx::CombGroupOp, calyx::ExtSILibOp>(
625  rewriter, op, {op.getOperand().getType()}, {op.getType()});
626 }
627 
628 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
629  IndexCastOp op) const {
630  Type sourceType = calyx::convIndexType(rewriter, op.getOperand().getType());
631  Type targetType = calyx::convIndexType(rewriter, op.getResult().getType());
632  unsigned targetBits = targetType.getIntOrFloatBitWidth();
633  unsigned sourceBits = sourceType.getIntOrFloatBitWidth();
634  LogicalResult res = success();
635 
636  if (targetBits == sourceBits) {
637  /// Drop the index cast and replace uses of the target value with the source
638  /// value.
639  op.getResult().replaceAllUsesWith(op.getOperand());
640  } else {
641  /// pad/slice the source operand.
642  if (sourceBits > targetBits)
643  res = buildLibraryOp<calyx::CombGroupOp, calyx::SliceLibOp>(
644  rewriter, op, {sourceType}, {targetType});
645  else
646  res = buildLibraryOp<calyx::CombGroupOp, calyx::PadLibOp>(
647  rewriter, op, {sourceType}, {targetType});
648  }
649  rewriter.eraseOp(op);
650  return res;
651 }
652 
653 /// Inlines Calyx ExecuteRegionOp operations within their parent blocks.
654 /// An execution region op (ERO) is inlined by:
655 /// i : add a sink basic block for all yield operations inside the
656 /// ERO to jump to
657 /// ii : Rewrite scf.yield calls inside the ERO to branch to the sink block
658 /// iii: inline the ERO region
659 /// TODO(#1850) evaluate the usefulness of this lowering pattern.
661  : public OpRewritePattern<scf::ExecuteRegionOp> {
662  using OpRewritePattern::OpRewritePattern;
663 
664  LogicalResult matchAndRewrite(scf::ExecuteRegionOp execOp,
665  PatternRewriter &rewriter) const override {
666  /// Determine type of "yield" operations inside the ERO.
667  TypeRange yieldTypes = execOp.getResultTypes();
668 
669  /// Create sink basic block and rewrite uses of yield results to sink block
670  /// arguments.
671  rewriter.setInsertionPointAfter(execOp);
672  auto *sinkBlock = rewriter.splitBlock(
673  execOp->getBlock(),
674  execOp.getOperation()->getIterator()->getNextNode()->getIterator());
675  sinkBlock->addArguments(
676  yieldTypes,
677  SmallVector<Location, 4>(yieldTypes.size(), rewriter.getUnknownLoc()));
678  for (auto res : enumerate(execOp.getResults()))
679  res.value().replaceAllUsesWith(sinkBlock->getArgument(res.index()));
680 
681  /// Rewrite yield calls as branches.
682  for (auto yieldOp :
683  make_early_inc_range(execOp.getRegion().getOps<scf::YieldOp>())) {
684  rewriter.setInsertionPointAfter(yieldOp);
685  rewriter.replaceOpWithNewOp<BranchOp>(yieldOp, sinkBlock,
686  yieldOp.getOperands());
687  }
688 
689  /// Inline the regionOp.
690  auto *preBlock = execOp->getBlock();
691  auto *execOpEntryBlock = &execOp.getRegion().front();
692  auto *postBlock = execOp->getBlock()->splitBlock(execOp);
693  rewriter.inlineRegionBefore(execOp.getRegion(), postBlock);
694  rewriter.mergeBlocks(postBlock, preBlock);
695  rewriter.eraseOp(execOp);
696 
697  /// Finally, erase the unused entry block of the execOp region.
698  rewriter.mergeBlocks(execOpEntryBlock, preBlock);
699 
700  return success();
701  }
702 };
703 
704 /// Creates a new Calyx component for each FuncOp in the program.
706  using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
707 
708  LogicalResult
710  PatternRewriter &rewriter) const override {
711  /// Maintain a mapping between funcOp input arguments and the port index
712  /// which the argument will eventually map to.
713  DenseMap<Value, unsigned> funcOpArgRewrites;
714 
715  /// Maintain a mapping between funcOp output indexes and the component
716  /// output port index which the return value will eventually map to.
717  DenseMap<unsigned, unsigned> funcOpResultMapping;
718 
719  /// Maintain a mapping between an external memory argument (identified by a
720  /// memref) and eventual component input- and output port indices that will
721  /// map to the memory ports. The pair denotes the start index of the memory
722  /// ports in the in- and output ports of the component. Ports are expected
723  /// to be ordered in the same manner as they are added by
724  /// calyx::appendPortsForExternalMemref.
725  DenseMap<Value, std::pair<unsigned, unsigned>> extMemoryCompPortIndices;
726 
727  /// Create I/O ports. Maintain separate in/out port vectors to determine
728  /// which port index each function argument will eventually map to.
729  SmallVector<calyx::PortInfo> inPorts, outPorts;
730  FunctionType funcType = funcOp.getFunctionType();
731  unsigned extMemCounter = 0;
732  for (auto &arg : enumerate(funcOp.getArguments())) {
733  if (arg.value().getType().isa<MemRefType>()) {
734  /// External memories
735  auto memName =
736  "ext_mem" + std::to_string(extMemoryCompPortIndices.size());
737  extMemoryCompPortIndices[arg.value()] = {inPorts.size(),
738  outPorts.size()};
739  calyx::appendPortsForExternalMemref(rewriter, memName, arg.value(),
740  extMemCounter++, inPorts, outPorts);
741  } else {
742  /// Single-port arguments
743  auto inName = "in" + std::to_string(arg.index());
744  funcOpArgRewrites[arg.value()] = inPorts.size();
745  inPorts.push_back(calyx::PortInfo{
746  rewriter.getStringAttr(inName),
747  calyx::convIndexType(rewriter, arg.value().getType()),
749  DictionaryAttr::get(rewriter.getContext(), {})});
750  }
751  }
752  for (auto &res : enumerate(funcType.getResults())) {
753  funcOpResultMapping[res.index()] = outPorts.size();
754  outPorts.push_back(calyx::PortInfo{
755  rewriter.getStringAttr("out" + std::to_string(res.index())),
756  calyx::convIndexType(rewriter, res.value()), calyx::Direction::Output,
757  DictionaryAttr::get(rewriter.getContext(), {})});
758  }
759 
760  /// We've now recorded all necessary indices. Merge in- and output ports
761  /// and add the required mandatory component ports.
762  auto ports = inPorts;
763  llvm::append_range(ports, outPorts);
764  calyx::addMandatoryComponentPorts(rewriter, ports);
765 
766  /// Create a calyx::ComponentOp corresponding to the to-be-lowered function.
767  auto compOp = rewriter.create<calyx::ComponentOp>(
768  funcOp.getLoc(), rewriter.getStringAttr(funcOp.getSymName()), ports);
769 
770  /// Mark this component as the toplevel.
771  compOp->setAttr("toplevel", rewriter.getUnitAttr());
772 
773  /// Store the function-to-component mapping.
774  functionMapping[funcOp] = compOp;
775  auto *compState = programState().getState<ComponentLoweringState>(compOp);
776  compState->setFuncOpResultMapping(funcOpResultMapping);
777 
778  /// Rewrite funcOp SSA argument values to the CompOp arguments.
779  for (auto &mapping : funcOpArgRewrites)
780  mapping.getFirst().replaceAllUsesWith(
781  compOp.getArgument(mapping.getSecond()));
782 
783  /// Register external memories
784  for (auto extMemPortIndices : extMemoryCompPortIndices) {
785  /// Create a mapping for the in- and output ports using the Calyx memory
786  /// port structure.
787  calyx::MemoryPortsImpl extMemPorts;
788  unsigned inPortsIt = extMemPortIndices.getSecond().first;
789  unsigned outPortsIt = extMemPortIndices.getSecond().second +
790  compOp.getInputPortInfo().size();
791  extMemPorts.readData = compOp.getArgument(inPortsIt++);
792  extMemPorts.done = compOp.getArgument(inPortsIt);
793  extMemPorts.writeData = compOp.getArgument(outPortsIt++);
794  unsigned nAddresses = extMemPortIndices.getFirst()
795  .getType()
796  .cast<MemRefType>()
797  .getShape()
798  .size();
799  for (unsigned j = 0; j < nAddresses; ++j)
800  extMemPorts.addrPorts.push_back(compOp.getArgument(outPortsIt++));
801  extMemPorts.writeEn = compOp.getArgument(outPortsIt);
802 
803  /// Register the external memory ports as a memory interface within the
804  /// component.
805  compState->registerMemoryInterface(extMemPortIndices.getFirst(),
806  calyx::MemoryInterface(extMemPorts));
807  }
808 
809  return success();
810  }
811 };
812 
813 /// In BuildWhileGroups, a register is created for each iteration argumenet of
814 /// the while op. These registers are then written to on the while op
815 /// terminating yield operation alongside before executing the whileOp in the
816 /// schedule, to set the initial values of the argument registers.
818  using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
819 
820  LogicalResult
822  PatternRewriter &rewriter) const override {
823  LogicalResult res = success();
824  funcOp.walk([&](Operation *op) {
825  // Only work on ops that support the ScfWhileOp.
826  if (!isa<scf::WhileOp>(op))
827  return WalkResult::advance();
828 
829  auto scfWhileOp = cast<scf::WhileOp>(op);
830  ScfWhileOp whileOp(scfWhileOp);
831 
832  getState<ComponentLoweringState>().setUniqueName(whileOp.getOperation(),
833  "while");
834 
835  /// Check for do-while loops.
836  /// TODO(mortbopet) can we support these? for now, do not support loops
837  /// where iterargs are changed in the 'before' region. scf.WhileOp also
838  /// has support for different types of iter_args and return args which we
839  /// also do not support; iter_args and while return values are placed in
840  /// the same registers.
841  for (auto barg :
842  enumerate(scfWhileOp.getBefore().front().getArguments())) {
843  auto condOp = scfWhileOp.getConditionOp().getArgs()[barg.index()];
844  if (barg.value() != condOp) {
845  res = whileOp.getOperation()->emitError()
846  << programState().irName(barg.value())
847  << " != " << programState().irName(condOp)
848  << "do-while loops not supported; expected iter-args to "
849  "remain untransformed in the 'before' region of the "
850  "scf.while op.";
851  return WalkResult::interrupt();
852  }
853  }
854 
855  /// Create iteration argument registers.
856  /// The iteration argument registers will be referenced:
857  /// - In the "before" part of the while loop, calculating the conditional,
858  /// - In the "after" part of the while loop,
859  /// - Outside the while loop, rewriting the while loop return values.
860  for (auto arg : enumerate(whileOp.getBodyArgs())) {
861  std::string name = getState<ComponentLoweringState>()
862  .getUniqueName(whileOp.getOperation())
863  .str() +
864  "_arg" + std::to_string(arg.index());
865  auto reg =
866  createRegister(arg.value().getLoc(), rewriter, getComponent(),
867  arg.value().getType().getIntOrFloatBitWidth(), name);
868  getState<ComponentLoweringState>().addLoopIterReg(whileOp, reg,
869  arg.index());
870  arg.value().replaceAllUsesWith(reg.out());
871 
872  /// Also replace uses in the "before" region of the while loop
873  whileOp.getConditionBlock()
874  ->getArgument(arg.index())
875  .replaceAllUsesWith(reg.out());
876  }
877 
878  /// Create iter args initial value assignment group(s), one per register.
879  SmallVector<calyx::GroupOp> initGroups;
880  auto numOperands = whileOp.getOperation()->getNumOperands();
881  for (size_t i = 0; i < numOperands; ++i) {
882  auto initGroupOp =
883  getState<ComponentLoweringState>().buildLoopIterArgAssignments(
884  rewriter, whileOp,
885  getState<ComponentLoweringState>().getComponentOp(),
886  getState<ComponentLoweringState>().getUniqueName(
887  whileOp.getOperation()) +
888  "_init_" + std::to_string(i),
889  whileOp.getOperation()->getOpOperand(i));
890  initGroups.push_back(initGroupOp);
891  }
892 
893  getState<ComponentLoweringState>().addBlockScheduleable(
894  whileOp.getOperation()->getBlock(), WhileScheduleable{
895  whileOp,
896  initGroups,
897  });
898  return WalkResult::advance();
899  });
900  return res;
901  }
902 };
903 
904 /// Builds a control schedule by traversing the CFG of the function and
905 /// associating this with the previously created groups.
906 /// For simplicity, the generated control flow is expanded for all possible
907 /// paths in the input DAG. This elaborated control flow is later reduced in
908 /// the runControlFlowSimplification passes.
910  using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
911 
912  LogicalResult
914  PatternRewriter &rewriter) const override {
915  auto *entryBlock = &funcOp.getBlocks().front();
916  rewriter.setInsertionPointToStart(getComponent().getControlOp().getBody());
917  auto topLevelSeqOp = rewriter.create<calyx::SeqOp>(funcOp.getLoc());
918  DenseSet<Block *> path;
919  return buildCFGControl(path, rewriter, topLevelSeqOp.getBody(), nullptr,
920  entryBlock);
921  }
922 
923 private:
924  /// Sequentially schedules the groups that registered themselves with
925  /// 'block'.
926  LogicalResult scheduleBasicBlock(PatternRewriter &rewriter,
927  const DenseSet<Block *> &path,
928  mlir::Block *parentCtrlBlock,
929  mlir::Block *block) const {
930  auto compBlockScheduleables =
931  getState<ComponentLoweringState>().getBlockScheduleables(block);
932  auto loc = block->front().getLoc();
933 
934  if (compBlockScheduleables.size() > 1) {
935  auto seqOp = rewriter.create<calyx::SeqOp>(loc);
936  parentCtrlBlock = seqOp.getBody();
937  }
938 
939  for (auto &group : compBlockScheduleables) {
940  rewriter.setInsertionPointToEnd(parentCtrlBlock);
941  if (auto groupPtr = std::get_if<calyx::GroupOp>(&group); groupPtr) {
942  rewriter.create<calyx::EnableOp>(groupPtr->getLoc(),
943  groupPtr->sym_name());
944  } else if (auto whileSchedPtr = std::get_if<WhileScheduleable>(&group);
945  whileSchedPtr) {
946  auto &whileOp = whileSchedPtr->whileOp;
947 
948  auto whileCtrlOp =
949  buildWhileCtrlOp(whileOp, whileSchedPtr->initGroups, rewriter);
950  rewriter.setInsertionPointToEnd(whileCtrlOp.getBody());
951  auto whileBodyOp =
952  rewriter.create<calyx::SeqOp>(whileOp.getOperation()->getLoc());
953  auto *whileBodyOpBlock = whileBodyOp.getBody();
954 
955  /// Only schedule the 'after' block. The 'before' block is
956  /// implicitly scheduled when evaluating the while condition.
957  LogicalResult res = buildCFGControl(path, rewriter, whileBodyOpBlock,
958  block, whileOp.getBodyBlock());
959 
960  // Insert loop-latch at the end of the while group
961  rewriter.setInsertionPointToEnd(whileBodyOpBlock);
962  calyx::GroupOp whileLatchGroup =
963  getState<ComponentLoweringState>().getLoopLatchGroup(whileOp);
964  rewriter.create<calyx::EnableOp>(whileLatchGroup.getLoc(),
965  whileLatchGroup.getName());
966 
967  if (res.failed())
968  return res;
969  } else
970  llvm_unreachable("Unknown scheduleable");
971  }
972  return success();
973  }
974 
975  /// Schedules a block by inserting a branch argument assignment block (if any)
976  /// before recursing into the scheduling of the block innards.
977  /// Blocks 'from' and 'to' refer to blocks in the source program.
978  /// parentCtrlBlock refers to the control block wherein control operations are
979  /// to be inserted.
980  LogicalResult schedulePath(PatternRewriter &rewriter,
981  const DenseSet<Block *> &path, Location loc,
982  Block *from, Block *to,
983  Block *parentCtrlBlock) const {
984  /// Schedule any registered block arguments to be executed before the body
985  /// of the branch.
986  rewriter.setInsertionPointToEnd(parentCtrlBlock);
987  auto preSeqOp = rewriter.create<calyx::SeqOp>(loc);
988  rewriter.setInsertionPointToEnd(preSeqOp.getBody());
989  for (auto barg :
990  getState<ComponentLoweringState>().getBlockArgGroups(from, to))
991  rewriter.create<calyx::EnableOp>(barg.getLoc(), barg.sym_name());
992 
993  return buildCFGControl(path, rewriter, parentCtrlBlock, from, to);
994  }
995 
996  LogicalResult buildCFGControl(DenseSet<Block *> path,
997  PatternRewriter &rewriter,
998  mlir::Block *parentCtrlBlock,
999  mlir::Block *preBlock,
1000  mlir::Block *block) const {
1001  if (path.count(block) != 0)
1002  return preBlock->getTerminator()->emitError()
1003  << "CFG backedge detected. Loops must be raised to 'scf.while' or "
1004  "'scf.for' operations.";
1005 
1006  rewriter.setInsertionPointToEnd(parentCtrlBlock);
1007  LogicalResult bbSchedResult =
1008  scheduleBasicBlock(rewriter, path, parentCtrlBlock, block);
1009  if (bbSchedResult.failed())
1010  return bbSchedResult;
1011 
1012  path.insert(block);
1013  auto successors = block->getSuccessors();
1014  auto nSuccessors = successors.size();
1015  if (nSuccessors > 0) {
1016  auto brOp = dyn_cast<BranchOpInterface>(block->getTerminator());
1017  assert(brOp);
1018  if (nSuccessors > 1) {
1019  /// TODO(mortbopet): we could choose to support ie. std.switch, but it
1020  /// would probably be easier to just require it to be lowered
1021  /// beforehand.
1022  assert(nSuccessors == 2 &&
1023  "only conditional branches supported for now...");
1024  /// Wrap each branch inside an if/else.
1025  auto cond = brOp->getOperand(0);
1026  auto condGroup = getState<ComponentLoweringState>()
1027  .getEvaluatingGroup<calyx::CombGroupOp>(cond);
1028  auto symbolAttr = FlatSymbolRefAttr::get(
1029  StringAttr::get(getContext(), condGroup.sym_name()));
1030 
1031  auto ifOp = rewriter.create<calyx::IfOp>(
1032  brOp->getLoc(), cond, symbolAttr, /*initializeElseBody=*/true);
1033  rewriter.setInsertionPointToStart(ifOp.getThenBody());
1034  auto thenSeqOp = rewriter.create<calyx::SeqOp>(brOp.getLoc());
1035  rewriter.setInsertionPointToStart(ifOp.getElseBody());
1036  auto elseSeqOp = rewriter.create<calyx::SeqOp>(brOp.getLoc());
1037 
1038  bool trueBrSchedSuccess =
1039  schedulePath(rewriter, path, brOp.getLoc(), block, successors[0],
1040  thenSeqOp.getBody())
1041  .succeeded();
1042  bool falseBrSchedSuccess = true;
1043  if (trueBrSchedSuccess) {
1044  falseBrSchedSuccess =
1045  schedulePath(rewriter, path, brOp.getLoc(), block, successors[1],
1046  elseSeqOp.getBody())
1047  .succeeded();
1048  }
1049 
1050  return success(trueBrSchedSuccess && falseBrSchedSuccess);
1051  } else {
1052  /// Schedule sequentially within the current parent control block.
1053  return schedulePath(rewriter, path, brOp.getLoc(), block,
1054  successors.front(), parentCtrlBlock);
1055  }
1056  }
1057  return success();
1058  }
1059 
1060  calyx::WhileOp buildWhileCtrlOp(ScfWhileOp whileOp,
1061  SmallVector<calyx::GroupOp> initGroups,
1062  PatternRewriter &rewriter) const {
1063  Location loc = whileOp.getLoc();
1064  /// Insert while iter arg initialization group(s). Emit a
1065  /// parallel group to assign one or more registers all at once.
1066  {
1067  PatternRewriter::InsertionGuard g(rewriter);
1068  auto parOp = rewriter.create<calyx::ParOp>(loc);
1069  rewriter.setInsertionPointToStart(parOp.getBody());
1070  for (calyx::GroupOp group : initGroups)
1071  rewriter.create<calyx::EnableOp>(group.getLoc(), group.getName());
1072  }
1073 
1074  /// Insert the while op itself.
1075  auto cond = whileOp.getConditionValue();
1076  auto condGroup = getState<ComponentLoweringState>()
1077  .getEvaluatingGroup<calyx::CombGroupOp>(cond);
1078  auto symbolAttr = FlatSymbolRefAttr::get(
1079  StringAttr::get(getContext(), condGroup.sym_name()));
1080  return rewriter.create<calyx::WhileOp>(loc, cond, symbolAttr);
1081  }
1082 };
1083 
1084 /// LateSSAReplacement contains various functions for replacing SSA values that
1085 /// were not replaced during op construction.
1087  using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
1088 
1089  LogicalResult partiallyLowerFuncToComp(FuncOp funcOp,
1090  PatternRewriter &) const override {
1091  funcOp.walk([&](scf::WhileOp op) {
1092  /// The yielded values returned from the while op will be present in the
1093  /// iterargs registers post execution of the loop.
1094  /// This is done now, as opposed to during BuildWhileGroups since if the
1095  /// results of the whileOp were replaced before
1096  /// BuildOpGroups/BuildControl, the whileOp would get dead-code
1097  /// eliminated.
1098  ScfWhileOp whileOp(op);
1099  for (auto res :
1100  getState<ComponentLoweringState>().getLoopIterRegs(whileOp))
1101  whileOp.getOperation()->getResults()[res.first].replaceAllUsesWith(
1102  res.second.out());
1103  });
1104 
1105  funcOp.walk([&](memref::LoadOp loadOp) {
1106  if (calyx::singleLoadFromMemory(loadOp)) {
1107  /// In buildOpGroups we did not replace loadOp's results, to ensure a
1108  /// link between evaluating groups (which fix the input addresses of a
1109  /// memory op) and a readData result. Now, we may replace these SSA
1110  /// values with their memoryOp readData output.
1111  loadOp.getResult().replaceAllUsesWith(
1112  getState<ComponentLoweringState>()
1113  .getMemoryInterface(loadOp.memref())
1114  .readData());
1115  }
1116  });
1117 
1118  return success();
1119  }
1120 };
1121 
1122 /// Erases FuncOp operations.
1124  using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
1125 
1126  LogicalResult
1128  PatternRewriter &rewriter) const override {
1129  rewriter.eraseOp(funcOp);
1130  return success();
1131  }
1132 };
1133 
1134 //===----------------------------------------------------------------------===//
1135 // Pass driver
1136 //===----------------------------------------------------------------------===//
1137 class SCFToCalyxPass : public SCFToCalyxBase<SCFToCalyxPass> {
1138 public:
1140  : SCFToCalyxBase<SCFToCalyxPass>(), partialPatternRes(success()) {}
1141  void runOnOperation() override;
1142 
1143  LogicalResult setTopLevelFunction(mlir::ModuleOp moduleOp,
1144  std::string &topLevelFunction) {
1145  if (!topLevelFunctionOpt.empty()) {
1146  if (SymbolTable::lookupSymbolIn(moduleOp, topLevelFunctionOpt) ==
1147  nullptr) {
1148  moduleOp.emitError() << "Top level function '" << topLevelFunctionOpt
1149  << "' not found in module.";
1150  return failure();
1151  }
1152  topLevelFunction = topLevelFunctionOpt;
1153  } else {
1154  /// No top level function set; infer top level if the module only contains
1155  /// a single function, else, throw error.
1156  auto funcOps = moduleOp.getOps<FuncOp>();
1157  if (std::distance(funcOps.begin(), funcOps.end()) == 1)
1158  topLevelFunction = (*funcOps.begin()).getSymName().str();
1159  else {
1160  moduleOp.emitError()
1161  << "Module contains multiple functions, but no top level "
1162  "function was set. Please see --top-level-function";
1163  return failure();
1164  }
1165  }
1166  return success();
1167  }
1168 
1170  enum class Strategy { Once, Greedy };
1171  RewritePatternSet pattern;
1173  };
1174 
1175  //// Creates a new Calyx program with the contents of the source module
1176  /// inlined within.
1177  /// Furthermore, this function performs validation on the input function,
1178  /// to ensure that we've implemented the capabilities necessary to convert
1179  /// it.
1180  LogicalResult createProgram(StringRef topLevelFunction,
1181  calyx::ProgramOp *programOpOut) {
1182  // Program legalization - the partial conversion driver will not run
1183  // unless some pattern is provided - provide a dummy pattern.
1184  struct DummyPattern : public OpRewritePattern<mlir::ModuleOp> {
1185  using OpRewritePattern::OpRewritePattern;
1186  LogicalResult matchAndRewrite(mlir::ModuleOp,
1187  PatternRewriter &) const override {
1188  return failure();
1189  }
1190  };
1191 
1192  ConversionTarget target(getContext());
1193  target.addLegalDialect<calyx::CalyxDialect>();
1194  target.addLegalDialect<scf::SCFDialect>();
1195  target.addIllegalDialect<hw::HWDialect>();
1196  target.addIllegalDialect<comb::CombDialect>();
1197 
1198  // For loops should have been lowered to while loops
1199  target.addIllegalOp<scf::ForOp>();
1200 
1201  // Only accept std operations which we've added lowerings for
1202  target.addIllegalDialect<FuncDialect>();
1203  target.addIllegalDialect<ArithmeticDialect>();
1204  target.addLegalOp<AddIOp, SubIOp, CmpIOp, ShLIOp, ShRUIOp, ShRSIOp, AndIOp,
1205  XOrIOp, OrIOp, ExtUIOp, TruncIOp, CondBranchOp, BranchOp,
1206  MulIOp, DivUIOp, DivSIOp, RemUIOp, RemSIOp, ReturnOp,
1207  arith::ConstantOp, IndexCastOp, FuncOp, ExtSIOp>();
1208 
1209  RewritePatternSet legalizePatterns(&getContext());
1210  legalizePatterns.add<DummyPattern>(&getContext());
1211  DenseSet<Operation *> legalizedOps;
1212  if (applyPartialConversion(getOperation(), target,
1213  std::move(legalizePatterns))
1214  .failed())
1215  return failure();
1216 
1217  // Program conversion
1218  RewritePatternSet conversionPatterns(&getContext());
1219  conversionPatterns.add<calyx::ModuleOpConversion>(
1220  &getContext(), topLevelFunction, programOpOut);
1221  return applyOpPatternsAndFold(getOperation(),
1222  std::move(conversionPatterns));
1223  }
1224 
1225  /// 'Once' patterns are expected to take an additional LogicalResult&
1226  /// argument, to forward their result state (greedyPatternRewriteDriver
1227  /// results are skipped for Once patterns).
1228  template <typename TPattern, typename... PatternArgs>
1229  void addOncePattern(SmallVectorImpl<LoweringPattern> &patterns,
1230  PatternArgs &&...args) {
1231  RewritePatternSet ps(&getContext());
1232  ps.add<TPattern>(&getContext(), partialPatternRes, args...);
1233  patterns.push_back(
1234  LoweringPattern{std::move(ps), LoweringPattern::Strategy::Once});
1235  }
1236 
1237  template <typename TPattern, typename... PatternArgs>
1238  void addGreedyPattern(SmallVectorImpl<LoweringPattern> &patterns,
1239  PatternArgs &&...args) {
1240  RewritePatternSet ps(&getContext());
1241  ps.add<TPattern>(&getContext(), args...);
1242  patterns.push_back(
1243  LoweringPattern{std::move(ps), LoweringPattern::Strategy::Greedy});
1244  }
1245 
1246  LogicalResult runPartialPattern(RewritePatternSet &pattern, bool runOnce) {
1247  assert(pattern.getNativePatterns().size() == 1 &&
1248  "Should only apply 1 partial lowering pattern at once");
1249 
1250  // During component creation, the function body is inlined into the
1251  // component body for further processing. However, proper control flow
1252  // will only be established later in the conversion process, so ensure
1253  // that rewriter optimizations (especially DCE) are disabled.
1254  GreedyRewriteConfig config;
1255  config.enableRegionSimplification = false;
1256  if (runOnce)
1257  config.maxIterations = 0;
1258 
1259  /// Can't return applyPatternsAndFoldGreedily. Root isn't
1260  /// necessarily erased so it will always return failed(). Instead,
1261  /// forward the 'succeeded' value from PartialLoweringPatternBase.
1262  (void)applyPatternsAndFoldGreedily(getOperation(), std::move(pattern),
1263  config);
1264  return partialPatternRes;
1265  }
1266 
1267 private:
1268  LogicalResult partialPatternRes;
1269  std::shared_ptr<calyx::ProgramLoweringState> loweringState = nullptr;
1270 };
1271 
1272 void SCFToCalyxPass::runOnOperation() {
1273  // Clear internal state. See https://github.com/llvm/circt/issues/3235
1274  loweringState.reset();
1275  partialPatternRes = LogicalResult::failure();
1276 
1277  std::string topLevelFunction;
1278  if (failed(setTopLevelFunction(getOperation(), topLevelFunction))) {
1279  signalPassFailure();
1280  return;
1281  }
1282 
1283  /// Start conversion
1284  calyx::ProgramOp programOp;
1285  if (failed(createProgram(topLevelFunction, &programOp))) {
1286  signalPassFailure();
1287  return;
1288  }
1289  assert(programOp.getOperation() != nullptr &&
1290  "programOp should have been set during module "
1291  "conversion, if module conversion succeeded.");
1292  loweringState = std::make_shared<calyx::ProgramLoweringState>(
1293  programOp, topLevelFunction);
1294 
1295  /// --------------------------------------------------------------------------
1296  /// If you are a developer, it may be helpful to add a
1297  /// 'getOperation()->dump()' call after the execution of each stage to
1298  /// view the transformations that's going on.
1299  /// --------------------------------------------------------------------------
1300 
1301  /// A mapping is maintained between a function operation and its corresponding
1302  /// Calyx component.
1303  DenseMap<FuncOp, calyx::ComponentOp> funcMap;
1304  SmallVector<LoweringPattern, 8> loweringPatterns;
1305 
1306  /// Creates a new Calyx component for each FuncOp in the inpurt module.
1307  addOncePattern<FuncOpConversion>(loweringPatterns, funcMap, *loweringState);
1308 
1309  /// This pass inlines scf.ExecuteRegionOp's by adding control-flow.
1310  addGreedyPattern<InlineExecuteRegionOpPattern>(loweringPatterns);
1311 
1312  /// This pattern converts all index typed values to an i32 integer.
1313  addOncePattern<calyx::ConvertIndexTypes>(loweringPatterns, funcMap,
1314  *loweringState);
1315 
1316  /// This pattern creates registers for all basic-block arguments.
1317  addOncePattern<calyx::BuildBasicBlockRegs>(loweringPatterns, funcMap,
1318  *loweringState);
1319 
1320  /// This pattern creates registers for the function return values.
1321  addOncePattern<calyx::BuildReturnRegs>(loweringPatterns, funcMap,
1322  *loweringState);
1323 
1324  /// This pattern creates registers for iteration arguments of scf.while
1325  /// operations. Additionally, creates a group for assigning the initial
1326  /// value of the iteration argument registers.
1327  addOncePattern<BuildWhileGroups>(loweringPatterns, funcMap, *loweringState);
1328 
1329  /// This pattern converts operations within basic blocks to Calyx library
1330  /// operators. Combinational operations are assigned inside a
1331  /// calyx::CombGroupOp, and sequential inside calyx::GroupOps.
1332  /// Sequential groups are registered with the Block* of which the operation
1333  /// originated from. This is used during control schedule generation. By
1334  /// having a distinct group for each operation, groups are analogous to SSA
1335  /// values in the source program.
1336  addOncePattern<BuildOpGroups>(loweringPatterns, funcMap, *loweringState);
1337 
1338  /// This pattern traverses the CFG of the program and generates a control
1339  /// schedule based on the calyx::GroupOp's which were registered for each
1340  /// basic block in the source function.
1341  addOncePattern<BuildControl>(loweringPatterns, funcMap, *loweringState);
1342 
1343  /// This pass recursively inlines use-def chains of combinational logic (from
1344  /// non-stateful groups) into groups referenced in the control schedule.
1345  addOncePattern<calyx::InlineCombGroups>(loweringPatterns, *loweringState);
1346 
1347  /// This pattern performs various SSA replacements that must be done
1348  /// after control generation.
1349  addOncePattern<LateSSAReplacement>(loweringPatterns, funcMap, *loweringState);
1350 
1351  /// Eliminate any unused combinational groups. This is done before
1352  /// calyx::RewriteMemoryAccesses to avoid inferring slice components for
1353  /// groups that will be removed.
1354  addGreedyPattern<calyx::EliminateUnusedCombGroups>(loweringPatterns);
1355 
1356  /// This pattern rewrites accesses to memories which are too wide due to
1357  /// index types being converted to a fixed-width integer type.
1358  addOncePattern<calyx::RewriteMemoryAccesses>(loweringPatterns,
1359  *loweringState);
1360 
1361  /// This pattern removes the source FuncOp which has now been converted into
1362  /// a Calyx component.
1363  addOncePattern<CleanupFuncOps>(loweringPatterns, funcMap, *loweringState);
1364 
1365  /// Sequentially apply each lowering pattern.
1366  for (auto &pat : loweringPatterns) {
1367  LogicalResult partialPatternRes = runPartialPattern(
1368  pat.pattern,
1369  /*runOnce=*/pat.strategy == LoweringPattern::Strategy::Once);
1370  if (succeeded(partialPatternRes))
1371  continue;
1372  signalPassFailure();
1373  return;
1374  }
1375 
1376  //===----------------------------------------------------------------------===//
1377  // Cleanup patterns
1378  //===----------------------------------------------------------------------===//
1379  RewritePatternSet cleanupPatterns(&getContext());
1380  cleanupPatterns.add<calyx::MultipleGroupDonePattern,
1381  calyx::NonTerminatingGroupDonePattern>(&getContext());
1382  if (failed(applyPatternsAndFoldGreedily(getOperation(),
1383  std::move(cleanupPatterns)))) {
1384  signalPassFailure();
1385  return;
1386  }
1387 
1388  if (ciderSourceLocationMetadata) {
1389  // Debugging information for the Cider debugger.
1390  // Reference: https://docs.calyxir.org/debug/cider.html
1391  SmallVector<Attribute, 16> sourceLocations;
1392  getOperation()->walk([&](calyx::ComponentOp component) {
1393  return getCiderSourceLocationMetadata(component, sourceLocations);
1394  });
1395 
1396  MLIRContext *context = getOperation()->getContext();
1397  getOperation()->setAttr("calyx.metadata",
1398  ArrayAttr::get(context, sourceLocations));
1399  }
1400 }
1401 
1402 } // namespace scftocalyx
1403 
1404 //===----------------------------------------------------------------------===//
1405 // Pass initialization
1406 //===----------------------------------------------------------------------===//
1407 
1408 std::unique_ptr<OperationPass<ModuleOp>> createSCFToCalyxPass() {
1409  return std::make_unique<scftocalyx::SCFToCalyxPass>();
1410 }
1411 
1412 } // namespace circt
circt::scftocalyx::BuildWhileGroups::partiallyLowerFuncToComp
LogicalResult partiallyLowerFuncToComp(FuncOp funcOp, PatternRewriter &rewriter) const override
Definition: SCFToCalyx.cpp:821
circt::scftocalyx::SCFToCalyxPass::partialPatternRes
LogicalResult partialPatternRes
Definition: SCFToCalyx.cpp:1268
circt::calyx::convIndexType
Type convIndexType(OpBuilder &builder, Type type)
Definition: CalyxLoweringUtils.cpp:138
circt::calyx::MemoryPortsImpl::done
Value done
Definition: CalyxLoweringUtils.h:88
llvm
Definition: CalyxEmitter.h:17
circt::createSCFToCalyxPass
std::unique_ptr< OperationPass< ModuleOp > > createSCFToCalyxPass()
Create an SCF to Calyx conversion pass.
Definition: SCFToCalyx.cpp:1408
circt::scftocalyx::LateSSAReplacement
LateSSAReplacement contains various functions for replacing SSA values that were not replaced during ...
Definition: SCFToCalyx.cpp:1086
circt::calyx::singleLoadFromMemory
bool singleLoadFromMemory(Value memoryReference)
Definition: CalyxLoweringUtils.cpp:119
CombOps.h
circt::esi.types
types
Definition: esi.py:103
circt::calyx::WhileOpInterface::getLoc
Location getLoc() override
Definition: CalyxLoweringUtils.h:151
circt::scftocalyx::ScfWhileOp::getBodyArgs
Block::BlockArgListType getBodyArgs() override
Definition: SCFToCalyx.cpp:52
circt::calyx::LoopLoweringStateInterface
Definition: CalyxLoweringUtils.h:199
circt::scftocalyx::ScfWhileOp
Definition: SCFToCalyx.cpp:47
mlir
Definition: DependenceAnalysis.h:21
circt::scftocalyx::WhileScheduleable::initGroups
SmallVector< calyx::GroupOp > initGroups
The group(s) to schedule before the while operation These groups should set the initial value(s) of t...
Definition: SCFToCalyx.cpp:78
SCFToCalyxBase
circt::scftocalyx::SCFToCalyxPass::runPartialPattern
LogicalResult runPartialPattern(RewritePatternSet &pattern, bool runOnce)
Definition: SCFToCalyx.cpp:1246
CalyxOps.h
circt::scftocalyx::SCFToCalyxPass::addOncePattern
void addOncePattern(SmallVectorImpl< LoweringPattern > &patterns, PatternArgs &&...args)
'Once' patterns are expected to take an additional LogicalResult& argument, to forward their result s...
Definition: SCFToCalyx.cpp:1229
circt::calyx::matchConstantOp
bool matchConstantOp(Operation *op, APInt &value)
Definition: CalyxLoweringUtils.cpp:115
circt::calyx::MemoryInterface
Definition: CalyxLoweringUtils.h:97
circt::scftocalyx::BuildWhileGroups
In BuildWhileGroups, a register is created for each iteration argumenet of the while op.
Definition: SCFToCalyx.cpp:817
circt::scftocalyx::BuildOpGroups::buildLibraryOp
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:220
circt::scftocalyx::SCFToCalyxPass::LoweringPattern
Definition: SCFToCalyx.cpp:1169
circt::calyx::appendPortsForExternalMemref
void appendPortsForExternalMemref(PatternRewriter &rewriter, StringRef memName, Value memref, unsigned memoryID, SmallVectorImpl< calyx::PortInfo > &inPorts, SmallVectorImpl< calyx::PortInfo > &outPorts)
Definition: CalyxLoweringUtils.cpp:31
circt::scftocalyx::SCFToCalyxPass::LoweringPattern::strategy
Strategy strategy
Definition: SCFToCalyx.cpp:1172
circt::calyx::handleZeroWidth
unsigned handleZeroWidth(int64_t dim)
Definition: CalyxHelpers.cpp:75
seq.reg
def reg(value, clock, reset=None, reset_value=None, name=None)
Definition: seq.py:15
circt::scftocalyx::ScfWhileOp::getConditionBlock
Block * getConditionBlock() override
Definition: SCFToCalyx.cpp:58
HWOps.h
circt::calyx::WhileOpInterface::getOperation
T getOperation()
Definition: CalyxLoweringUtils.h:148
circt::calyx::noStoresToMemory
bool noStoresToMemory(Value memoryReference)
Definition: CalyxLoweringUtils.cpp:125
circt::calyx::MemoryPortsImpl::writeData
Value writeData
Definition: CalyxLoweringUtils.h:89
circt::calyx::Output
@ Output
Definition: CalyxOps.h:63
mlir::arith
Definition: Passes.h:34
circt::scftocalyx::Scheduleable
std::variant< calyx::GroupOp, WhileScheduleable > Scheduleable
A variant of types representing scheduleable operations.
Definition: SCFToCalyx.cpp:82
circt::scftocalyx::ScfWhileOp::getBound
Optional< uint64_t > getBound() override
Definition: SCFToCalyx.cpp:66
circt::scftocalyx::BuildOpGroups::assignAddressPorts
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:278
circt::scftocalyx::buildAllocOp
static LogicalResult buildAllocOp(ComponentLoweringState &componentState, PatternRewriter &rewriter, TAllocOp allocOp)
Definition: SCFToCalyx.cpp:432
circt::calyx::MemoryPortsImpl
Definition: CalyxLoweringUtils.h:86
circt::calyx::ModuleOpConversion
Definition: CalyxLoweringUtils.h:483
WhileOp
circt::calyx::WhileOpInterface
Definition: CalyxLoweringUtils.h:140
circt::scftocalyx::BuildOpGroups::partiallyLowerFuncToComp
LogicalResult partiallyLowerFuncToComp(FuncOp funcOp, PatternRewriter &rewriter) const override
Definition: SCFToCalyx.cpp:106
circt::calyx::MemoryInterface::addrPorts
ValueRange addrPorts()
Definition: CalyxLoweringUtils.cpp:194
circt::calyx::addMandatoryComponentPorts
void addMandatoryComponentPorts(PatternRewriter &rewriter, SmallVectorImpl< calyx::PortInfo > &ports)
Definition: CalyxHelpers.cpp:46
circt::calyx::ComponentLoweringStateInterface::getComponentOp
calyx::ComponentOp getComponentOp()
Returns the calyx::ComponentOp associated with this lowering state.
Definition: CalyxLoweringUtils.cpp:217
mlir::func
Definition: DependenceAnalysis.h:23
CalyxHelpers.h
mlir::OpRewritePattern
Definition: LLVM.h:172
circt::calyx::MemoryPortsImpl::readData
Value readData
Definition: CalyxLoweringUtils.h:87
circt::calyx::MemoryPortsImpl::addrPorts
SmallVector< Value > addrPorts
Definition: CalyxLoweringUtils.h:90
circt::scftocalyx::BuildControl::scheduleBasicBlock
LogicalResult scheduleBasicBlock(PatternRewriter &rewriter, const DenseSet< Block * > &path, mlir::Block *parentCtrlBlock, mlir::Block *block) const
Sequentially schedules the groups that registered themselves with 'block'.
Definition: SCFToCalyx.cpp:926
circt::calyx::MultipleGroupDonePattern
When building groups which contain accesses to multiple sequential components, a group_done op is cre...
Definition: CalyxLoweringUtils.h:569
circt::calyx::ComponentLoweringStateInterface::getUniqueName
std::string getUniqueName(StringRef prefix)
Returns a unique name within compOp with the provided prefix.
Definition: CalyxLoweringUtils.cpp:244
circt::calyx::getCiderSourceLocationMetadata
WalkResult getCiderSourceLocationMetadata(calyx::ComponentOp component, SmallVectorImpl< Attribute > &sourceLocations)
Definition: CalyxLoweringUtils.cpp:96
circt::calyx::ComponentLoweringStateInterface::setFuncOpResultMapping
void setFuncOpResultMapping(const DenseMap< unsigned, unsigned > &mapping)
Assign a mapping between the source funcOp result indices and the corresponding output port indices o...
Definition: CalyxLoweringUtils.cpp:308
circt::scftocalyx::SCFToCalyxPass::addGreedyPattern
void addGreedyPattern(SmallVectorImpl< LoweringPattern > &patterns, PatternArgs &&...args)
Definition: SCFToCalyx.cpp:1238
CalyxLoweringUtils.h
circt::scftocalyx::WhileScheduleable
Definition: SCFToCalyx.cpp:73
circt::scftocalyx::CleanupFuncOps
Erases FuncOp operations.
Definition: SCFToCalyx.cpp:1123
circt::scftocalyx::FuncOpConversion
Creates a new Calyx component for each FuncOp in the program.
Definition: SCFToCalyx.cpp:705
circt::scftocalyx::WhileScheduleable::whileOp
ScfWhileOp whileOp
While operation to schedule.
Definition: SCFToCalyx.cpp:75
circt::calyx::createRegister
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.
Definition: CalyxHelpers.cpp:22
circt::scftocalyx::ComponentLoweringState::ComponentLoweringState
ComponentLoweringState(calyx::ComponentOp component)
Definition: SCFToCalyx.cpp:92
circt::scftocalyx::LateSSAReplacement::partiallyLowerFuncToComp
LogicalResult partiallyLowerFuncToComp(FuncOp funcOp, PatternRewriter &) const override
Definition: SCFToCalyx.cpp:1089
circt::scftocalyx::InlineExecuteRegionOpPattern::matchAndRewrite
LogicalResult matchAndRewrite(scf::ExecuteRegionOp execOp, PatternRewriter &rewriter) const override
Definition: SCFToCalyx.cpp:664
circt::scftocalyx::BuildControl
Builds a control schedule by traversing the CFG of the function and associating this with the previou...
Definition: SCFToCalyx.cpp:909
circt::scftocalyx::BuildControl::schedulePath
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 ...
Definition: SCFToCalyx.cpp:980
circt::calyx::ComponentLoweringStateInterface
Definition: CalyxLoweringUtils.h:278
circt::scftocalyx::ComponentLoweringState
Handles the current state of lowering of a Calyx component.
Definition: SCFToCalyx.cpp:87
circt::scftocalyx::BuildOpGroups::buildLibraryBinaryPipeOp
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:239
circt::calyx::direction::get
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:39
circt::scftocalyx::BuildOpGroups::createGroupForOp
TGroupOp createGroupForOp(PatternRewriter &rewriter, Operation *op) const
Creates a group named by the basic block which the input op resides in.
Definition: SCFToCalyx.cpp:227
SCFToCalyx.h
circt::calyx::FuncOpPartialLoweringPattern
FuncOpPartialLoweringPatterns are patterns which intend to match on FuncOps and then perform their ow...
Definition: CalyxLoweringUtils.h:497
circt::scftocalyx::BuildOpGroups::buildLibraryOp
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:176
circt::scftocalyx::FuncOpConversion::partiallyLowerFuncToComp
LogicalResult partiallyLowerFuncToComp(FuncOp funcOp, PatternRewriter &rewriter) const override
Definition: SCFToCalyx.cpp:709
circt::scftocalyx::InlineExecuteRegionOpPattern
Inlines Calyx ExecuteRegionOp operations within their parent blocks.
Definition: SCFToCalyx.cpp:660
circt::scftocalyx::BuildControl::buildWhileCtrlOp
calyx::WhileOp buildWhileCtrlOp(ScfWhileOp whileOp, SmallVector< calyx::GroupOp > initGroups, PatternRewriter &rewriter) const
Definition: SCFToCalyx.cpp:1060
circt::calyx::SchedulerInterface
Holds common utilities used for scheduling when lowering to Calyx.
Definition: CalyxLoweringUtils.h:159
circt::calyx::ComponentLoweringStateInterface::registerMemoryInterface
void registerMemoryInterface(Value memref, const calyx::MemoryInterface &memoryInterface)
Registers a memory interface as being associated with a memory identified by 'memref'.
Definition: CalyxLoweringUtils.cpp:281
circt::calyx::buildAssignmentsForRegisterWrite
void buildAssignmentsForRegisterWrite(OpBuilder &builder, calyx::GroupOp groupOp, calyx::ComponentOp componentOp, calyx::RegisterOp &reg, Value inputValue)
Creates register assignment operations within the provided groupOp.
Definition: CalyxLoweringUtils.cpp:144
circt::scftocalyx::ScfWhileOp::getBodyBlock
Block * getBodyBlock() override
Definition: SCFToCalyx.cpp:56
circt::calyx::Input
@ Input
Definition: CalyxOps.h:63
circt::scftocalyx::BuildControl::buildCFGControl
LogicalResult buildCFGControl(DenseSet< Block * > path, PatternRewriter &rewriter, mlir::Block *parentCtrlBlock, mlir::Block *preBlock, mlir::Block *block) const
Definition: SCFToCalyx.cpp:996
circt::scftocalyx::SCFToCalyxPass
Definition: SCFToCalyx.cpp:1137
circt::scftocalyx::ScfWhileOp::getConditionValue
Value getConditionValue() override
Definition: SCFToCalyx.cpp:62
circt::scftocalyx::SCFToCalyxPass::SCFToCalyxPass
SCFToCalyxPass()
Definition: SCFToCalyx.cpp:1139
circt::calyx::PortInfo
This holds information about the port for either a Component or Cell.
Definition: CalyxOps.h:76
circt::scftocalyx::BuildControl::partiallyLowerFuncToComp
LogicalResult partiallyLowerFuncToComp(FuncOp funcOp, PatternRewriter &rewriter) const override
Definition: SCFToCalyx.cpp:913
circt::scftocalyx::ScfWhileOp::ScfWhileOp
ScfWhileOp(scf::WhileOp op)
Definition: SCFToCalyx.cpp:49
circt::scftocalyx::SCFToCalyxPass::LoweringPattern::Strategy
Strategy
Definition: SCFToCalyx.cpp:1170
circt::calyx::NonTerminatingGroupDonePattern
GroupDoneOp's are terminator operations and should therefore be the last operator in a group.
Definition: CalyxLoweringUtils.h:558
circt::scftocalyx::CleanupFuncOps::partiallyLowerFuncToComp
LogicalResult partiallyLowerFuncToComp(FuncOp funcOp, PatternRewriter &rewriter) const override
Definition: SCFToCalyx.cpp:1127
circt::calyx::createConstant
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.
Definition: CalyxHelpers.cpp:30
circt::calyx::MemoryPortsImpl::writeEn
Value writeEn
Definition: CalyxLoweringUtils.h:91
circt::scftocalyx::SCFToCalyxPass::setTopLevelFunction
LogicalResult setTopLevelFunction(mlir::ModuleOp moduleOp, std::string &topLevelFunction)
Definition: SCFToCalyx.cpp:1143
circt::scftocalyx::BuildOpGroups
Iterate through the operations of a source function and instantiate components or primitives based on...
Definition: SCFToCalyx.cpp:102
mlir::cf
Definition: PassDetail.h:22
circt::scftocalyx::SCFToCalyxPass::createProgram
LogicalResult createProgram(StringRef topLevelFunction, calyx::ProgramOp *programOpOut)
Creates a new Calyx program with the contents of the source module inlined within.
Definition: SCFToCalyx.cpp:1180
circt::scftocalyx::SCFToCalyxPass::LoweringPattern::pattern
RewritePatternSet pattern
Definition: SCFToCalyx.cpp:1171
circt
Definition: DependenceAnalysis.h:28