Linux实验-分析RISC-V循环代码

qq_44803740 2023-03-13 18:25:15

RISC-V中常见寄存器作用

sp,ra,s0,a0,a5都是RISC-V汇编语言中的寄存器名。sp是栈指针寄存器,常储存栈顶地址。ra是返回地址寄存器,用来存储函数调用后的返回地址。s0是保存寄存器,用来保存函数调用前后不变的值,可以保存栈底。a0和a5是参数寄存器,用来传递函数调用的参数。

RISC-V中常见指令

加载指令:

ld和lw都是RISC-V指令集中的加载指令,用于从存储器中读取数据到寄存器中。它们的区别在于ld是加载双字(64位)数据,而lw是加载字(32位)数据。格式如下:

ld rd, offset(rs1) // rd = M[rs1 + offset]

lw rd, offset(rs1) // rd = M[rs1 + offset][31:0]

其中rd是目标寄存器,rs1是基址寄存器,offset是偏移量,M表示存储器1。

存储指令:

sd和sw都是RISC-V指令集中的存储指令,用于将寄存器中的数据写入到存储器中。它们的区别在于sd是存储双字(64位)数据,而sw是存储字(32位)数据。格式如下:

sd rs2, offset(rs1) // M[rs1 + offset] = rs2

sw rs2, offset(rs1) // M[rs1 + offset][31:0] = rs2[31:0]

其中rs2是源寄存器,rs1是基址寄存器,offset是偏移量,M表示存储器。

RISC-V目的数与操作数

如果你了解过RISC-V汇编,你会发现加载指令和存储指令的目的数和操作数似乎是相反的。

加载指令的目的数是寄存器,操作数是存储器。存储指令的目的数是存储器,操作数是寄存器。这是因为RISC-V通常将目的寄存器放在第一个位置,维护代码的一致性。

RISC-V伪指令

li伪指令

li指令是一种加载立即数的伪指令,它可以将一个常数加载到寄存器中。RISC-V中的li指令可能会被扩展为lui或addi或者两者都有,具体取决于立即数的大小和位数。

# 例子1:将立即数0x12345678加载到寄存器a5

li a5, 0x12345678 # 这条伪指令会被扩展为两条实际指令

lui a5, 0x12345   # 将高20位加载到a5

addi a5, a5, 0x678 # 将低12位加到a5

lui指令是加载上位立即数的指令,它可以将一个20位的立即数左移12位后存入目标寄存器,相当于将高20位赋值给寄存器,低12位补零12。addi指令是加上立即数的指令,它可以将一个12位的立即数与源寄存器中的值相加后存入目标寄存器3。如果要加载一个32位的立即数,可能需要用到lui和addi两条指令结合使用,先用lui加载高20位,再用addi加载低12位2。注意,在使用addi时,需要考虑立即数的符号和补码问题。

j指令是无条件跳转指令,它可以将一个26位的立即数左移2位后与当前程序计数器(PC)的高4位拼接,形成一个32位的地址,然后将该地址赋值给PC。jr指令是跳转寄存器指令,它可以将一个源寄存器中的值直接赋值给PC。j和jr都是用来改变程序执行流程的指令,但j需要提供一个立即数作为目标地址,而jr需要提供一个寄存器作为目标地址。

Call伪指令

RISC-V call伪指令是一种用于调用函数的简化指令,它实际上是两条真实的RISC-V指令的组合:auipc和jalr。

auipc和jalr是两条RISC-V指令,分别用于将程序计数器(PC)的高20位加到一个寄存器上,以及将PC设置为一个寄存器的值加上一个偏移量。它们通常一起使用,以实现跳转到任意地址的功能。例如,auipc t0, %pcrel_hi(label)会将PC的高20位加上label相对于当前指令的高位偏移量,存入t0寄存器;然后jalr t1, t0, %pcrel_lo(label)会将PC设置为t0寄存器的值加上label相对于当前指令的低位偏移量,并将原来的PC+4存入t1寄存器3。这样就完成了对label的跳转,并保存了返回地址。

  auipc ra, getSum[31:12] # 将当前PC与getSum 地址高20位拼接后赋值给ra寄存器

  jalr ra, ra, getSum[11:0] # 将当前PC+4赋值给ra寄存器,并将ra寄存器与getSum地址低12位相加后赋值给PC

