208 lines
5.0 KiB
NASM
208 lines
5.0 KiB
NASM
global gc_init, gc_alloc, gc_free, gc_collect
|
|
extern itoa
|
|
extern str_len
|
|
|
|
section .bss
|
|
alloc_list: resq 1
|
|
stack_start: resq 1
|
|
|
|
; TMP
|
|
|
|
section .data
|
|
newline: db 10, 0
|
|
start_mark: db "Starting to mark", 0
|
|
marked: db "Marked object", 0
|
|
|
|
; /TMP
|
|
|
|
section .text
|
|
|
|
; TMP
|
|
|
|
print_int:
|
|
push rbp
|
|
mov rbp, rsp
|
|
sub rsp, 8
|
|
mov [rbp - 8], rdi
|
|
mov rax, [rbp - 8]
|
|
push rax
|
|
pop rdi
|
|
call itoa
|
|
push rax
|
|
pop rdi
|
|
call print
|
|
mov rdi, newline
|
|
call print
|
|
mov rsp, rbp
|
|
pop rbp
|
|
ret
|
|
|
|
print:
|
|
push rbp
|
|
mov rbp, rsp
|
|
sub rsp, 8
|
|
mov [rbp - 8], rdi
|
|
mov rax, 1
|
|
push rax
|
|
mov rax, 1
|
|
push rax
|
|
mov rax, [rbp - 8]
|
|
push rax
|
|
mov rax, [rbp - 8]
|
|
push rax
|
|
pop rdi
|
|
call str_len
|
|
push rax
|
|
pop rdx
|
|
pop rsi
|
|
pop rdi
|
|
pop rax
|
|
syscall
|
|
mov rsp, rbp
|
|
pop rbp
|
|
ret
|
|
|
|
; /TMP
|
|
|
|
gc_init:
|
|
mov [stack_start], rsp
|
|
ret
|
|
|
|
gc_alloc:
|
|
add rdi, 17 ; add space for metadata
|
|
push rdi
|
|
call sys_mmap ; allocate size + metadata
|
|
pop rdi
|
|
mov byte [rax], 0 ; set mark to 0
|
|
mov qword [rax + 1], rdi ; set total size of object (including metadata)
|
|
mov rsi, [alloc_list] ; load first item in allocation list
|
|
mov qword [rax + 9], rsi ; make current head of allocation list the next item in this object
|
|
mov [alloc_list], rax ; update head of allocation list so it points to this object
|
|
add rax, 17 ; skip metadata for return value
|
|
ret
|
|
|
|
; Generated by chatgpt. Rewrite this later
|
|
; TODO: refactor to unlink easier
|
|
gc_free:
|
|
mov rsi, [alloc_list] ; Load head of allocation list
|
|
test rsi, rsi ; Check if list is empty
|
|
jz .not_found ; If empty, nothing to free
|
|
cmp rsi, rdi ; Is the first item the one to free?
|
|
je .remove_head ; If so, update head directly
|
|
.loop:
|
|
mov rdx, [rsi + 9] ; Load next item in list
|
|
test rdx, rdx ; Check if end of list
|
|
jz .not_found ; If not found, return
|
|
cmp rdx, rdi ; Is this the item to remove?
|
|
je .remove_item ; If so, unlink it
|
|
mov rsi, rdx ; Move to next item
|
|
jmp .loop ; Repeat
|
|
.remove_head:
|
|
mov rdx, [rdi + 9] ; Get next item
|
|
mov [alloc_list], rdx ; Update head of list
|
|
jmp .free_memory ; Free the object
|
|
.remove_item:
|
|
mov rdx, [rdi + 9] ; Get next item
|
|
mov [rsi + 9], rdx ; Bypass rdi in the list
|
|
.free_memory:
|
|
mov rsi, [rdi + 1] ; Get object size
|
|
call sys_munmap ; Free memory
|
|
ret
|
|
.not_found:
|
|
ret ; Item not found, do nothing
|
|
|
|
gc_collect:
|
|
call gc_mark_stack
|
|
call gc_sweep
|
|
ret
|
|
|
|
gc_mark_stack:
|
|
mov r8, rsp ; load current stack pointer
|
|
mov r9, [stack_start] ; load start of stack
|
|
.loop:
|
|
cmp r8, r9 ; have we reached end of stack?
|
|
ja .done ; yes? return
|
|
mov rdi, [r8] ; no? load the value
|
|
call gc_mark ; this might be an allocation, check
|
|
add r8, 8 ; next item in stack
|
|
jmp .loop
|
|
.done:
|
|
ret
|
|
|
|
gc_mark:
|
|
test rdi, rdi ; is input null?
|
|
jz .done ; yes? return
|
|
mov rsi, [alloc_list] ; load start of allocation list
|
|
.loop:
|
|
test rsi, rsi ; reached end of list?
|
|
jz .done ; yes? return
|
|
lea rdx, [rsi + 17]
|
|
cmp rdx, rdi ; no? is this the input object?
|
|
je .mark_object ; yes? mark it
|
|
mov rsi, [rsi + 9] ; no? next item
|
|
jmp .loop
|
|
.mark_object:
|
|
mov al, [rdi] ; load mark
|
|
test al, al ; already marked?
|
|
jnz .done ; yes? return
|
|
mov byte [rdi - 17], 1 ; mark object
|
|
mov rcx, [rdi + 1] ; load object size
|
|
lea rdx, [rdi + 17] ; start of data
|
|
add rcx, rdx ; end of data
|
|
.scan_object:
|
|
cmp rdx, rcx ; done scanning?
|
|
jae .done ; yes? return
|
|
mov rdi, [rbx] ; load value
|
|
call gc_mark
|
|
add rdx, 8 ; next object
|
|
jmp .scan_object
|
|
.done:
|
|
ret
|
|
|
|
gc_sweep:
|
|
mov rdi, [alloc_list]
|
|
.loop:
|
|
test rdi, rdi ; reached end of list?
|
|
jz .done ; yes? return
|
|
mov al, [rdi]
|
|
test al, al ; is object marked?
|
|
jz .free ; no? free it
|
|
mov byte [rdi], 0 ; yes? clear mark for next scan
|
|
mov rdi, [rdi + 9]
|
|
jmp .loop
|
|
.free:
|
|
mov rcx, [rdi + 9]
|
|
push rcx
|
|
call gc_free
|
|
pop rdi
|
|
jmp .loop
|
|
.done:
|
|
ret
|
|
|
|
sys_mmap:
|
|
mov rax, 9
|
|
mov rsi, rdi
|
|
mov rdi, 0
|
|
mov rdx, 3
|
|
mov r10, 34
|
|
mov r8, -1
|
|
mov r9, 0
|
|
syscall
|
|
cmp rax, -1
|
|
je .error
|
|
ret
|
|
.error:
|
|
mov rax, 60
|
|
mov rdi, 1
|
|
syscall
|
|
|
|
sys_munmap:
|
|
mov rax, 11
|
|
syscall
|
|
cmp rax, -1
|
|
je .error
|
|
ret
|
|
.error:
|
|
mov rax, 60
|
|
mov rdi, 1
|
|
syscall |