Recursion RISC-V递归阶乘函数调试

Recursion RISC-V递归阶乘函数调试,recursion,assembly,factorial,riscv,Recursion,Assembly,Factorial,Riscv,我试图在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, x0, '\n' addi a0, x0, 11

我试图在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, x0, '\n'
    addi a0, x0, 11
    ecall # Print newline

    addi a0, x0, 10
    ecall # Exit
factorial: 
        la t1, n
        beq x0, t1, finish
        addi t0, t1, -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, t1, -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;

如果要使用循环,可以通过将条件放在底部,将指令保存在循环体中<代码>bne a0,x0,循环而不是
j循环
。(要得到0!=1而不是0,您需要一个
beq
跳过循环,但是顶部的beq可以在循环之外,而不是运行每个迭代)。这是一个很好的链接,可以解释为什么不像我那样编写循环。我用一个跳转来编写它,使其与提供的代码最为相似(并逐行匹配C)。我应该包括一个关于while-vs-if-do-while循环格式的注释,但是我认为这个注释现在可以达到这个目的。