ret
pseudolyszt.net covers ret and other RISC-V instructions in more detail.
Description
ret returns from a function and hands control back to the caller. A call expects one. When a function is called, the caller puts a return address in the ra register (see call and jal), and ret jumps to that address, which continues right after the original call. It is a pseudo-instruction that jumps to ra.
ret only works when ra still holds the right address. In a function that calls nothing else, nothing changes ra between entry and exit. A call inside the function overwrites ra with a new return address and erases the one the function needs. A function that calls others saves its ra on entry and restores it before ret. Without the restore, the function returns to the wrong place, and the program jumps into unrelated code or loops forever.
By convention a function puts its result in the a0 register before returning, and the caller reads it there.
The RISC-V manual says a procedure return in the standard calling convention is encoded as a jalr with rd=x0, rs1=x1 and an immediate of 0. It jumps to the address in x1 and writes no link address, since x0 discards it. The manual adds that processors use the register numbers in jal and jalr as hints to predict function returns, which is one reason the standard convention keeps the return address in x1.
The example is the simplest complete function. square multiplies its argument by itself and ret returns the result. It calls nothing else, so it saves and restores nothing. Running it in the simulator shows control jumping back to the caller.
Expands to
jalr zero, ra, 0
The assembler replaces this pseudo-instruction with the real instruction or instructions listed above.
Example
square: mul a0, a0, a0 ret # return to the caller
