如何阻止 GCC 将此逐字节复制优化为 memcpy 调用?

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

我有 memcpy 的代码作为我实现标准 C 库的一部分,它将内存从 src 复制到 dest一次一个字节:

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;
}

加上gcc -O2,生成的代码是合理的:

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

但是,在 gcc -O3,GCC 将这个简单的逐字节复制优化为 memcpy 调用:

memcpy:
.LFB0:
        testq   %rdx, %rdx
        je      .L7
        subq    , %rsp
        call    memcpy
        addq    , %rsp
        ret
.L7:
        movq    %rdi, %rax
        ret
.LFE0:

这行不通(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) 

如何禁用导致副本转换为 memcpy 调用的优化?

This won't work (memcpy unconditionally calls itself), and it causes a segfault.

.

How do I disable the optimization that causes the copy to be transformed into a memcpy call (preferably while still compiling with -O3)?

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

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

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

这里有一件事似乎就足够了:而不是使用 -fno-builtin-memcpy use -fno-builtin 单独编译 memcpy 的翻译单元!

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

或者,由于您不能依赖 C 库中的任何内容,也许可以使用 -ffreestanding