RET

The ret instruction is used at the end of a procedure to return control back to the calling function. It works by popping the return address, which was saved on the stack by the call instruction, into the instruction pointer rip. This restores execution to the instruction immediately following the original call. Without ret, the program would not know where to continue after finishing a procedure, breaking the structured flow of execution.

section .data
    message db "Fibonacci Sequence:", 0x0a
    msg_len equ $ - message                               ; $ is the current address, so $ - message = length

section .text
  global  _start
  
  _start:
    ;The call instruction pushes the return address—the address of the instruction 
    ;immediately following the call—onto the stack and then jumps directly to the 
    ;target procedure to begin execution. When the procedure completes, the ret 
    ;instruction is executed, which pops the saved return address from the stack into 
    ;rip, restoring program execution to the point immediately after the original call.
    
    call printMessage      ; print intro message
    call initFib           ; set initial Fib values
    call loopFib           ; calculate Fib numbers
    call Exit              ; Exit the program

  printMessage:
    mov rax, 1             ; rax: syscall number 1
    mov rdi, 1             ; rdi: fd 1 for stdout
    mov rsi,message        ; rsi: pointer to message
    mov rdx, msg_len       ; length calculated dynamically with equ
    ;mov rdx, 20           ; rdx: print length of 20 bytes
    syscall                ; call write syscall to the intro message
    ret

  initFib:
    xor rax, rax           ; initialize rax to 0
    xor rbx, rbx           ; initialize rbx to 0
    inc rbx                ; increment rbx to 1
    ret

  loopFib:
    add rax, rbx           ; get the next number
    xchg rax, rbx          ; swap values
    cmp rbx, 10		   ; do rbx - 10
    js loopFib		   ; jump if result is <0
    ret

  ; The ret instruction is not used in the Exit procedure because the goal is to 
  ; terminate the program rather than return to the calling point. Using ret would pop 
  ; a return address from the stack and continue execution, but since the program is 
  ; finished, we instead use the exit syscall (e.g., rax = 60 on Linux x86-64) to end 
  ;the program cleanly and hand control back to the operating system.

  Exit:
    mov rax, 60
    mov rdi, 0
    syscall

* The call instruction saves the address of the next instruction (the current value of
   rip) onto the stack and then jumps to the specified procedure. Once the procedure 
   completes, the ret instruction pops the saved address from the top of the stack back 
   into rip, returning execution to the point immediately following the original call. 
   Together, call and ret provide a structured mechanism for function and procedure 
   calls in assembly, enabling nested calls, recursion, and proper program flow control.

VISUALIZATION

Program start: _start
Stack (top at bottom of diagram):

Empty

_start calls printMessage
Push return address of _start+next_instruction onto stack

+--------------------+ <- rsp
| return to _start   |
+--------------------+

Inside printMessage, syscall executes
ret pops return address from stack

Stack after ret from printMessage
(empty)

_start calls initFib
Push return address of _start+next_instruction onto stack

+--------------------+
| return to _start   |
+--------------------+

Inside initFib
ret pops return address from stack

Stack after ret from initFib
(empty)

_start calls loopFib
Push return address of _start+next_instruction onto stack

+--------------------+
| return to _start   |
+--------------------+

Inside loopFib
(ret inside loop after finishing calculation)
ret pops return address from stack

Stack after ret from loopFib
(empty)

_start calls Exit
Push return address of _start+next_instruction onto stack
Inside Exit, syscall 60 terminates program

Stack is no longer used

Last updated