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"
37 using namespace circt;
46 template <
class T,
class... Ts>
47 struct 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);
77 template <
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);
103 template <
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.");
121 static 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))
296 template <
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();
311 template <
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);
329 template <
typename OpTy>
330 static LogicalResult
emptyControl(OpTy controlOp, PatternRewriter &rewriter) {
331 if (controlOp.getBodyBlock()->empty()) {
332 rewriter.eraseOp(controlOp);
341 template <
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());
368 template <
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());
386 template <
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;
401 p.printAttributeWithoutType(port.attributes);
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();
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));
502 template <
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),
525 auto *body = result.addRegion();
526 if (parser.parseRegion(*body, ports))
530 body->push_back(
new Block());
532 if (parser.parseOptionalAttrDict(result.attributes))
539 template <
typename T>
540 static 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) {
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),
579 result.addAttribute(ComponentOp::getFunctionTypeAttrName(result.name),
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);
617 template <
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);
639 WiresOp calyx::ComponentOp::getWiresOp() {
640 return getControlOrWiresFrom<WiresOp>(*
this);
643 ControlOp calyx::ComponentOp::getControlOp() {
644 return getControlOrWiresFrom<ControlOp>(*
this);
647 Value calyx::ComponentOp::getGoPort() {
651 Value calyx::ComponentOp::getDonePort() {
655 Value calyx::ComponentOp::getClkPort() {
659 Value calyx::ComponentOp::getResetPort() {
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])});
678 template <
typename Pred>
680 SmallVector<PortInfo> ports = op.getPortInfo();
681 llvm::erase_if(ports, p);
685 SmallVector<PortInfo> ComponentOp::getInputPortInfo() {
690 SmallVector<PortInfo> ComponentOp::getOutputPortInfo() {
695 void ComponentOp::print(OpAsmPrinter &p) {
696 printComponentInterface<ComponentOp>(p, *
this);
699 ParseResult 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: "
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() <<
"'.";
771 void ComponentOp::build(OpBuilder &builder, OperationState &result,
772 StringAttr name, ArrayRef<PortInfo> ports) {
776 void 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());
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])});
804 WiresOp calyx::CombComponentOp::getWiresOp() {
806 auto opIt = body->getOps<WiresOp>().begin();
811 template <
typename Pred>
813 SmallVector<PortInfo> ports = op.getPortInfo();
814 llvm::erase_if(ports, p);
818 SmallVector<PortInfo> CombComponentOp::getInputPortInfo() {
823 SmallVector<PortInfo> CombComponentOp::getOutputPortInfo() {
828 void CombComponentOp::print(OpAsmPrinter &p) {
829 printComponentInterface<CombComponentOp>(p, *
this);
832 ParseResult CombComponentOp::parse(OpAsmParser &parser,
833 OperationState &result) {
834 return parseComponentInterface<CombComponentOp>(parser, result);
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();
882 void CombComponentOp::build(OpBuilder &builder, OperationState &result,
883 StringAttr name, ArrayRef<PortInfo> ports) {
887 void 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());
903 SmallVector<InvokeOp, 4> ControlOp::getInvokeOps() {
904 SmallVector<InvokeOp, 4> ret;
905 this->walk([&](InvokeOp invokeOp) { ret.push_back(invokeOp); });
913 void SeqOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
914 MLIRContext *context) {
915 patterns.add(collapseControl<SeqOp>);
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");
934 void StaticSeqOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
935 MLIRContext *context) {
936 patterns.add(collapseControl<StaticSeqOp>);
937 patterns.add(emptyControl<StaticSeqOp>);
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);
961 void ParOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
962 MLIRContext *context) {
963 patterns.add(collapseControl<ParOp>);
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");
996 void StaticParOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
997 MLIRContext *context) {
998 patterns.add(collapseControl<StaticParOp>);
999 patterns.add(emptyControl<StaticParOp>);
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.";
1099 auto assign = dyn_cast<AssignOp>(op);
1100 if (assign ==
nullptr)
1102 Value dst = assign.getDest(), src = assign.getSrc();
1113 GroupGoOp GroupOp::getGoOp() {
1115 size_t nOps = std::distance(goOps.begin(), goOps.end());
1116 return nOps ? *goOps.begin() : GroupGoOp();
1119 GroupDoneOp GroupOp::getDoneOp() {
1121 return cast<GroupDoneOp>(body->getTerminator());
1127 void CycleOp::print(OpAsmPrinter &p) {
1130 auto start = this->getStart();
1131 auto end = this->getEnd();
1132 if (
end.has_value()) {
1133 p <<
"[" << start <<
":" <<
end.value() <<
"]";
1139 ParseResult 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());
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");
1196 uint32_t CycleOp::getGroupLatency() {
1197 auto group = (*this)->getParentOfType<StaticGroupOp>();
1198 return group.getLatency();
1212 std::string AddFOpIEEE754::getCalyxLibraryName() {
return "std_addFN"; }
1214 std::string MulFOpIEEE754::getCalyxLibraryName() {
return "std_mulFN"; }
1224 return llvm::any_of(port.getUses(), [&](
auto &&use) {
1225 auto assignOp = dyn_cast<AssignOp>(use.getOwner());
1226 if (assignOp == nullptr)
1229 Operation *parent = assignOp->getParentOp();
1230 if (isa<WiresOp>(parent))
1239 Value expected = isDriven ? assignOp.getDest() : assignOp.getSrc();
1240 return expected == port && group == parent;
1252 if (
auto cell = dyn_cast<CellInterface>(port.getDefiningOp());
1254 return groupOp.drivesAnyPort(cell.getInputPorts());
1259 LogicalResult GroupOp::drivesPort(Value port) {
1263 LogicalResult CombGroupOp::drivesPort(Value port) {
1267 LogicalResult StaticGroupOp::drivesPort(Value port) {
1274 return success(llvm::all_of(ports, [&](Value port) {
1279 LogicalResult GroupOp::drivesAllPorts(ValueRange ports) {
1283 LogicalResult CombGroupOp::drivesAllPorts(ValueRange ports) {
1287 LogicalResult StaticGroupOp::drivesAllPorts(ValueRange ports) {
1294 return success(llvm::any_of(ports, [&](Value port) {
1299 LogicalResult GroupOp::drivesAnyPort(ValueRange ports) {
1303 LogicalResult CombGroupOp::drivesAnyPort(ValueRange ports) {
1307 LogicalResult StaticGroupOp::drivesAnyPort(ValueRange ports) {
1314 return success(llvm::any_of(ports, [&](Value port) {
1319 LogicalResult GroupOp::readsAnyPort(ValueRange ports) {
1323 LogicalResult CombGroupOp::readsAnyPort(ValueRange ports) {
1327 LogicalResult StaticGroupOp::readsAnyPort(ValueRange ports) {
1334 GroupInterface group) {
1335 Operation *destDefiningOp = assign.getDest().getDefiningOp();
1336 if (destDefiningOp ==
nullptr)
1338 auto destCell = dyn_cast<CellInterface>(destDefiningOp);
1339 if (destCell ==
nullptr)
1342 LogicalResult verifyWrites =
1343 TypeSwitch<Operation *, LogicalResult>(destCell)
1344 .Case<RegisterOp>([&](
auto op) {
1347 return succeeded(group.drivesAnyPort({op.getWriteEn(), op.getIn()}))
1348 ? group.drivesAllPorts({op.getWriteEn(), op.getIn()})
1351 .Case<MemoryOp>([&](
auto op) {
1352 SmallVector<Value> requiredWritePorts;
1355 requiredWritePorts.push_back(op.writeEn());
1356 requiredWritePorts.push_back(op.writeData());
1357 for (Value address : op.addrPorts())
1358 requiredWritePorts.push_back(address);
1363 group.drivesAnyPort({op.writeData(), op.writeEn()}))
1364 ? group.drivesAllPorts(requiredWritePorts)
1367 .Case<AndLibOp, OrLibOp, XorLibOp, AddLibOp, SubLibOp, GtLibOp,
1368 LtLibOp, EqLibOp, NeqLibOp, GeLibOp, LeLibOp, LshLibOp,
1369 RshLibOp, SgtLibOp, SltLibOp, SeqLibOp, SneqLibOp, SgeLibOp,
1370 SleLibOp, SrshLibOp>([&](
auto op) {
1371 Value lhs = op.getLeft(), rhs = op.getRight();
1372 return succeeded(group.drivesAnyPort({lhs, rhs}))
1373 ? group.drivesAllPorts({lhs, rhs})
1376 .Default([&](
auto op) {
return success(); });
1378 if (failed(verifyWrites))
1379 return group->emitOpError()
1380 <<
"with cell: " << destCell->getName() <<
" \""
1381 << destCell.instanceName()
1382 <<
"\" is performing a write and failed to drive all necessary "
1385 Operation *srcDefiningOp = assign.getSrc().getDefiningOp();
1386 if (srcDefiningOp ==
nullptr)
1388 auto srcCell = dyn_cast<CellInterface>(srcDefiningOp);
1389 if (srcCell ==
nullptr)
1392 LogicalResult verifyReads =
1393 TypeSwitch<Operation *, LogicalResult>(srcCell)
1394 .Case<MemoryOp>([&](
auto op) {
1398 return succeeded(group.readsAnyPort({op.readData()}))
1399 ? group.drivesAllPorts(op.addrPorts())
1402 .Default([&](
auto op) {
return success(); });
1404 if (failed(verifyReads))
1405 return group->emitOpError() <<
"with cell: " << srcCell->getName() <<
" \""
1406 << srcCell.instanceName()
1407 <<
"\" is having a read performed upon it, and "
1408 "failed to drive all necessary ports.";
1414 auto group = dyn_cast<GroupInterface>(op);
1415 if (group ==
nullptr)
1418 for (
auto &&groupOp : *group.getBody()) {
1419 auto assign = dyn_cast<AssignOp>(groupOp);
1420 if (assign ==
nullptr)
1436 ArrayRef<StringRef> portNames) {
1437 auto cellInterface = dyn_cast<CellInterface>(op);
1438 assert(cellInterface &&
"must implement the Cell interface");
1440 std::string prefix = cellInterface.instanceName().str() +
".";
1441 for (
size_t i = 0, e = portNames.size(); i != e; ++i)
1442 setNameFn(op->getResult(i), prefix + portNames[i].str());
1453 bool isDestination) {
1454 Operation *definingOp = value.getDefiningOp();
1455 bool isComponentPort = isa<BlockArgument>(value),
1456 isCellInterfacePort = isa_and_nonnull<CellInterface>(definingOp);
1457 assert((isComponentPort || isCellInterfacePort) &&
"Not a port.");
1461 : cast<CellInterface>(definingOp).portInfo(value);
1463 bool isSource = !isDestination;
1466 (isDestination && isComponentPort) || (isSource && isCellInterfacePort)
1473 <<
"has a " << (isComponentPort ?
"component" :
"cell")
1475 << (isDestination ?
"destination" :
"source")
1476 <<
" with the incorrect direction.";
1483 bool isSource = !isDestination;
1484 Value value = isDestination ? op.getDest() : op.getSrc();
1489 if (isDestination && !isa<GroupGoOp, GroupDoneOp>(value.getDefiningOp()))
1490 return op->emitOpError(
1491 "has an invalid destination port. It must be drive-able.");
1499 bool isDestination =
true, isSource =
false;
1508 ParseResult AssignOp::parse(OpAsmParser &parser, OperationState &result) {
1509 OpAsmParser::UnresolvedOperand destination;
1510 if (parser.parseOperand(destination) || parser.parseEqual())
1516 OpAsmParser::UnresolvedOperand guardOrSource;
1517 if (parser.parseOperand(guardOrSource))
1522 OpAsmParser::UnresolvedOperand source;
1523 bool hasGuard = succeeded(parser.parseOptionalQuestion());
1526 if (parser.parseOperand(source))
1531 if (parser.parseColonType(type) ||
1532 parser.resolveOperand(destination, type, result.operands))
1536 Type i1Type = parser.getBuilder().getI1Type();
1539 if (parser.resolveOperand(source, type, result.operands) ||
1540 parser.resolveOperand(guardOrSource, i1Type, result.operands))
1544 if (parser.resolveOperand(guardOrSource, type, result.operands))
1551 void AssignOp::print(OpAsmPrinter &p) {
1552 p <<
" " << getDest() <<
" = ";
1554 Value bguard = getGuard(), source = getSrc();
1557 p << bguard <<
" ? ";
1561 p << source <<
" : " << source.getType();
1570 ComponentInterface InstanceOp::getReferencedComponent() {
1571 auto module = (*this)->getParentOfType<ModuleOp>();
1575 return module.lookupSymbol<ComponentInterface>(getComponentName());
1581 static LogicalResult
1583 ComponentInterface referencedComponent) {
1584 auto module = instance->getParentOfType<ModuleOp>();
1585 StringRef entryPointName =
1586 module->getAttrOfType<StringAttr>(
"calyx.entrypoint");
1587 if (instance.getComponentName() == entryPointName)
1588 return instance.emitOpError()
1589 <<
"cannot reference the entry-point component: '" << entryPointName
1593 SmallVector<PortInfo> componentPorts = referencedComponent.getPortInfo();
1594 size_t numPorts = componentPorts.size();
1596 size_t numResults = instance.getNumResults();
1597 if (numResults != numPorts)
1598 return instance.emitOpError()
1599 <<
"has a wrong number of results; expected: " << numPorts
1600 <<
" but got " << numResults;
1602 for (
size_t i = 0; i != numResults; ++i) {
1603 auto resultType = instance.getResult(i).getType();
1604 auto expectedType = componentPorts[i].type;
1605 if (resultType == expectedType)
1607 return instance.emitOpError()
1608 <<
"result type for " << componentPorts[i].name <<
" must be "
1609 << expectedType <<
", but got " << resultType;
1614 LogicalResult InstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1615 Operation *op = *
this;
1616 auto module = op->getParentOfType<ModuleOp>();
1617 Operation *referencedComponent =
1618 symbolTable.lookupNearestSymbolFrom(module, getComponentNameAttr());
1619 if (referencedComponent ==
nullptr)
1620 return emitError() <<
"referencing component: '" << getComponentName()
1621 <<
"', which does not exist.";
1623 Operation *shadowedComponentName =
1624 symbolTable.lookupNearestSymbolFrom(module, getSymNameAttr());
1625 if (shadowedComponentName !=
nullptr)
1626 return emitError() <<
"instance symbol: '" << instanceName()
1627 <<
"' is already a symbol for another component.";
1630 auto parentComponent = op->getParentOfType<ComponentOp>();
1631 if (parentComponent == referencedComponent)
1632 return emitError() <<
"recursive instantiation of its parent component: '"
1633 << getComponentName() <<
"'";
1635 assert(isa<ComponentInterface>(referencedComponent) &&
1636 "Should be a ComponentInterface.");
1638 cast<ComponentInterface>(referencedComponent));
1646 SmallVector<StringRef> InstanceOp::portNames() {
1647 SmallVector<StringRef> portNames;
1648 for (Attribute name : getReferencedComponent().getPortNames())
1649 portNames.push_back(cast<StringAttr>(name).getValue());
1653 SmallVector<Direction> InstanceOp::portDirections() {
1654 SmallVector<Direction> portDirections;
1656 portDirections.push_back(port.direction);
1657 return portDirections;
1660 SmallVector<DictionaryAttr> InstanceOp::portAttributes() {
1661 SmallVector<DictionaryAttr> portAttributes;
1663 portAttributes.push_back(port.attributes);
1664 return portAttributes;
1668 return isa<CombComponentOp>(getReferencedComponent());
1678 auto module = (*this)->getParentOfType<ModuleOp>();
1688 static LogicalResult
1691 auto module = instance->getParentOfType<ModuleOp>();
1692 StringRef entryPointName =
1693 module->getAttrOfType<StringAttr>(
"calyx.entrypoint");
1694 if (instance.getPrimitiveName() == entryPointName)
1695 return instance.emitOpError()
1696 <<
"cannot reference the entry-point component: '" << entryPointName
1700 auto primitivePorts = referencedPrimitive.getPortList();
1701 size_t numPorts = primitivePorts.size();
1703 size_t numResults = instance.getNumResults();
1704 if (numResults != numPorts)
1705 return instance.emitOpError()
1706 <<
"has a wrong number of results; expected: " << numPorts
1707 <<
" but got " << numResults;
1710 ArrayAttr modParameters = referencedPrimitive.getParameters();
1711 ArrayAttr parameters = instance.getParameters().value_or(ArrayAttr());
1712 size_t numExpected = modParameters.size();
1713 size_t numParams = parameters.size();
1714 if (numParams != numExpected)
1715 return instance.emitOpError()
1716 <<
"has the wrong number of parameters; expected: " << numExpected
1717 <<
" but got " << numParams;
1719 for (
size_t i = 0; i != numExpected; ++i) {
1720 auto param = cast<circt::hw::ParamDeclAttr>(parameters[i]);
1721 auto modParam = cast<circt::hw::ParamDeclAttr>(modParameters[i]);
1723 auto paramName = param.getName();
1724 if (paramName != modParam.getName())
1725 return instance.emitOpError()
1726 <<
"parameter #" << i <<
" should have name " << modParam.getName()
1727 <<
" but has name " << paramName;
1729 if (param.getType() != modParam.getType())
1730 return instance.emitOpError()
1731 <<
"parameter " << paramName <<
" should have type "
1732 << modParam.getType() <<
" but has type " << param.getType();
1736 if (!param.getValue())
1737 return instance.emitOpError(
"parameter ")
1738 << paramName <<
" must have a value";
1741 for (
size_t i = 0; i != numResults; ++i) {
1742 auto resultType = instance.getResult(i).getType();
1743 auto expectedType = primitivePorts[i].type;
1745 instance.getLoc(), instance.getParametersAttr(), expectedType);
1746 if (failed(replacedType))
1748 if (resultType == replacedType)
1750 return instance.emitOpError()
1751 <<
"result type for " << primitivePorts[i].name <<
" must be "
1752 << expectedType <<
", but got " << resultType;
1758 PrimitiveOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1759 Operation *op = *
this;
1760 auto module = op->getParentOfType<ModuleOp>();
1761 Operation *referencedPrimitive =
1762 symbolTable.lookupNearestSymbolFrom(module, getPrimitiveNameAttr());
1763 if (referencedPrimitive ==
nullptr)
1764 return emitError() <<
"referencing primitive: '" << getPrimitiveName()
1765 <<
"', which does not exist.";
1767 Operation *shadowedPrimitiveName =
1768 symbolTable.lookupNearestSymbolFrom(module, getSymNameAttr());
1769 if (shadowedPrimitiveName !=
nullptr)
1770 return emitError() <<
"instance symbol: '" << instanceName()
1771 <<
"' is already a symbol for another primitive.";
1775 if (parentPrimitive == referencedPrimitive)
1776 return emitError() <<
"recursive instantiation of its parent primitive: '"
1777 << getPrimitiveName() <<
"'";
1779 assert(isa<hw::HWModuleExternOp>(referencedPrimitive) &&
1780 "Should be a HardwareModuleExternOp.");
1783 cast<hw::HWModuleExternOp>(referencedPrimitive));
1791 SmallVector<StringRef> PrimitiveOp::portNames() {
1792 SmallVector<StringRef> portNames;
1793 auto ports = getReferencedPrimitive().getPortList();
1794 for (
auto port : ports)
1795 portNames.push_back(port.name.getValue());
1801 switch (direction) {
1807 llvm_unreachable(
"InOut ports not supported by Calyx");
1809 llvm_unreachable(
"Impossible port type");
1812 SmallVector<Direction> PrimitiveOp::portDirections() {
1813 SmallVector<Direction> portDirections;
1814 auto ports = getReferencedPrimitive().getPortList();
1815 for (hw::PortInfo port : ports)
1817 return portDirections;
1826 DictionaryAttr dict) {
1830 llvm::SmallVector<NamedAttribute> attrs;
1831 for (NamedAttribute attr : dict) {
1832 Dialect *dialect = attr.getNameDialect();
1833 if (dialect ==
nullptr || !isa<CalyxDialect>(*dialect))
1835 StringRef name = attr.getName().strref();
1836 StringAttr newName = builder.getStringAttr(std::get<1>(name.split(
".")));
1837 attr.setName(newName);
1838 attrs.push_back(attr);
1840 return builder.getDictionaryAttr(attrs);
1844 SmallVector<DictionaryAttr> PrimitiveOp::portAttributes() {
1845 SmallVector<DictionaryAttr> portAttributes;
1846 OpBuilder builder(getContext());
1848 auto argAttrs = prim.getAllInputAttrs();
1849 auto resAttrs = prim.getAllOutputAttrs();
1850 for (
auto a : argAttrs)
1851 portAttributes.push_back(
1853 for (
auto a : resAttrs)
1854 portAttributes.push_back(
1856 return portAttributes;
1865 SmallVector<Attribute> ¶meters) {
1867 return parser.parseCommaSeparatedList(
1868 OpAsmParser::Delimiter::OptionalLessGreater, [&]() {
1873 if (parser.parseKeywordOrString(&name) || parser.parseColonType(type))
1877 if (succeeded(parser.parseOptionalEqual())) {
1878 if (parser.parseAttribute(value, type))
1882 auto &builder = parser.getBuilder();
1884 builder.getContext(), builder.getStringAttr(name), type, value));
1891 ArrayAttr ¶meters) {
1892 SmallVector<Attribute> parseParameters;
1896 parameters =
ArrayAttr::get(parser.getContext(), parseParameters);
1903 ArrayAttr parameters) {
1904 if (parameters.empty())
1908 llvm::interleaveComma(parameters, p, [&](Attribute param) {
1909 auto paramAttr = cast<hw::ParamDeclAttr>(param);
1910 p << paramAttr.getName().getValue() <<
": " << paramAttr.getType();
1911 if (
auto value = paramAttr.getValue()) {
1913 p.printAttributeWithoutType(value);
1919 //===----------------------------------------------------------------------===//
1921 //===----------------------------------------------------------------------===//
1923 LogicalResult GroupGoOp::verify() { return verifyNotComplexSource(*this); }
1926 void GroupGoOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1927 auto parent = (*this)->getParentOfType<GroupOp>();
1928 StringRef name = parent.getSymName();
1929 std::string resultName = name.str() + ".go";
1930 setNameFn(getResult(), resultName);
1933 void GroupGoOp::print(OpAsmPrinter &p) { printGroupPort(p, *this); }
1935 ParseResult GroupGoOp::parse(OpAsmParser &parser, OperationState &result) {
1936 if (parseGroupPort(parser, result))
1939 result.addTypes(parser.getBuilder().getI1Type());
1943 //===----------------------------------------------------------------------===//
1945 //===----------------------------------------------------------------------===//
1947 LogicalResult GroupDoneOp::verify() {
1948 Operation *srcOp = getSrc().getDefiningOp();
1949 Value optionalGuard = getGuard();
1950 Operation *guardOp = optionalGuard ? optionalGuard.getDefiningOp() : nullptr;
1951 bool noGuard = (guardOp == nullptr);
1953 if (srcOp == nullptr)
1954 // This is a port of the parent component.
1957 if (isa<hw::ConstantOp>(srcOp) && (noGuard || isa<hw::ConstantOp>(guardOp)))
1958 return emitOpError() << "with constant source"
1959 << (noGuard ? "" : " and constant guard")
1960 << ". This should be a combinational group.";
1962 return verifyNotComplexSource(*this);
1965 void GroupDoneOp::print(OpAsmPrinter &p) { printGroupPort(p, *this); }
1967 ParseResult GroupDoneOp::parse(OpAsmParser &parser, OperationState &result) {
1968 return parseGroupPort(parser, result);
1971 //===----------------------------------------------------------------------===//
1973 //===----------------------------------------------------------------------===//
1974 void ConstantOp::getAsmResultNames(
1975 function_ref<void(Value, StringRef)> setNameFn) {
1976 if (isa<FloatAttr>(getValue())) {
1977 setNameFn(getResult(), "cst");
1980 auto intCst = llvm::dyn_cast<IntegerAttr>(getValue());
1981 auto intType = llvm::dyn_cast<IntegerType>(getType());
1983 // Sugar i1 constants with '
true' and 'false'.
1984 if (intType && intType.getWidth() == 1)
1985 return setNameFn(getResult(), intCst.getInt() > 0 ? "true" : "false");
1987 // Otherwise, build a complex name with the value and type.
1988 SmallString<32> specialNameBuffer;
1989 llvm::raw_svector_ostream specialName(specialNameBuffer);
1990 specialName << 'c
' << intCst.getValue();
1992 specialName << '_
' << getType();
1993 setNameFn(getResult(), specialName.str());
1996 LogicalResult ConstantOp::verify() {
1997 auto type = getType();
1998 assert(isa<IntegerType>(type) && "must be an IntegerType");
1999 // The value's bit
width must match the
return type bitwidth.
2000 if (
auto valTyBitWidth = getValue().getType().getIntOrFloatBitWidth();
2001 valTyBitWidth != type.getIntOrFloatBitWidth()) {
2002 return emitOpError() <<
"value type bit width" << valTyBitWidth
2003 <<
" must match return type: "
2004 << type.getIntOrFloatBitWidth();
2007 if (llvm::isa<IntegerType>(type) &&
2008 !llvm::cast<IntegerType>(type).isSignless())
2009 return emitOpError(
"integer return type must be signless");
2011 if (!llvm::isa<IntegerAttr, FloatAttr>(getValue())) {
2012 return emitOpError(
"value must be an integer or float attribute");
2018 OpFoldResult calyx::ConstantOp::fold(FoldAdaptor adaptor) {
2019 return getValueAttr();
2022 void calyx::ConstantOp::build(OpBuilder &builder, OperationState &state,
2023 StringRef symName, Attribute attr, Type type) {
2024 state.addAttribute(SymbolTable::getSymbolAttrName(),
2025 builder.getStringAttr(symName));
2026 state.addAttribute(
"value", attr);
2027 SmallVector<Type> types;
2028 types.push_back(type);
2029 state.addTypes(types);
2032 SmallVector<StringRef> ConstantOp::portNames() {
return {
"out"}; }
2034 SmallVector<Direction> ConstantOp::portDirections() {
return {
Output}; }
2036 SmallVector<DictionaryAttr> ConstantOp::portAttributes() {
2051 SmallVector<StringRef> RegisterOp::portNames() {
2055 SmallVector<Direction> RegisterOp::portDirections() {
2059 SmallVector<DictionaryAttr> RegisterOp::portAttributes() {
2060 MLIRContext *context = getContext();
2062 NamedAttrList writeEn,
clk, reset, done;
2063 writeEn.append(
goPort, isSet);
2069 writeEn.getDictionary(context),
2070 clk.getDictionary(context),
2071 reset.getDictionary(context),
2073 done.getDictionary(context)
2088 SmallVector<StringRef> MemoryOp::portNames() {
2089 SmallVector<StringRef> portNames;
2090 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i) {
2093 portNames.push_back(nameAttr.getValue());
2095 portNames.append({
"write_data",
"write_en",
clkPort,
"read_data",
donePort});
2099 SmallVector<Direction> MemoryOp::portDirections() {
2100 SmallVector<Direction> portDirections;
2101 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2102 portDirections.push_back(
Input);
2104 return portDirections;
2107 SmallVector<DictionaryAttr> MemoryOp::portAttributes() {
2108 SmallVector<DictionaryAttr> portAttributes;
2109 MLIRContext *context = getContext();
2110 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2115 NamedAttrList writeEn,
clk, reset, done;
2116 writeEn.append(
goPort, isSet);
2120 writeEn.getDictionary(context),
2121 clk.getDictionary(context),
2123 done.getDictionary(context)}
2125 return portAttributes;
2128 void MemoryOp::build(OpBuilder &builder, OperationState &state,
2129 StringRef instanceName, int64_t
width,
2130 ArrayRef<int64_t> sizes, ArrayRef<int64_t> addrSizes) {
2131 state.addAttribute(SymbolTable::getSymbolAttrName(),
2132 builder.getStringAttr(instanceName));
2133 state.addAttribute(
"width", builder.getI64IntegerAttr(
width));
2134 state.addAttribute(
"sizes", builder.getI64ArrayAttr(sizes));
2135 state.addAttribute(
"addrSizes", builder.getI64ArrayAttr(addrSizes));
2136 SmallVector<Type> types;
2137 for (int64_t size : addrSizes)
2138 types.push_back(builder.getIntegerType(size));
2139 types.push_back(builder.getIntegerType(
width));
2140 types.push_back(builder.getI1Type());
2141 types.push_back(builder.getI1Type());
2142 types.push_back(builder.getIntegerType(
width));
2143 types.push_back(builder.getI1Type());
2144 state.addTypes(types);
2148 ArrayRef<Attribute> opSizes = getSizes().getValue();
2149 ArrayRef<Attribute> opAddrSizes = getAddrSizes().getValue();
2150 size_t numDims = getSizes().size();
2151 size_t numAddrs = getAddrSizes().size();
2152 if (numDims != numAddrs)
2153 return emitOpError(
"mismatched number of dimensions (")
2154 << numDims <<
") and address sizes (" << numAddrs <<
")";
2156 size_t numExtraPorts = 5;
2157 if (getNumResults() != numAddrs + numExtraPorts)
2158 return emitOpError(
"incorrect number of address ports, expected ")
2161 for (
size_t i = 0; i < numDims; ++i) {
2162 int64_t size = cast<IntegerAttr>(opSizes[i]).getInt();
2163 int64_t addrSize = cast<IntegerAttr>(opAddrSizes[i]).getInt();
2164 if (llvm::Log2_64_Ceil(size) > addrSize)
2165 return emitOpError(
"address size (")
2166 << addrSize <<
") for dimension " << i
2167 <<
" can't address the entire range (" << size <<
")";
2182 SmallVector<StringRef> SeqMemoryOp::portNames() {
2183 SmallVector<StringRef> portNames;
2184 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i) {
2187 portNames.push_back(nameAttr.getValue());
2189 portNames.append({
clkPort,
"reset",
"content_en",
"write_en",
"write_data",
2190 "read_data",
"done"});
2194 SmallVector<Direction> SeqMemoryOp::portDirections() {
2195 SmallVector<Direction> portDirections;
2196 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2197 portDirections.push_back(
Input);
2199 return portDirections;
2202 SmallVector<DictionaryAttr> SeqMemoryOp::portAttributes() {
2203 SmallVector<DictionaryAttr> portAttributes;
2204 MLIRContext *context = getContext();
2205 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2208 OpBuilder builder(context);
2212 NamedAttrList done,
clk, reset, contentEn;
2216 contentEn.append(
goPort, isTwo);
2217 portAttributes.append({
clk.getDictionary(context),
2218 reset.getDictionary(context),
2219 contentEn.getDictionary(context),
2223 done.getDictionary(context)}
2225 return portAttributes;
2228 void SeqMemoryOp::build(OpBuilder &builder, OperationState &state,
2229 StringRef instanceName, int64_t
width,
2230 ArrayRef<int64_t> sizes, ArrayRef<int64_t> addrSizes) {
2231 state.addAttribute(SymbolTable::getSymbolAttrName(),
2232 builder.getStringAttr(instanceName));
2233 state.addAttribute(
"width", builder.getI64IntegerAttr(
width));
2234 state.addAttribute(
"sizes", builder.getI64ArrayAttr(sizes));
2235 state.addAttribute(
"addrSizes", builder.getI64ArrayAttr(addrSizes));
2236 SmallVector<Type> types;
2237 for (int64_t size : addrSizes)
2238 types.push_back(builder.getIntegerType(size));
2239 types.push_back(builder.getI1Type());
2240 types.push_back(builder.getI1Type());
2241 types.push_back(builder.getI1Type());
2242 types.push_back(builder.getI1Type());
2243 types.push_back(builder.getIntegerType(
width));
2244 types.push_back(builder.getIntegerType(
width));
2245 types.push_back(builder.getI1Type());
2246 state.addTypes(types);
2250 ArrayRef<Attribute> opSizes = getSizes().getValue();
2251 ArrayRef<Attribute> opAddrSizes = getAddrSizes().getValue();
2252 size_t numDims = getSizes().size();
2253 size_t numAddrs = getAddrSizes().size();
2254 if (numDims != numAddrs)
2255 return emitOpError(
"mismatched number of dimensions (")
2256 << numDims <<
") and address sizes (" << numAddrs <<
")";
2258 size_t numExtraPorts =
2260 if (getNumResults() != numAddrs + numExtraPorts)
2261 return emitOpError(
"incorrect number of address ports, expected ")
2264 for (
size_t i = 0; i < numDims; ++i) {
2265 int64_t size = cast<IntegerAttr>(opSizes[i]).getInt();
2266 int64_t addrSize = cast<IntegerAttr>(opAddrSizes[i]).getInt();
2267 if (llvm::Log2_64_Ceil(size) > addrSize)
2268 return emitOpError(
"address size (")
2269 << addrSize <<
") for dimension " << i
2270 <<
" can't address the entire range (" << size <<
")";
2280 auto component = (*this)->getParentOfType<ComponentOp>();
2281 auto wiresOp = component.getWiresOp();
2282 StringRef name = getGroupName();
2284 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(name);
2286 return emitOpError() <<
"with group '" << name
2287 <<
"', which does not exist.";
2289 if (isa<CombGroupOp>(groupOp))
2290 return emitOpError() <<
"with group '" << name
2291 <<
"', which is a combinational group.";
2301 std::optional<StringRef> optGroupName = getGroupName();
2302 if (!optGroupName) {
2306 auto component = (*this)->getParentOfType<ComponentOp>();
2307 WiresOp wiresOp = component.getWiresOp();
2308 StringRef groupName = *optGroupName;
2309 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(groupName);
2311 return emitOpError() <<
"with group '" << groupName
2312 <<
"', which does not exist.";
2314 if (isa<GroupOp>(groupOp))
2315 return emitOpError() <<
"with group '" << groupName
2316 <<
"', which is not a combinational group.";
2318 if (failed(groupOp.drivesPort(getCond())))
2319 return emitError() <<
"with conditional op: '"
2321 <<
"' expected to be driven from group: '" << groupName
2322 <<
"' but no driver was found.";
2330 template <
typename OpTy>
2332 static_assert(IsAny<OpTy, SeqOp, StaticSeqOp>(),
2333 "Should be a StaticSeqOp or SeqOp.");
2334 if (parent.getBodyBlock()->empty())
2335 return std::nullopt;
2336 auto &lastOp = parent.getBodyBlock()->back();
2337 if (
auto enableOp = dyn_cast<EnableOp>(lastOp))
2339 if (
auto seqOp = dyn_cast<SeqOp>(lastOp))
2341 if (
auto staticSeqOp = dyn_cast<StaticSeqOp>(lastOp))
2344 return std::nullopt;
2349 template <
typename OpTy>
2351 static_assert(IsAny<OpTy, ParOp, StaticParOp>(),
2352 "Should be a StaticParOp or ParOp.");
2354 llvm::StringMap<EnableOp> enables;
2355 Block *body = parent.getBodyBlock();
2356 for (EnableOp op : body->getOps<EnableOp>())
2357 enables.insert(std::pair(op.getGroupName(), op));
2369 template <
typename IfOpTy,
typename TailOpTy>
2371 static_assert(IsAny<TailOpTy, SeqOp, ParOp, StaticSeqOp, StaticParOp>(),
2372 "Should be a SeqOp, ParOp, StaticSeqOp, or StaticParOp.");
2373 static_assert(IsAny<IfOpTy, IfOp, StaticIfOp>(),
2374 "Should be a IfOp or StaticIfOp.");
2376 if (!op.thenBodyExists() || !op.elseBodyExists())
2378 if (op.getThenBody()->empty() || op.getElseBody()->empty())
2381 Block *thenBody = op.getThenBody(), *elseBody = op.getElseBody();
2382 return isa<TailOpTy>(thenBody->front()) && isa<TailOpTy>(elseBody->front());
2393 template <
typename IfOpTy,
typename SeqOpTy>
2395 PatternRewriter &rewriter) {
2396 static_assert(IsAny<IfOpTy, IfOp, StaticIfOp>(),
2397 "Should be an IfOp or StaticIfOp.");
2398 static_assert(IsAny<SeqOpTy, SeqOp, StaticSeqOp>(),
2399 "Branches should be checking for an SeqOp or StaticSeqOp");
2400 if (!hasCommonTailPatternPreConditions<IfOpTy, SeqOpTy>(ifOp))
2402 auto thenControl = cast<SeqOpTy>(ifOp.getThenBody()->front()),
2403 elseControl = cast<SeqOpTy>(ifOp.getElseBody()->front());
2405 std::optional<EnableOp> lastThenEnableOp =
getLastEnableOp(thenControl),
2408 if (!lastThenEnableOp || !lastElseEnableOp)
2410 if (lastThenEnableOp->getGroupName() != lastElseEnableOp->getGroupName())
2416 rewriter.setInsertionPointAfter(ifOp);
2417 SeqOpTy seqOp = rewriter.create<SeqOpTy>(ifOp.getLoc());
2418 Block *body = seqOp.getBodyBlock();
2420 body->push_back(ifOp);
2421 rewriter.setInsertionPointToEnd(body);
2422 rewriter.create<EnableOp>(seqOp.getLoc(), lastThenEnableOp->getGroupName());
2425 rewriter.eraseOp(*lastThenEnableOp);
2426 rewriter.eraseOp(*lastElseEnableOp);
2443 template <
typename OpTy,
typename ParOpTy>
2445 PatternRewriter &rewriter) {
2446 static_assert(IsAny<OpTy, IfOp, StaticIfOp>(),
2447 "Should be an IfOp or StaticIfOp.");
2448 static_assert(IsAny<ParOpTy, ParOp, StaticParOp>(),
2449 "Branches should be checking for an ParOp or StaticParOp");
2450 if (!hasCommonTailPatternPreConditions<OpTy, ParOpTy>(controlOp))
2452 auto thenControl = cast<ParOpTy>(controlOp.getThenBody()->front()),
2453 elseControl = cast<ParOpTy>(controlOp.getElseBody()->front());
2458 SmallVector<StringRef> groupNames;
2459 for (
auto aIndex = a.begin(); aIndex != a.end(); ++aIndex) {
2460 StringRef groupName = aIndex->getKey();
2461 auto bIndex = b.find(groupName);
2462 if (bIndex == b.end())
2465 groupNames.push_back(groupName);
2467 rewriter.eraseOp(aIndex->getValue());
2468 rewriter.eraseOp(bIndex->getValue());
2474 rewriter.setInsertionPointAfter(controlOp);
2476 ParOpTy parOp = rewriter.create<ParOpTy>(controlOp.getLoc());
2477 Block *body = parOp.getBodyBlock();
2478 controlOp->remove();
2479 body->push_back(controlOp);
2482 rewriter.setInsertionPointToEnd(body);
2483 for (StringRef groupName : groupNames)
2484 rewriter.create<EnableOp>(parOp.getLoc(), groupName);
2495 PatternRewriter &rewriter)
const override {
2496 if (!ifOp.getThenBody()->empty())
2498 if (ifOp.elseBodyExists() && !ifOp.getElseBody()->empty())
2507 void IfOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
2508 MLIRContext *context) {
2510 patterns.add(commonTailPatternWithPar<IfOp, ParOp>);
2511 patterns.add(commonTailPatternWithSeq<IfOp, SeqOp>);
2518 if (elseBodyExists()) {
2519 auto *elseBod = getElseBody();
2520 auto &elseOps = elseBod->getOperations();
2522 for (Operation &op : elseOps) {
2524 return op.emitOpError(
2525 "static if's else branch has non static control within it");
2530 auto *thenBod = getThenBody();
2531 auto &thenOps = thenBod->getOperations();
2532 for (Operation &op : thenOps) {
2535 return op.emitOpError(
2536 "static if's then branch has non static control within it");
2549 PatternRewriter &rewriter)
const override {
2550 if (!ifOp.getThenBody()->empty())
2552 if (ifOp.elseBodyExists() && !ifOp.getElseBody()->empty())
2561 void StaticIfOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
2562 MLIRContext *context) {
2564 patterns.add(commonTailPatternWithPar<StaticIfOp, StaticParOp>);
2565 patterns.add(commonTailPatternWithSeq<StaticIfOp, StaticSeqOp>);
2572 auto component = (*this)->getParentOfType<ComponentOp>();
2573 auto wiresOp = component.getWiresOp();
2575 std::optional<StringRef> optGroupName = getGroupName();
2576 if (!optGroupName) {
2580 StringRef groupName = *optGroupName;
2581 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(groupName);
2583 return emitOpError() <<
"with group '" << groupName
2584 <<
"', which does not exist.";
2586 if (isa<GroupOp>(groupOp))
2587 return emitOpError() <<
"with group '" << groupName
2588 <<
"', which is not a combinational group.";
2590 if (failed(groupOp.drivesPort(getCond())))
2591 return emitError() <<
"conditional op: '" <<
valueName(component, getCond())
2592 <<
"' expected to be driven from group: '" << groupName
2593 <<
"' but no driver was found.";
2599 PatternRewriter &rewriter) {
2600 if (whileOp.getBodyBlock()->empty()) {
2612 for (
auto &&bodyOp : (*this).getRegion().front()) {
2615 return bodyOp.emitOpError(
2616 "static repeat has non static control within it");
2623 template <
typename OpTy>
2624 static LogicalResult
zeroRepeat(OpTy op, PatternRewriter &rewriter) {
2625 static_assert(IsAny<OpTy, RepeatOp, StaticRepeatOp>(),
2626 "Should be a RepeatOp or StaticPRepeatOp");
2627 if (op.getCount() == 0) {
2628 Block *controlBody = op.getBodyBlock();
2629 for (
auto &op : make_early_inc_range(*controlBody))
2632 rewriter.eraseOp(op);
2639 void StaticRepeatOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
2640 MLIRContext *context) {
2641 patterns.add(emptyControl<StaticRepeatOp>);
2642 patterns.add(zeroRepeat<StaticRepeatOp>);
2648 void RepeatOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
2649 MLIRContext *context) {
2650 patterns.add(emptyControl<RepeatOp>);
2651 patterns.add(zeroRepeat<RepeatOp>);
2661 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &ports,
2662 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &inputs,
2663 SmallVectorImpl<Attribute> &portNames,
2664 SmallVectorImpl<Attribute> &inputNames,
2665 SmallVectorImpl<Type> &types) {
2666 OpAsmParser::UnresolvedOperand port;
2667 OpAsmParser::UnresolvedOperand input;
2669 auto parseParameter = [&]() -> ParseResult {
2670 if (parser.parseOperand(port) || parser.parseEqual() ||
2671 parser.parseOperand(input))
2673 ports.push_back(port);
2675 inputs.push_back(input);
2676 inputNames.push_back(
StringAttr::get(parser.getContext(), input.name));
2679 if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
2682 if (parser.parseArrow())
2685 if (parser.parseType(type))
2687 types.push_back(type);
2690 return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
2694 ParseResult InvokeOp::parse(OpAsmParser &parser, OperationState &result) {
2695 StringAttr componentName;
2696 SmallVector<OpAsmParser::UnresolvedOperand, 4> ports;
2697 SmallVector<OpAsmParser::UnresolvedOperand, 4> inputs;
2698 SmallVector<Attribute> portNames;
2699 SmallVector<Attribute> inputNames;
2700 SmallVector<Type, 4> types;
2701 if (parser.parseSymbolName(componentName))
2704 SMLoc loc = parser.getCurrentLocation();
2706 SmallVector<Attribute, 4> refCells;
2707 if (succeeded(parser.parseOptionalLSquare())) {
2708 if (parser.parseCommaSeparatedList([&]() -> ParseResult {
2709 std::string refCellName;
2710 std::string externalMem;
2711 NamedAttrList refCellAttr;
2712 if (parser.parseKeywordOrString(&refCellName) ||
2713 parser.parseEqual() || parser.parseKeywordOrString(&externalMem))
2715 auto externalMemAttr =
2716 SymbolRefAttr::get(parser.getContext(), externalMem);
2717 refCellAttr.append(StringAttr::get(parser.getContext(), refCellName),
2720 DictionaryAttr::get(parser.getContext(), refCellAttr));
2723 parser.parseRSquare())
2726 result.addAttribute(
"refCellsMap",
2729 result.addAttribute(
"callee", callee);
2733 if (parser.resolveOperands(ports, types, loc, result.operands))
2735 if (parser.resolveOperands(inputs, types, loc, result.operands))
2737 result.addAttribute(
"portNames",
2739 result.addAttribute(
"inputNames",
2744 void InvokeOp::print(OpAsmPrinter &p) {
2745 p <<
" @" << getCallee() <<
"[";
2746 auto refCellNamesMap = getRefCellsMap();
2747 llvm::interleaveComma(refCellNamesMap, p, [&](Attribute attr) {
2748 auto dictAttr = cast<DictionaryAttr>(attr);
2749 llvm::interleaveComma(dictAttr, p, [&](NamedAttribute namedAttr) {
2750 auto refCellName = namedAttr.getName().str();
2752 cast<FlatSymbolRefAttr>(namedAttr.getValue()).getValue();
2753 p << refCellName <<
" = " << externalMem;
2758 auto ports = getPorts();
2759 auto inputs = getInputs();
2760 llvm::interleaveComma(llvm::zip(ports, inputs), p, [&](
auto arg) {
2761 p << std::get<0>(arg) <<
" = " << std::get<1>(arg);
2764 llvm::interleaveComma(ports, p, [&](
auto port) { p << port.getType(); });
2771 bool isDestination) {
2780 Operation *operation = value.getDefiningOp();
2781 if (operation ==
nullptr)
2783 if (
auto *dialect = operation->getDialect(); isa<comb::CombDialect>(dialect))
2789 Value InvokeOp::getInstGoValue() {
2790 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2791 Operation *operation = componentOp.lookupSymbol(getCallee());
2792 Value ret =
nullptr;
2793 llvm::TypeSwitch<Operation *>(operation)
2794 .Case<RegisterOp>([&](
auto op) { ret = operation->getResult(1); })
2795 .Case<MemoryOp, DivSPipeLibOp, DivUPipeLibOp, MultPipeLibOp,
2796 RemSPipeLibOp, RemUPipeLibOp>(
2797 [&](
auto op) { ret = operation->getResult(2); })
2798 .Case<InstanceOp>([&](
auto op) {
2799 auto portInfo = op.getReferencedComponent().getPortInfo();
2800 for (
auto [portInfo, res] :
2801 llvm::zip(portInfo, operation->getResults())) {
2802 if (portInfo.hasAttribute(
goPort))
2806 .Case<PrimitiveOp>([&](
auto op) {
2807 auto moduleExternOp = op.getReferencedPrimitive();
2808 auto argAttrs = moduleExternOp.getAllInputAttrs();
2809 for (
auto [attr, res] : llvm::zip(argAttrs, op.getResults())) {
2810 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2811 if (!dictAttr.empty()) {
2812 if (dictAttr.begin()->getName().getValue() ==
"calyx.go")
2822 Value InvokeOp::getInstDoneValue() {
2823 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2824 Operation *operation = componentOp.lookupSymbol(getCallee());
2825 Value ret =
nullptr;
2826 llvm::TypeSwitch<Operation *>(operation)
2827 .Case<RegisterOp, MemoryOp, DivSPipeLibOp, DivUPipeLibOp, MultPipeLibOp,
2828 RemSPipeLibOp, RemUPipeLibOp>([&](
auto op) {
2829 size_t doneIdx = operation->getResults().size() - 1;
2830 ret = operation->getResult(doneIdx);
2832 .Case<InstanceOp>([&](
auto op) {
2833 InstanceOp instanceOp = cast<InstanceOp>(operation);
2834 auto portInfo = instanceOp.getReferencedComponent().getPortInfo();
2835 for (
auto [portInfo, res] :
2836 llvm::zip(portInfo, operation->getResults())) {
2837 if (portInfo.hasAttribute(
donePort))
2841 .Case<PrimitiveOp>([&](
auto op) {
2842 PrimitiveOp primOp = cast<PrimitiveOp>(operation);
2843 auto moduleExternOp = primOp.getReferencedPrimitive();
2844 auto resAttrs = moduleExternOp.getAllOutputAttrs();
2845 for (
auto [attr, res] : llvm::zip(resAttrs, primOp.getResults())) {
2846 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2847 if (!dictAttr.empty()) {
2848 if (dictAttr.begin()->getName().getValue() ==
"calyx.done")
2863 std::string str = isGo ?
"calyx.go" :
"calyx.done";
2864 for (Attribute attr : moduleExternOp.getAllInputAttrs()) {
2865 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2866 ret = llvm::count_if(dictAttr, [&](NamedAttribute iter) {
2867 return iter.getName().getValue() == str;
2875 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2876 StringRef callee = getCallee();
2877 Operation *operation = componentOp.lookupSymbol(callee);
2880 return emitOpError() <<
"with instance '@" << callee
2881 <<
"', which does not exist.";
2883 if (getInputs().
empty() && getRefCellsMap().
empty()) {
2884 return emitOpError() <<
"'@" << callee
2885 <<
"' has zero input and output port connections and "
2886 "has no passing-by-reference cells; "
2887 "expected at least one.";
2889 size_t goPortNum = 0, donePortNum = 0;
2892 llvm::TypeSwitch<Operation *>(operation)
2893 .Case<RegisterOp, DivSPipeLibOp, DivUPipeLibOp, MemoryOp, MultPipeLibOp,
2894 RemSPipeLibOp, RemUPipeLibOp>(
2895 [&](
auto op) { goPortNum = 1, donePortNum = 1; })
2896 .Case<InstanceOp>([&](
auto op) {
2897 auto portInfo = op.getReferencedComponent().getPortInfo();
2899 if (info.hasAttribute(
goPort))
2905 .Case<PrimitiveOp>([&](
auto op) {
2906 auto moduleExternOp = op.getReferencedPrimitive();
2912 if (goPortNum != 1 && donePortNum != 1)
2913 return emitOpError()
2914 <<
"'@" << callee <<
"'"
2915 <<
" is a combinational component and cannot be invoked, which must "
2916 "have single go port and single done port.";
2918 auto ports = getPorts();
2919 auto inputs = getInputs();
2921 Value goValue = getInstGoValue();
2922 Value doneValue = getInstDoneValue();
2923 for (
auto [port, input, portName, inputName] :
2924 llvm::zip(ports, inputs, getPortNames(), getInputNames())) {
2929 return emitOpError() <<
"'@" << callee <<
"' has input '"
2930 << cast<StringAttr>(portName).getValue()
2931 <<
"', which is a source port. The inputs are "
2932 "required to be destination ports.";
2934 if (port == goValue)
2935 return emitOpError() <<
"the go or write_en port of '@" << callee
2936 <<
"' cannot appear here.";
2939 return emitOpError() <<
"'@" << callee <<
"' has output '"
2940 << cast<StringAttr>(inputName).getValue()
2941 <<
"', which is a destination port. The inputs are "
2942 "required to be source ports.";
2944 return emitOpError() <<
"'@" << callee <<
"' has '"
2945 << cast<StringAttr>(inputName).getValue()
2946 <<
"', which is not a port or constant. Complex "
2947 "logic should be conducted in the guard.";
2948 if (input == doneValue)
2949 return emitOpError() <<
"the done port of '@" << callee
2950 <<
"' cannot appear here.";
2952 if (port.getDefiningOp() != operation && input.getDefiningOp() != operation)
2953 return emitOpError() <<
"the connection "
2954 << cast<StringAttr>(portName).getValue() <<
" = "
2955 << cast<StringAttr>(inputName).getValue()
2956 <<
" is not defined as an input port of '@" << callee
2967 unsigned inBits = getResult(0).getType().getIntOrFloatBitWidth();
2968 unsigned outBits = getResult(1).getType().getIntOrFloatBitWidth();
2969 if (inBits >= outBits)
2970 return emitOpError(
"expected input bits (")
2971 << inBits <<
')' <<
" to be less than output bits (" << outBits
2977 unsigned inBits = getResult(0).getType().getIntOrFloatBitWidth();
2978 unsigned outBits = getResult(1).getType().getIntOrFloatBitWidth();
2979 if (inBits <= outBits)
2980 return emitOpError(
"expected input bits (")
2981 << inBits <<
')' <<
" to be greater than output bits (" << outBits
2986 #define ImplBinPipeOpCellInterface(OpType, outName) \
2987 SmallVector<StringRef> OpType::portNames() { \
2988 return {clkPort, resetPort, goPort, "left", "right", outName, donePort}; \
2991 SmallVector<Direction> OpType::portDirections() { \
2992 return {Input, Input, Input, Input, Input, Output, Output}; \
2995 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
2996 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
2999 SmallVector<DictionaryAttr> OpType::portAttributes() { \
3000 MLIRContext *context = getContext(); \
3001 IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1); \
3002 NamedAttrList go, clk, reset, done; \
3003 go.append(goPort, isSet); \
3004 clk.append(clkPort, isSet); \
3005 reset.append(resetPort, isSet); \
3006 done.append(donePort, isSet); \
3008 clk.getDictionary(context),
\
3009 reset.getDictionary(context), \
3010 go.getDictionary(context), \
3011 DictionaryAttr::get(context), \
3012 DictionaryAttr::get(context), \
3013 DictionaryAttr::get(context), \
3014 done.getDictionary(context) \
3018 bool OpType::isCombinational() { return false; }
3020 #define ImplUnaryOpCellInterface(OpType) \
3021 SmallVector<StringRef> OpType::portNames() { return {"in", "out"}; } \
3022 SmallVector<Direction> OpType::portDirections() { return {Input, Output}; } \
3023 SmallVector<DictionaryAttr> OpType::portAttributes() { \
3024 return {DictionaryAttr::get(getContext()), \
3025 DictionaryAttr::get(getContext())}; \
3027 bool OpType::isCombinational() { return true; } \
3028 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
3029 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
3032 #define ImplBinOpCellInterface(OpType) \
3033 SmallVector<StringRef> OpType::portNames() { \
3034 return {"left", "right", "out"}; \
3036 SmallVector<Direction> OpType::portDirections() { \
3037 return {Input, Input, Output}; \
3039 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
3040 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
3042 bool OpType::isCombinational() { return true; } \
3043 SmallVector<DictionaryAttr> OpType::portAttributes() { \
3044 return {DictionaryAttr::get(getContext()), \
3045 DictionaryAttr::get(getContext()), \
3046 DictionaryAttr::get(getContext())}; \
3090 #include "circt/Dialect/Calyx/CalyxInterfaces.cpp.inc"
3093 #define GET_OP_CLASSES
3094 #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 SmallVector< PortInfo > getFilteredPorts(ComponentOp op, Pred p)
A helper function to return a filtered subset of a component's ports.
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 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 std::optional< EnableOp > getLastEnableOp(OpTy parent)
Returns the last EnableOp within the child tree of 'parentSeqOp' or parentStaticSeqOp.
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 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 SmallVector< T > concat(const SmallVectorImpl< T > &a, const SmallVectorImpl< T > &b)
Returns a new vector containing the concatenation of vectors a and b.
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 { ...
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 LogicalResult isCombinational(Value value, GroupInterface group)
Verifies the defining operation of a value is combinational.
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 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 commonTailPatternWithPar(OpTy controlOp, PatternRewriter &rewriter)
if a with @G { par { par { if a with @G { ...
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.
static LogicalResult canonicalize(Op op, PatternRewriter &rewriter)
static LogicalResult verify(Value clock, bool eventExists, mlir::Location loc)
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
void getAsmResultNames(OpAsmSetValueNameFn setNameFn, StringRef instanceName, ArrayAttr resultNames, ValueRange results)
Suggest a name for each result value based on the saved result names attribute.
enum PEO uint32_t mlir::FailureOr< mlir::Type > evaluateParametricType(mlir::Location loc, mlir::ArrayAttr parameters, mlir::Type type, bool emitErrors=true)
Returns a resolved version of 'type' wherein any parameter reference has been evaluated based on the ...
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