We are evaluation Keil uVision V4.10 and ARMCC V4.1.0.481.
Our first test run didn't go so well.
With no optimisation, everything worked OK. Using optimisation 1 or above, the test failed.
For various reasons, I can not disclose the test source code. However, I will attempt to create a small self-contained example later on. The part of the test code that caused the problem is setting up an multi dimensional array, setting each element to '1' (ie. Array[x][y] = 1 with a for loop for both x and y).
In the meantime, I wonder if anyone has had a similar problem?
Using optimisation -O3, the assembler code in the debugger looks like this:
48: for ( y= 0 ; y < 8 ; y++ ) 0x80000AAC E5C02000 STRB R2,[R0] 0x80000AB0 E2822001 ADD R2,R2,#0x00000001 0x80000AB4 E3A0C001 MOV R12,#0x00000001 0x80000AB8 E20220FF AND R2,R2,#0x000000FF 0x80000ABC E7C3C001 STRB R12,[R3,R1] 0x80000AC0 E5C02001 STRB R2,[R0,#0x0001] 0x80000AC4 EAFFFFFC B 0x80000ABC -- Isn't this an infinite loop???
Therefore, we have decided to purchase the compiler from the different vendor and end the evaluation of Keil.
Based on your description of the problem, it's very likely that you are wrong to blame the compiler. The compiler is free to perform any transformations on the code as long as those transformations preserve the so-called side effects of the program. Hopefully, those transformations will lead to smaller and/or faster code. That's what optimization is about. Please look up side effects in literature on the C programming language, preferably the language standard. By the way, a change in the contents of an array in a debugger window is not a side effect. During the course of those transformations, the generated machine instructions may deviate from the source code very significantly. In fact, this is the case for many modern compilers. This will make debugging more difficult at high optimization levels, but the side effects will be preserved (unless you encounter a compiler bug, which is unlikely.) You might want to change your test code. One way to make a program create side effects is to use the printf() function.
We understand that the compiler attempts to create the fastest code possible whilst keeping the intended nature of the code.
However, we can not understand that simple code such as:
for (x = 0; x < 100; x ++) for (y = 0; y < 8; y ++) array[x][y] = 1;
should ever produce code such as this:
80000ABC: STRB R12,[R3,R1] (R12 contains the value '1'. R3 is 'array' and R1 is '1') STRB R2,[R0,#0x0001] B 0x80000ABC
and therefore creating an infinite loop. In case of some clever optimisation we also ran this code without trying to debug through it (ie, the code was left to run at full speed). After the timeout of 2 minutes, we stopped the execution of the code and noticed it was looping in the same place above. Checking the debug window, we also noticed that only the first two bytes of the array were populated with '1' - the rest contained '0'. There are no interrupts in this code, so volatile/etc should not be required.
The strange part is that some of the much more complex test cases run through fine, therefore it must be the whole context of the function (and perhaps the rest of the file) which causes the bug to occur. We have no doubt that other people have projects with thousands of lines which compile fine.
This is the only compiler that has failed this test. No doubt if we submit the test file to ARM then it could be fixed (perhaps even with different command line parameters), but we don't have the time for that when other solutions exist which do not fail this test (although granted, we may find bugs in that compiler too in some future project).
"There are no interrupts in this code, so volatile/etc should not be required"
That is a non sequitur; ie, the stated conclusion (volatile/etc should not be required) does not follow from the premise given (There are no interrupts in this code).
There are many places where volatile may be required other than with interrupts. The classic example is the software delay loop:
int i; for( i=0; i<10; i++ ) /* do nothing */ ;
Since the code has no side effects, the compiler is at liberty to optimise it away. This can be prevented by use of volatile:
volatile int i; for( i=0; i<10; i++ ) /* do nothing */ ;
No interrupts are involved here, either!
Again, this does suggest that you are being hasty in rejecting this compiler.
See also: blog.regehr.org/.../28
"it must be the whole context of the function (and perhaps the rest of the file) which causes the bug (sic?) to occur."
Yes, that is very likely indeed!
Of course, without seeing that context, there is nothing more to discuss here!
"This is the only compiler that has failed this test"
The problem is that it may well be the test that is flawed!
In addition, maybe the test failed because the ARM compiler is actually the best compiler of them all! Again - it smells like you guys committed yourselves to a certain software package based on incomplete and even flawed information. Having something like 20,000 Euro worth of Keil tool chain software here I know how hard it will be for you to change you minds... Do note that volatile is also generally required when working with memory mapped IO, even if it is not interrupt based!
One thing with volatile and concurrency is that for example SFR represents concurrency even if no interrupts are involved.
So volatile may be required to force reads, to be able to know that the SFR contents have changed.
This is beside the fact that a read of some SFR may result in acknowledges of a hardware state machine.
And writes to memory are always subject to cancellation by code optimizers if the compiler can't see anyone else use the memory contents.
You seem to be missing the point.
Regardless of whether or not the variables used are volatile, and whether or not the compiler chooses to complete ignore the filling of the array, the compiler should change a known loop with exit condition to a loop without an exit condition.
For example, if the code was "for (x = 0; x < 64; x ++) /* do nothing */;" then we wouldn't care if the code was optimised out. That would be ok.
The problem is that the compiler changed code like "for (x = 0; x < 64; x ++)" to "while (1)". These are most definitely NOT the same, regardless of any optimisation.
Therefore, we concluded that this was an optimisation fault.
The correlation between individual pieces of C code and the generated assembly becomes vague at best the higher optimization level selected. The infinite loop must have been associated with something else - that is my opinion.
"Regardless of whether or not the variables used are volatile, and whether or not the compiler chooses to complete ignore the filling of the array, the compiler should [not] change a known loop with exit condition to a loop without an exit condition."
That depends whether that loop in the particular context was equivalent to a loop without an exit condition.
eg,
void main( void ) { // Test filling an array for (x = 0; x < 100; x ++) for (y = 0; y < 8; y ++) array[x][y] = 1; // "Test finished - we're done" for( ;; ); // Embedded code - must not exit main! }
Again, without seeing the context, further discussion is impossible.
> although granted, we may find bugs in that compiler too in some future project
s/may/will/
Compiler bugs are nasty and time-consuming since they can only possibly be found by analyzing the disassembly, and only after coming to the conclusion that the source code was right.
Had I discarded every compiler with a defect, then I'd find myself writing hexfiles manually. Personally, I believe that the RealView compiler is a great product.
-- Marcus
"Personally, I believe that the RealView compiler is a great product."
I second that.
A fantastic machine indeed!
I'm not saying this is wrong but ... I don't quite like the space between 'y' and '++'...
Is this common practice to write like this?
There is no way I would release any code with a space between increment/decrement operator and the variable. I'm too much of a style police - whenever I see any space at the "wrong" place, my eyes hurt. But I limit myself to perfecting "my" code.