RISC-V基础之函数调用(五)函数递归调用及函数参数数量溢出(超出现有寄存器个数)约定(包含实例)

这篇具有很好参考价值的文章主要介绍了RISC-V基础之函数调用(五)函数递归调用及函数参数数量溢出(超出现有寄存器个数)约定(包含实例)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

首先先解释一下栈在函数调用中的作用,更详细的部分请参照考研复习之数据结构笔记(五)栈和队列(上)(包含栈的相关内容)_管二狗赶快去工作!的博客-CSDN博客

函数嵌套调用栈的作用是用来保存和恢复函数调用过程中的相关信息,如参数、局部变量、返回地址、上下文等。这些信息可以帮助函数在执行完毕后返回到正确的位置,以及在发生异常时恢复到合理的状态。函数嵌套调用栈的具体结构和操作取决于编译器、操作系统和体系结构的设计,但一般来说,它遵循以下原则:

  • 当一个函数被调用时,它会在栈顶分配一段空间,称为栈帧(stack frame)。栈帧中存放了该函数的参数、局部变量、返回地址、上下文等信息。
  • 当一个函数调用另一个函数时,它会将新的栈帧压入栈顶,形成一个嵌套的结构。这样,每个函数都可以访问自己的栈帧中的信息,而不会影响其他函数的信息。
  • 当一个函数执行完毕后,它会将自己的栈帧弹出栈顶,释放空间。然后,它会根据返回地址跳转回调用者,并将返回值传递给调用者。
  • 当一个函数发生异常时,它会将异常信息保存在栈中,并跳转到异常处理程序。异常处理程序可以根据栈中的信息进行恢复或终止操作。

为了更好地理解函数嵌套调用栈的作用,我们可以看一个简单的例子。假设我们有以下三个函数:

int f1(int a, int b) {
  int c = a + b;
  int d = f2(c);
  return d;
}

int f2(int x) {
  int y = x * x;
  int z = f3(y);
  return z;
}

int f3(int r) {
  int s = r - 1;
  return s;
}

现在我们假设主程序调用了 f1(2, 3)。那么函数嵌套调用栈的变化如下:

  • 主程序在调用 f1(2, 3) 之前,会将参数 a = 2b = 3 压入栈中,并将返回地址(主程序中 f1(2, 3) 的下一条指令)压入栈中。然后跳转到 f1 的起始地址。
  • f1 在执行时,会在栈顶分配一段空间,存放自己的局部变量 cd。然后计算 c = a + b = 5 并保存在栈中。
  • f1 在调用 f2(c) 之前,会将参数 x = c = 5 压入栈中,并将返回地址(f1f2(c) 的下一条指令)压入栈中。然后跳转到 f2 的起始地址。
  • f2 在执行时,会在栈顶分配一段空间,存放自己的局部变量 yz。然后计算 y = x * x = 25 并保存在栈中。
  • f2 在调用 f3(y) 之前,会将参数 r = y = 25 压入栈中,并将返回地址(f2f3(y) 的下一条指令)压入栈中。然后跳转到 f3 的起始地址。
  • f3 在执行时,会在栈顶分配一段空间,存放自己的局部变量 s。然后计算 s = r - 1 = 24 并保存在栈中。
  • f3 在执行完毕后,会将自己的返回值 s = 24 放在 a0 寄存器中,并将自己的栈帧弹出栈顶,释放空间。然后,它会根据返回地址跳转回 f2
  • f2 在接收到 f3 的返回值后,会将其保存在栈中的 z 中。然后,它会将自己的返回值 z = 24 放在 a0 寄存器中,并将自己的栈帧弹出栈顶,释放空间。然后,它会根据返回地址跳转回 f1
  • f1 在接收到 f2 的返回值后,会将其保存在栈中的 d 中。然后,它会将自己的返回值 d = 24 放在 a0 寄存器中,并将自己的栈帧弹出栈顶,释放空间。然后,它会根据返回地址跳转回主程序。
  • 主程序在接收到 f1 的返回值后,会将其保存在某个变量中。然后,它会将栈中的参数 a = 2b = 3 弹出栈顶,释放空间。然后,它会继续执行下一条指令。

递归函数是一种非叶函数,它调用自身。递归函数既是调用者又是被调用者,所以它必须保存和恢复保留和非保留寄存器。

例如,阶乘函数可以写成一个递归函数。回忆一下,阶乘(n) = n × (n – 1) × (n – 2) × ⋯ × 2 × 1。阶乘函数可以递归地写成阶乘(n) = n × 阶乘(n – 1),如下图所示。1 的阶乘就是 1。

RISC-V基础之函数调用(五)函数递归调用及函数参数数量溢出(超出现有寄存器个数)约定(包含实例),RISC-V基础知识,risc-v

 文章来源地址https://www.toymoban.com/news/detail-631221.html

