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