问题引入
当我在学习汇编语言,c语言代码转化成汇编代码时,了解到汇编代码需要将变量储存到寄存器,而x86-64寄存器只有稀缺的16个,这16个寄存器需要分给无限层级的函数调用,不可避免遇到深层的函数使用相同的寄存器覆盖上层函数的寄存器值,导致函数回调时原先的值已经被破坏的情况。那么是怎么处理这个问题的呢?
具体代码如下图,
是关于计算两个数的相乘结果同时用传指针的方法来获取值的一个功能。
c语言代码
c
long mult2(long x, long y) {
return x * y;
}
// 把乘法结果存到指定地址
void multstore(long x, long y, long *dest) {
long t = mult2(x, y); // 调用mult2计算
*dest = t; // 结果存入dest指向的内存
}
int main() {
long result = 0; // 初始化为0
multstore(3, 4, &result); // 调用:3*4=12,存入result
// result现在是12
return 0;
}
对应汇编代码
汇编
# mult2函数:计算x*y
mult2:
movq %rdi, %rax # x从%rdi移到%rax
imulq %rsi, %rax # %rax = x * y
ret # 返回,结果在%rax
# multstore函数:调用mult2,结果存到*dest
multstore:
pushq %rbx # 保存%rbx的旧值到栈(callee-saved)
movq %rdx, %rbx # 把dest指针存到%rbx(安全转移)
call mult2 # 调用mult2(x, y)
movq %rax, (%rbx) # 把mult2的返回值存到*dest
popq %rbx # 恢复%rbx的旧值
ret # 返回main
# main函数
main:
subq $16, %rsp # 给result分配栈空间
movq $0, 8(%rsp) # result = 0
leaq 8(%rsp), %rdx # &result → %rdx(第3个参数)
movl $4, %esi # 4 → %esi(第2个参数y)
movl $3, %edi # 3 → %edi(第1个参数x)
call multstore # 调用multstore
movq 8(%rsp), %rax # result的值(可选)
addq $16, %rsp # 恢复栈
xorl %eax, %eax # return 0
ret
ok,首先这里multstore中 pushq %rbx 和 popq %rbx 就体现了对应的寄存器值保存处理
发现问题
解决问题
这里用实际上是将寄存器分为 caller-saved(调用者保存) / callee-saved(被调用者保存) 两种,来避免这个问题
caller-saved / callee-saved

caller-saved寄存器,调用者保存,被调用者不负责保存,适合临时短期变量
具体例子说明
图一:

这里算平均值的例子中sum和count都是短期存一下得到一个短期结果,avg也是短期结果。在调用函数call printf中不会被用到,什么叫短期和长期使用变量呢。如下图
图二:

在图二场景中,(转换成汇编代码后)如果我将i赋给一个寄存器假如是&rbx,那么在while循环中会调用另一个函数do_something,那么这个do_something函数中可能也会将一个变量储存在&rbx中,那么i原本的值可能就被这个新变量的值覆盖了,当这个do_something函数结束后,i++的时候i的值已经被覆盖不是执行do_something前的数字,那么程序就出问题了。所以就需要保存调用函数之前那个i值。(挺像并发的,记住原来的模样后再切出去)
这里的i就是一个需要长期保存的值,长期变量(因为他有可能在后面调用函数时被改)
回过头来再看图一,方案A中就是一个短期变量了,