aboutsummaryrefslogtreecommitdiff
path: root/core/runtime
diff options
context:
space:
mode:
authorgingerBill <bill@gingerbill.org>2021-03-01 18:07:09 +0000
committergingerBill <bill@gingerbill.org>2021-03-01 18:07:09 +0000
commit35edf455144be7736dadced5949c64fb27e57b43 (patch)
treea16edca0ab009ce43f9cd35e0e373e95f2a0fc58 /core/runtime
parent667aa3671e585fb348a2e05ddf0992c637b40ec4 (diff)
Add `make_soa` and `delete_soa`; Reorganize soa procedures into a separate file
Diffstat (limited to 'core/runtime')
-rw-r--r--core/runtime/core_builtin.odin211
-rw-r--r--core/runtime/core_builtin_soa.odin400
2 files changed, 404 insertions, 207 deletions
diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin
index 8a60e33d5..547379721 100644
--- a/core/runtime/core_builtin.odin
+++ b/core/runtime/core_builtin.odin
@@ -176,6 +176,8 @@ new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_locat
return ptr;
}
+DEFAULT_RESERVE_CAPACITY :: 16;
+
make_aligned :: proc($T: typeid/[]$E, auto_cast len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> T {
make_slice_error_loc(loc, len);
data := mem_alloc(size_of(E)*len, alignment, allocator, loc);
@@ -194,7 +196,7 @@ make_slice :: proc($T: typeid/[]$E, auto_cast len: int, allocator := context.all
@builtin
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> T {
- return make_dynamic_array_len_cap(T, 0, 16, allocator, loc);
+ return make_dynamic_array_len_cap(T, 0, DEFAULT_RESERVE_CAPACITY, allocator, loc);
}
@builtin
@@ -215,7 +217,7 @@ make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, auto_cast len: int, a
}
@builtin
-make_map :: proc($T: typeid/map[$K]$E, auto_cast cap: int = 16, allocator := context.allocator, loc := #caller_location) -> T {
+make_map :: proc($T: typeid/map[$K]$E, auto_cast cap: int = DEFAULT_RESERVE_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
make_map_expr_error_loc(loc, cap);
context.allocator = allocator;
@@ -331,209 +333,6 @@ append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #ca
append_elems(array=array, args=args, loc=loc);
}
-@builtin
-reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> bool {
- if array == nil {
- return false;
- }
-
- old_cap := cap(array);
- if capacity <= old_cap {
- return true;
- }
-
- if array.allocator.procedure == nil {
- array.allocator = context.allocator;
- }
- assert(array.allocator.procedure != nil);
-
-
- ti := type_info_of(typeid_of(T));
- ti = type_info_base(ti);
- si := &ti.variant.(Type_Info_Struct);
-
- field_count := uintptr(len(si.offsets) - 3);
-
- if field_count == 0 {
- return true;
- }
-
- cap_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 1)*size_of(rawptr));
- assert(cap_ptr^ == old_cap);
-
-
- old_size := 0;
- new_size := 0;
-
- max_align := 0;
- for i in 0..<field_count {
- type := si.types[i].variant.(Type_Info_Pointer).elem;
- max_align = max(max_align, type.align);
-
- old_size = align_forward_int(old_size, type.align);
- new_size = align_forward_int(new_size, type.align);
-
- old_size += type.size * old_cap;
- new_size += type.size * capacity;
- }
-
- old_size = align_forward_int(old_size, max_align);
- new_size = align_forward_int(new_size, max_align);
-
- old_data := (^rawptr)(array)^;
-
- new_data := array.allocator.procedure(
- array.allocator.data, .Alloc, new_size, max_align,
- nil, old_size, 0, loc,
- );
- if new_data == nil {
- return false;
- }
-
-
- cap_ptr^ = capacity;
-
- old_offset := 0;
- new_offset := 0;
- for i in 0..<field_count {
- type := si.types[i].variant.(Type_Info_Pointer).elem;
- max_align = max(max_align, type.align);
-
- old_offset = align_forward_int(old_offset, type.align);
- new_offset = align_forward_int(new_offset, type.align);
-
- new_data_elem := rawptr(uintptr(new_data) + uintptr(new_offset));
- old_data_elem := rawptr(uintptr(old_data) + uintptr(old_offset));
-
- mem_copy(new_data_elem, old_data_elem, type.size * old_cap);
-
- (^rawptr)(uintptr(array) + i*size_of(rawptr))^ = new_data_elem;
-
- old_offset += type.size * old_cap;
- new_offset += type.size * capacity;
- }
-
- array.allocator.procedure(
- array.allocator.data, .Free, 0, max_align,
- old_data, old_size, 0, loc,
- );
-
- return true;
-}
-
-@builtin
-append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_location) {
- if array == nil {
- return;
- }
-
- arg_len := 1;
-
- if cap(array) <= len(array)+arg_len {
- cap := 2 * cap(array) + max(8, arg_len);
- _ = reserve_soa(array, cap, loc);
- }
- arg_len = min(cap(array)-len(array), arg_len);
- if arg_len > 0 {
- ti := type_info_of(typeid_of(T));
- ti = type_info_base(ti);
- si := &ti.variant.(Type_Info_Struct);
- field_count := uintptr(len(si.offsets) - 3);
-
- if field_count == 0 {
- return;
- }
-
- data := (^rawptr)(array)^;
-
- len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr));
-
-
- soa_offset := 0;
- item_offset := 0;
-
- arg_copy := arg;
- arg_ptr := &arg_copy;
-
- max_align := 0;
- for i in 0..<field_count {
- type := si.types[i].variant.(Type_Info_Pointer).elem;
- max_align = max(max_align, type.align);
-
- soa_offset = align_forward_int(soa_offset, type.align);
- item_offset = align_forward_int(item_offset, type.align);
-
- dst := rawptr(uintptr(data) + uintptr(soa_offset) + uintptr(type.size * len_ptr^));
- src := rawptr(uintptr(arg_ptr) + uintptr(item_offset));
- mem_copy(dst, src, type.size);
-
- soa_offset += type.size * cap(array);
- item_offset += type.size;
- }
-
- len_ptr^ += arg_len;
- }
-}
-
-@builtin
-append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_location) {
- if array == nil {
- return;
- }
-
- arg_len := len(args);
- if arg_len == 0 {
- return;
- }
-
- if cap(array) <= len(array)+arg_len {
- cap := 2 * cap(array) + max(8, arg_len);
- _ = reserve_soa(array, cap, loc);
- }
- arg_len = min(cap(array)-len(array), arg_len);
- if arg_len > 0 {
- ti := type_info_of(typeid_of(T));
- ti = type_info_base(ti);
- si := &ti.variant.(Type_Info_Struct);
- field_count := uintptr(len(si.offsets) - 3);
-
- if field_count == 0 {
- return;
- }
-
- data := (^rawptr)(array)^;
-
- len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr));
-
-
- soa_offset := 0;
- item_offset := 0;
-
- args_ptr := &args[0];
-
- max_align := 0;
- for i in 0..<field_count {
- type := si.types[i].variant.(Type_Info_Pointer).elem;
- max_align = max(max_align, type.align);
-
- soa_offset = align_forward_int(soa_offset, type.align);
- item_offset = align_forward_int(item_offset, type.align);
-
- dst := uintptr(data) + uintptr(soa_offset) + uintptr(type.size * len_ptr^);
- src := uintptr(args_ptr) + uintptr(item_offset);
- for j in 0..<arg_len {
- d := rawptr(dst + uintptr(j*type.size));
- s := rawptr(src + uintptr(j*size_of(E)));
- mem_copy(d, s, type.size);
- }
-
- soa_offset += type.size * cap(array);
- item_offset += type.size;
- }
-
- len_ptr^ += arg_len;
- }
-}
// The append_string built-in procedure appends multiple strings to the end of a [dynamic]u8 like type
@builtin
@@ -546,8 +345,6 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_
// The append built-in procedure appends elements to the end of a dynamic array
@builtin append :: proc{append_elem, append_elems, append_elem_string};
-// The append_soa built-in procedure appends elements to the end of an #soa dynamic array
-@builtin append_soa :: proc{append_soa_elem, append_soa_elems};
@builtin
append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) {
diff --git a/core/runtime/core_builtin_soa.odin b/core/runtime/core_builtin_soa.odin
new file mode 100644
index 000000000..161c85bc5
--- /dev/null
+++ b/core/runtime/core_builtin_soa.odin
@@ -0,0 +1,400 @@
+package runtime
+
+import "intrinsics"
+_ :: intrinsics;
+
+/*
+
+ SOA types are implemented with this sort of layout:
+
+ SOA Fixed Array
+ struct {
+ f0: [N]T0,
+ f1: [N]T1,
+ f2: [N]T2,
+ }
+
+ SOA Slice
+ struct {
+ f0: ^T0,
+ f1: ^T1,
+ f2: ^T2,
+
+ len: int,
+ }
+
+ SOA Dynamic Array
+ struct {
+ f0: ^T0,
+ f1: ^T1,
+ f2: ^T2,
+
+ len: int,
+ cap: int,
+ allocator: Allocator,
+ }
+
+ A footer is used rather than a header purely to simplify access to the fields internally
+ i.e. field index of the AOS == SOA
+
+*/
+
+
+Raw_SOA_Footer_Slice :: struct {
+ len: int,
+}
+
+Raw_SOA_Footer_Dynamic_Array :: struct {
+ len: int,
+ cap: int,
+ allocator: Allocator
+}
+
+raw_soa_footer_slice :: proc(array: ^$T/#soa[]$E) -> (footer: ^Raw_SOA_Footer_Slice) {
+ if array == nil {
+ return nil;
+ }
+ field_count := uintptr(intrinsics.type_struct_field_count(E));
+ footer = (^Raw_SOA_Footer_Slice)(uintptr(array) + field_count*size_of(rawptr));
+ return;
+}
+raw_soa_footer_dynamic_array :: proc(array: ^$T/#soa[dynamic]$E) -> (footer: ^Raw_SOA_Footer_Dynamic_Array) {
+ if array == nil {
+ return nil;
+ }
+ field_count := uintptr(intrinsics.type_struct_field_count(E));
+ footer = (^Raw_SOA_Footer_Dynamic_Array)(uintptr(array) + field_count*size_of(rawptr));
+ return;
+}
+raw_soa_footer :: proc{
+ raw_soa_footer_slice,
+ raw_soa_footer_dynamic_array,
+};
+
+
+
+@builtin
+make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (array: T) {
+ if length <= 0 {
+ return;
+ }
+
+ footer := raw_soa_footer(&array);
+ if size_of(E) == 0 {
+ footer.len = length;
+ return;
+ }
+
+ max_align := max(alignment, align_of(E));
+
+ ti := type_info_of(typeid_of(T));
+ ti = type_info_base(ti);
+ si := &ti.variant.(Type_Info_Struct);
+
+ field_count := uintptr(intrinsics.type_struct_field_count(E));
+
+ total_size := 0;
+ for i in 0..<field_count {
+ type := si.types[i].variant.(Type_Info_Pointer).elem;
+ total_size += type.size * length;
+ total_size = align_forward_int(total_size, max_align);
+ }
+
+ allocator := allocator;
+ if allocator.procedure == nil {
+ allocator = context.allocator;
+ }
+ assert(allocator.procedure != nil);
+
+ new_data := allocator.procedure(
+ allocator.data, .Alloc, total_size, max_align,
+ nil, 0, 0, loc,
+ );
+ if new_data == nil {
+ return;
+ }
+
+ data := uintptr(&array);
+ offset := 0;
+ for i in 0..<field_count {
+ type := si.types[i].variant.(Type_Info_Pointer).elem;
+
+ offset = align_forward_int(offset, max_align);
+
+ (^uintptr)(data)^ = uintptr(new_data) + uintptr(offset);
+ data += size_of(rawptr);
+ offset += type.size * length;
+ }
+ footer.len = length;
+
+ return;
+}
+
+@builtin
+make_soa_slice :: proc($T: typeid/#soa[]$E, length: int, allocator := context.allocator, loc := #caller_location) -> (array: T) {
+ return make_soa_aligned(T, length, align_of(E), allocator, loc);
+}
+
+@builtin
+make_soa_dynamic_array :: proc($T: typeid/#soa[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (array: T) {
+ context.allocator = allocator;
+ reserve_soa(&array, DEFAULT_RESERVE_CAPACITY, loc);
+ return;
+}
+
+@builtin
+make_soa_dynamic_array_len :: proc($T: typeid/#soa[dynamic]$E, auto_cast length: int, allocator := context.allocator, loc := #caller_location) -> (array: T) {
+ context.allocator = allocator;
+ resize_soa(&array, length, loc);
+ return;
+}
+
+@builtin
+make_soa_dynamic_array_len_cap :: proc($T: typeid/#soa[dynamic]$E, auto_cast length, capacity: int, allocator := context.allocator, loc := #caller_location) -> (array: T) {
+ context.allocator = allocator;
+ if reserve_soa(&array, capacity, loc) {
+ resize_soa(&array, length, loc);
+ }
+ return;
+}
+
+
+@builtin
+make_soa :: proc{
+ make_soa_slice,
+ make_soa_dynamic_array,
+ make_soa_dynamic_array_len,
+ make_soa_dynamic_array_len_cap,
+};
+
+
+@builtin
+resize_soa :: proc(array: ^$T/#soa[dynamic]$E, length: int, loc := #caller_location) -> bool {
+ if array == nil {
+ return false;
+ }
+ if !reserve_soa(array, length, loc) {
+ return false;
+ }
+ footer := raw_soa_footer(array);
+ footer.len = length;
+ return true;
+}
+
+@builtin
+reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> bool {
+ if array == nil {
+ return false;
+ }
+
+ old_cap := cap(array);
+ if capacity <= old_cap {
+ return true;
+ }
+
+ if array.allocator.procedure == nil {
+ array.allocator = context.allocator;
+ }
+ assert(array.allocator.procedure != nil);
+
+ footer := raw_soa_footer(array);
+ if size_of(E) == 0 {
+ footer.cap = capacity;
+ return true;
+ }
+
+ ti := type_info_of(typeid_of(T));
+ ti = type_info_base(ti);
+ si := &ti.variant.(Type_Info_Struct);
+
+ field_count := uintptr(intrinsics.type_struct_field_count(E));
+ assert(footer.cap == old_cap);
+
+ old_size := 0;
+ new_size := 0;
+
+ max_align :: align_of(E);
+ for i in 0..<field_count {
+ type := si.types[i].variant.(Type_Info_Pointer).elem;
+
+ old_size += type.size * old_cap;
+ new_size += type.size * capacity;
+
+ old_size = align_forward_int(old_size, max_align);
+ new_size = align_forward_int(new_size, max_align);
+ }
+
+ old_data := (^rawptr)(array)^;
+
+ new_data := array.allocator.procedure(
+ array.allocator.data, .Alloc, new_size, max_align,
+ nil, old_size, 0, loc,
+ );
+ if new_data == nil {
+ return false;
+ }
+
+
+ footer.cap = capacity;
+
+ old_offset := 0;
+ new_offset := 0;
+ for i in 0..<field_count {
+ type := si.types[i].variant.(Type_Info_Pointer).elem;
+
+ old_offset = align_forward_int(old_offset, max_align);
+ new_offset = align_forward_int(new_offset, max_align);
+
+ new_data_elem := rawptr(uintptr(new_data) + uintptr(new_offset));
+ old_data_elem := rawptr(uintptr(old_data) + uintptr(old_offset));
+
+ mem_copy(new_data_elem, old_data_elem, type.size * old_cap);
+
+ (^rawptr)(uintptr(array) + i*size_of(rawptr))^ = new_data_elem;
+
+ old_offset += type.size * old_cap;
+ new_offset += type.size * capacity;
+ }
+
+ array.allocator.procedure(
+ array.allocator.data, .Free, 0, max_align,
+ old_data, old_size, 0, loc,
+ );
+
+ return true;
+}
+
+@builtin
+append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_location) {
+ if array == nil {
+ return;
+ }
+
+ arg_len := 1;
+
+ if cap(array) <= len(array)+arg_len {
+ cap := 2 * cap(array) + max(8, arg_len);
+ _ = reserve_soa(array, cap, loc);
+ }
+ arg_len = min(cap(array)-len(array), arg_len);
+
+ footer := raw_soa_footer(array);
+
+ if size_of(E) > 0 && arg_len > 0 {
+ ti := type_info_of(typeid_of(T));
+ ti = type_info_base(ti);
+ si := &ti.variant.(Type_Info_Struct);
+ field_count := uintptr(intrinsics.type_struct_field_count(E));
+
+ data := (^rawptr)(array)^;
+
+ soa_offset := 0;
+ item_offset := 0;
+
+ arg_copy := arg;
+ arg_ptr := &arg_copy;
+
+ max_align :: align_of(E);
+ for i in 0..<field_count {
+ type := si.types[i].variant.(Type_Info_Pointer).elem;
+
+ soa_offset = align_forward_int(soa_offset, max_align);
+ item_offset = align_forward_int(item_offset, type.align);
+
+ dst := rawptr(uintptr(data) + uintptr(soa_offset) + uintptr(type.size * footer.len));
+ src := rawptr(uintptr(arg_ptr) + uintptr(item_offset));
+ mem_copy(dst, src, type.size);
+
+ soa_offset += type.size * cap(array);
+ item_offset += type.size;
+ }
+ }
+ footer.len += arg_len;
+}
+
+@builtin
+append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_location) {
+ if array == nil {
+ return;
+ }
+
+ arg_len := len(args);
+ if arg_len == 0 {
+ return;
+ }
+
+ if cap(array) <= len(array)+arg_len {
+ cap := 2 * cap(array) + max(8, arg_len);
+ _ = reserve_soa(array, cap, loc);
+ }
+ arg_len = min(cap(array)-len(array), arg_len);
+
+ footer := raw_soa_footer(array);
+ if size_of(E) > 0 && arg_len > 0 {
+ ti := type_info_of(typeid_of(T));
+ ti = type_info_base(ti);
+ si := &ti.variant.(Type_Info_Struct);
+ field_count := uintptr(intrinsics.type_struct_field_count(E));
+
+ data := (^rawptr)(array)^;
+
+ soa_offset := 0;
+ item_offset := 0;
+
+ args_ptr := &args[0];
+
+ max_align :: align_of(E);
+ for i in 0..<field_count {
+ type := si.types[i].variant.(Type_Info_Pointer).elem;
+
+ soa_offset = align_forward_int(soa_offset, max_align);
+ item_offset = align_forward_int(item_offset, type.align);
+
+ dst := uintptr(data) + uintptr(soa_offset) + uintptr(type.size * footer.len);
+ src := uintptr(args_ptr) + uintptr(item_offset);
+ for j in 0..<arg_len {
+ d := rawptr(dst + uintptr(j*type.size));
+ s := rawptr(src + uintptr(j*size_of(E)));
+ mem_copy(d, s, type.size);
+ }
+
+ soa_offset += type.size * cap(array);
+ item_offset += type.size;
+ }
+ }
+
+ footer.len += arg_len;
+}
+
+
+// The append_soa built-in procedure appends elements to the end of an #soa dynamic array
+@builtin
+append_soa :: proc{
+ append_soa_elem,
+ append_soa_elems,
+};
+
+
+delete_soa_slice :: proc(array: $T/#soa[]$E, allocator := context.allocator, loc := #caller_location) {
+ when intrinsics.type_struct_field_count(E) != 0 {
+ array := array;
+ ptr := (^rawptr)(&array)^;
+ free(ptr, allocator, loc);
+ }
+}
+
+delete_soa_dynamic_array :: proc(array: $T/#soa[dynamic]$E, loc := #caller_location) {
+ when intrinsics.type_struct_field_count(E) != 0 {
+ array := array;
+ ptr := (^rawptr)(&array)^;
+ footer := raw_soa_footer(&array);
+ free(ptr, footer.allocator, loc);
+ }
+}
+
+
+@builtin
+delete_soa :: proc{
+ delete_soa_slice,
+ delete_soa_dynamic_array,
+};