1 //===- AttributeSupport.h ---------------------------------------*- C++ -*-===// 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 defines support types for registering dialect extended attributes. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #ifndef MLIR_IR_ATTRIBUTESUPPORT_H 14 #define MLIR_IR_ATTRIBUTESUPPORT_H 15 16 #include "mlir/IR/MLIRContext.h" 17 #include "mlir/IR/StorageUniquerSupport.h" 18 #include "mlir/IR/Types.h" 19 #include "llvm/ADT/PointerIntPair.h" 20 #include "llvm/ADT/Twine.h" 21 22 namespace mlir { 23 class MLIRContext; 24 class Type; 25 26 //===----------------------------------------------------------------------===// 27 // AbstractAttribute 28 //===----------------------------------------------------------------------===// 29 30 /// This class contains all of the static information common to all instances of 31 /// a registered Attribute. 32 class AbstractAttribute { 33 public: 34 using HasTraitFn = llvm::unique_function<bool(TypeID) const>; 35 36 /// Look up the specified abstract attribute in the MLIRContext and return a 37 /// reference to it. 38 static const AbstractAttribute &lookup(TypeID typeID, MLIRContext *context); 39 40 /// This method is used by Dialect objects when they register the list of 41 /// attributes they contain. 42 template <typename T> get(Dialect & dialect)43 static AbstractAttribute get(Dialect &dialect) { 44 return AbstractAttribute(dialect, T::getInterfaceMap(), T::getHasTraitFn(), 45 T::getTypeID()); 46 } 47 48 /// This method is used by Dialect objects to register attributes with 49 /// custom TypeIDs. 50 /// The use of this method is in general discouraged in favor of 51 /// 'get<CustomAttribute>(dialect)'. get(Dialect & dialect,detail::InterfaceMap && interfaceMap,HasTraitFn && hasTrait,TypeID typeID)52 static AbstractAttribute get(Dialect &dialect, 53 detail::InterfaceMap &&interfaceMap, 54 HasTraitFn &&hasTrait, TypeID typeID) { 55 return AbstractAttribute(dialect, std::move(interfaceMap), 56 std::move(hasTrait), typeID); 57 } 58 59 /// Return the dialect this attribute was registered to. getDialect()60 Dialect &getDialect() const { return const_cast<Dialect &>(dialect); } 61 62 /// Returns an instance of the concept object for the given interface if it 63 /// was registered to this attribute, null otherwise. This should not be used 64 /// directly. 65 template <typename T> getInterface()66 typename T::Concept *getInterface() const { 67 return interfaceMap.lookup<T>(); 68 } 69 70 /// Returns true if the attribute has the interface with the given ID 71 /// registered. hasInterface(TypeID interfaceID)72 bool hasInterface(TypeID interfaceID) const { 73 return interfaceMap.contains(interfaceID); 74 } 75 76 /// Returns true if the attribute has a particular trait. 77 template <template <typename T> class Trait> hasTrait()78 bool hasTrait() const { 79 return hasTraitFn(TypeID::get<Trait>()); 80 } 81 82 /// Returns true if the attribute has a particular trait. hasTrait(TypeID traitID)83 bool hasTrait(TypeID traitID) const { return hasTraitFn(traitID); } 84 85 /// Return the unique identifier representing the concrete attribute class. getTypeID()86 TypeID getTypeID() const { return typeID; } 87 88 private: AbstractAttribute(Dialect & dialect,detail::InterfaceMap && interfaceMap,HasTraitFn && hasTrait,TypeID typeID)89 AbstractAttribute(Dialect &dialect, detail::InterfaceMap &&interfaceMap, 90 HasTraitFn &&hasTrait, TypeID typeID) 91 : dialect(dialect), interfaceMap(std::move(interfaceMap)), 92 hasTraitFn(std::move(hasTrait)), typeID(typeID) {} 93 94 /// Give StorageUserBase access to the mutable lookup. 95 template <typename ConcreteT, typename BaseT, typename StorageT, 96 typename UniquerT, template <typename T> class... Traits> 97 friend class detail::StorageUserBase; 98 99 /// Look up the specified abstract attribute in the MLIRContext and return a 100 /// (mutable) pointer to it. Return a null pointer if the attribute could not 101 /// be found in the context. 102 static AbstractAttribute *lookupMutable(TypeID typeID, MLIRContext *context); 103 104 /// This is the dialect that this attribute was registered to. 105 const Dialect &dialect; 106 107 /// This is a collection of the interfaces registered to this attribute. 108 detail::InterfaceMap interfaceMap; 109 110 /// Function to check if the attribute has a particular trait. 111 HasTraitFn hasTraitFn; 112 113 /// The unique identifier of the derived Attribute class. 114 const TypeID typeID; 115 }; 116 117 //===----------------------------------------------------------------------===// 118 // AttributeStorage 119 //===----------------------------------------------------------------------===// 120 121 namespace detail { 122 class AttributeUniquer; 123 } // namespace detail 124 125 /// Base storage class appearing in an attribute. Derived storage classes should 126 /// only be constructed within the context of the AttributeUniquer. 127 class alignas(8) AttributeStorage : public StorageUniquer::BaseStorage { 128 friend detail::AttributeUniquer; 129 friend StorageUniquer; 130 131 public: 132 /// Get the type of this attribute. getType()133 Type getType() const { return type; } 134 135 /// Return the abstract descriptor for this attribute. getAbstractAttribute()136 const AbstractAttribute &getAbstractAttribute() const { 137 assert(abstractAttribute && "Malformed attribute storage object."); 138 return *abstractAttribute; 139 } 140 141 protected: 142 /// Construct a new attribute storage instance with the given type. 143 /// Note: All attributes require a valid type. If no type is provided here, 144 /// the type of the attribute will automatically default to NoneType 145 /// upon initialization in the uniquer. type(type)146 AttributeStorage(Type type = nullptr) : type(type) {} 147 148 /// Set the type of this attribute. setType(Type newType)149 void setType(Type newType) { type = newType; } 150 151 /// Set the abstract attribute for this storage instance. This is used by the 152 /// AttributeUniquer when initializing a newly constructed storage object. initializeAbstractAttribute(const AbstractAttribute & abstractAttr)153 void initializeAbstractAttribute(const AbstractAttribute &abstractAttr) { 154 abstractAttribute = &abstractAttr; 155 } 156 157 /// Default initialization for attribute storage classes that require no 158 /// additional initialization. initialize(MLIRContext * context)159 void initialize(MLIRContext *context) {} 160 161 private: 162 /// The type of the attribute value. 163 Type type; 164 165 /// The abstract descriptor for this attribute. 166 const AbstractAttribute *abstractAttribute = nullptr; 167 }; 168 169 /// Default storage type for attributes that require no additional 170 /// initialization or storage. 171 using DefaultAttributeStorage = AttributeStorage; 172 173 //===----------------------------------------------------------------------===// 174 // AttributeStorageAllocator 175 //===----------------------------------------------------------------------===// 176 177 // This is a utility allocator used to allocate memory for instances of derived 178 // Attributes. 179 using AttributeStorageAllocator = StorageUniquer::StorageAllocator; 180 181 //===----------------------------------------------------------------------===// 182 // AttributeUniquer 183 //===----------------------------------------------------------------------===// 184 namespace detail { 185 // A utility class to get, or create, unique instances of attributes within an 186 // MLIRContext. This class manages all creation and uniquing of attributes. 187 class AttributeUniquer { 188 public: 189 /// Get an uniqued instance of an attribute T. 190 template <typename T, typename... Args> get(MLIRContext * ctx,Args &&...args)191 static T get(MLIRContext *ctx, Args &&...args) { 192 return getWithTypeID<T, Args...>(ctx, T::getTypeID(), 193 std::forward<Args>(args)...); 194 } 195 196 /// Get an uniqued instance of a parametric attribute T. 197 /// The use of this method is in general discouraged in favor of 198 /// 'get<T, Args>(ctx, args)'. 199 template <typename T, typename... Args> 200 static typename std::enable_if_t< 201 !std::is_same<typename T::ImplType, AttributeStorage>::value, T> getWithTypeID(MLIRContext * ctx,TypeID typeID,Args &&...args)202 getWithTypeID(MLIRContext *ctx, TypeID typeID, Args &&...args) { 203 #ifndef NDEBUG 204 if (!ctx->getAttributeUniquer().isParametricStorageInitialized(typeID)) 205 llvm::report_fatal_error( 206 llvm::Twine("can't create Attribute '") + llvm::getTypeName<T>() + 207 "' because storage uniquer isn't initialized: the dialect was likely " 208 "not loaded, or the attribute wasn't added with addAttributes<...>() " 209 "in the Dialect::initialize() method."); 210 #endif 211 return ctx->getAttributeUniquer().get<typename T::ImplType>( 212 [typeID, ctx](AttributeStorage *storage) { 213 initializeAttributeStorage(storage, ctx, typeID); 214 215 // Execute any additional attribute storage initialization with the 216 // context. 217 static_cast<typename T::ImplType *>(storage)->initialize(ctx); 218 }, 219 typeID, std::forward<Args>(args)...); 220 } 221 /// Get an uniqued instance of a singleton attribute T. 222 /// The use of this method is in general discouraged in favor of 223 /// 'get<T, Args>(ctx, args)'. 224 template <typename T> 225 static typename std::enable_if_t< 226 std::is_same<typename T::ImplType, AttributeStorage>::value, T> getWithTypeID(MLIRContext * ctx,TypeID typeID)227 getWithTypeID(MLIRContext *ctx, TypeID typeID) { 228 #ifndef NDEBUG 229 if (!ctx->getAttributeUniquer().isSingletonStorageInitialized(typeID)) 230 llvm::report_fatal_error( 231 llvm::Twine("can't create Attribute '") + llvm::getTypeName<T>() + 232 "' because storage uniquer isn't initialized: the dialect was likely " 233 "not loaded, or the attribute wasn't added with addAttributes<...>() " 234 "in the Dialect::initialize() method."); 235 #endif 236 return ctx->getAttributeUniquer().get<typename T::ImplType>(typeID); 237 } 238 239 template <typename T, typename... Args> mutate(MLIRContext * ctx,typename T::ImplType * impl,Args &&...args)240 static LogicalResult mutate(MLIRContext *ctx, typename T::ImplType *impl, 241 Args &&...args) { 242 assert(impl && "cannot mutate null attribute"); 243 return ctx->getAttributeUniquer().mutate(T::getTypeID(), impl, 244 std::forward<Args>(args)...); 245 } 246 247 /// Register an attribute instance T with the uniquer. 248 template <typename T> registerAttribute(MLIRContext * ctx)249 static void registerAttribute(MLIRContext *ctx) { 250 registerAttribute<T>(ctx, T::getTypeID()); 251 } 252 253 /// Register a parametric attribute instance T with the uniquer. 254 /// The use of this method is in general discouraged in favor of 255 /// 'registerAttribute<T>(ctx)'. 256 template <typename T> 257 static typename std::enable_if_t< 258 !std::is_same<typename T::ImplType, AttributeStorage>::value> registerAttribute(MLIRContext * ctx,TypeID typeID)259 registerAttribute(MLIRContext *ctx, TypeID typeID) { 260 ctx->getAttributeUniquer() 261 .registerParametricStorageType<typename T::ImplType>(typeID); 262 } 263 /// Register a singleton attribute instance T with the uniquer. 264 /// The use of this method is in general discouraged in favor of 265 /// 'registerAttribute<T>(ctx)'. 266 template <typename T> 267 static typename std::enable_if_t< 268 std::is_same<typename T::ImplType, AttributeStorage>::value> registerAttribute(MLIRContext * ctx,TypeID typeID)269 registerAttribute(MLIRContext *ctx, TypeID typeID) { 270 ctx->getAttributeUniquer() 271 .registerSingletonStorageType<typename T::ImplType>( 272 typeID, [ctx, typeID](AttributeStorage *storage) { 273 initializeAttributeStorage(storage, ctx, typeID); 274 }); 275 } 276 277 private: 278 /// Initialize the given attribute storage instance. 279 static void initializeAttributeStorage(AttributeStorage *storage, 280 MLIRContext *ctx, TypeID attrID); 281 }; 282 } // namespace detail 283 284 } // namespace mlir 285 286 #endif 287