如何在C函数调用期间保留寄存器和其他信息?

时间:2016-12-24 13:26:20

标签: c cpu-registers

我们假设我有三个功能,f1()f2()f3()。调用f1时,它会将信息存储在CPU寄存器中(我想还有其他重要信息)。现在,根据编译时未知的情况,f1会调用f2f3f2f3使用非常不同的寄存器,其中一些寄存器可能与f1使用的寄存器重叠。以下推理是否正确?

编译器知道特定功能在执行期间需要哪些寄存器。因此,当f1调用f2f3时,函数调用代码会保留f2f3在堆栈中使用的寄存器,无论是否为不是f1使用它们。

或者是否有其他一些机制可以让编译器保留寄存器,以便返回的函数不会丢失其数据?

3 个答案:

答案 0 :(得分:6)

回想一下,编程语言是文档中的规范。对于C11,请阅读n1570

C中不存在

Registers(换句话说,几乎过时的register关键字与处理器寄存器不再相关)。它们只在机器代码中起作用(通常由C编译器生成)。

但是,给定编译器生成的代码(对于给定的指令集和目标系统)遵循某些约定,特别是calling conventionsABI(阅读system V x86-64 ABI管理以Linux为例)。这些约定定义了如何使用寄存器,以及哪些寄存器是被调用者保存的或调用者保存的。 Register allocationoptimizing compiler工作的难点部分。

编译器通常会发出代码将一些寄存器内容泄漏到call stack中。并且给定的寄存器可用于多个事物(例如,如果它们出现在同一函数中的不同位置,它可以保留两个不同的变量)。

通常,调用约定不依赖于被调用的函数(回想一下你可以通过函数指针进行间接调用),但主要是它的签名。

答案 1 :(得分:4)

  

“编译器知道特定函数在执行期间需要哪些寄存器。”

不,一般不会知道这一点。

出于一个原因,函数可以来自(第三方)库,编译器对此一无所知。另一个原因是,如果该函数调用另一个函数,另一个函数是什么?

编译器只会将所有“可疑”寄存器压入堆栈并在返回之前弹出它们。

答案 2 :(得分:3)

我认为正如其他人所说,函数的参数通常是通过多个寄存器(此后在堆栈中)发送的。使用哪些寄存器取决于编译器 - gcc参见GNU C /汇编程序:http://cs.lmu.edu/~ray/notes/gasexamples/

值得注意的一些原则:

  1. 堆叠框架

  2. 调用者(调用f1的函数)和被调用函数(你的f1,f2 ......函数)

  3. 易失性和非易失性寄存器。对于您的问题,您只需要担心非易失性寄存器。

  4. 每个函数都有一个堆栈帧,这是一个堆栈的可扩展块,它临时存储需要加载进出寄存器的数据。

    在每次函数调用之前(来自调用者的被调用者),你希望传递的值,即你的参数,将放在许多预定的寄存器中(通常4-6取决于编译器 - 参见链接) ;如果参数多于预定义寄存器的数量,则这些附加值存储在堆栈中(通常是调用者堆栈帧)。

    如果调用者正在使用这些预定义的寄存器,那么编译器会将这些值推送到调用者的堆栈帧,然后在调用被调用者之前将参数分配给寄存器(例如你的f1函数)。一旦被调用的函数(被调用者)返回,这些值就会从堆栈中恢复到各自的寄存器。

    当编译器将C代码转换为汇编/操作码时,调用一系列函数的顺序和顺序并不重要。