Reputation: 3940
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.
Upvotes: 0
Views: 595
Reputation: 80325
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”.
Upvotes: 2
Reputation: 141533
Is it promised that volatile unsigned int x = *(unsigned int *)(ILLEGAL_ADDRESS); will take place before if (global_flag == 1)
From informative C11 AnnexC (added newlines/formatting for readability):
The following are the sequence points described in 5.1.2.3:
...
- Between the evaluation of a full expression and the next full expression to be evaluated.
- The following are full expressions:
- an initializer that is not part of a compound literal (6.7.9);
- the expression in an expression statement (6.8.3);
- the controlling expression of a selection statement (if or switch) (6.8.4);
- the controlling expression of a while or do statement (6.8.5);
- each of the (optional) expressions of a for statement (6.8.5.3);
- the (optional) expression in a return statement (6.8.6.4).
As the *(unsigned int *)(ILLEGAL_ADDRESS);
is an initializer (assignment expression) and the initializer is not part of a compound literal, it is a full expression. The next full expression is the controlling statement in if
, so between if
and the initialization of x
there is a sequence point.
And from the famous C11 5.1.2.3p6:
The least requirements on a conforming implementation are:
Accesses to volatile objects are evaluated strictly according to the rules of the abstract machine.
...
As x
is a volatile object, it is initialized strictly to the abstract machine, so after the sequence point it has to have the rvalue equal to the result of *(unsigned int *)(ILLEGAL_ADDRESS)
operation.
So yes, the initialization of x
object must happen before the control expression inside the if
.
On undefined behavior, there's the good quote from C11 6.5.3.2p4:
If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined.
As you commented:
accessing address 0x60000000 is not permitted in my system memory model
one can deduce that (unsigned int*)0x60000000
is an invalid pointer, so the unary * operator should spawn dragons.
Upvotes: 2