有一个一般性问题在 SSE 和 AVX512 寄存器之间移动数据 但这个问题是关于 C 内在函数的。
有一个intrinsic可以把两个xmm寄存器插入一个ymm:__m256 _mm256_set_m128(__m128 hi, __m128 lo)对应vinsertf128指令,但是没有类似的intrinsic可以把两个ymm寄存器插入一个zmm寄存器,例如假设的 __m512 _mm512_set_m256 (__m256 hi, __m256 lo)。使用汇编指令,当我设置 ymm 寄存器(即通过 vinsertf128)时,该操作还明确清除相应 zmm 寄存器的高 256 位,但是 C 将 ymm 类型转换为 zmm 的类似内在是什么? vinsertf32x8 指令的所有内在函数 已经需要一个 512 位 zmm 寄存器输入,而 _mm256_set_m128 只返回一个 256 位 ymm 寄存器。
从一个 zmm 寄存器中提取四个 xmm 寄存器的 C 内在函数是什么,反之亦然?我无法使用定义的寄存器创建汇编函数,我需要指令与任何可用的寄存器内联。
提取,使用
/* floating point domain */
__m128 _mm512_extractf32x4_ps (__m512 a, int imm8);
__m256 _mm512_extractf32x8_ps (__m512 a, int imm8);
/* integer domain */
__m128i _mm512_extracti64x2_epi64 (__m512i a, int imm8);
__m256i _mm512_extracti64x4_epi64 (__m512i a, int imm8);
要组装,要么遍历内存(例如,存储到 4
__m128
的数组中,然后从中加载),要么使用一系列插入指令。请注意,插入是跨通道操作,因此非常慢。通过内存可能会更快,你应该测量它。
/* floating point domain */
__m512 _mm512_insertf32x4 (__m512 a, __m128 b, int imm8);
__m512 _mm512_insertf32x8 (__m512 a, __m256 b, int imm8);
/* integer domain */
__m256i _mm256_inserti64x2 (__m256i a, __m128i b, int imm8);
__m512i _mm512_inserti64x4 (__m512i a, __m256i b, int imm8);
我们可以使用以下内在函数进行转换,它们不会产生任何指令:
_mm256_castps128_ps256 __m128 → __m256
_mm256_castps256_ps128 __m256 → __m128
_mm512_castps256_ps512 __m256 → __m512
_mm512_castps512_ps256 __m512 → __m256
不同类型有相似的内在函数:
_mm256_castsi256_si128 __m256i → __m128i
_mm256_castsi128_si256 __m128i → __m256i
此外,如果我们需要在相同大小的各种类型之间进行转换,我们可以使用以下类型转换内在函数:
_mm256_castps_si256 __m256 → __m256i
_mm256_castsi256_ps __m256i → __m256
所有演员说明都列在https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=6371&cats=Cast
因此,将4个xmm寄存器组合成1个zmm寄存器的代码如下:
__m512 m512_combine_m128x4(__m128 x4, __m128 x3, __m128 x2, __m128 x1)
{
const __m256 h = _mm256_set_m128(x4, x3);
const __m256 l = _mm256_set_m128(x2, x1);
return _mm512_insertf32x8(_mm512_castps256_ps512(l), h, 1);
}
翻译成两条vinsertf128指令和一条vinsertf32x8指令
因此,一旦我们使用 cast intrinsics,这就太容易了。 split 的代码是相似的,使用上面提到的转换。