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

news2024/10/1 17:17:32

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

 

假设程序从地址 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) 时的栈情况。

为了说明,我们假设 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中直接读取前八个参数,从堆栈中读取后两个参数。在返回前,被调用者需要将后两个参数放回堆栈中。调用者在函数返回后,需要将后两个参数从堆栈中弹出。

图(b) 显示了被调用者堆栈帧的组织。 堆栈帧保存临时、参数和返回地址寄存器(如果由于后续函数调用而需要保存它们),以及函数将修改的任何已保存寄存器。 它还保存局部数组和任何多余的局部变量。 如果被调用者有超过八个参数,它会在调用者的堆栈帧中找到它们。 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/836648.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

移动应用开发:为移动设备优化的在线教育培训平台

移动应用开发在当今数字化时代扮演着至关重要的角色,尤其是在在线教育培训领域。移动设备的普及和使用,使得人们可以随时随地访问学习资源,这为在线教育培训平台提供了巨大的发展机会。本文将探讨如何为移动设备优化在线教育培训平台&#xf…

常见的数据结构(顺序表、顺序表、链表、栈、队列、二叉树)

线性表(Linear List)  1.什么是线性表 2.线性表的特点 3.线性表的基本运算 顺序表 1.什么是顺序表 2.时间复杂度: 链表 1.什么是链表 2.单向链表 3. 双向链表 4.ArrayList和LinkedList的使用 栈Stack  1.什么是栈  2.栈的基本方法 队列…

新人如何高效写 API 文档

什么是 API 文档? 在深入研究 API 文档之前,让我简要解释一下 API 是什么以及它的基本功能。 API 是应用程序编程接口的首字母缩写。 ​ 编辑 切换为居中 通过 API 将设备连接到数据库 无论你是初学者还是高级开发人员,你都会在软件开发…

数学建模-博弈论

张雪峰: 小时候以为长大就好了 长大后才是问题的开始。 人生最重要的就三件事: 学习,工作,结婚。 第一件事:学习 以现在的人生角度去看,其实学习这件事是最容易的,十几年只干好这一件事就行了,我们那时候不…

网络安全 Day26-PHP 简单学习

PHP 简单学习 1. 为什么要学习PHP2. PHP语法3. php 变量4. 字符串数据5. PHP 函数6. 数组 1. 为什么要学习PHP php存量多开源软件多很多安全流程 渗透方法 sql注入基于PHP语言入门简单 2. PHP语法 格式: <?php 内容?>或<?内容?>结尾分号例子<?php phpin…

Qt展示动态波形

Qt展示动态波形 需求描述成品展示实现难点Qt多线程 需求描述 接入串口&#xff0c;配置串口顺序进行接收数据&#xff1b;数据分成两个串口分别传入&#xff0c;使用多线程并发接入&#xff1b;时域数据有两个通道&#xff08;I&#xff0c;Q&#xff09;&#xff0c;分别以实…

flask-session、数据库连接池

flask 自带session---》以cookie的形式放到了浏览器中---》加密 真正的session&#xff0c;是在服务端存储 -django中存在djangosession表中 -flask中&#xff0c;使用第三方&#xff0c;保存在---》redis中---》flask-session 使用步骤 pip install flask-session …

Linux笔记1(系统状态等)

man命令&#xff1a; man name: man section name: man -k regexp: 在 Linux 中&#xff0c;man 命令用于查看命令、函数或配置文件等的手册页&#xff0c;提供了详细的帮助文档。man 是 "manual" 的缩写。man 命令的用法如下&#xff1a; man [选项] [命令名]例如&…

探索编程世界的宝藏:程序员必掌握的20大算法

文章目录 1 引言2 冒泡排序算法&#xff1a;编程世界的排序魔法 &#x1f9d9;‍♀️&#x1f522;3 选择排序算法&#xff1a;排序世界的精确挑选器 &#x1f3af;&#x1f522;4 插入排序算法&#xff1a;排序世界的巧妙插珠者 ✨&#x1f522;5 快速排序算法&#xff1a;排序…

