CIRCT  18.0.0git
FIRRTLOps.cpp
Go to the documentation of this file.
1 //===- FIRRTLOps.cpp - Implement the FIRRTL operations --------------------===//
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 file implement the FIRRTL ops.
10 //
11 //===----------------------------------------------------------------------===//
12 
23 #include "mlir/IR/BuiltinTypes.h"
24 #include "mlir/IR/Diagnostics.h"
25 #include "mlir/IR/DialectImplementation.h"
26 #include "mlir/IR/PatternMatch.h"
27 #include "mlir/IR/SymbolTable.h"
28 #include "mlir/Interfaces/FunctionImplementation.h"
29 #include "llvm/ADT/BitVector.h"
30 #include "llvm/ADT/DenseMap.h"
31 #include "llvm/ADT/DenseSet.h"
32 #include "llvm/ADT/STLExtras.h"
33 #include "llvm/ADT/StringExtras.h"
34 #include "llvm/ADT/TypeSwitch.h"
35 #include "llvm/Support/FormatVariadic.h"
36 
37 using llvm::SmallDenseSet;
38 using mlir::RegionRange;
39 using namespace circt;
40 using namespace firrtl;
41 using namespace chirrtl;
42 
43 //===----------------------------------------------------------------------===//
44 // Utilities
45 //===----------------------------------------------------------------------===//
46 
47 /// Remove elements from the input array corresponding to set bits in
48 /// `indicesToDrop`, returning the elements not mentioned.
49 template <typename T>
50 static SmallVector<T>
51 removeElementsAtIndices(ArrayRef<T> input,
52  const llvm::BitVector &indicesToDrop) {
53 #ifndef NDEBUG
54  if (!input.empty()) {
55  int lastIndex = indicesToDrop.find_last();
56  if (lastIndex >= 0)
57  assert((size_t)lastIndex < input.size() && "index out of range");
58  }
59 #endif
60 
61  // If the input is empty (which is an optimization we do for certain array
62  // attributes), simply return an empty vector.
63  if (input.empty())
64  return {};
65 
66  // Copy over the live chunks.
67  size_t lastCopied = 0;
68  SmallVector<T> result;
69  result.reserve(input.size() - indicesToDrop.count());
70 
71  for (unsigned indexToDrop : indicesToDrop.set_bits()) {
72  // If we skipped over some valid elements, copy them over.
73  if (indexToDrop > lastCopied) {
74  result.append(input.begin() + lastCopied, input.begin() + indexToDrop);
75  lastCopied = indexToDrop;
76  }
77  // Ignore this value so we don't copy it in the next iteration.
78  ++lastCopied;
79  }
80 
81  // If there are live elements at the end, copy them over.
82  if (lastCopied < input.size())
83  result.append(input.begin() + lastCopied, input.end());
84 
85  return result;
86 }
87 
88 /// Emit an error if optional location is non-null, return null of return type.
89 template <typename RetTy = FIRRTLType, typename... Args>
90 static RetTy emitInferRetTypeError(std::optional<Location> loc,
91  const Twine &message, Args &&...args) {
92  if (loc)
93  (mlir::emitError(*loc, message) << ... << std::forward<Args>(args));
94  return {};
95 }
96 
97 bool firrtl::isDuplexValue(Value val) {
98  // Block arguments are not duplex values.
99  while (Operation *op = val.getDefiningOp()) {
100  auto isDuplex =
101  TypeSwitch<Operation *, std::optional<bool>>(op)
102  .Case<SubfieldOp, SubindexOp, SubaccessOp>([&val](auto op) {
103  val = op.getInput();
104  return std::nullopt;
105  })
106  .Case<RegOp, RegResetOp, WireOp>([](auto) { return true; })
107  .Default([](auto) { return false; });
108  if (isDuplex)
109  return *isDuplex;
110  }
111  return false;
112 }
113 
114 /// Return the kind of port this is given the port type from a 'mem' decl.
115 static MemOp::PortKind getMemPortKindFromType(FIRRTLType type) {
116  constexpr unsigned int addr = 1 << 0;
117  constexpr unsigned int en = 1 << 1;
118  constexpr unsigned int clk = 1 << 2;
119  constexpr unsigned int data = 1 << 3;
120  constexpr unsigned int mask = 1 << 4;
121  constexpr unsigned int rdata = 1 << 5;
122  constexpr unsigned int wdata = 1 << 6;
123  constexpr unsigned int wmask = 1 << 7;
124  constexpr unsigned int wmode = 1 << 8;
125  constexpr unsigned int def = 1 << 9;
126  // Get the kind of port based on the fields of the Bundle.
127  auto portType = type_dyn_cast<BundleType>(type);
128  if (!portType)
129  return MemOp::PortKind::Debug;
130  unsigned fields = 0;
131  // Get the kind of port based on the fields of the Bundle.
132  for (auto elem : portType.getElements()) {
133  fields |= llvm::StringSwitch<unsigned>(elem.name.getValue())
134  .Case("addr", addr)
135  .Case("en", en)
136  .Case("clk", clk)
137  .Case("data", data)
138  .Case("mask", mask)
139  .Case("rdata", rdata)
140  .Case("wdata", wdata)
141  .Case("wmask", wmask)
142  .Case("wmode", wmode)
143  .Default(def);
144  }
145  if (fields == (addr | en | clk | data))
146  return MemOp::PortKind::Read;
147  if (fields == (addr | en | clk | data | mask))
148  return MemOp::PortKind::Write;
149  if (fields == (addr | en | clk | wdata | wmask | rdata | wmode))
150  return MemOp::PortKind::ReadWrite;
151  return MemOp::PortKind::Debug;
152 }
153 
155  switch (flow) {
156  case Flow::None:
157  return Flow::None;
158  case Flow::Source:
159  return Flow::Sink;
160  case Flow::Sink:
161  return Flow::Source;
162  case Flow::Duplex:
163  return Flow::Duplex;
164  }
165 }
166 
167 constexpr const char *toString(Flow flow) {
168  switch (flow) {
169  case Flow::None:
170  return "no flow";
171  case Flow::Source:
172  return "source flow";
173  case Flow::Sink:
174  return "sink flow";
175  case Flow::Duplex:
176  return "duplex flow";
177  }
178 }
179 
180 Flow firrtl::foldFlow(Value val, Flow accumulatedFlow) {
181  auto swap = [&accumulatedFlow]() -> Flow {
182  return swapFlow(accumulatedFlow);
183  };
184 
185  if (auto blockArg = dyn_cast<BlockArgument>(val)) {
186  auto *op = val.getParentBlock()->getParentOp();
187  if (auto moduleLike = dyn_cast<FModuleLike>(op)) {
188  auto direction = moduleLike.getPortDirection(blockArg.getArgNumber());
189  if (direction == Direction::Out)
190  return swap();
191  }
192  return accumulatedFlow;
193  }
194 
195  Operation *op = val.getDefiningOp();
196 
197  return TypeSwitch<Operation *, Flow>(op)
198  .Case<SubfieldOp, OpenSubfieldOp>([&](auto op) {
199  return foldFlow(op.getInput(),
200  op.isFieldFlipped() ? swap() : accumulatedFlow);
201  })
202  .Case<SubindexOp, SubaccessOp, OpenSubindexOp, RefSubOp>(
203  [&](auto op) { return foldFlow(op.getInput(), accumulatedFlow); })
204  // Registers, Wires, and behavioral memory ports are always Duplex.
205  .Case<RegOp, RegResetOp, WireOp, MemoryPortOp>(
206  [](auto) { return Flow::Duplex; })
207  .Case<InstanceOp>([&](auto inst) {
208  auto resultNo = cast<OpResult>(val).getResultNumber();
209  if (inst.getPortDirection(resultNo) == Direction::Out)
210  return accumulatedFlow;
211  return swap();
212  })
213  .Case<MemOp>([&](auto op) {
214  // only debug ports with RefType have source flow.
215  if (type_isa<RefType>(val.getType()))
216  return Flow::Source;
217  return swap();
218  })
219  .Case<ObjectSubfieldOp>([&](ObjectSubfieldOp op) {
220  auto input = op.getInput();
221  auto *inputOp = input.getDefiningOp();
222 
223  // We are directly accessing a port on a local declaration.
224  if (auto objectOp = dyn_cast_or_null<ObjectOp>(inputOp)) {
225  auto classType = input.getType();
226  auto direction = classType.getElement(op.getIndex()).direction;
227  if (direction == Direction::In)
228  return Flow::Sink;
229  return Flow::Source;
230  }
231 
232  // We are accessing a remote object. Input ports on remote objects are
233  // inaccessible, and thus have Flow::None. Walk backwards through the
234  // chain of subindexes, to detect if we have indexed through an input
235  // port. At the end, either we did index through an input port, or the
236  // entire path was through output ports with source flow.
237  while (true) {
238  auto classType = input.getType();
239  auto direction = classType.getElement(op.getIndex()).direction;
240  if (direction == Direction::In)
241  return Flow::None;
242 
243  op = dyn_cast_or_null<ObjectSubfieldOp>(inputOp);
244  if (op) {
245  input = op.getInput();
246  inputOp = input.getDefiningOp();
247  continue;
248  }
249 
250  return accumulatedFlow;
251  };
252  })
253  // Anything else acts like a universal source.
254  .Default([&](auto) { return accumulatedFlow; });
255 }
256 
257 // TODO: This is doing the same walk as foldFlow. These two functions can be
258 // combined and return a (flow, kind) product.
260  Operation *op = val.getDefiningOp();
261  if (!op)
262  return DeclKind::Port;
263 
264  return TypeSwitch<Operation *, DeclKind>(op)
265  .Case<InstanceOp>([](auto) { return DeclKind::Instance; })
266  .Case<SubfieldOp, SubindexOp, SubaccessOp, OpenSubfieldOp, OpenSubindexOp,
267  RefSubOp>([](auto op) { return getDeclarationKind(op.getInput()); })
268  .Default([](auto) { return DeclKind::Other; });
269 }
270 
271 size_t firrtl::getNumPorts(Operation *op) {
272  if (auto module = dyn_cast<FModuleLike>(op))
273  return module.getNumPorts();
274  return op->getNumResults();
275 }
276 
277 /// Check whether an operation has a `DontTouch` annotation, or a symbol that
278 /// should prevent certain types of canonicalizations.
279 bool firrtl::hasDontTouch(Operation *op) {
280  return op->getAttr(hw::InnerSymbolTable::getInnerSymbolAttrName()) ||
282 }
283 
284 /// Check whether a block argument ("port") or the operation defining a value
285 /// has a `DontTouch` annotation, or a symbol that should prevent certain types
286 /// of canonicalizations.
287 bool firrtl::hasDontTouch(Value value) {
288  if (auto *op = value.getDefiningOp())
289  return hasDontTouch(op);
290  auto arg = dyn_cast<BlockArgument>(value);
291  auto module = cast<FModuleOp>(arg.getOwner()->getParentOp());
292  return (module.getPortSymbolAttr(arg.getArgNumber())) ||
293  AnnotationSet::forPort(module, arg.getArgNumber()).hasDontTouch();
294 }
295 
296 /// Get a special name to use when printing the entry block arguments of the
297 /// region contained by an operation in this dialect.
298 void getAsmBlockArgumentNamesImpl(Operation *op, mlir::Region &region,
299  OpAsmSetValueNameFn setNameFn) {
300  if (region.empty())
301  return;
302  auto *parentOp = op;
303  auto *block = &region.front();
304  // Check to see if the operation containing the arguments has 'firrtl.name'
305  // attributes for them. If so, use that as the name.
306  auto argAttr = parentOp->getAttrOfType<ArrayAttr>("portNames");
307  // Do not crash on invalid IR.
308  if (!argAttr || argAttr.size() != block->getNumArguments())
309  return;
310 
311  for (size_t i = 0, e = block->getNumArguments(); i != e; ++i) {
312  auto str = cast<StringAttr>(argAttr[i]).getValue();
313  if (!str.empty())
314  setNameFn(block->getArgument(i), str);
315  }
316 }
317 
318 /// A forward declaration for `NameKind` attribute parser.
319 static ParseResult parseNameKind(OpAsmParser &parser,
320  firrtl::NameKindEnumAttr &result);
321 
322 //===----------------------------------------------------------------------===//
323 // CircuitOp
324 //===----------------------------------------------------------------------===//
325 
326 void CircuitOp::build(OpBuilder &builder, OperationState &result,
327  StringAttr name, ArrayAttr annotations) {
328  // Add an attribute for the name.
329  result.addAttribute(builder.getStringAttr("name"), name);
330 
331  if (!annotations)
332  annotations = builder.getArrayAttr({});
333  result.addAttribute("annotations", annotations);
334 
335  // Create a region and a block for the body.
336  Region *bodyRegion = result.addRegion();
337  Block *body = new Block();
338  bodyRegion->push_back(body);
339 }
340 
341 // Return the main module that is the entry point of the circuit.
342 FModuleLike CircuitOp::getMainModule(mlir::SymbolTable *symtbl) {
343  if (symtbl)
344  return symtbl->lookup<FModuleLike>(getName());
345  return dyn_cast_or_null<FModuleLike>(lookupSymbol(getName()));
346 }
347 
348 static ParseResult parseCircuitOpAttrs(OpAsmParser &parser,
349  NamedAttrList &resultAttrs) {
350  auto result = parser.parseOptionalAttrDictWithKeyword(resultAttrs);
351  if (!resultAttrs.get("annotations"))
352  resultAttrs.append("annotations", parser.getBuilder().getArrayAttr({}));
353 
354  return result;
355 }
356 
357 static void printCircuitOpAttrs(OpAsmPrinter &p, Operation *op,
358  DictionaryAttr attr) {
359  // "name" is always elided.
360  SmallVector<StringRef> elidedAttrs = {"name"};
361  // Elide "annotations" if it doesn't exist or if it is empty
362  auto annotationsAttr = op->getAttrOfType<ArrayAttr>("annotations");
363  if (annotationsAttr.empty())
364  elidedAttrs.push_back("annotations");
365 
366  p.printOptionalAttrDictWithKeyword(op->getAttrs(), elidedAttrs);
367 }
368 
369 LogicalResult CircuitOp::verifyRegions() {
370  StringRef main = getName();
371 
372  // Check that the circuit has a non-empty name.
373  if (main.empty()) {
374  emitOpError("must have a non-empty name");
375  return failure();
376  }
377 
378  mlir::SymbolTable symtbl(getOperation());
379 
380  // Check that a module matching the "main" module exists in the circuit.
381  auto mainModule = getMainModule(&symtbl);
382  if (!mainModule)
383  return emitOpError("must contain one module that matches main name '" +
384  main + "'");
385 
386  // Even though ClassOps are FModuleLike, they are not a hardware entity, so
387  // we ban them from being our top-module in the design.
388  if (isa<ClassOp>(mainModule))
389  return emitOpError("must have a non-class top module");
390 
391  // Check that the main module is public.
392  if (!mainModule.isPublic())
393  return emitOpError("main module '" + main + "' must be public");
394 
395  // Store a mapping of defname to either the first external module
396  // that defines it or, preferentially, the first external module
397  // that defines it and has no parameters.
398  llvm::DenseMap<Attribute, FExtModuleOp> defnameMap;
399 
400  auto verifyExtModule = [&](FExtModuleOp extModule) -> LogicalResult {
401  if (!extModule)
402  return success();
403 
404  auto defname = extModule.getDefnameAttr();
405  if (!defname)
406  return success();
407 
408  // Check that this extmodule's defname does not conflict with
409  // the symbol name of any module.
410  if (auto collidingModule = symtbl.lookup<FModuleOp>(defname.getValue()))
411  return extModule.emitOpError()
412  .append("attribute 'defname' with value ", defname,
413  " conflicts with the name of another module in the circuit")
414  .attachNote(collidingModule.getLoc())
415  .append("previous module declared here");
416 
417  // Find an optional extmodule with a defname collision. Update
418  // the defnameMap if this is the first extmodule with that
419  // defname or if the current extmodule takes no parameters and
420  // the collision does. The latter condition improves later
421  // extmodule verification as checking against a parameterless
422  // module is stricter.
423  FExtModuleOp collidingExtModule;
424  if (auto &value = defnameMap[defname]) {
425  collidingExtModule = value;
426  if (!value.getParameters().empty() && extModule.getParameters().empty())
427  value = extModule;
428  } else {
429  value = extModule;
430  // Go to the next extmodule if no extmodule with the same
431  // defname was found.
432  return success();
433  }
434 
435  // Check that the number of ports is exactly the same.
436  SmallVector<PortInfo> ports = extModule.getPorts();
437  SmallVector<PortInfo> collidingPorts = collidingExtModule.getPorts();
438 
439  if (ports.size() != collidingPorts.size())
440  return extModule.emitOpError()
441  .append("with 'defname' attribute ", defname, " has ", ports.size(),
442  " ports which is different from a previously defined "
443  "extmodule with the same 'defname' which has ",
444  collidingPorts.size(), " ports")
445  .attachNote(collidingExtModule.getLoc())
446  .append("previous extmodule definition occurred here");
447 
448  // Check that ports match for name and type. Since parameters
449  // *might* affect widths, ignore widths if either module has
450  // parameters. Note that this allows for misdetections, but
451  // has zero false positives.
452  for (auto p : llvm::zip(ports, collidingPorts)) {
453  StringAttr aName = std::get<0>(p).name, bName = std::get<1>(p).name;
454  Type aType = std::get<0>(p).type, bType = std::get<1>(p).type;
455 
456  if (aName != bName)
457  return extModule.emitOpError()
458  .append("with 'defname' attribute ", defname,
459  " has a port with name ", aName,
460  " which does not match the name of the port in the same "
461  "position of a previously defined extmodule with the same "
462  "'defname', expected port to have name ",
463  bName)
464  .attachNote(collidingExtModule.getLoc())
465  .append("previous extmodule definition occurred here");
466 
467  if (!extModule.getParameters().empty() ||
468  !collidingExtModule.getParameters().empty()) {
469  // Compare base types as widthless, others must match.
470  if (auto base = type_dyn_cast<FIRRTLBaseType>(aType))
471  aType = base.getWidthlessType();
472  if (auto base = type_dyn_cast<FIRRTLBaseType>(bType))
473  bType = base.getWidthlessType();
474  }
475  if (aType != bType)
476  return extModule.emitOpError()
477  .append("with 'defname' attribute ", defname,
478  " has a port with name ", aName,
479  " which has a different type ", aType,
480  " which does not match the type of the port in the same "
481  "position of a previously defined extmodule with the same "
482  "'defname', expected port to have type ",
483  bType)
484  .attachNote(collidingExtModule.getLoc())
485  .append("previous extmodule definition occurred here");
486  }
487  return success();
488  };
489 
490  for (auto &op : *getBodyBlock()) {
491  // Verify external modules.
492  if (auto extModule = dyn_cast<FExtModuleOp>(op)) {
493  if (verifyExtModule(extModule).failed())
494  return failure();
495  }
496  }
497 
498  return success();
499 }
500 
501 Block *CircuitOp::getBodyBlock() { return &getBody().front(); }
502 
503 //===----------------------------------------------------------------------===//
504 // FExtModuleOp and FModuleOp
505 //===----------------------------------------------------------------------===//
506 
507 static SmallVector<PortInfo> getPortImpl(FModuleLike module) {
508  SmallVector<PortInfo> results;
509  for (unsigned i = 0, e = module.getNumPorts(); i < e; ++i) {
510  results.push_back({module.getPortNameAttr(i), module.getPortType(i),
511  module.getPortDirection(i), module.getPortSymbolAttr(i),
512  module.getPortLocation(i),
513  AnnotationSet::forPort(module, i)});
514  }
515  return results;
516 }
517 
518 SmallVector<PortInfo> FModuleOp::getPorts() { return ::getPortImpl(*this); }
519 
520 SmallVector<PortInfo> FExtModuleOp::getPorts() { return ::getPortImpl(*this); }
521 
522 SmallVector<PortInfo> FIntModuleOp::getPorts() { return ::getPortImpl(*this); }
523 
524 SmallVector<PortInfo> FMemModuleOp::getPorts() { return ::getPortImpl(*this); }
525 
527  if (dir == Direction::In)
529  if (dir == Direction::Out)
531  assert(0 && "invalid direction");
532  abort();
533 }
534 
535 static hw::ModulePortInfo getPortListImpl(FModuleLike module) {
536  SmallVector<hw::PortInfo> results;
537  for (unsigned i = 0, e = getNumPorts(module); i < e; ++i) {
538  results.push_back({{module.getPortNameAttr(i), module.getPortType(i),
539  dirFtoH(module.getPortDirection(i))},
540  i,
541  module.getPortSymbolAttr(i),
542  {},
543  module.getPortLocation(i)});
544  }
545  return hw::ModulePortInfo(results);
546 }
547 
548 hw::ModulePortInfo FModuleOp::getPortList() { return ::getPortListImpl(*this); }
549 
550 hw::ModulePortInfo FExtModuleOp::getPortList() {
552 }
553 
554 hw::ModulePortInfo FIntModuleOp::getPortList() {
556 }
557 
558 hw::ModulePortInfo FMemModuleOp::getPortList() {
560 }
561 
562 // Return the port with the specified name.
563 BlockArgument FModuleOp::getArgument(size_t portNumber) {
564  return getBodyBlock()->getArgument(portNumber);
565 }
566 
567 /// Inserts the given ports. The insertion indices are expected to be in order.
568 /// Insertion occurs in-order, such that ports with the same insertion index
569 /// appear in the module in the same order they appeared in the list.
570 static void insertPorts(FModuleLike op,
571  ArrayRef<std::pair<unsigned, PortInfo>> ports,
572  bool supportsInternalPaths = false) {
573  if (ports.empty())
574  return;
575  unsigned oldNumArgs = op.getNumPorts();
576  unsigned newNumArgs = oldNumArgs + ports.size();
577 
578  // Add direction markers and names for new ports.
579  SmallVector<Direction> existingDirections =
580  direction::unpackAttribute(op.getPortDirectionsAttr());
581  ArrayRef<Attribute> existingNames = op.getPortNames();
582  ArrayRef<Attribute> existingTypes = op.getPortTypes();
583  ArrayRef<Attribute> existingLocs = op.getPortLocations();
584  assert(existingDirections.size() == oldNumArgs);
585  assert(existingNames.size() == oldNumArgs);
586  assert(existingTypes.size() == oldNumArgs);
587  assert(existingLocs.size() == oldNumArgs);
588  SmallVector<Attribute> internalPaths;
589  auto emptyInternalPath = InternalPathAttr::get(op.getContext());
590  if (supportsInternalPaths) {
591  if (auto internalPathsAttr = op->getAttrOfType<ArrayAttr>("internalPaths"))
592  llvm::append_range(internalPaths, internalPathsAttr);
593  else
594  internalPaths.resize(oldNumArgs, emptyInternalPath);
595  assert(internalPaths.size() == oldNumArgs);
596  }
597 
598  SmallVector<Direction> newDirections;
599  SmallVector<Attribute> newNames, newTypes, newAnnos, newSyms, newLocs,
600  newInternalPaths;
601  newDirections.reserve(newNumArgs);
602  newNames.reserve(newNumArgs);
603  newTypes.reserve(newNumArgs);
604  newAnnos.reserve(newNumArgs);
605  newSyms.reserve(newNumArgs);
606  newLocs.reserve(newNumArgs);
607  newInternalPaths.reserve(newNumArgs);
608 
609  auto emptyArray = ArrayAttr::get(op.getContext(), {});
610 
611  unsigned oldIdx = 0;
612  auto migrateOldPorts = [&](unsigned untilOldIdx) {
613  while (oldIdx < oldNumArgs && oldIdx < untilOldIdx) {
614  newDirections.push_back(existingDirections[oldIdx]);
615  newNames.push_back(existingNames[oldIdx]);
616  newTypes.push_back(existingTypes[oldIdx]);
617  newAnnos.push_back(op.getAnnotationsAttrForPort(oldIdx));
618  newSyms.push_back(op.getPortSymbolAttr(oldIdx));
619  newLocs.push_back(existingLocs[oldIdx]);
620  if (supportsInternalPaths)
621  newInternalPaths.push_back(internalPaths[oldIdx]);
622  ++oldIdx;
623  }
624  };
625  for (auto pair : llvm::enumerate(ports)) {
626  auto idx = pair.value().first;
627  auto &port = pair.value().second;
628  migrateOldPorts(idx);
629  newDirections.push_back(port.direction);
630  newNames.push_back(port.name);
631  newTypes.push_back(TypeAttr::get(port.type));
632  auto annos = port.annotations.getArrayAttr();
633  newAnnos.push_back(annos ? annos : emptyArray);
634  newSyms.push_back(port.sym);
635  newLocs.push_back(port.loc);
636  if (supportsInternalPaths)
637  newInternalPaths.push_back(emptyInternalPath);
638  }
639  migrateOldPorts(oldNumArgs);
640 
641  // The lack of *any* port annotations is represented by an empty
642  // `portAnnotations` array as a shorthand.
643  if (llvm::all_of(newAnnos, [](Attribute attr) {
644  return cast<ArrayAttr>(attr).empty();
645  }))
646  newAnnos.clear();
647 
648  // Apply these changed markers.
649  op->setAttr("portDirections",
650  direction::packAttribute(op.getContext(), newDirections));
651  op->setAttr("portNames", ArrayAttr::get(op.getContext(), newNames));
652  op->setAttr("portTypes", ArrayAttr::get(op.getContext(), newTypes));
653  op->setAttr("portAnnotations", ArrayAttr::get(op.getContext(), newAnnos));
654  op.setPortSymbols(newSyms);
655  op->setAttr("portLocations", ArrayAttr::get(op.getContext(), newLocs));
656  if (supportsInternalPaths) {
657  // Drop if all-empty, otherwise set to new array.
658  auto empty = llvm::all_of(newInternalPaths, [](Attribute attr) {
659  return !cast<InternalPathAttr>(attr).getPath();
660  });
661  if (empty)
662  op->removeAttr("internalPaths");
663  else
664  op->setAttr("internalPaths",
665  ArrayAttr::get(op.getContext(), newInternalPaths));
666  }
667 }
668 
669 /// Erases the ports that have their corresponding bit set in `portIndices`.
670 static void erasePorts(FModuleLike op, const llvm::BitVector &portIndices) {
671  if (portIndices.none())
672  return;
673 
674  // Drop the direction markers for dead ports.
675  SmallVector<Direction> portDirections =
676  direction::unpackAttribute(op.getPortDirectionsAttr());
677  ArrayRef<Attribute> portNames = op.getPortNames();
678  ArrayRef<Attribute> portTypes = op.getPortTypes();
679  ArrayRef<Attribute> portAnnos = op.getPortAnnotations();
680  ArrayRef<Attribute> portSyms = op.getPortSymbols();
681  ArrayRef<Attribute> portLocs = op.getPortLocations();
682  auto numPorts = op.getNumPorts();
683  (void)numPorts;
684  assert(portDirections.size() == numPorts);
685  assert(portNames.size() == numPorts);
686  assert(portAnnos.size() == numPorts || portAnnos.empty());
687  assert(portTypes.size() == numPorts);
688  assert(portSyms.size() == numPorts || portSyms.empty());
689  assert(portLocs.size() == numPorts);
690 
691  SmallVector<Direction> newPortDirections =
692  removeElementsAtIndices<Direction>(portDirections, portIndices);
693  SmallVector<Attribute> newPortNames, newPortTypes, newPortAnnos, newPortSyms,
694  newPortLocs;
695  newPortNames = removeElementsAtIndices(portNames, portIndices);
696  newPortTypes = removeElementsAtIndices(portTypes, portIndices);
697  newPortAnnos = removeElementsAtIndices(portAnnos, portIndices);
698  newPortSyms = removeElementsAtIndices(portSyms, portIndices);
699  newPortLocs = removeElementsAtIndices(portLocs, portIndices);
700  op->setAttr("portDirections",
701  direction::packAttribute(op.getContext(), newPortDirections));
702  op->setAttr("portNames", ArrayAttr::get(op.getContext(), newPortNames));
703  op->setAttr("portAnnotations", ArrayAttr::get(op.getContext(), newPortAnnos));
704  op->setAttr("portTypes", ArrayAttr::get(op.getContext(), newPortTypes));
705  FModuleLike::fixupPortSymsArray(newPortSyms, op.getContext());
706  op->setAttr("portSyms", ArrayAttr::get(op.getContext(), newPortSyms));
707  op->setAttr("portLocations", ArrayAttr::get(op.getContext(), newPortLocs));
708 }
709 
710 template <typename T>
711 static void eraseInternalPaths(T op, const llvm::BitVector &portIndices) {
712  // Fixup internalPaths array.
713  auto internalPaths = op.getInternalPaths();
714  if (!internalPaths)
715  return;
716 
717  auto newPaths =
718  removeElementsAtIndices(internalPaths->getValue(), portIndices);
719 
720  // Drop if all-empty, otherwise set to new array.
721  auto empty = llvm::all_of(newPaths, [](Attribute attr) {
722  return !cast<InternalPathAttr>(attr).getPath();
723  });
724  if (empty)
725  op.removeInternalPathsAttr();
726  else
727  op.setInternalPathsAttr(ArrayAttr::get(op.getContext(), newPaths));
728 }
729 
730 void FExtModuleOp::erasePorts(const llvm::BitVector &portIndices) {
731  ::erasePorts(cast<FModuleLike>((Operation *)*this), portIndices);
732  eraseInternalPaths(*this, portIndices);
733 }
734 
735 void FIntModuleOp::erasePorts(const llvm::BitVector &portIndices) {
736  ::erasePorts(cast<FModuleLike>((Operation *)*this), portIndices);
737  eraseInternalPaths(*this, portIndices);
738 }
739 
740 void FMemModuleOp::erasePorts(const llvm::BitVector &portIndices) {
741  ::erasePorts(cast<FModuleLike>((Operation *)*this), portIndices);
742 }
743 
744 void FModuleOp::erasePorts(const llvm::BitVector &portIndices) {
745  ::erasePorts(cast<FModuleLike>((Operation *)*this), portIndices);
746  getBodyBlock()->eraseArguments(portIndices);
747 }
748 
749 /// Inserts the given ports. The insertion indices are expected to be in order.
750 /// Insertion occurs in-order, such that ports with the same insertion index
751 /// appear in the module in the same order they appeared in the list.
752 void FModuleOp::insertPorts(ArrayRef<std::pair<unsigned, PortInfo>> ports) {
753  ::insertPorts(cast<FModuleLike>((Operation *)*this), ports);
754 
755  // Insert the block arguments.
756  auto *body = getBodyBlock();
757  for (size_t i = 0, e = ports.size(); i < e; ++i) {
758  // Block arguments are inserted one at a time, so for each argument we
759  // insert we have to increase the index by 1.
760  auto &[index, port] = ports[i];
761  body->insertArgument(index + i, port.type, port.loc);
762  }
763 }
764 
765 void FExtModuleOp::insertPorts(ArrayRef<std::pair<unsigned, PortInfo>> ports) {
766  ::insertPorts(cast<FModuleLike>((Operation *)*this), ports,
767  /*supportsInternalPaths=*/true);
768 }
769 
770 void FIntModuleOp::insertPorts(ArrayRef<std::pair<unsigned, PortInfo>> ports) {
771  ::insertPorts(cast<FModuleLike>((Operation *)*this), ports,
772  /*supportsInternalPaths=*/true);
773 }
774 
775 /// Inserts the given ports. The insertion indices are expected to be in order.
776 /// Insertion occurs in-order, such that ports with the same insertion index
777 /// appear in the module in the same order they appeared in the list.
778 void FMemModuleOp::insertPorts(ArrayRef<std::pair<unsigned, PortInfo>> ports) {
779  ::insertPorts(cast<FModuleLike>((Operation *)*this), ports);
780 }
781 
782 static void buildModule(OpBuilder &builder, OperationState &result,
783  StringAttr name, ArrayRef<PortInfo> ports,
784  ArrayAttr annotations, bool withAnnotations = true) {
785  // Add an attribute for the name.
786  result.addAttribute(::mlir::SymbolTable::getSymbolAttrName(), name);
787 
788  // Record the names of the arguments if present.
789  SmallVector<Direction, 4> portDirections;
790  SmallVector<Attribute, 4> portNames;
791  SmallVector<Attribute, 4> portTypes;
792  SmallVector<Attribute, 4> portAnnotations;
793  SmallVector<Attribute, 4> portSyms;
794  SmallVector<Attribute, 4> portLocs;
795  for (const auto &port : ports) {
796  portDirections.push_back(port.direction);
797  portNames.push_back(port.name);
798  portTypes.push_back(TypeAttr::get(port.type));
799  portAnnotations.push_back(port.annotations.getArrayAttr());
800  portSyms.push_back(port.sym);
801  portLocs.push_back(port.loc);
802  }
803 
804  // The lack of *any* port annotations is represented by an empty
805  // `portAnnotations` array as a shorthand.
806  if (llvm::all_of(portAnnotations, [](Attribute attr) {
807  return cast<ArrayAttr>(attr).empty();
808  }))
809  portAnnotations.clear();
810 
811  FModuleLike::fixupPortSymsArray(portSyms, builder.getContext());
812  // Both attributes are added, even if the module has no ports.
813  result.addAttribute(
814  "portDirections",
815  direction::packAttribute(builder.getContext(), portDirections));
816  result.addAttribute("portNames", builder.getArrayAttr(portNames));
817  result.addAttribute("portTypes", builder.getArrayAttr(portTypes));
818  result.addAttribute("portSyms", builder.getArrayAttr(portSyms));
819  result.addAttribute("portLocations", builder.getArrayAttr(portLocs));
820 
821  if (withAnnotations) {
822  if (!annotations)
823  annotations = builder.getArrayAttr({});
824  result.addAttribute("annotations", annotations);
825  result.addAttribute("portAnnotations",
826  builder.getArrayAttr(portAnnotations));
827  }
828 
829  result.addRegion();
830 }
831 
832 static void buildModuleWithoutAnnos(OpBuilder &builder, OperationState &result,
833  StringAttr name, ArrayRef<PortInfo> ports) {
834  return buildModule(builder, result, name, ports, {},
835  /*withAnnotations=*/false);
836 }
837 
838 void FModuleOp::build(OpBuilder &builder, OperationState &result,
839  StringAttr name, ConventionAttr convention,
840  ArrayRef<PortInfo> ports, ArrayAttr annotations) {
841  buildModule(builder, result, name, ports, annotations);
842  result.addAttribute("convention", convention);
843 
844  // Create a region and a block for the body.
845  auto *bodyRegion = result.regions[0].get();
846  Block *body = new Block();
847  bodyRegion->push_back(body);
848 
849  // Add arguments to the body block.
850  for (auto &elt : ports)
851  body->addArgument(elt.type, elt.loc);
852 }
853 
854 void FExtModuleOp::build(OpBuilder &builder, OperationState &result,
855  StringAttr name, ConventionAttr convention,
856  ArrayRef<PortInfo> ports, StringRef defnameAttr,
857  ArrayAttr annotations, ArrayAttr parameters,
858  ArrayAttr internalPaths) {
859  buildModule(builder, result, name, ports, annotations);
860  result.addAttribute("convention", convention);
861  if (!defnameAttr.empty())
862  result.addAttribute("defname", builder.getStringAttr(defnameAttr));
863  if (!parameters)
864  parameters = builder.getArrayAttr({});
865  result.addAttribute(getParametersAttrName(result.name), parameters);
866  if (internalPaths && !internalPaths.empty())
867  result.addAttribute(getInternalPathsAttrName(result.name), internalPaths);
868 }
869 
870 void FIntModuleOp::build(OpBuilder &builder, OperationState &result,
871  StringAttr name, ArrayRef<PortInfo> ports,
872  StringRef intrinsicNameAttr, ArrayAttr annotations,
873  ArrayAttr parameters, ArrayAttr internalPaths) {
874  buildModule(builder, result, name, ports, annotations);
875  result.addAttribute("intrinsic", builder.getStringAttr(intrinsicNameAttr));
876  if (!parameters)
877  parameters = builder.getArrayAttr({});
878  result.addAttribute(getParametersAttrName(result.name), parameters);
879  if (internalPaths && !internalPaths.empty())
880  result.addAttribute(getInternalPathsAttrName(result.name), internalPaths);
881 }
882 
883 void FMemModuleOp::build(OpBuilder &builder, OperationState &result,
884  StringAttr name, ArrayRef<PortInfo> ports,
885  uint32_t numReadPorts, uint32_t numWritePorts,
886  uint32_t numReadWritePorts, uint32_t dataWidth,
887  uint32_t maskBits, uint32_t readLatency,
888  uint32_t writeLatency, uint64_t depth,
889  ArrayAttr annotations) {
890  auto *context = builder.getContext();
891  buildModule(builder, result, name, ports, annotations);
892  auto ui32Type = IntegerType::get(context, 32, IntegerType::Unsigned);
893  auto ui64Type = IntegerType::get(context, 64, IntegerType::Unsigned);
894  result.addAttribute("numReadPorts", IntegerAttr::get(ui32Type, numReadPorts));
895  result.addAttribute("numWritePorts",
896  IntegerAttr::get(ui32Type, numWritePorts));
897  result.addAttribute("numReadWritePorts",
898  IntegerAttr::get(ui32Type, numReadWritePorts));
899  result.addAttribute("dataWidth", IntegerAttr::get(ui32Type, dataWidth));
900  result.addAttribute("maskBits", IntegerAttr::get(ui32Type, maskBits));
901  result.addAttribute("readLatency", IntegerAttr::get(ui32Type, readLatency));
902  result.addAttribute("writeLatency", IntegerAttr::get(ui32Type, writeLatency));
903  result.addAttribute("depth", IntegerAttr::get(ui64Type, depth));
904  result.addAttribute("extraPorts", ArrayAttr::get(context, {}));
905 }
906 
907 /// Print a list of module ports in the following form:
908 /// in x: !firrtl.uint<1> [{class = "DontTouch}], out "_port": !firrtl.uint<2>
909 ///
910 /// When there is no block specified, the port names print as MLIR identifiers,
911 /// wrapping in quotes if not legal to print as-is. When there is no block
912 /// specified, this function always return false, indicating that there was no
913 /// issue printing port names.
914 ///
915 /// If there is a block specified, then port names will be printed as SSA
916 /// values. If there is a reason the printed SSA values can't match the true
917 /// port name, then this function will return true. When this happens, the
918 /// caller should print the port names as a part of the `attr-dict`.
919 static bool printModulePorts(OpAsmPrinter &p, Block *block,
920  ArrayRef<Direction> portDirections,
921  ArrayRef<Attribute> portNames,
922  ArrayRef<Attribute> portTypes,
923  ArrayRef<Attribute> portAnnotations,
924  ArrayRef<Attribute> portSyms,
925  ArrayRef<Attribute> portLocs) {
926  // When printing port names as SSA values, we can fail to print them
927  // identically.
928  bool printedNamesDontMatch = false;
929 
930  mlir::OpPrintingFlags flags;
931 
932  // If we are printing the ports as block arguments the op must have a first
933  // block.
934  SmallString<32> resultNameStr;
935  p << '(';
936  for (unsigned i = 0, e = portTypes.size(); i < e; ++i) {
937  if (i > 0)
938  p << ", ";
939 
940  // Print the port direction.
941  p << portDirections[i] << " ";
942 
943  // Print the port name. If there is a valid block, we print it as a block
944  // argument.
945  if (block) {
946  // Get the printed format for the argument name.
947  resultNameStr.clear();
948  llvm::raw_svector_ostream tmpStream(resultNameStr);
949  p.printOperand(block->getArgument(i), tmpStream);
950  // If the name wasn't printable in a way that agreed with portName, make
951  // sure to print out an explicit portNames attribute.
952  auto portName = cast<StringAttr>(portNames[i]).getValue();
953  if (!portName.empty() && tmpStream.str().drop_front() != portName)
954  printedNamesDontMatch = true;
955  p << tmpStream.str();
956  } else {
957  p.printKeywordOrString(cast<StringAttr>(portNames[i]).getValue());
958  }
959 
960  // Print the port type.
961  p << ": ";
962  auto portType = cast<TypeAttr>(portTypes[i]).getValue();
963  p.printType(portType);
964 
965  // Print the optional port symbol.
966  if (!portSyms.empty()) {
967  if (!portSyms[i].cast<hw::InnerSymAttr>().empty()) {
968  p << " sym ";
969  portSyms[i].cast<hw::InnerSymAttr>().print(p);
970  }
971  }
972 
973  // Print the port specific annotations. The port annotations array will be
974  // empty if there are none.
975  if (!portAnnotations.empty() &&
976  !cast<ArrayAttr>(portAnnotations[i]).empty()) {
977  p << " ";
978  p.printAttribute(portAnnotations[i]);
979  }
980 
981  // Print the port location.
982  // TODO: `printOptionalLocationSpecifier` will emit aliases for locations,
983  // even if they are not printed. This will have to be fixed upstream. For
984  // now, use what was specified on the command line.
985  if (flags.shouldPrintDebugInfo() && !portLocs.empty())
986  p.printOptionalLocationSpecifier(cast<LocationAttr>(portLocs[i]));
987  }
988 
989  p << ')';
990  return printedNamesDontMatch;
991 }
992 
993 /// Parse a list of module ports. If port names are SSA identifiers, then this
994 /// will populate `entryArgs`.
995 static ParseResult
996 parseModulePorts(OpAsmParser &parser, bool hasSSAIdentifiers,
997  bool supportsSymbols,
998  SmallVectorImpl<OpAsmParser::Argument> &entryArgs,
999  SmallVectorImpl<Direction> &portDirections,
1000  SmallVectorImpl<Attribute> &portNames,
1001  SmallVectorImpl<Attribute> &portTypes,
1002  SmallVectorImpl<Attribute> &portAnnotations,
1003  SmallVectorImpl<Attribute> &portSyms,
1004  SmallVectorImpl<Attribute> &portLocs) {
1005  auto *context = parser.getContext();
1006 
1007  auto parseArgument = [&]() -> ParseResult {
1008  // Parse port direction.
1009  if (succeeded(parser.parseOptionalKeyword("out")))
1010  portDirections.push_back(Direction::Out);
1011  else if (succeeded(parser.parseKeyword("in", "or 'out'")))
1012  portDirections.push_back(Direction::In);
1013  else
1014  return failure();
1015 
1016  // This is the location or the port declaration in the IR. If there is no
1017  // other location information, we use this to point to the MLIR.
1018  llvm::SMLoc irLoc;
1019 
1020  if (hasSSAIdentifiers) {
1021  OpAsmParser::Argument arg;
1022  if (parser.parseArgument(arg))
1023  return failure();
1024  entryArgs.push_back(arg);
1025 
1026  // The name of an argument is of the form "%42" or "%id", and since
1027  // parsing succeeded, we know it always has one character.
1028  assert(arg.ssaName.name.size() > 1 && arg.ssaName.name[0] == '%' &&
1029  "Unknown MLIR name");
1030  if (isdigit(arg.ssaName.name[1]))
1031  portNames.push_back(StringAttr::get(context, ""));
1032  else
1033  portNames.push_back(
1034  StringAttr::get(context, arg.ssaName.name.drop_front()));
1035 
1036  // Store the location of the SSA name.
1037  irLoc = arg.ssaName.location;
1038 
1039  } else {
1040  // Parse the port name.
1041  irLoc = parser.getCurrentLocation();
1042  std::string portName;
1043  if (parser.parseKeywordOrString(&portName))
1044  return failure();
1045  portNames.push_back(StringAttr::get(context, portName));
1046  }
1047 
1048  // Parse the port type.
1049  Type portType;
1050  if (parser.parseColonType(portType))
1051  return failure();
1052  portTypes.push_back(TypeAttr::get(portType));
1053 
1054  if (hasSSAIdentifiers)
1055  entryArgs.back().type = portType;
1056 
1057  // Parse the optional port symbol.
1058  if (supportsSymbols) {
1059  hw::InnerSymAttr innerSymAttr;
1060  if (succeeded(parser.parseOptionalKeyword("sym"))) {
1061  NamedAttrList dummyAttrs;
1062  if (parser.parseCustomAttributeWithFallback(
1063  innerSymAttr, ::mlir::Type{},
1065  return ::mlir::failure();
1066  }
1067  }
1068  portSyms.push_back(innerSymAttr);
1069  }
1070 
1071  // Parse the port annotations.
1072  ArrayAttr annos;
1073  auto parseResult = parser.parseOptionalAttribute(annos);
1074  if (!parseResult.has_value())
1075  annos = parser.getBuilder().getArrayAttr({});
1076  else if (failed(*parseResult))
1077  return failure();
1078  portAnnotations.push_back(annos);
1079 
1080  // Parse the optional port location.
1081  std::optional<Location> maybeLoc;
1082  if (failed(parser.parseOptionalLocationSpecifier(maybeLoc)))
1083  return failure();
1084  Location loc = maybeLoc ? *maybeLoc : parser.getEncodedSourceLoc(irLoc);
1085  portLocs.push_back(loc);
1086  if (hasSSAIdentifiers)
1087  entryArgs.back().sourceLoc = loc;
1088 
1089  return success();
1090  };
1091 
1092  // Parse all ports.
1093  return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
1094  parseArgument);
1095 }
1096 
1097 /// Print a paramter list for a module or instance.
1098 static void printParameterList(ArrayAttr parameters, OpAsmPrinter &p) {
1099  if (!parameters || parameters.empty())
1100  return;
1101 
1102  p << '<';
1103  llvm::interleaveComma(parameters, p, [&](Attribute param) {
1104  auto paramAttr = cast<ParamDeclAttr>(param);
1105  p << paramAttr.getName().getValue() << ": " << paramAttr.getType();
1106  if (auto value = paramAttr.getValue()) {
1107  p << " = ";
1108  p.printAttributeWithoutType(value);
1109  }
1110  });
1111  p << '>';
1112 }
1113 
1114 static void printFModuleLikeOp(OpAsmPrinter &p, FModuleLike op) {
1115  p << " ";
1116 
1117  // Print the visibility of the module.
1118  StringRef visibilityAttrName = SymbolTable::getVisibilityAttrName();
1119  if (auto visibility = op->getAttrOfType<StringAttr>(visibilityAttrName))
1120  p << visibility.getValue() << ' ';
1121 
1122  // Print the operation and the function name.
1123  p.printSymbolName(op.getModuleName());
1124 
1125  // Print the parameter list (if non-empty).
1126  printParameterList(op->getAttrOfType<ArrayAttr>("parameters"), p);
1127 
1128  // Both modules and external modules have a body, but it is always empty for
1129  // external modules.
1130  Block *body = nullptr;
1131  if (!op->getRegion(0).empty())
1132  body = &op->getRegion(0).front();
1133 
1134  auto portDirections = direction::unpackAttribute(op.getPortDirectionsAttr());
1135 
1136  auto needPortNamesAttr = printModulePorts(
1137  p, body, portDirections, op.getPortNames(), op.getPortTypes(),
1138  op.getPortAnnotations(), op.getPortSymbols(), op.getPortLocations());
1139 
1140  SmallVector<StringRef, 12> omittedAttrs = {
1141  "sym_name", "portDirections", "portTypes", "portAnnotations",
1142  "portSyms", "portLocations", "parameters", visibilityAttrName};
1143 
1144  if (op.getConvention() == Convention::Internal)
1145  omittedAttrs.push_back("convention");
1146 
1147  // We can omit the portNames if they were able to be printed as properly as
1148  // block arguments.
1149  if (!needPortNamesAttr)
1150  omittedAttrs.push_back("portNames");
1151 
1152  // If there are no annotations we can omit the empty array.
1153  if (op->getAttrOfType<ArrayAttr>("annotations").empty())
1154  omittedAttrs.push_back("annotations");
1155 
1156  p.printOptionalAttrDictWithKeyword(op->getAttrs(), omittedAttrs);
1157 }
1158 
1159 void FExtModuleOp::print(OpAsmPrinter &p) { printFModuleLikeOp(p, *this); }
1160 
1161 void FIntModuleOp::print(OpAsmPrinter &p) { printFModuleLikeOp(p, *this); }
1162 
1163 void FMemModuleOp::print(OpAsmPrinter &p) { printFModuleLikeOp(p, *this); }
1164 
1165 void FModuleOp::print(OpAsmPrinter &p) {
1166  printFModuleLikeOp(p, *this);
1167 
1168  // Print the body if this is not an external function. Since this block does
1169  // not have terminators, printing the terminator actually just prints the last
1170  // operation.
1171  Region &fbody = getBody();
1172  if (!fbody.empty()) {
1173  p << " ";
1174  p.printRegion(fbody, /*printEntryBlockArgs=*/false,
1175  /*printBlockTerminators=*/true);
1176  }
1177 }
1178 
1179 /// Parse an parameter list if present.
1180 /// module-parameter-list ::= `<` parameter-decl (`,` parameter-decl)* `>`
1181 /// parameter-decl ::= identifier `:` type
1182 /// parameter-decl ::= identifier `:` type `=` attribute
1183 ///
1184 static ParseResult
1185 parseOptionalParameters(OpAsmParser &parser,
1186  SmallVectorImpl<Attribute> &parameters) {
1187 
1188  return parser.parseCommaSeparatedList(
1189  OpAsmParser::Delimiter::OptionalLessGreater, [&]() {
1190  std::string name;
1191  Type type;
1192  Attribute value;
1193 
1194  if (parser.parseKeywordOrString(&name) || parser.parseColonType(type))
1195  return failure();
1196 
1197  // Parse the default value if present.
1198  if (succeeded(parser.parseOptionalEqual())) {
1199  if (parser.parseAttribute(value, type))
1200  return failure();
1201  }
1202 
1203  auto &builder = parser.getBuilder();
1204  parameters.push_back(ParamDeclAttr::get(
1205  builder.getContext(), builder.getStringAttr(name), type, value));
1206  return success();
1207  });
1208 }
1209 
1210 static ParseResult parseFModuleLikeOp(OpAsmParser &parser,
1211  OperationState &result,
1212  bool hasSSAIdentifiers) {
1213  auto *context = result.getContext();
1214  auto &builder = parser.getBuilder();
1215 
1216  // Parse the visibility attribute.
1217  (void)mlir::impl::parseOptionalVisibilityKeyword(parser, result.attributes);
1218 
1219  // Parse the name as a symbol.
1220  StringAttr nameAttr;
1221  if (parser.parseSymbolName(nameAttr, ::mlir::SymbolTable::getSymbolAttrName(),
1222  result.attributes))
1223  return failure();
1224 
1225  // Parse optional parameters.
1226  SmallVector<Attribute, 4> parameters;
1227  if (parseOptionalParameters(parser, parameters))
1228  return failure();
1229  result.addAttribute("parameters", builder.getArrayAttr(parameters));
1230 
1231  // Parse the module ports.
1232  SmallVector<OpAsmParser::Argument> entryArgs;
1233  SmallVector<Direction, 4> portDirections;
1234  SmallVector<Attribute, 4> portNames;
1235  SmallVector<Attribute, 4> portTypes;
1236  SmallVector<Attribute, 4> portAnnotations;
1237  SmallVector<Attribute, 4> portSyms;
1238  SmallVector<Attribute, 4> portLocs;
1239  if (parseModulePorts(parser, hasSSAIdentifiers, /*supportsSymbols=*/true,
1240  entryArgs, portDirections, portNames, portTypes,
1241  portAnnotations, portSyms, portLocs))
1242  return failure();
1243 
1244  // If module attributes are present, parse them.
1245  if (parser.parseOptionalAttrDictWithKeyword(result.attributes))
1246  return failure();
1247 
1248  assert(portNames.size() == portTypes.size());
1249 
1250  // Record the argument and result types as an attribute. This is necessary
1251  // for external modules.
1252 
1253  // Add port directions.
1254  if (!result.attributes.get("portDirections"))
1255  result.addAttribute("portDirections",
1256  direction::packAttribute(context, portDirections));
1257 
1258  // Add port names.
1259  if (!result.attributes.get("portNames"))
1260  result.addAttribute("portNames", builder.getArrayAttr(portNames));
1261 
1262  // Add the port types.
1263  if (!result.attributes.get("portTypes"))
1264  result.addAttribute("portTypes", ArrayAttr::get(context, portTypes));
1265 
1266  // Add the port annotations.
1267  if (!result.attributes.get("portAnnotations")) {
1268  // If there are no portAnnotations, don't add the attribute.
1269  if (llvm::any_of(portAnnotations, [&](Attribute anno) {
1270  return !cast<ArrayAttr>(anno).empty();
1271  }))
1272  result.addAttribute("portAnnotations",
1273  ArrayAttr::get(context, portAnnotations));
1274  }
1275 
1276  // Add port symbols.
1277  if (!result.attributes.get("portSyms")) {
1278  FModuleLike::fixupPortSymsArray(portSyms, builder.getContext());
1279  result.addAttribute("portSyms", builder.getArrayAttr(portSyms));
1280  }
1281 
1282  // Add port locations.
1283  if (!result.attributes.get("portLocations"))
1284  result.addAttribute("portLocations", ArrayAttr::get(context, portLocs));
1285 
1286  // The annotations attribute is always present, but not printed when empty.
1287  if (!result.attributes.get("annotations"))
1288  result.addAttribute("annotations", builder.getArrayAttr({}));
1289 
1290  // The portAnnotations attribute is always present, but not printed when
1291  // empty.
1292  if (!result.attributes.get("portAnnotations"))
1293  result.addAttribute("portAnnotations", builder.getArrayAttr({}));
1294 
1295  // Parse the optional function body.
1296  auto *body = result.addRegion();
1297 
1298  if (hasSSAIdentifiers) {
1299  if (parser.parseRegion(*body, entryArgs))
1300  return failure();
1301  if (body->empty())
1302  body->push_back(new Block());
1303  }
1304  return success();
1305 }
1306 
1307 ParseResult FModuleOp::parse(OpAsmParser &parser, OperationState &result) {
1308  if (parseFModuleLikeOp(parser, result, /*hasSSAIdentifiers=*/true))
1309  return failure();
1310  if (!result.attributes.get("convention"))
1311  result.addAttribute(
1312  "convention",
1313  ConventionAttr::get(result.getContext(), Convention::Internal));
1314  return success();
1315 }
1316 
1317 ParseResult FExtModuleOp::parse(OpAsmParser &parser, OperationState &result) {
1318  if (parseFModuleLikeOp(parser, result, /*hasSSAIdentifiers=*/false))
1319  return failure();
1320  if (!result.attributes.get("convention"))
1321  result.addAttribute(
1322  "convention",
1323  ConventionAttr::get(result.getContext(), Convention::Internal));
1324  return success();
1325 }
1326 
1327 ParseResult FIntModuleOp::parse(OpAsmParser &parser, OperationState &result) {
1328  return parseFModuleLikeOp(parser, result, /*hasSSAIdentifiers=*/false);
1329 }
1330 
1331 ParseResult FMemModuleOp::parse(OpAsmParser &parser, OperationState &result) {
1332  return parseFModuleLikeOp(parser, result, /*hasSSAIdentifiers=*/false);
1333 }
1334 
1335 LogicalResult FModuleOp::verify() {
1336  // Verify the block arguments.
1337  auto *body = getBodyBlock();
1338  auto portTypes = getPortTypes();
1339  auto portLocs = getPortLocations();
1340  auto numPorts = portTypes.size();
1341 
1342  // Verify that we have the correct number of block arguments.
1343  if (body->getNumArguments() != numPorts)
1344  return emitOpError("entry block must have ")
1345  << numPorts << " arguments to match module signature";
1346 
1347  // Verify the block arguments' types and locations match our attributes.
1348  for (auto [arg, type, loc] : zip(body->getArguments(), portTypes, portLocs)) {
1349  if (arg.getType() != cast<TypeAttr>(type).getValue())
1350  return emitOpError("block argument types should match signature types");
1351  if (arg.getLoc() != cast<LocationAttr>(loc))
1352  return emitOpError(
1353  "block argument locations should match signature locations");
1354  }
1355 
1356  return success();
1357 }
1358 
1359 static LogicalResult
1360 verifyInternalPaths(FModuleLike op,
1361  std::optional<::mlir::ArrayAttr> internalPaths) {
1362  if (!internalPaths)
1363  return success();
1364 
1365  // If internal paths are present, should cover all ports.
1366  if (internalPaths->size() != op.getNumPorts())
1367  return op.emitError("module has inconsistent internal path array with ")
1368  << internalPaths->size() << " entries for " << op.getNumPorts()
1369  << " ports";
1370 
1371  // No internal paths for non-ref-type ports.
1372  for (auto [idx, path, typeattr] : llvm::enumerate(
1373  internalPaths->getAsRange<InternalPathAttr>(), op.getPortTypes())) {
1374  if (path.getPath() &&
1375  !type_isa<RefType>(cast<TypeAttr>(typeattr).getValue())) {
1376  auto diag =
1377  op.emitError("module has internal path for non-ref-type port ")
1378  << op.getPortNameAttr(idx);
1379  return diag.attachNote(op.getPortLocation(idx)) << "this port";
1380  }
1381  }
1382 
1383  return success();
1384 }
1385 
1386 LogicalResult FExtModuleOp::verify() {
1387  if (failed(verifyInternalPaths(*this, getInternalPaths())))
1388  return failure();
1389 
1390  auto params = getParameters();
1391  if (params.empty())
1392  return success();
1393 
1394  auto checkParmValue = [&](Attribute elt) -> bool {
1395  auto param = cast<ParamDeclAttr>(elt);
1396  auto value = param.getValue();
1397  if (isa<IntegerAttr, StringAttr, FloatAttr, hw::ParamVerbatimAttr>(value))
1398  return true;
1399  emitError() << "has unknown extmodule parameter value '"
1400  << param.getName().getValue() << "' = " << value;
1401  return false;
1402  };
1403 
1404  if (!llvm::all_of(params, checkParmValue))
1405  return failure();
1406 
1407  return success();
1408 }
1409 
1410 LogicalResult FIntModuleOp::verify() {
1411  if (failed(verifyInternalPaths(*this, getInternalPaths())))
1412  return failure();
1413 
1414  auto params = getParameters();
1415  if (params.empty())
1416  return success();
1417 
1418  auto checkParmValue = [&](Attribute elt) -> bool {
1419  auto param = cast<ParamDeclAttr>(elt);
1420  auto value = param.getValue();
1421  if (isa<IntegerAttr, StringAttr, FloatAttr>(value))
1422  return true;
1423  emitError() << "has unknown intmodule parameter value '"
1424  << param.getName().getValue() << "' = " << value;
1425  return false;
1426  };
1427 
1428  if (!llvm::all_of(params, checkParmValue))
1429  return failure();
1430 
1431  return success();
1432 }
1433 
1434 static LogicalResult verifyPortSymbolUses(FModuleLike module,
1435  SymbolTableCollection &symbolTable) {
1436  auto circuitOp = module->getParentOfType<CircuitOp>();
1437 
1438  // verify types in ports.
1439  for (size_t i = 0, e = module.getNumPorts(); i < e; ++i) {
1440  auto type = module.getPortType(i);
1441  auto classType = dyn_cast<ClassType>(type);
1442  if (!classType)
1443  continue;
1444 
1445  // verify that the class exists.
1446  auto className = classType.getNameAttr();
1447  auto classOp = dyn_cast_or_null<ClassLike>(
1448  symbolTable.lookupSymbolIn(circuitOp, className));
1449  if (!classOp)
1450  return module.emitOpError() << "references unknown class " << className;
1451 
1452  // verify that the result type agrees with the class definition.
1453  if (failed(classOp.verifyType(classType,
1454  [&]() { return module.emitOpError(); })))
1455  return failure();
1456  }
1457 
1458  return success();
1459 }
1460 
1461 LogicalResult FModuleOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1462  return verifyPortSymbolUses(cast<FModuleLike>(getOperation()), symbolTable);
1463 }
1464 
1465 LogicalResult
1466 FExtModuleOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1467  return verifyPortSymbolUses(cast<FModuleLike>(getOperation()), symbolTable);
1468 }
1469 
1470 LogicalResult
1471 FIntModuleOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1472  return verifyPortSymbolUses(cast<FModuleLike>(getOperation()), symbolTable);
1473 }
1474 
1475 LogicalResult
1476 FMemModuleOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1477  return verifyPortSymbolUses(cast<FModuleLike>(getOperation()), symbolTable);
1478 }
1479 
1480 void FModuleOp::getAsmBlockArgumentNames(mlir::Region &region,
1481  mlir::OpAsmSetValueNameFn setNameFn) {
1482  getAsmBlockArgumentNamesImpl(getOperation(), region, setNameFn);
1483 }
1484 
1485 void FExtModuleOp::getAsmBlockArgumentNames(
1486  mlir::Region &region, mlir::OpAsmSetValueNameFn setNameFn) {
1487  getAsmBlockArgumentNamesImpl(getOperation(), region, setNameFn);
1488 }
1489 
1490 void FIntModuleOp::getAsmBlockArgumentNames(
1491  mlir::Region &region, mlir::OpAsmSetValueNameFn setNameFn) {
1492  getAsmBlockArgumentNamesImpl(getOperation(), region, setNameFn);
1493 }
1494 
1495 void FMemModuleOp::getAsmBlockArgumentNames(
1496  mlir::Region &region, mlir::OpAsmSetValueNameFn setNameFn) {
1497  getAsmBlockArgumentNamesImpl(getOperation(), region, setNameFn);
1498 }
1499 
1500 ArrayAttr FMemModuleOp::getParameters() { return {}; }
1501 
1502 ArrayAttr FModuleOp::getParameters() { return {}; }
1503 
1504 Convention FIntModuleOp::getConvention() { return Convention::Internal; }
1505 
1506 ConventionAttr FIntModuleOp::getConventionAttr() {
1507  return ConventionAttr::get(getContext(), getConvention());
1508 }
1509 
1510 Convention FMemModuleOp::getConvention() { return Convention::Internal; }
1511 
1512 ConventionAttr FMemModuleOp::getConventionAttr() {
1513  return ConventionAttr::get(getContext(), getConvention());
1514 }
1515 
1516 //===----------------------------------------------------------------------===//
1517 // ClassLike Helpers
1518 //===----------------------------------------------------------------------===//
1519 
1521  ClassLike classOp, ClassType type,
1522  function_ref<InFlightDiagnostic()> emitError) {
1523  // This check is probably not required, but done for sanity.
1524  auto name = type.getNameAttr().getAttr();
1525  auto expectedName = classOp.getModuleNameAttr();
1526  if (name != expectedName)
1527  return emitError() << "type has wrong name, got " << name << ", expected "
1528  << expectedName;
1529 
1530  auto elements = type.getElements();
1531  auto numElements = elements.size();
1532  auto expectedNumElements = classOp.getNumPorts();
1533  if (numElements != expectedNumElements)
1534  return emitError() << "has wrong number of ports, got " << numElements
1535  << ", expected " << expectedNumElements;
1536 
1537  auto portNames = classOp.getPortNames();
1538  auto portDirections = classOp.getPortDirections();
1539  auto portTypes = classOp.getPortTypes();
1540 
1541  for (unsigned i = 0; i < numElements; ++i) {
1542  auto element = elements[i];
1543 
1544  auto name = element.name;
1545  auto expectedName = portNames[i];
1546  if (name != expectedName)
1547  return emitError() << "port #" << i << " has wrong name, got " << name
1548  << ", expected " << expectedName;
1549 
1550  auto direction = element.direction;
1551  auto expectedDirection = Direction(portDirections[i]);
1552  if (direction != expectedDirection)
1553  return emitError() << "port " << name << " has wrong direction, got "
1554  << direction::toString(direction) << ", expected "
1555  << direction::toString(expectedDirection);
1556 
1557  auto type = element.type;
1558  auto expectedType = cast<TypeAttr>(portTypes[i]).getValue();
1559  if (type != expectedType)
1560  return emitError() << "port " << name << " has wrong type, got " << type
1561  << ", expected " << expectedType;
1562  }
1563 
1564  return success();
1565 }
1566 
1567 ClassType firrtl::detail::getInstanceTypeForClassLike(ClassLike classOp) {
1568  auto n = classOp.getNumPorts();
1569  SmallVector<ClassElement> elements;
1570  elements.reserve(n);
1571  for (size_t i = 0; i < n; ++i)
1572  elements.push_back({classOp.getPortNameAttr(i), classOp.getPortType(i),
1573  classOp.getPortDirection(i)});
1574  auto name = FlatSymbolRefAttr::get(classOp.getNameAttr());
1575  return ClassType::get(name, elements);
1576 }
1577 
1578 static ParseResult parseClassLike(OpAsmParser &parser, OperationState &result,
1579  bool hasSSAIdentifiers) {
1580  auto *context = result.getContext();
1581  auto &builder = parser.getBuilder();
1582 
1583  // Parse the visibility attribute.
1584  (void)mlir::impl::parseOptionalVisibilityKeyword(parser, result.attributes);
1585 
1586  // Parse the name as a symbol.
1587  StringAttr nameAttr;
1588  if (parser.parseSymbolName(nameAttr, ::mlir::SymbolTable::getSymbolAttrName(),
1589  result.attributes))
1590  return failure();
1591 
1592  // Parse the module ports.
1593  SmallVector<OpAsmParser::Argument> entryArgs;
1594  SmallVector<Direction, 4> portDirections;
1595  SmallVector<Attribute, 4> portNames;
1596  SmallVector<Attribute, 4> portTypes;
1597  SmallVector<Attribute, 4> portAnnotations;
1598  SmallVector<Attribute, 4> portSyms;
1599  SmallVector<Attribute, 4> portLocs;
1600  if (parseModulePorts(parser, hasSSAIdentifiers,
1601  /*supportsSymbols=*/false, entryArgs, portDirections,
1602  portNames, portTypes, portAnnotations, portSyms,
1603  portLocs))
1604  return failure();
1605 
1606  // Ports on ClassLike ops cannot have annotations
1607  for (auto annos : portAnnotations)
1608  if (!cast<ArrayAttr>(annos).empty())
1609  return failure();
1610 
1611  // If attributes are present, parse them.
1612  if (parser.parseOptionalAttrDictWithKeyword(result.attributes))
1613  return failure();
1614 
1615  assert(portNames.size() == portTypes.size());
1616 
1617  // Record the argument and result types as an attribute. This is necessary
1618  // for external modules.
1619 
1620  // Add port directions.
1621  if (!result.attributes.get("portDirections"))
1622  result.addAttribute("portDirections",
1623  direction::packAttribute(context, portDirections));
1624 
1625  // Add port names.
1626  if (!result.attributes.get("portNames"))
1627  result.addAttribute("portNames", builder.getArrayAttr(portNames));
1628 
1629  // Add the port types.
1630  if (!result.attributes.get("portTypes"))
1631  result.addAttribute("portTypes", builder.getArrayAttr(portTypes));
1632 
1633  // Add the port symbols.
1634  if (!result.attributes.get("portSyms")) {
1635  FModuleLike::fixupPortSymsArray(portSyms, builder.getContext());
1636  result.addAttribute("portSyms", builder.getArrayAttr(portSyms));
1637  }
1638 
1639  // Add port locations.
1640  if (!result.attributes.get("portLocations"))
1641  result.addAttribute("portLocations", ArrayAttr::get(context, portLocs));
1642 
1643  // Notably missing compared to other FModuleLike, we do not track port
1644  // annotations, nor port symbols, on classes.
1645 
1646  // Add the region (unused by extclass).
1647  auto *bodyRegion = result.addRegion();
1648 
1649  if (hasSSAIdentifiers) {
1650  if (parser.parseRegion(*bodyRegion, entryArgs))
1651  return failure();
1652  if (bodyRegion->empty())
1653  bodyRegion->push_back(new Block());
1654  }
1655 
1656  return success();
1657 }
1658 
1659 static void printClassLike(OpAsmPrinter &p, ClassLike op) {
1660  p << ' ';
1661 
1662  // Print the visibility of the class.
1663  StringRef visibilityAttrName = SymbolTable::getVisibilityAttrName();
1664  if (auto visibility = op->getAttrOfType<StringAttr>(visibilityAttrName))
1665  p << visibility.getValue() << ' ';
1666 
1667  // Print the class name.
1668  p.printSymbolName(op.getName());
1669 
1670  // Both classes and external classes have a body, but it is always empty for
1671  // external classes.
1672  Region &region = op->getRegion(0);
1673  Block *body = nullptr;
1674  if (!region.empty())
1675  body = &region.front();
1676 
1677  auto portDirections = direction::unpackAttribute(op.getPortDirectionsAttr());
1678 
1679  auto needPortNamesAttr = printModulePorts(
1680  p, body, portDirections, op.getPortNames(), op.getPortTypes(), {},
1681  op.getPortSymbols(), op.getPortLocations());
1682 
1683  // Print the attr-dict.
1684  SmallVector<StringRef, 8> omittedAttrs = {
1685  "sym_name", "portNames", "portTypes", "portDirections",
1686  "portSyms", "portLocations", visibilityAttrName};
1687 
1688  // We can omit the portNames if they were able to be printed as properly as
1689  // block arguments.
1690  if (!needPortNamesAttr)
1691  omittedAttrs.push_back("portNames");
1692 
1693  p.printOptionalAttrDictWithKeyword(op->getAttrs(), omittedAttrs);
1694 
1695  // print the body if it exists.
1696  if (!region.empty()) {
1697  p << " ";
1698  auto printEntryBlockArgs = false;
1699  auto printBlockTerminators = false;
1700  p.printRegion(region, printEntryBlockArgs, printBlockTerminators);
1701  }
1702 }
1703 
1704 //===----------------------------------------------------------------------===//
1705 // ClassOp
1706 //===----------------------------------------------------------------------===//
1707 
1708 void ClassOp::build(OpBuilder &builder, OperationState &result, StringAttr name,
1709  ArrayRef<PortInfo> ports) {
1710  assert(
1711  llvm::all_of(ports,
1712  [](const auto &port) { return port.annotations.empty(); }) &&
1713  "class ports may not have annotations");
1714 
1715  buildModuleWithoutAnnos(builder, result, name, ports);
1716 
1717  // Create a region and a block for the body.
1718  auto *bodyRegion = result.regions[0].get();
1719  Block *body = new Block();
1720  bodyRegion->push_back(body);
1721 
1722  // Add arguments to the body block.
1723  for (auto &elt : ports)
1724  body->addArgument(elt.type, elt.loc);
1725 }
1726 
1727 void ClassOp::print(OpAsmPrinter &p) {
1728  printClassLike(p, cast<ClassLike>(getOperation()));
1729 }
1730 
1731 ParseResult ClassOp::parse(OpAsmParser &parser, OperationState &result) {
1732  auto hasSSAIdentifiers = true;
1733  return parseClassLike(parser, result, hasSSAIdentifiers);
1734 }
1735 
1736 LogicalResult ClassOp::verify() {
1737  for (auto operand : getBodyBlock()->getArguments()) {
1738  auto type = operand.getType();
1739  if (!isa<PropertyType>(type)) {
1740  emitOpError("ports on a class must be properties");
1741  return failure();
1742  }
1743  }
1744 
1745  return success();
1746 }
1747 
1748 LogicalResult
1749 ClassOp::verifySymbolUses(::mlir::SymbolTableCollection &symbolTable) {
1750  return verifyPortSymbolUses(cast<FModuleLike>(getOperation()), symbolTable);
1751 }
1752 
1753 void ClassOp::getAsmBlockArgumentNames(mlir::Region &region,
1754  mlir::OpAsmSetValueNameFn setNameFn) {
1755  getAsmBlockArgumentNamesImpl(getOperation(), region, setNameFn);
1756 }
1757 
1758 SmallVector<PortInfo> ClassOp::getPorts() {
1759  return ::getPortImpl(cast<FModuleLike>((Operation *)*this));
1760 }
1761 
1762 void ClassOp::erasePorts(const llvm::BitVector &portIndices) {
1763  ::erasePorts(cast<FModuleLike>((Operation *)*this), portIndices);
1764  getBodyBlock()->eraseArguments(portIndices);
1765 }
1766 
1767 void ClassOp::insertPorts(ArrayRef<std::pair<unsigned, PortInfo>> ports) {
1768  ::insertPorts(cast<FModuleLike>((Operation *)*this), ports);
1769 }
1770 
1771 Convention ClassOp::getConvention() { return Convention::Internal; }
1772 
1773 ConventionAttr ClassOp::getConventionAttr() {
1774  return ConventionAttr::get(getContext(), getConvention());
1775 }
1776 
1777 ArrayAttr ClassOp::getParameters() { return {}; }
1778 
1779 ArrayAttr ClassOp::getPortAnnotationsAttr() {
1780  return ArrayAttr::get(getContext(), {});
1781 }
1782 
1783 hw::ModulePortInfo ClassOp::getPortList() { return ::getPortListImpl(*this); }
1784 
1785 BlockArgument ClassOp::getArgument(size_t portNumber) {
1786  return getBodyBlock()->getArgument(portNumber);
1787 }
1788 
1789 bool ClassOp::canDiscardOnUseEmpty() {
1790  // ClassOps are referenced by ClassTypes, and these uses are not
1791  // discoverable by the symbol infrastructure. Return false here to prevent
1792  // passes like symbolDCE from removing our classes.
1793  return false;
1794 }
1795 
1796 //===----------------------------------------------------------------------===//
1797 // ExtClassOp
1798 //===----------------------------------------------------------------------===//
1799 
1800 void ExtClassOp::build(OpBuilder &builder, OperationState &result,
1801  StringAttr name, ArrayRef<PortInfo> ports) {
1802  assert(
1803  llvm::all_of(ports,
1804  [](const auto &port) { return port.annotations.empty(); }) &&
1805  "class ports may not have annotations");
1806 
1807  buildModuleWithoutAnnos(builder, result, name, ports);
1808 }
1809 
1810 void ExtClassOp::print(OpAsmPrinter &p) {
1811  printClassLike(p, cast<ClassLike>(getOperation()));
1812 }
1813 
1814 ParseResult ExtClassOp::parse(OpAsmParser &parser, OperationState &result) {
1815  auto hasSSAIdentifiers = false;
1816  return parseClassLike(parser, result, hasSSAIdentifiers);
1817 }
1818 
1819 LogicalResult
1820 ExtClassOp::verifySymbolUses(::mlir::SymbolTableCollection &symbolTable) {
1821  return verifyPortSymbolUses(cast<FModuleLike>(getOperation()), symbolTable);
1822 }
1823 
1824 void ExtClassOp::getAsmBlockArgumentNames(mlir::Region &region,
1825  mlir::OpAsmSetValueNameFn setNameFn) {
1826  getAsmBlockArgumentNamesImpl(getOperation(), region, setNameFn);
1827 }
1828 
1829 SmallVector<PortInfo> ExtClassOp::getPorts() {
1830  return ::getPortImpl(cast<FModuleLike>((Operation *)*this));
1831 }
1832 
1833 void ExtClassOp::erasePorts(const llvm::BitVector &portIndices) {
1834  ::erasePorts(cast<FModuleLike>((Operation *)*this), portIndices);
1835 }
1836 
1837 void ExtClassOp::insertPorts(ArrayRef<std::pair<unsigned, PortInfo>> ports) {
1838  ::insertPorts(cast<FModuleLike>((Operation *)*this), ports);
1839 }
1840 
1841 Convention ExtClassOp::getConvention() { return Convention::Internal; }
1842 
1843 ConventionAttr ExtClassOp::getConventionAttr() {
1844  return ConventionAttr::get(getContext(), getConvention());
1845 }
1846 
1847 ArrayAttr ExtClassOp::getParameters() { return {}; }
1848 
1849 ArrayAttr ExtClassOp::getPortAnnotationsAttr() {
1850  return ArrayAttr::get(getContext(), {});
1851 }
1852 
1853 hw::ModulePortInfo ExtClassOp::getPortList() {
1854  return ::getPortListImpl(*this);
1855 }
1856 
1857 bool ExtClassOp::canDiscardOnUseEmpty() {
1858  // ClassOps are referenced by ClassTypes, and these uses are not
1859  // discovereable by the symbol infrastructure. Return false here to prevent
1860  // passes like symbolDCE from removing our classes.
1861  return false;
1862 }
1863 
1864 //===----------------------------------------------------------------------===//
1865 // Declarations
1866 //===----------------------------------------------------------------------===//
1867 
1868 /// Lookup the module or extmodule for the symbol. This returns null on
1869 /// invalid IR.
1870 Operation *InstanceOp::getReferencedModuleSlow() {
1871  auto circuit = (*this)->getParentOfType<CircuitOp>();
1872  if (!circuit)
1873  return nullptr;
1874 
1875  return circuit.lookupSymbol<FModuleLike>(getModuleNameAttr());
1876 }
1877 
1878 hw::ModulePortInfo InstanceOp::getPortList() {
1879  return cast<hw::PortList>(getReferencedModuleSlow()).getPortList();
1880 }
1881 
1882 void InstanceOp::build(OpBuilder &builder, OperationState &result,
1883  TypeRange resultTypes, StringRef moduleName,
1884  StringRef name, NameKindEnum nameKind,
1885  ArrayRef<Direction> portDirections,
1886  ArrayRef<Attribute> portNames,
1887  ArrayRef<Attribute> annotations,
1888  ArrayRef<Attribute> portAnnotations, bool lowerToBind,
1889  StringAttr innerSym) {
1890  build(builder, result, resultTypes, moduleName, name, nameKind,
1891  portDirections, portNames, annotations, portAnnotations, lowerToBind,
1892  innerSym ? hw::InnerSymAttr::get(innerSym) : hw::InnerSymAttr());
1893 }
1894 
1895 void InstanceOp::build(OpBuilder &builder, OperationState &result,
1896  TypeRange resultTypes, StringRef moduleName,
1897  StringRef name, NameKindEnum nameKind,
1898  ArrayRef<Direction> portDirections,
1899  ArrayRef<Attribute> portNames,
1900  ArrayRef<Attribute> annotations,
1901  ArrayRef<Attribute> portAnnotations, bool lowerToBind,
1902  hw::InnerSymAttr innerSym) {
1903  result.addTypes(resultTypes);
1904  result.addAttribute("moduleName",
1905  SymbolRefAttr::get(builder.getContext(), moduleName));
1906  result.addAttribute("name", builder.getStringAttr(name));
1907  result.addAttribute(
1908  "portDirections",
1909  direction::packAttribute(builder.getContext(), portDirections));
1910  result.addAttribute("portNames", builder.getArrayAttr(portNames));
1911  result.addAttribute("annotations", builder.getArrayAttr(annotations));
1912  if (lowerToBind)
1913  result.addAttribute("lowerToBind", builder.getUnitAttr());
1914  if (innerSym)
1915  result.addAttribute("inner_sym", innerSym);
1916  result.addAttribute("nameKind",
1917  NameKindEnumAttr::get(builder.getContext(), nameKind));
1918 
1919  if (portAnnotations.empty()) {
1920  SmallVector<Attribute, 16> portAnnotationsVec(resultTypes.size(),
1921  builder.getArrayAttr({}));
1922  result.addAttribute("portAnnotations",
1923  builder.getArrayAttr(portAnnotationsVec));
1924  } else {
1925  assert(portAnnotations.size() == resultTypes.size());
1926  result.addAttribute("portAnnotations",
1927  builder.getArrayAttr(portAnnotations));
1928  }
1929 }
1930 
1931 void InstanceOp::build(OpBuilder &builder, OperationState &result,
1932  FModuleLike module, StringRef name,
1933  NameKindEnum nameKind, ArrayRef<Attribute> annotations,
1934  ArrayRef<Attribute> portAnnotations, bool lowerToBind,
1935  StringAttr innerSym) {
1936 
1937  // Gather the result types.
1938  SmallVector<Type> resultTypes;
1939  resultTypes.reserve(module.getNumPorts());
1940  llvm::transform(
1941  module.getPortTypes(), std::back_inserter(resultTypes),
1942  [](Attribute typeAttr) { return cast<TypeAttr>(typeAttr).getValue(); });
1943 
1944  // Create the port annotations.
1945  ArrayAttr portAnnotationsAttr;
1946  if (portAnnotations.empty()) {
1947  portAnnotationsAttr = builder.getArrayAttr(SmallVector<Attribute, 16>(
1948  resultTypes.size(), builder.getArrayAttr({})));
1949  } else {
1950  portAnnotationsAttr = builder.getArrayAttr(portAnnotations);
1951  }
1952 
1953  return build(
1954  builder, result, resultTypes,
1955  SymbolRefAttr::get(builder.getContext(), module.getModuleNameAttr()),
1956  builder.getStringAttr(name),
1957  NameKindEnumAttr::get(builder.getContext(), nameKind),
1958  module.getPortDirectionsAttr(), module.getPortNamesAttr(),
1959  builder.getArrayAttr(annotations), portAnnotationsAttr,
1960  lowerToBind ? builder.getUnitAttr() : UnitAttr(),
1961  innerSym ? hw::InnerSymAttr::get(innerSym) : hw::InnerSymAttr());
1962 }
1963 
1964 /// Builds a new `InstanceOp` with the ports listed in `portIndices` erased, and
1965 /// updates any users of the remaining ports to point at the new instance.
1966 InstanceOp InstanceOp::erasePorts(OpBuilder &builder,
1967  const llvm::BitVector &portIndices) {
1968  assert(portIndices.size() >= getNumResults() &&
1969  "portIndices is not at least as large as getNumResults()");
1970 
1971  if (portIndices.none())
1972  return *this;
1973 
1974  SmallVector<Type> newResultTypes = removeElementsAtIndices<Type>(
1975  SmallVector<Type>(result_type_begin(), result_type_end()), portIndices);
1976  SmallVector<Direction> newPortDirections = removeElementsAtIndices<Direction>(
1977  direction::unpackAttribute(getPortDirectionsAttr()), portIndices);
1978  SmallVector<Attribute> newPortNames =
1979  removeElementsAtIndices(getPortNames().getValue(), portIndices);
1980  SmallVector<Attribute> newPortAnnotations =
1981  removeElementsAtIndices(getPortAnnotations().getValue(), portIndices);
1982 
1983  auto newOp = builder.create<InstanceOp>(
1984  getLoc(), newResultTypes, getModuleName(), getName(), getNameKind(),
1985  newPortDirections, newPortNames, getAnnotations().getValue(),
1986  newPortAnnotations, getLowerToBind(), getInnerSymAttr());
1987 
1988  for (unsigned oldIdx = 0, newIdx = 0, numOldPorts = getNumResults();
1989  oldIdx != numOldPorts; ++oldIdx) {
1990  if (portIndices.test(oldIdx)) {
1991  assert(getResult(oldIdx).use_empty() && "removed instance port has uses");
1992  continue;
1993  }
1994  getResult(oldIdx).replaceAllUsesWith(newOp.getResult(newIdx));
1995  ++newIdx;
1996  }
1997 
1998  // Compy over "output_file" information so that this is not lost when ports
1999  // are erased.
2000  //
2001  // TODO: Other attributes may need to be copied over.
2002  if (auto outputFile = (*this)->getAttr("output_file"))
2003  newOp->setAttr("output_file", outputFile);
2004 
2005  return newOp;
2006 }
2007 
2008 ArrayAttr InstanceOp::getPortAnnotation(unsigned portIdx) {
2009  assert(portIdx < getNumResults() &&
2010  "index should be smaller than result number");
2011  return cast<ArrayAttr>(getPortAnnotations()[portIdx]);
2012 }
2013 
2014 void InstanceOp::setAllPortAnnotations(ArrayRef<Attribute> annotations) {
2015  assert(annotations.size() == getNumResults() &&
2016  "number of annotations is not equal to result number");
2017  (*this)->setAttr("portAnnotations",
2018  ArrayAttr::get(getContext(), annotations));
2019 }
2020 
2021 InstanceOp
2022 InstanceOp::cloneAndInsertPorts(ArrayRef<std::pair<unsigned, PortInfo>> ports) {
2023  auto portSize = ports.size();
2024  auto newPortCount = getNumResults() + portSize;
2025  SmallVector<Direction> newPortDirections;
2026  newPortDirections.reserve(newPortCount);
2027  SmallVector<Attribute> newPortNames;
2028  newPortNames.reserve(newPortCount);
2029  SmallVector<Type> newPortTypes;
2030  newPortTypes.reserve(newPortCount);
2031  SmallVector<Attribute> newPortAnnos;
2032  newPortAnnos.reserve(newPortCount);
2033 
2034  unsigned oldIndex = 0;
2035  unsigned newIndex = 0;
2036  while (oldIndex + newIndex < newPortCount) {
2037  // Check if we should insert a port here.
2038  if (newIndex < portSize && ports[newIndex].first == oldIndex) {
2039  auto &newPort = ports[newIndex].second;
2040  newPortDirections.push_back(newPort.direction);
2041  newPortNames.push_back(newPort.name);
2042  newPortTypes.push_back(newPort.type);
2043  newPortAnnos.push_back(newPort.annotations.getArrayAttr());
2044  ++newIndex;
2045  } else {
2046  // Copy the next old port.
2047  newPortDirections.push_back(getPortDirection(oldIndex));
2048  newPortNames.push_back(getPortName(oldIndex));
2049  newPortTypes.push_back(getType(oldIndex));
2050  newPortAnnos.push_back(getPortAnnotation(oldIndex));
2051  ++oldIndex;
2052  }
2053  }
2054 
2055  // Create a new instance op with the reset inserted.
2056  return OpBuilder(*this).create<InstanceOp>(
2057  getLoc(), newPortTypes, getModuleName(), getName(), getNameKind(),
2058  newPortDirections, newPortNames, getAnnotations().getValue(),
2059  newPortAnnos, getLowerToBind(), getInnerSymAttr());
2060 }
2061 
2062 LogicalResult InstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2063  auto module = (*this)->getParentOfType<FModuleOp>();
2064  auto referencedModule = symbolTable.lookupNearestSymbolFrom<FModuleLike>(
2065  *this, getModuleNameAttr());
2066  if (!referencedModule) {
2067  return emitOpError("invalid symbol reference");
2068  }
2069 
2070  // Check this is not a class.
2071  if (isa<ClassOp /* ClassLike */>(referencedModule))
2072  return emitOpError("must instantiate a module not a class")
2073  .attachNote(referencedModule.getLoc())
2074  << "class declared here";
2075 
2076  // Check that this instance doesn't recursively instantiate its wrapping
2077  // module.
2078  if (referencedModule == module) {
2079  auto diag = emitOpError()
2080  << "is a recursive instantiation of its containing module";
2081  return diag.attachNote(module.getLoc())
2082  << "containing module declared here";
2083  }
2084 
2085  // Small helper add a note to the original declaration.
2086  auto emitNote = [&](InFlightDiagnostic &&diag) -> InFlightDiagnostic && {
2087  diag.attachNote(referencedModule->getLoc())
2088  << "original module declared here";
2089  return std::move(diag);
2090  };
2091 
2092  // Check that all the attribute arrays are the right length up front. This
2093  // lets us safely use the port name in error messages below.
2094  size_t numResults = getNumResults();
2095  size_t numExpected = referencedModule.getNumPorts();
2096  if (numResults != numExpected) {
2097  return emitNote(emitOpError() << "has a wrong number of results; expected "
2098  << numExpected << " but got " << numResults);
2099  }
2100  if (getPortDirections().getBitWidth() != numExpected)
2101  return emitNote(emitOpError("the number of port directions should be "
2102  "equal to the number of results"));
2103  if (getPortNames().size() != numExpected)
2104  return emitNote(emitOpError("the number of port names should be "
2105  "equal to the number of results"));
2106  if (getPortAnnotations().size() != numExpected)
2107  return emitNote(emitOpError("the number of result annotations should be "
2108  "equal to the number of results"));
2109 
2110  // Check that the port names match the referenced module.
2111  if (getPortNamesAttr() != referencedModule.getPortNamesAttr()) {
2112  // We know there is an error, try to figure out whats wrong.
2113  auto instanceNames = getPortNames();
2114  auto moduleNames = referencedModule.getPortNamesAttr();
2115  // First compare the sizes:
2116  if (instanceNames.size() != moduleNames.size()) {
2117  return emitNote(emitOpError()
2118  << "has a wrong number of directions; expected "
2119  << moduleNames.size() << " but got "
2120  << instanceNames.size());
2121  }
2122  // Next check the values:
2123  for (size_t i = 0; i != numResults; ++i) {
2124  if (instanceNames[i] != moduleNames[i]) {
2125  return emitNote(emitOpError()
2126  << "name for port " << i << " must be "
2127  << moduleNames[i] << ", but got " << instanceNames[i]);
2128  }
2129  }
2130  llvm_unreachable("should have found something wrong");
2131  }
2132 
2133  // Check that the types match.
2134  for (size_t i = 0; i != numResults; i++) {
2135  auto resultType = getResult(i).getType();
2136  auto expectedType = referencedModule.getPortType(i);
2137  if (resultType != expectedType) {
2138  return emitNote(emitOpError()
2139  << "result type for " << getPortName(i) << " must be "
2140  << expectedType << ", but got " << resultType);
2141  }
2142  }
2143 
2144  // Check that the port directions are consistent with the referenced module's.
2145  if (getPortDirectionsAttr() != referencedModule.getPortDirectionsAttr()) {
2146  // We know there is an error, try to figure out whats wrong.
2147  auto instanceDirectionAttr = getPortDirectionsAttr();
2148  auto moduleDirectionAttr = referencedModule.getPortDirectionsAttr();
2149  // First compare the sizes:
2150  auto expectedWidth = moduleDirectionAttr.getValue().getBitWidth();
2151  auto actualWidth = instanceDirectionAttr.getValue().getBitWidth();
2152  if (expectedWidth != actualWidth) {
2153  return emitNote(emitOpError()
2154  << "has a wrong number of directions; expected "
2155  << expectedWidth << " but got " << actualWidth);
2156  }
2157  // Next check the values.
2158  auto instanceDirs = direction::unpackAttribute(instanceDirectionAttr);
2159  auto moduleDirs = direction::unpackAttribute(moduleDirectionAttr);
2160  for (size_t i = 0; i != numResults; ++i) {
2161  if (instanceDirs[i] != moduleDirs[i]) {
2162  return emitNote(emitOpError()
2163  << "direction for " << getPortName(i) << " must be \""
2164  << direction::toString(moduleDirs[i])
2165  << "\", but got \""
2166  << direction::toString(instanceDirs[i]) << "\"");
2167  }
2168  }
2169  llvm_unreachable("should have found something wrong");
2170  }
2171 
2172  return success();
2173 }
2174 
2175 StringRef InstanceOp::getInstanceName() { return getName(); }
2176 
2177 StringAttr InstanceOp::getInstanceNameAttr() { return getNameAttr(); }
2178 
2179 void InstanceOp::print(OpAsmPrinter &p) {
2180  // Print the instance name.
2181  p << " ";
2182  p.printKeywordOrString(getName());
2183  if (auto attr = getInnerSymAttr()) {
2184  p << " sym ";
2185  p.printSymbolName(attr.getSymName());
2186  }
2187  if (getNameKindAttr().getValue() != NameKindEnum::DroppableName)
2188  p << ' ' << stringifyNameKindEnum(getNameKindAttr().getValue());
2189 
2190  // Print the attr-dict.
2191  SmallVector<StringRef, 9> omittedAttrs = {"moduleName", "name",
2192  "portDirections", "portNames",
2193  "portTypes", "portAnnotations",
2194  "inner_sym", "nameKind"};
2195  if (getAnnotations().empty())
2196  omittedAttrs.push_back("annotations");
2197  p.printOptionalAttrDict((*this)->getAttrs(), omittedAttrs);
2198 
2199  // Print the module name.
2200  p << " ";
2201  p.printSymbolName(getModuleName());
2202 
2203  // Collect all the result types as TypeAttrs for printing.
2204  SmallVector<Attribute> portTypes;
2205  portTypes.reserve(getNumResults());
2206  llvm::transform(getResultTypes(), std::back_inserter(portTypes),
2207  &TypeAttr::get);
2208  auto portDirections = direction::unpackAttribute(getPortDirectionsAttr());
2209  printModulePorts(p, /*block=*/nullptr, portDirections,
2210  getPortNames().getValue(), portTypes,
2211  getPortAnnotations().getValue(), {}, {});
2212 }
2213 
2214 ParseResult InstanceOp::parse(OpAsmParser &parser, OperationState &result) {
2215  auto *context = parser.getContext();
2216  auto &resultAttrs = result.attributes;
2217 
2218  std::string name;
2219  hw::InnerSymAttr innerSymAttr;
2220  FlatSymbolRefAttr moduleName;
2221  SmallVector<OpAsmParser::Argument> entryArgs;
2222  SmallVector<Direction, 4> portDirections;
2223  SmallVector<Attribute, 4> portNames;
2224  SmallVector<Attribute, 4> portTypes;
2225  SmallVector<Attribute, 4> portAnnotations;
2226  SmallVector<Attribute, 4> portSyms;
2227  SmallVector<Attribute, 4> portLocs;
2228  NameKindEnumAttr nameKind;
2229 
2230  if (parser.parseKeywordOrString(&name))
2231  return failure();
2232  if (succeeded(parser.parseOptionalKeyword("sym"))) {
2233  if (parser.parseCustomAttributeWithFallback(
2234  innerSymAttr, ::mlir::Type{},
2236  result.attributes)) {
2237  return ::mlir::failure();
2238  }
2239  }
2240  if (parseNameKind(parser, nameKind) ||
2241  parser.parseOptionalAttrDict(result.attributes) ||
2242  parser.parseAttribute(moduleName, "moduleName", resultAttrs) ||
2243  parseModulePorts(parser, /*hasSSAIdentifiers=*/false,
2244  /*supportsSymbols=*/false, entryArgs, portDirections,
2245  portNames, portTypes, portAnnotations, portSyms,
2246  portLocs))
2247  return failure();
2248 
2249  // Add the attributes. We let attributes defined in the attr-dict override
2250  // attributes parsed out of the module signature.
2251  if (!resultAttrs.get("moduleName"))
2252  result.addAttribute("moduleName", moduleName);
2253  if (!resultAttrs.get("name"))
2254  result.addAttribute("name", StringAttr::get(context, name));
2255  result.addAttribute("nameKind", nameKind);
2256  if (!resultAttrs.get("portDirections"))
2257  result.addAttribute("portDirections",
2258  direction::packAttribute(context, portDirections));
2259  if (!resultAttrs.get("portNames"))
2260  result.addAttribute("portNames", ArrayAttr::get(context, portNames));
2261  if (!resultAttrs.get("portAnnotations"))
2262  result.addAttribute("portAnnotations",
2263  ArrayAttr::get(context, portAnnotations));
2264 
2265  // Annotations and LowerToBind are omitted in the printed format if they are
2266  // empty and false, respectively.
2267  if (!resultAttrs.get("annotations"))
2268  resultAttrs.append("annotations", parser.getBuilder().getArrayAttr({}));
2269 
2270  // Add result types.
2271  result.types.reserve(portTypes.size());
2272  llvm::transform(
2273  portTypes, std::back_inserter(result.types),
2274  [](Attribute typeAttr) { return cast<TypeAttr>(typeAttr).getValue(); });
2275 
2276  return success();
2277 }
2278 
2280  StringRef base = getName();
2281  if (base.empty())
2282  base = "inst";
2283 
2284  for (size_t i = 0, e = (*this)->getNumResults(); i != e; ++i) {
2285  setNameFn(getResult(i), (base + "_" + getPortNameStr(i)).str());
2286  }
2287 }
2288 
2289 std::optional<size_t> InstanceOp::getTargetResultIndex() {
2290  // Inner symbols on instance operations target the op not any result.
2291  return std::nullopt;
2292 }
2293 
2294 void MemOp::build(OpBuilder &builder, OperationState &result,
2295  TypeRange resultTypes, uint32_t readLatency,
2296  uint32_t writeLatency, uint64_t depth, RUWAttr ruw,
2297  ArrayRef<Attribute> portNames, StringRef name,
2298  NameKindEnum nameKind, ArrayRef<Attribute> annotations,
2299  ArrayRef<Attribute> portAnnotations,
2300  hw::InnerSymAttr innerSym) {
2301  result.addAttribute(
2302  "readLatency",
2303  builder.getIntegerAttr(builder.getIntegerType(32), readLatency));
2304  result.addAttribute(
2305  "writeLatency",
2306  builder.getIntegerAttr(builder.getIntegerType(32), writeLatency));
2307  result.addAttribute(
2308  "depth", builder.getIntegerAttr(builder.getIntegerType(64), depth));
2309  result.addAttribute("ruw", ::RUWAttrAttr::get(builder.getContext(), ruw));
2310  result.addAttribute("portNames", builder.getArrayAttr(portNames));
2311  result.addAttribute("name", builder.getStringAttr(name));
2312  result.addAttribute("nameKind",
2313  NameKindEnumAttr::get(builder.getContext(), nameKind));
2314  result.addAttribute("annotations", builder.getArrayAttr(annotations));
2315  if (innerSym)
2316  result.addAttribute("inner_sym", innerSym);
2317  result.addTypes(resultTypes);
2318 
2319  if (portAnnotations.empty()) {
2320  SmallVector<Attribute, 16> portAnnotationsVec(resultTypes.size(),
2321  builder.getArrayAttr({}));
2322  result.addAttribute("portAnnotations",
2323  builder.getArrayAttr(portAnnotationsVec));
2324  } else {
2325  assert(portAnnotations.size() == resultTypes.size());
2326  result.addAttribute("portAnnotations",
2327  builder.getArrayAttr(portAnnotations));
2328  }
2329 }
2330 
2331 void MemOp::build(OpBuilder &builder, OperationState &result,
2332  TypeRange resultTypes, uint32_t readLatency,
2333  uint32_t writeLatency, uint64_t depth, RUWAttr ruw,
2334  ArrayRef<Attribute> portNames, StringRef name,
2335  NameKindEnum nameKind, ArrayRef<Attribute> annotations,
2336  ArrayRef<Attribute> portAnnotations, StringAttr innerSym) {
2337  build(builder, result, resultTypes, readLatency, writeLatency, depth, ruw,
2338  portNames, name, nameKind, annotations, portAnnotations,
2339  innerSym ? hw::InnerSymAttr::get(innerSym) : hw::InnerSymAttr());
2340 }
2341 
2342 ArrayAttr MemOp::getPortAnnotation(unsigned portIdx) {
2343  assert(portIdx < getNumResults() &&
2344  "index should be smaller than result number");
2345  return cast<ArrayAttr>(getPortAnnotations()[portIdx]);
2346 }
2347 
2348 void MemOp::setAllPortAnnotations(ArrayRef<Attribute> annotations) {
2349  assert(annotations.size() == getNumResults() &&
2350  "number of annotations is not equal to result number");
2351  (*this)->setAttr("portAnnotations",
2352  ArrayAttr::get(getContext(), annotations));
2353 }
2354 
2355 // Get the number of read, write and read-write ports.
2356 void MemOp::getNumPorts(size_t &numReadPorts, size_t &numWritePorts,
2357  size_t &numReadWritePorts, size_t &numDbgsPorts) {
2358  numReadPorts = 0;
2359  numWritePorts = 0;
2360  numReadWritePorts = 0;
2361  numDbgsPorts = 0;
2362  for (size_t i = 0, e = getNumResults(); i != e; ++i) {
2363  auto portKind = getPortKind(i);
2364  if (portKind == MemOp::PortKind::Debug)
2365  ++numDbgsPorts;
2366  else if (portKind == MemOp::PortKind::Read)
2367  ++numReadPorts;
2368  else if (portKind == MemOp::PortKind::Write) {
2369  ++numWritePorts;
2370  } else
2371  ++numReadWritePorts;
2372  }
2373 }
2374 
2375 /// Verify the correctness of a MemOp.
2376 LogicalResult MemOp::verify() {
2377 
2378  // Store the port names as we find them. This lets us check quickly
2379  // for uniqueneess.
2380  llvm::SmallDenseSet<Attribute, 8> portNamesSet;
2381 
2382  // Store the previous data type. This lets us check that the data
2383  // type is consistent across all ports.
2384  FIRRTLType oldDataType;
2385 
2386  for (size_t i = 0, e = getNumResults(); i != e; ++i) {
2387  auto portName = getPortName(i);
2388 
2389  // Get a bundle type representing this port, stripping an outer
2390  // flip if it exists. If this is not a bundle<> or
2391  // flip<bundle<>>, then this is an error.
2392  BundleType portBundleType =
2393  type_dyn_cast<BundleType>(getResult(i).getType());
2394 
2395  // Require that all port names are unique.
2396  if (!portNamesSet.insert(portName).second) {
2397  emitOpError() << "has non-unique port name " << portName;
2398  return failure();
2399  }
2400 
2401  // Determine the kind of the memory. If the kind cannot be
2402  // determined, then it's indicative of the wrong number of fields
2403  // in the type (but we don't know any more just yet).
2404 
2405  auto elt = getPortNamed(portName);
2406  if (!elt) {
2407  emitOpError() << "could not get port with name " << portName;
2408  return failure();
2409  }
2410  auto firrtlType = type_cast<FIRRTLType>(elt.getType());
2411  MemOp::PortKind portKind = getMemPortKindFromType(firrtlType);
2412 
2413  if (portKind == MemOp::PortKind::Debug &&
2414  !type_isa<RefType>(getResult(i).getType()))
2415  return emitOpError() << "has an invalid type on port " << portName
2416  << " (expected Read/Write/ReadWrite/Debug)";
2417  if (type_isa<RefType>(firrtlType) && e == 1)
2418  return emitOpError()
2419  << "cannot have only one port of debug type. Debug port can only "
2420  "exist alongside other read/write/read-write port";
2421 
2422  // Safely search for the "data" field, erroring if it can't be
2423  // found.
2424  FIRRTLBaseType dataType;
2425  if (portKind == MemOp::PortKind::Debug) {
2426  auto resType = type_cast<RefType>(getResult(i).getType());
2427  if (!(resType && type_isa<FVectorType>(resType.getType())))
2428  return emitOpError() << "debug ports must be a RefType of FVectorType";
2429  dataType = type_cast<FVectorType>(resType.getType()).getElementType();
2430  } else {
2431  auto dataTypeOption = portBundleType.getElement("data");
2432  if (!dataTypeOption && portKind == MemOp::PortKind::ReadWrite)
2433  dataTypeOption = portBundleType.getElement("wdata");
2434  if (!dataTypeOption) {
2435  emitOpError() << "has no data field on port " << portName
2436  << " (expected to see \"data\" for a read or write "
2437  "port or \"rdata\" for a read/write port)";
2438  return failure();
2439  }
2440  dataType = dataTypeOption->type;
2441  // Read data is expected to ba a flip.
2442  if (portKind == MemOp::PortKind::Read) {
2443  // FIXME error on missing bundle flip
2444  }
2445  }
2446 
2447  // Error if the data type isn't passive.
2448  if (!dataType.isPassive()) {
2449  emitOpError() << "has non-passive data type on port " << portName
2450  << " (memory types must be passive)";
2451  return failure();
2452  }
2453 
2454  // Error if the data type contains analog types.
2455  if (dataType.containsAnalog()) {
2456  emitOpError() << "has a data type that contains an analog type on port "
2457  << portName
2458  << " (memory types cannot contain analog types)";
2459  return failure();
2460  }
2461 
2462  // Check that the port type matches the kind that we determined
2463  // for this port. This catches situations of extraneous port
2464  // fields beind included or the fields being named incorrectly.
2465  FIRRTLType expectedType =
2466  getTypeForPort(getDepth(), dataType, portKind,
2467  dataType.isGround() ? getMaskBits() : 0);
2468  // Compute the original port type as portBundleType may have
2469  // stripped outer flip information.
2470  auto originalType = getResult(i).getType();
2471  if (originalType != expectedType) {
2472  StringRef portKindName;
2473  switch (portKind) {
2474  case MemOp::PortKind::Read:
2475  portKindName = "read";
2476  break;
2477  case MemOp::PortKind::Write:
2478  portKindName = "write";
2479  break;
2480  case MemOp::PortKind::ReadWrite:
2481  portKindName = "readwrite";
2482  break;
2483  case MemOp::PortKind::Debug:
2484  portKindName = "dbg";
2485  break;
2486  }
2487  emitOpError() << "has an invalid type for port " << portName
2488  << " of determined kind \"" << portKindName
2489  << "\" (expected " << expectedType << ", but got "
2490  << originalType << ")";
2491  return failure();
2492  }
2493 
2494  // Error if the type of the current port was not the same as the
2495  // last port, but skip checking the first port.
2496  if (oldDataType && oldDataType != dataType) {
2497  emitOpError() << "port " << getPortName(i)
2498  << " has a different type than port " << getPortName(i - 1)
2499  << " (expected " << oldDataType << ", but got " << dataType
2500  << ")";
2501  return failure();
2502  }
2503 
2504  oldDataType = dataType;
2505  }
2506 
2507  auto maskWidth = getMaskBits();
2508 
2509  auto dataWidth = getDataType().getBitWidthOrSentinel();
2510  if (dataWidth > 0 && maskWidth > (size_t)dataWidth)
2511  return emitOpError("the mask width cannot be greater than "
2512  "data width");
2513 
2514  if (getPortAnnotations().size() != getNumResults())
2515  return emitOpError("the number of result annotations should be "
2516  "equal to the number of results");
2517 
2518  return success();
2519 }
2520 
2521 static size_t getAddressWidth(size_t depth) {
2522  return std::max(1U, llvm::Log2_64_Ceil(depth));
2523 }
2524 
2525 size_t MemOp::getAddrBits() { return getAddressWidth(getDepth()); }
2526 
2527 FIRRTLType MemOp::getTypeForPort(uint64_t depth, FIRRTLBaseType dataType,
2528  PortKind portKind, size_t maskBits) {
2529 
2530  auto *context = dataType.getContext();
2531  if (portKind == PortKind::Debug)
2532  return RefType::get(FVectorType::get(dataType, depth));
2533  FIRRTLBaseType maskType;
2534  // maskBits not specified (==0), then get the mask type from the dataType.
2535  if (maskBits == 0)
2536  maskType = dataType.getMaskType();
2537  else
2538  maskType = UIntType::get(context, maskBits);
2539 
2540  auto getId = [&](StringRef name) -> StringAttr {
2541  return StringAttr::get(context, name);
2542  };
2543 
2544  SmallVector<BundleType::BundleElement, 7> portFields;
2545 
2546  auto addressType = UIntType::get(context, getAddressWidth(depth));
2547 
2548  portFields.push_back({getId("addr"), false, addressType});
2549  portFields.push_back({getId("en"), false, UIntType::get(context, 1)});
2550  portFields.push_back({getId("clk"), false, ClockType::get(context)});
2551 
2552  switch (portKind) {
2553  case PortKind::Read:
2554  portFields.push_back({getId("data"), true, dataType});
2555  break;
2556 
2557  case PortKind::Write:
2558  portFields.push_back({getId("data"), false, dataType});
2559  portFields.push_back({getId("mask"), false, maskType});
2560  break;
2561 
2562  case PortKind::ReadWrite:
2563  portFields.push_back({getId("rdata"), true, dataType});
2564  portFields.push_back({getId("wmode"), false, UIntType::get(context, 1)});
2565  portFields.push_back({getId("wdata"), false, dataType});
2566  portFields.push_back({getId("wmask"), false, maskType});
2567  break;
2568  default:
2569  llvm::report_fatal_error("memory port kind not handled");
2570  break;
2571  }
2572 
2573  return BundleType::get(context, portFields);
2574 }
2575 
2576 /// Return the name and kind of ports supported by this memory.
2577 SmallVector<MemOp::NamedPort> MemOp::getPorts() {
2578  SmallVector<MemOp::NamedPort> result;
2579  // Each entry in the bundle is a port.
2580  for (size_t i = 0, e = getNumResults(); i != e; ++i) {
2581  // Each port is a bundle.
2582  auto portType = type_cast<FIRRTLType>(getResult(i).getType());
2583  result.push_back({getPortName(i), getMemPortKindFromType(portType)});
2584  }
2585  return result;
2586 }
2587 
2588 /// Return the kind of the specified port.
2589 MemOp::PortKind MemOp::getPortKind(StringRef portName) {
2590  return getMemPortKindFromType(
2591  type_cast<FIRRTLType>(getPortNamed(portName).getType()));
2592 }
2593 
2594 /// Return the kind of the specified port number.
2595 MemOp::PortKind MemOp::getPortKind(size_t resultNo) {
2596  return getMemPortKindFromType(
2597  type_cast<FIRRTLType>(getResult(resultNo).getType()));
2598 }
2599 
2600 /// Return the number of bits in the mask for the memory.
2601 size_t MemOp::getMaskBits() {
2602 
2603  for (auto res : getResults()) {
2604  if (type_isa<RefType>(res.getType()))
2605  continue;
2606  auto firstPortType = type_cast<FIRRTLBaseType>(res.getType());
2607  if (getMemPortKindFromType(firstPortType) == PortKind::Read ||
2608  getMemPortKindFromType(firstPortType) == PortKind::Debug)
2609  continue;
2610 
2611  FIRRTLBaseType mType;
2612  for (auto t : type_cast<BundleType>(firstPortType.getPassiveType())) {
2613  if (t.name.getValue().contains("mask"))
2614  mType = t.type;
2615  }
2616  if (type_isa<UIntType>(mType))
2617  return mType.getBitWidthOrSentinel();
2618  }
2619  // Mask of zero bits means, either there are no write/readwrite ports or the
2620  // mask is of aggregate type.
2621  return 0;
2622 }
2623 
2624 /// Return the data-type field of the memory, the type of each element.
2625 FIRRTLBaseType MemOp::getDataType() {
2626  assert(getNumResults() != 0 && "Mems with no read/write ports are illegal");
2627 
2628  if (auto refType = type_dyn_cast<RefType>(getResult(0).getType()))
2629  return type_cast<FVectorType>(refType.getType()).getElementType();
2630  auto firstPortType = type_cast<FIRRTLBaseType>(getResult(0).getType());
2631 
2632  StringRef dataFieldName = "data";
2633  if (getMemPortKindFromType(firstPortType) == PortKind::ReadWrite)
2634  dataFieldName = "rdata";
2635 
2636  return type_cast<BundleType>(firstPortType.getPassiveType())
2637  .getElementType(dataFieldName);
2638 }
2639 
2640 StringAttr MemOp::getPortName(size_t resultNo) {
2641  return cast<StringAttr>(getPortNames()[resultNo]);
2642 }
2643 
2644 FIRRTLBaseType MemOp::getPortType(size_t resultNo) {
2645  return type_cast<FIRRTLBaseType>(getResults()[resultNo].getType());
2646 }
2647 
2648 Value MemOp::getPortNamed(StringAttr name) {
2649  auto namesArray = getPortNames();
2650  for (size_t i = 0, e = namesArray.size(); i != e; ++i) {
2651  if (namesArray[i] == name) {
2652  assert(i < getNumResults() && " names array out of sync with results");
2653  return getResult(i);
2654  }
2655  }
2656  return Value();
2657 }
2658 
2659 // Extract all the relevant attributes from the MemOp and return the FirMemory.
2661  auto op = *this;
2662  size_t numReadPorts = 0;
2663  size_t numWritePorts = 0;
2664  size_t numReadWritePorts = 0;
2666  SmallVector<int32_t> writeClockIDs;
2667 
2668  for (size_t i = 0, e = op.getNumResults(); i != e; ++i) {
2669  auto portKind = op.getPortKind(i);
2670  if (portKind == MemOp::PortKind::Read)
2671  ++numReadPorts;
2672  else if (portKind == MemOp::PortKind::Write) {
2673  for (auto *a : op.getResult(i).getUsers()) {
2674  auto subfield = dyn_cast<SubfieldOp>(a);
2675  if (!subfield || subfield.getFieldIndex() != 2)
2676  continue;
2677  auto clockPort = a->getResult(0);
2678  for (auto *b : clockPort.getUsers()) {
2679  if (auto connect = dyn_cast<FConnectLike>(b)) {
2680  if (connect.getDest() == clockPort) {
2681  auto result =
2682  clockToLeader.insert({circt::firrtl::getModuleScopedDriver(
2683  connect.getSrc(), true, true, true),
2684  numWritePorts});
2685  if (result.second) {
2686  writeClockIDs.push_back(numWritePorts);
2687  } else {
2688  writeClockIDs.push_back(result.first->second);
2689  }
2690  }
2691  }
2692  }
2693  break;
2694  }
2695  ++numWritePorts;
2696  } else
2697  ++numReadWritePorts;
2698  }
2699 
2700  size_t width = 0;
2701  if (auto widthV = getBitWidth(op.getDataType()))
2702  width = *widthV;
2703  else
2704  op.emitError("'firrtl.mem' should have simple type and known width");
2705  MemoryInitAttr init = op->getAttrOfType<MemoryInitAttr>("init");
2706  StringAttr modName;
2707  if (op->hasAttr("modName"))
2708  modName = op->getAttrOfType<StringAttr>("modName");
2709  else {
2710  SmallString<8> clocks;
2711  for (auto a : writeClockIDs)
2712  clocks.append(Twine((char)(a + 'a')).str());
2713  SmallString<32> initStr;
2714  // If there is a file initialization, then come up with a decent
2715  // representation for this. Use the filename, but only characters
2716  // [a-zA-Z0-9] and the bool/hex and inline booleans.
2717  if (init) {
2718  for (auto c : init.getFilename().getValue())
2719  if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
2720  (c >= '0' && c <= '9'))
2721  initStr.push_back(c);
2722  initStr.push_back('_');
2723  initStr.push_back(init.getIsBinary() ? 't' : 'f');
2724  initStr.push_back('_');
2725  initStr.push_back(init.getIsInline() ? 't' : 'f');
2726  }
2727  modName = StringAttr::get(
2728  op->getContext(),
2729  llvm::formatv(
2730  "{0}FIRRTLMem_{1}_{2}_{3}_{4}_{5}_{6}_{7}_{8}_{9}_{10}{11}{12}",
2731  op.getPrefix().value_or(""), numReadPorts, numWritePorts,
2732  numReadWritePorts, (size_t)width, op.getDepth(),
2733  op.getReadLatency(), op.getWriteLatency(), op.getMaskBits(),
2734  (unsigned)op.getRuw(), (unsigned)seq::WUW::PortOrder,
2735  clocks.empty() ? "" : "_" + clocks, init ? initStr.str() : ""));
2736  }
2737  return {numReadPorts,
2738  numWritePorts,
2739  numReadWritePorts,
2740  (size_t)width,
2741  op.getDepth(),
2742  op.getReadLatency(),
2743  op.getWriteLatency(),
2744  op.getMaskBits(),
2745  *seq::symbolizeRUW(unsigned(op.getRuw())),
2746  seq::WUW::PortOrder,
2747  writeClockIDs,
2748  modName,
2749  op.getMaskBits() > 1,
2750  init,
2751  op.getPrefixAttr(),
2752  op.getLoc()};
2753 }
2754 
2756  StringRef base = getName();
2757  if (base.empty())
2758  base = "mem";
2759 
2760  for (size_t i = 0, e = (*this)->getNumResults(); i != e; ++i) {
2761  setNameFn(getResult(i), (base + "_" + getPortNameStr(i)).str());
2762  }
2763 }
2764 
2765 std::optional<size_t> MemOp::getTargetResultIndex() {
2766  // Inner symbols on memory operations target the op not any result.
2767  return std::nullopt;
2768 }
2769 
2770 // Construct name of the module which will be used for the memory definition.
2771 StringAttr FirMemory::getFirMemoryName() const { return modName; }
2772 
2773 /// Helper for naming forceable declarations (and their optional ref result).
2774 static void forceableAsmResultNames(Forceable op, StringRef name,
2775  OpAsmSetValueNameFn setNameFn) {
2776  if (name.empty())
2777  return;
2778  setNameFn(op.getDataRaw(), name);
2779  if (op.isForceable())
2780  setNameFn(op.getDataRef(), (name + "_ref").str());
2781 }
2782 
2784  return forceableAsmResultNames(*this, getName(), setNameFn);
2785 }
2786 
2787 LogicalResult NodeOp::inferReturnTypes(
2788  mlir::MLIRContext *context, std::optional<mlir::Location> location,
2789  ::mlir::ValueRange operands, ::mlir::DictionaryAttr attributes,
2790  ::mlir::OpaqueProperties properties, ::mlir::RegionRange regions,
2791  ::llvm::SmallVectorImpl<::mlir::Type> &inferredReturnTypes) {
2792  if (operands.empty())
2793  return failure();
2794  inferredReturnTypes.push_back(operands[0].getType());
2795  for (auto &attr : attributes)
2796  if (attr.getName() == Forceable::getForceableAttrName()) {
2797  auto forceableType =
2798  firrtl::detail::getForceableResultType(true, operands[0].getType());
2799  if (!forceableType) {
2800  if (location)
2801  ::mlir::emitError(*location, "cannot force a node of type ")
2802  << operands[0].getType();
2803  return failure();
2804  }
2805  inferredReturnTypes.push_back(forceableType);
2806  }
2807  return success();
2808 }
2809 
2810 std::optional<size_t> NodeOp::getTargetResultIndex() { return 0; }
2811 
2813  return forceableAsmResultNames(*this, getName(), setNameFn);
2814 }
2815 
2816 std::optional<size_t> RegOp::getTargetResultIndex() { return 0; }
2817 
2818 LogicalResult RegResetOp::verify() {
2819  auto reset = getResetValue();
2820 
2821  FIRRTLBaseType resetType = reset.getType();
2822  FIRRTLBaseType regType = getResult().getType();
2823 
2824  // The type of the initialiser must be equivalent to the register type.
2825  if (!areTypesEquivalent(regType, resetType))
2826  return emitError("type mismatch between register ")
2827  << regType << " and reset value " << resetType;
2828 
2829  return success();
2830 }
2831 
2832 std::optional<size_t> RegResetOp::getTargetResultIndex() { return 0; }
2833 
2835  return forceableAsmResultNames(*this, getName(), setNameFn);
2836 }
2837 
2839  return forceableAsmResultNames(*this, getName(), setNameFn);
2840 }
2841 
2842 std::optional<size_t> WireOp::getTargetResultIndex() { return 0; }
2843 
2844 void ObjectOp::build(OpBuilder &builder, OperationState &state, ClassLike klass,
2845  StringRef name) {
2846  build(builder, state, klass.getInstanceType(),
2847  StringAttr::get(builder.getContext(), name));
2848 }
2849 
2850 LogicalResult ObjectOp::verify() { return success(); }
2851 
2852 LogicalResult ObjectOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2853  auto circuitOp = getOperation()->getParentOfType<CircuitOp>();
2854  auto classType = getType();
2855  auto className = classType.getNameAttr();
2856 
2857  // verify that the class exists.
2858  auto classOp = dyn_cast_or_null<ClassLike>(
2859  symbolTable.lookupSymbolIn(circuitOp, className));
2860  if (!classOp)
2861  return emitOpError() << "references unknown class " << className;
2862 
2863  // verify that the result type agrees with the class definition.
2864  if (failed(classOp.verifyType(classType, [&]() { return emitOpError(); })))
2865  return failure();
2866 
2867  return success();
2868 }
2869 
2870 StringAttr ObjectOp::getClassNameAttr() {
2871  return getType().getNameAttr().getAttr();
2872 }
2873 
2874 StringRef ObjectOp::getClassName() { return getType().getName(); }
2875 
2876 ClassLike ObjectOp::getReferencedClass(SymbolTable &symbolTable) {
2877  auto symRef = getType().getNameAttr();
2878  return symbolTable.lookup<ClassLike>(symRef.getLeafReference());
2879 }
2880 
2881 Operation *ObjectOp::getReferencedModule(SymbolTable &symtbl) {
2882  return getReferencedClass(symtbl);
2883 }
2884 
2885 Operation *ObjectOp::getReferencedModuleSlow() {
2886  auto circuit = (*this)->getParentOfType<CircuitOp>();
2887  if (!circuit)
2888  return nullptr;
2889 
2890  return circuit.lookupSymbol<ClassLike>(getClassNameAttr());
2891 }
2892 
2893 StringRef ObjectOp::getInstanceName() { return getName(); }
2894 
2895 StringAttr ObjectOp::getInstanceNameAttr() { return getNameAttr(); }
2896 
2897 StringRef ObjectOp::getReferencedModuleName() { return getClassName(); }
2898 
2899 StringAttr ObjectOp::getReferencedModuleNameAttr() {
2900  return getClassNameAttr();
2901 }
2902 
2904  setNameFn(getResult(), getName());
2905 }
2906 
2907 //===----------------------------------------------------------------------===//
2908 // Statements
2909 //===----------------------------------------------------------------------===//
2910 
2911 LogicalResult AttachOp::verify() {
2912  // All known widths must match.
2913  std::optional<int32_t> commonWidth;
2914  for (auto operand : getOperands()) {
2915  auto thisWidth = type_cast<AnalogType>(operand.getType()).getWidth();
2916  if (!thisWidth)
2917  continue;
2918  if (!commonWidth) {
2919  commonWidth = thisWidth;
2920  continue;
2921  }
2922  if (commonWidth != thisWidth)
2923  return emitOpError("is inavlid as not all known operand widths match");
2924  }
2925  return success();
2926 }
2927 
2928 /// Check if the source and sink are of appropriate flow.
2929 static LogicalResult checkConnectFlow(Operation *connect) {
2930  Value dst = connect->getOperand(0);
2931  Value src = connect->getOperand(1);
2932 
2933  // TODO: Relax this to allow reads from output ports,
2934  // instance/memory input ports.
2935  auto srcFlow = foldFlow(src);
2936  if (!isValidSrc(srcFlow)) {
2937  // A sink that is a port output or instance input used as a source is okay.
2938  auto kind = getDeclarationKind(src);
2939  if (kind != DeclKind::Port && kind != DeclKind::Instance) {
2940  auto srcRef = getFieldRefFromValue(src, /*lookThroughCasts=*/true);
2941  auto [srcName, rootKnown] = getFieldName(srcRef);
2942  auto diag = emitError(connect->getLoc());
2943  diag << "connect has invalid flow: the source expression ";
2944  if (rootKnown)
2945  diag << "\"" << srcName << "\" ";
2946  diag << "has " << toString(srcFlow) << ", expected source or duplex flow";
2947  return diag.attachNote(srcRef.getLoc()) << "the source was defined here";
2948  }
2949  }
2950 
2951  auto dstFlow = foldFlow(dst);
2952  if (!isValidDst(dstFlow)) {
2953  auto dstRef = getFieldRefFromValue(dst, /*lookThroughCasts=*/true);
2954  auto [dstName, rootKnown] = getFieldName(dstRef);
2955  auto diag = emitError(connect->getLoc());
2956  diag << "connect has invalid flow: the destination expression ";
2957  if (rootKnown)
2958  diag << "\"" << dstName << "\" ";
2959  diag << "has " << toString(dstFlow) << ", expected sink or duplex flow";
2960  return diag.attachNote(dstRef.getLoc())
2961  << "the destination was defined here";
2962  }
2963  return success();
2964 }
2965 
2966 // NOLINTBEGIN(misc-no-recursion)
2967 /// Checks if the type has any 'const' leaf elements . If `isFlip` is `true`,
2968 /// the `const` leaf is not considered to be driven.
2969 static bool isConstFieldDriven(FIRRTLBaseType type, bool isFlip = false,
2970  bool outerTypeIsConst = false) {
2971  auto typeIsConst = outerTypeIsConst || type.isConst();
2972 
2973  if (typeIsConst && type.isPassive())
2974  return !isFlip;
2975 
2976  if (auto bundleType = type_dyn_cast<BundleType>(type))
2977  return llvm::any_of(bundleType.getElements(), [&](auto &element) {
2978  return isConstFieldDriven(element.type, isFlip ^ element.isFlip,
2979  typeIsConst);
2980  });
2981 
2982  if (auto vectorType = type_dyn_cast<FVectorType>(type))
2983  return isConstFieldDriven(vectorType.getElementType(), isFlip, typeIsConst);
2984 
2985  if (typeIsConst)
2986  return !isFlip;
2987  return false;
2988 }
2989 // NOLINTEND(misc-no-recursion)
2990 
2991 /// Checks that connections to 'const' destinations are not dependent on
2992 /// non-'const' conditions in when blocks.
2993 static LogicalResult checkConnectConditionality(FConnectLike connect) {
2994  auto dest = connect.getDest();
2995  auto destType = type_dyn_cast<FIRRTLBaseType>(dest.getType());
2996  auto src = connect.getSrc();
2997  auto srcType = type_dyn_cast<FIRRTLBaseType>(src.getType());
2998  if (!destType || !srcType)
2999  return success();
3000 
3001  auto destRefinedType = destType;
3002  auto srcRefinedType = srcType;
3003 
3004  /// Looks up the value's defining op until the defining op is null or a
3005  /// declaration of the value. If a SubAccessOp is encountered with a 'const'
3006  /// input, `originalFieldType` is made 'const'.
3007  auto findFieldDeclarationRefiningFieldType =
3008  [](Value value, FIRRTLBaseType &originalFieldType) -> Value {
3009  while (auto *definingOp = value.getDefiningOp()) {
3010  bool shouldContinue = true;
3011  TypeSwitch<Operation *>(definingOp)
3012  .Case<SubfieldOp, SubindexOp>([&](auto op) { value = op.getInput(); })
3013  .Case<SubaccessOp>([&](SubaccessOp op) {
3014  if (op.getInput()
3015  .getType()
3016  .get()
3017  .getElementTypePreservingConst()
3018  .isConst())
3019  originalFieldType = originalFieldType.getConstType(true);
3020  value = op.getInput();
3021  })
3022  .Default([&](Operation *) { shouldContinue = false; });
3023  if (!shouldContinue)
3024  break;
3025  }
3026  return value;
3027  };
3028 
3029  auto destDeclaration =
3030  findFieldDeclarationRefiningFieldType(dest, destRefinedType);
3031  auto srcDeclaration =
3032  findFieldDeclarationRefiningFieldType(src, srcRefinedType);
3033 
3034  auto checkConstConditionality = [&](Value value, FIRRTLBaseType type,
3035  Value declaration) -> LogicalResult {
3036  auto *declarationBlock = declaration.getParentBlock();
3037  auto *block = connect->getBlock();
3038  while (block && block != declarationBlock) {
3039  auto *parentOp = block->getParentOp();
3040 
3041  if (auto whenOp = dyn_cast<WhenOp>(parentOp);
3042  whenOp && !whenOp.getCondition().getType().isConst()) {
3043  if (type.isConst())
3044  return connect.emitOpError()
3045  << "assignment to 'const' type " << type
3046  << " is dependent on a non-'const' condition";
3047  return connect->emitOpError()
3048  << "assignment to nested 'const' member of type " << type
3049  << " is dependent on a non-'const' condition";
3050  }
3051 
3052  block = parentOp->getBlock();
3053  }
3054  return success();
3055  };
3056 
3057  auto emitSubaccessError = [&] {
3058  return connect.emitError(
3059  "assignment to non-'const' subaccess of 'const' type is disallowed");
3060  };
3061 
3062  // Check destination if it contains 'const' leaves
3063  if (destRefinedType.containsConst() && isConstFieldDriven(destRefinedType)) {
3064  // Disallow assignment to non-'const' subaccesses of 'const' types
3065  if (destType != destRefinedType)
3066  return emitSubaccessError();
3067 
3068  if (failed(checkConstConditionality(dest, destType, destDeclaration)))
3069  return failure();
3070  }
3071 
3072  // Check source if it contains 'const' 'flip' leaves
3073  if (srcRefinedType.containsConst() &&
3074  isConstFieldDriven(srcRefinedType, /*isFlip=*/true)) {
3075  // Disallow assignment to non-'const' subaccesses of 'const' types
3076  if (srcType != srcRefinedType)
3077  return emitSubaccessError();
3078  if (failed(checkConstConditionality(src, srcType, srcDeclaration)))
3079  return failure();
3080  }
3081 
3082  return success();
3083 }
3084 
3085 LogicalResult ConnectOp::verify() {
3086  auto dstType = getDest().getType();
3087  auto srcType = getSrc().getType();
3088  auto dstBaseType = type_dyn_cast<FIRRTLBaseType>(dstType);
3089  auto srcBaseType = type_dyn_cast<FIRRTLBaseType>(srcType);
3090  if (!dstBaseType || !srcBaseType) {
3091  if (dstType != srcType)
3092  return emitError("may not connect different non-base types");
3093  } else {
3094  // Analog types cannot be connected and must be attached.
3095  if (dstBaseType.containsAnalog() || srcBaseType.containsAnalog())
3096  return emitError("analog types may not be connected");
3097 
3098  // Destination and source types must be equivalent.
3099  if (!areTypesEquivalent(dstBaseType, srcBaseType))
3100  return emitError("type mismatch between destination ")
3101  << dstBaseType << " and source " << srcBaseType;
3102 
3103  // Truncation is banned in a connection: destination bit width must be
3104  // greater than or equal to source bit width.
3105  if (!isTypeLarger(dstBaseType, srcBaseType))
3106  return emitError("destination ")
3107  << dstBaseType << " is not as wide as the source " << srcBaseType;
3108  }
3109 
3110  // Check that the flows make sense.
3111  if (failed(checkConnectFlow(*this)))
3112  return failure();
3113 
3114  if (failed(checkConnectConditionality(*this)))
3115  return failure();
3116 
3117  return success();
3118 }
3119 
3120 LogicalResult StrictConnectOp::verify() {
3121  if (auto type = type_dyn_cast<FIRRTLType>(getDest().getType())) {
3122  auto baseType = type_cast<FIRRTLBaseType>(type);
3123 
3124  // Analog types cannot be connected and must be attached.
3125  if (baseType && baseType.containsAnalog())
3126  return emitError("analog types may not be connected");
3127 
3128  // The anonymous types of operands must be equivalent.
3129  assert(areAnonymousTypesEquivalent(cast<FIRRTLBaseType>(getSrc().getType()),
3130  baseType) &&
3131  "`SameAnonTypeOperands` trait should have already rejected "
3132  "structurally non-equivalent types");
3133  }
3134 
3135  // Check that the flows make sense.
3136  if (failed(checkConnectFlow(*this)))
3137  return failure();
3138 
3139  if (failed(checkConnectConditionality(*this)))
3140  return failure();
3141 
3142  return success();
3143 }
3144 
3145 LogicalResult RefDefineOp::verify() {
3146  // Check that the flows make sense.
3147  if (failed(checkConnectFlow(*this)))
3148  return failure();
3149 
3150  // For now, refs can't be in bundles so this is sufficient.
3151  // In the future need to ensure no other define's to same "fieldSource".
3152  // (When aggregates can have references, we can define a reference within,
3153  // but this must be unique. Checking this here may be expensive,
3154  // consider adding something to FModuleLike's to check it there instead)
3155  for (auto *user : getDest().getUsers()) {
3156  if (auto conn = dyn_cast<FConnectLike>(user);
3157  conn && conn.getDest() == getDest() && conn != *this)
3158  return emitError("destination reference cannot be reused by multiple "
3159  "operations, it can only capture a unique dataflow");
3160  }
3161 
3162  // Check "static" source/dest
3163  if (auto *op = getDest().getDefiningOp()) {
3164  // TODO: Make ref.sub only source flow?
3165  if (isa<RefSubOp>(op))
3166  return emitError(
3167  "destination reference cannot be a sub-element of a reference");
3168  if (isa<RefCastOp>(op)) // Source flow, check anyway for now.
3169  return emitError(
3170  "destination reference cannot be a cast of another reference");
3171  }
3172 
3173  return success();
3174 }
3175 
3176 LogicalResult PropAssignOp::verify() {
3177  // Check that the flows make sense.
3178  if (failed(checkConnectFlow(*this)))
3179  return failure();
3180 
3181  // Verify that there is a single value driving the destination.
3182  for (auto *user : getDest().getUsers()) {
3183  if (auto conn = dyn_cast<FConnectLike>(user);
3184  conn && conn.getDest() == getDest() && conn != *this)
3185  return emitError("destination property cannot be reused by multiple "
3186  "operations, it can only capture a unique dataflow");
3187  }
3188 
3189  return success();
3190 }
3191 
3192 void WhenOp::createElseRegion() {
3193  assert(!hasElseRegion() && "already has an else region");
3194  getElseRegion().push_back(new Block());
3195 }
3196 
3197 void WhenOp::build(OpBuilder &builder, OperationState &result, Value condition,
3198  bool withElseRegion, std::function<void()> thenCtor,
3199  std::function<void()> elseCtor) {
3200  OpBuilder::InsertionGuard guard(builder);
3201  result.addOperands(condition);
3202 
3203  // Create "then" region.
3204  builder.createBlock(result.addRegion());
3205  if (thenCtor)
3206  thenCtor();
3207 
3208  // Create "else" region.
3209  Region *elseRegion = result.addRegion();
3210  if (withElseRegion) {
3211  builder.createBlock(elseRegion);
3212  if (elseCtor)
3213  elseCtor();
3214  }
3215 }
3216 
3217 //===----------------------------------------------------------------------===//
3218 // MatchOp
3219 //===----------------------------------------------------------------------===//
3220 
3221 LogicalResult MatchOp::verify() {
3222  FEnumType type = getInput().getType();
3223 
3224  // Make sure that the number of tags matches the number of regions.
3225  auto numCases = getTags().size();
3226  auto numRegions = getNumRegions();
3227  if (numRegions != numCases)
3228  return emitOpError("expected ")
3229  << numRegions << " tags but got " << numCases;
3230 
3231  auto numTags = type.getNumElements();
3232 
3233  SmallDenseSet<int64_t> seen;
3234  for (const auto &[tag, region] : llvm::zip(getTags(), getRegions())) {
3235  auto tagIndex = size_t(cast<IntegerAttr>(tag).getInt());
3236 
3237  // Ensure that the block has a single argument.
3238  if (region.getNumArguments() != 1)
3239  return emitOpError("region should have exactly one argument");
3240 
3241  // Make sure that it is a valid tag.
3242  if (tagIndex >= numTags)
3243  return emitOpError("the tag index ")
3244  << tagIndex << " is out of the range of valid tags in " << type;
3245 
3246  // Make sure we have not already matched this tag.
3247  auto [it, inserted] = seen.insert(tagIndex);
3248  if (!inserted)
3249  return emitOpError("the tag ") << type.getElementNameAttr(tagIndex)
3250  << " is matched more than once";
3251 
3252  // Check that the block argument type matches the tag's type.
3253  auto expectedType = type.getElementTypePreservingConst(tagIndex);
3254  auto regionType = region.getArgument(0).getType();
3255  if (regionType != expectedType)
3256  return emitOpError("region type ")
3257  << regionType << " does not match the expected type "
3258  << expectedType;
3259  }
3260 
3261  // Check that the match statement is exhaustive.
3262  for (size_t i = 0, e = type.getNumElements(); i < e; ++i)
3263  if (!seen.contains(i))
3264  return emitOpError("missing case for tag ") << type.getElementNameAttr(i);
3265 
3266  return success();
3267 }
3268 
3269 void MatchOp::print(OpAsmPrinter &p) {
3270  auto input = getInput();
3271  FEnumType type = input.getType();
3272  auto regions = getRegions();
3273  p << " " << input << " : " << type;
3274  SmallVector<StringRef> elided = {"tags"};
3275  p.printOptionalAttrDictWithKeyword((*this)->getAttrs(), elided);
3276  p << " {";
3277  p.increaseIndent();
3278  for (const auto &[tag, region] : llvm::zip(getTags(), regions)) {
3279  p.printNewline();
3280  p << "case ";
3281  p.printKeywordOrString(
3282  type.getElementName(cast<IntegerAttr>(tag).getInt()));
3283  p << "(";
3284  p.printRegionArgument(region.front().getArgument(0), /*attrs=*/{},
3285  /*omitType=*/true);
3286  p << ") ";
3287  p.printRegion(region, /*printEntryBlockArgs=*/false);
3288  }
3289  p.decreaseIndent();
3290  p.printNewline();
3291  p << "}";
3292 }
3293 
3294 ParseResult MatchOp::parse(OpAsmParser &parser, OperationState &result) {
3295  auto *context = parser.getContext();
3296  OpAsmParser::UnresolvedOperand input;
3297  if (parser.parseOperand(input) || parser.parseColon())
3298  return failure();
3299 
3300  auto loc = parser.getCurrentLocation();
3301  Type type;
3302  if (parser.parseType(type))
3303  return failure();
3304  auto enumType = type_dyn_cast<FEnumType>(type);
3305  if (!enumType)
3306  return parser.emitError(loc, "expected enumeration type but got") << type;
3307 
3308  if (parser.resolveOperand(input, type, result.operands) ||
3309  parser.parseOptionalAttrDictWithKeyword(result.attributes) ||
3310  parser.parseLBrace())
3311  return failure();
3312 
3313  auto i32Type = IntegerType::get(context, 32);
3314  SmallVector<Attribute> tags;
3315  while (true) {
3316  // Stop parsing when we don't find another "case" keyword.
3317  if (failed(parser.parseOptionalKeyword("case")))
3318  break;
3319 
3320  // Parse the tag and region argument.
3321  auto nameLoc = parser.getCurrentLocation();
3322  std::string name;
3323  OpAsmParser::Argument arg;
3324  auto *region = result.addRegion();
3325  if (parser.parseKeywordOrString(&name) || parser.parseLParen() ||
3326  parser.parseArgument(arg) || parser.parseRParen())
3327  return failure();
3328 
3329  // Figure out the enum index of the tag.
3330  auto index = enumType.getElementIndex(name);
3331  if (!index)
3332  return parser.emitError(nameLoc, "the tag \"")
3333  << name << "\" is not a member of the enumeration " << enumType;
3334  tags.push_back(IntegerAttr::get(i32Type, *index));
3335 
3336  // Parse the region.
3337  arg.type = enumType.getElementTypePreservingConst(*index);
3338  if (parser.parseRegion(*region, arg))
3339  return failure();
3340  }
3341  result.addAttribute("tags", ArrayAttr::get(context, tags));
3342 
3343  return parser.parseRBrace();
3344 }
3345 
3346 void MatchOp::build(OpBuilder &builder, OperationState &result, Value input,
3347  ArrayAttr tags,
3348  MutableArrayRef<std::unique_ptr<Region>> regions) {
3349  result.addOperands(input);
3350  result.addAttribute("tags", tags);
3351  result.addRegions(regions);
3352 }
3353 
3354 //===----------------------------------------------------------------------===//
3355 // Expressions
3356 //===----------------------------------------------------------------------===//
3357 
3358 /// Type inference adaptor that narrows from the very generic MLIR
3359 /// `InferTypeOpInterface` to what we need in the FIRRTL dialect: just operands
3360 /// and attributes, no context or regions. Also, we only ever produce a single
3361 /// result value, so the FIRRTL-specific type inference ops directly return the
3362 /// inferred type rather than pushing into the `results` vector.
3363 LogicalResult impl::inferReturnTypes(
3364  MLIRContext *context, std::optional<Location> loc, ValueRange operands,
3365  DictionaryAttr attrs, mlir::OpaqueProperties properties,
3366  RegionRange regions, SmallVectorImpl<Type> &results,
3367  llvm::function_ref<FIRRTLType(ValueRange, ArrayRef<NamedAttribute>,
3368  std::optional<Location>)>
3369  callback) {
3370  auto type = callback(
3371  operands, attrs ? attrs.getValue() : ArrayRef<NamedAttribute>{}, loc);
3372  if (type) {
3373  results.push_back(type);
3374  return success();
3375  }
3376  return failure();
3377 }
3378 
3379 /// Get an attribute by name from a list of named attributes. Aborts if the
3380 /// attribute does not exist.
3381 static Attribute getAttr(ArrayRef<NamedAttribute> attrs, StringRef name) {
3382  for (auto attr : attrs)
3383  if (attr.getName() == name)
3384  return attr.getValue();
3385  llvm::report_fatal_error("attribute '" + name + "' not found");
3386 }
3387 
3388 /// Same as above, but casts the attribute to a specific type.
3389 template <typename AttrClass>
3390 AttrClass getAttr(ArrayRef<NamedAttribute> attrs, StringRef name) {
3391  return cast<AttrClass>(getAttr(attrs, name));
3392 }
3393 
3394 /// Return true if the specified operation is a firrtl expression.
3395 bool firrtl::isExpression(Operation *op) {
3396  struct IsExprClassifier : public ExprVisitor<IsExprClassifier, bool> {
3397  bool visitInvalidExpr(Operation *op) { return false; }
3398  bool visitUnhandledExpr(Operation *op) { return true; }
3399  };
3400 
3401  return IsExprClassifier().dispatchExprVisitor(op);
3402 }
3403 
3405  // Set invalid values to have a distinct name.
3406  std::string name;
3407  if (auto ty = type_dyn_cast<IntType>(getType())) {
3408  const char *base = ty.isSigned() ? "invalid_si" : "invalid_ui";
3409  auto width = ty.getWidthOrSentinel();
3410  if (width == -1)
3411  name = base;
3412  else
3413  name = (Twine(base) + Twine(width)).str();
3414  } else if (auto ty = type_dyn_cast<AnalogType>(getType())) {
3415  auto width = ty.getWidthOrSentinel();
3416  if (width == -1)
3417  name = "invalid_analog";
3418  else
3419  name = ("invalid_analog" + Twine(width)).str();
3420  } else if (type_isa<AsyncResetType>(getType()))
3421  name = "invalid_asyncreset";
3422  else if (type_isa<ResetType>(getType()))
3423  name = "invalid_reset";
3424  else if (type_isa<ClockType>(getType()))
3425  name = "invalid_clock";
3426  else
3427  name = "invalid";
3428 
3429  setNameFn(getResult(), name);
3430 }
3431 
3432 void ConstantOp::print(OpAsmPrinter &p) {
3433  p << " ";
3434  p.printAttributeWithoutType(getValueAttr());
3435  p << " : ";
3436  p.printType(getType());
3437  p.printOptionalAttrDict((*this)->getAttrs(), /*elidedAttrs=*/{"value"});
3438 }
3439 
3440 ParseResult ConstantOp::parse(OpAsmParser &parser, OperationState &result) {
3441  // Parse the constant value, without knowing its width.
3442  APInt value;
3443  auto loc = parser.getCurrentLocation();
3444  auto valueResult = parser.parseOptionalInteger(value);
3445  if (!valueResult.has_value())
3446  return parser.emitError(loc, "expected integer value");
3447 
3448  // Parse the result firrtl integer type.
3449  IntType resultType;
3450  if (failed(*valueResult) || parser.parseColonType(resultType) ||
3451  parser.parseOptionalAttrDict(result.attributes))
3452  return failure();
3453  result.addTypes(resultType);
3454 
3455  // Now that we know the width and sign of the result type, we can munge the
3456  // APInt as appropriate.
3457  if (resultType.hasWidth()) {
3458  auto width = (unsigned)resultType.getWidthOrSentinel();
3459  if (width > value.getBitWidth()) {
3460  // sext is always safe here, even for unsigned values, because the
3461  // parseOptionalInteger method will return something with a zero in the
3462  // top bits if it is a positive number.
3463  value = value.sext(width);
3464  } else if (width < value.getBitWidth()) {
3465  // The parser can return an unnecessarily wide result with leading
3466  // zeros. This isn't a problem, but truncating off bits is bad.
3467  if (value.getNumSignBits() < value.getBitWidth() - width)
3468  return parser.emitError(loc, "constant too large for result type ")
3469  << resultType;
3470  value = value.trunc(width);
3471  }
3472  }
3473 
3474  auto intType = parser.getBuilder().getIntegerType(value.getBitWidth(),
3475  resultType.isSigned());
3476  auto valueAttr = parser.getBuilder().getIntegerAttr(intType, value);
3477  result.addAttribute("value", valueAttr);
3478  return success();
3479 }
3480 
3481 LogicalResult ConstantOp::verify() {
3482  // If the result type has a bitwidth, then the attribute must match its width.
3483  IntType intType = getType();
3484  auto width = intType.getWidthOrSentinel();
3485  if (width != -1 && (int)getValue().getBitWidth() != width)
3486  return emitError(
3487  "firrtl.constant attribute bitwidth doesn't match return type");
3488 
3489  // The sign of the attribute's integer type must match our integer type sign.
3490  auto attrType = type_cast<IntegerType>(getValueAttr().getType());
3491  if (attrType.isSignless() || attrType.isSigned() != intType.isSigned())
3492  return emitError("firrtl.constant attribute has wrong sign");
3493 
3494  return success();
3495 }
3496 
3497 /// Build a ConstantOp from an APInt and a FIRRTL type, handling the attribute
3498 /// formation for the 'value' attribute.
3499 void ConstantOp::build(OpBuilder &builder, OperationState &result, IntType type,
3500  const APInt &value) {
3501  int32_t width = type.getWidthOrSentinel();
3502  (void)width;
3503  assert((width == -1 || (int32_t)value.getBitWidth() == width) &&
3504  "incorrect attribute bitwidth for firrtl.constant");
3505 
3506  auto attr =
3507  IntegerAttr::get(type.getContext(), APSInt(value, !type.isSigned()));
3508  return build(builder, result, type, attr);
3509 }
3510 
3511 /// Build a ConstantOp from an APSInt, handling the attribute formation for the
3512 /// 'value' attribute and inferring the FIRRTL type.
3513 void ConstantOp::build(OpBuilder &builder, OperationState &result,
3514  const APSInt &value) {
3515  auto attr = IntegerAttr::get(builder.getContext(), value);
3516  auto type =
3517  IntType::get(builder.getContext(), value.isSigned(), value.getBitWidth());
3518  return build(builder, result, type, attr);
3519 }
3520 
3522  // For constants in particular, propagate the value into the result name to
3523  // make it easier to read the IR.
3524  IntType intTy = getType();
3525  assert(intTy);
3526 
3527  // Otherwise, build a complex name with the value and type.
3528  SmallString<32> specialNameBuffer;
3529  llvm::raw_svector_ostream specialName(specialNameBuffer);
3530  specialName << 'c';
3531  getValue().print(specialName, /*isSigned:*/ intTy.isSigned());
3532 
3533  specialName << (intTy.isSigned() ? "_si" : "_ui");
3534  auto width = intTy.getWidthOrSentinel();
3535  if (width != -1)
3536  specialName << width;
3537  setNameFn(getResult(), specialName.str());
3538 }
3539 
3540 void SpecialConstantOp::print(OpAsmPrinter &p) {
3541  p << " ";
3542  // SpecialConstant uses a BoolAttr, and we want to print `true` as `1`.
3543  p << static_cast<unsigned>(getValue());
3544  p << " : ";
3545  p.printType(getType());
3546  p.printOptionalAttrDict((*this)->getAttrs(), /*elidedAttrs=*/{"value"});
3547 }
3548 
3549 ParseResult SpecialConstantOp::parse(OpAsmParser &parser,
3550  OperationState &result) {
3551  // Parse the constant value. SpecialConstant uses bool attributes, but it
3552  // prints as an integer.
3553  APInt value;
3554  auto loc = parser.getCurrentLocation();
3555  auto valueResult = parser.parseOptionalInteger(value);
3556  if (!valueResult.has_value())
3557  return parser.emitError(loc, "expected integer value");
3558 
3559  // Clocks and resets can only be 0 or 1.
3560  if (value != 0 && value != 1)
3561  return parser.emitError(loc, "special constants can only be 0 or 1.");
3562 
3563  // Parse the result firrtl type.
3564  Type resultType;
3565  if (failed(*valueResult) || parser.parseColonType(resultType) ||
3566  parser.parseOptionalAttrDict(result.attributes))
3567  return failure();
3568  result.addTypes(resultType);
3569 
3570  // Create the attribute.
3571  auto valueAttr = parser.getBuilder().getBoolAttr(value == 1);
3572  result.addAttribute("value", valueAttr);
3573  return success();
3574 }
3575 
3577  SmallString<32> specialNameBuffer;
3578  llvm::raw_svector_ostream specialName(specialNameBuffer);
3579  specialName << 'c';
3580  specialName << static_cast<unsigned>(getValue());
3581  auto type = getType();
3582  if (type_isa<ClockType>(type)) {
3583  specialName << "_clock";
3584  } else if (type_isa<ResetType>(type)) {
3585  specialName << "_reset";
3586  } else if (type_isa<AsyncResetType>(type)) {
3587  specialName << "_asyncreset";
3588  }
3589  setNameFn(getResult(), specialName.str());
3590 }
3591 
3592 // Checks that an array attr representing an aggregate constant has the correct
3593 // shape. This recurses on the type.
3594 static bool checkAggConstant(Operation *op, Attribute attr,
3595  FIRRTLBaseType type) {
3596  if (type.isGround()) {
3597  if (!isa<IntegerAttr>(attr)) {
3598  op->emitOpError("Ground type is not an integer attribute");
3599  return false;
3600  }
3601  return true;
3602  }
3603  auto attrlist = dyn_cast<ArrayAttr>(attr);
3604  if (!attrlist) {
3605  op->emitOpError("expected array attribute for aggregate constant");
3606  return false;
3607  }
3608  if (auto array = type_dyn_cast<FVectorType>(type)) {
3609  if (array.getNumElements() != attrlist.size()) {
3610  op->emitOpError("array attribute (")
3611  << attrlist.size() << ") has wrong size for vector constant ("
3612  << array.getNumElements() << ")";
3613  return false;
3614  }
3615  return llvm::all_of(attrlist, [&array, op](Attribute attr) {
3616  return checkAggConstant(op, attr, array.getElementType());
3617  });
3618  }
3619  if (auto bundle = type_dyn_cast<BundleType>(type)) {
3620  if (bundle.getNumElements() != attrlist.size()) {
3621  op->emitOpError("array attribute (")
3622  << attrlist.size() << ") has wrong size for bundle constant ("
3623  << bundle.getNumElements() << ")";
3624  return false;
3625  }
3626  for (size_t i = 0; i < bundle.getNumElements(); ++i) {
3627  if (bundle.getElement(i).isFlip) {
3628  op->emitOpError("Cannot have constant bundle type with flip");
3629  return false;
3630  }
3631  if (!checkAggConstant(op, attrlist[i], bundle.getElement(i).type))
3632  return false;
3633  }
3634  return true;
3635  }
3636  op->emitOpError("Unknown aggregate type");
3637  return false;
3638 }
3639 
3640 LogicalResult AggregateConstantOp::verify() {
3641  if (checkAggConstant(getOperation(), getFields(), getType()))
3642  return success();
3643  return failure();
3644 }
3645 
3646 Attribute AggregateConstantOp::getAttributeFromFieldID(uint64_t fieldID) {
3647  FIRRTLBaseType type = getType();
3648  Attribute value = getFields();
3649  while (fieldID != 0) {
3650  if (auto bundle = type_dyn_cast<BundleType>(type)) {
3651  auto index = bundle.getIndexForFieldID(fieldID);
3652  fieldID -= bundle.getFieldID(index);
3653  type = bundle.getElementType(index);
3654  value = cast<ArrayAttr>(value)[index];
3655  } else {
3656  auto vector = type_cast<FVectorType>(type);
3657  auto index = vector.getIndexForFieldID(fieldID);
3658  fieldID -= vector.getFieldID(index);
3659  type = vector.getElementType();
3660  value = cast<ArrayAttr>(value)[index];
3661  }
3662  }
3663  return value;
3664 }
3665 
3666 void FIntegerConstantOp::print(OpAsmPrinter &p) {
3667  p << " ";
3668  p.printAttributeWithoutType(getValueAttr());
3669  p.printOptionalAttrDict((*this)->getAttrs(), /*elidedAttrs=*/{"value"});
3670 }
3671 
3672 ParseResult FIntegerConstantOp::parse(OpAsmParser &parser,
3673  OperationState &result) {
3674  auto *context = parser.getContext();
3675  APInt value;
3676  if (parser.parseInteger(value) ||
3677  parser.parseOptionalAttrDict(result.attributes))
3678  return failure();
3679  result.addTypes(FIntegerType::get(context));
3680  auto intType =
3681  IntegerType::get(context, value.getBitWidth(), IntegerType::Signed);
3682  auto valueAttr = parser.getBuilder().getIntegerAttr(intType, value);
3683  result.addAttribute("value", valueAttr);
3684  return success();
3685 }
3686 
3687 ParseResult ListCreateOp::parse(OpAsmParser &parser, OperationState &result) {
3688  llvm::SmallVector<OpAsmParser::UnresolvedOperand, 16> operands;
3689  ListType type;
3690 
3691  if (parser.parseOperandList(operands) ||
3692  parser.parseOptionalAttrDict(result.attributes) ||
3693  parser.parseColonType(type))
3694  return failure();
3695  result.addTypes(type);
3696 
3697  return parser.resolveOperands(operands, type.getElementType(),
3698  result.operands);
3699 }
3700 
3701 void ListCreateOp::print(OpAsmPrinter &p) {
3702  p << " ";
3703  p.printOperands(getElements());
3704  p.printOptionalAttrDict((*this)->getAttrs());
3705  p << " : " << getType();
3706 }
3707 
3708 LogicalResult ListCreateOp::verify() {
3709  if (getElements().empty())
3710  return success();
3711 
3712  auto elementType = getElements().front().getType();
3713  auto listElementType = getType().getElementType();
3714  if (elementType != listElementType)
3715  return emitOpError("has elements of type ")
3716  << elementType << " instead of " << listElementType;
3717 
3718  return success();
3719 }
3720 
3721 ParseResult MapCreateOp::parse(OpAsmParser &parser, OperationState &result) {
3722  llvm::SmallVector<OpAsmParser::UnresolvedOperand, 16> keys;
3723  llvm::SmallVector<OpAsmParser::UnresolvedOperand, 16> values;
3724 
3725  MapType type;
3726 
3727  auto parsePair = [&]() {
3728  OpAsmParser::UnresolvedOperand key, value;
3729  if (parser.parseOperand(key) || parser.parseArrow() ||
3730  parser.parseOperand(value))
3731  return failure();
3732  keys.push_back(key);
3733  values.push_back(value);
3734  return success();
3735  };
3736  if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::OptionalParen,
3737  parsePair) ||
3738  parser.parseOptionalAttrDict(result.attributes) ||
3739  parser.parseColonType(type))
3740  return failure();
3741  result.addTypes(type);
3742 
3743  if (parser.resolveOperands(keys, type.getKeyType(), result.operands) ||
3744  parser.resolveOperands(values, type.getValueType(), result.operands))
3745  return failure();
3746 
3747  return success();
3748 }
3749 
3750 void MapCreateOp::print(OpAsmPrinter &p) {
3751  p << " ";
3752  if (!getKeys().empty()) {
3753  p << "(";
3754  llvm::interleaveComma(llvm::zip_equal(getKeys(), getValues()), p,
3755  [&](auto pair) {
3756  auto &[key, value] = pair;
3757  p.printOperand(key);
3758  p << " -> ";
3759  p.printOperand(value);
3760  });
3761  p << ")";
3762  }
3763  p.printOptionalAttrDict((*this)->getAttrs());
3764  p << " : " << getType();
3765 }
3766 
3767 LogicalResult MapCreateOp::verify() {
3768  if (getKeys().empty())
3769  return success();
3770 
3771  if (!llvm::all_equal(getKeys().getTypes()))
3772  return emitOpError("has keys that are not all the same type");
3773  if (!llvm::all_equal(getValues().getTypes()))
3774  return emitOpError("has values that are not all the same type");
3775 
3776  if (getKeys().front().getType() != getType().getKeyType())
3777  return emitOpError("has unexpected key type ")
3778  << getKeys().front().getType() << " instead of map key type "
3779  << getType().getKeyType();
3780 
3781  if (getValues().front().getType() != getType().getValueType())
3782  return emitOpError("has unexpected value type ")
3783  << getValues().front().getType() << " instead of map value type "
3784  << getType().getValueType();
3785  return success();
3786 }
3787 
3788 LogicalResult BundleCreateOp::verify() {
3789  BundleType resultType = getType();
3790  if (resultType.getNumElements() != getFields().size())
3791  return emitOpError("number of fields doesn't match type");
3792  for (size_t i = 0; i < resultType.getNumElements(); ++i)
3793  if (!areTypesConstCastable(
3794  resultType.getElementTypePreservingConst(i),
3795  type_cast<FIRRTLBaseType>(getOperand(i).getType())))
3796  return emitOpError("type of element doesn't match bundle for field ")
3797  << resultType.getElement(i).name;
3798  // TODO: check flow
3799  return success();
3800 }
3801 
3802 LogicalResult VectorCreateOp::verify() {
3803  FVectorType resultType = getType();
3804  if (resultType.getNumElements() != getFields().size())
3805  return emitOpError("number of fields doesn't match type");
3806  auto elemTy = resultType.getElementTypePreservingConst();
3807  for (size_t i = 0; i < resultType.getNumElements(); ++i)
3808  if (!areTypesConstCastable(
3809  elemTy, type_cast<FIRRTLBaseType>(getOperand(i).getType())))
3810  return emitOpError("type of element doesn't match vector element");
3811  // TODO: check flow
3812  return success();
3813 }
3814 
3815 //===----------------------------------------------------------------------===//
3816 // FEnumCreateOp
3817 //===----------------------------------------------------------------------===//
3818 
3819 LogicalResult FEnumCreateOp::verify() {
3820  FEnumType resultType = getResult().getType();
3821  auto elementIndex = resultType.getElementIndex(getFieldName());
3822  if (!elementIndex)
3823  return emitOpError("label ")
3824  << getFieldName() << " is not a member of the enumeration type "
3825  << resultType;
3826  if (!areTypesConstCastable(
3827  resultType.getElementTypePreservingConst(*elementIndex),
3828  getInput().getType()))
3829  return emitOpError("type of element doesn't match enum element");
3830  return success();
3831 }
3832 
3833 void FEnumCreateOp::print(OpAsmPrinter &printer) {
3834  printer << ' ';
3835  printer.printKeywordOrString(getFieldName());
3836  printer << '(' << getInput() << ')';
3837  SmallVector<StringRef> elidedAttrs = {"fieldIndex"};
3838  printer.printOptionalAttrDictWithKeyword((*this)->getAttrs(), elidedAttrs);
3839  printer << " : ";
3840  printer.printFunctionalType(ArrayRef<Type>{getInput().getType()},
3841  ArrayRef<Type>{getResult().getType()});
3842 }
3843 
3844 ParseResult FEnumCreateOp::parse(OpAsmParser &parser, OperationState &result) {
3845  auto *context = parser.getContext();
3846 
3847  OpAsmParser::UnresolvedOperand input;
3848  std::string fieldName;
3849  mlir::FunctionType functionType;
3850  if (parser.parseKeywordOrString(&fieldName) || parser.parseLParen() ||
3851  parser.parseOperand(input) || parser.parseRParen() ||
3852  parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
3853  parser.parseType(functionType))
3854  return failure();
3855 
3856  if (functionType.getNumInputs() != 1)
3857  return parser.emitError(parser.getNameLoc(), "single input type required");
3858  if (functionType.getNumResults() != 1)
3859  return parser.emitError(parser.getNameLoc(), "single result type required");
3860 
3861  auto inputType = functionType.getInput(0);
3862  if (parser.resolveOperand(input, inputType, result.operands))
3863  return failure();
3864 
3865  auto outputType = functionType.getResult(0);
3866  auto enumType = type_dyn_cast<FEnumType>(outputType);
3867  if (!enumType)
3868  return parser.emitError(parser.getNameLoc(),
3869  "output must be enum type, got ")
3870  << outputType;
3871  auto fieldIndex = enumType.getElementIndex(fieldName);
3872  if (!fieldIndex)
3873  return parser.emitError(parser.getNameLoc(),
3874  "unknown field " + fieldName + " in enum type ")
3875  << enumType;
3876 
3877  result.addAttribute(
3878  "fieldIndex",
3879  IntegerAttr::get(IntegerType::get(context, 32), *fieldIndex));
3880 
3881  result.addTypes(enumType);
3882 
3883  return success();
3884 }
3885 
3886 //===----------------------------------------------------------------------===//
3887 // IsTagOp
3888 //===----------------------------------------------------------------------===//
3889 
3890 LogicalResult IsTagOp::verify() {
3891  if (getFieldIndex() >= getInput().getType().get().getNumElements())
3892  return emitOpError("element index is greater than the number of fields in "
3893  "the bundle type");
3894  return success();
3895 }
3896 
3897 void IsTagOp::print(::mlir::OpAsmPrinter &printer) {
3898  printer << ' ' << getInput() << ' ';
3899  printer.printKeywordOrString(getFieldName());
3900  SmallVector<::llvm::StringRef, 1> elidedAttrs = {"fieldIndex"};
3901  printer.printOptionalAttrDict((*this)->getAttrs(), elidedAttrs);
3902  printer << " : " << getInput().getType();
3903 }
3904 
3905 ParseResult IsTagOp::parse(OpAsmParser &parser, OperationState &result) {
3906  auto *context = parser.getContext();
3907 
3908  OpAsmParser::UnresolvedOperand input;
3909  std::string fieldName;
3910  Type inputType;
3911  if (parser.parseOperand(input) || parser.parseKeywordOrString(&fieldName) ||
3912  parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
3913  parser.parseType(inputType))
3914  return failure();
3915 
3916  if (parser.resolveOperand(input, inputType, result.operands))
3917  return failure();
3918 
3919  auto enumType = type_dyn_cast<FEnumType>(inputType);
3920  if (!enumType)
3921  return parser.emitError(parser.getNameLoc(),
3922  "input must be enum type, got ")
3923  << inputType;
3924  auto fieldIndex = enumType.getElementIndex(fieldName);
3925  if (!fieldIndex)
3926  return parser.emitError(parser.getNameLoc(),
3927  "unknown field " + fieldName + " in enum type ")
3928  << enumType;
3929 
3930  result.addAttribute(
3931  "fieldIndex",
3932  IntegerAttr::get(IntegerType::get(context, 32), *fieldIndex));
3933 
3934  result.addTypes(UIntType::get(context, 1, /*isConst=*/false));
3935 
3936  return success();
3937 }
3938 
3939 FIRRTLType IsTagOp::inferReturnType(ValueRange operands,
3940  ArrayRef<NamedAttribute> attrs,
3941  std::optional<Location> loc) {
3942  return UIntType::get(operands[0].getContext(), 1,
3943  isConst(operands[0].getType()));
3944 }
3945 
3946 template <typename OpTy>
3947 ParseResult parseSubfieldLikeOp(OpAsmParser &parser, OperationState &result) {
3948  auto *context = parser.getContext();
3949 
3950  OpAsmParser::UnresolvedOperand input;
3951  std::string fieldName;
3952  Type inputType;
3953  if (parser.parseOperand(input) || parser.parseLSquare() ||
3954  parser.parseKeywordOrString(&fieldName) || parser.parseRSquare() ||
3955  parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
3956  parser.parseType(inputType))
3957  return failure();
3958 
3959  if (parser.resolveOperand(input, inputType, result.operands))
3960  return failure();
3961 
3962  auto bundleType = type_dyn_cast<typename OpTy::InputType>(inputType);
3963  if (!bundleType)
3964  return parser.emitError(parser.getNameLoc(),
3965  "input must be bundle type, got ")
3966  << inputType;
3967  auto fieldIndex = bundleType.getElementIndex(fieldName);
3968  if (!fieldIndex)
3969  return parser.emitError(parser.getNameLoc(),
3970  "unknown field " + fieldName + " in bundle type ")
3971  << bundleType;
3972 
3973  result.addAttribute(
3974  "fieldIndex",
3975  IntegerAttr::get(IntegerType::get(context, 32), *fieldIndex));
3976 
3977  SmallVector<Type> inferredReturnTypes;
3978  if (failed(OpTy::inferReturnTypes(context, result.location, result.operands,
3979  result.attributes.getDictionary(context),
3980  result.getRawProperties(), result.regions,
3981  inferredReturnTypes)))
3982  return failure();
3983  result.addTypes(inferredReturnTypes);
3984 
3985  return success();
3986 }
3987 
3988 ParseResult SubtagOp::parse(OpAsmParser &parser, OperationState &result) {
3989  auto *context = parser.getContext();
3990 
3991  OpAsmParser::UnresolvedOperand input;
3992  std::string fieldName;
3993  Type inputType;
3994  if (parser.parseOperand(input) || parser.parseLSquare() ||
3995  parser.parseKeywordOrString(&fieldName) || parser.parseRSquare() ||
3996  parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
3997  parser.parseType(inputType))
3998  return failure();
3999 
4000  if (parser.resolveOperand(input, inputType, result.operands))
4001  return failure();
4002 
4003  auto enumType = type_dyn_cast<FEnumType>(inputType);
4004  if (!enumType)
4005  return parser.emitError(parser.getNameLoc(),
4006  "input must be enum type, got ")
4007  << inputType;
4008  auto fieldIndex = enumType.getElementIndex(fieldName);
4009  if (!fieldIndex)
4010  return parser.emitError(parser.getNameLoc(),
4011  "unknown field " + fieldName + " in enum type ")
4012  << enumType;
4013 
4014  result.addAttribute(
4015  "fieldIndex",
4016  IntegerAttr::get(IntegerType::get(context, 32), *fieldIndex));
4017 
4018  SmallVector<Type> inferredReturnTypes;
4019  if (failed(SubtagOp::inferReturnTypes(
4020  context, result.location, result.operands,
4021  result.attributes.getDictionary(context), result.getRawProperties(),
4022  result.regions, inferredReturnTypes)))
4023  return failure();
4024  result.addTypes(inferredReturnTypes);
4025 
4026  return success();
4027 }
4028 
4029 ParseResult SubfieldOp::parse(OpAsmParser &parser, OperationState &result) {
4030  return parseSubfieldLikeOp<SubfieldOp>(parser, result);
4031 }
4032 ParseResult OpenSubfieldOp::parse(OpAsmParser &parser, OperationState &result) {
4033  return parseSubfieldLikeOp<OpenSubfieldOp>(parser, result);
4034 }
4035 
4036 template <typename OpTy>
4037 static void printSubfieldLikeOp(OpTy op, ::mlir::OpAsmPrinter &printer) {
4038  printer << ' ' << op.getInput() << '[';
4039  printer.printKeywordOrString(op.getFieldName());
4040  printer << ']';
4041  ::llvm::SmallVector<::llvm::StringRef, 2> elidedAttrs;
4042  elidedAttrs.push_back("fieldIndex");
4043  printer.printOptionalAttrDict(op->getAttrs(), elidedAttrs);
4044  printer << " : " << op.getInput().getType();
4045 }
4046 void SubfieldOp::print(::mlir::OpAsmPrinter &printer) {
4047  return printSubfieldLikeOp<SubfieldOp>(*this, printer);
4048 }
4049 void OpenSubfieldOp::print(::mlir::OpAsmPrinter &printer) {
4050  return printSubfieldLikeOp<OpenSubfieldOp>(*this, printer);
4051 }
4052 
4053 void SubtagOp::print(::mlir::OpAsmPrinter &printer) {
4054  printer << ' ' << getInput() << '[';
4055  printer.printKeywordOrString(getFieldName());
4056  printer << ']';
4057  ::llvm::SmallVector<::llvm::StringRef, 2> elidedAttrs;
4058  elidedAttrs.push_back("fieldIndex");
4059  printer.printOptionalAttrDict((*this)->getAttrs(), elidedAttrs);
4060  printer << " : " << getInput().getType();
4061 }
4062 
4063 template <typename OpTy>
4064 static LogicalResult verifySubfieldLike(OpTy op) {
4065  if (op.getFieldIndex() >=
4066  firrtl::type_cast<typename OpTy::InputType>(op.getInput().getType())
4067  .getNumElements())
4068  return op.emitOpError("subfield element index is greater than the number "
4069  "of fields in the bundle type");
4070  return success();
4071 }
4072 LogicalResult SubfieldOp::verify() {
4073  return verifySubfieldLike<SubfieldOp>(*this);
4074 }
4075 LogicalResult OpenSubfieldOp::verify() {
4076  return verifySubfieldLike<OpenSubfieldOp>(*this);
4077 }
4078 
4079 LogicalResult SubtagOp::verify() {
4080  if (getFieldIndex() >= getInput().getType().get().getNumElements())
4081  return emitOpError("subfield element index is greater than the number "
4082  "of fields in the bundle type");
4083  return success();
4084 }
4085 
4086 /// Return true if the specified operation has a constant value. This trivially
4087 /// checks for `firrtl.constant` and friends, but also looks through subaccesses
4088 /// and correctly handles wires driven with only constant values.
4089 bool firrtl::isConstant(Operation *op) {
4090  // Worklist of ops that need to be examined that should all be constant in
4091  // order for the input operation to be constant.
4092  SmallVector<Operation *, 8> worklist({op});
4093 
4094  // Mutable state indicating if this op is a constant. Assume it is a constant
4095  // and look for counterexamples.
4096  bool constant = true;
4097 
4098  // While we haven't found a counterexample and there are still ops in the
4099  // worklist, pull ops off the worklist. If it provides a counterexample, set
4100  // the `constant` to false (and exit on the next loop iteration). Otherwise,
4101  // look through the op or spawn off more ops to look at.
4102  while (constant && !(worklist.empty()))
4103  TypeSwitch<Operation *>(worklist.pop_back_val())
4104  .Case<NodeOp, AsSIntPrimOp, AsUIntPrimOp>([&](auto op) {
4105  if (auto definingOp = op.getInput().getDefiningOp())
4106  worklist.push_back(definingOp);
4107  constant = false;
4108  })
4109  .Case<WireOp, SubindexOp, SubfieldOp>([&](auto op) {
4110  for (auto &use : op.getResult().getUses())
4111  worklist.push_back(use.getOwner());
4112  })
4113  .Case<ConstantOp, SpecialConstantOp, AggregateConstantOp>([](auto) {})
4114  .Default([&](auto) { constant = false; });
4115 
4116  return constant;
4117 }
4118 
4119 /// Return true if the specified value is a constant. This trivially checks for
4120 /// `firrtl.constant` and friends, but also looks through subaccesses and
4121 /// correctly handles wires driven with only constant values.
4122 bool firrtl::isConstant(Value value) {
4123  if (auto *op = value.getDefiningOp())
4124  return isConstant(op);
4125  return false;
4126 }
4127 
4128 LogicalResult ConstCastOp::verify() {
4129  if (!areTypesConstCastable(getResult().getType(), getInput().getType()))
4130  return emitOpError() << getInput().getType()
4131  << " is not 'const'-castable to "
4132  << getResult().getType();
4133  return success();
4134 }
4135 
4136 FIRRTLType SubfieldOp::inferReturnType(ValueRange operands,
4137  ArrayRef<NamedAttribute> attrs,
4138  std::optional<Location> loc) {
4139  auto inType = type_cast<BundleType>(operands[0].getType());
4140  auto fieldIndex =
4141  getAttr<IntegerAttr>(attrs, "fieldIndex").getValue().getZExtValue();
4142 
4143  if (fieldIndex >= inType.getNumElements())
4144  return emitInferRetTypeError(loc,
4145  "subfield element index is greater than the "
4146  "number of fields in the bundle type");
4147 
4148  // SubfieldOp verifier checks that the field index is valid with number of
4149  // subelements.
4150  return inType.getElementTypePreservingConst(fieldIndex);
4151 }
4152 
4153 FIRRTLType OpenSubfieldOp::inferReturnType(ValueRange operands,
4154  ArrayRef<NamedAttribute> attrs,
4155  std::optional<Location> loc) {
4156  auto inType = type_cast<OpenBundleType>(operands[0].getType());
4157  auto fieldIndex =
4158  getAttr<IntegerAttr>(attrs, "fieldIndex").getValue().getZExtValue();
4159 
4160  if (fieldIndex >= inType.getNumElements())
4161  return emitInferRetTypeError(loc,
4162  "subfield element index is greater than the "
4163  "number of fields in the bundle type");
4164 
4165  // OpenSubfieldOp verifier checks that the field index is valid with number of
4166  // subelements.
4167  return inType.getElementTypePreservingConst(fieldIndex);
4168 }
4169 
4170 bool SubfieldOp::isFieldFlipped() {
4171  BundleType bundle = getInput().getType();
4172  return bundle.getElement(getFieldIndex()).isFlip;
4173 }
4174 bool OpenSubfieldOp::isFieldFlipped() {
4175  auto bundle = getInput().getType();
4176  return bundle.getElement(getFieldIndex()).isFlip;
4177 }
4178 
4179 FIRRTLType SubindexOp::inferReturnType(ValueRange operands,
4180  ArrayRef<NamedAttribute> attrs,
4181  std::optional<Location> loc) {
4182  Type inType = operands[0].getType();
4183  auto fieldIdx =
4184  getAttr<IntegerAttr>(attrs, "index").getValue().getZExtValue();
4185 
4186  if (auto vectorType = type_dyn_cast<FVectorType>(inType)) {
4187  if (fieldIdx < vectorType.getNumElements())
4188  return vectorType.getElementTypePreservingConst();
4189  return emitInferRetTypeError(loc, "out of range index '", fieldIdx,
4190  "' in vector type ", inType);
4191  }
4192 
4193  return emitInferRetTypeError(loc, "subindex requires vector operand");
4194 }
4195 
4196 FIRRTLType OpenSubindexOp::inferReturnType(ValueRange operands,
4197  ArrayRef<NamedAttribute> attrs,
4198  std::optional<Location> loc) {
4199  Type inType = operands[0].getType();
4200  auto fieldIdx =
4201  getAttr<IntegerAttr>(attrs, "index").getValue().getZExtValue();
4202 
4203  if (auto vectorType = type_dyn_cast<OpenVectorType>(inType)) {
4204  if (fieldIdx < vectorType.getNumElements())
4205  return vectorType.getElementTypePreservingConst();
4206  return emitInferRetTypeError(loc, "out of range index '", fieldIdx,
4207  "' in vector type ", inType);
4208  }
4209 
4210  return emitInferRetTypeError(loc, "subindex requires vector operand");
4211 }
4212 
4213 FIRRTLType SubtagOp::inferReturnType(ValueRange operands,
4214  ArrayRef<NamedAttribute> attrs,
4215  std::optional<Location> loc) {
4216  auto inType = type_cast<FEnumType>(operands[0].getType());
4217  auto fieldIndex =
4218  getAttr<IntegerAttr>(attrs, "fieldIndex").getValue().getZExtValue();
4219 
4220  if (fieldIndex >= inType.getNumElements())
4221  return emitInferRetTypeError(loc,
4222  "subtag element index is greater than the "
4223  "number of fields in the enum type");
4224 
4225  // SubtagOp verifier checks that the field index is valid with number of
4226  // subelements.
4227  auto elementType = inType.getElement(fieldIndex).type;
4228  return elementType.getConstType(elementType.isConst() || inType.isConst());
4229 }
4230 
4231 FIRRTLType SubaccessOp::inferReturnType(ValueRange operands,
4232  ArrayRef<NamedAttribute> attrs,
4233  std::optional<Location> loc) {
4234  auto inType = operands[0].getType();
4235  auto indexType = operands[1].getType();
4236 
4237  if (!type_isa<UIntType>(indexType))
4238  return emitInferRetTypeError(loc, "subaccess index must be UInt type, not ",
4239  indexType);
4240 
4241  if (auto vectorType = type_dyn_cast<FVectorType>(inType)) {
4242  if (isConst(indexType))
4243  return vectorType.getElementTypePreservingConst();
4244  return vectorType.getElementType().getAllConstDroppedType();
4245  }
4246 
4247  return emitInferRetTypeError(loc, "subaccess requires vector operand, not ",
4248  inType);
4249 }
4250 
4251 FIRRTLType TagExtractOp::inferReturnType(ValueRange operands,
4252  ArrayRef<NamedAttribute> attrs,
4253  std::optional<Location> loc) {
4254  auto inType = type_cast<FEnumType>(operands[0].getType());
4255  auto i = llvm::Log2_32_Ceil(inType.getNumElements());
4256  return UIntType::get(inType.getContext(), i);
4257 }
4258 
4259 ParseResult MultibitMuxOp::parse(OpAsmParser &parser, OperationState &result) {
4260  OpAsmParser::UnresolvedOperand index;
4261  SmallVector<OpAsmParser::UnresolvedOperand, 16> inputs;
4262  Type indexType, elemType;
4263 
4264  if (parser.parseOperand(index) || parser.parseComma() ||
4265  parser.parseOperandList(inputs) ||
4266  parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
4267  parser.parseType(indexType) || parser.parseComma() ||
4268  parser.parseType(elemType))
4269  return failure();
4270 
4271  if (parser.resolveOperand(index, indexType, result.operands))
4272  return failure();
4273 
4274  result.addTypes(elemType);
4275 
4276  return parser.resolveOperands(inputs, elemType, result.operands);
4277 }
4278 
4279 void MultibitMuxOp::print(OpAsmPrinter &p) {
4280  p << " " << getIndex() << ", ";
4281  p.printOperands(getInputs());
4282  p.printOptionalAttrDict((*this)->getAttrs());
4283  p << " : " << getIndex().getType() << ", " << getType();
4284 }
4285 
4286 FIRRTLType MultibitMuxOp::inferReturnType(ValueRange operands,
4287  ArrayRef<NamedAttribute> attrs,
4288  std::optional<Location> loc) {
4289  if (operands.size() < 2)
4290  return emitInferRetTypeError(loc, "at least one input is required");
4291 
4292  // Check all mux inputs have the same type.
4293  if (!llvm::all_of(operands.drop_front(2), [&](auto op) {
4294  return operands[1].getType() == op.getType();
4295  }))
4296  return emitInferRetTypeError(loc, "all inputs must have the same type");
4297 
4298  return type_cast<FIRRTLType>(operands[1].getType());
4299 }
4300 
4301 //===----------------------------------------------------------------------===//
4302 // ObjectSubfieldOp
4303 //===----------------------------------------------------------------------===//
4304 
4306  MLIRContext *context, std::optional<mlir::Location> location,
4307  ValueRange operands, DictionaryAttr attributes, OpaqueProperties properties,
4308  RegionRange regions, llvm::SmallVectorImpl<Type> &inferredReturnTypes) {
4309  auto type = inferReturnType(operands, attributes.getValue(), location);
4310  if (!type)
4311  return failure();
4312  inferredReturnTypes.push_back(type);
4313  return success();
4314 }
4315 
4316 Type ObjectSubfieldOp::inferReturnType(ValueRange operands,
4317  ArrayRef<NamedAttribute> attrs,
4318  std::optional<Location> loc) {
4319  auto classType = dyn_cast<ClassType>(operands[0].getType());
4320  if (!classType)
4321  return emitInferRetTypeError(loc, "base object is not a class");
4322 
4323  auto index = getAttr<IntegerAttr>(attrs, "index").getValue().getZExtValue();
4324  if (classType.getNumElements() <= index)
4325  return emitInferRetTypeError(loc, "element index is greater than the "
4326  "number of fields in the object");
4327 
4328  return classType.getElement(index).type;
4329 }
4330 
4331 void ObjectSubfieldOp::print(OpAsmPrinter &p) {
4332  auto input = getInput();
4333  auto classType = input.getType();
4334  p << ' ' << input << "[";
4335  p.printKeywordOrString(classType.getElement(getIndex()).name);
4336  p << "]";
4337  p.printOptionalAttrDict((*this)->getAttrs(), std::array{StringRef("index")});
4338  p << " : " << classType;
4339 }
4340 
4341 ParseResult ObjectSubfieldOp::parse(OpAsmParser &parser,
4342  OperationState &result) {
4343  auto *context = parser.getContext();
4344 
4345  OpAsmParser::UnresolvedOperand input;
4346  std::string fieldName;
4347  ClassType inputType;
4348  if (parser.parseOperand(input) || parser.parseLSquare() ||
4349  parser.parseKeywordOrString(&fieldName) || parser.parseRSquare() ||
4350  parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
4351  parser.parseType(inputType) ||
4352  parser.resolveOperand(input, inputType, result.operands))
4353  return failure();
4354 
4355  auto index = inputType.getElementIndex(fieldName);
4356  if (!index)
4357  return parser.emitError(parser.getNameLoc(),
4358  "unknown field " + fieldName + " in class type ")
4359  << inputType;
4360  result.addAttribute("index",
4361  IntegerAttr::get(IntegerType::get(context, 32), *index));
4362 
4363  SmallVector<Type> inferredReturnTypes;
4364  if (failed(inferReturnTypes(context, result.location, result.operands,
4365  result.attributes.getDictionary(context),
4366  result.getRawProperties(), result.regions,
4367  inferredReturnTypes)))
4368  return failure();
4369  result.addTypes(inferredReturnTypes);
4370 
4371  return success();
4372 }
4373 
4374 //===----------------------------------------------------------------------===//
4375 // Binary Primitives
4376 //===----------------------------------------------------------------------===//
4377 
4378 /// If LHS and RHS are both UInt or SInt types, the return true and fill in the
4379 /// width of them if known. If unknown, return -1 for the widths.
4380 /// The constness of the result is also returned, where if both lhs and rhs are
4381 /// const, then the result is const.
4382 ///
4383 /// On failure, this reports and error and returns false. This function should
4384 /// not be used if you don't want an error reported.
4385 static bool isSameIntTypeKind(Type lhs, Type rhs, int32_t &lhsWidth,
4386  int32_t &rhsWidth, bool &isConstResult,
4387  std::optional<Location> loc) {
4388  // Must have two integer types with the same signedness.
4389  auto lhsi = type_dyn_cast<IntType>(lhs);
4390  auto rhsi = type_dyn_cast<IntType>(rhs);
4391  if (!lhsi || !rhsi || lhsi.isSigned() != rhsi.isSigned()) {
4392  if (loc) {
4393  if (lhsi && !rhsi)
4394  mlir::emitError(*loc, "second operand must be an integer type, not ")
4395  << rhs;
4396  else if (!lhsi && rhsi)
4397  mlir::emitError(*loc, "first operand must be an integer type, not ")
4398  << lhs;
4399  else if (!lhsi && !rhsi)
4400  mlir::emitError(*loc, "operands must be integer types, not ")
4401  << lhs << " and " << rhs;
4402  else
4403  mlir::emitError(*loc, "operand signedness must match");
4404  }
4405  return false;
4406  }
4407 
4408  lhsWidth = lhsi.getWidthOrSentinel();
4409  rhsWidth = rhsi.getWidthOrSentinel();
4410  isConstResult = lhsi.isConst() && rhsi.isConst();
4411  return true;
4412 }
4413 
4414 LogicalResult impl::verifySameOperandsIntTypeKind(Operation *op) {
4415  assert(op->getNumOperands() == 2 &&
4416  "SameOperandsIntTypeKind on non-binary op");
4417  int32_t lhsWidth, rhsWidth;
4418  bool isConstResult;
4419  return success(isSameIntTypeKind(op->getOperand(0).getType(),
4420  op->getOperand(1).getType(), lhsWidth,
4421  rhsWidth, isConstResult, op->getLoc()));
4422 }
4423 
4424 LogicalResult impl::validateBinaryOpArguments(ValueRange operands,
4425  ArrayRef<NamedAttribute> attrs,
4426  Location loc) {
4427  if (operands.size() != 2 || !attrs.empty()) {
4428  mlir::emitError(loc, "operation requires two operands and no constants");
4429  return failure();
4430  }
4431  return success();
4432 }
4433 
4435  std::optional<Location> loc) {
4436  int32_t lhsWidth, rhsWidth, resultWidth = -1;
4437  bool isConstResult = false;
4438  if (!isSameIntTypeKind(lhs, rhs, lhsWidth, rhsWidth, isConstResult, loc))
4439  return {};
4440 
4441  if (lhsWidth != -1 && rhsWidth != -1)
4442  resultWidth = std::max(lhsWidth, rhsWidth) + 1;
4443  return IntType::get(lhs.getContext(), type_isa<SIntType>(lhs), resultWidth,
4444  isConstResult);
4445 }
4446 
4447 FIRRTLType MulPrimOp::inferBinaryReturnType(FIRRTLType lhs, FIRRTLType rhs,
4448  std::optional<Location> loc) {
4449  int32_t lhsWidth, rhsWidth, resultWidth = -1;
4450  bool isConstResult = false;
4451  if (!isSameIntTypeKind(lhs, rhs, lhsWidth, rhsWidth, isConstResult, loc))
4452  return {};
4453 
4454  if (lhsWidth != -1 && rhsWidth != -1)
4455  resultWidth = lhsWidth + rhsWidth;
4456 
4457  return IntType::get(lhs.getContext(), type_isa<SIntType>(lhs), resultWidth,
4458  isConstResult);
4459 }
4460 
4461 FIRRTLType DivPrimOp::inferBinaryReturnType(FIRRTLType lhs, FIRRTLType rhs,
4462  std::optional<Location> loc) {
4463  int32_t lhsWidth, rhsWidth;
4464  bool isConstResult = false;
4465  if (!isSameIntTypeKind(lhs, rhs, lhsWidth, rhsWidth, isConstResult, loc))
4466  return {};
4467 
4468  // For unsigned, the width is the width of the numerator on the LHS.
4469  if (type_isa<UIntType>(lhs))
4470  return UIntType::get(lhs.getContext(), lhsWidth, isConstResult);
4471 
4472  // For signed, the width is the width of the numerator on the LHS, plus 1.
4473  int32_t resultWidth = lhsWidth != -1 ? lhsWidth + 1 : -1;
4474  return SIntType::get(lhs.getContext(), resultWidth, isConstResult);
4475 }
4476 
4477 FIRRTLType RemPrimOp::inferBinaryReturnType(FIRRTLType lhs, FIRRTLType rhs,
4478  std::optional<Location> loc) {
4479  int32_t lhsWidth, rhsWidth, resultWidth = -1;
4480  bool isConstResult = false;
4481  if (!isSameIntTypeKind(lhs, rhs, lhsWidth, rhsWidth, isConstResult, loc))
4482  return {};
4483 
4484  if (lhsWidth != -1 && rhsWidth != -1)
4485  resultWidth = std::min(lhsWidth, rhsWidth);
4486  return IntType::get(lhs.getContext(), type_isa<SIntType>(lhs), resultWidth,
4487  isConstResult);
4488 }
4489 
4491  std::optional<Location> loc) {
4492  int32_t lhsWidth, rhsWidth, resultWidth = -1;
4493  bool isConstResult = false;
4494  if (!isSameIntTypeKind(lhs, rhs, lhsWidth, rhsWidth, isConstResult, loc))
4495  return {};
4496 
4497  if (lhsWidth != -1 && rhsWidth != -1)
4498  resultWidth = std::max(lhsWidth, rhsWidth);
4499  return UIntType::get(lhs.getContext(), resultWidth, isConstResult);
4500 }
4501 
4503  std::optional<Location> loc) {
4504  if (!type_isa<FVectorType>(lhs) || !type_isa<FVectorType>(rhs))
4505  return {};
4506 
4507  auto lhsVec = type_cast<FVectorType>(lhs);
4508  auto rhsVec = type_cast<FVectorType>(rhs);
4509 
4510  if (lhsVec.getNumElements() != rhsVec.getNumElements())
4511  return {};
4512 
4513  auto elemType =
4514  impl::inferBitwiseResult(lhsVec.getElementTypePreservingConst(),
4515  rhsVec.getElementTypePreservingConst(), loc);
4516  if (!elemType)
4517  return {};
4518  auto elemBaseType = type_cast<FIRRTLBaseType>(elemType);
4519  return FVectorType::get(elemBaseType, lhsVec.getNumElements(),
4520  lhsVec.isConst() && rhsVec.isConst() &&
4521  elemBaseType.isConst());
4522 }
4523 
4525  std::optional<Location> loc) {
4526  return UIntType::get(lhs.getContext(), 1, isConst(lhs) && isConst(rhs));
4527 }
4528 
4529 FIRRTLType CatPrimOp::inferBinaryReturnType(FIRRTLType lhs, FIRRTLType rhs,
4530  std::optional<Location> loc) {
4531  int32_t lhsWidth, rhsWidth, resultWidth = -1;
4532  bool isConstResult = false;
4533  if (!isSameIntTypeKind(lhs, rhs, lhsWidth, rhsWidth, isConstResult, loc))
4534  return {};
4535 
4536  if (lhsWidth != -1 && rhsWidth != -1)
4537  resultWidth = lhsWidth + rhsWidth;
4538  return UIntType::get(lhs.getContext(), resultWidth, isConstResult);
4539 }
4540 
4541 FIRRTLType DShlPrimOp::inferBinaryReturnType(FIRRTLType lhs, FIRRTLType rhs,
4542  std::optional<Location> loc) {
4543  auto lhsi = type_dyn_cast<IntType>(lhs);
4544  auto rhsui = type_dyn_cast<UIntType>(rhs);
4545  if (!rhsui || !lhsi)
4546  return emitInferRetTypeError(
4547  loc, "first operand should be integer, second unsigned int");
4548 
4549  // If the left or right has unknown result type, then the operation does
4550  // too.
4551  auto width = lhsi.getWidthOrSentinel();
4552  if (width == -1 || !rhsui.getWidth().has_value()) {
4553  width = -1;
4554  } else {
4555  auto amount = *rhsui.getWidth();
4556  if (amount >= 32)
4557  return emitInferRetTypeError(loc,
4558  "shift amount too large: second operand of "
4559  "dshl is wider than 31 bits");
4560  int64_t newWidth = (int64_t)width + ((int64_t)1 << amount) - 1;
4561  if (newWidth > INT32_MAX)
4562  return emitInferRetTypeError(
4563  loc, "shift amount too large: first operand shifted by maximum "
4564  "amount exceeds maximum width");
4565  width = newWidth;
4566  }
4567  return IntType::get(lhs.getContext(), lhsi.isSigned(), width,
4568  lhsi.isConst() && rhsui.isConst());
4569 }
4570 
4571 FIRRTLType DShlwPrimOp::inferBinaryReturnType(FIRRTLType lhs, FIRRTLType rhs,
4572  std::optional<Location> loc) {
4573  auto lhsi = type_dyn_cast<IntType>(lhs);
4574  auto rhsu = type_dyn_cast<UIntType>(rhs);
4575  if (!lhsi || !rhsu)
4576  return emitInferRetTypeError(
4577  loc, "first operand should be integer, second unsigned int");
4578  return lhsi.getConstType(lhsi.isConst() && rhsu.isConst());
4579 }
4580 
4581 FIRRTLType DShrPrimOp::inferBinaryReturnType(FIRRTLType lhs, FIRRTLType rhs,
4582  std::optional<Location> loc) {
4583  auto lhsi = type_dyn_cast<IntType>(lhs);
4584  auto rhsu = type_dyn_cast<UIntType>(rhs);
4585  if (!lhsi || !rhsu)
4586  return emitInferRetTypeError(
4587  loc, "first operand should be integer, second unsigned int");
4588  return lhsi.getConstType(lhsi.isConst() && rhsu.isConst());
4589 }
4590 
4591 //===----------------------------------------------------------------------===//
4592 // Unary Primitives
4593 //===----------------------------------------------------------------------===//
4594 
4595 LogicalResult impl::validateUnaryOpArguments(ValueRange operands,
4596  ArrayRef<NamedAttribute> attrs,
4597  Location loc) {
4598  if (operands.size() != 1 || !attrs.empty()) {
4599  mlir::emitError(loc, "operation requires one operand and no constants");
4600  return failure();
4601  }
4602  return success();
4603 }
4604 
4605 FIRRTLType
4606 SizeOfIntrinsicOp::inferUnaryReturnType(FIRRTLType input,
4607  std::optional<Location> loc) {
4608  return UIntType::get(input.getContext(), 32);
4609 }
4610 
4611 FIRRTLType AsSIntPrimOp::inferUnaryReturnType(FIRRTLType input,
4612  std::optional<Location> loc) {
4613  auto base = type_dyn_cast<FIRRTLBaseType>(input);
4614  if (!base)
4615  return emitInferRetTypeError(loc, "operand must be a scalar base type");
4616  int32_t width = base.getBitWidthOrSentinel();
4617  if (width == -2)
4618  return emitInferRetTypeError(loc, "operand must be a scalar type");
4619  return SIntType::get(input.getContext(), width, base.isConst());
4620 }
4621 
4622 FIRRTLType AsUIntPrimOp::inferUnaryReturnType(FIRRTLType input,
4623  std::optional<Location> loc) {
4624  auto base = type_dyn_cast<FIRRTLBaseType>(input);
4625  if (!base)
4626  return emitInferRetTypeError(loc, "operand must be a scalar base type");
4627  int32_t width = base.getBitWidthOrSentinel();
4628  if (width == -2)
4629  return emitInferRetTypeError(loc, "operand must be a scalar type");
4630  return UIntType::get(input.getContext(), width, base.isConst());
4631 }
4632 
4633 FIRRTLType
4634 AsAsyncResetPrimOp::inferUnaryReturnType(FIRRTLType input,
4635  std::optional<Location> loc) {
4636  auto base = type_dyn_cast<FIRRTLBaseType>(input);
4637  if (!base)
4638  return emitInferRetTypeError(loc,
4639  "operand must be single bit scalar base type");
4640  int32_t width = base.getBitWidthOrSentinel();
4641  if (width == -2 || width == 0 || width > 1)
4642  return emitInferRetTypeError(loc, "operand must be single bit scalar type");
4643  return AsyncResetType::get(input.getContext(), base.isConst());
4644 }
4645 
4646 FIRRTLType AsClockPrimOp::inferUnaryReturnType(FIRRTLType input,
4647  std::optional<Location> loc) {
4648  return ClockType::get(input.getContext(), isConst(input));
4649 }
4650 
4651 FIRRTLType CvtPrimOp::inferUnaryReturnType(FIRRTLType input,
4652  std::optional<Location> loc) {
4653  if (auto uiType = type_dyn_cast<UIntType>(input)) {
4654  auto width = uiType.getWidthOrSentinel();
4655  if (width != -1)
4656  ++width;
4657  return SIntType::get(input.getContext(), width, uiType.isConst());
4658  }
4659 
4660  if (type_isa<SIntType>(input))
4661  return input;
4662 
4663  return emitInferRetTypeError(loc, "operand must have integer type");
4664 }
4665 
4666 FIRRTLType NegPrimOp::inferUnaryReturnType(FIRRTLType input,
4667  std::optional<Location> loc) {
4668  auto inputi = type_dyn_cast<IntType>(input);
4669  if (!inputi)
4670  return emitInferRetTypeError(loc, "operand must have integer type");
4671  int32_t width = inputi.getWidthOrSentinel();
4672  if (width != -1)
4673  ++width;
4674  return SIntType::get(input.getContext(), width, inputi.isConst());
4675 }
4676 
4677 FIRRTLType NotPrimOp::inferUnaryReturnType(FIRRTLType input,
4678  std::optional<Location> loc) {
4679  auto inputi = type_dyn_cast<IntType>(input);
4680  if (!inputi)
4681  return emitInferRetTypeError(loc, "operand must have integer type");
4682  return UIntType::get(input.getContext(), inputi.getWidthOrSentinel(),
4683  inputi.isConst());
4684 }
4685 
4687  std::optional<Location> loc) {
4688  return UIntType::get(input.getContext(), 1, isConst(input));
4689 }
4690 
4691 //===----------------------------------------------------------------------===//
4692 // Other Operations
4693 //===----------------------------------------------------------------------===//
4694 
4695 LogicalResult BitsPrimOp::validateArguments(ValueRange operands,
4696  ArrayRef<NamedAttribute> attrs,
4697  Location loc) {
4698  if (operands.size() != 1 || attrs.size() != 2) {
4699  mlir::emitError(loc, "operation requires one operand and two constants");
4700  return failure();
4701  }
4702  return success();
4703 }
4704 
4705 FIRRTLType BitsPrimOp::inferReturnType(ValueRange operands,
4706  ArrayRef<NamedAttribute> attrs,
4707  std::optional<Location> loc) {
4708  auto input = operands[0].getType();
4709  auto high = getAttr<IntegerAttr>(attrs, "hi").getValue().getSExtValue();
4710  auto low = getAttr<IntegerAttr>(attrs, "lo").getValue().getSExtValue();
4711 
4712  auto inputi = type_dyn_cast<IntType>(input);
4713  if (!inputi)
4714  return emitInferRetTypeError(
4715  loc, "input type should be the int type but got ", input);
4716 
4717  // High must be >= low and both most be non-negative.
4718  if (high < low)
4719  return emitInferRetTypeError(
4720  loc, "high must be equal or greater than low, but got high = ", high,
4721  ", low = ", low);
4722 
4723  if (low < 0)
4724  return emitInferRetTypeError(loc, "low must be non-negative but got ", low);
4725 
4726  // If the input has staticly known width, check it. Both and low must be
4727  // strictly less than width.
4728  int32_t width = inputi.getWidthOrSentinel();
4729  if (width != -1 && high >= width)
4730  return emitInferRetTypeError(
4731  loc,
4732  "high must be smaller than the width of input, but got high = ", high,
4733  ", width = ", width);
4734 
4735  return UIntType::get(input.getContext(), high - low + 1, inputi.isConst());
4736 }
4737 
4738 LogicalResult impl::validateOneOperandOneConst(ValueRange operands,
4739  ArrayRef<NamedAttribute> attrs,
4740  Location loc) {
4741  if (operands.size() != 1 || attrs.size() != 1) {
4742  mlir::emitError(loc, "operation requires one operand and one constant");
4743  return failure();
4744  }
4745  return success();
4746 }
4747 
4748 FIRRTLType HeadPrimOp::inferReturnType(ValueRange operands,
4749  ArrayRef<NamedAttribute> attrs,
4750  std::optional<Location> loc) {
4751  auto input = operands[0].getType();
4752  auto amount = getAttr<IntegerAttr>(attrs, "amount").getValue().getSExtValue();
4753 
4754  auto inputi = type_dyn_cast<IntType>(input);
4755  if (amount < 0 || !inputi)
4756  return emitInferRetTypeError(
4757  loc, "operand must have integer type and amount must be >= 0");
4758 
4759  int32_t width = inputi.getWidthOrSentinel();
4760  if (width != -1 && amount > width)
4761  return emitInferRetTypeError(loc, "amount larger than input width");
4762 
4763  return UIntType::get(input.getContext(), amount, inputi.isConst());
4764 }
4765 
4766 LogicalResult MuxPrimOp::validateArguments(ValueRange operands,
4767  ArrayRef<NamedAttribute> attrs,
4768  Location loc) {
4769  if (operands.size() != 3 || attrs.size() != 0) {
4770  mlir::emitError(loc, "operation requires three operands and no constants");
4771  return failure();
4772  }
4773  return success();
4774 }
4775 
4776 /// Infer the result type for a multiplexer given its two operand types, which
4777 /// may be aggregates.
4778 ///
4779 /// This essentially performs a pairwise comparison of fields and elements, as
4780 /// follows:
4781 /// - Identical operands inferred to their common type
4782 /// - Integer operands inferred to the larger one if both have a known width, a
4783 /// widthless integer otherwise.
4784 /// - Vectors inferred based on the element type.
4785 /// - Bundles inferred in a pairwise fashion based on the field types.
4787  FIRRTLBaseType low,
4788  bool isConstCondition,
4789  std::optional<Location> loc) {
4790  // If the types are identical we're done.
4791  if (high == low)
4792  return isConstCondition ? low : low.getAllConstDroppedType();
4793 
4794  // The base types need to be equivalent.
4795  if (high.getTypeID() != low.getTypeID())
4796  return emitInferRetTypeError<FIRRTLBaseType>(
4797  loc, "incompatible mux operand types, true value type: ", high,
4798  ", false value type: ", low);
4799 
4800  bool outerTypeIsConst = isConstCondition && low.isConst() && high.isConst();
4801 
4802  // Two different Int types can be compatible. If either has unknown width,
4803  // then return it. If both are known but different width, then return the
4804  // larger one.
4805  if (type_isa<IntType>(low)) {
4806  int32_t highWidth = high.getBitWidthOrSentinel();
4807  int32_t lowWidth = low.getBitWidthOrSentinel();
4808  if (lowWidth == -1)
4809  return low.getConstType(outerTypeIsConst);
4810  if (highWidth == -1)
4811  return high.getConstType(outerTypeIsConst);
4812  return (lowWidth > highWidth ? low : high).getConstType(outerTypeIsConst);
4813  }
4814 
4815  // Infer vector types by comparing the element types.
4816  auto highVector = type_dyn_cast<FVectorType>(high);
4817  auto lowVector = type_dyn_cast<FVectorType>(low);
4818  if (highVector && lowVector &&
4819  highVector.getNumElements() == lowVector.getNumElements()) {
4820  auto inner = inferMuxReturnType(highVector.getElementTypePreservingConst(),
4821  lowVector.getElementTypePreservingConst(),
4822  isConstCondition, loc);
4823  if (!inner)
4824  return {};
4825  return FVectorType::get(inner, lowVector.getNumElements(),
4826  outerTypeIsConst);
4827  }
4828 
4829  // Infer bundle types by inferring names in a pairwise fashion.
4830  auto highBundle = type_dyn_cast<BundleType>(high);
4831  auto lowBundle = type_dyn_cast<BundleType>(low);
4832  if (highBundle && lowBundle) {
4833  auto highElements = highBundle.getElements();
4834  auto lowElements = lowBundle.getElements();
4835  size_t numElements = highElements.size();
4836 
4837  SmallVector<BundleType::BundleElement> newElements;
4838  if (numElements == lowElements.size()) {
4839  bool failed = false;
4840  for (size_t i = 0; i < numElements; ++i) {
4841  if (highElements[i].name != lowElements[i].name ||
4842  highElements[i].isFlip != lowElements[i].isFlip) {
4843  failed = true;
4844  break;
4845  }
4846  auto element = highElements[i];
4847  element.type = inferMuxReturnType(
4848  highBundle.getElementTypePreservingConst(i),
4849  lowBundle.getElementTypePreservingConst(i), isConstCondition, loc);
4850  if (!element.type)
4851  return {};
4852  newElements.push_back(element);
4853  }
4854  if (!failed)
4855  return BundleType::get(low.getContext(), newElements, outerTypeIsConst);
4856  }
4857  return emitInferRetTypeError<FIRRTLBaseType>(
4858  loc, "incompatible mux operand bundle fields, true value type: ", high,
4859  ", false value type: ", low);
4860  }
4861 
4862  // If we arrive here the types of the two mux arms are fundamentally
4863  // incompatible.
4864  return emitInferRetTypeError<FIRRTLBaseType>(
4865  loc, "invalid mux operand types, true value type: ", high,
4866  ", false value type: ", low);
4867 }
4868 
4869 FIRRTLType MuxPrimOp::inferReturnType(ValueRange operands,
4870  ArrayRef<NamedAttribute> attrs,
4871  std::optional<Location> loc) {
4872  auto highType = type_dyn_cast<FIRRTLBaseType>(operands[1].getType());
4873  auto lowType = type_dyn_cast<FIRRTLBaseType>(operands[2].getType());
4874  if (!highType || !lowType)
4875  return emitInferRetTypeError(loc, "operands must be base type");
4876  return inferMuxReturnType(highType, lowType, isConst(operands[0].getType()),
4877  loc);
4878 }
4879 
4880 FIRRTLType Mux2CellIntrinsicOp::inferReturnType(ValueRange operands,
4881  ArrayRef<NamedAttribute> attrs,
4882  std::optional<Location> loc) {
4883  auto highType = type_dyn_cast<FIRRTLBaseType>(operands[1].getType());
4884  auto lowType = type_dyn_cast<FIRRTLBaseType>(operands[2].getType());
4885  if (!highType || !lowType)
4886  return emitInferRetTypeError(loc, "operands must be base type");
4887  return inferMuxReturnType(highType, lowType, isConst(operands[0].getType()),
4888  loc);
4889 }
4890 
4891 FIRRTLType Mux4CellIntrinsicOp::inferReturnType(ValueRange operands,
4892  ArrayRef<NamedAttribute> attrs,
4893  std::optional<Location> loc) {
4894  SmallVector<FIRRTLBaseType> types;
4895  FIRRTLBaseType result;
4896  for (unsigned i = 1; i < 5; i++) {
4897  types.push_back(type_dyn_cast<FIRRTLBaseType>(operands[i].getType()));
4898  if (!types.back())
4899  return emitInferRetTypeError(loc, "operands must be base type");
4900  if (result) {
4901  result = inferMuxReturnType(result, types.back(),
4902  isConst(operands[0].getType()), loc);
4903  if (!result)
4904  return result;
4905  } else {
4906  result = types.back();
4907  }
4908  }
4909  return result;
4910 }
4911 
4912 FIRRTLType PadPrimOp::inferReturnType(ValueRange operands,
4913  ArrayRef<NamedAttribute> attrs,
4914  std::optional<Location> loc) {
4915  auto input = operands[0].getType();
4916  auto amount = getAttr<IntegerAttr>(attrs, "amount").getValue().getSExtValue();
4917 
4918  auto inputi = type_dyn_cast<IntType>(input);
4919  if (amount < 0 || !inputi)
4920  return emitInferRetTypeError(
4921  loc, "pad input must be integer and amount must be >= 0");
4922 
4923  int32_t width = inputi.getWidthOrSentinel();
4924  if (width == -1)
4925  return inputi;
4926 
4927  width = std::max<int32_t>(width, amount);
4928  return IntType::get(input.getContext(), inputi.isSigned(), width,
4929  inputi.isConst());
4930 }
4931 
4932 FIRRTLType ShlPrimOp::inferReturnType(ValueRange operands,
4933  ArrayRef<NamedAttribute> attrs,
4934  std::optional<Location> loc) {
4935  auto input = operands[0].getType();
4936  auto amount = getAttr<IntegerAttr>(attrs, "amount").getValue().getSExtValue();
4937 
4938  auto inputi = type_dyn_cast<IntType>(input);
4939  if (amount < 0 || !inputi)
4940  return emitInferRetTypeError(
4941  loc, "shl input must be integer and amount must be >= 0");
4942 
4943  int32_t width = inputi.getWidthOrSentinel();
4944  if (width != -1)
4945  width += amount;
4946 
4947  return IntType::get(input.getContext(), inputi.isSigned(), width,
4948  inputi.isConst());
4949 }
4950 
4951 FIRRTLType ShrPrimOp::inferReturnType(ValueRange operands,
4952  ArrayRef<NamedAttribute> attrs,
4953  std::optional<Location> loc) {
4954  auto input = operands[0].getType();
4955  auto amount = getAttr<IntegerAttr>(attrs, "amount").getValue().getSExtValue();
4956 
4957  auto inputi = type_dyn_cast<IntType>(input);
4958  if (amount < 0 || !inputi)
4959  return emitInferRetTypeError(
4960  loc, "shr input must be integer and amount must be >= 0");
4961 
4962  int32_t width = inputi.getWidthOrSentinel();
4963  if (width != -1)
4964  width = std::max<int32_t>(1, width - amount);
4965 
4966  return IntType::get(input.getContext(), inputi.isSigned(), width,
4967  inputi.isConst());
4968 }
4969 
4970 FIRRTLType TailPrimOp::inferReturnType(ValueRange operands,
4971  ArrayRef<NamedAttribute> attrs,
4972  std::optional<Location> loc) {
4973  auto input = operands[0].getType();
4974  auto amount = getAttr<IntegerAttr>(attrs, "amount").getValue().getSExtValue();
4975 
4976  auto inputi = type_dyn_cast<IntType>(input);
4977  if (amount < 0 || !inputi)
4978  return emitInferRetTypeError(
4979  loc, "tail input must be integer and amount must be >= 0");
4980 
4981  int32_t width = inputi.getWidthOrSentinel();
4982  if (width != -1) {
4983  if (width < amount)
4984  return emitInferRetTypeError(
4985  loc, "amount must be less than or equal operand width");
4986  width -= amount;
4987  }
4988 
4989  return IntType::get(input.getContext(), false, width, inputi.isConst());
4990 }
4991 
4992 //===----------------------------------------------------------------------===//
4993 // VerbatimExprOp
4994 //===----------------------------------------------------------------------===//
4995 
4997  function_ref<void(Value, StringRef)> setNameFn) {
4998  // If the text is macro like, then use a pretty name. We only take the
4999  // text up to a weird character (like a paren) and currently ignore
5000  // parenthesized expressions.
5001  auto isOkCharacter = [](char c) { return llvm::isAlnum(c) || c == '_'; };
5002  auto name = getText();
5003  // Ignore a leading ` in macro name.
5004  if (name.startswith("`"))
5005  name = name.drop_front();
5006  name = name.take_while(isOkCharacter);
5007  if (!name.empty())
5008  setNameFn(getResult(), name);
5009 }
5010 
5011 //===----------------------------------------------------------------------===//
5012 // VerbatimWireOp
5013 //===----------------------------------------------------------------------===//
5014 
5016  function_ref<void(Value, StringRef)> setNameFn) {
5017  // If the text is macro like, then use a pretty name. We only take the
5018  // text up to a weird character (like a paren) and currently ignore
5019  // parenthesized expressions.
5020  auto isOkCharacter = [](char c) { return llvm::isAlnum(c) || c == '_'; };
5021  auto name = getText();
5022  // Ignore a leading ` in macro name.
5023  if (name.startswith("`"))
5024  name = name.drop_front();
5025  name = name.take_while(isOkCharacter);
5026  if (!name.empty())
5027  setNameFn(getResult(), name);
5028 }
5029 
5030 //===----------------------------------------------------------------------===//
5031 // Conversions to/from structs in the standard dialect.
5032 //===----------------------------------------------------------------------===//
5033 
5034 LogicalResult HWStructCastOp::verify() {
5035  // We must have a bundle and a struct, with matching pairwise fields
5036  BundleType bundleType;
5037  hw::StructType structType;
5038  if ((bundleType = type_dyn_cast<BundleType>(getOperand().getType()))) {
5039  structType = getType().dyn_cast<hw::StructType>();
5040  if (!structType)
5041  return emitError("result type must be a struct");
5042  } else if ((bundleType = type_dyn_cast<BundleType>(getType()))) {
5043  structType = getOperand().getType().dyn_cast<hw::StructType>();
5044  if (!structType)
5045  return emitError("operand type must be a struct");
5046  } else {
5047  return emitError("either source or result type must be a bundle type");
5048  }
5049 
5050  auto firFields = bundleType.getElements();
5051  auto hwFields = structType.getElements();
5052  if (firFields.size() != hwFields.size())
5053  return emitError("bundle and struct have different number of fields");
5054 
5055  for (size_t findex = 0, fend = firFields.size(); findex < fend; ++findex) {
5056  if (firFields[findex].name.getValue() != hwFields[findex].name)
5057  return emitError("field names don't match '")
5058  << firFields[findex].name.getValue() << "', '"
5059  << hwFields[findex].name.getValue() << "'";
5060  int64_t firWidth =
5061  FIRRTLBaseType(firFields[findex].type).getBitWidthOrSentinel();
5062  int64_t hwWidth = hw::getBitWidth(hwFields[findex].type);
5063  if (firWidth > 0 && hwWidth > 0 && firWidth != hwWidth)
5064  return emitError("size of field '")
5065  << hwFields[findex].name.getValue() << "' don't match " << firWidth
5066  << ", " << hwWidth;
5067  }
5068 
5069  return success();
5070 }
5071 
5072 LogicalResult BitCastOp::verify() {
5073  auto inTypeBits = getBitWidth(getInput().getType(), /*ignoreFlip=*/true);
5074  auto resTypeBits = getBitWidth(getType());
5075  if (inTypeBits.has_value() && resTypeBits.has_value()) {
5076  // Bitwidths must match for valid bit
5077  if (*inTypeBits == *resTypeBits) {
5078  // non-'const' cannot be casted to 'const'
5079  if (containsConst(getType()) && !isConst(getOperand().getType()))
5080  return emitError("cannot cast non-'const' input type ")
5081  << getOperand().getType() << " to 'const' result type "
5082  << getType();
5083  return success();
5084  }
5085  return emitError("the bitwidth of input (")
5086  << *inTypeBits << ") and result (" << *resTypeBits
5087  << ") don't match";
5088  }
5089  if (!inTypeBits.has_value())
5090  return emitError("bitwidth cannot be determined for input operand type ")
5091  << getInput().getType();
5092  return emitError("bitwidth cannot be determined for result type ")
5093  << getType();
5094 }
5095 
5096 //===----------------------------------------------------------------------===//
5097 // Custom attr-dict Directive that Elides Annotations
5098 //===----------------------------------------------------------------------===//
5099 
5100 /// Parse an optional attribute dictionary, adding an empty 'annotations'
5101 /// attribute if not specified.
5102 static ParseResult parseElideAnnotations(OpAsmParser &parser,
5103  NamedAttrList &resultAttrs) {
5104  auto result = parser.parseOptionalAttrDict(resultAttrs);
5105  if (!resultAttrs.get("annotations"))
5106  resultAttrs.append("annotations", parser.getBuilder().getArrayAttr({}));
5107 
5108  return result;
5109 }
5110 
5111 static void printElideAnnotations(OpAsmPrinter &p, Operation *op,
5112  DictionaryAttr attr,
5113  ArrayRef<StringRef> extraElides = {}) {
5114  SmallVector<StringRef> elidedAttrs(extraElides.begin(), extraElides.end());
5115  // Elide "annotations" if it is empty.
5116  if (op->getAttrOfType<ArrayAttr>("annotations").empty())
5117  elidedAttrs.push_back("annotations");
5118  // Elide "nameKind".
5119  elidedAttrs.push_back("nameKind");
5120 
5121  p.printOptionalAttrDict(op->getAttrs(), elidedAttrs);
5122 }
5123 
5124 /// Parse an optional attribute dictionary, adding empty 'annotations' and
5125 /// 'portAnnotations' attributes if not specified.
5126 static ParseResult parseElidePortAnnotations(OpAsmParser &parser,
5127  NamedAttrList &resultAttrs) {
5128  auto result = parseElideAnnotations(parser, resultAttrs);
5129 
5130  if (!resultAttrs.get("portAnnotations")) {
5131  SmallVector<Attribute, 16> portAnnotations(
5132  parser.getNumResults(), parser.getBuilder().getArrayAttr({}));
5133  resultAttrs.append("portAnnotations",
5134  parser.getBuilder().getArrayAttr(portAnnotations));
5135  }
5136  return result;
5137 }
5138 
5139 // Elide 'annotations' and 'portAnnotations' attributes if they are empty.
5140 static void printElidePortAnnotations(OpAsmPrinter &p, Operation *op,
5141  DictionaryAttr attr,
5142  ArrayRef<StringRef> extraElides = {}) {
5143  SmallVector<StringRef, 2> elidedAttrs(extraElides.begin(), extraElides.end());
5144 
5145  if (llvm::all_of(op->getAttrOfType<ArrayAttr>("portAnnotations"),
5146  [&](Attribute a) { return cast<ArrayAttr>(a).empty(); }))
5147  elidedAttrs.push_back("portAnnotations");
5148  printElideAnnotations(p, op, attr, elidedAttrs);
5149 }
5150 
5151 //===----------------------------------------------------------------------===//
5152 // NameKind Custom Directive
5153 //===----------------------------------------------------------------------===//
5154 
5155 static ParseResult parseNameKind(OpAsmParser &parser,
5156  firrtl::NameKindEnumAttr &result) {
5157  StringRef keyword;
5158 
5159  if (!parser.parseOptionalKeyword(&keyword,
5160  {"interesting_name", "droppable_name"})) {
5161  auto kind = symbolizeNameKindEnum(keyword);
5162  result = NameKindEnumAttr::get(parser.getContext(), kind.value());
5163  return success();
5164  }
5165 
5166  // Default is droppable name.
5167  result =
5168  NameKindEnumAttr::get(parser.getContext(), NameKindEnum::DroppableName);
5169  return success();
5170 }
5171 
5172 static void printNameKind(OpAsmPrinter &p, Operation *op,
5173  firrtl::NameKindEnumAttr attr,
5174  ArrayRef<StringRef> extraElides = {}) {
5175  if (attr.getValue() != NameKindEnum::DroppableName)
5176  p << " " << stringifyNameKindEnum(attr.getValue());
5177 }
5178 
5179 //===----------------------------------------------------------------------===//
5180 // ImplicitSSAName Custom Directive
5181 //===----------------------------------------------------------------------===//
5182 
5183 static ParseResult parseFIRRTLImplicitSSAName(OpAsmParser &parser,
5184  NamedAttrList &resultAttrs) {
5185  if (parseElideAnnotations(parser, resultAttrs))
5186  return failure();
5187  inferImplicitSSAName(parser, resultAttrs);
5188  return success();
5189 }
5190 
5191 static void printFIRRTLImplicitSSAName(OpAsmPrinter &p, Operation *op,
5192  DictionaryAttr attrs) {
5193  SmallVector<StringRef, 4> elides;
5195  elides.push_back(Forceable::getForceableAttrName());
5196  elideImplicitSSAName(p, op, attrs, elides);
5197  printElideAnnotations(p, op, attrs, elides);
5198 }
5199 
5200 //===----------------------------------------------------------------------===//
5201 // MemOp Custom attr-dict Directive
5202 //===----------------------------------------------------------------------===//
5203 
5204 static ParseResult parseMemOp(OpAsmParser &parser, NamedAttrList &resultAttrs) {
5205  return parseElidePortAnnotations(parser, resultAttrs);
5206 }
5207 
5208 /// Always elide "ruw" and elide "annotations" if it exists or if it is empty.
5209 static void printMemOp(OpAsmPrinter &p, Operation *op, DictionaryAttr attr) {
5210  // "ruw" and "inner_sym" is always elided.
5211  printElidePortAnnotations(p, op, attr, {"ruw", "inner_sym"});
5212 }
5213 
5214 //===----------------------------------------------------------------------===//
5215 // ClassInterface custom directive
5216 //===----------------------------------------------------------------------===//
5217 
5218 static ParseResult parseClassInterface(OpAsmParser &parser, Type &result) {
5219  ClassType type;
5220  if (ClassType::parseInterface(parser, type))
5221  return failure();
5222  result = type;
5223  return success();
5224 }
5225 
5226 static void printClassInterface(OpAsmPrinter &p, Operation *, ClassType type) {
5227  type.printInterface(p);
5228 }
5229 
5230 //===----------------------------------------------------------------------===//
5231 // Miscellaneous custom elision logic.
5232 //===----------------------------------------------------------------------===//
5233 
5234 static ParseResult parseElideEmptyName(OpAsmParser &p,
5235  NamedAttrList &resultAttrs) {
5236  auto result = p.parseOptionalAttrDict(resultAttrs);
5237  if (!resultAttrs.get("name"))
5238  resultAttrs.append("name", p.getBuilder().getStringAttr(""));
5239 
5240  return result;
5241 }
5242 
5243 static void printElideEmptyName(OpAsmPrinter &p, Operation *op,
5244  DictionaryAttr attr,
5245  ArrayRef<StringRef> extraElides = {}) {
5246  SmallVector<StringRef> elides(extraElides.begin(), extraElides.end());
5247  if (op->getAttrOfType<StringAttr>("name").getValue().empty())
5248  elides.push_back("name");
5249 
5250  p.printOptionalAttrDict(op->getAttrs(), elides);
5251 }
5252 
5253 static ParseResult parsePrintfAttrs(OpAsmParser &p,
5254  NamedAttrList &resultAttrs) {
5255  return parseElideEmptyName(p, resultAttrs);
5256 }
5257 
5258 static void printPrintfAttrs(OpAsmPrinter &p, Operation *op,
5259  DictionaryAttr attr) {
5260  printElideEmptyName(p, op, attr, {"formatString"});
5261 }
5262 
5263 static ParseResult parseStopAttrs(OpAsmParser &p, NamedAttrList &resultAttrs) {
5264  return parseElideEmptyName(p, resultAttrs);
5265 }
5266 
5267 static void printStopAttrs(OpAsmPrinter &p, Operation *op,
5268  DictionaryAttr attr) {
5269  printElideEmptyName(p, op, attr, {"exitCode"});
5270 }
5271 
5272 static ParseResult parseVerifAttrs(OpAsmParser &p, NamedAttrList &resultAttrs) {
5273  return parseElideEmptyName(p, resultAttrs);
5274 }
5275 
5276 static void printVerifAttrs(OpAsmPrinter &p, Operation *op,
5277  DictionaryAttr attr) {
5278  printElideEmptyName(p, op, attr, {"message"});
5279 }
5280 
5281 //===----------------------------------------------------------------------===//
5282 // Various namers.
5283 //===----------------------------------------------------------------------===//
5284 
5285 static void genericAsmResultNames(Operation *op,
5286  OpAsmSetValueNameFn setNameFn) {
5287  // Many firrtl dialect operations have an optional 'name' attribute. If
5288  // present, use it.
5289  if (op->getNumResults() == 1)
5290  if (auto nameAttr = op->getAttrOfType<StringAttr>("name"))
5291  setNameFn(op->getResult(0), nameAttr.getValue());
5292 }
5293 
5295  genericAsmResultNames(*this, setNameFn);
5296 }
5297 
5299  genericAsmResultNames(*this, setNameFn);
5300 }
5301 
5303  genericAsmResultNames(*this, setNameFn);
5304 }
5305 
5307  genericAsmResultNames(*this, setNameFn);
5308 }
5310  genericAsmResultNames(*this, setNameFn);
5311 }
5313  genericAsmResultNames(*this, setNameFn);
5314 }
5316  genericAsmResultNames(*this, setNameFn);
5317 }
5319  genericAsmResultNames(*this, setNameFn);
5320 }
5322  genericAsmResultNames(*this, setNameFn);
5323 }
5325  genericAsmResultNames(*this, setNameFn);
5326 }
5328  genericAsmResultNames(*this, setNameFn);
5329 }
5331  genericAsmResultNames(*this, setNameFn);
5332 }
5334  genericAsmResultNames(*this, setNameFn);
5335 }
5337  genericAsmResultNames(*this, setNameFn);
5338 }
5340  genericAsmResultNames(*this, setNameFn);
5341 }
5343  genericAsmResultNames(*this, setNameFn);
5344 }
5346  genericAsmResultNames(*this, setNameFn);
5347 }
5349  genericAsmResultNames(*this, setNameFn);
5350 }
5352  genericAsmResultNames(*this, setNameFn);
5353 }
5355  genericAsmResultNames(*this, setNameFn);
5356 }
5358  genericAsmResultNames(*this, setNameFn);
5359 }
5361  OpAsmSetValueNameFn setNameFn) {
5362  genericAsmResultNames(*this, setNameFn);
5363 }
5365  genericAsmResultNames(*this, setNameFn);
5366 }
5368  genericAsmResultNames(*this, setNameFn);
5369 }
5371  genericAsmResultNames(*this, setNameFn);
5372 }
5374  genericAsmResultNames(*this, setNameFn);
5375 }
5377  genericAsmResultNames(*this, setNameFn);
5378 }
5380  genericAsmResultNames(*this, setNameFn);
5381 }
5383  genericAsmResultNames(*this, setNameFn);
5384 }
5386  genericAsmResultNames(*this, setNameFn);
5387 }
5389  genericAsmResultNames(*this, setNameFn);
5390 }
5392  genericAsmResultNames(*this, setNameFn);
5393 }
5395  genericAsmResultNames(*this, setNameFn);
5396 }
5398  genericAsmResultNames(*this, setNameFn);
5399 }
5401  genericAsmResultNames(*this, setNameFn);
5402 }
5404  genericAsmResultNames(*this, setNameFn);
5405 }
5407  genericAsmResultNames(*this, setNameFn);
5408 }
5410  genericAsmResultNames(*this, setNameFn);
5411 }
5413  genericAsmResultNames(*this, setNameFn);
5414 }
5415 
5417  genericAsmResultNames(*this, setNameFn);
5418 }
5419 
5421  genericAsmResultNames(*this, setNameFn);
5422 }
5423 
5425  genericAsmResultNames(*this, setNameFn);
5426 }
5428  genericAsmResultNames(*this, setNameFn);
5429 }
5430 
5432  genericAsmResultNames(*this, setNameFn);
5433 }
5434 
5436  genericAsmResultNames(*this, setNameFn);
5437 }
5438 
5440  genericAsmResultNames(*this, setNameFn);
5441 }
5442 
5444  genericAsmResultNames(*this, setNameFn);
5445 }
5446 
5448  genericAsmResultNames(*this, setNameFn);
5449 }
5450 
5452  genericAsmResultNames(*this, setNameFn);
5453 }
5454 
5456  genericAsmResultNames(*this, setNameFn);
5457 }
5458 
5460  genericAsmResultNames(*this, setNameFn);
5461 }
5462 
5464  genericAsmResultNames(*this, setNameFn);
5465 }
5466 
5468  genericAsmResultNames(*this, setNameFn);
5469 }
5470 
5472  genericAsmResultNames(*this, setNameFn);
5473 }
5474 
5476  genericAsmResultNames(*this, setNameFn);
5477 }
5478 
5479 //===----------------------------------------------------------------------===//
5480 // RefOps
5481 //===----------------------------------------------------------------------===//
5482 
5484  genericAsmResultNames(*this, setNameFn);
5485 }
5486 
5488  genericAsmResultNames(*this, setNameFn);
5489 }
5490 
5492  genericAsmResultNames(*this, setNameFn);
5493 }
5494 
5496  genericAsmResultNames(*this, setNameFn);
5497 }
5498 
5500  genericAsmResultNames(*this, setNameFn);
5501 }
5502 
5503 FIRRTLType RefResolveOp::inferReturnType(ValueRange operands,
5504  ArrayRef<NamedAttribute> attrs,
5505  std::optional<Location> loc) {
5506  Type inType = operands[0].getType();
5507  auto inRefType = type_dyn_cast<RefType>(inType);
5508  if (!inRefType)
5509  return emitInferRetTypeError(
5510  loc, "ref.resolve operand must be ref type, not ", inType);
5511  return inRefType.getType();
5512 }
5513 
5514 FIRRTLType RefSendOp::inferReturnType(ValueRange operands,
5515  ArrayRef<NamedAttribute> attrs,
5516  std::optional<Location> loc) {
5517  Type inType = operands[0].getType();
5518  auto inBaseType = type_dyn_cast<FIRRTLBaseType>(inType);
5519  if (!inBaseType)
5520  return emitInferRetTypeError(
5521  loc, "ref.send operand must be base type, not ", inType);
5522  return RefType::get(inBaseType.getPassiveType());
5523 }
5524 
5525 FIRRTLType RefSubOp::inferReturnType(ValueRange operands,
5526  ArrayRef<NamedAttribute> attrs,
5527  std::optional<Location> loc) {
5528  auto refType = type_dyn_cast<RefType>(operands[0].getType());
5529  if (!refType)
5530  return emitInferRetTypeError(loc, "input must be of reference type");
5531  auto inType = refType.getType();
5532  auto fieldIdx =
5533  getAttr<IntegerAttr>(attrs, "index").getValue().getZExtValue();
5534 
5535  // TODO: Determine ref.sub + rwprobe behavior, test.
5536  // Probably best to demote to non-rw, but that has implications
5537  // for any LowerTypes behavior being relied on.
5538  // Allow for now, as need to LowerTypes things generally.
5539  if (auto vectorType = type_dyn_cast<FVectorType>(inType)) {
5540  if (fieldIdx < vectorType.getNumElements())
5541  return RefType::get(
5542  vectorType.getElementType().getConstType(
5543  vectorType.isConst() || vectorType.getElementType().isConst()),
5544  refType.getForceable());
5545  return emitInferRetTypeError(loc, "out of range index '", fieldIdx,
5546  "' in RefType of vector type ", refType);
5547  }
5548  if (auto bundleType = type_dyn_cast<BundleType>(inType)) {
5549  if (fieldIdx >= bundleType.getNumElements()) {
5550  return emitInferRetTypeError(loc,
5551  "subfield element index is greater than "
5552  "the number of fields in the bundle type");
5553  }
5554  return RefType::get(bundleType.getElement(fieldIdx).type.getConstType(
5555  bundleType.isConst() ||
5556  bundleType.getElement(fieldIdx).type.isConst()),
5557  refType.getForceable());
5558  }
5559 
5560  return emitInferRetTypeError(
5561  loc, "ref.sub op requires a RefType of vector or bundle base type");
5562 }
5563 
5564 LogicalResult RWProbeOp::verifyInnerRefs(hw::InnerRefNamespace &ns) {
5565  auto targetRef = getTarget();
5566  if (targetRef.getModule() !=
5567  (*this)->getParentOfType<FModuleLike>().getModuleNameAttr())
5568  return emitOpError() << "has non-local target";
5569 
5570  auto target = ns.lookup(targetRef);
5571  if (!target)
5572  return emitOpError() << "has target that cannot be resolved: " << targetRef;
5573 
5574  auto checkFinalType = [&](auto type, Location loc) -> LogicalResult {
5575  // Determine final type.
5576  mlir::Type fType =
5577  hw::FieldIdImpl::getFinalTypeByFieldID(type, target.getField());
5578  // Check.
5579  auto baseType = type_dyn_cast<FIRRTLBaseType>(fType);
5580  if (!baseType || baseType.getPassiveType() != getType().getType()) {
5581  auto diag = emitOpError("has type mismatch: target resolves to ")
5582  << fType << " instead of expected " << getType().getType();
5583  diag.attachNote(loc) << "target resolves here";
5584  return diag;
5585  }
5586  return success();
5587  };
5588  if (target.isPort()) {
5589  auto mod = cast<FModuleLike>(target.getOp());
5590  return checkFinalType(mod.getPortType(target.getPort()),
5591  mod.getPortLocation(target.getPort()));
5592  }
5593  hw::InnerSymbolOpInterface symOp =
5594  cast<hw::InnerSymbolOpInterface>(target.getOp());
5595  if (!symOp.getTargetResult())
5596  return emitOpError("has target that cannot be probed")
5597  .attachNote(symOp.getLoc())
5598  .append("target resolves here");
5599  auto *ancestor =
5600  symOp.getTargetResult().getParentBlock()->findAncestorOpInBlock(**this);
5601  if (!ancestor || !symOp->isBeforeInBlock(ancestor))
5602  return emitOpError("is not dominated by target")
5603  .attachNote(symOp.getLoc())
5604  .append("target here");
5605  return checkFinalType(symOp.getTargetResult().getType(), symOp.getLoc());
5606 }
5607 
5608 //===----------------------------------------------------------------------===//
5609 // Optional Group Operations
5610 //===----------------------------------------------------------------------===//
5611 
5612 LogicalResult GroupOp::verify() {
5613  auto groupName = getGroupName();
5614  auto *parentOp = (*this)->getParentOp();
5615 
5616  // Verify the correctness of the symbol reference. Only verify that this
5617  // group makes sense in its parent module or group.
5618  auto nestedReferences = groupName.getNestedReferences();
5619  if (nestedReferences.empty()) {
5620  if (!isa<FModuleOp>(parentOp)) {
5621  auto diag = emitOpError() << "has an un-nested group symbol, but does "
5622  "not have a 'firrtl.module' op as a parent";
5623  return diag.attachNote(parentOp->getLoc())
5624  << "illegal parent op defined here";
5625  }
5626  } else {
5627  auto parentGroup = dyn_cast<GroupOp>(parentOp);
5628  if (!parentGroup) {
5629  auto diag = emitOpError()
5630  << "has a nested group symbol, but does not have a '"
5631  << getOperationName() << "' op as a parent'";
5632  return diag.attachNote(parentOp->getLoc())
5633  << "illegal parent op defined here";
5634  }
5635  auto parentGroupName = parentGroup.getGroupName();
5636  if (parentGroupName.getRootReference() != groupName.getRootReference() ||
5637  parentGroupName.getNestedReferences() !=
5638  groupName.getNestedReferences().drop_back()) {
5639  auto diag = emitOpError() << "is nested under an illegal group";
5640  return diag.attachNote(parentGroup->getLoc())
5641  << "illegal parent group defined here";
5642  }
5643  }
5644 
5645  // Verify the body of the region.
5646  Block *body = getBody(0);
5647  bool failed = false;
5648  body->walk<mlir::WalkOrder::PreOrder>([&](Operation *op) {
5649  // Skip nested groups. Those will be verified separately.
5650  if (isa<GroupOp>(op))
5651  return WalkResult::skip();
5652  // Check all the operands of each op to make sure that only legal things are
5653  // captured.
5654  for (auto operand : op->getOperands()) {
5655  // Any value captured from the current group is fine.
5656  if (operand.getParentBlock() == body)
5657  continue;
5658  // Capture of a non-base type, e.g., reference is illegal.
5659  FIRRTLBaseType baseType = dyn_cast<FIRRTLBaseType>(operand.getType());
5660  if (!baseType) {
5661  auto diag = emitOpError()
5662  << "captures an operand which is not a FIRRTL base type";
5663  diag.attachNote(operand.getLoc()) << "operand is defined here";
5664  diag.attachNote(op->getLoc()) << "operand is used here";
5665  failed = true;
5666  return WalkResult::advance();
5667  }
5668  // Capturing a non-passive type is illegal.
5669  if (!baseType.isPassive()) {
5670  auto diag = emitOpError()
5671  << "captures an operand which is not a passive type";
5672  diag.attachNote(operand.getLoc()) << "operand is defined here";
5673  diag.attachNote(op->getLoc()) << "operand is used here";
5674  failed = true;
5675  return WalkResult::advance();
5676  }
5677  }
5678  // Ensure that the group does not drive any sinks.
5679  if (auto connect = dyn_cast<FConnectLike>(op)) {
5680  auto dest = getFieldRefFromValue(connect.getDest()).getValue();
5681  if (dest.getParentBlock() == body)
5682  return WalkResult::advance();
5683  auto diag = connect.emitOpError()
5684  << "connects to a destination which is defined outside its "
5685  "enclosing group";
5686  diag.attachNote(getLoc()) << "enclosing group is defined here";
5687  diag.attachNote(dest.getLoc()) << "destination is defined here";
5688  failed = true;
5689  }
5690  return WalkResult::advance();
5691  });
5692  if (failed)
5693  return failure();
5694 
5695  return success();
5696 }
5697 
5698 LogicalResult GroupOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
5699  auto groupDeclOp = symbolTable.lookupNearestSymbolFrom<GroupDeclOp>(
5700  *this, getGroupNameAttr());
5701  if (!groupDeclOp) {
5702  return emitOpError("invalid symbol reference");
5703  }
5704 
5705  return success();
5706 }
5707 
5708 //===----------------------------------------------------------------------===//
5709 // TblGen Generated Logic.
5710 //===----------------------------------------------------------------------===//
5711 
5712 // Provide the autogenerated implementation guts for the Op classes.
5713 #define GET_OP_CLASSES
5714 #include "circt/Dialect/FIRRTL/FIRRTL.cpp.inc"
assert(baseType &&"element must be base type")
MlirType uint64_t numElements
Definition: CHIRRTL.cpp:26
MlirType elementType
Definition: CHIRRTL.cpp:25
#define isdigit(x)
Definition: FIRLexer.cpp:26
static SmallVector< T > removeElementsAtIndices(ArrayRef< T > input, const llvm::BitVector &indicesToDrop)
Remove elements from the input array corresponding to set bits in indicesToDrop, returning the elemen...
Definition: FIRRTLOps.cpp:51
static void printStopAttrs(OpAsmPrinter &p, Operation *op, DictionaryAttr attr)
Definition: FIRRTLOps.cpp:5267
ParseResult parseSubfieldLikeOp(OpAsmParser &parser, OperationState &result)
Definition: FIRRTLOps.cpp:3947
static bool isSameIntTypeKind(Type lhs, Type rhs, int32_t &lhsWidth, int32_t &rhsWidth, bool &isConstResult, std::optional< Location > loc)
If LHS and RHS are both UInt or SInt types, the return true and fill in the width of them if known.
Definition: FIRRTLOps.cpp:4385
static LogicalResult verifySubfieldLike(OpTy op)
Definition: FIRRTLOps.cpp:4064
static void eraseInternalPaths(T op, const llvm::BitVector &portIndices)
Definition: FIRRTLOps.cpp:711
static bool isConstFieldDriven(FIRRTLBaseType type, bool isFlip=false, bool outerTypeIsConst=false)
Checks if the type has any 'const' leaf elements .
Definition: FIRRTLOps.cpp:2969
static hw::ModulePortInfo getPortListImpl(FModuleLike module)
Definition: FIRRTLOps.cpp:535
static ParseResult parsePrintfAttrs(OpAsmParser &p, NamedAttrList &resultAttrs)
Definition: FIRRTLOps.cpp:5253
static RetTy emitInferRetTypeError(std::optional< Location > loc, const Twine &message, Args &&...args)
Emit an error if optional location is non-null, return null of return type.
Definition: FIRRTLOps.cpp:90
static void buildModule(OpBuilder &builder, OperationState &result, StringAttr name, ArrayRef< PortInfo > ports, ArrayAttr annotations, bool withAnnotations=true)
Definition: FIRRTLOps.cpp:782
static ParseResult parseFModuleLikeOp(OpAsmParser &parser, OperationState &result, bool hasSSAIdentifiers)
Definition: FIRRTLOps.cpp:1210
static bool printModulePorts(OpAsmPrinter &p, Block *block, ArrayRef< Direction > portDirections, ArrayRef< Attribute > portNames, ArrayRef< Attribute > portTypes, ArrayRef< Attribute > portAnnotations, ArrayRef< Attribute > portSyms, ArrayRef< Attribute > portLocs)
Print a list of module ports in the following form: in x: !firrtl.uint<1> [{class = "DontTouch}],...
Definition: FIRRTLOps.cpp:919
static LogicalResult checkConnectConditionality(FConnectLike connect)
Checks that connections to 'const' destinations are not dependent on non-'const' conditions in when b...
Definition: FIRRTLOps.cpp:2993
static void erasePorts(FModuleLike op, const llvm::BitVector &portIndices)
Erases the ports that have their corresponding bit set in portIndices.
Definition: FIRRTLOps.cpp:670
static ParseResult parseModulePorts(OpAsmParser &parser, bool hasSSAIdentifiers, bool supportsSymbols, SmallVectorImpl< OpAsmParser::Argument > &entryArgs, SmallVectorImpl< Direction > &portDirections, SmallVectorImpl< Attribute > &portNames, SmallVectorImpl< Attribute > &portTypes, SmallVectorImpl< Attribute > &portAnnotations, SmallVectorImpl< Attribute > &portSyms, SmallVectorImpl< Attribute > &portLocs)
Parse a list of module ports.
Definition: FIRRTLOps.cpp:996
static ParseResult parseClassInterface(OpAsmParser &parser, Type &result)
Definition: FIRRTLOps.cpp:5218
static void printElidePortAnnotations(OpAsmPrinter &p, Operation *op, DictionaryAttr attr, ArrayRef< StringRef > extraElides={})
Definition: FIRRTLOps.cpp:5140
static void printNameKind(OpAsmPrinter &p, Operation *op, firrtl::NameKindEnumAttr attr, ArrayRef< StringRef > extraElides={})
Definition: FIRRTLOps.cpp:5172
static ParseResult parseStopAttrs(OpAsmParser &p, NamedAttrList &resultAttrs)
Definition: FIRRTLOps.cpp:5263
static ParseResult parseNameKind(OpAsmParser &parser, firrtl::NameKindEnumAttr &result)
A forward declaration for NameKind attribute parser.
Definition: FIRRTLOps.cpp:5155
static void printParameterList(ArrayAttr parameters, OpAsmPrinter &p)
Print a paramter list for a module or instance.
Definition: FIRRTLOps.cpp:1098
static size_t getAddressWidth(size_t depth)
Definition: FIRRTLOps.cpp:2521
static void forceableAsmResultNames(Forceable op, StringRef name, OpAsmSetValueNameFn setNameFn)
Helper for naming forceable declarations (and their optional ref result).
Definition: FIRRTLOps.cpp:2774
static void printSubfieldLikeOp(OpTy op, ::mlir::OpAsmPrinter &printer)
Definition: FIRRTLOps.cpp:4037
static bool checkAggConstant(Operation *op, Attribute attr, FIRRTLBaseType type)
Definition: FIRRTLOps.cpp:3594
static void printClassLike(OpAsmPrinter &p, ClassLike op)
Definition: FIRRTLOps.cpp:1659
static hw::ModulePort::Direction dirFtoH(Direction dir)
Definition: FIRRTLOps.cpp:526
static MemOp::PortKind getMemPortKindFromType(FIRRTLType type)
Return the kind of port this is given the port type from a 'mem' decl.
Definition: FIRRTLOps.cpp:115
static LogicalResult verifyInternalPaths(FModuleLike op, std::optional<::mlir::ArrayAttr > internalPaths)
Definition: FIRRTLOps.cpp:1360
static void genericAsmResultNames(Operation *op, OpAsmSetValueNameFn setNameFn)
Definition: FIRRTLOps.cpp:5285
static void printClassInterface(OpAsmPrinter &p, Operation *, ClassType type)
Definition: FIRRTLOps.cpp:5226
static void printPrintfAttrs(OpAsmPrinter &p, Operation *op, DictionaryAttr attr)
Definition: FIRRTLOps.cpp:5258
static void insertPorts(FModuleLike op, ArrayRef< std::pair< unsigned, PortInfo >> ports, bool supportsInternalPaths=false)
Inserts the given ports.
Definition: FIRRTLOps.cpp:570
static void printFIRRTLImplicitSSAName(OpAsmPrinter &p, Operation *op, DictionaryAttr attrs)
Definition: FIRRTLOps.cpp:5191
static ParseResult parseFIRRTLImplicitSSAName(OpAsmParser &parser, NamedAttrList &resultAttrs)
Definition: FIRRTLOps.cpp:5183
static FIRRTLBaseType inferMuxReturnType(FIRRTLBaseType high, FIRRTLBaseType low, bool isConstCondition, std::optional< Location > loc)
Infer the result type for a multiplexer given its two operand types, which may be aggregates.
Definition: FIRRTLOps.cpp:4786
static ParseResult parseCircuitOpAttrs(OpAsmParser &parser, NamedAttrList &resultAttrs)
Definition: FIRRTLOps.cpp:348
static SmallVector< PortInfo > getPortImpl(FModuleLike module)
Definition: FIRRTLOps.cpp:507
constexpr const char * toString(Flow flow)
Definition: FIRRTLOps.cpp:167
void getAsmBlockArgumentNamesImpl(Operation *op, mlir::Region &region, OpAsmSetValueNameFn setNameFn)
Get a special name to use when printing the entry block arguments of the region contained by an opera...
Definition: FIRRTLOps.cpp:298
static void printElideAnnotations(OpAsmPrinter &p, Operation *op, DictionaryAttr attr, ArrayRef< StringRef > extraElides={})
Definition: FIRRTLOps.cpp:5111
static ParseResult parseElidePortAnnotations(OpAsmParser &parser, NamedAttrList &resultAttrs)
Parse an optional attribute dictionary, adding empty 'annotations' and 'portAnnotations' attributes i...
Definition: FIRRTLOps.cpp:5126
static ParseResult parseMemOp(OpAsmParser &parser, NamedAttrList &resultAttrs)
Definition: FIRRTLOps.cpp:5204
static LogicalResult checkConnectFlow(Operation *connect)
Check if the source and sink are of appropriate flow.
Definition: FIRRTLOps.cpp:2929
static ParseResult parseVerifAttrs(OpAsmParser &p, NamedAttrList &resultAttrs)
Definition: FIRRTLOps.cpp:5272
static ParseResult parseElideAnnotations(OpAsmParser &parser, NamedAttrList &resultAttrs)
Parse an optional attribute dictionary, adding an empty 'annotations' attribute if not specified.
Definition: FIRRTLOps.cpp:5102
static void printCircuitOpAttrs(OpAsmPrinter &p, Operation *op, DictionaryAttr attr)
Definition: FIRRTLOps.cpp:357
static LogicalResult verifyPortSymbolUses(FModuleLike module, SymbolTableCollection &symbolTable)
Definition: FIRRTLOps.cpp:1434
static void printVerifAttrs(OpAsmPrinter &p, Operation *op, DictionaryAttr attr)
Definition: FIRRTLOps.cpp:5276
static Attribute getAttr(ArrayRef< NamedAttribute > attrs, StringRef name)
Get an attribute by name from a list of named attributes.
Definition: FIRRTLOps.cpp:3381
static void printMemOp(OpAsmPrinter &p, Operation *op, DictionaryAttr attr)
Always elide "ruw" and elide "annotations" if it exists or if it is empty.
Definition: FIRRTLOps.cpp:5209
static ParseResult parseElideEmptyName(OpAsmParser &p, NamedAttrList &resultAttrs)
Definition: FIRRTLOps.cpp:5234
static void printElideEmptyName(OpAsmPrinter &p, Operation *op, DictionaryAttr attr, ArrayRef< StringRef > extraElides={})
Definition: FIRRTLOps.cpp:5243
static ParseResult parseClassLike(OpAsmParser &parser, OperationState &result, bool hasSSAIdentifiers)
Definition: FIRRTLOps.cpp:1578
static void buildModuleWithoutAnnos(OpBuilder &builder, OperationState &result, StringAttr name, ArrayRef< PortInfo > ports)
Definition: FIRRTLOps.cpp:832
int32_t width
Definition: FIRRTL.cpp:27
static ModulePortInfo getPortList(ModuleTy &mod)
Definition: HWOps.cpp:1439
@ Input
Definition: HW.h:32
@ Output
Definition: HW.h:32
static InstancePath empty
FirMemory getSummary(MemOp op)
Definition: LowerMemory.cpp:35
llvm::SmallVector< StringAttr > inputs
Builder builder
static std::optional< APInt > getInt(Value value)
Helper to convert a value to a constant integer if it is one.
static int64_t size(hw::ArrayType mType, capnp::schema::Field::Reader cField)
Returns the expected size of an array (capnp list) in 64-bit words.
Definition: Schema.cpp:193
Value getValue() const
Get the Value which created this location.
Definition: FieldRef.h:37
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool hasDontTouch() const
firrtl.transforms.DontTouchAnnotation
static AnnotationSet forPort(FModuleLike op, size_t portNo)
Get an annotation set for the specified port.
ExprVisitor is a visitor for FIRRTL expression nodes.
ResultType dispatchExprVisitor(Operation *op, ExtraArgs... args)
FIRRTLBaseType getMaskType()
Return this type with all ground types replaced with UInt<1>.
int32_t getBitWidthOrSentinel()
If this is an IntType, AnalogType, or sugar type for a single bit (Clock, Reset, etc) then return the...
bool isConst()
Returns true if this is a 'const' type that can only hold compile-time constant values.
FIRRTLBaseType getAllConstDroppedType()
Return this type with a 'const' modifiers dropped.
bool isPassive() const
Return true if this is a "passive" type - one that contains no "flip" types recursively within itself...
Definition: FIRRTLTypes.h:152
FIRRTLBaseType getConstType(bool isConst)
Return a 'const' or non-'const' version of this type.
bool isConst()
Returns true if this is a 'const' type that can only hold compile-time constant values.
This is the common base class between SIntType and UIntType.
Definition: FIRRTLTypes.h:291
int32_t getWidthOrSentinel() const
Return the width of this type, or -1 if it has none specified.
static IntType get(MLIRContext *context, bool isSigned, int32_t widthOrSentinel=-1, bool isConst=false)
Return an SIntType or UIntType with the specified signedness, width, and constness.
bool hasWidth() const
Return true if this integer type has a known width.
Definition: FIRRTLTypes.h:278
static StringRef getInnerSymbolAttrName()
Return the name of the attribute used for inner symbol names.
int main(int argc, const char *argv[])
Definition: esiquery.cpp:25
def connect(destination, source)
Definition: support.py:37
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:53
std::string getInstanceName(mlir::func::CallOp callOp)
A helper function to get the instance name.
ClassType getInstanceTypeForClassLike(ClassLike classOp)
Definition: FIRRTLOps.cpp:1567
LogicalResult verifyTypeAgainstClassLike(ClassLike classOp, ClassType type, function_ref< InFlightDiagnostic()> emitError)
Assuming that the classOp is the source of truth, verify that the type accurately matches the signatu...
Definition: FIRRTLOps.cpp:1520
RefType getForceableResultType(bool forceable, Type type)
Return null or forceable reference result type.
IntegerAttr packAttribute(MLIRContext *context, ArrayRef< Direction > directions)
Return a IntegerAttr containing the packed representation of an array of directions.
StringRef toString(Direction direction)
SmallVector< Direction > unpackAttribute(IntegerAttr directions)
Turn a packed representation of port attributes into a vector that can be worked with.
LogicalResult validateBinaryOpArguments(ValueRange operands, ArrayRef< NamedAttribute > attrs, Location loc)
Definition: FIRRTLOps.cpp:4424
FIRRTLType inferElementwiseResult(FIRRTLType lhs, FIRRTLType rhs, std::optional< Location > loc)
Definition: FIRRTLOps.cpp:4502
LogicalResult inferReturnTypes(MLIRContext *context, std::optional< Location > loc, ValueRange operands, DictionaryAttr attrs, mlir::OpaqueProperties properties, mlir::RegionRange regions, SmallVectorImpl< Type > &results, llvm::function_ref< FIRRTLType(ValueRange, ArrayRef< NamedAttribute >, std::optional< Location >)> callback)
LogicalResult validateUnaryOpArguments(ValueRange operands, ArrayRef< NamedAttribute > attrs, Location loc)
Definition: FIRRTLOps.cpp:4595
FIRRTLType inferBitwiseResult(FIRRTLType lhs, FIRRTLType rhs, std::optional< Location > loc)
Definition: FIRRTLOps.cpp:4490
FIRRTLType inferAddSubResult(FIRRTLType lhs, FIRRTLType rhs, std::optional< Location > loc)
Definition: FIRRTLOps.cpp:4434
FIRRTLType inferComparisonResult(FIRRTLType lhs, FIRRTLType rhs, std::optional< Location > loc)
Definition: FIRRTLOps.cpp:4524
FIRRTLType inferReductionResult(FIRRTLType arg, std::optional< Location > loc)
Definition: FIRRTLOps.cpp:4686
LogicalResult validateOneOperandOneConst(ValueRange operands, ArrayRef< NamedAttribute > attrs, Location loc)
Definition: FIRRTLOps.cpp:4738
LogicalResult verifySameOperandsIntTypeKind(Operation *op)
Definition: FIRRTLOps.cpp:4414
Flow swapFlow(Flow flow)
Get a flow's reverse.
Definition: FIRRTLOps.cpp:154
Direction
This represents the direction of a single port.
FieldRef getFieldRefFromValue(Value value, bool lookThroughCasts=false)
Get the FieldRef from a value.
bool isConstant(Operation *op)
Return true if the specified operation has a constant value.
Definition: FIRRTLOps.cpp:4089
bool areAnonymousTypesEquivalent(FIRRTLBaseType lhs, FIRRTLBaseType rhs)
Return true if anonymous types of given arguments are equivalent by pointer comparison.
constexpr bool isValidDst(Flow flow)
Definition: FIRRTLOps.h:69
Flow foldFlow(Value val, Flow accumulatedFlow=Flow::Source)
Compute the flow for a Value, val, as determined by the FIRRTL specification.
Definition: FIRRTLOps.cpp:180
bool areTypesEquivalent(FIRRTLType destType, FIRRTLType srcType, bool destOuterTypeIsConst=false, bool srcOuterTypeIsConst=false, bool requireSameWidths=false)
Returns whether the two types are equivalent.
bool hasDontTouch(Value value)
Check whether a block argument ("port") or the operation defining a value has a DontTouch annotation,...
Definition: FIRRTLOps.cpp:287
size_t getNumPorts(Operation *op)
Return the number of ports in a module-like thing (modules, memories, etc)
Definition: FIRRTLOps.cpp:271
bool isTypeLarger(FIRRTLBaseType dstType, FIRRTLBaseType srcType)
Returns true if the destination is at least as wide as a source.
bool containsConst(Type type)
Returns true if the type is or contains a 'const' type whose value is guaranteed to be unchanging at ...
bool isDuplexValue(Value val)
Returns true if the value results from an expression with duplex flow.
Definition: FIRRTLOps.cpp:97
constexpr bool isValidSrc(Flow flow)
Definition: FIRRTLOps.h:65
Value getModuleScopedDriver(Value val, bool lookThroughWires, bool lookThroughNodes, bool lookThroughCasts)
Return the value that drives another FIRRTL value within module scope.
std::pair< std::string, bool > getFieldName(const FieldRef &fieldRef, bool nameSafe=false)
Get a string identifier representing the FieldRef.
bool isConst(Type type)
Returns true if this is a 'const' type whose value is guaranteed to be unchanging at circuit executio...
bool areTypesConstCastable(FIRRTLType destType, FIRRTLType srcType, bool srcOuterTypeIsConst=false)
Returns whether the srcType can be const-casted to the destType.
bool isExpression(Operation *op)
Return true if the specified operation is a firrtl expression.
Definition: FIRRTLOps.cpp:3395
DeclKind getDeclarationKind(Value val)
Definition: FIRRTLOps.cpp:259
std::optional< int64_t > getBitWidth(FIRRTLBaseType type, bool ignoreFlip=false)
::mlir::Type getFinalTypeByFieldID(Type type, uint64_t fieldID)
Operation * getReferencedModule(const HWSymbolCache *cache, Operation *instanceOp, mlir::FlatSymbolRefAttr moduleName)
Return a pointer to the referenced module operation.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
void getAsmResultNames(OpAsmSetValueNameFn setNameFn, StringRef instanceName, ArrayAttr resultNames, ValueRange results)
Suggest a name for each result value based on the saved result names attribute.
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
Definition: HWTypes.cpp:100
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
void elideImplicitSSAName(OpAsmPrinter &printer, Operation *op, DictionaryAttr attrs, SmallVectorImpl< StringRef > &elides)
Check if the name attribute in attrs matches the SSA name of the operation's first result.
bool inferImplicitSSAName(OpAsmParser &parser, NamedAttrList &attrs)
Ensure that attrs contains a name attribute by inferring its value from the SSA name of the operation...
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition: LLVM.h:186
StringAttr getFirMemoryName() const
Definition: FIRRTLOps.cpp:2771