此问题在此处已有答案:
Getting GCC to compile without inserting call to memcpy(5个答案)
21天前关闭。
我尝试在ARM Cortex M3处理器上执行一个GNU C项目。该项目在-Og优化级别上运行良好,但当我尝试将优化级别增加到-O2、-O3时,我遇到了总线故障。
GNU工具链是“arm-none-eabi V10.3.1”
尝试阅读BFSR寄存器,结果显示是PRECISERR & STKERR。错误发生在自实现的memset函数中,由于项目不需要标准CLib,因此已解决该问题。
void* memset(void s, int c, size_t len){
unsigned char *dst;
dst = (unsigned char) s;
while (len > 0) {
*dst = (unsigned char) c;
dst++;
len--;
}
return s; }
此外,在检查了此函数的汇编后,注意到这对于-Og选项(工作)和崩溃的-O2/3/s选项来说是完全不同的。
我在这里复制了两个选项的装配屏幕截图。
第一次
相信是该函数的返回导致了STKERR,而且我看到了BL指令(在-O2/O3/O 4选项中),这可能是根本原因,因为它将下一个指令地址推入链接寄存器,随后的弹出PC可能会失败?
但是我能够通过修改代码并使变量成为volatile来解决这个问题。下面是新的实现。
void* memset(void *s, int c, size_t len) {
unsigned char * volatile dst;
volatile size_t count = 0;
dst = (unsigned char * volatile) s;
while (count < len) {
dst[count] = (unsigned char) c;
count++;
}
return s;
}
请想知道这是否是GNU工具链中的一个bug?
有问题的memset函数的汇编如下(-O2/-O3/-Os):-
.section .text.memset,"ax",%progbits
.align 1
.p2align 2,,3
.global memset
.syntax unified
.thumb
.thumb_func
.type memset, %function
memset:
.cfi_startproc
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 0, uses_anonymous_args = 0
push {r4, lr}
mov r4, r0
cbz r2, .L34
uxtb r1, r1
bl memset
mov r0, r4
pop {r4, pc}
.cfi_endproc
使用-Og选项编译的memset函数的程序集(有效)
.section .text.memset,"ax",%progbits
.align 1
.global memset
.syntax unified
.thumb
.thumb_func
.type memset, %function
memset:
.cfi_startproc
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 0, uses_anonymous_args = 0
@ link register save eliminated.
mov r3, r0
.L20:
strb r1, [r3], #1
subs r2, r2, #1
cmp r2, #0
bne .L20
bx lr
.cfi_endproc
.LFE81:
.size memset, .-memset
2条答案
按热度按时间n3schb8v1#
即使使用
-ffreestanding
、-nostdlib
和类似的选项,愚者仍然会发出对某些“库函数”的调用,当它发现它可以将代码优化为对这些函数之一的调用时。memset
就是其中之一。因此愚者将memset
函数优化为对memset
的调用,这当然会导致无限递归和崩溃。这在www.example.com上有记录https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gcc/Standards.html#Standards:
愚者使用的大多数编译器支持例程都存在于libgcc中,但也有一些例外。愚者要求独立环境提供memcpy、memmove、memset和memcmp。最后,如果使用了__builtin_trap,并且目标没有实现陷阱模式,则愚者发出一个调用以中止。
我们希望您用汇编语言实现这些函数,而且您可能希望这样做,以便可以使用手动优化的版本。正如您所注意到的,您可以使用
volatile
等技巧来抑制优化,但这将导致memset
的最幼稚和最不优化的版本,您可能对性能不太满意。Getting GCC to compile without inserting call to memcpy还建议使用
-fno-tree-loop-distribute-patterns
。与-O3
结合使用,它使用16字节SIMD存储,而不是单字节strb
,因此这是一个相当大的改进。https://godbolt.org/z/K8645on1e。但是,未来的一些编译器版本可能会打破这一点。62lalag42#
所以我们基本上在昨天的评论中回答了这个问题。然后Nate回答了如何解决这个问题。那就是不要使用-O3,使用-O2(至少在我使用的gnu上)。一般来说不要使用-O3。