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