public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] [PR77760] [libstdc++] encode __time_get_state in tm
@ 2023-02-17  7:47 Alexandre Oliva
  2023-02-17 12:56 ` Jakub Jelinek
  0 siblings, 1 reply; 5+ messages in thread
From: Alexandre Oliva @ 2023-02-17  7:47 UTC (permalink / raw)
  To: gcc-patches, libstdc++; +Cc: jakub


On platforms that fail the ptrtomemfn-cast-to-pfn hack, such as
arm-*-vxworks*, time_get fails with %I and %p because the state is not
preserved across do_get calls.

This patch introduces an alternate hack, that encodes the state in
unused bits of struct tm before calling do_get, extracts them in
do_get, does the processing, and encodes it back, so that get extracts
it.

The finalizer is adjusted for idempotence, because both do_get and get
may call it.

Regstrapped on x86_64-linux-gnu.
Tested on arm-vxworks7 (gcc-12) and arm-eabi (trunk).  Ok to install?

for  libstdc++-v3/ChangeLog

	PR libstdc++/77760
	* include/bits/locale_facets_nonio.h (__time_get_state): Add
	_M_state_tm, _M_save_to and _M_restore_from.
	* include/bits/locale_facets_nonio.tcc (time_get::get): Drop
	do_get-overriding hack.  Use state unconditionally, and encode
	it in tm around do_get.
	(time_get::do_get): Extract state from tm, and encode it back,
	around parsing and finalizing.
	* src/c++98/locale_facets.cc
	(__time_get_state::_M_finalize_state): Make tm_hour and
	tm_year idempotent.
---
 libstdc++-v3/include/bits/locale_facets_nonio.h   |   80 +++++++++++++++++++++
 libstdc++-v3/include/bits/locale_facets_nonio.tcc |   43 ++---------
 libstdc++-v3/src/c++98/locale_facets.cc           |    8 ++
 3 files changed, 93 insertions(+), 38 deletions(-)

diff --git a/libstdc++-v3/include/bits/locale_facets_nonio.h b/libstdc++-v3/include/bits/locale_facets_nonio.h
index 372cf0429501d..711bede158427 100644
--- a/libstdc++-v3/include/bits/locale_facets_nonio.h
+++ b/libstdc++-v3/include/bits/locale_facets_nonio.h
@@ -361,6 +361,86 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     void
     _M_finalize_state(tm* __tm);
 
+  private:
+    void
+    _M_state_tm(tm* __tm, bool __totm)
+    {
+      // Check we don't invade the in-range tm bits, even if int is
+      // 16-bits wide.
+#define _M_min_shift_tm_sec 6
+#define _M_min_shift_tm_min 6
+#define _M_min_shift_tm_hour 5
+#define _M_min_shift_tm_mday 5
+#define _M_min_shift_tm_mon 4
+#define _M_min_shift_tm_year 16 // 14, but signed, so avoid it.
+#define _M_min_shift_tm_wday 3
+#define _M_min_shift_tm_yday 9
+#define _M_min_shift_tm_isdst 1
+      // Represent __STF in __WDT bits of __TMF up to the __MSB bit.
+      // In __MSB, 0 stands for the most significant bit of __TMF,
+      // 1 the bit next to it, and so on.
+#define _M_time_get_state_bitfield_inout(__tmf, __msb, __wdt, __stf)	\
+  do									\
+  {									\
+    const unsigned __shift = (sizeof (__tm->__tmf) * __CHAR_BIT__	\
+			      - (__msb) - (__wdt));			\
+    static char __attribute__ ((__unused__))				\
+      __check_parms_##__tmf[(__msb) >= 0 && (__wdt) > 0			\
+			    && __shift >= (_M_min_shift_##__tmf		\
+					   + (sizeof (__tm->__tmf)	\
+					      * __CHAR_BIT__) - 16)	\
+			    ? 1 : -1];					\
+    const unsigned __mask = ((1 << (__wdt)) - 1) << __shift;		\
+    if (!__totm)							\
+      this->__stf = (__tm->__tmf & __mask) >> __shift;			\
+    __tm->__tmf &= ~__mask;						\
+    if (__totm)								\
+      __tm->__tmf |= ((unsigned)this->__stf << __shift) & __mask;	\
+    }									\
+  while (0)
+
+      _M_time_get_state_bitfield_inout (tm_hour,  0, 1, _M_have_I);
+      _M_time_get_state_bitfield_inout (tm_wday,  0, 1, _M_have_wday);
+      _M_time_get_state_bitfield_inout (tm_yday,  0, 1, _M_have_yday);
+      _M_time_get_state_bitfield_inout (tm_mon,   0, 1, _M_have_mon);
+      _M_time_get_state_bitfield_inout (tm_mday,  0, 1, _M_have_mday);
+      _M_time_get_state_bitfield_inout (tm_yday,  1, 1, _M_have_uweek);
+      _M_time_get_state_bitfield_inout (tm_yday,  2, 1, _M_have_wweek);
+      _M_time_get_state_bitfield_inout (tm_isdst, 0, 1, _M_have_century);
+      _M_time_get_state_bitfield_inout (tm_hour,  1, 1, _M_is_pm);
+      _M_time_get_state_bitfield_inout (tm_isdst, 1, 1, _M_want_century);
+      _M_time_get_state_bitfield_inout (tm_yday,  3, 1, _M_want_xday);
+      // _M_pad1
+      _M_time_get_state_bitfield_inout (tm_wday,  1, 6, _M_week_no);
+      // _M_pad2
+      _M_time_get_state_bitfield_inout (tm_mon,   1, 8, _M_century);
+      // _M_pad3
+
+#undef _M_min_shift_tm_hour
+#undef _M_min_shift_tm_sec
+#undef _M_min_shift_tm_min
+#undef _M_min_shift_tm_hour
+#undef _M_min_shift_tm_mday
+#undef _M_min_shift_tm_mon
+#undef _M_min_shift_tm_year
+#undef _M_min_shift_tm_wday
+#undef _M_min_shift_tm_yday
+#undef _M_min_shift_tm_isdst
+#undef _M_time_get_state_bitfield_inout
+    }
+  public:
+    // Encode *THIS into scratch bits of __TM.
+    void
+    _M_save_to(tm* __tm) {
+      _M_state_tm (__tm, true);
+    }
+
+    // Decode and zero out scratch bits of __TM back into *THIS.
+    void
+    _M_restore_from(tm* __tm) {
+      _M_state_tm (__tm, false);
+    }
+
     unsigned int _M_have_I : 1;
     unsigned int _M_have_wday : 1;
     unsigned int _M_have_yday : 1;
diff --git a/libstdc++-v3/include/bits/locale_facets_nonio.tcc b/libstdc++-v3/include/bits/locale_facets_nonio.tcc
index b19f442530011..47d243ce84dd3 100644
--- a/libstdc++-v3/include/bits/locale_facets_nonio.tcc
+++ b/libstdc++-v3/include/bits/locale_facets_nonio.tcc
@@ -1464,21 +1464,8 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
       const locale& __loc = __io._M_getloc();
       ctype<_CharT> const& __ctype = use_facet<ctype<_CharT> >(__loc);
       __err = ios_base::goodbit;
-      bool __use_state = false;
-#if __GNUC__ >= 5 && !defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wpmf-conversions"
-      // Nasty hack.  The C++ standard mandates that get invokes the do_get
-      // virtual method, but unfortunately at least without an ABI change
-      // for the facets we can't keep state across the different do_get
-      // calls.  So e.g. if __fmt is "%p %I:%M:%S", we can't handle it
-      // properly, because we first handle the %p am/pm specifier and only
-      // later the 12-hour format specifier.
-      if ((void*)(this->*(&time_get::do_get)) == (void*)(&time_get::do_get))
-	__use_state = true;
-#pragma GCC diagnostic pop
-#endif
       __time_get_state __state = __time_get_state();
+      __state._M_save_to (__tm);
       while (__fmt != __fmtend &&
              __err == ios_base::goodbit)
         {
@@ -1510,26 +1497,8 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
                   __err = ios_base::failbit;
                   break;
                 }
-	      if (__use_state)
-		{
-		  char_type __new_fmt[4];
-		  __new_fmt[0] = __fmt_start[0];
-		  __new_fmt[1] = __fmt_start[1];
-		  if (__mod)
-		    {
-		      __new_fmt[2] = __fmt_start[2];
-		      __new_fmt[3] = char_type();
-		    }
-		  else
-		    __new_fmt[2] = char_type();
-		  __s = _M_extract_via_format(__s, __end, __io, __err, __tm,
-					      __new_fmt, __state);
-		  if (__s == __end)
-		    __err |= ios_base::eofbit;
-		}
-	      else
-		__s = this->do_get(__s, __end, __io, __err, __tm, __format,
-				   __mod);
+	      __s = this->do_get(__s, __end, __io, __err, __tm,
+				 __format, __mod);
               ++__fmt;
             }
           else if (__ctype.is(ctype_base::space, *__fmt))
@@ -1556,8 +1525,8 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
               break;
             }
         }
-      if (__use_state)
-	__state._M_finalize_state(__tm);
+      __state._M_restore_from (__tm);
+      __state._M_finalize_state(__tm);
       return __s;
     }
 
@@ -1588,9 +1557,11 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
         }
 
       __time_get_state __state = __time_get_state();
+      __state._M_restore_from (__tm);
       __beg = _M_extract_via_format(__beg, __end, __io, __err, __tm, __fmt,
 				    __state);
       __state._M_finalize_state(__tm);
+      __state._M_save_to (__tm);
       if (__beg == __end)
 	__err |= ios_base::eofbit;
       return __beg;
diff --git a/libstdc++-v3/src/c++98/locale_facets.cc b/libstdc++-v3/src/c++98/locale_facets.cc
index c0bb7fd181d7f..28c34b5f3794a 100644
--- a/libstdc++-v3/src/c++98/locale_facets.cc
+++ b/libstdc++-v3/src/c++98/locale_facets.cc
@@ -181,12 +181,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   __time_get_state::
   _M_finalize_state(tm* tm)
   {
-    if (_M_have_I && _M_is_pm)
+    if (_M_have_I && _M_is_pm && (unsigned) tm->tm_hour < 12)
       tm->tm_hour += 12;
     if (_M_have_century)
       {
 	if (_M_want_century)
-	  tm->tm_year = tm->tm_year % 100;
+	  {
+	    tm->tm_year = tm->tm_year % 100;
+	    if (tm->tm_year < 0)
+	      tm->tm_year += 100;
+	  }
 	else
 	  tm->tm_year = 0;
 	tm->tm_year += (_M_century - 19) * 100;

-- 
Alexandre Oliva, happy hacker                https://FSFLA.org/blogs/lxo/
   Free Software Activist                       GNU Toolchain Engineer
Disinformation flourishes because many people care deeply about injustice
but very few check the facts.  Ask me about <https://stallmansupport.org>

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2023-02-23 18:22 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-02-17  7:47 [PATCH] [PR77760] [libstdc++] encode __time_get_state in tm Alexandre Oliva
2023-02-17 12:56 ` Jakub Jelinek
2023-02-22 14:27   ` Alexandre Oliva
2023-02-23 17:55     ` Alexandre Oliva
2023-02-23 18:21       ` Jonathan Wakely

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).