[gcc r16-1739] libstdc++: Use runtime format for internal format calls in chrono [PR110739]

Tomasz Kaminski tkaminsk@gcc.gnu.org
Fri Jun 27 14:48:34 GMT 2025


https://gcc.gnu.org/g:e99040403f70cd4741f876bffa64259df8ab2199

commit r16-1739-ge99040403f70cd4741f876bffa64259df8ab2199
Author: Tomasz Kamiński <tkaminsk@redhat.com>
Date:   Fri Jun 27 09:50:18 2025 +0200

    libstdc++: Use runtime format for internal format calls in chrono [PR110739]
    
    This patch adjust all internal std::format call inside of __formatter_chrono,
    to use runtime format string and thus avoid compile time checking of validity
    of the format string. Majority of cases are covered by calling newly introduced
    _S_empty_fs() function that returns __Runtime_format_string containing
    _S_empty_spec, instead of passing later directly.
    
    In case of _M_j we use _S_str_d3 function (extracted from _S_str_d2), eliminating
    call to std::format outside of unlikely scenario in which day of year is greater
    than 1000 (this may happen for year_month_day with month greater than 12). In
    consequence, outside of handling subseconds, we no longer delegate to std::format
    or construct temporary strings, when formatting chrono types with ok() values.
    
            PR libstdc++/110739
    
    libstdc++-v3/ChangeLog:
    
            * include/bits/chrono_io.h (__formatter_chrono::_S_empty_fs): Define.
            (__formatter_chrono::_S_str_d2): Use _S_str_d3 for 3+ digits and
            place allways_inline attribute after comment.
            (__formatter_chrono::_S_str_d3): Extracted from _S_str_d2.
            (__formatter_chrono::_M_H_I, __formatter_chrono::_M_R_X): Replace
            _S_empty_spec with _S_empty_fs().
            (__formatter_chrono::_M_j): Likewise and use _S_str_d3 in common
            case.
            (__format::operator-(_ChronoParts, _ChronoParts))
            (__format::operator-=(_ChronoParts, _ChronoParts))
            (__formatter_chrono::_S_fill_two_digits)
            (__formatter_chrono::_S_str_d1): Place always_inline attribute
            after comment.

Diff:
---
 libstdc++-v3/include/bits/chrono_io.h | 37 +++++++++++++++++++++++++----------
 1 file changed, 27 insertions(+), 10 deletions(-)

diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h
index 0ffbf06a7ff4..bcf9830fb9e2 100644
--- a/libstdc++-v3/include/bits/chrono_io.h
+++ b/libstdc++-v3/include/bits/chrono_io.h
@@ -250,14 +250,14 @@ namespace __format
   operator|=(_ChronoParts& __x, _ChronoParts __y) noexcept
   { return __x = __x | __y; }
 
-  [[__gnu__::__always_inline__]]
   // returns copy of x with all bits from y unset.
+  [[__gnu__::__always_inline__]]
   constexpr _ChronoParts
   operator-(_ChronoParts __x, _ChronoParts __y) noexcept
   { return static_cast<_ChronoParts>((unsigned short)__x & ~(unsigned short)__y); }
 
-  [[__gnu__::__always_inline__]]
   // unsets all bits of x that are set in y
+  [[__gnu__::__always_inline__]]
   constexpr _ChronoParts&
   operator-=(_ChronoParts& __x, _ChronoParts __y) noexcept
   { return __x = __x - __y; }
@@ -873,6 +873,11 @@ namespace __format
       static constexpr const _CharT* _S_minus_empty_spec = _S_chars + 17;
       static constexpr const _CharT* _S_empty_spec = _S_chars + 18;
 
+      [[__gnu__::__always_inline__]]
+      static _Runtime_format_string<_CharT>
+      _S_empty_fs()
+      { return _Runtime_format_string<_CharT>(_S_empty_spec); }
+
       // Return the formatting locale.
       template<typename _FormatContext>
 	std::locale
@@ -1405,7 +1410,7 @@ namespace __format
 		__i = 12;
 	    }
 	  else if (__i >= 100) [[unlikely]]
-	    return std::format_to(std::move(__out), _S_empty_spec, __i);
+	    return std::format_to(std::move(__out), _S_empty_fs(), __i);
 
 	  return __format::__write(std::move(__out), _S_two_digits(__i));
 	}
@@ -1418,11 +1423,15 @@ namespace __format
 	  {
 	    // Decimal number of days, without padding.
 	    auto __d = chrono::floor<chrono::days>(__t._M_hours).count();
-	    return std::format_to(std::move(__out), _S_empty_spec, __d);
+	    return std::format_to(std::move(__out), _S_empty_fs(), __d);
 	  }
 
-	  return std::format_to(std::move(__out), _GLIBCXX_WIDEN("{:03d}"),
-				__t._M_day_of_year.count());
+	  auto __d = __t._M_day_of_year.count();
+	  if (__d >= 1000) [[unlikely]]
+	    return std::format_to(std::move(__out), _S_empty_fs(), __d);
+
+	  _CharT __buf[3];
+	  return __format::__write(std::move(__out), _S_str_d3(__buf, __d));
 	}
 
       template<typename _OutIter>
@@ -1523,7 +1532,7 @@ namespace __format
 
 	  if (__hi >= 100) [[unlikely]]
 	    {
-	      __out = std::format_to(std::move(__out), _S_empty_spec, __hi);
+	      __out = std::format_to(std::move(__out), _S_empty_fs(), __hi);
 	      __sv.remove_prefix(2);
 	    }
 	  else
@@ -1728,8 +1737,8 @@ namespace __format
 	};
       }
 
-      [[__gnu__::__always_inline__]]
       // Fills __buf[0] and __buf[1] with 2 digit value of __n.
+      [[__gnu__::__always_inline__]]
       static void
       _S_fill_two_digits(_CharT* __buf, unsigned __n)
       {
@@ -1738,9 +1747,9 @@ namespace __format
 	__buf[1] = __sv[1];
       }
 
-      [[__gnu__::__always_inline__]]
       // Returns decimal representation of __n.
       // Returned string_view may point to __buf.
+      [[__gnu__::__always_inline__]]
       static basic_string_view<_CharT>
       _S_str_d1(span<_CharT, 3> __buf, unsigned __n)
       {
@@ -1749,15 +1758,23 @@ namespace __format
 	return _S_str_d2(__buf, __n);
       }
 
-      [[__gnu__::__always_inline__]]
       // Returns decimal representation of __n, padded to 2 digits.
       // Returned string_view may point to __buf.
+      [[__gnu__::__always_inline__]]
       static basic_string_view<_CharT>
       _S_str_d2(span<_CharT, 3> __buf, unsigned __n)
       {
 	if (__n < 100) [[likely]]
 	  return _S_two_digits(__n);
+        return _S_str_d3(__buf, __n);
+      }
 
+      // Returns decimal representation of __n, padded to 3 digits.
+      // Returned string_view points to __buf.
+      [[__gnu__::__always_inline__]]
+      static basic_string_view<_CharT>
+      _S_str_d3(span<_CharT, 3> __buf, unsigned __n)
+      {
 	_S_fill_two_digits(__buf.data(), __n / 10);
 	__buf[2] = _S_chars[__n % 10];
 	return __string_view(__buf.data(), 3);


More information about the Libstdc++-cvs mailing list