19 #include "mlir/Conversion/LLVMCommon/ConversionTarget.h"
20 #include "mlir/Conversion/LLVMCommon/Pattern.h"
21 #include "mlir/Dialect/Arith/IR/Arith.h"
22 #include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
23 #include "mlir/Dialect/Func/IR/FuncOps.h"
24 #include "mlir/Dialect/MemRef/IR/MemRef.h"
25 #include "mlir/Dialect/SCF/IR/SCF.h"
26 #include "mlir/IR/AsmState.h"
27 #include "mlir/IR/Matchers.h"
28 #include "mlir/Pass/Pass.h"
29 #include "mlir/Support/LogicalResult.h"
30 #include "mlir/Transforms/GreedyPatternRewriteDriver.h"
31 #include "llvm/ADT/TypeSwitch.h"
36 #define GEN_PASS_DEF_SCFTOCALYX
37 #include "circt/Conversion/Passes.h.inc"
42 using namespace mlir::arith;
43 using namespace mlir::cf;
46 class ComponentLoweringStateInterface;
47 namespace scftocalyx {
56 : calyx::WhileOpInterface<scf::WhileOp>(op) {}
59 return getOperation().getAfterArguments();
62 Block *
getBodyBlock()
override {
return &getOperation().getAfter().front(); }
65 return &getOperation().getBefore().front();
69 return getOperation().getConditionOp().getOperand(0);
72 std::optional<int64_t>
getBound()
override {
return std::nullopt; }
77 explicit ScfForOp(scf::ForOp op) : calyx::RepeatOpInterface<scf::ForOp>(op) {}
80 return getOperation().getRegion().getArguments();
84 return &getOperation().getRegion().getBlocks().front();
88 return constantTripCount(getOperation().getLowerBound(),
89 getOperation().getUpperBound(),
90 getOperation().getStep());
125 return getLoopInitGroups(std::move(op));
128 OpBuilder &builder,
ScfWhileOp op, calyx::ComponentOp componentOp,
129 Twine uniqueSuffix, MutableArrayRef<OpOperand> ops) {
130 return buildLoopIterArgAssignments(builder, std::move(op), componentOp,
134 return addLoopIterReg(std::move(op),
reg, idx);
136 const DenseMap<unsigned, calyx::RegisterOp> &
138 return getLoopIterRegs(std::move(op));
141 return setLoopLatchGroup(std::move(op), group);
144 return getLoopLatchGroup(std::move(op));
147 SmallVector<calyx::GroupOp> groups) {
148 return setLoopInitGroups(std::move(op), std::move(groups));
156 return getLoopInitGroups(std::move(op));
159 OpBuilder &builder,
ScfForOp op, calyx::ComponentOp componentOp,
160 Twine uniqueSuffix, MutableArrayRef<OpOperand> ops) {
161 return buildLoopIterArgAssignments(builder, std::move(op), componentOp,
165 return addLoopIterReg(std::move(op),
reg, idx);
168 return getLoopIterRegs(std::move(op));
171 return getLoopIterReg(std::move(op), idx);
174 return setLoopLatchGroup(std::move(op), group);
177 return getLoopLatchGroup(std::move(op));
180 return setLoopInitGroups(std::move(op), std::move(groups));
193 : calyx::ComponentLoweringStateInterface(component) {}
203 using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
207 PatternRewriter &rewriter)
const override {
210 bool opBuiltSuccessfully =
true;
211 funcOp.walk([&](Operation *_op) {
212 opBuiltSuccessfully &=
213 TypeSwitch<mlir::Operation *, bool>(_op)
214 .template Case<arith::ConstantOp, ReturnOp, BranchOpInterface,
216 scf::YieldOp, scf::WhileOp, scf::ForOp,
218 memref::AllocOp, memref::AllocaOp, memref::LoadOp,
221 AddIOp, SubIOp, CmpIOp, ShLIOp, ShRUIOp, ShRSIOp,
222 AndIOp, XOrIOp, OrIOp, ExtUIOp, ExtSIOp, TruncIOp,
223 MulIOp, DivUIOp, DivSIOp, RemUIOp, RemSIOp,
224 SelectOp, IndexCastOp, CallOp>(
225 [&](
auto op) {
return buildOp(rewriter, op).succeeded(); })
226 .
template Case<FuncOp, scf::ConditionOp>([&](
auto) {
230 .Default([&](
auto op) {
231 op->emitError() <<
"Unhandled operation during BuildOpGroups()";
235 return opBuiltSuccessfully ? WalkResult::advance()
236 : WalkResult::interrupt();
239 return success(opBuiltSuccessfully);
244 LogicalResult buildOp(PatternRewriter &rewriter, scf::YieldOp yieldOp)
const;
245 LogicalResult buildOp(PatternRewriter &rewriter,
246 BranchOpInterface brOp)
const;
247 LogicalResult buildOp(PatternRewriter &rewriter,
248 arith::ConstantOp constOp)
const;
249 LogicalResult buildOp(PatternRewriter &rewriter, SelectOp op)
const;
250 LogicalResult buildOp(PatternRewriter &rewriter, AddIOp op)
const;
251 LogicalResult buildOp(PatternRewriter &rewriter, SubIOp op)
const;
252 LogicalResult buildOp(PatternRewriter &rewriter, MulIOp op)
const;
253 LogicalResult buildOp(PatternRewriter &rewriter, DivUIOp op)
const;
254 LogicalResult buildOp(PatternRewriter &rewriter, DivSIOp op)
const;
255 LogicalResult buildOp(PatternRewriter &rewriter, RemUIOp op)
const;
256 LogicalResult buildOp(PatternRewriter &rewriter, RemSIOp op)
const;
257 LogicalResult buildOp(PatternRewriter &rewriter, ShRUIOp op)
const;
258 LogicalResult buildOp(PatternRewriter &rewriter, ShRSIOp op)
const;
259 LogicalResult buildOp(PatternRewriter &rewriter, ShLIOp op)
const;
260 LogicalResult buildOp(PatternRewriter &rewriter, AndIOp op)
const;
261 LogicalResult buildOp(PatternRewriter &rewriter, OrIOp op)
const;
262 LogicalResult buildOp(PatternRewriter &rewriter, XOrIOp op)
const;
263 LogicalResult buildOp(PatternRewriter &rewriter, CmpIOp op)
const;
264 LogicalResult buildOp(PatternRewriter &rewriter, TruncIOp op)
const;
265 LogicalResult buildOp(PatternRewriter &rewriter, ExtUIOp op)
const;
266 LogicalResult buildOp(PatternRewriter &rewriter, ExtSIOp op)
const;
267 LogicalResult buildOp(PatternRewriter &rewriter, ReturnOp op)
const;
268 LogicalResult buildOp(PatternRewriter &rewriter, IndexCastOp op)
const;
269 LogicalResult buildOp(PatternRewriter &rewriter, memref::AllocOp op)
const;
270 LogicalResult buildOp(PatternRewriter &rewriter, memref::AllocaOp op)
const;
271 LogicalResult buildOp(PatternRewriter &rewriter, memref::LoadOp op)
const;
272 LogicalResult buildOp(PatternRewriter &rewriter, memref::StoreOp op)
const;
273 LogicalResult buildOp(PatternRewriter &rewriter, scf::WhileOp whileOp)
const;
274 LogicalResult buildOp(PatternRewriter &rewriter, scf::ForOp forOp)
const;
275 LogicalResult buildOp(PatternRewriter &rewriter, CallOp callOp)
const;
279 template <
typename TGroupOp,
typename TCalyxLibOp,
typename TSrcOp>
281 TypeRange srcTypes, TypeRange dstTypes)
const {
282 SmallVector<Type> types;
283 llvm::append_range(types, srcTypes);
284 llvm::append_range(types, dstTypes);
287 getState<ComponentLoweringState>().getNewLibraryOpInstance<TCalyxLibOp>(
288 rewriter, op.getLoc(), types);
290 auto directions = calyxOp.portDirections();
291 SmallVector<Value, 4> opInputPorts;
292 SmallVector<Value, 4> opOutputPorts;
293 for (
auto dir : enumerate(directions)) {
295 opInputPorts.push_back(calyxOp.getResult(dir.index()));
297 opOutputPorts.push_back(calyxOp.getResult(dir.index()));
300 opInputPorts.size() == op->getNumOperands() &&
301 opOutputPorts.size() == op->getNumResults() &&
302 "Expected an equal number of in/out ports in the Calyx library op with "
303 "respect to the number of operands/results of the source operation.");
306 auto group = createGroupForOp<TGroupOp>(rewriter, op);
307 rewriter.setInsertionPointToEnd(group.getBodyBlock());
308 for (
auto dstOp : enumerate(opInputPorts))
309 rewriter.create<calyx::AssignOp>(op.getLoc(), dstOp.value(),
310 op->getOperand(dstOp.index()));
313 for (
auto res : enumerate(opOutputPorts)) {
314 getState<ComponentLoweringState>().registerEvaluatingGroup(res.value(),
316 op->getResult(res.index()).replaceAllUsesWith(res.value());
323 template <
typename TGroupOp,
typename TCalyxLibOp,
typename TSrcOp>
325 return buildLibraryOp<TGroupOp, TCalyxLibOp, TSrcOp>(
326 rewriter, op, op.getOperandTypes(), op->getResultTypes());
330 template <
typename TGroupOp>
332 Block *block = op->getBlock();
333 auto groupName = getState<ComponentLoweringState>().getUniqueName(
335 return calyx::createGroup<TGroupOp>(
336 rewriter, getState<ComponentLoweringState>().getComponentOp(),
337 op->getLoc(), groupName);
342 template <
typename TOpType,
typename TSrcOp>
344 TOpType opPipe, Value out)
const {
345 StringRef opName = TSrcOp::getOperationName().split(
".").second;
346 Location loc = op.getLoc();
347 Type
width = op.getResult().getType();
349 op.getResult().replaceAllUsesWith(out);
351 op.getLoc(), rewriter, getComponent(),
width.getIntOrFloatBitWidth(),
352 getState<ComponentLoweringState>().getUniqueName(opName));
354 auto group = createGroupForOp<calyx::GroupOp>(rewriter, op);
355 OpBuilder builder(group->getRegion(0));
356 getState<ComponentLoweringState>().addBlockScheduleable(op->getBlock(),
359 rewriter.setInsertionPointToEnd(group.getBodyBlock());
360 rewriter.create<calyx::AssignOp>(loc, opPipe.getLeft(), op.getLhs());
361 rewriter.create<calyx::AssignOp>(loc, opPipe.getRight(), op.getRhs());
363 rewriter.create<calyx::AssignOp>(loc,
reg.getIn(), out);
365 rewriter.create<calyx::AssignOp>(loc,
reg.getWriteEn(), opPipe.getDone());
370 rewriter.create<calyx::AssignOp>(
371 loc, opPipe.getGo(), c1,
374 rewriter.create<calyx::GroupDoneOp>(loc,
reg.getDone());
377 getState<ComponentLoweringState>().registerEvaluatingGroup(out, group);
378 getState<ComponentLoweringState>().registerEvaluatingGroup(opPipe.getLeft(),
380 getState<ComponentLoweringState>().registerEvaluatingGroup(
381 opPipe.getRight(), group);
389 calyx::GroupInterface group,
391 Operation::operand_range addressValues)
const {
392 IRRewriter::InsertionGuard guard(rewriter);
393 rewriter.setInsertionPointToEnd(group.getBody());
394 auto addrPorts = memoryInterface.
addrPorts();
395 if (addressValues.empty()) {
397 addrPorts.size() == 1 &&
398 "We expected a 1 dimensional memory of size 1 because there were no "
399 "address assignment values");
401 rewriter.create<calyx::AssignOp>(
405 assert(addrPorts.size() == addressValues.size() &&
406 "Mismatch between number of address ports of the provided memory "
407 "and address assignment values");
408 for (
auto address : enumerate(addressValues))
409 rewriter.create<calyx::AssignOp>(loc, addrPorts[address.index()],
415 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
416 memref::LoadOp loadOp)
const {
417 Value memref = loadOp.getMemref();
418 auto memoryInterface =
419 getState<ComponentLoweringState>().getMemoryInterface(memref);
420 auto group = createGroupForOp<calyx::GroupOp>(rewriter, loadOp);
421 assignAddressPorts(rewriter, loadOp.getLoc(), group, memoryInterface,
422 loadOp.getIndices());
424 rewriter.setInsertionPointToEnd(group.getBodyBlock());
430 if (memoryInterface.readEnOpt().has_value()) {
433 rewriter.create<calyx::AssignOp>(loadOp.getLoc(), memoryInterface.readEn(),
435 regWriteEn = memoryInterface.done();
442 rewriter.create<calyx::GroupDoneOp>(loadOp.getLoc(),
443 memoryInterface.done());
453 res = loadOp.getResult();
455 }
else if (memoryInterface.contentEnOpt().has_value()) {
460 rewriter.create<calyx::AssignOp>(loadOp.getLoc(),
461 memoryInterface.contentEn(), oneI1);
462 rewriter.create<calyx::AssignOp>(loadOp.getLoc(), memoryInterface.writeEn(),
464 regWriteEn = memoryInterface.done();
471 rewriter.create<calyx::GroupDoneOp>(loadOp.getLoc(),
472 memoryInterface.done());
482 res = loadOp.getResult();
495 loadOp.getLoc(), rewriter, getComponent(),
496 loadOp.getMemRefType().getElementTypeBitWidth(),
497 getState<ComponentLoweringState>().getUniqueName(
"load"));
498 rewriter.setInsertionPointToEnd(group.getBodyBlock());
499 rewriter.create<calyx::AssignOp>(loadOp.getLoc(),
reg.getIn(),
500 memoryInterface.readData());
501 rewriter.create<calyx::AssignOp>(loadOp.getLoc(),
reg.getWriteEn(),
503 rewriter.create<calyx::GroupDoneOp>(loadOp.getLoc(),
reg.getDone());
504 loadOp.getResult().replaceAllUsesWith(
reg.getOut());
508 getState<ComponentLoweringState>().registerEvaluatingGroup(res, group);
509 getState<ComponentLoweringState>().addBlockScheduleable(loadOp->getBlock(),
514 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
515 memref::StoreOp storeOp)
const {
516 auto memoryInterface = getState<ComponentLoweringState>().getMemoryInterface(
517 storeOp.getMemref());
518 auto group = createGroupForOp<calyx::GroupOp>(rewriter, storeOp);
522 getState<ComponentLoweringState>().addBlockScheduleable(storeOp->getBlock(),
524 assignAddressPorts(rewriter, storeOp.getLoc(), group, memoryInterface,
525 storeOp.getIndices());
526 rewriter.setInsertionPointToEnd(group.getBodyBlock());
527 rewriter.create<calyx::AssignOp>(
528 storeOp.getLoc(), memoryInterface.writeData(), storeOp.getValueToStore());
529 rewriter.create<calyx::AssignOp>(
530 storeOp.getLoc(), memoryInterface.writeEn(),
531 createConstant(storeOp.getLoc(), rewriter, getComponent(), 1, 1));
532 if (memoryInterface.contentEnOpt().has_value()) {
534 rewriter.create<calyx::AssignOp>(
535 storeOp.getLoc(), memoryInterface.contentEn(),
536 createConstant(storeOp.getLoc(), rewriter, getComponent(), 1, 1));
538 rewriter.create<calyx::GroupDoneOp>(storeOp.getLoc(), memoryInterface.done());
543 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
545 Location loc = mul.getLoc();
546 Type
width = mul.getResult().getType(), one = rewriter.getI1Type();
548 getState<ComponentLoweringState>()
549 .getNewLibraryOpInstance<calyx::MultPipeLibOp>(
551 return buildLibraryBinaryPipeOp<calyx::MultPipeLibOp>(
552 rewriter, mul, mulPipe,
556 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
558 Location loc = div.getLoc();
559 Type
width = div.getResult().getType(), one = rewriter.getI1Type();
561 getState<ComponentLoweringState>()
562 .getNewLibraryOpInstance<calyx::DivUPipeLibOp>(
564 return buildLibraryBinaryPipeOp<calyx::DivUPipeLibOp>(
565 rewriter, div, divPipe,
569 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
571 Location loc = div.getLoc();
572 Type
width = div.getResult().getType(), one = rewriter.getI1Type();
574 getState<ComponentLoweringState>()
575 .getNewLibraryOpInstance<calyx::DivSPipeLibOp>(
577 return buildLibraryBinaryPipeOp<calyx::DivSPipeLibOp>(
578 rewriter, div, divPipe,
582 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
584 Location loc = rem.getLoc();
585 Type
width = rem.getResult().getType(), one = rewriter.getI1Type();
587 getState<ComponentLoweringState>()
588 .getNewLibraryOpInstance<calyx::RemUPipeLibOp>(
590 return buildLibraryBinaryPipeOp<calyx::RemUPipeLibOp>(
591 rewriter, rem, remPipe,
595 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
597 Location loc = rem.getLoc();
598 Type
width = rem.getResult().getType(), one = rewriter.getI1Type();
600 getState<ComponentLoweringState>()
601 .getNewLibraryOpInstance<calyx::RemSPipeLibOp>(
603 return buildLibraryBinaryPipeOp<calyx::RemSPipeLibOp>(
604 rewriter, rem, remPipe,
608 template <
typename TAllocOp>
610 PatternRewriter &rewriter, TAllocOp allocOp) {
611 rewriter.setInsertionPointToStart(
613 MemRefType memtype = allocOp.getType();
614 SmallVector<int64_t> addrSizes;
615 SmallVector<int64_t> sizes;
616 for (int64_t dim : memtype.getShape()) {
617 sizes.push_back(dim);
622 if (sizes.empty() && addrSizes.empty()) {
624 addrSizes.push_back(1);
626 auto memoryOp = rewriter.create<calyx::SeqMemoryOp>(
628 memtype.getElementType().getIntOrFloatBitWidth(), sizes, addrSizes);
631 memoryOp->setAttr(
"external",
638 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
639 memref::AllocOp allocOp)
const {
640 return buildAllocOp(getState<ComponentLoweringState>(), rewriter, allocOp);
643 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
644 memref::AllocaOp allocOp)
const {
645 return buildAllocOp(getState<ComponentLoweringState>(), rewriter, allocOp);
648 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
649 scf::YieldOp yieldOp)
const {
650 if (yieldOp.getOperands().empty()) {
652 auto forOp = dyn_cast<scf::ForOp>(yieldOp->getParentOp());
653 assert(forOp &&
"Empty yieldOps should only be located within ForOps");
658 getState<ComponentLoweringState>().getForLoopIterReg(forOpInterface, 0);
660 Type regWidth = inductionReg.getOut().getType();
662 SmallVector<Type> types(3, regWidth);
663 auto addOp = getState<ComponentLoweringState>()
664 .getNewLibraryOpInstance<calyx::AddLibOp>(
665 rewriter, forOp.getLoc(), types);
667 auto directions = addOp.portDirections();
669 SmallVector<Value, 2> opInputPorts;
671 for (
auto dir : enumerate(directions)) {
672 switch (dir.value()) {
674 opInputPorts.push_back(addOp.getResult(dir.index()));
678 opOutputPort = addOp.getResult(dir.index());
685 calyx::ComponentOp componentOp =
686 getState<ComponentLoweringState>().getComponentOp();
687 SmallVector<StringRef, 4> groupIdentifier = {
688 "incr", getState<ComponentLoweringState>().getUniqueName(forOp),
690 auto groupOp = calyx::createGroup<calyx::GroupOp>(
691 rewriter, componentOp, forOp.getLoc(),
692 llvm::join(groupIdentifier,
"_"));
693 rewriter.setInsertionPointToEnd(groupOp.getBodyBlock());
696 Value leftOp = opInputPorts.front();
697 rewriter.create<calyx::AssignOp>(forOp.getLoc(), leftOp,
698 inductionReg.getOut());
700 Value rightOp = opInputPorts.back();
701 rewriter.create<calyx::AssignOp>(
702 forOp.getLoc(), rightOp,
704 regWidth.getIntOrFloatBitWidth(),
705 forOp.getConstantStep().value().getSExtValue()));
708 inductionReg, opOutputPort);
710 getState<ComponentLoweringState>().setForLoopLatchGroup(forOpInterface,
712 getState<ComponentLoweringState>().registerEvaluatingGroup(opOutputPort,
717 if (dyn_cast<scf::ForOp>(yieldOp->getParentOp())) {
718 return yieldOp.getOperation()->emitError()
719 <<
"Currently do not support non-empty yield operations inside for "
720 "loops. Run --scf-for-to-while before running --scf-to-calyx.";
723 auto whileOp = dyn_cast<scf::WhileOp>(yieldOp->getParentOp());
725 return yieldOp.getOperation()->emitError()
726 <<
"Currently only support yield operations inside for and while "
732 getState<ComponentLoweringState>().buildWhileLoopIterArgAssignments(
733 rewriter, whileOpInterface,
734 getState<ComponentLoweringState>().getComponentOp(),
735 getState<ComponentLoweringState>().getUniqueName(whileOp) +
"_latch",
736 yieldOp->getOpOperands());
737 getState<ComponentLoweringState>().setWhileLoopLatchGroup(whileOpInterface,
742 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
743 BranchOpInterface brOp)
const {
748 Block *srcBlock = brOp->getBlock();
749 for (
auto succBlock : enumerate(brOp->getSuccessors())) {
750 auto succOperands = brOp.getSuccessorOperands(succBlock.index());
751 if (succOperands.empty())
754 std::string groupName =
loweringState().blockName(srcBlock) +
"_to_" +
756 auto groupOp = calyx::createGroup<calyx::GroupOp>(rewriter, getComponent(),
757 brOp.getLoc(), groupName);
759 auto dstBlockArgRegs =
760 getState<ComponentLoweringState>().getBlockArgRegs(succBlock.value());
762 for (
auto arg : enumerate(succOperands.getForwardedOperands())) {
763 auto reg = dstBlockArgRegs[arg.index()];
766 getState<ComponentLoweringState>().getComponentOp(),
reg,
771 getState<ComponentLoweringState>().addBlockArgGroup(
772 srcBlock, succBlock.value(), groupOp);
779 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
780 ReturnOp retOp)
const {
781 if (retOp.getNumOperands() == 0)
784 std::string groupName =
785 getState<ComponentLoweringState>().getUniqueName(
"ret_assign");
786 auto groupOp = calyx::createGroup<calyx::GroupOp>(rewriter, getComponent(),
787 retOp.getLoc(), groupName);
788 for (
auto op : enumerate(retOp.getOperands())) {
789 auto reg = getState<ComponentLoweringState>().getReturnReg(op.index());
791 rewriter, groupOp, getState<ComponentLoweringState>().getComponentOp(),
795 getState<ComponentLoweringState>().addBlockScheduleable(retOp->getBlock(),
800 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
801 arith::ConstantOp constOp)
const {
805 auto hwConstOp = rewriter.replaceOpWithNewOp<
hw::ConstantOp>(constOp, value);
806 hwConstOp->moveAfter(getComponent().getBodyBlock(),
807 getComponent().getBodyBlock()->begin());
811 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
813 return buildLibraryOp<calyx::CombGroupOp, calyx::AddLibOp>(rewriter, op);
815 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
817 return buildLibraryOp<calyx::CombGroupOp, calyx::SubLibOp>(rewriter, op);
819 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
821 return buildLibraryOp<calyx::CombGroupOp, calyx::RshLibOp>(rewriter, op);
823 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
825 return buildLibraryOp<calyx::CombGroupOp, calyx::SrshLibOp>(rewriter, op);
827 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
829 return buildLibraryOp<calyx::CombGroupOp, calyx::LshLibOp>(rewriter, op);
831 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
833 return buildLibraryOp<calyx::CombGroupOp, calyx::AndLibOp>(rewriter, op);
835 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
837 return buildLibraryOp<calyx::CombGroupOp, calyx::OrLibOp>(rewriter, op);
839 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
841 return buildLibraryOp<calyx::CombGroupOp, calyx::XorLibOp>(rewriter, op);
843 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
845 return buildLibraryOp<calyx::CombGroupOp, calyx::MuxLibOp>(rewriter, op);
848 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
850 switch (op.getPredicate()) {
851 case CmpIPredicate::eq:
852 return buildLibraryOp<calyx::CombGroupOp, calyx::EqLibOp>(rewriter, op);
853 case CmpIPredicate::ne:
854 return buildLibraryOp<calyx::CombGroupOp, calyx::NeqLibOp>(rewriter, op);
855 case CmpIPredicate::uge:
856 return buildLibraryOp<calyx::CombGroupOp, calyx::GeLibOp>(rewriter, op);
857 case CmpIPredicate::ult:
858 return buildLibraryOp<calyx::CombGroupOp, calyx::LtLibOp>(rewriter, op);
859 case CmpIPredicate::ugt:
860 return buildLibraryOp<calyx::CombGroupOp, calyx::GtLibOp>(rewriter, op);
861 case CmpIPredicate::ule:
862 return buildLibraryOp<calyx::CombGroupOp, calyx::LeLibOp>(rewriter, op);
863 case CmpIPredicate::sge:
864 return buildLibraryOp<calyx::CombGroupOp, calyx::SgeLibOp>(rewriter, op);
865 case CmpIPredicate::slt:
866 return buildLibraryOp<calyx::CombGroupOp, calyx::SltLibOp>(rewriter, op);
867 case CmpIPredicate::sgt:
868 return buildLibraryOp<calyx::CombGroupOp, calyx::SgtLibOp>(rewriter, op);
869 case CmpIPredicate::sle:
870 return buildLibraryOp<calyx::CombGroupOp, calyx::SleLibOp>(rewriter, op);
872 llvm_unreachable(
"unsupported comparison predicate");
874 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
876 return buildLibraryOp<calyx::CombGroupOp, calyx::SliceLibOp>(
877 rewriter, op, {op.getOperand().getType()}, {op.getType()});
879 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
881 return buildLibraryOp<calyx::CombGroupOp, calyx::PadLibOp>(
882 rewriter, op, {op.getOperand().getType()}, {op.getType()});
885 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
887 return buildLibraryOp<calyx::CombGroupOp, calyx::ExtSILibOp>(
888 rewriter, op, {op.getOperand().getType()}, {op.getType()});
891 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
892 IndexCastOp op)
const {
895 unsigned targetBits = targetType.getIntOrFloatBitWidth();
896 unsigned sourceBits = sourceType.getIntOrFloatBitWidth();
897 LogicalResult res = success();
899 if (targetBits == sourceBits) {
902 op.getResult().replaceAllUsesWith(op.getOperand());
905 if (sourceBits > targetBits)
906 res = buildLibraryOp<calyx::CombGroupOp, calyx::SliceLibOp>(
907 rewriter, op, {sourceType}, {targetType});
909 res = buildLibraryOp<calyx::CombGroupOp, calyx::PadLibOp>(
910 rewriter, op, {sourceType}, {targetType});
912 rewriter.eraseOp(op);
916 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
917 scf::WhileOp whileOp)
const {
921 getState<ComponentLoweringState>().addBlockScheduleable(
926 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
927 scf::ForOp forOp)
const {
933 std::optional<uint64_t> bound = scfForOp.
getBound();
934 if (!bound.has_value()) {
936 <<
"Loop bound not statically known. Should "
937 "transform into while loop using `--scf-for-to-while` before "
938 "running --lower-scf-to-calyx.";
940 getState<ComponentLoweringState>().addBlockScheduleable(
948 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
949 CallOp callOp)
const {
951 calyx::InstanceOp instanceOp =
952 getState<ComponentLoweringState>().getInstance(instanceName);
953 SmallVector<Value, 4> outputPorts;
954 auto portInfos = instanceOp.getReferencedComponent().getPortInfo();
955 for (
auto [idx, portInfo] : enumerate(portInfos)) {
957 outputPorts.push_back(instanceOp.getResult(idx));
961 for (
auto [idx, result] : llvm::enumerate(callOp.getResults()))
962 rewriter.replaceAllUsesWith(result, outputPorts[idx]);
966 getState<ComponentLoweringState>().addBlockScheduleable(
980 using OpRewritePattern::OpRewritePattern;
983 PatternRewriter &rewriter)
const override {
985 TypeRange yieldTypes = execOp.getResultTypes();
989 rewriter.setInsertionPointAfter(execOp);
990 auto *sinkBlock = rewriter.splitBlock(
992 execOp.getOperation()->getIterator()->getNextNode()->getIterator());
993 sinkBlock->addArguments(
995 SmallVector<Location, 4>(yieldTypes.size(), rewriter.getUnknownLoc()));
996 for (
auto res : enumerate(execOp.getResults()))
997 res.value().replaceAllUsesWith(sinkBlock->getArgument(res.index()));
1001 make_early_inc_range(execOp.getRegion().getOps<scf::YieldOp>())) {
1002 rewriter.setInsertionPointAfter(yieldOp);
1003 rewriter.replaceOpWithNewOp<BranchOp>(yieldOp, sinkBlock,
1004 yieldOp.getOperands());
1008 auto *preBlock = execOp->getBlock();
1009 auto *execOpEntryBlock = &execOp.getRegion().front();
1010 auto *postBlock = execOp->getBlock()->splitBlock(execOp);
1011 rewriter.inlineRegionBefore(execOp.getRegion(), postBlock);
1012 rewriter.mergeBlocks(postBlock, preBlock);
1013 rewriter.eraseOp(execOp);
1016 rewriter.mergeBlocks(execOpEntryBlock, preBlock);
1024 using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
1028 PatternRewriter &rewriter)
const override {
1031 DenseMap<Value, unsigned> funcOpArgRewrites;
1035 DenseMap<unsigned, unsigned> funcOpResultMapping;
1043 DenseMap<Value, std::pair<unsigned, unsigned>> extMemoryCompPortIndices;
1047 SmallVector<calyx::PortInfo> inPorts, outPorts;
1048 FunctionType funcType = funcOp.getFunctionType();
1049 unsigned extMemCounter = 0;
1050 for (
auto arg : enumerate(funcOp.getArguments())) {
1051 if (isa<MemRefType>(arg.value().getType())) {
1054 "ext_mem" + std::to_string(extMemoryCompPortIndices.size());
1055 extMemoryCompPortIndices[arg.value()] = {inPorts.size(),
1058 extMemCounter++, inPorts, outPorts);
1062 if (
auto portNameAttr = funcOp.getArgAttrOfType<StringAttr>(
1064 inName = portNameAttr.str();
1066 inName =
"in" + std::to_string(arg.index());
1067 funcOpArgRewrites[arg.value()] = inPorts.size();
1069 rewriter.getStringAttr(inName),
1075 for (
auto res : enumerate(funcType.getResults())) {
1076 std::string resName;
1077 if (
auto portNameAttr = funcOp.getResultAttrOfType<StringAttr>(
1079 resName = portNameAttr.str();
1081 resName =
"out" + std::to_string(res.index());
1082 funcOpResultMapping[res.index()] = outPorts.size();
1084 rewriter.getStringAttr(resName),
1091 auto ports = inPorts;
1092 llvm::append_range(ports, outPorts);
1096 auto compOp = rewriter.create<calyx::ComponentOp>(
1097 funcOp.getLoc(), rewriter.getStringAttr(funcOp.getSymName()), ports);
1099 std::string funcName =
"func_" + funcOp.getSymName().str();
1100 rewriter.modifyOpInPlace(funcOp, [&]() { funcOp.setSymName(funcName); });
1103 compOp->setAttr(
"toplevel", rewriter.getUnitAttr());
1106 functionMapping[funcOp] = compOp;
1111 for (
auto &mapping : funcOpArgRewrites)
1112 mapping.getFirst().replaceAllUsesWith(
1113 compOp.getArgument(mapping.getSecond()));
1116 for (
auto extMemPortIndices : extMemoryCompPortIndices) {
1120 unsigned inPortsIt = extMemPortIndices.getSecond().first;
1121 unsigned outPortsIt = extMemPortIndices.getSecond().second +
1122 compOp.getInputPortInfo().size();
1123 extMemPorts.
readData = compOp.getArgument(inPortsIt++);
1124 extMemPorts.
done = compOp.getArgument(inPortsIt);
1125 extMemPorts.
writeData = compOp.getArgument(outPortsIt++);
1126 unsigned nAddresses =
1127 cast<MemRefType>(extMemPortIndices.getFirst().getType())
1130 for (
unsigned j = 0; j < nAddresses; ++j)
1131 extMemPorts.
addrPorts.push_back(compOp.getArgument(outPortsIt++));
1132 extMemPorts.
writeEn = compOp.getArgument(outPortsIt);
1136 compState->registerMemoryInterface(extMemPortIndices.getFirst(),
1149 using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
1153 PatternRewriter &rewriter)
const override {
1154 LogicalResult res = success();
1155 funcOp.walk([&](Operation *op) {
1157 if (!isa<scf::WhileOp>(op))
1158 return WalkResult::advance();
1160 auto scfWhileOp = cast<scf::WhileOp>(op);
1163 getState<ComponentLoweringState>().setUniqueName(whileOp.
getOperation(),
1173 enumerate(scfWhileOp.getBefore().front().getArguments())) {
1174 auto condOp = scfWhileOp.getConditionOp().getArgs()[barg.index()];
1175 if (barg.value() != condOp) {
1179 <<
"do-while loops not supported; expected iter-args to "
1180 "remain untransformed in the 'before' region of the "
1182 return WalkResult::interrupt();
1191 for (
auto arg : enumerate(whileOp.
getBodyArgs())) {
1192 std::string name = getState<ComponentLoweringState>()
1195 "_arg" + std::to_string(arg.index());
1198 arg.value().getType().getIntOrFloatBitWidth(), name);
1199 getState<ComponentLoweringState>().addWhileLoopIterReg(whileOp,
reg,
1201 arg.value().replaceAllUsesWith(
reg.getOut());
1205 ->getArgument(arg.index())
1206 .replaceAllUsesWith(
reg.getOut());
1210 SmallVector<calyx::GroupOp> initGroups;
1211 auto numOperands = whileOp.
getOperation()->getNumOperands();
1212 for (
size_t i = 0; i < numOperands; ++i) {
1214 getState<ComponentLoweringState>().buildWhileLoopIterArgAssignments(
1216 getState<ComponentLoweringState>().getComponentOp(),
1217 getState<ComponentLoweringState>().getUniqueName(
1219 "_init_" + std::to_string(i),
1221 initGroups.push_back(initGroupOp);
1224 getState<ComponentLoweringState>().setWhileLoopInitGroups(whileOp,
1227 return WalkResult::advance();
1237 using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
1241 PatternRewriter &rewriter)
const override {
1242 LogicalResult res = success();
1243 funcOp.walk([&](Operation *op) {
1245 if (!isa<scf::ForOp>(op))
1246 return WalkResult::advance();
1248 auto scfForOp = cast<scf::ForOp>(op);
1251 getState<ComponentLoweringState>().setUniqueName(forOp.
getOperation(),
1256 auto inductionVar = forOp.
getOperation().getInductionVar();
1257 SmallVector<std::string, 3> inductionVarIdentifiers = {
1258 getState<ComponentLoweringState>()
1261 "induction",
"var"};
1262 std::string name = llvm::join(inductionVarIdentifiers,
"_");
1265 inductionVar.getType().getIntOrFloatBitWidth(), name);
1266 getState<ComponentLoweringState>().addForLoopIterReg(forOp,
reg, 0);
1267 inductionVar.replaceAllUsesWith(
reg.getOut());
1270 calyx::ComponentOp componentOp =
1271 getState<ComponentLoweringState>().getComponentOp();
1272 SmallVector<calyx::GroupOp> initGroups;
1273 SmallVector<std::string, 4> groupIdentifiers = {
1275 getState<ComponentLoweringState>()
1278 "induction",
"var"};
1279 std::string groupName = llvm::join(groupIdentifiers,
"_");
1280 auto groupOp = calyx::createGroup<calyx::GroupOp>(
1281 rewriter, componentOp, forOp.
getLoc(), groupName);
1284 initGroups.push_back(groupOp);
1285 getState<ComponentLoweringState>().setForLoopInitGroups(forOp,
1288 return WalkResult::advance();
1300 using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
1304 PatternRewriter &rewriter)
const override {
1305 auto *entryBlock = &funcOp.getBlocks().front();
1306 rewriter.setInsertionPointToStart(
1308 auto topLevelSeqOp = rewriter.create<calyx::SeqOp>(funcOp.getLoc());
1309 DenseSet<Block *> path;
1310 return buildCFGControl(path, rewriter, topLevelSeqOp.getBodyBlock(),
1311 nullptr, entryBlock);
1318 const DenseSet<Block *> &path,
1319 mlir::Block *parentCtrlBlock,
1320 mlir::Block *block)
const {
1321 auto compBlockScheduleables =
1322 getState<ComponentLoweringState>().getBlockScheduleables(block);
1323 auto loc = block->front().getLoc();
1325 if (compBlockScheduleables.size() > 1) {
1326 auto seqOp = rewriter.create<calyx::SeqOp>(loc);
1327 parentCtrlBlock = seqOp.getBodyBlock();
1330 for (
auto &group : compBlockScheduleables) {
1331 rewriter.setInsertionPointToEnd(parentCtrlBlock);
1332 if (
auto groupPtr = std::get_if<calyx::GroupOp>(&group); groupPtr) {
1333 rewriter.create<calyx::EnableOp>(groupPtr->getLoc(),
1334 groupPtr->getSymName());
1335 }
else if (
auto whileSchedPtr = std::get_if<WhileScheduleable>(&group);
1337 auto &whileOp = whileSchedPtr->whileOp;
1339 auto whileCtrlOp = buildWhileCtrlOp(
1341 getState<ComponentLoweringState>().getWhileLoopInitGroups(whileOp),
1343 rewriter.setInsertionPointToEnd(whileCtrlOp.getBodyBlock());
1345 rewriter.create<calyx::SeqOp>(whileOp.getOperation()->getLoc());
1346 auto *whileBodyOpBlock = whileBodyOp.getBodyBlock();
1350 LogicalResult res = buildCFGControl(path, rewriter, whileBodyOpBlock,
1351 block, whileOp.getBodyBlock());
1354 rewriter.setInsertionPointToEnd(whileBodyOpBlock);
1355 calyx::GroupOp whileLatchGroup =
1356 getState<ComponentLoweringState>().getWhileLoopLatchGroup(whileOp);
1357 rewriter.create<calyx::EnableOp>(whileLatchGroup.getLoc(),
1358 whileLatchGroup.getName());
1362 }
else if (
auto *forSchedPtr = std::get_if<ForScheduleable>(&group);
1364 auto forOp = forSchedPtr->forOp;
1366 auto forCtrlOp = buildForCtrlOp(
1368 getState<ComponentLoweringState>().getForLoopInitGroups(forOp),
1369 forSchedPtr->bound, rewriter);
1370 rewriter.setInsertionPointToEnd(forCtrlOp.getBodyBlock());
1372 rewriter.create<calyx::SeqOp>(forOp.getOperation()->getLoc());
1373 auto *forBodyOpBlock = forBodyOp.getBodyBlock();
1376 LogicalResult res = buildCFGControl(path, rewriter, forBodyOpBlock,
1377 block, forOp.getBodyBlock());
1380 rewriter.setInsertionPointToEnd(forBodyOpBlock);
1381 calyx::GroupOp forLatchGroup =
1382 getState<ComponentLoweringState>().getForLoopLatchGroup(forOp);
1383 rewriter.create<calyx::EnableOp>(forLatchGroup.getLoc(),
1384 forLatchGroup.getName());
1387 }
else if (
auto *callSchedPtr = std::get_if<CallScheduleable>(&group)) {
1388 auto instanceOp = callSchedPtr->instanceOp;
1389 OpBuilder::InsertionGuard g(rewriter);
1390 auto callBody = rewriter.create<calyx::SeqOp>(instanceOp.getLoc());
1391 rewriter.setInsertionPointToStart(callBody.getBodyBlock());
1392 std::string initGroupName =
"init_" + instanceOp.getSymName().str();
1393 rewriter.create<calyx::EnableOp>(instanceOp.getLoc(), initGroupName);
1394 SmallVector<Value, 4> instancePorts;
1395 auto inputPorts = callSchedPtr->callOp.getOperands();
1396 llvm::copy(instanceOp.getResults().take_front(inputPorts.size()),
1397 std::back_inserter(instancePorts));
1398 rewriter.create<calyx::InvokeOp>(
1399 instanceOp.getLoc(), instanceOp.getSymName(), instancePorts,
1403 llvm_unreachable(
"Unknown scheduleable");
1414 const DenseSet<Block *> &path, Location loc,
1415 Block *from, Block *to,
1416 Block *parentCtrlBlock)
const {
1419 rewriter.setInsertionPointToEnd(parentCtrlBlock);
1420 auto preSeqOp = rewriter.create<calyx::SeqOp>(loc);
1421 rewriter.setInsertionPointToEnd(preSeqOp.getBodyBlock());
1423 getState<ComponentLoweringState>().getBlockArgGroups(from, to))
1424 rewriter.create<calyx::EnableOp>(barg.getLoc(), barg.getSymName());
1426 return buildCFGControl(path, rewriter, parentCtrlBlock, from, to);
1430 PatternRewriter &rewriter,
1431 mlir::Block *parentCtrlBlock,
1432 mlir::Block *preBlock,
1433 mlir::Block *block)
const {
1434 if (path.count(block) != 0)
1435 return preBlock->getTerminator()->emitError()
1436 <<
"CFG backedge detected. Loops must be raised to 'scf.while' or "
1437 "'scf.for' operations.";
1439 rewriter.setInsertionPointToEnd(parentCtrlBlock);
1440 LogicalResult bbSchedResult =
1441 scheduleBasicBlock(rewriter, path, parentCtrlBlock, block);
1442 if (bbSchedResult.failed())
1443 return bbSchedResult;
1446 auto successors = block->getSuccessors();
1447 auto nSuccessors = successors.size();
1448 if (nSuccessors > 0) {
1449 auto brOp = dyn_cast<BranchOpInterface>(block->getTerminator());
1451 if (nSuccessors > 1) {
1455 assert(nSuccessors == 2 &&
1456 "only conditional branches supported for now...");
1458 auto cond = brOp->getOperand(0);
1459 auto condGroup = getState<ComponentLoweringState>()
1460 .getEvaluatingGroup<calyx::CombGroupOp>(cond);
1464 auto ifOp = rewriter.create<calyx::IfOp>(
1465 brOp->getLoc(), cond, symbolAttr,
true);
1466 rewriter.setInsertionPointToStart(ifOp.getThenBody());
1467 auto thenSeqOp = rewriter.create<calyx::SeqOp>(brOp.getLoc());
1468 rewriter.setInsertionPointToStart(ifOp.getElseBody());
1469 auto elseSeqOp = rewriter.create<calyx::SeqOp>(brOp.getLoc());
1471 bool trueBrSchedSuccess =
1472 schedulePath(rewriter, path, brOp.getLoc(), block, successors[0],
1473 thenSeqOp.getBodyBlock())
1475 bool falseBrSchedSuccess =
true;
1476 if (trueBrSchedSuccess) {
1477 falseBrSchedSuccess =
1478 schedulePath(rewriter, path, brOp.getLoc(), block, successors[1],
1479 elseSeqOp.getBodyBlock())
1483 return success(trueBrSchedSuccess && falseBrSchedSuccess);
1486 return schedulePath(rewriter, path, brOp.getLoc(), block,
1487 successors.front(), parentCtrlBlock);
1497 const SmallVector<calyx::GroupOp> &initGroups)
const {
1498 PatternRewriter::InsertionGuard g(rewriter);
1499 auto parOp = rewriter.create<calyx::ParOp>(loc);
1500 rewriter.setInsertionPointToStart(parOp.getBodyBlock());
1501 for (calyx::GroupOp group : initGroups)
1502 rewriter.create<calyx::EnableOp>(group.getLoc(), group.getName());
1506 SmallVector<calyx::GroupOp> initGroups,
1507 PatternRewriter &rewriter)
const {
1508 Location loc = whileOp.
getLoc();
1511 insertParInitGroups(rewriter, loc, initGroups);
1515 auto condGroup = getState<ComponentLoweringState>()
1516 .getEvaluatingGroup<calyx::CombGroupOp>(cond);
1519 return rewriter.create<calyx::WhileOp>(loc, cond, symbolAttr);
1523 SmallVector<calyx::GroupOp>
const &initGroups,
1525 PatternRewriter &rewriter)
const {
1526 Location loc = forOp.
getLoc();
1529 insertParInitGroups(rewriter, loc, initGroups);
1532 return rewriter.create<calyx::RepeatOp>(loc, bound);
1539 using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
1542 PatternRewriter &)
const override {
1543 funcOp.walk([&](scf::WhileOp op) {
1552 getState<ComponentLoweringState>().getWhileLoopIterRegs(whileOp))
1553 whileOp.
getOperation()->getResults()[res.first].replaceAllUsesWith(
1554 res.second.getOut());
1557 funcOp.walk([&](memref::LoadOp loadOp) {
1563 loadOp.getResult().replaceAllUsesWith(
1564 getState<ComponentLoweringState>()
1565 .getMemoryInterface(loadOp.getMemref())
1576 using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
1579 PatternRewriter &rewriter)
const override {
1580 rewriter.eraseOp(funcOp);
1586 PatternRewriter &rewriter)
const override {
1600 class SCFToCalyxPass :
public circt::impl::SCFToCalyxBase<SCFToCalyxPass> {
1604 void runOnOperation()
override;
1606 LogicalResult setTopLevelFunction(mlir::ModuleOp moduleOp,
1607 std::string &topLevelFunction) {
1608 if (!topLevelFunctionOpt.empty()) {
1609 if (SymbolTable::lookupSymbolIn(moduleOp, topLevelFunctionOpt) ==
1611 moduleOp.emitError() <<
"Top level function '" << topLevelFunctionOpt
1612 <<
"' not found in module.";
1615 topLevelFunction = topLevelFunctionOpt;
1619 auto funcOps = moduleOp.getOps<FuncOp>();
1620 if (std::distance(funcOps.begin(), funcOps.end()) == 1)
1621 topLevelFunction = (*funcOps.begin()).getSymName().str();
1623 moduleOp.emitError()
1624 <<
"Module contains multiple functions, but no top level "
1625 "function was set. Please see --top-level-function";
1632 struct LoweringPattern {
1633 enum class Strategy { Once, Greedy };
1642 LogicalResult labelEntryPoint(StringRef topLevelFunction) {
1646 using OpRewritePattern::OpRewritePattern;
1647 LogicalResult matchAndRewrite(mlir::ModuleOp,
1648 PatternRewriter &)
const override {
1653 ConversionTarget target(getContext());
1654 target.addLegalDialect<calyx::CalyxDialect>();
1655 target.addLegalDialect<scf::SCFDialect>();
1656 target.addIllegalDialect<hw::HWDialect>();
1657 target.addIllegalDialect<comb::CombDialect>();
1660 target.addIllegalDialect<FuncDialect>();
1661 target.addIllegalDialect<ArithDialect>();
1662 target.addLegalOp<AddIOp, SelectOp, SubIOp, CmpIOp, ShLIOp, ShRUIOp,
1663 ShRSIOp, AndIOp, XOrIOp, OrIOp, ExtUIOp, TruncIOp,
1664 CondBranchOp, BranchOp, MulIOp, DivUIOp, DivSIOp, RemUIOp,
1665 RemSIOp, ReturnOp, arith::ConstantOp, IndexCastOp, FuncOp,
1668 RewritePatternSet legalizePatterns(&getContext());
1669 legalizePatterns.add<DummyPattern>(&getContext());
1670 DenseSet<Operation *> legalizedOps;
1671 if (applyPartialConversion(getOperation(), target,
1672 std::move(legalizePatterns))
1683 template <
typename TPattern,
typename... PatternArgs>
1684 void addOncePattern(SmallVectorImpl<LoweringPattern> &
patterns,
1685 PatternArgs &&...args) {
1686 RewritePatternSet ps(&getContext());
1689 LoweringPattern{std::move(ps), LoweringPattern::Strategy::Once});
1692 template <
typename TPattern,
typename... PatternArgs>
1693 void addGreedyPattern(SmallVectorImpl<LoweringPattern> &
patterns,
1694 PatternArgs &&...args) {
1695 RewritePatternSet ps(&getContext());
1696 ps.add<TPattern>(&getContext(), args...);
1698 LoweringPattern{std::move(ps), LoweringPattern::Strategy::Greedy});
1701 LogicalResult runPartialPattern(RewritePatternSet &
pattern,
bool runOnce) {
1703 "Should only apply 1 partial lowering pattern at once");
1709 GreedyRewriteConfig config;
1710 config.enableRegionSimplification =
1711 mlir::GreedySimplifyRegionLevel::Disabled;
1713 config.maxIterations = 1;
1718 (void)applyPatternsAndFoldGreedily(getOperation(), std::move(
pattern),
1728 void SCFToCalyxPass::runOnOperation() {
1733 std::string topLevelFunction;
1734 if (failed(setTopLevelFunction(getOperation(), topLevelFunction))) {
1735 signalPassFailure();
1740 if (failed(labelEntryPoint(topLevelFunction))) {
1741 signalPassFailure();
1744 loweringState = std::make_shared<calyx::CalyxLoweringState>(getOperation(),
1755 DenseMap<FuncOp, calyx::ComponentOp> funcMap;
1756 SmallVector<LoweringPattern, 8> loweringPatterns;
1760 addOncePattern<FuncOpConversion>(loweringPatterns, patternState, funcMap,
1764 addGreedyPattern<InlineExecuteRegionOpPattern>(loweringPatterns);
1767 addOncePattern<calyx::ConvertIndexTypes>(loweringPatterns, patternState,
1771 addOncePattern<calyx::BuildBasicBlockRegs>(loweringPatterns, patternState,
1774 addOncePattern<calyx::BuildCallInstance>(loweringPatterns, patternState,
1778 addOncePattern<calyx::BuildReturnRegs>(loweringPatterns, patternState,
1784 addOncePattern<BuildWhileGroups>(loweringPatterns, patternState, funcMap,
1790 addOncePattern<BuildForGroups>(loweringPatterns, patternState, funcMap,
1800 addOncePattern<BuildOpGroups>(loweringPatterns, patternState, funcMap,
1806 addOncePattern<BuildControl>(loweringPatterns, patternState, funcMap,
1811 addOncePattern<calyx::InlineCombGroups>(loweringPatterns, patternState,
1816 addOncePattern<LateSSAReplacement>(loweringPatterns, patternState, funcMap,
1822 addGreedyPattern<calyx::EliminateUnusedCombGroups>(loweringPatterns);
1826 addOncePattern<calyx::RewriteMemoryAccesses>(loweringPatterns, patternState,
1831 addOncePattern<CleanupFuncOps>(loweringPatterns, patternState, funcMap,
1835 for (
auto &pat : loweringPatterns) {
1838 pat.strategy == LoweringPattern::Strategy::Once);
1841 signalPassFailure();
1848 RewritePatternSet cleanupPatterns(&getContext());
1849 cleanupPatterns.add<calyx::MultipleGroupDonePattern,
1850 calyx::NonTerminatingGroupDonePattern>(&getContext());
1851 if (failed(applyPatternsAndFoldGreedily(getOperation(),
1852 std::move(cleanupPatterns)))) {
1853 signalPassFailure();
1857 if (ciderSourceLocationMetadata) {
1860 SmallVector<Attribute, 16> sourceLocations;
1861 getOperation()->walk([&](calyx::ComponentOp component) {
1865 MLIRContext *context = getOperation()->getContext();
1866 getOperation()->setAttr(
"calyx.metadata",
1877 return std::make_unique<SCFToCalyxPass>();
assert(baseType &&"element must be base type")
static Block * getBodyBlock(FModuleLike mod)
RewritePatternSet pattern
std::shared_ptr< calyx::CalyxLoweringState > loweringState
LogicalResult partialPatternRes
void setFuncOpResultMapping(const DenseMap< unsigned, unsigned > &mapping)
Assign a mapping between the source funcOp result indices and the corresponding output port indices o...
std::string getUniqueName(StringRef prefix)
Returns a unique name within compOp with the provided prefix.
void registerMemoryInterface(Value memref, const calyx::MemoryInterface &memoryInterface)
Registers a memory interface as being associated with a memory identified by 'memref'.
calyx::ComponentOp getComponentOp()
Returns the calyx::ComponentOp associated with this lowering state.
FuncOpPartialLoweringPatterns are patterns which intend to match on FuncOps and then perform their ow...
Location getLoc() override
Holds common utilities used for scheduling when lowering to Calyx.
Location getLoc() override
Builds a control schedule by traversing the CFG of the function and associating this with the previou...
calyx::RepeatOp buildForCtrlOp(ScfForOp forOp, SmallVector< calyx::GroupOp > const &initGroups, uint64_t bound, PatternRewriter &rewriter) const
LogicalResult partiallyLowerFuncToComp(FuncOp funcOp, PatternRewriter &rewriter) const override
LogicalResult schedulePath(PatternRewriter &rewriter, const DenseSet< Block * > &path, Location loc, Block *from, Block *to, Block *parentCtrlBlock) const
Schedules a block by inserting a branch argument assignment block (if any) before recursing into the ...
calyx::WhileOp buildWhileCtrlOp(ScfWhileOp whileOp, SmallVector< calyx::GroupOp > initGroups, PatternRewriter &rewriter) const
LogicalResult scheduleBasicBlock(PatternRewriter &rewriter, const DenseSet< Block * > &path, mlir::Block *parentCtrlBlock, mlir::Block *block) const
Sequentially schedules the groups that registered themselves with 'block'.
LogicalResult buildCFGControl(DenseSet< Block * > path, PatternRewriter &rewriter, mlir::Block *parentCtrlBlock, mlir::Block *preBlock, mlir::Block *block) const
void insertParInitGroups(PatternRewriter &rewriter, Location loc, const SmallVector< calyx::GroupOp > &initGroups) const
In BuildForGroups, a register is created for the iteration argument of the for op.
LogicalResult partiallyLowerFuncToComp(FuncOp funcOp, PatternRewriter &rewriter) const override
Iterate through the operations of a source function and instantiate components or primitives based on...
TGroupOp createGroupForOp(PatternRewriter &rewriter, Operation *op) const
Creates a group named by the basic block which the input op resides in.
LogicalResult buildLibraryOp(PatternRewriter &rewriter, TSrcOp op) const
buildLibraryOp which provides in- and output types based on the operands and results of the op argume...
void assignAddressPorts(PatternRewriter &rewriter, Location loc, calyx::GroupInterface group, calyx::MemoryInterface memoryInterface, Operation::operand_range addressValues) const
Creates assignments within the provided group to the address ports of the memoryOp based on the provi...
LogicalResult buildLibraryOp(PatternRewriter &rewriter, TSrcOp op, TypeRange srcTypes, TypeRange dstTypes) const
buildLibraryOp will build a TCalyxLibOp inside a TGroupOp based on the source operation TSrcOp.
LogicalResult partiallyLowerFuncToComp(FuncOp funcOp, PatternRewriter &rewriter) const override
LogicalResult buildLibraryBinaryPipeOp(PatternRewriter &rewriter, TSrcOp op, TOpType opPipe, Value out) const
buildLibraryBinaryPipeOp will build a TCalyxLibBinaryPipeOp, to deal with MulIOp, DivUIOp and RemUIOp...
In BuildWhileGroups, a register is created for each iteration argumenet of the while op.
LogicalResult partiallyLowerFuncToComp(FuncOp funcOp, PatternRewriter &rewriter) const override
Erases FuncOp operations.
LogicalResult matchAndRewrite(FuncOp funcOp, PatternRewriter &rewriter) const override
LogicalResult partiallyLowerFuncToComp(FuncOp funcOp, PatternRewriter &rewriter) const override
Handles the current state of lowering of a Calyx component.
ComponentLoweringState(calyx::ComponentOp component)
void setForLoopInitGroups(ScfForOp op, SmallVector< calyx::GroupOp > groups)
calyx::GroupOp buildForLoopIterArgAssignments(OpBuilder &builder, ScfForOp op, calyx::ComponentOp componentOp, Twine uniqueSuffix, MutableArrayRef< OpOperand > ops)
void setForLoopLatchGroup(ScfForOp op, calyx::GroupOp group)
SmallVector< calyx::GroupOp > getForLoopInitGroups(ScfForOp op)
void addForLoopIterReg(ScfForOp op, calyx::RegisterOp reg, unsigned idx)
calyx::GroupOp getForLoopLatchGroup(ScfForOp op)
calyx::RegisterOp getForLoopIterReg(ScfForOp op, unsigned idx)
const DenseMap< unsigned, calyx::RegisterOp > & getForLoopIterRegs(ScfForOp op)
Inlines Calyx ExecuteRegionOp operations within their parent blocks.
LogicalResult matchAndRewrite(scf::ExecuteRegionOp execOp, PatternRewriter &rewriter) const override
LateSSAReplacement contains various functions for replacing SSA values that were not replaced during ...
LogicalResult partiallyLowerFuncToComp(FuncOp funcOp, PatternRewriter &) const override
std::optional< int64_t > getBound() override
Block * getBodyBlock() override
Block::BlockArgListType getBodyArgs() override
ScfWhileOp(scf::WhileOp op)
Block::BlockArgListType getBodyArgs() override
Block * getConditionBlock() override
std::optional< int64_t > getBound() override
Block * getBodyBlock() override
Value getConditionValue() override
calyx::GroupOp buildWhileLoopIterArgAssignments(OpBuilder &builder, ScfWhileOp op, calyx::ComponentOp componentOp, Twine uniqueSuffix, MutableArrayRef< OpOperand > ops)
void setWhileLoopInitGroups(ScfWhileOp op, SmallVector< calyx::GroupOp > groups)
SmallVector< calyx::GroupOp > getWhileLoopInitGroups(ScfWhileOp op)
void addWhileLoopIterReg(ScfWhileOp op, calyx::RegisterOp reg, unsigned idx)
const DenseMap< unsigned, calyx::RegisterOp > & getWhileLoopIterRegs(ScfWhileOp op)
void setWhileLoopLatchGroup(ScfWhileOp op, calyx::GroupOp group)
calyx::GroupOp getWhileLoopLatchGroup(ScfWhileOp op)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
void addMandatoryComponentPorts(PatternRewriter &rewriter, SmallVectorImpl< calyx::PortInfo > &ports)
void appendPortsForExternalMemref(PatternRewriter &rewriter, StringRef memName, Value memref, unsigned memoryID, SmallVectorImpl< calyx::PortInfo > &inPorts, SmallVectorImpl< calyx::PortInfo > &outPorts)
void buildAssignmentsForRegisterWrite(OpBuilder &builder, calyx::GroupOp groupOp, calyx::ComponentOp componentOp, calyx::RegisterOp ®, Value inputValue)
Creates register assignment operations within the provided groupOp.
DenseMap< const mlir::RewritePattern *, SmallPtrSet< Operation *, 16 > > PatternApplicationState
Extra state that is passed to all PartialLoweringPatterns so they can record when they have run on an...
Type convIndexType(OpBuilder &builder, Type type)
LogicalResult applyModuleOpConversion(mlir::ModuleOp, StringRef topLevelFunction)
Helper to update the top-level ModuleOp to set the entrypoing function.
WalkResult getCiderSourceLocationMetadata(calyx::ComponentOp component, SmallVectorImpl< Attribute > &sourceLocations)
bool matchConstantOp(Operation *op, APInt &value)
unsigned handleZeroWidth(int64_t dim)
hw::ConstantOp createConstant(Location loc, OpBuilder &builder, ComponentOp component, size_t width, size_t value)
A helper function to create constants in the HW dialect.
calyx::RegisterOp createRegister(Location loc, OpBuilder &builder, ComponentOp component, size_t width, Twine prefix)
Creates a RegisterOp, with input and output port bit widths defined by width.
bool noStoresToMemory(Value memoryReference)
bool singleLoadFromMemory(Value memoryReference)
std::string getInstanceName(mlir::func::CallOp callOp)
A helper function to get the instance name.
Value createOrFoldNot(Location loc, Value value, OpBuilder &builder, bool twoState=false)
Create a `‘Not’' gate on a value.
static constexpr std::string_view sPortNameAttr
std::variant< calyx::GroupOp, WhileScheduleable, ForScheduleable, CallScheduleable > Scheduleable
A variant of types representing scheduleable operations.
static LogicalResult buildAllocOp(ComponentLoweringState &componentState, PatternRewriter &rewriter, TAllocOp allocOp)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< OperationPass< ModuleOp > > createSCFToCalyxPass()
Create an SCF to Calyx conversion pass.
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
std::optional< Value > done
std::optional< Value > writeEn
std::optional< Value > writeData
std::optional< Value > readData
SmallVector< Value > addrPorts
This holds information about the port for either a Component or Cell.
calyx::InstanceOp instanceOp
Instance for invoking.
ScfForOp forOp
For operation to schedule.
Creates a new Calyx component for each FuncOp in the program.
LogicalResult partiallyLowerFuncToComp(FuncOp funcOp, PatternRewriter &rewriter) const override
ScfWhileOp whileOp
While operation to schedule.