[PATCH] libstdc++: Add mem_order_hle_acquire/release to atomic.h

Jonathan Wakely jwakely.gcc@gmail.com
Fri Nov 9 19:56:00 GMT 2012


On 9 November 2012 19:43, Andi Kleen wrote:
>> So please bear with me while I try to understand what these flags are
>> for - I haven't followed the HLE work and don't know where they're
>> documented (if at all.)
>
> I just submitted a patch to document them. There was a gcc-patches
> discussion some time ago.
>
>>
>> Is it valid to call a.store(1, memory_order_hle_acquire) or are they
>> only valid as modifiers on other memory orders?
>
> The compiler currently warns and forces acquire for hle_acquire
> (or release for hle_release)
>
>> If they're not valid on their own then I would suggest the modifies
>> should be a separate type, and overload operator| like so:
>>
>> enum __memory_order_hle_mod
>> {
>>     __memory_order_hle_acquire = 0x10000,
>>     __memory_order_hle_release = 0x20000
>> };
>>
>> constexpr memory_order
>> operator|(memory_order __m, __memory_order_hle_mod __mod)
>> {
>>     return memory_order(__m | __mod);
>> }
>>
>> This ensures users *cannot* call a.store(1,
>> __memory_order_hle_acquire) because it's the wrong type, but they
>
> They would get a warning in any case.

Only if a compile-time constant is used, if it's passed in as a
function parameter it won't be:

extern void clear_it(std::atomic<int>& a, memory_order m);

void clear_it(std::atomic<int>& a, memory_order m)
{
  a.clear(m);
}


>> *can* call a.store(1, memory_order_acquire|__memory_order_hle_acquire)
>> because that calls the overloaded operator which returns the right
>> type.
>
> Ok. Thanks.

Here's an untested implementation of that idea:

enum __memory_order_hle_mod
{
    __memory_order_hle_mask = 0x0ffff,
    __memory_order_hle_acquire = 0x10000,
    __memory_order_hle_release = 0x20000
};

constexpr memory_order
operator|(memory_order __m, __memory_order_hle_mod __mod)
{
    return memory_order(__m | int(__mod));
}

constexpr memory_order
operator&(memory_order __m, __memory_order_hle_mod __mod)
{
    return memory_order(__m & int(__mod));
}


Which would avoid the casts in the member functions:

     void
     clear(memory_order __m = memory_order_seq_cst) noexcept
     {
      auto __b = (__m & memory_order_mask);
      __glibcxx_assert(__b != memory_order_consume);
      __glibcxx_assert(__b != memory_order_acquire);
      __glibcxx_assert(__b != memory_order_acq_rel);

       __atomic_clear (&_M_i, __m);
     }

I assume __b will be completely optimised away when the assertions are
disabled, and we can add __attribute__((unused)) later if needed, to
prevent warnings with -Wsystem-headers

We could define operator^ and |= etc.  if they might be needed:

memory_order&
operator|=(memory_order& __m, __memory_order_hle_mod __mod)
{
    return __m = memory_order(__m | int(__mod));
}


We could even keep the constexpr on __cmpexch_failure_order if we
wanted (also untested):

constexpr memory_order
__cmpexch_failure_order2(memory_order __m) noexcept
{
  return __m == memory_order_acq_rel ? memory_order_acquire
    : __m == memory_order_release ? memory_order_relaxed : __m;
}

constexpr memory_order
__cmpexch_failure_order(memory_order __m) noexcept
{
  return memory_order(__cmpexch_failure_order2(__m & __memory_order_hle_mask)
    | (__m & __memory_order_hle_acquire)
    | (__m & __memory_order_hle_release));
}



More information about the Libstdc++ mailing list