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