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.";
209 DenseMap<StringAttr, Operation *> cells;
210 for (Operation &child : op->getRegion(0).front()) {
211 auto cell = dyn_cast<CellInterface>(&child);
214 auto name = StringAttr::get(op->getContext(), cell.instanceName());
215 auto [it, inserted] = cells.try_emplace(name, &child);
217 auto diagnostic = child.emitOpError() <<
"redefinition of symbol named '"
218 << cell.instanceName() <<
"'";
219 diagnostic.attachNote(it->second->getLoc())
220 <<
"see existing symbol definition here";
228 auto *opParent = op->getParentOp();
229 if (!isa<ComponentInterface>(opParent))
230 return op->emitOpError()
231 <<
"has parent: " << opParent <<
", expected ComponentInterface.";
236 auto *parent = op->getParentOp();
238 if (isa<calyx::EnableOp>(op) &&
239 !isa_and_nonnull<calyx::CalyxDialect>(parent->getDialect())) {
248 return op->emitOpError()
249 <<
"has parent: " << parent
250 <<
", which is not allowed for a control-like operation.";
252 if (op->getNumRegions() == 0)
255 auto ®ion = op->getRegion(0);
257 auto isValidBodyOp = [](Operation *operation) {
258 return isa<EnableOp, InvokeOp, SeqOp, IfOp, RepeatOp,
WhileOp, ParOp,
259 StaticParOp, StaticRepeatOp, StaticSeqOp, StaticIfOp>(operation);
261 for (
auto &&bodyOp : region.front()) {
262 if (isValidBodyOp(&bodyOp))
265 return op->emitOpError()
266 <<
"has operation: " << bodyOp.getName()
267 <<
", which is not allowed in this control-like operation";
273 auto ifOp = dyn_cast<IfInterface>(op);
275 if (ifOp.elseBodyExists() && ifOp.getElseBody()->empty())
276 return ifOp->emitOpError() <<
"empty 'else' region.";
286 SmallVector<OpAsmParser::UnresolvedOperand, 2> operandInfos;
287 OpAsmParser::UnresolvedOperand guardOrSource;
288 if (parser.parseOperand(guardOrSource))
291 if (succeeded(parser.parseOptionalQuestion())) {
292 OpAsmParser::UnresolvedOperand source;
294 if (parser.parseOperand(source))
296 operandInfos.push_back(source);
299 operandInfos.push_back(guardOrSource);
304 if (parser.parseColonType(type) ||
305 parser.resolveOperands(operandInfos, type, result.operands))
312template <
typename GroupPortType>
314 static_assert(IsAny<GroupPortType, GroupGoOp, GroupDoneOp>(),
315 "Should be a Calyx Group port.");
319 Value guard = op.getGuard(), source = op.getSrc();
322 p << source <<
" : " << source.getType();
327template <
typename OpTy>
329 PatternRewriter &rewriter) {
330 static_assert(IsAny<OpTy, SeqOp, ParOp, StaticSeqOp, StaticParOp>(),
331 "Should be a SeqOp, ParOp, StaticSeqOp, or StaticParOp");
333 if (isa<OpTy>(controlOp->getParentOp())) {
334 Block *controlBody = controlOp.getBodyBlock();
337 for (
auto &op : make_early_inc_range(*controlBody))
338 op.moveBefore(controlOp);
339 rewriter.eraseOp(controlOp);
346template <
typename OpTy>
347static LogicalResult
emptyControl(OpTy controlOp, PatternRewriter &rewriter) {
348 if (controlOp.getBodyBlock()->empty()) {
349 rewriter.eraseOp(controlOp);
358template <
typename OpTy>
360 PatternRewriter &rewriter) {
361 static_assert(IsAny<OpTy, IfOp, WhileOp>(),
362 "This is only applicable to WhileOp and IfOp.");
365 Value cond = op.getCond();
366 std::optional<StringRef> groupName = op.getGroupName();
367 auto component = op->template getParentOfType<ComponentOp>();
368 rewriter.eraseOp(op);
372 auto group = component.getWiresOp().template lookupSymbol<GroupInterface>(
374 if (SymbolTable::symbolKnownUseEmpty(group, component.getRegion()))
375 rewriter.eraseOp(group);
378 if (!isa<BlockArgument>(cond) && cond.getDefiningOp()->use_empty())
379 rewriter.eraseOp(cond.getDefiningOp());
385template <
typename OpTy>
387 static_assert(std::is_same<OpTy, StaticIfOp>(),
388 "This is only applicable to StatifIfOp.");
391 Value cond = op.getCond();
392 rewriter.eraseOp(op);
395 if (!isa<BlockArgument>(cond) && cond.getDefiningOp()->use_empty())
396 rewriter.eraseOp(cond.getDefiningOp());
403template <
typename ComponentTy>
405 auto componentName = comp.getName();
407 p.printSymbolName(componentName);
410 auto printPortDefList = [&](
auto ports) {
412 llvm::interleaveComma(ports, p, [&](
const PortInfo &port) {
413 p <<
"%" << port.
name.getValue() <<
": " << port.
type;
421 printPortDefList(comp.getInputPortInfo());
423 printPortDefList(comp.getOutputPortInfo());
426 p.printRegion(*comp.getRegion(),
false,
430 SmallVector<StringRef> elidedAttrs = {
434 ComponentTy::getSymNameAttrName(comp->getName()),
435 ComponentTy::getFunctionTypeAttrName(comp->getName()),
436 ComponentTy::getArgAttrsAttrName(comp->getName()),
437 ComponentTy::getResAttrsAttrName(comp->getName())};
438 p.printOptionalAttrDict(comp->getAttrs(), elidedAttrs);
445 SmallVectorImpl<OpAsmParser::Argument> &ports,
446 SmallVectorImpl<Type> &portTypes,
447 SmallVectorImpl<NamedAttrList> &portAttrs) {
449 OpAsmParser::Argument port;
452 if (parser.parseArgument(port) || parser.parseColon() ||
453 parser.parseType(portType))
455 port.type = portType;
456 ports.push_back(port);
457 portTypes.push_back(portType);
459 NamedAttrList portAttr;
460 portAttrs.push_back(succeeded(parser.parseOptionalAttrDict(portAttr))
466 return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
473 SmallVectorImpl<OpAsmParser::Argument> &ports,
474 SmallVectorImpl<Type> &portTypes) {
475 SmallVector<OpAsmParser::Argument> inPorts, outPorts;
476 SmallVector<Type> inPortTypes, outPortTypes;
477 SmallVector<NamedAttrList> portAttributes;
482 if (parser.parseArrow() ||
486 auto *
context = parser.getBuilder().getContext();
489 SmallVector<Attribute> portNames;
490 auto getPortName = [
context](
const auto &port) -> StringAttr {
491 StringRef name = port.ssaName.name;
492 if (name.starts_with(
"%"))
493 name = name.drop_front();
494 return StringAttr::get(
context, name);
496 llvm::transform(inPorts, std::back_inserter(portNames), getPortName);
497 llvm::transform(outPorts, std::back_inserter(portNames), getPortName);
499 result.addAttribute(
"portNames", ArrayAttr::get(
context, portNames));
504 ports.append(inPorts);
505 ports.append(outPorts);
506 portTypes.append(inPortTypes);
507 portTypes.append(outPortTypes);
509 SmallVector<Attribute> portAttrs;
510 llvm::transform(portAttributes, std::back_inserter(portAttrs),
511 [&](
auto attr) {
return attr.getDictionary(
context); });
512 result.addAttribute(
"portAttributes", ArrayAttr::get(
context, portAttrs));
517template <
typename ComponentTy>
519 OperationState &result) {
520 using namespace mlir::function_interface_impl;
522 StringAttr componentName;
523 if (parser.parseSymbolName(componentName,
524 ComponentTy::getSymNameAttrName(result.name),
528 SmallVector<mlir::OpAsmParser::Argument> ports;
530 SmallVector<Type> portTypes;
536 auto type = parser.getBuilder().getFunctionType(portTypes, {});
537 result.addAttribute(ComponentTy::getFunctionTypeAttrName(result.name),
538 TypeAttr::get(type));
540 auto *body = result.addRegion();
541 if (parser.parseRegion(*body, ports))
545 body->push_back(
new Block());
547 if (parser.parseOptionalAttrDict(result.attributes))
555static SmallVector<T>
concat(
const SmallVectorImpl<T> &a,
556 const SmallVectorImpl<T> &b) {
563template <
typename ComponentTy>
565 StringAttr name, ArrayRef<PortInfo> ports,
566 bool combinational) {
567 using namespace mlir::function_interface_impl;
569 result.addAttribute(ComponentTy::getSymNameAttrName(result.name), name);
571 std::pair<SmallVector<Type, 8>, SmallVector<Type, 8>> portIOTypes;
572 std::pair<SmallVector<Attribute, 8>, SmallVector<Attribute, 8>> portIONames;
573 std::pair<SmallVector<Attribute, 8>, SmallVector<Attribute, 8>>
575 SmallVector<Direction, 8> portDirections;
578 for (
auto &&port : ports) {
580 (isInput ? portIOTypes.first : portIOTypes.second).push_back(port.type);
581 (isInput ? portIONames.first : portIONames.second).push_back(port.name);
582 (isInput ? portIOAttributes.first : portIOAttributes.second)
583 .push_back(port.attributes);
585 auto portTypes =
concat(portIOTypes.first, portIOTypes.second);
586 auto portNames =
concat(portIONames.first, portIONames.second);
587 auto portAttributes =
concat(portIOAttributes.first, portIOAttributes.second);
590 auto functionType = builder.getFunctionType(portTypes, {});
592 result.addAttribute(CombComponentOp::getFunctionTypeAttrName(result.name),
593 TypeAttr::get(functionType));
595 result.addAttribute(ComponentOp::getFunctionTypeAttrName(result.name),
596 TypeAttr::get(functionType));
600 result.addAttribute(
"portNames", builder.getArrayAttr(portNames));
601 result.addAttribute(
"portDirections",
603 portIOTypes.first.size(),
604 portIOTypes.second.size()));
606 result.addAttribute(
"portAttributes", builder.getArrayAttr(portAttributes));
609 Region *region = result.addRegion();
610 Block *body =
new Block();
611 region->push_back(body);
614 body->addArguments(portTypes, SmallVector<Location, 4>(
615 portTypes.size(), builder.getUnknownLoc()));
618 IRRewriter::InsertionGuard guard(builder);
619 builder.setInsertionPointToStart(body);
620 WiresOp::create(builder, result.location);
622 ControlOp::create(builder, result.location);
633template <
typename Op>
635 auto *body = op.getBodyBlock();
638 auto opIt = body->getOps<Op>().begin();
645 ArrayAttr portNames = op.getPortNames();
647 for (
size_t i = 0, e = portNames.size(); i != e; ++i) {
648 auto portName = cast<StringAttr>(portNames[i]);
649 if (portName.getValue() == name)
650 return op.getBodyBlock()->getArgument(i);
655WiresOp calyx::ComponentOp::getWiresOp() {
659ControlOp calyx::ComponentOp::getControlOp() {
663Value calyx::ComponentOp::getGoPort() {
667Value calyx::ComponentOp::getDonePort() {
671Value calyx::ComponentOp::getClkPort() {
675Value calyx::ComponentOp::getResetPort() {
679SmallVector<PortInfo> ComponentOp::getPortInfo() {
680 auto portTypes = getArgumentTypes();
681 ArrayAttr portNamesAttr = getPortNames(), portAttrs = getPortAttributes();
682 APInt portDirectionsAttr = getPortDirections();
684 SmallVector<PortInfo> results;
685 for (
size_t i = 0, e = portNamesAttr.size(); i != e; ++i) {
686 results.push_back(PortInfo{cast<StringAttr>(portNamesAttr[i]), portTypes[i],
688 cast<DictionaryAttr>(portAttrs[i])});
694template <
typename Pred>
696 SmallVector<PortInfo> ports = op.getPortInfo();
697 llvm::erase_if(ports, p);
701SmallVector<PortInfo> ComponentOp::getInputPortInfo() {
706SmallVector<PortInfo> ComponentOp::getOutputPortInfo() {
711void ComponentOp::print(OpAsmPrinter &p) {
715ParseResult ComponentOp::parse(OpAsmParser &parser, OperationState &result) {
722 llvm::SmallVector<StringRef, 4> identifiers;
723 for (
PortInfo &port : op.getPortInfo()) {
724 auto portIds = port.getAllIdentifiers();
725 identifiers.append(portIds.begin(), portIds.end());
728 std::sort(identifiers.begin(), identifiers.end());
730 llvm::SmallVector<StringRef, 4> intersection,
733 std::set_intersection(interfacePorts.begin(), interfacePorts.end(),
734 identifiers.begin(), identifiers.end(),
735 std::back_inserter(intersection));
737 if (intersection.size() == interfacePorts.size())
740 SmallVector<StringRef, 4> difference;
741 std::set_difference(interfacePorts.begin(), interfacePorts.end(),
742 intersection.begin(), intersection.end(),
743 std::back_inserter(difference));
744 return op->emitOpError()
745 <<
"is missing the following required port attribute identifiers: "
749LogicalResult ComponentOp::verify() {
753 if (std::distance(wIt.begin(), wIt.end()) +
754 std::distance(cIt.begin(), cIt.end()) !=
756 return emitOpError() <<
"requires exactly one of each: '"
757 << WiresOp::getOperationName() <<
"', '"
758 << ControlOp::getOperationName() <<
"'.";
765 bool hasNoControlConstructs =
true;
766 getControlOp().walk<WalkOrder::PreOrder>([&](Operation *op) {
767 if (isa<EnableOp, InvokeOp, fsm::MachineOp>(op)) {
768 hasNoControlConstructs =
false;
769 return WalkResult::interrupt();
771 return WalkResult::advance();
773 bool hasNoAssignments =
774 getWiresOp().getBodyBlock()->getOps<AssignOp>().
empty();
775 if (hasNoControlConstructs && hasNoAssignments)
777 "The component currently does nothing. It needs to either have "
778 "continuous assignments in the Wires region or control "
779 "constructs in the Control region. The Control region "
780 "should contain at least one of ")
781 <<
"'" << EnableOp::getOperationName() <<
"' , "
782 <<
"'" << InvokeOp::getOperationName() <<
"' or "
783 <<
"'" << fsm::MachineOp::getOperationName() <<
"'.";
787void ComponentOp::build(OpBuilder &builder, OperationState &result,
788 StringAttr name, ArrayRef<PortInfo> ports) {
793void ComponentOp::getAsmBlockArgumentNames(
797 auto ports = getPortNames();
798 auto *block = &getRegion()->front();
799 for (
size_t i = 0, e = block->getNumArguments(); i != e; ++i)
800 setNameFn(block->getArgument(i), cast<StringAttr>(ports[i]).getValue());
807SmallVector<PortInfo> CombComponentOp::getPortInfo() {
808 auto portTypes = getArgumentTypes();
809 ArrayAttr portNamesAttr = getPortNames(), portAttrs = getPortAttributes();
810 APInt portDirectionsAttr = getPortDirections();
812 SmallVector<PortInfo> results;
813 for (
size_t i = 0, e = portNamesAttr.size(); i != e; ++i) {
814 results.push_back(
PortInfo{cast<StringAttr>(portNamesAttr[i]), portTypes[i],
816 cast<DictionaryAttr>(portAttrs[i])});
821WiresOp calyx::CombComponentOp::getWiresOp() {
823 auto opIt = body->getOps<WiresOp>().begin();
828template <
typename Pred>
830 SmallVector<PortInfo> ports = op.getPortInfo();
831 llvm::erase_if(ports, p);
835SmallVector<PortInfo> CombComponentOp::getInputPortInfo() {
840SmallVector<PortInfo> CombComponentOp::getOutputPortInfo() {
845void CombComponentOp::print(OpAsmPrinter &p) {
849ParseResult CombComponentOp::parse(OpAsmParser &parser,
850 OperationState &result) {
854LogicalResult CombComponentOp::verify() {
857 if (std::distance(wIt.begin(), wIt.end()) != 1)
858 return emitOpError() <<
"requires exactly one "
859 << WiresOp::getOperationName() <<
" op.";
863 if (std::distance(cIt.begin(), cIt.end()) != 0)
864 return emitOpError() <<
"must not have a `" << ControlOp::getOperationName()
868 bool hasNoAssignments =
869 getWiresOp().getBodyBlock()->getOps<AssignOp>().
empty();
870 if (hasNoAssignments)
872 "The component currently does nothing. It needs to either have "
873 "continuous assignments in the Wires region.");
876 auto cells = getOps<CellInterface>();
877 for (
auto cell : cells) {
878 if (!cell.isCombinational())
879 return emitOpError() <<
"contains non-combinational cell "
880 << cell.instanceName();
884 auto groups = getWiresOp().getOps<GroupOp>();
886 return emitOpError() <<
"contains group " << (*groups.begin()).getSymName();
891 auto combGroups = getWiresOp().getOps<CombGroupOp>();
892 if (!combGroups.empty())
893 return emitOpError() <<
"contains comb group "
894 << (*combGroups.begin()).getSymName();
899void CombComponentOp::build(OpBuilder &builder, OperationState &result,
900 StringAttr name, ArrayRef<PortInfo> ports) {
905void CombComponentOp::getAsmBlockArgumentNames(
909 auto ports = getPortNames();
910 auto *block = &getRegion()->front();
911 for (
size_t i = 0, e = block->getNumArguments(); i != e; ++i)
912 setNameFn(block->getArgument(i), cast<StringAttr>(ports[i]).getValue());
921SmallVector<InvokeOp, 4> ControlOp::getInvokeOps() {
922 SmallVector<InvokeOp, 4> ret;
923 this->walk([&](InvokeOp invokeOp) { ret.push_back(invokeOp); });
931void SeqOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
935 patterns.insert<CollapseUnaryControl<SeqOp>>(
context);
942LogicalResult StaticSeqOp::verify() {
944 auto &ops = (*this).getBodyBlock()->getOperations();
945 if (!llvm::all_of(ops, [&](Operation &op) {
return isStaticControl(&op); })) {
946 return emitOpError(
"StaticSeqOp has non static control within it");
952void StaticSeqOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
956 patterns.insert<CollapseUnaryControl<StaticSeqOp>>(
context);
963LogicalResult ParOp::verify() {
964 llvm::SmallSet<StringRef, 8> groupNames;
968 for (EnableOp op :
getBodyBlock()->getOps<EnableOp>()) {
969 StringRef groupName = op.getGroupName();
970 if (groupNames.count(groupName))
971 return emitOpError() <<
"cannot enable the same group: \"" << groupName
972 <<
"\" more than once.";
973 groupNames.insert(groupName);
979void ParOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
983 patterns.insert<CollapseUnaryControl<ParOp>>(
context);
990LogicalResult StaticParOp::verify() {
991 llvm::SmallSet<StringRef, 8> groupNames;
995 for (EnableOp op :
getBodyBlock()->getOps<EnableOp>()) {
996 StringRef groupName = op.getGroupName();
997 if (groupNames.count(groupName))
998 return emitOpError() <<
"cannot enable the same group: \"" << groupName
999 <<
"\" more than once.";
1000 groupNames.insert(groupName);
1004 auto &ops = (*this).getBodyBlock()->getOperations();
1005 for (Operation &op : ops) {
1007 return op.emitOpError(
"StaticParOp has non static control within it");
1014void StaticParOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1018 patterns.insert<CollapseUnaryControl<StaticParOp>>(
context);
1024LogicalResult WiresOp::verify() {
1025 auto componentInterface = (*this)->getParentOfType<ComponentInterface>();
1026 if (llvm::isa<ComponentOp>(componentInterface)) {
1027 auto component = llvm::cast<ComponentOp>(componentInterface);
1028 auto control = component.getControlOp();
1032 if (!isa<GroupInterface>(op))
1034 auto group = cast<GroupInterface>(op);
1035 auto groupName = group.symName();
1036 if (mlir::SymbolTable::symbolKnownUseEmpty(groupName, control))
1037 return op.emitOpError()
1038 <<
"with name: " << groupName
1039 <<
" is unused in the control execution schedule";
1046 for (
auto thisAssignment :
getBodyBlock()->getOps<AssignOp>()) {
1050 if (thisAssignment.getGuard())
1053 Value dest = thisAssignment.getDest();
1054 for (Operation *user : dest.getUsers()) {
1055 auto assignUser = dyn_cast<AssignOp>(user);
1056 if (!assignUser || assignUser.getDest() != dest ||
1057 assignUser == thisAssignment)
1060 return user->emitOpError() <<
"destination is already continuously "
1061 "driven. Other assignment is "
1075 Operation *definingOp = value.getDefiningOp();
1076 if (definingOp ==
nullptr || definingOp->hasTrait<
Combinational>())
1082 if (isa<InstanceOp>(definingOp))
1086 if (isa_and_nonnull<comb::CombDialect, hw::HWDialect>(
1087 definingOp->getDialect()))
1091 if (
auto r = dyn_cast<RegisterOp>(definingOp)) {
1092 return value == r.getOut()
1094 : group->emitOpError()
1095 <<
"with register: \"" << r.instanceName()
1096 <<
"\" is conducting a memory store. This is not "
1098 }
else if (
auto m = dyn_cast<MemoryOp>(definingOp)) {
1099 auto writePorts = {m.writeData(), m.writeEn()};
1100 return (llvm::none_of(
writePorts, [&](Value p) {
return p == value; }))
1102 : group->emitOpError()
1103 <<
"with memory: \"" << m.instanceName()
1104 <<
"\" is conducting a memory store. This "
1105 "is not combinational.";
1108 std::string portName =
1109 valueName(group->getParentOfType<ComponentOp>(), value);
1110 return group->emitOpError() <<
"with port: " << portName
1111 <<
". This operation is not combinational.";
1116LogicalResult CombGroupOp::verify() {
1118 auto assign = dyn_cast<AssignOp>(op);
1119 if (assign ==
nullptr)
1121 Value dst = assign.getDest(), src = assign.getSrc();
1132GroupGoOp GroupOp::getGoOp() {
1134 size_t nOps = std::distance(goOps.begin(), goOps.end());
1135 return nOps ? *goOps.begin() : GroupGoOp();
1138GroupDoneOp GroupOp::getDoneOp() {
1140 return cast<GroupDoneOp>(body->getTerminator());
1146void CycleOp::print(OpAsmPrinter &p) {
1149 auto start = this->getStart();
1150 auto end = this->getEnd();
1151 if (
end.has_value()) {
1152 p <<
"[" << start <<
":" <<
end.value() <<
"]";
1158ParseResult CycleOp::parse(OpAsmParser &parser, OperationState &result) {
1159 SmallVector<OpAsmParser::UnresolvedOperand, 2> operandInfos;
1161 uint32_t startLiteral;
1162 uint32_t endLiteral;
1164 auto hasEnd = succeeded(parser.parseOptionalLSquare());
1166 if (parser.parseInteger(startLiteral)) {
1167 parser.emitError(parser.getNameLoc(),
"Could not parse start cycle");
1171 auto start = parser.getBuilder().getI32IntegerAttr(startLiteral);
1172 result.addAttribute(getStartAttrName(result.name), start);
1175 if (parser.parseColon())
1178 if (
auto res = parser.parseOptionalInteger(endLiteral); res.has_value()) {
1179 auto end = parser.getBuilder().getI32IntegerAttr(endLiteral);
1180 result.addAttribute(getEndAttrName(result.name), end);
1183 if (parser.parseRSquare())
1187 result.addTypes(parser.getBuilder().getI1Type());
1192LogicalResult CycleOp::verify() {
1193 uint32_t latency = this->getGroupLatency();
1195 if (this->getStart() >= latency) {
1196 emitOpError(
"start cycle must be less than the group latency");
1200 if (this->getEnd().has_value()) {
1201 if (this->getStart() >= this->getEnd().value()) {
1202 emitOpError(
"start cycle must be less than end cycle");
1206 if (this->getEnd() >= latency) {
1207 emitOpError(
"end cycle must be less than the group latency");
1215uint32_t CycleOp::getGroupLatency() {
1216 auto group = (*this)->getParentOfType<StaticGroupOp>();
1217 return group.getLatency();
1247std::string AddFOpIEEE754::getCalyxLibraryName() {
return "std_addFN"; }
1249std::string MulFOpIEEE754::getCalyxLibraryName() {
return "std_mulFN"; }
1251std::string CompareFOpIEEE754::getCalyxLibraryName() {
return "std_compareFN"; }
1253std::string FpToIntOpIEEE754::getCalyxLibraryName() {
return "std_fpToInt"; }
1255std::string IntToFpOpIEEE754::getCalyxLibraryName() {
return "std_intToFp"; }
1257std::string DivSqrtOpIEEE754::getCalyxLibraryName() {
return "std_divSqrtFN"; }
1266 return llvm::any_of(port.getUses(), [&](
auto &&use) {
1267 auto assignOp = dyn_cast<AssignOp>(use.getOwner());
1268 if (assignOp == nullptr)
1271 Operation *parent = assignOp->getParentOp();
1272 if (isa<WiresOp>(parent))
1281 Value expected = isDriven ? assignOp.getDest() : assignOp.getSrc();
1282 return expected == port && group == parent;
1294 if (
auto cell = dyn_cast<CellInterface>(port.getDefiningOp());
1296 return groupOp.drivesAnyPort(cell.getInputPorts());
1301LogicalResult GroupOp::drivesPort(Value port) {
1305LogicalResult CombGroupOp::drivesPort(Value port) {
1309LogicalResult StaticGroupOp::drivesPort(Value port) {
1316 return success(llvm::all_of(ports, [&](Value port) {
1321LogicalResult GroupOp::drivesAllPorts(ValueRange ports) {
1325LogicalResult CombGroupOp::drivesAllPorts(ValueRange ports) {
1329LogicalResult StaticGroupOp::drivesAllPorts(ValueRange ports) {
1336 return success(llvm::any_of(ports, [&](Value port) {
1341LogicalResult GroupOp::drivesAnyPort(ValueRange ports) {
1345LogicalResult CombGroupOp::drivesAnyPort(ValueRange ports) {
1349LogicalResult StaticGroupOp::drivesAnyPort(ValueRange ports) {
1356 return success(llvm::any_of(ports, [&](Value port) {
1361LogicalResult GroupOp::readsAnyPort(ValueRange ports) {
1365LogicalResult CombGroupOp::readsAnyPort(ValueRange ports) {
1369LogicalResult StaticGroupOp::readsAnyPort(ValueRange ports) {
1376 GroupInterface group) {
1377 Operation *destDefiningOp = assign.getDest().getDefiningOp();
1378 if (destDefiningOp ==
nullptr)
1380 auto destCell = dyn_cast<CellInterface>(destDefiningOp);
1381 if (destCell ==
nullptr)
1384 LogicalResult verifyWrites =
1385 TypeSwitch<Operation *, LogicalResult>(destCell)
1386 .Case<RegisterOp>([&](
auto op) {
1389 return succeeded(group.drivesAnyPort({op.getWriteEn(), op.getIn()}))
1390 ? group.drivesAllPorts({op.getWriteEn(), op.getIn()})
1393 .Case<MemoryOp>([&](
auto op) {
1394 SmallVector<Value> requiredWritePorts;
1397 requiredWritePorts.push_back(op.writeEn());
1398 requiredWritePorts.push_back(op.writeData());
1399 for (Value address : op.addrPorts())
1400 requiredWritePorts.push_back(address);
1405 group.drivesAnyPort({op.writeData(), op.writeEn()}))
1406 ? group.drivesAllPorts(requiredWritePorts)
1409 .Case<AndLibOp, OrLibOp, XorLibOp, AddLibOp, SubLibOp, GtLibOp,
1410 LtLibOp, EqLibOp, NeqLibOp, GeLibOp, LeLibOp, LshLibOp,
1411 RshLibOp, SgtLibOp, SltLibOp, SeqLibOp, SneqLibOp, SgeLibOp,
1412 SleLibOp, SrshLibOp>([&](
auto op) {
1413 Value lhs = op.getLeft(), rhs = op.getRight();
1414 return succeeded(group.drivesAnyPort({lhs, rhs}))
1415 ? group.drivesAllPorts({lhs, rhs})
1418 .Default([&](
auto op) {
return success(); });
1420 if (failed(verifyWrites))
1421 return group->emitOpError()
1422 <<
"with cell: " << destCell->getName() <<
" \""
1423 << destCell.instanceName()
1424 <<
"\" is performing a write and failed to drive all necessary "
1427 Operation *srcDefiningOp = assign.getSrc().getDefiningOp();
1428 if (srcDefiningOp ==
nullptr)
1430 auto srcCell = dyn_cast<CellInterface>(srcDefiningOp);
1431 if (srcCell ==
nullptr)
1434 LogicalResult verifyReads =
1435 TypeSwitch<Operation *, LogicalResult>(srcCell)
1436 .Case<MemoryOp>([&](
auto op) {
1440 return succeeded(group.readsAnyPort({op.readData()}))
1441 ? group.drivesAllPorts(op.addrPorts())
1444 .Default([&](
auto op) {
return success(); });
1446 if (failed(verifyReads))
1447 return group->emitOpError() <<
"with cell: " << srcCell->getName() <<
" \""
1448 << srcCell.instanceName()
1449 <<
"\" is having a read performed upon it, and "
1450 "failed to drive all necessary ports.";
1456 auto group = dyn_cast<GroupInterface>(op);
1457 if (group ==
nullptr)
1460 for (
auto &&groupOp : *group.getBody()) {
1461 auto assign = dyn_cast<AssignOp>(groupOp);
1462 if (assign ==
nullptr)
1478 ArrayRef<StringRef> portNames) {
1479 auto cellInterface = dyn_cast<CellInterface>(op);
1480 assert(cellInterface &&
"must implement the Cell interface");
1482 std::string prefix = cellInterface.instanceName().str() +
".";
1483 for (
size_t i = 0, e = portNames.size(); i != e; ++i)
1484 setNameFn(op->getResult(i), prefix + portNames[i].str());
1495 bool isDestination) {
1496 Operation *definingOp = value.getDefiningOp();
1497 bool isComponentPort = isa<BlockArgument>(value),
1498 isCellInterfacePort = isa_and_nonnull<CellInterface>(definingOp);
1499 assert((isComponentPort || isCellInterfacePort) &&
"Not a port.");
1503 : cast<CellInterface>(definingOp).portInfo(value);
1505 bool isSource = !isDestination;
1508 (isDestination && isComponentPort) || (isSource && isCellInterfacePort)
1515 <<
"has a " << (isComponentPort ?
"component" :
"cell")
1517 << (isDestination ?
"destination" :
"source")
1518 <<
" with the incorrect direction.";
1525 bool isSource = !isDestination;
1526 Value value = isDestination ? op.getDest() : op.getSrc();
1531 if (isDestination && !isa<GroupGoOp, GroupDoneOp>(value.getDefiningOp()))
1532 return op->emitOpError(
1533 "has an invalid destination port. It must be drive-able.");
1540LogicalResult AssignOp::verify() {
1541 bool isDestination =
true, isSource =
false;
1550ParseResult AssignOp::parse(OpAsmParser &parser, OperationState &result) {
1551 OpAsmParser::UnresolvedOperand destination;
1552 if (parser.parseOperand(destination) || parser.parseEqual())
1558 OpAsmParser::UnresolvedOperand guardOrSource;
1559 if (parser.parseOperand(guardOrSource))
1564 OpAsmParser::UnresolvedOperand source;
1565 bool hasGuard = succeeded(parser.parseOptionalQuestion());
1568 if (parser.parseOperand(source))
1573 if (parser.parseColonType(type) ||
1574 parser.resolveOperand(destination, type, result.operands))
1578 Type i1Type = parser.getBuilder().getI1Type();
1581 if (parser.resolveOperand(source, type, result.operands) ||
1582 parser.resolveOperand(guardOrSource, i1Type, result.operands))
1586 if (parser.resolveOperand(guardOrSource, type, result.operands))
1593void AssignOp::print(OpAsmPrinter &p) {
1594 p <<
" " << getDest() <<
" = ";
1596 Value bguard = getGuard(), source = getSrc();
1599 p << bguard <<
" ? ";
1603 p << source <<
" : " << source.getType();
1612ComponentInterface InstanceOp::getReferencedComponent() {
1613 auto module = (*this)->getParentOfType<ModuleOp>();
1617 return module.lookupSymbol<ComponentInterface>(getComponentName());
1625 ComponentInterface referencedComponent) {
1626 auto module = instance->getParentOfType<ModuleOp>();
1627 StringRef entryPointName =
1628 module->getAttrOfType<StringAttr>("calyx.entrypoint");
1629 if (instance.getComponentName() == entryPointName)
1630 return instance.emitOpError()
1631 <<
"cannot reference the entry-point component: '" << entryPointName
1635 SmallVector<PortInfo> componentPorts = referencedComponent.getPortInfo();
1636 size_t numPorts = componentPorts.size();
1638 size_t numResults = instance.getNumResults();
1639 if (numResults != numPorts)
1640 return instance.emitOpError()
1641 <<
"has a wrong number of results; expected: " << numPorts
1642 <<
" but got " << numResults;
1644 for (
size_t i = 0; i != numResults; ++i) {
1645 auto resultType = instance.getResult(i).getType();
1646 auto expectedType = componentPorts[i].type;
1647 if (resultType == expectedType)
1649 return instance.emitOpError()
1650 <<
"result type for " << componentPorts[i].name <<
" must be "
1651 << expectedType <<
", but got " << resultType;
1656LogicalResult InstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1657 Operation *op = *
this;
1658 auto module = op->getParentOfType<ModuleOp>();
1659 Operation *referencedComponent =
1660 symbolTable.lookupNearestSymbolFrom(module, getComponentNameAttr());
1661 if (referencedComponent ==
nullptr)
1662 return emitError() <<
"referencing component: '" << getComponentName()
1663 <<
"', which does not exist.";
1665 Operation *shadowedComponentName =
1666 symbolTable.lookupNearestSymbolFrom(module, getSymNameAttr());
1667 if (shadowedComponentName !=
nullptr)
1668 return emitError() <<
"instance symbol: '" << instanceName()
1669 <<
"' is already a symbol for another component.";
1672 auto parentComponent = op->getParentOfType<ComponentOp>();
1673 if (parentComponent == referencedComponent)
1674 return emitError() <<
"recursive instantiation of its parent component: '"
1675 << getComponentName() <<
"'";
1677 assert(isa<ComponentInterface>(referencedComponent) &&
1678 "Should be a ComponentInterface.");
1680 cast<ComponentInterface>(referencedComponent));
1688SmallVector<StringRef> InstanceOp::portNames() {
1689 SmallVector<StringRef> portNames;
1690 for (Attribute name : getReferencedComponent().getPortNames())
1691 portNames.push_back(cast<StringAttr>(name).getValue());
1695SmallVector<Direction> InstanceOp::portDirections() {
1696 SmallVector<Direction> portDirections;
1698 portDirections.push_back(port.direction);
1699 return portDirections;
1702SmallVector<DictionaryAttr> InstanceOp::portAttributes() {
1703 SmallVector<DictionaryAttr> portAttributes;
1705 portAttributes.push_back(port.attributes);
1706 return portAttributes;
1709bool InstanceOp::isCombinational() {
1710 return isa<CombComponentOp>(getReferencedComponent());
1719hw::HWModuleExternOp PrimitiveOp::getReferencedPrimitive() {
1720 auto module = (*this)->getParentOfType<ModuleOp>();
1724 return module.lookupSymbol<hw::HWModuleExternOp>(getPrimitiveName());
1733 auto module = instance->getParentOfType<ModuleOp>();
1734 StringRef entryPointName =
1735 module->getAttrOfType<StringAttr>("calyx.entrypoint");
1736 if (instance.getPrimitiveName() == entryPointName)
1737 return instance.emitOpError()
1738 <<
"cannot reference the entry-point component: '" << entryPointName
1742 auto primitivePorts = referencedPrimitive.getPortList();
1743 size_t numPorts = primitivePorts.size();
1745 size_t numResults = instance.getNumResults();
1746 if (numResults != numPorts)
1747 return instance.emitOpError()
1748 <<
"has a wrong number of results; expected: " << numPorts
1749 <<
" but got " << numResults;
1752 ArrayAttr modParameters = referencedPrimitive.getParameters();
1753 ArrayAttr parameters = instance.getParameters().value_or(ArrayAttr());
1754 size_t numExpected = modParameters.size();
1755 size_t numParams = parameters.size();
1756 if (numParams != numExpected)
1757 return instance.emitOpError()
1758 <<
"has the wrong number of parameters; expected: " << numExpected
1759 <<
" but got " << numParams;
1761 for (
size_t i = 0; i != numExpected; ++i) {
1762 auto param = cast<circt::hw::ParamDeclAttr>(parameters[i]);
1763 auto modParam = cast<circt::hw::ParamDeclAttr>(modParameters[i]);
1765 auto paramName = param.getName();
1766 if (paramName != modParam.getName())
1767 return instance.emitOpError()
1768 <<
"parameter #" << i <<
" should have name " << modParam.getName()
1769 <<
" but has name " << paramName;
1771 if (param.getType() != modParam.getType())
1772 return instance.emitOpError()
1773 <<
"parameter " << paramName <<
" should have type "
1774 << modParam.getType() <<
" but has type " << param.getType();
1778 if (!param.getValue())
1779 return instance.emitOpError(
"parameter ")
1780 << paramName <<
" must have a value";
1783 for (
size_t i = 0; i != numResults; ++i) {
1784 auto resultType = instance.getResult(i).getType();
1785 auto expectedType = primitivePorts[i].type;
1787 instance.getLoc(), instance.getParametersAttr(), expectedType);
1788 if (failed(replacedType))
1790 if (resultType == replacedType)
1792 return instance.emitOpError()
1793 <<
"result type for " << primitivePorts[i].name <<
" must be "
1794 << expectedType <<
", but got " << resultType;
1800PrimitiveOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1801 Operation *op = *
this;
1802 auto module = op->getParentOfType<ModuleOp>();
1803 Operation *referencedPrimitive =
1804 symbolTable.lookupNearestSymbolFrom(module, getPrimitiveNameAttr());
1805 if (referencedPrimitive ==
nullptr)
1806 return emitError() <<
"referencing primitive: '" << getPrimitiveName()
1807 <<
"', which does not exist.";
1809 Operation *shadowedPrimitiveName =
1810 symbolTable.lookupNearestSymbolFrom(module, getSymNameAttr());
1811 if (shadowedPrimitiveName !=
nullptr)
1812 return emitError() <<
"instance symbol: '" << instanceName()
1813 <<
"' is already a symbol for another primitive.";
1816 auto parentPrimitive = op->getParentOfType<hw::HWModuleExternOp>();
1817 if (parentPrimitive == referencedPrimitive)
1818 return emitError() <<
"recursive instantiation of its parent primitive: '"
1819 << getPrimitiveName() <<
"'";
1821 assert(isa<hw::HWModuleExternOp>(referencedPrimitive) &&
1822 "Should be a HardwareModuleExternOp.");
1825 cast<hw::HWModuleExternOp>(referencedPrimitive));
1833SmallVector<StringRef> PrimitiveOp::portNames() {
1834 SmallVector<StringRef> portNames;
1835 auto ports = getReferencedPrimitive().getPortList();
1836 for (
auto port : ports)
1837 portNames.push_back(port.name.getValue());
1849 llvm_unreachable(
"Impossible port type");
1852SmallVector<Direction> PrimitiveOp::portDirections() {
1853 SmallVector<Direction> portDirections;
1854 auto ports = getReferencedPrimitive().getPortList();
1855 for (hw::PortInfo port : ports)
1857 return portDirections;
1860bool PrimitiveOp::isCombinational() {
return false; }
1866 DictionaryAttr dict) {
1870 llvm::SmallVector<NamedAttribute>
attrs;
1871 for (NamedAttribute attr : dict) {
1872 Dialect *dialect = attr.getNameDialect();
1873 if (dialect ==
nullptr || !isa<CalyxDialect>(*dialect))
1875 StringRef name = attr.getName().strref();
1876 StringAttr newName = builder.getStringAttr(std::get<1>(name.split(
".")));
1877 attr.setName(newName);
1878 attrs.push_back(attr);
1880 return builder.getDictionaryAttr(
attrs);
1884SmallVector<DictionaryAttr> PrimitiveOp::portAttributes() {
1885 SmallVector<DictionaryAttr> portAttributes;
1886 OpBuilder builder(getContext());
1887 hw::HWModuleExternOp prim = getReferencedPrimitive();
1888 auto argAttrs = prim.getAllInputAttrs();
1889 auto resAttrs = prim.getAllOutputAttrs();
1890 for (
auto a : argAttrs)
1891 portAttributes.push_back(
1893 for (
auto a : resAttrs)
1894 portAttributes.push_back(
1896 return portAttributes;
1905 SmallVector<Attribute> ¶meters) {
1907 return parser.parseCommaSeparatedList(
1908 OpAsmParser::Delimiter::OptionalLessGreater, [&]() {
1913 if (parser.parseKeywordOrString(&name) || parser.parseColonType(type))
1917 if (succeeded(parser.parseOptionalEqual())) {
1918 if (parser.parseAttribute(value, type))
1922 auto &builder = parser.getBuilder();
1923 parameters.push_back(hw::ParamDeclAttr::get(
1924 builder.getContext(), builder.getStringAttr(name), type, value));
1931 ArrayAttr ¶meters) {
1932 SmallVector<Attribute> parseParameters;
1936 parameters = ArrayAttr::get(parser.getContext(), parseParameters);
1943 ArrayAttr parameters) {
1944 if (parameters.empty())
1948 llvm::interleaveComma(parameters, p, [&](Attribute param) {
1949 auto paramAttr = cast<hw::ParamDeclAttr>(param);
1950 p << paramAttr.getName().getValue() <<
": " << paramAttr.getType();
1951 if (
auto value = paramAttr.getValue()) {
1953 p.printAttributeWithoutType(value);
1967 auto parent = (*this)->getParentOfType<GroupOp>();
1968 StringRef name = parent.getSymName();
1969 std::string resultName = name.str() +
".go";
1970 setNameFn(getResult(), resultName);
1973void GroupGoOp::print(OpAsmPrinter &p) {
printGroupPort(p, *
this); }
1975ParseResult GroupGoOp::parse(OpAsmParser &parser, OperationState &result) {
1979 result.addTypes(parser.getBuilder().getI1Type());
1987LogicalResult GroupDoneOp::verify() {
1988 Operation *srcOp = getSrc().getDefiningOp();
1989 Value optionalGuard = getGuard();
1990 Operation *guardOp = optionalGuard ? optionalGuard.getDefiningOp() :
nullptr;
1991 bool noGuard = (guardOp ==
nullptr);
1993 if (srcOp ==
nullptr)
1997 if (isa<hw::ConstantOp>(srcOp) && (noGuard || isa<hw::ConstantOp>(guardOp)))
1998 return emitOpError() <<
"with constant source"
1999 << (noGuard ?
"" :
" and constant guard")
2000 <<
". This should be a combinational group.";
2005void GroupDoneOp::print(OpAsmPrinter &p) {
printGroupPort(p, *
this); }
2007ParseResult GroupDoneOp::parse(OpAsmParser &parser, OperationState &result) {
2014void ConstantOp::getAsmResultNames(
2015 function_ref<
void(Value, StringRef)> setNameFn) {
2016 if (isa<FloatAttr>(getValue())) {
2017 setNameFn(getResult(),
"cst");
2020 auto intCst = llvm::dyn_cast<IntegerAttr>(getValue());
2021 auto intType = llvm::dyn_cast<IntegerType>(getType());
2024 if (intType && intType.getWidth() == 1)
2025 return setNameFn(getResult(), intCst.getInt() > 0 ?
"true" :
"false");
2028 SmallString<32> specialNameBuffer;
2029 llvm::raw_svector_ostream specialName(specialNameBuffer);
2030 specialName <<
'c' << intCst.getValue();
2032 specialName <<
'_' << getType();
2033 setNameFn(getResult(), specialName.str());
2036LogicalResult ConstantOp::verify() {
2037 auto type = getType();
2038 assert(isa<IntegerType>(type) &&
"must be an IntegerType");
2040 if (
auto valTyBitWidth = getValue().getType().getIntOrFloatBitWidth();
2041 valTyBitWidth != type.getIntOrFloatBitWidth()) {
2042 return emitOpError() <<
"value type bit width" << valTyBitWidth
2043 <<
" must match return type: "
2044 << type.getIntOrFloatBitWidth();
2047 if (llvm::isa<IntegerType>(type) &&
2048 !llvm::cast<IntegerType>(type).isSignless())
2049 return emitOpError(
"integer return type must be signless");
2051 if (!llvm::isa<IntegerAttr, FloatAttr>(getValue())) {
2052 return emitOpError(
"value must be an integer or float attribute");
2058OpFoldResult calyx::ConstantOp::fold(FoldAdaptor adaptor) {
2059 return getValueAttr();
2062void calyx::ConstantOp::build(OpBuilder &builder, OperationState &state,
2063 StringRef symName, Attribute attr, Type type) {
2064 state.addAttribute(ConstantOp::getSymNameAttrName(state.name),
2065 builder.getStringAttr(symName));
2066 state.addAttribute(
"value", attr);
2067 SmallVector<Type> types;
2068 types.push_back(type);
2069 state.addTypes(types);
2072SmallVector<StringRef> ConstantOp::portNames() {
return {
"out"}; }
2074SmallVector<Direction> ConstantOp::portDirections() {
return {
Output}; }
2076SmallVector<DictionaryAttr> ConstantOp::portAttributes() {
2077 return {DictionaryAttr::get(getContext())};
2080bool ConstantOp::isCombinational() {
return true; }
2091SmallVector<StringRef> RegisterOp::portNames() {
2095SmallVector<Direction> RegisterOp::portDirections() {
2099SmallVector<DictionaryAttr> RegisterOp::portAttributes() {
2100 MLIRContext *
context = getContext();
2101 IntegerAttr isSet = IntegerAttr::get(IntegerType::get(
context, 1), 1);
2102 NamedAttrList writeEn,
clk, reset, done;
2103 writeEn.append(
goPort, isSet);
2109 writeEn.getDictionary(
context),
2117bool RegisterOp::isCombinational() {
return false; }
2128SmallVector<StringRef> MemoryOp::portNames() {
2129 SmallVector<StringRef> portNames;
2130 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i) {
2132 StringAttr::get(this->getContext(),
"addr" + std::to_string(i));
2133 portNames.push_back(nameAttr.getValue());
2135 portNames.append({
"write_data",
"write_en",
clkPort,
"read_data",
donePort});
2139SmallVector<Direction> MemoryOp::portDirections() {
2140 SmallVector<Direction> portDirections;
2141 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2142 portDirections.push_back(
Input);
2144 return portDirections;
2147SmallVector<DictionaryAttr> MemoryOp::portAttributes() {
2148 SmallVector<DictionaryAttr> portAttributes;
2149 MLIRContext *
context = getContext();
2150 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2151 portAttributes.push_back(DictionaryAttr::get(
context));
2154 IntegerAttr isSet = IntegerAttr::get(IntegerType::get(
context, 1), 1);
2155 NamedAttrList writeEn,
clk, reset, done;
2156 writeEn.append(
goPort, isSet);
2159 portAttributes.append({DictionaryAttr::get(
context),
2160 writeEn.getDictionary(
context),
2165 return portAttributes;
2168void MemoryOp::build(OpBuilder &builder, OperationState &state,
2169 StringRef instanceName, int64_t width,
2170 ArrayRef<int64_t> sizes, ArrayRef<int64_t> addrSizes) {
2171 state.addAttribute(MemoryOp::getSymNameAttrName(state.name),
2172 builder.getStringAttr(instanceName));
2173 state.addAttribute(
"width", builder.getI64IntegerAttr(width));
2174 state.addAttribute(
"sizes", builder.getI64ArrayAttr(sizes));
2175 state.addAttribute(
"addrSizes", builder.getI64ArrayAttr(addrSizes));
2176 SmallVector<Type> types;
2177 for (int64_t size : addrSizes)
2178 types.push_back(builder.getIntegerType(size));
2179 types.push_back(builder.getIntegerType(width));
2180 types.push_back(builder.getI1Type());
2181 types.push_back(builder.getI1Type());
2182 types.push_back(builder.getIntegerType(width));
2183 types.push_back(builder.getI1Type());
2184 state.addTypes(types);
2187LogicalResult MemoryOp::verify() {
2188 ArrayRef<Attribute> opSizes = getSizes().getValue();
2189 ArrayRef<Attribute> opAddrSizes = getAddrSizes().getValue();
2190 size_t numDims = getSizes().size();
2191 size_t numAddrs = getAddrSizes().size();
2192 if (numDims != numAddrs)
2193 return emitOpError(
"mismatched number of dimensions (")
2194 << numDims <<
") and address sizes (" << numAddrs <<
")";
2196 size_t numExtraPorts = 5;
2197 if (getNumResults() != numAddrs + numExtraPorts)
2198 return emitOpError(
"incorrect number of address ports, expected ")
2201 for (
size_t i = 0; i < numDims; ++i) {
2202 int64_t size = cast<IntegerAttr>(opSizes[i]).getInt();
2203 int64_t addrSize = cast<IntegerAttr>(opAddrSizes[i]).getInt();
2204 if (llvm::Log2_64_Ceil(size) > addrSize)
2205 return emitOpError(
"address size (")
2206 << addrSize <<
") for dimension " << i
2207 <<
" can't address the entire range (" << size <<
")";
2222SmallVector<StringRef> SeqMemoryOp::portNames() {
2223 SmallVector<StringRef> portNames;
2224 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i) {
2226 StringAttr::get(this->getContext(),
"addr" + std::to_string(i));
2227 portNames.push_back(nameAttr.getValue());
2229 portNames.append({
clkPort,
"reset",
"content_en",
"write_en",
"write_data",
2230 "read_data",
"done"});
2234SmallVector<Direction> SeqMemoryOp::portDirections() {
2235 SmallVector<Direction> portDirections;
2236 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2237 portDirections.push_back(
Input);
2239 return portDirections;
2242SmallVector<DictionaryAttr> SeqMemoryOp::portAttributes() {
2243 SmallVector<DictionaryAttr> portAttributes;
2244 MLIRContext *
context = getContext();
2245 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2246 portAttributes.push_back(DictionaryAttr::get(
context));
2250 IntegerAttr isSet = IntegerAttr::get(builder.getIndexType(), 1);
2251 IntegerAttr isTwo = IntegerAttr::get(builder.getIndexType(), 2);
2252 NamedAttrList done,
clk, reset, contentEn;
2256 contentEn.append(
goPort, isTwo);
2257 portAttributes.append({
clk.getDictionary(
context),
2259 contentEn.getDictionary(
context),
2265 return portAttributes;
2268void SeqMemoryOp::build(OpBuilder &builder, OperationState &state,
2269 StringRef instanceName, int64_t width,
2270 ArrayRef<int64_t> sizes, ArrayRef<int64_t> addrSizes) {
2271 state.addAttribute(SeqMemoryOp::getSymNameAttrName(state.name),
2272 builder.getStringAttr(instanceName));
2273 state.addAttribute(
"width", builder.getI64IntegerAttr(width));
2274 state.addAttribute(
"sizes", builder.getI64ArrayAttr(sizes));
2275 state.addAttribute(
"addrSizes", builder.getI64ArrayAttr(addrSizes));
2276 SmallVector<Type> types;
2277 for (int64_t size : addrSizes)
2278 types.push_back(builder.getIntegerType(size));
2279 types.push_back(builder.getI1Type());
2280 types.push_back(builder.getI1Type());
2281 types.push_back(builder.getI1Type());
2282 types.push_back(builder.getI1Type());
2283 types.push_back(builder.getIntegerType(width));
2284 types.push_back(builder.getIntegerType(width));
2285 types.push_back(builder.getI1Type());
2286 state.addTypes(types);
2289LogicalResult SeqMemoryOp::verify() {
2290 ArrayRef<Attribute> opSizes = getSizes().getValue();
2291 ArrayRef<Attribute> opAddrSizes = getAddrSizes().getValue();
2292 size_t numDims = getSizes().size();
2293 size_t numAddrs = getAddrSizes().size();
2294 if (numDims != numAddrs)
2295 return emitOpError(
"mismatched number of dimensions (")
2296 << numDims <<
") and address sizes (" << numAddrs <<
")";
2298 size_t numExtraPorts =
2300 if (getNumResults() != numAddrs + numExtraPorts)
2301 return emitOpError(
"incorrect number of address ports, expected ")
2304 for (
size_t i = 0; i < numDims; ++i) {
2305 int64_t size = cast<IntegerAttr>(opSizes[i]).getInt();
2306 int64_t addrSize = cast<IntegerAttr>(opAddrSizes[i]).getInt();
2307 if (llvm::Log2_64_Ceil(size) > addrSize)
2308 return emitOpError(
"address size (")
2309 << addrSize <<
") for dimension " << i
2310 <<
" can't address the entire range (" << size <<
")";
2319LogicalResult EnableOp::verify() {
2320 auto component = (*this)->getParentOfType<ComponentOp>();
2321 auto wiresOp = component.getWiresOp();
2322 StringRef name = getGroupName();
2324 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(name);
2326 return emitOpError() <<
"with group '" << name
2327 <<
"', which does not exist.";
2329 if (isa<CombGroupOp>(groupOp))
2330 return emitOpError() <<
"with group '" << name
2331 <<
"', which is a combinational group.";
2340LogicalResult IfOp::verify() {
2341 std::optional<StringRef> optGroupName = getGroupName();
2342 if (!optGroupName) {
2346 auto component = (*this)->getParentOfType<ComponentOp>();
2347 WiresOp wiresOp = component.getWiresOp();
2348 StringRef groupName = *optGroupName;
2349 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(groupName);
2351 return emitOpError() <<
"with group '" << groupName
2352 <<
"', which does not exist.";
2354 if (isa<GroupOp>(groupOp))
2355 return emitOpError() <<
"with group '" << groupName
2356 <<
"', which is not a combinational group.";
2358 if (failed(groupOp.drivesPort(getCond())))
2359 return emitError() <<
"with conditional op: '"
2361 <<
"' expected to be driven from group: '" << groupName
2362 <<
"' but no driver was found.";
2370template <
typename OpTy>
2372 static_assert(IsAny<OpTy, SeqOp, StaticSeqOp>(),
2373 "Should be a StaticSeqOp or SeqOp.");
2374 if (parent.getBodyBlock()->empty())
2375 return std::nullopt;
2376 auto &lastOp = parent.getBodyBlock()->back();
2377 if (
auto enableOp = dyn_cast<EnableOp>(lastOp))
2379 if (
auto seqOp = dyn_cast<SeqOp>(lastOp))
2381 if (
auto staticSeqOp = dyn_cast<StaticSeqOp>(lastOp))
2384 return std::nullopt;
2389template <
typename OpTy>
2390static llvm::MapVector<StringAttr, EnableOp>
2392 static_assert(IsAny<OpTy, ParOp, StaticParOp>(),
2393 "Should be a StaticParOp or ParOp.");
2396 Block *body = parent.getBodyBlock();
2397 for (EnableOp op : body->getOps<EnableOp>())
2398 enables.insert(std::pair(op.getGroupNameAttr().getAttr(), op));
2410template <
typename IfOpTy,
typename TailOpTy>
2412 static_assert(IsAny<TailOpTy, SeqOp, ParOp, StaticSeqOp, StaticParOp>(),
2413 "Should be a SeqOp, ParOp, StaticSeqOp, or StaticParOp.");
2414 static_assert(IsAny<IfOpTy, IfOp, StaticIfOp>(),
2415 "Should be a IfOp or StaticIfOp.");
2417 if (!op.thenBodyExists() || !op.elseBodyExists())
2419 if (op.getThenBody()->empty() || op.getElseBody()->empty())
2422 Block *thenBody = op.getThenBody(), *elseBody = op.getElseBody();
2423 return isa<TailOpTy>(thenBody->front()) && isa<TailOpTy>(elseBody->front());
2434template <
typename IfOpTy,
typename SeqOpTy>
2436 PatternRewriter &rewriter) {
2437 static_assert(IsAny<IfOpTy, IfOp, StaticIfOp>(),
2438 "Should be an IfOp or StaticIfOp.");
2439 static_assert(IsAny<SeqOpTy, SeqOp, StaticSeqOp>(),
2440 "Branches should be checking for an SeqOp or StaticSeqOp");
2443 auto thenControl = cast<SeqOpTy>(ifOp.getThenBody()->front()),
2444 elseControl = cast<SeqOpTy>(ifOp.getElseBody()->front());
2446 std::optional<EnableOp> lastThenEnableOp =
getLastEnableOp(thenControl),
2449 if (!lastThenEnableOp || !lastElseEnableOp)
2451 if (lastThenEnableOp->getGroupName() != lastElseEnableOp->getGroupName())
2457 rewriter.setInsertionPointAfter(ifOp);
2458 SeqOpTy seqOp = SeqOpTy::create(rewriter, ifOp.getLoc());
2459 Block *body = seqOp.getBodyBlock();
2460 rewriter.moveOpBefore(ifOp, body, body->end());
2461 rewriter.setInsertionPointToEnd(body);
2462 EnableOp::create(rewriter, seqOp.getLoc(), lastThenEnableOp->getGroupName());
2465 rewriter.eraseOp(*lastThenEnableOp);
2466 rewriter.eraseOp(*lastElseEnableOp);
2483template <
typename OpTy,
typename ParOpTy>
2485 PatternRewriter &rewriter) {
2486 static_assert(IsAny<OpTy, IfOp, StaticIfOp>(),
2487 "Should be an IfOp or StaticIfOp.");
2488 static_assert(IsAny<ParOpTy, ParOp, StaticParOp>(),
2489 "Branches should be checking for an ParOp or StaticParOp");
2492 auto thenControl = cast<ParOpTy>(controlOp.getThenBody()->front()),
2493 elseControl = cast<ParOpTy>(controlOp.getElseBody()->front());
2497 SmallVector<StringRef> groupNames;
2499 for (
auto [groupName, aEnable] : a) {
2500 auto bIndex = b.find(groupName);
2501 if (bIndex == b.end())
2504 groupNames.push_back(groupName.getValue());
2506 rewriter.eraseOp(aEnable);
2507 rewriter.eraseOp(bIndex->second);
2513 rewriter.setInsertionPointAfter(controlOp);
2515 ParOpTy parOp = ParOpTy::create(rewriter, controlOp.getLoc());
2516 Block *body = parOp.getBodyBlock();
2517 controlOp->remove();
2518 body->push_back(controlOp);
2521 rewriter.setInsertionPointToEnd(body);
2522 for (StringRef groupName : groupNames)
2523 EnableOp::create(rewriter, parOp.getLoc(), groupName);
2534 PatternRewriter &rewriter)
const override {
2535 if (!ifOp.getThenBody()->empty())
2537 if (ifOp.elseBodyExists() && !ifOp.getElseBody()->empty())
2546void IfOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2548 patterns.add<EmptyIfBody>(
context);
2556LogicalResult StaticIfOp::verify() {
2557 if (elseBodyExists()) {
2558 auto *elseBod = getElseBody();
2559 auto &elseOps = elseBod->getOperations();
2561 for (Operation &op : elseOps) {
2563 return op.emitOpError(
2564 "static if's else branch has non static control within it");
2569 auto *thenBod = getThenBody();
2570 auto &thenOps = thenBod->getOperations();
2571 for (Operation &op : thenOps) {
2574 return op.emitOpError(
2575 "static if's then branch has non static control within it");
2588 PatternRewriter &rewriter)
const override {
2589 if (!ifOp.getThenBody()->empty())
2591 if (ifOp.elseBodyExists() && !ifOp.getElseBody()->empty())
2600void StaticIfOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2602 patterns.add<EmptyStaticIfBody>(
context);
2610LogicalResult WhileOp::verify() {
2611 auto component = (*this)->getParentOfType<ComponentOp>();
2612 auto wiresOp = component.getWiresOp();
2614 std::optional<StringRef> optGroupName = getGroupName();
2615 if (!optGroupName) {
2619 StringRef groupName = *optGroupName;
2620 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(groupName);
2622 return emitOpError() <<
"with group '" << groupName
2623 <<
"', which does not exist.";
2625 if (isa<GroupOp>(groupOp))
2626 return emitOpError() <<
"with group '" << groupName
2627 <<
"', which is not a combinational group.";
2629 if (failed(groupOp.drivesPort(getCond())))
2630 return emitError() <<
"conditional op: '" <<
valueName(component, getCond())
2631 <<
"' expected to be driven from group: '" << groupName
2632 <<
"' but no driver was found.";
2637LogicalResult WhileOp::canonicalize(WhileOp whileOp,
2638 PatternRewriter &rewriter) {
2639 if (whileOp.getBodyBlock()->empty()) {
2650LogicalResult StaticRepeatOp::verify() {
2651 for (
auto &&bodyOp : (*this).getRegion().front()) {
2654 return bodyOp.emitOpError(
2655 "static repeat has non static control within it");
2662template <
typename OpTy>
2663static LogicalResult
zeroRepeat(OpTy op, PatternRewriter &rewriter) {
2664 static_assert(IsAny<OpTy, RepeatOp, StaticRepeatOp>(),
2665 "Should be a RepeatOp or StaticPRepeatOp");
2666 if (op.getCount() == 0) {
2667 rewriter.eraseOp(op);
2674void StaticRepeatOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2683void RepeatOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2696 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &ports,
2697 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &inputs,
2698 SmallVectorImpl<Attribute> &portNames,
2699 SmallVectorImpl<Attribute> &inputNames,
2700 SmallVectorImpl<Type> &types) {
2701 OpAsmParser::UnresolvedOperand port;
2702 OpAsmParser::UnresolvedOperand input;
2704 auto parseParameter = [&]() -> ParseResult {
2705 if (parser.parseOperand(port) || parser.parseEqual() ||
2706 parser.parseOperand(input))
2708 ports.push_back(port);
2709 portNames.push_back(StringAttr::get(parser.getContext(), port.name));
2710 inputs.push_back(input);
2711 inputNames.push_back(StringAttr::get(parser.getContext(), input.name));
2714 if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
2717 if (parser.parseArrow())
2720 if (parser.parseType(type))
2722 types.push_back(type);
2725 return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
2729ParseResult InvokeOp::parse(OpAsmParser &parser, OperationState &result) {
2730 StringAttr componentName;
2731 SmallVector<OpAsmParser::UnresolvedOperand, 4> ports;
2732 SmallVector<OpAsmParser::UnresolvedOperand, 4> inputs;
2733 SmallVector<Attribute> portNames;
2734 SmallVector<Attribute> inputNames;
2735 SmallVector<Type, 4> types;
2736 if (parser.parseSymbolName(componentName))
2738 FlatSymbolRefAttr callee = FlatSymbolRefAttr::get(componentName);
2739 SMLoc loc = parser.getCurrentLocation();
2741 SmallVector<Attribute, 4> refCells;
2742 if (succeeded(parser.parseOptionalLSquare())) {
2743 if (parser.parseCommaSeparatedList([&]() -> ParseResult {
2744 std::string refCellName;
2745 std::string externalMem;
2746 NamedAttrList refCellAttr;
2747 if (parser.parseKeywordOrString(&refCellName) ||
2748 parser.parseEqual() || parser.parseKeywordOrString(&externalMem))
2750 auto externalMemAttr =
2751 SymbolRefAttr::get(parser.getContext(), externalMem);
2752 refCellAttr.append(StringAttr::get(parser.getContext(), refCellName),
2755 DictionaryAttr::get(parser.getContext(), refCellAttr));
2758 parser.parseRSquare())
2761 result.addAttribute(
"refCellsMap",
2762 ArrayAttr::get(parser.getContext(), refCells));
2764 result.addAttribute(
"callee", callee);
2768 if (parser.resolveOperands(ports, types, loc, result.operands))
2770 if (parser.resolveOperands(inputs, types, loc, result.operands))
2772 result.addAttribute(
"portNames",
2773 ArrayAttr::get(parser.getContext(), portNames));
2774 result.addAttribute(
"inputNames",
2775 ArrayAttr::get(parser.getContext(), inputNames));
2779void InvokeOp::print(OpAsmPrinter &p) {
2780 p <<
" @" << getCallee() <<
"[";
2781 auto refCellNamesMap = getRefCellsMap();
2782 llvm::interleaveComma(refCellNamesMap, p, [&](Attribute attr) {
2783 auto dictAttr = cast<DictionaryAttr>(attr);
2784 llvm::interleaveComma(dictAttr, p, [&](NamedAttribute namedAttr) {
2785 auto refCellName = namedAttr.getName().str();
2787 cast<FlatSymbolRefAttr>(namedAttr.getValue()).getValue();
2788 p << refCellName <<
" = " << externalMem;
2793 auto ports = getPorts();
2794 auto inputs = getInputs();
2795 llvm::interleaveComma(llvm::zip(ports, inputs), p, [&](
auto arg) {
2796 p << std::get<0>(arg) <<
" = " << std::get<1>(arg);
2799 llvm::interleaveComma(ports, p, [&](
auto port) { p << port.getType(); });
2806 bool isDestination) {
2815 Operation *operation = value.getDefiningOp();
2816 if (operation ==
nullptr)
2818 if (
auto *dialect = operation->getDialect(); isa<comb::CombDialect>(dialect))
2825static Operation *
lookupCell(ComponentOp componentOp, StringRef name) {
2826 for (
auto cell : componentOp.getOps<CellInterface>())
2827 if (cell.instanceName() == name)
2833Value InvokeOp::getInstGoValue() {
2834 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2835 Operation *operation =
lookupCell(componentOp, getCallee());
2836 Value ret =
nullptr;
2837 llvm::TypeSwitch<Operation *>(operation)
2838 .Case<RegisterOp>([&](
auto op) { ret = operation->getResult(1); })
2839 .Case<MemoryOp, DivSPipeLibOp, DivUPipeLibOp, MultPipeLibOp,
2840 RemSPipeLibOp, RemUPipeLibOp>(
2841 [&](
auto op) { ret = operation->getResult(2); })
2842 .Case<InstanceOp>([&](
auto op) {
2843 auto portInfo = op.getReferencedComponent().getPortInfo();
2844 for (
auto [portInfo, res] :
2845 llvm::zip(portInfo, operation->getResults())) {
2846 if (portInfo.hasAttribute(
goPort))
2850 .Case<PrimitiveOp>([&](
auto op) {
2851 auto moduleExternOp = op.getReferencedPrimitive();
2852 auto argAttrs = moduleExternOp.getAllInputAttrs();
2853 for (
auto [attr, res] : llvm::zip(argAttrs, op.getResults())) {
2854 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2855 if (!dictAttr.empty()) {
2856 if (dictAttr.begin()->getName().getValue() ==
"calyx.go")
2866Value InvokeOp::getInstDoneValue() {
2867 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2868 Operation *operation =
lookupCell(componentOp, getCallee());
2869 Value ret =
nullptr;
2870 llvm::TypeSwitch<Operation *>(operation)
2871 .Case<RegisterOp, MemoryOp, DivSPipeLibOp, DivUPipeLibOp, MultPipeLibOp,
2872 RemSPipeLibOp, RemUPipeLibOp>([&](
auto op) {
2873 size_t doneIdx = operation->getResults().size() - 1;
2874 ret = operation->getResult(doneIdx);
2876 .Case<InstanceOp>([&](
auto op) {
2877 InstanceOp instanceOp = cast<InstanceOp>(operation);
2878 auto portInfo = instanceOp.getReferencedComponent().getPortInfo();
2879 for (
auto [portInfo, res] :
2880 llvm::zip(portInfo, operation->getResults())) {
2881 if (portInfo.hasAttribute(
donePort))
2885 .Case<PrimitiveOp>([&](
auto op) {
2886 PrimitiveOp primOp = cast<PrimitiveOp>(operation);
2887 auto moduleExternOp = primOp.getReferencedPrimitive();
2888 auto resAttrs = moduleExternOp.getAllOutputAttrs();
2889 for (
auto [attr, res] : llvm::zip(resAttrs, primOp.getResults())) {
2890 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2891 if (!dictAttr.empty()) {
2892 if (dictAttr.begin()->getName().getValue() ==
"calyx.done")
2907 std::string str = isGo ?
"calyx.go" :
"calyx.done";
2908 for (Attribute attr : moduleExternOp.getAllInputAttrs()) {
2909 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2910 ret = llvm::count_if(dictAttr, [&](NamedAttribute iter) {
2911 return iter.getName().getValue() == str;
2918LogicalResult InvokeOp::verify() {
2919 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2920 StringRef callee = getCallee();
2921 Operation *operation =
lookupCell(componentOp, callee);
2924 return emitOpError() <<
"with instance '@" << callee
2925 <<
"', which does not exist.";
2927 if (getInputs().
empty() && getRefCellsMap().
empty()) {
2928 return emitOpError() <<
"'@" << callee
2929 <<
"' has zero input and output port connections and "
2930 "has no passing-by-reference cells; "
2931 "expected at least one.";
2933 size_t goPortNum = 0, donePortNum = 0;
2936 llvm::TypeSwitch<Operation *>(operation)
2937 .Case<RegisterOp, DivSPipeLibOp, DivUPipeLibOp, MemoryOp, MultPipeLibOp,
2938 RemSPipeLibOp, RemUPipeLibOp>(
2939 [&](
auto op) { goPortNum = 1, donePortNum = 1; })
2940 .Case<InstanceOp>([&](
auto op) {
2941 auto portInfo = op.getReferencedComponent().getPortInfo();
2949 .Case<PrimitiveOp>([&](
auto op) {
2950 auto moduleExternOp = op.getReferencedPrimitive();
2956 if (goPortNum != 1 && donePortNum != 1)
2957 return emitOpError()
2958 <<
"'@" << callee <<
"'"
2959 <<
" is a combinational component and cannot be invoked, which must "
2960 "have single go port and single done port.";
2962 auto ports = getPorts();
2963 auto inputs = getInputs();
2965 Value goValue = getInstGoValue();
2966 Value doneValue = getInstDoneValue();
2967 for (
auto [port, input, portName, inputName] :
2968 llvm::zip(ports, inputs, getPortNames(), getInputNames())) {
2973 return emitOpError() <<
"'@" << callee <<
"' has input '"
2974 << cast<StringAttr>(portName).getValue()
2975 <<
"', which is a source port. The inputs are "
2976 "required to be destination ports.";
2978 if (port == goValue)
2979 return emitOpError() <<
"the go or write_en port of '@" << callee
2980 <<
"' cannot appear here.";
2983 return emitOpError() <<
"'@" << callee <<
"' has output '"
2984 << cast<StringAttr>(inputName).getValue()
2985 <<
"', which is a destination port. The inputs are "
2986 "required to be source ports.";
2988 return emitOpError() <<
"'@" << callee <<
"' has '"
2989 << cast<StringAttr>(inputName).getValue()
2990 <<
"', which is not a port or constant. Complex "
2991 "logic should be conducted in the guard.";
2992 if (input == doneValue)
2993 return emitOpError() <<
"the done port of '@" << callee
2994 <<
"' cannot appear here.";
2996 if (port.getDefiningOp() != operation && input.getDefiningOp() != operation)
2997 return emitOpError() <<
"the connection "
2998 << cast<StringAttr>(portName).getValue() <<
" = "
2999 << cast<StringAttr>(inputName).getValue()
3000 <<
" is not defined as an input port of '@" << callee
3010LogicalResult PadLibOp::verify() {
3011 unsigned inBits = getResult(0).getType().getIntOrFloatBitWidth();
3012 unsigned outBits = getResult(1).getType().getIntOrFloatBitWidth();
3013 if (inBits >= outBits)
3014 return emitOpError(
"expected input bits (")
3015 << inBits <<
')' <<
" to be less than output bits (" << outBits
3020LogicalResult SliceLibOp::verify() {
3021 unsigned inBits = getResult(0).getType().getIntOrFloatBitWidth();
3022 unsigned outBits = getResult(1).getType().getIntOrFloatBitWidth();
3023 if (inBits <= outBits)
3024 return emitOpError(
"expected input bits (")
3025 << inBits <<
')' <<
" to be greater than output bits (" << outBits
3030#define ImplBinPipeOpCellInterface(OpType, outName) \
3031 SmallVector<StringRef> OpType::portNames() { \
3032 return {clkPort, resetPort, goPort, "left", "right", outName, donePort}; \
3035 SmallVector<Direction> OpType::portDirections() { \
3036 return {Input, Input, Input, Input, Input, Output, Output}; \
3039 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
3040 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
3043 SmallVector<DictionaryAttr> OpType::portAttributes() { \
3044 MLIRContext *context = getContext(); \
3045 IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1); \
3046 NamedAttrList go, clk, reset, done; \
3047 go.append(goPort, isSet); \
3048 clk.append(clkPort, isSet); \
3049 reset.append(resetPort, isSet); \
3050 done.append(donePort, isSet); \
3052 clk.getDictionary(context), \
3053 reset.getDictionary(context), \
3054 go.getDictionary(context), \
3055 DictionaryAttr::get(context), \
3056 DictionaryAttr::get(context), \
3057 DictionaryAttr::get(context), \
3058 done.getDictionary(context) \
3062 bool OpType::isCombinational() { return false; }
3064#define ImplUnaryOpCellInterface(OpType) \
3065 SmallVector<StringRef> OpType::portNames() { return {"in", "out"}; } \
3066 SmallVector<Direction> OpType::portDirections() { return {Input, Output}; } \
3067 SmallVector<DictionaryAttr> OpType::portAttributes() { \
3068 return {DictionaryAttr::get(getContext()), \
3069 DictionaryAttr::get(getContext())}; \
3071 bool OpType::isCombinational() { return true; } \
3072 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
3073 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
3076#define ImplBinOpCellInterface(OpType) \
3077 SmallVector<StringRef> OpType::portNames() { \
3078 return {"left", "right", "out"}; \
3080 SmallVector<Direction> OpType::portDirections() { \
3081 return {Input, Input, Output}; \
3083 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
3084 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
3086 bool OpType::isCombinational() { return true; } \
3087 SmallVector<DictionaryAttr> OpType::portAttributes() { \
3088 return {DictionaryAttr::get(getContext()), \
3089 DictionaryAttr::get(getContext()), \
3090 DictionaryAttr::get(getContext())}; \
3134#include "circt/Dialect/Calyx/CalyxInterfaces.cpp.inc"
3137#define GET_OP_CLASSES
3138#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 Operation * lookupCell(ComponentOp componentOp, StringRef name)
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 void buildComponentLike(OpBuilder &builder, OperationState &result, StringAttr name, ArrayRef< PortInfo > ports, bool combinational)
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 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 { -> }...
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.
false return failure()
Parse an optional keyword or string and set instance into 'result'.
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.
mlir::FailureOr< mlir::Type > evaluateParametricType(mlir::Location loc, mlir::ArrayAttr parameters, mlir::Type type, bool emitErrors=true)
Returns a resolved version of 'type' wherein any parameter reference has been evaluated based on the ...
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
This pattern collapses a calyx.seq or calyx.par operation when it contains exactly one calyx....
LogicalResult matchAndRewrite(CtrlOp ctrlOp, PatternRewriter &rewriter) const override
This 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