MMX的数据结构 & MMX指令集

来源:互联网 发布:盛大网页游戏矩阵 编辑:程序博客网 时间:2024/05/16 01:13

源地址:http://dev.gameres.com/Program/Other/MMXData.htm
       http://www.360doc.com/content/12/1129/18/1317564_251021032.shtml
MMX的数据结构
    多媒体软件具有如下显著的特点:
1、 小整型数据类型(图形数据为8位 ,声频数据为16位)
2、 对小整型数据的频繁且重复的计算操作(例如被频繁调用的核心算法);
3、 许多操作具有内存的并行性(例如对大量的数据进行同一个加,减或乘法运算操作);
    MMX技术设计了一套基本的,通用的紧缩整形指令,共57条。
所谓“紧缩整形数据”是指多个8/16/32位的整形数据组合成为一个64位的数据.MMX指令主要就是使用
这种紧缩整形数据,它又分成4种整形类型:紧缩字节、紧缩字、紧缩双字、紧缩4字
    *紧缩字节(Packed Byte): 8个字节组合成一个64位的数据;
    *紧缩字 (Packed Word): 4个字组合成一个64位的数据;
    *紧缩双字(Packed Doubleword): 2个双字组合成一个64位的数据;
    *紧缩4字 (Packed Quadword):一个64位数据
    这样一条MMX指令就能够同时处理8/4/2个数据单元,这就是所谓的“单指令多数据SIMD”结构。这种结构
是MMX技术把机器性能提高的最根本因素。
    为了方便使用64位紧缩整形数据,MMX技术含有8个64位的MMX寄存器(MM0-MM7),只有MMX指令可以使用MMX寄存器。值得一提的是,MMX寄存器是随机存取的,但实际上是借用了8个浮点数据寄存器实现的。浮点处理单元FPU有8个浮点寄存器FPR,以堆栈方式存取。每个浮点数据寄存器有80位,高16位用于指数和符号,低64位用于有效数字。MMX利用其64位有效数字部分用做随机存取的64位的MMX寄存器。也就是说MMX寄存器并非独立寄存器,而是复用了fpu数据堆栈寄存器,也就是说使用mmx指令集会破坏fpu的计算,如果同时使用着两种特性,一定要注意这点,避免出现莫名的错误。


MMX指令集
1、算术运算:
PADD[B、W、D] 环绕加[字节,字,双字]
PADDS[B , W] 有符号饱和加[字节,字]
PADDUS[B , W] 无符号饱和加[字节,字]
PSUB[B、W、D] 环绕减[字节,字,双字]
PSUBS[B,W] 有符号饱和减[字节,字]
PSUBUS[D,W] 无符号饱和减[字节,字]
PMULHW 紧缩字乘后取高位
PMULLW 紧缩字乘后取低位
PMADDWD 紧缩字乘,积相加
2、比较:
PCMPEQ[B,W,D] 紧缩比较是否相等[字节,字,双字]
PCMPGT[B,W,D] 紧缩比较是否大于[字节,字,双字]
3、类型转换:
PACKUSWB 按无符号饱和压缩[字成字节]
PACKSS[WB,DW] 按有符号饱和压缩[字/双字成/字节/字]
PUNPCKH[BW,WD,DQ] 扩展高位[字节,字,双字成字,双字,4字]
PUNPCKL[BW,WD,DQ] 扩展地位[字节,字,双字成字,双字,4字]
4、逻辑运算:
PAND 紧缩逻辑与
PANDN 紧缩逻辑与非
POR 紧缩逻辑或
PXOR 紧缩逻辑异或
5、位移:
PSLL[W,D,Q] 紧缩逻辑左移[字,双字,4字]
PSRL[W,D,Q] 紧缩逻辑右移[字,双字,4字]
PSRA[W,D] 紧缩算术右移[字,双字]
7、数据传送:
MOV[D,Q] 从MMX寄存器传人/传出[双字/4字]
8、状态清除
EMMS 清除MMX状态


首先mmx指令集需要cpu的支持,但不是所有cpu都支持,不然也不会称之为高级特性 了,所以在使用之前需要检测,检测指令为cpuid,获得cpu的特性,cpuid虽然只有一条指令,但是其隐含的内容太多,这里仅仅介绍检测SIMD指令集所需要的部分,其他一些信息可参阅Intel 手册获得。
当eax为1时,cpuid指令返回cpu签名信息,放入ecx和edx寄存器中,相应位为1表示支持。检测SIMD指令集的结果如下:
寄存器 特性
EDX      23 支持MMX
EDX 25 支持SSE
EDX 26 支持SSE2
ECX 0 支持SSE3

具体检测代码如下(AT&T 语法):
.section .data
mmxstring: .asciz "支持mmx指令集\n"
ssestring: .asciz "支持sse指令集\n"
sse2string: .asciz "支持sse2指令集\n"
sse3string: .asciz "支持sse3指令集\n"

.section .text
.globl _main
_main:
movl $1, %eax
cpuid

mmxop:
test $0x800000, %edx
jz sseop
pushl $mmxstring
call _printf

sseop:
test $0x2000000, %edx
jz sse2op
pushl $ssestring
call _printf

sse2op:
test $0x4000000, %edx
jz sse3op
pushl $sse2string
call _printf

sse3op:
test $0x01, %ecx
jz end
pushl $sse3string
call _printf
end:
pushl $0
call _exit
      

      下面正式开始mmx指令集的介绍,使用mmx需要三个步骤:
