compiler/
lib/
examples/
std/
arch/
collections/
lang/
alloc/
ast/
gen/
il/
module/
parser/
resolver/
scanner/
alloc.rad
4.2 KiB
ast.rad
22.4 KiB
gen.rad
489 B
il.rad
15.1 KiB
lower.rad
259.5 KiB
module.rad
13.4 KiB
package.rad
1.2 KiB
parser.rad
78.5 KiB
resolver.rad
244.3 KiB
scanner.rad
18.1 KiB
sexpr.rad
6.3 KiB
strings.rad
2.2 KiB
sys/
arch.rad
65 B
collections.rad
36 B
fmt.rad
3.8 KiB
intrinsics.rad
399 B
io.rad
1.2 KiB
lang.rad
222 B
mem.rad
2.1 KiB
sys.rad
167 B
testing.rad
2.3 KiB
tests.rad
11.6 KiB
vec.rad
3.1 KiB
std.rad
231 B
scripts/
seed/
test/
vim/
.gitignore
353 B
.gitsigners
112 B
LICENSE
1.1 KiB
Makefile
3.0 KiB
README
2.5 KiB
std.lib
1.0 KiB
std.lib.test
252 B
lib/std/lang/alloc.rad
raw
| 1 | //! Bump allocator for compiler data structures. |
| 2 | //! |
| 3 | //! Provides a simple arena-style allocator that allocates from a contiguous |
| 4 | //! byte buffer. Memory is never freed individually - the entire arena is |
| 5 | //! reset at once. This is ideal for compiler passes where all allocations |
| 6 | //! have the same lifetime. |
| 7 | @test mod tests; |
| 8 | |
| 9 | use std::mem; |
| 10 | |
| 11 | /// Error thrown by allocator. |
| 12 | pub union AllocError { |
| 13 | /// Allocator is out of memory. |
| 14 | OutOfMemory, |
| 15 | } |
| 16 | |
| 17 | /// Bump allocator backed by a byte slice. |
| 18 | /// |
| 19 | /// Allocations are made by advancing an offset pointer. Individual allocations |
| 20 | /// cannot be freed; instead, the entire arena is reset at once via [`reset`]. |
| 21 | pub record Arena { |
| 22 | /// Backing storage. |
| 23 | data: *mut [u8], |
| 24 | /// Current allocation offset in bytes. |
| 25 | offset: u32, |
| 26 | } |
| 27 | |
| 28 | /// Create a new arena backed by the given byte slice. |
| 29 | pub fn new(data: *mut [u8]) -> Arena { |
| 30 | return Arena { data, offset: 0 }; |
| 31 | } |
| 32 | |
| 33 | /// Allocate `size` bytes with the given alignment. |
| 34 | /// |
| 35 | /// Returns an opaque pointer to the allocated memory. Throws `AllocError` if |
| 36 | /// the arena is exhausted. The caller is responsible for casting to the |
| 37 | /// appropriate type and initializing the memory. |
| 38 | pub fn alloc(arena: *mut Arena, size: u32, alignment: u32) -> *mut opaque throws (AllocError) { |
| 39 | assert alignment > 0; |
| 40 | assert size > 0; |
| 41 | |
| 42 | let aligned = mem::alignUp(arena.offset, alignment); |
| 43 | let newOffset = aligned + size; |
| 44 | |
| 45 | if newOffset > arena.data.len as u32 { |
| 46 | throw AllocError::OutOfMemory; |
| 47 | } |
| 48 | let base: *mut u8 = &mut arena.data[aligned]; |
| 49 | arena.offset = newOffset; |
| 50 | |
| 51 | return base as *mut opaque; |
| 52 | } |
| 53 | |
| 54 | /// Reset the arena, allowing all memory to be reused. |
| 55 | /// |
| 56 | /// Does not zero the memory. |
| 57 | pub fn reset(arena: *mut Arena) { |
| 58 | arena.offset = 0; |
| 59 | } |
| 60 | |
| 61 | /// Save the current arena state for later restoration. |
| 62 | pub fn save(arena: *Arena) -> u32 { |
| 63 | return arena.offset; |
| 64 | } |
| 65 | |
| 66 | /// Restore the arena to a previously saved state, reclaiming all |
| 67 | /// allocations made since that point. |
| 68 | pub fn restore(arena: *mut Arena, savedOffset: u32) { |
| 69 | arena.offset = savedOffset; |
| 70 | } |
| 71 | |
| 72 | /// Returns the number of bytes currently allocated. |
| 73 | pub fn used(arena: *Arena) -> u32 { |
| 74 | return arena.offset; |
| 75 | } |
| 76 | |
| 77 | /// Returns the number of bytes remaining in the arena. |
| 78 | pub fn remaining(arena: *Arena) -> u32 { |
| 79 | return arena.data.len as u32 - arena.offset; |
| 80 | } |
| 81 | |
| 82 | /// Returns the remaining buffer as a mutable slice. |
| 83 | pub fn remainingBuf(arena: *mut Arena) -> *mut [u8] { |
| 84 | return &mut arena.data[arena.offset..]; |
| 85 | } |
| 86 | |
| 87 | /// Commits `size` bytes of allocation, advancing the offset. |
| 88 | /// Use after writing to the buffer returned by [`remainingBuf`]. |
| 89 | pub fn commit(arena: *mut Arena, size: u32) { |
| 90 | arena.offset += size; |
| 91 | } |
| 92 | |
| 93 | /// Allocate a slice of `count` elements, each of `size` bytes with given alignment. |
| 94 | /// |
| 95 | /// Returns a type-erased slice that should be cast to the appropriate `*[T]`. |
| 96 | /// The slice length is set to `count` (element count, not bytes). |
| 97 | /// Throws `AllocError` if the arena is exhausted. |
| 98 | pub fn allocSlice(arena: *mut Arena, size: u32, alignment: u32, count: u32) -> *mut [opaque] throws (AllocError) { |
| 99 | if count == 0 { |
| 100 | return &mut []; |
| 101 | } |
| 102 | let ptr = try alloc(arena, size * count, alignment); |
| 103 | |
| 104 | return @sliceOf(ptr, count); |
| 105 | } |
| 106 | |
| 107 | /// Generic allocator interface. |
| 108 | /// |
| 109 | /// Bundles an allocation function with an opaque context pointer so that |
| 110 | /// any allocation strategy (arena, free-list, mmap, pool) can be used |
| 111 | /// through a uniform interface. The `func` field is called with the context |
| 112 | /// pointer, a byte size and an alignment, and must return a pointer to |
| 113 | /// the allocated memory or panic on failure. |
| 114 | pub record Allocator { |
| 115 | /// Allocation function. Returns a pointer to `size` bytes |
| 116 | /// aligned to `alignment`, or panics on failure. |
| 117 | func: fn(*mut opaque, u32, u32) -> *mut opaque, |
| 118 | /// Opaque context pointer passed to `func`. |
| 119 | ctx: *mut opaque, |
| 120 | } |
| 121 | |
| 122 | /// Create an `Allocator` backed by an `Arena`. |
| 123 | pub fn arenaAllocator(arena: *mut Arena) -> Allocator { |
| 124 | return Allocator { |
| 125 | func: arenaAllocFn, |
| 126 | ctx: arena as *mut opaque, |
| 127 | }; |
| 128 | } |
| 129 | |
| 130 | /// Arena allocation function conforming to the `Allocator` interface. |
| 131 | fn arenaAllocFn(ctx: *mut opaque, size: u32, alignment: u32) -> *mut opaque { |
| 132 | let arena = ctx as *mut Arena; |
| 133 | return try! alloc(arena, size, alignment); |
| 134 | } |