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