我可以将 returns __m128i 的内在结果分配给类型 __m128i_u 的变量吗?
can I assign the result of intrinsic that returns __m128i to variable of the type__m128i_u?
如标题所示-我想做如下:
__m128i_u* avxVar = (__m128i_u*)Var; // Var allocated with alloc
*avxVar = _mm_set_epi64(...); // is that ok to assign __m128i to __m128i_u ?
是的,但请注意 __m128i_u
不可移植(例如到 MSVC); GCC/clang 在内部使用它来实现未对齐的 loadu/storeu 内在函数。 完全 等同于以正常方式进行:
_mm_storeu_si128((__m128i*)Var, vec);
(其中 vec
是任何 __m128i
。例如,它可以是 _mm_set_epi64x
或一个变量。)
GCC 11 的 emmintrin.h
实现 _mm_storeu_si128
是这样定义的,采用 __m128i_u*
指针 arg,因此取消引用执行未对齐访问(如果未优化)。
// GCC internals, quoted for reference only.
// Just use #include <immintrin.h> in your own code
extern __inline void __attribute__((__gnu_inline__, __always_inline__, __artificial__))
_mm_storeu_si128 (__m128i_u *__P, __m128i __B)
{
*__P = __B;
}
所以是的,GCC 的 headers 依赖于 __m128i*
和 __m128i_u*
的兼容和隐式转换。
正如 _mm_storeu_si128
是 movdqu
的固有函数一样,__m128i_u*
取消引用也是如此。但实际上这些内在函数的存在只是为了向编译器传达对齐信息,由编译器决定何时实际加载和存储,就像 char*
.
的 deref 一样
(有趣的事实:__m128i*
是一个 may_alias
类型,就像 char*
,所以你可以将它指向任何东西而不会违反 strict-aliasing。Is `reinterpret_cast`ing between hardware SIMD vector pointer and the corresponding type an undefined behavior? )
另请注意,_mm_set_epi64
采用 __m64
参数:它用于从两个 MMX 向量而非标量 int64_t
构建 SSE2 向量。你可能想要 _mm_set_epi64x
它们的编译方式相同
void foo(void *Var) {
__m128i_u* avxVar = (__m128i_u*)Var;
*avxVar = _mm_set_epi64x(1, 2);
}
void bar(void *Var) {
_mm_storeu_si128((__m128i*)Var, _mm_set_epi64x(1, 2) );
}
两个函数在 gcc/clang/MSVC 中的编译方式相同(并且在语义上是等效的,因此内联后将始终相同)。
但是只有第二个可以用 MSVC 编译,正如您在 Godbolt 编译器资源管理器上看到的那样: https://godbolt.org/z/Y8Wq96Pqs 。如果禁用 #ifdef __GNUC__
,则会在 MSVC 上出现编译器错误。
## GCC -O3
foo:
movdqa xmm0, XMMWORD PTR .LC0[rip]
movups XMMWORD PTR [rdi], xmm0
ret
bar:
movdqa xmm0, XMMWORD PTR .LC0[rip]
movups XMMWORD PTR [rdi], xmm0
ret
.LC0:
.quad 2
.quad 1
随着周围代码的复杂化,_mm_loadu_si128
可以折叠到仅使用 AVX 的 ALU 的内存源操作数(例如 vpaddb xmm0, xmm1, [rdi]
,但 _mm_load_si128
对齐的加载可以折叠到 SSE 内存源像 paddb xmm0, [rdi]
.
如标题所示-我想做如下:
__m128i_u* avxVar = (__m128i_u*)Var; // Var allocated with alloc
*avxVar = _mm_set_epi64(...); // is that ok to assign __m128i to __m128i_u ?
是的,但请注意 __m128i_u
不可移植(例如到 MSVC); GCC/clang 在内部使用它来实现未对齐的 loadu/storeu 内在函数。 完全 等同于以正常方式进行:
_mm_storeu_si128((__m128i*)Var, vec);
(其中 vec
是任何 __m128i
。例如,它可以是 _mm_set_epi64x
或一个变量。)
GCC 11 的 emmintrin.h
实现 _mm_storeu_si128
是这样定义的,采用 __m128i_u*
指针 arg,因此取消引用执行未对齐访问(如果未优化)。
// GCC internals, quoted for reference only.
// Just use #include <immintrin.h> in your own code
extern __inline void __attribute__((__gnu_inline__, __always_inline__, __artificial__))
_mm_storeu_si128 (__m128i_u *__P, __m128i __B)
{
*__P = __B;
}
所以是的,GCC 的 headers 依赖于 __m128i*
和 __m128i_u*
的兼容和隐式转换。
正如 _mm_storeu_si128
是 movdqu
的固有函数一样,__m128i_u*
取消引用也是如此。但实际上这些内在函数的存在只是为了向编译器传达对齐信息,由编译器决定何时实际加载和存储,就像 char*
.
(有趣的事实:__m128i*
是一个 may_alias
类型,就像 char*
,所以你可以将它指向任何东西而不会违反 strict-aliasing。Is `reinterpret_cast`ing between hardware SIMD vector pointer and the corresponding type an undefined behavior? )
另请注意,_mm_set_epi64
采用 __m64
参数:它用于从两个 MMX 向量而非标量 int64_t
构建 SSE2 向量。你可能想要 _mm_set_epi64x
它们的编译方式相同
void foo(void *Var) {
__m128i_u* avxVar = (__m128i_u*)Var;
*avxVar = _mm_set_epi64x(1, 2);
}
void bar(void *Var) {
_mm_storeu_si128((__m128i*)Var, _mm_set_epi64x(1, 2) );
}
两个函数在 gcc/clang/MSVC 中的编译方式相同(并且在语义上是等效的,因此内联后将始终相同)。
但是只有第二个可以用 MSVC 编译,正如您在 Godbolt 编译器资源管理器上看到的那样: https://godbolt.org/z/Y8Wq96Pqs 。如果禁用 #ifdef __GNUC__
,则会在 MSVC 上出现编译器错误。
## GCC -O3
foo:
movdqa xmm0, XMMWORD PTR .LC0[rip]
movups XMMWORD PTR [rdi], xmm0
ret
bar:
movdqa xmm0, XMMWORD PTR .LC0[rip]
movups XMMWORD PTR [rdi], xmm0
ret
.LC0:
.quad 2
.quad 1
随着周围代码的复杂化,_mm_loadu_si128
可以折叠到仅使用 AVX 的 ALU 的内存源操作数(例如 vpaddb xmm0, xmm1, [rdi]
,但 _mm_load_si128
对齐的加载可以折叠到 SSE 内存源像 paddb xmm0, [rdi]
.