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