c# 寄存器访问抑制可能的优化(avr-gcc)

pn9klfpd  于 2023-02-14  发布在  C#
关注(0)|答案(1)|浏览(181)

以下代码包含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完成的。

iq3niunx

iq3niunx1#

这似乎是一个库和/或编译器(端口)的bug,而且看起来比错过优化更严重。我可以在avr-gcc 12.2(以及更老的版本)中重现它,如下所示:

gcc -std=c11 -pedantic -Wall -Wextra -Os -mmcu=avr51 -fno-strict-aliasing
#include <stdint.h>

typedef struct VPORT_struct 
{     
  volatile uint8_t DIR;  /* Data Direction */     
  volatile uint8_t OUT;  /* Output Value */     
  volatile uint8_t IN;  /* Input Value */     
  volatile uint8_t INTFLAGS;  /* Interrupt Flags */ 
} VPORT_t;

#define VPORTA (*(VPORT_t*) 0x0000)

static uint16_t g; 

int main() {
    for(uint8_t i = 0; i < 20; i++) {
        ++g;
        VPORTA.DIR;
    }
}

产量(全香蕉):

__zero_reg__ = 1
main:
        ldi r24,lo8(20)
.L2:
        lds r18,g
        lds r19,g+1
        subi r18,-1
        sbci r19,-1
        sts g+1,r19
        sts g,r18
        lds r25,0
        subi r24,lo8(-(-1))
        cpse r24,__zero_reg__
        rjmp .L2
        ldi r24,0
        ldi r25,0
        ret

这里的#define VPORTA (*(VPORT_t*) 0x0000)(根据OP在AVR库中所做的)是一个肮脏的强制转换,似乎会绊倒编译器。不知何故,它似乎认为VPORT可能是g的别名,这是毫无意义的。uint16_tVPORT_t不兼容。严格别名规则的例外情况都不适用。
奇怪的是,当启用严格别名时(这应该是默认设置-fstrict-aliasing),它可以工作,但当我禁用它-fno-strict-aliasing时,它就不能工作了。而且这里没有严格别名的情况。使用严格别名时,会生成预期的代码,如下所示:

.L2:
    lds r19,0
    subi r18,lo8(-(-1))
    cpse r18,__zero_reg__
    rjmp .L2

0x0000恰好是空指针常量似乎并不重要,无论使用什么地址,我都会得到同样的错误机器码。如果我这样做了,我会得到一个毫无意义的警告:
警告:数组下标0在"VPORT_t [0]" {又称"结构VPORT_struct []"}[-Warray-bounds]的数组边界之外|#定义虚拟端口(*(虚拟端口_t *)0x0001)
见鬼?这一切看起来完全崩溃了,我不能解释为什么。显然,不要像这样使用肮脏的强制转换,但如果这是你从AVR库中得到的,那么那个库是坏的。
此外,编译器应该能够从代码中删除对g的所有访问,因为它没有在任何包含副作用的表达式中使用。
显而易见的解决方法是删除该结构体:

#define VPORTA_DIR (*(volatile uint8_t*)0x0000u)

相关问题