I am writing a Lisp compiler that targets x86_64. Most types of functions have worked so far, but I have now hit a roadblock. The quintessential Lisp factorial function gives me a segmentation fault. Looking at the code it seems perfectly okay, but it is not! After using GDB to trace my code it seems that the recursive process just keeps going even once rax is 1; I traced it down to -4. Does anyone know why this is happening?
This is how I'm compiling:
clang -O0 -mstackrealign -masm=intel test.asm (This uses GAS on MacOS.)
(define (factorial x)
(if (= x 1) 1
(* x (factorial (- x 1)))))
(define result (factorial 5))
I included definitions for the external functions that I'm calling below.
.global _main
.text
_main:
push 5 # push argument to factorial
call factorial
add rsp, 8 # discard 1 local argument
mov [x + rip], rax
mov rdi, 0
mov rax, 0x2000001
syscall
factorial:
push rbp
mov rbp, rsp
push 1 # push argument to =
push [rbp + 16] # push argument to =
call equal
add rsp, 16 # discard 2 local arguments
cmp rax, 1 # is true?
je true_1 # true branch
jmp false_2 # false branch
true_1:
mov rax, 1
jmp end_3
false_2:
push 1 # push argument to -
push [rbp + 16] # push argument to -
call minus
add rsp, 16 # discard 2 local arguments
push rax # result of -
call factorial
add rsp, 8 # discard 1 local argument
push rax # result of factorial
push [rbp + 16] # push argument to *
call multiply
add rsp, 16 # discard 2 local arguments
jmp end_3
end_3:
mov rsp, rbp
pop rbp
ret
.data
x:
.quad 0
.macro operator mnemonic
push rbp
mov rbp, rsp
mov rax, [rbp + 24]
\mnemonic rax, [rbp + 16]
mov rsp, rbp
pop rbp
ret
.endm
minus: operator sub
multiply: operator imul
equal:
push rbp
mov rbp, rsp
mov rsi, [rbp + 16]
cmp rsi, [rbp + 24]
je equal_true
mov rax, 0
jmp equal_end
equal_true:
mov rax, 1
jmp equal_end
equal_end:
mov rsp, rbp
pop rbp
ret