CIRCT 24.0.0git
Loading...
Searching...
No Matches
FIREmitter.cpp
Go to the documentation of this file.
1//===- FIREmitter.cpp - FIRRTL dialect to .fir emitter --------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This implements a .fir file emitter.
10//
11//===----------------------------------------------------------------------===//
12
19#include "circt/Support/LLVM.h"
21#include "mlir/IR/BuiltinOps.h"
22#include "mlir/Tools/mlir-translate/Translation.h"
23#include "llvm/ADT/APSInt.h"
24#include "llvm/ADT/StringSet.h"
25#include "llvm/ADT/TypeSwitch.h"
26#include "llvm/Support/Debug.h"
27
28#define DEBUG_TYPE "export-firrtl"
29
30using namespace circt;
31using namespace firrtl;
32using namespace chirrtl;
33using namespace pretty;
34
35//===----------------------------------------------------------------------===//
36// Emitter
37//===----------------------------------------------------------------------===//
38
39// NOLINTBEGIN(misc-no-recursion)
40namespace {
41
42constexpr size_t defaultTargetLineLength = 80;
43
44/// An emitter for FIRRTL dialect operations to .fir output.
45struct Emitter {
46 Emitter(llvm::raw_ostream &os, FIRVersion version,
47 size_t targetLineLength = defaultTargetLineLength)
48 : pp(os, targetLineLength), ps(pp, saver), version(version) {
49 pp.setListener(&saver);
50 }
51 LogicalResult finalize();
52
53 // Circuit/module emission
54 void emitCircuit(CircuitOp op);
55 void emitModule(FModuleOp op);
56 void emitModule(FExtModuleOp op);
57 void emitModule(FIntModuleOp op);
58 void emitModulePorts(ArrayRef<PortInfo> ports,
59 Block::BlockArgListType arguments = {});
60 void emitModuleParameters(Operation *op, ArrayAttr parameters);
61 void emitDeclaration(DomainOp op);
62 void emitDeclaration(LayerOp op);
63 void emitDeclaration(OptionOp op);
64 void emitDeclaration(FormalOp op);
65 void emitDeclaration(SimulationOp op);
66 void emitFormalLike(Operation *op, StringRef keyword, StringAttr symName,
67 StringAttr moduleName, DictionaryAttr params);
68 void emitEnabledLayers(ArrayRef<Attribute> layers, Operation *op);
69 void emitKnownLayers(ArrayRef<Attribute> layers, Operation *op);
70 void emitRequirements(ArrayRef<Attribute> requirements);
71 void emitParamAssign(ParamDeclAttr param, Operation *op,
72 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
73 void emitParamValue(Attribute value, Operation *op);
74
75 void emitGenericIntrinsic(GenericIntrinsicOp op);
76
77 // Statement emission
78 void emitStatementsInBlock(Block &block);
79 void emitStatement(WhenOp op);
80 void emitStatement(WireOp op);
81 void emitStatement(RegOp op);
82 void emitStatement(RegResetOp op);
83 void emitStatement(NodeOp op);
84 void emitStatement(StopOp op);
85 void emitStatement(SkipOp op);
86 void emitFormatString(Operation *op, StringRef formatString, OperandRange ops,
87 llvm::SmallVectorImpl<Value> &substitutions);
88 template <class T>
89 void emitPrintfLike(T op, StringAttr fileName);
90 void emitStatement(PrintFOp op);
91 void emitStatement(FPrintFOp op);
92 void emitStatement(FFlushOp op);
93 void emitStatement(ConnectOp op);
94 void emitStatement(MatchingConnectOp op);
95 void emitStatement(PropertyAssertOp op);
96 void emitStatement(PropAssignOp op);
97 void emitStatement(InstanceOp op);
98 void emitStatement(InstanceChoiceOp op);
99 void emitStatement(AttachOp op);
100 void emitStatement(MemOp op);
101 void emitStatement(InvalidValueOp op);
102 void emitStatement(CombMemOp op);
103 void emitStatement(SeqMemOp op);
104 void emitStatement(MemoryPortOp op);
105 void emitStatement(MemoryDebugPortOp op);
106 void emitStatement(MemoryPortAccessOp op);
107 void emitStatement(DomainDefineOp op);
108 void emitStatement(RefDefineOp op);
109 void emitStatement(RefForceOp op);
110 void emitStatement(RefForceInitialOp op);
111 void emitStatement(RefReleaseOp op);
112 void emitStatement(RefReleaseInitialOp op);
113 void emitStatement(LayerBlockOp op);
114 void emitStatement(GenericIntrinsicOp op);
115 void emitStatement(DomainCreateAnonOp op);
116 void emitStatement(DomainCreateOp op);
117
118 template <class T>
119 void emitVerifStatement(T op, StringRef mnemonic);
120 void emitStatement(AssertOp op) { emitVerifStatement(op, "assert"); }
121 void emitStatement(AssumeOp op) { emitVerifStatement(op, "assume"); }
122 void emitStatement(CoverOp op) { emitVerifStatement(op, "cover"); }
123
124 // Exprsesion emission
125 void emitExpression(Value value);
126 void emitExpression(ConstantOp op);
127 void emitExpression(SpecialConstantOp op);
128 void emitExpression(SubfieldOp op);
129 void emitExpression(SubindexOp op);
130 void emitExpression(SubaccessOp op);
131 void emitExpression(OpenSubfieldOp op);
132 void emitExpression(DomainSubfieldOp op);
133 void emitExpression(OpenSubindexOp op);
134 void emitExpression(RefResolveOp op);
135 void emitExpression(RefSendOp op);
136 void emitExpression(RefSubOp op);
137 void emitExpression(RWProbeOp op);
138 void emitExpression(RefCastOp op);
139 void emitExpression(UninferredResetCastOp op);
140 void emitExpression(ConstCastOp op);
141 void emitExpression(StringConstantOp op);
142 void emitExpression(FIntegerConstantOp op);
143 void emitExpression(BoolConstantOp op);
144 void emitExpression(DoubleConstantOp op);
145 void emitExpression(ListCreateOp op);
146 void emitExpression(UnresolvedPathOp op);
147 void emitExpression(GenericIntrinsicOp op);
148 void emitExpression(CatPrimOp op);
149 void emitExpression(UnsafeDomainCastOp op);
150 void emitExpression(UnknownValueOp op);
151
152 void emitPrimExpr(StringRef mnemonic, Operation *op,
153 ArrayRef<uint32_t> attrs = {});
154
155 void emitExpression(BitsPrimOp op) {
156 emitPrimExpr("bits", op, {op.getHi(), op.getLo()});
157 }
158 void emitExpression(HeadPrimOp op) {
159 emitPrimExpr("head", op, op.getAmount());
160 }
161 void emitExpression(TailPrimOp op) {
162 emitPrimExpr("tail", op, op.getAmount());
163 }
164 void emitExpression(PadPrimOp op) { emitPrimExpr("pad", op, op.getAmount()); }
165 void emitExpression(ShlPrimOp op) { emitPrimExpr("shl", op, op.getAmount()); }
166 void emitExpression(ShrPrimOp op) { emitPrimExpr("shr", op, op.getAmount()); }
167
168 void emitExpression(TimeOp op) {}
169
170 // Funnel all ops without attrs into `emitPrimExpr`.
171#define HANDLE(OPTYPE, MNEMONIC) \
172 void emitExpression(OPTYPE op) { emitPrimExpr(MNEMONIC, op); }
173 HANDLE(AddPrimOp, "add");
174 HANDLE(SubPrimOp, "sub");
175 HANDLE(MulPrimOp, "mul");
176 HANDLE(DivPrimOp, "div");
177 HANDLE(RemPrimOp, "rem");
178 HANDLE(AndPrimOp, "and");
179 HANDLE(OrPrimOp, "or");
180 HANDLE(XorPrimOp, "xor");
181 HANDLE(LEQPrimOp, "leq");
182 HANDLE(LTPrimOp, "lt");
183 HANDLE(GEQPrimOp, "geq");
184 HANDLE(GTPrimOp, "gt");
185 HANDLE(EQPrimOp, "eq");
186 HANDLE(NEQPrimOp, "neq");
187 HANDLE(DShlPrimOp, "dshl");
188 HANDLE(DShlwPrimOp, "dshlw");
189 HANDLE(DShrPrimOp, "dshr");
190 HANDLE(MuxPrimOp, "mux");
191 HANDLE(AsSIntPrimOp, "asSInt");
192 HANDLE(AsUIntPrimOp, "asUInt");
193 HANDLE(AsAsyncResetPrimOp, "asAsyncReset");
194 HANDLE(AsResetPrimOp, "asReset");
195 HANDLE(AsClockPrimOp, "asClock");
196 HANDLE(CvtPrimOp, "cvt");
197 HANDLE(NegPrimOp, "neg");
198 HANDLE(NotPrimOp, "not");
199 HANDLE(AndRPrimOp, "andr");
200 HANDLE(OrRPrimOp, "orr");
201 HANDLE(XorRPrimOp, "xorr");
202 HANDLE(StringConcatOp, "string_concat");
203#undef HANDLE
204
205 void emitExpression(PropEqOp op) {
206 if (failed(requireVersion({6, 0, 0}, op, "property equality")))
207 return;
208 emitPrimExpr("prop_eq", op);
209 }
210
211 void emitExpression(BoolAndOp op) {
212 if (failed(requireVersion({6, 0, 0}, op, "boolean and")))
213 return;
214 emitPrimExpr("bool_and", op);
215 }
216
217 void emitExpression(BoolOrOp op) {
218 if (failed(requireVersion({6, 0, 0}, op, "boolean or")))
219 return;
220 emitPrimExpr("bool_or", op);
221 }
222
223 void emitExpression(BoolXorOp op) {
224 if (failed(requireVersion({6, 0, 0}, op, "boolean xor")))
225 return;
226 emitPrimExpr("bool_xor", op);
227 }
228
229 // Attributes
230 void emitAttribute(MemDirAttr attr);
231 void emitAttribute(RUWBehaviorAttr attr);
232
233 // Types
234 void emitType(Type type, bool includeConst = true);
235 void emitTypeWithColon(Type type) {
236 ps << PP::space << ":" << PP::nbsp;
237 emitType(type);
238 }
239
240 // Domains
241 void emitDomains(Attribute attr, ArrayRef<PortInfo> ports);
242
243 // Locations
244 void emitLocation(Location loc);
245 void emitLocation(Operation *op) { emitLocation(op->getLoc()); }
246 template <typename... Args>
247 void emitLocationAndNewLine(Args... args) {
248 // Break so previous content is not impacted by following,
249 // but use a 'neverbreak' so it always fits.
250 ps << PP::neverbreak;
251 emitLocation(args...);
252 setPendingNewline();
253 }
254
255 void emitAssignLike(llvm::function_ref<void()> emitLHS,
256 llvm::function_ref<void()> emitRHS,
257 PPExtString syntax = PPExtString("="),
258 std::optional<PPExtString> wordBeforeLHS = std::nullopt) {
259 // If wraps, indent.
260 ps.scopedBox(PP::ibox2, [&]() {
261 if (wordBeforeLHS) {
262 ps << *wordBeforeLHS << PP::space;
263 }
264 emitLHS();
265 // Allow breaking before 'syntax' (e.g., '=') if long assignment.
266 ps << PP::space << syntax << PP::nbsp; /* PP::space; */
267 // RHS is boxed to right of the syntax.
268 ps.scopedBox(PP::ibox0, [&]() { emitRHS(); });
269 });
270 }
271
272 /// Emit the specified value as a subexpression, wrapping in an ibox2.
273 void emitSubExprIBox2(Value v) {
274 ps.scopedBox(PP::ibox2, [&]() { emitExpression(v); });
275 }
276
277 /// Emit a range of values separated by commas and a breakable space.
278 /// Each value is emitted by invoking `eachFn`.
279 template <typename Container, typename EachFn>
280 void interleaveComma(const Container &c, EachFn eachFn) {
281 llvm::interleave(c, eachFn, [&]() { ps << "," << PP::space; });
282 }
283
284 /// Emit a range of values separated by commas and a breakable space.
285 /// Each value is emitted in an ibox2.
286 void interleaveComma(ValueRange ops) {
287 return interleaveComma(ops, [&](Value v) { emitSubExprIBox2(v); });
288 }
289
290 void emitStatementFunctionOp(PPExtString name, Operation *op) {
291 startStatement();
292 ps << name << "(";
293 ps.scopedBox(PP::ibox0, [&]() {
294 interleaveComma(op->getOperands());
295 ps << ")";
296 });
297 emitLocationAndNewLine(op);
298 }
299
300 template <typename EachFn, typename Range>
301 void emitLiteralExpression(Type type, const Range &r, EachFn eachFn) {
302 emitType(type);
303 ps << "(";
304 ps.scopedBox(PP::ibox0, [&]() {
305 interleaveComma(r, eachFn);
306 ps << ")";
307 });
308 }
309
310 void emitLiteralExpression(Type type, ValueRange values) {
311 return emitLiteralExpression(type, values,
312 [&](Value v) { emitSubExprIBox2(v); });
313 }
314
315 /// Emit a (potentially nested) symbol reference as `A.B.C`.
316 void emitSymbol(SymbolRefAttr symbol) {
317 ps.ibox(2, IndentStyle::Block);
318 ps << symbol.getRootReference();
319 for (auto nested : symbol.getNestedReferences()) {
320 ps.zerobreak();
321 ps << ".";
322 ps << nested.getAttr();
323 }
324 ps.end();
325 }
326
327private:
328 /// Emit an error and remark that emission failed.
329 InFlightDiagnostic emitError(Operation *op, const Twine &message) {
330 encounteredError = true;
331 return op->emitError(message);
332 }
333
334 /// Emit an error and remark that emission failed.
335 InFlightDiagnostic emitOpError(Operation *op, const Twine &message) {
336 encounteredError = true;
337 return op->emitOpError(message);
338 }
339
340 /// Return false and emit an error if the target version is below minVersion.
341 LogicalResult requireVersion(FIRVersion minVersion, Operation *op,
342 Twine feature) {
343 if (version >= minVersion)
344 return success();
345
346 return emitOpError(op, feature + " requires FIRRTL ") << minVersion;
347 }
348
349 /// Return the name used during emission of a `Value`, or none if the value
350 /// has not yet been emitted or it was emitted inline.
351 std::optional<StringRef> lookupEmittedName(Value value) {
352 auto it = valueNames.find(value);
353 if (it != valueNames.end())
354 return {it->second};
355 return {};
356 }
357
358 /// If previous emission requires a newline, emit it now.
359 /// This gives us opportunity to open/close boxes before linebreak.
360 void emitPendingNewlineIfNeeded() {
361 if (pendingNewline) {
362 pendingNewline = false;
363 ps << PP::newline;
364 }
365 }
366 void setPendingNewline() {
367 assert(!pendingNewline);
368 pendingNewline = true;
369 }
370
371 void startStatement() { emitPendingNewlineIfNeeded(); }
372
373private:
374 /// String storage backing Tokens built from temporary strings.
375 /// PrettyPrinter will clear this as appropriate.
376 TokenStringSaver saver;
377
378 /// Pretty printer.
379 PrettyPrinter pp;
380
381 /// Stream helper (pp, saver).
382 TokenStream<> ps;
383
384 /// Whether a newline is expected, emitted late to provide opportunity to
385 /// open/close boxes we don't know we need at level of individual statement.
386 /// Every statement should set this instead of directly emitting (last)
387 /// newline. Most statements end with emitLocationInfoAndNewLine which handles
388 /// this.
389 bool pendingNewline = false;
390
391 /// Whether we have encountered any errors during emission.
392 bool encounteredError = false;
393
394 /// The names used to emit values already encountered. Anything that gets a
395 /// name in the output FIR is listed here, such that future expressions can
396 /// reference it.
397 DenseMap<Value, StringRef> valueNames;
398 StringSet<> valueNamesStorage;
399
400 /// Legalize names for emission. Convert names which begin with a number to
401 /// be escaped using backticks.
402 StringAttr legalize(StringAttr attr) {
403 StringRef str = attr.getValue();
404 if (str.empty() || !isdigit(str.front()))
405 return attr;
406 return StringAttr::get(attr.getContext(), "`" + Twine(attr) + "`");
407 }
408
409 void addValueName(Value value, StringAttr attr) {
410 valueNames.insert({value, attr.getValue()});
411 }
412 void addValueName(Value value, StringRef str) {
413 auto it = valueNamesStorage.insert(str);
414 valueNames.insert({value, it.first->getKey()});
415 }
416 void addForceable(Forceable op, StringAttr attr) {
417 addValueName(op.getData(), attr);
418 if (op.isForceable()) {
419 SmallString<32> rwName;
420 (Twine("rwprobe(") + attr.strref() + ")").toVector(rwName);
421 addValueName(op.getDataRef(), rwName);
422 }
423 }
424
425 /// The current circuit namespace valid within the call to `emitCircuit`.
426 CircuitNamespace circuitNamespace;
427
428 /// Symbol and Inner Symbol analyses, valid within the call to `emitCircuit`.
429 struct SymInfos {
430 SymbolTable symbolTable;
432 hw::InnerRefNamespace irn{symbolTable, istc};
433 SymInfos(Operation *op) : symbolTable(op), istc(op) {}
434 };
435 std::optional<std::reference_wrapper<SymInfos>> symInfos;
436
437 /// The version of the FIRRTL spec that should be emitted.
438 FIRVersion version;
439};
440} // namespace
441
442LogicalResult Emitter::finalize() { return failure(encounteredError); }
443
444/// Emit an entire circuit.
445void Emitter::emitCircuit(CircuitOp op) {
446 circuitNamespace.add(op);
447 SymInfos circuitSymInfos(op);
448 symInfos = circuitSymInfos;
449 startStatement();
450 ps << "FIRRTL version ";
451 ps.addAsString(version.major);
452 ps << ".";
453 ps.addAsString(version.minor);
454 ps << ".";
455 ps.addAsString(version.patch);
456 ps << PP::newline;
457 ps << "circuit " << PPExtString(legalize(op.getNameAttr())) << " :";
458 setPendingNewline();
459 ps.scopedBox(PP::bbox2, [&]() {
460 for (auto &bodyOp : *op.getBodyBlock()) {
461 if (encounteredError)
462 break;
463 TypeSwitch<Operation *>(&bodyOp)
464 .Case<FModuleOp, FExtModuleOp, FIntModuleOp>([&](auto op) {
465 emitModule(op);
466 ps << PP::newline;
467 })
468 .Case<DomainOp, LayerOp, OptionOp, FormalOp, SimulationOp>(
469 [&](auto op) { emitDeclaration(op); })
470 .Default([&](auto op) {
471 emitOpError(op, "not supported for emission inside circuit");
472 });
473 }
474 });
475 circuitNamespace.clear();
476 symInfos = std::nullopt;
477}
478
479void Emitter::emitEnabledLayers(ArrayRef<Attribute> layers, Operation *op) {
480 if (layers.empty())
481 return;
482 if (failed(requireVersion(FIRVersion(4, 0, 0), op, "enabled layers")))
483 return;
484 for (auto layer : layers) {
485 ps << PP::space;
486 ps.cbox(2, IndentStyle::Block);
487 ps << "enablelayer" << PP::space;
488 emitSymbol(cast<SymbolRefAttr>(layer));
489 ps << PP::end;
490 }
491}
492
493void Emitter::emitKnownLayers(ArrayRef<Attribute> layers, Operation *op) {
494 if (layers.empty())
495 return;
496 if (failed(requireVersion({6, 0, 0}, op, "known layers")))
497 return;
498 for (auto layer : layers) {
499 ps << PP::space;
500 ps.cbox(2, IndentStyle::Block);
501 ps << "knownlayer" << PP::space;
502 emitSymbol(cast<SymbolRefAttr>(layer));
503 ps << PP::end;
504 }
505}
506
507void Emitter::emitRequirements(ArrayRef<Attribute> requirements) {
508 if (requirements.empty())
509 return;
510 ps << PP::space;
511 ps.cbox(2, IndentStyle::Block);
512 ps << "requires" << PP::space;
513 llvm::interleaveComma(requirements, ps, [&](Attribute req) {
514 ps.writeQuotedEscaped(cast<StringAttr>(req).getValue());
515 });
516 ps << PP::end;
517}
518
519void Emitter::emitParamAssign(ParamDeclAttr param, Operation *op,
520 std::optional<PPExtString> wordBeforeLHS) {
521 if (wordBeforeLHS) {
522 ps << *wordBeforeLHS << PP::nbsp;
523 }
524 ps << PPExtString(param.getName().strref()) << PP::nbsp << "=" << PP::nbsp;
525 emitParamValue(param.getValue(), op);
526}
527
528void Emitter::emitParamValue(Attribute value, Operation *op) {
529 TypeSwitch<Attribute>(value)
530 .Case<IntegerAttr>([&](auto attr) { ps.addAsString(attr.getValue()); })
531 .Case<FloatAttr>([&](auto attr) {
532 SmallString<16> str;
533 attr.getValue().toString(str);
534 ps << str;
535 })
536 .Case<StringAttr>(
537 [&](auto attr) { ps.writeQuotedEscaped(attr.getValue()); })
538 .Case<ArrayAttr>([&](auto attr) {
539 ps.scopedBox(PP::bbox2, [&]() {
540 ps << "[";
541 interleaveComma(attr.getValue(),
542 [&](auto element) { emitParamValue(element, op); });
543 ps << "]";
544 });
545 })
546 .Case<DictionaryAttr>([&](auto attr) {
547 ps.scopedBox(PP::bbox2, [&]() {
548 ps << "{";
549 interleaveComma(attr.getValue(), [&](auto field) {
550 ps << PPExtString(field.getName()) << PP::nbsp << "=" << PP::nbsp;
551 emitParamValue(field.getValue(), op);
552 });
553 ps << "}";
554 });
555 })
556 .Default([&](auto attr) {
557 emitOpError(op, "with unsupported parameter attribute: ") << attr;
558 ps << "<unsupported-attr ";
559 ps.addAsString(attr);
560 ps << ">";
561 });
562}
563
564void Emitter::emitGenericIntrinsic(GenericIntrinsicOp op) {
565 ps << "intrinsic(";
566 ps.scopedBox(PP::cbox0, [&]() {
567 ps.scopedBox(PP::ibox2, [&]() {
568 ps << op.getIntrinsic();
569 ps.scopedBox(PP::ibox0, [&]() {
570 auto params = op.getParameters();
571 if (!params.empty()) {
572 ps << "<";
573 ps.scopedBox(PP::ibox0, [&]() {
574 interleaveComma(
575 params.getAsRange<ParamDeclAttr>(),
576 [&](ParamDeclAttr param) { emitParamAssign(param, op); });
577 });
578 ps << ">";
579 }
580 });
581 if (op.getNumResults() != 0)
582 emitTypeWithColon(op.getResult().getType());
583 });
584 if (op.getNumOperands() != 0) {
585 ps << "," << PP::space;
586 ps.scopedBox(PP::ibox0, [&]() { interleaveComma(op->getOperands()); });
587 }
588 ps << ")";
589 });
590}
591
592/// Emit an entire module.
593void Emitter::emitModule(FModuleOp op) {
594 startStatement();
595 ps.cbox(4, IndentStyle::Block);
596 if (op.isPublic() && FIRVersion(3, 3, 0) <= version)
597 ps << "public" << PP::nbsp;
598 ps << "module " << PPExtString(legalize(op.getNameAttr()));
599 emitEnabledLayers(op.getLayers(), op);
600 ps << PP::nbsp << ":" << PP::end;
601 emitLocation(op);
602
603 ps.scopedBox(PP::bbox2, [&]() {
604 setPendingNewline();
605
606 // Emit the ports.
607 auto ports = op.getPorts();
608 emitModulePorts(ports, op.getArguments());
609 if (!ports.empty() && !op.getBodyBlock()->empty())
610 ps << PP::newline;
611
612 // Emit the module body.
613 emitStatementsInBlock(*op.getBodyBlock());
614 });
615 valueNames.clear();
616 valueNamesStorage.clear();
617}
618
619/// Emit an external module.
620void Emitter::emitModule(FExtModuleOp op) {
621 startStatement();
622 ps.cbox(4, IndentStyle::Block);
623 ps << "extmodule " << PPExtString(legalize(op.getNameAttr()));
624 emitKnownLayers(op.getKnownLayers(), op);
625 emitEnabledLayers(op.getLayers(), op);
626 if (auto reqs = op.getExternalRequirements())
627 emitRequirements(reqs.getValue());
628 ps << PP::nbsp << ":" << PP::end;
629 emitLocation(op);
630
631 ps.scopedBox(PP::bbox2, [&]() {
632 setPendingNewline();
633
634 // Emit the ports.
635 auto ports = op.getPorts();
636 emitModulePorts(ports);
637
638 // Emit the optional `defname`.
639 if (op.getDefname() && !op.getDefname()->empty()) {
640 startStatement();
641 ps << "defname = " << PPExtString(*op.getDefname());
642 setPendingNewline();
643 }
644
645 // Emit the parameters.
646 emitModuleParameters(op, op.getParameters());
647 });
648}
649
650/// Emit an intrinsic module
651void Emitter::emitModule(FIntModuleOp op) {
652 if (FIRVersion(4, 0, 0) <= version) {
653 emitOpError(op, "intrinsic modules were removed in FIRRTL 4.0.0");
654 return;
655 }
656 startStatement();
657 ps.cbox(4, IndentStyle::Block);
658 ps << "intmodule " << PPExtString(legalize(op.getNameAttr()));
659 emitEnabledLayers(op.getLayers(), op);
660 ps << PP::nbsp << ":" << PP::end;
661 emitLocation(op);
662
663 ps.scopedBox(PP::bbox2, [&]() {
664 setPendingNewline();
665
666 // Emit the ports.
667 auto ports = op.getPorts();
668 emitModulePorts(ports);
669
670 startStatement();
671 ps << "intrinsic = " << PPExtString(op.getIntrinsic());
672 setPendingNewline();
673
674 // Emit the parameters.
675 emitModuleParameters(op, op.getParameters());
676 });
677}
678
679/// Emit the ports of a module or extmodule. If the `arguments` array is
680/// non-empty, it is used to populate `emittedNames` with the port names for use
681/// during expression emission.
682void Emitter::emitModulePorts(ArrayRef<PortInfo> ports,
683 Block::BlockArgListType arguments) {
684 // Emit the ports.
685 for (unsigned i = 0, e = ports.size(); i < e; ++i) {
686 startStatement();
687 const auto &port = ports[i];
688 ps << (port.direction == Direction::In ? "input " : "output ");
689 auto legalName = legalize(port.name);
690 if (!arguments.empty())
691 addValueName(arguments[i], legalName);
692 ps << PPExtString(legalName) << " : ";
693 emitType(port.type);
694 emitDomains(port.domains, ports);
695 emitLocation(ports[i].loc);
696 setPendingNewline();
697 }
698}
699
700void Emitter::emitModuleParameters(Operation *op, ArrayAttr parameters) {
701 for (auto param : parameters.getAsRange<ParamDeclAttr>()) {
702 startStatement();
703 emitParamAssign(param, op, PPExtString("parameter"));
704 setPendingNewline();
705 }
706}
707
708void Emitter::emitDeclaration(DomainOp op) {
709 if (failed(requireVersion(missingSpecFIRVersion, op, "domains")))
710 return;
711 startStatement();
712 ps << "domain " << PPExtString(op.getSymName()) << " :";
713 emitLocationAndNewLine(op);
714 ps.scopedBox(PP::bbox2, [&]() {
715 for (auto attr : op.getFields()) {
716 auto fieldAttr = cast<DomainFieldAttr>(attr);
717 ps << PP::newline << PPExtString(fieldAttr.getName()) << " : ";
718 emitType(fieldAttr.getType());
719 }
720 });
721}
722
723/// Emit a layer definition.
724void Emitter::emitDeclaration(LayerOp op) {
725 if (failed(requireVersion(FIRVersion(3, 3, 0), op, "layers")))
726 return;
727 if (op.getConvention() == LayerConvention::Inline &&
728 failed(requireVersion(FIRVersion(4, 1, 0), op, "inline layers")))
729 return;
730 startStatement();
731 ps << "layer " << PPExtString(op.getSymName()) << ", "
732 << PPExtString(stringifyLayerConvention(op.getConvention()));
733
734 if (auto outputFile = op->getAttrOfType<hw::OutputFileAttr>("output_file")) {
735 ps << ", ";
736 ps.writeQuotedEscaped(outputFile.getFilename().getValue());
737 }
738
739 ps << " : ";
740 emitLocationAndNewLine(op);
741 ps.scopedBox(PP::bbox2, [&]() {
742 for (auto &bodyOp : op.getBody().getOps()) {
743 TypeSwitch<Operation *>(&bodyOp)
744 .Case<LayerOp>([&](auto op) { emitDeclaration(op); })
745 .Default([&](auto op) {
746 emitOpError(op,
747 "not supported for emission inside layer definition");
748 });
749 }
750 });
751}
752
753/// Emit an option declaration.
754void Emitter::emitDeclaration(OptionOp op) {
755 if (failed(requireVersion(missingSpecFIRVersion, op, "option groups")))
756 return;
757 startStatement();
758 ps << "option " << PPExtString(legalize(op.getSymNameAttr())) << " :";
759 emitLocation(op);
760 ps.scopedBox(PP::bbox2, [&] {
761 for (auto caseOp : op.getBody().getOps<OptionCaseOp>()) {
762 ps << PP::newline;
763 ps << PPExtString(legalize(caseOp.getSymNameAttr()));
764 emitLocation(caseOp);
765 }
766 });
767 ps << PP::newline << PP::newline;
768}
769
770/// Emit a formal test definition.
771void Emitter::emitDeclaration(FormalOp op) {
772 if (failed(requireVersion(FIRVersion(4, 0, 0), op, "formal tests")))
773 return;
774 emitFormalLike(op, "formal", op.getSymNameAttr(),
775 op.getModuleNameAttr().getAttr(), op.getParameters());
776}
777
778/// Emit a simulation test definition.
779void Emitter::emitDeclaration(SimulationOp op) {
780 if (failed(requireVersion(nextFIRVersion, op, "simulation tests")))
781 return;
782 emitFormalLike(op, "simulation", op.getSymNameAttr(),
783 op.getModuleNameAttr().getAttr(), op.getParameters());
784}
785
786/// Emit a formal or simulation test definition.
787void Emitter::emitFormalLike(Operation *op, StringRef keyword,
788 StringAttr symName, StringAttr moduleName,
789 DictionaryAttr params) {
790 startStatement();
791 ps.cbox(4, IndentStyle::Block);
792 ps << keyword << " " << PPExtString(legalize(symName));
793 ps << " of " << PPExtString(legalize(moduleName));
794 ps << PP::nbsp << ":" << PP::end;
795 emitLocation(op);
796
797 ps.scopedBox(PP::bbox2, [&]() {
798 setPendingNewline();
799 for (auto param : params) {
800 startStatement();
801 ps << PPExtString(param.getName()) << PP::nbsp << "=" << PP::nbsp;
802 emitParamValue(param.getValue(), op);
803 setPendingNewline();
804 }
805 });
806}
807
808/// Check if an operation is inlined into the emission of their users. For
809/// example, subfields are always inlined.
810static bool isEmittedInline(Operation *op) {
811 // FIRRTL expressions are statically classified as always inlineable.
812 // InvalidValueOp never is inlined, and is handled specially.
813 // GenericIntrinsicOp is inlined if has exactly one use (only emit once)
814 // that is not emitted inline. This is to ensure it is emitted inline
815 // in common cases, but only inspect one level deep.
816 return (isExpression(op) && !isa<InvalidValueOp>(op)) ||
817 (isa<GenericIntrinsicOp>(op) && op->hasOneUse() &&
818 !isEmittedInline(*op->getUsers().begin()));
819}
820
821void Emitter::emitStatementsInBlock(Block &block) {
822 for (auto &bodyOp : block) {
823 if (encounteredError)
824 return;
825 if (isEmittedInline(&bodyOp))
826 continue;
827 TypeSwitch<Operation *>(&bodyOp)
828 .Case<WhenOp, WireOp, RegOp, RegResetOp, NodeOp, StopOp, SkipOp,
829 PrintFOp, FPrintFOp, FFlushOp, AssertOp, AssumeOp, CoverOp,
830 ConnectOp, MatchingConnectOp, PropertyAssertOp, PropAssignOp,
831 InstanceOp, InstanceChoiceOp, AttachOp, MemOp, InvalidValueOp,
832 SeqMemOp, CombMemOp, MemoryPortOp, MemoryDebugPortOp,
833 MemoryPortAccessOp, DomainDefineOp, RefDefineOp, RefForceOp,
834 RefForceInitialOp, RefReleaseOp, RefReleaseInitialOp,
835 LayerBlockOp, GenericIntrinsicOp, DomainCreateAnonOp,
836 DomainCreateOp>([&](auto op) { emitStatement(op); })
837 .Default([&](auto op) {
838 startStatement();
839 ps << "// operation " << PPExtString(op->getName().getStringRef());
840 setPendingNewline();
841 emitOpError(op, "not supported as statement");
842 });
843 }
844}
845
846void Emitter::emitStatement(WhenOp op) {
847 startStatement();
848 ps << "when ";
849 emitExpression(op.getCondition());
850 ps << " :";
851 emitLocationAndNewLine(op);
852 ps.scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(op.getThenBlock()); });
853 // emitStatementsInBlock(op.getThenBlock());
854 if (!op.hasElseRegion())
855 return;
856
857 startStatement();
858 ps << "else ";
859 // Sugar to `else when ...` if there's only a single when statement in the
860 // else block.
861 auto &elseBlock = op.getElseBlock();
862 if (!elseBlock.empty() && &elseBlock.front() == &elseBlock.back()) {
863 if (auto whenOp = dyn_cast<WhenOp>(&elseBlock.front())) {
864 emitStatement(whenOp);
865 return;
866 }
867 }
868 // Otherwise print the block as `else :`.
869 ps << ":";
870 setPendingNewline();
871 ps.scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(elseBlock); });
872}
873
874void Emitter::emitStatement(WireOp op) {
875 auto legalName = legalize(op.getNameAttr());
876 addForceable(op, legalName);
877 startStatement();
878 ps.scopedBox(PP::ibox2, [&]() {
879 ps << "wire " << PPExtString(legalName);
880 emitTypeWithColon(op.getResult().getType());
881
882 // Emit domain associations if present
883 if (!op.getDomains().empty()) {
884 ps << PP::space << "domains" << PP::space << "[";
885 ps.scopedBox(PP::cbox0, [&]() {
886 llvm::interleaveComma(op.getDomains(), ps, [&](Value domain) {
887 auto name = lookupEmittedName(domain);
888 assert(name && "domain value must have a name");
889 ps << PPExtString(*name);
890 });
891 });
892 ps << "]";
893 }
894 });
895 emitLocationAndNewLine(op);
896}
897
898void Emitter::emitStatement(RegOp op) {
899 auto legalName = legalize(op.getNameAttr());
900 addForceable(op, legalName);
901 startStatement();
902 ps.scopedBox(PP::ibox2, [&]() {
903 ps << "reg " << PPExtString(legalName);
904 emitTypeWithColon(op.getResult().getType());
905 ps << "," << PP::space;
906 emitExpression(op.getClockVal());
907 });
908 emitLocationAndNewLine(op);
909}
910
911void Emitter::emitStatement(RegResetOp op) {
912 auto legalName = legalize(op.getNameAttr());
913 addForceable(op, legalName);
914 startStatement();
915 if (FIRVersion(3, 0, 0) <= version) {
916 ps.scopedBox(PP::ibox2, [&]() {
917 ps << "regreset " << legalName;
918 emitTypeWithColon(op.getResult().getType());
919 ps << "," << PP::space;
920 emitExpression(op.getClockVal());
921 ps << "," << PP::space;
922 emitExpression(op.getResetSignal());
923 ps << "," << PP::space;
924 emitExpression(op.getResetValue());
925 });
926 } else {
927 ps.scopedBox(PP::ibox2, [&]() {
928 ps << "reg " << legalName;
929 emitTypeWithColon(op.getResult().getType());
930 ps << "," << PP::space;
931 emitExpression(op.getClockVal());
932 ps << PP::space << "with :";
933 // Don't break this because of the newline.
934 ps << PP::neverbreak;
935 // No-paren version must be newline + indent.
936 ps << PP::newline; // ibox2 will indent.
937 ps << "reset => (" << PP::ibox0;
938 emitExpression(op.getResetSignal());
939 ps << "," << PP::space;
940 emitExpression(op.getResetValue());
941 ps << ")" << PP::end;
942 });
943 }
944 emitLocationAndNewLine(op);
945}
946
947void Emitter::emitStatement(NodeOp op) {
948 auto legalName = legalize(op.getNameAttr());
949 addForceable(op, legalName);
950 startStatement();
951 emitAssignLike([&]() { ps << "node " << PPExtString(legalName); },
952 [&]() { emitExpression(op.getInput()); });
953 emitLocationAndNewLine(op);
954}
955
956void Emitter::emitStatement(StopOp op) {
957 startStatement();
958 ps.scopedBox(PP::ibox2, [&]() {
959 ps << "stop(" << PP::ibox0;
960 emitExpression(op.getClock());
961 ps << "," << PP::space;
962 emitExpression(op.getCond());
963 ps << "," << PP::space;
964 ps.addAsString(op.getExitCode());
965 ps << ")" << PP::end;
966 if (!op.getName().empty()) {
967 ps << PP::space << ": " << PPExtString(legalize(op.getNameAttr()));
968 }
969 });
970 emitLocationAndNewLine(op);
971}
972
973void Emitter::emitStatement(SkipOp op) {
974 startStatement();
975 ps << "skip";
976 emitLocationAndNewLine(op);
977}
978
979void Emitter::emitFormatString(Operation *op, StringRef origFormatString,
980 OperandRange substitutionOperands,
981 llvm::SmallVectorImpl<Value> &substitutions) {
982 // Replace the generic "{{}}" special substitutions with their attributes.
983 // E.g.:
984 //
985 // "hello {{}} world"(%time)
986 //
987 // Becomes:
988 //
989 // "hello {{SimulationTime}} world"
990 SmallString<64> formatString;
991 for (size_t i = 0, e = origFormatString.size(), opIdx = 0; i != e; ++i) {
992 auto c = origFormatString[i];
993 switch (c) {
994 case '%': {
995 formatString.push_back(c);
996
997 // Parse the width specifier.
998 SmallString<6> width;
999 c = origFormatString[++i];
1000 while (isdigit(c)) {
1001 width.push_back(c);
1002 c = origFormatString[++i];
1003 }
1004
1005 // Parse the radix.
1006 switch (c) {
1007 case 'b':
1008 case 'd':
1009 case 'x':
1010 if (!width.empty())
1011 formatString.append(width);
1012 [[fallthrough]];
1013 case 'c':
1014 substitutions.push_back(substitutionOperands[opIdx++]);
1015 [[fallthrough]];
1016 default:
1017 formatString.push_back(c);
1018 }
1019 break;
1020 }
1021 case '{':
1022 if (origFormatString.slice(i, i + 4) == "{{}}") {
1023 formatString.append("{{");
1024 TypeSwitch<Operation *>(substitutionOperands[opIdx++].getDefiningOp())
1025 .Case<TimeOp>(
1026 [&](auto time) { formatString.append("SimulationTime"); })
1027 .Case<HierarchicalModuleNameOp>([&](auto time) {
1028 formatString.append("HierarchicalModuleName");
1029 })
1030 .Default([&](auto) {
1031 emitError(op, "unsupported fstring substitution type");
1032 });
1033 formatString.append("}}");
1034 }
1035 i += 3;
1036 break;
1037 default:
1038 formatString.push_back(c);
1039 }
1040 }
1041 ps.writeQuotedEscaped(formatString);
1042}
1043
1044void Emitter::emitStatement(PrintFOp op) {
1045 startStatement();
1046 ps.scopedBox(PP::ibox2, [&]() {
1047 ps << "printf(" << PP::ibox0;
1048 emitExpression(op.getClock());
1049 ps << "," << PP::space;
1050 emitExpression(op.getCond());
1051 ps << "," << PP::space;
1052
1053 SmallVector<Value, 4> substitutions;
1054 emitFormatString(op, op.getFormatString(), op.getSubstitutions(),
1055 substitutions);
1056 for (auto operand : substitutions) {
1057 ps << "," << PP::space;
1058 emitExpression(operand);
1059 }
1060 ps << ")" << PP::end;
1061 if (!op.getName().empty()) {
1062 ps << PP::space << ": " << PPExtString(legalize(op.getNameAttr()));
1063 }
1064 });
1065 emitLocationAndNewLine(op);
1066}
1067
1068void Emitter::emitStatement(FPrintFOp op) {
1069 if (failed(requireVersion({6, 0, 0}, op, "fprintf")))
1070 return;
1071 startStatement();
1072 ps.scopedBox(PP::ibox2, [&]() {
1073 ps << "fprintf(" << PP::ibox0;
1074 emitExpression(op.getClock());
1075 ps << "," << PP::space;
1076 emitExpression(op.getCond());
1077 ps << "," << PP::space;
1078
1079 SmallVector<Value, 4> outputFileSubstitutions;
1080 emitFormatString(op, op.getOutputFile(), op.getOutputFileSubstitutions(),
1081 outputFileSubstitutions);
1082 if (!outputFileSubstitutions.empty()) {
1083 ps << "," << PP::space;
1084 interleaveComma(outputFileSubstitutions);
1085 }
1086
1087 ps << "," << PP::space;
1088 SmallVector<Value, 4> substitutions;
1089 emitFormatString(op, op.getFormatString(), op.getSubstitutions(),
1090 substitutions);
1091 if (!substitutions.empty()) {
1092 ps << "," << PP::space;
1093 interleaveComma(substitutions);
1094 }
1095
1096 ps << ")" << PP::end;
1097 if (!op.getName().empty()) {
1098 ps << PP::space << ": " << PPExtString(legalize(op.getNameAttr()));
1099 }
1100 });
1101 emitLocationAndNewLine(op);
1102}
1103
1104void Emitter::emitStatement(FFlushOp op) {
1105 if (failed(requireVersion({6, 0, 0}, op, "fflush")))
1106 return;
1107 startStatement();
1108 ps.scopedBox(PP::ibox2, [&]() {
1109 ps << "fflush(" << PP::ibox0;
1110 emitExpression(op.getClock());
1111 ps << "," << PP::space;
1112 emitExpression(op.getCond());
1113 if (op.getOutputFileAttr()) {
1114 ps << "," << PP::space;
1115 SmallVector<Value, 4> substitutions;
1116 emitFormatString(op, op.getOutputFileAttr(),
1117 op.getOutputFileSubstitutions(), substitutions);
1118 if (!substitutions.empty()) {
1119 ps << "," << PP::space;
1120 interleaveComma(substitutions);
1121 }
1122 }
1123 ps << ")" << PP::end;
1124 });
1125 emitLocationAndNewLine(op);
1126}
1127
1128template <class T>
1129void Emitter::emitVerifStatement(T op, StringRef mnemonic) {
1130 startStatement();
1131 ps.scopedBox(PP::ibox2, [&]() {
1132 ps << mnemonic << "(" << PP::ibox0;
1133 emitExpression(op.getClock());
1134 ps << "," << PP::space;
1135 emitExpression(op.getPredicate());
1136 ps << "," << PP::space;
1137 emitExpression(op.getEnable());
1138 ps << "," << PP::space;
1139 ps.writeQuotedEscaped(op.getMessage());
1140 ps << ")" << PP::end;
1141 if (!op.getName().empty()) {
1142 ps << PP::space << ": " << PPExtString(legalize(op.getNameAttr()));
1143 }
1144 });
1145 emitLocationAndNewLine(op);
1146}
1147
1148void Emitter::emitStatement(ConnectOp op) {
1149 startStatement();
1150 if (FIRVersion(3, 0, 0) <= version) {
1151 ps.scopedBox(PP::ibox2, [&]() {
1152 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
1153 ps << "invalidate" << PP::space;
1154 emitExpression(op.getDest());
1155 } else {
1156 ps << "connect" << PP::space;
1157 emitExpression(op.getDest());
1158 ps << "," << PP::space;
1159 emitExpression(op.getSrc());
1160 }
1161 });
1162 } else {
1163 auto emitLHS = [&]() { emitExpression(op.getDest()); };
1164 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
1165 emitAssignLike(
1166 emitLHS, [&]() { ps << "invalid"; }, PPExtString("is"));
1167 } else {
1168 emitAssignLike(
1169 emitLHS, [&]() { emitExpression(op.getSrc()); }, PPExtString("<="));
1170 }
1171 }
1172 emitLocationAndNewLine(op);
1173}
1174
1175void Emitter::emitStatement(MatchingConnectOp op) {
1176 startStatement();
1177 if (FIRVersion(3, 0, 0) <= version) {
1178 ps.scopedBox(PP::ibox2, [&]() {
1179 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
1180 ps << "invalidate" << PP::space;
1181 emitExpression(op.getDest());
1182 } else {
1183 ps << "connect" << PP::space;
1184 emitExpression(op.getDest());
1185 ps << "," << PP::space;
1186 emitExpression(op.getSrc());
1187 }
1188 });
1189 } else {
1190 auto emitLHS = [&]() { emitExpression(op.getDest()); };
1191 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
1192 emitAssignLike(
1193 emitLHS, [&]() { ps << "invalid"; }, PPExtString("is"));
1194 } else {
1195 emitAssignLike(
1196 emitLHS, [&]() { emitExpression(op.getSrc()); }, PPExtString("<="));
1197 }
1198 }
1199 emitLocationAndNewLine(op);
1200}
1201
1202void Emitter::emitStatement(PropertyAssertOp op) {
1203 if (failed(requireVersion(FIRVersion(6, 0, 0), op, "property assert")))
1204 return;
1205 startStatement();
1206 ps.scopedBox(PP::ibox2, [&]() {
1207 ps << "propassert" << PP::space;
1208 emitExpression(op.getCondition());
1209 ps << "," << PP::space;
1210 // For FIRRTL < 7.0.0, do a best effort emission that will inline a single
1211 // string expression. If we see anything more complicated than this, just
1212 // bail.
1213 if (version < FIRVersion(7, 0, 0)) {
1214 auto expr = dyn_cast<StringConstantOp>(op.getMessage().getDefiningOp());
1215 if (!expr) {
1216 auto diag =
1217 emitOpError(op, "unable to emit non-literal string expressions "
1218 "when targeting FIRRTL version <= 7.0.0");
1219 diag.attachNote(op.getMessage().getLoc())
1220 << "non-literal expression is here";
1221 }
1222 } else {
1223 emitExpression(op.getMessage());
1224 }
1225 });
1226 emitLocationAndNewLine(op);
1227}
1228
1229void Emitter::emitStatement(PropAssignOp op) {
1230 if (failed(requireVersion(FIRVersion(3, 1, 0), op, "properties")))
1231 return;
1232 startStatement();
1233 ps.scopedBox(PP::ibox2, [&]() {
1234 ps << "propassign" << PP::space;
1235 interleaveComma(op.getOperands());
1236 });
1237 emitLocationAndNewLine(op);
1238}
1239
1240void Emitter::emitStatement(InstanceOp op) {
1241 startStatement();
1242 auto legalName = legalize(op.getNameAttr());
1243 ps << "inst " << PPExtString(legalName) << " of "
1244 << PPExtString(legalize(op.getModuleNameAttr().getAttr()));
1245 emitLocationAndNewLine(op);
1246
1247 // Make sure we have a name like `<inst>.<port>` for each of the instance
1248 // result values.
1249 SmallString<16> portName(legalName);
1250 portName.push_back('.');
1251 unsigned baseLen = portName.size();
1252 for (unsigned i = 0, e = op.getNumResults(); i < e; ++i) {
1253 portName.append(legalize(op.getPortNameAttr(i)));
1254 addValueName(op.getResult(i), portName);
1255 portName.resize(baseLen);
1256 }
1257}
1258
1259void Emitter::emitStatement(InstanceChoiceOp op) {
1260 if (failed(requireVersion(missingSpecFIRVersion, op,
1261 "option groups/instance choices")))
1262 return;
1263 startStatement();
1264 auto legalName = legalize(op.getNameAttr());
1265 ps << "instchoice " << PPExtString(legalName) << " of "
1266 << PPExtString(legalize(op.getDefaultTargetAttr().getAttr())) << ", "
1267 << PPExtString(legalize(op.getOptionNameAttr())) << " :";
1268 emitLocation(op);
1269 ps.scopedBox(PP::bbox2, [&] {
1270 for (const auto &[optSym, targetSym] : op.getTargetChoices()) {
1271 ps << PP::newline;
1272 ps << PPExtString(legalize(optSym.getLeafReference()));
1273 ps << " => ";
1274 ps << PPExtString(legalize(targetSym.getAttr()));
1275 }
1276 });
1277 setPendingNewline();
1278
1279 SmallString<16> portName(legalName);
1280 portName.push_back('.');
1281 unsigned baseLen = portName.size();
1282 for (unsigned i = 0, e = op.getNumResults(); i < e; ++i) {
1283 portName.append(legalize(op.getPortNameAttr(i)));
1284 addValueName(op.getResult(i), portName);
1285 portName.resize(baseLen);
1286 }
1287}
1288
1289void Emitter::emitStatement(AttachOp op) {
1290 emitStatementFunctionOp(PPExtString("attach"), op);
1291}
1292
1293void Emitter::emitStatement(MemOp op) {
1294 auto legalName = legalize(op.getNameAttr());
1295 SmallString<16> portName(legalName);
1296 portName.push_back('.');
1297 auto portNameBaseLen = portName.size();
1298 for (auto result : llvm::zip(op.getResults(), op.getPortNames())) {
1299 portName.resize(portNameBaseLen);
1300 portName.append(legalize(cast<StringAttr>(std::get<1>(result))));
1301 addValueName(std::get<0>(result), portName);
1302 }
1303
1304 startStatement();
1305 ps << "mem " << PPExtString(legalName) << " :";
1306 emitLocationAndNewLine(op);
1307 ps.scopedBox(PP::bbox2, [&]() {
1308 startStatement();
1309 ps << "data-type => ";
1310 emitType(op.getDataType());
1311 ps << PP::newline;
1312 ps << "depth => ";
1313 ps.addAsString(op.getDepth());
1314 ps << PP::newline;
1315 ps << "read-latency => ";
1316 ps.addAsString(op.getReadLatency());
1317 ps << PP::newline;
1318 ps << "write-latency => ";
1319 ps.addAsString(op.getWriteLatency());
1320 ps << PP::newline;
1321
1322 SmallString<16> reader, writer, readwriter;
1323 for (std::pair<StringAttr, MemOp::PortKind> port : op.getPorts()) {
1324 auto add = [&](SmallString<16> &to, StringAttr name) {
1325 if (!to.empty())
1326 to.push_back(' ');
1327 to.append(name.getValue());
1328 };
1329 switch (port.second) {
1330 case MemOp::PortKind::Read:
1331 add(reader, legalize(port.first));
1332 break;
1333 case MemOp::PortKind::Write:
1334 add(writer, legalize(port.first));
1335 break;
1336 case MemOp::PortKind::ReadWrite:
1337 add(readwriter, legalize(port.first));
1338 break;
1339 case MemOp::PortKind::Debug:
1340 emitOpError(op, "has unsupported 'debug' port");
1341 return;
1342 }
1343 }
1344 if (!reader.empty())
1345 ps << "reader => " << reader << PP::newline;
1346 if (!writer.empty())
1347 ps << "writer => " << writer << PP::newline;
1348 if (!readwriter.empty())
1349 ps << "readwriter => " << readwriter << PP::newline;
1350
1351 ps << "read-under-write => ";
1352 emitAttribute(op.getRuwAttr());
1353 setPendingNewline();
1354 });
1355}
1356
1357void Emitter::emitStatement(SeqMemOp op) {
1358 startStatement();
1359 ps.scopedBox(PP::ibox2, [&]() {
1360 ps << "smem " << PPExtString(legalize(op.getNameAttr()));
1361 emitTypeWithColon(op.getType());
1362 ps << "," << PP::space;
1363 emitAttribute(op.getRuwAttr());
1364 });
1365 emitLocationAndNewLine(op);
1366}
1367
1368void Emitter::emitStatement(CombMemOp op) {
1369 startStatement();
1370 ps.scopedBox(PP::ibox2, [&]() {
1371 ps << "cmem " << PPExtString(legalize(op.getNameAttr()));
1372 emitTypeWithColon(op.getType());
1373 });
1374 emitLocationAndNewLine(op);
1375}
1376
1377void Emitter::emitStatement(MemoryPortOp op) {
1378 // Nothing to output for this operation.
1379 addValueName(op.getData(), legalize(op.getNameAttr()));
1380}
1381
1382void Emitter::emitStatement(MemoryDebugPortOp op) {
1383 // Nothing to output for this operation.
1384 addValueName(op.getData(), legalize(op.getNameAttr()));
1385}
1386
1387void Emitter::emitStatement(MemoryPortAccessOp op) {
1388 startStatement();
1389
1390 // Print the port direction and name.
1391 auto port = cast<MemoryPortOp>(op.getPort().getDefiningOp());
1392 emitAttribute(port.getDirection());
1393 // TODO: emitAssignLike
1394 ps << " mport " << PPExtString(legalize(port.getNameAttr())) << " = ";
1395
1396 // Print the memory name.
1397 auto *mem = port.getMemory().getDefiningOp();
1398 if (auto seqMem = dyn_cast<SeqMemOp>(mem))
1399 ps << legalize(seqMem.getNameAttr());
1400 else
1401 ps << legalize(cast<CombMemOp>(mem).getNameAttr());
1402
1403 // Print the address.
1404 ps << "[";
1405 emitExpression(op.getIndex());
1406 ps << "], ";
1407
1408 // Print the clock.
1409 emitExpression(op.getClock());
1410
1411 emitLocationAndNewLine(op);
1412}
1413
1414void Emitter::emitStatement(DomainDefineOp op) {
1415 if (failed(requireVersion(missingSpecFIRVersion, op, "domains")))
1416 return;
1417 // If the source is an anonymous domain, then we can skip emitting this op.
1418 if (isa_and_nonnull<DomainCreateAnonOp>(op.getSrc().getDefiningOp()))
1419 return;
1420
1421 startStatement();
1422 emitAssignLike([&]() { emitExpression(op.getDest()); },
1423 [&]() { emitExpression(op.getSrc()); }, PPExtString("="),
1424 PPExtString("domain_define"));
1425 emitLocationAndNewLine(op);
1426}
1427
1428void Emitter::emitStatement(RefDefineOp op) {
1429 startStatement();
1430 emitAssignLike([&]() { emitExpression(op.getDest()); },
1431 [&]() { emitExpression(op.getSrc()); }, PPExtString("="),
1432 PPExtString("define"));
1433 emitLocationAndNewLine(op);
1434}
1435
1436void Emitter::emitStatement(RefForceOp op) {
1437 emitStatementFunctionOp(PPExtString("force"), op);
1438}
1439
1440void Emitter::emitStatement(RefForceInitialOp op) {
1441 startStatement();
1442 auto constantPredicate =
1443 dyn_cast_or_null<ConstantOp>(op.getPredicate().getDefiningOp());
1444 bool hasEnable = !constantPredicate || constantPredicate.getValue() == 0;
1445 if (hasEnable) {
1446 ps << "when ";
1447 emitExpression(op.getPredicate());
1448 ps << ":" << PP::bbox2 << PP::neverbreak << PP::newline;
1449 }
1450 ps << "force_initial(";
1451 ps.scopedBox(PP::ibox0, [&]() {
1452 interleaveComma({op.getDest(), op.getSrc()});
1453 ps << ")";
1454 });
1455 if (hasEnable)
1456 ps << PP::end;
1457 emitLocationAndNewLine(op);
1458}
1459
1460void Emitter::emitStatement(RefReleaseOp op) {
1461 emitStatementFunctionOp(PPExtString("release"), op);
1462}
1463
1464void Emitter::emitStatement(RefReleaseInitialOp op) {
1465 startStatement();
1466 auto constantPredicate =
1467 dyn_cast_or_null<ConstantOp>(op.getPredicate().getDefiningOp());
1468 bool hasEnable = !constantPredicate || constantPredicate.getValue() == 0;
1469 if (hasEnable) {
1470 ps << "when ";
1471 emitExpression(op.getPredicate());
1472 ps << ":" << PP::bbox2 << PP::neverbreak << PP::newline;
1473 }
1474 ps << "release_initial(";
1475 emitExpression(op.getDest());
1476 ps << ")";
1477 if (hasEnable)
1478 ps << PP::end;
1479 emitLocationAndNewLine(op);
1480}
1481
1482void Emitter::emitStatement(LayerBlockOp op) {
1483 if (failed(requireVersion(FIRVersion(3, 3, 0), op, "layers")))
1484 return;
1485 startStatement();
1486 ps << "layerblock " << op.getLayerName().getLeafReference() << " :";
1487 emitLocationAndNewLine(op);
1488 auto *body = op.getBody();
1489 ps.scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(*body); });
1490}
1491
1492void Emitter::emitStatement(InvalidValueOp op) {
1493 // Only emit this invalid value if it is used somewhere else than the RHS of
1494 // a connect.
1495 if (llvm::all_of(op->getUses(), [&](OpOperand &use) {
1496 return use.getOperandNumber() == 1 &&
1497 isa<ConnectOp, MatchingConnectOp>(use.getOwner());
1498 }))
1499 return;
1500
1501 // TODO: emitAssignLike ?
1502 startStatement();
1503 auto name = circuitNamespace.newName("_invalid");
1504 addValueName(op, name);
1505 ps << "wire " << PPExtString(name) << " : ";
1506 emitType(op.getType());
1507 emitLocationAndNewLine(op);
1508 startStatement();
1509 if (FIRVersion(3, 0, 0) <= version)
1510 ps << "invalidate " << PPExtString(name);
1511 else
1512 ps << PPExtString(name) << " is invalid";
1513 emitLocationAndNewLine(op);
1514}
1515
1516void Emitter::emitStatement(GenericIntrinsicOp op) {
1517 if (failed(requireVersion(FIRVersion(4, 0, 0), op, "generic intrinsics")))
1518 return;
1519 startStatement();
1520 if (op.use_empty())
1521 emitGenericIntrinsic(op);
1522 else {
1523 assert(!isEmittedInline(op));
1524 auto name = circuitNamespace.newName("_gen_int");
1525 addValueName(op.getResult(), name);
1526 emitAssignLike([&]() { ps << "node " << PPExtString(name); },
1527 [&]() { emitGenericIntrinsic(op); });
1528 }
1529 emitLocationAndNewLine(op);
1530}
1531
1532void Emitter::emitStatement(DomainCreateAnonOp op) {
1533 // These ops are not emitted.
1534}
1535
1536void Emitter::emitStatement(DomainCreateOp op) {
1537 if (failed(requireVersion(missingSpecFIRVersion, op, "domains")))
1538 return;
1539 startStatement();
1540 auto name = legalize(op.getNameAttr());
1541 addValueName(op.getResult(), name);
1542 ps.scopedBox(PP::ibox2, [&]() {
1543 ps << "domain " << PPExtString(name) << " of "
1544 << PPExtString(op.getDomainAttr().getValue());
1545
1546 auto fieldValues = op.getFieldValues();
1547 if (fieldValues.empty())
1548 return;
1549
1550 ps << "(" << PP::ibox0;
1551 interleaveComma(fieldValues, [&](auto value) { emitExpression(value); });
1552 ps << ")" << PP::end;
1553 });
1554
1555 emitLocationAndNewLine(op);
1556}
1557
1558void Emitter::emitExpression(Value value) {
1559 // Handle the trivial case where we already have a name for this value which
1560 // we can use.
1561 if (auto name = lookupEmittedName(value)) {
1562 // Don't use PPExtString here, can't trust valueNames storage, cleared.
1563 ps << *name;
1564 return;
1565 }
1566
1567 auto op = value.getDefiningOp();
1568 assert(op && "value must either be a block arg or the result of an op");
1569 TypeSwitch<Operation *>(op)
1570 .Case<
1571 // Basic expressions
1572 ConstantOp, SpecialConstantOp, SubfieldOp, SubindexOp, SubaccessOp,
1573 OpenSubfieldOp, OpenSubindexOp, DomainSubfieldOp,
1574 // Binary
1575 AddPrimOp, SubPrimOp, MulPrimOp, DivPrimOp, RemPrimOp, AndPrimOp,
1576 OrPrimOp, XorPrimOp, LEQPrimOp, LTPrimOp, GEQPrimOp, GTPrimOp,
1577 EQPrimOp, NEQPrimOp, DShlPrimOp, DShlwPrimOp, DShrPrimOp,
1578 // Unary
1579 AsSIntPrimOp, AsUIntPrimOp, AsAsyncResetPrimOp, AsResetPrimOp,
1580 AsClockPrimOp, CvtPrimOp, NegPrimOp, NotPrimOp, AndRPrimOp, OrRPrimOp,
1581 XorRPrimOp,
1582 // Miscellaneous
1583 BitsPrimOp, HeadPrimOp, TailPrimOp, PadPrimOp, MuxPrimOp, ShlPrimOp,
1584 ShrPrimOp, UninferredResetCastOp, ConstCastOp, StringConstantOp,
1585 FIntegerConstantOp, BoolConstantOp, DoubleConstantOp, ListCreateOp,
1586 UnresolvedPathOp, GenericIntrinsicOp, CatPrimOp, UnsafeDomainCastOp,
1587 UnknownValueOp, StringConcatOp, PropEqOp, BoolAndOp, BoolOrOp,
1588 BoolXorOp,
1589 // Reference expressions
1590 RefSendOp, RefResolveOp, RefSubOp, RWProbeOp, RefCastOp,
1591 // Format String expressions
1592 TimeOp>([&](auto op) {
1593 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op); });
1594 })
1595 .Default([&](auto op) {
1596 emitOpError(op, "not supported as expression");
1597 ps << "<unsupported-expr-" << PPExtString(op->getName().stripDialect())
1598 << ">";
1599 });
1600}
1601
1602void Emitter::emitExpression(ConstantOp op) {
1603 // Don't include 'const' on the type in a literal expression
1604 emitType(op.getType(), false);
1605 // TODO: Add option to control base-2/8/10/16 output here.
1606 ps << "(";
1607 ps.addAsString(op.getValue());
1608 ps << ")";
1609}
1610
1611void Emitter::emitExpression(SpecialConstantOp op) {
1612 auto emitInner = [&]() {
1613 ps << "UInt<1>(";
1614 ps.addAsString(op.getValue());
1615 ps << ")";
1616 };
1617 // TODO: Emit type decl for type alias.
1618 FIRRTLTypeSwitch<FIRRTLType>(type_cast<FIRRTLType>(op.getType()))
1619 .Case<ClockType>([&](auto type) {
1620 ps << "asClock(";
1621 emitInner();
1622 ps << ")";
1623 })
1624 .Case<ResetType>([&](auto type) { emitInner(); })
1625 .Case<AsyncResetType>([&](auto type) {
1626 ps << "asAsyncReset(";
1627 emitInner();
1628 ps << ")";
1629 });
1630}
1631
1632// NOLINTNEXTLINE(misc-no-recursion)
1633void Emitter::emitExpression(SubfieldOp op) {
1634 BundleType type = op.getInput().getType();
1635 emitExpression(op.getInput());
1636 ps << "." << legalize(type.getElementNameAttr(op.getFieldIndex()));
1637}
1638
1639// NOLINTNEXTLINE(misc-no-recursion)
1640void Emitter::emitExpression(SubindexOp op) {
1641 emitExpression(op.getInput());
1642 ps << "[";
1643 ps.addAsString(op.getIndex());
1644 ps << "]";
1645}
1646
1647// NOLINTNEXTLINE(misc-no-recursion)
1648void Emitter::emitExpression(SubaccessOp op) {
1649 emitExpression(op.getInput());
1650 ps << "[";
1651 emitExpression(op.getIndex());
1652 ps << "]";
1653}
1654
1655void Emitter::emitExpression(OpenSubfieldOp op) {
1656 auto type = op.getInput().getType();
1657 emitExpression(op.getInput());
1658 ps << "." << legalize(type.getElementNameAttr(op.getFieldIndex()));
1659}
1660
1661// NOLINTNEXTLINE(misc-no-recursion)
1662void Emitter::emitExpression(DomainSubfieldOp op) {
1663 emitExpression(op.getInput());
1664 ps << "." << legalize(op.getFieldName());
1665}
1666
1667void Emitter::emitExpression(OpenSubindexOp op) {
1668 emitExpression(op.getInput());
1669 ps << "[";
1670 ps.addAsString(op.getIndex());
1671 ps << "]";
1672}
1673
1674void Emitter::emitExpression(RefSendOp op) {
1675 ps << "probe(";
1676 emitExpression(op.getBase());
1677 ps << ")";
1678}
1679
1680void Emitter::emitExpression(RefResolveOp op) {
1681 ps << "read(";
1682 emitExpression(op.getRef());
1683 ps << ")";
1684}
1685
1686void Emitter::emitExpression(RefSubOp op) {
1687 emitExpression(op.getInput());
1688 FIRRTLTypeSwitch<FIRRTLBaseType, void>(op.getInput().getType().getType())
1689 .Case<FVectorType>([&](auto type) {
1690 ps << "[";
1691 ps.addAsString(op.getIndex());
1692 ps << "]";
1693 })
1694 .Case<BundleType>(
1695 [&](auto type) { ps << "." << type.getElementName(op.getIndex()); });
1696}
1697
1698void Emitter::emitExpression(RWProbeOp op) {
1699 ps << "rwprobe(";
1700
1701 // Find the probe target.
1702 auto target = symInfos->get().irn.lookup(op.getTarget());
1703 Value base;
1704 if (target.isPort()) {
1705 auto mod = cast<FModuleOp>(target.getOp());
1706 auto port = target.getPort();
1707 base = mod.getArgument(port);
1708 } else
1709 base = cast<hw::InnerSymbolOpInterface>(target.getOp()).getTargetResult();
1710
1711 // Print target. Needs this to have a name already.
1712 emitExpression(base);
1713
1714 // Print indexing for the target field.
1715 auto fieldID = target.getField();
1716 auto type = base.getType();
1717 while (fieldID) {
1719 .Case<FVectorType, OpenVectorType>([&](auto vecTy) {
1720 auto index = vecTy.getIndexForFieldID(fieldID);
1721 ps << "[";
1722 ps.addAsString(index);
1723 ps << "]";
1724 auto [subtype, subfieldID] = vecTy.getSubTypeByFieldID(fieldID);
1725 type = subtype;
1726 fieldID = subfieldID;
1727 })
1728 .Case<BundleType, OpenBundleType>([&](auto bundleTy) {
1729 auto index = bundleTy.getIndexForFieldID(fieldID);
1730 ps << "." << bundleTy.getElementName(index);
1731 auto [subtype, subfieldID] = bundleTy.getSubTypeByFieldID(fieldID);
1732 type = subtype;
1733 fieldID = subfieldID;
1734 });
1735 }
1736 ps << ")";
1737}
1738
1739void Emitter::emitExpression(RefCastOp op) { emitExpression(op.getInput()); }
1740
1741void Emitter::emitExpression(UninferredResetCastOp op) {
1742 emitExpression(op.getInput());
1743}
1744
1745void Emitter::emitExpression(FIntegerConstantOp op) {
1746 if (failed(requireVersion(FIRVersion(3, 1, 0), op, "Integers")))
1747 return;
1748 ps << "Integer(";
1749 ps.addAsString(op.getValue());
1750 ps << ")";
1751}
1752
1753void Emitter::emitExpression(BoolConstantOp op) {
1754 if (failed(requireVersion({6, 0, 0}, op, "Bools")))
1755 return;
1756 ps << "Bool(" << (op.getValue() ? "true" : "false") << ")";
1757}
1758
1759void Emitter::emitExpression(DoubleConstantOp op) {
1760 if (failed(requireVersion({6, 0, 0}, op, "Doubles")))
1761 return;
1762 ps << "Double(";
1763 // Use APFloat::toString.
1764 // Printing as double is not what we want,
1765 // and this at least handles the basic cases in a way
1766 // that will round-trip.
1767 SmallString<16> str;
1768 op.getValueAttr().getValue().toString(str);
1769 ps << str;
1770 ps << ")";
1771}
1772
1773void Emitter::emitExpression(StringConstantOp op) {
1774 if (failed(requireVersion(FIRVersion(3, 1, 0), op, "Strings")))
1775 return;
1776 ps << "String(";
1777 ps.writeQuotedEscaped(op.getValue());
1778 ps << ")";
1779}
1780
1781void Emitter::emitExpression(ListCreateOp op) {
1782 if (failed(requireVersion(FIRVersion(4, 0, 0), op, "Lists")))
1783 return;
1784 return emitLiteralExpression(op.getType(), op.getElements());
1785}
1786
1787void Emitter::emitExpression(UnresolvedPathOp op) {
1788 if (failed(requireVersion({6, 0, 0}, op, "Paths")))
1789 return;
1790 ps << "path(";
1791 ps.writeQuotedEscaped(op.getTarget());
1792 ps << ")";
1793}
1794
1795void Emitter::emitExpression(GenericIntrinsicOp op) {
1796 if (failed(requireVersion(FIRVersion(4, 0, 0), op, "generic intrinsics")))
1797 return;
1798 emitGenericIntrinsic(op);
1799}
1800
1801void Emitter::emitExpression(ConstCastOp op) { emitExpression(op.getInput()); }
1802
1803void Emitter::emitPrimExpr(StringRef mnemonic, Operation *op,
1804 ArrayRef<uint32_t> attrs) {
1805 ps << mnemonic << "(" << PP::ibox0;
1806 interleaveComma(op->getOperands());
1807 if (!op->getOperands().empty() && !attrs.empty())
1808 ps << "," << PP::space;
1809 interleaveComma(attrs, [&](auto attr) { ps.addAsString(attr); });
1810 ps << ")" << PP::end;
1811}
1812
1813void Emitter::emitExpression(CatPrimOp op) {
1814 size_t numOperands = op.getNumOperands();
1815 switch (numOperands) {
1816 case 0:
1817 // Emit "UInt<0>(0)"
1818 emitType(op.getType(), false);
1819 ps << "(0)";
1820 return;
1821 case 1: {
1822 auto operand = op->getOperand(0);
1823 // If there is no sign conversion, just emit the operand.
1824 if (isa<UIntType>(operand.getType()))
1825 return emitExpression(operand);
1826
1827 // Emit cat to convert sign.
1828 ps << "cat(" << PP::ibox0;
1829 emitExpression(op->getOperand(0));
1830 ps << "," << PP::space << "SInt<0>(0))" << PP::end;
1831 return;
1832 }
1833
1834 default:
1835 // Construct a linear tree of cats.
1836 for (size_t i = 0; i < numOperands - 1; ++i) {
1837 ps << "cat(" << PP::ibox0;
1838 emitExpression(op->getOperand(i));
1839 ps << "," << PP::space;
1840 }
1841
1842 emitExpression(op->getOperand(numOperands - 1));
1843 for (size_t i = 0; i < numOperands - 1; ++i)
1844 ps << ")" << PP::end;
1845 return;
1846 }
1847}
1848
1849void Emitter::emitExpression(UnsafeDomainCastOp op) {
1850 if (failed(requireVersion(nextFIRVersion, op, "unsafe_domain_cast")))
1851 return;
1852 ps << "unsafe_domain_cast(" << PP::ibox0;
1853 interleaveComma(op.getOperands(),
1854 [&](Value operand) { emitExpression(operand); });
1855 ps << ")" << PP::end;
1856}
1857
1858void Emitter::emitExpression(UnknownValueOp op) {
1859 if (failed(
1860 requireVersion(nextFIRVersion, op, "unknown property expressions")))
1861 return;
1862 ps << "Unknown(";
1863 emitType(op.getType());
1864 ps << ")";
1865}
1866
1867void Emitter::emitAttribute(MemDirAttr attr) {
1868 switch (attr) {
1869 case MemDirAttr::Infer:
1870 ps << "infer";
1871 break;
1872 case MemDirAttr::Read:
1873 ps << "read";
1874 break;
1875 case MemDirAttr::Write:
1876 ps << "write";
1877 break;
1878 case MemDirAttr::ReadWrite:
1879 ps << "rdwr";
1880 break;
1881 }
1882}
1883
1884void Emitter::emitAttribute(RUWBehaviorAttr attr) {
1885 switch (attr.getValue()) {
1886 case RUWBehavior::Undefined:
1887 ps << "undefined";
1888 break;
1889 case RUWBehavior::Old:
1890 ps << "old";
1891 break;
1892 case RUWBehavior::New:
1893 ps << "new";
1894 break;
1895 }
1896}
1897
1898/// Emit a FIRRTL type into the output.
1899void Emitter::emitType(Type type, bool includeConst) {
1900 if (includeConst && isConst(type))
1901 ps << "const ";
1902 auto emitWidth = [&](std::optional<int32_t> width) {
1903 if (width) {
1904 ps << "<";
1905 ps.addAsString(*width);
1906 ps << ">";
1907 }
1908 };
1909 // TODO: Emit type decl for type alias.
1911 .Case<ClockType>([&](auto) { ps << "Clock"; })
1912 .Case<ResetType>([&](auto) { ps << "Reset"; })
1913 .Case<AsyncResetType>([&](auto) { ps << "AsyncReset"; })
1914 .Case<UIntType>([&](auto type) {
1915 ps << "UInt";
1916 emitWidth(type.getWidth());
1917 })
1918 .Case<SIntType>([&](auto type) {
1919 ps << "SInt";
1920 emitWidth(type.getWidth());
1921 })
1922 .Case<AnalogType>([&](auto type) {
1923 ps << "Analog";
1924 emitWidth(type.getWidth());
1925 })
1926 .Case<OpenBundleType, BundleType>([&](auto type) {
1927 ps << "{";
1928 if (!type.getElements().empty())
1929 ps << PP::nbsp;
1930 bool anyEmitted = false;
1931 ps.scopedBox(PP::cbox0, [&]() {
1932 for (auto &element : type.getElements()) {
1933 if (anyEmitted)
1934 ps << "," << PP::space;
1935 ps.scopedBox(PP::ibox2, [&]() {
1936 if (element.isFlip)
1937 ps << "flip ";
1938 ps << legalize(element.name);
1939 emitTypeWithColon(element.type);
1940 anyEmitted = true;
1941 });
1942 }
1943 if (anyEmitted)
1944 ps << PP::nbsp;
1945 ps << "}";
1946 });
1947 })
1948 .Case<OpenVectorType, FVectorType, CMemoryType>([&](auto type) {
1949 emitType(type.getElementType());
1950 ps << "[";
1951 ps.addAsString(type.getNumElements());
1952 ps << "]";
1953 })
1954 .Case<RefType>([&](RefType type) {
1955 if (type.getForceable())
1956 ps << "RW";
1957 ps << "Probe<";
1958 ps.cbox(2, IndentStyle::Block);
1959 ps.zerobreak();
1960 emitType(type.getType());
1961 if (auto layer = type.getLayer()) {
1962 ps << ",";
1963 ps.space();
1964 emitSymbol(type.getLayer());
1965 }
1966 ps << BreakToken(0, -2) << ">";
1967 ps.end();
1968 })
1969 .Case<AnyRefType>([&](AnyRefType type) { ps << "AnyRef"; })
1970 .Case<StringType>([&](StringType type) { ps << "String"; })
1971 .Case<FIntegerType>([&](FIntegerType type) { ps << "Integer"; })
1972 .Case<BoolType>([&](BoolType type) { ps << "Bool"; })
1973 .Case<DoubleType>([&](DoubleType type) { ps << "Double"; })
1974 .Case<PathType>([&](PathType type) { ps << "Path"; })
1975 .Case<ListType>([&](ListType type) {
1976 ps << "List<";
1977 emitType(type.getElementType());
1978 ps << ">";
1979 })
1980 .Case<DomainType>([&](DomainType type) {
1981 ps << "Domain of " << PPExtString(type.getName().getValue());
1982 })
1983 .Default([&](auto type) {
1984 llvm_unreachable("all types should be implemented");
1985 });
1986}
1987
1988void Emitter::emitDomains(Attribute attr, ArrayRef<PortInfo> ports) {
1989 if (!attr)
1990 return;
1991 auto domains = cast<ArrayAttr>(attr);
1992 if (domains.empty())
1993 return;
1994 ps << " domains [";
1995 ps.scopedBox(PP::ibox0, [&]() {
1996 interleaveComma(domains, [&](Attribute attr) {
1997 ps.addAsString(ports[cast<IntegerAttr>(attr).getUInt()].name.getValue());
1998 });
1999 ps << "]";
2000 });
2001}
2002
2003/// Emit a location as `@[<filename> <line>:<column>]` annotation, including a
2004/// leading space.
2005void Emitter::emitLocation(Location loc) {
2006 // TODO: Handle FusedLoc and uniquify locations, avoid repeated file names.
2007 ps << PP::neverbreak;
2008 if (auto fileLoc =
2009 dyn_cast_or_null<FileLineColLoc, LocationAttr>(LocationAttr(loc))) {
2010 ps << " @[" << fileLoc.getFilename().getValue();
2011 if (auto line = fileLoc.getLine()) {
2012 ps << " ";
2013 ps.addAsString(line);
2014 if (auto col = fileLoc.getColumn()) {
2015 ps << ":";
2016 ps.addAsString(col);
2017 }
2018 }
2019 ps << "]";
2020 }
2021}
2022// NOLINTEND(misc-no-recursion)
2023
2024//===----------------------------------------------------------------------===//
2025// Driver
2026//===----------------------------------------------------------------------===//
2027
2028// Emit the specified FIRRTL circuit into the given output stream.
2029mlir::LogicalResult
2030circt::firrtl::exportFIRFile(mlir::ModuleOp module, llvm::raw_ostream &os,
2031 std::optional<size_t> targetLineLength,
2032 FIRVersion version) {
2033 if (version < minimumFIRVersion)
2034 return module.emitError("--firrtl-version ")
2035 << version << " is below the minimum supported "
2036 << "version " << minimumFIRVersion;
2037 Emitter emitter(os, version,
2038 targetLineLength.value_or(defaultTargetLineLength));
2039 for (auto &op : *module.getBody()) {
2040 if (auto circuitOp = dyn_cast<CircuitOp>(op))
2041 emitter.emitCircuit(circuitOp);
2042 }
2043 return emitter.finalize();
2044}
2045
2047 static llvm::cl::opt<size_t> targetLineLength(
2048 "target-line-length",
2049 llvm::cl::desc("Target line length for emitted .fir; 0 disables line "
2050 "wrapping"),
2051 llvm::cl::value_desc("number of chars"),
2052 llvm::cl::init(defaultTargetLineLength));
2053 static llvm::cl::opt<std::string> firrtlVersionStr(
2054 "firrtl-version",
2055 llvm::cl::desc("FIRRTL version to target (e.g. \"3.0.0\"). "
2056 "Defaults to the latest supported version."),
2057 llvm::cl::value_desc("major.minor.patch"), llvm::cl::init(""));
2058 static mlir::TranslateFromMLIRRegistration toFIR(
2059 "export-firrtl", "emit FIRRTL dialect operations to .fir output",
2060 [](ModuleOp module, llvm::raw_ostream &os) -> mlir::LogicalResult {
2061 FIRVersion version = exportFIRVersion;
2062 if (!firrtlVersionStr.empty()) {
2063 auto ver = FIRVersion::fromString(firrtlVersionStr);
2064 if (!ver)
2065 return module.emitError("invalid --firrtl-version: '")
2066 << firrtlVersionStr
2067 << "', expected format 'major.minor.patch'";
2068 version = *ver;
2069 }
2070 return exportFIRFile(module, os, targetLineLength, version);
2071 },
2072 [](mlir::DialectRegistry &registry) {
2073 registry.insert<chirrtl::CHIRRTLDialect>();
2074 registry.insert<firrtl::FIRRTLDialect>();
2075 });
2076}
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 nextFIRVersion(7, 0, 0)
The next version of FIRRTL that is not yet released.
constexpr FIRVersion exportFIRVersion
The version of FIRRTL that the exporter produces.
Definition FIRParser.h:151
void registerToFIRFileTranslation()
bool isConst(Type type)
Returns true if this is a 'const' type whose value is guaranteed to be unchanging at circuit executio...
constexpr FIRVersion missingSpecFIRVersion
A marker for parser features that are currently missing from the spec.
Definition FIRParser.h:147
bool isExpression(Operation *op)
Return true if the specified operation is a firrtl expression.
constexpr FIRVersion minimumFIRVersion(2, 0, 0)
The current minimum version of FIRRTL that the parser supports.
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:89
This class represents the namespace in which InnerRef's can be resolved.
String wrapper to indicate string has external storage.