MSVC优化后:

    Listing 7.4: MSVC 2012 /Ox x64

    我们可以看到函数f()直接使用寄存器来操作参数,LEA指令用来做加法,编译器认为使用LEA比使用ADD指令要更快。在mian()中也使用了LEA指令,编译器认为使用LEA比使用MOV指令效率更高。

    我们来看看MSVC没有优化的情况:

    1. ; shadow space:
    2. arg_0 = dword ptr 8
    3. arg_8 = dword ptr 10h
    4. arg_10 = dword ptr 18h
    5. ; ECX - 1st argument
    6. ; EDX - 2nd argument
    7. ; R8D - 3rd argument
    8. mov [rsp+arg_10], r8d
    9. mov [rsp+arg_8], edx
    10. mov [rsp+arg_0], ecx
    11. mov eax, [rsp+arg_0]
    12. imul eax, [rsp+arg_8]
    13. add eax, [rsp+arg_10]
    14. retn
    15. f endp
    16. main proc near
    17. sub rsp, 28h
    18. mov r8d, 3 ; 3rd argument
    19. mov edx, 2 ; 2nd argument
    20. mov ecx, 1 ; 1st argument
    21. call f
    22. mov edx, eax
    23. "
    24. call printf
    25. ; return 0
    26. xor eax, eax
    27. add rsp, 28h
    28. retn
    29. main endp

    这里从寄存器传递进来的3个参数因为某种情况又被保存到栈里。这就是所谓的“shadow space”2:每个Win64通常(不是必需)会保存所有4个寄存器的值。这样做由两个原因:1)为输入参数分配所有寄存器(即使是4个)太浪费,所以要通过堆栈来访问;2)每次中断下来调试器总是能够定位函数参数3。

    调用者负责在栈中分配“shadow space”。

    GCC优化后的代码:

    Listing 7.6: GCC 4.4.6 -O3 x64

    Listing 7.7: GCC 4.4.6 x64

    1. f:
    2. ; EDI - 1st argument
    3. ; ESI - 2nd argument
    4. ; EDX - 3rd argument
    5. push rbp
    6. mov rbp, rsp
    7. mov DWORD PTR [rbp-4], edi
    8. mov DWORD PTR [rbp-8], esi
    9. mov DWORD PTR [rbp-12], edx
    10. mov eax, DWORD PTR [rbp-4]
    11. imul eax, DWORD PTR [rbp-8]
    12. add eax, DWORD PTR [rbp-12]
    13. leave
    14. ret
    15. main:
    16. push rbp
    17. mov rbp, rsp
    18. mov edx, 3
    19. mov esi, 2
    20. mov edi, 1
    21. mov edx, eax
    22. "
    23. mov esi, edx
    24. mov rdi, rax
    25. mov eax, 0 ; number of vector registers passed
    26. call printf
    27. mov eax, 0
    28. leave
    29. ret

    System V *NIX [21]没有“shadow space”,但被调用者可能会保存参数,这也是造成寄存器短缺的原因。

    我们例子使用的是32位int,寄存器也为32位寄存器(前缀为E-)。

    为处理64位数值内部会自动调整为64位寄存器:

    1. f proc near
    2. imul rsi, rdi
    3. lea rax, [rdx+rsi]
    4. retn
    5. f endp
    6. main proc near
    7. sub rsp, 8
    8. mov rdx, 3333333344444444h ; 3rd argument
    9. mov rsi, 1111111122222222h ; 2nd argument
    10. mov rdi, 1122334455667788h ; 1st argument
    11. call f
    12. mov edi, offset format ; "%lld
    13. "
    14. mov rsi, rax
    15. xor eax, eax ; number of vector registers passed
    16. call _printf
    17. xor eax, eax
    18. add rsp, 8

    代码非常相似,只是使用了64位寄存器(前缀为R)。