[mlir] Add MLIR_USE_FALLBACK_TYPE_IDS macro support for TypeID (#126999)

Adds a macro definition `MLIR_USE_FALLBACK_TYPE_IDS`. When this is
defined, the `MLIR_{DECLARE,DEFINE}_EXPLICIT_TYPE_ID` functions
explicitly fall back to string comparison.

This is useful for complex shared library setups
where it may be difficult to agree on a source of truth for specific
type ID resolution. As long as there is a single resolution for
`registerImplicitTypeID`, all type IDs can be reference a shared
registration. This way types which are logically shared across multiple
DSOs can have the same type ID, even if their definitions are
duplicated.
This commit is contained in:
Stef Lindall
2025-02-14 18:21:17 -08:00
committed by GitHub
parent e9fb239121
commit 5866032834
2 changed files with 77 additions and 18 deletions

View File

@@ -19,6 +19,7 @@
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/PointerLikeTypeTraits.h"
#include "llvm/Support/TypeName.h"
@@ -100,6 +101,8 @@ namespace mlir {
/// 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.
/// - This behavior may be forced even in the presence of explicit declarations
/// by specifying `MLIR_USE_FALLBACK_TYPE_IDS`.
///
class TypeID {
/// This class represents the storage of a type info object.
@@ -161,9 +164,30 @@ namespace detail {
class FallbackTypeIDResolver {
protected:
/// Register an implicit type ID for the given type name.
static TypeID registerImplicitTypeID(StringRef name);
LLVM_ALWAYS_EXPORT static TypeID registerImplicitTypeID(StringRef name);
};
template <typename T>
struct is_fully_resolved_t {
/// Trait to check if `U` is fully resolved. We use this to verify that `T` is
/// fully resolved when trying to resolve a TypeID. We don't technically need
/// to have the full definition of `T` for the fallback, but it does help
/// prevent situations where a forward declared type uses this fallback even
/// though there is a strong definition for the TypeID in the location where
/// `T` is defined.
template <typename U>
using is_fully_resolved_trait = decltype(sizeof(U));
template <typename U>
using is_fully_resolved = llvm::is_detected<is_fully_resolved_trait, U>;
static constexpr bool value = is_fully_resolved<T>::value;
};
template <typename T>
constexpr bool is_fully_resolved() {
/// Helper function for is_fully_resolved_t.
return is_fully_resolved_t<T>::value;
}
/// 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.
@@ -178,19 +202,8 @@ protected:
template <typename T, typename Enable = void>
class TypeIDResolver : public FallbackTypeIDResolver {
public:
/// Trait to check if `U` is fully resolved. We use this to verify that `T` is
/// fully resolved when trying to resolve a TypeID. We don't technically need
/// to have the full definition of `T` for the fallback, but it does help
/// prevent situations where a forward declared type uses this fallback even
/// though there is a strong definition for the TypeID in the location where
/// `T` is defined.
template <typename U>
using is_fully_resolved_trait = decltype(sizeof(U));
template <typename U>
using is_fully_resolved = llvm::is_detected<is_fully_resolved_trait, U>;
static TypeID resolveTypeID() {
static_assert(is_fully_resolved<T>::value,
static_assert(is_fully_resolved<T>(),
"TypeID::get<> requires the complete definition of `T`");
static TypeID id = registerImplicitTypeID(llvm::getTypeName<T>());
return id;
@@ -246,7 +259,7 @@ TypeID TypeID::get() {
// 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_DECLARE_EXPLICIT_SELF_OWNING_TYPE_ID(CLASS_NAME) \
namespace mlir { \
namespace detail { \
template <> \
@@ -260,13 +273,57 @@ TypeID TypeID::get() {
} /* namespace detail */ \
} /* namespace mlir */
#define MLIR_DEFINE_EXPLICIT_TYPE_ID(CLASS_NAME) \
#define MLIR_DEFINE_EXPLICIT_SELF_OWNING_TYPE_ID(CLASS_NAME) \
namespace mlir { \
namespace detail { \
SelfOwningTypeID TypeIDResolver<CLASS_NAME>::id = {}; \
} /* namespace detail */ \
} /* namespace mlir */
/// Declare/define an explicit specialization for TypeID using the string
/// comparison fallback. This is useful for complex shared library setups
/// where it may be difficult to agree on a source of truth for specific
/// type ID resolution. As long as there is a single resolution for
/// registerImplicitTypeID, all type IDs can be reference a shared
/// registration. This way types which are logically shared across multiple
/// DSOs can have the same type ID, even if their definitions are duplicated.
#define MLIR_DECLARE_EXPLICIT_FALLBACK_TYPE_ID(CLASS_NAME) \
namespace mlir { \
namespace detail { \
template <> \
class TypeIDResolver<CLASS_NAME> : public FallbackTypeIDResolver { \
public: \
static TypeID resolveTypeID() { \
static_assert(is_fully_resolved<CLASS_NAME>(), \
"TypeID::get<> requires the complete definition of `T`"); \
static TypeID id = \
registerImplicitTypeID(llvm::getTypeName<CLASS_NAME>()); \
return id; \
} \
}; \
} /* namespace detail */ \
} /* namespace mlir */
#define MLIR_DEFINE_EXPLICIT_FALLBACK_TYPE_ID(CLASS_NAME)
#ifndef MLIR_USE_FALLBACK_TYPE_IDS
#define MLIR_USE_FALLBACK_TYPE_IDS false
#endif
#if MLIR_USE_FALLBACK_TYPE_IDS
#define MLIR_DECLARE_EXPLICIT_TYPE_ID(CLASS_NAME) \
MLIR_DECLARE_EXPLICIT_FALLBACK_TYPE_ID(CLASS_NAME)
#define MLIR_DEFINE_EXPLICIT_TYPE_ID(CLASS_NAME) \
MLIR_DEFINE_EXPLICIT_FALLBACK_TYPE_ID(CLASS_NAME)
#else
#define MLIR_DECLARE_EXPLICIT_TYPE_ID(CLASS_NAME) \
MLIR_DECLARE_EXPLICIT_SELF_OWNING_TYPE_ID(CLASS_NAME)
#define MLIR_DEFINE_EXPLICIT_TYPE_ID(CLASS_NAME) \
MLIR_DEFINE_EXPLICIT_SELF_OWNING_TYPE_ID(CLASS_NAME)
#endif /* MLIR_USE_FALLBACK_TYPE_IDS */
// 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
@@ -331,7 +388,8 @@ public:
//===----------------------------------------------------------------------===//
/// Explicitly register a set of "builtin" types.
MLIR_DECLARE_EXPLICIT_TYPE_ID(void)
/// `void` must be self-owning, it can't be fully resolved.
MLIR_DECLARE_EXPLICIT_SELF_OWNING_TYPE_ID(void)
namespace llvm {
template <>

View File

@@ -80,7 +80,8 @@ struct ImplicitTypeIDRegistry {
};
} // end namespace
TypeID detail::FallbackTypeIDResolver::registerImplicitTypeID(StringRef name) {
LLVM_ALWAYS_EXPORT TypeID
detail::FallbackTypeIDResolver::registerImplicitTypeID(StringRef name) {
static ImplicitTypeIDRegistry registry;
return registry.lookupOrInsert(name);
}
@@ -89,4 +90,4 @@ TypeID detail::FallbackTypeIDResolver::registerImplicitTypeID(StringRef name) {
// Builtin TypeIDs
//===----------------------------------------------------------------------===//
MLIR_DEFINE_EXPLICIT_TYPE_ID(void)
MLIR_DEFINE_EXPLICIT_SELF_OWNING_TYPE_ID(void)