所以,我已经试了两天写一个程序在mips大会行使我的考试在几天内,但是的,我的大脑一直滞后,我不明白它应该如何工作,我应该做什么,非常混乱。不幸的是,我写的所有版本的程序都不能计算斐波那契数列。
So this is my code as of now
.data
prompt: .asciiz "Give me a number to find its fibonacci: "
result: .asciiz "The fibonacci is: "
number: .word 0
answer: .word 0
.text
.globl main
.globl fib
main: li $v0, 4
la $a0, prompt
syscall
li $v0, 5
syscall
sw $v0, number
lw $a0, number
li $v0, 0
jal fib
sw $v0, answer
li $v0, 4
la $a0, result
syscall
li $v0, 1
lw $a0, answer
syscall
li $v0, 10
syscall
fib: addi $sp, $sp, -8
sw $ra, 4($sp)
sw $a0, 0($sp)
slti $t0, $a0, 2 # if (t0=(a0 < 2))
beqz $t0, fibB # if (t0 == 0) / a0 < 2 false goto fibB
# if (t0 == 1)
add $v0, $a0, $v0
addi $sp, $sp, 8
jr $ra
fibB: addi $a0, $a0, -1
jal fib
addi $a0, $a0, -2
jal fib
lw $a0, 0($sp)
lw $ra, 4($sp)
addi $sp, $sp, 8
jr $ra
1条答案
按热度按时间plicqrtu1#
如果您选择使用自定义/非标准调用约定,既保存和恢复
$a0
,又将$v0
作为累加器参数传递,那么您就远离了主流轨道。有可能你的导师告诉你这样做,但你需要去他们那里寻求帮助,或者至少获得这种改变的知识。恕我直言,这种方法充其量可以被认为是一种非常先进的优化技术(尽管有缺陷或不完整),所以在你知道正常调用应该如何工作之前,学习这种方法几乎没有什么好处。
我还看到你试图将表达式
fib(...) + fib(...)
的加法部分移到基本情况中--这是愚蠢的,因为它根本不属于那里,然后从所有非基本情况中消失,结果函数根本不是正常的递归fib()
。这段代码正在做类似这样的事情:
所以,这只会在基本情况下做加法,而不是在每个级别上。
另外,需要明确的是,在互联网上可以找到一些不好的例子,
fib()
使用了许多教育工作者认为有问题的非标准调用约定,但其他人甚至不理解这些问题。这篇文章的其余部分将处理标准调用约定和寄存器用法。
首先,你的基本案例有一个错别字(或设计缺陷):
add $v0, $a0, $v0
。在基本情况下,您希望简单地将$a0
复制到$v0
中,而不是将$v0
中的任何内容与$a0
相加,即return n;
。总是先用最小的数字测试你的代码,比如
fib(0)
,然后fib)(1)
,然后fib(2)
。但是,在调用函数之前,您已经巧妙地将$v0
清除为0,因为非标准的创可贴-$v0
不是函数的参数,而且您的创可贴没有应用于fib
中的内部调用站点。我们不应该依赖于$v0
在函数入口时有一些值:简单地设置它,覆盖任何旧值,以向调用者返回所需的值。第二,在
fib
函数中,对于在函数中第二次调用fib
,通常不遵循标准调用约定。$a0
不应该被恢复,并且调用者不应该依赖于被调用者恢复它。$a0
应该为了你自己的函数的利益而保存,但是没有理由为了调用者的利益而恢复它。fib
的第一次调用的返回值(假设)在$v0
中,但您对fib
进行了第二次(递归)调用,这必然也会返回$v0
中的返回值,因此您不再能够访问第一次调用的返回值;它丢失了!您可以在调试中观察到这一点。(非标准调用约定将恢复调用获得的$a0
,而不是您想要的原始参数值,这意味着它将在$a0
中返回-1值,但正确的解决方案是使用标准调用约定。$a0
在第一次调用fib
时被清除,您自己的代码将其减去1,以使该调用=。您可以在调试中观察到这一点,只需使用fib(2)
。fib(..) + fib(..)
中的加法。您很可能错过了这一点,因为您不知道要添加什么(或者可能是由于替代设计),这是由于没有遵循调用约定的要求,以及第一次调用fib
时第一个$v0
返回值的相关丢失。为了解决这个问题并遵循标准调用约定:
1.你已经有了序幕和尾声,这是节省
$a0
,这是好的。你应该简单地删除在尾声的$a0
的恢复。在两次调用fib之间:
1.您需要恢复原始的
$a0
,以便从中减去2,以便将参数传递给fib
的第二次调用。这需要一条lw
指令从堆栈存储器位置取入$a0
,该指令保存在序言中(减法之前)。1.您需要保存
$v0
(在对fib
的两次调用之间),因为它保存了第一次调用的返回值,这将是执行正确加法所必需的。您可以将它保存到您刚刚加载$a0
的同一个堆栈位置,因为通过分析函数的其余部分,您将不再需要原始参数值。(You可以以相反的顺序执行项(2.)和(3.),但是这将需要额外的堆栈槽,因为在该顺序中,项(3.)不能保存到相同的堆栈槽,因为那里的值尚未为项(2.)恢复。
在第二次调用
fib
之后,需要将两个返回值相加:其中一个在$v0
中,另一个已按照项目(3.)保存。因此,将(3.)中保存的值重新加载到任何可用的临时寄存器(即而不是$v0,比如$t0
甚至$a0
),并将其值相加为$v0
。