【RISC-V】寄存器及 PCS(过程调用标准)

news2024/11/23 13:27:10

文章目录

    • 寄存器与别名
    • 函数入栈
      • 示例代码
      • 作用
      • 为什么需要保存
    • 函数出栈
      • 示例代码
      • 作用
      • 为什么需要恢复
    • 浮点寄存器的保存
      • 示例代码
      • 作用
    • 浮点寄存器的恢复
      • 示例代码
      • 作用

寄存器与别名

在这里插入图片描述

  • Caller(调用者)指的是调用(或执行)一个函数的代码段或函数。它是主动发起函数调用的一方。

  • Callee(被调用者)指的是被调用的函数本身。它是被动接受函数调用并执行相应操作的一方。

简而言之,caller 是调用函数的一方,而 callee 是被调用函数的一方。

所以函数调用时需要保存的寄存器为

  • 通用寄存器 16 个
  • 浮点寄存器 20 个(需要注意的是,浮点状态寄存器一般也是需要保存的,所以一般为 21 个)

函数入栈

示例代码

/* RV32I caller registers + 21 FPU caller registers */
#define CONTEXT_REG_NUM (4*(16 + 21))

__asm volatile("addi sp, sp, %0" : : "i"(-CONTEXT_REG_NUM) :);\
__asm volatile("sw ra,  0*4(sp) \n\ 
            sw t0,  1*4(sp) \n\ 
            sw t1,  2*4(sp) \n\ 
            sw t2,  3*4(sp) \n\ 
            sw a0,  4*4(sp) \n\ 
            sw a1,  5*4(sp) \n\ 
            sw a2,  6*4(sp) \n\ 
            sw a3,  7*4(sp) \n\ 
            sw a4,  8*4(sp) \n\ 
            sw a5,  9*4(sp) \n\ 
            sw a6, 10*4(sp) \n\ 
            sw a7, 11*4(sp) \n\ 
            sw t3, 12*4(sp) \n\ 
            sw t4, 13*4(sp) \n\ 
            sw t5, 14*4(sp) \n\ 
            sw t6, 15*4(sp)"); \
            SAVE_FPU_CONTEXT(); \

作用

#define CONTEXT_REG_NUM (4*(16 + 21))

这一行定义了一个常量 CONTEXT_REG_NUM ,它的值是16和21之和乘以4的结果。这个值表示需要保存的寄存器数量。

__asm volatile("addi sp, sp, %0" : : "i"(-CONTEXT_REG_NUM) :);\

这一行使用 addi 指令将 CONTEXT_REG_NUM 的值从堆栈指针( sp )减去。它有效地在堆栈上为保存的寄存器分配空间。 volatile 关键字确保编译器不会优化这段汇编代码。

剩下的代码使用 sw (存储字)指令将特定寄存器的值保存到堆栈中。每个 sw 指令将一个寄存器的值存储在距离堆栈指针( sp )的偏移量处。偏移量通过将寄存器编号乘以4(因为每个字的大小为4个字节)并加到堆栈指针上来计算。

在这段代码中保存的寄存器是:

  • ra (返回地址寄存器)
  • t0 到 t6 (临时寄存器)
  • a0 到 a7 (参数寄存器)

RISC-V 这段内联汇编代码是用来保存寄存器的值到栈上的。具体来说,它将寄存器的值保存在栈指针(sp)指向的内存位置上,每个寄存器的值占据4个字节的空间。

这段代码使用了RISC-V的汇编指令 sw ,它用于将寄存器的值存储到内存中。每个 sw 指令都有两个操作数,第一个操作数是要存储的寄存器,第二个操作数是存储位置的地址偏移量。

具体来说,这段代码将以下寄存器的值保存到栈上:

  • ra
  • t0
  • t1
  • t2
  • a0
  • a1
  • a2
  • a3
  • a4
  • a5
  • a6
  • a7
  • t3
  • t4
  • t5
  • t6

这段代码通常用于在函数调用时保存寄存器的值,以便在函数返回时能够正确地恢复这些寄存器的值。这样可以确保函数调用过程中不会破坏这些寄存器中的值。

  • 需要注意的是,这段代码是内联汇编,嵌入在C或C++代码中。它使用了 asm 关键字来指示编译器这是一段汇编代码,并使用 volatile 来确保编译器不会对这段代码进行优化

SAVE_FPU_CONTEXT(); 保存浮点寄存器见下文。

为什么需要保存

函数调用时需要保存这些寄存器的原因是为了保护调用者的寄存器值,以便函数执行时不会干扰调用者的代码。
当一个函数被调用时,它会使用一些寄存器来保存重要的值或临时变量。然而,这些寄存器的值可能会在函数执行过程中被修改。为了确保函数的正确执行,需要保存这些寄存器的值,以便在函数执行完毕后能够恢复调用者原来的寄存器值。

具体来说,保存这些寄存器的目的是为了:

  • 保护调用者的返回地址(ra寄存器),以便函数执行完毕后能够正确返回到调用者的位置。
  • 保护调用者的临时寄存器(t0-t6),以便函数执行期间可以使用这些寄存器来保存临时值,而不会干扰调用者的代码。
  • 保护调用者的参数寄存器(a0-a7),以便函数执行期间可以使用这些寄存器来访问传递给函数的参数,而不会干扰调用者的代码。

通过将这些寄存器的值保存到堆栈中,函数可以在执行期间使用堆栈空间来保存自己的局部变量和临时值,而不会覆盖调用者的寄存器值。在函数执行完毕后,可以从堆栈中恢复保存的寄存器值,然后返回到调用者。

这种做法可以确保调用者的代码的完整性,并确保函数可以安全地被调用和返回,而不会产生意外的副作用。

函数出栈

示例代码

__asm volatile("lw ra,  0*4(sp) \n\
    lw t0,  1*4(sp) \n\
    lw t1,  2*4(sp) \n\
    lw t2,  3*4(sp) \n\
    lw a0,  4*4(sp) \n\
    lw a1,  5*4(sp) \n\
    lw a2,  6*4(sp) \n\
    lw a3,  7*4(sp) \n\
    lw a4,  8*4(sp) \n\
    lw a5,  9*4(sp) \n\
    lw a6, 10*4(sp) \n\
    lw a7, 11*4(sp) \n\
    lw t3, 12*4(sp) \n\
    lw t4, 13*4(sp) \n\
    lw t5, 14*4(sp) \n\
    lw t6, 15*4(sp) \n");\
    RESTORE_FPU_CONTEXT(); \
__asm volatile("addi sp, sp, %0" : : "i"(CONTEXT_REG_NUM) :);\

作用

RISC-V 内联汇编代码,用于从栈中恢复寄存器的值。它使用了RISC-V的汇编指令lw(load word)来加载栈中的值到相应的寄存器中。

具体解释如下:

  • lw ra, 0 * 4(sp) :从栈中加载值到寄存器ra(返回地址寄存器)中。栈指针(sp)加上0 * 4表示栈中的偏移量为0,乘以4是因为每个字(word)的大小为4字节。
  • lw t0, 1 * 4(sp) :从栈中加载值到寄存器t0中,偏移量为1 * 4。
  • lw t1, 2 * 4(sp) :从栈中加载值到寄存器t1中,偏移量为2 * 4。
  • 依此类推,后续指令将从栈中加载值到其他寄存器中,每个寄存器的偏移量都会增加。

具体来说,这段代码将从栈上恢复以下寄存器的值:

  • ra
  • t0
  • t1
  • t2
  • a0
  • a1
  • a2
  • a3
  • a4
  • a5
  • a6
  • a7
  • t3
  • t4
  • t5
  • t6

这段代码的作用是将之前保存在栈上的寄存器值恢复到相应的寄存器中,通常用于函数返回之前的清理工作。这种技术被称为函数调用的栈帧恢复。

  • 需要注意的是,这段代码是内联汇编,嵌入在C或C++代码中。它使用了 asm 关键字来指示编译器这是一段汇编代码,并使用 volatile 来确保编译器不会对这段代码进行优化
__asm volatile("addi sp, sp, %0" : : "i"(CONTEXT_REG_NUM) :);\

这部分代码使用 RISC-V 汇编语言的 addi 指令来修改堆栈指针 sp 的值,以恢复堆栈的大小。 %0 是占位符,表示在指令中使用 CONTEXT_REG_NUM 的值。

RESTORE_FPU_CONTEXT(); 恢复浮点寄存器见下文。

为什么需要恢复

在程序执行过程中,函数调用会导致寄存器的值被修改。这些寄存器可能包含重要的数据或程序状态。为了确保函数调用后程序能够正确继续执行,需要在函数返回之前将寄存器的值恢复到函数调用之前的状态。

这种寄存器值的恢复通常发生在函数的退出代码中,以确保程序能够正确地返回到调用函数的位置。如果不进行寄存器值的恢复,函数的返回值可能会出错,导致程序的行为不可预测。

浮点寄存器的保存

示例代码

#define SAVE_FPU_CONTEXT()  { \
    __asm volatile("fsw ft0, 21*4(sp) \n\
             fsw ft1, 22*4(sp) \n\
             fsw ft2, 23*4(sp) \n\
             fsw ft3, 24*4(sp) \n\
             fsw ft4, 25*4(sp) \n\
             fsw ft5, 26*4(sp) \n\
             fsw ft6, 27*4(sp) \n\
             fsw ft7, 28*4(sp) \n\
             fsw fa0, 29*4(sp) \n\
             fsw fa1, 30*4(sp) \n\
             fsw fa2, 31*4(sp) \n\
             fsw fa3, 32*4(sp) \n\
             fsw fa4, 33*4(sp) \n\
             fsw fa5, 34*4(sp) \n\
             fsw fa6, 35*4(sp) \n\
             fsw fa7, 36*4(sp) \n\
             fsw ft8, 37*4(sp) \n\
             fsw ft9, 38*4(sp) \n\
             fsw ft10, 39*4(sp) \n\
             fsw ft11, 40*4(sp) \n\
             frsr t6 \n\
             sw t6, 41*4(sp) \n");\
}

