pxor zero, zero
neg strideD
自己跟自己异或,相当于清零,NEG取反操作;
lea pSrcRow, [pSrc]
这个就是讲pSrc的地址给pSrcRow,此时pSrcRow和pSrc具有不同的名字但是指向同一个内存;
mov pSrcRow, pSrc
这个相当于把pSrc的内容复制一次给pSrcRow,pSrcRow值的变动不会影响pSrc
imul strideD, 4
这个是无符号乘法,strideD = strideD*4;
这个等同于逻辑左移或算数左移
shl strideD, 2
sal strideD, 2
逻辑右移高位用0补,而算数右移高位不变(用原有的符号位补,负数为1,正数为0);
同时,
imul strideS, strideD, 4
也是合法的;
movq srcShift0, [pSrcCol]
这个是从内存[pSrcCol]读取64位到mm寄存器
指令后缀解读:
b;字节-8bit
w;字-16bit
d;双字-32bit
q;四字-64bit
a;align,对齐
u;unalign,不对齐
p;package,打包,128位同时处理
s;single,不打包,只处理部分
s;single precision,单精度,32bit
d;double precision,双精度,64Bit
l;低64位
h;高64位
下面摘自文章《Intro_to_Intel_Avx》
许多指令都是紧缩或标量形式:
即它们在寄存器的多个并行元素或者一个单一元素上执行——标记为 [P/S];
条目长度分为双精度或单精度浮点(简称为双精度和单精度),标记为 [D/S];
整数形式分为字节、字、双字和四字,标记为 [B/W/D/Q]。
整数形式有时还分为带符号形式和无符号形式,标记为 [S/U] ;
有些指令在寄存器的高区或低区运行,标记为 [H/L];
比如:
paddb,按字节对齐相加,paddw,按字对齐相加,paddd,按双字对齐相加。。。。
movlps,将源64位的两个单精度(s,32x2)的值打包(p)移到目的寄存器的低(l)64位中;
movss,将单精度(s)的值不打包(s)移致目的寄存器的低32位;
位数扩展指令:
punpcklbw srcShift0, zero;在低(l)64位,将字节(b)扩展成字(w),变成128位,原来的高64位被覆盖
12345678,-> 01020304
同理,有
punpckhbw, punpcklwd, punpckhwd....
移位指令:
pslldq ,按双四字(dq)(128bit)逻辑(l)左移(l)
pslld, 按双字(32位)逻辑左移
psllw,按字逻辑左移
psrlw,按字逻辑右移
psraw,按字算术右移
psrad,按双字算术右移
pshufd dst,src,imm
这个指令的意思是从src中取双字(32bit)按照imm(8位)的顺序复制给dst:
imm=01 11 00 00(1,3,0,0)
则: dst[0] = src[0]
dst[1] =src[0]
dst[2] = src[3]
dst[3] = src[1]
pshufd tmp, srcHigh, 01000001B
srcHigh的值:
tmp的值:
执行以上操作后:
验证!
movdqu m0, [t1 + t2],
[]内不能有内存
t1:寄存器/立即数
t2:寄存器/立即数
且t1,t2至少一个是寄存器,t1,和t2不能是内存
经典的用法是:movdqu m0, [t1 + 2*t2 + 3],其中t1,t2都是寄存器
当寄存器不够使用时(win32下只有7个可以使用),可以定义一个临时寄存器tmp,参数存在内存s1上,那么可以这么做:
mov tmp, s1
movdqu m0, [t1 + tmp],
这样不会报错,而
movdqu m0, [t1 + s1],会报错,其他例子如,s3是放在栈上的参数,栈上的参数是不能当做目的操作数的,在add,sub,mul等中只能做源操作数,为了改变s3,可以借助临时寄存器:
mov tmp, s3
dec tmp
mov s3, tmp
sub,cmp,test
sub:会改变目的操作室,cmp和test不会改变目的操作数,所以如果不想改变dst值,则可以
cmp dst, 8
改变的话则
sub dst, 8
psadbw
这个是按字节打包计算绝对差的和,低64位的绝对差和放在第一个字处[0:15],高64位的绝对差和放在第五个字处[64:79],其他字为0,
m0: 按字节,1-1 1-1- 1-1-1-1 -1-1-1-1-1-1-1-1
m1:按字节, 0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0
psadbw:m0,m1
得m0,按字,8-0-0-0-8-0-0-0
pavgb
这个是按字节打包计算平均值,结果仍然为字节
m0: 按字节,6-16-10-14-21-21-2-10-2-2-1-8-99-6-22-8
m1:按字节, 0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0
pagv m0, m1
得m0:3-8-5-7-10-10-1-5-1-1-0-4-49-3-11-4
pslldq
这个是按字节左移,而其他非dq结尾的移位操作都是以bit为单位进行移位,psrld,m1, 24.m1逻辑向右移位24bit,也就是3bytes,而pslldq m1,1表示逻辑左移1byte
移位操作可以配合paddd实现水平求和,如
m0按字为:6-16-10-14-21-21-2-10
如果计算低64位和,可以这么做:
punpcklwd m0,zer0
得按双字,6-16-10-14
pslldq m0,4
得0-6-16-10
重复,得到
6-16-10-14
0- 6- 16-10
0- 0- 6- 16
0- 0- 0- 6
利用paddd即可愿意得到结果
6-22-32-46
packssdw,packuswb
这两个指令某种意义上是punpckxxx的逆操作;
假如,m0按字节为152-0-0-0-123-0-0-0-140-0-0-0-78-0-0-0,我想把152-123-140-78store进一个连续(字节类型)的内存中,
如果直接store,保存的是152-0-0-0,达不到目的;
执行packssdw,m0
得152-0-123-0-140-0-78-152-0-123-0-140-0-78-0
执行packuswb,m0
得152-123-140-78-152-123-140-78-152-123-140-78-152-123-140-78