[libc++] Reduces std::to_chars instantiations.

Instead of instantiating all functions called by std::to_chars for the
integral types only instantiate them for 32 and 64 bit integral types.
This results in a smaller binary when using different types.

In an example using the types: signed char, short, int, long, long long,
unsigned char, unsigned short, unsigned int, unsigned long, and
unsigned long long this saved 2792 bytes of code size. For libc++.so.1
is saves 688 bytes of code size (64-bit Linux).

This was discovered while investigating a solution for #52709.

Reviewed By: #libc, ldionne

Differential Revision: https://reviews.llvm.org/D128215
This commit is contained in:
Mark de Wever 2022-06-20 18:28:34 +02:00
parent 9e6261edc0
commit cf927669eb
3 changed files with 36 additions and 4 deletions

View File

@ -26,6 +26,9 @@ template <class _If, class _Then>
template <bool _Bp, class _If, class _Then> using conditional_t = typename conditional<_Bp, _If, _Then>::type;
#endif
// Helper so we can use "conditional_t" in all language versions.
template <bool _Bp, class _If, class _Then> using __conditional_t = typename conditional<_Bp, _If, _Then>::type;
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___TYPE_TRAITS_CONDITIONAL_H

View File

@ -487,16 +487,21 @@ template <typename _Tp, typename enable_if<is_integral<_Tp>::value, int>::type =
inline _LIBCPP_HIDE_FROM_ABI to_chars_result
to_chars(char* __first, char* __last, _Tp __value)
{
return __to_chars_itoa(__first, __last, __value, is_signed<_Tp>());
using _Type = __make_32_64_or_128_bit_t<_Tp>;
static_assert(!is_same<_Type, void>::value, "unsupported integral type used in to_chars");
static_assert(sizeof(_Tp) <= sizeof(int64_t), "128-bit integral support isn't available yet in to_chars");
return std::__to_chars_itoa(__first, __last, static_cast<_Type>(__value), is_signed<_Tp>());
}
template <typename _Tp, typename enable_if<is_integral<_Tp>::value, int>::type = 0>
inline _LIBCPP_HIDE_FROM_ABI to_chars_result
to_chars(char* __first, char* __last, _Tp __value, int __base)
{
_LIBCPP_ASSERT(2 <= __base && __base <= 36, "base not in [2, 36]");
return __to_chars_integral(__first, __last, __value, __base,
is_signed<_Tp>());
_LIBCPP_ASSERT(2 <= __base && __base <= 36, "base not in [2, 36]");
using _Type = __make_32_64_or_128_bit_t<_Tp>;
static_assert(sizeof(_Tp) <= sizeof(int64_t), "128-bit integral support isn't available yet in to_chars");
return std::__to_chars_integral(__first, __last, static_cast<_Type>(__value), __base, is_signed<_Tp>());
}
template <typename _It, typename _Tp, typename _Fn, typename... _Ts>

View File

@ -517,6 +517,7 @@ namespace std
#include <__type_traits/void_t.h>
#include <__utility/declval.h>
#include <cstddef>
#include <cstdint>
#include <version>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@ -1020,6 +1021,29 @@ typename make_unsigned<_Tp>::type __to_unsigned_like(_Tp __x) noexcept {
}
#endif
template <class _Tp, class _Up>
using __copy_unsigned_t = __conditional_t<is_unsigned<_Tp>::value, typename make_unsigned<_Up>::type, _Up>;
/// Helper to promote an integral to smallest 32, 64, or 128 bit representation.
///
/// The restriction is the same as the integral version of to_char.
template <class _Tp>
#if _LIBCPP_STD_VER > 17
requires (is_signed_v<_Tp> || is_unsigned_v<_Tp> || is_same_v<_Tp, char>)
#endif
using __make_32_64_or_128_bit_t =
__copy_unsigned_t<_Tp,
__conditional_t<sizeof(_Tp) <= sizeof(int32_t), int32_t,
__conditional_t<sizeof(_Tp) <= sizeof(int64_t), int64_t,
#ifndef _LIBCPP_HAS_NO_INT128
__conditional_t<sizeof(_Tp) <= sizeof(__int128_t), __int128_t,
/* else */ void>
#else
/* else */ void
#endif
> >
>;
#if _LIBCPP_STD_VER > 17
// Let COND_RES(X, Y) be:
template <class _Tp, class _Up>