这段代码(手臂):

void blinkRed(void)
{
    for(;;)
    {
        bb[0x0008646B] ^= 1;
        sys.Delay_ms(14);
    }
}

...被编译为以下asm代码:
08000470:   ldr r4, [pc, #20]       ; (0x8000488 <blinkRed()+24>) // r4 = 0x422191ac
08000472:   ldr r6, [pc, #24]       ; (0x800048c <blinkRed()+28>)
08000474:   movs r5, #14
08000476:   ldr r3, [r4, #0]
08000478:   eor.w r3, r3, #1
0800047c:   str r3, [r4, #0]
0800047e:   mov r0, r6
08000480:   mov r1, r5
08000482:   bl 0x80001ac <CSTM32F100C6::Delay_ms(unsigned int)>
08000486:   b.n 0x8000476 <blinkRed()+6>

没关系。

但是,如果我只是更改数组索引(-0x400)....
void blinkRed(void)
{
    for(;;)
    {
        bb[0x0008606B] ^= 1;
        sys.Delay_ms(14);
    }
}

...我没有那么优化的代码:
08000470:   ldr r4, [pc, #24]       ; (0x800048c <blinkRed()+28>) // r4 = 0x42218000
08000472:   ldr r6, [pc, #28]       ; (0x8000490 <blinkRed()+32>)
08000474:   movs r5, #14
08000476:   ldr.w r3, [r4, #428]    ; 0x1ac
0800047a:   eor.w r3, r3, #1
0800047e:   str.w r3, [r4, #428]    ; 0x1ac
08000482:   mov r0, r6
08000484:   mov r1, r5
08000486:   bl 0x80001ac <CSTM32F100C6::Delay_ms(unsigned int)>
0800048a:   b.n 0x8000476 <blinkRed()+6>

区别在于,在第一种情况下,r4立即加载了目标地址(0x422191ac),然后使用2字节指令执行对内存的访问,但是在第二种情况下,r4加载了一些中间地址
地址(0x42218000),然后使用偏移量(+0x1ac)到目标地址(0x422181ac)的4字节指令执行对内存的访问。

为什么编译器会这样做?

我用:arm-none-eabi-g++ -mcpu=cortex-m3 -mthumb -g2 -Wall -O1 -std=gnu++14 -fno-exceptions -fno-use-cxa-atexit -fstrict-volatile-bitfields -c -DSTM32F100C6T6B -DSTM32F10X_LD_VLbb是:
__attribute__ ((section(".bitband"))) volatile u32 bb[0x00800000];

.ld中,它定义为:
MEMORY部分:
BITBAND(rwx): ORIGIN = 0x42000000, LENGTH = 0x02000000

SECTIONS部分:
.bitband (NOLOAD) :
SUBALIGN(0x02000000)
{
    KEEP(*(.bitband))
} > BITBAND

最佳答案

我认为这是-O1的伪像/缺失优化机会。

如果我们看一下用-O-生成的代码来加载bb[...],可以更详细地理解:

第一种情况:

movw    r2, #:lower16:bb
movt    r2, #:upper16:bb
movw    r3, #37292
movt    r3, 33
adds    r3, r2, r3
ldr r3, [r3, #0]

第二种情况:
movw    r3, #:lower16:bb
movt    r3, #:upper16:bb
add r3, r3, #2195456       ; 0x218000    = 4*0x86000
add r3, r3, #428
ldr r3, [r3, #0]

第二种情况下的代码更好,并且可以通过这种方式完成,因为可以用两个添加指令来添加常量(如果索引为0x0008646B,则不是这种情况)。

-O1仅执行不费时的优化。因此,显然,它早先合并了add和ldr,因此错过了以后再用一个pc相对ldr加载整个地址的机会。

用-O2(或-fgcse)编译,代码看起来像预期的那样。

07-24 09:45
查看更多