假设程序从地址 0x8500 开始。根据被调用者保存规则,阶乘是一个非叶函数,必须保存 ra。根据调用者保存规则,阶乘在调用自身后仍然需要 n,所以它必须保存 a0。因此,它在开始时将这两个寄存器压入栈中。然后它检查 n 是否小于等于 1。如果是的话,它将返回值 1 放在 a0 中,恢复栈指针,并返回到调用者。在这种情况下,它不需要恢复 ra,因为它从未被修改过。如果 n 大于 1,函数递归地调用阶乘(n−1)。然后它从栈中恢复 n 和返回地址寄存器 (ra),进行乘法,并返回这个结果。注意,函数巧妙地将 n 恢复到 t1 中,以免覆盖返回值。乘法指令 (mul a0,t1,a0) 将 n (t1) 和返回值 (a0) 相乘,并将结果放在 a0 中,即返回寄存器。

为了清楚起见,我们在函数调用开始时保存寄存器。一个优化的编译器可能会观察到当 n 小于等于 1 时,没有必要保存 a0 和 ra,并且只在函数的 else 部分将寄存器保存到栈上。下图显示了执行阶乘(3) 时的栈情况。

RISC-V基础之函数调用(五)函数递归调用及函数参数数量溢出(超出现有寄存器个数)约定(包含实例),RISC-V基础知识,risc-v为了说明,我们假设 sp 最初指向 0xFF0(高地址位为 0),如上图(a) 所示。函数创建了一个两字的栈帧来保存 n (a0) 和 ra。在第一次调用时,阶乘将 a0(保存 n = 3)保存在 0xFEC 和 ra 在 0xFE8 中,如图 (b) 所示。函数然后将 n 改变为 2 并递归地调用阶乘(2),使 ra 持有 0x8528。在第二次调用时,它将 a0(保存 n = 2)保存在 0xFE4 和 ra 在 0xFE0 中。这次我们知道 ra 包含 0x8528。函数然后将 n 改变为 1 并递归地调用阶乘(1)。

在第三次调用时,它将 a0(保存 n = 1)保存在 0xFDC 和 ra 在 0xFD8 中。这次 ra 再次包含 0x8528。第三次调用阶乘返回值为 1 的 a0,并在返回到第二次调用之前释放栈帧。第二次调用恢复 n(到 t1)为 2,恢复 ra 到 0x8528(它恰好已经有了这个值),释放栈帧,并返回 a0 = 2 × 1 = 2 给第一次调用。第一次调用恢复 n(到 t1)为 3,恢复 ra,调用者的返回地址,释放栈帧,并返回 a0 = 3 × 2 = 6。图© 显示了递归调用的函数返回时的栈情况。当阶乘返回到调用者时,栈指针在它的原始位置(0xFF0),栈指针以上的栈内容没有改变,并且所有的保留寄存器保持它们的原始值。a0 持有返回值,6。

另外,在函数调用过程中如果一个RISC-V函数需要的参数超过了八个寄存器,即a0到a7,那么它应该按照标准调用约定的规则,将多余的参数通过堆栈(stack)传递。具体来说,调用者(caller)在进行函数调用前,需要将多余的参数按照顺序压入堆栈中,并且在调用后将它们从堆栈中弹出。被调用者(callee)在接收到参数后,需要从堆栈中按照相反的顺序取出多余的参数,并且在返回前将它们放回堆栈中。这样可以保证函数调用前后堆栈的内容和指针不变,以及参数的正确传递。

例如,如果一个函数需要10个整数参数,那么它可以将前八个参数放在寄存器a0到a7中,将后两个参数压入堆栈中。被调用者可以从寄存器a0到a7中直接读取前八个参数,从堆栈中读取后两个参数。在返回前,被调用者需要将后两个参数放回堆栈中。调用者在函数返回后,需要将后两个参数从堆栈中弹出。

RISC-V基础之函数调用(五)函数递归调用及函数参数数量溢出(超出现有寄存器个数)约定(包含实例),RISC-V基础知识,risc-v图(b) 显示了被调用者堆栈帧的组织。 堆栈帧保存临时、参数和返回地址寄存器(如果由于后续函数调用而需要保存它们),以及函数将修改的任何已保存寄存器。 它还保存局部数组和任何多余的局部变量。 如果被调用者有超过八个参数,它会在调用者的堆栈帧中找到它们。 

 

