public inbox for gcc-help@gcc.gnu.org
 help / color / mirror / Atom feed
* Should atomic_xxx() functions reject not-_Atomic() arguments ?
       [not found] <72f6344e-d8b2-bab4-b047-63e298063492@gmch.uk>
@ 2020-02-27 17:27 ` Chris Hall
  2020-02-28 15:23   ` Jim Wilson
  0 siblings, 1 reply; 10+ messages in thread
From: Chris Hall @ 2020-02-27 17:27 UTC (permalink / raw)
  To: gcc-help

I'm told this belongs here and not in glibc-help...
----------

The C11/C18 Standard defines, for <stdatomic.h>, for example:

   C atomic_fetch_<key>(volatile A *object, M operand);

where "An A refers to one of the atomic types."

So:

    _Atomic(uint64_t) foo ;
    uint64_t bar ;

    bar = atomic_fetch_add(&foo, 1) ;

is all by the book.

Now, the Standard also tells us that _Atomic(uint64_t) and uint64_t may 
have different sizes, representations and alignment.  So I guess:

    bar = atomic_fetch_add(&bar, 1) ;

should be an error ?

On my x86_64, gcc 9.2/glibc 2.30 do not think it is an error, and indeed 
for the x86_64 _Atomic(uint64_t) and uint64_t are identical.

But this looks like a trap for the unwary... on some machine out there, 
the Standard says 'bar = atomic_fetch_add(&bar, 1) ;' is a *mistake*.

Or will gcc/glibs throw an error on such a machine ?

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Should atomic_xxx() functions reject not-_Atomic() arguments ?
  2020-02-27 17:27 ` Should atomic_xxx() functions reject not-_Atomic() arguments ? Chris Hall
@ 2020-02-28 15:23   ` Jim Wilson
  2020-02-28 21:04     ` Chris Hall
  0 siblings, 1 reply; 10+ messages in thread
From: Jim Wilson @ 2020-02-28 15:23 UTC (permalink / raw)
  To: Chris Hall; +Cc: gcc-help

On Thu, Feb 27, 2020 at 7:20 AM Chris Hall <gcc@gmch.uk> wrote:
> Now, the Standard also tells us that _Atomic(uint64_t) and uint64_t may
> have different sizes, representations and alignment.  So I guess:
>     bar = atomic_fetch_add(&bar, 1) ;
> should be an error ?

__atomic_fetch_add accepts any integer or pointer type.  So the fact
that _Atomic(uint64_t) and uint64_t may be different types is not a
problem, as long as they are still integer types.  This works like an
overloaded function in C++.

https://gcc.gnu.org/onlinedocs/gcc-9.2.0/gcc/_005f_005fatomic-Builtins.html#g_t_005f_005fatomic-Builtins

Jim

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Should atomic_xxx() functions reject not-_Atomic() arguments ?
  2020-02-28 15:23   ` Jim Wilson
@ 2020-02-28 21:04     ` Chris Hall
  2020-03-06 16:17       ` Chris Hall
  0 siblings, 1 reply; 10+ messages in thread
From: Chris Hall @ 2020-02-28 21:04 UTC (permalink / raw)
  To: Jim Wilson, Chris Hall; +Cc: gcc-help

On 28/02/2020 01:01, Jim Wilson wrote:
> On Thu, Feb 27, 2020 at 7:20 AM Chris Hall <gcc@gmch.uk> wrote:
>> Now, the Standard also tells us that _Atomic(uint64_t) and uint64_t may
>> have different sizes, representations and alignment.  So I guess:
>>      bar = atomic_fetch_add(&bar, 1) ;
>> should be an error ?

> __atomic_fetch_add accepts any integer or pointer type.  So the fact
> that _Atomic(uint64_t) and uint64_t may be different types is not a
> problem, as long as they are still integer types.  This works like an
> overloaded function in C++.
> 
> https://gcc.gnu.org/onlinedocs/gcc-9.2.0/gcc/_005f_005fatomic-Builtins.html#g_t_005f_005fatomic-Builtins

