diff --git a/lang/Makefile b/lang/Makefile index 7c70e5a..bd8c4a3 100644 --- a/lang/Makefile +++ b/lang/Makefile @@ -1,21 +1,45 @@ # Makefile: Compile and link main.asm using nasm and mold, intermediate files in target/ TARGET_DIR := target -SRC := src/main.asm src/lib.asm src/int_to_str.asm -OBJ := $(addprefix $(TARGET_DIR)/, $(notdir $(SRC:.asm=.o))) +SRC := src/lib.asm src/int_to_str.asm src/vec.asm +OBJ := $(patsubst src/%.asm,$(TARGET_DIR)/%.o,$(SRC)) + +BIN_SRC := src/main.asm +BIN_OBJ := $(patsubst src/%.asm,$(TARGET_DIR)/%.o,$(BIN_SRC)) 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 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): - 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) nasm -f elf64 -g $< -o $@ -$(BIN): $(OBJ) +$(BIN): $(OBJ) $(BIN_OBJ) mold -run ld -o $(BIN) $(OBJ) run: $(BIN) diff --git a/lang/src/lib.asm b/lang/src/lib.asm index b5fef7d..1876a55 100644 --- a/lang/src/lib.asm +++ b/lang/src/lib.asm @@ -1,10 +1,10 @@ +default rel + section .rdata panic_msg db "panic occured!", 10 panic_msg_len equ $ - panic_msg oom_msg db "panic: oom!", 10 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_len equ $ - error_msg @@ -23,6 +23,7 @@ global exit global error_to_str global eprint_error global alloc_pages +global allocate global is_alpha global is_numeric @@ -35,8 +36,8 @@ global is_id_start ;; Abort the program with a default panic message panic: - mov rdi, panic_msg - mov rsi, panic_msg_len + lea rdi, [panic_msg] + lea rsi, [panic_msg_len] call eprint_str ; exit with error code 1 mov rax, 60 ; syscall: exit @@ -45,8 +46,8 @@ panic: ;; Abort the program with a default panic message oom: - mov rdi, oom_msg - mov rsi, oom_msg_len + lea rdi, [oom_msg] + lea rsi, [oom_msg_len] call eprint_str ; exit with error code 1 mov rax, 60 ; syscall: exit @@ -64,9 +65,9 @@ exit: ;; rsi: length of string eprint_str: mov rax, 1 ; syscall: write - mov rdi, 2 ; fd: stderr mov rdx, rsi ; len: length mov rsi, rdi ; buf: str + mov rdi, 2 ; fd: stderr syscall ret @@ -95,7 +96,7 @@ streq: jne .false ; for i in 0..a.len() { xor r8, r8 -.loop +.loop: cmp r8, rsi jge .true ; if a[i] != b[i] { @@ -136,10 +137,10 @@ strcmp: .loop: cmp r8, rax jge .length_check - mov r9l, [rdi + r8] - mov r10l, [rdx + r8] + mov r9b, [rdi + r8] + mov r10b, [rdx + r8] ; if a[i] < b[i] { - cmp r9l, r10l + cmp r9b, r10b ; return Ordering::Less; jb .less ; } else if a[i] > b[i] { @@ -299,8 +300,8 @@ eprint_error: ; let err_code = err_code; push rdi ; eprint_str(ERROR_STR, ERROR_STR.len()); - mov rdi, error_msg - mov rsi, error_msg_len + lea rdi, [error_msg] + lea rsi, [error_msg_len] call eprint_str ; let (err, len) = error_to_str(err_code); pop rdi diff --git a/lang/src/main.asm b/lang/src/main.asm index 2887770..a07cbf8 100644 --- a/lang/src/main.asm +++ b/lang/src/main.asm @@ -12,21 +12,35 @@ extern exit extern error_to_str extern eprint_error extern alloc_pages +extern allocate extern is_alpha extern is_numeric extern is_id_continue extern is_id_start +extern vec_tests + section .data hello_msg db "Hello, World!", 10 hello_msg_len equ $ - hello_msg file_error_msg db "Could not open file: " file_error_msg_len equ $ - file_error_msg + test_success db "All tests passed!", 10 + test_success_len equ $ - test_success section .text global _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] ; argv is at rsp + 8 ; check if argc > 1 @@ -158,9 +172,7 @@ tokeniser_init: ; allocate buffer mov rcx, r15 - add rcx, 4095 - shr rcx, 12 ; divide by 4096 - call alloc_pages + call allocate mov [buffer], rax mov [buffer_len], r15 diff --git a/lang/src/vec.asm b/lang/src/vec.asm index 1714329..abc92d6 100644 --- a/lang/src/vec.asm +++ b/lang/src/vec.asm @@ -17,6 +17,8 @@ global vec_remove global vec_get global vec_drop +global vec_tests + ;; Byte vector structure ;; struct Vec { @@ -50,7 +52,8 @@ vec_init: mov qword [r15 + 24], rsi ; item_size ; (*vec).capacity = INIT_CAPACITY / item_size; mov rax, INIT_CAPACITY - idiv rax, rsi + xor rdx, rdx + div rsi mov qword [r15 + 16], rax pop rdx mov qword [r15 + 32], rdx ; drop @@ -66,7 +69,7 @@ vec_get: jge .panic ; return &mut vec.data[index * vec.item_size]; mov rax, [rdi + 24] ; item_size - imul rax, rsi ; index * item_size + mul rsi ; index * item_size mov rsi, [rdi] ; data add rax, rsi ; data + index * item_size ret @@ -90,7 +93,6 @@ vec_push: mov [rsp + 8], rsi ; save data ptr mov [rsp + 16], rdx ; save data size ; vec_try_grow(vec, size); - mov rsi, [rdi + 24] ; item_size call vec_try_grow ; memcpy(&vec.data[vec.len], data, size); mov rax, [rsp] ; vec @@ -101,7 +103,7 @@ vec_push: call memcpy ; vec.len += 1; mov rax, [rsp] ; vec - add [rax + 8], 1 + add qword [rax + 8], 1 add rsp, 24 ret .panic: @@ -124,9 +126,9 @@ vec_pop: test r8, r8 je .ret ; drop(&mut vec.data[vec.len..vec.len + vec.item_size]); - imul rax, [rdi + 24] ; len * item_size + mul qword [rdi + 24] ; len * item_size mov rsi, [rdi] ; data - add rsi, rax ; data + len * item_size + add rax, rsi ; data + len * item_size mov rdi, rsi call r8 .ret: @@ -156,11 +158,12 @@ vec_swap: jge .panic ; a = a * item_size; - mov rbx, [rdi + 24] ; item_size - imul rdx, rbx ; b * item_size + mov rax, [rdi + 24] ; item_size + mul rbx ; b * item_size mov [rsp + 16], rax ; b = b * item_size; - imul rsi, rbx ; a * item_size + mov rax, rsi + mul rbx ; a * item_size mov [rsp + 8], rax ; // SAFETY: a and b must not overlap @@ -168,7 +171,7 @@ vec_swap: mov rcx, [rdi + 24] ; item_size lea rdi, [rdi] ; vec.data mov rsi, rdi - add rdi [rsp + 16] ; data + a + add rdi, [rsp + 16] ; data + a add rsi, [rsp + 24] ; data + b call memswap add rsp, 24 @@ -205,22 +208,23 @@ vec_remove: test r8, r8 je .ret ; drop(&mut vec.data[vec.len..vec.len + vec.item_size]); - imul rax, [rdi + 24] ; len * item_size + mul qword [rdi + 24] ; len * item_size lea rdi, [rdi] ; data add rdi, rax ; data + len * item_size call r8 ; } .ret: ret +.panic: + call panic + ret ;; rdi: pointer to Vec struct -;; rsi: push size ;; fn vec_try_grow(vec: *mut Vec, push_size: usize) -> bool vec_try_grow: push rbx mov rax, [rdi + 16] ; capacity mov rbx, [rdi + 8] ; len - add rbx, rsi cmp rbx, rax jg .grow pop rbx @@ -238,7 +242,8 @@ vec_try_grow: cmp rcx, rbx cmovl rcx, rbx ; rcx = new_capacity mov [rsp + 8], rcx - imul rcx, [rdi + 24] ; new_capacity * item_size + mov rax, rcx + mul qword [rdi + 24] ; new_capacity * item_size ; new_data = allocate(new_capacity); mov rdi, rax call allocate @@ -246,7 +251,7 @@ vec_try_grow: mov rdi, [rsp] ; memcpy(new_data, old_data, len * vec.item_size); mov rax, [rdi + 8] ; len - imul rax, [rdi + 24] ; len * item_size + mul qword [rdi + 24] ; len * item_size mov rdx, rax ; len mov rsi, [rdi] ; old_data mov rdi, [rsp + 16] ; new_data @@ -281,7 +286,8 @@ vec_drop: jge .ret mov rdi, [rsp] ; drop(&mut vec.data[i * vec.item_size..(i + 1) * vec.item_size]); - imul r13, [rdi + 24] ; i * item_size + mov rax, r13 + mul qword [rdi + 24] ; i * item_size mov rdi, [rdi] ; data add rdi, rax call r12 diff --git a/lang/tests/vec.rs b/lang/tests/vec.rs new file mode 100644 index 0000000..e9a45f6 --- /dev/null +++ b/lang/tests/vec.rs @@ -0,0 +1,62 @@ +#[repr(C)] +pub struct BlobVec { + pub data: *mut u8, + pub len: usize, + pub cap: usize, + pub elem_size: usize, + pub drop: Option, +} + +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); + unsafe fn vec_push(vec: *mut BlobVec, elem: *const u8); + 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, + }; + + extern "C" fn drop(ptr: *mut u8) { + unsafe { + DROPS *= ptr.cast::().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); + assert_eq!(vec.len, 1); + 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); + value = 5; + vec_push(&mut vec, &value as *const u32 as *const u8); + 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"); + } +}