Loading [MathJax]/extensions/tex2jax.js
CIRCT 21.0.0git
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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#include "llvm/Support/JSON.h"
31
32#include <optional>
33#include <variant>
34
35namespace circt {
36namespace calyx {
37
38void appendPortsForExternalMemref(PatternRewriter &rewriter, StringRef memName,
39 Value memref, unsigned memoryID,
40 SmallVectorImpl<calyx::PortInfo> &inPorts,
41 SmallVectorImpl<calyx::PortInfo> &outPorts);
42
43// Walks the control of this component, and appends source information for leaf
44// nodes. It also appends a position attribute that connects the source location
45// metadata to the corresponding control operation.
46WalkResult
47getCiderSourceLocationMetadata(calyx::ComponentOp component,
48 SmallVectorImpl<Attribute> &sourceLocations);
49
50// Tries to match a constant value defined by op. If the match was
51// successful, returns true and binds the constant to 'value'. If unsuccessful,
52// the value is unmodified.
53bool matchConstantOp(Operation *op, APInt &value);
54
55// Returns true if there exists only a single memref::LoadOp which loads from
56// the memory referenced by loadOp.
57bool singleLoadFromMemory(Value memoryReference);
58
59// Returns true if there are no memref::StoreOp uses with the referenced
60// memory.
61bool noStoresToMemory(Value memoryReference);
62
63// Get the index'th output port of compOp.
64Value getComponentOutput(calyx::ComponentOp compOp, unsigned outPortIdx);
65
66// If the provided type is an index type, converts it to i32; else if the
67// provided is an integer or floating point, bitcasts it to a signless integer
68// type; otherwise, returns the unmodified type.
69Type normalizeType(OpBuilder &builder, Type type);
70
71// Creates a new calyx::CombGroupOp or calyx::GroupOp group within compOp.
72template <typename TGroup>
73TGroup createGroup(OpBuilder &builder, calyx::ComponentOp compOp, Location loc,
74 Twine uniqueName) {
75 mlir::IRRewriter::InsertionGuard guard(builder);
76 builder.setInsertionPointToEnd(compOp.getWiresOp().getBodyBlock());
77 return builder.create<TGroup>(loc, uniqueName.str());
78}
79
80/// Creates register assignment operations within the provided groupOp.
81/// The component operation will house the constants.
82void buildAssignmentsForRegisterWrite(OpBuilder &builder,
83 calyx::GroupOp groupOp,
84 calyx::ComponentOp componentOp,
85 calyx::RegisterOp &reg, Value inputValue);
86
87// A structure representing a set of ports which act as a memory interface for
88// external memories.
90 std::string memName;
91 std::optional<Value> readData;
92 std::optional<Value> readOrContentEn;
93 std::optional<Value> writeData;
94 std::optional<Value> writeEn;
95 std::optional<Value> done;
96 SmallVector<Value> addrPorts;
97 std::optional<bool> isContentEn;
98};
99
100// Represents the interface of memory in Calyx. The various lowering passes
101// are agnostic wrt. whether working with a calyx::MemoryOp (internally
102// allocated memory) or MemoryPortsImpl (external memory).
105 explicit MemoryInterface(const MemoryPortsImpl &ports);
106 explicit MemoryInterface(calyx::MemoryOp memOp);
107 explicit MemoryInterface(calyx::SeqMemoryOp memOp);
108
109 // Getter methods for each memory interface port.
110 std::string memName();
111 Value readData();
112 Value readEn();
113 Value contentEn();
114 Value writeData();
115 Value writeEn();
116 Value done();
117 std::optional<Value> readDataOpt();
118 std::optional<Value> readEnOpt();
119 std::optional<Value> contentEnOpt();
120 std::optional<Value> writeDataOpt();
121 std::optional<Value> writeEnOpt();
122 std::optional<Value> doneOpt();
123 ValueRange addrPorts();
124
125private:
126 std::variant<calyx::MemoryOp, calyx::SeqMemoryOp, MemoryPortsImpl> impl;
127};
128
129// A common interface for any loop operation that needs to be lowered to Calyx.
131public:
133
134 // Returns the arguments to this loop operation.
135 virtual Block::BlockArgListType getBodyArgs() = 0;
136
137 // Returns body of this loop operation.
138 virtual Block *getBodyBlock() = 0;
139
140 // Returns the location of the loop interface.
141 virtual Location getLoc() = 0;
142
143 // Returns the number of iterations the loop will conduct if known.
144 virtual std::optional<int64_t> getBound() = 0;
145};
146
147// A common interface for loop operations that have conditionals (e.g., while
148// loops) that need to be lowered to Calyx.
150public:
151 // Returns the Block in which the condition exists.
152 virtual Block *getConditionBlock() = 0;
153
154 // Returns the condition as a Value.
155 virtual Value getConditionValue() = 0;
156};
157
158// Provides an interface for the control flow `while` operation across different
159// dialects.
160template <typename T>
162 static_assert(std::is_convertible_v<T, Operation *>);
163
164public:
165 explicit WhileOpInterface(T op) : impl(op) {}
166 explicit WhileOpInterface(Operation *op) : impl(dyn_cast_or_null<T>(op)) {}
167
168 // Returns the operation.
169 T getOperation() { return impl; }
170
171 // Returns the source location of the operation.
172 Location getLoc() override { return impl->getLoc(); }
173
174private:
176};
177
178// Provides an interface for the control flow `forOp` operation across different
179// dialects.
180template <typename T>
182 static_assert(std::is_convertible_v<T, Operation *>);
183
184public:
185 explicit RepeatOpInterface(T op) : impl(op) {}
186 explicit RepeatOpInterface(Operation *op) : impl(dyn_cast_or_null<T>(op)) {}
187
188 // Returns the operation.
189 T getOperation() { return impl; }
190
191 // Returns the source location of the operation.
192 Location getLoc() override { return impl->getLoc(); }
193
194private:
196};
197
198/// Holds common utilities used for scheduling when lowering to Calyx.
199template <typename T>
201public:
202 /// Register 'scheduleable' as being generated through lowering 'block'.
203 ///
204 /// TODO(mortbopet): Add a post-insertion check to ensure that the use-def
205 /// ordering invariant holds for the groups. When the control schedule is
206 /// generated, scheduleables within a block are emitted sequentially based on
207 /// the order that this function was called during conversion.
208 ///
209 /// Currently, we assume this to always be true. Walking the FuncOp IR implies
210 /// sequential iteration over operations within basic blocks.
211 void addBlockScheduleable(mlir::Block *block, const T &scheduleable) {
212 blockScheduleables[block].push_back(scheduleable);
213 }
214
215 /// Returns an ordered list of schedulables which registered themselves to be
216 /// a result of lowering the block in the source program. The list order
217 /// follows def-use chains between the scheduleables in the block.
218 SmallVector<T> getBlockScheduleables(mlir::Block *block) {
219 if (auto it = blockScheduleables.find(block);
220 it != blockScheduleables.end())
221 return it->second;
222 /// In cases of a block resulting in purely combinational logic, no
223 /// scheduleables registered themselves with the block.
224 return {};
225 }
226
227private:
228 /// BlockScheduleables is a list of scheduleables that should be
229 /// sequentially executed when executing the associated basic block.
230 DenseMap<mlir::Block *, SmallVector<T>> blockScheduleables;
231};
232
233//===----------------------------------------------------------------------===//
234// Lowering state classes
235//===----------------------------------------------------------------------===//
236
237// Handles state during the lowering of a loop. It will be used for
238// several lowering patterns.
239template <typename Loop>
241 static_assert(std::is_base_of_v<BasicLoopInterface, Loop>);
242
243public:
245
246 /// Register reg as being the idx'th iter_args register for 'op'.
247 void addLoopIterReg(Loop op, calyx::RegisterOp reg, unsigned idx) {
248 assert(loopIterRegs[op.getOperation()].count(idx) == 0 &&
249 "A register was already registered for the given loop iter_arg "
250 "index");
251 assert(idx < op.getBodyArgs().size());
252 loopIterRegs[op.getOperation()][idx] = reg;
253 }
254
255 /// Return a mapping of block argument indices to block argument.
256 calyx::RegisterOp getLoopIterReg(Loop op, unsigned idx) {
257 auto iterRegs = getLoopIterRegs(op);
258 auto it = iterRegs.find(idx);
259 assert(it != iterRegs.end() &&
260 "No iter arg register set for the provided index");
261 return it->second;
262 }
263
264 /// Return a mapping of block argument indices to block argument.
265 const DenseMap<unsigned, calyx::RegisterOp> &getLoopIterRegs(Loop op) {
266 return loopIterRegs[op.getOperation()];
267 }
268
269 /// Registers grp to be the loop latch group of `op`.
270 void setLoopLatchGroup(Loop op, calyx::GroupOp group) {
271 Operation *operation = op.getOperation();
272 assert(loopLatchGroups.count(operation) == 0 &&
273 "A latch group was already set for this loopOp");
274 loopLatchGroups[operation] = group;
275 }
276
277 /// Retrieve the loop latch group registered for `op`.
278 calyx::GroupOp getLoopLatchGroup(Loop op) {
279 auto it = loopLatchGroups.find(op.getOperation());
280 assert(it != loopLatchGroups.end() &&
281 "No loop latch group was set for this loopOp");
282 return it->second;
283 }
284
285 /// Registers groups to be the loop init groups of `op`.
286 void setLoopInitGroups(Loop op, SmallVector<calyx::GroupOp> groups) {
287 Operation *operation = op.getOperation();
288 assert(loopInitGroups.count(operation) == 0 &&
289 "Init group(s) was already set for this loopOp");
290 loopInitGroups[operation] = std::move(groups);
291 }
292
293 /// Retrieve the loop init groups registered for `op`.
294 SmallVector<calyx::GroupOp> getLoopInitGroups(Loop op) {
295 auto it = loopInitGroups.find(op.getOperation());
296 assert(it != loopInitGroups.end() &&
297 "No init group(s) was set for this loopOp");
298 return it->second;
299 }
300
301 /// Creates a new group that assigns the 'ops' values to the iter arg
302 /// registers of the loop operation.
303 calyx::GroupOp buildLoopIterArgAssignments(OpBuilder &builder, Loop op,
304 calyx::ComponentOp componentOp,
305 Twine uniqueSuffix,
306 MutableArrayRef<OpOperand> ops) {
307 /// Pass iteration arguments through registers. This follows closely
308 /// to what is done for branch ops.
309 std::string groupName = "assign_" + uniqueSuffix.str();
310 auto groupOp = calyx::createGroup<calyx::GroupOp>(builder, componentOp,
311 op.getLoc(), groupName);
312 /// Create register assignment for each iter_arg. a calyx::GroupDone signal
313 /// is created for each register. These will be &'ed together in
314 /// MultipleGroupDonePattern.
315 for (OpOperand &arg : ops) {
316 auto reg = getLoopIterReg(op, arg.getOperandNumber());
317 buildAssignmentsForRegisterWrite(builder, groupOp, componentOp, reg,
318 arg.get());
319 }
320 return groupOp;
321 }
322
323private:
324 /// A mapping from loop ops to iteration argument registers.
325 DenseMap<Operation *, DenseMap<unsigned, calyx::RegisterOp>> loopIterRegs;
326
327 /// A loop latch group is a group that should be sequentially executed when
328 /// finishing a loop body. The execution of this group will write the
329 /// yield'ed loop body values to the iteration argument registers.
330 DenseMap<Operation *, calyx::GroupOp> loopLatchGroups;
331
332 /// Loop init groups are to be scheduled before the while operation. These
333 /// groups should set the initial value(s) of the loop init_args register(s).
334 DenseMap<Operation *, SmallVector<calyx::GroupOp>> loopInitGroups;
335};
336
337// Handles state during the lowering of a Calyx component. This provides common
338// tools for converting to the Calyx ComponentOp.
340public:
341 ComponentLoweringStateInterface(calyx::ComponentOp component);
342
344
345 /// Returns the calyx::ComponentOp associated with this lowering state.
346 calyx::ComponentOp getComponentOp();
347
348 /// Register reg as being the idx'th argument register for block. This is
349 /// necessary for the `BuildBBReg` pass.
350 void addBlockArgReg(Block *block, calyx::RegisterOp reg, unsigned idx);
351
352 /// Return a mapping of block argument indices to block argument registers.
353 /// This is necessary for the `BuildBBReg` pass.
354 const DenseMap<unsigned, calyx::RegisterOp> &getBlockArgRegs(Block *block);
355
356 /// Register 'grp' as a group which performs block argument
357 /// register transfer when transitioning from basic block 'from' to 'to'.
358 void addBlockArgGroup(Block *from, Block *to, calyx::GroupOp grp);
359
360 /// Returns a list of groups to be evaluated to perform the block argument
361 /// register assignments when transitioning from basic block 'from' to 'to'.
362 ArrayRef<calyx::GroupOp> getBlockArgGroups(Block *from, Block *to);
363
364 /// Returns a unique name within compOp with the provided prefix.
365 std::string getUniqueName(StringRef prefix);
366
367 /// Returns a unique name associated with a specific operation.
368 StringRef getUniqueName(Operation *op);
369
370 /// Registers a unique name for a given operation using a provided prefix.
371 void setUniqueName(Operation *op, StringRef prefix);
372
373 /// Register value v as being evaluated when scheduling group.
374 void registerEvaluatingGroup(Value v, calyx::GroupInterface group);
375
376 /// Register reg as being the idx'th return value register.
377 void addReturnReg(calyx::RegisterOp reg, unsigned idx);
378
379 /// Returns the idx'th return value register.
380 calyx::RegisterOp getReturnReg(unsigned idx);
381
382 /// Registers a memory interface as being associated with a memory identified
383 /// by 'memref'.
384 void registerMemoryInterface(Value memref,
385 const calyx::MemoryInterface &memoryInterface);
386
387 /// Returns the memory interface registered for the given memref.
389
390 /// If v is an input to any memory registered within this component, returns
391 /// the memory. If not, returns null.
392 std::optional<calyx::MemoryInterface> isInputPortOfMemory(Value v);
393
394 /// Assign a mapping between the source funcOp result indices and the
395 /// corresponding output port indices of this componentOp.
396 void setFuncOpResultMapping(const DenseMap<unsigned, unsigned> &mapping);
397
398 /// Get the output port index of this component for which the funcReturnIdx of
399 /// the original function maps to.
400 unsigned getFuncOpResultMapping(unsigned funcReturnIdx);
401
402 /// The instance is obtained from the name of the callee.
403 InstanceOp getInstance(StringRef calleeName);
404
405 /// Put the name of the callee and the instance of the call into map.
406 void addInstance(StringRef calleeName, InstanceOp instanceOp);
407
408 /// Returns if `op` is a compare operator that requires a register to hold the
409 /// value of its sequential guard computation.
410 bool isSeqGuardCmpLibOp(Operation *op);
411
412 /// Add `op` if it's a compare operator that requires a register to hold the
413 /// value of its sequential guard computation.
414 void addSeqGuardCmpLibOp(Operation *op);
415
416 /// Returns the evaluating group or None if not found.
417 template <typename TGroupOp = calyx::GroupInterface>
418 std::optional<TGroupOp> findEvaluatingGroup(Value v) {
419 auto it = valueGroupAssigns.find(v);
420 if (it == valueGroupAssigns.end())
421 return std::nullopt;
422
423 if constexpr (std::is_same_v<TGroupOp, calyx::GroupInterface>)
424 return it->second;
425 else {
426 auto group = dyn_cast<TGroupOp>(it->second.getOperation());
427 assert(group && "Actual group type differed from expected group type");
428 return group;
429 }
430 }
431
432 /// Return the group which evaluates the value v. Optionally, caller may
433 /// specify the expected type of the group.
434 template <typename TGroupOp = calyx::GroupInterface>
435 TGroupOp getEvaluatingGroup(Value v) {
436 std::optional<TGroupOp> group = findEvaluatingGroup<TGroupOp>(v);
437 assert(group.has_value() && "No group evaluating value!");
438 return *group;
439 }
440
441 template <typename T, typename = void>
442 struct IsFloatingPoint : std::false_type {};
443
444 template <typename T>
446 T, std::void_t<decltype(std::declval<T>().getFloatingPointStandard())>>
447 : std::is_same<decltype(std::declval<T>().getFloatingPointStandard()),
448 FloatingPointStandard> {};
449
450 template <typename TLibraryOp>
451 TLibraryOp getNewLibraryOpInstance(OpBuilder &builder, Location loc,
452 TypeRange resTypes) {
453 mlir::IRRewriter::InsertionGuard guard(builder);
454 Block *body = component.getBodyBlock();
455 builder.setInsertionPoint(body, body->begin());
456 std::string name = TLibraryOp::getOperationName().split(".").second.str();
458 switch (TLibraryOp::getFloatingPointStandard()) {
460 constexpr char prefix[] = "ieee754.";
461 assert(name.find(prefix) == 0 &&
462 ("IEEE754 type operation's name must begin with '" +
463 std::string(prefix) + "'")
464 .c_str());
465 name.erase(0, sizeof(prefix) - 1);
466 name = llvm::join_items(/*separator=*/"", "std_", name, "FN");
467 break;
468 }
469 }
470 }
471 return builder.create<TLibraryOp>(loc, getUniqueName(name), resTypes);
472 }
473
474 llvm::json::Value &getExtMemData() { return extMemData; }
475
476 const llvm::json::Value &getExtMemData() const { return extMemData; }
477
478 void setDataField(StringRef name, llvm::json::Array data) {
479 auto *extMemDataObj = extMemData.getAsObject();
480 assert(extMemDataObj && "extMemData should be an object");
481
482 auto &value = (*extMemDataObj)[name.str()];
483 llvm::json::Object *obj = value.getAsObject();
484 if (!obj) {
485 value = llvm::json::Object{};
486 obj = value.getAsObject();
487 }
488 (*obj)["data"] = llvm::json::Value(std::move(data));
489 }
490
491 void setFormat(StringRef name, std::string numType, bool isSigned,
492 unsigned width) {
493 auto *extMemDataObj = extMemData.getAsObject();
494 assert(extMemDataObj && "extMemData should be an object");
495
496 auto &value = (*extMemDataObj)[name.str()];
497 llvm::json::Object *obj = value.getAsObject();
498 if (!obj) {
499 value = llvm::json::Object{};
500 obj = value.getAsObject();
501 }
502 (*obj)["format"] = llvm::json::Object{
503 {"numeric_type", numType}, {"is_signed", isSigned}, {"width", width}};
504 }
505
506private:
507 /// The component which this lowering state is associated to.
508 calyx::ComponentOp component;
509
510 /// A mapping from blocks to block argument registers.
511 DenseMap<Block *, DenseMap<unsigned, calyx::RegisterOp>> blockArgRegs;
512
513 /// Block arg groups is a list of groups that should be sequentially
514 /// executed when passing control from the source to destination block.
515 /// Block arg groups are executed before blockScheduleables (akin to a
516 /// phi-node).
517 DenseMap<Block *, DenseMap<Block *, SmallVector<calyx::GroupOp>>>
519
520 /// A mapping of string prefixes and the current uniqueness counter for that
521 /// prefix. Used to generate unique names.
522 std::map<std::string, unsigned> prefixIdMap;
523
524 /// A mapping from Operations and previously assigned unique name of the op.
525 std::map<Operation *, std::string> opNames;
526
527 /// A mapping between SSA values and the groups which assign them.
528 DenseMap<Value, calyx::GroupInterface> valueGroupAssigns;
529
530 /// A mapping from return value indexes to return value registers.
531 DenseMap<unsigned, calyx::RegisterOp> returnRegs;
532
533 /// A mapping from memref's to their corresponding Calyx memory interface.
534 DenseMap<Value, calyx::MemoryInterface> memories;
535
536 /// A mapping between the source funcOp result indices and the corresponding
537 /// output port indices of this componentOp.
538 DenseMap<unsigned, unsigned> funcOpResultMapping;
539
540 /// A mapping between the callee and the instance.
541 llvm::StringMap<calyx::InstanceOp> instanceMap;
542
543 /// A json file to store external global memory data. See
544 /// https://docs.calyxir.org/lang/data-format.html?highlight=json#the-data-format
545 llvm::json::Value extMemData;
546
547 /// A set of compare operators that require registers to hold their sequential
548 /// guard computation.
549 DenseSet<Operation *> seqGuardCmpLibOps;
550};
551
552/// An interface for conversion passes that lower Calyx programs. This handles
553/// state during the lowering of a Calyx program.
555public:
556 explicit CalyxLoweringState(mlir::ModuleOp module,
557 StringRef topLevelFunction);
558
559 /// Returns the current program.
560 mlir::ModuleOp getModule();
561
562 /// Returns the name of the top-level function in the source program.
563 StringRef getTopLevelFunction() const;
564
565 /// Returns a meaningful name for a block within the program scope (removes
566 /// the ^ prefix from block names).
567 std::string blockName(Block *b);
568
569 /// Returns the component lowering state associated with `op`. If not found
570 /// already found, a new mapping is added for this ComponentOp. Different
571 /// conversions may have different derived classes of the interface, so we
572 /// provided a template.
573 template <typename T = calyx::ComponentLoweringStateInterface>
574 T *getState(calyx::ComponentOp op) {
575 static_assert(std::is_convertible_v<T, ComponentLoweringStateInterface>);
576 auto it = componentStates.find(op);
577 if (it == componentStates.end()) {
578 // Create a new ComponentLoweringState for the compOp.
579 bool success;
580 std::tie(it, success) =
581 componentStates.try_emplace(op, std::make_unique<T>(op));
582 }
583
584 return static_cast<T *>(it->second.get());
585 }
586
587 /// Returns a meaningful name for a value within the program scope.
588 template <typename ValueOrBlock>
589 std::string irName(ValueOrBlock &v) {
590 std::string s;
591 llvm::raw_string_ostream os(s);
592 mlir::AsmState asmState(module);
593 v.printAsOperand(os, asmState);
594 return s;
595 }
596
597private:
598 /// The name of this top-level function.
600 /// The program associated with this state.
601 mlir::ModuleOp module;
602 /// Mapping from ComponentOp to component lowering state.
603 DenseMap<Operation *, std::unique_ptr<ComponentLoweringStateInterface>>
605};
606
607/// Extra state that is passed to all PartialLoweringPatterns so they can record
608/// when they have run on an Operation, and only run once.
610 DenseMap<const mlir::RewritePattern *, SmallPtrSet<Operation *, 16>>;
611
612/// Base class for partial lowering passes. A partial lowering pass
613/// modifies the root operation in place, but does not replace the root
614/// operation.
615/// The RewritePatternType template parameter allows for using both
616/// OpRewritePattern (default) or OpInterfaceRewritePattern.
617template <class OpType,
618 template <class> class RewritePatternType = OpRewritePattern>
619class PartialLoweringPattern : public RewritePatternType<OpType> {
620public:
621 using RewritePatternType<OpType>::RewritePatternType;
622 PartialLoweringPattern(MLIRContext *ctx, LogicalResult &resRef,
624 : RewritePatternType<OpType>(ctx), partialPatternRes(resRef),
626
627 LogicalResult matchAndRewrite(OpType op,
628 PatternRewriter &rewriter) const override {
629 // If this pattern has been applied to this op, it should now fail to match.
630 if (patternState[this].contains(op))
631 return failure();
632
633 // Do the actual rewrite, marking this op as updated. Because the op is
634 // marked as updated, the pattern driver will re-enqueue the op again.
635 rewriter.modifyOpInPlace(
636 op, [&] { partialPatternRes = partiallyLower(op, rewriter); });
637
638 // Mark that this pattern has been applied to this op.
639 patternState[this].insert(op);
640
641 return partialPatternRes;
642 }
643
644 // Hook for subclasses to lower the op using the rewriter.
645 //
646 // Note that this call is wrapped in `modifyOpInPlace`, so any direct IR
647 // mutations that are legal to apply during a root update of op are allowed.
648 //
649 // Also note that this means the op will be re-enqueued to the greedy
650 // rewriter's worklist. A safeguard is in place to prevent patterns from
651 // running multiple times, but if the op is erased or otherwise becomes dead
652 // after the call to `partiallyLower`, there will likely be use-after-free
653 // violations. If you will erase the op, override `matchAndRewrite` directly.
654 virtual LogicalResult partiallyLower(OpType op,
655 PatternRewriter &rewriter) const = 0;
656
657private:
658 LogicalResult &partialPatternRes;
660};
661
662/// Helper to update the top-level ModuleOp to set the entrypoing function.
663LogicalResult applyModuleOpConversion(mlir::ModuleOp,
664 StringRef topLevelFunction);
665
666/// FuncOpPartialLoweringPatterns are patterns which intend to match on FuncOps
667/// and then perform their own walking of the IR.
669 : public calyx::PartialLoweringPattern<mlir::func::FuncOp> {
670
671public:
673 MLIRContext *context, LogicalResult &resRef,
675 DenseMap<mlir::func::FuncOp, calyx::ComponentOp> &map,
677
678 /// Entry point to initialize the state of this class and conduct the partial
679 /// lowering.
680 LogicalResult partiallyLower(mlir::func::FuncOp funcOp,
681 PatternRewriter &rewriter) const override final;
682
683 /// Returns the component operation associated with the currently executing
684 /// partial lowering.
685 calyx::ComponentOp getComponent() const;
686
687 // Returns the component state associated with the currently executing
688 // partial lowering.
689 template <typename T = ComponentLoweringStateInterface>
690 T &getState() const {
691 static_assert(
692 std::is_convertible_v<T, calyx::ComponentLoweringStateInterface>);
693 assert(
694 componentLoweringState != nullptr &&
695 "Component lowering state should be set during pattern construction");
696 return *static_cast<T *>(componentLoweringState);
697 }
698
699 /// Return the calyx lowering state for this pattern.
701
702 // Hook for subclasses to lower the op using the rewriter.
703 //
704 // Note that this call is wrapped in `modifyOpInPlace`, so any direct IR
705 // mutations that are legal to apply during a root update of op are allowed.
706 //
707 // Also note that this means the op will be re-enqueued to the greedy
708 // rewriter's worklist. A safeguard is in place to prevent patterns from
709 // running multiple times, but if the op is erased or otherwise becomes dead
710 // after the call to `partiallyLower`, there will likely be use-after-free
711 // violations. If you will erase the op, override `matchAndRewrite` directly.
712 virtual LogicalResult
713 partiallyLowerFuncToComp(mlir::func::FuncOp funcOp,
714 PatternRewriter &rewriter) const = 0;
715
716protected:
717 // A map from FuncOp to it's respective ComponentOp lowering.
718 DenseMap<mlir::func::FuncOp, calyx::ComponentOp> &functionMapping;
719
720private:
721 mutable ComponentOp componentOp;
724};
725
726/// Converts all index-typed operations and values to i32 values.
729
730 LogicalResult
731 partiallyLowerFuncToComp(mlir::func::FuncOp funcOp,
732 PatternRewriter &rewriter) const override;
733};
734
735/// GroupDoneOp's are terminator operations and should therefore be the last
736/// operator in a group. During group construction, we always append assignments
737/// to the end of a group, resulting in group_done ops migrating away from the
738/// terminator position. This pattern moves such ops to the end of their group.
740 : mlir::OpRewritePattern<calyx::GroupDoneOp> {
741 using mlir::OpRewritePattern<calyx::GroupDoneOp>::OpRewritePattern;
742
743 LogicalResult matchAndRewrite(calyx::GroupDoneOp groupDoneOp,
744 PatternRewriter &) const override;
745};
746
747/// When building groups which contain accesses to multiple sequential
748/// components, a group_done op is created for each of these. This pattern
749/// and's each of the group_done values into a single group_done.
751 using mlir::OpRewritePattern<calyx::GroupOp>::OpRewritePattern;
752
753 LogicalResult matchAndRewrite(calyx::GroupOp groupOp,
754 PatternRewriter &rewriter) const override;
755};
756
757/// Removes calyx::CombGroupOps which are unused. These correspond to
758/// combinational groups created during op building that, after conversion,
759/// have either been inlined into calyx::GroupOps or are referenced by an
760/// if/while with statement.
761/// We do not eliminate unused calyx::GroupOps; this should never happen, and is
762/// considered an error. In these cases, the program will be invalidated when
763/// the Calyx verifiers execute.
765 using mlir::OpRewritePattern<calyx::CombGroupOp>::OpRewritePattern;
766
767 LogicalResult matchAndRewrite(calyx::CombGroupOp combGroupOp,
768 PatternRewriter &rewriter) const override;
769};
770
771/// Removes duplicate EnableOps in parallel operations.
773 using mlir::OpRewritePattern<calyx::ParOp>::OpRewritePattern;
774
775 LogicalResult matchAndRewrite(calyx::ParOp parOp,
776 PatternRewriter &rewriter) const override;
777};
778
779/// Removes duplicate EnableOps in static parallel operations.
781 : mlir::OpRewritePattern<calyx::StaticParOp> {
782 using mlir::OpRewritePattern<calyx::StaticParOp>::OpRewritePattern;
783
784 LogicalResult matchAndRewrite(calyx::StaticParOp parOp,
785 PatternRewriter &rewriter) const override;
786};
787
788/// This pass recursively inlines use-def chains of combinational logic (from
789/// non-stateful groups) into groups referenced in the control schedule.
791 : public calyx::PartialLoweringPattern<calyx::GroupInterface,
792 mlir::OpInterfaceRewritePattern> {
793public:
794 InlineCombGroups(MLIRContext *context, LogicalResult &resRef,
797
798 LogicalResult partiallyLower(calyx::GroupInterface originGroup,
799 PatternRewriter &rewriter) const override;
800
801private:
802 void
803 recurseInlineCombGroups(PatternRewriter &rewriter,
805 llvm::SmallSetVector<Operation *, 8> &inlinedGroups,
806 calyx::GroupInterface originGroup,
807 calyx::GroupInterface recGroup, bool doInline) const;
808
810};
811
812/// This pass rewrites memory accesses that have a width mismatch. Such
813/// mismatches are due to index types being assumed 32-bit wide due to the lack
814/// of a width inference pass.
816 : public calyx::PartialLoweringPattern<calyx::AssignOp> {
817public:
818 RewriteMemoryAccesses(MLIRContext *context, LogicalResult &resRef,
821 : PartialLoweringPattern(context, resRef, patternState), cls(cls) {}
822
823 LogicalResult partiallyLower(calyx::AssignOp assignOp,
824 PatternRewriter &rewriter) const override;
825
826private:
828};
829
830/// Builds registers for each block argument in the program.
833
834 LogicalResult
835 partiallyLowerFuncToComp(mlir::func::FuncOp funcOp,
836 PatternRewriter &rewriter) const override;
837};
838
839/// Builds registers for the return statement of the program and constant
840/// assignments to the component return value.
843
844 LogicalResult
845 partiallyLowerFuncToComp(mlir::func::FuncOp funcOp,
846 PatternRewriter &rewriter) const override;
847};
848
849/// Builds instance for the calyx.invoke and calyx.group in order to initialize
850/// the instance.
853
854 LogicalResult
855 partiallyLowerFuncToComp(mlir::func::FuncOp funcOp,
856 PatternRewriter &rewriter) const override;
857 ComponentOp getCallComponent(mlir::func::CallOp callOp) const;
858};
859
860/// Predicate information for the floating point comparisons
862 struct InputPorts {
863 // Relevant ports to extract from the `std_compareFN`. For example, we
864 // extract the `lt` and the `unordered` ports when the predicate is `oge`.
865 enum class Port { Eq, Gt, Lt, Unordered };
867 // Whether we should invert the port before passing as inputs to the `op`
868 // field. For example, we should invert both the `lt` and the `unordered`
869 // port just extracted for predicate `oge`.
870 bool invert;
871 };
872
873 // The combinational logic to apply to the input ports. For example, we should
874 // apply `And` to the two input ports for predicate `oge`.
875 enum class CombLogic { None, And, Or };
877 SmallVector<InputPorts> inputPorts;
878};
879
880PredicateInfo getPredicateInfo(mlir::arith::CmpFPredicate pred);
881
882/// Performs a bit cast from a non-signless integer type value, such as a
883/// floating point value, to a signless integer type. Calyx treats everything as
884/// bit vectors, and leaves their interpretation to the respective operation
885/// using it. In CIRCT Calyx, we use signless `IntegerType` to represent a bit
886/// vector.
887template <typename T>
888Type toBitVector(T type) {
889 if (!type.isSignlessInteger()) {
890 unsigned bitWidth = cast<T>(type).getIntOrFloatBitWidth();
891 return IntegerType::get(type.getContext(), bitWidth);
892 }
893 return type;
894}
895
896// Returns whether `value` is the result of a sequential Calyx library
897// operation.
898bool parentIsSeqCell(Value value);
899
900} // namespace calyx
901} // namespace circt
902
903#endif // CIRCT_DIALECT_CALYX_CALYXLOWERINGUTILS_H
assert(baseType &&"element must be base type")
virtual Location getLoc()=0
virtual Block * getBodyBlock()=0
virtual std::optional< int64_t > getBound()=0
virtual Block::BlockArgListType getBodyArgs()=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 DenseMap< Operation *, std::unique_ptr< ComponentLoweringStateInterface > > componentStates
The program associated with this state.
mlir::ModuleOp getModule()
Returns the current program.
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.
T * getState(calyx::ComponentOp op)
Returns the component lowering state associated with op.
const llvm::json::Value & getExtMemData() const
void addSeqGuardCmpLibOp(Operation *op)
Add op if it's a compare operator that requires a register to hold the value of its sequential guard ...
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.
bool isSeqGuardCmpLibOp(Operation *op)
Returns if op is a compare operator that requires a register to hold the value of its sequential guar...
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...
void setDataField(StringRef name, llvm::json::Array data)
void registerEvaluatingGroup(Value v, calyx::GroupInterface group)
Register value v as being evaluated when scheduling group.
void setFormat(StringRef name, std::string numType, bool isSigned, unsigned width)
DenseSet< Operation * > seqGuardCmpLibOps
A set of compare operators that require registers to hold their sequential guard computation.
std::optional< calyx::MemoryInterface > isInputPortOfMemory(Value v)
If v is an input to any memory registered within this component, returns the memory.
llvm::json::Value extMemData
A json file to store external global memory data.
void addBlockArgGroup(Block *from, Block *to, calyx::GroupOp grp)
Register 'grp' as a group which performs block argument register transfer when transitioning from bas...
std::optional< TGroupOp > findEvaluatingGroup(Value v)
Returns the evaluating group or None if not found.
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
void recurseInlineCombGroups(PatternRewriter &rewriter, ComponentLoweringStateInterface &state, llvm::SmallSetVector< Operation *, 8 > &inlinedGroups, calyx::GroupInterface originGroup, calyx::GroupInterface recGroup, bool doInline) const
calyx::CalyxLoweringState & cls
virtual Block * getConditionBlock()=0
virtual Value getConditionValue()=0
calyx::GroupOp getLoopLatchGroup(Loop op)
Retrieve the loop latch group registered for op.
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.
SmallVector< calyx::GroupOp > getLoopInitGroups(Loop op)
Retrieve the loop init groups registered for op.
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.
const DenseMap< unsigned, calyx::RegisterOp > & getLoopIterRegs(Loop op)
Return a mapping of block argument indices to block argument.
Base class for partial lowering passes.
virtual LogicalResult partiallyLower(OpType op, PatternRewriter &rewriter) const =0
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.
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.
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...
SmallVector< T > getBlockScheduleables(mlir::Block *block)
Returns an ordered list of schedulables which registered themselves to be a result of lowering the bl...
bool parentIsSeqCell(Value value)
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...
PredicateInfo getPredicateInfo(mlir::arith::CmpFPredicate pred)
Type normalizeType(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)
Type toBitVector(T type)
Performs a bit cast from a non-signless integer type value, such as a floating point value,...
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Removes duplicate EnableOps in parallel operations.
LogicalResult matchAndRewrite(calyx::ParOp parOp, PatternRewriter &rewriter) const override
Removes duplicate EnableOps in static parallel operations.
LogicalResult matchAndRewrite(calyx::StaticParOp parOp, PatternRewriter &rewriter) const override
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 > writeEn
std::optional< Value > writeData
std::optional< bool > isContentEn
std::optional< Value > readData
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
Predicate information for the floating point comparisons.
SmallVector< InputPorts > inputPorts