在编译为C ++与C

时间:2016-12-22 23:11:02

标签: c++ c gcc assembly x86-64

我一直在玩x86-64程序集试图了解更多有关可用的各种SIMD扩展(MMX,SSE,AVX)。

为了了解GCC如何将不同的C或C ++结构转换为机器代码,我一直在使用Compiler Explorer这是一个极好的工具。

在我的一场比赛期间'我想看看GCC如何优化整数数组的简单运行时初始化。在这种情况下,我试图将数字0到2047写入2048个无符号整数的数组。

代码如下:

unsigned int buffer[2048];

void setup()
{
  for (unsigned int i = 0; i < 2048; ++i)
  {
    buffer[i] = i;
  }
}

如果我启用优化和AVX-512指令-O3 -mavx512f -mtune=intel GCC 6.3生成一些非常聪明的代码:)

setup():
        mov     eax, OFFSET FLAT:buffer
        mov     edx, OFFSET FLAT:buffer+8192
        vmovdqa64       zmm0, ZMMWORD PTR .LC0[rip]
        vmovdqa64       zmm1, ZMMWORD PTR .LC1[rip]
.L2:
        vmovdqa64       ZMMWORD PTR [rax], zmm0
        add     rax, 64
        cmp     rdx, rax
        vpaddd  zmm0, zmm0, zmm1
        jne     .L2
        ret
buffer:
        .zero   8192
.LC0:
        .long   0
        .long   1
        .long   2
        .long   3
        .long   4
        .long   5
        .long   6
        .long   7
        .long   8
        .long   9
        .long   10
        .long   11
        .long   12
        .long   13
        .long   14
        .long   15
.LC1:
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16

然而,当我测试通过添加标志-x c使用GCC C编译器编译相同代码时会产生什么,我真的很惊讶。

我期望类似的,如果不相同的结果,但C编译器似乎生成更复杂,并且可能也是更慢的机器代码。生成的程序集太大而无法完全粘贴到此处,但可以通过this链接在godbolt.org上查看。

生成的代码的片段,第58到83行,如下所示:

.L2:
        vpbroadcastd    zmm0, r8d
        lea     rsi, buffer[0+rcx*4]
        vmovdqa64       zmm1, ZMMWORD PTR .LC1[rip]
        vpaddd  zmm0, zmm0, ZMMWORD PTR .LC0[rip]
        xor     ecx, ecx
.L4:
        add     ecx, 1
        add     rsi, 64
        vmovdqa64       ZMMWORD PTR [rsi-64], zmm0
        cmp     ecx, edi
        vpaddd  zmm0, zmm0, zmm1
        jb      .L4
        sub     edx, r10d
        cmp     r9d, r10d
        lea     eax, [r8+r10]
        je      .L1
        mov     ecx, eax
        cmp     edx, 1
        mov     DWORD PTR buffer[0+rcx*4], eax
        lea     ecx, [rax+1]
        je      .L1
        mov     esi, ecx
        cmp     edx, 2
        mov     DWORD PTR buffer[0+rsi*4], ecx
        lea     ecx, [rax+2]

正如您所看到的,此代码具有许多复杂的移动和跳转,并且通常感觉像执行简单数组初始化的非常复杂的方式。

为什么生成的代码会有这么大的差异?

与C编译器相比,GCC C ++编译器在优化C和C ++中有效的代码方面是否更好?

1 个答案:

答案 0 :(得分:39)

额外的代码用于处理未对齐,因为使用的指令vmovdqa64需要64字节对齐。

我的测试显示,即使标准没有,gcc确实允许另一个模块中的定义在C模式下覆盖此处的定义。该定义可能仅符合基本对齐要求(4个字节),因此编译器不能依赖更大的对齐。从技术上讲,gcc为此暂定定义发出.comm汇编指令,而外部定义在.data部分使用正常符号。在链接期间,此符号优先于.comm符号。

请注意,如果您将程序更改为使用extern unsigned int buffer[2048];,那么即使是C ++版本也会添加代码。相反,将其设为static unsigned int buffer[2048];会将C版本转换为优化版本。

相关问题