Search code examples
clanguage-lawyervolatile

Volatile and sequence point


Given the following code:

unsigned int global_flag = 0;

void exception_handle()
{
    global_flag = 1;
}

void func()
{
    /* access will cause exception which will assign global_flag = 1
       then execution continues */
    volatile unsigned int x = *(unsigned int *)(0x60000000U); /* memory protection unit configured to raise exception upon accessing this address */

    if (global_flag == 1)
    {
        /* some code */
    }
}

Given the fact that volatile must not be reordered across sequence points:

The minimum requirement is that at a sequence point all previous accesses to volatile objects have stabilized and no subsequent accesses have occurred

And given the following about sequence points:

sequence points occur in the following places ... (1) .. (2) .. (3) At the end of a full expression. This category includes expression statements (such as the assignment a=b;), return statements, the controlling expressions of if, switch, while, or do-while statements, and all three expressions in a for statement.

Is it promised that volatile unsigned int x = *(unsigned int *)(0x60000000U); will take place before if (global_flag == 1) (in the binary asm, the CPU out-of-order execution is not relevant here) ?

According to the citations above, volatile unsigned int x = *(unsigned int *)(0x60000000U); must be evaluated before the end of next sequence point, and volatile unsigned int x = *(unsigned int *)(0x60000000U); is a sequence point by itself, so is that means that every volatile assignment is evaluated at the assignment time?

If the answer to above question is no, than next sequence point is at the end of the if, does it mean that something like that can be executed:

if (global_flag == 1)
{
    volatile unsigned int x = *(unsigned int *)(0x60000000U);
    /* some code */
}

System is an embedded one- ARM cortex m0, single core, single thread application.


Solution

  • In your snippet the variable global_flag is not volatile, so nothing prevents the compiler from moving the access to global_flag across sequence points or to remove it entirely if circumstances allow it. It does not make sense to talk about the order of the access to x and the access to global_flag because the latter is not an observable event, only the former is.

    (Also note that there is no volatile qualifier in the expression *(unsigned int *)(0x60000000U). I think it is really that expression that you wish to treat specially, but your code does not do that. The compiler is allowed to produce code that evaluates *(unsigned int *)(0x60000000U) well in advance, then does a ton of other stuff it has on its plate, then assigns the value that was obtained to x and this would satisfy the constraints that the C standards place on volatile lvalues.)

    If your snippet had unsigned int volatile global_flag = 0; and *(volatile unsigned int *)(0x60000000U) then the answer to the question “Is it promised that …” would be an unambiguous “yes”.