[cfe-dev] libc++: help diagnosing the following std::atomic compile error

Howard Hinnant hhinnant at apple.com
Tue May 21 07:18:51 PDT 2013


On May 21, 2013, at 9:04 AM, David Chisnall <David.Chisnall at cl.cam.ac.uk> wrote:

> On 20 May 2013, at 22:49, Richard Smith <richard at metafoo.co.uk> wrote:
> 
>> struct T { char c[3]; };
>> static_assert(sizeof(std::atomic<T>) == 3 && alignof(std::atomic<T>) == 1, "under libstdc++");
>> static_assert(sizeof(std::atomic<T>) == 4 && alignof(std::atomic<T>) == 4, "under libc++");
> 
> This looks like an ABI issue, but I don't imagine that we'll encounter any ABIs where the libstdc++ answer should be correct for alignof.  It's debatable whether the sizeof answer is correct, as the last byte should be padding, but as the intention of C11 _Atomic() and C++11 std::atomic<> is to be compiled down (for <word size types) to single atomic load / store operations (or short load-linked, store-conditional sequences) without exposing racy issues libstdc++ answer looks wrong (I know of no architectures which can do 3-byte atomic ops, and with alignof(1) this could end up spanning two cache lines and be impossible to operate on atomically without a mutex in anything except Haswell or BlueGene/Q).
> 
> This is my concern with the GCC atomic builtins, and why I am not happy with clang supporting them or encouraging their use.  There is explicitly no guarantee in C11 that T and _Atomic(T) have the same ABI and it was expected that they would not, because efficient atomic operations have stricter constraints (much stricter on some architectures, a bit stricter on x86).  It is almost impossible to use the GCC builtins correctly in a situation where the ABIs differ for the atomic and non-atomic types.
> 
> David
> 

I'm getting {3, 1}, not {4, 4} with both the current <atomic>, and the one I'm testing.  That is on both x86_64, and i386, Mountain Lion.


> On May 21, 2013, at 9:11 AM, David Chisnall <David.Chisnall at cl.cam.ac.uk> wrote:
> 
>> 
>> On 21 May 2013, at 01:55, Howard Hinnant <hhinnant at apple.com> wrote:
>> 
>>> I've enclosed a proposed patch for fixing <atomic>.  This patch changes us from using _Atomic(T) for the stored type, to just T, but aligned to _Atomic(T).  
>> 
>> This may still end up with the std::atomic<> types lacking padding in structures that makes it possible to act on them correctly.  For example, what do you get when you do:
>> 
>> struct {
>> 	std::atomic<char> x;
>> 	char y;
>> };
>> 
>> y should be sufficiently after x that they can be operated on independently (at least from an ISA perspective - if they're in the same cache line then most RISCy CPUs will do a bit of redundant work anyway).  Even though the alignment of y is 1, we (typically) should be adding 3 bytes of padding after x.
> 

I should stress that I have no particular attachment to this approach.  From Richard's suggestions:


On May 20, 2013, at 5:20 PM, Richard Smith <richard at metafoo.co.uk> wrote:

> I think there are basically two long-term options and one short-term workaround:
> 
> 1) Use T rather than _Atomic(T), manually ensure that std::atomic<T> has the right alignment, and use the __atomic_* builtins instead of the __c11_atomic_* builtins (this is the approach which libstdc++ takes), or
> 2) Get someone to implement initialization support for _Atomic(T), where T is a class type where the selected copy/move constructor is trivial. I don't have time to work on this right now, I'm afraid.
> 
> Workaround: Use __c11_atomic_init instead of initializing the member in the mem-initializer-list, for now at least. This has the disadvantage that you can't mark the constructor as 'constexpr', but you could restrict this to the case of non-scalar T to avoid regressing.

I picked the one that I had the skills to do.  It is fine with me if we abandon 1), and go for 2).

Howard




More information about the cfe-dev mailing list