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(StrictConnectOp 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  emitLocationAndNewLine(op);
592  ps.scopedBox(PP::bbox2, [&]() {
593  for (auto &bodyOp : op.getBody().getOps()) {
594  TypeSwitch<Operation *>(&bodyOp)
595  .Case<LayerOp>([&](auto op) { emitDeclaration(op); })
596  .Default([&](auto op) {
597  emitOpError(op,
598  "not supported for emission inside layer definition");
599  });
600  }
601  });
602 }
603 
604 /// Emit an option declaration.
605 void Emitter::emitDeclaration(OptionOp op) {
606  startStatement();
607  ps << "option " << PPExtString(legalize(op.getSymNameAttr())) << " :";
608  emitLocation(op);
609  ps.scopedBox(PP::bbox2, [&] {
610  for (auto caseOp : op.getBody().getOps<OptionCaseOp>()) {
611  ps << PP::newline;
612  ps << PPExtString(legalize(caseOp.getSymNameAttr()));
613  emitLocation(caseOp);
614  }
615  });
616  ps << PP::newline << PP::newline;
617 }
618 
619 /// Check if an operation is inlined into the emission of their users. For
620 /// example, subfields are always inlined.
621 static bool isEmittedInline(Operation *op) {
622  // FIRRTL expressions are statically classified as always inlineable.
623  // InvalidValueOp never is inlined, and is handled specially.
624  // GenericIntrinsicOp is inlined if has exactly one use (only emit once)
625  // that is not emitted inline. This is to ensure it is emitted inline
626  // in common cases, but only inspect one level deep.
627  return (isExpression(op) && !isa<InvalidValueOp>(op)) ||
628  (isa<GenericIntrinsicOp>(op) && op->hasOneUse() &&
629  !isEmittedInline(*op->getUsers().begin()));
630 }
631 
632 void Emitter::emitStatementsInBlock(Block &block) {
633  for (auto &bodyOp : block) {
634  if (encounteredError)
635  return;
636  if (isEmittedInline(&bodyOp))
637  continue;
638  TypeSwitch<Operation *>(&bodyOp)
639  .Case<WhenOp, WireOp, RegOp, RegResetOp, NodeOp, StopOp, SkipOp,
640  PrintFOp, AssertOp, AssumeOp, CoverOp, ConnectOp, StrictConnectOp,
641  PropAssignOp, InstanceOp, InstanceChoiceOp, AttachOp, MemOp,
642  InvalidValueOp, SeqMemOp, CombMemOp, MemoryPortOp,
643  MemoryDebugPortOp, MemoryPortAccessOp, RefDefineOp, RefForceOp,
644  RefForceInitialOp, RefReleaseOp, RefReleaseInitialOp,
645  LayerBlockOp, GenericIntrinsicOp>(
646  [&](auto op) { emitStatement(op); })
647  .Default([&](auto op) {
648  startStatement();
649  ps << "// operation " << PPExtString(op->getName().getStringRef());
650  setPendingNewline();
651  emitOpError(op, "not supported as statement");
652  });
653  }
654 }
655 
656 void Emitter::emitStatement(WhenOp op) {
657  startStatement();
658  ps << "when ";
659  emitExpression(op.getCondition());
660  ps << " :";
661  emitLocationAndNewLine(op);
662  ps.scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(op.getThenBlock()); });
663  // emitStatementsInBlock(op.getThenBlock());
664  if (!op.hasElseRegion())
665  return;
666 
667  startStatement();
668  ps << "else ";
669  // Sugar to `else when ...` if there's only a single when statement in the
670  // else block.
671  auto &elseBlock = op.getElseBlock();
672  if (!elseBlock.empty() && &elseBlock.front() == &elseBlock.back()) {
673  if (auto whenOp = dyn_cast<WhenOp>(&elseBlock.front())) {
674  emitStatement(whenOp);
675  return;
676  }
677  }
678  // Otherwise print the block as `else :`.
679  ps << ":";
680  setPendingNewline();
681  ps.scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(elseBlock); });
682 }
683 
684 void Emitter::emitStatement(WireOp op) {
685  auto legalName = legalize(op.getNameAttr());
686  addForceable(op, legalName);
687  startStatement();
688  ps.scopedBox(PP::ibox2, [&]() {
689  ps << "wire " << PPExtString(legalName);
690  emitTypeWithColon(op.getResult().getType());
691  });
692  emitLocationAndNewLine(op);
693 }
694 
695 void Emitter::emitStatement(RegOp op) {
696  auto legalName = legalize(op.getNameAttr());
697  addForceable(op, legalName);
698  startStatement();
699  ps.scopedBox(PP::ibox2, [&]() {
700  ps << "reg " << PPExtString(legalName);
701  emitTypeWithColon(op.getResult().getType());
702  ps << "," << PP::space;
703  emitExpression(op.getClockVal());
704  });
705  emitLocationAndNewLine(op);
706 }
707 
708 void Emitter::emitStatement(RegResetOp op) {
709  auto legalName = legalize(op.getNameAttr());
710  addForceable(op, legalName);
711  startStatement();
712  if (FIRVersion(3, 0, 0) <= version) {
713  ps.scopedBox(PP::ibox2, [&]() {
714  ps << "regreset " << legalName;
715  emitTypeWithColon(op.getResult().getType());
716  ps << "," << PP::space;
717  emitExpression(op.getClockVal());
718  ps << "," << PP::space;
719  emitExpression(op.getResetSignal());
720  ps << "," << PP::space;
721  emitExpression(op.getResetValue());
722  });
723  } else {
724  ps.scopedBox(PP::ibox2, [&]() {
725  ps << "reg " << legalName;
726  emitTypeWithColon(op.getResult().getType());
727  ps << "," << PP::space;
728  emitExpression(op.getClockVal());
729  ps << PP::space << "with :";
730  // Don't break this because of the newline.
731  ps << PP::neverbreak;
732  // No-paren version must be newline + indent.
733  ps << PP::newline; // ibox2 will indent.
734  ps << "reset => (" << PP::ibox0;
735  emitExpression(op.getResetSignal());
736  ps << "," << PP::space;
737  emitExpression(op.getResetValue());
738  ps << ")" << PP::end;
739  });
740  }
741  emitLocationAndNewLine(op);
742 }
743 
744 void Emitter::emitStatement(NodeOp op) {
745  auto legalName = legalize(op.getNameAttr());
746  addForceable(op, legalName);
747  startStatement();
748  emitAssignLike([&]() { ps << "node " << PPExtString(legalName); },
749  [&]() { emitExpression(op.getInput()); });
750  emitLocationAndNewLine(op);
751 }
752 
753 void Emitter::emitStatement(StopOp op) {
754  startStatement();
755  ps.scopedBox(PP::ibox2, [&]() {
756  ps << "stop(" << PP::ibox0;
757  emitExpression(op.getClock());
758  ps << "," << PP::space;
759  emitExpression(op.getCond());
760  ps << "," << PP::space;
761  ps.addAsString(op.getExitCode());
762  ps << ")" << PP::end;
763  if (!op.getName().empty()) {
764  ps << PP::space << ": " << PPExtString(legalize(op.getNameAttr()));
765  }
766  });
767  emitLocationAndNewLine(op);
768 }
769 
770 void Emitter::emitStatement(SkipOp op) {
771  startStatement();
772  ps << "skip";
773  emitLocationAndNewLine(op);
774 }
775 
776 void Emitter::emitStatement(PrintFOp op) {
777  startStatement();
778  ps.scopedBox(PP::ibox2, [&]() {
779  ps << "printf(" << PP::ibox0;
780  emitExpression(op.getClock());
781  ps << "," << PP::space;
782  emitExpression(op.getCond());
783  ps << "," << PP::space;
784  ps.writeQuotedEscaped(op.getFormatString());
785  for (auto operand : op.getSubstitutions()) {
786  ps << "," << PP::space;
787  emitExpression(operand);
788  }
789  ps << ")" << PP::end;
790  if (!op.getName().empty()) {
791  ps << PP::space << ": " << PPExtString(legalize(op.getNameAttr()));
792  }
793  });
794  emitLocationAndNewLine(op);
795 }
796 
797 template <class T>
798 void Emitter::emitVerifStatement(T op, StringRef mnemonic) {
799  startStatement();
800  ps.scopedBox(PP::ibox2, [&]() {
801  ps << mnemonic << "(" << PP::ibox0;
802  emitExpression(op.getClock());
803  ps << "," << PP::space;
804  emitExpression(op.getPredicate());
805  ps << "," << PP::space;
806  emitExpression(op.getEnable());
807  ps << "," << PP::space;
808  ps.writeQuotedEscaped(op.getMessage());
809  ps << ")" << PP::end;
810  if (!op.getName().empty()) {
811  ps << PP::space << ": " << PPExtString(legalize(op.getNameAttr()));
812  }
813  });
814  emitLocationAndNewLine(op);
815 }
816 
817 void Emitter::emitStatement(ConnectOp op) {
818  startStatement();
819  if (FIRVersion(3, 0, 0) <= version) {
820  ps.scopedBox(PP::ibox2, [&]() {
821  if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
822  ps << "invalidate" << PP::space;
823  emitExpression(op.getDest());
824  } else {
825  ps << "connect" << PP::space;
826  emitExpression(op.getDest());
827  ps << "," << PP::space;
828  emitExpression(op.getSrc());
829  }
830  });
831  } else {
832  auto emitLHS = [&]() { emitExpression(op.getDest()); };
833  if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
834  emitAssignLike(
835  emitLHS, [&]() { ps << "invalid"; }, PPExtString("is"));
836  } else {
837  emitAssignLike(
838  emitLHS, [&]() { emitExpression(op.getSrc()); }, PPExtString("<="));
839  }
840  }
841  emitLocationAndNewLine(op);
842 }
843 
844 void Emitter::emitStatement(StrictConnectOp op) {
845  startStatement();
846  if (FIRVersion(3, 0, 0) <= version) {
847  ps.scopedBox(PP::ibox2, [&]() {
848  if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
849  ps << "invalidate" << PP::space;
850  emitExpression(op.getDest());
851  } else {
852  ps << "connect" << PP::space;
853  emitExpression(op.getDest());
854  ps << "," << PP::space;
855  emitExpression(op.getSrc());
856  }
857  });
858  } else {
859  auto emitLHS = [&]() { emitExpression(op.getDest()); };
860  if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
861  emitAssignLike(
862  emitLHS, [&]() { ps << "invalid"; }, PPExtString("is"));
863  } else {
864  emitAssignLike(
865  emitLHS, [&]() { emitExpression(op.getSrc()); }, PPExtString("<="));
866  }
867  }
868  emitLocationAndNewLine(op);
869 }
870 
871 void Emitter::emitStatement(PropAssignOp op) {
872  startStatement();
873  ps.scopedBox(PP::ibox2, [&]() {
874  ps << "propassign" << PP::space;
875  interleaveComma(op.getOperands());
876  });
877  emitLocationAndNewLine(op);
878 }
879 
880 void Emitter::emitStatement(InstanceOp op) {
881  startStatement();
882  auto legalName = legalize(op.getNameAttr());
883  ps << "inst " << PPExtString(legalName) << " of "
884  << PPExtString(legalize(op.getModuleNameAttr().getAttr()));
885  emitLocationAndNewLine(op);
886 
887  // Make sure we have a name like `<inst>.<port>` for each of the instance
888  // result values.
889  SmallString<16> portName(legalName);
890  portName.push_back('.');
891  unsigned baseLen = portName.size();
892  for (unsigned i = 0, e = op.getNumResults(); i < e; ++i) {
893  portName.append(legalize(op.getPortName(i)));
894  addValueName(op.getResult(i), portName);
895  portName.resize(baseLen);
896  }
897 }
898 
899 void Emitter::emitStatement(InstanceChoiceOp op) {
900  startStatement();
901  auto legalName = legalize(op.getNameAttr());
902  ps << "instchoice " << PPExtString(legalName) << " of "
903  << PPExtString(legalize(op.getDefaultTargetAttr().getAttr())) << ", "
904  << PPExtString(legalize(op.getOptionNameAttr())) << " :";
905  emitLocation(op);
906  ps.scopedBox(PP::bbox2, [&] {
907  for (const auto &[optSym, targetSym] : op.getTargetChoices()) {
908  ps << PP::newline;
909  ps << PPExtString(legalize(optSym.getLeafReference()));
910  ps << " => ";
911  ps << PPExtString(legalize(targetSym.getAttr()));
912  }
913  });
914  setPendingNewline();
915 
916  SmallString<16> portName(legalName);
917  portName.push_back('.');
918  unsigned baseLen = portName.size();
919  for (unsigned i = 0, e = op.getNumResults(); i < e; ++i) {
920  portName.append(legalize(op.getPortName(i)));
921  addValueName(op.getResult(i), portName);
922  portName.resize(baseLen);
923  }
924 }
925 
926 void Emitter::emitStatement(AttachOp op) {
927  emitStatementFunctionOp(PPExtString("attach"), op);
928 }
929 
930 void Emitter::emitStatement(MemOp op) {
931  auto legalName = legalize(op.getNameAttr());
932  SmallString<16> portName(legalName);
933  portName.push_back('.');
934  auto portNameBaseLen = portName.size();
935  for (auto result : llvm::zip(op.getResults(), op.getPortNames())) {
936  portName.resize(portNameBaseLen);
937  portName.append(legalize(cast<StringAttr>(std::get<1>(result))));
938  addValueName(std::get<0>(result), portName);
939  }
940 
941  startStatement();
942  ps << "mem " << PPExtString(legalName) << " :";
943  emitLocationAndNewLine(op);
944  ps.scopedBox(PP::bbox2, [&]() {
945  startStatement();
946  ps << "data-type => ";
947  emitType(op.getDataType());
948  ps << PP::newline;
949  ps << "depth => ";
950  ps.addAsString(op.getDepth());
951  ps << PP::newline;
952  ps << "read-latency => ";
953  ps.addAsString(op.getReadLatency());
954  ps << PP::newline;
955  ps << "write-latency => ";
956  ps.addAsString(op.getWriteLatency());
957  ps << PP::newline;
958 
959  SmallString<16> reader, writer, readwriter;
960  for (std::pair<StringAttr, MemOp::PortKind> port : op.getPorts()) {
961  auto add = [&](SmallString<16> &to, StringAttr name) {
962  if (!to.empty())
963  to.push_back(' ');
964  to.append(name.getValue());
965  };
966  switch (port.second) {
967  case MemOp::PortKind::Read:
968  add(reader, legalize(port.first));
969  break;
970  case MemOp::PortKind::Write:
971  add(writer, legalize(port.first));
972  break;
973  case MemOp::PortKind::ReadWrite:
974  add(readwriter, legalize(port.first));
975  break;
976  case MemOp::PortKind::Debug:
977  emitOpError(op, "has unsupported 'debug' port");
978  return;
979  }
980  }
981  if (!reader.empty())
982  ps << "reader => " << reader << PP::newline;
983  if (!writer.empty())
984  ps << "writer => " << writer << PP::newline;
985  if (!readwriter.empty())
986  ps << "readwriter => " << readwriter << PP::newline;
987 
988  ps << "read-under-write => ";
989  emitAttribute(op.getRuw());
990  setPendingNewline();
991  });
992 }
993 
994 void Emitter::emitStatement(SeqMemOp op) {
995  startStatement();
996  ps.scopedBox(PP::ibox2, [&]() {
997  ps << "smem " << PPExtString(legalize(op.getNameAttr()));
998  emitTypeWithColon(op.getType());
999  ps << PP::space;
1000  emitAttribute(op.getRuw());
1001  });
1002  emitLocationAndNewLine(op);
1003 }
1004 
1005 void Emitter::emitStatement(CombMemOp op) {
1006  startStatement();
1007  ps.scopedBox(PP::ibox2, [&]() {
1008  ps << "cmem " << PPExtString(legalize(op.getNameAttr()));
1009  emitTypeWithColon(op.getType());
1010  });
1011  emitLocationAndNewLine(op);
1012 }
1013 
1014 void Emitter::emitStatement(MemoryPortOp op) {
1015  // Nothing to output for this operation.
1016  addValueName(op.getData(), legalize(op.getNameAttr()));
1017 }
1018 
1019 void Emitter::emitStatement(MemoryDebugPortOp op) {
1020  // Nothing to output for this operation.
1021  addValueName(op.getData(), legalize(op.getNameAttr()));
1022 }
1023 
1024 void Emitter::emitStatement(MemoryPortAccessOp op) {
1025  startStatement();
1026 
1027  // Print the port direction and name.
1028  auto port = cast<MemoryPortOp>(op.getPort().getDefiningOp());
1029  emitAttribute(port.getDirection());
1030  // TODO: emitAssignLike
1031  ps << " mport " << PPExtString(legalize(port.getNameAttr())) << " = ";
1032 
1033  // Print the memory name.
1034  auto *mem = port.getMemory().getDefiningOp();
1035  if (auto seqMem = dyn_cast<SeqMemOp>(mem))
1036  ps << legalize(seqMem.getNameAttr());
1037  else
1038  ps << legalize(cast<CombMemOp>(mem).getNameAttr());
1039 
1040  // Print the address.
1041  ps << "[";
1042  emitExpression(op.getIndex());
1043  ps << "], ";
1044 
1045  // Print the clock.
1046  emitExpression(op.getClock());
1047 
1048  emitLocationAndNewLine(op);
1049 }
1050 
1051 void Emitter::emitStatement(RefDefineOp op) {
1052  startStatement();
1053  emitAssignLike([&]() { emitExpression(op.getDest()); },
1054  [&]() { emitExpression(op.getSrc()); }, PPExtString("="),
1055  PPExtString("define"));
1056  emitLocationAndNewLine(op);
1057 }
1058 
1059 void Emitter::emitStatement(RefForceOp op) {
1060  emitStatementFunctionOp(PPExtString("force"), op);
1061 }
1062 
1063 void Emitter::emitStatement(RefForceInitialOp op) {
1064  startStatement();
1065  auto constantPredicate =
1066  dyn_cast_or_null<ConstantOp>(op.getPredicate().getDefiningOp());
1067  bool hasEnable = !constantPredicate || constantPredicate.getValue() == 0;
1068  if (hasEnable) {
1069  ps << "when ";
1070  emitExpression(op.getPredicate());
1071  ps << ":" << PP::bbox2 << PP::neverbreak << PP::newline;
1072  }
1073  ps << "force_initial(";
1074  ps.scopedBox(PP::ibox0, [&]() {
1075  interleaveComma({op.getDest(), op.getSrc()});
1076  ps << ")";
1077  });
1078  if (hasEnable)
1079  ps << PP::end;
1080  emitLocationAndNewLine(op);
1081 }
1082 
1083 void Emitter::emitStatement(RefReleaseOp op) {
1084  emitStatementFunctionOp(PPExtString("release"), op);
1085 }
1086 
1087 void Emitter::emitStatement(RefReleaseInitialOp op) {
1088  startStatement();
1089  auto constantPredicate =
1090  dyn_cast_or_null<ConstantOp>(op.getPredicate().getDefiningOp());
1091  bool hasEnable = !constantPredicate || constantPredicate.getValue() == 0;
1092  if (hasEnable) {
1093  ps << "when ";
1094  emitExpression(op.getPredicate());
1095  ps << ":" << PP::bbox2 << PP::neverbreak << PP::newline;
1096  }
1097  ps << "release_initial(";
1098  emitExpression(op.getDest());
1099  ps << ")";
1100  if (hasEnable)
1101  ps << PP::end;
1102  emitLocationAndNewLine(op);
1103 }
1104 
1105 void Emitter::emitStatement(LayerBlockOp op) {
1106  startStatement();
1107  ps << "layerblock " << op.getLayerName().getLeafReference() << " :";
1108  emitLocationAndNewLine(op);
1109  auto *body = op.getBody();
1110  ps.scopedBox(PP::bbox2, [&]() { emitStatementsInBlock(*body); });
1111 }
1112 
1113 void Emitter::emitStatement(InvalidValueOp op) {
1114  // Only emit this invalid value if it is used somewhere else than the RHS of
1115  // a connect.
1116  if (llvm::all_of(op->getUses(), [&](OpOperand &use) {
1117  return use.getOperandNumber() == 1 &&
1118  isa<ConnectOp, StrictConnectOp>(use.getOwner());
1119  }))
1120  return;
1121 
1122  // TODO: emitAssignLike ?
1123  startStatement();
1124  auto name = circuitNamespace.newName("_invalid");
1125  addValueName(op, name);
1126  ps << "wire " << PPExtString(name) << " : ";
1127  emitType(op.getType());
1128  emitLocationAndNewLine(op);
1129  startStatement();
1130  if (FIRVersion(3, 0, 0) <= version)
1131  ps << "invalidate " << PPExtString(name);
1132  else
1133  ps << PPExtString(name) << " is invalid";
1134  emitLocationAndNewLine(op);
1135 }
1136 
1137 void Emitter::emitStatement(GenericIntrinsicOp op) {
1138  startStatement();
1139  if (op.use_empty())
1140  emitGenericIntrinsic(op);
1141  else {
1142  assert(!isEmittedInline(op));
1143  auto name = circuitNamespace.newName("_gen_int");
1144  addValueName(op.getResult(), name);
1145  emitAssignLike([&]() { ps << "node " << PPExtString(name); },
1146  [&]() { emitGenericIntrinsic(op); });
1147  }
1148  emitLocationAndNewLine(op);
1149 }
1150 
1151 void Emitter::emitExpression(Value value) {
1152  // Handle the trivial case where we already have a name for this value which
1153  // we can use.
1154  if (auto name = lookupEmittedName(value)) {
1155  // Don't use PPExtString here, can't trust valueNames storage, cleared.
1156  ps << *name;
1157  return;
1158  }
1159 
1160  auto op = value.getDefiningOp();
1161  assert(op && "value must either be a block arg or the result of an op");
1162  TypeSwitch<Operation *>(op)
1163  .Case<
1164  // Basic expressions
1165  ConstantOp, SpecialConstantOp, SubfieldOp, SubindexOp, SubaccessOp,
1166  OpenSubfieldOp, OpenSubindexOp,
1167  // Binary
1168  AddPrimOp, SubPrimOp, MulPrimOp, DivPrimOp, RemPrimOp, AndPrimOp,
1169  OrPrimOp, XorPrimOp, LEQPrimOp, LTPrimOp, GEQPrimOp, GTPrimOp,
1170  EQPrimOp, NEQPrimOp, CatPrimOp, DShlPrimOp, DShlwPrimOp, DShrPrimOp,
1171  // Unary
1172  AsSIntPrimOp, AsUIntPrimOp, AsAsyncResetPrimOp, AsClockPrimOp,
1173  CvtPrimOp, NegPrimOp, NotPrimOp, AndRPrimOp, OrRPrimOp, XorRPrimOp,
1174  // Miscellaneous
1175  BitsPrimOp, HeadPrimOp, TailPrimOp, PadPrimOp, MuxPrimOp, ShlPrimOp,
1176  ShrPrimOp, UninferredResetCastOp, ConstCastOp, StringConstantOp,
1177  FIntegerConstantOp, BoolConstantOp, DoubleConstantOp, ListCreateOp,
1178  UnresolvedPathOp, GenericIntrinsicOp,
1179  // Reference expressions
1180  RefSendOp, RefResolveOp, RefSubOp, RWProbeOp, RefCastOp>(
1181  [&](auto op) {
1182  ps.scopedBox(PP::ibox0, [&]() { emitExpression(op); });
1183  })
1184  .Default([&](auto op) {
1185  emitOpError(op, "not supported as expression");
1186  ps << "<unsupported-expr-" << PPExtString(op->getName().stripDialect())
1187  << ">";
1188  });
1189 }
1190 
1191 void Emitter::emitExpression(ConstantOp op) {
1192  // Don't include 'const' on the type in a literal expression
1193  emitType(op.getType(), false);
1194  // TODO: Add option to control base-2/8/10/16 output here.
1195  ps << "(";
1196  ps.addAsString(op.getValue());
1197  ps << ")";
1198 }
1199 
1200 void Emitter::emitExpression(SpecialConstantOp op) {
1201  auto emitInner = [&]() {
1202  ps << "UInt<1>(";
1203  ps.addAsString(op.getValue());
1204  ps << ")";
1205  };
1206  // TODO: Emit type decl for type alias.
1207  FIRRTLTypeSwitch<FIRRTLType>(type_cast<FIRRTLType>(op.getType()))
1208  .Case<ClockType>([&](auto type) {
1209  ps << "asClock(";
1210  emitInner();
1211  ps << ")";
1212  })
1213  .Case<ResetType>([&](auto type) { emitInner(); })
1214  .Case<AsyncResetType>([&](auto type) {
1215  ps << "asAsyncReset(";
1216  emitInner();
1217  ps << ")";
1218  });
1219 }
1220 
1221 // NOLINTNEXTLINE(misc-no-recursion)
1222 void Emitter::emitExpression(SubfieldOp op) {
1223  BundleType type = op.getInput().getType();
1224  emitExpression(op.getInput());
1225  ps << "." << legalize(type.getElementNameAttr(op.getFieldIndex()));
1226 }
1227 
1228 // NOLINTNEXTLINE(misc-no-recursion)
1229 void Emitter::emitExpression(SubindexOp op) {
1230  emitExpression(op.getInput());
1231  ps << "[";
1232  ps.addAsString(op.getIndex());
1233  ps << "]";
1234 }
1235 
1236 // NOLINTNEXTLINE(misc-no-recursion)
1237 void Emitter::emitExpression(SubaccessOp op) {
1238  emitExpression(op.getInput());
1239  ps << "[";
1240  emitExpression(op.getIndex());
1241  ps << "]";
1242 }
1243 
1244 void Emitter::emitExpression(OpenSubfieldOp op) {
1245  auto type = op.getInput().getType();
1246  emitExpression(op.getInput());
1247  ps << "." << legalize(type.getElementNameAttr(op.getFieldIndex()));
1248 }
1249 
1250 void Emitter::emitExpression(OpenSubindexOp op) {
1251  emitExpression(op.getInput());
1252  ps << "[";
1253  ps.addAsString(op.getIndex());
1254  ps << "]";
1255 }
1256 
1257 void Emitter::emitExpression(RefSendOp op) {
1258  ps << "probe(";
1259  emitExpression(op.getBase());
1260  ps << ")";
1261 }
1262 
1263 void Emitter::emitExpression(RefResolveOp op) {
1264  ps << "read(";
1265  emitExpression(op.getRef());
1266  ps << ")";
1267 }
1268 
1269 void Emitter::emitExpression(RefSubOp op) {
1270  emitExpression(op.getInput());
1271  FIRRTLTypeSwitch<FIRRTLBaseType, void>(op.getInput().getType().getType())
1272  .Case<FVectorType>([&](auto type) {
1273  ps << "[";
1274  ps.addAsString(op.getIndex());
1275  ps << "]";
1276  })
1277  .Case<BundleType>(
1278  [&](auto type) { ps << "." << type.getElementName(op.getIndex()); });
1279 }
1280 
1281 void Emitter::emitExpression(RWProbeOp op) {
1282  ps << "rwprobe(";
1283 
1284  // Find the probe target.
1285  auto target = symInfos->get().irn.lookup(op.getTarget());
1286  Value base;
1287  if (target.isPort()) {
1288  auto mod = cast<FModuleOp>(target.getOp());
1289  auto port = target.getPort();
1290  base = mod.getArgument(port);
1291  } else
1292  base = cast<hw::InnerSymbolOpInterface>(target.getOp()).getTargetResult();
1293 
1294  // Print target. Needs this to have a name already.
1295  emitExpression(base);
1296 
1297  // Print indexing for the target field.
1298  auto fieldID = target.getField();
1299  auto type = base.getType();
1300  while (fieldID) {
1302  .Case<FVectorType, OpenVectorType>([&](auto vecTy) {
1303  auto index = vecTy.getIndexForFieldID(fieldID);
1304  ps << "[";
1305  ps.addAsString(index);
1306  ps << "]";
1307  auto [subtype, subfieldID] = vecTy.getSubTypeByFieldID(fieldID);
1308  type = subtype;
1309  fieldID = subfieldID;
1310  })
1311  .Case<BundleType, OpenBundleType>([&](auto bundleTy) {
1312  auto index = bundleTy.getIndexForFieldID(fieldID);
1313  ps << "." << bundleTy.getElementName(index);
1314  auto [subtype, subfieldID] = bundleTy.getSubTypeByFieldID(fieldID);
1315  type = subtype;
1316  fieldID = subfieldID;
1317  });
1318  }
1319  ps << ")";
1320 }
1321 
1322 void Emitter::emitExpression(RefCastOp op) { emitExpression(op.getInput()); }
1323 
1324 void Emitter::emitExpression(UninferredResetCastOp op) {
1325  emitExpression(op.getInput());
1326 }
1327 
1328 void Emitter::emitExpression(FIntegerConstantOp op) {
1329  ps << "Integer(";
1330  ps.addAsString(op.getValue());
1331  ps << ")";
1332 }
1333 
1334 void Emitter::emitExpression(BoolConstantOp op) {
1335  ps << "Bool(" << (op.getValue() ? "true" : "false") << ")";
1336 }
1337 
1338 void Emitter::emitExpression(DoubleConstantOp op) {
1339  ps << "Double(";
1340  // Use APFloat::toString.
1341  // Printing as double is not what we want,
1342  // and this at least handles the basic cases in a way
1343  // that will round-trip.
1344  SmallString<16> str;
1345  op.getValueAttr().getValue().toString(str);
1346  ps << str;
1347  ps << ")";
1348 }
1349 
1350 void Emitter::emitExpression(StringConstantOp op) {
1351  ps << "String(";
1352  ps.writeQuotedEscaped(op.getValue());
1353  ps << ")";
1354 }
1355 
1356 void Emitter::emitExpression(ListCreateOp op) {
1357  return emitLiteralExpression(op.getType(), op.getElements());
1358 }
1359 
1360 void Emitter::emitExpression(UnresolvedPathOp op) {
1361  ps << "path(";
1362  ps.writeQuotedEscaped(op.getTarget());
1363  ps << ")";
1364 }
1365 
1366 void Emitter::emitExpression(GenericIntrinsicOp op) {
1367  emitGenericIntrinsic(op);
1368 }
1369 
1370 void Emitter::emitExpression(ConstCastOp op) { emitExpression(op.getInput()); }
1371 
1372 void Emitter::emitPrimExpr(StringRef mnemonic, Operation *op,
1373  ArrayRef<uint32_t> attrs) {
1374  ps << mnemonic << "(" << PP::ibox0;
1375  interleaveComma(op->getOperands());
1376  if (!op->getOperands().empty() && !attrs.empty())
1377  ps << "," << PP::space;
1378  interleaveComma(attrs, [&](auto attr) { ps.addAsString(attr); });
1379  ps << ")" << PP::end;
1380 }
1381 
1382 void Emitter::emitAttribute(MemDirAttr attr) {
1383  switch (attr) {
1384  case MemDirAttr::Infer:
1385  ps << "infer";
1386  break;
1387  case MemDirAttr::Read:
1388  ps << "read";
1389  break;
1390  case MemDirAttr::Write:
1391  ps << "write";
1392  break;
1393  case MemDirAttr::ReadWrite:
1394  ps << "rdwr";
1395  break;
1396  }
1397 }
1398 
1399 void Emitter::emitAttribute(RUWAttr attr) {
1400  switch (attr) {
1401  case RUWAttr::Undefined:
1402  ps << "undefined";
1403  break;
1404  case RUWAttr::Old:
1405  ps << "old";
1406  break;
1407  case RUWAttr::New:
1408  ps << "new";
1409  break;
1410  }
1411 }
1412 
1413 /// Emit a FIRRTL type into the output.
1414 void Emitter::emitType(Type type, bool includeConst) {
1415  if (includeConst && isConst(type))
1416  ps << "const ";
1417  auto emitWidth = [&](std::optional<int32_t> width) {
1418  if (width) {
1419  ps << "<";
1420  ps.addAsString(*width);
1421  ps << ">";
1422  }
1423  };
1424  // TODO: Emit type decl for type alias.
1426  .Case<ClockType>([&](auto) { ps << "Clock"; })
1427  .Case<ResetType>([&](auto) { ps << "Reset"; })
1428  .Case<AsyncResetType>([&](auto) { ps << "AsyncReset"; })
1429  .Case<UIntType>([&](auto type) {
1430  ps << "UInt";
1431  emitWidth(type.getWidth());
1432  })
1433  .Case<SIntType>([&](auto type) {
1434  ps << "SInt";
1435  emitWidth(type.getWidth());
1436  })
1437  .Case<AnalogType>([&](auto type) {
1438  ps << "Analog";
1439  emitWidth(type.getWidth());
1440  })
1441  .Case<OpenBundleType, BundleType>([&](auto type) {
1442  ps << "{";
1443  if (!type.getElements().empty())
1444  ps << PP::nbsp;
1445  bool anyEmitted = false;
1446  ps.scopedBox(PP::cbox0, [&]() {
1447  for (auto &element : type.getElements()) {
1448  if (anyEmitted)
1449  ps << "," << PP::space;
1450  ps.scopedBox(PP::ibox2, [&]() {
1451  if (element.isFlip)
1452  ps << "flip ";
1453  ps << legalize(element.name);
1454  emitTypeWithColon(element.type);
1455  anyEmitted = true;
1456  });
1457  }
1458  if (anyEmitted)
1459  ps << PP::nbsp;
1460  ps << "}";
1461  });
1462  })
1463  .Case<OpenVectorType, FVectorType, CMemoryType>([&](auto type) {
1464  emitType(type.getElementType());
1465  ps << "[";
1466  ps.addAsString(type.getNumElements());
1467  ps << "]";
1468  })
1469  .Case<RefType>([&](RefType type) {
1470  if (type.getForceable())
1471  ps << "RW";
1472  ps << "Probe<";
1473  ps.cbox(2, IndentStyle::Block);
1474  ps.zerobreak();
1475  emitType(type.getType());
1476  if (auto layer = type.getLayer()) {
1477  ps << ",";
1478  ps.space();
1479  emitSymbol(type.getLayer());
1480  }
1481  ps << BreakToken(0, -2) << ">";
1482  ps.end();
1483  })
1484  .Case<AnyRefType>([&](AnyRefType type) { ps << "AnyRef"; })
1485  .Case<StringType>([&](StringType type) { ps << "String"; })
1486  .Case<FIntegerType>([&](FIntegerType type) { ps << "Integer"; })
1487  .Case<BoolType>([&](BoolType type) { ps << "Bool"; })
1488  .Case<DoubleType>([&](DoubleType type) { ps << "Double"; })
1489  .Case<PathType>([&](PathType type) { ps << "Path"; })
1490  .Case<ListType>([&](ListType type) {
1491  ps << "List<";
1492  emitType(type.getElementType());
1493  ps << ">";
1494  })
1495  .Default([&](auto type) {
1496  llvm_unreachable("all types should be implemented");
1497  });
1498 }
1499 
1500 /// Emit a location as `@[<filename> <line>:<column>]` annotation, including a
1501 /// leading space.
1502 void Emitter::emitLocation(Location loc) {
1503  // TODO: Handle FusedLoc and uniquify locations, avoid repeated file names.
1504  ps << PP::neverbreak;
1505  if (auto fileLoc = loc->dyn_cast_or_null<FileLineColLoc>()) {
1506  ps << " @[" << fileLoc.getFilename().getValue();
1507  if (auto line = fileLoc.getLine()) {
1508  ps << " ";
1509  ps.addAsString(line);
1510  if (auto col = fileLoc.getColumn()) {
1511  ps << ":";
1512  ps.addAsString(col);
1513  }
1514  }
1515  ps << "]";
1516  }
1517 }
1518 // NOLINTEND(misc-no-recursion)
1519 
1520 //===----------------------------------------------------------------------===//
1521 // Driver
1522 //===----------------------------------------------------------------------===//
1523 
1524 // Emit the specified FIRRTL circuit into the given output stream.
1525 mlir::LogicalResult
1526 circt::firrtl::exportFIRFile(mlir::ModuleOp module, llvm::raw_ostream &os,
1527  std::optional<size_t> targetLineLength,
1528  FIRVersion version) {
1529  Emitter emitter(os, version,
1530  targetLineLength.value_or(defaultTargetLineLength));
1531  for (auto &op : *module.getBody()) {
1532  if (auto circuitOp = dyn_cast<CircuitOp>(op))
1533  emitter.emitCircuit(circuitOp);
1534  }
1535  return emitter.finalize();
1536 }
1537 
1539  static llvm::cl::opt<size_t> targetLineLength(
1540  "target-line-length",
1541  llvm::cl::desc("Target line length for emitted .fir"),
1542  llvm::cl::value_desc("number of chars"),
1543  llvm::cl::init(defaultTargetLineLength));
1544  static mlir::TranslateFromMLIRRegistration toFIR(
1545  "export-firrtl", "emit FIRRTL dialect operations to .fir output",
1546  [](ModuleOp module, llvm::raw_ostream &os) {
1547  return exportFIRFile(module, os, targetLineLength, exportFIRVersion);
1548  },
1549  [](mlir::DialectRegistry &registry) {
1550  registry.insert<chirrtl::CHIRRTLDialect>();
1551  registry.insert<firrtl::FIRRTLDialect>();
1552  });
1553 }
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:621
#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:518
FIRRTLTypeSwitch< T, ResultT > & Case(CallableT &&caseFn)
Add a case on the given type.
Definition: FIRRTLTypes.h:528
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:3865
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
Definition: DebugAnalysis.h:21
The namespace of a CircuitOp, generally inhabited by modules.
Definition: Namespace.h:24
The FIRRTL specification version.
Definition: FIRParser.h:81
String wrapper to indicate string has external storage.