ret
pseudolyszt.net cubre ret y otras instrucciones RISC-V con más detalle.
Descripción
ret regresa de una función y devuelve el control al llamador. Un call espera uno. Cuando se llama a una función, el llamador coloca una dirección de retorno en el registro ra (véanse call y jal), y ret salta a esa dirección, que continúa justo después de la llamada original. Es una pseudoinstrucción que salta a ra.
ret solo funciona cuando ra todavía contiene la dirección correcta. En una función que no llama a nada más, nada modifica ra entre la entrada y la salida. Un call dentro de la función sobrescribe ra con una nueva dirección de retorno y borra la que la función necesita. Una función que llama a otras guarda su ra al entrar y lo restaura antes de ret. Sin la restauración, la función regresa al lugar equivocado, y el programa salta a código no relacionado o entra en un bucle infinito.
Por convención, una función coloca su resultado en el registro a0 antes de regresar, y el llamador lo lee allí.
El manual de RISC-V dice que un retorno de procedimiento en la convención de llamada estándar se codifica como un jalr con rd=x0, rs1=x1 y un inmediato de 0. Salta a la dirección en x1 y no escribe ninguna dirección de enlace, ya que x0 la descarta. El manual añade que los procesadores usan los números de registro en jal y jalr como pistas para predecir retornos de función, lo cual es una razón por la que la convención estándar mantiene la dirección de retorno en x1.
El ejemplo es la función completa más simple. square multiplica su argumento por sí mismo y ret devuelve el resultado. No llama a nada más, así que no guarda ni restaura nada. Ejecutarlo en el simulador muestra el control saltando de vuelta al llamador.
Se expande a
jalr zero, ra, 0
El ensamblador reemplaza esta pseudoinstrucción con la instrucción o instrucciones reales enumeradas arriba.
Ejemplo
square: mul a0, a0, a0 ret # return to the caller
