diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/zisp/gc/meta_alloc.zig | 39 |
1 files changed, 32 insertions, 7 deletions
diff --git a/src/zisp/gc/meta_alloc.zig b/src/zisp/gc/meta_alloc.zig index 28494ee..e8502df 100644 --- a/src/zisp/gc/meta_alloc.zig +++ b/src/zisp/gc/meta_alloc.zig @@ -18,9 +18,13 @@ // that this means the slabs are limited to 16 GiB each (2^31 units because i32; // 8 bytes each) for a 256 GiB total, since we have 16 size classes. // -// Using 8-byte units means free conversion to byte-based addresses thanks to -// instructions like LEA or the ARM barrel shifter. Using size class sized -// units would require additional shift instructions and seems unnecessary. +// Using 8-byte units means that many (but not all) of the conversions to byte +// based addresses are effectively free thanks to instructions like LEA or the +// ARM barrel shifter. Using size class sized units would only require a few +// more shift instructions here and there, but seems unnecessary. +// +// Downsizing units to one byte would shave off an instruction here and there, +// and decrease vmem to 32 GiB (2 per slab), but that may be too restrictive. // // The global slab watermark of a size class is bumped in chunks, reserving a // number of slots for the thread that performed the bump. This way, threads @@ -204,7 +208,7 @@ export fn alloc(size: usize) [*]u8 { } } -/// Free memory that was returned by alloc(). +/// Free memory of a specific size that was returned by alloc(). export fn free(size: usize, ptr: [*]u8) void { const sci = getSizeClassIndex(size); if (sci < SIZES.len) { @@ -215,6 +219,27 @@ export fn free(size: usize, ptr: [*]u8) void { } } +/// Free memory that was returned by alloc(), calculating the size class from +/// the pointer address, which is only possible if it lies within one of the +/// managed slabs. If this pointer came from a call to alloc() with a size +/// greater than the largest size class, this function will panic. +export fn free_auto(ptr: [*]u8) void { + const paddr = @intFromPtr(ptr); + const saddr = @intFromPtr(slabs); + if (paddr < saddr) { + @panic("Invalid pointer passed to free_auto()."); + } + + const uaddr = paddr - saddr; + const uidx = uaddr / @sizeOf(Unit); + const sci = uidx / SLAB_LEN; + if (sci >= SIZES.len) { + @panic("Invalid pointer passed to free_auto()."); + } + + free_size_class(@intCast(sci), @ptrCast(@alignCast(ptr))); +} + fn alloc_size_class(sci: u8, size: usize) SlotPtr { std.debug.assert(sci < SIZES.len); @@ -260,10 +285,10 @@ fn alloc_from_fl( if (fl_head.idx >= SLAB_LEN) return null; ptr = slab[fl_head.idx..].ptr; - arr = @ptrCast(ptr); + arr = @ptrCast(@alignCast(ptr)); // Load this atomically since it's still globally accessible memory; - // unordered is fine since we discard it if the following CAS fails. + // unordered is fine since we discard it if the cmpxchg below fails. const next_head = @atomicLoad(UnitIdx, &arr[0], .unordered); fl_head = @cmpxchgWeak( @@ -342,7 +367,7 @@ fn free_into_fl( // Note: n = 0 is valid and must work. std.debug.assert(n <= FSC_MAX / 2); - const arr: [*]UnitIdx = @ptrCast(ptr); + const arr: [*]UnitIdx = @ptrCast(@alignCast(ptr)); const split = tl.fsc_count - n; tl.fsc_count = split; |
