MIPS: сохранение `$ ra` в стек для вложенных функций - PullRequest
1 голос
/ 04 марта 2020

Я новичок в MIPS и пытаюсь выяснить возвращаемые значения во вложенных функциях. Я пытаюсь выяснить, почему в функции test, когда я загружаю $ra из стека, она переводит меня в инструкцию после вызова функции calcs в main вместо инструкции после jal test в calcs функция? Создан ли новый стек для каждой функции?

Когда я нахожусь в функции test, у меня должно быть 2 $ra значений в стеке, и когда я загружаю последнее значение в $ra, это должно быть один, чтобы вернуть меня к инструкции после jal test в calcs функции, но этого не происходит, и я не могу понять, почему.

.data
    newline: .asciiz    " XXXX "

.text
main:

    addi $s0, $0, 39        # val 1
    addi $s1, $0, 2         # val 2
    addi $s2, $0, 14        # val 3
    addi $s3, $0, 11        # val 4

    add $a0, $0, $s0        # copy val 1 to $a0
    add $a1, $0, $s1        # copy val 2 to $a1

    jal calcs
    add $s4, $0, $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, $0, $v0        # move returned value to $t0
    add $a0, $0, $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, $0, $t0

    jr $ra

test:

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

    add $t0, $0, -989898989
    add $v0, $0, $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, $0, $t0        
    li $v0, 1           
    syscall             

    li $v0, 4
    la $a0, newline
    syscall

    add $v0, $0, $t0        # copy $t0 value to $v0 again

    jr $ra

1 Ответ

1 голос
/ 05 марта 2020

В calcs у вас есть:

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

Эта (неправильная) кодовая последовательность вытолкнет стек, а затем попытается извлечь значение $ ra из пустого стека. Когда я запускаю это, так как пустой стек заполнен нулями, так что последовательность загружает 0 в $ra, что заставляет программу обработать sh вскоре после выполнения jr $ra с нулем в $ra.

Правильная последовательность кода для pop - сначала извлечь, а затем освободить пространство стека.

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

Я пытаюсь выяснить, почему в функциональном тесте при загрузке $ra из стека это приводит меня к инструкции после вызова функции calcs в main вместо инструкции после jal test в calcs функции?

Мы не видим такого поведения в коде как опубликованном, поэтому, должно быть, это произошло в каком-то другом воплощении, с которым вы экспериментировали.

Например, если бы у вас тоже была та же самая всплывающая последовательность из двух команд в test. , код возвращался бы непосредственно с test до main (вместо правильного возврата с test на calcs, и только после этого происходит сбой при попытке вернуться к нулевому адресу, как это сделано в том виде, в котором оно было отправлено).

Создан ли новый стек для каждой функции?

Нет, все функции в потоке процесса совместно используют один и тот же стек, разделяя указатель стека друг с другом. Указатель стека $sp неявно используется совместно (т. Е. Как параметр) с функциями при их вызове.

Когда я нахожусь в тестовой функции, у меня должно быть 2 $ ra значений в стеке, и когда Я загружаю последнее значение в $ ra, оно должно быть тем, которое возвращает меня к инструкции после jal-теста в функции calcs, но этого не происходит, и я не могу понять, почему.

Я не уверен, что вы подразумеваете под последним, но как только вы используете правильную последовательность pop везде, в стеке появятся два адреса возврата: старый для возврата из calcs в main и новый для возврата от test до calcs. Как и характер стека (вызова), сначала используется более новый, а затем более старый.


Поскольку указатели не подписаны, используйте addiu при манипулировании ими, например при настройке $ sp.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...