19#include "mlir/IR/AsmState.h"
20#include "mlir/IR/Builders.h"
21#include "mlir/IR/BuiltinAttributes.h"
22#include "mlir/IR/BuiltinTypes.h"
23#include "mlir/IR/Diagnostics.h"
24#include "mlir/IR/DialectImplementation.h"
25#include "mlir/IR/PatternMatch.h"
26#include "mlir/IR/SymbolTable.h"
27#include "mlir/Interfaces/FunctionImplementation.h"
28#include "mlir/Support/LLVM.h"
29#include "llvm/ADT/DenseMap.h"
30#include "llvm/ADT/PriorityQueue.h"
31#include "llvm/ADT/STLExtras.h"
32#include "llvm/ADT/SmallSet.h"
33#include "llvm/ADT/StringExtras.h"
34#include "llvm/ADT/TypeSwitch.h"
35#include "llvm/Support/Casting.h"
46template <
class T,
class... Ts>
47struct IsAny : std::disjunction<std::is_same<T, Ts>...> {};
63 size_t numDirections = nIns + nOuts;
64 APInt portDirections(numDirections, 0);
65 for (
size_t i = nIns, e = numDirections; i != e; ++i)
66 portDirections.setBit(i);
68 return IntegerAttr::get(IntegerType::get(ctx, numDirections), portDirections);
77template <
typename CtrlOp>
81 PatternRewriter &rewriter)
const override {
82 auto &ops = ctrlOp.getBodyBlock()->getOperations();
84 (ops.size() == 1) && isa<EnableOp>(ops.front()) &&
85 isa<SeqOp, ParOp, StaticSeqOp, StaticParOp>(ctrlOp->getParentOp());
89 ops.front().moveBefore(ctrlOp);
90 rewriter.eraseOp(ctrlOp);
103template <
typename Op>
105 Operation *definingOp = op.getSrc().getDefiningOp();
106 if (definingOp ==
nullptr)
112 if (
auto dialect = definingOp->getDialect(); isa<comb::CombDialect>(dialect))
113 return op->emitOpError(
"has source that is not a port or constant. "
114 "Complex logic should be conducted in the guard.");
121static std::string
valueName(Operation *scopeOp, Value v) {
123 llvm::raw_string_ostream os(s);
128 AsmState asmState(scopeOp, OpPrintingFlags().assumeVerified());
129 v.printAsOperand(os, asmState);
136 Operation *definingOp = value.getDefiningOp();
137 return isa<BlockArgument>(value) ||
138 isa_and_nonnull<CellInterface>(definingOp);
143 Operation *op = arg.getOwner()->getParentOp();
144 assert(isa<ComponentInterface>(op) &&
145 "Only ComponentInterface should support lookup by BlockArgument.");
146 return cast<ComponentInterface>(op).getPortInfo()[arg.getArgNumber()];
151 return isa<ControlOp, SeqOp, IfOp, RepeatOp,
WhileOp, ParOp, StaticRepeatOp,
152 StaticParOp, StaticSeqOp, StaticIfOp>(op);
157 if (isa<EnableOp>(op)) {
159 auto component = op->getParentOfType<ComponentOp>();
160 auto enableOp = llvm::cast<EnableOp>(op);
161 StringRef groupName = enableOp.getGroupName();
162 auto group = component.getWiresOp().lookupSymbol<GroupInterface>(groupName);
163 return isa<StaticGroupOp>(group);
165 return isa<StaticIfOp, StaticSeqOp, StaticRepeatOp, StaticParOp>(op);
170 if (isa<SeqOp, ParOp, StaticSeqOp, StaticParOp>(op))
175 for (
auto ®ion : op->getRegions()) {
176 auto opsIt = region.getOps();
177 size_t numOperations = std::distance(opsIt.begin(), opsIt.end());
182 bool usesEnableAsCompositionOperator =
183 numOperations > 1 && llvm::any_of(region.front(), [](
auto &&bodyOp) {
184 return isa<EnableOp>(bodyOp);
186 if (usesEnableAsCompositionOperator)
187 return op->emitOpError(
188 "EnableOp is not a composition operator. It should be nested "
189 "in a control flow operation, such as \"calyx.seq\"");
193 size_t numControlFlowRegions =
195 if (numControlFlowRegions > 1)
196 return op->emitOpError(
197 "has an invalid control sequence. Multiple control flow operations "
198 "must all be nested in a single calyx.seq or calyx.par");
204 auto *opParent = op->getParentOp();
205 if (!isa<ModuleOp>(opParent))
206 return op->emitOpError()
207 <<
"has parent: " << opParent <<
", expected ModuleOp.";
212 auto opParent = op->getParentOp();
213 if (!isa<ComponentInterface>(opParent))
214 return op->emitOpError()
215 <<
"has parent: " << opParent <<
", expected ComponentInterface.";
220 auto parent = op->getParentOp();
222 if (isa<calyx::EnableOp>(op) &&
223 !isa_and_nonnull<calyx::CalyxDialect>(parent->getDialect())) {
232 return op->emitOpError()
233 <<
"has parent: " << parent
234 <<
", which is not allowed for a control-like operation.";
236 if (op->getNumRegions() == 0)
239 auto ®ion = op->getRegion(0);
241 auto isValidBodyOp = [](Operation *operation) {
242 return isa<EnableOp, InvokeOp, SeqOp, IfOp, RepeatOp,
WhileOp, ParOp,
243 StaticParOp, StaticRepeatOp, StaticSeqOp, StaticIfOp>(operation);
245 for (
auto &&bodyOp : region.front()) {
246 if (isValidBodyOp(&bodyOp))
249 return op->emitOpError()
250 <<
"has operation: " << bodyOp.getName()
251 <<
", which is not allowed in this control-like operation";
257 auto ifOp = dyn_cast<IfInterface>(op);
259 if (ifOp.elseBodyExists() && ifOp.getElseBody()->empty())
260 return ifOp->emitOpError() <<
"empty 'else' region.";
270 SmallVector<OpAsmParser::UnresolvedOperand, 2> operandInfos;
271 OpAsmParser::UnresolvedOperand guardOrSource;
272 if (parser.parseOperand(guardOrSource))
275 if (succeeded(parser.parseOptionalQuestion())) {
276 OpAsmParser::UnresolvedOperand source;
278 if (parser.parseOperand(source))
280 operandInfos.push_back(source);
283 operandInfos.push_back(guardOrSource);
288 if (parser.parseColonType(type) ||
289 parser.resolveOperands(operandInfos, type, result.operands))
296template <
typename GroupPortType>
298 static_assert(IsAny<GroupPortType, GroupGoOp, GroupDoneOp>(),
299 "Should be a Calyx Group port.");
303 Value guard = op.getGuard(), source = op.getSrc();
306 p << source <<
" : " << source.getType();
311template <
typename OpTy>
313 PatternRewriter &rewriter) {
314 static_assert(IsAny<OpTy, SeqOp, ParOp, StaticSeqOp, StaticParOp>(),
315 "Should be a SeqOp, ParOp, StaticSeqOp, or StaticParOp");
317 if (isa<OpTy>(controlOp->getParentOp())) {
318 Block *controlBody = controlOp.getBodyBlock();
319 for (
auto &op : make_early_inc_range(*controlBody))
320 op.moveBefore(controlOp);
322 rewriter.eraseOp(controlOp);
329template <
typename OpTy>
330static LogicalResult
emptyControl(OpTy controlOp, PatternRewriter &rewriter) {
331 if (controlOp.getBodyBlock()->empty()) {
332 rewriter.eraseOp(controlOp);
341template <
typename OpTy>
343 PatternRewriter &rewriter) {
344 static_assert(IsAny<OpTy, IfOp, WhileOp>(),
345 "This is only applicable to WhileOp and IfOp.");
348 Value cond = op.getCond();
349 std::optional<StringRef> groupName = op.getGroupName();
350 auto component = op->template getParentOfType<ComponentOp>();
351 rewriter.eraseOp(op);
355 auto group = component.getWiresOp().template lookupSymbol<GroupInterface>(
357 if (SymbolTable::symbolKnownUseEmpty(group, component.getRegion()))
358 rewriter.eraseOp(group);
361 if (!isa<BlockArgument>(cond) && cond.getDefiningOp()->use_empty())
362 rewriter.eraseOp(cond.getDefiningOp());
368template <
typename OpTy>
370 static_assert(std::is_same<OpTy, StaticIfOp>(),
371 "This is only applicable to StatifIfOp.");
374 Value cond = op.getCond();
375 rewriter.eraseOp(op);
378 if (!isa<BlockArgument>(cond) && cond.getDefiningOp()->use_empty())
379 rewriter.eraseOp(cond.getDefiningOp());
386template <
typename ComponentTy>
388 auto componentName = comp->template getAttrOfType<StringAttr>(
389 ::mlir::SymbolTable::getSymbolAttrName())
392 p.printSymbolName(componentName);
395 auto printPortDefList = [&](
auto ports) {
397 llvm::interleaveComma(ports, p, [&](
const PortInfo &port) {
398 p <<
"%" << port.
name.getValue() <<
": " << port.
type;
406 printPortDefList(comp.getInputPortInfo());
408 printPortDefList(comp.getOutputPortInfo());
411 p.printRegion(*comp.getRegion(),
false,
415 SmallVector<StringRef> elidedAttrs = {
420 ComponentTy::getFunctionTypeAttrName(comp->getName()),
421 ComponentTy::getArgAttrsAttrName(comp->getName()),
422 ComponentTy::getResAttrsAttrName(comp->getName())};
423 p.printOptionalAttrDict(comp->getAttrs(), elidedAttrs);
430 SmallVectorImpl<OpAsmParser::Argument> &ports,
431 SmallVectorImpl<Type> &portTypes,
432 SmallVectorImpl<NamedAttrList> &portAttrs) {
434 OpAsmParser::Argument port;
437 if (parser.parseArgument(port) || parser.parseColon() ||
438 parser.parseType(portType))
440 port.type = portType;
441 ports.push_back(port);
442 portTypes.push_back(portType);
444 NamedAttrList portAttr;
445 portAttrs.push_back(succeeded(parser.parseOptionalAttrDict(portAttr))
451 return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
458 SmallVectorImpl<OpAsmParser::Argument> &ports,
459 SmallVectorImpl<Type> &portTypes) {
460 SmallVector<OpAsmParser::Argument> inPorts, outPorts;
461 SmallVector<Type> inPortTypes, outPortTypes;
462 SmallVector<NamedAttrList> portAttributes;
467 if (parser.parseArrow() ||
471 auto *context = parser.getBuilder().getContext();
474 SmallVector<Attribute> portNames;
475 auto getPortName = [context](
const auto &port) -> StringAttr {
476 StringRef name = port.ssaName.name;
477 if (name.starts_with(
"%"))
478 name = name.drop_front();
479 return StringAttr::get(context, name);
481 llvm::transform(inPorts, std::back_inserter(portNames), getPortName);
482 llvm::transform(outPorts, std::back_inserter(portNames), getPortName);
484 result.addAttribute(
"portNames", ArrayAttr::get(context, portNames));
489 ports.append(inPorts);
490 ports.append(outPorts);
491 portTypes.append(inPortTypes);
492 portTypes.append(outPortTypes);
494 SmallVector<Attribute> portAttrs;
495 llvm::transform(portAttributes, std::back_inserter(portAttrs),
496 [&](
auto attr) {
return attr.getDictionary(context); });
497 result.addAttribute(
"portAttributes", ArrayAttr::get(context, portAttrs));
502template <
typename ComponentTy>
504 OperationState &result) {
505 using namespace mlir::function_interface_impl;
507 StringAttr componentName;
508 if (parser.parseSymbolName(componentName,
509 ::mlir::SymbolTable::getSymbolAttrName(),
513 SmallVector<mlir::OpAsmParser::Argument> ports;
515 SmallVector<Type> portTypes;
521 auto type = parser.getBuilder().getFunctionType(portTypes, {});
522 result.addAttribute(ComponentTy::getFunctionTypeAttrName(result.name),
523 TypeAttr::get(type));
525 auto *body = result.addRegion();
526 if (parser.parseRegion(*body, ports))
530 body->push_back(
new Block());
532 if (parser.parseOptionalAttrDict(result.attributes))
540static SmallVector<T>
concat(
const SmallVectorImpl<T> &a,
541 const SmallVectorImpl<T> &b) {
549 StringAttr name, ArrayRef<PortInfo> ports,
550 bool combinational) {
551 using namespace mlir::function_interface_impl;
553 result.addAttribute(::mlir::SymbolTable::getSymbolAttrName(), name);
555 std::pair<SmallVector<Type, 8>, SmallVector<Type, 8>> portIOTypes;
556 std::pair<SmallVector<Attribute, 8>, SmallVector<Attribute, 8>> portIONames;
557 std::pair<SmallVector<Attribute, 8>, SmallVector<Attribute, 8>>
559 SmallVector<Direction, 8> portDirections;
562 for (
auto &&port : ports) {
563 bool isInput = port.direction == Direction::Input;
564 (isInput ? portIOTypes.first : portIOTypes.second).push_back(port.type);
565 (isInput ? portIONames.first : portIONames.second).push_back(port.name);
566 (isInput ? portIOAttributes.first : portIOAttributes.second)
567 .push_back(port.attributes);
569 auto portTypes =
concat(portIOTypes.first, portIOTypes.second);
570 auto portNames =
concat(portIONames.first, portIONames.second);
571 auto portAttributes =
concat(portIOAttributes.first, portIOAttributes.second);
574 auto functionType = builder.getFunctionType(portTypes, {});
576 result.addAttribute(CombComponentOp::getFunctionTypeAttrName(result.name),
577 TypeAttr::get(functionType));
579 result.addAttribute(ComponentOp::getFunctionTypeAttrName(result.name),
580 TypeAttr::get(functionType));
584 result.addAttribute(
"portNames", builder.getArrayAttr(portNames));
585 result.addAttribute(
"portDirections",
587 portIOTypes.first.size(),
588 portIOTypes.second.size()));
590 result.addAttribute(
"portAttributes", builder.getArrayAttr(portAttributes));
593 Region *region = result.addRegion();
594 Block *body =
new Block();
595 region->push_back(body);
598 body->addArguments(portTypes, SmallVector<Location, 4>(
599 portTypes.size(), builder.getUnknownLoc()));
602 IRRewriter::InsertionGuard guard(builder);
603 builder.setInsertionPointToStart(body);
604 WiresOp::create(builder, result.location);
606 ControlOp::create(builder, result.location);
617template <
typename Op>
619 auto *body = op.getBodyBlock();
622 auto opIt = body->getOps<Op>().begin();
629 ArrayAttr portNames = op.getPortNames();
631 for (
size_t i = 0, e = portNames.size(); i != e; ++i) {
632 auto portName = cast<StringAttr>(portNames[i]);
633 if (portName.getValue() == name)
634 return op.getBodyBlock()->getArgument(i);
639WiresOp calyx::ComponentOp::getWiresOp() {
640 return getControlOrWiresFrom<WiresOp>(*
this);
643ControlOp calyx::ComponentOp::getControlOp() {
644 return getControlOrWiresFrom<ControlOp>(*
this);
647Value calyx::ComponentOp::getGoPort() {
651Value calyx::ComponentOp::getDonePort() {
655Value calyx::ComponentOp::getClkPort() {
659Value calyx::ComponentOp::getResetPort() {
663SmallVector<PortInfo> ComponentOp::getPortInfo() {
664 auto portTypes = getArgumentTypes();
665 ArrayAttr portNamesAttr = getPortNames(), portAttrs = getPortAttributes();
666 APInt portDirectionsAttr = getPortDirections();
668 SmallVector<PortInfo> results;
669 for (
size_t i = 0, e = portNamesAttr.size(); i != e; ++i) {
670 results.push_back(
PortInfo{cast<StringAttr>(portNamesAttr[i]), portTypes[i],
672 cast<DictionaryAttr>(portAttrs[i])});
678template <
typename Pred>
680 SmallVector<PortInfo> ports = op.getPortInfo();
681 llvm::erase_if(ports, p);
685SmallVector<PortInfo> ComponentOp::getInputPortInfo() {
690SmallVector<PortInfo> ComponentOp::getOutputPortInfo() {
695void ComponentOp::print(OpAsmPrinter &p) {
696 printComponentInterface<ComponentOp>(p, *
this);
699ParseResult 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: "
733LogicalResult ComponentOp::verify() {
737 if (std::distance(wIt.begin(), wIt.end()) +
738 std::distance(cIt.begin(), cIt.end()) !=
740 return emitOpError() <<
"requires exactly one of each: '"
741 << WiresOp::getOperationName() <<
"', '"
742 << ControlOp::getOperationName() <<
"'.";
749 bool hasNoControlConstructs =
true;
750 getControlOp().walk<WalkOrder::PreOrder>([&](Operation *op) {
751 if (isa<EnableOp, InvokeOp, fsm::MachineOp>(op)) {
752 hasNoControlConstructs =
false;
753 return WalkResult::interrupt();
755 return WalkResult::advance();
757 bool hasNoAssignments =
758 getWiresOp().getBodyBlock()->getOps<AssignOp>().
empty();
759 if (hasNoControlConstructs && hasNoAssignments)
761 "The component currently does nothing. It needs to either have "
762 "continuous assignments in the Wires region or control "
763 "constructs in the Control region. The Control region "
764 "should contain at least one of ")
765 <<
"'" << EnableOp::getOperationName() <<
"' , "
766 <<
"'" << InvokeOp::getOperationName() <<
"' or "
767 <<
"'" << fsm::MachineOp::getOperationName() <<
"'.";
771void ComponentOp::build(OpBuilder &builder, OperationState &result,
772 StringAttr name, ArrayRef<PortInfo> ports) {
776void ComponentOp::getAsmBlockArgumentNames(
780 auto ports = getPortNames();
781 auto *block = &getRegion()->front();
782 for (
size_t i = 0, e = block->getNumArguments(); i != e; ++i)
783 setNameFn(block->getArgument(i), cast<StringAttr>(ports[i]).getValue());
790SmallVector<PortInfo> CombComponentOp::getPortInfo() {
791 auto portTypes = getArgumentTypes();
792 ArrayAttr portNamesAttr = getPortNames(), portAttrs = getPortAttributes();
793 APInt portDirectionsAttr = getPortDirections();
795 SmallVector<PortInfo> results;
796 for (
size_t i = 0, e = portNamesAttr.size(); i != e; ++i) {
797 results.push_back(
PortInfo{cast<StringAttr>(portNamesAttr[i]), portTypes[i],
799 cast<DictionaryAttr>(portAttrs[i])});
804WiresOp calyx::CombComponentOp::getWiresOp() {
806 auto opIt = body->getOps<WiresOp>().begin();
811template <
typename Pred>
813 SmallVector<PortInfo> ports = op.getPortInfo();
814 llvm::erase_if(ports, p);
818SmallVector<PortInfo> CombComponentOp::getInputPortInfo() {
823SmallVector<PortInfo> CombComponentOp::getOutputPortInfo() {
828void CombComponentOp::print(OpAsmPrinter &p) {
829 printComponentInterface<CombComponentOp>(p, *
this);
832ParseResult CombComponentOp::parse(OpAsmParser &parser,
833 OperationState &result) {
834 return parseComponentInterface<CombComponentOp>(parser, result);
837LogicalResult CombComponentOp::verify() {
840 if (std::distance(wIt.begin(), wIt.end()) != 1)
841 return emitOpError() <<
"requires exactly one "
842 << WiresOp::getOperationName() <<
" op.";
846 if (std::distance(cIt.begin(), cIt.end()) != 0)
847 return emitOpError() <<
"must not have a `" << ControlOp::getOperationName()
851 bool hasNoAssignments =
852 getWiresOp().getBodyBlock()->getOps<AssignOp>().
empty();
853 if (hasNoAssignments)
855 "The component currently does nothing. It needs to either have "
856 "continuous assignments in the Wires region.");
859 auto cells = getOps<CellInterface>();
860 for (
auto cell : cells) {
861 if (!cell.isCombinational())
862 return emitOpError() <<
"contains non-combinational cell "
863 << cell.instanceName();
867 auto groups = getWiresOp().getOps<GroupOp>();
869 return emitOpError() <<
"contains group " << (*groups.begin()).getSymName();
874 auto combGroups = getWiresOp().getOps<CombGroupOp>();
875 if (!combGroups.empty())
876 return emitOpError() <<
"contains comb group "
877 << (*combGroups.begin()).getSymName();
882void CombComponentOp::build(OpBuilder &builder, OperationState &result,
883 StringAttr name, ArrayRef<PortInfo> ports) {
887void CombComponentOp::getAsmBlockArgumentNames(
891 auto ports = getPortNames();
892 auto *block = &getRegion()->front();
893 for (
size_t i = 0, e = block->getNumArguments(); i != e; ++i)
894 setNameFn(block->getArgument(i), cast<StringAttr>(ports[i]).getValue());
903SmallVector<InvokeOp, 4> ControlOp::getInvokeOps() {
904 SmallVector<InvokeOp, 4> ret;
905 this->walk([&](InvokeOp invokeOp) { ret.push_back(invokeOp); });
913void SeqOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
914 MLIRContext *context) {
915 patterns.add(collapseControl<SeqOp>);
924LogicalResult StaticSeqOp::verify() {
926 auto &ops = (*this).getBodyBlock()->getOperations();
927 if (!llvm::all_of(ops, [&](Operation &op) {
return isStaticControl(&op); })) {
928 return emitOpError(
"StaticSeqOp has non static control within it");
934void StaticSeqOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
935 MLIRContext *context) {
936 patterns.add(collapseControl<StaticSeqOp>);
937 patterns.add(emptyControl<StaticSeqOp>);
945LogicalResult ParOp::verify() {
950 for (EnableOp op :
getBodyBlock()->getOps<EnableOp>()) {
951 StringRef groupName = op.getGroupName();
952 if (groupNames.count(groupName))
953 return emitOpError() <<
"cannot enable the same group: \"" << groupName
954 <<
"\" more than once.";
955 groupNames.insert(groupName);
961void ParOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
962 MLIRContext *context) {
963 patterns.add(collapseControl<ParOp>);
972LogicalResult StaticParOp::verify() {
977 for (EnableOp op :
getBodyBlock()->getOps<EnableOp>()) {
978 StringRef groupName = op.getGroupName();
979 if (groupNames.count(groupName))
980 return emitOpError() <<
"cannot enable the same group: \"" << groupName
981 <<
"\" more than once.";
982 groupNames.insert(groupName);
986 auto &ops = (*this).getBodyBlock()->getOperations();
987 for (Operation &op : ops) {
989 return op.emitOpError(
"StaticParOp has non static control within it");
996void StaticParOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
997 MLIRContext *context) {
998 patterns.add(collapseControl<StaticParOp>);
999 patterns.add(emptyControl<StaticParOp>);
1006LogicalResult WiresOp::verify() {
1007 auto componentInterface = (*this)->getParentOfType<ComponentInterface>();
1008 if (llvm::isa<ComponentOp>(componentInterface)) {
1009 auto component = llvm::cast<ComponentOp>(componentInterface);
1010 auto control = component.getControlOp();
1014 if (!isa<GroupInterface>(op))
1016 auto group = cast<GroupInterface>(op);
1017 auto groupName = group.symName();
1018 if (mlir::SymbolTable::symbolKnownUseEmpty(groupName, control))
1019 return op.emitOpError()
1020 <<
"with name: " << groupName
1021 <<
" is unused in the control execution schedule";
1028 for (
auto thisAssignment :
getBodyBlock()->getOps<AssignOp>()) {
1032 if (thisAssignment.getGuard())
1035 Value dest = thisAssignment.getDest();
1036 for (Operation *user : dest.getUsers()) {
1037 auto assignUser = dyn_cast<AssignOp>(user);
1038 if (!assignUser || assignUser.getDest() != dest ||
1039 assignUser == thisAssignment)
1042 return user->emitOpError() <<
"destination is already continuously "
1043 "driven. Other assignment is "
1057 Operation *definingOp = value.getDefiningOp();
1058 if (definingOp ==
nullptr || definingOp->hasTrait<
Combinational>())
1064 if (isa<InstanceOp>(definingOp))
1068 if (isa_and_nonnull<comb::CombDialect, hw::HWDialect>(
1069 definingOp->getDialect()))
1073 if (
auto r = dyn_cast<RegisterOp>(definingOp)) {
1074 return value == r.getOut()
1076 : group->emitOpError()
1077 <<
"with register: \"" << r.instanceName()
1078 <<
"\" is conducting a memory store. This is not "
1080 }
else if (
auto m = dyn_cast<MemoryOp>(definingOp)) {
1081 auto writePorts = {m.writeData(), m.writeEn()};
1082 return (llvm::none_of(
writePorts, [&](Value p) {
return p == value; }))
1084 : group->emitOpError()
1085 <<
"with memory: \"" << m.instanceName()
1086 <<
"\" is conducting a memory store. This "
1087 "is not combinational.";
1090 std::string portName =
1091 valueName(group->getParentOfType<ComponentOp>(), value);
1092 return group->emitOpError() <<
"with port: " << portName
1093 <<
". This operation is not combinational.";
1098LogicalResult CombGroupOp::verify() {
1100 auto assign = dyn_cast<AssignOp>(op);
1101 if (assign ==
nullptr)
1103 Value dst = assign.getDest(), src = assign.getSrc();
1114GroupGoOp GroupOp::getGoOp() {
1116 size_t nOps = std::distance(goOps.begin(), goOps.end());
1117 return nOps ? *goOps.begin() : GroupGoOp();
1120GroupDoneOp GroupOp::getDoneOp() {
1122 return cast<GroupDoneOp>(body->getTerminator());
1128void CycleOp::print(OpAsmPrinter &p) {
1131 auto start = this->getStart();
1132 auto end = this->getEnd();
1133 if (
end.has_value()) {
1134 p <<
"[" << start <<
":" <<
end.value() <<
"]";
1140ParseResult CycleOp::parse(OpAsmParser &parser, OperationState &result) {
1141 SmallVector<OpAsmParser::UnresolvedOperand, 2> operandInfos;
1143 uint32_t startLiteral;
1144 uint32_t endLiteral;
1146 auto hasEnd = succeeded(parser.parseOptionalLSquare());
1148 if (parser.parseInteger(startLiteral)) {
1149 parser.emitError(parser.getNameLoc(),
"Could not parse start cycle");
1153 auto start = parser.getBuilder().getI32IntegerAttr(startLiteral);
1154 result.addAttribute(getStartAttrName(result.name), start);
1157 if (parser.parseColon())
1160 if (
auto res = parser.parseOptionalInteger(endLiteral); res.has_value()) {
1161 auto end = parser.getBuilder().getI32IntegerAttr(endLiteral);
1162 result.addAttribute(getEndAttrName(result.name),
end);
1165 if (parser.parseRSquare())
1169 result.addTypes(parser.getBuilder().getI1Type());
1174LogicalResult CycleOp::verify() {
1175 uint32_t latency = this->getGroupLatency();
1177 if (this->getStart() >= latency) {
1178 emitOpError(
"start cycle must be less than the group latency");
1182 if (this->getEnd().has_value()) {
1183 if (this->getStart() >= this->getEnd().value()) {
1184 emitOpError(
"start cycle must be less than end cycle");
1188 if (this->getEnd() >= latency) {
1189 emitOpError(
"end cycle must be less than the group latency");
1197uint32_t CycleOp::getGroupLatency() {
1198 auto group = (*this)->getParentOfType<StaticGroupOp>();
1199 return group.getLatency();
1206 return FloatingPointStandard::IEEE754;
1210 return FloatingPointStandard::IEEE754;
1214 return FloatingPointStandard::IEEE754;
1218 return FloatingPointStandard::IEEE754;
1222 return FloatingPointStandard::IEEE754;
1226 return FloatingPointStandard::IEEE754;
1229std::string AddFOpIEEE754::getCalyxLibraryName() {
return "std_addFN"; }
1231std::string MulFOpIEEE754::getCalyxLibraryName() {
return "std_mulFN"; }
1233std::string CompareFOpIEEE754::getCalyxLibraryName() {
return "std_compareFN"; }
1235std::string FpToIntOpIEEE754::getCalyxLibraryName() {
return "std_fpToInt"; }
1237std::string IntToFpOpIEEE754::getCalyxLibraryName() {
return "std_intToFp"; }
1239std::string DivSqrtOpIEEE754::getCalyxLibraryName() {
return "std_divSqrtFN"; }
1248 return llvm::any_of(port.getUses(), [&](
auto &&use) {
1249 auto assignOp = dyn_cast<AssignOp>(use.getOwner());
1250 if (assignOp == nullptr)
1253 Operation *parent = assignOp->getParentOp();
1254 if (isa<WiresOp>(parent))
1263 Value expected = isDriven ? assignOp.getDest() : assignOp.getSrc();
1264 return expected == port && group == parent;
1276 if (
auto cell = dyn_cast<CellInterface>(port.getDefiningOp());
1277 cell && cell.direction(port) == calyx::Direction::Output)
1278 return groupOp.drivesAnyPort(cell.getInputPorts());
1283LogicalResult GroupOp::drivesPort(Value port) {
1287LogicalResult CombGroupOp::drivesPort(Value port) {
1291LogicalResult StaticGroupOp::drivesPort(Value port) {
1298 return success(llvm::all_of(ports, [&](Value port) {
1303LogicalResult GroupOp::drivesAllPorts(ValueRange ports) {
1307LogicalResult CombGroupOp::drivesAllPorts(ValueRange ports) {
1311LogicalResult StaticGroupOp::drivesAllPorts(ValueRange ports) {
1318 return success(llvm::any_of(ports, [&](Value port) {
1323LogicalResult GroupOp::drivesAnyPort(ValueRange ports) {
1327LogicalResult CombGroupOp::drivesAnyPort(ValueRange ports) {
1331LogicalResult StaticGroupOp::drivesAnyPort(ValueRange ports) {
1338 return success(llvm::any_of(ports, [&](Value port) {
1343LogicalResult GroupOp::readsAnyPort(ValueRange ports) {
1347LogicalResult CombGroupOp::readsAnyPort(ValueRange ports) {
1351LogicalResult StaticGroupOp::readsAnyPort(ValueRange ports) {
1358 GroupInterface group) {
1359 Operation *destDefiningOp = assign.getDest().getDefiningOp();
1360 if (destDefiningOp ==
nullptr)
1362 auto destCell = dyn_cast<CellInterface>(destDefiningOp);
1363 if (destCell ==
nullptr)
1366 LogicalResult verifyWrites =
1367 TypeSwitch<Operation *, LogicalResult>(destCell)
1368 .Case<RegisterOp>([&](
auto op) {
1371 return succeeded(group.drivesAnyPort({op.getWriteEn(), op.getIn()}))
1372 ? group.drivesAllPorts({op.getWriteEn(), op.getIn()})
1375 .Case<MemoryOp>([&](
auto op) {
1376 SmallVector<Value> requiredWritePorts;
1379 requiredWritePorts.push_back(op.writeEn());
1380 requiredWritePorts.push_back(op.writeData());
1381 for (Value address : op.addrPorts())
1382 requiredWritePorts.push_back(address);
1387 group.drivesAnyPort({op.writeData(), op.writeEn()}))
1388 ? group.drivesAllPorts(requiredWritePorts)
1391 .Case<AndLibOp, OrLibOp, XorLibOp, AddLibOp, SubLibOp, GtLibOp,
1392 LtLibOp, EqLibOp, NeqLibOp, GeLibOp, LeLibOp, LshLibOp,
1393 RshLibOp, SgtLibOp, SltLibOp, SeqLibOp, SneqLibOp, SgeLibOp,
1394 SleLibOp, SrshLibOp>([&](
auto op) {
1395 Value lhs = op.getLeft(), rhs = op.getRight();
1396 return succeeded(group.drivesAnyPort({lhs, rhs}))
1397 ? group.drivesAllPorts({lhs, rhs})
1400 .Default([&](
auto op) {
return success(); });
1402 if (failed(verifyWrites))
1403 return group->emitOpError()
1404 <<
"with cell: " << destCell->getName() <<
" \""
1405 << destCell.instanceName()
1406 <<
"\" is performing a write and failed to drive all necessary "
1409 Operation *srcDefiningOp = assign.getSrc().getDefiningOp();
1410 if (srcDefiningOp ==
nullptr)
1412 auto srcCell = dyn_cast<CellInterface>(srcDefiningOp);
1413 if (srcCell ==
nullptr)
1416 LogicalResult verifyReads =
1417 TypeSwitch<Operation *, LogicalResult>(srcCell)
1418 .Case<MemoryOp>([&](
auto op) {
1422 return succeeded(group.readsAnyPort({op.readData()}))
1423 ? group.drivesAllPorts(op.addrPorts())
1426 .Default([&](
auto op) {
return success(); });
1428 if (failed(verifyReads))
1429 return group->emitOpError() <<
"with cell: " << srcCell->getName() <<
" \""
1430 << srcCell.instanceName()
1431 <<
"\" is having a read performed upon it, and "
1432 "failed to drive all necessary ports.";
1438 auto group = dyn_cast<GroupInterface>(op);
1439 if (group ==
nullptr)
1442 for (
auto &&groupOp : *group.getBody()) {
1443 auto assign = dyn_cast<AssignOp>(groupOp);
1444 if (assign ==
nullptr)
1460 ArrayRef<StringRef> portNames) {
1461 auto cellInterface = dyn_cast<CellInterface>(op);
1462 assert(cellInterface &&
"must implement the Cell interface");
1464 std::string prefix = cellInterface.instanceName().str() +
".";
1465 for (
size_t i = 0, e = portNames.size(); i != e; ++i)
1466 setNameFn(op->getResult(i), prefix + portNames[i].str());
1477 bool isDestination) {
1478 Operation *definingOp = value.getDefiningOp();
1479 bool isComponentPort = isa<BlockArgument>(value),
1480 isCellInterfacePort = isa_and_nonnull<CellInterface>(definingOp);
1481 assert((isComponentPort || isCellInterfacePort) &&
"Not a port.");
1485 : cast<CellInterface>(definingOp).portInfo(value);
1487 bool isSource = !isDestination;
1490 (isDestination && isComponentPort) || (isSource && isCellInterfacePort)
1497 <<
"has a " << (isComponentPort ?
"component" :
"cell")
1499 << (isDestination ?
"destination" :
"source")
1500 <<
" with the incorrect direction.";
1507 bool isSource = !isDestination;
1508 Value value = isDestination ? op.getDest() : op.getSrc();
1513 if (isDestination && !isa<GroupGoOp, GroupDoneOp>(value.getDefiningOp()))
1514 return op->emitOpError(
1515 "has an invalid destination port. It must be drive-able.");
1522LogicalResult AssignOp::verify() {
1523 bool isDestination =
true, isSource =
false;
1532ParseResult AssignOp::parse(OpAsmParser &parser, OperationState &result) {
1533 OpAsmParser::UnresolvedOperand destination;
1534 if (parser.parseOperand(destination) || parser.parseEqual())
1540 OpAsmParser::UnresolvedOperand guardOrSource;
1541 if (parser.parseOperand(guardOrSource))
1546 OpAsmParser::UnresolvedOperand source;
1547 bool hasGuard = succeeded(parser.parseOptionalQuestion());
1550 if (parser.parseOperand(source))
1555 if (parser.parseColonType(type) ||
1556 parser.resolveOperand(destination, type, result.operands))
1560 Type i1Type = parser.getBuilder().getI1Type();
1563 if (parser.resolveOperand(source, type, result.operands) ||
1564 parser.resolveOperand(guardOrSource, i1Type, result.operands))
1568 if (parser.resolveOperand(guardOrSource, type, result.operands))
1575void AssignOp::print(OpAsmPrinter &p) {
1576 p <<
" " << getDest() <<
" = ";
1578 Value bguard = getGuard(), source = getSrc();
1581 p << bguard <<
" ? ";
1585 p << source <<
" : " << source.getType();
1594ComponentInterface InstanceOp::getReferencedComponent() {
1595 auto module = (*this)->getParentOfType<ModuleOp>();
1599 return module.lookupSymbol<ComponentInterface>(getComponentName());
1607 ComponentInterface referencedComponent) {
1608 auto module = instance->getParentOfType<ModuleOp>();
1609 StringRef entryPointName =
1610 module->getAttrOfType<StringAttr>("calyx.entrypoint");
1611 if (instance.getComponentName() == entryPointName)
1612 return instance.emitOpError()
1613 <<
"cannot reference the entry-point component: '" << entryPointName
1617 SmallVector<PortInfo> componentPorts = referencedComponent.getPortInfo();
1618 size_t numPorts = componentPorts.size();
1620 size_t numResults = instance.getNumResults();
1621 if (numResults != numPorts)
1622 return instance.emitOpError()
1623 <<
"has a wrong number of results; expected: " << numPorts
1624 <<
" but got " << numResults;
1626 for (
size_t i = 0; i != numResults; ++i) {
1627 auto resultType = instance.getResult(i).getType();
1628 auto expectedType = componentPorts[i].type;
1629 if (resultType == expectedType)
1631 return instance.emitOpError()
1632 <<
"result type for " << componentPorts[i].name <<
" must be "
1633 << expectedType <<
", but got " << resultType;
1638LogicalResult InstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1639 Operation *op = *
this;
1640 auto module = op->getParentOfType<ModuleOp>();
1641 Operation *referencedComponent =
1642 symbolTable.lookupNearestSymbolFrom(module, getComponentNameAttr());
1643 if (referencedComponent ==
nullptr)
1644 return emitError() <<
"referencing component: '" << getComponentName()
1645 <<
"', which does not exist.";
1647 Operation *shadowedComponentName =
1648 symbolTable.lookupNearestSymbolFrom(module, getSymNameAttr());
1649 if (shadowedComponentName !=
nullptr)
1650 return emitError() <<
"instance symbol: '" << instanceName()
1651 <<
"' is already a symbol for another component.";
1654 auto parentComponent = op->getParentOfType<ComponentOp>();
1655 if (parentComponent == referencedComponent)
1656 return emitError() <<
"recursive instantiation of its parent component: '"
1657 << getComponentName() <<
"'";
1659 assert(isa<ComponentInterface>(referencedComponent) &&
1660 "Should be a ComponentInterface.");
1662 cast<ComponentInterface>(referencedComponent));
1670SmallVector<StringRef> InstanceOp::portNames() {
1671 SmallVector<StringRef> portNames;
1672 for (Attribute name : getReferencedComponent().getPortNames())
1673 portNames.push_back(cast<StringAttr>(name).getValue());
1677SmallVector<Direction> InstanceOp::portDirections() {
1678 SmallVector<Direction> portDirections;
1680 portDirections.push_back(port.direction);
1681 return portDirections;
1684SmallVector<DictionaryAttr> InstanceOp::portAttributes() {
1685 SmallVector<DictionaryAttr> portAttributes;
1687 portAttributes.push_back(port.attributes);
1688 return portAttributes;
1691bool InstanceOp::isCombinational() {
1692 return isa<CombComponentOp>(getReferencedComponent());
1702 auto module = (*this)->getParentOfType<ModuleOp>();
1706 return module.lookupSymbol<hw::HWModuleExternOp>(getPrimitiveName());
1715 auto module = instance->getParentOfType<ModuleOp>();
1716 StringRef entryPointName =
1717 module->getAttrOfType<StringAttr>("calyx.entrypoint");
1718 if (instance.getPrimitiveName() == entryPointName)
1719 return instance.emitOpError()
1720 <<
"cannot reference the entry-point component: '" << entryPointName
1724 auto primitivePorts = referencedPrimitive.getPortList();
1725 size_t numPorts = primitivePorts.size();
1727 size_t numResults = instance.getNumResults();
1728 if (numResults != numPorts)
1729 return instance.emitOpError()
1730 <<
"has a wrong number of results; expected: " << numPorts
1731 <<
" but got " << numResults;
1734 ArrayAttr modParameters = referencedPrimitive.getParameters();
1735 ArrayAttr parameters = instance.getParameters().value_or(ArrayAttr());
1736 size_t numExpected = modParameters.size();
1737 size_t numParams = parameters.size();
1738 if (numParams != numExpected)
1739 return instance.emitOpError()
1740 <<
"has the wrong number of parameters; expected: " << numExpected
1741 <<
" but got " << numParams;
1743 for (
size_t i = 0; i != numExpected; ++i) {
1744 auto param = cast<circt::hw::ParamDeclAttr>(parameters[i]);
1745 auto modParam = cast<circt::hw::ParamDeclAttr>(modParameters[i]);
1747 auto paramName = param.getName();
1748 if (paramName != modParam.getName())
1749 return instance.emitOpError()
1750 <<
"parameter #" << i <<
" should have name " << modParam.getName()
1751 <<
" but has name " << paramName;
1753 if (param.getType() != modParam.getType())
1754 return instance.emitOpError()
1755 <<
"parameter " << paramName <<
" should have type "
1756 << modParam.getType() <<
" but has type " << param.getType();
1760 if (!param.getValue())
1761 return instance.emitOpError(
"parameter ")
1762 << paramName <<
" must have a value";
1765 for (
size_t i = 0; i != numResults; ++i) {
1766 auto resultType = instance.getResult(i).getType();
1767 auto expectedType = primitivePorts[i].type;
1768 auto replacedType = hw::evaluateParametricType(
1769 instance.getLoc(), instance.getParametersAttr(), expectedType);
1770 if (failed(replacedType))
1772 if (resultType == replacedType)
1774 return instance.emitOpError()
1775 <<
"result type for " << primitivePorts[i].name <<
" must be "
1776 << expectedType <<
", but got " << resultType;
1782PrimitiveOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1783 Operation *op = *
this;
1784 auto module = op->getParentOfType<ModuleOp>();
1785 Operation *referencedPrimitive =
1786 symbolTable.lookupNearestSymbolFrom(module, getPrimitiveNameAttr());
1787 if (referencedPrimitive ==
nullptr)
1788 return emitError() <<
"referencing primitive: '" << getPrimitiveName()
1789 <<
"', which does not exist.";
1791 Operation *shadowedPrimitiveName =
1792 symbolTable.lookupNearestSymbolFrom(module, getSymNameAttr());
1793 if (shadowedPrimitiveName !=
nullptr)
1794 return emitError() <<
"instance symbol: '" << instanceName()
1795 <<
"' is already a symbol for another primitive.";
1799 if (parentPrimitive == referencedPrimitive)
1800 return emitError() <<
"recursive instantiation of its parent primitive: '"
1801 << getPrimitiveName() <<
"'";
1803 assert(isa<hw::HWModuleExternOp>(referencedPrimitive) &&
1804 "Should be a HardwareModuleExternOp.");
1807 cast<hw::HWModuleExternOp>(referencedPrimitive));
1815SmallVector<StringRef> PrimitiveOp::portNames() {
1816 SmallVector<StringRef> portNames;
1817 auto ports = getReferencedPrimitive().getPortList();
1818 for (
auto port : ports)
1819 portNames.push_back(port.name.getValue());
1825 switch (direction) {
1826 case hw::ModulePort::Direction::Input:
1827 return Direction::Input;
1828 case hw::ModulePort::Direction::Output:
1829 return Direction::Output;
1830 case hw::ModulePort::Direction::InOut:
1831 llvm_unreachable(
"InOut ports not supported by Calyx");
1833 llvm_unreachable(
"Impossible port type");
1836SmallVector<Direction> PrimitiveOp::portDirections() {
1837 SmallVector<Direction> portDirections;
1838 auto ports = getReferencedPrimitive().getPortList();
1841 return portDirections;
1844bool PrimitiveOp::isCombinational() {
return false; }
1850 DictionaryAttr dict) {
1854 llvm::SmallVector<NamedAttribute> attrs;
1855 for (NamedAttribute attr : dict) {
1856 Dialect *dialect = attr.getNameDialect();
1857 if (dialect ==
nullptr || !isa<CalyxDialect>(*dialect))
1859 StringRef name = attr.getName().strref();
1860 StringAttr newName = builder.getStringAttr(std::get<1>(name.split(
".")));
1861 attr.setName(newName);
1862 attrs.push_back(attr);
1864 return builder.getDictionaryAttr(attrs);
1868SmallVector<DictionaryAttr> PrimitiveOp::portAttributes() {
1869 SmallVector<DictionaryAttr> portAttributes;
1870 OpBuilder builder(getContext());
1872 auto argAttrs = prim.getAllInputAttrs();
1873 auto resAttrs = prim.getAllOutputAttrs();
1874 for (
auto a : argAttrs)
1875 portAttributes.push_back(
1877 for (
auto a : resAttrs)
1878 portAttributes.push_back(
1880 return portAttributes;
1889 SmallVector<Attribute> ¶meters) {
1891 return parser.parseCommaSeparatedList(
1892 OpAsmParser::Delimiter::OptionalLessGreater, [&]() {
1897 if (parser.parseKeywordOrString(&name) || parser.parseColonType(type))
1901 if (succeeded(parser.parseOptionalEqual())) {
1902 if (parser.parseAttribute(value, type))
1906 auto &builder = parser.getBuilder();
1907 parameters.push_back(hw::ParamDeclAttr::get(
1908 builder.getContext(), builder.getStringAttr(name), type, value));
1915 ArrayAttr ¶meters) {
1916 SmallVector<Attribute> parseParameters;
1920 parameters = ArrayAttr::get(parser.getContext(), parseParameters);
1927 ArrayAttr parameters) {
1928 if (parameters.empty())
1932 llvm::interleaveComma(parameters, p, [&](Attribute param) {
1933 auto paramAttr = cast<hw::ParamDeclAttr>(param);
1934 p << paramAttr.getName().getValue() <<
": " << paramAttr.getType();
1935 if (
auto value = paramAttr.getValue()) {
1937 p.printAttributeWithoutType(value);
1951 auto parent = (*this)->getParentOfType<GroupOp>();
1952 StringRef name = parent.getSymName();
1953 std::string resultName = name.str() +
".go";
1954 setNameFn(getResult(), resultName);
1957void GroupGoOp::print(OpAsmPrinter &p) {
printGroupPort(p, *
this); }
1959ParseResult GroupGoOp::parse(OpAsmParser &parser, OperationState &result) {
1963 result.addTypes(parser.getBuilder().getI1Type());
1971LogicalResult GroupDoneOp::verify() {
1972 Operation *srcOp = getSrc().getDefiningOp();
1973 Value optionalGuard = getGuard();
1974 Operation *guardOp = optionalGuard ? optionalGuard.getDefiningOp() :
nullptr;
1975 bool noGuard = (guardOp ==
nullptr);
1977 if (srcOp ==
nullptr)
1981 if (isa<hw::ConstantOp>(srcOp) && (noGuard || isa<hw::ConstantOp>(guardOp)))
1982 return emitOpError() <<
"with constant source"
1983 << (noGuard ?
"" :
" and constant guard")
1984 <<
". This should be a combinational group.";
1989void GroupDoneOp::print(OpAsmPrinter &p) {
printGroupPort(p, *
this); }
1991ParseResult GroupDoneOp::parse(OpAsmParser &parser, OperationState &result) {
1998void ConstantOp::getAsmResultNames(
1999 function_ref<
void(Value, StringRef)> setNameFn) {
2000 if (isa<FloatAttr>(getValue())) {
2001 setNameFn(getResult(),
"cst");
2004 auto intCst = llvm::dyn_cast<IntegerAttr>(getValue());
2005 auto intType = llvm::dyn_cast<IntegerType>(getType());
2008 if (intType && intType.getWidth() == 1)
2009 return setNameFn(getResult(), intCst.getInt() > 0 ?
"true" :
"false");
2012 SmallString<32> specialNameBuffer;
2013 llvm::raw_svector_ostream specialName(specialNameBuffer);
2014 specialName <<
'c' << intCst.getValue();
2016 specialName <<
'_' << getType();
2017 setNameFn(getResult(), specialName.str());
2020LogicalResult ConstantOp::verify() {
2021 auto type = getType();
2022 assert(isa<IntegerType>(type) &&
"must be an IntegerType");
2024 if (
auto valTyBitWidth = getValue().getType().getIntOrFloatBitWidth();
2025 valTyBitWidth != type.getIntOrFloatBitWidth()) {
2026 return emitOpError() <<
"value type bit width" << valTyBitWidth
2027 <<
" must match return type: "
2028 << type.getIntOrFloatBitWidth();
2031 if (llvm::isa<IntegerType>(type) &&
2032 !llvm::cast<IntegerType>(type).isSignless())
2033 return emitOpError(
"integer return type must be signless");
2035 if (!llvm::isa<IntegerAttr, FloatAttr>(getValue())) {
2036 return emitOpError(
"value must be an integer or float attribute");
2042OpFoldResult calyx::ConstantOp::fold(FoldAdaptor adaptor) {
2043 return getValueAttr();
2046void calyx::ConstantOp::build(OpBuilder &builder, OperationState &state,
2047 StringRef symName, Attribute attr, Type type) {
2048 state.addAttribute(SymbolTable::getSymbolAttrName(),
2049 builder.getStringAttr(symName));
2050 state.addAttribute(
"value", attr);
2051 SmallVector<Type> types;
2052 types.push_back(type);
2053 state.addTypes(types);
2056SmallVector<StringRef> ConstantOp::portNames() {
return {
"out"}; }
2058SmallVector<Direction> ConstantOp::portDirections() {
return {
Output}; }
2060SmallVector<DictionaryAttr> ConstantOp::portAttributes() {
2061 return {DictionaryAttr::get(getContext())};
2064bool ConstantOp::isCombinational() {
return true; }
2075SmallVector<StringRef> RegisterOp::portNames() {
2079SmallVector<Direction> RegisterOp::portDirections() {
2083SmallVector<DictionaryAttr> RegisterOp::portAttributes() {
2084 MLIRContext *context = getContext();
2085 IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1);
2086 NamedAttrList writeEn,
clk, reset, done;
2087 writeEn.append(
goPort, isSet);
2092 DictionaryAttr::get(context),
2093 writeEn.getDictionary(context),
2094 clk.getDictionary(context),
2095 reset.getDictionary(context),
2096 DictionaryAttr::get(context),
2097 done.getDictionary(context)
2101bool RegisterOp::isCombinational() {
return false; }
2112SmallVector<StringRef> MemoryOp::portNames() {
2113 SmallVector<StringRef> portNames;
2114 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i) {
2116 StringAttr::get(this->getContext(),
"addr" + std::to_string(i));
2117 portNames.push_back(nameAttr.getValue());
2119 portNames.append({
"write_data",
"write_en",
clkPort,
"read_data",
donePort});
2123SmallVector<Direction> MemoryOp::portDirections() {
2124 SmallVector<Direction> portDirections;
2125 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2126 portDirections.push_back(
Input);
2128 return portDirections;
2131SmallVector<DictionaryAttr> MemoryOp::portAttributes() {
2132 SmallVector<DictionaryAttr> portAttributes;
2133 MLIRContext *context = getContext();
2134 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2135 portAttributes.push_back(DictionaryAttr::get(context));
2138 IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1);
2139 NamedAttrList writeEn,
clk, reset, done;
2140 writeEn.append(
goPort, isSet);
2143 portAttributes.append({DictionaryAttr::get(context),
2144 writeEn.getDictionary(context),
2145 clk.getDictionary(context),
2146 DictionaryAttr::get(context),
2147 done.getDictionary(context)}
2149 return portAttributes;
2152void MemoryOp::build(OpBuilder &builder, OperationState &state,
2153 StringRef instanceName, int64_t width,
2154 ArrayRef<int64_t> sizes, ArrayRef<int64_t> addrSizes) {
2155 state.addAttribute(SymbolTable::getSymbolAttrName(),
2156 builder.getStringAttr(instanceName));
2157 state.addAttribute(
"width", builder.getI64IntegerAttr(width));
2158 state.addAttribute(
"sizes", builder.getI64ArrayAttr(sizes));
2159 state.addAttribute(
"addrSizes", builder.getI64ArrayAttr(addrSizes));
2160 SmallVector<Type> types;
2161 for (int64_t size : addrSizes)
2162 types.push_back(builder.getIntegerType(size));
2163 types.push_back(builder.getIntegerType(width));
2164 types.push_back(builder.getI1Type());
2165 types.push_back(builder.getI1Type());
2166 types.push_back(builder.getIntegerType(width));
2167 types.push_back(builder.getI1Type());
2168 state.addTypes(types);
2171LogicalResult MemoryOp::verify() {
2172 ArrayRef<Attribute> opSizes = getSizes().getValue();
2173 ArrayRef<Attribute> opAddrSizes = getAddrSizes().getValue();
2174 size_t numDims = getSizes().size();
2175 size_t numAddrs = getAddrSizes().size();
2176 if (numDims != numAddrs)
2177 return emitOpError(
"mismatched number of dimensions (")
2178 << numDims <<
") and address sizes (" << numAddrs <<
")";
2180 size_t numExtraPorts = 5;
2181 if (getNumResults() != numAddrs + numExtraPorts)
2182 return emitOpError(
"incorrect number of address ports, expected ")
2185 for (
size_t i = 0; i < numDims; ++i) {
2186 int64_t size = cast<IntegerAttr>(opSizes[i]).getInt();
2187 int64_t addrSize = cast<IntegerAttr>(opAddrSizes[i]).getInt();
2188 if (llvm::Log2_64_Ceil(size) > addrSize)
2189 return emitOpError(
"address size (")
2190 << addrSize <<
") for dimension " << i
2191 <<
" can't address the entire range (" << size <<
")";
2206SmallVector<StringRef> SeqMemoryOp::portNames() {
2207 SmallVector<StringRef> portNames;
2208 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i) {
2210 StringAttr::get(this->getContext(),
"addr" + std::to_string(i));
2211 portNames.push_back(nameAttr.getValue());
2213 portNames.append({
clkPort,
"reset",
"content_en",
"write_en",
"write_data",
2214 "read_data",
"done"});
2218SmallVector<Direction> SeqMemoryOp::portDirections() {
2219 SmallVector<Direction> portDirections;
2220 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2221 portDirections.push_back(
Input);
2223 return portDirections;
2226SmallVector<DictionaryAttr> SeqMemoryOp::portAttributes() {
2227 SmallVector<DictionaryAttr> portAttributes;
2228 MLIRContext *context = getContext();
2229 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2230 portAttributes.push_back(DictionaryAttr::get(context));
2232 OpBuilder builder(context);
2234 IntegerAttr isSet = IntegerAttr::get(builder.getIndexType(), 1);
2235 IntegerAttr isTwo = IntegerAttr::get(builder.getIndexType(), 2);
2236 NamedAttrList done,
clk, reset, contentEn;
2240 contentEn.append(
goPort, isTwo);
2241 portAttributes.append({
clk.getDictionary(context),
2242 reset.getDictionary(context),
2243 contentEn.getDictionary(context),
2244 DictionaryAttr::get(context),
2245 DictionaryAttr::get(context),
2246 DictionaryAttr::get(context),
2247 done.getDictionary(context)}
2249 return portAttributes;
2252void SeqMemoryOp::build(OpBuilder &builder, OperationState &state,
2253 StringRef instanceName, int64_t width,
2254 ArrayRef<int64_t> sizes, ArrayRef<int64_t> addrSizes) {
2255 state.addAttribute(SymbolTable::getSymbolAttrName(),
2256 builder.getStringAttr(instanceName));
2257 state.addAttribute(
"width", builder.getI64IntegerAttr(width));
2258 state.addAttribute(
"sizes", builder.getI64ArrayAttr(sizes));
2259 state.addAttribute(
"addrSizes", builder.getI64ArrayAttr(addrSizes));
2260 SmallVector<Type> types;
2261 for (int64_t size : addrSizes)
2262 types.push_back(builder.getIntegerType(size));
2263 types.push_back(builder.getI1Type());
2264 types.push_back(builder.getI1Type());
2265 types.push_back(builder.getI1Type());
2266 types.push_back(builder.getI1Type());
2267 types.push_back(builder.getIntegerType(width));
2268 types.push_back(builder.getIntegerType(width));
2269 types.push_back(builder.getI1Type());
2270 state.addTypes(types);
2273LogicalResult SeqMemoryOp::verify() {
2274 ArrayRef<Attribute> opSizes = getSizes().getValue();
2275 ArrayRef<Attribute> opAddrSizes = getAddrSizes().getValue();
2276 size_t numDims = getSizes().size();
2277 size_t numAddrs = getAddrSizes().size();
2278 if (numDims != numAddrs)
2279 return emitOpError(
"mismatched number of dimensions (")
2280 << numDims <<
") and address sizes (" << numAddrs <<
")";
2282 size_t numExtraPorts =
2284 if (getNumResults() != numAddrs + numExtraPorts)
2285 return emitOpError(
"incorrect number of address ports, expected ")
2288 for (
size_t i = 0; i < numDims; ++i) {
2289 int64_t size = cast<IntegerAttr>(opSizes[i]).getInt();
2290 int64_t addrSize = cast<IntegerAttr>(opAddrSizes[i]).getInt();
2291 if (llvm::Log2_64_Ceil(size) > addrSize)
2292 return emitOpError(
"address size (")
2293 << addrSize <<
") for dimension " << i
2294 <<
" can't address the entire range (" << size <<
")";
2303LogicalResult EnableOp::verify() {
2304 auto component = (*this)->getParentOfType<ComponentOp>();
2305 auto wiresOp = component.getWiresOp();
2306 StringRef name = getGroupName();
2308 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(name);
2310 return emitOpError() <<
"with group '" << name
2311 <<
"', which does not exist.";
2313 if (isa<CombGroupOp>(groupOp))
2314 return emitOpError() <<
"with group '" << name
2315 <<
"', which is a combinational group.";
2324LogicalResult IfOp::verify() {
2325 std::optional<StringRef> optGroupName = getGroupName();
2326 if (!optGroupName) {
2330 auto component = (*this)->getParentOfType<ComponentOp>();
2331 WiresOp wiresOp = component.getWiresOp();
2332 StringRef groupName = *optGroupName;
2333 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(groupName);
2335 return emitOpError() <<
"with group '" << groupName
2336 <<
"', which does not exist.";
2338 if (isa<GroupOp>(groupOp))
2339 return emitOpError() <<
"with group '" << groupName
2340 <<
"', which is not a combinational group.";
2342 if (failed(groupOp.drivesPort(getCond())))
2343 return emitError() <<
"with conditional op: '"
2345 <<
"' expected to be driven from group: '" << groupName
2346 <<
"' but no driver was found.";
2354template <
typename OpTy>
2356 static_assert(IsAny<OpTy, SeqOp, StaticSeqOp>(),
2357 "Should be a StaticSeqOp or SeqOp.");
2358 if (parent.getBodyBlock()->empty())
2359 return std::nullopt;
2360 auto &lastOp = parent.getBodyBlock()->back();
2361 if (
auto enableOp = dyn_cast<EnableOp>(lastOp))
2363 if (
auto seqOp = dyn_cast<SeqOp>(lastOp))
2365 if (
auto staticSeqOp = dyn_cast<StaticSeqOp>(lastOp))
2368 return std::nullopt;
2373template <
typename OpTy>
2375 static_assert(IsAny<OpTy, ParOp, StaticParOp>(),
2376 "Should be a StaticParOp or ParOp.");
2378 llvm::StringMap<EnableOp> enables;
2379 Block *body = parent.getBodyBlock();
2380 for (EnableOp op : body->getOps<EnableOp>())
2381 enables.insert(std::pair(op.getGroupName(), op));
2393template <
typename IfOpTy,
typename TailOpTy>
2395 static_assert(IsAny<TailOpTy, SeqOp, ParOp, StaticSeqOp, StaticParOp>(),
2396 "Should be a SeqOp, ParOp, StaticSeqOp, or StaticParOp.");
2397 static_assert(IsAny<IfOpTy, IfOp, StaticIfOp>(),
2398 "Should be a IfOp or StaticIfOp.");
2400 if (!op.thenBodyExists() || !op.elseBodyExists())
2402 if (op.getThenBody()->empty() || op.getElseBody()->empty())
2405 Block *thenBody = op.getThenBody(), *elseBody = op.getElseBody();
2406 return isa<TailOpTy>(thenBody->front()) && isa<TailOpTy>(elseBody->front());
2417template <
typename IfOpTy,
typename SeqOpTy>
2419 PatternRewriter &rewriter) {
2420 static_assert(IsAny<IfOpTy, IfOp, StaticIfOp>(),
2421 "Should be an IfOp or StaticIfOp.");
2422 static_assert(IsAny<SeqOpTy, SeqOp, StaticSeqOp>(),
2423 "Branches should be checking for an SeqOp or StaticSeqOp");
2424 if (!hasCommonTailPatternPreConditions<IfOpTy, SeqOpTy>(ifOp))
2426 auto thenControl = cast<SeqOpTy>(ifOp.getThenBody()->front()),
2427 elseControl = cast<SeqOpTy>(ifOp.getElseBody()->front());
2429 std::optional<EnableOp> lastThenEnableOp =
getLastEnableOp(thenControl),
2432 if (!lastThenEnableOp || !lastElseEnableOp)
2434 if (lastThenEnableOp->getGroupName() != lastElseEnableOp->getGroupName())
2440 rewriter.setInsertionPointAfter(ifOp);
2441 SeqOpTy seqOp = SeqOpTy::create(rewriter, ifOp.getLoc());
2442 Block *body = seqOp.getBodyBlock();
2444 body->push_back(ifOp);
2445 rewriter.setInsertionPointToEnd(body);
2446 EnableOp::create(rewriter, seqOp.getLoc(), lastThenEnableOp->getGroupName());
2449 rewriter.eraseOp(*lastThenEnableOp);
2450 rewriter.eraseOp(*lastElseEnableOp);
2467template <
typename OpTy,
typename ParOpTy>
2469 PatternRewriter &rewriter) {
2470 static_assert(IsAny<OpTy, IfOp, StaticIfOp>(),
2471 "Should be an IfOp or StaticIfOp.");
2472 static_assert(IsAny<ParOpTy, ParOp, StaticParOp>(),
2473 "Branches should be checking for an ParOp or StaticParOp");
2474 if (!hasCommonTailPatternPreConditions<OpTy, ParOpTy>(controlOp))
2476 auto thenControl = cast<ParOpTy>(controlOp.getThenBody()->front()),
2477 elseControl = cast<ParOpTy>(controlOp.getElseBody()->front());
2482 SmallVector<StringRef> groupNames;
2483 for (
auto aIndex = a.begin(); aIndex != a.end(); ++aIndex) {
2484 StringRef groupName = aIndex->getKey();
2485 auto bIndex = b.find(groupName);
2486 if (bIndex == b.end())
2489 groupNames.push_back(groupName);
2491 rewriter.eraseOp(aIndex->getValue());
2492 rewriter.eraseOp(bIndex->getValue());
2498 rewriter.setInsertionPointAfter(controlOp);
2500 ParOpTy parOp = ParOpTy::create(rewriter, controlOp.getLoc());
2501 Block *body = parOp.getBodyBlock();
2502 controlOp->remove();
2503 body->push_back(controlOp);
2506 rewriter.setInsertionPointToEnd(body);
2507 for (StringRef groupName : groupNames)
2508 EnableOp::create(rewriter, parOp.getLoc(), groupName);
2519 PatternRewriter &rewriter)
const override {
2520 if (!ifOp.getThenBody()->empty())
2522 if (ifOp.elseBodyExists() && !ifOp.getElseBody()->empty())
2531void IfOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
2532 MLIRContext *context) {
2534 patterns.add(commonTailPatternWithPar<IfOp, ParOp>);
2535 patterns.add(commonTailPatternWithSeq<IfOp, SeqOp>);
2541LogicalResult StaticIfOp::verify() {
2542 if (elseBodyExists()) {
2543 auto *elseBod = getElseBody();
2544 auto &elseOps = elseBod->getOperations();
2546 for (Operation &op : elseOps) {
2548 return op.emitOpError(
2549 "static if's else branch has non static control within it");
2554 auto *thenBod = getThenBody();
2555 auto &thenOps = thenBod->getOperations();
2556 for (Operation &op : thenOps) {
2559 return op.emitOpError(
2560 "static if's then branch has non static control within it");
2573 PatternRewriter &rewriter)
const override {
2574 if (!ifOp.getThenBody()->empty())
2576 if (ifOp.elseBodyExists() && !ifOp.getElseBody()->empty())
2585void StaticIfOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
2586 MLIRContext *context) {
2588 patterns.add(commonTailPatternWithPar<StaticIfOp, StaticParOp>);
2589 patterns.add(commonTailPatternWithSeq<StaticIfOp, StaticSeqOp>);
2595LogicalResult WhileOp::verify() {
2596 auto component = (*this)->getParentOfType<ComponentOp>();
2597 auto wiresOp = component.getWiresOp();
2599 std::optional<StringRef> optGroupName = getGroupName();
2600 if (!optGroupName) {
2604 StringRef groupName = *optGroupName;
2605 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(groupName);
2607 return emitOpError() <<
"with group '" << groupName
2608 <<
"', which does not exist.";
2610 if (isa<GroupOp>(groupOp))
2611 return emitOpError() <<
"with group '" << groupName
2612 <<
"', which is not a combinational group.";
2614 if (failed(groupOp.drivesPort(getCond())))
2615 return emitError() <<
"conditional op: '" <<
valueName(component, getCond())
2616 <<
"' expected to be driven from group: '" << groupName
2617 <<
"' but no driver was found.";
2622LogicalResult WhileOp::canonicalize(
WhileOp whileOp,
2623 PatternRewriter &rewriter) {
2624 if (whileOp.getBodyBlock()->empty()) {
2635LogicalResult StaticRepeatOp::verify() {
2636 for (
auto &&bodyOp : (*this).getRegion().front()) {
2639 return bodyOp.emitOpError(
2640 "static repeat has non static control within it");
2647template <
typename OpTy>
2648static LogicalResult
zeroRepeat(OpTy op, PatternRewriter &rewriter) {
2649 static_assert(IsAny<OpTy, RepeatOp, StaticRepeatOp>(),
2650 "Should be a RepeatOp or StaticPRepeatOp");
2651 if (op.getCount() == 0) {
2652 Block *controlBody = op.getBodyBlock();
2653 for (
auto &op : make_early_inc_range(*controlBody))
2656 rewriter.eraseOp(op);
2663void StaticRepeatOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
2664 MLIRContext *context) {
2665 patterns.add(emptyControl<StaticRepeatOp>);
2666 patterns.add(zeroRepeat<StaticRepeatOp>);
2672void RepeatOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
2673 MLIRContext *context) {
2674 patterns.add(emptyControl<RepeatOp>);
2675 patterns.add(zeroRepeat<RepeatOp>);
2685 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &ports,
2686 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &inputs,
2687 SmallVectorImpl<Attribute> &portNames,
2688 SmallVectorImpl<Attribute> &inputNames,
2689 SmallVectorImpl<Type> &types) {
2690 OpAsmParser::UnresolvedOperand port;
2691 OpAsmParser::UnresolvedOperand input;
2693 auto parseParameter = [&]() -> ParseResult {
2694 if (parser.parseOperand(port) || parser.parseEqual() ||
2695 parser.parseOperand(input))
2697 ports.push_back(port);
2698 portNames.push_back(StringAttr::get(parser.getContext(), port.name));
2699 inputs.push_back(input);
2700 inputNames.push_back(StringAttr::get(parser.getContext(), input.name));
2703 if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
2706 if (parser.parseArrow())
2709 if (parser.parseType(type))
2711 types.push_back(type);
2714 return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
2718ParseResult InvokeOp::parse(OpAsmParser &parser, OperationState &result) {
2719 StringAttr componentName;
2720 SmallVector<OpAsmParser::UnresolvedOperand, 4> ports;
2721 SmallVector<OpAsmParser::UnresolvedOperand, 4> inputs;
2722 SmallVector<Attribute> portNames;
2723 SmallVector<Attribute> inputNames;
2724 SmallVector<Type, 4> types;
2725 if (parser.parseSymbolName(componentName))
2727 FlatSymbolRefAttr callee = FlatSymbolRefAttr::get(componentName);
2728 SMLoc loc = parser.getCurrentLocation();
2730 SmallVector<Attribute, 4> refCells;
2731 if (succeeded(parser.parseOptionalLSquare())) {
2732 if (parser.parseCommaSeparatedList([&]() -> ParseResult {
2733 std::string refCellName;
2734 std::string externalMem;
2735 NamedAttrList refCellAttr;
2736 if (parser.parseKeywordOrString(&refCellName) ||
2737 parser.parseEqual() || parser.parseKeywordOrString(&externalMem))
2739 auto externalMemAttr =
2740 SymbolRefAttr::get(parser.getContext(), externalMem);
2741 refCellAttr.append(StringAttr::get(parser.getContext(), refCellName),
2744 DictionaryAttr::get(parser.getContext(), refCellAttr));
2747 parser.parseRSquare())
2750 result.addAttribute(
"refCellsMap",
2751 ArrayAttr::get(parser.getContext(), refCells));
2753 result.addAttribute(
"callee", callee);
2757 if (parser.resolveOperands(ports, types, loc, result.operands))
2759 if (parser.resolveOperands(inputs, types, loc, result.operands))
2761 result.addAttribute(
"portNames",
2762 ArrayAttr::get(parser.getContext(), portNames));
2763 result.addAttribute(
"inputNames",
2764 ArrayAttr::get(parser.getContext(), inputNames));
2768void InvokeOp::print(OpAsmPrinter &p) {
2769 p <<
" @" << getCallee() <<
"[";
2770 auto refCellNamesMap = getRefCellsMap();
2771 llvm::interleaveComma(refCellNamesMap, p, [&](Attribute attr) {
2772 auto dictAttr = cast<DictionaryAttr>(attr);
2773 llvm::interleaveComma(dictAttr, p, [&](NamedAttribute namedAttr) {
2774 auto refCellName = namedAttr.getName().str();
2776 cast<FlatSymbolRefAttr>(namedAttr.getValue()).getValue();
2777 p << refCellName <<
" = " << externalMem;
2782 auto ports = getPorts();
2783 auto inputs = getInputs();
2784 llvm::interleaveComma(llvm::zip(ports, inputs), p, [&](
auto arg) {
2785 p << std::get<0>(arg) <<
" = " << std::get<1>(arg);
2788 llvm::interleaveComma(ports, p, [&](
auto port) { p << port.getType(); });
2795 bool isDestination) {
2804 Operation *operation = value.getDefiningOp();
2805 if (operation ==
nullptr)
2807 if (
auto *dialect = operation->getDialect(); isa<comb::CombDialect>(dialect))
2813Value InvokeOp::getInstGoValue() {
2814 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2815 Operation *operation = componentOp.lookupSymbol(getCallee());
2816 Value ret =
nullptr;
2817 llvm::TypeSwitch<Operation *>(operation)
2818 .Case<RegisterOp>([&](
auto op) { ret = operation->getResult(1); })
2819 .Case<MemoryOp, DivSPipeLibOp, DivUPipeLibOp, MultPipeLibOp,
2820 RemSPipeLibOp, RemUPipeLibOp>(
2821 [&](
auto op) { ret = operation->getResult(2); })
2822 .Case<InstanceOp>([&](
auto op) {
2823 auto portInfo = op.getReferencedComponent().getPortInfo();
2824 for (
auto [portInfo, res] :
2825 llvm::zip(portInfo, operation->getResults())) {
2826 if (portInfo.hasAttribute(
goPort))
2830 .Case<PrimitiveOp>([&](
auto op) {
2831 auto moduleExternOp = op.getReferencedPrimitive();
2832 auto argAttrs = moduleExternOp.getAllInputAttrs();
2833 for (
auto [attr, res] :
llvm::zip(argAttrs, op.getResults())) {
2834 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2835 if (!dictAttr.empty()) {
2836 if (dictAttr.begin()->getName().getValue() ==
"calyx.go")
2846Value InvokeOp::getInstDoneValue() {
2847 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2848 Operation *operation = componentOp.lookupSymbol(getCallee());
2849 Value ret =
nullptr;
2850 llvm::TypeSwitch<Operation *>(operation)
2851 .Case<RegisterOp, MemoryOp, DivSPipeLibOp, DivUPipeLibOp, MultPipeLibOp,
2852 RemSPipeLibOp, RemUPipeLibOp>([&](
auto op) {
2853 size_t doneIdx = operation->getResults().size() - 1;
2854 ret = operation->getResult(doneIdx);
2856 .Case<InstanceOp>([&](
auto op) {
2857 InstanceOp instanceOp = cast<InstanceOp>(operation);
2858 auto portInfo = instanceOp.getReferencedComponent().getPortInfo();
2859 for (
auto [portInfo, res] :
2860 llvm::zip(portInfo, operation->getResults())) {
2861 if (portInfo.hasAttribute(
donePort))
2865 .Case<PrimitiveOp>([&](
auto op) {
2866 PrimitiveOp primOp = cast<PrimitiveOp>(operation);
2867 auto moduleExternOp = primOp.getReferencedPrimitive();
2868 auto resAttrs = moduleExternOp.getAllOutputAttrs();
2869 for (
auto [attr, res] :
llvm::zip(resAttrs, primOp.getResults())) {
2870 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2871 if (!dictAttr.empty()) {
2872 if (dictAttr.begin()->getName().getValue() ==
"calyx.done")
2887 std::string str = isGo ?
"calyx.go" :
"calyx.done";
2888 for (Attribute attr : moduleExternOp.getAllInputAttrs()) {
2889 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2890 ret = llvm::count_if(dictAttr, [&](NamedAttribute iter) {
2891 return iter.getName().getValue() == str;
2898LogicalResult InvokeOp::verify() {
2899 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2900 StringRef callee = getCallee();
2901 Operation *operation = componentOp.lookupSymbol(callee);
2904 return emitOpError() <<
"with instance '@" << callee
2905 <<
"', which does not exist.";
2907 if (getInputs().
empty() && getRefCellsMap().
empty()) {
2908 return emitOpError() <<
"'@" << callee
2909 <<
"' has zero input and output port connections and "
2910 "has no passing-by-reference cells; "
2911 "expected at least one.";
2913 size_t goPortNum = 0, donePortNum = 0;
2916 llvm::TypeSwitch<Operation *>(operation)
2917 .Case<RegisterOp, DivSPipeLibOp, DivUPipeLibOp, MemoryOp, MultPipeLibOp,
2918 RemSPipeLibOp, RemUPipeLibOp>(
2919 [&](
auto op) { goPortNum = 1, donePortNum = 1; })
2920 .Case<InstanceOp>([&](
auto op) {
2921 auto portInfo = op.getReferencedComponent().getPortInfo();
2929 .Case<PrimitiveOp>([&](
auto op) {
2930 auto moduleExternOp = op.getReferencedPrimitive();
2936 if (goPortNum != 1 && donePortNum != 1)
2937 return emitOpError()
2938 <<
"'@" << callee <<
"'"
2939 <<
" is a combinational component and cannot be invoked, which must "
2940 "have single go port and single done port.";
2942 auto ports = getPorts();
2943 auto inputs = getInputs();
2945 Value goValue = getInstGoValue();
2946 Value doneValue = getInstDoneValue();
2947 for (
auto [port, input, portName, inputName] :
2948 llvm::zip(ports, inputs, getPortNames(), getInputNames())) {
2953 return emitOpError() <<
"'@" << callee <<
"' has input '"
2954 << cast<StringAttr>(portName).getValue()
2955 <<
"', which is a source port. The inputs are "
2956 "required to be destination ports.";
2958 if (port == goValue)
2959 return emitOpError() <<
"the go or write_en port of '@" << callee
2960 <<
"' cannot appear here.";
2963 return emitOpError() <<
"'@" << callee <<
"' has output '"
2964 << cast<StringAttr>(inputName).getValue()
2965 <<
"', which is a destination port. The inputs are "
2966 "required to be source ports.";
2968 return emitOpError() <<
"'@" << callee <<
"' has '"
2969 << cast<StringAttr>(inputName).getValue()
2970 <<
"', which is not a port or constant. Complex "
2971 "logic should be conducted in the guard.";
2972 if (input == doneValue)
2973 return emitOpError() <<
"the done port of '@" << callee
2974 <<
"' cannot appear here.";
2976 if (port.getDefiningOp() != operation && input.getDefiningOp() != operation)
2977 return emitOpError() <<
"the connection "
2978 << cast<StringAttr>(portName).getValue() <<
" = "
2979 << cast<StringAttr>(inputName).getValue()
2980 <<
" is not defined as an input port of '@" << callee
2990LogicalResult PadLibOp::verify() {
2991 unsigned inBits = getResult(0).getType().getIntOrFloatBitWidth();
2992 unsigned outBits = getResult(1).getType().getIntOrFloatBitWidth();
2993 if (inBits >= outBits)
2994 return emitOpError(
"expected input bits (")
2995 << inBits <<
')' <<
" to be less than output bits (" << outBits
3000LogicalResult SliceLibOp::verify() {
3001 unsigned inBits = getResult(0).getType().getIntOrFloatBitWidth();
3002 unsigned outBits = getResult(1).getType().getIntOrFloatBitWidth();
3003 if (inBits <= outBits)
3004 return emitOpError(
"expected input bits (")
3005 << inBits <<
')' <<
" to be greater than output bits (" << outBits
3010#define ImplBinPipeOpCellInterface(OpType, outName) \
3011 SmallVector<StringRef> OpType::portNames() { \
3012 return {clkPort, resetPort, goPort, "left", "right", outName, donePort}; \
3015 SmallVector<Direction> OpType::portDirections() { \
3016 return {Input, Input, Input, Input, Input, Output, Output}; \
3019 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
3020 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
3023 SmallVector<DictionaryAttr> OpType::portAttributes() { \
3024 MLIRContext *context = getContext(); \
3025 IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1); \
3026 NamedAttrList go, clk, reset, done; \
3027 go.append(goPort, isSet); \
3028 clk.append(clkPort, isSet); \
3029 reset.append(resetPort, isSet); \
3030 done.append(donePort, isSet); \
3032 clk.getDictionary(context), \
3033 reset.getDictionary(context), \
3034 go.getDictionary(context), \
3035 DictionaryAttr::get(context), \
3036 DictionaryAttr::get(context), \
3037 DictionaryAttr::get(context), \
3038 done.getDictionary(context) \
3042 bool OpType::isCombinational() { return false; }
3044#define ImplUnaryOpCellInterface(OpType) \
3045 SmallVector<StringRef> OpType::portNames() { return {"in", "out"}; } \
3046 SmallVector<Direction> OpType::portDirections() { return {Input, Output}; } \
3047 SmallVector<DictionaryAttr> OpType::portAttributes() { \
3048 return {DictionaryAttr::get(getContext()), \
3049 DictionaryAttr::get(getContext())}; \
3051 bool OpType::isCombinational() { return true; } \
3052 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
3053 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
3056#define ImplBinOpCellInterface(OpType) \
3057 SmallVector<StringRef> OpType::portNames() { \
3058 return {"left", "right", "out"}; \
3060 SmallVector<Direction> OpType::portDirections() { \
3061 return {Input, Input, Output}; \
3063 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
3064 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
3066 bool OpType::isCombinational() { return true; } \
3067 SmallVector<DictionaryAttr> OpType::portAttributes() { \
3068 return {DictionaryAttr::get(getContext()), \
3069 DictionaryAttr::get(getContext()), \
3070 DictionaryAttr::get(getContext())}; \
3114#include "circt/Dialect/Calyx/CalyxInterfaces.cpp.inc"
3117#define GET_OP_CLASSES
3118#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 Op getControlOrWiresFrom(ComponentOp op)
This is a helper function that should only be used to get the WiresOp or ControlOp of a ComponentOp,...
static LogicalResult verifyPrimitivePortDriving(AssignOp assign, GroupInterface group)
Verifies that certain ports of primitives are either driven or read together.
#define ImplBinPipeOpCellInterface(OpType, outName)
static bool portIsUsedInGroup(GroupInterface group, Value port, bool isDriven)
Determines whether the given port is used in the group.
static Value getBlockArgumentWithName(StringRef name, ComponentOp op)
Returns the Block argument with the given name from a ComponentOp.
static ParseResult parsePortDefList(OpAsmParser &parser, OperationState &result, SmallVectorImpl< OpAsmParser::Argument > &ports, SmallVectorImpl< Type > &portTypes, SmallVectorImpl< NamedAttrList > &portAttrs)
Parses the ports of a Calyx component signature, and adds the corresponding port names to attrName.
static std::string valueName(Operation *scopeOp, Value v)
Convenience function for getting the SSA name of v under the scope of operation scopeOp.
static LogicalResult verifyNotComplexSource(Op op)
Verify that the value is not a "complex" value.
static LogicalResult verifyInstanceOpType(InstanceOp instance, ComponentInterface referencedComponent)
Verifies the port information in comparison with the referenced component of an instance.
static LogicalResult collapseControl(OpTy controlOp, PatternRewriter &rewriter)
static bool hasCommonTailPatternPreConditions(IfOpTy op)
Checks preconditions for the common tail pattern.
Direction convertHWDirectionToCalyx(hw::ModulePort::Direction direction)
static SmallVector< PortInfo > getFilteredPorts(ComponentOp op, Pred p)
A helper function to return a filtered subset of a component's ports.
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 LogicalResult verifyInvokeOpValue(InvokeOp &op, Value &value, bool isDestination)
static void eraseControlWithGroupAndConditional(OpTy op, PatternRewriter &rewriter)
A helper function to check whether the conditional and group (if it exists) needs to be erased to mai...
static ParseResult parseComponentInterface(OpAsmParser &parser, OperationState &result)
static void printComponentInterface(OpAsmPrinter &p, ComponentInterface comp)
static LogicalResult hasRequiredPorts(ComponentOp op)
Determines whether the given ComponentOp has all the required ports.
static SmallVector< T > concat(const SmallVectorImpl< T > &a, const SmallVectorImpl< T > &b)
Returns a new vector containing the concatenation of vectors a and b.
static std::optional< EnableOp > getLastEnableOp(OpTy parent)
Returns the last EnableOp within the child tree of 'parentSeqOp' or parentStaticSeqOp.
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 llvm::StringMap< EnableOp > getAllEnableOpsInImmediateBody(OpTy parent)
Returns a mapping of {enabled Group name, EnableOp} for all EnableOps within the immediate ParOp's bo...
static LogicalResult portDrivenByGroup(GroupInterface groupOp, Value port)
Checks whether port is driven from within groupOp.
static LogicalResult zeroRepeat(OpTy op, PatternRewriter &rewriter)
static void buildComponentLike(OpBuilder &builder, OperationState &result, StringAttr name, ArrayRef< PortInfo > ports, bool combinational)
static void getCellAsmResultNames(OpAsmSetValueNameFn setNameFn, Operation *op, ArrayRef< StringRef > portNames)
Gives each result of the cell a meaningful name in the form: <instance-name>.
static LogicalResult commonTailPatternWithSeq(IfOpTy ifOp, PatternRewriter &rewriter)
seq { if a with @G { if a with @G { seq { ... calyx.enable @A } seq { ... } else { -> } else { seq { ...
static LogicalResult allPortsDrivenByGroup(GroupInterface group, ValueRange ports)
Checks whether all ports are driven within the group.
static LogicalResult verifyPortDirection(Operation *op, Value value, bool isDestination)
Determines whether the given direction is valid with the given inputs.
static size_t getHwModuleExtGoOrDonePortNumber(hw::HWModuleExternOp &moduleExternOp, bool isGo)
static bool isStaticControl(Operation *op)
Returns whether the given operation is a static control operator.
static void printParameterList(OpAsmPrinter &p, Operation *op, ArrayAttr parameters)
Print a parameter list for a module or instance. Same format as HW dialect.
static DictionaryAttr cleanCalyxPortAttrs(OpBuilder builder, DictionaryAttr dict)
Returns a new DictionaryAttr containing only the calyx dialect attrs in the input DictionaryAttr.
static void printGroupPort(OpAsmPrinter &p, GroupPortType op)
#define ImplUnaryOpCellInterface(OpType)
static ParseResult parseGroupPort(OpAsmParser &parser, OperationState &result)
static LogicalResult verifyComplexLogic(InvokeOp &op, Value &value)
static LogicalResult commonTailPatternWithPar(OpTy controlOp, PatternRewriter &rewriter)
if a with @G { par { par { if a with @G { ... par { ... } calyx.enable @A } else { calyx....
std::map< std::string, WriteChannelPort & > writePorts
static SmallVector< Block *, 8 > intersection(SmallVectorImpl< Block * > &v1, SmallVectorImpl< Block * > &v2)
Calculate intersection of two vectors, returns a new vector.
static ParseResult parseType(Type &result, StringRef name, AsmParser &parser)
Parse a type defined by this dialect.
static ParseResult parsePort(OpAsmParser &p, module_like_impl::PortParse &result)
Parse a single argument with the following syntax:
static Block * getBodyBlock(FModuleLike mod)
static InstancePath empty
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
bool isCombinational(Operation *op)
Return true if the specified operation is a combinational logic op.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
This pattern collapses a calyx.seq or calyx.par operation when it contains exactly one calyx....
LogicalResult matchAndRewrite(CtrlOp ctrlOp, PatternRewriter &rewriter) const override
This pattern checks for one of two cases that will lead to IfOp deletion: (1) Then and Else bodies ar...
LogicalResult matchAndRewrite(IfOp ifOp, PatternRewriter &rewriter) const override
This pattern checks for one of two cases that will lead to StaticIfOp deletion: (1) Then and Else bod...
LogicalResult matchAndRewrite(StaticIfOp ifOp, PatternRewriter &rewriter) const override
This holds information about the port for either a Component or Cell.
DictionaryAttr attributes
This holds the name, type, direction of a module's ports.