I have a code snippet as given below
static unsigned int ticks; void IRQ_SomeTimer(void) { ticks ++; } int main() { int sav_ticks; sav_ticks = ticks; while (sav_ticks + TIME_TO_WAIT > *(volatile unsigned int *)&ticks); /* Do somethine here */ return 0; }
This compiles and runs on ARM-Mx targets up to optimization level O2, at optimization level O3 the code generated for
while (sav_ticks + TIME_TO_WAIT > *(volatile unsigned int *)&ticks);
is the same as the code generated for
while (sav_ticks + TIME_TO_WAIT > ticks);
Hence the code generated by O3 never comes out of the while loop!! Is this a BUG in Keil armcc(?) I am using Keil U-Vision (MDK ARM 4.73).
The program I posted is just a simple example, but on a complex projects there will be many places where the identifier of interest will be referenced. By cast I could selectively make the identifier to behave as volatile and hence obtaining more optimized code than the one where it is just declared volatile, and you could also make access to extern storage class identifier as volatile without modifying the declaration itself, and there are many reasons why people want their code be in a certain way.
Just to get give an idea of how other tools handle this, I have generated the assembler listing for the same source file with various compilers
Here is the IAR generated code (with highest possible optimization supported by the tool)
// 14 while (sticks + TICKS_TO_WAIT > *(volatile uint32_t *)&ticks); ??main_0: LDR R2,[R0, #+0] CMP R2,R1 BCC.N ??main_0
As you could see branch to ??main_0 is reloading the object from memory.
Here is the GCC generated code (with -O3)
65 .L5: 12:main.c **** uint32_t sticks; 13:main.c **** sticks = ticks; 14:main.c **** while (sticks + TICKS_TO_WAIT > *(volatile uint32_t *)&ticks); 66 000c 1368 ldr r3, [r2] 67 000e 8B42 cmp r3, r1 68 0010 FCD3 bcc .L5
Again the branch to .L5 will properly reload the object from memory.
I would expect the same kind of code to be generated by Keil (armcc) too.
By cast I could selectively make the identifier to behave as volatile and hence obtaining more optimized code than the one where it is just declared volatile
Who the heck cast things to volatile pointers, and back-and-forth, project's got to be a nightmare to manage, for nominal to non-existent benefit.
Code it as ((volatile uint32_t)ticks)
Does the example I presented compile cleanly on all the compilers, and optimization settings? It's simpler, cleaner, and doesn't break when the count wraps.
Submit your bug report to your support contact at Keil
"selectively make the identifier to behave as volatile"
Why would you want it so selectively behave as volatile?
Surely, a variable is either volatile, or it isn't?!
"on a complex projects"
In a complex project, the last thing you want is schizophrenic variables which are sometimes volatile, and other times not!
"Preceding an expression by a parenthesized type name converts the value of the expression to the named type. This construction is called a cast [44]".
And the note [44] says,
"A cast does not yield an lvalue. Thus a cast to qualified type has the same effect as a cast to the unqualified version of the type"
volatile is a qualifier - so I think that says that the compiler is correct to ignore the volatile in your cast...?
Well that settles it (Thank you Andrew Neil).
By cast I could selectively make the identifier to behave as volatile
... and by doing so, you're lying to your tools. Let there be no doubt about one thing: that variable is volatile, period. So you're putting each and every piece of your code that you made believe otherwise at a considerable risk of breaking, for no good reason at all. Pardon the French, but that's just utterly foolish.
and hence obtaining more optimized code than the one where it is just declared volatile
Looks like you need to hear the applicable words of wisdom, loud and clear:
Premature optimization is the root of all evil. (Donald E. Knuth)
Well the funny thing is the code below shows error in Keil
uint8_t x; *(const uint8_t *)&x = 0x48;
It says the resulting expression from the Left hand side of the = operator is a non modifiable 'lvalue'. If cast to the qualified type has the same effect as cast to the unqualified type then the above should be perfectly valid!!
Oops I spoke too soon! After a little bit of analysis I think Footnote [44] of the standard (in draft foot note [86]) does not settle it after all!!
So the note says "A cast does not yield an lvalue. Thus, a cast to a qualified type has the same effect as a cast to the unqualified version of the type." Let us look at what this says
int x; long int y; y = (long) x; y = (volatile long) x;
In the above code the type is "long" and the cast converts it to "volatile qualified long", hence the standard says the compiler to consider the cast to "volatile long" as the same as cast to "long".
Well if we consider a type "int *" the volatile qualified type of "int *" is "int * volatile" and not "volatile int *" [this is totally a different type]. So according to the standards the compiler is free consider cast to "int *" and a cast to "int * volatile" as the same, but it is supposed to do the conversion according to standard given at 6.5.4 Cast operators paragraph 3
Conversions that involve pointers, other than where permitted by the constraints of 6.5.16.1, shall be specified by means of an explicit cast.
In my original example I do a conversion of "uint32_t *" to "volatile uint32_t *" which is not permitted by the constraint specified in 6.5.16.1 [it permits conversion of "volatile uint32_t *" to "uint32_t *" but not vice-versa]. Hence the compiler should consider my explicit cast. It is indeed a bug in armcc.
To shed more light on this as I mentioned before
the above code shown error [perfectly good] as the compiler did the conversion of type-x to type-y [Where type-x is "uint8_t *" and type-y is "const uint8_t *" and type-y is not a qualified type of type-x]
Now considering the code below
uint8_t x; *(uint8_t * const)&x = 0x48;
compiles with a simple warning as given below
main.c(15): warning: #191-D: type qualifier is meaningless on cast type
Which is apt according to the section of standards mentioned by "Andrew Neil"
P.S: Please don't ask me why I am using const and not volatile.