zoukankan      html  css  js  c++  java
  • MMX指令集

    这篇来介绍intel cpu的高级特性,SIMD-单指令多数据,从名字来看,就是执行一条指令可以计算多个数据。先从最简单的mmx指令集来看,在寄存器那篇已经提 到,mmx有 mm0-mm7 共8个64位寄存器,但是寄存器并非独立寄存器,而是复用了上篇说到的fpu数据堆栈寄存器,也就是说使用mmx指令集会破坏fpu的计算,如果同时使用 着两种特性,一定要注意这点,避免出现莫名的错误。

    首先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指令集
    "
        ssestring: .asciz "支持sse指令集
    "
        sse2string: .asciz "支持sse2指令集
    "
        sse3string: .asciz "支持sse3指令集
    "
    
    .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需要三个步骤:

    • 从整数值创建打包整数,载入mmx寄存器
    • 使用mmx指令集计算
    • 从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
    "
    .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指令集的,这也对我们手工汇编优化程序提供了很大的空间,但是需要注意打包整数的效率损耗。

    另外,intel除了mmx指令集,另有SIMD指令如sse指令集,将会再下篇详细说明。

    转载:http://fancymore.com/reading/assembler-mmx-instruct.html

  • 相关阅读:
    基础【五】字典的操作方法
    基础【四】列表的操作方法
    基础【三】字符串的操作方法
    基础【二】while循环及基本运算符
    基础【一】基础数据类型
    C++ string 深拷贝 与 浅拷贝
    多进程引用的动态链接库中的全局变量问题
    C++ 在类里面使用多线程技术
    openwrt 解决包依赖关系
    lua 的元表与元方法
  • 原文地址:https://www.cnblogs.com/DeeLMind/p/7367775.html
Copyright © 2011-2022 走看看