4

我想通过使用 LLVM 的 JIT 动态生成代码来加速我正在处理的程序。该算法可以对向量进行操作,我宁愿使用 LLVM 中的 SIMD 向量扩展来执行此操作(它不仅使某些操作更快,而且实际上使代码生成更简单)。

我是否有机会以合理的便携方式完成这项工作?

在 C 方面,我将使用 gcc、clang 或者可能是 icc 进行编译。float x 4我的向量会很简单double x 4。这个世界上非平台特定向量操作的事实标准似乎是 gcc 向量扩展:

typedef double Vector4 __attribute__ ((vector_size (sizeof(double)*4)));

检查生成的代码表明,clang 将double x 4在寄存器中传递一个向量,而 gcc 则希望它在堆栈上 --- 这很糟糕。(它们都float x 4在寄存器中传递向量。)

我的理解是这两个系统应该是 ABI 兼容的,但显然向量不计算在内。我真的可以这样做吗?

我的示例程序是::

typedef double real;
typedef real Vector4 __attribute__ ((vector_size (sizeof(real)*4)));

Vector4 scale(Vector4 a)
{
    Vector4 s = {2, 2, 2, 2};
    return a*s;
}

这与 LLVM 一起编译为:

scale:
    movapd  .LCPI0_0(%rip), %xmm2
    mulpd   %xmm2, %xmm0
    mulpd   %xmm2, %xmm1
    ret

...但是 gcc 产生了这种恐怖:

scale:
    subq    $64, %rsp
    movq    %rdi, %rax
    movsd   .LC0(%rip), %xmm0
    movapd  72(%rsp), %xmm1
    movsd   %xmm0, -56(%rsp)
    movsd   %xmm0, -48(%rsp)
    movsd   %xmm0, -72(%rsp)
    movsd   %xmm0, -64(%rsp)
    mulpd   -56(%rsp), %xmm1
    movapd  88(%rsp), %xmm0
    mulpd   -72(%rsp), %xmm0
    movapd  %xmm1, -104(%rsp)
    movq    -104(%rsp), %rdx
    movapd  %xmm1, -24(%rsp)
    movapd  %xmm0, -8(%rsp)
    movq    %rdx, (%rdi)
    movq    -16(%rsp), %rdx
    movq    %rdx, 8(%rdi)
    movq    -8(%rsp), %rdx
    movq    %rdx, 16(%rdi)
    movq    (%rsp), %rdx
    movq    %rdx, 24(%rdi)
    addq    $64, %rsp
    ret

如果我重新定义real为 a float,我会从两个编译器中得到这个(它们产生相同的代码):

scale:
    mulps   .LCPI0_0(%rip), %xmm0
    ret

这些都是用$CC -O3 -S -msse test.c.

更新:我突然想到,简单的解决方案就是使用 LLVM 创建一个蹦床,将结构转换为向量,反之亦然。这样,互操作性问题就被简化为由 ABI 确定的按值传递结构;这些向量仅存在于 LLVM-land 中。这意味着我只能在 LLVM 中使用 SIMD 的东西,但我可以忍受。

但是,我仍然想知道上述问题的答案;矢量很棒,我希望能够更多地使用它们。

更新更新:事实证明,C 通过值传递结构的方式非常疯狂......呃,疯狂!Astruct { double x, y, z; }通过指针传递;a作为一%xmm 寄存器struct { float x, y, z }传递:并被打包到第一个中,而is 在第二个中...xyz

简单而无痛不是!

4

0 回答 0