21 #include "mlir/IR/BuiltinOps.h"
22 #include "mlir/Tools/mlir-translate/Translation.h"
23 #include "llvm/ADT/APSInt.h"
24 #include "llvm/ADT/StringSet.h"
25 #include "llvm/ADT/TypeSwitch.h"
26 #include "llvm/Support/Debug.h"
28 #define DEBUG_TYPE "export-firrtl"
30 using namespace circt;
31 using namespace firrtl;
32 using namespace chirrtl;
33 using namespace pretty;
42 constexpr
size_t defaultTargetLineLength = 80;
46 Emitter(llvm::raw_ostream &os,
FIRVersion version,
47 size_t targetLineLength = defaultTargetLineLength)
48 : pp(os, targetLineLength), ps(pp, saver), version(version) {
49 pp.setListener(&saver);
51 LogicalResult finalize();
54 void emitCircuit(CircuitOp op);
55 void emitModule(FModuleOp op);
56 void emitModule(FExtModuleOp op);
57 void emitModule(FIntModuleOp op);
58 void emitModulePorts(ArrayRef<PortInfo> ports,
59 Block::BlockArgListType arguments = {});
60 void emitModuleParameters(Operation *op, ArrayAttr parameters);
61 void emitDeclaration(LayerOp op);
62 void emitDeclaration(OptionOp op);
63 void emitDeclaration(FormalOp op);
64 void emitEnabledLayers(ArrayRef<Attribute> layers);
66 void emitParamAssign(ParamDeclAttr param, Operation *op,
67 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
68 void emitParamValue(Attribute value, Operation *op);
70 void emitGenericIntrinsic(GenericIntrinsicOp op);
73 void emitStatementsInBlock(
Block &block);
74 void emitStatement(WhenOp op);
75 void emitStatement(WireOp op);
76 void emitStatement(RegOp op);
77 void emitStatement(RegResetOp op);
78 void emitStatement(NodeOp op);
79 void emitStatement(StopOp op);
80 void emitStatement(SkipOp op);
81 void emitStatement(PrintFOp op);
82 void emitStatement(ConnectOp op);
83 void emitStatement(MatchingConnectOp op);
84 void emitStatement(PropAssignOp op);
85 void emitStatement(InstanceOp op);
86 void emitStatement(InstanceChoiceOp op);
87 void emitStatement(AttachOp op);
88 void emitStatement(MemOp op);
89 void emitStatement(InvalidValueOp op);
90 void emitStatement(CombMemOp op);
91 void emitStatement(SeqMemOp op);
92 void emitStatement(MemoryPortOp op);
93 void emitStatement(MemoryDebugPortOp op);
94 void emitStatement(MemoryPortAccessOp op);
95 void emitStatement(RefDefineOp op);
96 void emitStatement(RefForceOp op);
97 void emitStatement(RefForceInitialOp op);
98 void emitStatement(RefReleaseOp op);
99 void emitStatement(RefReleaseInitialOp op);
100 void emitStatement(LayerBlockOp op);
101 void emitStatement(GenericIntrinsicOp op);
104 void emitVerifStatement(T op, StringRef mnemonic);
105 void emitStatement(AssertOp op) { emitVerifStatement(op,
"assert"); }
106 void emitStatement(AssumeOp op) { emitVerifStatement(op,
"assume"); }
107 void emitStatement(CoverOp op) { emitVerifStatement(op,
"cover"); }
110 void emitExpression(Value value);
111 void emitExpression(ConstantOp op);
112 void emitExpression(SpecialConstantOp op);
113 void emitExpression(SubfieldOp op);
114 void emitExpression(SubindexOp op);
115 void emitExpression(SubaccessOp op);
116 void emitExpression(OpenSubfieldOp op);
117 void emitExpression(OpenSubindexOp op);
118 void emitExpression(RefResolveOp op);
119 void emitExpression(RefSendOp op);
120 void emitExpression(RefSubOp op);
121 void emitExpression(RWProbeOp op);
122 void emitExpression(RefCastOp op);
123 void emitExpression(UninferredResetCastOp op);
124 void emitExpression(ConstCastOp op);
125 void emitExpression(StringConstantOp op);
126 void emitExpression(FIntegerConstantOp op);
127 void emitExpression(BoolConstantOp op);
128 void emitExpression(DoubleConstantOp op);
129 void emitExpression(ListCreateOp op);
130 void emitExpression(UnresolvedPathOp op);
131 void emitExpression(GenericIntrinsicOp op);
133 void emitPrimExpr(StringRef mnemonic, Operation *op,
134 ArrayRef<uint32_t> attrs = {});
136 void emitExpression(BitsPrimOp op) {
137 emitPrimExpr(
"bits", op, {op.getHi(), op.getLo()});
139 void emitExpression(HeadPrimOp op) {
140 emitPrimExpr(
"head", op, op.getAmount());
142 void emitExpression(TailPrimOp op) {
143 emitPrimExpr(
"tail", op, op.getAmount());
145 void emitExpression(PadPrimOp op) { emitPrimExpr(
"pad", op, op.getAmount()); }
146 void emitExpression(ShlPrimOp op) { emitPrimExpr(
"shl", op, op.getAmount()); }
147 void emitExpression(ShrPrimOp op) { emitPrimExpr(
"shr", op, op.getAmount()); }
150 #define HANDLE(OPTYPE, MNEMONIC) \
151 void emitExpression(OPTYPE op) { emitPrimExpr(MNEMONIC, op); }
167 HANDLE(DShlPrimOp,
"dshl");
168 HANDLE(DShlwPrimOp,
"dshlw");
169 HANDLE(DShrPrimOp,
"dshr");
171 HANDLE(AsSIntPrimOp,
"asSInt");
172 HANDLE(AsUIntPrimOp,
"asUInt");
173 HANDLE(AsAsyncResetPrimOp,
"asAsyncReset");
174 HANDLE(AsClockPrimOp,
"asClock");
178 HANDLE(AndRPrimOp,
"andr");
180 HANDLE(XorRPrimOp,
"xorr");
184 void emitAttribute(MemDirAttr attr);
185 void emitAttribute(RUWAttr attr);
188 void emitType(Type type,
bool includeConst =
true);
189 void emitTypeWithColon(Type type) {
190 ps << PP::space <<
":" << PP::nbsp;
195 void emitLocation(Location loc);
196 void emitLocation(Operation *op) { emitLocation(op->getLoc()); }
197 template <
typename... Args>
198 void emitLocationAndNewLine(Args... args) {
201 ps << PP::neverbreak;
202 emitLocation(args...);
206 void emitAssignLike(llvm::function_ref<
void()> emitLHS,
207 llvm::function_ref<
void()> emitRHS,
209 std::optional<PPExtString> wordBeforeLHS = std::nullopt) {
211 ps.scopedBox(PP::ibox2, [&]() {
213 ps << *wordBeforeLHS << PP::space;
217 ps << PP::space << syntax << PP::nbsp;
219 ps.scopedBox(PP::ibox0, [&]() { emitRHS(); });
224 void emitSubExprIBox2(Value v) {
225 ps.scopedBox(PP::ibox2, [&]() { emitExpression(v); });
230 template <
typename Container,
typename EachFn>
231 void interleaveComma(
const Container &c, EachFn eachFn) {
232 llvm::interleave(c, eachFn, [&]() { ps <<
"," << PP::space; });
237 void interleaveComma(ValueRange ops) {
238 return interleaveComma(ops, [&](Value v) { emitSubExprIBox2(v); });
241 void emitStatementFunctionOp(
PPExtString name, Operation *op) {
244 ps.scopedBox(PP::ibox0, [&]() {
245 interleaveComma(op->getOperands());
248 emitLocationAndNewLine(op);
251 template <
typename EachFn,
typename Range>
252 void emitLiteralExpression(Type type,
const Range &r, EachFn eachFn) {
255 ps.scopedBox(PP::ibox0, [&]() {
256 interleaveComma(r, eachFn);
261 void emitLiteralExpression(Type type, ValueRange values) {
262 return emitLiteralExpression(type, values,
263 [&](Value v) { emitSubExprIBox2(v); });
267 void emitSymbol(SymbolRefAttr symbol) {
268 ps.ibox(2, IndentStyle::Block);
269 ps << symbol.getRootReference();
270 for (
auto nested : symbol.getNestedReferences()) {
273 ps << nested.getAttr();
280 InFlightDiagnostic emitError(Operation *op,
const Twine &message) {
281 encounteredError =
true;
282 return op->emitError(message);
286 InFlightDiagnostic emitOpError(Operation *op,
const Twine &message) {
287 encounteredError =
true;
288 return op->emitOpError(message);
293 std::optional<StringRef> lookupEmittedName(Value value) {
294 auto it = valueNames.find(value);
295 if (it != valueNames.end())
302 void emitPendingNewlineIfNeeded() {
303 if (pendingNewline) {
304 pendingNewline =
false;
308 void setPendingNewline() {
310 pendingNewline =
true;
313 void startStatement() { emitPendingNewlineIfNeeded(); }
331 bool pendingNewline =
false;
334 bool encounteredError =
false;
339 DenseMap<Value, StringRef> valueNames;
340 StringSet<> valueNamesStorage;
344 StringAttr legalize(StringAttr attr) {
345 StringRef str = attr.getValue();
346 if (str.empty() || !
isdigit(str.front()))
351 void addValueName(Value value, StringAttr attr) {
352 valueNames.insert({value, attr.getValue()});
354 void addValueName(Value value, StringRef str) {
355 auto it = valueNamesStorage.insert(str);
356 valueNames.insert({value, it.first->getKey()});
358 void addForceable(Forceable op, StringAttr attr) {
359 addValueName(op.getData(), attr);
360 if (op.isForceable()) {
361 SmallString<32> rwName;
362 (Twine(
"rwprobe(") + attr.strref() +
")").
toVector(rwName);
363 addValueName(op.getDataRef(), rwName);
372 SymbolTable symbolTable;
373 hw::InnerSymbolTableCollection istc;
374 hw::InnerRefNamespace irn{symbolTable, istc};
375 SymInfos(Operation *op) : symbolTable(op), istc(op) {};
377 std::optional<std::reference_wrapper<SymInfos>> symInfos;
384 LogicalResult Emitter::finalize() {
return failure(encounteredError); }
387 void Emitter::emitCircuit(CircuitOp op) {
388 circuitNamespace.add(op);
389 SymInfos circuitSymInfos(op);
390 symInfos = circuitSymInfos;
392 ps <<
"FIRRTL version ";
399 ps <<
"circuit " <<
PPExtString(legalize(op.getNameAttr())) <<
" :";
402 for (
auto &bodyOp : *op.getBodyBlock()) {
403 if (encounteredError)
405 TypeSwitch<Operation *>(&bodyOp)
406 .Case<FModuleOp, FExtModuleOp, FIntModuleOp>([&](auto op) {
410 .Case<LayerOp>([&](
auto op) { emitDeclaration(op); })
411 .Case<OptionOp>([&](
auto op) { emitDeclaration(op); })
412 .Case<FormalOp>([&](
auto op) { emitDeclaration(op); })
413 .Default([&](
auto op) {
414 emitOpError(op,
"not supported for emission inside circuit");
418 circuitNamespace.clear();
419 symInfos = std::nullopt;
422 void Emitter::emitEnabledLayers(ArrayRef<Attribute> layers) {
423 for (
auto layer : layers) {
425 ps.
cbox(2, IndentStyle::Block);
426 ps <<
"enablelayer" << PP::space;
427 emitSymbol(cast<SymbolRefAttr>(layer));
432 void Emitter::emitParamAssign(ParamDeclAttr param, Operation *op,
433 std::optional<PPExtString> wordBeforeLHS) {
435 ps << *wordBeforeLHS << PP::nbsp;
437 ps <<
PPExtString(param.getName().strref()) << PP::nbsp <<
"=" << PP::nbsp;
438 emitParamValue(param.getValue(), op);
441 void Emitter::emitParamValue(Attribute value, Operation *op) {
442 TypeSwitch<Attribute>(value)
443 .Case<IntegerAttr>([&](
auto attr) { ps.
addAsString(attr.getValue()); })
444 .Case<FloatAttr>([&](
auto attr) {
446 attr.getValue().toString(str);
451 .Case<ArrayAttr>([&](
auto attr) {
454 interleaveComma(attr.getValue(),
455 [&](
auto element) { emitParamValue(element, op); });
459 .Case<DictionaryAttr>([&](
auto attr) {
462 interleaveComma(attr.getValue(), [&](
auto field) {
463 ps << PPExtString(field.getName()) << PP::nbsp <<
"=" << PP::nbsp;
464 emitParamValue(field.getValue(), op);
469 .Default([&](
auto attr) {
470 emitOpError(op,
"with unsupported parameter attribute: ") << attr;
471 ps <<
"<unsupported-attr ";
477 void Emitter::emitGenericIntrinsic(GenericIntrinsicOp op) {
481 ps << op.getIntrinsic();
483 auto params = op.getParameters();
484 if (!params.empty()) {
486 ps.scopedBox(PP::ibox0, [&]() {
488 params.getAsRange<ParamDeclAttr>(),
489 [&](ParamDeclAttr param) { emitParamAssign(param, op); });
494 if (op.getNumResults() != 0)
495 emitTypeWithColon(op.getResult().getType());
497 if (op.getNumOperands() != 0) {
498 ps <<
"," << PP::space;
499 ps.
scopedBox(PP::ibox0, [&]() { interleaveComma(op->getOperands()); });
506 void Emitter::emitModule(FModuleOp op) {
508 ps.
cbox(4, IndentStyle::Block);
510 ps <<
"public" << PP::nbsp;
511 ps <<
"module " <<
PPExtString(legalize(op.getNameAttr()));
512 emitEnabledLayers(op.getLayers());
513 ps << PP::nbsp <<
":" << PP::end;
520 auto ports = op.getPorts();
521 emitModulePorts(ports, op.getArguments());
522 if (!ports.empty() && !op.getBodyBlock()->empty())
526 emitStatementsInBlock(*op.getBodyBlock());
529 valueNamesStorage.clear();
533 void Emitter::emitModule(FExtModuleOp op) {
535 ps.
cbox(4, IndentStyle::Block);
536 ps <<
"extmodule " <<
PPExtString(legalize(op.getNameAttr()));
537 emitEnabledLayers(op.getLayers());
538 ps << PP::nbsp <<
":" << PP::end;
545 auto ports = op.getPorts();
546 emitModulePorts(ports);
549 if (op.getDefname() && !op.getDefname()->empty()) {
551 ps <<
"defname = " <<
PPExtString(*op.getDefname());
556 emitModuleParameters(op, op.getParameters());
561 void Emitter::emitModule(FIntModuleOp op) {
563 ps.
cbox(4, IndentStyle::Block);
564 ps <<
"intmodule " <<
PPExtString(legalize(op.getNameAttr()));
565 emitEnabledLayers(op.getLayers());
566 ps << PP::nbsp <<
":" << PP::end;
573 auto ports = op.getPorts();
574 emitModulePorts(ports);
577 ps <<
"intrinsic = " <<
PPExtString(op.getIntrinsic());
581 emitModuleParameters(op, op.getParameters());
588 void Emitter::emitModulePorts(ArrayRef<PortInfo> ports,
589 Block::BlockArgListType arguments) {
590 for (
unsigned i = 0, e = ports.size(); i < e; ++i) {
592 const auto &port = ports[i];
593 ps << (port.direction ==
Direction::In ?
"input " :
"output ");
594 auto legalName = legalize(port.name);
595 if (!arguments.empty())
596 addValueName(arguments[i], legalName);
599 emitLocation(ports[i].loc);
604 void Emitter::emitModuleParameters(Operation *op, ArrayAttr parameters) {
605 for (
auto param : parameters.getAsRange<ParamDeclAttr>()) {
607 emitParamAssign(param, op,
PPExtString(
"parameter"));
613 void Emitter::emitDeclaration(LayerOp op) {
615 ps <<
"layer " <<
PPExtString(op.getSymName()) <<
", "
616 <<
PPExtString(stringifyLayerConvention(op.getConvention()));
618 if (
auto outputFile = op->getAttrOfType<hw::OutputFileAttr>(
"output_file")) {
624 emitLocationAndNewLine(op);
626 for (
auto &bodyOp : op.getBody().getOps()) {
627 TypeSwitch<Operation *>(&bodyOp)
628 .Case<LayerOp>([&](auto op) { emitDeclaration(op); })
629 .Default([&](
auto op) {
631 "not supported for emission inside layer definition");
638 void Emitter::emitDeclaration(OptionOp op) {
640 ps <<
"option " <<
PPExtString(legalize(op.getSymNameAttr())) <<
" :";
643 for (
auto caseOp : op.getBody().getOps<OptionCaseOp>()) {
645 ps << PPExtString(legalize(caseOp.getSymNameAttr()));
646 emitLocation(caseOp);
649 ps << PP::newline << PP::newline;
653 void Emitter::emitDeclaration(FormalOp op) {
655 ps.
cbox(4, IndentStyle::Block);
656 ps <<
"formal " <<
PPExtString(legalize(op.getSymNameAttr()));
657 ps <<
" of " <<
PPExtString(legalize(op.getModuleNameAttr().getAttr()));
658 ps << PP::nbsp <<
":" << PP::end;
663 for (
auto param : op.getParameters()) {
665 ps <<
PPExtString(param.getName()) << PP::nbsp <<
"=" << PP::nbsp;
666 emitParamValue(param.getValue(), op);
680 return (
isExpression(op) && !isa<InvalidValueOp>(op)) ||
681 (isa<GenericIntrinsicOp>(op) && op->hasOneUse() &&
685 void Emitter::emitStatementsInBlock(Block &block) {
686 for (
auto &bodyOp : block) {
687 if (encounteredError)
691 TypeSwitch<Operation *>(&bodyOp)
692 .Case<WhenOp, WireOp, RegOp, RegResetOp, NodeOp, StopOp, SkipOp,
693 PrintFOp, AssertOp, AssumeOp, CoverOp, ConnectOp,
694 MatchingConnectOp, PropAssignOp, InstanceOp, InstanceChoiceOp,
695 AttachOp, MemOp, InvalidValueOp, SeqMemOp, CombMemOp,
696 MemoryPortOp, MemoryDebugPortOp, MemoryPortAccessOp, RefDefineOp,
697 RefForceOp, RefForceInitialOp, RefReleaseOp, RefReleaseInitialOp,
698 LayerBlockOp, GenericIntrinsicOp>(
699 [&](
auto op) { emitStatement(op); })
700 .Default([&](
auto op) {
702 ps <<
"// operation " <<
PPExtString(op->getName().getStringRef());
704 emitOpError(op,
"not supported as statement");
709 void Emitter::emitStatement(WhenOp op) {
712 emitExpression(op.getCondition());
714 emitLocationAndNewLine(op);
715 ps.
scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(op.getThenBlock()); });
717 if (!op.hasElseRegion())
724 auto &elseBlock = op.getElseBlock();
725 if (!elseBlock.empty() && &elseBlock.front() == &elseBlock.back()) {
726 if (
auto whenOp = dyn_cast<WhenOp>(&elseBlock.front())) {
727 emitStatement(whenOp);
734 ps.
scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(elseBlock); });
737 void Emitter::emitStatement(WireOp op) {
738 auto legalName = legalize(op.getNameAttr());
739 addForceable(op, legalName);
743 emitTypeWithColon(op.getResult().getType());
745 emitLocationAndNewLine(op);
748 void Emitter::emitStatement(RegOp op) {
749 auto legalName = legalize(op.getNameAttr());
750 addForceable(op, legalName);
754 emitTypeWithColon(op.getResult().getType());
755 ps <<
"," << PP::space;
756 emitExpression(op.getClockVal());
758 emitLocationAndNewLine(op);
761 void Emitter::emitStatement(RegResetOp op) {
762 auto legalName = legalize(op.getNameAttr());
763 addForceable(op, legalName);
767 ps <<
"regreset " << legalName;
768 emitTypeWithColon(op.getResult().getType());
769 ps <<
"," << PP::space;
770 emitExpression(op.getClockVal());
771 ps <<
"," << PP::space;
772 emitExpression(op.getResetSignal());
773 ps <<
"," << PP::space;
774 emitExpression(op.getResetValue());
778 ps <<
"reg " << legalName;
779 emitTypeWithColon(op.getResult().getType());
780 ps <<
"," << PP::space;
781 emitExpression(op.getClockVal());
782 ps << PP::space <<
"with :";
784 ps << PP::neverbreak;
787 ps <<
"reset => (" << PP::ibox0;
788 emitExpression(op.getResetSignal());
789 ps <<
"," << PP::space;
790 emitExpression(op.getResetValue());
791 ps <<
")" << PP::end;
794 emitLocationAndNewLine(op);
797 void Emitter::emitStatement(NodeOp op) {
798 auto legalName = legalize(op.getNameAttr());
799 addForceable(op, legalName);
801 emitAssignLike([&]() { ps <<
"node " <<
PPExtString(legalName); },
802 [&]() { emitExpression(op.getInput()); });
803 emitLocationAndNewLine(op);
806 void Emitter::emitStatement(StopOp op) {
809 ps <<
"stop(" << PP::ibox0;
810 emitExpression(op.getClock());
811 ps <<
"," << PP::space;
812 emitExpression(op.getCond());
813 ps <<
"," << PP::space;
815 ps <<
")" << PP::end;
816 if (!op.getName().empty()) {
817 ps << PP::space <<
": " <<
PPExtString(legalize(op.getNameAttr()));
820 emitLocationAndNewLine(op);
823 void Emitter::emitStatement(SkipOp op) {
826 emitLocationAndNewLine(op);
829 void Emitter::emitStatement(PrintFOp op) {
832 ps <<
"printf(" << PP::ibox0;
833 emitExpression(op.getClock());
834 ps <<
"," << PP::space;
835 emitExpression(op.getCond());
836 ps <<
"," << PP::space;
838 for (
auto operand : op.getSubstitutions()) {
839 ps <<
"," << PP::space;
840 emitExpression(operand);
842 ps <<
")" << PP::end;
843 if (!op.getName().empty()) {
844 ps << PP::space <<
": " <<
PPExtString(legalize(op.getNameAttr()));
847 emitLocationAndNewLine(op);
851 void Emitter::emitVerifStatement(T op, StringRef mnemonic) {
854 ps << mnemonic <<
"(" << PP::ibox0;
855 emitExpression(op.getClock());
856 ps <<
"," << PP::space;
857 emitExpression(op.getPredicate());
858 ps <<
"," << PP::space;
859 emitExpression(op.getEnable());
860 ps <<
"," << PP::space;
862 ps <<
")" << PP::end;
863 if (!op.getName().empty()) {
864 ps << PP::space <<
": " <<
PPExtString(legalize(op.getNameAttr()));
867 emitLocationAndNewLine(op);
870 void Emitter::emitStatement(ConnectOp op) {
874 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
875 ps <<
"invalidate" << PP::space;
876 emitExpression(op.getDest());
878 ps <<
"connect" << PP::space;
879 emitExpression(op.getDest());
880 ps <<
"," << PP::space;
881 emitExpression(op.getSrc());
885 auto emitLHS = [&]() { emitExpression(op.getDest()); };
886 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
887 emitAssignLike(emitLHS, [&]() { ps <<
"invalid"; },
PPExtString(
"is"));
890 emitLHS, [&]() { emitExpression(op.getSrc()); },
PPExtString(
"<="));
893 emitLocationAndNewLine(op);
896 void Emitter::emitStatement(MatchingConnectOp op) {
900 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
901 ps <<
"invalidate" << PP::space;
902 emitExpression(op.getDest());
904 ps <<
"connect" << PP::space;
905 emitExpression(op.getDest());
906 ps <<
"," << PP::space;
907 emitExpression(op.getSrc());
911 auto emitLHS = [&]() { emitExpression(op.getDest()); };
912 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
913 emitAssignLike(emitLHS, [&]() { ps <<
"invalid"; },
PPExtString(
"is"));
916 emitLHS, [&]() { emitExpression(op.getSrc()); },
PPExtString(
"<="));
919 emitLocationAndNewLine(op);
922 void Emitter::emitStatement(PropAssignOp op) {
925 ps <<
"propassign" << PP::space;
926 interleaveComma(op.getOperands());
928 emitLocationAndNewLine(op);
931 void Emitter::emitStatement(InstanceOp op) {
933 auto legalName = legalize(op.getNameAttr());
935 <<
PPExtString(legalize(op.getModuleNameAttr().getAttr()));
936 emitLocationAndNewLine(op);
940 SmallString<16> portName(legalName);
941 portName.push_back(
'.');
942 unsigned baseLen = portName.size();
943 for (
unsigned i = 0, e = op.getNumResults(); i < e; ++i) {
944 portName.append(legalize(op.getPortName(i)));
945 addValueName(op.getResult(i), portName);
946 portName.resize(baseLen);
950 void Emitter::emitStatement(InstanceChoiceOp op) {
952 auto legalName = legalize(op.getNameAttr());
953 ps <<
"instchoice " <<
PPExtString(legalName) <<
" of "
954 <<
PPExtString(legalize(op.getDefaultTargetAttr().getAttr())) <<
", "
955 <<
PPExtString(legalize(op.getOptionNameAttr())) <<
" :";
958 for (
const auto &[optSym, targetSym] : op.getTargetChoices()) {
960 ps << PPExtString(legalize(optSym.getLeafReference()));
962 ps << PPExtString(legalize(targetSym.getAttr()));
967 SmallString<16> portName(legalName);
968 portName.push_back(
'.');
969 unsigned baseLen = portName.size();
970 for (
unsigned i = 0, e = op.getNumResults(); i < e; ++i) {
971 portName.append(legalize(op.getPortName(i)));
972 addValueName(op.getResult(i), portName);
973 portName.resize(baseLen);
977 void Emitter::emitStatement(AttachOp op) {
978 emitStatementFunctionOp(
PPExtString(
"attach"), op);
981 void Emitter::emitStatement(MemOp op) {
982 auto legalName = legalize(op.getNameAttr());
983 SmallString<16> portName(legalName);
984 portName.push_back(
'.');
985 auto portNameBaseLen = portName.size();
986 for (
auto result : llvm::zip(op.getResults(), op.getPortNames())) {
987 portName.resize(portNameBaseLen);
988 portName.append(legalize(cast<StringAttr>(std::get<1>(result))));
989 addValueName(std::get<0>(result), portName);
994 emitLocationAndNewLine(op);
997 ps <<
"data-type => ";
998 emitType(op.getDataType());
1003 ps <<
"read-latency => ";
1006 ps <<
"write-latency => ";
1010 SmallString<16> reader, writer, readwriter;
1011 for (std::pair<StringAttr, MemOp::PortKind> port : op.getPorts()) {
1012 auto add = [&](SmallString<16> &to, StringAttr name) {
1015 to.append(name.getValue());
1017 switch (port.second) {
1018 case MemOp::PortKind::Read:
1019 add(reader, legalize(port.first));
1021 case MemOp::PortKind::Write:
1022 add(writer, legalize(port.first));
1024 case MemOp::PortKind::ReadWrite:
1025 add(readwriter, legalize(port.first));
1027 case MemOp::PortKind::Debug:
1028 emitOpError(op,
"has unsupported 'debug' port");
1032 if (!reader.empty())
1033 ps <<
"reader => " << reader << PP::newline;
1034 if (!writer.empty())
1035 ps <<
"writer => " << writer << PP::newline;
1036 if (!readwriter.empty())
1037 ps <<
"readwriter => " << readwriter << PP::newline;
1039 ps <<
"read-under-write => ";
1040 emitAttribute(op.getRuw());
1041 setPendingNewline();
1045 void Emitter::emitStatement(SeqMemOp op) {
1048 ps <<
"smem " <<
PPExtString(legalize(op.getNameAttr()));
1049 emitTypeWithColon(op.getType());
1050 ps <<
"," << PP::space;
1051 emitAttribute(op.getRuw());
1053 emitLocationAndNewLine(op);
1056 void Emitter::emitStatement(CombMemOp op) {
1059 ps <<
"cmem " <<
PPExtString(legalize(op.getNameAttr()));
1060 emitTypeWithColon(op.getType());
1062 emitLocationAndNewLine(op);
1065 void Emitter::emitStatement(MemoryPortOp op) {
1067 addValueName(op.getData(), legalize(op.getNameAttr()));
1070 void Emitter::emitStatement(MemoryDebugPortOp op) {
1072 addValueName(op.getData(), legalize(op.getNameAttr()));
1075 void Emitter::emitStatement(MemoryPortAccessOp op) {
1079 auto port = cast<MemoryPortOp>(op.getPort().getDefiningOp());
1080 emitAttribute(port.getDirection());
1082 ps <<
" mport " <<
PPExtString(legalize(port.getNameAttr())) <<
" = ";
1085 auto *mem = port.getMemory().getDefiningOp();
1086 if (
auto seqMem = dyn_cast<SeqMemOp>(mem))
1087 ps << legalize(seqMem.getNameAttr());
1089 ps << legalize(cast<CombMemOp>(mem).getNameAttr());
1093 emitExpression(op.getIndex());
1097 emitExpression(op.getClock());
1099 emitLocationAndNewLine(op);
1102 void Emitter::emitStatement(RefDefineOp op) {
1104 emitAssignLike([&]() { emitExpression(op.getDest()); },
1105 [&]() { emitExpression(op.getSrc()); },
PPExtString(
"="),
1107 emitLocationAndNewLine(op);
1110 void Emitter::emitStatement(RefForceOp op) {
1111 emitStatementFunctionOp(
PPExtString(
"force"), op);
1114 void Emitter::emitStatement(RefForceInitialOp op) {
1116 auto constantPredicate =
1117 dyn_cast_or_null<ConstantOp>(op.getPredicate().getDefiningOp());
1118 bool hasEnable = !constantPredicate || constantPredicate.getValue() == 0;
1121 emitExpression(op.getPredicate());
1122 ps <<
":" << PP::bbox2 << PP::neverbreak << PP::newline;
1124 ps <<
"force_initial(";
1126 interleaveComma({op.getDest(), op.getSrc()});
1131 emitLocationAndNewLine(op);
1134 void Emitter::emitStatement(RefReleaseOp op) {
1135 emitStatementFunctionOp(
PPExtString(
"release"), op);
1138 void Emitter::emitStatement(RefReleaseInitialOp op) {
1140 auto constantPredicate =
1141 dyn_cast_or_null<ConstantOp>(op.getPredicate().getDefiningOp());
1142 bool hasEnable = !constantPredicate || constantPredicate.getValue() == 0;
1145 emitExpression(op.getPredicate());
1146 ps <<
":" << PP::bbox2 << PP::neverbreak << PP::newline;
1148 ps <<
"release_initial(";
1149 emitExpression(op.getDest());
1153 emitLocationAndNewLine(op);
1156 void Emitter::emitStatement(LayerBlockOp op) {
1158 ps <<
"layerblock " << op.getLayerName().getLeafReference() <<
" :";
1159 emitLocationAndNewLine(op);
1160 auto *body = op.getBody();
1161 ps.
scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(*body); });
1164 void Emitter::emitStatement(InvalidValueOp op) {
1167 if (llvm::all_of(op->getUses(), [&](OpOperand &use) {
1168 return use.getOperandNumber() == 1 &&
1169 isa<ConnectOp, MatchingConnectOp>(use.getOwner());
1175 auto name = circuitNamespace.newName(
"_invalid");
1176 addValueName(op, name);
1178 emitType(op.getType());
1179 emitLocationAndNewLine(op);
1185 emitLocationAndNewLine(op);
1188 void Emitter::emitStatement(GenericIntrinsicOp op) {
1191 emitGenericIntrinsic(op);
1194 auto name = circuitNamespace.newName(
"_gen_int");
1195 addValueName(op.getResult(), name);
1196 emitAssignLike([&]() { ps <<
"node " <<
PPExtString(name); },
1197 [&]() { emitGenericIntrinsic(op); });
1199 emitLocationAndNewLine(op);
1202 void Emitter::emitExpression(Value value) {
1205 if (
auto name = lookupEmittedName(value)) {
1211 auto op = value.getDefiningOp();
1212 assert(op &&
"value must either be a block arg or the result of an op");
1213 TypeSwitch<Operation *>(op)
1216 ConstantOp, SpecialConstantOp, SubfieldOp, SubindexOp, SubaccessOp,
1217 OpenSubfieldOp, OpenSubindexOp,
1219 AddPrimOp, SubPrimOp, MulPrimOp, DivPrimOp, RemPrimOp, AndPrimOp,
1220 OrPrimOp, XorPrimOp, LEQPrimOp, LTPrimOp, GEQPrimOp, GTPrimOp,
1221 EQPrimOp, NEQPrimOp, CatPrimOp, DShlPrimOp, DShlwPrimOp, DShrPrimOp,
1223 AsSIntPrimOp, AsUIntPrimOp, AsAsyncResetPrimOp, AsClockPrimOp,
1224 CvtPrimOp, NegPrimOp, NotPrimOp, AndRPrimOp, OrRPrimOp, XorRPrimOp,
1226 BitsPrimOp, HeadPrimOp, TailPrimOp, PadPrimOp, MuxPrimOp, ShlPrimOp,
1227 ShrPrimOp, UninferredResetCastOp, ConstCastOp, StringConstantOp,
1228 FIntegerConstantOp, BoolConstantOp, DoubleConstantOp, ListCreateOp,
1229 UnresolvedPathOp, GenericIntrinsicOp,
1231 RefSendOp, RefResolveOp, RefSubOp, RWProbeOp, RefCastOp>(
1233 ps.
scopedBox(PP::ibox0, [&]() { emitExpression(op); });
1235 .Default([&](
auto op) {
1236 emitOpError(op,
"not supported as expression");
1237 ps <<
"<unsupported-expr-" <<
PPExtString(op->getName().stripDialect())
1242 void Emitter::emitExpression(ConstantOp op) {
1244 emitType(op.getType(),
false);
1251 void Emitter::emitExpression(SpecialConstantOp op) {
1252 auto emitInner = [&]() {
1259 .
Case<ClockType>([&](
auto type) {
1264 .Case<ResetType>([&](
auto type) { emitInner(); })
1265 .Case<AsyncResetType>([&](
auto type) {
1266 ps <<
"asAsyncReset(";
1273 void Emitter::emitExpression(SubfieldOp op) {
1274 BundleType type = op.getInput().getType();
1275 emitExpression(op.getInput());
1276 ps <<
"." << legalize(type.getElementNameAttr(op.getFieldIndex()));
1280 void Emitter::emitExpression(SubindexOp op) {
1281 emitExpression(op.getInput());
1288 void Emitter::emitExpression(SubaccessOp op) {
1289 emitExpression(op.getInput());
1291 emitExpression(op.getIndex());
1295 void Emitter::emitExpression(OpenSubfieldOp op) {
1296 auto type = op.getInput().getType();
1297 emitExpression(op.getInput());
1298 ps <<
"." << legalize(type.getElementNameAttr(op.getFieldIndex()));
1301 void Emitter::emitExpression(OpenSubindexOp op) {
1302 emitExpression(op.getInput());
1308 void Emitter::emitExpression(RefSendOp op) {
1310 emitExpression(op.getBase());
1314 void Emitter::emitExpression(RefResolveOp op) {
1316 emitExpression(op.getRef());
1320 void Emitter::emitExpression(RefSubOp op) {
1321 emitExpression(op.getInput());
1323 .
Case<FVectorType>([&](
auto type) {
1329 [&](
auto type) { ps <<
"." << type.getElementName(op.getIndex()); });
1332 void Emitter::emitExpression(RWProbeOp op) {
1336 auto target = symInfos->get().irn.lookup(op.getTarget());
1338 if (target.isPort()) {
1339 auto mod = cast<FModuleOp>(target.getOp());
1340 auto port = target.getPort();
1341 base = mod.getArgument(port);
1343 base = cast<hw::InnerSymbolOpInterface>(target.getOp()).getTargetResult();
1346 emitExpression(base);
1349 auto fieldID = target.getField();
1350 auto type = base.getType();
1353 .
Case<FVectorType, OpenVectorType>([&](
auto vecTy) {
1354 auto index = vecTy.getIndexForFieldID(fieldID);
1358 auto [subtype, subfieldID] = vecTy.getSubTypeByFieldID(fieldID);
1360 fieldID = subfieldID;
1362 .Case<BundleType, OpenBundleType>([&](
auto bundleTy) {
1363 auto index = bundleTy.getIndexForFieldID(fieldID);
1364 ps <<
"." << bundleTy.getElementName(index);
1365 auto [subtype, subfieldID] = bundleTy.getSubTypeByFieldID(fieldID);
1367 fieldID = subfieldID;
1373 void Emitter::emitExpression(RefCastOp op) { emitExpression(op.getInput()); }
1375 void Emitter::emitExpression(UninferredResetCastOp op) {
1376 emitExpression(op.getInput());
1379 void Emitter::emitExpression(FIntegerConstantOp op) {
1385 void Emitter::emitExpression(BoolConstantOp op) {
1386 ps <<
"Bool(" << (op.getValue() ?
"true" :
"false") <<
")";
1389 void Emitter::emitExpression(DoubleConstantOp op) {
1395 SmallString<16> str;
1396 op.getValueAttr().getValue().toString(str);
1401 void Emitter::emitExpression(StringConstantOp op) {
1407 void Emitter::emitExpression(ListCreateOp op) {
1408 return emitLiteralExpression(op.getType(), op.getElements());
1411 void Emitter::emitExpression(UnresolvedPathOp op) {
1417 void Emitter::emitExpression(GenericIntrinsicOp op) {
1418 emitGenericIntrinsic(op);
1421 void Emitter::emitExpression(ConstCastOp op) { emitExpression(op.getInput()); }
1423 void Emitter::emitPrimExpr(StringRef mnemonic, Operation *op,
1424 ArrayRef<uint32_t> attrs) {
1425 ps << mnemonic <<
"(" << PP::ibox0;
1426 interleaveComma(op->getOperands());
1427 if (!op->getOperands().empty() && !attrs.empty())
1428 ps <<
"," << PP::space;
1429 interleaveComma(attrs, [&](
auto attr) { ps.
addAsString(attr); });
1430 ps <<
")" << PP::end;
1433 void Emitter::emitAttribute(MemDirAttr attr) {
1435 case MemDirAttr::Infer:
1438 case MemDirAttr::Read:
1441 case MemDirAttr::Write:
1444 case MemDirAttr::ReadWrite:
1450 void Emitter::emitAttribute(RUWAttr attr) {
1452 case RUWAttr::Undefined:
1465 void Emitter::emitType(Type type,
bool includeConst) {
1466 if (includeConst &&
isConst(type))
1468 auto emitWidth = [&](std::optional<int32_t>
width) {
1477 .
Case<ClockType>([&](
auto) { ps <<
"Clock"; })
1478 .Case<ResetType>([&](
auto) { ps <<
"Reset"; })
1479 .Case<AsyncResetType>([&](
auto) { ps <<
"AsyncReset"; })
1480 .Case<UIntType>([&](
auto type) {
1482 emitWidth(type.getWidth());
1484 .Case<SIntType>([&](
auto type) {
1486 emitWidth(type.getWidth());
1488 .Case<AnalogType>([&](
auto type) {
1490 emitWidth(type.getWidth());
1492 .Case<OpenBundleType, BundleType>([&](
auto type) {
1494 if (!type.getElements().empty())
1496 bool anyEmitted =
false;
1498 for (
auto &element : type.getElements()) {
1500 ps <<
"," << PP::space;
1501 ps.scopedBox(PP::ibox2, [&]() {
1504 ps << legalize(element.name);
1505 emitTypeWithColon(element.type);
1514 .Case<OpenVectorType, FVectorType, CMemoryType>([&](
auto type) {
1515 emitType(type.getElementType());
1520 .Case<RefType>([&](RefType type) {
1521 if (type.getForceable())
1524 ps.
cbox(2, IndentStyle::Block);
1526 emitType(type.getType());
1527 if (
auto layer = type.getLayer()) {
1530 emitSymbol(type.getLayer());
1535 .Case<AnyRefType>([&](AnyRefType type) { ps <<
"AnyRef"; })
1536 .Case<StringType>([&](StringType type) { ps <<
"String"; })
1537 .Case<FIntegerType>([&](FIntegerType type) { ps <<
"Integer"; })
1538 .Case<BoolType>([&](BoolType type) { ps <<
"Bool"; })
1539 .Case<DoubleType>([&](DoubleType type) { ps <<
"Double"; })
1540 .Case<PathType>([&](PathType type) { ps <<
"Path"; })
1541 .Case<ListType>([&](ListType type) {
1543 emitType(type.getElementType());
1546 .Default([&](
auto type) {
1547 llvm_unreachable(
"all types should be implemented");
1553 void Emitter::emitLocation(Location loc) {
1555 ps << PP::neverbreak;
1557 dyn_cast_or_null<FileLineColLoc, LocationAttr>(LocationAttr(loc))) {
1558 ps <<
" @[" << fileLoc.getFilename().getValue();
1559 if (
auto line = fileLoc.getLine()) {
1562 if (
auto col = fileLoc.getColumn()) {
1579 std::optional<size_t> targetLineLength,
1581 Emitter emitter(os, version,
1582 targetLineLength.value_or(defaultTargetLineLength));
1583 for (
auto &op : *module.getBody()) {
1584 if (
auto circuitOp = dyn_cast<CircuitOp>(op))
1585 emitter.emitCircuit(circuitOp);
1587 return emitter.finalize();
1592 "target-line-length",
1593 llvm::cl::desc(
"Target line length for emitted .fir"),
1594 llvm::cl::value_desc(
"number of chars"),
1595 llvm::cl::init(defaultTargetLineLength));
1596 static mlir::TranslateFromMLIRRegistration toFIR(
1597 "export-firrtl",
"emit FIRRTL dialect operations to .fir output",
1598 [](ModuleOp module, llvm::raw_ostream &os) {
1601 [](mlir::DialectRegistry ®istry) {
1602 registry.insert<chirrtl::CHIRRTLDialect>();
1603 registry.insert<firrtl::FIRRTLDialect>();
assert(baseType &&"element must be base type")
#define HANDLE(OPTYPE, MNEMONIC)
static bool isEmittedInline(Operation *op)
Check if an operation is inlined into the emission of their users.
static std::vector< mlir::Value > toVector(mlir::ValueRange range)
This class implements the same functionality as TypeSwitch except that it uses firrtl::type_dyn_cast ...
FIRRTLTypeSwitch< T, ResultT > & Case(CallableT &&caseFn)
Add a case on the given type.
void space()
Add a breakable space.
void cbox(int32_t offset=0, IndentStyle style=IndentStyle::Visual)
Start a consistent group with specified offset.
void zerobreak()
Add a break that is zero-wide if not broken.
Wrap a PrettyPrinter with TokenBuilder features as well as operator<<'s.
auto scopedBox(T &&t, Callable &&c, Token close=EndToken())
Open a box, invoke the lambda, and close it after.
TokenStream & writeQuotedEscaped(StringRef str, bool useHexEscapes=false, StringRef left="\"", StringRef right="\"")
TokenStream & addAsString(T &&t)
General-purpose "format this" helper, for types not supported by operator<< yet.
PrettyPrinter::Listener that saves strings while live.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
mlir::LogicalResult exportFIRFile(mlir::ModuleOp module, llvm::raw_ostream &os, std::optional< size_t > targetLineLength, FIRVersion version)
constexpr FIRVersion exportFIRVersion
The version of FIRRTL that the exporter produces.
void registerToFIRFileTranslation()
bool isConst(Type type)
Returns true if this is a 'const' type whose value is guaranteed to be unchanging at circuit executio...
bool isExpression(Operation *op)
Return true if the specified operation is a firrtl expression.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
The namespace of a CircuitOp, generally inhabited by modules.
The FIRRTL specification version.
String wrapper to indicate string has external storage.