On Thu, Mar 5, 2026 at 3:48 PM Tomasz Kamiński <[email protected]> wrote:
> This patch eliminate the use of __builtin_alloca for non-localized
> formatting
> of integers and pointers.
>
> For integers, the transcoding to _CharT moved from _M_format_int function
> to format. This makes the maximum size of the buffer known (__buf_size)
> that depends on sizeof(_Int) and allows use local array of _CharT (__wbuf)
> for the storage. The _M_format_int is modified to accept the string of
> _CharT.
>
> For pointers, format specifiers are subset of one allowed from the
> integers,
> so we simply delegate to __formatter_int::format, instead of repeated
> formatting.
> (_Pres_p and _Pres_P was modified by r16-7844-gbfc2b87f8244a1 to have same
> value
> as _Pres_x and _Pres_X). The set of allowed specifier is still limited per
> C++
> standard by __formatter_ptr::parse.
>
> We also fix issue in __formatter_ptr::parse, where for 'p' and 'P' the
> value
> of _M_alt was negated for _M_spec (result of previous parse) instead of
> __spec
> (result of current parse), and adjust the __formatter_ptr default
> constructor
> to set _M_spec._M_type and _M_spec._M_alt appropriately.
>
Locally cleanedup description to:
This patch eliminate the use of __builtin_alloca for non-localized
formatting
of integers and pointers.
For integers, the transcoding to _CharT moved from _M_format_int
function
to format. This makes the maximum size (that depends on sizeof(_Int))
of the
buffer known (__buf_size) and allows use local array of _CharT (__wbuf)
for
the storage. The _M_format_int is modified to accept the string of
_CharT.
For pointers, r16-7844-gbfc2b87f8244a1 modified _Pres_p and _Pres_P to
have
same value as _Pres_x and _Pres_X, so format specifiers are subset of
one
allowed for integers. In consequence we simply delegate to format
method of __formatter_int, reducing the code duplication. The set of
allowed
specifiers is still limited per C++ standard by __formatter_ptr::parse.
We also fix issue in __formatter_ptr::parse, where for 'p' and 'P' the
value
of _M_alt was negated for _M_spec (result of previous parse) instead of
__spec
(result of current parse), and adjust the __formatter_ptr default
constructor
to set _M_spec._M_type and _M_spec._M_alt appropriately.
>
> libstdc++-v3/ChangeLog:
>
> * include/std/format (__formatter_int::format): Handle transcoding
> to _CharT before calling _M_format_int.
> (__formatter_int::_M_format_int): Accept basic_string_view<_CharT>
> and remove transcoding.
> (__formatter_ptr::__formatter_ptr): Configure _M_spec member.
> (__formatter_ptr::parse): Negate _M_alt for __spec and not _M_spec.
> (__formatter_ptr::format): Delegate to __formatter_int.
>
> Signed-off-by: Tomasz Kamiński <[email protected]>
> ---
> This could be done for GCC-17, but I think we should at least merge
> chagnes for __formatter_ptr cosntructor and parse for GCC16, so the __spec
> value is configured in way compatible with __formatter_int.
>
> Testing on x86_64-linux. OK for trunk when test passes?
>
> libstdc++-v3/include/std/format | 102 +++++++++-----------------------
> 1 file changed, 29 insertions(+), 73 deletions(-)
>
> diff --git a/libstdc++-v3/include/std/format
> b/libstdc++-v3/include/std/format
> index ec3d0018efd..9ee9bb4c9e2 100644
> --- a/libstdc++-v3/include/std/format
> +++ b/libstdc++-v3/include/std/format
> @@ -1647,7 +1647,8 @@ namespace __format
> if (_M_spec._M_type == _Pres_c)
> return _M_format_character(_S_to_character(__i), __fc);
>
> - char __buf[sizeof(_Int) * __CHAR_BIT__ + 3];
> + constexpr size_t __buf_size = sizeof(_Int) * __CHAR_BIT__ + 3;
> + char __buf[__buf_size];
> to_chars_result __res{};
>
> string_view __base_prefix;
> @@ -1707,8 +1708,21 @@ namespace __format
> }
> __start = __format::__put_sign(__i, _M_spec._M_sign, __start -
> 1);
>
> - return _M_format_int(string_view(__start, __res.ptr - __start),
> - __start_digits - __start, __fc);
> +
> + string_view __narrow_str(__start, __res.ptr - __start);
> + size_t __prefix_len = __start_digits - __start;
> + if constexpr (is_same_v<char, _CharT>)
> + return _M_format_int(__narrow_str, __prefix_len, __fc);
> +#ifdef _GLIBCXX_USE_WCHAR_T
> + else
> + {
> + _CharT __wbuf[__buf_size];
> + size_t __n = __narrow_str.size();
> + std::__to_wstring_numeric(__narrow_str.data(), __n, __wbuf);
> + return _M_format_int(basic_string_view<_CharT>(__wbuf, __n),
> + __prefix_len, __fc);
> + }
> +#endif
> }
>
> template<typename _Out>
> @@ -1795,24 +1809,10 @@ namespace __format
>
> template<typename _Out>
> typename basic_format_context<_Out, _CharT>::iterator
> - _M_format_int(string_view __narrow_str, size_t __prefix_len,
> + _M_format_int(basic_string_view<_CharT> __str, size_t __prefix_len,
> basic_format_context<_Out, _CharT>& __fc) const
> {
> size_t __width = _M_spec._M_get_width(__fc);
> -
> - basic_string_view<_CharT> __str;
> - if constexpr (is_same_v<char, _CharT>)
> - __str = __narrow_str;
> -#ifdef _GLIBCXX_USE_WCHAR_T
> - else
> - {
> - size_t __n = __narrow_str.size();
> - auto __p = (_CharT*)__builtin_alloca(__n * sizeof(_CharT));
> - std::__to_wstring_numeric(__narrow_str.data(), __n, __p);
> - __str = {__p, __n};
> - }
> -#endif
> -
> if (_M_spec._M_localized)
> {
> const auto& __l = __fc.locale();
> @@ -2433,7 +2433,13 @@ namespace __format
> template<__format::__char _CharT>
> struct __formatter_ptr
> {
> - __formatter_ptr() = default;
> + constexpr
> + __formatter_ptr() noexcept
> + : _M_spec()
> + {
> + _M_spec._M_type = _Pres_p;
> + _M_spec._M_alt = true;
> + }
>
> constexpr
> __formatter_ptr(_Spec<_CharT> __spec) noexcept
> @@ -2483,14 +2489,14 @@ namespace __format
> if (*__first == 'p')
> {
> __spec._M_type = _Pres_p;
> - _M_spec._M_alt = !_M_spec._M_alt;
> + __spec._M_alt = !__spec._M_alt;
> ++__first;
> }
> #if __glibcxx_format >= 202304L
> else if (*__first == 'P')
> {
> __spec._M_type = _Pres_P;
> - _M_spec._M_alt = !_M_spec._M_alt;
> + __spec._M_alt = !__spec._M_alt;
> ++__first;
> }
> #endif
> @@ -2506,57 +2512,7 @@ namespace __format
> format(const void* __v, basic_format_context<_Out, _CharT>& __fc)
> const
> {
> auto __u = reinterpret_cast<__UINTPTR_TYPE__>(__v);
> - char __buf[2 + sizeof(__v) * 2];
> - auto [__ptr, __ec] = std::to_chars(__buf + 2, std::end(__buf),
> - __u, 16);
> - int __n = __ptr - __buf;
> - __buf[0] = '0';
> - __buf[1] = 'x';
> -#if __glibcxx_format >= 202304L
> - if (_M_spec._M_type == __format::_Pres_P)
> - {
> - __buf[1] = 'X';
> - for (auto __p = __buf + 2; __p != __ptr; ++__p)
> -#if __has_builtin(__builtin_toupper)
> - *__p = __builtin_toupper(*__p);
> -#else
> - *__p = std::toupper(*__p);
> -#endif
> - }
> -#endif
> -
> - basic_string_view<_CharT> __str;
> - if constexpr (is_same_v<_CharT, char>)
> - __str = string_view(__buf, __n);
> -#ifdef _GLIBCXX_USE_WCHAR_T
> - else
> - {
> - auto __p = (_CharT*)__builtin_alloca(__n * sizeof(_CharT));
> - std::__to_wstring_numeric(__buf, __n, __p);
> - __str = wstring_view(__p, __n);
> - }
> -#endif
> -
> -#if __glibcxx_format >= 202304L
> - if (_M_spec._M_zero_fill)
> - {
> - size_t __width = _M_spec._M_get_width(__fc);
> - if (__width <= __str.size())
> - return __format::__write(__fc.out(), __str);
> -
> - auto __out = __fc.out();
> - // Write "0x" or "0X" prefix before zero-filling.
> - __out = __format::__write(std::move(__out), __str.substr(0,
> 2));
> - __str.remove_prefix(2);
> - size_t __nfill = __width - __n;
> - return __format::__write_padded(std::move(__out), __str,
> - __format::_Align_right,
> - __nfill, _CharT('0'));
> - }
> -#endif
> -
> - return __format::__write_padded_as_spec(__str, __n, __fc,
> _M_spec,
> - __format::_Align_right);
> + return __formatter_int<_CharT>(_M_spec).format(__u, __fc);
> }
>
> private:
> @@ -2571,7 +2527,7 @@ namespace __format
> }
> }
>
> - __format::_Spec<_CharT> _M_spec{};
> + __format::_Spec<_CharT> _M_spec;
> };
>
> } // namespace __format
> --
> 2.53.0
>
>