Sure.  But the Standard atomic_fetch_add() takes an _Atomic(xxx)* (as 
the first parameter), and for the reasons given, I understand that 
uint64_t* is not compatible with _Atomic(uint64_t)*.

Chris

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Should atomic_xxx() functions reject not-_Atomic() arguments ?
  2020-02-28 21:04     ` Chris Hall
@ 2020-03-06 16:17       ` Chris Hall
  2020-03-06 17:46         ` Jonathan Wakely
  0 siblings, 1 reply; 10+ messages in thread
From: Chris Hall @ 2020-03-06 16:17 UTC (permalink / raw)
  Cc: gcc-help

On 28/02/2020 15:27, Chris Hall wrote:
> On 28/02/2020 01:01, Jim Wilson wrote:
>> On Thu, Feb 27, 2020 at 7:20 AM Chris Hall <gcc@gmch.uk> wrote:
>>> Now, the Standard also tells us that _Atomic(uint64_t) and uint64_t may
>>> have different sizes, representations and alignment.  So I guess:
>>>      bar = atomic_fetch_add(&bar, 1) ;
>>> should be an error ?

>> __atomic_fetch_add accepts any integer or pointer type.  So the fact
>> that _Atomic(uint64_t) and uint64_t may be different types is not a
>> problem, as long as they are still integer types.  This works like an
>> overloaded function in C++.
>>
>> https://gcc.gnu.org/onlinedocs/gcc-9.2.0/gcc/_005f_005fatomic-Builtins.html#g_t_005f_005fatomic-Builtins 

> Sure.  But the Standard atomic_fetch_add() takes an _Atomic(xxx)* (as 
> the first parameter), and for the reasons given, I understand that 
> uint64_t* is not compatible with _Atomic(uint64_t)*.

FWIW: clang gets this right, and where the Standard says a parameter 
must be an _Atomic(foo_t)* [for a standard atomic_xxx()], clang rejects 
foo_t* arguments.

Chris

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Should atomic_xxx() functions reject not-_Atomic() arguments ?
  2020-03-06 16:17       ` Chris Hall
@ 2020-03-06 17:46         ` Jonathan Wakely
  2020-03-07 11:00           ` Andrew Haley
  2020-03-07 13:04           ` Chris Hall
  0 siblings, 2 replies; 10+ messages in thread
From: Jonathan Wakely @ 2020-03-06 17:46 UTC (permalink / raw)
  To: Chris Hall; +Cc: gcc-help

On Fri, 6 Mar 2020 at 16:17, Chris Hall <gcc@gmch.uk> wrote:
>
> On 28/02/2020 15:27, Chris Hall wrote:
> > On 28/02/2020 01:01, Jim Wilson wrote:
> >> On Thu, Feb 27, 2020 at 7:20 AM Chris Hall <gcc@gmch.uk> wrote:
> >>> Now, the Standard also tells us that _Atomic(uint64_t) and uint64_t may
> >>> have different sizes, representations and alignment.  So I guess:
> >>>      bar = atomic_fetch_add(&bar, 1) ;
> >>> should be an error ?
>
> >> __atomic_fetch_add accepts any integer or pointer type.  So the fact
> >> that _Atomic(uint64_t) and uint64_t may be different types is not a
> >> problem, as long as they are still integer types.  This works like an
> >> overloaded function in C++.
> >>
> >> https://gcc.gnu.org/onlinedocs/gcc-9.2.0/gcc/_005f_005fatomic-Builtins.html#g_t_005f_005fatomic-Builtins
>
> > Sure.  But the Standard atomic_fetch_add() takes an _Atomic(xxx)* (as
> > the first parameter), and for the reasons given, I understand that
> > uint64_t* is not compatible with _Atomic(uint64_t)*.

I don't think GCC is treating them as though they are compatible. It
just accepts a broader range of types than only  _Atomic ones, and
does the right thing for them all (apart from pointers where the
arithmetic is wrong).

> FWIW: clang gets this right, and where the Standard says a parameter
> must be an _Atomic(foo_t)* [for a standard atomic_xxx()], clang rejects
> foo_t* arguments.

It's not clear to me that C actually requires it to be rejected, or if
it's just undefined (in which case GCC's decision to accept it and do
the obvious thing is OK).

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Should atomic_xxx() functions reject not-_Atomic() arguments ?
  2020-03-06 17:46         ` Jonathan Wakely
