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 emitGenericIntrinsic(GenericIntrinsicOp op);
71 void emitStatementsInBlock(
Block &block);
72 void emitStatement(WhenOp op);
73 void emitStatement(WireOp op);
74 void emitStatement(RegOp op);
75 void emitStatement(RegResetOp op);
76 void emitStatement(NodeOp op);
77 void emitStatement(StopOp op);
78 void emitStatement(SkipOp op);
79 void emitStatement(PrintFOp op);
80 void emitStatement(ConnectOp op);
81 void emitStatement(MatchingConnectOp op);
82 void emitStatement(PropAssignOp op);
83 void emitStatement(InstanceOp op);
84 void emitStatement(InstanceChoiceOp op);
85 void emitStatement(AttachOp op);
86 void emitStatement(MemOp op);
87 void emitStatement(InvalidValueOp op);
88 void emitStatement(CombMemOp op);
89 void emitStatement(SeqMemOp op);
90 void emitStatement(MemoryPortOp op);
91 void emitStatement(MemoryDebugPortOp op);
92 void emitStatement(MemoryPortAccessOp op);
93 void emitStatement(RefDefineOp op);
94 void emitStatement(RefForceOp op);
95 void emitStatement(RefForceInitialOp op);
96 void emitStatement(RefReleaseOp op);
97 void emitStatement(RefReleaseInitialOp op);
98 void emitStatement(LayerBlockOp op);
99 void emitStatement(GenericIntrinsicOp op);
102 void emitVerifStatement(T op, StringRef mnemonic);
103 void emitStatement(AssertOp op) { emitVerifStatement(op,
"assert"); }
104 void emitStatement(AssumeOp op) { emitVerifStatement(op,
"assume"); }
105 void emitStatement(CoverOp op) { emitVerifStatement(op,
"cover"); }
108 void emitExpression(Value value);
109 void emitExpression(ConstantOp op);
110 void emitExpression(SpecialConstantOp op);
111 void emitExpression(SubfieldOp op);
112 void emitExpression(SubindexOp op);
113 void emitExpression(SubaccessOp op);
114 void emitExpression(OpenSubfieldOp op);
115 void emitExpression(OpenSubindexOp op);
116 void emitExpression(RefResolveOp op);
117 void emitExpression(RefSendOp op);
118 void emitExpression(RefSubOp op);
119 void emitExpression(RWProbeOp op);
120 void emitExpression(RefCastOp op);
121 void emitExpression(UninferredResetCastOp op);
122 void emitExpression(ConstCastOp op);
123 void emitExpression(StringConstantOp op);
124 void emitExpression(FIntegerConstantOp op);
125 void emitExpression(BoolConstantOp op);
126 void emitExpression(DoubleConstantOp op);
127 void emitExpression(ListCreateOp op);
128 void emitExpression(UnresolvedPathOp op);
129 void emitExpression(GenericIntrinsicOp op);
131 void emitPrimExpr(StringRef mnemonic, Operation *op,
132 ArrayRef<uint32_t> attrs = {});
134 void emitExpression(BitsPrimOp op) {
135 emitPrimExpr(
"bits", op, {op.getHi(), op.getLo()});
137 void emitExpression(HeadPrimOp op) {
138 emitPrimExpr(
"head", op, op.getAmount());
140 void emitExpression(TailPrimOp op) {
141 emitPrimExpr(
"tail", op, op.getAmount());
143 void emitExpression(PadPrimOp op) { emitPrimExpr(
"pad", op, op.getAmount()); }
144 void emitExpression(ShlPrimOp op) { emitPrimExpr(
"shl", op, op.getAmount()); }
145 void emitExpression(ShrPrimOp op) { emitPrimExpr(
"shr", op, op.getAmount()); }
148 #define HANDLE(OPTYPE, MNEMONIC) \
149 void emitExpression(OPTYPE op) { emitPrimExpr(MNEMONIC, op); }
165 HANDLE(DShlPrimOp,
"dshl");
166 HANDLE(DShlwPrimOp,
"dshlw");
167 HANDLE(DShrPrimOp,
"dshr");
169 HANDLE(AsSIntPrimOp,
"asSInt");
170 HANDLE(AsUIntPrimOp,
"asUInt");
171 HANDLE(AsAsyncResetPrimOp,
"asAsyncReset");
172 HANDLE(AsClockPrimOp,
"asClock");
176 HANDLE(AndRPrimOp,
"andr");
178 HANDLE(XorRPrimOp,
"xorr");
182 void emitAttribute(MemDirAttr attr);
183 void emitAttribute(RUWAttr attr);
186 void emitType(Type type,
bool includeConst =
true);
187 void emitTypeWithColon(Type type) {
188 ps << PP::space <<
":" << PP::nbsp;
193 void emitLocation(Location loc);
194 void emitLocation(Operation *op) { emitLocation(op->getLoc()); }
195 template <
typename... Args>
196 void emitLocationAndNewLine(Args... args) {
199 ps << PP::neverbreak;
200 emitLocation(args...);
204 void emitAssignLike(llvm::function_ref<
void()> emitLHS,
205 llvm::function_ref<
void()> emitRHS,
207 std::optional<PPExtString> wordBeforeLHS = std::nullopt) {
209 ps.scopedBox(PP::ibox2, [&]() {
211 ps << *wordBeforeLHS << PP::space;
215 ps << PP::space << syntax << PP::nbsp;
217 ps.scopedBox(PP::ibox0, [&]() { emitRHS(); });
222 void emitSubExprIBox2(Value v) {
223 ps.scopedBox(PP::ibox2, [&]() { emitExpression(v); });
228 template <
typename Container,
typename EachFn>
229 void interleaveComma(
const Container &c, EachFn eachFn) {
230 llvm::interleave(c, eachFn, [&]() { ps <<
"," << PP::space; });
235 void interleaveComma(ValueRange ops) {
236 return interleaveComma(ops, [&](Value v) { emitSubExprIBox2(v); });
239 void emitStatementFunctionOp(
PPExtString name, Operation *op) {
242 ps.scopedBox(PP::ibox0, [&]() {
243 interleaveComma(op->getOperands());
246 emitLocationAndNewLine(op);
249 template <
typename EachFn,
typename Range>
250 void emitLiteralExpression(Type type,
const Range &r, EachFn eachFn) {
253 ps.scopedBox(PP::ibox0, [&]() {
254 interleaveComma(r, eachFn);
259 void emitLiteralExpression(Type type, ValueRange values) {
260 return emitLiteralExpression(type, values,
261 [&](Value v) { emitSubExprIBox2(v); });
265 void emitSymbol(SymbolRefAttr symbol) {
266 ps.ibox(2, IndentStyle::Block);
267 ps << symbol.getRootReference();
268 for (
auto nested : symbol.getNestedReferences()) {
271 ps << nested.getAttr();
278 InFlightDiagnostic emitError(Operation *op,
const Twine &message) {
279 encounteredError =
true;
280 return op->emitError(message);
284 InFlightDiagnostic emitOpError(Operation *op,
const Twine &message) {
285 encounteredError =
true;
286 return op->emitOpError(message);
291 std::optional<StringRef> lookupEmittedName(Value value) {
292 auto it = valueNames.find(value);
293 if (it != valueNames.end())
300 void emitPendingNewlineIfNeeded() {
301 if (pendingNewline) {
302 pendingNewline =
false;
306 void setPendingNewline() {
308 pendingNewline =
true;
311 void startStatement() { emitPendingNewlineIfNeeded(); }
329 bool pendingNewline =
false;
332 bool encounteredError =
false;
337 DenseMap<Value, StringRef> valueNames;
338 StringSet<> valueNamesStorage;
342 StringAttr legalize(StringAttr attr) {
343 StringRef str = attr.getValue();
344 if (str.empty() || !
isdigit(str.front()))
349 void addValueName(Value value, StringAttr attr) {
350 valueNames.insert({value, attr.getValue()});
352 void addValueName(Value value, StringRef str) {
353 auto it = valueNamesStorage.insert(str);
354 valueNames.insert({value, it.first->getKey()});
356 void addForceable(Forceable op, StringAttr attr) {
357 addValueName(op.getData(), attr);
358 if (op.isForceable()) {
359 SmallString<32> rwName;
360 (Twine(
"rwprobe(") + attr.strref() +
")").
toVector(rwName);
361 addValueName(op.getDataRef(), rwName);
370 SymbolTable symbolTable;
371 hw::InnerSymbolTableCollection istc;
372 hw::InnerRefNamespace irn{symbolTable, istc};
373 SymInfos(Operation *op) : symbolTable(op), istc(op){};
375 std::optional<std::reference_wrapper<SymInfos>> symInfos;
382 LogicalResult Emitter::finalize() {
return failure(encounteredError); }
385 void Emitter::emitCircuit(CircuitOp op) {
386 circuitNamespace.add(op);
387 SymInfos circuitSymInfos(op);
388 symInfos = circuitSymInfos;
390 ps <<
"FIRRTL version ";
397 ps <<
"circuit " <<
PPExtString(legalize(op.getNameAttr())) <<
" :";
400 for (
auto &bodyOp : *op.getBodyBlock()) {
401 if (encounteredError)
403 TypeSwitch<Operation *>(&bodyOp)
404 .Case<FModuleOp, FExtModuleOp, FIntModuleOp>([&](auto op) {
408 .Case<LayerOp>([&](
auto op) { emitDeclaration(op); })
409 .Case<OptionOp>([&](
auto op) { emitDeclaration(op); })
410 .Case<FormalOp>([&](
auto op) { emitDeclaration(op); })
411 .Default([&](
auto op) {
412 emitOpError(op,
"not supported for emission inside circuit");
416 circuitNamespace.clear();
417 symInfos = std::nullopt;
420 void Emitter::emitEnabledLayers(ArrayRef<Attribute> layers) {
421 for (
auto layer : layers) {
423 ps.
cbox(2, IndentStyle::Block);
424 ps <<
"enablelayer" << PP::space;
425 emitSymbol(cast<SymbolRefAttr>(layer));
430 void Emitter::emitParamAssign(ParamDeclAttr param, Operation *op,
431 std::optional<PPExtString> wordBeforeLHS) {
433 ps << *wordBeforeLHS << PP::nbsp;
435 ps <<
PPExtString(param.getName().strref()) << PP::nbsp <<
"=" << PP::nbsp;
436 TypeSwitch<Attribute>(param.getValue())
437 .Case<IntegerAttr>([&](
auto attr) { ps.
addAsString(attr.getValue()); })
438 .Case<FloatAttr>([&](
auto attr) {
440 attr.getValue().toString(str);
445 .Default([&](
auto attr) {
446 emitOpError(op,
"with unsupported parameter attribute: ") << attr;
447 ps <<
"<unsupported-attr ";
453 void Emitter::emitGenericIntrinsic(GenericIntrinsicOp op) {
457 ps << op.getIntrinsic();
459 auto params = op.getParameters();
460 if (!params.empty()) {
462 ps.scopedBox(PP::ibox0, [&]() {
464 params.getAsRange<ParamDeclAttr>(),
465 [&](ParamDeclAttr param) { emitParamAssign(param, op); });
470 if (op.getNumResults() != 0)
471 emitTypeWithColon(op.getResult().getType());
473 if (op.getNumOperands() != 0) {
474 ps <<
"," << PP::space;
475 ps.
scopedBox(PP::ibox0, [&]() { interleaveComma(op->getOperands()); });
482 void Emitter::emitModule(FModuleOp op) {
484 ps.
cbox(4, IndentStyle::Block);
486 ps <<
"public" << PP::nbsp;
487 ps <<
"module " <<
PPExtString(legalize(op.getNameAttr()));
488 emitEnabledLayers(op.getLayers());
489 ps << PP::nbsp <<
":" << PP::end;
496 auto ports = op.getPorts();
497 emitModulePorts(ports, op.getArguments());
498 if (!ports.empty() && !op.getBodyBlock()->empty())
502 emitStatementsInBlock(*op.getBodyBlock());
505 valueNamesStorage.clear();
509 void Emitter::emitModule(FExtModuleOp op) {
511 ps.
cbox(4, IndentStyle::Block);
512 ps <<
"extmodule " <<
PPExtString(legalize(op.getNameAttr()));
513 emitEnabledLayers(op.getLayers());
514 ps << PP::nbsp <<
":" << PP::end;
521 auto ports = op.getPorts();
522 emitModulePorts(ports);
525 if (op.getDefname() && !op.getDefname()->empty()) {
527 ps <<
"defname = " <<
PPExtString(*op.getDefname());
532 emitModuleParameters(op, op.getParameters());
537 void Emitter::emitModule(FIntModuleOp op) {
539 ps.
cbox(4, IndentStyle::Block);
540 ps <<
"intmodule " <<
PPExtString(legalize(op.getNameAttr()));
541 emitEnabledLayers(op.getLayers());
542 ps << PP::nbsp <<
":" << PP::end;
549 auto ports = op.getPorts();
550 emitModulePorts(ports);
553 ps <<
"intrinsic = " <<
PPExtString(op.getIntrinsic());
557 emitModuleParameters(op, op.getParameters());
564 void Emitter::emitModulePorts(ArrayRef<PortInfo> ports,
565 Block::BlockArgListType arguments) {
566 for (
unsigned i = 0, e = ports.size(); i < e; ++i) {
568 const auto &port = ports[i];
569 ps << (port.direction ==
Direction::In ?
"input " :
"output ");
570 auto legalName = legalize(port.name);
571 if (!arguments.empty())
572 addValueName(arguments[i], legalName);
575 emitLocation(ports[i].loc);
580 void Emitter::emitModuleParameters(Operation *op, ArrayAttr parameters) {
581 for (
auto param : parameters.getAsRange<ParamDeclAttr>()) {
583 emitParamAssign(param, op,
PPExtString(
"parameter"));
589 void Emitter::emitDeclaration(LayerOp op) {
591 ps <<
"layer " <<
PPExtString(op.getSymName()) <<
", "
592 <<
PPExtString(stringifyLayerConvention(op.getConvention()));
594 if (
auto outputFile = op->getAttrOfType<hw::OutputFileAttr>(
"output_file")) {
600 emitLocationAndNewLine(op);
602 for (
auto &bodyOp : op.getBody().getOps()) {
603 TypeSwitch<Operation *>(&bodyOp)
604 .Case<LayerOp>([&](auto op) { emitDeclaration(op); })
605 .Default([&](
auto op) {
607 "not supported for emission inside layer definition");
614 void Emitter::emitDeclaration(OptionOp op) {
616 ps <<
"option " <<
PPExtString(legalize(op.getSymNameAttr())) <<
" :";
619 for (
auto caseOp : op.getBody().getOps<OptionCaseOp>()) {
621 ps << PPExtString(legalize(caseOp.getSymNameAttr()));
622 emitLocation(caseOp);
625 ps << PP::newline << PP::newline;
629 void Emitter::emitDeclaration(FormalOp op) {
631 ps <<
"formal " <<
PPExtString(op.getSymName()) <<
" of "
632 <<
PPExtString(op.getModuleName()) <<
", bound = ";
635 if (
auto outputFile = op->getAttrOfType<hw::OutputFileAttr>(
"output_file")) {
640 emitLocationAndNewLine(op);
651 return (
isExpression(op) && !isa<InvalidValueOp>(op)) ||
652 (isa<GenericIntrinsicOp>(op) && op->hasOneUse() &&
656 void Emitter::emitStatementsInBlock(Block &block) {
657 for (
auto &bodyOp : block) {
658 if (encounteredError)
662 TypeSwitch<Operation *>(&bodyOp)
663 .Case<WhenOp, WireOp, RegOp, RegResetOp, NodeOp, StopOp, SkipOp,
664 PrintFOp, AssertOp, AssumeOp, CoverOp, ConnectOp,
665 MatchingConnectOp, PropAssignOp, InstanceOp, InstanceChoiceOp,
666 AttachOp, MemOp, InvalidValueOp, SeqMemOp, CombMemOp,
667 MemoryPortOp, MemoryDebugPortOp, MemoryPortAccessOp, RefDefineOp,
668 RefForceOp, RefForceInitialOp, RefReleaseOp, RefReleaseInitialOp,
669 LayerBlockOp, GenericIntrinsicOp>(
670 [&](
auto op) { emitStatement(op); })
671 .Default([&](
auto op) {
673 ps <<
"// operation " <<
PPExtString(op->getName().getStringRef());
675 emitOpError(op,
"not supported as statement");
680 void Emitter::emitStatement(WhenOp op) {
683 emitExpression(op.getCondition());
685 emitLocationAndNewLine(op);
686 ps.
scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(op.getThenBlock()); });
688 if (!op.hasElseRegion())
695 auto &elseBlock = op.getElseBlock();
696 if (!elseBlock.empty() && &elseBlock.front() == &elseBlock.back()) {
697 if (
auto whenOp = dyn_cast<WhenOp>(&elseBlock.front())) {
698 emitStatement(whenOp);
705 ps.
scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(elseBlock); });
708 void Emitter::emitStatement(WireOp op) {
709 auto legalName = legalize(op.getNameAttr());
710 addForceable(op, legalName);
714 emitTypeWithColon(op.getResult().getType());
716 emitLocationAndNewLine(op);
719 void Emitter::emitStatement(RegOp op) {
720 auto legalName = legalize(op.getNameAttr());
721 addForceable(op, legalName);
725 emitTypeWithColon(op.getResult().getType());
726 ps <<
"," << PP::space;
727 emitExpression(op.getClockVal());
729 emitLocationAndNewLine(op);
732 void Emitter::emitStatement(RegResetOp op) {
733 auto legalName = legalize(op.getNameAttr());
734 addForceable(op, legalName);
738 ps <<
"regreset " << legalName;
739 emitTypeWithColon(op.getResult().getType());
740 ps <<
"," << PP::space;
741 emitExpression(op.getClockVal());
742 ps <<
"," << PP::space;
743 emitExpression(op.getResetSignal());
744 ps <<
"," << PP::space;
745 emitExpression(op.getResetValue());
749 ps <<
"reg " << legalName;
750 emitTypeWithColon(op.getResult().getType());
751 ps <<
"," << PP::space;
752 emitExpression(op.getClockVal());
753 ps << PP::space <<
"with :";
755 ps << PP::neverbreak;
758 ps <<
"reset => (" << PP::ibox0;
759 emitExpression(op.getResetSignal());
760 ps <<
"," << PP::space;
761 emitExpression(op.getResetValue());
762 ps <<
")" << PP::end;
765 emitLocationAndNewLine(op);
768 void Emitter::emitStatement(NodeOp op) {
769 auto legalName = legalize(op.getNameAttr());
770 addForceable(op, legalName);
772 emitAssignLike([&]() { ps <<
"node " <<
PPExtString(legalName); },
773 [&]() { emitExpression(op.getInput()); });
774 emitLocationAndNewLine(op);
777 void Emitter::emitStatement(StopOp op) {
780 ps <<
"stop(" << PP::ibox0;
781 emitExpression(op.getClock());
782 ps <<
"," << PP::space;
783 emitExpression(op.getCond());
784 ps <<
"," << PP::space;
786 ps <<
")" << PP::end;
787 if (!op.getName().empty()) {
788 ps << PP::space <<
": " <<
PPExtString(legalize(op.getNameAttr()));
791 emitLocationAndNewLine(op);
794 void Emitter::emitStatement(SkipOp op) {
797 emitLocationAndNewLine(op);
800 void Emitter::emitStatement(PrintFOp op) {
803 ps <<
"printf(" << PP::ibox0;
804 emitExpression(op.getClock());
805 ps <<
"," << PP::space;
806 emitExpression(op.getCond());
807 ps <<
"," << PP::space;
809 for (
auto operand : op.getSubstitutions()) {
810 ps <<
"," << PP::space;
811 emitExpression(operand);
813 ps <<
")" << PP::end;
814 if (!op.getName().empty()) {
815 ps << PP::space <<
": " <<
PPExtString(legalize(op.getNameAttr()));
818 emitLocationAndNewLine(op);
822 void Emitter::emitVerifStatement(T op, StringRef mnemonic) {
825 ps << mnemonic <<
"(" << PP::ibox0;
826 emitExpression(op.getClock());
827 ps <<
"," << PP::space;
828 emitExpression(op.getPredicate());
829 ps <<
"," << PP::space;
830 emitExpression(op.getEnable());
831 ps <<
"," << PP::space;
833 ps <<
")" << PP::end;
834 if (!op.getName().empty()) {
835 ps << PP::space <<
": " <<
PPExtString(legalize(op.getNameAttr()));
838 emitLocationAndNewLine(op);
841 void Emitter::emitStatement(ConnectOp op) {
845 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
846 ps <<
"invalidate" << PP::space;
847 emitExpression(op.getDest());
849 ps <<
"connect" << PP::space;
850 emitExpression(op.getDest());
851 ps <<
"," << PP::space;
852 emitExpression(op.getSrc());
856 auto emitLHS = [&]() { emitExpression(op.getDest()); };
857 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
859 emitLHS, [&]() { ps <<
"invalid"; },
PPExtString(
"is"));
862 emitLHS, [&]() { emitExpression(op.getSrc()); },
PPExtString(
"<="));
865 emitLocationAndNewLine(op);
868 void Emitter::emitStatement(MatchingConnectOp op) {
872 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
873 ps <<
"invalidate" << PP::space;
874 emitExpression(op.getDest());
876 ps <<
"connect" << PP::space;
877 emitExpression(op.getDest());
878 ps <<
"," << PP::space;
879 emitExpression(op.getSrc());
883 auto emitLHS = [&]() { emitExpression(op.getDest()); };
884 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
886 emitLHS, [&]() { ps <<
"invalid"; },
PPExtString(
"is"));
889 emitLHS, [&]() { emitExpression(op.getSrc()); },
PPExtString(
"<="));
892 emitLocationAndNewLine(op);
895 void Emitter::emitStatement(PropAssignOp op) {
898 ps <<
"propassign" << PP::space;
899 interleaveComma(op.getOperands());
901 emitLocationAndNewLine(op);
904 void Emitter::emitStatement(InstanceOp op) {
906 auto legalName = legalize(op.getNameAttr());
908 <<
PPExtString(legalize(op.getModuleNameAttr().getAttr()));
909 emitLocationAndNewLine(op);
913 SmallString<16> portName(legalName);
914 portName.push_back(
'.');
915 unsigned baseLen = portName.size();
916 for (
unsigned i = 0, e = op.getNumResults(); i < e; ++i) {
917 portName.append(legalize(op.getPortName(i)));
918 addValueName(op.getResult(i), portName);
919 portName.resize(baseLen);
923 void Emitter::emitStatement(InstanceChoiceOp op) {
925 auto legalName = legalize(op.getNameAttr());
926 ps <<
"instchoice " <<
PPExtString(legalName) <<
" of "
927 <<
PPExtString(legalize(op.getDefaultTargetAttr().getAttr())) <<
", "
928 <<
PPExtString(legalize(op.getOptionNameAttr())) <<
" :";
931 for (
const auto &[optSym, targetSym] : op.getTargetChoices()) {
933 ps << PPExtString(legalize(optSym.getLeafReference()));
935 ps << PPExtString(legalize(targetSym.getAttr()));
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(AttachOp op) {
951 emitStatementFunctionOp(
PPExtString(
"attach"), op);
954 void Emitter::emitStatement(MemOp op) {
955 auto legalName = legalize(op.getNameAttr());
956 SmallString<16> portName(legalName);
957 portName.push_back(
'.');
958 auto portNameBaseLen = portName.size();
959 for (
auto result : llvm::zip(op.getResults(), op.getPortNames())) {
960 portName.resize(portNameBaseLen);
961 portName.append(legalize(cast<StringAttr>(std::get<1>(result))));
962 addValueName(std::get<0>(result), portName);
967 emitLocationAndNewLine(op);
970 ps <<
"data-type => ";
971 emitType(op.getDataType());
976 ps <<
"read-latency => ";
979 ps <<
"write-latency => ";
983 SmallString<16> reader, writer, readwriter;
984 for (std::pair<StringAttr, MemOp::PortKind> port : op.getPorts()) {
985 auto add = [&](SmallString<16> &to, StringAttr name) {
988 to.append(name.getValue());
990 switch (port.second) {
991 case MemOp::PortKind::Read:
992 add(reader, legalize(port.first));
994 case MemOp::PortKind::Write:
995 add(writer, legalize(port.first));
997 case MemOp::PortKind::ReadWrite:
998 add(readwriter, legalize(port.first));
1000 case MemOp::PortKind::Debug:
1001 emitOpError(op,
"has unsupported 'debug' port");
1005 if (!reader.empty())
1006 ps <<
"reader => " << reader << PP::newline;
1007 if (!writer.empty())
1008 ps <<
"writer => " << writer << PP::newline;
1009 if (!readwriter.empty())
1010 ps <<
"readwriter => " << readwriter << PP::newline;
1012 ps <<
"read-under-write => ";
1013 emitAttribute(op.getRuw());
1014 setPendingNewline();
1018 void Emitter::emitStatement(SeqMemOp op) {
1021 ps <<
"smem " <<
PPExtString(legalize(op.getNameAttr()));
1022 emitTypeWithColon(op.getType());
1023 ps <<
"," << PP::space;
1024 emitAttribute(op.getRuw());
1026 emitLocationAndNewLine(op);
1029 void Emitter::emitStatement(CombMemOp op) {
1032 ps <<
"cmem " <<
PPExtString(legalize(op.getNameAttr()));
1033 emitTypeWithColon(op.getType());
1035 emitLocationAndNewLine(op);
1038 void Emitter::emitStatement(MemoryPortOp op) {
1040 addValueName(op.getData(), legalize(op.getNameAttr()));
1043 void Emitter::emitStatement(MemoryDebugPortOp op) {
1045 addValueName(op.getData(), legalize(op.getNameAttr()));
1048 void Emitter::emitStatement(MemoryPortAccessOp op) {
1052 auto port = cast<MemoryPortOp>(op.getPort().getDefiningOp());
1053 emitAttribute(port.getDirection());
1055 ps <<
" mport " <<
PPExtString(legalize(port.getNameAttr())) <<
" = ";
1058 auto *mem = port.getMemory().getDefiningOp();
1059 if (
auto seqMem = dyn_cast<SeqMemOp>(mem))
1060 ps << legalize(seqMem.getNameAttr());
1062 ps << legalize(cast<CombMemOp>(mem).getNameAttr());
1066 emitExpression(op.getIndex());
1070 emitExpression(op.getClock());
1072 emitLocationAndNewLine(op);
1075 void Emitter::emitStatement(RefDefineOp op) {
1077 emitAssignLike([&]() { emitExpression(op.getDest()); },
1078 [&]() { emitExpression(op.getSrc()); },
PPExtString(
"="),
1080 emitLocationAndNewLine(op);
1083 void Emitter::emitStatement(RefForceOp op) {
1084 emitStatementFunctionOp(
PPExtString(
"force"), op);
1087 void Emitter::emitStatement(RefForceInitialOp op) {
1089 auto constantPredicate =
1090 dyn_cast_or_null<ConstantOp>(op.getPredicate().getDefiningOp());
1091 bool hasEnable = !constantPredicate || constantPredicate.getValue() == 0;
1094 emitExpression(op.getPredicate());
1095 ps <<
":" << PP::bbox2 << PP::neverbreak << PP::newline;
1097 ps <<
"force_initial(";
1099 interleaveComma({op.getDest(), op.getSrc()});
1104 emitLocationAndNewLine(op);
1107 void Emitter::emitStatement(RefReleaseOp op) {
1108 emitStatementFunctionOp(
PPExtString(
"release"), op);
1111 void Emitter::emitStatement(RefReleaseInitialOp op) {
1113 auto constantPredicate =
1114 dyn_cast_or_null<ConstantOp>(op.getPredicate().getDefiningOp());
1115 bool hasEnable = !constantPredicate || constantPredicate.getValue() == 0;
1118 emitExpression(op.getPredicate());
1119 ps <<
":" << PP::bbox2 << PP::neverbreak << PP::newline;
1121 ps <<
"release_initial(";
1122 emitExpression(op.getDest());
1126 emitLocationAndNewLine(op);
1129 void Emitter::emitStatement(LayerBlockOp op) {
1131 ps <<
"layerblock " << op.getLayerName().getLeafReference() <<
" :";
1132 emitLocationAndNewLine(op);
1133 auto *body = op.getBody();
1134 ps.
scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(*body); });
1137 void Emitter::emitStatement(InvalidValueOp op) {
1140 if (llvm::all_of(op->getUses(), [&](OpOperand &use) {
1141 return use.getOperandNumber() == 1 &&
1142 isa<ConnectOp, MatchingConnectOp>(use.getOwner());
1148 auto name = circuitNamespace.newName(
"_invalid");
1149 addValueName(op, name);
1151 emitType(op.getType());
1152 emitLocationAndNewLine(op);
1158 emitLocationAndNewLine(op);
1161 void Emitter::emitStatement(GenericIntrinsicOp op) {
1164 emitGenericIntrinsic(op);
1167 auto name = circuitNamespace.newName(
"_gen_int");
1168 addValueName(op.getResult(), name);
1169 emitAssignLike([&]() { ps <<
"node " <<
PPExtString(name); },
1170 [&]() { emitGenericIntrinsic(op); });
1172 emitLocationAndNewLine(op);
1175 void Emitter::emitExpression(Value value) {
1178 if (
auto name = lookupEmittedName(value)) {
1184 auto op = value.getDefiningOp();
1185 assert(op &&
"value must either be a block arg or the result of an op");
1186 TypeSwitch<Operation *>(op)
1189 ConstantOp, SpecialConstantOp, SubfieldOp, SubindexOp, SubaccessOp,
1190 OpenSubfieldOp, OpenSubindexOp,
1192 AddPrimOp, SubPrimOp, MulPrimOp, DivPrimOp, RemPrimOp, AndPrimOp,
1193 OrPrimOp, XorPrimOp, LEQPrimOp, LTPrimOp, GEQPrimOp, GTPrimOp,
1194 EQPrimOp, NEQPrimOp, CatPrimOp, DShlPrimOp, DShlwPrimOp, DShrPrimOp,
1196 AsSIntPrimOp, AsUIntPrimOp, AsAsyncResetPrimOp, AsClockPrimOp,
1197 CvtPrimOp, NegPrimOp, NotPrimOp, AndRPrimOp, OrRPrimOp, XorRPrimOp,
1199 BitsPrimOp, HeadPrimOp, TailPrimOp, PadPrimOp, MuxPrimOp, ShlPrimOp,
1200 ShrPrimOp, UninferredResetCastOp, ConstCastOp, StringConstantOp,
1201 FIntegerConstantOp, BoolConstantOp, DoubleConstantOp, ListCreateOp,
1202 UnresolvedPathOp, GenericIntrinsicOp,
1204 RefSendOp, RefResolveOp, RefSubOp, RWProbeOp, RefCastOp>(
1206 ps.
scopedBox(PP::ibox0, [&]() { emitExpression(op); });
1208 .Default([&](
auto op) {
1209 emitOpError(op,
"not supported as expression");
1210 ps <<
"<unsupported-expr-" <<
PPExtString(op->getName().stripDialect())
1215 void Emitter::emitExpression(ConstantOp op) {
1217 emitType(op.getType(),
false);
1224 void Emitter::emitExpression(SpecialConstantOp op) {
1225 auto emitInner = [&]() {
1232 .
Case<ClockType>([&](
auto type) {
1237 .Case<ResetType>([&](
auto type) { emitInner(); })
1238 .Case<AsyncResetType>([&](
auto type) {
1239 ps <<
"asAsyncReset(";
1246 void Emitter::emitExpression(SubfieldOp op) {
1247 BundleType type = op.getInput().getType();
1248 emitExpression(op.getInput());
1249 ps <<
"." << legalize(type.getElementNameAttr(op.getFieldIndex()));
1253 void Emitter::emitExpression(SubindexOp op) {
1254 emitExpression(op.getInput());
1261 void Emitter::emitExpression(SubaccessOp op) {
1262 emitExpression(op.getInput());
1264 emitExpression(op.getIndex());
1268 void Emitter::emitExpression(OpenSubfieldOp op) {
1269 auto type = op.getInput().getType();
1270 emitExpression(op.getInput());
1271 ps <<
"." << legalize(type.getElementNameAttr(op.getFieldIndex()));
1274 void Emitter::emitExpression(OpenSubindexOp op) {
1275 emitExpression(op.getInput());
1281 void Emitter::emitExpression(RefSendOp op) {
1283 emitExpression(op.getBase());
1287 void Emitter::emitExpression(RefResolveOp op) {
1289 emitExpression(op.getRef());
1293 void Emitter::emitExpression(RefSubOp op) {
1294 emitExpression(op.getInput());
1296 .
Case<FVectorType>([&](
auto type) {
1302 [&](
auto type) { ps <<
"." << type.getElementName(op.getIndex()); });
1305 void Emitter::emitExpression(RWProbeOp op) {
1309 auto target = symInfos->get().irn.lookup(op.getTarget());
1311 if (target.isPort()) {
1312 auto mod = cast<FModuleOp>(target.getOp());
1313 auto port = target.getPort();
1314 base = mod.getArgument(port);
1316 base = cast<hw::InnerSymbolOpInterface>(target.getOp()).getTargetResult();
1319 emitExpression(base);
1322 auto fieldID = target.getField();
1323 auto type = base.getType();
1326 .
Case<FVectorType, OpenVectorType>([&](
auto vecTy) {
1327 auto index = vecTy.getIndexForFieldID(fieldID);
1331 auto [subtype, subfieldID] = vecTy.getSubTypeByFieldID(fieldID);
1333 fieldID = subfieldID;
1335 .Case<BundleType, OpenBundleType>([&](
auto bundleTy) {
1336 auto index = bundleTy.getIndexForFieldID(fieldID);
1337 ps <<
"." << bundleTy.getElementName(index);
1338 auto [subtype, subfieldID] = bundleTy.getSubTypeByFieldID(fieldID);
1340 fieldID = subfieldID;
1346 void Emitter::emitExpression(RefCastOp op) { emitExpression(op.getInput()); }
1348 void Emitter::emitExpression(UninferredResetCastOp op) {
1349 emitExpression(op.getInput());
1352 void Emitter::emitExpression(FIntegerConstantOp op) {
1358 void Emitter::emitExpression(BoolConstantOp op) {
1359 ps <<
"Bool(" << (op.getValue() ?
"true" :
"false") <<
")";
1362 void Emitter::emitExpression(DoubleConstantOp op) {
1368 SmallString<16> str;
1369 op.getValueAttr().getValue().toString(str);
1374 void Emitter::emitExpression(StringConstantOp op) {
1380 void Emitter::emitExpression(ListCreateOp op) {
1381 return emitLiteralExpression(op.getType(), op.getElements());
1384 void Emitter::emitExpression(UnresolvedPathOp op) {
1390 void Emitter::emitExpression(GenericIntrinsicOp op) {
1391 emitGenericIntrinsic(op);
1394 void Emitter::emitExpression(ConstCastOp op) { emitExpression(op.getInput()); }
1396 void Emitter::emitPrimExpr(StringRef mnemonic, Operation *op,
1397 ArrayRef<uint32_t> attrs) {
1398 ps << mnemonic <<
"(" << PP::ibox0;
1399 interleaveComma(op->getOperands());
1400 if (!op->getOperands().empty() && !attrs.empty())
1401 ps <<
"," << PP::space;
1402 interleaveComma(attrs, [&](
auto attr) { ps.
addAsString(attr); });
1403 ps <<
")" << PP::end;
1406 void Emitter::emitAttribute(MemDirAttr attr) {
1408 case MemDirAttr::Infer:
1411 case MemDirAttr::Read:
1414 case MemDirAttr::Write:
1417 case MemDirAttr::ReadWrite:
1423 void Emitter::emitAttribute(RUWAttr attr) {
1425 case RUWAttr::Undefined:
1438 void Emitter::emitType(Type type,
bool includeConst) {
1439 if (includeConst &&
isConst(type))
1441 auto emitWidth = [&](std::optional<int32_t>
width) {
1450 .
Case<ClockType>([&](
auto) { ps <<
"Clock"; })
1451 .Case<ResetType>([&](
auto) { ps <<
"Reset"; })
1452 .Case<AsyncResetType>([&](
auto) { ps <<
"AsyncReset"; })
1453 .Case<UIntType>([&](
auto type) {
1455 emitWidth(type.getWidth());
1457 .Case<SIntType>([&](
auto type) {
1459 emitWidth(type.getWidth());
1461 .Case<AnalogType>([&](
auto type) {
1463 emitWidth(type.getWidth());
1465 .Case<OpenBundleType, BundleType>([&](
auto type) {
1467 if (!type.getElements().empty())
1469 bool anyEmitted =
false;
1471 for (
auto &element : type.getElements()) {
1473 ps <<
"," << PP::space;
1474 ps.scopedBox(PP::ibox2, [&]() {
1477 ps << legalize(element.name);
1478 emitTypeWithColon(element.type);
1487 .Case<OpenVectorType, FVectorType, CMemoryType>([&](
auto type) {
1488 emitType(type.getElementType());
1493 .Case<RefType>([&](RefType type) {
1494 if (type.getForceable())
1497 ps.
cbox(2, IndentStyle::Block);
1499 emitType(type.getType());
1500 if (
auto layer = type.getLayer()) {
1503 emitSymbol(type.getLayer());
1508 .Case<AnyRefType>([&](AnyRefType type) { ps <<
"AnyRef"; })
1509 .Case<StringType>([&](StringType type) { ps <<
"String"; })
1510 .Case<FIntegerType>([&](FIntegerType type) { ps <<
"Integer"; })
1511 .Case<BoolType>([&](BoolType type) { ps <<
"Bool"; })
1512 .Case<DoubleType>([&](DoubleType type) { ps <<
"Double"; })
1513 .Case<PathType>([&](PathType type) { ps <<
"Path"; })
1514 .Case<ListType>([&](ListType type) {
1516 emitType(type.getElementType());
1519 .Default([&](
auto type) {
1520 llvm_unreachable(
"all types should be implemented");
1526 void Emitter::emitLocation(Location loc) {
1528 ps << PP::neverbreak;
1530 dyn_cast_or_null<FileLineColLoc, LocationAttr>(LocationAttr(loc))) {
1531 ps <<
" @[" << fileLoc.getFilename().getValue();
1532 if (
auto line = fileLoc.getLine()) {
1535 if (
auto col = fileLoc.getColumn()) {
1552 std::optional<size_t> targetLineLength,
1554 Emitter emitter(os, version,
1555 targetLineLength.value_or(defaultTargetLineLength));
1556 for (
auto &op : *module.getBody()) {
1557 if (
auto circuitOp = dyn_cast<CircuitOp>(op))
1558 emitter.emitCircuit(circuitOp);
1560 return emitter.finalize();
1565 "target-line-length",
1566 llvm::cl::desc(
"Target line length for emitted .fir"),
1567 llvm::cl::value_desc(
"number of chars"),
1568 llvm::cl::init(defaultTargetLineLength));
1569 static mlir::TranslateFromMLIRRegistration toFIR(
1570 "export-firrtl",
"emit FIRRTL dialect operations to .fir output",
1571 [](ModuleOp module, llvm::raw_ostream &os) {
1574 [](mlir::DialectRegistry ®istry) {
1575 registry.insert<chirrtl::CHIRRTLDialect>();
1576 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(4, 0, 0)
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.