ret
pseudolyszt.net aborda ret e outras instruções RISC-V com mais detalhes.
Descrição
ret retorna de uma função e devolve o controle ao chamador. Um call espera um. Quando uma função é chamada, o chamador coloca um endereço de retorno no registrador ra (veja call e jal), e ret salta para esse endereço, que continua logo após a chamada original. É uma pseudoinstrução que salta para ra.
ret só funciona quando ra ainda contém o endereço correto. Em uma função que não chama nada mais, nada altera ra entre a entrada e a saída. Um call dentro da função sobrescreve ra com um novo endereço de retorno e apaga aquele de que a função precisa. Uma função que chama outras salva seu ra na entrada e o restaura antes do ret. Sem a restauração, a função retorna ao lugar errado, e o programa salta para código não relacionado ou entra em loop infinito.
Por convenção, uma função coloca seu resultado no registrador a0 antes de retornar, e o chamador o lê ali.
O manual do RISC-V diz que um retorno de procedimento na convenção de chamada padrão é codificado como um jalr com rd=x0, rs1=x1 e um imediato de 0. Ele salta para o endereço em x1 e não escreve nenhum endereço de link, já que x0 o descarta. O manual acrescenta que os processadores usam os números de registrador em jal e jalr como dicas para prever retornos de função, o que é uma das razões pelas quais a convenção padrão mantém o endereço de retorno em x1.
O exemplo é a função completa mais simples. square multiplica seu argumento por ele mesmo e ret retorna o resultado. Ela não chama nada mais, então não salva nem restaura nada. Executá-la no simulador mostra o controle saltando de volta para o chamador.
Expande para
jalr zero, ra, 0
O montador substitui esta pseudoinstrução pela instrução ou instruções reais listadas acima.
Exemplo
square: mul a0, a0, a0 ret # return to the caller
