大理寺少卿 发表于 2025-6-7 22:05:57

X86C++反汇编12.浮点指令和多媒体指令

浮点寄存器有时候会用在其他优化地方,例如 数组的初始化

__asm cpuid   通过 这条指令可以拿到cpu 的信息

### 浮点协处理器

```
定点数:      早期计算机是不能做浮点运算的,只支持正数的运算,因此发明了定点数,2个浮点数转成整数在 除以 10 的小数点次数的幂,获得正数,在打印一个点,在获取%   0 的小数点次数的幂 获得余数
```


```
但是定点数保存的数值范围很小,因此小数点 的 位置确定 , 例如16位 ,前8位整数, 后8位保存小数,那么整数部分超过8位就无法保存,空间没办法最大化利用,因此发明了浮点数,即小数点的位置是浮动的
```


```
现在的浮点格式 基本是    IEEE   1(符号)   8 (指数,小数点位置)      23 (值)       浮点数的有效数字是7位
```


为了解决浮点运算,因此发明了浮点协处理器

```
浮点指令最开始是通过模拟运算来进行的,效率低下。后来为了提高效率,从硬件入手,产生了只能做浮点运算的x87浮点处理器(副处理器),协助整型处理器(主处理器)进行运算,故浮点处理器又称为浮点协处理器FPU。
```


```
在早期的计算机行业浮点协处理器 并不是普及的,因此价格比较贵或者不需要
```



```
浮点指令的更新很慢,因为旧的cpu不能用,没这条指令,只能等新的cpu普及了才能用,否则程序可能无法运行
```



```
AMD 的浮点指令 和INTEL的 浮点指令存在部分差异
```


### x87浮点处理器

#### 1.浮点寄存器(存储浮点数)

- 数量8个:ST(0)–ST(7);
- 结构:stack registers,栈结构(循环堆栈闭环结构), 只能操作栈顶。
- 运算:
- - 入栈相当于从st(0)向下压。
- 只能在栈顶运算,所以做完浮点运算后,数据必须出栈。
- 出栈相当于整体向上挪,出栈的寄存器st(0)的值移动到末尾ST(7)。
- 浮点寄存器满了再操作会报错。
- 不能直接操作寄存器

#### 2.浮点指令(操作)

- Intel x87 FPU专门用于执行浮点计算,可以对单精度浮点(32位)、双精度浮点(64位)、扩展双精度浮点(80位)进行计算。
- **常用浮点数指令:**
- - 入栈:fld(内存地址)fild(整形内存地址,转为浮点型)
- 加:fadd   带p代表出栈一次
- 减:fsub
- 乘:fmul
- 除:fdiv
- 作比较:fcom
- 求绝对值: fabs
- 出栈:fst(把寄存器值拷贝到内存,栈不变)、fstp(把寄存器值拷贝到内存,出栈一次)fisp (把值转整形再给内存)   fistp (把值转整形再给内存,出栈一次)

!(./notesimg/1639450721195-dc8d4576-01b0-4210-a95d-cf570ee00071.png)

- **FADDP运算过程模拟:**1(栈顶st(0))+2作运算后,原1出栈弃,原2循环至st(7),结果3置于原2处(新晋栈顶)。

!(./notesimg/1639482573457-21f71380-d989-4917-9496-78a49929e405.png)

#### 3.**代码用例**

```
int main()
{
    //注意,如果用 {}赋值表示不允许隐式转换,避免=赋值运算时产生的丢失精度问题
    float f1 = 1.23f;
    float f2 = 2.34f;
    float result = 0.0f;        // 返回值必须初始化

    // 优化前:寄存器间操作
    __asm
    {
                fld f1;                        //fl压栈 st(0) = 1.23f
      fld f2;                        //f2压栈 st(0) = 2.34f;st(1) = 1.23f
      faddp st(1), st(0);        // st(0) = 3.57f;st(7) = 2.34f
      fstp result;                        // st(6) = 2.34f;st(7) = 3.57f;ret = 3.57f
    }

    // 优化后:直接加内存,减少两次操作(入栈出栈各一次) 内存操作不能带p
    __asm
    {
           fld f1;                // st(0) = 1.23f
      fadd f2;        // st(0) 1.23f + 内存值2.34f → st(0) = 3.57f
      fstp result;        // st(7) = 3.57f ;ret = 3.57f
    }   
}
```

调试:

!(./notesimg/1658410319274-eb6ca69a-5795-4d9d-a8cf-c4d05769565c.png)

