[libstdc++/65033] Give alignment info to libatomic
Jonathan Wakely
jwakely@redhat.com
Fri Apr 3 14:13:00 GMT 2015
On 03/04/15 05:24 -0400, Hans-Peter Nilsson wrote:
>On Thu, 2 Apr 2015, Hans-Peter Nilsson wrote:
>> Why then use __alignof(_M_i) (the object-alignment)
>> instead of _S_alignment (the deduced alas insufficiently
>> increased type-alignment)?
Isn't the object aligned to _S_alignment?
>
>(The immediate reason is that _S_alignment wasn't there until a
>later revision, but the gist of the question remains. :-)
>> > making sure that atomic_is_lock_free returns the same
>> > value for all objects of a given type,
>>
>> (That would work but it doesn't seem to be the case.)
Because we haven't done anything to increase the alignment for the
__atomic_base<_ITp> specialization yet, see the additional patch at:
https://gcc.gnu.org/ml/gcc-patches/2015-03/msg01664.html
(Although that's insufficient as you point out, because it should
depend on the size too.)
>> > we probably should have changed the
>> > interface so that we would pass size and alignment rather than size and object
>> > pointer.
>> >
>> > Instead, we decided that passing null for the object pointer would be
>> > sufficient. But as this PR shows, we really do need to take alignment into
>> > account.
>>
>> Regarding what's actually needed, alignment of an atomic type
>> should always be *forced to be at least the natural alignment of
>> of the object* (with non-power-of-two sized-objects rounded up)
>> and until then atomic types won't work for targets where the
>> non-atomic equivalents have less alignment (as straddling a
>> page-boundary won't be lock-less-atomic anywhere where
>> straddling a page-boundary may cause a non-atomic-access...) So,
>> not target-specific except for targets that require even
>> higher-than-natural alignment.
>
>So, can we do something like this instead for gcc5?
>(Completely untested and may be syntactically, namespacingly and
>cxxstandardversionly flawed.)
>
>Index: include/std/atomic
>===================================================================
>--- include/std/atomic (revision 221849)
>+++ include/std/atomic (working copy)
>@@ -165,16 +165,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> struct atomic
> {
> private:
>- // Align 1/2/4/8/16-byte types the same as integer types of that size.
>+ // Align 1/2/4/8/16-byte types to the natural alignment of that size.
> // This matches the alignment effects of the C11 _Atomic qualifier.
> static constexpr int _S_min_alignment
>- = sizeof(_Tp) == sizeof(char) ? alignof(char)
>- : sizeof(_Tp) == sizeof(short) ? alignof(short)
>- : sizeof(_Tp) == sizeof(int) ? alignof(int)
>- : sizeof(_Tp) == sizeof(long) ? alignof(long)
>- : sizeof(_Tp) == sizeof(long long) ? alignof(long long)
>+ = sizeof(_Tp) == sizeof(char) ? max(sizeof(char), alignof(char))
>+ : sizeof(_Tp) == sizeof(short) ? max(sizeof(short), alignof(short))
>+ : sizeof(_Tp) == sizeof(int) ? max(sizeof(int), alignof(int))
>+ : sizeof(_Tp) == sizeof(long) ? max(sizeof(long), alignof(long))
>+ : sizeof(_Tp) == sizeof(long long) ? max(sizeof(long long), alignof(long long))
> #ifdef _GLIBCXX_USE_INT128
>- : sizeof(_Tp) == sizeof(__int128) ? alignof(__int128)
>+ : sizeof(_Tp) == sizeof(__int128) ? max(sizeof(__int128), alignof(__int128))
> #endif
> : 0;
Instead of changing every case in the condition to include sizeof why
not just do it afterwards using sizeof(_Tp), in the _S_alignment
calculation?
We know sizeof(_Tp) == sizeof(corresponding integer type) because
that's the whole point of the conditionals! See attachment.
>@@ -216,7 +216,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> is_lock_free() const noexcept
> {
> // Produce a fake, minimally aligned pointer.
>- void *__a = reinterpret_cast<void *>(-__alignof(_M_i));
>+ void *__a = reinterpret_cast<void *>(-_S_alignment);
> return __atomic_is_lock_free(sizeof(_M_i), __a);
> }
If _M_i is aligned to _S_alignment then what difference does the
change above make?
It doesn't matter if the value is per-object if we've forced all such
objects to have the same alignment, does it?
Or is it different if a std::atomic<T> is included in some other
struct and the user forces a different alignment on it? I don't think
we really need to support that, users shouldn't be doing that.
>Index: include/bits/atomic_base.h
>===================================================================
>--- include/bits/atomic_base.h (revision 221849)
>+++ include/bits/atomic_base.h (working copy)
>@@ -240,7 +240,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> private:
> typedef _ITp __int_type;
>
>- __int_type _M_i;
>+ // Align 1/2/4/8/16-byte types to the natural alignment of that size.
>+ // This matches the alignment effects of the C11 _Atomic qualifier.
>+ static constexpr int _S_min_alignment
>+ = sizeof(_Tp) == sizeof(char) ? max(sizeof(char), __alignof(char))
>+ : sizeof(_Tp) == sizeof(short) ? max(sizeof(short), __alignof(short))
>+ : sizeof(_Tp) == sizeof(int) ? max(sizeof(int), __alignof(int))
>+ : sizeof(_Tp) == sizeof(long) ? max(sizeof(long), __alignof(long))
>+ : sizeof(_Tp) == sizeof(long long) ? max(sizeof(long long), __alignof(long long))
>+#ifdef _GLIBCXX_USE_INT128
>+ : sizeof(_Tp) == sizeof(__int128) ? max(sizeof(__int128), __alignof(__int128))
>+#endif
>+ : 0;
>+
>+ static constexpr int _S_alignment
>+ = _S_min_alignment > alignof(_Tp) ? _S_min_alignment : __alignof(_Tp);
>+
>+ __int_type _M_i __attribute ((__aligned(_S_alignment)));
This is massively overcomplicated. Unlike the generic template in
<atomic> that handles arbitrary types, here in <bits/atomic_base.h> we
know for a fact that _ITp is one of the standard integer types so we
don't need to do the silly dance to find a standard integer type of
the same size.
The attached patch against trunk should have the same result with much
less effort.
It doesn't include the changes to the reinterpret_cast<void *>
expressions to produce a minimally aligned pointer, but I think this
is progress, thanks :-)
-------------- next part --------------
diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h
index 8104c98..79769cf 100644
--- a/libstdc++-v3/include/bits/atomic_base.h
+++ b/libstdc++-v3/include/bits/atomic_base.h
@@ -240,7 +240,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
private:
typedef _ITp __int_type;
- __int_type _M_i;
+ static constexpr int _S_alignment =
+ sizeof(_ITp) > alignof(_ITp) ? sizeof(_ITp) : alignof(_ITp);
+
+ alignas(_S_alignment) __int_type _M_i;
public:
__atomic_base() noexcept = default;
diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic
index 88c8b17..81f29d8 100644
--- a/libstdc++-v3/include/std/atomic
+++ b/libstdc++-v3/include/std/atomic
@@ -165,9 +165,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
struct atomic
{
private:
- // Align 1/2/4/8/16-byte types the same as integer types of that size.
+ // Align 1/2/4/8/16-byte types the natural alignment of that size.
// This matches the alignment effects of the C11 _Atomic qualifier.
- static constexpr int _S_min_alignment
+ static constexpr int _S_int_alignment
= sizeof(_Tp) == sizeof(char) ? alignof(char)
: sizeof(_Tp) == sizeof(short) ? alignof(short)
: sizeof(_Tp) == sizeof(int) ? alignof(int)
@@ -178,6 +178,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#endif
: 0;
+ static constexpr int _S_min_alignment
+ = _S_int_alignment > sizeof(_Tp) ? _S_int_alignment : sizeof(_Tp);
+
static constexpr int _S_alignment
= _S_min_alignment > alignof(_Tp) ? _S_min_alignment : alignof(_Tp);
More information about the Libstdc++
mailing list