Abstract the internal storage of the NamedAttributeList into a new attribute...
authorRiver Riddle <riverriddle@google.com>
Fri, 31 May 2019 16:24:48 +0000 (09:24 -0700)
committerMehdi Amini <joker.eph@gmail.com>
Sun, 2 Jun 2019 03:12:01 +0000 (20:12 -0700)
    The syntax is the same as top level attribute dictionaries:
       {sub_dictionary: {fn: @someFn, boolAttr: true}}

--

PiperOrigin-RevId: 250898950

mlir/g3doc/LangRef.md
mlir/include/mlir/IR/Attributes.h
mlir/include/mlir/IR/Builders.h
mlir/lib/IR/AsmPrinter.cpp
mlir/lib/IR/AttributeDetail.h
mlir/lib/IR/Attributes.cpp
mlir/lib/IR/Builders.cpp
mlir/lib/IR/MLIRContext.cpp
mlir/lib/Parser/Parser.cpp
mlir/test/IR/parser.mlir

index 83517c4..14ff81c 100644 (file)
@@ -693,6 +693,7 @@ Attributes values are represented by the following forms:
 attribute-value ::= affine-map-attribute
                   | array-attribute
                   | bool-attribute
+                  | dictionary-attribute
                   | elements-attribute
                   | integer-attribute
                   | integer-set-attribute
@@ -735,6 +736,18 @@ bool-attribute ::= bool-literal
 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:
index 8c2a949..3807637 100644 (file)
@@ -37,6 +37,7 @@ namespace detail {
 
 struct OpaqueAttributeStorage;
 struct BoolAttributeStorage;
+struct DictionaryAttributeStorage;
 struct IntegerAttributeStorage;
 struct FloatAttributeStorage;
 struct StringAttributeStorage;
@@ -51,8 +52,6 @@ struct DenseFPElementsAttributeStorage;
 struct OpaqueElementsAttributeStorage;
 struct SparseElementsAttributeStorage;
 
-class AttributeListStorage;
-
 } // namespace detail
 
 /// Attributes are known-constant values of operations and functions.
@@ -144,6 +143,7 @@ enum Kind {
   Unit = Attribute::FIRST_STANDARD_ATTR,
   Opaque,
   Bool,
+  Dictionary,
   Integer,
   Float,
   String,
@@ -310,11 +310,11 @@ public:
 
   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) {
@@ -322,6 +322,42 @@ public:
   }
 };
 
+/// 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> {
@@ -757,31 +793,30 @@ inline ::llvm::hash_code hash_value(Attribute arg) {
   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
@@ -795,7 +830,7 @@ public:
   RemoveResult remove(Identifier name);
 
 private:
-  detail::AttributeListStorage *attrs;
+  DictionaryAttr attrs;
 };
 
 } // end namespace mlir.
index e848966..b869dcd 100644 (file)
@@ -103,6 +103,7 @@ public:
 
   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);
index 4c056a1..b3e2649 100644 (file)
@@ -630,6 +630,15 @@ void ModulePrinter::printAttributeOptionalType(Attribute attr,
   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.
index d29c06c..09a7bd7 100644 (file)
@@ -229,6 +229,53 @@ struct ArrayAttributeStorage : public AttributeStorage {
   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;
@@ -403,36 +450,6 @@ struct SparseElementsAttributeStorage : public AttributeStorage {
   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
 
index e20d7b5..225fe41 100644 (file)
@@ -104,6 +104,98 @@ BoolAttr BoolAttr::get(bool value, MLIRContext *context) {
 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
 //===----------------------------------------------------------------------===//
 
@@ -675,10 +767,10 @@ void DenseIntElementsAttr::getValues(SmallVectorImpl<APInt> &values) const {
   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;
@@ -692,7 +784,7 @@ static ShapedType mappingHelper(
     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) {
@@ -709,8 +801,8 @@ DenseElementsAttr DenseIntElementsAttr::mapValues(
     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);
 }
@@ -746,8 +838,8 @@ DenseElementsAttr DenseFPElementsAttr::mapValues(
     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);
 }
@@ -862,37 +954,18 @@ NamedAttributeList::NamedAttributeList(ArrayRef<NamedAttribute> attributes) {
   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
@@ -906,13 +979,13 @@ void NamedAttributeList::set(Identifier name, Attribute value) {
   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
@@ -931,7 +1004,7 @@ auto NamedAttributeList::remove(Identifier name) -> RemoveResult {
       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;
     }
   }
index c6e84ff..4accfb5 100644 (file)
@@ -117,6 +117,10 @@ BoolAttr Builder::getBoolAttr(bool value) {
   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));
 }
index 1f7aca8..bf1ae96 100644 (file)
@@ -136,8 +136,8 @@ namespace {
 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,
@@ -198,26 +198,6 @@ struct IntegerSetKeyInfo : DenseMapInfo<IntegerSet> {
   }
 };
 
-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.
@@ -361,14 +341,6 @@ public:
   //===--------------------------------------------------------------------===//
   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) {}
@@ -720,72 +692,6 @@ AttributeUniquer::getInitFn(MLIRContext *ctx, const ClassID *const attrID) {
   };
 }
 
-/// 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
 //===----------------------------------------------------------------------===//
index f1a6601..10f80ed 100644 (file)
@@ -1025,6 +1025,7 @@ Attribute Parser::parseExtendedAttribute(Type type) {
 ///                    | string-literal
 ///                    | type
 ///                    | `[` (attribute-value (`,` attribute-value)*)? `]`
+///                    | `{` (attribute-entry (`,` attribute-entry)*)? `}`
 ///                    | function-id `:` function-type
 ///                    | (`splat` | `dense`) `<` (tensor-type | vector-type) `,`
 ///                      attribute-value `>`
@@ -1153,6 +1154,12 @@ Attribute Parser::parseAttribute(Type type) {
     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;
index 253dfd5..801eb00 100644 (file)
@@ -369,6 +369,13 @@ func @attributes() {
   // 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} : () -> ()