1、从整数值创建打包整数,载入mmx寄存器
2、使用mmx指令集计算
3、从mmx获得结果,存入内存
      第一个和最后一个步骤比较简单,仅仅是数据移动而已,这里提到打包,因为这里要单指令多数据,所以需要把多数据合成一个操作数进行计算,存入64位的mmx寄存器中,打包的过程就是把 8个字节/4个字/2个双字合成一个64位数据。
     从加减法说起,对于普通数据,如果数据溢出可以置标记位,但是对于多数据的运算,由于同时计算多个加法,就不能单纯的设置标志,对mmx计算有几种情况:

环绕运算 截断其值,丢弃进位
带符号饱和 最大/最小 带符号值
无符号饱和 最大/最小 无符号值
      其中饱和运算的预设值根据结果的位数决定,有符号8位最大为127,如果超过127,结果按127计算,其他情况与此类似,这里方便与一些图形处理,比如色彩黑色为0,为无符号最小值,小于其值也按黑色处理。
      好 了,到此可以看一下具体的指令,这里的指令有相同的格式,instruction source, destination;其中source可以是mmx寄存器或者64位内存,destination为mmx寄存器。这是AT&T语法,对于 MASM语法源目的操作数相反。
   指令 说明
paddb 环绕打包字节整数加法
paddw 环绕打包字整数加法
paddd 环绕打包双字整数加法
paddsb 带符号饱和打包字节整数加法
paddsw 带符号饱和打包字整数加法
paddusb    无符号饱和打包字节整数加法
paddusw 无符号饱和打包字整数加法
psubb 环绕打包字节整数减法
psubw 环绕打包字整数减法
psubd 环绕打包双字整数减法
psubsb     带符号饱和打包字节整数减法
psubsw    带符号饱和打包字整数减法
psubusb 无符号饱和打包字节整数减法
psubusw 无符号饱和打包字整数减法
下面以AT&T加法为例进行说明,这里以饱和方式计算4个无符号字之和:

# add four word
# output : result is 18932, 7631, 65535, 510
.section .data
value1: .short 12300, 2384, 60000, 456
value2: .short 6632, 5247, 40000, 54

outstring: .asciz "result is %u, %u, %u, %u\n"
.section .text
.globl _main
_main:
movq value1, %mm0
movq value2, %mm1
paddusw %mm1, %mm0
movq %mm0, value1

movl $value1, %ebx
xorl %eax, %eax
movw 6(%ebx), %ax
pushl %eax
movw 4(%ebx), %ax
pushl %eax
movw 2(%ebx), %ax
pushl %eax
movw (%ebx), %ax
pushl %eax

pushl $outstring
call _printf

pushl $0
call _exit
      movq 指令把内存中的数据传送至mmx寄存器,如果数据之前在内存中不是连续的,则需要集中存放,即进行打包,之后使用paddusw进行加法计算,输出时 word需转化成dword放入堆栈,可以看到以饱和方式第三个结果为65535,即16位无符号数的最大值。从这里例子可以看出,通过一条指令计算了四 个word整数相加,很大程度上提高了计算的效率,但是同时需要注意,整数的打包以及传送过程也需要耗时,如果打包操作很多,结果不是提高效率而是降低效率了。
     mmx指令集的加法根据需要有饱和方式和环绕方式计算,但对于乘法而言,由于结果的宽度可能是操作数的两倍,所以两种方式看上去都不合适,所以intel提供了两个指令,一个得到计算结果的低字节,另一个得到计算结果的高字节。

    指令                                  描述
pmulluw         对无符号16位整数相乘取结果低16位
pmulhuw 对无符号16位整数相乘取结果高16位
pmullw          对有符号16位整数相乘取结果低16位
pmulhw         对有符号16位整数相乘取结果高16位
pmaddwd 对4个带符号整数相乘,高位两个结果相加存入高32位,低位相同
      mmx 指令集还提供对四字值进行布尔逻辑操作和移位指令:
  指令                      描述
pand 对源和目标操作数按位与操作
pandn 对目标操作数进行按位逻辑非操作,然后对源和目标操作数按位与操作
por           对源和目标操作数按位或操作
pxor         对源和目标操作数按位异或操作
psll          对目标操作数执行逻辑左移操作,使用0填充空位
psra 对目标操作数执行逻辑右移操作,使用0填充空位


其AT&T指令格式如下:
pand source, destination
其中source是mmx寄存器或者64位内存,destination必须是mmx寄存器。移位指令可以使用字,双字或者四字操作数,还有移位的位置数量。MASM格式的源目的操作数相反。

mmx构架提供了用于比较两个值的指令:
     指令                              描述
pcmpeqb 比较打包字节整数值的相等性
pcmpeqw 比较打包字整数值的相等性
pcmpeqd 比较打包双字整数值的相等性
pcmpgtb 判断打包字节整数值是否大于另一个
pcmpgtw 判断打包字整数值是否大于另一个
pcmpgtd 判断打包双字整数值是否大于另一个
    因为mmx同时比较多个数据,所以不能设置标志,替换的做法是把判断结果放到目标打包整数值中,如果打包整数值满足对比提交,就把结果设置为全1,否则设置为全0。
    由于mmx指令集并非所有cpu都可以支持,所以对c语言这种编译通用性的程序而言,是不会贸然使用mmx指令集的,这也对我们手工汇编优化程序提供了很大的空间,但是需要注意打包整数的效率损耗。




0 0