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 = 2和b = 3压入栈中,并将返回地址(主程序中f1(2, 3)的下一条指令)压入栈中。然后跳转到f1的起始地址。 f1在执行时,会在栈顶分配一段空间,存放自己的局部变量c和d。然后计算c = a + b = 5并保存在栈中。f1在调用f2(c)之前,会将参数x = c = 5压入栈中,并将返回地址(f1中f2(c)的下一条指令)压入栈中。然后跳转到f2的起始地址。f2在执行时,会在栈顶分配一段空间,存放自己的局部变量y和z。然后计算y = x * x = 25并保存在栈中。f2在调用f3(y)之前,会将参数r = y = 25压入栈中,并将返回地址(f2中f3(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 = 2和b = 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) 显示了被调用者堆栈帧的组织。 堆栈帧保存临时、参数和返回地址寄存器(如果由于后续函数调用而需要保存它们),以及函数将修改的任何已保存寄存器。 它还保存局部数组和任何多余的局部变量。 如果被调用者有超过八个参数,它会在调用者的堆栈帧中找到它们。
相关文章:
RISC-V基础之函数调用(五)函数递归调用及函数参数数量溢出(超出现有寄存器个数)约定(包含实例)
首先先解释一下栈在函数调用中的作用,更详细的部分请参照考研复习之数据结构笔记(五)栈和队列(上)(包含栈的相关内容)_管二狗赶快去工作!的博客-CSDN博客 函数嵌套调用栈的作用是用…...
力扣:48. 旋转图像(Python3)
题目: 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 来源:力扣(LeetCode) 链接&…...
HarmonyOS应用开发者基础与高级认证题库——中级篇
系列文章目录 HarmonyOS应用开发者基础与高级认证题库——基础篇 HarmonyOS应用开发者基础与高级认证题库——中级篇 文章目录 系列文章目录前言一、判断二、单选三、多选 前言 今天刚换了台果子手机就收到了华子鸿蒙开发认证邀请(认证链接)࿰…...
Python中实现多个列表、字典、元组、集合的连接
目录 目录 前言 一、列表 1、运算符 2、extend()方法 3、解包操作 * 二、字典 1、update()方法 2、解包操作 ** 三、元组 1、 运算符 2、解包操作 * 四、集合 1、union方法 2、| 运算符 3、解包操作 * 五、不同类…...
1005 继续(3n+1)猜想
描述 卡拉兹(Callatz)猜想已经在1001中给出了描述。在这个题目里,情况稍微有些复杂。 当我们验证卡拉兹猜想的时候,为了避免重复计算,可以记录下递推过程中遇到的每一个数。例如对 n3 进行验证的时候,我们需要计算 3、5、8、4、…...
基于图片、无人机、摄像头拍摄进行智能检测功能
根据要求进行无人机拍摄的视频或图片进行智能识别,开发过程需要事项 1、根据图片案例进行标记,进行模型训练 2、视频模型训练 开发语言为python 根据需求功能进行测试结果如下 根据车辆识别标记进行的测试结果截图 测经过查看视频 8月1日...
Boost开发指南-4.2ignore_unused
ignore_unused 编写代码的过程中有时会出现一些暂时用不到但又必须保留的变量,GCC等编译器会对此发出警告,使用-Wunused可以关闭这些警告消息,不过这也有可能导致潜在的隐患。古老的办法是使用(void)var的形式来“使用”一下变量,…...
【Mybatis】XML映射文件
目录 11.3XML映射文件 1.select 2.insert、update、delete 3.Sql 4.parameters(参数) 5.resultMap 6.resultMap 使用示例 (1)在先前创建的数据库stu中创建表student 2,并插入若干条数据,代码如下: (2)创建工程mybatis_ResultMap_demo。 (…...
11.2【MyBatis】主配置文件
目录 11.2【MyBatis】主配置文件 1.properties(属性) 2.settings(设置) 3.typeAliases(别名) 4.typeHandlers类型处理器 5.objectFactory 对象工厂 6.plugins(插件) 7.environments (配置环境) 8.mappers (映射器) 11.2【MyBatis】主配置文件 MyBatis的 …...
linuxARM裸机学习笔记(2)----汇编LED灯实验
MX6ULL 的 IO IO的复用功能 这里的只使用了低五位,用来配置io口,其中bit0~bit3(MUX_MODE)就是设置 GPIO1_IO00 的复用功能的,GPIO1_IO00 一共可以复用为 9种功能 IO,分别对应 ALT0~ALT8。每种对应了不同的功能 io的属性配置 HY…...
用C语言实现插入排序算法
1.设计思路 用插入排序对长度为n的待排序数组A进行排序的伪代码(在代码中,A中元素的数目n用A.length来表示)。 伪代码如下: INSERTION-SORT(A) for j2 to A.length:keyA[j] //将A[j]插入已排序序列A[1..j-1]ij-1while i>0…...
2023 电赛E题--可能会出现的问题以及解决方法
2023年电赛E题报告模板(K210版)--可直接使用 本文链接:2023年电赛E题报告模板(K210版)--可直接使用_皓悦编程记的博客-CSDN博客 解决激光笔在黑色区域无法识别 本文链接: 2023 电赛 E 题 激光笔识别有误-…...
Demystifying Prompts in Language Models via Perplexity Estimation
Demystifying Prompts in Language Models via Perplexity Estimation 原文链接 Gonen H, Iyer S, Blevins T, et al. Demystifying prompts in language models via perplexity estimation[J]. arXiv preprint arXiv:2212.04037, 2022. 简单来说就是作者通过在不同LLM和不同…...
WEB集群——http、tomcat
1. 简述静态网页和动态网页的区别。 2. 简述 Webl.0 和 Web2.0 的区别。 3. 安装tomcat8,配置服务启动脚本,部署jpress应用。 1. 简述静态网页和动态网页的区别。 1)、静态网页 (1)、什么是静态网页 请求响应信息&…...
Socks5代理:网络安全与爬虫之利器
一、Socks5代理:简介与工作原理 Socks5代理,全称为Socket Secure 5代理,是一种允许用户通过代理服务器进行网络连接的技术。它是Socks协议的最新版本,在网络安全和数据传输方面有着显著的优势。 Socks5代理与其他代理的不同之处在…...
如何兼容低版本浏览器
如何兼容低版本浏览器 分为三个部分来说 HTML 低版本浏览器无法识别新增的HTML5元素,如果要兼容这部分浏览器,需要做以下处理: 对于非可替换元素,比如article、section、header、footer等,这种元素虽然低版本浏览器不识…...
【雕爷学编程】MicroPython动手做(39)——机器视觉之图像基础2
MixPY——让爱(AI)触手可及 MixPY布局 主控芯片:K210(64位双核带硬件FPU和卷积加速器的 RISC-V CPU) 显示屏:LCD_2.8寸 320*240分辨率,支持电阻触摸 摄像头:OV2640,200W像素 扬声器&#…...
gitlab搭建
回到目录 GitLab 是一个用于仓库管理系统的开源项目,使用 Git 作为代码管理工具,并在此基础上搭建起来的 Web 服务。 Gitlab 是被广泛使用的基于 git 的开源代码管理平台, 基于 Ruby on Rails 构建, 主要针对软件开发过程中产生的代码和文档进行管理,…...
JMeter 的使用
文章目录 1. JMeter下载2. JMeter的使用2.1 JMeter中文设置2.2 JMeter的使用2.2.1 创建线程组2.2.2 HTTP请求2.2.3 监听器 1. JMeter下载 官网地址 https://jmeter.apache.org/download_jmeter.cgi https://dlcdn.apache.org//jmeter/binaries/apache-jmeter-5.6.2.zip 下载解…...
Java语言 Iterator 如何装换成 List
迭代器如何逆向转换成List集合 在 Java 中,迭代器(Iterator)是一种用于遍历集合中元素的对象,它提供了一种简单而一致的方式来访问集合中的元素,而不需要暴露集合内部的结构。如果我们需要将一个迭代器逆向转换成 Lis…...
【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...
国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...
1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...
