RISC-V 递归阶乘函数调试

问题描述

我正在尝试在 RISCV 中创建递归阶乘函数,但遇到了一些问题。

这是我们目前所拥有的:

.globl factorial

.data
n: .word 8

.text
main:
    la t0,n
    lw a0,0(t0)
    jal ra,factorial

    addi a1,a0,0
    addi a0,x0,1
    ecall # Print Result

    addi a1,'\n'
    addi a0,11
    ecall # Print newline

    addi a0,10
    ecall # Exit
factorial: 
        la t1,n
        beq x0,t1,finish
        addi t0,-1
        mul a0,t0,a0
        j factorial
finish:
        ret
        ecall 

我们尝试添加和更改要使用的寄存器,但仍然没有将正确的值加载到正确的寄存器中。我们也有点坚持如何递归地做到这一点。希望得到一些帮助!

解决方法

您的 main 代码看起来不错。我看到的所有问题都在阶乘函数中。首先,您的阶乘函数有四个明显的问题:

factorial: 
        # This loads the address of n not the value at label n
        # You need to additionally lw t1,0(t1) to get the value
        la t1,n 
        # t1 is never getting modified so why would this loop ever terminate?
        beq x0,t1,finish
        # You should do these two operations in the opposite order
        # if t1 = 1,a0 would become 0
        addi t0,-1
        mul a0,t0,a0
        j factorial
    finish:
        ret
        # Why ecall here? You have already returned. This is unreachable.
        ecall 

但是,您不能仅仅修复这些并期望它起作用。您当前的实现缺乏如何实际计算阶乘的计划。我假设您正在尝试进行如下实现:

int factorial_recursive(int n) {
    if (n == 0) {
        return 1;
    }
    int recursive = factorial_recursive(n-1);
    return n * recursive;
}

该 C 代码的直接翻译需要使用堆栈来保存 n 和返回地址,并正确遵循调用约定。不过,我不准备写出完整的解释,所以我将解释如何转换 factorial 的循环版本以帮助您朝着正确的方向开始。

我将实现的 C 代码是 RISC-V 汇编:

int factorial_loop(int n) {
    int out = 1;
    while (n > 0) {
        out *= n;
        n -= 1;
    }
    return out;
}

对于此代码,n 将从 a0 开始,但最终需要将其移出,以便我们可以返回 out,以便我们分配寄存器,以便函数看起来像:

int factorial_loop(int a0) {
    int a1 = 1;
    while (a0 > 0) {
        a1 *= a0;
        a0 -= 1;
    }
    a0 = a1;
    return a0;
}

从这里可以很容易地进行直接转换。

factorial_loop:
    li a1,1          # int a1 = 1;
loop:
    beq a0,x0,finish # while (a0 > 0) {
    mul a1,a1,a0    # a1 *= a0;
    addi a0,a0,-1   # a0 -= 1;
    j loop            # }
finish:
    mv a0,a1         # a0 = a1;
    ret               # return a0;