使用数组表示法访问SSE向量寄存器

时间:2015-10-18 03:39:13

标签: c gcc simd intrinsics

当我惊讶地发现以下情况时,我正在乱搞sse。我试图访问__m128中的各个浮点数。

__m128 x = _mm_set_ps(1.0, 2.0, 3.0, 4.0);
cout << x[1] << endl;

在这里,我可以读取它,就像它是一个数组。 x [0]打印4和x [1]打印三个,这是我期望的向后但这可能与字节顺序有关。我想知道的是这是一种标准/推荐的方式来读取甚至编写向量的各个组件。我在网上看到的所有例子似乎都在使用矢量类型和数组之间的联合。

2 个答案:

答案 0 :(得分:4)

不,这不合法,MSDN文档明确警告不要这样做。

在C ++中,您可以使用valarray。在C中,大多数编译器都支持GCC扩展__attribute__ ((vector_size(N)))

自Parallel Studio 2011以来的英特尔编译器,MSVC更长,但不是GCC(虽然GCC和Clang确实支持打字),你可以写:

__m128 result = foo();
float f1 = result.m128_f32[0];

这是否是一般的未定义行为,它可能在你可能关心的编译器上得到支持,并且未来的实现不太可能编译使用m128_f32但是默默地破坏它的代码。您可以使用其他内在函数(例如_mm_store_ss())来提取浮点数。

最便携的解决方案是memcpy()

答案 1 :(得分:4)

您提出两个问题:读取/写入x86 SIMD寄存器的顺序以及执行此操作的方法。

您可以像这样

设置大端格式的寄存器值
__m128 x = _mm_set_ps(4.0, 3.0, 2.0, 1.0)

或像这样的小端格式

__m128 x = _mm_setr_ps(1.0, 2.0, 3.0, 4.0) // r presumably means reverse

混乱可能来自阵列。我们以小端格式编写/存储数组,而不管硬件如何。我的意思是例如我们写

float xa[] = {1.0, 2.0, 3.0, 4.0};

(x86架构以little-endian格式存储每个float的字节,但这是一个单独的问题。)

因此,只有一种方法可以按顺序加载数组

__m128 x = _mm_loadu_ps(xa);

现在我们可以回答您的其他问题。如果要访问SSE寄存器的多个元素,最好的方法是将值存储到像这样的数组

float t[4]; _mm_storeu_ps(t, x);

由于它存储到一个数组,因此只有一个方法就像加载一样。

使用store intrinsics是我认为最好的解决方案,因为它不依赖于编译器特定的实现。这将适用于C和C ++中的GCC,ICC,Clang和MSVC。这是内在论的重点。它们为您提供类似于组件的功能,这些功能不依赖于某种编译器实现或汇编语法。

但是如果你只想要第一个元素使用_mm_cvtss_f32

This is also worth reading

更多关于enddianess。

如果我们以小端格式编写数字,可能会减少混淆。考虑比较用我们通常写数字的方式(big-endian风格)分别写成的整数:

54321
 4321
  321
   21
    1

我们最终通过输入适当的空格来正确地证明这些数字。如果我们使用了little-endian格式,那就是

12345
1234
123
1

这需要更少的工作来写,但我们可能会从右到左阅读数字。