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/MapVector.h"
31#include "llvm/ADT/PriorityQueue.h"
32#include "llvm/ADT/STLExtras.h"
33#include "llvm/ADT/SmallSet.h"
34#include "llvm/ADT/StringExtras.h"
35#include "llvm/ADT/TypeSwitch.h"
36#include "llvm/Support/Casting.h"
47template <
class T,
class... Ts>
48struct IsAny : std::disjunction<std::is_same<T, Ts>...> {};
64 size_t numDirections = nIns + nOuts;
65 APInt portDirections(numDirections, 0);
66 for (
size_t i = nIns, e = numDirections; i != e; ++i)
67 portDirections.setBit(i);
69 return IntegerAttr::get(IntegerType::get(ctx, numDirections), portDirections);
78template <
typename CtrlOp>
82 PatternRewriter &rewriter)
const override {
83 auto &ops = ctrlOp.getBodyBlock()->getOperations();
85 (ops.size() == 1) && isa<EnableOp>(ops.front()) &&
86 isa<SeqOp, ParOp, StaticSeqOp, StaticParOp>(ctrlOp->getParentOp());
90 ops.front().moveBefore(ctrlOp);
91 rewriter.eraseOp(ctrlOp);
104template <
typename Op>
106 Operation *definingOp = op.getSrc().getDefiningOp();
107 if (definingOp ==
nullptr)
113 if (
auto dialect = definingOp->getDialect(); isa<comb::CombDialect>(dialect))
114 return op->emitOpError(
"has source that is not a port or constant. "
115 "Complex logic should be conducted in the guard.");
122static std::string
valueName(Operation *scopeOp, Value v) {
124 llvm::raw_string_ostream os(s);
129 AsmState asmState(scopeOp, OpPrintingFlags().assumeVerified());
130 v.printAsOperand(os, asmState);
137 Operation *definingOp = value.getDefiningOp();
138 return isa<BlockArgument>(value) ||
139 isa_and_nonnull<CellInterface>(definingOp);
144 Operation *op = arg.getOwner()->getParentOp();
145 assert(isa<ComponentInterface>(op) &&
146 "Only ComponentInterface should support lookup by BlockArgument.");
147 return cast<ComponentInterface>(op).getPortInfo()[arg.getArgNumber()];
152 return isa<ControlOp, SeqOp, IfOp, RepeatOp,
WhileOp, ParOp, StaticRepeatOp,
153 StaticParOp, StaticSeqOp, StaticIfOp>(op);
158 if (isa<EnableOp>(op)) {
160 auto component = op->getParentOfType<ComponentOp>();
161 auto enableOp = llvm::cast<EnableOp>(op);
162 StringRef groupName = enableOp.getGroupName();
163 auto group = component.getWiresOp().lookupSymbol<GroupInterface>(groupName);
164 return isa<StaticGroupOp>(group);
166 return isa<StaticIfOp, StaticSeqOp, StaticRepeatOp, StaticParOp>(op);
171 if (isa<SeqOp, ParOp, StaticSeqOp, StaticParOp>(op))
176 for (
auto ®ion : op->getRegions()) {
177 auto opsIt = region.getOps();
178 size_t numOperations = std::distance(opsIt.begin(), opsIt.end());
183 bool usesEnableAsCompositionOperator =
184 numOperations > 1 && llvm::any_of(region.front(), [](
auto &&bodyOp) {
185 return isa<EnableOp>(bodyOp);
187 if (usesEnableAsCompositionOperator)
188 return op->emitOpError(
189 "EnableOp is not a composition operator. It should be nested "
190 "in a control flow operation, such as \"calyx.seq\"");
194 size_t numControlFlowRegions =
196 if (numControlFlowRegions > 1)
197 return op->emitOpError(
198 "has an invalid control sequence. Multiple control flow operations "
199 "must all be nested in a single calyx.seq or calyx.par");
205 auto *opParent = op->getParentOp();
206 if (!isa<ModuleOp>(opParent))
207 return op->emitOpError()
208 <<
"has parent: " << opParent <<
", expected ModuleOp.";
213 auto opParent = op->getParentOp();
214 if (!isa<ComponentInterface>(opParent))
215 return op->emitOpError()
216 <<
"has parent: " << opParent <<
", expected ComponentInterface.";
221 auto parent = op->getParentOp();
223 if (isa<calyx::EnableOp>(op) &&
224 !isa_and_nonnull<calyx::CalyxDialect>(parent->getDialect())) {
233 return op->emitOpError()
234 <<
"has parent: " << parent
235 <<
", which is not allowed for a control-like operation.";
237 if (op->getNumRegions() == 0)
240 auto ®ion = op->getRegion(0);
242 auto isValidBodyOp = [](Operation *operation) {
243 return isa<EnableOp, InvokeOp, SeqOp, IfOp, RepeatOp,
WhileOp, ParOp,
244 StaticParOp, StaticRepeatOp, StaticSeqOp, StaticIfOp>(operation);
246 for (
auto &&bodyOp : region.front()) {
247 if (isValidBodyOp(&bodyOp))
250 return op->emitOpError()
251 <<
"has operation: " << bodyOp.getName()
252 <<
", which is not allowed in this control-like operation";
258 auto ifOp = dyn_cast<IfInterface>(op);
260 if (ifOp.elseBodyExists() && ifOp.getElseBody()->empty())
261 return ifOp->emitOpError() <<
"empty 'else' region.";
271 SmallVector<OpAsmParser::UnresolvedOperand, 2> operandInfos;
272 OpAsmParser::UnresolvedOperand guardOrSource;
273 if (parser.parseOperand(guardOrSource))
276 if (succeeded(parser.parseOptionalQuestion())) {
277 OpAsmParser::UnresolvedOperand source;
279 if (parser.parseOperand(source))
281 operandInfos.push_back(source);
284 operandInfos.push_back(guardOrSource);
289 if (parser.parseColonType(type) ||
290 parser.resolveOperands(operandInfos, type, result.operands))
297template <
typename GroupPortType>
299 static_assert(IsAny<GroupPortType, GroupGoOp, GroupDoneOp>(),
300 "Should be a Calyx Group port.");
304 Value guard = op.getGuard(), source = op.getSrc();
307 p << source <<
" : " << source.getType();
312template <
typename OpTy>
314 PatternRewriter &rewriter) {
315 static_assert(IsAny<OpTy, SeqOp, ParOp, StaticSeqOp, StaticParOp>(),
316 "Should be a SeqOp, ParOp, StaticSeqOp, or StaticParOp");
318 if (isa<OpTy>(controlOp->getParentOp())) {
319 Block *controlBody = controlOp.getBodyBlock();
322 for (
auto &op : make_early_inc_range(*controlBody))
323 op.moveBefore(controlOp);
324 rewriter.eraseOp(controlOp);
331template <
typename OpTy>
332static LogicalResult
emptyControl(OpTy controlOp, PatternRewriter &rewriter) {
333 if (controlOp.getBodyBlock()->empty()) {
334 rewriter.eraseOp(controlOp);
343template <
typename OpTy>
345 PatternRewriter &rewriter) {
346 static_assert(IsAny<OpTy, IfOp, WhileOp>(),
347 "This is only applicable to WhileOp and IfOp.");
350 Value cond = op.getCond();
351 std::optional<StringRef> groupName = op.getGroupName();
352 auto component = op->template getParentOfType<ComponentOp>();
353 rewriter.eraseOp(op);
357 auto group = component.getWiresOp().template lookupSymbol<GroupInterface>(
359 if (SymbolTable::symbolKnownUseEmpty(group, component.getRegion()))
360 rewriter.eraseOp(group);
363 if (!isa<BlockArgument>(cond) && cond.getDefiningOp()->use_empty())
364 rewriter.eraseOp(cond.getDefiningOp());
370template <
typename OpTy>
372 static_assert(std::is_same<OpTy, StaticIfOp>(),
373 "This is only applicable to StatifIfOp.");
376 Value cond = op.getCond();
377 rewriter.eraseOp(op);
380 if (!isa<BlockArgument>(cond) && cond.getDefiningOp()->use_empty())
381 rewriter.eraseOp(cond.getDefiningOp());
388template <
typename ComponentTy>
390 auto componentName = comp->template getAttrOfType<StringAttr>(
391 ::mlir::SymbolTable::getSymbolAttrName())
394 p.printSymbolName(componentName);
397 auto printPortDefList = [&](
auto ports) {
399 llvm::interleaveComma(ports, p, [&](
const PortInfo &port) {
400 p <<
"%" << port.
name.getValue() <<
": " << port.
type;
408 printPortDefList(comp.getInputPortInfo());
410 printPortDefList(comp.getOutputPortInfo());
413 p.printRegion(*comp.getRegion(),
false,
417 SmallVector<StringRef> elidedAttrs = {
422 ComponentTy::getFunctionTypeAttrName(comp->getName()),
423 ComponentTy::getArgAttrsAttrName(comp->getName()),
424 ComponentTy::getResAttrsAttrName(comp->getName())};
425 p.printOptionalAttrDict(comp->getAttrs(), elidedAttrs);
432 SmallVectorImpl<OpAsmParser::Argument> &ports,
433 SmallVectorImpl<Type> &portTypes,
434 SmallVectorImpl<NamedAttrList> &portAttrs) {
436 OpAsmParser::Argument port;
439 if (parser.parseArgument(port) || parser.parseColon() ||
440 parser.parseType(portType))
442 port.type = portType;
443 ports.push_back(port);
444 portTypes.push_back(portType);
446 NamedAttrList portAttr;
447 portAttrs.push_back(succeeded(parser.parseOptionalAttrDict(portAttr))
453 return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
460 SmallVectorImpl<OpAsmParser::Argument> &ports,
461 SmallVectorImpl<Type> &portTypes) {
462 SmallVector<OpAsmParser::Argument> inPorts, outPorts;
463 SmallVector<Type> inPortTypes, outPortTypes;
464 SmallVector<NamedAttrList> portAttributes;
469 if (parser.parseArrow() ||
473 auto *
context = parser.getBuilder().getContext();
476 SmallVector<Attribute> portNames;
477 auto getPortName = [
context](
const auto &port) -> StringAttr {
478 StringRef name = port.ssaName.name;
479 if (name.starts_with(
"%"))
480 name = name.drop_front();
481 return StringAttr::get(
context, name);
483 llvm::transform(inPorts, std::back_inserter(portNames), getPortName);
484 llvm::transform(outPorts, std::back_inserter(portNames), getPortName);
486 result.addAttribute(
"portNames", ArrayAttr::get(
context, portNames));
491 ports.append(inPorts);
492 ports.append(outPorts);
493 portTypes.append(inPortTypes);
494 portTypes.append(outPortTypes);
496 SmallVector<Attribute> portAttrs;
497 llvm::transform(portAttributes, std::back_inserter(portAttrs),
498 [&](
auto attr) {
return attr.getDictionary(
context); });
499 result.addAttribute(
"portAttributes", ArrayAttr::get(
context, portAttrs));
504template <
typename ComponentTy>
506 OperationState &result) {
507 using namespace mlir::function_interface_impl;
509 StringAttr componentName;
510 if (parser.parseSymbolName(componentName,
511 ::mlir::SymbolTable::getSymbolAttrName(),
515 SmallVector<mlir::OpAsmParser::Argument> ports;
517 SmallVector<Type> portTypes;
523 auto type = parser.getBuilder().getFunctionType(portTypes, {});
524 result.addAttribute(ComponentTy::getFunctionTypeAttrName(result.name),
525 TypeAttr::get(type));
527 auto *body = result.addRegion();
528 if (parser.parseRegion(*body, ports))
532 body->push_back(
new Block());
534 if (parser.parseOptionalAttrDict(result.attributes))
542static SmallVector<T>
concat(
const SmallVectorImpl<T> &a,
543 const SmallVectorImpl<T> &b) {
551 StringAttr name, ArrayRef<PortInfo> ports,
552 bool combinational) {
553 using namespace mlir::function_interface_impl;
555 result.addAttribute(::mlir::SymbolTable::getSymbolAttrName(), name);
557 std::pair<SmallVector<Type, 8>, SmallVector<Type, 8>> portIOTypes;
558 std::pair<SmallVector<Attribute, 8>, SmallVector<Attribute, 8>> portIONames;
559 std::pair<SmallVector<Attribute, 8>, SmallVector<Attribute, 8>>
561 SmallVector<Direction, 8> portDirections;
564 for (
auto &&port : ports) {
565 bool isInput = port.direction == Direction::Input;
566 (isInput ? portIOTypes.first : portIOTypes.second).push_back(port.type);
567 (isInput ? portIONames.first : portIONames.second).push_back(port.name);
568 (isInput ? portIOAttributes.first : portIOAttributes.second)
569 .push_back(port.attributes);
571 auto portTypes =
concat(portIOTypes.first, portIOTypes.second);
572 auto portNames =
concat(portIONames.first, portIONames.second);
573 auto portAttributes =
concat(portIOAttributes.first, portIOAttributes.second);
576 auto functionType = builder.getFunctionType(portTypes, {});
578 result.addAttribute(CombComponentOp::getFunctionTypeAttrName(result.name),
579 TypeAttr::get(functionType));
581 result.addAttribute(ComponentOp::getFunctionTypeAttrName(result.name),
582 TypeAttr::get(functionType));
586 result.addAttribute(
"portNames", builder.getArrayAttr(portNames));
587 result.addAttribute(
"portDirections",
589 portIOTypes.first.size(),
590 portIOTypes.second.size()));
592 result.addAttribute(
"portAttributes", builder.getArrayAttr(portAttributes));
595 Region *region = result.addRegion();
596 Block *body =
new Block();
597 region->push_back(body);
600 body->addArguments(portTypes, SmallVector<Location, 4>(
601 portTypes.size(), builder.getUnknownLoc()));
604 IRRewriter::InsertionGuard guard(builder);
605 builder.setInsertionPointToStart(body);
606 WiresOp::create(builder, result.location);
608 ControlOp::create(builder, result.location);
619template <
typename Op>
621 auto *body = op.getBodyBlock();
624 auto opIt = body->getOps<Op>().begin();
631 ArrayAttr portNames = op.getPortNames();
633 for (
size_t i = 0, e = portNames.size(); i != e; ++i) {
634 auto portName = cast<StringAttr>(portNames[i]);
635 if (portName.getValue() == name)
636 return op.getBodyBlock()->getArgument(i);
641WiresOp calyx::ComponentOp::getWiresOp() {
642 return getControlOrWiresFrom<WiresOp>(*
this);
645ControlOp calyx::ComponentOp::getControlOp() {
646 return getControlOrWiresFrom<ControlOp>(*
this);
649Value calyx::ComponentOp::getGoPort() {
653Value calyx::ComponentOp::getDonePort() {
657Value calyx::ComponentOp::getClkPort() {
661Value calyx::ComponentOp::getResetPort() {
665SmallVector<PortInfo> ComponentOp::getPortInfo() {
666 auto portTypes = getArgumentTypes();
667 ArrayAttr portNamesAttr = getPortNames(), portAttrs = getPortAttributes();
668 APInt portDirectionsAttr = getPortDirections();
670 SmallVector<PortInfo> results;
671 for (
size_t i = 0, e = portNamesAttr.size(); i != e; ++i) {
672 results.push_back(
PortInfo{cast<StringAttr>(portNamesAttr[i]), portTypes[i],
674 cast<DictionaryAttr>(portAttrs[i])});
680template <
typename Pred>
682 SmallVector<PortInfo> ports = op.getPortInfo();
683 llvm::erase_if(ports, p);
687SmallVector<PortInfo> ComponentOp::getInputPortInfo() {
692SmallVector<PortInfo> ComponentOp::getOutputPortInfo() {
697void ComponentOp::print(OpAsmPrinter &p) {
698 printComponentInterface<ComponentOp>(p, *
this);
701ParseResult ComponentOp::parse(OpAsmParser &parser, OperationState &result) {
702 return parseComponentInterface<ComponentOp>(parser, result);
708 llvm::SmallVector<StringRef, 4> identifiers;
709 for (
PortInfo &port : op.getPortInfo()) {
710 auto portIds = port.getAllIdentifiers();
711 identifiers.append(portIds.begin(), portIds.end());
714 std::sort(identifiers.begin(), identifiers.end());
716 llvm::SmallVector<StringRef, 4> intersection,
719 std::set_intersection(interfacePorts.begin(), interfacePorts.end(),
720 identifiers.begin(), identifiers.end(),
721 std::back_inserter(intersection));
723 if (intersection.size() == interfacePorts.size())
726 SmallVector<StringRef, 4> difference;
727 std::set_difference(interfacePorts.begin(), interfacePorts.end(),
728 intersection.begin(), intersection.end(),
729 std::back_inserter(difference));
730 return op->emitOpError()
731 <<
"is missing the following required port attribute identifiers: "
735LogicalResult ComponentOp::verify() {
739 if (std::distance(wIt.begin(), wIt.end()) +
740 std::distance(cIt.begin(), cIt.end()) !=
742 return emitOpError() <<
"requires exactly one of each: '"
743 << WiresOp::getOperationName() <<
"', '"
744 << ControlOp::getOperationName() <<
"'.";
751 bool hasNoControlConstructs =
true;
752 getControlOp().walk<WalkOrder::PreOrder>([&](Operation *op) {
753 if (isa<EnableOp, InvokeOp, fsm::MachineOp>(op)) {
754 hasNoControlConstructs =
false;
755 return WalkResult::interrupt();
757 return WalkResult::advance();
759 bool hasNoAssignments =
760 getWiresOp().getBodyBlock()->getOps<AssignOp>().
empty();
761 if (hasNoControlConstructs && hasNoAssignments)
763 "The component currently does nothing. It needs to either have "
764 "continuous assignments in the Wires region or control "
765 "constructs in the Control region. The Control region "
766 "should contain at least one of ")
767 <<
"'" << EnableOp::getOperationName() <<
"' , "
768 <<
"'" << InvokeOp::getOperationName() <<
"' or "
769 <<
"'" << fsm::MachineOp::getOperationName() <<
"'.";
773void ComponentOp::build(OpBuilder &builder, OperationState &result,
774 StringAttr name, ArrayRef<PortInfo> ports) {
778void ComponentOp::getAsmBlockArgumentNames(
782 auto ports = getPortNames();
783 auto *block = &getRegion()->front();
784 for (
size_t i = 0, e = block->getNumArguments(); i != e; ++i)
785 setNameFn(block->getArgument(i), cast<StringAttr>(ports[i]).getValue());
792SmallVector<PortInfo> CombComponentOp::getPortInfo() {
793 auto portTypes = getArgumentTypes();
794 ArrayAttr portNamesAttr = getPortNames(), portAttrs = getPortAttributes();
795 APInt portDirectionsAttr = getPortDirections();
797 SmallVector<PortInfo> results;
798 for (
size_t i = 0, e = portNamesAttr.size(); i != e; ++i) {
799 results.push_back(
PortInfo{cast<StringAttr>(portNamesAttr[i]), portTypes[i],
801 cast<DictionaryAttr>(portAttrs[i])});
806WiresOp calyx::CombComponentOp::getWiresOp() {
808 auto opIt = body->getOps<WiresOp>().begin();
813template <
typename Pred>
815 SmallVector<PortInfo> ports = op.getPortInfo();
816 llvm::erase_if(ports, p);
820SmallVector<PortInfo> CombComponentOp::getInputPortInfo() {
825SmallVector<PortInfo> CombComponentOp::getOutputPortInfo() {
830void CombComponentOp::print(OpAsmPrinter &p) {
831 printComponentInterface<CombComponentOp>(p, *
this);
834ParseResult CombComponentOp::parse(OpAsmParser &parser,
835 OperationState &result) {
836 return parseComponentInterface<CombComponentOp>(parser, result);
839LogicalResult CombComponentOp::verify() {
842 if (std::distance(wIt.begin(), wIt.end()) != 1)
843 return emitOpError() <<
"requires exactly one "
844 << WiresOp::getOperationName() <<
" op.";
848 if (std::distance(cIt.begin(), cIt.end()) != 0)
849 return emitOpError() <<
"must not have a `" << ControlOp::getOperationName()
853 bool hasNoAssignments =
854 getWiresOp().getBodyBlock()->getOps<AssignOp>().
empty();
855 if (hasNoAssignments)
857 "The component currently does nothing. It needs to either have "
858 "continuous assignments in the Wires region.");
861 auto cells = getOps<CellInterface>();
862 for (
auto cell : cells) {
863 if (!cell.isCombinational())
864 return emitOpError() <<
"contains non-combinational cell "
865 << cell.instanceName();
869 auto groups = getWiresOp().getOps<GroupOp>();
871 return emitOpError() <<
"contains group " << (*groups.begin()).getSymName();
876 auto combGroups = getWiresOp().getOps<CombGroupOp>();
877 if (!combGroups.empty())
878 return emitOpError() <<
"contains comb group "
879 << (*combGroups.begin()).getSymName();
884void CombComponentOp::build(OpBuilder &builder, OperationState &result,
885 StringAttr name, ArrayRef<PortInfo> ports) {
889void CombComponentOp::getAsmBlockArgumentNames(
893 auto ports = getPortNames();
894 auto *block = &getRegion()->front();
895 for (
size_t i = 0, e = block->getNumArguments(); i != e; ++i)
896 setNameFn(block->getArgument(i), cast<StringAttr>(ports[i]).getValue());
905SmallVector<InvokeOp, 4> ControlOp::getInvokeOps() {
906 SmallVector<InvokeOp, 4> ret;
907 this->walk([&](InvokeOp invokeOp) { ret.push_back(invokeOp); });
915void SeqOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
917 patterns.add(collapseControl<SeqOp>);
926LogicalResult StaticSeqOp::verify() {
928 auto &ops = (*this).getBodyBlock()->getOperations();
929 if (!llvm::all_of(ops, [&](Operation &op) {
return isStaticControl(&op); })) {
930 return emitOpError(
"StaticSeqOp has non static control within it");
936void StaticSeqOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
938 patterns.add(collapseControl<StaticSeqOp>);
939 patterns.add(emptyControl<StaticSeqOp>);
947LogicalResult ParOp::verify() {
952 for (EnableOp op :
getBodyBlock()->getOps<EnableOp>()) {
953 StringRef groupName = op.getGroupName();
954 if (groupNames.count(groupName))
955 return emitOpError() <<
"cannot enable the same group: \"" << groupName
956 <<
"\" more than once.";
957 groupNames.insert(groupName);
963void ParOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
965 patterns.add(collapseControl<ParOp>);
974LogicalResult StaticParOp::verify() {
979 for (EnableOp op :
getBodyBlock()->getOps<EnableOp>()) {
980 StringRef groupName = op.getGroupName();
981 if (groupNames.count(groupName))
982 return emitOpError() <<
"cannot enable the same group: \"" << groupName
983 <<
"\" more than once.";
984 groupNames.insert(groupName);
988 auto &ops = (*this).getBodyBlock()->getOperations();
989 for (Operation &op : ops) {
991 return op.emitOpError(
"StaticParOp has non static control within it");
998void StaticParOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
1000 patterns.add(collapseControl<StaticParOp>);
1001 patterns.add(emptyControl<StaticParOp>);
1008LogicalResult WiresOp::verify() {
1009 auto componentInterface = (*this)->getParentOfType<ComponentInterface>();
1010 if (llvm::isa<ComponentOp>(componentInterface)) {
1011 auto component = llvm::cast<ComponentOp>(componentInterface);
1012 auto control = component.getControlOp();
1016 if (!isa<GroupInterface>(op))
1018 auto group = cast<GroupInterface>(op);
1019 auto groupName = group.symName();
1020 if (mlir::SymbolTable::symbolKnownUseEmpty(groupName, control))
1021 return op.emitOpError()
1022 <<
"with name: " << groupName
1023 <<
" is unused in the control execution schedule";
1030 for (
auto thisAssignment :
getBodyBlock()->getOps<AssignOp>()) {
1034 if (thisAssignment.getGuard())
1037 Value dest = thisAssignment.getDest();
1038 for (Operation *user : dest.getUsers()) {
1039 auto assignUser = dyn_cast<AssignOp>(user);
1040 if (!assignUser || assignUser.getDest() != dest ||
1041 assignUser == thisAssignment)
1044 return user->emitOpError() <<
"destination is already continuously "
1045 "driven. Other assignment is "
1059 Operation *definingOp = value.getDefiningOp();
1060 if (definingOp ==
nullptr || definingOp->hasTrait<
Combinational>())
1066 if (isa<InstanceOp>(definingOp))
1070 if (isa_and_nonnull<comb::CombDialect, hw::HWDialect>(
1071 definingOp->getDialect()))
1075 if (
auto r = dyn_cast<RegisterOp>(definingOp)) {
1076 return value == r.getOut()
1078 : group->emitOpError()
1079 <<
"with register: \"" << r.instanceName()
1080 <<
"\" is conducting a memory store. This is not "
1082 }
else if (
auto m = dyn_cast<MemoryOp>(definingOp)) {
1083 auto writePorts = {m.writeData(), m.writeEn()};
1084 return (llvm::none_of(
writePorts, [&](Value p) {
return p == value; }))
1086 : group->emitOpError()
1087 <<
"with memory: \"" << m.instanceName()
1088 <<
"\" is conducting a memory store. This "
1089 "is not combinational.";
1092 std::string portName =
1093 valueName(group->getParentOfType<ComponentOp>(), value);
1094 return group->emitOpError() <<
"with port: " << portName
1095 <<
". This operation is not combinational.";
1100LogicalResult CombGroupOp::verify() {
1102 auto assign = dyn_cast<AssignOp>(op);
1103 if (assign ==
nullptr)
1105 Value dst = assign.getDest(), src = assign.getSrc();
1116GroupGoOp GroupOp::getGoOp() {
1118 size_t nOps = std::distance(goOps.begin(), goOps.end());
1119 return nOps ? *goOps.begin() : GroupGoOp();
1122GroupDoneOp GroupOp::getDoneOp() {
1124 return cast<GroupDoneOp>(body->getTerminator());
1130void CycleOp::print(OpAsmPrinter &p) {
1133 auto start = this->getStart();
1134 auto end = this->getEnd();
1135 if (
end.has_value()) {
1136 p <<
"[" << start <<
":" <<
end.value() <<
"]";
1142ParseResult CycleOp::parse(OpAsmParser &parser, OperationState &result) {
1143 SmallVector<OpAsmParser::UnresolvedOperand, 2> operandInfos;
1145 uint32_t startLiteral;
1146 uint32_t endLiteral;
1148 auto hasEnd = succeeded(parser.parseOptionalLSquare());
1150 if (parser.parseInteger(startLiteral)) {
1151 parser.emitError(parser.getNameLoc(),
"Could not parse start cycle");
1155 auto start = parser.getBuilder().getI32IntegerAttr(startLiteral);
1156 result.addAttribute(getStartAttrName(result.name), start);
1159 if (parser.parseColon())
1162 if (
auto res = parser.parseOptionalInteger(endLiteral); res.has_value()) {
1163 auto end = parser.getBuilder().getI32IntegerAttr(endLiteral);
1164 result.addAttribute(getEndAttrName(result.name),
end);
1167 if (parser.parseRSquare())
1171 result.addTypes(parser.getBuilder().getI1Type());
1176LogicalResult CycleOp::verify() {
1177 uint32_t latency = this->getGroupLatency();
1179 if (this->getStart() >= latency) {
1180 emitOpError(
"start cycle must be less than the group latency");
1184 if (this->getEnd().has_value()) {
1185 if (this->getStart() >= this->getEnd().value()) {
1186 emitOpError(
"start cycle must be less than end cycle");
1190 if (this->getEnd() >= latency) {
1191 emitOpError(
"end cycle must be less than the group latency");
1199uint32_t CycleOp::getGroupLatency() {
1200 auto group = (*this)->getParentOfType<StaticGroupOp>();
1201 return group.getLatency();
1208 return FloatingPointStandard::IEEE754;
1212 return FloatingPointStandard::IEEE754;
1216 return FloatingPointStandard::IEEE754;
1220 return FloatingPointStandard::IEEE754;
1224 return FloatingPointStandard::IEEE754;
1228 return FloatingPointStandard::IEEE754;
1231std::string AddFOpIEEE754::getCalyxLibraryName() {
return "std_addFN"; }
1233std::string MulFOpIEEE754::getCalyxLibraryName() {
return "std_mulFN"; }
1235std::string CompareFOpIEEE754::getCalyxLibraryName() {
return "std_compareFN"; }
1237std::string FpToIntOpIEEE754::getCalyxLibraryName() {
return "std_fpToInt"; }
1239std::string IntToFpOpIEEE754::getCalyxLibraryName() {
return "std_intToFp"; }
1241std::string DivSqrtOpIEEE754::getCalyxLibraryName() {
return "std_divSqrtFN"; }
1250 return llvm::any_of(port.getUses(), [&](
auto &&use) {
1251 auto assignOp = dyn_cast<AssignOp>(use.getOwner());
1252 if (assignOp == nullptr)
1255 Operation *parent = assignOp->getParentOp();
1256 if (isa<WiresOp>(parent))
1265 Value expected = isDriven ? assignOp.getDest() : assignOp.getSrc();
1266 return expected == port && group == parent;
1278 if (
auto cell = dyn_cast<CellInterface>(port.getDefiningOp());
1279 cell && cell.direction(port) == calyx::Direction::Output)
1280 return groupOp.drivesAnyPort(cell.getInputPorts());
1285LogicalResult GroupOp::drivesPort(Value port) {
1289LogicalResult CombGroupOp::drivesPort(Value port) {
1293LogicalResult StaticGroupOp::drivesPort(Value port) {
1300 return success(llvm::all_of(ports, [&](Value port) {
1305LogicalResult GroupOp::drivesAllPorts(ValueRange ports) {
1309LogicalResult CombGroupOp::drivesAllPorts(ValueRange ports) {
1313LogicalResult StaticGroupOp::drivesAllPorts(ValueRange ports) {
1320 return success(llvm::any_of(ports, [&](Value port) {
1325LogicalResult GroupOp::drivesAnyPort(ValueRange ports) {
1329LogicalResult CombGroupOp::drivesAnyPort(ValueRange ports) {
1333LogicalResult StaticGroupOp::drivesAnyPort(ValueRange ports) {
1340 return success(llvm::any_of(ports, [&](Value port) {
1345LogicalResult GroupOp::readsAnyPort(ValueRange ports) {
1349LogicalResult CombGroupOp::readsAnyPort(ValueRange ports) {
1353LogicalResult StaticGroupOp::readsAnyPort(ValueRange ports) {
1360 GroupInterface group) {
1361 Operation *destDefiningOp = assign.getDest().getDefiningOp();
1362 if (destDefiningOp ==
nullptr)
1364 auto destCell = dyn_cast<CellInterface>(destDefiningOp);
1365 if (destCell ==
nullptr)
1368 LogicalResult verifyWrites =
1369 TypeSwitch<Operation *, LogicalResult>(destCell)
1370 .Case<RegisterOp>([&](
auto op) {
1373 return succeeded(group.drivesAnyPort({op.getWriteEn(), op.getIn()}))
1374 ? group.drivesAllPorts({op.getWriteEn(), op.getIn()})
1377 .Case<MemoryOp>([&](
auto op) {
1378 SmallVector<Value> requiredWritePorts;
1381 requiredWritePorts.push_back(op.writeEn());
1382 requiredWritePorts.push_back(op.writeData());
1383 for (Value address : op.addrPorts())
1384 requiredWritePorts.push_back(address);
1389 group.drivesAnyPort({op.writeData(), op.writeEn()}))
1390 ? group.drivesAllPorts(requiredWritePorts)
1393 .Case<AndLibOp, OrLibOp, XorLibOp, AddLibOp, SubLibOp, GtLibOp,
1394 LtLibOp, EqLibOp, NeqLibOp, GeLibOp, LeLibOp, LshLibOp,
1395 RshLibOp, SgtLibOp, SltLibOp, SeqLibOp, SneqLibOp, SgeLibOp,
1396 SleLibOp, SrshLibOp>([&](
auto op) {
1397 Value lhs = op.getLeft(), rhs = op.getRight();
1398 return succeeded(group.drivesAnyPort({lhs, rhs}))
1399 ? group.drivesAllPorts({lhs, rhs})
1402 .Default([&](
auto op) {
return success(); });
1404 if (failed(verifyWrites))
1405 return group->emitOpError()
1406 <<
"with cell: " << destCell->getName() <<
" \""
1407 << destCell.instanceName()
1408 <<
"\" is performing a write and failed to drive all necessary "
1411 Operation *srcDefiningOp = assign.getSrc().getDefiningOp();
1412 if (srcDefiningOp ==
nullptr)
1414 auto srcCell = dyn_cast<CellInterface>(srcDefiningOp);
1415 if (srcCell ==
nullptr)
1418 LogicalResult verifyReads =
1419 TypeSwitch<Operation *, LogicalResult>(srcCell)
1420 .Case<MemoryOp>([&](
auto op) {
1424 return succeeded(group.readsAnyPort({op.readData()}))
1425 ? group.drivesAllPorts(op.addrPorts())
1428 .Default([&](
auto op) {
return success(); });
1430 if (failed(verifyReads))
1431 return group->emitOpError() <<
"with cell: " << srcCell->getName() <<
" \""
1432 << srcCell.instanceName()
1433 <<
"\" is having a read performed upon it, and "
1434 "failed to drive all necessary ports.";
1440 auto group = dyn_cast<GroupInterface>(op);
1441 if (group ==
nullptr)
1444 for (
auto &&groupOp : *group.getBody()) {
1445 auto assign = dyn_cast<AssignOp>(groupOp);
1446 if (assign ==
nullptr)
1462 ArrayRef<StringRef> portNames) {
1463 auto cellInterface = dyn_cast<CellInterface>(op);
1464 assert(cellInterface &&
"must implement the Cell interface");
1466 std::string prefix = cellInterface.instanceName().str() +
".";
1467 for (
size_t i = 0, e = portNames.size(); i != e; ++i)
1468 setNameFn(op->getResult(i), prefix + portNames[i].str());
1479 bool isDestination) {
1480 Operation *definingOp = value.getDefiningOp();
1481 bool isComponentPort = isa<BlockArgument>(value),
1482 isCellInterfacePort = isa_and_nonnull<CellInterface>(definingOp);
1483 assert((isComponentPort || isCellInterfacePort) &&
"Not a port.");
1487 : cast<CellInterface>(definingOp).portInfo(value);
1489 bool isSource = !isDestination;
1492 (isDestination && isComponentPort) || (isSource && isCellInterfacePort)
1499 <<
"has a " << (isComponentPort ?
"component" :
"cell")
1501 << (isDestination ?
"destination" :
"source")
1502 <<
" with the incorrect direction.";
1509 bool isSource = !isDestination;
1510 Value value = isDestination ? op.getDest() : op.getSrc();
1515 if (isDestination && !isa<GroupGoOp, GroupDoneOp>(value.getDefiningOp()))
1516 return op->emitOpError(
1517 "has an invalid destination port. It must be drive-able.");
1524LogicalResult AssignOp::verify() {
1525 bool isDestination =
true, isSource =
false;
1534ParseResult AssignOp::parse(OpAsmParser &parser, OperationState &result) {
1535 OpAsmParser::UnresolvedOperand destination;
1536 if (parser.parseOperand(destination) || parser.parseEqual())
1542 OpAsmParser::UnresolvedOperand guardOrSource;
1543 if (parser.parseOperand(guardOrSource))
1548 OpAsmParser::UnresolvedOperand source;
1549 bool hasGuard = succeeded(parser.parseOptionalQuestion());
1552 if (parser.parseOperand(source))
1557 if (parser.parseColonType(type) ||
1558 parser.resolveOperand(destination, type, result.operands))
1562 Type i1Type = parser.getBuilder().getI1Type();
1565 if (parser.resolveOperand(source, type, result.operands) ||
1566 parser.resolveOperand(guardOrSource, i1Type, result.operands))
1570 if (parser.resolveOperand(guardOrSource, type, result.operands))
1577void AssignOp::print(OpAsmPrinter &p) {
1578 p <<
" " << getDest() <<
" = ";
1580 Value bguard = getGuard(), source = getSrc();
1583 p << bguard <<
" ? ";
1587 p << source <<
" : " << source.getType();
1596ComponentInterface InstanceOp::getReferencedComponent() {
1597 auto module = (*this)->getParentOfType<ModuleOp>();
1601 return module.lookupSymbol<ComponentInterface>(getComponentName());
1609 ComponentInterface referencedComponent) {
1610 auto module = instance->getParentOfType<ModuleOp>();
1611 StringRef entryPointName =
1612 module->getAttrOfType<StringAttr>("calyx.entrypoint");
1613 if (instance.getComponentName() == entryPointName)
1614 return instance.emitOpError()
1615 <<
"cannot reference the entry-point component: '" << entryPointName
1619 SmallVector<PortInfo> componentPorts = referencedComponent.getPortInfo();
1620 size_t numPorts = componentPorts.size();
1622 size_t numResults = instance.getNumResults();
1623 if (numResults != numPorts)
1624 return instance.emitOpError()
1625 <<
"has a wrong number of results; expected: " << numPorts
1626 <<
" but got " << numResults;
1628 for (
size_t i = 0; i != numResults; ++i) {
1629 auto resultType = instance.getResult(i).getType();
1630 auto expectedType = componentPorts[i].type;
1631 if (resultType == expectedType)
1633 return instance.emitOpError()
1634 <<
"result type for " << componentPorts[i].name <<
" must be "
1635 << expectedType <<
", but got " << resultType;
1640LogicalResult InstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1641 Operation *op = *
this;
1642 auto module = op->getParentOfType<ModuleOp>();
1643 Operation *referencedComponent =
1644 symbolTable.lookupNearestSymbolFrom(module, getComponentNameAttr());
1645 if (referencedComponent ==
nullptr)
1646 return emitError() <<
"referencing component: '" << getComponentName()
1647 <<
"', which does not exist.";
1649 Operation *shadowedComponentName =
1650 symbolTable.lookupNearestSymbolFrom(module, getSymNameAttr());
1651 if (shadowedComponentName !=
nullptr)
1652 return emitError() <<
"instance symbol: '" << instanceName()
1653 <<
"' is already a symbol for another component.";
1656 auto parentComponent = op->getParentOfType<ComponentOp>();
1657 if (parentComponent == referencedComponent)
1658 return emitError() <<
"recursive instantiation of its parent component: '"
1659 << getComponentName() <<
"'";
1661 assert(isa<ComponentInterface>(referencedComponent) &&
1662 "Should be a ComponentInterface.");
1664 cast<ComponentInterface>(referencedComponent));
1672SmallVector<StringRef> InstanceOp::portNames() {
1673 SmallVector<StringRef> portNames;
1674 for (Attribute name : getReferencedComponent().getPortNames())
1675 portNames.push_back(cast<StringAttr>(name).getValue());
1679SmallVector<Direction> InstanceOp::portDirections() {
1680 SmallVector<Direction> portDirections;
1682 portDirections.push_back(port.direction);
1683 return portDirections;
1686SmallVector<DictionaryAttr> InstanceOp::portAttributes() {
1687 SmallVector<DictionaryAttr> portAttributes;
1689 portAttributes.push_back(port.attributes);
1690 return portAttributes;
1693bool InstanceOp::isCombinational() {
1694 return isa<CombComponentOp>(getReferencedComponent());
1704 auto module = (*this)->getParentOfType<ModuleOp>();
1708 return module.lookupSymbol<hw::HWModuleExternOp>(getPrimitiveName());
1717 auto module = instance->getParentOfType<ModuleOp>();
1718 StringRef entryPointName =
1719 module->getAttrOfType<StringAttr>("calyx.entrypoint");
1720 if (instance.getPrimitiveName() == entryPointName)
1721 return instance.emitOpError()
1722 <<
"cannot reference the entry-point component: '" << entryPointName
1726 auto primitivePorts = referencedPrimitive.getPortList();
1727 size_t numPorts = primitivePorts.size();
1729 size_t numResults = instance.getNumResults();
1730 if (numResults != numPorts)
1731 return instance.emitOpError()
1732 <<
"has a wrong number of results; expected: " << numPorts
1733 <<
" but got " << numResults;
1736 ArrayAttr modParameters = referencedPrimitive.getParameters();
1737 ArrayAttr parameters = instance.getParameters().value_or(ArrayAttr());
1738 size_t numExpected = modParameters.size();
1739 size_t numParams = parameters.size();
1740 if (numParams != numExpected)
1741 return instance.emitOpError()
1742 <<
"has the wrong number of parameters; expected: " << numExpected
1743 <<
" but got " << numParams;
1745 for (
size_t i = 0; i != numExpected; ++i) {
1746 auto param = cast<circt::hw::ParamDeclAttr>(parameters[i]);
1747 auto modParam = cast<circt::hw::ParamDeclAttr>(modParameters[i]);
1749 auto paramName = param.getName();
1750 if (paramName != modParam.getName())
1751 return instance.emitOpError()
1752 <<
"parameter #" << i <<
" should have name " << modParam.getName()
1753 <<
" but has name " << paramName;
1755 if (param.getType() != modParam.getType())
1756 return instance.emitOpError()
1757 <<
"parameter " << paramName <<
" should have type "
1758 << modParam.getType() <<
" but has type " << param.getType();
1762 if (!param.getValue())
1763 return instance.emitOpError(
"parameter ")
1764 << paramName <<
" must have a value";
1767 for (
size_t i = 0; i != numResults; ++i) {
1768 auto resultType = instance.getResult(i).getType();
1769 auto expectedType = primitivePorts[i].type;
1770 auto replacedType = hw::evaluateParametricType(
1771 instance.getLoc(), instance.getParametersAttr(), expectedType);
1772 if (failed(replacedType))
1774 if (resultType == replacedType)
1776 return instance.emitOpError()
1777 <<
"result type for " << primitivePorts[i].name <<
" must be "
1778 << expectedType <<
", but got " << resultType;
1784PrimitiveOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1785 Operation *op = *
this;
1786 auto module = op->getParentOfType<ModuleOp>();
1787 Operation *referencedPrimitive =
1788 symbolTable.lookupNearestSymbolFrom(module, getPrimitiveNameAttr());
1789 if (referencedPrimitive ==
nullptr)
1790 return emitError() <<
"referencing primitive: '" << getPrimitiveName()
1791 <<
"', which does not exist.";
1793 Operation *shadowedPrimitiveName =
1794 symbolTable.lookupNearestSymbolFrom(module, getSymNameAttr());
1795 if (shadowedPrimitiveName !=
nullptr)
1796 return emitError() <<
"instance symbol: '" << instanceName()
1797 <<
"' is already a symbol for another primitive.";
1801 if (parentPrimitive == referencedPrimitive)
1802 return emitError() <<
"recursive instantiation of its parent primitive: '"
1803 << getPrimitiveName() <<
"'";
1805 assert(isa<hw::HWModuleExternOp>(referencedPrimitive) &&
1806 "Should be a HardwareModuleExternOp.");
1809 cast<hw::HWModuleExternOp>(referencedPrimitive));
1817SmallVector<StringRef> PrimitiveOp::portNames() {
1818 SmallVector<StringRef> portNames;
1819 auto ports = getReferencedPrimitive().getPortList();
1820 for (
auto port : ports)
1821 portNames.push_back(port.name.getValue());
1827 switch (direction) {
1828 case hw::ModulePort::Direction::Input:
1829 return Direction::Input;
1830 case hw::ModulePort::Direction::Output:
1831 return Direction::Output;
1832 case hw::ModulePort::Direction::InOut:
1833 llvm_unreachable(
"InOut ports not supported by Calyx");
1835 llvm_unreachable(
"Impossible port type");
1838SmallVector<Direction> PrimitiveOp::portDirections() {
1839 SmallVector<Direction> portDirections;
1840 auto ports = getReferencedPrimitive().getPortList();
1843 return portDirections;
1846bool PrimitiveOp::isCombinational() {
return false; }
1852 DictionaryAttr dict) {
1856 llvm::SmallVector<NamedAttribute> attrs;
1857 for (NamedAttribute attr : dict) {
1858 Dialect *dialect = attr.getNameDialect();
1859 if (dialect ==
nullptr || !isa<CalyxDialect>(*dialect))
1861 StringRef name = attr.getName().strref();
1862 StringAttr newName = builder.getStringAttr(std::get<1>(name.split(
".")));
1863 attr.setName(newName);
1864 attrs.push_back(attr);
1866 return builder.getDictionaryAttr(attrs);
1870SmallVector<DictionaryAttr> PrimitiveOp::portAttributes() {
1871 SmallVector<DictionaryAttr> portAttributes;
1872 OpBuilder builder(getContext());
1874 auto argAttrs = prim.getAllInputAttrs();
1875 auto resAttrs = prim.getAllOutputAttrs();
1876 for (
auto a : argAttrs)
1877 portAttributes.push_back(
1879 for (
auto a : resAttrs)
1880 portAttributes.push_back(
1882 return portAttributes;
1891 SmallVector<Attribute> ¶meters) {
1893 return parser.parseCommaSeparatedList(
1894 OpAsmParser::Delimiter::OptionalLessGreater, [&]() {
1899 if (parser.parseKeywordOrString(&name) || parser.parseColonType(type))
1903 if (succeeded(parser.parseOptionalEqual())) {
1904 if (parser.parseAttribute(value, type))
1908 auto &builder = parser.getBuilder();
1909 parameters.push_back(hw::ParamDeclAttr::get(
1910 builder.getContext(), builder.getStringAttr(name), type, value));
1917 ArrayAttr ¶meters) {
1918 SmallVector<Attribute> parseParameters;
1922 parameters = ArrayAttr::get(parser.getContext(), parseParameters);
1929 ArrayAttr parameters) {
1930 if (parameters.empty())
1934 llvm::interleaveComma(parameters, p, [&](Attribute param) {
1935 auto paramAttr = cast<hw::ParamDeclAttr>(param);
1936 p << paramAttr.getName().getValue() <<
": " << paramAttr.getType();
1937 if (
auto value = paramAttr.getValue()) {
1939 p.printAttributeWithoutType(value);
1953 auto parent = (*this)->getParentOfType<GroupOp>();
1954 StringRef name = parent.getSymName();
1955 std::string resultName = name.str() +
".go";
1956 setNameFn(getResult(), resultName);
1959void GroupGoOp::print(OpAsmPrinter &p) {
printGroupPort(p, *
this); }
1961ParseResult GroupGoOp::parse(OpAsmParser &parser, OperationState &result) {
1965 result.addTypes(parser.getBuilder().getI1Type());
1973LogicalResult GroupDoneOp::verify() {
1974 Operation *srcOp = getSrc().getDefiningOp();
1975 Value optionalGuard = getGuard();
1976 Operation *guardOp = optionalGuard ? optionalGuard.getDefiningOp() :
nullptr;
1977 bool noGuard = (guardOp ==
nullptr);
1979 if (srcOp ==
nullptr)
1983 if (isa<hw::ConstantOp>(srcOp) && (noGuard || isa<hw::ConstantOp>(guardOp)))
1984 return emitOpError() <<
"with constant source"
1985 << (noGuard ?
"" :
" and constant guard")
1986 <<
". This should be a combinational group.";
1991void GroupDoneOp::print(OpAsmPrinter &p) {
printGroupPort(p, *
this); }
1993ParseResult GroupDoneOp::parse(OpAsmParser &parser, OperationState &result) {
2000void ConstantOp::getAsmResultNames(
2001 function_ref<
void(Value, StringRef)> setNameFn) {
2002 if (isa<FloatAttr>(getValue())) {
2003 setNameFn(getResult(),
"cst");
2006 auto intCst = llvm::dyn_cast<IntegerAttr>(getValue());
2007 auto intType = llvm::dyn_cast<IntegerType>(getType());
2010 if (intType && intType.getWidth() == 1)
2011 return setNameFn(getResult(), intCst.getInt() > 0 ?
"true" :
"false");
2014 SmallString<32> specialNameBuffer;
2015 llvm::raw_svector_ostream specialName(specialNameBuffer);
2016 specialName <<
'c' << intCst.getValue();
2018 specialName <<
'_' << getType();
2019 setNameFn(getResult(), specialName.str());
2022LogicalResult ConstantOp::verify() {
2023 auto type = getType();
2024 assert(isa<IntegerType>(type) &&
"must be an IntegerType");
2026 if (
auto valTyBitWidth = getValue().getType().getIntOrFloatBitWidth();
2027 valTyBitWidth != type.getIntOrFloatBitWidth()) {
2028 return emitOpError() <<
"value type bit width" << valTyBitWidth
2029 <<
" must match return type: "
2030 << type.getIntOrFloatBitWidth();
2033 if (llvm::isa<IntegerType>(type) &&
2034 !llvm::cast<IntegerType>(type).isSignless())
2035 return emitOpError(
"integer return type must be signless");
2037 if (!llvm::isa<IntegerAttr, FloatAttr>(getValue())) {
2038 return emitOpError(
"value must be an integer or float attribute");
2044OpFoldResult calyx::ConstantOp::fold(FoldAdaptor adaptor) {
2045 return getValueAttr();
2048void calyx::ConstantOp::build(OpBuilder &builder, OperationState &state,
2049 StringRef symName, Attribute attr, Type type) {
2050 state.addAttribute(SymbolTable::getSymbolAttrName(),
2051 builder.getStringAttr(symName));
2052 state.addAttribute(
"value", attr);
2053 SmallVector<Type> types;
2054 types.push_back(type);
2055 state.addTypes(types);
2058SmallVector<StringRef> ConstantOp::portNames() {
return {
"out"}; }
2060SmallVector<Direction> ConstantOp::portDirections() {
return {
Output}; }
2062SmallVector<DictionaryAttr> ConstantOp::portAttributes() {
2063 return {DictionaryAttr::get(getContext())};
2066bool ConstantOp::isCombinational() {
return true; }
2077SmallVector<StringRef> RegisterOp::portNames() {
2081SmallVector<Direction> RegisterOp::portDirections() {
2085SmallVector<DictionaryAttr> RegisterOp::portAttributes() {
2086 MLIRContext *
context = getContext();
2087 IntegerAttr isSet = IntegerAttr::get(IntegerType::get(
context, 1), 1);
2088 NamedAttrList writeEn,
clk, reset, done;
2089 writeEn.append(
goPort, isSet);
2095 writeEn.getDictionary(
context),
2103bool RegisterOp::isCombinational() {
return false; }
2114SmallVector<StringRef> MemoryOp::portNames() {
2115 SmallVector<StringRef> portNames;
2116 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i) {
2118 StringAttr::get(this->getContext(),
"addr" + std::to_string(i));
2119 portNames.push_back(nameAttr.getValue());
2121 portNames.append({
"write_data",
"write_en",
clkPort,
"read_data",
donePort});
2125SmallVector<Direction> MemoryOp::portDirections() {
2126 SmallVector<Direction> portDirections;
2127 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2128 portDirections.push_back(
Input);
2130 return portDirections;
2133SmallVector<DictionaryAttr> MemoryOp::portAttributes() {
2134 SmallVector<DictionaryAttr> portAttributes;
2135 MLIRContext *
context = getContext();
2136 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2137 portAttributes.push_back(DictionaryAttr::get(
context));
2140 IntegerAttr isSet = IntegerAttr::get(IntegerType::get(
context, 1), 1);
2141 NamedAttrList writeEn,
clk, reset, done;
2142 writeEn.append(
goPort, isSet);
2145 portAttributes.append({DictionaryAttr::get(
context),
2146 writeEn.getDictionary(
context),
2151 return portAttributes;
2154void MemoryOp::build(OpBuilder &builder, OperationState &state,
2155 StringRef instanceName, int64_t width,
2156 ArrayRef<int64_t> sizes, ArrayRef<int64_t> addrSizes) {
2157 state.addAttribute(SymbolTable::getSymbolAttrName(),
2158 builder.getStringAttr(instanceName));
2159 state.addAttribute(
"width", builder.getI64IntegerAttr(width));
2160 state.addAttribute(
"sizes", builder.getI64ArrayAttr(sizes));
2161 state.addAttribute(
"addrSizes", builder.getI64ArrayAttr(addrSizes));
2162 SmallVector<Type> types;
2163 for (int64_t size : addrSizes)
2164 types.push_back(builder.getIntegerType(size));
2165 types.push_back(builder.getIntegerType(width));
2166 types.push_back(builder.getI1Type());
2167 types.push_back(builder.getI1Type());
2168 types.push_back(builder.getIntegerType(width));
2169 types.push_back(builder.getI1Type());
2170 state.addTypes(types);
2173LogicalResult MemoryOp::verify() {
2174 ArrayRef<Attribute> opSizes = getSizes().getValue();
2175 ArrayRef<Attribute> opAddrSizes = getAddrSizes().getValue();
2176 size_t numDims = getSizes().size();
2177 size_t numAddrs = getAddrSizes().size();
2178 if (numDims != numAddrs)
2179 return emitOpError(
"mismatched number of dimensions (")
2180 << numDims <<
") and address sizes (" << numAddrs <<
")";
2182 size_t numExtraPorts = 5;
2183 if (getNumResults() != numAddrs + numExtraPorts)
2184 return emitOpError(
"incorrect number of address ports, expected ")
2187 for (
size_t i = 0; i < numDims; ++i) {
2188 int64_t size = cast<IntegerAttr>(opSizes[i]).getInt();
2189 int64_t addrSize = cast<IntegerAttr>(opAddrSizes[i]).getInt();
2190 if (llvm::Log2_64_Ceil(size) > addrSize)
2191 return emitOpError(
"address size (")
2192 << addrSize <<
") for dimension " << i
2193 <<
" can't address the entire range (" << size <<
")";
2208SmallVector<StringRef> SeqMemoryOp::portNames() {
2209 SmallVector<StringRef> portNames;
2210 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i) {
2212 StringAttr::get(this->getContext(),
"addr" + std::to_string(i));
2213 portNames.push_back(nameAttr.getValue());
2215 portNames.append({
clkPort,
"reset",
"content_en",
"write_en",
"write_data",
2216 "read_data",
"done"});
2220SmallVector<Direction> SeqMemoryOp::portDirections() {
2221 SmallVector<Direction> portDirections;
2222 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2223 portDirections.push_back(
Input);
2225 return portDirections;
2228SmallVector<DictionaryAttr> SeqMemoryOp::portAttributes() {
2229 SmallVector<DictionaryAttr> portAttributes;
2230 MLIRContext *
context = getContext();
2231 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2232 portAttributes.push_back(DictionaryAttr::get(
context));
2236 IntegerAttr isSet = IntegerAttr::get(builder.getIndexType(), 1);
2237 IntegerAttr isTwo = IntegerAttr::get(builder.getIndexType(), 2);
2238 NamedAttrList done,
clk, reset, contentEn;
2242 contentEn.append(
goPort, isTwo);
2243 portAttributes.append({
clk.getDictionary(
context),
2245 contentEn.getDictionary(
context),
2251 return portAttributes;
2254void SeqMemoryOp::build(OpBuilder &builder, OperationState &state,
2255 StringRef instanceName, int64_t width,
2256 ArrayRef<int64_t> sizes, ArrayRef<int64_t> addrSizes) {
2257 state.addAttribute(SymbolTable::getSymbolAttrName(),
2258 builder.getStringAttr(instanceName));
2259 state.addAttribute(
"width", builder.getI64IntegerAttr(width));
2260 state.addAttribute(
"sizes", builder.getI64ArrayAttr(sizes));
2261 state.addAttribute(
"addrSizes", builder.getI64ArrayAttr(addrSizes));
2262 SmallVector<Type> types;
2263 for (int64_t size : addrSizes)
2264 types.push_back(builder.getIntegerType(size));
2265 types.push_back(builder.getI1Type());
2266 types.push_back(builder.getI1Type());
2267 types.push_back(builder.getI1Type());
2268 types.push_back(builder.getI1Type());
2269 types.push_back(builder.getIntegerType(width));
2270 types.push_back(builder.getIntegerType(width));
2271 types.push_back(builder.getI1Type());
2272 state.addTypes(types);
2275LogicalResult SeqMemoryOp::verify() {
2276 ArrayRef<Attribute> opSizes = getSizes().getValue();
2277 ArrayRef<Attribute> opAddrSizes = getAddrSizes().getValue();
2278 size_t numDims = getSizes().size();
2279 size_t numAddrs = getAddrSizes().size();
2280 if (numDims != numAddrs)
2281 return emitOpError(
"mismatched number of dimensions (")
2282 << numDims <<
") and address sizes (" << numAddrs <<
")";
2284 size_t numExtraPorts =
2286 if (getNumResults() != numAddrs + numExtraPorts)
2287 return emitOpError(
"incorrect number of address ports, expected ")
2290 for (
size_t i = 0; i < numDims; ++i) {
2291 int64_t size = cast<IntegerAttr>(opSizes[i]).getInt();
2292 int64_t addrSize = cast<IntegerAttr>(opAddrSizes[i]).getInt();
2293 if (llvm::Log2_64_Ceil(size) > addrSize)
2294 return emitOpError(
"address size (")
2295 << addrSize <<
") for dimension " << i
2296 <<
" can't address the entire range (" << size <<
")";
2305LogicalResult EnableOp::verify() {
2306 auto component = (*this)->getParentOfType<ComponentOp>();
2307 auto wiresOp = component.getWiresOp();
2308 StringRef name = getGroupName();
2310 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(name);
2312 return emitOpError() <<
"with group '" << name
2313 <<
"', which does not exist.";
2315 if (isa<CombGroupOp>(groupOp))
2316 return emitOpError() <<
"with group '" << name
2317 <<
"', which is a combinational group.";
2326LogicalResult IfOp::verify() {
2327 std::optional<StringRef> optGroupName = getGroupName();
2328 if (!optGroupName) {
2332 auto component = (*this)->getParentOfType<ComponentOp>();
2333 WiresOp wiresOp = component.getWiresOp();
2334 StringRef groupName = *optGroupName;
2335 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(groupName);
2337 return emitOpError() <<
"with group '" << groupName
2338 <<
"', which does not exist.";
2340 if (isa<GroupOp>(groupOp))
2341 return emitOpError() <<
"with group '" << groupName
2342 <<
"', which is not a combinational group.";
2344 if (failed(groupOp.drivesPort(getCond())))
2345 return emitError() <<
"with conditional op: '"
2347 <<
"' expected to be driven from group: '" << groupName
2348 <<
"' but no driver was found.";
2356template <
typename OpTy>
2358 static_assert(IsAny<OpTy, SeqOp, StaticSeqOp>(),
2359 "Should be a StaticSeqOp or SeqOp.");
2360 if (parent.getBodyBlock()->empty())
2361 return std::nullopt;
2362 auto &lastOp = parent.getBodyBlock()->back();
2363 if (
auto enableOp = dyn_cast<EnableOp>(lastOp))
2365 if (
auto seqOp = dyn_cast<SeqOp>(lastOp))
2367 if (
auto staticSeqOp = dyn_cast<StaticSeqOp>(lastOp))
2370 return std::nullopt;
2375template <
typename OpTy>
2378 static_assert(IsAny<OpTy, ParOp, StaticParOp>(),
2379 "Should be a StaticParOp or ParOp.");
2382 Block *body = parent.getBodyBlock();
2383 for (EnableOp op : body->getOps<EnableOp>())
2384 enables.insert(std::pair(op.getGroupNameAttr().getAttr(), op));
2396template <
typename IfOpTy,
typename TailOpTy>
2398 static_assert(IsAny<TailOpTy, SeqOp, ParOp, StaticSeqOp, StaticParOp>(),
2399 "Should be a SeqOp, ParOp, StaticSeqOp, or StaticParOp.");
2400 static_assert(IsAny<IfOpTy, IfOp, StaticIfOp>(),
2401 "Should be a IfOp or StaticIfOp.");
2403 if (!op.thenBodyExists() || !op.elseBodyExists())
2405 if (op.getThenBody()->empty() || op.getElseBody()->empty())
2408 Block *thenBody = op.getThenBody(), *elseBody = op.getElseBody();
2409 return isa<TailOpTy>(thenBody->front()) && isa<TailOpTy>(elseBody->front());
2420template <
typename IfOpTy,
typename SeqOpTy>
2422 PatternRewriter &rewriter) {
2423 static_assert(IsAny<IfOpTy, IfOp, StaticIfOp>(),
2424 "Should be an IfOp or StaticIfOp.");
2425 static_assert(IsAny<SeqOpTy, SeqOp, StaticSeqOp>(),
2426 "Branches should be checking for an SeqOp or StaticSeqOp");
2427 if (!hasCommonTailPatternPreConditions<IfOpTy, SeqOpTy>(ifOp))
2429 auto thenControl = cast<SeqOpTy>(ifOp.getThenBody()->front()),
2430 elseControl = cast<SeqOpTy>(ifOp.getElseBody()->front());
2432 std::optional<EnableOp> lastThenEnableOp =
getLastEnableOp(thenControl),
2435 if (!lastThenEnableOp || !lastElseEnableOp)
2437 if (lastThenEnableOp->getGroupName() != lastElseEnableOp->getGroupName())
2443 rewriter.setInsertionPointAfter(ifOp);
2444 SeqOpTy seqOp = SeqOpTy::create(rewriter, ifOp.getLoc());
2445 Block *body = seqOp.getBodyBlock();
2446 rewriter.moveOpBefore(ifOp, body, body->end());
2447 rewriter.setInsertionPointToEnd(body);
2448 EnableOp::create(rewriter, seqOp.getLoc(), lastThenEnableOp->getGroupName());
2451 rewriter.eraseOp(*lastThenEnableOp);
2452 rewriter.eraseOp(*lastElseEnableOp);
2469template <
typename OpTy,
typename ParOpTy>
2471 PatternRewriter &rewriter) {
2472 static_assert(IsAny<OpTy, IfOp, StaticIfOp>(),
2473 "Should be an IfOp or StaticIfOp.");
2474 static_assert(IsAny<ParOpTy, ParOp, StaticParOp>(),
2475 "Branches should be checking for an ParOp or StaticParOp");
2476 if (!hasCommonTailPatternPreConditions<OpTy, ParOpTy>(controlOp))
2478 auto thenControl = cast<ParOpTy>(controlOp.getThenBody()->front()),
2479 elseControl = cast<ParOpTy>(controlOp.getElseBody()->front());
2483 SmallVector<StringRef> groupNames;
2485 for (
auto [groupName, aEnable] : a) {
2486 auto bIndex = b.find(groupName);
2487 if (bIndex == b.end())
2490 groupNames.push_back(groupName.getValue());
2492 rewriter.eraseOp(aEnable);
2493 rewriter.eraseOp(bIndex->second);
2499 rewriter.setInsertionPointAfter(controlOp);
2501 ParOpTy parOp = ParOpTy::create(rewriter, controlOp.getLoc());
2502 Block *body = parOp.getBodyBlock();
2503 controlOp->remove();
2504 body->push_back(controlOp);
2507 rewriter.setInsertionPointToEnd(body);
2508 for (StringRef groupName : groupNames)
2509 EnableOp::create(rewriter, parOp.getLoc(), groupName);
2520 PatternRewriter &rewriter)
const override {
2521 if (!ifOp.getThenBody()->empty())
2523 if (ifOp.elseBodyExists() && !ifOp.getElseBody()->empty())
2532void IfOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
2535 patterns.add(commonTailPatternWithPar<IfOp, ParOp>);
2536 patterns.add(commonTailPatternWithSeq<IfOp, SeqOp>);
2542LogicalResult StaticIfOp::verify() {
2543 if (elseBodyExists()) {
2544 auto *elseBod = getElseBody();
2545 auto &elseOps = elseBod->getOperations();
2547 for (Operation &op : elseOps) {
2549 return op.emitOpError(
2550 "static if's else branch has non static control within it");
2555 auto *thenBod = getThenBody();
2556 auto &thenOps = thenBod->getOperations();
2557 for (Operation &op : thenOps) {
2560 return op.emitOpError(
2561 "static if's then branch has non static control within it");
2574 PatternRewriter &rewriter)
const override {
2575 if (!ifOp.getThenBody()->empty())
2577 if (ifOp.elseBodyExists() && !ifOp.getElseBody()->empty())
2586void StaticIfOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
2589 patterns.add(commonTailPatternWithPar<StaticIfOp, StaticParOp>);
2590 patterns.add(commonTailPatternWithSeq<StaticIfOp, StaticSeqOp>);
2596LogicalResult WhileOp::verify() {
2597 auto component = (*this)->getParentOfType<ComponentOp>();
2598 auto wiresOp = component.getWiresOp();
2600 std::optional<StringRef> optGroupName = getGroupName();
2601 if (!optGroupName) {
2605 StringRef groupName = *optGroupName;
2606 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(groupName);
2608 return emitOpError() <<
"with group '" << groupName
2609 <<
"', which does not exist.";
2611 if (isa<GroupOp>(groupOp))
2612 return emitOpError() <<
"with group '" << groupName
2613 <<
"', which is not a combinational group.";
2615 if (failed(groupOp.drivesPort(getCond())))
2616 return emitError() <<
"conditional op: '" <<
valueName(component, getCond())
2617 <<
"' expected to be driven from group: '" << groupName
2618 <<
"' but no driver was found.";
2623LogicalResult WhileOp::canonicalize(
WhileOp whileOp,
2624 PatternRewriter &rewriter) {
2625 if (whileOp.getBodyBlock()->empty()) {
2636LogicalResult StaticRepeatOp::verify() {
2637 for (
auto &&bodyOp : (*this).getRegion().front()) {
2640 return bodyOp.emitOpError(
2641 "static repeat has non static control within it");
2648template <
typename OpTy>
2649static LogicalResult
zeroRepeat(OpTy op, PatternRewriter &rewriter) {
2650 static_assert(IsAny<OpTy, RepeatOp, StaticRepeatOp>(),
2651 "Should be a RepeatOp or StaticPRepeatOp");
2652 if (op.getCount() == 0) {
2653 rewriter.eraseOp(op);
2660void StaticRepeatOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
2662 patterns.add(emptyControl<StaticRepeatOp>);
2663 patterns.add(zeroRepeat<StaticRepeatOp>);
2669void RepeatOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
2671 patterns.add(emptyControl<RepeatOp>);
2672 patterns.add(zeroRepeat<RepeatOp>);
2682 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &ports,
2683 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &inputs,
2684 SmallVectorImpl<Attribute> &portNames,
2685 SmallVectorImpl<Attribute> &inputNames,
2686 SmallVectorImpl<Type> &types) {
2687 OpAsmParser::UnresolvedOperand port;
2688 OpAsmParser::UnresolvedOperand input;
2690 auto parseParameter = [&]() -> ParseResult {
2691 if (parser.parseOperand(port) || parser.parseEqual() ||
2692 parser.parseOperand(input))
2694 ports.push_back(port);
2695 portNames.push_back(StringAttr::get(parser.getContext(), port.name));
2696 inputs.push_back(input);
2697 inputNames.push_back(StringAttr::get(parser.getContext(), input.name));
2700 if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
2703 if (parser.parseArrow())
2706 if (parser.parseType(type))
2708 types.push_back(type);
2711 return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
2715ParseResult InvokeOp::parse(OpAsmParser &parser, OperationState &result) {
2716 StringAttr componentName;
2717 SmallVector<OpAsmParser::UnresolvedOperand, 4> ports;
2718 SmallVector<OpAsmParser::UnresolvedOperand, 4> inputs;
2719 SmallVector<Attribute> portNames;
2720 SmallVector<Attribute> inputNames;
2721 SmallVector<Type, 4> types;
2722 if (parser.parseSymbolName(componentName))
2724 FlatSymbolRefAttr callee = FlatSymbolRefAttr::get(componentName);
2725 SMLoc loc = parser.getCurrentLocation();
2727 SmallVector<Attribute, 4> refCells;
2728 if (succeeded(parser.parseOptionalLSquare())) {
2729 if (parser.parseCommaSeparatedList([&]() -> ParseResult {
2730 std::string refCellName;
2731 std::string externalMem;
2732 NamedAttrList refCellAttr;
2733 if (parser.parseKeywordOrString(&refCellName) ||
2734 parser.parseEqual() || parser.parseKeywordOrString(&externalMem))
2736 auto externalMemAttr =
2737 SymbolRefAttr::get(parser.getContext(), externalMem);
2738 refCellAttr.append(StringAttr::get(parser.getContext(), refCellName),
2741 DictionaryAttr::get(parser.getContext(), refCellAttr));
2744 parser.parseRSquare())
2747 result.addAttribute(
"refCellsMap",
2748 ArrayAttr::get(parser.getContext(), refCells));
2750 result.addAttribute(
"callee", callee);
2754 if (parser.resolveOperands(ports, types, loc, result.operands))
2756 if (parser.resolveOperands(inputs, types, loc, result.operands))
2758 result.addAttribute(
"portNames",
2759 ArrayAttr::get(parser.getContext(), portNames));
2760 result.addAttribute(
"inputNames",
2761 ArrayAttr::get(parser.getContext(), inputNames));
2765void InvokeOp::print(OpAsmPrinter &p) {
2766 p <<
" @" << getCallee() <<
"[";
2767 auto refCellNamesMap = getRefCellsMap();
2768 llvm::interleaveComma(refCellNamesMap, p, [&](Attribute attr) {
2769 auto dictAttr = cast<DictionaryAttr>(attr);
2770 llvm::interleaveComma(dictAttr, p, [&](NamedAttribute namedAttr) {
2771 auto refCellName = namedAttr.getName().str();
2773 cast<FlatSymbolRefAttr>(namedAttr.getValue()).getValue();
2774 p << refCellName <<
" = " << externalMem;
2779 auto ports = getPorts();
2780 auto inputs = getInputs();
2781 llvm::interleaveComma(llvm::zip(ports, inputs), p, [&](
auto arg) {
2782 p << std::get<0>(arg) <<
" = " << std::get<1>(arg);
2785 llvm::interleaveComma(ports, p, [&](
auto port) { p << port.getType(); });
2792 bool isDestination) {
2801 Operation *operation = value.getDefiningOp();
2802 if (operation ==
nullptr)
2804 if (
auto *dialect = operation->getDialect(); isa<comb::CombDialect>(dialect))
2810Value InvokeOp::getInstGoValue() {
2811 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2812 Operation *operation = componentOp.lookupSymbol(getCallee());
2813 Value ret =
nullptr;
2814 llvm::TypeSwitch<Operation *>(operation)
2815 .Case<RegisterOp>([&](
auto op) { ret = operation->getResult(1); })
2816 .Case<MemoryOp, DivSPipeLibOp, DivUPipeLibOp, MultPipeLibOp,
2817 RemSPipeLibOp, RemUPipeLibOp>(
2818 [&](
auto op) { ret = operation->getResult(2); })
2819 .Case<InstanceOp>([&](
auto op) {
2820 auto portInfo = op.getReferencedComponent().getPortInfo();
2821 for (
auto [portInfo, res] :
2822 llvm::zip(portInfo, operation->getResults())) {
2823 if (portInfo.hasAttribute(
goPort))
2827 .Case<PrimitiveOp>([&](
auto op) {
2828 auto moduleExternOp = op.getReferencedPrimitive();
2829 auto argAttrs = moduleExternOp.getAllInputAttrs();
2830 for (
auto [attr, res] :
llvm::zip(argAttrs, op.getResults())) {
2831 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2832 if (!dictAttr.empty()) {
2833 if (dictAttr.begin()->getName().getValue() ==
"calyx.go")
2843Value InvokeOp::getInstDoneValue() {
2844 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2845 Operation *operation = componentOp.lookupSymbol(getCallee());
2846 Value ret =
nullptr;
2847 llvm::TypeSwitch<Operation *>(operation)
2848 .Case<RegisterOp, MemoryOp, DivSPipeLibOp, DivUPipeLibOp, MultPipeLibOp,
2849 RemSPipeLibOp, RemUPipeLibOp>([&](
auto op) {
2850 size_t doneIdx = operation->getResults().size() - 1;
2851 ret = operation->getResult(doneIdx);
2853 .Case<InstanceOp>([&](
auto op) {
2854 InstanceOp instanceOp = cast<InstanceOp>(operation);
2855 auto portInfo = instanceOp.getReferencedComponent().getPortInfo();
2856 for (
auto [portInfo, res] :
2857 llvm::zip(portInfo, operation->getResults())) {
2858 if (portInfo.hasAttribute(
donePort))
2862 .Case<PrimitiveOp>([&](
auto op) {
2863 PrimitiveOp primOp = cast<PrimitiveOp>(operation);
2864 auto moduleExternOp = primOp.getReferencedPrimitive();
2865 auto resAttrs = moduleExternOp.getAllOutputAttrs();
2866 for (
auto [attr, res] :
llvm::zip(resAttrs, primOp.getResults())) {
2867 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2868 if (!dictAttr.empty()) {
2869 if (dictAttr.begin()->getName().getValue() ==
"calyx.done")
2884 std::string str = isGo ?
"calyx.go" :
"calyx.done";
2885 for (Attribute attr : moduleExternOp.getAllInputAttrs()) {
2886 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2887 ret = llvm::count_if(dictAttr, [&](NamedAttribute iter) {
2888 return iter.getName().getValue() == str;
2895LogicalResult InvokeOp::verify() {
2896 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2897 StringRef callee = getCallee();
2898 Operation *operation = componentOp.lookupSymbol(callee);
2901 return emitOpError() <<
"with instance '@" << callee
2902 <<
"', which does not exist.";
2904 if (getInputs().
empty() && getRefCellsMap().
empty()) {
2905 return emitOpError() <<
"'@" << callee
2906 <<
"' has zero input and output port connections and "
2907 "has no passing-by-reference cells; "
2908 "expected at least one.";
2910 size_t goPortNum = 0, donePortNum = 0;
2913 llvm::TypeSwitch<Operation *>(operation)
2914 .Case<RegisterOp, DivSPipeLibOp, DivUPipeLibOp, MemoryOp, MultPipeLibOp,
2915 RemSPipeLibOp, RemUPipeLibOp>(
2916 [&](
auto op) { goPortNum = 1, donePortNum = 1; })
2917 .Case<InstanceOp>([&](
auto op) {
2918 auto portInfo = op.getReferencedComponent().getPortInfo();
2926 .Case<PrimitiveOp>([&](
auto op) {
2927 auto moduleExternOp = op.getReferencedPrimitive();
2933 if (goPortNum != 1 && donePortNum != 1)
2934 return emitOpError()
2935 <<
"'@" << callee <<
"'"
2936 <<
" is a combinational component and cannot be invoked, which must "
2937 "have single go port and single done port.";
2939 auto ports = getPorts();
2940 auto inputs = getInputs();
2942 Value goValue = getInstGoValue();
2943 Value doneValue = getInstDoneValue();
2944 for (
auto [port, input, portName, inputName] :
2945 llvm::zip(ports, inputs, getPortNames(), getInputNames())) {
2950 return emitOpError() <<
"'@" << callee <<
"' has input '"
2951 << cast<StringAttr>(portName).getValue()
2952 <<
"', which is a source port. The inputs are "
2953 "required to be destination ports.";
2955 if (port == goValue)
2956 return emitOpError() <<
"the go or write_en port of '@" << callee
2957 <<
"' cannot appear here.";
2960 return emitOpError() <<
"'@" << callee <<
"' has output '"
2961 << cast<StringAttr>(inputName).getValue()
2962 <<
"', which is a destination port. The inputs are "
2963 "required to be source ports.";
2965 return emitOpError() <<
"'@" << callee <<
"' has '"
2966 << cast<StringAttr>(inputName).getValue()
2967 <<
"', which is not a port or constant. Complex "
2968 "logic should be conducted in the guard.";
2969 if (input == doneValue)
2970 return emitOpError() <<
"the done port of '@" << callee
2971 <<
"' cannot appear here.";
2973 if (port.getDefiningOp() != operation && input.getDefiningOp() != operation)
2974 return emitOpError() <<
"the connection "
2975 << cast<StringAttr>(portName).getValue() <<
" = "
2976 << cast<StringAttr>(inputName).getValue()
2977 <<
" is not defined as an input port of '@" << callee
2987LogicalResult PadLibOp::verify() {
2988 unsigned inBits = getResult(0).getType().getIntOrFloatBitWidth();
2989 unsigned outBits = getResult(1).getType().getIntOrFloatBitWidth();
2990 if (inBits >= outBits)
2991 return emitOpError(
"expected input bits (")
2992 << inBits <<
')' <<
" to be less than output bits (" << outBits
2997LogicalResult SliceLibOp::verify() {
2998 unsigned inBits = getResult(0).getType().getIntOrFloatBitWidth();
2999 unsigned outBits = getResult(1).getType().getIntOrFloatBitWidth();
3000 if (inBits <= outBits)
3001 return emitOpError(
"expected input bits (")
3002 << inBits <<
')' <<
" to be greater than output bits (" << outBits
3007#define ImplBinPipeOpCellInterface(OpType, outName) \
3008 SmallVector<StringRef> OpType::portNames() { \
3009 return {clkPort, resetPort, goPort, "left", "right", outName, donePort}; \
3012 SmallVector<Direction> OpType::portDirections() { \
3013 return {Input, Input, Input, Input, Input, Output, Output}; \
3016 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
3017 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
3020 SmallVector<DictionaryAttr> OpType::portAttributes() { \
3021 MLIRContext *context = getContext(); \
3022 IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1); \
3023 NamedAttrList go, clk, reset, done; \
3024 go.append(goPort, isSet); \
3025 clk.append(clkPort, isSet); \
3026 reset.append(resetPort, isSet); \
3027 done.append(donePort, isSet); \
3029 clk.getDictionary(context), \
3030 reset.getDictionary(context), \
3031 go.getDictionary(context), \
3032 DictionaryAttr::get(context), \
3033 DictionaryAttr::get(context), \
3034 DictionaryAttr::get(context), \
3035 done.getDictionary(context) \
3039 bool OpType::isCombinational() { return false; }
3041#define ImplUnaryOpCellInterface(OpType) \
3042 SmallVector<StringRef> OpType::portNames() { return {"in", "out"}; } \
3043 SmallVector<Direction> OpType::portDirections() { return {Input, Output}; } \
3044 SmallVector<DictionaryAttr> OpType::portAttributes() { \
3045 return {DictionaryAttr::get(getContext()), \
3046 DictionaryAttr::get(getContext())}; \
3048 bool OpType::isCombinational() { return true; } \
3049 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
3050 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
3053#define ImplBinOpCellInterface(OpType) \
3054 SmallVector<StringRef> OpType::portNames() { \
3055 return {"left", "right", "out"}; \
3057 SmallVector<Direction> OpType::portDirections() { \
3058 return {Input, Input, Output}; \
3060 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
3061 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
3063 bool OpType::isCombinational() { return true; } \
3064 SmallVector<DictionaryAttr> OpType::portAttributes() { \
3065 return {DictionaryAttr::get(getContext()), \
3066 DictionaryAttr::get(getContext()), \
3067 DictionaryAttr::get(getContext())}; \
3111#include "circt/Dialect/Calyx/CalyxInterfaces.cpp.inc"
3114#define GET_OP_CLASSES
3115#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 llvm::MapVector< StringAttr, EnableOp > getAllEnableOpsInImmediateBody(OpTy parent)
Returns a mapping of {enabled Group name, EnableOp} for all EnableOps within the immediate ParOp's bo...
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 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 std::unique_ptr< Context > context
static ParseResult parseType(Type &result, StringRef name, AsmParser &parser)
Parse a type defined by this dialect.
static bool isDriven(DomainValue port)
Returns true if the value is driven by a connect op.
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.