英特尔处理器家族在IA-32指令集中提供了先进的数学处理能力,如果你的应用需要进行大量的数学运算的话,如音频和视频的处理,就可以利用下面要介绍的技术来提高应用程序的性能...

    本文由zengl.com站长对汇编教程英文版相应章节进行翻译得来。

    汇编教程英文版的下载地址:点此进入原百度盘   , 点此进入Dropbox网盘   , 点此进入Google Drive  (Dropbox与Google Drive里是Assembly_Language.pdf文档)

    另外再附加一个英特尔英文手册的共享链接地址:
    点此进入原百度盘点此进入Dropbox网盘点此进入Google Drive  (在某些例子中会用到,Dropbox与Google Drive里是intel_manual.pdf文档)

    Dropbox与Google Drive可能需要通过代理访问。

    本篇翻译对应汇编教程英文原著的第512页到第518页,对应原著第17章 (注意这里的页数不是页脚的页数,而是pdf电子档顶部,分页输入框中的页数,也就是包含了目录,前言部分的总页数,pdf电子档总页数是577,当前已翻译完的页数为518 / 577)。

    英特尔处理器家族在IA-32指令集中提供了先进的数学处理能力,如果你的应用需要进行大量的数学运算的话,如音频和视频的处理,就可以利用下面要介绍的技术来提高应用程序的性能。

    在之前的"汇编数据处理 (一)""汇编数据处理 (四) 数据处理结束篇"的这几篇文章中,我们介绍过SIMD(Single Instruction Multiple Data 单指令多数据模式)。该模式里的技术可以在单一的指令中处理多个数据,因此,适合于大量的复杂的数学运算,下面先对SIMD进行简单的回顾。

A Brief Review of SIMD -- SIMD的简单回顾

    IA-32平台的SIMD主要由以下4个技术组成:
  • Multimedia Extensions (MMX)
    多媒体扩展技术
  • Streaming SIMD Extensions (SSE)
    奔腾三处理器中引入的MMX的扩充指令集,或者叫做MMX的超集
  • Streaming SIMD Extensions Second Implementation (SSE2)
    第二代SSE技术
  • Streaming SIMD Extension Third Implementation (SSE3)
    第三代SSE技术
    从奔腾MMX和奔腾二处理器开始,支持MMX技术。奔腾三开始支持MMX和SSE技术。奔腾四开始支持MMX,SSE和SSE2技术。奔腾4TH(超线程)和Xeon(志强)处理器开始支持MMX,SSE,SSE2以及SSE3技术。

    SIMD的主要优势就在于,可以在单一的指令中并行的执行多个数学运算,为了能实现并行的数学运算,MMX和SSE技术都提供了一些额外的寄存器,可以用于存储压缩的数据(也就是在单一的寄存器里存储多个数据),这样,MMX和SSE的指令就可以对这些单一寄存器中的多个数据同时进行数学运算了。

    下面先对MMX,SSE,SSE2及SSE3做个简要的概述。

MMX:

    MMX技术主要是对整数类型的数据进行操作,该技术提供了三种新的整数类型:
  • 64-bit packed byte integers (contains eight single-byte integer values)
    64位压缩字节整数 (包含8个单字节整数值)
  • 64-bit packed word integers (contains four word integer values)
    64位压缩字整数 (包含4个16位字大小的整数值)
  • 64-bit packed doubleword integers (contains two doubleword integer values)
    64位压缩双字整数 (包含2个32位双字大小的整数值)
    这三个整数类型的具体结构可以参考"汇编数据处理 (二)"文章里的图1 ,由于MMX技术所使用的整数类型是64位的,因此,不能用一般的通用寄存器来存储这些数据,取而代之的是8个80位的MMX寄存器,这8个寄存器被取名为MMX0到MMX7,另外,这些MMX寄存器在实际使用时,会被映射到FPU浮点运算单元的R0到R7寄存器中,而且MMX寄存器在FPU里是静态的,它们并不会被组织成寄存器栈的形式,在之前的"高级数学运算 (一) FPU寄存器介绍"的文章中,我们介绍过:FPU里的8个数据寄存器R0到R7在进行浮点运算时,会被组织成寄存器栈的形式,但是当这些寄存器被当作MMX寄存器使用时,它们就不再是寄存器栈了,而是普通的相互独立的寄存器,也就是向MMX0中存储数据时,MMX0里的之前写入的数据,不会移入MMX1中。

    可以看出来,FPU寄存器既可以存储MMX数据,又可以存储FPU浮点数据。

    当FPU寄存器的exponent指数部分的二进制值全部被设置为1时,存储的就是MMX数据,这里提到的exponent是浮点数科学记数的组成部分,详情可以参考之前的"汇编数据处理 (三) 浮点数"的文章里的图1图2。其他情况下存储的就是普通的双精度扩展格式的浮点数。

    当FPU寄存器被用于存储MMX的数据时,FPU的Tag寄存器的值就会被破坏掉,也就是无法用Tag寄存器来检测FPU数据寄存器里的数据类型,有关Tag寄存器的作用可以参考之前的"高级数学运算 (一) FPU寄存器介绍"文章中的图1 。因此,在执行MMX相关的指令之前,最好先用FSAVE或FXSAVE指令将FPU寄存器的完整工作状态(包括数据寄存器的值)保存到内存里,当执行完MMX相关的指令后,再通过FRSTOR或FXRSTOR指令来恢复之前的FPU的工作状态。FSAVE与FRSTOR指令的详细用法可以参考之前的"高级数学运算 (四) 高级运算结束篇"的文章。另外,FSAVE,FRSTOR与FXSAVE,FXRSTOR指令的区别在于,前者只会保存和恢复FPU的状态,而后者则可以保存和恢复FPU,MMX以及SSE的状态。

    当MMX相关的指令执行完毕后,还需要使用EMMS指令来清除FPU的Tag寄存器的值,这样,后面和FPU相关的浮点运算指令才能被正确执行。

    MMX技术除了引入了64位的压缩整数类型外,还相应的引入了一些额外的指令,这些指令可以同时操作MMX寄存器里的多个整数,例如之前的"汇编数据处理 (二)"文章中提到的MOVQ指令等。

