Как работает регистр обратного адреса в архитектуре процессора, которая не хранит обратный адрес в стеке? - PullRequest
4 голосов
/ 21 апреля 2020

Я пытаюсь выяснить, как будет работать архитектура, в которой хранится адрес возврата вызова в регистре (RR) (в отличие от нажатия и возврата адреса возврата в стеке).

Не будет ли регистр адреса возврата перезаписываться каждый раз при выполнении вложенного вызова (следовательно, возврат после одного возврата невозможен)? Читая мою домашнюю задачу, я должен был изменить программу сборки, чтобы использовать регистр RR, который хранит адрес возврата вызовов вместо того, чтобы помещать его в стек. Я искал, как это будет работать, но либо там ничего нет, информация хорошо спрятана, либо мои навыки поиска в Google не так уж велики.

Я не прошу, чтобы проблема была решена, но я хотел бы знать, как хранение адреса возврата в одном регистре возможно при нескольких вызовах в программе без последующего сохранения значения регистра в стеке ( что бы победить суть упражнения).

Спасибо за любую помощь.

Ответы [ 2 ]

5 голосов
/ 21 апреля 2020

Предполагая, что рекурсия не требуется, вы можете придумать соглашение, согласно которому ссылка (регистр возврата) хранится в другом регистре, в зависимости от уровня вложенности.

Обратите внимание, что мэйнфреймы IBM в режиме classi c нет стека. Вместо этого вызывающая сторона предоставляет область сохранения, на которую указывает R13, затем, когда выполняется вызов, R14 содержит адрес возврата, а R15 является базовым адресом вызываемой функции. Для рекурсии каждый вызывающий абонент выделяет новую область сохранения из кучи перед выполнением вызова. Соглашение заключается в том, чтобы вызываемый пользователь сохранял R13 в своем надлежащем месте в области сохранения, создавая цепочку ссылок областей сохранения, называемую «стеком связей». При возврате вызываемый должен освободить выделенную область сохранения непосредственно перед возвратом.

5 голосов
/ 21 апреля 2020

Да, на ISA, которые используют «регистр связи» для передачи обратного адреса, неконечная функция должна сохранять / восстанавливать свой обратный адрес, очень похоже на то, как они сохраняют сохраняемый вызовом регистр, который они хотели использовать внутри функции. то есть обычно в стеке вызовов.

Многие ISA RIS C не имеют инструкций push / pop, но одну и ту же операцию можно выполнить с несколькими инструкциями. например, вычесть из указателя стека, чтобы освободить место, затем сохранить некоторые регистры, включая LR, при входе в функцию. Затем, перед возвратом, перезагрузите регистры и добавьте в указатель стека, чтобы восстановить значение SP вызывающей стороны и любые другие регистры.

Листовые функции (которые не выполняют никаких вызовов функций) могут просто оставить этот регистр в покое, поэтому адрес возврата все еще там, когда они ret (или как бы ни была вызвана инструкция возврата, например, MIPS jr $ra - переход в регистр адреса возврата).

Посмотрите на вывод компилятора, например:

void external();

void foo(int *p) {
    external();
    *p = 0;    // defeat tail-call optimization
}

скомпилировано для MIPS G CC 5.4 -O2 -fno-delayed-branch в проводнике компилятора Godbolt

foo(int*):
        addiu   $sp,$sp,-32        # reserve 32 bytes of stack space (MIPS calling convention I think guarantees some "shadow space" for callees)
        sw      $31,28($sp)        # $31 is MIPS's $ra return address reg
        sw      $16,24($sp)        # $16 is a call-preserved register
        move    $16,$4             # save p for later use
        jal     external
        nop                 # branch-delay slot

        lw      $31,28($sp)        # reload return address
        sw      $0,0($16)          # *p = 0
        lw      $16,24($sp)        # restore caller's $16
        addiu   $sp,$sp,32         # restore stack
        j       $31                # jump to return address
        nop                 # branch delay slot

Это не , как правило, необходимо для возврата функции с адресом возврата в том же регистре, в котором она была раньше, в зависимости от того, какую инструкцию возврата использует ISA. Это типично и, возможно, помогает прогнозировать переходы на некоторых микроархитектурах.

32-битный ARM забавен и содержит микрокодированные инструкции push / pop, которые переводят битовое поле регистров в pu sh и поп-музыка. Так что обычно push {r4, lr} при вводе функции и pop {r4, pc} как инструкция возврата. (ARM имеет счетчик программ в качестве одного из 16 архитектурных регистров общего назначения. Запись в него - прыжок.) Нажатие r4 вместе с регистром ссылок lr сохраняет стек в выравнивании и обеспечивает сохранение вызова. Зарегистрируйтесь, чтобы играть с.

...