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