export shared code

This commit is contained in:
janis 2026-07-14 16:36:54 +02:00
parent 4690b3c7ec
commit ca9fd58bed
Signed by: janis
SSH key fingerprint: SHA256:bB1qbbqmDXZNT0KKD5c2Dfjg53JGhj7B3CFcLIzSqq8
6 changed files with 1558 additions and 0 deletions

8
stages/shared/Makefile Normal file
View file

@ -0,0 +1,8 @@
lib.o: lib.asm
nasm -g -f elf64 -o lib.o lib.asm
alloc.o: alloc.asm
nasm -g -f elf64 -o alloc.o alloc.asm
vec.o: vec.asm
nasm -g -f elf64 -o vec.o vec.asm

253
stages/shared/alloc.asm Normal file
View file

@ -0,0 +1,253 @@
;; Allocator
global alloc_pages
global dealloc_pages
global realloc_pages
global heap_alloc
global heap_dealloc
global heap_realloc
extern memcpy
section .bss
heap resq 1
section .text
oom_msg: db "Out of memory!", 10
oom:
lea rdi, [abs oom_msg]
mov esi, 15
mov rax, 1 ; syscall: write
mov rdi, 2 ; fd: stderr
syscall
mov rax, 60 ; syscall: exit
mov rdi, -1 ; status: -1
syscall
;; allocates $rdi bytes worth of pages via mmap
alloc_pages:
mov rax, 9 ; syscall: mmap
mov rsi, rdi ; length: rdi
xor rdi, rdi ; addr: NULL
mov rdx, 3 ; prot: PROT_READ | PROT_WRITE
mov r10, 34 ; flags: MAP_PRIVATE | MAP_ANONYMOUS
mov r8, -1 ; fd: -1
xor r9, r9 ; offset: 0
syscall
cmp rax, -1
jle oom
ret
dealloc_pages:
mov rax, 11 ; syscall: munmap
mov rdi, rsi ; addr: rsi
mov rsi, rdx ; length: rdx
syscall
cmp rax, -1
jle oom
ret
;; reallocates memory at $rdi[..$rsi] to a new location of size $rdx.
realloc_pages:
push rbp
mov rbp, rsp
sub rsp, 32
mov qword [rsp], rsi
mov qword [rsp + 8], rdi
mov rdi, rdx
call alloc_pages
mov rsi, rdi
mov rdi, qword [rsp + 8]
mov rdx, qword [rsp]
mov qword [rsp + 16], rax
call memcpy
mov rdi, qword [rsp + 8]
mov rsi, qword [rsp]
call dealloc_pages
mov rax, qword [rsp + 16]
add rsp, 32
pop rbp
ret
make_slab:
push rdi
mov rdi, 0x4000
call alloc_pages
mov qword [rax + 0x4000 - 8], 0 ; initialize the tail pointer to NULL
pop rdi
mov qword [rdi], 0 ; tail_end = 0
mov qword [rdi + 8], 0 ; free = NULL
mov qword [rdi + 16], rax ; first_block = allocated
mov qword [rdi + 24], rax ; last_block = allocated
mov rax, rdi
ret
init_heap:
push r14
xor r14, r14
mov rax, qword [rel heap]
test rax, rax
jnz .done
mov rdi, 0x1000
call alloc_pages
mov qword [rel heap], rax
.loop:
cmp r14, 9
jge .done
mov rdi, r14
shl rdi, 5 ; idx * 32
mov rax, qword [rel heap]
lea rdi, [rax + rdi] ; &heap.slabs[idx]
call make_slab
inc r14
jmp .loop
.done:
pop r14
ret
;; finds the correct slab for an allocation with size $rdi and align $rsi
slab_bucket:
xor rax, rax
dec rdi ; if size is a power of two, dec so we can later inc
dec rsi ; ^^
or rdi, rsi ; we only care about the log2, so just gather all the bits
bsr rsi, rdi ; log2((size-1) | (align-1))
sub rsi, 2 ; +1 for the dec, -3 to collapse the first 3 slabs into one
cmovae rax, rsi ; saturating sub
ret
slab_alloc:
push rbx
mov rax, qword [rdi + 8] ; free
test rax, rax
jz .no_free
mov rdx, qword [rax] ; next free chunk
mov qword [rdi + 8], rdx ; free = next
pop rbx
ret
.no_free:
add esi, 3 ; undo the -3 from slab_bucket to get the actual log2(size)
and esi, 63 ; clamp for safety
mov edx, 16376 ; 0x4000 - 8
mov ecx, esi
shr rdx, cl ; 0x4000 - 8 >> log2(size)
mov rax, qword [rdi] ; tail_end
mov rbx, qword [rdi + 24] ; last_block
cmp rax, rdx
jb .alloc_from_block
push rsi
push rax
push rdi
mov rdi, 0x4000 ; allocate a new block
call alloc_pages
mov qword [rax + 0x4000 - 8], 0 ; initialize the tail pointer to NULL
pop rdi
mov rsi, qword [rdi + 24] ; last_block
mov qword [rsi + 0x4000 - 8], rax
mov qword [rdi + 24], rax ; last_block = new block
mov rbx, rax
pop rax
pop rsi
.alloc_from_block:
inc qword [rdi] ; tail_end++
mov ecx, esi
shl rax, cl
add rax, rbx
pop rbx
ret
;; allocate a chunk of memory of size $rdi and alignment $rsi
heap_alloc:
mov rax, qword [rel heap]
cmp rax, 0
je .init
.is_init:
push rdi
call slab_bucket
cmp rax, 9
jge .mmap
mov rsi, rax
shl rax, 5 ; idx * 32
mov rdi, qword [rel heap]
lea rdi, [rdi + rax] ; &heap.slabs[idx]
call slab_alloc
pop rdi
ret
.init:
push rdi
push rsi
call init_heap
pop rsi
pop rdi
jmp .is_init
.mmap:
pop rdi
call alloc_pages
ret
;; deallocates a chunk of memory at $rdi of size $rsi and alignment $rdx
heap_dealloc:
push rbp
mov rbp, rsp
push rsi
push rdi
mov rdi, rsi
mov rsi, rdx
call slab_bucket
cmp rax, 9
jge .mmap
mov rsi, rax
shl rax, 5 ; idx * 32
mov rdi, qword [rel heap]
lea rdi, [rdi + rax] ; &heap.slabs[idx]
mov rax, qword [rdi + 8] ; free
pop rsi
mov qword [rsi], rax
mov qword [rdi + 8], rsi
pop rsi
pop rbp
ret
.mmap:
pop rdi
pop rsi
call dealloc_pages
pop rbp
ret
;; reallocates a chunk of memory at $rdi of size $rsi and alignment $rdx to a new location of size $rcx
heap_realloc:
push rbp
mov rbp, rsp
push rbx
push r12
push r13
push r14
mov rbx, rdi
mov r12, rsi
mov r13, rdx
mov r14, rcx
mov rdi, r14
mov rsi, r13
call heap_alloc
mov r14, rax
mov rdi, rbx
mov rsi, rax
mov rdx, r12
call memcpy
mov rdi, rbx
mov rsi, r12
mov rdx, r13
call heap_dealloc
mov rax, r14
pop r14
pop r13
pop r12
pop rbx
pop rbp
ret

