[libcxx-commits] [libcxx] [libc++] Instantiate integral to_chars overloads externally (PR #107071)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Tue Sep 3 02:44:24 PDT 2024


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/107071

None

>From 903c7be557f64681b5ff46d019608068d6f3ddfc Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 3 Sep 2024 11:44:00 +0200
Subject: [PATCH] [libc++] Instantiate integral to_chars overloads externally

---
 libcxx/include/__charconv/to_chars_integral.h | 83 ++++++++++++++++++-
 libcxx/include/__config                       |  6 ++
 libcxx/include/__configuration/availability.h |  6 ++
 libcxx/src/charconv.cpp                       | 40 ++++++++-
 4 files changed, 132 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__charconv/to_chars_integral.h b/libcxx/include/__charconv/to_chars_integral.h
index 0369f4dfb9bda6..511902f3510489 100644
--- a/libcxx/include/__charconv/to_chars_integral.h
+++ b/libcxx/include/__charconv/to_chars_integral.h
@@ -91,7 +91,7 @@ __to_chars_itoa(char* __first, char* __last, __uint128_t __value, false_type) {
 }
 #  endif
 
-template <class _Tp>
+template <class _Tp, bool _UseExternalToChars = true>
 inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI to_chars_result
 __to_chars_integral(char* __first, char* __last, _Tp __value, int __base, false_type);
 
@@ -233,12 +233,60 @@ _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int __to_chars_integral_widt
   return std::__to_chars_integral_width<_Base>(static_cast<unsigned>(__value));
 }
 
+// Don't return the pointer so we don't capture `__first` or `__last`.
+struct __to_chars_offset_result {
+  int32_t __offset_;
+  errc __ec_;
+};
+
+template <unsigned _Base, class _Tp>
+[[__gnu__::__noinline__]] _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI __to_chars_offset_result
+__external_to_chars_integral(_LIBCPP_NOESCAPE char* __first, _LIBCPP_NOESCAPE char* __last, _Tp __value) {
+  auto __result = __itoa::__integral<_Base>::__to_chars(__first, __last, __value);
+  return {static_cast<int32_t>(__result.ptr - __first), __result.ec};
+}
+
 template <unsigned _Base, typename _Tp, __enable_if_t<(sizeof(_Tp) >= sizeof(unsigned)), int> = 0>
 _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI to_chars_result
 __to_chars_integral(char* __first, char* __last, _Tp __value) {
+  if (!__builtin_constant_p(__value)) {
+    auto __result = std::__external_to_chars_integral<_Base, _Tp>(__first, __last, __value);
+    return {__first + __result.__offset_, __result.__ec_};
+  }
   return __itoa::__integral<_Base>::__to_chars(__first, __last, __value);
 }
 
+#  if _LIBCPP_AVAILABILITY_HAS_EXTERNAL_TO_CHARS
+extern template _LIBCPP_EXPORTED_FROM_ABI __to_chars_offset_result
+__external_to_chars_integral<2, uint32_t>(_LIBCPP_NOESCAPE char*, _LIBCPP_NOESCAPE char*, uint32_t);
+
+extern template _LIBCPP_EXPORTED_FROM_ABI __to_chars_offset_result
+__external_to_chars_integral<8, uint32_t>(_LIBCPP_NOESCAPE char*, _LIBCPP_NOESCAPE char*, uint32_t);
+
+extern template _LIBCPP_EXPORTED_FROM_ABI __to_chars_offset_result
+__external_to_chars_integral<16, uint32_t>(_LIBCPP_NOESCAPE char*, _LIBCPP_NOESCAPE char*, uint32_t);
+
+extern template _LIBCPP_EXPORTED_FROM_ABI __to_chars_offset_result
+__external_to_chars_integral<2, uint64_t>(_LIBCPP_NOESCAPE char*, _LIBCPP_NOESCAPE char*, uint64_t);
+
+extern template _LIBCPP_EXPORTED_FROM_ABI __to_chars_offset_result
+__external_to_chars_integral<8, uint64_t>(_LIBCPP_NOESCAPE char*, _LIBCPP_NOESCAPE char*, uint64_t);
+
+extern template _LIBCPP_EXPORTED_FROM_ABI __to_chars_offset_result
+__external_to_chars_integral<16, uint64_t>(_LIBCPP_NOESCAPE char*, _LIBCPP_NOESCAPE char*, uint64_t);
+
+#    ifndef _LIBCPP_HAS_NO_INT128
+extern template _LIBCPP_EXPORTED_FROM_ABI __to_chars_offset_result
+__external_to_chars_integral<2, __uint128_t>(_LIBCPP_NOESCAPE char*, _LIBCPP_NOESCAPE char*, __uint128_t);
+
+extern template _LIBCPP_EXPORTED_FROM_ABI __to_chars_offset_result
+__external_to_chars_integral<8, __uint128_t>(_LIBCPP_NOESCAPE char*, _LIBCPP_NOESCAPE char*, __uint128_t);
+
+extern template _LIBCPP_EXPORTED_FROM_ABI __to_chars_offset_result
+__external_to_chars_integral<16, __uint128_t>(_LIBCPP_NOESCAPE char*, _LIBCPP_NOESCAPE char*, __uint128_t);
+#    endif // _LIBCPP_HAS_NO_INT128
+#  endif   // _LIBCPP_AVAILABILITY_HAS_EXTERNAL_TO_CHARS
+
 template <unsigned _Base, typename _Tp, __enable_if_t<(sizeof(_Tp) < sizeof(unsigned)), int> = 0>
 _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI to_chars_result
 __to_chars_integral(char* __first, char* __last, _Tp __value) {
@@ -271,9 +319,35 @@ _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int __to_chars_integral_widt
   __libcpp_unreachable();
 }
 
