RFR (M): 8184334: Generalizing Atomic with templates

Erik Österlund erik.osterlund at oracle.com
Tue Jul 18 13:46:42 UTC 2017



On 2017-07-18 11:57, Andrew Haley wrote:
> On 18/07/17 10:38, Erik Österlund wrote:
>
>>> ------------------------------------------------------------------------
>>> 3.10, Lvalues and rvalues
>>>
>>> If a program attempts to access the stored value of an object through
>>> a glvalue of other than one of the following types the behavior is
>>> undefined:
>>>
>>> — the dynamic type of the object,
>>>
>>> — a cv-qualified version of the dynamic type of the object,
>>>
>>> — a type similar (as defined in 4.4) to the dynamic type of the object,
>>>
>>> — a type that is the signed or unsigned type corresponding to the
>>>     dynamic type of the object,
>>>
>>> — a type that is the signed or unsigned type corresponding to a
>>>     cv-qualified version of the dynamic type of the object,
>>>
>>> — an aggregate or union type that includes one of the aforementioned
>>>     types among its elements or non- static data members (including,
>>>     recursively, an element or non-static data member of a subaggregate
>>>     or contained union),
>>>
>>> — a type that is a (possibly cv-qualified) base class type of the
>>>     dynamic type of the object,
>>>
>>> — a char or unsigned char type.
>>> ------------------------------------------------------------------------
>>>
>>> You only have permission to convert pointers to intptr_t and back: you
>>> do not have permission to access the stored value of a pointer an an
>>> intptr_t.
>> I would say the scenario you describe goes under "the dynamic type of
>> the object" or "a type that is the signed or unsigned type corresponding
>> to the dynamic type of the object",
> OK.
>
>> in the quoted section 3.10 of the standard, depending on specific
>> use case.  The problem that type aliasing is aimed at is if you
>> store an A* and then load it as a B*, then the dynamic type is A*,
>> yet it is loaded as B*, where B is not compatible with A.
> Precisely.  That is what is happening in this case.  A is, say, void*
> and B is intptr_t.  void* is not compatible with intptr_t.

My interpretation is that the aliasing rules are for points-to analysis 
being able to alias that if somebody stores A* and then other code loads 
that as B* and accesses B, then it is assumed that the B* does not 
points-to the A* as they are of incompatible types, and that therefore 
it is fine to load something (that was stored as A*) as intptr_t and 
subsequently cast it to A* before the A itself is being accessed. Am I 
wrong?

For example, the following test program compiles and runs with g++ 
-fstrict-aliasing -Wstrict-aliasing=3 -O3 -std=c++03:

#include <stdio.h>
#include <stdint.h>

class A{
public:
   int _x;
   A() : _x(0) {}
};

int main(int argc, char* argv[]) {
   A a;
   A b;
   A* ptr = &a;
   A** ptr_ptr = &ptr;
   intptr_t* iptr_ptr = reinterpret_cast<intptr_t*>(ptr_ptr);

   *ptr_ptr = &b;
   intptr_t iptr = *iptr_ptr;
   A* ptr2 = reinterpret_cast<A*>(iptr);

   printf("iptr = %lx, &a = %lx, &b = %lx, iptr->_x = %d\n", iptr,
          reinterpret_cast<intptr_t>(&a), 
reinterpret_cast<intptr_t>(&b), ptr2->_x);

   return 0;
}

The program stores an A*, reads it as intptr_t and casts it to A*, and 
then dereferences into the A. Even with -Wstrict-aliasing=3 GCC does not 
complain about this. Is GCC wrong about not complaining about this?

The way I interpret the standard, intptr_t is the signed type 
corresponding to the dynamic type of A*, which seems compliant to me. Of 
course the way it is stated in the standard is a bit vague (as usual), 
but the compilers seem to support my interpretation. Is my 
interpretation and GCC -Wstrict-aliasing=3 wrong here in allowing this?

>> This is invariant of whether the value of the store is indirectly
>> performed through intptr_t or loaded indirectly through intptr_t, as
>> the dynamic type of the stored value is still A*.
> Exactly so.  The stored object is an A*.  We're accessing it as an
> intptr_t.  A* and intptr_t are not compatible types.  This is true
> even if the intptr_t was the result of casting from an A*.  The
> compiler doesn't necessarily know that the intptr_t was originally an
> A*: they might even be in separate compilation units.  The compiler is
> entitled to assume that the object in memory of type A* does not alias
> with any value of type intptr_t.
>
>> If you now perform a store of A* through Atomic that casts the pointer
>> to intptr_t, and stores it, then the dynamic type is still A*.
> This isn't how it works.  (BTW, in case it helps understand where I'm
> coming from, although I am not the greatest expert in this area, I am
> familiar with GCC's alias analysis because I am a GCC author.)
>
>> In summary, my point is that as long as the performed stores and loads
>> in Atomic preserves the bit representation of whatever pointer was
>> passed in, the dynamic type of that pointer is unchanged, invariantly of
>> casting it to/from intptr_t, and hence the aliasing is allowed.
> That is incorrect.  Rules about aliasing are nothing to do with the
> bit representation.

What I meant is that if the bit representation changed, it would point 
to something different, hence the dynamic type of whatever is pointed at 
would not (necessarily) be the same.

> Let me reiterate: you may cast from any pointer type to intptr_t.  You
> may not access a pointer in memory as though it were an intptr_t.
>

Are you 100% sure about this? Again, I do not know where in the standard 
this is stated, but -Wstrict-aliasing=3 allows me to do that as long as 
the intptr_t is appropriately casted to the A* before the A behind the 
dynamic A* type is used. Perhaps I have missed something?

Thanks,
/Erik


More information about the hotspot-runtime-dev mailing list