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 value.isa<BlockArgument>() ||
137 (definingOp && isa<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 (!cond.isa<BlockArgument>() && 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 (!cond.isa<BlockArgument>() && 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 = portNames[i].cast<StringAttr>();
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{portNamesAttr[i].cast<StringAttr>(),
672 portAttrs[i].cast<DictionaryAttr>()});
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: "
733 LogicalResult ComponentOp::verify() {
735 auto wIt = getBodyBlock()->getOps<WiresOp>();
736 auto cIt = getBodyBlock()->getOps<ControlOp>();
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 =
750 getControlOp().getBodyBlock()->getOperations().empty();
751 bool hasNoAssignments =
752 getWiresOp().getBodyBlock()->getOps<AssignOp>().
empty();
753 if (hasNoControlConstructs && hasNoAssignments)
755 "The component currently does nothing. It needs to either have "
756 "continuous assignments in the Wires region or control constructs in "
757 "the Control region.");
762 void ComponentOp::build(OpBuilder &
builder, OperationState &result,
763 StringAttr name, ArrayRef<PortInfo> ports) {
767 void ComponentOp::getAsmBlockArgumentNames(
771 auto ports = getPortNames();
772 auto *block = &getRegion()->front();
773 for (
size_t i = 0, e = block->getNumArguments(); i != e; ++i)
774 setNameFn(block->getArgument(i), ports[i].cast<StringAttr>().getValue());
782 auto portTypes = getArgumentTypes();
783 ArrayAttr portNamesAttr = getPortNames(), portAttrs = getPortAttributes();
784 APInt portDirectionsAttr = getPortDirections();
786 SmallVector<PortInfo> results;
787 for (
size_t i = 0, e = portNamesAttr.size(); i != e; ++i) {
788 results.push_back(
PortInfo{portNamesAttr[i].cast<StringAttr>(),
791 portAttrs[i].cast<DictionaryAttr>()});
796 WiresOp calyx::CombComponentOp::getWiresOp() {
797 auto *body = getBodyBlock();
798 auto opIt = body->getOps<WiresOp>().begin();
803 template <
typename Pred>
805 SmallVector<PortInfo> ports = op.getPortInfo();
806 llvm::erase_if(ports, p);
810 SmallVector<PortInfo> CombComponentOp::getInputPortInfo() {
815 SmallVector<PortInfo> CombComponentOp::getOutputPortInfo() {
820 void CombComponentOp::print(OpAsmPrinter &p) {
821 printComponentInterface<CombComponentOp>(p, *
this);
824 ParseResult CombComponentOp::parse(OpAsmParser &parser,
825 OperationState &result) {
826 return parseComponentInterface<CombComponentOp>(parser, result);
829 LogicalResult CombComponentOp::verify() {
831 auto wIt = getBodyBlock()->getOps<WiresOp>();
832 if (std::distance(wIt.begin(), wIt.end()) != 1)
833 return emitOpError() <<
"requires exactly one "
834 << WiresOp::getOperationName() <<
" op.";
837 auto cIt = getBodyBlock()->getOps<ControlOp>();
838 if (std::distance(cIt.begin(), cIt.end()) != 0)
839 return emitOpError() <<
"must not have a `" << ControlOp::getOperationName()
843 bool hasNoAssignments =
844 getWiresOp().getBodyBlock()->getOps<AssignOp>().
empty();
845 if (hasNoAssignments)
847 "The component currently does nothing. It needs to either have "
848 "continuous assignments in the Wires region or control constructs in "
849 "the Control region.");
852 auto cells = getOps<CellInterface>();
853 for (
auto cell : cells) {
854 if (!cell.isCombinational())
855 return emitOpError() <<
"contains non-combinational cell "
856 << cell.instanceName();
860 auto groups = getWiresOp().getOps<GroupOp>();
862 return emitOpError() <<
"contains group " << (*groups.begin()).getSymName();
867 auto combGroups = getWiresOp().getOps<CombGroupOp>();
868 if (!combGroups.empty())
869 return emitOpError() <<
"contains comb group "
870 << (*combGroups.begin()).getSymName();
875 void CombComponentOp::build(OpBuilder &
builder, OperationState &result,
876 StringAttr name, ArrayRef<PortInfo> ports) {
880 void CombComponentOp::getAsmBlockArgumentNames(
884 auto ports = getPortNames();
885 auto *block = &getRegion()->front();
886 for (
size_t i = 0, e = block->getNumArguments(); i != e; ++i)
887 setNameFn(block->getArgument(i), ports[i].cast<StringAttr>().getValue());
896 SmallVector<InvokeOp, 4> ControlOp::getInvokeOps() {
897 SmallVector<InvokeOp, 4> ret;
898 this->walk([&](InvokeOp invokeOp) { ret.push_back(invokeOp); });
906 void SeqOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
907 MLIRContext *context) {
908 patterns.add(collapseControl<SeqOp>);
917 LogicalResult StaticSeqOp::verify() {
919 auto &ops = (*this).getBodyBlock()->getOperations();
920 if (!llvm::all_of(ops, [&](Operation &op) {
return isStaticControl(&op); })) {
921 return emitOpError(
"StaticSeqOp has non static control within it");
927 void StaticSeqOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
928 MLIRContext *context) {
929 patterns.add(collapseControl<StaticSeqOp>);
930 patterns.add(emptyControl<StaticSeqOp>);
938 LogicalResult ParOp::verify() {
943 for (EnableOp op : getBodyBlock()->getOps<EnableOp>()) {
944 StringRef groupName = op.getGroupName();
945 if (groupNames.count(groupName))
946 return emitOpError() <<
"cannot enable the same group: \"" << groupName
947 <<
"\" more than once.";
948 groupNames.insert(groupName);
954 void ParOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
955 MLIRContext *context) {
956 patterns.add(collapseControl<ParOp>);
965 LogicalResult StaticParOp::verify() {
970 for (EnableOp op : getBodyBlock()->getOps<EnableOp>()) {
971 StringRef groupName = op.getGroupName();
972 if (groupNames.count(groupName))
973 return emitOpError() <<
"cannot enable the same group: \"" << groupName
974 <<
"\" more than once.";
975 groupNames.insert(groupName);
979 auto &ops = (*this).getBodyBlock()->getOperations();
980 for (Operation &op : ops) {
982 return op.emitOpError(
"StaticParOp has non static control within it");
989 void StaticParOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
990 MLIRContext *context) {
991 patterns.add(collapseControl<StaticParOp>);
992 patterns.add(emptyControl<StaticParOp>);
999 LogicalResult WiresOp::verify() {
1000 auto componentInterface = (*this)->getParentOfType<ComponentInterface>();
1001 if (llvm::isa<ComponentOp>(componentInterface)) {
1002 auto component = llvm::cast<ComponentOp>(componentInterface);
1003 auto control = component.getControlOp();
1006 for (
auto &&op : *getBodyBlock()) {
1007 if (!isa<GroupInterface>(op))
1009 auto group = cast<GroupInterface>(op);
1010 auto groupName = group.symName();
1011 if (mlir::SymbolTable::symbolKnownUseEmpty(groupName, control))
1012 return op.emitOpError()
1013 <<
"with name: " << groupName
1014 <<
" is unused in the control execution schedule";
1021 for (
auto thisAssignment : getBodyBlock()->getOps<AssignOp>()) {
1025 if (thisAssignment.getGuard())
1028 Value dest = thisAssignment.getDest();
1029 for (Operation *user : dest.getUsers()) {
1030 auto assignUser = dyn_cast<AssignOp>(user);
1031 if (!assignUser || assignUser.getDest() != dest ||
1032 assignUser == thisAssignment)
1035 return user->emitOpError() <<
"destination is already continuously "
1036 "driven. Other assignment is "
1050 Operation *definingOp = value.getDefiningOp();
1051 if (definingOp ==
nullptr || definingOp->hasTrait<
Combinational>())
1057 if (isa<InstanceOp>(definingOp))
1061 if (isa<comb::CombDialect, hw::HWDialect>(definingOp->getDialect()))
1065 if (
auto r = dyn_cast<RegisterOp>(definingOp)) {
1066 return value == r.getOut()
1068 : group->emitOpError()
1069 <<
"with register: \"" << r.instanceName()
1070 <<
"\" is conducting a memory store. This is not "
1072 }
else if (
auto m = dyn_cast<MemoryOp>(definingOp)) {
1073 auto writePorts = {m.writeData(), m.writeEn()};
1074 return (llvm::none_of(writePorts, [&](Value p) {
return p == value; }))
1076 : group->emitOpError()
1077 <<
"with memory: \"" << m.instanceName()
1078 <<
"\" is conducting a memory store. This "
1079 "is not combinational.";
1082 std::string portName =
1083 valueName(group->getParentOfType<ComponentOp>(), value);
1084 return group->emitOpError() <<
"with port: " << portName
1085 <<
". This operation is not combinational.";
1090 LogicalResult CombGroupOp::verify() {
1091 for (
auto &&op : *getBodyBlock()) {
1092 auto assign = dyn_cast<AssignOp>(op);
1093 if (assign ==
nullptr)
1095 Value dst = assign.getDest(), src = assign.getSrc();
1106 GroupGoOp GroupOp::getGoOp() {
1107 auto goOps = getBodyBlock()->getOps<GroupGoOp>();
1108 size_t nOps = std::distance(goOps.begin(), goOps.end());
1109 return nOps ? *goOps.begin() : GroupGoOp();
1112 GroupDoneOp GroupOp::getDoneOp() {
1113 auto body = this->getBodyBlock();
1114 return cast<GroupDoneOp>(body->getTerminator());
1120 void CycleOp::print(OpAsmPrinter &p) {
1123 auto start = this->getStart();
1124 auto end = this->getEnd();
1125 if (
end.has_value()) {
1126 p <<
"[" << start <<
":" <<
end.value() <<
"]";
1132 ParseResult CycleOp::parse(OpAsmParser &parser, OperationState &result) {
1133 SmallVector<OpAsmParser::UnresolvedOperand, 2> operandInfos;
1135 uint32_t startLiteral;
1136 uint32_t endLiteral;
1138 auto hasEnd = succeeded(parser.parseOptionalLSquare());
1140 if (parser.parseInteger(startLiteral)) {
1141 parser.emitError(parser.getNameLoc(),
"Could not parse start cycle");
1145 auto start = parser.getBuilder().getI32IntegerAttr(startLiteral);
1146 result.addAttribute(getStartAttrName(result.name), start);
1149 if (parser.parseColon())
1152 if (
auto res = parser.parseOptionalInteger(endLiteral); res.has_value()) {
1153 auto end = parser.getBuilder().getI32IntegerAttr(endLiteral);
1154 result.addAttribute(getEndAttrName(result.name), end);
1157 if (parser.parseRSquare())
1161 result.addTypes(parser.getBuilder().getI1Type());
1166 LogicalResult CycleOp::verify() {
1167 uint32_t latency = this->getGroupLatency();
1169 if (this->getStart() >= latency) {
1170 emitOpError(
"start cycle must be less than the group latency");
1174 if (this->getEnd().has_value()) {
1175 if (this->getStart() >= this->getEnd().value()) {
1176 emitOpError(
"start cycle must be less than end cycle");
1180 if (this->getEnd() >= latency) {
1181 emitOpError(
"end cycle must be less than the group latency");
1189 uint32_t CycleOp::getGroupLatency() {
1190 auto group = (*this)->getParentOfType<StaticGroupOp>();
1191 return group.getLatency();
1202 return llvm::any_of(port.getUses(), [&](
auto &&use) {
1203 auto assignOp = dyn_cast<AssignOp>(use.getOwner());
1204 if (assignOp == nullptr)
1207 Operation *parent = assignOp->getParentOp();
1208 if (isa<WiresOp>(parent))
1217 Value expected = isDriven ? assignOp.getDest() : assignOp.getSrc();
1218 return expected == port && group == parent;
1230 if (
auto cell = dyn_cast<CellInterface>(port.getDefiningOp());
1232 return groupOp.drivesAnyPort(cell.getInputPorts());
1237 LogicalResult GroupOp::drivesPort(Value port) {
1241 LogicalResult CombGroupOp::drivesPort(Value port) {
1245 LogicalResult StaticGroupOp::drivesPort(Value port) {
1252 return success(llvm::all_of(ports, [&](Value port) {
1257 LogicalResult GroupOp::drivesAllPorts(ValueRange ports) {
1261 LogicalResult CombGroupOp::drivesAllPorts(ValueRange ports) {
1265 LogicalResult StaticGroupOp::drivesAllPorts(ValueRange ports) {
1272 return success(llvm::any_of(ports, [&](Value port) {
1277 LogicalResult GroupOp::drivesAnyPort(ValueRange ports) {
1281 LogicalResult CombGroupOp::drivesAnyPort(ValueRange ports) {
1285 LogicalResult StaticGroupOp::drivesAnyPort(ValueRange ports) {
1292 return success(llvm::any_of(ports, [&](Value port) {
1297 LogicalResult GroupOp::readsAnyPort(ValueRange ports) {
1301 LogicalResult CombGroupOp::readsAnyPort(ValueRange ports) {
1305 LogicalResult StaticGroupOp::readsAnyPort(ValueRange ports) {
1312 GroupInterface group) {
1313 Operation *destDefiningOp = assign.getDest().getDefiningOp();
1314 if (destDefiningOp ==
nullptr)
1316 auto destCell = dyn_cast<CellInterface>(destDefiningOp);
1317 if (destCell ==
nullptr)
1320 LogicalResult verifyWrites =
1321 TypeSwitch<Operation *, LogicalResult>(destCell)
1322 .Case<RegisterOp>([&](
auto op) {
1325 return succeeded(group.drivesAnyPort({op.getWriteEn(), op.getIn()}))
1326 ? group.drivesAllPorts({op.getWriteEn(), op.getIn()})
1329 .Case<MemoryOp>([&](
auto op) {
1330 SmallVector<Value> requiredWritePorts;
1333 requiredWritePorts.push_back(op.writeEn());
1334 requiredWritePorts.push_back(op.writeData());
1335 for (Value address : op.addrPorts())
1336 requiredWritePorts.push_back(address);
1341 group.drivesAnyPort({op.writeData(), op.writeEn()}))
1342 ? group.drivesAllPorts(requiredWritePorts)
1345 .Case<AndLibOp, OrLibOp, XorLibOp, AddLibOp, SubLibOp, GtLibOp,
1346 LtLibOp, EqLibOp, NeqLibOp, GeLibOp, LeLibOp, LshLibOp,
1347 RshLibOp, SgtLibOp, SltLibOp, SeqLibOp, SneqLibOp, SgeLibOp,
1348 SleLibOp, SrshLibOp>([&](
auto op) {
1349 Value lhs = op.getLeft(), rhs = op.getRight();
1350 return succeeded(group.drivesAnyPort({lhs, rhs}))
1351 ? group.drivesAllPorts({lhs, rhs})
1354 .Default([&](
auto op) {
return success(); });
1356 if (failed(verifyWrites))
1357 return group->emitOpError()
1358 <<
"with cell: " << destCell->getName() <<
" \""
1359 << destCell.instanceName()
1360 <<
"\" is performing a write and failed to drive all necessary "
1363 Operation *srcDefiningOp = assign.getSrc().getDefiningOp();
1364 if (srcDefiningOp ==
nullptr)
1366 auto srcCell = dyn_cast<CellInterface>(srcDefiningOp);
1367 if (srcCell ==
nullptr)
1370 LogicalResult verifyReads =
1371 TypeSwitch<Operation *, LogicalResult>(srcCell)
1372 .Case<MemoryOp>([&](
auto op) {
1376 return succeeded(group.readsAnyPort({op.readData()}))
1377 ? group.drivesAllPorts(op.addrPorts())
1380 .Default([&](
auto op) {
return success(); });
1382 if (failed(verifyReads))
1383 return group->emitOpError() <<
"with cell: " << srcCell->getName() <<
" \""
1384 << srcCell.instanceName()
1385 <<
"\" is having a read performed upon it, and "
1386 "failed to drive all necessary ports.";
1392 auto group = dyn_cast<GroupInterface>(op);
1393 if (group ==
nullptr)
1396 for (
auto &&groupOp : *group.getBody()) {
1397 auto assign = dyn_cast<AssignOp>(groupOp);
1398 if (assign ==
nullptr)
1414 ArrayRef<StringRef> portNames) {
1415 auto cellInterface = dyn_cast<CellInterface>(op);
1416 assert(cellInterface &&
"must implement the Cell interface");
1418 std::string prefix = cellInterface.instanceName().str() +
".";
1419 for (
size_t i = 0, e = portNames.size(); i != e; ++i)
1420 setNameFn(op->getResult(i), prefix + portNames[i].str());
1431 bool isDestination) {
1432 Operation *definingOp = value.getDefiningOp();
1433 bool isComponentPort = value.isa<BlockArgument>(),
1434 isCellInterfacePort = definingOp && isa<CellInterface>(definingOp);
1435 assert((isComponentPort || isCellInterfacePort) &&
"Not a port.");
1439 : cast<CellInterface>(definingOp).portInfo(value);
1441 bool isSource = !isDestination;
1444 (isDestination && isComponentPort) || (isSource && isCellInterfacePort)
1451 <<
"has a " << (isComponentPort ?
"component" :
"cell")
1453 << (isDestination ?
"destination" :
"source")
1454 <<
" with the incorrect direction.";
1461 bool isSource = !isDestination;
1462 Value value = isDestination ? op.getDest() : op.getSrc();
1467 if (isDestination && !isa<GroupGoOp, GroupDoneOp>(value.getDefiningOp()))
1468 return op->emitOpError(
1469 "has an invalid destination port. It must be drive-able.");
1476 LogicalResult AssignOp::verify() {
1477 bool isDestination =
true, isSource =
false;
1486 ParseResult AssignOp::parse(OpAsmParser &parser, OperationState &result) {
1487 OpAsmParser::UnresolvedOperand destination;
1488 if (parser.parseOperand(destination) || parser.parseEqual())
1494 OpAsmParser::UnresolvedOperand guardOrSource;
1495 if (parser.parseOperand(guardOrSource))
1500 OpAsmParser::UnresolvedOperand source;
1501 bool hasGuard = succeeded(parser.parseOptionalQuestion());
1504 if (parser.parseOperand(source))
1509 if (parser.parseColonType(type) ||
1510 parser.resolveOperand(destination, type, result.operands))
1514 Type i1Type = parser.getBuilder().getI1Type();
1517 if (parser.resolveOperand(source, type, result.operands) ||
1518 parser.resolveOperand(guardOrSource, i1Type, result.operands))
1522 if (parser.resolveOperand(guardOrSource, type, result.operands))
1529 void AssignOp::print(OpAsmPrinter &p) {
1530 p <<
" " << getDest() <<
" = ";
1532 Value bguard = getGuard(), source = getSrc();
1535 p << bguard <<
" ? ";
1539 p << source <<
" : " << source.getType();
1548 ComponentInterface InstanceOp::getReferencedComponent() {
1549 auto module = (*this)->getParentOfType<ModuleOp>();
1553 return module.lookupSymbol<ComponentInterface>(getComponentName());
1559 static LogicalResult
1561 ComponentInterface referencedComponent) {
1562 auto module = instance->getParentOfType<ModuleOp>();
1563 StringRef entryPointName =
1564 module->getAttrOfType<StringAttr>(
"calyx.entrypoint");
1565 if (instance.getComponentName() == entryPointName)
1566 return instance.emitOpError()
1567 <<
"cannot reference the entry-point component: '" << entryPointName
1571 SmallVector<PortInfo> componentPorts = referencedComponent.getPortInfo();
1572 size_t numPorts = componentPorts.size();
1574 size_t numResults = instance.getNumResults();
1575 if (numResults != numPorts)
1576 return instance.emitOpError()
1577 <<
"has a wrong number of results; expected: " << numPorts
1578 <<
" but got " << numResults;
1580 for (
size_t i = 0; i != numResults; ++i) {
1581 auto resultType = instance.getResult(i).getType();
1582 auto expectedType = componentPorts[i].type;
1583 if (resultType == expectedType)
1585 return instance.emitOpError()
1586 <<
"result type for " << componentPorts[i].name <<
" must be "
1587 << expectedType <<
", but got " << resultType;
1592 LogicalResult InstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1593 Operation *op = *
this;
1594 auto module = op->getParentOfType<ModuleOp>();
1595 Operation *referencedComponent =
1596 symbolTable.lookupNearestSymbolFrom(module, getComponentNameAttr());
1597 if (referencedComponent ==
nullptr)
1598 return emitError() <<
"referencing component: '" << getComponentName()
1599 <<
"', which does not exist.";
1601 Operation *shadowedComponentName =
1602 symbolTable.lookupNearestSymbolFrom(module, getSymNameAttr());
1603 if (shadowedComponentName !=
nullptr)
1604 return emitError() <<
"instance symbol: '" << instanceName()
1605 <<
"' is already a symbol for another component.";
1608 auto parentComponent = op->getParentOfType<ComponentOp>();
1609 if (parentComponent == referencedComponent)
1610 return emitError() <<
"recursive instantiation of its parent component: '"
1611 << getComponentName() <<
"'";
1613 assert(isa<ComponentInterface>(referencedComponent) &&
1614 "Should be a ComponentInterface.");
1616 cast<ComponentInterface>(referencedComponent));
1624 SmallVector<StringRef> InstanceOp::portNames() {
1625 SmallVector<StringRef> portNames;
1626 for (Attribute name : getReferencedComponent().getPortNames())
1627 portNames.push_back(name.cast<StringAttr>().getValue());
1631 SmallVector<Direction> InstanceOp::portDirections() {
1632 SmallVector<Direction> portDirections;
1634 portDirections.push_back(port.direction);
1635 return portDirections;
1638 SmallVector<DictionaryAttr> InstanceOp::portAttributes() {
1639 SmallVector<DictionaryAttr> portAttributes;
1641 portAttributes.push_back(port.attributes);
1642 return portAttributes;
1646 return isa<CombComponentOp>(getReferencedComponent());
1656 auto module = (*this)->getParentOfType<ModuleOp>();
1666 static LogicalResult
1669 auto module = instance->getParentOfType<ModuleOp>();
1670 StringRef entryPointName =
1671 module->getAttrOfType<StringAttr>(
"calyx.entrypoint");
1672 if (instance.getPrimitiveName() == entryPointName)
1673 return instance.emitOpError()
1674 <<
"cannot reference the entry-point component: '" << entryPointName
1678 auto primitivePorts = referencedPrimitive.getPortList();
1679 size_t numPorts = primitivePorts.size();
1681 size_t numResults = instance.getNumResults();
1682 if (numResults != numPorts)
1683 return instance.emitOpError()
1684 <<
"has a wrong number of results; expected: " << numPorts
1685 <<
" but got " << numResults;
1688 ArrayAttr modParameters = referencedPrimitive.getParameters();
1689 ArrayAttr parameters = instance.getParameters().value_or(ArrayAttr());
1690 size_t numExpected = modParameters.size();
1691 size_t numParams = parameters.size();
1692 if (numParams != numExpected)
1693 return instance.emitOpError()
1694 <<
"has the wrong number of parameters; expected: " << numExpected
1695 <<
" but got " << numParams;
1697 for (
size_t i = 0; i != numExpected; ++i) {
1698 auto param = parameters[i].cast<circt::hw::ParamDeclAttr>();
1699 auto modParam = modParameters[i].cast<circt::hw::ParamDeclAttr>();
1701 auto paramName = param.getName();
1702 if (paramName != modParam.getName())
1703 return instance.emitOpError()
1704 <<
"parameter #" << i <<
" should have name " << modParam.getName()
1705 <<
" but has name " << paramName;
1707 if (param.getType() != modParam.getType())
1708 return instance.emitOpError()
1709 <<
"parameter " << paramName <<
" should have type "
1710 << modParam.getType() <<
" but has type " << param.getType();
1714 if (!param.getValue())
1715 return instance.emitOpError(
"parameter ")
1716 << paramName <<
" must have a value";
1719 for (
size_t i = 0; i != numResults; ++i) {
1720 auto resultType = instance.getResult(i).getType();
1721 auto expectedType = primitivePorts[i].
type;
1723 instance.getLoc(), instance.getParametersAttr(), expectedType);
1724 if (failed(replacedType))
1726 if (resultType == replacedType)
1728 return instance.emitOpError()
1729 <<
"result type for " << primitivePorts[i].name <<
" must be "
1730 << expectedType <<
", but got " << resultType;
1736 PrimitiveOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1737 Operation *op = *
this;
1738 auto module = op->getParentOfType<ModuleOp>();
1739 Operation *referencedPrimitive =
1740 symbolTable.lookupNearestSymbolFrom(module, getPrimitiveNameAttr());
1741 if (referencedPrimitive ==
nullptr)
1742 return emitError() <<
"referencing primitive: '" << getPrimitiveName()
1743 <<
"', which does not exist.";
1745 Operation *shadowedPrimitiveName =
1746 symbolTable.lookupNearestSymbolFrom(module, getSymNameAttr());
1747 if (shadowedPrimitiveName !=
nullptr)
1748 return emitError() <<
"instance symbol: '" << instanceName()
1749 <<
"' is already a symbol for another primitive.";
1753 if (parentPrimitive == referencedPrimitive)
1754 return emitError() <<
"recursive instantiation of its parent primitive: '"
1755 << getPrimitiveName() <<
"'";
1757 assert(isa<hw::HWModuleExternOp>(referencedPrimitive) &&
1758 "Should be a HardwareModuleExternOp.");
1761 cast<hw::HWModuleExternOp>(referencedPrimitive));
1769 SmallVector<StringRef> PrimitiveOp::portNames() {
1770 SmallVector<StringRef> portNames;
1771 auto ports = getReferencedPrimitive().getPortList();
1772 for (
auto port : ports)
1773 portNames.push_back(port.name.getValue());
1779 switch (direction) {
1785 llvm_unreachable(
"InOut ports not supported by Calyx");
1787 llvm_unreachable(
"Impossible port type");
1790 SmallVector<Direction> PrimitiveOp::portDirections() {
1791 SmallVector<Direction> portDirections;
1792 auto ports = getReferencedPrimitive().getPortList();
1793 for (hw::PortInfo port : ports)
1795 return portDirections;
1804 DictionaryAttr dict) {
1808 llvm::SmallVector<NamedAttribute> attrs;
1809 for (NamedAttribute attr : dict) {
1810 Dialect *dialect = attr.getNameDialect();
1811 if (dialect ==
nullptr || !isa<CalyxDialect>(*dialect))
1813 StringRef name = attr.getName().strref();
1814 StringAttr newName =
builder.getStringAttr(std::get<1>(name.split(
".")));
1815 attr.setName(newName);
1816 attrs.push_back(attr);
1818 return builder.getDictionaryAttr(attrs);
1822 SmallVector<DictionaryAttr> PrimitiveOp::portAttributes() {
1823 SmallVector<DictionaryAttr> portAttributes;
1824 OpBuilder
builder(getContext());
1826 auto argAttrs = prim.getAllInputAttrs();
1827 auto resAttrs = prim.getAllOutputAttrs();
1828 for (
auto a : argAttrs)
1829 portAttributes.push_back(
1831 for (
auto a : resAttrs)
1832 portAttributes.push_back(
1834 return portAttributes;
1843 SmallVector<Attribute> ¶meters) {
1845 return parser.parseCommaSeparatedList(
1846 OpAsmParser::Delimiter::OptionalLessGreater, [&]() {
1851 if (parser.parseKeywordOrString(&name) || parser.parseColonType(type))
1855 if (succeeded(parser.parseOptionalEqual())) {
1856 if (parser.parseAttribute(value, type))
1860 auto &
builder = parser.getBuilder();
1862 builder.getContext(),
builder.getStringAttr(name), type, value));
1869 ArrayAttr ¶meters) {
1870 SmallVector<Attribute> parseParameters;
1874 parameters =
ArrayAttr::get(parser.getContext(), parseParameters);
1881 ArrayAttr parameters) {
1882 if (parameters.empty())
1886 llvm::interleaveComma(parameters, p, [&](Attribute param) {
1887 auto paramAttr = param.cast<hw::ParamDeclAttr>();
1888 p << paramAttr.getName().getValue() <<
": " << paramAttr.getType();
1889 if (
auto value = paramAttr.getValue()) {
1891 p.printAttributeWithoutType(value);
1897 //===----------------------------------------------------------------------===//
1899 //===----------------------------------------------------------------------===//
1901 LogicalResult GroupGoOp::verify() { return verifyNotComplexSource(*this); }
1904 void GroupGoOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1905 auto parent = (*this)->getParentOfType<GroupOp>();
1906 StringRef name = parent.getSymName();
1907 std::string resultName = name.str() + ".go";
1908 setNameFn(getResult(), resultName);
1911 void GroupGoOp::print(OpAsmPrinter &p) { printGroupPort(p, *this); }
1913 ParseResult GroupGoOp::parse(OpAsmParser &parser, OperationState &result) {
1914 if (parseGroupPort(parser, result))
1917 result.addTypes(parser.getBuilder().getI1Type());
1921 //===----------------------------------------------------------------------===//
1923 //===----------------------------------------------------------------------===//
1925 LogicalResult GroupDoneOp::verify() {
1926 Operation *srcOp = getSrc().getDefiningOp();
1927 Value optionalGuard = getGuard();
1928 Operation *guardOp = optionalGuard ? optionalGuard.getDefiningOp() : nullptr;
1929 bool noGuard = (guardOp == nullptr);
1931 if (srcOp == nullptr)
1932 // This is a port of the parent component.
1935 if (isa<hw::ConstantOp>(srcOp) && (noGuard || isa<hw::ConstantOp>(guardOp)))
1936 return emitOpError() << "with constant source"
1937 << (noGuard ? "" : " and constant guard")
1938 << ". This should be a combinational group.";
1940 return verifyNotComplexSource(*this);
1943 void GroupDoneOp::print(OpAsmPrinter &p) { printGroupPort(p, *this); }
1945 ParseResult GroupDoneOp::parse(OpAsmParser &parser, OperationState &result) {
1946 return parseGroupPort(parser, result);
1949 //===----------------------------------------------------------------------===//
1951 //===----------------------------------------------------------------------===//
1954 void RegisterOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1955 getCellAsmResultNames(setNameFn, *this, this->portNames());
1958 SmallVector<StringRef> RegisterOp::portNames() {
1959 return {"in", "write_en", clkPort, resetPort, "out", donePort};
1962 SmallVector<Direction> RegisterOp::portDirections() {
1963 return {Input, Input, Input, Input, Output, Output};
1966 SmallVector<DictionaryAttr> RegisterOp::portAttributes() {
1967 MLIRContext *context = getContext();
1968 IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1);
1969 NamedAttrList writeEn, clk, reset, done;
1970 writeEn.append(goPort, isSet);
1971 clk.append(clkPort, isSet);
1972 reset.append(resetPort, isSet);
1973 done.append(donePort, isSet);
1975 DictionaryAttr::get(context), // In
1976 writeEn.getDictionary(context), // Write enable
1977 clk.getDictionary(context), // Clk
1978 reset.getDictionary(context), // Reset
1979 DictionaryAttr::get(context), // Out
1980 done.getDictionary(context) // Done
1984 bool RegisterOp::isCombinational() { return false; }
1986 //===----------------------------------------------------------------------===//
1988 //===----------------------------------------------------------------------===//
1991 void MemoryOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1992 getCellAsmResultNames(setNameFn, *this, this->portNames());
1995 SmallVector<StringRef> MemoryOp::portNames() {
1996 SmallVector<StringRef> portNames;
1997 for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i) {
1999 StringAttr::get(this->getContext(), "addr" + std::to_string(i));
2000 portNames.push_back(nameAttr.getValue());
2002 portNames.append({"write_data", "write_en", clkPort, "read_data", donePort});
2006 SmallVector<Direction> MemoryOp::portDirections() {
2007 SmallVector<Direction> portDirections;
2008 for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2009 portDirections.push_back(Input);
2010 portDirections.append({Input, Input, Input, Output, Output});
2011 return portDirections;
2014 SmallVector<DictionaryAttr> MemoryOp::portAttributes() {
2015 SmallVector<DictionaryAttr> portAttributes;
2016 MLIRContext *context = getContext();
2017 for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2018 portAttributes.push_back(DictionaryAttr::get(context)); // Addresses
2020 // Use a boolean to indicate this attribute is used.
2021 IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1);
2022 NamedAttrList writeEn, clk, reset, done;
2023 writeEn.append(goPort, isSet);
2024 clk.append(clkPort, isSet);
2025 done.append(donePort, isSet);
2026 portAttributes.append({DictionaryAttr::get(context), // In
2027 writeEn.getDictionary(context), // Write enable
2028 clk.getDictionary(context), // Clk
2029 DictionaryAttr::get(context), // Out
2030 done.getDictionary(context)} // Done
2032 return portAttributes;
2035 void MemoryOp::build(OpBuilder &builder, OperationState &state,
2036 StringRef instanceName, int64_t width,
2037 ArrayRef<int64_t> sizes, ArrayRef<int64_t> addrSizes) {
2038 state.addAttribute(SymbolTable::getSymbolAttrName(),
2039 builder.getStringAttr(instanceName));
2040 state.addAttribute("width", builder.getI64IntegerAttr(width));
2041 state.addAttribute("sizes", builder.getI64ArrayAttr(sizes));
2042 state.addAttribute("addrSizes", builder.getI64ArrayAttr(addrSizes));
2043 SmallVector<Type> types;
2044 for (int64_t size : addrSizes)
2045 types.push_back(builder.getIntegerType(size)); // Addresses
2046 types.push_back(builder.getIntegerType(width)); // Write data
2047 types.push_back(builder.getI1Type()); // Write enable
2048 types.push_back(builder.getI1Type()); // Clk
2049 types.push_back(builder.getIntegerType(width)); // Read data
2050 types.push_back(builder.getI1Type()); // Done
2051 state.addTypes(types);
2054 LogicalResult MemoryOp::verify() {
2055 ArrayRef<Attribute> opSizes = getSizes().getValue();
2056 ArrayRef<Attribute> opAddrSizes = getAddrSizes().getValue();
2057 size_t numDims = getSizes().size();
2058 size_t numAddrs = getAddrSizes().size();
2059 if (numDims != numAddrs)
2060 return emitOpError("mismatched number of dimensions (")
2061 << numDims << ") and address sizes (" << numAddrs << ")";
2063 size_t numExtraPorts = 5; // write data/enable, clk, and read data/done.
2064 if (getNumResults() != numAddrs + numExtraPorts)
2065 return emitOpError("incorrect number of address ports, expected ")
2068 for (size_t i = 0; i < numDims; ++i) {
2069 int64_t size = opSizes[i].cast<IntegerAttr>().getInt();
2070 int64_t addrSize = opAddrSizes[i].cast<IntegerAttr>().getInt();
2071 if (llvm::Log2_64_Ceil(size) > addrSize)
2072 return emitOpError("address size (")
2073 << addrSize << ") for dimension " << i
2074 << " can't address the entire range (
" << size << ")
";
2080 //===----------------------------------------------------------------------===//
2082 //===----------------------------------------------------------------------===//
2085 void SeqMemoryOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
2086 getCellAsmResultNames(setNameFn, *this, this->portNames());
2089 SmallVector<StringRef> SeqMemoryOp::portNames() {
2090 SmallVector<StringRef> portNames;
2091 for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i) {
2093 StringAttr::get(this->getContext(), "addr
" + std::to_string(i));
2094 portNames.push_back(nameAttr.getValue());
2096 portNames.append({clkPort, "reset
", "content_en
", "write_en
", "write_data
",
2097 "read_data
", "done
"});
2101 SmallVector<Direction> SeqMemoryOp::portDirections() {
2102 SmallVector<Direction> portDirections;
2103 for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2104 portDirections.push_back(Input);
2105 portDirections.append({Input, Input, Input, Input, Input, Output, Output});
2106 return portDirections;
2109 SmallVector<DictionaryAttr> SeqMemoryOp::portAttributes() {
2110 SmallVector<DictionaryAttr> portAttributes;
2111 MLIRContext *context = getContext();
2112 for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2113 portAttributes.push_back(DictionaryAttr::get(context)); // Addresses
2115 OpBuilder builder(context);
2116 // Use a boolean to indicate this attribute is used.
2117 IntegerAttr isSet = IntegerAttr::get(builder.getIndexType(), 1);
2118 IntegerAttr isTwo = IntegerAttr::get(builder.getIndexType(), 2);
2119 NamedAttrList done, clk, reset, contentEn;
2120 done.append(donePort, isSet);
2121 clk.append(clkPort, isSet);
2122 clk.append(resetPort, isSet);
2123 contentEn.append(goPort, isTwo);
2124 portAttributes.append({clk.getDictionary(context), // Clk
2125 reset.getDictionary(context), // Reset
2126 contentEn.getDictionary(context), // Content enable
2127 DictionaryAttr::get(context), // Write enable
2128 DictionaryAttr::get(context), // Write data
2129 DictionaryAttr::get(context), // Read data
2130 done.getDictionary(context)} // Done
2132 return portAttributes;
2135 void SeqMemoryOp::build(OpBuilder &builder, OperationState &state,
2136 StringRef instanceName, int64_t width,
2137 ArrayRef<int64_t> sizes, ArrayRef<int64_t> addrSizes) {
2138 state.addAttribute(SymbolTable::getSymbolAttrName(),
2139 builder.getStringAttr(instanceName));
2140 state.addAttribute("width", builder.getI64IntegerAttr(width));
2141 state.addAttribute("sizes
", builder.getI64ArrayAttr(sizes));
2142 state.addAttribute("addrSizes
", builder.getI64ArrayAttr(addrSizes));
2143 SmallVector<Type> types;
2144 for (int64_t size : addrSizes)
2145 types.push_back(builder.getIntegerType(size)); // Addresses
2146 types.push_back(builder.getI1Type()); // Clk
2147 types.push_back(builder.getI1Type()); // Reset
2148 types.push_back(builder.getI1Type()); // Content enable
2149 types.push_back(builder.getI1Type()); // Write enable
2150 types.push_back(builder.getIntegerType(width)); // Write data
2151 types.push_back(builder.getIntegerType(width)); // Read data
2152 types.push_back(builder.getI1Type()); // Done
2153 state.addTypes(types);
2156 LogicalResult SeqMemoryOp::verify() {
2157 ArrayRef<Attribute> opSizes = getSizes().getValue();
2158 ArrayRef<Attribute> opAddrSizes = getAddrSizes().getValue();
2159 size_t numDims = getSizes().size();
2160 size_t numAddrs = getAddrSizes().size();
2161 if (numDims != numAddrs)
2162 return emitOpError("mismatched number of dimensions (
")
2163 << numDims << ") and address sizes (
" << numAddrs << ")
";
2165 size_t numExtraPorts =
2166 7; // write data/enable, clk, reset, read data, content enable, and done.
2167 if (getNumResults() != numAddrs + numExtraPorts)
2168 return emitOpError("incorrect number of address ports, expected
")
2171 for (size_t i = 0; i < numDims; ++i) {
2172 int64_t size = opSizes[i].cast<IntegerAttr>().getInt();
2173 int64_t addrSize = opAddrSizes[i].cast<IntegerAttr>().getInt();
2174 if (llvm::Log2_64_Ceil(size) > addrSize)
2175 return emitOpError("address size (
")
2176 << addrSize << ")
for dimension
" << i
2177 << " can
't address the entire range (" << size << ")";
2183 //===----------------------------------------------------------------------===//
2185 //===----------------------------------------------------------------------===//
2186 LogicalResult EnableOp::verify() {
2187 auto component = (*this)->getParentOfType<ComponentOp>();
2188 auto wiresOp = component.getWiresOp();
2189 StringRef name = getGroupName();
2191 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(name);
2193 return emitOpError() << "with group '" << name
2194 << "', which does not exist.";
2196 if (isa<CombGroupOp>(groupOp))
2197 return emitOpError() << "with group '" << name
2198 << "', which is a combinational group.";
2203 //===----------------------------------------------------------------------===//
2205 //===----------------------------------------------------------------------===//
2207 LogicalResult IfOp::verify() {
2208 std::optional<StringRef> optGroupName = getGroupName();
2209 if (!optGroupName) {
2210 // No combinational group was provided.
2213 auto component = (*this)->getParentOfType<ComponentOp>();
2214 WiresOp wiresOp = component.getWiresOp();
2215 StringRef groupName = *optGroupName;
2216 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(groupName);
2218 return emitOpError() << "with group '" << groupName
2219 << "', which does not exist.";
2221 if (isa<GroupOp>(groupOp))
2222 return emitOpError() << "with group '" << groupName
2223 << "', which is not a combinational group.";
2225 if (failed(groupOp.drivesPort(getCond())))
2226 return emitError() << "with conditional op: '"
2227 << valueName(component, getCond())
2228 << "' expected to be driven from group: '" << groupName
2229 << "' but no driver was found.";
2237 template <typename OpTy>
2238 static std::optional<EnableOp> getLastEnableOp(OpTy parent) {
2239 static_assert(IsAny<OpTy, SeqOp, StaticSeqOp>(),
2240 "Should be a StaticSeqOp or SeqOp.");
2241 auto &lastOp = parent.getBodyBlock()->back();
2242 if (auto enableOp = dyn_cast<EnableOp>(lastOp))
2244 if (auto seqOp = dyn_cast<SeqOp>(lastOp))
2245 return getLastEnableOp(seqOp);
2246 if (auto staticSeqOp = dyn_cast<StaticSeqOp>(lastOp))
2247 return getLastEnableOp(staticSeqOp);
2249 return std::nullopt;
2254 template <typename OpTy>
2255 static llvm::StringMap<EnableOp> getAllEnableOpsInImmediateBody(OpTy parent) {
2256 static_assert(IsAny<OpTy, ParOp, StaticParOp>(),
2257 "Should be a StaticParOp or ParOp.");
2259 llvm::StringMap<EnableOp> enables;
2260 Block *body = parent.getBodyBlock();
2261 for (EnableOp op : body->getOps<EnableOp>())
2262 enables.insert(std::pair(op.getGroupName(), op));
2274 template <typename IfOpTy, typename TailOpTy>
2275 static bool hasCommonTailPatternPreConditions(IfOpTy op) {
2276 static_assert(IsAny<TailOpTy, SeqOp, ParOp, StaticSeqOp, StaticParOp>(),
2277 "Should be a SeqOp, ParOp, StaticSeqOp, or StaticParOp.");
2278 static_assert(IsAny<IfOpTy, IfOp, StaticIfOp>(),
2279 "Should be a IfOp or StaticIfOp.");
2281 if (!op.thenBodyExists() || !op.elseBodyExists())
2283 if (op.getThenBody()->empty() || op.getElseBody()->empty())
2286 Block *thenBody = op.getThenBody(), *elseBody = op.getElseBody();
2287 return isa<TailOpTy>(thenBody->front()) && isa<TailOpTy>(elseBody->front());
2298 template <typename IfOpTy, typename SeqOpTy>
2299 static LogicalResult commonTailPatternWithSeq(IfOpTy ifOp,
2300 PatternRewriter &rewriter) {
2301 static_assert(IsAny<IfOpTy, IfOp, StaticIfOp>(),
2302 "Should be an IfOp or StaticIfOp.");
2303 static_assert(IsAny<SeqOpTy, SeqOp, StaticSeqOp>(),
2304 "Branches should be checking for an SeqOp or StaticSeqOp");
2305 if (!hasCommonTailPatternPreConditions<IfOpTy, SeqOpTy>(ifOp))
2307 auto thenControl = cast<SeqOpTy>(ifOp.getThenBody()->front()),
2308 elseControl = cast<SeqOpTy>(ifOp.getElseBody()->front());
2310 std::optional<EnableOp> lastThenEnableOp = getLastEnableOp(thenControl),
2311 lastElseEnableOp = getLastEnableOp(elseControl);
2313 if (!lastThenEnableOp || !lastElseEnableOp)
2315 if (lastThenEnableOp->getGroupName() != lastElseEnableOp->getGroupName())
2318 // Place the IfOp and pulled EnableOp inside a sequential region, in case
2319 // this IfOp is nested in a ParOp. This avoids unintentionally
2320 // parallelizing the pulled out EnableOps.
2321 rewriter.setInsertionPointAfter(ifOp);
2322 SeqOpTy seqOp = rewriter.create<SeqOpTy>(ifOp.getLoc());
2323 Block *body = seqOp.getBodyBlock();
2325 body->push_back(ifOp);
2326 rewriter.setInsertionPointToEnd(body);
2327 rewriter.create<EnableOp>(seqOp.getLoc(), lastThenEnableOp->getGroupName());
2329 // Erase the common EnableOp from the Then and Else regions.
2330 rewriter.eraseOp(*lastThenEnableOp);
2331 rewriter.eraseOp(*lastElseEnableOp);
2348 template <typename OpTy, typename ParOpTy>
2349 static LogicalResult commonTailPatternWithPar(OpTy controlOp,
2350 PatternRewriter &rewriter) {
2351 static_assert(IsAny<OpTy, IfOp, StaticIfOp>(),
2352 "Should be an IfOp or StaticIfOp.");
2353 static_assert(IsAny<ParOpTy, ParOp, StaticParOp>(),
2354 "Branches should be checking for an ParOp or StaticParOp");
2355 if (!hasCommonTailPatternPreConditions<OpTy, ParOpTy>(controlOp))
2357 auto thenControl = cast<ParOpTy>(controlOp.getThenBody()->front()),
2358 elseControl = cast<ParOpTy>(controlOp.getElseBody()->front());
2360 llvm::StringMap<EnableOp> a = getAllEnableOpsInImmediateBody(thenControl),
2361 b = getAllEnableOpsInImmediateBody(elseControl);
2362 // Compute the intersection between `A` and `B`.
2363 SmallVector<StringRef> groupNames;
2364 for (auto aIndex = a.begin(); aIndex != a.end(); ++aIndex) {
2365 StringRef groupName = aIndex->getKey();
2366 auto bIndex = b.find(groupName);
2367 if (bIndex == b.end())
2369 // This is also an element in B.
2370 groupNames.push_back(groupName);
2371 // Since these are being pulled out, erase them.
2372 rewriter.eraseOp(aIndex->getValue());
2373 rewriter.eraseOp(bIndex->getValue());
2376 // Place the IfOp and EnableOp(s) inside a parallel region, in case this
2377 // IfOp is nested in a SeqOp. This avoids unintentionally sequentializing
2378 // the pulled out EnableOps.
2379 rewriter.setInsertionPointAfter(controlOp);
2381 ParOpTy parOp = rewriter.create<ParOpTy>(controlOp.getLoc());
2382 Block *body = parOp.getBodyBlock();
2383 controlOp->remove();
2384 body->push_back(controlOp);
2385 // Pull out the intersection between these two sets, and erase their
2386 // counterparts in the Then and Else regions.
2387 rewriter.setInsertionPointToEnd(body);
2388 for (StringRef groupName : groupNames)
2389 rewriter.create<EnableOp>(parOp.getLoc(), groupName);
2397 struct EmptyIfBody : mlir::OpRewritePattern<IfOp> {
2398 using mlir::OpRewritePattern<IfOp>::OpRewritePattern;
2399 LogicalResult matchAndRewrite(IfOp ifOp,
2400 PatternRewriter &rewriter) const override {
2401 if (!ifOp.getThenBody()->empty())
2403 if (ifOp.elseBodyExists() && !ifOp.getElseBody()->empty())
2406 eraseControlWithGroupAndConditional(ifOp, rewriter);
2412 void IfOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2413 MLIRContext *context) {
2414 patterns.add<EmptyIfBody>(context);
2415 patterns.add(commonTailPatternWithPar<IfOp, ParOp>);
2416 patterns.add(commonTailPatternWithSeq<IfOp, SeqOp>);
2419 //===----------------------------------------------------------------------===//
2421 //===----------------------------------------------------------------------===//
2422 LogicalResult StaticIfOp::verify() {
2423 if (elseBodyExists()) {
2424 auto *elseBod = getElseBody();
2425 auto &elseOps = elseBod->getOperations();
2426 // should only have one Operation, static, in the else branch
2427 for (Operation &op : elseOps) {
2428 if (!isStaticControl(&op)) {
2429 return op.emitOpError(
2430 "static if's
else branch has non
static control within it
");
2435 auto *thenBod = getThenBody();
2436 auto &thenOps = thenBod->getOperations();
2437 for (Operation &op : thenOps) {
2438 // should only have one, static, Operation in the then branch
2439 if (!isStaticControl(&op)) {
2440 return op.emitOpError(
2441 "static if's then branch has non static control within it");
2451 struct EmptyStaticIfBody : mlir::OpRewritePattern<StaticIfOp> {
2452 using mlir::OpRewritePattern<StaticIfOp>::OpRewritePattern;
2453 LogicalResult matchAndRewrite(StaticIfOp ifOp,
2454 PatternRewriter &rewriter) const override {
2455 if (!ifOp.getThenBody()->empty())
2457 if (ifOp.elseBodyExists() && !ifOp.getElseBody()->empty())
2460 eraseControlWithConditional(ifOp, rewriter);
2466 void StaticIfOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2467 MLIRContext *context) {
2468 patterns.add<EmptyStaticIfBody>(context);
2469 patterns.add(commonTailPatternWithPar<StaticIfOp, StaticParOp>);
2470 patterns.add(commonTailPatternWithSeq<StaticIfOp, StaticSeqOp>);
2473 //===----------------------------------------------------------------------===//
2475 //===----------------------------------------------------------------------===//
2476 LogicalResult WhileOp::verify() {
2477 auto component = (*this)->getParentOfType<ComponentOp>();
2478 auto wiresOp = component.getWiresOp();
2480 std::optional<StringRef> optGroupName = getGroupName();
2481 if (!optGroupName) {
2485 StringRef groupName = *optGroupName;
2486 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(groupName);
2488 return emitOpError() << "with group '
" << groupName
2489 << "', which does not exist.";
2491 if (isa<GroupOp>(groupOp))
2492 return emitOpError() << "with group '" << groupName
2493 << "', which is not a combinational group.";
2495 if (failed(groupOp.drivesPort(getCond())))
2496 return emitError() << "conditional op: '" << valueName(component, getCond())
2497 << "' expected to be driven from group: '" << groupName
2498 << "' but no driver was found.";
2503 LogicalResult WhileOp::canonicalize(WhileOp whileOp,
2504 PatternRewriter &rewriter) {
2505 if (whileOp.getBodyBlock()->empty()) {
2506 eraseControlWithGroupAndConditional(whileOp, rewriter);
2513 //===----------------------------------------------------------------------===//
2515 //===----------------------------------------------------------------------===//
2516 LogicalResult StaticRepeatOp::verify() {
2517 for (auto &&bodyOp : (*this).getRegion().front()) {
2518 // there should only be one bodyOp for each StaticRepeatOp
2519 if (!isStaticControl(&bodyOp)) {
2520 return bodyOp.emitOpError(
2521 "static repeat has non static control within it");
2528 template <typename OpTy>
2529 static LogicalResult zeroRepeat(OpTy op, PatternRewriter &rewriter) {
2530 static_assert(IsAny<OpTy, RepeatOp, StaticRepeatOp>(),
2531 "Should be a RepeatOp or StaticPRepeatOp");
2532 if (op.getCount() == 0) {
2533 Block *controlBody = op.getBodyBlock();
2534 for (auto &op : make_early_inc_range(*controlBody))
2537 rewriter.eraseOp(op);
2544 void StaticRepeatOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2545 MLIRContext *context) {
2546 patterns.add(emptyControl<StaticRepeatOp>);
2547 patterns.add(zeroRepeat<StaticRepeatOp>);
2550 //===----------------------------------------------------------------------===//
2552 //===----------------------------------------------------------------------===//
2553 void RepeatOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2554 MLIRContext *context) {
2555 patterns.add(emptyControl<RepeatOp>);
2556 patterns.add(zeroRepeat<RepeatOp>);
2559 //===----------------------------------------------------------------------===//
2561 //===----------------------------------------------------------------------===//
2563 // Parse the parameter list of invoke.
2565 parseParameterList(OpAsmParser &parser, OperationState &result,
2566 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &ports,
2567 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &inputs,
2568 SmallVectorImpl<Attribute> &portNames,
2569 SmallVectorImpl<Attribute> &inputNames,
2570 SmallVectorImpl<Type> &types) {
2571 OpAsmParser::UnresolvedOperand port;
2572 OpAsmParser::UnresolvedOperand input;
2574 auto parseParameter = [&]() -> ParseResult {
2575 if (parser.parseOperand(port) || parser.parseEqual() ||
2576 parser.parseOperand(input))
2578 ports.push_back(port);
2579 portNames.push_back(StringAttr::get(parser.getContext(), port.name));
2580 inputs.push_back(input);
2581 inputNames.push_back(StringAttr::get(parser.getContext(), input.name));
2584 if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
2587 if (parser.parseArrow())
2589 auto parseType = [&]() -> ParseResult {
2590 if (parser.parseType(type))
2592 types.push_back(type);
2595 return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
2599 ParseResult InvokeOp::parse(OpAsmParser &parser, OperationState &result) {
2600 StringAttr componentName;
2601 SmallVector<OpAsmParser::UnresolvedOperand, 4> ports;
2602 SmallVector<OpAsmParser::UnresolvedOperand, 4> inputs;
2603 SmallVector<Attribute> portNames;
2604 SmallVector<Attribute> inputNames;
2605 SmallVector<Type, 4> types;
2606 if (parser.parseSymbolName(componentName))
2608 FlatSymbolRefAttr callee = FlatSymbolRefAttr::get(componentName);
2609 SMLoc loc = parser.getCurrentLocation();
2610 result.addAttribute("callee", callee);
2611 if (parseParameterList(parser, result, ports, inputs, portNames, inputNames,
2614 if (parser.resolveOperands(ports, types, loc, result.operands))
2616 if (parser.resolveOperands(inputs, types, loc, result.operands))
2618 result.addAttribute("portNames",
2619 ArrayAttr::get(parser.getContext(), portNames));
2620 result.addAttribute("inputNames",
2621 ArrayAttr::get(parser.getContext(), inputNames));
2625 void InvokeOp::print(OpAsmPrinter &p) {
2626 p << " @" << getCallee() << "(";
2627 auto ports = getPorts();
2628 auto inputs = getInputs();
2629 llvm::interleaveComma(llvm::zip(ports, inputs), p, [&](auto arg) {
2630 p << std::get<0>(arg) << " = " << std::get<1>(arg);
2633 llvm::interleaveComma(ports, p, [&](auto port) { p << port.getType(); });
2637 // Check the direction of one of the ports in one of the connections of an
2639 static LogicalResult verifyInvokeOpValue(InvokeOp &op, Value &value,
2640 bool isDestination) {
2642 return verifyPortDirection(op, value, isDestination);
2646 // Checks if the value comes from complex logic.
2647 static LogicalResult verifyComplexLogic(InvokeOp &op, Value &value) {
2648 // Refer to the above function verifyNotComplexSource for its role.
2649 Operation *operation = value.getDefiningOp();
2650 if (operation == nullptr)
2652 if (auto *dialect = operation->getDialect(); isa<comb::CombDialect>(dialect))
2657 // Get the go port of the invoked component.
2658 Value InvokeOp::getInstGoValue() {
2659 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2660 Operation *operation = componentOp.lookupSymbol(getCallee());
2661 Value ret = nullptr;
2662 llvm::TypeSwitch<Operation *>(operation)
2663 .Case<RegisterOp>([&](auto op) { ret = operation->getResult(1); })
2664 .Case<MemoryOp, DivSPipeLibOp, DivUPipeLibOp, MultPipeLibOp,
2665 RemSPipeLibOp, RemUPipeLibOp>(
2666 [&](auto op) { ret = operation->getResult(2); })
2667 .Case<InstanceOp>([&](auto op) {
2668 auto portInfo = op.getReferencedComponent().getPortInfo();
2669 for (auto [portInfo, res] :
2670 llvm::zip(portInfo, operation->getResults())) {
2671 if (portInfo.hasAttribute(goPort))
2675 .Case<PrimitiveOp>([&](auto op) {
2676 auto moduleExternOp = op.getReferencedPrimitive();
2677 auto argAttrs = moduleExternOp.getAllInputAttrs();
2678 for (auto [attr, res] : llvm::zip(argAttrs, op.getResults())) {
2679 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2680 if (!dictAttr.empty()) {
2681 if (dictAttr.begin()->getName().getValue() == "calyx.go")
2690 // Get the done port of the invoked component.
2691 Value InvokeOp::getInstDoneValue() {
2692 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2693 Operation *operation = componentOp.lookupSymbol(getCallee());
2694 Value ret = nullptr;
2695 llvm::TypeSwitch<Operation *>(operation)
2696 .Case<RegisterOp, MemoryOp, DivSPipeLibOp, DivUPipeLibOp, MultPipeLibOp,
2697 RemSPipeLibOp, RemUPipeLibOp>([&](auto op) {
2698 size_t doneIdx = operation->getResults().size() - 1;
2699 ret = operation->getResult(doneIdx);
2701 .Case<InstanceOp>([&](auto op) {
2702 InstanceOp instanceOp = cast<InstanceOp>(operation);
2703 auto portInfo = instanceOp.getReferencedComponent().getPortInfo();
2704 for (auto [portInfo, res] :
2705 llvm::zip(portInfo, operation->getResults())) {
2706 if (portInfo.hasAttribute(donePort))
2710 .Case<PrimitiveOp>([&](auto op) {
2711 PrimitiveOp primOp = cast<PrimitiveOp>(operation);
2712 auto moduleExternOp = primOp.getReferencedPrimitive();
2713 auto resAttrs = moduleExternOp.getAllOutputAttrs();
2714 for (auto [attr, res] : llvm::zip(resAttrs, primOp.getResults())) {
2715 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2716 if (!dictAttr.empty()) {
2717 if (dictAttr.begin()->getName().getValue() == "calyx.done")
2726 // A helper function that gets the number of go or done ports in
2727 // hw.module.extern.
2729 getHwModuleExtGoOrDonePortNumber(hw::HWModuleExternOp &moduleExternOp,
2732 std::string str = isGo ? "calyx.go" : "calyx.done";
2733 for (Attribute attr : moduleExternOp.getAllInputAttrs()) {
2734 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2735 ret = llvm::count_if(dictAttr, [&](NamedAttribute iter) {
2736 return iter.getName().getValue() == str;
2743 LogicalResult InvokeOp::verify() {
2744 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2745 StringRef callee = getCallee();
2746 Operation *operation = componentOp.lookupSymbol(callee);
2747 // The referenced symbol does not exist.
2749 return emitOpError() << "with instance '@" << callee
2750 << "', which does not exist.";
2751 // The argument list of invoke is empty.
2752 if (getInputs().empty())
2753 return emitOpError() << "'@" << callee
2754 << "' has zero input and output port connections; "
2755 "expected at least one.";
2756 size_t goPortNum = 0, donePortNum = 0;
2757 // They both have a go port and a done port, but the "go" port for
2758 // registers and memrey should be "write_en" port.
2759 llvm::TypeSwitch<Operation *>(operation)
2760 .Case<RegisterOp, DivSPipeLibOp, DivUPipeLibOp, MemoryOp, MultPipeLibOp,
2761 RemSPipeLibOp, RemUPipeLibOp>(
2762 [&](auto op) { goPortNum = 1, donePortNum = 1; })
2763 .Case<InstanceOp>([&](auto op) {
2764 auto portInfo = op.getReferencedComponent().getPortInfo();
2765 for (PortInfo info : portInfo) {
2766 if (info.hasAttribute(goPort))
2768 if (info.hasAttribute(donePort))
2772 .Case<PrimitiveOp>([&](auto op) {
2773 auto moduleExternOp = op.getReferencedPrimitive();
2774 // Get the number of go ports and done ports by their attrubutes.
2775 goPortNum = getHwModuleExtGoOrDonePortNumber(moduleExternOp, true);
2776 donePortNum = getHwModuleExtGoOrDonePortNumber(moduleExternOp, false);
2778 // If the number of go ports and done ports is wrong.
2779 if (goPortNum != 1 && donePortNum != 1)
2780 return emitOpError()
2781 << "'@" << callee << "'"
2782 << " is a combinational component and cannot be invoked, which must "
2783 "have single go port and single done port.";
2785 auto ports = getPorts();
2786 auto inputs = getInputs();
2787 // We have verified earlier that the instance has a go and a done port.
2788 Value goValue = getInstGoValue();
2789 Value doneValue = getInstDoneValue();
2790 for (auto [port, input, portName, inputName] :
2791 llvm::zip(ports, inputs, getPortNames(), getInputNames())) {
2792 // Check the direction of these destination ports.
2793 // 'calyx.invoke
' op '@r0
' has input '%r.out
', which is a source port. The
2794 // inputs are required to be destination ports.
2795 if (failed(verifyInvokeOpValue(*this, port, true)))
2796 return emitOpError() << "'@" << callee << "' has input '"
2797 << portName.cast<StringAttr>().getValue()
2798 << "', which is a source port. The inputs are "
2799 "required to be destination ports.";
2800 // The go port should not appear in the parameter list.
2801 if (port == goValue)
2802 return emitOpError() << "the go or write_en port of '@" << callee
2803 << "' cannot appear here.";
2804 // Check the direction of these source ports.
2805 if (failed(verifyInvokeOpValue(*this, input, false)))
2806 return emitOpError() << "'@" << callee << "' has output '"
2807 << inputName.cast<StringAttr>().getValue()
2808 << "', which is a destination port. The inputs are "
2809 "required to be source ports.";
2810 if (failed(verifyComplexLogic(*this, input)))
2811 return emitOpError() << "'@" << callee << "' has '"
2812 << inputName.cast<StringAttr>().getValue()
2813 << "', which is not a port or constant. Complex "
2814 "logic should be conducted in the guard.";
2815 if (input == doneValue)
2816 return emitOpError() << "the done port of '@" << callee
2817 << "' cannot appear here.";
2818 // Check if the connection uses the callee's port.
2819 if (port.getDefiningOp() != operation && input.getDefiningOp() != operation)
2820 return emitOpError() <<
"the connection "
2821 << portName.cast<StringAttr>().getValue() <<
" = "
2822 << inputName.cast<StringAttr>().getValue()
2823 <<
" is not defined as an input port of '@" << callee
2833 LogicalResult PadLibOp::verify() {
2834 unsigned inBits = getResult(0).getType().getIntOrFloatBitWidth();
2835 unsigned outBits = getResult(1).getType().getIntOrFloatBitWidth();
2836 if (inBits >= outBits)
2837 return emitOpError(
"expected input bits (")
2838 << inBits <<
')' <<
" to be less than output bits (" << outBits
2843 LogicalResult SliceLibOp::verify() {
2844 unsigned inBits = getResult(0).getType().getIntOrFloatBitWidth();
2845 unsigned outBits = getResult(1).getType().getIntOrFloatBitWidth();
2846 if (inBits <= outBits)
2847 return emitOpError(
"expected input bits (")
2848 << inBits <<
')' <<
" to be greater than output bits (" << outBits
2853 #define ImplBinPipeOpCellInterface(OpType, outName) \
2854 SmallVector<StringRef> OpType::portNames() { \
2855 return {clkPort, resetPort, goPort, "left", "right", outName, donePort}; \
2858 SmallVector<Direction> OpType::portDirections() { \
2859 return {Input, Input, Input, Input, Input, Output, Output}; \
2862 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
2863 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
2866 SmallVector<DictionaryAttr> OpType::portAttributes() { \
2867 MLIRContext *context = getContext(); \
2868 IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1); \
2869 NamedAttrList go, clk, reset, done; \
2870 go.append(goPort, isSet); \
2871 clk.append(clkPort, isSet); \
2872 reset.append(resetPort, isSet); \
2873 done.append(donePort, isSet); \
2875 clk.getDictionary(context),
\
2876 reset.getDictionary(context), \
2877 go.getDictionary(context), \
2878 DictionaryAttr::get(context), \
2879 DictionaryAttr::get(context), \
2880 DictionaryAttr::get(context), \
2881 done.getDictionary(context) \
2885 bool OpType::isCombinational() { return false; }
2887 #define ImplUnaryOpCellInterface(OpType) \
2888 SmallVector<StringRef> OpType::portNames() { return {"in", "out"}; } \
2889 SmallVector<Direction> OpType::portDirections() { return {Input, Output}; } \
2890 SmallVector<DictionaryAttr> OpType::portAttributes() { \
2891 return {DictionaryAttr::get(getContext()), \
2892 DictionaryAttr::get(getContext())}; \
2894 bool OpType::isCombinational() { return true; } \
2895 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
2896 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
2899 #define ImplBinOpCellInterface(OpType) \
2900 SmallVector<StringRef> OpType::portNames() { \
2901 return {"left", "right", "out"}; \
2903 SmallVector<Direction> OpType::portDirections() { \
2904 return {Input, Input, Output}; \
2906 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
2907 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
2909 bool OpType::isCombinational() { return true; } \
2910 SmallVector<DictionaryAttr> OpType::portAttributes() { \
2911 return {DictionaryAttr::get(getContext()), \
2912 DictionaryAttr::get(getContext()), \
2913 DictionaryAttr::get(getContext())}; \
2957 #include "circt/Dialect/Calyx/CalyxInterfaces.cpp.inc"
2960 #define GET_OP_CLASSES
2961 #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)
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:
Signals that the following operation is combinational.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
IntegerAttr packAttribute(MLIRContext *context, size_t nIns, size_t nOuts)
Returns an IntegerAttr containing the packed representation of the direction counts.
static constexpr std::string_view clkPort
LogicalResult verifyComponent(Operation *op)
A helper function to verify each operation with the Ccomponent trait.
static constexpr std::string_view donePort
LogicalResult verifyControlLikeOp(Operation *op)
A helper function to verify each control-like operation has a valid parent and, if applicable,...
LogicalResult verifyGroupInterface(Operation *op)
A helper function to verify each operation with the Group Interface trait.
LogicalResult verifyCell(Operation *op)
A helper function to verify each operation with the Cell trait.
static constexpr std::string_view resetPort
Direction
The direction of a Component or Cell port.
LogicalResult verifyIf(Operation *op)
A helper function to verify each operation with the If trait.
PortInfo getPortInfo(BlockArgument arg)
Returns port information for the block argument provided.
static constexpr std::string_view goPort
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