!(./notesimg/1658410973369-f53333d2-56a8-472e-a92f-67b0b71b8f51.png)

```
int main() {
float f1 = 3.12;
float f2 = 2.66;
printf("%f\n", f1 + f2 * f1 / f2 );
return 0;
}
反汇编代码:
debug版
fld   ds:flt_417B3C      
fstp   
;入栈 出栈 初数化值
fld   ds:flt_417B38
fstp   

fld   
fmul          ;乘法
fdiv           ;除法
fadd           ;加法
sub   esp, 8
fstp    qword ptr ; char
push    offset aLf      ; "%lf\n"
call    sub_4110CD
add   esp, 0Ch
xor   eax, eax


int main() {
    float f1 = 3.12f;
   float f2 = 2.66f;
   scanf_s("%f %f", &f1, &f2);
   printf("%lf\n", f1 + f2 * f1 / f2 );
   return 0;
}
反汇编代码:
release版
fld   ds:flt_402114
lea   eax,
push    eax
fstp    dword ptr
fld   ds:flt_402110
lea   eax,
push    eax             ; Arglist
push    offset aFF      ; "%f %f"
fstp   
call    sub_401060
fld   
add   esp, 4
fld   dword ptr
fld   st
;都压栈,内存操作效率低
fmul    st, st(2)
fdivrpst(2), st
faddp   st(1), st
fstp    qword ptr ; ArgList
push    offset Format   ; "%lf\n"
call    sub_401020
```

4.缺点

```
操作不方便,需要不停出栈入栈
```


```
不能直接操作指定寄存器
```


MMX指令集

并行正数运算

1.MMX指令集概念

●MMX(Multi Media eXtension,多媒体扩展指令集)指令集是Intel公司于1996年推出的一项多媒体指令增强技术(并行运算技术,使得做一次运算和做多次运算的速度是一样的 )。

●MMX指令集包括57条多媒体指令,通过这些指令可以一次处理多个数据,在处理结果超过实际处理能力的时候也能正常进行处理,这样在软件的配合下,就可以得到更高的性能。

●只支持整型的并行运算,不支持浮点数的并行运算(此问题由AMD公司解决)。

2.MMX指令集优势

●取消了浮点栈结构,与通用寄存器操作一致。

3.MMX寄存器

●MMX8个64位寄存器名称:MM0 ~ MM7。

●MM0 ~ MM7== st(0) ~ st(7) ,以便兼容都是栈相关,改变mm0,st(0)也改变,一般观察mmx指令集后即可。

4.mmx常用浮点指令

●首字母前缀p:打包运算,即整个寄存器做运算。

●饱和方式:有符号运算不会溢出,如255+1=255,一般用在显示器算法当中。

常用指令表

私锁

CRACKING

使用格式

指令名称

指令功能

复制MIX寄存器中的低位双字到一个通用寄存器或内存中,也可以把通

mmxreg/mem32

movd

用寄存器或内存中的数据复制到MMX寄存器的低位双字中

w,reg/mem32

mmx,

把一个MMX寄存器的内容复制到另一个MX寄存器中,这个指令也能被

mmxl,mmx2/mem64

movg

用来把一个内存区域中的内容复制到一个MNX寄存器中,或者把MX寄

mmx1/mem64,

mmx2

存器中的内容复制到内存中

环绕方式,并行执行1个字节整型加法

mmx1,mmx2/mem64

paddb

环绕方式,并行执行4个字节整型加法

mmx1,mmx2/mem64

paddd

饱和方式,并行执行有符号1个字节整型加法

mmx1,mmx2/mem64

paddsb

饱和方式,并行执行有符号2个字节整型加法

mmx1,mmx2/mem64

paddsw

饱和方式,并行执行无符号1个字节整型加法

mmx1,mmx2/mem64

paddusb

饱和方式,并行执行无符号2个字节整型加法

mmx1,mmx2/mem64

paddusw

环绕方式,并行执行1个字节整型减法

mmx1,mmx2/mem64

psubb

环绕方式,并行执行2个字节整型减法

mmx1,mmx2/mem64

psubw

环绕方式,并行执行4个字节整型减法

mmx1,mmx2/mem64

psubd

饱和方式,并行执行有符号1个字节整型加法

mmx1,mmx2/mem64

psubsb

