CIRCT 20.0.0git
Loading...
Searching...
No Matches
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
25using namespace circt;
26using namespace firrtl;
27
28static ArrayAttr getAnnotationsFrom(Operation *op) {
29 if (auto annots = getAnnotationsIfPresent(op))
30 return annots;
31 return ArrayAttr::get(op->getContext(), {});
32}
33
34static 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.
46AnnotationSet::AnnotationSet(ArrayRef<Attribute> annotations,
47 MLIRContext *context)
48 : annotations(ArrayAttr::get(context, annotations)) {}
49
50/// Form an annotation set from an array of annotations.
51AnnotationSet::AnnotationSet(ArrayRef<Annotation> annotations,
52 MLIRContext *context)
53 : annotations(getAnnotationsFrom(annotations, context)) {}
54
55/// Form an annotation set with a possibly-null ArrayAttr.
56AnnotationSet::AnnotationSet(ArrayAttr annotations, MLIRContext *context)
57 : AnnotationSet(annotations ? annotations : ArrayAttr::get(context, {})) {}
58
59/// Get an annotation set for the specified operation.
62
63static 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
70AnnotationSet AnnotationSet::forPort(FModuleLike op, size_t portNo) {
71 return ::forPort(op.getOperation(), portNo);
72}
73
74AnnotationSet 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.
90bool AnnotationSet::applyToOperation(Operation *op) const {
91 auto before = op->getAttrDictionary();
92 op->setAttr(getAnnotationAttrName(), getArrayAttr());
93 return op->getAttrDictionary() != before;
94}
95
96static 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
113bool AnnotationSet::applyToPort(FModuleLike op, size_t portNo) const {
114 return ::applyToPort(*this, op.getOperation(), op.getNumPorts(), portNo);
115}
116
117bool AnnotationSet::applyToPort(MemOp op, size_t portNo) const {
118 return ::applyToPort(*this, op.getOperation(), op->getNumResults(), portNo);
119}
120
121Annotation AnnotationSet::getAnnotationImpl(StringAttr className) const {
122 for (auto annotation : *this) {
123 if (annotation.getClassAttr() == className)
124 return annotation;
125 }
126 return {};
127}
128
130 for (auto annotation : *this) {
131 if (annotation.getClass() == className)
132 return annotation;
133 }
134 return {};
135}
136
137bool AnnotationSet::hasAnnotationImpl(StringAttr className) const {
138 return getAnnotationImpl(className) != Annotation();
139}
140
141bool AnnotationSet::hasAnnotationImpl(StringRef className) const {
142 return getAnnotationImpl(className) != Annotation();
143}
144
148
149bool AnnotationSet::setDontTouch(bool dontTouch) {
150 if (dontTouch)
151 return addDontTouch();
152 return removeDontTouch();
153}
154
156 if (hasDontTouch())
157 return false;
158 addAnnotations(DictionaryAttr::get(
159 getContext(), {{StringAttr::get(getContext(), "class"),
160 StringAttr::get(getContext(), dontTouchAnnoClass)}}));
161 return true;
162}
163
167
168bool AnnotationSet::hasDontTouch(Operation *op) {
169 return AnnotationSet(op).hasDontTouch();
170}
171
172bool AnnotationSet::setDontTouch(Operation *op, bool dontTouch) {
173 if (dontTouch)
174 return addDontTouch(op);
175 return removeDontTouch(op);
176}
177
178bool 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
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.
195void 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
207void 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
223void 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.
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.
254bool 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
302bool 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
345DictionaryAttr Annotation::getDict() const {
346 return cast<DictionaryAttr>(attr);
347}
348
349void Annotation::setDict(DictionaryAttr dict) { attr = dict; }
350
351unsigned 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.
358StringAttr Annotation::getClassAttr() const {
359 return getDict().getAs<StringAttr>("class");
360}
361
362/// Return the 'class' that this annotation is representing.
363StringRef Annotation::getClass() const {
364 if (auto classAttr = getClassAttr())
365 return classAttr.getValue();
366 return {};
367}
368
369void Annotation::setMember(StringAttr name, Attribute value) {
370 setMember(name.getValue(), value);
371}
372
373void 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
394void 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
411void 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
431void 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
445FModuleLike 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
460 TypeSwitch<AnnoTarget>(*this).Case<OpAnnoTarget, PortAnnoTarget>(
461 [&](auto target) { target.setAnnotations(annotations); });
462}
463
464Attribute
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
482
484 annotations.applyToOperation(getOp());
485}
486
487Attribute
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.
495 return ::getInnerRefTo(
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
516PortAnnoTarget::PortAnnoTarget(FModuleLike op, unsigned portNo)
517 : AnnoTarget({op, portNo}) {}
518
519PortAnnoTarget::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)
544 : hw::InnerSymTarget(getOp());
545 return ::getInnerRefTo(
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}
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...
MLIRContext * getContext() const
Return the MLIRContext corresponding to this AnnotationSet.
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.
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.
StringRef getAnnotationAttrName()
Return the name of the attribute used for annotations on FIRRTL ops.
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.
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.
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)