21.1参数的传递,加法,减法

  1. #include <stdint.h>
  2. uint64_t f1 (uint64_t a, uint64_t b)
  3. {
  4. return a+b;
  5. };
  6. void f1_test ()
  7. {
  8. #ifdef __GNUC__
  9. printf ("%lld", f1(12345678901234, 23456789012345));
  10. #else
  11. printf ("%I64d", f1(12345678901234, 23456789012345));
  12. #endif
  13. };
  14. uint64_t f2 (uint64_t a, uint64_t b)
  15. {
  16. return a-b;
  17. };

代码 21.1: MSVC 2012 /Ox /Ob1

  1. _a$ = 8 ; size = 8
  2. _b$ = 16 ; size = 8
  3. _f1 PROC
  4. mov eax, DWORD PTR _a$[esp-4]
  5. add eax, DWORD PTR _b$[esp-4]
  6. mov edx, DWORD PTR _a$[esp]
  7. adc edx, DWORD PTR _b$[esp]
  8. ret 0
  9. _f1 ENDP
  10. _f1_test PROC
  11. push 5461 ; 00001555H
  12. push 1972608889 ; 75939f79H
  13. push 2874 ; 00000b3aH
  14. push 1942892530 ; 73ce2ff2H
  15. call _f1
  16. push edx
  17. push eax
  18. push OFFSET $SG1436 ; ’%I64d’, 0aH, 00H
  19. call _printf
  20. add esp, 28 ; 0000001cH
  21. ret 0
  22. _f1_test ENDP
  23. _f2 PROC
  24. mov eax, DWORD PTR _a$[esp-4]
  25. sub eax, DWORD PTR _b$[esp-4]
  26. mov edx, DWORD PTR _a$[esp]
  27. sbb edx, DWORD PTR _b$[esp]
  28. ret 0
  29. _f2 ENDP

我们可以看到在函数f1_test()中每个64位值转化为2个32位值,高位先转,然后是低位。加法和减法也是如此。

当进行加法操作时,低32位部分先做加法。如果相加过程中产生进位,则设置CF标志。下一步通过ADC指令加上高位部分,如果CF置1了就增加1。

减法操作也是如此。第一个SUB操作也会导致CF标志的改变,并在随后的SBB操作中检查:如果CF置1了,那么最终结果也会减去1。

在32位环境中,64位的值是从EDX:EAX这一对寄存器的函数中返回的。可以很容易看出f1()函数是如何转化为printf()函数的。

代码 21.2: GCC 4.8.1 -O1 -fno-inline

  1. _f1:
  2. mov eax, DWORD PTR [esp+12]
  3. mov edx, DWORD PTR [esp+16]
  4. add eax, DWORD PTR [esp+4]
  5. adc edx, DWORD PTR [esp+8]
  6. ret
  7. _f1_test:
  8. sub esp, 28
  9. mov DWORD PTR [esp+8], 1972608889 ; 75939f79H
  10. mov DWORD PTR [esp+12], 5461 ; 00001555H
  11. mov DWORD PTR [esp], 1942892530 ; 73ce2ff2H
  12. mov DWORD PTR [esp+4], 2874 ; 00000b3aH
  13. call _f1
  14. mov DWORD PTR [esp+4], eax
  15. mov DWORD PTR [esp+8], edx
  16. mov DWORD PTR [esp], OFFSET FLAT:LC0 ; "%lld12"
  17. call _printf
  18. add esp, 28
  19. ret
  20. _f2:
  21. mov eax, DWORD PTR [esp+4]
  22. mov edx, DWORD PTR [esp+8]
  23. sub eax, DWORD PTR [esp+12]
  24. sbb edx, DWORD PTR [esp+16]
  25. ret

GCC代码也是如此。