From 2f45beecbe41a48d692da666c53d9efd0cee0d6a Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Thu, 19 Jun 2025 01:13:43 +0200 Subject: [PATCH] `@pool` now has an optional `reserve` parameter, some minor changes to the temp_allocator API --- lib/std/core/allocators/temp_allocator.c3 | 26 ++++++++++++------ lib/std/core/mem.c3 | 27 ++++++++++++++++--- lib/std/core/mem_allocator.c3 | 17 ++++++------ releasenotes.md | 1 + .../test_suite/macros/short_trailing_body.c3t | 2 +- test/test_suite/stdlib/map_linux.c3t | 2 +- test/test_suite/stdlib/map_macos.c3t | 2 +- 7 files changed, 54 insertions(+), 23 deletions(-) diff --git a/lib/std/core/allocators/temp_allocator.c3 b/lib/std/core/allocators/temp_allocator.c3 index 9eb4647a8..ddd15cd92 100644 --- a/lib/std/core/allocators/temp_allocator.c3 +++ b/lib/std/core/allocators/temp_allocator.c3 @@ -35,6 +35,9 @@ struct TempAllocator (Allocator) TempAllocatorPage* last_page; TempAllocator* derived; bool allocated; + usz reserve_size; + usz realloc_size; + usz min_size; usz used; usz capacity; usz original_capacity; @@ -62,15 +65,20 @@ macro usz TempAllocatorPage.pagesize(&self) => self.size & ~PAGE_IS_ALIGNED; macro bool TempAllocatorPage.is_aligned(&self) => self.size & PAGE_IS_ALIGNED == PAGE_IS_ALIGNED; <* - @require size >= 16 + @require size >= 64 + @require realloc_size >= 64 @require allocator.type != TempAllocator.typeid : "You may not create a temp allocator with a TempAllocator as the backing allocator." + @require min_size > TempAllocator.sizeof + 64 : "Min size must meaningfully hold the data + some bytes" *> -fn TempAllocator*? new_temp_allocator(Allocator allocator, usz size) +fn TempAllocator*? new_temp_allocator(Allocator allocator, usz size, usz reserve = temp_allocator_reserve_size, usz min_size = temp_allocator_min_size, usz realloc_size = temp_allocator_realloc_size) { TempAllocator* temp = allocator::alloc_with_padding(allocator, TempAllocator, size)!; temp.last_page = null; temp.backing_allocator = allocator; temp.used = 0; + temp.min_size = min_size; + temp.realloc_size = realloc_size; + temp.reserve_size = reserve; temp.allocated = true; temp.derived = null; temp.original_capacity = temp.capacity = size; @@ -78,19 +86,18 @@ fn TempAllocator*? new_temp_allocator(Allocator allocator, usz size) } <* @require !self.derived - @require min_size > TempAllocator.sizeof + 64 : "Min size must meaningfully hold the data + some bytes" - @require mult > 0 : "The multiple can never be zero" *> -fn TempAllocator*? TempAllocator.derive_allocator(&self, usz min_size, usz buffer, usz mult) +fn TempAllocator*? TempAllocator.derive_allocator(&self, usz reserve = 0) { + if (!reserve) reserve = self.reserve_size; usz remaining = self.capacity - self.used; void* mem @noinit; usz size @noinit; - if (min_size + buffer > remaining) + if (self.min_size + reserve > remaining) { - return self.derived = new_temp_allocator(self.backing_allocator, min_size * mult)!; + return self.derived = new_temp_allocator(self.backing_allocator, self.realloc_size, self.reserve_size, self.min_size, self.realloc_size)!; } - usz start = mem::aligned_offset(self.used + buffer, mem::DEFAULT_MEM_ALIGNMENT); + usz start = mem::aligned_offset(self.used + reserve, mem::DEFAULT_MEM_ALIGNMENT); void* ptr = &self.data[start]; TempAllocator* temp = (TempAllocator*)ptr; $if env::ADDRESS_SANITIZER: @@ -99,6 +106,9 @@ fn TempAllocator*? TempAllocator.derive_allocator(&self, usz min_size, usz buffe temp.last_page = null; temp.backing_allocator = self.backing_allocator; temp.used = 0; + temp.min_size = self.min_size; + temp.reserve_size = self.reserve_size; + temp.realloc_size = self.realloc_size; temp.allocated = false; temp.derived = null; temp.original_capacity = temp.capacity = self.capacity - start - TempAllocator.sizeof; diff --git a/lib/std/core/mem.c3 b/lib/std/core/mem.c3 index f3d2dba6f..c05835eb1 100644 --- a/lib/std/core/mem.c3 +++ b/lib/std/core/mem.c3 @@ -576,11 +576,20 @@ fn void temp_pop(PoolState old_state) allocator::pop_pool(old_state) @inline; } -macro void @pool_init(Allocator allocator, usz pool_size, usz buffer_size; @body) @builtin +<* + @require pool_size >= 64 + @require realloc_size >= 64 + @require allocator.type != TempAllocator.typeid : "You may not create a temp allocator with a TempAllocator as the backing allocator." + @require min_size > TempAllocator.sizeof + 64 : "Min size must meaningfully hold the data + some bytes" +*> +macro void @pool_init(Allocator allocator, usz pool_size, + usz reserve_size = allocator::temp_allocator_reserve_size, + usz min_size = allocator::temp_allocator_min_size, + usz realloc_size = allocator::temp_allocator_realloc_size; @body) @builtin { Allocator current = allocator::current_temp; TempAllocator* top = allocator::top_temp; - allocator::create_temp_allocator(allocator, pool_size, buffer_size); + allocator::create_temp_allocator(allocator, pool_size, reserve_size, min_size, realloc_size); defer { allocator::destroy_temp_allocators(); @@ -589,9 +598,19 @@ macro void @pool_init(Allocator allocator, usz pool_size, usz buffer_size; @body } @body(); } -macro void @pool(;@body) @builtin + +<* + Create a new temporary allocator. + + The `reserve` parameter allows you to determine how many bytes should be reserved for + allocations on the current temporary allocator, if allocations are made inside of the pool scope. + It is made available for optimization, and can usually be ignored. + + @param reserve : "The amount of bytes to reserve for out-of-order allocations, 0 gives the default." +*> +macro void @pool(usz reserve = 0; @body) @builtin { - PoolState state = allocator::push_pool() @inline; + PoolState state = allocator::push_pool(reserve) @inline; defer { allocator::pop_pool(state) @inline; diff --git a/lib/std/core/mem_allocator.c3 b/lib/std/core/mem_allocator.c3 index 8cbb23fc4..5deaeb1a6 100644 --- a/lib/std/core/mem_allocator.c3 +++ b/lib/std/core/mem_allocator.c3 @@ -407,13 +407,13 @@ tlocal TempAllocator* top_temp; tlocal bool auto_create_temp = false; usz temp_allocator_min_size = temp_allocator_default_min_size(); -usz temp_allocator_buffer_size = temp_allocator_default_buffer_size(); -usz temp_allocator_new_mult = 4; +usz temp_allocator_reserve_size = temp_allocator_default_reserve_size(); +usz temp_allocator_realloc_size = temp_allocator_default_min_size() * 4; -fn PoolState push_pool() +fn PoolState push_pool(usz reserve = 0) { Allocator old = top_temp ? current_temp : create_temp_allocator_on_demand(); - current_temp = ((TempAllocator*)old).derive_allocator(temp_allocator_min_size, temp_allocator_buffer_size, temp_allocator_new_mult)!!; + current_temp = ((TempAllocator*)old).derive_allocator(reserve)!!; return (PoolState)old.ptr; } @@ -453,7 +453,7 @@ macro usz temp_allocator_default_min_size() @local $endswitch } -macro usz temp_allocator_default_buffer_size() @local +macro usz temp_allocator_default_reserve_size() @local { $switch env::MEMORY_ENV: $case NORMAL: return 1024; @@ -475,14 +475,15 @@ fn Allocator create_temp_allocator_on_demand() @private auto_create_temp = true; abort("Use '@pool_init()' to enable the temp allocator on a new thread. A temp allocator is only implicitly created on the main thread."); } - return create_temp_allocator(temp_base_allocator, temp_allocator_size()); + return create_temp_allocator(temp_base_allocator, temp_allocator_size(), temp_allocator_reserve_size, temp_allocator_min_size, temp_allocator_realloc_size); } + <* @require !top_temp : "This should never be called when temp already exists" *> -fn Allocator create_temp_allocator(Allocator allocator, usz size, usz buffer = temp_allocator_default_buffer_size()) @private +fn Allocator create_temp_allocator(Allocator allocator, usz size, usz reserve, usz min_size, usz realloc_size) @private { - return current_temp = top_temp = allocator::new_temp_allocator(allocator, size)!!; + return current_temp = top_temp = allocator::new_temp_allocator(allocator, size, reserve, min_size, realloc_size)!!; } macro Allocator temp() diff --git a/releasenotes.md b/releasenotes.md index 254205393..bcbfc96a7 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -58,6 +58,7 @@ - Deprecate `String.is_zstr` and `String.quick_zstr` #2188. - Add comparison with `==` for ZString types. - `is_array_or_slice_of_char` and `is_arrayptr_or_slice_of_char` are replaced by constant `@` variants. +- `@pool` now has an optional `reserve` parameter, some minor changes to the temp_allocator API ## 0.7.2 Change list diff --git a/test/test_suite/macros/short_trailing_body.c3t b/test/test_suite/macros/short_trailing_body.c3t index 53b3409ec..ed636606b 100644 --- a/test/test_suite/macros/short_trailing_body.c3t +++ b/test/test_suite/macros/short_trailing_body.c3t @@ -30,7 +30,7 @@ entry: %retparam = alloca i64, align 8 %error_var4 = alloca i64, align 8 %error_var10 = alloca i64, align 8 - %1 = call ptr @std.core.mem.allocator.push_pool() #3 + %1 = call ptr @std.core.mem.allocator.push_pool(i64 0) #3 store ptr %1, ptr %state, align 8 store i32 %0, ptr %taddr, align 4 %2 = insertvalue %any undef, ptr %taddr, 0 diff --git a/test/test_suite/stdlib/map_linux.c3t b/test/test_suite/stdlib/map_linux.c3t index a46e8a7b5..b987bf525 100644 --- a/test/test_suite/stdlib/map_linux.c3t +++ b/test/test_suite/stdlib/map_linux.c3t @@ -231,7 +231,7 @@ after_check18: ; preds = %entry, %after_check %48 = insertvalue %any %47, i64 ptrtoint (ptr @"$ct.sa$double" to i64), 1 store %any %48, ptr %varargslots57, align 16 %49 = call i64 @std.io.printfn(ptr %retparam61, ptr @.str.12, i64 2, ptr %varargslots57, i64 1) - %50 = call ptr @std.core.mem.allocator.push_pool() #4 + %50 = call ptr @std.core.mem.allocator.push_pool(i64 0) #4 store ptr %50, ptr %state, align 8 call void @llvm.memset.p0.i64(ptr align 8 %map3, i8 0, i64 48, i1 false) %lo64 = load i64, ptr @std.core.mem.allocator.thread_allocator, align 8 diff --git a/test/test_suite/stdlib/map_macos.c3t b/test/test_suite/stdlib/map_macos.c3t index 5d613dd84..e16ea24de 100644 --- a/test/test_suite/stdlib/map_macos.c3t +++ b/test/test_suite/stdlib/map_macos.c3t @@ -231,7 +231,7 @@ after_check18: ; preds = %entry, %after_check %48 = insertvalue %any %47, i64 ptrtoint (ptr @"$ct.sa$double" to i64), 1 store %any %48, ptr %varargslots57, align 16 %49 = call i64 @std.io.printfn(ptr %retparam61, ptr @.str.12, i64 2, ptr %varargslots57, i64 1) - %50 = call ptr @std.core.mem.allocator.push_pool() #4 + %50 = call ptr @std.core.mem.allocator.push_pool(i64 0) #4 store ptr %50, ptr %state, align 8 call void @llvm.memset.p0.i64(ptr align 8 %map3, i8 0, i64 48, i1 false) %lo64 = load i64, ptr @std.core.mem.allocator.thread_allocator, align 8