CIRCT 23.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 startStatement();
1204 ps.scopedBox(PP::ibox2, [&]() {
1205 ps << "propassert" << PP::space;
1206 emitExpression(op.getCondition());
1207 ps << "," << PP::space;
1208 ps.writeQuotedEscaped(op.getMessage());
1209 });
1210 emitLocationAndNewLine(op);
1211}
1212
1213void Emitter::emitStatement(PropAssignOp op) {
1214 if (failed(requireVersion(FIRVersion(3, 1, 0), op, "properties")))
1215 return;
1216 startStatement();
1217 ps.scopedBox(PP::ibox2, [&]() {
1218 ps << "propassign" << PP::space;
1219 interleaveComma(op.getOperands());
1220 });
1221 emitLocationAndNewLine(op);
1222}
1223
1224void Emitter::emitStatement(InstanceOp op) {
1225 startStatement();
1226 auto legalName = legalize(op.getNameAttr());
1227 ps << "inst " << PPExtString(legalName) << " of "
1228 << PPExtString(legalize(op.getModuleNameAttr().getAttr()));
1229 emitLocationAndNewLine(op);
1230
1231 // Make sure we have a name like `<inst>.<port>` for each of the instance
1232 // result values.
1233 SmallString<16> portName(legalName);
1234 portName.push_back('.');
1235 unsigned baseLen = portName.size();
1236 for (unsigned i = 0, e = op.getNumResults(); i < e; ++i) {
1237 portName.append(legalize(op.getPortNameAttr(i)));
1238 addValueName(op.getResult(i), portName);
1239 portName.resize(baseLen);
1240 }
1241}
1242
1243void Emitter::emitStatement(InstanceChoiceOp op) {
1244 if (failed(requireVersion(missingSpecFIRVersion, op,
1245 "option groups/instance choices")))
1246 return;
1247 startStatement();
1248 auto legalName = legalize(op.getNameAttr());
1249 ps << "instchoice " << PPExtString(legalName) << " of "
1250 << PPExtString(legalize(op.getDefaultTargetAttr().getAttr())) << ", "
1251 << PPExtString(legalize(op.getOptionNameAttr())) << " :";
1252 emitLocation(op);
1253 ps.scopedBox(PP::bbox2, [&] {
1254 for (const auto &[optSym, targetSym] : op.getTargetChoices()) {
1255 ps << PP::newline;
1256 ps << PPExtString(legalize(optSym.getLeafReference()));
1257 ps << " => ";
1258 ps << PPExtString(legalize(targetSym.getAttr()));
1259 }
1260 });
1261 setPendingNewline();
1262
1263 SmallString<16> portName(legalName);
1264 portName.push_back('.');
1265 unsigned baseLen = portName.size();
1266 for (unsigned i = 0, e = op.getNumResults(); i < e; ++i) {
1267 portName.append(legalize(op.getPortNameAttr(i)));
1268 addValueName(op.getResult(i), portName);
1269 portName.resize(baseLen);
1270 }
1271}
1272
1273void Emitter::emitStatement(AttachOp op) {
1274 emitStatementFunctionOp(PPExtString("attach"), op);
1275}
1276
1277void Emitter::emitStatement(MemOp op) {
1278 auto legalName = legalize(op.getNameAttr());
1279 SmallString<16> portName(legalName);
1280 portName.push_back('.');
1281 auto portNameBaseLen = portName.size();
1282 for (auto result : llvm::zip(op.getResults(), op.getPortNames())) {
1283 portName.resize(portNameBaseLen);
1284 portName.append(legalize(cast<StringAttr>(std::get<1>(result))));
1285 addValueName(std::get<0>(result), portName);
1286 }
1287
1288 startStatement();
1289 ps << "mem " << PPExtString(legalName) << " :";
1290 emitLocationAndNewLine(op);
1291 ps.scopedBox(PP::bbox2, [&]() {
1292 startStatement();
1293 ps << "data-type => ";
1294 emitType(op.getDataType());
1295 ps << PP::newline;
1296 ps << "depth => ";
1297 ps.addAsString(op.getDepth());
1298 ps << PP::newline;
1299 ps << "read-latency => ";
1300 ps.addAsString(op.getReadLatency());
1301 ps << PP::newline;
1302 ps << "write-latency => ";
1303 ps.addAsString(op.getWriteLatency());
1304 ps << PP::newline;
1305
1306 SmallString<16> reader, writer, readwriter;
1307 for (std::pair<StringAttr, MemOp::PortKind> port : op.getPorts()) {
1308 auto add = [&](SmallString<16> &to, StringAttr name) {
1309 if (!to.empty())
1310 to.push_back(' ');
1311 to.append(name.getValue());
1312 };
1313 switch (port.second) {
1314 case MemOp::PortKind::Read:
1315 add(reader, legalize(port.first));
1316 break;
1317 case MemOp::PortKind::Write:
1318 add(writer, legalize(port.first));
1319 break;
1320 case MemOp::PortKind::ReadWrite:
1321 add(readwriter, legalize(port.first));
1322 break;
1323 case MemOp::PortKind::Debug:
1324 emitOpError(op, "has unsupported 'debug' port");
1325 return;
1326 }
1327 }
1328 if (!reader.empty())
1329 ps << "reader => " << reader << PP::newline;
1330 if (!writer.empty())
1331 ps << "writer => " << writer << PP::newline;
1332 if (!readwriter.empty())
1333 ps << "readwriter => " << readwriter << PP::newline;
1334
1335 ps << "read-under-write => ";
1336 emitAttribute(op.getRuwAttr());
1337 setPendingNewline();
1338 });
1339}
1340
1341void Emitter::emitStatement(SeqMemOp op) {
1342 startStatement();
1343 ps.scopedBox(PP::ibox2, [&]() {
1344 ps << "smem " << PPExtString(legalize(op.getNameAttr()));
1345 emitTypeWithColon(op.getType());
1346 ps << "," << PP::space;
1347 emitAttribute(op.getRuwAttr());
1348 });
1349 emitLocationAndNewLine(op);
1350}
1351
1352void Emitter::emitStatement(CombMemOp op) {
1353 startStatement();
1354 ps.scopedBox(PP::ibox2, [&]() {
1355 ps << "cmem " << PPExtString(legalize(op.getNameAttr()));
1356 emitTypeWithColon(op.getType());
1357 });
1358 emitLocationAndNewLine(op);
1359}
1360
1361void Emitter::emitStatement(MemoryPortOp op) {
1362 // Nothing to output for this operation.
1363 addValueName(op.getData(), legalize(op.getNameAttr()));
1364}
1365
1366void Emitter::emitStatement(MemoryDebugPortOp op) {
1367 // Nothing to output for this operation.
1368 addValueName(op.getData(), legalize(op.getNameAttr()));
1369}
1370
1371void Emitter::emitStatement(MemoryPortAccessOp op) {
1372 startStatement();
1373
1374 // Print the port direction and name.
1375 auto port = cast<MemoryPortOp>(op.getPort().getDefiningOp());
1376 emitAttribute(port.getDirection());
1377 // TODO: emitAssignLike
1378 ps << " mport " << PPExtString(legalize(port.getNameAttr())) << " = ";
1379
1380 // Print the memory name.
1381 auto *mem = port.getMemory().getDefiningOp();
1382 if (auto seqMem = dyn_cast<SeqMemOp>(mem))
1383 ps << legalize(seqMem.getNameAttr());
1384 else
1385 ps << legalize(cast<CombMemOp>(mem).getNameAttr());
1386
1387 // Print the address.
1388 ps << "[";
1389 emitExpression(op.getIndex());
1390 ps << "], ";
1391
1392 // Print the clock.
1393 emitExpression(op.getClock());
1394
1395 emitLocationAndNewLine(op);
1396}
1397
1398void Emitter::emitStatement(DomainDefineOp op) {
1399 if (failed(requireVersion(missingSpecFIRVersion, op, "domains")))
1400 return;
1401 // If the source is an anonymous domain, then we can skip emitting this op.
1402 if (isa_and_nonnull<DomainCreateAnonOp>(op.getSrc().getDefiningOp()))
1403 return;
1404
1405 startStatement();
1406 emitAssignLike([&]() { emitExpression(op.getDest()); },
1407 [&]() { emitExpression(op.getSrc()); }, PPExtString("="),
1408 PPExtString("domain_define"));
1409 emitLocationAndNewLine(op);
1410}
1411
1412void Emitter::emitStatement(RefDefineOp op) {
1413 startStatement();
1414 emitAssignLike([&]() { emitExpression(op.getDest()); },
1415 [&]() { emitExpression(op.getSrc()); }, PPExtString("="),
1416 PPExtString("define"));
1417 emitLocationAndNewLine(op);
1418}
1419
1420void Emitter::emitStatement(RefForceOp op) {
1421 emitStatementFunctionOp(PPExtString("force"), op);
1422}
1423
1424void Emitter::emitStatement(RefForceInitialOp op) {
1425 startStatement();
1426 auto constantPredicate =
1427 dyn_cast_or_null<ConstantOp>(op.getPredicate().getDefiningOp());
1428 bool hasEnable = !constantPredicate || constantPredicate.getValue() == 0;
1429 if (hasEnable) {
1430 ps << "when ";
1431 emitExpression(op.getPredicate());
1432 ps << ":" << PP::bbox2 << PP::neverbreak << PP::newline;
1433 }
1434 ps << "force_initial(";
1435 ps.scopedBox(PP::ibox0, [&]() {
1436 interleaveComma({op.getDest(), op.getSrc()});
1437 ps << ")";
1438 });
1439 if (hasEnable)
1440 ps << PP::end;
1441 emitLocationAndNewLine(op);
1442}
1443
1444void Emitter::emitStatement(RefReleaseOp op) {
1445 emitStatementFunctionOp(PPExtString("release"), op);
1446}
1447
1448void Emitter::emitStatement(RefReleaseInitialOp op) {
1449 startStatement();
1450 auto constantPredicate =
1451 dyn_cast_or_null<ConstantOp>(op.getPredicate().getDefiningOp());
1452 bool hasEnable = !constantPredicate || constantPredicate.getValue() == 0;
1453 if (hasEnable) {
1454 ps << "when ";
1455 emitExpression(op.getPredicate());
1456 ps << ":" << PP::bbox2 << PP::neverbreak << PP::newline;
1457 }
1458 ps << "release_initial(";
1459 emitExpression(op.getDest());
1460 ps << ")";
1461 if (hasEnable)
1462 ps << PP::end;
1463 emitLocationAndNewLine(op);
1464}
1465
1466void Emitter::emitStatement(LayerBlockOp op) {
1467 if (failed(requireVersion(FIRVersion(3, 3, 0), op, "layers")))
1468 return;
1469 startStatement();
1470 ps << "layerblock " << op.getLayerName().getLeafReference() << " :";
1471 emitLocationAndNewLine(op);
1472 auto *body = op.getBody();
1473 ps.scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(*body); });
1474}
1475
1476void Emitter::emitStatement(InvalidValueOp op) {
1477 // Only emit this invalid value if it is used somewhere else than the RHS of
1478 // a connect.
1479 if (llvm::all_of(op->getUses(), [&](OpOperand &use) {
1480 return use.getOperandNumber() == 1 &&
1481 isa<ConnectOp, MatchingConnectOp>(use.getOwner());
1482 }))
1483 return;
1484
1485 // TODO: emitAssignLike ?
1486 startStatement();
1487 auto name = circuitNamespace.newName("_invalid");
1488 addValueName(op, name);
1489 ps << "wire " << PPExtString(name) << " : ";
1490 emitType(op.getType());
1491 emitLocationAndNewLine(op);
1492 startStatement();
1493 if (FIRVersion(3, 0, 0) <= version)
1494 ps << "invalidate " << PPExtString(name);
1495 else
1496 ps << PPExtString(name) << " is invalid";
1497 emitLocationAndNewLine(op);
1498}
1499
1500void Emitter::emitStatement(GenericIntrinsicOp op) {
1501 if (failed(requireVersion(FIRVersion(4, 0, 0), op, "generic intrinsics")))
1502 return;
1503 startStatement();
1504 if (op.use_empty())
1505 emitGenericIntrinsic(op);
1506 else {
1507 assert(!isEmittedInline(op));
1508 auto name = circuitNamespace.newName("_gen_int");
1509 addValueName(op.getResult(), name);
1510 emitAssignLike([&]() { ps << "node " << PPExtString(name); },
1511 [&]() { emitGenericIntrinsic(op); });
1512 }
1513 emitLocationAndNewLine(op);
1514}
1515
1516void Emitter::emitStatement(DomainCreateAnonOp op) {
1517 // These ops are not emitted.
1518}
1519
1520void Emitter::emitStatement(DomainCreateOp op) {
1521 if (failed(requireVersion(missingSpecFIRVersion, op, "domains")))
1522 return;
1523 startStatement();
1524 auto name = legalize(op.getNameAttr());
1525 addValueName(op.getResult(), name);
1526 ps.scopedBox(PP::ibox2, [&]() {
1527 ps << "domain " << PPExtString(name) << " of "
1528 << PPExtString(op.getDomainAttr().getValue());
1529
1530 auto fieldValues = op.getFieldValues();
1531 if (fieldValues.empty())
1532 return;
1533
1534 ps << "(" << PP::ibox0;
1535 interleaveComma(fieldValues, [&](auto value) { emitExpression(value); });
1536 ps << ")" << PP::end;
1537 });
1538
1539 emitLocationAndNewLine(op);
1540}
1541
1542void Emitter::emitExpression(Value value) {
1543 // Handle the trivial case where we already have a name for this value which
1544 // we can use.
1545 if (auto name = lookupEmittedName(value)) {
1546 // Don't use PPExtString here, can't trust valueNames storage, cleared.
1547 ps << *name;
1548 return;
1549 }
1550
1551 auto op = value.getDefiningOp();
1552 assert(op && "value must either be a block arg or the result of an op");
1553 TypeSwitch<Operation *>(op)
1554 .Case<
1555 // Basic expressions
1556 ConstantOp, SpecialConstantOp, SubfieldOp, SubindexOp, SubaccessOp,
1557 OpenSubfieldOp, OpenSubindexOp, DomainSubfieldOp,
1558 // Binary
1559 AddPrimOp, SubPrimOp, MulPrimOp, DivPrimOp, RemPrimOp, AndPrimOp,
1560 OrPrimOp, XorPrimOp, LEQPrimOp, LTPrimOp, GEQPrimOp, GTPrimOp,
1561 EQPrimOp, NEQPrimOp, DShlPrimOp, DShlwPrimOp, DShrPrimOp,
1562 // Unary
1563 AsSIntPrimOp, AsUIntPrimOp, AsAsyncResetPrimOp, AsResetPrimOp,
1564 AsClockPrimOp, CvtPrimOp, NegPrimOp, NotPrimOp, AndRPrimOp, OrRPrimOp,
1565 XorRPrimOp,
1566 // Miscellaneous
1567 BitsPrimOp, HeadPrimOp, TailPrimOp, PadPrimOp, MuxPrimOp, ShlPrimOp,
1568 ShrPrimOp, UninferredResetCastOp, ConstCastOp, StringConstantOp,
1569 FIntegerConstantOp, BoolConstantOp, DoubleConstantOp, ListCreateOp,
1570 UnresolvedPathOp, GenericIntrinsicOp, CatPrimOp, UnsafeDomainCastOp,
1571 UnknownValueOp, StringConcatOp, PropEqOp, BoolAndOp, BoolOrOp,
1572 BoolXorOp,
1573 // Reference expressions
1574 RefSendOp, RefResolveOp, RefSubOp, RWProbeOp, RefCastOp,
1575 // Format String expressions
1576 TimeOp>([&](auto op) {
1577 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op); });
1578 })
1579 .Default([&](auto op) {
1580 emitOpError(op, "not supported as expression");
1581 ps << "<unsupported-expr-" << PPExtString(op->getName().stripDialect())
1582 << ">";
1583 });
1584}
1585
1586void Emitter::emitExpression(ConstantOp op) {
1587 // Don't include 'const' on the type in a literal expression
1588 emitType(op.getType(), false);
1589 // TODO: Add option to control base-2/8/10/16 output here.
1590 ps << "(";
1591 ps.addAsString(op.getValue());
1592 ps << ")";
1593}
1594
1595void Emitter::emitExpression(SpecialConstantOp op) {
1596 auto emitInner = [&]() {
1597 ps << "UInt<1>(";
1598 ps.addAsString(op.getValue());
1599 ps << ")";
1600 };
1601 // TODO: Emit type decl for type alias.
1602 FIRRTLTypeSwitch<FIRRTLType>(type_cast<FIRRTLType>(op.getType()))
1603 .Case<ClockType>([&](auto type) {
1604 ps << "asClock(";
1605 emitInner();
1606 ps << ")";
1607 })
1608 .Case<ResetType>([&](auto type) { emitInner(); })
1609 .Case<AsyncResetType>([&](auto type) {
1610 ps << "asAsyncReset(";
1611 emitInner();
1612 ps << ")";
1613 });
1614}
1615
1616// NOLINTNEXTLINE(misc-no-recursion)
1617void Emitter::emitExpression(SubfieldOp op) {
1618 BundleType type = op.getInput().getType();
1619 emitExpression(op.getInput());
1620 ps << "." << legalize(type.getElementNameAttr(op.getFieldIndex()));
1621}
1622
1623// NOLINTNEXTLINE(misc-no-recursion)
1624void Emitter::emitExpression(SubindexOp op) {
1625 emitExpression(op.getInput());
1626 ps << "[";
1627 ps.addAsString(op.getIndex());
1628 ps << "]";
1629}
1630
1631// NOLINTNEXTLINE(misc-no-recursion)
1632void Emitter::emitExpression(SubaccessOp op) {
1633 emitExpression(op.getInput());
1634 ps << "[";
1635 emitExpression(op.getIndex());
1636 ps << "]";
1637}
1638
1639void Emitter::emitExpression(OpenSubfieldOp op) {
1640 auto type = op.getInput().getType();
1641 emitExpression(op.getInput());
1642 ps << "." << legalize(type.getElementNameAttr(op.getFieldIndex()));
1643}
1644
1645// NOLINTNEXTLINE(misc-no-recursion)
1646void Emitter::emitExpression(DomainSubfieldOp op) {
1647 emitExpression(op.getInput());
1648 ps << "." << legalize(op.getFieldName());
1649}
1650
1651void Emitter::emitExpression(OpenSubindexOp op) {
1652 emitExpression(op.getInput());
1653 ps << "[";
1654 ps.addAsString(op.getIndex());
1655 ps << "]";
1656}
1657
1658void Emitter::emitExpression(RefSendOp op) {
1659 ps << "probe(";
1660 emitExpression(op.getBase());
1661 ps << ")";
1662}
1663
1664void Emitter::emitExpression(RefResolveOp op) {
1665 ps << "read(";
1666 emitExpression(op.getRef());
1667 ps << ")";
1668}
1669
1670void Emitter::emitExpression(RefSubOp op) {
1671 emitExpression(op.getInput());
1672 FIRRTLTypeSwitch<FIRRTLBaseType, void>(op.getInput().getType().getType())
1673 .Case<FVectorType>([&](auto type) {
1674 ps << "[";
1675 ps.addAsString(op.getIndex());
1676 ps << "]";
1677 })
1678 .Case<BundleType>(
1679 [&](auto type) { ps << "." << type.getElementName(op.getIndex()); });
1680}
1681
1682void Emitter::emitExpression(RWProbeOp op) {
1683 ps << "rwprobe(";
1684
1685 // Find the probe target.
1686 auto target = symInfos->get().irn.lookup(op.getTarget());
1687 Value base;
1688 if (target.isPort()) {
1689 auto mod = cast<FModuleOp>(target.getOp());
1690 auto port = target.getPort();
1691 base = mod.getArgument(port);
1692 } else
1693 base = cast<hw::InnerSymbolOpInterface>(target.getOp()).getTargetResult();
1694
1695 // Print target. Needs this to have a name already.
1696 emitExpression(base);
1697
1698 // Print indexing for the target field.
1699 auto fieldID = target.getField();
1700 auto type = base.getType();
1701 while (fieldID) {
1703 .Case<FVectorType, OpenVectorType>([&](auto vecTy) {
1704 auto index = vecTy.getIndexForFieldID(fieldID);
1705 ps << "[";
1706 ps.addAsString(index);
1707 ps << "]";
1708 auto [subtype, subfieldID] = vecTy.getSubTypeByFieldID(fieldID);
1709 type = subtype;
1710 fieldID = subfieldID;
1711 })
1712 .Case<BundleType, OpenBundleType>([&](auto bundleTy) {
1713 auto index = bundleTy.getIndexForFieldID(fieldID);
1714 ps << "." << bundleTy.getElementName(index);
1715 auto [subtype, subfieldID] = bundleTy.getSubTypeByFieldID(fieldID);
1716 type = subtype;
1717 fieldID = subfieldID;
1718 });
1719 }
1720 ps << ")";
1721}
1722
1723void Emitter::emitExpression(RefCastOp op) { emitExpression(op.getInput()); }
1724
1725void Emitter::emitExpression(UninferredResetCastOp op) {
1726 emitExpression(op.getInput());
1727}
1728
1729void Emitter::emitExpression(FIntegerConstantOp op) {
1730 if (failed(requireVersion(FIRVersion(3, 1, 0), op, "Integers")))
1731 return;
1732 ps << "Integer(";
1733 ps.addAsString(op.getValue());
1734 ps << ")";
1735}
1736
1737void Emitter::emitExpression(BoolConstantOp op) {
1738 if (failed(requireVersion({6, 0, 0}, op, "Bools")))
1739 return;
1740 ps << "Bool(" << (op.getValue() ? "true" : "false") << ")";
1741}
1742
1743void Emitter::emitExpression(DoubleConstantOp op) {
1744 if (failed(requireVersion({6, 0, 0}, op, "Doubles")))
1745 return;
1746 ps << "Double(";
1747 // Use APFloat::toString.
1748 // Printing as double is not what we want,
1749 // and this at least handles the basic cases in a way
1750 // that will round-trip.
1751 SmallString<16> str;
1752 op.getValueAttr().getValue().toString(str);
1753 ps << str;
1754 ps << ")";
1755}
1756
1757void Emitter::emitExpression(StringConstantOp op) {
1758 if (failed(requireVersion(FIRVersion(3, 1, 0), op, "Strings")))
1759 return;
1760 ps << "String(";
1761 ps.writeQuotedEscaped(op.getValue());
1762 ps << ")";
1763}
1764
1765void Emitter::emitExpression(ListCreateOp op) {
1766 if (failed(requireVersion(FIRVersion(4, 0, 0), op, "Lists")))
1767 return;
1768 return emitLiteralExpression(op.getType(), op.getElements());
1769}
1770
1771void Emitter::emitExpression(UnresolvedPathOp op) {
1772 if (failed(requireVersion({6, 0, 0}, op, "Paths")))
1773 return;
1774 ps << "path(";
1775 ps.writeQuotedEscaped(op.getTarget());
1776 ps << ")";
1777}
1778
1779void Emitter::emitExpression(GenericIntrinsicOp op) {
1780 if (failed(requireVersion(FIRVersion(4, 0, 0), op, "generic intrinsics")))
1781 return;
1782 emitGenericIntrinsic(op);
1783}
1784
1785void Emitter::emitExpression(ConstCastOp op) { emitExpression(op.getInput()); }
1786
1787void Emitter::emitPrimExpr(StringRef mnemonic, Operation *op,
1788 ArrayRef<uint32_t> attrs) {
1789 ps << mnemonic << "(" << PP::ibox0;
1790 interleaveComma(op->getOperands());
1791 if (!op->getOperands().empty() && !attrs.empty())
1792 ps << "," << PP::space;
1793 interleaveComma(attrs, [&](auto attr) { ps.addAsString(attr); });
1794 ps << ")" << PP::end;
1795}
1796
1797void Emitter::emitExpression(CatPrimOp op) {
1798 size_t numOperands = op.getNumOperands();
1799 switch (numOperands) {
1800 case 0:
1801 // Emit "UInt<0>(0)"
1802 emitType(op.getType(), false);
1803 ps << "(0)";
1804 return;
1805 case 1: {
1806 auto operand = op->getOperand(0);
1807 // If there is no sign conversion, just emit the operand.
1808 if (isa<UIntType>(operand.getType()))
1809 return emitExpression(operand);
1810
1811 // Emit cat to convert sign.
1812 ps << "cat(" << PP::ibox0;
1813 emitExpression(op->getOperand(0));
1814 ps << "," << PP::space << "SInt<0>(0))" << PP::end;
1815 return;
1816 }
1817
1818 default:
1819 // Construct a linear tree of cats.
1820 for (size_t i = 0; i < numOperands - 1; ++i) {
1821 ps << "cat(" << PP::ibox0;
1822 emitExpression(op->getOperand(i));
1823 ps << "," << PP::space;
1824 }
1825
1826 emitExpression(op->getOperand(numOperands - 1));
1827 for (size_t i = 0; i < numOperands - 1; ++i)
1828 ps << ")" << PP::end;
1829 return;
1830 }
1831}
1832
1833void Emitter::emitExpression(UnsafeDomainCastOp op) {
1834 if (failed(requireVersion(nextFIRVersion, op, "unsafe_domain_cast")))
1835 return;
1836 ps << "unsafe_domain_cast(" << PP::ibox0;
1837 interleaveComma(op.getOperands(),
1838 [&](Value operand) { emitExpression(operand); });
1839 ps << ")" << PP::end;
1840}
1841
1842void Emitter::emitExpression(UnknownValueOp op) {
1843 if (failed(
1844 requireVersion(nextFIRVersion, op, "unknown property expressions")))
1845 return;
1846 ps << "Unknown(";
1847 emitType(op.getType());
1848 ps << ")";
1849}
1850
1851void Emitter::emitAttribute(MemDirAttr attr) {
1852 switch (attr) {
1853 case MemDirAttr::Infer:
1854 ps << "infer";
1855 break;
1856 case MemDirAttr::Read:
1857 ps << "read";
1858 break;
1859 case MemDirAttr::Write:
1860 ps << "write";
1861 break;
1862 case MemDirAttr::ReadWrite:
1863 ps << "rdwr";
1864 break;
1865 }
1866}
1867
1868void Emitter::emitAttribute(RUWBehaviorAttr attr) {
1869 switch (attr.getValue()) {
1870 case RUWBehavior::Undefined:
1871 ps << "undefined";
1872 break;
1873 case RUWBehavior::Old:
1874 ps << "old";
1875 break;
1876 case RUWBehavior::New:
1877 ps << "new";
1878 break;
1879 }
1880}
1881
1882/// Emit a FIRRTL type into the output.
1883void Emitter::emitType(Type type, bool includeConst) {
1884 if (includeConst && isConst(type))
1885 ps << "const ";
1886 auto emitWidth = [&](std::optional<int32_t> width) {
1887 if (width) {
1888 ps << "<";
1889 ps.addAsString(*width);
1890 ps << ">";
1891 }
1892 };
1893 // TODO: Emit type decl for type alias.
1895 .Case<ClockType>([&](auto) { ps << "Clock"; })
1896 .Case<ResetType>([&](auto) { ps << "Reset"; })
1897 .Case<AsyncResetType>([&](auto) { ps << "AsyncReset"; })
1898 .Case<UIntType>([&](auto type) {
1899 ps << "UInt";
1900 emitWidth(type.getWidth());
1901 })
1902 .Case<SIntType>([&](auto type) {
1903 ps << "SInt";
1904 emitWidth(type.getWidth());
1905 })
1906 .Case<AnalogType>([&](auto type) {
1907 ps << "Analog";
1908 emitWidth(type.getWidth());
1909 })
1910 .Case<OpenBundleType, BundleType>([&](auto type) {
1911 ps << "{";
1912 if (!type.getElements().empty())
1913 ps << PP::nbsp;
1914 bool anyEmitted = false;
1915 ps.scopedBox(PP::cbox0, [&]() {
1916 for (auto &element : type.getElements()) {
1917 if (anyEmitted)
1918 ps << "," << PP::space;
1919 ps.scopedBox(PP::ibox2, [&]() {
1920 if (element.isFlip)
1921 ps << "flip ";
1922 ps << legalize(element.name);
1923 emitTypeWithColon(element.type);
1924 anyEmitted = true;
1925 });
1926 }
1927 if (anyEmitted)
1928 ps << PP::nbsp;
1929 ps << "}";
1930 });
1931 })
1932 .Case<OpenVectorType, FVectorType, CMemoryType>([&](auto type) {
1933 emitType(type.getElementType());
1934 ps << "[";
1935 ps.addAsString(type.getNumElements());
1936 ps << "]";
1937 })
1938 .Case<RefType>([&](RefType type) {
1939 if (type.getForceable())
1940 ps << "RW";
1941 ps << "Probe<";
1942 ps.cbox(2, IndentStyle::Block);
1943 ps.zerobreak();
1944 emitType(type.getType());
1945 if (auto layer = type.getLayer()) {
1946 ps << ",";
1947 ps.space();
1948 emitSymbol(type.getLayer());
1949 }
1950 ps << BreakToken(0, -2) << ">";
1951 ps.end();
1952 })
1953 .Case<AnyRefType>([&](AnyRefType type) { ps << "AnyRef"; })
1954 .Case<StringType>([&](StringType type) { ps << "String"; })
1955 .Case<FIntegerType>([&](FIntegerType type) { ps << "Integer"; })
1956 .Case<BoolType>([&](BoolType type) { ps << "Bool"; })
1957 .Case<DoubleType>([&](DoubleType type) { ps << "Double"; })
1958 .Case<PathType>([&](PathType type) { ps << "Path"; })
1959 .Case<ListType>([&](ListType type) {
1960 ps << "List<";
1961 emitType(type.getElementType());
1962 ps << ">";
1963 })
1964 .Case<DomainType>([&](DomainType type) {
1965 ps << "Domain of " << PPExtString(type.getName().getValue());
1966 })
1967 .Default([&](auto type) {
1968 llvm_unreachable("all types should be implemented");
1969 });
1970}
1971
1972void Emitter::emitDomains(Attribute attr, ArrayRef<PortInfo> ports) {
1973 if (!attr)
1974 return;
1975 auto domains = cast<ArrayAttr>(attr);
1976 if (domains.empty())
1977 return;
1978 ps << " domains [";
1979 ps.scopedBox(PP::ibox0, [&]() {
1980 interleaveComma(domains, [&](Attribute attr) {
1981 ps.addAsString(ports[cast<IntegerAttr>(attr).getUInt()].name.getValue());
1982 });
1983 ps << "]";
1984 });
1985}
1986
1987/// Emit a location as `@[<filename> <line>:<column>]` annotation, including a
1988/// leading space.
1989void Emitter::emitLocation(Location loc) {
1990 // TODO: Handle FusedLoc and uniquify locations, avoid repeated file names.
1991 ps << PP::neverbreak;
1992 if (auto fileLoc =
1993 dyn_cast_or_null<FileLineColLoc, LocationAttr>(LocationAttr(loc))) {
1994 ps << " @[" << fileLoc.getFilename().getValue();
1995 if (auto line = fileLoc.getLine()) {
1996 ps << " ";
1997 ps.addAsString(line);
1998 if (auto col = fileLoc.getColumn()) {
1999 ps << ":";
2000 ps.addAsString(col);
2001 }
2002 }
2003 ps << "]";
2004 }
2005}
2006// NOLINTEND(misc-no-recursion)
2007
2008//===----------------------------------------------------------------------===//
2009// Driver
2010//===----------------------------------------------------------------------===//
2011
2012// Emit the specified FIRRTL circuit into the given output stream.
2013mlir::LogicalResult
2014circt::firrtl::exportFIRFile(mlir::ModuleOp module, llvm::raw_ostream &os,
2015 std::optional<size_t> targetLineLength,
2016 FIRVersion version) {
2017 if (version < minimumFIRVersion)
2018 return module.emitError("--firrtl-version ")
2019 << version << " is below the minimum supported "
2020 << "version " << minimumFIRVersion;
2021 Emitter emitter(os, version,
2022 targetLineLength.value_or(defaultTargetLineLength));
2023 for (auto &op : *module.getBody()) {
2024 if (auto circuitOp = dyn_cast<CircuitOp>(op))
2025 emitter.emitCircuit(circuitOp);
2026 }
2027 return emitter.finalize();
2028}
2029
2031 static llvm::cl::opt<size_t> targetLineLength(
2032 "target-line-length",
2033 llvm::cl::desc("Target line length for emitted .fir"),
2034 llvm::cl::value_desc("number of chars"),
2035 llvm::cl::init(defaultTargetLineLength));
2036 static llvm::cl::opt<std::string> firrtlVersionStr(
2037 "firrtl-version",
2038 llvm::cl::desc("FIRRTL version to target (e.g. \"3.0.0\"). "
2039 "Defaults to the latest supported version."),
2040 llvm::cl::value_desc("major.minor.patch"), llvm::cl::init(""));
2041 static mlir::TranslateFromMLIRRegistration toFIR(
2042 "export-firrtl", "emit FIRRTL dialect operations to .fir output",
2043 [](ModuleOp module, llvm::raw_ostream &os) -> mlir::LogicalResult {
2044 FIRVersion version = exportFIRVersion;
2045 if (!firrtlVersionStr.empty()) {
2046 auto ver = FIRVersion::fromString(firrtlVersionStr);
2047 if (!ver)
2048 return module.emitError("invalid --firrtl-version: '")
2049 << firrtlVersionStr
2050 << "', expected format 'major.minor.patch'";
2051 version = *ver;
2052 }
2053 return exportFIRFile(module, os, targetLineLength, version);
2054 },
2055 [](mlir::DialectRegistry &registry) {
2056 registry.insert<chirrtl::CHIRRTLDialect>();
2057 registry.insert<firrtl::FIRRTLDialect>();
2058 });
2059}
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:150
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:146
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:88
This class represents the namespace in which InnerRef's can be resolved.
String wrapper to indicate string has external storage.