Hello experts, I'm a beginner of embedded system development.
I have question.about difference of two code.
at first, suppose there is function like
int SomeFunction(void) {
......
return (some integer);
}
And using this function return value like these
retval = SomeFunction();
if (retval == {some Variable}) {
. . .
....
former one doesn't make any problem, but when i used like latter one.. some times, retval is not correct and some strange value remains on r0 register...;
I don't know what is problem on latter one exactly...;
Could explain what is the problem of these?
Thank you very much for reading this..... though it is written by pool english...
Hello levi,
if the retval of the volatile function was incorrect, it would might be a bug of the compiler.The difference betweenretval = SomeFunction();andretval = (volatile int)SomeFunction();is whether the function must be executed for every call.In the case of several function calls of SomeFunction, if there would not be the volatile cast, the call might be executed once and the return value might be re-use for the other return values of the same functions.If there is the volatile cast, the function must be executed for every its function call.Therefore, if the return value is defined in one way according to the input arguments, the volatile cast has no means.The volatile cast would be used when the return value would vary at every call even if the input arguments are the same.
For example, please refer to the following source code and assembled result.
o Source Code
int main() { int retval; retval = SomeFunction(); if (retval == 0x11223344) { foo(); } if (retval == 0x11223344) { bar(); } if ( 0x11223344 == (volatile int) SomeFunction()) { foo(); } if ( 0x11223344 == (volatile int) SomeFunction()) { bar(); } return 0; }
o Assembled Result
main: stmfd sp!, {fp, lr} add fp, sp, #4 sub sp, sp, #8 bl SomeFunction str r0, [fp, #-8] ldr r2, [fp, #-8] movw r3, #13124 movt r3, 4386 cmp r2, r3 bne .L4 bl foo .L4: ldr r2, [fp, #-8] @ return value is re-used movw r3, #13124 movt r3, 4386 cmp r2, r3 bne .L5 bl bar .L5: bl SomeFunction mov r2, r0 movw r3, #13124 movt r3, 4386 cmp r2, r3 bne .L6 bl foo .L6: bl SomeFunction mov r2, r0 movw r3, #13124 movt r3, 4386 cmp r2, r3 bne .L7 bl bar .L7: mov r3, #0 mov r0, r3 sub sp, fp, #4 ldmfd sp!, {fp, pc}
Best regards,Yasuhiko Koumoto.
Thanks a lot yasuhikokoumoto.
Your answer is so helpful.
But i stiil have a question.
Strangely, when I used the return value by saving volatile variable, there is no problem.
In other word.. if I used like..
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
...
return {Some integer but changeable like Tick..}
int main (void)
(volatile int) retval;
int comparedVariable = {Some value} ;
retval = SomeFunction(void);
// There is no Bugs.
if (retval == compareVriable) {
foo();
} else {
exit(1);
Colored by Color Scripter
above way doesn't make any problem....
but if I compare using typecasting like..
// Some times retrun value saved in r0 is wrong.
if (compareVriable == (volatile int) SomeFunction()) {
makes the problem.
when i compared the assembly code..... also, there is no big difference.. Just r0 value is wrong, when comparing.
Could I think it is compiler bug as you said?
Thx a lot.
Hello,
I think the line 16 should be omitted in the 2nd example.
On this condition, I tried the both cases and got the almost identical results.
That is, there was no incorrect code.
I think the reason would be that you did not declare the SomeFunction as the volatile int type.
Because the SomeFunction returns different value for each function call, you should it as volatile type.
For example,
volatile int SomeFunction(void) { return {Some integer but changeable like Tick..}; }
Anyway, please show us the assembly code for each example.
Best regards,
Yasuhiko Koumoto.
As you said, (because of my situation.. i can't open the assem code which makes problem.. really sorry.)
There is no big difference of two case assembly code.. but problem happen on RUNTIME. (+ second one line 16 is just my mistake.. originally line 16 must be deleted.)
In second case, I just confirm SomeFunction's return value is correct before it calls pop instruct, but, it happen when return caller (in case of above code, it is main).
so that makes me so confused, though there is no difference of assembly code but r0 value is changed.....
Thank you for ur answer.
As far as I am aware, casting the result of, or declaring a function as volatile is meaningless within the C standard; however, I believe early versions of GCC used this as an alternative to __attribute__((noreturn)), i.e. an indication that the function in question should never return, and thus the compiler could make optimisations based on this assumption.
If your compiler is implementing this functionality, then this could be the cause of the behaviour you are seeing, i.e. the compiler simply isn't expecting the function to return, and thus it isn't generating the code required to either robustly get the return value back to the caller, or to preserve any of the caller's variables while calling the "volatile" function.
Simon.
As a general observation, the compiler can not eliminate a function call without ensuring that it implements all of the side-effects that calling that function at that specific time may have produced.
As a simple example, the following code compiled without access to the body of func_one() will produce code that calls func_one() twice:
extern int func_one(void); void example1(void) { func_one(); func_one(); }
This is the case even if func_one() has no side-effects simply because the compiler has no way of knowing this.
If, however, the body of func_one() is available, then its side-effects can be inlined and the calls subsequently optimised away, e.g.:
int func_one(void) { return 0; } void example2(void) { func_one(); func_one(); }
can compile such that example() makes no calls whatsoever, and just returns, i.e. the compiler can legitimately interpret this as:
void example2(void) { }
Conversely, if we replace func_one() with func_two() which does have side-effects, then even if it is inlined the effects must remain, e.g.:
int func_two(void) { return *(volatile int*)0xE000E010u; } void example3(void) { func_two(); func_two(); }
Could be implemented by the compiler as:
void example3(void) { *(volatile int*)0xE000E010u; *(volatile int*)0xE000E010u; }
which retains all of the side-effects, but could not be optimised to a single volatile access, or an empty function.
As such, there appears to be no requirement to need to be able to mark a function as volatile.
Beyond what is specified in the C standard, some tool chains do provide the opposite feature, i.e. the ability to tell the compiler that the function either has no side-effects and/or that its return value is dependent only on the arguments passed to the function and not on global variables / volatile memory accesses. Significant care must be taken, as these can potentially produce very strange results if used incorrectly, e.g:
extern int func_two(void) __attribute__((pure)); int example4(void) { return func_two() + func_two(); }
Might be expected to perform two separate reads of 0xE000E010, add them together and return the result, however, due to erroneously marking our previous func_two() as "pure", the compiler is free to implement this as:
int example4(void) { return func_two() * 2; }
i.e., reuse the value from the first call/read, and simply multiply it be two.
I just... admire your knowledge simon...
Relly Rellay Thanks!!!!!
I don't know what's happening about the bug you're getting, but I would like to point out that a (volatile int) cast does nothing more that just (int) does on its own. volatile applies to accessing a location, not a value. If you declared a function as pure using a pragma the compiler would be entitled to only call it once for the same parameters even if you put (volatile int) before each call.
The only really useful use of volatile in a cast is in things like
*(volatile int *)intptr
The whole bunch of linux code is written alluding to "volatile" where it just shouldn't rely on this behaviour.
Many problems, yes _many_, arise this way.
Perhaps, I'd recommend +never+ using optimisation options when you feel that the control flow may in some or other way be affected.
Normally, GCC outputs ingenious code, but when you force, the compiler is also forced to think otherwise making you code unpredictable.
And such errors are really hard to find sometimes.
So, any code relying on "volatile" or other compiler "hints" is intrinsically +buggy+. Do try avoiding them altogether.