C代码如下

int getSum(int num){

       int sum = 0;

    if(num < 0){

           return 0;

    }else{

           while(num > 0){

                 sum += num;

                     num--;

         }

    }

    return sum;

 

}

int main(){

    return getSum(5);

}

 

编译为RISC-V汇编如下

       .file  "code.c"

       .option pic

       .text

       .align      1

       .globl      getSum

       .type       getSum, @function

getSum:

       addi sp,sp,-48            //开辟48byte的栈空间

       sd    s0,40(sp)                     //将s0的值写入栈顶指针sp+40的内存空间

       addi s0,sp,48                       //将栈顶指针sp+48,即栈底的地址赋给s0

       mv  a5,a0                           //将a0的值赋给a5,a0寄存器保存的是传入参数

       sw   a5,-36(s0)                   //将a5 的值(32位)写入s0-36的空间

       sw   zero,-20(s0)                //将0(32位)写入s0-20的地址空间

       lw    a5,-36(s0)                   //将s0 -36地址中的值(32位)赋给a5

       sext.w     a5,a5                    //将a5扩展为64位

       bgez       a5,.L4                   //比较a5与0的大小,若a5 >= 0 ,跳转到L4

       li     a5,0                            //将立即数0加载到a5

       j      .L3                               //无条件跳转到L3

.L5:

       lw    a4,-20(s0)                   //s0-20地址处的值加载到a4

       lw    a5,-36(s0)                   //s0-36地址处的值加载到a5

       addw      a5,a4,a5               //a4 + a5 的值(32)写回a5

       sw   a5,-20(s0)                   //a5值写入s0-20  注意这里保存的是sum值

       lw    a5,-36(s0)                   //s0-36值加载到a5  将num值重新写入a5

       addiw     a5,a5,-1               //a5值自减

       sw   a5,-36(s0)                   //a5的值写入s0-36 ,这里保存num

.L4:                                            //注意这里没有跳转,顺序执行

       lw    a5,-36(s0)                   //s0-36 -> a5

       sext.w     a5,a5                    //a5扩展为64位

       bgtz a5,.L5                          //这里验证while循环条件,a5 > 0 跳转到L5

       lw    a5,-20(s0)                   //s0 – 20 -> a5 , 即将sum值a5

.L3:

       mv  a0,a5                           //a5 -> a0

       ld    s0,40(sp)                     //sp+40 ->s0,还记得吗,这里保存的是mian栈的栈底       addi sp,sp,48                       //sp指向getSum栈底

       jr     ra                                //ra->pc,pc将会执行call的下一条语句

       .size getSum, .-getSum

       .align      1

       .globl      main

       .type       main, @function

main:

       addi sp,sp,-16                     //sp = sp – 16,开辟16byte空间

       sd    ra,8(sp)                        //ra中即返回地址写入sp+8

       sd    s0,0(sp)                       //s0中即上个栈底写入sp

       addi s0,sp,16                       //sp+16的写入s0,即将s0置为当前栈底

       li     a0,5                            //将立即数5加载到a0,准备参数传递

       call  getSum                       //伪指令,保存下条指令到ra,并且pc转到getSum

       mv  a5,a0                           //a0->a5

       mv  a0,a5                           //a5->a0

       ld    ra,8(sp)                        //sp+8 -> ra ,返回地址值

       ld    s0,0(sp)                       //sp->s0,上个栈底

       addi sp,sp,16                       //sp + 16->sp, 栈顶指针指向栈底

       jr     ra                                //ra->pc

       .size main, .-main

       .ident     "GCC: (Ubuntu 9.4.0-1ubuntu1~20.04) 9.4.0"

       .section  .note.GNU-stack,"",@progbits

...全文
1453 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

571

社区成员

发帖
与我相关
我的任务
社区描述
软件工程教学新范式,强化专项技能训练+基于项目的学习PBL。Git仓库:https://gitee.com/mengning997/se
软件工程 高校
社区管理员
  • 码农孟宁
加入社区
  • 近7日
  • 近30日
  • 至今

试试用AI创作助手写篇文章吧