Browse Source

Temporary allocator for `context`

gingerBill 7 years ago
parent
commit
001837e6bb
7 changed files with 144 additions and 13 deletions
  1. 18 0
      core/fmt/fmt.odin
  2. 78 0
      core/mem/alloc.odin
  3. 17 6
      core/mem/mem.odin
  4. 10 1
      core/runtime/core.odin
  5. 6 3
      examples/demo/demo.odin
  6. 1 0
      src/checker.cpp
  7. 14 3
      src/ir.cpp

+ 18 - 0
core/fmt/fmt.odin

@@ -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)]);

+ 78 - 0
core/mem/alloc.odin

@@ -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,
+	};
+}
+
 
 
 

+ 17 - 6
core/mem/mem.odin

@@ -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;

+ 10 - 1
core/runtime/core.odin

@@ -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)

+ 6 - 3
examples/demo/demo.odin

@@ -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);

+ 1 - 0
src/checker.cpp

@@ -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"),

+ 14 - 3
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);
+
+#if 1
 	Array<irValue *> args = {};
-	irValue *v = ir_emit_package_call(proc, "os", "heap_allocator", args);
-	ir_emit_store(proc, ep, v);
+	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;
 }