[libcxx-commits] [libcxx] 9ee97ce - [libc++] Use ABI tags instead of internal linkage to provide per-TU insulation

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Wed Jul 6 12:30:39 PDT 2022


Author: Louis Dionne
Date: 2022-07-06T15:30:04-04:00
New Revision: 9ee97ce3b8305c5762ec34eecb4daf379984c95b

URL: https://github.com/llvm/llvm-project/commit/9ee97ce3b8305c5762ec34eecb4daf379984c95b
DIFF: https://github.com/llvm/llvm-project/commit/9ee97ce3b8305c5762ec34eecb4daf379984c95b.diff

LOG: [libc++] Use ABI tags instead of internal linkage to provide per-TU insulation

Instead of marking private symbols with internal_linkage (which leads to
one copy per translation unit -- rather wasteful), use an ABI tag that
gets rev'd with each libc++ version. That way, we know that we can't have
name collisions between implementation-detail functions across libc++
versions, so we'll never violate the ODR. However, within a single program,
each symbol still has a proper name with external linkage, which means
that the linker is free to deduplicate symbols even across TUs.

This actually means that we can guarantee that versions of libc++ can
be mixed within the same program without ever having to take a code size
hit, and without having to manually opt-in -- it should just work out of
the box.

Differential Revision: https://reviews.llvm.org/D127444

Added: 
    

Modified: 
    libcxx/CMakeLists.txt
    libcxx/cmake/caches/Apple.cmake
    libcxx/docs/BuildingLibcxx.rst
    libcxx/docs/DesignDocs/VisibilityMacros.rst
    libcxx/docs/ReleaseNotes.rst
    libcxx/include/__config
    libcxx/include/__config_site.in

Removed: 
    libcxx/test/libcxx/strings/basic.string/PR42676.sh.cpp


################################################################################
diff  --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index b118007eacbc..3d63bb0c8fbf 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -205,7 +205,6 @@ if (NOT ("${LIBCXX_TYPEINFO_COMPARISON_IMPLEMENTATION}" IN_LIST TYPEINFO_COMPARI
                        LIBCXX_TYPEINFO_COMPARISON_IMPLEMENTATION")
 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_EXTRA_SITE_DEFINES "Extra defines to add into __config_site")
 option(LIBCXX_USE_COMPILER_RT "Use compiler-rt instead of libgcc" OFF)
@@ -847,7 +846,6 @@ config_define(${LIBCXX_ABI_VERSION} _LIBCPP_ABI_VERSION)
 config_define(${LIBCXX_ABI_NAMESPACE} _LIBCPP_ABI_NAMESPACE)
 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_THREADS _LIBCPP_HAS_NO_THREADS)
 config_define_if_not(LIBCXX_ENABLE_MONOTONIC_CLOCK _LIBCPP_HAS_NO_MONOTONIC_CLOCK)
 if (NOT LIBCXX_TYPEINFO_COMPARISON_IMPLEMENTATION STREQUAL "default")

diff  --git a/libcxx/cmake/caches/Apple.cmake b/libcxx/cmake/caches/Apple.cmake
index db567bd394b1..ea701a43f96a 100644
--- a/libcxx/cmake/caches/Apple.cmake
+++ b/libcxx/cmake/caches/Apple.cmake
@@ -8,7 +8,6 @@ set(LIBCXX_ENABLE_EXPERIMENTAL_LIBRARY OFF CACHE BOOL "")
 set(LIBCXX_ENABLE_STATIC ON CACHE BOOL "")
 set(LIBCXX_ENABLE_SHARED ON CACHE BOOL "")
 set(LIBCXX_CXX_ABI libcxxabi CACHE STRING "")
-set(LIBCXX_HIDE_FROM_ABI_PER_TU_BY_DEFAULT ON CACHE BOOL "")
 set(LIBCXX_ENABLE_BACKWARDS_COMPATIBILITY_DEBUG_MODE_SYMBOLS OFF CACHE BOOL "")
 set(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS ON CACHE BOOL "")
 set(LIBCXX_ENABLE_INCOMPLETE_FEATURES OFF CACHE BOOL "")

