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