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