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