diff options
| author | gingerBill <bill@gingerbill.org> | 2021-03-01 18:07:09 +0000 |
|---|---|---|
| committer | gingerBill <bill@gingerbill.org> | 2021-03-01 18:07:09 +0000 |
| commit | 35edf455144be7736dadced5949c64fb27e57b43 (patch) | |
| tree | a16edca0ab009ce43f9cd35e0e373e95f2a0fc58 /core/runtime | |
| parent | 667aa3671e585fb348a2e05ddf0992c637b40ec4 (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.odin | 211 | ||||
| -rw-r--r-- | core/runtime/core_builtin_soa.odin | 400 |
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, +}; |