代码之家  ›  专栏  ›  技术社区  ›  S.S. Anne

如何阻止gcc优化这个逐字节复制到memcpy调用中?

  •  5
  • S.S. Anne  · 技术社区  · 5 年前

    我有这个密码 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    $1, %rcx
            cmpq    %rdx, %rcx
            jne     .L3
    .L2:
            ret
    .LFE0:
    

    但是,在 gcc -O3 ,gcc优化这个简单的逐字节复制到 曼皮西 呼叫:

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

    由于明显的原因,这是次优的,并且会导致一个segfault。

    以下是复制segfault的示例(编译时使用 gcc -O3 -fno-builtin-memcpy test.c memcpy.c ) test.c :

    #include <string.h>
    #include <stdio.h>
    
    int main(void)
    {
            int c, a[] = {1, 2, 3, 4}, b[4];
            memcpy(b, a, sizeof(b));
            for( c = 0; c < sizeof(b)/sizeof(b[0]); c++ )
                    printf("%d\n", b[c]);
            return 0;
    }
    

    我试过传球 -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) 
    

    如何禁用导致副本转换为 曼皮西 调用(最好在编译时使用 -O3 ?

    2 回复  |  直到 5 年前
        1
  •  5
  •   Antti Haapala -- Слава Україні    5 年前

    这里有一件事似乎足够了:不要使用 -fno-builtin-memcpy 使用 -fno-builtin 用于编译的翻译单元 memcpy 独自!

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

    或者,因为您不能依赖C库中的任何内容,所以可能使用 -ffreestanding 可能是正常的。

        2
  •  -1
  •   Acorn    5 年前

    由于明显的原因,这是次优的。

    我看不出明显的原因。这个 memcpy 对于任何非常重要的大小,实现都可能比逐字节复制快得多。

    它会导致一个断层。

    没有。很可能是代码中的一个错误调用了 曼皮西 -喜欢。

    如果您指的是递归,请注意 redefining memcpy is undefined behavior . 用另一个名字。

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

    不要,你必须修改你的代码。