How do I stop GCC from optimizing this byte-for-byte copy into a memcpy call?

JL2*_*210 8 c gcc compiler-optimization

I have this code for memcpy as part of my implementation of the standard C library which copies memory from src to dest one byte at a time:

void *memcpy(void *restrict dest, const void *restrict src, size_t len)
{
    char *dp = (char *restrict)dest;
    const char *sp = (const char *restrict)src;

    while( len-- )
    {
        *dp++ = *sp++;
    }

    return dest;
}
Run Code Online (Sandbox Code Playgroud)

With gcc -O2, the code generated is reasonable:

memcpy:
.LFB0:
        movq    %rdi, %rax
        testq   %rdx, %rdx
        je      .L2
        xorl    %ecx, %ecx
.L3:
        movzbl  (%rsi,%rcx), %r8d
        movb    %r8b, (%rax,%rcx)
        addq    $1, %rcx
        cmpq    %rdx, %rcx
        jne     .L3
.L2:
        ret
.LFE0:
Run Code Online (Sandbox Code Playgroud)

但是,在处gcc -O3,GCC将此天真的逐字节副本优化为memcpy调用:

memcpy:
.LFB0:
        testq   %rdx, %rdx
        je      .L7
        subq    $8, %rsp
        call    memcpy
        addq    $8, %rsp
        ret
.L7:
        movq    %rdi, %rax
        ret
.LFE0:
Run Code Online (Sandbox Code Playgroud)

这将不起作用(memcpy无条件调用自身),并且会导致段错误。

我尝试过传递-fno-builtin-memcpy-fno-loop-optimizations,并且发生相同的事情。

我正在使用GCC版本8.3.0:

Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/local/libexec/gcc/x86_64-cros-linux-gnu/8.3.0/lto-wrapper
Target: x86_64-cros-linux-gnu
Configured with: ../configure --prefix=/usr/local --libdir=/usr/local/lib64 --build=x86_64-cros-linux-gnu --host=x86_64-cros-linux-gnu --target=x86_64-cros-linux-gnu --enable-checking=release --disable-multilib --enable-threads=posix --disable-bootstrap --disable-werror --disable-libmpx --enable-static --enable-shared --program-suffix=-8.3.0 --with-arch-64=x86-64
Thread model: posix
gcc version 8.3.0 (GCC) 
Run Code Online (Sandbox Code Playgroud)

如何禁用使副本转换为memcpy呼叫的优化?

Ant*_*ala 11

在这里似乎一件事就足够了:不用使用-fno-builtin-memcpyuse -fno-builtin来编译single的翻译单元memcpy

另一种选择是通过-fno-tree-loop-distribute-patterns; 尽管这可能很脆弱,因为它禁止编译器先重新组织循环代码,然后再用mem*函数的调用来替换其中的一部分。

或者,由于您不能依赖C库中的任何内容,-ffreestanding因此可以按顺序使用。


Aco*_*orn 6

这不起作用(memcpy 无条件调用自身),并且会导致段错误。

重新定义memcpy是未定义的行为

如何禁用导致副本转换为 memcpy 调用的优化(最好仍然使用 -O3 进行编译)?

不。最好的方法是修复你的代码:

  • 在大多数情况下,您应该使用其他名称。

  • 在极少数情况下,您确实正在实现 C 库(如评论中所述),并且您确实想要重新实现memcpy,那么您应该使用特定于编译器的选项来实现这一目标。对于 GCC,请参见-fno-builtin*-ffreestanding,以及-nodefaultlibs-nostdlib

  • 如果这个答案至少尝试如何解决问题,那就太好了,因为目前,它没有提供任何有用的信息(除了告诉我修复代码或重命名函数)。是否有可能提出一个解决方案(除了“不要这样做”)来帮助我摆脱未定义的行为领域? (2认同)
  • @JL2210如果我现在理解你的问题,你向我们展示的函数不应该被称为“memcpy”,因为你感兴趣的是防止普通的循环副本被编译器重写为 memcpy 调用。但既然你将它命名为“memcpy”,很明显你的秘密意图“也”是用你自己的 memcpy 替换 memcpy,这带来了 Acorn 开始解决的新问题。 (2认同)
  • @JL2210 对于没有自己优化代码(即 asm 代码)的体系结构,单独保留原始内置 memcpy 可能是有意义的。另外,我认为编译器不会调用您自己的版本,除非您明确说明。您如何处理 ODR?它是否适用于编译器内置函数? (2认同)
  • @AndrewHenle好吧,如果OP合法地实现了`memcpy()`,他们应该告诉编译器禁用整个标准C库...... (2认同)