diff  --git a/libcxx/docs/BuildingLibcxx.rst b/libcxx/docs/BuildingLibcxx.rst
index 3f4e314382d8..5069c7fe0692 100644
--- a/libcxx/docs/BuildingLibcxx.rst
+++ b/libcxx/docs/BuildingLibcxx.rst
@@ -412,15 +412,6 @@ libc++ Feature Options
   Use the specified GCC toolchain and standard library when building the native
   stdlib benchmark tests.
 
-.. option:: LIBCXX_HIDE_FROM_ABI_PER_TU_BY_DEFAULT:BOOL
-
-  **Default**: ``OFF``
-
-  Pick the default for whether to constrain ABI-unstable symbols to
-  each individual translation unit. This setting controls whether
-  `_LIBCPP_HIDE_FROM_ABI_PER_TU_BY_DEFAULT` is defined by default --
-  see the documentation of that macro for details.
-
 
 libc++ ABI Feature Options
 --------------------------

diff  --git a/libcxx/docs/DesignDocs/VisibilityMacros.rst b/libcxx/docs/DesignDocs/VisibilityMacros.rst
index a165fc49afcf..d1c162682b66 100644
--- a/libcxx/docs/DesignDocs/VisibilityMacros.rst
+++ b/libcxx/docs/DesignDocs/VisibilityMacros.rst
@@ -65,41 +65,6 @@ Visibility Macros
   ABI, we should create a new _LIBCPP_HIDE_FROM_ABI_AFTER_XXX macro, and we can
   use it to start removing symbols from the ABI after that stable version.
 
-**_LIBCPP_HIDE_FROM_ABI_PER_TU**
-  This macro controls whether symbols hidden from the ABI with `_LIBCPP_HIDE_FROM_ABI`
-  are local to each translation unit in addition to being local to each final
-  linked image. This macro is defined to either 0 or 1. When it is defined to
-  1, translation units compiled with 
diff erent versions of libc++ can be linked
-  together, since all non ABI-facing functions are local to each translation unit.
-  This allows static archives built with 
diff erent versions of libc++ to be linked
-  together. This also means that functions marked with `_LIBCPP_HIDE_FROM_ABI`
-  are not guaranteed to have the same address across translation unit boundaries.
-
-  When the macro is defined to 0, there is no guarantee that translation units
-  compiled with 
diff erent versions of libc++ can interoperate. However, this
-  leads to code size improvements, since non ABI-facing functions can be
-  deduplicated across translation unit boundaries.
-
-  This macro can be defined by users to control the behavior they want from
-  libc++. The default value of this macro (0 or 1) is controlled by whether
-  `_LIBCPP_HIDE_FROM_ABI_PER_TU_BY_DEFAULT` is defined, which is intended to
-  be used by vendors only (see below).
-
-**_LIBCPP_HIDE_FROM_ABI_PER_TU_BY_DEFAULT**
-  This macro controls the default value for `_LIBCPP_HIDE_FROM_ABI_PER_TU`.
-  When the macro is defined, per TU ABI insulation is enabled by default, and
-  `_LIBCPP_HIDE_FROM_ABI_PER_TU` is defined to 1 unless overridden by users.
-  Otherwise, per TU ABI insulation is disabled by default, and
-  `_LIBCPP_HIDE_FROM_ABI_PER_TU` is defined to 0 unless overridden by users.
-
-  This macro is intended for vendors to control whether they want to ship
-  libc++ with per TU ABI insulation enabled by default. Users can always
-  control the behavior they want by defining `_LIBCPP_HIDE_FROM_ABI_PER_TU`
-  appropriately.
-
-  By default, this macro is not defined, which means that per TU ABI insulation
-  is not provided unless explicitly overridden by users.
-
 **_LIBCPP_TYPE_VIS**
   Mark a type's typeinfo, vtable and members as having default visibility.
   This attribute cannot be used on class templates.
