From: Alexandre Oliva <oliva@adacore.com>
To: gcc-patches@gcc.gnu.org, libstdc++@gcc.gnu.org
Cc: jakub@redhat.com
Subject: [PATCH] [PR77760] [libstdc++] encode __time_get_state in tm
Date: Fri, 17 Feb 2023 04:47:45 -0300 [thread overview]
Message-ID: <orh6vkravy.fsf@lxoliva.fsfla.org> (raw)
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>
next reply other threads:[~2023-02-17 7:48 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-02-17 7:47 Alexandre Oliva [this message]
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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=orh6vkravy.fsf@lxoliva.fsfla.org \
--to=oliva@adacore.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=jakub@redhat.com \
--cc=libstdc++@gcc.gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).