作用

  • fsw ft0, 21 * 4(sp) :将ft0寄存器的值保存到栈指针(sp)加上21 * 4的地址处。
  • fsw ft1, 22 * 4(sp) :将ft1寄存器的值保存到栈指针(sp)加上22 * 4的地址处。
  • …(以此类推,保存ft2至ft11寄存器的值)
  • fsw fa0, 29 * 4(sp) :将fa0寄存器的值保存到栈指针(sp)加上29 * 4的地址处。
  • fsw fa1, 30 * 4(sp) :将fa1寄存器的值保存到栈指针(sp)加上30 * 4的地址处。
  • …(以此类推,保存fa2至fa7寄存器的值)
  • fsw ft8, 37 * 4(sp) :将ft8寄存器的值保存到栈指针(sp)加上37 * 4的地址处。
  • fsw ft9, 38 * 4(sp) :将ft9寄存器的值保存到栈指针(sp)加上38 * 4的地址处。
  • …(以此类推,保存ft10和ft11寄存器的值)
  • frsr t6 :将浮点状态寄存器的值保存到t6寄存器中。
  • sw t6, 41 * 4(sp) :将t6寄存器的值保存到栈指针(sp)加上41 * 4的地址处。

具体来说,这段代码将以下寄存器的值保存到栈上:

  • ft0
  • ft1
  • ft2
  • ft3
  • ft4
  • ft5
  • ft6
  • ft7
  • fa0
  • fa1
  • fa2
  • fa3
  • fa4
  • fa5
  • fa6
  • fa7
  • ft8
  • ft9
  • ft10
  • ft11
  • 浮点状态寄存器