-template <typename _Tp>
+template <class _Tp>
+[[__gnu__::__noinline__]] __to_chars_offset_result
+__external_to_chars_integral(_LIBCPP_NOESCAPE char* __first, _LIBCPP_NOESCAPE char* __last, int __base, _Tp __value) {
+  static_assert(is_unsigned_v<_Tp>);
+  auto __result = std::__to_chars_integral<_Tp, false>(__first, __last, __value, __base, false_type());
+  return {static_cast<int32_t>(__result.ptr - __first), __result.ec};
+}
+
+#  if _LIBCPP_AVAILABILITY_HAS_EXTERNAL_TO_CHARS
+extern template _LIBCPP_EXPORTED_FROM_ABI __to_chars_offset_result
+__external_to_chars_integral<uint32_t>(_LIBCPP_NOESCAPE char*, _LIBCPP_NOESCAPE char*, int, uint32_t);
+
+extern template _LIBCPP_EXPORTED_FROM_ABI __to_chars_offset_result
+__external_to_chars_integral<uint64_t>(_LIBCPP_NOESCAPE char*, _LIBCPP_NOESCAPE char*, int, uint64_t);
+
+#    ifndef _LIBCPP_HAS_NO_INT128
+extern template _LIBCPP_EXPORTED_FROM_ABI __to_chars_offset_result
+__external_to_chars_integral<__uint128_t>(_LIBCPP_NOESCAPE char*, _LIBCPP_NOESCAPE char*, int, __uint128_t);
+#    endif
+#  endif // _LIBCPP_AVAILABILITY_HAS_EXTERNAL_TO_CHARS
+
+template <typename _Tp, bool _UseExternalToChars>
 inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI to_chars_result
 __to_chars_integral(char* __first, char* __last, _Tp __value, int __base, false_type) {
+  if (_UseExternalToChars && !__builtin_constant_p(__base)) {
+    auto __result = std::__external_to_chars_integral(__first, __last, __base, __value);
+    return {__first + __result.__offset_, __result.__ec_};
+  }
+
   if (__base == 10) [[likely]]
     return std::__to_chars_itoa(__first, __last, __value, false_type());
 
@@ -286,6 +360,11 @@ __to_chars_integral(char* __first, char* __last, _Tp __value, int __base, false_
     return std::__to_chars_integral<16>(__first, __last, __value);
   }
 
+  if (_UseExternalToChars && !__builtin_constant_p(__value)) {
+    auto __result = std::__external_to_chars_integral(__first, __last, __base, __value);
+    return {__first + __result.__offset_, __result.__ec_};
+  }
+
   ptrdiff_t __cap = __last - __first;
   int __n         = std::__to_chars_integral_width(__value, __base);
   if (__n > __cap)
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 9dd8d46b48d28f..0fb25aa32d1725 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -1158,6 +1158,12 @@ typedef __char32_t char32_t;
 #    define _LIBCPP_LIFETIMEBOUND
 #  endif
 
+#  if __has_cpp_attribute(_Clang::__noescape__)
+#    define _LIBCPP_NOESCAPE [[_Clang::__noescape__]]
+#  else
+#    define _LIBCPP_NOESCAPE
+#  endif
+
 #  if __has_attribute(__nodebug__)
 #    define _LIBCPP_NODEBUG __attribute__((__nodebug__))
 #  else
diff --git a/libcxx/include/__configuration/availability.h b/libcxx/include/__configuration/availability.h
index ab483a07c9c137..a690afe024123c 100644
--- a/libcxx/include/__configuration/availability.h
+++ b/libcxx/include/__configuration/availability.h
@@ -87,6 +87,9 @@
 // in all versions of the library are available.
 #if defined(_LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS)
 
+#  define _LIBCPP_INTRODUCED_IN_LLVM_20 1
+#  define _LIBCPP_INTRODUCED_IN_LLVM_20_ATTRIBUTE /* nothing */
+
 #  define _LIBCPP_INTRODUCED_IN_LLVM_19 1
 #  define _LIBCPP_INTRODUCED_IN_LLVM_19_ATTRIBUTE /* nothing */
 
@@ -284,6 +287,9 @@
 
 #endif
 
+// This macro controls whether the external instantiations for integral to_chars are available
+#define _LIBCPP_AVAILABILITY_HAS_EXTERNAL_TO_CHARS _LIBCPP_INTRODUCED_IN_LLVM_20
+
 // These macros control the availability of std::bad_optional_access and
 // other exception types. These were put in the shared library to prevent
 // code bloat from every user program defining the vtable for these exception
diff --git a/libcxx/src/charconv.cpp b/libcxx/src/charconv.cpp
index 4fd7a2c2c0f038..77063e291a1f83 100644
--- a/libcxx/src/charconv.cpp
+++ b/libcxx/src/charconv.cpp
@@ -7,7 +7,6 @@
 //===----------------------------------------------------------------------===//
 
 #include <charconv>
-#include <string.h>
 
 #include "include/to_chars_floating_point.h"
 
@@ -74,4 +73,43 @@ to_chars_result to_chars(char* __first, char* __last, long double __value, chars
       __first, __last, static_cast<double>(__value), __fmt, __precision);
 }
 
