14.1 x86

以一种十分容易预测的方式编译的

  1. _a$ = 8 ; size = 4
  2. _f PROC
  3. push ebp
  4. mov ebp, esp
  5. mov eax, DWORD PTR _a$[ebp]
  6. cdq ; sign extend EAX to EDX:EAX
  7. mov ecx, 9
  8. idiv ecx
  9. pop ebp
  10. ret 0
  11. _f ENDP

IDIV 有符号数除法指令 64位的被除数分存在两个寄存器EDX:EAX,除数放在单个寄存器ECX中。运算结束后,商放在EAX,余数放在EDX。f()函数的返回值将包含在eax寄存器中,也就是说,在进行除法运算之后,值不会再放到其他位置,它已经在合适的地方了。正因为IDIV指令要求被除数分存在EDX:EAX里,所以需要在做除法前用CDQ指令将EAX中的值扩展成64位有符号数,就像MOVSX指令(13.1.1)所做的一样。如果我们切换到优化模式(/0x),我们会得到

清单14.2:MSVC优化模式

  1. _a$ = 8 ; size = 4
  2. _f PROC
  3. mov ecx, DWORD PTR _a$[esp-4]
  4. mov eax, 954437177 ; 38e38e39H
  5. imul ecx
  6. sar edx, 1
  7. mov eax, edx
  8. shr eax, 31 ; 0000001fH
  9. add eax, edx
  10. ret 0
  11. _f ENDP

这里将除法优化为乘法。乘法运算要快得多。使用这种技巧可以得到更高效的代码。

在编译器优化中,这也称为“strength reduction”

GCC4.4.1甚至在没有打开优化模式的情况下生成了和在MSVC下打开优化模式的生成的几乎一样的代码。

清单14.3 GCC 4.4.1 非优化模式

  1. public f
  2. f procnear
  3. arg_0 = dword ptr 8
  4. push ebp
  5. mov ebp, esp
  6. mov ecx, [ebp+arg_0]
  7. mov edx, 954437177 ; 38E38E39h
  8. mov eax, ecx
  9. imul edx
  10. sar edx, 1
  11. mov eax, ecx
  12. sar eax, 1Fh
  13. mov ecx, edx
  14. sub ecx, eax
  15. mov eax, ecx
  16. pop ebp
  17. retn
  18. f endp