SSE:

    SSE技术的主要目的是将SIMD的单指令多数据的操作模式给应用到浮点数上。我们在之前的"汇编数据处理 (四) 数据处理结束篇"的文章里,对SSE技术做过介绍,该技术引入了128位的压缩单精度浮点数类型(可以包含4个32位的单精度浮点值),为了能容纳这种新的128位的数据类型,SSE技术中就引入了8个128位的XMM寄存器,这8个寄存器被命名为XMM0到XMM7 ,在SSE的浮点数学运算中就会使用到这些XMM寄存器。

    SSE还相应的引入了一些新的指令,这些指令可以对压缩单精度浮点数里的4个浮点数同时进行运算。

SSE2:

    SSE2是对SSE技术的扩展,它新增了5个新的数据类型:
  • 128-bit packed double-precision floating-point value (contains two double-precision values)
    128位压缩双精度浮点数 (包含2个双精度浮点值)
  • 128-bit packed byte integer value (contains 16 single-byte integer values)
    128位压缩字节整数 (包含16个单字节整数值)
  • 128-bit packed word integer value (contains eight word integer values)
    128位压缩字整数 (包含8个16位字大小的整数值)
  • 128-bit packed doubleword integer value (contains four doubleword integer values)
    128位压缩双字整数 (包含4个32位双字大小的整数值)
  • 128-bit packed quadword integer value (contains two quadword integer values)
    128位压缩四字整数 (包含2个64位的四字大小的整数值)
    这些新的数据类型都是使用128位的XMM寄存器来进行存储的。SSE2同样也提供了一些额外的指令用于对这些压缩的整数和浮点数进行处理。

    SSE3技术并没有在SSE2的基础上新增什么额外的数据类型,只是提供了一些更加先进的数据处理指令。

Detecting Supported SIMD Operations 检测当前处理器所支持的SIMD技术:

    在学习具体的MMX和SSE的指令之前,最好先弄清楚你的处理器到底支持哪些技术,下面将通过汇编程式来检测处理器是否支持MMX,SSE,SSE2或SSE3技术。

Detecting support 检测支持的SIMD技术:

    通过CPUID指令就可以检测出当前处理器所支持的SIMD技术。

    在之前的"汇编开发示例 (一)"的文章中,我们通过示例演示了CPUID指令的用法,不过在该示例里,我们只是用CPUID指令来获取处理器的供应商ID字符串信息,其实,该指令还可以获取到处理器的其他信息,我们可以通过设置EAX的值,来控制CPUID指令返回什么类型的信息。

    当EAX的值为1时,CPUID指令将返回处理器的签名信息,这些签名信息会包含在ECX与EDX这两个寄存器里,这些寄存器的某些二进制位就可以用于检测当前处理器所支持的SIMD技术,如下表所示:

