compiler/
kernel/
lib/
examples/
std/
arch/
char/
collections/
lang/
alloc/
ast/
gen/
il/
module/
parser/
resolver/
scanner/
alloc.rad
5.3 KiB
ast.rad
23.6 KiB
gen.rad
513 B
il.rad
17.0 KiB
lower.rad
278.5 KiB
module.rad
13.5 KiB
package.rad
1.3 KiB
parser.rad
79.6 KiB
resolver.rad
329.1 KiB
scanner.rad
17.6 KiB
sexpr.rad
6.4 KiB
strings.rad
2.2 KiB
types.rad
286 B
sys/
arch.rad
68 B
char.rad
855 B
collections.rad
39 B
fmt.rad
8.3 KiB
intrinsics.rad
467 B
io.rad
1.7 KiB
lang.rad
276 B
mem.rad
2.3 KiB
sys.rad
179 B
testing.rad
2.4 KiB
tests.rad
15.7 KiB
vec.rad
3.2 KiB
std.rad
281 B
scripts/
seed/
sublime/
test/
vim/
.gitignore
336 B
.gitsigners
112 B
CONTRIBUTING
2.1 KiB
LICENSE
1.1 KiB
Makefile
10.2 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.5 KiB
std.lib.test
551 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 | export union AllocError: Copy { |
| 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 | export 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 | export 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 | export fn alloc(arena: &mut Arena, size: u32, alignment: u32) -> *mut opaque throws (AllocError) { |
| 39 | assert alignment > 0; |
| 40 | assert size > 0; |
| 41 | |
| 42 | let aligned64 = (arena.offset as u64 + alignment as u64 - 1) & ~(alignment as u64 - 1); |
| 43 | if aligned64 > arena.data.len as u64 or size as u64 > arena.data.len as u64 - aligned64 { |
| 44 | throw AllocError::OutOfMemory; |
| 45 | } |
| 46 | let aligned = aligned64 as u32; |
| 47 | let newOffset = aligned + size; |
| 48 | |
| 49 | let base: *mut u8 = &mut arena.data[aligned]; |
| 50 | set arena.offset = newOffset; |
| 51 | |
| 52 | return base as *mut opaque; |
| 53 | } |
| 54 | |
| 55 | /// Reset the arena, allowing all memory to be reused. |
| 56 | /// |
| 57 | /// Does not zero the memory. |
| 58 | export fn reset(arena: &mut Arena) { |
| 59 | set arena.offset = 0; |
| 60 | } |
| 61 | |
| 62 | /// Save the current arena state for later restoration. |
| 63 | export fn save(arena: &Arena) -> u32 { |
| 64 | return arena.offset; |
| 65 | } |
| 66 | |
| 67 | /// Restore the arena to a previously saved state, reclaiming all |
| 68 | /// allocations made since that point. |
| 69 | export fn restore(arena: &mut Arena, savedOffset: u32) { |
| 70 | set arena.offset = savedOffset; |
| 71 | } |
| 72 | |
| 73 | /// Returns the number of bytes currently allocated. |
| 74 | export fn used(arena: &Arena) -> u32 { |
| 75 | return arena.offset; |
| 76 | } |
| 77 | |
| 78 | /// Returns the number of bytes remaining in the arena. |
| 79 | export fn remaining(arena: &Arena) -> u32 { |
| 80 | return arena.data.len as u32 - arena.offset; |
| 81 | } |
| 82 | |
| 83 | /// Returns the remaining buffer as a mutable slice. |
| 84 | export fn remainingBuf(arena: &mut Arena) -> *mut [u8] { |
| 85 | return &mut arena.data[arena.offset..]; |
| 86 | } |
| 87 | |
| 88 | /// Commits `size` bytes of allocation, advancing the offset. |
| 89 | /// Use after writing to the buffer returned by [`remainingBuf`]. |
| 90 | export fn commit(arena: &mut Arena, size: u32) { |
| 91 | set arena.offset += size; |
| 92 | } |
| 93 | |
| 94 | /// Allocate a slice of `count` elements, each of `size` bytes with given alignment. |
| 95 | /// |
| 96 | /// Returns a type-erased slice that should be cast to the appropriate `*[T]`. |
| 97 | /// The slice length is set to `count` (element count, not bytes). |
| 98 | /// Throws `AllocError` if the arena is exhausted. |
| 99 | export unsafe fn allocSlice(arena: &mut Arena, size: u32, alignment: u32, count: u32) -> *mut [opaque] throws (AllocError) { |
| 100 | if count == 0 { |
| 101 | return &mut []; |
| 102 | } |
| 103 | if size > 0xffffffff / count { throw AllocError::OutOfMemory; } |
| 104 | let ptr = try alloc(arena, size * count, alignment); |
| 105 | |
| 106 | return @sliceOf(ptr, count); |
| 107 | } |
| 108 | |
| 109 | /// Generic allocator interface. |
| 110 | /// |
| 111 | /// Bundles an allocation function with an opaque context pointer so that |
| 112 | /// any allocation strategy (arena, free-list, mmap, pool) can be used |
| 113 | /// through a uniform interface. The `func` field is called with the context |
| 114 | /// pointer, a byte size and an alignment, and must return a pointer to |
| 115 | /// the allocated memory or panic on failure. |
| 116 | export record Allocator: Copy { |
| 117 | /// Allocation function. Returns a pointer to `size` bytes |
| 118 | /// aligned to `alignment`, or panics on failure. |
| 119 | func: unsafe fn(*unsafe mut opaque, u32, u32) -> *mut opaque, |
| 120 | /// Opaque context pointer passed to `func`. |
| 121 | ctx: *unsafe mut opaque, |
| 122 | } |
| 123 | |
| 124 | /// Create an allocator whose arena must outlive all uses of the allocator. |
| 125 | export unsafe fn arenaAllocator(arena: &mut Arena) -> Allocator { |
| 126 | return Allocator { |
| 127 | func: arenaAllocFn, |
| 128 | ctx: (arena as *unsafe mut Arena) as *unsafe mut opaque, |
| 129 | }; |
| 130 | } |
| 131 | |
| 132 | /// Arena allocation function conforming to the `Allocator` interface. |
| 133 | unsafe fn arenaAllocFn(ctx: *unsafe mut opaque, size: u32, alignment: u32) -> *mut opaque { |
| 134 | let arena = ctx as *unsafe mut Arena; |
| 135 | return try! alloc(arena, size, alignment); |
| 136 | } |
| 137 | |
| 138 | /// Allocate raw storage that remains valid until the arena is reset. |
| 139 | export unsafe fn allocRaw(arena: &mut Arena, size: u32, alignment: u32) -> *unsafe mut opaque throws (AllocError) { |
| 140 | let owner = try alloc(arena, size, alignment); |
| 141 | let byte = owner as *mut u8; |
| 142 | let raw: *unsafe mut u8 = &mut *byte; |
| 143 | return raw as *unsafe mut opaque; |
| 144 | } |
| 145 | |
| 146 | /// Allocate a raw slice that remains valid until the arena is reset. |
| 147 | export unsafe fn allocRawSlice(arena: &mut Arena, size: u32, alignment: u32, count: u32) -> *unsafe mut [opaque] throws (AllocError) { |
| 148 | if count == 0 { |
| 149 | return &mut []; |
| 150 | } |
| 151 | let raw = try allocRaw(arena, size * count, alignment); |
| 152 | return @sliceOf(raw, count); |
| 153 | } |