From 6a6af5366751eee1da80adc4e7f34071396f591b Mon Sep 17 00:00:00 2001 From: janis Date: Tue, 28 Oct 2025 20:40:25 +0100 Subject: [PATCH] bump allocator --- lang/Makefile | 2 +- lang/src/alloc.asm | 153 +++++++++++++++++++++++++++++++++++++++++++ lang/tests/bump.rs | 69 +++++++++++++++++++ lang/tests/tokens.rs | 4 +- 4 files changed, 225 insertions(+), 3 deletions(-) create mode 100644 lang/src/alloc.asm create mode 100644 lang/tests/bump.rs diff --git a/lang/Makefile b/lang/Makefile index d88d227..e4c30c4 100644 --- a/lang/Makefile +++ b/lang/Makefile @@ -1,7 +1,7 @@ # 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 src/tokeniser.asm src/file.asm +SRC := src/lib.asm src/int_to_str.asm src/vec.asm src/tokeniser.asm src/file.asm src/alloc.asm OBJ := $(patsubst src/%.asm,$(TARGET_DIR)/%.o,$(SRC)) BIN_SRC := src/main.asm src/panic.asm diff --git a/lang/src/alloc.asm b/lang/src/alloc.asm new file mode 100644 index 0000000..35a327f --- /dev/null +++ b/lang/src/alloc.asm @@ -0,0 +1,153 @@ +default rel + +section .bss +align 8 +free_list: resb 40 + +section .text +extern memcpy +extern memswap +extern oom +extern panic +extern eprint_error +extern allocate + +extern vec_init +extern vec_push +extern vec_get + + +global bump_init +global bump_alloc + +;; rdi: number of bytes to allocate +;; fn mmap(length: usize) -> *mut u8 { +mmap_alloc: + push rbp + mov rbp, rsp + call allocate + pop rbp + ret + +bump_init: + push rbp + mov rbp, rsp + + lea rdi, [rel free_list] + mov rsi, 16 + mov rdx, 0 + call vec_init + + pop rbp + ret + +;; rdi: min_size +bump_new_block: + push rbp + mov rbp, rsp + + cmp rdi, 4096 + ja .mmap + mov rdi, 4096 +.mmap: + ; next power of 2 + lea rax, [rdi - 1] + lzcnt rax, rax + mov rdx, -1 + shrx rdx, rdx, rax + inc rdx + mov rdi, rdx + push rdi + call mmap_alloc + pop rdi + + ; Add to free list + sub rsp, 0x10 + mov [rsp], rax + mov [rsp + 8], rdi + + lea rdi, [rel free_list] + mov rsi, rsp + mov rdx, 0x10 + call vec_push + + add rsp, 0x10 + pop rbp + ret + +;; rdi: number of bytes to allocate +;; rsi: alignment +bump_alloc: + push rbp + mov rbp, rsp + push r12 + push r13 + sub rsp, 0x28 + mov [rsp], rdi ; size + mov [rsp + 8], rsi ; alignment + + lea rdi, [rel free_list] + mov r12, [rdi + 8] + xor r13, r13 +.alloc_loop: + cmp r13, r12 + jae .no_block + + lea rdi, [rel free_list] + mov rsi, r13 + call vec_get + mov [rsp + 32], rax ; block entry ptr + mov rdx, [rax] + mov [rsp + 16], rdx ; block_ptr + mov rax, [rax + 8] ; block_size + mov [rsp + 24], rax + + ; check for space + ; block_ptr.next_multiple_of(alignment) + size <= block_ptr + block_size + mov rcx, [rsp + 8] ; alignment + ; let ptr = block_ptr | alignment + or rax, rcx ; align up + xor rdx, rdx ; clear high qword + ; let rem = ptr % alignment + div rcx + ; let diff = alignment - rem + sub rcx, rdx + mov rax, [rsp + 16] ; block+ptr + ; let aligned_ptr = block_ptr + diff + add rax, rcx + ; let aligned_end = aligned_ptr + size + add rax, [rsp] ; size + ; let block_end = block_ptr + block_size + mov rdx, [rsp + 16] ; block_ptr + add rdx, [rsp + 24] ; block_ptr + block_size + ; if aligned_end <= block_end + cmp rax, rdx + jle .found_space + ; else try next block + inc r13 + jmp .alloc_loop +.no_block: + ; allocate new block + mov rdi, [rsp] ; size + call bump_new_block + lea r13, [rel free_list] + mov r13, [r13 + 8] ; new block index + mov r12, r13 + dec r13 + jmp .alloc_loop +.found_space: + sub rdx, rax ; remaining size in block + mov r13, [rsp + 32] ; block entry ptr + mov [r13], rax + mov rax, [rsp + 24] ; block_size + sub rax, rdx + mov [r13 + 8], rax + mov rax, [r13] + add rsp, 0x28 + pop r13 + pop r12 + pop rbp + ret + + + diff --git a/lang/tests/bump.rs b/lang/tests/bump.rs new file mode 100644 index 0000000..db39ffc --- /dev/null +++ b/lang/tests/bump.rs @@ -0,0 +1,69 @@ +#![feature(allocator_api, box_as_ptr)] + +#[unsafe(no_mangle)] +extern "C" fn panic() -> ! { + panic!("Called panic from external code."); +} + +unsafe extern "C" { + unsafe fn bump_init(); + unsafe fn bump_alloc(size: usize, align: usize) -> *mut u8; +} + +struct BumpAllocator; + +unsafe impl std::alloc::Allocator for BumpAllocator { + fn allocate( + &self, + layout: std::alloc::Layout, + ) -> Result, std::alloc::AllocError> { + unsafe { + let ptr = bump_alloc(layout.size(), layout.align()); + if ptr.is_null() { + Err(std::alloc::AllocError) + } else { + Ok(std::ptr::NonNull::slice_from_raw_parts( + std::ptr::NonNull::new_unchecked(ptr), + layout.size(), + )) + } + } + } + + unsafe fn deallocate(&self, _ptr: std::ptr::NonNull, _layout: std::alloc::Layout) { + // Bump allocator does not deallocate individual allocations + } +} + +fn main() { + unsafe { + bump_init(); + } + + let a = Box::new_in(42u32, BumpAllocator); + let b = Box::new_in(42u32, BumpAllocator); + let c = Box::new_in(52u32, BumpAllocator); + eprintln!("a: {a}, b: {b}, c: {c}"); + assert_ne!(Box::as_ptr(&a), Box::as_ptr(&b)); + assert_eq!(*a, *b); + + struct BigType { + data: [u8; 0x1010], + } + + let mut big = Box::new_in(BigType { data: [0; 0x1010] }, BumpAllocator); + assert_ne!(Box::as_ptr(&big) as *const (), Box::as_ptr(&a) as *const ()); + big.data[47] = 123; + assert_eq!(big.data[47], 123); + + #[repr(align(256))] + struct AlignedType { + data: [u8; 512], + } + let aligned = Box::new_in(AlignedType { data: [0; 512] }, BumpAllocator); + assert_eq!( + (Box::as_ptr(&aligned) as usize) % 256, + 0, + "Aligned allocation should be aligned to 256 bytes" + ); +} diff --git a/lang/tests/tokens.rs b/lang/tests/tokens.rs index 0de0be8..72ffb4c 100644 --- a/lang/tests/tokens.rs +++ b/lang/tests/tokens.rs @@ -215,13 +215,13 @@ fn main() { assert_eq!( &collect_tokens()[..], - &[[ + &[ Lexeme(32, "\"this is a string\""), Lexeme(32, "\"another\nstring\nspanning multiple\n lines\""), Lexeme(32, "\"string with a \\\"quoted\\\" word\""), Lexeme(32, "\"a\""), Lexeme(32, "\"\"") - ],] + ], ); eprintln!("Finished tokenising.");