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