@ 2020-03-07 11:00           ` Andrew Haley
  2020-03-07 19:27             ` Jonathan Wakely
  2020-03-07 13:04           ` Chris Hall
  1 sibling, 1 reply; 10+ messages in thread
From: Andrew Haley @ 2020-03-07 11:00 UTC (permalink / raw)
  To: Jonathan Wakely, Chris Hall; +Cc: gcc-help

On 3/6/20 5:45 PM, Jonathan Wakely wrote:
> On Fri, 6 Mar 2020 at 16:17, Chris Hall <gcc@gmch.uk> wrote:
> 
>> FWIW: clang gets this right, and where the Standard says a parameter
>> must be an _Atomic(foo_t)* [for a standard atomic_xxx()], clang rejects
>> foo_t* arguments.
> 
> It's not clear to me that C actually requires it to be rejected, or if
> it's just undefined (in which case GCC's decision to accept it and do
> the obvious thing is OK).

Except in pedantic mode. I remember there was a move to actually permit
this in C++:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4013.html

This seems obviously right to me...

-- 
Andrew Haley  (he/him)
Java Platform Lead Engineer
Red Hat UK Ltd. <https://www.redhat.com>
https://keybase.io/andrewhaley
EAC8 43EB D3EF DB98 CC77 2FAD A5CD 6035 332F A671


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Should atomic_xxx() functions reject not-_Atomic() arguments ?
  2020-03-06 17:46         ` Jonathan Wakely
  2020-03-07 11:00           ` Andrew Haley
@ 2020-03-07 13:04           ` Chris Hall
  2020-03-07 19:29             ` Jonathan Wakely
  2020-03-08 20:11             ` Martin Sebor
  1 sibling, 2 replies; 10+ messages in thread
From: Chris Hall @ 2020-03-07 13:04 UTC (permalink / raw)
  To: gcc-help

On 06/03/2020 17:45, Jonathan Wakely wrote:
> On Fri, 6 Mar 2020 at 16:17, Chris Hall <gcc@gmch.uk> wrote:
>>> Sure.  But the Standard atomic_fetch_add() takes an _Atomic(xxx)* (as
>>> the first parameter), and for the reasons given, I understand that
>>> uint64_t* is not compatible with _Atomic(uint64_t)*.

> I don't think GCC is treating them as though they are compatible. It
> just accepts a broader range of types than only  _Atomic ones, and
> does the right thing for them all (apart from pointers where the
> arithmetic is wrong).

>> FWIW: clang gets this right, and where the Standard says a parameter
>> must be an _Atomic(foo_t)* [for a standard atomic_xxx()], clang rejects
>> foo_t* arguments.

> It's not clear to me that C actually requires it to be rejected, or if
> it's just undefined (in which case GCC's decision to accept it and do
> the obvious thing is OK).

