Are arrays guaranteed to be affected by a "memory" clobber?

David Brown david.brown@hesbynett.no
Sun Jun 14 12:06:00 GMT 2015


On 13/06/15 21:51, Andrew Haley wrote:
> On 13/06/15 18:49, Sebastian wrote:
>> On Fri, 12 Jun 2015 19:53:03 +0100
>> Andrew Haley <aph@redhat.com> wrote:
>>
>>>> That's the whole point about the example.
>>>
>>> No it's not.  The only thing moved across the barrier is the
>>> division.  A memory op cannot  be moved across a memory barrier.
>
>> One memory barrier is at the cli(), as the article points out "where
>> the potentially slow division is moved across cli(),". The
>> assignment to val is moved, over that.
>
> Ah, okay.  I wasn't even thinking of that assignment as a memory
> operation: see below.
>
>> It was before cli() in the C
>> source and it's after cli() after optimization. As your own comment
>> shows.
>>
>>> Here's the code:
>>>
>>> 00000112 <test2>:
>>>   112:   bc 01           movw    r22, r24
>>>   114:   f8 94           cli
>> That's the cli().
>>
>>>   116:   8f ef           ldi     r24, 0xFF       ; 255
>>>   118:   9f ef           ldi     r25, 0xFF       ; 255
>>>   11a:   0e 94 96 00     call    0x12c   ; 0x12c <__udivmodhi4>
>>>   11e:   70 93 01 02     sts     0x0201, r23
>>>   122:   60 93 00 02     sts     0x0200, r22
>>>
>>> The store to val is here ^
>
>> Thanks. Didn't try to find that, just figured that it would be
>> difficult to assign the result of the division before the division
>> happened.
>
>> So you just confirmed everything. This is after cli().
>
>> Actually, I think you're wrong here - this is not the store to val
>> (I guess you could r24 consider to be val, since that's it at the
>> beginning of the function), it's the store to ivar.
>
> Sorry, yes.  I made a mistake.  I meant the store to ivar.
>
>> The store to val never happens, it is optimized away because val is
>> not a volatile variable, it's just a temporary one which is never
>> used afterwards.
>
> Yes.  val has been eliminated by the compiler.
>
>> Which, still, is the point of the article: Because val is not
>> declared volatile, it is not "memory", so assignments to it (and the
>> division required to happen before the assignment) can be moved
>> across the barrier.
>
> The question of whether a local variable is considered by GCC to be
> "in memory" depends on whether it has ever had its address taken.
>
> So, if you say
>
>     int n;
>     int *p = &n;
>
> then n is potentially a memory operand.  (But be careful not to extend
> this too far: if GCC knows that p is not used, it may be eliminated
> and n is no longer potentially in memory.)  If a variable is global it
> is also a memory operand.  But if a local variable never has its
> address taken, no memory operation can access it (because there is no
> way to know where it is.)  Therefore it is not affected by a memory
> clobber.
>
>> So, again, why would it be ok to remove the volatile qualifier from
>> my array elements?  How else can I be sure a variable of mine is
>> "memory"?
>
> The only way to be really sure is to use volatile.  But it depends
> on exactly what you're trying to do: if you're more specific we can
> provide better advice.
>
> Andrew.
>

There is another way to force the calculation here - using a fake 
assembly input to force the calculation:

#define cli() __asm volatile( "cli" ::: "memory" )
#define sei() __asm volatile( "sei" ::: "memory" )
unsigned int ivar;
void test2( unsigned int val )
{
     val = 65535U / val;
     asm volatile("" :: "" (val));
     cli();
     ivar = val;
     sei();
}

The memory clobber on cli() and sei() ensures that no memory operations 
are moved before or after those statements.  But as already noted, the 
memory clobber does not affect non-memory operations such as 
calculations or register-only manipulation.

But the extra "asm volatile" here with a fake input tells the compiler 
that "val" is an input to the (empty) assembly, and must therefore be 
calculated before the statement is executed.  The empty input constraint 
(no "r" or "m") gives the compiler complete freedom about where it wants 
to put this fake input - all we are saying is that the value "val" must 
be calculated before executing

	asm volatile("" :: "" (val))

Generating assembly from this (using gcc-4.5.1, which is the latest 
avr-gcc I have installed at the moment) shows the division being done 
before the cli() - the code is optimal and correct, with no unnecessary 
memory operations (as you would need by making "val" volatile).







More information about the Gcc-help mailing list