[PATCH] libstdc+: Eliminate usage of alloca for non-localized formatting

Jonathan Wakely jwakely@redhat.com
Mon Mar 9 21:19:55 GMT 2026


On Thu, 05 Mar 2026 at 15:45 +0100, Tomasz Kamiński 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.
>
>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 <tkaminsk@redhat.com>
>---
>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?

Nice, OK for trunk, thanks.

> 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
>
>



More information about the Libstdc++ mailing list