428
stages/shared/alloc.hex1 Normal file
View file

@ -0,0 +1,428 @@
@oom_msg:
4f 75 74 20 6f 66 20 6d 65 6d 6f 72 79 21 0a
@oom:
# lea rdi, ['oom_msg]
48 8d 3d 'oom_msg
# mov esi, 15
be 0f 00 00 00
# mov eax, 1
b8 01 00 00 00
# mov edi, 2
bf 02 00 00 00
# syscall
0f 05
# mov eax, 60
b8 3c 00 00 00
# mov rdi, -1
48 c7 c7 ff ff ff ff
# syscall
0f 05
@alloc_pages:
# mov rax, 9
b8 09 00 00 00
# mov rsi, rdi
48 89 fe
# xor rdi, rdi
48 31 ff
# mov edi, 3
ba 03 00 00 00
# mov r10, 0x22
41 ba 22 00 00 00
# mov r8, -1
49 c7 c0 ff ff ff ff
# xor r9, r9
4d 31 c9
# syscall
0f 05
# cmp rax, -1
48 83 f8 ff
# jle 'oom
0f 8e 'oom
# ret
c3
@dealloc_pages:
# mov rax, 11
b8 0b 00 00 00
# mov rdi, rsi
48 89 f7
# mov rsi, rdx
48 89 d6
# syscall
0f 05
# cmp rax, -1
48 83 f8 ff
# jle 'oom
0f 8e 'oom
# ret
c3
@realloc_pages:
# push rbp
55
# mov rbp, rsp
48 89 e5
# sub rsp, 32
48 83 ec 20
# mov qword [rsp], rsi
48 89 34 24
# mov qword [rsp + 8], rdi
48 89 7c 24 08
# mov rdi, rdx
48 89 d7
# call 'alloc_pages
e8 'alloc_pages
# mov rsi, rax
48 89 fe
# mov rdi, qword [rsp + 8]
48 8b 7c 24 08
# mov rdx, qword [rsp]
48 8b 14 24
# mov qword [rsp + 16], rax
48 89 44 24 10
# call 'memcpy
e8 'memcpy
# mov rdi, qword [rsp + 8]
48 8b 7c 24 08
# mov rsi, qword [rsp]
48 8b 34 24
# call 'dealloc_pagesA
e8 'dealloc_pages
# mov rax, qword [rsp + 16]
48 8b 44 24 10
# add rsp, 32
48 83 c4 20
# pop rbp
5d
# ret
c3
@make_slab:
# push rdi
57
# mov rdi, 0x4000
bf 00 40 00 00
# call 'alloc_pages
e8 'alloc_pages
# mov qword [rsp + 0x4000 - 8], 0
48 c7 80 f8 3f 00 00 00 00 00 00
# pop rdi
5f
# mov qword [rdi], 0
48 c7 07 00 00 00 00
# mov qword [rdi + 8], 0
48 c7 47 08 00 00 00 00
# mov qword [rdi + 16], rax
48 89 47 10
# mov qword [rdi + 24], rax
48 89 47 18
# mov rax, rdi
48 89 f8
# ret
c3
@heap:
00 00 00 00 00 00 00 00
@init_heap.done:
# pop r14
41 5e
# ret
c3
@init_heap:
# push r14
41 56
# xor r14, r14
4d 31 f6
# mov rax, ['heap]
48 8b 05 'heap
# test rax, rax
48 85 c0
# jne .done
0f 85 'init_heap.done
# mov rdi, 0x1000
bf 00 10 00 00
# call alloc_pages
e8 'alloc_pages
# mov ['heap], rax
48 89 05 'heap
@init_heap.loop:
# cmp r14, 9
49 83 fe 09
# jge .done
0f 8d 'init_heap.done
# mov rdi, r14
4c 89 f7
# shl rdi, 12
48 c1 e7 05
# mov rax, ['heap]
48 8b 05 'heap
# lea rdi, [rax + rdi]
48 8d 3c 38
# call make_slab
e8 'make_slab
# inc r14
49 ff c6
# jmp .loop
e9 'init_heap.loop
@slab_bucket:
# xor eax, eax
31 c0
# dec rdi
48 ff cf
# dec rsi
48 ff ce
# or rdi, rsi
48 09 f7
# bsr rsi, rdi
48 0f bd f7
# sub rsi, 2
48 83 ee 02
# cmovae rax, rsi
48 0f 43 c6
# ret
c3
@slab_alloc.alloc_from_block:
# inc qword [rdi]
48 ff 07
#mov ecx, esi
89 f1
# shl rax, cl
48 d3 e0
# add rax, rbx
48 01 d8
# pop rbx
5b
# ret
c3
@slab_alloc.no_free:
# add esi, 3
83 c6 03
# and esi, 63
83 e6 3f
# mov edx, 0x4000 - 8
ba f8 3f 00 00
# mov ecx, esi
89 f1
# shr rdx, cl
48 d3 ea
# mov rax, qword [rdi]
48 8b 07
# mov rbx, qword [rdi + 24]
48 8b 5f 18
# cmp rax, rdx
48 39 d0
# jb .alloc_from_block
0f 82 'slab_alloc.alloc_from_block
# push rsi
56
# push rax
50
# push rdi
57
# mov rdi, 0x4000
bf 00 40 00 00
# call 'alloc_pages
e8 'alloc_pages
# mov qword [rax + 0x4000 - 8], 0
48 c7 80 f8 3f 00 00 00 00 00 00
# pop rdi
5f
# mov rsi, qword [rdi + 24]
48 8b 77 18
# mov qword [rdi + 24], rax
48 89 86 f8 3f 00 00
# mov qword [rdi + 24], rax
48 89 47 18
# mov rbx, rax
48 89 c3
# pop rax
58
# pop rsi
5e
@slab_alloc:
# push rbx
53
# mov rax, qword [rdi + 8]
48 8b 47 08
# test rax, rax
48 85 c0
# je .no_free
0f 84 'slab_alloc.no_free
# mov rdx, qword [rax]
48 8b 10
# mov qword [rdi + 8], rdx
48 89 57 08
# pop rbx
5b
# ret
c3
@heap_alloc.mmap:
# pop rdi
5f
# call 'alloc_pages
e8 'alloc_pages
# ret
c3
@heap_alloc.is_init:
# push rdi
57
# call slab_bucket
e8 'slab_bucket
# cmp rax, 9
48 83 f8 09
# jge .mmap
0f 8d 'heap_alloc.mmap
# mov rsi, rax
48 89 c6
# shl rax, 5
48 c1 e0 05
# mov rdi, qword ['heap]
48 8b 3d 'heap
# lea rdi, [rdi + rax]
48 8d 3c 07
# call 'slab_alloc
e8 'slab_alloc
# pop rdi
5f
# ret
c3
@heap_alloc.init:
# push rdi
57
# push rsi
56
# call 'init_heap
e8 'init_heap
# pop rsi
5e
# pop rdi
5f
# jmp 'heap_alloc.is_init
e9 'heap_alloc.is_init
@heap_alloc:
# mov rax, qword ['heap]
48 8b 05 'heap
# cmp rax, 0
48 83 f8 00
# je .init
0f 84 'heap_alloc.init
# jmp 'heap_alloc.is_init
e9 'heap_alloc.is_init
@heap_dealloc.mmap:
# pop rdi
5f
# pop rsi
5e
# call 'dealloc_pages
e8 'dealloc_pages
# pop rbp
5d
# ret
c3
@heap_dealloc:
# push rbp
55
# mov rbp, rsp
48 89 e5
# push rsi
56
# push rdi
57
# mov rdi, rsi
48 89 f7
# mov rsi, rdx
48 89 d6
# call 'slab_bucket
e8 'slab_bucket
# cmp rax, 9
48 83 f8 09
# jge 'heap_dealloc.mmap
0f 8d 'heap_dealloc.mmap
# mov rsi, rax
48 89 c6
# shl rax, 5
48 c1 e0 05
# mov rdi, qword ['heap]
48 8b 3d 'heap
# lea rdi, [rdi + rax]
48 8d 3c 07
# mov rax, qword [rdi + 8]
48 8b 47 08
# pop rsi
5e
# mov qword [rsi], rax
48 89 06
# mov qword [rdi + 8], rsi
48 89 77 08
# pop rsi
5e
# pop rbp
5d
# ret
c3
@heap_realloc:
# push rbp
55
# mov rbp, rsp
48 89 e5
# push rbx
53
# push r12
41 54
# push r13
41 55
# push r14
41 56
# mov rbx, rdi
48 89 fb
# mov r12, rsi
49 89 f4
# mov r13, rdx
49 89 d5
# mov r14, rcx
49 89 ce
# mov rdi, r14
4c 89 f7
# mov rsi, r13
4c 89 ee
# call 'heap_alloc
e8 'heap_alloc
# mov r14, rax
49 89 c6
# mov rdi, rbx
48 89 df
# mov rsi, rax
48 89 c6
# mov rdx, r12
4c 89 e2
# call 'memcpy
e8 'memcpy
# mov rdi, rbx
48 89 df
# mov rsi, r12
4c 89 e6
# mov rdx, r13
4c 89 ea
# call 'heap_dealloc
e8 'heap_dealloc
# mov rax, r14
4c 89 f0
# pop r14
41 5e
# pop r13
41 5d
# pop r12
41 5c
# pop rbx
5b
# pop rbp
5d
# ret
c3