!(https://cdn.nlark.com/yuque/0/2021/png/1773393/1639486882432-f24e41d6-44eb-4728-a1f9-008b5c07b8ee.png?x-oss-process=image%2Fresize%2Cw_750%2Climit_0)

mmx常用浮点指令表

5.代码用例

```
int main()
{
    // 4字节长度数据类型
    int ary1[] = {10,20};
    int ary2[] = {10,10};


    // 并行运算
    __asm
    {
      //q 8字节 d 四字节
           movq mm0, ary1;       // MM0 = 00000140000000a
      movq mm1, ary2;// 同上,MM1 = 000000a000000a
      paddd mm0, mm1;       // 并行执行加法:10+10和20+10的结果给mm0                      
      movq ary1, mm0;       //ary1 = 0000001e ary1 = 00000014;
      
      movd eax,mm0;    //可以直接把浮点寄存器的值给通用寄存器
    }

    // 1字节长度数据类型:可以同时8个整型做加法
    char ary1[] = {1,1,1,1,1,1,1,1};
    char ary2[] = {2,2,2,2,2,2,2,2};


   // 并行运算
    __asm
    {
           movq mm0, ary1;        // MM0 = 0101010101010101
      movq mm1, ary2; // 同上,MM1 = 0202020202020202
      paddb mm0, mm1;        // 并行执行加法:结果给mm0                             
      movq ary1, mm0;
    }
    return 0;
}
```

AMD 3DNOW指令集

●3DNow!(据称是“3D No Waiting!”的缩写),是由AMD开发的一套SIMD多媒体指令集,支持单精度浮点数的矢量运算,用于增强x86架构的计算机在三维图像处理上的性能。

●新增浮点数的并行运算指令:

○Pfadd

○pfsub

●后面因为 intel不支持3DNOW 指令集, 推出了 sse 指令集 所以后面该指令集就作废了,因此生命周期很短

SSE指令

这一套指令集是目前逆向遇到最多的,也是 vs 2019 默认的浮点指令

1.SSE指令概念

●SSE( Streaming SIMD Extensions)是英特尔在AMD的3D Now!发布一年之后,在其计算机芯片Pentium III中引入的指令集,是继MMX扩展指令集。提供70条新指令。AMD后来在Athlon XP中加入了对这个新指令集的支持。

●加快浮点运算的同时,改善了内存的使用效率,使内存速度更快。对游戏和图形处理方面的效果尤其显著。

●SSE:有8个128位独立寄存器(XMM0~XMM7)。现在有256位

●MM:指64位MMX寄存器。

●XMM:指XMM寄存器。

●m128:指128位内存变量。

●SSE1:主要是单精度浮点运算。

●SSE2:主要是双精度浮点运算,与SSE1使用相同寄存器。

●SSE5:   AMD发布,支持3操作数

2.常用指令集

●打包运算(除了paddw,其它指令仅支持浮点):除了movaps ,还有movups。

○movaps:对齐,变量内存地址必须 %16 = 0(最低4位一定是0),以保证更高的计算效率,否则cpu报错;

○movups:不对齐。

○考虑效率,movaps优于movups,但需要自行把控地址,所以一般使用movups。

常用指令表

私锁

CRACKING

指令名称

指令功能

使用格式

传送单精度数

MOVSS

xmml,xmm2

Xmml,mem32

xmm2/mem32,xmml

传送双精度数

MOVSD

xmml,xmm2

mem64

xmm

xmm2/mem64,xmml

传送对齐的封装好的单精度数

MOVAPS

xmml,xmm2/mem128

xmm1/mem128.

Xmm2

传送对

专好的双精度数

MOVAPD

xmml,xmm2/mem128

Xmm2

xmml/mem128,

单精度数加法

ADDSS

xmml,xmm2/mem32

双精度数加法

xmml,xmm2/mem64

ADDSD

!(https://cdn.nlark.com/yuque/0/2021/png/1773393/1639489226765-9ee71b90-361a-41ab-bd7c-55a132ee63da.png?x-oss-process=image%2Fresize%2Cw_750%2Climit_0)

科锐

CRACKING



向实训

使用格式

指令名称

指令功能

并行4个单精度数加法

ADDPS

XMML,XMM2/MEM128

并行2个双精度数加法

ADDPD

XMML,XMM2/MEM128

单精度数减法

SUBSS

XMML,XMM2/MEM32

双精度数减法

SUBSD

XMM1,XMM2/MEM64

并行4个单精度数减法

SUBPS

XMML,XMM2/MEM128

并行4个整型减法

XMML,XMM2/MEM128

PADDD

!(https://cdn.nlark.com/yuque/0/2022/png/27242010/1658413250409-79d24c39-1d7a-46b8-80eb-3340a1e30f08.png?x-oss-process=image%2Fresize%2Cw_802%2Climit_0)

●注意:SSE不支持3DNOW!。编译可通过,无法运行。

!(./notesimg/1639488511982-0bd1771e-7fb5-431f-bf04-b5f9df15ca56.png)

3.代码用例

```
#include <stdio.h>

#include <xmmintrin.h> //SSE1 内部函数
/*
* SSE指令寄存器:xmm0~xmm7
*
* 原:dword ptr、qword ptr
* 新:mmword ptr(8字节)、xmmword ptr
*/

int main()
{
// 单精度float
float f1 = 1.1f;
float f2 = 2.2f;
float ret1 = 0.0f;
// 双精度double
double d1 = 1.1;
double d2 = 2.2;
double ret2 = 0.0;
// float[]
float ary1[] = { 1.1f, 1.1f, 1.1f, 1.1f };
float ary2[] = { 1.1f, 1.1f, 1.1f, 1.1f };


float f;
int ary11[] = {1, 1, 1, 1 };
__asm {

    //类型转换
    mov eax, 6                        // eax = 6
    cvtsi2sd xmm1, eax        // 将后者eax的值转换成浮点数给前者xmm1
    movss f, xmm1                // f = 6.0
      
    // 单精度float
    movss xmm0, f1        // xmm0 = 0...03F8CCCCD
    movss xmm1, f2        // xmm1 = 0...0400CCCCD
    addss xmm0, xmm1// xmm1同上,xmm0 = 0...040533334
    movss ret1, xmm0// xmm0和xmm1同上,ret1 =3.3
      
    // 双精度double
    movsd xmm0, d1                // xmm0 = 0...3FF199999999999A
    movsd xmm1, d2                // xmm1 = 0...400199999999999A
    addsd xmm0, xmm1        // xmm1同上,xmm0 = 0...400A666666666667
    movsd ret2, xmm0        // xmm0和xmm1同上,ret2 =3.3
   
    //并行运算
    movups xmm0, ary1   // xmm1同上,xmm0 = 3F8CCCCD3F8CCCCD*2组
    movups xmm1, ary2         // xmm1同上xmm0
    addpsxmm0, xmm1        // xmm1同上,xmm0 = 400CCCCD400CCCCD*2组
    movups ary1, xmm0        // ret ~ ret = 2.2
      
    movups xmm0, ary11        // xmm1同上,xmm0 = 3F8CCCCD3F8CCCCD*2组
    movups xmm1, ary11
    paddw xmm0, xmm1
}
return 0;


   //高级语言并行运算   
      __m128 ary1 = { 1, 2, 3, 4 };
      __m128 ary2 = { 1, 2, 3, 4 };
      ary1 = _mm_add_ps(ary1, ary2);

}
```

```
int main() {
float f1 = 3.12;
float f2 = 2.66;
scanf_s("%f %f", &f1, &f2);
printf("%f\n", f1 + f2 * f1 / f2 );
return 0;
}
反汇编代码
mov   ecx, offset unk_41C005
call    j_@__CheckForDebuggerJustMyCode@4 ; __CheckForDebuggerJustMyCode(x)
movss   xmm0, ds:dword_417B40
movss   dword ptr , xmm0
movss   xmm0, ds:dword_417B3C
movss   , xmm0
lea   eax,
push    eax
lea   ecx,
push    ecx             ; char
push    offset aFF      ; "%f %f"
call    sub_411280
add   esp, 0Ch
movss   xmm0,
mulss   xmm0, dword ptr    ;乘法
divss   xmm0,                      ;除法
addss   xmm0, dword ptr ;加法
cvtss2sd xmm0, xmm0      ;类型强转
sub   esp, 8
movsd   qword ptr , xmm0 ; char
push    offset asc_417B38 ; "%f\n"
call    sub_4110D7
```

### AVX指令集

#### 1.AVX指令集概念

- AVX(Advanced Vector Extension,,高级向量扩展)。即寄存器扩展到256位。
- - 在XMM的基础上寄存器范围增加一倍
- - - XMM0 ~ XMM15 128bit
    - YMM0 ~ YMM15 256bit
- - 运算指令支持三个操作数。
- 兼容性:全面兼容SSE指令,只需要将SSE指令前+V即可。
- !(./notesimg/1639493023357-fece478f-0a39-4063-a278-b076454d104e.png)

![](./notesimg/_PGZ6U3JMZMTHM@Z%2]WM3X.jpg)

![](./notesimg/2.png)

```
#include <stdio.h>
#include <xmmintrin.h> //SSE1 内部函数
#include <immintrin.h> //avx 内部函数

int main() {


   float ary1[] = {1, 2, 3, 4};
   float ary2[] = {1, 1, 1, 1};

   
   float f1 = 1.2f;
   float f2 = 1.3f;
   float result = 0.0f;
__asm {
   vmovss   xmm0, f1
   vcvtss2si eax, xmm0
   vmovss   xmm1, f2
   vaddss   xmm0, xmm0, xmm1
   vmovss   result, xmm0
   
   //并行运算
   vmovupsxmm0, ary1
   vmovupsxmm1, ary2
   vaddps   xmm0, xmm0, xmm1
   vmovupsary1, xmm0
   }
   __asm cpuid

   //c语言版并行运算
   __m256 ary1 = { 1, 2, 3, 4, 1, 2, 3, 4 };
   __m256 ary2 = { 1, 2, 3, 4, 1, 2, 3, 4 };
    ary1 = _mm256_add_ps(ary1, ary2);


return 0;
}
```

```
#include <stdio.h>
#include <xmmintrin.h> //SSE1 内部函数
#include <immintrin.h> //avx 内部函数

int main() {
float f1 = 3.12;
   float f2 = 2.66;
   scanf_s("%f %f", &f1, &f2);
   printf("%f\n", f1 + f2 * f1 / f2);

   __m256 ary1 = { 1, 2, 3, 4, 1, 2, 3, 4 };
   __m256 ary2 = { 1, 2, 3, 4, 1, 2, 3, 4 };
   ary1 = _mm256_add_ps(ary1, ary2);
return 0;
}

lea   eax,
mov   dword ptr , 4047AE14h
push    eax
lea   eax,
mov   , 402A3D71h
push    eax             ; Arglist
push    offset aFF      ; "%f %f"
call    sub_401060
movss   xmm2,
add   esp, 4
mulss   xmm2, dword ptr
divss   xmm2,
addss   xmm2, dword ptr
cvtps2pd xmm0, xmm2
movsd   qword ptr , xmm0 ; ArgList
push    offset Format   ; "%f\n"
call    sub_401020
```

### 多媒体指令集小结

#### 1.发展历程

!(./notesimg/1639496767549-62ec0cc7-4727-4a68-9914-5fb5a972593f.png)

#### 2.常见选择

- 多媒体指令集发展到现在,浮点寄存器不仅仅是用来进行浮点运算,有时候IDE为可执行文件做优化的时候也会使用独立的浮点寄存器做运算等(如Release版本)。
- **VS指定增强指令设置:**项目属性 → C/C++ →代码生成 →启用增强指令集 →选择。出于兼容性考虑,一般建议使用集vs默认使用SSE2。

#### 3.封装为函数的并行运算

- VS中不使用内联汇编就能并行运算,将所有的指令封装成函数,这样在64位的情况下也能使用并行运算了。
- 如何描述大小:通过独有的数据类如:__m128
- 各个版本的多媒体函数头文件特点:
- mm:mmx指令
- xmm:SSE指令
- imm:AVX指令

```
#include <stdio.h>
#include <mmintrin.h> //mmx指令
#include <xmmintrin.h> // SSE
//emmintrin.h   SSE2
//pmmintrin.h   SSE3
//smmintrin.h   SSE4
#include <immintrin.h> //avx
int main()
{
__m64 n1;
n1.m64_i32 = 1;
n1.m64_i32 = 2;
__m64 ret = _m_paddd(n1, n1);

//__m128 n1;
//__m256 n2;
float f1;
float f2;
scanf_s("%f %f", &f1, &f2);
printf("%f\n", f1 + f2);
}
```

4.查看cpu支持指令集的工具 -- CPU-Z

!(./notesimg/1639497014543-f3a8ce38-6d07-4204-99d8-fb4794e6132a.png)

asmprofan 发表于 2025-6-8 06:08:02

啥也不说了,楼主就是给力!

YINXN 发表于 2025-6-9 20:56:39

谢谢分享

xht530 发表于 2025-6-10 09:14:44

感谢分享!

ldljlzw 发表于 2025-6-13 08:43:42

果断MARK,前十有我必火!

ldljlzw 发表于 2025-6-15 16:15:22

444444444444444444444

ldljlzw 发表于 2025-6-16 08:42:51

啥也不说了,楼主就是给力!

CatReverse 发表于 2025-6-21 09:10:19

学习!!!
页: [1]
查看完整版本: X86C++反汇编12.浮点指令和多媒体指令