基于人工智能的智能矿山解决方案

什么是智能矿山&#xff1f; 智能矿山是一种运用先进技术和智能化系统来管理和监控矿山运营的概念。它利用传感器、无线通信、数据分析和人工智能等技术&#xff0c;实现对矿山内部各个环节的实时监测、自动化控制和智能决策&#xff0c;从而提高矿山的效率、安全性和可持续性。…

自动化测试的优缺点

围绕测试自动化有很多议论&#xff0c;组织正在进行大量投资以利用测试自动化的好处。测试自动化可以指使用软件工具自动执行测试、将实际结果与预期结果进行比较以及报告差异/错误的过程。实施测试自动化的主要原因之一是减少手动工作和相关风险&#xff0c;同时测试重复性任务…

List集合的对象传输的两种方式

说明&#xff1a;在一些特定的情况&#xff0c;我们需要把对象中的List集合属性存入到数据库中&#xff0c;之后把该字段取出来转为List集合的对象使用&#xff08;如下图&#xff09; 自定义对象 public class User implements Serializable {/*** ID*/private Integer id;/*…

LCD驱动芯片VK1024B兼容HT系列驱动芯片,体积更小

产品型号&#xff1a;VK1024B 产品&#xff1a;VINKA/永嘉微电 封装形式&#xff1a;SOP16 产品年份&#xff1a;新年份 工程服务&#xff0c;技术支持&#xff0c;用芯服务 VK1024概述&#xff1a; VK1024B 是 24 点、 内存映象和多功能的 LCD 驱动&#xff0c; VK1024B …

用Log4j 2记录日志

说明 maven工程中增加对Log4j 2的依赖 下面代码示例的maven工程中的pom.xml文件中需要增加对Log4j 2的依赖&#xff1a; <dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.20.0&…

纯小白也能看懂,十分钟帮你快速了解云原生概念

纯小白也能看懂&#xff0c;十分钟帮你了解云原生技术 一、麻烦的一天二、魔法的种子1. Docker2. Kubernetes 三、渐入佳境1. 技术与术语容器化技术DevOps弹性伸缩Sidecar服务网格 2. 组件与框架DockerKubernetesHelmIstioPrometheusJaegerEnvoy 四、前路漫漫 随着云原生相关技…

PHP实践:用openssl打造安全可靠的API签名验证系统

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f3c6;本文已…

clickhouse断电重启故障解决方案

业务场景 公司的一个日志系统用到了clickhouse。一线运维反映说有个生产环境因为异常断电造成服务器重启。在执行日志系统的启动脚本时&#xff0c;一直报clickhouse启动不起来&#xff0c;日志系统无法使用。 问题排查 通过阅读启动脚本代码&#xff0c;以及启动日志系统&a…

【安全测试】Web应用安全之XSS跨站脚本攻击漏洞

目录 前言 XSS概念及分类 反射型XSS(非持久性XSS) 存储型XSS(持久型XSS) 如何测试XSS漏洞 方法一&#xff1a; 方法二&#xff1a; XSS漏洞修复 原则&#xff1a;不相信客户输入的数据 处理建议 资料获取方法 前言 以前都只是在各类文档中见到过XSS&#xff0c;也进…

接口测试前置基础学习

网址结构&#xff08;面试重点&#xff09; 网址就是浏览器请求的地址。 网址组成&#xff1a;&#xff08;6个部分&#xff09; 1 协议http协议&#xff0c;超文本传输协议&#xff0c;https协议&#xff0c;s表示ssl加密。传输更安全。 2 域名&#xff1a;就是ip地址。从…

巨量算数:2023中国家居行业洞察报告(附下载

关于报告的所有内容&#xff0c;公众【营销人星球】获取下载查看 核心观点 回首过去几年&#xff0c;在疫情反复、地产热度消减、人口出生率下降等各种不利因素影响下&#xff0c;家居行业及其上下游面临极大挑战&#xff0c;整体行业遇冷&#xff0c;市场规模的增速进一步放…