14 #include "../PassDetail.h"
20 #include "mlir/Conversion/LLVMCommon/ConversionTarget.h"
21 #include "mlir/Conversion/LLVMCommon/Pattern.h"
22 #include "mlir/Dialect/Arith/IR/Arith.h"
23 #include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
24 #include "mlir/Dialect/Func/IR/FuncOps.h"
25 #include "mlir/Dialect/MemRef/IR/MemRef.h"
26 #include "mlir/Dialect/SCF/IR/SCF.h"
27 #include "mlir/IR/AsmState.h"
28 #include "mlir/IR/Matchers.h"
29 #include "mlir/Support/LogicalResult.h"
30 #include "mlir/Transforms/GreedyPatternRewriteDriver.h"
31 #include "llvm/ADT/TypeSwitch.h"
42 class ComponentLoweringStateInterface;
43 namespace scftocalyx {
52 : calyx::WhileOpInterface<scf::WhileOp>(op) {}
55 return getOperation().getAfterArguments();
58 Block *
getBodyBlock()
override {
return &getOperation().getAfter().front(); }
61 return &getOperation().getBefore().front();
65 return getOperation().getConditionOp().getOperand(0);
68 std::optional<int64_t>
getBound()
override {
return std::nullopt; }
73 explicit ScfForOp(scf::ForOp op) : calyx::RepeatOpInterface<scf::ForOp>(op) {}
76 return getOperation().getRegion().getArguments();
80 return &getOperation().getRegion().getBlocks().front();
84 return constantTripCount(getOperation().getLowerBound(),
85 getOperation().getUpperBound(),
86 getOperation().getStep());
121 return getLoopInitGroups(std::move(op));
125 Twine uniqueSuffix, MutableArrayRef<OpOperand> ops) {
126 return buildLoopIterArgAssignments(
builder, std::move(op), componentOp,
130 return addLoopIterReg(std::move(op),
reg, idx);
132 const DenseMap<unsigned, calyx::RegisterOp> &
134 return getLoopIterRegs(std::move(op));
137 return setLoopLatchGroup(std::move(op), group);
140 return getLoopLatchGroup(std::move(op));
143 SmallVector<calyx::GroupOp> groups) {
144 return setLoopInitGroups(std::move(op), std::move(groups));
152 return getLoopInitGroups(std::move(op));
156 Twine uniqueSuffix, MutableArrayRef<OpOperand> ops) {
157 return buildLoopIterArgAssignments(
builder, std::move(op), componentOp,
161 return addLoopIterReg(std::move(op),
reg, idx);
164 return getLoopIterRegs(std::move(op));
167 return getLoopIterReg(std::move(op), idx);
170 return setLoopLatchGroup(std::move(op), group);
173 return getLoopLatchGroup(std::move(op));
176 return setLoopInitGroups(std::move(op), std::move(groups));
189 : calyx::ComponentLoweringStateInterface(component) {}
199 using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
203 PatternRewriter &rewriter)
const override {
206 bool opBuiltSuccessfully =
true;
207 funcOp.walk([&](Operation *_op) {
208 opBuiltSuccessfully &=
209 TypeSwitch<mlir::Operation *, bool>(_op)
210 .template Case<arith::ConstantOp, ReturnOp, BranchOpInterface,
212 scf::YieldOp, scf::WhileOp, scf::ForOp,
214 memref::AllocOp, memref::AllocaOp, memref::LoadOp,
217 AddIOp, SubIOp, CmpIOp, ShLIOp, ShRUIOp, ShRSIOp,
218 AndIOp, XOrIOp, OrIOp, ExtUIOp, ExtSIOp, TruncIOp,
219 MulIOp, DivUIOp, DivSIOp, RemUIOp, RemSIOp,
220 SelectOp, IndexCastOp, CallOp>(
221 [&](
auto op) {
return buildOp(rewriter, op).succeeded(); })
222 .
template Case<FuncOp, scf::ConditionOp>([&](
auto) {
226 .Default([&](
auto op) {
227 op->emitError() <<
"Unhandled operation during BuildOpGroups()";
231 return opBuiltSuccessfully ? WalkResult::advance()
232 : WalkResult::interrupt();
235 return success(opBuiltSuccessfully);
240 LogicalResult buildOp(PatternRewriter &rewriter, scf::YieldOp yieldOp)
const;
241 LogicalResult buildOp(PatternRewriter &rewriter,
242 BranchOpInterface brOp)
const;
243 LogicalResult buildOp(PatternRewriter &rewriter,
244 arith::ConstantOp constOp)
const;
245 LogicalResult buildOp(PatternRewriter &rewriter, SelectOp op)
const;
246 LogicalResult buildOp(PatternRewriter &rewriter, AddIOp op)
const;
247 LogicalResult buildOp(PatternRewriter &rewriter, SubIOp op)
const;
248 LogicalResult buildOp(PatternRewriter &rewriter, MulIOp op)
const;
249 LogicalResult buildOp(PatternRewriter &rewriter, DivUIOp op)
const;
250 LogicalResult buildOp(PatternRewriter &rewriter, DivSIOp op)
const;
251 LogicalResult buildOp(PatternRewriter &rewriter, RemUIOp op)
const;
252 LogicalResult buildOp(PatternRewriter &rewriter, RemSIOp op)
const;
253 LogicalResult buildOp(PatternRewriter &rewriter, ShRUIOp op)
const;
254 LogicalResult buildOp(PatternRewriter &rewriter, ShRSIOp op)
const;
255 LogicalResult buildOp(PatternRewriter &rewriter, ShLIOp op)
const;
256 LogicalResult buildOp(PatternRewriter &rewriter, AndIOp op)
const;
257 LogicalResult buildOp(PatternRewriter &rewriter, OrIOp op)
const;
258 LogicalResult buildOp(PatternRewriter &rewriter, XOrIOp op)
const;
259 LogicalResult buildOp(PatternRewriter &rewriter, CmpIOp op)
const;
260 LogicalResult buildOp(PatternRewriter &rewriter, TruncIOp op)
const;
261 LogicalResult buildOp(PatternRewriter &rewriter, ExtUIOp op)
const;
262 LogicalResult buildOp(PatternRewriter &rewriter, ExtSIOp op)
const;
263 LogicalResult buildOp(PatternRewriter &rewriter, ReturnOp op)
const;
264 LogicalResult buildOp(PatternRewriter &rewriter, IndexCastOp op)
const;
265 LogicalResult buildOp(PatternRewriter &rewriter, memref::AllocOp op)
const;
266 LogicalResult buildOp(PatternRewriter &rewriter, memref::AllocaOp op)
const;
267 LogicalResult buildOp(PatternRewriter &rewriter, memref::LoadOp op)
const;
268 LogicalResult buildOp(PatternRewriter &rewriter, memref::StoreOp op)
const;
269 LogicalResult buildOp(PatternRewriter &rewriter, scf::WhileOp whileOp)
const;
270 LogicalResult buildOp(PatternRewriter &rewriter, scf::ForOp forOp)
const;
271 LogicalResult buildOp(PatternRewriter &rewriter, CallOp callOp)
const;
275 template <
typename TGroupOp,
typename TCalyxLibOp,
typename TSrcOp>
277 TypeRange srcTypes, TypeRange dstTypes)
const {
278 SmallVector<Type> types;
279 llvm::append_range(types, srcTypes);
280 llvm::append_range(types, dstTypes);
283 getState<ComponentLoweringState>().getNewLibraryOpInstance<TCalyxLibOp>(
284 rewriter, op.getLoc(), types);
286 auto directions = calyxOp.portDirections();
287 SmallVector<Value, 4> opInputPorts;
288 SmallVector<Value, 4> opOutputPorts;
289 for (
auto dir : enumerate(directions)) {
291 opInputPorts.push_back(calyxOp.getResult(dir.index()));
293 opOutputPorts.push_back(calyxOp.getResult(dir.index()));
296 opInputPorts.size() == op->getNumOperands() &&
297 opOutputPorts.size() == op->getNumResults() &&
298 "Expected an equal number of in/out ports in the Calyx library op with "
299 "respect to the number of operands/results of the source operation.");
302 auto group = createGroupForOp<TGroupOp>(rewriter, op);
303 rewriter.setInsertionPointToEnd(group.getBodyBlock());
304 for (
auto dstOp : enumerate(opInputPorts))
305 rewriter.create<calyx::AssignOp>(op.getLoc(), dstOp.value(),
306 op->getOperand(dstOp.index()));
309 for (
auto res : enumerate(opOutputPorts)) {
310 getState<ComponentLoweringState>().registerEvaluatingGroup(res.value(),
312 op->getResult(res.index()).replaceAllUsesWith(res.value());
319 template <
typename TGroupOp,
typename TCalyxLibOp,
typename TSrcOp>
321 return buildLibraryOp<TGroupOp, TCalyxLibOp, TSrcOp>(
322 rewriter, op, op.getOperandTypes(), op->getResultTypes());
326 template <
typename TGroupOp>
328 Block *block = op->getBlock();
329 auto groupName = getState<ComponentLoweringState>().getUniqueName(
330 loweringState().blockName(block));
331 return calyx::createGroup<TGroupOp>(
332 rewriter, getState<ComponentLoweringState>().getComponentOp(),
333 op->getLoc(), groupName);
338 template <
typename TOpType,
typename TSrcOp>
340 TOpType opPipe, Value out)
const {
341 StringRef opName = TSrcOp::getOperationName().split(
".").second;
342 Location loc = op.getLoc();
343 Type
width = op.getResult().getType();
345 op.getResult().replaceAllUsesWith(out);
347 op.getLoc(), rewriter, getComponent(),
width.getIntOrFloatBitWidth(),
348 getState<ComponentLoweringState>().getUniqueName(opName));
350 auto group = createGroupForOp<calyx::GroupOp>(rewriter, op);
351 OpBuilder
builder(group->getRegion(0));
352 getState<ComponentLoweringState>().addBlockScheduleable(op->getBlock(),
355 rewriter.setInsertionPointToEnd(group.getBodyBlock());
356 rewriter.create<calyx::AssignOp>(loc, opPipe.getLeft(), op.getLhs());
357 rewriter.create<calyx::AssignOp>(loc, opPipe.getRight(), op.getRhs());
359 rewriter.create<calyx::AssignOp>(loc,
reg.getIn(), out);
361 rewriter.create<calyx::AssignOp>(loc,
reg.getWriteEn(), opPipe.getDone());
366 rewriter.create<calyx::AssignOp>(
367 loc, opPipe.getGo(), c1,
370 rewriter.create<calyx::GroupDoneOp>(loc,
reg.getDone());
373 getState<ComponentLoweringState>().registerEvaluatingGroup(out, group);
374 getState<ComponentLoweringState>().registerEvaluatingGroup(opPipe.getLeft(),
376 getState<ComponentLoweringState>().registerEvaluatingGroup(
377 opPipe.getRight(), group);
385 calyx::GroupInterface group,
387 Operation::operand_range addressValues)
const {
388 IRRewriter::InsertionGuard guard(rewriter);
389 rewriter.setInsertionPointToEnd(group.getBody());
390 auto addrPorts = memoryInterface.
addrPorts();
391 if (addressValues.empty()) {
393 addrPorts.size() == 1 &&
394 "We expected a 1 dimensional memory of size 1 because there were no "
395 "address assignment values");
397 rewriter.create<calyx::AssignOp>(
401 assert(addrPorts.size() == addressValues.size() &&
402 "Mismatch between number of address ports of the provided memory "
403 "and address assignment values");
404 for (
auto address : enumerate(addressValues))
405 rewriter.create<calyx::AssignOp>(loc, addrPorts[address.index()],
411 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
412 memref::LoadOp loadOp)
const {
413 Value memref = loadOp.getMemref();
414 auto memoryInterface =
415 getState<ComponentLoweringState>().getMemoryInterface(memref);
416 auto group = createGroupForOp<calyx::GroupOp>(rewriter, loadOp);
417 assignAddressPorts(rewriter, loadOp.getLoc(), group, memoryInterface,
418 loadOp.getIndices());
420 rewriter.setInsertionPointToEnd(group.getBodyBlock());
426 if (memoryInterface.readEnOpt().has_value()) {
429 rewriter.create<calyx::AssignOp>(loadOp.getLoc(), memoryInterface.readEn(),
431 regWriteEn = memoryInterface.done();
438 rewriter.create<calyx::GroupDoneOp>(loadOp.getLoc(),
439 memoryInterface.done());
449 res = loadOp.getResult();
451 }
else if (memoryInterface.contentEnOpt().has_value()) {
456 rewriter.create<calyx::AssignOp>(loadOp.getLoc(),
457 memoryInterface.contentEn(), oneI1);
458 rewriter.create<calyx::AssignOp>(loadOp.getLoc(), memoryInterface.writeEn(),
460 regWriteEn = memoryInterface.done();
467 rewriter.create<calyx::GroupDoneOp>(loadOp.getLoc(),
468 memoryInterface.done());
478 res = loadOp.getResult();
491 loadOp.getLoc(), rewriter, getComponent(),
492 loadOp.getMemRefType().getElementTypeBitWidth(),
493 getState<ComponentLoweringState>().getUniqueName(
"load"));
494 rewriter.setInsertionPointToEnd(group.getBodyBlock());
495 rewriter.create<calyx::AssignOp>(loadOp.getLoc(),
reg.getIn(),
496 memoryInterface.readData());
497 rewriter.create<calyx::AssignOp>(loadOp.getLoc(),
reg.getWriteEn(),
499 rewriter.create<calyx::GroupDoneOp>(loadOp.getLoc(),
reg.getDone());
500 loadOp.getResult().replaceAllUsesWith(
reg.getOut());
504 getState<ComponentLoweringState>().registerEvaluatingGroup(res, group);
505 getState<ComponentLoweringState>().addBlockScheduleable(loadOp->getBlock(),
510 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
511 memref::StoreOp storeOp)
const {
512 auto memoryInterface = getState<ComponentLoweringState>().getMemoryInterface(
513 storeOp.getMemref());
514 auto group = createGroupForOp<calyx::GroupOp>(rewriter, storeOp);
518 getState<ComponentLoweringState>().addBlockScheduleable(storeOp->getBlock(),
520 assignAddressPorts(rewriter, storeOp.getLoc(), group, memoryInterface,
521 storeOp.getIndices());
522 rewriter.setInsertionPointToEnd(group.getBodyBlock());
523 rewriter.create<calyx::AssignOp>(
524 storeOp.getLoc(), memoryInterface.writeData(), storeOp.getValueToStore());
525 rewriter.create<calyx::AssignOp>(
526 storeOp.getLoc(), memoryInterface.writeEn(),
527 createConstant(storeOp.getLoc(), rewriter, getComponent(), 1, 1));
528 if (memoryInterface.contentEnOpt().has_value()) {
530 rewriter.create<calyx::AssignOp>(
531 storeOp.getLoc(), memoryInterface.contentEn(),
532 createConstant(storeOp.getLoc(), rewriter, getComponent(), 1, 1));
534 rewriter.create<calyx::GroupDoneOp>(storeOp.getLoc(), memoryInterface.done());
539 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
541 Location loc = mul.getLoc();
542 Type
width = mul.getResult().getType(), one = rewriter.getI1Type();
544 getState<ComponentLoweringState>()
545 .getNewLibraryOpInstance<calyx::MultPipeLibOp>(
547 return buildLibraryBinaryPipeOp<calyx::MultPipeLibOp>(
548 rewriter, mul, mulPipe,
552 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
554 Location loc = div.getLoc();
555 Type
width = div.getResult().getType(), one = rewriter.getI1Type();
557 getState<ComponentLoweringState>()
558 .getNewLibraryOpInstance<calyx::DivUPipeLibOp>(
560 return buildLibraryBinaryPipeOp<calyx::DivUPipeLibOp>(
561 rewriter, div, divPipe,
565 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
567 Location loc = div.getLoc();
568 Type
width = div.getResult().getType(), one = rewriter.getI1Type();
570 getState<ComponentLoweringState>()
571 .getNewLibraryOpInstance<calyx::DivSPipeLibOp>(
573 return buildLibraryBinaryPipeOp<calyx::DivSPipeLibOp>(
574 rewriter, div, divPipe,
578 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
580 Location loc = rem.getLoc();
581 Type
width = rem.getResult().getType(), one = rewriter.getI1Type();
583 getState<ComponentLoweringState>()
584 .getNewLibraryOpInstance<calyx::RemUPipeLibOp>(
586 return buildLibraryBinaryPipeOp<calyx::RemUPipeLibOp>(
587 rewriter, rem, remPipe,
591 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
593 Location loc = rem.getLoc();
594 Type
width = rem.getResult().getType(), one = rewriter.getI1Type();
596 getState<ComponentLoweringState>()
597 .getNewLibraryOpInstance<calyx::RemSPipeLibOp>(
599 return buildLibraryBinaryPipeOp<calyx::RemSPipeLibOp>(
600 rewriter, rem, remPipe,
604 template <
typename TAllocOp>
606 PatternRewriter &rewriter, TAllocOp allocOp) {
607 rewriter.setInsertionPointToStart(
609 MemRefType memtype = allocOp.getType();
610 SmallVector<int64_t> addrSizes;
611 SmallVector<int64_t> sizes;
612 for (int64_t dim : memtype.getShape()) {
613 sizes.push_back(dim);
618 if (sizes.empty() && addrSizes.empty()) {
620 addrSizes.push_back(1);
622 auto memoryOp = rewriter.create<calyx::SeqMemoryOp>(
624 memtype.getElementType().getIntOrFloatBitWidth(), sizes, addrSizes);
627 memoryOp->setAttr(
"external",
634 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
635 memref::AllocOp allocOp)
const {
636 return buildAllocOp(getState<ComponentLoweringState>(), rewriter, allocOp);
639 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
640 memref::AllocaOp allocOp)
const {
641 return buildAllocOp(getState<ComponentLoweringState>(), rewriter, allocOp);
644 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
645 scf::YieldOp yieldOp)
const {
646 if (yieldOp.getOperands().empty()) {
648 auto forOp = dyn_cast<scf::ForOp>(yieldOp->getParentOp());
649 assert(forOp &&
"Empty yieldOps should only be located within ForOps");
654 getState<ComponentLoweringState>().getForLoopIterReg(forOpInterface, 0);
656 Type regWidth = inductionReg.getOut().getType();
658 SmallVector<Type> types(3, regWidth);
659 auto addOp = getState<ComponentLoweringState>()
660 .getNewLibraryOpInstance<calyx::AddLibOp>(
661 rewriter, forOp.getLoc(), types);
663 auto directions = addOp.portDirections();
665 SmallVector<Value, 2> opInputPorts;
667 for (
auto dir : enumerate(directions)) {
668 switch (dir.value()) {
670 opInputPorts.push_back(addOp.getResult(dir.index()));
674 opOutputPort = addOp.getResult(dir.index());
681 calyx::ComponentOp componentOp =
682 getState<ComponentLoweringState>().getComponentOp();
683 SmallVector<StringRef, 4> groupIdentifier = {
684 "incr", getState<ComponentLoweringState>().getUniqueName(forOp),
686 auto groupOp = calyx::createGroup<calyx::GroupOp>(
687 rewriter, componentOp, forOp.getLoc(),
688 llvm::join(groupIdentifier,
"_"));
689 rewriter.setInsertionPointToEnd(groupOp.getBodyBlock());
692 Value leftOp = opInputPorts.front();
693 rewriter.create<calyx::AssignOp>(forOp.getLoc(), leftOp,
694 inductionReg.getOut());
696 Value rightOp = opInputPorts.back();
697 rewriter.create<calyx::AssignOp>(
698 forOp.getLoc(), rightOp,
700 regWidth.getIntOrFloatBitWidth(),
701 forOp.getConstantStep().value().getSExtValue()));
704 inductionReg, opOutputPort);
706 getState<ComponentLoweringState>().setForLoopLatchGroup(forOpInterface,
708 getState<ComponentLoweringState>().registerEvaluatingGroup(opOutputPort,
713 if (dyn_cast<scf::ForOp>(yieldOp->getParentOp())) {
714 return yieldOp.getOperation()->emitError()
715 <<
"Currently do not support non-empty yield operations inside for "
716 "loops. Run --scf-for-to-while before running --scf-to-calyx.";
719 auto whileOp = dyn_cast<scf::WhileOp>(yieldOp->getParentOp());
721 return yieldOp.getOperation()->emitError()
722 <<
"Currently only support yield operations inside for and while "
728 getState<ComponentLoweringState>().buildWhileLoopIterArgAssignments(
729 rewriter, whileOpInterface,
730 getState<ComponentLoweringState>().getComponentOp(),
731 getState<ComponentLoweringState>().getUniqueName(whileOp) +
"_latch",
732 yieldOp->getOpOperands());
733 getState<ComponentLoweringState>().setWhileLoopLatchGroup(whileOpInterface,
738 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
739 BranchOpInterface brOp)
const {
744 Block *srcBlock = brOp->getBlock();
745 for (
auto succBlock : enumerate(brOp->getSuccessors())) {
746 auto succOperands = brOp.getSuccessorOperands(succBlock.index());
747 if (succOperands.empty())
750 std::string groupName = loweringState().blockName(srcBlock) +
"_to_" +
751 loweringState().blockName(succBlock.value());
752 auto groupOp = calyx::createGroup<calyx::GroupOp>(rewriter, getComponent(),
753 brOp.getLoc(), groupName);
755 auto dstBlockArgRegs =
756 getState<ComponentLoweringState>().getBlockArgRegs(succBlock.value());
758 for (
auto arg : enumerate(succOperands.getForwardedOperands())) {
759 auto reg = dstBlockArgRegs[arg.index()];
762 getState<ComponentLoweringState>().getComponentOp(),
reg,
767 getState<ComponentLoweringState>().addBlockArgGroup(
768 srcBlock, succBlock.value(), groupOp);
775 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
776 ReturnOp retOp)
const {
777 if (retOp.getNumOperands() == 0)
780 std::string groupName =
781 getState<ComponentLoweringState>().getUniqueName(
"ret_assign");
782 auto groupOp = calyx::createGroup<calyx::GroupOp>(rewriter, getComponent(),
783 retOp.getLoc(), groupName);
784 for (
auto op : enumerate(retOp.getOperands())) {
785 auto reg = getState<ComponentLoweringState>().getReturnReg(op.index());
787 rewriter, groupOp, getState<ComponentLoweringState>().getComponentOp(),
791 getState<ComponentLoweringState>().addBlockScheduleable(retOp->getBlock(),
796 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
797 arith::ConstantOp constOp)
const {
801 auto hwConstOp = rewriter.replaceOpWithNewOp<
hw::ConstantOp>(constOp, value);
802 hwConstOp->moveAfter(getComponent().getBodyBlock(),
803 getComponent().getBodyBlock()->begin());
807 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
809 return buildLibraryOp<calyx::CombGroupOp, calyx::AddLibOp>(rewriter, op);
811 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
813 return buildLibraryOp<calyx::CombGroupOp, calyx::SubLibOp>(rewriter, op);
815 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
817 return buildLibraryOp<calyx::CombGroupOp, calyx::RshLibOp>(rewriter, op);
819 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
821 return buildLibraryOp<calyx::CombGroupOp, calyx::SrshLibOp>(rewriter, op);
823 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
825 return buildLibraryOp<calyx::CombGroupOp, calyx::LshLibOp>(rewriter, op);
827 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
829 return buildLibraryOp<calyx::CombGroupOp, calyx::AndLibOp>(rewriter, op);
831 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
833 return buildLibraryOp<calyx::CombGroupOp, calyx::OrLibOp>(rewriter, op);
835 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
837 return buildLibraryOp<calyx::CombGroupOp, calyx::XorLibOp>(rewriter, op);
839 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
841 return buildLibraryOp<calyx::CombGroupOp, calyx::MuxLibOp>(rewriter, op);
844 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
846 switch (op.getPredicate()) {
847 case CmpIPredicate::eq:
848 return buildLibraryOp<calyx::CombGroupOp, calyx::EqLibOp>(rewriter, op);
849 case CmpIPredicate::ne:
850 return buildLibraryOp<calyx::CombGroupOp, calyx::NeqLibOp>(rewriter, op);
851 case CmpIPredicate::uge:
852 return buildLibraryOp<calyx::CombGroupOp, calyx::GeLibOp>(rewriter, op);
853 case CmpIPredicate::ult:
854 return buildLibraryOp<calyx::CombGroupOp, calyx::LtLibOp>(rewriter, op);
855 case CmpIPredicate::ugt:
856 return buildLibraryOp<calyx::CombGroupOp, calyx::GtLibOp>(rewriter, op);
857 case CmpIPredicate::ule:
858 return buildLibraryOp<calyx::CombGroupOp, calyx::LeLibOp>(rewriter, op);
859 case CmpIPredicate::sge:
860 return buildLibraryOp<calyx::CombGroupOp, calyx::SgeLibOp>(rewriter, op);
861 case CmpIPredicate::slt:
862 return buildLibraryOp<calyx::CombGroupOp, calyx::SltLibOp>(rewriter, op);
863 case CmpIPredicate::sgt:
864 return buildLibraryOp<calyx::CombGroupOp, calyx::SgtLibOp>(rewriter, op);
865 case CmpIPredicate::sle:
866 return buildLibraryOp<calyx::CombGroupOp, calyx::SleLibOp>(rewriter, op);
868 llvm_unreachable(
"unsupported comparison predicate");
870 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
872 return buildLibraryOp<calyx::CombGroupOp, calyx::SliceLibOp>(
873 rewriter, op, {op.getOperand().getType()}, {op.getType()});
875 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
877 return buildLibraryOp<calyx::CombGroupOp, calyx::PadLibOp>(
878 rewriter, op, {op.getOperand().getType()}, {op.getType()});
881 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
883 return buildLibraryOp<calyx::CombGroupOp, calyx::ExtSILibOp>(
884 rewriter, op, {op.getOperand().getType()}, {op.getType()});
887 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
888 IndexCastOp op)
const {
891 unsigned targetBits = targetType.getIntOrFloatBitWidth();
892 unsigned sourceBits = sourceType.getIntOrFloatBitWidth();
893 LogicalResult res = success();
895 if (targetBits == sourceBits) {
898 op.getResult().replaceAllUsesWith(op.getOperand());
901 if (sourceBits > targetBits)
902 res = buildLibraryOp<calyx::CombGroupOp, calyx::SliceLibOp>(
903 rewriter, op, {sourceType}, {targetType});
905 res = buildLibraryOp<calyx::CombGroupOp, calyx::PadLibOp>(
906 rewriter, op, {sourceType}, {targetType});
908 rewriter.eraseOp(op);
912 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
913 scf::WhileOp whileOp)
const {
917 getState<ComponentLoweringState>().addBlockScheduleable(
922 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
923 scf::ForOp forOp)
const {
929 std::optional<uint64_t> bound = scfForOp.
getBound();
930 if (!bound.has_value()) {
932 <<
"Loop bound not statically known. Should "
933 "transform into while loop using `--scf-for-to-while` before "
934 "running --lower-scf-to-calyx.";
936 getState<ComponentLoweringState>().addBlockScheduleable(
944 LogicalResult BuildOpGroups::buildOp(PatternRewriter &rewriter,
945 CallOp callOp)
const {
947 calyx::InstanceOp instanceOp =
948 getState<ComponentLoweringState>().getInstance(instanceName);
949 SmallVector<Value, 4> outputPorts;
950 auto portInfos = instanceOp.getReferencedComponent().getPortInfo();
951 for (
auto [idx, portInfo] : enumerate(portInfos)) {
953 outputPorts.push_back(instanceOp.getResult(idx));
957 for (
auto [idx, result] : llvm::enumerate(callOp.getResults()))
958 rewriter.replaceAllUsesWith(result, outputPorts[idx]);
962 getState<ComponentLoweringState>().addBlockScheduleable(
976 using OpRewritePattern::OpRewritePattern;
979 PatternRewriter &rewriter)
const override {
981 TypeRange yieldTypes = execOp.getResultTypes();
985 rewriter.setInsertionPointAfter(execOp);
986 auto *sinkBlock = rewriter.splitBlock(
988 execOp.getOperation()->getIterator()->getNextNode()->getIterator());
989 sinkBlock->addArguments(
991 SmallVector<Location, 4>(yieldTypes.size(), rewriter.getUnknownLoc()));
992 for (
auto res : enumerate(execOp.getResults()))
993 res.value().replaceAllUsesWith(sinkBlock->getArgument(res.index()));
997 make_early_inc_range(execOp.getRegion().getOps<scf::YieldOp>())) {
998 rewriter.setInsertionPointAfter(yieldOp);
999 rewriter.replaceOpWithNewOp<BranchOp>(yieldOp, sinkBlock,
1000 yieldOp.getOperands());
1004 auto *preBlock = execOp->getBlock();
1005 auto *execOpEntryBlock = &execOp.getRegion().front();
1006 auto *postBlock = execOp->getBlock()->splitBlock(execOp);
1007 rewriter.inlineRegionBefore(execOp.getRegion(), postBlock);
1008 rewriter.mergeBlocks(postBlock, preBlock);
1009 rewriter.eraseOp(execOp);
1012 rewriter.mergeBlocks(execOpEntryBlock, preBlock);
1020 using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
1024 PatternRewriter &rewriter)
const override {
1027 DenseMap<Value, unsigned> funcOpArgRewrites;
1031 DenseMap<unsigned, unsigned> funcOpResultMapping;
1039 DenseMap<Value, std::pair<unsigned, unsigned>> extMemoryCompPortIndices;
1043 SmallVector<calyx::PortInfo> inPorts, outPorts;
1044 FunctionType funcType = funcOp.getFunctionType();
1045 unsigned extMemCounter = 0;
1046 for (
auto arg : enumerate(funcOp.getArguments())) {
1047 if (arg.value().getType().isa<MemRefType>()) {
1050 "ext_mem" + std::to_string(extMemoryCompPortIndices.size());
1051 extMemoryCompPortIndices[arg.value()] = {inPorts.size(),
1054 extMemCounter++, inPorts, outPorts);
1058 if (
auto portNameAttr = funcOp.getArgAttrOfType<StringAttr>(
1060 inName = portNameAttr.str();
1062 inName =
"in" + std::to_string(arg.index());
1063 funcOpArgRewrites[arg.value()] = inPorts.size();
1065 rewriter.getStringAttr(inName),
1071 for (
auto res : enumerate(funcType.getResults())) {
1072 std::string resName;
1073 if (
auto portNameAttr = funcOp.getResultAttrOfType<StringAttr>(
1075 resName = portNameAttr.str();
1077 resName =
"out" + std::to_string(res.index());
1078 funcOpResultMapping[res.index()] = outPorts.size();
1080 rewriter.getStringAttr(resName),
1087 auto ports = inPorts;
1088 llvm::append_range(ports, outPorts);
1092 auto compOp = rewriter.create<calyx::ComponentOp>(
1093 funcOp.getLoc(), rewriter.getStringAttr(funcOp.getSymName()), ports);
1095 std::string funcName =
"func_" + funcOp.getSymName().str();
1096 rewriter.modifyOpInPlace(funcOp, [&]() { funcOp.setSymName(funcName); });
1099 compOp->setAttr(
"toplevel", rewriter.getUnitAttr());
1102 functionMapping[funcOp] = compOp;
1107 for (
auto &mapping : funcOpArgRewrites)
1108 mapping.getFirst().replaceAllUsesWith(
1109 compOp.getArgument(mapping.getSecond()));
1112 for (
auto extMemPortIndices : extMemoryCompPortIndices) {
1116 unsigned inPortsIt = extMemPortIndices.getSecond().first;
1117 unsigned outPortsIt = extMemPortIndices.getSecond().second +
1118 compOp.getInputPortInfo().size();
1119 extMemPorts.
readData = compOp.getArgument(inPortsIt++);
1120 extMemPorts.
done = compOp.getArgument(inPortsIt);
1121 extMemPorts.
writeData = compOp.getArgument(outPortsIt++);
1122 unsigned nAddresses = extMemPortIndices.getFirst()
1127 for (
unsigned j = 0; j < nAddresses; ++j)
1128 extMemPorts.
addrPorts.push_back(compOp.getArgument(outPortsIt++));
1129 extMemPorts.
writeEn = compOp.getArgument(outPortsIt);
1133 compState->registerMemoryInterface(extMemPortIndices.getFirst(),
1146 using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
1150 PatternRewriter &rewriter)
const override {
1151 LogicalResult res = success();
1152 funcOp.walk([&](Operation *op) {
1154 if (!isa<scf::WhileOp>(op))
1155 return WalkResult::advance();
1157 auto scfWhileOp = cast<scf::WhileOp>(op);
1160 getState<ComponentLoweringState>().setUniqueName(whileOp.
getOperation(),
1170 enumerate(scfWhileOp.getBefore().front().getArguments())) {
1171 auto condOp = scfWhileOp.getConditionOp().getArgs()[barg.index()];
1172 if (barg.value() != condOp) {
1174 << loweringState().irName(barg.value())
1175 <<
" != " << loweringState().irName(condOp)
1176 <<
"do-while loops not supported; expected iter-args to "
1177 "remain untransformed in the 'before' region of the "
1179 return WalkResult::interrupt();
1188 for (
auto arg : enumerate(whileOp.
getBodyArgs())) {
1189 std::string name = getState<ComponentLoweringState>()
1192 "_arg" + std::to_string(arg.index());
1195 arg.value().getType().getIntOrFloatBitWidth(), name);
1196 getState<ComponentLoweringState>().addWhileLoopIterReg(whileOp,
reg,
1198 arg.value().replaceAllUsesWith(
reg.getOut());
1202 ->getArgument(arg.index())
1203 .replaceAllUsesWith(
reg.getOut());
1207 SmallVector<calyx::GroupOp> initGroups;
1208 auto numOperands = whileOp.
getOperation()->getNumOperands();
1209 for (
size_t i = 0; i < numOperands; ++i) {
1211 getState<ComponentLoweringState>().buildWhileLoopIterArgAssignments(
1213 getState<ComponentLoweringState>().getComponentOp(),
1214 getState<ComponentLoweringState>().getUniqueName(
1216 "_init_" + std::to_string(i),
1218 initGroups.push_back(initGroupOp);
1221 getState<ComponentLoweringState>().setWhileLoopInitGroups(whileOp,
1224 return WalkResult::advance();
1234 using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
1238 PatternRewriter &rewriter)
const override {
1239 LogicalResult res = success();
1240 funcOp.walk([&](Operation *op) {
1242 if (!isa<scf::ForOp>(op))
1243 return WalkResult::advance();
1245 auto scfForOp = cast<scf::ForOp>(op);
1248 getState<ComponentLoweringState>().setUniqueName(forOp.
getOperation(),
1253 auto inductionVar = forOp.
getOperation().getInductionVar();
1254 SmallVector<std::string, 3> inductionVarIdentifiers = {
1255 getState<ComponentLoweringState>()
1258 "induction",
"var"};
1259 std::string name = llvm::join(inductionVarIdentifiers,
"_");
1262 inductionVar.getType().getIntOrFloatBitWidth(), name);
1263 getState<ComponentLoweringState>().addForLoopIterReg(forOp,
reg, 0);
1264 inductionVar.replaceAllUsesWith(
reg.getOut());
1267 calyx::ComponentOp componentOp =
1268 getState<ComponentLoweringState>().getComponentOp();
1269 SmallVector<calyx::GroupOp> initGroups;
1270 SmallVector<std::string, 4> groupIdentifiers = {
1272 getState<ComponentLoweringState>()
1275 "induction",
"var"};
1276 std::string groupName = llvm::join(groupIdentifiers,
"_");
1277 auto groupOp = calyx::createGroup<calyx::GroupOp>(
1278 rewriter, componentOp, forOp.
getLoc(), groupName);
1281 initGroups.push_back(groupOp);
1282 getState<ComponentLoweringState>().setForLoopInitGroups(forOp,
1285 return WalkResult::advance();
1297 using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
1301 PatternRewriter &rewriter)
const override {
1302 auto *entryBlock = &funcOp.getBlocks().front();
1303 rewriter.setInsertionPointToStart(
1304 getComponent().getControlOp().getBodyBlock());
1305 auto topLevelSeqOp = rewriter.create<calyx::SeqOp>(funcOp.getLoc());
1306 DenseSet<Block *> path;
1307 return buildCFGControl(path, rewriter, topLevelSeqOp.getBodyBlock(),
1308 nullptr, entryBlock);
1315 const DenseSet<Block *> &path,
1316 mlir::Block *parentCtrlBlock,
1317 mlir::Block *block)
const {
1318 auto compBlockScheduleables =
1319 getState<ComponentLoweringState>().getBlockScheduleables(block);
1320 auto loc = block->front().getLoc();
1322 if (compBlockScheduleables.size() > 1) {
1323 auto seqOp = rewriter.create<calyx::SeqOp>(loc);
1324 parentCtrlBlock = seqOp.getBodyBlock();
1327 for (
auto &group : compBlockScheduleables) {
1328 rewriter.setInsertionPointToEnd(parentCtrlBlock);
1329 if (
auto groupPtr = std::get_if<calyx::GroupOp>(&group); groupPtr) {
1330 rewriter.create<calyx::EnableOp>(groupPtr->getLoc(),
1331 groupPtr->getSymName());
1332 }
else if (
auto whileSchedPtr = std::get_if<WhileScheduleable>(&group);
1334 auto &whileOp = whileSchedPtr->whileOp;
1336 auto whileCtrlOp = buildWhileCtrlOp(
1338 getState<ComponentLoweringState>().getWhileLoopInitGroups(whileOp),
1340 rewriter.setInsertionPointToEnd(whileCtrlOp.getBodyBlock());
1342 rewriter.create<calyx::SeqOp>(whileOp.getOperation()->getLoc());
1343 auto *whileBodyOpBlock = whileBodyOp.getBodyBlock();
1347 LogicalResult res = buildCFGControl(path, rewriter, whileBodyOpBlock,
1348 block, whileOp.getBodyBlock());
1351 rewriter.setInsertionPointToEnd(whileBodyOpBlock);
1352 calyx::GroupOp whileLatchGroup =
1353 getState<ComponentLoweringState>().getWhileLoopLatchGroup(whileOp);
1354 rewriter.create<calyx::EnableOp>(whileLatchGroup.getLoc(),
1355 whileLatchGroup.getName());
1359 }
else if (
auto *forSchedPtr = std::get_if<ForScheduleable>(&group);
1361 auto forOp = forSchedPtr->forOp;
1363 auto forCtrlOp = buildForCtrlOp(
1365 getState<ComponentLoweringState>().getForLoopInitGroups(forOp),
1366 forSchedPtr->bound, rewriter);
1367 rewriter.setInsertionPointToEnd(forCtrlOp.getBodyBlock());
1369 rewriter.create<calyx::SeqOp>(forOp.getOperation()->getLoc());
1370 auto *forBodyOpBlock = forBodyOp.getBodyBlock();
1373 LogicalResult res = buildCFGControl(path, rewriter, forBodyOpBlock,
1374 block, forOp.getBodyBlock());
1377 rewriter.setInsertionPointToEnd(forBodyOpBlock);
1378 calyx::GroupOp forLatchGroup =
1379 getState<ComponentLoweringState>().getForLoopLatchGroup(forOp);
1380 rewriter.create<calyx::EnableOp>(forLatchGroup.getLoc(),
1381 forLatchGroup.getName());
1384 }
else if (
auto *callSchedPtr = std::get_if<CallScheduleable>(&group)) {
1385 auto instanceOp = callSchedPtr->instanceOp;
1386 OpBuilder::InsertionGuard g(rewriter);
1387 auto callBody = rewriter.create<calyx::SeqOp>(instanceOp.getLoc());
1388 rewriter.setInsertionPointToStart(callBody.getBodyBlock());
1389 std::string initGroupName =
"init_" + instanceOp.getSymName().str();
1390 rewriter.create<calyx::EnableOp>(instanceOp.getLoc(), initGroupName);
1391 SmallVector<Value, 4> instancePorts;
1392 auto inputPorts = callSchedPtr->callOp.getOperands();
1393 llvm::copy(instanceOp.getResults().take_front(inputPorts.size()),
1394 std::back_inserter(instancePorts));
1395 rewriter.create<calyx::InvokeOp>(
1396 instanceOp.getLoc(), instanceOp.getSymName(), instancePorts,
1400 llvm_unreachable(
"Unknown scheduleable");
1411 const DenseSet<Block *> &path, Location loc,
1412 Block *from, Block *to,
1413 Block *parentCtrlBlock)
const {
1416 rewriter.setInsertionPointToEnd(parentCtrlBlock);
1417 auto preSeqOp = rewriter.create<calyx::SeqOp>(loc);
1418 rewriter.setInsertionPointToEnd(preSeqOp.getBodyBlock());
1420 getState<ComponentLoweringState>().getBlockArgGroups(from, to))
1421 rewriter.create<calyx::EnableOp>(barg.getLoc(), barg.getSymName());
1423 return buildCFGControl(path, rewriter, parentCtrlBlock, from, to);
1427 PatternRewriter &rewriter,
1428 mlir::Block *parentCtrlBlock,
1429 mlir::Block *preBlock,
1430 mlir::Block *block)
const {
1431 if (path.count(block) != 0)
1432 return preBlock->getTerminator()->emitError()
1433 <<
"CFG backedge detected. Loops must be raised to 'scf.while' or "
1434 "'scf.for' operations.";
1436 rewriter.setInsertionPointToEnd(parentCtrlBlock);
1437 LogicalResult bbSchedResult =
1438 scheduleBasicBlock(rewriter, path, parentCtrlBlock, block);
1439 if (bbSchedResult.failed())
1440 return bbSchedResult;
1443 auto successors = block->getSuccessors();
1444 auto nSuccessors = successors.size();
1445 if (nSuccessors > 0) {
1446 auto brOp = dyn_cast<BranchOpInterface>(block->getTerminator());
1448 if (nSuccessors > 1) {
1452 assert(nSuccessors == 2 &&
1453 "only conditional branches supported for now...");
1455 auto cond = brOp->getOperand(0);
1456 auto condGroup = getState<ComponentLoweringState>()
1457 .getEvaluatingGroup<calyx::CombGroupOp>(cond);
1461 auto ifOp = rewriter.create<calyx::IfOp>(
1462 brOp->getLoc(), cond, symbolAttr,
true);
1463 rewriter.setInsertionPointToStart(ifOp.getThenBody());
1464 auto thenSeqOp = rewriter.create<calyx::SeqOp>(brOp.getLoc());
1465 rewriter.setInsertionPointToStart(ifOp.getElseBody());
1466 auto elseSeqOp = rewriter.create<calyx::SeqOp>(brOp.getLoc());
1468 bool trueBrSchedSuccess =
1469 schedulePath(rewriter, path, brOp.getLoc(), block, successors[0],
1470 thenSeqOp.getBodyBlock())
1472 bool falseBrSchedSuccess =
true;
1473 if (trueBrSchedSuccess) {
1474 falseBrSchedSuccess =
1475 schedulePath(rewriter, path, brOp.getLoc(), block, successors[1],
1476 elseSeqOp.getBodyBlock())
1480 return success(trueBrSchedSuccess && falseBrSchedSuccess);
1483 return schedulePath(rewriter, path, brOp.getLoc(), block,
1484 successors.front(), parentCtrlBlock);
1494 const SmallVector<calyx::GroupOp> &initGroups)
const {
1495 PatternRewriter::InsertionGuard g(rewriter);
1496 auto parOp = rewriter.create<calyx::ParOp>(loc);
1497 rewriter.setInsertionPointToStart(parOp.getBodyBlock());
1498 for (calyx::GroupOp group : initGroups)
1499 rewriter.create<calyx::EnableOp>(group.getLoc(), group.getName());
1503 SmallVector<calyx::GroupOp> initGroups,
1504 PatternRewriter &rewriter)
const {
1505 Location loc = whileOp.
getLoc();
1508 insertParInitGroups(rewriter, loc, initGroups);
1512 auto condGroup = getState<ComponentLoweringState>()
1513 .getEvaluatingGroup<calyx::CombGroupOp>(cond);
1516 return rewriter.create<calyx::WhileOp>(loc, cond, symbolAttr);
1520 SmallVector<calyx::GroupOp>
const &initGroups,
1522 PatternRewriter &rewriter)
const {
1523 Location loc = forOp.
getLoc();
1526 insertParInitGroups(rewriter, loc, initGroups);
1529 return rewriter.create<calyx::RepeatOp>(loc, bound);
1536 using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
1539 PatternRewriter &)
const override {
1540 funcOp.walk([&](scf::WhileOp op) {
1549 getState<ComponentLoweringState>().getWhileLoopIterRegs(whileOp))
1550 whileOp.
getOperation()->getResults()[res.first].replaceAllUsesWith(
1551 res.second.getOut());
1554 funcOp.walk([&](memref::LoadOp loadOp) {
1560 loadOp.getResult().replaceAllUsesWith(
1561 getState<ComponentLoweringState>()
1562 .getMemoryInterface(loadOp.getMemref())
1573 using FuncOpPartialLoweringPattern::FuncOpPartialLoweringPattern;
1576 PatternRewriter &rewriter)
const override {
1577 rewriter.eraseOp(funcOp);
1583 PatternRewriter &rewriter)
const override {
1595 void runOnOperation()
override;
1598 std::string &topLevelFunction) {
1599 if (!topLevelFunctionOpt.empty()) {
1600 if (SymbolTable::lookupSymbolIn(moduleOp, topLevelFunctionOpt) ==
1602 moduleOp.emitError() <<
"Top level function '" << topLevelFunctionOpt
1603 <<
"' not found in module.";
1606 topLevelFunction = topLevelFunctionOpt;
1610 auto funcOps = moduleOp.getOps<FuncOp>();
1611 if (std::distance(funcOps.begin(), funcOps.end()) == 1)
1612 topLevelFunction = (*funcOps.begin()).getSymName().str();
1614 moduleOp.emitError()
1615 <<
"Module contains multiple functions, but no top level "
1616 "function was set. Please see --top-level-function";
1637 using OpRewritePattern::OpRewritePattern;
1638 LogicalResult matchAndRewrite(mlir::ModuleOp,
1639 PatternRewriter &)
const override {
1644 ConversionTarget target(getContext());
1645 target.addLegalDialect<calyx::CalyxDialect>();
1646 target.addLegalDialect<scf::SCFDialect>();
1647 target.addIllegalDialect<hw::HWDialect>();
1648 target.addIllegalDialect<comb::CombDialect>();
1651 target.addIllegalDialect<FuncDialect>();
1652 target.addIllegalDialect<ArithDialect>();
1653 target.addLegalOp<AddIOp, SelectOp, SubIOp, CmpIOp, ShLIOp, ShRUIOp,
1654 ShRSIOp, AndIOp, XOrIOp, OrIOp, ExtUIOp, TruncIOp,
1655 CondBranchOp, BranchOp, MulIOp, DivUIOp, DivSIOp, RemUIOp,
1656 RemSIOp, ReturnOp, arith::ConstantOp, IndexCastOp, FuncOp,
1659 RewritePatternSet legalizePatterns(&getContext());
1660 legalizePatterns.add<DummyPattern>(&getContext());
1661 DenseSet<Operation *> legalizedOps;
1662 if (applyPartialConversion(getOperation(), target,
1663 std::move(legalizePatterns))
1674 template <
typename TPattern,
typename... PatternArgs>
1676 PatternArgs &&...args) {
1677 RewritePatternSet ps(&getContext());
1678 ps.add<TPattern>(&getContext(), partialPatternRes, args...);
1683 template <
typename TPattern,
typename... PatternArgs>
1685 PatternArgs &&...args) {
1686 RewritePatternSet ps(&getContext());
1687 ps.add<TPattern>(&getContext(), args...);
1693 assert(pattern.getNativePatterns().size() == 1 &&
1694 "Should only apply 1 partial lowering pattern at once");
1700 GreedyRewriteConfig config;
1701 config.enableRegionSimplification =
false;
1703 config.maxIterations = 1;
1708 (void)applyPatternsAndFoldGreedily(getOperation(), std::move(pattern),
1710 return partialPatternRes;
1715 std::shared_ptr<calyx::CalyxLoweringState> loweringState =
nullptr;
1718 void SCFToCalyxPass::runOnOperation() {
1720 loweringState.reset();
1721 partialPatternRes = LogicalResult::failure();
1723 std::string topLevelFunction;
1724 if (failed(setTopLevelFunction(getOperation(), topLevelFunction))) {
1725 signalPassFailure();
1730 if (failed(labelEntryPoint(topLevelFunction))) {
1731 signalPassFailure();
1734 loweringState = std::make_shared<calyx::CalyxLoweringState>(getOperation(),
1745 DenseMap<FuncOp, calyx::ComponentOp> funcMap;
1746 SmallVector<LoweringPattern, 8> loweringPatterns;
1750 addOncePattern<FuncOpConversion>(loweringPatterns, patternState, funcMap,
1754 addGreedyPattern<InlineExecuteRegionOpPattern>(loweringPatterns);
1757 addOncePattern<calyx::ConvertIndexTypes>(loweringPatterns, patternState,
1758 funcMap, *loweringState);
1761 addOncePattern<calyx::BuildBasicBlockRegs>(loweringPatterns, patternState,
1762 funcMap, *loweringState);
1764 addOncePattern<calyx::BuildCallInstance>(loweringPatterns, patternState,
1765 funcMap, *loweringState);
1768 addOncePattern<calyx::BuildReturnRegs>(loweringPatterns, patternState,
1769 funcMap, *loweringState);
1774 addOncePattern<BuildWhileGroups>(loweringPatterns, patternState, funcMap,
1780 addOncePattern<BuildForGroups>(loweringPatterns, patternState, funcMap,
1790 addOncePattern<BuildOpGroups>(loweringPatterns, patternState, funcMap,
1796 addOncePattern<BuildControl>(loweringPatterns, patternState, funcMap,
1801 addOncePattern<calyx::InlineCombGroups>(loweringPatterns, patternState,
1806 addOncePattern<LateSSAReplacement>(loweringPatterns, patternState, funcMap,
1812 addGreedyPattern<calyx::EliminateUnusedCombGroups>(loweringPatterns);
1816 addOncePattern<calyx::RewriteMemoryAccesses>(loweringPatterns, patternState,
1821 addOncePattern<CleanupFuncOps>(loweringPatterns, patternState, funcMap,
1825 for (
auto &pat : loweringPatterns) {
1826 LogicalResult partialPatternRes = runPartialPattern(
1828 pat.strategy == LoweringPattern::Strategy::Once);
1829 if (succeeded(partialPatternRes))
1831 signalPassFailure();
1838 RewritePatternSet cleanupPatterns(&getContext());
1841 if (failed(applyPatternsAndFoldGreedily(getOperation(),
1842 std::move(cleanupPatterns)))) {
1843 signalPassFailure();
1847 if (ciderSourceLocationMetadata) {
1850 SmallVector<Attribute, 16> sourceLocations;
1851 getOperation()->walk([&](calyx::ComponentOp component) {
1855 MLIRContext *context = getOperation()->getContext();
1856 getOperation()->setAttr(
"calyx.metadata",
1868 return std::make_unique<scftocalyx::SCFToCalyxPass>();
assert(baseType &&"element must be base type")
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
LogicalResult labelEntryPoint(StringRef topLevelFunction)
Labels the entry point of a Calyx program.
LogicalResult runPartialPattern(RewritePatternSet &pattern, bool runOnce)
LogicalResult setTopLevelFunction(mlir::ModuleOp moduleOp, std::string &topLevelFunction)
void addGreedyPattern(SmallVectorImpl< LoweringPattern > &patterns, PatternArgs &&...args)
LogicalResult partialPatternRes
void addOncePattern(SmallVectorImpl< LoweringPattern > &patterns, PatternArgs &&...args)
'Once' patterns are expected to take an additional LogicalResult& argument, to forward their result s...
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
When building groups which contain accesses to multiple sequential components, a group_done op is cre...
GroupDoneOp's are terminator operations and should therefore be the last operator in a group.
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
RewritePatternSet pattern
ScfWhileOp whileOp
While operation to schedule.