Skip to main content

IAR Embedded Workbench for Arm 9.70.x

Memory barriers in the IAR Compiler

In this section:

The compiler's speed optimizations can result in a reshuffle of the order in which instructions are executed. This is not a problem for single-threaded applications, but for multi-threaded applications re-ordered memory accesses can create problems for the application semantics, leading to unpredictable behavior. To prevent this, you can use compiler memory barriers (also called memory fences). A compiler memory barrier makes sure that the compiler does not reorder memory access instructions. Typically, instructions meant to be executed before the barrier will be executed before instructions meant to be executed after the barrier—the optimizer cannot re-order memory accesses across the barrier. The most common sequence point is a function call.

Note

The memory barrier constrains the compiler, but not the CPU at runtime—only CPU memory barriers in hardware do that.

Even if the compiler outputs instructions in correct order, some hardware features might need further sequencing. Processor optimizations can result in memory operations occurring in an order different from that specified in the executing code. To force the core to wait for memory accesses to complete, many Arm architectures specify the CPU memory barrier instructions DMB, DSB, and ISB. Use the intrinsic functions __DMB, __DSB, and __ISB for direct access to these from your code. The compiler treats these CPU memory barriers also as compiler barriers and will not reorder instructions across them when optimizing.

The intrinsic functions __disable_interrupt and __enable_interrupt are also treated as memory barriers by the IAR Compiler.

Implementing compiler memory barriers using inline assembler

The asm volatile ("" : : : "memory") statement works similar to the corresponding statement in the GCC compiler: it keeps an object in the location where it is placed and most memory accesses cannot move across it.

Note that both GCC and the IAR Compiler allow memory accesses to move across an object if the compiler can know that asm volatile ("" : : : "memory") cannot modify its value. In particular this is the case for variables with automatic storage duration, where the address has not leaked outsize the function. This implies that the loads and stores *val1 in the example below are free to move across the barrier—to prevent this, you must move the declaration of val1 out of the function like this:

int *val1;

int test_mem_barrier( int a);
int test_mem_barrier( int a)
{
  int b = a;
  val1 = &b;
  volatile int val2;

  *val1 = (*val1 + 20);
  asm volatile (";;; BARRIER" : : : "memory");
  val2 = 20;

  return (val2 + *val1 + a);
}

int main()
{
  int i = 25;
  int y = test_mem_barrier(i);
  return 0;
}