以下代码包含avr 128 da 32 MCU的简单示例。用户可以通过VPORTA_DIR
等旧宏或VPORTA.DIR
等结构Map访问SFR。
#include <avr/io.h>
#include <stdint.h>
static uint16_t g;
int main() {
for(uint8_t i = 0; i < 20; i++) {
++g;
// VPORTA_DIR; // <1> suppresses optimization
VPORTA.DIR; // <2> OK
}
}
对于<2>
的情况,载荷/悬挂物到g
的提升/下沉如预期的那样离开环。
但对于<1>
情况,不会发生这种情况-但代码应该相同。
在<1>
的情况下,avr-gcc抑制了哪些优化?<1>
的组装:
main:
ldi r24,lo8(20) ; ivtmp_4,
.L2:
lds r18,g ; g, g
lds r19,g+1 ; g, g
subi r18,-1 ; tmp48,
sbci r19,-1 ; ,
sts g,r18 ; g, tmp48
sts g+1,r19 ; g, tmp48
in r25,0 ; vol.1_7, MEM[(volatile uint8_t *)0B]
subi r24,lo8(-(-1)) ; ivtmp_4,
cpse r24,__zero_reg__ ; ivtmp_4,
rjmp .L2 ;
ldi r24,0 ;
ldi r25,0 ;
ret
<2>
的组装:
main:
lds r24,g ; g_lsm.6, g
lds r25,g+1 ; g_lsm.6, g
ldi r18,lo8(20) ; ivtmp_2,
.L2:
in r19,0 ; vol.1_7, MEM[(struct VPORT_t *)0B].DIR
subi r18,lo8(-(-1)) ; ivtmp_2,
cpse r18,__zero_reg__ ; ivtmp_2,
rjmp .L2 ;
adiw r24,20 ; tmp49,
sts g,r24 ; g, tmp49
sts g+1,r25 ; g, tmp49
ldi r24,0 ;
ldi r25,0 ;
ret
编译是用avr-gcc 12.2和-Os
完成的。
1条答案
按热度按时间iq3niunx1#
这似乎是一个库和/或编译器(端口)的bug,而且看起来比错过优化更严重。我可以在avr-gcc 12.2(以及更老的版本)中重现它,如下所示:
产量(全香蕉):
这里的
#define VPORTA (*(VPORT_t*) 0x0000)
(根据OP在AVR库中所做的)是一个肮脏的强制转换,似乎会绊倒编译器。不知何故,它似乎认为VPORT
可能是g
的别名,这是毫无意义的。uint16_t
与VPORT_t
不兼容。严格别名规则的例外情况都不适用。奇怪的是,当启用严格别名时(这应该是默认设置
-fstrict-aliasing
),它可以工作,但当我禁用它-fno-strict-aliasing
时,它就不能工作了。而且这里没有严格别名的情况。使用严格别名时,会生成预期的代码,如下所示:0x0000恰好是空指针常量似乎并不重要,无论使用什么地址,我都会得到同样的错误机器码。如果我这样做了,我会得到一个毫无意义的警告:
警告:数组下标0在"VPORT_t [0]" {又称"结构VPORT_struct []"}[-Warray-bounds]的数组边界之外|#定义虚拟端口(*(虚拟端口_t *)0x0001)
见鬼?这一切看起来完全崩溃了,我不能解释为什么。显然,不要像这样使用肮脏的强制转换,但如果这是你从AVR库中得到的,那么那个库是坏的。
此外,编译器应该能够从代码中删除对
g
的所有访问,因为它没有在任何包含副作用的表达式中使用。显而易见的解决方法是删除该结构体: