按键精灵 如何给xmmx64 xmm 寄存器器赋值,用大漠dm.AsmAdd "movsd xmm0,[eax]" 提示书写格式错误,跪求大佬指点

SSE2 (单指令多数据流扩展)浮点指令使用128位的XMM寄存器,可以处理双精度(64位)浮点值。也有一些工作于单精度(32位)浮点值的指令。SSE2在Pentium 4 和 Xeon处理器中被提出。

这些指令跟SSE浮点指令非常类似,除了它们工作的数据长度不同。

在你的代码中使用这些指令之前,你必须检测你的机器是否支持它们。设置EAX=1,调用CPUID指令,此时测试EDX的第26位,如果为1则表示支持SSE2指令。

本文的测试程序都将使用以下的数据声明:

由于这些数据不能保证16位对齐,所以从内存到XMM寄存器传输数据必须使用MOVUPD指令。MOVUPD(移动两个未对齐的双精度值)不关心对齐。如果你在数据声明的时候指定了16字节对齐,那么就可以使用更快的MOVAPD(移动两个对齐的双精度值)指令。当在两个寄存器之间传输的时候,MOVUPD或者MOVAPD都可以使用。

我们在这里看到的指令往往可以分为两种类型,第一种指令一次处理两个64位浮点数,这些指令的名字里包含“PD”,指的是“packed double-precision”。第二种指令一次处理一个64位浮点数,这些指令的名字里包含“SD”,指的是“scalar double-precision”。 它们仅仅工作在XMM寄存器的低位部分,也就是说寄存器的64位(0-63)。

下面的测试程序,你可以给它们设置适当的断点,单步运行。你能看到在程序运行中XMM寄存器的改变。

这个测试程序演示在寄存器之间移动数据。MOVUPD和MOVAPD(对齐版本),MOVSD,MOVLPD和MOVHPD也能被使用在内存输入输出中获得数据。MOVMSKPD在比较指令后使用,可以把比较结果存入eax以便分析。

作为一个测试程序,我们也能尝试使用SSE整数指令MOVDQU和SSE浮点指令MOVUPS做这些事,后者看起来很像MOVUPD。它似乎只是位拷贝数据到XMM寄存器。然而,Intel警告反对这种不明确方式的使用指令,以防止未知的性能问题。

SSE2乱序与扩展指令

在我的印象中,有几个版本的FFmpeg中有Intrinsic优化的指令集代码。可能是由于纯汇编的性能和灵活性,随着版本的迭代,现在优化代码已经逐渐替换成了纯汇编代码。FFmpeg中的纯汇编代码使用了nasm汇编语法格式,且使用了x264工程中的两个汇编源文件“> ;原 新增 原 新增 原 新增 扩展 新增 %rep %0 ;假设该函数传入了2个参数,分别是tt和zz ;stack_offset主要是因为通用寄存器压栈导致的当前栈地址和返回地址之间的偏移 ;低地址;在保存xmm寄存器的时候,返回地址已经压栈(64位条件下返回地址压栈需要8字节), ;这里加32是为了在汇编函数中调用下一个汇编函数时,分配的32字节的shadow space ;%1在该文件中是rsp,这里是在从堆栈中恢复xmm寄存器的内容 ;由于32位只有6个可随意使用的通用寄存器,这里将r7~r14声明在栈上, ;这里之所以将3、4、5和6寄存器保存起来,是因为在x86_32环境下0,1和2对应的寄存器 ;分别对应的是eax,ecx和edx,它们3个易挥发的寄存器操作前不用保护. ;将堆栈中的数据拷贝到寄存器中 ;'$'和'$$',它们允许引用当前指令的地址。 ;'$'计算得到它本身所在源代码行的开始处的地址; ;所以你可以简单地写这样的代码'jmp $'来表示无限循环。 ;'$$'计算当前段开始处的地址,所以你可以通过($-$$)找出你当前在段内的偏移。 ;但是会将指令和函数后缀设置为sse. %ifidn %%dst, %3 ;目的操作数和第二及第三源操作数相同,则不支持 %elifnnum sizeof%8 ;第二源操作数为内存值,比如为一个m256值,而且是可commutative的,则交换第一第二源操作数的顺序 %if %0 >= 9 ;指令有4个参数,上面已经使用mov*指令将第一操作数移到寄存器 %else ;指令有3个参数,上面已经使用mov*指令将第一操作数移到%6寄存器, %1 %6, __src2 ;然后使用指令对%6和第二源操作数进行操作 ;一个函数在调用时,前四个参数是从左至右依次存放于RCX、RDX、R8、R9寄存器里面,剩下的参数从左至右顺序入栈; ;调用者负责在栈上分配32字节的“shadow space”,用于存放那四个存放调用参数的寄存器的值(亦即前四个调用参数); ;RAX,RCX,RDX,R8,R9,R10,R11是“易挥发”的,不用特别保护,其余寄存器需要保护。(x86下只有eax, ecx, edx是易挥发的) ;栈需要16字节对齐,“call”指令会入栈一个8字节的返回值 ;整数前 4 个参数传入 RCX、RDX、R8 和 R9 中。其他参数传递到堆栈中。 ;常用寄存器有16个,分为x86通用寄存器以及r8-r15寄存器。 ;通用寄存器中,函数执行前后必须保持原始的寄存器有3个:是rbx、rbp、rsp。rx寄存器中,最后4个必须保持原值:r12、r13、r14、r15。 ;保持原值的意义是为了让当前函数有可信任的寄存器,减小在函数调用过程中的保存&恢复操作。除了rbp、rsp用于特定用途外,其余5个寄存器可随意使用。 ;通用寄存器中,不必假设保存值可随意使用的寄存器有5个:是rax、rcx、rdx、rdi、rsi。其中rax用于第一个返回寄存器(当 然也可以用于其它用途),rdx用于第二个返回寄存器(在调用函数时也用于第三个参数寄存器)。rcx用于第四个参数。rdi用于第一个参数。rsi用于 第二个函数参数。 ;r8、r9分配用于第5、第6个参数。

