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);
68 void emitKnownLayers(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);
141 void emitExpression(CatPrimOp op);
143 void emitPrimExpr(StringRef mnemonic, Operation *op,
144 ArrayRef<uint32_t> attrs = {});
146 void emitExpression(BitsPrimOp op) {
147 emitPrimExpr(
"bits", op, {op.getHi(), op.getLo()});
149 void emitExpression(HeadPrimOp op) {
150 emitPrimExpr(
"head", op, op.getAmount());
152 void emitExpression(TailPrimOp op) {
153 emitPrimExpr(
"tail", op, op.getAmount());
155 void emitExpression(PadPrimOp op) { emitPrimExpr(
"pad", op, op.getAmount()); }
156 void emitExpression(ShlPrimOp op) { emitPrimExpr(
"shl", op, op.getAmount()); }
157 void emitExpression(ShrPrimOp op) { emitPrimExpr(
"shr", op, op.getAmount()); }
159 void emitExpression(TimeOp op){};
162#define HANDLE(OPTYPE, MNEMONIC) \
163 void emitExpression(OPTYPE op) { emitPrimExpr(MNEMONIC, op); }
162#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::emitKnownLayers(ArrayRef<Attribute> layers) {
443 for (
auto layer : layers) {
445 ps.
cbox(2, IndentStyle::Block);
446 ps <<
"knownlayer" << PP::space;
447 emitSymbol(cast<SymbolRefAttr>(layer));
452void Emitter::emitParamAssign(ParamDeclAttr param, Operation *op,
453 std::optional<PPExtString> wordBeforeLHS) {
455 ps << *wordBeforeLHS << PP::nbsp;
457 ps <<
PPExtString(param.getName().strref()) << PP::nbsp <<
"=" << PP::nbsp;
458 emitParamValue(param.getValue(), op);
461void Emitter::emitParamValue(Attribute value, Operation *op) {
462 TypeSwitch<Attribute>(value)
463 .Case<IntegerAttr>([&](
auto attr) { ps.
addAsString(attr.getValue()); })
464 .Case<FloatAttr>([&](
auto attr) {
466 attr.getValue().toString(str);
471 .Case<ArrayAttr>([&](
auto attr) {
474 interleaveComma(attr.getValue(),
475 [&](
auto element) { emitParamValue(element, op); });
479 .Case<DictionaryAttr>([&](
auto attr) {
482 interleaveComma(attr.getValue(), [&](
auto field) {
483 ps << PPExtString(field.getName()) << PP::nbsp <<
"=" << PP::nbsp;
484 emitParamValue(field.getValue(), op);
489 .Default([&](
auto attr) {
490 emitOpError(op,
"with unsupported parameter attribute: ") << attr;
491 ps <<
"<unsupported-attr ";
497void Emitter::emitGenericIntrinsic(GenericIntrinsicOp op) {
501 ps << op.getIntrinsic();
503 auto params = op.getParameters();
504 if (!params.empty()) {
506 ps.scopedBox(PP::ibox0, [&]() {
508 params.getAsRange<ParamDeclAttr>(),
509 [&](ParamDeclAttr param) { emitParamAssign(param, op); });
514 if (op.getNumResults() != 0)
515 emitTypeWithColon(op.getResult().getType());
517 if (op.getNumOperands() != 0) {
518 ps <<
"," << PP::space;
519 ps.
scopedBox(PP::ibox0, [&]() { interleaveComma(op->getOperands()); });
526void Emitter::emitModule(FModuleOp op) {
528 ps.
cbox(4, IndentStyle::Block);
530 ps <<
"public" << PP::nbsp;
531 ps <<
"module " <<
PPExtString(legalize(op.getNameAttr()));
532 emitEnabledLayers(op.getLayers());
533 ps << PP::nbsp <<
":" << PP::end;
540 auto ports = op.getPorts();
541 emitModulePorts(ports, op.getArguments());
542 if (!ports.empty() && !op.getBodyBlock()->empty())
546 emitStatementsInBlock(*op.getBodyBlock());
549 valueNamesStorage.clear();
553void Emitter::emitModule(FExtModuleOp op) {
555 ps.
cbox(4, IndentStyle::Block);
556 ps <<
"extmodule " <<
PPExtString(legalize(op.getNameAttr()));
557 emitKnownLayers(op.getKnownLayers());
558 emitEnabledLayers(op.getLayers());
559 ps << PP::nbsp <<
":" << PP::end;
566 auto ports = op.getPorts();
567 emitModulePorts(ports);
570 if (op.getDefname() && !op.getDefname()->empty()) {
572 ps <<
"defname = " <<
PPExtString(*op.getDefname());
577 emitModuleParameters(op, op.getParameters());
582void Emitter::emitModule(FIntModuleOp op) {
584 ps.
cbox(4, IndentStyle::Block);
585 ps <<
"intmodule " <<
PPExtString(legalize(op.getNameAttr()));
586 emitEnabledLayers(op.getLayers());
587 ps << PP::nbsp <<
":" << PP::end;
594 auto ports = op.getPorts();
595 emitModulePorts(ports);
598 ps <<
"intrinsic = " <<
PPExtString(op.getIntrinsic());
602 emitModuleParameters(op, op.getParameters());
609void Emitter::emitModulePorts(ArrayRef<PortInfo> ports,
610 Block::BlockArgListType arguments) {
611 for (
unsigned i = 0, e = ports.size(); i < e; ++i) {
613 const auto &port = ports[i];
614 ps << (port.direction == Direction::In ?
"input " :
"output ");
615 auto legalName = legalize(port.name);
616 if (!arguments.empty())
617 addValueName(arguments[i], legalName);
620 emitLocation(ports[i].loc);
625void Emitter::emitModuleParameters(Operation *op, ArrayAttr parameters) {
626 for (
auto param : parameters.getAsRange<ParamDeclAttr>()) {
628 emitParamAssign(param, op,
PPExtString(
"parameter"));
634void Emitter::emitDeclaration(LayerOp op) {
636 ps <<
"layer " <<
PPExtString(op.getSymName()) <<
", "
637 <<
PPExtString(stringifyLayerConvention(op.getConvention()));
639 if (
auto outputFile = op->getAttrOfType<hw::OutputFileAttr>(
"output_file")) {
645 emitLocationAndNewLine(op);
647 for (
auto &bodyOp : op.getBody().getOps()) {
648 TypeSwitch<Operation *>(&bodyOp)
649 .Case<LayerOp>([&](
auto op) { emitDeclaration(op); })
650 .Default([&](
auto op) {
652 "not supported for emission inside layer definition");
659void Emitter::emitDeclaration(OptionOp op) {
661 ps <<
"option " <<
PPExtString(legalize(op.getSymNameAttr())) <<
" :";
664 for (
auto caseOp : op.getBody().getOps<OptionCaseOp>()) {
666 ps <<
PPExtString(legalize(caseOp.getSymNameAttr()));
667 emitLocation(caseOp);
670 ps << PP::newline << PP::newline;
674void Emitter::emitDeclaration(FormalOp op) {
675 emitFormalLike(op,
"formal", op.getSymNameAttr(),
676 op.getModuleNameAttr().getAttr(), op.getParameters());
680void Emitter::emitDeclaration(SimulationOp op) {
681 emitFormalLike(op,
"simulation", op.getSymNameAttr(),
682 op.getModuleNameAttr().getAttr(), op.getParameters());
686void Emitter::emitFormalLike(Operation *op, StringRef keyword,
687 StringAttr symName, StringAttr moduleName,
688 DictionaryAttr params) {
690 ps.
cbox(4, IndentStyle::Block);
691 ps << keyword <<
" " <<
PPExtString(legalize(symName));
693 ps << PP::nbsp <<
":" << PP::end;
698 for (
auto param : params) {
700 ps <<
PPExtString(param.getName()) << PP::nbsp <<
"=" << PP::nbsp;
701 emitParamValue(param.getValue(), op);
715 return (
isExpression(op) && !isa<InvalidValueOp>(op)) ||
716 (isa<GenericIntrinsicOp>(op) && op->hasOneUse() &&
720void Emitter::emitStatementsInBlock(Block &block) {
721 for (
auto &bodyOp : block) {
722 if (encounteredError)
726 TypeSwitch<Operation *>(&bodyOp)
727 .Case<WhenOp, WireOp, RegOp, RegResetOp, NodeOp, StopOp, SkipOp,
728 PrintFOp, FPrintFOp, FFlushOp, AssertOp, AssumeOp, CoverOp,
729 ConnectOp, MatchingConnectOp, PropAssignOp, InstanceOp,
730 InstanceChoiceOp, AttachOp, MemOp, InvalidValueOp, SeqMemOp,
731 CombMemOp, MemoryPortOp, MemoryDebugPortOp, MemoryPortAccessOp,
732 RefDefineOp, RefForceOp, RefForceInitialOp, RefReleaseOp,
733 RefReleaseInitialOp, LayerBlockOp, GenericIntrinsicOp>(
734 [&](
auto op) { emitStatement(op); })
735 .Default([&](
auto op) {
737 ps <<
"// operation " <<
PPExtString(op->getName().getStringRef());
739 emitOpError(op,
"not supported as statement");
744void Emitter::emitStatement(WhenOp op) {
747 emitExpression(op.getCondition());
749 emitLocationAndNewLine(op);
750 ps.
scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(op.getThenBlock()); });
752 if (!op.hasElseRegion())
759 auto &elseBlock = op.getElseBlock();
760 if (!elseBlock.empty() && &elseBlock.front() == &elseBlock.back()) {
761 if (
auto whenOp = dyn_cast<WhenOp>(&elseBlock.front())) {
762 emitStatement(whenOp);
769 ps.
scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(elseBlock); });
772void Emitter::emitStatement(WireOp op) {
773 auto legalName = legalize(op.getNameAttr());
774 addForceable(op, legalName);
778 emitTypeWithColon(op.getResult().getType());
780 emitLocationAndNewLine(op);
783void Emitter::emitStatement(RegOp op) {
784 auto legalName = legalize(op.getNameAttr());
785 addForceable(op, legalName);
789 emitTypeWithColon(op.getResult().getType());
790 ps <<
"," << PP::space;
791 emitExpression(op.getClockVal());
793 emitLocationAndNewLine(op);
796void Emitter::emitStatement(RegResetOp op) {
797 auto legalName = legalize(op.getNameAttr());
798 addForceable(op, legalName);
802 ps <<
"regreset " << legalName;
803 emitTypeWithColon(op.getResult().getType());
804 ps <<
"," << PP::space;
805 emitExpression(op.getClockVal());
806 ps <<
"," << PP::space;
807 emitExpression(op.getResetSignal());
808 ps <<
"," << PP::space;
809 emitExpression(op.getResetValue());
813 ps <<
"reg " << legalName;
814 emitTypeWithColon(op.getResult().getType());
815 ps <<
"," << PP::space;
816 emitExpression(op.getClockVal());
817 ps << PP::space <<
"with :";
819 ps << PP::neverbreak;
822 ps <<
"reset => (" << PP::ibox0;
823 emitExpression(op.getResetSignal());
824 ps <<
"," << PP::space;
825 emitExpression(op.getResetValue());
826 ps <<
")" << PP::end;
829 emitLocationAndNewLine(op);
832void Emitter::emitStatement(NodeOp op) {
833 auto legalName = legalize(op.getNameAttr());
834 addForceable(op, legalName);
836 emitAssignLike([&]() { ps <<
"node " <<
PPExtString(legalName); },
837 [&]() { emitExpression(op.getInput()); });
838 emitLocationAndNewLine(op);
841void Emitter::emitStatement(StopOp op) {
844 ps <<
"stop(" << PP::ibox0;
845 emitExpression(op.getClock());
846 ps <<
"," << PP::space;
847 emitExpression(op.getCond());
848 ps <<
"," << PP::space;
850 ps <<
")" << PP::end;
851 if (!op.getName().empty()) {
852 ps << PP::space <<
": " <<
PPExtString(legalize(op.getNameAttr()));
855 emitLocationAndNewLine(op);
858void Emitter::emitStatement(SkipOp op) {
861 emitLocationAndNewLine(op);
864void Emitter::emitFormatString(Operation *op, StringRef origFormatString,
865 OperandRange substitutionOperands,
866 llvm::SmallVectorImpl<Value> &substitutions) {
875 SmallString<64> formatString;
876 for (
size_t i = 0, e = origFormatString.size(), opIdx = 0; i != e; ++i) {
877 auto c = origFormatString[i];
880 formatString.push_back(c);
883 SmallString<6> width;
884 c = origFormatString[++i];
887 c = origFormatString[++i];
896 formatString.append(width);
899 substitutions.push_back(substitutionOperands[opIdx++]);
902 formatString.push_back(c);
907 if (origFormatString.slice(i, i + 4) ==
"{{}}") {
908 formatString.append(
"{{");
909 TypeSwitch<Operation *>(substitutionOperands[opIdx++].getDefiningOp())
911 [&](
auto time) { formatString.append(
"SimulationTime"); })
912 .Case<HierarchicalModuleNameOp>([&](
auto time) {
913 formatString.append(
"HierarchicalModuleName");
916 emitError(op,
"unsupported fstring substitution type");
918 formatString.append(
"}}");
923 formatString.push_back(c);
929void Emitter::emitStatement(PrintFOp op) {
932 ps <<
"printf(" << PP::ibox0;
933 emitExpression(op.getClock());
934 ps <<
"," << PP::space;
935 emitExpression(op.getCond());
936 ps <<
"," << PP::space;
938 SmallVector<Value, 4> substitutions;
939 emitFormatString(op, op.getFormatString(), op.getSubstitutions(),
941 for (
auto operand : substitutions) {
942 ps <<
"," << PP::space;
943 emitExpression(operand);
945 ps <<
")" << PP::end;
946 if (!op.getName().empty()) {
947 ps << PP::space <<
": " <<
PPExtString(legalize(op.getNameAttr()));
950 emitLocationAndNewLine(op);
953void Emitter::emitStatement(FPrintFOp op) {
956 ps <<
"fprintf(" << PP::ibox0;
957 emitExpression(op.getClock());
958 ps <<
"," << PP::space;
959 emitExpression(op.getCond());
960 ps <<
"," << PP::space;
962 SmallVector<Value, 4> outputFileSubstitutions;
963 emitFormatString(op, op.getOutputFile(), op.getOutputFileSubstitutions(),
964 outputFileSubstitutions);
965 if (!outputFileSubstitutions.empty()) {
966 ps <<
"," << PP::space;
967 interleaveComma(outputFileSubstitutions);
970 ps <<
"," << PP::space;
971 SmallVector<Value, 4> substitutions;
972 emitFormatString(op, op.getFormatString(), op.getSubstitutions(),
974 if (!substitutions.empty()) {
975 ps <<
"," << PP::space;
976 interleaveComma(substitutions);
979 ps <<
")" << PP::end;
980 if (!op.getName().empty()) {
981 ps << PP::space <<
": " <<
PPExtString(legalize(op.getNameAttr()));
984 emitLocationAndNewLine(op);
987void Emitter::emitStatement(FFlushOp op) {
990 ps <<
"fflush(" << PP::ibox0;
991 emitExpression(op.getClock());
992 ps <<
"," << PP::space;
993 emitExpression(op.getCond());
994 if (op.getOutputFileAttr()) {
995 ps <<
"," << PP::space;
996 SmallVector<Value, 4> substitutions;
997 emitFormatString(op, op.getOutputFileAttr(),
998 op.getOutputFileSubstitutions(), substitutions);
999 if (!substitutions.empty()) {
1000 ps <<
"," << PP::space;
1001 interleaveComma(substitutions);
1004 ps <<
")" << PP::end;
1006 emitLocationAndNewLine(op);
1010void Emitter::emitVerifStatement(T op, StringRef mnemonic) {
1013 ps << mnemonic <<
"(" << PP::ibox0;
1014 emitExpression(op.getClock());
1015 ps <<
"," << PP::space;
1016 emitExpression(op.getPredicate());
1017 ps <<
"," << PP::space;
1018 emitExpression(op.getEnable());
1019 ps <<
"," << PP::space;
1021 ps <<
")" << PP::end;
1022 if (!op.getName().empty()) {
1023 ps << PP::space <<
": " <<
PPExtString(legalize(op.getNameAttr()));
1026 emitLocationAndNewLine(op);
1029void Emitter::emitStatement(ConnectOp op) {
1033 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
1034 ps <<
"invalidate" << PP::space;
1035 emitExpression(op.getDest());
1037 ps <<
"connect" << PP::space;
1038 emitExpression(op.getDest());
1039 ps <<
"," << PP::space;
1040 emitExpression(op.getSrc());
1044 auto emitLHS = [&]() { emitExpression(op.getDest()); };
1045 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
1047 emitLHS, [&]() { ps <<
"invalid"; },
PPExtString(
"is"));
1050 emitLHS, [&]() { emitExpression(op.getSrc()); },
PPExtString(
"<="));
1053 emitLocationAndNewLine(op);
1056void Emitter::emitStatement(MatchingConnectOp op) {
1060 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
1061 ps <<
"invalidate" << PP::space;
1062 emitExpression(op.getDest());
1064 ps <<
"connect" << PP::space;
1065 emitExpression(op.getDest());
1066 ps <<
"," << PP::space;
1067 emitExpression(op.getSrc());
1071 auto emitLHS = [&]() { emitExpression(op.getDest()); };
1072 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
1074 emitLHS, [&]() { ps <<
"invalid"; },
PPExtString(
"is"));
1077 emitLHS, [&]() { emitExpression(op.getSrc()); },
PPExtString(
"<="));
1080 emitLocationAndNewLine(op);
1083void Emitter::emitStatement(PropAssignOp op) {
1086 ps <<
"propassign" << PP::space;
1087 interleaveComma(op.getOperands());
1089 emitLocationAndNewLine(op);
1092void Emitter::emitStatement(InstanceOp op) {
1094 auto legalName = legalize(op.getNameAttr());
1096 <<
PPExtString(legalize(op.getModuleNameAttr().getAttr()));
1097 emitLocationAndNewLine(op);
1101 SmallString<16> portName(legalName);
1102 portName.push_back(
'.');
1103 unsigned baseLen = portName.size();
1104 for (
unsigned i = 0, e = op.getNumResults(); i < e; ++i) {
1105 portName.append(legalize(op.getPortName(i)));
1106 addValueName(op.getResult(i), portName);
1107 portName.resize(baseLen);
1111void Emitter::emitStatement(InstanceChoiceOp op) {
1113 auto legalName = legalize(op.getNameAttr());
1114 ps <<
"instchoice " <<
PPExtString(legalName) <<
" of "
1115 <<
PPExtString(legalize(op.getDefaultTargetAttr().getAttr())) <<
", "
1116 <<
PPExtString(legalize(op.getOptionNameAttr())) <<
" :";
1119 for (
const auto &[optSym, targetSym] : op.getTargetChoices()) {
1121 ps <<
PPExtString(legalize(optSym.getLeafReference()));
1126 setPendingNewline();
1128 SmallString<16> portName(legalName);
1129 portName.push_back(
'.');
1130 unsigned baseLen = portName.size();
1131 for (
unsigned i = 0, e = op.getNumResults(); i < e; ++i) {
1132 portName.append(legalize(op.getPortName(i)));
1133 addValueName(op.getResult(i), portName);
1134 portName.resize(baseLen);
1138void Emitter::emitStatement(AttachOp op) {
1139 emitStatementFunctionOp(
PPExtString(
"attach"), op);
1142void Emitter::emitStatement(MemOp op) {
1143 auto legalName = legalize(op.getNameAttr());
1144 SmallString<16> portName(legalName);
1145 portName.push_back(
'.');
1146 auto portNameBaseLen = portName.size();
1147 for (
auto result :
llvm::zip(op.getResults(), op.getPortNames())) {
1148 portName.resize(portNameBaseLen);
1149 portName.append(legalize(cast<StringAttr>(std::get<1>(result))));
1150 addValueName(std::get<0>(result), portName);
1155 emitLocationAndNewLine(op);
1158 ps <<
"data-type => ";
1159 emitType(op.getDataType());
1164 ps <<
"read-latency => ";
1167 ps <<
"write-latency => ";
1171 SmallString<16> reader, writer, readwriter;
1172 for (std::pair<StringAttr, MemOp::PortKind> port : op.getPorts()) {
1173 auto add = [&](SmallString<16> &to, StringAttr name) {
1176 to.append(name.getValue());
1178 switch (port.second) {
1179 case MemOp::PortKind::Read:
1180 add(reader, legalize(port.first));
1182 case MemOp::PortKind::Write:
1183 add(writer, legalize(port.first));
1185 case MemOp::PortKind::ReadWrite:
1186 add(readwriter, legalize(port.first));
1188 case MemOp::PortKind::Debug:
1189 emitOpError(op,
"has unsupported 'debug' port");
1193 if (!reader.empty())
1194 ps <<
"reader => " << reader << PP::newline;
1195 if (!writer.empty())
1196 ps <<
"writer => " << writer << PP::newline;
1197 if (!readwriter.empty())
1198 ps <<
"readwriter => " << readwriter << PP::newline;
1200 ps <<
"read-under-write => ";
1201 emitAttribute(op.getRuw());
1202 setPendingNewline();
1206void Emitter::emitStatement(SeqMemOp op) {
1209 ps <<
"smem " <<
PPExtString(legalize(op.getNameAttr()));
1210 emitTypeWithColon(op.getType());
1211 ps <<
"," << PP::space;
1212 emitAttribute(op.getRuw());
1214 emitLocationAndNewLine(op);
1217void Emitter::emitStatement(CombMemOp op) {
1220 ps <<
"cmem " <<
PPExtString(legalize(op.getNameAttr()));
1221 emitTypeWithColon(op.getType());
1223 emitLocationAndNewLine(op);
1226void Emitter::emitStatement(MemoryPortOp op) {
1228 addValueName(op.getData(), legalize(op.getNameAttr()));
1231void Emitter::emitStatement(MemoryDebugPortOp op) {
1233 addValueName(op.getData(), legalize(op.getNameAttr()));
1236void Emitter::emitStatement(MemoryPortAccessOp op) {
1240 auto port = cast<MemoryPortOp>(op.getPort().getDefiningOp());
1241 emitAttribute(port.getDirection());
1243 ps <<
" mport " <<
PPExtString(legalize(port.getNameAttr())) <<
" = ";
1246 auto *mem = port.getMemory().getDefiningOp();
1247 if (
auto seqMem = dyn_cast<SeqMemOp>(mem))
1248 ps << legalize(seqMem.getNameAttr());
1250 ps << legalize(cast<CombMemOp>(mem).getNameAttr());
1254 emitExpression(op.getIndex());
1258 emitExpression(op.getClock());
1260 emitLocationAndNewLine(op);
1263void Emitter::emitStatement(RefDefineOp op) {
1265 emitAssignLike([&]() { emitExpression(op.getDest()); },
1266 [&]() { emitExpression(op.getSrc()); },
PPExtString(
"="),
1268 emitLocationAndNewLine(op);
1271void Emitter::emitStatement(RefForceOp op) {
1272 emitStatementFunctionOp(
PPExtString(
"force"), op);
1275void Emitter::emitStatement(RefForceInitialOp op) {
1277 auto constantPredicate =
1278 dyn_cast_or_null<ConstantOp>(op.getPredicate().getDefiningOp());
1279 bool hasEnable = !constantPredicate || constantPredicate.getValue() == 0;
1282 emitExpression(op.getPredicate());
1283 ps <<
":" << PP::bbox2 << PP::neverbreak << PP::newline;
1285 ps <<
"force_initial(";
1287 interleaveComma({op.getDest(), op.getSrc()});
1292 emitLocationAndNewLine(op);
1295void Emitter::emitStatement(RefReleaseOp op) {
1296 emitStatementFunctionOp(
PPExtString(
"release"), op);
1299void Emitter::emitStatement(RefReleaseInitialOp op) {
1301 auto constantPredicate =
1302 dyn_cast_or_null<ConstantOp>(op.getPredicate().getDefiningOp());
1303 bool hasEnable = !constantPredicate || constantPredicate.getValue() == 0;
1306 emitExpression(op.getPredicate());
1307 ps <<
":" << PP::bbox2 << PP::neverbreak << PP::newline;
1309 ps <<
"release_initial(";
1310 emitExpression(op.getDest());
1314 emitLocationAndNewLine(op);
1317void Emitter::emitStatement(LayerBlockOp op) {
1319 ps <<
"layerblock " << op.getLayerName().getLeafReference() <<
" :";
1320 emitLocationAndNewLine(op);
1321 auto *body = op.getBody();
1322 ps.
scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(*body); });
1325void Emitter::emitStatement(InvalidValueOp op) {
1328 if (llvm::all_of(op->getUses(), [&](OpOperand &use) {
1329 return use.getOperandNumber() == 1 &&
1330 isa<ConnectOp, MatchingConnectOp>(use.getOwner());
1336 auto name = circuitNamespace.newName(
"_invalid");
1337 addValueName(op, name);
1339 emitType(op.getType());
1340 emitLocationAndNewLine(op);
1346 emitLocationAndNewLine(op);
1349void Emitter::emitStatement(GenericIntrinsicOp op) {
1352 emitGenericIntrinsic(op);
1355 auto name = circuitNamespace.newName(
"_gen_int");
1356 addValueName(op.getResult(), name);
1357 emitAssignLike([&]() { ps <<
"node " <<
PPExtString(name); },
1358 [&]() { emitGenericIntrinsic(op); });
1360 emitLocationAndNewLine(op);
1363void Emitter::emitExpression(Value value) {
1366 if (
auto name = lookupEmittedName(value)) {
1372 auto op = value.getDefiningOp();
1373 assert(op &&
"value must either be a block arg or the result of an op");
1374 TypeSwitch<Operation *>(op)
1377 ConstantOp, SpecialConstantOp, SubfieldOp, SubindexOp, SubaccessOp,
1378 OpenSubfieldOp, OpenSubindexOp,
1380 AddPrimOp, SubPrimOp, MulPrimOp, DivPrimOp, RemPrimOp, AndPrimOp,
1381 OrPrimOp, XorPrimOp, LEQPrimOp, LTPrimOp, GEQPrimOp, GTPrimOp,
1382 EQPrimOp, NEQPrimOp, DShlPrimOp, DShlwPrimOp, DShrPrimOp,
1384 AsSIntPrimOp, AsUIntPrimOp, AsAsyncResetPrimOp, AsClockPrimOp,
1385 CvtPrimOp, NegPrimOp, NotPrimOp, AndRPrimOp, OrRPrimOp, XorRPrimOp,
1387 BitsPrimOp, HeadPrimOp, TailPrimOp, PadPrimOp, MuxPrimOp, ShlPrimOp,
1388 ShrPrimOp, UninferredResetCastOp, ConstCastOp, StringConstantOp,
1389 FIntegerConstantOp, BoolConstantOp, DoubleConstantOp, ListCreateOp,
1390 UnresolvedPathOp, GenericIntrinsicOp, CatPrimOp,
1392 RefSendOp, RefResolveOp, RefSubOp, RWProbeOp, RefCastOp,
1394 TimeOp>([&](
auto op) {
1395 ps.
scopedBox(PP::ibox0, [&]() { emitExpression(op); });
1397 .Default([&](
auto op) {
1398 emitOpError(op,
"not supported as expression");
1399 ps <<
"<unsupported-expr-" <<
PPExtString(op->getName().stripDialect())
1404void Emitter::emitExpression(ConstantOp op) {
1406 emitType(op.getType(),
false);
1413void Emitter::emitExpression(SpecialConstantOp op) {
1414 auto emitInner = [&]() {
1421 .
Case<ClockType>([&](
auto type) {
1426 .Case<ResetType>([&](
auto type) { emitInner(); })
1427 .Case<AsyncResetType>([&](
auto type) {
1428 ps <<
"asAsyncReset(";
1435void Emitter::emitExpression(SubfieldOp op) {
1436 BundleType type = op.getInput().getType();
1437 emitExpression(op.getInput());
1438 ps <<
"." << legalize(type.getElementNameAttr(op.getFieldIndex()));
1442void Emitter::emitExpression(SubindexOp op) {
1443 emitExpression(op.getInput());
1450void Emitter::emitExpression(SubaccessOp op) {
1451 emitExpression(op.getInput());
1453 emitExpression(op.getIndex());
1457void Emitter::emitExpression(OpenSubfieldOp op) {
1458 auto type = op.getInput().getType();
1459 emitExpression(op.getInput());
1460 ps <<
"." << legalize(type.getElementNameAttr(op.getFieldIndex()));
1463void Emitter::emitExpression(OpenSubindexOp op) {
1464 emitExpression(op.getInput());
1470void Emitter::emitExpression(RefSendOp op) {
1472 emitExpression(op.getBase());
1476void Emitter::emitExpression(RefResolveOp op) {
1478 emitExpression(op.getRef());
1482void Emitter::emitExpression(RefSubOp op) {
1483 emitExpression(op.getInput());
1485 .
Case<FVectorType>([&](
auto type) {
1491 [&](
auto type) { ps <<
"." << type.getElementName(op.getIndex()); });
1494void Emitter::emitExpression(RWProbeOp op) {
1498 auto target = symInfos->get().irn.lookup(op.getTarget());
1500 if (target.isPort()) {
1501 auto mod = cast<FModuleOp>(target.getOp());
1502 auto port = target.getPort();
1503 base = mod.getArgument(port);
1505 base = cast<hw::InnerSymbolOpInterface>(target.getOp()).getTargetResult();
1508 emitExpression(base);
1511 auto fieldID = target.getField();
1512 auto type = base.getType();
1515 .
Case<FVectorType, OpenVectorType>([&](
auto vecTy) {
1516 auto index = vecTy.getIndexForFieldID(fieldID);
1520 auto [subtype, subfieldID] = vecTy.getSubTypeByFieldID(fieldID);
1522 fieldID = subfieldID;
1524 .Case<BundleType, OpenBundleType>([&](
auto bundleTy) {
1525 auto index = bundleTy.getIndexForFieldID(fieldID);
1526 ps <<
"." << bundleTy.getElementName(index);
1527 auto [subtype, subfieldID] = bundleTy.getSubTypeByFieldID(fieldID);
1529 fieldID = subfieldID;
1535void Emitter::emitExpression(RefCastOp op) { emitExpression(op.getInput()); }
1537void Emitter::emitExpression(UninferredResetCastOp op) {
1538 emitExpression(op.getInput());
1541void Emitter::emitExpression(FIntegerConstantOp op) {
1547void Emitter::emitExpression(BoolConstantOp op) {
1548 ps <<
"Bool(" << (op.getValue() ?
"true" :
"false") <<
")";
1551void Emitter::emitExpression(DoubleConstantOp op) {
1557 SmallString<16> str;
1558 op.getValueAttr().getValue().toString(str);
1563void Emitter::emitExpression(StringConstantOp op) {
1569void Emitter::emitExpression(ListCreateOp op) {
1570 return emitLiteralExpression(op.getType(), op.getElements());
1573void Emitter::emitExpression(UnresolvedPathOp op) {
1579void Emitter::emitExpression(GenericIntrinsicOp op) {
1580 emitGenericIntrinsic(op);
1583void Emitter::emitExpression(ConstCastOp op) { emitExpression(op.getInput()); }
1585void Emitter::emitPrimExpr(StringRef mnemonic, Operation *op,
1586 ArrayRef<uint32_t> attrs) {
1587 ps << mnemonic <<
"(" << PP::ibox0;
1588 interleaveComma(op->getOperands());
1589 if (!op->getOperands().empty() && !attrs.empty())
1590 ps <<
"," << PP::space;
1591 interleaveComma(attrs, [&](
auto attr) { ps.
addAsString(attr); });
1592 ps <<
")" << PP::end;
1595void Emitter::emitExpression(CatPrimOp op) {
1596 size_t numOperands = op.getNumOperands();
1597 switch (numOperands) {
1600 emitType(op.getType(),
false);
1604 auto operand = op->getOperand(0);
1606 if (isa<UIntType>(operand.getType()))
1607 return emitExpression(operand);
1610 ps <<
"cat(" << PP::ibox0;
1611 emitExpression(op->getOperand(0));
1612 ps <<
"," << PP::space <<
"SInt<0>(0))" << PP::end;
1618 for (
size_t i = 0; i < numOperands - 1; ++i) {
1619 ps <<
"cat(" << PP::ibox0;
1620 emitExpression(op->getOperand(i));
1621 ps <<
"," << PP::space;
1624 emitExpression(op->getOperand(numOperands - 1));
1625 for (
size_t i = 0; i < numOperands - 1; ++i)
1626 ps <<
")" << PP::end;
1631void Emitter::emitAttribute(MemDirAttr attr) {
1633 case MemDirAttr::Infer:
1636 case MemDirAttr::Read:
1639 case MemDirAttr::Write:
1642 case MemDirAttr::ReadWrite:
1648void Emitter::emitAttribute(RUWAttr attr) {
1650 case RUWAttr::Undefined:
1663void Emitter::emitType(Type type,
bool includeConst) {
1664 if (includeConst &&
isConst(type))
1666 auto emitWidth = [&](std::optional<int32_t> width) {
1675 .
Case<ClockType>([&](
auto) { ps <<
"Clock"; })
1676 .Case<ResetType>([&](
auto) { ps <<
"Reset"; })
1677 .Case<AsyncResetType>([&](
auto) { ps <<
"AsyncReset"; })
1678 .Case<UIntType>([&](
auto type) {
1680 emitWidth(type.getWidth());
1682 .Case<SIntType>([&](
auto type) {
1684 emitWidth(type.getWidth());
1686 .Case<AnalogType>([&](
auto type) {
1688 emitWidth(type.getWidth());
1690 .Case<OpenBundleType, BundleType>([&](
auto type) {
1692 if (!type.getElements().empty())
1694 bool anyEmitted =
false;
1696 for (
auto &element : type.getElements()) {
1698 ps <<
"," << PP::space;
1702 ps << legalize(element.name);
1703 emitTypeWithColon(element.type);
1712 .Case<OpenVectorType, FVectorType, CMemoryType>([&](
auto type) {
1713 emitType(type.getElementType());
1718 .Case<RefType>([&](RefType type) {
1719 if (type.getForceable())
1722 ps.
cbox(2, IndentStyle::Block);
1724 emitType(type.getType());
1725 if (
auto layer = type.getLayer()) {
1728 emitSymbol(type.getLayer());
1733 .Case<AnyRefType>([&](AnyRefType type) { ps <<
"AnyRef"; })
1734 .Case<StringType>([&](StringType type) { ps <<
"String"; })
1735 .Case<FIntegerType>([&](FIntegerType type) { ps <<
"Integer"; })
1736 .Case<BoolType>([&](BoolType type) { ps <<
"Bool"; })
1737 .Case<DoubleType>([&](DoubleType type) { ps <<
"Double"; })
1738 .Case<PathType>([&](PathType type) { ps <<
"Path"; })
1739 .Case<ListType>([&](ListType type) {
1741 emitType(type.getElementType());
1744 .Default([&](
auto type) {
1745 llvm_unreachable(
"all types should be implemented");
1751void Emitter::emitLocation(Location loc) {
1753 ps << PP::neverbreak;
1755 dyn_cast_or_null<FileLineColLoc, LocationAttr>(LocationAttr(loc))) {
1756 ps <<
" @[" << fileLoc.getFilename().getValue();
1757 if (
auto line = fileLoc.getLine()) {
1760 if (
auto col = fileLoc.getColumn()) {
1777 std::optional<size_t> targetLineLength,
1779 Emitter emitter(os, version,
1780 targetLineLength.value_or(defaultTargetLineLength));
1781 for (
auto &op : *
module.getBody()) {
1782 if (auto circuitOp = dyn_cast<CircuitOp>(op))
1783 emitter.emitCircuit(circuitOp);
1785 return emitter.finalize();
1790 "target-line-length",
1791 llvm::cl::desc(
"Target line length for emitted .fir"),
1792 llvm::cl::value_desc(
"number of chars"),
1793 llvm::cl::init(defaultTargetLineLength));
1794 static mlir::TranslateFromMLIRRegistration toFIR(
1795 "export-firrtl",
"emit FIRRTL dialect operations to .fir output",
1796 [](ModuleOp module, llvm::raw_ostream &os) {
1799 [](mlir::DialectRegistry ®istry) {
1800 registry.insert<chirrtl::CHIRRTLDialect>();
1801 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.