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) {
579 bool isInput = port.direction == Direction::Input;
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() {
656 return getControlOrWiresFrom<WiresOp>(*
this);
659ControlOp calyx::ComponentOp::getControlOp() {
660 return getControlOrWiresFrom<ControlOp>(*
this);
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) {
712 printComponentInterface<ComponentOp>(p, *
this);
715ParseResult ComponentOp::parse(OpAsmParser &parser, OperationState &result) {
716 return parseComponentInterface<ComponentOp>(parser, 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) {
789 buildComponentLike<ComponentOp>(builder, result, name, 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) {
846 printComponentInterface<CombComponentOp>(p, *
this);
849ParseResult CombComponentOp::parse(OpAsmParser &parser,
850 OperationState &result) {
851 return parseComponentInterface<CombComponentOp>(parser, 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) {
901 buildComponentLike<CombComponentOp>(builder, result, name, 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,
933 patterns.add(collapseControl<SeqOp>);
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,
954 patterns.add(collapseControl<StaticSeqOp>);
955 patterns.add(emptyControl<StaticSeqOp>);
963LogicalResult ParOp::verify() {
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,
981 patterns.add(collapseControl<ParOp>);
990LogicalResult StaticParOp::verify() {
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,
1016 patterns.add(collapseControl<StaticParOp>);
1017 patterns.add(emptyControl<StaticParOp>);
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();
1224 return FloatingPointStandard::IEEE754;
1228 return FloatingPointStandard::IEEE754;
1232 return FloatingPointStandard::IEEE754;
1236 return FloatingPointStandard::IEEE754;
1240 return FloatingPointStandard::IEEE754;
1244 return FloatingPointStandard::IEEE754;
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());
1295 cell && cell.direction(port) == calyx::Direction::Output)
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());
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;
1786 auto replacedType = hw::evaluateParametricType(
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.";
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());
1843 switch (direction) {
1844 case hw::ModulePort::Direction::Input:
1845 return Direction::Input;
1846 case hw::ModulePort::Direction::Output:
1847 return Direction::Output;
1848 case hw::ModulePort::Direction::InOut:
1849 llvm_unreachable(
"InOut ports not supported by Calyx");
1851 llvm_unreachable(
"Impossible port type");
1854SmallVector<Direction> PrimitiveOp::portDirections() {
1855 SmallVector<Direction> portDirections;
1856 auto ports = getReferencedPrimitive().getPortList();
1859 return portDirections;
1862bool PrimitiveOp::isCombinational() {
return false; }
1868 DictionaryAttr dict) {
1872 llvm::SmallVector<NamedAttribute> attrs;
1873 for (NamedAttribute attr : dict) {
1874 Dialect *dialect = attr.getNameDialect();
1875 if (dialect ==
nullptr || !isa<CalyxDialect>(*dialect))
1877 StringRef name = attr.getName().strref();
1878 StringAttr newName = builder.getStringAttr(std::get<1>(name.split(
".")));
1879 attr.setName(newName);
1880 attrs.push_back(attr);
1882 return builder.getDictionaryAttr(attrs);
1886SmallVector<DictionaryAttr> PrimitiveOp::portAttributes() {
1887 SmallVector<DictionaryAttr> portAttributes;
1888 OpBuilder builder(getContext());
1890 auto argAttrs = prim.getAllInputAttrs();
1891 auto resAttrs = prim.getAllOutputAttrs();
1892 for (
auto a : argAttrs)
1893 portAttributes.push_back(
1895 for (
auto a : resAttrs)
1896 portAttributes.push_back(
1898 return portAttributes;
1907 SmallVector<Attribute> ¶meters) {
1909 return parser.parseCommaSeparatedList(
1910 OpAsmParser::Delimiter::OptionalLessGreater, [&]() {
1915 if (parser.parseKeywordOrString(&name) || parser.parseColonType(type))
1919 if (succeeded(parser.parseOptionalEqual())) {
1920 if (parser.parseAttribute(value, type))
1924 auto &builder = parser.getBuilder();
1925 parameters.push_back(hw::ParamDeclAttr::get(
1926 builder.getContext(), builder.getStringAttr(name), type, value));
1933 ArrayAttr ¶meters) {
1934 SmallVector<Attribute> parseParameters;
1938 parameters = ArrayAttr::get(parser.getContext(), parseParameters);
1945 ArrayAttr parameters) {
1946 if (parameters.empty())
1950 llvm::interleaveComma(parameters, p, [&](Attribute param) {
1951 auto paramAttr = cast<hw::ParamDeclAttr>(param);
1952 p << paramAttr.getName().getValue() <<
": " << paramAttr.getType();
1953 if (
auto value = paramAttr.getValue()) {
1955 p.printAttributeWithoutType(value);
1969 auto parent = (*this)->getParentOfType<GroupOp>();
1970 StringRef name = parent.getSymName();
1971 std::string resultName = name.str() +
".go";
1972 setNameFn(getResult(), resultName);
1975void GroupGoOp::print(OpAsmPrinter &p) {
printGroupPort(p, *
this); }
1977ParseResult GroupGoOp::parse(OpAsmParser &parser, OperationState &result) {
1981 result.addTypes(parser.getBuilder().getI1Type());
1989LogicalResult GroupDoneOp::verify() {
1990 Operation *srcOp = getSrc().getDefiningOp();
1991 Value optionalGuard = getGuard();
1992 Operation *guardOp = optionalGuard ? optionalGuard.getDefiningOp() :
nullptr;
1993 bool noGuard = (guardOp ==
nullptr);
1995 if (srcOp ==
nullptr)
1999 if (isa<hw::ConstantOp>(srcOp) && (noGuard || isa<hw::ConstantOp>(guardOp)))
2000 return emitOpError() <<
"with constant source"
2001 << (noGuard ?
"" :
" and constant guard")
2002 <<
". This should be a combinational group.";
2007void GroupDoneOp::print(OpAsmPrinter &p) {
printGroupPort(p, *
this); }
2009ParseResult GroupDoneOp::parse(OpAsmParser &parser, OperationState &result) {
2016void ConstantOp::getAsmResultNames(
2017 function_ref<
void(Value, StringRef)> setNameFn) {
2018 if (isa<FloatAttr>(getValue())) {
2019 setNameFn(getResult(),
"cst");
2022 auto intCst = llvm::dyn_cast<IntegerAttr>(getValue());
2023 auto intType = llvm::dyn_cast<IntegerType>(getType());
2026 if (intType && intType.getWidth() == 1)
2027 return setNameFn(getResult(), intCst.getInt() > 0 ?
"true" :
"false");
2030 SmallString<32> specialNameBuffer;
2031 llvm::raw_svector_ostream specialName(specialNameBuffer);
2032 specialName <<
'c' << intCst.getValue();
2034 specialName <<
'_' << getType();
2035 setNameFn(getResult(), specialName.str());
2038LogicalResult ConstantOp::verify() {
2039 auto type = getType();
2040 assert(isa<IntegerType>(type) &&
"must be an IntegerType");
2042 if (
auto valTyBitWidth = getValue().getType().getIntOrFloatBitWidth();
2043 valTyBitWidth != type.getIntOrFloatBitWidth()) {
2044 return emitOpError() <<
"value type bit width" << valTyBitWidth
2045 <<
" must match return type: "
2046 << type.getIntOrFloatBitWidth();
2049 if (llvm::isa<IntegerType>(type) &&
2050 !llvm::cast<IntegerType>(type).isSignless())
2051 return emitOpError(
"integer return type must be signless");
2053 if (!llvm::isa<IntegerAttr, FloatAttr>(getValue())) {
2054 return emitOpError(
"value must be an integer or float attribute");
2060OpFoldResult calyx::ConstantOp::fold(FoldAdaptor adaptor) {
2061 return getValueAttr();
2064void calyx::ConstantOp::build(OpBuilder &builder, OperationState &state,
2065 StringRef symName, Attribute attr, Type type) {
2066 state.addAttribute(ConstantOp::getSymNameAttrName(state.name),
2067 builder.getStringAttr(symName));
2068 state.addAttribute(
"value", attr);
2069 SmallVector<Type> types;
2070 types.push_back(type);
2071 state.addTypes(types);
2074SmallVector<StringRef> ConstantOp::portNames() {
return {
"out"}; }
2076SmallVector<Direction> ConstantOp::portDirections() {
return {
Output}; }
2078SmallVector<DictionaryAttr> ConstantOp::portAttributes() {
2079 return {DictionaryAttr::get(getContext())};
2082bool ConstantOp::isCombinational() {
return true; }
2093SmallVector<StringRef> RegisterOp::portNames() {
2097SmallVector<Direction> RegisterOp::portDirections() {
2101SmallVector<DictionaryAttr> RegisterOp::portAttributes() {
2102 MLIRContext *
context = getContext();
2103 IntegerAttr isSet = IntegerAttr::get(IntegerType::get(
context, 1), 1);
2104 NamedAttrList writeEn,
clk, reset, done;
2105 writeEn.append(
goPort, isSet);
2111 writeEn.getDictionary(
context),
2119bool RegisterOp::isCombinational() {
return false; }
2130SmallVector<StringRef> MemoryOp::portNames() {
2131 SmallVector<StringRef> portNames;
2132 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i) {
2134 StringAttr::get(this->getContext(),
"addr" + std::to_string(i));
2135 portNames.push_back(nameAttr.getValue());
2137 portNames.append({
"write_data",
"write_en",
clkPort,
"read_data",
donePort});
2141SmallVector<Direction> MemoryOp::portDirections() {
2142 SmallVector<Direction> portDirections;
2143 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2144 portDirections.push_back(
Input);
2146 return portDirections;
2149SmallVector<DictionaryAttr> MemoryOp::portAttributes() {
2150 SmallVector<DictionaryAttr> portAttributes;
2151 MLIRContext *
context = getContext();
2152 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2153 portAttributes.push_back(DictionaryAttr::get(
context));
2156 IntegerAttr isSet = IntegerAttr::get(IntegerType::get(
context, 1), 1);
2157 NamedAttrList writeEn,
clk, reset, done;
2158 writeEn.append(
goPort, isSet);
2161 portAttributes.append({DictionaryAttr::get(
context),
2162 writeEn.getDictionary(
context),
2167 return portAttributes;
2170void MemoryOp::build(OpBuilder &builder, OperationState &state,
2171 StringRef instanceName, int64_t width,
2172 ArrayRef<int64_t> sizes, ArrayRef<int64_t> addrSizes) {
2173 state.addAttribute(MemoryOp::getSymNameAttrName(state.name),
2174 builder.getStringAttr(instanceName));
2175 state.addAttribute(
"width", builder.getI64IntegerAttr(width));
2176 state.addAttribute(
"sizes", builder.getI64ArrayAttr(sizes));
2177 state.addAttribute(
"addrSizes", builder.getI64ArrayAttr(addrSizes));
2178 SmallVector<Type> types;
2179 for (int64_t size : addrSizes)
2180 types.push_back(builder.getIntegerType(size));
2181 types.push_back(builder.getIntegerType(width));
2182 types.push_back(builder.getI1Type());
2183 types.push_back(builder.getI1Type());
2184 types.push_back(builder.getIntegerType(width));
2185 types.push_back(builder.getI1Type());
2186 state.addTypes(types);
2189LogicalResult MemoryOp::verify() {
2190 ArrayRef<Attribute> opSizes = getSizes().getValue();
2191 ArrayRef<Attribute> opAddrSizes = getAddrSizes().getValue();
2192 size_t numDims = getSizes().size();
2193 size_t numAddrs = getAddrSizes().size();
2194 if (numDims != numAddrs)
2195 return emitOpError(
"mismatched number of dimensions (")
2196 << numDims <<
") and address sizes (" << numAddrs <<
")";
2198 size_t numExtraPorts = 5;
2199 if (getNumResults() != numAddrs + numExtraPorts)
2200 return emitOpError(
"incorrect number of address ports, expected ")
2203 for (
size_t i = 0; i < numDims; ++i) {
2204 int64_t size = cast<IntegerAttr>(opSizes[i]).getInt();
2205 int64_t addrSize = cast<IntegerAttr>(opAddrSizes[i]).getInt();
2206 if (llvm::Log2_64_Ceil(size) > addrSize)
2207 return emitOpError(
"address size (")
2208 << addrSize <<
") for dimension " << i
2209 <<
" can't address the entire range (" << size <<
")";
2224SmallVector<StringRef> SeqMemoryOp::portNames() {
2225 SmallVector<StringRef> portNames;
2226 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i) {
2228 StringAttr::get(this->getContext(),
"addr" + std::to_string(i));
2229 portNames.push_back(nameAttr.getValue());
2231 portNames.append({
clkPort,
"reset",
"content_en",
"write_en",
"write_data",
2232 "read_data",
"done"});
2236SmallVector<Direction> SeqMemoryOp::portDirections() {
2237 SmallVector<Direction> portDirections;
2238 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2239 portDirections.push_back(
Input);
2241 return portDirections;
2244SmallVector<DictionaryAttr> SeqMemoryOp::portAttributes() {
2245 SmallVector<DictionaryAttr> portAttributes;
2246 MLIRContext *
context = getContext();
2247 for (
size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
2248 portAttributes.push_back(DictionaryAttr::get(
context));
2252 IntegerAttr isSet = IntegerAttr::get(builder.getIndexType(), 1);
2253 IntegerAttr isTwo = IntegerAttr::get(builder.getIndexType(), 2);
2254 NamedAttrList done,
clk, reset, contentEn;
2258 contentEn.append(
goPort, isTwo);
2259 portAttributes.append({
clk.getDictionary(
context),
2261 contentEn.getDictionary(
context),
2267 return portAttributes;
2270void SeqMemoryOp::build(OpBuilder &builder, OperationState &state,
2271 StringRef instanceName, int64_t width,
2272 ArrayRef<int64_t> sizes, ArrayRef<int64_t> addrSizes) {
2273 state.addAttribute(SeqMemoryOp::getSymNameAttrName(state.name),
2274 builder.getStringAttr(instanceName));
2275 state.addAttribute(
"width", builder.getI64IntegerAttr(width));
2276 state.addAttribute(
"sizes", builder.getI64ArrayAttr(sizes));
2277 state.addAttribute(
"addrSizes", builder.getI64ArrayAttr(addrSizes));
2278 SmallVector<Type> types;
2279 for (int64_t size : addrSizes)
2280 types.push_back(builder.getIntegerType(size));
2281 types.push_back(builder.getI1Type());
2282 types.push_back(builder.getI1Type());
2283 types.push_back(builder.getI1Type());
2284 types.push_back(builder.getI1Type());
2285 types.push_back(builder.getIntegerType(width));
2286 types.push_back(builder.getIntegerType(width));
2287 types.push_back(builder.getI1Type());
2288 state.addTypes(types);
2291LogicalResult SeqMemoryOp::verify() {
2292 ArrayRef<Attribute> opSizes = getSizes().getValue();
2293 ArrayRef<Attribute> opAddrSizes = getAddrSizes().getValue();
2294 size_t numDims = getSizes().size();
2295 size_t numAddrs = getAddrSizes().size();
2296 if (numDims != numAddrs)
2297 return emitOpError(
"mismatched number of dimensions (")
2298 << numDims <<
") and address sizes (" << numAddrs <<
")";
2300 size_t numExtraPorts =
2302 if (getNumResults() != numAddrs + numExtraPorts)
2303 return emitOpError(
"incorrect number of address ports, expected ")
2306 for (
size_t i = 0; i < numDims; ++i) {
2307 int64_t size = cast<IntegerAttr>(opSizes[i]).getInt();
2308 int64_t addrSize = cast<IntegerAttr>(opAddrSizes[i]).getInt();
2309 if (llvm::Log2_64_Ceil(size) > addrSize)
2310 return emitOpError(
"address size (")
2311 << addrSize <<
") for dimension " << i
2312 <<
" can't address the entire range (" << size <<
")";
2321LogicalResult EnableOp::verify() {
2322 auto component = (*this)->getParentOfType<ComponentOp>();
2323 auto wiresOp = component.getWiresOp();
2324 StringRef name = getGroupName();
2326 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(name);
2328 return emitOpError() <<
"with group '" << name
2329 <<
"', which does not exist.";
2331 if (isa<CombGroupOp>(groupOp))
2332 return emitOpError() <<
"with group '" << name
2333 <<
"', which is a combinational group.";
2342LogicalResult IfOp::verify() {
2343 std::optional<StringRef> optGroupName = getGroupName();
2344 if (!optGroupName) {
2348 auto component = (*this)->getParentOfType<ComponentOp>();
2349 WiresOp wiresOp = component.getWiresOp();
2350 StringRef groupName = *optGroupName;
2351 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(groupName);
2353 return emitOpError() <<
"with group '" << groupName
2354 <<
"', which does not exist.";
2356 if (isa<GroupOp>(groupOp))
2357 return emitOpError() <<
"with group '" << groupName
2358 <<
"', which is not a combinational group.";
2360 if (failed(groupOp.drivesPort(getCond())))
2361 return emitError() <<
"with conditional op: '"
2363 <<
"' expected to be driven from group: '" << groupName
2364 <<
"' but no driver was found.";
2372template <
typename OpTy>
2374 static_assert(IsAny<OpTy, SeqOp, StaticSeqOp>(),
2375 "Should be a StaticSeqOp or SeqOp.");
2376 if (parent.getBodyBlock()->empty())
2377 return std::nullopt;
2378 auto &lastOp = parent.getBodyBlock()->back();
2379 if (
auto enableOp = dyn_cast<EnableOp>(lastOp))
2381 if (
auto seqOp = dyn_cast<SeqOp>(lastOp))
2383 if (
auto staticSeqOp = dyn_cast<StaticSeqOp>(lastOp))
2386 return std::nullopt;
2391template <
typename OpTy>
2394 static_assert(IsAny<OpTy, ParOp, StaticParOp>(),
2395 "Should be a StaticParOp or ParOp.");
2398 Block *body = parent.getBodyBlock();
2399 for (EnableOp op : body->getOps<EnableOp>())
2400 enables.insert(std::pair(op.getGroupNameAttr().getAttr(), op));
2412template <
typename IfOpTy,
typename TailOpTy>
2414 static_assert(IsAny<TailOpTy, SeqOp, ParOp, StaticSeqOp, StaticParOp>(),
2415 "Should be a SeqOp, ParOp, StaticSeqOp, or StaticParOp.");
2416 static_assert(IsAny<IfOpTy, IfOp, StaticIfOp>(),
2417 "Should be a IfOp or StaticIfOp.");
2419 if (!op.thenBodyExists() || !op.elseBodyExists())
2421 if (op.getThenBody()->empty() || op.getElseBody()->empty())
2424 Block *thenBody = op.getThenBody(), *elseBody = op.getElseBody();
2425 return isa<TailOpTy>(thenBody->front()) && isa<TailOpTy>(elseBody->front());
2436template <
typename IfOpTy,
typename SeqOpTy>
2438 PatternRewriter &rewriter) {
2439 static_assert(IsAny<IfOpTy, IfOp, StaticIfOp>(),
2440 "Should be an IfOp or StaticIfOp.");
2441 static_assert(IsAny<SeqOpTy, SeqOp, StaticSeqOp>(),
2442 "Branches should be checking for an SeqOp or StaticSeqOp");
2443 if (!hasCommonTailPatternPreConditions<IfOpTy, SeqOpTy>(ifOp))
2445 auto thenControl = cast<SeqOpTy>(ifOp.getThenBody()->front()),
2446 elseControl = cast<SeqOpTy>(ifOp.getElseBody()->front());
2448 std::optional<EnableOp> lastThenEnableOp =
getLastEnableOp(thenControl),
2451 if (!lastThenEnableOp || !lastElseEnableOp)
2453 if (lastThenEnableOp->getGroupName() != lastElseEnableOp->getGroupName())
2459 rewriter.setInsertionPointAfter(ifOp);
2460 SeqOpTy seqOp = SeqOpTy::create(rewriter, ifOp.getLoc());
2461 Block *body = seqOp.getBodyBlock();
2462 rewriter.moveOpBefore(ifOp, body, body->end());
2463 rewriter.setInsertionPointToEnd(body);
2464 EnableOp::create(rewriter, seqOp.getLoc(), lastThenEnableOp->getGroupName());
2467 rewriter.eraseOp(*lastThenEnableOp);
2468 rewriter.eraseOp(*lastElseEnableOp);
2485template <
typename OpTy,
typename ParOpTy>
2487 PatternRewriter &rewriter) {
2488 static_assert(IsAny<OpTy, IfOp, StaticIfOp>(),
2489 "Should be an IfOp or StaticIfOp.");
2490 static_assert(IsAny<ParOpTy, ParOp, StaticParOp>(),
2491 "Branches should be checking for an ParOp or StaticParOp");
2492 if (!hasCommonTailPatternPreConditions<OpTy, ParOpTy>(controlOp))
2494 auto thenControl = cast<ParOpTy>(controlOp.getThenBody()->front()),
2495 elseControl = cast<ParOpTy>(controlOp.getElseBody()->front());
2499 SmallVector<StringRef> groupNames;
2501 for (
auto [groupName, aEnable] : a) {
2502 auto bIndex = b.find(groupName);
2503 if (bIndex == b.end())
2506 groupNames.push_back(groupName.getValue());
2508 rewriter.eraseOp(aEnable);
2509 rewriter.eraseOp(bIndex->second);
2515 rewriter.setInsertionPointAfter(controlOp);
2517 ParOpTy parOp = ParOpTy::create(rewriter, controlOp.getLoc());
2518 Block *body = parOp.getBodyBlock();
2519 controlOp->remove();
2520 body->push_back(controlOp);
2523 rewriter.setInsertionPointToEnd(body);
2524 for (StringRef groupName : groupNames)
2525 EnableOp::create(rewriter, parOp.getLoc(), groupName);
2536 PatternRewriter &rewriter)
const override {
2537 if (!ifOp.getThenBody()->empty())
2539 if (ifOp.elseBodyExists() && !ifOp.getElseBody()->empty())
2548void IfOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
2551 patterns.add(commonTailPatternWithPar<IfOp, ParOp>);
2552 patterns.add(commonTailPatternWithSeq<IfOp, SeqOp>);
2558LogicalResult StaticIfOp::verify() {
2559 if (elseBodyExists()) {
2560 auto *elseBod = getElseBody();
2561 auto &elseOps = elseBod->getOperations();
2563 for (Operation &op : elseOps) {
2565 return op.emitOpError(
2566 "static if's else branch has non static control within it");
2571 auto *thenBod = getThenBody();
2572 auto &thenOps = thenBod->getOperations();
2573 for (Operation &op : thenOps) {
2576 return op.emitOpError(
2577 "static if's then branch has non static control within it");
2590 PatternRewriter &rewriter)
const override {
2591 if (!ifOp.getThenBody()->empty())
2593 if (ifOp.elseBodyExists() && !ifOp.getElseBody()->empty())
2602void StaticIfOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
2605 patterns.add(commonTailPatternWithPar<StaticIfOp, StaticParOp>);
2606 patterns.add(commonTailPatternWithSeq<StaticIfOp, StaticSeqOp>);
2612LogicalResult WhileOp::verify() {
2613 auto component = (*this)->getParentOfType<ComponentOp>();
2614 auto wiresOp = component.getWiresOp();
2616 std::optional<StringRef> optGroupName = getGroupName();
2617 if (!optGroupName) {
2621 StringRef groupName = *optGroupName;
2622 auto groupOp = wiresOp.lookupSymbol<GroupInterface>(groupName);
2624 return emitOpError() <<
"with group '" << groupName
2625 <<
"', which does not exist.";
2627 if (isa<GroupOp>(groupOp))
2628 return emitOpError() <<
"with group '" << groupName
2629 <<
"', which is not a combinational group.";
2631 if (failed(groupOp.drivesPort(getCond())))
2632 return emitError() <<
"conditional op: '" <<
valueName(component, getCond())
2633 <<
"' expected to be driven from group: '" << groupName
2634 <<
"' but no driver was found.";
2639LogicalResult WhileOp::canonicalize(
WhileOp whileOp,
2640 PatternRewriter &rewriter) {
2641 if (whileOp.getBodyBlock()->empty()) {
2652LogicalResult StaticRepeatOp::verify() {
2653 for (
auto &&bodyOp : (*this).getRegion().front()) {
2656 return bodyOp.emitOpError(
2657 "static repeat has non static control within it");
2664template <
typename OpTy>
2665static LogicalResult
zeroRepeat(OpTy op, PatternRewriter &rewriter) {
2666 static_assert(IsAny<OpTy, RepeatOp, StaticRepeatOp>(),
2667 "Should be a RepeatOp or StaticPRepeatOp");
2668 if (op.getCount() == 0) {
2669 rewriter.eraseOp(op);
2676void StaticRepeatOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
2678 patterns.add(emptyControl<StaticRepeatOp>);
2679 patterns.add(zeroRepeat<StaticRepeatOp>);
2685void RepeatOp::getCanonicalizationPatterns(RewritePatternSet &
patterns,
2687 patterns.add(emptyControl<RepeatOp>);
2688 patterns.add(zeroRepeat<RepeatOp>);
2698 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &ports,
2699 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &inputs,
2700 SmallVectorImpl<Attribute> &portNames,
2701 SmallVectorImpl<Attribute> &inputNames,
2702 SmallVectorImpl<Type> &types) {
2703 OpAsmParser::UnresolvedOperand port;
2704 OpAsmParser::UnresolvedOperand input;
2706 auto parseParameter = [&]() -> ParseResult {
2707 if (parser.parseOperand(port) || parser.parseEqual() ||
2708 parser.parseOperand(input))
2710 ports.push_back(port);
2711 portNames.push_back(StringAttr::get(parser.getContext(), port.name));
2712 inputs.push_back(input);
2713 inputNames.push_back(StringAttr::get(parser.getContext(), input.name));
2716 if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
2719 if (parser.parseArrow())
2722 if (parser.parseType(type))
2724 types.push_back(type);
2727 return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
2731ParseResult InvokeOp::parse(OpAsmParser &parser, OperationState &result) {
2732 StringAttr componentName;
2733 SmallVector<OpAsmParser::UnresolvedOperand, 4> ports;
2734 SmallVector<OpAsmParser::UnresolvedOperand, 4> inputs;
2735 SmallVector<Attribute> portNames;
2736 SmallVector<Attribute> inputNames;
2737 SmallVector<Type, 4> types;
2738 if (parser.parseSymbolName(componentName))
2740 FlatSymbolRefAttr callee = FlatSymbolRefAttr::get(componentName);
2741 SMLoc loc = parser.getCurrentLocation();
2743 SmallVector<Attribute, 4> refCells;
2744 if (succeeded(parser.parseOptionalLSquare())) {
2745 if (parser.parseCommaSeparatedList([&]() -> ParseResult {
2746 std::string refCellName;
2747 std::string externalMem;
2748 NamedAttrList refCellAttr;
2749 if (parser.parseKeywordOrString(&refCellName) ||
2750 parser.parseEqual() || parser.parseKeywordOrString(&externalMem))
2752 auto externalMemAttr =
2753 SymbolRefAttr::get(parser.getContext(), externalMem);
2754 refCellAttr.append(StringAttr::get(parser.getContext(), refCellName),
2757 DictionaryAttr::get(parser.getContext(), refCellAttr));
2760 parser.parseRSquare())
2763 result.addAttribute(
"refCellsMap",
2764 ArrayAttr::get(parser.getContext(), refCells));
2766 result.addAttribute(
"callee", callee);
2770 if (parser.resolveOperands(ports, types, loc, result.operands))
2772 if (parser.resolveOperands(inputs, types, loc, result.operands))
2774 result.addAttribute(
"portNames",
2775 ArrayAttr::get(parser.getContext(), portNames));
2776 result.addAttribute(
"inputNames",
2777 ArrayAttr::get(parser.getContext(), inputNames));
2781void InvokeOp::print(OpAsmPrinter &p) {
2782 p <<
" @" << getCallee() <<
"[";
2783 auto refCellNamesMap = getRefCellsMap();
2784 llvm::interleaveComma(refCellNamesMap, p, [&](Attribute attr) {
2785 auto dictAttr = cast<DictionaryAttr>(attr);
2786 llvm::interleaveComma(dictAttr, p, [&](NamedAttribute namedAttr) {
2787 auto refCellName = namedAttr.getName().str();
2789 cast<FlatSymbolRefAttr>(namedAttr.getValue()).getValue();
2790 p << refCellName <<
" = " << externalMem;
2795 auto ports = getPorts();
2796 auto inputs = getInputs();
2797 llvm::interleaveComma(llvm::zip(ports, inputs), p, [&](
auto arg) {
2798 p << std::get<0>(arg) <<
" = " << std::get<1>(arg);
2801 llvm::interleaveComma(ports, p, [&](
auto port) { p << port.getType(); });
2808 bool isDestination) {
2817 Operation *operation = value.getDefiningOp();
2818 if (operation ==
nullptr)
2820 if (
auto *dialect = operation->getDialect(); isa<comb::CombDialect>(dialect))
2827static Operation *
lookupCell(ComponentOp componentOp, StringRef name) {
2828 for (
auto cell : componentOp.getOps<CellInterface>())
2829 if (cell.instanceName() == name)
2835Value InvokeOp::getInstGoValue() {
2836 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2837 Operation *operation =
lookupCell(componentOp, getCallee());
2838 Value ret =
nullptr;
2839 llvm::TypeSwitch<Operation *>(operation)
2840 .Case<RegisterOp>([&](
auto op) { ret = operation->getResult(1); })
2841 .Case<MemoryOp, DivSPipeLibOp, DivUPipeLibOp, MultPipeLibOp,
2842 RemSPipeLibOp, RemUPipeLibOp>(
2843 [&](
auto op) { ret = operation->getResult(2); })
2844 .Case<InstanceOp>([&](
auto op) {
2845 auto portInfo = op.getReferencedComponent().getPortInfo();
2846 for (
auto [portInfo, res] :
2847 llvm::zip(portInfo, operation->getResults())) {
2848 if (portInfo.hasAttribute(
goPort))
2852 .Case<PrimitiveOp>([&](
auto op) {
2853 auto moduleExternOp = op.getReferencedPrimitive();
2854 auto argAttrs = moduleExternOp.getAllInputAttrs();
2855 for (
auto [attr, res] :
llvm::zip(argAttrs, op.getResults())) {
2856 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2857 if (!dictAttr.empty()) {
2858 if (dictAttr.begin()->getName().getValue() ==
"calyx.go")
2868Value InvokeOp::getInstDoneValue() {
2869 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2870 Operation *operation =
lookupCell(componentOp, getCallee());
2871 Value ret =
nullptr;
2872 llvm::TypeSwitch<Operation *>(operation)
2873 .Case<RegisterOp, MemoryOp, DivSPipeLibOp, DivUPipeLibOp, MultPipeLibOp,
2874 RemSPipeLibOp, RemUPipeLibOp>([&](
auto op) {
2875 size_t doneIdx = operation->getResults().size() - 1;
2876 ret = operation->getResult(doneIdx);
2878 .Case<InstanceOp>([&](
auto op) {
2879 InstanceOp instanceOp = cast<InstanceOp>(operation);
2880 auto portInfo = instanceOp.getReferencedComponent().getPortInfo();
2881 for (
auto [portInfo, res] :
2882 llvm::zip(portInfo, operation->getResults())) {
2883 if (portInfo.hasAttribute(
donePort))
2887 .Case<PrimitiveOp>([&](
auto op) {
2888 PrimitiveOp primOp = cast<PrimitiveOp>(operation);
2889 auto moduleExternOp = primOp.getReferencedPrimitive();
2890 auto resAttrs = moduleExternOp.getAllOutputAttrs();
2891 for (
auto [attr, res] :
llvm::zip(resAttrs, primOp.getResults())) {
2892 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2893 if (!dictAttr.empty()) {
2894 if (dictAttr.begin()->getName().getValue() ==
"calyx.done")
2909 std::string str = isGo ?
"calyx.go" :
"calyx.done";
2910 for (Attribute attr : moduleExternOp.getAllInputAttrs()) {
2911 if (DictionaryAttr dictAttr = dyn_cast<DictionaryAttr>(attr)) {
2912 ret = llvm::count_if(dictAttr, [&](NamedAttribute iter) {
2913 return iter.getName().getValue() == str;
2920LogicalResult InvokeOp::verify() {
2921 ComponentOp componentOp = (*this)->getParentOfType<ComponentOp>();
2922 StringRef callee = getCallee();
2923 Operation *operation =
lookupCell(componentOp, callee);
2926 return emitOpError() <<
"with instance '@" << callee
2927 <<
"', which does not exist.";
2929 if (getInputs().
empty() && getRefCellsMap().
empty()) {
2930 return emitOpError() <<
"'@" << callee
2931 <<
"' has zero input and output port connections and "
2932 "has no passing-by-reference cells; "
2933 "expected at least one.";
2935 size_t goPortNum = 0, donePortNum = 0;
2938 llvm::TypeSwitch<Operation *>(operation)
2939 .Case<RegisterOp, DivSPipeLibOp, DivUPipeLibOp, MemoryOp, MultPipeLibOp,
2940 RemSPipeLibOp, RemUPipeLibOp>(
2941 [&](
auto op) { goPortNum = 1, donePortNum = 1; })
2942 .Case<InstanceOp>([&](
auto op) {
2943 auto portInfo = op.getReferencedComponent().getPortInfo();
2951 .Case<PrimitiveOp>([&](
auto op) {
2952 auto moduleExternOp = op.getReferencedPrimitive();
2958 if (goPortNum != 1 && donePortNum != 1)
2959 return emitOpError()
2960 <<
"'@" << callee <<
"'"
2961 <<
" is a combinational component and cannot be invoked, which must "
2962 "have single go port and single done port.";
2964 auto ports = getPorts();
2965 auto inputs = getInputs();
2967 Value goValue = getInstGoValue();
2968 Value doneValue = getInstDoneValue();
2969 for (
auto [port, input, portName, inputName] :
2970 llvm::zip(ports, inputs, getPortNames(), getInputNames())) {
2975 return emitOpError() <<
"'@" << callee <<
"' has input '"
2976 << cast<StringAttr>(portName).getValue()
2977 <<
"', which is a source port. The inputs are "
2978 "required to be destination ports.";
2980 if (port == goValue)
2981 return emitOpError() <<
"the go or write_en port of '@" << callee
2982 <<
"' cannot appear here.";
2985 return emitOpError() <<
"'@" << callee <<
"' has output '"
2986 << cast<StringAttr>(inputName).getValue()
2987 <<
"', which is a destination port. The inputs are "
2988 "required to be source ports.";
2990 return emitOpError() <<
"'@" << callee <<
"' has '"
2991 << cast<StringAttr>(inputName).getValue()
2992 <<
"', which is not a port or constant. Complex "
2993 "logic should be conducted in the guard.";
2994 if (input == doneValue)
2995 return emitOpError() <<
"the done port of '@" << callee
2996 <<
"' cannot appear here.";
2998 if (port.getDefiningOp() != operation && input.getDefiningOp() != operation)
2999 return emitOpError() <<
"the connection "
3000 << cast<StringAttr>(portName).getValue() <<
" = "
3001 << cast<StringAttr>(inputName).getValue()
3002 <<
" is not defined as an input port of '@" << callee
3012LogicalResult PadLibOp::verify() {
3013 unsigned inBits = getResult(0).getType().getIntOrFloatBitWidth();
3014 unsigned outBits = getResult(1).getType().getIntOrFloatBitWidth();
3015 if (inBits >= outBits)
3016 return emitOpError(
"expected input bits (")
3017 << inBits <<
')' <<
" to be less than output bits (" << outBits
3022LogicalResult SliceLibOp::verify() {
3023 unsigned inBits = getResult(0).getType().getIntOrFloatBitWidth();
3024 unsigned outBits = getResult(1).getType().getIntOrFloatBitWidth();
3025 if (inBits <= outBits)
3026 return emitOpError(
"expected input bits (")
3027 << inBits <<
')' <<
" to be greater than output bits (" << outBits
3032#define ImplBinPipeOpCellInterface(OpType, outName) \
3033 SmallVector<StringRef> OpType::portNames() { \
3034 return {clkPort, resetPort, goPort, "left", "right", outName, donePort}; \
3037 SmallVector<Direction> OpType::portDirections() { \
3038 return {Input, Input, Input, Input, Input, Output, Output}; \
3041 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
3042 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
3045 SmallVector<DictionaryAttr> OpType::portAttributes() { \
3046 MLIRContext *context = getContext(); \
3047 IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1); \
3048 NamedAttrList go, clk, reset, done; \
3049 go.append(goPort, isSet); \
3050 clk.append(clkPort, isSet); \
3051 reset.append(resetPort, isSet); \
3052 done.append(donePort, isSet); \
3054 clk.getDictionary(context), \
3055 reset.getDictionary(context), \
3056 go.getDictionary(context), \
3057 DictionaryAttr::get(context), \
3058 DictionaryAttr::get(context), \
3059 DictionaryAttr::get(context), \
3060 done.getDictionary(context) \
3064 bool OpType::isCombinational() { return false; }
3066#define ImplUnaryOpCellInterface(OpType) \
3067 SmallVector<StringRef> OpType::portNames() { return {"in", "out"}; } \
3068 SmallVector<Direction> OpType::portDirections() { return {Input, Output}; } \
3069 SmallVector<DictionaryAttr> OpType::portAttributes() { \
3070 return {DictionaryAttr::get(getContext()), \
3071 DictionaryAttr::get(getContext())}; \
3073 bool OpType::isCombinational() { return true; } \
3074 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
3075 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
3078#define ImplBinOpCellInterface(OpType) \
3079 SmallVector<StringRef> OpType::portNames() { \
3080 return {"left", "right", "out"}; \
3082 SmallVector<Direction> OpType::portDirections() { \
3083 return {Input, Input, Output}; \
3085 void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
3086 getCellAsmResultNames(setNameFn, *this, this->portNames()); \
3088 bool OpType::isCombinational() { return true; } \
3089 SmallVector<DictionaryAttr> OpType::portAttributes() { \
3090 return {DictionaryAttr::get(getContext()), \
3091 DictionaryAttr::get(getContext()), \
3092 DictionaryAttr::get(getContext())}; \
3136#include "circt/Dialect/Calyx/CalyxInterfaces.cpp.inc"
3139#define GET_OP_CLASSES
3140#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 { -> } 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.