在我的印象中,有几个版本的FFmpeg中有Intrinsic优化的指令集代码。可能是由于纯汇编的性能和灵活性,随着版本的迭代,现在优化代码已经逐渐替换成了纯汇编代码。FFmpeg中的纯汇编代码使用了nasm汇编语法格式,且使用了x264工程中的两个汇编源文件“> ;原 新增 原 新增 原 新增 扩展 新增 %rep %0 ;假设该函数传入了2个参数,分别是tt和zz ;stack_offset主要是因为通用寄存器压栈导致的当前栈地址和返回地址之间的偏移 ;低地址;在保存xmm寄存器的时候,返回地址已经压栈(64位条件下返回地址压栈需要8字节), ;这里加32是为了在汇编函数中调用下一个汇编函数时,分配的32字节的shadow space ;%1在该文件中是rsp,这里是在从堆栈中恢复xmm寄存器的内容 ;由于32位只有6个可随意使用的通用寄存器,这里将r7~r14声明在栈上, ;这里之所以将3、4、5和6寄存器保存起来,是因为在x86_32环境下0,1和2对应的寄存器 ;分别对应的是eax,ecx和edx,它们3个易挥发的寄存器操作前不用保护. ;将堆栈中的数据拷贝到寄存器中 ;'$'和'$$',它们允许引用当前指令的地址。 ;'$'计算得到它本身所在源代码行的开始处的地址; ;所以你可以简单地写这样的代码'jmp $'来表示无限循环。 ;'$$'计算当前段开始处的地址,所以你可以通过($-$$)找出你当前在段内的偏移。 ;但是会将指令和函数后缀设置为sse. %ifidn %%dst, %3 ;目的操作数和第二及第三源操作数相同,则不支持 %elifnnum sizeof%8 ;第二源操作数为内存值,比如为一个m256值,而且是可commutative的,则交换第一第二源操作数的顺序 %if %0 >= 9 ;指令有4个参数,上面已经使用mov*指令将第一操作数移到寄存器 %else ;指令有3个参数,上面已经使用mov*指令将第一操作数移到%6寄存器, %1 %6, __src2 ;然后使用指令对%6和第二源操作数进行操作 ;一个函数在调用时,前四个参数是从左至右依次存放于RCX、RDX、R8、R9寄存器里面,剩下的参数从左至右顺序入栈; ;调用者负责在栈上分配32字节的“shadow space”,用于存放那四个存放调用参数的寄存器的值(亦即前四个调用参数); ;RAX,RCX,RDX,R8,R9,R10,R11是“易挥发”的,不用特别保护,其余寄存器需要保护。(x86下只有eax, ecx, edx是易挥发的) ;栈需要16字节对齐,“call”指令会入栈一个8字节的返回值 ;整数前 4 个参数传入 RCX、RDX、R8 和 R9 中。其他参数传递到堆栈中。 ;常用寄存器有16个,分为x86通用寄存器以及r8-r15寄存器。 ;通用寄存器中,函数执行前后必须保持原始的寄存器有3个:是rbx、rbp、rsp。rx寄存器中,最后4个必须保持原值:r12、r13、r14、r15。 ;保持原值的意义是为了让当前函数有可信任的寄存器,减小在函数调用过程中的保存&恢复操作。除了rbp、rsp用于特定用途外,其余5个寄存器可随意使用。 ;通用寄存器中,不必假设保存值可随意使用的寄存器有5个:是rax、rcx、rdx、rdi、rsi。其中rax用于第一个返回寄存器(当 然也可以用于其它用途),rdx用于第二个返回寄存器(在调用函数时也用于第三个参数寄存器)。rcx用于第四个参数。rdi用于第一个参数。rsi用于 第二个函数参数。 ;r8、r9分配用于第5、第6个参数。

我要回帖

更多关于 xmm寄存器 的文章

 

随机推荐