简体   繁体   English

avr-gcc破坏性优化

[英]avr-gcc destructive optimizations

I'm programming an Atmel ATtiny13a microcontroller using avr-gcc 4.8.2. 我正在使用avr-gcc 4.8.2编程Atmel ATtiny13a微控制器。

This is my c code: 这是我的c代码:

#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). 从不通过主功能调用测试功能(LED快速闪烁),因此控制器只应进入主功能(慢速闪烁)。

When I compile the code with -O1 , everything works fine: 当我用-O1编译代码时,一切正常:

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. 但是如果我使用-Os (尺寸优化)或-O2 ,微控制器会运行test功能而不是main功能:LED快速闪烁并且永不停止。

Is the -Os flag simply too dangerous to use, should it be avoided? 如果避免使用-Os标志就太危险了吗? 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. ATtiny13a只有1K的闪光灯,因此尺寸减小很重要。


Edit: As suggested in the comments, here's the assembler diff with -O1 and -O2 : http://www.diffchecker.com/3l9cdln6 编辑:正如评论中所建议的,这里是带有-O1-O2的汇编器差异: http//www.diffchecker.com/3l9cdln6

In there you can see that -O2 changes the first section from .text to .text.startup . 在那里你可以看到-O2将第一部分从.text更改为.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. 经过一些进一步的测试后,我发现罪魁祸首是-freorder-functions优化。 Is there a way to prevent this behavior? 有没有办法防止这种行为?

I did some further debugging, and found that the "culprit" was the -freorder-functions optimization. 我做了一些进一步的调试,发现“罪魁祸首”是-freorder-functions优化。 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. 我假设CFLAGS只影响编译而不是链接,所以我没有将它们传递给链接器,但在那种情况下我错了。

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. 并且因为test函数由编译器放在main函数之前而不是由链接器重新排列,这是在微控制器上实际执行的代码。

So the solution turned out to be: Compiler flags should also be passed to the linker! 所以解决方案结果是: 编译器标志也应该传递给链接器!

I am aware that my response comes after ~2 years since this question has been asked, but I belive that there's still no correct, in-depth answer. 我知道,自从提出这个问题以来,我的回答是在2年之后,但我相信仍然没有正确,深入的答案。


Let us begin with a little bit of theory: 让我们从一点理论开始:

When you invoke GCC, it normally does preprocessing, compilation, assembly and linking. 当您调用GCC时,它通常会进行预处理,编译,汇编和链接。 The “overall options” allow you to stop this process at an intermediate stage. “整体选项”允许您在中间阶段停止此过程。 For example, the -c option says not to run the linker. 例如,-c选项表示不运行链接器。 Then the output consists of object files output by the assembler. 然后输出由汇编器输出的目标文件组成。

Other options are passed on to one stage of processing. 其他选项被传递到一个处理阶段。 Some options control the preprocessor and others the compiler itself. 一些选项控制预处理器,其他选项控制编译器本身。 Yet other options control the assembler and linker; 还有其他选项控制汇编器和链接器; most of these are not documented here, since you rarely need to use any of them. 这里没有记录大部分内容,因为您很少需要使用它们中的任何一个。

source: GCC Online Docs 来源: GCC在线文档

LDFLAGS LDFLAGS

Extra flags to give to compilers when they are supposed to invoke the linker, 'ld', such as -L. 当编译器应该调用链接器'ld'时给出编译器的额外标志,例如-L。 Libraries (-lfoo) should be added to the LDLIBS variable instead. 应该将库(-lfoo)添加到LDLIBS变量中。

source: GNU make Manual 来源: GNU make Manual

As you can see, it is up to GCC (I will call it this way to distinguish from actual compiler; you can find it beeing called C compiler frontend or simply compiler though) which options will be passed to which tools and it appears that -On option isn't passed to the linker (you can check it by giving -v option to GCC). 正如你所看到的,它取决于GCC(我会这样称呼它以区别于实际的编译器;你可以发现它叫做C编译器前端或者只是简单的编译器 ),哪些选项将被传递给哪些工具,看起来-On选项未传递给链接器(您可以通过向GCC提供-v选项来检查它)。 So invoking GCC without this option when it is supposed to do only the linking is OK. 因此,当它应该只进行链接时,调用没有此选项的GCC就可以了。

The real problem is that you don't provide -mmcu=dev option to GCC when linking. 真正的问题是您在链接时不向GCC提供-mmcu=dev选项。 It is therefore unable to find proper crt*.o file (C RunTime) and tell the linker to link it in; 因此无法找到正确的crt*.o文件(C RunTime)并告诉链接器将其链接到; your application ends up without any initialization code. 您的应用程序最终没有任何初始化代码。

So please note that you must include -mmcu=dev in LDFLAGS or pass it to GCC regardless of what it is meant to do (preprocessing/compilation/assemblation/linking). 所以请注意,必须在LDFLAGS中包含-mmcu=dev或将其传递给GCC,无论其意图是什么(预处理/编译/组合/链接)。 I've already seen a couple of makefiles without this option in LDFLAGS on the Internet, so beware. 我已经在互联网上看到了LDFLAGS中没有这个选项的几个makefile,所以要小心。


