当前位置: 代码迷 >> 汇编语言 >> 请SSE/SSE2相熟的前辈指点下指令,比较简单
  详细解决方案

请SSE/SSE2相熟的前辈指点下指令,比较简单

热度:367   发布时间:2016-05-02 04:28:01.0
请SSE/SSE2熟悉的前辈指点下指令,比较简单
我刚学用汇编来写SSE/2相关操作的东西,所以对指令的运用相当不熟练,写一条都要查好久。。。

SSE里有这么个指令:MASKMOVQ,而SSE2又加了这个指令:MASKMOVDQU;
这俩用法差不多,只不过一个针对MMX寄存器,一个针对XMM寄存器,位数不同而已,都是:

指令  源数据寄存器, 移位表寄存器

这样的格式,实际上指令作用是把 64或128位的源数据拷贝到对应位数的指针里,但命令里没有指针,指针要在edi里面提供;
而之所以特殊是这个指令不是移动全部64或128位数据,只移动指定的字节,所以就有了第二个操作数:移位表。
它是跟源数据相同位数的寄存器,比如64位就是mm(可以指定最多8个字节)、128位就是xmm(可以指定最多16个字节);
当然一般没人会在移动整个(8或16)字节时来这么复杂的搞,用其他完整移动的指令就好了。。

而移位表的表示法是取每个字节的最高位,比如最低位[0-7]的字节,bit7 = 1 表示复制,bit7 = 0 表示不复制,用十进制说就是第一个字节是128就表示复制;而32678就表示复制第二个字节 bit15 = 1;以此类推。。。

以上是我读手册里自己总结的,应该是这个作用把???
而它能用在很多复杂的情况下,不但移动整个数据高位几个字节,低位几个字节可以用,甚至随意自由的移动中间或相间隔的几个字节都可以,但我现在还没要求这么复杂。

我现在只需要用在从低位开始拷贝多少(1-7,或1-15)个字节就行,但要想实现这个,就要首先把移位表这个寄存器填充好,说了一大堆终于说到我要问的了,我想问下如何构造这个移位表啊???
准确的说,如何快速构造移位表???因为如果是使用通用寄存器与MMX/XMM寄存器交叉循环一点点的算,一个个字节为赋值的话(就是最傻瓜的原理),我肯定是会的。。。
但我觉得肯定有比较简单快速的方法的,甚至只通过MMX/XMM寄存器相关的位运算等指令左右移动啥的就可以实现,所以我说我的问题实际上并不复杂,但是对于32位数的位运算我脑子的反应就很不灵活,只会照搬照抄,别说一下子到了128位了。。。

上面移位表的构造原理说了是根据每个字节的最高位,而我自己感觉为了快速生成移位表,要复制的那个字节对应的其他7个位都是1也行,这样应该会构造的更快吧,比如255表示复制第一个字节,65536表示复制第二个字节……

这样的话其实我自己想了个最基本的简单方法,拿128位寄存器举例:
将一个xmm先置0,再 -1,这样128个位都是1咯,
然后 先 pslldq xmm, (16-n);
接着 再 psrldq xmm, (16-n);
其中 n = 要从低位开始复制的字节数,是不是就会把右边不复制的那些位弄成0了???
我不知道这样想对不对,也不知道这么算是不是比较快。。。
因为连最基本的我都有点诧异:
在x86汇编里,我们将一个32位寄存器每一位全都搞成1,也就是说 reg32 = -1 呗,这样可以:
mov eax, -1;
或者专业一点:
xor eax, eax;
dec eax
都可以,但是这个xmm寄存器怎么弄成128位全是1啊?莫非是:
xor eax, eax
inc eax
movd xmm1, eax
pxor xmm0, xmm0
psubq xmm0, xmm1
movdqa xmm1, xmm0
punpckldq xmm0, xmm1
哈哈,我觉得有点扯淡了,这样确实能把128位全部置1,但我觉得这肯定不会是构造移位表应该采用的方法,所以越想越糊涂,只好来请教前辈了。。。

教教我,如何能快速填充好移位表,用1-7位 或 1-15位都行,我明白套路即可。。。多谢!
------解决思路----------------------
额 ... 怎么赶脚 LZ可以洗洗碎了 ? 这两条指令 都没有提供关于 mem 的读写操作 ... 也就是说要想从新写入内存 还得 再用一次 movd/movq/movqda ... 你去看看 这几条指令的吞吐量和指令延迟 为毛感觉没多大效率提升的 ... 如果lz 想要过滤掉内存中指定的一个 byte/word/dword .... 估计还不如这样写 ... (我说估计是因为我没写过 无法测试效率 ...)
        jmp _SSE_ROUTINE_MAIN_LOOP
_SSE_ROUTINE_MAIN_LOOP_ALL_COPY
movdqa [edi], xmm0 ; 完整的 一次 movdqa
_SSE_ROUTINE_MAIN_LOOP:

movdqa xmm0, [esi] ; 取源内存 至 xmm0 
movdqa xmm3, xmm7 ; xmm7 之前准备好的 掩码 比如想要 过滤 0xFFFFFFFF 那么 xmm7 就是 0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF

pcmpeqd xmm3, xmm0 ; 如果 里面有掩码字节 那么就会有 0xFFFFFFFF 
movmskps ecx, xmm3 ; 提取 掩码命中次数 ... 如果 里面么有 掩码命中 那么 ecx 为 0 有的话 就不会是 0 

test ecx, ecx ; 测试是否有掩码命中 ?
je _SSE_ROUTINE_MAIN_LOOP_ALL_COPY ; 继续复制 ... 当前字节中没有要过滤的字节 ... 
#if 0       
; 根据当前 pcmpeqd xmm3, xmm0 的时候 xmm3 中的 0xFFFFFFFF 情况 过滤指定的dword ... 
#else
        ; 根据ecx 中的情况做张 表 ... 呵呵 这样可以精确到每次 要过滤的字节数 ... 
#endif

------解决思路----------------------
引用:
lz 你要觉得 sse 做内存复制很慢的话 我建议 在满足下列条件的情况下 你用标准库的memcpy 和 自己写的试一下 ... 看看那个快 ...
src/des 地址十六字节对齐 ... 
复制数据量 大于 512 bytes ...

标准库的memcpy 在满足这两个条件的情况下 会使用 sse 的 movdqa 来复制 ...

CopyUp:
;
; First, see if we can use a "fast" copy SSE2 routine
        ; block size greater than min threshold?
        cmp     ecx,080h
        jb      Dword_align
        ; SSE2 supported?
        cmp     DWORD PTR __sse2_available,0
        je      Dword_align
        ; alignments equal?
        push    edi
        push    esi
        and     edi,15
        and     esi,15
        cmp     edi,esi
        pop     esi
        pop     edi
        jne     Dword_align

        ; do fast SSE2 copy, params already set
        jmp     _VEC_memcpy
        ; no return
复制数据量 大于 512 bytes ... -> 128 bytes 
------解决思路----------------------
查表法,两条指令完成转换,但是要占用256字节的表空间,典型的以空间换取时间的算法。