compiler/
lib/
examples/
std/
arch/
char/
collections/
lang/
alloc/
ast/
gen/
il/
module/
parser/
resolver/
scanner/
alloc.rad
5.4 KiB
ast.rad
23.1 KiB
gen.rad
507 B
il.rad
15.3 KiB
lower.rad
271.6 KiB
module.rad
13.5 KiB
package.rad
1.2 KiB
parser.rad
78.7 KiB
resolver.rad
314.0 KiB
scanner.rad
17.4 KiB
sexpr.rad
6.3 KiB
strings.rad
2.2 KiB
types.rad
280 B
sys/
arch.rad
68 B
char.rad
855 B
collections.rad
39 B
fmt.rad
8.1 KiB
intrinsics.rad
683 B
io.rad
1.4 KiB
lang.rad
360 B
mem.rad
2.2 KiB
sys.rad
173 B
testing.rad
2.4 KiB
tests.rad
15.4 KiB
vec.rad
4.8 KiB
std.rad
358 B
scripts/
seed/
sublime/
test/
vim/
.gitignore
336 B
.gitsigners
112 B
LICENSE
1.1 KiB
Makefile
3.7 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.2 KiB
std.lib.test
373 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 | /// Largest byte extent representable by allocator APIs. |
| 10 | constant MAX_EXTENT: u32 = 0xFFFFFFFF; |
| 11 | |
| 12 | /// Error thrown by allocator. |
| 13 | export union AllocError { |
| 14 | /// Allocator is out of memory. |
| 15 | OutOfMemory, |
| 16 | } |
| 17 | |
| 18 | /// Bump allocator backed by a byte slice. |
| 19 | /// |
| 20 | /// Allocations are made by advancing an offset pointer. Individual allocations |
| 21 | /// cannot be freed; instead, the entire arena is reset at once via [`reset`]. |
| 22 | export record Arena { |
| 23 | /// Backing storage. |
| 24 | data: *mut [u8], |
| 25 | /// Current allocation offset in bytes. |
| 26 | offset: u32, |
| 27 | } |
| 28 | |
| 29 | /// Create a new arena backed by the given byte slice. |
| 30 | export fn new(data: *mut [u8]) -> Arena { |
| 31 | return Arena { data, offset: 0 }; |
| 32 | } |
| 33 | |
| 34 | /// Allocate `size` bytes with the given alignment. |
| 35 | /// |
| 36 | /// `size` must be nonzero and `alignment` must be a nonzero power of two. |
| 37 | /// Returns an opaque pointer to the allocated memory. Throws `AllocError` if |
| 38 | /// the arena is exhausted or its offset lies beyond the backing storage. The |
| 39 | /// caller is responsible for casting to the appropriate type and initializing |
| 40 | /// the memory. |
| 41 | export fn alloc(arena: *mut Arena, size: u32, alignment: u32) -> *mut opaque throws (AllocError) { |
| 42 | assert alignment > 0; |
| 43 | assert (alignment & (alignment - 1)) == 0; |
| 44 | assert size > 0; |
| 45 | |
| 46 | let capacity = arena.data.len as u32; |
| 47 | if arena.offset > capacity { |
| 48 | throw AllocError::OutOfMemory; |
| 49 | } |
| 50 | let available = capacity - arena.offset; |
| 51 | let padding = (0 - arena.offset) & (alignment - 1); |
| 52 | if padding > available { |
| 53 | throw AllocError::OutOfMemory; |
| 54 | } |
| 55 | let alignedAvailable = available - padding; |
| 56 | if size > alignedAvailable { |
| 57 | throw AllocError::OutOfMemory; |
| 58 | } |
| 59 | let aligned = arena.offset + padding; |
| 60 | let newOffset = aligned + size; |
| 61 | let base: *mut u8 = &mut arena.data[aligned]; |
| 62 | set arena.offset = newOffset; |
| 63 | |
| 64 | return base as *mut opaque; |
| 65 | } |
| 66 | |
| 67 | /// Reset the arena, allowing all memory to be reused. |
| 68 | /// |
| 69 | /// The caller must not use any allocation from this arena after the reset. |
| 70 | /// Does not zero the memory. |
| 71 | export unsafe fn reset(arena: *mut Arena) { |
| 72 | set arena.offset = 0; |
| 73 | } |
| 74 | |
| 75 | /// Save the current arena state for later restoration. |
| 76 | export fn save(arena: *Arena) -> u32 { |
| 77 | return arena.offset; |
| 78 | } |
| 79 | |
| 80 | /// Restore the arena to a previously saved state, reclaiming all |
| 81 | /// allocations made since that point. |
| 82 | /// |
| 83 | /// The caller must not use an allocation reclaimed by this operation. |
| 84 | export unsafe fn restore(arena: *mut Arena, savedOffset: u32) { |
| 85 | set arena.offset = savedOffset; |
| 86 | } |
| 87 | |
| 88 | /// Returns the number of bytes currently allocated. |
| 89 | export fn used(arena: *Arena) -> u32 { |
| 90 | return arena.offset; |
| 91 | } |
| 92 | |
| 93 | /// Returns the number of bytes remaining in the arena. |
| 94 | export fn remaining(arena: *Arena) -> u32 { |
| 95 | return arena.data.len as u32 - arena.offset; |
| 96 | } |
| 97 | |
| 98 | /// Return an exclusive view of the remaining buffer. |
| 99 | /// |
| 100 | /// The caller must end access through the view before another arena operation. |
| 101 | export unsafe fn remainingBuf(arena: *mut Arena) -> *mut [u8] { |
| 102 | return &mut arena.data[arena.offset..]; |
| 103 | } |
| 104 | |
| 105 | /// Commit `size` bytes of allocation and advance the offset. |
| 106 | /// |
| 107 | /// The caller must provide a size within the view returned by [`remainingBuf`] |
| 108 | /// and must end access through that view before another arena operation. |
| 109 | export unsafe fn commit(arena: *mut Arena, size: u32) { |
| 110 | assert size <= remaining(arena), "alloc::commit: size exceeds remaining capacity"; |
| 111 | set arena.offset += size; |
| 112 | } |
| 113 | |
| 114 | /// Allocate a slice of `count` elements, each of `size` bytes with given alignment. |
| 115 | /// |
| 116 | /// Returns a type-erased slice that should be cast to the appropriate `*[T]`. |
| 117 | /// The slice length is set to `count` (element count, not bytes). |
| 118 | /// Throws `AllocError` if the arena is exhausted or the total byte extent |
| 119 | /// cannot be represented by `u32`. |
| 120 | export fn allocSlice(arena: *mut Arena, size: u32, alignment: u32, count: u32) -> *mut [opaque] throws (AllocError) { |
| 121 | if count == 0 { |
| 122 | return &mut []; |
| 123 | } |
| 124 | if size > MAX_EXTENT / count { |
| 125 | throw AllocError::OutOfMemory; |
| 126 | } |
| 127 | let ptr = try alloc(arena, size * count, alignment); |
| 128 | |
| 129 | return @sliceOf(ptr, count); |
| 130 | } |
| 131 | |
| 132 | /// Generic allocator interface. |
| 133 | /// |
| 134 | /// Bundles an allocation function with an opaque context pointer so that |
| 135 | /// any allocation strategy (arena, free-list, mmap, pool) can be used |
| 136 | /// through a uniform interface. The `func` field is called with the context |
| 137 | /// pointer, a byte size and an alignment, and must return a pointer to |
| 138 | /// the allocated memory or panic on failure. |
| 139 | export record Allocator { |
| 140 | /// Allocation function. Returns a pointer to `size` bytes |
| 141 | /// aligned to `alignment`, or panics on failure. |
| 142 | func: fn(*mut opaque, u32, u32) -> *mut opaque, |
| 143 | /// Opaque context pointer passed to `func`. |
| 144 | ctx: *mut opaque, |
| 145 | } |
| 146 | |
| 147 | /// Create an `Allocator` backed by an `Arena`. |
| 148 | export fn arenaAllocator(arena: *mut Arena) -> Allocator { |
| 149 | return Allocator { |
| 150 | func: arenaAllocFn, |
| 151 | ctx: arena as *mut opaque, |
| 152 | }; |
| 153 | } |
| 154 | |
| 155 | /// Arena allocation function conforming to the `Allocator` interface. |
| 156 | fn arenaAllocFn(ctx: *mut opaque, size: u32, alignment: u32) -> *mut opaque { |
| 157 | let arena = ctx as *mut Arena; |
| 158 | return try! alloc(arena, size, alignment); |
| 159 | } |