21.4从32位值转化为64位值

  1. #include <stdint.h>
  2. int64_t f7 (int64_t a, int64_t b, int32_t c)
  3. {
  4. return a*b+c;
  5. };
  6. int64_t f7_main ()
  7. {
  8. return f7(12345678901234, 23456789012345, 12345);
  9. };

代码 21.7: MSVC 2012 /Ox /Ob1

  1. _a$ = 8 ; size = 8
  2. _b$ = 16 ; size = 8
  3. _c$ = 24 ; size = 4
  4. _f7 PROC
  5. push esi
  6. push DWORD PTR _b$[esp+4]
  7. push DWORD PTR _b$[esp+4]
  8. push DWORD PTR _a$[esp+12]
  9. push DWORD PTR _a$[esp+12]
  10. call __allmul ; long long multiplication
  11. mov ecx, eax
  12. mov eax, DWORD PTR _c$[esp]
  13. mov esi, edx
  14. cdq ; input: 32-bit value in EAX; output: 64-bit value in EDX:EAX
  15. add eax, ecx
  16. adc edx, esi
  17. pop esi
  18. ret 0
  19. _f7 ENDP
  20. _f7_main PROC
  21. push 12345 ; 00003039H
  22. push 5461 ; 00001555H
  23. push 1972608889 ; 75939f79H
  24. push 2874 ; 00000b3aH
  25. push 1942892530 ; 73ce2ff2H
  26. call _f7
  27. add esp, 20 ; 00000014H
  28. ret 0
  29. _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

  1. _f7:
  2. push edi
  3. push esi
  4. push ebx
  5. mov esi, DWORD PTR [esp+16]
  6. mov edi, DWORD PTR [esp+24]
  7. mov ebx, DWORD PTR [esp+20]
  8. mov ecx, DWORD PTR [esp+28]
  9. mov eax, esi
  10. mul edi
  11. imul ebx, edi
  12. imul ecx, esi
  13. mov esi, edx
  14. add ecx, ebx
  15. mov ebx, eax
  16. mov eax, DWORD PTR [esp+32]
  17. add esi, ecx
  18. cdq ; input: 32-bit value in EAX; output: 64-bit value in EDX:EAX
  19. add eax, ebx
  20. adc edx, esi
  21. pop ebx
  22. pop esi
  23. pop edi
  24. ret
  25. _f7_main:
  26. sub esp, 28
  27. mov DWORD PTR [esp+16], 12345 ; 00003039H
  28. mov DWORD PTR [esp+8], 1972608889 ; 75939f79H
  29. mov DWORD PTR [esp+12], 5461 ; 00001555H
  30. mov DWORD PTR [esp], 1942892530 ; 73ce2ff2H
  31. mov DWORD PTR [esp+4], 2874 ; 00000b3aH
  32. call
  33. _f7
  34. add esp, 28
  35. 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对。

http://conus.info/utils/ops_SIMD/