@@ -194,22 +159,6 @@ Visibility Macros
   versioning namespace. This allows throwing and catching some exception types
   between libc++ and libstdc++.
 
-**_LIBCPP_INTERNAL_LINKAGE**
-  Mark the affected entity as having internal linkage (i.e. the `static`
-  keyword in C). This is only a best effort: when the `internal_linkage`
-  attribute is not available, we fall back to forcing the function to be
-  inlined, which approximates internal linkage since an externally visible
-  symbol is never generated for that function. This is an internal macro
-  used as an implementation detail by other visibility macros. Never mark
-  a function or a class with this macro directly.
-
-**_LIBCPP_ALWAYS_INLINE**
-  Forces inlining of the function it is applied to. For visibility purposes,
-  this macro is used to make sure that an externally visible symbol is never
-  generated in an object file when the `internal_linkage` attribute is not
-  available. This is an internal macro used by other visibility macros, and
-  it should not be used directly.
-
 Links
 =====
 

diff  --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst
index c2917d4710df..c6eb835f9739 100644
--- a/libcxx/docs/ReleaseNotes.rst
+++ b/libcxx/docs/ReleaseNotes.rst
@@ -224,3 +224,8 @@ Build System Changes
   means that the same set of installed headers works for both DLL and static
   linkage. This means that distributors finally can build both library
   versions with a single CMake invocation.
+
+- The ``LIBCXX_HIDE_FROM_ABI_PER_TU_BY_DEFAULT`` configuration option has been removed. Indeed,
+  the risk of ODR violations from mixing 
diff erent versions of libc++ in the same program has
+  been mitigated with a 
diff erent technique that is simpler and does not have the drawbacks of
+  using internal linkage.

diff  --git a/libcxx/include/__config b/libcxx/include/__config
index e4b7d25edf34..22c2ed7fd87b 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -26,6 +26,13 @@
 
 #  define _LIBCPP_VERSION 15000
 
+#  define _LIBCPP_CONCAT_IMPL(_X, _Y) _X##_Y
+#  define _LIBCPP_CONCAT(_X, _Y) _LIBCPP_CONCAT_IMPL(_X, _Y)
+
+// Valid C++ identifier that revs with every libc++ version. This can be used to
+// generate identifiers that must be unique for every released libc++ version.
+#  define _LIBCPP_VERSIONED_IDENTIFIER _LIBCPP_CONCAT(v, _LIBCPP_VERSION)
+
 #  if __STDC_HOSTED__ == 0
 #    define _LIBCPP_FREESTANDING
 #  endif
@@ -568,12 +575,6 @@ typedef __char32_t char32_t;
 
 #  endif // defined(_LIBCPP_OBJECT_FORMAT_COFF)
 
-#  if __has_attribute(internal_linkage)
-#    define _LIBCPP_INTERNAL_LINKAGE __attribute__((internal_linkage))
-#  else
-#    define _LIBCPP_INTERNAL_LINKAGE _LIBCPP_ALWAYS_INLINE
-#  endif
-
 #  if __has_attribute(exclude_from_explicit_instantiation)
 #    define _LIBCPP_EXCLUDE_FROM_EXPLICIT_INSTANTIATION __attribute__((__exclude_from_explicit_instantiation__))
 #  else
@@ -583,20 +584,35 @@ typedef __char32_t char32_t;
 #    define _LIBCPP_EXCLUDE_FROM_EXPLICIT_INSTANTIATION _LIBCPP_ALWAYS_INLINE
 #  endif
 
