21.4从32位值转化为64位值
#include <stdint.h>
int64_t f7 (int64_t a, int64_t b, int32_t c)
{
return a*b+c;
};
int64_t f7_main ()
{
return f7(12345678901234, 23456789012345, 12345);
};
代码 21.7: MSVC 2012 /Ox /Ob1
_a$ = 8 ; size = 8
_b$ = 16 ; size = 8
_c$ = 24 ; size = 4
_f7 PROC
push esi
push DWORD PTR _b$[esp+4]
push DWORD PTR _b$[esp+4]
push DWORD PTR _a$[esp+12]
push DWORD PTR _a$[esp+12]
call __allmul ; long long multiplication
mov ecx, eax
mov eax, DWORD PTR _c$[esp]
mov esi, edx
cdq ; input: 32-bit value in EAX; output: 64-bit value in EDX:EAX
add eax, ecx
adc edx, esi
pop esi
ret 0
_f7 ENDP
_f7_main PROC
push 12345 ; 00003039H
push 5461 ; 00001555H
push 1972608889 ; 75939f79H
push 2874 ; 00000b3aH
push 1942892530 ; 73ce2ff2H
call _f7
add esp, 20 ; 00000014H
ret 0
_f7_main ENDP
这里我们有必要将有符号的32位值从c转化为有符号的64位值。无符号值的转化简单了当:所有的高位部分全部置0。但是这样不适合有符号的数据类型:符号标志应复制到结果中的高位部分。这里用到的指令是CDQ,它从EAX中取出数值,将其变为64位并存放到EDX:EAX这一对寄存器中。换句话说,指令CDQ从EAX中获取符号(通过EAX中最重要的位),并根据它来设置EDX中所有位为0还是为1。它的操作类似于指令MOVSX(13.1.1)。
代码 21.8: GCC 4.8.1 -O3 -fno-inline
_f7:
push edi
push esi
push ebx
mov esi, DWORD PTR [esp+16]
mov edi, DWORD PTR [esp+24]
mov ebx, DWORD PTR [esp+20]
mov ecx, DWORD PTR [esp+28]
mov eax, esi
mul edi
imul ebx, edi
imul ecx, esi
mov esi, edx
add ecx, ebx
mov ebx, eax
mov eax, DWORD PTR [esp+32]
add esi, ecx
cdq ; input: 32-bit value in EAX; output: 64-bit value in EDX:EAX
add eax, ebx
adc edx, esi
pop ebx
pop esi
pop edi
ret
_f7_main:
sub esp, 28
mov DWORD PTR [esp+16], 12345 ; 00003039H
mov DWORD PTR [esp+8], 1972608889 ; 75939f79H
mov DWORD PTR [esp+12], 5461 ; 00001555H
mov DWORD PTR [esp], 1942892530 ; 73ce2ff2H
mov DWORD PTR [esp+4], 2874 ; 00000b3aH
call
_f7
add esp, 28
ret
GCC生成的汇编代码跟MSVC一样,但是在函数中内联乘法代码。 更多:32位值在16位环境中(30.4) # SIMD
SIMD是Single Instruction, Multiple Data的首字母。简单说就是单指令多数据流。
就像FPU,FPU看起来更像独立于x86处理器。
SIMD开始于MMX x86。8个新的64位寄存器MM0-MM7被添加。
每个MMX寄存器包含2个32-bit值/4个16-bit值/8字节。比如可以通过一次添加两个值到MMX寄存器来添加8个8-bit(字节)。
一个简单的例子就是图形编辑器,将图像表示为一个二维数组,当用户改变图像的亮度,编辑器必须添加每个像素的差值。为了简单起见,将每个像素定义为一个8位字节,就可以同时改变8个像素的亮度。
当使用MMX的时候,这些寄存器实际上位于FPU寄存器。所以可以同时使用FPU和MMX寄存器。有人可能会认为,intel基于晶体管保存,事实上,这种共生关系的原因是:老的操作系统不知道额外的CPU寄存器,上下文切换是不会保存这些寄存器,可以节省FPU寄存器。这样激活MMX的CPU+旧的操作系统+利用MMX特性的处理器=所有一起工作。
SSE-SIMD寄存器扩展至128bits,独立于FPU。
AVX-另一种256bits扩展。
实际应用还包括内存复制(memcpy)和内存比较(memcmp)等等。
一个例子是:DES加密算法需要64-bits block,56-bits key,加密块生成64位结果。DES算法可以认为是一个非常大的电子电路,带有网格和AND/OR/NOT门。
Bitslice DES2—可以同时处理块和密钥。比如说unsigned int类型变量在X86下可以容纳32位,因此,使用64+56 unsigned int类型的变量,可以同时存储32个blocks-keys对。
我写了一个爆破Oracle RDBMS密码/哈希(基于DES)的工具。稍微修改了DES算法(SSE2和AVX)现在可以同时加密128或256block-keys对。