MLIR  21.0.0git
TypeID.h
Go to the documentation of this file.
1 //===- TypeID.h - TypeID RTTI class -----------------------------*- 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 contains a definition of the TypeID class. This provides a non
10 // RTTI mechanism for producing unique type IDs in LLVM.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef MLIR_SUPPORT_TYPEID_H
15 #define MLIR_SUPPORT_TYPEID_H
16 
17 #include "mlir/Support/LLVM.h"
18 #include "llvm/ADT/DenseMapInfo.h"
19 #include "llvm/ADT/Hashing.h"
20 #include "llvm/ADT/STLExtras.h"
21 #include "llvm/Support/Allocator.h"
22 #include "llvm/Support/Compiler.h"
23 #include "llvm/Support/PointerLikeTypeTraits.h"
24 #include "llvm/Support/TypeName.h"
25 
26 namespace mlir {
27 //===----------------------------------------------------------------------===//
28 // TypeID
29 //===----------------------------------------------------------------------===//
30 
31 /// This class provides an efficient unique identifier for a specific C++ type.
32 /// This allows for a C++ type to be compared, hashed, and stored in an opaque
33 /// context. This class is similar in some ways to std::type_index, but can be
34 /// used for any type. For example, this class could be used to implement LLVM
35 /// style isa/dyn_cast functionality for a type hierarchy:
36 ///
37 /// struct Base {
38 /// Base(TypeID typeID) : typeID(typeID) {}
39 /// TypeID typeID;
40 /// };
41 ///
42 /// struct DerivedA : public Base {
43 /// DerivedA() : Base(TypeID::get<DerivedA>()) {}
44 ///
45 /// static bool classof(const Base *base) {
46 /// return base->typeID == TypeID::get<DerivedA>();
47 /// }
48 /// };
49 ///
50 /// void foo(Base *base) {
51 /// if (DerivedA *a = llvm::dyn_cast<DerivedA>(base))
52 /// ...
53 /// }
54 ///
55 /// C++ RTTI is a notoriously difficult topic; given the nature of shared
56 /// libraries many different approaches fundamentally break down in either the
57 /// area of support (i.e. only certain types of classes are supported), or in
58 /// terms of performance (e.g. by using string comparison). This class intends
59 /// to strike a balance between performance and the setup required to enable its
60 /// use.
61 ///
62 /// Assume we are adding support for some class Foo, below are the set of ways
63 /// in which a given c++ type may be supported:
64 ///
65 /// * Explicitly via `MLIR_DECLARE_EXPLICIT_TYPE_ID` and
66 /// `MLIR_DEFINE_EXPLICIT_TYPE_ID`
67 ///
68 /// - This method explicitly defines the type ID for a given type using the
69 /// given macros. These should be placed at the top-level of the file (i.e.
70 /// not within any namespace or class). This is the most effective and
71 /// efficient method, but requires explicit annotations for each type.
72 ///
73 /// Example:
74 ///
75 /// // Foo.h
76 /// MLIR_DECLARE_EXPLICIT_TYPE_ID(Foo);
77 ///
78 /// // Foo.cpp
79 /// MLIR_DEFINE_EXPLICIT_TYPE_ID(Foo);
80 ///
81 /// * Explicitly via `MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID`
82 /// - This method explicitly defines the type ID for a given type by
83 /// annotating the class directly. This has similar effectiveness and
84 /// efficiency to the above method, but should only be used on internal
85 /// classes; i.e. those with definitions constrained to a specific library
86 /// (generally classes in anonymous namespaces).
87 ///
88 /// Example:
89 ///
90 /// namespace {
91 /// class Foo {
92 /// public:
93 /// MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(Foo)
94 /// };
95 /// } // namespace
96 ///
97 /// * Implicitly via a fallback using the type name
98 /// - This method implicitly defines a type ID for a given type by using the
99 /// type name. This method requires nothing explicitly from the user, but
100 /// pays additional access and initialization cost. Given that this method
101 /// uses the name of the type, it may not be used for types defined in
102 /// anonymous namespaces (which is asserted when it can be detected). String
103 /// names do not provide any guarantees on uniqueness in these contexts.
104 /// - This behavior may be forced even in the presence of explicit declarations
105 /// by specifying `MLIR_USE_FALLBACK_TYPE_IDS`.
106 ///
107 class TypeID {
108  /// This class represents the storage of a type info object.
109  /// Note: We specify an explicit alignment here to allow use with
110  /// PointerIntPair and other utilities/data structures that require a known
111  /// pointer alignment.
112  struct alignas(8) Storage {};
113 
114 public:
115  TypeID() : TypeID(get<void>()) {}
116 
117  /// Comparison operations.
118  inline bool operator==(const TypeID &other) const {
119  return storage == other.storage;
120  }
121  inline bool operator!=(const TypeID &other) const {
122  return !(*this == other);
123  }
124 
125  /// Construct a type info object for the given type T.
126  template <typename T>
127  static TypeID get();
128  template <template <typename> class Trait>
129  static TypeID get();
130 
131  /// Methods for supporting PointerLikeTypeTraits.
132  const void *getAsOpaquePointer() const {
133  return static_cast<const void *>(storage);
134  }
135  static TypeID getFromOpaquePointer(const void *pointer) {
136  return TypeID(reinterpret_cast<const Storage *>(pointer));
137  }
138 
139  /// Enable hashing TypeID.
140  friend ::llvm::hash_code hash_value(TypeID id);
141 
142 private:
143  TypeID(const Storage *storage) : storage(storage) {}
144 
145  /// The storage of this type info object.
146  const Storage *storage;
147 
148  friend class TypeIDAllocator;
149 };
150 
151 /// Enable hashing TypeID.
152 inline ::llvm::hash_code hash_value(TypeID id) {
154 }
155 
156 //===----------------------------------------------------------------------===//
157 // TypeIDResolver
158 //===----------------------------------------------------------------------===//
159 
160 namespace detail {
161 /// This class provides a fallback for resolving TypeIDs. It uses the string
162 /// name of the type to perform the resolution, and as such does not allow the
163 /// use of classes defined in "anonymous" contexts.
165 protected:
166  /// Register an implicit type ID for the given type name.
167  LLVM_ALWAYS_EXPORT static TypeID registerImplicitTypeID(StringRef name);
168 };
169 
170 template <typename T>
172  /// Trait to check if `U` is fully resolved. We use this to verify that `T` is
173  /// fully resolved when trying to resolve a TypeID. We don't technically need
174  /// to have the full definition of `T` for the fallback, but it does help
175  /// prevent situations where a forward declared type uses this fallback even
176  /// though there is a strong definition for the TypeID in the location where
177  /// `T` is defined.
178  template <typename U>
179  using is_fully_resolved_trait = decltype(sizeof(U));
180  template <typename U>
181  using is_fully_resolved = llvm::is_detected<is_fully_resolved_trait, U>;
182  static constexpr bool value = is_fully_resolved<T>::value;
183 };
184 
185 template <typename T>
186 constexpr bool is_fully_resolved() {
187  /// Helper function for is_fully_resolved_t.
189 }
190 
191 /// This class provides a resolver for getting the ID for a given class T. This
192 /// allows for the derived type to specialize its resolution behavior. The
193 /// default implementation uses the string name of the type to resolve the ID.
194 /// This provides a strong definition, but at the cost of performance (we need
195 /// to do an initial lookup) and is not usable by classes defined in anonymous
196 /// contexts.
197 ///
198 /// TODO: The use of the type name is only necessary when building in the
199 /// presence of shared libraries. We could add a build flag that guarantees
200 /// "static"-like environments and switch this to a more optimal implementation
201 /// when that is enabled.
202 template <typename T, typename Enable = void>
204 public:
206  static_assert(is_fully_resolved<T>(),
207  "TypeID::get<> requires the complete definition of `T`");
208  static TypeID id = registerImplicitTypeID(llvm::getTypeName<T>());
209  return id;
210  }
211 };
212 
213 /// This class provides utilities for resolving the TypeID of a class that
214 /// provides a `static TypeID resolveTypeID()` method. This allows for
215 /// simplifying situations when the class can resolve the ID itself. This
216 /// functionality is separated from the corresponding `TypeIDResolver`
217 /// specialization below to enable referencing it more easily in different
218 /// contexts.
220  /// Trait to check if `T` provides a static `resolveTypeID` method.
221  template <typename T>
222  using has_resolve_typeid_trait = decltype(T::resolveTypeID());
223  template <typename T>
224  using has_resolve_typeid = llvm::is_detected<has_resolve_typeid_trait, T>;
225 
226  template <typename T>
228  return T::resolveTypeID();
229  }
230 };
231 /// This class provides a resolver for getting the ID for a given class T, when
232 /// the class provides a `static TypeID resolveTypeID()` method. This allows for
233 /// simplifying situations when the class can resolve the ID itself.
234 template <typename T>
236  T, std::enable_if_t<InlineTypeIDResolver::has_resolve_typeid<T>::value>> {
237 public:
239  return InlineTypeIDResolver::resolveTypeID<T>();
240  }
241 };
242 } // namespace detail
243 
244 template <typename T>
247 }
248 template <template <typename> class Trait>
250  // An empty class used to simplify the use of Trait types.
251  struct Empty {};
252  return TypeID::get<Trait<Empty>>();
253 }
254 
255 // Declare/define an explicit specialization for TypeID: this forces the
256 // compiler to emit a strong definition for a class and controls which
257 // translation unit and shared object will actually have it.
258 // This can be useful to turn to a link-time failure what would be in other
259 // circumstances a hard-to-catch runtime bug when a TypeID is hidden in two
260 // different shared libraries and instances of the same class only gets the same
261 // TypeID inside a given DSO.
262 #define MLIR_DECLARE_EXPLICIT_SELF_OWNING_TYPE_ID(CLASS_NAME) \
263  namespace mlir { \
264  namespace detail { \
265  template <> \
266  class TypeIDResolver<CLASS_NAME> { \
267  public: \
268  static TypeID resolveTypeID() { return id; } \
269  \
270  private: \
271  static SelfOwningTypeID id; \
272  }; \
273  } /* namespace detail */ \
274  } /* namespace mlir */
275 
276 #define MLIR_DEFINE_EXPLICIT_SELF_OWNING_TYPE_ID(CLASS_NAME) \
277  namespace mlir { \
278  namespace detail { \
279  SelfOwningTypeID TypeIDResolver<CLASS_NAME>::id = {}; \
280  } /* namespace detail */ \
281  } /* namespace mlir */
282 
283 
284 /// Declare/define an explicit specialization for TypeID using the string
285 /// comparison fallback. This is useful for complex shared library setups
286 /// where it may be difficult to agree on a source of truth for specific
287 /// type ID resolution. As long as there is a single resolution for
288 /// registerImplicitTypeID, all type IDs can be reference a shared
289 /// registration. This way types which are logically shared across multiple
290 /// DSOs can have the same type ID, even if their definitions are duplicated.
291 #define MLIR_DECLARE_EXPLICIT_FALLBACK_TYPE_ID(CLASS_NAME) \
292  namespace mlir { \
293  namespace detail { \
294  template <> \
295  class TypeIDResolver<CLASS_NAME> : public FallbackTypeIDResolver { \
296  public: \
297  static TypeID resolveTypeID() { \
298  static_assert(is_fully_resolved<CLASS_NAME>(), \
299  "TypeID::get<> requires the complete definition of `T`"); \
300  static TypeID id = \
301  registerImplicitTypeID(llvm::getTypeName<CLASS_NAME>()); \
302  return id; \
303  } \
304  }; \
305  } /* namespace detail */ \
306  } /* namespace mlir */
307 
308 #define MLIR_DEFINE_EXPLICIT_FALLBACK_TYPE_ID(CLASS_NAME)
309 
310 
311 #ifndef MLIR_USE_FALLBACK_TYPE_IDS
312 #define MLIR_USE_FALLBACK_TYPE_IDS false
313 #endif
314 
315 #if MLIR_USE_FALLBACK_TYPE_IDS
316 #define MLIR_DECLARE_EXPLICIT_TYPE_ID(CLASS_NAME) \
317  MLIR_DECLARE_EXPLICIT_FALLBACK_TYPE_ID(CLASS_NAME)
318 #define MLIR_DEFINE_EXPLICIT_TYPE_ID(CLASS_NAME) \
319  MLIR_DEFINE_EXPLICIT_FALLBACK_TYPE_ID(CLASS_NAME)
320 #else
321 #define MLIR_DECLARE_EXPLICIT_TYPE_ID(CLASS_NAME) \
322  MLIR_DECLARE_EXPLICIT_SELF_OWNING_TYPE_ID(CLASS_NAME)
323 #define MLIR_DEFINE_EXPLICIT_TYPE_ID(CLASS_NAME) \
324  MLIR_DEFINE_EXPLICIT_SELF_OWNING_TYPE_ID(CLASS_NAME)
325 #endif /* MLIR_USE_FALLBACK_TYPE_IDS */
326 
327 // Declare/define an explicit, **internal**, specialization of TypeID for the
328 // given class. This is useful for providing an explicit specialization of
329 // TypeID for a class that is known to be internal to a specific library. It
330 // should be placed within a public section of the declaration of the class.
331 #define MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(CLASS_NAME) \
332  static ::mlir::TypeID resolveTypeID() { \
333  static ::mlir::SelfOwningTypeID id; \
334  return id; \
335  } \
336  static_assert( \
337  ::mlir::detail::InlineTypeIDResolver::has_resolve_typeid< \
338  CLASS_NAME>::value, \
339  "`MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID` must be placed in a " \
340  "public section of `" #CLASS_NAME "`");
341 
342 //===----------------------------------------------------------------------===//
343 // TypeIDAllocator
344 //===----------------------------------------------------------------------===//
345 
346 /// This class provides a way to define new TypeIDs at runtime.
347 /// When the allocator is destructed, all allocated TypeIDs become invalid and
348 /// therefore should not be used.
350 public:
351  /// Allocate a new TypeID, that is ensured to be unique for the lifetime
352  /// of the TypeIDAllocator.
353  TypeID allocate() { return TypeID(ids.Allocate()); }
354 
355 private:
356  /// The TypeIDs allocated are the addresses of the different storages.
357  /// Keeping those in memory ensure uniqueness of the TypeIDs.
358  llvm::SpecificBumpPtrAllocator<TypeID::Storage> ids;
359 };
360 
361 //===----------------------------------------------------------------------===//
362 // SelfOwningTypeID
363 //===----------------------------------------------------------------------===//
364 
365 /// Defines a TypeID for each instance of this class by using a pointer to the
366 /// instance. Thus, the copy and move constructor are deleted.
367 /// Note: We align by 8 to match the alignment of TypeID::Storage, as we treat
368 /// an instance of this class similarly to TypeID::Storage.
369 class alignas(8) SelfOwningTypeID {
370 public:
371  SelfOwningTypeID() = default;
376 
377  /// Implicitly converts to the owned TypeID.
378  operator TypeID() const { return getTypeID(); }
379 
380  /// Return the TypeID owned by this object.
382 };
383 
384 } // namespace mlir
385 
386 //===----------------------------------------------------------------------===//
387 // Builtin TypeIDs
388 //===----------------------------------------------------------------------===//
389 
390 /// Explicitly register a set of "builtin" types.
391 /// `void` must be self-owning, it can't be fully resolved.
393 
394 namespace llvm {
395 template <>
396 struct DenseMapInfo<mlir::TypeID> {
397  static inline mlir::TypeID getEmptyKey() {
398  void *pointer = llvm::DenseMapInfo<void *>::getEmptyKey();
399  return mlir::TypeID::getFromOpaquePointer(pointer);
400  }
401  static inline mlir::TypeID getTombstoneKey() {
403  return mlir::TypeID::getFromOpaquePointer(pointer);
404  }
405  static unsigned getHashValue(mlir::TypeID val) {
406  return mlir::hash_value(val);
407  }
408  static bool isEqual(mlir::TypeID lhs, mlir::TypeID rhs) { return lhs == rhs; }
409 };
410 
411 /// We align TypeID::Storage by 8, so allow LLVM to steal the low bits.
412 template <>
413 struct PointerLikeTypeTraits<mlir::TypeID> {
414  static inline void *getAsVoidPointer(mlir::TypeID info) {
415  return const_cast<void *>(info.getAsOpaquePointer());
416  }
417  static inline mlir::TypeID getFromVoidPointer(void *ptr) {
419  }
420  static constexpr int NumLowBitsAvailable = 3;
421 };
422 
423 } // namespace llvm
424 
425 #endif // MLIR_SUPPORT_TYPEID_H
#define MLIR_DECLARE_EXPLICIT_SELF_OWNING_TYPE_ID(CLASS_NAME)
Definition: TypeID.h:262
Defines a TypeID for each instance of this class by using a pointer to the instance.
Definition: TypeID.h:369
SelfOwningTypeID(SelfOwningTypeID &&)=delete
TypeID getTypeID() const
Return the TypeID owned by this object.
Definition: TypeID.h:381
SelfOwningTypeID & operator=(SelfOwningTypeID &&)=delete
SelfOwningTypeID & operator=(const SelfOwningTypeID &)=delete
SelfOwningTypeID(const SelfOwningTypeID &)=delete
This class provides a way to define new TypeIDs at runtime.
Definition: TypeID.h:349
TypeID allocate()
Allocate a new TypeID, that is ensured to be unique for the lifetime of the TypeIDAllocator.
Definition: TypeID.h:353
This class provides an efficient unique identifier for a specific C++ type.
Definition: TypeID.h:107
static TypeID get()
bool operator==(const TypeID &other) const
Comparison operations.
Definition: TypeID.h:118
static TypeID get()
Construct a type info object for the given type T.
Definition: TypeID.h:245
friend ::llvm::hash_code hash_value(TypeID id)
Enable hashing TypeID.
Definition: TypeID.h:152
static TypeID getFromOpaquePointer(const void *pointer)
Definition: TypeID.h:135
bool operator!=(const TypeID &other) const
Definition: TypeID.h:121
const void * getAsOpaquePointer() const
Methods for supporting PointerLikeTypeTraits.
Definition: TypeID.h:132
This class provides a fallback for resolving TypeIDs.
Definition: TypeID.h:164
static LLVM_ALWAYS_EXPORT TypeID registerImplicitTypeID(StringRef name)
Register an implicit type ID for the given type name.
Definition: TypeID.cpp:84
This class provides a resolver for getting the ID for a given class T.
Definition: TypeID.h:203
static TypeID resolveTypeID()
Definition: TypeID.h:205
The OpAsmOpInterface, see OpAsmInterface.td for more details.
Definition: CallGraph.h:229
constexpr bool is_fully_resolved()
Definition: TypeID.h:186
Include the generated interface declarations.
inline ::llvm::hash_code hash_value(AffineExpr arg)
Make AffineExpr hashable.
Definition: AffineExpr.h:247
static unsigned getHashValue(mlir::TypeID val)
Definition: TypeID.h:405
static bool isEqual(mlir::TypeID lhs, mlir::TypeID rhs)
Definition: TypeID.h:408
static mlir::TypeID getEmptyKey()
Definition: TypeID.h:397
static mlir::TypeID getTombstoneKey()
Definition: TypeID.h:401
static void * getAsVoidPointer(mlir::TypeID info)
Definition: TypeID.h:414
static mlir::TypeID getFromVoidPointer(void *ptr)
Definition: TypeID.h:417
This class provides utilities for resolving the TypeID of a class that provides a static TypeID resol...
Definition: TypeID.h:219
decltype(T::resolveTypeID()) has_resolve_typeid_trait
Trait to check if T provides a static resolveTypeID method.
Definition: TypeID.h:222
static TypeID resolveTypeID()
Definition: TypeID.h:227
llvm::is_detected< has_resolve_typeid_trait, T > has_resolve_typeid
Definition: TypeID.h:224
decltype(sizeof(U)) is_fully_resolved_trait
Trait to check if U is fully resolved.
Definition: TypeID.h:179
llvm::is_detected< is_fully_resolved_trait, U > is_fully_resolved
Definition: TypeID.h:181
static constexpr bool value
Definition: TypeID.h:182