函数调用约定中寄存器的保存问题
函数调用约定中寄存器的保存问题
calling convention
Created: 2024-03-25T17:03+08:00
Published: 2024-03-29T09:50+08:00
Categories: Compiler
一个方便的在线查看汇编网站:Compiler Explorer
函数调用时,caller 和 callee 的寄存器保存问题。
callee 至少能看到:
- local variables:自己的局部变量
- function arguments:调用自己的函数参数
- return address: 要返回到的 caller 的地址
基于 risc-v(32-bits) gcc(trunk),执行过程中:
- local variables 由 stack pointer(sp 寄存器)索引
- function arguments 由 frame pointer(fp 寄存器,也叫做 s0)索引
- caller 的信息,如 return address 和 caller 的 fp 在 frame pointer 下面,sp 指向的栈区高地址
例子
int foo(int p0, int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, int p9, int p10) { int a = 1; int b = 2; return p0 + p1 + a; } int main() { foo(0,1,2,3,4,5,6,7,8,9,10); return 0; }
对应汇编如下:
# call foo(0,1,2,3,4,5,6,7,8,9,10) in main(): li a5,10 sw a5,8(sp) li a5,9 sw a5,4(sp) li a5,8 sw a5,0(sp) li a7,7 li a6,6 li a5,5 li a4,4 li a3,3 li a2,2 li a1,1 li a0,0 call foo(int, int, int, int, int, int, int, int, int, int, int)
- 函数参数压栈的顺序是从后往前,10 先被压栈,然后是 9 ……
- 把 foo 的参数放到 sp 上,sp[0] 就是 倒数第一个参数,sp[1] 是倒数第二个参数,……
这样 foo 作为 callee,看到的内存和寄存器的场景就是:
- 前 8 个参数在寄存器上
- sp 指向了函数第 8 个以后的参数
说明现在 callee 的参数是由 sp 索引的
risc-v 32-bits gcc(trunk) 这样处理 fp 和 sp:
- local variables 由 stack pointer 索引
- function arguments 由 frame pointer 索引
- caller 的 ra 等存储在 sp 指向的栈区高地址
callee 要执行 fp=sp
,这样就可以用 fp 索引调用参数了,
但是现在 foo 看到的 fp 是前文 main 的函数参数索引,
如果直接执行 fp=sp
,原来 fp 的值就被覆盖了,所以要把 fp 保存起来。
这就是 foo 函数开头的逻辑:
- 存储 caller 的 ra 和 fp
- 修改 fp 和 sp 方便自己调用
foo 最后的逻辑就是把 fp、ra 恢复到 main 调用前的状态
foo(int, int, int, int, int, int, int, int, int, int, int): addi sp,sp,-64 # save a stack for local variables sw ra,60(sp) sw s0,56(sp) addi s0,sp,64 # adjust fp to previous stack pointer to index arguments sw a0,-36(s0) sw a1,-40(s0) sw a2,-44(s0) sw a3,-48(s0) sw a4,-52(s0) sw a5,-56(s0) sw a6,-60(s0) sw a7,-64(s0) li a5,1 sw a5,-20(s0) li a5,2 sw a5,-24(s0) lw a4,-36(s0) lw a5,-40(s0) add a4,a4,a5 lw a5,-20(s0) add a5,a4,a5 mv a0,a5 # result in a0 lw ra,60(sp) lw s0,56(sp) # restore register fp for caller addi sp,sp,64 # restore register sp for caller jr ra
caller-save 和 callee-save
callee-save 的意思是说,如果 callee 想要用这个寄存器,那么就要预先保存好里面的值,用完了以后恢复。
比如 fp 和 sp 就是 callee-save,caller 将这两个寄存器交给 callee,callee 为了自己的方便,调整它们的位置,
但是最后返回的时候,fp 和 sp 要像调用 callee 之前一样。
所以,callee-save 的寄存器在函数调用前后保持不变。
如果您有任何关于文章的建议,欢迎评论或在 GitHub 提 PR
作者:dutrmp19
本文为作者原创,转载请在 文章开头 注明出处:https://www.cnblogs.com/dutrmp19/p/18103119
遵循 CC 4.0 BY-SA 版权协议
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
2023-03-29 listening-list-for-children