C/SIMD实现立即数数组:解决_mm_shuffle_ps参数需8位立即数报错
错误原因
_mm_shuffle_ps 属于SSE基础内置函数,其第三个参数要求是编译阶段即可确定的8位立即数常量。你代码中传入的LUT[shufctr]是运行时通过mask计算得到的变量,不符合语法约束,因此抛出编译错误。
解决方案
根据你能支持的指令集等级,可以选择以下三种实现方案:
方案1:纯SSE兼容实现(兼容性最好,仅需SSE1指令集支持)
由于_mm_movemask_ps返回的是4位整数,取值范围固定为0~15共16种可能,可以通过switch case枚举所有情况,每个分支内直接传入常量作为shuffle的控制参数,满足编译要求:
#include <immintrin.h> static const unsigned char LUT[16] = { 0xE4, 0x24, 0x34, 0x04, 0x38, 0x08, 0x0C, 0x00, 0x39, 0x09, 0x0D, 0x01, 0x0E, 0x02, 0x03, 0x00 }; int main( ) { float input[4] = { -1.0f, 2.0f, 3.0f, -4.0f }; float output[4] = {0}; __m128 data = _mm_loadu_ps( input ); __m128 mmask = _mm_cmpge_ps( data, _mm_setzero_ps( ) ); int shufctr = _mm_movemask_ps( mmask ); __m128 res; switch(shufctr) { case 0: res = _mm_shuffle_ps(data, data, LUT[0]); break; case 1: res = _mm_shuffle_ps(data, data, LUT[1]); break; case 2: res = _mm_shuffle_ps(data, data, LUT[2]); break; case 3: res = _mm_shuffle_ps(data, data, LUT[3]); break; case 4: res = _mm_shuffle_ps(data, data, LUT[4]); break; case 5: res = _mm_shuffle_ps(data, data, LUT[5]); break; case 6: res = _mm_shuffle_ps(data, data, LUT[6]); break; case 7: res = _mm_shuffle_ps(data, data, LUT[7]); break; case 8: res = _mm_shuffle_ps(data, data, LUT[8]); break; case 9: res = _mm_shuffle_ps(data, data, LUT[9]); break; case 10: res = _mm_shuffle_ps(data, data, LUT[10]); break; case 11: res = _mm_shuffle_ps(data, data, LUT[11]); break; case 12: res = _mm_shuffle_ps(data, data, LUT[12]); break; case 13: res = _mm_shuffle_ps(data, data, LUT[13]); break; case 14: res = _mm_shuffle_ps(data, data, LUT[14]); break; case 15: res = _mm_shuffle_ps(data, data, LUT[15]); break; default: res = _mm_setzero_ps(); } _mm_storeu_ps( output, res ); }
现代编译器会自动优化这个switch分支为跳转表,运行效率几乎没有损失。
方案2:AVX2兼容实现(代码更简洁,需AVX2指令集支持)
AVX2提供了支持运行时控制参数的向量置换函数_mm_permutevar_ps,不需要编译期常量,你可以直接将你的LUT控制值转换为对应的置换向量使用,保留查表逻辑即可:
#include <immintrin.h> // 置换控制向量LUT,每个4元组对应一个shufctr取值的置换索引 static const int SHUF_LUT[16][4] = { {3,2,1,0}, {3,2,0,1}, {3,1,2,0}, {3,0,1,2}, {2,3,1,0}, {2,3,0,1}, {2,1,3,0}, {2,0,3,1}, {1,3,2,0}, {1,3,0,2}, {1,2,3,0}, {1,0,3,2}, {0,3,2,1}, {0,3,1,2}, {0,2,3,1}, {0,1,2,3} // 可根据你的实际打包需求调整上述索引值 }; int main( ) { float input[4] = { -1.0f, 2.0f, 3.0f, -4.0f }; float output[4] = {0}; __m128 data = _mm_loadu_ps( input ); __m128 mmask = _mm_cmpge_ps( data, _mm_setzero_ps( ) ); int shufctr = _mm_movemask_ps( mmask ); __m128i shuf_ctrl = _mm_loadu_si128((const __m128i*)SHUF_LUT[shufctr]); __m128 res = _mm_permutevar_ps(data, shuf_ctrl); _mm_storeu_ps( output, res ); }
方案3:AVX512F实现(最简实现,需AVX512F指令集支持)
AVX512提供了原生的元素压缩指令_mm_mask_compress_ps,可以直接将满足mask条件的元素左对齐打包,不需要自己实现shuffle逻辑和LUT表:
#include <immintrin.h> int main( ) { float input[4] = { -1.0f, 2.0f, 3.0f, -4.0f }; float output[4] = {0}; __m128 data = _mm_loadu_ps( input ); __mmask8 mmask = _mm_cmpge_ps_mask(data, _mm_setzero_ps(), _CMP_GE_OQ); __m128 res = _mm_mask_compress_ps(_mm_setzero_ps(), mmask, data); _mm_storeu_ps( output, res ); }
内容的提问来源于stack exchange,提问作者Timothy s
相关产品推荐
相关产品推荐

