Compare commits

...

9 commits

Author SHA1 Message Date
janis d47c933c04
fix vec 2025-10-20 23:00:55 +02:00
janis b5c0a41aa4
move panic out, add stack frames, proper rust test 2025-10-20 22:44:27 +02:00
janis 9a235e625a
asdf 2025-10-20 22:32:24 +02:00
janis e9f634bca5
vec get 2025-10-20 17:55:32 +02:00
janis 8bc56220fc
vec drop 2025-10-20 17:53:42 +02:00
janis ac2f3a04b2
add item size to vec struct rather than arg 2025-10-20 17:47:36 +02:00
janis 69bd77c8c0
strchr, memswap, blob vec 2025-10-20 16:35:36 +02:00
janis 669650ffea
strchr 2025-10-20 00:51:48 +02:00
janis 208e05b1e6
convert lib.asm to sysv cc 2025-10-20 00:43:03 +02:00
6 changed files with 688 additions and 180 deletions

View file

@ -1,21 +1,45 @@
# Makefile: Compile and link main.asm using nasm and mold, intermediate files in target/ # Makefile: Compile and link main.asm using nasm and mold, intermediate files in target/
TARGET_DIR := target TARGET_DIR := target
SRC := src/main.asm src/lib.asm src/int_to_str.asm SRC := src/lib.asm src/int_to_str.asm src/vec.asm
OBJ := $(addprefix $(TARGET_DIR)/, $(notdir $(SRC:.asm=.o))) OBJ := $(patsubst src/%.asm,$(TARGET_DIR)/%.o,$(SRC))
BIN_SRC := src/main.asm src/panic.asm
BIN_OBJ := $(patsubst src/%.asm,$(TARGET_DIR)/%.o,$(BIN_SRC))
BIN := $(TARGET_DIR)/main BIN := $(TARGET_DIR)/main
TEST_SRCS := $(wildcard tests/*.rs)
TEST_BINS := $(patsubst tests/%.rs,$(TARGET_DIR)/tests/%,$(TEST_SRCS))
OBJ_LINK_ARGS := $(foreach o,$(OBJ),-C link-arg=$(o))
.PHONY: all clean .PHONY: all clean
all: $(BIN) all: $(BIN)
test-bins: $(TEST_BINS)
test: test-bins
@echo "Running all tests.."
@for b in $(TEST_BINS); do \
echo "==> Running $$b"; \
"$$b" || exit $$?; \
done
# pattern rule: compile each .rs into a binary with the same base name
$(TARGET_DIR)/tests/%: tests/%.rs | $(OBJ) $(TARGET_DIR)/tests
@echo "[$(RUSTC)] $< -> $@"
rustc -Clink-arg=-fuse-ld=mold $(OBJ_LINK_ARGS) -g -o $@ $<
$(TARGET_DIR): $(TARGET_DIR):
mkdir -p $(TARGET_DIR) mkdir -p $(TARGET_DIR)/tests
$(TARGET_DIR)/tests: $(TARGET_DIR)
mkdir -p $(TARGET_DIR)/tests
$(TARGET_DIR)/%.o: src/%.asm | $(TARGET_DIR) $(TARGET_DIR)/%.o: src/%.asm | $(TARGET_DIR)
nasm -f elf64 -g $< -o $@ nasm -f elf64 -g $< -o $@
$(BIN): $(OBJ) $(BIN): $(OBJ) $(BIN_OBJ)
mold -run ld -o $(BIN) $(OBJ) mold -run ld -o $(BIN) $(OBJ)
run: $(BIN) run: $(BIN)

View file

@ -1,50 +1,44 @@
default rel
section .rdata section .rdata
panic_msg db "panic occured!", 10 panic_msg db "panic occured!", 10
panic_msg_len equ $ - panic_msg panic_msg_len equ $ - panic_msg
oom_msg db "panic: oom!", 10 oom_msg db "oom!", 10
oom_msg_len equ $ - oom_msg oom_msg_len equ $ - oom_msg
file_error_msg db "Could not open file: "
file_error_msg_len equ $ - file_error_msg
error_msg db "Error: " error_msg db "Error: "
error_msg_len equ $ - error_msg error_msg_len equ $ - error_msg
section .text section .text
global oom global oom
global panic
global strlen global strlen
global strcmp global strcmp
global strchr
global streq global streq
global memcpy global memcpy
global memswap
global eprint_str global eprint_str
global exit global exit
global error_to_str global error_to_str
global eprint_error global eprint_error
global alloc_pages global alloc_pages
global allocate
global is_alpha global is_alpha
global is_numeric global is_numeric
global is_id_continue global is_id_continue
global is_id_start global is_id_start
extern panic
;; ============================== ;; ==============================
;; Helper functions ;; Helper functions
;; ============================== ;; ==============================
;; Abort the program with a default panic message
panic:
mov rcx, panic_msg
mov rdx, panic_msg_len
call eprint_str
; exit with error code 1
mov rax, 60 ; syscall: exit
mov rdi, 1 ; status: 1
syscall
;; Abort the program with a default panic message ;; Abort the program with a default panic message
oom: oom:
mov rcx, oom_msg lea rdi, [oom_msg]
mov rdx, oom_msg_len lea rsi, [oom_msg_len]
call eprint_str call eprint_str
; exit with error code 1 ; exit with error code 1
mov rax, 60 ; syscall: exit mov rax, 60 ; syscall: exit
@ -52,28 +46,28 @@ oom:
syscall syscall
;; abort the program ;; abort the program
;; rcx: status code ;; rdi: status code
exit: exit:
mov rax, 60 ; syscall: exit mov rax, 60 ; syscall: exit
mov rdi, rcx
syscall syscall
;; Writes a string to stderr: ;; Writes a string to stderr:
;; rcx: pointer to string ;; rdi: pointer to string
;; rdx: length of string ;; rsi: length of string
eprint_str: eprint_str:
mov rax, 1 ; syscall: write mov rax, 1 ; syscall: write
mov rdx, rsi ; len: length
mov rsi, rdi ; buf: str
mov rdi, 2 ; fd: stderr mov rdi, 2 ; fd: stderr
mov rsi, rcx ; buf: str
syscall syscall
ret ret
;; calculates length of null-terminated string ;; calculates length of null-terminated string
;; rcx: pointer to string ;; rdi: pointer to string
strlen: strlen:
xor rax, rax ; length counter xor rax, rax ; length counter
.strlen_loop: .strlen_loop:
cmp byte [rcx + rax], 0 cmp byte [rdi + rax], 0
je .strlen_done je .strlen_done
inc rax inc rax
jmp .strlen_loop jmp .strlen_loop
@ -81,109 +75,173 @@ strlen:
ret ret
;; Checks two byte slices for equality ;; Checks two byte slices for equality
;; rcx: pointer to first slice ;; rdi: pointer to first slice
;; rdx: length of first slice ;; rsi: length of first slice
;; r8: pointer to second slice ;; rdx: pointer to second slice
;; r9: length of second slice ;; rcx: length of second slice
;; returns: 1 if equal, 0 if not equal ;; returns: 1 if equal, 0 if not equal
;; fn streq(a: &[u8], b: &[u8]) -> bool
streq: streq:
cmp rdx, r9 ; if a.len() == b.len() {
jne .not_equal ; lengths differ cmp rsi, rcx
jne .false
xor r10, r10 ; index = 0 ; for i in 0..a.len() {
xor r8, r8
.loop: .loop:
cmp r10, rdx cmp r8, rsi
jge .equal ; done all bytes jge .true
; if a[i] != b[i] {
mov al, [rcx + r10] ; char from a mov al, [rdi + r8]
mov bl, [r8 + r10] ; char from b mov cl, [rdx + r8]
cmp al, bl cmp al, cl
jne .not_equal ; chars differ ; return false;
jne .false
inc r10 ; }
inc r8
jmp .loop jmp .loop
.equal: ; }
mov rax, 1 ; equal ; return true;
.true:
mov rax, 1
ret ret
.not_equal: ; } else {
xor rax, rax ; not equal ; return false;
.false:
xor rax, rax
ret ret
; }
;; Compares two byte slices ;; Compares two byte slices
;; rcx: pointer to first slice ;; rdi: pointer to first slice
;; rdx: length of first slice ;; rsi: length of first slice
;; r8: pointer to second slice ;; rdx: pointer to second slice
;; r9: length of second slice ;; rcx: length of second slice
; returns: -1, 0, or 1 in rax ;; returns: -1, 0, or 1 in rax
;; fn strcmp(a: &[u8], b: &[u8]) -> Ordering
strcmp: strcmp:
xor rax, rax ; result = 0 (assume equal) ; let min_len = min(a.len(), b.len());
test rdx, rdx mov rax, rsi
jz .check_empty_b ; if len a == 0 cmp rsi, rcx
test r9, r9 cmovg rax, rcx
jz .check_empty_a ; if len b == 0 ; for i in 0..min_len {
xor r8, r8
mov r10, rdx ; min(len a, len b) in r10
cmp r9, rdx
cmovb r10, r9
xor r11, r11 ; index = 0
.loop: .loop:
cmp r11, r10 cmp r8, rax
jge .after_loop ; done min(len a, len b) bytes jge .length_check
mov r9b, [rdi + r8]
mov al, [rcx + r11] ; char from a mov r10b, [rdx + r8]
mov bl, [r8 + r11] ; char from b ; if a[i] < b[i] {
cmp al, bl cmp r9b, r10b
jb .less ; if al < bl: return -1 ; return Ordering::Less;
ja .greater ; if al > bl: return 1 jb .less
; } else if a[i] > b[i] {
inc r11 ; return Ordering::Greater;
ja .greater
; }
inc r8
jmp .loop jmp .loop
; }
.after_loop: ; if a.len() < b.len() {
cmp rdx, r9 .length_check:
je .equal ; lengths equal, strings equal cmp rsi, rcx
jb .less ; a shorter than b ; return Ordering::Less;
ja .greater ; a longer than b jb .less
; } else if a.len() > b.len() {
.equal: ; return Ordering::Greater;
xor rax, rax ; 0 ja .greater
; } else {
; return Ordering::Equal;
xor rax, rax
ret ret
; }
.less: .less:
mov rax, -1 mov rax, -1
ret ret
.greater: .greater:
mov rax, 1 mov rax, 1
ret ret
.check_empty_a: ;; Copy bytes from one memory location to another
test rdx, rdx ;; rdi: destination pointer
jz .equal ; both empty ;; rsi: source pointer
jmp .greater ; a not empty, b empty ;; rdx: number of bytes to copy
;; fn memcpy(dest: *mut u8, src: *const u8, n: usize)
memcpy:
; for i in 0..n {
xor r8, r8
.loop:
cmp r8, rdx
jge .done
; dest[i] = src[i];
mov al, [rsi + r8]
mov [rdi + r8], al
inc r8
jmp .loop
; }
.done:
ret
.check_empty_b:
test r9, r9 ;; Swap bytes between two memory locations
jz .equal ; both empty ;; rdi: pointer to first memory location
jmp .less ; b not empty, a empty ;; rsi: pointer to second memory location
;; rdx: number of bytes to swap
;; fn memswap(a: *mut u8, b: *mut u8, n: usize)
memswap:
; for i in 0..n {
xor r8, r8
.loop:
cmp r8, rdx
jge .done
; let temp = a[i];
mov al, [rdi + r8]
; a[i] = b[i];
mov cl, [rsi + r8]
mov [rdi + r8], cl
; b[i] = temp;
mov [rsi + r8], al
inc r8
jmp .loop
; }
.done:
ret
;; Copy bytes from one memory location to another ;; Returns a pointer to the first occurrence of c in s, or null if not found
;; rcx: destination pointer ;; rdi: pointer to byte slice
;; rdx: source pointer ;; rsi: length of byte slice
;; r8: number of bytes to copy ;; dl: byte to find
memcpy: ;; fn strchr(s: &[u8], c: u8) -> &[u8]
xor r10, r10 strchr:
.memcpy_loop_byte: ; if s.len() == 0 {
cmp r10, r8 test rsi, rsi
jge .memcpy_done ; return &[];
mov al, [rdx + r10] je .null
mov [rcx + r10], al ; } else {
inc r10 xor r8, r8
jmp .memcpy_loop_byte ; for i in 0..s.len() {
.memcpy_done: .loop:
cmp r8, rsi
jge .null
; if s[i] == c {
mov al, [rdi + r8]
cmp al, dl
; return &s[i..];
je .found
; }
inc r8
; }
jmp .loop
; }
.null:
xor rax, rax
xor rdx, rdx
ret
.found:
add rdi, r8
mov rax, rdi
mov rdx, rsi
sub rdx, r8
ret ret
section .rdata section .rdata
@ -198,16 +256,16 @@ section .rdata
section .text section .text
;; Converts an error code to a str (pointer, length) pair ;; Converts an error code to a str (pointer, length) pair
;; rcx: error code ;; rdi: error code
;; Returns: ;; Returns:
;; rax: pointer to string ;; rax: pointer to string
;; rdx: length of string ;; rdx: length of string
error_to_str: error_to_str:
cmp rcx, -21 cmp rdi, -21
je .e_is_dir je .e_is_dir
cmp rcx, -5 cmp rdi, -5
je .e_io je .e_io
cmp rcx, -9 cmp rdi, -9
je .e_bad_fd je .e_bad_fd
; unknown error ; unknown error
@ -227,92 +285,74 @@ error_to_str:
mov rdx, e_bad_fd_len mov rdx, e_bad_fd_len
ret ret
;; rcx: error code ;; rdi: error code
;; fn eprint_error(err_code: isize)
eprint_error: eprint_error:
; prologue ; let err_code = err_code;
push rsi push rdi
; eprint_str(ERROR_STR, ERROR_STR.len());
; get error string lea rdi, [error_msg]
call error_to_str lea rsi, [error_msg_len]
mov r12, rax ; r12 = pointer to error string call eprint_str
mov r13, rdx ; r13 = length of error string ; let (err, len) = error_to_str(err_code);
mov rsi, r13 pop rdi
add rsi, error_msg_len call error_to_str
add rsi, 1 ; eprint_str(err, len);
add rsi, 15 mov rdi, rax
and rsi, -16 ; align up to 16 mov rsi, rdx
sub rsp, rsi ; allocate buffer
push rsi ; save allocation size
; copy error_msg
lea rcx, [rsp + 8]
mov rdx, error_msg
mov r8, error_msg_len
call memcpy
; copy error string
lea rcx, [rsp + 8 + error_msg_len]
mov rdx, r12
mov r8, r13
call memcpy
; trailing newline
lea rdx, [rsp + 8 + error_msg_len + r13]
mov byte [rdx], 10
; print error message
lea rcx, [rsp + 8]
mov rdx, error_msg_len
add rdx, r13
add rdx, 1 ; include newline
call eprint_str call eprint_str
pop rsi
add rsp, rsi ; dealloc
; epilogue
pop rsi
ret ret
;; Allocates n pages of memory ;; rdi: number of bytes to allocate
;; rcx: number of pages ;; fn allocate(bytes: usize) -> *mut u8
;; Returns: allocate:
;; rax: pointer to allocated memory
alloc_pages:
mov rax, 9 ; syscall: mmap mov rax, 9 ; syscall: mmap
mov rsi, rdi ; length: number of bytes
xor rdi, rdi ; addr: NULL xor rdi, rdi ; addr: NULL
mov rsi, rcx ; length: number of pages
shl rsi, 12 ; length in bytes (page size = 4096)
mov rdx, 3 ; prot: PROT_READ | PROT_WRITE mov rdx, 3 ; prot: PROT_READ | PROT_WRITE
mov r10, 34 ; flags: MAP_PRIVATE | MAP_ANONYMOUS mov r10, 34 ; flags: MAP_PRIVATE | MAP_ANONYMOUS
mov r8, -1 ; fd: -1 mov r8, -1 ; fd: -1
xor r9, r9 ; offset: 0 xor r9, r9 ; offset: 0
syscall syscall
cmp rax, -4095 ; check for error cmp rax, -1 ; check for error
jae .alloc_error jae .alloc_error
ret ret
.alloc_error: .alloc_error:
mov rcx, rax ; error code mov rdi, rax
call eprint_error call eprint_error
call oom call oom
;; Returns 1 if cl is an ASCII alphabetic character, 0 otherwise ;; Returns 1 if cl is an ASCII alphabetic character, 0 otherwise
;; cl: byte to check ;; rdi: byte to check
;; fn is_alpha(c: u8) -> bool
is_alpha: is_alpha:
cmp cl, 'A' ; if ('A' <= c
jb .not_alpha cmp dil, 'A'
cmp cl, 'Z' jb .false
jbe .is_alpha_ret ; && c <= 'Z')
cmp cl, 'a' cmp dil, 'Z'
jb .not_alpha jbe .true
cmp cl, 'z' ; || ('a' <= c
jbe .is_alpha_ret cmp dil, 'a'
.is_alpha_ret: jb .false
; && c <= 'z') {
cmp dil, 'z'
jbe .true
; return true;
.true:
mov rax, 1 mov rax, 1
ret ret
.not_alpha: ; } else {
; return false;
.false:
xor rax, rax xor rax, rax
ret ret
;; check if dil is numeric (decimal)
is_numeric: is_numeric:
cmp cl, '0' cmp dil, '0'
jb .not_numeric jb .not_numeric
cmp cl, '9' cmp dil, '9'
jbe .is_numeric_ret jbe .is_numeric_ret
.is_numeric_ret: .is_numeric_ret:
mov rax, 1 mov rax, 1
@ -321,13 +361,13 @@ is_numeric:
xor rax, rax xor rax, rax
ret ret
;; cl: byte to check ;; dil: byte to check
is_id_continue: is_id_continue:
call is_alpha call is_alpha
cmp rax, 1 test rax, rax
je .is_id_continue_ret je .is_id_continue_ret
call is_numeric call is_numeric
cmp rax, 1 test rax, rax
je .is_id_continue_ret je .is_id_continue_ret
cmp cl, '_' cmp cl, '_'
je .is_id_continue_ret je .is_id_continue_ret
@ -337,10 +377,10 @@ is_id_continue:
mov rax, 1 mov rax, 1
ret ret
;; cl: byte to check ;; dil: byte to check
is_id_start: is_id_start:
call is_alpha call is_alpha
cmp rax, 1 test rax, rax
je .is_ret je .is_ret
cmp cl, '_' cmp cl, '_'
je .is_ret je .is_ret

View file

@ -12,21 +12,35 @@ extern exit
extern error_to_str extern error_to_str
extern eprint_error extern eprint_error
extern alloc_pages extern alloc_pages
extern allocate
extern is_alpha extern is_alpha
extern is_numeric extern is_numeric
extern is_id_continue extern is_id_continue
extern is_id_start extern is_id_start
extern vec_tests
section .data section .data
hello_msg db "Hello, World!", 10 hello_msg db "Hello, World!", 10
hello_msg_len equ $ - hello_msg hello_msg_len equ $ - hello_msg
file_error_msg db "Could not open file: " file_error_msg db "Could not open file: "
file_error_msg_len equ $ - file_error_msg file_error_msg_len equ $ - file_error_msg
test_success db "All tests passed!", 10
test_success_len equ $ - test_success
section .text section .text
global _start global _start
_start: _start:
mov rdi, test_success
mov rsi, test_success_len
call eprint_str
mov rdi, 0
call exit
compiler_entry:
; get filename from argv[1] ; get filename from argv[1]
; argv is at rsp + 8 ; argv is at rsp + 8
; check if argc > 1 ; check if argc > 1
@ -158,9 +172,7 @@ tokeniser_init:
; allocate buffer ; allocate buffer
mov rcx, r15 mov rcx, r15
add rcx, 4095 call allocate
shr rcx, 12 ; divide by 4096
call alloc_pages
mov [buffer], rax mov [buffer], rax
mov [buffer_len], r15 mov [buffer_len], r15

19
lang/src/panic.asm Normal file
View file

@ -0,0 +1,19 @@
default rel
section .rdata
panic_msg db "panic occured!", 10
panic_msg_len equ $ - panic_msg
extern eprint_str
section .text
global panic
;; Abort the program with a default panic message
panic:
lea rdi, [panic_msg]
lea rsi, [panic_msg_len]
call eprint_str
; exit with error code 1
mov rax, 60 ; syscall: exit
mov rdi, 1 ; status: 1
syscall

338
lang/src/vec.asm Normal file
View file

@ -0,0 +1,338 @@
section .rdata
INIT_CAPACITY equ 4096
section .text
extern memcpy
extern memswap
extern oom
extern panic
extern eprint_str
extern allocate
global vec_init
global vec_push
global vec_pop
global vec_swap
global vec_remove
global vec_get
global vec_drop
global vec_tests
;; Byte vector structure
;; struct Vec {
;; data: *mut u8,
;; len: usize,
;; capacity: usize,
;; item_size: usize,
;; drop: Option<fn(*mut u8)>,
;; }
;; size: 40 bytes
;; align: 8 bytes
;; Initialize a new vector
;; rdi: pointer to Vec struct
;; rsi: item_size
;; rdx: drop function pointer
;; fn vec_init(vec: *mut Vec, item_size: usize, drop: Option<fn(*mut u8)>)
vec_init:
push rbp
mov rbp, rsp
push rdx
push rsi
push rdi
; (*vec).data = allocate(INIT_CAPACITY);
mov rdi, INIT_CAPACITY
call allocate
pop r15
mov [r15], rax
; (*vec).len = 0;
mov qword [r15 + 8], 0
pop rsi
mov qword [r15 + 24], rsi ; item_size
; (*vec).capacity = INIT_CAPACITY / item_size;
mov rax, INIT_CAPACITY
xor rdx, rdx
div rsi
mov qword [r15 + 16], rax
pop rdx
mov qword [r15 + 32], rdx ; drop
pop rbp
ret
;; rdi: pointer to Vec struct
;; rsi: index
;; fn vec_get(vec: *mut Vec, index: usize) -> *mut u8
vec_get:
push rbp
mov rbp, rsp
; if (index >= vec.len) panic();
mov rax, [rdi + 8] ; len
cmp rsi, rax
jge .panic
; return &mut vec.data[index * vec.item_size];
mov rax, [rdi + 24] ; item_size
mul rsi ; index * item_size
mov rsi, [rdi] ; data
add rax, rsi ; data + index * item_size
pop rbp
ret
.panic:
call panic
pop rbp
ret
;; rdi: pointer to Vec struct
;; rsi: pointer to data to push
;; rdx: size of data to push
;; fn vec_push(vec: *mut Vec, data: &[u8])
vec_push:
push rbp
mov rbp, rsp
; if data.len() != vec.item_size panic();
mov rax, [rdi + 24]
cmp rdx, rax
jne .panic
; let vec, ptr, size;
sub rsp, 24
mov [rsp], rdi ; save vec
mov [rsp + 8], rsi ; save data ptr
mov [rsp + 16], rdx ; save data size
; vec_try_grow(vec, size);
call vec_try_grow
; memcpy(&vec.data[vec.len], data, size);
mov rax, [rsp] ; vec
mov rcx, [rax] ; vec.data
mov rdi, [rax + 8] ; vec.len
mov rax, [rax + 24] ; vec.item_size
mul rdi ; len * item_size
add rax, rcx ; data + len * item_size
mov rdi, rax ; dest ptr
mov rsi, [rsp + 8] ; data ptr
mov rdx, [rsp + 16] ; data size
call memcpy
; vec.len += 1;
mov rax, [rsp] ; vec
add qword [rax + 8], 1
add rsp, 24
pop rbp
ret
.panic:
call panic
pop rbp
ret
;; rdi: pointer to Vec struct
;; fn vec_pop(vec: *mut Vec)
vec_pop:
push rbp
mov rbp, rsp
; if vec.len == 0 panic();
mov rax, [rdi + 8] ; len
test rax, rax
je .underflow
; vec.len -= 1;
sub rax, 1
mov [rdi + 8], rax
; if let Some(drop) = vec.drop {
mov r8, [rdi + 32]
test r8, r8
je .ret
; drop(&mut vec.data[vec.len..vec.len + vec.item_size]);
mul qword [rdi + 24] ; len * item_size
mov rsi, [rdi] ; data
add rax, rsi ; data + len * item_size
mov rdi, rax
call r8
.ret:
pop rbp
ret
; }
.underflow:
call panic
pop rbp
ret
;; rdi: pointer to Vec struct
;; rsi: index a
;; rdx: index b
;; fn vec_swap(vec: *mut Vec, a: usize, b: usize)
vec_swap:
push rbp
mov rbp, rsp
push rbx
; let vec, a, b;
sub rsp, 24
mov [rsp], rdi
; let max_index = max(a, b);
mov rbx, rsi
cmp rbx, rdx
cmovl rbx, rdx
; if (max_index >= vec.len) panic();
mov rax, [rdi + 8] ; len
cmp rbx, rax
jge .panic
; a = a * item_size;
mov rax, [rdi + 24] ; item_size
mul rbx ; b * item_size
mov [rsp + 16], rax
; b = b * item_size;
mov rax, rsi
mul rbx ; a * item_size
mov [rsp + 8], rax
; // SAFETY: a and b must not overlap
; memswap(&mut vec.data.add(a), &mut vec.data.add(b), vec.item_size);
mov rcx, [rdi + 24] ; item_size
lea rdi, [rdi] ; vec.data
mov rsi, rdi
add rdi, [rsp + 16] ; data + a
add rsi, [rsp + 24] ; data + b
call memswap
add rsp, 24
pop rbx
pop rbp
ret
.panic:
call panic
pop rbx
ret
;; rdi: pointer to Vec struct
;; rsi: index
;; fn vec_remove(vec: *mut Vec, index: usize)
vec_remove:
push rbp
mov rbp, rsp
; if (index >= vec.len) panic();
mov rax, [rdi + 8] ; len
cmp rsi, rax
jge .panic
; if (vec.len - 1 != index {
sub rax, 1
cmp rsi, rax
je .remove_last
; vec_swap(vec, index, vec.len - 1);
mov rdx, rax ; len - 1
call vec_swap
; }
; vec.len -= 1;
.remove_last:
mov rax, [rdi + 8] ; len
sub rax, 1
mov [rdi + 8], rax
; if let Some(drop) = vec.drop {
mov r8, [rdi + 32]
test r8, r8
je .ret
; drop(&mut vec.data[vec.len..vec.len + vec.item_size]);
mul qword [rdi + 24] ; len * item_size
lea rdi, [rdi] ; data
add rdi, rax ; data + len * item_size
call r8
; }
.ret:
pop rbp
ret
.panic:
call panic
pop rbp
ret
;; rdi: pointer to Vec struct
;; fn vec_try_grow(vec: *mut Vec, push_size: usize) -> bool
vec_try_grow:
push rbp
mov rbp, rsp
push rbx
mov rax, [rdi + 16] ; capacity
mov rbx, [rdi + 8] ; len
cmp rbx, rax
jg .grow
pop rbx
xor rax, rax
pop rbp
ret
.grow:
; let vec;
; let new_capacity;
; let new_data;
sub rsp, 24
mov [rsp], rdi
; new_capacity = max(capacity * 2, len + 1)
mov rcx, [rdi + 16] ; capacity
shl rcx, 1 ; capacity * 2
cmp rcx, rbx
cmovl rcx, rbx ; rcx = new_capacity
mov [rsp + 8], rcx
mov rax, rcx
mul qword [rdi + 24] ; new_capacity * item_size
; new_data = allocate(new_capacity);
mov rdi, rax
call allocate
mov [rsp + 16], rax
mov rdi, [rsp]
; memcpy(new_data, old_data, len * vec.item_size);
mov rax, [rdi + 8] ; len
mul qword [rdi + 24] ; len * item_size
mov rdx, rax ; len
mov rsi, [rdi] ; old_data
mov rdi, [rsp + 16] ; new_data
call memcpy
; free(old_data) // TODO
; update vec
mov rdi, [rsp]
mov rax, [rsp + 16]
mov [rdi], rax ; data = new_data
mov rcx, [rsp + 8]
mov [rdi + 16], rcx ; capacity = new_capacity
add rsp, 24
pop rbx
xor rax, rax
pop rbp
ret
;; rdi: pointer to Vec struct
vec_drop:
push rbp
mov rbp, rsp
push r12
push r13
push r14
push rdi
; if let Some(drop) = vec.drop {
mov r12, [rdi + 32]
test r12, r12
je .ret
; for i in 0..vec.len {
xor r13, r13
mov r14, [rdi + 8] ; len
.loop:
cmp r13, r14
jge .ret
mov rdi, [rsp]
; drop(&mut vec.data[i * vec.item_size..(i + 1) * vec.item_size]);
mov rax, r13
mul qword [rdi + 24] ; i * item_size
mov rdi, [rdi] ; data
add rdi, rax
call r12
inc r13
jmp .loop
; }
; }
; free(vec.data);
.ret:
pop rdi
pop r14
pop r13
pop r12
pop rbp
ret

75
lang/tests/vec.rs Normal file
View file

@ -0,0 +1,75 @@
#[repr(C)]
pub struct BlobVec {
pub data: *mut u8,
pub len: usize,
pub cap: usize,
pub elem_size: usize,
pub drop: Option<extern "C" fn(*mut u8)>,
}
#[no_mangle]
extern "C" fn panic() -> ! {
panic!("Called panic from external code.");
}
unsafe impl Send for BlobVec {}
unsafe impl Sync for BlobVec {}
unsafe extern "C" {
unsafe fn vec_init(vec: *mut BlobVec, elem_size: usize, drop: Option<extern "C" fn(*mut u8)>);
unsafe fn vec_push(vec: *mut BlobVec, elem: *const u8, size: usize);
unsafe fn vec_pop(vec: *mut BlobVec);
unsafe fn vec_get(vec: *mut BlobVec, index: usize) -> *mut u8;
unsafe fn vec_remove(vec: *mut BlobVec, index: usize);
unsafe fn vec_drop(vec: *mut BlobVec);
unsafe fn exit(code: i32) -> !;
}
fn main() {
static mut DROPS: usize = 1;
let mut vec = BlobVec {
data: core::ptr::null_mut(),
len: 0,
cap: 0,
elem_size: 1,
drop: None,
};
fn as_slice<T>(vec: &BlobVec) -> &[T] {
assert_eq!(vec.elem_size, core::mem::size_of::<T>());
unsafe { core::slice::from_raw_parts(vec.data as *const T, vec.len) }
}
extern "C" fn drop(ptr: *mut u8) {
unsafe {
DROPS *= ptr.cast::<u32>().read() as usize;
}
}
unsafe {
vec_init(&mut vec, 4, Some(drop));
let mut value: u32 = 2;
assert_eq!(vec.len, 0);
vec_push(&mut vec, &value as *const u32 as *const u8, 4);
assert_eq!(vec.len, 1);
assert_eq!(as_slice::<u32>(&vec), &[2]);
let retrieved = *(vec_get(&mut vec, 0) as *mut u32);
assert_eq!(retrieved, 2);
assert_eq!(DROPS, 1);
vec_pop(&mut vec);
assert_eq!(vec.len, 0);
assert_eq!(DROPS, 2);
value = 3;
vec_push(&mut vec, &value as *const u32 as *const u8, 4);
assert_eq!(as_slice::<u32>(&vec), &[3]);
value = 5;
vec_push(&mut vec, &value as *const u32 as *const u8, 4);
assert_eq!(as_slice::<u32>(&vec), &[3, 5]);
assert_eq!(vec.len, 2);
vec_pop(&mut vec);
vec_pop(&mut vec);
assert_eq!(DROPS, 2 * 3 * 5);
eprintln!("Push/pop test passed\n");
}
}