343
stages/shared/lib.asm Normal file
View file

@ -0,0 +1,343 @@
;; Common Library Functions
global strlen
global strstr
global strcmp
global strfind
global memcpy
global is_alpha
global is_digit
global is_alphanumeric
global is_id_start
global is_id_cont
global to_digit
global parse_i64
global parse_i64_cstr
global exit
global fasthash
section .text
;; fn strlen(s: *const u8) -> usize
strlen:
xor eax, eax
.loop:
cmp byte [rdi + rax], 0
je .done
inc rax
jmp .loop
.done:
ret
;; find in the first $rsi bytes of $rdi (haystack) the first $rcx bytes of $rdx (needle).
;; returns the index in $rax, or -1 if not found.
;; fn strstr(haystack: *const u8, haystack_len: usize, needle: *const u8, needle_len: usize) -> isize
strstr:
push rbx
push r12
push r13
mov r13, rdi ; save original haystack pointer
mov rax, rsi ; remaining haystack length
.loop_haystack:
cmp rax, rcx
jb .not_found ; not enough haystack left for needle
xor rbx, rbx ; needle index
.loop_needle:
cmp rbx, rcx
jz .found ; found the needle
mov r12b, byte [rdx + rbx] ; needle[needle_index]
cmp r12b, byte [rdi + rbx] ; haystack[haystack_index + needle_index]
jne .next_haystack ; mismatch, move to next haystack index
inc rbx
jmp .loop_needle
.next_haystack:
inc rdi ; move to next haystack index
dec rax ; decrease remaining haystack length
jmp .loop_haystack
.not_found:
xor eax, eax
dec rax
jmp .done
.found:
sub rdi, r13 ; calculate the index of the found needle
mov rax, rdi
.done:
pop r13
pop r12
pop rbx
ret
;; fn strcmp(s1: *const u8, s1_len: usize, s2: *const u8, s2_len: usize) -> i32
strcmp:
cmp rcx, rsi
cmovb rsi, rcx ; if rhs is shorter, use its length for the loop
xor eax, eax
.strcmp_loop:
cmp rsi, rax
jz .strcmp_equal
movzx ecx, byte [rdx + rax]
cmp byte [rdi + rax], cl
lea rax, [rax + 1]
je .strcmp_loop
seta al ; al = lhs > rhs
sbb al, 0 ; al = al - CF
ret
.strcmp_equal:
xor eax, eax
ret
;; fn strfind(s: *const u8, len: usize, c: u8) -> isize
strfind:
strch:
xor eax, eax
.loop:
cmp rax, rsi
jge .not_found
mov cl, byte [rdi + rax]
cmp cl, dl
je .found
inc rax
jmp .loop
.not_found:
xor eax, eax
dec rax
.found:
ret
;; fn memcpy(src: *const u8, dst: *mut u8, len: usize)
memcpy:
.loop:
test rdx, rdx
jz .done
mov al, byte [rdi]
mov byte [rsi], al
inc rsi
inc rdi
dec rdx
jmp .loop
.done:
ret
;; fn is_alpha(c: u8) -> bool
is_alpha:
movzx eax, dil
and eax, 0x1fffdf ; ignore bit 5 (case)
add eax, -65 ; subtract 'A'
cmp eax, 26 ; check if in range 0-25
setb al
ret
;; fn is_digit(c: u8) -> bool
is_digit:
movzx edi, dil
lea eax, [rdi - 48] ; subtract '0'
cmp eax, 10 ; check if in range 0-9
setb al
ret
;; fn is_alphanumeric(c: u8) -> bool
is_alphanumeric:
lea eax, [rdi - 48]
cmp eax, 10
setb cl
and dil, 0xdf
add dil, -65
cmp dil, 26
setb al
or al, cl
ret
;; fn is_id_start(c: u8) -> bool
is_id_start:
call is_alpha
cmp dil, 95
sete cl
or al, cl
ret
;; fn is_id_continue(c: u8) -> bool
is_id_cont:
call is_alphanumeric
cmp dil, 45
sete cl
or al, cl
cmp dil, 95
sete cl
or al, cl
ret
;; converts char $dil to a digit with radix $rsi, returning it in $edx. $al is set to 1 if the char is a valid digit, and 0 otherwise.
;; fn to_digit(c: u8, radix: u8) -> (is_valid: bool, digit: u8)
to_digit:
lea eax, [rsi - 2]
cmp eax, 35
jae .invalid
movzx rdi, dil
lea edx, [rdi - 65] ; 'A' = 65
and edx, -33 ; convert to uppercase
add edx, 10 ; 'A' should map to 10
lea eax, [rdi - 48] ; '0' = 48
cmp esi, 11
cmovb edx, eax ; if radix <= 10, then take the difference from '0'
cmp edi, 58
cmovb edx, eax ; or if char < '9', then take the difference from '0'
xor eax, eax
cmp edx, esi
setb al ; al = edx < radix
ret
.invalid:
xor eax, eax
ret
;; fn parse_i64(s: *const u8) -> (is_valid: bool, value: i64)
parse_i64_cstr:
call strlen
mov rsi, rax
jmp parse_i64
;; fn parse_i64(s: *const u8, len: usize) -> (is_valid: bool, value: i64)
parse_i64:
push rbp
mov rbp, rsp
push rbx ; buf
push r12 ; radix
push r13 ; acc
push r14 ; end
push rdi
sub rsp, 8
mov dword [rsp], 1 ; sign = 1
mov rbx, rdi ; buf
mov r12, 10 ; radix
xor r13, r13 ; acc
xor eax, eax
mov r14, rsi
add r14, rdi ; end = buf + len
cmp rbx, r14
jge .done ; if buf >= end, return 0
; eat leading -
cmp byte [rbx], '-'
sete al
lea rbx, [rbx + rax]
shl eax, 1
sub dword [rsp], eax
cmp rbx, r14
jge .done
; eat leading +
cmp byte [rbx], '+'
sete al
lea rbx, [rbx + rax]
cmp rbx, r14
jge .done
mov dil, byte [rbx]
mov esi, 10
call to_digit
jnb .not_numeric
; radix
cmp dil, '0'
jne .loop
inc rbx
cmp rbx, r14
jge .done
mov dil, byte [rbx]
and dil, 0xdf ; convert to uppercase
cmp dil, 'X'
sete al
lea ecx, [eax + eax * 2]
shl ecx, 1 ; multiply by 6
add r12d, ecx ; radix += 'x' ? 6 : 0
cmp dil, 'O'
sete al
shl al, 1 ; multiply by 2
sub r12d, eax ; radix -= 'o' ? 2 : 0
cmp dil, 'B'
sete al
shl al, 3 ; multiply by 8
sub r12d, eax ; radix -= 'b' ? 8 : 0
cmp r12d, 10
setne al
lea rbx, [rbx + rax] ; skip the radix prefix if it was present
.loop:
cmp rbx, r14
jge .done
mov dil, byte [rbx]
inc rbx
cmp dil, '_'
je .loop
mov esi, r12d
call to_digit
mov ecx, edx
test al, al
jz .done
mov rax, r13
imul r12
add rax, rcx
mov r13, rax
jmp .loop
.not_numeric:
xor eax, eax
xor edx, edx
add rsp, 8
pop rdi
jmp .epilogue
.done:
mov rdx, rax
neg r13
cmp dword [rsp], 0
cmovl rdx, r13
add rsp, 8
pop rdi
sub rbx, rdi
mov rax, rbx
.epilogue:
pop r14
pop r13
pop r12
pop rbx
pop rbp
ret
;; fn exit() -> !
exit:
xor edi, edi
mov rax, 60
syscall
;; fn fasthash(s: *const u8, len: usize) -> u32
fasthash:
mov eax, 0x811C9DC5 ; FNV-1a 32-bit offset basis
.loop:
test rsi, rsi
jz .done
movzx ecx, byte [rdi]
xor eax, ecx
imul eax, 0x01000193 ; FNV prime
inc rdi
dec rsi
jmp .loop
.done:
ret

