MIPS:将 `$ra` 保存到堆栈以用于嵌套函数

MIPS: saving `$ra` to stack for nested functions

我是 MIPS 的新手,正在尝试找出 return 嵌套函数中的值。我试图弄清楚为什么在函数 test 中,当我从堆栈加载 $ra 时,它会在我调用 main 中的 calcs 函数之后将我带到指令而不是calcs 函数中 jal test 之后的指令?是否为每个函数都创建了一个新堆栈?

当我在 test 函数中时,堆栈中应该有 2 个 $ra 值,当我加载 $ra 中的最后一个值时,它应该是 return 我在 calcs 函数中 jal test 之后的指令,但那没有发生,我不明白为什么。

.data
    newline: .asciiz    " XXXX "

.text
main:

    addi $s0, [=10=], 39        # val 1
    addi $s1, [=10=], 2         # val 2
    addi $s2, [=10=], 14        # val 3
    addi $s3, [=10=], 11        # val 4

    add $a0, [=10=], $s0        # copy val 1 to $a0
    add $a1, [=10=], $s1        # copy val 2 to $a1

    jal calcs
    add $s4, [=10=], $v0        # move returned value to $s4


    # Exit program
    li $v0, 10          # system call to exit program
    syscall


calcs:
    addi $sp, $sp, -4       # make space in stack 
    sw $ra, 0($sp)          # add $ra value to stack

    add $t8, $s0, $a0       # save arg $a0 to $t8
    add $t9, $s0, $a1       # save arg $a1 to $t9

    jal test

    add $t0, [=10=], $v0        # move returned value to $t0
    add $a0, [=10=], $v0        # move returned value to $a0

    # print
    li $v0, 1           
    syscall             

    li $v0, 4
    la $a0, newline
    syscall

    # get value from stack
    addi $sp, $sp, 4
    lw $ra, 0($sp)

    add $v0, [=10=], $t0

    jr $ra

test:

    addi $sp, $sp, -4       # make space in stack
    sw $ra, 0($sp)          # push $ra value to stack

    add $t0, [=10=], -989898989
    add $v0, [=10=], $t0

    #### If I keep the two lines below then the $ra value jumps to be right after I call the calcs function in main. But if I remove it then it goes to the value right after I call the test function in calcs
    lw $ra, 0($sp)          # load $ra value from stack
    addi $sp, $sp, 4        # pop value off stack

    # printing
    add $a0, [=10=], $t0        
    li $v0, 1           
    syscall             

    li $v0, 4
    la $a0, newline
    syscall

    add $v0, [=10=], $t0        # copy $t0 value to $v0 again

    jr $ra

calcs 你有:

addi $sp, $sp, 4
lw $ra, 0($sp)

此(不正确的)代码序列将弹出堆栈,然后尝试从空堆栈中获取 $ra 值。当我 运行 这个时,由于空堆栈被零填充,因此序列将 0 加载到 $ra 中,这导致程序在执行 jr $ra 之后很快崩溃$ra.

中为零

弹出的正确代码顺序是先获取,然后释放堆栈 space。

lw $ra, 0($sp)
addiu $sp, $sp, 4

I am trying to figure out why in the function test when I load $ra from the stack it takes me to the instruction after I call calcs function in main instead of to the instruction after jal test in calcs function?

我们在发布的代码中没有看到这种行为,所以它一定发生在您正在试验的其他版本中。

例如,如果您在 test 中也有相同的双指令弹出序列。 代码将 return 直接从 testmain(而不是从 test 正确地 return 到 calcs,然后才通过尝试 return 到崩溃与张贴的一样的空地址)。

Is a new stack created for every function?

不,进程线程中的所有函数通过彼此共享堆栈指针共享同一个堆栈。 $sp 堆栈指针在调用时与函数隐式共享(即作为参数)。

When I am in the test function I should have 2 $ra values in the stack and when I load the last in $ra value it should be the one to return me to the instruction after jal test in calcs function, but that isn't happening and I can't figure out why.

我不确定你说的 last 是什么意思,但是一旦你在所有地方都使用了正确的弹出序列,堆栈上就会有两个 return 地址,一个较旧的 return 来自calcsmain,较新的 return 从 testcalcs。由于(调用)堆栈的性质,首先使用较新的,然后再使用较旧的。


由于指针是无符号的,因此在操作它们时使用 addiu,例如调整 $sp.