一个函数(父级函数)在调用另一个函数(子级函数)时会执行以下任务。
- 寄存器的值未必由被调用函数保存(不是入口保存(SOE) 寄存器),但在函数返回后将需要保存在堆栈中。
- 如果被调用函数返回一个结构体,则调用函数会为该结构体分配空间并将该空间的地址作为第一个参数传递给被调用函数。
- 传递给被调用函数的参数放置在寄存器中,并在必要时放置在堆栈中。根据以下方案将参数放置在寄存器中:
- 如果目标是 FPU 并且存在任何 32 位浮点参数,则前四个浮点参数被放置在寄存器 R0H-R3H 中。
- 如果存在任何 64 位浮点参数 (long double),它们将通过引用传递。
- 如果存在任何 64 位整数参数 (long long),第一个参数将放置在 ACC 和 P 中(ACC 保存高 32 位,P 保存低 32 位)。所有其他 64 位整数参数以相反的顺序放置在堆栈中。
如果 P 寄存器用于参数传递,则禁用该函数的 prolog/epilog 抽象。有关抽象的更多信息,请参阅节 3.13。
- 如果有任何 32 位参数(long 或 float),则第一个参数放置在 32 位 ACC (AH/AL) 中。所有其他 32 位参数以相反的顺序放置在堆栈中。
func1(long a, long long b, int c, int* d);
stack ACC/P XAR5, XAR4
- 指针参数放置在 XAR4 和 XAR5 中。所有其他指针都放置在堆栈中。
- 其余的 16 位参数按 AL、AH、XAR4、XAR5(如果可用)的顺序放置。
- 其余未放置在寄存器中的参数以相反的顺序推入堆栈中。也就是说,放在堆栈上最左边的参数最后被推入堆栈中。所有 32 位参数与堆栈中的偶数地址对齐。
结构体参数作为结构体的地址传递。被调用函数必须创建本地副本。
对于使用省略号声明的函数,表示该函数是用不同数量的参数调用的,相关惯例略有修改。最后一个显式声明的参数在堆栈上传递,因此其栈地址可以作为访问未声明参数的引用。
- 在调用子级函数之前,栈指针 (SP) 必须由父级函数偶数对齐。如有必要,通过将栈指针增加 1 来完成。如果需要,编码器应该在调用之前递增 SP。这些函数调用示例展示了参数的放置位置:
func1 (int a, int b. long c)
XAR4 XAR5 AH/AL
func1 (long a, int b, long c) ;
AH/AL XAR4 stack
vararg (int a, int b, int c, ...)
AL AH stack
- 调用方使用 LCR 指令来调用函数。RPC 寄存器值被压在堆栈上。然后将返回地址存储在 RPC 寄存器中。
- 堆栈在函数边界对齐。