buddhabrot
buddhabrot

Reputation: 1586

Tail recursion in assembly

I'm trying to learn assembly a bit. I use NASM instead of AT&T syntax.

This is my first program, it compares the two command line arguments and outputs which one is the smallest (favouring the first in case they're equal).

I think I'm doing this wrong. I noticed that the stack grows with each "call" of compare, so I could probably make this better with tail recursion optimization. However, I don't know how to do it correctly. Should I replace all occurrences of 'call' here with something else, like jmp? I read that the manner of jumping also depends on how you link with the linux kernel and/or libc regarding 'function' objects (I do not link with libc and so do not have a main 'function'), which confuses me so I thought I'd come here for simple short advice.

Also, another question I had is how foolish it is to do "jl" immediately followed by "jg", which might cause unwanted behaviour if the "jl" jump actually changed the flag contents so "jg" also jumps. Is there a two-way 'atomic' jump?

section .data
    usage:  db 'Please provide two strings',10
    usageLen:   equ $-usage
    bigger:     db 'It is bigger!',10
    biggerLen: equ $-bigger
    smaller:    db 'It is smaller!',10
    smallerLen: equ $-smaller

section .text
    global _start

_start:
    pop ebx ; argc
    cmp ebx, 3
    jne usage_exit

    pop eax ; argv[0]
    pop eax
    pop ebx
    call compare ;

usage_exit:
    mov eax, 4 ; sys_write
    mov ebx, 1 ; int fd = stdout
    mov ecx, usage 
    mov edx, usageLen 
    int 80h ; call kernel

    mov eax, 1  ; sys_exit
    mov ebx, 0  ; int status = 0
    int 80h ;   call kernel

report:
    mov ecx, eax
    mov edx, ebx 
    mov eax, 4 ; sys_write
    mov ebx, 1 ; int fd = stdout
    int 80h ; call kernel

    mov eax, 1  ; sys_exit
    mov ebx, 0  ; int status = 0
    int 80h ;   call kernel (exit)

compare:
    push eax
    push ebx
    mov eax, [eax]
    mov ebx, [ebx]

    test ax, ax
    jz is_smaller
    test bx, bx
    jz is_bigger

    cmp ax, bx
    jl is_smaller
    jg is_bigger
    ; if the same, try next positions
    pop ebx
    pop eax
    inc eax
    inc ebx
    call compare

is_smaller:
    mov eax, smaller
    mov ebx, smallerLen
    call report

is_bigger:
    mov eax, bigger
    mov ebx, biggerLen
    call report

Upvotes: 6

Views: 2390

Answers (1)

interjay
interjay

Reputation: 110069

Yes, you should replace the calls with jmps. In general, you should use call when you expect execution to resume on the following line when the call returns. In your code, execution never returns since compare is just a loop, so jmp is the correct way to go to the next iteration. The same is true for the two instances of call report you have.

As for your second question, jl doesn't change the flags, so there is no problem calling jg after it.

Upvotes: 7

Related Questions