gpt4 book ai didi

c - 自己编写的简单 memset 不适用于 ARMv7 上的 -03 eabi gcc

转载 作者:行者123 更新时间:2023-12-03 23:47:18 32 4
gpt4 key购买 nike

我在 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) 这是一个问题。当函数由于 'n' 为零而没有提前退出时,这种推送会在没有匹配的 pop 的情况下无休止地重复,因为它被 (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)它没有调用你的 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已经回答了这个问题:

Compile with -fno-builtin-memset. The compiler recognises that the function implements memset and thus replaces it with a call to memset. You should in general compile with -ffreestanding when writing bare-metal code. I believe this fixes this sort of problem, too



它正在用 memset 替换您的代码,如果您不想这样做,请使用 -ffreestanding。

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

关于c - 自己编写的简单 memset 不适用于 ARMv7 上的 -03 eabi gcc,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61866475/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com