449
stages/shared/lib.hex1 Normal file
View file

@ -0,0 +1,449 @@
@strlen.done:
# ret
c3
@strlen:
# xor eax, eax
31 c0
@strlen.loop:
# cmp byte [rdi + rax], 0
80 3c 07 00
# je .done:
0f 84 'strlen.done
# inc rax
48 ff c0
# jmp .loop
e9 'strlen.loop
@strstr.found:
# sub rdi, r13
4c 29 ef
# mov rax, rdi
48 89 f8
@strstr.done:
# pop r13
41 5d
# pop r12
41 5c
# pop rbx
5b
# ret
c3
@strstr.notfound
# xor eax, eax
31 c0
# dec rax
48 ff c8
# jmp .done
e9 'strstr.done
@strstr.next_hackstack:
# inc rdi
48 ff c7
# dec rax
48 ff c8
@strstr.loop_haystack:
# cmp rax, rcx
48 39 c8
# jb 'strstr.notfound
0f 82 'strstr.notfound
# xor ebx, ebx
31 db
@strstr.loop_needle:
# cmp rbx, rcx
48 39 cb
# je .found
0f 84 'strstr.found
# mov r12b, byte [rdx + rbx]
44 8a 24 1a
# cmp r12b, byte [rdi + rbx]
44 3a 24 1f
# jne .next_haystack
0f 85 'strstr.next_haystack
# inc rbx
48 ff c3
# jmp .loop_needle
e9 'strstr.loop_needle
@strstr:
# push rbx
53
# push r12
41 54
# push r13
41 55
# mov r13, rdi
49 89 fd
# mov rax, rsi
48 89 f0
# jmp .loop_haystack
e9 'strstr.loop_haystack
@strcmp.eq:
31 c0
c3
@strcmp:
# cmp rcx, rsi
48 39 f1
# cmovb rsi, rcx
48 0f 42 f1
# xor eax, eax
31 c0
@strcmp.loop:
# cmp rsi, rax
48 39 c6
# je .eq
0f 84 'strcmp.eq
# movzx ecx, byte [rdx + rax]
0f b6 0c 02
# cmp byte [rdi + rax, cl
38 0c 07
# inc rax
48 ff c0
# je .loop
0f 84 'strcmp.loop
# seta al
0f 97 c0
# sbb al, 0
1c 00
# ret
c3
@strfind.nf:
# xor eax, eax
31 c0
# dec rax
48 ff c8
@strfind.f:
c3
@strfind:
# xor eax, eax
31 c0
# cmp rax, rsi
48 39 f0
# jge .nf
0f 8d 'strfind.nf
# mov cl, byte [rdi + rax]
8a 0c 07
# cmp cl, dl
38 d1
# je .f
0f 84 'strfind.f
# inc rax
48 ff c0
# jmp strfind
e9 'strfind
@memcpy.done:
c3
@memcpy:
# test rdx, rdx
48 85 d2
# jz .done
0f 84 'memcpy.done
# mov al, byte [rdi]
8a 07
# mov byte [rsi], al
88 06
# inc rsi
48 ff c6
# inc rdi
48 ff c7
# dec rdx
48 ff ca
# jmp .loop
e9 'memcpy
@is_alpha:
40 0f b6 c7
25 df ff 1f 00
83 c0 bf
83 f8 1a
0f 92 c0
c3
@is_digit:
40 0f b6 ff
8d 47 d0
83 f8 0a
0f 92 c0
c3
@is_alphanumeric:
8d 47 d0
83 f8 0a
0f 92 c1
40 80 e7 df
40 80 c7 bf
40 80 ff 1a
0f 92 c0
08 c8
c3
@is_id_start:
# call is_alpha
e8 'is_alpha
40 80 ff 5f
0f 94 c1
08 c8
c3
@is_id_cont:
# call is_alphanumeric
e8 'is_alphanumeric
40 80 ff 2d
0f 94 c1
08 c8
40 80 ff 5f
0f 94 c1
08 c8
c3
@to_digit.inv:
# xor eax, eax
31 c0
# ret
c3
@to_digit:
8d 46 fe
83 f8 23
# jae .inv
0f 83 'to_digit.inv
48 0f b6 ff
8d 57 bf
83 e2 df
83 c2 0a
8d 47 d0
83 fe 0b
0f 42 d0
83 ff 3a
0f 42 d0
31 c0
39 f2
0f 92 c0
c3
@parse_i64.done:
# mov rdx, rax
48 89 c2
# neg r13
49 f7 dd
# cmp dword [rsp], 0
83 3c 24 00
# cmovl rdx, r13
49 0f 4c d5
# add rsp, 8
48 83 c4 08
# pop rdi
5f
# sub rbx, rdi
48 29 fb
# mov rax, rbx
48 89 d8
@parse_i64.epilogue:
# pop r14
41 5e
# pop r13
41 5d
# pop r12
41 5c
# pop rbx
5b
# pop rbp
5d
# ret
c3
@parse_i64.not_num:
# xor eax, eax
31 c0
# xor edx, edx
31 d2
# add rsp, 8
48 83 c4 08
# pop rdi
5f
# jmp .epilogue
e9 'parse_i64.epilogue
@parse_i64_cstr:
e8 'strlen
# mov rsi, rax
48 89 c6
@parse_i64:
# push rbp
55
# mov rbp, rsp
48 89 e5
# push rbx
53
# push r12
41 54
# push r13
41 55
# push r14
41 56
# push rdi
57
# sub rsp, 8
48 83 ec 08
# mov dword [rsp], 1
c7 04 24 01 00 00 00
# mov rbx, rdi
48 89 fb
# mov r12, 10
41 bc 0a 00 00 00
# xor r13, r13
4d 31 ed
# xor eax, eax
31 c0
# mov r14, rsi
49 89 f6
# add r14, rdi
49 01 fe
# cmp rbx, r14
4c 39 f3
# jge .done
0f 8d 'parse_i64.done
# cmp byte [rbx], '-'
80 3b 2d
# sete al
0f 94 c0
# lea rbx, [rbx + rax]
48 8d 1c 03
# shl eax, 1
d1 e0
# sub dword [rsp], eax
29 04 24
# cmp rbx, r14
4c 39 f3
# jge .done
0f 8d 'parse_i64.done
# cmp byte [rbx], '+'
80 3b 2b
# sete al
0f 94 c0
# lea rbx, [rbx + rax]
48 8d 1c 03
# cmp rbx, r14
4c 39 f3
# jge .done
0f 8d 'parse_i64.done
# mov dil, byte [rbx]
40 8a 3b
# mov esi, 10
be 0a 00 00 00
# call to_digit
e8 'to_digit
# jae .not_num
0f 83 'parse_i64.not_num
# cmp dil, '0'
40 80 ff 30
# jne .loop
0f 85 'parse_i64.loop
# inc rbx
48 ff c3
# cmp rbx, r14
4c 39 f3
# jge .done
0f 8d 'parse_i64.done
# mov dil, byte [rbx]
40 8a 3b
# and dil, 0xdf
40 80 e7 df
# cmp dil, 'X'
40 80 ff 58
# sete al
0f 94 c0
# lea ecx, [eax + eax * 2]
67 8d 0c 40
# shl ecx, 1
d1 e1
# add r12d, ecx
41 01 cc
# cmp dil, 'O'
40 80 ff 4f
# sete al
0f 94 c0
# shl al, 1
d0 e0
# sub r12d, eax
41 29 c4
# cmp dil, 'B'
40 80 ff 42
# sete al
0f 94 c0
# shl al, 3
c0 e0 03
# sub r12d, eax
41 29 c4
# cmp r12d, 10
41 83 fc 0a
# setne al
0f 95 c0
# lea rbx, [rbx + rax]
48 8d 1c 03
@parse_i64.loop:
# cmp rbx, r14
4c 39 f3
# jge .done
0f 8d 'parse_i64.done
# mov dil, byte [rbx]
40 8a 3b
# inc rbx
48 ff c3
# cmp dil, '_'
40 80 ff 5f
# je .loop
0f 84 'parse_i64.loop
# mov esi, r12d
44 89 e6
# call to_digit
e8 'to_digit
# mov ecx, edx
89 d1
# test al, al
84 c0
# je .done
0f 84 'parse_i64.done
# mov rax, r13
4c 89 e8
# imul r12
49 f7 ec
# add rax, rcx
48 01 c8
mov r13, rax
49 89 c5
# jmp .loop
e9 'parse_i64.loop
@exit:
# xor edi, edi
31 ff
# mov eax, 60
b8 3c 00 00 00
# syscall
0f 05
@fasthash.done:
c3
@fasthash:
# mov eax, 0x811c9dc5
b8 c5 9d 1c 81
@fasthash.loop:
# test rsi, rsi
48 85 f6
# jz .done
0f 84 'fasthash.done
# movzx ecx, byte [rdi]
0f b6 0f
# xor eax, ecx
31 c8
# imul eax, 0x01000193
69 c0 93 01 00 01
# inc rdi
48 ff c7
# dec rsi
48 ff ce
# jmp .loop
e9 'fasthash.loop

