//===- TypeID.h - TypeID RTTI class -----------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file contains a definition of the TypeID class. This provides a non // RTTI mechanism for producing unique type IDs in LLVM. // //===----------------------------------------------------------------------===// #ifndef MLIR_SUPPORT_TYPEID_H #define MLIR_SUPPORT_TYPEID_H #include "mlir/Support/LLVM.h" #include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/PointerLikeTypeTraits.h" #include "llvm/Support/TypeName.h" namespace mlir { //===----------------------------------------------------------------------===// // TypeID //===----------------------------------------------------------------------===// /// This class provides an efficient unique identifier for a specific C++ type. /// This allows for a C++ type to be compared, hashed, and stored in an opaque /// context. This class is similar in some ways to std::type_index, but can be /// used for any type. For example, this class could be used to implement LLVM /// style isa/dyn_cast functionality for a type hierarchy: /// /// struct Base { /// Base(TypeID typeID) : typeID(typeID) {} /// TypeID typeID; /// }; /// /// struct DerivedA : public Base { /// DerivedA() : Base(TypeID::get<DerivedA>()) {} /// /// static bool classof(const Base *base) { /// return base->typeID == TypeID::get<DerivedA>(); /// } /// }; /// /// void foo(Base *base) { /// if (DerivedA *a = llvm::dyn_cast<DerivedA>(base)) /// ... /// } /// /// C++ RTTI is a notoriously difficult topic; given the nature of shared /// libraries many different approaches fundamentally break down in either the /// area of support (i.e. only certain types of classes are supported), or in /// terms of performance (e.g. by using string comparison). This class intends /// to strike a balance between performance and the setup required to enable its /// use. /// /// Assume we are adding support for some class Foo, below are the set of ways /// in which a given c++ type may be supported: /// /// * Explicitly via `MLIR_DECLARE_EXPLICIT_TYPE_ID` and /// `MLIR_DEFINE_EXPLICIT_TYPE_ID` /// /// - This method explicitly defines the type ID for a given type using the /// given macros. These should be placed at the top-level of the file (i.e. /// not within any namespace or class). This is the most effective and /// efficient method, but requires explicit annotations for each type. /// /// Example: /// /// // Foo.h /// MLIR_DECLARE_EXPLICIT_TYPE_ID(Foo); /// /// // Foo.cpp /// MLIR_DEFINE_EXPLICIT_TYPE_ID(Foo); /// /// * Explicitly via `MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID` /// - This method explicitly defines the type ID for a given type by /// annotating the class directly. This has similar effectiveness and /// efficiency to the above method, but should only be used on internal /// classes; i.e. those with definitions constrained to a specific library /// (generally classes in anonymous namespaces). /// /// Example: /// /// namespace { /// class Foo { /// public: /// MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(Foo) /// }; /// } // namespace /// /// * Implicitly via a fallback using the type name /// - This method implicitly defines a type ID for a given type by using the /// type name. This method requires nothing explicitly from the user, but /// pays additional access and initialization cost. Given that this method /// uses the name of the type, it may not be used for types defined in /// anonymous namespaces (which is asserted when it can be detected). String /// names do not provide any guarantees on uniqueness in these contexts. /// class TypeID { … }; /// Enable hashing TypeID. inline ::llvm::hash_code hash_value(TypeID id) { … } //===----------------------------------------------------------------------===// // TypeIDResolver //===----------------------------------------------------------------------===// namespace detail { /// This class provides a fallback for resolving TypeIDs. It uses the string /// name of the type to perform the resolution, and as such does not allow the /// use of classes defined in "anonymous" contexts. class FallbackTypeIDResolver { … }; /// This class provides a resolver for getting the ID for a given class T. This /// allows for the derived type to specialize its resolution behavior. The /// default implementation uses the string name of the type to resolve the ID. /// This provides a strong definition, but at the cost of performance (we need /// to do an initial lookup) and is not usable by classes defined in anonymous /// contexts. /// /// TODO: The use of the type name is only necessary when building in the /// presence of shared libraries. We could add a build flag that guarantees /// "static"-like environments and switch this to a more optimal implementation /// when that is enabled. template <typename T, typename Enable = void> class TypeIDResolver : public FallbackTypeIDResolver { … }; /// This class provides utilities for resolving the TypeID of a class that /// provides a `static TypeID resolveTypeID()` method. This allows for /// simplifying situations when the class can resolve the ID itself. This /// functionality is separated from the corresponding `TypeIDResolver` /// specialization below to enable referencing it more easily in different /// contexts. struct InlineTypeIDResolver { … }; /// This class provides a resolver for getting the ID for a given class T, when /// the class provides a `static TypeID resolveTypeID()` method. This allows for /// simplifying situations when the class can resolve the ID itself. TypeIDResolver<T, std::enable_if_t<InlineTypeIDResolver::has_resolve_typeid<T>::value>>; } // namespace detail template <typename T> TypeID TypeID::get() { … } template <template <typename> class Trait> TypeID TypeID::get() { … } // Declare/define an explicit specialization for TypeID: this forces the // compiler to emit a strong definition for a class and controls which // translation unit and shared object will actually have it. // This can be useful to turn to a link-time failure what would be in other // circumstances a hard-to-catch runtime bug when a TypeID is hidden in two // different shared libraries and instances of the same class only gets the same // TypeID inside a given DSO. #define MLIR_DECLARE_EXPLICIT_TYPE_ID(CLASS_NAME) … #define MLIR_DEFINE_EXPLICIT_TYPE_ID(CLASS_NAME) … // Declare/define an explicit, **internal**, specialization of TypeID for the // given class. This is useful for providing an explicit specialization of // TypeID for a class that is known to be internal to a specific library. It // should be placed within a public section of the declaration of the class. #define MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(CLASS_NAME) … //===----------------------------------------------------------------------===// // TypeIDAllocator //===----------------------------------------------------------------------===// /// This class provides a way to define new TypeIDs at runtime. /// When the allocator is destructed, all allocated TypeIDs become invalid and /// therefore should not be used. class TypeIDAllocator { … }; //===----------------------------------------------------------------------===// // SelfOwningTypeID //===----------------------------------------------------------------------===// /// Defines a TypeID for each instance of this class by using a pointer to the /// instance. Thus, the copy and move constructor are deleted. /// Note: We align by 8 to match the alignment of TypeID::Storage, as we treat /// an instance of this class similarly to TypeID::Storage. class alignas(8) SelfOwningTypeID { … }; } // namespace mlir //===----------------------------------------------------------------------===// // Builtin TypeIDs //===----------------------------------------------------------------------===// /// Explicitly register a set of "builtin" types. MLIR_DECLARE_EXPLICIT_TYPE_ID(void) namespace llvm { template <> struct DenseMapInfo<mlir::TypeID> { … }; /// We align TypeID::Storage by 8, so allow LLVM to steal the low bits. template <> struct PointerLikeTypeTraits<mlir::TypeID> { … }; } // namespace llvm #endif // MLIR_SUPPORT_TYPEID_H