C语言 自写的简单内存集不适用于 ARMv7 上的 -03 eabi gcc



我用 c 写了一个非常简单的内存集,它适用于 -O2,但不适用于 -O3...

记忆集:

void * memset(void * blk, int c, size_t n)
{
unsigned char * dst = blk;
while (n-- > 0)
*dst++ = (unsigned char)c;
return blk;
}

。使用 -O2 时编译到此程序集:

20000430 <memset>:
20000430:       e3520000        cmp     r2, #0                  @ compare param 'n' with zero
20000434:       012fff1e        bxeq    lr                      @ if equal return to caller
20000438:       e6ef1071        uxtb    r1, r1                  @ else zero extend (extract byte from) param 'c'
2000043c:       e0802002        add     r2, r0, r2              @ add pointer 'blk' to 'n'
20000440:       e1a03000        mov     r3, r0                  @ move pointer 'blk' to r3
20000444:       e4c31001        strb    r1, [r3], #1            @ store value of 'c' to address of r3, increment r3 for next pass
20000448:       e1530002        cmp     r3, r2                  @ compare current store address to calculated max address
2000044c:       1afffffc        bne     20000444 <memset+0x14>  @ if not equal store next byte
20000450:       e12fff1e        bx      lr                      @ else back to caller

这对我来说是有道理的。我注释了这里发生的事情。

当我使用 -O3 编译它时,程序崩溃了。我的memset反复调用自己,直到它吃掉了整个堆栈:

200005e4 <memset>:
200005e4:       e3520000        cmp     r2, #0                  @ compare param 'n' with zero
200005e8:       e92d4010        push    {r4, lr}                @ ? (1)
200005ec:       e1a04000        mov     r4, r0                  @ move pointer 'blk' to r4 (temp to hold return value)
200005f0:       0a000001        beq     200005fc <memset+0x18>  @ if equal (first line compare) jump to epilogue
200005f4:       e6ef1071        uxtb    r1, r1                  @ zero extend (extract byte from) param 'c'
200005f8:       ebfffff9        bl      200005e4 <memset>       @ call myself ? (2)
200005fc:       e1a00004        mov     r0, r4                  @ epilogue start. move return value to r0
20000600:       e8bd8010        pop     {r4, pc}                @ restore r4 and back to caller

