ret
pseudolyszt.net obejmuje ret i inne instrukcje RISC-V w większych szczegółach.
Opis
ret powraca z funkcji i przekazuje sterowanie z powrotem do wywołującego. call oczekuje jednego. Gdy funkcja jest wywoływana, wywołujący umieszcza adres powrotu w rejestrze ra (zobacz call i jal), a ret skacze do tego adresu, który kontynuuje bezpośrednio po pierwotnym wywołaniu. Jest to pseudoinstrukcja, która skacze do ra.
ret działa tylko wtedy, gdy ra wciąż przechowuje właściwy adres. W funkcji, która niczego innego nie wywołuje, nic nie zmienia ra między wejściem a wyjściem. call wewnątrz funkcji nadpisuje ra nowym adresem powrotu i usuwa ten, którego funkcja potrzebuje. Funkcja, która wywołuje inne, zapisuje swoje ra przy wejściu i przywraca je przed ret. Bez przywrócenia funkcja powraca w niewłaściwe miejsce, a program skacze do niepowiązanego kodu lub zapętla się na zawsze.
Zgodnie z konwencją funkcja umieszcza swój wynik w rejestrze a0 przed powrotem, a wywołujący odczytuje go stamtąd.
Podręcznik RISC-V mówi, że powrót z procedury w standardowej konwencji wywołań jest kodowany jako jalr z rd=x0, rs1=x1 i natychmiastową wartością 0. Skacze do adresu w x1 i nie zapisuje adresu powrotu, ponieważ x0 go odrzuca. Podręcznik dodaje, że procesory używają numerów rejestrów w jal i jalr jako wskazówek do przewidywania powrotów z funkcji, co jest jednym z powodów, dla których standardowa konwencja przechowuje adres powrotu w x1.
Przykład to najprostsza kompletna funkcja. square mnoży swój argument przez siebie, a ret zwraca wynik. Nie wywołuje niczego innego, więc niczego nie zapisuje ani nie przywraca. Uruchomienie jej w symulatorze pokazuje, jak sterowanie wraca do wywołującego.
Rozwija się do
jalr zero, ra, 0
Asembler zastępuje tę pseudoinstrukcję rzeczywistą instrukcją lub instrukcjami wymienionymi powyżej.
Przykład
square: mul a0, a0, a0 ret # return to the caller
