18 #include "mlir/IR/AsmState.h"
19 #include "mlir/IR/Builders.h"
20 #include "mlir/IR/BuiltinTypes.h"
21 #include "mlir/IR/Diagnostics.h"
22 #include "mlir/IR/DialectImplementation.h"
23 #include "mlir/IR/FunctionImplementation.h"
24 #include "mlir/IR/PatternMatch.h"
25 #include "mlir/IR/SymbolTable.h"
26 #include "mlir/Support/LLVM.h"
27 #include "llvm/ADT/DenseMap.h"
28 #include "llvm/ADT/STLExtras.h"
29 #include "llvm/ADT/SmallSet.h"
30 #include "llvm/ADT/StringExtras.h"
31 #include "llvm/ADT/TypeSwitch.h"
32 #include "llvm/Support/Casting.h"
34 using namespace circt;
50 size_t numDirections = nIns + nOuts;
51 APInt portDirections(numDirections, 0);
52 for (
size_t i = nIns, e = numDirections; i != e; ++i)
53 portDirections.setBit(i);
64 template <
typename CtrlOp>
68 PatternRewriter &rewriter)
const override {
69 auto &ops = ctrlOp.getBodyBlock()->getOperations();
70 bool isUnaryControl = (ops.size() == 1) && isa<EnableOp>(ops.front()) &&
71 isa<SeqOp, ParOp>(ctrlOp->getParentOp());
75 ops.front().moveBefore(ctrlOp);
76 rewriter.eraseOp(ctrlOp);
89 template <
typename Op>
91 Operation *definingOp = op.getSrc().getDefiningOp();
92 if (definingOp ==
nullptr)
98 if (
auto dialect = definingOp->getDialect(); isa<comb::CombDialect>(dialect))
99 return op->emitOpError(
"has source that is not a port or constant. "
100 "Complex logic should be conducted in the guard.");
107 static std::string
valueName(Operation *scopeOp, Value v) {
109 llvm::raw_string_ostream os(s);
114 AsmState asmState(scopeOp, OpPrintingFlags().assumeVerified());
115 v.printAsOperand(os, asmState);
122 Operation *definingOp = value.getDefiningOp();
123 return value.isa<BlockArgument>() ||
124 (definingOp && isa<CellInterface>(definingOp));
129 Operation *op = arg.getOwner()->getParentOp();
130 assert(isa<ComponentInterface>(op) &&
131 "Only ComponentInterface should support lookup by BlockArgument.");
132 return cast<ComponentInterface>(op).getPortInfo()[arg.getArgNumber()];
137 return isa<ControlOp, SeqOp, IfOp, WhileOp, ParOp>(op);
142 if (isa<SeqOp, ParOp>(op))
147 for (
auto ®ion : op->getRegions()) {
148 auto opsIt = region.getOps();
149 size_t numOperations = std::distance(opsIt.begin(), opsIt.end());
154 bool usesEnableAsCompositionOperator =
155 numOperations > 1 && llvm::any_of(region.front(), [](
auto &&bodyOp) {
156 return isa<EnableOp>(bodyOp);
158 if (usesEnableAsCompositionOperator)
159 return op->emitOpError(
160 "EnableOp is not a composition operator. It should be nested "
161 "in a control flow operation, such as \"calyx.seq\"");
165 size_t numControlFlowRegions =
167 if (numControlFlowRegions > 1)
168 return op->emitOpError(
169 "has an invalid control sequence. Multiple control flow operations "
170 "must all be nested in a single calyx.seq or calyx.par");
176 auto *opParent = op->getParentOp();
177 if (!isa<ModuleOp>(opParent))
178 return op->emitOpError()
179 <<
"has parent: " << opParent <<
", expected ModuleOp.";
184 auto opParent = op->getParentOp();
185 if (!isa<ComponentInterface>(opParent))
186 return op->emitOpError()
187 <<
"has parent: " << opParent <<
", expected ComponentInterface.";
192 auto parent = op->getParentOp();
194 if (isa<calyx::EnableOp>(op) &&
195 !isa<calyx::CalyxDialect>(parent->getDialect())) {
204 return op->emitOpError()
205 <<
"has parent: " << parent
206 <<
", which is not allowed for a control-like operation.";
208 if (op->getNumRegions() == 0)
211 auto ®ion = op->getRegion(0);
213 auto isValidBodyOp = [](Operation *operation) {
214 return isa<EnableOp, SeqOp, IfOp, WhileOp, ParOp>(operation);
216 for (
auto &&bodyOp : region.front()) {
217 if (isValidBodyOp(&bodyOp))
220 return op->emitOpError()
221 <<
"has operation: " << bodyOp.getName()
222 <<
", which is not allowed in this control-like operation";
232 SmallVector<OpAsmParser::UnresolvedOperand, 2> operandInfos;
233 OpAsmParser::UnresolvedOperand guardOrSource;
234 if (parser.parseOperand(guardOrSource))
237 if (succeeded(parser.parseOptionalQuestion())) {
238 OpAsmParser::UnresolvedOperand source;
240 if (parser.parseOperand(source))
242 operandInfos.push_back(source);
245 operandInfos.push_back(guardOrSource);
250 if (parser.parseColonType(type) ||
251 parser.resolveOperands(operandInfos, type, result.operands))
258 template <
typename GroupPortType>
260 static_assert(std::is_same<GroupGoOp, GroupPortType>() ||
261 std::is_same<GroupDoneOp, GroupPortType>(),
262 "Should be a Calyx Group port.");
266 Value guard = op.getGuard(), source = op.getSrc();
269 p << source <<
" : " << source.getType();
274 template <
typename OpTy>
276 PatternRewriter &rewriter) {
277 static_assert(std::is_same<SeqOp, OpTy>() || std::is_same<ParOp, OpTy>(),
278 "Should be a SeqOp or ParOp.");
280 if (isa<OpTy>(controlOp->getParentOp())) {
281 Block *controlBody = controlOp.getBodyBlock();
282 for (
auto &op : make_early_inc_range(*controlBody))
283 op.moveBefore(controlOp);
285 rewriter.eraseOp(controlOp);
292 template <
typename OpTy>
293 static LogicalResult
emptyControl(OpTy controlOp, PatternRewriter &rewriter) {
294 if (controlOp.getBodyBlock()->empty()) {
295 rewriter.eraseOp(controlOp);
304 template <
typename OpTy>
306 PatternRewriter &rewriter) {
307 static_assert(std::is_same<OpTy, IfOp>() || std::is_same<OpTy, WhileOp>(),
308 "This is only applicable to WhileOp and IfOp.");
311 Value cond = op.getCond();
312 std::optional<StringRef> groupName = op.getGroupName();
313 auto component = op->template getParentOfType<ComponentOp>();
314 rewriter.eraseOp(op);
318 auto group = component.getWiresOp().template lookupSymbol<GroupInterface>(
320 if (SymbolTable::symbolKnownUseEmpty(group, component.getRegion()))
321 rewriter.eraseOp(group);
324 if (!cond.isa<BlockArgument>() && cond.getDefiningOp()->use_empty())
325 rewriter.eraseOp(cond.getDefiningOp());
332 template <
typename ComponentTy>
334 auto componentName = comp->template getAttrOfType<StringAttr>(
335 ::mlir::SymbolTable::getSymbolAttrName())
338 p.printSymbolName(componentName);
341 auto printPortDefList = [&](
auto ports) {
343 llvm::interleaveComma(ports, p, [&](
const PortInfo &port) {
344 p <<
"%" << port.
name.getValue() <<
": " << port.
type;
347 p.printAttributeWithoutType(port.attributes);
352 printPortDefList(comp.getInputPortInfo());
354 printPortDefList(comp.getOutputPortInfo());
357 p.printRegion(*comp.getRegion(),
false,
361 SmallVector<StringRef> elidedAttrs = {
366 ComponentTy::getFunctionTypeAttrName(comp->getName()),
367 ComponentTy::getArgAttrsAttrName(comp->getName()),
368 ComponentTy::getResAttrsAttrName(comp->getName())};
369 p.printOptionalAttrDict(comp->getAttrs(), elidedAttrs);
376 SmallVectorImpl<OpAsmParser::Argument> &ports,
377 SmallVectorImpl<Type> &portTypes,
378 SmallVectorImpl<NamedAttrList> &portAttrs) {
379 auto parsePort = [&]() -> ParseResult {
380 OpAsmParser::Argument port;
383 if (parser.parseArgument(port) || parser.parseColon() ||
384 parser.parseType(portType))
386 port.type = portType;
387 ports.push_back(port);
388 portTypes.push_back(portType);
390 NamedAttrList portAttr;
391 portAttrs.push_back(succeeded(parser.parseOptionalAttrDict(portAttr))
397 return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
404 SmallVectorImpl<OpAsmParser::Argument> &ports,
405 SmallVectorImpl<Type> &portTypes) {
406 SmallVector<OpAsmParser::Argument> inPorts, outPorts;
407 SmallVector<Type> inPortTypes, outPortTypes;
408 SmallVector<NamedAttrList> portAttributes;
413 if (parser.parseArrow() ||
417 auto *context = parser.getBuilder().getContext();
420 SmallVector<Attribute> portNames;
421 auto getPortName = [context](
const auto &port) -> StringAttr {
422 StringRef name = port.ssaName.name;
423 if (name.startswith(
"%"))
424 name = name.drop_front();
427 llvm::transform(inPorts, std::back_inserter(portNames), getPortName);
428 llvm::transform(outPorts, std::back_inserter(portNames), getPortName);
430 result.addAttribute(
"portNames",
ArrayAttr::get(context, portNames));
435 ports.append(inPorts);
436 ports.append(outPorts);
437 portTypes.append(inPortTypes);
438 portTypes.append(outPortTypes);
440 SmallVector<Attribute> portAttrs;
441 llvm::transform(portAttributes, std::back_inserter(portAttrs),
442 [&](
auto attr) {
return attr.getDictionary(context); });
443 result.addAttribute(
"portAttributes",
ArrayAttr::get(context, portAttrs));
448 template <
typename ComponentTy>
450 OperationState &result) {
451 using namespace mlir::function_interface_impl;
453 StringAttr componentName;
454 if (parser.parseSymbolName(componentName,
455 ::mlir::SymbolTable::getSymbolAttrName(),
459 SmallVector<mlir::OpAsmParser::Argument> ports;
461 SmallVector<Type> portTypes;
467 auto type = parser.getBuilder().getFunctionType(portTypes, {});
468 result.addAttribute(ComponentTy::getFunctionTypeAttrName(result.name),
471 auto *body = result.addRegion();
472 if (parser.parseRegion(*body, ports))
476 body->push_back(
new Block());
478 if (parser.parseOptionalAttrDict(result.attributes))
485 template <
typename T>
486 static SmallVector<T>
concat(
const SmallVectorImpl<T> &a,
487 const SmallVectorImpl<T> &b) {
495 StringAttr name, ArrayRef<PortInfo> ports,
496 bool combinational) {
497 using namespace mlir::function_interface_impl;
499 result.addAttribute(::mlir::SymbolTable::getSymbolAttrName(), name);
501 std::pair<SmallVector<Type, 8>, SmallVector<Type, 8>> portIOTypes;
502 std::pair<SmallVector<Attribute, 8>, SmallVector<Attribute, 8>> portIONames;
503 std::pair<SmallVector<Attribute, 8>, SmallVector<Attribute, 8>>
505 SmallVector<Direction, 8> portDirections;
508 for (
auto &&port : ports) {
510 (isInput ? portIOTypes.first : portIOTypes.second).push_back(port.type);
511 (isInput ? portIONames.first : portIONames.second).push_back(port.name);
512 (isInput ? portIOAttributes.first : portIOAttributes.second)
513 .push_back(port.attributes);
515 auto portTypes =
concat(portIOTypes.first, portIOTypes.second);
516 auto portNames =
concat(portIONames.first, portIONames.second);
517 auto portAttributes =
concat(portIOAttributes.first, portIOAttributes.second);
520 auto functionType =
builder.getFunctionType(portTypes, {});
522 result.addAttribute(CombComponentOp::getFunctionTypeAttrName(result.name),
525 result.addAttribute(ComponentOp::getFunctionTypeAttrName(result.name),
530 result.addAttribute(
"portNames",
builder.getArrayAttr(portNames));
531 result.addAttribute(
"portDirections",
533 portIOTypes.first.size(),
534 portIOTypes.second.size()));
536 result.addAttribute(
"portAttributes",
builder.getArrayAttr(portAttributes));
539 Region *region = result.addRegion();
540 Block *body =
new Block();
541 region->push_back(body);
544 body->addArguments(portTypes, SmallVector<Location, 4>(
545 portTypes.size(),
builder.getUnknownLoc()));
548 IRRewriter::InsertionGuard guard(
builder);
549 builder.setInsertionPointToStart(body);
550 builder.create<WiresOp>(result.location);
552 builder.create<ControlOp>(result.location);
563 template <
typename Op>
565 auto *body = op.getBodyBlock();
568 auto opIt = body->getOps<Op>().begin();
575 ArrayAttr portNames = op.getPortNames();
577 for (
size_t i = 0, e = portNames.size(); i != e; ++i) {
578 auto portName = portNames[i].cast<StringAttr>();
579 if (portName.getValue() == name)
580 return op.getBodyBlock()->getArgument(i);
585 WiresOp calyx::ComponentOp::getWiresOp() {
586 return getControlOrWiresFrom<WiresOp>(*
this);
589 ControlOp calyx::ComponentOp::getControlOp() {
590 return getControlOrWiresFrom<ControlOp>(*
this);
593 Value calyx::ComponentOp::getGoPort() {
597 Value calyx::ComponentOp::getDonePort() {
601 Value calyx::ComponentOp::getClkPort() {
605 Value calyx::ComponentOp::getResetPort() {
610 auto portTypes = getArgumentTypes();
611 ArrayAttr portNamesAttr = getPortNames(), portAttrs = getPortAttributes();
612 APInt portDirectionsAttr = getPortDirections();
614 SmallVector<PortInfo> results;
615 for (
size_t i = 0, e = portNamesAttr.size(); i != e; ++i) {
616 results.push_back(
PortInfo{portNamesAttr[i].cast<StringAttr>(),
619 portAttrs[i].cast<DictionaryAttr>()});
625 template <
typename Pred>
627 SmallVector<PortInfo> ports = op.getPortInfo();
628 llvm::erase_if(ports, p);
632 SmallVector<PortInfo> ComponentOp::getInputPortInfo() {
637 SmallVector<PortInfo> ComponentOp::getOutputPortInfo() {
642 void ComponentOp::print(OpAsmPrinter &p) {
643 printComponentInterface<ComponentOp>(p, *
this);
646 ParseResult ComponentOp::parse(OpAsmParser &parser, OperationState &result) {
647 return parseComponentInterface<ComponentOp>(parser, result);
653 llvm::SmallVector<StringRef, 4> identifiers;
654 for (
PortInfo &port : op.getPortInfo()) {
655 auto portIds = port.getAllIdentifiers();
656 identifiers.append(portIds.begin(), portIds.end());
659 std::sort(identifiers.begin(), identifiers.end());
662 interfacePorts{
"clk",
"done",
"go",
"reset"};
664 std::set_intersection(interfacePorts.begin(), interfacePorts.end(),
665 identifiers.begin(), identifiers.end(),
671 SmallVector<StringRef, 4> difference;
672 std::set_difference(interfacePorts.begin(), interfacePorts.end(),
674 std::back_inserter(difference));
675 return op->emitOpError()
676 <<
"is missing the following required port attribute identifiers: "
680 LogicalResult ComponentOp::verify() {
682 auto wIt = getBodyBlock()->getOps<WiresOp>();
683 auto cIt = getBodyBlock()->getOps<ControlOp>();
684 if (std::distance(wIt.begin(), wIt.end()) +
685 std::distance(cIt.begin(), cIt.end()) !=
687 return emitOpError() <<
"requires exactly one of each: '"
688 << WiresOp::getOperationName() <<
"', '"
689 << ControlOp::getOperationName() <<
"'.";
696 bool hasNoControlConstructs =
697 getControlOp().getBodyBlock()->getOperations().empty();
698 bool hasNoAssignments =
699 getWiresOp().getBodyBlock()->getOps<AssignOp>().empty();
700 if (hasNoControlConstructs && hasNoAssignments)
702 "The component currently does nothing. It needs to either have "
703 "continuous assignments in the Wires region or control constructs in "
704 "the Control region.");
709 void ComponentOp::build(OpBuilder &
builder, OperationState &result,
710 StringAttr name, ArrayRef<PortInfo> ports) {
714 void ComponentOp::getAsmBlockArgumentNames(
718 auto ports = getPortNames();
719 auto *block = &getRegion()->front();
720 for (
size_t i = 0, e = block->getNumArguments(); i != e; ++i)
721 setNameFn(block->getArgument(i), ports[i].cast<StringAttr>().getValue());
729 auto portTypes = getArgumentTypes();
730 ArrayAttr portNamesAttr = getPortNames(), portAttrs = getPortAttributes();
731 APInt portDirectionsAttr = getPortDirections();
733 SmallVector<PortInfo> results;
734 for (
size_t i = 0, e = portNamesAttr.size(); i != e; ++i) {
735 results.push_back(
PortInfo{portNamesAttr[i].cast<StringAttr>(),
738 portAttrs[i].cast<DictionaryAttr>()});
743 WiresOp calyx::CombComponentOp::getWiresOp() {
744 auto *body = getBodyBlock();
745 auto opIt = body->getOps<WiresOp>().begin();
750 template <
typename Pred>
752 SmallVector<PortInfo> ports = op.getPortInfo();
753 llvm::erase_if(ports, p);
757 SmallVector<PortInfo> CombComponentOp::getInputPortInfo() {
762 SmallVector<PortInfo> CombComponentOp::getOutputPortInfo() {
767 void CombComponentOp::print(OpAsmPrinter &p) {
768 printComponentInterface<CombComponentOp>(p, *
this);
771 ParseResult CombComponentOp::parse(OpAsmParser &parser,
772 OperationState &result) {
773 return parseComponentInterface<CombComponentOp>(parser, result);
776 LogicalResult CombComponentOp::verify() {
778 auto wIt = getBodyBlock()->getOps<WiresOp>();
779 if (std::distance(wIt.begin(), wIt.end()) != 1)
780 return emitOpError() <<
"requires exactly one "
781 << WiresOp::getOperationName() <<
" op.";
784 auto cIt = getBodyBlock()->getOps<ControlOp>();
785 if (std::distance(cIt.begin(), cIt.end()) != 0)
786 return emitOpError() <<
"must not have a `" << ControlOp::getOperationName()
790 bool hasNoAssignments =
791 getWiresOp().getBodyBlock()->getOps<AssignOp>().empty();
792 if (hasNoAssignments)
794 "The component currently does nothing. It needs to either have "
795 "continuous assignments in the Wires region or control constructs in "
796 "the Control region.");
799 auto cells = getOps<CellInterface>();
800 for (
auto cell : cells) {
801 if (!cell.isCombinational())
802 return emitOpError() <<
"contains non-combinational cell "
803 << cell.instanceName();
807 auto groups = getWiresOp().getOps<GroupOp>();
809 return emitOpError() <<
"contains group " << (*groups.begin()).getSymName();
814 auto combGroups = getWiresOp().getOps<CombGroupOp>();
815 if (!combGroups.empty())
816 return emitOpError() <<
"contains comb group "
817 << (*combGroups.begin()).getSymName();
822 void CombComponentOp::build(OpBuilder &
builder, OperationState &result,
823 StringAttr name, ArrayRef<PortInfo> ports) {
827 void CombComponentOp::getAsmBlockArgumentNames(
831 auto ports = getPortNames();
832 auto *block = &getRegion()->front();
833 for (
size_t i = 0, e = block->getNumArguments(); i != e; ++i)
834 setNameFn(block->getArgument(i), ports[i].cast<StringAttr>().getValue());
846 void SeqOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
847 MLIRContext *context) {
848 patterns.add(collapseControl<SeqOp>);
857 LogicalResult ParOp::verify() {
862 for (EnableOp op : getBodyBlock()->getOps<EnableOp>()) {
863 StringRef groupName = op.getGroupName();
864 if (groupNames.count(groupName))
865 return emitOpError() <<
"cannot enable the same group: \"" << groupName
866 <<
"\" more than once.";
867 groupNames.insert(groupName);
873 void ParOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
874 MLIRContext *context) {
875 patterns.add(collapseControl<ParOp>);
883 LogicalResult WiresOp::verify() {
884 auto componentInterface = (*this)->getParentOfType<ComponentInterface>();
885 if (llvm::isa<ComponentOp>(componentInterface)) {
886 auto component = llvm::cast<ComponentOp>(componentInterface);
887 auto control = component.getControlOp();
890 for (
auto &&op : *getBodyBlock()) {
891 if (!isa<GroupInterface>(op))
893 auto group = cast<GroupInterface>(op);
894 auto groupName = group.symName();
895 if (mlir::SymbolTable::symbolKnownUseEmpty(groupName, control))
896 return op.emitOpError()
897 <<
"with name: " << groupName
898 <<
" is unused in the control execution schedule";
905 for (
auto thisAssignment : getBodyBlock()->getOps<AssignOp>()) {
909 if (thisAssignment.getGuard())
912 Value dest = thisAssignment.getDest();
913 for (Operation *user : dest.getUsers()) {
914 auto assignUser = dyn_cast<AssignOp>(user);
915 if (!assignUser || assignUser.getDest() != dest ||
916 assignUser == thisAssignment)
919 return user->emitOpError() <<
"destination is already continuously "
920 "driven. Other assignment is "
934 Operation *definingOp = value.getDefiningOp();
935 if (definingOp ==
nullptr || definingOp->hasTrait<
Combinational>())
941 if (isa<InstanceOp>(definingOp))
945 if (isa<comb::CombDialect, hw::HWDialect>(definingOp->getDialect()))
949 if (
auto r = dyn_cast<RegisterOp>(definingOp)) {
950 return value == r.getOut()
952 : group->emitOpError()
953 <<
"with register: \"" << r.instanceName()
954 <<
"\" is conducting a memory store. This is not "
956 }
else if (
auto m = dyn_cast<MemoryOp>(definingOp)) {
957 auto writePorts = {m.writeData(), m.writeEn()};
958 return (llvm::none_of(writePorts, [&](Value p) {
return p == value; }))
960 : group->emitOpError()
961 <<
"with memory: \"" << m.instanceName()
962 <<
"\" is conducting a memory store. This "
963 "is not combinational.";
966 std::string portName =
967 valueName(group->getParentOfType<ComponentOp>(), value);
968 return group->emitOpError() <<
"with port: " << portName
969 <<
". This operation is not combinational.";
974 LogicalResult CombGroupOp::verify() {
975 for (
auto &&op : *getBodyBlock()) {
976 auto assign = dyn_cast<AssignOp>(op);
977 if (assign ==
nullptr)
979 Value dst = assign.getDest(), src = assign.getSrc();
990 GroupGoOp GroupOp::getGoOp() {
991 auto goOps = getBodyBlock()->getOps<GroupGoOp>();
992 size_t nOps = std::distance(goOps.begin(), goOps.end());
993 return nOps ? *goOps.begin() : GroupGoOp();
996 GroupDoneOp GroupOp::getDoneOp() {
997 auto body = this->getBodyBlock();
998 return cast<GroupDoneOp>(body->getTerminator());
1009 return llvm::any_of(port.getUses(), [&](
auto &&use) {
1010 auto assignOp = dyn_cast<AssignOp>(use.getOwner());
1011 if (assignOp == nullptr)
1014 Operation *parent = assignOp->getParentOp();
1015 if (isa<WiresOp>(parent))
1024 Value expected = isDriven ? assignOp.getDest() : assignOp.getSrc();
1025 return expected == port && group == parent;
1037 if (
auto cell = dyn_cast<CellInterface>(port.getDefiningOp());
1039 return groupOp.drivesAnyPort(cell.getInputPorts());
1044 LogicalResult GroupOp::drivesPort(Value port) {
1048 LogicalResult CombGroupOp::drivesPort(Value port) {
1055 return success(llvm::all_of(ports, [&](Value port) {
1060 LogicalResult GroupOp::drivesAllPorts(ValueRange ports) {
1064 LogicalResult CombGroupOp::drivesAllPorts(ValueRange ports) {
1071 return success(llvm::any_of(ports, [&](Value port) {
1076 LogicalResult GroupOp::drivesAnyPort(ValueRange ports) {
1080 LogicalResult CombGroupOp::drivesAnyPort(ValueRange ports) {
1087 return success(llvm::any_of(ports, [&](Value port) {
1092 LogicalResult GroupOp::readsAnyPort(ValueRange ports) {
1096 LogicalResult CombGroupOp::readsAnyPort(ValueRange ports) {
1103 GroupInterface group) {
1104 Operation *destDefiningOp = assign.getDest().getDefiningOp();
1105 if (destDefiningOp ==
nullptr)
1107 auto destCell = dyn_cast<CellInterface>(destDefiningOp);
1108 if (destCell ==
nullptr)
1111 LogicalResult verifyWrites =
1112 TypeSwitch<Operation *, LogicalResult>(destCell)
1113 .Case<RegisterOp>([&](
auto op) {
1116 return succeeded(group.drivesAnyPort({op.getWriteEn(), op.getIn()}))
1117 ? group.drivesAllPorts({op.getWriteEn(), op.getIn()})
1120 .Case<MemoryOp>([&](
auto op) {
1121 SmallVector<Value> requiredWritePorts;
1124 requiredWritePorts.push_back(op.writeEn());
1125 requiredWritePorts.push_back(op.writeData());
1126 for (Value address : op.addrPorts())
1127 requiredWritePorts.push_back(address);
1132 group.drivesAnyPort({op.writeData(), op.writeEn()}))
1133 ? group.drivesAllPorts(requiredWritePorts)
1136 .Case<AndLibOp, OrLibOp, XorLibOp, AddLibOp, SubLibOp, GtLibOp,
1137 LtLibOp, EqLibOp, NeqLibOp, GeLibOp, LeLibOp, LshLibOp,
1138 RshLibOp, SgtLibOp, SltLibOp, SeqLibOp, SneqLibOp, SgeLibOp,
1139 SleLibOp, SrshLibOp>([&](
auto op) {
1140 Value lhs = op.getLeft(), rhs = op.getRight();
1141 return succeeded(group.drivesAnyPort({lhs, rhs}))
1142 ? group.drivesAllPorts({lhs, rhs})
1145 .Default([&](
auto op) {
return success(); });
1147 if (failed(verifyWrites))
1148 return group->emitOpError()
1149 <<
"with cell: " << destCell->getName() <<
" \""
1150 << destCell.instanceName()
1151 <<
"\" is performing a write and failed to drive all necessary "
1154 Operation *srcDefiningOp = assign.getSrc().getDefiningOp();
1155 if (srcDefiningOp ==
nullptr)
1157 auto srcCell = dyn_cast<CellInterface>(srcDefiningOp);
1158 if (srcCell ==
nullptr)
1161 LogicalResult verifyReads =
1162 TypeSwitch<Operation *, LogicalResult>(srcCell)
1163 .Case<MemoryOp>([&](
auto op) {
1167 return succeeded(group.readsAnyPort({op.readData()}))
1168 ? group.drivesAllPorts(op.addrPorts())
1171 .Default([&](
auto op) {
return success(); });
1173 if (failed(verifyReads))
1174 return group->emitOpError() <<
"with cell: " << srcCell->getName() <<
" \""
1175 << srcCell.instanceName()
1176 <<
"\" is having a read performed upon it, and "
1177 "failed to drive all necessary ports.";
1183 auto group = dyn_cast<GroupInterface>(op);
1184 if (group ==
nullptr)
1187 for (
auto &&groupOp : *group.getBody()) {
1188 auto assign = dyn_cast<AssignOp>(groupOp);
1189 if (assign ==
nullptr)
1205 ArrayRef<StringRef> portNames) {
1206 auto cellInterface = dyn_cast<CellInterface>(op);
1207 assert(cellInterface &&
"must implement the Cell interface");
1209 std::string prefix = cellInterface.instanceName().str() +
".";
1210 for (
size_t i = 0, e = portNames.size(); i != e; ++i)
1211 setNameFn(op->getResult(i), prefix + portNames[i].str());
1222 bool isDestination) {
1223 Operation *definingOp = value.getDefiningOp();
1224 bool isComponentPort = value.isa<BlockArgument>(),
1225 isCellInterfacePort = definingOp && isa<CellInterface>(definingOp);
1226 assert((isComponentPort || isCellInterfacePort) &&
"Not a port.");
1230 : cast<CellInterface>(definingOp).portInfo(value);
1232 bool isSource = !isDestination;
1235 (isDestination && isComponentPort) || (isSource && isCellInterfacePort)
1242 <<
"has a " << (isComponentPort ?
"component" :
"cell")
1244 << (isDestination ?
"destination" :
"source")
1245 <<
" with the incorrect direction.";
1252 bool isSource = !isDestination;
1253 Value value = isDestination ? op.getDest() : op.getSrc();
1258 if (isDestination && !isa<GroupGoOp, GroupDoneOp>(value.getDefiningOp()))
1259 return op->emitOpError(
1260 "has an invalid destination port. It must be drive-able.");
1267 LogicalResult AssignOp::verify() {
1268 bool isDestination =
true, isSource =
false;
1277 ParseResult AssignOp::parse(OpAsmParser &parser, OperationState &result) {
1278 OpAsmParser::UnresolvedOperand destination;
1279 if (parser.parseOperand(destination) || parser.parseEqual())
1285 OpAsmParser::UnresolvedOperand guardOrSource;
1286 if (parser.parseOperand(guardOrSource))
1291 OpAsmParser::UnresolvedOperand source;
1292 bool hasGuard = succeeded(parser.parseOptionalQuestion());
1295 if (parser.parseOperand(source))
1300 if (parser.parseColonType(type) ||
1301 parser.resolveOperand(destination, type, result.operands))
1305 Type i1Type = parser.getBuilder().getI1Type();
1308 if (parser.resolveOperand(source, type, result.operands) ||
1309 parser.resolveOperand(guardOrSource, i1Type, result.operands))
1313 if (parser.resolveOperand(guardOrSource, type, result.operands))
1320 void AssignOp::print(OpAsmPrinter &p) {
1321 p <<
" " << getDest() <<
" = ";
1323 Value bguard = getGuard(), source = getSrc();
1326 p << bguard <<
" ? ";
1330 p << source <<
" : " << source.getType();
1339 ComponentInterface InstanceOp::getReferencedComponent() {
1340 auto module = (*this)->getParentOfType<ModuleOp>();
1344 return module.lookupSymbol<ComponentInterface>(getComponentName());
1350 static LogicalResult
1352 ComponentInterface referencedComponent) {
1353 auto module = instance->getParentOfType<ModuleOp>();
1354 StringRef entryPointName =
1355 module->getAttrOfType<StringAttr>(
"calyx.entrypoint");
1356 if (instance.getComponentName() == entryPointName)
1357 return instance.emitOpError()
1358 <<
"cannot reference the entry-point component: '" << entryPointName
1362 SmallVector<PortInfo> componentPorts = referencedComponent.getPortInfo();
1363 size_t numPorts = componentPorts.size();
1365 size_t numResults = instance.getNumResults();
1366 if (numResults != numPorts)
1367 return instance.emitOpError()
1368 <<
"has a wrong number of results; expected: " << numPorts
1369 <<
" but got " << numResults;
1371 for (
size_t i = 0; i != numResults; ++i) {
1372 auto resultType = instance.getResult(i).getType();
1373 auto expectedType = componentPorts[i].type;
1374 if (resultType == expectedType)
1376 return instance.emitOpError()
1377 <<
"result type for " << componentPorts[i].name <<
" must be "
1378 << expectedType <<
", but got " << resultType;
1383 LogicalResult InstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1384 Operation *op = *
this;
1385 auto module = op->getParentOfType<ModuleOp>();
1386 Operation *referencedComponent =
1387 symbolTable.lookupNearestSymbolFrom(module, getComponentNameAttr());
1388 if (referencedComponent ==
nullptr)
1389 return emitError() <<
"referencing component: '" << getComponentName()
1390 <<
"', which does not exist.";
1392 Operation *shadowedComponentName =
1393 symbolTable.lookupNearestSymbolFrom(module, getSymNameAttr());
1394 if (shadowedComponentName !=
nullptr)
1395 return emitError() <<
"instance symbol: '" << instanceName()
1396 <<
"' is already a symbol for another component.";
1399 auto parentComponent = op->getParentOfType<ComponentOp>();
1400 if (parentComponent == referencedComponent)
1401 return emitError() <<
"recursive instantiation of its parent component: '"
1402 << getComponentName() <<
"'";
1404 assert(isa<ComponentInterface>(referencedComponent) &&
1405 "Should be a ComponentInterface.");
1407 cast<ComponentInterface>(referencedComponent));
1415 SmallVector<StringRef> InstanceOp::portNames() {
1416 SmallVector<StringRef> portNames;
1417 for (Attribute name : getReferencedComponent().getPortNames())
1418 portNames.push_back(name.cast<StringAttr>().getValue());
1422 SmallVector<Direction> InstanceOp::portDirections() {
1423 SmallVector<Direction> portDirections;
1425 portDirections.push_back(port.direction);
1426 return portDirections;
1429 SmallVector<DictionaryAttr> InstanceOp::portAttributes() {
1430 SmallVector<DictionaryAttr> portAttributes;
1432 portAttributes.push_back(port.attributes);
1433 return portAttributes;
1437 return isa<CombComponentOp>(getReferencedComponent());
1446 hw::HWModuleExternOp PrimitiveOp::getReferencedPrimitive() {
1447 auto module = (*this)->getParentOfType<ModuleOp>();
1451 return module.lookupSymbol<hw::HWModuleExternOp>(getPrimitiveName());
1457 static LogicalResult
1459 hw::HWModuleExternOp referencedPrimitive) {
1460 auto module = instance->getParentOfType<ModuleOp>();
1461 StringRef entryPointName =
1462 module->getAttrOfType<StringAttr>(
"calyx.entrypoint");
1463 if (instance.getPrimitiveName() == entryPointName)
1464 return instance.emitOpError()
1465 <<
"cannot reference the entry-point component: '" << entryPointName
1469 SmallVector<hw::PortInfo> primitivePorts = referencedPrimitive.getAllPorts();
1470 size_t numPorts = primitivePorts.size();
1472 size_t numResults = instance.getNumResults();
1473 if (numResults != numPorts)
1474 return instance.emitOpError()
1475 <<
"has a wrong number of results; expected: " << numPorts
1476 <<
" but got " << numResults;
1479 ArrayAttr modParameters = referencedPrimitive.getParameters();
1480 ArrayAttr parameters = instance.getParameters().value_or(ArrayAttr());
1481 size_t numExpected = modParameters.size();
1482 size_t numParams = parameters.size();
1483 if (numParams != numExpected)
1484 return instance.emitOpError()
1485 <<
"has the wrong number of parameters; expected: " << numExpected
1486 <<
" but got " << numParams;
1488 for (
size_t i = 0; i != numExpected; ++i) {
1489 auto param = parameters[i].cast<circt::hw::ParamDeclAttr>();
1490 auto modParam = modParameters[i].cast<circt::hw::ParamDeclAttr>();
1492 auto paramName = param.getName();
1493 if (paramName != modParam.getName())
1494 return instance.emitOpError()
1495 <<
"parameter #" << i <<
" should have name " << modParam.getName()
1496 <<
" but has name " << paramName;
1498 if (param.getType() != modParam.getType())
1499 return instance.emitOpError()
1500 <<
"parameter " << paramName <<
" should have type "
1501 << modParam.getType() <<
" but has type " << param.getType();
1505 if (!param.getValue())
1506 return instance.emitOpError(
"parameter ")
1507 << paramName <<
" must have a value";
1510 for (
size_t i = 0; i != numResults; ++i) {
1511 auto resultType = instance.getResult(i).getType();
1512 auto expectedType = primitivePorts[i].type;
1514 instance.getLoc(), instance.getParametersAttr(), expectedType);
1515 if (failed(replacedType))
1517 if (resultType == replacedType)
1519 return instance.emitOpError()
1520 <<
"result type for " << primitivePorts[i].name <<
" must be "
1521 << expectedType <<
", but got " << resultType;
1527 PrimitiveOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1528 Operation *op = *
this;
1529 auto module = op->getParentOfType<ModuleOp>();
1530 Operation *referencedPrimitive =
1531 symbolTable.lookupNearestSymbolFrom(module, getPrimitiveNameAttr());
1532 if (referencedPrimitive ==
nullptr)
1533 return emitError() <<
"referencing primitive: '" << getPrimitiveName()
1534 <<
"', which does not exist.";
1536 Operation *shadowedPrimitiveName =
1537 symbolTable.lookupNearestSymbolFrom(module, getSymNameAttr());
1538 if (shadowedPrimitiveName !=
nullptr)
1539 return emitError() <<
"instance symbol: '" << instanceName()
1540 <<
"' is already a symbol for another primitive.";
1543 auto parentPrimitive = op->getParentOfType<hw::HWModuleExternOp>();
1544 if (parentPrimitive == referencedPrimitive)
1545 return emitError() <<
"recursive instantiation of its parent primitive: '"
1546 << getPrimitiveName() <<
"'";
1548 assert(isa<hw::HWModuleExternOp>(referencedPrimitive) &&
1549 "Should be a HardwareModuleExternOp.");
1552 cast<hw::HWModuleExternOp>(referencedPrimitive));
1560 SmallVector<StringRef> PrimitiveOp::portNames() {
1561 SmallVector<StringRef> portNames;
1562 for (hw::PortInfo port : getReferencedPrimitive().getAllPorts())
1563 portNames.push_back(port.name.getValue());
1569 switch (direction) {
1575 llvm_unreachable(
"InOut ports not supported by Calyx");
1577 llvm_unreachable(
"Impossible port type");
1580 SmallVector<Direction> PrimitiveOp::portDirections() {
1581 SmallVector<Direction> portDirections;
1582 for (hw::PortInfo port : getReferencedPrimitive().getAllPorts())
1584 return portDirections;
1593 DictionaryAttr dict) {
1597 llvm::SmallVector<NamedAttribute> attrs;
1598 for (NamedAttribute attr : dict) {
1599 Dialect *dialect = attr.getNameDialect();
1600 if (dialect ==
nullptr || !isa<CalyxDialect>(*dialect))
1602 StringRef name = attr.getName().strref();
1603 StringAttr newName =
builder.getStringAttr(std::get<1>(name.split(
".")));
1604 attr.setName(newName);
1605 attrs.push_back(attr);
1607 return builder.getDictionaryAttr(attrs);
1611 SmallVector<DictionaryAttr> PrimitiveOp::portAttributes() {
1612 SmallVector<DictionaryAttr> portAttributes;
1613 OpBuilder
builder(getContext());
1614 hw::HWModuleExternOp prim = getReferencedPrimitive();
1615 for (
size_t i = 0, e = prim.getNumArguments(); i != e; ++i) {
1617 portAttributes.push_back(dict);
1619 for (
size_t i = 0, e = prim.getNumResults(); i != e; ++i) {
1620 DictionaryAttr dict =
1622 portAttributes.push_back(dict);
1624 return portAttributes;
1633 SmallVector<Attribute> ¶meters) {
1635 return parser.parseCommaSeparatedList(
1636 OpAsmParser::Delimiter::OptionalLessGreater, [&]() {
1641 if (parser.parseKeywordOrString(&name) || parser.parseColonType(type))
1645 if (succeeded(parser.parseOptionalEqual())) {
1646 if (parser.parseAttribute(value, type))
1650 auto &
builder = parser.getBuilder();
1652 builder.getContext(),
builder.getStringAttr(name), type, value));
1659 ArrayAttr ¶meters) {
1660 SmallVector<Attribute> parseParameters;
1664 parameters =
ArrayAttr::get(parser.getContext(), parseParameters);
1671 ArrayAttr parameters) {
1672 if (parameters.empty())
1676 llvm::interleaveComma(parameters, p, [&](Attribute param) {
1677 auto paramAttr = param.cast<hw::ParamDeclAttr>();
1678 p << paramAttr.getName().getValue() <<
": " << paramAttr.getType();
1679 if (
auto value = paramAttr.getValue()) {
1681 p.printAttributeWithoutType(value);
1687 //===----------------------------------------------------------------------===//
1689 //===----------------------------------------------------------------------===//
1691 LogicalResult GroupGoOp::verify() { return verifyNotComplexSource(*this); }
1694 void GroupGoOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1695 auto parent = (*this)->getParentOfType<GroupOp>();
1696 StringRef name = parent.getSymName();
1697 std::string resultName = name.str() + ".go";
1698 setNameFn(getResult(), resultName);
1701 void GroupGoOp::print(OpAsmPrinter &p) { printGroupPort(p, *this); }
1703 ParseResult GroupGoOp::parse(OpAsmParser &parser, OperationState &result) {
1704 if (parseGroupPort(parser, result))
1707 result.addTypes(parser.getBuilder().getI1Type());
1711 //===----------------------------------------------------------------------===//
1713 //===----------------------------------------------------------------------===//
1715 LogicalResult GroupDoneOp::verify() {
1716 Operation *srcOp = getSrc().getDefiningOp();
1717 Value optionalGuard = getGuard();
1718 Operation *guardOp = optionalGuard ? optionalGuard.getDefiningOp() : nullptr;
1719 bool noGuard = (guardOp == nullptr);
1721 if (srcOp == nullptr)
1722 // This is a port of the parent component.
1725 if (isa<hw::ConstantOp>(srcOp) && (noGuard || isa<hw::ConstantOp>(guardOp)))
1726 return emitOpError() << "with constant source"
1727 << (noGuard ? "" : " and constant guard")
1728 << ". This should be a combinational group.";
1730 return verifyNotComplexSource(*this);
1733 void GroupDoneOp::print(OpAsmPrinter &p) { printGroupPort(p, *this); }
1735 ParseResult GroupDoneOp::parse(OpAsmParser &parser, OperationState &result) {
1736 return parseGroupPort(parser, result);
1739 //===----------------------------------------------------------------------===//
1741 //===----------------------------------------------------------------------===//
1744 void RegisterOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1745 getCellAsmResultNames(setNameFn, *this, this->portNames());
1748 SmallVector<StringRef> RegisterOp::portNames() {
1749 return {"in", "write_en", "clk", "reset", "out", "done"};
1752 SmallVector<Direction> RegisterOp::portDirections() {
1753 return {Input, Input, Input, Input, Output, Output};
1756 SmallVector<DictionaryAttr> RegisterOp::portAttributes() {
1757 MLIRContext *context = getContext();
1758 IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1);
1759 NamedAttrList writeEn, clk, reset, done;
1760 writeEn.append("go", isSet);
1761 clk.append("clk", isSet);
1762 reset.append("reset", isSet);
1763 done.append("done", isSet);
1765 DictionaryAttr::get(context), // In
1766 writeEn.getDictionary(context), // Write enable
1767 clk.getDictionary(context), // Clk
1768 reset.getDictionary(context), // Reset
1769 DictionaryAttr::get(context), // Out
1770 done.getDictionary(context) // Done
1774 bool RegisterOp::isCombinational() { return false; }
1776 //===----------------------------------------------------------------------===//
1778 //===----------------------------------------------------------------------===//
1781 void MemoryOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1782 getCellAsmResultNames(setNameFn, *this, this->portNames());
1785 SmallVector<StringRef> MemoryOp::portNames() {
1786 SmallVector<StringRef> portNames;
1787 for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i) {
1789 StringAttr::get(this->getContext(), "addr" + std::to_string(i));
1790 portNames.push_back(nameAttr.getValue());
1792 portNames.append({"write_data", "write_en", "clk", "read_data", "done"});
1796 SmallVector<Direction> MemoryOp::portDirections() {
1797 SmallVector<Direction> portDirections;
1798 for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
1799 portDirections.push_back(Input);
1800 portDirections.append({Input, Input, Input, Output, Output});
1801 return portDirections;
1804 SmallVector<DictionaryAttr> MemoryOp::portAttributes() {
1805 SmallVector<DictionaryAttr> portAttributes;
1806 MLIRContext *context = getContext();
1807 for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
1808 portAttributes.push_back(DictionaryAttr::get(context)); // Addresses
1810 // Use a boolean to indicate this attribute is used.
1811 IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1);
1812 NamedAttrList writeEn, clk, reset, done;
1813 writeEn.append("go", isSet);
1814 clk.append("clk", isSet);
1815 done.append("done", isSet);
1816 portAttributes.append({DictionaryAttr::get(context), // In
1817 writeEn.getDictionary(context), // Write enable
1818 clk.getDictionary(context), // Clk
1819 DictionaryAttr::get(context), // Out
1820 done.getDictionary(context)} // Done
1822 return portAttributes;
1825 bool MemoryOp::isCombinational() { return false; }
1827 void MemoryOp::build(OpBuilder &builder, OperationState &state,
1828 StringRef instanceName, int64_t width,
1829 ArrayRef<int64_t> sizes, ArrayRef<int64_t> addrSizes) {
1830 state.addAttribute(SymbolTable::getSymbolAttrName(),
1831 builder.getStringAttr(instanceName));
1832 state.addAttribute("width", builder.getI64IntegerAttr(width));
1833 state.addAttribute("sizes", builder.getI64ArrayAttr(sizes));
1834 state.addAttribute("addrSizes", builder.getI64ArrayAttr(addrSizes));
1835 SmallVector<Type> types;
1836 for (int64_t size : addrSizes)
1837 types.push_back(builder.getIntegerType(size)); // Addresses
1838 types.push_back(builder.getIntegerType(width)); // Write data
1839 types.push_back(builder.getI1Type()); // Write enable
1840 types.push_back(builder.getI1Type()); // Clk
1841 types.push_back(builder.getIntegerType(width)); // Read data
1842 types.push_back(builder.getI1Type()); // Done
1843 state.addTypes(types);
1846 LogicalResult MemoryOp::verify() {
1847 ArrayRef<Attribute> opSizes = getSizes().getValue();
1848 ArrayRef<Attribute> opAddrSizes = getAddrSizes().getValue();
1849 size_t numDims = getSizes().size();
1850 size_t numAddrs = getAddrSizes().size();
1851 if (numDims != numAddrs)
1852 return emitOpError("mismatched number of dimensions (")
1853 << numDims << ") and address sizes (" << numAddrs << ")";
1855 size_t numExtraPorts = 5; // write data/enable, clk, and read data/done.
1856 if (getNumResults() != numAddrs + numExtraPorts)
1857 return emitOpError("incorrect number of address ports, expected ")
1860 for (size_t i = 0; i < numDims; ++i) {
1861 int64_t size = opSizes[i].cast<IntegerAttr>().getInt();
1862 int64_t addrSize = opAddrSizes[i].cast<IntegerAttr>().getInt();
1863 if (llvm::Log2_64_Ceil(size) > addrSize)
1864 return emitOpError("address size (")
1865 << addrSize << ") for dimension " << i
1866 << " can't address the entire range (
" << size << ")
";
1872 //===----------------------------------------------------------------------===//
1874 //===----------------------------------------------------------------------===//
1875 LogicalResult EnableOp::verify() {
1876 auto component = (*this)->getParentOfType<ComponentOp>();
1877 auto wiresOp = component.getWiresOp();
1878 StringRef name = getGroupName();
1880 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(name);
1882 return emitOpError() << "with group
'" << name
1883 << "', which does not exist.
";
1885 if (isa<CombGroupOp>(groupOp))
1886 return emitOpError() << "with group
'" << name
1887 << "', which is a combinational group.
";
1892 //===----------------------------------------------------------------------===//
1894 //===----------------------------------------------------------------------===//
1896 LogicalResult IfOp::verify() {
1897 auto component = (*this)->getParentOfType<ComponentOp>();
1898 WiresOp wiresOp = component.getWiresOp();
1900 if (elseBodyExists() && getElseBody()->empty())
1901 return emitError() << "empty
'else' region.
";
1903 std::optional<StringRef> optGroupName = getGroupName();
1904 if (!optGroupName) {
1905 // No combinational group was provided.
1908 StringRef groupName = *optGroupName;
1909 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(groupName);
1911 return emitOpError() << "with group
'" << groupName
1912 << "', which does not exist.
";
1914 if (isa<GroupOp>(groupOp))
1915 return emitOpError() << "with group
'" << groupName
1916 << "', which is not a combinational group.
";
1918 if (failed(groupOp.drivesPort(getCond())))
1919 return emitError() << "with conditional op:
'"
1920 << valueName(component, getCond())
1921 << "' expected to be driven from group:
'" << groupName
1922 << "' but no driver was found.
";
1930 static std::optional<EnableOp> getLastEnableOp(SeqOp parent) {
1931 auto &lastOp = parent.getBodyBlock()->back();
1932 if (auto enableOp = dyn_cast<EnableOp>(lastOp))
1934 else if (auto seqOp = dyn_cast<SeqOp>(lastOp))
1935 return getLastEnableOp(seqOp);
1937 return std::nullopt;
1942 static llvm::StringMap<EnableOp> getAllEnableOpsInImmediateBody(ParOp parent) {
1943 llvm::StringMap<EnableOp> enables;
1944 Block *body = parent.getBodyBlock();
1945 for (EnableOp op : body->getOps<EnableOp>())
1946 enables.insert(std::pair(op.getGroupName(), op));
1958 template <typename OpTy>
1959 static bool hasCommonTailPatternPreConditions(IfOp op) {
1960 static_assert(std::is_same<SeqOp, OpTy>() || std::is_same<ParOp, OpTy>(),
1961 "Should be a SeqOp or ParOp.
");
1963 if (!op.thenBodyExists() || !op.elseBodyExists())
1965 if (op.getThenBody()->empty() || op.getElseBody()->empty())
1968 Block *thenBody = op.getThenBody(), *elseBody = op.getElseBody();
1969 return isa<OpTy>(thenBody->front()) && isa<OpTy>(elseBody->front());
1980 struct CommonTailPatternWithSeq : mlir::OpRewritePattern<IfOp> {
1981 using mlir::OpRewritePattern<IfOp>::OpRewritePattern;
1983 LogicalResult matchAndRewrite(IfOp ifOp,
1984 PatternRewriter &rewriter) const override {
1985 if (!hasCommonTailPatternPreConditions<SeqOp>(ifOp))
1988 auto thenControl = cast<SeqOp>(ifOp.getThenBody()->front()),
1989 elseControl = cast<SeqOp>(ifOp.getElseBody()->front());
1990 std::optional<EnableOp> lastThenEnableOp = getLastEnableOp(thenControl),
1991 lastElseEnableOp = getLastEnableOp(elseControl);
1993 if (!lastThenEnableOp || !lastElseEnableOp)
1995 if (lastThenEnableOp->getGroupName() != lastElseEnableOp->getGroupName())
1998 // Place the IfOp and pulled EnableOp inside a sequential region, in case
1999 // this IfOp is nested in a ParOp. This avoids unintentionally
2000 // parallelizing the pulled out EnableOps.
2001 rewriter.setInsertionPointAfter(ifOp);
2002 SeqOp seqOp = rewriter.create<SeqOp>(ifOp.getLoc());
2003 Block *body = seqOp.getBodyBlock();
2005 body->push_back(ifOp);
2006 rewriter.setInsertionPointToEnd(body);
2007 rewriter.create<EnableOp>(seqOp.getLoc(), lastThenEnableOp->getGroupName());
2009 // Erase the common EnableOp from the Then and Else regions.
2010 rewriter.eraseOp(*lastThenEnableOp);
2011 rewriter.eraseOp(*lastElseEnableOp);
2029 struct CommonTailPatternWithPar : mlir::OpRewritePattern<IfOp> {
2030 using mlir::OpRewritePattern<IfOp>::OpRewritePattern;
2032 LogicalResult matchAndRewrite(IfOp ifOp,
2033 PatternRewriter &rewriter) const override {
2034 if (!hasCommonTailPatternPreConditions<ParOp>(ifOp))
2036 auto thenControl = cast<ParOp>(ifOp.getThenBody()->front()),
2037 elseControl = cast<ParOp>(ifOp.getElseBody()->front());
2039 llvm::StringMap<EnableOp> A = getAllEnableOpsInImmediateBody(thenControl),
2040 B = getAllEnableOpsInImmediateBody(elseControl);
2042 // Compute the intersection between `A` and `B`.
2043 SmallVector<StringRef> groupNames;
2044 for (auto a = A.begin(); a != A.end(); ++a) {
2045 StringRef groupName = a->getKey();
2046 auto b = B.find(groupName);
2049 // This is also an element in B.
2050 groupNames.push_back(groupName);
2051 // Since these are being pulled out, erase them.
2052 rewriter.eraseOp(a->getValue());
2053 rewriter.eraseOp(b->getValue());
2055 // Place the IfOp and EnableOp(s) inside a parallel region, in case this
2056 // IfOp is nested in a SeqOp. This avoids unintentionally sequentializing
2057 // the pulled out EnableOps.
2058 rewriter.setInsertionPointAfter(ifOp);
2059 ParOp parOp = rewriter.create<ParOp>(ifOp.getLoc());
2060 Block *body = parOp.getBodyBlock();
2062 body->push_back(ifOp);
2064 // Pull out the intersection between these two sets, and erase their
2065 // counterparts in the Then and Else regions.
2066 rewriter.setInsertionPointToEnd(body);
2067 for (StringRef groupName : groupNames)
2068 rewriter.create<EnableOp>(parOp.getLoc(), groupName);
2077 struct EmptyIfBody : mlir::OpRewritePattern<IfOp> {
2078 using mlir::OpRewritePattern<IfOp>::OpRewritePattern;
2079 LogicalResult matchAndRewrite(IfOp ifOp,
2080 PatternRewriter &rewriter) const override {
2081 if (!ifOp.getThenBody()->empty())
2083 if (ifOp.elseBodyExists() && !ifOp.getElseBody()->empty())
2086 eraseControlWithGroupAndConditional(ifOp, rewriter);
2092 void IfOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2093 MLIRContext *context) {
2094 patterns.add<CommonTailPatternWithSeq, CommonTailPatternWithPar, EmptyIfBody>(
2098 //===----------------------------------------------------------------------===//
2100 //===----------------------------------------------------------------------===//
2101 LogicalResult WhileOp::verify() {
2102 auto component = (*this)->getParentOfType<ComponentOp>();
2103 auto wiresOp = component.getWiresOp();
2105 std::optional<StringRef> optGroupName = getGroupName();
2106 if (!optGroupName) {
2110 StringRef groupName = *optGroupName;
2111 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(groupName);
2113 return emitOpError() << "with group
'" << groupName
2114 << "', which does not exist.
";
2116 if (isa<GroupOp>(groupOp))
2117 return emitOpError() << "with group
'" << groupName
2118 << "', which is not a combinational group.
";
2120 if (failed(groupOp.drivesPort(getCond())))
2121 return emitError() << "conditional op:
'" << valueName(component, getCond())
2122 << "' expected to be driven from group:
'" << groupName
2123 << "' but no driver was found.
";
2128 LogicalResult WhileOp::canonicalize(WhileOp whileOp,
2129 PatternRewriter &rewriter) {
2130 if (whileOp.getBodyBlock()->empty()) {
2131 eraseControlWithGroupAndConditional(whileOp, rewriter);
2138 //===----------------------------------------------------------------------===//
2139 // Calyx library ops
2140 //===----------------------------------------------------------------------===//
2142 LogicalResult PadLibOp::verify() {
2143 unsigned inBits = getResult(0).getType().getIntOrFloatBitWidth();
2144 unsigned outBits = getResult(1).getType().getIntOrFloatBitWidth();
2145 if (inBits >= outBits)
2146 return emitOpError("expected input
bits (
")
2147 << inBits << ')' << " to be less than output
bits (
" << outBits
2152 LogicalResult SliceLibOp::verify() {
2153 unsigned inBits = getResult(0).getType().getIntOrFloatBitWidth();
2154 unsigned outBits = getResult(1).getType().getIntOrFloatBitWidth();
2155 if (inBits <= outBits)
2156 return emitOpError("expected input
bits (
")
2157 << inBits << ')' << " to be greater than output
bits (
" << outBits
2162 #define ImplBinPipeOpCellInterface(OpType, outName) \
2163 SmallVector<StringRef> OpType::portNames() { \
2164 return {"clk
", "reset
", "go
", "left
", "right
", outName, "done
"}; \
2167 SmallVector<Direction> OpType::portDirections() { \
2168 return {Input, Input, Input, Input, Input, Output, Output}; \
2171 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
2172 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
2175 SmallVector<DictionaryAttr> OpType::portAttributes() { \
2176 MLIRContext *context = getContext(); \
2177 IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1); \
2178 NamedAttrList go, clk, reset, done; \
2179 go.append("go
", isSet); \
2180 clk.append("clk
", isSet); \
2181 reset.append("reset
", isSet); \
2182 done.append("done
", isSet); \
2184 clk.getDictionary(context), /* Clk */ \
2185 reset.getDictionary(context), /* Reset */ \
2186 go.getDictionary(context), /* Go */ \
2187 DictionaryAttr::get(context), /* Lhs */ \
2188 DictionaryAttr::get(context), /* Rhs */ \
2189 DictionaryAttr::get(context), /* Out */ \
2190 done.getDictionary(context) /* Done */ \
2194 bool OpType::isCombinational() { return false; }
2196 #define ImplUnaryOpCellInterface(OpType) \
2197 SmallVector<StringRef> OpType::portNames() { return {"in
", "out
"}; } \
2198 SmallVector<Direction> OpType::portDirections() { return {Input, Output}; } \
2199 SmallVector<DictionaryAttr> OpType::portAttributes() { \
2200 return {DictionaryAttr::get(getContext()), \
2201 DictionaryAttr::get(getContext())}; \
2203 bool OpType::isCombinational() { return true; } \
2204 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
2205 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
2208 #define ImplBinOpCellInterface(OpType) \
2209 SmallVector<StringRef> OpType::portNames() { \
2210 return {"left
", "right
", "out
"}; \
2212 SmallVector<Direction> OpType::portDirections() { \
2213 return {Input, Input, Output}; \
2215 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
2216 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
2218 bool OpType::isCombinational() { return true; } \
2219 SmallVector<DictionaryAttr> OpType::portAttributes() { \
2220 return {DictionaryAttr::get(getContext()), \
2221 DictionaryAttr::get(getContext()), \
2222 DictionaryAttr::get(getContext())}; \
2226 ImplBinPipeOpCellInterface(MultPipeLibOp, "out
")
2227 ImplBinPipeOpCellInterface(DivUPipeLibOp, "out_quotient
")
2228 ImplBinPipeOpCellInterface(DivSPipeLibOp, "out_quotient
")
2229 ImplBinPipeOpCellInterface(RemUPipeLibOp, "out_remainder
")
2230 ImplBinPipeOpCellInterface(RemSPipeLibOp, "out_remainder
")
2232 ImplUnaryOpCellInterface(PadLibOp)
2233 ImplUnaryOpCellInterface(SliceLibOp)
2234 ImplUnaryOpCellInterface(NotLibOp)
2235 ImplUnaryOpCellInterface(WireLibOp)
2236 ImplUnaryOpCellInterface(ExtSILibOp)
2238 ImplBinOpCellInterface(LtLibOp)
2239 ImplBinOpCellInterface(GtLibOp)
2240 ImplBinOpCellInterface(EqLibOp)
2241 ImplBinOpCellInterface(NeqLibOp)
2242 ImplBinOpCellInterface(GeLibOp)
2243 ImplBinOpCellInterface(LeLibOp)
2244 ImplBinOpCellInterface(SltLibOp)
2245 ImplBinOpCellInterface(SgtLibOp)
2246 ImplBinOpCellInterface(SeqLibOp)
2247 ImplBinOpCellInterface(SneqLibOp)
2248 ImplBinOpCellInterface(SgeLibOp)
2249 ImplBinOpCellInterface(SleLibOp)
2251 ImplBinOpCellInterface(AddLibOp)
2252 ImplBinOpCellInterface(SubLibOp)
2253 ImplBinOpCellInterface(ShruLibOp)
2254 ImplBinOpCellInterface(RshLibOp)
2255 ImplBinOpCellInterface(SrshLibOp)
2256 ImplBinOpCellInterface(LshLibOp)
2257 ImplBinOpCellInterface(AndLibOp)
2258 ImplBinOpCellInterface(OrLibOp)
2259 ImplBinOpCellInterface(XorLibOp)
2262 //===----------------------------------------------------------------------===//
2263 // TableGen generated logic.
2264 //===----------------------------------------------------------------------===//
2266 #include "circt/Dialect/Calyx/CalyxInterfaces.cpp.inc
"
2268 // Provide the autogenerated implementation guts for the Op classes.
2269 #define GET_OP_CLASSES
2270 #include "circt/Dialect/Calyx/Calyx.cpp.inc
"
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.
Direction convertHWDirectionToCalyx(hw::PortDirection direction)
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.
static bool portIsUsedInGroup(GroupInterface group, Value port, bool isDriven)
Determines whether the given port is used in the group.
static Value getBlockArgumentWithName(StringRef name, ComponentOp op)
Returns the Block argument with the given name from a ComponentOp.
static ParseResult parsePortDefList(OpAsmParser &parser, OperationState &result, SmallVectorImpl< OpAsmParser::Argument > &ports, SmallVectorImpl< Type > &portTypes, SmallVectorImpl< NamedAttrList > &portAttrs)
Parses the ports of a Calyx component signature, and adds the corresponding port names to attrName.
static std::string valueName(Operation *scopeOp, Value v)
Convenience function for getting the SSA name of v under the scope of operation scopeOp.
static LogicalResult verifyNotComplexSource(Op op)
Verify that the value is not a "complex" value.
static LogicalResult verifyInstanceOpType(InstanceOp instance, ComponentInterface referencedComponent)
Verifies the port information in comparison with the referenced component of an instance.
static LogicalResult collapseControl(OpTy controlOp, PatternRewriter &rewriter)
static 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 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.
static LogicalResult verifyPortDirection(AssignOp op, Value value, bool isDestination)
Determines whether the given direction is valid with the given inputs.
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 isCombinational(Value value, GroupInterface group)
Verifies the defining operation of a value is combinational.
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)
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 size_t bits(::capnp::schema::Type::Reader type)
Return the number of bits used by a Capnp type.
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.
LogicalResult verifyComponent(Operation *op)
A helper function to verify each operation with the Ccomponent trait.
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.
Direction
The direction of a Component or Cell port.
PortInfo getPortInfo(BlockArgument arg)
Returns port information for the block argument provided.
void getAsmResultNames(OpAsmSetValueNameFn setNameFn, StringRef instanceName, ArrayAttr resultNames, ValueRange results)
Suggest a name for each result value based on the saved result names attribute.
mlir::FailureOr< mlir::Type > evaluateParametricType(mlir::Location loc, mlir::ArrayAttr parameters, mlir::Type type)
Returns a resolved version of 'type' wherein any parameter reference has been evaluated based on the ...
PortDirection
A module port direction.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
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