请求详细分步解释RARS模拟器中RISC-V阶乘代码的执行周期
RISC-V阶乘代码分步执行详解(基于RARS模拟器)
这段代码通过递归实现5的阶乘计算(5! = 120),以下是每条指令在RARS模拟器中的执行周期具体操作:
一、主程序(main)执行流程
li a0, 5- 执行操作:立即数加载指令,将数值5写入参数寄存器
a0(RISC-V规范中a0用于传递函数参数及返回结果),此时a0 = 5。
- 执行操作:立即数加载指令,将数值5写入参数寄存器
jal fact- 执行操作:跳转并链接指令,先将当前下一条指令(
li a7, 1)的地址写入返回地址寄存器ra,再跳转到fact标签对应的指令地址,启动递归计算。
- 执行操作:跳转并链接指令,先将当前下一条指令(
li a7, 1- 执行操作:立即数加载指令,将数值1写入系统调用寄存器
a7(RARS中a7指定ecall功能,1对应整数输出)。
- 执行操作:立即数加载指令,将数值1写入系统调用寄存器
ecall- 执行操作:触发系统调用,根据
a7=1的功能,将a0中存储的阶乘结果(120)输出到控制台。
- 执行操作:触发系统调用,根据
li a7, 10- 执行操作:立即数加载指令,将数值10写入
a7(10对应程序退出的系统调用功能)。
- 执行操作:立即数加载指令,将数值10写入
ecall- 执行操作:触发系统调用,终止程序运行。
二、递归函数(fact)执行流程
递归分为递推调用和回溯计算两个阶段,以下按调用层级拆解:
递推调用阶段(从5到1)
第1次进入fact(a0=5)
li t0, 2- 执行操作:立即数加载指令,将数值2写入临时寄存器
t0,用于后续条件判断,此时t0=2。
- 执行操作:立即数加载指令,将数值2写入临时寄存器
bge a0, t0, else- 执行操作:分支比较指令,判断
a0(5)是否大于等于t0(2),条件成立,跳转到else标签执行。
- 执行操作:分支比较指令,判断
- 进入
else块:addi sp, sp, -8:栈指针sp减8,在栈上分配8字节空间(RISC-V栈向下生长),用于保存当前a0和ra的值。sw a0, 4(sp):存储字指令,将a0的当前值(5)写入栈的sp+4位置。sw ra, 0(sp):存储字指令,将ra的当前值(主程序li a7,1的地址)写入栈的sp+0位置。addi a0, a0, -1:立即数加法指令,将a0减1,此时a0=4,作为下一次递归的参数。jal fact:跳转并链接指令,将当前下一条指令(lw t0, 4(sp))的地址写入ra,跳转到fact启动第2次递归。
第2次进入fact(a0=4)
重复第1次的逻辑:
li t0,2→t0=2bge a0,t0,else→ 4≥2,跳转到elseaddi sp,sp,-8→ 栈再分配8字节空间sw a0,4(sp)→ 保存a0=4到栈中sp+4sw ra,0(sp)→ 保存当前ra(第1次else中lw t0,4(sp)的地址)到栈中sp+0addi a0,a0,-1→a0=3jal fact→ 启动第3次递归
第3次进入fact(a0=3)
逻辑同上:
li t0,2→t0=2bge a0,t0,else→3≥2,跳转到elseaddi sp,sp,-8→ 分配栈空间sw a0,4(sp)→ 保存a0=3sw ra,0(sp)→ 保存当前ra(第2次else中lw t0,4(sp)的地址)addi a0,a0,-1→a0=2jal fact→ 启动第4次递归
第4次进入fact(a0=2)
逻辑同上:
li t0,2→t0=2bge a0,t0,else→2≥2,跳转到elseaddi sp,sp,-8→ 分配栈空间sw a0,4(sp)→ 保存a0=2sw ra,0(sp)→ 保存当前ra(第3次else中lw t0,4(sp)的地址)addi a0,a0,-1→a0=1jal fact→ 启动第5次递归
第5次进入fact(a0=1,基准情况)
li t0,2→t0=2bge a0,t0,else→1<2,条件不成立,执行下一条指令li a0,1→ 立即数加载指令,将1写入a0(阶乘基准规则:1! =1)jr ra→ 跳转寄存器指令,根据ra中存储的地址(第4次else中lw t0,4(sp)的地址)返回,进入回溯计算阶段。
回溯计算阶段(从1到5的阶乘结果)
回到第4次递归的else块(当前a0=1)
lw t0,4(sp)→ 加载字指令,从栈的sp+4位置取出之前保存的a0=2,写入t0,此时t0=2。mul a0,a0,t0→ 乘法指令,计算1*2=2,结果写入a0(对应2! =2)。lw ra,0(sp)→ 加载字指令,从栈的sp+0位置取出之前保存的ra(第3次else中lw t0,4(sp)的地址),写入ra。addi sp,sp,8→ 栈指针加8,释放当前栈帧的8字节空间,栈回到第3次递归调用前的位置。jr ra→ 返回第3次递归的else块。
回到第3次递归的else块(当前a0=2)
lw t0,4(sp)→ 取出栈中保存的3,写入t0,t0=3。mul a0,a0,t0→ 计算2*3=6,写入a0(对应3! =6)。lw ra,0(sp)→ 取出第2次else中lw t0,4(sp)的地址到ra。addi sp,sp,8→ 释放栈空间,栈回到第2次递归前的位置。jr ra→ 返回第2次递归的else块。
回到第2次递归的else块(当前a0=6)
lw t0,4(sp)→ 取出栈中保存的4,写入t0,t0=4。mul a0,a0,t0→ 计算6*4=24,写入a0(对应4! =24)。lw ra,0(sp)→ 取出第1次else中lw t0,4(sp)的地址到ra。addi sp,sp,8→ 释放栈空间,栈回到第1次递归前的位置。jr ra→ 返回第1次递归的else块。
回到第1次递归的else块(当前a0=24)
lw t0,4(sp)→ 取出栈中保存的5,写入t0,t0=5。mul a0,a0,t0→ 计算24*5=120,写入a0(对应5! =120)。lw ra,0(sp)→ 取出主程序中li a7,1的地址到ra。addi sp,sp,8→ 释放栈空间,栈回到主程序调用fact前的位置。jr ra→ 返回主程序,继续执行后续指令。
内容的提问来源于stack exchange,提问作者Emanuele Iandolo
相关产品推荐
相关产品推荐

