RFR (M): 8184334: Generalizing Atomic with templates

Andrew Haley aph at redhat.com
Fri Jul 21 16:26:35 UTC 2017

On 21/07/17 17:08, Erik Österlund wrote:
> I will make a store load pair of A* example split into three relevant cases:
> 1) A non-Atomic store is observed by an Atomic load
> 2) An Atomic store is observed by a non-Atomic load
> 3) An Atomic store is observed by an Atomic load
> Case 1: A non-Atomic store is observed by an Atomic load
> A non-Atomic store (possibly initialization code) stores an A* that is 
> observed as char* and subsequently reinterpret_casted to A*. I claim 
> that A* and char* are compatible types in this context.

It depends on exactly what this language means.  What is the type of
the lvalue?  And the type of the stored object?

If the type of the lvalue is any character type, it's fine.  If the
type of the lvalue is a pointer type other than the type of the stored
object, it is not.

> It is not undefined behaviour w.r.t. 3.10 if the type behind the pointer 
> in a pointer load is a char, regardless of the dynamic type it actually 
> points at. The load then has to assume conservatively that it does not 
> know what those bytes are referring to - it might be anything (including 
> the dynamic type behind the stored pointer: A). In other words, char 
> aliases all dynamic types that the pointer could point at. It seems like 
> we both agree this case is fine.
> Case 2: An Atomic store is observed by a non-Atomic load
> Conversely, if the Atomic API was to store something as a char* that 
> actually has a dynamic type of A*, and a normal load observes this as an 
> A*, that is also fine as the dynamic type is still A* despite being 
> stored as char*. In other words, the load is invariant of what type the 
> store had, w.r.t. 3.10, if its type matches the dynamic type. And it does.
> It seems to me like this is the case where you made a point that despite 
> being stored as char*, that does not mean that a non-Atomic load of A* 
> will understand the aliasing. And my point is that this is still not 
> undefined behavior w.r.t. 3.10 as the non-Atomic load is true to the 
> dynamic type. I hope we agree here.
> But then apart from the standard there is the problem of aliasing 
> optimizations of specific compilers, like -fstrict-aliasing on GCC. 

Well, they're not really separate: GCC does what the standard says it
may do.

> Let's remember that in C++03, "strict aliasing" is not a thing. And a 
> concrete points-to analysis might not know what the true dynamic type of 
> a pointer is, so depending on how conservative the data flow analysis, 
> points-to analysis and type-based aliasing analysis is, an aggressive 
> compiler might make further assumptions than what the standard outlines 
> as undefined behaviour, and as a result mess up the program. But then 
> again, we turn such optimizations off, and they are arguably not 
> supported by the standard. If we were to support that some day, perhaps 
> CanonicalPointer would be better off as the union of A* and char*. But 
> then as I mentioned, that is not enough because C++ programs can send, 
> via JNI, a pointer as a jlong into Java that will try to perform a CAS 
> through var handles and unsafe, and ultimately end up in a possible 
> runtime call to Atomic performing that CAS with Atomic::cmpxchg of 
> jlong. Therefore, we must inherently be able to deal with the dynamic 
> type being destroyed in "shady land" long before it reaches Atomic. It 
> will seemingly just never work reliably with -fstrict-aliasing, as far 
> as I can tell.

I think it can, but it would require a sophisticated analysis of what
we really mean.  Having said that, I have a lot of far more
interesting work to do, and I'm sure the same goes for you and
everyone else whi is a HotSpot author, so it won't get done.

> Case 3: An Atomic store is observed by an Atomic load
> This seems trivially compatible as the store and load pair are 
> communicating through the same type, char* that are compatible and aliased.

I may have misinterpreted what you were suggesting.  It seemed to me
that you said you'd have a canonical pointer type, and all of your
pointer stores would go through something like

void store_ptr(char **addr, char *ptr) {
  *addr = ptr;

and we could do something like

  long a_long;
  long *long_ptr;
  store_ptr((char**)&long_ptr, (char*)&a_long);

to make long_ptr point to a_long.

But I'm happy to drop this conversation, because we're agreed we're
going to use -fno-strict-aliasing everywhere, so this kind of
correctness doesn't matter.

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

More information about the hotspot-runtime-dev mailing list