自写的简单memset不适用于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
cat2799 回答:自写的简单memset不适用于ARMv7上的-03 eabi gcc

您的第一个问题(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,#2
  2c:   e7cfc41e    bfi r12,#8,#8
  30:   e7d7c81e    bfi r12,#16,#8
  34:   e7dfcc1e    bfi r12,#24,#8
  38:   9a000024    bls d0 <xmemset+0xd0>
  3c:   e2445009    sub r5,r4,#9
  40:   e1a03000    mov r3,r0
  44:   e3c55007    bic r5,#7
  48:   e3a07000    mov r7,#0
  4c:   e2851008    add r1,#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,#8
  68:   e583c008    str r12,#8]
  6c:   e2833020    add r3,r3,#32
  70:   e503c014    str r12,#-20] ; 0xffffffec
  74:   e503c010    str r12,#-16]
  78:   e503c00c    str r12,#-12]
  7c:   e503c008    str r12,#-8]
  80:   e503c004    str r12,#-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,#3
  9c:   e1520003    cmp r2,r3
  a0:   e0466003    sub r6,r3
  a4:   e0803003    add r3,r0,r3
  a8:   08bd81f0    popeq   {r4,pc}
  ac:   e3560000    cmp r6,#0
  b0:   e5c3e000    strb    lr,[r3]
  b4:   08bd81f0    popeq   {r4,pc}
  b8:   e3560001    cmp r6,#1
  bc:   e5c3e001    strb    lr,#1]
  c0:   15c3e002    strbne  lr,#2]
  c4:   e8bd81f0    pop {r4,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,#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进行编译。我相信这也可以解决此类问题

如果您不希望使用-ffreestanding这样做,它将用memset替换您的代码。

如果您希望超越此范围,并想知道-fno-builtin-memset为什么不起作用,这是gcc人士的一个问题,请提交票证,让我们知道他们说的是什么(或者只是看看编译器源代码)代码)。

本文链接:https://www.f2er.com/2329735.html

大家都在问