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/FormatVariadic.h"
27 using namespace circt;
28 using namespace calyx;
33 static constexpr std::string_view LSquare() {
return "["; }
34 static constexpr std::string_view RSquare() {
return "]"; }
35 static constexpr std::string_view LAngleBracket() {
return "<"; }
36 static constexpr std::string_view RAngleBracket() {
return ">"; }
37 static constexpr std::string_view LParen() {
return "("; }
38 static constexpr std::string_view RParen() {
return ")"; }
39 static constexpr std::string_view colon() {
return ": "; }
40 static constexpr std::string_view
space() {
return " "; }
41 static constexpr std::string_view period() {
return "."; }
42 static constexpr std::string_view questionMark() {
return " ? "; }
43 static constexpr std::string_view exclamationMark() {
return "!"; }
44 static constexpr std::string_view equals() {
return "="; }
45 static constexpr std::string_view comma() {
return ", "; }
46 static constexpr std::string_view arrow() {
return " -> "; }
47 static constexpr std::string_view quote() {
return "\""; }
48 static constexpr std::string_view apostrophe() {
return "'"; }
49 static constexpr std::string_view LBraceEndL() {
return "{\n"; }
50 static constexpr std::string_view RBraceEndL() {
return "}\n"; }
51 static constexpr std::string_view semicolonEndL() {
return ";\n"; }
52 static constexpr std::string_view addressSymbol() {
return "@"; }
53 static constexpr std::string_view endl() {
return "\n"; }
54 static constexpr std::string_view metadataLBrace() {
return "#{\n"; }
55 static constexpr std::string_view metadataRBrace() {
return "}#\n"; }
58 constexpr std::array<StringRef, 7> integerAttributes{
59 "external",
"static",
"share",
"bound",
60 "write_together",
"read_together",
"pos",
64 constexpr std::array<StringRef, 12> booleanAttributes{
65 "clk",
"reset",
"go",
"done",
"generated",
"precious",
66 "toplevel",
"stable",
"nointerface",
"inline",
"state_share",
"data",
69 static std::optional<StringRef> getCalyxAttrIdentifier(NamedAttribute attr) {
70 StringRef identifier = attr.getName().strref();
71 if (identifier.contains(
".")) {
72 Dialect *dialect = attr.getNameDialect();
73 if (dialect !=
nullptr && isa<CalyxDialect>(*dialect)) {
74 return std::get<1>(identifier.split(
"."));
83 static bool isValidCalyxAttribute(StringRef identifier) {
85 return llvm::find(integerAttributes, identifier) != integerAttributes.end() ||
86 llvm::find(booleanAttributes, identifier) != booleanAttributes.end();
91 struct ImportTracker {
96 auto walkRes = module.walk([&](ComponentOp component) {
97 for (
auto &op : *component.getBodyBlock()) {
98 if (!isa<CellInterface>(op) || isa<InstanceOp, PrimitiveOp>(op))
101 auto libraryName = getLibraryFor(&op);
102 if (failed(libraryName))
103 return WalkResult::interrupt();
104 usedLibraries.insert(*libraryName);
106 return WalkResult::advance();
108 if (walkRes.wasInterrupted())
110 return usedLibraries;
116 return TypeSwitch<Operation *, FailureOr<StringRef>>(op)
117 .Case<AddLibOp, RegisterOp, UndefLibOp, WireLibOp>(
119 static constexpr std::string_view sCompile =
"compile";
122 .Case<NotLibOp, AndLibOp, OrLibOp, XorLibOp, SubLibOp, GtLibOp, LtLibOp,
123 EqLibOp, NeqLibOp, GeLibOp, LeLibOp, LshLibOp, RshLibOp,
124 SliceLibOp, PadLibOp, MuxLibOp>(
126 static constexpr std::string_view sCore =
"core";
129 .Case<SgtLibOp, SltLibOp, SeqLibOp, SneqLibOp, SgeLibOp, SleLibOp,
130 SrshLibOp, MultPipeLibOp, RemUPipeLibOp, RemSPipeLibOp,
131 DivUPipeLibOp, DivSPipeLibOp, ExtSILibOp>(
133 static constexpr std::string_view sBinaryOperators =
135 return {sBinaryOperators};
138 static constexpr std::string_view sMemories =
"memories/comb";
142 static constexpr std::string_view sMemories =
"memories/seq";
145 .Default([&](
auto op) {
146 auto diag = op->emitOpError() <<
"not supported for emission";
161 Emitter(llvm::raw_ostream &os) : os(os) {}
162 LogicalResult finalize();
165 raw_ostream &indent() {
return os.indent(currentIndent); }
166 void addIndent() { currentIndent += 2; }
167 void reduceIndent() {
168 assert(currentIndent >= 2 &&
"Unintended indentation wrap");
173 void emitModule(ModuleOp op);
176 void emitCiderMetadata(mlir::ModuleOp op) {
177 auto metadata = op->getAttrOfType<ArrayAttr>(
"calyx.metadata");
181 constexpr std::string_view metadataIdentifier =
"metadata";
182 os << endl() << metadataIdentifier <<
space() << metadataLBrace();
184 for (
auto sourceLoc : llvm::enumerate(metadata)) {
186 os << std::to_string(sourceLoc.index()) << colon();
187 os << cast<StringAttr>(sourceLoc.value()).getValue() << endl();
190 os << metadataRBrace();
194 LogicalResult emitImports(ModuleOp op) {
195 auto emitImport = [&](StringRef library) {
198 os <<
"import " << quote() <<
"primitives/" << library << period()
199 <<
"futil" << quote() << semicolonEndL();
202 auto libraryNames = importTracker.getLibraryNames(op);
203 if (failed(libraryNames))
206 for (StringRef library : *libraryNames)
213 void emitComponent(ComponentInterface op);
214 void emitComponentPorts(ComponentInterface op);
221 void emitInstance(InstanceOp op);
224 void emitPrimitive(PrimitiveOp op);
227 void emitWires(WiresOp op);
230 void emitGroup(GroupInterface group);
233 void emitControl(ControlOp control);
236 void emitAssignment(AssignOp op);
239 void emitEnable(EnableOp enable);
242 void emitRegister(RegisterOp
reg);
245 void emitUndef(UndefLibOp op);
248 void emitMemory(MemoryOp memory);
251 void emitSeqMemory(SeqMemoryOp memory);
254 void emitInvoke(InvokeOp invoke);
262 void emitLibraryPrimTypedByAllPorts(Operation *op);
270 void emitLibraryPrimTypedByFirstInputPort(Operation *op);
278 void emitLibraryPrimTypedByFirstOutputPort(
279 Operation *op, std::optional<StringRef> calyxLibName = {});
283 ImportTracker importTracker;
286 InFlightDiagnostic emitError(Operation *op,
const Twine &message) {
287 encounteredError =
true;
288 return op->emitError(message);
292 InFlightDiagnostic emitOpError(Operation *op,
const Twine &message) {
293 encounteredError =
true;
294 return op->emitOpError(message);
307 std::string getAttribute(Operation *op, NamedAttribute attr,
bool isPort,
310 std::optional<StringRef> identifierOpt = getCalyxAttrIdentifier(attr);
312 if (!identifierOpt.has_value())
315 StringRef identifier = *identifierOpt;
317 if (!isValidCalyxAttribute(identifier))
321 llvm::raw_string_ostream buffer(output);
322 buffer.reserveExtraSpace(32);
324 bool isBooleanAttribute =
325 llvm::find(booleanAttributes, identifier) != booleanAttributes.end();
327 if (isa<UnitAttr>(attr.getValue())) {
328 assert(isBooleanAttribute &&
329 "Non-boolean attributes must provide an integer value.");
331 buffer << quote() << identifier << quote() << equals() <<
"1";
333 buffer << addressSymbol() << identifier;
335 }
else if (
auto intAttr = dyn_cast<IntegerAttr>(attr.getValue())) {
336 APInt value = intAttr.getValue();
338 buffer << quote() << identifier << quote() << equals() << value;
340 buffer << addressSymbol() << identifier;
343 if (!isBooleanAttribute || intAttr.getValue() != 1) {
345 SmallVector<char, 4> s;
346 value.toStringUnsigned(s, 10);
347 buffer << LParen() << s << RParen();
356 std::string getAttributes(Operation *op,
bool atFormat,
357 DictionaryAttr attributes =
nullptr) {
358 bool isPort = attributes !=
nullptr;
359 bool atLeastOne =
false;
362 attributes = op->getAttrDictionary();
364 std::string calyxAttributes;
365 llvm::raw_string_ostream buf(calyxAttributes);
368 buf << LAngleBracket();
370 for (
auto &attr : attributes) {
372 if (
auto out = getAttribute(op, attr,
isPort, atFormat); !out.empty()) {
375 buf << (atFormat ?
space() : comma());
380 auto out = buf.str();
383 out.append(atFormat ?
space() : RAngleBracket());
395 template <
typename Func>
396 void emitCalyxBody(Func emitBody) {
397 os <<
space() << LBraceEndL();
401 indent() << RBraceEndL();
405 template <
typename Func>
406 void emitCalyxSection(StringRef sectionName, Func emitBody,
407 StringRef symbolName =
"") {
408 indent() << sectionName;
409 if (!symbolName.empty())
410 os <<
space() << symbolName;
411 emitCalyxBody(emitBody);
415 template <
typename CombinationalOp>
416 void emitCombinationalValue(CombinationalOp op, StringRef logicalSymbol) {
417 auto inputs = op.getInputs();
419 for (
size_t i = 0, e =
inputs.size(); i != e; ++i) {
420 emitValue(
inputs[i],
false);
428 void emitCycleValue(CycleOp op) {
430 if (op.getEnd().has_value()) {
432 os << op.getStart() <<
":" << op.getEnd();
440 void emitValue(Value value,
bool isIndented) {
441 if (
auto blockArg = dyn_cast<BlockArgument>(value)) {
444 (isIndented ? indent() : os) << portName.getValue();
448 auto definingOp = value.getDefiningOp();
449 assert(definingOp &&
"Value does not have a defining operation.");
451 TypeSwitch<Operation *>(definingOp)
452 .Case<CellInterface>([&](
auto cell) {
454 (isIndented ? indent() : os)
455 << cell.instanceName() << period() << cell.portName(value);
457 .Case<hw::ConstantOp>([&](
auto op) {
460 APInt value = op.getValue();
462 (isIndented ? indent() : os)
463 << std::to_string(value.
getBitWidth()) << apostrophe() <<
"d";
465 value.print(os,
false);
467 .Case<comb::AndOp>([&](
auto op) { emitCombinationalValue(op,
"&"); })
468 .Case<comb::OrOp>([&](
auto op) { emitCombinationalValue(op,
"|"); })
469 .Case<comb::XorOp>([&](
auto op) {
472 if (!op.isBinaryNot()) {
473 emitOpError(op,
"Only supporting Binary Not for XOR.");
478 os << exclamationMark();
479 emitValue(op.getInputs()[0],
false);
481 .Case<CycleOp>([&](
auto op) { emitCycleValue(op); })
483 [&](
auto op) { emitOpError(op,
"not supported for emission"); });
487 template <
typename OpTy>
488 void emitGroupPort(GroupInterface group, OpTy op, StringRef portHole) {
489 assert((isa<GroupGoOp>(op) || isa<GroupDoneOp>(op)) &&
490 "Required to be a group port.");
491 indent() << group.symName().getValue() << LSquare() << portHole << RSquare()
494 emitValue(op.getGuard(),
false);
495 os << questionMark();
497 emitValue(op.getSrc(),
false);
498 os << semicolonEndL();
502 void emitCalyxControl(Block *body) {
503 Operation *parent = body->getParentOp();
505 "This should only be used to emit Calyx Control structures.");
509 if (
auto enable = dyn_cast<EnableOp>(parent)) {
515 auto prependAttributes = [&](Operation *op, StringRef sym) {
516 return (getAttributes(op,
true) + sym).str();
519 for (
auto &&op : *body) {
521 TypeSwitch<Operation *>(&op)
522 .Case<SeqOp>([&](
auto op) {
523 emitCalyxSection(prependAttributes(op,
"seq"),
524 [&]() { emitCalyxControl(op.getBodyBlock()); });
526 .Case<StaticSeqOp>([&](
auto op) {
527 emitCalyxSection(prependAttributes(op,
"static seq"),
528 [&]() { emitCalyxControl(op.getBodyBlock()); });
530 .Case<ParOp>([&](
auto op) {
531 emitCalyxSection(prependAttributes(op,
"par"),
532 [&]() { emitCalyxControl(op.getBodyBlock()); });
534 .Case<WhileOp>([&](
auto op) {
535 indent() << prependAttributes(op,
"while ");
536 emitValue(op.getCond(),
false);
538 if (
auto groupName = op.getGroupName())
539 os <<
" with " << *groupName;
541 emitCalyxBody([&]() { emitCalyxControl(op.getBodyBlock()); });
543 .Case<IfOp>([&](
auto op) {
544 indent() << prependAttributes(op,
"if ");
545 emitValue(op.getCond(),
false);
547 if (
auto groupName = op.getGroupName())
548 os <<
" with " << *groupName;
550 emitCalyxBody([&]() { emitCalyxControl(op.getThenBody()); });
551 if (op.elseBodyExists())
552 emitCalyxSection(
"else",
553 [&]() { emitCalyxControl(op.getElseBody()); });
555 .Case<StaticIfOp>([&](
auto op) {
556 indent() << prependAttributes(op,
"static if ");
557 emitValue(op.getCond(),
false);
559 emitCalyxBody([&]() { emitCalyxControl(op.getThenBody()); });
560 if (op.elseBodyExists())
561 emitCalyxSection(
"else",
562 [&]() { emitCalyxControl(op.getElseBody()); });
564 .Case<RepeatOp>([&](
auto op) {
565 indent() << prependAttributes(op,
"repeat ");
568 emitCalyxBody([&]() { emitCalyxControl(op.getBodyBlock()); });
570 .Case<StaticRepeatOp>([&](
auto op) {
571 indent() << prependAttributes(op,
"static repeat ");
574 emitCalyxBody([&]() { emitCalyxControl(op.getBodyBlock()); });
576 .Case<StaticParOp>([&](
auto op) {
577 emitCalyxSection(prependAttributes(op,
"static par"),
578 [&]() { emitCalyxControl(op.getBodyBlock()); });
580 .Case<EnableOp>([&](
auto op) { emitEnable(op); })
581 .Case<InvokeOp>([&](
auto op) { emitInvoke(op); })
582 .Default([&](
auto op) {
583 emitOpError(op,
"not supported for emission inside control.");
589 llvm::raw_ostream &os;
592 bool encounteredError =
false;
596 unsigned currentIndent = 0;
601 LogicalResult Emitter::finalize() {
return failure(encounteredError); }
604 void Emitter::emitModule(ModuleOp op) {
605 for (
auto &bodyOp : *op.getBody()) {
606 if (
auto componentOp = dyn_cast<ComponentInterface>(bodyOp))
607 emitComponent(componentOp);
608 else if (
auto hwModuleExternOp = dyn_cast<hw::HWModuleExternOp>(bodyOp))
609 emitPrimitiveExtern(hwModuleExternOp);
611 emitOpError(&bodyOp,
"Unexpected op");
616 void Emitter::emitComponent(ComponentInterface op) {
617 std::string combinationalPrefix = op.isComb() ?
"comb " :
"";
619 indent() << combinationalPrefix <<
"component " << op.getName()
620 << getAttributes(op,
false,
nullptr);
622 emitComponentPorts(op);
623 os <<
space() << LBraceEndL();
629 emitCalyxSection(
"cells", [&]() {
630 for (
auto &&bodyOp : *op.getBodyBlock()) {
631 TypeSwitch<Operation *>(&bodyOp)
632 .Case<UndefLibOp>([&](auto op) { emitUndef(op); })
633 .Case<WiresOp>([&](
auto op) { wires = op; })
634 .Case<ControlOp>([&](
auto op) { control = op; })
635 .Case<InstanceOp>([&](
auto op) { emitInstance(op); })
636 .Case<PrimitiveOp>([&](
auto op) { emitPrimitive(op); })
637 .Case<RegisterOp>([&](
auto op) { emitRegister(op); })
638 .Case<MemoryOp>([&](
auto op) { emitMemory(op); })
639 .Case<SeqMemoryOp>([&](
auto op) { emitSeqMemory(op); })
640 .Case<hw::ConstantOp>([&](
auto op) { })
641 .Case<SliceLibOp, PadLibOp, ExtSILibOp>(
642 [&](
auto op) { emitLibraryPrimTypedByAllPorts(op); })
643 .Case<LtLibOp, GtLibOp, EqLibOp, NeqLibOp, GeLibOp, LeLibOp, SltLibOp,
644 SgtLibOp, SeqLibOp, SneqLibOp, SgeLibOp, SleLibOp, AddLibOp,
645 SubLibOp, ShruLibOp, RshLibOp, SrshLibOp, LshLibOp, AndLibOp,
646 NotLibOp, OrLibOp, XorLibOp, WireLibOp>(
647 [&](
auto op) { emitLibraryPrimTypedByFirstInputPort(op); })
649 [&](
auto op) { emitLibraryPrimTypedByFirstOutputPort(op); })
650 .Case<MultPipeLibOp>(
651 [&](
auto op) { emitLibraryPrimTypedByFirstOutputPort(op); })
652 .Case<RemUPipeLibOp, DivUPipeLibOp>([&](
auto op) {
653 emitLibraryPrimTypedByFirstOutputPort(
654 op, {
"std_div_pipe"});
656 .Case<RemSPipeLibOp, DivSPipeLibOp>([&](
auto op) {
657 emitLibraryPrimTypedByFirstOutputPort(
658 op, {
"std_sdiv_pipe"});
660 .Default([&](
auto op) {
661 emitOpError(op,
"not supported for emission inside component");
667 emitControl(control);
673 void Emitter::emitComponentPorts(ComponentInterface op) {
674 auto emitPorts = [&](
auto ports) {
676 for (
size_t i = 0, e = ports.size(); i < e; ++i) {
680 unsigned int bitWidth = port.
type.getIntOrFloatBitWidth();
681 os << getAttributes(op,
true, port.
attributes)
682 << port.
name.getValue() << colon() << bitWidth;
689 emitPorts(op.getInputPortInfo());
691 emitPorts(op.getOutputPortInfo());
696 Attribute filename = op->getAttrDictionary().get(
"filename");
697 indent() <<
"extern " << filename <<
space() << LBraceEndL();
699 indent() <<
"primitive " << op.getName();
701 if (!op.getParameters().empty()) {
703 llvm::interleaveComma(op.getParameters(), os, [&](Attribute param) {
704 auto paramAttr = cast<hw::ParamDeclAttr>(param);
705 os << paramAttr.getName().str();
709 os << getAttributes(op,
false);
711 emitPrimitivePorts(op);
712 os << semicolonEndL();
719 auto emitPorts = [&](
auto ports,
bool isInput) {
720 auto e =
static_cast<size_t>(std::distance(ports.begin(), ports.end()));
722 auto type = op.getHWModuleType();
723 for (
auto [i, port] : llvm::enumerate(ports)) {
724 DictionaryAttr portAttr = cast_or_null<DictionaryAttr>(
725 op.getPortAttrs(isInput ? type.getPortIdForInputId(i)
726 : type.getPortIdForOutputId(i)));
728 os << getAttributes(op,
true, portAttr)
729 << port.
name.getValue() << colon();
733 hw::ParamDeclRefAttr bitWidth = dyn_cast<hw::ParamDeclRefAttr>(
734 cast<hw::IntType>(port.
type).getWidth());
735 os << bitWidth.getName().str();
737 unsigned int bitWidth = port.
type.getIntOrFloatBitWidth();
746 hw::ModulePortInfo ports(op.getPortList());
747 emitPorts(ports.getInputs(),
true);
749 emitPorts(ports.getOutputs(),
false);
752 void Emitter::emitInstance(InstanceOp op) {
753 indent() << getAttributes(op,
true) << op.instanceName()
754 <<
space() << equals() <<
space() << op.getComponentName()
755 << LParen() << RParen() << semicolonEndL();
758 void Emitter::emitPrimitive(PrimitiveOp op) {
759 indent() << getAttributes(op,
true) << op.instanceName()
760 <<
space() << equals() <<
space() << op.getPrimitiveName()
763 if (op.getParameters().has_value()) {
764 llvm::interleaveComma(*op.getParameters(), os, [&](Attribute param) {
765 auto paramAttr = cast<hw::ParamDeclAttr>(param);
766 auto value = paramAttr.getValue();
767 if (auto intAttr = dyn_cast<IntegerAttr>(value)) {
768 os << intAttr.getInt();
769 }
else if (
auto fpAttr = dyn_cast<FloatAttr>(value)) {
770 os << fpAttr.getValue().convertToFloat();
772 llvm_unreachable(
"Primitive parameter type not supported");
777 os << RParen() << semicolonEndL();
780 void Emitter::emitRegister(RegisterOp
reg) {
781 size_t bitWidth =
reg.getIn().getType().getIntOrFloatBitWidth();
782 indent() << getAttributes(
reg,
true) <<
reg.instanceName()
783 <<
space() << equals() <<
space() <<
"std_reg" << LParen()
784 << std::to_string(bitWidth) << RParen() << semicolonEndL();
787 void Emitter::emitUndef(UndefLibOp op) {
788 size_t bitwidth = op.getOut().getType().getIntOrFloatBitWidth();
789 indent() << getAttributes(op,
true) << op.instanceName()
790 <<
space() << equals() <<
space() <<
"undef" << LParen()
791 << std::to_string(bitwidth) << RParen() << semicolonEndL();
794 void Emitter::emitMemory(MemoryOp memory) {
795 size_t dimension = memory.getSizes().size();
796 if (dimension < 1 || dimension > 4) {
797 emitOpError(memory,
"Only memories with dimensionality in range [1, 4] are "
798 "supported by the native Calyx compiler.");
801 indent() << getAttributes(memory,
true) << memory.instanceName()
802 <<
space() << equals() <<
space() <<
"std_mem_d"
803 << std::to_string(dimension) << LParen() << memory.getWidth()
805 for (Attribute size : memory.getSizes()) {
806 APInt memSize = cast<IntegerAttr>(size).getValue();
807 memSize.print(os,
false);
811 ArrayAttr addrSizes = memory.getAddrSizes();
812 for (
size_t i = 0, e = addrSizes.size(); i != e; ++i) {
813 APInt addrSize = cast<IntegerAttr>(addrSizes[i]).getValue();
814 addrSize.print(os,
false);
819 os << RParen() << semicolonEndL();
822 void Emitter::emitSeqMemory(SeqMemoryOp 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() <<
"seq_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::emitInvoke(InvokeOp invoke) {
851 StringRef callee = invoke.getCallee();
852 indent() <<
"invoke " << callee;
853 ArrayAttr portNames = invoke.getPortNames();
854 ArrayAttr inputNames = invoke.getInputNames();
858 llvm::StringMap<std::string> inputsMap;
859 llvm::StringMap<std::string> outputsMap;
860 for (
auto [portNameAttr, inputNameAttr, input] :
861 llvm::zip(portNames, inputNames, invoke.getInputs())) {
862 StringRef portName = cast<StringAttr>(portNameAttr).getValue();
863 StringRef inputName = cast<StringAttr>(inputNameAttr).getValue();
871 StringRef inputMapKey = portName.drop_front(2 + callee.size());
872 if (portName.substr(1, callee.size()) == callee) {
874 if (isa_and_nonnull<hw::ConstantOp>(input.getDefiningOp())) {
875 hw::ConstantOp constant = cast<hw::ConstantOp>(input.getDefiningOp());
876 APInt value = constant.getValue();
877 std::string mapValue = std::to_string(value.getBitWidth()) +
878 apostrophe().data() +
"d" +
879 std::to_string(value.getZExtValue());
880 inputsMap[inputMapKey] = mapValue;
883 inputsMap[inputMapKey] = inputName.drop_front(1).str();
884 }
else if (inputName.substr(1, callee.size()) == callee)
885 outputsMap[inputName.drop_front(2 + callee.size())] =
886 portName.drop_front(1).str();
890 llvm::interleaveComma(inputsMap, os, [&](
const auto &iter) {
891 os << iter.getKey() <<
" = " << iter.getValue();
896 llvm::interleaveComma(outputsMap, os, [&](
const auto &iter) {
897 os << iter.getKey() <<
" = " << iter.getValue();
899 os << RParen() << semicolonEndL();
907 void Emitter::emitLibraryPrimTypedByAllPorts(Operation *op) {
908 auto cell = cast<CellInterface>(op);
909 indent() << getAttributes(op,
true) << cell.instanceName()
912 llvm::interleaveComma(op->getResults(), os, [&](
auto res) {
913 os << std::to_string(res.getType().getIntOrFloatBitWidth());
915 os << RParen() << semicolonEndL();
918 void Emitter::emitLibraryPrimTypedByFirstInputPort(Operation *op) {
919 auto cell = cast<CellInterface>(op);
920 unsigned bitWidth = cell.getInputPorts()[0].getType().getIntOrFloatBitWidth();
921 StringRef opName = op->getName().getStringRef();
922 indent() << getAttributes(op,
true) << cell.instanceName()
924 << LParen() << bitWidth << RParen() << semicolonEndL();
927 void Emitter::emitLibraryPrimTypedByFirstOutputPort(
928 Operation *op, std::optional<StringRef> calyxLibName) {
929 auto cell = cast<CellInterface>(op);
931 cell.getOutputPorts()[0].getType().getIntOrFloatBitWidth();
932 StringRef opName = op->getName().getStringRef();
933 indent() << getAttributes(op,
true) << cell.instanceName()
936 << LParen() << bitWidth << RParen() << semicolonEndL();
939 void Emitter::emitAssignment(AssignOp op) {
941 emitValue(op.getDest(),
true);
944 emitValue(op.getGuard(),
false);
945 os << questionMark();
947 emitValue(op.getSrc(),
false);
948 os << semicolonEndL();
951 void Emitter::emitWires(WiresOp op) {
952 emitCalyxSection(
"wires", [&]() {
953 for (
auto &&bodyOp : *op.getBodyBlock()) {
954 TypeSwitch<Operation *>(&bodyOp)
955 .Case<GroupInterface>([&](auto op) { emitGroup(op); })
956 .Case<AssignOp>([&](
auto op) { emitAssignment(op); })
957 .Case<hw::ConstantOp, comb::AndOp, comb::OrOp, comb::XorOp, CycleOp>(
959 .Default([&](
auto op) {
960 emitOpError(op,
"not supported for emission inside wires section");
966 void Emitter::emitGroup(GroupInterface group) {
967 auto emitGroupBody = [&]() {
968 for (
auto &&bodyOp : *group.getBody()) {
969 TypeSwitch<Operation *>(&bodyOp)
970 .Case<AssignOp>([&](
auto op) { emitAssignment(op); })
971 .Case<GroupDoneOp>([&](
auto op) { emitGroupPort(group, op,
"done"); })
972 .Case<GroupGoOp>([&](
auto op) { emitGroupPort(group, op,
"go"); })
973 .Case<hw::ConstantOp, comb::AndOp, comb::OrOp, comb::XorOp, CycleOp>(
975 .Default([&](
auto op) {
976 emitOpError(op,
"not supported for emission inside group.");
981 if (isa<StaticGroupOp>(group)) {
982 auto staticGroup = cast<StaticGroupOp>(group);
983 prefix = llvm::formatv(
"static<{0}> group", staticGroup.getLatency());
985 prefix = isa<CombGroupOp>(group) ?
"comb group" :
"group";
988 (group.symName().getValue() + getAttributes(group,
false))
990 emitCalyxSection(prefix, emitGroupBody, groupHeader);
993 void Emitter::emitEnable(EnableOp enable) {
994 indent() << getAttributes(enable,
true) << enable.getGroupName()
998 void Emitter::emitControl(ControlOp control) {
1000 if (control ==
nullptr)
1002 emitCalyxSection(
"control",
1003 [&]() { emitCalyxControl(control.getBodyBlock()); });
1012 llvm::raw_ostream &os) {
1013 Emitter emitter(os);
1014 if (failed(emitter.emitImports(module)))
1016 emitter.emitModule(module);
1017 emitter.emitCiderMetadata(module);
1018 return emitter.finalize();
1022 static mlir::TranslateFromMLIRRegistration toCalyx(
1023 "export-calyx",
"export Calyx",
1024 [](ModuleOp module, llvm::raw_ostream &os) {
1027 [](mlir::DialectRegistry ®istry) {
1029 .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.
llvm::SmallVector< StringAttr > inputs
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