1

我正在尝试学习 RISC-V 并编写了一个阶乘函数,但它遇到了模拟器错误,暗示可能存在无限循环。我现在不确定如何调试我的代码,并且想知道人们是否可以提示我可能做错了什么。

谢谢!

.globl factorial

.data
n: .word 8

.text
main:
    la t0, n  #t0 corresponds to 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:
    addi sp sp -16
    sw s0 0(sp)  #s0 corresponds to i, initialised to n
    sw s1 4(sp)  #s1 corresponds to factorial that will be constantly updated; also initialised to 1
    sw s2 8(sp)  #s2 corresponds to n, or t0
    sw s3 12(sp)
    add s2 x0 t0
    addi s1 x0 1
    add s0 x0 t0
    addi s3 x0 4  #this is what we use to decrement s0 (i) by 1 each time
    loop: 
    beq s0 x0 exit
    mul s1 s1 s0
    sub s0 s0 s3
    j loop
    exit:
    lw s0 0(sp)
    lw s1 4(sp)
    lw s2 8(sp)
    lw s3 12(sp)
    addi sp sp 16
    ret
4

1 回答 1

2

如何调试我在 RISC-V 程序集中编写的阶乘函数?

我现在不确定如何调试我的代码,

所以,你想学习调试。是的,这是任何编程的必备技能,尤其是汇编语言。调试是一个交互式过程,不适合问答形式。

通常的方法是运行每一行代码并验证它是否按照您的想法执行。如果任何代码行没有达到您的预期,那么这就是要处理的。每一行都必须正常工作,否则程序将无法正常运行。

在装配中,我们称之为单步。一条指令的行为既包括它对寄存器的影响,也包括对内存的影响——如果你愿意的话,还包括程序的状态。我们验证寄存器和内存都按预期更新,并且它继续到正确的下一条指令——控制流同样重要,也可以满足或不匹配预期。

我们应该编写少量代码并运行它们以验证它们是否正常工作,而不是编写一个完整的程序然后查看它是否编译/汇编和运行。以增量方式构建工作代码要好得多,因为通常调试一小段新代码会改变您的理解(例如对机器或您要解决的问题的理解),从而使编写其余部分更容易。

在测试某些代码时,首先使用尽可能小的输入进行调试验证(单步):例如,对于阶乘,首先使用 f(1) 运行它,然后再使用 f(2)。

在进行函数调用时,您需要切换角色,首先考虑调用者,然后是被调用者,然后再考虑调用者。在调用时,验证参数是否在正确的寄存器和堆栈中(如果适用)。在被调用函数的第一条指令处,验证是否相同,并在单步执行函数之前记下返回地址值(在 ra 寄存器中)和堆栈指针值 (sp)。当您将值存储到内存中时,请验证这些值以及它们的去向,这样当您以后使用内存时,您就会得到预期的结果。

于 2021-09-24T15:44:23.877 回答