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