The subtlety of the distinction between "only defined to accept" and 
"defined to only accept" had escaped me for a moment :-(

Of course, for all the machines I know well enough I can say that there 
is no difference between, inter alia, uint64_t and _Atomic(uint64_t). 
So what I have observed GCC doing is, arguably, helpful.

For all I know, GCC may be equally helpful on machines where uint64_t 
and _Atomic(uint64_t) are not the same -- and issue some form of 
diagnostic.  Hopefully an error, rather than a 
-Wincompatible-pointer-types warning, since a warning would (probably?) 
not prevent the resulting code from crashing and burning.

If I write:

   uint64_t px(uint64_t* p) { return *p ; }

   char m[] = "There's glory for you!" ;

   printf("%lu", px(m+9)) ;

GCC chips in a -Wincompatible-pointer-types (if suitably prompted... 
-Wall ?).

Now, if I make a conscious decision to ignore all machines which may 
crash or otherwise misbehave for unaligned uint64_t reads, I can cast 
the m+9 -- and anything I then get is my fault.

I wonder if, by extension, the atomic_xxx() should (at least) warn when 
the _Atomic() parameters are passed not-_Atomic() arguments ?

As I have said elsewhere, I think the real problem is that the Standard 
fails to define vital things as Implementation Defined, leaving the 
programmer guessing as to what their code is going to do when compiled 
for some machine/system they are not familiar with.

I note 
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4013.html -- 
courtesy of Andrew Haley -- suggests new facilities to check at compile 
time specifically for this case.  IMO there should also be facilities to 
check for "wait-free" -- unless "lock-free" already does that job... but 
that's another story.

Chris

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Should atomic_xxx() functions reject not-_Atomic() arguments ?
  2020-03-07 11:00           ` Andrew Haley
@ 2020-03-07 19:27             ` Jonathan Wakely
  0 siblings, 0 replies; 10+ messages in thread
From: Jonathan Wakely @ 2020-03-07 19:27 UTC (permalink / raw)
  To: Andrew Haley; +Cc: Chris Hall, gcc-help

On Sat, 7 Mar 2020 at 11:00, Andrew Haley <aph@redhat.com> wrote:
>
> On 3/6/20 5:45 PM, Jonathan Wakely wrote:
> > On Fri, 6 Mar 2020 at 16:17, Chris Hall <gcc@gmch.uk> wrote:
> >
> >> FWIW: clang gets this right, and where the Standard says a parameter
> >> must be an _Atomic(foo_t)* [for a standard atomic_xxx()], clang rejects
> >> foo_t* arguments.
> >
> > It's not clear to me that C actually requires it to be rejected, or if
> > it's just undefined (in which case GCC's decision to accept it and do
> > the obvious thing is OK).
>
> Except in pedantic mode.

If the standard doesn't actually forbid it (which I don't think it
does) then it's up to the implementation if and when it diagnoses it.
Doing so for -pedantic might be reasonable though.

> I remember there was a move to actually permit
> this in C++:
>
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4013.html
>
> This seems obviously right to me...

Yeah. The feature is in C++20, via the std::atomic_ref<T> template.
You wrap an ordinary non-atomic variable in an atomic_ref and can
perform atomic operations on it.

For this to work, the implementation has to be able to accept
non-atomic variables in its __atomic_xxx functions (though not
necessarily in the public atomic_xxx ones).

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Should atomic_xxx() functions reject not-_Atomic() arguments ?
  2020-03-07 13:04           ` Chris Hall
@ 2020-03-07 19:29             ` Jonathan Wakely
  2020-03-08 20:11             ` Martin Sebor
  1 sibling, 0 replies; 10+ messages in thread
From: Jonathan Wakely @ 2020-03-07 19:29 UTC (permalink / raw)
  To: Chris Hall; +Cc: gcc-help

On Sat, 7 Mar 2020 at 16:01, Chris Hall wrote:
> Of course, for all the machines I know well enough I can say that there
> is no difference between, inter alia, uint64_t and _Atomic(uint64_t).

For some types on some targets the atomic version has stricter
alignment requirements. For example, it might be fine to access an int
that is only byte-aligned, but for the atomic instructions to work it
might need to be 4-byte aligned. This is true for some types on x86.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Should atomic_xxx() functions reject not-_Atomic() arguments ?
  2020-03-07 13:04           ` Chris Hall
  2020-03-07 19:29             ` Jonathan Wakely
@ 2020-03-08 20:11             ` Martin Sebor
  1 sibling, 0 replies; 10+ messages in thread
From: Martin Sebor @ 2020-03-08 20:11 UTC (permalink / raw)
  To: Chris Hall, gcc-help

On 3/7/20 6:04 AM, Chris Hall wrote:
> On 06/03/2020 17:45, Jonathan Wakely wrote:
>> On Fri, 6 Mar 2020 at 16:17, Chris Hall <gcc@gmch.uk> wrote:
>>>> Sure.  But the Standard atomic_fetch_add() takes an _Atomic(xxx)* (as
>>>> the first parameter), and for the reasons given, I understand that
>>>> uint64_t* is not compatible with _Atomic(uint64_t)*.
> 
>> I don't think GCC is treating them as though they are compatible. It
>> just accepts a broader range of types than only  _Atomic ones, and
>> does the right thing for them all (apart from pointers where the
>> arithmetic is wrong).
> 
>>> FWIW: clang gets this right, and where the Standard says a parameter
>>> must be an _Atomic(foo_t)* [for a standard atomic_xxx()], clang rejects
>>> foo_t* arguments.
> 
>> It's not clear to me that C actually requires it to be rejected, or if
>> it's just undefined (in which case GCC's decision to accept it and do
>> the obvious thing is OK).

