mips recursion how to correctly store return addre

2019-08-11 23:51发布

问题:

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.

回答1:

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