MLIR 22.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
26namespace 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///
107class 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
114public:
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
142private:
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.
152inline ::llvm::hash_code hash_value(TypeID id) {
154}
155
156//===----------------------------------------------------------------------===//
157// TypeIDResolver
158//===----------------------------------------------------------------------===//
159
160namespace 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.
165protected:
166 /// Register an implicit type ID for the given type name.
167 LLVM_ALWAYS_EXPORT static TypeID registerImplicitTypeID(StringRef name);
168};
169
170template <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
185template <typename T>
186constexpr 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.
202template <typename T, typename Enable = void>
204public:
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.
234template <typename T>
236 T, std::enable_if_t<InlineTypeIDResolver::has_resolve_typeid<T>::value>> {
237public:
241};
242} // namespace detail
243
244template <typename T>
248template <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.
350public:
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
355private:
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.
369class alignas(8) SelfOwningTypeID {
370public:
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
394namespace llvm {
395template <>
396struct DenseMapInfo<mlir::TypeID> {
397 static inline mlir::TypeID getEmptyKey() {
400 }
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.
412template <>
413struct 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
lhs
#define MLIR_DECLARE_EXPLICIT_SELF_OWNING_TYPE_ID(CLASS_NAME)
Definition TypeID.h:262
SelfOwningTypeID(SelfOwningTypeID &&)=delete
TypeID getTypeID() const
Return the TypeID owned by this object.
Definition TypeID.h:381
SelfOwningTypeID & operator=(const SelfOwningTypeID &)=delete
SelfOwningTypeID & operator=(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
static TypeID getFromOpaquePointer(const void *pointer)
Definition TypeID.h:135
friend::llvm::hash_code hash_value(TypeID id)
Enable hashing TypeID.
friend class TypeIDAllocator
Definition TypeID.h:148
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:81
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.
llvm::DenseMapInfo< T, Enable > DenseMapInfo
Definition LLVM.h:122
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
static constexpr int NumLowBitsAvailable
Definition TypeID.h:420
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
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