77
stages/shared/vec.asm Normal file
View file

@ -0,0 +1,77 @@
;; Vec / Dynamic Array Structure
extern heap_realloc
global vec_init
global vec_get_nth
global vec_reserve
struc Vec
.data resq 1 ; pointer to elements
.size resd 1 ; size in element count
.cap resd 1 ; cap in element count
.ele_size resd 1 ; size of each element in bytes
.ele_align resd 1 ; alignment of each element in bytes
endstruc
;; fn vec_init(vec: *mut Vec, ele_size: usize, ele_align: usize) -> *mut Vec
vec_init:
mov qword [rdi + Vec.data], 0
mov dword [rdi + Vec.size], 0
mov dword [rdi + Vec.cap], 0
mov dword [rdi + Vec.ele_size], esi
mov dword [rdi + Vec.ele_align], edx
mov rax, rdi
ret
;; fn vec_get_nth(vec: *const Vec, idx: usize) -> *const u8
vec_get_nth:
mov rax, rsi
mov ecx, dword [rdi + Vec.ele_size]
mul ecx
add rax, qword [rdi + Vec.data]
ret
;; reserve space for $rsi additional entries in vec $rdi.
;; fn vec_reserve(vec: *mut Vec, count: usize)
vec_reserve:
push rbp
mov rbp, rsp
sub rsp, 16
mov edx, dword [rdi + Vec.ele_size]
mov ecx, dword [rdi + Vec.ele_align]
mov dword [rsp], edx ; obj-size
mov dword [rsp + 4], ecx ; alignment
mov qword [rsp + 8], rdi ; table pointer
mov eax, dword [rdi + Vec.size]
add eax, esi
cmp eax, dword [rdi + Vec.cap]
jl .done
mov eax, esi
add eax, dword [rdi + Vec.size]
mov ecx, dword [rdi + Vec.cap]
mov edx, ecx
shl edx, 1
cmp eax, edx
cmovl eax, edx
; new cap
mul dword [rsp] ; obj-size
; old cap
xchg eax, ecx
mul dword [rsp] ; obj-size
mov rdi, qword [rdi + Vec.data] ; old pointer
mov esi, eax ; old size
mov edx, ecx ; new size
mov ecx, dword [rsp + 4] ; alignment
call heap_realloc
mov rdi, qword [rsp + 8] ; table pointer
mov qword [rdi + Vec.data], rax
.done:
add rsp, 16
pop rbp
ret