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