20 #include "mlir/IR/BuiltinOps.h"
21 #include "mlir/IR/BuiltinTypes.h"
22 #include "mlir/Tools/mlir-translate/Translation.h"
23 #include "llvm/ADT/SmallSet.h"
24 #include "llvm/ADT/TypeSwitch.h"
25 #include "llvm/Support/Casting.h"
26 #include "llvm/Support/FormatVariadic.h"
30 using namespace circt;
31 using namespace calyx;
36 static constexpr std::string_view LSquare() {
return "["; }
37 static constexpr std::string_view RSquare() {
return "]"; }
38 static constexpr std::string_view LAngleBracket() {
return "<"; }
39 static constexpr std::string_view RAngleBracket() {
return ">"; }
40 static constexpr std::string_view LParen() {
return "("; }
41 static constexpr std::string_view RParen() {
return ")"; }
42 static constexpr std::string_view colon() {
return ": "; }
43 static constexpr std::string_view
space() {
return " "; }
44 static constexpr std::string_view period() {
return "."; }
45 static constexpr std::string_view questionMark() {
return " ? "; }
46 static constexpr std::string_view exclamationMark() {
return "!"; }
47 static constexpr std::string_view equals() {
return "="; }
48 static constexpr std::string_view comma() {
return ", "; }
49 static constexpr std::string_view arrow() {
return " -> "; }
50 static constexpr std::string_view quote() {
return "\""; }
51 static constexpr std::string_view apostrophe() {
return "'"; }
52 static constexpr std::string_view LBraceEndL() {
return "{\n"; }
53 static constexpr std::string_view RBraceEndL() {
return "}\n"; }
54 static constexpr std::string_view semicolonEndL() {
return ";\n"; }
55 static constexpr std::string_view addressSymbol() {
return "@"; }
56 static constexpr std::string_view endl() {
return "\n"; }
57 static constexpr std::string_view metadataLBrace() {
return "#{\n"; }
58 static constexpr std::string_view metadataRBrace() {
return "}#\n"; }
61 constexpr std::array<StringRef, 7> integerAttributes{
62 "external",
"static",
"share",
"bound",
63 "write_together",
"read_together",
"pos",
67 constexpr std::array<StringRef, 12> booleanAttributes{
68 "clk",
"reset",
"go",
"done",
"generated",
"precious",
69 "toplevel",
"stable",
"nointerface",
"inline",
"state_share",
"data",
72 static std::optional<StringRef> getCalyxAttrIdentifier(NamedAttribute attr) {
73 StringRef identifier = attr.getName().strref();
74 if (identifier.contains(
".")) {
75 Dialect *dialect = attr.getNameDialect();
76 if (dialect !=
nullptr && isa<CalyxDialect>(*dialect)) {
77 return std::get<1>(identifier.split(
"."));
86 static bool isValidCalyxAttribute(StringRef identifier) {
88 return llvm::find(integerAttributes, identifier) != integerAttributes.end() ||
89 llvm::find(booleanAttributes, identifier) != booleanAttributes.end();
94 struct ImportTracker {
98 FailureOr<llvm::SmallSet<StringRef, 4>> getLibraryNames(ModuleOp module) {
99 auto walkRes = module.walk([&](ComponentOp component) {
100 for (
auto &op : *component.getBodyBlock()) {
101 if (!isa<CellInterface>(op) || isa<InstanceOp, PrimitiveOp>(op))
104 auto libraryName = getLibraryFor(&op);
105 if (failed(libraryName))
106 return WalkResult::interrupt();
107 usedLibraries.insert(*libraryName);
109 return WalkResult::advance();
111 if (walkRes.wasInterrupted())
113 return usedLibraries;
118 FailureOr<StringRef> getLibraryFor(Operation *op) {
119 return TypeSwitch<Operation *, FailureOr<StringRef>>(op)
120 .Case<AddLibOp, RegisterOp, UndefLibOp, WireLibOp>(
121 [&](
auto op) -> FailureOr<StringRef> {
122 static constexpr std::string_view sCompile =
"compile";
125 .Case<NotLibOp, AndLibOp, OrLibOp, XorLibOp, SubLibOp, GtLibOp, LtLibOp,
126 EqLibOp, NeqLibOp, GeLibOp, LeLibOp, LshLibOp, RshLibOp,
127 SliceLibOp, PadLibOp, MuxLibOp>(
128 [&](
auto op) -> FailureOr<StringRef> {
129 static constexpr std::string_view sCore =
"core";
132 .Case<SgtLibOp, SltLibOp, SeqLibOp, SneqLibOp, SgeLibOp, SleLibOp,
133 SrshLibOp, MultPipeLibOp, RemUPipeLibOp, RemSPipeLibOp,
134 DivUPipeLibOp, DivSPipeLibOp, ExtSILibOp>(
135 [&](
auto op) -> FailureOr<StringRef> {
136 static constexpr std::string_view sBinaryOperators =
138 return {sBinaryOperators};
140 .Case<MemoryOp>([&](
auto op) -> FailureOr<StringRef> {
141 static constexpr std::string_view sMemories =
"memories/comb";
144 .Case<SeqMemoryOp>([&](
auto op) -> FailureOr<StringRef> {
145 static constexpr std::string_view sMemories =
"memories/seq";
148 .Case<ConstantOp>([&](
auto op) -> FailureOr<StringRef> {
149 static constexpr std::string_view sFloat =
"float";
152 .Case<AddFOpIEEE754>([&](
auto op) -> FailureOr<StringRef> {
153 static constexpr std::string_view sFloatingPoint =
"float/addFN";
154 return {sFloatingPoint};
156 .Case<MulFOpIEEE754>([&](
auto op) -> FailureOr<StringRef> {
157 static constexpr std::string_view sFloatingPoint =
"float/mulFN";
158 return {sFloatingPoint};
160 .Case<CompareFOpIEEE754>([&](
auto op) -> FailureOr<StringRef> {
161 static constexpr std::string_view sFloatingPoint =
"float/compareFN";
162 return {sFloatingPoint};
164 .Default([&](
auto op) {
165 auto diag = op->emitOpError() <<
"not supported for emission";
180 Emitter(llvm::raw_ostream &os) : os(os) {}
181 LogicalResult finalize();
184 raw_ostream &indent() {
return os.indent(currentIndent); }
185 void addIndent() { currentIndent += 2; }
186 void reduceIndent() {
187 assert(currentIndent >= 2 &&
"Unintended indentation wrap");
192 void emitModule(ModuleOp op);
195 void emitCiderMetadata(mlir::ModuleOp op) {
196 auto metadata = op->getAttrOfType<ArrayAttr>(
"calyx.metadata");
200 constexpr std::string_view metadataIdentifier =
"metadata";
201 os << endl() << metadataIdentifier <<
space() << metadataLBrace();
203 for (
auto sourceLoc : llvm::enumerate(metadata)) {
205 os << std::to_string(sourceLoc.index()) << colon();
206 os << cast<StringAttr>(sourceLoc.value()).getValue() << endl();
209 os << metadataRBrace();
213 LogicalResult emitImports(ModuleOp op) {
214 auto emitImport = [&](StringRef library) {
217 os <<
"import " << quote() <<
"primitives/" << library << period()
218 <<
"futil" << quote() << semicolonEndL();
221 auto libraryNames = importTracker.getLibraryNames(op);
222 if (failed(libraryNames))
225 for (StringRef library : *libraryNames)
232 void emitComponent(ComponentInterface op);
233 void emitComponentPorts(ComponentInterface op);
240 void emitInstance(InstanceOp op);
243 void emitPrimitive(PrimitiveOp op);
246 void emitWires(WiresOp op);
249 void emitGroup(GroupInterface group);
252 void emitControl(ControlOp control);
255 void emitAssignment(AssignOp op);
258 void emitEnable(EnableOp enable);
261 void emitRegister(RegisterOp
reg);
264 void emitUndef(UndefLibOp op);
267 void emitMemory(MemoryOp memory);
270 void emitSeqMemory(SeqMemoryOp memory);
273 void emitInvoke(InvokeOp invoke);
276 void emitConstant(ConstantOp constant);
284 void emitLibraryPrimTypedByAllPorts(Operation *op);
292 void emitLibraryPrimTypedByFirstInputPort(Operation *op);
300 void emitLibraryPrimTypedByFirstOutputPort(
301 Operation *op, std::optional<StringRef> calyxLibName = {});
304 void emitLibraryFloatingPoint(Operation *op);
308 ImportTracker importTracker;
311 InFlightDiagnostic emitError(Operation *op,
const Twine &message) {
312 encounteredError =
true;
313 return op->emitError(message);
317 InFlightDiagnostic emitOpError(Operation *op,
const Twine &message) {
318 encounteredError =
true;
319 return op->emitOpError(message);
332 std::string getAttribute(Operation *op, NamedAttribute attr,
bool isPort,
335 std::optional<StringRef> identifierOpt = getCalyxAttrIdentifier(attr);
337 if (!identifierOpt.has_value())
340 StringRef identifier = *identifierOpt;
342 if (!isValidCalyxAttribute(identifier))
346 llvm::raw_string_ostream buffer(output);
347 buffer.reserveExtraSpace(32);
349 bool isBooleanAttribute =
350 llvm::find(booleanAttributes, identifier) != booleanAttributes.end();
352 if (isa<UnitAttr>(attr.getValue())) {
353 assert(isBooleanAttribute &&
354 "Non-boolean attributes must provide an integer value.");
356 buffer << quote() << identifier << quote() << equals() <<
"1";
358 buffer << addressSymbol() << identifier;
360 }
else if (
auto intAttr = dyn_cast<IntegerAttr>(attr.getValue())) {
361 APInt value = intAttr.getValue();
363 buffer << quote() << identifier << quote() << equals() << value;
365 buffer << addressSymbol() << identifier;
368 if (!isBooleanAttribute || intAttr.getValue() != 1) {
370 SmallVector<char, 4> s;
371 value.toStringUnsigned(s, 10);
372 buffer << LParen() << s << RParen();
381 std::string getAttributes(Operation *op,
bool atFormat,
382 DictionaryAttr attributes =
nullptr) {
383 bool isPort = attributes !=
nullptr;
384 bool atLeastOne =
false;
387 attributes = op->getAttrDictionary();
389 std::string calyxAttributes;
390 llvm::raw_string_ostream buf(calyxAttributes);
393 buf << LAngleBracket();
395 for (
auto &attr : attributes) {
397 if (
auto out = getAttribute(op, attr,
isPort, atFormat); !out.empty()) {
400 buf << (atFormat ?
space() : comma());
405 auto out = buf.str();
408 out.append(atFormat ?
space() : RAngleBracket());
420 template <
typename Func>
421 void emitCalyxBody(Func emitBody) {
422 os <<
space() << LBraceEndL();
426 indent() << RBraceEndL();
430 template <
typename Func>
431 void emitCalyxSection(StringRef sectionName, Func emitBody,
432 StringRef symbolName =
"") {
433 indent() << sectionName;
434 if (!symbolName.empty())
435 os <<
space() << symbolName;
436 emitCalyxBody(emitBody);
440 template <
typename CombinationalOp>
441 void emitCombinationalValue(CombinationalOp op, StringRef logicalSymbol) {
442 auto inputs = op.getInputs();
444 for (
size_t i = 0, e = inputs.size(); i != e; ++i) {
445 emitValue(inputs[i],
false);
453 void emitCycleValue(CycleOp op) {
455 if (op.getEnd().has_value()) {
457 os << op.getStart() <<
":" << op.getEnd();
465 void emitValue(Value value,
bool isIndented) {
466 if (
auto blockArg = dyn_cast<BlockArgument>(value)) {
469 (isIndented ? indent() : os) << portName.getValue();
473 auto *definingOp = value.getDefiningOp();
474 assert(definingOp &&
"Value does not have a defining operation.");
476 TypeSwitch<Operation *>(definingOp)
477 .Case<CellInterface>([&](
auto cell) {
479 (isIndented ? indent() : os)
480 << cell.instanceName() << period() << cell.portName(value);
482 .Case<hw::ConstantOp>([&](
auto op) {
485 APInt value = op.getValue();
487 (isIndented ? indent() : os)
488 << std::to_string(value.
getBitWidth()) << apostrophe() <<
"d";
490 value.print(os,
false);
492 .Case<comb::AndOp>([&](
auto op) { emitCombinationalValue(op,
"&"); })
493 .Case<comb::OrOp>([&](
auto op) { emitCombinationalValue(op,
"|"); })
494 .Case<comb::XorOp>([&](
auto op) {
497 if (!op.isBinaryNot()) {
498 emitOpError(op,
"Only supporting Binary Not for XOR.");
503 os << exclamationMark();
504 emitValue(op.getInputs()[0],
false);
506 .Case<CycleOp>([&](
auto op) { emitCycleValue(op); })
508 [&](
auto op) { emitOpError(op,
"not supported for emission"); });
512 template <
typename OpTy>
513 void emitGroupPort(GroupInterface group, OpTy op, StringRef portHole) {
514 assert((isa<GroupGoOp>(op) || isa<GroupDoneOp>(op)) &&
515 "Required to be a group port.");
516 indent() << group.symName().getValue() << LSquare() << portHole << RSquare()
519 emitValue(op.getGuard(),
false);
520 os << questionMark();
522 emitValue(op.getSrc(),
false);
523 os << semicolonEndL();
527 void emitCalyxControl(Block *body) {
528 Operation *parent = body->getParentOp();
530 "This should only be used to emit Calyx Control structures.");
534 if (
auto enable = dyn_cast<EnableOp>(parent)) {
540 auto prependAttributes = [&](Operation *op, StringRef sym) {
541 return (getAttributes(op,
true) + sym).str();
544 for (
auto &&op : *body) {
546 TypeSwitch<Operation *>(&op)
547 .Case<SeqOp>([&](
auto op) {
548 emitCalyxSection(prependAttributes(op,
"seq"),
549 [&]() { emitCalyxControl(op.getBodyBlock()); });
551 .Case<StaticSeqOp>([&](
auto op) {
552 emitCalyxSection(prependAttributes(op,
"static seq"),
553 [&]() { emitCalyxControl(op.getBodyBlock()); });
555 .Case<ParOp>([&](
auto op) {
556 emitCalyxSection(prependAttributes(op,
"par"),
557 [&]() { emitCalyxControl(op.getBodyBlock()); });
559 .Case<WhileOp>([&](
auto op) {
560 indent() << prependAttributes(op,
"while ");
561 emitValue(op.getCond(),
false);
563 if (
auto groupName = op.getGroupName())
564 os <<
" with " << *groupName;
566 emitCalyxBody([&]() { emitCalyxControl(op.getBodyBlock()); });
568 .Case<IfOp>([&](
auto op) {
569 indent() << prependAttributes(op,
"if ");
570 emitValue(op.getCond(),
false);
572 if (
auto groupName = op.getGroupName())
573 os <<
" with " << *groupName;
575 emitCalyxBody([&]() { emitCalyxControl(op.getThenBody()); });
576 if (op.elseBodyExists())
577 emitCalyxSection(
"else",
578 [&]() { emitCalyxControl(op.getElseBody()); });
580 .Case<StaticIfOp>([&](
auto op) {
581 indent() << prependAttributes(op,
"static if ");
582 emitValue(op.getCond(),
false);
584 emitCalyxBody([&]() { emitCalyxControl(op.getThenBody()); });
585 if (op.elseBodyExists())
586 emitCalyxSection(
"else",
587 [&]() { emitCalyxControl(op.getElseBody()); });
589 .Case<RepeatOp>([&](
auto op) {
590 indent() << prependAttributes(op,
"repeat ");
593 emitCalyxBody([&]() { emitCalyxControl(op.getBodyBlock()); });
595 .Case<StaticRepeatOp>([&](
auto op) {
596 indent() << prependAttributes(op,
"static repeat ");
599 emitCalyxBody([&]() { emitCalyxControl(op.getBodyBlock()); });
601 .Case<StaticParOp>([&](
auto op) {
602 emitCalyxSection(prependAttributes(op,
"static par"),
603 [&]() { emitCalyxControl(op.getBodyBlock()); });
605 .Case<EnableOp>([&](
auto op) { emitEnable(op); })
606 .Case<InvokeOp>([&](
auto op) { emitInvoke(op); })
607 .Default([&](
auto op) {
608 emitOpError(op,
"not supported for emission inside control.");
614 llvm::raw_ostream &os;
617 bool encounteredError =
false;
621 unsigned currentIndent = 0;
626 LogicalResult Emitter::finalize() {
return failure(encounteredError); }
629 void Emitter::emitModule(ModuleOp op) {
630 for (
auto &bodyOp : *op.getBody()) {
631 if (
auto componentOp = dyn_cast<ComponentInterface>(bodyOp))
632 emitComponent(componentOp);
633 else if (
auto hwModuleExternOp = dyn_cast<hw::HWModuleExternOp>(bodyOp))
634 emitPrimitiveExtern(hwModuleExternOp);
636 emitOpError(&bodyOp,
"Unexpected op");
641 void Emitter::emitComponent(ComponentInterface op) {
642 std::string combinationalPrefix = op.isComb() ?
"comb " :
"";
644 indent() << combinationalPrefix <<
"component " << op.getName()
645 << getAttributes(op,
false,
nullptr);
647 emitComponentPorts(op);
648 os <<
space() << LBraceEndL();
654 emitCalyxSection(
"cells", [&]() {
655 for (
auto &&bodyOp : *op.getBodyBlock()) {
656 TypeSwitch<Operation *>(&bodyOp)
657 .Case<UndefLibOp>([&](auto op) { emitUndef(op); })
658 .Case<WiresOp>([&](
auto op) { wires = op; })
659 .Case<ControlOp>([&](
auto op) { control = op; })
660 .Case<InstanceOp>([&](
auto op) { emitInstance(op); })
661 .Case<PrimitiveOp>([&](
auto op) { emitPrimitive(op); })
662 .Case<RegisterOp>([&](
auto op) { emitRegister(op); })
663 .Case<MemoryOp>([&](
auto op) { emitMemory(op); })
664 .Case<SeqMemoryOp>([&](
auto op) { emitSeqMemory(op); })
665 .Case<hw::ConstantOp>([&](
auto op) { })
666 .Case<calyx::ConstantOp>([&](
auto op) { emitConstant(op); })
667 .Case<SliceLibOp, PadLibOp, ExtSILibOp>(
668 [&](
auto op) { emitLibraryPrimTypedByAllPorts(op); })
669 .Case<LtLibOp, GtLibOp, EqLibOp, NeqLibOp, GeLibOp, LeLibOp, SltLibOp,
670 SgtLibOp, SeqLibOp, SneqLibOp, SgeLibOp, SleLibOp, AddLibOp,
671 SubLibOp, ShruLibOp, RshLibOp, SrshLibOp, LshLibOp, AndLibOp,
672 NotLibOp, OrLibOp, XorLibOp, WireLibOp>(
673 [&](
auto op) { emitLibraryPrimTypedByFirstInputPort(op); })
675 [&](
auto op) { emitLibraryPrimTypedByFirstOutputPort(op); })
676 .Case<MultPipeLibOp>(
677 [&](
auto op) { emitLibraryPrimTypedByFirstOutputPort(op); })
678 .Case<RemUPipeLibOp, DivUPipeLibOp>([&](
auto op) {
679 emitLibraryPrimTypedByFirstOutputPort(
680 op, {
"std_div_pipe"});
682 .Case<RemSPipeLibOp, DivSPipeLibOp>([&](
auto op) {
683 emitLibraryPrimTypedByFirstOutputPort(
684 op, {
"std_sdiv_pipe"});
686 .Case<AddFOpIEEE754, MulFOpIEEE754, CompareFOpIEEE754>(
687 [&](
auto op) { emitLibraryFloatingPoint(op); })
688 .Default([&](
auto op) {
689 emitOpError(op,
"not supported for emission inside component");
695 emitControl(control);
701 void Emitter::emitComponentPorts(ComponentInterface op) {
702 auto emitPorts = [&](
auto ports) {
704 for (
size_t i = 0, e = ports.size(); i < e; ++i) {
708 unsigned int bitWidth = port.
type.getIntOrFloatBitWidth();
709 os << getAttributes(op,
true, port.
attributes)
710 << port.
name.getValue() << colon() << bitWidth;
717 emitPorts(op.getInputPortInfo());
719 emitPorts(op.getOutputPortInfo());
724 Attribute filename = op->getAttrDictionary().get(
"filename");
725 indent() <<
"extern " << filename <<
space() << LBraceEndL();
727 indent() <<
"primitive " << op.getName();
729 if (!op.getParameters().empty()) {
731 llvm::interleaveComma(op.getParameters(), os, [&](Attribute param) {
732 auto paramAttr = cast<hw::ParamDeclAttr>(param);
733 os << paramAttr.getName().str();
737 os << getAttributes(op,
false);
739 emitPrimitivePorts(op);
740 os << semicolonEndL();
747 auto emitPorts = [&](
auto ports,
bool isInput) {
748 auto e =
static_cast<size_t>(std::distance(ports.begin(), ports.end()));
750 auto type = op.getHWModuleType();
751 for (
auto [i, port] : llvm::enumerate(ports)) {
752 DictionaryAttr portAttr = cast_or_null<DictionaryAttr>(
753 op.getPortAttrs(isInput ? type.getPortIdForInputId(i)
754 : type.getPortIdForOutputId(i)));
756 os << getAttributes(op,
true, portAttr)
757 << port.
name.getValue() << colon();
761 hw::ParamDeclRefAttr bitWidth = dyn_cast<hw::ParamDeclRefAttr>(
762 cast<hw::IntType>(port.
type).getWidth());
763 os << bitWidth.getName().str();
765 unsigned int bitWidth = port.
type.getIntOrFloatBitWidth();
774 hw::ModulePortInfo ports(op.getPortList());
775 emitPorts(ports.getInputs(),
true);
777 emitPorts(ports.getOutputs(),
false);
780 void Emitter::emitInstance(InstanceOp op) {
781 indent() << getAttributes(op,
true) << op.instanceName()
782 <<
space() << equals() <<
space() << op.getComponentName()
783 << LParen() << RParen() << semicolonEndL();
786 void Emitter::emitPrimitive(PrimitiveOp op) {
787 indent() << getAttributes(op,
true) << op.instanceName()
788 <<
space() << equals() <<
space() << op.getPrimitiveName()
791 if (op.getParameters().has_value()) {
792 llvm::interleaveComma(*op.getParameters(), os, [&](Attribute param) {
793 auto paramAttr = cast<hw::ParamDeclAttr>(param);
794 auto value = paramAttr.getValue();
795 if (auto intAttr = dyn_cast<IntegerAttr>(value)) {
796 os << intAttr.getInt();
797 }
else if (
auto fpAttr = dyn_cast<FloatAttr>(value)) {
798 os << fpAttr.getValue().convertToFloat();
800 llvm_unreachable(
"Primitive parameter type not supported");
805 os << RParen() << semicolonEndL();
808 void Emitter::emitRegister(RegisterOp
reg) {
809 size_t bitWidth =
reg.getIn().getType().getIntOrFloatBitWidth();
810 indent() << getAttributes(
reg,
true) <<
reg.instanceName()
811 <<
space() << equals() <<
space() <<
"std_reg" << LParen()
812 << std::to_string(bitWidth) << RParen() << semicolonEndL();
815 void Emitter::emitUndef(UndefLibOp op) {
816 size_t bitwidth = op.getOut().getType().getIntOrFloatBitWidth();
817 indent() << getAttributes(op,
true) << op.instanceName()
818 <<
space() << equals() <<
space() <<
"undef" << LParen()
819 << std::to_string(bitwidth) << RParen() << semicolonEndL();
822 void Emitter::emitMemory(MemoryOp memory) {
823 size_t dimension = memory.getSizes().size();
824 if (dimension < 1 || dimension > 4) {
825 emitOpError(memory,
"Only memories with dimensionality in range [1, 4] are "
826 "supported by the native Calyx compiler.");
829 indent() << getAttributes(memory,
true) << memory.instanceName()
830 <<
space() << equals() <<
space() <<
"std_mem_d"
831 << std::to_string(dimension) << LParen() << memory.getWidth()
833 for (Attribute size : memory.getSizes()) {
834 APInt memSize = cast<IntegerAttr>(size).getValue();
835 memSize.print(os,
false);
839 ArrayAttr addrSizes = memory.getAddrSizes();
840 for (
size_t i = 0, e = addrSizes.size(); i != e; ++i) {
841 APInt addrSize = cast<IntegerAttr>(addrSizes[i]).getValue();
842 addrSize.print(os,
false);
847 os << RParen() << semicolonEndL();
850 void Emitter::emitSeqMemory(SeqMemoryOp memory) {
851 size_t dimension = memory.getSizes().size();
852 if (dimension < 1 || dimension > 4) {
853 emitOpError(memory,
"Only memories with dimensionality in range [1, 4] are "
854 "supported by the native Calyx compiler.");
857 bool isRef = !memory->hasAttr(
"external");
861 os << getAttributes(memory,
true) << memory.instanceName()
862 <<
space() << equals() <<
space() <<
"seq_mem_d"
863 << std::to_string(dimension) << LParen() << memory.getWidth() << comma();
864 for (Attribute size : memory.getSizes()) {
865 APInt memSize = cast<IntegerAttr>(size).getValue();
866 memSize.print(os,
false);
870 ArrayAttr addrSizes = memory.getAddrSizes();
871 for (
size_t i = 0, e = addrSizes.size(); i != e; ++i) {
872 APInt addrSize = cast<IntegerAttr>(addrSizes[i]).getValue();
873 addrSize.print(os,
false);
878 os << RParen() << semicolonEndL();
881 void Emitter::emitInvoke(InvokeOp invoke) {
882 StringRef callee = invoke.getCallee();
883 indent() <<
"invoke " << callee;
884 auto refCellsMap = invoke.getRefCellsMap();
885 if (!refCellsMap.empty()) {
887 llvm::interleaveComma(refCellsMap, os, [&](Attribute attr) {
888 auto dictAttr = cast<DictionaryAttr>(attr);
889 llvm::interleaveComma(dictAttr, os, [&](NamedAttribute namedAttr) {
890 auto refCellName = namedAttr.getName().str();
892 cast<FlatSymbolRefAttr>(namedAttr.getValue()).getValue();
893 os << refCellName <<
" = " << externalMem;
898 ArrayAttr portNames = invoke.getPortNames();
899 ArrayAttr inputNames = invoke.getInputNames();
903 llvm::StringMap<std::string> inputsMap;
904 llvm::StringMap<std::string> outputsMap;
905 for (
auto [portNameAttr, inputNameAttr, input] :
906 llvm::zip(portNames, inputNames, invoke.getInputs())) {
907 StringRef portName = cast<StringAttr>(portNameAttr).getValue();
908 StringRef inputName = cast<StringAttr>(inputNameAttr).getValue();
916 StringRef inputMapKey = portName.drop_front(2 + callee.size());
917 if (portName.substr(1, callee.size()) == callee) {
919 if (isa_and_nonnull<hw::ConstantOp>(input.getDefiningOp())) {
920 hw::ConstantOp constant = cast<hw::ConstantOp>(input.getDefiningOp());
921 APInt value = constant.getValue();
922 std::string mapValue = std::to_string(value.getBitWidth()) +
923 apostrophe().data() +
"d" +
924 std::to_string(value.getZExtValue());
925 inputsMap[inputMapKey] = mapValue;
928 inputsMap[inputMapKey] = inputName.drop_front(1).str();
929 }
else if (inputName.substr(1, callee.size()) == callee)
930 outputsMap[inputName.drop_front(2 + callee.size())] =
931 portName.drop_front(1).str();
935 llvm::interleaveComma(inputsMap, os, [&](
const auto &iter) {
936 os << iter.getKey() <<
" = " << iter.getValue();
941 llvm::interleaveComma(outputsMap, os, [&](
const auto &iter) {
942 os << iter.getKey() <<
" = " << iter.getValue();
944 os << RParen() << semicolonEndL();
947 void Emitter::emitConstant(ConstantOp constantOp) {
948 TypedAttr attr = constantOp.getValueAttr();
949 assert(isa<FloatAttr>(attr) &&
"must be a floating point constant");
950 auto fltAttr = cast<FloatAttr>(attr);
951 APFloat value = fltAttr.getValue();
952 auto type = cast<FloatType>(fltAttr.getType());
953 double doubleValue = value.convertToDouble();
954 auto floatBits = value.getSizeInBits(type.getFloatSemantics());
955 indent() << constantOp.getName().str() <<
space() << equals() <<
space()
956 <<
"std_float_const";
959 static constexpr int32_t
IEEE754 = 0;
960 os << LParen() << std::to_string(IEEE754) << comma() << floatBits << comma()
961 << std::to_string(doubleValue) << RParen() << semicolonEndL();
969 void Emitter::emitLibraryPrimTypedByAllPorts(Operation *op) {
970 auto cell = cast<CellInterface>(op);
971 indent() << getAttributes(op,
true) << cell.instanceName()
974 llvm::interleaveComma(op->getResults(), os, [&](
auto res) {
975 os << std::to_string(res.getType().getIntOrFloatBitWidth());
977 os << RParen() << semicolonEndL();
980 void Emitter::emitLibraryPrimTypedByFirstInputPort(Operation *op) {
981 auto cell = cast<CellInterface>(op);
982 unsigned bitWidth = cell.getInputPorts()[0].getType().getIntOrFloatBitWidth();
983 StringRef opName = op->getName().getStringRef();
984 indent() << getAttributes(op,
true) << cell.instanceName()
986 << LParen() << bitWidth << RParen() << semicolonEndL();
989 void Emitter::emitLibraryPrimTypedByFirstOutputPort(
990 Operation *op, std::optional<StringRef> calyxLibName) {
991 auto cell = cast<CellInterface>(op);
993 cell.getOutputPorts()[0].getType().getIntOrFloatBitWidth();
994 StringRef opName = op->getName().getStringRef();
995 indent() << getAttributes(op,
true) << cell.instanceName()
998 << LParen() << bitWidth << RParen() << semicolonEndL();
1001 void Emitter::emitLibraryFloatingPoint(Operation *op) {
1002 auto cell = cast<CellInterface>(op);
1004 size_t inputPortIndex = cell.getInputPorts().size() - 3;
1006 cell.getInputPorts()[inputPortIndex].getType().getIntOrFloatBitWidth();
1010 unsigned expWidth, sigWidth;
1029 op->emitError(
"The supported bitwidths are 16, 32, 64, and 128");
1034 if (
auto fpOp = dyn_cast<calyx::FloatingPointOpInterface>(op)) {
1035 opName = fpOp.getCalyxLibraryName();
1037 indent() << getAttributes(op,
true) << cell.instanceName()
1038 <<
space() << equals() <<
space() << opName << LParen() << expWidth
1039 << comma() << sigWidth << comma() << bitWidth << RParen()
1043 void Emitter::emitAssignment(AssignOp op) {
1045 emitValue(op.getDest(),
true);
1047 if (op.getGuard()) {
1048 emitValue(op.getGuard(),
false);
1049 os << questionMark();
1051 emitValue(op.getSrc(),
false);
1052 os << semicolonEndL();
1055 void Emitter::emitWires(WiresOp op) {
1056 emitCalyxSection(
"wires", [&]() {
1057 for (
auto &&bodyOp : *op.getBodyBlock()) {
1058 TypeSwitch<Operation *>(&bodyOp)
1059 .Case<GroupInterface>([&](auto op) { emitGroup(op); })
1060 .Case<AssignOp>([&](
auto op) { emitAssignment(op); })
1063 .Default([&](
auto op) {
1064 emitOpError(op,
"not supported for emission inside wires section");
1070 void Emitter::emitGroup(GroupInterface group) {
1071 auto emitGroupBody = [&]() {
1072 for (
auto &&bodyOp : *group.getBody()) {
1073 TypeSwitch<Operation *>(&bodyOp)
1074 .Case<AssignOp>([&](
auto op) { emitAssignment(op); })
1075 .Case<GroupDoneOp>([&](
auto op) { emitGroupPort(group, op,
"done"); })
1076 .Case<GroupGoOp>([&](
auto op) { emitGroupPort(group, op,
"go"); })
1077 .Case<hw::ConstantOp, comb::AndOp, comb::OrOp, comb::XorOp, CycleOp>(
1079 .Default([&](
auto op) {
1080 emitOpError(op,
"not supported for emission inside group.");
1085 if (isa<StaticGroupOp>(group)) {
1086 auto staticGroup = cast<StaticGroupOp>(group);
1087 prefix = llvm::formatv(
"static<{0}> group", staticGroup.getLatency());
1089 prefix = isa<CombGroupOp>(group) ?
"comb group" :
"group";
1092 (group.symName().getValue() + getAttributes(group,
false))
1094 emitCalyxSection(prefix, emitGroupBody, groupHeader);
1097 void Emitter::emitEnable(EnableOp enable) {
1098 indent() << getAttributes(enable,
true) << enable.getGroupName()
1102 void Emitter::emitControl(ControlOp control) {
1104 if (control ==
nullptr)
1106 emitCalyxSection(
"control",
1107 [&]() { emitCalyxControl(control.getBodyBlock()); });
1116 llvm::raw_ostream &os) {
1117 Emitter emitter(os);
1118 if (failed(emitter.emitImports(module)))
1120 emitter.emitModule(module);
1121 emitter.emitCiderMetadata(module);
1122 return emitter.finalize();
1126 static mlir::TranslateFromMLIRRegistration toCalyx(
1127 "export-calyx",
"export Calyx",
1128 [](ModuleOp module, llvm::raw_ostream &os) {
1131 [](mlir::DialectRegistry ®istry) {
1133 .insert<calyx::CalyxDialect, comb::CombDialect, hw::HWDialect>();
assert(baseType &&"element must be base type")
static StringRef removeCalyxPrefix(StringRef s)
Calling getName() on a calyx operation will return "calyx.${opname}".
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.
Signals that the following operation is "control-like.".
PortInfo getPortInfo(BlockArgument arg)
Returns port information for the block argument provided.
void registerToCalyxTranslation()
mlir::LogicalResult exportCalyx(mlir::ModuleOp module, llvm::raw_ostream &os)
std::optional< int64_t > getBitWidth(FIRRTLBaseType type, bool ignoreFlip=false)
bool isParametricType(mlir::Type t)
Returns true if any part of t is parametric.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
This holds information about the port for either a Component or Cell.
DictionaryAttr attributes