retpseudo

ret

pseudo
Asembler RISC-V Referencja

lyszt.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