首页 > 编程语言 >一起学RISC-V汇编第9讲之RISC-V ABI之寄存器使用约定

一起学RISC-V汇编第9讲之RISC-V ABI之寄存器使用约定

时间:2024-10-07 21:49:31浏览次数:9  
标签:调用者 callee ABI caller RISC 保存 寄存器 saved

目录

寄存器使用约定告诉我们函数调用时通过哪些寄存器传递参数、通过哪些寄存器保存返回值、哪些寄存器可以任意使用而不用保存旧值等。

1 RISC-V 寄存器使用约定

第2讲寄存器这一章,列出了32个通用寄存器以及32个浮点寄存器:

整理如下:

Preserved(callee-saved) NonPreserved(caller-saved)
Saved registers:s0-s11 Return address: ra
Stack pointer:sp Argument registers:a0-a7
Return values: a0-a1
Temporary registers:t0-t6

2 Caller-saved 与 Callee-saved

由RISC-V寄存器的个数是有限的,而函数是非常多的,调用路径可能非常长,这么多函数共用有限的寄存器,怎么样才能安全的访问寄存器呢?最安全的做法是,每次调用其它的函数前把寄存器值保存到栈中,等从子函数返回后,再将寄存器的值出栈,恢复函数调用前的状态,通过这个办法,各个函数就都可以随意使用所有寄存器了。

我们将调用函数称为caller,被调用函数称为callee。

2.1 对比几种不同的寄存器保存方式

对比如下几种方式:

  • 方式1:寄存器全部由caller 保存
  • 方式2:寄存器全部由callee保存
  • 方式3:按照程序调用约定,一部分寄存器由调用者保存(caller-saved), 一部分由被调用者保存(callee-saved)

方式1 寄存器全部由caller保存-可行但效率很低

这种做法是最安全的,但效率非常低,访问栈相当于访问内存,比直接访问寄存器速度明显下降,所以有必要想办法减少栈的使用。

方式2 寄存器全部由callee保存-不可行

寄存器全部由callee保存行吗?答案是不可行,有些寄存器我们必须在改变之前保存(即caller中保存)。

参数寄存器(a0-a7)只能是 caller-saved 。为了传递参数,caller 必须将参数寄存器中原本的值覆盖成传给 callee 的参数,如果参数寄存器内之前有值,那么就需要 caller 进行保存。这个工作没法由 callee 进行,因为提交给 callee 的参数寄存器已经被污染(改变)了。

返回值寄存器(a0-a1)只能是 caller-saved 。如果让 callee 保存 caller 的返回值,那么 callee 的自己的返回值该怎么传递给 caller 呢?

返回地址寄存器(ra)只能是 caller-saved,如果 callee 在进入函数时保存了 caller 的 ra ,而退出函数前恢复了 caller 的 ra ,那么返回到一个错误的地方,应该保存自己的ra,而非caller的ra

方式3 一部分寄存器由调用者保存(caller-saved), 一部分由被调用者保存(callee-saved)

我们将寄存器分为两组:

  • caller-saved (调用者保存),这是说调用者可以按照自己的需要来保存这些寄存器,如果自己使用了,那么就保存,如果没有使用,就不需要保存。
  • callee-saved (被调用者保存),被调用者在执行正式的功能前前需要将这些寄存器压栈,在返回前需要将这些寄存器弹栈恢复,从caller的视角看,这些寄存器在调用前后值肯定是一样的。从而叫做保存寄存器。

下面一段话引用自计算机组成-寄存器保存

不过这种标准的定义其实并不容易让人理解其中的原因。我们可以换一个角度去看这件事。

callee-saved 寄存器都会本着“谁污染,谁治理”的策略被保护起来,所以callee-saved register是 safe 的,也就是在 call后寄存器的值不会改变。。

而 caller-saved 的寄存器有可能被污染(在 caller 并不保存的情况下),其实核心特性是 unsafe ,也就是“调用前后并能保证寄存器中的值不发生变化”,所以这种寄存器也被称为临时寄存器,如果有需求才会由caller保存。

2.2 为什么要分caller-saved与callee-saved?

原因归根到底是因为编译器缺少全局优化的能力。

如果在全局视角下分析,在调用过程中,我们真正需要保存的寄存器,具有两个特性:

  1. 被 caller 使用且生命周期必须跨越调用过程。
  2. 被 callee 使用。

那无论是 callee 还是 caller 都没有办法很好达到这个理想状态,因为作为 caller 虽然可以正确的分析出具有第 1 点特性的寄存器,但是对于第 2 点无能为力。同样的 callee 可以清楚地知道第 2 点,但是对于第 1 点一无所知。这种困境是由于“函数”这个概念本就是为了代码段间的解耦和隔离,如果想要一个个解耦的函数,就必须承担无法全局优化的代价。

如果这样看,那么将寄存器分成差不多大小的两组的行为是有一定道理的,我们虽然不能做到全局优化,但是可以做到基于 caller 和 callee 各自一定的权力,使得双方的关于寄存器使用的“洞见”都可以得到利用。总好过只利用一方的信息。

2.3 caller-saved与callee-saved寄存器的灵活使用

从使用上来说,如果寄存器中是一个生命周期很长的变量,那么应当使用 callee-saved register ,它可以保证在其生命周期的多次函数调用中,值始终不发生变化;而如果寄存器中是一个用完就扔的临时变量,那么应当使用 caller-saved-register ,使用它并不需要保存,心理负担小。

caller-saved的使用场景:

  1. 临时数据的存放:在进行一系列计算时,编译器可能会选择使用caller saved registers来存储临时结果,因为这些结果在当前函数调用结束后不再需要。
  2. 非关键性数据的处理:对于那些不会被随后的函数调用所需的数据,使用caller saved registers可以在调用后立即丢弃。

