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"
36static constexpr std::string_view LSquare() {
return "["; }
37static constexpr std::string_view RSquare() {
return "]"; }
38static constexpr std::string_view LAngleBracket() {
return "<"; }
39static constexpr std::string_view RAngleBracket() {
return ">"; }
40static constexpr std::string_view LParen() {
return "("; }
41static constexpr std::string_view RParen() {
return ")"; }
42static constexpr std::string_view colon() {
return ": "; }
43static constexpr std::string_view
space() {
return " "; }
44static constexpr std::string_view period() {
return "."; }
45static constexpr std::string_view questionMark() {
return " ? "; }
46static constexpr std::string_view exclamationMark() {
return "!"; }
47static constexpr std::string_view equals() {
return "="; }
48static constexpr std::string_view comma() {
return ", "; }
49static constexpr std::string_view arrow() {
return " -> "; }
50static constexpr std::string_view quote() {
return "\""; }
51static constexpr std::string_view apostrophe() {
return "'"; }
52static constexpr std::string_view LBraceEndL() {
return "{\n"; }
53static constexpr std::string_view RBraceEndL() {
return "}\n"; }
54static constexpr std::string_view semicolonEndL() {
return ";\n"; }
55static constexpr std::string_view addressSymbol() {
return "@"; }
56static constexpr std::string_view endl() {
return "\n"; }
57static constexpr std::string_view metadataLBrace() {
return "#{\n"; }
58static constexpr std::string_view metadataRBrace() {
return "}#\n"; }
61constexpr std::array<StringRef, 7> integerAttributes{
62 "external",
"static",
"share",
"bound",
63 "write_together",
"read_together",
"pos",
67constexpr std::array<StringRef, 12> booleanAttributes{
68 "clk",
"reset",
"go",
"done",
"generated",
"precious",
69 "toplevel",
"stable",
"nointerface",
"inline",
"state_share",
"data",
72static 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(
"."));
86static bool isValidCalyxAttribute(StringRef identifier) {
88 return llvm::find(integerAttributes, identifier) != integerAttributes.end() ||
89 llvm::find(booleanAttributes, identifier) != booleanAttributes.end();
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 .Case<FpToIntOpIEEE754>([&](
auto op) -> FailureOr<StringRef> {
165 static constexpr std::string_view sFloatingPoint =
"float/fpToInt";
166 return {sFloatingPoint};
168 .Case<IntToFpOpIEEE754>([&](
auto op) -> FailureOr<StringRef> {
169 static constexpr std::string_view sFloatingPoint =
"float/intToFp";
170 return {sFloatingPoint};
172 .Case<DivSqrtOpIEEE754>([&](
auto op) -> FailureOr<StringRef> {
173 static constexpr std::string_view sFloatingPoint =
"float/divSqrtFN";
174 return {sFloatingPoint};
176 .Default([&](
auto op) {
177 auto diag = op->emitOpError() <<
"not supported for emission";
192 Emitter(llvm::raw_ostream &os) : os(os) {}
193 LogicalResult finalize();
196 raw_ostream &indent() {
return os.indent(currentIndent); }
197 void addIndent() { currentIndent += 2; }
198 void reduceIndent() {
199 assert(currentIndent >= 2 &&
"Unintended indentation wrap");
204 void emitModule(ModuleOp op);
207 void emitCiderMetadata(mlir::ModuleOp op) {
208 auto metadata = op->getAttrOfType<ArrayAttr>(
"calyx.metadata");
212 constexpr std::string_view metadataIdentifier =
"metadata";
213 os << endl() << metadataIdentifier <<
space() << metadataLBrace();
215 for (
auto sourceLoc : llvm::enumerate(metadata)) {
217 os << std::to_string(sourceLoc.index()) << colon();
218 os << cast<StringAttr>(sourceLoc.value()).getValue() << endl();
221 os << metadataRBrace();
225 LogicalResult emitImports(ModuleOp op) {
226 auto emitImport = [&](StringRef library) {
229 os <<
"import " << quote() <<
"primitives/" << library << period()
230 <<
"futil" << quote() << semicolonEndL();
233 auto libraryNames = importTracker.getLibraryNames(op);
234 if (failed(libraryNames))
237 for (StringRef library : *libraryNames)
244 void emitComponent(ComponentInterface op);
245 void emitComponentPorts(ComponentInterface op);
248 void emitPrimitiveExtern(hw::HWModuleExternOp op);
249 void emitPrimitivePorts(hw::HWModuleExternOp op);
252 void emitInstance(InstanceOp op);
255 void emitPrimitive(PrimitiveOp op);
258 void emitWires(WiresOp op);
261 void emitGroup(GroupInterface group);
264 void emitControl(ControlOp control);
267 void emitAssignment(AssignOp op);
270 void emitEnable(EnableOp enable);
273 void emitRegister(RegisterOp reg);
276 void emitUndef(UndefLibOp op);
279 void emitMemory(MemoryOp memory);
282 void emitSeqMemory(SeqMemoryOp memory);
285 void emitInvoke(InvokeOp invoke);
288 void emitConstant(ConstantOp constant);
296 void emitLibraryPrimTypedByAllPorts(Operation *op);
304 void emitLibraryPrimTypedByFirstInputPort(Operation *op);
312 void emitLibraryPrimTypedByFirstOutputPort(
313 Operation *op, std::optional<StringRef> calyxLibName = {});
316 void emitLibraryFloatingPoint(Operation *op);
320 ImportTracker importTracker;
323 InFlightDiagnostic emitError(Operation *op,
const Twine &message) {
324 encounteredError =
true;
325 return op->emitError(message);
329 InFlightDiagnostic emitOpError(Operation *op,
const Twine &message) {
330 encounteredError =
true;
331 return op->emitOpError(message);
344 std::string getAttribute(Operation *op, NamedAttribute attr,
bool isPort,
347 std::optional<StringRef> identifierOpt = getCalyxAttrIdentifier(attr);
349 if (!identifierOpt.has_value())
352 StringRef identifier = *identifierOpt;
354 if (!isValidCalyxAttribute(identifier))
358 llvm::raw_string_ostream buffer(output);
359 buffer.reserveExtraSpace(32);
361 bool isBooleanAttribute =
362 llvm::find(booleanAttributes, identifier) != booleanAttributes.end();
364 if (isa<UnitAttr>(attr.getValue())) {
365 assert(isBooleanAttribute &&
366 "Non-boolean attributes must provide an integer value.");
368 buffer << quote() << identifier << quote() << equals() <<
"1";
370 buffer << addressSymbol() << identifier;
372 }
else if (
auto intAttr = dyn_cast<IntegerAttr>(attr.getValue())) {
373 APInt value = intAttr.getValue();
375 buffer << quote() << identifier << quote() << equals() << value;
377 buffer << addressSymbol() << identifier;
380 if (!isBooleanAttribute || intAttr.getValue() != 1) {
382 SmallVector<char, 4>
s;
383 value.toStringUnsigned(s, 10);
384 buffer << LParen() <<
s << RParen();
393 std::string getAttributes(Operation *op,
bool atFormat,
394 DictionaryAttr attributes =
nullptr) {
395 bool isPort = attributes !=
nullptr;
396 bool atLeastOne =
false;
399 attributes = op->getAttrDictionary();
401 std::string calyxAttributes;
402 llvm::raw_string_ostream buf(calyxAttributes);
405 buf << LAngleBracket();
407 for (
auto &attr : attributes) {
409 if (
auto out = getAttribute(op, attr,
isPort, atFormat); !out.empty()) {
412 buf << (atFormat ?
space() : comma());
417 auto out = buf.str();
420 out.append(atFormat ?
space() : RAngleBracket());
432 template <
typename Func>
433 void emitCalyxBody(Func emitBody) {
434 os <<
space() << LBraceEndL();
438 indent() << RBraceEndL();
442 template <
typename Func>
443 void emitCalyxSection(StringRef sectionName, Func emitBody,
444 StringRef symbolName =
"") {
445 indent() << sectionName;
446 if (!symbolName.empty())
447 os <<
space() << symbolName;
448 emitCalyxBody(emitBody);
452 template <
typename CombinationalOp>
453 void emitCombinationalValue(CombinationalOp op, StringRef logicalSymbol) {
454 auto inputs = op.getInputs();
456 for (
size_t i = 0, e = inputs.size(); i != e; ++i) {
457 emitValue(inputs[i],
false);
465 void emitCycleValue(CycleOp op) {
467 if (op.getEnd().has_value()) {
469 os << op.getStart() <<
":" << op.getEnd();
477 void emitValue(Value value,
bool isIndented) {
478 if (
auto blockArg = dyn_cast<BlockArgument>(value)) {
481 (isIndented ? indent() : os) << portName.getValue();
485 auto *definingOp = value.getDefiningOp();
486 assert(definingOp &&
"Value does not have a defining operation.");
488 TypeSwitch<Operation *>(definingOp)
489 .Case<CellInterface>([&](
auto cell) {
491 (isIndented ? indent() : os)
492 << cell.instanceName() << period() << cell.portName(value);
494 .Case<hw::ConstantOp>([&](
auto op) {
497 APInt value = op.getValue();
498 auto &stream = isIndented ? indent() : os;
499 bool isNegative = value.isNegative();
500 StringRef base = isNegative ?
"b" :
"d";
502 stream << std::to_string(value.getBitWidth()) << apostrophe() << base;
506 value.toStringUnsigned(str, 2);
509 value.print(stream,
false);
512 .Case<comb::AndOp>([&](
auto op) { emitCombinationalValue(op,
"&"); })
513 .Case<comb::OrOp>([&](
auto op) { emitCombinationalValue(op,
"|"); })
514 .Case<comb::XorOp>([&](
auto op) {
517 if (!op.isBinaryNot()) {
518 emitOpError(op,
"Only supporting Binary Not for XOR.");
523 os << exclamationMark();
524 emitValue(op.getInputs()[0],
false);
526 .Case<CycleOp>([&](
auto op) { emitCycleValue(op); })
528 [&](
auto op) { emitOpError(op,
"not supported for emission"); });
532 template <
typename OpTy>
533 void emitGroupPort(GroupInterface group, OpTy op, StringRef portHole) {
534 assert((isa<GroupGoOp>(op) || isa<GroupDoneOp>(op)) &&
535 "Required to be a group port.");
536 indent() << group.symName().getValue() << LSquare() << portHole << RSquare()
539 emitValue(op.getGuard(),
false);
540 os << questionMark();
542 emitValue(op.getSrc(),
false);
543 os << semicolonEndL();
547 void emitCalyxControl(Block *body) {
548 Operation *parent = body->getParentOp();
549 assert((isa<ControlOp>(parent) || parent->hasTrait<ControlLike>()) &&
550 "This should only be used to emit Calyx Control structures.");
554 if (
auto enable = dyn_cast<EnableOp>(parent)) {
560 auto prependAttributes = [&](Operation *op, StringRef sym) {
561 return (getAttributes(op,
true) + sym).str();
564 for (
auto &&op : *body) {
566 TypeSwitch<Operation *>(&op)
567 .Case<SeqOp>([&](
auto op) {
568 emitCalyxSection(prependAttributes(op,
"seq"),
569 [&]() { emitCalyxControl(op.getBodyBlock()); });
571 .Case<StaticSeqOp>([&](
auto op) {
572 emitCalyxSection(prependAttributes(op,
"static seq"),
573 [&]() { emitCalyxControl(op.getBodyBlock()); });
575 .Case<ParOp>([&](
auto op) {
576 emitCalyxSection(prependAttributes(op,
"par"),
577 [&]() { emitCalyxControl(op.getBodyBlock()); });
579 .Case<WhileOp>([&](
auto op) {
580 indent() << prependAttributes(op,
"while ");
581 emitValue(op.getCond(),
false);
583 if (
auto groupName = op.getGroupName())
584 os <<
" with " << *groupName;
586 emitCalyxBody([&]() { emitCalyxControl(op.getBodyBlock()); });
588 .Case<IfOp>([&](
auto op) {
589 indent() << prependAttributes(op,
"if ");
590 emitValue(op.getCond(),
false);
592 if (
auto groupName = op.getGroupName())
593 os <<
" with " << *groupName;
595 emitCalyxBody([&]() { emitCalyxControl(op.getThenBody()); });
596 if (op.elseBodyExists())
597 emitCalyxSection(
"else",
598 [&]() { emitCalyxControl(op.getElseBody()); });
600 .Case<StaticIfOp>([&](
auto op) {
601 indent() << prependAttributes(op,
"static if ");
602 emitValue(op.getCond(),
false);
604 emitCalyxBody([&]() { emitCalyxControl(op.getThenBody()); });
605 if (op.elseBodyExists())
606 emitCalyxSection(
"else",
607 [&]() { emitCalyxControl(op.getElseBody()); });
609 .Case<RepeatOp>([&](
auto op) {
610 indent() << prependAttributes(op,
"repeat ");
613 emitCalyxBody([&]() { emitCalyxControl(op.getBodyBlock()); });
615 .Case<StaticRepeatOp>([&](
auto op) {
616 indent() << prependAttributes(op,
"static repeat ");
619 emitCalyxBody([&]() { emitCalyxControl(op.getBodyBlock()); });
621 .Case<StaticParOp>([&](
auto op) {
622 emitCalyxSection(prependAttributes(op,
"static par"),
623 [&]() { emitCalyxControl(op.getBodyBlock()); });
625 .Case<EnableOp>([&](
auto op) { emitEnable(op); })
626 .Case<InvokeOp>([&](
auto op) { emitInvoke(op); })
627 .Default([&](
auto op) {
628 emitOpError(op,
"not supported for emission inside control.");
634 llvm::raw_ostream &os;
637 bool encounteredError =
false;
641 unsigned currentIndent = 0;
646LogicalResult Emitter::finalize() {
return failure(encounteredError); }
649void Emitter::emitModule(ModuleOp op) {
650 for (
auto &bodyOp : *op.getBody()) {
651 if (
auto componentOp = dyn_cast<ComponentInterface>(bodyOp))
652 emitComponent(componentOp);
653 else if (
auto hwModuleExternOp = dyn_cast<hw::HWModuleExternOp>(bodyOp))
654 emitPrimitiveExtern(hwModuleExternOp);
656 emitOpError(&bodyOp,
"Unexpected op");
661void Emitter::emitComponent(ComponentInterface op) {
662 std::string combinationalPrefix = op.isComb() ?
"comb " :
"";
664 indent() << combinationalPrefix <<
"component " << op.getName()
665 << getAttributes(op,
false,
nullptr);
667 emitComponentPorts(op);
668 os <<
space() << LBraceEndL();
674 emitCalyxSection(
"cells", [&]() {
675 for (
auto &&bodyOp : *op.getBodyBlock()) {
676 TypeSwitch<Operation *>(&bodyOp)
677 .Case<UndefLibOp>([&](
auto op) { emitUndef(op); })
678 .Case<WiresOp>([&](
auto op) { wires = op; })
679 .Case<ControlOp>([&](
auto op) { control = op; })
680 .Case<InstanceOp>([&](
auto op) { emitInstance(op); })
681 .Case<PrimitiveOp>([&](
auto op) { emitPrimitive(op); })
682 .Case<RegisterOp>([&](
auto op) { emitRegister(op); })
683 .Case<MemoryOp>([&](
auto op) { emitMemory(op); })
684 .Case<SeqMemoryOp>([&](
auto op) { emitSeqMemory(op); })
685 .Case<hw::ConstantOp>([&](
auto op) { })
686 .Case<calyx::ConstantOp>([&](
auto op) { emitConstant(op); })
687 .Case<SliceLibOp, PadLibOp, ExtSILibOp>(
688 [&](
auto op) { emitLibraryPrimTypedByAllPorts(op); })
689 .Case<LtLibOp, GtLibOp, EqLibOp, NeqLibOp, GeLibOp, LeLibOp, SltLibOp,
690 SgtLibOp, SeqLibOp, SneqLibOp, SgeLibOp, SleLibOp, AddLibOp,
691 SubLibOp, ShruLibOp, RshLibOp, SrshLibOp, LshLibOp, AndLibOp,
692 NotLibOp, OrLibOp, XorLibOp, WireLibOp>(
693 [&](
auto op) { emitLibraryPrimTypedByFirstInputPort(op); })
695 [&](
auto op) { emitLibraryPrimTypedByFirstOutputPort(op); })
696 .Case<MultPipeLibOp>(
697 [&](
auto op) { emitLibraryPrimTypedByFirstOutputPort(op); })
698 .Case<RemUPipeLibOp, DivUPipeLibOp>([&](
auto op) {
699 emitLibraryPrimTypedByFirstOutputPort(
700 op, {
"std_div_pipe"});
702 .Case<RemSPipeLibOp, DivSPipeLibOp>([&](
auto op) {
703 emitLibraryPrimTypedByFirstOutputPort(
704 op, {
"std_sdiv_pipe"});
706 .Case<AddFOpIEEE754, MulFOpIEEE754, CompareFOpIEEE754,
707 FpToIntOpIEEE754, IntToFpOpIEEE754, DivSqrtOpIEEE754>(
708 [&](
auto op) { emitLibraryFloatingPoint(op); })
709 .Default([&](
auto op) {
710 emitOpError(op,
"not supported for emission inside component");
716 emitControl(control);
722void Emitter::emitComponentPorts(ComponentInterface op) {
723 auto emitPorts = [&](
auto ports) {
725 for (
size_t i = 0, e = ports.size(); i < e; ++i) {
726 const PortInfo &port = ports[i];
729 unsigned int bitWidth = port.
type.getIntOrFloatBitWidth();
730 os << getAttributes(op,
true, port.
attributes)
731 << port.
name.getValue() << colon() << bitWidth;
738 emitPorts(op.getInputPortInfo());
740 emitPorts(op.getOutputPortInfo());
744void Emitter::emitPrimitiveExtern(hw::HWModuleExternOp op) {
745 Attribute filename = op->getAttrDictionary().get(
"filename");
746 indent() <<
"extern " << filename <<
space() << LBraceEndL();
748 indent() <<
"primitive " << op.getName();
750 if (!op.getParameters().empty()) {
752 llvm::interleaveComma(op.getParameters(), os, [&](Attribute param) {
753 auto paramAttr = cast<hw::ParamDeclAttr>(param);
754 os << paramAttr.getName().str();
758 os << getAttributes(op,
false);
760 emitPrimitivePorts(op);
761 os << semicolonEndL();
767void Emitter::emitPrimitivePorts(hw::HWModuleExternOp op) {
768 auto emitPorts = [&](
auto ports,
bool isInput) {
769 auto e =
static_cast<size_t>(std::distance(ports.begin(), ports.end()));
771 auto type = op.getHWModuleType();
772 for (
auto [i, port] : llvm::enumerate(ports)) {
773 DictionaryAttr portAttr = cast_or_null<DictionaryAttr>(
774 op.getPortAttrs(isInput ? type.getPortIdForInputId(i)
775 : type.getPortIdForOutputId(i)));
777 os << getAttributes(op,
true, portAttr)
778 << port.
name.getValue() << colon();
781 if (hw::isParametricType(port.
type)) {
782 hw::ParamDeclRefAttr bitWidth = dyn_cast<hw::ParamDeclRefAttr>(
783 cast<hw::IntType>(port.
type).getWidth());
784 os << bitWidth.getName().str();
786 unsigned int bitWidth = port.
type.getIntOrFloatBitWidth();
795 hw::ModulePortInfo ports(op.getPortList());
796 emitPorts(ports.getInputs(),
true);
798 emitPorts(ports.getOutputs(),
false);
801void Emitter::emitInstance(InstanceOp op) {
802 indent() << getAttributes(op,
true) << op.instanceName()
803 <<
space() << equals() <<
space() << op.getComponentName()
804 << LParen() << RParen() << semicolonEndL();
807void Emitter::emitPrimitive(PrimitiveOp op) {
808 indent() << getAttributes(op,
true) << op.instanceName()
809 <<
space() << equals() <<
space() << op.getPrimitiveName()
812 if (op.getParameters().has_value()) {
813 llvm::interleaveComma(*op.getParameters(), os, [&](Attribute param) {
814 auto paramAttr = cast<hw::ParamDeclAttr>(param);
815 auto value = paramAttr.getValue();
816 if (auto intAttr = dyn_cast<IntegerAttr>(value)) {
817 os << intAttr.getInt();
818 }
else if (
auto fpAttr = dyn_cast<FloatAttr>(value)) {
819 os << fpAttr.getValue().convertToFloat();
821 llvm_unreachable(
"Primitive parameter type not supported");
826 os << RParen() << semicolonEndL();
829void Emitter::emitRegister(RegisterOp reg) {
830 size_t bitWidth =
reg.getIn().getType().getIntOrFloatBitWidth();
831 indent() << getAttributes(reg,
true) <<
reg.instanceName()
832 <<
space() << equals() <<
space() <<
"std_reg" << LParen()
833 << std::to_string(bitWidth) << RParen() << semicolonEndL();
836void Emitter::emitUndef(UndefLibOp op) {
837 size_t bitwidth = op.getOut().getType().getIntOrFloatBitWidth();
838 indent() << getAttributes(op,
true) << op.instanceName()
839 <<
space() << equals() <<
space() <<
"undef" << LParen()
840 << std::to_string(bitwidth) << RParen() << semicolonEndL();
843void Emitter::emitMemory(MemoryOp memory) {
844 size_t dimension = memory.getSizes().size();
845 if (dimension < 1 || dimension > 4) {
846 emitOpError(memory,
"Only memories with dimensionality in range [1, 4] are "
847 "supported by the native Calyx compiler.");
850 indent() << getAttributes(memory,
true) << memory.instanceName()
851 <<
space() << equals() <<
space() <<
"std_mem_d"
852 << std::to_string(dimension) << LParen() << memory.getWidth()
854 for (Attribute size : memory.getSizes()) {
855 APInt memSize = cast<IntegerAttr>(size).getValue();
856 memSize.print(os,
false);
860 ArrayAttr addrSizes = memory.getAddrSizes();
861 for (
size_t i = 0, e = addrSizes.size(); i != e; ++i) {
862 APInt addrSize = cast<IntegerAttr>(addrSizes[i]).getValue();
863 addrSize.print(os,
false);
868 os << RParen() << semicolonEndL();
871void Emitter::emitSeqMemory(SeqMemoryOp memory) {
872 size_t dimension = memory.getSizes().size();
873 if (dimension < 1 || dimension > 4) {
874 emitOpError(memory,
"Only memories with dimensionality in range [1, 4] are "
875 "supported by the native Calyx compiler.");
878 bool isRef = !memory->hasAttr(
"external");
882 os << getAttributes(memory,
true) << memory.instanceName()
883 <<
space() << equals() <<
space() <<
"seq_mem_d"
884 << std::to_string(dimension) << LParen() << memory.getWidth() << comma();
885 for (Attribute size : memory.getSizes()) {
886 APInt memSize = cast<IntegerAttr>(size).getValue();
887 memSize.print(os,
false);
891 ArrayAttr addrSizes = memory.getAddrSizes();
892 for (
size_t i = 0, e = addrSizes.size(); i != e; ++i) {
893 APInt addrSize = cast<IntegerAttr>(addrSizes[i]).getValue();
894 addrSize.print(os,
false);
899 os << RParen() << semicolonEndL();
902void Emitter::emitInvoke(InvokeOp invoke) {
903 StringRef callee = invoke.getCallee();
904 indent() <<
"invoke " << callee;
905 auto refCellsMap = invoke.getRefCellsMap();
906 if (!refCellsMap.empty()) {
908 llvm::interleaveComma(refCellsMap, os, [&](Attribute attr) {
909 auto dictAttr = cast<DictionaryAttr>(attr);
910 llvm::interleaveComma(dictAttr, os, [&](NamedAttribute namedAttr) {
911 auto refCellName = namedAttr.getName().str();
913 cast<FlatSymbolRefAttr>(namedAttr.getValue()).getValue();
914 os << refCellName <<
" = " << externalMem;
919 ArrayAttr portNames = invoke.getPortNames();
920 ArrayAttr inputNames = invoke.getInputNames();
924 llvm::StringMap<std::string> inputsMap;
925 llvm::StringMap<std::string> outputsMap;
926 for (
auto [portNameAttr, inputNameAttr, input] :
927 llvm::zip(portNames, inputNames, invoke.getInputs())) {
928 StringRef portName = cast<StringAttr>(portNameAttr).getValue();
929 StringRef inputName = cast<StringAttr>(inputNameAttr).getValue();
937 StringRef inputMapKey = portName.drop_front(2 + callee.size());
938 if (portName.substr(1, callee.size()) == callee) {
940 if (isa_and_nonnull<hw::ConstantOp>(input.getDefiningOp())) {
941 hw::ConstantOp constant = cast<hw::ConstantOp>(input.getDefiningOp());
942 APInt value = constant.getValue();
943 std::string mapValue = std::to_string(value.getBitWidth()) +
944 apostrophe().data() +
"d" +
945 std::to_string(value.getZExtValue());
946 inputsMap[inputMapKey] = mapValue;
949 inputsMap[inputMapKey] = inputName.drop_front(1).str();
950 }
else if (inputName.substr(1, callee.size()) == callee)
951 outputsMap[inputName.drop_front(2 + callee.size())] =
952 portName.drop_front(1).str();
956 llvm::interleaveComma(inputsMap, os, [&](
const auto &iter) {
957 os << iter.getKey() <<
" = " << iter.getValue();
962 llvm::interleaveComma(outputsMap, os, [&](
const auto &iter) {
963 os << iter.getKey() <<
" = " << iter.getValue();
965 os << RParen() << semicolonEndL();
968void Emitter::emitConstant(ConstantOp constantOp) {
969 TypedAttr attr = constantOp.getValueAttr();
970 assert(isa<FloatAttr>(attr) &&
"must be a floating point constant");
971 auto fltAttr = cast<FloatAttr>(attr);
972 APFloat value = fltAttr.getValue();
973 auto type = cast<FloatType>(fltAttr.getType());
974 double doubleValue = value.convertToDouble();
975 auto floatBits = value.getSizeInBits(type.getFloatSemantics());
976 indent() << constantOp.instanceName() <<
space() << equals() <<
space()
977 <<
"std_float_const";
980 static constexpr int32_t
IEEE754 = 0;
981 os << LParen() << std::to_string(
IEEE754) << comma() << floatBits << comma()
982 << std::to_string(doubleValue) << RParen() << semicolonEndL();
990void Emitter::emitLibraryPrimTypedByAllPorts(Operation *op) {
991 auto cell = cast<CellInterface>(op);
992 indent() << getAttributes(op,
true) << cell.instanceName()
995 llvm::interleaveComma(op->getResults(), os, [&](
auto res) {
996 os << std::to_string(res.getType().getIntOrFloatBitWidth());
998 os << RParen() << semicolonEndL();
1001void Emitter::emitLibraryPrimTypedByFirstInputPort(Operation *op) {
1002 auto cell = cast<CellInterface>(op);
1003 unsigned bitWidth = cell.getInputPorts()[0].getType().getIntOrFloatBitWidth();
1004 StringRef opName = op->getName().getStringRef();
1005 indent() << getAttributes(op,
true) << cell.instanceName()
1007 << LParen() << bitWidth << RParen() << semicolonEndL();
1010void Emitter::emitLibraryPrimTypedByFirstOutputPort(
1011 Operation *op, std::optional<StringRef> calyxLibName) {
1012 auto cell = cast<CellInterface>(op);
1014 cell.getOutputPorts()[0].getType().getIntOrFloatBitWidth();
1015 StringRef opName = op->getName().getStringRef();
1016 indent() << getAttributes(op,
true) << cell.instanceName()
1019 << LParen() << bitWidth << RParen() << semicolonEndL();
1023 if (isa<IntToFpOpIEEE754>(cell.getOperation())) {
1025 return cell.getOutputPorts()[0].getType().getIntOrFloatBitWidth();
1028 auto inputPorts = cell.getInputPorts();
1029 assert(inputPorts.size() >= 2 &&
"There should be at least two input ports");
1033 size_t inputPortIndex = inputPorts.size() - 2;
1034 return cell.getInputPorts()[inputPortIndex].getType().getIntOrFloatBitWidth();
1037unsigned getIntWidth(Operation *op, CellInterface &cell,
bool isIntToFp) {
1038 auto inputPorts = cell.getInputPorts();
1039 assert(inputPorts.size() >= 2);
1041 size_t integerIndex = inputPorts.size() - 2;
1044 return cell.getOutputPorts()[0].getType().getIntOrFloatBitWidth();
1046 return cell.getInputPorts()[integerIndex].getType().getIntOrFloatBitWidth();
1049void Emitter::emitLibraryFloatingPoint(Operation *op) {
1050 auto cell = cast<CellInterface>(op);
1053 unsigned expWidth, sigWidth;
1054 switch (fpBitWidth) {
1072 op->emitError(
"The supported bitwidths are 16, 32, 64, and 128");
1077 if (
auto fpOp = dyn_cast<calyx::FloatingPointOpInterface>(op)) {
1078 opName = fpOp.getCalyxLibraryName();
1081 indent() << getAttributes(op,
true) << cell.instanceName()
1082 <<
space() << equals() <<
space() << opName << LParen();
1084 if (isa<calyx::IntToFpOpIEEE754>(op)) {
1086 os << intWidth << comma();
1089 os << expWidth << comma() << sigWidth << comma() << fpBitWidth;
1091 if (
auto fpToIntOp = dyn_cast<calyx::FpToIntOpIEEE754>(op)) {
1093 os << comma() << intWidth;
1096 os << RParen() << semicolonEndL();
1099void Emitter::emitAssignment(AssignOp op) {
1101 emitValue(op.getDest(),
true);
1103 if (op.getGuard()) {
1104 emitValue(op.getGuard(),
false);
1105 os << questionMark();
1107 if (
auto constantOp =
1108 dyn_cast_or_null<calyx::ConstantOp>(op.getSrc().getDefiningOp())) {
1109 TypedAttr attr = constantOp.getValueAttr();
1110 assert(isa<FloatAttr>(attr) &&
"must be a floating point constant");
1111 auto fltAttr = dyn_cast<FloatAttr>(attr);
1112 assert(attr !=
nullptr &&
"must be a floating point constant");
1113 APFloat value = fltAttr.getValue();
1114 if (value.isInfinity() || value.isNaN() || value.isNegative()) {
1116 auto intValue = value.bitcastToAPInt();
1117 intValue.toStringUnsigned(str, 2);
1118 os << std::to_string(intValue.getBitWidth()) << apostrophe() <<
"b" << str
1123 emitValue(op.getSrc(),
false);
1124 os << semicolonEndL();
1127void Emitter::emitWires(WiresOp op) {
1128 emitCalyxSection(
"wires", [&]() {
1129 for (
auto &&bodyOp : *op.getBodyBlock()) {
1130 TypeSwitch<Operation *>(&bodyOp)
1131 .Case<GroupInterface>([&](
auto op) { emitGroup(op); })
1132 .Case<AssignOp>([&](
auto op) { emitAssignment(op); })
1133 .Case<hw::ConstantOp, calyx::ConstantOp, comb::AndOp, comb::OrOp,
1134 comb::XorOp, CycleOp>([&](
auto op) { })
1135 .Default([&](
auto op) {
1136 emitOpError(op,
"not supported for emission inside wires section");
1142void Emitter::emitGroup(GroupInterface group) {
1143 auto emitGroupBody = [&]() {
1144 for (
auto &&bodyOp : *group.getBody()) {
1145 TypeSwitch<Operation *>(&bodyOp)
1146 .Case<AssignOp>([&](
auto op) { emitAssignment(op); })
1147 .Case<GroupDoneOp>([&](
auto op) { emitGroupPort(group, op,
"done"); })
1148 .Case<GroupGoOp>([&](
auto op) { emitGroupPort(group, op,
"go"); })
1149 .Case<hw::ConstantOp, comb::AndOp, comb::OrOp, comb::XorOp, CycleOp>(
1151 .Default([&](
auto op) {
1152 emitOpError(op,
"not supported for emission inside group.");
1157 if (isa<StaticGroupOp>(group)) {
1158 auto staticGroup = cast<StaticGroupOp>(group);
1159 prefix = llvm::formatv(
"static<{0}> group", staticGroup.getLatency());
1161 prefix = isa<CombGroupOp>(group) ?
"comb group" :
"group";
1164 (group.symName().getValue() + getAttributes(group,
false))
1166 emitCalyxSection(prefix, emitGroupBody, groupHeader);
1169void Emitter::emitEnable(EnableOp enable) {
1170 indent() << getAttributes(enable,
true) << enable.getGroupName()
1174void Emitter::emitControl(ControlOp control) {
1176 if (control ==
nullptr)
1178 emitCalyxSection(
"control",
1179 [&]() { emitCalyxControl(control.getBodyBlock()); });
1188 llvm::raw_ostream &os) {
1189 Emitter emitter(os);
1190 if (failed(emitter.emitImports(module)))
1192 emitter.emitModule(module);
1193 emitter.emitCiderMetadata(module);
1194 return emitter.finalize();
1198 static mlir::TranslateFromMLIRRegistration toCalyx(
1199 "export-calyx",
"export Calyx",
1200 [](ModuleOp module, llvm::raw_ostream &os) {
1203 [](mlir::DialectRegistry ®istry) {
1205 .insert<calyx::CalyxDialect, comb::CombDialect, hw::HWDialect>();
assert(baseType &&"element must be base type")
unsigned getIntWidth(Operation *op, CellInterface &cell, bool isIntToFp)
unsigned getFPBitWidth(CellInterface &cell)
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.
false return failure()
Parse an optional keyword or string and set instance into 'result'.
if(parseOptionalKeywordOrOptionalString(parser, result.rawName, found)) return failure()
PortInfo getPortInfo(BlockArgument arg)
Returns port information for the block argument provided.
void registerToCalyxTranslation()
mlir::LogicalResult exportCalyx(mlir::ModuleOp module, llvm::raw_ostream &os)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
DictionaryAttr attributes