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 delimiter() {
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",
"done",
"go",
"reset",
"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();
90 static std::optional<StringRef> unsupportedOpInfo(Operation *op) {
91 return llvm::TypeSwitch<Operation *, std::optional<StringRef>>(op)
92 .Case<ExtSILibOp>([](
auto) -> std::optional<StringRef> {
93 static constexpr std::string_view info =
94 "calyx.std_extsi is currently not available in the native Rust "
95 "compiler (see github.com/cucapra/calyx/issues/1009)";
98 .Default([](
auto) {
return std::nullopt; });
103 struct ImportTracker {
108 auto walkRes = module.walk([&](ComponentOp component) {
109 for (
auto &op : *component.getBodyBlock()) {
110 if (!isa<CellInterface>(op) || isa<InstanceOp, PrimitiveOp>(op))
113 auto libraryName = getLibraryFor(&op);
114 if (failed(libraryName))
115 return WalkResult::interrupt();
116 usedLibraries.insert(*libraryName);
118 return WalkResult::advance();
120 if (walkRes.wasInterrupted())
122 return usedLibraries;
128 return TypeSwitch<Operation *, FailureOr<StringRef>>(op)
129 .Case<MemoryOp, RegisterOp, NotLibOp, AndLibOp, OrLibOp, XorLibOp,
130 AddLibOp, SubLibOp, GtLibOp, LtLibOp, EqLibOp, NeqLibOp, GeLibOp,
131 LeLibOp, LshLibOp, RshLibOp, SliceLibOp, PadLibOp, WireLibOp,
133 static constexpr std::string_view sCore =
"core";
136 .Case<SgtLibOp, SltLibOp, SeqLibOp, SneqLibOp, SgeLibOp, SleLibOp,
137 SrshLibOp, MultPipeLibOp, RemUPipeLibOp, RemSPipeLibOp,
138 DivUPipeLibOp, DivSPipeLibOp>(
140 static constexpr std::string_view sBinaryOperators =
142 return {sBinaryOperators};
145 static constexpr std::string_view sMemories =
"memories";
149 .Default([&](
auto op) {
150 auto diag = op->emitOpError() <<
"not supported for emission";
151 auto note = unsupportedOpInfo(op);
153 diag.attachNote() << *note;
168 Emitter(llvm::raw_ostream &os) : os(os) {}
169 LogicalResult finalize();
172 raw_ostream &indent() {
return os.indent(currentIndent); }
173 void addIndent() { currentIndent += 2; }
174 void reduceIndent() {
175 assert(currentIndent >= 2 &&
"Unintended indentation wrap");
180 void emitModule(ModuleOp op);
183 void emitCiderMetadata(mlir::ModuleOp op) {
184 auto metadata = op->getAttrOfType<ArrayAttr>(
"calyx.metadata");
188 constexpr std::string_view metadataIdentifier =
"metadata";
189 os << endl() << metadataIdentifier <<
space() << metadataLBrace();
191 for (
auto sourceLoc : llvm::enumerate(metadata)) {
193 os << std::to_string(sourceLoc.index()) << colon();
194 os << sourceLoc.value().cast<StringAttr>().getValue() << endl();
197 os << metadataRBrace();
201 LogicalResult emitImports(ModuleOp op) {
202 auto emitImport = [&](StringRef library) {
205 os <<
"import " << delimiter() <<
"primitives/" << library << period()
206 <<
"futil" << delimiter() << semicolonEndL();
209 auto libraryNames = importTracker.getLibraryNames(op);
210 if (failed(libraryNames))
213 for (StringRef library : *libraryNames)
220 void emitComponent(ComponentInterface op);
221 void emitComponentPorts(ComponentInterface op);
224 void emitPrimitiveExtern(hw::HWModuleExternOp op);
225 void emitPrimitivePorts(hw::HWModuleExternOp op);
228 void emitInstance(InstanceOp op);
231 void emitPrimitive(PrimitiveOp op);
234 void emitWires(WiresOp op);
237 void emitGroup(GroupInterface group);
240 void emitControl(ControlOp control);
243 void emitAssignment(AssignOp op);
246 void emitEnable(EnableOp enable);
249 void emitRegister(RegisterOp
reg);
252 void emitMemory(MemoryOp memory);
255 void emitSeqMemory(SeqMemoryOp memory);
258 void emitInvoke(InvokeOp invoke);
266 void emitLibraryPrimTypedByAllPorts(Operation *op);
274 void emitLibraryPrimTypedByFirstInputPort(Operation *op);
282 void emitLibraryPrimTypedByFirstOutputPort(
283 Operation *op, std::optional<StringRef> calyxLibName = {});
287 ImportTracker importTracker;
290 InFlightDiagnostic emitError(Operation *op,
const Twine &message) {
291 encounteredError =
true;
292 return op->emitError(message);
296 InFlightDiagnostic emitOpError(Operation *op,
const Twine &message) {
297 encounteredError =
true;
298 return op->emitOpError(message);
310 std::optional<StringRef> identifierOpt = getCalyxAttrIdentifier(attr);
312 if (!identifierOpt.has_value())
315 StringRef identifier = *identifierOpt;
317 if (!isValidCalyxAttribute(identifier))
321 bool isGroupOrComponentAttr = isa<GroupOp, ComponentOp>(op) && !
isPort;
324 llvm::raw_string_ostream buffer(output);
325 buffer.reserveExtraSpace(16);
327 bool isBooleanAttribute =
328 llvm::find(booleanAttributes, identifier) != booleanAttributes.end();
329 if (attr.getValue().isa<UnitAttr>()) {
330 assert(isBooleanAttribute &&
331 "Non-boolean attributes must provide an integer value.");
332 if (isGroupOrComponentAttr) {
333 buffer << LAngleBracket() << delimiter() << identifier << delimiter()
334 << equals() <<
"1" << RAngleBracket();
336 buffer << addressSymbol() << identifier <<
space();
338 }
else if (
auto intAttr = attr.getValue().dyn_cast<IntegerAttr>()) {
339 APInt value = intAttr.getValue();
340 if (isGroupOrComponentAttr) {
341 buffer << LAngleBracket() << delimiter() << identifier << delimiter()
342 << equals() << value << RAngleBracket();
344 buffer << addressSymbol() << identifier;
347 if (!isBooleanAttribute || intAttr.getValue() != 1) {
349 SmallVector<char, 4> s;
350 value.toStringUnsigned(s, 10);
351 buffer << LParen() << s << RParen();
361 std::string getAttributes(Operation *op,
362 DictionaryAttr attributes =
nullptr) {
363 bool isPort = attributes !=
nullptr;
365 attributes = op->getAttrDictionary();
367 SmallString<16> calyxAttributes;
368 for (
auto &attr : attributes)
371 return calyxAttributes.c_str();
379 template <
typename Func>
380 void emitCalyxBody(Func emitBody) {
381 os <<
space() << LBraceEndL();
385 indent() << RBraceEndL();
389 template <
typename Func>
390 void emitCalyxSection(StringRef sectionName, Func emitBody,
391 StringRef symbolName =
"") {
392 indent() << sectionName;
393 if (!symbolName.empty())
394 os <<
space() << symbolName;
395 emitCalyxBody(emitBody);
399 template <
typename CombinationalOp>
400 void emitCombinationalValue(CombinationalOp op, StringRef logicalSymbol) {
401 auto inputs = op.getInputs();
403 for (
size_t i = 0, e =
inputs.size(); i != e; ++i) {
404 emitValue(
inputs[i],
false);
412 void emitCycleValue(CycleOp op) {
414 if (op.getEnd().has_value()) {
416 os << op.getStart() <<
":" << op.getEnd();
424 void emitValue(Value value,
bool isIndented) {
425 if (
auto blockArg = value.dyn_cast<BlockArgument>()) {
428 (isIndented ? indent() : os) << portName.getValue();
432 auto definingOp = value.getDefiningOp();
433 assert(definingOp &&
"Value does not have a defining operation.");
435 TypeSwitch<Operation *>(definingOp)
436 .Case<CellInterface>([&](
auto cell) {
438 (isIndented ? indent() : os)
439 << cell.instanceName() << period() << cell.portName(value);
441 .Case<hw::ConstantOp>([&](
auto op) {
444 APInt value = op.getValue();
446 (isIndented ? indent() : os)
447 << std::to_string(value.
getBitWidth()) << apostrophe() <<
"d";
449 value.print(os,
false);
451 .Case<comb::AndOp>([&](
auto op) { emitCombinationalValue(op,
"&"); })
452 .Case<comb::OrOp>([&](
auto op) { emitCombinationalValue(op,
"|"); })
453 .Case<comb::XorOp>([&](
auto op) {
456 if (!op.isBinaryNot()) {
457 emitOpError(op,
"Only supporting Binary Not for XOR.");
462 os << exclamationMark();
463 emitValue(op.getInputs()[0],
false);
465 .Case<CycleOp>([&](
auto op) { emitCycleValue(op); })
467 [&](
auto op) { emitOpError(op,
"not supported for emission"); });
471 template <
typename OpTy>
472 void emitGroupPort(GroupInterface group, OpTy op, StringRef portHole) {
473 assert((isa<GroupGoOp>(op) || isa<GroupDoneOp>(op)) &&
474 "Required to be a group port.");
475 indent() << group.symName().getValue() << LSquare() << portHole << RSquare()
478 emitValue(op.getGuard(),
false);
479 os << questionMark();
481 emitValue(op.getSrc(),
false);
482 os << semicolonEndL();
486 void emitCalyxControl(Block *body) {
487 Operation *parent = body->getParentOp();
489 "This should only be used to emit Calyx Control structures.");
493 if (
auto enable = dyn_cast<EnableOp>(parent)) {
499 auto prependAttributes = [&](Operation *op, StringRef sym) {
500 return (getAttributes(op) + sym).str();
503 for (
auto &&op : *body) {
505 TypeSwitch<Operation *>(&op)
506 .Case<SeqOp>([&](
auto op) {
507 emitCalyxSection(prependAttributes(op,
"seq"),
508 [&]() { emitCalyxControl(op.getBodyBlock()); });
510 .Case<StaticSeqOp>([&](
auto op) {
511 emitCalyxSection(prependAttributes(op,
"static seq"),
512 [&]() { emitCalyxControl(op.getBodyBlock()); });
514 .Case<ParOp>([&](
auto op) {
515 emitCalyxSection(prependAttributes(op,
"par"),
516 [&]() { emitCalyxControl(op.getBodyBlock()); });
518 .Case<WhileOp>([&](
auto op) {
519 indent() << prependAttributes(op,
"while ");
520 emitValue(op.getCond(),
false);
522 if (
auto groupName = op.getGroupName())
523 os <<
" with " << *groupName;
525 emitCalyxBody([&]() { emitCalyxControl(op.getBodyBlock()); });
527 .Case<IfOp>([&](
auto op) {
528 indent() << prependAttributes(op,
"if ");
529 emitValue(op.getCond(),
false);
531 if (
auto groupName = op.getGroupName())
532 os <<
" with " << *groupName;
534 emitCalyxBody([&]() { emitCalyxControl(op.getThenBody()); });
535 if (op.elseBodyExists())
536 emitCalyxSection(
"else",
537 [&]() { emitCalyxControl(op.getElseBody()); });
539 .Case<StaticIfOp>([&](
auto op) {
540 indent() << prependAttributes(op,
"static if ");
541 emitValue(op.getCond(),
false);
543 emitCalyxBody([&]() { emitCalyxControl(op.getThenBody()); });
544 if (op.elseBodyExists())
545 emitCalyxSection(
"else",
546 [&]() { emitCalyxControl(op.getElseBody()); });
548 .Case<RepeatOp>([&](
auto op) {
549 indent() << prependAttributes(op,
"repeat ");
552 emitCalyxBody([&]() { emitCalyxControl(op.getBodyBlock()); });
554 .Case<StaticRepeatOp>([&](
auto op) {
555 indent() << prependAttributes(op,
"static repeat ");
558 emitCalyxBody([&]() { emitCalyxControl(op.getBodyBlock()); });
560 .Case<StaticParOp>([&](
auto op) {
561 emitCalyxSection(prependAttributes(op,
"static par"),
562 [&]() { emitCalyxControl(op.getBodyBlock()); });
564 .Case<EnableOp>([&](
auto op) { emitEnable(op); })
565 .Case<InvokeOp>([&](
auto op) { emitInvoke(op); })
566 .Default([&](
auto op) {
567 emitOpError(op,
"not supported for emission inside control.");
573 llvm::raw_ostream &os;
576 bool encounteredError =
false;
580 unsigned currentIndent = 0;
585 LogicalResult Emitter::finalize() {
return failure(encounteredError); }
588 void Emitter::emitModule(ModuleOp op) {
589 for (
auto &bodyOp : *op.getBody()) {
590 if (
auto componentOp = dyn_cast<ComponentInterface>(bodyOp))
591 emitComponent(componentOp);
592 else if (
auto hwModuleExternOp = dyn_cast<hw::HWModuleExternOp>(bodyOp))
593 emitPrimitiveExtern(hwModuleExternOp);
595 emitOpError(&bodyOp,
"Unexpected op");
600 void Emitter::emitComponent(ComponentInterface op) {
601 std::string combinationalPrefix = op.isComb() ?
"comb " :
"";
603 indent() << combinationalPrefix <<
"component " << op.getName()
604 << getAttributes(op);
606 emitComponentPorts(op);
607 os <<
space() << LBraceEndL();
613 emitCalyxSection(
"cells", [&]() {
614 for (
auto &&bodyOp : *op.getBodyBlock()) {
615 TypeSwitch<Operation *>(&bodyOp)
616 .Case<WiresOp>([&](auto op) { wires = op; })
617 .Case<ControlOp>([&](
auto op) { control = op; })
618 .Case<InstanceOp>([&](
auto op) { emitInstance(op); })
619 .Case<PrimitiveOp>([&](
auto op) { emitPrimitive(op); })
620 .Case<RegisterOp>([&](
auto op) { emitRegister(op); })
621 .Case<MemoryOp>([&](
auto op) { emitMemory(op); })
622 .Case<SeqMemoryOp>([&](
auto op) { emitSeqMemory(op); })
623 .Case<hw::ConstantOp>([&](
auto op) { })
624 .Case<SliceLibOp, PadLibOp>(
625 [&](
auto op) { emitLibraryPrimTypedByAllPorts(op); })
626 .Case<LtLibOp, GtLibOp, EqLibOp, NeqLibOp, GeLibOp, LeLibOp, SltLibOp,
627 SgtLibOp, SeqLibOp, SneqLibOp, SgeLibOp, SleLibOp, AddLibOp,
628 SubLibOp, ShruLibOp, RshLibOp, SrshLibOp, LshLibOp, AndLibOp,
629 NotLibOp, OrLibOp, XorLibOp, WireLibOp>(
630 [&](
auto op) { emitLibraryPrimTypedByFirstInputPort(op); })
632 [&](
auto op) { emitLibraryPrimTypedByFirstOutputPort(op); })
633 .Case<MultPipeLibOp>(
634 [&](
auto op) { emitLibraryPrimTypedByFirstOutputPort(op); })
635 .Case<RemUPipeLibOp, DivUPipeLibOp>([&](
auto op) {
636 emitLibraryPrimTypedByFirstOutputPort(
637 op, {
"std_div_pipe"});
639 .Case<RemSPipeLibOp, DivSPipeLibOp>([&](
auto op) {
640 emitLibraryPrimTypedByFirstOutputPort(
641 op, {
"std_sdiv_pipe"});
643 .Default([&](
auto op) {
644 emitOpError(op,
"not supported for emission inside component");
650 emitControl(control);
656 void Emitter::emitComponentPorts(ComponentInterface op) {
657 auto emitPorts = [&](
auto ports) {
659 for (
size_t i = 0, e = ports.size(); i < e; ++i) {
663 unsigned int bitWidth = port.
type.getIntOrFloatBitWidth();
665 << colon() << bitWidth;
672 emitPorts(op.getInputPortInfo());
674 emitPorts(op.getOutputPortInfo());
678 void Emitter::emitPrimitiveExtern(hw::HWModuleExternOp op) {
679 Attribute filename = op->getAttrDictionary().get(
"filename");
680 indent() <<
"extern " << filename <<
space() << LBraceEndL();
682 indent() <<
"primitive " << op.getName();
684 if (!op.getParameters().empty()) {
686 llvm::interleaveComma(op.getParameters(), os, [&](Attribute param) {
687 auto paramAttr = param.cast<hw::ParamDeclAttr>();
688 os << paramAttr.getName().str();
692 os << getAttributes(op);
694 emitPrimitivePorts(op);
695 os << semicolonEndL();
701 void Emitter::emitPrimitivePorts(hw::HWModuleExternOp op) {
702 auto emitPorts = [&](
auto ports,
bool isInput) {
703 auto e =
static_cast<size_t>(std::distance(ports.begin(), ports.end()));
705 auto type = op.getHWModuleType();
706 for (
auto [i, port] : llvm::enumerate(ports)) {
707 DictionaryAttr portAttr = cast_or_null<DictionaryAttr>(
708 op.getPortAttrs(isInput ? type.getPortIdForInputId(i)
709 : type.getPortIdForOutputId(i)));
711 os << getAttributes(op, portAttr) << port.
name.getValue() << colon();
715 hw::ParamDeclRefAttr bitWidth =
716 port.
type.template cast<hw::IntType>()
718 .template dyn_cast<hw::ParamDeclRefAttr>();
719 os << bitWidth.getName().str();
721 unsigned int bitWidth = port.
type.getIntOrFloatBitWidth();
730 auto ports = op.getPortList();
731 emitPorts(ports.getInputs(),
true);
733 emitPorts(ports.getOutputs(),
false);
736 void Emitter::emitInstance(InstanceOp op) {
737 indent() << getAttributes(op) << op.instanceName() <<
space() << equals()
738 <<
space() << op.getComponentName() << LParen() << RParen()
742 void Emitter::emitPrimitive(PrimitiveOp op) {
743 indent() << getAttributes(op) << op.instanceName() <<
space() << equals()
744 <<
space() << op.getPrimitiveName() << LParen();
746 if (op.getParameters().has_value()) {
747 llvm::interleaveComma(*op.getParameters(), os, [&](Attribute param) {
748 auto paramAttr = param.cast<hw::ParamDeclAttr>();
749 auto value = paramAttr.getValue();
750 if (auto intAttr = value.dyn_cast<IntegerAttr>()) {
751 os << intAttr.getInt();
752 }
else if (
auto fpAttr = value.dyn_cast<FloatAttr>()) {
753 os << fpAttr.getValue().convertToFloat();
755 llvm_unreachable(
"Primitive parameter type not supported");
760 os << RParen() << semicolonEndL();
763 void Emitter::emitRegister(RegisterOp
reg) {
764 size_t bitWidth =
reg.getIn().getType().getIntOrFloatBitWidth();
765 indent() << getAttributes(
reg) <<
reg.instanceName() <<
space() << equals()
766 <<
space() <<
"std_reg" << LParen() << std::to_string(bitWidth)
767 << RParen() << semicolonEndL();
770 void Emitter::emitMemory(MemoryOp memory) {
771 size_t dimension = memory.getSizes().size();
772 if (dimension < 1 || dimension > 4) {
773 emitOpError(memory,
"Only memories with dimensionality in range [1, 4] are "
774 "supported by the native Calyx compiler.");
777 indent() << getAttributes(memory) << memory.instanceName() <<
space()
778 << equals() <<
space() <<
"std_mem_d" << std::to_string(dimension)
779 << LParen() << memory.getWidth() << comma();
780 for (Attribute
size : memory.getSizes()) {
781 APInt memSize =
size.cast<IntegerAttr>().getValue();
782 memSize.print(os,
false);
786 ArrayAttr addrSizes = memory.getAddrSizes();
787 for (
size_t i = 0, e = addrSizes.size(); i != e; ++i) {
788 APInt addrSize = addrSizes[i].cast<IntegerAttr>().getValue();
789 addrSize.print(os,
false);
794 os << RParen() << semicolonEndL();
797 void Emitter::emitSeqMemory(SeqMemoryOp memory) {
798 size_t dimension = memory.getSizes().size();
799 if (dimension < 1 || dimension > 4) {
800 emitOpError(memory,
"Only memories with dimensionality in range [1, 4] are "
801 "supported by the native Calyx compiler.");
804 indent() << getAttributes(memory) << memory.instanceName() <<
space()
805 << equals() <<
space() <<
"seq_mem_d" << std::to_string(dimension)
806 << LParen() << memory.getWidth() << comma();
807 for (Attribute
size : memory.getSizes()) {
808 APInt memSize =
size.cast<IntegerAttr>().getValue();
809 memSize.print(os,
false);
813 ArrayAttr addrSizes = memory.getAddrSizes();
814 for (
size_t i = 0, e = addrSizes.size(); i != e; ++i) {
815 APInt addrSize = addrSizes[i].cast<IntegerAttr>().getValue();
816 addrSize.print(os,
false);
821 os << RParen() << semicolonEndL();
824 void Emitter::emitInvoke(InvokeOp invoke) {
825 StringRef callee = invoke.getCallee();
826 indent() <<
"invoke " << callee;
827 ArrayAttr portNames = invoke.getPortNames();
828 ArrayAttr inputNames = invoke.getInputNames();
832 llvm::StringMap<std::string> inputsMap;
833 llvm::StringMap<std::string> outputsMap;
834 for (
auto [portNameAttr, inputNameAttr, input] :
835 llvm::zip(portNames, inputNames, invoke.getInputs())) {
836 StringRef portName = cast<StringAttr>(portNameAttr).getValue();
837 StringRef inputName = cast<StringAttr>(inputNameAttr).getValue();
845 StringRef inputMapKey = portName.drop_front(2 + callee.size());
846 if (portName.substr(1, callee.size()) == callee) {
848 if (isa_and_nonnull<hw::ConstantOp>(input.getDefiningOp())) {
849 hw::ConstantOp constant = cast<hw::ConstantOp>(input.getDefiningOp());
850 APInt value = constant.getValue();
851 std::string mapValue = std::to_string(value.getBitWidth()) +
852 apostrophe().data() +
"d" +
853 std::to_string(value.getZExtValue());
854 inputsMap[inputMapKey] = mapValue;
857 inputsMap[inputMapKey] = inputName.drop_front(1).str();
858 }
else if (inputName.substr(1, callee.size()) == callee)
859 outputsMap[inputName.drop_front(2 + callee.size())] =
860 portName.drop_front(1).str();
864 llvm::interleaveComma(inputsMap, os, [&](
const auto &iter) {
865 os << iter.getKey() <<
" = " << iter.getValue();
870 llvm::interleaveComma(outputsMap, os, [&](
const auto &iter) {
871 os << iter.getKey() <<
" = " << iter.getValue();
873 os << RParen() << semicolonEndL();
881 void Emitter::emitLibraryPrimTypedByAllPorts(Operation *op) {
882 auto cell = cast<CellInterface>(op);
883 indent() << getAttributes(op) << cell.instanceName() <<
space() << equals()
886 llvm::interleaveComma(op->getResults(), os, [&](
auto res) {
887 os << std::to_string(res.getType().getIntOrFloatBitWidth());
889 os << RParen() << semicolonEndL();
892 void Emitter::emitLibraryPrimTypedByFirstInputPort(Operation *op) {
893 auto cell = cast<CellInterface>(op);
894 unsigned bitWidth = cell.getInputPorts()[0].getType().getIntOrFloatBitWidth();
895 StringRef opName = op->getName().getStringRef();
896 indent() << getAttributes(op) << cell.instanceName() <<
space() << equals()
898 << RParen() << semicolonEndL();
901 void Emitter::emitLibraryPrimTypedByFirstOutputPort(
902 Operation *op, std::optional<StringRef> calyxLibName) {
903 auto cell = cast<CellInterface>(op);
905 cell.getOutputPorts()[0].getType().getIntOrFloatBitWidth();
906 StringRef opName = op->getName().getStringRef();
907 indent() << getAttributes(op) << cell.instanceName() <<
space() << equals()
910 << LParen() << bitWidth << RParen() << semicolonEndL();
913 void Emitter::emitAssignment(AssignOp op) {
915 emitValue(op.getDest(),
true);
918 emitValue(op.getGuard(),
false);
919 os << questionMark();
921 emitValue(op.getSrc(),
false);
922 os << semicolonEndL();
925 void Emitter::emitWires(WiresOp op) {
926 emitCalyxSection(
"wires", [&]() {
927 for (
auto &&bodyOp : *op.getBodyBlock()) {
928 TypeSwitch<Operation *>(&bodyOp)
929 .Case<GroupInterface>([&](auto op) { emitGroup(op); })
930 .Case<AssignOp>([&](
auto op) { emitAssignment(op); })
931 .Case<hw::ConstantOp, comb::AndOp, comb::OrOp, comb::XorOp, CycleOp>(
933 .Default([&](
auto op) {
934 emitOpError(op,
"not supported for emission inside wires section");
940 void Emitter::emitGroup(GroupInterface group) {
941 auto emitGroupBody = [&]() {
942 for (
auto &&bodyOp : *group.getBody()) {
943 TypeSwitch<Operation *>(&bodyOp)
944 .Case<AssignOp>([&](
auto op) { emitAssignment(op); })
945 .Case<GroupDoneOp>([&](
auto op) { emitGroupPort(group, op,
"done"); })
946 .Case<GroupGoOp>([&](
auto op) { emitGroupPort(group, op,
"go"); })
947 .Case<hw::ConstantOp, comb::AndOp, comb::OrOp, comb::XorOp, CycleOp>(
949 .Default([&](
auto op) {
950 emitOpError(op,
"not supported for emission inside group.");
955 if (isa<StaticGroupOp>(group)) {
956 auto staticGroup = cast<StaticGroupOp>(group);
957 prefix = llvm::formatv(
"static<{0}> group", staticGroup.getLatency());
959 prefix = isa<CombGroupOp>(group) ?
"comb group" :
"group";
961 auto groupHeader = (group.symName().getValue() + getAttributes(group)).str();
962 emitCalyxSection(prefix, emitGroupBody, groupHeader);
965 void Emitter::emitEnable(EnableOp enable) {
966 indent() << getAttributes(enable) << enable.getGroupName() << semicolonEndL();
969 void Emitter::emitControl(ControlOp control) {
971 if (control ==
nullptr)
973 emitCalyxSection(
"control",
974 [&]() { emitCalyxControl(control.getBodyBlock()); });
983 llvm::raw_ostream &os) {
985 if (failed(emitter.emitImports(module)))
987 emitter.emitModule(module);
988 emitter.emitCiderMetadata(module);
989 return emitter.finalize();
993 static mlir::TranslateFromMLIRRegistration toCalyx(
994 "export-calyx",
"export Calyx",
995 [](ModuleOp module, llvm::raw_ostream &os) {
998 [](mlir::DialectRegistry ®istry) {
1000 .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.
static Attribute getAttribute(StringRef name, ArrayRef< NamedAttribute > attrs)
llvm::SmallVector< StringAttr > inputs
static int64_t size(hw::ArrayType mType, capnp::schema::Field::Reader cField)
Returns the expected size of an array (capnp list) in 64-bit words.
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.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
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