在ARMv7上使用-03 eabi gcc时,自己编写的简单的memset不能工作。

问题描述 投票:4回答:1

我用c语言写了一个非常简单的memset,在-O2下可以正常工作,但在-O3下就不行了......。

memset:

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)这是个问题。这个推送会被无休止地重复,而没有匹配的pop,因为它被(2)调用时,函数不会因为'n'为零而提前退出。我用uart打印验证了这一点。另外r2从来没有被碰过,为什么比较为零会变成真的?

请帮助我理解这里发生了什么。编译器是否假设了一些我可能不满足的先决条件?

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

编辑:编译器是用这些选项调用的。

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
c assembly armv7 eabi
1个回答
3
投票

你的第一个问题(1)。 这是根据调用惯例,如果你要进行嵌套函数调用,你需要保留链接寄存器,你需要64位对齐。 代码中使用了r4,所以这就是额外保存的寄存器。 没有什么神奇的地方。

你的第二个问题(2)它不是在调用你的memset它是在优化你的代码,因为它认为这是一个低效的memset。 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的建议使用-ffreestanding,那么你就会看到这个或类似的东西。

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替换你的代码,如果你想让它不这样做,请使用-ffreestanding。

如果你想知道为什么-fno-builtin-memset不工作,那是gcc的人的问题,你可以提交一个票据,让我们知道他们是怎么说的(或者看看编译器的源代码)。

© www.soinside.com 2019 - 2024. All rights reserved.