Compare commits
No commits in common. "d47c933c047ecec3d4dfa13ea6ef908dc414cc98" and "81417de6cadedb0ddc9cc19313cb267833682c4f" have entirely different histories.
d47c933c04
...
81417de6ca
|
|
@ -1,45 +1,21 @@
|
|||
# Makefile: Compile and link main.asm using nasm and mold, intermediate files in target/
|
||||
|
||||
TARGET_DIR := target
|
||||
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 src/panic.asm
|
||||
BIN_OBJ := $(patsubst src/%.asm,$(TARGET_DIR)/%.o,$(BIN_SRC))
|
||||
SRC := src/main.asm src/lib.asm src/int_to_str.asm
|
||||
OBJ := $(addprefix $(TARGET_DIR)/, $(notdir $(SRC:.asm=.o)))
|
||||
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)/tests
|
||||
|
||||
$(TARGET_DIR)/tests: $(TARGET_DIR)
|
||||
mkdir -p $(TARGET_DIR)/tests
|
||||
mkdir -p $(TARGET_DIR)
|
||||
|
||||
$(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)
|
||||
|
|
|
|||
384
lang/src/lib.asm
384
lang/src/lib.asm
|
|
@ -1,44 +1,50 @@
|
|||
default rel
|
||||
|
||||
section .rdata
|
||||
panic_msg db "panic occured!", 10
|
||||
panic_msg_len equ $ - panic_msg
|
||||
oom_msg db "oom!", 10
|
||||
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
|
||||
|
||||
|
||||
section .text
|
||||
global oom
|
||||
global panic
|
||||
global strlen
|
||||
global strcmp
|
||||
global strchr
|
||||
global streq
|
||||
global memcpy
|
||||
global memswap
|
||||
global eprint_str
|
||||
global exit
|
||||
global error_to_str
|
||||
global eprint_error
|
||||
global alloc_pages
|
||||
global allocate
|
||||
|
||||
global is_alpha
|
||||
global is_numeric
|
||||
global is_id_continue
|
||||
global is_id_start
|
||||
|
||||
extern panic
|
||||
|
||||
;; ==============================
|
||||
;; 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
|
||||
oom:
|
||||
lea rdi, [oom_msg]
|
||||
lea rsi, [oom_msg_len]
|
||||
mov rcx, oom_msg
|
||||
mov rdx, oom_msg_len
|
||||
call eprint_str
|
||||
; exit with error code 1
|
||||
mov rax, 60 ; syscall: exit
|
||||
|
|
@ -46,28 +52,28 @@ oom:
|
|||
syscall
|
||||
|
||||
;; abort the program
|
||||
;; rdi: status code
|
||||
;; rcx: status code
|
||||
exit:
|
||||
mov rax, 60 ; syscall: exit
|
||||
mov rdi, rcx
|
||||
syscall
|
||||
|
||||
;; Writes a string to stderr:
|
||||
;; rdi: pointer to string
|
||||
;; rsi: length of string
|
||||
;; rcx: pointer to string
|
||||
;; rdx: length of string
|
||||
eprint_str:
|
||||
mov rax, 1 ; syscall: write
|
||||
mov rdx, rsi ; len: length
|
||||
mov rsi, rdi ; buf: str
|
||||
mov rdi, 2 ; fd: stderr
|
||||
mov rsi, rcx ; buf: str
|
||||
syscall
|
||||
ret
|
||||
|
||||
;; calculates length of null-terminated string
|
||||
;; rdi: pointer to string
|
||||
;; rcx: pointer to string
|
||||
strlen:
|
||||
xor rax, rax ; length counter
|
||||
.strlen_loop:
|
||||
cmp byte [rdi + rax], 0
|
||||
cmp byte [rcx + rax], 0
|
||||
je .strlen_done
|
||||
inc rax
|
||||
jmp .strlen_loop
|
||||
|
|
@ -75,173 +81,109 @@ strlen:
|
|||
ret
|
||||
|
||||
;; Checks two byte slices for equality
|
||||
;; rdi: pointer to first slice
|
||||
;; rsi: length of first slice
|
||||
;; rdx: pointer to second slice
|
||||
;; rcx: length of second slice
|
||||
;; rcx: pointer to first slice
|
||||
;; rdx: length of first slice
|
||||
;; r8: pointer to second slice
|
||||
;; r9: length of second slice
|
||||
;; returns: 1 if equal, 0 if not equal
|
||||
;; fn streq(a: &[u8], b: &[u8]) -> bool
|
||||
streq:
|
||||
; if a.len() == b.len() {
|
||||
cmp rsi, rcx
|
||||
jne .false
|
||||
; for i in 0..a.len() {
|
||||
xor r8, r8
|
||||
cmp rdx, r9
|
||||
jne .not_equal ; lengths differ
|
||||
|
||||
xor r10, r10 ; index = 0
|
||||
.loop:
|
||||
cmp r8, rsi
|
||||
jge .true
|
||||
; if a[i] != b[i] {
|
||||
mov al, [rdi + r8]
|
||||
mov cl, [rdx + r8]
|
||||
cmp al, cl
|
||||
; return false;
|
||||
jne .false
|
||||
; }
|
||||
inc r8
|
||||
cmp r10, rdx
|
||||
jge .equal ; done all bytes
|
||||
|
||||
mov al, [rcx + r10] ; char from a
|
||||
mov bl, [r8 + r10] ; char from b
|
||||
cmp al, bl
|
||||
jne .not_equal ; chars differ
|
||||
|
||||
inc r10
|
||||
jmp .loop
|
||||
; }
|
||||
; return true;
|
||||
.true:
|
||||
mov rax, 1
|
||||
.equal:
|
||||
mov rax, 1 ; equal
|
||||
ret
|
||||
; } else {
|
||||
; return false;
|
||||
.false:
|
||||
xor rax, rax
|
||||
.not_equal:
|
||||
xor rax, rax ; not equal
|
||||
ret
|
||||
; }
|
||||
|
||||
|
||||
;; Compares two byte slices
|
||||
;; rdi: pointer to first slice
|
||||
;; rsi: length of first slice
|
||||
;; rdx: pointer to second slice
|
||||
;; rcx: length of second slice
|
||||
;; returns: -1, 0, or 1 in rax
|
||||
;; fn strcmp(a: &[u8], b: &[u8]) -> Ordering
|
||||
;; rcx: pointer to first slice
|
||||
;; rdx: length of first slice
|
||||
;; r8: pointer to second slice
|
||||
;; r9: length of second slice
|
||||
; returns: -1, 0, or 1 in rax
|
||||
strcmp:
|
||||
; let min_len = min(a.len(), b.len());
|
||||
mov rax, rsi
|
||||
cmp rsi, rcx
|
||||
cmovg rax, rcx
|
||||
; for i in 0..min_len {
|
||||
xor r8, r8
|
||||
xor rax, rax ; result = 0 (assume equal)
|
||||
test rdx, rdx
|
||||
jz .check_empty_b ; if len a == 0
|
||||
test r9, r9
|
||||
jz .check_empty_a ; if len b == 0
|
||||
|
||||
mov r10, rdx ; min(len a, len b) in r10
|
||||
cmp r9, rdx
|
||||
cmovb r10, r9
|
||||
|
||||
xor r11, r11 ; index = 0
|
||||
.loop:
|
||||
cmp r8, rax
|
||||
jge .length_check
|
||||
mov r9b, [rdi + r8]
|
||||
mov r10b, [rdx + r8]
|
||||
; if a[i] < b[i] {
|
||||
cmp r9b, r10b
|
||||
; return Ordering::Less;
|
||||
jb .less
|
||||
; } else if a[i] > b[i] {
|
||||
; return Ordering::Greater;
|
||||
ja .greater
|
||||
; }
|
||||
inc r8
|
||||
cmp r11, r10
|
||||
jge .after_loop ; done min(len a, len b) bytes
|
||||
|
||||
mov al, [rcx + r11] ; char from a
|
||||
mov bl, [r8 + r11] ; char from b
|
||||
cmp al, bl
|
||||
jb .less ; if al < bl: return -1
|
||||
ja .greater ; if al > bl: return 1
|
||||
|
||||
inc r11
|
||||
jmp .loop
|
||||
; }
|
||||
; if a.len() < b.len() {
|
||||
.length_check:
|
||||
cmp rsi, rcx
|
||||
; return Ordering::Less;
|
||||
jb .less
|
||||
; } else if a.len() > b.len() {
|
||||
; return Ordering::Greater;
|
||||
ja .greater
|
||||
; } else {
|
||||
; return Ordering::Equal;
|
||||
xor rax, rax
|
||||
|
||||
.after_loop:
|
||||
cmp rdx, r9
|
||||
je .equal ; lengths equal, strings equal
|
||||
jb .less ; a shorter than b
|
||||
ja .greater ; a longer than b
|
||||
|
||||
.equal:
|
||||
xor rax, rax ; 0
|
||||
ret
|
||||
; }
|
||||
|
||||
.less:
|
||||
mov rax, -1
|
||||
ret
|
||||
|
||||
.greater:
|
||||
mov rax, 1
|
||||
ret
|
||||
|
||||
;; Copy bytes from one memory location to another
|
||||
;; rdi: destination pointer
|
||||
;; rsi: source pointer
|
||||
;; 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_a:
|
||||
test rdx, rdx
|
||||
jz .equal ; both empty
|
||||
jmp .greater ; a not empty, b empty
|
||||
|
||||
|
||||
;; Swap bytes between two memory locations
|
||||
;; rdi: pointer to first memory location
|
||||
;; 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
|
||||
.check_empty_b:
|
||||
test r9, r9
|
||||
jz .equal ; both empty
|
||||
jmp .less ; b not empty, a empty
|
||||
|
||||
|
||||
;; Returns a pointer to the first occurrence of c in s, or null if not found
|
||||
;; rdi: pointer to byte slice
|
||||
;; rsi: length of byte slice
|
||||
;; dl: byte to find
|
||||
;; fn strchr(s: &[u8], c: u8) -> &[u8]
|
||||
strchr:
|
||||
; if s.len() == 0 {
|
||||
test rsi, rsi
|
||||
; return &[];
|
||||
je .null
|
||||
; } else {
|
||||
xor r8, r8
|
||||
; for i in 0..s.len() {
|
||||
.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
|
||||
;; Copy bytes from one memory location to another
|
||||
;; rcx: destination pointer
|
||||
;; rdx: source pointer
|
||||
;; r8: number of bytes to copy
|
||||
memcpy:
|
||||
xor r10, r10
|
||||
.memcpy_loop_byte:
|
||||
cmp r10, r8
|
||||
jge .memcpy_done
|
||||
mov al, [rdx + r10]
|
||||
mov [rcx + r10], al
|
||||
inc r10
|
||||
jmp .memcpy_loop_byte
|
||||
.memcpy_done:
|
||||
ret
|
||||
|
||||
section .rdata
|
||||
|
|
@ -256,16 +198,16 @@ section .rdata
|
|||
|
||||
section .text
|
||||
;; Converts an error code to a str (pointer, length) pair
|
||||
;; rdi: error code
|
||||
;; rcx: error code
|
||||
;; Returns:
|
||||
;; rax: pointer to string
|
||||
;; rdx: length of string
|
||||
error_to_str:
|
||||
cmp rdi, -21
|
||||
cmp rcx, -21
|
||||
je .e_is_dir
|
||||
cmp rdi, -5
|
||||
cmp rcx, -5
|
||||
je .e_io
|
||||
cmp rdi, -9
|
||||
cmp rcx, -9
|
||||
je .e_bad_fd
|
||||
|
||||
; unknown error
|
||||
|
|
@ -285,74 +227,92 @@ error_to_str:
|
|||
mov rdx, e_bad_fd_len
|
||||
ret
|
||||
|
||||
;; rdi: error code
|
||||
;; fn eprint_error(err_code: isize)
|
||||
;; rcx: error code
|
||||
eprint_error:
|
||||
; let err_code = err_code;
|
||||
push rdi
|
||||
; eprint_str(ERROR_STR, ERROR_STR.len());
|
||||
lea rdi, [error_msg]
|
||||
lea rsi, [error_msg_len]
|
||||
call eprint_str
|
||||
; let (err, len) = error_to_str(err_code);
|
||||
pop rdi
|
||||
; prologue
|
||||
push rsi
|
||||
|
||||
; get error string
|
||||
call error_to_str
|
||||
; eprint_str(err, len);
|
||||
mov rdi, rax
|
||||
mov rsi, rdx
|
||||
mov r12, rax ; r12 = pointer to error string
|
||||
mov r13, rdx ; r13 = length of error string
|
||||
mov rsi, r13
|
||||
add rsi, error_msg_len
|
||||
add rsi, 1
|
||||
add rsi, 15
|
||||
and rsi, -16 ; align up to 16
|
||||
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
|
||||
pop rsi
|
||||
add rsp, rsi ; dealloc
|
||||
|
||||
; epilogue
|
||||
pop rsi
|
||||
ret
|
||||
|
||||
;; rdi: number of bytes to allocate
|
||||
;; fn allocate(bytes: usize) -> *mut u8
|
||||
allocate:
|
||||
;; Allocates n pages of memory
|
||||
;; rcx: number of pages
|
||||
;; Returns:
|
||||
;; rax: pointer to allocated memory
|
||||
alloc_pages:
|
||||
mov rax, 9 ; syscall: mmap
|
||||
mov rsi, rdi ; length: number of bytes
|
||||
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 r10, 34 ; flags: MAP_PRIVATE | MAP_ANONYMOUS
|
||||
mov r8, -1 ; fd: -1
|
||||
xor r9, r9 ; offset: 0
|
||||
syscall
|
||||
cmp rax, -1 ; check for error
|
||||
cmp rax, -4095 ; check for error
|
||||
jae .alloc_error
|
||||
ret
|
||||
.alloc_error:
|
||||
mov rdi, rax
|
||||
mov rcx, rax ; error code
|
||||
call eprint_error
|
||||
call oom
|
||||
|
||||
;; Returns 1 if cl is an ASCII alphabetic character, 0 otherwise
|
||||
;; rdi: byte to check
|
||||
;; fn is_alpha(c: u8) -> bool
|
||||
;; cl: byte to check
|
||||
is_alpha:
|
||||
; if ('A' <= c
|
||||
cmp dil, 'A'
|
||||
jb .false
|
||||
; && c <= 'Z')
|
||||
cmp dil, 'Z'
|
||||
jbe .true
|
||||
; || ('a' <= c
|
||||
cmp dil, 'a'
|
||||
jb .false
|
||||
; && c <= 'z') {
|
||||
cmp dil, 'z'
|
||||
jbe .true
|
||||
; return true;
|
||||
.true:
|
||||
cmp cl, 'A'
|
||||
jb .not_alpha
|
||||
cmp cl, 'Z'
|
||||
jbe .is_alpha_ret
|
||||
cmp cl, 'a'
|
||||
jb .not_alpha
|
||||
cmp cl, 'z'
|
||||
jbe .is_alpha_ret
|
||||
.is_alpha_ret:
|
||||
mov rax, 1
|
||||
ret
|
||||
; } else {
|
||||
; return false;
|
||||
.false:
|
||||
.not_alpha:
|
||||
xor rax, rax
|
||||
ret
|
||||
|
||||
;; check if dil is numeric (decimal)
|
||||
is_numeric:
|
||||
cmp dil, '0'
|
||||
cmp cl, '0'
|
||||
jb .not_numeric
|
||||
cmp dil, '9'
|
||||
cmp cl, '9'
|
||||
jbe .is_numeric_ret
|
||||
.is_numeric_ret:
|
||||
mov rax, 1
|
||||
|
|
@ -361,13 +321,13 @@ is_numeric:
|
|||
xor rax, rax
|
||||
ret
|
||||
|
||||
;; dil: byte to check
|
||||
;; cl: byte to check
|
||||
is_id_continue:
|
||||
call is_alpha
|
||||
test rax, rax
|
||||
cmp rax, 1
|
||||
je .is_id_continue_ret
|
||||
call is_numeric
|
||||
test rax, rax
|
||||
cmp rax, 1
|
||||
je .is_id_continue_ret
|
||||
cmp cl, '_'
|
||||
je .is_id_continue_ret
|
||||
|
|
@ -377,10 +337,10 @@ is_id_continue:
|
|||
mov rax, 1
|
||||
ret
|
||||
|
||||
;; dil: byte to check
|
||||
;; cl: byte to check
|
||||
is_id_start:
|
||||
call is_alpha
|
||||
test rax, rax
|
||||
cmp rax, 1
|
||||
je .is_ret
|
||||
cmp cl, '_'
|
||||
je .is_ret
|
||||
|
|
|
|||
|
|
@ -12,35 +12,21 @@ 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
|
||||
|
|
@ -172,7 +158,9 @@ tokeniser_init:
|
|||
|
||||
; allocate buffer
|
||||
mov rcx, r15
|
||||
call allocate
|
||||
add rcx, 4095
|
||||
shr rcx, 12 ; divide by 4096
|
||||
call alloc_pages
|
||||
mov [buffer], rax
|
||||
mov [buffer_len], r15
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
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
338
lang/src/vec.asm
|
|
@ -1,338 +0,0 @@
|
|||
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
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
#[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");
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue