CIRCT  20.0.0git
FIRRTLAnnotations.cpp
Go to the documentation of this file.
1 //===- FIRRTLAnnotations.cpp - Code for working with Annotations ----------===//
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 implements helpers for working with FIRRTL annotations.
10 //
11 //===----------------------------------------------------------------------===//
12 
21 #include "mlir/IR/Operation.h"
22 #include "mlir/Interfaces/FunctionImplementation.h"
23 #include "llvm/ADT/TypeSwitch.h"
24 
25 using namespace circt;
26 using namespace firrtl;
27 
28 static ArrayAttr getAnnotationsFrom(Operation *op) {
29  if (auto annots = getAnnotationsIfPresent(op))
30  return annots;
31  return ArrayAttr::get(op->getContext(), {});
32 }
33 
34 static ArrayAttr getAnnotationsFrom(ArrayRef<Annotation> annotations,
35  MLIRContext *context) {
36  if (annotations.empty())
37  return ArrayAttr::get(context, {});
38  SmallVector<Attribute> attrs;
39  attrs.reserve(annotations.size());
40  for (auto anno : annotations)
41  attrs.push_back(anno.getAttr());
42  return ArrayAttr::get(context, attrs);
43 }
44 
45 /// Form an annotation set from an array of annotation attributes.
46 AnnotationSet::AnnotationSet(ArrayRef<Attribute> annotations,
47  MLIRContext *context)
48  : annotations(ArrayAttr::get(context, annotations)) {}
49 
50 /// Form an annotation set from an array of annotations.
51 AnnotationSet::AnnotationSet(ArrayRef<Annotation> annotations,
52  MLIRContext *context)
53  : annotations(getAnnotationsFrom(annotations, context)) {}
54 
55 /// Form an annotation set with a possibly-null ArrayAttr.
56 AnnotationSet::AnnotationSet(ArrayAttr annotations, MLIRContext *context)
57  : AnnotationSet(annotations ? annotations : ArrayAttr::get(context, {})) {}
58 
59 /// Get an annotation set for the specified operation.
62 
63 static AnnotationSet forPort(Operation *op, size_t portNo) {
64  auto ports = op->getAttrOfType<ArrayAttr>(getPortAnnotationAttrName());
65  if (ports && !ports.empty())
66  return AnnotationSet(cast<ArrayAttr>(ports[portNo]));
67  return AnnotationSet(ArrayAttr::get(op->getContext(), {}));
68 }
69 
70 AnnotationSet AnnotationSet::forPort(FModuleLike op, size_t portNo) {
71  return ::forPort(op.getOperation(), portNo);
72 }
73 
74 AnnotationSet AnnotationSet::forPort(MemOp op, size_t portNo) {
75  return ::forPort(op.getOperation(), portNo);
76 }
77 
78 /// Get an annotation set for the specified value.
80  if (auto *op = v.getDefiningOp())
81  return AnnotationSet(op);
82  // If its not an Operation, then must be a block argument.
83  auto arg = dyn_cast<BlockArgument>(v);
84  auto module = cast<FModuleOp>(arg.getOwner()->getParentOp());
85  return forPort(module, arg.getArgNumber());
86 }
87 
88 /// Store the annotations in this set in an operation's `annotations` attribute,
89 /// overwriting any existing annotations.
90 bool AnnotationSet::applyToOperation(Operation *op) const {
91  auto before = op->getAttrDictionary();
92  op->setAttr(getAnnotationAttrName(), getArrayAttr());
93  return op->getAttrDictionary() != before;
94 }
95 
96 static bool applyToPort(AnnotationSet annos, Operation *op, size_t portCount,
97  size_t portNo) {
98  assert(portNo < portCount && "port index out of range.");
99  auto *context = op->getContext();
100  auto before = op->getAttrOfType<ArrayAttr>(getPortAnnotationAttrName());
101  SmallVector<Attribute> portAnnotations;
102  if (!before || before.empty())
103  portAnnotations.assign(portCount, ArrayAttr::get(context, {}));
104  else
105  portAnnotations.append(before.begin(), before.end());
106  portAnnotations[portNo] = annos.getArrayAttr();
107  auto after = ArrayAttr::get(context, portAnnotations);
108  if (before != after)
109  op->setAttr(getPortAnnotationAttrName(), after);
110  return before != after;
111 }
112 
113 bool AnnotationSet::applyToPort(FModuleLike op, size_t portNo) const {
114  return ::applyToPort(*this, op.getOperation(), op.getNumPorts(), portNo);
115 }
116 
117 bool AnnotationSet::applyToPort(MemOp op, size_t portNo) const {
118  return ::applyToPort(*this, op.getOperation(), op->getNumResults(), portNo);
119 }
120 
121 Annotation AnnotationSet::getAnnotationImpl(StringAttr className) const {
122  for (auto annotation : *this) {
123  if (annotation.getClassAttr() == className)
124  return annotation;
125  }
126  return {};
127 }
128 
129 Annotation AnnotationSet::getAnnotationImpl(StringRef className) const {
130  for (auto annotation : *this) {
131  if (annotation.getClass() == className)
132  return annotation;
133  }
134  return {};
135 }
136 
137 bool AnnotationSet::hasAnnotationImpl(StringAttr className) const {
138  return getAnnotationImpl(className) != Annotation();
139 }
140 
141 bool AnnotationSet::hasAnnotationImpl(StringRef className) const {
142  return getAnnotationImpl(className) != Annotation();
143 }
144 
147 }
148 
149 bool AnnotationSet::setDontTouch(bool dontTouch) {
150  if (dontTouch)
151  return addDontTouch();
152  return removeDontTouch();
153 }
154 
156  if (hasDontTouch())
157  return false;
159  getContext(), {{StringAttr::get(getContext(), "class"),
161  return true;
162 }
163 
166 }
167 
168 bool AnnotationSet::hasDontTouch(Operation *op) {
169  return AnnotationSet(op).hasDontTouch();
170 }
171 
172 bool AnnotationSet::setDontTouch(Operation *op, bool dontTouch) {
173  if (dontTouch)
174  return addDontTouch(op);
175  return removeDontTouch(op);
176 }
177 
178 bool AnnotationSet::addDontTouch(Operation *op) {
179  AnnotationSet annos(op);
180  auto changed = annos.addDontTouch();
181  if (changed)
182  annos.applyToOperation(op);
183  return changed;
184 }
185 
186 bool AnnotationSet::removeDontTouch(Operation *op) {
187  AnnotationSet annos(op);
188  auto changed = annos.removeDontTouch();
189  if (changed)
190  annos.applyToOperation(op);
191  return changed;
192 }
193 
194 /// Add more annotations to this AttributeSet.
195 void AnnotationSet::addAnnotations(ArrayRef<Annotation> newAnnotations) {
196  if (newAnnotations.empty())
197  return;
198 
199  SmallVector<Attribute> annotationVec;
200  annotationVec.reserve(annotations.size() + newAnnotations.size());
201  annotationVec.append(annotations.begin(), annotations.end());
202  for (auto anno : newAnnotations)
203  annotationVec.push_back(anno.getDict());
204  annotations = ArrayAttr::get(getContext(), annotationVec);
205 }
206 
207 void AnnotationSet::addAnnotations(ArrayRef<Attribute> newAnnotations) {
208  if (newAnnotations.empty())
209  return;
210 
211  if (empty()) {
212  annotations = ArrayAttr::get(getContext(), newAnnotations);
213  return;
214  }
215 
216  SmallVector<Attribute> annotationVec;
217  annotationVec.reserve(annotations.size() + newAnnotations.size());
218  annotationVec.append(annotations.begin(), annotations.end());
219  annotationVec.append(newAnnotations.begin(), newAnnotations.end());
220  annotations = ArrayAttr::get(getContext(), annotationVec);
221 }
222 
223 void AnnotationSet::addAnnotations(ArrayAttr newAnnotations) {
224  if (!newAnnotations)
225  return;
226 
227  if (empty()) {
228  annotations = newAnnotations;
229  return;
230  }
231 
232  SmallVector<Attribute> annotationVec;
233  annotationVec.reserve(annotations.size() + newAnnotations.size());
234  annotationVec.append(annotations.begin(), annotations.end());
235  annotationVec.append(newAnnotations.begin(), newAnnotations.end());
236  annotations = ArrayAttr::get(getContext(), annotationVec);
237 }
238 
239 /// Remove an annotation from this annotation set. Returns true if any were
240 /// removed, false otherwise.
242  return removeAnnotations([&](Annotation other) { return other == anno; });
243 }
244 
245 /// Remove an annotation from this annotation set. Returns true if any were
246 /// removed, false otherwise.
247 bool AnnotationSet::removeAnnotation(Attribute anno) {
248  return removeAnnotations(
249  [&](Annotation other) { return other.getDict() == anno; });
250 }
251 
252 /// Remove an annotation from this annotation set. Returns true if any were
253 /// removed, false otherwise.
254 bool AnnotationSet::removeAnnotation(StringRef className) {
255  return removeAnnotations(
256  [&](Annotation other) { return other.getClass() == className; });
257 }
258 
259 /// Remove all annotations from this annotation set for which `predicate`
260 /// returns true.
262  llvm::function_ref<bool(Annotation)> predicate) {
263  // Search for the first match.
264  ArrayRef<Attribute> annos = getArrayAttr().getValue();
265  auto *it = annos.begin();
266  while (it != annos.end() && !predicate(Annotation(*it)))
267  ++it;
268 
269  // Fast path for sets where the predicate never matched.
270  if (it == annos.end())
271  return false;
272 
273  // Build a filtered list of annotations.
274  SmallVector<Attribute> filteredAnnos;
275  filteredAnnos.reserve(annos.size());
276  filteredAnnos.append(annos.begin(), it);
277  ++it;
278  while (it != annos.end()) {
279  if (!predicate(Annotation(*it)))
280  filteredAnnos.push_back(*it);
281  ++it;
282  }
283  annotations = ArrayAttr::get(getContext(), filteredAnnos);
284  return true;
285 }
286 
287 /// Remove all annotations from an operation for which `predicate` returns true.
289  Operation *op, llvm::function_ref<bool(Annotation)> predicate) {
290  // Fast-path for no annotations.
291  auto annosArray = getAnnotationsIfPresent(op);
292  if (!annosArray)
293  return false;
294  AnnotationSet annos(annosArray);
295  if (annos.removeAnnotations(predicate)) {
296  annos.applyToOperation(op);
297  return true;
298  }
299  return false;
300 }
301 
302 bool AnnotationSet::removeAnnotations(Operation *op, StringRef className) {
303  return removeAnnotations(
304  op, [&](Annotation a) { return (a.getClass() == className); });
305 }
306 
307 /// Remove all port annotations from a module or extmodule for which `predicate`
308 /// returns true.
310  Operation *module,
311  llvm::function_ref<bool(unsigned, Annotation)> predicate) {
312  auto ports = dyn_cast_or_null<ArrayAttr>(module->getAttr("portAnnotations"));
313  if (!ports || ports.empty())
314  return false;
315 
316  // Collect results
317  SmallVector<Attribute> newAnnos;
318 
319  // Filter the annotations on each argument.
320  bool changed = false;
321  for (unsigned argNum = 0, argNumEnd = ports.size(); argNum < argNumEnd;
322  ++argNum) {
323  AnnotationSet annos(AnnotationSet(cast<ArrayAttr>(ports[argNum])));
324 
325  // Go through all annotations on this port and extract the interesting
326  // ones. If any modifications were done, keep a reduced set of attributes
327  // around for the port, otherwise just stick with the existing ones.
328  if (!annos.empty())
329  changed |= annos.removeAnnotations(
330  [&](Annotation anno) { return predicate(argNum, anno); });
331  newAnnos.push_back(annos.getArrayAttr());
332  }
333 
334  // If we have made any changes, apply them to the operation.
335  if (changed)
336  module->setAttr("portAnnotations",
337  ArrayAttr::get(module->getContext(), newAnnos));
338  return changed;
339 }
340 
341 //===----------------------------------------------------------------------===//
342 // Annotation
343 //===----------------------------------------------------------------------===//
344 
345 DictionaryAttr Annotation::getDict() const {
346  return cast<DictionaryAttr>(attr);
347 }
348 
349 void Annotation::setDict(DictionaryAttr dict) { attr = dict; }
350 
351 unsigned Annotation::getFieldID() const {
352  if (auto fieldID = getMember<IntegerAttr>("circt.fieldID"))
353  return fieldID.getInt();
354  return 0;
355 }
356 
357 /// Return the 'class' that this annotation is representing.
358 StringAttr Annotation::getClassAttr() const {
359  return getDict().getAs<StringAttr>("class");
360 }
361 
362 /// Return the 'class' that this annotation is representing.
363 StringRef Annotation::getClass() const {
364  if (auto classAttr = getClassAttr())
365  return classAttr.getValue();
366  return {};
367 }
368 
369 void Annotation::setMember(StringAttr name, Attribute value) {
370  setMember(name.getValue(), value);
371 }
372 
373 void Annotation::setMember(StringRef name, Attribute value) {
374  // Binary search for the matching field.
375  auto dict = getDict();
376  auto [it, found] = mlir::impl::findAttrSorted(dict.begin(), dict.end(), name);
377  auto index = std::distance(dict.begin(), it);
378  // Create an array for the new members.
379  SmallVector<NamedAttribute> attributes;
380  attributes.reserve(dict.size() + 1);
381  // Copy over the leading annotations.
382  for (auto field : dict.getValue().take_front(index))
383  attributes.push_back(field);
384  // Push the new member.
385  auto nameAttr = StringAttr::get(dict.getContext(), name);
386  attributes.push_back(NamedAttribute(nameAttr, value));
387  // Copy remaining members, skipping the old field value.
388  for (auto field : dict.getValue().drop_front(index + found))
389  attributes.push_back(field);
390  // Commit the dictionary.
391  setDict(DictionaryAttr::getWithSorted(dict.getContext(), attributes));
392 }
393 
394 void Annotation::removeMember(StringAttr name) {
395  auto dict = getDict();
396  SmallVector<NamedAttribute> attributes;
397  attributes.reserve(dict.size() - 1);
398  auto *i = dict.begin();
399  auto *e = dict.end();
400  while (i != e && i->getValue() != name)
401  attributes.push_back(*(i++));
402  // If the member was not here, just return.
403  if (i == e)
404  return;
405  // Copy the rest of the members over.
406  attributes.append(++i, e);
407  // Commit the dictionary.
408  setDict(DictionaryAttr::getWithSorted(dict.getContext(), attributes));
409 }
410 
411 void Annotation::removeMember(StringRef name) {
412  // Binary search for the matching field.
413  auto dict = getDict();
414  auto [it, found] = mlir::impl::findAttrSorted(dict.begin(), dict.end(), name);
415  auto index = std::distance(dict.begin(), it);
416  if (!found)
417  return;
418  // Create an array for the new members.
419  SmallVector<NamedAttribute> attributes;
420  attributes.reserve(dict.size() - 1);
421  // Copy over the leading annotations.
422  for (auto field : dict.getValue().take_front(index))
423  attributes.push_back(field);
424  // Copy remaining members, skipping the old field value.
425  for (auto field : dict.getValue().drop_front(index + 1))
426  attributes.push_back(field);
427  // Commit the dictionary.
428  setDict(DictionaryAttr::getWithSorted(dict.getContext(), attributes));
429 }
430 
431 void Annotation::dump() { attr.dump(); }
432 
433 //===----------------------------------------------------------------------===//
434 // AnnotationSetIterator
435 //===----------------------------------------------------------------------===//
436 
438  return Annotation(this->getBase().getArray()[this->getIndex()]);
439 }
440 
441 //===----------------------------------------------------------------------===//
442 // AnnoTarget
443 //===----------------------------------------------------------------------===//
444 
445 FModuleLike AnnoTarget::getModule() const {
446  auto *op = getOp();
447  if (auto module = llvm::dyn_cast<FModuleLike>(op))
448  return module;
449  return op->getParentOfType<FModuleLike>();
450 }
451 
453  return TypeSwitch<AnnoTarget, AnnotationSet>(*this)
455  [&](auto target) { return target.getAnnotations(); })
456  .Default([&](auto target) { return AnnotationSet(getOp()); });
457 }
458 
459 void AnnoTarget::setAnnotations(AnnotationSet annotations) const {
460  TypeSwitch<AnnoTarget>(*this).Case<OpAnnoTarget, PortAnnoTarget>(
461  [&](auto target) { target.setAnnotations(annotations); });
462 }
463 
464 Attribute
466  return TypeSwitch<AnnoTarget, Attribute>(*this)
468  [&](auto target) { return target.getNLAReference(moduleNamespace); })
469  .Default([](auto target) { return Attribute(); });
470 }
471 
473  return TypeSwitch<AnnoTarget, FIRRTLType>(*this)
475  [](auto target) { return target.getType(); })
476  .Default([](auto target) { return FIRRTLType(); });
477 }
478 
480  return AnnotationSet(getOp());
481 }
482 
484  annotations.applyToOperation(getOp());
485 }
486 
487 Attribute
489  // If the op is a module, just return the module name.
490  if (auto module = llvm::dyn_cast<FModuleLike>(getOp())) {
491  assert(module.getModuleNameAttr() && "invalid NLA reference");
492  return FlatSymbolRefAttr::get(module.getModuleNameAttr());
493  }
494  // Return an inner-ref to the target.
496  getOp(), [&moduleNamespace](auto _) -> hw::InnerSymbolNamespace & {
497  return moduleNamespace;
498  });
499 }
500 
502  auto *op = getOp();
503  // Annotations that target operations are resolved like inner symbols.
504  if (auto is = llvm::dyn_cast<hw::InnerSymbolOpInterface>(op)) {
505  auto result = is.getTargetResult();
506  if (!result)
507  return {};
508  return type_cast<FIRRTLType>(result.getType());
509  }
510  // Fallback to assuming the single result is the target.
511  if (op->getNumResults() != 1)
512  return {};
513  return type_cast<FIRRTLType>(op->getResult(0).getType());
514 }
515 
516 PortAnnoTarget::PortAnnoTarget(FModuleLike op, unsigned portNo)
517  : AnnoTarget({op, portNo}) {}
518 
519 PortAnnoTarget::PortAnnoTarget(MemOp op, unsigned portNo)
520  : AnnoTarget({op, portNo}) {}
521 
523  if (auto memOp = llvm::dyn_cast<MemOp>(getOp()))
524  return AnnotationSet::forPort(memOp, getPortNo());
525  if (auto moduleOp = llvm::dyn_cast<FModuleLike>(getOp()))
526  return AnnotationSet::forPort(moduleOp, getPortNo());
527  llvm_unreachable("unknown port target");
528  return AnnotationSet(getOp()->getContext());
529 }
530 
532  if (auto memOp = llvm::dyn_cast<MemOp>(getOp()))
533  annotations.applyToPort(memOp, getPortNo());
534  else if (auto moduleOp = llvm::dyn_cast<FModuleLike>(getOp()))
535  annotations.applyToPort(moduleOp, getPortNo());
536  else
537  llvm_unreachable("unknown port target");
538 }
539 
541  hw::InnerSymbolNamespace &moduleNamespace) const {
542  auto module = llvm::dyn_cast<FModuleLike>(getOp());
543  auto target = module ? hw::InnerSymTarget(getPortNo(), module)
546  target, [&moduleNamespace](auto _) -> hw::InnerSymbolNamespace & {
547  return moduleNamespace;
548  });
549 }
550 
552  auto *op = getOp();
553  if (auto module = llvm::dyn_cast<FModuleLike>(op))
554  return type_cast<FIRRTLType>(module.getPortType(getPortNo()));
555  if (llvm::isa<MemOp, InstanceOp>(op))
556  return type_cast<FIRRTLType>(op->getResult(getPortNo()).getType());
557  llvm_unreachable("unknown operation kind");
558  return {};
559 }
560 
561 //===----------------------------------------------------------------------===//
562 // Utilities for Specific Annotations
563 //
564 // TODO: Remove these in favor of first-class annotations.
565 //===----------------------------------------------------------------------===//
566 
567 LogicalResult circt::firrtl::extractDUT(const FModuleLike mod,
568  FModuleLike &dut) {
569  if (!AnnotationSet(mod).hasAnnotation(dutAnnoClass))
570  return success();
571 
572  // TODO: This check is duplicated multiple places. This should be factored
573  // out as part of the annotation lowering pass.
574  if (dut) {
575  auto diag = emitError(mod->getLoc())
576  << "is marked with a '" << dutAnnoClass << "', but '"
577  << dut.getModuleName()
578  << "' also had such an annotation (this should "
579  "be impossible!)";
580  diag.attachNote(dut.getLoc()) << "the first DUT was found here";
581  return failure();
582  }
583  dut = mod;
584  return success();
585 }
assert(baseType &&"element must be base type")
static bool applyToPort(AnnotationSet annos, Operation *op, size_t portCount, size_t portNo)
static AnnotationSet forPort(Operation *op, size_t portNo)
static ArrayAttr getAnnotationsFrom(Operation *op)
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool hasDontTouch() const
firrtl.transforms.DontTouchAnnotation
bool hasAnnotationImpl(StringAttr className) const
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
Annotation getAnnotationImpl(StringAttr className) const
bool applyToPort(FModuleLike op, size_t portNo) const
Store the annotations in this set in an operation's portAnnotations attribute, overwriting any existi...
ArrayAttr getArrayAttr() const
Return this annotation set as an ArrayAttr.
bool removeAnnotation(Annotation anno)
Remove an annotation from this annotation set.
static AnnotationSet get(Value v)
Get an annotation set for the specified value.
bool applyToOperation(Operation *op) const
Store the annotations in this set in an operation's annotations attribute, overwriting any existing a...
void addAnnotations(ArrayRef< Annotation > annotations)
Add more annotations to this annotation set.
bool hasAnnotation(StringRef className) const
Return true if we have an annotation with the specified class name.
AnnotationSet(MLIRContext *context)
Form an empty annotation set.
static bool removePortAnnotations(Operation *module, llvm::function_ref< bool(unsigned, Annotation)> predicate)
Remove all port annotations from a module or extmodule for which predicate returns true.
static AnnotationSet forPort(FModuleLike op, size_t portNo)
Get an annotation set for the specified port.
bool setDontTouch(bool dontTouch)
MLIRContext * getContext() const
Return the MLIRContext corresponding to this AnnotationSet.
This class provides a read-only projection of an annotation.
DictionaryAttr getDict() const
Get the data dictionary of this attribute.
void setDict(DictionaryAttr dict)
Set the data dictionary of this attribute.
unsigned getFieldID() const
Get the field id this attribute targets.
void setMember(StringAttr name, Attribute value)
Add or set a member of the annotation to a value.
void removeMember(StringAttr name)
Remove a member of the annotation.
StringRef getClass() const
Return the 'class' that this annotation is representing.
StringAttr getClassAttr() const
Return the 'class' that this annotation is representing.
The target of an inner symbol, the entity the symbol is a handle for.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
StringRef getAnnotationAttrName()
Return the name of the attribute used for annotations on FIRRTL ops.
constexpr const char * dutAnnoClass
hw::InnerRefAttr getInnerRefTo(const hw::InnerSymTarget &target, GetNamespaceCallback getNamespace)
Obtain an inner reference to the target (operation or port), adding an inner symbol as necessary.
LogicalResult extractDUT(FModuleLike mod, FModuleLike &dut)
Utility that searches for a MarkDUTAnnotation on a specific module, mod, and tries to update a design...
StringRef getPortAnnotationAttrName()
Return the name of the attribute used for port annotations on FIRRTL ops.
ArrayAttr getAnnotationsIfPresent(Operation *op)
constexpr const char * dontTouchAnnoClass
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
An annotation target is used to keep track of something that is targeted by an Annotation.
FIRRTLType getType() const
Get the type of the target.
Attribute getNLAReference(hw::InnerSymbolNamespace &moduleNamespace) const
Get a reference to this target suitable for use in an NLA.
Operation * getOp() const
FModuleLike getModule() const
Get the parent module of the target.
AnnotationSet getAnnotations() const
Get the annotations associated with the target.
void setAnnotations(AnnotationSet annotations) const
Set the annotations associated with the target.
This represents an annotation targeting a specific operation.
Attribute getNLAReference(hw::InnerSymbolNamespace &moduleNamespace) const
void setAnnotations(AnnotationSet annotations) const
AnnotationSet getAnnotations() const
This represents an annotation targeting a specific port of a module, memory, or instance.
AnnotationSet getAnnotations() const
void setAnnotations(AnnotationSet annotations) const
Attribute getNLAReference(hw::InnerSymbolNamespace &moduleNamespace) const
PortAnnoTarget(FModuleLike op, unsigned portNo)