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
114 template <class T>
115 void emitVerifStatement(T op, StringRef mnemonic);
116 void emitStatement(AssertOp op) { emitVerifStatement(op, "assert"); }
117 void emitStatement(AssumeOp op) { emitVerifStatement(op, "assume"); }
118 void emitStatement(CoverOp op) { emitVerifStatement(op, "cover"); }
119
120 // Exprsesion emission
121 void emitExpression(Value value);
122 void emitExpression(ConstantOp op);
123 void emitExpression(SpecialConstantOp op);
124 void emitExpression(SubfieldOp op);
125 void emitExpression(SubindexOp op);
126 void emitExpression(SubaccessOp op);
127 void emitExpression(OpenSubfieldOp op);
128 void emitExpression(OpenSubindexOp op);
129 void emitExpression(RefResolveOp op);
130 void emitExpression(RefSendOp op);
131 void emitExpression(RefSubOp op);
132 void emitExpression(RWProbeOp op);
133 void emitExpression(RefCastOp op);
134 void emitExpression(UninferredResetCastOp op);
135 void emitExpression(ConstCastOp op);
136 void emitExpression(StringConstantOp op);
137 void emitExpression(FIntegerConstantOp op);
138 void emitExpression(BoolConstantOp op);
139 void emitExpression(DoubleConstantOp op);
140 void emitExpression(ListCreateOp op);
141 void emitExpression(UnresolvedPathOp op);
142 void emitExpression(GenericIntrinsicOp op);
143 void emitExpression(CatPrimOp op);
144 void emitExpression(UnsafeDomainCastOp op);
145
146 void emitPrimExpr(StringRef mnemonic, Operation *op,
147 ArrayRef<uint32_t> attrs = {});
148
149 void emitExpression(BitsPrimOp op) {
150 emitPrimExpr("bits", op, {op.getHi(), op.getLo()});
151 }
152 void emitExpression(HeadPrimOp op) {
153 emitPrimExpr("head", op, op.getAmount());
154 }
155 void emitExpression(TailPrimOp op) {
156 emitPrimExpr("tail", op, op.getAmount());
157 }
158 void emitExpression(PadPrimOp op) { emitPrimExpr("pad", op, op.getAmount()); }
159 void emitExpression(ShlPrimOp op) { emitPrimExpr("shl", op, op.getAmount()); }
160 void emitExpression(ShrPrimOp op) { emitPrimExpr("shr", op, op.getAmount()); }
161
162 void emitExpression(TimeOp op){};
163
164 // Funnel all ops without attrs into `emitPrimExpr`.
165#define HANDLE(OPTYPE, MNEMONIC) \
166 void emitExpression(OPTYPE op) { emitPrimExpr(MNEMONIC, op); }
167 HANDLE(AddPrimOp, "add");
168 HANDLE(SubPrimOp, "sub");
169 HANDLE(MulPrimOp, "mul");
170 HANDLE(DivPrimOp, "div");
171 HANDLE(RemPrimOp, "rem");
172 HANDLE(AndPrimOp, "and");
173 HANDLE(OrPrimOp, "or");
174 HANDLE(XorPrimOp, "xor");
175 HANDLE(LEQPrimOp, "leq");
176 HANDLE(LTPrimOp, "lt");
177 HANDLE(GEQPrimOp, "geq");
178 HANDLE(GTPrimOp, "gt");
179 HANDLE(EQPrimOp, "eq");
180 HANDLE(NEQPrimOp, "neq");
181 HANDLE(DShlPrimOp, "dshl");
182 HANDLE(DShlwPrimOp, "dshlw");
183 HANDLE(DShrPrimOp, "dshr");
184 HANDLE(MuxPrimOp, "mux");
185 HANDLE(AsSIntPrimOp, "asSInt");
186 HANDLE(AsUIntPrimOp, "asUInt");
187 HANDLE(AsAsyncResetPrimOp, "asAsyncReset");
188 HANDLE(AsClockPrimOp, "asClock");
189 HANDLE(CvtPrimOp, "cvt");
190 HANDLE(NegPrimOp, "neg");
191 HANDLE(NotPrimOp, "not");
192 HANDLE(AndRPrimOp, "andr");
193 HANDLE(OrRPrimOp, "orr");
194 HANDLE(XorRPrimOp, "xorr");
195#undef HANDLE
196
197 // Attributes
198 void emitAttribute(MemDirAttr attr);
199 void emitAttribute(RUWBehaviorAttr attr);
200
201 // Types
202 void emitType(Type type, bool includeConst = true);
203 void emitTypeWithColon(Type type) {
204 ps << PP::space << ":" << PP::nbsp;
205 emitType(type);
206 }
207
208 // Domains
209 void emitDomains(Attribute domains,
210 const DenseMap<size_t, StringRef> &domainMap);
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 DenseMap<size_t, StringRef> domainMap;
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 (isa<DomainType>(port.type))
625 domainMap.insert({i, port.name});
626 if (!arguments.empty())
627 addValueName(arguments[i], legalName);
628 ps << PPExtString(legalName) << " : ";
629 emitType(port.type);
630 emitDomains(port.domains, domainMap);
631 emitLocation(ports[i].loc);
632 setPendingNewline();
633 }
634}
635
636void Emitter::emitModuleParameters(Operation *op, ArrayAttr parameters) {
637 for (auto param : parameters.getAsRange<ParamDeclAttr>()) {
638 startStatement();
639 emitParamAssign(param, op, PPExtString("parameter"));
640 setPendingNewline();
641 }
642}
643
644void Emitter::emitDeclaration(DomainOp op) {
645 startStatement();
646 ps << "domain " << PPExtString(op.getSymName()) << " :";
647 emitLocationAndNewLine(op);
648 ps.scopedBox(PP::bbox2, [&]() {
649 for (auto attr : op.getFields()) {
650 auto fieldAttr = cast<DomainFieldAttr>(attr);
651 ps << PP::newline << PPExtString(fieldAttr.getName()) << " : ";
652 emitType(fieldAttr.getType());
653 }
654 });
655}
656
657/// Emit a layer definition.
658void Emitter::emitDeclaration(LayerOp op) {
659 startStatement();
660 ps << "layer " << PPExtString(op.getSymName()) << ", "
661 << PPExtString(stringifyLayerConvention(op.getConvention()));
662
663 if (auto outputFile = op->getAttrOfType<hw::OutputFileAttr>("output_file")) {
664 ps << ", ";
665 ps.writeQuotedEscaped(outputFile.getFilename().getValue());
666 }
667
668 ps << " : ";
669 emitLocationAndNewLine(op);
670 ps.scopedBox(PP::bbox2, [&]() {
671 for (auto &bodyOp : op.getBody().getOps()) {
672 TypeSwitch<Operation *>(&bodyOp)
673 .Case<LayerOp>([&](auto op) { emitDeclaration(op); })
674 .Default([&](auto op) {
675 emitOpError(op,
676 "not supported for emission inside layer definition");
677 });
678 }
679 });
680}
681
682/// Emit an option declaration.
683void Emitter::emitDeclaration(OptionOp op) {
684 startStatement();
685 ps << "option " << PPExtString(legalize(op.getSymNameAttr())) << " :";
686 emitLocation(op);
687 ps.scopedBox(PP::bbox2, [&] {
688 for (auto caseOp : op.getBody().getOps<OptionCaseOp>()) {
689 ps << PP::newline;
690 ps << PPExtString(legalize(caseOp.getSymNameAttr()));
691 emitLocation(caseOp);
692 }
693 });
694 ps << PP::newline << PP::newline;
695}
696
697/// Emit a formal test definition.
698void Emitter::emitDeclaration(FormalOp op) {
699 emitFormalLike(op, "formal", op.getSymNameAttr(),
700 op.getModuleNameAttr().getAttr(), op.getParameters());
701}
702
703/// Emit a simulation test definition.
704void Emitter::emitDeclaration(SimulationOp op) {
705 emitFormalLike(op, "simulation", op.getSymNameAttr(),
706 op.getModuleNameAttr().getAttr(), op.getParameters());
707}
708
709/// Emit a formal or simulation test definition.
710void Emitter::emitFormalLike(Operation *op, StringRef keyword,
711 StringAttr symName, StringAttr moduleName,
712 DictionaryAttr params) {
713 startStatement();
714 ps.cbox(4, IndentStyle::Block);
715 ps << keyword << " " << PPExtString(legalize(symName));
716 ps << " of " << PPExtString(legalize(moduleName));
717 ps << PP::nbsp << ":" << PP::end;
718 emitLocation(op);
719
720 ps.scopedBox(PP::bbox2, [&]() {
721 setPendingNewline();
722 for (auto param : params) {
723 startStatement();
724 ps << PPExtString(param.getName()) << PP::nbsp << "=" << PP::nbsp;
725 emitParamValue(param.getValue(), op);
726 setPendingNewline();
727 }
728 });
729}
730
731/// Check if an operation is inlined into the emission of their users. For
732/// example, subfields are always inlined.
733static bool isEmittedInline(Operation *op) {
734 // FIRRTL expressions are statically classified as always inlineable.
735 // InvalidValueOp never is inlined, and is handled specially.
736 // GenericIntrinsicOp is inlined if has exactly one use (only emit once)
737 // that is not emitted inline. This is to ensure it is emitted inline
738 // in common cases, but only inspect one level deep.
739 return (isExpression(op) && !isa<InvalidValueOp>(op)) ||
740 (isa<GenericIntrinsicOp>(op) && op->hasOneUse() &&
741 !isEmittedInline(*op->getUsers().begin()));
742}
743
744void Emitter::emitStatementsInBlock(Block &block) {
745 for (auto &bodyOp : block) {
746 if (encounteredError)
747 return;
748 if (isEmittedInline(&bodyOp))
749 continue;
750 TypeSwitch<Operation *>(&bodyOp)
751 .Case<WhenOp, WireOp, RegOp, RegResetOp, NodeOp, StopOp, SkipOp,
752 PrintFOp, FPrintFOp, FFlushOp, AssertOp, AssumeOp, CoverOp,
753 ConnectOp, MatchingConnectOp, PropAssignOp, InstanceOp,
754 InstanceChoiceOp, AttachOp, MemOp, InvalidValueOp, SeqMemOp,
755 CombMemOp, MemoryPortOp, MemoryDebugPortOp, MemoryPortAccessOp,
756 DomainDefineOp, RefDefineOp, RefForceOp, RefForceInitialOp,
757 RefReleaseOp, RefReleaseInitialOp, LayerBlockOp,
758 GenericIntrinsicOp>([&](auto op) { emitStatement(op); })
759 .Default([&](auto op) {
760 startStatement();
761 ps << "// operation " << PPExtString(op->getName().getStringRef());
762 setPendingNewline();
763 emitOpError(op, "not supported as statement");
764 });
765 }
766}
767
768void Emitter::emitStatement(WhenOp op) {
769 startStatement();
770 ps << "when ";
771 emitExpression(op.getCondition());
772 ps << " :";
773 emitLocationAndNewLine(op);
774 ps.scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(op.getThenBlock()); });
775 // emitStatementsInBlock(op.getThenBlock());
776 if (!op.hasElseRegion())
777 return;
778
779 startStatement();
780 ps << "else ";
781 // Sugar to `else when ...` if there's only a single when statement in the
782 // else block.
783 auto &elseBlock = op.getElseBlock();
784 if (!elseBlock.empty() && &elseBlock.front() == &elseBlock.back()) {
785 if (auto whenOp = dyn_cast<WhenOp>(&elseBlock.front())) {
786 emitStatement(whenOp);
787 return;
788 }
789 }
790 // Otherwise print the block as `else :`.
791 ps << ":";
792 setPendingNewline();
793 ps.scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(elseBlock); });
794}
795
796void Emitter::emitStatement(WireOp op) {
797 auto legalName = legalize(op.getNameAttr());
798 addForceable(op, legalName);
799 startStatement();
800 ps.scopedBox(PP::ibox2, [&]() {
801 ps << "wire " << PPExtString(legalName);
802 emitTypeWithColon(op.getResult().getType());
803 });
804 emitLocationAndNewLine(op);
805}
806
807void Emitter::emitStatement(RegOp op) {
808 auto legalName = legalize(op.getNameAttr());
809 addForceable(op, legalName);
810 startStatement();
811 ps.scopedBox(PP::ibox2, [&]() {
812 ps << "reg " << PPExtString(legalName);
813 emitTypeWithColon(op.getResult().getType());
814 ps << "," << PP::space;
815 emitExpression(op.getClockVal());
816 });
817 emitLocationAndNewLine(op);
818}
819
820void Emitter::emitStatement(RegResetOp op) {
821 auto legalName = legalize(op.getNameAttr());
822 addForceable(op, legalName);
823 startStatement();
824 if (FIRVersion(3, 0, 0) <= version) {
825 ps.scopedBox(PP::ibox2, [&]() {
826 ps << "regreset " << legalName;
827 emitTypeWithColon(op.getResult().getType());
828 ps << "," << PP::space;
829 emitExpression(op.getClockVal());
830 ps << "," << PP::space;
831 emitExpression(op.getResetSignal());
832 ps << "," << PP::space;
833 emitExpression(op.getResetValue());
834 });
835 } else {
836 ps.scopedBox(PP::ibox2, [&]() {
837 ps << "reg " << legalName;
838 emitTypeWithColon(op.getResult().getType());
839 ps << "," << PP::space;
840 emitExpression(op.getClockVal());
841 ps << PP::space << "with :";
842 // Don't break this because of the newline.
843 ps << PP::neverbreak;
844 // No-paren version must be newline + indent.
845 ps << PP::newline; // ibox2 will indent.
846 ps << "reset => (" << PP::ibox0;
847 emitExpression(op.getResetSignal());
848 ps << "," << PP::space;
849 emitExpression(op.getResetValue());
850 ps << ")" << PP::end;
851 });
852 }
853 emitLocationAndNewLine(op);
854}
855
856void Emitter::emitStatement(NodeOp op) {
857 auto legalName = legalize(op.getNameAttr());
858 addForceable(op, legalName);
859 startStatement();
860 emitAssignLike([&]() { ps << "node " << PPExtString(legalName); },
861 [&]() { emitExpression(op.getInput()); });
862 emitLocationAndNewLine(op);
863}
864
865void Emitter::emitStatement(StopOp op) {
866 startStatement();
867 ps.scopedBox(PP::ibox2, [&]() {
868 ps << "stop(" << PP::ibox0;
869 emitExpression(op.getClock());
870 ps << "," << PP::space;
871 emitExpression(op.getCond());
872 ps << "," << PP::space;
873 ps.addAsString(op.getExitCode());
874 ps << ")" << PP::end;
875 if (!op.getName().empty()) {
876 ps << PP::space << ": " << PPExtString(legalize(op.getNameAttr()));
877 }
878 });
879 emitLocationAndNewLine(op);
880}
881
882void Emitter::emitStatement(SkipOp op) {
883 startStatement();
884 ps << "skip";
885 emitLocationAndNewLine(op);
886}
887
888void Emitter::emitFormatString(Operation *op, StringRef origFormatString,
889 OperandRange substitutionOperands,
890 llvm::SmallVectorImpl<Value> &substitutions) {
891 // Replace the generic "{{}}" special substitutions with their attributes.
892 // E.g.:
893 //
894 // "hello {{}} world"(%time)
895 //
896 // Becomes:
897 //
898 // "hello {{SimulationTime}} world"
899 SmallString<64> formatString;
900 for (size_t i = 0, e = origFormatString.size(), opIdx = 0; i != e; ++i) {
901 auto c = origFormatString[i];
902 switch (c) {
903 case '%': {
904 formatString.push_back(c);
905
906 // Parse the width specifier.
907 SmallString<6> width;
908 c = origFormatString[++i];
909 while (isdigit(c)) {
910 width.push_back(c);
911 c = origFormatString[++i];
912 }
913
914 // Parse the radix.
915 switch (c) {
916 case 'b':
917 case 'd':
918 case 'x':
919 if (!width.empty())
920 formatString.append(width);
921 [[fallthrough]];
922 case 'c':
923 substitutions.push_back(substitutionOperands[opIdx++]);
924 [[fallthrough]];
925 default:
926 formatString.push_back(c);
927 }
928 break;
929 }
930 case '{':
931 if (origFormatString.slice(i, i + 4) == "{{}}") {
932 formatString.append("{{");
933 TypeSwitch<Operation *>(substitutionOperands[opIdx++].getDefiningOp())
934 .Case<TimeOp>(
935 [&](auto time) { formatString.append("SimulationTime"); })
936 .Case<HierarchicalModuleNameOp>([&](auto time) {
937 formatString.append("HierarchicalModuleName");
938 })
939 .Default([&](auto) {
940 emitError(op, "unsupported fstring substitution type");
941 });
942 formatString.append("}}");
943 }
944 i += 3;
945 break;
946 default:
947 formatString.push_back(c);
948 }
949 }
950 ps.writeQuotedEscaped(formatString);
951}
952
953void Emitter::emitStatement(PrintFOp op) {
954 startStatement();
955 ps.scopedBox(PP::ibox2, [&]() {
956 ps << "printf(" << PP::ibox0;
957 emitExpression(op.getClock());
958 ps << "," << PP::space;
959 emitExpression(op.getCond());
960 ps << "," << PP::space;
961
962 SmallVector<Value, 4> substitutions;
963 emitFormatString(op, op.getFormatString(), op.getSubstitutions(),
964 substitutions);
965 for (auto operand : substitutions) {
966 ps << "," << PP::space;
967 emitExpression(operand);
968 }
969 ps << ")" << PP::end;
970 if (!op.getName().empty()) {
971 ps << PP::space << ": " << PPExtString(legalize(op.getNameAttr()));
972 }
973 });
974 emitLocationAndNewLine(op);
975}
976
977void Emitter::emitStatement(FPrintFOp op) {
978 startStatement();
979 ps.scopedBox(PP::ibox2, [&]() {
980 ps << "fprintf(" << PP::ibox0;
981 emitExpression(op.getClock());
982 ps << "," << PP::space;
983 emitExpression(op.getCond());
984 ps << "," << PP::space;
985
986 SmallVector<Value, 4> outputFileSubstitutions;
987 emitFormatString(op, op.getOutputFile(), op.getOutputFileSubstitutions(),
988 outputFileSubstitutions);
989 if (!outputFileSubstitutions.empty()) {
990 ps << "," << PP::space;
991 interleaveComma(outputFileSubstitutions);
992 }
993
994 ps << "," << PP::space;
995 SmallVector<Value, 4> substitutions;
996 emitFormatString(op, op.getFormatString(), op.getSubstitutions(),
997 substitutions);
998 if (!substitutions.empty()) {
999 ps << "," << PP::space;
1000 interleaveComma(substitutions);
1001 }
1002
1003 ps << ")" << PP::end;
1004 if (!op.getName().empty()) {
1005 ps << PP::space << ": " << PPExtString(legalize(op.getNameAttr()));
1006 }
1007 });
1008 emitLocationAndNewLine(op);
1009}
1010
1011void Emitter::emitStatement(FFlushOp op) {
1012 startStatement();
1013 ps.scopedBox(PP::ibox2, [&]() {
1014 ps << "fflush(" << PP::ibox0;
1015 emitExpression(op.getClock());
1016 ps << "," << PP::space;
1017 emitExpression(op.getCond());
1018 if (op.getOutputFileAttr()) {
1019 ps << "," << PP::space;
1020 SmallVector<Value, 4> substitutions;
1021 emitFormatString(op, op.getOutputFileAttr(),
1022 op.getOutputFileSubstitutions(), substitutions);
1023 if (!substitutions.empty()) {
1024 ps << "," << PP::space;
1025 interleaveComma(substitutions);
1026 }
1027 }
1028 ps << ")" << PP::end;
1029 });
1030 emitLocationAndNewLine(op);
1031}
1032
1033template <class T>
1034void Emitter::emitVerifStatement(T op, StringRef mnemonic) {
1035 startStatement();
1036 ps.scopedBox(PP::ibox2, [&]() {
1037 ps << mnemonic << "(" << PP::ibox0;
1038 emitExpression(op.getClock());
1039 ps << "," << PP::space;
1040 emitExpression(op.getPredicate());
1041 ps << "," << PP::space;
1042 emitExpression(op.getEnable());
1043 ps << "," << PP::space;
1044 ps.writeQuotedEscaped(op.getMessage());
1045 ps << ")" << PP::end;
1046 if (!op.getName().empty()) {
1047 ps << PP::space << ": " << PPExtString(legalize(op.getNameAttr()));
1048 }
1049 });
1050 emitLocationAndNewLine(op);
1051}
1052
1053void Emitter::emitStatement(ConnectOp op) {
1054 startStatement();
1055 if (FIRVersion(3, 0, 0) <= version) {
1056 ps.scopedBox(PP::ibox2, [&]() {
1057 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
1058 ps << "invalidate" << PP::space;
1059 emitExpression(op.getDest());
1060 } else {
1061 ps << "connect" << PP::space;
1062 emitExpression(op.getDest());
1063 ps << "," << PP::space;
1064 emitExpression(op.getSrc());
1065 }
1066 });
1067 } else {
1068 auto emitLHS = [&]() { emitExpression(op.getDest()); };
1069 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
1070 emitAssignLike(
1071 emitLHS, [&]() { ps << "invalid"; }, PPExtString("is"));
1072 } else {
1073 emitAssignLike(
1074 emitLHS, [&]() { emitExpression(op.getSrc()); }, PPExtString("<="));
1075 }
1076 }
1077 emitLocationAndNewLine(op);
1078}
1079
1080void Emitter::emitStatement(MatchingConnectOp op) {
1081 startStatement();
1082 if (FIRVersion(3, 0, 0) <= version) {
1083 ps.scopedBox(PP::ibox2, [&]() {
1084 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
1085 ps << "invalidate" << PP::space;
1086 emitExpression(op.getDest());
1087 } else {
1088 ps << "connect" << PP::space;
1089 emitExpression(op.getDest());
1090 ps << "," << PP::space;
1091 emitExpression(op.getSrc());
1092 }
1093 });
1094 } else {
1095 auto emitLHS = [&]() { emitExpression(op.getDest()); };
1096 if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
1097 emitAssignLike(
1098 emitLHS, [&]() { ps << "invalid"; }, PPExtString("is"));
1099 } else {
1100 emitAssignLike(
1101 emitLHS, [&]() { emitExpression(op.getSrc()); }, PPExtString("<="));
1102 }
1103 }
1104 emitLocationAndNewLine(op);
1105}
1106
1107void Emitter::emitStatement(PropAssignOp op) {
1108 startStatement();
1109 ps.scopedBox(PP::ibox2, [&]() {
1110 ps << "propassign" << PP::space;
1111 interleaveComma(op.getOperands());
1112 });
1113 emitLocationAndNewLine(op);
1114}
1115
1116void Emitter::emitStatement(InstanceOp op) {
1117 startStatement();
1118 auto legalName = legalize(op.getNameAttr());
1119 ps << "inst " << PPExtString(legalName) << " of "
1120 << PPExtString(legalize(op.getModuleNameAttr().getAttr()));
1121 emitLocationAndNewLine(op);
1122
1123 // Make sure we have a name like `<inst>.<port>` for each of the instance
1124 // result values.
1125 SmallString<16> portName(legalName);
1126 portName.push_back('.');
1127 unsigned baseLen = portName.size();
1128 for (unsigned i = 0, e = op.getNumResults(); i < e; ++i) {
1129 portName.append(legalize(op.getPortNameAttr(i)));
1130 addValueName(op.getResult(i), portName);
1131 portName.resize(baseLen);
1132 }
1133}
1134
1135void Emitter::emitStatement(InstanceChoiceOp op) {
1136 startStatement();
1137 auto legalName = legalize(op.getNameAttr());
1138 ps << "instchoice " << PPExtString(legalName) << " of "
1139 << PPExtString(legalize(op.getDefaultTargetAttr().getAttr())) << ", "
1140 << PPExtString(legalize(op.getOptionNameAttr())) << " :";
1141 emitLocation(op);
1142 ps.scopedBox(PP::bbox2, [&] {
1143 for (const auto &[optSym, targetSym] : op.getTargetChoices()) {
1144 ps << PP::newline;
1145 ps << PPExtString(legalize(optSym.getLeafReference()));
1146 ps << " => ";
1147 ps << PPExtString(legalize(targetSym.getAttr()));
1148 }
1149 });
1150 setPendingNewline();
1151
1152 SmallString<16> portName(legalName);
1153 portName.push_back('.');
1154 unsigned baseLen = portName.size();
1155 for (unsigned i = 0, e = op.getNumResults(); i < e; ++i) {
1156 portName.append(legalize(op.getPortNameAttr(i)));
1157 addValueName(op.getResult(i), portName);
1158 portName.resize(baseLen);
1159 }
1160}
1161
1162void Emitter::emitStatement(AttachOp op) {
1163 emitStatementFunctionOp(PPExtString("attach"), op);
1164}
1165
1166void Emitter::emitStatement(MemOp op) {
1167 auto legalName = legalize(op.getNameAttr());
1168 SmallString<16> portName(legalName);
1169 portName.push_back('.');
1170 auto portNameBaseLen = portName.size();
1171 for (auto result : llvm::zip(op.getResults(), op.getPortNames())) {
1172 portName.resize(portNameBaseLen);
1173 portName.append(legalize(cast<StringAttr>(std::get<1>(result))));
1174 addValueName(std::get<0>(result), portName);
1175 }
1176
1177 startStatement();
1178 ps << "mem " << PPExtString(legalName) << " :";
1179 emitLocationAndNewLine(op);
1180 ps.scopedBox(PP::bbox2, [&]() {
1181 startStatement();
1182 ps << "data-type => ";
1183 emitType(op.getDataType());
1184 ps << PP::newline;
1185 ps << "depth => ";
1186 ps.addAsString(op.getDepth());
1187 ps << PP::newline;
1188 ps << "read-latency => ";
1189 ps.addAsString(op.getReadLatency());
1190 ps << PP::newline;
1191 ps << "write-latency => ";
1192 ps.addAsString(op.getWriteLatency());
1193 ps << PP::newline;
1194
1195 SmallString<16> reader, writer, readwriter;
1196 for (std::pair<StringAttr, MemOp::PortKind> port : op.getPorts()) {
1197 auto add = [&](SmallString<16> &to, StringAttr name) {
1198 if (!to.empty())
1199 to.push_back(' ');
1200 to.append(name.getValue());
1201 };
1202 switch (port.second) {
1203 case MemOp::PortKind::Read:
1204 add(reader, legalize(port.first));
1205 break;
1206 case MemOp::PortKind::Write:
1207 add(writer, legalize(port.first));
1208 break;
1209 case MemOp::PortKind::ReadWrite:
1210 add(readwriter, legalize(port.first));
1211 break;
1212 case MemOp::PortKind::Debug:
1213 emitOpError(op, "has unsupported 'debug' port");
1214 return;
1215 }
1216 }
1217 if (!reader.empty())
1218 ps << "reader => " << reader << PP::newline;
1219 if (!writer.empty())
1220 ps << "writer => " << writer << PP::newline;
1221 if (!readwriter.empty())
1222 ps << "readwriter => " << readwriter << PP::newline;
1223
1224 ps << "read-under-write => ";
1225 emitAttribute(op.getRuwAttr());
1226 setPendingNewline();
1227 });
1228}
1229
1230void Emitter::emitStatement(SeqMemOp op) {
1231 startStatement();
1232 ps.scopedBox(PP::ibox2, [&]() {
1233 ps << "smem " << PPExtString(legalize(op.getNameAttr()));
1234 emitTypeWithColon(op.getType());
1235 ps << "," << PP::space;
1236 emitAttribute(op.getRuwAttr());
1237 });
1238 emitLocationAndNewLine(op);
1239}
1240
1241void Emitter::emitStatement(CombMemOp op) {
1242 startStatement();
1243 ps.scopedBox(PP::ibox2, [&]() {
1244 ps << "cmem " << PPExtString(legalize(op.getNameAttr()));
1245 emitTypeWithColon(op.getType());
1246 });
1247 emitLocationAndNewLine(op);
1248}
1249
1250void Emitter::emitStatement(MemoryPortOp op) {
1251 // Nothing to output for this operation.
1252 addValueName(op.getData(), legalize(op.getNameAttr()));
1253}
1254
1255void Emitter::emitStatement(MemoryDebugPortOp op) {
1256 // Nothing to output for this operation.
1257 addValueName(op.getData(), legalize(op.getNameAttr()));
1258}
1259
1260void Emitter::emitStatement(MemoryPortAccessOp op) {
1261 startStatement();
1262
1263 // Print the port direction and name.
1264 auto port = cast<MemoryPortOp>(op.getPort().getDefiningOp());
1265 emitAttribute(port.getDirection());
1266 // TODO: emitAssignLike
1267 ps << " mport " << PPExtString(legalize(port.getNameAttr())) << " = ";
1268
1269 // Print the memory name.
1270 auto *mem = port.getMemory().getDefiningOp();
1271 if (auto seqMem = dyn_cast<SeqMemOp>(mem))
1272 ps << legalize(seqMem.getNameAttr());
1273 else
1274 ps << legalize(cast<CombMemOp>(mem).getNameAttr());
1275
1276 // Print the address.
1277 ps << "[";
1278 emitExpression(op.getIndex());
1279 ps << "], ";
1280
1281 // Print the clock.
1282 emitExpression(op.getClock());
1283
1284 emitLocationAndNewLine(op);
1285}
1286
1287void Emitter::emitStatement(DomainDefineOp op) {
1288 startStatement();
1289 emitAssignLike([&]() { emitExpression(op.getDest()); },
1290 [&]() { emitExpression(op.getSrc()); }, PPExtString("="),
1291 PPExtString("domain_define"));
1292 emitLocationAndNewLine(op);
1293}
1294
1295void Emitter::emitStatement(RefDefineOp op) {
1296 startStatement();
1297 emitAssignLike([&]() { emitExpression(op.getDest()); },
1298 [&]() { emitExpression(op.getSrc()); }, PPExtString("="),
1299 PPExtString("define"));
1300 emitLocationAndNewLine(op);
1301}
1302
1303void Emitter::emitStatement(RefForceOp op) {
1304 emitStatementFunctionOp(PPExtString("force"), op);
1305}
1306
1307void Emitter::emitStatement(RefForceInitialOp op) {
1308 startStatement();
1309 auto constantPredicate =
1310 dyn_cast_or_null<ConstantOp>(op.getPredicate().getDefiningOp());
1311 bool hasEnable = !constantPredicate || constantPredicate.getValue() == 0;
1312 if (hasEnable) {
1313 ps << "when ";
1314 emitExpression(op.getPredicate());
1315 ps << ":" << PP::bbox2 << PP::neverbreak << PP::newline;
1316 }
1317 ps << "force_initial(";
1318 ps.scopedBox(PP::ibox0, [&]() {
1319 interleaveComma({op.getDest(), op.getSrc()});
1320 ps << ")";
1321 });
1322 if (hasEnable)
1323 ps << PP::end;
1324 emitLocationAndNewLine(op);
1325}
1326
1327void Emitter::emitStatement(RefReleaseOp op) {
1328 emitStatementFunctionOp(PPExtString("release"), op);
1329}
1330
1331void Emitter::emitStatement(RefReleaseInitialOp op) {
1332 startStatement();
1333 auto constantPredicate =
1334 dyn_cast_or_null<ConstantOp>(op.getPredicate().getDefiningOp());
1335 bool hasEnable = !constantPredicate || constantPredicate.getValue() == 0;
1336 if (hasEnable) {
1337 ps << "when ";
1338 emitExpression(op.getPredicate());
1339 ps << ":" << PP::bbox2 << PP::neverbreak << PP::newline;
1340 }
1341 ps << "release_initial(";
1342 emitExpression(op.getDest());
1343 ps << ")";
1344 if (hasEnable)
1345 ps << PP::end;
1346 emitLocationAndNewLine(op);
1347}
1348
1349void Emitter::emitStatement(LayerBlockOp op) {
1350 startStatement();
1351 ps << "layerblock " << op.getLayerName().getLeafReference() << " :";
1352 emitLocationAndNewLine(op);
1353 auto *body = op.getBody();
1354 ps.scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(*body); });
1355}
1356
1357void Emitter::emitStatement(InvalidValueOp op) {
1358 // Only emit this invalid value if it is used somewhere else than the RHS of
1359 // a connect.
1360 if (llvm::all_of(op->getUses(), [&](OpOperand &use) {
1361 return use.getOperandNumber() == 1 &&
1362 isa<ConnectOp, MatchingConnectOp>(use.getOwner());
1363 }))
1364 return;
1365
1366 // TODO: emitAssignLike ?
1367 startStatement();
1368 auto name = circuitNamespace.newName("_invalid");
1369 addValueName(op, name);
1370 ps << "wire " << PPExtString(name) << " : ";
1371 emitType(op.getType());
1372 emitLocationAndNewLine(op);
1373 startStatement();
1374 if (FIRVersion(3, 0, 0) <= version)
1375 ps << "invalidate " << PPExtString(name);
1376 else
1377 ps << PPExtString(name) << " is invalid";
1378 emitLocationAndNewLine(op);
1379}
1380
1381void Emitter::emitStatement(GenericIntrinsicOp op) {
1382 startStatement();
1383 if (op.use_empty())
1384 emitGenericIntrinsic(op);
1385 else {
1386 assert(!isEmittedInline(op));
1387 auto name = circuitNamespace.newName("_gen_int");
1388 addValueName(op.getResult(), name);
1389 emitAssignLike([&]() { ps << "node " << PPExtString(name); },
1390 [&]() { emitGenericIntrinsic(op); });
1391 }
1392 emitLocationAndNewLine(op);
1393}
1394
1395void Emitter::emitExpression(Value value) {
1396 // Handle the trivial case where we already have a name for this value which
1397 // we can use.
1398 if (auto name = lookupEmittedName(value)) {
1399 // Don't use PPExtString here, can't trust valueNames storage, cleared.
1400 ps << *name;
1401 return;
1402 }
1403
1404 auto op = value.getDefiningOp();
1405 assert(op && "value must either be a block arg or the result of an op");
1406 TypeSwitch<Operation *>(op)
1407 .Case<
1408 // Basic expressions
1409 ConstantOp, SpecialConstantOp, SubfieldOp, SubindexOp, SubaccessOp,
1410 OpenSubfieldOp, OpenSubindexOp,
1411 // Binary
1412 AddPrimOp, SubPrimOp, MulPrimOp, DivPrimOp, RemPrimOp, AndPrimOp,
1413 OrPrimOp, XorPrimOp, LEQPrimOp, LTPrimOp, GEQPrimOp, GTPrimOp,
1414 EQPrimOp, NEQPrimOp, DShlPrimOp, DShlwPrimOp, DShrPrimOp,
1415 // Unary
1416 AsSIntPrimOp, AsUIntPrimOp, AsAsyncResetPrimOp, AsClockPrimOp,
1417 CvtPrimOp, NegPrimOp, NotPrimOp, AndRPrimOp, OrRPrimOp, XorRPrimOp,
1418 // Miscellaneous
1419 BitsPrimOp, HeadPrimOp, TailPrimOp, PadPrimOp, MuxPrimOp, ShlPrimOp,
1420 ShrPrimOp, UninferredResetCastOp, ConstCastOp, StringConstantOp,
1421 FIntegerConstantOp, BoolConstantOp, DoubleConstantOp, ListCreateOp,
1422 UnresolvedPathOp, GenericIntrinsicOp, CatPrimOp, UnsafeDomainCastOp,
1423 // Reference expressions
1424 RefSendOp, RefResolveOp, RefSubOp, RWProbeOp, RefCastOp,
1425 // Format String expressions
1426 TimeOp>([&](auto op) {
1427 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op); });
1428 })
1429 .Default([&](auto op) {
1430 emitOpError(op, "not supported as expression");
1431 ps << "<unsupported-expr-" << PPExtString(op->getName().stripDialect())
1432 << ">";
1433 });
1434}
1435
1436void Emitter::emitExpression(ConstantOp op) {
1437 // Don't include 'const' on the type in a literal expression
1438 emitType(op.getType(), false);
1439 // TODO: Add option to control base-2/8/10/16 output here.
1440 ps << "(";
1441 ps.addAsString(op.getValue());
1442 ps << ")";
1443}
1444
1445void Emitter::emitExpression(SpecialConstantOp op) {
1446 auto emitInner = [&]() {
1447 ps << "UInt<1>(";
1448 ps.addAsString(op.getValue());
1449 ps << ")";
1450 };
1451 // TODO: Emit type decl for type alias.
1452 FIRRTLTypeSwitch<FIRRTLType>(type_cast<FIRRTLType>(op.getType()))
1453 .Case<ClockType>([&](auto type) {
1454 ps << "asClock(";
1455 emitInner();
1456 ps << ")";
1457 })
1458 .Case<ResetType>([&](auto type) { emitInner(); })
1459 .Case<AsyncResetType>([&](auto type) {
1460 ps << "asAsyncReset(";
1461 emitInner();
1462 ps << ")";
1463 });
1464}
1465
1466// NOLINTNEXTLINE(misc-no-recursion)
1467void Emitter::emitExpression(SubfieldOp op) {
1468 BundleType type = op.getInput().getType();
1469 emitExpression(op.getInput());
1470 ps << "." << legalize(type.getElementNameAttr(op.getFieldIndex()));
1471}
1472
1473// NOLINTNEXTLINE(misc-no-recursion)
1474void Emitter::emitExpression(SubindexOp op) {
1475 emitExpression(op.getInput());
1476 ps << "[";
1477 ps.addAsString(op.getIndex());
1478 ps << "]";
1479}
1480
1481// NOLINTNEXTLINE(misc-no-recursion)
1482void Emitter::emitExpression(SubaccessOp op) {
1483 emitExpression(op.getInput());
1484 ps << "[";
1485 emitExpression(op.getIndex());
1486 ps << "]";
1487}
1488
1489void Emitter::emitExpression(OpenSubfieldOp op) {
1490 auto type = op.getInput().getType();
1491 emitExpression(op.getInput());
1492 ps << "." << legalize(type.getElementNameAttr(op.getFieldIndex()));
1493}
1494
1495void Emitter::emitExpression(OpenSubindexOp op) {
1496 emitExpression(op.getInput());
1497 ps << "[";
1498 ps.addAsString(op.getIndex());
1499 ps << "]";
1500}
1501
1502void Emitter::emitExpression(RefSendOp op) {
1503 ps << "probe(";
1504 emitExpression(op.getBase());
1505 ps << ")";
1506}
1507
1508void Emitter::emitExpression(RefResolveOp op) {
1509 ps << "read(";
1510 emitExpression(op.getRef());
1511 ps << ")";
1512}
1513
1514void Emitter::emitExpression(RefSubOp op) {
1515 emitExpression(op.getInput());
1516 FIRRTLTypeSwitch<FIRRTLBaseType, void>(op.getInput().getType().getType())
1517 .Case<FVectorType>([&](auto type) {
1518 ps << "[";
1519 ps.addAsString(op.getIndex());
1520 ps << "]";
1521 })
1522 .Case<BundleType>(
1523 [&](auto type) { ps << "." << type.getElementName(op.getIndex()); });
1524}
1525
1526void Emitter::emitExpression(RWProbeOp op) {
1527 ps << "rwprobe(";
1528
1529 // Find the probe target.
1530 auto target = symInfos->get().irn.lookup(op.getTarget());
1531 Value base;
1532 if (target.isPort()) {
1533 auto mod = cast<FModuleOp>(target.getOp());
1534 auto port = target.getPort();
1535 base = mod.getArgument(port);
1536 } else
1537 base = cast<hw::InnerSymbolOpInterface>(target.getOp()).getTargetResult();
1538
1539 // Print target. Needs this to have a name already.
1540 emitExpression(base);
1541
1542 // Print indexing for the target field.
1543 auto fieldID = target.getField();
1544 auto type = base.getType();
1545 while (fieldID) {
1547 .Case<FVectorType, OpenVectorType>([&](auto vecTy) {
1548 auto index = vecTy.getIndexForFieldID(fieldID);
1549 ps << "[";
1550 ps.addAsString(index);
1551 ps << "]";
1552 auto [subtype, subfieldID] = vecTy.getSubTypeByFieldID(fieldID);
1553 type = subtype;
1554 fieldID = subfieldID;
1555 })
1556 .Case<BundleType, OpenBundleType>([&](auto bundleTy) {
1557 auto index = bundleTy.getIndexForFieldID(fieldID);
1558 ps << "." << bundleTy.getElementName(index);
1559 auto [subtype, subfieldID] = bundleTy.getSubTypeByFieldID(fieldID);
1560 type = subtype;
1561 fieldID = subfieldID;
1562 });
1563 }
1564 ps << ")";
1565}
1566
1567void Emitter::emitExpression(RefCastOp op) { emitExpression(op.getInput()); }
1568
1569void Emitter::emitExpression(UninferredResetCastOp op) {
1570 emitExpression(op.getInput());
1571}
1572
1573void Emitter::emitExpression(FIntegerConstantOp op) {
1574 ps << "Integer(";
1575 ps.addAsString(op.getValue());
1576 ps << ")";
1577}
1578
1579void Emitter::emitExpression(BoolConstantOp op) {
1580 ps << "Bool(" << (op.getValue() ? "true" : "false") << ")";
1581}
1582
1583void Emitter::emitExpression(DoubleConstantOp op) {
1584 ps << "Double(";
1585 // Use APFloat::toString.
1586 // Printing as double is not what we want,
1587 // and this at least handles the basic cases in a way
1588 // that will round-trip.
1589 SmallString<16> str;
1590 op.getValueAttr().getValue().toString(str);
1591 ps << str;
1592 ps << ")";
1593}
1594
1595void Emitter::emitExpression(StringConstantOp op) {
1596 ps << "String(";
1597 ps.writeQuotedEscaped(op.getValue());
1598 ps << ")";
1599}
1600
1601void Emitter::emitExpression(ListCreateOp op) {
1602 return emitLiteralExpression(op.getType(), op.getElements());
1603}
1604
1605void Emitter::emitExpression(UnresolvedPathOp op) {
1606 ps << "path(";
1607 ps.writeQuotedEscaped(op.getTarget());
1608 ps << ")";
1609}
1610
1611void Emitter::emitExpression(GenericIntrinsicOp op) {
1612 emitGenericIntrinsic(op);
1613}
1614
1615void Emitter::emitExpression(ConstCastOp op) { emitExpression(op.getInput()); }
1616
1617void Emitter::emitPrimExpr(StringRef mnemonic, Operation *op,
1618 ArrayRef<uint32_t> attrs) {
1619 ps << mnemonic << "(" << PP::ibox0;
1620 interleaveComma(op->getOperands());
1621 if (!op->getOperands().empty() && !attrs.empty())
1622 ps << "," << PP::space;
1623 interleaveComma(attrs, [&](auto attr) { ps.addAsString(attr); });
1624 ps << ")" << PP::end;
1625}
1626
1627void Emitter::emitExpression(CatPrimOp op) {
1628 size_t numOperands = op.getNumOperands();
1629 switch (numOperands) {
1630 case 0:
1631 // Emit "UInt<0>(0)"
1632 emitType(op.getType(), false);
1633 ps << "(0)";
1634 return;
1635 case 1: {
1636 auto operand = op->getOperand(0);
1637 // If there is no sign conversion, just emit the operand.
1638 if (isa<UIntType>(operand.getType()))
1639 return emitExpression(operand);
1640
1641 // Emit cat to convert sign.
1642 ps << "cat(" << PP::ibox0;
1643 emitExpression(op->getOperand(0));
1644 ps << "," << PP::space << "SInt<0>(0))" << PP::end;
1645 return;
1646 }
1647
1648 default:
1649 // Construct a linear tree of cats.
1650 for (size_t i = 0; i < numOperands - 1; ++i) {
1651 ps << "cat(" << PP::ibox0;
1652 emitExpression(op->getOperand(i));
1653 ps << "," << PP::space;
1654 }
1655
1656 emitExpression(op->getOperand(numOperands - 1));
1657 for (size_t i = 0; i < numOperands - 1; ++i)
1658 ps << ")" << PP::end;
1659 return;
1660 }
1661}
1662
1663void Emitter::emitExpression(UnsafeDomainCastOp op) {
1664 ps << "unsafe_domain_cast(" << PP::ibox0;
1665 interleaveComma(op.getOperands(),
1666 [&](Value operand) { emitExpression(operand); });
1667 ps << ")" << PP::end;
1668}
1669
1670void Emitter::emitAttribute(MemDirAttr attr) {
1671 switch (attr) {
1672 case MemDirAttr::Infer:
1673 ps << "infer";
1674 break;
1675 case MemDirAttr::Read:
1676 ps << "read";
1677 break;
1678 case MemDirAttr::Write:
1679 ps << "write";
1680 break;
1681 case MemDirAttr::ReadWrite:
1682 ps << "rdwr";
1683 break;
1684 }
1685}
1686
1687void Emitter::emitAttribute(RUWBehaviorAttr attr) {
1688 switch (attr.getValue()) {
1689 case RUWBehavior::Undefined:
1690 ps << "undefined";
1691 break;
1692 case RUWBehavior::Old:
1693 ps << "old";
1694 break;
1695 case RUWBehavior::New:
1696 ps << "new";
1697 break;
1698 }
1699}
1700
1701/// Emit a FIRRTL type into the output.
1702void Emitter::emitType(Type type, bool includeConst) {
1703 if (includeConst && isConst(type))
1704 ps << "const ";
1705 auto emitWidth = [&](std::optional<int32_t> width) {
1706 if (width) {
1707 ps << "<";
1708 ps.addAsString(*width);
1709 ps << ">";
1710 }
1711 };
1712 // TODO: Emit type decl for type alias.
1714 .Case<ClockType>([&](auto) { ps << "Clock"; })
1715 .Case<ResetType>([&](auto) { ps << "Reset"; })
1716 .Case<AsyncResetType>([&](auto) { ps << "AsyncReset"; })
1717 .Case<UIntType>([&](auto type) {
1718 ps << "UInt";
1719 emitWidth(type.getWidth());
1720 })
1721 .Case<SIntType>([&](auto type) {
1722 ps << "SInt";
1723 emitWidth(type.getWidth());
1724 })
1725 .Case<AnalogType>([&](auto type) {
1726 ps << "Analog";
1727 emitWidth(type.getWidth());
1728 })
1729 .Case<OpenBundleType, BundleType>([&](auto type) {
1730 ps << "{";
1731 if (!type.getElements().empty())
1732 ps << PP::nbsp;
1733 bool anyEmitted = false;
1734 ps.scopedBox(PP::cbox0, [&]() {
1735 for (auto &element : type.getElements()) {
1736 if (anyEmitted)
1737 ps << "," << PP::space;
1738 ps.scopedBox(PP::ibox2, [&]() {
1739 if (element.isFlip)
1740 ps << "flip ";
1741 ps << legalize(element.name);
1742 emitTypeWithColon(element.type);
1743 anyEmitted = true;
1744 });
1745 }
1746 if (anyEmitted)
1747 ps << PP::nbsp;
1748 ps << "}";
1749 });
1750 })
1751 .Case<OpenVectorType, FVectorType, CMemoryType>([&](auto type) {
1752 emitType(type.getElementType());
1753 ps << "[";
1754 ps.addAsString(type.getNumElements());
1755 ps << "]";
1756 })
1757 .Case<RefType>([&](RefType type) {
1758 if (type.getForceable())
1759 ps << "RW";
1760 ps << "Probe<";
1761 ps.cbox(2, IndentStyle::Block);
1762 ps.zerobreak();
1763 emitType(type.getType());
1764 if (auto layer = type.getLayer()) {
1765 ps << ",";
1766 ps.space();
1767 emitSymbol(type.getLayer());
1768 }
1769 ps << BreakToken(0, -2) << ">";
1770 ps.end();
1771 })
1772 .Case<AnyRefType>([&](AnyRefType type) { ps << "AnyRef"; })
1773 .Case<StringType>([&](StringType type) { ps << "String"; })
1774 .Case<FIntegerType>([&](FIntegerType type) { ps << "Integer"; })
1775 .Case<BoolType>([&](BoolType type) { ps << "Bool"; })
1776 .Case<DoubleType>([&](DoubleType type) { ps << "Double"; })
1777 .Case<PathType>([&](PathType type) { ps << "Path"; })
1778 .Case<ListType>([&](ListType type) {
1779 ps << "List<";
1780 emitType(type.getElementType());
1781 ps << ">";
1782 })
1783 .Case<DomainType>([&](DomainType type) { ps << "Domain"; })
1784 .Default([&](auto type) {
1785 llvm_unreachable("all types should be implemented");
1786 });
1787}
1788
1789void Emitter::emitDomains(Attribute attr,
1790 const DenseMap<size_t, StringRef> &domainMap) {
1791 if (!attr)
1792 return;
1793 if (auto domains = dyn_cast<ArrayAttr>(attr)) {
1794 if (domains.empty())
1795 return;
1796 ps << " domains [";
1797 ps.scopedBox(PP::ibox0, [&]() {
1798 interleaveComma(domains, [&](Attribute attr) {
1799 auto itr = domainMap.find(cast<IntegerAttr>(attr).getUInt());
1800 assert(itr != domainMap.end() && "Unable to find domain");
1801 ps.addAsString(itr->second);
1802 });
1803 ps << "]";
1804 });
1805 } else {
1806 auto kind = cast<FlatSymbolRefAttr>(attr);
1807 ps << " of " << PPExtString(kind.getValue());
1808 }
1809}
1810
1811/// Emit a location as `@[<filename> <line>:<column>]` annotation, including a
1812/// leading space.
1813void Emitter::emitLocation(Location loc) {
1814 // TODO: Handle FusedLoc and uniquify locations, avoid repeated file names.
1815 ps << PP::neverbreak;
1816 if (auto fileLoc =
1817 dyn_cast_or_null<FileLineColLoc, LocationAttr>(LocationAttr(loc))) {
1818 ps << " @[" << fileLoc.getFilename().getValue();
1819 if (auto line = fileLoc.getLine()) {
1820 ps << " ";
1821 ps.addAsString(line);
1822 if (auto col = fileLoc.getColumn()) {
1823 ps << ":";
1824 ps.addAsString(col);
1825 }
1826 }
1827 ps << "]";
1828 }
1829}
1830// NOLINTEND(misc-no-recursion)
1831
1832//===----------------------------------------------------------------------===//
1833// Driver
1834//===----------------------------------------------------------------------===//
1835
1836// Emit the specified FIRRTL circuit into the given output stream.
1837mlir::LogicalResult
1838circt::firrtl::exportFIRFile(mlir::ModuleOp module, llvm::raw_ostream &os,
1839 std::optional<size_t> targetLineLength,
1840 FIRVersion version) {
1841 Emitter emitter(os, version,
1842 targetLineLength.value_or(defaultTargetLineLength));
1843 for (auto &op : *module.getBody()) {
1844 if (auto circuitOp = dyn_cast<CircuitOp>(op))
1845 emitter.emitCircuit(circuitOp);
1846 }
1847 return emitter.finalize();
1848}
1849
1851 static llvm::cl::opt<size_t> targetLineLength(
1852 "target-line-length",
1853 llvm::cl::desc("Target line length for emitted .fir"),
1854 llvm::cl::value_desc("number of chars"),
1855 llvm::cl::init(defaultTargetLineLength));
1856 static mlir::TranslateFromMLIRRegistration toFIR(
1857 "export-firrtl", "emit FIRRTL dialect operations to .fir output",
1858 [](ModuleOp module, llvm::raw_ostream &os) {
1859 return exportFIRFile(module, os, targetLineLength, exportFIRVersion);
1860 },
1861 [](mlir::DialectRegistry &registry) {
1862 registry.insert<chirrtl::CHIRRTLDialect>();
1863 registry.insert<firrtl::FIRRTLDialect>();
1864 });
1865}
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.