mirror of
https://github.com/odin-lang/Odin.git
synced 2026-03-08 09:35:36 +00:00
Temporary allocator for context
This commit is contained in:
@@ -159,6 +159,24 @@ aprintf :: proc(fmt: string, args: ..any) -> string {
|
||||
}
|
||||
|
||||
|
||||
// tprint* procedures return a string that was allocated with the current context's temporary allocator
|
||||
tprint :: proc(args: ..any) -> string {
|
||||
buf := String_Buffer(make([dynamic]byte, context.temp_allocator));
|
||||
sbprint(&buf, ..args);
|
||||
return to_string(buf);
|
||||
}
|
||||
tprintln :: proc(args: ..any) -> string {
|
||||
buf := String_Buffer(make([dynamic]byte, context.temp_allocator));
|
||||
sbprintln(&buf, ..args);
|
||||
return to_string(buf);
|
||||
}
|
||||
tprintf :: proc(fmt: string, args: ..any) -> string {
|
||||
buf := String_Buffer(make([dynamic]byte, context.temp_allocator));
|
||||
sbprintf(&buf, fmt, ..args);
|
||||
return to_string(buf);
|
||||
}
|
||||
|
||||
|
||||
// bprint* procedures return a string using a buffer from an array
|
||||
bprint :: proc(buf: []byte, args: ..any) -> string {
|
||||
sb := string_buffer_from_slice(buf[0:len(buf)]);
|
||||
|
||||
@@ -159,6 +159,84 @@ nil_allocator :: proc() -> Allocator {
|
||||
};
|
||||
}
|
||||
|
||||
Scratch_Allocator :: struct {
|
||||
data: []byte,
|
||||
curr_offset: int,
|
||||
prev_offset: int,
|
||||
backup_allocator: Allocator,
|
||||
}
|
||||
|
||||
scratch_allocator_init :: proc(scratch: ^Scratch_Allocator, data: []byte, backup_allocator := context.allocator) {
|
||||
scratch.data = data;
|
||||
scratch.curr_offset = 0;
|
||||
scratch.prev_offset = 0;
|
||||
scratch.backup_allocator = backup_allocator;
|
||||
}
|
||||
|
||||
scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
|
||||
scratch := (^Scratch_Allocator)(allocator_data);
|
||||
|
||||
switch mode {
|
||||
case Allocator_Mode.Alloc:
|
||||
switch {
|
||||
case scratch.curr_offset+size <= len(scratch.data):
|
||||
offset := align_forward_uintptr(uintptr(scratch.curr_offset), uintptr(alignment));
|
||||
ptr := &scratch.data[offset];
|
||||
zero(ptr, size);
|
||||
scratch.prev_offset = int(offset);
|
||||
scratch.curr_offset = int(offset) + size;
|
||||
return ptr;
|
||||
case size <= len(scratch.data):
|
||||
offset := align_forward_uintptr(uintptr(0), uintptr(alignment));
|
||||
ptr := &scratch.data[offset];
|
||||
zero(ptr, size);
|
||||
scratch.prev_offset = int(offset);
|
||||
scratch.curr_offset = int(offset) + size;
|
||||
return ptr;
|
||||
}
|
||||
// TODO(bill): Should leaks be notified about? Should probably use a logging system that is built into the context system
|
||||
a := scratch.backup_allocator;
|
||||
if a.procedure == nil {
|
||||
a = context.allocator;
|
||||
}
|
||||
return alloc(size, alignment, a, loc);
|
||||
|
||||
case Allocator_Mode.Free:
|
||||
last_ptr := rawptr(&scratch.data[scratch.prev_offset]);
|
||||
if old_memory == last_ptr {
|
||||
size := scratch.curr_offset - scratch.prev_offset;
|
||||
scratch.curr_offset = scratch.prev_offset;
|
||||
zero(last_ptr, size);
|
||||
return nil;
|
||||
}
|
||||
// NOTE(bill): It's scratch memory, don't worry about freeing
|
||||
|
||||
case Allocator_Mode.Free_All:
|
||||
scratch.curr_offset = 0;
|
||||
scratch.prev_offset = 0;
|
||||
|
||||
case Allocator_Mode.Resize:
|
||||
last_ptr := rawptr(&scratch.data[scratch.prev_offset]);
|
||||
if old_memory == last_ptr && len(scratch.data)-scratch.prev_offset >= size {
|
||||
scratch.curr_offset = scratch.prev_offset+size;
|
||||
return old_memory;
|
||||
}
|
||||
return scratch_allocator_proc(allocator_data, Allocator_Mode.Alloc, size, alignment, old_memory, old_size, flags, loc);
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
scratch_allocator :: proc(scratch: ^Scratch_Allocator) -> Allocator {
|
||||
return Allocator{
|
||||
procedure = scratch_allocator_proc,
|
||||
data = scratch,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -10,8 +10,9 @@ foreign _ {
|
||||
swap :: proc[swap16, swap32, swap64];
|
||||
|
||||
|
||||
set :: proc "contextless" (data: rawptr, value: i32, len: int) -> rawptr {
|
||||
set :: proc "contextless" (data: rawptr, value: byte, len: int) -> rawptr {
|
||||
if data == nil do return nil;
|
||||
if len < 0 do return data;
|
||||
foreign _ {
|
||||
when size_of(rawptr) == 8 {
|
||||
@(link_name="llvm.memset.p0i8.i64")
|
||||
@@ -138,6 +139,16 @@ align_forward :: proc(ptr: rawptr, align: uintptr) -> rawptr {
|
||||
return rawptr(p);
|
||||
}
|
||||
|
||||
align_forward_uintptr :: proc(ptr, align: uintptr) -> uintptr {
|
||||
assert(is_power_of_two(align));
|
||||
|
||||
a := uintptr(align);
|
||||
p := uintptr(ptr);
|
||||
modulo := p & (a-1);
|
||||
if modulo != 0 do p += a - modulo;
|
||||
return uintptr(p);
|
||||
}
|
||||
|
||||
|
||||
|
||||
AllocationHeader :: struct {size: int};
|
||||
@@ -181,7 +192,7 @@ Arena :: struct {
|
||||
temp_count: int,
|
||||
}
|
||||
|
||||
ArenaTempMemory :: struct {
|
||||
Arena_Temp_Memory :: struct {
|
||||
arena: ^Arena,
|
||||
original_count: int,
|
||||
}
|
||||
@@ -249,7 +260,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
case Free:
|
||||
// NOTE(bill): Free all at once
|
||||
// Use ArenaTempMemory if you want to free a block
|
||||
// Use Arena_Temp_Memory if you want to free a block
|
||||
|
||||
case Free_All:
|
||||
(^Raw_Slice)(&arena.memory).len = 0;
|
||||
@@ -261,15 +272,15 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return nil;
|
||||
}
|
||||
|
||||
begin_arena_temp_memory :: proc(a: ^Arena) -> ArenaTempMemory {
|
||||
tmp: ArenaTempMemory;
|
||||
begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory {
|
||||
tmp: Arena_Temp_Memory;
|
||||
tmp.arena = a;
|
||||
tmp.original_count = len(a.memory);
|
||||
a.temp_count += 1;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
end_arena_temp_memory :: proc(using tmp: ArenaTempMemory) {
|
||||
end_arena_temp_memory :: proc(using tmp: Arena_Temp_Memory) {
|
||||
assert(len(arena.memory) >= original_count);
|
||||
assert(arena.temp_count > 0);
|
||||
(^Raw_Dynamic_Array)(&arena.memory).len = original_count;
|
||||
|
||||
@@ -184,7 +184,8 @@ Source_Code_Location :: struct {
|
||||
}
|
||||
|
||||
Context :: struct {
|
||||
allocator: mem.Allocator,
|
||||
allocator: mem.Allocator,
|
||||
temp_allocator: mem.Allocator,
|
||||
thread_id: int,
|
||||
|
||||
user_data: any,
|
||||
@@ -194,6 +195,8 @@ Context :: struct {
|
||||
derived: any, // May be used for derived data types
|
||||
}
|
||||
|
||||
global_scratch_allocator_data: mem.Scratch_Allocator;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -315,9 +318,15 @@ __init_context :: proc "contextless" (c: ^Context) {
|
||||
if c == nil do return;
|
||||
|
||||
c.allocator = os.heap_allocator();
|
||||
c.temp_allocator = mem.scratch_allocator(&global_scratch_allocator_data);
|
||||
c.thread_id = os.current_thread_id();
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
init_global_temporary_allocator :: proc(data: []byte, backup_allocator := context.allocator) {
|
||||
mem.scratch_allocator_init(&global_scratch_allocator_data, data, backup_allocator);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(builtin)
|
||||
|
||||
@@ -489,12 +489,15 @@ threading_example :: proc() {
|
||||
when os.OS == "windows" {
|
||||
fmt.println("# threading_example");
|
||||
|
||||
unordered_remove :: proc(array: ^[dynamic]$T, index: int, loc := #caller_location) {
|
||||
unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) {
|
||||
runtime.bounds_check_error_loc(loc, index, len(array));
|
||||
array[index] = array[len(array)-1];
|
||||
n := len(array)-1;
|
||||
if index != n {
|
||||
array[index] = array[n];
|
||||
}
|
||||
pop(array);
|
||||
}
|
||||
ordered_remove :: proc(array: ^[dynamic]$T, index: int, loc := #caller_location) {
|
||||
ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) {
|
||||
runtime.bounds_check_error_loc(loc, index, len(array));
|
||||
copy(array[index:], array[index+1:]);
|
||||
pop(array);
|
||||
|
||||
@@ -1309,6 +1309,7 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
|
||||
|
||||
str_lit("args__"),
|
||||
str_lit("type_table"),
|
||||
str_lit("global_scratch_allocator"),
|
||||
|
||||
str_lit("Type_Info"),
|
||||
str_lit("Source_Code_Location"),
|
||||
|
||||
19
src/ir.cpp
19
src/ir.cpp
@@ -1658,11 +1658,22 @@ irValue *ir_find_or_generate_context_ptr(irProcedure *proc) {
|
||||
ir_push_context_onto_stack(proc, c);
|
||||
ir_emit_store(proc, c, ir_emit_load(proc, proc->module->global_default_context));
|
||||
|
||||
irValue *ep = ir_emit_struct_ep(proc, c, 0);
|
||||
Array<irValue *> args = {};
|
||||
irValue *v = ir_emit_package_call(proc, "os", "heap_allocator", args);
|
||||
ir_emit_store(proc, ep, v);
|
||||
|
||||
#if 1
|
||||
Array<irValue *> args = {};
|
||||
ir_emit_store(proc, ir_emit_struct_ep(proc, c, 0), ir_emit_package_call(proc, "os", "heap_allocator", args));
|
||||
ir_emit_store(proc, ir_emit_struct_ep(proc, c, 2), ir_emit_package_call(proc, "os", "current_thread_id", args));
|
||||
|
||||
array_init(&args, heap_allocator(), 1);
|
||||
AstPackage *rt_pkg = get_core_package(proc->module->info, str_lit("runtime"));
|
||||
Entity *e = scope_lookup_current(rt_pkg->scope, str_lit("global_scratch_allocator_data"));
|
||||
irValue **found = map_get(&proc->module->values, hash_entity(e));
|
||||
GB_ASSERT_MSG(found != nullptr, "%.*s", LIT(e->token.string));
|
||||
args[0] = *found;
|
||||
ir_emit_store(proc, ir_emit_struct_ep(proc, c, 1), ir_emit_package_call(proc, "mem", "scratch_allocator", args));
|
||||
#else
|
||||
ir_emit_init_context(proc, c);
|
||||
#endif
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user