[libcxx-commits] [libcxx] r361913 - Rework std::type_info definition to support systems without fully

Eric Fiselier via libcxx-commits libcxx-commits at lists.llvm.org
Tue May 28 19:21:37 PDT 2019


Author: ericwf
Date: Tue May 28 19:21:37 2019
New Revision: 361913

URL: http://llvm.org/viewvc/llvm-project?rev=361913&view=rev
Log:
Rework std::type_info definition to support systems without fully
merged type info names.

Previously std::type_info always expected type info string to be unique.
But this isn't always the case. Like when -Bsymbolic is passed to the
linker or due to llvm.org/PR37398.

This patch adds the LIBCXX_HAS_MERGED_TYPEINFO_NAMES_DEFAULT CMake
option which, when specified, overrides the default configuration for
the library.

The current defaults still assume unique names even though this isn't
strictly correct for ELF binaries. We should consider changing the
default in a follow up commit.

Modified:
    libcxx/trunk/CMakeLists.txt
    libcxx/trunk/docs/BuildingLibcxx.rst
    libcxx/trunk/include/__config
    libcxx/trunk/include/__config_site.in
    libcxx/trunk/include/typeinfo

Modified: libcxx/trunk/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/CMakeLists.txt?rev=361913&r1=361912&r2=361913&view=diff
==============================================================================
--- libcxx/trunk/CMakeLists.txt (original)
+++ libcxx/trunk/CMakeLists.txt Tue May 28 19:21:37 2019
@@ -123,6 +123,18 @@ set(LIBCXX_ABI_NAMESPACE "" CACHE STRING
 option(LIBCXX_ABI_UNSTABLE "Unstable ABI of libc++." OFF)
 option(LIBCXX_ABI_FORCE_ITANIUM "Ignore auto-detection and force use of the Itanium ABI.")
 option(LIBCXX_ABI_FORCE_MICROSOFT "Ignore auto-detection and force use of the Microsoft ABI.")
+
+
+set(LIBCXX_HAS_MERGED_TYPEINFO_NAMES_DEFAULT  "" CACHE STRING
+  "Whether typeinfo names are expected to be unique. Defining this option overrides the default configuration in the library.")
+set(MERGED_TYPEINFO_VALUES ";ON;OFF")
+set_property(CACHE LIBCXX_HAS_MERGED_TYPEINFO_NAMES_DEFAULT PROPERTY STRINGS ${MERGED_TYPEINFO_DEFAULTS})
+list(FIND MERGED_TYPEINFO_VALUES "${LIBCXX_HAS_MERGED_TYPEINFO_NAMES_DEFAULT}" IS_VALID_DEFAULT)
+if (${IS_VALID_DEFAULT} EQUAL -1)
+  message(FATAL_ERROR "Value '${LIBCXX_HAS_MERGED_TYPEINFO_NAMES_DEFAULT}' is not a valid value for
+          LIBCXX_HAS_MERGED_TYPEINFO_NAMES_DEFAULT")
+endif()
+
 option(LIBCXX_HIDE_FROM_ABI_PER_TU_BY_DEFAULT "Enable per TU ABI insulation by default. To be used by vendors." OFF)
 set(LIBCXX_ABI_DEFINES "" CACHE STRING "A semicolon separated list of ABI macros to define in the site config header.")
 option(LIBCXX_USE_COMPILER_RT "Use compiler-rt instead of libgcc" OFF)
@@ -701,13 +713,15 @@ config_define_if(LIBCXX_ABI_UNSTABLE _LI
 config_define_if(LIBCXX_ABI_FORCE_ITANIUM _LIBCPP_ABI_FORCE_ITANIUM)
 config_define_if(LIBCXX_ABI_FORCE_MICROSOFT _LIBCPP_ABI_FORCE_MICROSOFT)
 config_define_if(LIBCXX_HIDE_FROM_ABI_PER_TU_BY_DEFAULT _LIBCPP_HIDE_FROM_ABI_PER_TU_BY_DEFAULT)
-
 config_define_if_not(LIBCXX_ENABLE_GLOBAL_FILESYSTEM_NAMESPACE _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE)
 config_define_if_not(LIBCXX_ENABLE_STDIN _LIBCPP_HAS_NO_STDIN)
 config_define_if_not(LIBCXX_ENABLE_STDOUT _LIBCPP_HAS_NO_STDOUT)
 config_define_if_not(LIBCXX_ENABLE_THREADS _LIBCPP_HAS_NO_THREADS)
 config_define_if_not(LIBCXX_ENABLE_MONOTONIC_CLOCK _LIBCPP_HAS_NO_MONOTONIC_CLOCK)
 config_define_if_not(LIBCXX_ENABLE_THREAD_UNSAFE_C_FUNCTIONS _LIBCPP_HAS_NO_THREAD_UNSAFE_C_FUNCTIONS)
+if (NOT LIBCXX_HAS_MERGED_TYPEINFO_NAMES_DEFAULT STREQUAL "")
+  config_define("${LIBCXX_HAS_MERGED_TYPEINFO_NAMES_DEFAULT}" _LIBCPP_HAS_MERGED_TYPEINFO_NAMES_DEFAULT)
+endif()
 
 config_define_if(LIBCXX_HAS_PTHREAD_API _LIBCPP_HAS_THREAD_API_PTHREAD)
 config_define_if(LIBCXX_HAS_EXTERNAL_THREAD_API _LIBCPP_HAS_THREAD_API_EXTERNAL)

Modified: libcxx/trunk/docs/BuildingLibcxx.rst
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/docs/BuildingLibcxx.rst?rev=361913&r1=361912&r2=361913&view=diff
==============================================================================
--- libcxx/trunk/docs/BuildingLibcxx.rst (original)
+++ libcxx/trunk/docs/BuildingLibcxx.rst Tue May 28 19:21:37 2019
@@ -369,6 +369,21 @@ The following options allow building lib
   A semicolon-separated list of ABI macros to persist in the site config header.
   See ``include/__config`` for the list of ABI macros.
 
+
+.. option:: LIBCXX_HAS_MERGED_TYPEINFO_NAMES_DEFAULT
+
+  **Default**: ``None``. When defined this option overrides the libraries default configuration
+  for whether merged type info names are present.
+
+
+  Build ``std::type_info`` with the assumption that type info names for a type have been fully
+  merged are unique across the entire program. This may not be the case for libraries built with
+  ``-Bsymbolic`` or due to compiler or linker bugs (Ex. llvm.org/PR37398).
+
+  When the value is ``ON`` typeinfo comparisons compare only the pointer value, otherwise ``strcmp``
+  is used as a fallback.
+
+
 .. _LLVM-specific variables:
 
 LLVM-specific options

Modified: libcxx/trunk/include/__config
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/__config?rev=361913&r1=361912&r2=361913&view=diff
==============================================================================
--- libcxx/trunk/include/__config (original)
+++ libcxx/trunk/include/__config Tue May 28 19:21:37 2019
@@ -775,6 +775,16 @@ typedef __char32_t char32_t;
 #  endif
 #endif
 
+#ifndef _LIBCPP_HAS_MERGED_TYPEINFO_NAMES_DEFAULT
+# ifdef _LIBCPP_OBJECT_FORMAT_COFF // Windows binaries can't merge typeinfos.
+# define _LIBCPP_HAS_MERGED_TYPEINFO_NAMES_DEFAULT 0
+#else
+// TODO: This isn't strictly correct on ELF platforms due to llvm.org/PR37398
+// And we should consider defaulting to OFF.
+# define _LIBCPP_HAS_MERGED_TYPEINFO_NAMES_DEFAULT 1
+#endif
+#endif
+
 #ifndef _LIBCPP_HIDE_FROM_ABI
 #  if _LIBCPP_HIDE_FROM_ABI_PER_TU
 #    define _LIBCPP_HIDE_FROM_ABI _LIBCPP_HIDDEN _LIBCPP_INTERNAL_LINKAGE
@@ -936,10 +946,6 @@ template <unsigned> struct __static_asse
 #define _LIBCPP_EXTERN_TEMPLATE2(...) extern template __VA_ARGS__;
 #endif
 
-#if defined(__APPLE__) && defined(__LP64__) && !defined(__x86_64__)
-#define _LIBCPP_NONUNIQUE_RTTI_BIT (1ULL << 63)
-#endif
-
 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(_LIBCPP_MSVCRT_LIKE) || \
     defined(__sun__) || defined(__NetBSD__) || defined(__CloudABI__)
 #define _LIBCPP_LOCALE__L_EXTENSIONS 1

Modified: libcxx/trunk/include/__config_site.in
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/__config_site.in?rev=361913&r1=361912&r2=361913&view=diff
==============================================================================
--- libcxx/trunk/include/__config_site.in (original)
+++ libcxx/trunk/include/__config_site.in Tue May 28 19:21:37 2019
@@ -27,6 +27,7 @@
 #cmakedefine _LIBCPP_HAS_THREAD_LIBRARY_EXTERNAL
 #cmakedefine _LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS
 #cmakedefine _LIBCPP_NO_VCRUNTIME
+#cmakedefine01 _LIBCPP_HAS_MERGED_TYPEINFO_NAMES_DEFAULT
 #cmakedefine _LIBCPP_ABI_NAMESPACE @_LIBCPP_ABI_NAMESPACE@
 
 @_LIBCPP_ABI_DEFINES@

Modified: libcxx/trunk/include/typeinfo
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/typeinfo?rev=361913&r1=361912&r2=361913&view=diff
==============================================================================
--- libcxx/trunk/include/typeinfo (original)
+++ libcxx/trunk/include/typeinfo Tue May 28 19:21:37 2019
@@ -72,13 +72,10 @@ public:
 #include <vcruntime_typeinfo.h>
 #else
 
-#if defined(_LIBCPP_NONUNIQUE_RTTI_BIT) && !defined(_LIBCPP_ABI_MICROSOFT)
-#   define _LIBCPP_HAS_NONUNIQUE_TYPEINFO
-#endif
-
 namespace std  // purposefully not using versioning namespace
 {
 
+
 #if defined(_LIBCPP_ABI_MICROSOFT)
 
 class _LIBCPP_EXCEPTION_ABI type_info
@@ -116,8 +113,32 @@ public:
     { return !operator==(__arg); }
 };
 
-#elif defined(_LIBCPP_HAS_NONUNIQUE_TYPEINFO)
+#else // !defined(_LIBCPP_ABI_MICROSOFT)
 
+// ========================================================================== //
+//                           Implementations
+// ========================================================================== //
+// ------------------------------------------------------------------------- //
+//                               Unique
+// ------------------------------------------------------------------------- //
+// This implementation of type_info assumes a unique copy of the RTTI for a
+// given type inside a program. This is a valid assumption when abiding to
+// Itanium ABI (http://itanium-cxx-abi.github.io/cxx-abi/abi.html#vtable-components).
+// Under this assumption, we can always compare the addresses of the type names
+// to implement equality-comparison of type_infos instead of having to perform
+// a deep string comparison.
+// -------------------------------------------------------------------------- //
+//                             NonUnique
+// -------------------------------------------------------------------------- //
+// This implementation of type_info does not assume there is always a unique
+// copy of the RTTI for a given type inside a program. For various reasons
+// the linker may have failed to merge every copy of a types RTTI
+// (For example: -Bsymbolic or llvm.org/PR37398). Under this assumption, two
+// type_infos are equal if their addresses are equal or if a deep string
+// comparison is equal.
+// -------------------------------------------------------------------------- //
+//                          NonUniqueARMRTTIBit
+// -------------------------------------------------------------------------- //
 // This implementation of type_info does not assume always a unique copy of
 // the RTTI for a given type inside a program. It packs the pointer to the
 // type name into a uintptr_t and reserves the high bit of that pointer (which
@@ -129,91 +150,131 @@ public:
 // faster. If at least one of the type_infos can't guarantee uniqueness, we
 // have no choice but to fall back to a deep string comparison.
 //
+// This implementation is specific to ARM64 on Apple platforms.
+//
 // Note that the compiler is the one setting (or unsetting) the high bit of
 // the pointer when it constructs the type_info, depending on whether it can
 // guarantee uniqueness for that specific type_info.
-class _LIBCPP_EXCEPTION_ABI type_info
-{
-    type_info& operator=(const type_info&);
-    type_info(const type_info&);
-
-    _LIBCPP_INLINE_VISIBILITY
-    int __compare_nonunique_names(const type_info &__arg) const _NOEXCEPT
-    { return __builtin_strcmp(name(), __arg.name()); }
 
-protected:
-    uintptr_t __type_name;
-
-    _LIBCPP_INLINE_VISIBILITY
-    explicit type_info(const char* __n)
-      : __type_name(reinterpret_cast<uintptr_t>(__n)) {}
-
-public:
-    _LIBCPP_AVAILABILITY_TYPEINFO_VTABLE
-    virtual ~type_info();
-
-    _LIBCPP_INLINE_VISIBILITY
-    const char* name() const _NOEXCEPT
-    {
-      return reinterpret_cast<const char*>(__type_name &
-                                           ~_LIBCPP_NONUNIQUE_RTTI_BIT);
+struct __type_info_implementations {
+  struct __string_impl_base {
+    typedef const char* __type_name_t;
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_ALWAYS_INLINE
+    _LIBCPP_CONSTEXPR static const char* __type_name_to_string(__type_name_t __v) _NOEXCEPT {
+      return __v;
     }
-
-    _LIBCPP_INLINE_VISIBILITY
-    bool before(const type_info& __arg) const _NOEXCEPT
-    {
-      if (!((__type_name & __arg.__type_name) & _LIBCPP_NONUNIQUE_RTTI_BIT))
-        return __type_name < __arg.__type_name;
-      return __compare_nonunique_names(__arg) < 0;
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_ALWAYS_INLINE
+    _LIBCPP_CONSTEXPR static __type_name_t __string_to_type_name(const char* __v) _NOEXCEPT {
+      return __v;
     }
+  };
 
-    _LIBCPP_INLINE_VISIBILITY
-    size_t hash_code() const _NOEXCEPT
-    {
-      if (!(__type_name & _LIBCPP_NONUNIQUE_RTTI_BIT))
-        return __type_name;
+  struct __unique_impl : __string_impl_base {
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_ALWAYS_INLINE
+    static size_t __hash(__type_name_t __v) _NOEXCEPT {
+      return reinterpret_cast<size_t>(__v);
+    }
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_ALWAYS_INLINE
+    static bool __eq(__type_name_t __lhs, __type_name_t __rhs) _NOEXCEPT {
+      return __lhs == __rhs;
+    }
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_ALWAYS_INLINE
+    static bool __lt(__type_name_t __lhs, __type_name_t __rhs) _NOEXCEPT {
+      return __lhs < __rhs;
+    }
+  };
 
-      const char* __ptr = name();
+  struct __non_unique_impl : __string_impl_base {
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_ALWAYS_INLINE
+    static size_t __hash(__type_name_t __ptr) _NOEXCEPT {
       size_t __hash = 5381;
       while (unsigned char __c = static_cast<unsigned char>(*__ptr++))
         __hash = (__hash * 33) ^ __c;
       return __hash;
     }
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_ALWAYS_INLINE
+    static bool __eq(__type_name_t __lhs, __type_name_t __rhs) _NOEXCEPT {
+      return __lhs == __rhs || __builtin_strcmp(__lhs, __rhs) == 0;
+    }
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_ALWAYS_INLINE
+    static bool __lt(__type_name_t __lhs, __type_name_t __rhs) _NOEXCEPT {
+      return __builtin_strcmp(__lhs, __rhs) < 0;
+    }
+  };
 
-    _LIBCPP_INLINE_VISIBILITY
-    bool operator==(const type_info& __arg) const _NOEXCEPT
-    {
-      if (__type_name == __arg.__type_name)
-        return true;
+  struct __non_unique_arm_rtti_bit_impl {
+    typedef uintptr_t __type_name_t;
+
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_ALWAYS_INLINE
+    static const char* __type_name_to_string(__type_name_t __v) _NOEXCEPT {
+      return reinterpret_cast<const char*>(__v &
+          ~__non_unique_rtti_bit::value);
+    }
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_ALWAYS_INLINE
+    static __type_name_t __string_to_type_name(const char* __v) _NOEXCEPT {
+      return reinterpret_cast<__type_name_t>(__v);
+    }
 
-      if (!((__type_name & __arg.__type_name) & _LIBCPP_NONUNIQUE_RTTI_BIT))
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_ALWAYS_INLINE
+    static size_t __hash(__type_name_t __v) _NOEXCEPT {
+      if (__is_type_name_unique(__v))
+        return reinterpret_cast<size_t>(__v);
+      return __non_unique_impl::__hash(__type_name_to_string(__v));
+    }
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_ALWAYS_INLINE
+    static bool __eq(__type_name_t __lhs, __type_name_t __rhs) _NOEXCEPT {
+      if (__lhs == __rhs)
+        return true;
+      if (__is_type_name_unique(__lhs, __rhs))
         return false;
-      return __compare_nonunique_names(__arg) == 0;
+      return __builtin_strcmp(__type_name_to_string(__lhs), __type_name_to_string(__rhs)) == 0;
+    }
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_ALWAYS_INLINE
+    static bool __lt(__type_name_t __lhs, __type_name_t __rhs) _NOEXCEPT {
+      if (__is_type_name_unique(__lhs, __rhs))
+        return __lhs < __rhs;
+      return __builtin_strcmp(__type_name_to_string(__lhs), __type_name_to_string(__rhs)) < 0;
     }
 
+   private:
+    typedef std::integral_constant<__type_name_t, (1ULL << 63)> __non_unique_rtti_bit;
+
     _LIBCPP_INLINE_VISIBILITY
-    bool operator!=(const type_info& __arg) const _NOEXCEPT
-    { return !operator==(__arg); }
-};
+    static bool __is_type_name_unique(__type_name_t __lhs) _NOEXCEPT {
+      return !(__lhs & __non_unique_rtti_bit::value);
+    }
+    _LIBCPP_INLINE_VISIBILITY
+    static bool __is_type_name_unique(__type_name_t __lhs, __type_name_t __rhs) _NOEXCEPT {
+      return !((__lhs & __rhs) & __non_unique_rtti_bit::value);
+    }
+  };
 
-#else // !_LIBCPP_ABI_MICROSOFT && !_LIBCPP_HAS_NONUNIQUE_TYPEINFO
+  typedef
+#if defined(__APPLE__) && defined(__LP64__) && !defined(__x86_64__)
+    __non_unique_arm_rtti_bit_impl
+#elif _LIBCPP_HAS_MERGED_TYPEINFO_NAMES_DEFAULT == 0
+    __non_unique_impl
+#elif _LIBCPP_HAS_MERGED_TYPEINFO_NAMES_DEFAULT == 1
+    __unique_impl
+#else
+#   error invalid configuration for _LIBCPP_HAS_MERGED_TYPEINFO_NAMES_DEFAULT
+#endif
+     __impl;
+};
 
-// This implementation of type_info assumes a unique copy of the RTTI for a
-// given type inside a program. This is a valid assumption when abiding to
-// Itanium ABI (http://itanium-cxx-abi.github.io/cxx-abi/abi.html#vtable-components).
-// Under this assumption, we can always compare the addresses of the type names
-// to implement equality-comparison of type_infos instead of having to perform
-// a deep string comparison.
 class _LIBCPP_EXCEPTION_ABI type_info
 {
-    type_info& operator=(const type_info&);
-    type_info(const type_info&);
+  type_info& operator=(const type_info&);
+  type_info(const type_info&);
+
+ protected:
+    typedef __type_info_implementations::__impl __impl;
 
-protected:
-    const char *__type_name;
+    __impl::__type_name_t __type_name;
 
     _LIBCPP_INLINE_VISIBILITY
-    explicit type_info(const char* __n) : __type_name(__n) {}
+    explicit type_info(const char* __n)
+      : __type_name(__impl::__string_to_type_name(__n)) {}
 
 public:
     _LIBCPP_AVAILABILITY_TYPEINFO_VTABLE
@@ -221,43 +282,50 @@ public:
 
     _LIBCPP_INLINE_VISIBILITY
     const char* name() const _NOEXCEPT
-    { return __type_name; }
+    {
+      return __impl::__type_name_to_string(__type_name);
+    }
 
     _LIBCPP_INLINE_VISIBILITY
     bool before(const type_info& __arg) const _NOEXCEPT
-    { return __type_name < __arg.__type_name; }
+    {
+      return __impl::__lt(__type_name, __arg.__type_name);
+    }
 
     _LIBCPP_INLINE_VISIBILITY
     size_t hash_code() const _NOEXCEPT
-    { return reinterpret_cast<size_t>(__type_name); }
+    {
+      return __impl::__hash(__type_name);
+    }
 
     _LIBCPP_INLINE_VISIBILITY
     bool operator==(const type_info& __arg) const _NOEXCEPT
-    { return __type_name == __arg.__type_name; }
+    {
+      return __impl::__eq(__type_name, __arg.__type_name);
+    }
 
     _LIBCPP_INLINE_VISIBILITY
     bool operator!=(const type_info& __arg) const _NOEXCEPT
     { return !operator==(__arg); }
 };
-
-#endif
+#endif // defined(_LIBCPP_ABI_MICROSOFT)
 
 class _LIBCPP_EXCEPTION_ABI bad_cast
     : public exception
 {
-public:
-    bad_cast() _NOEXCEPT;
-    virtual ~bad_cast() _NOEXCEPT;
-    virtual const char* what() const _NOEXCEPT;
+ public:
+  bad_cast() _NOEXCEPT;
+  virtual ~bad_cast() _NOEXCEPT;
+  virtual const char* what() const _NOEXCEPT;
 };
 
 class _LIBCPP_EXCEPTION_ABI bad_typeid
     : public exception
 {
-public:
-    bad_typeid() _NOEXCEPT;
-    virtual ~bad_typeid() _NOEXCEPT;
-    virtual const char* what() const _NOEXCEPT;
+ public:
+  bad_typeid() _NOEXCEPT;
+  virtual ~bad_typeid() _NOEXCEPT;
+  virtual const char* what() const _NOEXCEPT;
 };
 
 }  // std




More information about the libcxx-commits mailing list