通过这段代码,浮点寄存器的值和浮点状态寄存器的值被保存到了指定的内存位置,以便稍后可以恢复它们的值

浮点寄存器的恢复

示例代码

#define RESTORE_FPU_CONTEXT() { \
    __asm volatile("flw ft0, 21*4(sp) \n\
             flw ft1, 22*4(sp) \n\
             flw ft2, 23*4(sp) \n\
             flw ft3, 24*4(sp) \n\
             flw ft4, 25*4(sp) \n\
             flw ft5, 26*4(sp) \n\
             flw ft6, 27*4(sp) \n\
             flw ft7, 28*4(sp) \n\
             flw fa0, 29*4(sp) \n\
             flw fa1, 30*4(sp) \n\
             flw fa2, 31*4(sp) \n\
             flw fa3, 32*4(sp) \n\
             flw fa4, 33*4(sp) \n\
             flw fa5, 34*4(sp) \n\
             flw fa6, 35*4(sp) \n\
             flw fa7, 36*4(sp) \n\
             flw ft8, 37*4(sp) \n\
             flw ft9, 38*4(sp) \n\
             flw ft10, 39*4(sp) \n\
             flw ft11, 40*4(sp) \n\
             lw t6, 41*4(sp) \n\
             fssr t6, t6 \n");\
}

