libstdc++: Fix warnings from std::make_unsigned<_Atomic_word> [PR122172]

GCC gives a -Wignored-attributes warning when a class template is
instantiated with a type that has an aligned(n) attribute. Specifically,
cris-elf uses 'typedef int __attribute_((__aligned(4))) _Atomic_word;'
and so compiling libstdc++ headers gives:
warning: ignoring attributes on template argument ‘int’ [-Wignored-attributes]

This commit reduces four occurrences of make_unsigned<_Atomic_word> into
two, one in bits/shared_ptr_base.h and one in ext/atomicity.h, and uses
diagnostic pragmas around the two remaining uses to avoid the warnings.
Because the unsigned type might have lost the alignment of _Atomic_word
that is needed for atomic ops (at least on cris-elf), the unsigned type
should only be used for plain non-atomic arithmetic. To prevent misuse,
it's defined as a private type in _Sp_counted_base, and is defined and
then undefined as a macro in ext/atomicity.h, so that it's not usable
after __exchange_and_add_single and __atomic_add_single have been
defined.

We also get a warning from instantiating __int_traits<_Atomic_word> in
shared_ptr_base.h which can be avoided by calculating the maximum signed
value from the maximum unsigned value.

libstdc++-v3/ChangeLog:

	PR libstdc++/122172
	* include/bits/shared_ptr_base.h (_Sp_counted_base): Define
	_Unsigned_count_type for make_unsigned<_Atomic_word>.
	Replace __int_traits<_Atomic_word> with equivalent expression.
	* include/ext/atomicity.h (_GLIBCXX_UNSIGNED_ATOMIC_WORD):
	Define macro for unsigned type to use for arithmetic.
	(__exchange_and_add_single, __atomic_add_single): Use it.

Reviewed-by: Hans-Peter Nilsson <hp@axis.com>
This commit is contained in:
Jonathan Wakely 2025-10-06 15:51:28 +01:00 committed by Jonathan Wakely
parent 52ee235811
commit 385984f555
No known key found for this signature in database
2 changed files with 38 additions and 22 deletions

View File

@ -230,25 +230,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
long
_M_get_use_count() const noexcept
{
// No memory barrier is used here so there is no synchronization
// with other threads.
auto __count = __atomic_load_n(&_M_use_count, __ATOMIC_RELAXED);
// If long is wider than _Atomic_word then we can treat _Atomic_word
// as unsigned, and so double its usable range. If the widths are the
// same then casting to unsigned and then to long is a no-op.
using _Up = typename make_unsigned<_Atomic_word>::type;
// No memory barrier is used here so there is no synchronization
// with other threads.
return (_Up) __atomic_load_n(&_M_use_count, __ATOMIC_RELAXED);
return static_cast<_Unsigned_count_type>(__count);
}
private:
_Sp_counted_base(_Sp_counted_base const&) = delete;
_Sp_counted_base& operator=(_Sp_counted_base const&) = delete;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wignored-attributes"
// This is only to be used for arithmetic, not for atomic ops.
using _Unsigned_count_type = make_unsigned<_Atomic_word>::type;
#pragma GCC diagnostic pop
// Called when incrementing _M_use_count to cause a trap on overflow.
// This should be passed the value of the counter before the increment.
static void
_S_chk(_Atomic_word __count)
{
constexpr _Atomic_word __max_atomic_word = _Unsigned_count_type(-1)/2;
// __max is the maximum allowed value for the shared reference count.
// All valid reference count values need to fit into [0,LONG_MAX)
// because users can observe the count via shared_ptr::use_count().
@ -266,8 +274,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// would not fit in [0,LONG_MAX) after casting to an unsigned type,
// which would cause use_count() to return bogus values.
constexpr _Atomic_word __max
= sizeof(long) > sizeof(_Atomic_word)
? -1 : __gnu_cxx::__int_traits<_Atomic_word>::__max;
= sizeof(long) > sizeof(_Atomic_word) ? -1 : __max_atomic_word;
if (__count == __max) [[__unlikely__]]
__builtin_trap();
@ -300,8 +307,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
inline long
_Sp_counted_base<_S_single>::_M_get_use_count() const noexcept
{
using _Up = typename make_unsigned<_Atomic_word>::type;
return (_Up) _M_use_count;
return static_cast<_Unsigned_count_type>(_M_use_count);
}

View File

@ -90,20 +90,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_Atomic_word_fits_in_long[sizeof(_Atomic_word) <= sizeof(long) ? 1 : -1];
#endif
// Targets where _Atomic_word uses __attribute__((__aligned__(n))) will get
// a warning for make_unsigned<_Atomic_word>. That warning can be ignored,
// because we only need an unsigned type, we don't care about its alignment.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wignored-attributes"
// We need an unsigned type that can be used for the arithmetic below.
// This type must not be use for atomic ops because it might not be
// sufficiently aligned. Define it as a macro that we #undef below,
// to prevent misuse elsewhere in the library.
#if __cplusplus >= 201103L
# define _GLIBCXX_UNSIGNED_ATOMIC_WORD std::make_unsigned<_Atomic_word>::type
#else
// For most targets make_unsigned_t<_Atomic_word> is unsigned int,
// but 64-bit sparc uses long for _Atomic_word, so needs unsigned long.
// Sign-extending to unsigned long works for both cases, so use that.
# define _GLIBCXX_UNSIGNED_ATOMIC_WORD unsigned long
#endif
inline _Atomic_word
__attribute__((__always_inline__))
__exchange_and_add_single(_Atomic_word* __mem, int __val)
{
_Atomic_word __result = *__mem;
// Do the addition with an unsigned type so that overflow is well defined.
#if __cplusplus >= 201103L
std::make_unsigned<_Atomic_word>::type __u;
#else
// For most targets make_unsigned_t<_Atomic_word> is unsigned int,
// but 64-bit sparc uses long for _Atomic_word.
// Sign-extending to unsigned long works for both cases.
unsigned long __u;
#endif
_GLIBCXX_UNSIGNED_ATOMIC_WORD __u;
__u = __result;
__u += __val;
*__mem = __u;
@ -114,15 +126,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__attribute__((__always_inline__))
__atomic_add_single(_Atomic_word* __mem, int __val)
{
#if __cplusplus >= 201103L
std::make_unsigned<_Atomic_word>::type __u;
#else
unsigned long __u; // see above
#endif
_GLIBCXX_UNSIGNED_ATOMIC_WORD __u;
__u = *__mem;
__u += __val;
*__mem = __u;
}
#undef _GLIBCXX_UNSIGNED_ATOMIC_WORD
#pragma GCC diagnostic pop
inline _Atomic_word
__attribute__ ((__always_inline__))