如何在无AVX2时用字节位设置YMM寄存器双字?(vmovmskps逆操作)
实现
_mm256_movemask_ps的逆操作(AVX1兼容) 这个需求我之前也碰到过——把vmovmskps/_mm256_movemask_ps输出的位图转成对应的YMM掩码向量,让对应位为1的双字全置1,还要兼容Sandy Bridge(也就是只能用AVX1,不能碰AVX2)。确实AVX1没有直接的逆操作指令,但我们可以用几个指令组合来实现,思路其实很清晰:
核心思路
我们需要把输入掩码的每一位,映射到YMM寄存器的对应32位元素:如果掩码的第i位是1,就让第i个32位元素变成0xFFFFFFFF,否则保持0x00000000。具体步骤如下:
- 将8位掩码零扩展到32位,然后广播到YMM寄存器的所有8个元素。
- 用一个预定义的位掩码向量(每个元素对应掩码的一位,比如第0个元素是
0x01,第1个是0x02,以此类推)和广播后的掩码做按位与,提取出每个元素对应的位值。 - 将提取结果和零向量比较,大于0的元素会被置为全1,否则为0——这就是我们要的掩码向量。
汇编实现(Intel语法)
; 输入:al = 从vmovmskps得到的8位掩码 ; 输出:ymm0 = 对应的全1/全0掩码向量 movzx eax, al ; 把8位掩码零扩展到32位 vbroadcastss ymm1, eax ; 将掩码广播到ymm1的所有8个32位元素 ; 预定义位掩码向量:ymm2 = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80](每个元素都是32位值) vpand ymm1, ymm1, ymm2 ; 提取每个元素对应的位值 vpxor ymm0, ymm0, ymm0 ; 生成零向量 vpcmpgtd ymm0, ymm1, ymm0 ; 比较:如果元素>0则置为0xFFFFFFFF,否则0
C语言 Intrinsics 实现
#include <immintrin.h> __m256i movemask_to_ymm_mask(unsigned char mask) { // 预定义位掩码向量,每个元素对应掩码的一位 const __m256i bit_mask = _mm256_setr_epi32(0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80); // 将8位掩码零扩展到32位,再广播到YMM的所有元素 const unsigned int mask_32 = mask; __m256i broadcast_mask = _mm256_broadcast_ss((const __m128*)&mask_32); // 提取每个元素对应的位值 __m256i masked_bits = _mm256_and_si256(broadcast_mask, bit_mask); // 比较非零,生成全1/全0掩码 return _mm256_cmpgt_epi32(masked_bits, _mm256_setzero_si256()); }
兼容性说明
这套方案完全兼容Sandy Bridge:
vbroadcastss、vpand、vpcmpgtd都是AVX1指令,Sandy Bridge原生支持。- 用到的所有intrinsics也都是AVX1和SSE2标准的,不需要AVX2的指令集支持。
内容的提问来源于stack exchange,提问作者poby
相关产品推荐
相关产品推荐

