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