我想通过使用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
重新定义为float
,我会从两个编译器(它们产生相同的代码)中得到这个:
scale:
mulps .LCPI0_0(%rip), %xmm0
ret
这些都是用$CC -O3 -S -msse test.c
编译的。
更新:我突然想到,简单的解决方案就是使用LLVM创建一个从结构转换为向量的蹦床,反之亦然。通过这种方式,互操作性问题被简化为按值传递的结构,其中被ABI确定为;向量仅存在于LLVM-land中。这意味着我只能在LLVM中使用SIMD,但我可以忍受它。
但是,我仍然想知道上述答案;矢量很棒,我希望能够更多地使用它们。
更新更新:原来C传递结构的方式是疯狂的......呃,疯了!指针传递struct { double x, y, z; }
; struct { float x, y, z }
作为%xmm寄存器的对传递:x
和y
打包到第一个,z
位于第二...
简单而不受欢迎它不是!