You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

请求详细分步解释RARS模拟器中RISC-V阶乘代码的执行周期

RISC-V阶乘代码分步执行详解(基于RARS模拟器)

这段代码通过递归实现5的阶乘计算(5! = 120),以下是每条指令在RARS模拟器中的执行周期具体操作:


一、主程序(main)执行流程

  1. li a0, 5
    • 执行操作:立即数加载指令,将数值5写入参数寄存器a0(RISC-V规范中a0用于传递函数参数及返回结果),此时a0 = 5。
  2. jal fact
    • 执行操作:跳转并链接指令,先将当前下一条指令(li a7, 1)的地址写入返回地址寄存器ra,再跳转到fact标签对应的指令地址,启动递归计算。
  3. li a7, 1
    • 执行操作:立即数加载指令,将数值1写入系统调用寄存器a7(RARS中a7指定ecall功能,1对应整数输出)。
  4. ecall
    • 执行操作:触发系统调用,根据a7=1的功能,将a0中存储的阶乘结果(120)输出到控制台。
  5. li a7, 10
    • 执行操作:立即数加载指令,将数值10写入a7(10对应程序退出的系统调用功能)。
  6. ecall
    • 执行操作:触发系统调用,终止程序运行。

二、递归函数(fact)执行流程

递归分为递推调用和回溯计算两个阶段,以下按调用层级拆解:

递推调用阶段(从5到1)

第1次进入fact(a0=5)

  1. li t0, 2
    • 执行操作:立即数加载指令,将数值2写入临时寄存器t0,用于后续条件判断,此时t0=2。
  2. bge a0, t0, else
    • 执行操作:分支比较指令,判断a0(5)是否大于等于t0(2),条件成立,跳转到else标签执行。
  3. 进入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次的逻辑:

  1. li t0,2 → t0=2
  2. bge a0,t0,else → 4≥2,跳转到else
  3. addi sp,sp,-8 → 栈再分配8字节空间
  4. sw a0,4(sp) → 保存a0=4到栈中sp+4
  5. sw ra,0(sp) → 保存当前ra(第1次else中lw t0,4(sp)的地址)到栈中sp+0
  6. addi a0,a0,-1 → a0=3
  7. jal fact → 启动第3次递归

第3次进入fact(a0=3)

逻辑同上:

  1. li t0,2 → t0=2
  2. bge a0,t0,else →3≥2,跳转到else
  3. addi sp,sp,-8 → 分配栈空间
  4. sw a0,4(sp) → 保存a0=3
  5. sw ra,0(sp) → 保存当前ra(第2次else中lw t0,4(sp)的地址)
  6. addi a0,a0,-1 → a0=2
  7. jal fact → 启动第4次递归

第4次进入fact(a0=2)

逻辑同上:

  1. li t0,2 → t0=2
  2. bge a0,t0,else →2≥2,跳转到else
  3. addi sp,sp,-8 → 分配栈空间
  4. sw a0,4(sp) → 保存a0=2
  5. sw ra,0(sp) → 保存当前ra(第3次else中lw t0,4(sp)的地址)
  6. addi a0,a0,-1 → a0=1
  7. jal fact → 启动第5次递归

第5次进入fact(a0=1,基准情况)

  1. li t0,2 → t0=2
  2. bge a0,t0,else →1<2,条件不成立,执行下一条指令
  3. li a0,1 → 立即数加载指令,将1写入a0(阶乘基准规则:1! =1)
  4. jr ra → 跳转寄存器指令,根据ra中存储的地址(第4次else中lw t0,4(sp)的地址)返回,进入回溯计算阶段。

回溯计算阶段(从1到5的阶乘结果)

回到第4次递归的else块(当前a0=1)

  1. lw t0,4(sp) → 加载字指令,从栈的sp+4位置取出之前保存的a0=2,写入t0,此时t0=2。
  2. mul a0,a0,t0 → 乘法指令,计算1*2=2,结果写入a0(对应2! =2)。
  3. lw ra,0(sp) → 加载字指令,从栈的sp+0位置取出之前保存的ra(第3次else中lw t0,4(sp)的地址),写入ra。
  4. addi sp,sp,8 → 栈指针加8,释放当前栈帧的8字节空间,栈回到第3次递归调用前的位置。
  5. jr ra → 返回第3次递归的else块。

回到第3次递归的else块(当前a0=2)

  1. lw t0,4(sp) → 取出栈中保存的3,写入t0,t0=3。
  2. mul a0,a0,t0 → 计算2*3=6,写入a0(对应3! =6)。
  3. lw ra,0(sp) → 取出第2次else中lw t0,4(sp)的地址到ra。
  4. addi sp,sp,8 → 释放栈空间,栈回到第2次递归前的位置。
  5. jr ra → 返回第2次递归的else块。

回到第2次递归的else块(当前a0=6)

  1. lw t0,4(sp) → 取出栈中保存的4,写入t0,t0=4。
  2. mul a0,a0,t0 → 计算6*4=24,写入a0(对应4! =24)。
  3. lw ra,0(sp) → 取出第1次else中lw t0,4(sp)的地址到ra。
  4. addi sp,sp,8 → 释放栈空间,栈回到第1次递归前的位置。
  5. jr ra → 返回第1次递归的else块。

回到第1次递归的else块(当前a0=24)

  1. lw t0,4(sp) → 取出栈中保存的5,写入t0,t0=5。
  2. mul a0,a0,t0 → 计算24*5=120,写入a0(对应5! =120)。
  3. lw ra,0(sp) → 取出主程序中li a7,1的地址到ra。
  4. addi sp,sp,8 → 释放栈空间,栈回到主程序调用fact前的位置。
  5. jr ra → 返回主程序,继续执行后续指令。

内容的提问来源于stack exchange,提问作者Emanuele Iandolo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 05:25:53