-#  ifndef _LIBCPP_HIDE_FROM_ABI_PER_TU
-#    ifndef _LIBCPP_HIDE_FROM_ABI_PER_TU_BY_DEFAULT
-#      define _LIBCPP_HIDE_FROM_ABI_PER_TU 0
-#    else
-#      define _LIBCPP_HIDE_FROM_ABI_PER_TU 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
-#    else
-#      define _LIBCPP_HIDE_FROM_ABI _LIBCPP_HIDDEN _LIBCPP_EXCLUDE_FROM_EXPLICIT_INSTANTIATION
-#    endif
+// This macro marks a symbol as being hidden from libc++'s ABI. This is achieved
+// on two levels:
+// 1. The symbol is given hidden visibility, which ensures that users won't start exporting
+//    symbols from their dynamic library by means of using the libc++ headers. This ensures
+//    that those symbols stay private to the dynamic library in which it is defined.
+//
+// 2. The symbol is given an ABI tag that changes with each version of libc++. This ensures
+//    that no ODR violation can arise from mixing two TUs compiled with 
diff erent versions
+//    of libc++ where we would have changed the definition of a symbol. If the symbols shared
+//    the same name, the ODR would require that their definitions be token-by-token equivalent,
+//    which basically prevents us from being able to make any change to any function in our
+//    headers. Using this ABI tag ensures that the symbol name is "bumped" artificially at
+//    each release, which lets us change the definition of these symbols at our leisure.
+//    Note that historically, this has been achieved in various ways, including force-inlining
+//    all functions or giving internal linkage to all functions. Both these (previous) solutions
+//    suffer from drawbacks that lead notably to code bloat.
+//
+// Note that we use _LIBCPP_EXCLUDE_FROM_EXPLICIT_INSTANTIATION to ensure that we don't depend
+// on _LIBCPP_HIDE_FROM_ABI methods of classes explicitly instantiated in the dynamic library.
+//
+// TODO: We provide a escape hatch with _LIBCPP_NO_ABI_TAG for folks who want to avoid increasing
+//       the length of symbols with an ABI tag. In practice, we should remove the escape hatch and
+//       use compression mangling instead, see https://github.com/itanium-cxx-abi/cxx-abi/issues/70.
+#  ifndef _LIBCPP_NO_ABI_TAG
+#    define _LIBCPP_HIDE_FROM_ABI                                                                                      \
+      _LIBCPP_HIDDEN _LIBCPP_EXCLUDE_FROM_EXPLICIT_INSTANTIATION                                                       \
+          __attribute__((__abi_tag__(_LIBCPP_TOSTRING(_LIBCPP_VERSIONED_IDENTIFIER))))
+#  else
+#    define _LIBCPP_HIDE_FROM_ABI _LIBCPP_HIDDEN _LIBCPP_EXCLUDE_FROM_EXPLICIT_INSTANTIATION
 #  endif
 
 #  ifdef _LIBCPP_BUILDING_LIBRARY

diff  --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index c615575226ea..97892e99fa49 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -13,7 +13,6 @@
 #cmakedefine _LIBCPP_ABI_NAMESPACE @_LIBCPP_ABI_NAMESPACE@
 #cmakedefine _LIBCPP_ABI_FORCE_ITANIUM
 #cmakedefine _LIBCPP_ABI_FORCE_MICROSOFT
-#cmakedefine _LIBCPP_HIDE_FROM_ABI_PER_TU_BY_DEFAULT
 #cmakedefine _LIBCPP_HAS_NO_THREADS
 #cmakedefine _LIBCPP_HAS_NO_MONOTONIC_CLOCK
 #cmakedefine _LIBCPP_HAS_MUSL_LIBC

diff  --git a/libcxx/test/libcxx/strings/basic.string/PR42676.sh.cpp b/libcxx/test/libcxx/strings/basic.string/PR42676.sh.cpp
deleted file mode 100644
index 217091562b46..000000000000
--- a/libcxx/test/libcxx/strings/basic.string/PR42676.sh.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// Regression test for PR42676.
-
-// RUN: %{cxx} %{flags} %s -o %t.exe %{compile_flags} %{link_flags} -D_LIBCPP_HIDE_FROM_ABI_PER_TU
-// RUN: %{run}
-
-#include <memory>
-#include <string>
-
-int main(int, char**) {
-    std::string s1(10u, '-', std::allocator<char>()); (void)s1;
-    std::string s2("hello", std::allocator<char>()); (void)s2;
-    std::string s3("hello"); (void)s3;
-    return 0;
-}


        


More information about the libcxx-commits mailing list