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