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