The original outcome when atomics were first introduced in C as pure
library types (i.e., opaque structs -- see N1284) would have been
that these incompatibilities would have unavoidably been rejected.
But since then atomics morphed into first class language constructs
as have the atomic library APIs (see  N1485).  They now require
compiler support in the form of what are essentially function templates
that accepts arguments of unlimited (if not unconstrained) types.  In
view of that, one could argue that an implementation is conforming (and
useful) that accepts any types as arguments to the atomic APIs.  I don't
know if the choice of making the atomic APIs unconstrained in GCC was
deliberate but going back on that decision now, either in GCC, or by
trying to enforce it in the standard, after they've been out there for
a decade, seems risky.  This is a bit of a mess, in large part because
the integration of the specification for first-class atomic types into
C11 was less than perfect.

Some of this background is discussed in my article
https://developers.redhat.com/blog/2016/01/14/toward-a-better-use-of-c11-atomics-part-1/

> 
> The subtlety of the distinction between "only defined to accept" and 
> "defined to only accept" had escaped me for a moment :-(
> 
> Of course, for all the machines I know well enough I can say that there 
> is no difference between, inter alia, uint64_t and _Atomic(uint64_t). So 
> what I have observed GCC doing is, arguably, helpful.
> 
> For all I know, GCC may be equally helpful on machines where uint64_t 
> and _Atomic(uint64_t) are not the same -- and issue some form of 
> diagnostic.  Hopefully an error, rather than a 
> -Wincompatible-pointer-types warning, since a warning would (probably?) 
> not prevent the resulting code from crashing and burning.
> 
> If I write:
> 
>    uint64_t px(uint64_t* p) { return *p ; }
> 
>    char m[] = "There's glory for you!" ;
> 
>    printf("%lu", px(m+9)) ;
> 
> GCC chips in a -Wincompatible-pointer-types (if suitably prompted... 
> -Wall ?).
> 
> Now, if I make a conscious decision to ignore all machines which may 
> crash or otherwise misbehave for unaligned uint64_t reads, I can cast 
> the m+9 -- and anything I then get is my fault.
> 
> I wonder if, by extension, the atomic_xxx() should (at least) warn when 
> the _Atomic() parameters are passed not-_Atomic() arguments ?

I think it would be useful to expose a command-line option in GCC
to request a diagnostic for uses of the APIs with non-atomic types.
Making it an error would probably not be a great solution because
of the risk of breaking working code that has come to rely on it.

Martin

> 
> As I have said elsewhere, I think the real problem is that the Standard 
> fails to define vital things as Implementation Defined, leaving the 
> programmer guessing as to what their code is going to do when compiled 
> for some machine/system they are not familiar with.
> 
> I note 
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4013.html -- 
> courtesy of Andrew Haley -- suggests new facilities to check at compile 
> time specifically for this case.  IMO there should also be facilities to 
> check for "wait-free" -- unless "lock-free" already does that job... but 
> that's another story.
> 
> Chris


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2020-03-08 20:11 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <72f6344e-d8b2-bab4-b047-63e298063492@gmch.uk>
2020-02-27 17:27 ` Should atomic_xxx() functions reject not-_Atomic() arguments ? Chris Hall
2020-02-28 15:23   ` Jim Wilson
2020-02-28 21:04     ` Chris Hall
2020-03-06 16:17       ` Chris Hall
2020-03-06 17:46         ` Jonathan Wakely
2020-03-07 11:00           ` Andrew Haley
2020-03-07 19:27             ` Jonathan Wakely
2020-03-07 13:04           ` Chris Hall
2020-03-07 19:29             ` Jonathan Wakely
2020-03-08 20:11             ` Martin Sebor

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).