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