数组初始化优化

Cor*_*mer 33 c++ x86-64 abi calling-convention compiler-optimization

编译以下代码片段时(clang x86-64 -O3

std::array<int, 5> test()
{
    std::array<int, 5> values {{0, 1, 2, 3, 4}};
    return values;
}
Run Code Online (Sandbox Code Playgroud)

它产生了我期望的典型装配

test():                               # @test()
        mov     rax, rdi
        mov     ecx, dword ptr [rip + .L__const.test().values+16]
        mov     dword ptr [rdi + 16], ecx
        movups  xmm0, xmmword ptr [rip + .L__const.test().values]
        movups  xmmword ptr [rdi], xmm0
        ret
.L__const.test().values:
        .long   0                       # 0x0
        .long   1                       # 0x1
        .long   2                       # 0x2
        .long   3                       # 0x3
        .long   4                       # 0x4
Run Code Online (Sandbox Code Playgroud)

但是对于小型阵列,似乎已经找到了窍门?

std::array<int, 3> test()
{
    std::array<int, 3> values {{0, 1, 2}};
    return values;
}
Run Code Online (Sandbox Code Playgroud)

是相应的程序集

test():                               # @test()
        movabs  rax, 4294967296
        mov     edx, 2
        ret
Run Code Online (Sandbox Code Playgroud)

那个魔术数字(4294967296)从哪里来?这本质上是可以以某种方式reinterpret_cast返回到数组的值int吗?

Mar*_*ica 40

A std::array<int, 3>在您的实现中为96位宽。因此,ABI声明应以RAX + RDX的低32位(也称为EDX)返回它。

4294967296是2 32,十六进制是$1'0000'0000。因此,movabs将0存储在RAX的低阶32位中,并将1存储在RAX的高阶位中。将mov在EDX店2(这是你想要什么)。

  • 有趣的事实:使用BMI2进行编译的最佳方法可能是“ mov edx,2”,然后是[`rorx rax,rdx,33`](https://www.felixcloutier.com/x86/RORX.html)从`1 &lt;&lt; 1`创建`1 &lt;&lt; 32`。“ movabs”太大(10个字节),因此解码速度很慢,并且会在uop缓存中占用额外的空间。rorx是6个字节(3个字节的VEX +操作码+ modrm + imm8),但是它的立即数很小。它确实依赖于`mov`,并且只能在Intel CPU的shift端口上运行,但通常不是瓶颈(特别是在导致`ret'的代码路径中,而不是紧密循环中包含) ) (3认同)