Ok its an old thread - but it does show some of the issues with optimisation.
Optimisation will try to get rid of code that 'does nothing' - that's its job! For example:-
int i;
for(i=0; i<256; i++){
}
This code doesn't actually do anything - it just wastes time. At the time of compiliing your program the optimiser realises this - since all it does is use a local variable called 'i', and it doesn't call any other functions. So the compiler realises that this doesn't actually do anything so it throws all the code away.
But if your intention is that you WANT to waste time, say for the delay loop on a servo pulse, then you can fool the optimiser by saying that your variable is 'volatile' (which means that its value can change under interrupts). eg
void _delay_cycles(volatile unsigned int cycles)
{
while(cycles > 0){
cycles--;
};
}
Since the 'cycles' parameter is 'volatile' then it means that this routine could be effected by interrupts (although it isn't in reality). So the optimiser leaves it alone.
As a low level programmer you MUST understand the 'volatile' keyword - otherwise you will never be able to benefit from optimisation. Why is this? Variables are stored in memory but are loaded into registers in the microcontroller for comparison or arithmetic operations. Code such as:
x = x + 1;
at runtime this will load the value of 'x' into register(s), add 1 to it, and then write the register back into the memory used to store the value of 'x'. Many compilers will only make the microcontroller do this once it has run out of registers. So lets consider a simple routine that waits for the next millisecond tick. Lets assume that an interrupt routine increments a variable called 'millis' and the rest of your code looks like this:-
unsigned millis = 0; // The variable that is incremented under interrupts
// Wait until the 'millis' has moved to the next value
for(unsigned now=millis; now==millis;){
}
With optimisation disabled then this is turned into the following pseudo code:-
load register with 0;
store register into variable millis;
load register from variable millis;
store register into variable now;
1)
load register from variable now;
register = register - variable millis;
if register equals zero then goto 1)
With optimisation enabled then we don't bother to write out the value to 'now' and then reload it immediately. We also may use more registers. So we may get:-
load registerA with 0;
// DONT 'store register into variable millis;' as we have it in registerA
// DON'T NEED TO 'load register from variable millis;' AS WE HAVE IT IN registerA
// DONT 'store register into variable now;' UNTIL WE NEED TO
1)
// DONT NEED TO 'load register from variable now;' AS WE HAVE IT IN registerA
// AT THIS POINT RegisterA has the value of 'millis' and of 'now'
// SO THE FOLLOWING LINE WILL ALWAYS RETURN 0 (ie 'now' = 'millis')
// register = register - variable millis;
// And is replaced by:
goto 1
So your loop will go round FOREVER !!!!.
This happens because the compiler doesn't know that the variable 'millis' is changed under interrupts so once it has been read into a register - it may not be read again. By making the variable 'volatile' then we force the compiler to re-read the variable wherever it is referenced and to write its value back immediately if it is changed.
This gives us the following pseudo-code that does what we expect:-
load registerA with 0;
store registerA into variable millis;
load registerA from variable millis;' - AS IT MAY HAVE BEEN CHANGED UNDER INTERRUPT
// DONT 'store register into variable now;' UNTIL WE NEED TO
1)
// DONT NEED TO 'load register from variable now;' AS WE HAVE IT IN registerA
// AT THIS POINT RegisterA has the value of 'now', but 'millis' may have changed under interrupt
registerB = registerA - variable millis;
if registerB equals zero then goto 1)
So our code now works again, and is slightly smaller than with no optimisation as we can hold the variable 'now' in registerA rather than reading/writing it from/to memory.
Is the optimiser being a pain? Well not really - its just doing its job. Say you wrote some (bad) code like this:-
int random(){
if( 1 > 2){
// This will never happen - but lets assume we have loads of code in here
}else if( 2 < 1 ){
// This will also never happen
}else{
return 1;
}
}
At the time of compilation the optimiser can realise that the first two 'ifs' will NEVER happen and so will just generate the code for 'return 1'.
But the optimiser can also optimise for execution speed. So if your code does something like scan a 100 by 100 thing such as:
for(int y = 0; y < 100; y++){
for(int x=0; x < 100; x++){
int pos = y * 100 + x;
readBit(pos);
}
}
Then the optimiser should realise that the int pos = y * 100 + x;
wastes a lot of time as it does y * 100
for each x
even though the y
hasn't changed. So, if optimising for speed then the optimiser should, at the very least, re-arrange your code as if it was:-
for(int y = 0; y < 100; y++){
int temp = y * 100;
for(int x=0; x < 100; x++){
int pos = temp + x;
readBit(pos);
}
}
A more aggressive optimiser may realise that the following does the same thing:-
for(int pos= 0; pos < 10000; pos++){
readBit(pos);
}
Optimisation is very helpful - but does have its unexpected issues. If you are using optimisaton and stuff is going wrong then turn the optimisation flags off, clean, and recompile. If you still have a problem then its your fault, otherwise its because the optimiser is being too helpfull and optimising stuff you dont want optimised. Having said that:- if you have a central method to pause for a given time then the rest of your code should only ever need to call this and can otherwise run at full speed - so optimisation should not be a problem.
Most compilers allow you to change the optimisation for certain bits of code, if you want, via the 'pragma' directive. However: I don't really recommend this. Your makefile should dictate if you are optimising for speed/code size/or none. Placing directives all over your code only makes it harder to make global changes.