使用 RISC-V (RV32I) 编译器无需递归计算第 n 个斐波那契数

Calculate nth Fibonacci number using RISC-V (RV32I) compiler without recursion

我用RISC-V汇编语言写了一个计算第n个斐波那契数的代码。它有两部分 - fib.sruntest.s,它将 n 的值加载到 a0 并调用 fib,计算第 n 个斐波那契数(没有递归), 将结果加载到 a0 本身和 return 中。这是我的代码:

.global fib              # the runtest.s will give an a0 value as input n and call fib 
fib:
    li a1, 0             # This is a
    li a2, 1             # This is b
    li a3, 0             # This is c 
    li a4, 2             # This is i
   
    li a6, 2             # dummy, just to check a0 with
    ble a0, a6, cond1    # check if a0 <= 2
    bgt a0, a6, head     # if a0 > 2, proceed to loop

head:                    # start of loop
    add a3, a1, a2       # Here I'm implementing the space optimized version of fibonacci series without recursion:
    mv a1, a2            # for i in range(2, n+1): c = a + b; a = b; b = c; return b
    mv a2, a3            
    addi a4, a4, 1
    blt a4, a0, head
    bge a4, a0, end      # iterates n-1 times and goes to end

cond1:
    li a0, 1             # if n==1 or n==2 return 1
    li a7, 93
    ecall

end:
    mv a0, a2            # copying the value of a2 (which is b) to a0, since the testbench
    li a7, 93            # runtest.s is setup that way.
    ecall

这是我的测试台 (runtest.s):

.global _start
_start:
    # Load 'n' into a0 and call fib
    # Test 1
    li      a0,1  # Check n'th Fib number
    call    fib
    li      a5,1  # Expected result
    bne     a0,a5,.FINISH
    # Test 2
    li      a0,3  
    call    fib
    li      a5,3  
    bne     a0,a5,.FINISH
    # Test 3
    li      a0,7  
    call    fib
    li      a5,13  
    bne     a0,a5,.FINISH
    # Test 4
    li      a0,9 
    call    fib
    li      a5,34
    bne     a0,a5,.FINISH
    # Test 5
    li      a0,20 
    call    fib
    li      a5,6765 
    bne     a0,a5,.FINISH
    # Test 6
    li      a0,30
    call    fib
    li      a5,832040 
    bne     a0,a5,.FINISH
    # Finished tests
    li      a5,0  # All passed
.FINISH:
    mv      a0, a5
    li      a7, 93
    ecall

每次我 运行 这段代码我只得到一个 return 值 1。有人可以指出其中的错误吗?另外,如果有更好的方法来实现相同的逻辑,也请告诉我。

我能够让您所有的测试台案例通过 3 处更改的 RISC-V 模拟器:

(1) 你的第二个测试用例的预期结果是错误的。我假设您使用的是 0 索引的斐波那契数,因为这是您的大多数测试用例所做的(例如,测试用例 1 说斐波那契数 1 是 1,测试用例 3 说斐波那契数 7 是 13)。在那种情况下,斐波那契数 3 应该是 2,而不是 3。

(2) 为了获得正确的迭代次数,在你的循环中,你的“i”变量/寄存器 a4 应该开始设置为 1,而不是 2。

(3) 您通常不会从 RISC-V 中的函数使用 ECALL 到 return。根据 RISC-V 规范,“ECALL 指令用于向执行环境发出服务请求。”它会导致一个精确的陷阱,并在(例如)实现系统调用时使用。取而代之的是函数调用中的 return 伪指令(类似于 x86 的表示法)。 ret 等同于 jalr x0 0(ra)。 (话虽这么说,也许您已经定义了 ecall 行为,以便当 a7 为 93 时,执行相当于 ret 的操作。)