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