我无法弄清楚这个优化版本应该如何在没有任何strb或类似的情况下工作。如果我尝试将内存设置为"0"或其他什么东西并不重要,因此该函数不仅在 .bss(零初始化(变量上调用。

(1(这是一个问题。这种推送在没有匹配的弹出声的情况下无休止地重复,当函数由于"n"为零而没有提前退出时,它是由 (2( 调用的。我用uart印刷品验证了这一点。此外,r2从未被触及,那么为什么与零的比较会成为现实呢?

请帮助我了解这里发生的事情。编译器是否假定我可能无法满足的先决条件?

背景:我在我的裸机项目中使用了需要 memset 的外部代码,所以我推出了自己的代码。它仅在启动时使用一次,而不是性能关键型。

/edit:使用以下选项调用编译器:

arm-none-eabi-gcc -O3 -Wall -Wextra -fPIC -nostdlib -nostartfiles -marm -fstrict-volatile-bitfields -march=armv7-a -mcpu=cortex-a9 -mfloat-abi=hard -mfpu=neon-vfpv3

你的第一个问题(1(。 这是根据调用约定,如果您要进行嵌套函数调用,则需要保留链接寄存器,并且需要 64 位对齐。 代码使用 r4,因此这是保存的额外寄存器。 那里没有魔法。

你的第二个问题(2(它不是调用你的内存集,而是在优化你的代码,因为它认为它是一个低效的内存集。 Fuz已经为您的问题提供了答案。

重命名函数

00000000 <xmemset>:
0:   e3520000    cmp r2, #0
4:   e92d4010    push    {r4, lr}
8:   e1a04000    mov r4, r0
c:   0a000001    beq 18 <xmemset+0x18>
10:   e6ef1071    uxtb    r1, r1
14:   ebfffffe    bl  0 <memset>
18:   e1a00004    mov r0, r4
1c:   e8bd8010    pop {r4, pc}

你可以看到这一点。

如果你按照 Fuz 推荐使用 -ffreestand,那么你会看到这个或类似的东西

00000000 <xmemset>:
0:   e3520000    cmp r2, #0
4:   012fff1e    bxeq    lr
8:   e92d41f0    push    {r4, r5, r6, r7, r8, lr}
c:   e2426001    sub r6, r2, #1
10:   e3560002    cmp r6, #2
14:   e6efe071    uxtb    lr, r1
18:   9a00002a    bls c8 <xmemset+0xc8>
1c:   e3a0c000    mov r12, #0
20:   e3520023    cmp r2, #35 ; 0x23
24:   e7c7c01e    bfi r12, lr, #0, #8
28:   e1a04122    lsr r4, r2, #2
2c:   e7cfc41e    bfi r12, lr, #8, #8
30:   e7d7c81e    bfi r12, lr, #16, #8
34:   e7dfcc1e    bfi r12, lr, #24, #8
38:   9a000024    bls d0 <xmemset+0xd0>
3c:   e2445009    sub r5, r4, #9
40:   e1a03000    mov r3, r0
44:   e3c55007    bic r5, r5, #7
48:   e3a07000    mov r7, #0
4c:   e2851008    add r1, r5, #8
50:   e1570005    cmp r7, r5
54:   f5d3f0a0    pld [r3, #160]  ; 0xa0
58:   e1a08007    mov r8, r7
5c:   e583c000    str r12, [r3]
60:   e583c004    str r12, [r3, #4]
64:   e2877008    add r7, r7, #8
68:   e583c008    str r12, [r3, #8]
6c:   e2833020    add r3, r3, #32
70:   e503c014    str r12, [r3, #-20] ; 0xffffffec
74:   e503c010    str r12, [r3, #-16]
78:   e503c00c    str r12, [r3, #-12]
7c:   e503c008    str r12, [r3, #-8]
80:   e503c004    str r12, [r3, #-4]
84:   1afffff1    bne 50 <xmemset+0x50>
88:   e2811001    add r1, r1, #1
8c:   e483c004    str r12, [r3], #4
90:   e1540001    cmp r4, r1
94:   8afffffb    bhi 88 <xmemset+0x88>
98:   e3c23003    bic r3, r2, #3
9c:   e1520003    cmp r2, r3
a0:   e0466003    sub r6, r6, r3
a4:   e0803003    add r3, r0, r3
a8:   08bd81f0    popeq   {r4, r5, r6, r7, r8, pc}
ac:   e3560000    cmp r6, #0
b0:   e5c3e000    strb    lr, [r3]
b4:   08bd81f0    popeq   {r4, r5, r6, r7, r8, pc}
b8:   e3560001    cmp r6, #1
bc:   e5c3e001    strb    lr, [r3, #1]
c0:   15c3e002    strbne  lr, [r3, #2]
c4:   e8bd81f0    pop {r4, r5, r6, r7, r8, pc}
c8:   e1a03000    mov r3, r0
cc:   eafffff6    b   ac <xmemset+0xac>
d0:   e1a03000    mov r3, r0
d4:   e3a01000    mov r1, #0
d8:   eaffffea    b   88 <xmemset+0x88>

看起来它只是内联了 memset,它不知道你的代码(更快的那个(。

因此,如果您希望它使用您的代码,请坚持使用 -O2。 你的效率很低,所以不知道为什么你需要把它推得更远。

20000444:       e4c31001        strb    r1, [r3], #1            @ store value of 'c' to address of r3, increment r3 for next pass
20000448:       e1530002        cmp     r3, r2                  @ compare current store address to calculated max address
2000044c:       1afffffc        bne     20000444 <memset+0x14>  @ if not equal store next byte

如果不用其他东西替换您的代码,它不会比这更好。

Fuz 已经回答了这个问题:

使用 -fno-builtin-memset 编译。编译器识别出该函数实现 memset,因此将其替换为对 memset 的调用。在编写裸机代码时,通常应该使用 -ffreestanding 进行编译。我相信这也解决了这类问题

它正在用memset替换你的代码,如果你不希望它这样做,请使用-ffreestand。

如果你想超越这一点,想知道为什么-fno-builtin-memset不起作用,这是gcc人员的问题,提交票证,让我们知道他们说什么(或者只是看看编译器源代码(。

最新更新