作用

代码中的 __asm volatile 表示内联汇编代码的开始。

flw 指令用于从栈指针(sp)的特定偏移位置加载浮点寄存器的值。例如, flw ft0, 21 * 4(sp) 表示将栈指针(sp)偏移21 * 4字节处的值加载到ft0寄存器中。

这段代码一共恢复了20个浮点寄存器的值,从ft0到ft11和fa0到fa7。

此外,还使用 lw 指令将栈指针(sp)偏移41 * 4字节处的值加载到 t6 寄存器中,并使用 fssr 指令将 t 6寄存器的值存储到浮点状态和控制寄存器中。 其实就是将浮点保存时保存在栈上的浮点状态寄存器恢复到浮点状态寄存器中

具体来说,这段代码从栈上恢复以下寄存器的值:

  • ft0
  • ft1
  • ft2
  • ft3
  • ft4
  • ft5
  • ft6
  • ft7
  • fa0
  • fa1
  • fa2
  • fa3
  • fa4
  • fa5
  • fa6
  • fa7
  • ft8
  • ft9
  • ft10
  • ft11
  • 浮点状态寄存器

因此,这段代码的作用是从栈中恢复浮点寄存器的值,以便在程序中继续使用之前保存的浮点寄存器上下文。

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

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

相关文章

Pandas 和 CSV文件读取导出小纪

要从CSV文件中访问数据,我们需要一个函数read_csv(),它以数据帧的形式检索数据。 read_csv() 语法: pd.read_csv(filepath_or_buffer, sep’ ,’ , header’infer’, index_colNone, usecolsNone, engineNone, skiprowsNone, nrowsNone) 参…

vue启动编译时报错:134(内存溢出)

项目环境:win7 vue2 webpack2 最近开发过程中项目莫名其妙就起不来了,报错大致如下: 经过一番搜索,尝试了多种方法都不行,比如: 1. npm install increase-memory-limit npm install cross-env 在package.json中添加…

首个!AI开发者创作激励计划开启,有成长、有收入

各种视频网站都有什么创作激励!那什么时候有专属于AI开发者的创作激励?好!那AI开发者的福利来了!!既能潜心进行模型开发,又能提升技术能力,还能领一份创作金!!飞桨AI Stu…

Codeforces Round 882 (Div. 2)(视频讲解A——D)

