attribute-value ::= affine-map-attribute
| array-attribute
| bool-attribute
+ | dictionary-attribute
| elements-attribute
| integer-attribute
| integer-set-attribute
A boolean attribute is a literal attribute that represents a one-bit boolean
value, true or false.
+#### Dictionary Attribute
+
+Syntax:
+
+``` {.ebnf}
+dictionary-attribute ::= `{` (attribute-entry (`,` attribute-entry)*)? `}`
+```
+
+A dictionary attribute is an attribute that represents a sorted collection of
+named attribute values. The elements are sorted by name, and each name must be
+unique within the collection.
+
#### Elements Attributes
Syntax:
struct OpaqueAttributeStorage;
struct BoolAttributeStorage;
+struct DictionaryAttributeStorage;
struct IntegerAttributeStorage;
struct FloatAttributeStorage;
struct StringAttributeStorage;
struct OpaqueElementsAttributeStorage;
struct SparseElementsAttributeStorage;
-class AttributeListStorage;
-
} // namespace detail
/// Attributes are known-constant values of operations and functions.
Unit = Attribute::FIRST_STANDARD_ATTR,
Opaque,
Bool,
+ Dictionary,
Integer,
Float,
String,
ArrayRef<Attribute> getValue() const;
- size_t size() const { return getValue().size(); }
-
+ /// Support range iteration.
using iterator = llvm::ArrayRef<Attribute>::iterator;
iterator begin() const { return getValue().begin(); }
iterator end() const { return getValue().end(); }
+ size_t size() const { return getValue().size(); }
/// Methods for support type inquiry through isa, cast, and dyn_cast.
static bool kindof(unsigned kind) {
}
};
+/// NamedAttribute is used for dictionary attributes, it holds an identifier for
+/// the name and a value for the attribute. The attribute pointer should always
+/// be non-null.
+using NamedAttribute = std::pair<Identifier, Attribute>;
+
+/// Dictionary attribute is an attribute that represents a sorted collection of
+/// named attribute values. The elements are sorted by name, and each name must
+/// be unique within the collection.
+class DictionaryAttr
+ : public Attribute::AttrBase<DictionaryAttr, Attribute,
+ detail::DictionaryAttributeStorage> {
+public:
+ using Base::Base;
+ using ValueType = ArrayRef<NamedAttribute>;
+
+ static DictionaryAttr get(ArrayRef<NamedAttribute> value,
+ MLIRContext *context);
+
+ ArrayRef<NamedAttribute> getValue() const;
+
+ /// Return the specified attribute if present, null otherwise.
+ Attribute get(StringRef name) const;
+ Attribute get(Identifier name) const;
+
+ /// Support range iteration.
+ using iterator = llvm::ArrayRef<NamedAttribute>::iterator;
+ iterator begin() const;
+ iterator end() const;
+ size_t size() const;
+
+ /// Methods for supporting type inquiry through isa, cast, and dyn_cast.
+ static bool kindof(unsigned kind) {
+ return kind == StandardAttributes::Dictionary;
+ }
+};
+
class AffineMapAttr
: public Attribute::AttrBase<AffineMapAttr, Attribute,
detail::AffineMapAttributeStorage> {
return ::llvm::hash_value(arg.impl);
}
-/// NamedAttribute is used for named attribute lists, it holds an identifier for
-/// the name and a value for the attribute. The attribute pointer should always
-/// be non-null.
-using NamedAttribute = std::pair<Identifier, Attribute>;
-
/// A NamedAttributeList is used to manage a list of named attributes. This
/// provides simple interfaces for adding/removing/finding attributes from
-/// within a raw AttributeListStorage.
+/// within a DictionaryAttr.
///
-/// We assume there will be relatively few attributes on a given function
+/// We assume there will be relatively few attributes on a given operation
/// (maybe a dozen or so, but not hundreds or thousands) so we use linear
/// searches for everything.
class NamedAttributeList {
public:
- NamedAttributeList() : attrs(nullptr) {}
+ NamedAttributeList(DictionaryAttr attrs = nullptr) : attrs(attrs) {}
NamedAttributeList(ArrayRef<NamedAttribute> attributes);
/// Return all of the attributes on this operation.
- ArrayRef<NamedAttribute> getAttrs() const;
+ ArrayRef<NamedAttribute> getAttrs() const {
+ return attrs ? attrs.getValue() : llvm::None;
+ }
/// Replace the held attributes with ones provided in 'newAttrs'.
void setAttrs(ArrayRef<NamedAttribute> attributes);
/// Return the specified attribute if present, null otherwise.
- Attribute get(StringRef name) const;
+ Attribute get(StringRef name) const {
+ return attrs ? attrs.get(name) : nullptr;
+ }
Attribute get(Identifier name) const;
/// If the an attribute exists with the specified name, change it to the new
RemoveResult remove(Identifier name);
private:
- detail::AttributeListStorage *attrs;
+ DictionaryAttr attrs;
};
} // end namespace mlir.
UnitAttr getUnitAttr();
BoolAttr getBoolAttr(bool value);
+ DictionaryAttr getDictionaryAttr(ArrayRef<NamedAttribute> value);
IntegerAttr getIntegerAttr(Type type, int64_t value);
IntegerAttr getIntegerAttr(Type type, const APInt &value);
FloatAttr getFloatAttr(Type type, double value);
case StandardAttributes::Bool:
os << (attr.cast<BoolAttr>().getValue() ? "true" : "false");
break;
+ case StandardAttributes::Dictionary:
+ os << '{';
+ interleaveComma(attr.cast<DictionaryAttr>().getValue(),
+ [&](NamedAttribute attr) {
+ os << attr.first << ": ";
+ printAttribute(attr.second);
+ });
+ os << '}';
+ break;
case StandardAttributes::Integer: {
auto intAttr = attr.cast<IntegerAttr>();
// Print all integer attributes as signed unless i1.
ArrayRef<Attribute> value;
};
+/// An attribute representing a dictionary of sorted named attributes.
+struct DictionaryAttributeStorage final
+ : public AttributeStorage,
+ private llvm::TrailingObjects<DictionaryAttributeStorage,
+ NamedAttribute> {
+ using KeyTy = ArrayRef<NamedAttribute>;
+
+ /// Given a list of NamedAttribute's, canonicalize the list (sorting
+ /// by name) and return the unique'd result.
+ static DictionaryAttributeStorage *get(ArrayRef<NamedAttribute> attrs);
+
+ /// Key equality function.
+ bool operator==(const KeyTy &key) const { return key == getElements(); }
+
+ /// Construct a new storage instance.
+ static DictionaryAttributeStorage *
+ construct(AttributeStorageAllocator &allocator, const KeyTy &key) {
+ auto size = DictionaryAttributeStorage::totalSizeToAlloc<NamedAttribute>(
+ key.size());
+ auto rawMem = allocator.allocate(size, alignof(NamedAttribute));
+
+ // Initialize the storage and trailing attribute list.
+ auto result = ::new (rawMem) DictionaryAttributeStorage(key.size());
+ std::uninitialized_copy(key.begin(), key.end(),
+ result->getTrailingObjects<NamedAttribute>());
+ return result;
+ }
+
+ /// Return the elements of this dictionary attribute.
+ ArrayRef<NamedAttribute> getElements() const {
+ return {getTrailingObjects<NamedAttribute>(), numElements};
+ }
+
+private:
+ friend class llvm::TrailingObjects<DictionaryAttributeStorage,
+ NamedAttribute>;
+
+ // This is used by the llvm::TrailingObjects base class.
+ size_t numTrailingObjects(OverloadToken<NamedAttribute>) const {
+ return numElements;
+ }
+ DictionaryAttributeStorage(unsigned numElements) : numElements(numElements) {}
+
+ /// This is the number of attributes.
+ const unsigned numElements;
+};
+
// An attribute representing a reference to an affine map.
struct AffineMapAttributeStorage : public AttributeStorage {
using KeyTy = AffineMap;
DenseIntElementsAttr indices;
DenseElementsAttr values;
};
-
-/// A raw list of named attributes stored as a trailing array.
-class AttributeListStorage final
- : private llvm::TrailingObjects<AttributeListStorage, NamedAttribute> {
- friend class llvm::TrailingObjects<AttributeListStorage, NamedAttribute>;
-
-public:
- /// Given a list of NamedAttribute's, canonicalize the list (sorting
- /// by name) and return the unique'd result. Note that the empty list is
- /// represented with a null pointer.
- static AttributeListStorage *get(ArrayRef<NamedAttribute> attrs);
-
- /// Return the element constants for this aggregate constant. These are
- /// known to all be constants.
- ArrayRef<NamedAttribute> getElements() const {
- return {getTrailingObjects<NamedAttribute>(), numElements};
- }
-
-private:
- // This is used by the llvm::TrailingObjects base class.
- size_t numTrailingObjects(OverloadToken<NamedAttribute>) const {
- return numElements;
- }
- AttributeListStorage() = delete;
- AttributeListStorage(const AttributeListStorage &) = delete;
- AttributeListStorage(unsigned numElements) : numElements(numElements) {}
-
- /// This is the number of attributes.
- const unsigned numElements;
-};
} // namespace detail
} // namespace mlir
bool BoolAttr::getValue() const { return getImpl()->value; }
//===----------------------------------------------------------------------===//
+// DictionaryAttr
+//===----------------------------------------------------------------------===//
+
+/// Perform a three-way comparison between the names of the specified
+/// NamedAttributes.
+static int compareNamedAttributes(const NamedAttribute *lhs,
+ const NamedAttribute *rhs) {
+ return lhs->first.str().compare(rhs->first.str());
+}
+
+DictionaryAttr DictionaryAttr::get(ArrayRef<NamedAttribute> value,
+ MLIRContext *context) {
+ assert(llvm::all_of(value,
+ [](const NamedAttribute &attr) { return attr.second; }) &&
+ "value cannot have null entries");
+
+ // We need to sort the element list to canonicalize it, but we also don't want
+ // to do a ton of work in the super common case where the element list is
+ // already sorted.
+ SmallVector<NamedAttribute, 8> storage;
+ switch (value.size()) {
+ case 0:
+ break;
+ case 1:
+ // A single element is already sorted.
+ break;
+ case 2:
+ assert(value[0].first != value[1].first &&
+ "DictionaryAttr element names must be unique");
+
+ // Don't invoke a general sort for two element case.
+ if (value[0].first.strref() > value[1].first.strref()) {
+ storage.push_back(value[1]);
+ storage.push_back(value[0]);
+ value = storage;
+ }
+ break;
+ default:
+ // Check to see they are sorted already.
+ bool isSorted = true;
+ for (unsigned i = 0, e = value.size() - 1; i != e; ++i) {
+ if (value[i].first.strref() > value[i + 1].first.strref()) {
+ isSorted = false;
+ break;
+ }
+ }
+ // If not, do a general sort.
+ if (!isSorted) {
+ storage.append(value.begin(), value.end());
+ llvm::array_pod_sort(storage.begin(), storage.end(),
+ compareNamedAttributes);
+ value = storage;
+ }
+
+ // Ensure that the attribute elements are unique.
+ assert(std::adjacent_find(value.begin(), value.end(),
+ [](NamedAttribute l, NamedAttribute r) {
+ return l.first == r.first;
+ }) == value.end() &&
+ "DictionaryAttr element names must be unique");
+ }
+
+ return Base::get(context, StandardAttributes::Dictionary, value);
+}
+
+ArrayRef<NamedAttribute> DictionaryAttr::getValue() const {
+ return getImpl()->getElements();
+}
+
+/// Return the specified attribute if present, null otherwise.
+Attribute DictionaryAttr::get(StringRef name) const {
+ for (auto elt : getValue())
+ if (elt.first.is(name))
+ return elt.second;
+ return nullptr;
+}
+Attribute DictionaryAttr::get(Identifier name) const {
+ for (auto elt : getValue())
+ if (elt.first == name)
+ return elt.second;
+ return nullptr;
+}
+
+DictionaryAttr::iterator DictionaryAttr::begin() const {
+ return getValue().begin();
+}
+DictionaryAttr::iterator DictionaryAttr::end() const {
+ return getValue().end();
+}
+size_t DictionaryAttr::size() const { return getValue().size(); }
+
+//===----------------------------------------------------------------------===//
// IntegerAttr
//===----------------------------------------------------------------------===//
values.assign(raw_begin(), raw_end());
}
-template<typename Fn, typename Attr>
-static ShapedType mappingHelper(
- Fn mapping, Attr& attr, ShapedType inType, Type newElementType,
- llvm::SmallVectorImpl<char>& data) {
+template <typename Fn, typename Attr>
+static ShapedType mappingHelper(Fn mapping, Attr &attr, ShapedType inType,
+ Type newElementType,
+ llvm::SmallVectorImpl<char> &data) {
size_t bitWidth = getDenseElementBitwidth(newElementType);
ShapedType newArrayType;
assert(newArrayType && "Unhandled tensor type");
data.resize(APInt::getNumWords(bitWidth * inType.getNumElements()) *
- APInt::APINT_WORD_SIZE);
+ APInt::APINT_WORD_SIZE);
uint64_t elementIdx = 0;
for (auto value : attr) {
Type newElementType,
llvm::function_ref<APInt(const APInt &)> mapping) const {
llvm::SmallVector<char, 8> elementData;
- auto newArrayType = mappingHelper(
- mapping, *this, getType(), newElementType, elementData);
+ auto newArrayType =
+ mappingHelper(mapping, *this, getType(), newElementType, elementData);
return get(newArrayType, elementData);
}
Type newElementType,
llvm::function_ref<APInt(const APFloat &)> mapping) const {
llvm::SmallVector<char, 8> elementData;
- auto newArrayType = mappingHelper(
- mapping, *this, getType(), newElementType, elementData);
+ auto newArrayType =
+ mappingHelper(mapping, *this, getType(), newElementType, elementData);
return get(newArrayType, elementData);
}
setAttrs(attributes);
}
-/// Return all of the attributes on this operation.
-ArrayRef<NamedAttribute> NamedAttributeList::getAttrs() const {
- return attrs ? attrs->getElements() : llvm::None;
-}
-
/// Replace the held attributes with ones provided in 'newAttrs'.
void NamedAttributeList::setAttrs(ArrayRef<NamedAttribute> attributes) {
// Don't create an attribute list if there are no attributes.
- if (attributes.empty()) {
+ if (attributes.empty())
attrs = nullptr;
- return;
- }
-
- assert(llvm::all_of(attributes,
- [](const NamedAttribute &attr) { return attr.second; }) &&
- "attributes cannot have null entries");
- attrs = AttributeListStorage::get(attributes);
+ else
+ attrs = DictionaryAttr::get(attributes, attributes[0].second.getContext());
}
/// Return the specified attribute if present, null otherwise.
-Attribute NamedAttributeList::get(StringRef name) const {
- for (auto elt : getAttrs())
- if (elt.first.is(name))
- return elt.second;
- return nullptr;
-}
Attribute NamedAttributeList::get(Identifier name) const {
- for (auto elt : getAttrs())
- if (elt.first == name)
- return elt.second;
- return nullptr;
+ return attrs ? attrs.get(name) : nullptr;
}
/// If the an attribute exists with the specified name, change it to the new
for (auto &elt : newAttrs)
if (elt.first == name) {
elt.second = value;
- attrs = AttributeListStorage::get(newAttrs);
+ attrs = DictionaryAttr::get(newAttrs, value.getContext());
return;
}
// Otherwise, add it.
newAttrs.push_back({name, value});
- attrs = AttributeListStorage::get(newAttrs);
+ attrs = DictionaryAttr::get(newAttrs, value.getContext());
}
/// Remove the attribute with the specified name if it exists. The return
newAttrs.reserve(origAttrs.size() - 1);
newAttrs.append(origAttrs.begin(), origAttrs.begin() + i);
newAttrs.append(origAttrs.begin() + i + 1, origAttrs.end());
- attrs = AttributeListStorage::get(newAttrs);
+ attrs = DictionaryAttr::get(newAttrs, newAttrs[0].second.getContext());
return RemoveResult::Removed;
}
}
return BoolAttr::get(value, context);
}
+DictionaryAttr Builder::getDictionaryAttr(ArrayRef<NamedAttribute> value) {
+ return DictionaryAttr::get(value, context);
+}
+
IntegerAttr Builder::getI64IntegerAttr(int64_t value) {
return IntegerAttr::get(getIntegerType(64), APInt(64, value));
}
struct BuiltinDialect : public Dialect {
BuiltinDialect(MLIRContext *context) : Dialect(/*name=*/"", context) {
addAttributes<AffineMapAttr, ArrayAttr, BoolAttr, DenseIntElementsAttr,
- DenseFPElementsAttr, FloatAttr, FunctionAttr, IntegerAttr,
- IntegerSetAttr, OpaqueAttr, OpaqueElementsAttr,
+ DenseFPElementsAttr, DictionaryAttr, FloatAttr, FunctionAttr,
+ IntegerAttr, IntegerSetAttr, OpaqueAttr, OpaqueElementsAttr,
SparseElementsAttr, SplatElementsAttr, StringAttr, TypeAttr,
UnitAttr>();
addTypes<ComplexType, FloatType, FunctionType, IndexType, IntegerType,
}
};
-struct AttributeListKeyInfo : DenseMapInfo<AttributeListStorage *> {
- // Array attributes are uniqued based on their elements.
- using KeyTy = ArrayRef<NamedAttribute>;
- using DenseMapInfo<AttributeListStorage *>::isEqual;
-
- static unsigned getHashValue(AttributeListStorage *key) {
- return getHashValue(KeyTy(key->getElements()));
- }
-
- static unsigned getHashValue(KeyTy key) {
- return hash_combine_range(key.begin(), key.end());
- }
-
- static bool isEqual(const KeyTy &lhs, const AttributeListStorage *rhs) {
- if (rhs == getEmptyKey() || rhs == getTombstoneKey())
- return false;
- return lhs == rhs->getElements();
- }
-};
-
struct CallSiteLocationKeyInfo : DenseMapInfo<CallSiteLocationStorage *> {
// Call locations are uniqued based on their held concret location
// and the caller location.
//===--------------------------------------------------------------------===//
StorageUniquer attributeUniquer;
- // Attribute list allocator and mutex for thread safety.
- llvm::BumpPtrAllocator attributeAllocator;
- llvm::sys::SmartRWMutex<true> attributeMutex;
-
- using AttributeListSet =
- DenseSet<AttributeListStorage *, AttributeListKeyInfo>;
- AttributeListSet attributeLists;
-
public:
MLIRContextImpl()
: filenames(locationAllocator), identifiers(identifierAllocator) {}
};
}
-/// Perform a three-way comparison between the names of the specified
-/// NamedAttributes.
-static int compareNamedAttributes(const NamedAttribute *lhs,
- const NamedAttribute *rhs) {
- return lhs->first.str().compare(rhs->first.str());
-}
-
-/// Given a list of NamedAttribute's, canonicalize the list (sorting
-/// by name) and return the unique'd result. Note that the empty list is
-/// represented with a null pointer.
-AttributeListStorage *
-AttributeListStorage::get(ArrayRef<NamedAttribute> attrs) {
- // We need to sort the element list to canonicalize it, but we also don't want
- // to do a ton of work in the super common case where the element list is
- // already sorted.
- SmallVector<NamedAttribute, 8> storage;
- switch (attrs.size()) {
- case 0:
- // An empty list is represented with a null pointer.
- return nullptr;
- case 1:
- // A single element is already sorted.
- break;
- case 2:
- // Don't invoke a general sort for two element case.
- if (attrs[0].first.str() > attrs[1].first.str()) {
- storage.push_back(attrs[1]);
- storage.push_back(attrs[0]);
- attrs = storage;
- }
- break;
- default:
- // Check to see they are sorted already.
- bool isSorted = true;
- for (unsigned i = 0, e = attrs.size() - 1; i != e; ++i) {
- if (attrs[i].first.str() > attrs[i + 1].first.str()) {
- isSorted = false;
- break;
- }
- }
- // If not, do a general sort.
- if (!isSorted) {
- storage.append(attrs.begin(), attrs.end());
- llvm::array_pod_sort(storage.begin(), storage.end(),
- compareNamedAttributes);
- attrs = storage;
- }
- }
-
- auto &impl = attrs[0].second.getContext()->getImpl();
-
- // Safely get or create an attribute instance.
- return safeGetOrCreate(impl.attributeLists, attrs, impl.attributeMutex, [&] {
- auto byteSize =
- AttributeListStorage::totalSizeToAlloc<NamedAttribute>(attrs.size());
- auto rawMem =
- impl.attributeAllocator.Allocate(byteSize, alignof(NamedAttribute));
-
- // Placement initialize the AggregateSymbolicValue.
- auto result = ::new (rawMem) AttributeListStorage(attrs.size());
- std::uninitialized_copy(attrs.begin(), attrs.end(),
- result->getTrailingObjects<NamedAttribute>());
- return result;
- });
-}
-
//===----------------------------------------------------------------------===//
// AffineMap uniquing
//===----------------------------------------------------------------------===//
/// | string-literal
/// | type
/// | `[` (attribute-value (`,` attribute-value)*)? `]`
+/// | `{` (attribute-entry (`,` attribute-entry)*)? `}`
/// | function-id `:` function-type
/// | (`splat` | `dense`) `<` (tensor-type | vector-type) `,`
/// attribute-value `>`
return builder.getStringAttr(val);
}
+ case Token::l_brace: {
+ SmallVector<NamedAttribute, 4> elements;
+ if (parseAttributeDict(elements))
+ return nullptr;
+ return builder.getDictionaryAttr(elements);
+ }
case Token::l_square: {
consumeToken(Token::l_square);
SmallVector<Attribute, 4> elements;
// CHECK: "foo"() {set12: [#set{{[0-9]+}}, #set{{[0-9]+}}]}
"foo"() {set12: [#set1, #set2]} : () -> ()
+ // CHECK: "foo"() {dictionary: {bool: true, fn: @ifinst}}
+ "foo"() {dictionary: {bool: true, fn: @ifinst}} : () -> ()
+
+ // Check that the dictionary attribute elements are sorted.
+ // CHECK: "foo"() {dictionary: {bar: false, bool: true, fn: @ifinst}}
+ "foo"() {dictionary: {fn: @ifinst, bar: false, bool: true}} : () -> ()
+
// CHECK: "foo"() {d: 1.000000e-09 : f64, func: [], i123: 7 : i64, if: "foo"} : () -> ()
"foo"() {if: "foo", func: [], i123: 7, d: 1.e-9} : () -> ()