如何帮助不同的C(++)编译器生成干净的SIMD代码?

时间:2019-07-13 23:23:33

标签: c compiler-optimization simd llvm-clang

我一直在尝试使用不同的编译器,以查看它们在没有难看的内在函数或手写asm的情况下可用于编写有效的SIMD代码(aarch64和x64)的程度。到目前为止,只有Clang似乎对aarch64做得正确。我想我可能缺少一些编译器提示或编译标志。

我尝试使用__restrict关键字,并且在GCC和Clang上使用了以下编译标志:

-force-vector-width=8 -ffast-math -ftree-vectorize

代码如下:

typedef union
{
    float  coords[3];
    struct { float x; float y; float z; };

} vec3; // simple 3D vector

vec3 AddVectors(vec3 a, vec3 b)
{
    vec3 x = { { a.x + b.x, a.y + b.y, a.z + b.z } };

    return x;
}

void Add4Vectors(vec3* __restrict a, vec3* __restrict b)
{
    a[0] = AddVectors(a[0], b[0]);
    a[1] = AddVectors(a[1], b[1]);
    a[2] = AddVectors(a[2], b[2]);
    a[3] = AddVectors(a[3], b[3]);
}

void Add16Vectors(vec3* __restrict a, vec3* __restrict b)
{
    Add4Vectors(a, b);
    Add4Vectors(a + 4, b + 4);
    Add4Vectors(a + 8, b + 8);
    Add4Vectors(a + 12, b + 12);
}

void Add64Vectors(vec3* __restrict a, vec3* __restrict b)
{
    Add16Vectors(a, b);
    Add16Vectors(a + 16, b + 16);
    Add16Vectors(a + 32, b + 32);
    Add16Vectors(a + 48, b + 48);
}

您可以在GodBolt.org上玩 https://godbolt.org/z/SW3aH-

这是我从c 8中获得的结果(标志:-O3 -target aarch64)

AddVectors(vec3, vec3):                  // @AddVectors(vec3, vec3)
        fadd    s0, s0, s3
        fadd    s1, s1, s4
        fadd    s2, s2, s5
        ret
Add4Vectors(vec3*, vec3*):               // @Add4Vectors(vec3*, vec3*)
        ldp     q0, q1, [x0]
        ldp     q2, q3, [x1]
        ldr     q4, [x0, #32]
        ldr     q5, [x1, #32]
        fadd    v0.4s, v0.4s, v2.4s
        fadd    v1.4s, v1.4s, v3.4s
        fadd    v2.4s, v4.4s, v5.4s
        stp     q0, q1, [x0]
        str     q2, [x0, #32]
        ret
Add16Vectors(vec3*, vec3*):              // @Add16Vectors(vec3*, vec3*)
        ldp     q0, q1, [x0]
        ldp     q2, q3, [x1]
        ldp     q4, q5, [x0, #32]
        ldp     q6, q7, [x1, #32]
        ldp     q16, q17, [x0, #64]
        ldp     q18, q19, [x1, #64]
        ldp     q20, q21, [x0, #96]
        ldp     q22, q23, [x1, #96]
        fadd    v0.4s, v0.4s, v2.4s
        fadd    v1.4s, v1.4s, v3.4s
        ldp     q2, q3, [x0, #128]
        fadd    v4.4s, v4.4s, v6.4s
        fadd    v5.4s, v5.4s, v7.4s
        ldp     q6, q7, [x1, #128]
        fadd    v16.4s, v16.4s, v18.4s
        fadd    v17.4s, v17.4s, v19.4s
        ldp     q18, q19, [x0, #160]
        fadd    v20.4s, v20.4s, v22.4s
        fadd    v21.4s, v21.4s, v23.4s
        ldp     q22, q23, [x1, #160]
        fadd    v2.4s, v2.4s, v6.4s
        fadd    v3.4s, v3.4s, v7.4s
        stp     q0, q1, [x0]
        fadd    v6.4s, v18.4s, v22.4s
        fadd    v7.4s, v19.4s, v23.4s
        stp     q4, q5, [x0, #32]
        stp     q16, q17, [x0, #64]
        stp     q20, q21, [x0, #96]
        stp     q2, q3, [x0, #128]
        stp     q6, q7, [x0, #160]
        ret
Add64Vectors(vec3*, vec3*):              // @Add64Vectors(vec3*, vec3*)
        stp     x20, x19, [sp, #-32]!   // 8-byte Folded Spill
        stp     x29, x30, [sp, #16]     // 8-byte Folded Spill
        add     x29, sp, #16            // =16
        mov     x19, x1
        mov     x20, x0
        bl      Add16Vectors(vec3*, vec3*)
        add     x0, x20, #192           // =192
        add     x1, x19, #192           // =192
        bl      Add16Vectors(vec3*, vec3*)
        add     x0, x20, #384           // =384
        add     x1, x19, #384           // =384
        bl      Add16Vectors(vec3*, vec3*)
        ldp     x29, x30, [sp, #16]     // 8-byte Folded Reload
        add     x0, x20, #576           // =576
        add     x1, x19, #576           // =576
        ldp     x20, x19, [sp], #32     // 8-byte Folded Reload
        b       Add16Vectors(vec3*, vec3*)

如果您问我,这很简洁,尤其是Add16Vectors例程,我认为我不会写 手工制作的更好的版本。

但是,仍然有一些东西用Add64Vectors例程困扰着我。 似乎不需要将寄存器保存在堆栈中。 要避免这种情况,编译器似乎一点也不复杂,因为有很多可用的寄存器。

这是我的写法: 我想念什么吗?

Add64Vectors(vec3*, vec3*):             
        mov     x3, x1
        mov     x2, x0
        bl      Add16Vectors(vec3*, vec3*)
        add     x0, x2, #192           
        add     x1, x3, #192           
        bl      Add16Vectors(vec3*, vec3*)
        add     x0, x2, #384           
        add     x1, x3, #384           
        bl      Add16Vectors(vec3*, vec3*)
        add     x0, x2, #576           
        add     x1, x3, #576           
        b       Add16Vectors(vec3*, vec3*)

现在,我对此有点挑剔,但这是丑陋的部分。

我不想在这里发布太长的消息,因此请尝试在您这边查看结果。 (在https://godbolt.org上) 但是,我认为Clang产生的aarch64版本是唯一认为编译器做得很好的地方。

用于ARM64的GCC(8.2)在同一例程中使用的指令大约是用于ARM64的MSVC的两倍。

但这也许是因为目标不如x64主流吗? 不。

x64的结果令人震惊,即使Clang似乎也是赢家,我也不明白为什么x64版本是两倍长 作为aarch64版本...

我正在考虑对齐问题或缺少编译标志?

0 个答案:

没有答案