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