mips递归如何正确存储函数的返回地址 [英] mips recursion how to correctly store return address for a function
问题描述
我在通过mips的递归函数中传递正确的地址时遇到问题
我有一个无法更改的主要功能,它实际上是在设置值并按如下方式调用我的功能:
jal findX
我的函数对浮点数进行计算并执行以下操作:
findX:
addi $sp, $sp, -4 #make room on stack
sw $ra, 0($sp) #save ra
#a series of calculations that are not part of the problem
jal evaluate #gives an output based on the calculations before that triggers various branches
bc1t x_return
jal findX
x_return
mov.s $f0,$f17 #holds the value to return
lw $ra, 0($sp) #restore $ra
addi $sp, $sp, 4 #restore stack pointer
jr $ra
问题是,一旦找到正确的值,我的寄信人地址就会被弄乱,"jr $ ra"会再次调用该函数而不是返回.我该如何解决这个问题,我认为我正确地遵循了mips递归约定.
您在findX
中的推送/弹出代码很好,并且符合ABI.而且,$sp
始终的预减法始终有效,而与ABI无关.
您没有显示evaluate
,但这很可能是可疑的.如果有的话,检查代码并诊断问题将非常容易.
在 true 返回上,evaluate
更改了$sp
或覆盖了findX
堆栈框架的某些部分.
您可以在evaluate
中为实际情况添加断点.大多数模拟器都保留最后N条指令的历史记录,因此您可以查看对sp
的更改或相对于sp
的存储.那可能就足够了.
但是,另一种方法是将一些调试代码添加到与调试器一起工作的findX
中.也就是说,失败条件可能太复杂了,调试器无法停止(即它没有像gdb
这样的复杂的 watch 条件),因此我们可以添加一些帮助程序"代码. /p>
我在findX
堆栈框架中添加了一些额外的值,这些值允许一些一致性和交叉检查以及额外的代码来检查这些值
下面是修改后的代码以供调试.
使用调试器,将断点放在nop
指令的 all 上.当您遇到问题时,请检查历史记录,值等.这应该有助于找出问题所在.
# recursive debug
findX:
addi $sp,$sp,-12 # make room on stack
sw $ra,8($sp) # save ra
sw $sp,4($sp) # save sp
li $t7,0xdeadbeef # save a "magic" number
sw $t7,0($sp) # we want this at the old offset for sp
# a series of calculations that are not part of the problem
# gives an output based on the calculations before that triggers various
# branches
jal evaluate
# NOTE/BUG: if this returns "true", then one of two things happened:
# (1) evaluate changed the sp register contents
# (2) evaluate overwrote the the return value we stored above
bc1t matchfound
jal findX
x_return:
li $t7,0xdeadbeef # get the expected magic number
lw $t6,0($sp) # get the stored value
bne $t6,$t7,badnews1 # did it get trashed? if yes, fly
ignore1:
# the sp value must be the same as when we called the function
lw $t6,4($sp) # get the stored value
bne $t6,$sp,badnews2 # did it get trashed? if yes, fly
ignore2:
# NOTE: we take advantage of the fact that evaluate is called from only
# _one_ place within findX, so we _know_ the ra value
la $t7,x_return # get the expected return value
bne $ra,$t7,badnews3 # did it get trashed? if yes, fly
ignore3:
# NOTE: we take advantage of the fact that evaluate is called from only
# _one_ place within findX, so we _know_ the ra value
la $t7,x_return # get the expected return value
lw $t6,8($sp) # get the saved return value
bne $t6,$t7,badnews4 # did it get trashed? if yes, fly
ignore4:
mov.s $f0,$f17 # holds the value to return
lw $ra,8($sp) # restore $ra
addi $sp,$sp,12 # restore stack pointer
jr $ra
# trap for success
matchfound:
nop # put a breakpoint here
j x_return
# trap for error
badnews1:
nop # put a breakpoint here
j ignore1
# trap for error
badnews2:
nop # put a breakpoint here
j ignore2
# trap for error
badnews3:
nop # put a breakpoint here
j ignore3
# trap for error
# NOTE: this one is special
# we may get a false positive on the first call to findX
# that is, (e.g.) main does "jal findX" and so we'd fail here
# for that case, just continue
badnews4:
nop # put a breakpoint here
j ignore4
I have a problem passing the correct address in a recurssive function in mips
I have a main function I can't alter that goes essentially sets up values and calls my function like this:
jal findX
my function performs calculations on floats and does the following:
findX:
addi $sp, $sp, -4 #make room on stack
sw $ra, 0($sp) #save ra
#a series of calculations that are not part of the problem
jal evaluate #gives an output based on the calculations before that triggers various branches
bc1t x_return
jal findX
x_return
mov.s $f0,$f17 #holds the value to return
lw $ra, 0($sp) #restore $ra
addi $sp, $sp, 4 #restore stack pointer
jr $ra
The problem is that once the correct values are found it appears that my return address is messed up and "jr $ra" calls the function again instead of returning. How do I fix this I think I am following mips recursive convention correctly.
Your push/pop code in findX
is fine and is ABI conforming. And, the pre-subtract of $sp
always works regardless of ABI.
You're not showing evaluate
, but it's the likely suspect. If you had, it would be fairly easy to examine the code and diagnose the problem.
Upon a true return, evaluate
either has changed $sp
or has overwritten parts of the findX
stack frame.
You can add a breakpoint in evaluate
for the true case. Most simulators keep a history of the last N instructions, so you can look at changes to sp
or stores relative to it. That may be enough.
But, another way, is to add some debug code to findX
that works in conjunction with the debugger. That is, the failure condition may be too complex for the debugger to stop on (i.e. it doesn't have sophisticated watch conditions like gdb
), so we can add some "helper" code.
I've added some extra values to the findX
stack frame that allow for some consistency and cross-checking and extra code to check these values
Below is your code modified for debug.
Using the debugger, put breakpoints on all the nop
instructions. When you hit one, examine the history, values, etc. This should help isolate your problem.
# recursive debug
findX:
addi $sp,$sp,-12 # make room on stack
sw $ra,8($sp) # save ra
sw $sp,4($sp) # save sp
li $t7,0xdeadbeef # save a "magic" number
sw $t7,0($sp) # we want this at the old offset for sp
# a series of calculations that are not part of the problem
# gives an output based on the calculations before that triggers various
# branches
jal evaluate
# NOTE/BUG: if this returns "true", then one of two things happened:
# (1) evaluate changed the sp register contents
# (2) evaluate overwrote the the return value we stored above
bc1t matchfound
jal findX
x_return:
li $t7,0xdeadbeef # get the expected magic number
lw $t6,0($sp) # get the stored value
bne $t6,$t7,badnews1 # did it get trashed? if yes, fly
ignore1:
# the sp value must be the same as when we called the function
lw $t6,4($sp) # get the stored value
bne $t6,$sp,badnews2 # did it get trashed? if yes, fly
ignore2:
# NOTE: we take advantage of the fact that evaluate is called from only
# _one_ place within findX, so we _know_ the ra value
la $t7,x_return # get the expected return value
bne $ra,$t7,badnews3 # did it get trashed? if yes, fly
ignore3:
# NOTE: we take advantage of the fact that evaluate is called from only
# _one_ place within findX, so we _know_ the ra value
la $t7,x_return # get the expected return value
lw $t6,8($sp) # get the saved return value
bne $t6,$t7,badnews4 # did it get trashed? if yes, fly
ignore4:
mov.s $f0,$f17 # holds the value to return
lw $ra,8($sp) # restore $ra
addi $sp,$sp,12 # restore stack pointer
jr $ra
# trap for success
matchfound:
nop # put a breakpoint here
j x_return
# trap for error
badnews1:
nop # put a breakpoint here
j ignore1
# trap for error
badnews2:
nop # put a breakpoint here
j ignore2
# trap for error
badnews3:
nop # put a breakpoint here
j ignore3
# trap for error
# NOTE: this one is special
# we may get a false positive on the first call to findX
# that is, (e.g.) main does "jal findX" and so we'd fail here
# for that case, just continue
badnews4:
nop # put a breakpoint here
j ignore4
这篇关于mips递归如何正确存储函数的返回地址的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!