到了这里,关于RISC-V基础之函数调用(五)函数递归调用及函数参数数量溢出(超出现有寄存器个数)约定(包含实例)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • RISC-V— 架构基础知识学习

    CPU ,全称为 中央处理器单元 ,简称为 处理器 。 ARM (Advanced RISC Machines )是一家诞生于英国的处理器设计与软件公司,总部位于英国的剑桥,其主要业务是设计 ARM 架构的处理器,同时提供与 ARM 处理器相关的配套软件,各种 Soc 系统 IP 、物理 IP 、 GPU 、视频和显示等产品。

    2024年02月08日
    浏览(49)
  • RISC-V基础之浮点指令(包含实例)

    RISC-V体系结构定义了可选的浮点扩展,分别称为RVF、RVD和RVQ,用于操作单精度、双精度和四倍精度的浮点数。RVF/D/Q定义了32个浮点寄存器,f0到f31,它们的宽度分别为32位、64位或128位。当一个处理器实现了多个浮点扩展时,它使用浮点寄存器的低位部分来执行低精度的指令。

    2024年02月13日
    浏览(43)
  • 【RISC-V DSP设计】基于CEVA DSP架构的指令集分析(二)-函数列表

    目录 表3-1:定点滤波器功能 表3-2:定点快速傅里叶变换(FFT)函数 表3-3:定点数学函数 表3-4:定点三角函数 表3-5:定点向量函数 表3-6:定点矩阵函数 表3-7:浮点滤波器函数 表3-8:浮点快速傅里叶变换(FFT)函数 表3-9:浮点数学函数 表3-10:浮点三角函数 表3-11:浮点向量

    2024年02月19日
    浏览(45)
  • 计算机系统基础(六)之RISC-V流水线设计——用ChatGPT辅助学习

    一、CPU的流水线通常被划分为五个部分,它们是: 取指令(Instruction Fetch):从内存中获取指令并将其放入指令寄存器中。 指令译码(Instruction Decode):将指令从指令寄存器中读取并解码成相应的操作。 执行指令(Execution):根据操作码执行指令,可能需要读取寄存器或内

    2024年02月03日
    浏览(41)
  • RISC-V基础指令之addi与lui(生成一个更大的立即数,包含负数符号拓展的特殊情况)

    这两条指令都是RISC-V体系结构中的整数指令,它们的功能和格式如下: lui指令的全称是Load Upper Immediate,它的功能是把一个20位的立即数加载到寄存器的高20位,低12位为0。它的格式是: lui rd, imm 其中,rd是目标寄存器,imm是20位的立即数。例如,lui x1, 0x12345会把0x12345000加载到

    2024年02月11日
    浏览(47)
  • 【RISC-V】RISC-V寄存器

    寄存器 别名 全称 说明 X0 zero 零寄存器 可做源寄存器(rs)或目标寄存器(rd) X1 ra 链接寄存器 保存函数返回地址 X2 sp 栈指针寄存器 指向栈的地址 X3 gp 全局寄存器 用于链接器松弛优化 X4 tp 线程寄存器 常用于在OS中保存指向进程控制块(task_struct)数据结构的指针 X5 ~ X7 X28 ~ X31 t0

    2024年02月10日
    浏览(40)
  • RISC-V(1)——RISC-V是什么,有什么用

    目录 1. RISC-V是什么 2. RISC-V指令集 3. RISC-V特权架构 4. RiscV的寄存器描述 5. 指令  5.1 算数运算—add/sub/addi/mul/div/rem  5.2 逻辑运算—and/andi/or/ori/xor/xori 5.3 位移运算—sll/slli/srl/srli/sra/srai 5.4 数据传输—lb/lh/lw/lbu/lhu/lwu/sb/sh/sw 5.5 比较指令—slt/slti/sltu/sltiu 5.6 条件分支指令—

    2024年02月11日
    浏览(48)
  • 什么是RISC-V?以及RISC-V和ARM、X86的区别

    RISC-V 是基于 RISC 精简指令集架构开发的一个开放式指令集架构,它是由加州大学伯克利分校的计算机科学教授 Krste Asanovic (克里斯蒂安·阿萨诺维奇)领导的团队开发,RISC-V是 开放 的,任何人都可以使用它来开发处理器芯片和其他硬件,而无需支付任何许可或使用费用。

    2024年02月15日
    浏览(50)
  • 【RISC-V】基于开源RISC-V MCU内核PicoRV32的FPGA移植

    核心板:GW2A-LV18PG256C8IC8I7 底板:Dock底板 有关核心板和底板相关资料的参考网址:Tang Primer 20K - Sipeed Wiki  FPGA硬件设计软件:高云云源软件;下载地址:广东高云半导体科技股份有限公司 PicoRV32描述,以及文档介绍详情见网址PicoRV32 - 高云 - 广东高云半导体科技股份有限公司

    2023年04月09日
    浏览(43)
  • 国内 RISC-V 产学研基地成立,Intel、Arm、RISC-V 将三分天下?

    作者 | 伍杏玲 出品 | CSDN(ID:CSDNnews) 在IT界,CPU芯片和操作系统是网信领域最基础的核心技术。但在芯片领域,技术和资本的门槛较高,应用范围最广的指令集架构需获得专利授权才能使用,如x86、Arm等。而投入研发的资金,以14nm工艺为例,需上亿元的研发费用。 如何降

    2024年01月17日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包