18 #include "mlir/IR/AsmState.h"
19 #include "mlir/IR/Builders.h"
20 #include "mlir/IR/BuiltinAttributes.h"
21 #include "mlir/IR/BuiltinTypes.h"
22 #include "mlir/IR/Diagnostics.h"
23 #include "mlir/IR/DialectImplementation.h"
24 #include "mlir/IR/PatternMatch.h"
25 #include "mlir/IR/SymbolTable.h"
26 #include "mlir/Interfaces/FunctionImplementation.h"
27 #include "mlir/Support/LLVM.h"
28 #include "llvm/ADT/DenseMap.h"
29 #include "llvm/ADT/PriorityQueue.h"
30 #include "llvm/ADT/STLExtras.h"
31 #include "llvm/ADT/SmallSet.h"
32 #include "llvm/ADT/StringExtras.h"
33 #include "llvm/ADT/TypeSwitch.h"
34 #include "llvm/Support/Casting.h"
36 using namespace circt;
45 template <
class T,
class... Ts>
46 struct IsAny : std::disjunction<std::is_same<T, Ts>...> {};
62 size_t numDirections = nIns + nOuts;
63 APInt portDirections(numDirections, 0);
64 for (
size_t i = nIns, e = numDirections; i != e; ++i)
65 portDirections.setBit(i);
76 template <
typename CtrlOp>
80 PatternRewriter &rewriter)
const override {
81 auto &ops = ctrlOp.getBodyBlock()->getOperations();
83 (ops.size() == 1) && isa<EnableOp>(ops.front()) &&
84 isa<SeqOp, ParOp, StaticSeqOp, StaticParOp>(ctrlOp->getParentOp());
88 ops.front().moveBefore(ctrlOp);
89 rewriter.eraseOp(ctrlOp);
102 template <
typename Op>
104 Operation *definingOp = op.getSrc().getDefiningOp();
105 if (definingOp ==
nullptr)
111 if (
auto dialect = definingOp->getDialect(); isa<comb::CombDialect>(dialect))
112 return op->emitOpError(
"has source that is not a port or constant. "
113 "Complex logic should be conducted in the guard.");
120 static std::string
valueName(Operation *scopeOp, Value v) {
122 llvm::raw_string_ostream os(s);
127 AsmState asmState(scopeOp, OpPrintingFlags().assumeVerified());
128 v.printAsOperand(os, asmState);
135 Operation *definingOp = value.getDefiningOp();
136 return isa<BlockArgument>(value) ||
137 isa_and_nonnull<CellInterface>(definingOp);
142 Operation *op = arg.getOwner()->getParentOp();
143 assert(isa<ComponentInterface>(op) &&
144 "Only ComponentInterface should support lookup by BlockArgument.");
145 return cast<ComponentInterface>(op).getPortInfo()[arg.getArgNumber()];
150 return isa<ControlOp, SeqOp, IfOp, RepeatOp, WhileOp, ParOp, StaticRepeatOp,
151 StaticParOp, StaticSeqOp, StaticIfOp>(op);
156 if (isa<EnableOp>(op)) {
158 auto component = op->getParentOfType<ComponentOp>();
159 auto enableOp = llvm::cast<EnableOp>(op);
160 StringRef groupName = enableOp.getGroupName();
161 auto group = component.getWiresOp().lookupSymbol<GroupInterface>(groupName);
162 return isa<StaticGroupOp>(group);
164 return isa<StaticIfOp, StaticSeqOp, StaticRepeatOp, StaticParOp>(op);
169 if (isa<SeqOp, ParOp, StaticSeqOp, StaticParOp>(op))
174 for (
auto ®ion : op->getRegions()) {
175 auto opsIt = region.getOps();
176 size_t numOperations = std::distance(opsIt.begin(), opsIt.end());
181 bool usesEnableAsCompositionOperator =
182 numOperations > 1 && llvm::any_of(region.front(), [](
auto &&bodyOp) {
183 return isa<EnableOp>(bodyOp);
185 if (usesEnableAsCompositionOperator)
186 return op->emitOpError(
187 "EnableOp is not a composition operator. It should be nested "
188 "in a control flow operation, such as \"calyx.seq\"");
192 size_t numControlFlowRegions =
194 if (numControlFlowRegions > 1)
195 return op->emitOpError(
196 "has an invalid control sequence. Multiple control flow operations "
197 "must all be nested in a single calyx.seq or calyx.par");
203 auto *opParent = op->getParentOp();
204 if (!isa<ModuleOp>(opParent))
205 return op->emitOpError()
206 <<
"has parent: " << opParent <<
", expected ModuleOp.";
211 auto opParent = op->getParentOp();
212 if (!isa<ComponentInterface>(opParent))
213 return op->emitOpError()
214 <<
"has parent: " << opParent <<
", expected ComponentInterface.";
219 auto parent = op->getParentOp();
221 if (isa<calyx::EnableOp>(op) &&
222 !isa<calyx::CalyxDialect>(parent->getDialect())) {
231 return op->emitOpError()
232 <<
"has parent: " << parent
233 <<
", which is not allowed for a control-like operation.";
235 if (op->getNumRegions() == 0)
238 auto ®ion = op->getRegion(0);
240 auto isValidBodyOp = [](Operation *operation) {
241 return isa<EnableOp, InvokeOp, SeqOp, IfOp, RepeatOp, WhileOp, ParOp,
242 StaticParOp, StaticRepeatOp, StaticSeqOp, StaticIfOp>(operation);
244 for (
auto &&bodyOp : region.front()) {
245 if (isValidBodyOp(&bodyOp))
248 return op->emitOpError()
249 <<
"has operation: " << bodyOp.getName()
250 <<
", which is not allowed in this control-like operation";
256 auto ifOp = dyn_cast<IfInterface>(op);
258 if (ifOp.elseBodyExists() && ifOp.getElseBody()->empty())
259 return ifOp->emitOpError() <<
"empty 'else' region.";
269 SmallVector<OpAsmParser::UnresolvedOperand, 2> operandInfos;
270 OpAsmParser::UnresolvedOperand guardOrSource;
271 if (parser.parseOperand(guardOrSource))
274 if (succeeded(parser.parseOptionalQuestion())) {
275 OpAsmParser::UnresolvedOperand source;
277 if (parser.parseOperand(source))
279 operandInfos.push_back(source);
282 operandInfos.push_back(guardOrSource);
287 if (parser.parseColonType(type) ||
288 parser.resolveOperands(operandInfos, type, result.operands))
295 template <
typename GroupPortType>
297 static_assert(IsAny<GroupPortType, GroupGoOp, GroupDoneOp>(),
298 "Should be a Calyx Group port.");
302 Value guard = op.getGuard(), source = op.getSrc();
305 p << source <<
" : " << source.getType();
310 template <
typename OpTy>
312 PatternRewriter &rewriter) {
313 static_assert(IsAny<OpTy, SeqOp, ParOp, StaticSeqOp, StaticParOp>(),
314 "Should be a SeqOp, ParOp, StaticSeqOp, or StaticParOp");
316 if (isa<OpTy>(controlOp->getParentOp())) {
317 Block *controlBody = controlOp.getBodyBlock();
318 for (
auto &op : make_early_inc_range(*controlBody))
319 op.moveBefore(controlOp);
321 rewriter.eraseOp(controlOp);
328 template <
typename OpTy>
329 static LogicalResult
emptyControl(OpTy controlOp, PatternRewriter &rewriter) {
330 if (controlOp.getBodyBlock()->empty()) {
331 rewriter.eraseOp(controlOp);
340 template <
typename OpTy>
342 PatternRewriter &rewriter) {
343 static_assert(IsAny<OpTy, IfOp, WhileOp>(),
344 "This is only applicable to WhileOp and IfOp.");
347 Value cond = op.getCond();
348 std::optional<StringRef> groupName = op.getGroupName();
349 auto component = op->template getParentOfType<ComponentOp>();
350 rewriter.eraseOp(op);
354 auto group = component.getWiresOp().template lookupSymbol<GroupInterface>(
356 if (SymbolTable::symbolKnownUseEmpty(group, component.getRegion()))
357 rewriter.eraseOp(group);
360 if (!isa<BlockArgument>(cond) && cond.getDefiningOp()->use_empty())
361 rewriter.eraseOp(cond.getDefiningOp());
367 template <
typename OpTy>
369 static_assert(std::is_same<OpTy, StaticIfOp>(),
370 "This is only applicable to StatifIfOp.");
373 Value cond = op.getCond();
374 rewriter.eraseOp(op);
377 if (!isa<BlockArgument>(cond) && cond.getDefiningOp()->use_empty())
378 rewriter.eraseOp(cond.getDefiningOp());
385 template <
typename ComponentTy>
387 auto componentName = comp->template getAttrOfType<StringAttr>(
388 ::mlir::SymbolTable::getSymbolAttrName())
391 p.printSymbolName(componentName);
394 auto printPortDefList = [&](
auto ports) {
396 llvm::interleaveComma(ports, p, [&](
const PortInfo &port) {
397 p <<
"%" << port.
name.getValue() <<
": " << port.
type;
400 p.printAttributeWithoutType(port.attributes);
405 printPortDefList(comp.getInputPortInfo());
407 printPortDefList(comp.getOutputPortInfo());
410 p.printRegion(*comp.getRegion(),
false,
414 SmallVector<StringRef> elidedAttrs = {
419 ComponentTy::getFunctionTypeAttrName(comp->getName()),
420 ComponentTy::getArgAttrsAttrName(comp->getName()),
421 ComponentTy::getResAttrsAttrName(comp->getName())};
422 p.printOptionalAttrDict(comp->getAttrs(), elidedAttrs);
429 SmallVectorImpl<OpAsmParser::Argument> &ports,
430 SmallVectorImpl<Type> &portTypes,
431 SmallVectorImpl<NamedAttrList> &portAttrs) {
433 OpAsmParser::Argument port;
436 if (parser.parseArgument(port) || parser.parseColon() ||
437 parser.parseType(portType))
439 port.type = portType;
440 ports.push_back(port);
441 portTypes.push_back(portType);
443 NamedAttrList portAttr;
444 portAttrs.push_back(succeeded(parser.parseOptionalAttrDict(portAttr))
450 return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
457 SmallVectorImpl<OpAsmParser::Argument> &ports,
458 SmallVectorImpl<Type> &portTypes) {
459 SmallVector<OpAsmParser::Argument> inPorts, outPorts;
460 SmallVector<Type> inPortTypes, outPortTypes;
461 SmallVector<NamedAttrList> portAttributes;
466 if (parser.parseArrow() ||
470 auto *context = parser.getBuilder().getContext();
473 SmallVector<Attribute> portNames;
474 auto getPortName = [context](
const auto &port) -> StringAttr {
475 StringRef name = port.ssaName.name;
476 if (name.starts_with(
"%"))
477 name = name.drop_front();
480 llvm::transform(inPorts, std::back_inserter(portNames), getPortName);
481 llvm::transform(outPorts, std::back_inserter(portNames), getPortName);
483 result.addAttribute(
"portNames",
ArrayAttr::get(context, portNames));
488 ports.append(inPorts);
489 ports.append(outPorts);
490 portTypes.append(inPortTypes);
491 portTypes.append(outPortTypes);
493 SmallVector<Attribute> portAttrs;
494 llvm::transform(portAttributes, std::back_inserter(portAttrs),
495 [&](
auto attr) {
return attr.getDictionary(context); });
496 result.addAttribute(
"portAttributes",
ArrayAttr::get(context, portAttrs));
501 template <
typename ComponentTy>
503 OperationState &result) {
504 using namespace mlir::function_interface_impl;
506 StringAttr componentName;
507 if (parser.parseSymbolName(componentName,
508 ::mlir::SymbolTable::getSymbolAttrName(),
512 SmallVector<mlir::OpAsmParser::Argument> ports;
514 SmallVector<Type> portTypes;
520 auto type = parser.getBuilder().getFunctionType(portTypes, {});
521 result.addAttribute(ComponentTy::getFunctionTypeAttrName(result.name),
524 auto *body = result.addRegion();
525 if (parser.parseRegion(*body, ports))
529 body->push_back(
new Block());
531 if (parser.parseOptionalAttrDict(result.attributes))
538 template <
typename T>
539 static SmallVector<T>
concat(
const SmallVectorImpl<T> &a,
540 const SmallVectorImpl<T> &b) {
548 StringAttr name, ArrayRef<PortInfo> ports,
549 bool combinational) {
550 using namespace mlir::function_interface_impl;
552 result.addAttribute(::mlir::SymbolTable::getSymbolAttrName(), name);
554 std::pair<SmallVector<Type, 8>, SmallVector<Type, 8>> portIOTypes;
555 std::pair<SmallVector<Attribute, 8>, SmallVector<Attribute, 8>> portIONames;
556 std::pair<SmallVector<Attribute, 8>, SmallVector<Attribute, 8>>
558 SmallVector<Direction, 8> portDirections;
561 for (
auto &&port : ports) {
563 (isInput ? portIOTypes.first : portIOTypes.second).push_back(port.type);
564 (isInput ? portIONames.first : portIONames.second).push_back(port.name);
565 (isInput ? portIOAttributes.first : portIOAttributes.second)
566 .push_back(port.attributes);
568 auto portTypes =
concat(portIOTypes.first, portIOTypes.second);
569 auto portNames =
concat(portIONames.first, portIONames.second);
570 auto portAttributes =
concat(portIOAttributes.first, portIOAttributes.second);
573 auto functionType = builder.getFunctionType(portTypes, {});
575 result.addAttribute(CombComponentOp::getFunctionTypeAttrName(result.name),
578 result.addAttribute(ComponentOp::getFunctionTypeAttrName(result.name),
583 result.addAttribute(
"portNames", builder.getArrayAttr(portNames));
584 result.addAttribute(
"portDirections",
586 portIOTypes.first.size(),
587 portIOTypes.second.size()));
589 result.addAttribute(
"portAttributes", builder.getArrayAttr(portAttributes));
592 Region *region = result.addRegion();
593 Block *body =
new Block();
594 region->push_back(body);
597 body->addArguments(portTypes, SmallVector<Location, 4>(
598 portTypes.size(), builder.getUnknownLoc()));
601 IRRewriter::InsertionGuard guard(builder);
602 builder.setInsertionPointToStart(body);
603 builder.create<WiresOp>(result.location);
605 builder.create<ControlOp>(result.location);
616 template <
typename Op>
618 auto *body = op.getBodyBlock();
621 auto opIt = body->getOps<Op>().begin();
628 ArrayAttr portNames = op.getPortNames();
630 for (
size_t i = 0, e = portNames.size(); i != e; ++i) {
631 auto portName = cast<StringAttr>(portNames[i]);
632 if (portName.getValue() == name)
633 return op.getBodyBlock()->getArgument(i);
638 WiresOp calyx::ComponentOp::getWiresOp() {
639 return getControlOrWiresFrom<WiresOp>(*
this);
642 ControlOp calyx::ComponentOp::getControlOp() {
643 return getControlOrWiresFrom<ControlOp>(*
this);
646 Value calyx::ComponentOp::getGoPort() {
650 Value calyx::ComponentOp::getDonePort() {
654 Value calyx::ComponentOp::getClkPort() {
658 Value calyx::ComponentOp::getResetPort() {
663 auto portTypes = getArgumentTypes();
664 ArrayAttr portNamesAttr = getPortNames(), portAttrs = getPortAttributes();
665 APInt portDirectionsAttr = getPortDirections();
667 SmallVector<PortInfo> results;
668 for (
size_t i = 0, e = portNamesAttr.size(); i != e; ++i) {
669 results.push_back(
PortInfo{cast<StringAttr>(portNamesAttr[i]), portTypes[i],
671 cast<DictionaryAttr>(portAttrs[i])});
677 template <
typename Pred>
679 SmallVector<PortInfo> ports = op.getPortInfo();
680 llvm::erase_if(ports, p);
684 SmallVector<PortInfo> ComponentOp::getInputPortInfo() {
689 SmallVector<PortInfo> ComponentOp::getOutputPortInfo() {
694 void ComponentOp::print(OpAsmPrinter &p) {
695 printComponentInterface<ComponentOp>(p, *
this);
698 ParseResult ComponentOp::parse(OpAsmParser &parser, OperationState &result) {
699 return parseComponentInterface<ComponentOp>(parser, result);
705 llvm::SmallVector<StringRef, 4> identifiers;
706 for (
PortInfo &port : op.getPortInfo()) {
707 auto portIds = port.getAllIdentifiers();
708 identifiers.append(portIds.begin(), portIds.end());
711 std::sort(identifiers.begin(), identifiers.end());
716 std::set_intersection(interfacePorts.begin(), interfacePorts.end(),
717 identifiers.begin(), identifiers.end(),
723 SmallVector<StringRef, 4> difference;
724 std::set_difference(interfacePorts.begin(), interfacePorts.end(),
726 std::back_inserter(difference));
727 return op->emitOpError()
728 <<
"is missing the following required port attribute identifiers: "
736 if (std::distance(wIt.begin(), wIt.end()) +
737 std::distance(cIt.begin(), cIt.end()) !=
739 return emitOpError() <<
"requires exactly one of each: '"
740 << WiresOp::getOperationName() <<
"', '"
741 << ControlOp::getOperationName() <<
"'.";
748 bool hasNoControlConstructs =
749 getControlOp().getBodyBlock()->getOperations().empty();
750 bool hasNoAssignments =
751 getWiresOp().getBodyBlock()->getOps<AssignOp>().
empty();
752 if (hasNoControlConstructs && hasNoAssignments)
754 "The component currently does nothing. It needs to either have "
755 "continuous assignments in the Wires region or control constructs in "
756 "the Control region.");
761 void ComponentOp::build(OpBuilder &builder, OperationState &result,
762 StringAttr name, ArrayRef<PortInfo> ports) {
766 void ComponentOp::getAsmBlockArgumentNames(
770 auto ports = getPortNames();
771 auto *block = &getRegion()->front();
772 for (
size_t i = 0, e = block->getNumArguments(); i != e; ++i)
773 setNameFn(block->getArgument(i), cast<StringAttr>(ports[i]).getValue());
781 auto portTypes = getArgumentTypes();
782 ArrayAttr portNamesAttr = getPortNames(), portAttrs = getPortAttributes();
783 APInt portDirectionsAttr = getPortDirections();
785 SmallVector<PortInfo> results;
786 for (
size_t i = 0, e = portNamesAttr.size(); i != e; ++i) {
787 results.push_back(
PortInfo{cast<StringAttr>(portNamesAttr[i]), portTypes[i],
789 cast<DictionaryAttr>(portAttrs[i])});
794 WiresOp calyx::CombComponentOp::getWiresOp() {
796 auto opIt = body->getOps<WiresOp>().begin();
801 template <
typename Pred>
803 SmallVector<PortInfo> ports = op.getPortInfo();
804 llvm::erase_if(ports, p);
808 SmallVector<PortInfo> CombComponentOp::getInputPortInfo() {
813 SmallVector<PortInfo> CombComponentOp::getOutputPortInfo() {
818 void CombComponentOp::print(OpAsmPrinter &p) {
819 printComponentInterface<CombComponentOp>(p, *
this);
822 ParseResult CombComponentOp::parse(OpAsmParser &parser,
823 OperationState &result) {
824 return parseComponentInterface<CombComponentOp>(parser, result);
830 if (std::distance(wIt.begin(), wIt.end()) != 1)
831 return emitOpError() <<
"requires exactly one "
832 << WiresOp::getOperationName() <<
" op.";
836 if (std::distance(cIt.begin(), cIt.end()) != 0)
837 return emitOpError() <<
"must not have a `" << ControlOp::getOperationName()
841 bool hasNoAssignments =
842 getWiresOp().getBodyBlock()->getOps<AssignOp>().
empty();
843 if (hasNoAssignments)
845 "The component currently does nothing. It needs to either have "
846 "continuous assignments in the Wires region or control constructs in "
847 "the Control region.");
850 auto cells = getOps<CellInterface>();
851 for (
auto cell : cells) {
852 if (!cell.isCombinational())
853 return emitOpError() <<
"contains non-combinational cell "
854 << cell.instanceName();
858 auto groups = getWiresOp().getOps<GroupOp>();
860 return emitOpError() <<
"contains group " << (*groups.begin()).getSymName();
865 auto combGroups = getWiresOp().getOps<CombGroupOp>();
866 if (!combGroups.empty())
867 return emitOpError() <<
"contains comb group "
868 << (*combGroups.begin()).getSymName();
873 void CombComponentOp::build(OpBuilder &builder, OperationState &result,
874 StringAttr name, ArrayRef<PortInfo> ports) {
878 void CombComponentOp::getAsmBlockArgumentNames(
882 auto ports = getPortNames();
883 auto *block = &getRegion()->front();
884 for (
size_t i = 0, e = block->getNumArguments(); i != e; ++i)
885 setNameFn(block->getArgument(i), cast<StringAttr>(ports[i]).getValue());
894 SmallVector<InvokeOp, 4> ControlOp::getInvokeOps() {
895 SmallVector<InvokeOp, 4> ret;
896 this->walk([&](InvokeOp invokeOp) { ret.push_back(invokeOp); });
904 void SeqOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
905 MLIRContext *context) {
906 patterns.add(collapseControl<SeqOp>);
917 auto &ops = (*this).getBodyBlock()->getOperations();
918 if (!llvm::all_of(ops, [&](Operation &op) {
return isStaticControl(&op); })) {
919 return emitOpError(
"StaticSeqOp has non static control within it");
925 void StaticSeqOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
926 MLIRContext *context) {
927 patterns.add(collapseControl<StaticSeqOp>);
928 patterns.add(emptyControl<StaticSeqOp>);
941 for (EnableOp op :
getBodyBlock()->getOps<EnableOp>()) {
942 StringRef groupName = op.getGroupName();
943 if (groupNames.count(groupName))
944 return emitOpError() <<
"cannot enable the same group: \"" << groupName
945 <<
"\" more than once.";
946 groupNames.insert(groupName);
952 void ParOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
953 MLIRContext *context) {
954 patterns.add(collapseControl<ParOp>);
968 for (EnableOp op :
getBodyBlock()->getOps<EnableOp>()) {
969 StringRef groupName = op.getGroupName();
970 if (groupNames.count(groupName))
971 return emitOpError() <<
"cannot enable the same group: \"" << groupName
972 <<
"\" more than once.";
973 groupNames.insert(groupName);
977 auto &ops = (*this).getBodyBlock()->getOperations();
978 for (Operation &op : ops) {
980 return op.emitOpError(
"StaticParOp has non static control within it");
987 void StaticParOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
988 MLIRContext *context) {
989 patterns.add(collapseControl<StaticParOp>);
990 patterns.add(emptyControl<StaticParOp>);
998 auto componentInterface = (*this)->getParentOfType<ComponentInterface>();
999 if (llvm::isa<ComponentOp>(componentInterface)) {
1000 auto component = llvm::cast<ComponentOp>(componentInterface);
1001 auto control = component.getControlOp();
1005 if (!isa<GroupInterface>(op))
1007 auto group = cast<GroupInterface>(op);
1008 auto groupName = group.symName();
1009 if (mlir::SymbolTable::symbolKnownUseEmpty(groupName, control))
1010 return op.emitOpError()
1011 <<
"with name: " << groupName
1012 <<
" is unused in the control execution schedule";
1019 for (
auto thisAssignment :
getBodyBlock()->getOps<AssignOp>()) {
1023 if (thisAssignment.getGuard())
1026 Value dest = thisAssignment.getDest();
1027 for (Operation *user : dest.getUsers()) {
1028 auto assignUser = dyn_cast<AssignOp>(user);
1029 if (!assignUser || assignUser.getDest() != dest ||
1030 assignUser == thisAssignment)
1033 return user->emitOpError() <<
"destination is already continuously "
1034 "driven. Other assignment is "
1048 Operation *definingOp = value.getDefiningOp();
1049 if (definingOp ==
nullptr || definingOp->hasTrait<
Combinational>())
1055 if (isa<InstanceOp>(definingOp))
1059 if (isa<comb::CombDialect, hw::HWDialect>(definingOp->getDialect()))
1063 if (
auto r = dyn_cast<RegisterOp>(definingOp)) {
1064 return value == r.getOut()
1066 : group->emitOpError()
1067 <<
"with register: \"" << r.instanceName()
1068 <<
"\" is conducting a memory store. This is not "
1070 }
else if (
auto m = dyn_cast<MemoryOp>(definingOp)) {
1071 auto writePorts = {m.writeData(), m.writeEn()};
1072 return (llvm::none_of(
writePorts, [&](Value p) {
return p == value; }))
1074 : group->emitOpError()
1075 <<
"with memory: \"" << m.instanceName()
1076 <<
"\" is conducting a memory store. This "
1077 "is not combinational.";
1080 std::string portName =
1081 valueName(group->getParentOfType<ComponentOp>(), value);
1082 return group->emitOpError() <<
"with port: " << portName
1083 <<
". This operation is not combinational.";
1090 auto assign = dyn_cast<AssignOp>(op);
1091 if (assign ==
nullptr)
1093 Value dst = assign.getDest(), src = assign.getSrc();
1104 GroupGoOp GroupOp::getGoOp() {
1106 size_t nOps = std::distance(goOps.begin(), goOps.end());
1107 return nOps ? *goOps.begin() : GroupGoOp();
1110 GroupDoneOp GroupOp::getDoneOp() {
1112 return cast<GroupDoneOp>(body->getTerminator());
1118 void CycleOp::print(OpAsmPrinter &p) {
1121 auto start = this->getStart();
1122 auto end = this->getEnd();
1123 if (
end.has_value()) {
1124 p <<
"[" << start <<
":" <<
end.value() <<
"]";
1130 ParseResult CycleOp::parse(OpAsmParser &parser, OperationState &result) {
1131 SmallVector<OpAsmParser::UnresolvedOperand, 2> operandInfos;
1133 uint32_t startLiteral;
1134 uint32_t endLiteral;
1136 auto hasEnd = succeeded(parser.parseOptionalLSquare());
1138 if (parser.parseInteger(startLiteral)) {
1139 parser.emitError(parser.getNameLoc(),
"Could not parse start cycle");
1143 auto start = parser.getBuilder().getI32IntegerAttr(startLiteral);
1144 result.addAttribute(getStartAttrName(result.name), start);
1147 if (parser.parseColon())
1150 if (
auto res = parser.parseOptionalInteger(endLiteral); res.has_value()) {
1151 auto end = parser.getBuilder().getI32IntegerAttr(endLiteral);
1152 result.addAttribute(getEndAttrName(result.name), end);
1155 if (parser.parseRSquare())
1159 result.addTypes(parser.getBuilder().getI1Type());
1165 uint32_t latency = this->getGroupLatency();
1167 if (this->getStart() >= latency) {
1168 emitOpError(
"start cycle must be less than the group latency");
1172 if (this->getEnd().has_value()) {
1173 if (this->getStart() >= this->getEnd().value()) {
1174 emitOpError(
"start cycle must be less than end cycle");
1178 if (this->getEnd() >= latency) {
1179 emitOpError(
"end cycle must be less than the group latency");
1187 uint32_t CycleOp::getGroupLatency() {
1188 auto group = (*this)->getParentOfType<StaticGroupOp>();
1189 return group.getLatency();
1200 return llvm::any_of(port.getUses(), [&](
auto &&use) {
1201 auto assignOp = dyn_cast<AssignOp>(use.getOwner());
1202 if (assignOp == nullptr)
1205 Operation *parent = assignOp->getParentOp();
1206 if (isa<WiresOp>(parent))
1215 Value expected = isDriven ? assignOp.getDest() : assignOp.getSrc();
1216 return expected == port && group == parent;
1228 if (
auto cell = dyn_cast<CellInterface>(port.getDefiningOp());
1230 return groupOp.drivesAnyPort(cell.getInputPorts());
1235 LogicalResult GroupOp::drivesPort(Value port) {
1239 LogicalResult CombGroupOp::drivesPort(Value port) {
1243 LogicalResult StaticGroupOp::drivesPort(Value port) {
1250 return success(llvm::all_of(ports, [&](Value port) {
1255 LogicalResult GroupOp::drivesAllPorts(ValueRange ports) {
1259 LogicalResult CombGroupOp::drivesAllPorts(ValueRange ports) {
1263 LogicalResult StaticGroupOp::drivesAllPorts(ValueRange ports) {
1270 return success(llvm::any_of(ports, [&](Value port) {
1275 LogicalResult GroupOp::drivesAnyPort(ValueRange ports) {
1279 LogicalResult CombGroupOp::drivesAnyPort(ValueRange ports) {
1283 LogicalResult StaticGroupOp::drivesAnyPort(ValueRange ports) {
1290 return success(llvm::any_of(ports, [&](Value port) {
1295 LogicalResult GroupOp::readsAnyPort(ValueRange ports) {
1299 LogicalResult CombGroupOp::readsAnyPort(ValueRange ports) {
1303 LogicalResult StaticGroupOp::readsAnyPort(ValueRange ports) {
1310 GroupInterface group) {
1311 Operation *destDefiningOp = assign.getDest().getDefiningOp();
1312 if (destDefiningOp ==
nullptr)
1314 auto destCell = dyn_cast<CellInterface>(destDefiningOp);
1315 if (destCell ==
nullptr)
1318 LogicalResult verifyWrites =
1319 TypeSwitch<Operation *, LogicalResult>(destCell)
1320 .Case<RegisterOp>([&](
auto op) {
1323 return succeeded(group.drivesAnyPort({op.getWriteEn(), op.getIn()}))
1324 ? group.drivesAllPorts({op.getWriteEn(), op.getIn()})
1327 .Case<MemoryOp>([&](
auto op) {
1328 SmallVector<Value> requiredWritePorts;
1331 requiredWritePorts.push_back(op.writeEn());
1332 requiredWritePorts.push_back(op.writeData());
1333 for (Value address : op.addrPorts())
1334 requiredWritePorts.push_back(address);
1339 group.drivesAnyPort({op.writeData(), op.writeEn()}))
1340 ? group.drivesAllPorts(requiredWritePorts)
1343 .Case<AndLibOp, OrLibOp, XorLibOp, AddLibOp, SubLibOp, GtLibOp,
1344 LtLibOp, EqLibOp, NeqLibOp, GeLibOp, LeLibOp, LshLibOp,
1345 RshLibOp, SgtLibOp, SltLibOp, SeqLibOp, SneqLibOp, SgeLibOp,
1346 SleLibOp, SrshLibOp>([&](
auto op) {
1347 Value lhs = op.getLeft(), rhs = op.getRight();
1348 return succeeded(group.drivesAnyPort({lhs, rhs}))
1349 ? group.drivesAllPorts({lhs, rhs})
1352 .Default([&](
auto op) {
return success(); });
1354 if (failed(verifyWrites))
1355 return group->emitOpError()
1356 <<
"with cell: " << destCell->getName() <<
" \""
1357 << destCell.instanceName()
1358 <<
"\" is performing a write and failed to drive all necessary "
1361 Operation *srcDefiningOp = assign.getSrc().getDefiningOp();
1362 if (srcDefiningOp ==
nullptr)
1364 auto srcCell = dyn_cast<CellInterface>(srcDefiningOp);
1365 if (srcCell ==
nullptr)
1368 LogicalResult verifyReads =
1369 TypeSwitch<Operation *, LogicalResult>(srcCell)
1370 .Case<MemoryOp>([&](
auto op) {
1374 return succeeded(group.readsAnyPort({op.readData()}))
1375 ? group.drivesAllPorts(op.addrPorts())
1378 .Default([&](
auto op) {
return success(); });
1380 if (failed(verifyReads))
1381 return group->emitOpError() <<
"with cell: " << srcCell->getName() <<
" \""
1382 << srcCell.instanceName()
1383 <<
"\" is having a read performed upon it, and "
1384 "failed to drive all necessary ports.";
1390 auto group = dyn_cast<GroupInterface>(op);
1391 if (group ==
nullptr)
1394 for (
auto &&groupOp : *group.getBody()) {
1395 auto assign = dyn_cast<AssignOp>(groupOp);
1396 if (assign ==
nullptr)
1412 ArrayRef<StringRef> portNames) {
1413 auto cellInterface = dyn_cast<CellInterface>(op);
1414 assert(cellInterface &&
"must implement the Cell interface");
1416 std::string prefix = cellInterface.instanceName().str() +
".";
1417 for (
size_t i = 0, e = portNames.size(); i != e; ++i)
1418 setNameFn(op->getResult(i), prefix + portNames[i].str());
1429 bool isDestination) {
1430 Operation *definingOp = value.getDefiningOp();
1431 bool isComponentPort = isa<BlockArgument>(value),
1432 isCellInterfacePort = isa_and_nonnull<CellInterface>(definingOp);
1433 assert((isComponentPort || isCellInterfacePort) &&
"Not a port.");
1437 : cast<CellInterface>(definingOp).portInfo(value);
1439 bool isSource = !isDestination;
1442 (isDestination && isComponentPort) || (isSource && isCellInterfacePort)
1449 <<
"has a " << (isComponentPort ?
"component" :
"cell")
1451 << (isDestination ?
"destination" :
"source")
1452 <<
" with the incorrect direction.";
1459 bool isSource = !isDestination;
1460 Value value = isDestination ? op.getDest() : op.getSrc();
1465 if (isDestination && !isa<GroupGoOp, GroupDoneOp>(value.getDefiningOp()))
1466 return op->emitOpError(
1467 "has an invalid destination port. It must be drive-able.");
1475 bool isDestination =
true, isSource =
false;
1484 ParseResult AssignOp::parse(OpAsmParser &parser, OperationState &result) {
1485 OpAsmParser::UnresolvedOperand destination;
1486 if (parser.parseOperand(destination) || parser.parseEqual())
1492 OpAsmParser::UnresolvedOperand guardOrSource;
1493 if (parser.parseOperand(guardOrSource))
1498 OpAsmParser::UnresolvedOperand source;
1499 bool hasGuard = succeeded(parser.parseOptionalQuestion());
1502 if (parser.parseOperand(source))
1507 if (parser.parseColonType(type) ||
1508 parser.resolveOperand(destination, type, result.operands))
1512 Type i1Type = parser.getBuilder().getI1Type();
1515 if (parser.resolveOperand(source, type, result.operands) ||
1516 parser.resolveOperand(guardOrSource, i1Type, result.operands))
1520 if (parser.resolveOperand(guardOrSource, type, result.operands))
1527 void AssignOp::print(OpAsmPrinter &p) {
1528 p <<
" " << getDest() <<
" = ";
1530 Value bguard = getGuard(), source = getSrc();
1533 p << bguard <<
" ? ";
1537 p << source <<
" : " << source.getType();
1546 ComponentInterface InstanceOp::getReferencedComponent() {
1547 auto module = (*this)->getParentOfType<ModuleOp>();
1551 return module.lookupSymbol<ComponentInterface>(getComponentName());
1557 static LogicalResult
1559 ComponentInterface referencedComponent) {
1560 auto module = instance->getParentOfType<ModuleOp>();
1561 StringRef entryPointName =
1562 module->getAttrOfType<StringAttr>(
"calyx.entrypoint");
1563 if (instance.getComponentName() == entryPointName)
1564 return instance.emitOpError()
1565 <<
"cannot reference the entry-point component: '" << entryPointName
1569 SmallVector<PortInfo> componentPorts = referencedComponent.getPortInfo();
1570 size_t numPorts = componentPorts.size();
1572 size_t numResults = instance.getNumResults();
1573 if (numResults != numPorts)
1574 return instance.emitOpError()
1575 <<
"has a wrong number of results; expected: " << numPorts
1576 <<
" but got " << numResults;
1578 for (
size_t i = 0; i != numResults; ++i) {
1579 auto resultType = instance.getResult(i).getType();
1580 auto expectedType = componentPorts[i].type;
1581 if (resultType == expectedType)
1583 return instance.emitOpError()
1584 <<
"result type for " << componentPorts[i].name <<
" must be "
1585 << expectedType <<
", but got " << resultType;
1590 LogicalResult InstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1591 Operation *op = *
this;
1592 auto module = op->getParentOfType<ModuleOp>();
1593 Operation *referencedComponent =
1594 symbolTable.lookupNearestSymbolFrom(module, getComponentNameAttr());
1595 if (referencedComponent ==
nullptr)
1596 return emitError() <<
"referencing component: '" << getComponentName()
1597 <<
"', which does not exist.";
1599 Operation *shadowedComponentName =
1600 symbolTable.lookupNearestSymbolFrom(module, getSymNameAttr());
1601 if (shadowedComponentName !=
nullptr)
1602 return emitError() <<
"instance symbol: '" << instanceName()
1603 <<
"' is already a symbol for another component.";
1606 auto parentComponent = op->getParentOfType<ComponentOp>();
1607 if (parentComponent == referencedComponent)
1608 return emitError() <<
"recursive instantiation of its parent component: '"
1609 << getComponentName() <<
"'";
1611 assert(isa<ComponentInterface>(referencedComponent) &&
1612 "Should be a ComponentInterface.");
1614 cast<ComponentInterface>(referencedComponent));
1622 SmallVector<StringRef> InstanceOp::portNames() {
1623 SmallVector<StringRef> portNames;
1624 for (Attribute name : getReferencedComponent().getPortNames())
1625 portNames.push_back(cast<StringAttr>(name).getValue());
1629 SmallVector<Direction> InstanceOp::portDirections() {
1630 SmallVector<Direction> portDirections;
1632 portDirections.push_back(port.direction);
1633 return portDirections;
1636 SmallVector<DictionaryAttr> InstanceOp::portAttributes() {
1637 SmallVector<DictionaryAttr> portAttributes;
1639 portAttributes.push_back(port.attributes);
1640 return portAttributes;
1644 return isa<CombComponentOp>(getReferencedComponent());
1654 auto module = (*this)->getParentOfType<ModuleOp>();
1664 static LogicalResult
1667 auto module = instance->getParentOfType<ModuleOp>();
1668 StringRef entryPointName =
1669 module->getAttrOfType<StringAttr>(
"calyx.entrypoint");
1670 if (instance.getPrimitiveName() == entryPointName)
1671 return instance.emitOpError()
1672 <<
"cannot reference the entry-point component: '" << entryPointName
1676 auto primitivePorts = referencedPrimitive.getPortList();
1677 size_t numPorts = primitivePorts.size();
1679 size_t numResults = instance.getNumResults();
1680 if (numResults != numPorts)
1681 return instance.emitOpError()
1682 <<
"has a wrong number of results; expected: " << numPorts
1683 <<
" but got " << numResults;
1686 ArrayAttr modParameters = referencedPrimitive.getParameters();
1687 ArrayAttr parameters = instance.getParameters().value_or(ArrayAttr());
1688 size_t numExpected = modParameters.size();
1689 size_t numParams = parameters.size();
1690 if (numParams != numExpected)
1691 return instance.emitOpError()
1692 <<
"has the wrong number of parameters; expected: " << numExpected
1693 <<
" but got " << numParams;
1695 for (
size_t i = 0; i != numExpected; ++i) {
1696 auto param = cast<circt::hw::ParamDeclAttr>(parameters[i]);
1697 auto modParam = cast<circt::hw::ParamDeclAttr>(modParameters[i]);
1699 auto paramName = param.getName();
1700 if (paramName != modParam.getName())
1701 return instance.emitOpError()
1702 <<
"parameter #" << i <<
" should have name " << modParam.getName()
1703 <<
" but has name " << paramName;
1705 if (param.getType() != modParam.getType())
1706 return instance.emitOpError()
1707 <<
"parameter " << paramName <<
" should have type "
1708 << modParam.getType() <<
" but has type " << param.getType();
1712 if (!param.getValue())
1713 return instance.emitOpError(
"parameter ")
1714 << paramName <<
" must have a value";
1717 for (
size_t i = 0; i != numResults; ++i) {
1718 auto resultType = instance.getResult(i).getType();
1719 auto expectedType = primitivePorts[i].type;
1721 instance.getLoc(), instance.getParametersAttr(), expectedType);
1722 if (failed(replacedType))
1724 if (resultType == replacedType)
1726 return instance.emitOpError()
1727 <<
"result type for " << primitivePorts[i].name <<
" must be "
1728 << expectedType <<
", but got " << resultType;
1734 PrimitiveOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1735 Operation *op = *
this;
1736 auto module = op->getParentOfType<ModuleOp>();
1737 Operation *referencedPrimitive =
1738 symbolTable.lookupNearestSymbolFrom(module, getPrimitiveNameAttr());
1739 if (referencedPrimitive ==
nullptr)
1740 return emitError() <<
"referencing primitive: '" << getPrimitiveName()
1741 <<
"', which does not exist.";
1743 Operation *shadowedPrimitiveName =
1744 symbolTable.lookupNearestSymbolFrom(module, getSymNameAttr());
1745 if (shadowedPrimitiveName !=
nullptr)
1746 return emitError() <<
"instance symbol: '" << instanceName()
1747 <<
"' is already a symbol for another primitive.";
1751 if (parentPrimitive == referencedPrimitive)
1752 return emitError() <<
"recursive instantiation of its parent primitive: '"
1753 << getPrimitiveName() <<
"'";
1755 assert(isa<hw::HWModuleExternOp>(referencedPrimitive) &&
1756 "Should be a HardwareModuleExternOp.");
1759 cast<hw::HWModuleExternOp>(referencedPrimitive));
1767 SmallVector<StringRef> PrimitiveOp::portNames() {
1768 SmallVector<StringRef> portNames;
1769 auto ports = getReferencedPrimitive().getPortList();
1770 for (
auto port : ports)
1771 portNames.push_back(port.name.getValue());
1777 switch (direction) {
1783 llvm_unreachable(
"InOut ports not supported by Calyx");
1785 llvm_unreachable(
"Impossible port type");
1788 SmallVector<Direction> PrimitiveOp::portDirections() {
1789 SmallVector<Direction> portDirections;
1790 auto ports = getReferencedPrimitive().getPortList();
1791 for (hw::PortInfo port : ports)
1793 return portDirections;
1802 DictionaryAttr dict) {
1806 llvm::SmallVector<NamedAttribute> attrs;
1807 for (NamedAttribute attr : dict) {
1808 Dialect *dialect = attr.getNameDialect();
1809 if (dialect ==
nullptr || !isa<CalyxDialect>(*dialect))
1811 StringRef name = attr.getName().strref();
1812 StringAttr newName = builder.getStringAttr(std::get<1>(name.split(
".")));
1813 attr.setName(newName);
1814 attrs.push_back(attr);
1816 return builder.getDictionaryAttr(attrs);
1820 SmallVector<DictionaryAttr> PrimitiveOp::portAttributes() {
1821 SmallVector<DictionaryAttr> portAttributes;
1822 OpBuilder builder(getContext());
1824 auto argAttrs = prim.getAllInputAttrs();
1825 auto resAttrs = prim.getAllOutputAttrs();
1826 for (
auto a : argAttrs)
1827 portAttributes.push_back(
1829 for (
auto a : resAttrs)
1830 portAttributes.push_back(
1832 return portAttributes;
1841 SmallVector<Attribute> ¶meters) {
1843 return parser.parseCommaSeparatedList(
1844 OpAsmParser::Delimiter::OptionalLessGreater, [&]() {
1849 if (parser.parseKeywordOrString(&name) || parser.parseColonType(type))
1853 if (succeeded(parser.parseOptionalEqual())) {
1854 if (parser.parseAttribute(value, type))
1858 auto &builder = parser.getBuilder();
1860 builder.getContext(), builder.getStringAttr(name), type, value));
1867 ArrayAttr ¶meters) {
1868 SmallVector<Attribute> parseParameters;
1872 parameters =
ArrayAttr::get(parser.getContext(), parseParameters);
1879 ArrayAttr parameters) {
1880 if (parameters.empty())
1884 llvm::interleaveComma(parameters, p, [&](Attribute param) {
1885 auto paramAttr = cast<hw::ParamDeclAttr>(param);
1886 p << paramAttr.getName().getValue() <<
": " << paramAttr.getType();
1887 if (
auto value = paramAttr.getValue()) {
1889 p.printAttributeWithoutType(value);
1895 //===----------------------------------------------------------------------===//
1897 //===----------------------------------------------------------------------===//
1899 LogicalResult GroupGoOp::verify() { return verifyNotComplexSource(*this); }
1902 void GroupGoOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1903 auto parent = (*this)->getParentOfType<GroupOp>();
1904 StringRef name = parent.getSymName();
1905 std::string resultName = name.str() + ".go";
1906 setNameFn(getResult(), resultName);
1909 void GroupGoOp::print(OpAsmPrinter &p) { printGroupPort(p, *this); }
1911 ParseResult GroupGoOp::parse(OpAsmParser &parser, OperationState &result) {
1912 if (parseGroupPort(parser, result))
1915 result.addTypes(parser.getBuilder().getI1Type());
1919 //===----------------------------------------------------------------------===//
1921 //===----------------------------------------------------------------------===//
1923 LogicalResult GroupDoneOp::verify() {
1924 Operation *srcOp = getSrc().getDefiningOp();
1925 Value optionalGuard = getGuard();
1926 Operation *guardOp = optionalGuard ? optionalGuard.getDefiningOp() : nullptr;
1927 bool noGuard = (guardOp == nullptr);
1929 if (srcOp == nullptr)
1930 // This is a port of the parent component.
1933 if (isa<hw::ConstantOp>(srcOp) && (noGuard || isa<hw::ConstantOp>(guardOp)))
1934 return emitOpError() << "with constant source"
1935 << (noGuard ? "" : " and constant guard")
1936 << ". This should be a combinational group.";
1938 return verifyNotComplexSource(*this);
1941 void GroupDoneOp::print(OpAsmPrinter &p) { printGroupPort(p, *this); }
1943 ParseResult GroupDoneOp::parse(OpAsmParser &parser, OperationState &result) {
1944 return parseGroupPort(parser, result);
1947 //===----------------------------------------------------------------------===//
1949 //===----------------------------------------------------------------------===//
1952 void RegisterOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1953 getCellAsmResultNames(setNameFn, *this, this->portNames());
1956 SmallVector<StringRef> RegisterOp::portNames() {
1957 return {"in", "write_en", clkPort, resetPort, "out", donePort};
1960 SmallVector<Direction> RegisterOp::portDirections() {
1961 return {Input, Input, Input, Input, Output, Output};
1964 SmallVector<DictionaryAttr> RegisterOp::portAttributes() {
1965 MLIRContext *context = getContext();
1966 IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1);
1967 NamedAttrList writeEn, clk, reset, done;
1968 writeEn.append(goPort, isSet);
1969 clk.append(clkPort, isSet);
1970 reset.append(resetPort, isSet);
1971 done.append(donePort, isSet);
1973 DictionaryAttr::get(context), // In
1974 writeEn.getDictionary(context), // Write enable
1975 clk.getDictionary(context), // Clk
1976 reset.getDictionary(context), // Reset
1977 DictionaryAttr::get(context), // Out
1978 done.getDictionary(context) // Done
1982 bool RegisterOp::isCombinational() { return false; }
1984 //===----------------------------------------------------------------------===//
1986 //===----------------------------------------------------------------------===//
1989 void MemoryOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1990 getCellAsmResultNames(setNameFn, *this, this->portNames());
1993 SmallVector<StringRef> MemoryOp::portNames() {
1994 SmallVector<StringRef> portNames;
1995 for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i) {
1997 StringAttr::get(this->getContext(), "addr" + std::to_string(i));
1998 portNames.push_back(nameAttr.getValue());
2000 portNames.append({"write_data", "write_en", clkPort, "read_data", donePort});
2004 SmallVector<Direction> MemoryOp::portDirections() {
2005 SmallVector<Direction> portDirections;
2006 for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2007 portDirections.push_back(Input);
2008 portDirections.append({Input, Input, Input, Output, Output});
2009 return portDirections;
2012 SmallVector<DictionaryAttr> MemoryOp::portAttributes() {
2013 SmallVector<DictionaryAttr> portAttributes;
2014 MLIRContext *context = getContext();
2015 for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2016 portAttributes.push_back(DictionaryAttr::get(context)); // Addresses
2018 // Use a boolean to indicate this attribute is used.
2019 IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1);
2020 NamedAttrList writeEn, clk, reset, done;
2021 writeEn.append(goPort, isSet);
2022 clk.append(clkPort, isSet);
2023 done.append(donePort, isSet);
2024 portAttributes.append({DictionaryAttr::get(context), // In
2025 writeEn.getDictionary(context), // Write enable
2026 clk.getDictionary(context), // Clk
2027 DictionaryAttr::get(context), // Out
2028 done.getDictionary(context)} // Done
2030 return portAttributes;
2033 void MemoryOp::build(OpBuilder &builder, OperationState &state,
2034 StringRef instanceName, int64_t width,
2035 ArrayRef<int64_t> sizes, ArrayRef<int64_t> addrSizes) {
2036 state.addAttribute(SymbolTable::getSymbolAttrName(),
2037 builder.getStringAttr(instanceName));
2038 state.addAttribute("width", builder.getI64IntegerAttr(width));
2039 state.addAttribute("sizes", builder.getI64ArrayAttr(sizes));
2040 state.addAttribute("addrSizes", builder.getI64ArrayAttr(addrSizes));
2041 SmallVector<Type> types;
2042 for (int64_t size : addrSizes)
2043 types.push_back(builder.getIntegerType(size)); // Addresses
2044 types.push_back(builder.getIntegerType(width)); // Write data
2045 types.push_back(builder.getI1Type()); // Write enable
2046 types.push_back(builder.getI1Type()); // Clk
2047 types.push_back(builder.getIntegerType(width)); // Read data
2048 types.push_back(builder.getI1Type()); // Done
2049 state.addTypes(types);
2052 LogicalResult MemoryOp::verify() {
2053 ArrayRef<Attribute> opSizes = getSizes().getValue();
2054 ArrayRef<Attribute> opAddrSizes = getAddrSizes().getValue();
2055 size_t numDims = getSizes().size();
2056 size_t numAddrs = getAddrSizes().size();
2057 if (numDims != numAddrs)
2058 return emitOpError("mismatched number of dimensions (")
2059 << numDims << ") and address sizes (" << numAddrs << ")";
2061 size_t numExtraPorts = 5; // write data/enable, clk, and read data/done.
2062 if (getNumResults() != numAddrs + numExtraPorts)
2063 return emitOpError("incorrect number of address ports, expected ")
2066 for (size_t i = 0; i < numDims; ++i) {
2067 int64_t size = cast<IntegerAttr>(opSizes[i]).getInt();
2068 int64_t addrSize = cast<IntegerAttr>(opAddrSizes[i]).getInt();
2069 if (llvm::Log2_64_Ceil(size) > addrSize)
2070 return emitOpError("address size (")
2071 << addrSize << ") for dimension " << i
2072 << " can't address the entire range (
" << size << ")
";
2078 //===----------------------------------------------------------------------===//
2080 //===----------------------------------------------------------------------===//
2083 void SeqMemoryOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
2084 getCellAsmResultNames(setNameFn, *this, this->portNames());
2087 SmallVector<StringRef> SeqMemoryOp::portNames() {
2088 SmallVector<StringRef> portNames;
2089 for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i) {
2091 StringAttr::get(this->getContext(), "addr
" + std::to_string(i));
2092 portNames.push_back(nameAttr.getValue());
2094 portNames.append({clkPort, "reset
", "content_en
", "write_en
", "write_data
",
2095 "read_data
", "done
"});
2099 SmallVector<Direction> SeqMemoryOp::portDirections() {
2100 SmallVector<Direction> portDirections;
2101 for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2102 portDirections.push_back(Input);
2103 portDirections.append({Input, Input, Input, Input, Input, Output, Output});
2104 return portDirections;
2107 SmallVector<DictionaryAttr> SeqMemoryOp::portAttributes() {
2108 SmallVector<DictionaryAttr> portAttributes;
2109 MLIRContext *context = getContext();
2110 for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2111 portAttributes.push_back(DictionaryAttr::get(context)); // Addresses
2113 OpBuilder builder(context);
2114 // Use a boolean to indicate this attribute is used.
2115 IntegerAttr isSet = IntegerAttr::get(builder.getIndexType(), 1);
2116 IntegerAttr isTwo = IntegerAttr::get(builder.getIndexType(), 2);
2117 NamedAttrList done, clk, reset, contentEn;
2118 done.append(donePort, isSet);
2119 clk.append(clkPort, isSet);
2120 clk.append(resetPort, isSet);
2121 contentEn.append(goPort, isTwo);
2122 portAttributes.append({clk.getDictionary(context), // Clk
2123 reset.getDictionary(context), // Reset
2124 contentEn.getDictionary(context), // Content enable
2125 DictionaryAttr::get(context), // Write enable
2126 DictionaryAttr::get(context), // Write data
2127 DictionaryAttr::get(context), // Read data
2128 done.getDictionary(context)} // Done
2130 return portAttributes;
2133 void SeqMemoryOp::build(OpBuilder &builder, OperationState &state,
2134 StringRef instanceName, int64_t width,
2135 ArrayRef<int64_t> sizes, ArrayRef<int64_t> addrSizes) {
2136 state.addAttribute(SymbolTable::getSymbolAttrName(),
2137 builder.getStringAttr(instanceName));
2138 state.addAttribute("width", builder.getI64IntegerAttr(width));
2139 state.addAttribute("sizes
", builder.getI64ArrayAttr(sizes));
2140 state.addAttribute("addrSizes
", builder.getI64ArrayAttr(addrSizes));
2141 SmallVector<Type> types;
2142 for (int64_t size : addrSizes)
2143 types.push_back(builder.getIntegerType(size)); // Addresses
2144 types.push_back(builder.getI1Type()); // Clk
2145 types.push_back(builder.getI1Type()); // Reset
2146 types.push_back(builder.getI1Type()); // Content enable
2147 types.push_back(builder.getI1Type()); // Write enable
2148 types.push_back(builder.getIntegerType(width)); // Write data
2149 types.push_back(builder.getIntegerType(width)); // Read data
2150 types.push_back(builder.getI1Type()); // Done
2151 state.addTypes(types);
2154 LogicalResult SeqMemoryOp::verify() {
2155 ArrayRef<Attribute> opSizes = getSizes().getValue();
2156 ArrayRef<Attribute> opAddrSizes = getAddrSizes().getValue();
2157 size_t numDims = getSizes().size();
2158 size_t numAddrs = getAddrSizes().size();
2159 if (numDims != numAddrs)
2160 return emitOpError("mismatched number of dimensions (
")
2161 << numDims << ") and address sizes (
" << numAddrs << ")
";
2163 size_t numExtraPorts =
2164 7; // write data/enable, clk, reset, read data, content enable, and done.
2165 if (getNumResults() != numAddrs + numExtraPorts)
2166 return emitOpError("incorrect number of address ports, expected
")
2169 for (size_t i = 0; i < numDims; ++i) {
2170 int64_t size = cast<IntegerAttr>(opSizes[i]).getInt();
2171 int64_t addrSize = cast<IntegerAttr>(opAddrSizes[i]).getInt();
2172 if (llvm::Log2_64_Ceil(size) > addrSize)
2173 return emitOpError("address size (
")
2174 << addrSize << ")
for dimension
" << i
2175 << " can
't address the entire range (" << size << ")";
2181 //===----------------------------------------------------------------------===//
2183 //===----------------------------------------------------------------------===//
2184 LogicalResult EnableOp::verify() {
2185 auto component = (*this)->getParentOfType<ComponentOp>();
2186 auto wiresOp = component.getWiresOp();
2187 StringRef name = getGroupName();
2189 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(name);
2191 return emitOpError() << "with group '" << name
2192 << "', which does not exist.";
2194 if (isa<CombGroupOp>(groupOp))
2195 return emitOpError() << "with group '" << name
2196 << "', which is a combinational group.";
2201 //===----------------------------------------------------------------------===//
2203 //===----------------------------------------------------------------------===//
2205 LogicalResult IfOp::verify() {
2206 std::optional<StringRef> optGroupName = getGroupName();
2207 if (!optGroupName) {
2208 // No combinational group was provided.
2211 auto component = (*this)->getParentOfType<ComponentOp>();
2212 WiresOp wiresOp = component.getWiresOp();
2213 StringRef groupName = *optGroupName;
2214 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(groupName);
2216 return emitOpError() << "with group '" << groupName
2217 << "', which does not exist.";
2219 if (isa<GroupOp>(groupOp))
2220 return emitOpError() << "with group '" << groupName
2221 << "', which is not a combinational group.";
2223 if (failed(groupOp.drivesPort(getCond())))
2224 return emitError() << "with conditional op: '"
2225 << valueName(component, getCond())
2226 << "' expected to be driven from group: '" << groupName
2227 << "' but no driver was found.";
2235 template <typename OpTy>
2236 static std::optional<EnableOp> getLastEnableOp(OpTy parent) {
2237 static_assert(IsAny<OpTy, SeqOp, StaticSeqOp>(),
2238 "Should be a StaticSeqOp or SeqOp.");
2239 auto &lastOp = parent.getBodyBlock()->back();
2240 if (auto enableOp = dyn_cast<EnableOp>(lastOp))
2242 if (auto seqOp = dyn_cast<SeqOp>(lastOp))
2243 return getLastEnableOp(seqOp);
2244 if (auto staticSeqOp = dyn_cast<StaticSeqOp>(lastOp))
2245 return getLastEnableOp(staticSeqOp);
2247 return std::nullopt;
2252 template <typename OpTy>
2253 static llvm::StringMap<EnableOp> getAllEnableOpsInImmediateBody(OpTy parent) {
2254 static_assert(IsAny<OpTy, ParOp, StaticParOp>(),
2255 "Should be a StaticParOp or ParOp.");
2257 llvm::StringMap<EnableOp> enables;
2258 Block *body = parent.getBodyBlock();
2259 for (EnableOp op : body->getOps<EnableOp>())
2260 enables.insert(std::pair(op.getGroupName(), op));
2272 template <typename IfOpTy, typename TailOpTy>
2273 static bool hasCommonTailPatternPreConditions(IfOpTy op) {
2274 static_assert(IsAny<TailOpTy, SeqOp, ParOp, StaticSeqOp, StaticParOp>(),
2275 "Should be a SeqOp, ParOp, StaticSeqOp, or StaticParOp.");
2276 static_assert(IsAny<IfOpTy, IfOp, StaticIfOp>(),
2277 "Should be a IfOp or StaticIfOp.");
2279 if (!op.thenBodyExists() || !op.elseBodyExists())
2281 if (op.getThenBody()->empty() || op.getElseBody()->empty())
2284 Block *thenBody = op.getThenBody(), *elseBody = op.getElseBody();
2285 return isa<TailOpTy>(thenBody->front()) && isa<TailOpTy>(elseBody->front());
2296 template <typename IfOpTy, typename SeqOpTy>
2297 static LogicalResult commonTailPatternWithSeq(IfOpTy ifOp,
2298 PatternRewriter &rewriter) {
2299 static_assert(IsAny<IfOpTy, IfOp, StaticIfOp>(),
2300 "Should be an IfOp or StaticIfOp.");
2301 static_assert(IsAny<SeqOpTy, SeqOp, StaticSeqOp>(),
2302 "Branches should be checking for an SeqOp or StaticSeqOp");
2303 if (!hasCommonTailPatternPreConditions<IfOpTy, SeqOpTy>(ifOp))
2305 auto thenControl = cast<SeqOpTy>(ifOp.getThenBody()->front()),
2306 elseControl = cast<SeqOpTy>(ifOp.getElseBody()->front());
2308 std::optional<EnableOp> lastThenEnableOp = getLastEnableOp(thenControl),
2309 lastElseEnableOp = getLastEnableOp(elseControl);
2311 if (!lastThenEnableOp || !lastElseEnableOp)
2313 if (lastThenEnableOp->getGroupName() != lastElseEnableOp->getGroupName())
2316 // Place the IfOp and pulled EnableOp inside a sequential region, in case
2317 // this IfOp is nested in a ParOp. This avoids unintentionally
2318 // parallelizing the pulled out EnableOps.
2319 rewriter.setInsertionPointAfter(ifOp);
2320 SeqOpTy seqOp = rewriter.create<SeqOpTy>(ifOp.getLoc());
2321 Block *body = seqOp.getBodyBlock();
2323 body->push_back(ifOp);
2324 rewriter.setInsertionPointToEnd(body);
2325 rewriter.create<EnableOp>(seqOp.getLoc(), lastThenEnableOp->getGroupName());
2327 // Erase the common EnableOp from the Then and Else regions.
2328 rewriter.eraseOp(*lastThenEnableOp);
2329 rewriter.eraseOp(*lastElseEnableOp);
2346 template <typename OpTy, typename ParOpTy>
2347 static LogicalResult commonTailPatternWithPar(OpTy controlOp,
2348 PatternRewriter &rewriter) {
2349 static_assert(IsAny<OpTy, IfOp, StaticIfOp>(),
2350 "Should be an IfOp or StaticIfOp.");
2351 static_assert(IsAny<ParOpTy, ParOp, StaticParOp>(),
2352 "Branches should be checking for an ParOp or StaticParOp");
2353 if (!hasCommonTailPatternPreConditions<OpTy, ParOpTy>(controlOp))
2355 auto thenControl = cast<ParOpTy>(controlOp.getThenBody()->front()),
2356 elseControl = cast<ParOpTy>(controlOp.getElseBody()->front());
2358 llvm::StringMap<EnableOp> a = getAllEnableOpsInImmediateBody(thenControl),
2359 b = getAllEnableOpsInImmediateBody(elseControl);
2360 // Compute the intersection between `A` and `B`.
2361 SmallVector<StringRef> groupNames;
2362 for (auto aIndex = a.begin(); aIndex != a.end(); ++aIndex) {
2363 StringRef groupName = aIndex->getKey();
2364 auto bIndex = b.find(groupName);
2365 if (bIndex == b.end())
2367 // This is also an element in B.
2368 groupNames.push_back(groupName);
2369 // Since these are being pulled out, erase them.
2370 rewriter.eraseOp(aIndex->getValue());
2371 rewriter.eraseOp(bIndex->getValue());
2374 // Place the IfOp and EnableOp(s) inside a parallel region, in case this
2375 // IfOp is nested in a SeqOp. This avoids unintentionally sequentializing
2376 // the pulled out EnableOps.
2377 rewriter.setInsertionPointAfter(controlOp);
2379 ParOpTy parOp = rewriter.create<ParOpTy>(controlOp.getLoc());
2380 Block *body = parOp.getBodyBlock();
2381 controlOp->remove();
2382 body->push_back(controlOp);
2383 // Pull out the intersection between these two sets, and erase their
2384 // counterparts in the Then and Else regions.
2385 rewriter.setInsertionPointToEnd(body);
2386 for (StringRef groupName : groupNames)
2387 rewriter.create<EnableOp>(parOp.getLoc(), groupName);
2395 struct EmptyIfBody : mlir::OpRewritePattern<IfOp> {
2396 using mlir::OpRewritePattern<IfOp>::OpRewritePattern;
2397 LogicalResult matchAndRewrite(IfOp ifOp,
2398 PatternRewriter &rewriter) const override {
2399 if (!ifOp.getThenBody()->empty())
2401 if (ifOp.elseBodyExists() && !ifOp.getElseBody()->empty())
2404 eraseControlWithGroupAndConditional(ifOp, rewriter);
2410 void IfOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2411 MLIRContext *context) {
2412 patterns.add<EmptyIfBody>(context);
2413 patterns.add(commonTailPatternWithPar<IfOp, ParOp>);
2414 patterns.add(commonTailPatternWithSeq<IfOp, SeqOp>);
2417 //===----------------------------------------------------------------------===//
2419 //===----------------------------------------------------------------------===//
2420 LogicalResult StaticIfOp::verify() {
2421 if (elseBodyExists()) {
2422 auto *elseBod = getElseBody();
2423 auto &elseOps = elseBod->getOperations();
2424 // should only have one Operation, static, in the else branch
2425 for (Operation &op : elseOps) {
2426 if (!isStaticControl(&op)) {
2427 return op.emitOpError(
2428 "static if's
else branch has non
static control within it
");
2433 auto *thenBod = getThenBody();
2434 auto &thenOps = thenBod->getOperations();
2435 for (Operation &op : thenOps) {
2436 // should only have one, static, Operation in the then branch
2437 if (!isStaticControl(&op)) {
2438 return op.emitOpError(
2439 "static if's then branch has non static control within it");
2449 struct EmptyStaticIfBody : mlir::OpRewritePattern<StaticIfOp> {
2450 using mlir::OpRewritePattern<StaticIfOp>::OpRewritePattern;
2451 LogicalResult matchAndRewrite(StaticIfOp ifOp,
2452 PatternRewriter &rewriter) const override {
2453 if (!ifOp.getThenBody()->empty())
2455 if (ifOp.elseBodyExists() && !ifOp.getElseBody()->empty())
2458 eraseControlWithConditional(ifOp, rewriter);
2464 void StaticIfOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2465 MLIRContext *context) {
2466 patterns.add<EmptyStaticIfBody>(context);
2467 patterns.add(commonTailPatternWithPar<StaticIfOp, StaticParOp>);
2468 patterns.add(commonTailPatternWithSeq<StaticIfOp, StaticSeqOp>);
2471 //===----------------------------------------------------------------------===//
2473 //===----------------------------------------------------------------------===//
2474 LogicalResult WhileOp::verify() {
2475 auto component = (*this)->getParentOfType<ComponentOp>();
2476 auto wiresOp = component.getWiresOp();
2478 std::optional<StringRef> optGroupName = getGroupName();
2479 if (!optGroupName) {
2483 StringRef groupName = *optGroupName;
2484 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(groupName);
2486 return emitOpError() << "with group '
" << groupName
2487 << "', which does not exist.";
2489 if (isa<GroupOp>(groupOp))
2490 return emitOpError() << "with group '" << groupName
2491 << "', which is not a combinational group.";
2493 if (failed(groupOp.drivesPort(getCond())))
2494 return emitError() << "conditional op: '" << valueName(component, getCond())
2495 << "' expected to be driven from group: '" << groupName
2496 << "' but no driver was found.";
2501 LogicalResult WhileOp::canonicalize(WhileOp whileOp,
2502 PatternRewriter &rewriter) {
2503 if (whileOp.getBodyBlock()->empty()) {
2504 eraseControlWithGroupAndConditional(whileOp, rewriter);
2511 //===----------------------------------------------------------------------===//
2513 //===----------------------------------------------------------------------===//
2514 LogicalResult StaticRepeatOp::verify() {
2515 for (auto &&bodyOp : (*this).getRegion().front()) {
2516 // there should only be one bodyOp for each StaticRepeatOp
2517 if (!isStaticControl(&bodyOp)) {
2518 return bodyOp.emitOpError(
2519 "static repeat has non static control within it");
2526 template <typename OpTy>
2527 static LogicalResult zeroRepeat(OpTy op, PatternRewriter &rewriter) {
2528 static_assert(IsAny<OpTy, RepeatOp, StaticRepeatOp>(),
2529 "Should be a RepeatOp or StaticPRepeatOp");
2530 if (op.getCount() == 0) {
2531 Block *controlBody = op.getBodyBlock();
2532 for (auto &op : make_early_inc_range(*controlBody))
2535 rewriter.eraseOp(op);
2542 void StaticRepeatOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2543 MLIRContext *context) {
2544 patterns.add(emptyControl<StaticRepeatOp>);
2545 patterns.add(zeroRepeat<StaticRepeatOp>);
2548 //===----------------------------------------------------------------------===//
2550 //===----------------------------------------------------------------------===//
2551 void RepeatOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2552 MLIRContext *context) {
2553 patterns.add(emptyControl<RepeatOp>);
2554 patterns.add(zeroRepeat<RepeatOp>);
2557 //===----------------------------------------------------------------------===//
2559 //===----------------------------------------------------------------------===//
2561 // Parse the parameter list of invoke.
2563 parseParameterList(OpAsmParser &parser, OperationState &result,
2564 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &ports,
2565 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &inputs,
2566 SmallVectorImpl<Attribute> &portNames,
2567 SmallVectorImpl<Attribute> &inputNames,
2568 SmallVectorImpl<Type> &types) {
2569 OpAsmParser::UnresolvedOperand port;
2570 OpAsmParser::UnresolvedOperand input;
2572 auto parseParameter = [&]() -> ParseResult {
2573 if (parser.parseOperand(port) || parser.parseEqual() ||
2574 parser.parseOperand(input))
2576 ports.push_back(port);
2577 portNames.push_back(StringAttr::get(parser.getContext(), port.name));
2578 inputs.push_back(input);
2579 inputNames.push_back(StringAttr::get(parser.getContext(), input.name));
2582 if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
2585 if (parser.parseArrow())
2587 auto parseType = [&]() -> ParseResult {
2588 if (parser.parseType(type))
2590 types.push_back(type);
2593 return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
2597 ParseResult InvokeOp::parse(OpAsmParser &parser, OperationState &result) {
2598 StringAttr componentName;
2599 SmallVector<OpAsmParser::UnresolvedOperand, 4> ports;
2600 SmallVector<OpAsmParser::UnresolvedOperand, 4> inputs;
2601 SmallVector<Attribute> portNames;
2602 SmallVector<Attribute> inputNames;
2603 SmallVector<Type, 4> types;
2604 if (parser.parseSymbolName(componentName))
2606 FlatSymbolRefAttr callee = FlatSymbolRefAttr::get(componentName);
2607 SMLoc loc = parser.getCurrentLocation();
2608 result.addAttribute("callee", callee);
2609 if (parseParameterList(parser, result, ports, inputs, portNames, inputNames,
2612 if (parser.resolveOperands(ports, types, loc, result.operands))
2614 if (parser.resolveOperands(inputs, types, loc, result.operands))
2616 result.addAttribute("portNames",
2617 ArrayAttr::get(parser.getContext(), portNames));
2618 result.addAttribute("inputNames",
2619 ArrayAttr::get(parser.getContext(), inputNames));
2623 void InvokeOp::print(OpAsmPrinter &p) {
2624 p << " @" << getCallee() << "(";
2625 auto ports = getPorts();
2626 auto inputs = getInputs();
2627 llvm::interleaveComma(llvm::zip(ports, inputs), p, [&](auto arg) {
2628 p << std::get<0>(arg) << " = " << std::get<1>(arg);
2631 llvm::interleaveComma(ports, p, [&](auto port) { p << port.getType(); });
2635 // Check the direction of one of the ports in one of the connections of an
2637 static LogicalResult verifyInvokeOpValue(InvokeOp &op, Value &value,
2638 bool isDestination) {
2640 return verifyPortDirection(op, value, isDestination);
2644 // Checks if the value comes from complex logic.
2645 static LogicalResult verifyComplexLogic(InvokeOp &op, Value &value) {
2646 // Refer to the above function verifyNotComplexSource for its role.
2647 Operation *operation = value.getDefiningOp();
2648 if (operation == nullptr)
2650 if (auto *dialect = operation->getDialect(); isa<comb::CombDialect>(dialect))
2655 // Get the go port of the invoked component.
2656 Value InvokeOp::getInstGoValue() {
2657 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2658 Operation *operation = componentOp.lookupSymbol(getCallee());
2659 Value ret = nullptr;
2660 llvm::TypeSwitch<Operation *>(operation)
2661 .Case<RegisterOp>([&](auto op) { ret = operation->getResult(1); })
2662 .Case<MemoryOp, DivSPipeLibOp, DivUPipeLibOp, MultPipeLibOp,
2663 RemSPipeLibOp, RemUPipeLibOp>(
2664 [&](auto op) { ret = operation->getResult(2); })
2665 .Case<InstanceOp>([&](auto op) {
2666 auto portInfo = op.getReferencedComponent().getPortInfo();
2667 for (auto [portInfo, res] :
2668 llvm::zip(portInfo, operation->getResults())) {
2669 if (portInfo.hasAttribute(goPort))
2673 .Case<PrimitiveOp>([&](auto op) {
2674 auto moduleExternOp = op.getReferencedPrimitive();
2675 auto argAttrs = moduleExternOp.getAllInputAttrs();
2676 for (auto [attr, res] : llvm::zip(argAttrs, op.getResults())) {
2677 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2678 if (!dictAttr.empty()) {
2679 if (dictAttr.begin()->getName().getValue() == "calyx.go")
2688 // Get the done port of the invoked component.
2689 Value InvokeOp::getInstDoneValue() {
2690 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2691 Operation *operation = componentOp.lookupSymbol(getCallee());
2692 Value ret = nullptr;
2693 llvm::TypeSwitch<Operation *>(operation)
2694 .Case<RegisterOp, MemoryOp, DivSPipeLibOp, DivUPipeLibOp, MultPipeLibOp,
2695 RemSPipeLibOp, RemUPipeLibOp>([&](auto op) {
2696 size_t doneIdx = operation->getResults().size() - 1;
2697 ret = operation->getResult(doneIdx);
2699 .Case<InstanceOp>([&](auto op) {
2700 InstanceOp instanceOp = cast<InstanceOp>(operation);
2701 auto portInfo = instanceOp.getReferencedComponent().getPortInfo();
2702 for (auto [portInfo, res] :
2703 llvm::zip(portInfo, operation->getResults())) {
2704 if (portInfo.hasAttribute(donePort))
2708 .Case<PrimitiveOp>([&](auto op) {
2709 PrimitiveOp primOp = cast<PrimitiveOp>(operation);
2710 auto moduleExternOp = primOp.getReferencedPrimitive();
2711 auto resAttrs = moduleExternOp.getAllOutputAttrs();
2712 for (auto [attr, res] : llvm::zip(resAttrs, primOp.getResults())) {
2713 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2714 if (!dictAttr.empty()) {
2715 if (dictAttr.begin()->getName().getValue() == "calyx.done")
2724 // A helper function that gets the number of go or done ports in
2725 // hw.module.extern.
2727 getHwModuleExtGoOrDonePortNumber(hw::HWModuleExternOp &moduleExternOp,
2730 std::string str = isGo ? "calyx.go" : "calyx.done";
2731 for (Attribute attr : moduleExternOp.getAllInputAttrs()) {
2732 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2733 ret = llvm::count_if(dictAttr, [&](NamedAttribute iter) {
2734 return iter.getName().getValue() == str;
2741 LogicalResult InvokeOp::verify() {
2742 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2743 StringRef callee = getCallee();
2744 Operation *operation = componentOp.lookupSymbol(callee);
2745 // The referenced symbol does not exist.
2747 return emitOpError() << "with instance '@" << callee
2748 << "', which does not exist.";
2749 // The argument list of invoke is empty.
2750 if (getInputs().empty())
2751 return emitOpError() << "'@" << callee
2752 << "' has zero input and output port connections; "
2753 "expected at least one.";
2754 size_t goPortNum = 0, donePortNum = 0;
2755 // They both have a go port and a done port, but the "go" port for
2756 // registers and memrey should be "write_en" port.
2757 llvm::TypeSwitch<Operation *>(operation)
2758 .Case<RegisterOp, DivSPipeLibOp, DivUPipeLibOp, MemoryOp, MultPipeLibOp,
2759 RemSPipeLibOp, RemUPipeLibOp>(
2760 [&](auto op) { goPortNum = 1, donePortNum = 1; })
2761 .Case<InstanceOp>([&](auto op) {
2762 auto portInfo = op.getReferencedComponent().getPortInfo();
2763 for (PortInfo info : portInfo) {
2764 if (info.hasAttribute(goPort))
2766 if (info.hasAttribute(donePort))
2770 .Case<PrimitiveOp>([&](auto op) {
2771 auto moduleExternOp = op.getReferencedPrimitive();
2772 // Get the number of go ports and done ports by their attrubutes.
2773 goPortNum = getHwModuleExtGoOrDonePortNumber(moduleExternOp, true);
2774 donePortNum = getHwModuleExtGoOrDonePortNumber(moduleExternOp, false);
2776 // If the number of go ports and done ports is wrong.
2777 if (goPortNum != 1 && donePortNum != 1)
2778 return emitOpError()
2779 << "'@" << callee << "'"
2780 << " is a combinational component and cannot be invoked, which must "
2781 "have single go port and single done port.";
2783 auto ports = getPorts();
2784 auto inputs = getInputs();
2785 // We have verified earlier that the instance has a go and a done port.
2786 Value goValue = getInstGoValue();
2787 Value doneValue = getInstDoneValue();
2788 for (auto [port, input, portName, inputName] :
2789 llvm::zip(ports, inputs, getPortNames(), getInputNames())) {
2790 // Check the direction of these destination ports.
2791 // 'calyx.invoke
' op '@r0
' has input '%r.out
', which is a source port. The
2792 // inputs are required to be destination ports.
2793 if (failed(verifyInvokeOpValue(*this, port, true)))
2794 return emitOpError() << "'@" << callee << "' has input '"
2795 << cast<StringAttr>(portName).getValue()
2796 << "', which is a source port. The inputs are "
2797 "required to be destination ports.";
2798 // The go port should not appear in the parameter list.
2799 if (port == goValue)
2800 return emitOpError() << "the go or write_en port of '@" << callee
2801 << "' cannot appear here.";
2802 // Check the direction of these source ports.
2803 if (failed(verifyInvokeOpValue(*this, input, false)))
2804 return emitOpError() << "'@" << callee << "' has output '"
2805 << cast<StringAttr>(inputName).getValue()
2806 << "', which is a destination port. The inputs are "
2807 "required to be source ports.";
2808 if (failed(verifyComplexLogic(*this, input)))
2809 return emitOpError() << "'@" << callee << "' has '"
2810 << cast<StringAttr>(inputName).getValue()
2811 << "', which is not a port or constant. Complex "
2812 "logic should be conducted in the guard.";
2813 if (input == doneValue)
2814 return emitOpError() << "the done port of '@" << callee
2815 << "' cannot appear here.";
2816 // Check if the connection uses the callee's port.
2817 if (port.getDefiningOp() != operation && input.getDefiningOp() != operation)
2818 return emitOpError() <<
"the connection "
2819 << cast<StringAttr>(portName).getValue() <<
" = "
2820 << cast<StringAttr>(inputName).getValue()
2821 <<
" is not defined as an input port of '@" << callee
2832 unsigned inBits = getResult(0).getType().getIntOrFloatBitWidth();
2833 unsigned outBits = getResult(1).getType().getIntOrFloatBitWidth();
2834 if (inBits >= outBits)
2835 return emitOpError(
"expected input bits (")
2836 << inBits <<
')' <<
" to be less than output bits (" << outBits
2842 unsigned inBits = getResult(0).getType().getIntOrFloatBitWidth();
2843 unsigned outBits = getResult(1).getType().getIntOrFloatBitWidth();
2844 if (inBits <= outBits)
2845 return emitOpError(
"expected input bits (")
2846 << inBits <<
')' <<
" to be greater than output bits (" << outBits
2851 #define ImplBinPipeOpCellInterface(OpType, outName) \
2852 SmallVector<StringRef> OpType::portNames() { \
2853 return {clkPort, resetPort, goPort, "left", "right", outName, donePort}; \
2856 SmallVector<Direction> OpType::portDirections() { \
2857 return {Input, Input, Input, Input, Input, Output, Output}; \
2860 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
2861 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
2864 SmallVector<DictionaryAttr> OpType::portAttributes() { \
2865 MLIRContext *context = getContext(); \
2866 IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1); \
2867 NamedAttrList go, clk, reset, done; \
2868 go.append(goPort, isSet); \
2869 clk.append(clkPort, isSet); \
2870 reset.append(resetPort, isSet); \
2871 done.append(donePort, isSet); \
2873 clk.getDictionary(context),
\
2874 reset.getDictionary(context), \
2875 go.getDictionary(context), \
2876 DictionaryAttr::get(context), \
2877 DictionaryAttr::get(context), \
2878 DictionaryAttr::get(context), \
2879 done.getDictionary(context) \
2883 bool OpType::isCombinational() { return false; }
2885 #define ImplUnaryOpCellInterface(OpType) \
2886 SmallVector<StringRef> OpType::portNames() { return {"in", "out"}; } \
2887 SmallVector<Direction> OpType::portDirections() { return {Input, Output}; } \
2888 SmallVector<DictionaryAttr> OpType::portAttributes() { \
2889 return {DictionaryAttr::get(getContext()), \
2890 DictionaryAttr::get(getContext())}; \
2892 bool OpType::isCombinational() { return true; } \
2893 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
2894 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
2897 #define ImplBinOpCellInterface(OpType) \
2898 SmallVector<StringRef> OpType::portNames() { \
2899 return {"left", "right", "out"}; \
2901 SmallVector<Direction> OpType::portDirections() { \
2902 return {Input, Input, Output}; \
2904 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
2905 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
2907 bool OpType::isCombinational() { return true; } \
2908 SmallVector<DictionaryAttr> OpType::portAttributes() { \
2909 return {DictionaryAttr::get(getContext()), \
2910 DictionaryAttr::get(getContext()), \
2911 DictionaryAttr::get(getContext())}; \
2955 #include "circt/Dialect/Calyx/CalyxInterfaces.cpp.inc"
2958 #define GET_OP_CLASSES
2959 #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)
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 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 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 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 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 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)
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 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 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 holds information about the port for either a Component or Cell.
DictionaryAttr attributes