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