飞腾的函数调用GCC分析

飞腾的函数调用GCC分析

1. 堆栈和栈帧

64位的通用寄存器虽然可以随意被使用;但是针对函数调用,GCC编译器对通用寄存器的使用要符合下表的软件约定。

寄存器 说明
X0…X7 函数调用参数,其中X0也用于函数返回值
X8 保留
X9…X15 临时寄存器,使用时不保存到栈帧中
X16…X18 保留
X19…X28 临时寄存器,使用时保存到栈帧中
X29 栈帧寄存器FP,使用时需要保存
X30 链接寄存器LR,保存函数返回地址
X31 零寄存器XZR,该寄存器并不真正存在,WZR是对应的32位
注:如果是32/16/8位的变量,就使用Wn寄存器形式。

C语言程序的函数动态调用的关系体现在堆栈内的栈帧关系上,参见下图。
在这里插入图片描述

  1. 每个函数最开始时会创建自己的栈帧区,在函数退出之前会撤销栈帧区。每个还没有退出的函数,在堆栈中都有对应的栈帧;当函数退出之后,对应的栈帧也就不存在。
  2. 调用函数的栈帧和被调函数的栈帧在堆栈里是上下紧挨布局。
  3. 堆栈寄存器SP指向当前正在运行的函数栈帧起始地址。

标准栈帧包括本地局部参数区、函数输入参数区、X19-X28寄存器保存区、64位返回地址和64位前一个栈帧地址。栈帧的长度都是C语言编译器GCC静态计算的。

输入参数区、本地变量区、或者X19-X28寄存器变量区,属于可选的;即栈帧中相应的区依赖于函数实现的具体情况。例如,函数实现中用到X19-X28临时寄存器时,那么该函数就会提前将用到的临时寄存器保存到栈帧区中,函数返回前恢复X19-X28临时寄存器的值。

标准栈帧对应的函数实现,会调用其他子函数;当函数实现中没有调用其他子函数时,对应的栈帧就不用保存64位返回地址和64位前一个栈帧地址,直接用寄存器X30和X29。

C语言标准函数执行的第一步是先把链接寄存器X30和X29保存在当前栈帧的底部,同时将堆栈寄存器SP都指向当前栈帧底部;然后将Xn/Wn(n=0/1/…)依次保存到函数输入参数区。在函数返回之前,先将返回值X0/W0准备好,再将前一栈帧地址和函数返回地址读取到寄存器X29和X30,最后执行ret返回指令。

猜你喜欢

转载自blog.csdn.net/lsshao/article/details/111110657