How to prevent GCC from optimizing out a busy wait loop?
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:
- How to prevent gcc optimizing some statements in C?
- Is there a way to tell GCC not to optimise a particular piece of code?
- How not to optimize away - mechanics of a folly function
Solution 1:
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:
noinline
This function attribute prevents a function from being considered for inlining. If the function does not have side-effects, there are optimizations other than inlining that causes function calls to be optimized away, although the function call is live. To keep such calls from being optimized away, putasm ("");
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:
unsigned char i, j;
j = 0;
while(--j) {
i = 0;
while(--i)
asm("");
}
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 of ldd
and std
to copy them to temporary registers. This approach, on the other hand, doesn't use volatile
and generates no such overhead.
Update: If you are compiling code using -ansi
or -std
, you must replace the asm
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).
Solution 2:
Declare i
and j
variables as volatile
. This will prevent compiler to optimize code involving these variables.
unsigned volatile char i, j;