REGISTER
寄存器
Bit
二进制位
Feature
所支持的SIMD技术
EDX 23 Supports MMX instructions
支持MMX指令集
EDX 25 Supports SSE instructions
支持SSE指令集
EDX 26 Supports SSE2 instructions
支持SSE2指令集
ECX 0 Supports SSE3 instructions
支持SSE3指令集

    当上表中对应的二进制被设置时,则说明支持对应的功能,反之,清零时,则说明不支持对应的功能。在汇编程式里,可以通过TEST指令和eflags寄存器里的ZF标志位来进行检测。

    要使用TEST对二进制位进行检测,还必须知道这些二进制位所对应的十六进制值,如下图所示:


图1

    根据上图,要用TEST指令检测MMX功能的话,可以使用如下代码:

test $0x00800000, %edx
jz notfound


    当eflags寄存器的ZF标志被设置时,则jz指令就会跳转到notfound处,表示当前处理器不支持MMX功能,类似的,检测SSE功能的汇编代码如下:

test $0x02000000, %edx
jz notfound


    检测SSE2的汇编代码如下:

test $0x04000000, %edx
jz notfound


    最后,检测SSE3的代码如下:

test $0x00000001, %ecx
jz notfound


    接下来,我们将上面的代码整合到自己的汇编程式里,就可以检测出当前处理器所支持的SIMD技术了。

SIMD feature program 检测SIMD的汇编程式:

    下面的features.s程式就用于检测出处理器支持的SIMD技术:

# features.s - Determine MMX, SSE, SSE2, and SSE3 capabilities
.section .data
gotmmx:
	.asciz "Supports MMX"
gotsse:
	.asciz "Supports SSE"
gotsse2:
	.asciz "Supports SSE2"
gotsse3:
	.asciz "Supports SSE3"
output:
	.asciz "%s\n"
.section .bss
	.lcomm ecxdata, 4
	.lcomm edxdata, 4
.section .text
.globl _start
_start:
	nop
	movl $1, %eax
	cpuid
	movl %ecx, ecxdata
	movl %edx, edxdata
	test $0x00800000, %edx
	jz done
	pushl $gotmmx
	pushl $output
	call printf
	addl $8, %esp
	movl edxdata, %edx
	test $0x02000000, %edx
	jz done
	pushl $gotsse
	pushl $output
	call printf
	addl $8, %esp
	movl edxdata, %edx
	test $0x04000000, %edx
	jz done
	pushl $gotsse2
	pushl $output
	call printf
	addl $8, %esp
	movl ecxdata, %ecx
	test $0x00000001, %ecx
	jz done
	pushl $gotsse3
	pushl $output
	call printf
	addl $8, %esp
done:
	pushl $0
	call exit


    上面的features.s程式会将检测到的处理器所支持的SIMD功能都显示出来,首先会通过movl $1, %eax指令将EAX设置为1,这样,随后执行的CPUID指令将返回处理器的签名信息,然后我们就可以通过TEST指令来对ECX和EDX里的相关二进制位进行检测,由于要进行4次检测,因此,有必要将ECX与EDX里的值分别存储到.bss段的ecxdata与edxdata的内存位置处。

    此外,由于SIMD的功能是向上累积的,例如,假设处理器不支持MMX功能,那么其他的后面新增的SSE功能也就不支持,还比如,假设处理器不支持SSE2功能,那么后面新增的SSE3功能就肯定不支持,就不需要再进行SSE3的检测了,并且,上面的代码是以MMX,SSE,SSE2,SSE3的顺序来进行检测的,因此,在某个TEST指令进行检测后,如果不支持对应的功能,就可以直接通过jz done指令跳转到done结束位置。

    在TEST指令进行检测后,如果支持对应的功能,就会通过printf的C库函数将支持某功能的字符串信息给显示出来,由于用到了C库函数,因此,在链接时,需要将C库文件及动态库的加载器给添加到链接参数里。

    features.s程式的编译运行结果如下:

$ as -o features.o features.s 
$ ld -dynamic-linker /lib/ld-linux.so.2 -lc -o features features.o
$ ./features 
Supports MMX
Supports SSE
Supports SSE2
Supports SSE3
$


    可以看出来,作者的处理器支持MMX,SSE,SSE2和SSE3的功能。

    本篇就到这里,下一篇将介绍MMX技术中引入的汇编指令。

    OK,休息,休息一下 o(∩_∩)o~~
上下篇

下一篇: 使用IA-32平台提供的高级功能 (二)

上一篇: 汇编中使用文件 (三) 使用文件结束篇

相关文章

汇编开发相关工具 (一)

调用汇编模块里的函数 (三) 静态库、共享库、本章结束篇

汇编里使用Linux系统调用 (三) 系统调用结束篇

Moving Data 汇编数据移动 (四) 结束篇 栈操作

汇编函数的定义和使用 (三) 汇编函数结束篇

汇编字符串操作 (一)