x86_64 Linux使用avx指令(补充)
x86_64 Linux使用avx指令(补充)
一些必要的函数的补充,主要包含fma相关指令,cmp比较指令以及shuffle相关的指令,这些函数在实现一些比较简单数学函数、FFT蝶形变换的用处会比较大,所以专门作为指令的进行补充。
立即数指令为何不能使用内联函数
在cmp和shuffle函数中需要使用到立即数,但是C语言是没有一种函数参数的类型是可以表示立即数据,也就是说cmp和shuffle函数如果需要使用内联汇编函数实现的格式大致是根据某一个int类型的变量,使用switch-case的形式将变量转换为函数需要的立即数格式,大致形式如下:
__m256 _mm256_cmp_ps(__m256 a,__m256 b,int imm8)
{switch(imm8){case 0:AVX256_INLINE_x_n_xx(vcmpps,0x00,a,b);case 1:AVX256_INLINE_x_n_xx(vcmpps,0x01,a,b);case 2:AVX256_INLINE_x_n_xx(vcmpps,0x02,a,b);case 3:AVX256_INLINE_x_n_xx(vcmpps,0x03,a,b);case 4:AVX256_INLINE_x_n_xx(vcmpps,0x04,a,b);}AVX256_INLINE_x_n_xx(vcmpps,0x01,a,b);
}
通过O3优化反编译可以发现,只要_mm256_cmp_ps中给定的参数是imm8是一个立即数,_mm256_cmp_ps就会被优化成一条vcmpps指令,也不存在说什么降低指令集性能的情况,按理说这种带立即数的这样写就完事了。但是_mm256_shuffle_ps这种函数该怎么办,这种是拿立即数当掩码用的,如果是拿switch-case需要展开256次,如果真这么写,以后一个带掩码的内联函数都得写几百行,内联函数又要求函数完整的出现在头文件中,多加几个带掩码的avx指令,这头文件都没法看了。
因此,这里就只能使用宏定义函数实现对应的功能,问题也是有的,就是不能看出变量的类型到底是什么样的,所以就在变量名上下功夫,如__m256类型的就以__m256_a、__m256_b表示其输入是一个256位的向量类型的变量;interl的官方文档中avx汇编指令部分均是以imm8表示立即数,这里也沿用这种写法,以imm8表示立即数。当然因为宏定义函数没有对变量的类型的强制限制,可能会存在一些使用上的BUG。
当然我还是希望C语言能够出一种函数的参数类型,能用于表示立即数,或者在内联汇编函数中对"i"(num)中的num只能是立即数而不能是变量的问题进行修复。
avx指令补充
#define AVX256_INLINE_x_xxx(op,a,b,c) {__m256 result;__asm__ volatile (#op " %2, %1, %0": "=x" (result.__float32__): "x" (a.__float32__), "x" (b.__float32__), "0" (c.__float32__));return result;}#define AVX256_INLINE_x_n_xx(op,num,a,b) ({__m256 result;__asm__ volatile (#op " $"#num ", %1, %2, %0": "=x" (result.__float32__): "x" (a.__float32__), "x" (b.__float32__)); result;})static inline __m256 _mm256_floor_pd(__m256 a) AVX256_INLINE_x_n_x(vroundpd,0x01,a);
static inline __m256 _mm256_ceil_ps(__m256 a) AVX256_INLINE_x_n_x(vroundps,0x02,a);
static inline __m256 _mm256_ceil_pd(__m256 a) AVX256_INLINE_x_n_x(vroundpd,0x02,a);static inline __m256 _mm256_fmadd_ps(__m256 a, __m256 b, __m256 c) AVX256_INLINE_x_xxx(vfmadd231ps,a,b,c);
static inline __m256 _mm256_fmadd_pd(__m256 a, __m256 b, __m256 c) AVX256_INLINE_x_xxx(vfmadd231pd,a,b,c);static inline __m256 _mm256_fmsub_ps(__m256 a, __m256 b, __m256 c) AVX256_INLINE_x_xxx(vfmsub231ps,a,b,c);
static inline __m256 _mm256_fmsub_pd(__m256 a, __m256 b, __m256 c) AVX256_INLINE_x_xxx(vfmsub231pd,a,b,c);static inline __m256 _mm256_fnmadd_ps(__m256 a, __m256 b, __m256 c) AVX256_INLINE_x_xxx(vfnmsadd231ps,a,b,c);
static inline __m256 _mm256_fnmadd_pd(__m256 a, __m256 b, __m256 c) AVX256_INLINE_x_xxx(vfnmsadd231pd,a,b,c);static inline __m256 _mm256_fnmsub_ps(__m256 a, __m256 b, __m256 c) AVX256_INLINE_x_xxx(vfnmsub231ps,a,b,c);
static inline __m256 _mm256_fnmsub_pd(__m256 a, __m256 b, __m256 c) AVX256_INLINE_x_xxx(vfnmsub231pd,a,b,c);static inline __m256 _mm256_fmsubadd_ps(__m256 a, __m256 b, __m256 c) AVX256_INLINE_x_xxx(vfmsubadd231ps,a,b,c);
static inline __m256 _mm256_fmsubadd_pd(__m256 a, __m256 b, __m256 c) AVX256_INLINE_x_xxx(vfmsubadd231pd,a,b,c);static inline __m256 _mm256_fmaddsub_ps(__m256 a, __m256 b, __m256 c) AVX256_INLINE_x_xxx(vfmaddsub231ps,a,b,c);
static inline __m256 _mm256_fmaddsub_pd(__m256 a, __m256 b, __m256 c) AVX256_INLINE_x_xxx(vfmaddsub231pd,a,b,c);// __m256_a:__m256,__m256_b:__m256,imm8:立即数 return:__m256
#define _mm256_cmp_ps(__m256_a,__m256_b,imm8) AVX256_INLINE_x_n_xx(vcmpps,imm8,__m256_a,__m256_b);
// __m256_a:__m256,__m256_b:__m256,imm8:立即数 return:__m256
#define _mm256_cmp_pd(__m256_a,__m256_b,imm8) AVX256_INLINE_x_n_xx(vcmppd,imm8,__m256_a,__m256_b);
// __m256_a:__m256,__m256_b:__m256,imm8:立即数 return:__m256
#define _mm256_shuffle_ps(__m256_a,__m256_b,imm8) AVX256_INLINE_x_n_xx(vshufps,imm8,__m256_a,__m256_b);
// __m256_a:__m256,__m256_b:__m256,imm8:立即数 return:__m256
#define _mm256_shuffle_pd(__m256_a,__m256_b,imm8) AVX256_INLINE_x_n_xx(vshufpd,imm8,__m256_a,__m256_b);static inline __m256 _mm256_odd_epi64(__m256 a)
{return _mm256_and_epi64(a,_mm256_set1_epi64(1));
}static inline __m256 _mm256_odd_epi32(__m256 a)
{return _mm256_and_epi32(a,_mm256_set1_epi32(1));
}static inline __m256 _mm256_odd_epi16(__m256 a)
{return _mm256_and_epi16(a,_mm256_set1_epi16(1));
}static inline __m256 _mm256_odd_epi8(__m256 a)
{return _mm256_and_epi8(a,_mm256_set1_epi8(1));
}static inline __m256 _mm256_even_epi64(__m256 a)
{return _mm256_sub_epi64(_mm256_set1_epi64(1),_mm256_and_epi64(a,_mm256_set1_epi64(1)));
}static inline __m256 _mm256_even_epi32(__m256 a)
{return _mm256_sub_epi32(_mm256_set1_epi32(1),_mm256_and_epi32(a,_mm256_set1_epi32(1)));
}static inline __m256 _mm256_even_epi16(__m256 a)
{return _mm256_sub_epi16(_mm256_set1_epi16(1),_mm256_and_epi16(a,_mm256_set1_epi16(1)));
}static inline __m256 _mm256_even_epi8(__m256 a)
{return _mm256_sub_epi8(_mm256_set1_epi8(1),_mm256_and_epi8(a,_mm256_set1_epi8(1)));
}
说明
__m256定义以及一些基本的avx函数在之前的一篇博客中展示过。使用这里面的函数时不能#include <immintrin.h> ,因函数和变量名存在冲突。
仓库
x86_64 Linux使用avx指令