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<AddFNOp>([&](
auto op) -> FailureOr<StringRef> {
153 static constexpr std::string_view sFloatingPoint =
"float/addFN";
154 return {sFloatingPoint};
156 .Default([&](
auto op) {
157 auto diag = op->emitOpError() <<
"not supported for emission";
172 Emitter(llvm::raw_ostream &os) : os(os) {}
173 LogicalResult finalize();
176 raw_ostream &indent() {
return os.indent(currentIndent); }
177 void addIndent() { currentIndent += 2; }
178 void reduceIndent() {
179 assert(currentIndent >= 2 &&
"Unintended indentation wrap");
184 void emitModule(ModuleOp op);
187 void emitCiderMetadata(mlir::ModuleOp op) {
188 auto metadata = op->getAttrOfType<ArrayAttr>(
"calyx.metadata");
192 constexpr std::string_view metadataIdentifier =
"metadata";
193 os << endl() << metadataIdentifier <<
space() << metadataLBrace();
195 for (
auto sourceLoc : llvm::enumerate(metadata)) {
197 os << std::to_string(sourceLoc.index()) << colon();
198 os << cast<StringAttr>(sourceLoc.value()).getValue() << endl();
201 os << metadataRBrace();
205 LogicalResult emitImports(ModuleOp op) {
206 auto emitImport = [&](StringRef library) {
209 os <<
"import " << quote() <<
"primitives/" << library << period()
210 <<
"futil" << quote() << semicolonEndL();
213 auto libraryNames = importTracker.getLibraryNames(op);
214 if (failed(libraryNames))
217 for (StringRef library : *libraryNames)
224 void emitComponent(ComponentInterface op);
225 void emitComponentPorts(ComponentInterface op);
232 void emitInstance(InstanceOp op);
235 void emitPrimitive(PrimitiveOp op);
238 void emitWires(WiresOp op);
241 void emitGroup(GroupInterface group);
244 void emitControl(ControlOp control);
247 void emitAssignment(AssignOp op);
250 void emitEnable(EnableOp enable);
253 void emitRegister(RegisterOp
reg);
256 void emitUndef(UndefLibOp op);
259 void emitMemory(MemoryOp memory);
262 void emitSeqMemory(SeqMemoryOp memory);
265 void emitInvoke(InvokeOp invoke);
268 void emitConstant(ConstantOp constant);
276 void emitLibraryPrimTypedByAllPorts(Operation *op);
284 void emitLibraryPrimTypedByFirstInputPort(Operation *op);
292 void emitLibraryPrimTypedByFirstOutputPort(
293 Operation *op, std::optional<StringRef> calyxLibName = {});
296 void emitLibraryFloatingPoint(Operation *op);
300 ImportTracker importTracker;
303 InFlightDiagnostic emitError(Operation *op,
const Twine &message) {
304 encounteredError =
true;
305 return op->emitError(message);
309 InFlightDiagnostic emitOpError(Operation *op,
const Twine &message) {
310 encounteredError =
true;
311 return op->emitOpError(message);
324 std::string getAttribute(Operation *op, NamedAttribute attr,
bool isPort,
327 std::optional<StringRef> identifierOpt = getCalyxAttrIdentifier(attr);
329 if (!identifierOpt.has_value())
332 StringRef identifier = *identifierOpt;
334 if (!isValidCalyxAttribute(identifier))
338 llvm::raw_string_ostream buffer(output);
339 buffer.reserveExtraSpace(32);
341 bool isBooleanAttribute =
342 llvm::find(booleanAttributes, identifier) != booleanAttributes.end();
344 if (isa<UnitAttr>(attr.getValue())) {
345 assert(isBooleanAttribute &&
346 "Non-boolean attributes must provide an integer value.");
348 buffer << quote() << identifier << quote() << equals() <<
"1";
350 buffer << addressSymbol() << identifier;
352 }
else if (
auto intAttr = dyn_cast<IntegerAttr>(attr.getValue())) {
353 APInt value = intAttr.getValue();
355 buffer << quote() << identifier << quote() << equals() << value;
357 buffer << addressSymbol() << identifier;
360 if (!isBooleanAttribute || intAttr.getValue() != 1) {
362 SmallVector<char, 4> s;
363 value.toStringUnsigned(s, 10);
364 buffer << LParen() << s << RParen();
373 std::string getAttributes(Operation *op,
bool atFormat,
374 DictionaryAttr attributes =
nullptr) {
375 bool isPort = attributes !=
nullptr;
376 bool atLeastOne =
false;
379 attributes = op->getAttrDictionary();
381 std::string calyxAttributes;
382 llvm::raw_string_ostream buf(calyxAttributes);
385 buf << LAngleBracket();
387 for (
auto &attr : attributes) {
389 if (
auto out = getAttribute(op, attr,
isPort, atFormat); !out.empty()) {
392 buf << (atFormat ?
space() : comma());
397 auto out = buf.str();
400 out.append(atFormat ?
space() : RAngleBracket());
412 template <
typename Func>
413 void emitCalyxBody(Func emitBody) {
414 os <<
space() << LBraceEndL();
418 indent() << RBraceEndL();
422 template <
typename Func>
423 void emitCalyxSection(StringRef sectionName, Func emitBody,
424 StringRef symbolName =
"") {
425 indent() << sectionName;
426 if (!symbolName.empty())
427 os <<
space() << symbolName;
428 emitCalyxBody(emitBody);
432 template <
typename CombinationalOp>
433 void emitCombinationalValue(CombinationalOp op, StringRef logicalSymbol) {
434 auto inputs = op.getInputs();
436 for (
size_t i = 0, e = inputs.size(); i != e; ++i) {
437 emitValue(inputs[i],
false);
445 void emitCycleValue(CycleOp op) {
447 if (op.getEnd().has_value()) {
449 os << op.getStart() <<
":" << op.getEnd();
457 void emitValue(Value value,
bool isIndented) {
458 if (
auto blockArg = dyn_cast<BlockArgument>(value)) {
461 (isIndented ? indent() : os) << portName.getValue();
465 auto *definingOp = value.getDefiningOp();
466 assert(definingOp &&
"Value does not have a defining operation.");
468 TypeSwitch<Operation *>(definingOp)
469 .Case<CellInterface>([&](
auto cell) {
471 (isIndented ? indent() : os)
472 << cell.instanceName() << period() << cell.portName(value);
474 .Case<hw::ConstantOp>([&](
auto op) {
477 APInt value = op.getValue();
479 (isIndented ? indent() : os)
480 << std::to_string(value.
getBitWidth()) << apostrophe() <<
"d";
482 value.print(os,
false);
484 .Case<comb::AndOp>([&](
auto op) { emitCombinationalValue(op,
"&"); })
485 .Case<comb::OrOp>([&](
auto op) { emitCombinationalValue(op,
"|"); })
486 .Case<comb::XorOp>([&](
auto op) {
489 if (!op.isBinaryNot()) {
490 emitOpError(op,
"Only supporting Binary Not for XOR.");
495 os << exclamationMark();
496 emitValue(op.getInputs()[0],
false);
498 .Case<CycleOp>([&](
auto op) { emitCycleValue(op); })
500 [&](
auto op) { emitOpError(op,
"not supported for emission"); });
504 template <
typename OpTy>
505 void emitGroupPort(GroupInterface group, OpTy op, StringRef portHole) {
506 assert((isa<GroupGoOp>(op) || isa<GroupDoneOp>(op)) &&
507 "Required to be a group port.");
508 indent() << group.symName().getValue() << LSquare() << portHole << RSquare()
511 emitValue(op.getGuard(),
false);
512 os << questionMark();
514 emitValue(op.getSrc(),
false);
515 os << semicolonEndL();
519 void emitCalyxControl(Block *body) {
520 Operation *parent = body->getParentOp();
522 "This should only be used to emit Calyx Control structures.");
526 if (
auto enable = dyn_cast<EnableOp>(parent)) {
532 auto prependAttributes = [&](Operation *op, StringRef sym) {
533 return (getAttributes(op,
true) + sym).str();
536 for (
auto &&op : *body) {
538 TypeSwitch<Operation *>(&op)
539 .Case<SeqOp>([&](
auto op) {
540 emitCalyxSection(prependAttributes(op,
"seq"),
541 [&]() { emitCalyxControl(op.getBodyBlock()); });
543 .Case<StaticSeqOp>([&](
auto op) {
544 emitCalyxSection(prependAttributes(op,
"static seq"),
545 [&]() { emitCalyxControl(op.getBodyBlock()); });
547 .Case<ParOp>([&](
auto op) {
548 emitCalyxSection(prependAttributes(op,
"par"),
549 [&]() { emitCalyxControl(op.getBodyBlock()); });
551 .Case<WhileOp>([&](
auto op) {
552 indent() << prependAttributes(op,
"while ");
553 emitValue(op.getCond(),
false);
555 if (
auto groupName = op.getGroupName())
556 os <<
" with " << *groupName;
558 emitCalyxBody([&]() { emitCalyxControl(op.getBodyBlock()); });
560 .Case<IfOp>([&](
auto op) {
561 indent() << prependAttributes(op,
"if ");
562 emitValue(op.getCond(),
false);
564 if (
auto groupName = op.getGroupName())
565 os <<
" with " << *groupName;
567 emitCalyxBody([&]() { emitCalyxControl(op.getThenBody()); });
568 if (op.elseBodyExists())
569 emitCalyxSection(
"else",
570 [&]() { emitCalyxControl(op.getElseBody()); });
572 .Case<StaticIfOp>([&](
auto op) {
573 indent() << prependAttributes(op,
"static if ");
574 emitValue(op.getCond(),
false);
576 emitCalyxBody([&]() { emitCalyxControl(op.getThenBody()); });
577 if (op.elseBodyExists())
578 emitCalyxSection(
"else",
579 [&]() { emitCalyxControl(op.getElseBody()); });
581 .Case<RepeatOp>([&](
auto op) {
582 indent() << prependAttributes(op,
"repeat ");
585 emitCalyxBody([&]() { emitCalyxControl(op.getBodyBlock()); });
587 .Case<StaticRepeatOp>([&](
auto op) {
588 indent() << prependAttributes(op,
"static repeat ");
591 emitCalyxBody([&]() { emitCalyxControl(op.getBodyBlock()); });
593 .Case<StaticParOp>([&](
auto op) {
594 emitCalyxSection(prependAttributes(op,
"static par"),
595 [&]() { emitCalyxControl(op.getBodyBlock()); });
597 .Case<EnableOp>([&](
auto op) { emitEnable(op); })
598 .Case<InvokeOp>([&](
auto op) { emitInvoke(op); })
599 .Default([&](
auto op) {
600 emitOpError(op,
"not supported for emission inside control.");
606 llvm::raw_ostream &os;
609 bool encounteredError =
false;
613 unsigned currentIndent = 0;
618 LogicalResult Emitter::finalize() {
return failure(encounteredError); }
621 void Emitter::emitModule(ModuleOp op) {
622 for (
auto &bodyOp : *op.getBody()) {
623 if (
auto componentOp = dyn_cast<ComponentInterface>(bodyOp))
624 emitComponent(componentOp);
625 else if (
auto hwModuleExternOp = dyn_cast<hw::HWModuleExternOp>(bodyOp))
626 emitPrimitiveExtern(hwModuleExternOp);
628 emitOpError(&bodyOp,
"Unexpected op");
633 void Emitter::emitComponent(ComponentInterface op) {
634 std::string combinationalPrefix = op.isComb() ?
"comb " :
"";
636 indent() << combinationalPrefix <<
"component " << op.getName()
637 << getAttributes(op,
false,
nullptr);
639 emitComponentPorts(op);
640 os <<
space() << LBraceEndL();
646 emitCalyxSection(
"cells", [&]() {
647 for (
auto &&bodyOp : *op.getBodyBlock()) {
648 TypeSwitch<Operation *>(&bodyOp)
649 .Case<UndefLibOp>([&](auto op) { emitUndef(op); })
650 .Case<WiresOp>([&](
auto op) { wires = op; })
651 .Case<ControlOp>([&](
auto op) { control = op; })
652 .Case<InstanceOp>([&](
auto op) { emitInstance(op); })
653 .Case<PrimitiveOp>([&](
auto op) { emitPrimitive(op); })
654 .Case<RegisterOp>([&](
auto op) { emitRegister(op); })
655 .Case<MemoryOp>([&](
auto op) { emitMemory(op); })
656 .Case<SeqMemoryOp>([&](
auto op) { emitSeqMemory(op); })
657 .Case<hw::ConstantOp>([&](
auto op) { })
658 .Case<calyx::ConstantOp>([&](
auto op) { emitConstant(op); })
659 .Case<SliceLibOp, PadLibOp, ExtSILibOp>(
660 [&](
auto op) { emitLibraryPrimTypedByAllPorts(op); })
661 .Case<LtLibOp, GtLibOp, EqLibOp, NeqLibOp, GeLibOp, LeLibOp, SltLibOp,
662 SgtLibOp, SeqLibOp, SneqLibOp, SgeLibOp, SleLibOp, AddLibOp,
663 SubLibOp, ShruLibOp, RshLibOp, SrshLibOp, LshLibOp, AndLibOp,
664 NotLibOp, OrLibOp, XorLibOp, WireLibOp>(
665 [&](
auto op) { emitLibraryPrimTypedByFirstInputPort(op); })
667 [&](
auto op) { emitLibraryPrimTypedByFirstOutputPort(op); })
668 .Case<MultPipeLibOp>(
669 [&](
auto op) { emitLibraryPrimTypedByFirstOutputPort(op); })
670 .Case<RemUPipeLibOp, DivUPipeLibOp>([&](
auto op) {
671 emitLibraryPrimTypedByFirstOutputPort(
672 op, {
"std_div_pipe"});
674 .Case<RemSPipeLibOp, DivSPipeLibOp>([&](
auto op) {
675 emitLibraryPrimTypedByFirstOutputPort(
676 op, {
"std_sdiv_pipe"});
678 .Case<AddFNOp>([&](
auto op) { emitLibraryFloatingPoint(op); })
679 .Default([&](
auto op) {
680 emitOpError(op,
"not supported for emission inside component");
686 emitControl(control);
692 void Emitter::emitComponentPorts(ComponentInterface op) {
693 auto emitPorts = [&](
auto ports) {
695 for (
size_t i = 0, e = ports.size(); i < e; ++i) {
699 unsigned int bitWidth = port.
type.getIntOrFloatBitWidth();
700 os << getAttributes(op,
true, port.
attributes)
701 << port.
name.getValue() << colon() << bitWidth;
708 emitPorts(op.getInputPortInfo());
710 emitPorts(op.getOutputPortInfo());
715 Attribute filename = op->getAttrDictionary().get(
"filename");
716 indent() <<
"extern " << filename <<
space() << LBraceEndL();
718 indent() <<
"primitive " << op.getName();
720 if (!op.getParameters().empty()) {
722 llvm::interleaveComma(op.getParameters(), os, [&](Attribute param) {
723 auto paramAttr = cast<hw::ParamDeclAttr>(param);
724 os << paramAttr.getName().str();
728 os << getAttributes(op,
false);
730 emitPrimitivePorts(op);
731 os << semicolonEndL();
738 auto emitPorts = [&](
auto ports,
bool isInput) {
739 auto e =
static_cast<size_t>(std::distance(ports.begin(), ports.end()));
741 auto type = op.getHWModuleType();
742 for (
auto [i, port] : llvm::enumerate(ports)) {
743 DictionaryAttr portAttr = cast_or_null<DictionaryAttr>(
744 op.getPortAttrs(isInput ? type.getPortIdForInputId(i)
745 : type.getPortIdForOutputId(i)));
747 os << getAttributes(op,
true, portAttr)
748 << port.
name.getValue() << colon();
752 hw::ParamDeclRefAttr bitWidth = dyn_cast<hw::ParamDeclRefAttr>(
753 cast<hw::IntType>(port.
type).getWidth());
754 os << bitWidth.getName().str();
756 unsigned int bitWidth = port.
type.getIntOrFloatBitWidth();
765 hw::ModulePortInfo ports(op.getPortList());
766 emitPorts(ports.getInputs(),
true);
768 emitPorts(ports.getOutputs(),
false);
771 void Emitter::emitInstance(InstanceOp op) {
772 indent() << getAttributes(op,
true) << op.instanceName()
773 <<
space() << equals() <<
space() << op.getComponentName()
774 << LParen() << RParen() << semicolonEndL();
777 void Emitter::emitPrimitive(PrimitiveOp op) {
778 indent() << getAttributes(op,
true) << op.instanceName()
779 <<
space() << equals() <<
space() << op.getPrimitiveName()
782 if (op.getParameters().has_value()) {
783 llvm::interleaveComma(*op.getParameters(), os, [&](Attribute param) {
784 auto paramAttr = cast<hw::ParamDeclAttr>(param);
785 auto value = paramAttr.getValue();
786 if (auto intAttr = dyn_cast<IntegerAttr>(value)) {
787 os << intAttr.getInt();
788 }
else if (
auto fpAttr = dyn_cast<FloatAttr>(value)) {
789 os << fpAttr.getValue().convertToFloat();
791 llvm_unreachable(
"Primitive parameter type not supported");
796 os << RParen() << semicolonEndL();
799 void Emitter::emitRegister(RegisterOp
reg) {
800 size_t bitWidth =
reg.getIn().getType().getIntOrFloatBitWidth();
801 indent() << getAttributes(
reg,
true) <<
reg.instanceName()
802 <<
space() << equals() <<
space() <<
"std_reg" << LParen()
803 << std::to_string(bitWidth) << RParen() << semicolonEndL();
806 void Emitter::emitUndef(UndefLibOp op) {
807 size_t bitwidth = op.getOut().getType().getIntOrFloatBitWidth();
808 indent() << getAttributes(op,
true) << op.instanceName()
809 <<
space() << equals() <<
space() <<
"undef" << LParen()
810 << std::to_string(bitwidth) << RParen() << semicolonEndL();
813 void Emitter::emitMemory(MemoryOp memory) {
814 size_t dimension = memory.getSizes().size();
815 if (dimension < 1 || dimension > 4) {
816 emitOpError(memory,
"Only memories with dimensionality in range [1, 4] are "
817 "supported by the native Calyx compiler.");
820 indent() << getAttributes(memory,
true) << memory.instanceName()
821 <<
space() << equals() <<
space() <<
"std_mem_d"
822 << std::to_string(dimension) << LParen() << memory.getWidth()
824 for (Attribute size : memory.getSizes()) {
825 APInt memSize = cast<IntegerAttr>(size).getValue();
826 memSize.print(os,
false);
830 ArrayAttr addrSizes = memory.getAddrSizes();
831 for (
size_t i = 0, e = addrSizes.size(); i != e; ++i) {
832 APInt addrSize = cast<IntegerAttr>(addrSizes[i]).getValue();
833 addrSize.print(os,
false);
838 os << RParen() << semicolonEndL();
841 void Emitter::emitSeqMemory(SeqMemoryOp memory) {
842 size_t dimension = memory.getSizes().size();
843 if (dimension < 1 || dimension > 4) {
844 emitOpError(memory,
"Only memories with dimensionality in range [1, 4] are "
845 "supported by the native Calyx compiler.");
848 bool isRef = !memory->hasAttr(
"external");
852 os << getAttributes(memory,
true) << memory.instanceName()
853 <<
space() << equals() <<
space() <<
"seq_mem_d"
854 << std::to_string(dimension) << LParen() << memory.getWidth() << comma();
855 for (Attribute size : memory.getSizes()) {
856 APInt memSize = cast<IntegerAttr>(size).getValue();
857 memSize.print(os,
false);
861 ArrayAttr addrSizes = memory.getAddrSizes();
862 for (
size_t i = 0, e = addrSizes.size(); i != e; ++i) {
863 APInt addrSize = cast<IntegerAttr>(addrSizes[i]).getValue();
864 addrSize.print(os,
false);
869 os << RParen() << semicolonEndL();
872 void Emitter::emitInvoke(InvokeOp invoke) {
873 StringRef callee = invoke.getCallee();
874 indent() <<
"invoke " << callee;
875 auto refCellsMap = invoke.getRefCellsMap();
876 if (!refCellsMap.empty()) {
878 llvm::interleaveComma(refCellsMap, os, [&](Attribute attr) {
879 auto dictAttr = cast<DictionaryAttr>(attr);
880 llvm::interleaveComma(dictAttr, os, [&](NamedAttribute namedAttr) {
881 auto refCellName = namedAttr.getName().str();
883 cast<FlatSymbolRefAttr>(namedAttr.getValue()).getValue();
884 os << refCellName <<
" = " << externalMem;
889 ArrayAttr portNames = invoke.getPortNames();
890 ArrayAttr inputNames = invoke.getInputNames();
894 llvm::StringMap<std::string> inputsMap;
895 llvm::StringMap<std::string> outputsMap;
896 for (
auto [portNameAttr, inputNameAttr, input] :
897 llvm::zip(portNames, inputNames, invoke.getInputs())) {
898 StringRef portName = cast<StringAttr>(portNameAttr).getValue();
899 StringRef inputName = cast<StringAttr>(inputNameAttr).getValue();
907 StringRef inputMapKey = portName.drop_front(2 + callee.size());
908 if (portName.substr(1, callee.size()) == callee) {
910 if (isa_and_nonnull<hw::ConstantOp>(input.getDefiningOp())) {
911 hw::ConstantOp constant = cast<hw::ConstantOp>(input.getDefiningOp());
912 APInt value = constant.getValue();
913 std::string mapValue = std::to_string(value.getBitWidth()) +
914 apostrophe().data() +
"d" +
915 std::to_string(value.getZExtValue());
916 inputsMap[inputMapKey] = mapValue;
919 inputsMap[inputMapKey] = inputName.drop_front(1).str();
920 }
else if (inputName.substr(1, callee.size()) == callee)
921 outputsMap[inputName.drop_front(2 + callee.size())] =
922 portName.drop_front(1).str();
926 llvm::interleaveComma(inputsMap, os, [&](
const auto &iter) {
927 os << iter.getKey() <<
" = " << iter.getValue();
932 llvm::interleaveComma(outputsMap, os, [&](
const auto &iter) {
933 os << iter.getKey() <<
" = " << iter.getValue();
935 os << RParen() << semicolonEndL();
938 void Emitter::emitConstant(ConstantOp constantOp) {
939 TypedAttr attr = constantOp.getValueAttr();
940 assert(isa<FloatAttr>(attr) &&
"must be a floating point constant");
941 auto fltAttr = cast<FloatAttr>(attr);
942 APFloat value = fltAttr.getValue();
943 auto type = cast<FloatType>(fltAttr.getType());
944 double doubleValue = value.convertToDouble();
945 auto floatBits = value.getSizeInBits(type.getFloatSemantics());
946 indent() << constantOp.getName().str() <<
space() << equals() <<
space()
947 <<
"std_float_const";
950 static constexpr int32_t IEEE754 = 0;
951 os << LParen() << std::to_string(IEEE754) << comma() << floatBits << comma()
952 << std::to_string(doubleValue) << RParen() << semicolonEndL();
960 void Emitter::emitLibraryPrimTypedByAllPorts(Operation *op) {
961 auto cell = cast<CellInterface>(op);
962 indent() << getAttributes(op,
true) << cell.instanceName()
965 llvm::interleaveComma(op->getResults(), os, [&](
auto res) {
966 os << std::to_string(res.getType().getIntOrFloatBitWidth());
968 os << RParen() << semicolonEndL();
971 void Emitter::emitLibraryPrimTypedByFirstInputPort(Operation *op) {
972 auto cell = cast<CellInterface>(op);
973 unsigned bitWidth = cell.getInputPorts()[0].getType().getIntOrFloatBitWidth();
974 StringRef opName = op->getName().getStringRef();
975 indent() << getAttributes(op,
true) << cell.instanceName()
977 << LParen() << bitWidth << RParen() << semicolonEndL();
980 void Emitter::emitLibraryPrimTypedByFirstOutputPort(
981 Operation *op, std::optional<StringRef> calyxLibName) {
982 auto cell = cast<CellInterface>(op);
984 cell.getOutputPorts()[0].getType().getIntOrFloatBitWidth();
985 StringRef opName = op->getName().getStringRef();
986 indent() << getAttributes(op,
true) << cell.instanceName()
989 << LParen() << bitWidth << RParen() << semicolonEndL();
992 void Emitter::emitLibraryFloatingPoint(Operation *op) {
993 auto cell = cast<CellInterface>(op);
995 cell.getOutputPorts()[0].getType().getIntOrFloatBitWidth();
999 unsigned expWidth, sigWidth;
1018 op->emitError(
"The supported bitwidths are 16, 32, 64, and 128");
1022 StringRef opName = op->getName().getStringRef();
1023 indent() << getAttributes(op,
true) << cell.instanceName()
1025 << LParen() << expWidth << comma() << sigWidth << comma() << bitWidth
1026 << RParen() << semicolonEndL();
1029 void Emitter::emitAssignment(AssignOp op) {
1031 emitValue(op.getDest(),
true);
1033 if (op.getGuard()) {
1034 emitValue(op.getGuard(),
false);
1035 os << questionMark();
1037 emitValue(op.getSrc(),
false);
1038 os << semicolonEndL();
1041 void Emitter::emitWires(WiresOp op) {
1042 emitCalyxSection(
"wires", [&]() {
1043 for (
auto &&bodyOp : *op.getBodyBlock()) {
1044 TypeSwitch<Operation *>(&bodyOp)
1045 .Case<GroupInterface>([&](auto op) { emitGroup(op); })
1046 .Case<AssignOp>([&](
auto op) { emitAssignment(op); })
1049 .Default([&](
auto op) {
1050 emitOpError(op,
"not supported for emission inside wires section");
1056 void Emitter::emitGroup(GroupInterface group) {
1057 auto emitGroupBody = [&]() {
1058 for (
auto &&bodyOp : *group.getBody()) {
1059 TypeSwitch<Operation *>(&bodyOp)
1060 .Case<AssignOp>([&](
auto op) { emitAssignment(op); })
1061 .Case<GroupDoneOp>([&](
auto op) { emitGroupPort(group, op,
"done"); })
1062 .Case<GroupGoOp>([&](
auto op) { emitGroupPort(group, op,
"go"); })
1063 .Case<hw::ConstantOp, comb::AndOp, comb::OrOp, comb::XorOp, CycleOp>(
1065 .Default([&](
auto op) {
1066 emitOpError(op,
"not supported for emission inside group.");
1071 if (isa<StaticGroupOp>(group)) {
1072 auto staticGroup = cast<StaticGroupOp>(group);
1073 prefix = llvm::formatv(
"static<{0}> group", staticGroup.getLatency());
1075 prefix = isa<CombGroupOp>(group) ?
"comb group" :
"group";
1078 (group.symName().getValue() + getAttributes(group,
false))
1080 emitCalyxSection(prefix, emitGroupBody, groupHeader);
1083 void Emitter::emitEnable(EnableOp enable) {
1084 indent() << getAttributes(enable,
true) << enable.getGroupName()
1088 void Emitter::emitControl(ControlOp control) {
1090 if (control ==
nullptr)
1092 emitCalyxSection(
"control",
1093 [&]() { emitCalyxControl(control.getBodyBlock()); });
1102 llvm::raw_ostream &os) {
1103 Emitter emitter(os);
1104 if (failed(emitter.emitImports(module)))
1106 emitter.emitModule(module);
1107 emitter.emitCiderMetadata(module);
1108 return emitter.finalize();
1112 static mlir::TranslateFromMLIRRegistration toCalyx(
1113 "export-calyx",
"export Calyx",
1114 [](ModuleOp module, llvm::raw_ostream &os) {
1117 [](mlir::DialectRegistry ®istry) {
1119 .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