mips递归如何正确存储函数的返回地址

mips递归如何正确存储函数的返回地址,mips,Mips,我在mips中的递归函数中传递正确的地址时遇到问题 我有一个无法更改的主函数,它基本上设置值并调用我的函数,如下所示: jal findX my函数对浮点数执行计算,并执行以下操作: 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 a

我在mips中的递归函数中传递正确的地址时遇到问题

我有一个无法更改的主函数,它基本上设置值并调用我的函数,如下所示:

jal findX
my函数对浮点数执行计算,并执行以下操作:

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标准。而且,无论ABI如何,
$sp
的预减法始终有效

您没有显示
evaluate
,但它可能是嫌疑犯。如果有,那么检查代码和诊断问题就相当容易了

当返回真值时,
evaluate
要么更改了
$sp
,要么覆盖了
findX
堆栈帧的一部分

对于真实情况,您可以在
evaluate
中添加断点。大多数模拟器都会保存最近N条指令的历史记录,因此您可以查看sp的更改或与之相关的存储。这也许足够了

但是,另一种方法是将一些调试代码添加到
findX
,该代码与调试器一起工作。也就是说,故障条件可能太复杂,调试器无法停止(即,它没有复杂的监视条件,如
gdb
),因此我们可以添加一些“助手”代码

我已经在
findX
堆栈框架中添加了一些额外的值,允许一些一致性和交叉检查,以及额外的代码来检查这些值

下面是为调试而修改的代码

使用调试器,在所有
nop
指令上设置断点。当你点击一个时,检查历史记录、值等。这应该有助于隔离你的问题

# 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

ABI的堆栈指针指向堆栈上的第一个空插槽还是最后使用的插槽?应用程序二进制接口。除此之外,它还指定了函数如何相互调用、要保留哪些寄存器、如何传递对寄存器来说太大的参数。