这篇内容主要介绍了x86-64架构下的过程调用规范,对比了x86-64和IA-32平台上的差异,并给出了具体的汇编代码示例。
在x86-64架构中,过程调用发生了显著的变化,优化了参数传递方式和寄存器使用,从而提高了性能。首先,由于x86-64支持64位计算,`longint`等长类型的数据长度发生了变化,导致参数传递方式的差异。在IA-32平台上,`sample.c`中的函数`sample`被编译成的汇编代码使用栈来传递参数和返回值,而x86-64平台上,同样的函数则直接通过寄存器 `%rsi` 和 `%rdi` 来传递参数,返回值存储在 `%rax` 寄存器中,这使得执行速度更快。
x86-64过程调用的一个关键特点在于参数传递的优化。最多可以有6个整型或指针参数通过寄存器 `%rdi`, `%rsi`, `%rdx`, `%rcx`, `%r8`, `%r9` 传递。如果参数数量超过6个,额外的参数会按顺序压入栈中,每个分配8个字节的空间。`call`指令在调用前会将64位的返回地址保存到栈中,`ret`指令则从栈中取出返回地址,然后调整栈指针恢复栈的状态。
关于寄存器的使用约定,x86-64规定了一些通用寄存器的特定用途。`RAX`、`R10` 和 `R11` 在过程内部可以自由使用,不需要保存和恢复。然而,如果使用了 `RBX`、`RBP`、`R12`、`R13`、`R14` 和 `R15` 这些寄存器,过程必须负责在使用前保存它们的值到栈中,返回前再恢复,以保持调用者可见性(caller-saved)。
举例来说,在`caller()`函数中,它调用了`test`函数,并传递了不同类型的参数。在实际的x86-64汇编代码实现中,这些参数会根据约定被放入相应的寄存器或栈中,然后调用`test`函数。返回后,`caller()`函数会继续执行,可能使用到的结果也会存储在指定的寄存器中。
x86-64的过程调用机制利用更多的寄存器进行参数传递,减少了对栈的依赖,提高了执行效率,同时通过寄存器使用约定确保了代码的兼容性和可预测性。