const volatile behaviour change in GCC 7

David Brown david@westcontrol.com
Thu Sep 22 18:41:00 GMT 2016


On 22/09/16 17:30, Richard Biener wrote:
> On September 22, 2016 5:20:56 PM GMT+02:00, Paul.Koning@dell.com
> wrote:
>>
>>> On Sep 22, 2016, at 11:16 AM, David Brown
>>> <david@westcontrol.com>
>> wrote:
>>>
>>> On 22/09/16 16:57, Paul.Koning@dell.com wrote:
>>>>
>>>>> On Sep 22, 2016, at 6:17 AM, David Brown
>>>>> <david@westcontrol.com>
>> wrote:
>>>>>
>>>>> ... Your trouble is that your two pointers, cur and end, are
>>>>> pointing at different variables. Comparing two pointers that
>>>>> are independent (i.e., not pointing to parts of the same
>>>>> aggregate object) is undefined - the compiler can assume that
>>>>> these two external objects could beanywhere in
>>>>> memory, so there is no way (in pure C) for you to know or
>>>>> care how they are related. Therefore it can assume that you
>>>>> will never reach "cur == end".
>>>>
>>>> Would making them intptr_t instead of pointers fix that?
>>>>
>>>
>>> With care, yes.  But I think it still relies on gcc not being
>>> quite
>> as
>>> smart as it could be.  This seems to generate working code, but
>>> the compier could in theory still apply the same analysis:
>>>
>>> void rtems_initialize_executive(void) { uintptr_t cur =
>>> (uintptr_t) _Linker_set__Sysinit_begin; uintptr_t end =
>>> (uintptr_t) _Linker_set__Sysinit_end;
>>
>> I would not expect the compiler to apply pointer rules for code
>> like this.  (u)intptr_t is an integer type; it happens to be one
>> whose width is chosen to match the width of pointers on the
>> platform in question, but that doesn't change the fact the type is
>> integer.  For example, it is perfectly valid for an intptr_t
>> variable to contain values that could not possibly be pointers on
>> a given platform.

The compiler /can/ perform such analysis and see the undefined behaviour 
of the code, and use that to optimise away the check for the loop.  It 
is legal to convert (by cast or initialisation) an intptr_t or uintptr_t 
variable to a pointer - but it is /not/ legal to dereference it unless 
the intptr_t or uintptr_t was set from a legal pointer.  Thus when the 
compiler is considering the validity of access through the casted 
uintptr_t variable, it can look back to where the contents of that 
variable came and consider those pointers.  Since the compiler can 
assume that incrementing one pointer repeatedly never matches the other 
pointer (as they point to unrelated objects), the same applies even if 
the values have been cast back and forth through intptr_t variables.

In general, I find that if you can't figure out a simple way to express 
your desires for this sort of thing using fully defined standard C 
behaviour, then complex methods with castings and the like will not be 
entirely correct either.  They might work at the moment - gcc is a smart 
compiler, but it has its limits - but they might not work in the future 
(as the compiler gets better all the time).  Compiler-specific 
workarounds like the assembly macro proposed are often the safest 
methods.  Alternatively, you accept a slight performance hit by using an 
extra "volatile" step to limit the optimisation.  Or you change the 
requirements, by finding a different way to handle it - such as by 
letting the linker figure out the size of the table and including it as 
a readable constant.

>
> I can't see how it could either.  BTW your testcase contains another
> fragility, the order of two global vars.
>

That is, in fact, the crux of the problem - the two linker symbols are 
unrelated, so by the rules of C pointers they are incomparable as are 
all pointers derived from them.



More information about the Gcc mailing list