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???
For various reasons, I can not disclose the test source code. However, I will attempt to create a small self-contained example later on.
Nothing to discuss so far, then.
Isn't this an infinite loop???
It looks like it. Is this supposed to be a problem? Since there is no complete code posted, there is nothing to discuss here either.
The assembler looks like it enters an infinite loop, but without seeing some surrounding code I think it is difficult to establish what is really happening.
If you are doing an evaluation of the compiler, I would suggest that you do so with the very latest (ARMCC V4.1.0.567).
I explained what the assembler was supposed to be above. It's basically this:
for (x = 0; x < 100; x ++) for (y = 0; y < 8; y ++) array[x][y] = 1;
At optimisation level 0, the output code is correct and works correctly, giving the correct array output. At optimisation level 1, the output code is incorrect. 'x' never increases past the value of '1'. At optimisation level 3, the output code is incorrect. 'y' never increases past the value of '1'.
We have discussed this in a meeting this afternoon and have decided that we simply can not depend on a compiler which can make the kind of mistake as explained in the assembler code in the first message - the compiler seems to have optimised away the increment of the variable 'y' and 'x'.
We had no problem with the exact same code with a compiler from a different vendor under the same optimisation levels.
Therefore, we have decided to purchase the compiler from the different vendor and end the evaluation of Keil.
Shame really, as the debugger is much better in the Keil product. The other compiler support zero peripheral emulation. (The ideal would be to use the other compiler and use Keil as the debugger, but we don't have the budget to do that).
Something tell me you made a rash, premature decision!
It is rather difficult to comment as no real code is provided, but it is very likely that the root cause is faulty code, a missing volatile type qualifier etc. I'm am already working on a second product consisting of about 150000 - 200000 lines of code, with components (namely bootloaders and some application code) heavily optimized. I don't believe the compiler is at fault here. Did you ask Keil support before making such a commitment?
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:
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.