Compare commits

..

No commits in common. "d47c933c047ecec3d4dfa13ea6ef908dc414cc98" and "81417de6cadedb0ddc9cc19313cb267833682c4f" have entirely different histories.

6 changed files with 179 additions and 687 deletions

View file

@ -1,45 +1,21 @@
# 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/lib.asm src/int_to_str.asm src/vec.asm SRC := src/main.asm src/lib.asm src/int_to_str.asm
OBJ := $(patsubst src/%.asm,$(TARGET_DIR)/%.o,$(SRC)) OBJ := $(addprefix $(TARGET_DIR)/, $(notdir $(SRC:.asm=.o)))
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)/tests mkdir -p $(TARGET_DIR)
$(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,44 +1,50 @@
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 "oom!", 10 oom_msg db "panic: 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:
lea rdi, [oom_msg] mov rcx, oom_msg
lea rsi, [oom_msg_len] mov rdx, 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
@ -46,28 +52,28 @@ oom:
syscall syscall
;; abort the program ;; abort the program
;; rdi: status code ;; rcx: 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:
;; rdi: pointer to string ;; rcx: pointer to string
;; rsi: length of string ;; rdx: 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
;; rdi: pointer to string ;; rcx: pointer to string
strlen: strlen:
xor rax, rax ; length counter xor rax, rax ; length counter
.strlen_loop: .strlen_loop:
cmp byte [rdi + rax], 0 cmp byte [rcx + rax], 0
je .strlen_done je .strlen_done
inc rax inc rax
jmp .strlen_loop jmp .strlen_loop
@ -75,173 +81,109 @@ strlen:
ret ret
;; Checks two byte slices for equality ;; Checks two byte slices for equality
;; rdi: pointer to first slice ;; rcx: pointer to first slice
;; rsi: length of first slice ;; rdx: length of first slice
;; rdx: pointer to second slice ;; r8: pointer to second slice
;; rcx: length of second slice ;; r9: 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:
; if a.len() == b.len() { cmp rdx, r9
cmp rsi, rcx jne .not_equal ; lengths differ
jne .false
; for i in 0..a.len() { xor r10, r10 ; index = 0
xor r8, r8
.loop: .loop:
cmp r8, rsi cmp r10, rdx
jge .true jge .equal ; done all bytes
; if a[i] != b[i] {
mov al, [rdi + r8] mov al, [rcx + r10] ; char from a
mov cl, [rdx + r8] mov bl, [r8 + r10] ; char from b
cmp al, cl cmp al, bl
; return false; jne .not_equal ; chars differ
jne .false
; } inc r10
inc r8
jmp .loop jmp .loop
; } .equal:
; return true; mov rax, 1 ; equal
.true:
mov rax, 1
ret ret
; } else { .not_equal:
; return false; xor rax, rax ; not equal
.false:
xor rax, rax
ret ret
; }
;; Compares two byte slices ;; Compares two byte slices
;; rdi: pointer to first slice ;; rcx: pointer to first slice
;; rsi: length of first slice ;; rdx: length of first slice
;; rdx: pointer to second slice ;; r8: pointer to second slice
;; rcx: length of second slice ;; r9: 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:
; let min_len = min(a.len(), b.len()); xor rax, rax ; result = 0 (assume equal)
mov rax, rsi test rdx, rdx
cmp rsi, rcx jz .check_empty_b ; if len a == 0
cmovg rax, rcx test r9, r9
; for i in 0..min_len { jz .check_empty_a ; if len b == 0
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 r8, rax cmp r11, r10
jge .length_check jge .after_loop ; done min(len a, len b) bytes
mov r9b, [rdi + r8]
mov r10b, [rdx + r8] mov al, [rcx + r11] ; char from a
; if a[i] < b[i] { mov bl, [r8 + r11] ; char from b
cmp r9b, r10b cmp al, bl
; return Ordering::Less; jb .less ; if al < bl: return -1
jb .less ja .greater ; if al > bl: return 1
; } else if a[i] > b[i] {
; return Ordering::Greater; inc r11
ja .greater
; }
inc r8
jmp .loop jmp .loop
; }
; if a.len() < b.len() { .after_loop:
.length_check: cmp rdx, r9
cmp rsi, rcx je .equal ; lengths equal, strings equal
; return Ordering::Less; jb .less ; a shorter than b
jb .less ja .greater ; a longer than b
; } else if a.len() > b.len() {
; return Ordering::Greater; .equal:
ja .greater xor rax, rax ; 0
; } 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
;; Copy bytes from one memory location to another .check_empty_a:
;; rdi: destination pointer test rdx, rdx
;; rsi: source pointer jz .equal ; both empty
;; rdx: number of bytes to copy jmp .greater ; a not empty, b empty
;; 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:
;; Swap bytes between two memory locations test r9, r9
;; rdi: pointer to first memory location jz .equal ; both empty
;; rsi: pointer to second memory location jmp .less ; b not empty, a empty
;; 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
;; Returns a pointer to the first occurrence of c in s, or null if not found ;; Copy bytes from one memory location to another
;; rdi: pointer to byte slice ;; rcx: destination pointer
;; rsi: length of byte slice ;; rdx: source pointer
;; dl: byte to find ;; r8: number of bytes to copy
;; fn strchr(s: &[u8], c: u8) -> &[u8] memcpy:
strchr: xor r10, r10
; if s.len() == 0 { .memcpy_loop_byte:
test rsi, rsi cmp r10, r8
; return &[]; jge .memcpy_done
je .null mov al, [rdx + r10]
; } else { mov [rcx + r10], al
xor r8, r8 inc r10
; for i in 0..s.len() { jmp .memcpy_loop_byte
.loop: .memcpy_done:
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
@ -256,16 +198,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
;; rdi: error code ;; rcx: 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 rdi, -21 cmp rcx, -21
je .e_is_dir je .e_is_dir
cmp rdi, -5 cmp rcx, -5
je .e_io je .e_io
cmp rdi, -9 cmp rcx, -9
je .e_bad_fd je .e_bad_fd
; unknown error ; unknown error
@ -285,74 +227,92 @@ error_to_str:
mov rdx, e_bad_fd_len mov rdx, e_bad_fd_len
ret ret
;; rdi: error code ;; rcx: error code
;; fn eprint_error(err_code: isize)
eprint_error: eprint_error:
; let err_code = err_code; ; prologue
push rdi push rsi
; eprint_str(ERROR_STR, ERROR_STR.len());
lea rdi, [error_msg] ; get error string
lea rsi, [error_msg_len]
call eprint_str
; let (err, len) = error_to_str(err_code);
pop rdi
call error_to_str call error_to_str
; eprint_str(err, len); mov r12, rax ; r12 = pointer to error string
mov rdi, rax mov r13, rdx ; r13 = length of error string
mov rsi, rdx 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 call eprint_str
pop rsi
add rsp, rsi ; dealloc
; epilogue
pop rsi
ret ret
;; rdi: number of bytes to allocate ;; Allocates n pages of memory
;; fn allocate(bytes: usize) -> *mut u8 ;; rcx: number of pages
allocate: ;; Returns:
;; 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, -1 ; check for error cmp rax, -4095 ; check for error
jae .alloc_error jae .alloc_error
ret ret
.alloc_error: .alloc_error:
mov rdi, rax mov rcx, rax ; error code
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
;; rdi: byte to check ;; cl: byte to check
;; fn is_alpha(c: u8) -> bool
is_alpha: is_alpha:
; if ('A' <= c cmp cl, 'A'
cmp dil, 'A' jb .not_alpha
jb .false cmp cl, 'Z'
; && c <= 'Z') jbe .is_alpha_ret
cmp dil, 'Z' cmp cl, 'a'
jbe .true jb .not_alpha
; || ('a' <= c cmp cl, 'z'
cmp dil, 'a' jbe .is_alpha_ret
jb .false .is_alpha_ret:
; && c <= 'z') {
cmp dil, 'z'
jbe .true
; return true;
.true:
mov rax, 1 mov rax, 1
ret ret
; } else { .not_alpha:
; return false;
.false:
xor rax, rax xor rax, rax
ret ret
;; check if dil is numeric (decimal)
is_numeric: is_numeric:
cmp dil, '0' cmp cl, '0'
jb .not_numeric jb .not_numeric
cmp dil, '9' cmp cl, '9'
jbe .is_numeric_ret jbe .is_numeric_ret
.is_numeric_ret: .is_numeric_ret:
mov rax, 1 mov rax, 1
@ -361,13 +321,13 @@ is_numeric:
xor rax, rax xor rax, rax
ret ret
;; dil: byte to check ;; cl: byte to check
is_id_continue: is_id_continue:
call is_alpha call is_alpha
test rax, rax cmp rax, 1
je .is_id_continue_ret je .is_id_continue_ret
call is_numeric call is_numeric
test rax, rax cmp rax, 1
je .is_id_continue_ret je .is_id_continue_ret
cmp cl, '_' cmp cl, '_'
je .is_id_continue_ret je .is_id_continue_ret
@ -377,10 +337,10 @@ is_id_continue:
mov rax, 1 mov rax, 1
ret ret
;; dil: byte to check ;; cl: byte to check
is_id_start: is_id_start:
call is_alpha call is_alpha
test rax, rax cmp rax, 1
je .is_ret je .is_ret
cmp cl, '_' cmp cl, '_'
je .is_ret je .is_ret

View file

@ -12,35 +12,21 @@ 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
@ -172,7 +158,9 @@ tokeniser_init:
; allocate buffer ; allocate buffer
mov rcx, r15 mov rcx, r15
call allocate add rcx, 4095
shr rcx, 12 ; divide by 4096
call alloc_pages
mov [buffer], rax mov [buffer], rax
mov [buffer_len], r15 mov [buffer_len], r15

View file

@ -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

View file

@ -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

View file

@ -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");
}
}