我是一个初学者在汇编,我试图创建一个斐波那契函数。我试着调试我的代码,并以增量方式编写代码,确保它在我放入的每一行代码之后都能运行。通过这样做,我能够修复编译错误,但我不知道为什么我一直收到错误的输出,应该是13,而不是4(输入是7)。我不知道是什么原因造成的,我应该做些什么来解决它。下面是我在RV64I中编写的代码:
# main function
main:
li x10, 7 # Load immediate value 7 into register x10
jal ra, fib # Jump and link to the fib function
li x17, 10 # Load immediate value 10 into register x17
ecall # System call (end of program)
# fib function
fib:
addi sp, sp, -16 # Allocate 16 bytes on the stack
sd ra, 8(sp) # Save the return address on the stack at offset 8
sd x10, 0(sp) # Save the value of x10 on the stack at offset 0
bnez x10, elif # Branch if x10 is not equal to zero
li x10, 0 # Load immediate value 0 into register x10
addi sp, sp, 16 # Deallocate 16 bytes from the stack
jr ra # Jump to the return address (end of function)
elif:
li x28, 1 # Load immediate value 1 into register x28
bne x10, x28, else # Branch if x10 is not equal to x28
li x10, 1 # Load immediate value 1 into register x10
addi sp, sp, 16 # Deallocate 16 bytes from the stack
jr ra # Jump to the return address (end of function)
else:
addi x10, x10, -1 # Subtract 1 from the value of x10
jal fib # Jump and link to the fib function (commented out)
mv x29, x10 # Move the value of x10 into register x29
ld x10, 0(sp) # Load the saved value of x10 from the stack
addi x10, x10, -2 # Subtract 2 from the value of x10
jal fib # Jump and link to the fib function (commented out)
add x10, x29, x10 # Add the value of x29 and x10, and store the result in x10
ld ra, 8(sp) # Load the saved return address from the stack
addi sp, sp, 16 # Deallocate 16 bytes from the stack
jr ra # Jump to the return address (end of function)
下面是我试图在汇编中创建的fib函数的C++版本:
int fib(int n)
{
if (n==0) return 0;
else if (n == 1) return 1;
else return fib(n−1) + fib(n−2);
}
任何帮助将不胜感激。
1条答案
按热度按时间kcwpcxri1#
对于许多情况,我会说运行调试器,看看出了什么问题。然而,当涉及到函数调用时,在一个函数调用上有很多需要注意的地方,你必须从两个Angular 来看待它--函数调用工作了吗(即函数调用是否有效)?step in并查看它的工作情况并提供正确的返回值),并且,* 函数调用是否正确地为调用者恢复了环境(即,寄存器值,包括堆栈指针)*,这更多的是来自调用者上下文的连续性的“跨越”概念。
要自己调试这种东西,您可能会发现
fib(0)
、fib(1)
、fib(2)
都可以工作(尽管fib(2)
有点意外),而fib(3)
不能,因此这就是调试的重点。这个想法是建立测试用例,从最简单的可能,当这些工作,稍微大一点。函数调用是困难的,因为它的所有移动部件,而递归,尽管没有添加规则或普通函数调用之外的其他开销,但跟踪起来会很混乱,因为它的本质是会涉及多个堆栈帧。
您的寄存器使用没有遵循调用约定-但是,要明确的是,虽然代码应该遵循调用约定,但这不是特定的缺陷-问题是您将多个值放入x29并期望旧值神奇地重新出现,即使它们已经被删除。
寄存器是有效的(快速的)全局变量。因此,如果一个
fib
将x29设置为某个值,那么第二个递归调用也会这样做,第二个递归调用“获胜”,它会消除先前/更大递归调用的x29。x29中的值是最后设置它的人的值,当第一个递归调用被恢复时,这是您的代码所不期望的。顺便说一句,你使用x寄存器编号而不是友好的寄存器名称是不可读的,而且你还混合了友好的名称(例如。
ra
)与x数字。如果你想写出可读的代码,坚持使用友好的寄存器名,避免使用x寄存器号(除了特殊的x 0)。从
x29
中寻找的是调用保留寄存器的概念。x29
数字指的是t4
,按照惯例,它不是一个保留调用的寄存器,您应该使用的是s
寄存器之一,比如s0
。然而,仅仅改变寄存器是不够的,因为使s
寄存器保留调用的魔术是用代码完成的,您还需要添加代码。要使
s0
工作,您需要额外的堆栈空间,并在序言和后记中保存和恢复s0
。调用保留寄存器的想法是,你不知道你在保存和恢复什么,但你知道,如果你这样做,那么你就可以以一种在调用中保留的方式使用寄存器。作为一个完全独立的替代方案,您可以避免
s
寄存器,只需将fib
返回值保存到堆栈中。这也需要额外的堆栈空间字,但这次这个字在序言后记中不是用来保存/恢复的,而是简单地将第一个fib
的值保存到堆栈中,然后在第二次调用fib
后重新加载它。使用这种方法,您可以在内存中保存稍后需要的(返回)值,在函数调用(第二个到
fib
)之后,堆栈内存将保留函数调用。这种使用内存的方法实际上比调用保留寄存器方法更简单,但仅适用于非常简单的函数,如递归fib
。如果函数做了更多的工作(即有一个循环或多个函数调用),那么s
寄存器的方法将成为一个净赢。精明的读者可能会意识到,用于保存传入
a0
值的堆栈空间可以重用于存储第一个fib
的返回值,这意味着该函数只需要两个(64位)字的堆栈空间。为了使其工作,在第一个fib调用之后,内存中的值需要在存储返回值之前重新加载到寄存器中。这意味着在两个递归
fib
调用之间:a0
值重新加载到a1
-a1
中,因为a0
仍然保存稍后需要的返回值a0
(返回)值存储到该内存插槽中,现在不再需要该插槽fib
调用的参数a1-2
的值计算到a0
中。在第二次递归调用之后,重新加载堆栈保存的值以计算最终的返回值。