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"
31using namespace firrtl;
32using namespace chirrtl;
33using namespace pretty;
42constexpr 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 emitDeclaration(SimulationOp op);
65 void emitFormalLike(Operation *op, StringRef keyword, StringAttr symName,
66 StringAttr moduleName, DictionaryAttr params);
67 void emitEnabledLayers(ArrayRef<Attribute> layers);
69 void emitParamAssign(ParamDeclAttr param, Operation *op,
70 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
71 void emitParamValue(Attribute value, Operation *op);
73 void emitGenericIntrinsic(GenericIntrinsicOp op);
76 void emitStatementsInBlock(
Block &block);
77 void emitStatement(WhenOp op);
78 void emitStatement(WireOp op);
79 void emitStatement(RegOp op);
80 void emitStatement(RegResetOp op);
81 void emitStatement(NodeOp op);
82 void emitStatement(StopOp op);
83 void emitStatement(SkipOp op);
84 void emitFormatString(Operation *op, StringRef formatString, OperandRange ops,
85 llvm::SmallVectorImpl<Value> &substitutions);
87 void emitPrintfLike(T op, StringAttr fileName);
88 void emitStatement(PrintFOp op);
89 void emitStatement(FPrintFOp op);
90 void emitStatement(FFlushOp op);
91 void emitStatement(ConnectOp op);
92 void emitStatement(MatchingConnectOp op);
93 void emitStatement(PropAssignOp op);
94 void emitStatement(InstanceOp op);
95 void emitStatement(InstanceChoiceOp op);
96 void emitStatement(AttachOp op);
97 void emitStatement(MemOp op);
98 void emitStatement(InvalidValueOp op);
99 void emitStatement(CombMemOp op);
100 void emitStatement(SeqMemOp op);
101 void emitStatement(MemoryPortOp op);
102 void emitStatement(MemoryDebugPortOp op);
103 void emitStatement(MemoryPortAccessOp op);
104 void emitStatement(RefDefineOp op);
105 void emitStatement(RefForceOp op);
106 void emitStatement(RefForceInitialOp op);
107 void emitStatement(RefReleaseOp op);
108 void emitStatement(RefReleaseInitialOp op);
109 void emitStatement(LayerBlockOp op);
110 void emitStatement(GenericIntrinsicOp op);
113 void emitVerifStatement(T op, StringRef mnemonic);
114 void emitStatement(AssertOp op) { emitVerifStatement(op,
"assert"); }
115 void emitStatement(AssumeOp op) { emitVerifStatement(op,
"assume"); }
116 void emitStatement(CoverOp op) { emitVerifStatement(op,
"cover"); }
119 void emitExpression(Value value);
120 void emitExpression(ConstantOp op);
121 void emitExpression(SpecialConstantOp op);
122 void emitExpression(SubfieldOp op);
123 void emitExpression(SubindexOp op);
124 void emitExpression(SubaccessOp op);
125 void emitExpression(OpenSubfieldOp op);
126 void emitExpression(OpenSubindexOp op);
127 void emitExpression(RefResolveOp op);
128 void emitExpression(RefSendOp op);
129 void emitExpression(RefSubOp op);
130 void emitExpression(RWProbeOp op);
131 void emitExpression(RefCastOp op);
132 void emitExpression(UninferredResetCastOp op);
133 void emitExpression(ConstCastOp op);
134 void emitExpression(StringConstantOp op);
135 void emitExpression(FIntegerConstantOp op);
136 void emitExpression(BoolConstantOp op);
137 void emitExpression(DoubleConstantOp op);
138 void emitExpression(ListCreateOp op);
139 void emitExpression(UnresolvedPathOp op);
140 void emitExpression(GenericIntrinsicOp op);
142 void emitPrimExpr(StringRef mnemonic, Operation *op,
143 ArrayRef<uint32_t> attrs = {});
145 void emitExpression(BitsPrimOp op) {
146 emitPrimExpr(
"bits", op, {op.getHi(), op.getLo()});
148 void emitExpression(HeadPrimOp op) {
149 emitPrimExpr(
"head", op, op.getAmount());
151 void emitExpression(TailPrimOp op) {
152 emitPrimExpr(
"tail", op, op.getAmount());
154 void emitExpression(PadPrimOp op) { emitPrimExpr(
"pad", op, op.getAmount()); }
155 void emitExpression(ShlPrimOp op) { emitPrimExpr(
"shl", op, op.getAmount()); }
156 void emitExpression(ShrPrimOp op) { emitPrimExpr(
"shr", op, op.getAmount()); }
158 void emitExpression(TimeOp op){};
161#define HANDLE(OPTYPE, MNEMONIC) \
162 void emitExpression(OPTYPE op) { emitPrimExpr(MNEMONIC, op); }
161#define HANDLE(OPTYPE, MNEMONIC) \ …
178 HANDLE(DShlPrimOp,
"dshl");
179 HANDLE(DShlwPrimOp,
"dshlw");
180 HANDLE(DShrPrimOp,
"dshr");
182 HANDLE(AsSIntPrimOp,
"asSInt");
183 HANDLE(AsUIntPrimOp,
"asUInt");
184 HANDLE(AsAsyncResetPrimOp,
"asAsyncReset");
185 HANDLE(AsClockPrimOp,
"asClock");
189 HANDLE(AndRPrimOp,
"andr");
191 HANDLE(XorRPrimOp,
"xorr");
195 void emitAttribute(MemDirAttr attr);
196 void emitAttribute(RUWAttr attr);
199 void emitType(Type type,
bool includeConst =
true);
200 void emitTypeWithColon(Type type) {
201 ps << PP::space <<
":" << PP::nbsp;
206 void emitLocation(Location loc);
207 void emitLocation(Operation *op) { emitLocation(op->getLoc()); }
208 template <
typename... Args>
209 void emitLocationAndNewLine(Args... args) {
212 ps << PP::neverbreak;
213 emitLocation(args...);
217 void emitAssignLike(llvm::function_ref<
void()> emitLHS,
218 llvm::function_ref<
void()> emitRHS,
220 std::optional<PPExtString> wordBeforeLHS = std::nullopt) {
222 ps.scopedBox(PP::ibox2, [&]() {
224 ps << *wordBeforeLHS << PP::space;
228 ps << PP::space << syntax << PP::nbsp;
230 ps.scopedBox(PP::ibox0, [&]() { emitRHS(); });
235 void emitSubExprIBox2(Value v) {
236 ps.scopedBox(PP::ibox2, [&]() { emitExpression(v); });
241 template <
typename Container,
typename EachFn>
242 void interleaveComma(
const Container &c, EachFn eachFn) {
243 llvm::interleave(c, eachFn, [&]() { ps <<
"," << PP::space; });
248 void interleaveComma(ValueRange ops) {
249 return interleaveComma(ops, [&](Value v) { emitSubExprIBox2(v); });
252 void emitStatementFunctionOp(
PPExtString name, Operation *op) {
255 ps.scopedBox(PP::ibox0, [&]() {
256 interleaveComma(op->getOperands());
259 emitLocationAndNewLine(op);
262 template <
typename EachFn,
typename Range>
263 void emitLiteralExpression(Type type,
const Range &r, EachFn eachFn) {
266 ps.scopedBox(PP::ibox0, [&]() {
267 interleaveComma(r, eachFn);
272 void emitLiteralExpression(Type type, ValueRange values) {
273 return emitLiteralExpression(type, values,
274 [&](Value v) { emitSubExprIBox2(v); });
278 void emitSymbol(SymbolRefAttr symbol) {
279 ps.ibox(2, IndentStyle::Block);
280 ps << symbol.getRootReference();
281 for (
auto nested : symbol.getNestedReferences()) {
284 ps << nested.getAttr();
291 InFlightDiagnostic emitError(Operation *op,
const Twine &message) {
292 encounteredError =
true;
293 return op->emitError(message);
297 InFlightDiagnostic emitOpError(Operation *op,
const Twine &message) {
298 encounteredError =
true;
299 return op->emitOpError(message);
304 std::optional<StringRef> lookupEmittedName(Value value) {
305 auto it = valueNames.find(value);
306 if (it != valueNames.end())
313 void emitPendingNewlineIfNeeded() {
314 if (pendingNewline) {
315 pendingNewline =
false;
319 void setPendingNewline() {
321 pendingNewline =
true;
324 void startStatement() { emitPendingNewlineIfNeeded(); }
342 bool pendingNewline =
false;
345 bool encounteredError =
false;
350 DenseMap<Value, StringRef> valueNames;
351 StringSet<> valueNamesStorage;
355 StringAttr legalize(StringAttr attr) {
356 StringRef str = attr.getValue();
357 if (str.empty() || !
isdigit(str.front()))
359 return StringAttr::get(attr.getContext(),
"`" + Twine(attr) +
"`");
362 void addValueName(Value value, StringAttr attr) {
363 valueNames.insert({value, attr.getValue()});
365 void addValueName(Value value, StringRef str) {
366 auto it = valueNamesStorage.insert(str);
367 valueNames.insert({value, it.first->getKey()});
369 void addForceable(Forceable op, StringAttr attr) {
370 addValueName(op.getData(), attr);
371 if (op.isForceable()) {
372 SmallString<32> rwName;
373 (Twine(
"rwprobe(") + attr.strref() +
")").
toVector(rwName);
374 addValueName(op.getDataRef(), rwName);
383 SymbolTable symbolTable;
386 SymInfos(Operation *op) : symbolTable(op), istc(op){};
388 std::optional<std::reference_wrapper<SymInfos>> symInfos;
395LogicalResult Emitter::finalize() {
return failure(encounteredError); }
398void Emitter::emitCircuit(CircuitOp op) {
399 circuitNamespace.add(op);
400 SymInfos circuitSymInfos(op);
401 symInfos = circuitSymInfos;
403 ps <<
"FIRRTL version ";
410 ps <<
"circuit " <<
PPExtString(legalize(op.getNameAttr())) <<
" :";
414 if (encounteredError)
416 TypeSwitch<Operation *>(&bodyOp)
417 .Case<FModuleOp, FExtModuleOp, FIntModuleOp>([&](
auto op) {
421 .Case<LayerOp, OptionOp, FormalOp, SimulationOp>(
422 [&](
auto op) { emitDeclaration(op); })
423 .Default([&](
auto op) {
424 emitOpError(op,
"not supported for emission inside circuit");
428 circuitNamespace.clear();
429 symInfos = std::nullopt;
432void Emitter::emitEnabledLayers(ArrayRef<Attribute> layers) {
433 for (
auto layer : layers) {
435 ps.
cbox(2, IndentStyle::Block);
436 ps <<
"enablelayer" << PP::space;
437 emitSymbol(cast<SymbolRefAttr>(layer));
442void Emitter::emitParamAssign(ParamDeclAttr param, Operation *op,
443 std::optional<PPExtString> wordBeforeLHS) {
445 ps << *wordBeforeLHS << PP::nbsp;
447 ps <<
PPExtString(param.getName().strref()) << PP::nbsp <<
"=" << PP::nbsp;
448 emitParamValue(param.getValue(), op);
451void Emitter::emitParamValue(Attribute value, Operation *op) {
452 TypeSwitch<Attribute>(value)
453 .Case<IntegerAttr>([&](
auto attr) { ps.
addAsString(attr.getValue()); })
454 .Case<FloatAttr>([&](
auto attr) {
456 attr.getValue().toString(str);
461 .Case<ArrayAttr>([&](
auto attr) {
464 interleaveComma(attr.getValue(),
465 [&](
auto element) { emitParamValue(element, op); });
469 .Case<DictionaryAttr>([&](
auto attr) {
472 interleaveComma(attr.getValue(), [&](
auto field) {
473 ps << PPExtString(field.getName()) << PP::nbsp <<
"=" << PP::nbsp;
474 emitParamValue(field.getValue(), op);
479 .Default([&](
auto attr) {
480 emitOpError(op,
"with unsupported parameter attribute: ") << attr;
481 ps <<
"<unsupported-attr ";
487void Emitter::emitGenericIntrinsic(GenericIntrinsicOp op) {
491 ps << op.getIntrinsic();
493 auto params = op.getParameters();
494 if (!params.empty()) {
496 ps.scopedBox(PP::ibox0, [&]() {
498 params.getAsRange<ParamDeclAttr>(),
499 [&](ParamDeclAttr param) { emitParamAssign(param, op); });
504 if (op.getNumResults() != 0)
505 emitTypeWithColon(op.getResult().getType());
507 if (op.getNumOperands() != 0) {
508 ps <<
"," << PP::space;
509 ps.
scopedBox(PP::ibox0, [&]() { interleaveComma(op->getOperands()); });
516void Emitter::emitModule(FModuleOp op) {
518 ps.
cbox(4, IndentStyle::Block);
520 ps <<
"public" << PP::nbsp;
521 ps <<
"module " <<
PPExtString(legalize(op.getNameAttr()));
522 emitEnabledLayers(op.getLayers());
523 ps << PP::nbsp <<
":" << PP::end;
530 auto ports = op.getPorts();
531 emitModulePorts(ports, op.getArguments());
532 if (!ports.empty() && !op.getBodyBlock()->empty())
536 emitStatementsInBlock(*op.getBodyBlock());
539 valueNamesStorage.clear();
543void Emitter::emitModule(FExtModuleOp op) {
545 ps.
cbox(4, IndentStyle::Block);
546 ps <<
"extmodule " <<
PPExtString(legalize(op.getNameAttr()));
547 emitEnabledLayers(op.getLayers());
548 ps << PP::nbsp <<
":" << PP::end;
555 auto ports = op.getPorts();
556 emitModulePorts(ports);
559 if (op.getDefname() && !op.getDefname()->empty()) {
561 ps <<
"defname = " <<
PPExtString(*op.getDefname());
566 emitModuleParameters(op, op.getParameters());
571void Emitter::emitModule(FIntModuleOp op) {
573 ps.
cbox(4, IndentStyle::Block);
574 ps <<
"intmodule " <<
PPExtString(legalize(op.getNameAttr()));
575 emitEnabledLayers(op.getLayers());
576 ps << PP::nbsp <<
":" << PP::end;
583 auto ports = op.getPorts();
584 emitModulePorts(ports);
587 ps <<
"intrinsic = " <<
PPExtString(op.getIntrinsic());
591 emitModuleParameters(op, op.getParameters());
598void Emitter::emitModulePorts(ArrayRef<PortInfo> ports,
599 Block::BlockArgListType arguments) {
600 for (
unsigned i = 0, e = ports.size(); i < e; ++i) {
602 const auto &port = ports[i];
603 ps << (port.direction == Direction::In ?
"input " :
"output ");
604 auto legalName = legalize(port.name);
605 if (!arguments.empty())
606 addValueName(arguments[i], legalName);
609 emitLocation(ports[i].loc);
614void Emitter::emitModuleParameters(Operation *op, ArrayAttr parameters) {
615 for (
auto param : parameters.getAsRange<ParamDeclAttr>()) {
617 emitParamAssign(param, op,
PPExtString(
"parameter"));
623void Emitter::emitDeclaration(LayerOp op) {
625 ps <<
"layer " <<
PPExtString(op.getSymName()) <<
", "
626 <<
PPExtString(stringifyLayerConvention(op.getConvention()));
628 if (
auto outputFile = op->getAttrOfType<hw::OutputFileAttr>(
"output_file")) {
634 emitLocationAndNewLine(op);
636 for (
auto &bodyOp : op.getBody().getOps()) {
637 TypeSwitch<Operation *>(&bodyOp)
638 .Case<LayerOp>([&](
auto op) { emitDeclaration(op); })
639 .Default([&](
auto op) {
641 "not supported for emission inside layer definition");
648void Emitter::emitDeclaration(OptionOp op) {
650 ps <<
"option " <<
PPExtString(legalize(op.getSymNameAttr())) <<
" :";
653 for (
auto caseOp : op.getBody().getOps<OptionCaseOp>()) {
655 ps <<
PPExtString(legalize(caseOp.getSymNameAttr()));
656 emitLocation(caseOp);
659 ps << PP::newline << PP::newline;
663void Emitter::emitDeclaration(FormalOp op) {
664 emitFormalLike(op,
"formal", op.getSymNameAttr(),
665 op.getModuleNameAttr().getAttr(), op.getParameters());
669void Emitter::emitDeclaration(SimulationOp op) {
670 emitFormalLike(op,
"simulation", op.getSymNameAttr(),
671 op.getModuleNameAttr().getAttr(), op.getParameters());
675void Emitter::emitFormalLike(Operation *op, StringRef keyword,
676 StringAttr symName, StringAttr moduleName,
677 DictionaryAttr params) {
679 ps.
cbox(4, IndentStyle::Block);
680 ps << keyword <<
" " <<
PPExtString(legalize(symName));
682 ps << PP::nbsp <<
":" << PP::end;
687 for (
auto param : params) {
689 ps <<
PPExtString(param.getName()) << PP::nbsp <<
"=" << PP::nbsp;
690 emitParamValue(param.getValue(), op);
704 return (
isExpression(op) && !isa<InvalidValueOp>(op)) ||
705 (isa<GenericIntrinsicOp>(op) && op->hasOneUse() &&
709void Emitter::emitStatementsInBlock(Block &block) {
710 for (
auto &bodyOp : block) {
711 if (encounteredError)
715 TypeSwitch<Operation *>(&bodyOp)
716 .Case<WhenOp, WireOp, RegOp, RegResetOp, NodeOp, StopOp, SkipOp,
717 PrintFOp, FPrintFOp, FFlushOp, AssertOp, AssumeOp, CoverOp,
718 ConnectOp, MatchingConnectOp, PropAssignOp, InstanceOp,
719 InstanceChoiceOp, AttachOp, MemOp, InvalidValueOp, SeqMemOp,
720 CombMemOp, MemoryPortOp, MemoryDebugPortOp, MemoryPortAccessOp,
721 RefDefineOp, RefForceOp, RefForceInitialOp, RefReleaseOp,
722 RefReleaseInitialOp, LayerBlockOp, GenericIntrinsicOp>(
723 [&](
auto op) { emitStatement(op); })
724 .Default([&](
auto op) {
726 ps <<
"// operation " <<
PPExtString(op->getName().getStringRef());
728 emitOpError(op,
"not supported as statement");
733void Emitter::emitStatement(WhenOp op) {
736 emitExpression(op.getCondition());
738 emitLocationAndNewLine(op);
739 ps.
scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(op.getThenBlock()); });
741 if (!op.hasElseRegion())
748 auto &elseBlock = op.getElseBlock();
749 if (!elseBlock.empty() && &elseBlock.front() == &elseBlock.back()) {
750 if (
auto whenOp = dyn_cast<WhenOp>(&elseBlock.front())) {
751 emitStatement(whenOp);
758 ps.
scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(elseBlock); });
761void Emitter::emitStatement(WireOp op) {
762 auto legalName = legalize(op.getNameAttr());
763 addForceable(op, legalName);
767 emitTypeWithColon(op.getResult().getType());
769 emitLocationAndNewLine(op);
772void Emitter::emitStatement(RegOp op) {
773 auto legalName = legalize(op.getNameAttr());
774 addForceable(op, legalName);
778 emitTypeWithColon(op.getResult().getType());
779 ps <<
"," << PP::space;
780 emitExpression(op.getClockVal());
782 emitLocationAndNewLine(op);
785void Emitter::emitStatement(RegResetOp op) {
786 auto legalName = legalize(op.getNameAttr());
787 addForceable(op, legalName);
791 ps <<
"regreset " << legalName;
792 emitTypeWithColon(op.getResult().getType());
793 ps <<
"," << PP::space;
794 emitExpression(op.getClockVal());
795 ps <<
"," << PP::space;
796 emitExpression(op.getResetSignal());
797 ps <<
"," << PP::space;
798 emitExpression(op.getResetValue());
802 ps <<
"reg " << legalName;
803 emitTypeWithColon(op.getResult().getType());
804 ps <<
"," << PP::space;
805 emitExpression(op.getClockVal());
806 ps << PP::space <<
"with :";
808 ps << PP::neverbreak;
811 ps <<
"reset => (" << PP::ibox0;
812 emitExpression(op.getResetSignal());
813 ps <<
"," << PP::space;
814 emitExpression(op.getResetValue());
815 ps <<
")" << PP::end;
818 emitLocationAndNewLine(op);
821void Emitter::emitStatement(NodeOp op) {
822 auto legalName = legalize(op.getNameAttr());
823 addForceable(op, legalName);
825 emitAssignLike([&]() { ps <<
"node " <<
PPExtString(legalName); },
826 [&]() { emitExpression(op.getInput()); });
827 emitLocationAndNewLine(op);
830void Emitter::emitStatement(StopOp op) {
833 ps <<
"stop(" << PP::ibox0;
834 emitExpression(op.getClock());
835 ps <<
"," << PP::space;
836 emitExpression(op.getCond());
837 ps <<
"," << PP::space;
839 ps <<
")" << PP::end;
840 if (!op.getName().empty()) {
841 ps << PP::space <<
": " <<
PPExtString(legalize(op.getNameAttr()));
844 emitLocationAndNewLine(op);
847void Emitter::emitStatement(SkipOp op) {
850 emitLocationAndNewLine(op);
853void Emitter::emitFormatString(Operation *op, StringRef origFormatString,
854 OperandRange substitutionOperands,
855 llvm::SmallVectorImpl<Value> &substitutions) {
864 SmallString<64> formatString;
865 for (
size_t i = 0, e = origFormatString.size(), opIdx = 0; i != e; ++i) {
866 auto c = origFormatString[i];
869 formatString.push_back(c);
872 SmallString<6> width;
873 c = origFormatString[++i];
876 c = origFormatString[++i];
885 formatString.append(width);
888 substitutions.push_back(substitutionOperands[opIdx++]);
891 formatString.push_back(c);
896 if (origFormatString.slice(i, i + 4) ==
"{{}}") {
897 formatString.append(
"{{");
898 TypeSwitch<Operation *>(substitutionOperands[opIdx++].getDefiningOp())
900 [&](
auto time) { formatString.append(
"SimulationTime"); })
901 .Case<HierarchicalModuleNameOp>([&](
auto time) {
902 formatString.append(
"HierarchicalModuleName");
905 emitError(op,
"unsupported fstring substitution type");
907 formatString.append(
"}}");
912 formatString.push_back(c);
918void Emitter::emitStatement(PrintFOp op) {
921 ps <<
"printf(" << PP::ibox0;
922 emitExpression(op.getClock());
923 ps <<
"," << PP::space;
924 emitExpression(op.getCond());
925 ps <<
"," << PP::space;
927 SmallVector<Value, 4> substitutions;
928 emitFormatString(op, op.getFormatString(), op.getSubstitutions(),
930 for (
auto operand : substitutions) {
931 ps <<
"," << PP::space;
932 emitExpression(operand);
934 ps <<
")" << PP::end;
935 if (!op.getName().empty()) {
936 ps << PP::space <<
": " <<
PPExtString(legalize(op.getNameAttr()));
939 emitLocationAndNewLine(op);
942void Emitter::emitStatement(FPrintFOp op) {
945 ps <<
"fprintf(" << PP::ibox0;
946 emitExpression(op.getClock());
947 ps <<
"," << PP::space;
948 emitExpression(op.getCond());
949 ps <<
"," << PP::space;
951 SmallVector<Value, 4> outputFileSubstitutions;
952 emitFormatString(op, op.getOutputFile(), op.getOutputFileSubstitutions(),
953 outputFileSubstitutions);
954 if (!outputFileSubstitutions.empty()) {
955 ps <<
"," << PP::space;
956 interleaveComma(outputFileSubstitutions);
959 ps <<
"," << PP::space;
960 SmallVector<Value, 4> substitutions;
961 emitFormatString(op, op.getFormatString(), op.getSubstitutions(),
963 if (!substitutions.empty()) {
964 ps <<
"," << PP::space;
965 interleaveComma(substitutions);
968 ps <<
")" << PP::end;
969 if (!op.getName().empty()) {
970 ps << PP::space <<
": " <<
PPExtString(legalize(op.getNameAttr()));
973 emitLocationAndNewLine(op);
976void Emitter::emitStatement(FFlushOp op) {
979 ps <<
"fflush(" << PP::ibox0;
980 emitExpression(op.getClock());
981 ps <<
"," << PP::space;
982 emitExpression(op.getCond());
983 if (op.getOutputFileAttr()) {
984 ps <<
"," << PP::space;
985 SmallVector<Value, 4> substitutions;
986 emitFormatString(op, op.getOutputFileAttr(),
987 op.getOutputFileSubstitutions(), substitutions);
988 if (!substitutions.empty()) {
989 ps <<
"," << PP::space;
990 interleaveComma(substitutions);
993 ps <<
")" << PP::end;
995 emitLocationAndNewLine(op);
999void Emitter::emitVerifStatement(T op, StringRef mnemonic) {
1002 ps << mnemonic <<
"(" << PP::ibox0;
1003 emitExpression(op.getClock());
1004 ps <<
"," << PP::space;
1005 emitExpression(op.getPredicate());
1006 ps <<
"," << PP::space;
1007 emitExpression(op.getEnable());
1008 ps <<
"," << PP::space;
1010 ps <<
")" << PP::end;
1011 if (!op.getName().empty()) {
1012 ps << PP::space <<
": " <<
PPExtString(legalize(op.getNameAttr()));
1015 emitLocationAndNewLine(op);
1018void Emitter::emitStatement(ConnectOp op) {
1022 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
1023 ps <<
"invalidate" << PP::space;
1024 emitExpression(op.getDest());
1026 ps <<
"connect" << PP::space;
1027 emitExpression(op.getDest());
1028 ps <<
"," << PP::space;
1029 emitExpression(op.getSrc());
1033 auto emitLHS = [&]() { emitExpression(op.getDest()); };
1034 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
1036 emitLHS, [&]() { ps <<
"invalid"; },
PPExtString(
"is"));
1039 emitLHS, [&]() { emitExpression(op.getSrc()); },
PPExtString(
"<="));
1042 emitLocationAndNewLine(op);
1045void Emitter::emitStatement(MatchingConnectOp op) {
1049 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
1050 ps <<
"invalidate" << PP::space;
1051 emitExpression(op.getDest());
1053 ps <<
"connect" << PP::space;
1054 emitExpression(op.getDest());
1055 ps <<
"," << PP::space;
1056 emitExpression(op.getSrc());
1060 auto emitLHS = [&]() { emitExpression(op.getDest()); };
1061 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
1063 emitLHS, [&]() { ps <<
"invalid"; },
PPExtString(
"is"));
1066 emitLHS, [&]() { emitExpression(op.getSrc()); },
PPExtString(
"<="));
1069 emitLocationAndNewLine(op);
1072void Emitter::emitStatement(PropAssignOp op) {
1075 ps <<
"propassign" << PP::space;
1076 interleaveComma(op.getOperands());
1078 emitLocationAndNewLine(op);
1081void Emitter::emitStatement(InstanceOp op) {
1083 auto legalName = legalize(op.getNameAttr());
1085 <<
PPExtString(legalize(op.getModuleNameAttr().getAttr()));
1086 emitLocationAndNewLine(op);
1090 SmallString<16> portName(legalName);
1091 portName.push_back(
'.');
1092 unsigned baseLen = portName.size();
1093 for (
unsigned i = 0, e = op.getNumResults(); i < e; ++i) {
1094 portName.append(legalize(op.getPortName(i)));
1095 addValueName(op.getResult(i), portName);
1096 portName.resize(baseLen);
1100void Emitter::emitStatement(InstanceChoiceOp op) {
1102 auto legalName = legalize(op.getNameAttr());
1103 ps <<
"instchoice " <<
PPExtString(legalName) <<
" of "
1104 <<
PPExtString(legalize(op.getDefaultTargetAttr().getAttr())) <<
", "
1105 <<
PPExtString(legalize(op.getOptionNameAttr())) <<
" :";
1108 for (
const auto &[optSym, targetSym] : op.getTargetChoices()) {
1110 ps <<
PPExtString(legalize(optSym.getLeafReference()));
1115 setPendingNewline();
1117 SmallString<16> portName(legalName);
1118 portName.push_back(
'.');
1119 unsigned baseLen = portName.size();
1120 for (
unsigned i = 0, e = op.getNumResults(); i < e; ++i) {
1121 portName.append(legalize(op.getPortName(i)));
1122 addValueName(op.getResult(i), portName);
1123 portName.resize(baseLen);
1127void Emitter::emitStatement(AttachOp op) {
1128 emitStatementFunctionOp(
PPExtString(
"attach"), op);
1131void Emitter::emitStatement(MemOp op) {
1132 auto legalName = legalize(op.getNameAttr());
1133 SmallString<16> portName(legalName);
1134 portName.push_back(
'.');
1135 auto portNameBaseLen = portName.size();
1136 for (
auto result :
llvm::zip(op.getResults(), op.getPortNames())) {
1137 portName.resize(portNameBaseLen);
1138 portName.append(legalize(cast<StringAttr>(std::get<1>(result))));
1139 addValueName(std::get<0>(result), portName);
1144 emitLocationAndNewLine(op);
1147 ps <<
"data-type => ";
1148 emitType(op.getDataType());
1153 ps <<
"read-latency => ";
1156 ps <<
"write-latency => ";
1160 SmallString<16> reader, writer, readwriter;
1161 for (std::pair<StringAttr, MemOp::PortKind> port : op.getPorts()) {
1162 auto add = [&](SmallString<16> &to, StringAttr name) {
1165 to.append(name.getValue());
1167 switch (port.second) {
1168 case MemOp::PortKind::Read:
1169 add(reader, legalize(port.first));
1171 case MemOp::PortKind::Write:
1172 add(writer, legalize(port.first));
1174 case MemOp::PortKind::ReadWrite:
1175 add(readwriter, legalize(port.first));
1177 case MemOp::PortKind::Debug:
1178 emitOpError(op,
"has unsupported 'debug' port");
1182 if (!reader.empty())
1183 ps <<
"reader => " << reader << PP::newline;
1184 if (!writer.empty())
1185 ps <<
"writer => " << writer << PP::newline;
1186 if (!readwriter.empty())
1187 ps <<
"readwriter => " << readwriter << PP::newline;
1189 ps <<
"read-under-write => ";
1190 emitAttribute(op.getRuw());
1191 setPendingNewline();
1195void Emitter::emitStatement(SeqMemOp op) {
1198 ps <<
"smem " <<
PPExtString(legalize(op.getNameAttr()));
1199 emitTypeWithColon(op.getType());
1200 ps <<
"," << PP::space;
1201 emitAttribute(op.getRuw());
1203 emitLocationAndNewLine(op);
1206void Emitter::emitStatement(CombMemOp op) {
1209 ps <<
"cmem " <<
PPExtString(legalize(op.getNameAttr()));
1210 emitTypeWithColon(op.getType());
1212 emitLocationAndNewLine(op);
1215void Emitter::emitStatement(MemoryPortOp op) {
1217 addValueName(op.getData(), legalize(op.getNameAttr()));
1220void Emitter::emitStatement(MemoryDebugPortOp op) {
1222 addValueName(op.getData(), legalize(op.getNameAttr()));
1225void Emitter::emitStatement(MemoryPortAccessOp op) {
1229 auto port = cast<MemoryPortOp>(op.getPort().getDefiningOp());
1230 emitAttribute(port.getDirection());
1232 ps <<
" mport " <<
PPExtString(legalize(port.getNameAttr())) <<
" = ";
1235 auto *mem = port.getMemory().getDefiningOp();
1236 if (
auto seqMem = dyn_cast<SeqMemOp>(mem))
1237 ps << legalize(seqMem.getNameAttr());
1239 ps << legalize(cast<CombMemOp>(mem).getNameAttr());
1243 emitExpression(op.getIndex());
1247 emitExpression(op.getClock());
1249 emitLocationAndNewLine(op);
1252void Emitter::emitStatement(RefDefineOp op) {
1254 emitAssignLike([&]() { emitExpression(op.getDest()); },
1255 [&]() { emitExpression(op.getSrc()); },
PPExtString(
"="),
1257 emitLocationAndNewLine(op);
1260void Emitter::emitStatement(RefForceOp op) {
1261 emitStatementFunctionOp(
PPExtString(
"force"), op);
1264void Emitter::emitStatement(RefForceInitialOp op) {
1266 auto constantPredicate =
1267 dyn_cast_or_null<ConstantOp>(op.getPredicate().getDefiningOp());
1268 bool hasEnable = !constantPredicate || constantPredicate.getValue() == 0;
1271 emitExpression(op.getPredicate());
1272 ps <<
":" << PP::bbox2 << PP::neverbreak << PP::newline;
1274 ps <<
"force_initial(";
1276 interleaveComma({op.getDest(), op.getSrc()});
1281 emitLocationAndNewLine(op);
1284void Emitter::emitStatement(RefReleaseOp op) {
1285 emitStatementFunctionOp(
PPExtString(
"release"), op);
1288void Emitter::emitStatement(RefReleaseInitialOp op) {
1290 auto constantPredicate =
1291 dyn_cast_or_null<ConstantOp>(op.getPredicate().getDefiningOp());
1292 bool hasEnable = !constantPredicate || constantPredicate.getValue() == 0;
1295 emitExpression(op.getPredicate());
1296 ps <<
":" << PP::bbox2 << PP::neverbreak << PP::newline;
1298 ps <<
"release_initial(";
1299 emitExpression(op.getDest());
1303 emitLocationAndNewLine(op);
1306void Emitter::emitStatement(LayerBlockOp op) {
1308 ps <<
"layerblock " << op.getLayerName().getLeafReference() <<
" :";
1309 emitLocationAndNewLine(op);
1310 auto *body = op.getBody();
1311 ps.
scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(*body); });
1314void Emitter::emitStatement(InvalidValueOp op) {
1317 if (llvm::all_of(op->getUses(), [&](OpOperand &use) {
1318 return use.getOperandNumber() == 1 &&
1319 isa<ConnectOp, MatchingConnectOp>(use.getOwner());
1325 auto name = circuitNamespace.newName(
"_invalid");
1326 addValueName(op, name);
1328 emitType(op.getType());
1329 emitLocationAndNewLine(op);
1335 emitLocationAndNewLine(op);
1338void Emitter::emitStatement(GenericIntrinsicOp op) {
1341 emitGenericIntrinsic(op);
1344 auto name = circuitNamespace.newName(
"_gen_int");
1345 addValueName(op.getResult(), name);
1346 emitAssignLike([&]() { ps <<
"node " <<
PPExtString(name); },
1347 [&]() { emitGenericIntrinsic(op); });
1349 emitLocationAndNewLine(op);
1352void Emitter::emitExpression(Value value) {
1355 if (
auto name = lookupEmittedName(value)) {
1361 auto op = value.getDefiningOp();
1362 assert(op &&
"value must either be a block arg or the result of an op");
1363 TypeSwitch<Operation *>(op)
1366 ConstantOp, SpecialConstantOp, SubfieldOp, SubindexOp, SubaccessOp,
1367 OpenSubfieldOp, OpenSubindexOp,
1369 AddPrimOp, SubPrimOp, MulPrimOp, DivPrimOp, RemPrimOp, AndPrimOp,
1370 OrPrimOp, XorPrimOp, LEQPrimOp, LTPrimOp, GEQPrimOp, GTPrimOp,
1371 EQPrimOp, NEQPrimOp, CatPrimOp, DShlPrimOp, DShlwPrimOp, DShrPrimOp,
1373 AsSIntPrimOp, AsUIntPrimOp, AsAsyncResetPrimOp, AsClockPrimOp,
1374 CvtPrimOp, NegPrimOp, NotPrimOp, AndRPrimOp, OrRPrimOp, XorRPrimOp,
1376 BitsPrimOp, HeadPrimOp, TailPrimOp, PadPrimOp, MuxPrimOp, ShlPrimOp,
1377 ShrPrimOp, UninferredResetCastOp, ConstCastOp, StringConstantOp,
1378 FIntegerConstantOp, BoolConstantOp, DoubleConstantOp, ListCreateOp,
1379 UnresolvedPathOp, GenericIntrinsicOp,
1381 RefSendOp, RefResolveOp, RefSubOp, RWProbeOp, RefCastOp,
1383 TimeOp>([&](
auto op) {
1384 ps.
scopedBox(PP::ibox0, [&]() { emitExpression(op); });
1386 .Default([&](
auto op) {
1387 emitOpError(op,
"not supported as expression");
1388 ps <<
"<unsupported-expr-" <<
PPExtString(op->getName().stripDialect())
1393void Emitter::emitExpression(ConstantOp op) {
1395 emitType(op.getType(),
false);
1402void Emitter::emitExpression(SpecialConstantOp op) {
1403 auto emitInner = [&]() {
1410 .
Case<ClockType>([&](
auto type) {
1415 .Case<ResetType>([&](
auto type) { emitInner(); })
1416 .Case<AsyncResetType>([&](
auto type) {
1417 ps <<
"asAsyncReset(";
1424void Emitter::emitExpression(SubfieldOp op) {
1425 BundleType type = op.getInput().getType();
1426 emitExpression(op.getInput());
1427 ps <<
"." << legalize(type.getElementNameAttr(op.getFieldIndex()));
1431void Emitter::emitExpression(SubindexOp op) {
1432 emitExpression(op.getInput());
1439void Emitter::emitExpression(SubaccessOp op) {
1440 emitExpression(op.getInput());
1442 emitExpression(op.getIndex());
1446void Emitter::emitExpression(OpenSubfieldOp op) {
1447 auto type = op.getInput().getType();
1448 emitExpression(op.getInput());
1449 ps <<
"." << legalize(type.getElementNameAttr(op.getFieldIndex()));
1452void Emitter::emitExpression(OpenSubindexOp op) {
1453 emitExpression(op.getInput());
1459void Emitter::emitExpression(RefSendOp op) {
1461 emitExpression(op.getBase());
1465void Emitter::emitExpression(RefResolveOp op) {
1467 emitExpression(op.getRef());
1471void Emitter::emitExpression(RefSubOp op) {
1472 emitExpression(op.getInput());
1474 .
Case<FVectorType>([&](
auto type) {
1480 [&](
auto type) { ps <<
"." << type.getElementName(op.getIndex()); });
1483void Emitter::emitExpression(RWProbeOp op) {
1487 auto target = symInfos->get().irn.lookup(op.getTarget());
1489 if (target.isPort()) {
1490 auto mod = cast<FModuleOp>(target.getOp());
1491 auto port = target.getPort();
1492 base = mod.getArgument(port);
1494 base = cast<hw::InnerSymbolOpInterface>(target.getOp()).getTargetResult();
1497 emitExpression(base);
1500 auto fieldID = target.getField();
1501 auto type = base.getType();
1504 .
Case<FVectorType, OpenVectorType>([&](
auto vecTy) {
1505 auto index = vecTy.getIndexForFieldID(fieldID);
1509 auto [subtype, subfieldID] = vecTy.getSubTypeByFieldID(fieldID);
1511 fieldID = subfieldID;
1513 .Case<BundleType, OpenBundleType>([&](
auto bundleTy) {
1514 auto index = bundleTy.getIndexForFieldID(fieldID);
1515 ps <<
"." << bundleTy.getElementName(index);
1516 auto [subtype, subfieldID] = bundleTy.getSubTypeByFieldID(fieldID);
1518 fieldID = subfieldID;
1524void Emitter::emitExpression(RefCastOp op) { emitExpression(op.getInput()); }
1526void Emitter::emitExpression(UninferredResetCastOp op) {
1527 emitExpression(op.getInput());
1530void Emitter::emitExpression(FIntegerConstantOp op) {
1536void Emitter::emitExpression(BoolConstantOp op) {
1537 ps <<
"Bool(" << (op.getValue() ?
"true" :
"false") <<
")";
1540void Emitter::emitExpression(DoubleConstantOp op) {
1546 SmallString<16> str;
1547 op.getValueAttr().getValue().toString(str);
1552void Emitter::emitExpression(StringConstantOp op) {
1558void Emitter::emitExpression(ListCreateOp op) {
1559 return emitLiteralExpression(op.getType(), op.getElements());
1562void Emitter::emitExpression(UnresolvedPathOp op) {
1568void Emitter::emitExpression(GenericIntrinsicOp op) {
1569 emitGenericIntrinsic(op);
1572void Emitter::emitExpression(ConstCastOp op) { emitExpression(op.getInput()); }
1574void Emitter::emitPrimExpr(StringRef mnemonic, Operation *op,
1575 ArrayRef<uint32_t> attrs) {
1576 ps << mnemonic <<
"(" << PP::ibox0;
1577 interleaveComma(op->getOperands());
1578 if (!op->getOperands().empty() && !attrs.empty())
1579 ps <<
"," << PP::space;
1580 interleaveComma(attrs, [&](
auto attr) { ps.
addAsString(attr); });
1581 ps <<
")" << PP::end;
1584void Emitter::emitAttribute(MemDirAttr attr) {
1586 case MemDirAttr::Infer:
1589 case MemDirAttr::Read:
1592 case MemDirAttr::Write:
1595 case MemDirAttr::ReadWrite:
1601void Emitter::emitAttribute(RUWAttr attr) {
1603 case RUWAttr::Undefined:
1616void Emitter::emitType(Type type,
bool includeConst) {
1617 if (includeConst &&
isConst(type))
1619 auto emitWidth = [&](std::optional<int32_t> width) {
1628 .
Case<ClockType>([&](
auto) { ps <<
"Clock"; })
1629 .Case<ResetType>([&](
auto) { ps <<
"Reset"; })
1630 .Case<AsyncResetType>([&](
auto) { ps <<
"AsyncReset"; })
1631 .Case<UIntType>([&](
auto type) {
1633 emitWidth(type.getWidth());
1635 .Case<SIntType>([&](
auto type) {
1637 emitWidth(type.getWidth());
1639 .Case<AnalogType>([&](
auto type) {
1641 emitWidth(type.getWidth());
1643 .Case<OpenBundleType, BundleType>([&](
auto type) {
1645 if (!type.getElements().empty())
1647 bool anyEmitted =
false;
1649 for (
auto &element : type.getElements()) {
1651 ps <<
"," << PP::space;
1655 ps << legalize(element.name);
1656 emitTypeWithColon(element.type);
1665 .Case<OpenVectorType, FVectorType, CMemoryType>([&](
auto type) {
1666 emitType(type.getElementType());
1671 .Case<RefType>([&](RefType type) {
1672 if (type.getForceable())
1675 ps.
cbox(2, IndentStyle::Block);
1677 emitType(type.getType());
1678 if (
auto layer = type.getLayer()) {
1681 emitSymbol(type.getLayer());
1686 .Case<AnyRefType>([&](AnyRefType type) { ps <<
"AnyRef"; })
1687 .Case<StringType>([&](StringType type) { ps <<
"String"; })
1688 .Case<FIntegerType>([&](FIntegerType type) { ps <<
"Integer"; })
1689 .Case<BoolType>([&](BoolType type) { ps <<
"Bool"; })
1690 .Case<DoubleType>([&](DoubleType type) { ps <<
"Double"; })
1691 .Case<PathType>([&](PathType type) { ps <<
"Path"; })
1692 .Case<ListType>([&](ListType type) {
1694 emitType(type.getElementType());
1697 .Default([&](
auto type) {
1698 llvm_unreachable(
"all types should be implemented");
1704void Emitter::emitLocation(Location loc) {
1706 ps << PP::neverbreak;
1708 dyn_cast_or_null<FileLineColLoc, LocationAttr>(LocationAttr(loc))) {
1709 ps <<
" @[" << fileLoc.getFilename().getValue();
1710 if (
auto line = fileLoc.getLine()) {
1713 if (
auto col = fileLoc.getColumn()) {
1730 std::optional<size_t> targetLineLength,
1732 Emitter emitter(os, version,
1733 targetLineLength.value_or(defaultTargetLineLength));
1734 for (
auto &op : *
module.getBody()) {
1735 if (auto circuitOp = dyn_cast<CircuitOp>(op))
1736 emitter.emitCircuit(circuitOp);
1738 return emitter.finalize();
1743 "target-line-length",
1744 llvm::cl::desc(
"Target line length for emitted .fir"),
1745 llvm::cl::value_desc(
"number of chars"),
1746 llvm::cl::init(defaultTargetLineLength));
1747 static mlir::TranslateFromMLIRRegistration toFIR(
1748 "export-firrtl",
"emit FIRRTL dialect operations to .fir output",
1749 [](ModuleOp module, llvm::raw_ostream &os) {
1752 [](mlir::DialectRegistry ®istry) {
1753 registry.insert<chirrtl::CHIRRTLDialect>();
1754 registry.insert<firrtl::FIRRTLDialect>();
assert(baseType &&"element must be base type")
#define HANDLE(OPTYPE, OPKIND)
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)
static Block * getBodyBlock(FModuleLike mod)
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.
This class represents a collection of InnerSymbolTable's.
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 & addAsString(T &&t)
General-purpose "format this" helper, for types not supported by operator<< yet.
TokenStream & writeQuotedEscaped(StringRef str, bool useHexEscapes=false, StringRef left="\"", StringRef right="\"")
PrettyPrinter::Listener that saves strings while live.
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.
This class represents the namespace in which InnerRef's can be resolved.
String wrapper to indicate string has external storage.