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();
1216 std::string AddFOpIEEE754::getCalyxLibraryName() {
return "std_addFN"; }
1218 std::string MulFOpIEEE754::getCalyxLibraryName() {
return "std_mulFN"; }
1220 std::string CompareFOpIEEE754::getCalyxLibraryName() {
return "std_compareFN"; }
1230 return llvm::any_of(port.getUses(), [&](
auto &&use) {
1231 auto assignOp = dyn_cast<AssignOp>(use.getOwner());
1232 if (assignOp == nullptr)
1235 Operation *parent = assignOp->getParentOp();
1236 if (isa<WiresOp>(parent))
1245 Value expected = isDriven ? assignOp.getDest() : assignOp.getSrc();
1246 return expected == port && group == parent;
1258 if (
auto cell = dyn_cast<CellInterface>(port.getDefiningOp());
1260 return groupOp.drivesAnyPort(cell.getInputPorts());
1265 LogicalResult GroupOp::drivesPort(Value port) {
1269 LogicalResult CombGroupOp::drivesPort(Value port) {
1273 LogicalResult StaticGroupOp::drivesPort(Value port) {
1280 return success(llvm::all_of(ports, [&](Value port) {
1285 LogicalResult GroupOp::drivesAllPorts(ValueRange ports) {
1289 LogicalResult CombGroupOp::drivesAllPorts(ValueRange ports) {
1293 LogicalResult StaticGroupOp::drivesAllPorts(ValueRange ports) {
1300 return success(llvm::any_of(ports, [&](Value port) {
1305 LogicalResult GroupOp::drivesAnyPort(ValueRange ports) {
1309 LogicalResult CombGroupOp::drivesAnyPort(ValueRange ports) {
1313 LogicalResult StaticGroupOp::drivesAnyPort(ValueRange ports) {
1320 return success(llvm::any_of(ports, [&](Value port) {
1325 LogicalResult GroupOp::readsAnyPort(ValueRange ports) {
1329 LogicalResult CombGroupOp::readsAnyPort(ValueRange ports) {
1333 LogicalResult StaticGroupOp::readsAnyPort(ValueRange ports) {
1340 GroupInterface group) {
1341 Operation *destDefiningOp = assign.getDest().getDefiningOp();
1342 if (destDefiningOp ==
nullptr)
1344 auto destCell = dyn_cast<CellInterface>(destDefiningOp);
1345 if (destCell ==
nullptr)
1348 LogicalResult verifyWrites =
1349 TypeSwitch<Operation *, LogicalResult>(destCell)
1350 .Case<RegisterOp>([&](
auto op) {
1353 return succeeded(group.drivesAnyPort({op.getWriteEn(), op.getIn()}))
1354 ? group.drivesAllPorts({op.getWriteEn(), op.getIn()})
1357 .Case<MemoryOp>([&](
auto op) {
1358 SmallVector<Value> requiredWritePorts;
1361 requiredWritePorts.push_back(op.writeEn());
1362 requiredWritePorts.push_back(op.writeData());
1363 for (Value address : op.addrPorts())
1364 requiredWritePorts.push_back(address);
1369 group.drivesAnyPort({op.writeData(), op.writeEn()}))
1370 ? group.drivesAllPorts(requiredWritePorts)
1373 .Case<AndLibOp, OrLibOp, XorLibOp, AddLibOp, SubLibOp, GtLibOp,
1374 LtLibOp, EqLibOp, NeqLibOp, GeLibOp, LeLibOp, LshLibOp,
1375 RshLibOp, SgtLibOp, SltLibOp, SeqLibOp, SneqLibOp, SgeLibOp,
1376 SleLibOp, SrshLibOp>([&](
auto op) {
1377 Value lhs = op.getLeft(), rhs = op.getRight();
1378 return succeeded(group.drivesAnyPort({lhs, rhs}))
1379 ? group.drivesAllPorts({lhs, rhs})
1382 .Default([&](
auto op) {
return success(); });
1384 if (failed(verifyWrites))
1385 return group->emitOpError()
1386 <<
"with cell: " << destCell->getName() <<
" \""
1387 << destCell.instanceName()
1388 <<
"\" is performing a write and failed to drive all necessary "
1391 Operation *srcDefiningOp = assign.getSrc().getDefiningOp();
1392 if (srcDefiningOp ==
nullptr)
1394 auto srcCell = dyn_cast<CellInterface>(srcDefiningOp);
1395 if (srcCell ==
nullptr)
1398 LogicalResult verifyReads =
1399 TypeSwitch<Operation *, LogicalResult>(srcCell)
1400 .Case<MemoryOp>([&](
auto op) {
1404 return succeeded(group.readsAnyPort({op.readData()}))
1405 ? group.drivesAllPorts(op.addrPorts())
1408 .Default([&](
auto op) {
return success(); });
1410 if (failed(verifyReads))
1411 return group->emitOpError() <<
"with cell: " << srcCell->getName() <<
" \""
1412 << srcCell.instanceName()
1413 <<
"\" is having a read performed upon it, and "
1414 "failed to drive all necessary ports.";
1420 auto group = dyn_cast<GroupInterface>(op);
1421 if (group ==
nullptr)
1424 for (
auto &&groupOp : *group.getBody()) {
1425 auto assign = dyn_cast<AssignOp>(groupOp);
1426 if (assign ==
nullptr)
1442 ArrayRef<StringRef> portNames) {
1443 auto cellInterface = dyn_cast<CellInterface>(op);
1444 assert(cellInterface &&
"must implement the Cell interface");
1446 std::string prefix = cellInterface.instanceName().str() +
".";
1447 for (
size_t i = 0, e = portNames.size(); i != e; ++i)
1448 setNameFn(op->getResult(i), prefix + portNames[i].str());
1459 bool isDestination) {
1460 Operation *definingOp = value.getDefiningOp();
1461 bool isComponentPort = isa<BlockArgument>(value),
1462 isCellInterfacePort = isa_and_nonnull<CellInterface>(definingOp);
1463 assert((isComponentPort || isCellInterfacePort) &&
"Not a port.");
1467 : cast<CellInterface>(definingOp).portInfo(value);
1469 bool isSource = !isDestination;
1472 (isDestination && isComponentPort) || (isSource && isCellInterfacePort)
1479 <<
"has a " << (isComponentPort ?
"component" :
"cell")
1481 << (isDestination ?
"destination" :
"source")
1482 <<
" with the incorrect direction.";
1489 bool isSource = !isDestination;
1490 Value value = isDestination ? op.getDest() : op.getSrc();
1495 if (isDestination && !isa<GroupGoOp, GroupDoneOp>(value.getDefiningOp()))
1496 return op->emitOpError(
1497 "has an invalid destination port. It must be drive-able.");
1505 bool isDestination =
true, isSource =
false;
1514 ParseResult AssignOp::parse(OpAsmParser &parser, OperationState &result) {
1515 OpAsmParser::UnresolvedOperand destination;
1516 if (parser.parseOperand(destination) || parser.parseEqual())
1522 OpAsmParser::UnresolvedOperand guardOrSource;
1523 if (parser.parseOperand(guardOrSource))
1528 OpAsmParser::UnresolvedOperand source;
1529 bool hasGuard = succeeded(parser.parseOptionalQuestion());
1532 if (parser.parseOperand(source))
1537 if (parser.parseColonType(type) ||
1538 parser.resolveOperand(destination, type, result.operands))
1542 Type i1Type = parser.getBuilder().getI1Type();
1545 if (parser.resolveOperand(source, type, result.operands) ||
1546 parser.resolveOperand(guardOrSource, i1Type, result.operands))
1550 if (parser.resolveOperand(guardOrSource, type, result.operands))
1557 void AssignOp::print(OpAsmPrinter &p) {
1558 p <<
" " << getDest() <<
" = ";
1560 Value bguard = getGuard(), source = getSrc();
1563 p << bguard <<
" ? ";
1567 p << source <<
" : " << source.getType();
1576 ComponentInterface InstanceOp::getReferencedComponent() {
1577 auto module = (*this)->getParentOfType<ModuleOp>();
1581 return module.lookupSymbol<ComponentInterface>(getComponentName());
1587 static LogicalResult
1589 ComponentInterface referencedComponent) {
1590 auto module = instance->getParentOfType<ModuleOp>();
1591 StringRef entryPointName =
1592 module->getAttrOfType<StringAttr>(
"calyx.entrypoint");
1593 if (instance.getComponentName() == entryPointName)
1594 return instance.emitOpError()
1595 <<
"cannot reference the entry-point component: '" << entryPointName
1599 SmallVector<PortInfo> componentPorts = referencedComponent.getPortInfo();
1600 size_t numPorts = componentPorts.size();
1602 size_t numResults = instance.getNumResults();
1603 if (numResults != numPorts)
1604 return instance.emitOpError()
1605 <<
"has a wrong number of results; expected: " << numPorts
1606 <<
" but got " << numResults;
1608 for (
size_t i = 0; i != numResults; ++i) {
1609 auto resultType = instance.getResult(i).getType();
1610 auto expectedType = componentPorts[i].type;
1611 if (resultType == expectedType)
1613 return instance.emitOpError()
1614 <<
"result type for " << componentPorts[i].name <<
" must be "
1615 << expectedType <<
", but got " << resultType;
1620 LogicalResult InstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1621 Operation *op = *
this;
1622 auto module = op->getParentOfType<ModuleOp>();
1623 Operation *referencedComponent =
1624 symbolTable.lookupNearestSymbolFrom(module, getComponentNameAttr());
1625 if (referencedComponent ==
nullptr)
1626 return emitError() <<
"referencing component: '" << getComponentName()
1627 <<
"', which does not exist.";
1629 Operation *shadowedComponentName =
1630 symbolTable.lookupNearestSymbolFrom(module, getSymNameAttr());
1631 if (shadowedComponentName !=
nullptr)
1632 return emitError() <<
"instance symbol: '" << instanceName()
1633 <<
"' is already a symbol for another component.";
1636 auto parentComponent = op->getParentOfType<ComponentOp>();
1637 if (parentComponent == referencedComponent)
1638 return emitError() <<
"recursive instantiation of its parent component: '"
1639 << getComponentName() <<
"'";
1641 assert(isa<ComponentInterface>(referencedComponent) &&
1642 "Should be a ComponentInterface.");
1644 cast<ComponentInterface>(referencedComponent));
1652 SmallVector<StringRef> InstanceOp::portNames() {
1653 SmallVector<StringRef> portNames;
1654 for (Attribute name : getReferencedComponent().getPortNames())
1655 portNames.push_back(cast<StringAttr>(name).getValue());
1659 SmallVector<Direction> InstanceOp::portDirections() {
1660 SmallVector<Direction> portDirections;
1662 portDirections.push_back(port.direction);
1663 return portDirections;
1666 SmallVector<DictionaryAttr> InstanceOp::portAttributes() {
1667 SmallVector<DictionaryAttr> portAttributes;
1669 portAttributes.push_back(port.attributes);
1670 return portAttributes;
1674 return isa<CombComponentOp>(getReferencedComponent());
1684 auto module = (*this)->getParentOfType<ModuleOp>();
1694 static LogicalResult
1697 auto module = instance->getParentOfType<ModuleOp>();
1698 StringRef entryPointName =
1699 module->getAttrOfType<StringAttr>(
"calyx.entrypoint");
1700 if (instance.getPrimitiveName() == entryPointName)
1701 return instance.emitOpError()
1702 <<
"cannot reference the entry-point component: '" << entryPointName
1706 auto primitivePorts = referencedPrimitive.getPortList();
1707 size_t numPorts = primitivePorts.size();
1709 size_t numResults = instance.getNumResults();
1710 if (numResults != numPorts)
1711 return instance.emitOpError()
1712 <<
"has a wrong number of results; expected: " << numPorts
1713 <<
" but got " << numResults;
1716 ArrayAttr modParameters = referencedPrimitive.getParameters();
1717 ArrayAttr parameters = instance.getParameters().value_or(ArrayAttr());
1718 size_t numExpected = modParameters.size();
1719 size_t numParams = parameters.size();
1720 if (numParams != numExpected)
1721 return instance.emitOpError()
1722 <<
"has the wrong number of parameters; expected: " << numExpected
1723 <<
" but got " << numParams;
1725 for (
size_t i = 0; i != numExpected; ++i) {
1726 auto param = cast<circt::hw::ParamDeclAttr>(parameters[i]);
1727 auto modParam = cast<circt::hw::ParamDeclAttr>(modParameters[i]);
1729 auto paramName = param.getName();
1730 if (paramName != modParam.getName())
1731 return instance.emitOpError()
1732 <<
"parameter #" << i <<
" should have name " << modParam.getName()
1733 <<
" but has name " << paramName;
1735 if (param.getType() != modParam.getType())
1736 return instance.emitOpError()
1737 <<
"parameter " << paramName <<
" should have type "
1738 << modParam.getType() <<
" but has type " << param.getType();
1742 if (!param.getValue())
1743 return instance.emitOpError(
"parameter ")
1744 << paramName <<
" must have a value";
1747 for (
size_t i = 0; i != numResults; ++i) {
1748 auto resultType = instance.getResult(i).getType();
1749 auto expectedType = primitivePorts[i].type;
1751 instance.getLoc(), instance.getParametersAttr(), expectedType);
1752 if (failed(replacedType))
1754 if (resultType == replacedType)
1756 return instance.emitOpError()
1757 <<
"result type for " << primitivePorts[i].name <<
" must be "
1758 << expectedType <<
", but got " << resultType;
1764 PrimitiveOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1765 Operation *op = *
this;
1766 auto module = op->getParentOfType<ModuleOp>();
1767 Operation *referencedPrimitive =
1768 symbolTable.lookupNearestSymbolFrom(module, getPrimitiveNameAttr());
1769 if (referencedPrimitive ==
nullptr)
1770 return emitError() <<
"referencing primitive: '" << getPrimitiveName()
1771 <<
"', which does not exist.";
1773 Operation *shadowedPrimitiveName =
1774 symbolTable.lookupNearestSymbolFrom(module, getSymNameAttr());
1775 if (shadowedPrimitiveName !=
nullptr)
1776 return emitError() <<
"instance symbol: '" << instanceName()
1777 <<
"' is already a symbol for another primitive.";
1781 if (parentPrimitive == referencedPrimitive)
1782 return emitError() <<
"recursive instantiation of its parent primitive: '"
1783 << getPrimitiveName() <<
"'";
1785 assert(isa<hw::HWModuleExternOp>(referencedPrimitive) &&
1786 "Should be a HardwareModuleExternOp.");
1789 cast<hw::HWModuleExternOp>(referencedPrimitive));
1797 SmallVector<StringRef> PrimitiveOp::portNames() {
1798 SmallVector<StringRef> portNames;
1799 auto ports = getReferencedPrimitive().getPortList();
1800 for (
auto port : ports)
1801 portNames.push_back(port.name.getValue());
1807 switch (direction) {
1813 llvm_unreachable(
"InOut ports not supported by Calyx");
1815 llvm_unreachable(
"Impossible port type");
1818 SmallVector<Direction> PrimitiveOp::portDirections() {
1819 SmallVector<Direction> portDirections;
1820 auto ports = getReferencedPrimitive().getPortList();
1821 for (hw::PortInfo port : ports)
1823 return portDirections;
1832 DictionaryAttr dict) {
1836 llvm::SmallVector<NamedAttribute> attrs;
1837 for (NamedAttribute attr : dict) {
1838 Dialect *dialect = attr.getNameDialect();
1839 if (dialect ==
nullptr || !isa<CalyxDialect>(*dialect))
1841 StringRef name = attr.getName().strref();
1842 StringAttr newName = builder.getStringAttr(std::get<1>(name.split(
".")));
1843 attr.setName(newName);
1844 attrs.push_back(attr);
1846 return builder.getDictionaryAttr(attrs);
1850 SmallVector<DictionaryAttr> PrimitiveOp::portAttributes() {
1851 SmallVector<DictionaryAttr> portAttributes;
1852 OpBuilder builder(getContext());
1854 auto argAttrs = prim.getAllInputAttrs();
1855 auto resAttrs = prim.getAllOutputAttrs();
1856 for (
auto a : argAttrs)
1857 portAttributes.push_back(
1859 for (
auto a : resAttrs)
1860 portAttributes.push_back(
1862 return portAttributes;
1871 SmallVector<Attribute> ¶meters) {
1873 return parser.parseCommaSeparatedList(
1874 OpAsmParser::Delimiter::OptionalLessGreater, [&]() {
1879 if (parser.parseKeywordOrString(&name) || parser.parseColonType(type))
1883 if (succeeded(parser.parseOptionalEqual())) {
1884 if (parser.parseAttribute(value, type))
1888 auto &builder = parser.getBuilder();
1890 builder.getContext(), builder.getStringAttr(name), type, value));
1897 ArrayAttr ¶meters) {
1898 SmallVector<Attribute> parseParameters;
1902 parameters =
ArrayAttr::get(parser.getContext(), parseParameters);
1909 ArrayAttr parameters) {
1910 if (parameters.empty())
1914 llvm::interleaveComma(parameters, p, [&](Attribute param) {
1915 auto paramAttr = cast<hw::ParamDeclAttr>(param);
1916 p << paramAttr.getName().getValue() <<
": " << paramAttr.getType();
1917 if (
auto value = paramAttr.getValue()) {
1919 p.printAttributeWithoutType(value);
1925 //===----------------------------------------------------------------------===//
1927 //===----------------------------------------------------------------------===//
1929 LogicalResult GroupGoOp::verify() { return verifyNotComplexSource(*this); }
1932 void GroupGoOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1933 auto parent = (*this)->getParentOfType<GroupOp>();
1934 StringRef name = parent.getSymName();
1935 std::string resultName = name.str() + ".go";
1936 setNameFn(getResult(), resultName);
1939 void GroupGoOp::print(OpAsmPrinter &p) { printGroupPort(p, *this); }
1941 ParseResult GroupGoOp::parse(OpAsmParser &parser, OperationState &result) {
1942 if (parseGroupPort(parser, result))
1945 result.addTypes(parser.getBuilder().getI1Type());
1949 //===----------------------------------------------------------------------===//
1951 //===----------------------------------------------------------------------===//
1953 LogicalResult GroupDoneOp::verify() {
1954 Operation *srcOp = getSrc().getDefiningOp();
1955 Value optionalGuard = getGuard();
1956 Operation *guardOp = optionalGuard ? optionalGuard.getDefiningOp() : nullptr;
1957 bool noGuard = (guardOp == nullptr);
1959 if (srcOp == nullptr)
1960 // This is a port of the parent component.
1963 if (isa<hw::ConstantOp>(srcOp) && (noGuard || isa<hw::ConstantOp>(guardOp)))
1964 return emitOpError() << "with constant source"
1965 << (noGuard ? "" : " and constant guard")
1966 << ". This should be a combinational group.";
1968 return verifyNotComplexSource(*this);
1971 void GroupDoneOp::print(OpAsmPrinter &p) { printGroupPort(p, *this); }
1973 ParseResult GroupDoneOp::parse(OpAsmParser &parser, OperationState &result) {
1974 return parseGroupPort(parser, result);
1977 //===----------------------------------------------------------------------===//
1979 //===----------------------------------------------------------------------===//
1980 void ConstantOp::getAsmResultNames(
1981 function_ref<void(Value, StringRef)> setNameFn) {
1982 if (isa<FloatAttr>(getValue())) {
1983 setNameFn(getResult(), "cst");
1986 auto intCst = llvm::dyn_cast<IntegerAttr>(getValue());
1987 auto intType = llvm::dyn_cast<IntegerType>(getType());
1989 // Sugar i1 constants with '
true' and 'false'.
1990 if (intType && intType.getWidth() == 1)
1991 return setNameFn(getResult(), intCst.getInt() > 0 ? "true" : "false");
1993 // Otherwise, build a complex name with the value and type.
1994 SmallString<32> specialNameBuffer;
1995 llvm::raw_svector_ostream specialName(specialNameBuffer);
1996 specialName << 'c
' << intCst.getValue();
1998 specialName << '_
' << getType();
1999 setNameFn(getResult(), specialName.str());
2002 LogicalResult ConstantOp::verify() {
2003 auto type = getType();
2004 assert(isa<IntegerType>(type) && "must be an IntegerType");
2005 // The value's bit
width must match the
return type bitwidth.
2006 if (
auto valTyBitWidth = getValue().getType().getIntOrFloatBitWidth();
2007 valTyBitWidth != type.getIntOrFloatBitWidth()) {
2008 return emitOpError() <<
"value type bit width" << valTyBitWidth
2009 <<
" must match return type: "
2010 << type.getIntOrFloatBitWidth();
2013 if (llvm::isa<IntegerType>(type) &&
2014 !llvm::cast<IntegerType>(type).isSignless())
2015 return emitOpError(
"integer return type must be signless");
2017 if (!llvm::isa<IntegerAttr, FloatAttr>(getValue())) {
2018 return emitOpError(
"value must be an integer or float attribute");
2024 OpFoldResult calyx::ConstantOp::fold(FoldAdaptor adaptor) {
2025 return getValueAttr();
2028 void calyx::ConstantOp::build(OpBuilder &builder, OperationState &state,
2029 StringRef symName, Attribute attr, Type type) {
2030 state.addAttribute(SymbolTable::getSymbolAttrName(),
2031 builder.getStringAttr(symName));
2032 state.addAttribute(
"value", attr);
2033 SmallVector<Type> types;
2034 types.push_back(type);
2035 state.addTypes(types);
2038 SmallVector<StringRef> ConstantOp::portNames() {
return {
"out"}; }
2040 SmallVector<Direction> ConstantOp::portDirections() {
return {
Output}; }
2042 SmallVector<DictionaryAttr> ConstantOp::portAttributes() {
2057 SmallVector<StringRef> RegisterOp::portNames() {
2061 SmallVector<Direction> RegisterOp::portDirections() {
2065 SmallVector<DictionaryAttr> RegisterOp::portAttributes() {
2066 MLIRContext *context = getContext();
2068 NamedAttrList writeEn,
clk, reset, done;
2069 writeEn.append(
goPort, isSet);
2075 writeEn.getDictionary(context),
2076 clk.getDictionary(context),
2077 reset.getDictionary(context),
2079 done.getDictionary(context)
2094 SmallVector<StringRef> MemoryOp::portNames() {
2095 SmallVector<StringRef> portNames;
2096 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i) {
2099 portNames.push_back(nameAttr.getValue());
2101 portNames.append({
"write_data",
"write_en",
clkPort,
"read_data",
donePort});
2105 SmallVector<Direction> MemoryOp::portDirections() {
2106 SmallVector<Direction> portDirections;
2107 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2108 portDirections.push_back(
Input);
2110 return portDirections;
2113 SmallVector<DictionaryAttr> MemoryOp::portAttributes() {
2114 SmallVector<DictionaryAttr> portAttributes;
2115 MLIRContext *context = getContext();
2116 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2121 NamedAttrList writeEn,
clk, reset, done;
2122 writeEn.append(
goPort, isSet);
2126 writeEn.getDictionary(context),
2127 clk.getDictionary(context),
2129 done.getDictionary(context)}
2131 return portAttributes;
2134 void MemoryOp::build(OpBuilder &builder, OperationState &state,
2135 StringRef instanceName, int64_t
width,
2136 ArrayRef<int64_t> sizes, ArrayRef<int64_t> addrSizes) {
2137 state.addAttribute(SymbolTable::getSymbolAttrName(),
2138 builder.getStringAttr(instanceName));
2139 state.addAttribute(
"width", builder.getI64IntegerAttr(
width));
2140 state.addAttribute(
"sizes", builder.getI64ArrayAttr(sizes));
2141 state.addAttribute(
"addrSizes", builder.getI64ArrayAttr(addrSizes));
2142 SmallVector<Type> types;
2143 for (int64_t size : addrSizes)
2144 types.push_back(builder.getIntegerType(size));
2145 types.push_back(builder.getIntegerType(
width));
2146 types.push_back(builder.getI1Type());
2147 types.push_back(builder.getI1Type());
2148 types.push_back(builder.getIntegerType(
width));
2149 types.push_back(builder.getI1Type());
2150 state.addTypes(types);
2154 ArrayRef<Attribute> opSizes = getSizes().getValue();
2155 ArrayRef<Attribute> opAddrSizes = getAddrSizes().getValue();
2156 size_t numDims = getSizes().size();
2157 size_t numAddrs = getAddrSizes().size();
2158 if (numDims != numAddrs)
2159 return emitOpError(
"mismatched number of dimensions (")
2160 << numDims <<
") and address sizes (" << numAddrs <<
")";
2162 size_t numExtraPorts = 5;
2163 if (getNumResults() != numAddrs + numExtraPorts)
2164 return emitOpError(
"incorrect number of address ports, expected ")
2167 for (
size_t i = 0; i < numDims; ++i) {
2168 int64_t size = cast<IntegerAttr>(opSizes[i]).getInt();
2169 int64_t addrSize = cast<IntegerAttr>(opAddrSizes[i]).getInt();
2170 if (llvm::Log2_64_Ceil(size) > addrSize)
2171 return emitOpError(
"address size (")
2172 << addrSize <<
") for dimension " << i
2173 <<
" can't address the entire range (" << size <<
")";
2188 SmallVector<StringRef> SeqMemoryOp::portNames() {
2189 SmallVector<StringRef> portNames;
2190 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i) {
2193 portNames.push_back(nameAttr.getValue());
2195 portNames.append({
clkPort,
"reset",
"content_en",
"write_en",
"write_data",
2196 "read_data",
"done"});
2200 SmallVector<Direction> SeqMemoryOp::portDirections() {
2201 SmallVector<Direction> portDirections;
2202 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2203 portDirections.push_back(
Input);
2205 return portDirections;
2208 SmallVector<DictionaryAttr> SeqMemoryOp::portAttributes() {
2209 SmallVector<DictionaryAttr> portAttributes;
2210 MLIRContext *context = getContext();
2211 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2214 OpBuilder builder(context);
2218 NamedAttrList done,
clk, reset, contentEn;
2222 contentEn.append(
goPort, isTwo);
2223 portAttributes.append({
clk.getDictionary(context),
2224 reset.getDictionary(context),
2225 contentEn.getDictionary(context),
2229 done.getDictionary(context)}
2231 return portAttributes;
2234 void SeqMemoryOp::build(OpBuilder &builder, OperationState &state,
2235 StringRef instanceName, int64_t
width,
2236 ArrayRef<int64_t> sizes, ArrayRef<int64_t> addrSizes) {
2237 state.addAttribute(SymbolTable::getSymbolAttrName(),
2238 builder.getStringAttr(instanceName));
2239 state.addAttribute(
"width", builder.getI64IntegerAttr(
width));
2240 state.addAttribute(
"sizes", builder.getI64ArrayAttr(sizes));
2241 state.addAttribute(
"addrSizes", builder.getI64ArrayAttr(addrSizes));
2242 SmallVector<Type> types;
2243 for (int64_t size : addrSizes)
2244 types.push_back(builder.getIntegerType(size));
2245 types.push_back(builder.getI1Type());
2246 types.push_back(builder.getI1Type());
2247 types.push_back(builder.getI1Type());
2248 types.push_back(builder.getI1Type());
2249 types.push_back(builder.getIntegerType(
width));
2250 types.push_back(builder.getIntegerType(
width));
2251 types.push_back(builder.getI1Type());
2252 state.addTypes(types);
2256 ArrayRef<Attribute> opSizes = getSizes().getValue();
2257 ArrayRef<Attribute> opAddrSizes = getAddrSizes().getValue();
2258 size_t numDims = getSizes().size();
2259 size_t numAddrs = getAddrSizes().size();
2260 if (numDims != numAddrs)
2261 return emitOpError(
"mismatched number of dimensions (")
2262 << numDims <<
") and address sizes (" << numAddrs <<
")";
2264 size_t numExtraPorts =
2266 if (getNumResults() != numAddrs + numExtraPorts)
2267 return emitOpError(
"incorrect number of address ports, expected ")
2270 for (
size_t i = 0; i < numDims; ++i) {
2271 int64_t size = cast<IntegerAttr>(opSizes[i]).getInt();
2272 int64_t addrSize = cast<IntegerAttr>(opAddrSizes[i]).getInt();
2273 if (llvm::Log2_64_Ceil(size) > addrSize)
2274 return emitOpError(
"address size (")
2275 << addrSize <<
") for dimension " << i
2276 <<
" can't address the entire range (" << size <<
")";
2286 auto component = (*this)->getParentOfType<ComponentOp>();
2287 auto wiresOp = component.getWiresOp();
2288 StringRef name = getGroupName();
2290 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(name);
2292 return emitOpError() <<
"with group '" << name
2293 <<
"', which does not exist.";
2295 if (isa<CombGroupOp>(groupOp))
2296 return emitOpError() <<
"with group '" << name
2297 <<
"', which is a combinational group.";
2307 std::optional<StringRef> optGroupName = getGroupName();
2308 if (!optGroupName) {
2312 auto component = (*this)->getParentOfType<ComponentOp>();
2313 WiresOp wiresOp = component.getWiresOp();
2314 StringRef groupName = *optGroupName;
2315 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(groupName);
2317 return emitOpError() <<
"with group '" << groupName
2318 <<
"', which does not exist.";
2320 if (isa<GroupOp>(groupOp))
2321 return emitOpError() <<
"with group '" << groupName
2322 <<
"', which is not a combinational group.";
2324 if (failed(groupOp.drivesPort(getCond())))
2325 return emitError() <<
"with conditional op: '"
2327 <<
"' expected to be driven from group: '" << groupName
2328 <<
"' but no driver was found.";
2336 template <
typename OpTy>
2338 static_assert(IsAny<OpTy, SeqOp, StaticSeqOp>(),
2339 "Should be a StaticSeqOp or SeqOp.");
2340 if (parent.getBodyBlock()->empty())
2341 return std::nullopt;
2342 auto &lastOp = parent.getBodyBlock()->back();
2343 if (
auto enableOp = dyn_cast<EnableOp>(lastOp))
2345 if (
auto seqOp = dyn_cast<SeqOp>(lastOp))
2347 if (
auto staticSeqOp = dyn_cast<StaticSeqOp>(lastOp))
2350 return std::nullopt;
2355 template <
typename OpTy>
2357 static_assert(IsAny<OpTy, ParOp, StaticParOp>(),
2358 "Should be a StaticParOp or ParOp.");
2360 llvm::StringMap<EnableOp> enables;
2361 Block *body = parent.getBodyBlock();
2362 for (EnableOp op : body->getOps<EnableOp>())
2363 enables.insert(std::pair(op.getGroupName(), op));
2375 template <
typename IfOpTy,
typename TailOpTy>
2377 static_assert(IsAny<TailOpTy, SeqOp, ParOp, StaticSeqOp, StaticParOp>(),
2378 "Should be a SeqOp, ParOp, StaticSeqOp, or StaticParOp.");
2379 static_assert(IsAny<IfOpTy, IfOp, StaticIfOp>(),
2380 "Should be a IfOp or StaticIfOp.");
2382 if (!op.thenBodyExists() || !op.elseBodyExists())
2384 if (op.getThenBody()->empty() || op.getElseBody()->empty())
2387 Block *thenBody = op.getThenBody(), *elseBody = op.getElseBody();
2388 return isa<TailOpTy>(thenBody->front()) && isa<TailOpTy>(elseBody->front());
2399 template <
typename IfOpTy,
typename SeqOpTy>
2401 PatternRewriter &rewriter) {
2402 static_assert(IsAny<IfOpTy, IfOp, StaticIfOp>(),
2403 "Should be an IfOp or StaticIfOp.");
2404 static_assert(IsAny<SeqOpTy, SeqOp, StaticSeqOp>(),
2405 "Branches should be checking for an SeqOp or StaticSeqOp");
2406 if (!hasCommonTailPatternPreConditions<IfOpTy, SeqOpTy>(ifOp))
2408 auto thenControl = cast<SeqOpTy>(ifOp.getThenBody()->front()),
2409 elseControl = cast<SeqOpTy>(ifOp.getElseBody()->front());
2411 std::optional<EnableOp> lastThenEnableOp =
getLastEnableOp(thenControl),
2414 if (!lastThenEnableOp || !lastElseEnableOp)
2416 if (lastThenEnableOp->getGroupName() != lastElseEnableOp->getGroupName())
2422 rewriter.setInsertionPointAfter(ifOp);
2423 SeqOpTy seqOp = rewriter.create<SeqOpTy>(ifOp.getLoc());
2424 Block *body = seqOp.getBodyBlock();
2426 body->push_back(ifOp);
2427 rewriter.setInsertionPointToEnd(body);
2428 rewriter.create<EnableOp>(seqOp.getLoc(), lastThenEnableOp->getGroupName());
2431 rewriter.eraseOp(*lastThenEnableOp);
2432 rewriter.eraseOp(*lastElseEnableOp);
2449 template <
typename OpTy,
typename ParOpTy>
2451 PatternRewriter &rewriter) {
2452 static_assert(IsAny<OpTy, IfOp, StaticIfOp>(),
2453 "Should be an IfOp or StaticIfOp.");
2454 static_assert(IsAny<ParOpTy, ParOp, StaticParOp>(),
2455 "Branches should be checking for an ParOp or StaticParOp");
2456 if (!hasCommonTailPatternPreConditions<OpTy, ParOpTy>(controlOp))
2458 auto thenControl = cast<ParOpTy>(controlOp.getThenBody()->front()),
2459 elseControl = cast<ParOpTy>(controlOp.getElseBody()->front());
2464 SmallVector<StringRef> groupNames;
2465 for (
auto aIndex = a.begin(); aIndex != a.end(); ++aIndex) {
2466 StringRef groupName = aIndex->getKey();
2467 auto bIndex = b.find(groupName);
2468 if (bIndex == b.end())
2471 groupNames.push_back(groupName);
2473 rewriter.eraseOp(aIndex->getValue());
2474 rewriter.eraseOp(bIndex->getValue());
2480 rewriter.setInsertionPointAfter(controlOp);
2482 ParOpTy parOp = rewriter.create<ParOpTy>(controlOp.getLoc());
2483 Block *body = parOp.getBodyBlock();
2484 controlOp->remove();
2485 body->push_back(controlOp);
2488 rewriter.setInsertionPointToEnd(body);
2489 for (StringRef groupName : groupNames)
2490 rewriter.create<EnableOp>(parOp.getLoc(), groupName);
2501 PatternRewriter &rewriter)
const override {
2502 if (!ifOp.getThenBody()->empty())
2504 if (ifOp.elseBodyExists() && !ifOp.getElseBody()->empty())
2513 void IfOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
2514 MLIRContext *context) {
2516 patterns.add(commonTailPatternWithPar<IfOp, ParOp>);
2517 patterns.add(commonTailPatternWithSeq<IfOp, SeqOp>);
2524 if (elseBodyExists()) {
2525 auto *elseBod = getElseBody();
2526 auto &elseOps = elseBod->getOperations();
2528 for (Operation &op : elseOps) {
2530 return op.emitOpError(
2531 "static if's else branch has non static control within it");
2536 auto *thenBod = getThenBody();
2537 auto &thenOps = thenBod->getOperations();
2538 for (Operation &op : thenOps) {
2541 return op.emitOpError(
2542 "static if's then branch has non static control within it");
2555 PatternRewriter &rewriter)
const override {
2556 if (!ifOp.getThenBody()->empty())
2558 if (ifOp.elseBodyExists() && !ifOp.getElseBody()->empty())
2567 void StaticIfOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
2568 MLIRContext *context) {
2570 patterns.add(commonTailPatternWithPar<StaticIfOp, StaticParOp>);
2571 patterns.add(commonTailPatternWithSeq<StaticIfOp, StaticSeqOp>);
2578 auto component = (*this)->getParentOfType<ComponentOp>();
2579 auto wiresOp = component.getWiresOp();
2581 std::optional<StringRef> optGroupName = getGroupName();
2582 if (!optGroupName) {
2586 StringRef groupName = *optGroupName;
2587 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(groupName);
2589 return emitOpError() <<
"with group '" << groupName
2590 <<
"', which does not exist.";
2592 if (isa<GroupOp>(groupOp))
2593 return emitOpError() <<
"with group '" << groupName
2594 <<
"', which is not a combinational group.";
2596 if (failed(groupOp.drivesPort(getCond())))
2597 return emitError() <<
"conditional op: '" <<
valueName(component, getCond())
2598 <<
"' expected to be driven from group: '" << groupName
2599 <<
"' but no driver was found.";
2605 PatternRewriter &rewriter) {
2606 if (whileOp.getBodyBlock()->empty()) {
2618 for (
auto &&bodyOp : (*this).getRegion().front()) {
2621 return bodyOp.emitOpError(
2622 "static repeat has non static control within it");
2629 template <
typename OpTy>
2630 static LogicalResult
zeroRepeat(OpTy op, PatternRewriter &rewriter) {
2631 static_assert(IsAny<OpTy, RepeatOp, StaticRepeatOp>(),
2632 "Should be a RepeatOp or StaticPRepeatOp");
2633 if (op.getCount() == 0) {
2634 Block *controlBody = op.getBodyBlock();
2635 for (
auto &op : make_early_inc_range(*controlBody))
2638 rewriter.eraseOp(op);
2645 void StaticRepeatOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
2646 MLIRContext *context) {
2647 patterns.add(emptyControl<StaticRepeatOp>);
2648 patterns.add(zeroRepeat<StaticRepeatOp>);
2654 void RepeatOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
2655 MLIRContext *context) {
2656 patterns.add(emptyControl<RepeatOp>);
2657 patterns.add(zeroRepeat<RepeatOp>);
2667 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &ports,
2668 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &inputs,
2669 SmallVectorImpl<Attribute> &portNames,
2670 SmallVectorImpl<Attribute> &inputNames,
2671 SmallVectorImpl<Type> &types) {
2672 OpAsmParser::UnresolvedOperand port;
2673 OpAsmParser::UnresolvedOperand input;
2675 auto parseParameter = [&]() -> ParseResult {
2676 if (parser.parseOperand(port) || parser.parseEqual() ||
2677 parser.parseOperand(input))
2679 ports.push_back(port);
2681 inputs.push_back(input);
2682 inputNames.push_back(
StringAttr::get(parser.getContext(), input.name));
2685 if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
2688 if (parser.parseArrow())
2691 if (parser.parseType(type))
2693 types.push_back(type);
2696 return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
2700 ParseResult InvokeOp::parse(OpAsmParser &parser, OperationState &result) {
2701 StringAttr componentName;
2702 SmallVector<OpAsmParser::UnresolvedOperand, 4> ports;
2703 SmallVector<OpAsmParser::UnresolvedOperand, 4> inputs;
2704 SmallVector<Attribute> portNames;
2705 SmallVector<Attribute> inputNames;
2706 SmallVector<Type, 4> types;
2707 if (parser.parseSymbolName(componentName))
2710 SMLoc loc = parser.getCurrentLocation();
2712 SmallVector<Attribute, 4> refCells;
2713 if (succeeded(parser.parseOptionalLSquare())) {
2714 if (parser.parseCommaSeparatedList([&]() -> ParseResult {
2715 std::string refCellName;
2716 std::string externalMem;
2717 NamedAttrList refCellAttr;
2718 if (parser.parseKeywordOrString(&refCellName) ||
2719 parser.parseEqual() || parser.parseKeywordOrString(&externalMem))
2721 auto externalMemAttr =
2722 SymbolRefAttr::get(parser.getContext(), externalMem);
2723 refCellAttr.append(StringAttr::get(parser.getContext(), refCellName),
2726 DictionaryAttr::get(parser.getContext(), refCellAttr));
2729 parser.parseRSquare())
2732 result.addAttribute(
"refCellsMap",
2735 result.addAttribute(
"callee", callee);
2739 if (parser.resolveOperands(ports, types, loc, result.operands))
2741 if (parser.resolveOperands(inputs, types, loc, result.operands))
2743 result.addAttribute(
"portNames",
2745 result.addAttribute(
"inputNames",
2750 void InvokeOp::print(OpAsmPrinter &p) {
2751 p <<
" @" << getCallee() <<
"[";
2752 auto refCellNamesMap = getRefCellsMap();
2753 llvm::interleaveComma(refCellNamesMap, p, [&](Attribute attr) {
2754 auto dictAttr = cast<DictionaryAttr>(attr);
2755 llvm::interleaveComma(dictAttr, p, [&](NamedAttribute namedAttr) {
2756 auto refCellName = namedAttr.getName().str();
2758 cast<FlatSymbolRefAttr>(namedAttr.getValue()).getValue();
2759 p << refCellName <<
" = " << externalMem;
2764 auto ports = getPorts();
2765 auto inputs = getInputs();
2766 llvm::interleaveComma(llvm::zip(ports, inputs), p, [&](
auto arg) {
2767 p << std::get<0>(arg) <<
" = " << std::get<1>(arg);
2770 llvm::interleaveComma(ports, p, [&](
auto port) { p << port.getType(); });
2777 bool isDestination) {
2786 Operation *operation = value.getDefiningOp();
2787 if (operation ==
nullptr)
2789 if (
auto *dialect = operation->getDialect(); isa<comb::CombDialect>(dialect))
2795 Value InvokeOp::getInstGoValue() {
2796 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2797 Operation *operation = componentOp.lookupSymbol(getCallee());
2798 Value ret =
nullptr;
2799 llvm::TypeSwitch<Operation *>(operation)
2800 .Case<RegisterOp>([&](
auto op) { ret = operation->getResult(1); })
2801 .Case<MemoryOp, DivSPipeLibOp, DivUPipeLibOp, MultPipeLibOp,
2802 RemSPipeLibOp, RemUPipeLibOp>(
2803 [&](
auto op) { ret = operation->getResult(2); })
2804 .Case<InstanceOp>([&](
auto op) {
2805 auto portInfo = op.getReferencedComponent().getPortInfo();
2806 for (
auto [portInfo, res] :
2807 llvm::zip(portInfo, operation->getResults())) {
2808 if (portInfo.hasAttribute(
goPort))
2812 .Case<PrimitiveOp>([&](
auto op) {
2813 auto moduleExternOp = op.getReferencedPrimitive();
2814 auto argAttrs = moduleExternOp.getAllInputAttrs();
2815 for (
auto [attr, res] : llvm::zip(argAttrs, op.getResults())) {
2816 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2817 if (!dictAttr.empty()) {
2818 if (dictAttr.begin()->getName().getValue() ==
"calyx.go")
2828 Value InvokeOp::getInstDoneValue() {
2829 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2830 Operation *operation = componentOp.lookupSymbol(getCallee());
2831 Value ret =
nullptr;
2832 llvm::TypeSwitch<Operation *>(operation)
2833 .Case<RegisterOp, MemoryOp, DivSPipeLibOp, DivUPipeLibOp, MultPipeLibOp,
2834 RemSPipeLibOp, RemUPipeLibOp>([&](
auto op) {
2835 size_t doneIdx = operation->getResults().size() - 1;
2836 ret = operation->getResult(doneIdx);
2838 .Case<InstanceOp>([&](
auto op) {
2839 InstanceOp instanceOp = cast<InstanceOp>(operation);
2840 auto portInfo = instanceOp.getReferencedComponent().getPortInfo();
2841 for (
auto [portInfo, res] :
2842 llvm::zip(portInfo, operation->getResults())) {
2843 if (portInfo.hasAttribute(
donePort))
2847 .Case<PrimitiveOp>([&](
auto op) {
2848 PrimitiveOp primOp = cast<PrimitiveOp>(operation);
2849 auto moduleExternOp = primOp.getReferencedPrimitive();
2850 auto resAttrs = moduleExternOp.getAllOutputAttrs();
2851 for (
auto [attr, res] : llvm::zip(resAttrs, primOp.getResults())) {
2852 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2853 if (!dictAttr.empty()) {
2854 if (dictAttr.begin()->getName().getValue() ==
"calyx.done")
2869 std::string str = isGo ?
"calyx.go" :
"calyx.done";
2870 for (Attribute attr : moduleExternOp.getAllInputAttrs()) {
2871 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2872 ret = llvm::count_if(dictAttr, [&](NamedAttribute iter) {
2873 return iter.getName().getValue() == str;
2881 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2882 StringRef callee = getCallee();
2883 Operation *operation = componentOp.lookupSymbol(callee);
2886 return emitOpError() <<
"with instance '@" << callee
2887 <<
"', which does not exist.";
2889 if (getInputs().
empty() && getRefCellsMap().
empty()) {
2890 return emitOpError() <<
"'@" << callee
2891 <<
"' has zero input and output port connections and "
2892 "has no passing-by-reference cells; "
2893 "expected at least one.";
2895 size_t goPortNum = 0, donePortNum = 0;
2898 llvm::TypeSwitch<Operation *>(operation)
2899 .Case<RegisterOp, DivSPipeLibOp, DivUPipeLibOp, MemoryOp, MultPipeLibOp,
2900 RemSPipeLibOp, RemUPipeLibOp>(
2901 [&](
auto op) { goPortNum = 1, donePortNum = 1; })
2902 .Case<InstanceOp>([&](
auto op) {
2903 auto portInfo = op.getReferencedComponent().getPortInfo();
2905 if (info.hasAttribute(
goPort))
2911 .Case<PrimitiveOp>([&](
auto op) {
2912 auto moduleExternOp = op.getReferencedPrimitive();
2918 if (goPortNum != 1 && donePortNum != 1)
2919 return emitOpError()
2920 <<
"'@" << callee <<
"'"
2921 <<
" is a combinational component and cannot be invoked, which must "
2922 "have single go port and single done port.";
2924 auto ports = getPorts();
2925 auto inputs = getInputs();
2927 Value goValue = getInstGoValue();
2928 Value doneValue = getInstDoneValue();
2929 for (
auto [port, input, portName, inputName] :
2930 llvm::zip(ports, inputs, getPortNames(), getInputNames())) {
2935 return emitOpError() <<
"'@" << callee <<
"' has input '"
2936 << cast<StringAttr>(portName).getValue()
2937 <<
"', which is a source port. The inputs are "
2938 "required to be destination ports.";
2940 if (port == goValue)
2941 return emitOpError() <<
"the go or write_en port of '@" << callee
2942 <<
"' cannot appear here.";
2945 return emitOpError() <<
"'@" << callee <<
"' has output '"
2946 << cast<StringAttr>(inputName).getValue()
2947 <<
"', which is a destination port. The inputs are "
2948 "required to be source ports.";
2950 return emitOpError() <<
"'@" << callee <<
"' has '"
2951 << cast<StringAttr>(inputName).getValue()
2952 <<
"', which is not a port or constant. Complex "
2953 "logic should be conducted in the guard.";
2954 if (input == doneValue)
2955 return emitOpError() <<
"the done port of '@" << callee
2956 <<
"' cannot appear here.";
2958 if (port.getDefiningOp() != operation && input.getDefiningOp() != operation)
2959 return emitOpError() <<
"the connection "
2960 << cast<StringAttr>(portName).getValue() <<
" = "
2961 << cast<StringAttr>(inputName).getValue()
2962 <<
" is not defined as an input port of '@" << callee
2973 unsigned inBits = getResult(0).getType().getIntOrFloatBitWidth();
2974 unsigned outBits = getResult(1).getType().getIntOrFloatBitWidth();
2975 if (inBits >= outBits)
2976 return emitOpError(
"expected input bits (")
2977 << inBits <<
')' <<
" to be less than output bits (" << outBits
2983 unsigned inBits = getResult(0).getType().getIntOrFloatBitWidth();
2984 unsigned outBits = getResult(1).getType().getIntOrFloatBitWidth();
2985 if (inBits <= outBits)
2986 return emitOpError(
"expected input bits (")
2987 << inBits <<
')' <<
" to be greater than output bits (" << outBits
2992 #define ImplBinPipeOpCellInterface(OpType, outName) \
2993 SmallVector<StringRef> OpType::portNames() { \
2994 return {clkPort, resetPort, goPort, "left", "right", outName, donePort}; \
2997 SmallVector<Direction> OpType::portDirections() { \
2998 return {Input, Input, Input, Input, Input, Output, Output}; \
3001 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
3002 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
3005 SmallVector<DictionaryAttr> OpType::portAttributes() { \
3006 MLIRContext *context = getContext(); \
3007 IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1); \
3008 NamedAttrList go, clk, reset, done; \
3009 go.append(goPort, isSet); \
3010 clk.append(clkPort, isSet); \
3011 reset.append(resetPort, isSet); \
3012 done.append(donePort, isSet); \
3014 clk.getDictionary(context),
\
3015 reset.getDictionary(context), \
3016 go.getDictionary(context), \
3017 DictionaryAttr::get(context), \
3018 DictionaryAttr::get(context), \
3019 DictionaryAttr::get(context), \
3020 done.getDictionary(context) \
3024 bool OpType::isCombinational() { return false; }
3026 #define ImplUnaryOpCellInterface(OpType) \
3027 SmallVector<StringRef> OpType::portNames() { return {"in", "out"}; } \
3028 SmallVector<Direction> OpType::portDirections() { return {Input, Output}; } \
3029 SmallVector<DictionaryAttr> OpType::portAttributes() { \
3030 return {DictionaryAttr::get(getContext()), \
3031 DictionaryAttr::get(getContext())}; \
3033 bool OpType::isCombinational() { return true; } \
3034 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
3035 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
3038 #define ImplBinOpCellInterface(OpType) \
3039 SmallVector<StringRef> OpType::portNames() { \
3040 return {"left", "right", "out"}; \
3042 SmallVector<Direction> OpType::portDirections() { \
3043 return {Input, Input, Output}; \
3045 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
3046 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
3048 bool OpType::isCombinational() { return true; } \
3049 SmallVector<DictionaryAttr> OpType::portAttributes() { \
3050 return {DictionaryAttr::get(getContext()), \
3051 DictionaryAttr::get(getContext()), \
3052 DictionaryAttr::get(getContext())}; \
3096 #include "circt/Dialect/Calyx/CalyxInterfaces.cpp.inc"
3099 #define GET_OP_CLASSES
3100 #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