Is read to local volatile variable always guaranteed?
I have a function that performs some operation on struct members. Struct members are not volatile, and may change (embedded application, interrupts) while function operates. It is no a problem if values change during read (copy-to-local variable), but they shall not change while doing multiple if
statements.
#include <string.h>
#include <stdio.h>
typedef struct {
int wptr;
int rptr;
} rw_t;
void
use_it(const rw_t* rw) {
volatile int r, w;
/* It is OK if rw->rptr or rw->wptr get changed during these copy operation */
r = rw->rptr;
w = rw->wptr;
/* I must ensure that r and w are actually local references,
therefore a copy to local shall be done first */
if (r > w) {
printf("R is more than W\r\n");
} else if (r < w) {
printf("W is more than R\r\n");
} else {
printf("R and W are equal\r\n");
}
}
Compiling with ARM-GCC none-eabi 10.2.1 with -Os
seems to work properly. But is this guaranteed to always work with any compiler and any optimization?
Minimal reproduceable example: https://godbolt.org/z/nvocrsrzE
CodePudding user response:
If you aim to pass a volatile
qualified pointer to the function, then the function's parameter must be volatile rw_t* rw
or volatile const rw_t* rw
. Not using volatile
would be problematic because then the compiler may assume that the pointed-at data will never change and therefore optimise out the local variables, since it might as well read directly from the original location in that case.
The local variables shouldn't need to be volatile
however, since there is no reason why the compiler needs to re-read them after the first assignment. You don't want them to be updated so why volatile
? A local variable which is not volatile
will not get suddenly updated in the middle of an if
- else
chain and that's fully portable behavior. Whereas a volatile
variable might change at any time.
Please note however that volatile
does not guarantee atomic access. The values might be updated between r = rw->rptr;
and w = rw->wptr;
so that r
ends up containing an old value but w
a new one.
CodePudding user response:
It does not matter if an object has automatic, static or dynamic storage duration. volatile
shows the compiler that object is side effects prone and it will be read from its storage location every time it is used and stored every time it is modified.
int foo(volatile int x)
{
return x x x x;
}
foo:
sub sp, sp, #8
str r0, [sp, #4]
ldr r3, [sp, #4]
ldr r1, [sp, #4]
ldr r2, [sp, #4]
add r3, r3, r1
ldr r0, [sp, #4]
add r3, r3, r2
add r0, r3, r0
add sp, sp, #8
bx lr
But in your case you need something completely different
void
use_it(volatile const rw_t* rw) {
int r, w;
r = rw->rptr; //guaranteed to read from memory
w = rw->wptr;
//further usage guaranteed to be using local r & w
use_it:
ldr r2, [r0, #4] //reads from memory
ldr r3, [r0] //reads from memory
cmp r2, r3 //from now uses local variables (stored in registers is optimizations are on
push {r4, lr}
bgt .L9
ldrlt r0, .L10
ldrge r0, .L10 4
bl puts
pop {r4, lr}
bx lr
use_it: (cortex-M4 thumb version)
ldr r2, [r0, #4]
ldr r3, [r0]
cmp r2, r3
bgt .L7
bge .L6
ldr r0, .L8
b puts
.L6:
ldr r0, .L8 4
b puts
.L7:
ldr r0, .L8 8
b puts