Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

avr-gcc destructive optimizations

I'm programming an Atmel ATtiny13a microcontroller using avr-gcc 4.8.2.

This is my c code:

#include <avr/io.h> 
#include <util/delay.h> 

int main(void) {
    DDRB = 1; // PB0 is output
    for (uint8_t i = 0; i < 10; i++) {
        PORTB = 1;
        _delay_ms(500);
        PORTB = 0;
        _delay_ms(500);
    }
    while(1);
}

void test(void) {
    DDRB = 1; // PB0 is output
    for (uint8_t i = 0; i < 10; i++) {
        PORTB = 1;
        _delay_ms(100);
        PORTB = 0;
        _delay_ms(100);
    }
}

The test function (fast blinking of an LED) is never called from the main function, so the controller should only enter the main function (slow blinking).

When I compile the code with -O1, everything works fine:

avr-gcc -std=gnu99 -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -mmcu=attiny13 -DF_CPU=1200000   -Wall -Wstrict-prototypes -Os -c test.c -o test.o
avr-gcc  test.o -o test.elf
avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature test.elf test.hex

But if I use -Os (optimization for size) or -O2, the microcontroller runs the test function instead of the main function: The LED blinks quickly and never stops.

Is the -Os flag simply too dangerous to use, should it be avoided? Or is there something I can change in my code to avoid this kind of bug? The ATtiny13a only has 1K of flash, so size reduction is something important.


Edit: As suggested in the comments, here's the assembler diff with -O1 and -O2: http://www.diffchecker.com/3l9cdln6

In there you can see that -O2 changes the first section from .text to .text.startup.

--- test.o1.txt 2013-12-03 19:10:43.874598682 +0100
+++ test.o2.txt 2013-12-03 19:10:50.574674155 +0100
@@ -3,7 +3,7 @@
 __SREG__ = 0x3f
 __tmp_reg__ = 0
 __zero_reg__ = 1
-       .text
+       .section        .text.startup,"ax",@progbits
 .global        main
        .type   main, @function
 main:

That's probably the main issue here. After some further testing I found that the culprit is the -freorder-functions optimization. Is there a way to prevent this behavior?

like image 824
Danilo Bargen Avatar asked Dec 06 '22 04:12

Danilo Bargen


1 Answers

I did some further debugging, and found that the "culprit" was the -freorder-functions optimization. It is documented in the manpage as follows:

-freorder-functions
    Reorder functions in the object file in order to improve code locality.
    This is implemented by using special subsections ".text.hot" for most
    frequently executed functions and ".text.unlikely" for unlikely executed
    functions. Reordering is done by the linker so object file format must
    support named sections and linker must place them in a reasonable way.

The last line in the documentation explains the problem I was having/causing. If we look at the compile commands from the original question again:

$ avr-gcc -std=gnu99 -funsigned-char -funsigned-bitfields -fpack-struct \
   -fshort-enums -mmcu=attiny13 -DF_CPU=1200000   -Wall -Wstrict-prototypes \
   -Os -c test.c -o test.o
$ avr-gcc  test.o -o test.elf

...we see that I was passing the optimization flags to the compiler, but not to the linker. I assumed that the CFLAGS only affect compilation and not linking, so I did not pass them to the linker, but in that case I was wrong.

The result: The assembly code was reordered by the compiler (including appropriate labels), but the linker did not consider these labels. And because the test function was placed before the main function by the compiler and not re-arranged by the linker, that was the code that was actually executed on the microcontroller.

So the solution turned out to be: Compiler flags should also be passed to the linker!

like image 173
Danilo Bargen Avatar answered Dec 14 '22 13:12

Danilo Bargen