在了解现代x86汇编之前,最好有一定的汇编基础,可以看一下两万字汇编学习笔记
CPU指令集
CPU的Code Name、支持的指令集可以在CPU-Z中查看
指令集划分很复杂,看到好几个版本了,所有内容以官方文档和实际情况为准
MMX
Multi Media eXtension,多媒体扩展*指令*集
MMX指令集支持算数、比较、移位等运算,MMX指令集的向量寄存器是64bit
SSE
Streaming SIMD Extensions,单指令多数据流扩展
所有的SSE系列指令的向量寄存器都是128bit
-
SSE
向量寄存器由MMX的64bit拓展到128bit
-
SSE2
包括了SIMD的浮点和整型运算的指令以及整型和浮点数据之间的转换
-
SSE3
支持不对其访问,处理虚数运算的复杂指令以及水平加减操作运算指令
-
SSE4.1
加入了处理字符串文本和面向应用的优化指令
-
SSE4.2
AVX
Advanced Vector Extensions
在之前的SSE128位扩展到和256位的单指令多数据流
-
AVX
由128bit拓展到256bit,增强了数据重排和灵活的不对齐地址访问
-
AVX2
增加了256bit的整数向量操作,融合乘加,跨通道数据重排等等
-
AVX-512
由256bit扩展到512bit
AVX-512 由多个指令子集组成,并非所有指令子集都被所有实现它们的处理器支持
-
AVX-VNNI、AVX-IFMA
AVX-VNNI 是AVX512-VNNI指令集扩展的VEX编码变体,AVX-IFMA 是AVX512-IFMA的VEX编码变体
-
AVX10
于 2023 年 8 月发布,强制支持512位向量
Intel IMCI
向量寄存器长度扩展到512bit
VT-x
虚拟化
EM64T
扩展64bit内存技术
FMA
Fused-Multiply-Add,积和熔加运算,又叫乘积累加运算
实际上是用一个指令把加减法和乘法结合在一起,提高处理速度
现代x86汇编
现代CPU加入了许多SIMD指令以提高性能,而现代汇编正与此对应。
现代x86汇编中的多数指令,同时支持多个指令集,如vmovups
既可以被用来执行vmovups xmm0, xmmword ptr [rcx]
又可以用来执行vmovups ymm0, ymmword ptr [rcx]
和vmovups zmm0, zmmword ptr [rcx]
x64调用规约
关于调用规约,详见x64调用约定; 关于寄存器使用,详见x64 ABI约定:x64寄存器使用情况
- 前四个浮点参数值通过寄存器XMM0、XMM1、XMM2和XMM3传递给函数,这四个寄存器是易失的
- 浮点返回值在XMM0中
- 浮点值不能作为立即数参数
叶函数是包含以下特点的函数
- 不会调用任何其它函数
- 不会修改RSP寄存器内容
- 不会分配任何局部堆栈空间
- 不会修改任何非易失性寄存器或者XMM寄存器
- 不会使用异常处理
个人对于叶函数的理解:把程序以树的形式呈现,一个函数是一个结点,那么这类没有进一步调用的基础函数就是叶函数
; 非叶函数示例
; extern "C" framePointerExample(void)
framePointerExample proc frame
; prologue
push rbp ; 保存非易失性寄存器
.pushreg rbp
sub rsp, 16 ; 在堆栈上为局部变量分配空间
.allocstack 16
mov rbp, rsp ; 设置堆栈帧指针
.setframe rbp, 0
.endprolog ; 8 + 16 = 24
; epilogue
add rsp, 16 ; 恢复RSP寄存器
pop rbp ; 还原非易失性寄存器
ret
framePointerExample endp
AVX指令集
环绕与饱和运算:环绕算术运算中,数据可能会在运算过程中溢出,而此时在饱和算术运算中,结果会被裁剪为可以装载的最大值
x86-AVX和x86-SSE之间的区别
AVX执行环境
寄存器集
支持AVX的x86-64处理器包含16个256位大小的寄存器,名为YMM0~YMM15。每个YMM寄存器的低阶128位的别名是相对应的XMM寄存器。早期指令集扩展和x86-AVX之间存在一些细微的执行差异。
x86-AVX执行环境还包括一个名为MXCSR的控制状态寄存器,检测浮点运算引起的错误情况。
指令语法
大多数x86-AVX指令使用一个三操作数格式,即包括两个源操作数和一个目标操作数。x86-AVX指令采用的一般语法格式为InstrMnemonic DesOp, SrcOp1, SrcOp2
,其中InstrMnemonic表示指令助记符,DesOp表示目标操作数,SrcOp表示源操作数。一小部分x86-AVX指令集适用一个或者三个源操作数和一个目标操作数。
几乎所有的x86-AVX指令源操作数都是非破坏性的。 这意味着在指令执行期间不会修改源操作数,除非目标操作数寄存器与其中一个源操作数寄存器相同。
使用非破坏性源操作数通常会导致代码更简单、执行速度更快,因为函数必须执行的寄存器到寄存器的数据传输数量有所减少
AVX标量浮点数
控制状态寄存器
寄存器占32位,从低位到高位,依次是IE、DE、ZE、OE、UE、PE、DAZ、IM、DM、ZM、OM、UM、PM、RC、FTZ,剩余16位为保留字段。
应用程序可以修改MXCSR的任何控制标志或者状态位,以适应其特定的SIMD浮点处理要求。
MXCSR寄存器的控制标志和状态位可以使用vldmxcsr
(加载MXCSR寄存器)指令修改。
将掩码位设置为1将禁用相应的异常
vstmxcsr
(存储MXCSR寄存器)指令可以用于保存当前MXCSR状态。
应用程序无法直接访问指定浮点异常处理程序的内部处理表。大多数C++编译器提供了库函数,当发生浮点异常时,允许程序指定调用的回调函数。
MXCSR包含两个控制标志,可以用于加速某些浮点运算。(不符合IEEE 754浮点数标准)
- 把MXCSR.DAZ控制标志设置为1,可以提高非规范化值舍入为零的算法性能
- MXCSR.FTZ常用于加速浮点向下溢出的计算
任何试图将非零值写入保留位位置的尝试都将导致处理器产生异常
出现错误后,处理器将MXCSR错误标志设置为1。
检测到错误后,处理器不会自动清除MXCSR错误标志,必须手动重置这些标志
指令集
[d|s]表示指令既可以用于双精度浮点操作数,也可以用于单精度浮点操作数
助记符 | 说明 |
---|---|
vadds[d|s] | 标量浮点值加法 |
vbroadcasts[d|s] | 广播标量浮点值 |
vcmps[d|s] | 标量浮点值比较 |
vcomis[d|s] | 有序标量浮点值比较并设置RFLAGS |
vcvts[d|s]2si | 把标量浮点值转换为双字有符号整数 |
vcvtsd2ss | 把标量DPFP转换为标量SPFP |
vcvtsi2s[d|s] | 把有符号双字整数转换为标量浮点值 |
vcvtss2sd | 把标量SPFP转换为DPFP |
vcvtts[d|s]2si | 以截断方式把标量浮点值转换为有符号整数 |
vdivs[d|s] | 标量浮点值除法 |
vmaxs[d|s] | 标量浮点值最大值 |
vmins[d|s] | 标量浮点值最小值 |
vmovs[d|s] | 移动标量浮点值 |
vmuls[d|s] | 标量浮点值乘法 |
vrounds[d|s] | 四舍五入标量浮点值 |
vsqrts[d|s] | 标量浮点值平方根 |
vsubs[d|s] | 标量浮点值减法 |
vucomis[d|s] | 无序标量浮点值比较并设置RFLAGS |
vcmps[d|s]与vcomis[d|s]和vucomis[d|s]的区别是vcmps[d|s]是四操作数的,返回双字掩码,且需要用到比较谓词
; e.g.
vcmpss xmm2, xmm0, xmm1, CMP_EQ
option casemap:none
;-------------------------------
; 比较谓词
;-------------------------------
; 基本比较谓词
CMP_EQ equ 00h
CMP_LT equ 01h
CMP_LE equ 02h
CMP_UNORD equ 03h
CMP_NEQ equ 04h
CMP_NLT equ 05h
CMP_NLE equ 06h
CMP_ORD equ 07h
; AVX的扩展比较谓词
CMP_EQU_UQ equ 08h
CMP_NGE equ 09h
CMP_NGT equ 0Ah
CMP_FALSE equ 0Bh
CMP_NEQ_OQ equ 0Ch
CMP_GE equ 0Dh
CMP_GT equ 0Eh
CMP_TRUE equ 0Fh
CMP_EQ_OS equ 10h
CMP_LT_OQ equ 11h
CMP_LE_OQ equ 12h
CMP_UNORD_S equ 13h
CMP_NEQ_US equ 14h
CMP_NLT_UQ equ 15h
CMP_NLE_UQ equ 16h
CMP_ORD_S equ 17h
CMP_EQ_US equ 18h
CMP_NGE_UQ equ 19h
CMP_NGT_UQ equ 1Ah
CMP_FLASE_OS equ 1Bh
CMP_NEQ_OS equ 1Ch
CMP_GE_OQ equ 1Dh
CMP_GT_OQ equ 1Eh
CMP_TRUE_US equ 1Fh
AVX打包浮点值
指令集
指令 | 描述 |
---|---|
vaddp[d|s] | 打包浮点值加法 |
vaddsubp[d|s] | 打包浮点值加减法 |
vandp[d|s] | 打包浮点值按位与 |
vandnp[d|s] | 打包浮点值按位与非 |
vblendp[d|s] | 打包浮点值混合运算 |
vblendvp[d|s] | 可变打包浮点值混合运算 |
vcmpp[d|s] | 打包浮点值比较 |
vcvtdq2p[d|s] | 打包有符号双字转换为浮点值 |
vcvtp[d|s]2dq | 打包浮点值转换为有符号双字 |
vcv2pd2ps | 打包DPFP转换为打包SPFP |
vcvtpd2ps | 打包SPFP转换为打包DPFP |
vdivp[d|s] | 打包浮点值除法 |
vdpp[d|s] | 打包浮点值点积 |
vhaddp[d|s] | 水平打包浮点值加法 |
vhsubp[d|s] | 水平打包浮点值减法 |
vmaskmovp[d|s] | 打包浮点值载入和存储 |
vmaxp[d|s] | 打包浮点值最大值 |
vminp[d|s] | 打包浮点值最小值 |
vmovap[d|s] | 移动对齐的打包浮点值 |
vmovskp[d|s] | 提取打包浮点值符号位掩码 |
vmovup[d|s] | 移动未对齐的打包浮点值 |
vmulp[d|s] | 打包浮点值乘法 |
vorp[d|s] | 打包浮点值按位兼或 |
vpermilp[d|s] | 按通道排列打包浮点值元素 |
vroundp[d|s] | 四舍五入打包浮点值 |
vshufp[d|s] | 混洗打包浮点值 |
vsqrtp[d|s] | 打包浮点值平方根 |
vsubp[d|s] | 打包浮点值减法 |
vunpckhp[d|s] | 解包并交叉高位打包浮点值 |
vunpcklp[d|s] | 解包并交叉低位打包浮点值 |
vxorp[d|s] | 打包浮点值按位异或 |
-
像
__m128
这种变量,它们的定义为typedef union __declspec(intrin_type) __declspec(align(16)) __m128
,也就是已经对齐过了,直接使用vmovap[d|s]
即可 -
打包浮点数的比较,是两个向量的同一位置互相比较,为真则全1,为否则全0
-
打包浮点数的最大/最小值,是两个向量的同一位置互相比较,返回同一位置上的最大/最小值
-
解包并交叉低位打包浮点值(高位与之相似):
-
xmm0: a 3 a 2 a 1 a 0 a_3\quad a_2\quad a_1\quad a_0 a3a2a1a0
-
xmm1: b 3 b 2 b 1 b 0 b_3\quad b_2\quad b_1\quad b_0 b3b2b1b0
-
vunpcklps xmm2, xmm0, xmm1
-
xmm2: b 1 a 1 b 0 a 0 b_1\quad a_1\quad b_0\quad a_0 b1a1b0a0
-
-
利用交叉将浮点矩阵转置
; void spfp4x4Transpose(__m128& a, __m128&b, __m128&c, __m128&d) spfp4x4Transpose proc sub rsp, 48 vmovups xmmword ptr [rsp-16], xmm4 vmovups xmmword ptr [rsp-32], xmm5 vmovups xmmword ptr [rsp-48], xmm6 vmovaps xmm0, xmmword ptr [rcx] ; xmm0 = a3 a2 a1 a0 vmovaps xmm1, xmmword ptr [rdx] ; xmm1 = b3 b2 b1 b0 vmovaps xmm2, xmmword ptr [r8] ; xmm2 = c3 c2 c1 c0 vmovaps xmm3, xmmword ptr [r9] ; xmm3 = d3 d2 d1 d0 vunpcklps xmm4, xmm0, xmm1 ; xmm4 = b1 a1 b0 a0 vunpcklps xmm5, xmm2, xmm3 ; xmm5 = d1 c1 d0 c0 vmovlhps xmm6, xmm4, xmm5 ; xmm6 = d0 c0 b0 a0 vmovaps xmmword ptr [rcx], xmm6 vmovhlps xmm6, xmm4, xmm5 ; xmm6 = d1 c1 b1 a1 vmovaps xmmword ptr [rdx], xmm6 vunpckhps xmm0, xmm0, xmm1 ; xmm0 = b3 a3 b2 a2 vunpckhps xmm1, xmm2, xmm3 ; xmm1 = d3 c3 d2 c2 vmovlhps xmm4, xmm0, xmm1 ; xmm4 = d2 c2 b2 a2 vmovaps xmmword ptr [r8], xmm4 vmovhlps xmm5, xmm1, xmm0 ; xmm5 = d3 c3 b2 a2 vmovaps xmmword ptr [r9], xmm5 vmovups xmm6, xmmword ptr [rsp-48] vmovups xmm5, xmmword ptr [rsp-32] vmovups xmm4, xmmword ptr [rsp-16] add rsp, 48 ret spfp4x4Transpose endp
AVX打包整数
指令集
指令 | 描述 |
---|---|
vmov[d|q] | 移动到/移动出XMM寄存器 |
vmovdqa | 移动对齐的打包整数值 |
vmovdqu | 移动非对齐的打包整数值 |
vpabs[b|w|d] | 打包整数绝对值 |
vpackss[dw|wb] | 有符号饱和的打包 |
vpackus[dw|wb] | 无符号饱和的打包 |
vpadd[b|w] | 打包整数加法 |
vpadds[b|w] | 有符号饱和的打包整数加法 |
vpaddus[b|w] | 无符号饱和的打包整数加法 |
vpand | 打包整数按位与 |
vpandn | 打包整数按位与非 |
vpcmpeq[b|w|d|q] | 打包整数比较相等性 |
vpcmpgt[b|w|d|q] | 打包整数比较大于 |
vpextr[b|w|d|q] | 从XMM寄存器提取整数 |
vphadd[w|d] | 水平打包加法 |
vphsub[w|d] | 水平打包减法 |
vpinsr[b|w|d|q] | 将整数插入XMM寄存器 |
vpmaxs[b|w|d] | 打包有符号整数最大值 |
vpmaxu[b|w|d] | 打包无符号整数最大值 |
vpmins[b|w|d] | 打包有符号整数最小值 |
vpminu[b|w|d] | 打包无符号整数最小值 |
vpmovsx | 带符号扩展的打包整数移动 |
vpmovzx | 零扩展的打包整数移动 |
vpmuldq | 打包有符号双字乘法 |
vpmulhuw | 打包无符号双字乘法,保存高位结果 |
vpmul[h|l]w | 打包有符号字乘法,保存[高位|低位]结果 |
vpmull[d|w] | 打包有符号字乘法,保存低位结果 |
vpmuludq | 打包无符号字乘法 |
vpshuf[b|d] | 混洗打包整数 |
vpshuf[h|l]w | 混洗[高位|低位]打包整数 |
vpslldq | 双四字逻辑左移 |
vpsll[w|d|q] | 打包逻辑左移 |
vpsra[w|d] | 打包算术右移 |
vpsrldq | 双四字逻辑右移 |
vpsrl[w|d|q] | 打包逻辑右移 |
vpsub[b|w|d|q] | 打包整数减法 |
vpsubs[b|w] | 有符号饱和的打包整数减法 |
vpsubus[b|w] | 无符号饱和的打包整数减法 |
vpunpckh[bw|wd|dq] | 解包高位数据 |
vpunpckl[bw|wd|dq] | 解包低位数据 |
AVX2
x86指令集扩展
助记符 | 说明 |
---|---|
vbroadcasti128 | 广播128位整数数据 |
vextracti128 | 提取128位整数数据 |
vinserti128 | 插入128位整数数据 |
vpblendd | 混合打包双字 |
vpbroadcast[b|w|d|q] | 广播整数值 |
vperm2i128 | 排列128位整数数据 |
vperm[d|q] | 排列打包整数 |
vpgatherd[d|q] | 使用有符号双字索引的打包整数收集 |
vpgatherq[d|q] | 使用有符号四字索引的打包整数收集 |
vpmaskmov[d|q] | 条件打包整数移动 |
vpsllv[d|q] | 使用单独元素计数的逻辑左移位 |
vpsravd | 使用单独元素计数的算术右移位 |
vpsrlv[d|q] | 使用单独元素计数的逻辑右移位 |
指令集扩展 | CPUID功能标志 |
---|---|
扩展无符号整数加法 | ADX |
高级位操作(组1) | BMI1 |
高级位操作(组2) | BMI2 |
半精度浮点转换 | F16C |
乘法加法融合 | FMA |
计数前导零位 | LZCNT |
计数设置位 | POPCNT |
半精度浮点数
助记符 | 说明 |
---|---|
vcvtph2ps | 把半精度浮点数转换为单精度浮点数 |
vcvtps2ph | 把单精度浮点数转换为半精度浮点数 |
FMA(积和熔加运算)
通用寄存器指令集扩展
AVX-512
其实和AVX、AVX2差不多,指令几乎没什么变化,只是提供了更高的吞吐量