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