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