CIRCT  19.0.0git
CalyxLoweringUtils.h
Go to the documentation of this file.
1 //===- CalyxLoweringUtils.h - Calyx lowering utility methods ----*- 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 header file defines various lowering utility methods for converting to
10 // and from Calyx programs.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef CIRCT_DIALECT_CALYX_CALYXLOWERINGUTILS_H
15 #define CIRCT_DIALECT_CALYX_CALYXLOWERINGUTILS_H
16 
20 #include "circt/Support/LLVM.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/PatternMatch.h"
28 #include "llvm/ADT/SmallPtrSet.h"
29 #include "llvm/ADT/TypeSwitch.h"
30 
31 #include <variant>
32 
33 namespace circt {
34 namespace calyx {
35 
36 void appendPortsForExternalMemref(PatternRewriter &rewriter, StringRef memName,
37  Value memref, unsigned memoryID,
38  SmallVectorImpl<calyx::PortInfo> &inPorts,
39  SmallVectorImpl<calyx::PortInfo> &outPorts);
40 
41 // Walks the control of this component, and appends source information for leaf
42 // nodes. It also appends a position attribute that connects the source location
43 // metadata to the corresponding control operation.
44 WalkResult
45 getCiderSourceLocationMetadata(calyx::ComponentOp component,
46  SmallVectorImpl<Attribute> &sourceLocations);
47 
48 // Tries to match a constant value defined by op. If the match was
49 // successful, returns true and binds the constant to 'value'. If unsuccessful,
50 // the value is unmodified.
51 bool matchConstantOp(Operation *op, APInt &value);
52 
53 // Returns true if there exists only a single memref::LoadOp which loads from
54 // the memory referenced by loadOp.
55 bool singleLoadFromMemory(Value memoryReference);
56 
57 // Returns true if there are no memref::StoreOp uses with the referenced
58 // memory.
59 bool noStoresToMemory(Value memoryReference);
60 
61 // Get the index'th output port of compOp.
62 Value getComponentOutput(calyx::ComponentOp compOp, unsigned outPortIdx);
63 
64 // If the provided type is an index type, converts it to i32, else, returns the
65 // unmodified type.
66 Type convIndexType(OpBuilder &builder, Type type);
67 
68 // Creates a new calyx::CombGroupOp or calyx::GroupOp group within compOp.
69 template <typename TGroup>
70 TGroup createGroup(OpBuilder &builder, calyx::ComponentOp compOp, Location loc,
71  Twine uniqueName) {
72  mlir::IRRewriter::InsertionGuard guard(builder);
73  builder.setInsertionPointToEnd(compOp.getWiresOp().getBodyBlock());
74  return builder.create<TGroup>(loc, uniqueName.str());
75 }
76 
77 /// Creates register assignment operations within the provided groupOp.
78 /// The component operation will house the constants.
80  calyx::GroupOp groupOp,
81  calyx::ComponentOp componentOp,
82  calyx::RegisterOp &reg, Value inputValue);
83 
84 // A structure representing a set of ports which act as a memory interface for
85 // external memories.
87  std::optional<Value> readData;
88  std::optional<Value> readOrContentEn;
89  std::optional<Value> writeData;
90  std::optional<Value> writeEn;
91  std::optional<Value> done;
92  SmallVector<Value> addrPorts;
93  std::optional<bool> isContentEn;
94 };
95 
96 // Represents the interface of memory in Calyx. The various lowering passes
97 // are agnostic wrt. whether working with a calyx::MemoryOp (internally
98 // allocated memory) or MemoryPortsImpl (external memory).
101  explicit MemoryInterface(const MemoryPortsImpl &ports);
102  explicit MemoryInterface(calyx::MemoryOp memOp);
103  explicit MemoryInterface(calyx::SeqMemoryOp memOp);
104 
105  // Getter methods for each memory interface port.
106  Value readData();
107  Value readEn();
108  Value contentEn();
109  Value writeData();
110  Value writeEn();
111  Value done();
112  std::optional<Value> readDataOpt();
113  std::optional<Value> readEnOpt();
114  std::optional<Value> contentEnOpt();
115  std::optional<Value> writeDataOpt();
116  std::optional<Value> writeEnOpt();
117  std::optional<Value> doneOpt();
118  ValueRange addrPorts();
119 
120 private:
121  std::variant<calyx::MemoryOp, calyx::SeqMemoryOp, MemoryPortsImpl> impl;
122 };
123 
124 // A common interface for any loop operation that needs to be lowered to Calyx.
126 public:
128 
129  // Returns the arguments to this loop operation.
130  virtual Block::BlockArgListType getBodyArgs() = 0;
131 
132  // Returns body of this loop operation.
133  virtual Block *getBodyBlock() = 0;
134 
135  // Returns the location of the loop interface.
136  virtual Location getLoc() = 0;
137 
138  // Returns the number of iterations the loop will conduct if known.
139  virtual std::optional<int64_t> getBound() = 0;
140 };
141 
142 // A common interface for loop operations that have conditionals (e.g., while
143 // loops) that need to be lowered to Calyx.
145 public:
146  // Returns the Block in which the condition exists.
147  virtual Block *getConditionBlock() = 0;
148 
149  // Returns the condition as a Value.
150  virtual Value getConditionValue() = 0;
151 };
152 
153 // Provides an interface for the control flow `while` operation across different
154 // dialects.
155 template <typename T>
157  static_assert(std::is_convertible_v<T, Operation *>);
158 
159 public:
160  explicit WhileOpInterface(T op) : impl(op) {}
161  explicit WhileOpInterface(Operation *op) : impl(dyn_cast_or_null<T>(op)) {}
162 
163  // Returns the operation.
164  T getOperation() { return impl; }
165 
166  // Returns the source location of the operation.
167  Location getLoc() override { return impl->getLoc(); }
168 
169 private:
170  T impl;
171 };
172 
173 // Provides an interface for the control flow `forOp` operation across different
174 // dialects.
175 template <typename T>
177  static_assert(std::is_convertible_v<T, Operation *>);
178 
179 public:
180  explicit RepeatOpInterface(T op) : impl(op) {}
181  explicit RepeatOpInterface(Operation *op) : impl(dyn_cast_or_null<T>(op)) {}
182 
183  // Returns the operation.
184  T getOperation() { return impl; }
185 
186  // Returns the source location of the operation.
187  Location getLoc() override { return impl->getLoc(); }
188 
189 private:
190  T impl;
191 };
192 
193 /// Holds common utilities used for scheduling when lowering to Calyx.
194 template <typename T>
196 public:
197  /// Register 'scheduleable' as being generated through lowering 'block'.
198  ///
199  /// TODO(mortbopet): Add a post-insertion check to ensure that the use-def
200  /// ordering invariant holds for the groups. When the control schedule is
201  /// generated, scheduleables within a block are emitted sequentially based on
202  /// the order that this function was called during conversion.
203  ///
204  /// Currently, we assume this to always be true. Walking the FuncOp IR implies
205  /// sequential iteration over operations within basic blocks.
206  void addBlockScheduleable(mlir::Block *block, const T &scheduleable) {
207  blockScheduleables[block].push_back(scheduleable);
208  }
209 
210  /// Returns an ordered list of schedulables which registered themselves to be
211  /// a result of lowering the block in the source program. The list order
212  /// follows def-use chains between the scheduleables in the block.
213  SmallVector<T> getBlockScheduleables(mlir::Block *block) {
214  if (auto it = blockScheduleables.find(block);
215  it != blockScheduleables.end())
216  return it->second;
217  /// In cases of a block resulting in purely combinational logic, no
218  /// scheduleables registered themselves with the block.
219  return {};
220  }
221 
222 private:
223  /// BlockScheduleables is a list of scheduleables that should be
224  /// sequentially executed when executing the associated basic block.
225  DenseMap<mlir::Block *, SmallVector<T>> blockScheduleables;
226 };
227 
228 //===----------------------------------------------------------------------===//
229 // Lowering state classes
230 //===----------------------------------------------------------------------===//
231 
232 // Handles state during the lowering of a loop. It will be used for
233 // several lowering patterns.
234 template <typename Loop>
236  static_assert(std::is_base_of_v<BasicLoopInterface, Loop>);
237 
238 public:
240 
241  /// Register reg as being the idx'th iter_args register for 'op'.
242  void addLoopIterReg(Loop op, calyx::RegisterOp reg, unsigned idx) {
243  assert(loopIterRegs[op.getOperation()].count(idx) == 0 &&
244  "A register was already registered for the given loop iter_arg "
245  "index");
246  assert(idx < op.getBodyArgs().size());
247  loopIterRegs[op.getOperation()][idx] = reg;
248  }
249 
250  /// Return a mapping of block argument indices to block argument.
251  calyx::RegisterOp getLoopIterReg(Loop op, unsigned idx) {
252  auto iterRegs = getLoopIterRegs(op);
253  auto it = iterRegs.find(idx);
254  assert(it != iterRegs.end() &&
255  "No iter arg register set for the provided index");
256  return it->second;
257  }
258 
259  /// Return a mapping of block argument indices to block argument.
260  const DenseMap<unsigned, calyx::RegisterOp> &getLoopIterRegs(Loop op) {
261  return loopIterRegs[op.getOperation()];
262  }
263 
264  /// Registers grp to be the loop latch group of `op`.
265  void setLoopLatchGroup(Loop op, calyx::GroupOp group) {
266  Operation *operation = op.getOperation();
267  assert(loopLatchGroups.count(operation) == 0 &&
268  "A latch group was already set for this loopOp");
269  loopLatchGroups[operation] = group;
270  }
271 
272  /// Retrieve the loop latch group registered for `op`.
273  calyx::GroupOp getLoopLatchGroup(Loop op) {
274  auto it = loopLatchGroups.find(op.getOperation());
275  assert(it != loopLatchGroups.end() &&
276  "No loop latch group was set for this loopOp");
277  return it->second;
278  }
279 
280  /// Registers groups to be the loop init groups of `op`.
281  void setLoopInitGroups(Loop op, SmallVector<calyx::GroupOp> groups) {
282  Operation *operation = op.getOperation();
283  assert(loopInitGroups.count(operation) == 0 &&
284  "Init group(s) was already set for this loopOp");
285  loopInitGroups[operation] = std::move(groups);
286  }
287 
288  /// Retrieve the loop init groups registered for `op`.
289  SmallVector<calyx::GroupOp> getLoopInitGroups(Loop op) {
290  auto it = loopInitGroups.find(op.getOperation());
291  assert(it != loopInitGroups.end() &&
292  "No init group(s) was set for this loopOp");
293  return it->second;
294  }
295 
296  /// Creates a new group that assigns the 'ops' values to the iter arg
297  /// registers of the loop operation.
298  calyx::GroupOp buildLoopIterArgAssignments(OpBuilder &builder, Loop op,
299  calyx::ComponentOp componentOp,
300  Twine uniqueSuffix,
301  MutableArrayRef<OpOperand> ops) {
302  /// Pass iteration arguments through registers. This follows closely
303  /// to what is done for branch ops.
304  std::string groupName = "assign_" + uniqueSuffix.str();
305  auto groupOp = calyx::createGroup<calyx::GroupOp>(builder, componentOp,
306  op.getLoc(), groupName);
307  /// Create register assignment for each iter_arg. a calyx::GroupDone signal
308  /// is created for each register. These will be &'ed together in
309  /// MultipleGroupDonePattern.
310  for (OpOperand &arg : ops) {
311  auto reg = getLoopIterReg(op, arg.getOperandNumber());
312  buildAssignmentsForRegisterWrite(builder, groupOp, componentOp, reg,
313  arg.get());
314  }
315  return groupOp;
316  }
317 
318 private:
319  /// A mapping from loop ops to iteration argument registers.
320  DenseMap<Operation *, DenseMap<unsigned, calyx::RegisterOp>> loopIterRegs;
321 
322  /// A loop latch group is a group that should be sequentially executed when
323  /// finishing a loop body. The execution of this group will write the
324  /// yield'ed loop body values to the iteration argument registers.
325  DenseMap<Operation *, calyx::GroupOp> loopLatchGroups;
326 
327  /// Loop init groups are to be scheduled before the while operation. These
328  /// groups should set the initial value(s) of the loop init_args register(s).
329  DenseMap<Operation *, SmallVector<calyx::GroupOp>> loopInitGroups;
330 };
331 
332 // Handles state during the lowering of a Calyx component. This provides common
333 // tools for converting to the Calyx ComponentOp.
335 public:
336  ComponentLoweringStateInterface(calyx::ComponentOp component);
337 
339 
340  /// Returns the calyx::ComponentOp associated with this lowering state.
341  calyx::ComponentOp getComponentOp();
342 
343  /// Register reg as being the idx'th argument register for block. This is
344  /// necessary for the `BuildBBReg` pass.
345  void addBlockArgReg(Block *block, calyx::RegisterOp reg, unsigned idx);
346 
347  /// Return a mapping of block argument indices to block argument registers.
348  /// This is necessary for the `BuildBBReg` pass.
349  const DenseMap<unsigned, calyx::RegisterOp> &getBlockArgRegs(Block *block);
350 
351  /// Register 'grp' as a group which performs block argument
352  /// register transfer when transitioning from basic block 'from' to 'to'.
353  void addBlockArgGroup(Block *from, Block *to, calyx::GroupOp grp);
354 
355  /// Returns a list of groups to be evaluated to perform the block argument
356  /// register assignments when transitioning from basic block 'from' to 'to'.
357  ArrayRef<calyx::GroupOp> getBlockArgGroups(Block *from, Block *to);
358 
359  /// Returns a unique name within compOp with the provided prefix.
360  std::string getUniqueName(StringRef prefix);
361 
362  /// Returns a unique name associated with a specific operation.
363  StringRef getUniqueName(Operation *op);
364 
365  /// Registers a unique name for a given operation using a provided prefix.
366  void setUniqueName(Operation *op, StringRef prefix);
367 
368  /// Register value v as being evaluated when scheduling group.
369  void registerEvaluatingGroup(Value v, calyx::GroupInterface group);
370 
371  /// Register reg as being the idx'th return value register.
372  void addReturnReg(calyx::RegisterOp reg, unsigned idx);
373 
374  /// Returns the idx'th return value register.
375  calyx::RegisterOp getReturnReg(unsigned idx);
376 
377  /// Registers a memory interface as being associated with a memory identified
378  /// by 'memref'.
379  void registerMemoryInterface(Value memref,
380  const calyx::MemoryInterface &memoryInterface);
381 
382  /// Returns the memory interface registered for the given memref.
384 
385  /// If v is an input to any memory registered within this component, returns
386  /// the memory. If not, returns null.
387  std::optional<calyx::MemoryInterface> isInputPortOfMemory(Value v);
388 
389  /// Assign a mapping between the source funcOp result indices and the
390  /// corresponding output port indices of this componentOp.
391  void setFuncOpResultMapping(const DenseMap<unsigned, unsigned> &mapping);
392 
393  /// Get the output port index of this component for which the funcReturnIdx of
394  /// the original function maps to.
395  unsigned getFuncOpResultMapping(unsigned funcReturnIdx);
396 
397  /// The instance is obtained from the name of the callee.
398  InstanceOp getInstance(StringRef calleeName);
399 
400  /// Put the name of the callee and the instance of the call into map.
401  void addInstance(StringRef calleeName, InstanceOp instanceOp);
402 
403  /// Return the group which evaluates the value v. Optionally, caller may
404  /// specify the expected type of the group.
405  template <typename TGroupOp = calyx::GroupInterface>
406  TGroupOp getEvaluatingGroup(Value v) {
407  auto it = valueGroupAssigns.find(v);
408  assert(it != valueGroupAssigns.end() && "No group evaluating value!");
409  if constexpr (std::is_same_v<TGroupOp, calyx::GroupInterface>)
410  return it->second;
411  else {
412  auto group = dyn_cast<TGroupOp>(it->second.getOperation());
413  assert(group && "Actual group type differed from expected group type");
414  return group;
415  }
416  }
417 
418  template <typename TLibraryOp>
419  TLibraryOp getNewLibraryOpInstance(OpBuilder &builder, Location loc,
420  TypeRange resTypes) {
421  mlir::IRRewriter::InsertionGuard guard(builder);
422  Block *body = component.getBodyBlock();
423  builder.setInsertionPoint(body, body->begin());
424  auto name = TLibraryOp::getOperationName().split(".").second;
425  return builder.create<TLibraryOp>(loc, getUniqueName(name), resTypes);
426  }
427 
428 private:
429  /// The component which this lowering state is associated to.
430  calyx::ComponentOp component;
431 
432  /// A mapping from blocks to block argument registers.
433  DenseMap<Block *, DenseMap<unsigned, calyx::RegisterOp>> blockArgRegs;
434 
435  /// Block arg groups is a list of groups that should be sequentially
436  /// executed when passing control from the source to destination block.
437  /// Block arg groups are executed before blockScheduleables (akin to a
438  /// phi-node).
439  DenseMap<Block *, DenseMap<Block *, SmallVector<calyx::GroupOp>>>
441 
442  /// A mapping of string prefixes and the current uniqueness counter for that
443  /// prefix. Used to generate unique names.
444  std::map<std::string, unsigned> prefixIdMap;
445 
446  /// A mapping from Operations and previously assigned unique name of the op.
447  std::map<Operation *, std::string> opNames;
448 
449  /// A mapping between SSA values and the groups which assign them.
450  DenseMap<Value, calyx::GroupInterface> valueGroupAssigns;
451 
452  /// A mapping from return value indexes to return value registers.
453  DenseMap<unsigned, calyx::RegisterOp> returnRegs;
454 
455  /// A mapping from memref's to their corresponding Calyx memory interface.
456  DenseMap<Value, calyx::MemoryInterface> memories;
457 
458  /// A mapping between the source funcOp result indices and the corresponding
459  /// output port indices of this componentOp.
460  DenseMap<unsigned, unsigned> funcOpResultMapping;
461 
462  /// A mapping between the callee and the instance.
463  llvm::StringMap<calyx::InstanceOp> instanceMap;
464 };
465 
466 /// An interface for conversion passes that lower Calyx programs. This handles
467 /// state during the lowering of a Calyx program.
469 public:
470  explicit CalyxLoweringState(mlir::ModuleOp module,
471  StringRef topLevelFunction);
472 
473  /// Returns the current program.
474  mlir::ModuleOp getModule();
475 
476  /// Returns the name of the top-level function in the source program.
477  StringRef getTopLevelFunction() const;
478 
479  /// Returns a meaningful name for a block within the program scope (removes
480  /// the ^ prefix from block names).
481  std::string blockName(Block *b);
482 
483  /// Returns the component lowering state associated with `op`. If not found
484  /// already found, a new mapping is added for this ComponentOp. Different
485  /// conversions may have different derived classes of the interface, so we
486  /// provided a template.
487  template <typename T = calyx::ComponentLoweringStateInterface>
488  T *getState(calyx::ComponentOp op) {
489  static_assert(std::is_convertible_v<T, ComponentLoweringStateInterface>);
490  auto it = componentStates.find(op);
491  if (it == componentStates.end()) {
492  // Create a new ComponentLoweringState for the compOp.
493  bool success;
494  std::tie(it, success) =
495  componentStates.try_emplace(op, std::make_unique<T>(op));
496  }
497 
498  return static_cast<T *>(it->second.get());
499  }
500 
501  /// Returns a meaningful name for a value within the program scope.
502  template <typename ValueOrBlock>
503  std::string irName(ValueOrBlock &v) {
504  std::string s;
505  llvm::raw_string_ostream os(s);
506  mlir::AsmState asmState(module);
507  v.printAsOperand(os, asmState);
508  return s;
509  }
510 
511 private:
512  /// The name of this top-level function.
513  StringRef topLevelFunction;
514  /// The program associated with this state.
515  mlir::ModuleOp module;
516  /// Mapping from ComponentOp to component lowering state.
517  DenseMap<Operation *, std::unique_ptr<ComponentLoweringStateInterface>>
519 };
520 
521 /// Extra state that is passed to all PartialLoweringPatterns so they can record
522 /// when they have run on an Operation, and only run once.
524  DenseMap<const mlir::RewritePattern *, SmallPtrSet<Operation *, 16>>;
525 
526 /// Base class for partial lowering passes. A partial lowering pass
527 /// modifies the root operation in place, but does not replace the root
528 /// operation.
529 /// The RewritePatternType template parameter allows for using both
530 /// OpRewritePattern (default) or OpInterfaceRewritePattern.
531 template <class OpType,
532  template <class> class RewritePatternType = OpRewritePattern>
533 class PartialLoweringPattern : public RewritePatternType<OpType> {
534 public:
535  using RewritePatternType<OpType>::RewritePatternType;
536  PartialLoweringPattern(MLIRContext *ctx, LogicalResult &resRef,
538  : RewritePatternType<OpType>(ctx), partialPatternRes(resRef),
540 
541  LogicalResult matchAndRewrite(OpType op,
542  PatternRewriter &rewriter) const override {
543  // If this pattern has been applied to this op, it should now fail to match.
544  if (patternState[this].contains(op))
545  return failure();
546 
547  // Do the actual rewrite, marking this op as updated. Because the op is
548  // marked as updated, the pattern driver will re-enqueue the op again.
549  rewriter.modifyOpInPlace(
550  op, [&] { partialPatternRes = partiallyLower(op, rewriter); });
551 
552  // Mark that this pattern has been applied to this op.
553  patternState[this].insert(op);
554 
555  return partialPatternRes;
556  }
557 
558  // Hook for subclasses to lower the op using the rewriter.
559  //
560  // Note that this call is wrapped in `modifyOpInPlace`, so any direct IR
561  // mutations that are legal to apply during a root update of op are allowed.
562  //
563  // Also note that this means the op will be re-enqueued to the greedy
564  // rewriter's worklist. A safeguard is in place to prevent patterns from
565  // running multiple times, but if the op is erased or otherwise becomes dead
566  // after the call to `partiallyLower`, there will likely be use-after-free
567  // violations. If you will erase the op, override `matchAndRewrite` directly.
568  virtual LogicalResult partiallyLower(OpType op,
569  PatternRewriter &rewriter) const = 0;
570 
571 private:
572  LogicalResult &partialPatternRes;
574 };
575 
576 /// Helper to update the top-level ModuleOp to set the entrypoing function.
577 LogicalResult applyModuleOpConversion(mlir::ModuleOp,
578  StringRef topLevelFunction);
579 
580 /// FuncOpPartialLoweringPatterns are patterns which intend to match on FuncOps
581 /// and then perform their own walking of the IR.
583  : public calyx::PartialLoweringPattern<mlir::func::FuncOp> {
584 
585 public:
587  MLIRContext *context, LogicalResult &resRef,
589  DenseMap<mlir::func::FuncOp, calyx::ComponentOp> &map,
591 
592  /// Entry point to initialize the state of this class and conduct the partial
593  /// lowering.
594  LogicalResult partiallyLower(mlir::func::FuncOp funcOp,
595  PatternRewriter &rewriter) const override final;
596 
597  /// Returns the component operation associated with the currently executing
598  /// partial lowering.
599  calyx::ComponentOp getComponent() const;
600 
601  // Returns the component state associated with the currently executing
602  // partial lowering.
603  template <typename T = ComponentLoweringStateInterface>
604  T &getState() const {
605  static_assert(
606  std::is_convertible_v<T, calyx::ComponentLoweringStateInterface>);
607  assert(
608  componentLoweringState != nullptr &&
609  "Component lowering state should be set during pattern construction");
610  return *static_cast<T *>(componentLoweringState);
611  }
612 
613  /// Return the calyx lowering state for this pattern.
615 
616  // Hook for subclasses to lower the op using the rewriter.
617  //
618  // Note that this call is wrapped in `modifyOpInPlace`, so any direct IR
619  // mutations that are legal to apply during a root update of op are allowed.
620  //
621  // Also note that this means the op will be re-enqueued to the greedy
622  // rewriter's worklist. A safeguard is in place to prevent patterns from
623  // running multiple times, but if the op is erased or otherwise becomes dead
624  // after the call to `partiallyLower`, there will likely be use-after-free
625  // violations. If you will erase the op, override `matchAndRewrite` directly.
626  virtual LogicalResult
627  partiallyLowerFuncToComp(mlir::func::FuncOp funcOp,
628  PatternRewriter &rewriter) const = 0;
629 
630 protected:
631  // A map from FuncOp to it's respective ComponentOp lowering.
632  DenseMap<mlir::func::FuncOp, calyx::ComponentOp> &functionMapping;
633 
634 private:
635  mutable ComponentOp componentOp;
638 };
639 
640 /// Converts all index-typed operations and values to i32 values.
643 
644  LogicalResult
645  partiallyLowerFuncToComp(mlir::func::FuncOp funcOp,
646  PatternRewriter &rewriter) const override;
647 };
648 
649 /// GroupDoneOp's are terminator operations and should therefore be the last
650 /// operator in a group. During group construction, we always append assignments
651 /// to the end of a group, resulting in group_done ops migrating away from the
652 /// terminator position. This pattern moves such ops to the end of their group.
654  : mlir::OpRewritePattern<calyx::GroupDoneOp> {
656 
657  LogicalResult matchAndRewrite(calyx::GroupDoneOp groupDoneOp,
658  PatternRewriter &) const override;
659 };
660 
661 /// When building groups which contain accesses to multiple sequential
662 /// components, a group_done op is created for each of these. This pattern
663 /// and's each of the group_done values into a single group_done.
666 
667  LogicalResult matchAndRewrite(calyx::GroupOp groupOp,
668  PatternRewriter &rewriter) const override;
669 };
670 
671 /// Removes calyx::CombGroupOps which are unused. These correspond to
672 /// combinational groups created during op building that, after conversion,
673 /// have either been inlined into calyx::GroupOps or are referenced by an
674 /// if/while with statement.
675 /// We do not eliminate unused calyx::GroupOps; this should never happen, and is
676 /// considered an error. In these cases, the program will be invalidated when
677 /// the Calyx verifiers execute.
678 struct EliminateUnusedCombGroups : mlir::OpRewritePattern<calyx::CombGroupOp> {
680 
681  LogicalResult matchAndRewrite(calyx::CombGroupOp combGroupOp,
682  PatternRewriter &rewriter) const override;
683 };
684 
685 /// This pass recursively inlines use-def chains of combinational logic (from
686 /// non-stateful groups) into groups referenced in the control schedule.
688  : public calyx::PartialLoweringPattern<calyx::GroupInterface,
689  mlir::OpInterfaceRewritePattern> {
690 public:
691  InlineCombGroups(MLIRContext *context, LogicalResult &resRef,
694 
695  LogicalResult partiallyLower(calyx::GroupInterface originGroup,
696  PatternRewriter &rewriter) const override;
697 
698 private:
699  void
700  recurseInlineCombGroups(PatternRewriter &rewriter,
702  llvm::SmallSetVector<Operation *, 8> &inlinedGroups,
703  calyx::GroupInterface originGroup,
704  calyx::GroupInterface recGroup, bool doInline) const;
705 
707 };
708 
709 /// This pass rewrites memory accesses that have a width mismatch. Such
710 /// mismatches are due to index types being assumed 32-bit wide due to the lack
711 /// of a width inference pass.
713  : public calyx::PartialLoweringPattern<calyx::AssignOp> {
714 public:
715  RewriteMemoryAccesses(MLIRContext *context, LogicalResult &resRef,
718  : PartialLoweringPattern(context, resRef, patternState), cls(cls) {}
719 
720  LogicalResult partiallyLower(calyx::AssignOp assignOp,
721  PatternRewriter &rewriter) const override;
722 
723 private:
725 };
726 
727 /// Builds registers for each block argument in the program.
730 
731  LogicalResult
732  partiallyLowerFuncToComp(mlir::func::FuncOp funcOp,
733  PatternRewriter &rewriter) const override;
734 };
735 
736 /// Builds registers for the return statement of the program and constant
737 /// assignments to the component return value.
740 
741  LogicalResult
742  partiallyLowerFuncToComp(mlir::func::FuncOp funcOp,
743  PatternRewriter &rewriter) const override;
744 };
745 
746 /// Builds instance for the calyx.invoke and calyx.group in order to initialize
747 /// the instance.
750 
751  LogicalResult
752  partiallyLowerFuncToComp(mlir::func::FuncOp funcOp,
753  PatternRewriter &rewriter) const override;
754  ComponentOp getCallComponent(mlir::func::CallOp callOp) const;
755 };
756 
757 } // namespace calyx
758 } // namespace circt
759 
760 #endif // CIRCT_DIALECT_CALYX_CALYXLOWERINGUTILS_H
assert(baseType &&"element must be base type")
Builder builder
virtual std::optional< int64_t > getBound()=0
virtual Location getLoc()=0
virtual Block::BlockArgListType getBodyArgs()=0
virtual Block * getBodyBlock()=0
Builds registers for each block argument in the program.
LogicalResult partiallyLowerFuncToComp(mlir::func::FuncOp funcOp, PatternRewriter &rewriter) const override
Builds instance for the calyx.invoke and calyx.group in order to initialize the instance.
LogicalResult partiallyLowerFuncToComp(mlir::func::FuncOp funcOp, PatternRewriter &rewriter) const override
ComponentOp getCallComponent(mlir::func::CallOp callOp) const
Builds registers for the return statement of the program and constant assignments to the component re...
LogicalResult partiallyLowerFuncToComp(mlir::func::FuncOp funcOp, PatternRewriter &rewriter) const override
An interface for conversion passes that lower Calyx programs.
std::string irName(ValueOrBlock &v)
Returns a meaningful name for a value within the program scope.
mlir::ModuleOp getModule()
Returns the current program.
CalyxLoweringState(mlir::ModuleOp module, StringRef topLevelFunction)
DenseMap< Operation *, std::unique_ptr< ComponentLoweringStateInterface > > componentStates
Mapping from ComponentOp to component lowering state.
T * getState(calyx::ComponentOp op)
Returns the component lowering state associated with op.
std::string blockName(Block *b)
Returns a meaningful name for a block within the program scope (removes the ^ prefix from block names...
StringRef getTopLevelFunction() const
Returns the name of the top-level function in the source program.
StringRef topLevelFunction
The name of this top-level function.
mlir::ModuleOp module
The program associated with this state.
const DenseMap< unsigned, calyx::RegisterOp > & getBlockArgRegs(Block *block)
Return a mapping of block argument indices to block argument registers.
void setFuncOpResultMapping(const DenseMap< unsigned, unsigned > &mapping)
Assign a mapping between the source funcOp result indices and the corresponding output port indices o...
DenseMap< Value, calyx::MemoryInterface > memories
A mapping from memref's to their corresponding Calyx memory interface.
TGroupOp getEvaluatingGroup(Value v)
Return the group which evaluates the value v.
void addReturnReg(calyx::RegisterOp reg, unsigned idx)
Register reg as being the idx'th return value register.
calyx::MemoryInterface getMemoryInterface(Value memref)
Returns the memory interface registered for the given memref.
llvm::StringMap< calyx::InstanceOp > instanceMap
A mapping between the callee and the instance.
DenseMap< Block *, DenseMap< Block *, SmallVector< calyx::GroupOp > > > blockArgGroups
Block arg groups is a list of groups that should be sequentially executed when passing control from t...
void setUniqueName(Operation *op, StringRef prefix)
Registers a unique name for a given operation using a provided prefix.
calyx::RegisterOp getReturnReg(unsigned idx)
Returns the idx'th return value register.
std::string getUniqueName(StringRef prefix)
Returns a unique name within compOp with the provided prefix.
calyx::ComponentOp component
The component which this lowering state is associated to.
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.
void addBlockArgReg(Block *block, calyx::RegisterOp reg, unsigned idx)
Register reg as being the idx'th argument register for block.
InstanceOp getInstance(StringRef calleeName)
The instance is obtained from the name of the callee.
unsigned getFuncOpResultMapping(unsigned funcReturnIdx)
Get the output port index of this component for which the funcReturnIdx of the original function maps...
DenseMap< unsigned, unsigned > funcOpResultMapping
A mapping between the source funcOp result indices and the corresponding output port indices of this ...
void addInstance(StringRef calleeName, InstanceOp instanceOp)
Put the name of the callee and the instance of the call into map.
DenseMap< Value, calyx::GroupInterface > valueGroupAssigns
A mapping between SSA values and the groups which assign them.
std::map< std::string, unsigned > prefixIdMap
A mapping of string prefixes and the current uniqueness counter for that prefix.
ArrayRef< calyx::GroupOp > getBlockArgGroups(Block *from, Block *to)
Returns a list of groups to be evaluated to perform the block argument register assignments when tran...
ComponentLoweringStateInterface(calyx::ComponentOp component)
void registerEvaluatingGroup(Value v, calyx::GroupInterface group)
Register value v as being evaluated when scheduling group.
std::optional< calyx::MemoryInterface > isInputPortOfMemory(Value v)
If v is an input to any memory registered within this component, returns the memory.
void addBlockArgGroup(Block *from, Block *to, calyx::GroupOp grp)
Register 'grp' as a group which performs block argument register transfer when transitioning from bas...
TLibraryOp getNewLibraryOpInstance(OpBuilder &builder, Location loc, TypeRange resTypes)
DenseMap< Block *, DenseMap< unsigned, calyx::RegisterOp > > blockArgRegs
A mapping from blocks to block argument registers.
DenseMap< unsigned, calyx::RegisterOp > returnRegs
A mapping from return value indexes to return value registers.
std::map< Operation *, std::string > opNames
A mapping from Operations and previously assigned unique name of the op.
Converts all index-typed operations and values to i32 values.
LogicalResult partiallyLowerFuncToComp(mlir::func::FuncOp funcOp, PatternRewriter &rewriter) const override
FuncOpPartialLoweringPatterns are patterns which intend to match on FuncOps and then perform their ow...
calyx::ComponentOp getComponent() const
Returns the component operation associated with the currently executing partial lowering.
ComponentLoweringStateInterface * componentLoweringState
DenseMap< mlir::func::FuncOp, calyx::ComponentOp > & functionMapping
virtual LogicalResult partiallyLowerFuncToComp(mlir::func::FuncOp funcOp, PatternRewriter &rewriter) const =0
CalyxLoweringState & loweringState() const
Return the calyx lowering state for this pattern.
FuncOpPartialLoweringPattern(MLIRContext *context, LogicalResult &resRef, PatternApplicationState &patternState, DenseMap< mlir::func::FuncOp, calyx::ComponentOp > &map, calyx::CalyxLoweringState &state)
LogicalResult partiallyLower(mlir::func::FuncOp funcOp, PatternRewriter &rewriter) const override final
Entry point to initialize the state of this class and conduct the partial lowering.
This pass recursively inlines use-def chains of combinational logic (from non-stateful groups) into g...
LogicalResult partiallyLower(calyx::GroupInterface originGroup, PatternRewriter &rewriter) const override
InlineCombGroups(MLIRContext *context, LogicalResult &resRef, PatternApplicationState &patternState, calyx::CalyxLoweringState &pls)
void recurseInlineCombGroups(PatternRewriter &rewriter, ComponentLoweringStateInterface &state, llvm::SmallSetVector< Operation *, 8 > &inlinedGroups, calyx::GroupInterface originGroup, calyx::GroupInterface recGroup, bool doInline) const
calyx::CalyxLoweringState & cls
virtual Value getConditionValue()=0
virtual Block * getConditionBlock()=0
calyx::GroupOp getLoopLatchGroup(Loop op)
Retrieve the loop latch group registered for op.
const DenseMap< unsigned, calyx::RegisterOp > & getLoopIterRegs(Loop op)
Return a mapping of block argument indices to block argument.
void setLoopLatchGroup(Loop op, calyx::GroupOp group)
Registers grp to be the loop latch group of op.
calyx::RegisterOp getLoopIterReg(Loop op, unsigned idx)
Return a mapping of block argument indices to block argument.
void addLoopIterReg(Loop op, calyx::RegisterOp reg, unsigned idx)
Register reg as being the idx'th iter_args register for 'op'.
void setLoopInitGroups(Loop op, SmallVector< calyx::GroupOp > groups)
Registers groups to be the loop init groups of op.
DenseMap< Operation *, DenseMap< unsigned, calyx::RegisterOp > > loopIterRegs
A mapping from loop ops to iteration argument registers.
DenseMap< Operation *, calyx::GroupOp > loopLatchGroups
A loop latch group is a group that should be sequentially executed when finishing a loop body.
calyx::GroupOp buildLoopIterArgAssignments(OpBuilder &builder, Loop op, calyx::ComponentOp componentOp, Twine uniqueSuffix, MutableArrayRef< OpOperand > ops)
Creates a new group that assigns the 'ops' values to the iter arg registers of the loop operation.
DenseMap< Operation *, SmallVector< calyx::GroupOp > > loopInitGroups
Loop init groups are to be scheduled before the while operation.
SmallVector< calyx::GroupOp > getLoopInitGroups(Loop op)
Retrieve the loop init groups registered for op.
Base class for partial lowering passes.
virtual LogicalResult partiallyLower(OpType op, PatternRewriter &rewriter) const =0
PatternApplicationState & patternState
PartialLoweringPattern(MLIRContext *ctx, LogicalResult &resRef, PatternApplicationState &patternState)
LogicalResult matchAndRewrite(OpType op, PatternRewriter &rewriter) const override
This pass rewrites memory accesses that have a width mismatch.
calyx::CalyxLoweringState & cls
LogicalResult partiallyLower(calyx::AssignOp assignOp, PatternRewriter &rewriter) const override
RewriteMemoryAccesses(MLIRContext *context, LogicalResult &resRef, PatternApplicationState &patternState, calyx::CalyxLoweringState &cls)
Holds common utilities used for scheduling when lowering to Calyx.
SmallVector< T > getBlockScheduleables(mlir::Block *block)
Returns an ordered list of schedulables which registered themselves to be a result of lowering the bl...
void addBlockScheduleable(mlir::Block *block, const T &scheduleable)
Register 'scheduleable' as being generated through lowering 'block'.
DenseMap< mlir::Block *, SmallVector< T > > blockScheduleables
BlockScheduleables is a list of scheduleables that should be sequentially executed when executing the...
TGroup createGroup(OpBuilder &builder, calyx::ComponentOp compOp, Location loc, Twine uniqueName)
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)
bool noStoresToMemory(Value memoryReference)
Value getComponentOutput(calyx::ComponentOp compOp, unsigned outPortIdx)
bool singleLoadFromMemory(Value memoryReference)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Definition: seq.py:20
Removes calyx::CombGroupOps which are unused.
LogicalResult matchAndRewrite(calyx::CombGroupOp combGroupOp, PatternRewriter &rewriter) const override
std::optional< Value > contentEnOpt()
std::variant< calyx::MemoryOp, calyx::SeqMemoryOp, MemoryPortsImpl > impl
std::optional< Value > writeDataOpt()
std::optional< Value > readEnOpt()
std::optional< Value > readDataOpt()
std::optional< Value > doneOpt()
std::optional< Value > writeEnOpt()
std::optional< Value > readOrContentEn
std::optional< Value > done
std::optional< Value > writeEn
std::optional< Value > writeData
std::optional< bool > isContentEn
std::optional< Value > readData
SmallVector< Value > addrPorts
When building groups which contain accesses to multiple sequential components, a group_done op is cre...
LogicalResult matchAndRewrite(calyx::GroupOp groupOp, PatternRewriter &rewriter) const override
GroupDoneOp's are terminator operations and should therefore be the last operator in a group.
LogicalResult matchAndRewrite(calyx::GroupDoneOp groupDoneOp, PatternRewriter &) const override