[TOC](Codeforces Round 882 (Div. 2)&#xff08;视频讲解A——D&#xff09;) 讲解在B站&#xff1a;Codeforces Round 882 (Div. 2)&#xff08;视频讲解A——D&#xff09; A The Man who became a God #include<bits/stdc.h> #define endl \n #define INF 0x3f3…

k8s 大量 pod 处于 ContainerStatusUnknown 状态

如图所示&#xff0c;nexus 正常运行&#xff0c;但产生了大量的状态不明的 pod&#xff0c;原因也无从所知 解决办法&#xff0c;删除多余的 pod&#xff0c;一个一个删除&#xff0c;非常费劲 获取 namespace 中状态为 ContainerStatusUnknown 的 pod&#xff0c;并删除 …

从零学习微服务

更新中&#xff0c;关注不断更… 这里讲的是基于springboot和springboot alibaba的微服务&#xff0c;Spring Boot和Spring Boot Alibaba都是基于Spring框架的开源框架&#xff0c;用于简化应用程序的开发和部署。 这篇文章里会介绍微服务的整体概念&#xff0c;目前国内常用的…

CSDN周赛62期反馈及简要题解

持续了十期的《计算之魂》主题周赛告一段落&#xff0c;可能上周就已经告一段落了&#xff0c;以致于也出现了重复的考题。这本书确实不错&#xff0c;里面提到的计算机思维我认为是理解和学习计算机科学的基础。第一次读此书的时候就一口气读到第八章&#xff0c;读到精彩之处…

漏洞复现 || Hadoop未授权访问反弹Shell漏洞

免责声明 技术文章仅供参考,任何个人和组织使用网络应当遵守宪法法律,遵守公共秩序,尊重社会公德,不得危害网络安全,不得利用网络从事危害国家安全、荣誉和利益,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接…

ppt转pdf怎么转换?推荐这几种转换方法

ppt转pdf怎么转换&#xff1f;PPT转PDF可以确保演示文稿的格式、布局和字体在不同设备上的一致性。PDF文件在不同操作系统和软件中都能以相同的方式呈现&#xff0c;避免了因PPT文件打开时出现格式错误或乱码。可能很多人还不知道如何进行转换&#xff0c;下面就给大家推荐几种…

gitee 使用

1.打开git bash 2.cd 进入到合适位置 3.git clone 项目 4.配置用户名和email&#xff08;不然没法记录谁操作的&#xff09;

unordered系列的底层结构——哈希表

目录 哈希概念 哈希冲突 哈希函数 解决哈希冲突的方法 闭散列 线性探测 线性探测的实现 ​编辑 二次探测 开散列 开散列概念 开散列的实现 开散列增容 开散列的思考 哈希概念 顺序结构以及平衡树中&#xff0c;元素关键码与其存储位置之间没有对应的关系&#xf…

https证书已经部署到宝塔,但访问网站还显示不生效问题解决

先说解决方法&#xff1a;重启Nginx服务器即可&#xff0c;可以在宝塔面板右上角直接重启即可解决。 过程 腾讯云的https免费证书只有一年有效期&#xff0c;而且续期不能在原证书上续&#xff0c;只能替换。但是我替换后&#xff0c;访问网站异常。如下图&#xff1a; 可以看…

TypeScript 中的【声明合并】规则

概念&#xff1a; 在TS中&#xff0c;如果定义了多个相同命名的函数&#xff0c;接口或者class 类&#xff0c;那么它们会自动合并成一个类型 函数的合并&#xff1a; 前面章节讲解的函数重载就是使用了定义多个函数的类型进行合并&#xff1a; function reverse(x: number):…

UE4 关于使用Webbrowser插件遇到的问题以及解决办法

1.无法播放网页视频&#xff0c;这是因为UE4的WebBrowser自带的cef3为3071版本&#xff0c;默认不支持h264等直播流&#xff0c;导致web里的直播流无法播放 解决办法&#xff1a;第一种办法&#xff0c;重新编译了cef源码&#xff0c;改成支持H.264&#xff0c;然后在UE4安装目…

2023AU软件,Adobe Audition 全系列软件下载安装教程

Audition 是一款强大的音频编辑软件&#xff0c;能够帮助用户实现各种音频剪辑和混音操作。下面是关于Audition 2023软件的安装教程&#xff0c;供大家参考。 步骤一&#xff1a;下载Audition 通过全系列网站下载或者百度网盘下载都行 步骤二&#xff1a;运行安装程序 下载…

node版本安装包大全

node所有的版本&#xff1a; https://nodejs.org/dist/

利用ChatGPT场景化学习英语听说读写

大家好&#xff0c;我是可夫小子&#xff0c;关注AIGC、读书和自媒体。解锁更多ChatGPT、AI绘画玩法。加我&#xff0c;备注&#xff1a;chatgpt&#xff0c;拉你进群。 我们从初中就开始学习英语&#xff0c;到大学也有小十年&#xff0c;在这个过程中&#xff0c;我们投入了很…

Java OkHttp3 异常:gzip finished without exhausting source 解决办法

今天在开发中遇见一个问题&#xff0c;同样一个请求&#xff0c;okhttp调用时候&#xff0c;报错gzip finished without exhausting source&#xff0c;浏览器和postman确是正常返回结果 String getsync OkHttpUtils.builder().url(wfsSearchUrl)// .addHeader("Accept-E…

【线程池管理工具-Hippo4j】

&#x1f680; 线程池管理工具-Hippo4j &#x1f680; &#x1f332; AI工具、AI绘图、AI专栏 &#x1f340; &#x1f332; 如果你想学到最前沿、最火爆的技术&#xff0c;赶快加入吧✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;CSDN-Java领域优质创作者&#…

【 -bash: /usr/bin/id: 参数列表过长】

这个错误提示说明在执行 /etc/profile 或者 /etc/profile.d 目录下的脚本时&#xff0c;出现了参数列表过长的问题。这可能是由于环境变量或者执行的命令导致的。 要解决这个问题&#xff0c;可以尝试以下方法&#xff1a; 检查脚本内容&#xff1a;检查 /etc/profile 和 /et…