+template __to_chars_offset_result
+__external_to_chars_integral<2, uint32_t>(_LIBCPP_NOESCAPE char*, _LIBCPP_NOESCAPE char*, uint32_t);
+
+template __to_chars_offset_result
+__external_to_chars_integral<8, uint32_t>(_LIBCPP_NOESCAPE char*, _LIBCPP_NOESCAPE char*, uint32_t);
+
+template __to_chars_offset_result
+__external_to_chars_integral<16, uint32_t>(_LIBCPP_NOESCAPE char*, _LIBCPP_NOESCAPE char*, uint32_t);
+
+template __to_chars_offset_result
+__external_to_chars_integral<2, uint64_t>(_LIBCPP_NOESCAPE char*, _LIBCPP_NOESCAPE char*, uint64_t);
+
+template __to_chars_offset_result
+__external_to_chars_integral<8, uint64_t>(_LIBCPP_NOESCAPE char*, _LIBCPP_NOESCAPE char*, uint64_t);
+
+template __to_chars_offset_result
+__external_to_chars_integral<16, uint64_t>(_LIBCPP_NOESCAPE char*, _LIBCPP_NOESCAPE char*, uint64_t);
+
+#ifndef _LIBCPP_HAS_NO_INT128
+template __to_chars_offset_result
+__external_to_chars_integral<2, __uint128_t>(_LIBCPP_NOESCAPE char*, _LIBCPP_NOESCAPE char*, __uint128_t);
+
+template __to_chars_offset_result
+__external_to_chars_integral<8, __uint128_t>(_LIBCPP_NOESCAPE char*, _LIBCPP_NOESCAPE char*, __uint128_t);
+
+template __to_chars_offset_result
+__external_to_chars_integral<16, __uint128_t>(_LIBCPP_NOESCAPE char*, _LIBCPP_NOESCAPE char*, __uint128_t);
+#endif // _LIBCPP_HAS_NO_INT128
+
+template __to_chars_offset_result
+__external_to_chars_integral<uint32_t>(_LIBCPP_NOESCAPE char*, _LIBCPP_NOESCAPE char*, int, uint32_t);
+
+template __to_chars_offset_result
+__external_to_chars_integral<uint64_t>(_LIBCPP_NOESCAPE char*, _LIBCPP_NOESCAPE char*, int, uint64_t);
+
+#ifndef _LIBCPP_HAS_NO_INT128
+template __to_chars_offset_result
+__external_to_chars_integral<__uint128_t>(_LIBCPP_NOESCAPE char*, _LIBCPP_NOESCAPE char*, int, __uint128_t);
+#endif
 _LIBCPP_END_NAMESPACE_STD



More information about the libcxx-commits mailing list