This is the mail archive of the
mailing list for the glibc project.
Re: Implementing C++1x and C1x atomics
- From: Lawrence Crowl <crowl at google dot com>
- To: "Joseph S. Myers" <joseph at codesourcery dot com>
- Cc: Richard Guenther <richard dot guenther at gmail dot com>, "Boehm, Hans" <hans dot boehm at hp dot com>, Andrew Haley <aph at redhat dot com>, Paolo Bonzini <bonzini at gnu dot org>, gcc at gcc dot gnu dot org, libc-alpha at sourceware dot org
- Date: Thu, 13 Aug 2009 14:52:10 -0700
- Subject: Re: Implementing C++1x and C1x atomics
- References: <4A82E93B.email@example.com> <4A82F47A.firstname.lastname@example.org> <4A82F5C0.email@example.com> <4A82F88D.firstname.lastname@example.org> <238A96A773B3934685A7269CC8A8D042577A01E71A@GVW0436EXB.americas.hpqcorp.net> <238A96A773B3934685A7269CC8A8D042577A01E754@GVW0436EXB.americas.hpqcorp.net> <email@example.com> <Pine.LNX.firstname.lastname@example.org> <email@example.com> <Pine.LNX.firstname.lastname@example.org>
On 8/12/09, Joseph S. Myers <email@example.com> wrote:
> On Wed, 12 Aug 2009, Lawrence Crowl wrote:
> > > The C1x atomics specification
> > > <http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1349.htm>
> > > does not mention any amendment to the list of headers to be
> > > provided by freestanding implementations (clause 4 paragraph
> > > 6), which suggests that providing this header is actually in
> > > the domain of libc, not the compiler (which accords with it
> > > providing (generic) library functions, which the headers for
> > > freestanding implementations do not). It seems clear however
> > > that close cooperation with the compiler will be needed in
> > > the implementation to ensure that the right semantics are
> > > available from the compiler for the header to use.
> > I think the C++ specification currently has the same problem.
> > The atomics are really a compiler issue.
> In that it defines functions, <stdatomic.h> is unlike all the
> headers presently required of freestanding implementations,
But <exception>, <new>, and <typeinfo> all define functions.
> and seems much more like <tgmath.h> - a header that involves
> library functions, depends on the library to some extent (if
> you implement the ISO 24747 functions then that has additions
> to <tgmath.h> as well as <math.h>) but is nevertheless almost
> entirely compiler-specific.
We agree that is almost entirely compiler-specific.
> > > The information the compiler should provide the library would
> > > include which operations are available built-in or in libgcc
> > > and which the C library has to emulate with locks (if e.g. a
> > > particular architecture or subarchitecture does not have 64-bit
> > > atomic operations). Given how many variations there are on
> > > what targets support and how, and given that what's supported
> > > may depend on -march etc. options passed when compiling code
> > > that includes the header, I think a single version of the
> > > header for all targets, that contains code conditional on
> > > various predefined macros, would be a good ideal to aim for.
> > The compiler must recognize all the atomic operations, so that
> > it can respect their implications, which means that they must
> > be intrinsics.
> It is of course valid for a user program to wrap a call to an
> atomic operation in a call to its own function that calls an
> intrinsic - and a macro or inline function in the header could
> just as much use intrinsics in some cases and fall back to library
> functions, or more complicated sequences of intrinsics, in other
> cases. It doesn't seem immediately obvious whether the compiler
> should provide intrinsics for each case and handle falling back to
> the library as needed or whether the header should handle fallback.
I think it is best if the compiler provides all of the intrinsics.
This approach will enable a smoother upgrade to newer processors.
> If the compiler provides all the intrinsics, it needs to agree
> with the library on what all the underlying functions are.
We really need to clarify what "the library" is here. If you mean
the C++ standard library, as delivered by Dinkumware, RogueWave or
STLport, then I think the answer is definitely not. Those libraries
should just use the atomic header as though they were client code.
If you mean the OS-supplied platform-dependent library, then I
think the answer is yes. The names of the routines that back up
the intrinsics should be part of the platform ABI.
> > Then there is direct instruction support for all the operations
> > of a type, it should emit that code. When not, the compiler
> > should emit a call into a shared library that comes with
> > the system.
> Existing practice is to use static-only libgcc functions for
> __sync_* where they go in libgcc (for ARM, SH and PA GNU/Linux,
> where kernel help is needed for atomic operations in some cases),
> not shared libraries. But those are cases where atomic operations
> are supported with kernel help rather than ones where you are
> trying to make a userspace emulation with locks - and it's
> the kernel's job to make the kernel-supported implementations
> interoperate with native hardware instructions. "comes with the
> system" could mean either libgcc or libc (or a vDSO provided by
> the kernel).
> > You really do not want to take the risk of getting two different
> > implementations of the atomics. They will fail to synchronize
> > with each other, and result in intermitent concurrency bugs.
> > Yuck.
> Is the point here that it's problematic to use real atomic
> operations on some subarchitectures that support them for a given
> type but emulations using locks for other subarchitectures, because
> they will not interoperate properly? That does seem a good point,
> that which types direct atomic operations are used on must be
> considered part of the platform ABI, and so if newer hardware adds
> 64-bit atomic operations (say) they must still not be used if some
> other code (accessing the same object) might be using emulations.
That is the point. Any type that is not known to have a direct
implementation, should be implemented on the platform via a library.
That way all uses of that type will share the same implementation,
which may be busy-waiting on older machines and atomic instructions
on newer machines.
> Since static libgcc is generally useful and so programs may end
> up using functions from more than one version of libgcc your
> suggestion of putting functions in a shared library to avoid this
> issue would imply:
> * The out-of-line functions go in shared libc.
> * The header therefore comes with libc.
I don't think we need a header. These calls are directly generated
by the compiler, not referenced by the user.
> * The header never uses an inline operation when compiling for a
> particular subarchitecture unless the corresponding version of
> libc, when executing on hardware capable of executing code for
> that subarchitecture, will always use an atomic operation that
> interoperates correctly with the header. (libc might need in
> some cases to determine the hardware in use at runtime.)
I'm not quite following that. Any any event, since I don't see
the need for a header, I think it is moot.
> So whether an operation is inlined would be a function both
> of what the compiler knows the hardware supports and what the
> header knows about what libc will do at runtime. (But much of
> the complexity only arises when a single ABI supports hardware
> with different sets of atomic operations.)
The i386 architecture will run on later processors. A dynamic
library on those later processors can implement the atomics via
atomic instructions rather than locking.
> (Using functions present in libgcc_s but not static libgcc might
> be possible here as an alternative to using libc, but libc can
> more readily access HWCAP information for hardware identification
> at runtime than libgcc can.)
My preference would be to put all this information in libc. We want
all compilers, regardless of vendor, to use the same functions on
a given platform.
> > > The compiler would also need to meet the underlying memory
> > > model requirements - avoiding optimizations that break the
> > > memory model assumptions (writes to locations that may not
> > > be written in the memory model, in particular) unless given
> > > an option to say it doesn't need to follow the model.
> > My recommendation is to just do a good enough job on _changing_
> > the optimizations so that you don't need an option. It would
> > probably avoid some rather elusive bugs when someone links in
> > a library compiled the wrong way.
> I'm thinking of such an option as being one to use when building
> a single-threaded program, not for use when building libraries.
That is the scenario that works. However, my experience is that
if there is no option to get back to the old optimization, there
is more incentive to do a good job on the new optimization. :-)
Furthermore, fewer options yields a better user experience, and
very few users will fine-tune their options, so it is usually better
for the compiler implementors to do more work to avoid the option.