I want to write a C code firmware for Atmel AVR microcontrollers. I will compile it using GCC. Also, I want to enable compiler optimizations (-Os
or -O2
), as I see no reason to not enable them, and they will probably generate a better assembly way faster than writing assembly manually.
But I want a small piece of code not optimized. I want to delay the execution of a function by some time, and thus I wanted to write a do-nothing loop just to waste some time. No need to be precise, just wait some time.
/* How to NOT optimize this, while optimizing other code? */
unsigned char i, j;
j = 0;
while(--j) {
i = 0;
while(--i);
}
Since memory access in AVR is a lot slower, I want i
and j
to be kept in CPU registers.
Update: I just found util/delay.h and util/delay_basic.h from AVR Libc. Although most times it might be a better idea to use those functions, this question remains valid and interesting.
Related questions:
Putting volatile asm should help. You can read more on this here:-
http://www.nongnu.org/avr-libc/user-manual/optimization.html
If you are working on Windows, you can even try putting the code under pragmas, as explained in detail below:-
https://www.securecoding.cert.org/confluence/display/cplusplus/MSC06-CPP.+Be+aware+of+compiler+optimization+when+dealing+with+sensitive+data
Hope this helps.
For me, on GCC 4.7.0, empty asm was optimized away anyways with -O3 (didnt try with -O2). and using a i++ in register or volatile resulted in a big performance penalty (in my case).
What i did was linking with another empty function which the compiler couldnt see when compiling the "main program"
Basically this:
Created "helper.c" with this function declared (empty function)
Then compiled "gcc helper.c -c -o helper.o" and then
This gave me best results (and from my belief, no overhead at all, but can't test because my program won't work without it :) )
I think it should work with icc too. Maybe not if you enable linking optimizations, but with gcc it does.
put that loop in a separate .c file and do not optimize that one file. Even better write that routine in assembler and call it from C, either way the optimizer wont get involved.
I sometimes do the volatile thing but normally create an asm function that simply returns put a call to that function the optimizer will make the for/while loop tight but it wont optimize it out because it has to make all the calls to the dummy function. The nop answer from Denilson Sá does the same thing but even tighter...
Declare
i
andj
variables asvolatile
. This will prevent compiler to optimize code involving these variables.I'm not sure why it hasn't been mentioned yet that this approach is completely misguided and easily broken by compiler upgrades, etc. It would make a lot more sense to determine the time value you want to wait until and spin polling the current time until the desired value is exceeded. On x86 you could use
rdtsc
for this purpose, but the more portable way would be to callclock_gettime
(or the variant for your non-POSIX OS) to get the time. Current x86_64 Linux will even avoid the syscall forclock_gettime
and userdtsc
internally. Or, if you can handle the cost of a syscall, just useclock_nanosleep
to begin with...I developed this answer after following a link from dmckee's answer, but it takes a different approach than his/her answer.
Function Attributes documentation from GCC mentions:
This gave me an interesting idea... Instead of adding a
nop
instruction at the inner loop, I tried adding an empty assembly code in there, like this:And it worked! That loop has not been optimized-out, and no extra
nop
instructions were inserted.What's more, if you use
volatile
, gcc will store those variables in RAM and add a bunch ofldd
andstd
to copy them to temporary registers. This approach, on the other hand, doesn't usevolatile
and generates no such overhead.Update: If you are compiling code using
-ansi
or-std
, you must replace theasm
keyword with__asm__
, as described in GCC documentation.In addition, you can also use
__asm__ __volatile__("")
if your assembly statement must execute where we put it, (i.e. must not be moved out of a loop as an optimization).