asm volatile statement reordering

David Brown david@westcontrol.com
Tue Oct 17 12:32:00 GMT 2017


On 17/10/17 14:06, Andrew Haley wrote:
> On 17/10/17 13:03, David Brown wrote:
>> It would be marvellous to be able to mark a statement, block, function,
>> function call, etc., as "volatile", meaning "do not re-order with
>> respect to other volatile statements, memory accesses or volatile asm
>> statements".
> 
> You can achieve that with memory clobbers.
> 

No, you can't - not always.  Memory clobbers enforce an ordering on data
that is stored in memory or loaded from memory.  They don't affect data
in registers, and they don't affect code execution and calculations.
And memory clobbers are expensive on many targets, as are alternatives
like making extra volatile variables.

A well-known example is here:
<http://www.nongnu.org/avr-libc/user-manual/optimization.html>

The key code is this:

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

On the AVR, division is an expensive operation handled by a library
call.  gcc sees it as a simple arithmetic operation, and does the
(perfectly valid) re-arrangement to:

  __asm volatile( "cli" ::: "memory" )
  ivar = 65535U / val;
  __asm volatile( "sei" ::: "memory" )

The memory clobbers are there, but they don't stop the code movement -
the slow division library call is done inside the critical region.

What does help here is the addition of an artificial dependency:

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

This gives the optimal and desired code (val is calculated before cli).


If there were a way to make a statement "volatile" and restrict its
movement, that could be used instead.  For example:

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

Casting the expression to void does nothing to it, but casting to
volatile void could be a reasonable way to tell the compiler that it
should do this statement /here/ (with respect to other volatiles), and
then ignore the result.  The syntax is legal in C at the moment (and has
no effect in gcc), which would be good for compatibility.

I suspect the idea would be difficult to implement, however, since it
would mean tracking multiple levels of "volatileness" - code within the
volatile statement could be reordered amongst itself, but not outside
the boundaries of the volatile statement, and there could be volatile
variable accesses within the volatile statement.  There could even be
sub-statements that are volatile themselves.  Yes, it would be
complicated :-(




More information about the Gcc-help mailing list