CIRCT 20.0.0git
Loading...
Searching...
No Matches
FIREmitter.cpp
Go to the documentation of this file.
1//===- FIREmitter.cpp - FIRRTL dialect to .fir emitter --------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This implements a .fir file emitter.
10//
11//===----------------------------------------------------------------------===//
12
19#include "circt/Support/LLVM.h"
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"
27
28#define DEBUG_TYPE "export-firrtl"
29
30using namespace circt;
31using namespace firrtl;
32using namespace chirrtl;
33using namespace pretty;
34
35//===----------------------------------------------------------------------===//
36// Emitter
37//===----------------------------------------------------------------------===//
38
39// NOLINTBEGIN(misc-no-recursion)
40namespace {
41
42constexpr size_t defaultTargetLineLength = 80;
43
44/// An emitter for FIRRTL dialect operations to .fir output.
45struct Emitter {
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);
50 }
51 LogicalResult finalize();
52
53 // Circuit/module emission
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);
65
66 void emitParamAssign(ParamDeclAttr param, Operation *op,
67 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
68 void emitParamValue(Attribute value, Operation *op);
69
70 void emitGenericIntrinsic(GenericIntrinsicOp op);
71
72 // Statement emission
73 void emitStatementsInBlock(Block &block);
74 void emitStatement(WhenOp op);
75 void emitStatement(WireOp op);
76 void emitStatement(RegOp op);
77 void emitStatement(RegResetOp op);
78 void emitStatement(NodeOp op);
79 void emitStatement(StopOp op);
80 void emitStatement(SkipOp op);
81 void emitStatement(PrintFOp op);
82 void emitStatement(ConnectOp op);
83 void emitStatement(MatchingConnectOp op);
84 void emitStatement(PropAssignOp op);
85 void emitStatement(InstanceOp op);
86 void emitStatement(InstanceChoiceOp op);
87 void emitStatement(AttachOp op);
88 void emitStatement(MemOp op);
89 void emitStatement(InvalidValueOp op);
90 void emitStatement(CombMemOp op);
91 void emitStatement(SeqMemOp op);
92 void emitStatement(MemoryPortOp op);
93 void emitStatement(MemoryDebugPortOp op);
94 void emitStatement(MemoryPortAccessOp op);
95 void emitStatement(RefDefineOp op);
96 void emitStatement(RefForceOp op);
97 void emitStatement(RefForceInitialOp op);
98 void emitStatement(RefReleaseOp op);
99 void emitStatement(RefReleaseInitialOp op);
100 void emitStatement(LayerBlockOp op);
101 void emitStatement(GenericIntrinsicOp op);
102
103 template <class T>
104 void emitVerifStatement(T op, StringRef mnemonic);
105 void emitStatement(AssertOp op) { emitVerifStatement(op, "assert"); }
106 void emitStatement(AssumeOp op) { emitVerifStatement(op, "assume"); }
107 void emitStatement(CoverOp op) { emitVerifStatement(op, "cover"); }
108
109 // Exprsesion emission
110 void emitExpression(Value value);
111 void emitExpression(ConstantOp op);
112 void emitExpression(SpecialConstantOp op);
113 void emitExpression(SubfieldOp op);
114 void emitExpression(SubindexOp op);
115 void emitExpression(SubaccessOp op);
116 void emitExpression(OpenSubfieldOp op);
117 void emitExpression(OpenSubindexOp op);
118 void emitExpression(RefResolveOp op);
119 void emitExpression(RefSendOp op);
120 void emitExpression(RefSubOp op);
121 void emitExpression(RWProbeOp op);
122 void emitExpression(RefCastOp op);
123 void emitExpression(UninferredResetCastOp op);
124 void emitExpression(ConstCastOp op);
125 void emitExpression(StringConstantOp op);
126 void emitExpression(FIntegerConstantOp op);
127 void emitExpression(BoolConstantOp op);
128 void emitExpression(DoubleConstantOp op);
129 void emitExpression(ListCreateOp op);
130 void emitExpression(UnresolvedPathOp op);
131 void emitExpression(GenericIntrinsicOp op);
132
133 void emitPrimExpr(StringRef mnemonic, Operation *op,
134 ArrayRef<uint32_t> attrs = {});
135
136 void emitExpression(BitsPrimOp op) {
137 emitPrimExpr("bits", op, {op.getHi(), op.getLo()});
138 }
139 void emitExpression(HeadPrimOp op) {
140 emitPrimExpr("head", op, op.getAmount());
141 }
142 void emitExpression(TailPrimOp op) {
143 emitPrimExpr("tail", op, op.getAmount());
144 }
145 void emitExpression(PadPrimOp op) { emitPrimExpr("pad", op, op.getAmount()); }
146 void emitExpression(ShlPrimOp op) { emitPrimExpr("shl", op, op.getAmount()); }
147 void emitExpression(ShrPrimOp op) { emitPrimExpr("shr", op, op.getAmount()); }
148
149 // Funnel all ops without attrs into `emitPrimExpr`.
150#define HANDLE(OPTYPE, MNEMONIC) \
151 void emitExpression(OPTYPE op) { emitPrimExpr(MNEMONIC, op); }
152 HANDLE(AddPrimOp, "add");
153 HANDLE(SubPrimOp, "sub");
154 HANDLE(MulPrimOp, "mul");
155 HANDLE(DivPrimOp, "div");
156 HANDLE(RemPrimOp, "rem");
157 HANDLE(AndPrimOp, "and");
158 HANDLE(OrPrimOp, "or");
159 HANDLE(XorPrimOp, "xor");
160 HANDLE(LEQPrimOp, "leq");
161 HANDLE(LTPrimOp, "lt");
162 HANDLE(GEQPrimOp, "geq");
163 HANDLE(GTPrimOp, "gt");
164 HANDLE(EQPrimOp, "eq");
165 HANDLE(NEQPrimOp, "neq");
166 HANDLE(CatPrimOp, "cat");
167 HANDLE(DShlPrimOp, "dshl");
168 HANDLE(DShlwPrimOp, "dshlw");
169 HANDLE(DShrPrimOp, "dshr");
170 HANDLE(MuxPrimOp, "mux");
171 HANDLE(AsSIntPrimOp, "asSInt");
172 HANDLE(AsUIntPrimOp, "asUInt");
173 HANDLE(AsAsyncResetPrimOp, "asAsyncReset");
174 HANDLE(AsClockPrimOp, "asClock");
175 HANDLE(CvtPrimOp, "cvt");
176 HANDLE(NegPrimOp, "neg");
177 HANDLE(NotPrimOp, "not");
178 HANDLE(AndRPrimOp, "andr");
179 HANDLE(OrRPrimOp, "orr");
180 HANDLE(XorRPrimOp, "xorr");
181#undef HANDLE
182
183 // Attributes
184 void emitAttribute(MemDirAttr attr);
185 void emitAttribute(RUWAttr attr);
186
187 // Types
188 void emitType(Type type, bool includeConst = true);
189 void emitTypeWithColon(Type type) {
190 ps << PP::space << ":" << PP::nbsp;
191 emitType(type);
192 }
193
194 // Locations
195 void emitLocation(Location loc);
196 void emitLocation(Operation *op) { emitLocation(op->getLoc()); }
197 template <typename... Args>
198 void emitLocationAndNewLine(Args... args) {
199 // Break so previous content is not impacted by following,
200 // but use a 'neverbreak' so it always fits.
201 ps << PP::neverbreak;
202 emitLocation(args...);
203 setPendingNewline();
204 }
205
206 void emitAssignLike(llvm::function_ref<void()> emitLHS,
207 llvm::function_ref<void()> emitRHS,
208 PPExtString syntax = PPExtString("="),
209 std::optional<PPExtString> wordBeforeLHS = std::nullopt) {
210 // If wraps, indent.
211 ps.scopedBox(PP::ibox2, [&]() {
212 if (wordBeforeLHS) {
213 ps << *wordBeforeLHS << PP::space;
214 }
215 emitLHS();
216 // Allow breaking before 'syntax' (e.g., '=') if long assignment.
217 ps << PP::space << syntax << PP::nbsp; /* PP::space; */
218 // RHS is boxed to right of the syntax.
219 ps.scopedBox(PP::ibox0, [&]() { emitRHS(); });
220 });
221 }
222
223 /// Emit the specified value as a subexpression, wrapping in an ibox2.
224 void emitSubExprIBox2(Value v) {
225 ps.scopedBox(PP::ibox2, [&]() { emitExpression(v); });
226 }
227
228 /// Emit a range of values separated by commas and a breakable space.
229 /// Each value is emitted by invoking `eachFn`.
230 template <typename Container, typename EachFn>
231 void interleaveComma(const Container &c, EachFn eachFn) {
232 llvm::interleave(c, eachFn, [&]() { ps << "," << PP::space; });
233 }
234
235 /// Emit a range of values separated by commas and a breakable space.
236 /// Each value is emitted in an ibox2.
237 void interleaveComma(ValueRange ops) {
238 return interleaveComma(ops, [&](Value v) { emitSubExprIBox2(v); });
239 }
240
241 void emitStatementFunctionOp(PPExtString name, Operation *op) {
242 startStatement();
243 ps << name << "(";
244 ps.scopedBox(PP::ibox0, [&]() {
245 interleaveComma(op->getOperands());
246 ps << ")";
247 });
248 emitLocationAndNewLine(op);
249 }
250
251 template <typename EachFn, typename Range>
252 void emitLiteralExpression(Type type, const Range &r, EachFn eachFn) {
253 emitType(type);
254 ps << "(";
255 ps.scopedBox(PP::ibox0, [&]() {
256 interleaveComma(r, eachFn);
257 ps << ")";
258 });
259 }
260
261 void emitLiteralExpression(Type type, ValueRange values) {
262 return emitLiteralExpression(type, values,
263 [&](Value v) { emitSubExprIBox2(v); });
264 }
265
266 /// Emit a (potentially nested) symbol reference as `A.B.C`.
267 void emitSymbol(SymbolRefAttr symbol) {
268 ps.ibox(2, IndentStyle::Block);
269 ps << symbol.getRootReference();
270 for (auto nested : symbol.getNestedReferences()) {
271 ps.zerobreak();
272 ps << ".";
273 ps << nested.getAttr();
274 }
275 ps.end();
276 }
277
278private:
279 /// Emit an error and remark that emission failed.
280 InFlightDiagnostic emitError(Operation *op, const Twine &message) {
281 encounteredError = true;
282 return op->emitError(message);
283 }
284
285 /// Emit an error and remark that emission failed.
286 InFlightDiagnostic emitOpError(Operation *op, const Twine &message) {
287 encounteredError = true;
288 return op->emitOpError(message);
289 }
290
291 /// Return the name used during emission of a `Value`, or none if the value
292 /// has not yet been emitted or it was emitted inline.
293 std::optional<StringRef> lookupEmittedName(Value value) {
294 auto it = valueNames.find(value);
295 if (it != valueNames.end())
296 return {it->second};
297 return {};
298 }
299
300 /// If previous emission requires a newline, emit it now.
301 /// This gives us opportunity to open/close boxes before linebreak.
302 void emitPendingNewlineIfNeeded() {
303 if (pendingNewline) {
304 pendingNewline = false;
305 ps << PP::newline;
306 }
307 }
308 void setPendingNewline() {
309 assert(!pendingNewline);
310 pendingNewline = true;
311 }
312
313 void startStatement() { emitPendingNewlineIfNeeded(); }
314
315private:
316 /// String storage backing Tokens built from temporary strings.
317 /// PrettyPrinter will clear this as appropriate.
318 TokenStringSaver saver;
319
320 /// Pretty printer.
321 PrettyPrinter pp;
322
323 /// Stream helper (pp, saver).
324 TokenStream<> ps;
325
326 /// Whether a newline is expected, emitted late to provide opportunity to
327 /// open/close boxes we don't know we need at level of individual statement.
328 /// Every statement should set this instead of directly emitting (last)
329 /// newline. Most statements end with emitLocationInfoAndNewLine which handles
330 /// this.
331 bool pendingNewline = false;
332
333 /// Whether we have encountered any errors during emission.
334 bool encounteredError = false;
335
336 /// The names used to emit values already encountered. Anything that gets a
337 /// name in the output FIR is listed here, such that future expressions can
338 /// reference it.
339 DenseMap<Value, StringRef> valueNames;
340 StringSet<> valueNamesStorage;
341
342 /// Legalize names for emission. Convert names which begin with a number to
343 /// be escaped using backticks.
344 StringAttr legalize(StringAttr attr) {
345 StringRef str = attr.getValue();
346 if (str.empty() || !isdigit(str.front()))
347 return attr;
348 return StringAttr::get(attr.getContext(), "`" + Twine(attr) + "`");
349 }
350
351 void addValueName(Value value, StringAttr attr) {
352 valueNames.insert({value, attr.getValue()});
353 }
354 void addValueName(Value value, StringRef str) {
355 auto it = valueNamesStorage.insert(str);
356 valueNames.insert({value, it.first->getKey()});
357 }
358 void addForceable(Forceable op, StringAttr attr) {
359 addValueName(op.getData(), attr);
360 if (op.isForceable()) {
361 SmallString<32> rwName;
362 (Twine("rwprobe(") + attr.strref() + ")").toVector(rwName);
363 addValueName(op.getDataRef(), rwName);
364 }
365 }
366
367 /// The current circuit namespace valid within the call to `emitCircuit`.
368 CircuitNamespace circuitNamespace;
369
370 /// Symbol and Inner Symbol analyses, valid within the call to `emitCircuit`.
371 struct SymInfos {
372 SymbolTable symbolTable;
374 hw::InnerRefNamespace irn{symbolTable, istc};
375 SymInfos(Operation *op) : symbolTable(op), istc(op) {};
376 };
377 std::optional<std::reference_wrapper<SymInfos>> symInfos;
378
379 /// The version of the FIRRTL spec that should be emitted.
380 FIRVersion version;
381};
382} // namespace
383
384LogicalResult Emitter::finalize() { return failure(encounteredError); }
385
386/// Emit an entire circuit.
387void Emitter::emitCircuit(CircuitOp op) {
388 circuitNamespace.add(op);
389 SymInfos circuitSymInfos(op);
390 symInfos = circuitSymInfos;
391 startStatement();
392 ps << "FIRRTL version ";
393 ps.addAsString(version.major);
394 ps << ".";
395 ps.addAsString(version.minor);
396 ps << ".";
397 ps.addAsString(version.patch);
398 ps << PP::newline;
399 ps << "circuit " << PPExtString(legalize(op.getNameAttr())) << " :";
400 setPendingNewline();
401 ps.scopedBox(PP::bbox2, [&]() {
402 for (auto &bodyOp : *op.getBodyBlock()) {
403 if (encounteredError)
404 break;
405 TypeSwitch<Operation *>(&bodyOp)
406 .Case<FModuleOp, FExtModuleOp, FIntModuleOp>([&](auto op) {
407 emitModule(op);
408 ps << PP::newline;
409 })
410 .Case<LayerOp>([&](auto op) { emitDeclaration(op); })
411 .Case<OptionOp>([&](auto op) { emitDeclaration(op); })
412 .Case<FormalOp>([&](auto op) { emitDeclaration(op); })
413 .Default([&](auto op) {
414 emitOpError(op, "not supported for emission inside circuit");
415 });
416 }
417 });
418 circuitNamespace.clear();
419 symInfos = std::nullopt;
420}
421
422void Emitter::emitEnabledLayers(ArrayRef<Attribute> layers) {
423 for (auto layer : layers) {
424 ps << PP::space;
425 ps.cbox(2, IndentStyle::Block);
426 ps << "enablelayer" << PP::space;
427 emitSymbol(cast<SymbolRefAttr>(layer));
428 ps << PP::end;
429 }
430}
431
432void Emitter::emitParamAssign(ParamDeclAttr param, Operation *op,
433 std::optional<PPExtString> wordBeforeLHS) {
434 if (wordBeforeLHS) {
435 ps << *wordBeforeLHS << PP::nbsp;
436 }
437 ps << PPExtString(param.getName().strref()) << PP::nbsp << "=" << PP::nbsp;
438 emitParamValue(param.getValue(), op);
439}
440
441void Emitter::emitParamValue(Attribute value, Operation *op) {
442 TypeSwitch<Attribute>(value)
443 .Case<IntegerAttr>([&](auto attr) { ps.addAsString(attr.getValue()); })
444 .Case<FloatAttr>([&](auto attr) {
445 SmallString<16> str;
446 attr.getValue().toString(str);
447 ps << str;
448 })
449 .Case<StringAttr>(
450 [&](auto attr) { ps.writeQuotedEscaped(attr.getValue()); })
451 .Case<ArrayAttr>([&](auto attr) {
452 ps.scopedBox(PP::bbox2, [&]() {
453 ps << "[";
454 interleaveComma(attr.getValue(),
455 [&](auto element) { emitParamValue(element, op); });
456 ps << "]";
457 });
458 })
459 .Case<DictionaryAttr>([&](auto attr) {
460 ps.scopedBox(PP::bbox2, [&]() {
461 ps << "{";
462 interleaveComma(attr.getValue(), [&](auto field) {
463 ps << PPExtString(field.getName()) << PP::nbsp << "=" << PP::nbsp;
464 emitParamValue(field.getValue(), op);
465 });
466 ps << "}";
467 });
468 })
469 .Default([&](auto attr) {
470 emitOpError(op, "with unsupported parameter attribute: ") << attr;
471 ps << "<unsupported-attr ";
472 ps.addAsString(attr);
473 ps << ">";
474 });
475}
476
477void Emitter::emitGenericIntrinsic(GenericIntrinsicOp op) {
478 ps << "intrinsic(";
479 ps.scopedBox(PP::cbox0, [&]() {
480 ps.scopedBox(PP::ibox2, [&]() {
481 ps << op.getIntrinsic();
482 ps.scopedBox(PP::ibox0, [&]() {
483 auto params = op.getParameters();
484 if (!params.empty()) {
485 ps << "<";
486 ps.scopedBox(PP::ibox0, [&]() {
487 interleaveComma(
488 params.getAsRange<ParamDeclAttr>(),
489 [&](ParamDeclAttr param) { emitParamAssign(param, op); });
490 });
491 ps << ">";
492 }
493 });
494 if (op.getNumResults() != 0)
495 emitTypeWithColon(op.getResult().getType());
496 });
497 if (op.getNumOperands() != 0) {
498 ps << "," << PP::space;
499 ps.scopedBox(PP::ibox0, [&]() { interleaveComma(op->getOperands()); });
500 }
501 ps << ")";
502 });
503}
504
505/// Emit an entire module.
506void Emitter::emitModule(FModuleOp op) {
507 startStatement();
508 ps.cbox(4, IndentStyle::Block);
509 if (op.isPublic())
510 ps << "public" << PP::nbsp;
511 ps << "module " << PPExtString(legalize(op.getNameAttr()));
512 emitEnabledLayers(op.getLayers());
513 ps << PP::nbsp << ":" << PP::end;
514 emitLocation(op);
515
516 ps.scopedBox(PP::bbox2, [&]() {
517 setPendingNewline();
518
519 // Emit the ports.
520 auto ports = op.getPorts();
521 emitModulePorts(ports, op.getArguments());
522 if (!ports.empty() && !op.getBodyBlock()->empty())
523 ps << PP::newline;
524
525 // Emit the module body.
526 emitStatementsInBlock(*op.getBodyBlock());
527 });
528 valueNames.clear();
529 valueNamesStorage.clear();
530}
531
532/// Emit an external module.
533void Emitter::emitModule(FExtModuleOp op) {
534 startStatement();
535 ps.cbox(4, IndentStyle::Block);
536 ps << "extmodule " << PPExtString(legalize(op.getNameAttr()));
537 emitEnabledLayers(op.getLayers());
538 ps << PP::nbsp << ":" << PP::end;
539 emitLocation(op);
540
541 ps.scopedBox(PP::bbox2, [&]() {
542 setPendingNewline();
543
544 // Emit the ports.
545 auto ports = op.getPorts();
546 emitModulePorts(ports);
547
548 // Emit the optional `defname`.
549 if (op.getDefname() && !op.getDefname()->empty()) {
550 startStatement();
551 ps << "defname = " << PPExtString(*op.getDefname());
552 setPendingNewline();
553 }
554
555 // Emit the parameters.
556 emitModuleParameters(op, op.getParameters());
557 });
558}
559
560/// Emit an intrinsic module
561void Emitter::emitModule(FIntModuleOp op) {
562 startStatement();
563 ps.cbox(4, IndentStyle::Block);
564 ps << "intmodule " << PPExtString(legalize(op.getNameAttr()));
565 emitEnabledLayers(op.getLayers());
566 ps << PP::nbsp << ":" << PP::end;
567 emitLocation(op);
568
569 ps.scopedBox(PP::bbox2, [&]() {
570 setPendingNewline();
571
572 // Emit the ports.
573 auto ports = op.getPorts();
574 emitModulePorts(ports);
575
576 startStatement();
577 ps << "intrinsic = " << PPExtString(op.getIntrinsic());
578 setPendingNewline();
579
580 // Emit the parameters.
581 emitModuleParameters(op, op.getParameters());
582 });
583}
584
585/// Emit the ports of a module or extmodule. If the `arguments` array is
586/// non-empty, it is used to populate `emittedNames` with the port names for use
587/// during expression emission.
588void Emitter::emitModulePorts(ArrayRef<PortInfo> ports,
589 Block::BlockArgListType arguments) {
590 for (unsigned i = 0, e = ports.size(); i < e; ++i) {
591 startStatement();
592 const auto &port = ports[i];
593 ps << (port.direction == Direction::In ? "input " : "output ");
594 auto legalName = legalize(port.name);
595 if (!arguments.empty())
596 addValueName(arguments[i], legalName);
597 ps << PPExtString(legalName) << " : ";
598 emitType(port.type);
599 emitLocation(ports[i].loc);
600 setPendingNewline();
601 }
602}
603
604void Emitter::emitModuleParameters(Operation *op, ArrayAttr parameters) {
605 for (auto param : parameters.getAsRange<ParamDeclAttr>()) {
606 startStatement();
607 emitParamAssign(param, op, PPExtString("parameter"));
608 setPendingNewline();
609 }
610}
611
612/// Emit a layer definition.
613void Emitter::emitDeclaration(LayerOp op) {
614 startStatement();
615 ps << "layer " << PPExtString(op.getSymName()) << ", "
616 << PPExtString(stringifyLayerConvention(op.getConvention()));
617
618 if (auto outputFile = op->getAttrOfType<hw::OutputFileAttr>("output_file")) {
619 ps << ", ";
620 ps.writeQuotedEscaped(outputFile.getFilename().getValue());
621 }
622
623 ps << " : ";
624 emitLocationAndNewLine(op);
625 ps.scopedBox(PP::bbox2, [&]() {
626 for (auto &bodyOp : op.getBody().getOps()) {
627 TypeSwitch<Operation *>(&bodyOp)
628 .Case<LayerOp>([&](auto op) { emitDeclaration(op); })
629 .Default([&](auto op) {
630 emitOpError(op,
631 "not supported for emission inside layer definition");
632 });
633 }
634 });
635}
636
637/// Emit an option declaration.
638void Emitter::emitDeclaration(OptionOp op) {
639 startStatement();
640 ps << "option " << PPExtString(legalize(op.getSymNameAttr())) << " :";
641 emitLocation(op);
642 ps.scopedBox(PP::bbox2, [&] {
643 for (auto caseOp : op.getBody().getOps<OptionCaseOp>()) {
644 ps << PP::newline;
645 ps << PPExtString(legalize(caseOp.getSymNameAttr()));
646 emitLocation(caseOp);
647 }
648 });
649 ps << PP::newline << PP::newline;
650}
651
652/// Emit a formal test definition.
653void Emitter::emitDeclaration(FormalOp op) {
654 startStatement();
655 ps.cbox(4, IndentStyle::Block);
656 ps << "formal " << PPExtString(legalize(op.getSymNameAttr()));
657 ps << " of " << PPExtString(legalize(op.getModuleNameAttr().getAttr()));
658 ps << PP::nbsp << ":" << PP::end;
659 emitLocation(op);
660
661 ps.scopedBox(PP::bbox2, [&]() {
662 setPendingNewline();
663 for (auto param : op.getParameters()) {
664 startStatement();
665 ps << PPExtString(param.getName()) << PP::nbsp << "=" << PP::nbsp;
666 emitParamValue(param.getValue(), op);
667 setPendingNewline();
668 }
669 });
670}
671
672/// Check if an operation is inlined into the emission of their users. For
673/// example, subfields are always inlined.
674static bool isEmittedInline(Operation *op) {
675 // FIRRTL expressions are statically classified as always inlineable.
676 // InvalidValueOp never is inlined, and is handled specially.
677 // GenericIntrinsicOp is inlined if has exactly one use (only emit once)
678 // that is not emitted inline. This is to ensure it is emitted inline
679 // in common cases, but only inspect one level deep.
680 return (isExpression(op) && !isa<InvalidValueOp>(op)) ||
681 (isa<GenericIntrinsicOp>(op) && op->hasOneUse() &&
682 !isEmittedInline(*op->getUsers().begin()));
683}
684
685void Emitter::emitStatementsInBlock(Block &block) {
686 for (auto &bodyOp : block) {
687 if (encounteredError)
688 return;
689 if (isEmittedInline(&bodyOp))
690 continue;
691 TypeSwitch<Operation *>(&bodyOp)
692 .Case<WhenOp, WireOp, RegOp, RegResetOp, NodeOp, StopOp, SkipOp,
693 PrintFOp, AssertOp, AssumeOp, CoverOp, ConnectOp,
694 MatchingConnectOp, PropAssignOp, InstanceOp, InstanceChoiceOp,
695 AttachOp, MemOp, InvalidValueOp, SeqMemOp, CombMemOp,
696 MemoryPortOp, MemoryDebugPortOp, MemoryPortAccessOp, RefDefineOp,
697 RefForceOp, RefForceInitialOp, RefReleaseOp, RefReleaseInitialOp,
698 LayerBlockOp, GenericIntrinsicOp>(
699 [&](auto op) { emitStatement(op); })
700 .Default([&](auto op) {
701 startStatement();
702 ps << "// operation " << PPExtString(op->getName().getStringRef());
703 setPendingNewline();
704 emitOpError(op, "not supported as statement");
705 });
706 }
707}
708
709void Emitter::emitStatement(WhenOp op) {
710 startStatement();
711 ps << "when ";
712 emitExpression(op.getCondition());
713 ps << " :";
714 emitLocationAndNewLine(op);
715 ps.scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(op.getThenBlock()); });
716 // emitStatementsInBlock(op.getThenBlock());
717 if (!op.hasElseRegion())
718 return;
719
720 startStatement();
721 ps << "else ";
722 // Sugar to `else when ...` if there's only a single when statement in the
723 // else block.
724 auto &elseBlock = op.getElseBlock();
725 if (!elseBlock.empty() && &elseBlock.front() == &elseBlock.back()) {
726 if (auto whenOp = dyn_cast<WhenOp>(&elseBlock.front())) {
727 emitStatement(whenOp);
728 return;
729 }
730 }
731 // Otherwise print the block as `else :`.
732 ps << ":";
733 setPendingNewline();
734 ps.scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(elseBlock); });
735}
736
737void Emitter::emitStatement(WireOp op) {
738 auto legalName = legalize(op.getNameAttr());
739 addForceable(op, legalName);
740 startStatement();
741 ps.scopedBox(PP::ibox2, [&]() {
742 ps << "wire " << PPExtString(legalName);
743 emitTypeWithColon(op.getResult().getType());
744 });
745 emitLocationAndNewLine(op);
746}
747
748void Emitter::emitStatement(RegOp op) {
749 auto legalName = legalize(op.getNameAttr());
750 addForceable(op, legalName);
751 startStatement();
752 ps.scopedBox(PP::ibox2, [&]() {
753 ps << "reg " << PPExtString(legalName);
754 emitTypeWithColon(op.getResult().getType());
755 ps << "," << PP::space;
756 emitExpression(op.getClockVal());
757 });
758 emitLocationAndNewLine(op);
759}
760
761void Emitter::emitStatement(RegResetOp op) {
762 auto legalName = legalize(op.getNameAttr());
763 addForceable(op, legalName);
764 startStatement();
765 if (FIRVersion(3, 0, 0) <= version) {
766 ps.scopedBox(PP::ibox2, [&]() {
767 ps << "regreset " << legalName;
768 emitTypeWithColon(op.getResult().getType());
769 ps << "," << PP::space;
770 emitExpression(op.getClockVal());
771 ps << "," << PP::space;
772 emitExpression(op.getResetSignal());
773 ps << "," << PP::space;
774 emitExpression(op.getResetValue());
775 });
776 } else {
777 ps.scopedBox(PP::ibox2, [&]() {
778 ps << "reg " << legalName;
779 emitTypeWithColon(op.getResult().getType());
780 ps << "," << PP::space;
781 emitExpression(op.getClockVal());
782 ps << PP::space << "with :";
783 // Don't break this because of the newline.
784 ps << PP::neverbreak;
785 // No-paren version must be newline + indent.
786 ps << PP::newline; // ibox2 will indent.
787 ps << "reset => (" << PP::ibox0;
788 emitExpression(op.getResetSignal());
789 ps << "," << PP::space;
790 emitExpression(op.getResetValue());
791 ps << ")" << PP::end;
792 });
793 }
794 emitLocationAndNewLine(op);
795}
796
797void Emitter::emitStatement(NodeOp op) {
798 auto legalName = legalize(op.getNameAttr());
799 addForceable(op, legalName);
800 startStatement();
801 emitAssignLike([&]() { ps << "node " << PPExtString(legalName); },
802 [&]() { emitExpression(op.getInput()); });
803 emitLocationAndNewLine(op);
804}
805
806void Emitter::emitStatement(StopOp op) {
807 startStatement();
808 ps.scopedBox(PP::ibox2, [&]() {
809 ps << "stop(" << PP::ibox0;
810 emitExpression(op.getClock());
811 ps << "," << PP::space;
812 emitExpression(op.getCond());
813 ps << "," << PP::space;
814 ps.addAsString(op.getExitCode());
815 ps << ")" << PP::end;
816 if (!op.getName().empty()) {
817 ps << PP::space << ": " << PPExtString(legalize(op.getNameAttr()));
818 }
819 });
820 emitLocationAndNewLine(op);
821}
822
823void Emitter::emitStatement(SkipOp op) {
824 startStatement();
825 ps << "skip";
826 emitLocationAndNewLine(op);
827}
828
829void Emitter::emitStatement(PrintFOp op) {
830 startStatement();
831 ps.scopedBox(PP::ibox2, [&]() {
832 ps << "printf(" << PP::ibox0;
833 emitExpression(op.getClock());
834 ps << "," << PP::space;
835 emitExpression(op.getCond());
836 ps << "," << PP::space;
837 ps.writeQuotedEscaped(op.getFormatString());
838 for (auto operand : op.getSubstitutions()) {
839 ps << "," << PP::space;
840 emitExpression(operand);
841 }
842 ps << ")" << PP::end;
843 if (!op.getName().empty()) {
844 ps << PP::space << ": " << PPExtString(legalize(op.getNameAttr()));
845 }
846 });
847 emitLocationAndNewLine(op);
848}
849
850template <class T>
851void Emitter::emitVerifStatement(T op, StringRef mnemonic) {
852 startStatement();
853 ps.scopedBox(PP::ibox2, [&]() {
854 ps << mnemonic << "(" << PP::ibox0;
855 emitExpression(op.getClock());
856 ps << "," << PP::space;
857 emitExpression(op.getPredicate());
858 ps << "," << PP::space;
859 emitExpression(op.getEnable());
860 ps << "," << PP::space;
861 ps.writeQuotedEscaped(op.getMessage());
862 ps << ")" << PP::end;
863 if (!op.getName().empty()) {
864 ps << PP::space << ": " << PPExtString(legalize(op.getNameAttr()));
865 }
866 });
867 emitLocationAndNewLine(op);
868}
869
870void Emitter::emitStatement(ConnectOp op) {
871 startStatement();
872 if (FIRVersion(3, 0, 0) <= version) {
873 ps.scopedBox(PP::ibox2, [&]() {
874 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
875 ps << "invalidate" << PP::space;
876 emitExpression(op.getDest());
877 } else {
878 ps << "connect" << PP::space;
879 emitExpression(op.getDest());
880 ps << "," << PP::space;
881 emitExpression(op.getSrc());
882 }
883 });
884 } else {
885 auto emitLHS = [&]() { emitExpression(op.getDest()); };
886 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
887 emitAssignLike(emitLHS, [&]() { ps << "invalid"; }, PPExtString("is"));
888 } else {
889 emitAssignLike(
890 emitLHS, [&]() { emitExpression(op.getSrc()); }, PPExtString("<="));
891 }
892 }
893 emitLocationAndNewLine(op);
894}
895
896void Emitter::emitStatement(MatchingConnectOp op) {
897 startStatement();
898 if (FIRVersion(3, 0, 0) <= version) {
899 ps.scopedBox(PP::ibox2, [&]() {
900 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
901 ps << "invalidate" << PP::space;
902 emitExpression(op.getDest());
903 } else {
904 ps << "connect" << PP::space;
905 emitExpression(op.getDest());
906 ps << "," << PP::space;
907 emitExpression(op.getSrc());
908 }
909 });
910 } else {
911 auto emitLHS = [&]() { emitExpression(op.getDest()); };
912 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
913 emitAssignLike(emitLHS, [&]() { ps << "invalid"; }, PPExtString("is"));
914 } else {
915 emitAssignLike(
916 emitLHS, [&]() { emitExpression(op.getSrc()); }, PPExtString("<="));
917 }
918 }
919 emitLocationAndNewLine(op);
920}
921
922void Emitter::emitStatement(PropAssignOp op) {
923 startStatement();
924 ps.scopedBox(PP::ibox2, [&]() {
925 ps << "propassign" << PP::space;
926 interleaveComma(op.getOperands());
927 });
928 emitLocationAndNewLine(op);
929}
930
931void Emitter::emitStatement(InstanceOp op) {
932 startStatement();
933 auto legalName = legalize(op.getNameAttr());
934 ps << "inst " << PPExtString(legalName) << " of "
935 << PPExtString(legalize(op.getModuleNameAttr().getAttr()));
936 emitLocationAndNewLine(op);
937
938 // Make sure we have a name like `<inst>.<port>` for each of the instance
939 // result values.
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);
947 }
948}
949
950void Emitter::emitStatement(InstanceChoiceOp op) {
951 startStatement();
952 auto legalName = legalize(op.getNameAttr());
953 ps << "instchoice " << PPExtString(legalName) << " of "
954 << PPExtString(legalize(op.getDefaultTargetAttr().getAttr())) << ", "
955 << PPExtString(legalize(op.getOptionNameAttr())) << " :";
956 emitLocation(op);
957 ps.scopedBox(PP::bbox2, [&] {
958 for (const auto &[optSym, targetSym] : op.getTargetChoices()) {
959 ps << PP::newline;
960 ps << PPExtString(legalize(optSym.getLeafReference()));
961 ps << " => ";
962 ps << PPExtString(legalize(targetSym.getAttr()));
963 }
964 });
965 setPendingNewline();
966
967 SmallString<16> portName(legalName);
968 portName.push_back('.');
969 unsigned baseLen = portName.size();
970 for (unsigned i = 0, e = op.getNumResults(); i < e; ++i) {
971 portName.append(legalize(op.getPortName(i)));
972 addValueName(op.getResult(i), portName);
973 portName.resize(baseLen);
974 }
975}
976
977void Emitter::emitStatement(AttachOp op) {
978 emitStatementFunctionOp(PPExtString("attach"), op);
979}
980
981void Emitter::emitStatement(MemOp op) {
982 auto legalName = legalize(op.getNameAttr());
983 SmallString<16> portName(legalName);
984 portName.push_back('.');
985 auto portNameBaseLen = portName.size();
986 for (auto result : llvm::zip(op.getResults(), op.getPortNames())) {
987 portName.resize(portNameBaseLen);
988 portName.append(legalize(cast<StringAttr>(std::get<1>(result))));
989 addValueName(std::get<0>(result), portName);
990 }
991
992 startStatement();
993 ps << "mem " << PPExtString(legalName) << " :";
994 emitLocationAndNewLine(op);
995 ps.scopedBox(PP::bbox2, [&]() {
996 startStatement();
997 ps << "data-type => ";
998 emitType(op.getDataType());
999 ps << PP::newline;
1000 ps << "depth => ";
1001 ps.addAsString(op.getDepth());
1002 ps << PP::newline;
1003 ps << "read-latency => ";
1004 ps.addAsString(op.getReadLatency());
1005 ps << PP::newline;
1006 ps << "write-latency => ";
1007 ps.addAsString(op.getWriteLatency());
1008 ps << PP::newline;
1009
1010 SmallString<16> reader, writer, readwriter;
1011 for (std::pair<StringAttr, MemOp::PortKind> port : op.getPorts()) {
1012 auto add = [&](SmallString<16> &to, StringAttr name) {
1013 if (!to.empty())
1014 to.push_back(' ');
1015 to.append(name.getValue());
1016 };
1017 switch (port.second) {
1018 case MemOp::PortKind::Read:
1019 add(reader, legalize(port.first));
1020 break;
1021 case MemOp::PortKind::Write:
1022 add(writer, legalize(port.first));
1023 break;
1024 case MemOp::PortKind::ReadWrite:
1025 add(readwriter, legalize(port.first));
1026 break;
1027 case MemOp::PortKind::Debug:
1028 emitOpError(op, "has unsupported 'debug' port");
1029 return;
1030 }
1031 }
1032 if (!reader.empty())
1033 ps << "reader => " << reader << PP::newline;
1034 if (!writer.empty())
1035 ps << "writer => " << writer << PP::newline;
1036 if (!readwriter.empty())
1037 ps << "readwriter => " << readwriter << PP::newline;
1038
1039 ps << "read-under-write => ";
1040 emitAttribute(op.getRuw());
1041 setPendingNewline();
1042 });
1043}
1044
1045void Emitter::emitStatement(SeqMemOp op) {
1046 startStatement();
1047 ps.scopedBox(PP::ibox2, [&]() {
1048 ps << "smem " << PPExtString(legalize(op.getNameAttr()));
1049 emitTypeWithColon(op.getType());
1050 ps << "," << PP::space;
1051 emitAttribute(op.getRuw());
1052 });
1053 emitLocationAndNewLine(op);
1054}
1055
1056void Emitter::emitStatement(CombMemOp op) {
1057 startStatement();
1058 ps.scopedBox(PP::ibox2, [&]() {
1059 ps << "cmem " << PPExtString(legalize(op.getNameAttr()));
1060 emitTypeWithColon(op.getType());
1061 });
1062 emitLocationAndNewLine(op);
1063}
1064
1065void Emitter::emitStatement(MemoryPortOp op) {
1066 // Nothing to output for this operation.
1067 addValueName(op.getData(), legalize(op.getNameAttr()));
1068}
1069
1070void Emitter::emitStatement(MemoryDebugPortOp op) {
1071 // Nothing to output for this operation.
1072 addValueName(op.getData(), legalize(op.getNameAttr()));
1073}
1074
1075void Emitter::emitStatement(MemoryPortAccessOp op) {
1076 startStatement();
1077
1078 // Print the port direction and name.
1079 auto port = cast<MemoryPortOp>(op.getPort().getDefiningOp());
1080 emitAttribute(port.getDirection());
1081 // TODO: emitAssignLike
1082 ps << " mport " << PPExtString(legalize(port.getNameAttr())) << " = ";
1083
1084 // Print the memory name.
1085 auto *mem = port.getMemory().getDefiningOp();
1086 if (auto seqMem = dyn_cast<SeqMemOp>(mem))
1087 ps << legalize(seqMem.getNameAttr());
1088 else
1089 ps << legalize(cast<CombMemOp>(mem).getNameAttr());
1090
1091 // Print the address.
1092 ps << "[";
1093 emitExpression(op.getIndex());
1094 ps << "], ";
1095
1096 // Print the clock.
1097 emitExpression(op.getClock());
1098
1099 emitLocationAndNewLine(op);
1100}
1101
1102void Emitter::emitStatement(RefDefineOp op) {
1103 startStatement();
1104 emitAssignLike([&]() { emitExpression(op.getDest()); },
1105 [&]() { emitExpression(op.getSrc()); }, PPExtString("="),
1106 PPExtString("define"));
1107 emitLocationAndNewLine(op);
1108}
1109
1110void Emitter::emitStatement(RefForceOp op) {
1111 emitStatementFunctionOp(PPExtString("force"), op);
1112}
1113
1114void Emitter::emitStatement(RefForceInitialOp op) {
1115 startStatement();
1116 auto constantPredicate =
1117 dyn_cast_or_null<ConstantOp>(op.getPredicate().getDefiningOp());
1118 bool hasEnable = !constantPredicate || constantPredicate.getValue() == 0;
1119 if (hasEnable) {
1120 ps << "when ";
1121 emitExpression(op.getPredicate());
1122 ps << ":" << PP::bbox2 << PP::neverbreak << PP::newline;
1123 }
1124 ps << "force_initial(";
1125 ps.scopedBox(PP::ibox0, [&]() {
1126 interleaveComma({op.getDest(), op.getSrc()});
1127 ps << ")";
1128 });
1129 if (hasEnable)
1130 ps << PP::end;
1131 emitLocationAndNewLine(op);
1132}
1133
1134void Emitter::emitStatement(RefReleaseOp op) {
1135 emitStatementFunctionOp(PPExtString("release"), op);
1136}
1137
1138void Emitter::emitStatement(RefReleaseInitialOp op) {
1139 startStatement();
1140 auto constantPredicate =
1141 dyn_cast_or_null<ConstantOp>(op.getPredicate().getDefiningOp());
1142 bool hasEnable = !constantPredicate || constantPredicate.getValue() == 0;
1143 if (hasEnable) {
1144 ps << "when ";
1145 emitExpression(op.getPredicate());
1146 ps << ":" << PP::bbox2 << PP::neverbreak << PP::newline;
1147 }
1148 ps << "release_initial(";
1149 emitExpression(op.getDest());
1150 ps << ")";
1151 if (hasEnable)
1152 ps << PP::end;
1153 emitLocationAndNewLine(op);
1154}
1155
1156void Emitter::emitStatement(LayerBlockOp op) {
1157 startStatement();
1158 ps << "layerblock " << op.getLayerName().getLeafReference() << " :";
1159 emitLocationAndNewLine(op);
1160 auto *body = op.getBody();
1161 ps.scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(*body); });
1162}
1163
1164void Emitter::emitStatement(InvalidValueOp op) {
1165 // Only emit this invalid value if it is used somewhere else than the RHS of
1166 // a connect.
1167 if (llvm::all_of(op->getUses(), [&](OpOperand &use) {
1168 return use.getOperandNumber() == 1 &&
1169 isa<ConnectOp, MatchingConnectOp>(use.getOwner());
1170 }))
1171 return;
1172
1173 // TODO: emitAssignLike ?
1174 startStatement();
1175 auto name = circuitNamespace.newName("_invalid");
1176 addValueName(op, name);
1177 ps << "wire " << PPExtString(name) << " : ";
1178 emitType(op.getType());
1179 emitLocationAndNewLine(op);
1180 startStatement();
1181 if (FIRVersion(3, 0, 0) <= version)
1182 ps << "invalidate " << PPExtString(name);
1183 else
1184 ps << PPExtString(name) << " is invalid";
1185 emitLocationAndNewLine(op);
1186}
1187
1188void Emitter::emitStatement(GenericIntrinsicOp op) {
1189 startStatement();
1190 if (op.use_empty())
1191 emitGenericIntrinsic(op);
1192 else {
1193 assert(!isEmittedInline(op));
1194 auto name = circuitNamespace.newName("_gen_int");
1195 addValueName(op.getResult(), name);
1196 emitAssignLike([&]() { ps << "node " << PPExtString(name); },
1197 [&]() { emitGenericIntrinsic(op); });
1198 }
1199 emitLocationAndNewLine(op);
1200}
1201
1202void Emitter::emitExpression(Value value) {
1203 // Handle the trivial case where we already have a name for this value which
1204 // we can use.
1205 if (auto name = lookupEmittedName(value)) {
1206 // Don't use PPExtString here, can't trust valueNames storage, cleared.
1207 ps << *name;
1208 return;
1209 }
1210
1211 auto op = value.getDefiningOp();
1212 assert(op && "value must either be a block arg or the result of an op");
1213 TypeSwitch<Operation *>(op)
1214 .Case<
1215 // Basic expressions
1216 ConstantOp, SpecialConstantOp, SubfieldOp, SubindexOp, SubaccessOp,
1217 OpenSubfieldOp, OpenSubindexOp,
1218 // Binary
1219 AddPrimOp, SubPrimOp, MulPrimOp, DivPrimOp, RemPrimOp, AndPrimOp,
1220 OrPrimOp, XorPrimOp, LEQPrimOp, LTPrimOp, GEQPrimOp, GTPrimOp,
1221 EQPrimOp, NEQPrimOp, CatPrimOp, DShlPrimOp, DShlwPrimOp, DShrPrimOp,
1222 // Unary
1223 AsSIntPrimOp, AsUIntPrimOp, AsAsyncResetPrimOp, AsClockPrimOp,
1224 CvtPrimOp, NegPrimOp, NotPrimOp, AndRPrimOp, OrRPrimOp, XorRPrimOp,
1225 // Miscellaneous
1226 BitsPrimOp, HeadPrimOp, TailPrimOp, PadPrimOp, MuxPrimOp, ShlPrimOp,
1227 ShrPrimOp, UninferredResetCastOp, ConstCastOp, StringConstantOp,
1228 FIntegerConstantOp, BoolConstantOp, DoubleConstantOp, ListCreateOp,
1229 UnresolvedPathOp, GenericIntrinsicOp,
1230 // Reference expressions
1231 RefSendOp, RefResolveOp, RefSubOp, RWProbeOp, RefCastOp>(
1232 [&](auto op) {
1233 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op); });
1234 })
1235 .Default([&](auto op) {
1236 emitOpError(op, "not supported as expression");
1237 ps << "<unsupported-expr-" << PPExtString(op->getName().stripDialect())
1238 << ">";
1239 });
1240}
1241
1242void Emitter::emitExpression(ConstantOp op) {
1243 // Don't include 'const' on the type in a literal expression
1244 emitType(op.getType(), false);
1245 // TODO: Add option to control base-2/8/10/16 output here.
1246 ps << "(";
1247 ps.addAsString(op.getValue());
1248 ps << ")";
1249}
1250
1251void Emitter::emitExpression(SpecialConstantOp op) {
1252 auto emitInner = [&]() {
1253 ps << "UInt<1>(";
1254 ps.addAsString(op.getValue());
1255 ps << ")";
1256 };
1257 // TODO: Emit type decl for type alias.
1258 FIRRTLTypeSwitch<FIRRTLType>(type_cast<FIRRTLType>(op.getType()))
1259 .Case<ClockType>([&](auto type) {
1260 ps << "asClock(";
1261 emitInner();
1262 ps << ")";
1263 })
1264 .Case<ResetType>([&](auto type) { emitInner(); })
1265 .Case<AsyncResetType>([&](auto type) {
1266 ps << "asAsyncReset(";
1267 emitInner();
1268 ps << ")";
1269 });
1270}
1271
1272// NOLINTNEXTLINE(misc-no-recursion)
1273void Emitter::emitExpression(SubfieldOp op) {
1274 BundleType type = op.getInput().getType();
1275 emitExpression(op.getInput());
1276 ps << "." << legalize(type.getElementNameAttr(op.getFieldIndex()));
1277}
1278
1279// NOLINTNEXTLINE(misc-no-recursion)
1280void Emitter::emitExpression(SubindexOp op) {
1281 emitExpression(op.getInput());
1282 ps << "[";
1283 ps.addAsString(op.getIndex());
1284 ps << "]";
1285}
1286
1287// NOLINTNEXTLINE(misc-no-recursion)
1288void Emitter::emitExpression(SubaccessOp op) {
1289 emitExpression(op.getInput());
1290 ps << "[";
1291 emitExpression(op.getIndex());
1292 ps << "]";
1293}
1294
1295void Emitter::emitExpression(OpenSubfieldOp op) {
1296 auto type = op.getInput().getType();
1297 emitExpression(op.getInput());
1298 ps << "." << legalize(type.getElementNameAttr(op.getFieldIndex()));
1299}
1300
1301void Emitter::emitExpression(OpenSubindexOp op) {
1302 emitExpression(op.getInput());
1303 ps << "[";
1304 ps.addAsString(op.getIndex());
1305 ps << "]";
1306}
1307
1308void Emitter::emitExpression(RefSendOp op) {
1309 ps << "probe(";
1310 emitExpression(op.getBase());
1311 ps << ")";
1312}
1313
1314void Emitter::emitExpression(RefResolveOp op) {
1315 ps << "read(";
1316 emitExpression(op.getRef());
1317 ps << ")";
1318}
1319
1320void Emitter::emitExpression(RefSubOp op) {
1321 emitExpression(op.getInput());
1322 FIRRTLTypeSwitch<FIRRTLBaseType, void>(op.getInput().getType().getType())
1323 .Case<FVectorType>([&](auto type) {
1324 ps << "[";
1325 ps.addAsString(op.getIndex());
1326 ps << "]";
1327 })
1328 .Case<BundleType>(
1329 [&](auto type) { ps << "." << type.getElementName(op.getIndex()); });
1330}
1331
1332void Emitter::emitExpression(RWProbeOp op) {
1333 ps << "rwprobe(";
1334
1335 // Find the probe target.
1336 auto target = symInfos->get().irn.lookup(op.getTarget());
1337 Value base;
1338 if (target.isPort()) {
1339 auto mod = cast<FModuleOp>(target.getOp());
1340 auto port = target.getPort();
1341 base = mod.getArgument(port);
1342 } else
1343 base = cast<hw::InnerSymbolOpInterface>(target.getOp()).getTargetResult();
1344
1345 // Print target. Needs this to have a name already.
1346 emitExpression(base);
1347
1348 // Print indexing for the target field.
1349 auto fieldID = target.getField();
1350 auto type = base.getType();
1351 while (fieldID) {
1353 .Case<FVectorType, OpenVectorType>([&](auto vecTy) {
1354 auto index = vecTy.getIndexForFieldID(fieldID);
1355 ps << "[";
1356 ps.addAsString(index);
1357 ps << "]";
1358 auto [subtype, subfieldID] = vecTy.getSubTypeByFieldID(fieldID);
1359 type = subtype;
1360 fieldID = subfieldID;
1361 })
1362 .Case<BundleType, OpenBundleType>([&](auto bundleTy) {
1363 auto index = bundleTy.getIndexForFieldID(fieldID);
1364 ps << "." << bundleTy.getElementName(index);
1365 auto [subtype, subfieldID] = bundleTy.getSubTypeByFieldID(fieldID);
1366 type = subtype;
1367 fieldID = subfieldID;
1368 });
1369 }
1370 ps << ")";
1371}
1372
1373void Emitter::emitExpression(RefCastOp op) { emitExpression(op.getInput()); }
1374
1375void Emitter::emitExpression(UninferredResetCastOp op) {
1376 emitExpression(op.getInput());
1377}
1378
1379void Emitter::emitExpression(FIntegerConstantOp op) {
1380 ps << "Integer(";
1381 ps.addAsString(op.getValue());
1382 ps << ")";
1383}
1384
1385void Emitter::emitExpression(BoolConstantOp op) {
1386 ps << "Bool(" << (op.getValue() ? "true" : "false") << ")";
1387}
1388
1389void Emitter::emitExpression(DoubleConstantOp op) {
1390 ps << "Double(";
1391 // Use APFloat::toString.
1392 // Printing as double is not what we want,
1393 // and this at least handles the basic cases in a way
1394 // that will round-trip.
1395 SmallString<16> str;
1396 op.getValueAttr().getValue().toString(str);
1397 ps << str;
1398 ps << ")";
1399}
1400
1401void Emitter::emitExpression(StringConstantOp op) {
1402 ps << "String(";
1403 ps.writeQuotedEscaped(op.getValue());
1404 ps << ")";
1405}
1406
1407void Emitter::emitExpression(ListCreateOp op) {
1408 return emitLiteralExpression(op.getType(), op.getElements());
1409}
1410
1411void Emitter::emitExpression(UnresolvedPathOp op) {
1412 ps << "path(";
1413 ps.writeQuotedEscaped(op.getTarget());
1414 ps << ")";
1415}
1416
1417void Emitter::emitExpression(GenericIntrinsicOp op) {
1418 emitGenericIntrinsic(op);
1419}
1420
1421void Emitter::emitExpression(ConstCastOp op) { emitExpression(op.getInput()); }
1422
1423void Emitter::emitPrimExpr(StringRef mnemonic, Operation *op,
1424 ArrayRef<uint32_t> attrs) {
1425 ps << mnemonic << "(" << PP::ibox0;
1426 interleaveComma(op->getOperands());
1427 if (!op->getOperands().empty() && !attrs.empty())
1428 ps << "," << PP::space;
1429 interleaveComma(attrs, [&](auto attr) { ps.addAsString(attr); });
1430 ps << ")" << PP::end;
1431}
1432
1433void Emitter::emitAttribute(MemDirAttr attr) {
1434 switch (attr) {
1435 case MemDirAttr::Infer:
1436 ps << "infer";
1437 break;
1438 case MemDirAttr::Read:
1439 ps << "read";
1440 break;
1441 case MemDirAttr::Write:
1442 ps << "write";
1443 break;
1444 case MemDirAttr::ReadWrite:
1445 ps << "rdwr";
1446 break;
1447 }
1448}
1449
1450void Emitter::emitAttribute(RUWAttr attr) {
1451 switch (attr) {
1452 case RUWAttr::Undefined:
1453 ps << "undefined";
1454 break;
1455 case RUWAttr::Old:
1456 ps << "old";
1457 break;
1458 case RUWAttr::New:
1459 ps << "new";
1460 break;
1461 }
1462}
1463
1464/// Emit a FIRRTL type into the output.
1465void Emitter::emitType(Type type, bool includeConst) {
1466 if (includeConst && isConst(type))
1467 ps << "const ";
1468 auto emitWidth = [&](std::optional<int32_t> width) {
1469 if (width) {
1470 ps << "<";
1471 ps.addAsString(*width);
1472 ps << ">";
1473 }
1474 };
1475 // TODO: Emit type decl for type alias.
1477 .Case<ClockType>([&](auto) { ps << "Clock"; })
1478 .Case<ResetType>([&](auto) { ps << "Reset"; })
1479 .Case<AsyncResetType>([&](auto) { ps << "AsyncReset"; })
1480 .Case<UIntType>([&](auto type) {
1481 ps << "UInt";
1482 emitWidth(type.getWidth());
1483 })
1484 .Case<SIntType>([&](auto type) {
1485 ps << "SInt";
1486 emitWidth(type.getWidth());
1487 })
1488 .Case<AnalogType>([&](auto type) {
1489 ps << "Analog";
1490 emitWidth(type.getWidth());
1491 })
1492 .Case<OpenBundleType, BundleType>([&](auto type) {
1493 ps << "{";
1494 if (!type.getElements().empty())
1495 ps << PP::nbsp;
1496 bool anyEmitted = false;
1497 ps.scopedBox(PP::cbox0, [&]() {
1498 for (auto &element : type.getElements()) {
1499 if (anyEmitted)
1500 ps << "," << PP::space;
1501 ps.scopedBox(PP::ibox2, [&]() {
1502 if (element.isFlip)
1503 ps << "flip ";
1504 ps << legalize(element.name);
1505 emitTypeWithColon(element.type);
1506 anyEmitted = true;
1507 });
1508 }
1509 if (anyEmitted)
1510 ps << PP::nbsp;
1511 ps << "}";
1512 });
1513 })
1514 .Case<OpenVectorType, FVectorType, CMemoryType>([&](auto type) {
1515 emitType(type.getElementType());
1516 ps << "[";
1517 ps.addAsString(type.getNumElements());
1518 ps << "]";
1519 })
1520 .Case<RefType>([&](RefType type) {
1521 if (type.getForceable())
1522 ps << "RW";
1523 ps << "Probe<";
1524 ps.cbox(2, IndentStyle::Block);
1525 ps.zerobreak();
1526 emitType(type.getType());
1527 if (auto layer = type.getLayer()) {
1528 ps << ",";
1529 ps.space();
1530 emitSymbol(type.getLayer());
1531 }
1532 ps << BreakToken(0, -2) << ">";
1533 ps.end();
1534 })
1535 .Case<AnyRefType>([&](AnyRefType type) { ps << "AnyRef"; })
1536 .Case<StringType>([&](StringType type) { ps << "String"; })
1537 .Case<FIntegerType>([&](FIntegerType type) { ps << "Integer"; })
1538 .Case<BoolType>([&](BoolType type) { ps << "Bool"; })
1539 .Case<DoubleType>([&](DoubleType type) { ps << "Double"; })
1540 .Case<PathType>([&](PathType type) { ps << "Path"; })
1541 .Case<ListType>([&](ListType type) {
1542 ps << "List<";
1543 emitType(type.getElementType());
1544 ps << ">";
1545 })
1546 .Default([&](auto type) {
1547 llvm_unreachable("all types should be implemented");
1548 });
1549}
1550
1551/// Emit a location as `@[<filename> <line>:<column>]` annotation, including a
1552/// leading space.
1553void Emitter::emitLocation(Location loc) {
1554 // TODO: Handle FusedLoc and uniquify locations, avoid repeated file names.
1555 ps << PP::neverbreak;
1556 if (auto fileLoc =
1557 dyn_cast_or_null<FileLineColLoc, LocationAttr>(LocationAttr(loc))) {
1558 ps << " @[" << fileLoc.getFilename().getValue();
1559 if (auto line = fileLoc.getLine()) {
1560 ps << " ";
1561 ps.addAsString(line);
1562 if (auto col = fileLoc.getColumn()) {
1563 ps << ":";
1564 ps.addAsString(col);
1565 }
1566 }
1567 ps << "]";
1568 }
1569}
1570// NOLINTEND(misc-no-recursion)
1571
1572//===----------------------------------------------------------------------===//
1573// Driver
1574//===----------------------------------------------------------------------===//
1575
1576// Emit the specified FIRRTL circuit into the given output stream.
1577mlir::LogicalResult
1578circt::firrtl::exportFIRFile(mlir::ModuleOp module, llvm::raw_ostream &os,
1579 std::optional<size_t> targetLineLength,
1580 FIRVersion version) {
1581 Emitter emitter(os, version,
1582 targetLineLength.value_or(defaultTargetLineLength));
1583 for (auto &op : *module.getBody()) {
1584 if (auto circuitOp = dyn_cast<CircuitOp>(op))
1585 emitter.emitCircuit(circuitOp);
1586 }
1587 return emitter.finalize();
1588}
1589
1591 static llvm::cl::opt<size_t> targetLineLength(
1592 "target-line-length",
1593 llvm::cl::desc("Target line length for emitted .fir"),
1594 llvm::cl::value_desc("number of chars"),
1595 llvm::cl::init(defaultTargetLineLength));
1596 static mlir::TranslateFromMLIRRegistration toFIR(
1597 "export-firrtl", "emit FIRRTL dialect operations to .fir output",
1598 [](ModuleOp module, llvm::raw_ostream &os) {
1599 return exportFIRFile(module, os, targetLineLength, exportFIRVersion);
1600 },
1601 [](mlir::DialectRegistry &registry) {
1602 registry.insert<chirrtl::CHIRRTLDialect>();
1603 registry.insert<firrtl::FIRRTLDialect>();
1604 });
1605}
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.
#define isdigit(x)
Definition FIRLexer.cpp:26
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.
Definition FIRParser.h:139
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.
Definition Namespace.h:24
The FIRRTL specification version.
Definition FIRParser.h:88
This class represents the namespace in which InnerRef's can be resolved.
String wrapper to indicate string has external storage.