Now it's time for some practice -- assuming that your source is in test.c file, issue the following commands (on linux): 现在是时候进行一些练习了 - 假设你的源代码在test.c文件中,发出以下命令(在linux上):

avr-gcc -mmcu=attiny13a -DF_CPU=1200000 -Wall -O1 -c -o testO1.o test.c
avr-gcc -mmcu=attiny13a -DF_CPU=1200000 -Wall -Os -c -o testOs.o test.c
avr-gcc -o testO1_nodev.elf testO1.o
avr-gcc -v -o testOs_nodev.elf testOs.o > testOs_nodev.log 2>&1
avr-gcc -v -mmcu=attiny13a -o testOs_correct.elf testOs.o > testOs_correct.log 2>&1

I left only necessary options + -Wall , for ATtiny13a you need -mmcu=attiny13a instead of -mmcu=attiny13 . 我只留下必要的选项+ -Wall ,对于ATtiny13a你需要-mmcu=attiny13a而不是-mmcu=attiny13

Let's take a look at testOs_nodev.log and testOs_correct.log . 我们来看看testOs_nodev.logtestOs_correct.log Issue the following command: 发出以下命令:

diff testOs_nodev.log testOs_correct.log

and you will see something like: 你会看到类似的东西:

2c2
< Reading specs from /usr/lib/gcc/avr/5.2.0/device-specs/specs-avr2
---
> Reading specs from /usr/lib/gcc/avr/5.2.0/device-specs/specs-attiny13a
10,12c10,12
< LIBRARY_PATH=/usr/lib/gcc/avr/5.2.0/:/usr/lib/gcc/avr/5.2.0/../../../../avr/lib/
< COLLECT_GCC_OPTIONS='-v' '-o' 'testOs_nodev.elf' '-specs=device-specs/specs-avr2'
<  /usr/lib/gcc/avr/5.2.0/collect2 -plugin /usr/lib/gcc/avr/5.2.0/liblto_plugin.so \
-plugin-opt=/usr/lib/gcc/avr/5.2.0/lto-wrapper -plugin-opt=-fresolution=/tmp/ccqBjM6T.res \
-plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lm -plugin-opt=-pass-through=-lc \ 
-o testOs_nodev.elf -L/usr/lib/gcc/avr/5.2.0 -L/usr/lib/gcc/avr/5.2.0/../../../../avr/lib \
testOs.o --start-group -lgcc -lm -lc --end-group
---
> LIBRARY_PATH=/usr/lib/gcc/avr/5.2.0/avr25/tiny-stack/:\
/usr/lib/gcc/avr/5.2.0/../../../../avr/lib/avr25/tiny-stack/:\
/usr/lib/gcc/avr/5.2.0/:/usr/lib/gcc/avr/5.2.0/../../../../avr/lib/
> COLLECT_GCC_OPTIONS='-v'  '-o' 'testOs_correct.elf' '-specs=device-specs/specs-attiny13a' \
'-mmcu=avr25' '-msp8'
>  /usr/lib/gcc/avr/5.2.0/collect2 -plugin /usr/lib/gcc/avr/5.2.0/liblto_plugin.so \
-plugin-opt=/usr/lib/gcc/avr/5.2.0/lto-wrapper -plugin-opt=-fresolution=/tmp/ccV919rY.res \
-plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lm -plugin-opt=-pass-through=-lc \
-plugin-opt=-pass-through=-lattiny13a -mavr25 -o testOs_correct.elf \
/usr/lib/gcc/avr/5.2.0/../../../../avr/lib/avr25/tiny-stack/crtattiny13a.o \
-L/usr/lib/gcc/avr/5.2.0/avr25/tiny-stack -L/usr/lib/gcc/avr/5.2.0/../../../../avr/lib/avr25/tiny-stack \
-L/usr/lib/gcc/avr/5.2.0 -L/usr/lib/gcc/avr/5.2.0/../../../../avr/lib testOs.o \
--start-group -lgcc -lm -lc -lattiny13a --end-group

(I broke a few lines to make it readable) (我打破了几行使其可读)

The difference is that without -mmcu=dev option GCC defaults to use avr2 specs file and doesn't link any CRT file. 区别在于没有-mmcu=dev选项GCC默认使用avr2 specs文件,并且不链接任何CRT文件。

Examine object files ( *.o ) and output files ( *.elf ) using avr-objdump: 使用avr-objdump检查目标文件( *.o )和输出文件( *.elf ):

avr-objdump -xd testOs_nodev.elf

You will notice that *_nodev.elf files don't contain correct information about architecture (avr instead of avr:25) nor any startup code (compare testOs_correct.elf with testOs_nodev.elf ). 您会注意到*_nodev.elf文件不包含有关体系结构的正确信息(avr而不是avr:25),也没有任何启动代码(将testOs_correct.elftestOs_nodev.elf进行比较)。 The code section seems to a verbatim copy of what was supplied in object file. 代码部分似乎是对象文件中提供的内容的逐字副本。


If any part of my elaboration seems unclear or needs extra explanation, feel free to ask (comment). 如果我的阐述的任何部分似乎不清楚或需要额外的解释,请随意询问(评论)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM