diff --git a/stages/shared/Makefile b/stages/shared/Makefile new file mode 100644 index 0000000..7b7e9aa --- /dev/null +++ b/stages/shared/Makefile @@ -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 diff --git a/stages/shared/alloc.asm b/stages/shared/alloc.asm new file mode 100644 index 0000000..1633420 --- /dev/null +++ b/stages/shared/alloc.asm @@ -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 diff --git a/stages/shared/alloc.hex1 b/stages/shared/alloc.hex1 new file mode 100644 index 0000000..57c13a5 --- /dev/null +++ b/stages/shared/alloc.hex1 @@ -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 \ No newline at end of file diff --git a/stages/shared/lib.asm b/stages/shared/lib.asm new file mode 100644 index 0000000..6cfb9d8 --- /dev/null +++ b/stages/shared/lib.asm @@ -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 diff --git a/stages/shared/lib.hex1 b/stages/shared/lib.hex1 new file mode 100644 index 0000000..9b6caae --- /dev/null +++ b/stages/shared/lib.hex1 @@ -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 \ No newline at end of file diff --git a/stages/shared/vec.asm b/stages/shared/vec.asm new file mode 100644 index 0000000..79b6b00 --- /dev/null +++ b/stages/shared/vec.asm @@ -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