冯剑龙*_*冯剑龙 2 c gcc sse intrinsics
在sse内在函数中有两种实现累积的方法。但是其中之一得到了错误的结果。
#include <smmintrin.h>
int main(int argc, const char * argv[]) {
int32_t A[4] = {10, 20, 30, 40};
int32_t B[8] = {-1, 2, -3, -4, -5, -6, -7, -8};
int32_t C[4] = {0, 0, 0, 0};
int32_t D[4] = {0, 0, 0, 0};
__m128i lv = _mm_load_si128((__m128i *)A);
__m128i rv = _mm_load_si128((__m128i *)B);
// way 1 unexpected
rv += lv;
_mm_store_si128((__m128i *)C, rv);
// way 2 expected
rv = _mm_load_si128((__m128i *)B);
rv = _mm_add_epi32(lv, rv);
_mm_store_si128((__m128i *)D, rv);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
预期结果是:
9 22 27 36
C是:
9 23 27 37
D是:
9 22 27 36
在GNU C中,__m128i被定义为64位整数的向量,其中包含类似
typedef long long __m128i __attribute__((vector_size(16), may_alias));
Run Code Online (Sandbox Code Playgroud)
使用GNU C本机向量语法(+运算符)可对每个元素添加64位元素大小。即_mm_add_epi64。
在您的情况下,从一个32位元素的顶部进行进位会为其上方的32位元素添加一个额外的进位,因为64位元素的大小确实会在成对的32位元素之间传播进位。(将负数添加到非零目标会产生结转。)
英特尔内在API没有+为__m128/ __m128d/ 定义运算符__m128i。例如,您的代码将无法在MSVC上编译。
因此,您得到的行为仅来自GCC标头中内在类型的实现细节。对于具有明显元素大小的浮点向量很有用,但是对于整数向量,除非确实碰巧具有64位整数,否则您要定义自己的向量。
如果您想使用v1 += v2;它,可以定义自己的GNU C本机向量类型,例如
typedef uint32_t v4ui __attribute__((vector_size(16), aligned(4)));
Run Code Online (Sandbox Code Playgroud)
请注意,我省略了may_alias,因此仅将指针unsigned强制转换为是安全的,而不是像那样读取任意数据char[]。
实际上,GCC emmintrin.h(SSE2)确实定义了一堆类型:
/* SSE2 */
typedef double __v2df __attribute__ ((__vector_size__ (16)));
typedef long long __v2di __attribute__ ((__vector_size__ (16)));
typedef unsigned long long __v2du __attribute__ ((__vector_size__ (16)));
typedef int __v4si __attribute__ ((__vector_size__ (16)));
typedef unsigned int __v4su __attribute__ ((__vector_size__ (16)));
typedef short __v8hi __attribute__ ((__vector_size__ (16)));
typedef unsigned short __v8hu __attribute__ ((__vector_size__ (16)));
typedef char __v16qi __attribute__ ((__vector_size__ (16)));
typedef unsigned char __v16qu __attribute__ ((__vector_size__ (16)));
Run Code Online (Sandbox Code Playgroud)
我不确定它们是否打算供外部使用。
当您想让编译器发出有效的代码以除以编译时常量或类似的东西时,GNU C本机矢量最有用。例如digit = v1 % 10; ,v1 /= 10;带有16位无符号整数的将会编译为,pmulhuw然后向右移。但是它们对于可读代码也很方便。
有一些C ++包装器库可移植地提供运算符重载,并且具有Vec4i(4x有符号int)/ Vec4u(4x无符号int)/ Vec16c(16x有符号char)之类的类型,可以为您提供不同类型的整数向量的类型系统,因此您知道什么您来自v1 += v2;或v1 >>= 2; (右移是一种重要的情况。)
例如,Agner Fog的VCL(GPL许可)或DirectXMath(MIT许可)。