callee-saved的使用场景:

  1. 重要数据的保护:当函数需要调用另一个函数时,它依赖callee saved registers来保护那些必须跨函数调用保存的关键数据。
  2. 保持状态的连续性:对于递归调用和多层嵌套调用,callee saved registers可以帮助保持调用链上每个函数的状态。

参考:

  1. Releases · riscv-non-isa/riscv-elf-psabi-doc (github.com)
  2. RISC-V函数调用规范 - 知乎 (zhihu.com)
  3. 为什么CPU寄存器要分为两组,被调用者保存寄存器和调用者保存寄存器?
  4. (15 封私信 / 80 条消息) risc-v 返回地址寄存器是由caller保存还是callee保存? - 知乎 (zhihu.com)
  5. Caller-saved register and Callee-saved register-CSDN博客
  6. 计算机组成-寄存器保存 | 钟鼓楼 (thysrael.github.io)
  7. RISC-V基础之函数调用(三)保留寄存器(包含实例)_riscv中如何保存和恢复更新寄存器的值-CSDN博客
  8. 为什么要区分caller saved和callee saved registers – PingCode

标签:调用者,callee,ABI,caller,RISC,保存,寄存器,saved
From: https://www.cnblogs.com/sureZ-learning/p/18450722

相关文章

  • 一起学RISC-V汇编第9讲之RISC-V ABI之栈帧
    这一节讲解RISC-V中的栈帧。1C语言中的{}的秘密函数执行的底层其实是操作寄存器,CPU的寄存器是有限的,为什么我们进行一系列函数调用后还能正确运行,这些函数之间是怎么协调使用寄存器的?答案是:栈函数之间能随意调用,还能顺利恢复现场,这个就是栈的功劳。为什么我们在代码中并没有......
  • 一起学RISC-V汇编第9讲之RISC-V ABI之函数调用
    目录1RISC-VABI接口2RISC-V函数调用约定2.1入参的传递2.2返回值的传递1RISC-VABI接口ABI(ApplicationBinaryInterface)为应用程序二进制接口,它定义了应用程序之间或应用程序和操作系统之间进行二进制级交互时必须遵循的规则和约定。ABI包括了关于函数调用约定(参数传递,函......
  • SciTech-Mathmatics-Probability+Statistics:Quantifing Uncertainty_统计分析: SciTec
    一般数学表示方法概率数学表示方法\(\large\begin{array}{rl}\\\bm{X}:&符合某种概率分布的Random\Variable(随机变量)\\\bm{x}:&\bm{rnorm},随机变量X的一个实例,,…\\\bm{f}:&\bm{pdf},\bm{dnorm},\text{ProbabilityDistributionFunction}\of\\text......
  • SciTech-Mathmatics-Probability+Statistics:Quantifing Uncertainty_多元数据统计分
    Chapt1学习目标理解多元数据及多元统计分析与一元统计分析的区别。掌握数据的计量尺度与数据类型。了解多元统计分析的应用分类。1.1 多元数据认知1.1.1多元数据的概念对任何一个现实问题要转化为一个统计问题,首要的工作是要对其特征进行刻画;一般采用随机变量,多......
  • SciTech-Mathmatics-Probability+Statistics:Quantifing Uncertainty_统计分析: SciTec
    一般数学表示方法概率数学表示方法\(\large\begin{array}{rl}\\\bm{X}:&符合某种概率分布的Random\Variable(随机变量)\\\bm{x}:&\bm{rnorm},随机变量X的一个实例,,…\\\bm{f}:&\bm{pdf},\bm{dnorm},\text{ProbabilityDistributionFunction}\of\\text......
  • SciTech-Mathmatics-Probability+Statistics:Quantifing Uncertainty_统计数据分析:
    多元数据和多元统计分析<<实用多元统计分析>>清华大学出版社,5校正文1.indd12023/9/1217:14:25Chapt1学习目标理解多元数据及多元统计分析与一元统计分析的区别。掌握数据的计量尺度与数据类型。了解多元统计分析的应用分类。1.1 多元数据认知1.1.1多元数据的......
  • Databinding(kotlin)
    简单使用(只作为view获取)build.gradle.kts配置android{dataBinding{enable=true}}activity注入//setContentView(R.layout.activity_main)valbinding:ActivityMainBinding=DataBindingUtil.setContentView(this,R.layout.activity_main......
  • 题解:TopCoder12316 ThreeColorability
    Vjudge可以出成《三色绘恋》背景。题意给一个格点数为\((n+1)\times(m+1)\)的网格,给格点染色,相邻的格点不能染成同样的颜色。每个格子有一条对角线的边,可选N形和Z形。现在有一个残缺的网格,存在一些格子的对角线连法不确定,构造一种字典序最小的方案使得至少存在一种染色......
  • PICO 2 RP2350使用官方推荐RISC-V编译器在O3优化下的coremark跑分,与Hazard3库宣传跑分
    编译环境:WSLUbuntu22.04GCC13.2.0 Hazard3存储库https://github.com/Wren6991/Hazard3/RP2350默认频率150MHz,编译内核为其RISC-V架构内核,在此频率下实测O3等级跑分453左右,O2等级跑分429左右。在测试时,当我打开第二个核心后,并且第二个核心只用来控制led灯,此时coremark跑......
  • CPU中跟踪后继指令地址的寄存器
    错题考的是计算机系统中CPU中跟踪后继指令地址的寄存器的知识点。正确答案是C.程序计数器(ProgramCounter,PC)。程序计数器(PC)程序计数器是CPU内部的一个小型寄存器,它的作用是存储下一条要执行的指令的地址。在计算机执行程序时,程序计数器起到了非常重要的作用:指令定位:程序计数器......