我们假设我有三个功能,f1()
,f2()
和f3()
。调用f1
时,它会将信息存储在CPU寄存器中(我想还有其他重要信息)。现在,根据编译时未知的情况,f1
会调用f2
或f3
。 f2
和f3
使用非常不同的寄存器,其中一些寄存器可能与f1
使用的寄存器重叠。以下推理是否正确?
编译器知道特定功能在执行期间需要哪些寄存器。因此,当f1
调用f2
或f3
时,函数调用代码会保留f2
或f3
在堆栈中使用的寄存器,无论是否为不是f1
使用它们。
或者是否有其他一些机制可以让编译器保留寄存器,以便返回的函数不会丢失其数据?
答案 0 :(得分:6)
回想一下,编程语言是文档中的规范。对于C11,请阅读n1570。
C中不存在 Registers(换句话说,几乎过时的register
关键字与处理器寄存器不再相关)。它们只在机器代码中起作用(通常由C编译器生成)。
但是,给定编译器生成的代码(对于给定的指令集和目标系统)遵循某些约定,特别是calling conventions和ABI(阅读system V x86-64 ABI管理以Linux为例)。这些约定定义了如何使用寄存器,以及哪些寄存器是被调用者保存的或调用者保存的。 Register allocation是optimizing compiler工作的难点部分。
编译器通常会发出代码将一些寄存器内容泄漏到call stack中。并且给定的寄存器可用于多个事物(例如,如果它们出现在同一函数中的不同位置,它可以保留两个不同的变量)。
通常,调用约定不依赖于被调用的函数(回想一下你可以通过函数指针进行间接调用),但主要是它的签名。
答案 1 :(得分:4)
“编译器知道特定函数在执行期间需要哪些寄存器。”
不,一般不会知道这一点。
出于一个原因,函数可以来自(第三方)库,编译器对此一无所知。另一个原因是,如果该函数调用另一个函数,另一个函数是什么?
编译器只会将所有“可疑”寄存器压入堆栈并在返回之前弹出它们。
答案 2 :(得分:3)
我认为正如其他人所说,函数的参数通常是通过多个寄存器(此后在堆栈中)发送的。使用哪些寄存器取决于编译器 - gcc
参见GNU C /汇编程序:http://cs.lmu.edu/~ray/notes/gasexamples/
值得注意的一些原则:
堆叠框架
调用者(调用f1的函数)和被调用函数(你的f1,f2 ......函数)
易失性和非易失性寄存器。对于您的问题,您只需要担心非易失性寄存器。
每个函数都有一个堆栈帧,这是一个堆栈的可扩展块,它临时存储需要加载进出寄存器的数据。
在每次函数调用之前(来自调用者的被调用者),你希望传递的值,即你的参数,将放在许多预定的寄存器中(通常4-6取决于编译器 - 参见链接) ;如果参数多于预定义寄存器的数量,则这些附加值存储在堆栈中(通常是调用者堆栈帧)。
如果调用者正在使用这些预定义的寄存器,那么编译器会将这些值推送到调用者的堆栈帧,然后在调用被调用者之前将参数分配给寄存器(例如你的f1函数)。一旦被调用的函数(被调用者)返回,这些值就会从堆栈中恢复到各自的寄存器。
当编译器将C代码转换为汇编/操作码时,调用一系列函数的顺序和顺序并不重要。