19#include "mlir/IR/AsmState.h"
20#include "mlir/IR/Builders.h"
21#include "mlir/IR/BuiltinAttributes.h"
22#include "mlir/IR/BuiltinTypes.h"
23#include "mlir/IR/Diagnostics.h"
24#include "mlir/IR/DialectImplementation.h"
25#include "mlir/IR/PatternMatch.h"
26#include "mlir/IR/SymbolTable.h"
27#include "mlir/Interfaces/FunctionImplementation.h"
28#include "mlir/Support/LLVM.h"
29#include "llvm/ADT/DenseMap.h"
30#include "llvm/ADT/PriorityQueue.h"
31#include "llvm/ADT/STLExtras.h"
32#include "llvm/ADT/SmallSet.h"
33#include "llvm/ADT/StringExtras.h"
34#include "llvm/ADT/TypeSwitch.h"
35#include "llvm/Support/Casting.h"
46template <
class T,
class... Ts>
47struct IsAny : std::disjunction<std::is_same<T, Ts>...> {};
63 size_t numDirections = nIns + nOuts;
64 APInt portDirections(numDirections, 0);
65 for (
size_t i = nIns, e = numDirections; i != e; ++i)
66 portDirections.setBit(i);
68 return IntegerAttr::get(IntegerType::get(ctx, numDirections), portDirections);
77template <
typename CtrlOp>
81 PatternRewriter &rewriter)
const override {
82 auto &ops = ctrlOp.getBodyBlock()->getOperations();
84 (ops.size() == 1) && isa<EnableOp>(ops.front()) &&
85 isa<SeqOp, ParOp, StaticSeqOp, StaticParOp>(ctrlOp->getParentOp());
89 ops.front().moveBefore(ctrlOp);
90 rewriter.eraseOp(ctrlOp);
103template <
typename Op>
105 Operation *definingOp = op.getSrc().getDefiningOp();
106 if (definingOp ==
nullptr)
112 if (
auto dialect = definingOp->getDialect(); isa<comb::CombDialect>(dialect))
113 return op->emitOpError(
"has source that is not a port or constant. "
114 "Complex logic should be conducted in the guard.");
121static std::string
valueName(Operation *scopeOp, Value v) {
123 llvm::raw_string_ostream os(s);
128 AsmState asmState(scopeOp, OpPrintingFlags().assumeVerified());
129 v.printAsOperand(os, asmState);
136 Operation *definingOp = value.getDefiningOp();
137 return isa<BlockArgument>(value) ||
138 isa_and_nonnull<CellInterface>(definingOp);
143 Operation *op = arg.getOwner()->getParentOp();
144 assert(isa<ComponentInterface>(op) &&
145 "Only ComponentInterface should support lookup by BlockArgument.");
146 return cast<ComponentInterface>(op).getPortInfo()[arg.getArgNumber()];
151 return isa<ControlOp, SeqOp, IfOp, RepeatOp,
WhileOp, ParOp, StaticRepeatOp,
152 StaticParOp, StaticSeqOp, StaticIfOp>(op);
157 if (isa<EnableOp>(op)) {
159 auto component = op->getParentOfType<ComponentOp>();
160 auto enableOp = llvm::cast<EnableOp>(op);
161 StringRef groupName = enableOp.getGroupName();
162 auto group = component.getWiresOp().lookupSymbol<GroupInterface>(groupName);
163 return isa<StaticGroupOp>(group);
165 return isa<StaticIfOp, StaticSeqOp, StaticRepeatOp, StaticParOp>(op);
170 if (isa<SeqOp, ParOp, StaticSeqOp, StaticParOp>(op))
175 for (
auto ®ion : op->getRegions()) {
176 auto opsIt = region.getOps();
177 size_t numOperations = std::distance(opsIt.begin(), opsIt.end());
182 bool usesEnableAsCompositionOperator =
183 numOperations > 1 && llvm::any_of(region.front(), [](
auto &&bodyOp) {
184 return isa<EnableOp>(bodyOp);
186 if (usesEnableAsCompositionOperator)
187 return op->emitOpError(
188 "EnableOp is not a composition operator. It should be nested "
189 "in a control flow operation, such as \"calyx.seq\"");
193 size_t numControlFlowRegions =
195 if (numControlFlowRegions > 1)
196 return op->emitOpError(
197 "has an invalid control sequence. Multiple control flow operations "
198 "must all be nested in a single calyx.seq or calyx.par");
204 auto *opParent = op->getParentOp();
205 if (!isa<ModuleOp>(opParent))
206 return op->emitOpError()
207 <<
"has parent: " << opParent <<
", expected ModuleOp.";
212 auto opParent = op->getParentOp();
213 if (!isa<ComponentInterface>(opParent))
214 return op->emitOpError()
215 <<
"has parent: " << opParent <<
", expected ComponentInterface.";
220 auto parent = op->getParentOp();
222 if (isa<calyx::EnableOp>(op) &&
223 !isa<calyx::CalyxDialect>(parent->getDialect())) {
232 return op->emitOpError()
233 <<
"has parent: " << parent
234 <<
", which is not allowed for a control-like operation.";
236 if (op->getNumRegions() == 0)
239 auto ®ion = op->getRegion(0);
241 auto isValidBodyOp = [](Operation *operation) {
242 return isa<EnableOp, InvokeOp, SeqOp, IfOp, RepeatOp,
WhileOp, ParOp,
243 StaticParOp, StaticRepeatOp, StaticSeqOp, StaticIfOp>(operation);
245 for (
auto &&bodyOp : region.front()) {
246 if (isValidBodyOp(&bodyOp))
249 return op->emitOpError()
250 <<
"has operation: " << bodyOp.getName()
251 <<
", which is not allowed in this control-like operation";
257 auto ifOp = dyn_cast<IfInterface>(op);
259 if (ifOp.elseBodyExists() && ifOp.getElseBody()->empty())
260 return ifOp->emitOpError() <<
"empty 'else' region.";
270 SmallVector<OpAsmParser::UnresolvedOperand, 2> operandInfos;
271 OpAsmParser::UnresolvedOperand guardOrSource;
272 if (parser.parseOperand(guardOrSource))
275 if (succeeded(parser.parseOptionalQuestion())) {
276 OpAsmParser::UnresolvedOperand source;
278 if (parser.parseOperand(source))
280 operandInfos.push_back(source);
283 operandInfos.push_back(guardOrSource);
288 if (parser.parseColonType(type) ||
289 parser.resolveOperands(operandInfos, type, result.operands))
296template <
typename GroupPortType>
298 static_assert(IsAny<GroupPortType, GroupGoOp, GroupDoneOp>(),
299 "Should be a Calyx Group port.");
303 Value guard = op.getGuard(), source = op.getSrc();
306 p << source <<
" : " << source.getType();
311template <
typename OpTy>
313 PatternRewriter &rewriter) {
314 static_assert(IsAny<OpTy, SeqOp, ParOp, StaticSeqOp, StaticParOp>(),
315 "Should be a SeqOp, ParOp, StaticSeqOp, or StaticParOp");
317 if (isa<OpTy>(controlOp->getParentOp())) {
318 Block *controlBody = controlOp.getBodyBlock();
319 for (
auto &op : make_early_inc_range(*controlBody))
320 op.moveBefore(controlOp);
322 rewriter.eraseOp(controlOp);
329template <
typename OpTy>
330static LogicalResult
emptyControl(OpTy controlOp, PatternRewriter &rewriter) {
331 if (controlOp.getBodyBlock()->empty()) {
332 rewriter.eraseOp(controlOp);
341template <
typename OpTy>
343 PatternRewriter &rewriter) {
344 static_assert(IsAny<OpTy, IfOp, WhileOp>(),
345 "This is only applicable to WhileOp and IfOp.");
348 Value cond = op.getCond();
349 std::optional<StringRef> groupName = op.getGroupName();
350 auto component = op->template getParentOfType<ComponentOp>();
351 rewriter.eraseOp(op);
355 auto group = component.getWiresOp().template lookupSymbol<GroupInterface>(
357 if (SymbolTable::symbolKnownUseEmpty(group, component.getRegion()))
358 rewriter.eraseOp(group);
361 if (!isa<BlockArgument>(cond) && cond.getDefiningOp()->use_empty())
362 rewriter.eraseOp(cond.getDefiningOp());
368template <
typename OpTy>
370 static_assert(std::is_same<OpTy, StaticIfOp>(),
371 "This is only applicable to StatifIfOp.");
374 Value cond = op.getCond();
375 rewriter.eraseOp(op);
378 if (!isa<BlockArgument>(cond) && cond.getDefiningOp()->use_empty())
379 rewriter.eraseOp(cond.getDefiningOp());
386template <
typename ComponentTy>
388 auto componentName = comp->template getAttrOfType<StringAttr>(
389 ::mlir::SymbolTable::getSymbolAttrName())
392 p.printSymbolName(componentName);
395 auto printPortDefList = [&](
auto ports) {
397 llvm::interleaveComma(ports, p, [&](
const PortInfo &port) {
398 p <<
"%" << port.
name.getValue() <<
": " << port.
type;
406 printPortDefList(comp.getInputPortInfo());
408 printPortDefList(comp.getOutputPortInfo());
411 p.printRegion(*comp.getRegion(),
false,
415 SmallVector<StringRef> elidedAttrs = {
420 ComponentTy::getFunctionTypeAttrName(comp->getName()),
421 ComponentTy::getArgAttrsAttrName(comp->getName()),
422 ComponentTy::getResAttrsAttrName(comp->getName())};
423 p.printOptionalAttrDict(comp->getAttrs(), elidedAttrs);
430 SmallVectorImpl<OpAsmParser::Argument> &ports,
431 SmallVectorImpl<Type> &portTypes,
432 SmallVectorImpl<NamedAttrList> &portAttrs) {
434 OpAsmParser::Argument port;
437 if (parser.parseArgument(port) || parser.parseColon() ||
438 parser.parseType(portType))
440 port.type = portType;
441 ports.push_back(port);
442 portTypes.push_back(portType);
444 NamedAttrList portAttr;
445 portAttrs.push_back(succeeded(parser.parseOptionalAttrDict(portAttr))
451 return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
458 SmallVectorImpl<OpAsmParser::Argument> &ports,
459 SmallVectorImpl<Type> &portTypes) {
460 SmallVector<OpAsmParser::Argument> inPorts, outPorts;
461 SmallVector<Type> inPortTypes, outPortTypes;
462 SmallVector<NamedAttrList> portAttributes;
467 if (parser.parseArrow() ||
471 auto *context = parser.getBuilder().getContext();
474 SmallVector<Attribute> portNames;
475 auto getPortName = [context](
const auto &port) -> StringAttr {
476 StringRef name = port.ssaName.name;
477 if (name.starts_with(
"%"))
478 name = name.drop_front();
479 return StringAttr::get(context, name);
481 llvm::transform(inPorts, std::back_inserter(portNames), getPortName);
482 llvm::transform(outPorts, std::back_inserter(portNames), getPortName);
484 result.addAttribute(
"portNames", ArrayAttr::get(context, portNames));
489 ports.append(inPorts);
490 ports.append(outPorts);
491 portTypes.append(inPortTypes);
492 portTypes.append(outPortTypes);
494 SmallVector<Attribute> portAttrs;
495 llvm::transform(portAttributes, std::back_inserter(portAttrs),
496 [&](
auto attr) {
return attr.getDictionary(context); });
497 result.addAttribute(
"portAttributes", ArrayAttr::get(context, portAttrs));
502template <
typename ComponentTy>
504 OperationState &result) {
505 using namespace mlir::function_interface_impl;
507 StringAttr componentName;
508 if (parser.parseSymbolName(componentName,
509 ::mlir::SymbolTable::getSymbolAttrName(),
513 SmallVector<mlir::OpAsmParser::Argument> ports;
515 SmallVector<Type> portTypes;
521 auto type = parser.getBuilder().getFunctionType(portTypes, {});
522 result.addAttribute(ComponentTy::getFunctionTypeAttrName(result.name),
523 TypeAttr::get(type));
525 auto *body = result.addRegion();
526 if (parser.parseRegion(*body, ports))
530 body->push_back(
new Block());
532 if (parser.parseOptionalAttrDict(result.attributes))
540static SmallVector<T>
concat(
const SmallVectorImpl<T> &a,
541 const SmallVectorImpl<T> &b) {
549 StringAttr name, ArrayRef<PortInfo> ports,
550 bool combinational) {
551 using namespace mlir::function_interface_impl;
553 result.addAttribute(::mlir::SymbolTable::getSymbolAttrName(), name);
555 std::pair<SmallVector<Type, 8>, SmallVector<Type, 8>> portIOTypes;
556 std::pair<SmallVector<Attribute, 8>, SmallVector<Attribute, 8>> portIONames;
557 std::pair<SmallVector<Attribute, 8>, SmallVector<Attribute, 8>>
559 SmallVector<Direction, 8> portDirections;
562 for (
auto &&port : ports) {
563 bool isInput = port.direction == Direction::Input;
564 (isInput ? portIOTypes.first : portIOTypes.second).push_back(port.type);
565 (isInput ? portIONames.first : portIONames.second).push_back(port.name);
566 (isInput ? portIOAttributes.first : portIOAttributes.second)
567 .push_back(port.attributes);
569 auto portTypes =
concat(portIOTypes.first, portIOTypes.second);
570 auto portNames =
concat(portIONames.first, portIONames.second);
571 auto portAttributes =
concat(portIOAttributes.first, portIOAttributes.second);
574 auto functionType = builder.getFunctionType(portTypes, {});
576 result.addAttribute(CombComponentOp::getFunctionTypeAttrName(result.name),
577 TypeAttr::get(functionType));
579 result.addAttribute(ComponentOp::getFunctionTypeAttrName(result.name),
580 TypeAttr::get(functionType));
584 result.addAttribute(
"portNames", builder.getArrayAttr(portNames));
585 result.addAttribute(
"portDirections",
587 portIOTypes.first.size(),
588 portIOTypes.second.size()));
590 result.addAttribute(
"portAttributes", builder.getArrayAttr(portAttributes));
593 Region *region = result.addRegion();
594 Block *body =
new Block();
595 region->push_back(body);
598 body->addArguments(portTypes, SmallVector<Location, 4>(
599 portTypes.size(), builder.getUnknownLoc()));
602 IRRewriter::InsertionGuard guard(builder);
603 builder.setInsertionPointToStart(body);
604 builder.create<WiresOp>(result.location);
606 builder.create<ControlOp>(result.location);
617template <
typename Op>
619 auto *body = op.getBodyBlock();
622 auto opIt = body->getOps<Op>().begin();
629 ArrayAttr portNames = op.getPortNames();
631 for (
size_t i = 0, e = portNames.size(); i != e; ++i) {
632 auto portName = cast<StringAttr>(portNames[i]);
633 if (portName.getValue() == name)
634 return op.getBodyBlock()->getArgument(i);
639WiresOp calyx::ComponentOp::getWiresOp() {
640 return getControlOrWiresFrom<WiresOp>(*
this);
643ControlOp calyx::ComponentOp::getControlOp() {
644 return getControlOrWiresFrom<ControlOp>(*
this);
647Value calyx::ComponentOp::getGoPort() {
651Value calyx::ComponentOp::getDonePort() {
655Value calyx::ComponentOp::getClkPort() {
659Value calyx::ComponentOp::getResetPort() {
663SmallVector<PortInfo> ComponentOp::getPortInfo() {
664 auto portTypes = getArgumentTypes();
665 ArrayAttr portNamesAttr = getPortNames(), portAttrs = getPortAttributes();
666 APInt portDirectionsAttr = getPortDirections();
668 SmallVector<PortInfo> results;
669 for (
size_t i = 0, e = portNamesAttr.size(); i != e; ++i) {
670 results.push_back(
PortInfo{cast<StringAttr>(portNamesAttr[i]), portTypes[i],
672 cast<DictionaryAttr>(portAttrs[i])});
678template <
typename Pred>
680 SmallVector<PortInfo> ports = op.getPortInfo();
681 llvm::erase_if(ports, p);
685SmallVector<PortInfo> ComponentOp::getInputPortInfo() {
690SmallVector<PortInfo> ComponentOp::getOutputPortInfo() {
695void ComponentOp::print(OpAsmPrinter &p) {
696 printComponentInterface<ComponentOp>(p, *
this);
699ParseResult ComponentOp::parse(OpAsmParser &parser, OperationState &result) {
700 return parseComponentInterface<ComponentOp>(parser, result);
706 llvm::SmallVector<StringRef, 4> identifiers;
707 for (
PortInfo &port : op.getPortInfo()) {
708 auto portIds = port.getAllIdentifiers();
709 identifiers.append(portIds.begin(), portIds.end());
712 std::sort(identifiers.begin(), identifiers.end());
717 std::set_intersection(interfacePorts.begin(), interfacePorts.end(),
718 identifiers.begin(), identifiers.end(),
724 SmallVector<StringRef, 4> difference;
725 std::set_difference(interfacePorts.begin(), interfacePorts.end(),
727 std::back_inserter(difference));
728 return op->emitOpError()
729 <<
"is missing the following required port attribute identifiers: "
733LogicalResult ComponentOp::verify() {
737 if (std::distance(wIt.begin(), wIt.end()) +
738 std::distance(cIt.begin(), cIt.end()) !=
740 return emitOpError() <<
"requires exactly one of each: '"
741 << WiresOp::getOperationName() <<
"', '"
742 << ControlOp::getOperationName() <<
"'.";
749 bool hasNoControlConstructs =
true;
750 getControlOp().walk<WalkOrder::PreOrder>([&](Operation *op) {
751 if (isa<EnableOp, InvokeOp, fsm::MachineOp>(op)) {
752 hasNoControlConstructs =
false;
753 return WalkResult::interrupt();
755 return WalkResult::advance();
757 bool hasNoAssignments =
758 getWiresOp().getBodyBlock()->getOps<AssignOp>().
empty();
759 if (hasNoControlConstructs && hasNoAssignments)
761 "The component currently does nothing. It needs to either have "
762 "continuous assignments in the Wires region or control "
763 "constructs in the Control region. The Control region "
764 "should contain at least one of ")
765 <<
"'" << EnableOp::getOperationName() <<
"' , "
766 <<
"'" << InvokeOp::getOperationName() <<
"' or "
767 <<
"'" << fsm::MachineOp::getOperationName() <<
"'.";
771void ComponentOp::build(OpBuilder &builder, OperationState &result,
772 StringAttr name, ArrayRef<PortInfo> ports) {
776void ComponentOp::getAsmBlockArgumentNames(
780 auto ports = getPortNames();
781 auto *block = &getRegion()->front();
782 for (
size_t i = 0, e = block->getNumArguments(); i != e; ++i)
783 setNameFn(block->getArgument(i), cast<StringAttr>(ports[i]).getValue());
790SmallVector<PortInfo> CombComponentOp::getPortInfo() {
791 auto portTypes = getArgumentTypes();
792 ArrayAttr portNamesAttr = getPortNames(), portAttrs = getPortAttributes();
793 APInt portDirectionsAttr = getPortDirections();
795 SmallVector<PortInfo> results;
796 for (
size_t i = 0, e = portNamesAttr.size(); i != e; ++i) {
797 results.push_back(
PortInfo{cast<StringAttr>(portNamesAttr[i]), portTypes[i],
799 cast<DictionaryAttr>(portAttrs[i])});
804WiresOp calyx::CombComponentOp::getWiresOp() {
806 auto opIt = body->getOps<WiresOp>().begin();
811template <
typename Pred>
813 SmallVector<PortInfo> ports = op.getPortInfo();
814 llvm::erase_if(ports, p);
818SmallVector<PortInfo> CombComponentOp::getInputPortInfo() {
823SmallVector<PortInfo> CombComponentOp::getOutputPortInfo() {
828void CombComponentOp::print(OpAsmPrinter &p) {
829 printComponentInterface<CombComponentOp>(p, *
this);
832ParseResult CombComponentOp::parse(OpAsmParser &parser,
833 OperationState &result) {
834 return parseComponentInterface<CombComponentOp>(parser, result);
837LogicalResult CombComponentOp::verify() {
840 if (std::distance(wIt.begin(), wIt.end()) != 1)
841 return emitOpError() <<
"requires exactly one "
842 << WiresOp::getOperationName() <<
" op.";
846 if (std::distance(cIt.begin(), cIt.end()) != 0)
847 return emitOpError() <<
"must not have a `" << ControlOp::getOperationName()
851 bool hasNoAssignments =
852 getWiresOp().getBodyBlock()->getOps<AssignOp>().
empty();
853 if (hasNoAssignments)
855 "The component currently does nothing. It needs to either have "
856 "continuous assignments in the Wires region.");
859 auto cells = getOps<CellInterface>();
860 for (
auto cell : cells) {
861 if (!cell.isCombinational())
862 return emitOpError() <<
"contains non-combinational cell "
863 << cell.instanceName();
867 auto groups = getWiresOp().getOps<GroupOp>();
869 return emitOpError() <<
"contains group " << (*groups.begin()).getSymName();
874 auto combGroups = getWiresOp().getOps<CombGroupOp>();
875 if (!combGroups.empty())
876 return emitOpError() <<
"contains comb group "
877 << (*combGroups.begin()).getSymName();
882void CombComponentOp::build(OpBuilder &builder, OperationState &result,
883 StringAttr name, ArrayRef<PortInfo> ports) {
887void CombComponentOp::getAsmBlockArgumentNames(
891 auto ports = getPortNames();
892 auto *block = &getRegion()->front();
893 for (
size_t i = 0, e = block->getNumArguments(); i != e; ++i)
894 setNameFn(block->getArgument(i), cast<StringAttr>(ports[i]).getValue());
903SmallVector<InvokeOp, 4> ControlOp::getInvokeOps() {
904 SmallVector<InvokeOp, 4> ret;
905 this->walk([&](InvokeOp invokeOp) { ret.push_back(invokeOp); });
913void SeqOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
914 MLIRContext *context) {
915 patterns.add(collapseControl<SeqOp>);
924LogicalResult StaticSeqOp::verify() {
926 auto &ops = (*this).getBodyBlock()->getOperations();
927 if (!llvm::all_of(ops, [&](Operation &op) {
return isStaticControl(&op); })) {
928 return emitOpError(
"StaticSeqOp has non static control within it");
934void StaticSeqOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
935 MLIRContext *context) {
936 patterns.add(collapseControl<StaticSeqOp>);
937 patterns.add(emptyControl<StaticSeqOp>);
945LogicalResult ParOp::verify() {
950 for (EnableOp op :
getBodyBlock()->getOps<EnableOp>()) {
951 StringRef groupName = op.getGroupName();
952 if (groupNames.count(groupName))
953 return emitOpError() <<
"cannot enable the same group: \"" << groupName
954 <<
"\" more than once.";
955 groupNames.insert(groupName);
961void ParOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
962 MLIRContext *context) {
963 patterns.add(collapseControl<ParOp>);
972LogicalResult StaticParOp::verify() {
977 for (EnableOp op :
getBodyBlock()->getOps<EnableOp>()) {
978 StringRef groupName = op.getGroupName();
979 if (groupNames.count(groupName))
980 return emitOpError() <<
"cannot enable the same group: \"" << groupName
981 <<
"\" more than once.";
982 groupNames.insert(groupName);
986 auto &ops = (*this).getBodyBlock()->getOperations();
987 for (Operation &op : ops) {
989 return op.emitOpError(
"StaticParOp has non static control within it");
996void StaticParOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
997 MLIRContext *context) {
998 patterns.add(collapseControl<StaticParOp>);
999 patterns.add(emptyControl<StaticParOp>);
1006LogicalResult WiresOp::verify() {
1007 auto componentInterface = (*this)->getParentOfType<ComponentInterface>();
1008 if (llvm::isa<ComponentOp>(componentInterface)) {
1009 auto component = llvm::cast<ComponentOp>(componentInterface);
1010 auto control = component.getControlOp();
1014 if (!isa<GroupInterface>(op))
1016 auto group = cast<GroupInterface>(op);
1017 auto groupName = group.symName();
1018 if (mlir::SymbolTable::symbolKnownUseEmpty(groupName, control))
1019 return op.emitOpError()
1020 <<
"with name: " << groupName
1021 <<
" is unused in the control execution schedule";
1028 for (
auto thisAssignment :
getBodyBlock()->getOps<AssignOp>()) {
1032 if (thisAssignment.getGuard())
1035 Value dest = thisAssignment.getDest();
1036 for (Operation *user : dest.getUsers()) {
1037 auto assignUser = dyn_cast<AssignOp>(user);
1038 if (!assignUser || assignUser.getDest() != dest ||
1039 assignUser == thisAssignment)
1042 return user->emitOpError() <<
"destination is already continuously "
1043 "driven. Other assignment is "
1057 Operation *definingOp = value.getDefiningOp();
1058 if (definingOp ==
nullptr || definingOp->hasTrait<
Combinational>())
1064 if (isa<InstanceOp>(definingOp))
1068 if (isa<comb::CombDialect, hw::HWDialect>(definingOp->getDialect()))
1072 if (
auto r = dyn_cast<RegisterOp>(definingOp)) {
1073 return value == r.getOut()
1075 : group->emitOpError()
1076 <<
"with register: \"" << r.instanceName()
1077 <<
"\" is conducting a memory store. This is not "
1079 }
else if (
auto m = dyn_cast<MemoryOp>(definingOp)) {
1080 auto writePorts = {m.writeData(), m.writeEn()};
1081 return (llvm::none_of(
writePorts, [&](Value p) {
return p == value; }))
1083 : group->emitOpError()
1084 <<
"with memory: \"" << m.instanceName()
1085 <<
"\" is conducting a memory store. This "
1086 "is not combinational.";
1089 std::string portName =
1090 valueName(group->getParentOfType<ComponentOp>(), value);
1091 return group->emitOpError() <<
"with port: " << portName
1092 <<
". This operation is not combinational.";
1097LogicalResult CombGroupOp::verify() {
1099 auto assign = dyn_cast<AssignOp>(op);
1100 if (assign ==
nullptr)
1102 Value dst = assign.getDest(), src = assign.getSrc();
1113GroupGoOp GroupOp::getGoOp() {
1115 size_t nOps = std::distance(goOps.begin(), goOps.end());
1116 return nOps ? *goOps.begin() : GroupGoOp();
1119GroupDoneOp GroupOp::getDoneOp() {
1121 return cast<GroupDoneOp>(body->getTerminator());
1127void CycleOp::print(OpAsmPrinter &p) {
1130 auto start = this->getStart();
1131 auto end = this->getEnd();
1132 if (
end.has_value()) {
1133 p <<
"[" << start <<
":" <<
end.value() <<
"]";
1139ParseResult CycleOp::parse(OpAsmParser &parser, OperationState &result) {
1140 SmallVector<OpAsmParser::UnresolvedOperand, 2> operandInfos;
1142 uint32_t startLiteral;
1143 uint32_t endLiteral;
1145 auto hasEnd = succeeded(parser.parseOptionalLSquare());
1147 if (parser.parseInteger(startLiteral)) {
1148 parser.emitError(parser.getNameLoc(),
"Could not parse start cycle");
1152 auto start = parser.getBuilder().getI32IntegerAttr(startLiteral);
1153 result.addAttribute(getStartAttrName(result.name), start);
1156 if (parser.parseColon())
1159 if (
auto res = parser.parseOptionalInteger(endLiteral); res.has_value()) {
1160 auto end = parser.getBuilder().getI32IntegerAttr(endLiteral);
1161 result.addAttribute(getEndAttrName(result.name),
end);
1164 if (parser.parseRSquare())
1168 result.addTypes(parser.getBuilder().getI1Type());
1173LogicalResult CycleOp::verify() {
1174 uint32_t latency = this->getGroupLatency();
1176 if (this->getStart() >= latency) {
1177 emitOpError(
"start cycle must be less than the group latency");
1181 if (this->getEnd().has_value()) {
1182 if (this->getStart() >= this->getEnd().value()) {
1183 emitOpError(
"start cycle must be less than end cycle");
1187 if (this->getEnd() >= latency) {
1188 emitOpError(
"end cycle must be less than the group latency");
1196uint32_t CycleOp::getGroupLatency() {
1197 auto group = (*this)->getParentOfType<StaticGroupOp>();
1198 return group.getLatency();
1205 return FloatingPointStandard::IEEE754;
1209 return FloatingPointStandard::IEEE754;
1213 return FloatingPointStandard::IEEE754;
1217 return FloatingPointStandard::IEEE754;
1221 return FloatingPointStandard::IEEE754;
1225 return FloatingPointStandard::IEEE754;
1228std::string AddFOpIEEE754::getCalyxLibraryName() {
return "std_addFN"; }
1230std::string MulFOpIEEE754::getCalyxLibraryName() {
return "std_mulFN"; }
1232std::string CompareFOpIEEE754::getCalyxLibraryName() {
return "std_compareFN"; }
1234std::string FpToIntOpIEEE754::getCalyxLibraryName() {
return "std_fpToInt"; }
1236std::string IntToFpOpIEEE754::getCalyxLibraryName() {
return "std_intToFp"; }
1238std::string DivSqrtOpIEEE754::getCalyxLibraryName() {
return "std_divSqrtFN"; }
1247 return llvm::any_of(port.getUses(), [&](
auto &&use) {
1248 auto assignOp = dyn_cast<AssignOp>(use.getOwner());
1249 if (assignOp == nullptr)
1252 Operation *parent = assignOp->getParentOp();
1253 if (isa<WiresOp>(parent))
1262 Value expected = isDriven ? assignOp.getDest() : assignOp.getSrc();
1263 return expected == port && group == parent;
1275 if (
auto cell = dyn_cast<CellInterface>(port.getDefiningOp());
1276 cell && cell.direction(port) == calyx::Direction::Output)
1277 return groupOp.drivesAnyPort(cell.getInputPorts());
1282LogicalResult GroupOp::drivesPort(Value port) {
1286LogicalResult CombGroupOp::drivesPort(Value port) {
1290LogicalResult StaticGroupOp::drivesPort(Value port) {
1297 return success(llvm::all_of(ports, [&](Value port) {
1302LogicalResult GroupOp::drivesAllPorts(ValueRange ports) {
1306LogicalResult CombGroupOp::drivesAllPorts(ValueRange ports) {
1310LogicalResult StaticGroupOp::drivesAllPorts(ValueRange ports) {
1317 return success(llvm::any_of(ports, [&](Value port) {
1322LogicalResult GroupOp::drivesAnyPort(ValueRange ports) {
1326LogicalResult CombGroupOp::drivesAnyPort(ValueRange ports) {
1330LogicalResult StaticGroupOp::drivesAnyPort(ValueRange ports) {
1337 return success(llvm::any_of(ports, [&](Value port) {
1342LogicalResult GroupOp::readsAnyPort(ValueRange ports) {
1346LogicalResult CombGroupOp::readsAnyPort(ValueRange ports) {
1350LogicalResult StaticGroupOp::readsAnyPort(ValueRange ports) {
1357 GroupInterface group) {
1358 Operation *destDefiningOp = assign.getDest().getDefiningOp();
1359 if (destDefiningOp ==
nullptr)
1361 auto destCell = dyn_cast<CellInterface>(destDefiningOp);
1362 if (destCell ==
nullptr)
1365 LogicalResult verifyWrites =
1366 TypeSwitch<Operation *, LogicalResult>(destCell)
1367 .Case<RegisterOp>([&](
auto op) {
1370 return succeeded(group.drivesAnyPort({op.getWriteEn(), op.getIn()}))
1371 ? group.drivesAllPorts({op.getWriteEn(), op.getIn()})
1374 .Case<MemoryOp>([&](
auto op) {
1375 SmallVector<Value> requiredWritePorts;
1378 requiredWritePorts.push_back(op.writeEn());
1379 requiredWritePorts.push_back(op.writeData());
1380 for (Value address : op.addrPorts())
1381 requiredWritePorts.push_back(address);
1386 group.drivesAnyPort({op.writeData(), op.writeEn()}))
1387 ? group.drivesAllPorts(requiredWritePorts)
1390 .Case<AndLibOp, OrLibOp, XorLibOp, AddLibOp, SubLibOp, GtLibOp,
1391 LtLibOp, EqLibOp, NeqLibOp, GeLibOp, LeLibOp, LshLibOp,
1392 RshLibOp, SgtLibOp, SltLibOp, SeqLibOp, SneqLibOp, SgeLibOp,
1393 SleLibOp, SrshLibOp>([&](
auto op) {
1394 Value lhs = op.getLeft(), rhs = op.getRight();
1395 return succeeded(group.drivesAnyPort({lhs, rhs}))
1396 ? group.drivesAllPorts({lhs, rhs})
1399 .Default([&](
auto op) {
return success(); });
1401 if (failed(verifyWrites))
1402 return group->emitOpError()
1403 <<
"with cell: " << destCell->getName() <<
" \""
1404 << destCell.instanceName()
1405 <<
"\" is performing a write and failed to drive all necessary "
1408 Operation *srcDefiningOp = assign.getSrc().getDefiningOp();
1409 if (srcDefiningOp ==
nullptr)
1411 auto srcCell = dyn_cast<CellInterface>(srcDefiningOp);
1412 if (srcCell ==
nullptr)
1415 LogicalResult verifyReads =
1416 TypeSwitch<Operation *, LogicalResult>(srcCell)
1417 .Case<MemoryOp>([&](
auto op) {
1421 return succeeded(group.readsAnyPort({op.readData()}))
1422 ? group.drivesAllPorts(op.addrPorts())
1425 .Default([&](
auto op) {
return success(); });
1427 if (failed(verifyReads))
1428 return group->emitOpError() <<
"with cell: " << srcCell->getName() <<
" \""
1429 << srcCell.instanceName()
1430 <<
"\" is having a read performed upon it, and "
1431 "failed to drive all necessary ports.";
1437 auto group = dyn_cast<GroupInterface>(op);
1438 if (group ==
nullptr)
1441 for (
auto &&groupOp : *group.getBody()) {
1442 auto assign = dyn_cast<AssignOp>(groupOp);
1443 if (assign ==
nullptr)
1459 ArrayRef<StringRef> portNames) {
1460 auto cellInterface = dyn_cast<CellInterface>(op);
1461 assert(cellInterface &&
"must implement the Cell interface");
1463 std::string prefix = cellInterface.instanceName().str() +
".";
1464 for (
size_t i = 0, e = portNames.size(); i != e; ++i)
1465 setNameFn(op->getResult(i), prefix + portNames[i].str());
1476 bool isDestination) {
1477 Operation *definingOp = value.getDefiningOp();
1478 bool isComponentPort = isa<BlockArgument>(value),
1479 isCellInterfacePort = isa_and_nonnull<CellInterface>(definingOp);
1480 assert((isComponentPort || isCellInterfacePort) &&
"Not a port.");
1484 : cast<CellInterface>(definingOp).portInfo(value);
1486 bool isSource = !isDestination;
1489 (isDestination && isComponentPort) || (isSource && isCellInterfacePort)
1496 <<
"has a " << (isComponentPort ?
"component" :
"cell")
1498 << (isDestination ?
"destination" :
"source")
1499 <<
" with the incorrect direction.";
1506 bool isSource = !isDestination;
1507 Value value = isDestination ? op.getDest() : op.getSrc();
1512 if (isDestination && !isa<GroupGoOp, GroupDoneOp>(value.getDefiningOp()))
1513 return op->emitOpError(
1514 "has an invalid destination port. It must be drive-able.");
1521LogicalResult AssignOp::verify() {
1522 bool isDestination =
true, isSource =
false;
1531ParseResult AssignOp::parse(OpAsmParser &parser, OperationState &result) {
1532 OpAsmParser::UnresolvedOperand destination;
1533 if (parser.parseOperand(destination) || parser.parseEqual())
1539 OpAsmParser::UnresolvedOperand guardOrSource;
1540 if (parser.parseOperand(guardOrSource))
1545 OpAsmParser::UnresolvedOperand source;
1546 bool hasGuard = succeeded(parser.parseOptionalQuestion());
1549 if (parser.parseOperand(source))
1554 if (parser.parseColonType(type) ||
1555 parser.resolveOperand(destination, type, result.operands))
1559 Type i1Type = parser.getBuilder().getI1Type();
1562 if (parser.resolveOperand(source, type, result.operands) ||
1563 parser.resolveOperand(guardOrSource, i1Type, result.operands))
1567 if (parser.resolveOperand(guardOrSource, type, result.operands))
1574void AssignOp::print(OpAsmPrinter &p) {
1575 p <<
" " << getDest() <<
" = ";
1577 Value bguard = getGuard(), source = getSrc();
1580 p << bguard <<
" ? ";
1584 p << source <<
" : " << source.getType();
1593ComponentInterface InstanceOp::getReferencedComponent() {
1594 auto module = (*this)->getParentOfType<ModuleOp>();
1598 return module.lookupSymbol<ComponentInterface>(getComponentName());
1606 ComponentInterface referencedComponent) {
1607 auto module = instance->getParentOfType<ModuleOp>();
1608 StringRef entryPointName =
1609 module->getAttrOfType<StringAttr>("calyx.entrypoint");
1610 if (instance.getComponentName() == entryPointName)
1611 return instance.emitOpError()
1612 <<
"cannot reference the entry-point component: '" << entryPointName
1616 SmallVector<PortInfo> componentPorts = referencedComponent.getPortInfo();
1617 size_t numPorts = componentPorts.size();
1619 size_t numResults = instance.getNumResults();
1620 if (numResults != numPorts)
1621 return instance.emitOpError()
1622 <<
"has a wrong number of results; expected: " << numPorts
1623 <<
" but got " << numResults;
1625 for (
size_t i = 0; i != numResults; ++i) {
1626 auto resultType = instance.getResult(i).getType();
1627 auto expectedType = componentPorts[i].type;
1628 if (resultType == expectedType)
1630 return instance.emitOpError()
1631 <<
"result type for " << componentPorts[i].name <<
" must be "
1632 << expectedType <<
", but got " << resultType;
1637LogicalResult InstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1638 Operation *op = *
this;
1639 auto module = op->getParentOfType<ModuleOp>();
1640 Operation *referencedComponent =
1641 symbolTable.lookupNearestSymbolFrom(module, getComponentNameAttr());
1642 if (referencedComponent ==
nullptr)
1643 return emitError() <<
"referencing component: '" << getComponentName()
1644 <<
"', which does not exist.";
1646 Operation *shadowedComponentName =
1647 symbolTable.lookupNearestSymbolFrom(module, getSymNameAttr());
1648 if (shadowedComponentName !=
nullptr)
1649 return emitError() <<
"instance symbol: '" << instanceName()
1650 <<
"' is already a symbol for another component.";
1653 auto parentComponent = op->getParentOfType<ComponentOp>();
1654 if (parentComponent == referencedComponent)
1655 return emitError() <<
"recursive instantiation of its parent component: '"
1656 << getComponentName() <<
"'";
1658 assert(isa<ComponentInterface>(referencedComponent) &&
1659 "Should be a ComponentInterface.");
1661 cast<ComponentInterface>(referencedComponent));
1669SmallVector<StringRef> InstanceOp::portNames() {
1670 SmallVector<StringRef> portNames;
1671 for (Attribute name : getReferencedComponent().getPortNames())
1672 portNames.push_back(cast<StringAttr>(name).getValue());
1676SmallVector<Direction> InstanceOp::portDirections() {
1677 SmallVector<Direction> portDirections;
1679 portDirections.push_back(port.direction);
1680 return portDirections;
1683SmallVector<DictionaryAttr> InstanceOp::portAttributes() {
1684 SmallVector<DictionaryAttr> portAttributes;
1686 portAttributes.push_back(port.attributes);
1687 return portAttributes;
1690bool InstanceOp::isCombinational() {
1691 return isa<CombComponentOp>(getReferencedComponent());
1701 auto module = (*this)->getParentOfType<ModuleOp>();
1705 return module.lookupSymbol<hw::HWModuleExternOp>(getPrimitiveName());
1714 auto module = instance->getParentOfType<ModuleOp>();
1715 StringRef entryPointName =
1716 module->getAttrOfType<StringAttr>("calyx.entrypoint");
1717 if (instance.getPrimitiveName() == entryPointName)
1718 return instance.emitOpError()
1719 <<
"cannot reference the entry-point component: '" << entryPointName
1723 auto primitivePorts = referencedPrimitive.getPortList();
1724 size_t numPorts = primitivePorts.size();
1726 size_t numResults = instance.getNumResults();
1727 if (numResults != numPorts)
1728 return instance.emitOpError()
1729 <<
"has a wrong number of results; expected: " << numPorts
1730 <<
" but got " << numResults;
1733 ArrayAttr modParameters = referencedPrimitive.getParameters();
1734 ArrayAttr parameters = instance.getParameters().value_or(ArrayAttr());
1735 size_t numExpected = modParameters.size();
1736 size_t numParams = parameters.size();
1737 if (numParams != numExpected)
1738 return instance.emitOpError()
1739 <<
"has the wrong number of parameters; expected: " << numExpected
1740 <<
" but got " << numParams;
1742 for (
size_t i = 0; i != numExpected; ++i) {
1743 auto param = cast<circt::hw::ParamDeclAttr>(parameters[i]);
1744 auto modParam = cast<circt::hw::ParamDeclAttr>(modParameters[i]);
1746 auto paramName = param.getName();
1747 if (paramName != modParam.getName())
1748 return instance.emitOpError()
1749 <<
"parameter #" << i <<
" should have name " << modParam.getName()
1750 <<
" but has name " << paramName;
1752 if (param.getType() != modParam.getType())
1753 return instance.emitOpError()
1754 <<
"parameter " << paramName <<
" should have type "
1755 << modParam.getType() <<
" but has type " << param.getType();
1759 if (!param.getValue())
1760 return instance.emitOpError(
"parameter ")
1761 << paramName <<
" must have a value";
1764 for (
size_t i = 0; i != numResults; ++i) {
1765 auto resultType = instance.getResult(i).getType();
1766 auto expectedType = primitivePorts[i].type;
1767 auto replacedType = hw::evaluateParametricType(
1768 instance.getLoc(), instance.getParametersAttr(), expectedType);
1769 if (failed(replacedType))
1771 if (resultType == replacedType)
1773 return instance.emitOpError()
1774 <<
"result type for " << primitivePorts[i].name <<
" must be "
1775 << expectedType <<
", but got " << resultType;
1781PrimitiveOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1782 Operation *op = *
this;
1783 auto module = op->getParentOfType<ModuleOp>();
1784 Operation *referencedPrimitive =
1785 symbolTable.lookupNearestSymbolFrom(module, getPrimitiveNameAttr());
1786 if (referencedPrimitive ==
nullptr)
1787 return emitError() <<
"referencing primitive: '" << getPrimitiveName()
1788 <<
"', which does not exist.";
1790 Operation *shadowedPrimitiveName =
1791 symbolTable.lookupNearestSymbolFrom(module, getSymNameAttr());
1792 if (shadowedPrimitiveName !=
nullptr)
1793 return emitError() <<
"instance symbol: '" << instanceName()
1794 <<
"' is already a symbol for another primitive.";
1798 if (parentPrimitive == referencedPrimitive)
1799 return emitError() <<
"recursive instantiation of its parent primitive: '"
1800 << getPrimitiveName() <<
"'";
1802 assert(isa<hw::HWModuleExternOp>(referencedPrimitive) &&
1803 "Should be a HardwareModuleExternOp.");
1806 cast<hw::HWModuleExternOp>(referencedPrimitive));
1814SmallVector<StringRef> PrimitiveOp::portNames() {
1815 SmallVector<StringRef> portNames;
1816 auto ports = getReferencedPrimitive().getPortList();
1817 for (
auto port : ports)
1818 portNames.push_back(port.name.getValue());
1824 switch (direction) {
1825 case hw::ModulePort::Direction::Input:
1826 return Direction::Input;
1827 case hw::ModulePort::Direction::Output:
1828 return Direction::Output;
1829 case hw::ModulePort::Direction::InOut:
1830 llvm_unreachable(
"InOut ports not supported by Calyx");
1832 llvm_unreachable(
"Impossible port type");
1835SmallVector<Direction> PrimitiveOp::portDirections() {
1836 SmallVector<Direction> portDirections;
1837 auto ports = getReferencedPrimitive().getPortList();
1840 return portDirections;
1843bool PrimitiveOp::isCombinational() {
return false; }
1849 DictionaryAttr dict) {
1853 llvm::SmallVector<NamedAttribute> attrs;
1854 for (NamedAttribute attr : dict) {
1855 Dialect *dialect = attr.getNameDialect();
1856 if (dialect ==
nullptr || !isa<CalyxDialect>(*dialect))
1858 StringRef name = attr.getName().strref();
1859 StringAttr newName = builder.getStringAttr(std::get<1>(name.split(
".")));
1860 attr.setName(newName);
1861 attrs.push_back(attr);
1863 return builder.getDictionaryAttr(attrs);
1867SmallVector<DictionaryAttr> PrimitiveOp::portAttributes() {
1868 SmallVector<DictionaryAttr> portAttributes;
1869 OpBuilder builder(getContext());
1871 auto argAttrs = prim.getAllInputAttrs();
1872 auto resAttrs = prim.getAllOutputAttrs();
1873 for (
auto a : argAttrs)
1874 portAttributes.push_back(
1876 for (
auto a : resAttrs)
1877 portAttributes.push_back(
1879 return portAttributes;
1888 SmallVector<Attribute> ¶meters) {
1890 return parser.parseCommaSeparatedList(
1891 OpAsmParser::Delimiter::OptionalLessGreater, [&]() {
1896 if (parser.parseKeywordOrString(&name) || parser.parseColonType(type))
1900 if (succeeded(parser.parseOptionalEqual())) {
1901 if (parser.parseAttribute(value, type))
1905 auto &builder = parser.getBuilder();
1906 parameters.push_back(hw::ParamDeclAttr::get(
1907 builder.getContext(), builder.getStringAttr(name), type, value));
1914 ArrayAttr ¶meters) {
1915 SmallVector<Attribute> parseParameters;
1919 parameters = ArrayAttr::get(parser.getContext(), parseParameters);
1926 ArrayAttr parameters) {
1927 if (parameters.empty())
1931 llvm::interleaveComma(parameters, p, [&](Attribute param) {
1932 auto paramAttr = cast<hw::ParamDeclAttr>(param);
1933 p << paramAttr.getName().getValue() <<
": " << paramAttr.getType();
1934 if (
auto value = paramAttr.getValue()) {
1936 p.printAttributeWithoutType(value);
1950 auto parent = (*this)->getParentOfType<GroupOp>();
1951 StringRef name = parent.getSymName();
1952 std::string resultName = name.str() +
".go";
1953 setNameFn(getResult(), resultName);
1956void GroupGoOp::print(OpAsmPrinter &p) {
printGroupPort(p, *
this); }
1958ParseResult GroupGoOp::parse(OpAsmParser &parser, OperationState &result) {
1962 result.addTypes(parser.getBuilder().getI1Type());
1970LogicalResult GroupDoneOp::verify() {
1971 Operation *srcOp = getSrc().getDefiningOp();
1972 Value optionalGuard = getGuard();
1973 Operation *guardOp = optionalGuard ? optionalGuard.getDefiningOp() :
nullptr;
1974 bool noGuard = (guardOp ==
nullptr);
1976 if (srcOp ==
nullptr)
1980 if (isa<hw::ConstantOp>(srcOp) && (noGuard || isa<hw::ConstantOp>(guardOp)))
1981 return emitOpError() <<
"with constant source"
1982 << (noGuard ?
"" :
" and constant guard")
1983 <<
". This should be a combinational group.";
1988void GroupDoneOp::print(OpAsmPrinter &p) {
printGroupPort(p, *
this); }
1990ParseResult GroupDoneOp::parse(OpAsmParser &parser, OperationState &result) {
1997void ConstantOp::getAsmResultNames(
1998 function_ref<
void(Value, StringRef)> setNameFn) {
1999 if (isa<FloatAttr>(getValue())) {
2000 setNameFn(getResult(),
"cst");
2003 auto intCst = llvm::dyn_cast<IntegerAttr>(getValue());
2004 auto intType = llvm::dyn_cast<IntegerType>(getType());
2007 if (intType && intType.getWidth() == 1)
2008 return setNameFn(getResult(), intCst.getInt() > 0 ?
"true" :
"false");
2011 SmallString<32> specialNameBuffer;
2012 llvm::raw_svector_ostream specialName(specialNameBuffer);
2013 specialName <<
'c' << intCst.getValue();
2015 specialName <<
'_' << getType();
2016 setNameFn(getResult(), specialName.str());
2019LogicalResult ConstantOp::verify() {
2020 auto type = getType();
2021 assert(isa<IntegerType>(type) &&
"must be an IntegerType");
2023 if (
auto valTyBitWidth = getValue().getType().getIntOrFloatBitWidth();
2024 valTyBitWidth != type.getIntOrFloatBitWidth()) {
2025 return emitOpError() <<
"value type bit width" << valTyBitWidth
2026 <<
" must match return type: "
2027 << type.getIntOrFloatBitWidth();
2030 if (llvm::isa<IntegerType>(type) &&
2031 !llvm::cast<IntegerType>(type).isSignless())
2032 return emitOpError(
"integer return type must be signless");
2034 if (!llvm::isa<IntegerAttr, FloatAttr>(getValue())) {
2035 return emitOpError(
"value must be an integer or float attribute");
2041OpFoldResult calyx::ConstantOp::fold(FoldAdaptor adaptor) {
2042 return getValueAttr();
2045void calyx::ConstantOp::build(OpBuilder &builder, OperationState &state,
2046 StringRef symName, Attribute attr, Type type) {
2047 state.addAttribute(SymbolTable::getSymbolAttrName(),
2048 builder.getStringAttr(symName));
2049 state.addAttribute(
"value", attr);
2050 SmallVector<Type> types;
2051 types.push_back(type);
2052 state.addTypes(types);
2055SmallVector<StringRef> ConstantOp::portNames() {
return {
"out"}; }
2057SmallVector<Direction> ConstantOp::portDirections() {
return {
Output}; }
2059SmallVector<DictionaryAttr> ConstantOp::portAttributes() {
2060 return {DictionaryAttr::get(getContext())};
2063bool ConstantOp::isCombinational() {
return true; }
2074SmallVector<StringRef> RegisterOp::portNames() {
2078SmallVector<Direction> RegisterOp::portDirections() {
2082SmallVector<DictionaryAttr> RegisterOp::portAttributes() {
2083 MLIRContext *context = getContext();
2084 IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1);
2085 NamedAttrList writeEn,
clk, reset, done;
2086 writeEn.append(
goPort, isSet);
2091 DictionaryAttr::get(context),
2092 writeEn.getDictionary(context),
2093 clk.getDictionary(context),
2094 reset.getDictionary(context),
2095 DictionaryAttr::get(context),
2096 done.getDictionary(context)
2100bool RegisterOp::isCombinational() {
return false; }
2111SmallVector<StringRef> MemoryOp::portNames() {
2112 SmallVector<StringRef> portNames;
2113 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i) {
2115 StringAttr::get(this->getContext(),
"addr" + std::to_string(i));
2116 portNames.push_back(nameAttr.getValue());
2118 portNames.append({
"write_data",
"write_en",
clkPort,
"read_data",
donePort});
2122SmallVector<Direction> MemoryOp::portDirections() {
2123 SmallVector<Direction> portDirections;
2124 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2125 portDirections.push_back(
Input);
2127 return portDirections;
2130SmallVector<DictionaryAttr> MemoryOp::portAttributes() {
2131 SmallVector<DictionaryAttr> portAttributes;
2132 MLIRContext *context = getContext();
2133 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2134 portAttributes.push_back(DictionaryAttr::get(context));
2137 IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1);
2138 NamedAttrList writeEn,
clk, reset, done;
2139 writeEn.append(
goPort, isSet);
2142 portAttributes.append({DictionaryAttr::get(context),
2143 writeEn.getDictionary(context),
2144 clk.getDictionary(context),
2145 DictionaryAttr::get(context),
2146 done.getDictionary(context)}
2148 return portAttributes;
2151void MemoryOp::build(OpBuilder &builder, OperationState &state,
2152 StringRef instanceName, int64_t width,
2153 ArrayRef<int64_t> sizes, ArrayRef<int64_t> addrSizes) {
2154 state.addAttribute(SymbolTable::getSymbolAttrName(),
2155 builder.getStringAttr(instanceName));
2156 state.addAttribute(
"width", builder.getI64IntegerAttr(width));
2157 state.addAttribute(
"sizes", builder.getI64ArrayAttr(sizes));
2158 state.addAttribute(
"addrSizes", builder.getI64ArrayAttr(addrSizes));
2159 SmallVector<Type> types;
2160 for (int64_t size : addrSizes)
2161 types.push_back(builder.getIntegerType(size));
2162 types.push_back(builder.getIntegerType(width));
2163 types.push_back(builder.getI1Type());
2164 types.push_back(builder.getI1Type());
2165 types.push_back(builder.getIntegerType(width));
2166 types.push_back(builder.getI1Type());
2167 state.addTypes(types);
2170LogicalResult MemoryOp::verify() {
2171 ArrayRef<Attribute> opSizes = getSizes().getValue();
2172 ArrayRef<Attribute> opAddrSizes = getAddrSizes().getValue();
2173 size_t numDims = getSizes().size();
2174 size_t numAddrs = getAddrSizes().size();
2175 if (numDims != numAddrs)
2176 return emitOpError(
"mismatched number of dimensions (")
2177 << numDims <<
") and address sizes (" << numAddrs <<
")";
2179 size_t numExtraPorts = 5;
2180 if (getNumResults() != numAddrs + numExtraPorts)
2181 return emitOpError(
"incorrect number of address ports, expected ")
2184 for (
size_t i = 0; i < numDims; ++i) {
2185 int64_t size = cast<IntegerAttr>(opSizes[i]).getInt();
2186 int64_t addrSize = cast<IntegerAttr>(opAddrSizes[i]).getInt();
2187 if (llvm::Log2_64_Ceil(size) > addrSize)
2188 return emitOpError(
"address size (")
2189 << addrSize <<
") for dimension " << i
2190 <<
" can't address the entire range (" << size <<
")";
2205SmallVector<StringRef> SeqMemoryOp::portNames() {
2206 SmallVector<StringRef> portNames;
2207 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i) {
2209 StringAttr::get(this->getContext(),
"addr" + std::to_string(i));
2210 portNames.push_back(nameAttr.getValue());
2212 portNames.append({
clkPort,
"reset",
"content_en",
"write_en",
"write_data",
2213 "read_data",
"done"});
2217SmallVector<Direction> SeqMemoryOp::portDirections() {
2218 SmallVector<Direction> portDirections;
2219 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2220 portDirections.push_back(
Input);
2222 return portDirections;
2225SmallVector<DictionaryAttr> SeqMemoryOp::portAttributes() {
2226 SmallVector<DictionaryAttr> portAttributes;
2227 MLIRContext *context = getContext();
2228 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2229 portAttributes.push_back(DictionaryAttr::get(context));
2231 OpBuilder builder(context);
2233 IntegerAttr isSet = IntegerAttr::get(builder.getIndexType(), 1);
2234 IntegerAttr isTwo = IntegerAttr::get(builder.getIndexType(), 2);
2235 NamedAttrList done,
clk, reset, contentEn;
2239 contentEn.append(
goPort, isTwo);
2240 portAttributes.append({
clk.getDictionary(context),
2241 reset.getDictionary(context),
2242 contentEn.getDictionary(context),
2243 DictionaryAttr::get(context),
2244 DictionaryAttr::get(context),
2245 DictionaryAttr::get(context),
2246 done.getDictionary(context)}
2248 return portAttributes;
2251void SeqMemoryOp::build(OpBuilder &builder, OperationState &state,
2252 StringRef instanceName, int64_t width,
2253 ArrayRef<int64_t> sizes, ArrayRef<int64_t> addrSizes) {
2254 state.addAttribute(SymbolTable::getSymbolAttrName(),
2255 builder.getStringAttr(instanceName));
2256 state.addAttribute(
"width", builder.getI64IntegerAttr(width));
2257 state.addAttribute(
"sizes", builder.getI64ArrayAttr(sizes));
2258 state.addAttribute(
"addrSizes", builder.getI64ArrayAttr(addrSizes));
2259 SmallVector<Type> types;
2260 for (int64_t size : addrSizes)
2261 types.push_back(builder.getIntegerType(size));
2262 types.push_back(builder.getI1Type());
2263 types.push_back(builder.getI1Type());
2264 types.push_back(builder.getI1Type());
2265 types.push_back(builder.getI1Type());
2266 types.push_back(builder.getIntegerType(width));
2267 types.push_back(builder.getIntegerType(width));
2268 types.push_back(builder.getI1Type());
2269 state.addTypes(types);
2272LogicalResult SeqMemoryOp::verify() {
2273 ArrayRef<Attribute> opSizes = getSizes().getValue();
2274 ArrayRef<Attribute> opAddrSizes = getAddrSizes().getValue();
2275 size_t numDims = getSizes().size();
2276 size_t numAddrs = getAddrSizes().size();
2277 if (numDims != numAddrs)
2278 return emitOpError(
"mismatched number of dimensions (")
2279 << numDims <<
") and address sizes (" << numAddrs <<
")";
2281 size_t numExtraPorts =
2283 if (getNumResults() != numAddrs + numExtraPorts)
2284 return emitOpError(
"incorrect number of address ports, expected ")
2287 for (
size_t i = 0; i < numDims; ++i) {
2288 int64_t size = cast<IntegerAttr>(opSizes[i]).getInt();
2289 int64_t addrSize = cast<IntegerAttr>(opAddrSizes[i]).getInt();
2290 if (llvm::Log2_64_Ceil(size) > addrSize)
2291 return emitOpError(
"address size (")
2292 << addrSize <<
") for dimension " << i
2293 <<
" can't address the entire range (" << size <<
")";
2302LogicalResult EnableOp::verify() {
2303 auto component = (*this)->getParentOfType<ComponentOp>();
2304 auto wiresOp = component.getWiresOp();
2305 StringRef name = getGroupName();
2307 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(name);
2309 return emitOpError() <<
"with group '" << name
2310 <<
"', which does not exist.";
2312 if (isa<CombGroupOp>(groupOp))
2313 return emitOpError() <<
"with group '" << name
2314 <<
"', which is a combinational group.";
2323LogicalResult IfOp::verify() {
2324 std::optional<StringRef> optGroupName = getGroupName();
2325 if (!optGroupName) {
2329 auto component = (*this)->getParentOfType<ComponentOp>();
2330 WiresOp wiresOp = component.getWiresOp();
2331 StringRef groupName = *optGroupName;
2332 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(groupName);
2334 return emitOpError() <<
"with group '" << groupName
2335 <<
"', which does not exist.";
2337 if (isa<GroupOp>(groupOp))
2338 return emitOpError() <<
"with group '" << groupName
2339 <<
"', which is not a combinational group.";
2341 if (failed(groupOp.drivesPort(getCond())))
2342 return emitError() <<
"with conditional op: '"
2344 <<
"' expected to be driven from group: '" << groupName
2345 <<
"' but no driver was found.";
2353template <
typename OpTy>
2355 static_assert(IsAny<OpTy, SeqOp, StaticSeqOp>(),
2356 "Should be a StaticSeqOp or SeqOp.");
2357 if (parent.getBodyBlock()->empty())
2358 return std::nullopt;
2359 auto &lastOp = parent.getBodyBlock()->back();
2360 if (
auto enableOp = dyn_cast<EnableOp>(lastOp))
2362 if (
auto seqOp = dyn_cast<SeqOp>(lastOp))
2364 if (
auto staticSeqOp = dyn_cast<StaticSeqOp>(lastOp))
2367 return std::nullopt;
2372template <
typename OpTy>
2374 static_assert(IsAny<OpTy, ParOp, StaticParOp>(),
2375 "Should be a StaticParOp or ParOp.");
2377 llvm::StringMap<EnableOp> enables;
2378 Block *body = parent.getBodyBlock();
2379 for (EnableOp op : body->getOps<EnableOp>())
2380 enables.insert(std::pair(op.getGroupName(), op));
2392template <
typename IfOpTy,
typename TailOpTy>
2394 static_assert(IsAny<TailOpTy, SeqOp, ParOp, StaticSeqOp, StaticParOp>(),
2395 "Should be a SeqOp, ParOp, StaticSeqOp, or StaticParOp.");
2396 static_assert(IsAny<IfOpTy, IfOp, StaticIfOp>(),
2397 "Should be a IfOp or StaticIfOp.");
2399 if (!op.thenBodyExists() || !op.elseBodyExists())
2401 if (op.getThenBody()->empty() || op.getElseBody()->empty())
2404 Block *thenBody = op.getThenBody(), *elseBody = op.getElseBody();
2405 return isa<TailOpTy>(thenBody->front()) && isa<TailOpTy>(elseBody->front());
2416template <
typename IfOpTy,
typename SeqOpTy>
2418 PatternRewriter &rewriter) {
2419 static_assert(IsAny<IfOpTy, IfOp, StaticIfOp>(),
2420 "Should be an IfOp or StaticIfOp.");
2421 static_assert(IsAny<SeqOpTy, SeqOp, StaticSeqOp>(),
2422 "Branches should be checking for an SeqOp or StaticSeqOp");
2423 if (!hasCommonTailPatternPreConditions<IfOpTy, SeqOpTy>(ifOp))
2425 auto thenControl = cast<SeqOpTy>(ifOp.getThenBody()->front()),
2426 elseControl = cast<SeqOpTy>(ifOp.getElseBody()->front());
2428 std::optional<EnableOp> lastThenEnableOp =
getLastEnableOp(thenControl),
2431 if (!lastThenEnableOp || !lastElseEnableOp)
2433 if (lastThenEnableOp->getGroupName() != lastElseEnableOp->getGroupName())
2439 rewriter.setInsertionPointAfter(ifOp);
2440 SeqOpTy seqOp = rewriter.create<SeqOpTy>(ifOp.getLoc());
2441 Block *body = seqOp.getBodyBlock();
2443 body->push_back(ifOp);
2444 rewriter.setInsertionPointToEnd(body);
2445 rewriter.create<EnableOp>(seqOp.getLoc(), lastThenEnableOp->getGroupName());
2448 rewriter.eraseOp(*lastThenEnableOp);
2449 rewriter.eraseOp(*lastElseEnableOp);
2466template <
typename OpTy,
typename ParOpTy>
2468 PatternRewriter &rewriter) {
2469 static_assert(IsAny<OpTy, IfOp, StaticIfOp>(),
2470 "Should be an IfOp or StaticIfOp.");
2471 static_assert(IsAny<ParOpTy, ParOp, StaticParOp>(),
2472 "Branches should be checking for an ParOp or StaticParOp");
2473 if (!hasCommonTailPatternPreConditions<OpTy, ParOpTy>(controlOp))
2475 auto thenControl = cast<ParOpTy>(controlOp.getThenBody()->front()),
2476 elseControl = cast<ParOpTy>(controlOp.getElseBody()->front());
2481 SmallVector<StringRef> groupNames;
2482 for (
auto aIndex = a.begin(); aIndex != a.end(); ++aIndex) {
2483 StringRef groupName = aIndex->getKey();
2484 auto bIndex = b.find(groupName);
2485 if (bIndex == b.end())
2488 groupNames.push_back(groupName);
2490 rewriter.eraseOp(aIndex->getValue());
2491 rewriter.eraseOp(bIndex->getValue());
2497 rewriter.setInsertionPointAfter(controlOp);
2499 ParOpTy parOp = rewriter.create<ParOpTy>(controlOp.getLoc());
2500 Block *body = parOp.getBodyBlock();
2501 controlOp->remove();
2502 body->push_back(controlOp);
2505 rewriter.setInsertionPointToEnd(body);
2506 for (StringRef groupName : groupNames)
2507 rewriter.create<EnableOp>(parOp.getLoc(), groupName);
2518 PatternRewriter &rewriter)
const override {
2519 if (!ifOp.getThenBody()->empty())
2521 if (ifOp.elseBodyExists() && !ifOp.getElseBody()->empty())
2530void IfOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
2531 MLIRContext *context) {
2533 patterns.add(commonTailPatternWithPar<IfOp, ParOp>);
2534 patterns.add(commonTailPatternWithSeq<IfOp, SeqOp>);
2540LogicalResult StaticIfOp::verify() {
2541 if (elseBodyExists()) {
2542 auto *elseBod = getElseBody();
2543 auto &elseOps = elseBod->getOperations();
2545 for (Operation &op : elseOps) {
2547 return op.emitOpError(
2548 "static if's else branch has non static control within it");
2553 auto *thenBod = getThenBody();
2554 auto &thenOps = thenBod->getOperations();
2555 for (Operation &op : thenOps) {
2558 return op.emitOpError(
2559 "static if's then branch has non static control within it");
2572 PatternRewriter &rewriter)
const override {
2573 if (!ifOp.getThenBody()->empty())
2575 if (ifOp.elseBodyExists() && !ifOp.getElseBody()->empty())
2584void StaticIfOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
2585 MLIRContext *context) {
2587 patterns.add(commonTailPatternWithPar<StaticIfOp, StaticParOp>);
2588 patterns.add(commonTailPatternWithSeq<StaticIfOp, StaticSeqOp>);
2594LogicalResult WhileOp::verify() {
2595 auto component = (*this)->getParentOfType<ComponentOp>();
2596 auto wiresOp = component.getWiresOp();
2598 std::optional<StringRef> optGroupName = getGroupName();
2599 if (!optGroupName) {
2603 StringRef groupName = *optGroupName;
2604 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(groupName);
2606 return emitOpError() <<
"with group '" << groupName
2607 <<
"', which does not exist.";
2609 if (isa<GroupOp>(groupOp))
2610 return emitOpError() <<
"with group '" << groupName
2611 <<
"', which is not a combinational group.";
2613 if (failed(groupOp.drivesPort(getCond())))
2614 return emitError() <<
"conditional op: '" <<
valueName(component, getCond())
2615 <<
"' expected to be driven from group: '" << groupName
2616 <<
"' but no driver was found.";
2621LogicalResult WhileOp::canonicalize(
WhileOp whileOp,
2622 PatternRewriter &rewriter) {
2623 if (whileOp.getBodyBlock()->empty()) {
2634LogicalResult StaticRepeatOp::verify() {
2635 for (
auto &&bodyOp : (*this).getRegion().front()) {
2638 return bodyOp.emitOpError(
2639 "static repeat has non static control within it");
2646template <
typename OpTy>
2647static LogicalResult
zeroRepeat(OpTy op, PatternRewriter &rewriter) {
2648 static_assert(IsAny<OpTy, RepeatOp, StaticRepeatOp>(),
2649 "Should be a RepeatOp or StaticPRepeatOp");
2650 if (op.getCount() == 0) {
2651 Block *controlBody = op.getBodyBlock();
2652 for (
auto &op : make_early_inc_range(*controlBody))
2655 rewriter.eraseOp(op);
2662void StaticRepeatOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
2663 MLIRContext *context) {
2664 patterns.add(emptyControl<StaticRepeatOp>);
2665 patterns.add(zeroRepeat<StaticRepeatOp>);
2671void RepeatOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
2672 MLIRContext *context) {
2673 patterns.add(emptyControl<RepeatOp>);
2674 patterns.add(zeroRepeat<RepeatOp>);
2684 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &ports,
2685 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &inputs,
2686 SmallVectorImpl<Attribute> &portNames,
2687 SmallVectorImpl<Attribute> &inputNames,
2688 SmallVectorImpl<Type> &types) {
2689 OpAsmParser::UnresolvedOperand port;
2690 OpAsmParser::UnresolvedOperand input;
2692 auto parseParameter = [&]() -> ParseResult {
2693 if (parser.parseOperand(port) || parser.parseEqual() ||
2694 parser.parseOperand(input))
2696 ports.push_back(port);
2697 portNames.push_back(StringAttr::get(parser.getContext(), port.name));
2698 inputs.push_back(input);
2699 inputNames.push_back(StringAttr::get(parser.getContext(), input.name));
2702 if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
2705 if (parser.parseArrow())
2708 if (parser.parseType(type))
2710 types.push_back(type);
2713 return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
2717ParseResult InvokeOp::parse(OpAsmParser &parser, OperationState &result) {
2718 StringAttr componentName;
2719 SmallVector<OpAsmParser::UnresolvedOperand, 4> ports;
2720 SmallVector<OpAsmParser::UnresolvedOperand, 4> inputs;
2721 SmallVector<Attribute> portNames;
2722 SmallVector<Attribute> inputNames;
2723 SmallVector<Type, 4> types;
2724 if (parser.parseSymbolName(componentName))
2726 FlatSymbolRefAttr callee = FlatSymbolRefAttr::get(componentName);
2727 SMLoc loc = parser.getCurrentLocation();
2729 SmallVector<Attribute, 4> refCells;
2730 if (succeeded(parser.parseOptionalLSquare())) {
2731 if (parser.parseCommaSeparatedList([&]() -> ParseResult {
2732 std::string refCellName;
2733 std::string externalMem;
2734 NamedAttrList refCellAttr;
2735 if (parser.parseKeywordOrString(&refCellName) ||
2736 parser.parseEqual() || parser.parseKeywordOrString(&externalMem))
2738 auto externalMemAttr =
2739 SymbolRefAttr::get(parser.getContext(), externalMem);
2740 refCellAttr.append(StringAttr::get(parser.getContext(), refCellName),
2743 DictionaryAttr::get(parser.getContext(), refCellAttr));
2746 parser.parseRSquare())
2749 result.addAttribute(
"refCellsMap",
2750 ArrayAttr::get(parser.getContext(), refCells));
2752 result.addAttribute(
"callee", callee);
2756 if (parser.resolveOperands(ports, types, loc, result.operands))
2758 if (parser.resolveOperands(inputs, types, loc, result.operands))
2760 result.addAttribute(
"portNames",
2761 ArrayAttr::get(parser.getContext(), portNames));
2762 result.addAttribute(
"inputNames",
2763 ArrayAttr::get(parser.getContext(), inputNames));
2767void InvokeOp::print(OpAsmPrinter &p) {
2768 p <<
" @" << getCallee() <<
"[";
2769 auto refCellNamesMap = getRefCellsMap();
2770 llvm::interleaveComma(refCellNamesMap, p, [&](Attribute attr) {
2771 auto dictAttr = cast<DictionaryAttr>(attr);
2772 llvm::interleaveComma(dictAttr, p, [&](NamedAttribute namedAttr) {
2773 auto refCellName = namedAttr.getName().str();
2775 cast<FlatSymbolRefAttr>(namedAttr.getValue()).getValue();
2776 p << refCellName <<
" = " << externalMem;
2781 auto ports = getPorts();
2782 auto inputs = getInputs();
2783 llvm::interleaveComma(llvm::zip(ports, inputs), p, [&](
auto arg) {
2784 p << std::get<0>(arg) <<
" = " << std::get<1>(arg);
2787 llvm::interleaveComma(ports, p, [&](
auto port) { p << port.getType(); });
2794 bool isDestination) {
2803 Operation *operation = value.getDefiningOp();
2804 if (operation ==
nullptr)
2806 if (
auto *dialect = operation->getDialect(); isa<comb::CombDialect>(dialect))
2812Value InvokeOp::getInstGoValue() {
2813 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2814 Operation *operation = componentOp.lookupSymbol(getCallee());
2815 Value ret =
nullptr;
2816 llvm::TypeSwitch<Operation *>(operation)
2817 .Case<RegisterOp>([&](
auto op) { ret = operation->getResult(1); })
2818 .Case<MemoryOp, DivSPipeLibOp, DivUPipeLibOp, MultPipeLibOp,
2819 RemSPipeLibOp, RemUPipeLibOp>(
2820 [&](
auto op) { ret = operation->getResult(2); })
2821 .Case<InstanceOp>([&](
auto op) {
2822 auto portInfo = op.getReferencedComponent().getPortInfo();
2823 for (
auto [portInfo, res] :
2824 llvm::zip(portInfo, operation->getResults())) {
2825 if (portInfo.hasAttribute(
goPort))
2829 .Case<PrimitiveOp>([&](
auto op) {
2830 auto moduleExternOp = op.getReferencedPrimitive();
2831 auto argAttrs = moduleExternOp.getAllInputAttrs();
2832 for (
auto [attr, res] :
llvm::zip(argAttrs, op.getResults())) {
2833 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2834 if (!dictAttr.empty()) {
2835 if (dictAttr.begin()->getName().getValue() ==
"calyx.go")
2845Value InvokeOp::getInstDoneValue() {
2846 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2847 Operation *operation = componentOp.lookupSymbol(getCallee());
2848 Value ret =
nullptr;
2849 llvm::TypeSwitch<Operation *>(operation)
2850 .Case<RegisterOp, MemoryOp, DivSPipeLibOp, DivUPipeLibOp, MultPipeLibOp,
2851 RemSPipeLibOp, RemUPipeLibOp>([&](
auto op) {
2852 size_t doneIdx = operation->getResults().size() - 1;
2853 ret = operation->getResult(doneIdx);
2855 .Case<InstanceOp>([&](
auto op) {
2856 InstanceOp instanceOp = cast<InstanceOp>(operation);
2857 auto portInfo = instanceOp.getReferencedComponent().getPortInfo();
2858 for (
auto [portInfo, res] :
2859 llvm::zip(portInfo, operation->getResults())) {
2860 if (portInfo.hasAttribute(
donePort))
2864 .Case<PrimitiveOp>([&](
auto op) {
2865 PrimitiveOp primOp = cast<PrimitiveOp>(operation);
2866 auto moduleExternOp = primOp.getReferencedPrimitive();
2867 auto resAttrs = moduleExternOp.getAllOutputAttrs();
2868 for (
auto [attr, res] :
llvm::zip(resAttrs, primOp.getResults())) {
2869 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2870 if (!dictAttr.empty()) {
2871 if (dictAttr.begin()->getName().getValue() ==
"calyx.done")
2886 std::string str = isGo ?
"calyx.go" :
"calyx.done";
2887 for (Attribute attr : moduleExternOp.getAllInputAttrs()) {
2888 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2889 ret = llvm::count_if(dictAttr, [&](NamedAttribute iter) {
2890 return iter.getName().getValue() == str;
2897LogicalResult InvokeOp::verify() {
2898 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2899 StringRef callee = getCallee();
2900 Operation *operation = componentOp.lookupSymbol(callee);
2903 return emitOpError() <<
"with instance '@" << callee
2904 <<
"', which does not exist.";
2906 if (getInputs().
empty() && getRefCellsMap().
empty()) {
2907 return emitOpError() <<
"'@" << callee
2908 <<
"' has zero input and output port connections and "
2909 "has no passing-by-reference cells; "
2910 "expected at least one.";
2912 size_t goPortNum = 0, donePortNum = 0;
2915 llvm::TypeSwitch<Operation *>(operation)
2916 .Case<RegisterOp, DivSPipeLibOp, DivUPipeLibOp, MemoryOp, MultPipeLibOp,
2917 RemSPipeLibOp, RemUPipeLibOp>(
2918 [&](
auto op) { goPortNum = 1, donePortNum = 1; })
2919 .Case<InstanceOp>([&](
auto op) {
2920 auto portInfo = op.getReferencedComponent().getPortInfo();
2928 .Case<PrimitiveOp>([&](
auto op) {
2929 auto moduleExternOp = op.getReferencedPrimitive();
2935 if (goPortNum != 1 && donePortNum != 1)
2936 return emitOpError()
2937 <<
"'@" << callee <<
"'"
2938 <<
" is a combinational component and cannot be invoked, which must "
2939 "have single go port and single done port.";
2941 auto ports = getPorts();
2942 auto inputs = getInputs();
2944 Value goValue = getInstGoValue();
2945 Value doneValue = getInstDoneValue();
2946 for (
auto [port, input, portName, inputName] :
2947 llvm::zip(ports, inputs, getPortNames(), getInputNames())) {
2952 return emitOpError() <<
"'@" << callee <<
"' has input '"
2953 << cast<StringAttr>(portName).getValue()
2954 <<
"', which is a source port. The inputs are "
2955 "required to be destination ports.";
2957 if (port == goValue)
2958 return emitOpError() <<
"the go or write_en port of '@" << callee
2959 <<
"' cannot appear here.";
2962 return emitOpError() <<
"'@" << callee <<
"' has output '"
2963 << cast<StringAttr>(inputName).getValue()
2964 <<
"', which is a destination port. The inputs are "
2965 "required to be source ports.";
2967 return emitOpError() <<
"'@" << callee <<
"' has '"
2968 << cast<StringAttr>(inputName).getValue()
2969 <<
"', which is not a port or constant. Complex "
2970 "logic should be conducted in the guard.";
2971 if (input == doneValue)
2972 return emitOpError() <<
"the done port of '@" << callee
2973 <<
"' cannot appear here.";
2975 if (port.getDefiningOp() != operation && input.getDefiningOp() != operation)
2976 return emitOpError() <<
"the connection "
2977 << cast<StringAttr>(portName).getValue() <<
" = "
2978 << cast<StringAttr>(inputName).getValue()
2979 <<
" is not defined as an input port of '@" << callee
2989LogicalResult PadLibOp::verify() {
2990 unsigned inBits = getResult(0).getType().getIntOrFloatBitWidth();
2991 unsigned outBits = getResult(1).getType().getIntOrFloatBitWidth();
2992 if (inBits >= outBits)
2993 return emitOpError(
"expected input bits (")
2994 << inBits <<
')' <<
" to be less than output bits (" << outBits
2999LogicalResult SliceLibOp::verify() {
3000 unsigned inBits = getResult(0).getType().getIntOrFloatBitWidth();
3001 unsigned outBits = getResult(1).getType().getIntOrFloatBitWidth();
3002 if (inBits <= outBits)
3003 return emitOpError(
"expected input bits (")
3004 << inBits <<
')' <<
" to be greater than output bits (" << outBits
3009#define ImplBinPipeOpCellInterface(OpType, outName) \
3010 SmallVector<StringRef> OpType::portNames() { \
3011 return {clkPort, resetPort, goPort, "left", "right", outName, donePort}; \
3014 SmallVector<Direction> OpType::portDirections() { \
3015 return {Input, Input, Input, Input, Input, Output, Output}; \
3018 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
3019 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
3022 SmallVector<DictionaryAttr> OpType::portAttributes() { \
3023 MLIRContext *context = getContext(); \
3024 IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1); \
3025 NamedAttrList go, clk, reset, done; \
3026 go.append(goPort, isSet); \
3027 clk.append(clkPort, isSet); \
3028 reset.append(resetPort, isSet); \
3029 done.append(donePort, isSet); \
3031 clk.getDictionary(context), \
3032 reset.getDictionary(context), \
3033 go.getDictionary(context), \
3034 DictionaryAttr::get(context), \
3035 DictionaryAttr::get(context), \
3036 DictionaryAttr::get(context), \
3037 done.getDictionary(context) \
3041 bool OpType::isCombinational() { return false; }
3043#define ImplUnaryOpCellInterface(OpType) \
3044 SmallVector<StringRef> OpType::portNames() { return {"in", "out"}; } \
3045 SmallVector<Direction> OpType::portDirections() { return {Input, Output}; } \
3046 SmallVector<DictionaryAttr> OpType::portAttributes() { \
3047 return {DictionaryAttr::get(getContext()), \
3048 DictionaryAttr::get(getContext())}; \
3050 bool OpType::isCombinational() { return true; } \
3051 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
3052 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
3055#define ImplBinOpCellInterface(OpType) \
3056 SmallVector<StringRef> OpType::portNames() { \
3057 return {"left", "right", "out"}; \
3059 SmallVector<Direction> OpType::portDirections() { \
3060 return {Input, Input, Output}; \
3062 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
3063 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
3065 bool OpType::isCombinational() { return true; } \
3066 SmallVector<DictionaryAttr> OpType::portAttributes() { \
3067 return {DictionaryAttr::get(getContext()), \
3068 DictionaryAttr::get(getContext()), \
3069 DictionaryAttr::get(getContext())}; \
3113#include "circt/Dialect/Calyx/CalyxInterfaces.cpp.inc"
3116#define GET_OP_CLASSES
3117#include "circt/Dialect/Calyx/Calyx.cpp.inc"
assert(baseType &&"element must be base type")
static LogicalResult verifyPrimitiveOpType(PrimitiveOp instance, hw::HWModuleExternOp referencedPrimitive)
Verifies the port information in comparison with the referenced component of an instance.
static ParseResult parseComponentSignature(OpAsmParser &parser, OperationState &result, SmallVectorImpl< OpAsmParser::Argument > &ports, SmallVectorImpl< Type > &portTypes)
Parses the signature of a Calyx component.
static LogicalResult verifyAssignOpValue(AssignOp op, bool isDestination)
Verifies the value of a given assignment operation.
static ParseResult parseParameterList(OpAsmParser &parser, SmallVector< Attribute > ¶meters)
Parse an parameter list if present.
static Op getControlOrWiresFrom(ComponentOp op)
This is a helper function that should only be used to get the WiresOp or ControlOp of a ComponentOp,...
static LogicalResult verifyPrimitivePortDriving(AssignOp assign, GroupInterface group)
Verifies that certain ports of primitives are either driven or read together.
#define ImplBinPipeOpCellInterface(OpType, outName)
static bool portIsUsedInGroup(GroupInterface group, Value port, bool isDriven)
Determines whether the given port is used in the group.
static Value getBlockArgumentWithName(StringRef name, ComponentOp op)
Returns the Block argument with the given name from a ComponentOp.
static ParseResult parsePortDefList(OpAsmParser &parser, OperationState &result, SmallVectorImpl< OpAsmParser::Argument > &ports, SmallVectorImpl< Type > &portTypes, SmallVectorImpl< NamedAttrList > &portAttrs)
Parses the ports of a Calyx component signature, and adds the corresponding port names to attrName.
static std::string valueName(Operation *scopeOp, Value v)
Convenience function for getting the SSA name of v under the scope of operation scopeOp.
static LogicalResult verifyNotComplexSource(Op op)
Verify that the value is not a "complex" value.
static LogicalResult verifyInstanceOpType(InstanceOp instance, ComponentInterface referencedComponent)
Verifies the port information in comparison with the referenced component of an instance.
static LogicalResult collapseControl(OpTy controlOp, PatternRewriter &rewriter)
static bool hasCommonTailPatternPreConditions(IfOpTy op)
Checks preconditions for the common tail pattern.
Direction convertHWDirectionToCalyx(hw::ModulePort::Direction direction)
static SmallVector< PortInfo > getFilteredPorts(ComponentOp op, Pred p)
A helper function to return a filtered subset of a component's ports.
static LogicalResult anyPortsReadByGroup(GroupInterface group, ValueRange ports)
Checks whether any ports are read within the group.
static bool hasControlRegion(Operation *op)
Returns whether the given operation has a control region.
static LogicalResult emptyControl(OpTy controlOp, PatternRewriter &rewriter)
static LogicalResult verifyControlBody(Operation *op)
Verifies the body of a ControlLikeOp.
static void eraseControlWithConditional(OpTy op, PatternRewriter &rewriter)
A helper function to check whether the conditional needs to be erased to maintain a valid state of a ...
static LogicalResult verifyInvokeOpValue(InvokeOp &op, Value &value, bool isDestination)
static void eraseControlWithGroupAndConditional(OpTy op, PatternRewriter &rewriter)
A helper function to check whether the conditional and group (if it exists) needs to be erased to mai...
static ParseResult parseComponentInterface(OpAsmParser &parser, OperationState &result)
static void printComponentInterface(OpAsmPrinter &p, ComponentInterface comp)
static LogicalResult hasRequiredPorts(ComponentOp op)
Determines whether the given ComponentOp has all the required ports.
static SmallVector< T > concat(const SmallVectorImpl< T > &a, const SmallVectorImpl< T > &b)
Returns a new vector containing the concatenation of vectors a and b.
static std::optional< EnableOp > getLastEnableOp(OpTy parent)
Returns the last EnableOp within the child tree of 'parentSeqOp' or parentStaticSeqOp.
static LogicalResult anyPortsDrivenByGroup(GroupInterface group, ValueRange ports)
Checks whether any ports are driven within the group.
static bool isPort(Value value)
Returns whether this value is either (1) a port on a ComponentOp or (2) a port on a cell interface.
#define ImplBinOpCellInterface(OpType)
static llvm::StringMap< EnableOp > getAllEnableOpsInImmediateBody(OpTy parent)
Returns a mapping of {enabled Group name, EnableOp} for all EnableOps within the immediate ParOp's bo...
static LogicalResult portDrivenByGroup(GroupInterface groupOp, Value port)
Checks whether port is driven from within groupOp.
static LogicalResult zeroRepeat(OpTy op, PatternRewriter &rewriter)
static void buildComponentLike(OpBuilder &builder, OperationState &result, StringAttr name, ArrayRef< PortInfo > ports, bool combinational)
static void getCellAsmResultNames(OpAsmSetValueNameFn setNameFn, Operation *op, ArrayRef< StringRef > portNames)
Gives each result of the cell a meaningful name in the form: <instance-name>.
static LogicalResult commonTailPatternWithSeq(IfOpTy ifOp, PatternRewriter &rewriter)
seq { if a with @G { if a with @G { seq { ... calyx.enable @A } seq { ... } else { -> } else { seq { ...
static LogicalResult allPortsDrivenByGroup(GroupInterface group, ValueRange ports)
Checks whether all ports are driven within the group.
static LogicalResult verifyPortDirection(Operation *op, Value value, bool isDestination)
Determines whether the given direction is valid with the given inputs.
static size_t getHwModuleExtGoOrDonePortNumber(hw::HWModuleExternOp &moduleExternOp, bool isGo)
static bool isStaticControl(Operation *op)
Returns whether the given operation is a static control operator.
static void printParameterList(OpAsmPrinter &p, Operation *op, ArrayAttr parameters)
Print a parameter list for a module or instance. Same format as HW dialect.
static DictionaryAttr cleanCalyxPortAttrs(OpBuilder builder, DictionaryAttr dict)
Returns a new DictionaryAttr containing only the calyx dialect attrs in the input DictionaryAttr.
static void printGroupPort(OpAsmPrinter &p, GroupPortType op)
#define ImplUnaryOpCellInterface(OpType)
static ParseResult parseGroupPort(OpAsmParser &parser, OperationState &result)
static LogicalResult verifyComplexLogic(InvokeOp &op, Value &value)
static LogicalResult commonTailPatternWithPar(OpTy controlOp, PatternRewriter &rewriter)
if a with @G { par { par { if a with @G { ... par { ... } calyx.enable @A } else { calyx....
std::map< std::string, WriteChannelPort & > writePorts
static SmallVector< Block *, 8 > intersection(SmallVectorImpl< Block * > &v1, SmallVectorImpl< Block * > &v2)
Calculate intersection of two vectors, returns a new vector.
static ParseResult parseType(Type &result, StringRef name, AsmParser &parser)
Parse a type defined by this dialect.
static InstancePath empty
static ParseResult parsePort(OpAsmParser &p, module_like_impl::PortParse &result)
Parse a single argument with the following syntax:
static Block * getBodyBlock(FModuleLike mod)
Signals that the following operation is combinational.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
IntegerAttr packAttribute(MLIRContext *context, size_t nIns, size_t nOuts)
Returns an IntegerAttr containing the packed representation of the direction counts.
static constexpr std::string_view clkPort
LogicalResult verifyComponent(Operation *op)
A helper function to verify each operation with the Ccomponent trait.
static constexpr std::string_view donePort
LogicalResult verifyControlLikeOp(Operation *op)
A helper function to verify each control-like operation has a valid parent and, if applicable,...
LogicalResult verifyGroupInterface(Operation *op)
A helper function to verify each operation with the Group Interface trait.
LogicalResult verifyCell(Operation *op)
A helper function to verify each operation with the Cell trait.
static constexpr std::string_view resetPort
Direction
The direction of a Component or Cell port.
LogicalResult verifyIf(Operation *op)
A helper function to verify each operation with the If trait.
PortInfo getPortInfo(BlockArgument arg)
Returns port information for the block argument provided.
static constexpr std::string_view goPort
bool isCombinational(Operation *op)
Return true if the specified operation is a combinational logic op.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
This pattern collapses a calyx.seq or calyx.par operation when it contains exactly one calyx....
LogicalResult matchAndRewrite(CtrlOp ctrlOp, PatternRewriter &rewriter) const override
This pattern checks for one of two cases that will lead to IfOp deletion: (1) Then and Else bodies ar...
LogicalResult matchAndRewrite(IfOp ifOp, PatternRewriter &rewriter) const override
This pattern checks for one of two cases that will lead to StaticIfOp deletion: (1) Then and Else bod...
LogicalResult matchAndRewrite(StaticIfOp ifOp, PatternRewriter &rewriter) const override
This holds information about the port for either a Component or Cell.
DictionaryAttr attributes
This holds the name, type, direction of a module's ports.