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