ret
псевдоlyszt.net охватывает ret и другие инструкции RISC-V более подробно.
Описание
ret выполняет возврат из функции и передаёт управление обратно вызывающему. call ожидает его. Когда функция вызывается, вызывающий помещает адрес возврата в регистр ra (см. call и jal), а ret выполняет переход по этому адресу, который продолжается сразу после исходного вызова. Это псевдоинструкция, выполняющая переход по ra.
ret работает только тогда, когда ra всё ещё содержит правильный адрес. В функции, которая ничего больше не вызывает, ничто не изменяет ra между входом и выходом. call внутри функции перезаписывает ra новым адресом возврата и стирает тот, который нужен функции. Функция, которая вызывает другие, сохраняет свой ra при входе и восстанавливает его перед ret. Без восстановления функция возвращается не туда, и программа переходит в несвязанный код или зацикливается навсегда.
По соглашению функция помещает свой результат в регистр a0 перед возвратом, и вызывающий читает его оттуда.
Руководство RISC-V говорит, что возврат из процедуры в стандартном соглашении о вызовах кодируется как jalr с rd=x0, rs1=x1 и непосредственным значением 0. Он выполняет переход по адресу в x1 и не записывает адрес связи, поскольку x0 его отбрасывает. Руководство добавляет, что процессоры используют номера регистров в jal и jalr как подсказки для предсказания возвратов из функций, что является одной из причин, по которой стандартное соглашение хранит адрес возврата в x1.
Пример — это простейшая полная функция. square умножает свой аргумент на себя, а ret возвращает результат. Она ничего больше не вызывает, поэтому ничего не сохраняет и не восстанавливает. Запуск её в симуляторе показывает, как управление возвращается к вызывающему.
Разворачивается в
jalr zero, ra, 0
Ассемблер заменяет эту псевдоинструкцию реальной инструкцией или инструкциями, перечисленными выше.
Пример
square: mul a0, a0, a0 ret # return to the caller
