- 第一章 CPU 简介
- 第二章 Hello,world!
- 第三章 函数开始和结束
- 第四章 栈
- Chapter 5 printf() 与参数处理
- Chapter 6 scanf()
- CHAPER7 访问传递参数
- Chapter 8 一个或者多个字的返回值
- Chapter 9 指针
- Chapter 10 条件跳转
- 第 11 章 选择结构 switch()/case/default
- 第 12 章 循环结构
- 第 13 章 strlen()
- Chapter 14 Division by 9
- chapter 15 用 FPU 工作
- Chapter 16 数组
- Chapter 17 位域
- 第 18 章 结构体
- 19 章 联合体
- 第二十章 函数指针
- 第 21 章 在 32 位环境中的 64 位值
- 第二十二章 SIMD
- 23 章 64 位化
- 24 章 使用 x64 下的 SIMD 来处理浮点数
- 25 章 温度转换
- 26 章 C99 的限制
- 27 章 内联函数
- 第 28 章 得到不正确反汇编结果
- 第 29 章 花指令
- 第 30 章 16 位 Windows
- 第 31 章 类
- 三十二 ostream
- 34.2.2 MSVC
- 34.2.3 C++ 11 std::forward_list
- 34.3 std::vector
- 34.4 std::map and std::set
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
7.2 X64
x86-64 架构下有点不同,函数参数(4 或 6)使用寄存器传递,被调用函数通过访问寄存器来访问传递进来的参数。
7.2.1 MSVC
MSVC 优化后:
Listing 7.4: MSVC 2012 /Ox x64
#!bash
$SG2997 DB ’%d’, 0aH, 00H
main PROC
sub rsp, 40
mov edx, 2
lea r8d, QWORD PTR [rdx+1] ; R8D=3
lea ecx, QWORD PTR [rdx-1] ; ECX=1
call f
lea rcx, OFFSET FLAT:$SG2997 ; ’%d’
mov edx, eax
call printf
xor eax, eax
add rsp, 40
ret 0
main ENDP
f PROC
; ECX - 1st argument
; EDX - 2nd argument
; R8D - 3rd argument
imul ecx, edx
lea eax, DWORD PTR [r8+rcx]
ret 0
f ENDP
我们可以看到函数 f() 直接使用寄存器来操作参数,LEA 指令用来做加法,编译器认为使用 LEA 比使用 ADD 指令要更快。在 mian() 中也使用了 LEA 指令,编译器认为使用 LEA 比使用 MOV 指令效率更高。
我们来看看 MSVC 没有优化的情况:
Listing 7.5: MSVC 2012 x64
#!bash
f proc near
; shadow space:
arg_0 = dword ptr 8
arg_8 = dword ptr 10h
arg_10 = dword ptr 18h
; ECX - 1st argument
; EDX - 2nd argument
; R8D - 3rd argument
mov [rsp+arg_10], r8d
mov [rsp+arg_8], edx
mov [rsp+arg_0], ecx
mov eax, [rsp+arg_0]
imul eax, [rsp+arg_8]
add eax, [rsp+arg_10]
retn
f endp
main proc near
sub rsp, 28h
mov r8d, 3 ; 3rd argument
mov edx, 2 ; 2nd argument
mov ecx, 1 ; 1st argument
call f
mov edx, eax
lea rcx, $SG2931 ; "%d
"
call printf
; return 0
xor eax, eax
add rsp, 28h
retn
main endp
这里从寄存器传递进来的 3 个参数因为某种情况又被保存到栈里。这就是所谓的“shadow space”2:每个 Win64 通常(不是必需)会保存所有 4 个寄存器的值。这样做由两个原因:1)为输入参数分配所有寄存器(即使是 4 个)太浪费,所以要通过堆栈来访问;2)每次中断下来调试器总是能够定位函数参数 3。
调用者负责在栈中分配“shadow space”。
7.2.2 GCC
GCC 优化后的代码:
Listing 7.6: GCC 4.4.6 -O3 x64
#!bash
f:
; EDI - 1st argument
; ESI - 2nd argument
; EDX - 3rd argument
imul esi, edi
lea eax, [rdx+rsi]
ret
main:
sub rsp, 8
mov edx, 3
mov esi, 2
mov edi, 1
call f
mov edi, OFFSET FLAT:.LC0 ; "%d
"
mov esi, eax
xor eax, eax ; number of vector registers passed
call printf
xor eax, eax
add rsp, 8
ret
GCC 无优化代码:
Listing 7.7: GCC 4.4.6 x64
#!bash
f:
; EDI - 1st argument
; ESI - 2nd argument
; EDX - 3rd argument
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], edi
mov DWORD PTR [rbp-8], esi
mov DWORD PTR [rbp-12], edx
mov eax, DWORD PTR [rbp-4]
imul eax, DWORD PTR [rbp-8]
add eax, DWORD PTR [rbp-12]
leave
ret
main:
push rbp
mov rbp, rsp
mov edx, 3
mov esi, 2
mov edi, 1
call f
mov edx, eax
mov eax, OFFSET FLAT:.LC0 ; "%d
"
mov esi, edx
mov rdi, rax
mov eax, 0 ; number of vector registers passed
call printf
mov eax, 0
leave
ret
System V *NIX [21]没有“shadow space”,但被调用者可能会保存参数,这也是造成寄存器短缺的原因。
7.2.3 GCC: uint64_t instead int
我们例子使用的是 32 位 int,寄存器也为 32 位寄存器(前缀为 E-)。
为处理 64 位数值内部会自动调整为 64 位寄存器:
#!cpp
#include <stdio.h>
#include <stdint.h>
uint64_t f (uint64_t a, uint64_t b, uint64_t c)
{
return a*b+c;
};
int main()
{
printf ("%lld
", f(0x1122334455667788,0x1111111122222222,0x3333333344444444));
return 0;
};
Listing 7.8: GCC 4.4.6 -O3 x64
#!cpp
f proc near
imul rsi, rdi
lea rax, [rdx+rsi]
retn
f endp
main proc near
sub rsp, 8
mov rdx, 3333333344444444h ; 3rd argument
mov rsi, 1111111122222222h ; 2nd argument
mov rdi, 1122334455667788h ; 1st argument
call f
mov edi, offset format ; "%lld
"
mov rsi, rax
xor eax, eax ; number of vector registers passed
call _printf
xor eax, eax
add rsp, 8
retn
main endp
代码非常相似,只是使用了 64 位寄存器(前缀为 R)。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论