diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index f06dbdfbf..ee99da1b3 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -77,7 +77,9 @@ prefetch_write_instruction :: proc(address: rawptr, #const locality: i32 /* 0..= prefetch_write_data :: proc(address: rawptr, #const locality: i32 /* 0..=3 */) --- // Compiler Hints -expect :: proc(val, expected_val: $T) -> T --- +expect :: proc(val, expected_val: $T) -> T --- +likely :: proc(val: $T) -> T where type_is_boolean(T) --- +unlikely :: proc(val: $T) -> T where type_is_boolean(T) --- // Linux and Darwin Only syscall :: proc(id: uintptr, args: ..uintptr) -> uintptr --- @@ -180,6 +182,7 @@ type_is_bit_set :: proc($T: typeid) -> bool --- type_is_bit_field :: proc($T: typeid) -> bool --- type_is_simd_vector :: proc($T: typeid) -> bool --- type_is_matrix :: proc($T: typeid) -> bool --- +type_is_fixed_capacity_dynamic_array :: proc($T: typeid) -> bool --- type_has_nil :: proc($T: typeid) -> bool --- @@ -222,6 +225,8 @@ type_is_superset_of :: proc($Super, $Sub: typeid) -> bool --- type_field_index_of :: proc($T: typeid, $name: string) -> uintptr --- +type_fixed_capacity_dynamic_array_len_offset :: proc($T: typeid/[dynamic; $N]$E) -> uintptr --- + // "Contiguous" means that the set of enum constants, when sorted, have a difference of either 0 or 1 between consecutive values. // This is the exact opposite of "sparse". type_enum_is_contiguous :: proc($T: typeid) -> bool where type_is_enum(T) --- diff --git a/base/runtime/core.odin b/base/runtime/core.odin index e2ed78452..dbd8ba468 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -206,6 +206,14 @@ Type_Info_Bit_Field :: struct { field_count: int, } +Type_Info_Fixed_Capacity_Dynamic_Array :: struct { + elem: ^Type_Info, + elem_size: int, + capacity: int, + len_offset: uintptr, +} + + Type_Info_Flag :: enum u8 { Comparable = 0, Simple_Compare = 1, @@ -246,6 +254,7 @@ Type_Info :: struct { Type_Info_Matrix, Type_Info_Soa_Pointer, Type_Info_Bit_Field, + Type_Info_Fixed_Capacity_Dynamic_Array, }, } @@ -425,6 +434,11 @@ Raw_Dynamic_Array :: struct { allocator: Allocator, } +Raw_Fixed_Capacity_Dynamic_Array :: struct($Capacity: uint, $T: typeid) { + data: [Capacity]T, + len: int, +} + // The raw, type-erased representation of a map. // // 32-bytes on 64-bit diff --git a/base/runtime/core_builtin.odin b/base/runtime/core_builtin.odin index 4d8b493f7..fe76ee5a5 100644 --- a/base/runtime/core_builtin.odin +++ b/base/runtime/core_builtin.odin @@ -119,14 +119,14 @@ copy :: proc{copy_slice, copy_from_string, copy_from_string16} -// `unordered_remove` removed the element at the specified `index`. It does so by replacing the current end value +// `unordered_remove_dynamic_array` removed the element at the specified `index`. It does so by replacing the current end value // with the old value, and reducing the length of the dynamic array by 1. // // Note: This is an O(1) operation. // Note: If you want the elements to remain in their order, use `ordered_remove`. // Note: If the index is out of bounds, this procedure will panic. @builtin -unordered_remove :: proc(array: ^$D/[dynamic]$T, #any_int index: int, loc := #caller_location) #no_bounds_check { +unordered_remove_dynamic_array :: proc(array: ^$D/[dynamic]$T, #any_int index: int, loc := #caller_location) #no_bounds_check { bounds_check_error_loc(loc, index, len(array)) n := len(array)-1 if index != n { @@ -134,13 +134,13 @@ unordered_remove :: proc(array: ^$D/[dynamic]$T, #any_int index: int, loc := #ca } (^Raw_Dynamic_Array)(array).len -= 1 } -// `ordered_remove` removed the element at the specified `index` whilst keeping the order of the other elements. +// `ordered_remove_dynamic_array` removed the element at the specified `index` whilst keeping the order of the other elements. // // Note: This is an O(N) operation. // Note: If the elements do not have to remain in their order, prefer `unordered_remove`. // Note: If the index is out of bounds, this procedure will panic. @builtin -ordered_remove :: proc(array: ^$D/[dynamic]$T, #any_int index: int, loc := #caller_location) #no_bounds_check { +ordered_remove_dynamic_array :: proc(array: ^$D/[dynamic]$T, #any_int index: int, loc := #caller_location) #no_bounds_check { bounds_check_error_loc(loc, index, len(array)) if index+1 < len(array) { copy(array[index:], array[index+1:]) @@ -148,12 +148,12 @@ ordered_remove :: proc(array: ^$D/[dynamic]$T, #any_int index: int, loc := #call (^Raw_Dynamic_Array)(array).len -= 1 } -// `remove_range` removes a range of elements specified by the range `lo` and `hi`, whilst keeping the order of the other elements. +// `remove_range_dynamic_array` removes a range of elements specified by the range `lo` and `hi`, whilst keeping the order of the other elements. // // Note: This is an O(N) operation. // Note: If the range is out of bounds, this procedure will panic. @builtin -remove_range :: proc(array: ^$D/[dynamic]$T, #any_int lo, hi: int, loc := #caller_location) #no_bounds_check { +remove_range_dynamic_array :: proc(array: ^$D/[dynamic]$T, #any_int lo, hi: int, loc := #caller_location) #no_bounds_check { slice_expr_error_lo_hi_loc(loc, lo, hi, len(array)) n := max(hi-lo, 0) if n > 0 { @@ -164,29 +164,117 @@ remove_range :: proc(array: ^$D/[dynamic]$T, #any_int lo, hi: int, loc := #calle } } +// `unordered_remove_fixed_capacity_dynamic_array` removed the element at the specified `index`. It does so by replacing the current end value +// with the old value, and reducing the length of the dynamic array by 1. +// +// Note: This is an O(1) operation. +// Note: If you want the elements to remain in their order, use `ordered_remove`. +// Note: If the index is out of bounds, this procedure will panic. +@builtin +unordered_remove_fixed_capacity_dynamic_array :: proc(array: ^$D/[dynamic; $N]$E, #any_int index: int, loc := #caller_location) #no_bounds_check { + bounds_check_error_loc(loc, index, len(array)) + n := len(array)-1 + if index != n { + array[index] = array[n] + } + (^Raw_Fixed_Capacity_Dynamic_Array(N, E))(array).len -= 1 +} +// `ordered_remove_fixed_capacity_dynamic_array` removed the element at the specified `index` whilst keeping the order of the other elements. +// +// Note: This is an O(N) operation. +// Note: If the elements do not have to remain in their order, prefer `unordered_remove`. +// Note: If the index is out of bounds, this procedure will panic. +@builtin +ordered_remove_fixed_capacity_dynamic_array :: proc(array: ^$D/[dynamic; $N]$E, #any_int index: int, loc := #caller_location) #no_bounds_check { + bounds_check_error_loc(loc, index, len(array)) + if index+1 < len(array) { + copy(array[index:], array[index+1:]) + } + (^Raw_Fixed_Capacity_Dynamic_Array(N, E))(array).len -= 1 +} -// `pop` will remove and return the end value of dynamic array `array` and reduces the length of `array` by 1. +// `remove_range_fixed_capacity_dynamic_array` removes a range of elements specified by the range `lo` and `hi`, whilst keeping the order of the other elements. +// +// Note: This is an O(N) operation. +// Note: If the range is out of bounds, this procedure will panic. +@builtin +remove_range_fixed_capacity_dynamic_array :: proc(array: ^$D/[dynamic; $N]$E, #any_int lo, hi: int, loc := #caller_location) #no_bounds_check { + slice_expr_error_lo_hi_loc(loc, lo, hi, len(array)) + n := max(hi-lo, 0) + if n > 0 { + if hi != len(array) { + copy(array[lo:], array[hi:]) + } + (^Raw_Fixed_Capacity_Dynamic_Array(N, E))(array).len -= n + } +} + +@builtin +unordered_remove :: proc{ + unordered_remove_dynamic_array, + unordered_remove_fixed_capacity_dynamic_array, +} + + +@builtin +ordered_remove :: proc{ + ordered_remove_dynamic_array, + ordered_remove_fixed_capacity_dynamic_array, +} + +@builtin +remove_range :: proc{ + remove_range_dynamic_array, + remove_range_fixed_capacity_dynamic_array, +} + + + +// `pop_dynamic_array` will remove and return the end value of dynamic array `array` and reduces the length of `array` by 1. // // Note: If the dynamic array has no elements (`len(array) == 0`), this procedure will panic. @builtin -pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check { +pop_dynamic_array :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check { assert(len(array) > 0, loc=loc) - _pop_type_erased(&res, (^Raw_Dynamic_Array)(array), size_of(E)) + _pop_dynamic_array_type_erased(&res, (^Raw_Dynamic_Array)(array), size_of(E)) return res } -_pop_type_erased :: proc(res: rawptr, array: ^Raw_Dynamic_Array, elem_size: int, loc := #caller_location) { +_pop_dynamic_array_type_erased :: proc(res: rawptr, array: ^Raw_Dynamic_Array, elem_size: int) { end := rawptr(uintptr(array.data) + uintptr(elem_size*(array.len-1))) intrinsics.mem_copy_non_overlapping(res, end, elem_size) array.len -= 1 } +// `pop_fixed_capacity_dynamic_array` will remove and return the end value of fixed capacity dynamic array `array` and reduces the length of `array` by 1. +// +// Note: If the fixed capacity dynamic array has no elements (`len(array) == 0`), this procedure will panic. +@builtin +pop_fixed_capacity_dynamic_array :: proc(array: ^$T/[dynamic; $N]$E, loc := #caller_location) -> (res: E) #no_bounds_check { + assert(len(array) > 0, loc=loc) -// `pop_safe` trys to remove and return the end value of dynamic array `array` and reduces the length of `array` by 1. + elem_size :: size_of(E) + end := rawptr(uintptr(array) + uintptr(elem_size*(len(array)-1))) + intrinsics.mem_copy_non_overlapping(&res, end, elem_size) + (^Raw_Fixed_Capacity_Dynamic_Array(N, E))(array).len -= 1 + return res +} + + +// `pop` will remove and return the end value of dynamic array `array` and reduces the length of `array` by 1. +// +// Note: If the dynamic array has no elements (`len(array) == 0`), this procedure will panic. +@builtin +pop :: proc{ + pop_dynamic_array, + pop_fixed_capacity_dynamic_array, +} + +// `pop_safe_dynamic_array` trys to remove and return the end value of dynamic array `array` and reduces the length of `array` by 1. // If the operation is not possible, it will return false. @builtin -pop_safe :: proc "contextless" (array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check { +pop_safe_dynamic_array :: proc "contextless" (array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check { if len(array) == 0 { return } @@ -195,11 +283,32 @@ pop_safe :: proc "contextless" (array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #n return } -// `pop_front` will remove and return the first value of dynamic array `array` and reduces the length of `array` by 1. +// `pop_safe_fixed_capacity_dynamic_array` trys to remove and return the end value of dynamic array `array` and reduces the length of `array` by 1. +// If the operation is not possible, it will return false. +@builtin +pop_safe_fixed_capacity_dynamic_array :: proc "contextless" (array: ^$T/[dynamic; $N]$E) -> (res: E, ok: bool) #no_bounds_check { + if len(array) == 0 { + return + } + res, ok = array[len(array)-1], true + (^Raw_Fixed_Capacity_Dynamic_Array(N, E))(array).len -= 1 + return +} + +// `pop_safe` trys to remove and return the end value of dynamic array `array` and reduces the length of `array` by 1. +// If the operation is not possible, it will return false. +@builtin +pop_safe :: proc{ + pop_safe_dynamic_array, + pop_safe_fixed_capacity_dynamic_array, +} + + +// `pop_front_dynamic_array` will remove and return the first value of dynamic array `array` and reduces the length of `array` by 1. // // Note: If the dynamic array as no elements (`len(array) == 0`), this procedure will panic. @builtin -pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check { +pop_front_dynamic_array :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check { assert(len(array) > 0, loc=loc) res = array[0] if len(array) > 1 { @@ -209,10 +318,35 @@ pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) # return res } -// `pop_front_safe` trys to return and remove the first value of dynamic array `array` and reduces the length of `array` by 1. +// `pop_front_fixed_capacity_dynamic_array` will remove and return the first value of fixed capacity dynamic array `array` and reduces the length of `array` by 1. +// +// Note: If the fixed capacity dynamic array as no elements (`len(array) == 0`), this procedure will panic. +@builtin +pop_front_fixed_capacity_dynamic_array :: proc(array: ^$T/[dynamic; $N]$E, loc := #caller_location) -> (res: E) #no_bounds_check { + assert(len(array) > 0, loc=loc) + res = array[0] + if len(array) > 1 { + copy(array[0:], array[1:]) + } + (^Raw_Fixed_Capacity_Dynamic_Array(N, E))(array).len -= 1 + return res +} + + +// `pop_front` will remove and return the first value of dynamic array `array` and reduces the length of `array` by 1. +// +// Note: If the dynamic array as no elements (`len(array) == 0`), this procedure will panic. +@builtin +pop_front :: proc{ + pop_front_dynamic_array, + pop_front_fixed_capacity_dynamic_array, +} + + +// `pop_front_safe_dynamic_array` trys to return and remove the first value of dynamic array `array` and reduces the length of `array` by 1. // If the operation is not possible, it will return false. @builtin -pop_front_safe :: proc "contextless" (array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check { +pop_front_safe_dynamic_array :: proc "contextless" (array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check { if len(array) == 0 { return } @@ -224,12 +358,37 @@ pop_front_safe :: proc "contextless" (array: ^$T/[dynamic]$E) -> (res: E, ok: bo return } +// `pop_front_safe_fixed_capacity_dynamic_array` trys to return and remove the first value of dynamic array `array` and reduces the length of `array` by 1. +// If the operation is not possible, it will return false. +@builtin +pop_front_safe_fixed_capacity_dynamic_array :: proc "contextless" (array: ^$T/[dynamic; $N]$E) -> (res: E, ok: bool) #no_bounds_check { + if len(array) == 0 { + return + } + res, ok = array[0], true + if len(array) > 1 { + copy(array[0:], array[1:]) + } + (^Raw_Fixed_Capacity_Dynamic_Array(N, E))(array).len -= 1 + return +} + +// `pop_front_safe` trys to return and remove the first value of dynamic array `array` and reduces the length of `array` by 1. +// If the operation is not possible, it will return false. +@builtin +pop_front_safe :: proc { + pop_front_safe_dynamic_array, + pop_front_safe_fixed_capacity_dynamic_array, +} + + // `clear` will set the length of a passed dynamic array or map to `0` @builtin clear :: proc{ clear_dynamic_array, clear_map, + clear_fixed_capacity_dynamic_array, clear_soa_dynamic_array, } @@ -254,6 +413,7 @@ non_zero_reserve :: proc{ @builtin resize :: proc{ resize_dynamic_array, + resize_fixed_capacity_dynamic_array, resize_soa, } @@ -261,6 +421,7 @@ resize :: proc{ @builtin non_zero_resize :: proc{ non_zero_resize_dynamic_array, + non_zero_resize_fixed_capacity_dynamic_array, non_zero_resize_soa, } @@ -669,6 +830,15 @@ non_zero_append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, l return _append_elem_string(array, arg, false, loc) } +// `non_zero_append_elem_fixed_capacity_string` appends a string to the end of a dynamic array of bytes, without zeroing any reserved memory +// +// Note: Prefer using the procedure group `non_zero_append`. +@builtin +non_zero_append_elem_fixed_capacity_string :: proc "contextless" (array: ^$T/[dynamic; $N]$E/u8, arg: $A/string) -> (n: int) { + return append_fixed_capacity_elem(array, transmute([]byte)arg) +} + + // The append_string built-in procedure appends multiple strings to the end of a [dynamic]u8 like type // @@ -686,6 +856,57 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_ return } + +// `append_fixed_capacity_elem` appends an element to the end of a fixed capacity dynamic array. Returns 0 on failure +@builtin +append_fixed_capacity_elem :: proc "contextless" (array: ^$T/[dynamic; $N]$E, #no_broadcast arg: E) -> (n: int) { + Raw :: Raw_Fixed_Capacity_Dynamic_Array(N, E) + + if (^Raw)(array).len >= N { + return 0 + } + + when size_of(E) != 0 { + #no_bounds_check (^Raw)(array).data[(^Raw)(array).len] = arg + } + (^Raw)(array).len += 1 + return 1 +} + + +// `append_fixed_capacity_elem` appends an element to the end of a fixed capacity dynamic array. Returns 0 on failure +@builtin +append_fixed_capacity_elems :: proc "contextless" (array: ^$T/[dynamic; $N]$E, #no_broadcast args: ..E) -> (n: int) { + Raw :: Raw_Fixed_Capacity_Dynamic_Array(N, E) + raw := (^Raw)(array) + + n = min(N - len(array), len(args)) + + #no_bounds_check when size_of(E) != 0 { + intrinsics.mem_copy(&raw.data[raw.len], raw_data(args), n*size_of(E)) + } + + raw.len += n + return n +} + +// The append_fixed_capacity_string built-in procedure appends multiple strings to the end of a [dynamic]u8 like type +// +// Note: Prefer using the procedure group `append`. +@builtin +append_fixed_capacity_string :: proc "contextless" (array: ^$T/[dynamic; $N]$E/u8, args: ..string) -> (n: int) { + n_arg: int + for arg in args { + n_arg = append_fixed_capacity_elems(array, ..transmute([]E)(arg)) + n += n_arg + if n_arg < len(arg) { + return + } + } + return +} + + // The append built-in procedure appends elements to the end of a dynamic array @builtin append :: proc{ @@ -693,6 +914,10 @@ append :: proc{ append_elems, append_elem_string, + append_fixed_capacity_elem, + append_fixed_capacity_elems, + append_fixed_capacity_string, + append_soa_elem, append_soa_elems, } @@ -703,6 +928,10 @@ non_zero_append :: proc{ non_zero_append_elems, non_zero_append_elem_string, + append_fixed_capacity_elem, + append_fixed_capacity_elems, + non_zero_append_elem_fixed_capacity_string, + non_zero_append_soa_elem, non_zero_append_soa_elems, } @@ -711,7 +940,7 @@ non_zero_append :: proc{ // `append_nothing` appends an empty value to a dynamic array. It returns `1, nil` if successful, and `0, err` when it was not possible, // whatever `err` happens to be. @builtin -append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { +append_nothing_dynamic_array :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { if array == nil { return 0, nil } @@ -720,6 +949,27 @@ append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: i return len(array)-prev_len, nil } +// `append_nothing` appends an empty value to a dynamic array. It returns `1, nil` if successful, and `0, err` when it was not possible, +// whatever `err` happens to be. +@builtin +append_nothing_fixed_capacity_dynamic_array :: proc "contextless" (array: ^$T/[dynamic; $N]$E) -> (n: int, ok: bool) { + if array == nil { + return 0, true + } + prev_len := len(array) + resize_fixed_capacity_dynamic_array(array, len(array)+1) or_return + return len(array)-prev_len, true +} + + +// `append_nothing` appends an empty value to a dynamic array. It returns `1, nil` if successful, and `0, err` when it was not possible, +// whatever `err` happens to be. +@builtin +append_nothing :: proc{ + append_nothing_dynamic_array, + append_nothing_fixed_capacity_dynamic_array, +} + // `inject_at_elem` injects an element in a dynamic array at a specified index and moves the previous elements after that index "across" @builtin @@ -795,11 +1045,87 @@ inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, ar return } + +// `inject_at_elem_fixed_capacity_dynamic_array` injects an element in a dynamic array at a specified index and moves the previous elements after that index "across" +@builtin +inject_at_elem_fixed_capacity_dynamic_array :: proc(array: ^$T/[dynamic; $N]$E, #any_int index: int, #no_broadcast arg: E, loc := #caller_location) -> (ok: bool) #no_bounds_check { + when !ODIN_NO_BOUNDS_CHECK { + ensure(index >= 0, "Index must be positive.", loc) + } + if array == nil { + return false + } + n := max(len(array), index) + m :: 1 + new_size := n + m + + resize(array, new_size) or_return + when size_of(E) != 0 { + copy(array[index + m:], array[index:]) + array[index] = arg + } + return true +} + +// `inject_at_elems_fixed_capacity_dynamic_array` injects multiple elements in a dynamic array at a specified index and moves the previous elements after that index "across" +@builtin +inject_at_elems_fixed_capacity_dynamic_array :: proc(array: ^$T/[dynamic; $N]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check { + when !ODIN_NO_BOUNDS_CHECK { + ensure(index >= 0, "Index must be positive.", loc) + } + if array == nil { + return false + } + if len(args) == 0 { + return true + } + + n := max(len(array), index) + m := len(args) + new_size := n + m + + resize(array, new_size) or_return + when size_of(E) != 0 { + copy(array[index + m:], array[index:]) + copy(array[index:], args) + } + return true +} + +// `inject_at_elem_string_fixed_capacity_dynamic_array` injects a string into a dynamic array at a specified index and moves the previous elements after that index "across" +@builtin +inject_at_elem_string_fixed_capacity_dynamic_array :: proc(array: ^$T/[dynamic; $N]$E/u8, #any_int index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check { + when !ODIN_NO_BOUNDS_CHECK { + ensure(index >= 0, "Index must be positive.", loc) + } + if array == nil { + return false + } + if len(arg) == 0 { + return true + } + + n := max(len(array), index) + m := len(arg) + new_size := n + m + + resize(array, new_size) or_return + copy(array[index+m:], array[index:]) + copy(array[index:], arg) + return true +} + + // `inject_at` injects something into a dynamic array at a specified index and moves the previous elements after that index "across" -@builtin inject_at :: proc{ +@builtin +inject_at :: proc{ inject_at_elem, inject_at_elems, inject_at_elem_string, + + inject_at_elem_fixed_capacity_dynamic_array, + inject_at_elems_fixed_capacity_dynamic_array, + inject_at_elem_string_fixed_capacity_dynamic_array, } @@ -856,6 +1182,60 @@ assign_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, ar return } + +// `assign_at_elem_fixed_capacity_dynamic_array` assigns a value at a given index. If the requested index is past the end of the current +// size of the dynamic array, it will attempt to `resize` the a new length of `index+1` and then assign as `index`. +@builtin +assign_at_elem_fixed_capacity_dynamic_array :: proc "contextless" (array: ^$T/[dynamic; $N]$E, #any_int index: int, arg: E) -> (ok: bool) #no_bounds_check { + if index < len(array) { + array[index] = arg + ok = true + } else { + resize(array, index+1, loc) or_return + array[index] = arg + ok = true + } + return +} + + +// `assign_at_elems_fixed_capacity_dynamic_array` assigns a values at a given index. If the requested index is past the end of the current +// size of the dynamic array, it will attempt to `resize` the a new length of `index+len(args)` and then assign as `index`. +@builtin +assign_at_elems_fixed_capacity_dynamic_array :: proc "contextless" (array: ^$T/[dynamic; $N]$E, #any_int index: int, #no_broadcast args: ..E) -> (ok: bool) #no_bounds_check { + new_size := index + len(args) + if len(args) == 0 { + ok = true + } else if new_size < len(array) { + copy(array[index:], args) + ok = true + } else { + resize(array, new_size, loc) or_return + copy(array[index:], args) + ok = true + } + return +} + +// `assign_at_elem_string_fixed_capacity_dynamic_array` assigns a string at a given index. If the requested index is past the end of the current +// size of the dynamic array, it will attempt to `resize` the a new length of `index+len(arg)` and then assign as `index`. +@builtin +assign_at_elem_string_fixed_capacity_dynamic_array :: proc "contextless" (array: ^$T/[dynamic; $N]$E/u8, #any_int index: int, arg: string) -> (ok: bool) #no_bounds_check { + new_size := index + len(arg) + if len(arg) == 0 { + ok = true + } else if new_size < len(array) { + copy(array[index:], arg) + ok = true + } else { + resize(array, new_size, loc) or_return + copy(array[index:], arg) + ok = true + } + return +} + + // `assign_at` assigns a value at a given index. If the requested index is past the end of the current // size of the dynamic array, it will attempt to `resize` the a new length of `index+size_needed` and then assign as `index`. @builtin @@ -863,6 +1243,10 @@ assign_at :: proc{ assign_at_elem, assign_at_elems, assign_at_elem_string, + + assign_at_elem_fixed_capacity_dynamic_array, + assign_at_elems_fixed_capacity_dynamic_array, + assign_at_elem_string_fixed_capacity_dynamic_array, } @@ -877,6 +1261,16 @@ clear_dynamic_array :: proc "contextless" (array: ^$T/[dynamic]$E) { } } +// `clear_fixed_capacity_dynamic_array` will set the length of a passed dynamic array to `0` +// +// Note: Prefer the procedure group `clear`. +@builtin +clear_fixed_capacity_dynamic_array :: proc "contextless" (array: ^$T/[dynamic; $N]$E) { + if array != nil { + (^Raw_Fixed_Capacity_Dynamic_Array(N, E))(array).len = 0 + } +} + // `reserve_dynamic_array` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`). // // When a memory resize allocation is required, the memory will be asked to be zeroed (i.e. it calls `mem_resize`). @@ -996,6 +1390,43 @@ non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: i return _resize_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), length, false, loc=loc) } + + +// `resize_fixed_capacity_dynamic_array` will try to resize memory of a passed fixed capacity dynamic array or map to the requested element count (setting the `len`, and possibly `cap`). +// +// Note: Prefer the procedure group `resize` +@builtin +resize_fixed_capacity_dynamic_array :: proc "contextless" (array: ^$T/[dynamic; $N]$E, #any_int length: int) -> bool { + if array == nil { + return false + } + if len(array) < length { + size_of_elem :: size_of(E) + + num_reused := min(N, length) - len(array) + intrinsics.mem_zero(([^]byte)(array)[len(array)*size_of_elem:], num_reused*size_of_elem) + } + + raw := (^Raw_Fixed_Capacity_Dynamic_Array(N, E))(array) + new_length := clamp(length, 0, N) + raw.len = new_length + return true +} + +// `non_zero_resize_fixed_capacity_dynamic_array` will try to resize memory of a passed fixed capacity dynamic array or map to the requested element count (setting the `len`, and possibly `cap`). +// +// Note: Prefer the procedure group `resize` +@builtin +non_zero_resize_fixed_capacity_dynamic_array :: proc "contextless" (array: ^$T/[dynamic; $N]$E, #any_int length: int) -> bool { + if array == nil { + return false + } + raw := (^Raw_Fixed_Capacity_Dynamic_Array(N, E))(array) + new_length := clamp(length, 0, N) + raw.len = new_length + return true +} + // Shrinks the capacity of a dynamic array down to the current length, or the given capacity. // // If `new_cap` is negative, then `len(array)` is used. diff --git a/base/runtime/internal.odin b/base/runtime/internal.odin index cf96098b8..12f0dceca 100644 --- a/base/runtime/internal.odin +++ b/base/runtime/internal.odin @@ -718,7 +718,7 @@ quaternion256_eq :: #force_inline proc "contextless" (a, b: quaternion256) -> bo quaternion256_ne :: #force_inline proc "contextless" (a, b: quaternion256) -> bool { return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b) } -string_decode_rune :: proc "contextless" (s: string) -> (rune, int) { +string_decode_rune :: proc "contextless" (s: string) -> (rune, int) #no_bounds_check { // NOTE(bill): Duplicated here to remove dependency on package unicode/utf8 @(static, rodata) accept_sizes := [256]u8{ @@ -797,7 +797,7 @@ string_decode_rune :: proc "contextless" (s: string) -> (rune, int) { return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4 } -string_decode_last_rune :: proc "contextless" (s: string) -> (rune, int) { +string_decode_last_rune :: proc "contextless" (s: string) -> (rune, int) #no_bounds_check { RUNE_ERROR :: '\ufffd' RUNE_SELF :: 0x80 UTF_MAX :: 4 @@ -833,7 +833,7 @@ string_decode_last_rune :: proc "contextless" (s: string) -> (rune, int) { } -string16_decode_rune :: proc "contextless" (s: string16) -> (rune, int) { +string16_decode_rune :: proc "contextless" (s: string16) -> (rune, int) #no_bounds_check { REPLACEMENT_CHAR :: '\ufffd' _surr1 :: 0xd800 _surr2 :: 0xdc00 @@ -861,7 +861,7 @@ string16_decode_rune :: proc "contextless" (s: string16) -> (rune, int) { return r, w } -string16_decode_last_rune :: proc "contextless" (s: string16) -> (rune, int) { +string16_decode_last_rune :: proc "contextless" (s: string16) -> (rune, int) #no_bounds_check { REPLACEMENT_CHAR :: '\ufffd' _surr1 :: 0xd800 _surr2 :: 0xdc00 diff --git a/base/runtime/print.odin b/base/runtime/print.odin index 6569ece6c..ad205d887 100644 --- a/base/runtime/print.odin +++ b/base/runtime/print.odin @@ -392,6 +392,12 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) { print_string("[]") print_type(info.elem) + case Type_Info_Fixed_Capacity_Dynamic_Array: + print_string("[dynamic; ") + print_u64(u64(info.capacity)) + print_string("]") + print_type(info.elem) + case Type_Info_Map: print_string("map[") print_type(info.key) @@ -807,6 +813,12 @@ write_write_type :: #force_no_inline proc "contextless" (i: ^int, buf: []byte, t write_string (i, buf, "[]") or_return write_write_type(i, buf, info.elem) or_return + case Type_Info_Fixed_Capacity_Dynamic_Array: + write_string (i, buf, "[dynamic; ") or_return + write_u64 (i, buf, u64(info.capacity)) or_return + write_string (i, buf, "]") or_return + write_write_type(i, buf, info.elem) or_return + case Type_Info_Map: write_string (i, buf, "map[") or_return write_write_type(i, buf, info.key) or_return diff --git a/build.bat b/build.bat index 32c2ae658..b6e8b890d 100644 --- a/build.bat +++ b/build.bat @@ -94,6 +94,7 @@ if %release_mode% EQU 0 ( rem Debug set compiler_warnings= ^ -W4 -WX ^ -wd4100 -wd4101 -wd4127 -wd4146 ^ + -wd4324 ^ -wd4505 ^ -wd4456 -wd4457 diff --git a/core/compress/common.odin b/core/compress/common.odin index d78aec328..07ea7b71c 100644 --- a/core/compress/common.odin +++ b/core/compress/common.odin @@ -368,8 +368,6 @@ refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width := if len(z.input_data) != 0 { b = u64(z.input_data[0]) z.input_data = z.input_data[1:] - } else { - b = 0 } z.code_buffer |= b << u8(z.num_bits) diff --git a/core/compress/zlib/zlib.odin b/core/compress/zlib/zlib.odin index e484a4958..72664846e 100644 --- a/core/compress/zlib/zlib.odin +++ b/core/compress/zlib/zlib.odin @@ -326,7 +326,7 @@ decode_huffman :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bo return 0, .Code_Buffer_Malformed } compress.refill_lsb(z) - if z.num_bits > 63 { + if z.code_buffer == 0 { return 0, .Stream_Too_Short } } @@ -491,7 +491,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all */ expected_output_size = max(max(expected_output_size, compress.COMPRESS_OUTPUT_ALLOCATE_MIN), 512) - // fmt.printf("\nZLIB: Expected Payload Size: %v\n\n", expected_output_size); + // fmt.printfln("ZLIB: Expected Payload Size: %v", expected_output_size) if expected_output_size > 0 && expected_output_size <= compress.COMPRESS_OUTPUT_ALLOCATE_MAX { /* @@ -522,11 +522,16 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all final := u32(0) type := u32(0) + defer if int(z.bytes_written) != len(z.output.buf) { + resize(&z.output.buf, int(z.bytes_written)) + } + for { final = compress.read_bits_lsb(z, 1) type = compress.read_bits_lsb(z, 2) - // fmt.printf("Final: %v | Type: %v\n", final, type) + // fmt.printfln("len(z): %v", len(z.input_data)) + // fmt.printfln("Final: %v | Type: %v", final, type) switch type { case 0: @@ -561,7 +566,6 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all case 3: return .BType_3 case: - // fmt.printf("Err: %v | Final: %v | Type: %v\n", err, final, type) if type == 1 { // Use fixed code lengths. build_huffman(z_repeat, Z_FIXED_LENGTH[:]) or_return @@ -590,7 +594,6 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all for n < ntot { c = decode_huffman(z, codelength_ht) or_return - if c < 0 || c >= 19 { return .Huffman_Bad_Code_Lengths } @@ -635,15 +638,12 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all } parse_huffman_block(z, z_repeat, z_offset) or_return } + if final == 1 { break } } - if int(z.bytes_written) != len(z.output.buf) { - resize(&z.output.buf, int(z.bytes_written)) or_return - } - return nil } diff --git a/core/container/small_array/doc.odin b/core/container/small_array/doc.odin index 21d000a10..5696ae58c 100644 --- a/core/container/small_array/doc.odin +++ b/core/container/small_array/doc.odin @@ -1,4 +1,6 @@ /* +Deprecation Notice: Prefer using `[dynamic; N]T` (fixed capacity dynamic arrays). + A dynamic array-like interface on a stack-allocated, fixed-size array. The `Small_Array` type is optimal for scenarios where you need diff --git a/core/container/small_array/small_array.odin b/core/container/small_array/small_array.odin index 8498da852..901ed419e 100644 --- a/core/container/small_array/small_array.odin +++ b/core/container/small_array/small_array.odin @@ -21,7 +21,7 @@ Example: } */ Small_Array :: struct($N: int, $T: typeid) where N >= 0 { - data: [N]T, + data: [N]T `fmt:",len"`, len: int, } diff --git a/core/crypto/sha2/sha2.odin b/core/crypto/sha2/sha2.odin index dc41462e4..2eac9240b 100644 --- a/core/crypto/sha2/sha2.odin +++ b/core/crypto/sha2/sha2.odin @@ -44,7 +44,8 @@ Context_256 :: struct { length: u64, md_bits: int, - is_initialized: bool, + is_hw_accelerated: bool, + is_initialized: bool, } // Context_512 is a SHA-384, SHA-512 or SHA-512/256 instance. @@ -55,7 +56,8 @@ Context_512 :: struct { length: u64, md_bits: int, - is_initialized: bool, + is_hw_accelerated: bool, + is_initialized: bool, } // init_224 initializes a Context_256 for SHA-224. @@ -88,6 +90,9 @@ init_512_256 :: proc(ctx: ^Context_512) { _init(ctx) } +@(private) +ERR_HW_NOT_SUPPORTED :: "crypto/sha2: hardware implementation unsupported" + @(private) _init :: proc(ctx: ^$T) { when T == Context_256 { @@ -113,6 +118,8 @@ _init :: proc(ctx: ^$T) { case: panic("crypto/sha2: invalid digest output length") } + + ctx.is_hw_accelerated = is_hardware_accelerated_256() } else when T == Context_512 { switch ctx.md_bits { case 256: @@ -148,6 +155,8 @@ _init :: proc(ctx: ^$T) { case: panic("crypto/sha2: invalid digest output length") } + + ctx.is_hw_accelerated = is_hardware_accelerated_512() } ctx.length = 0 @@ -267,7 +276,7 @@ reset :: proc(ctx: ^$T) { SHA2 implementation */ -@(private, rodata) +@(private = "file", rodata) SHA256_K := [64]u32 { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, @@ -287,7 +296,7 @@ SHA256_K := [64]u32 { 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, } -@(private, rodata) +@(private = "file", rodata) SHA512_K := [80]u64 { 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, @@ -336,70 +345,70 @@ SHA256_ROUNDS :: 64 @(private) SHA512_ROUNDS :: 80 -@(private) +@(private = "file") SHA256_CH :: #force_inline proc "contextless" (x, y, z: u32) -> u32 { return (x & y) ~ (~x & z) } -@(private) +@(private = "file") SHA256_MAJ :: #force_inline proc "contextless" (x, y, z: u32) -> u32 { return (x & y) ~ (x & z) ~ (y & z) } -@(private) +@(private = "file") SHA512_CH :: #force_inline proc "contextless" (x, y, z: u64) -> u64 { return (x & y) ~ (~x & z) } -@(private) +@(private = "file") SHA512_MAJ :: #force_inline proc "contextless" (x, y, z: u64) -> u64 { return (x & y) ~ (x & z) ~ (y & z) } -@(private) +@(private = "file") SHA256_F1 :: #force_inline proc "contextless" (x: u32) -> u32 { return bits.rotate_left32(x, 30) ~ bits.rotate_left32(x, 19) ~ bits.rotate_left32(x, 10) } -@(private) +@(private = "file") SHA256_F2 :: #force_inline proc "contextless" (x: u32) -> u32 { return bits.rotate_left32(x, 26) ~ bits.rotate_left32(x, 21) ~ bits.rotate_left32(x, 7) } -@(private) +@(private = "file") SHA256_F3 :: #force_inline proc "contextless" (x: u32) -> u32 { return bits.rotate_left32(x, 25) ~ bits.rotate_left32(x, 14) ~ (x >> 3) } -@(private) +@(private = "file") SHA256_F4 :: #force_inline proc "contextless" (x: u32) -> u32 { return bits.rotate_left32(x, 15) ~ bits.rotate_left32(x, 13) ~ (x >> 10) } -@(private) +@(private = "file") SHA512_F1 :: #force_inline proc "contextless" (x: u64) -> u64 { return bits.rotate_left64(x, 36) ~ bits.rotate_left64(x, 30) ~ bits.rotate_left64(x, 25) } -@(private) +@(private = "file") SHA512_F2 :: #force_inline proc "contextless" (x: u64) -> u64 { return bits.rotate_left64(x, 50) ~ bits.rotate_left64(x, 46) ~ bits.rotate_left64(x, 23) } -@(private) +@(private = "file") SHA512_F3 :: #force_inline proc "contextless" (x: u64) -> u64 { return bits.rotate_left64(x, 63) ~ bits.rotate_left64(x, 56) ~ (x >> 7) } -@(private) +@(private = "file") SHA512_F4 :: #force_inline proc "contextless" (x: u64) -> u64 { return bits.rotate_left64(x, 45) ~ bits.rotate_left64(x, 3) ~ (x >> 6) } -@(private) +@(private = "file") sha2_transf :: proc "contextless" (ctx: ^$T, data: []byte) #no_bounds_check { when T == Context_256 { - if is_hardware_accelerated_256() { + if ctx.is_hw_accelerated { sha256_transf_hw(ctx, data) return } @@ -410,6 +419,11 @@ sha2_transf :: proc "contextless" (ctx: ^$T, data: []byte) #no_bounds_check { CURR_BLOCK_SIZE :: BLOCK_SIZE_256 } else when T == Context_512 { + if ctx.is_hw_accelerated { + sha512_transf_hw(ctx, data) + return + } + w: [SHA512_ROUNDS]u64 wv: [8]u64 t1, t2: u64 diff --git a/core/crypto/sha2/sha256_impl_hw_arm.odin b/core/crypto/sha2/sha256_impl_hw_arm.odin new file mode 100644 index 000000000..618cc6fff --- /dev/null +++ b/core/crypto/sha2/sha256_impl_hw_arm.odin @@ -0,0 +1,224 @@ +#+build arm64,arm32 +package sha2 + +// Based on the public domain code by Jeffrey Walton, though +// realistically, there only is one sensible way to write this. +// +// See: https://github.com/noloader/SHA-Intrinsics + +import "base:intrinsics" +import "core:simd" +import "core:simd/arm" +import "core:sys/info" + +// is_hardware_accelerated_256 returns true if and only if (⟺) hardware +// accelerated SHA-224/SHA-256 is supported. +is_hardware_accelerated_256 :: proc "contextless" () -> bool { + req_features :: info.CPU_Features{ + .asimd, + .sha256, + } + return info.cpu_features() >= req_features +} + +@(private = "file") +K_0 :: simd.u32x4{0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5} +@(private = "file") +K_1 :: simd.u32x4{0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5} +@(private = "file") +K_2 :: simd.u32x4{0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3} +@(private = "file") +K_3 :: simd.u32x4{0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174} +@(private = "file") +K_4 :: simd.u32x4{0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC} +@(private = "file") +K_5 :: simd.u32x4{0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA} +@(private = "file") +K_6 :: simd.u32x4{0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7} +@(private = "file") +K_7 :: simd.u32x4{0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967} +@(private = "file") +K_8 :: simd.u32x4{0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13} +@(private = "file") +K_9 :: simd.u32x4{0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85} +@(private = "file") +K_10 :: simd.u32x4{0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3} +@(private = "file") +K_11 :: simd.u32x4{0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070} +@(private = "file") +K_12 :: simd.u32x4{0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5} +@(private = "file") +K_13 :: simd.u32x4{0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3} +@(private = "file") +K_14 :: simd.u32x4{0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208} +@(private = "file") +K_15 :: simd.u32x4{0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2} + +@(private, enable_target_feature = "neon,sha2") +sha256_transf_hw :: proc "contextless" (ctx: ^Context_256, data: []byte) #no_bounds_check { + state_0 := intrinsics.unaligned_load((^simd.u32x4)(&ctx.h[0])) + state_1 := intrinsics.unaligned_load((^simd.u32x4)(&ctx.h[4])) + + data := data + for len(data) >= BLOCK_SIZE_256 { + // Save state + abef_save, cdgh_save := state_0, state_1 + + // Load message + msg_0 := intrinsics.unaligned_load((^simd.u32x4)(raw_data(data))) + msg_1 := intrinsics.unaligned_load((^simd.u32x4)(raw_data(data[16:]))) + msg_2 := intrinsics.unaligned_load((^simd.u32x4)(raw_data(data[32:]))) + msg_3 := intrinsics.unaligned_load((^simd.u32x4)(raw_data(data[48:]))) + + // Reverse for little endian + when ODIN_ENDIAN == .Little { + msg_0 = byteswap_u32x4(msg_0) + msg_1 = byteswap_u32x4(msg_1) + msg_2 = byteswap_u32x4(msg_2) + msg_3 = byteswap_u32x4(msg_3) + } + + tmp_0 := simd.add(msg_0, K_0) + + // Rounds 0-3 + msg_0 = arm.vsha256su0q_u32(msg_0, msg_1) + tmp_2 := state_0 + tmp_1 := simd.add(msg_1, K_1) + state_0 = arm.vsha256hq_u32(state_0, state_1, tmp_0) + state_1 = arm.vsha256h2q_u32(state_1, tmp_2, tmp_0) + msg_0 = arm.vsha256su1q_u32(msg_0, msg_2, msg_3) + + // Rounds 4-7 + msg_1 = arm.vsha256su0q_u32(msg_1, msg_2) + tmp_2 = state_0 + tmp_0 = simd.add(msg_2, K_2) + state_0 = arm.vsha256hq_u32(state_0, state_1, tmp_1) + state_1 = arm.vsha256h2q_u32(state_1, tmp_2, tmp_1) + msg_1 = arm.vsha256su1q_u32(msg_1, msg_3, msg_0) + + // Rounds 8-11 + msg_2 = arm.vsha256su0q_u32(msg_2, msg_3) + tmp_2 = state_0 + tmp_1 = simd.add(msg_3, K_3) + state_0 = arm.vsha256hq_u32(state_0, state_1, tmp_0) + state_1 = arm.vsha256h2q_u32(state_1, tmp_2, tmp_0) + msg_2 = arm.vsha256su1q_u32(msg_2, msg_0, msg_1) + + // Rounds 12-15 + msg_3 = arm.vsha256su0q_u32(msg_3, msg_0) + tmp_2 = state_0 + tmp_0 = simd.add(msg_0, K_4) + state_0 = arm.vsha256hq_u32(state_0, state_1, tmp_1) + state_1 = arm.vsha256h2q_u32(state_1, tmp_2, tmp_1) + msg_3 = arm.vsha256su1q_u32(msg_3, msg_1, msg_2) + + // Rounds 16-19 + msg_0 = arm.vsha256su0q_u32(msg_0, msg_1) + tmp_2 = state_0 + tmp_1 = simd.add(msg_1, K_5) + state_0 = arm.vsha256hq_u32(state_0, state_1, tmp_0) + state_1 = arm.vsha256h2q_u32(state_1, tmp_2, tmp_0) + msg_0 = arm.vsha256su1q_u32(msg_0, msg_2, msg_3) + + // Rounds 20-23 + msg_1 = arm.vsha256su0q_u32(msg_1, msg_2) + tmp_2 = state_0 + tmp_0 = simd.add(msg_2, K_6) + state_0 = arm.vsha256hq_u32(state_0, state_1, tmp_1) + state_1 = arm.vsha256h2q_u32(state_1, tmp_2, tmp_1) + msg_1 = arm.vsha256su1q_u32(msg_1, msg_3, msg_0) + + // Rounds 24-27 + msg_2 = arm.vsha256su0q_u32(msg_2, msg_3) + tmp_2 = state_0 + tmp_1 = simd.add(msg_3, K_7) + state_0 = arm.vsha256hq_u32(state_0, state_1, tmp_0) + state_1 = arm.vsha256h2q_u32(state_1, tmp_2, tmp_0) + msg_2 = arm.vsha256su1q_u32(msg_2, msg_0, msg_1) + + // Rounds 28-31 + msg_3 = arm.vsha256su0q_u32(msg_3, msg_0) + tmp_2 = state_0 + tmp_0 = simd.add(msg_0, K_8) + state_0 = arm.vsha256hq_u32(state_0, state_1, tmp_1) + state_1 = arm.vsha256h2q_u32(state_1, tmp_2, tmp_1) + msg_3 = arm.vsha256su1q_u32(msg_3, msg_1, msg_2) + + // Rounds 32-35 + msg_0 = arm.vsha256su0q_u32(msg_0, msg_1) + tmp_2 = state_0 + tmp_1 = simd.add(msg_1, K_9) + state_0 = arm.vsha256hq_u32(state_0, state_1, tmp_0) + state_1 = arm.vsha256h2q_u32(state_1, tmp_2, tmp_0) + msg_0 = arm.vsha256su1q_u32(msg_0, msg_2, msg_3) + + // Rounds 36-39 + msg_1 = arm.vsha256su0q_u32(msg_1, msg_2) + tmp_2 = state_0 + tmp_0 = simd.add(msg_2, K_10) + state_0 = arm.vsha256hq_u32(state_0, state_1, tmp_1) + state_1 = arm.vsha256h2q_u32(state_1, tmp_2, tmp_1) + msg_1 = arm.vsha256su1q_u32(msg_1, msg_3, msg_0) + + // Rounds 40-43 + msg_2 = arm.vsha256su0q_u32(msg_2, msg_3) + tmp_2 = state_0 + tmp_1 = simd.add(msg_3, K_11) + state_0 = arm.vsha256hq_u32(state_0, state_1, tmp_0) + state_1 = arm.vsha256h2q_u32(state_1, tmp_2, tmp_0) + msg_2 = arm.vsha256su1q_u32(msg_2, msg_0, msg_1) + + // Rounds 44-47 + msg_3 = arm.vsha256su0q_u32(msg_3, msg_0) + tmp_2 = state_0 + tmp_0 = simd.add(msg_0, K_12) + state_0 = arm.vsha256hq_u32(state_0, state_1, tmp_1) + state_1 = arm.vsha256h2q_u32(state_1, tmp_2, tmp_1) + msg_3 = arm.vsha256su1q_u32(msg_3, msg_1, msg_2) + + // Rounds 48-51 + tmp_2 = state_0 + tmp_1 = simd.add(msg_1, K_13) + state_0 = arm.vsha256hq_u32(state_0, state_1, tmp_0) + state_1 = arm.vsha256h2q_u32(state_1, tmp_2, tmp_0) + + // Rounds 52-55 + tmp_2 = state_0 + tmp_0 = simd.add(msg_2, K_14) + state_0 = arm.vsha256hq_u32(state_0, state_1, tmp_1) + state_1 = arm.vsha256h2q_u32(state_1, tmp_2, tmp_1) + + // Rounds 56-59 + tmp_2 = state_0 + tmp_1 = simd.add(msg_3, K_15) + state_0 = arm.vsha256hq_u32(state_0, state_1, tmp_0) + state_1 = arm.vsha256h2q_u32(state_1, tmp_2, tmp_0) + + // Rounds 60-63 + tmp_2 = state_0 + state_0 = arm.vsha256hq_u32(state_0, state_1, tmp_1) + state_1 = arm.vsha256h2q_u32(state_1, tmp_2, tmp_1) + + // Combine state + state_0 = simd.add(state_0, abef_save) + state_1 = simd.add(state_1, cdgh_save) + + data = data[BLOCK_SIZE_256:] + } + + intrinsics.unaligned_store((^simd.u32x4)(&ctx.h[0]), state_0) + intrinsics.unaligned_store((^simd.u32x4)(&ctx.h[4]), state_1) +} + +when ODIN_ENDIAN == .Little { + @(private = "file", enable_target_feature = "neon") + byteswap_u32x4 :: #force_inline proc "contextless" (a: simd.u32x4) -> simd.u32x4 { + return transmute(simd.u32x4)( + simd.shuffle( + transmute(simd.u8x16)(a), + transmute(simd.u8x16)(a), + 3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12, + ) + ) + } +} \ No newline at end of file diff --git a/core/crypto/sha2/sha2_impl_hw_gen.odin b/core/crypto/sha2/sha256_impl_hw_gen.odin similarity index 65% rename from core/crypto/sha2/sha2_impl_hw_gen.odin rename to core/crypto/sha2/sha256_impl_hw_gen.odin index 837d0656d..ad384caaa 100644 --- a/core/crypto/sha2/sha2_impl_hw_gen.odin +++ b/core/crypto/sha2/sha256_impl_hw_gen.odin @@ -1,15 +1,15 @@ #+build !amd64 +#+build !arm64 +#+build !arm32 package sha2 -@(private = "file") -ERR_HW_NOT_SUPPORTED :: "crypto/sha2: hardware implementation unsupported" - -// is_hardware_accelerated_256 returns true if and only if (⟺) hardware accelerated -// SHA-224/SHA-256 is supported. +// is_hardware_accelerated_256 returns true if and only if (⟺) hardware +// accelerated SHA-224/SHA-256 is supported. is_hardware_accelerated_256 :: proc "contextless" () -> bool { return false } +@(private) sha256_transf_hw :: proc "contextless" (ctx: ^Context_256, data: []byte) { panic_contextless(ERR_HW_NOT_SUPPORTED) } diff --git a/core/crypto/sha2/sha2_impl_hw_intel.odin b/core/crypto/sha2/sha256_impl_hw_intel.odin similarity index 95% rename from core/crypto/sha2/sha2_impl_hw_intel.odin rename to core/crypto/sha2/sha256_impl_hw_intel.odin index 3f6ebb746..fcec80a3c 100644 --- a/core/crypto/sha2/sha2_impl_hw_intel.odin +++ b/core/crypto/sha2/sha256_impl_hw_intel.odin @@ -70,8 +70,7 @@ sha256_transf_hw :: proc "contextless" (ctx: ^Context_256, data: []byte) #no_bou tmp = x86._mm_shuffle_epi32(tmp, 0xb1) // CDAB state_1 = x86._mm_shuffle_epi32(state_1, 0x1b) // EFGH state_0 := x86._mm_alignr_epi8(tmp, state_1, 8) // ABEF - // state_1 = x86._mm_blend_epi16(state_1, tmp, 0xf0) // CDGH - state_1 = kludge_mm_blend_epi16_0xf0(state_1, tmp) + state_1 = x86._mm_blend_epi16(state_1, tmp, 0xf0) // CDGH data := data for len(data) >= BLOCK_SIZE_256 { @@ -238,18 +237,9 @@ sha256_transf_hw :: proc "contextless" (ctx: ^Context_256, data: []byte) #no_bou // Write back the updated state tmp = x86._mm_shuffle_epi32(state_0, 0x1b) // FEBA state_1 = x86._mm_shuffle_epi32(state_1, 0xb1) // DCHG - // state_0 = x86._mm_blend_epi16(tmp, state_1, 0xf0) // DCBA - state_0 = kludge_mm_blend_epi16_0xf0(tmp, state_1) + state_0 = x86._mm_blend_epi16(tmp, state_1, 0xf0) // DCBA state_1 = x86._mm_alignr_epi8(state_1, tmp, 8) // ABEF intrinsics.unaligned_store((^x86.__m128i)(&ctx.h[0]), state_0) intrinsics.unaligned_store((^x86.__m128i)(&ctx.h[4]), state_1) } - -@(private = "file") -kludge_mm_blend_epi16_0xf0 :: #force_inline proc "contextless"(a, b: x86.__m128i) -> x86.__m128i { - // HACK HACK HACK: LLVM got rid of `llvm.x86.sse41.pblendw`. - a_ := simd.to_array(a) - b_ := simd.to_array(b) - return x86.__m128i{a_[0], b_[1]} -} diff --git a/core/crypto/sha2/sha512_impl_hw_arm.odin b/core/crypto/sha2/sha512_impl_hw_arm.odin new file mode 100644 index 000000000..27ef83f5a --- /dev/null +++ b/core/crypto/sha2/sha512_impl_hw_arm.odin @@ -0,0 +1,498 @@ +// The round function's intrinsic calls are based on: +// https://github.com/LostInCompilation/HashMe/blob/main/src/SHA512_Hardware.cpp +// +// The zlib License +// +// Copyright (C) 2024 Marc Schöndorf +// +// This software is provided 'as-is', without any express or implied warranty. In +// no event will the authors be held liable for any damages arising from the use of +// this software. +// +// Permission is granted to anyone to use this software for any purpose, including +// commercial applications, and to alter it and redistribute it freely, subject to +// the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not claim +// that you wrote the original software. If you use this software in a product, +// an acknowledgment in the product documentation would be appreciated but is +// not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. + +#+build arm64 +package sha2 + +import "base:intrinsics" +import "core:simd" +import "core:simd/arm" +import "core:sys/info" + +// is_hardware_accelerated_512 returns true if and only if (⟺) hardware +// accelerated SHA-384, SHA-512, and SHA-512/256 are supported. +is_hardware_accelerated_512 :: proc "contextless" () -> bool { + req_features :: info.CPU_Features{ + .asimd, + .sha512, + .sha3, // XXX: LLVM groups these under `sha3`. + } + return info.cpu_features() >= req_features +} + +@(private = "file") +K_0 :: simd.u64x2{0x428a2f98d728ae22, 0x7137449123ef65cd} +@(private = "file") +K_1 :: simd.u64x2{0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc} +@(private = "file") +K_2 :: simd.u64x2{0x3956c25bf348b538, 0x59f111f1b605d019} +@(private = "file") +K_3 :: simd.u64x2{0x923f82a4af194f9b, 0xab1c5ed5da6d8118} +@(private = "file") +K_4 :: simd.u64x2{0xd807aa98a3030242, 0x12835b0145706fbe} +@(private = "file") +K_5 :: simd.u64x2{0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2} +@(private = "file") +K_6 :: simd.u64x2{0x72be5d74f27b896f, 0x80deb1fe3b1696b1} +@(private = "file") +K_7 :: simd.u64x2{0x9bdc06a725c71235, 0xc19bf174cf692694} +@(private = "file") +K_8 :: simd.u64x2{0xe49b69c19ef14ad2, 0xefbe4786384f25e3} +@(private = "file") +K_9 :: simd.u64x2{0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65} +@(private = "file") +K_10 :: simd.u64x2{0x2de92c6f592b0275, 0x4a7484aa6ea6e483} +@(private = "file") +K_11 :: simd.u64x2{0x5cb0a9dcbd41fbd4, 0x76f988da831153b5} +@(private = "file") +K_12 :: simd.u64x2{0x983e5152ee66dfab, 0xa831c66d2db43210} +@(private = "file") +K_13 :: simd.u64x2{0xb00327c898fb213f, 0xbf597fc7beef0ee4} +@(private = "file") +K_14 :: simd.u64x2{0xc6e00bf33da88fc2, 0xd5a79147930aa725} +@(private = "file") +K_15 :: simd.u64x2{0x06ca6351e003826f, 0x142929670a0e6e70} +@(private = "file") +K_16 :: simd.u64x2{0x27b70a8546d22ffc, 0x2e1b21385c26c926} +@(private = "file") +K_17 :: simd.u64x2{0x4d2c6dfc5ac42aed, 0x53380d139d95b3df} +@(private = "file") +K_18 :: simd.u64x2{0x650a73548baf63de, 0x766a0abb3c77b2a8} +@(private = "file") +K_19 :: simd.u64x2{0x81c2c92e47edaee6, 0x92722c851482353b} +@(private = "file") +K_20 :: simd.u64x2{0xa2bfe8a14cf10364, 0xa81a664bbc423001} +@(private = "file") +K_21 :: simd.u64x2{0xc24b8b70d0f89791, 0xc76c51a30654be30} +@(private = "file") +K_22 :: simd.u64x2{0xd192e819d6ef5218, 0xd69906245565a910} +@(private = "file") +K_23 :: simd.u64x2{0xf40e35855771202a, 0x106aa07032bbd1b8} +@(private = "file") +K_24 :: simd.u64x2{0x19a4c116b8d2d0c8, 0x1e376c085141ab53} +@(private = "file") +K_25 :: simd.u64x2{0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8} +@(private = "file") +K_26 :: simd.u64x2{0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb} +@(private = "file") +K_27 :: simd.u64x2{0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3} +@(private = "file") +K_28 :: simd.u64x2{0x748f82ee5defb2fc, 0x78a5636f43172f60} +@(private = "file") +K_29 :: simd.u64x2{0x84c87814a1f0ab72, 0x8cc702081a6439ec} +@(private = "file") +K_30 :: simd.u64x2{0x90befffa23631e28, 0xa4506cebde82bde9} +@(private = "file") +K_31 :: simd.u64x2{0xbef9a3f7b2c67915, 0xc67178f2e372532b} +@(private = "file") +K_32 :: simd.u64x2{0xca273eceea26619c, 0xd186b8c721c0c207} +@(private = "file") +K_33 :: simd.u64x2{0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178} +@(private = "file") +K_34 :: simd.u64x2{0x06f067aa72176fba, 0x0a637dc5a2c898a6} +@(private = "file") +K_35 :: simd.u64x2{0x113f9804bef90dae, 0x1b710b35131c471b} +@(private = "file") +K_36 :: simd.u64x2{0x28db77f523047d84, 0x32caab7b40c72493} +@(private = "file") +K_37 :: simd.u64x2{0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c} +@(private = "file") +K_38 :: simd.u64x2{0x4cc5d4becb3e42b6, 0x597f299cfc657e2a} +@(private = "file") +K_39 :: simd.u64x2{0x5fcb6fab3ad6faec, 0x6c44198c4a475817} + +@(private, enable_target_feature = "neon,sha3") +sha512_transf_hw :: proc "contextless" (ctx: ^Context_512, data: []byte) #no_bounds_check { + state_0 := intrinsics.unaligned_load((^simd.u64x2)(&ctx.h[0])) + state_1 := intrinsics.unaligned_load((^simd.u64x2)(&ctx.h[2])) + state_2 := intrinsics.unaligned_load((^simd.u64x2)(&ctx.h[4])) + state_3 := intrinsics.unaligned_load((^simd.u64x2)(&ctx.h[6])) + + data := data + for len(data) >= BLOCK_SIZE_512 { + ab_save, cd_save, ef_save, gh_save := state_0, state_1, state_2, state_3 + + // Load message + msg_0 := intrinsics.unaligned_load((^simd.u64x2)(raw_data(data))) + msg_1 := intrinsics.unaligned_load((^simd.u64x2)(raw_data(data[16:]))) + msg_2 := intrinsics.unaligned_load((^simd.u64x2)(raw_data(data[32:]))) + msg_3 := intrinsics.unaligned_load((^simd.u64x2)(raw_data(data[48:]))) + msg_4 := intrinsics.unaligned_load((^simd.u64x2)(raw_data(data[64:]))) + msg_5 := intrinsics.unaligned_load((^simd.u64x2)(raw_data(data[80:]))) + msg_6 := intrinsics.unaligned_load((^simd.u64x2)(raw_data(data[96:]))) + msg_7 := intrinsics.unaligned_load((^simd.u64x2)(raw_data(data[112:]))) + + // Reverse for little endian + when ODIN_ENDIAN == .Little { + msg_0 = byteswap_u64x2(msg_0) + msg_1 = byteswap_u64x2(msg_1) + msg_2 = byteswap_u64x2(msg_2) + msg_3 = byteswap_u64x2(msg_3) + msg_4 = byteswap_u64x2(msg_4) + msg_5 = byteswap_u64x2(msg_5) + msg_6 = byteswap_u64x2(msg_6) + msg_7 = byteswap_u64x2(msg_7) + } + + // Rounds 0-1 + msg_k := simd.add(msg_0, K_0) + tmp_0 := simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_3) + tmp_1 := arm.vsha512hq_u64(tmp_0, simd.shuffle(state_2, state_3, 1, 2), simd.shuffle(state_1, state_2, 1, 2)) + state_3 = arm.vsha512h2q_u64(tmp_1, state_1, state_0) + state_1 = simd.add(state_1, tmp_1) + msg_0 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_0, msg_1), msg_7, simd.shuffle(msg_4, msg_5, 1, 2)) + + // Rounds 2-3 + msg_k = simd.add(msg_1, K_1) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_2) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_1, state_2, 1, 2), simd.shuffle(state_0, state_1, 1, 2)) + state_2 = arm.vsha512h2q_u64(tmp_1, state_0, state_3) + state_0 = simd.add(state_0, tmp_1) + msg_1 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_1, msg_2), msg_0, simd.shuffle(msg_5, msg_6, 1, 2)) + + // Rounds 4-5 + msg_k = simd.add(msg_2, K_2) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_1) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_0, state_1, 1, 2), simd.shuffle(state_3, state_0, 1, 2)) + state_1 = arm.vsha512h2q_u64(tmp_1, state_3, state_2) + state_3 = simd.add(state_3, tmp_1) + msg_2 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_2, msg_3), msg_1, simd.shuffle(msg_6, msg_7, 1, 2)) + + // Rounds 6-7 + msg_k = simd.add(msg_3, K_3) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_0) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_3, state_0, 1, 2), simd.shuffle(state_2, state_3, 1, 2)) + state_0 = arm.vsha512h2q_u64(tmp_1, state_2, state_1) + state_2 = simd.add(state_2, tmp_1) + msg_3 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_3, msg_4), msg_2, simd.shuffle(msg_7, msg_0, 1, 2)) + + // Rounds 8-9 + msg_k = simd.add(msg_4, K_4) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_3) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_2, state_3, 1, 2), simd.shuffle(state_1, state_2, 1, 2)) + state_3 = arm.vsha512h2q_u64(tmp_1, state_1, state_0) + state_1 = simd.add(state_1, tmp_1) + msg_4 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_4, msg_5), msg_3, simd.shuffle(msg_0, msg_1, 1, 2)) + + // Rounds 10-11 + msg_k = simd.add(msg_5, K_5) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_2) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_1, state_2, 1, 2), simd.shuffle(state_0, state_1, 1, 2)) + state_2 = arm.vsha512h2q_u64(tmp_1, state_0, state_3) + state_0 = simd.add(state_0, tmp_1) + msg_5 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_5, msg_6), msg_4, simd.shuffle(msg_1, msg_2, 1, 2)) + + // Rounds 12-13 + msg_k = simd.add(msg_6, K_6) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_1) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_0, state_1, 1, 2), simd.shuffle(state_3, state_0, 1, 2)) + state_1 = arm.vsha512h2q_u64(tmp_1, state_3, state_2) + state_3 = simd.add(state_3, tmp_1) + msg_6 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_6, msg_7), msg_5, simd.shuffle(msg_2, msg_3, 1, 2)) + + // Rounds 14-15 + msg_k = simd.add(msg_7, K_7) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_0) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_3, state_0, 1, 2), simd.shuffle(state_2, state_3, 1, 2)) + state_0 = arm.vsha512h2q_u64(tmp_1, state_2, state_1) + state_2 = simd.add(state_2, tmp_1) + msg_7 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_7, msg_0), msg_6, simd.shuffle(msg_3, msg_4, 1, 2)) + + // Rounds 16-17 + msg_k = simd.add(msg_0, K_8) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_3) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_2, state_3, 1, 2), simd.shuffle(state_1, state_2, 1, 2)) + state_3 = arm.vsha512h2q_u64(tmp_1, state_1, state_0) + state_1 = simd.add(state_1, tmp_1) + msg_0 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_0, msg_1), msg_7, simd.shuffle(msg_4, msg_5, 1, 2)) + + // Rounds 18-19 + msg_k = simd.add(msg_1, K_9) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_2) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_1, state_2, 1, 2), simd.shuffle(state_0, state_1, 1, 2)) + state_2 = arm.vsha512h2q_u64(tmp_1, state_0, state_3) + state_0 = simd.add(state_0, tmp_1) + msg_1 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_1, msg_2), msg_0, simd.shuffle(msg_5, msg_6, 1, 2)) + + // Rounds 20-21 + msg_k = simd.add(msg_2, K_10) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_1) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_0, state_1, 1, 2), simd.shuffle(state_3, state_0, 1, 2)) + state_1 = arm.vsha512h2q_u64(tmp_1, state_3, state_2) + state_3 = simd.add(state_3, tmp_1) + msg_2 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_2, msg_3), msg_1, simd.shuffle(msg_6, msg_7, 1, 2)) + + // Rounds 22-23 + msg_k = simd.add(msg_3, K_11) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_0) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_3, state_0, 1, 2), simd.shuffle(state_2, state_3, 1, 2)) + state_0 = arm.vsha512h2q_u64(tmp_1, state_2, state_1) + state_2 = simd.add(state_2, tmp_1) + msg_3 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_3, msg_4), msg_2, simd.shuffle(msg_7, msg_0, 1, 2)) + + // Rounds 24-25 + msg_k = simd.add(msg_4, K_12) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_3) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_2, state_3, 1, 2), simd.shuffle(state_1, state_2, 1, 2)) + state_3 = arm.vsha512h2q_u64(tmp_1, state_1, state_0) + state_1 = simd.add(state_1, tmp_1) + msg_4 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_4, msg_5), msg_3, simd.shuffle(msg_0, msg_1, 1, 2)) + + // Rounds 26-27 + msg_k = simd.add(msg_5, K_13) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_2) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_1, state_2, 1, 2), simd.shuffle(state_0, state_1, 1, 2)) + state_2 = arm.vsha512h2q_u64(tmp_1, state_0, state_3) + state_0 = simd.add(state_0, tmp_1) + msg_5 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_5, msg_6), msg_4, simd.shuffle(msg_1, msg_2, 1, 2)) + + // Rounds 28-29 + msg_k = simd.add(msg_6, K_14) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_1) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_0, state_1, 1, 2), simd.shuffle(state_3, state_0, 1, 2)) + state_1 = arm.vsha512h2q_u64(tmp_1, state_3, state_2) + state_3 = simd.add(state_3, tmp_1) + msg_6 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_6, msg_7), msg_5, simd.shuffle(msg_2, msg_3, 1, 2)) + + // Rounds 30-31 + msg_k = simd.add(msg_7, K_15) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_0) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_3, state_0, 1, 2), simd.shuffle(state_2, state_3, 1, 2)) + state_0 = arm.vsha512h2q_u64(tmp_1, state_2, state_1) + state_2 = simd.add(state_2, tmp_1) + msg_7 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_7, msg_0), msg_6, simd.shuffle(msg_3, msg_4, 1, 2)) + + // Rounds 32-33 + msg_k = simd.add(msg_0, K_16) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_3) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_2, state_3, 1, 2), simd.shuffle(state_1, state_2, 1, 2)) + state_3 = arm.vsha512h2q_u64(tmp_1, state_1, state_0) + state_1 = simd.add(state_1, tmp_1) + msg_0 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_0, msg_1), msg_7, simd.shuffle(msg_4, msg_5, 1, 2)) + + // Rounds 34-35 + msg_k = simd.add(msg_1, K_17) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_2) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_1, state_2, 1, 2), simd.shuffle(state_0, state_1, 1, 2)) + state_2 = arm.vsha512h2q_u64(tmp_1, state_0, state_3) + state_0 = simd.add(state_0, tmp_1) + msg_1 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_1, msg_2), msg_0, simd.shuffle(msg_5, msg_6, 1, 2)) + + // Rounds 36-37 + msg_k = simd.add(msg_2, K_18) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_1) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_0, state_1, 1, 2), simd.shuffle(state_3, state_0, 1, 2)) + state_1 = arm.vsha512h2q_u64(tmp_1, state_3, state_2) + state_3 = simd.add(state_3, tmp_1) + msg_2 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_2, msg_3), msg_1, simd.shuffle(msg_6, msg_7, 1, 2)) + + // Rounds 38-39 + msg_k = simd.add(msg_3, K_19) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_0) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_3, state_0, 1, 2), simd.shuffle(state_2, state_3, 1, 2)) + state_0 = arm.vsha512h2q_u64(tmp_1, state_2, state_1) + state_2 = simd.add(state_2, tmp_1) + msg_3 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_3, msg_4), msg_2, simd.shuffle(msg_7, msg_0, 1, 2)) + + // Rounds 40-41 + msg_k = simd.add(msg_4, K_20) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_3) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_2, state_3, 1, 2), simd.shuffle(state_1, state_2, 1, 2)) + state_3 = arm.vsha512h2q_u64(tmp_1, state_1, state_0) + state_1 = simd.add(state_1, tmp_1) + msg_4 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_4, msg_5), msg_3, simd.shuffle(msg_0, msg_1, 1, 2)) + + // Rounds 42-43 + msg_k = simd.add(msg_5, K_21) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_2) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_1, state_2, 1, 2), simd.shuffle(state_0, state_1, 1, 2)) + state_2 = arm.vsha512h2q_u64(tmp_1, state_0, state_3) + state_0 = simd.add(state_0, tmp_1) + msg_5 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_5, msg_6), msg_4, simd.shuffle(msg_1, msg_2, 1, 2)) + + // Rounds 44-45 + msg_k = simd.add(msg_6, K_22) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_1) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_0, state_1, 1, 2), simd.shuffle(state_3, state_0, 1, 2)) + state_1 = arm.vsha512h2q_u64(tmp_1, state_3, state_2) + state_3 = simd.add(state_3, tmp_1) + msg_6 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_6, msg_7), msg_5, simd.shuffle(msg_2, msg_3, 1, 2)) + + // Rounds 46-47 + msg_k = simd.add(msg_7, K_23) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_0) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_3, state_0, 1, 2), simd.shuffle(state_2, state_3, 1, 2)) + state_0 = arm.vsha512h2q_u64(tmp_1, state_2, state_1) + state_2 = simd.add(state_2, tmp_1) + msg_7 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_7, msg_0), msg_6, simd.shuffle(msg_3, msg_4, 1, 2)) + + // Rounds 48-49 + msg_k = simd.add(msg_0, K_24) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_3) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_2, state_3, 1, 2), simd.shuffle(state_1, state_2, 1, 2)) + state_3 = arm.vsha512h2q_u64(tmp_1, state_1, state_0) + state_1 = simd.add(state_1, tmp_1) + msg_0 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_0, msg_1), msg_7, simd.shuffle(msg_4, msg_5, 1, 2)) + + // Rounds 50-51 + msg_k = simd.add(msg_1, K_25) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_2) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_1, state_2, 1, 2), simd.shuffle(state_0, state_1, 1, 2)) + state_2 = arm.vsha512h2q_u64(tmp_1, state_0, state_3) + state_0 = simd.add(state_0, tmp_1) + msg_1 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_1, msg_2), msg_0, simd.shuffle(msg_5, msg_6, 1, 2)) + + // Rounds 52-53 + msg_k = simd.add(msg_2, K_26) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_1) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_0, state_1, 1, 2), simd.shuffle(state_3, state_0, 1, 2)) + state_1 = arm.vsha512h2q_u64(tmp_1, state_3, state_2) + state_3 = simd.add(state_3, tmp_1) + msg_2 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_2, msg_3), msg_1, simd.shuffle(msg_6, msg_7, 1, 2)) + + // Rounds 54-55 + msg_k = simd.add(msg_3, K_27) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_0) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_3, state_0, 1, 2), simd.shuffle(state_2, state_3, 1, 2)) + state_0 = arm.vsha512h2q_u64(tmp_1, state_2, state_1) + state_2 = simd.add(state_2, tmp_1) + msg_3 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_3, msg_4), msg_2, simd.shuffle(msg_7, msg_0, 1, 2)) + + // Rounds 56-57 + msg_k = simd.add(msg_4, K_28) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_3) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_2, state_3, 1, 2), simd.shuffle(state_1, state_2, 1, 2)) + state_3 = arm.vsha512h2q_u64(tmp_1, state_1, state_0) + state_1 = simd.add(state_1, tmp_1) + msg_4 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_4, msg_5), msg_3, simd.shuffle(msg_0, msg_1, 1, 2)) + + // Rounds 58-59 + msg_k = simd.add(msg_5, K_29) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_2) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_1, state_2, 1, 2), simd.shuffle(state_0, state_1, 1, 2)) + state_2 = arm.vsha512h2q_u64(tmp_1, state_0, state_3) + state_0 = simd.add(state_0, tmp_1) + msg_5 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_5, msg_6), msg_4, simd.shuffle(msg_1, msg_2, 1, 2)) + + // Rounds 60-61 + msg_k = simd.add(msg_6, K_30) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_1) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_0, state_1, 1, 2), simd.shuffle(state_3, state_0, 1, 2)) + state_1 = arm.vsha512h2q_u64(tmp_1, state_3, state_2) + state_3 = simd.add(state_3, tmp_1) + msg_6 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_6, msg_7), msg_5, simd.shuffle(msg_2, msg_3, 1, 2)) + + // Rounds 62-63 + msg_k = simd.add(msg_7, K_31) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_0) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_3, state_0, 1, 2), simd.shuffle(state_2, state_3, 1, 2)) + state_0 = arm.vsha512h2q_u64(tmp_1, state_2, state_1) + state_2 = simd.add(state_2, tmp_1) + msg_7 = arm.vsha512su1q_u64(arm.vsha512su0q_u64(msg_7, msg_0), msg_6, simd.shuffle(msg_3, msg_4, 1, 2)) + + // Rounds 64-65 + msg_k = simd.add(msg_0, K_32) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_3) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_2, state_3, 1, 2), simd.shuffle(state_1, state_2, 1, 2)) + state_3 = arm.vsha512h2q_u64(tmp_1, state_1, state_0) + state_1 = simd.add(state_1, tmp_1) + + // Rounds 66-67 + msg_k = simd.add(msg_1, K_33) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_2) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_1, state_2, 1, 2), simd.shuffle(state_0, state_1, 1, 2)) + state_2 = arm.vsha512h2q_u64(tmp_1, state_0, state_3) + state_0 = simd.add(state_0, tmp_1) + + // Rounds 68-69 + msg_k = simd.add(msg_2, K_34) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_1) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_0, state_1, 1, 2), simd.shuffle(state_3, state_0, 1, 2)) + state_1 = arm.vsha512h2q_u64(tmp_1, state_3, state_2) + state_3 = simd.add(state_3, tmp_1) + + // Rounds 70-71 + msg_k = simd.add(msg_3, K_35) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_0) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_3, state_0, 1, 2), simd.shuffle(state_2, state_3, 1, 2)) + state_0 = arm.vsha512h2q_u64(tmp_1, state_2, state_1) + state_2 = simd.add(state_2, tmp_1) + + // Rounds 72-73 + msg_k = simd.add(msg_4, K_36) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_3) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_2, state_3, 1, 2), simd.shuffle(state_1, state_2, 1, 2)) + state_3 = arm.vsha512h2q_u64(tmp_1, state_1, state_0) + state_1 = simd.add(state_1, tmp_1) + + // Rounds 74-75 + msg_k = simd.add(msg_5, K_37) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_2) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_1, state_2, 1, 2), simd.shuffle(state_0, state_1, 1, 2)) + state_2 = arm.vsha512h2q_u64(tmp_1, state_0, state_3) + state_0 = simd.add(state_0, tmp_1) + + // Rounds 76-77 + msg_k = simd.add(msg_6, K_38) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_1) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_0, state_1, 1, 2), simd.shuffle(state_3, state_0, 1, 2)) + state_1 = arm.vsha512h2q_u64(tmp_1, state_3, state_2) + state_3 = simd.add(state_3, tmp_1) + + // Rounds 78-79 + msg_k = simd.add(msg_7, K_39) + tmp_0 = simd.add(simd.shuffle(msg_k, msg_k, 1, 2), state_0) + tmp_1 = arm.vsha512hq_u64(tmp_0, simd.shuffle(state_3, state_0, 1, 2), simd.shuffle(state_2, state_3, 1, 2)) + state_0 = arm.vsha512h2q_u64(tmp_1, state_2, state_1) + state_2 = simd.add(state_2, tmp_1) + + // Combine state + state_0 = simd.add(state_0, ab_save) + state_1 = simd.add(state_1, cd_save) + state_2 = simd.add(state_2, ef_save) + state_3 = simd.add(state_3, gh_save) + + data = data[BLOCK_SIZE_512:] + } + + intrinsics.unaligned_store((^simd.u64x2)(&ctx.h[0]), state_0) + intrinsics.unaligned_store((^simd.u64x2)(&ctx.h[2]), state_1) + intrinsics.unaligned_store((^simd.u64x2)(&ctx.h[4]), state_2) + intrinsics.unaligned_store((^simd.u64x2)(&ctx.h[6]), state_3) +} + +when ODIN_ENDIAN == .Little { + @(private = "file", enable_target_feature = "neon") + byteswap_u64x2 :: #force_inline proc "contextless" (a: simd.u64x2) -> simd.u64x2 { + return transmute(simd.u64x2)( + simd.shuffle( + transmute(simd.u8x16)(a), + transmute(simd.u8x16)(a), + 7, 6, 5, 4, 3, 2, 1, 0, + 15, 14, 13, 12, 11, 10, 9, 8, + ) + ) + } +} diff --git a/core/crypto/sha2/sha512_impl_hw_gen.odin b/core/crypto/sha2/sha512_impl_hw_gen.odin new file mode 100644 index 000000000..701f82709 --- /dev/null +++ b/core/crypto/sha2/sha512_impl_hw_gen.odin @@ -0,0 +1,13 @@ +#+build !arm64 +package sha2 + +// is_hardware_accelerated_512 returns true if and only if (⟺) hardware +// accelerated SHA-384, SHA-512, and SHA-512/256 are supported. +is_hardware_accelerated_512 :: proc "contextless" () -> bool { + return false +} + +@(private) +sha512_transf_hw :: proc "contextless" (ctx: ^Context_512, data: []byte) { + panic_contextless(ERR_HW_NOT_SUPPORTED) +} diff --git a/core/encoding/cbor/marshal.odin b/core/encoding/cbor/marshal.odin index b23087c90..b9333bf2b 100644 --- a/core/encoding/cbor/marshal.odin +++ b/core/encoding/cbor/marshal.odin @@ -285,6 +285,32 @@ _marshal_into_encoder :: proc(e: Encoder, v: any, ti: ^runtime.Type_Info) -> (er } return + + case runtime.Type_Info_Fixed_Capacity_Dynamic_Array: + array_data := uintptr(v.data) + array_len := (^int)(array_data + info.len_offset)^ + if info.elem.id == byte { + raw := runtime.Raw_Slice{v.data, array_len} + return err_conv(_encode_bytes(e, transmute([]byte)raw)) + } + + err_conv(_encode_u64(e, u64(array_len), .Array)) or_return + + if impl, ok := _tag_implementations_type[info.elem.id]; ok { + for i in 0..marshal(e, any{rawptr(data), info.elem.id}) or_return + } + return + } + + elem_ti := runtime.type_info_core(type_info_of(info.elem.id)) + for i in 0.. t.capacity { return _unsupported(v, hdr) } + + // Copy into array type, delete original. + slice := ([^]byte)(v.data)[:len(bytes)] + n := copy(slice, bytes) + assert(n == len(bytes)) + (^int)(uintptr(v.data) + t.len_offset)^ = n + return } return _unsupported(v, hdr) @@ -553,6 +570,21 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header if out_of_space { return _unsupported(v, hdr) } return + case reflect.Type_Info_Fixed_Capacity_Dynamic_Array: + length, _ := err_conv(_decode_len_container(d, add)) or_return + if length > t.capacity { + return _unsupported(v, hdr) + } + + da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, length, allocator } + + out_of_space := assign_array(d, &da, t.elem, length, growable=false) or_return + if out_of_space { return _unsupported(v, hdr) } + + (^int)(uintptr(v.data) + t.len_offset)^ = length + + return + case reflect.Type_Info_Complex: length, _ := err_conv(_decode_len_container(d, add)) or_return if length > 2 { @@ -661,8 +693,7 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, unknown := length == -1 fields := reflect.struct_fields_zipped(ti.id) - idx := 0 - for ; idx < len(fields) && (unknown || idx < length); idx += 1 { + for idx := 0; unknown || idx < length; idx += 1 { // Decode key, keys can only be strings. key: string if keyv, kerr := decode_key(d, v, context.temp_allocator); unknown && kerr == .Break { @@ -710,16 +741,6 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, _unmarshal_value(d, fany, _decode_header(r) or_return) or_return } - // If there are fields left in the map that did not get decoded into the struct, decode and discard them. - if !unknown { - for _ in idx.. (err: } opt_write_end(w, opt, ']') or_return + case runtime.Type_Info_Fixed_Capacity_Dynamic_Array: + opt_write_start(w, opt, '[') or_return + len := (^int)(uintptr(v.data) + info.len_offset)^ + for i in 0..', &fi.n) diff --git a/core/math/big/prime.odin b/core/math/big/prime.odin index 1c772143b..7fa6d8e4a 100644 --- a/core/math/big/prime.odin +++ b/core/math/big/prime.odin @@ -101,7 +101,7 @@ internal_int_power_modulo :: proc(res, G, X, P: ^Int, allocator := context.alloc If the modulus is odd or dr != 0 use the montgomery method. */ if internal_int_is_odd(P) || dr != 0 { - return _private_int_exponent_mod(res, G, X, P, dr) + return _private_int_exponent_mod_fast(res, G, X, P, dr) } /* diff --git a/core/math/big/private.odin b/core/math/big/private.odin index 506f68165..1ca706c6d 100644 --- a/core/math/big/private.odin +++ b/core/math/big/private.odin @@ -439,8 +439,14 @@ _private_int_mul_high :: proc(dest, a, b: ^Int, digits: int, allocator := contex return _private_int_mul_high_comba(dest, a, b, digits) } - internal_grow(dest, a.used + b.used + 1) or_return - dest.used = a.used + b.used + 1 + /* + Set up temporary output `Int`, which we'll swap for `dest` when done. + */ + + t := &Int{} + + internal_grow(t, a.used + b.used + 1) or_return + t.used = a.used + b.used + 1 pa := a.used pb := b.used @@ -451,20 +457,23 @@ _private_int_mul_high :: proc(dest, a, b: ^Int, digits: int, allocator := contex /* Calculate the double precision result. */ - r := _WORD(dest.digit[ix + iy]) + _WORD(a.digit[ix]) * _WORD(b.digit[iy]) + _WORD(carry) + r := _WORD(t.digit[ix + iy]) + _WORD(a.digit[ix]) * _WORD(b.digit[iy]) + _WORD(carry) /* Get the lower part. */ - dest.digit[ix + iy] = DIGIT(r & _WORD(_MASK)) + t.digit[ix + iy] = DIGIT(r & _WORD(_MASK)) /* Carry the carry. */ carry = DIGIT(r >> _WORD(_DIGIT_BITS)) } - dest.digit[ix + pb] = carry + t.digit[ix + pb] = carry } + + internal_swap(dest, t) + internal_destroy(t) return internal_clamp(dest) } diff --git a/core/nbio/impl_posix.odin b/core/nbio/impl_posix.odin index 3ecb5d8a3..0d3f57e9c 100644 --- a/core/nbio/impl_posix.odin +++ b/core/nbio/impl_posix.odin @@ -11,7 +11,6 @@ import "core:strings" import "core:sys/posix" import "core:time" import kq "core:sys/kqueue" -import sa "core:container/small_array" @(private="package") _FULLY_SUPPORTED :: true @@ -23,7 +22,7 @@ _Event_Loop :: struct { // that would be the same (ident, filter) pair we need to bundle the operations under one kevent. submitted: map[Queue_Identifier]^Operation, // Holds all events we want to flush. Flushing is done each tick at which point this is emptied. - pending: sa.Small_Array(QUEUE_SIZE, kq.KEvent), + pending: [dynamic; QUEUE_SIZE]kq.KEvent, // Holds what should be in `pending` but didn't fit. // When `pending`is flushed these are moved to `pending`. overflow: queue.Queue(kq.KEvent), @@ -116,7 +115,7 @@ _init :: proc(l: ^Event_Loop, allocator: mem.Allocator) -> (rerr: General_Error) l.kqueue = kqueue - sa.append(&l.pending, kq.KEvent{ + append(&l.pending, kq.KEvent{ ident = IDENT_WAKE_UP, filter = .User, flags = {.Add, .Enable, .Clear}, @@ -150,7 +149,7 @@ __tick :: proc(l: ^Event_Loop, timeout: time.Duration) -> General_Error { } if NBIO_DEBUG { - npending := sa.len(l.pending) + npending := len(l.pending) if npending > 0 { debug("queueing", npending, "new events, there are", int(len(l.submitted)), "events pending") } else { @@ -177,9 +176,9 @@ __tick :: proc(l: ^Event_Loop, timeout: time.Duration) -> General_Error { results_buf: [128]kq.KEvent results := kevent(l, results_buf[:], ts_pointer) or_return - sa.clear(&l.pending) + clear(&l.pending) for overflow in queue.pop_front_safe(&l.overflow) { - sa.append(&l.pending, overflow) or_break + (append(&l.pending, overflow) != 0) or_break } l.now = time.now() @@ -202,7 +201,7 @@ __tick :: proc(l: ^Event_Loop, timeout: time.Duration) -> General_Error { kevent :: proc(l: ^Event_Loop, buf: []kq.KEvent, ts: ^posix.timespec) -> ([]kq.KEvent, General_Error) { for { - new_events, err := kq.kevent(l.kqueue, sa.slice(&l.pending), buf, ts) + new_events, err := kq.kevent(l.kqueue, l.pending[:], buf, ts) #partial switch err { case nil: assert(new_events >= 0) @@ -1188,7 +1187,7 @@ add_pending :: proc(op: ^Operation, filter: kq.Filter, ident: uintptr) { } append_pending :: #force_inline proc(l: ^Event_Loop, ev: kq.KEvent) { - if !sa.append(&l.pending, ev) { + if append(&l.pending, ev) == 0 { warn("queue is full, adding to overflow, should QUEUE_SIZE be increased?") _, err := queue.append(&l.overflow, ev) ensure(err == nil, "allocation failure") @@ -1325,7 +1324,7 @@ timeout_and_delete :: proc(target: ^Operation) { flags = {.Add, .Enable, .One_Shot}, udata = target._impl.next, } - if !sa.append(&target.l.pending, ev) { + if append(&target.l.pending, ev) == 0 { warn("just removed the head operation of a list of multiple, and the queue is full, have to force this update through inefficiently") // This has to happen the next time we submit or we could have udata pointing wrong. // Very inefficient but probably never hit. diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index 2cee6e385..22139d7f9 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -786,6 +786,16 @@ Dynamic_Array_Type :: struct { elem: ^Expr, } +Fixed_Capacity_Dynamic_Array_Type :: struct { + using node: Expr, + tag: ^Expr, // possibly nil + open: tokenizer.Pos, + dynamic_pos: tokenizer.Pos, + capacity: ^Expr, + close: tokenizer.Pos, + elem: ^Expr, +} + Struct_Type :: struct { using node: Expr, tok_pos: tokenizer.Pos, @@ -931,6 +941,7 @@ Any_Node :: union { ^Multi_Pointer_Type, ^Array_Type, ^Dynamic_Array_Type, + ^Fixed_Capacity_Dynamic_Array_Type, ^Struct_Type, ^Union_Type, ^Enum_Type, @@ -1017,6 +1028,7 @@ Any_Expr :: union { ^Multi_Pointer_Type, ^Array_Type, ^Dynamic_Array_Type, + ^Fixed_Capacity_Dynamic_Array_Type, ^Struct_Type, ^Union_Type, ^Enum_Type, diff --git a/core/odin/ast/clone.odin b/core/odin/ast/clone.odin index df3e1df0d..163485840 100644 --- a/core/odin/ast/clone.odin +++ b/core/odin/ast/clone.odin @@ -311,10 +311,16 @@ clone_node :: proc(node: ^Node) -> ^Node { case ^Multi_Pointer_Type: r.elem = clone(r.elem) case ^Array_Type: + r.tag = clone(r.tag) r.len = clone(r.len) r.elem = clone(r.elem) case ^Dynamic_Array_Type: + r.tag = clone(r.tag) r.elem = clone(r.elem) + case ^Fixed_Capacity_Dynamic_Array_Type: + r.tag = clone(r.tag) + r.capacity = clone(r.capacity) + r.elem = clone(r.elem) case ^Struct_Type: r.poly_params = auto_cast clone(r.poly_params) r.align = clone(r.align) diff --git a/core/odin/ast/walk.odin b/core/odin/ast/walk.odin index 5b9340c62..d3824c213 100644 --- a/core/odin/ast/walk.odin +++ b/core/odin/ast/walk.odin @@ -380,6 +380,12 @@ walk :: proc(v: ^Visitor, node: ^Node) { walk(v, n.tag) } walk(v, n.elem) + case ^Fixed_Capacity_Dynamic_Array_Type: + if n.tag != nil { + walk(v, n.tag) + } + walk(v, n.capacity) + walk(v, n.elem) case ^Struct_Type: if n.poly_params != nil { walk(v, n.poly_params) diff --git a/core/odin/doc-format/doc_format.odin b/core/odin/doc-format/doc_format.odin index df36c1b55..28552b346 100644 --- a/core/odin/doc-format/doc_format.odin +++ b/core/odin/doc-format/doc_format.odin @@ -12,7 +12,7 @@ String :: distinct Array(byte) Version_Type_Major :: 0 Version_Type_Minor :: 3 -Version_Type_Patch :: 1 +Version_Type_Patch :: 2 Version_Type :: struct { major, minor, patch: u8, @@ -168,32 +168,33 @@ Attribute :: struct { } Type_Kind :: enum u32le { - Invalid = 0, - Basic = 1, - Named = 2, - Generic = 3, - Pointer = 4, - Array = 5, - Enumerated_Array = 6, - Slice = 7, - Dynamic_Array = 8, - Map = 9, - Struct = 10, - Union = 11, - Enum = 12, - Parameters = 13, - Proc = 14, - Bit_Set = 15, - Simd_Vector = 16, - SOA_Struct_Fixed = 17, - SOA_Struct_Slice = 18, - SOA_Struct_Dynamic = 19, - Relative_Pointer = 20, - Relative_Multi_Pointer = 21, - Multi_Pointer = 22, - Matrix = 23, - Soa_Pointer = 24, - Bit_Field = 25, + Invalid = 0, + Basic = 1, + Named = 2, + Generic = 3, + Pointer = 4, + Array = 5, + Enumerated_Array = 6, + Slice = 7, + Dynamic_Array = 8, + Map = 9, + Struct = 10, + Union = 11, + Enum = 12, + Parameters = 13, + Proc = 14, + Bit_Set = 15, + Simd_Vector = 16, + SOA_Struct_Fixed = 17, + SOA_Struct_Slice = 18, + SOA_Struct_Dynamic = 19, + Relative_Pointer = 20, + Relative_Multi_Pointer = 21, + Multi_Pointer = 22, + Matrix = 23, + Soa_Pointer = 24, + Bit_Field = 25, + Fixed_Capacity_Dynamic_Array = 26, } Type_Elems_Cap :: 4 @@ -219,13 +220,14 @@ Type :: struct { custom_align: String, // Used by: - // .Array - 1 count: 0=len - // .Enumerated_Array - 1 count: 0=len - // .SOA_Struct_Fixed - 1 count: 0=len - // .Bit_Set - 2 count: 0=lower, 1=upper - // .Simd_Vector - 1 count: 0=len - // .Matrix - 2 count: 0=row_count, 1=column_count - // .Struct - <=2 count: 0=min_field_align, 1=max_field_align + // .Array - 1 count: 0=len + // .Enumerated_Array - 1 count: 0=len + // .SOA_Struct_Fixed - 1 count: 0=len + // .Bit_Set - 2 count: 0=lower, 1=upper + // .Simd_Vector - 1 count: 0=len + // .Matrix - 2 count: 0=row_count, 1=column_count + // .Struct - <=2 count: 0=min_field_align, 1=max_field_align + // .Fixed_Capacity_Dynamic_Array - 1 count: 0=cap elem_count_len: u32le, elem_counts: [Type_Elems_Cap]i64le, @@ -234,27 +236,28 @@ Type :: struct { calling_convention: String, // Used by: - // .Named - 1 type: 0=base type - // .Generic - <1 type: 0=specialization - // .Pointer - 1 type: 0=element - // .Array - 1 type: 0=element - // .Enumerated_Array - 2 types: 0=index and 1=element - // .Slice - 1 type: 0=element - // .Dynamic_Array - 1 type: 0=element - // .Map - 2 types: 0=key, 1=value - // .SOA_Struct_Fixed - 1 type: underlying SOA struct element - // .SOA_Struct_Slice - 1 type: underlying SOA struct element - // .SOA_Struct_Dynamic - 1 type: underlying SOA struct element - // .Union - 0+ types: variants - // .Enum - <1 type: 0=base type - // .Proc - 2 types: 0=parameters, 1=results - // .Bit_Set - <=2 types: 0=element type, 1=underlying type (Underlying_Type flag will be set) - // .Simd_Vector - 1 type: 0=element - // .Relative_Pointer - 2 types: 0=pointer type, 1=base integer - // .Multi_Pointer - 1 type: 0=element - // .Matrix - 1 type: 0=element - // .Soa_Pointer - 1 type: 0=element - // .Bit_Field - 1 type: 0=backing type + // .Named - 1 type: 0=base type + // .Generic - <1 type: 0=specialization + // .Pointer - 1 type: 0=element + // .Array - 1 type: 0=element (and 1=generic index (if exists)) + // .Enumerated_Array - 2 types: 0=index and 1=element + // .Slice - 1 type: 0=element + // .Dynamic_Array - 1 type: 0=element + // .Map - 2 types: 0=key, 1=value + // .SOA_Struct_Fixed - 1 type: underlying SOA struct element + // .SOA_Struct_Slice - 1 type: underlying SOA struct element + // .SOA_Struct_Dynamic - 1 type: underlying SOA struct element + // .Union - 0+ types: variants + // .Enum - <1 type: 0=base type + // .Proc - 2 types: 0=parameters, 1=results + // .Bit_Set - <=2 types: 0=element type, 1=underlying type (Underlying_Type flag will be set) + // .Simd_Vector - 1 type: 0=element + // .Relative_Pointer - 2 types: 0=pointer type, 1=base integer + // .Multi_Pointer - 1 type: 0=element + // .Matrix - 1 type: 0=element + // .Soa_Pointer - 1 type: 0=element + // .Bit_Field - 1 type: 0=backing type + // .Fixed_Capacity_Dynamic_Array - 1 type: 0=element (and 1=generic index (if exists)) types: Array(Type_Index), // Used by: diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 643673c69..0f3ac78b2 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -2389,6 +2389,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { case ^ast.Array_Type: t.tag = bd case ^ast.Dynamic_Array_Type: t.tag = bd case ^ast.Pointer_Type: t.tag = bd + case ^ast.Fixed_Capacity_Dynamic_Array_Type: t.tag = bd case: error(p, original_type.pos, "expected an array or pointer type after #%s", name.text) } @@ -2626,6 +2627,20 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { return t case .Dynamic: tok := expect_token(p, .Dynamic) + if allow_token(p, .Semicolon) { + capacity := parse_expr(p, false) + close := expect_token(p, .Close_Bracket) + elem := parse_type(p) + + da := ast.new(ast.Fixed_Capacity_Dynamic_Array_Type, open.pos, elem) + da.open = open.pos + da.dynamic_pos = tok.pos + da.capacity = capacity + da.close = close.pos + da.elem = elem + return da + } + close := expect_token(p, .Close_Bracket) elem := parse_type(p) da := ast.new(ast.Dynamic_Array_Type, open.pos, elem) @@ -3107,6 +3122,7 @@ is_literal_type :: proc(expr: ^ast.Expr) -> bool { ^ast.Union_Type, ^ast.Enum_Type, ^ast.Dynamic_Array_Type, + ^ast.Fixed_Capacity_Dynamic_Array_Type, ^ast.Map_Type, ^ast.Bit_Set_Type, ^ast.Matrix_Type, diff --git a/core/os/path_linux.odin b/core/os/path_linux.odin index cba4b4e98..8911e6986 100644 --- a/core/os/path_linux.odin +++ b/core/os/path_linux.odin @@ -71,7 +71,7 @@ _mkdir_all :: proc(path: string, perm: Permissions) -> Error { if errno != .NONE { return _get_platform_error(errno) } - + has_created: bool mkdirat(dfd, path_bytes, perm, &has_created) or_return return nil if has_created else .Exist @@ -147,13 +147,13 @@ _remove_all :: proc(path: string) -> Error { return _get_platform_error(linux.rmdir(path_cstr)) } -_get_working_directory :: proc(allocator: runtime.Allocator) -> (string, Error) { +_get_working_directory :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) { // NOTE(tetra): I would use PATH_MAX here, but I was not able to find // an authoritative value for it across all systems. // The largest value I could find was 4096, so might as well use the page size. // NOTE(jason): Avoiding libc, so just use 4096 directly PATH_MAX :: 4096 - buf := make([dynamic]u8, PATH_MAX, allocator) + buf := make([dynamic]u8, PATH_MAX, allocator) or_return for { #no_bounds_check n, errno := linux.getcwd(buf[:]) if errno == .NONE { @@ -162,7 +162,7 @@ _get_working_directory :: proc(allocator: runtime.Allocator) -> (string, Error) if errno != .ERANGE { return "", _get_platform_error(errno) } - resize(&buf, len(buf)+PATH_MAX) + resize(&buf, len(buf)+PATH_MAX) or_return } unreachable() } diff --git a/core/os/path_windows.odin b/core/os/path_windows.odin index 1a0245c52..b471ab0b0 100644 --- a/core/os/path_windows.odin +++ b/core/os/path_windows.odin @@ -1,6 +1,7 @@ #+private package os +import "base:intrinsics" import "base:runtime" import "core:strings" import win32 "core:sys/windows" @@ -157,18 +158,30 @@ _get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err } } -@(private) -can_use_long_paths: bool -@(init) -init_long_path_support :: proc "contextless" () { +@(require_results) +has_long_path_support :: proc "contextless" () -> bool { + @(static) + can_use_long_paths_lock: win32.SRWLOCK + @(static) + can_use_long_paths_checked: bool // atomic + @(static) + can_use_long_paths: bool + + if intrinsics.atomic_load_explicit(&can_use_long_paths_checked, .Acquire) == true { + return can_use_long_paths + } + win32.AcquireSRWLockExclusive(&can_use_long_paths_lock) + defer win32.ReleaseSRWLockExclusive(&can_use_long_paths_lock) + defer intrinsics.atomic_store_explicit(&can_use_long_paths_checked, true, .Release) + can_use_long_paths = false key: win32.HKEY res := win32.RegOpenKeyExW(win32.HKEY_LOCAL_MACHINE, win32.L(`SYSTEM\CurrentControlSet\Control\FileSystem`), 0, win32.KEY_READ, &key) defer win32.RegCloseKey(key) if res != 0 { - return + return can_use_long_paths } value: u32 @@ -183,11 +196,13 @@ init_long_path_support :: proc "contextless" () { &size, ) if res != 0 { - return + return can_use_long_paths } if value == 1 { can_use_long_paths = true } + + return can_use_long_paths } @(require_results) @@ -202,10 +217,11 @@ _fix_long_path :: proc(path: string, allocator: runtime.Allocator) -> (win32.wst @(require_results) _fix_long_path_internal :: proc(path: string) -> string { - if can_use_long_paths { + if has_long_path_support() { return path } + // When using win32 to create a directory, the path // cannot be too long that you cannot append an 8.3 // file name, because MAX_PATH is 260, 260-12 = 248 diff --git a/core/reflect/iterator.odin b/core/reflect/iterator.odin index e96019f68..d283f190f 100644 --- a/core/reflect/iterator.odin +++ b/core/reflect/iterator.odin @@ -44,6 +44,15 @@ iterate_array :: proc(val: any, it: ^int) -> (elem: any, index: int, ok: bool) { index = it^ it^ += 1 } + case Type_Info_Fixed_Capacity_Dynamic_Array: + count := (^int)(uintptr(val.data) + info.len_offset)^ + if it^ < count { + elem.data = rawptr(uintptr(val.data) + uintptr(it^ * info.elem_size)) + elem.id = info.elem.id + ok = true + index = it^ + it^ += 1 + } } return diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin index b39c6ac6b..e7115fc96 100644 --- a/core/reflect/reflect.odin +++ b/core/reflect/reflect.odin @@ -6,33 +6,34 @@ _ :: intrinsics Type_Info :: runtime.Type_Info -Type_Info_Named :: runtime.Type_Info_Named -Type_Info_Integer :: runtime.Type_Info_Integer -Type_Info_Rune :: runtime.Type_Info_Rune -Type_Info_Float :: runtime.Type_Info_Float -Type_Info_Complex :: runtime.Type_Info_Complex -Type_Info_Quaternion :: runtime.Type_Info_Quaternion -Type_Info_String :: runtime.Type_Info_String -Type_Info_Boolean :: runtime.Type_Info_Boolean -Type_Info_Any :: runtime.Type_Info_Any -Type_Info_Type_Id :: runtime.Type_Info_Type_Id -Type_Info_Pointer :: runtime.Type_Info_Pointer -Type_Info_Multi_Pointer :: runtime.Type_Info_Multi_Pointer -Type_Info_Procedure :: runtime.Type_Info_Procedure -Type_Info_Array :: runtime.Type_Info_Array -Type_Info_Enumerated_Array :: runtime.Type_Info_Enumerated_Array -Type_Info_Dynamic_Array :: runtime.Type_Info_Dynamic_Array -Type_Info_Slice :: runtime.Type_Info_Slice -Type_Info_Parameters :: runtime.Type_Info_Parameters -Type_Info_Struct :: runtime.Type_Info_Struct -Type_Info_Union :: runtime.Type_Info_Union -Type_Info_Enum :: runtime.Type_Info_Enum -Type_Info_Map :: runtime.Type_Info_Map -Type_Info_Bit_Set :: runtime.Type_Info_Bit_Set -Type_Info_Simd_Vector :: runtime.Type_Info_Simd_Vector -Type_Info_Matrix :: runtime.Type_Info_Matrix -Type_Info_Soa_Pointer :: runtime.Type_Info_Soa_Pointer -Type_Info_Bit_Field :: runtime.Type_Info_Bit_Field +Type_Info_Named :: runtime.Type_Info_Named +Type_Info_Integer :: runtime.Type_Info_Integer +Type_Info_Rune :: runtime.Type_Info_Rune +Type_Info_Float :: runtime.Type_Info_Float +Type_Info_Complex :: runtime.Type_Info_Complex +Type_Info_Quaternion :: runtime.Type_Info_Quaternion +Type_Info_String :: runtime.Type_Info_String +Type_Info_Boolean :: runtime.Type_Info_Boolean +Type_Info_Any :: runtime.Type_Info_Any +Type_Info_Type_Id :: runtime.Type_Info_Type_Id +Type_Info_Pointer :: runtime.Type_Info_Pointer +Type_Info_Multi_Pointer :: runtime.Type_Info_Multi_Pointer +Type_Info_Procedure :: runtime.Type_Info_Procedure +Type_Info_Array :: runtime.Type_Info_Array +Type_Info_Enumerated_Array :: runtime.Type_Info_Enumerated_Array +Type_Info_Dynamic_Array :: runtime.Type_Info_Dynamic_Array +Type_Info_Slice :: runtime.Type_Info_Slice +Type_Info_Parameters :: runtime.Type_Info_Parameters +Type_Info_Struct :: runtime.Type_Info_Struct +Type_Info_Union :: runtime.Type_Info_Union +Type_Info_Enum :: runtime.Type_Info_Enum +Type_Info_Map :: runtime.Type_Info_Map +Type_Info_Bit_Set :: runtime.Type_Info_Bit_Set +Type_Info_Simd_Vector :: runtime.Type_Info_Simd_Vector +Type_Info_Matrix :: runtime.Type_Info_Matrix +Type_Info_Soa_Pointer :: runtime.Type_Info_Soa_Pointer +Type_Info_Bit_Field :: runtime.Type_Info_Bit_Field +Type_Info_Fixed_Capacity_Dynamic_Array :: runtime.Type_Info_Fixed_Capacity_Dynamic_Array Type_Info_Enum_Value :: runtime.Type_Info_Enum_Value @@ -67,6 +68,7 @@ Type_Kind :: enum { Matrix, Soa_Pointer, Bit_Field, + Fixed_Capacity_Dynamic_Array, } @@ -76,33 +78,34 @@ type_kind :: proc(T: typeid) -> Type_Kind { ti := type_info_of(T) if ti != nil { switch _ in ti.variant { - case Type_Info_Named: return .Named - case Type_Info_Integer: return .Integer - case Type_Info_Rune: return .Rune - case Type_Info_Float: return .Float - case Type_Info_Complex: return .Complex - case Type_Info_Quaternion: return .Quaternion - case Type_Info_String: return .String - case Type_Info_Boolean: return .Boolean - case Type_Info_Any: return .Any - case Type_Info_Type_Id: return .Type_Id - case Type_Info_Pointer: return .Pointer - case Type_Info_Multi_Pointer: return .Multi_Pointer - case Type_Info_Procedure: return .Procedure - case Type_Info_Array: return .Array - case Type_Info_Enumerated_Array: return .Enumerated_Array - case Type_Info_Dynamic_Array: return .Dynamic_Array - case Type_Info_Slice: return .Slice - case Type_Info_Parameters: return .Parameters - case Type_Info_Struct: return .Struct - case Type_Info_Union: return .Union - case Type_Info_Enum: return .Enum - case Type_Info_Map: return .Map - case Type_Info_Bit_Set: return .Bit_Set - case Type_Info_Simd_Vector: return .Simd_Vector - case Type_Info_Matrix: return .Matrix - case Type_Info_Soa_Pointer: return .Soa_Pointer - case Type_Info_Bit_Field: return .Bit_Field + case Type_Info_Named: return .Named + case Type_Info_Integer: return .Integer + case Type_Info_Rune: return .Rune + case Type_Info_Float: return .Float + case Type_Info_Complex: return .Complex + case Type_Info_Quaternion: return .Quaternion + case Type_Info_String: return .String + case Type_Info_Boolean: return .Boolean + case Type_Info_Any: return .Any + case Type_Info_Type_Id: return .Type_Id + case Type_Info_Pointer: return .Pointer + case Type_Info_Multi_Pointer: return .Multi_Pointer + case Type_Info_Procedure: return .Procedure + case Type_Info_Array: return .Array + case Type_Info_Enumerated_Array: return .Enumerated_Array + case Type_Info_Dynamic_Array: return .Dynamic_Array + case Type_Info_Slice: return .Slice + case Type_Info_Parameters: return .Parameters + case Type_Info_Struct: return .Struct + case Type_Info_Union: return .Union + case Type_Info_Enum: return .Enum + case Type_Info_Map: return .Map + case Type_Info_Bit_Set: return .Bit_Set + case Type_Info_Simd_Vector: return .Simd_Vector + case Type_Info_Matrix: return .Matrix + case Type_Info_Soa_Pointer: return .Soa_Pointer + case Type_Info_Bit_Field: return .Bit_Field + case Type_Info_Fixed_Capacity_Dynamic_Array: return .Fixed_Capacity_Dynamic_Array } } @@ -215,6 +218,7 @@ typeid_elem :: proc(id: typeid) -> typeid { case Type_Info_Slice: return v.elem.id case Type_Info_Dynamic_Array: return v.elem.id case Type_Info_Simd_Vector: return v.elem.id + case Type_Info_Fixed_Capacity_Dynamic_Array: return v.elem.id } return id } @@ -305,6 +309,9 @@ length :: proc(val: any) -> int { case Type_Info_Dynamic_Array: return (^runtime.Raw_Dynamic_Array)(val.data).len + case Type_Info_Fixed_Capacity_Dynamic_Array: + return (^int)(uintptr(val.data) + a.len_offset)^ + case Type_Info_Map: return runtime.map_len((^runtime.Raw_Map)(val.data)^) @@ -357,6 +364,9 @@ capacity :: proc(val: any) -> int { case Type_Info_Dynamic_Array: return (^runtime.Raw_Dynamic_Array)(val.data).cap + case Type_Info_Fixed_Capacity_Dynamic_Array: + return a.capacity + case Type_Info_Map: return runtime.map_cap((^runtime.Raw_Map)(val.data)^) @@ -417,6 +427,13 @@ index :: proc(val: any, i: int, loc := #caller_location) -> any { data := rawptr(uintptr(raw.data) + offset) return any{data, a.elem.id} + case Type_Info_Fixed_Capacity_Dynamic_Array: + count := (^int)(uintptr(val.data) + a.len_offset)^ + runtime.bounds_check_error_loc(loc, i, count) + offset := uintptr(a.elem.size * i) + data := rawptr(uintptr(val.data) + offset) + return any{data, a.elem.id} + case Type_Info_String: if a.is_cstring { return nil } @@ -1773,6 +1790,10 @@ as_raw_data :: proc(a: any) -> (value: rawptr, valid: bool) { valid = true value = (^runtime.Raw_Slice)(a.data).data + case Type_Info_Fixed_Capacity_Dynamic_Array: + valid = true + value = a.data + case Type_Info_Dynamic_Array: valid = true value = (^runtime.Raw_Dynamic_Array)(a.data).data @@ -1919,6 +1940,23 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_ } } return true + case Type_Info_Fixed_Capacity_Dynamic_Array: + a_count := (^int)(uintptr(a.data) + v.len_offset)^ + b_count := (^int)(uintptr(b.data) + v.len_offset)^ + if a_count != b_count { + return false + } + + for i in 0.. bool { return x.signed == y.signed && x.endianness == y.endianness case Type_Info_Rune: - _, ok := b.variant.(Type_Info_Rune) - return ok + _ = b.variant.(Type_Info_Rune) or_return + return true case Type_Info_Float: - _, ok := b.variant.(Type_Info_Float) - return ok + y := b.variant.(Type_Info_Float) or_return + return x.endianness == y.endianness case Type_Info_Complex: - _, ok := b.variant.(Type_Info_Complex) - return ok + _ = b.variant.(Type_Info_Complex) or_return + return true case Type_Info_Quaternion: - _, ok := b.variant.(Type_Info_Quaternion) - return ok + _ = b.variant.(Type_Info_Quaternion) or_return + return true case Type_Info_Type_Id: - _, ok := b.variant.(Type_Info_Type_Id) - return ok + _ = b.variant.(Type_Info_Type_Id) or_return + return true case Type_Info_String: - _, ok := b.variant.(Type_Info_String) - return ok + y := b.variant.(Type_Info_String) or_return + return x.is_cstring == y.is_cstring && x.encoding == y.encoding case Type_Info_Boolean: - _, ok := b.variant.(Type_Info_Boolean) - return ok + _ = b.variant.(Type_Info_Boolean) or_return + return true case Type_Info_Any: - _, ok := b.variant.(Type_Info_Any) - return ok + _ = b.variant.(Type_Info_Any) or_return + return true case Type_Info_Pointer: y := b.variant.(Type_Info_Pointer) or_return @@ -78,22 +78,19 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool { case Type_Info_Procedure: y := b.variant.(Type_Info_Procedure) or_return - switch { - case x.variadic != y.variadic, - x.convention != y.convention: - return false - } + (x.variadic == y.variadic) or_return + (x.convention == y.convention) or_return return are_types_identical(x.params, y.params) && are_types_identical(x.results, y.results) case Type_Info_Array: y := b.variant.(Type_Info_Array) or_return - if x.count != y.count { return false } + (x.count == y.count) or_return return are_types_identical(x.elem, y.elem) case Type_Info_Enumerated_Array: y := b.variant.(Type_Info_Enumerated_Array) or_return - if x.count != y.count { return false } + (x.count == y.count) or_return return are_types_identical(x.index, y.index) && are_types_identical(x.elem, y.elem) @@ -105,14 +102,17 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool { y := b.variant.(Type_Info_Slice) or_return return are_types_identical(x.elem, y.elem) + case Type_Info_Fixed_Capacity_Dynamic_Array: + y := b.variant.(Type_Info_Fixed_Capacity_Dynamic_Array) or_return + (x.capacity == y.capacity) or_return + return are_types_identical(x.elem, y.elem) + case Type_Info_Parameters: y := b.variant.(Type_Info_Parameters) or_return - if len(x.types) != len(y.types) { return false } + (len(x.types) == len(y.types)) or_return for _, i in x.types { xt, yt := x.types[i], y.types[i] - if !are_types_identical(xt, yt) { - return false - } + are_types_identical(xt, yt) or_return } return true @@ -131,59 +131,64 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool { xt, yt := x.types[i], y.types[i] xl, yl := x.tags[i], y.tags[i] - if xn != yn { return false } - if !are_types_identical(xt, yt) { return false } - if xl != yl { return false } + (xn == yn) or_return + are_types_identical(xt, yt) or_return + (xl == yl) or_return } return true case Type_Info_Union: - y := b.variant.(Type_Info_Union) or_return - if len(x.variants) != len(y.variants) { return false } + y := b.variant.(Type_Info_Union) or_return + (len(x.variants) == len(y.variants)) or_return for _, i in x.variants { xv, yv := x.variants[i], y.variants[i] - if !are_types_identical(xv, yv) { return false } + are_types_identical(xv, yv) or_return } return true case Type_Info_Enum: - // NOTE(bill): Should be handled above - return false + y := b.variant.(Type_Info_Enum) or_return + are_types_identical(x.base, y.base) or_return + (len(x.names) == len(y.names)) or_return + + for _, i in x.names { + (x.names[i] == y.names[i]) or_return + (x.values[i] == y.values[i]) or_return + } + return true case Type_Info_Map: y := b.variant.(Type_Info_Map) or_return return are_types_identical(x.key, y.key) && are_types_identical(x.value, y.value) case Type_Info_Bit_Set: - y := b.variant.(Type_Info_Bit_Set) or_return - return x.elem == y.elem && x.lower == y.lower && x.upper == y.upper + y := b.variant.(Type_Info_Bit_Set) or_return + are_types_identical(x.underlying, y.underlying) or_return + are_types_identical(x.elem, y.elem) or_return + + return x.lower == y.lower && x.upper == y.upper case Type_Info_Simd_Vector: y := b.variant.(Type_Info_Simd_Vector) or_return return x.count == y.count && x.elem == y.elem case Type_Info_Matrix: - y := b.variant.(Type_Info_Matrix) or_return - if x.row_count != y.row_count { return false } - if x.column_count != y.column_count { return false } - if x.layout != y.layout { return false } + y := b.variant.(Type_Info_Matrix) or_return + (x.row_count == y.row_count) or_return + (x.column_count == y.column_count) or_return + (x.layout == y.layout) or_return return are_types_identical(x.elem, y.elem) case Type_Info_Bit_Field: - y := b.variant.(Type_Info_Bit_Field) or_return - if !are_types_identical(x.backing_type, y.backing_type) { return false } - if x.field_count != y.field_count { return false } + y := b.variant.(Type_Info_Bit_Field) or_return + are_types_identical(x.backing_type, y.backing_type) or_return + (x.field_count == y.field_count) or_return + for _, i in x.names[:x.field_count] { - if x.names[i] != y.names[i] { - return false - } - if !are_types_identical(x.types[i], y.types[i]) { - return false - } - if x.bit_sizes[i] != y.bit_sizes[i] { - return false - } + (x.names[i] == y.names[i]) or_return + are_types_identical(x.types[i], y.types[i]) or_return + (x.bit_sizes[i] == y.bit_sizes[i]) or_return } return true } @@ -329,7 +334,8 @@ is_soa_pointer :: proc(info: ^Type_Info) -> bool { is_pointer_internally :: proc(info: ^Type_Info) -> bool { if info == nil { return false } #partial switch v in type_info_base(info).variant { - case Type_Info_Pointer, Type_Info_Multi_Pointer, + case Type_Info_Pointer, + Type_Info_Multi_Pointer, Type_Info_Procedure: return true case Type_Info_String: @@ -428,6 +434,13 @@ is_simd_vector :: proc(info: ^Type_Info) -> bool { _, ok := type_info_base(info).variant.(Type_Info_Simd_Vector) return ok } +// Returns true when the type is a fixed-capacity dynamic-array type ([dynamic; N]T), false otherwise. +@(require_results) +is_fixed_capacity_dynamic_array :: proc(info: ^Type_Info) -> bool { + if info == nil { return false } + _, ok := type_info_base(info).variant.(Type_Info_Fixed_Capacity_Dynamic_Array) + return ok +} // Returns true when the core-type is represented with a platform-native endian type, and returns false otherwise. @@ -441,6 +454,8 @@ is_endian_platform :: proc(info: ^Type_Info) -> bool { #partial switch v in info.variant { case Type_Info_Integer: return v.endianness == .Platform + case Type_Info_Float: + return v.endianness == .Platform case Type_Info_Bit_Set: return is_endian_platform(v.underlying) case Type_Info_Pointer: @@ -463,6 +478,11 @@ is_endian_little :: proc(info: ^Type_Info) -> bool { return ODIN_ENDIAN == .Little } return v.endianness == .Little + case Type_Info_Float: + if v.endianness == .Platform { + return ODIN_ENDIAN == .Little + } + return v.endianness == .Little case Type_Info_Bit_Set: return is_endian_little(v.underlying) case Type_Info_Pointer: @@ -485,6 +505,11 @@ is_endian_big :: proc(info: ^Type_Info) -> bool { return ODIN_ENDIAN == .Big } return v.endianness == .Big + case Type_Info_Float: + if v.endianness == .Platform { + return ODIN_ENDIAN == .Big + } + return v.endianness == .Big case Type_Info_Bit_Set: return is_endian_big(v.underlying) case Type_Info_Pointer: @@ -661,6 +686,12 @@ write_type_writer :: #force_no_inline proc(w: io.Writer, ti: ^Type_Info, n_writt io.write_string(w, "[]", &n) or_return write_type(w, info.elem, &n) or_return + case Type_Info_Fixed_Capacity_Dynamic_Array: + io.write_string(w, "[dynamic; ", &n) or_return + io.write_i64(w, i64(info.capacity), 10, &n) or_return + io.write_string(w, "]", &n) or_return + write_type(w, info.elem, &n) or_return + case Type_Info_Map: io.write_string(w, "map[", &n) or_return write_type(w, info.key, &n) or_return @@ -829,6 +860,8 @@ has_no_indirections :: proc(ti: ^Type_Info) -> bool { return has_no_indirections(info.elem) case Type_Info_Enumerated_Array: return has_no_indirections(info.elem) + case Type_Info_Fixed_Capacity_Dynamic_Array: + return has_no_indirections(info.elem) case Type_Info_Simd_Vector: return true diff --git a/core/simd/arm/aes.odin b/core/simd/arm/aes.odin index acafb9f1e..b1f44e52c 100644 --- a/core/simd/arm/aes.odin +++ b/core/simd/arm/aes.odin @@ -1,27 +1,27 @@ #+build arm64,arm32 package simd_arm -@(require_results,enable_target_feature="aes") +@(require_results, enable_target_feature = "aes") vaeseq_u8 :: #force_inline proc "c" (data, key: uint8x16_t) -> uint8x16_t { return _vaeseq_u8(data, key) } -@(require_results,enable_target_feature="aes") +@(require_results, enable_target_feature = "aes") vaesdq_u8 :: #force_inline proc "c" (data, key: uint8x16_t) -> uint8x16_t { return _vaesdq_u8(data, key) } -@(require_results,enable_target_feature="aes") +@(require_results, enable_target_feature = "aes") vaesmcq_u8 :: #force_inline proc "c" (data: uint8x16_t) -> uint8x16_t { return _vaesmcq_u8(data) } -@(require_results,enable_target_feature="aes") +@(require_results,enable_target_feature = "aes") vaesimcq_u8 :: #force_inline proc "c" (data: uint8x16_t) -> uint8x16_t { return _vaesimcq_u8(data) } -@(private,default_calling_convention="none") +@(private, default_calling_convention = "none") foreign _ { @(link_name = "llvm.aarch64.crypto.aese" when ODIN_ARCH == .arm64 else "llvm.arm.neon.aese") _vaeseq_u8 :: proc(data, key: uint8x16_t) -> uint8x16_t --- diff --git a/core/simd/arm/sha.odin b/core/simd/arm/sha.odin new file mode 100644 index 000000000..ca87c9795 --- /dev/null +++ b/core/simd/arm/sha.odin @@ -0,0 +1,108 @@ +#+build arm64,arm32 +package simd_arm + +@(require_results, enable_target_feature = "sha2") +vsha1cq_u32 :: #force_inline proc "c" (hash_abcd: uint32x4_t, e: uint32_t, wk: uint32x4_t) -> uint32x4_t { + return _vsha1cq_u32(hash_abcd, e, wk) +} + +@(require_results, enable_target_feature = "sha2") +vsha1pq_u32 :: #force_inline proc "c" (hash_abcd: uint32x4_t, e: uint32_t, wk: uint32x4_t) -> uint32x4_t { + return _vsha1pq_u32(hash_abcd, e, wk) +} + +@(require_results, enable_target_feature = "sha2") +vsha1mq_u32 :: #force_inline proc "c" (hash_abcd: uint32x4_t, e: uint32_t, wk: uint32x4_t) -> uint32x4_t { + return _vsha1mq_u32(hash_abcd, e, wk) +} + +@(require_results, enable_target_feature = "sha2") +vsha1h_u32 :: #force_inline proc "c" (e: uint32_t) -> uint32_t { + return _vsha1h_u32(e) +} + +@(require_results, enable_target_feature = "sha2") +vsha1su0q_u32 :: #force_inline proc "c" (w0_3, w4_7, w8_11: uint32x4_t) -> uint32x4_t { + return _vsha1su0q_u32(w0_3, w4_7, w8_11) +} + +@(require_results, enable_target_feature = "sha2") +vsha1su1q_u32 :: #force_inline proc "c" (tw0_3, w12_15: uint32x4_t) -> uint32x4_t { + return _vsha1su1q_u32(tw0_3, w12_15) +} + +@(require_results, enable_target_feature = "sha2") +vsha256hq_u32 :: #force_inline proc "c" (hash_abcd, hash_efgh, wk: uint32x4_t) -> uint32x4_t { + return _vsha256hq_u32(hash_abcd, hash_efgh, wk) +} + +@(require_results, enable_target_feature = "sha2") +vsha256h2q_u32 :: #force_inline proc "c" (hash_efgh, hash_abcd, wk: uint32x4_t) -> uint32x4_t { + return _vsha256h2q_u32(hash_efgh, hash_abcd, wk) +} + +@(require_results, enable_target_feature = "sha2") +vsha256su0q_u32 :: #force_inline proc "c" (w0_3, w4_7: uint32x4_t) -> uint32x4_t { + return _vsha256su0q_u32(w0_3, w4_7) +} + +@(require_results, enable_target_feature = "sha2") +vsha256su1q_u32 :: #force_inline proc "c" (tw0_3, w8_11, w12_15: uint32x4_t) -> uint32x4_t { + return _vsha256su1q_u32(tw0_3, w8_11, w12_15) +} + +// Note: The SHA512 instructions are part of the `sha3` feature set. + +@(require_results, enable_target_feature = "sha3") +vsha512hq_u64 :: #force_inline proc "c" (hash_ed, hash_gf, kwh_kwh2: uint64x2_t) -> uint64x2_t { + return _vsha512hq_u64(hash_ed, hash_gf, kwh_kwh2) +} + +@(require_results, enable_target_feature = "sha3") +vsha512h2q_u64 :: #force_inline proc "c" (sum_ab, hash_c_, hash_ab: uint64x2_t) -> uint64x2_t { + return _vsha512h2q_u64(sum_ab, hash_c_, hash_ab) +} + +@(require_results, enable_target_feature = "sha3") +vsha512su0q_u64 :: #force_inline proc "c" (w0_1, w2_: uint64x2_t) -> uint64x2_t { + return _vsha512su0q_u64(w0_1, w2_) +} + +@(require_results, enable_target_feature = "sha3") +vsha512su1q_u64 :: #force_inline proc "c" (s01_s02, w14_15, w9_10: uint64x2_t) -> uint64x2_t { + return _vsha512su1q_u64(s01_s02, w14_15, w9_10) +} + +@(private, default_calling_convention = "none") +foreign _ { + @(link_name = "llvm.aarch64.crypto.sha1c" when ODIN_ARCH == .arm64 else "llvm.arm.neon.sha1c") + _vsha1cq_u32 :: proc(hash_abcd: uint32x4_t, e: uint32_t, wk: uint32x4_t) -> uint32x4_t --- + @(link_name = "llvm.aarch64.crypto.sha1p" when ODIN_ARCH == .arm64 else "llvm.arm.neon.sha1p") + _vsha1pq_u32 :: proc(hash_abcd: uint32x4_t, e: uint32_t, wk: uint32x4_t) -> uint32x4_t --- + @(link_name = "llvm.aarch64.crypto.sha1m" when ODIN_ARCH == .arm64 else "llvm.arm.neon.sha1m") + _vsha1mq_u32 :: proc(hash_abcd: uint32x4_t, e: uint32_t, wk: uint32x4_t) -> uint32x4_t --- + @(link_name = "llvm.aarch64.crypto.sha1h" when ODIN_ARCH == .arm64 else "llvm.arm.neon.sha1h") + _vsha1h_u32 :: proc(e: uint32_t) -> uint32_t --- + @(link_name = "llvm.aarch64.crypto.sha1su0" when ODIN_ARCH == .arm64 else "llvm.arm.neon.sha1su0") + _vsha1su0q_u32 :: proc(w0_3, w4_7, w8_11: uint32x4_t) -> uint32x4_t --- + @(link_name = "llvm.aarch64.crypto.sha1su1" when ODIN_ARCH == .arm64 else "llvm.arm.neon.sha1su1") + _vsha1su1q_u32 :: proc(tw0_3, w12_15: uint32x4_t) -> uint32x4_t --- + + @(link_name = "llvm.aarch64.crypto.sha256h" when ODIN_ARCH == .arm64 else "llvm.arm.neon.sha256h") + _vsha256hq_u32 :: proc(hash_abcd, hash_efgh, wk: uint32x4_t) -> uint32x4_t --- + @(link_name = "llvm.aarch64.crypto.sha256h2" when ODIN_ARCH == .arm64 else "llvm.arm.neon.sha256h2") + _vsha256h2q_u32 :: proc(hash_efgh, hash_abcd, wk: uint32x4_t) -> uint32x4_t --- + @(link_name = "llvm.aarch64.crypto.sha256su0" when ODIN_ARCH == .arm64 else "llvm.arm.neon.sha256su0") + _vsha256su0q_u32 :: proc(w0_3, w4_7: uint32x4_t) -> uint32x4_t --- + @(link_name = "llvm.aarch64.crypto.sha256su1" when ODIN_ARCH == .arm64 else "llvm.arm.neon.sha256su1") + _vsha256su1q_u32 :: proc(tw0_3, w8_11, w12_15: uint32x4_t) -> uint32x4_t --- + + @(link_name = "llvm.aarch64.crypto.sha512h" when ODIN_ARCH == .arm64 else "llvm.arm.neon.sha512h") + _vsha512hq_u64 :: proc(hash_ed, hash_gf, kwh_kwh2: uint64x2_t) -> uint64x2_t --- + @(link_name = "llvm.aarch64.crypto.sha512h2" when ODIN_ARCH == .arm64 else "llvm.arm.neon.sha512h2") + _vsha512h2q_u64 :: proc(sum_ab, hash_c_, hash_ab: uint64x2_t) -> uint64x2_t --- + @(link_name = "llvm.aarch64.crypto.sha512su0" when ODIN_ARCH == .arm64 else "llvm.arm.neon.sha512su0") + _vsha512su0q_u64 :: proc(w0_1, w2_: uint64x2_t) -> uint64x2_t --- + @(link_name = "llvm.aarch64.crypto.sha512su1" when ODIN_ARCH == .arm64 else "llvm.arm.neon.sha512su1") + _vsha512su1q_u64 :: proc(s01_s02, w14_15, w9_10: uint64x2_t) -> uint64x2_t --- +} diff --git a/core/simd/arm/types.odin b/core/simd/arm/types.odin index 7c86483a7..05e3540b6 100644 --- a/core/simd/arm/types.odin +++ b/core/simd/arm/types.odin @@ -1,5 +1,9 @@ #+build arm64,arm32 package simd_arm +// Type aliases to match `arm_neon.h`. +uint32_t :: u32 + uint8x16_t :: #simd[16]u8 uint32x4_t :: #simd[4]u32 +uint64x2_t :: #simd[2]u64 diff --git a/core/simd/x86/sse41.odin b/core/simd/x86/sse41.odin index 81089ed63..510f5d8e9 100644 --- a/core/simd/x86/sse41.odin +++ b/core/simd/x86/sse41.odin @@ -26,7 +26,18 @@ _mm_blendv_epi8 :: #force_inline proc "c" (a, b, mask: __m128i) -> __m128i { } @(require_results, enable_target_feature="sse4.1") _mm_blend_epi16 :: #force_inline proc "c" (a, b: __m128i, $IMM8: u8) -> __m128i { - return transmute(__m128i)pblendw(transmute(i16x8)a, transmute(i16x8)b, IMM8) + return transmute(__m128i)simd.shuffle( + transmute(i16x8)b, + transmute(i16x8)a, + 0 when (IMM8 >> 0) & 1 == 1 else 8, + 1 when (IMM8 >> 1) & 1 == 1 else 9, + 2 when (IMM8 >> 2) & 1 == 1 else 10, + 3 when (IMM8 >> 3) & 1 == 1 else 11, + 4 when (IMM8 >> 4) & 1 == 1 else 12, + 5 when (IMM8 >> 5) & 1 == 1 else 13, + 6 when (IMM8 >> 6) & 1 == 1 else 14, + 7 when (IMM8 >> 7) & 1 == 1 else 15, + ) } @(require_results, enable_target_feature="sse4.1") _mm_blendv_pd :: #force_inline proc "c" (a, b, mask: __m128d) -> __m128d { @@ -303,8 +314,6 @@ foreign _ { blendpd :: proc(a, b: __m128d, #const imm2: u8) -> __m128d --- @(link_name = "llvm.x86.sse41.blendps") blendps :: proc(a, b: __m128, #const imm4: u8) -> __m128 --- - @(link_name = "llvm.x86.sse41.pblendw") - pblendw :: proc(a: i16x8, b: i16x8, #const imm8: u8) -> i16x8 --- @(link_name = "llvm.x86.sse41.insertps") insertps :: proc(a, b: __m128, #const imm8: u8) -> __m128 --- @(link_name = "llvm.x86.sse41.pmaxsb") diff --git a/core/strings/strings.odin b/core/strings/strings.odin index d55fea69e..40dc11e75 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -112,7 +112,7 @@ NOTE: Failure to find the byte results in returning the entire string. Returns: - res: The truncated string */ -truncate_to_byte :: proc "contextless" (str: string, b: byte) -> (res: string) { +truncate_to_byte :: proc "contextless" (str: string, b: byte) -> (res: string) #no_bounds_check { n := index_byte(str, b) if n < 0 { n = len(str) @@ -130,7 +130,7 @@ Inputs: Returns: - res: The truncated string */ -truncate_to_rune :: proc(str: string, r: rune) -> (res: string) { +truncate_to_rune :: proc(str: string, r: rune) -> (res: string) #no_bounds_check { n := index_rune(str, r) if n < 0 { n = len(str) @@ -152,7 +152,7 @@ Returns: - res: The cloned string from the byte array with a null-byte - err: An optional allocator error if one occured, `nil` otherwise */ -clone_from_bytes :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> (res: string, err: runtime.Allocator_Error) #optional_allocator_error { +clone_from_bytes :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> (res: string, err: runtime.Allocator_Error) #optional_allocator_error #no_bounds_check { c := make([]byte, len(s)+1, allocator, loc) or_return copy(c, s) c[len(s)] = 0 @@ -398,7 +398,7 @@ Output: false */ -equal_fold :: proc(u, v: string) -> (res: bool) { +equal_fold :: proc(u, v: string) -> (res: bool) #no_bounds_check { s, t := u, v loop: for s != "" && t != "" { sr, tr: rune @@ -483,7 +483,7 @@ prefix_length :: proc "contextless" (a, b: string) -> (n: int) { n = runtime.memory_prefix_length(raw_data(a), raw_data(b), min(len(a), len(b))) lim := max(n - UTF_MAX + 1, 0) - for l := n; l > lim; l -= 1 { + #no_bounds_check for l := n; l > lim; l -= 1 { r, _ := runtime.string_decode_rune(a[l - 1:]) if r != RUNE_ERROR { if l > 0 && (a[l - 1] & 0xc0 == 0xc0) { @@ -592,7 +592,7 @@ Output: true */ -has_suffix :: proc(s, suffix: string) -> (result: bool) { +has_suffix :: proc(s, suffix: string) -> (result: bool) #no_bounds_check { return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix } @@ -631,7 +631,7 @@ Output: a...b...c */ -join :: proc(a: []string, sep: string, allocator := context.allocator, loc := #caller_location) -> (res: string, err: runtime.Allocator_Error) #optional_allocator_error { +join :: proc(a: []string, sep: string, allocator := context.allocator, loc := #caller_location) -> (res: string, err: runtime.Allocator_Error) #optional_allocator_error #no_bounds_check { if len(a) == 0 { return "", nil } @@ -678,7 +678,7 @@ Output: abc */ -concatenate :: proc(a: []string, allocator := context.allocator, loc := #caller_location) -> (res: string, err: runtime.Allocator_Error) #optional_allocator_error { +concatenate :: proc(a: []string, allocator := context.allocator, loc := #caller_location) -> (res: string, err: runtime.Allocator_Error) #optional_allocator_error #no_bounds_check { if len(a) == 0 { return "", nil } @@ -812,7 +812,7 @@ Returns: - err: An optional allocator error if one occured, `nil` otherwise */ @private -_split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocator, loc := #caller_location) -> (res: []string, err: runtime.Allocator_Error) { +_split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocator, loc := #caller_location) -> (res: []string, err: runtime.Allocator_Error) #no_bounds_check { s, n := s_, n_ if n == 0 { @@ -1037,7 +1037,7 @@ _split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string, } else { m = index(s^, sep) } - if m < 0 { + #no_bounds_check if m < 0 { // not found res = s[:] ok = res != "" @@ -1084,7 +1084,7 @@ Output: */ split_by_byte_iterator :: proc(s: ^string, sep: u8) -> (res: string, ok: bool) { m := index_byte(s^, sep) - if m < 0 { + #no_bounds_check if m < 0 { // not found res = s[:] ok = res != "" @@ -1181,7 +1181,7 @@ Returns: - res: The trimmed string as a slice of the original. */ @(private) -_trim_cr :: proc(s: string) -> (res: string) { +_trim_cr :: proc(s: string) -> (res: string) #no_bounds_check { n := len(s) if n > 0 { if s[n-1] == '\r' { @@ -1591,8 +1591,8 @@ Output: -1 */ -index :: proc "contextless" (s, substr: string) -> (res: int) { - hash_str_rabin_karp :: proc "contextless" (s: string) -> (hash: u32 = 0, pow: u32 = 1) { +index :: proc "contextless" (s, substr: string) -> (res: int) #no_bounds_check { + hash_str_rabin_karp :: proc "contextless" (s: string) -> (hash: u32 = 0, pow: u32 = 1) #no_bounds_check { for i := 0; i < len(s); i += 1 { hash = hash*PRIME_RABIN_KARP + u32(s[i]) } @@ -1671,8 +1671,8 @@ Output: -1 */ -last_index :: proc(s, substr: string) -> (res: int) { - hash_str_rabin_karp_reverse :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) { +last_index :: proc(s, substr: string) -> (res: int) #no_bounds_check { + hash_str_rabin_karp_reverse :: proc "contextless" (s: string) -> (hash: u32 = 0, pow: u32 = 1) #no_bounds_check { for i := len(s) - 1; i >= 0; i -= 1 { hash = hash*PRIME_RABIN_KARP + u32(s[i]) } @@ -1751,7 +1751,7 @@ Output: -1 */ -index_any :: proc(s, chars: string) -> (res: int) { +index_any :: proc(s, chars: string) -> (res: int) #no_bounds_check { if chars == "" { return -1 } @@ -1815,7 +1815,7 @@ Output: -1 */ -last_index_any :: proc(s, chars: string) -> (res: int) { +last_index_any :: proc(s, chars: string) -> (res: int) #no_bounds_check { if chars == "" { return -1 } @@ -1876,7 +1876,7 @@ Returns: - idx: the index of the first matching substring - width: the length of the found substring */ -index_multi :: proc(s: string, substrs: []string) -> (idx: int, width: int) { +index_multi :: proc(s: string, substrs: []string) -> (idx: int, width: int) #no_bounds_check { idx = -1 if s == "" || len(substrs) <= 0 { return @@ -1939,7 +1939,7 @@ Output: 0 */ -count :: proc(s, substr: string) -> (res: int) { +count :: proc(s, substr: string) -> (res: int) #no_bounds_check { if len(substr) == 0 { // special case return rune_count(s) + 1 } @@ -2014,7 +2014,7 @@ repeat :: proc(s: string, count: int, allocator := context.allocator, loc := #ca b := make([]byte, len(s)*count, allocator, loc) or_return i := copy(b, s) - for i < len(b) { // 2^N trick to reduce the need to copy + #no_bounds_check for i < len(b) { // 2^N trick to reduce the need to copy copy(b[i:], b[:i]) i *= 2 } @@ -2121,7 +2121,7 @@ replace :: proc(s, old, new: string, n: int, allocator := context.allocator, loc w := 0 start := 0 - for i := 0; i < byte_count; i += 1 { + #no_bounds_check for i := 0; i < byte_count; i += 1 { j := start if len(old) == 0 { if i > 0 { @@ -2438,7 +2438,7 @@ Output: test */ -trim_right_proc :: proc(s: string, p: proc(rune) -> bool) -> (res: string) { +trim_right_proc :: proc(s: string, p: proc(rune) -> bool) -> (res: string) #no_bounds_check { i := last_index_proc(s, p, false) if i >= 0 && s[i] >= utf8.RUNE_SELF { _, w := utf8.decode_rune_in_string(s[i:]) @@ -2460,7 +2460,7 @@ Inputs: Returns: - res: The trimmed string as a slice of the original, empty when no match */ -trim_right_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr) -> (res: string) { +trim_right_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr) -> (res: string) #no_bounds_check { i := last_index_proc_with_state(s, p, state, false) if i >= 0 && s[i] >= utf8.RUNE_SELF { _, w := utf8.decode_rune_in_string(s[i:]) @@ -2635,7 +2635,7 @@ Output: testing */ -trim_prefix :: proc(s, prefix: string) -> (res: string) { +trim_prefix :: proc(s, prefix: string) -> (res: string) #no_bounds_check { if has_prefix(s, prefix) { return s[len(prefix):] } @@ -2668,7 +2668,7 @@ Output: todo.doc */ -trim_suffix :: proc(s, suffix: string) -> (res: string) { +trim_suffix :: proc(s, suffix: string) -> (res: string) #no_bounds_check { if has_suffix(s, suffix) { return s[:len(s)-len(suffix)] } @@ -2848,7 +2848,7 @@ scrub :: proc(s: string, replacement: string, allocator := context.allocator, lo cursor := 0 origin := str - for len(str) > 0 { + #no_bounds_check for len(str) > 0 { r, w := utf8.decode_rune_in_string(str) if r == utf8.RUNE_ERROR { @@ -2907,7 +2907,7 @@ reverse :: proc(s: string, allocator := context.allocator, loc := #caller_locati buf := make([]byte, n, allocator, loc) or_return i := n - for len(str) > 0 { + #no_bounds_check for len(str) > 0 { _, w := utf8.decode_rune_in_string(str) i -= w copy(buf[i:], str[:w]) @@ -2963,7 +2963,7 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator, lo str := s column: int - for len(str) > 0 { + #no_bounds_check for len(str) > 0 { r, w := utf8.decode_rune_in_string(str) if r == '\t' { @@ -3028,7 +3028,7 @@ Output: true */ -partition :: proc(str, sep: string) -> (head, match, tail: string) { +partition :: proc(str, sep: string) -> (head, match, tail: string) #no_bounds_check { i := index(str, sep) if i == -1 { head = str @@ -3166,14 +3166,14 @@ Inputs: write_pad_string :: proc(w: io.Writer, pad: string, pad_len, remains: int) { repeats := remains / pad_len - for i := 0; i < repeats; i += 1 { + for _ in 0.. (field: string, ok: bool) { +fields_iterator :: proc(s: ^string) -> (field: string, ok: bool) #no_bounds_check { start, end := -1, -1 for r, offset in s { end = offset @@ -3404,7 +3404,7 @@ levenshtein_distance :: proc(a, b: string, allocator := context.allocator, loc : } @(private) -internal_substring :: proc(s: string, rune_start: int, rune_end: int) -> (sub: string, ok: bool) { +internal_substring :: proc(s: string, rune_start: int, rune_end: int) -> (sub: string, ok: bool) #no_bounds_check { sub = s ok = true diff --git a/core/unicode/utf8/utf8.odin b/core/unicode/utf8/utf8.odin index 85687b749..c53b2e3a4 100644 --- a/core/unicode/utf8/utf8.odin +++ b/core/unicode/utf8/utf8.odin @@ -63,7 +63,7 @@ accept_sizes := [256]u8{ } @(require_results) -encode_rune :: proc "contextless" (c: rune) -> ([4]u8, int) { +encode_rune :: proc "contextless" (c: rune) -> ([4]u8, int) #no_bounds_check { r := c buf: [4]u8 @@ -109,7 +109,7 @@ decode_rune_in_string :: #force_inline proc "contextless" (s: string) -> (rune, return decode_rune_in_bytes(transmute([]u8)s) } @(require_results) -decode_rune_in_bytes :: proc "contextless" (s: []u8) -> (rune, int) { +decode_rune_in_bytes :: proc "contextless" (s: []u8) -> (rune, int) #no_bounds_check { n := len(s) if n < 1 { return RUNE_ERROR, 0 @@ -147,7 +147,7 @@ decode_rune_in_bytes :: proc "contextless" (s: []u8) -> (rune, int) { } @(require_results) -string_to_runes :: proc "odin" (s: string, allocator := context.allocator) -> (runes: []rune, err: runtime.Allocator_Error) #optional_allocator_error { +string_to_runes :: proc "odin" (s: string, allocator := context.allocator) -> (runes: []rune, err: runtime.Allocator_Error) #optional_allocator_error #no_bounds_check { n := rune_count_in_string(s) runes = make([]rune, n, allocator) or_return @@ -160,7 +160,7 @@ string_to_runes :: proc "odin" (s: string, allocator := context.allocator) -> (r } @(require_results) -runes_to_string :: proc "odin" (runes: []rune, allocator := context.allocator) -> (s: string, err: runtime.Allocator_Error) #optional_allocator_error { +runes_to_string :: proc "odin" (runes: []rune, allocator := context.allocator) -> (s: string, err: runtime.Allocator_Error) #optional_allocator_error #no_bounds_check { byte_count := 0 for r in runes { _, w := encode_rune(r) @@ -190,7 +190,7 @@ decode_last_rune_in_string :: #force_inline proc "contextless" (s: string) -> (r return decode_last_rune_in_bytes(transmute([]u8)s) } @(require_results) -decode_last_rune_in_bytes :: proc "contextless" (s: []u8) -> (rune, int) { +decode_last_rune_in_bytes :: proc "contextless" (s: []u8) -> (rune, int) #no_bounds_check { r: rune size: int start, end, limit: int @@ -239,7 +239,7 @@ rune_at_pos :: proc "contextless" (s: string, pos: int) -> rune { } @(require_results) -rune_string_at_pos :: proc "contextless" (s: string, pos: int) -> string { +rune_string_at_pos :: proc "contextless" (s: string, pos: int) -> string #no_bounds_check { if pos < 0 { return "" } @@ -256,7 +256,7 @@ rune_string_at_pos :: proc "contextless" (s: string, pos: int) -> string { } @(require_results) -rune_at :: proc "contextless" (s: string, byte_index: int) -> rune { +rune_at :: proc "contextless" (s: string, byte_index: int) -> rune #no_bounds_check { r, _ := decode_rune_in_string(s[byte_index:]) return r } @@ -292,7 +292,7 @@ valid_rune :: proc "contextless" (r: rune) -> bool { } @(require_results) -valid_string :: proc "contextless" (s: string) -> bool { +valid_string :: proc "contextless" (s: string) -> bool #no_bounds_check { n := len(s) for i := 0; i < n; { si := s[i] @@ -340,7 +340,7 @@ rune_count_in_string :: #force_inline proc(s: string) -> int { return rune_count_in_bytes(transmute([]u8)s) } @(require_results) -rune_count_in_bytes :: proc "contextless" (s: []u8) -> int { +rune_count_in_bytes :: proc "contextless" (s: []u8) -> int #no_bounds_check { count := 0 n := len(s) @@ -402,7 +402,7 @@ full_rune :: proc{ // full_rune_in_bytes reports if the bytes in b begin with a full utf-8 encoding of a rune or not // An invalid encoding is considered a full rune since it will convert as an error rune of width 1 (RUNE_ERROR) @(require_results) -full_rune_in_bytes :: proc "contextless" (b: []byte) -> bool { +full_rune_in_bytes :: proc "contextless" (b: []byte) -> bool #no_bounds_check { n := len(b) if n == 0 { return false diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index a5455846d..a12217548 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -2247,7 +2247,7 @@ arbitrary_precision_mathematics :: proc() { cb := big.internal_count_bits(a) if print_name { - fmt.printf(name) + fmt.print(name) } if err != nil { fmt.printf(" (Error: %v) ", err) diff --git a/src/big_int.cpp b/src/big_int.cpp index 7067a7c3d..e2ebb5c76 100644 --- a/src/big_int.cpp +++ b/src/big_int.cpp @@ -16,14 +16,25 @@ void MP_FREE(void *mem, size_t size) { #else void *MP_MALLOC(size_t size) { - return gb_alloc(permanent_allocator(), cast(isize)size); + Arena *arena = get_arena(ThreadArena_Permanent); + return arena_alloc(arena, cast(isize)size, 16); } void *MP_REALLOC(void *mem, size_t oldsize, size_t newsize) { - return gb_resize(permanent_allocator(), mem, cast(isize)oldsize, cast(isize)newsize); + if (newsize < oldsize) { + return mem; + } + if (newsize == 0) { + return mem; + } + Arena *arena = get_arena(ThreadArena_Permanent); + void *new_mem = arena_alloc(arena, cast(isize)newsize, 16); + gb_memcopy(new_mem, mem, oldsize); + return new_mem; } void *MP_CALLOC(size_t nmemb, size_t size) { - size_t total = nmemb*size; - return gb_alloc(permanent_allocator(), cast(isize)total); + Arena *arena = get_arena(ThreadArena_Permanent); + isize total_size = cast(isize)(nmemb * size); + return arena_alloc(arena, total_size, 16); } void MP_FREE(void *mem, size_t size) { // DO NOTHING @@ -46,6 +57,8 @@ gb_internal void big_int_dealloc(BigInt *dst) { mp_clear(dst); } +gb_internal bool big_int_can_be_represented_in_64_bits(BigInt const *x); + gb_internal BigInt big_int_make(BigInt const *b, bool abs=false); gb_internal BigInt big_int_make_abs(BigInt const *b); gb_internal BigInt big_int_make_u64(u64 x); @@ -282,6 +295,11 @@ gb_internal void big_int_from_string(BigInt *dst, String const &s, bool *success +gb_internal bool big_int_can_be_represented_in_64_bits(BigInt const *x) { + int bits_used = (x->used-1) * MP_DIGIT_BIT; + return bits_used <= 64; +} + gb_internal u64 big_int_to_u64(BigInt const *x) { GB_ASSERT(x->sign == 0); return mp_get_u64(x); diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 1d836b1ec..d52553d65 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -577,6 +577,9 @@ struct BuildContext { bool internal_by_value; bool internal_weak_monomorphization; bool internal_ignore_llvm_verification; + bool internal_llvm_mem2reg; + + bool enable_rvo; bool no_threaded_checker; diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 1a094c1f0..4b3e8807e 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -58,6 +58,7 @@ gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_bool is_type_simd_vector, is_type_matrix, is_type_raw_union, + is_type_fixed_capacity_dynamic_array, is_type_polymorphic_record_specialized, is_type_polymorphic_record_unspecialized, @@ -1706,7 +1707,7 @@ gb_internal bool cache_load_file_directive(CheckerContext *c, Ast *call, String } defer ({ if (cache == nullptr) { - LoadFileCache *new_cache = gb_alloc_item(permanent_allocator(), LoadFileCache); + LoadFileCache *new_cache = permanent_alloc_item(); new_cache->path = path; new_cache->data = data; new_cache->file_error = file_error; @@ -1744,7 +1745,7 @@ gb_internal bool cache_load_file_directive(CheckerContext *c, Ast *call, String case LoadFileTier_Contents: { isize file_size = cast(isize)gb_file_size(&f); if (file_size > 0) { - u8 *ptr = cast(u8 *)gb_alloc(permanent_allocator(), file_size+1); + u8 *ptr = permanent_alloc_array(file_size+1); gb_file_read_at(&f, ptr, file_size, 0); ptr[file_size] = '\0'; data.text = ptr; @@ -1949,7 +1950,7 @@ gb_internal LoadDirectiveResult check_load_directory_directive(CheckerContext *c } defer ({ if (cache == nullptr) { - LoadDirectoryCache *new_cache = gb_alloc_item(permanent_allocator(), LoadDirectoryCache); + LoadDirectoryCache *new_cache = permanent_alloc_item(); new_cache->path = path; new_cache->files = file_caches; new_cache->file_error = file_error; @@ -2412,13 +2413,14 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o } String name = arg->Ident.token.string; + auto interned = arg->Ident.interned; operand->type = def.type; operand->mode = def.mode; operand->value = def.value; - Entity *found = scope_lookup_current(config_pkg->scope, name); + Entity *found = scope_lookup_current(config_pkg->scope, interned); if (found != nullptr) { if (found->kind != Entity_Constant) { error(arg, "'#config' entity '%.*s' found but expected a constant", LIT(name)); @@ -2634,6 +2636,16 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As mode = Addressing_Constant; value = exact_value_i64(at->Array.count); type = t_untyped_integer; + } else if (is_type_fixed_capacity_dynamic_array(op_type)) { + Type *at = core_type(op_type); + if (id == BuiltinProc_cap) { + mode = Addressing_Constant; + value = exact_value_i64(at->FixedCapacityDynamicArray.capacity); + type = t_untyped_integer; + } else { + GB_ASSERT(id == BuiltinProc_len); + mode = Addressing_Value; + } } else if (is_type_enumerated_array(op_type) && id == BuiltinProc_len) { Type *at = core_type(op_type); mode = Addressing_Constant; @@ -2793,7 +2805,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As } GB_ASSERT(type != nullptr); - String field_name = {}; + InternedString field_name = {}; if (field_arg == nullptr) { error(call, "Expected an identifier for field argument"); @@ -2801,9 +2813,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As } if (field_arg->kind == Ast_Ident) { - field_name = field_arg->Ident.token.string; + field_name = field_arg->Ident.interned; } - if (field_name.len == 0) { + if (field_name.value == 0) { error(field_arg, "Expected an identifier for field argument"); return false; } @@ -2836,19 +2848,19 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As ERROR_BLOCK(); gbString type_str = type_to_string_shorthand(type); error(ce->args[0], - "'%s' has no field named '%.*s'", type_str, LIT(field_name)); + "'%s' has no field named '%s'", type_str, field_name.cstring()); gb_string_free(type_str); Type *bt = base_type(type); if (bt->kind == Type_Struct) { - check_did_you_mean_type(field_name, bt->Struct.fields); + check_did_you_mean_type(field_name.string(), bt->Struct.fields); } return false; } if (sel.indirect) { gbString type_str = type_to_string_shorthand(type); error(ce->args[0], - "Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str); + "Field '%s' is embedded via a pointer in '%s'", field_name.cstring(), type_str); gb_string_free(type_str); return false; } @@ -2880,7 +2892,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As } GB_ASSERT(type != nullptr); - String field_name = {}; + InternedString field_name = {}; if (field_arg == nullptr) { error(call, "Expected a constant (not-empty) string for field argument"); @@ -2890,9 +2902,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As Operand x = {}; check_expr(c, &x, field_arg); if (x.mode == Addressing_Constant && x.value.kind == ExactValue_String) { - field_name = x.value.value_string; + field_name = string_interner_insert(x.value.value_string); } - if (field_name.len == 0) { + if (field_name.value == 0) { error(field_arg, "Expected a constant (non-empty) string for field argument"); return false; } @@ -2910,19 +2922,19 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As ERROR_BLOCK(); gbString type_str = type_to_string_shorthand(type); error(ce->args[0], - "'%s' has no field named '%.*s'", type_str, LIT(field_name)); + "'%s' has no field named '%s'", type_str, field_name.cstring()); gb_string_free(type_str); Type *bt = base_type(type); if (bt->kind == Type_Struct) { - check_did_you_mean_type(field_name, bt->Struct.fields); + check_did_you_mean_type(field_name.string(), bt->Struct.fields); } return false; } if (sel.indirect) { gbString type_str = type_to_string_shorthand(type); error(ce->args[0], - "Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str); + "Field '%s' is embedded via a pointer in '%s'", field_name.string(), type_str); gb_string_free(type_str); return false; } @@ -3594,18 +3606,17 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As gb_string_free(type_str); return false; } - gbAllocator a = permanent_allocator(); Type *tuple = alloc_type_tuple(); if (is_type_struct(type)) { isize variable_count = type->Struct.fields.count; - slice_init(&tuple->Tuple.variables, a, variable_count); + tuple->Tuple.variables = permanent_slice_make(variable_count); // NOTE(bill): don't copy the entities, this should be good enough gb_memmove_array(tuple->Tuple.variables.data, type->Struct.fields.data, variable_count); } else if (is_type_array(type)) { isize variable_count = cast(isize)type->Array.count; - slice_init(&tuple->Tuple.variables, a, variable_count); + tuple->Tuple.variables = permanent_slice_make(variable_count); for (isize i = 0; i < variable_count; i++) { tuple->Tuple.variables[i] = alloc_entity_array_elem(nullptr, blank_token, type->Array.elem, cast(i32)i); } @@ -4426,7 +4437,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As } if (!fail && first_is_field_value) { for_array(i, names) { - Selection sel = lookup_field(et, names[i], false); + Selection sel = lookup_field(et, string_interner_insert(names[i]), false); if (sel.entity == nullptr) { goto soa_zip_end; } @@ -5167,6 +5178,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As case Type_Array: case Type_EnumeratedArray: case Type_SimdVector: + case Type_FixedCapacityDynamicArray: operand->type = alloc_type_multi_pointer(base_array_type(base)); break; case Type_Matrix: @@ -6245,6 +6257,33 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As operand->type = x.type; } break; + + case BuiltinProc_likely: + case BuiltinProc_unlikely: + { + Operand x = {}; + check_expr(c, &x, ce->args[0]); + if (x.mode == Addressing_Invalid) { + return false; + } + if (!is_type_boolean(x.type)) { + gbString xt = type_to_string(x.type); + error(x.expr, "Expected a boolean expression to '%.*s', got %s", LIT(builtin_name), xt); + gb_string_free(xt); + *operand = x; // minimize error propagation + return true; + } + + if (x.mode == Addressing_Constant) { + // NOTE(bill): just completely ignore this intrinsic entirely + *operand = x; + return true; + } + + operand->mode = Addressing_Value; + operand->type = x.type; + } + break; case BuiltinProc_prefetch_read_instruction: case BuiltinProc_prefetch_read_data: @@ -6747,7 +6786,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As return false; } - String field_name = x.value.value_string; + InternedString field_name = string_interner_insert(x.value.value_string); Selection sel = lookup_field(type, field_name, false); operand->mode = Addressing_Constant; @@ -6827,12 +6866,12 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As return false; } - String field_name = x.value.value_string; + InternedString field_name = string_interner_insert(x.value.value_string); Selection sel = lookup_field(type, field_name, false); if (sel.index.count == 0) { gbString t = type_to_string(type); - error(ce->args[1], "'%.*s' is not a field of type %s", LIT(field_name), t); + error(ce->args[1], "'%s' is not a field of type %s", field_name.cstring(), t); gb_string_free(t); return false; } @@ -7587,25 +7626,25 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As return false; } - String field_name = x.value.value_string; + InternedString field_name = string_interner_insert(x.value.value_string); Selection sel = lookup_field(type, field_name, false); if (sel.entity == nullptr) { ERROR_BLOCK(); gbString type_str = type_to_string(bt); error(ce->args[0], - "'%s' has no field named '%.*s'", type_str, LIT(field_name)); + "'%s' has no field named '%s'", type_str, field_name.cstring()); gb_string_free(type_str); if (bt->kind == Type_Struct) { - check_did_you_mean_type(field_name, bt->Struct.fields); + check_did_you_mean_type(field_name.string(), bt->Struct.fields); } return false; } if (sel.indirect) { gbString type_str = type_to_string(bt); error(ce->args[0], - "Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str); + "Field '%s' is embedded via a pointer in '%s'", field_name.cstring(), type_str); gb_string_free(type_str); return false; } @@ -7617,6 +7656,31 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As } break; + case BuiltinProc_type_fixed_capacity_dynamic_array_len_offset: + { + Operand op = {}; + Type *bt = check_type(c, ce->args[0]); + Type *type = base_type(bt); + if (type == nullptr || type == t_invalid) { + error(ce->args[0], "Expected a fixed capacity dynamic array type for '%.*s'", LIT(builtin_name)); + return false; + } + if (!is_type_fixed_capacity_dynamic_array(type)) { + error(ce->args[0], "Expected a fixed capacity dynamic array type for '%.*s'", LIT(builtin_name)); + return false; + } + + i64 sz = type_size_of(type); + gb_unused(sz); + i64 offset = type_offset_of(type, 1); + + operand->mode = Addressing_Constant; + operand->value = exact_value_u64(cast(u64)offset); + operand->type = t_uintptr; + break; + } + break; + case BuiltinProc_type_bit_set_backing_type: { Operand op = {}; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 22a74f370..4f224affa 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -156,9 +156,12 @@ gb_internal void override_entity_in_scope(Entity *original_entity, Entity *new_e // NOTE(bill): The original_entity's scope may not be same scope that it was inserted into // e.g. file entity inserted into its package scope String original_name = original_entity->token.string; + auto original_intern = entity_interned_name(original_entity); + u32 hash = original_entity->interned_name_hash.load(); + Scope *found_scope = nullptr; Entity *found_entity = nullptr; - scope_lookup_parent(original_entity->scope, original_name, &found_scope, &found_entity); + scope_lookup_parent(original_entity->scope, original_intern, &found_scope, &found_entity, hash); if (found_scope == nullptr) { return; } @@ -171,7 +174,7 @@ gb_internal void override_entity_in_scope(Entity *original_entity, Entity *new_e // has been "evaluated" and the variant data can be copied across rw_mutex_lock(&found_scope->mutex); - string_map_set(&found_scope->elements, original_name, new_entity); + scope_map_insert(&found_scope->elements, original_intern, hash, new_entity); rw_mutex_unlock(&found_scope->mutex); original_entity->flags |= EntityFlag_Overridden; @@ -518,6 +521,8 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, AttributeContext ac = {}; check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac); + e->deprecated_message = ac.deprecated_message; + if (e->kind == Entity_TypeName && ac.objc_class != "") { e->TypeName.objc_class_name = ac.objc_class; @@ -990,7 +995,7 @@ gb_internal Entity *init_entity_foreign_library(CheckerContext *ctx, Entity *e) error(ident, "foreign library names must be an identifier"); } else { String name = ident->Ident.token.string; - Entity *found = scope_lookup(ctx->scope, name, ident->Ident.hash); + Entity *found = scope_lookup(ctx->scope, ident->Ident.interned, ident->Ident.hash); if (found == nullptr) { if (is_blank_ident(name)) { @@ -1189,26 +1194,26 @@ gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeCon if (!ac.objc_is_class_method) { bool ok = true; for (TypeNameObjCMetadataEntry const &entry : md->value_entries) { - if (entry.name == ac.objc_name) { + if (entry.interned.string() == ac.objc_name) { error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name)); ok = false; break; } } if (ok) { - array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e}); + array_add(&md->value_entries, TypeNameObjCMetadataEntry{string_interner_insert(ac.objc_name), e}); } } else { bool ok = true; for (TypeNameObjCMetadataEntry const &entry : md->type_entries) { - if (entry.name == ac.objc_name) { + if (entry.interned.string() == ac.objc_name) { error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name)); ok = false; break; } } if (ok) { - array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e}); + array_add(&md->type_entries, TypeNameObjCMetadataEntry{string_interner_insert(ac.objc_name), e}); } } } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 3dd4808c3..9044d8346 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -183,11 +183,11 @@ gb_internal void populate_check_did_you_mean_objc_entity(StringSet *set, Entity if (is_type) { for (auto const &entry : objc_metadata->type_entries) { - string_set_add(set, entry.name); + string_set_add(set, entry.interned.string()); } } else { for (auto const &entry : objc_metadata->value_entries) { - string_set_add(set, entry.name); + string_set_add(set, entry.interned.string()); } } @@ -499,7 +499,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E rw_mutex_shared_unlock(&gen_procs->mutex); // @local-mutex } else { - gen_procs = gb_alloc_item(permanent_allocator(), GenProcsData); + gen_procs = permanent_alloc_item(); gen_procs->procs.allocator = heap_allocator(); base_entity->Procedure.gen_procs = gen_procs; mutex_unlock(&base_entity->Procedure.gen_procs_mutex); // @entity-mutex @@ -514,7 +514,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E // NOTE(bill): Reset scope from the failed procedure type scope->head_child.store(nullptr, std::memory_order_relaxed); - string_map_clear(&scope->elements); + scope_map_clear(&scope->elements); ptr_set_clear(&scope->imported); // LEAK NOTE(bill): Cloning this AST may be leaky but this is not really an issue due to arena-based allocation @@ -536,7 +536,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E DeclInfo *decl = other->decl_info; if (decl->proc_checked_state != ProcCheckedState_Checked) { - ProcInfo *proc_info = gb_alloc_item(permanent_allocator(), ProcInfo); + ProcInfo *proc_info = permanent_alloc_item(); proc_info->file = other->file; proc_info->token = other->token; proc_info->decl = decl; @@ -630,7 +630,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E array_add(&gen_procs->procs, entity); rw_mutex_unlock(&gen_procs->mutex); // @local-mutex - ProcInfo *proc_info = gb_alloc_item(permanent_allocator(), ProcInfo); + ProcInfo *proc_info = permanent_alloc_item(); proc_info->file = file; proc_info->token = token; proc_info->decl = d; @@ -1090,18 +1090,20 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ return; } - // Grab definite or indefinite article matching `context_name`, or "" if not found. - String article = error_article(context_name); if (is_type_untyped(operand->type)) { Type *target_type = type; if (type == nullptr || is_type_any(type)) { if (type == nullptr && is_type_untyped_uninit(operand->type)) { + String article = error_article(context_name); // Grab definite or indefinite article matching `context_name`, or "" if not found. + error(operand->expr, "Use of --- in %.*s%.*s", LIT(article), LIT(context_name)); operand->mode = Addressing_Invalid; return; } if (type == nullptr && is_type_untyped_nil(operand->type)) { + String article = error_article(context_name); // Grab definite or indefinite article matching `context_name`, or "" if not found. + error(operand->expr, "Use of untyped nil in %.*s%.*s", LIT(article), LIT(context_name)); operand->mode = Addressing_Invalid; return; @@ -1159,6 +1161,8 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ defer (gb_string_free(op_type_str)); defer (gb_string_free(expr_str)); + String article = error_article(context_name); // Grab definite or indefinite article matching `context_name`, or "" if not found. + // TODO(bill): is this a good enough error message? error(operand->expr, "Cannot assign overloaded procedure group '%s' to '%s' in %.*s%.*s", @@ -1187,6 +1191,8 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ defer (gb_string_free(op_type_str)); defer (gb_string_free(expr_str)); + String article = error_article(context_name); // Grab definite or indefinite article matching `context_name`, or "" if not found. + switch (operand->mode) { case Addressing_Builtin: error(operand->expr, @@ -1329,7 +1335,7 @@ gb_internal bool polymorphic_assign_index(Type **gt_, i64 *dst_count, i64 source Type *gt = *gt_; GB_ASSERT(gt->kind == Type_Generic); - Entity *e = scope_lookup(gt->Generic.scope, gt->Generic.name); + Entity *e = scope_lookup(gt->Generic.scope, gt->Generic.interned_name, 0); GB_ASSERT(e != nullptr); if (e->kind == Entity_TypeName) { *gt_ = nullptr; @@ -1430,7 +1436,7 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T if (poly->Array.generic_count != nullptr) { Type *gt = poly->Array.generic_count; GB_ASSERT(gt->kind == Type_Generic); - Entity *e = scope_lookup(gt->Generic.scope, gt->Generic.name); + Entity *e = scope_lookup(gt->Generic.scope, gt->Generic.interned_name, 0); GB_ASSERT(e != nullptr); if (e->kind == Entity_TypeName) { Type *index = source->EnumeratedArray.index; @@ -1490,6 +1496,22 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T return is_polymorphic_type_assignable(c, poly->DynamicArray.elem, source->DynamicArray.elem, true, modify_type); } return false; + + case Type_FixedCapacityDynamicArray: + if (source->kind == Type_FixedCapacityDynamicArray) { + if (poly->FixedCapacityDynamicArray.generic_capacity != nullptr) { + if (!polymorphic_assign_index(&poly->FixedCapacityDynamicArray.generic_capacity, + &poly->FixedCapacityDynamicArray.capacity, + source->FixedCapacityDynamicArray.capacity)) { + return false; + } + } + if (poly->FixedCapacityDynamicArray.capacity == source->FixedCapacityDynamicArray.capacity) { + return is_polymorphic_type_assignable(c, poly->FixedCapacityDynamicArray.elem, source->FixedCapacityDynamicArray.elem, true, modify_type); + } + } + return false; + case Type_Slice: if (source->kind == Type_Slice) { return is_polymorphic_type_assignable(c, poly->Slice.elem, source->Slice.elem, true, modify_type); @@ -1754,9 +1776,9 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam GB_ASSERT(n->kind == Ast_Ident); o->mode = Addressing_Invalid; o->expr = n; - String name = n->Ident.token.string; + auto name = n->Ident.token.string; - Entity *e = scope_lookup(c->scope, name, n->Ident.hash); + Entity *e = scope_lookup(c->scope, n->Ident.interned, n->Ident.hash); if (e == nullptr) { if (is_blank_ident(name)) { error(n, "'_' cannot be used as a value"); @@ -2145,9 +2167,13 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i BigInt i = v.value_integer; i64 byte_size = type_size_of(type); - BigInt umax = {}; - BigInt imin = {}; - BigInt imax = {}; + BigInt umax; + BigInt imin; + BigInt imax; + + u64 umax_64 = 0; + i64 imin_64 = 0; + i64 imax_64 = 0; if (c->bit_field_bit_size > 0) { i64 bit_size = gb_min(cast(i64)(8*byte_size), cast(i64)c->bit_field_bit_size); @@ -2170,10 +2196,10 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i big_int_shl_eq(&imax, &bi); mp_decr(&imax); } else { - if (byte_size < 16) { - big_int_from_u64(&umax, unsigned_integer_maxs[byte_size]); - big_int_from_i64(&imin, signed_integer_mins[byte_size]); - big_int_from_i64(&imax, signed_integer_maxs[byte_size]); + if (byte_size <= 8) { + umax_64 = unsigned_integer_maxs[byte_size]; + imin_64 = signed_integer_mins[byte_size]; + imax_64 = signed_integer_maxs[byte_size]; } else { big_int_from_u64(&umax, 1); big_int_from_i64(&imin, 1); @@ -2201,16 +2227,27 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i case Basic_i16: case Basic_i32: case Basic_i64: - case Basic_i128: case Basic_int: case Basic_i16le: case Basic_i32le: case Basic_i64le: - case Basic_i128le: case Basic_i16be: case Basic_i32be: case Basic_i64be: + if (c->bit_field_bit_size == 0) { + // return imin <= i && i <= imax; + if (!big_int_can_be_represented_in_64_bits(&i)) { + return false; + } + + i64 val64 = big_int_to_i64(&i); + + return imin_64 <= val64 && val64 <= imax_64; + } + /*fallthrough*/ + case Basic_i128le: + case Basic_i128: case Basic_i128be: { // return imin <= i && i <= imax; @@ -2223,17 +2260,30 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i case Basic_u16: case Basic_u32: case Basic_u64: - case Basic_u128: case Basic_uint: case Basic_uintptr: case Basic_u16le: case Basic_u32le: case Basic_u64le: - case Basic_u128le: case Basic_u16be: case Basic_u32be: case Basic_u64be: + if (c->bit_field_bit_size == 0) { + if (big_int_is_neg(&i)) { + return false; + } + if (!big_int_can_be_represented_in_64_bits(&i)) { + return false; + } + u64 val64 = big_int_to_u64(&i); + + return val64 <= umax_64; + + } + /*fallthrough*/ + case Basic_u128: + case Basic_u128le: case Basic_u128be: { // return 0ull <= i && i <= umax; @@ -2955,14 +3005,21 @@ gb_internal void check_comparison(CheckerContext *c, Ast *node, Operand *x, Oper if (check_is_assignable_to(c, x, y->type) || check_is_assignable_to(c, y, x->type)) { + if (x->type->failure || y->type->failure) { + // // skip any failures + x->mode = Addressing_Value; + x->type = t_untyped_bool; + return; + } + Type *err_type = x->type; bool defined = false; switch (op) { case Token_CmpEq: case Token_NotEq: - defined = (is_type_comparable(x->type) && is_type_comparable(y->type)) || - (is_operand_nil(*x) && type_has_nil(y->type)) || - (is_operand_nil(*y) && type_has_nil(x->type)); + defined = ((is_operand_nil(*x) && type_has_nil(y->type)) || + (is_operand_nil(*y) && type_has_nil(x->type)) || + is_type_comparable(x->type) && is_type_comparable(y->type)); break; case Token_Lt: case Token_Gt: @@ -5169,7 +5226,7 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v continue; } ast_node(fv, FieldValue, elem); - String name = fv->field->Ident.token.string; + auto name = fv->field->Ident.interned; Selection sub_sel = lookup_field(node->tav.type, name, false); if (sub_sel.index.count > 0 && sub_sel.index[0] == index) { @@ -5435,8 +5492,12 @@ gb_internal Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast * return e; } } else */if (node->kind == Ast_Ident) { + Entity *e = node->Ident.entity.load(); + if (e != nullptr) { + return e; + } String name = node->Ident.token.string; - return scope_lookup(c->scope, name, node->Ident.hash); + return scope_lookup(c->scope, node->Ident.interned, node->Ident.hash); } else if (!ident_only) if (node->kind == Ast_SelectorExpr) { ast_node(se, SelectorExpr, node); if (se->token.kind == Token_ArrowRight) { @@ -5458,7 +5519,7 @@ gb_internal Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast * if (op_expr->kind == Ast_Ident) { String op_name = op_expr->Ident.token.string; - Entity *e = scope_lookup(c->scope, op_name, op_expr->Ident.hash); + Entity *e = scope_lookup(c->scope, op_expr->Ident.interned, op_expr->Ident.hash); if (e == nullptr) { return nullptr; } @@ -5471,7 +5532,7 @@ gb_internal Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast * // If you can clean this up, please do but be really careful String import_name = op_name; Scope *import_scope = e->ImportName.scope; - String entity_name = selector->Ident.token.string; + auto entity_name = selector->Ident.interned; check_op_expr = false; entity = scope_lookup_current(import_scope, entity_name); @@ -5497,7 +5558,7 @@ gb_internal Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast * } if (entity == nullptr && selector->kind == Ast_Ident) { - String field_name = selector->Ident.token.string; + auto field_name = selector->Ident.interned; if (is_type_dynamic_array(type_deref(operand.type))) { init_mem_allocator(c->checker); } @@ -5555,7 +5616,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod if (op_expr->kind == Ast_Ident) { String op_name = op_expr->Ident.token.string; - Entity *e = scope_lookup(c->scope, op_name, op_expr->Ident.hash); + Entity *e = scope_lookup(c->scope, op_expr->Ident.interned, op_expr->Ident.hash); add_entity_use(c, op_expr, e); expr_entity = e; @@ -5573,6 +5634,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod String import_name = op_name; Scope *import_scope = e->ImportName.scope; String entity_name = selector->Ident.token.string; + InternedString entity_name_interned = selector->Ident.interned; if (import_scope == nullptr) { ERROR_BLOCK(); @@ -5583,7 +5645,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod } check_op_expr = false; - entity = scope_lookup_current(import_scope, entity_name); + entity = scope_lookup_current(import_scope, entity_name_interned); bool allow_builtin = false; if (!is_entity_declared_for_selector(entity, import_scope, &allow_builtin)) { ERROR_BLOCK(); @@ -5631,7 +5693,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod } if (entity == nullptr && selector->kind == Ast_Ident) { - String field_name = selector->Ident.token.string; + auto field_name = selector->Ident.interned; Type *t = type_deref(operand->type); if (t == nullptr) { error(operand->expr, "Cannot use a selector expression on 0-value expression"); @@ -5970,15 +6032,14 @@ gb_internal bool is_type_valid_atomic_type(Type *elem) { gb_internal bool check_identifier_exists(Scope *s, Ast *node, bool nested = false, Scope **out_scope = nullptr) { switch (node->kind) { case_ast_node(i, Ident, node); - String name = i->token.string; if (nested) { - Entity *e = scope_lookup_current(s, name); + Entity *e = scope_lookup_current(s, i->interned, i->hash); if (e != nullptr) { if (out_scope) *out_scope = e->scope; return true; } } else { - Entity *e = scope_lookup(s, name, i->hash); + Entity *e = scope_lookup(s, i->interned, i->hash); if (e != nullptr) { if (out_scope) *out_scope = e->scope; return true; @@ -7673,7 +7734,7 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op } named_args = slice(ce->args, positional_args.count, ce->args.count); - auto split_args = gb_alloc_item(permanent_allocator(), AstSplitArgs); + auto split_args = permanent_alloc_item(); split_args->positional = positional_args; split_args->named = named_args; ce->split_args = split_args; @@ -7793,11 +7854,14 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O { // NOTE(bill, 2019-10-26): Allow a cycle in the parameters but not in the fields themselves auto prev_type_path = c->type_path; + + c->type_path = new_checker_type_path(); + defer ({ + destroy_checker_type_path(c->type_path); + c->type_path = prev_type_path; + }); + TEMPORARY_ALLOCATOR_GUARD(); - - c->type_path = new_checker_type_path(temporary_allocator()); - defer (c->type_path = prev_type_path); - if (is_call_expr_field_value(ce)) { named_fields = true; operands = array_make(temporary_allocator(), ce->args.count); @@ -8761,6 +8825,14 @@ gb_internal bool check_set_index_data(Operand *o, Type *t, bool indirection, i64 o->mode = Addressing_Variable; } return true; + + case Type_FixedCapacityDynamicArray: + o->type = t->FixedCapacityDynamicArray.elem; + if (o->mode != Addressing_Constant) { + o->mode = Addressing_Variable; + } + return true; + case Type_Struct: if (t->Struct.soa_kind != StructSoa_None) { if (t->Struct.soa_kind == StructSoa_Fixed) { @@ -8941,7 +9013,7 @@ gb_internal bool attempt_implicit_selector_expr(CheckerContext *c, Operand *o, A Type *enum_type = base_type(th); GB_ASSERT(enum_type->kind == Type_Enum); - String name = ise->selector->Ident.token.string; + auto name = ise->selector->Ident.interned; Entity *e = scope_lookup_current(enum_type->Enum.scope, name); if (e == nullptr) { @@ -9876,8 +9948,9 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, SliceIdent.token.string; + auto interned = ident->Ident.interned; - Selection sel = lookup_field(type, name, o->mode == Addressing_Type); + Selection sel = lookup_field(type, interned, o->mode == Addressing_Type); bool is_unknown = sel.entity == nullptr; if (is_unknown) { error(ident, "Unknown field '%.*s' in structure literal", LIT(name)); @@ -10297,6 +10370,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * case Type_DynamicArray: case Type_SimdVector: case Type_Matrix: + case Type_FixedCapacityDynamicArray: { Type *elem_type = nullptr; String context_name = {}; @@ -10327,6 +10401,10 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * elem_type = t->DynamicArray.elem; context_name = str_lit("dynamic array literal"); is_constant = false; + } else if (t->kind == Type_FixedCapacityDynamicArray) { + elem_type = t->FixedCapacityDynamicArray.elem; + context_name = str_lit("fixed capacity dynamic array literal"); + max_type_count = t->FixedCapacityDynamicArray.capacity; } else if (t->kind == Type_SimdVector) { elem_type = t->SimdVector.elem; context_name = str_lit("simd vector literal"); @@ -10827,8 +10905,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * continue; } String name = fv->field->Ident.token.string; + auto interned = fv->field->Ident.interned; - Selection sel = lookup_field(type, name, o->mode == Addressing_Type); + Selection sel = lookup_field(type, interned, o->mode == Addressing_Type); if (sel.entity == nullptr) { error(elem, "Unknown field '%.*s' in 'any' literal", LIT(name)); continue; @@ -11410,6 +11489,8 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, // Okay } else if (is_type_enumerated_array(t)) { // Okay + } else if (is_type_fixed_capacity_dynamic_array(t)) { + // Okay } else if (is_type_string(t)) { // Okay } else if (is_type_matrix(t)) { @@ -11556,6 +11637,11 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node, o->type = alloc_type_slice(t->DynamicArray.elem); break; + case Type_FixedCapacityDynamicArray: + valid = true; + o->type = alloc_type_slice(t->FixedCapacityDynamicArray.elem); + break; + case Type_Struct: if (is_type_soa_struct(t)) { valid = true; @@ -11966,6 +12052,17 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast if (o->mode != Addressing_Invalid) { check_unary_expr(c, o, ue->op, node); + } else { + ERROR_BLOCK(); + gbString s = expr_to_string(ue->expr); + defer (gb_string_free(s)); + + error(node, "Cannot address value '%s' as it has not got a determined type yet", s); + + Entity *e = entity_of_node(ue->expr); + if (e != nullptr && e->kind == Entity_Variable) { + error_line("\tSuggestion: Add an explicit type to the declaration of '%.*s' rather than relying on type inference", LIT(e->token.string)); + } } o->expr = node; return Expr_Expr; @@ -12110,6 +12207,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast case Ast_MultiPointerType: case Ast_ArrayType: case Ast_DynamicArrayType: + case Ast_FixedCapacityDynamicArrayType: case Ast_StructType: case Ast_UnionType: case Ast_EnumType: diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index bfa68ca78..5775c3e53 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -484,7 +484,7 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O } if (ident_node != nullptr) { ast_node(i, Ident, ident_node); - e = scope_lookup(ctx->scope, i->token.string, i->hash); + e = scope_lookup(ctx->scope, i->interned, i->hash); if (e != nullptr && e->kind == Entity_Variable) { used = (e->flags & EntityFlag_Used) != 0; // NOTE(bill): Make backup just in case } @@ -789,11 +789,12 @@ gb_internal bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, defer (rw_mutex_unlock(&scope->mutex)); for (auto const &entry : scope->elements) { - String name = entry.key; Entity *decl = entry.value; if (!is_entity_exported(decl, true)) continue; + u32 hash = entry.hash; + auto interned = scope->elements.keys[hash & (scope->elements.cap-1)]; - Entity *found = scope_insert_with_name(ctx->scope, name, decl); + Entity *found = scope_insert_with_name(ctx->scope, interned, hash, decl); if (found != nullptr) { gbString expr_str = expr_to_string(expr); error(us->token, @@ -1018,15 +1019,28 @@ gb_internal void check_unroll_range_stmt(CheckerContext *ctx, Ast *node, u32 mod inline_for_depth = exact_value_i64(unroll_count); } break; + case Type_FixedCapacityDynamicArray: + if (unroll_count > 0) { + val0 = t->FixedCapacityDynamicArray.elem; + val1 = t_int; + inline_for_depth = exact_value_i64(unroll_count); + } + break; } } if (val0 == nullptr) { + ERROR_BLOCK(); gbString s = expr_to_string(operand.expr); gbString t = type_to_string(operand.type); + defer (gb_string_free(s)); + defer (gb_string_free(t)); error(operand.expr, "Cannot iterate over '%s' of type '%s' in an '#unroll for' statement", s, t); - gb_string_free(t); - gb_string_free(s); + + if (is_type_slice(operand.type) || is_type_dynamic_array(operand.type) || is_type_fixed_capacity_dynamic_array(operand.type)) { + error_line("\tSuggestion: An unroll count `#unroll(N)` must be specified with an array of a runtime-known length\n"); + } + } else if (operand.mode != Addressing_Constant && ( unroll_count <= 0 && compare_exact_values(Token_CmpEq, inline_for_depth, exact_value_i64(0)))) { @@ -1053,7 +1067,7 @@ gb_internal void check_unroll_range_stmt(CheckerContext *ctx, Ast *node, u32 mod Entity *found = nullptr; if (!is_blank_ident(str)) { - found = scope_lookup_current(ctx->scope, str); + found = scope_lookup_current(ctx->scope, name->Ident.interned, name->Ident.hash); } if (found == nullptr) { entity = alloc_entity_variable(ctx->scope, token, type, EntityState_Resolved); @@ -1822,10 +1836,10 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) } if (rs->vals.count == 1 && rs->vals[0] && rs->vals[0]->kind == Ast_Ident) { AstIdent *ident = &rs->vals[0]->Ident; - String name = ident->token.string; - Entity *found = scope_lookup(ctx->scope, name, ident->hash); + Entity *found = scope_lookup(ctx->scope, ident->interned, ident->hash); if (found && are_types_identical(found->type, t->BitSet.elem)) { ERROR_BLOCK(); + String name = ident->token.string; gbString s = expr_to_string(expr); error(rs->vals[0], "'%.*s' shadows a previous declaration which might be ambiguous with 'for (%.*s in %s)'", LIT(name), LIT(name), s); error_line("\tSuggestion: Use a different identifier if iteration is wanted, or surround in parentheses if a normal for loop is wanted\n"); @@ -1846,6 +1860,12 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) array_add(&vals, t_int); break; + case Type_FixedCapacityDynamicArray: + is_possibly_addressable = operand.mode == Addressing_Variable || is_ptr; + array_add(&vals, t->FixedCapacityDynamicArray.elem); + array_add(&vals, t_int); + break; + case Type_DynamicArray: is_possibly_addressable = true; array_add(&vals, t->DynamicArray.elem); @@ -1868,10 +1888,10 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) } if (rs->vals.count == 1 && rs->vals[0] && rs->vals[0]->kind == Ast_Ident) { AstIdent *ident = &rs->vals[0]->Ident; - String name = ident->token.string; - Entity *found = scope_lookup(ctx->scope, name, ident->hash); + Entity *found = scope_lookup(ctx->scope, ident->interned, ident->hash); if (found && are_types_identical(found->type, t->Map.key)) { ERROR_BLOCK(); + String name = ident->token.string; gbString s = expr_to_string(expr); error(rs->vals[0], "'%.*s' shadows a previous declaration which might be ambiguous with 'for (%.*s in %s)'", LIT(name), LIT(name), s); error_line("\tSuggestion: Use a different identifier if iteration is wanted, or surround in parentheses if a normal for loop is wanted\n"); @@ -2012,7 +2032,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) Entity *found = nullptr; if (!is_blank_ident(str)) { - found = scope_lookup_current(ctx->scope, str); + found = scope_lookup_current(ctx->scope, name->Ident.interned, name->Ident.hash); } if (found == nullptr) { entity = alloc_entity_variable(ctx->scope, token, type, EntityState_Resolved); @@ -2106,7 +2126,7 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f Entity *found = nullptr; // NOTE(bill): Ignore assignments to '_' if (!is_blank_ident(str)) { - found = scope_lookup_current(ctx->scope, str); + found = scope_lookup_current(ctx->scope, name->Ident.interned, name->Ident.hash); new_name_count += 1; } if (found == nullptr) { diff --git a/src/check_type.cpp b/src/check_type.cpp index ed0d6528e..b625fb3d0 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -5,7 +5,9 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para gb_internal void populate_using_array_index(CheckerContext *ctx, Ast *node, AstField *field, Type *t, String name, i32 idx) { t = base_type(t); GB_ASSERT(t->kind == Type_Array); - Entity *e = scope_lookup_current(ctx->scope, name); + InternedString interned = string_interner_insert(name); + + Entity *e = scope_lookup_current(ctx->scope, interned); if (e != nullptr) { gbString str = nullptr; defer (gb_string_free(str)); @@ -37,22 +39,20 @@ gb_internal void populate_using_entity_scope(CheckerContext *ctx, Ast *node, Ast } Type *original_type = t; t = base_type(type_deref(t)); - gbString str = nullptr; - defer (gb_string_free(str)); - if (node != nullptr) { - str = expr_to_string(node); - } if (t->kind == Type_Struct) { for (Entity *f : t->Struct.fields) { GB_ASSERT(f->kind == Entity_Variable); String name = f->token.string; - Entity *e = scope_lookup_current(ctx->scope, name); + InternedString interned = entity_interned_name(f); + Entity *e = scope_lookup_current(ctx->scope, interned); if (e != nullptr && name != "_") { gbString ot = type_to_string(original_type); // TODO(bill): Better type error - if (str != nullptr) { + if (node != nullptr) { + gbString str = expr_to_string(node); error(e->token, "'%.*s' is already declared in '%s', through 'using' from '%s'", LIT(name), str, ot); + gb_string_free(str); } else { error(e->token, "'%.*s' is already declared, through 'using' from '%s'", LIT(name), ot); } @@ -102,9 +102,6 @@ gb_internal bool does_field_type_allow_using(Type *t) { gb_internal void check_struct_fields(CheckerContext *ctx, Ast *node, Slice *fields, String **tags, Slice const ¶ms, isize init_field_capacity, Type *struct_type, String context) { - auto fields_array = array_make(heap_allocator(), 0, init_field_capacity); - auto tags_array = array_make(heap_allocator(), 0, init_field_capacity); - GB_ASSERT(node->kind == Ast_StructType); GB_ASSERT(struct_type->kind == Type_Struct); @@ -117,6 +114,16 @@ gb_internal void check_struct_fields(CheckerContext *ctx, Ast *node, Slice(permanent_allocator(), 0, init_field_capacity); + auto tags_array = array_make(permanent_allocator(), 0, init_field_capacity); + + defer (GB_ASSERT(fields_array.count == init_field_capacity)); + + // Allocate all at once + Entity *entities_to_use = permanent_alloc_array(variable_count); + isize entities_to_use_index = 0; + i32 field_src_index = 0; i32 field_group_index = -1; for_array(i, params) { @@ -165,7 +172,17 @@ gb_internal void check_struct_fields(CheckerContext *ctx, Ast *node, SliceIdent.token; - Entity *field = alloc_entity_field(ctx->scope, name_token, type, is_using, field_src_index); + // Entity *field = alloc_entity_field(ctx->scope, name_token, type, is_using, field_src_index); + Entity *field = &entities_to_use[entities_to_use_index++]; + INTERNAL_ENTITY_INIT(field, Entity_Variable, ctx->scope, name_token, type); + + field->interned_name.store(name->Ident.interned); + field->interned_name_hash.store(name->Ident.hash); + + field->flags |= EntityFlag_Field; + if (is_using) field->flags |= EntityFlag_Using; + field->Variable.field_index = field_src_index; + add_entity(ctx, ctx->scope, name, field); field->Variable.field_group_index = field_group_index; if (is_subtype) { @@ -272,7 +289,7 @@ gb_internal GenTypesData *ensure_polymorphic_record_entity_has_gen_types(Checker GB_ASSERT(original_type->kind == Type_Named); mutex_lock(&original_type->Named.gen_types_data_mutex); if (original_type->Named.gen_types_data == nullptr) { - GenTypesData *gen_types = gb_alloc_item(permanent_allocator(), GenTypesData); + GenTypesData *gen_types = permanent_alloc_item(); gen_types->types = array_make(heap_allocator()); original_type->Named.gen_types_data = gen_types; } @@ -412,7 +429,7 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly Ast *s = type_expr->TypeidType.specialization; specialization = check_type(ctx, s); } - type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization); + type = alloc_type_generic(ctx->scope, 0, string_interner_insert(str_lit("")), specialization); } else { type = check_type(ctx, type_expr); if (is_type_polymorphic(type)) { @@ -636,7 +653,7 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast * isize min_field_count = 0; for_array(field_index, st->fields) { - Ast *field = st->fields[field_index]; + Ast *field = st->fields[field_index]; switch (field->kind) { case_ast_node(f, ValueDecl, field); min_field_count += f->names.count; @@ -887,6 +904,10 @@ gb_internal void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *nam scope_reserve(ctx->scope, et->fields.count); + // Allocate all at once + Entity *entities_to_use = permanent_alloc_array(et->fields.count); + isize entities_to_use_index = 0; + for_array(i, et->fields) { Ast *field = et->fields[i]; Ast *ident = nullptr; @@ -931,9 +952,6 @@ gb_internal void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *nam // NOTE(bill): Skip blank identifiers if (is_blank_ident(name)) { continue; - } else if (name == "names") { - error(field, "'names' is a reserved identifier for enumerations"); - continue; } if (min_value_set) { @@ -957,7 +975,15 @@ gb_internal void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *nam max_value_set = true; } - Entity *e = alloc_entity_constant(ctx->scope, ident->Ident.token, constant_type, iota); + // Entity *e = alloc_entity_constant(ctx->scope, ident->Ident.token, constant_type, iota); + Entity *e = &entities_to_use[entities_to_use_index++]; + Token token = ident->Ident.token; + INTERNAL_ENTITY_INIT(e, Entity_Constant, ctx->scope, token, constant_type); + + e->interned_name.store(ident->Ident.interned); + e->interned_name_hash.store(ident->Ident.hash); + + e->Constant.value = iota; e->identifier = ident; e->flags |= EntityFlag_Visited; e->state = EntityState_Resolved; @@ -965,7 +991,9 @@ gb_internal void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *nam e->Constant.docs = docs; e->Constant.comment = comment; - if (scope_lookup_current(ctx->scope, name) != nullptr) { + auto interned = entity_interned_name(e); + + if (scope_lookup_current(ctx->scope, interned) != nullptr) { error(ident, "'%.*s' is already declared in this enumeration", LIT(name)); } else { add_entity(ctx, ctx->scope, nullptr, e); @@ -1027,6 +1055,7 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, CommentGroup *comment = f->comment; String name = f->name->Ident.token.string; + InternedString interned = f->name->Ident.interned; if (f->type == nullptr) { error(field, "A bit_field's field must have a type"); @@ -1077,7 +1106,7 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, gb_string_free(s); } - if (scope_lookup_current(ctx->scope, name) != nullptr) { + if (scope_lookup_current(ctx->scope, interned) != nullptr) { error(f->name, "'%.*s' is already declared in this bit_field", LIT(name)); } else { i64 bit_size_i64 = exact_value_to_i64(bit_size); @@ -1497,7 +1526,7 @@ gb_internal bool check_type_specialization_to(CheckerContext *ctx, Type *special // NOTE(bill, 2018-12-14): This is needed to override polymorphic named constants in types if (st->kind == Type_Generic && t_e->kind == Entity_Constant) { - Entity *e = scope_lookup(st->Generic.scope, st->Generic.name); + Entity *e = scope_lookup(st->Generic.scope, st->Generic.interned_name, 0); GB_ASSERT(e != nullptr); if (modify_type) { e->kind = Entity_Constant; @@ -1550,7 +1579,7 @@ gb_internal bool check_type_specialization_to(CheckerContext *ctx, Type *special // NOTE(bill, 2018-12-14): This is needed to override polymorphic named constants in types if (st->kind == Type_Generic && t_e->kind == Entity_Constant) { - Entity *e = scope_lookup(st->Generic.scope, st->Generic.name); + Entity *e = scope_lookup(st->Generic.scope, st->Generic.interned_name, 0); GB_ASSERT(e != nullptr); if (modify_type) { e->kind = Entity_Constant; @@ -1828,6 +1857,11 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para bool is_c_vararg = false; auto variables = array_make(permanent_allocator(), 0, variable_count); i32 field_group_index = -1; + + + Entity *entities_to_use = permanent_alloc_array(variable_count); + isize entities_to_use_index = 0; + for_array(i, params) { Ast *param = params[i]; if (param->kind != Ast_Field) { @@ -1889,7 +1923,7 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para detemine_type_from_operand = true; type = t_invalid; } else { - type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization); + type = alloc_type_generic(ctx->scope, 0, string_interner_insert(str_lit("")), specialization); } } else { type = t_typeid; @@ -2067,7 +2101,12 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para p->flags &= ~FieldFlag_no_capture; } - param = alloc_entity_type_name(scope, name->Ident.token, type, EntityState_Resolved); + param = &entities_to_use[entities_to_use_index++]; + INTERNAL_ENTITY_INIT(param, Entity_TypeName, scope, name->Ident.token, type); + param->state = EntityState_Resolved; + param->interned_name.store(name->Ident.interned); + param->interned_name_hash.store(name->Ident.hash); + param->TypeName.is_type_alias = true; } else { ExactValue poly_const = {}; @@ -2221,10 +2260,36 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para // failed } - param = alloc_entity_const_param(scope, name->Ident.token, type, poly_const, is_type_polymorphic(type)); + // param = alloc_entity_const_param(scope, name->Ident.token, type, poly_const, is_type_polymorphic(type)); + + param = &entities_to_use[entities_to_use_index++]; + INTERNAL_ENTITY_INIT(param, Entity_Constant, scope, name->Ident.token, type); + + param->flags |= EntityFlag_Used|EntityFlag_Param; + if (is_type_polymorphic(type)) { + param->flags |= EntityFlag_PolyConst; + } + param->Constant.value = poly_const; + + param->interned_name.store(name->Ident.interned); + param->interned_name_hash.store(name->Ident.hash); + param->Constant.field_group_index = field_group_index; } else { - param = alloc_entity_param(scope, name->Ident.token, type, is_using, true); + // param = alloc_entity_param(scope, name->Ident.token, type, is_using, true); + + param = &entities_to_use[entities_to_use_index++]; + INTERNAL_ENTITY_INIT(param, Entity_Variable, scope, name->Ident.token, type); + + param->state = EntityState_Resolved; + param->flags |= EntityFlag_Used|EntityFlag_Param|EntityFlag_Value; + if (is_using) { + param->flags |= EntityFlag_Using; + } + + param->interned_name.store(name->Ident.interned); + param->interned_name_hash.store(name->Ident.hash); + param->Variable.param_value = param_value; param->Variable.field_group_index = field_group_index; param->Variable.type_expr = type_expr; @@ -2306,6 +2371,56 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para return tuple; } +// Check if an AST node references any polymorphic type parameters +// TODO(bill): is this even complete enough? +gb_internal bool ast_references_poly_params(Scope *scope, Ast *node) { + if (node == nullptr) { + return false; + } + switch (node->kind) { + case Ast_Ident: { + Entity *e = scope_lookup(scope, node->Ident.interned, node->Ident.hash); + if (e != nullptr && e->kind == Entity_TypeName && e->type != nullptr && e->type->kind == Type_Generic) { + return true; + } + return false; + } + case Ast_SelectorExpr: + return ast_references_poly_params(scope, node->SelectorExpr.expr); + case Ast_IndexExpr: + return ast_references_poly_params(scope, node->IndexExpr.expr); + case Ast_CallExpr: + for (Ast *arg : node->CallExpr.args) { + if (ast_references_poly_params(scope, arg)) { + return true; + } + } + return ast_references_poly_params(scope, node->CallExpr.proc); + case Ast_CompoundLit: + return ast_references_poly_params(scope, node->CompoundLit.type); + case Ast_UnaryExpr: + return ast_references_poly_params(scope, node->UnaryExpr.expr); + case Ast_ParenExpr: + return ast_references_poly_params(scope, node->ParenExpr.expr); + case Ast_DerefExpr: + return ast_references_poly_params(scope, node->DerefExpr.expr); + case Ast_PointerType: + return ast_references_poly_params(scope, node->PointerType.type); + case Ast_ArrayType: + return ast_references_poly_params(scope, node->ArrayType.elem) || + ast_references_poly_params(scope, node->ArrayType.count); + case Ast_FixedCapacityDynamicArrayType: + return ast_references_poly_params(scope, node->FixedCapacityDynamicArrayType.elem) || + ast_references_poly_params(scope, node->FixedCapacityDynamicArrayType.capacity); + case Ast_DynamicArrayType: + return ast_references_poly_params(scope, node->DynamicArrayType.elem); + case Ast_MapType: + return ast_references_poly_params(scope, node->MapType.key) || + ast_references_poly_params(scope, node->MapType.value); + } + return false; +} + gb_internal Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) { if (_results == nullptr) { return nullptr; @@ -2327,6 +2442,9 @@ gb_internal Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_res } } + Entity *entities_to_use = permanent_alloc_array(variable_count); + isize entities_to_use_index = 0; + auto variables = array_make(permanent_allocator(), 0, variable_count); i32 field_group_index = -1; for_array(i, results) { @@ -2340,7 +2458,12 @@ gb_internal Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_res if (field->type == nullptr) { param_value = handle_parameter_value(ctx, nullptr, &type, default_value, false); } else { - type = check_type(ctx, field->type); + if (ctx->allow_polymorphic_types && ast_references_poly_params(ctx->scope, field->type)) { + type = alloc_type_generic(ctx->scope, 0, string_interner_insert(str_lit("$deferred_return")), nullptr); + } else { + type = check_type(ctx, field->type); + } + if (default_value != nullptr) { param_value = handle_parameter_value(ctx, type, nullptr, default_value, false); @@ -2360,7 +2483,12 @@ gb_internal Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_res if (field->names.count == 0) { Token token = ast_token(field->type); token.string = str_lit(""); - Entity *param = alloc_entity_param(scope, token, type, false, false); + // Entity *param = alloc_entity_param(scope, token, type, false, false); + Entity *param = &entities_to_use[entities_to_use_index++]; + INTERNAL_ENTITY_INIT(param, Entity_Variable, scope, token, type); + param->state = EntityState_Resolved; + param->flags |= EntityFlag_Used|EntityFlag_Param|EntityFlag_Result; + param->Variable.param_value = param_value; param->Variable.field_group_index = -1; array_add(&variables, param); @@ -2383,8 +2511,17 @@ gb_internal Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_res error(name, "Result value cannot be a blank identifer `_`"); } - Entity *param = alloc_entity_param(scope, token, type, false, false); - param->flags |= EntityFlag_Result; + // Entity *param = alloc_entity_param(scope, token, type, false, false); + Entity *param = &entities_to_use[entities_to_use_index++]; + INTERNAL_ENTITY_INIT(param, Entity_Variable, scope, token, type); + param->state = EntityState_Resolved; + param->flags |= EntityFlag_Used|EntityFlag_Param|EntityFlag_Result; + + if (name->kind == Ast_Ident) { + param->interned_name.store(name->Ident.interned); + param->interned_name_hash.store(name->Ident.hash); + } + param->Variable.param_value = param_value; param->Variable.field_group_index = field_group_index; array_add(&variables, param); @@ -3149,7 +3286,7 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e soa_struct->Struct.fields = permanent_slice_make(field_count+extra_field_count); soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count); - string_map_init(&scope->elements, 8); + scope_map_init(&scope->elements); String params_xyzw[4] = { str_lit("x"), @@ -3247,7 +3384,7 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e add_type_info_type(ctx, soa_struct); wait_signal_set(&soa_struct->Struct.fields_wait_signal); } else { - SoaTypeWorkerData *wd = gb_alloc_item(permanent_allocator(), SoaTypeWorkerData); + SoaTypeWorkerData *wd = permanent_alloc_item(); wd->ctx = *ctx; wd->type = soa_struct; wd->wait_to_finish = true; @@ -3462,7 +3599,7 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T Ast *s = pt->specialization; specific = check_type(&c, s); } - Type *t = alloc_type_generic(ctx->scope, 0, token.string, specific); + Type *t = alloc_type_generic(ctx->scope, 0, ident->Ident.interned, specific); if (ctx->allow_polymorphic_types) { if (ctx->disallow_polymorphic_return_types) { error(ident, "Undeclared polymorphic parameter '%.*s' in return type", LIT(token.string)); @@ -3543,7 +3680,8 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T CheckerContext c = *ctx; TEMPORARY_ALLOCATOR_GUARD(); - c.type_path = new_checker_type_path(temporary_allocator()); + c.type_path = new_checker_type_path(); + defer (destroy_checker_type_path(c.type_path)); Type *elem = t_invalid; Operand o = {}; @@ -3642,6 +3780,31 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T return true; case_end; + case_ast_node(dat, FixedCapacityDynamicArrayType, e); + Operand o = {}; + i64 capacity = check_array_count(ctx, &o, dat->capacity); + Type *generic_type = nullptr; + if (o.mode == Addressing_Type && o.type->kind == Type_Generic) { + generic_type = o.type; + } + + if (capacity < 0) { + error(dat->capacity, "? can only be used in conjunction with compound literals of fixed-length arrays"); + capacity = 0; + } + + + Type *elem = check_type(ctx, dat->elem); + if (dat->tag != nullptr) { + GB_ASSERT(dat->tag->kind == Ast_BasicDirective); + String name = dat->tag->BasicDirective.name.string; + error(dat->tag, "Invalid tag applied to fixed capacity dynamic array, got #%.*s", LIT(name)); + } + *type = alloc_type_fixed_capacity_dynamic_array(elem, capacity, generic_type); + set_base_type(named_type, *type); + return true; + case_end; + case_ast_node(st, StructType, e); CheckerContext c = *ctx; c.in_polymorphic_specialization = false; @@ -3791,8 +3954,8 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T gb_internal Type *check_type(CheckerContext *ctx, Ast *e) { CheckerContext c = *ctx; - TEMPORARY_ALLOCATOR_GUARD(); - c.type_path = new_checker_type_path(temporary_allocator()); + c.type_path = new_checker_type_path(); + defer (destroy_checker_type_path(c.type_path)); return check_type_expr(&c, e, nullptr); } diff --git a/src/checker.cpp b/src/checker.cpp index 8acc5f4ae..ce8042b41 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -55,7 +55,8 @@ gb_internal bool check_rtti_type_disallowed(Ast *expr, Type *type, char const *f gb_internal void scope_reserve(Scope *scope, isize count) { - string_map_reserve(&scope->elements, 2*count); + scope_map_reserve(&scope->elements, 2*count); + // string_map_reserve(&scope->elements, 2*count); } gb_internal void entity_graph_node_set_destroy(EntityGraphNodeSet *s) { @@ -123,7 +124,7 @@ gb_internal void import_graph_node_set_add(ImportGraphNodeSet *s, ImportGraphNod // } gb_internal ImportGraphNode *import_graph_node_create(gbAllocator a, AstPackage *pkg) { - ImportGraphNode *n = gb_alloc_item(a, ImportGraphNode); + ImportGraphNode *n = permanent_alloc_item(); n->pkg = pkg; n->scope = pkg->scope; return n; @@ -163,7 +164,6 @@ gb_internal void import_graph_node_swap(ImportGraphNode **data, isize i, isize j gb_internal void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) { - gb_zero_item(d); if (parent) { mutex_lock(&parent->next_mutex); d->next_sibling = parent->next_child; @@ -181,7 +181,7 @@ gb_internal void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) { } gb_internal DeclInfo *make_decl_info(Scope *scope, DeclInfo *parent) { - DeclInfo *d = gb_alloc_item(permanent_allocator(), DeclInfo); + DeclInfo *d = permanent_alloc_item(); init_decl_info(d, scope, parent); return d; } @@ -214,7 +214,8 @@ gb_internal DeclInfo *make_decl_info(Scope *scope, DeclInfo *parent) { gb_internal Scope *create_scope(CheckerInfo *info, Scope *parent) { - Scope *s = gb_alloc_item(permanent_allocator(), Scope); + Scope *s = permanent_alloc_item(); + scope_map_init(&s->elements); s->parent = parent; if (parent != nullptr && parent != builtin_pkg->scope) { @@ -255,9 +256,10 @@ gb_internal Scope *create_scope_from_package(CheckerContext *c, AstPackage *pkg) total_pkg_decl_count += file->total_file_decl_count; } - isize init_elements_capacity = gb_max(DEFAULT_SCOPE_CAPACITY, 2*total_pkg_decl_count); + // isize init_elements_capacity = gb_max(DEFAULT_SCOPE_CAPACITY, 2*total_pkg_decl_count); Scope *s = create_scope(c->info, builtin_pkg->scope); - string_map_init(&s->elements, init_elements_capacity); + scope_map_reserve(&s->elements, 2*total_pkg_decl_count); + // string_map_init(&s->elements, init_elements_capacity); s->flags |= ScopeFlag_Pkg; s->pkg = pkg; @@ -284,7 +286,7 @@ gb_internal void destroy_scope(Scope *scope) { destroy_scope(child); } - string_map_destroy(&scope->elements); + // string_map_destroy(&scope->elements); ptr_set_destroy(&scope->imported); // NOTE(bill): No need to free scope as it "should" be allocated in an arena (except for the global scope) @@ -370,10 +372,14 @@ gb_internal void check_close_scope(CheckerContext *c) { } -gb_internal Entity *scope_lookup_current(Scope *s, String const &name) { - Entity **found = string_map_get(&s->elements, name); +gb_internal Entity *scope_lookup_current(Scope *s, InternedString name, u32 hash) { + // Entity **found = string_map_get(&s->elements, name); + if (hash == 0) { + hash = name.hash(); + } + Entity *found = scope_map_get(&s->elements, name, hash); if (found) { - return *found; + return found; } return nullptr; } @@ -381,25 +387,21 @@ gb_internal Entity *scope_lookup_current(Scope *s, String const &name) { gb_global std::atomic in_single_threaded_checker_stage; -gb_internal void scope_lookup_parent(Scope *scope, String const &name, Scope **scope_, Entity **entity_, u32 hash) { +gb_internal void scope_lookup_parent(Scope *scope, InternedString name, Scope **scope_, Entity **entity_, u32 hash) { bool is_single_threaded = in_single_threaded_checker_stage.load(std::memory_order_relaxed); if (scope != nullptr) { bool gone_thru_proc = false; bool gone_thru_package = false; - StringHashKey key = {}; - if (hash) { - key.hash = hash; - key.string = name; - } else { - key = string_hash_string(name); + if (!hash) { + hash = name.hash(); } for (Scope *s = scope; s != nullptr; s = s->parent) { - Entity **found = nullptr; + Entity *found = nullptr; if (!is_single_threaded) rw_mutex_shared_lock(&s->mutex); - found = string_map_get(&s->elements, key); + found = scope_map_get(&s->elements, name, hash); if (!is_single_threaded) rw_mutex_shared_unlock(&s->mutex); if (found) { - Entity *e = *found; + Entity *e = found; if (gone_thru_proc) { if (e->kind == Entity_Label) { continue; @@ -432,41 +434,40 @@ gb_internal void scope_lookup_parent(Scope *scope, String const &name, Scope **s if (scope_) *scope_ = nullptr; } -gb_internal Entity *scope_lookup(Scope *s, String const &name, u32 hash) { +gb_internal Entity *scope_lookup(Scope *s, InternedString interned, u32 hash) { Entity *entity = nullptr; - scope_lookup_parent(s, name, nullptr, &entity, hash); + scope_lookup_parent(s, interned, nullptr, &entity, hash); return entity; } -gb_internal Entity *scope_insert_with_name_no_mutex(Scope *s, String const &name, Entity *entity) { - if (name == "") { +gb_internal Entity *scope_insert_with_name_no_mutex(Scope *s, InternedString name, u32 hash, Entity *entity) { + if (name.value == 0) { return nullptr; } - StringHashKey key = string_hash_string(name); - Entity **found = nullptr; + Entity *found = nullptr; Entity *result = nullptr; - found = string_map_get(&s->elements, key); + found = scope_map_get(&s->elements, name, hash); if (found) { - if (entity != *found) { - result = *found; + if (entity != found) { + result = found; } goto end; } if (s->parent != nullptr && (s->parent->flags & ScopeFlag_Proc) != 0) { - found = string_map_get(&s->parent->elements, key); + found = scope_map_get(&s->parent->elements, name, hash); if (found) { - if ((*found)->flags & EntityFlag_Result) { - if (entity != *found) { - result = *found; + if (found->flags & EntityFlag_Result) { + if (entity != found) { + result = found; } goto end; } } } - string_map_set(&s->elements, key, entity); + scope_map_insert(&s->elements, name, hash, entity); if (entity->scope == nullptr) { entity->scope = s; } @@ -475,31 +476,30 @@ end:; } -gb_internal Entity *scope_insert_with_name(Scope *s, String const &name, Entity *entity) { - if (name == "") { +gb_internal Entity *scope_insert_with_name(Scope *s, InternedString name, u32 hash, Entity *entity) { + if (name.value == 0) { return nullptr; } - StringHashKey key = string_hash_string(name); - Entity **found = nullptr; + Entity *found = nullptr; Entity *result = nullptr; rw_mutex_lock(&s->mutex); - found = string_map_get(&s->elements, key); + found = scope_map_get(&s->elements, name, hash); if (found) { - if (entity != *found) { - result = *found; + if (entity != found) { + result = found; } goto end; } if (s->parent != nullptr && (s->parent->flags & ScopeFlag_Proc) != 0) { rw_mutex_shared_lock(&s->parent->mutex); - found = string_map_get(&s->parent->elements, key); + found = scope_map_get(&s->parent->elements, name, hash); if (found) { - if ((*found)->flags & EntityFlag_Result) { - if (entity != *found) { - result = *found; + if (found->flags & EntityFlag_Result) { + if (entity != found) { + result = found; } rw_mutex_shared_unlock(&s->parent->mutex); goto end; @@ -508,7 +508,7 @@ gb_internal Entity *scope_insert_with_name(Scope *s, String const &name, Entity rw_mutex_shared_unlock(&s->parent->mutex); } - string_map_set(&s->elements, key, entity); + scope_map_insert(&s->elements, name, hash, entity); if (entity->scope == nullptr) { entity->scope = s; } @@ -519,17 +519,21 @@ end:; } gb_internal Entity *scope_insert(Scope *s, Entity *entity) { - String name = entity->token.string; + auto name = entity_interned_name(entity); + u32 hash = entity->interned_name_hash.load(std::memory_order_relaxed); + GB_ASSERT(hash != 0); if (in_single_threaded_checker_stage.load(std::memory_order_relaxed)) { - return scope_insert_with_name_no_mutex(s, name, entity); + return scope_insert_with_name_no_mutex(s, name, hash, entity); } else { - return scope_insert_with_name(s, name, entity); + return scope_insert_with_name(s, name, hash, entity); } } gb_internal Entity *scope_insert_no_mutex(Scope *s, Entity *entity) { - String name = entity->token.string; - return scope_insert_with_name_no_mutex(s, name, entity); + auto name = string_interner_insert(entity->token.string); + u32 hash = entity->interned_name_hash.load(std::memory_order_relaxed); + GB_ASSERT(hash != 0); + return scope_insert_with_name_no_mutex(s, name, hash, entity); } @@ -663,7 +667,8 @@ gb_internal bool check_vet_shadowing(Checker *c, Entity *e, VettedEntity *ve) { return false; } - Entity *shadowed = scope_lookup(parent, name); + auto interned = entity_interned_name(e); + Entity *shadowed = scope_lookup(parent, interned, e->interned_name_hash.load()); if (shadowed == nullptr) { return false; } @@ -945,8 +950,10 @@ gb_internal AstPackage *try_get_core_package(CheckerInfo *info, String name) { gb_internal void add_package_dependency(CheckerContext *c, char const *package_name, char const *name, bool required=false) { String n = make_string_c(name); + u32 hash = 0; + InternedString key = string_interner_insert(n, 0, &hash); AstPackage *p = get_core_package(&c->checker->info, make_string_c(package_name)); - Entity *e = scope_lookup(p->scope, n); + Entity *e = scope_lookup(p->scope, key, hash); GB_ASSERT_MSG(e != nullptr, "%s", name); GB_ASSERT(c->decl != nullptr); e->flags |= EntityFlag_Used; @@ -958,8 +965,10 @@ gb_internal void add_package_dependency(CheckerContext *c, char const *package_n gb_internal void try_to_add_package_dependency(CheckerContext *c, char const *package_name, char const *name) { String n = make_string_c(name); + u32 hash = 0; + InternedString key = string_interner_insert(n, 0, &hash); AstPackage *p = get_core_package(&c->checker->info, make_string_c(package_name)); - Entity *e = scope_lookup(p->scope, n); + Entity *e = scope_lookup(p->scope, key, hash); if (e == nullptr) { return; } @@ -1018,7 +1027,7 @@ gb_internal void add_global_type_entity(String name, Type *type) { gb_internal AstPackage *create_builtin_package(char const *name) { gbAllocator a = permanent_allocator(); - AstPackage *pkg = gb_alloc_item(a, AstPackage); + AstPackage *pkg = permanent_alloc_item(); pkg->name = make_string_c(name); pkg->kind = Package_Builtin; @@ -1357,6 +1366,9 @@ gb_internal void init_universal(void) { BuiltinProcId id = cast(BuiltinProcId)i; String name = builtin_procs[i].name; if (name != "") { + u32 hash = 0; + InternedString interned = string_interner_insert(name, 0, &hash); + Entity *entity = alloc_entity(Entity_Builtin, nullptr, make_token_ident(name), t_invalid); entity->Builtin.id = id; switch (builtin_procs[i].pkg) { @@ -1365,7 +1377,7 @@ gb_internal void init_universal(void) { break; case BuiltinProcPkg_intrinsics: add_global_entity(entity, intrinsics_pkg->scope); - GB_ASSERT(scope_lookup_current(intrinsics_pkg->scope, name) != nullptr); + GB_ASSERT(scope_lookup_current(intrinsics_pkg->scope, interned, hash) != nullptr); break; } } @@ -1538,12 +1550,12 @@ gb_internal void init_checker_context(CheckerContext *ctx, Checker *c) { ctx->scope = builtin_pkg->scope; ctx->pkg = builtin_pkg; - ctx->type_path = new_checker_type_path(heap_allocator()); + ctx->type_path = new_checker_type_path(); ctx->type_level = 0; } gb_internal void destroy_checker_context(CheckerContext *ctx) { - destroy_checker_type_path(ctx->type_path, heap_allocator()); + destroy_checker_type_path(ctx->type_path); } gb_internal bool add_curr_ast_file(CheckerContext *ctx, AstFile *file) { @@ -2240,7 +2252,7 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) { case Type_BitSet: add_type_info_type_internal(c, bt->BitSet.elem); - add_type_info_type_internal(c, bt->BitSet.underlying); + add_type_info_type_internal(c, bit_set_to_int(bt)); break; case Type_Pointer: @@ -2276,6 +2288,11 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) { add_type_info_type_internal(c, t_int); break; + case Type_FixedCapacityDynamicArray: + add_type_info_type_internal(c, bt->FixedCapacityDynamicArray.elem); + add_type_info_type_internal(c, t_int); + break; + case Type_Enum: add_type_info_type_internal(c, bt->Enum.base_type); break; @@ -2407,7 +2424,7 @@ gb_internal void check_procedure_later(Checker *c, ProcInfo *info) { } gb_internal void check_procedure_later(Checker *c, AstFile *file, Token token, DeclInfo *decl, Type *type, Ast *body, u64 tags) { - ProcInfo *info = gb_alloc_item(permanent_allocator(), ProcInfo); + ProcInfo *info = permanent_alloc_item(); info->file = file; info->token = token; info->decl = decl; @@ -2479,7 +2496,7 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) { case Type_BitSet: add_min_dep_type_info(c, bt->BitSet.elem); - add_min_dep_type_info(c, bt->BitSet.underlying); + add_min_dep_type_info(c, bit_set_to_int(bt)); break; case Type_Pointer: @@ -2514,6 +2531,12 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) { add_min_dep_type_info(c, t_int); break; + case Type_FixedCapacityDynamicArray: + add_min_dep_type_info(c, bt->FixedCapacityDynamicArray.elem); + add_min_dep_type_info(c, alloc_type_pointer(bt->FixedCapacityDynamicArray.elem)); + add_min_dep_type_info(c, alloc_type_array(bt->FixedCapacityDynamicArray.elem, bt->FixedCapacityDynamicArray.capacity)); + add_min_dep_type_info(c, t_int); + case Type_Enum: add_min_dep_type_info(c, bt->Enum.base_type); break; @@ -2736,7 +2759,9 @@ gb_internal void add_dependency_to_set_threaded(Checker *c, Entity *entity) { gb_internal void force_add_dependency_entity(Checker *c, Scope *scope, String const &name) { - Entity *e = scope_lookup(scope, name); + u32 hash = 0; + auto interned = string_interner_insert(name, 0, &hash); + Entity *e = scope_lookup(scope, interned, hash); if (e == nullptr) { return; } @@ -2748,7 +2773,9 @@ gb_internal void force_add_dependency_entity(Checker *c, Scope *scope, String co gb_internal void collect_testing_procedures_of_package(Checker *c, AstPackage *pkg) { AstPackage *testing_package = get_core_package(&c->info, str_lit("testing")); Scope *testing_scope = testing_package->scope; - Entity *test_signature = scope_lookup_current(testing_scope, str_lit("Test_Signature")); + u32 hash = 0; + InternedString interned = string_interner_insert(str_lit("Test_Signature"), 0, &hash); + Entity *test_signature = scope_lookup_current(testing_scope, interned, hash); Scope *s = pkg->scope; for (auto const &entry : s->elements) { @@ -3202,7 +3229,9 @@ gb_internal void check_single_global_entity(Checker *c, Entity *e, DeclInfo *d); gb_internal Entity *find_core_entity(Checker *c, String name) { - Entity *e = scope_lookup_current(c->info.runtime_package->scope, name); + u32 hash = 0; + InternedString interned = string_interner_insert(name, 0, &hash); + Entity *e = scope_lookup_current(c->info.runtime_package->scope, interned, hash); if (e == nullptr) { compiler_error("Could not find type declaration for '%.*s'\n" , LIT(name)); @@ -3212,7 +3241,9 @@ gb_internal Entity *find_core_entity(Checker *c, String name) { } gb_internal Type *find_core_type(Checker *c, String name) { - Entity *e = scope_lookup_current(c->info.runtime_package->scope, name); + u32 hash = 0; + InternedString interned = string_interner_insert(name, 0, &hash); + Entity *e = scope_lookup_current(c->info.runtime_package->scope, interned, hash); if (e == nullptr) { compiler_error("Could not find type declaration for '%.*s'\n" , LIT(name)); @@ -3227,8 +3258,10 @@ gb_internal Type *find_core_type(Checker *c, String name) { gb_internal Entity *find_entity_in_pkg(CheckerInfo *info, String const &pkg, String const &name) { + u32 hash = 0; + InternedString interned = string_interner_insert(name, 0, &hash); AstPackage *package = get_core_package(info, pkg); - Entity *e = scope_lookup_current(package->scope, name); + Entity *e = scope_lookup_current(package->scope, interned, hash); if (e == nullptr) { compiler_error("Could not find type declaration for '%.*s.%.*s'\n", LIT(pkg), LIT(name)); // NOTE(bill): This will exit the program as it's cannot continue without it! @@ -3237,8 +3270,10 @@ gb_internal Entity *find_entity_in_pkg(CheckerInfo *info, String const &pkg, Str } gb_internal Type *find_type_in_pkg(CheckerInfo *info, String const &pkg, String const &name) { + u32 hash = 0; + InternedString interned = string_interner_insert(name, 0, &hash); AstPackage *package = get_core_package(info, pkg); - Entity *e = scope_lookup_current(package->scope, name); + Entity *e = scope_lookup_current(package->scope, interned, hash); if (e == nullptr) { compiler_error("Could not find type declaration for '%.*s.%.*s'\n", LIT(pkg), LIT(name)); // NOTE(bill): This will exit the program as it's cannot continue without it! @@ -3247,15 +3282,24 @@ gb_internal Type *find_type_in_pkg(CheckerInfo *info, String const &pkg, String return e->type; } -gb_internal CheckerTypePath *new_checker_type_path(gbAllocator allocator) { - auto *tp = gb_alloc_item(allocator, CheckerTypePath); - array_init(tp, allocator, 0, 16); - return tp; +gb_internal gb_thread_local std::atomic *> checker_type_path_free_list; + +gb_internal CheckerTypePath *new_checker_type_path() { + // TODO(bill): Cache to reuse `CheckerTypePath + + auto *tp = atomic_freelist_get(checker_type_path_free_list); + if (tp == nullptr) { + tp = permanent_alloc_item >(); + array_init(&tp->value, permanent_allocator(), 0, 16); + } + return &tp->value; } -gb_internal void destroy_checker_type_path(CheckerTypePath *tp, gbAllocator allocator) { - array_free(tp); - gb_free(allocator, tp); +gb_internal void destroy_checker_type_path(CheckerTypePath *path) { + auto *tp = cast(AtomicFreelist *)path; + array_clear(&tp->value); + + atomic_freelist_put(checker_type_path_free_list, tp); } gb_internal void check_type_path_push(CheckerContext *c, Entity *e) { @@ -3350,6 +3394,7 @@ gb_internal void init_core_type_info(Checker *c) { t_type_info_matrix = find_core_type(c, str_lit("Type_Info_Matrix")); t_type_info_soa_pointer = find_core_type(c, str_lit("Type_Info_Soa_Pointer")); t_type_info_bit_field = find_core_type(c, str_lit("Type_Info_Bit_Field")); + t_type_info_fixed_capacity_dynamic_array = find_core_type(c, str_lit("Type_Info_Fixed_Capacity_Dynamic_Array")); t_type_info_named_ptr = alloc_type_pointer(t_type_info_named); t_type_info_integer_ptr = alloc_type_pointer(t_type_info_integer); @@ -3378,6 +3423,7 @@ gb_internal void init_core_type_info(Checker *c) { t_type_info_matrix_ptr = alloc_type_pointer(t_type_info_matrix); t_type_info_soa_pointer_ptr = alloc_type_pointer(t_type_info_soa_pointer); t_type_info_bit_field_ptr = alloc_type_pointer(t_type_info_bit_field); + t_type_info_fixed_capacity_dynamic_array_ptr = alloc_type_pointer(t_type_info_fixed_capacity_dynamic_array); } gb_internal void init_mem_allocator(Checker *c) { @@ -4269,6 +4315,20 @@ gb_internal DECL_ATTRIBUTE_PROC(type_decl_attribute) { error(elem, "Expected a string or no value for '%.*s'", LIT(name)); } return true; + } else if (name == "deprecated") { + ExactValue ev = check_decl_attribute_value(c, value); + + if (ev.kind == ExactValue_String) { + String msg = ev.value_string; + if (msg.len == 0) { + error(elem, "Deprecation message cannot be an empty string"); + } else { + ac->deprecated_message = msg; + } + } else { + error(elem, "Expected a string value for '%.*s'", LIT(name)); + } + return true; } return false; } @@ -4514,7 +4574,9 @@ gb_internal void check_builtin_attributes(CheckerContext *ctx, Entity *e, Array< if (name == "builtin") { mutex_lock(&ctx->info->builtin_mutex); add_entity(ctx, builtin_pkg->scope, nullptr, e); - GB_ASSERT(scope_lookup(builtin_pkg->scope, e->token.string) != nullptr); + auto interned = entity_interned_name(e); + u32 hash = e->interned_name_hash.load(); + GB_ASSERT(scope_lookup(builtin_pkg->scope, interned, hash) != nullptr); if (value != nullptr) { error(value, "'builtin' cannot have a field value"); } @@ -4856,9 +4918,9 @@ gb_internal bool correct_single_type_alias(CheckerContext *c, Entity *e) { gb_internal bool correct_type_alias_in_scope_backwards(CheckerContext *c, Scope *s) { bool correction = false; for (u32 n = s->elements.count, i = n-1; i < n; i--) { - auto const &entry = s->elements.entries[i]; - Entity *e = entry.value; - if (entry.hash && e != nullptr) { + auto const &slot = s->elements.slots[i]; + Entity *e = slot.value; + if (slot.hash && e != nullptr) { correction |= correct_single_type_alias(c, e); } } @@ -4989,7 +5051,7 @@ gb_internal void check_collect_entities(CheckerContext *c, Slice const &n } gb_internal CheckerContext *create_checker_context(Checker *c) { - CheckerContext *ctx = gb_alloc_item(permanent_allocator(), CheckerContext); + CheckerContext *ctx = permanent_alloc_item(); init_checker_context(ctx, c); return ctx; } @@ -6201,7 +6263,7 @@ gb_internal void check_procedure_later_from_entity(Checker *c, Entity *e, char c GB_ASSERT(e->decl_info != nullptr); - ProcInfo *pi = gb_alloc_item(permanent_allocator(), ProcInfo); + ProcInfo *pi = permanent_alloc_item(); pi->file = e->file; pi->token = e->token; pi->decl = e->decl_info; @@ -7470,7 +7532,7 @@ gb_internal void check_parsed_files(Checker *c) { Scope *s = c->info.init_scope; GB_ASSERT(s != nullptr); GB_ASSERT(s->flags&ScopeFlag_Init); - Entity *e = scope_lookup_current(s, str_lit("main")); + Entity *e = scope_lookup_current(s, string_interner_insert(str_lit("main"))); if (e == nullptr) { Token token = {}; token.pos.file_id = 0; diff --git a/src/checker.hpp b/src/checker.hpp index 374aaf10d..5e295dc84 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -250,7 +250,7 @@ struct DeclInfo { i64 variadic_reuse_max_align; // NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time - struct lbModule *code_gen_module; + std::atomic code_gen_module; }; // ProcInfo stores the information needed for checking a procedure @@ -266,6 +266,264 @@ struct ProcInfo { }; +enum { DEFAULT_SCOPE_CAPACITY = 32 }; + + +struct ScopeMapSlot { + u32 hash; + u32 _pad; + Entity *value; +}; + +enum { SCOPE_MAP_INLINE_CAP = 16 }; + +struct ScopeMap { + InternedString inline_keys [SCOPE_MAP_INLINE_CAP]; + ScopeMapSlot inline_slots[SCOPE_MAP_INLINE_CAP]; + InternedString *keys; + ScopeMapSlot * slots; + u32 count; + u32 cap; +}; + +gb_internal gb_inline u32 scope_map_max_load(u32 cap) { + return cap - (cap>>2); // 75% +} + +gb_internal gb_inline void scope_map_init(ScopeMap *m) { + m->cap = SCOPE_MAP_INLINE_CAP; + m->slots = m->inline_slots; + m->keys = m->inline_keys; +} + + +gb_internal Entity *scope_map_insert_for_rehash( + InternedString *keys, ScopeMapSlot *slots, u32 mask, + InternedString key, u32 hash, Entity *value) { + u32 pos = hash & mask; + u32 dist = 0; + + for (;;) { + ScopeMapSlot *s = &slots[pos]; + + if (s->hash == 0) { + keys[pos] = key; + s->hash = hash; + s->value = value; + return nullptr; + } + + u32 existing_dist = (pos - s->hash) & mask; + + if (dist > existing_dist) { + auto tmp_key = keys[pos]; + u32 tmp_hash = s->hash; + Entity *tmp_value = s->value; + + keys[pos] = key; + s->hash = hash; + s->value = value; + + hash = tmp_hash; + value = tmp_value; + key = tmp_key; + dist = existing_dist; + } + + dist += 1; + pos = (pos+1) & mask; + } +} + +gb_internal gb_inline void scope_map_allocate_entries(u32 cap, InternedString **keys, ScopeMapSlot **slots) { + Arena *arena = get_arena(ThreadArena_Permanent); + isize size = (gb_size_of(InternedString) + gb_size_of(ScopeMapSlot)) * cap; + u8 *data = cast(u8 *)arena_alloc(arena, size, 8); + + *keys = cast(InternedString *)data; + *slots = cast(ScopeMapSlot *)(*keys + cap); + + // *keys = permanent_alloc_array(cap); + // *slots = permanent_alloc_array(cap); +} + + +gb_internal void scope_map_grow(ScopeMap *m) { + u32 new_cap = m->cap << 1; + u32 new_mask = new_cap - 1; + + InternedString *new_keys; + ScopeMapSlot * new_slots; + scope_map_allocate_entries(new_cap, &new_keys, &new_slots); + + if (m->count > 0) { + for (u32 i = 0; i < m->cap; i++) { + if (m->slots[i].hash) { + scope_map_insert_for_rehash(new_keys, new_slots, new_mask, + m->keys[i], m->slots[i].hash, m->slots[i].value); + } + } + } + + m->slots = new_slots; + m->keys = new_keys; + m->cap = new_cap; +} + +gb_internal void scope_map_reserve(ScopeMap *m, isize capacity) { + if (m->slots == nullptr) { + scope_map_init(m); + } + u32 new_cap = next_pow2_u32(cast(u32)capacity); + if (m->cap < new_cap && new_cap > SCOPE_MAP_INLINE_CAP) { + scope_map_allocate_entries(new_cap, &m->keys, &m->slots); + m->cap = new_cap; + } +} + + + +gb_internal Entity *scope_map_insert(ScopeMap *m, InternedString key, u32 hash, Entity *value) { + if (m->slots == nullptr) { + scope_map_init(m); + } + if (m->count >= scope_map_max_load(m->cap)) { + scope_map_grow(m); + } + + u32 mask = m->cap-1; + u32 pos = hash & mask; + u32 dist = 0; + + for (;;) { + ScopeMapSlot *s = &m->slots[pos]; + + if (s->hash == 0) { + m->keys[pos] = key; + s->hash = hash; + s->value = value; + m->count += 1; + return nullptr; + } + + if (s->hash == hash && m->keys[pos] == key) { + Entity *old = s->value; + s->value = value; + return old; + } + + u32 existing_dist = (pos - s->hash) & mask; + + if (dist > existing_dist) { + auto tmp_key = m->keys[pos]; + u32 tmp_hash = s->hash; + Entity *tmp_value = s->value; + + m->keys[pos] = key; + s->hash = hash; + s->value = value; + + key = tmp_key; + hash = tmp_hash; + value = tmp_value; + dist = existing_dist; + } + + dist += 1; + pos = (pos+1) & mask; + } +} + +gb_internal Entity *scope_map_get(ScopeMap *m, InternedString key, u32 hash) { + u32 mask = m->cap-1; + u32 pos = hash & mask; + u32 dist = 0; + for (;;) { + ScopeMapSlot *s = &m->slots[pos]; + u32 curr_hash = s->hash; + if (curr_hash == 0) { + return nullptr; + } + + u32 existing_dist = (pos - curr_hash) & mask; + if (dist > existing_dist) { + return nullptr; + } + if (curr_hash == hash && m->keys[pos] == key) { + return s->value; + } + + dist += 1; + pos = (pos + 1) & mask; + } +} + +gb_internal void scope_map_clear(ScopeMap *m) { + gb_memset(m->slots, 0, gb_size_of(*m->slots) * m->cap); + m->count = 0; +} + +struct ScopeMapIterator { + ScopeMap const *map; + u32 index; + + ScopeMapIterator &operator++() noexcept { + for (;;) { + ++index; + if (map->cap == index) { + return *this; + } + ScopeMapSlot *s = map->slots+index; + if (s->hash) { + return *this; + } + } + } + + bool operator==(ScopeMapIterator const &other) const noexcept { + return this->map == other.map && this->index == other.index; + } + + operator ScopeMapSlot *() const { + return map->slots+index; + } +}; + + +gb_internal ScopeMapIterator end(ScopeMap &m) noexcept { + return ScopeMapIterator{&m, m.cap}; +} +gb_internal ScopeMapIterator const end(ScopeMap const &m) noexcept { + return ScopeMapIterator{&m, m.cap}; +} +gb_internal ScopeMapIterator begin(ScopeMap &m) noexcept { + if (m.count == 0) { + return end(m); + } + + u32 index = 0; + while (index < m.cap) { + if (m.slots[index].hash) { + break; + } + index++; + } + return ScopeMapIterator{&m, index}; +} +gb_internal ScopeMapIterator const begin(ScopeMap const &m) noexcept { + if (m.count == 0) { + return end(m); + } + + u32 index = 0; + while (index < m.cap) { + if (m.slots[index].hash) { + break; + } + index++; + } + return ScopeMapIterator{&m, index}; +} enum ScopeFlag : i32 { ScopeFlag_Pkg = 1<<1, @@ -281,7 +539,6 @@ enum ScopeFlag : i32 { ScopeFlag_ContextDefined = 1<<16, }; -enum { DEFAULT_SCOPE_CAPACITY = 32 }; struct Scope { Ast * node; @@ -292,7 +549,7 @@ struct Scope { i32 index; // within a procedure RwMutex mutex; - StringMap elements; + ScopeMap elements; PtrSet imported; DeclInfo *decl_info; @@ -625,9 +882,9 @@ gb_internal isize type_info_index (CheckerInfo *info, TypeInfoPair gb_internal Entity *entity_of_node(Ast *expr); -gb_internal Entity *scope_lookup_current(Scope *s, String const &name); -gb_internal Entity *scope_lookup (Scope *s, String const &name, u32 hash=0); -gb_internal void scope_lookup_parent (Scope *s, String const &name, Scope **scope_, Entity **entity_, u32 hash=0); +// gb_internal Entity *scope_lookup_current(Scope *s, String const &name, u32 hash=0); +gb_internal Entity *scope_lookup (Scope *s, InternedString interned, u32 hash); +gb_internal void scope_lookup_parent (Scope *s, InternedString name, Scope **scope_, Entity **entity_, u32 hash); gb_internal Entity *scope_insert (Scope *s, Entity *entity); @@ -652,8 +909,8 @@ gb_internal void check_collect_entities(CheckerContext *c, Slice const &n gb_internal void check_collect_entities_from_when_stmt(CheckerContext *c, AstWhenStmt *ws); gb_internal void check_delayed_file_import_entity(CheckerContext *c, Ast *decl); -gb_internal CheckerTypePath *new_checker_type_path(gbAllocator allocator); -gb_internal void destroy_checker_type_path(CheckerTypePath *tp, gbAllocator allocator); +gb_internal CheckerTypePath *new_checker_type_path(); +gb_internal void destroy_checker_type_path(CheckerTypePath *tp); gb_internal void check_type_path_push(CheckerContext *c, Entity *e); gb_internal Entity *check_type_path_pop (CheckerContext *c); diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index a13ffc3cd..f73422932 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -144,6 +144,8 @@ BuiltinProc__atomic_end, BuiltinProc_fixed_point_div_sat, BuiltinProc_expect, + BuiltinProc_likely, + BuiltinProc_unlikely, BuiltinProc__simd_begin, BuiltinProc_simd_add, @@ -297,6 +299,7 @@ BuiltinProc__type_simple_boolean_begin, BuiltinProc_type_is_simd_vector, BuiltinProc_type_is_matrix, BuiltinProc_type_is_raw_union, + BuiltinProc_type_is_fixed_capacity_dynamic_array, BuiltinProc_type_is_specialized_polymorphic_record, @@ -342,6 +345,8 @@ BuiltinProc__type_simple_boolean_end, BuiltinProc_type_field_index_of, + BuiltinProc_type_fixed_capacity_dynamic_array_len_offset, + BuiltinProc_type_bit_set_backing_type, BuiltinProc_type_enum_is_contiguous, @@ -524,7 +529,9 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("fixed_point_mul_sat"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("fixed_point_div_sat"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("expect"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("expect"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("likely"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("unlikely"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, @@ -674,6 +681,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_is_simd_vector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_matrix"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_raw_union"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_fixed_capacity_dynamic_array"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_specialized_polymorphic_record"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_unspecialized_polymorphic_record"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, @@ -717,6 +725,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_field_index_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_fixed_capacity_dynamic_array_len_offset"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_bit_set_backing_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_enum_is_contiguous"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics }, diff --git a/src/common.cpp b/src/common.cpp index d5fc1df4b..86ebb0fa8 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -366,6 +366,7 @@ gb_global bool global_module_path_set = false; #include "string_set.cpp" #include "priority_queue.cpp" #include "thread_pool.cpp" +#include "string_interner.cpp" gb_internal String obfuscate_string(String const &s, char const *prefix) { @@ -388,48 +389,6 @@ gb_internal i32 obfuscate_i32(i32 i) { } - -struct StringIntern { - StringIntern *next; - isize len; - char str[1]; -}; - -PtrMap string_intern_map = {}; // Key: u64 -gb_global Arena string_intern_arena = {}; - -gb_internal char const *string_intern(char const *text, isize len) { - u64 hash = gb_fnv64a(text, len); - uintptr key = cast(uintptr)(hash ? hash : 1); - StringIntern **found = map_get(&string_intern_map, key); - if (found) { - for (StringIntern *it = *found; it != nullptr; it = it->next) { - if (it->len == len && gb_strncmp(it->str, (char *)text, len) == 0) { - return it->str; - } - } - } - - StringIntern *new_intern = cast(StringIntern *)arena_alloc(&string_intern_arena, gb_offset_of(StringIntern, str) + len + 1, gb_align_of(StringIntern)); - new_intern->len = len; - new_intern->next = found ? *found : nullptr; - gb_memmove(new_intern->str, text, len); - new_intern->str[len] = 0; - map_set(&string_intern_map, key, new_intern); - return new_intern->str; -} - -gb_internal char const *string_intern(String const &string) { - return string_intern(cast(char const *)string.text, string.len); -} - -gb_internal void init_string_interner(void) { - map_init(&string_intern_map); -} - - - - gb_internal i32 next_pow2(i32 n) { if (n <= 0) { return 0; diff --git a/src/common_memory.cpp b/src/common_memory.cpp index addd43687..1590aac65 100644 --- a/src/common_memory.cpp +++ b/src/common_memory.cpp @@ -40,6 +40,7 @@ struct MemoryBlock { u8 * base; isize size; isize used; + isize committed; }; struct Arena { @@ -48,13 +49,14 @@ struct Arena { // BlockingMutex mutex; isize temp_count; Thread * parent_thread; + bool custom_arena; }; enum { DEFAULT_MINIMUM_BLOCK_SIZE = 8ll*1024ll*1024ll }; gb_global isize DEFAULT_PAGE_SIZE = 4096; -gb_internal MemoryBlock *virtual_memory_alloc(isize size); +gb_internal MemoryBlock *virtual_memory_alloc(isize size, bool commit); gb_internal void virtual_memory_dealloc(MemoryBlock *block); gb_internal void *arena_alloc(Arena *arena, isize min_size, isize alignment); gb_internal void arena_free_all(Arena *arena); @@ -82,7 +84,7 @@ gb_internal void thread_init_arenas(Thread *t) { gb_internal void *arena_alloc(Arena *arena, isize min_size, isize alignment) { GB_ASSERT(gb_is_power_of_two(alignment)); - GB_ASSERT(arena->parent_thread == get_current_thread()); + GB_ASSERT(arena->custom_arena || arena->parent_thread == get_current_thread()); isize size = 0; if (arena->curr_block != nullptr) { @@ -95,7 +97,7 @@ gb_internal void *arena_alloc(Arena *arena, isize min_size, isize alignment) { isize block_size = gb_max(size, arena->minimum_block_size); - MemoryBlock *new_block = virtual_memory_alloc(block_size); + MemoryBlock *new_block = virtual_memory_alloc(block_size, true); new_block->prev = arena->curr_block; arena->curr_block = new_block; } @@ -113,6 +115,62 @@ gb_internal void *arena_alloc(Arena *arena, isize min_size, isize alignment) { return ptr; } +gb_internal void *platform_virtual_memory_alloc_internal(isize total_size, bool commit); +gb_internal bool platform_virtual_memory_commit_internal(void *data, isize commit_amount); + +struct StaticArena { + u8 * data; + isize used; + isize committed; + isize reserved; + isize commit_block_size; +}; + +enum {STATIC_ARENA_DEFAULT_COMMIT_BLOCK_SIZE = 8<<20}; + +gb_internal void static_arena_init(StaticArena *arena, isize reserve_size, isize commit_block_size) { + GB_ASSERT(gb_is_power_of_two(reserve_size)); + GB_ASSERT(gb_is_power_of_two(commit_block_size)); + GB_ASSERT(commit_block_size <= reserve_size); + arena->data = cast(u8 *)platform_virtual_memory_alloc_internal(reserve_size, false); + arena->reserved = reserve_size; + arena->commit_block_size = commit_block_size; +} + +gb_internal void static_arena_commit_memory(StaticArena *arena, isize amount) { + isize blocks = (amount + arena->commit_block_size-1)/arena->commit_block_size; + isize total_amount = blocks * arena->commit_block_size; + + if (total_amount > arena->reserved - arena->committed) { + total_amount = arena->reserved - arena->committed; + } + + platform_virtual_memory_commit_internal(arena->data + arena->committed, total_amount); + arena->committed += total_amount; +} + +gb_internal void *static_arena_alloc(StaticArena *arena, isize size, isize alignment) { + GB_ASSERT(gb_is_power_of_two(alignment)); + + size = align_formula_isize(size, alignment); + + u8 *curr = arena->data + arena->used; + curr = cast(u8 *)align_formula_ptr(curr, alignment); + + u8 *end = curr + size; + if (end-arena->data > arena->committed) { + isize needed = (end - arena->data) - arena->committed; + static_arena_commit_memory(arena, needed); + } + GB_ASSERT_MSG(end-arena->data <= arena->committed, "out of memory for the static arena"); + + arena->used = end - arena->data; + + return curr; +} + + + template gb_internal T *arena_alloc_item(Arena *arena) { @@ -138,10 +196,13 @@ struct PlatformMemoryBlock { gb_global std::atomic global_platform_memory_total_usage; gb_global PlatformMemoryBlock global_platform_memory_block_sentinel; -gb_internal PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size); +gb_internal PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size, bool commit); gb_internal void platform_virtual_memory_free(PlatformMemoryBlock *block); gb_internal void platform_virtual_memory_protect(void *memory, isize size); +gb_internal void *platform_virtual_memory_alloc_internal(isize total_size, bool commit); +gb_internal bool platform_virtual_memory_commit_internal(void *data, isize commit_amount); + #if defined(GB_SYSTEM_WINDOWS) gb_internal void platform_virtual_memory_init(void) { global_platform_memory_block_sentinel.prev = &global_platform_memory_block_sentinel; @@ -153,14 +214,20 @@ gb_internal void platform_virtual_memory_protect(void *memory, isize size); GB_ASSERT(gb_is_power_of_two(DEFAULT_PAGE_SIZE)); } - gb_internal PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size) { - PlatformMemoryBlock *pmblock = (PlatformMemoryBlock *)VirtualAlloc(0, total_size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); - if (pmblock == nullptr) { + gb_internal void *platform_virtual_memory_alloc_internal(isize total_size, bool commit) { + DWORD flags = commit ? MEM_RESERVE|MEM_COMMIT : MEM_RESERVE; + void *mem = VirtualAlloc(0, total_size, flags, PAGE_READWRITE); + if (mem == nullptr) { gb_printf_err("Out of Virtual memory, oh no...\n"); gb_printf_err("Requested: %lld bytes\n", cast(long long)total_size); gb_printf_err("Total Usage: %lld bytes\n", cast(long long)global_platform_memory_total_usage); - GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no..."); + GB_ASSERT_MSG(mem != nullptr, "Out of Virtual Memory, oh no..."); } + return mem; + } + + gb_internal PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size, bool commit) { + PlatformMemoryBlock *pmblock = cast(PlatformMemoryBlock *)platform_virtual_memory_alloc_internal(total_size, commit); global_platform_memory_total_usage.fetch_add(total_size); return pmblock; } @@ -173,6 +240,16 @@ gb_internal void platform_virtual_memory_protect(void *memory, isize size); BOOL is_protected = VirtualProtect(memory, size, PAGE_NOACCESS, &old_protect); GB_ASSERT(is_protected); } + + gb_internal bool platform_virtual_memory_commit_internal(void *data, isize commit_amount) { + void *res = VirtualAlloc(data, commit_amount, MEM_COMMIT, PAGE_READWRITE); + if (res == nullptr) { + GB_PANIC("Out of Virtual Memory, oh no...\n"); + GB_ASSERT_MSG(res != nullptr, "Out of Virtual Memory, oh no..."); + return false; + } + return true; + } #else #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) #define MAP_ANONYMOUS MAP_ANON @@ -185,9 +262,20 @@ gb_internal void platform_virtual_memory_protect(void *memory, isize size); DEFAULT_PAGE_SIZE = gb_max(DEFAULT_PAGE_SIZE, cast(isize)sysconf(_SC_PAGE_SIZE)); GB_ASSERT(gb_is_power_of_two(DEFAULT_PAGE_SIZE)); } + + gb_internal void *platform_virtual_memory_alloc_internal(isize total_size, bool commit) { + void *mem = mmap(nullptr, total_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (mem == nullptr) { + gb_printf_err("Out of Virtual memory, oh no...\n"); + gb_printf_err("Requested: %lld bytes\n", cast(long long)total_size); + gb_printf_err("Total Usage: %lld bytes\n", cast(long long)global_platform_memory_total_usage); + GB_ASSERT_MSG(mem != nullptr, "Out of Virtual Memory, oh no..."); + } + return mem; + } - gb_internal PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size) { - PlatformMemoryBlock *pmblock = (PlatformMemoryBlock *)mmap(nullptr, total_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + gb_internal PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size, bool commit) { + PlatformMemoryBlock *pmblock = cast(PlatformMemoryBlock *)platform_virtual_memory_alloc_internal(total_size, commit); if (pmblock == nullptr) { gb_printf_err("Out of Virtual memory, oh no...\n"); gb_printf_err("Requested: %lld bytes\n", cast(long long)total_size); @@ -197,6 +285,9 @@ gb_internal void platform_virtual_memory_protect(void *memory, isize size); global_platform_memory_total_usage.fetch_add(total_size); return pmblock; } + gb_internal PlatformMemoryBlock *platform_virtual_memory_alloc_uncommited(isize total_size) { + return platform_virtual_memory_alloc(total_size, false); + } gb_internal void platform_virtual_memory_free(PlatformMemoryBlock *block) { isize size = block->total_size; global_platform_memory_total_usage.fetch_sub(size); @@ -206,9 +297,19 @@ gb_internal void platform_virtual_memory_protect(void *memory, isize size); int err = mprotect(memory, size, PROT_NONE); GB_ASSERT(err == 0); } + + gb_internal bool platform_virtual_memory_commit_internal(void *data, isize commit_amount) { + int err = mprotect(data, commit_amount, PROT_READ | PROT_WRITE); + if (err != 0) { + GB_PANIC("Out of Virtual Memory, oh no...\n"); + GB_ASSERT_MSG(err == 0, "Out of Virtual Memory, oh no..."); + return false; + } + return true; + } #endif -gb_internal MemoryBlock *virtual_memory_alloc(isize size) { +gb_internal MemoryBlock *virtual_memory_alloc(isize size, bool commit) { isize const page_size = DEFAULT_PAGE_SIZE; isize total_size = size + gb_size_of(PlatformMemoryBlock); @@ -224,7 +325,7 @@ gb_internal MemoryBlock *virtual_memory_alloc(isize size) { do_protection = true; } - PlatformMemoryBlock *pmblock = platform_virtual_memory_alloc(total_size); + PlatformMemoryBlock *pmblock = platform_virtual_memory_alloc(total_size, commit); GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no..."); pmblock->block.base = cast(u8 *)pmblock + base_offset; diff --git a/src/docs_format.cpp b/src/docs_format.cpp index 2235789d5..30dcf40b9 100644 --- a/src/docs_format.cpp +++ b/src/docs_format.cpp @@ -15,7 +15,7 @@ struct OdinDocVersionType { #define OdinDocVersionType_Major 0 #define OdinDocVersionType_Minor 3 -#define OdinDocVersionType_Patch 1 +#define OdinDocVersionType_Patch 2 struct OdinDocHeaderBase { u8 magic[8]; @@ -59,31 +59,31 @@ struct OdinDocPosition { }; enum OdinDocTypeKind : u32 { - OdinDocType_Invalid = 0, - OdinDocType_Basic = 1, - OdinDocType_Named = 2, - OdinDocType_Generic = 3, - OdinDocType_Pointer = 4, - OdinDocType_Array = 5, - OdinDocType_EnumeratedArray = 6, - OdinDocType_Slice = 7, - OdinDocType_DynamicArray = 8, - OdinDocType_Map = 9, - OdinDocType_Struct = 10, - OdinDocType_Union = 11, - OdinDocType_Enum = 12, - OdinDocType_Tuple = 13, - OdinDocType_Proc = 14, - OdinDocType_BitSet = 15, - OdinDocType_SimdVector = 16, - OdinDocType_SOAStructFixed = 17, - OdinDocType_SOAStructSlice = 18, - OdinDocType_SOAStructDynamic = 19, - - OdinDocType_MultiPointer = 22, - OdinDocType_Matrix = 23, - OdinDocType_SoaPointer = 24, - OdinDocType_BitField = 25, + OdinDocType_Invalid = 0, + OdinDocType_Basic = 1, + OdinDocType_Named = 2, + OdinDocType_Generic = 3, + OdinDocType_Pointer = 4, + OdinDocType_Array = 5, + OdinDocType_EnumeratedArray = 6, + OdinDocType_Slice = 7, + OdinDocType_DynamicArray = 8, + OdinDocType_Map = 9, + OdinDocType_Struct = 10, + OdinDocType_Union = 11, + OdinDocType_Enum = 12, + OdinDocType_Tuple = 13, + OdinDocType_Proc = 14, + OdinDocType_BitSet = 15, + OdinDocType_SimdVector = 16, + OdinDocType_SOAStructFixed = 17, + OdinDocType_SOAStructSlice = 18, + OdinDocType_SOAStructDynamic = 19, + OdinDocType_MultiPointer = 22, + OdinDocType_Matrix = 23, + OdinDocType_SoaPointer = 24, + OdinDocType_BitField = 25, + OdinDocType_FixedCapacityDynamicArray = 26, }; enum OdinDocTypeFlag_Basic : u32 { @@ -144,13 +144,14 @@ struct OdinDocType { OdinDocString custom_align; // Used by: - // .Array - 1 count: 0=len - // .Enumerated_Array - 1 count: 0=len - // .SOA_Struct_Fixed - 1 count: 0=len - // .Bit_Set - 2 count: 0=lower, 1=upper - // .Simd_Vector - 1 count: 0=len - // .Matrix - 2 count: 0=row_count, 1=column_count - // .Struct - <=2 count: 0=min_field_align, 1=max_field_align + // .Array - 1 count: 0=len + // .Enumerated_Array - 1 count: 0=len + // .SOA_Struct_Fixed - 1 count: 0=len + // .Bit_Set - 2 count: 0=lower, 1=upper + // .Simd_Vector - 1 count: 0=len + // .Matrix - 2 count: 0=row_count, 1=column_count + // .Struct - <=2 count: 0=min_field_align, 1=max_field_align + // .Fixed_Capacity_Dynamic_Array - 1 count: 0=cap u32 elem_count_len; i64 elem_counts[OdinDocType_ElemsCap]; @@ -159,27 +160,28 @@ struct OdinDocType { OdinDocString calling_convention; // Used by: - // .Named - 1 type: 0=base type - // .Generic - <1 type: 0=specialization - // .Pointer - 1 type: 0=element - // .Array - 1 type: 0=element - // .Enumerated_Array - 2 types: 0=index and 1=element - // .Slice - 1 type: 0=element - // .Dynamic_Array - 1 type: 0=element - // .Map - 2 types: 0=key, 1=value - // .SOA_Struct_Fixed - 1 type: underlying SOA struct element - // .SOA_Struct_Slice - 1 type: underlying SOA struct element - // .SOA_Struct_Dynamic - 1 type: underlying SOA struct element - // .Union - 0+ types: variants - // .Enum - <1 type: 0=base type - // .Proc - 2 types: 0=parameters, 1=results - // .Bit_Set - <=2 types: 0=element type, 1=underlying type (Underlying_Type flag will be set) - // .Simd_Vector - 1 type: 0=element - // .Relative_Pointer - 2 types: 0=pointer type, 1=base integer - // .Multi_Pointer - 1 type: 0=element - // .Matrix - 1 type: 0=element - // .Soa_Pointer - 1 type: 0=element - // .Bit_Field - 1 type: 0=backing type + // .Named - 1 type: 0=base type + // .Generic - <1 type: 0=specialization + // .Pointer - 1 type: 0=element + // .Array - 1 type: 0=element (and 1=generic index (if exists)) + // .Enumerated_Array - 2 types: 0=index and 1=element + // .Slice - 1 type: 0=element + // .Dynamic_Array - 1 type: 0=element + // .Map - 2 types: 0=key, 1=value + // .SOA_Struct_Fixed - 1 type: underlying SOA struct element + // .SOA_Struct_Slice - 1 type: underlying SOA struct element + // .SOA_Struct_Dynamic - 1 type: underlying SOA struct element + // .Union - 0+ types: variants + // .Enum - <1 type: 0=base type + // .Proc - 2 types: 0=parameters, 1=results + // .Bit_Set - <=2 types: 0=element type, 1=underlying type (Underlying_Type flag will be set) + // .Simd_Vector - 1 type: 0=element + // .Relative_Pointer - 2 types: 0=pointer type, 1=base integer + // .Multi_Pointer - 1 type: 0=element + // .Matrix - 1 type: 0=element + // .Soa_Pointer - 1 type: 0=element + // .Bit_Field - 1 type: 0=backing type + // .Fixed_Capacity_Dynamic_Array - 1 type: 0=element (and 1=generic index (if exists)) OdinDocArray types; // Used by: diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp index 3edd7da9d..3f5d4876a 100644 --- a/src/docs_writer.cpp +++ b/src/docs_writer.cpp @@ -472,6 +472,14 @@ gb_internal OdinDocArray odin_doc_type_as_slice(OdinDocWriter return odin_write_item_as_slice(w, index); } +gb_internal OdinDocArray odin_doc_type2_as_slice(OdinDocWriter *w, Type *type0, Type *type1, bool cache=true) { + OdinDocTypeIndex indices[2] = {}; + indices[0] = odin_doc_type(w, type0, cache); + indices[1] = odin_doc_type(w, type1, cache); + + return odin_write_slice(w, indices, gb_count_of(indices)); +} + gb_internal OdinDocArray odin_doc_add_entity_as_slice(OdinDocWriter *w, Entity *e) { OdinDocEntityIndex index = odin_doc_add_entity(w, e); return odin_write_item_as_slice(w, index); @@ -555,7 +563,13 @@ gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type, bool ca doc_type.kind = OdinDocType_Array; doc_type.elem_count_len = 1; doc_type.elem_counts[0] = type->Array.count; - doc_type.types = odin_doc_type_as_slice(w, type->Array.elem); + if (type->Array.generic_count != nullptr) { + doc_type.types = odin_doc_type2_as_slice(w, + type->Array.elem, + type->Array.generic_count); + } else { + doc_type.types = odin_doc_type_as_slice(w, type->Array.elem); + } break; case Type_EnumeratedArray: doc_type.kind = OdinDocType_EnumeratedArray; @@ -576,6 +590,20 @@ gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type, bool ca doc_type.kind = OdinDocType_DynamicArray; doc_type.types = odin_doc_type_as_slice(w, type->DynamicArray.elem); break; + case Type_FixedCapacityDynamicArray: + doc_type.kind = OdinDocType_FixedCapacityDynamicArray; + doc_type.elem_count_len = 1; + doc_type.elem_counts[0] = type->FixedCapacityDynamicArray.capacity; + + if (type->FixedCapacityDynamicArray.generic_capacity != nullptr) { + doc_type.types = odin_doc_type2_as_slice(w, + type->FixedCapacityDynamicArray.elem, + type->FixedCapacityDynamicArray.generic_capacity); + + } else { + doc_type.types = odin_doc_type_as_slice(w, type->FixedCapacityDynamicArray.elem); + } + break; case Type_Map: doc_type.kind = OdinDocType_Map; { @@ -1022,7 +1050,8 @@ gb_internal OdinDocArray odin_doc_add_pkg_entries(OdinDocWrit defer (array_free(&entries)); for (auto const &element : pkg->scope->elements) { - String name = element.key; + u32 hash = element.hash; + auto interned = pkg->scope->elements.keys[hash & (pkg->scope->elements.cap-1)]; Entity *e = element.value; switch (e->kind) { case Entity_Invalid: @@ -1051,7 +1080,7 @@ gb_internal OdinDocArray odin_doc_add_pkg_entries(OdinDocWrit } OdinDocScopeEntry entry = {}; - entry.name = odin_doc_write_string(w, name); + entry.name = odin_doc_write_string(w, interned.string()); entry.entity = odin_doc_add_entity(w, e); array_add(&entries, entry); } diff --git a/src/entity.cpp b/src/entity.cpp index 070b05462..5062599de 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -141,7 +141,7 @@ enum ProcedureOptimizationMode : u8 { BlockingMutex global_type_name_objc_metadata_mutex; struct TypeNameObjCMetadataEntry { - String name; + InternedString interned; Entity *entity; }; struct TypeNameObjCMetadata { @@ -151,8 +151,8 @@ struct TypeNameObjCMetadata { }; gb_internal TypeNameObjCMetadata *create_type_name_obj_c_metadata() { - TypeNameObjCMetadata *md = gb_alloc_item(permanent_allocator(), TypeNameObjCMetadata); - md->mutex = gb_alloc_item(permanent_allocator(), BlockingMutex); + TypeNameObjCMetadata *md = permanent_alloc_item(); + md->mutex = permanent_alloc_item(); array_init(&md->type_entries, heap_allocator()); array_init(&md->value_entries, heap_allocator()); return md; @@ -169,6 +169,10 @@ struct Entity { Scope * scope; Type * type; std::atomic identifier; // Can be nullptr + + std::atomic interned_name; + std::atomic interned_name_hash; + DeclInfo * decl_info; std::atomic parent_proc_decl; // nullptr if in file/global scope AstFile * file; @@ -300,6 +304,16 @@ struct Entity { }; }; +gb_internal InternedString entity_interned_name(Entity *entity) { + auto name = entity->interned_name.load(); + if (name.value == 0) { + name = string_interner_insert(entity->token.string); + entity->interned_name.store(name); + entity->interned_name_hash.store(name.hash()); + } + return name; +} + gb_internal bool is_entity_kind_exported(EntityKind kind, bool allow_builtin = false) { switch (kind) { case Entity_Builtin: @@ -344,18 +358,24 @@ gb_internal bool entity_has_deferred_procedure(Entity *e) { gb_global std::atomic global_entity_id; +// NOTE(bill): This exists to allow for bulk allocations of entities all at once to improve performance for type generation +#define INTERNAL_ENTITY_INIT(e_, kind_, scope_, token_, type_) do { \ + (e_)->kind = (kind_); \ + (e_)->state = EntityState_Unresolved; \ + (e_)->scope = (scope_); \ + (e_)->token = (token_); \ + (e_)->type = (type_); \ + (e_)->id = 1 + global_entity_id.fetch_add(1); \ + if ((token_).pos.file_id) { \ + e_->file = thread_unsafe_get_ast_file_from_id((token_).pos.file_id); \ + } \ +} while (0) + + gb_internal Entity *alloc_entity(EntityKind kind, Scope *scope, Token token, Type *type) { - gbAllocator a = permanent_allocator(); - Entity *entity = gb_alloc_item(a, Entity); - entity->kind = kind; - entity->state = EntityState_Unresolved; - entity->scope = scope; - entity->token = token; - entity->type = type; - entity->id = 1 + global_entity_id.fetch_add(1); - if (token.pos.file_id) { - entity->file = thread_unsafe_get_ast_file_from_id(token.pos.file_id); - } + Entity *entity = permanent_alloc_item(); + INTERNAL_ENTITY_INIT(entity, kind, scope, token, type); + entity_interned_name(entity); return entity; } @@ -392,10 +412,9 @@ gb_internal Entity *alloc_entity_type_name(Scope *scope, Token token, Type *type } gb_internal Entity *alloc_entity_param(Scope *scope, Token token, Type *type, bool is_using, bool is_value) { - Entity *entity = alloc_entity_variable(scope, token, type); + Entity *entity = alloc_entity_variable(scope, token, type, EntityState_Resolved); entity->flags |= EntityFlag_Used; entity->flags |= EntityFlag_Param; - entity->state = EntityState_Resolved; if (is_using) entity->flags |= EntityFlag_Using; if (is_value) entity->flags |= EntityFlag_Value; return entity; @@ -412,11 +431,10 @@ gb_internal Entity *alloc_entity_const_param(Scope *scope, Token token, Type *ty gb_internal Entity *alloc_entity_field(Scope *scope, Token token, Type *type, bool is_using, i32 field_index, EntityState state = EntityState_Unresolved) { - Entity *entity = alloc_entity_variable(scope, token, type); + Entity *entity = alloc_entity_variable(scope, token, type, state); entity->Variable.field_index = field_index; if (is_using) entity->flags |= EntityFlag_Using; entity->flags |= EntityFlag_Field; - entity->state = state; return entity; } diff --git a/src/exact_value.cpp b/src/exact_value.cpp index fa99ed3fe..525b8cb91 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -146,7 +146,7 @@ gb_internal ExactValue exact_value_float(f64 f) { gb_internal ExactValue exact_value_complex(f64 real, f64 imag) { ExactValue result = {ExactValue_Complex}; - result.value_complex = gb_alloc_item(permanent_allocator(), Complex128); + result.value_complex = permanent_alloc_item(); result.value_complex->real = real; result.value_complex->imag = imag; return result; @@ -154,7 +154,7 @@ gb_internal ExactValue exact_value_complex(f64 real, f64 imag) { gb_internal ExactValue exact_value_quaternion(f64 real, f64 imag, f64 jmag, f64 kmag) { ExactValue result = {ExactValue_Quaternion}; - result.value_quaternion = gb_alloc_item(permanent_allocator(), Quaternion256); + result.value_quaternion = permanent_alloc_item(); result.value_quaternion->real = real; result.value_quaternion->imag = imag; result.value_quaternion->jmag = jmag; @@ -438,7 +438,7 @@ gb_internal ExactValue exact_value_to_complex(ExactValue v) { // return exact_value_complex(v.value_quaternion.real, v.value_quaternion.imag); } ExactValue r = {ExactValue_Invalid}; - v.value_complex = gb_alloc_item(permanent_allocator(), Complex128); + v.value_complex = permanent_alloc_item(); return r; } gb_internal ExactValue exact_value_to_quaternion(ExactValue v) { @@ -453,7 +453,7 @@ gb_internal ExactValue exact_value_to_quaternion(ExactValue v) { return v; } ExactValue r = {ExactValue_Invalid}; - v.value_quaternion = gb_alloc_item(permanent_allocator(), Quaternion256); + v.value_quaternion = permanent_alloc_item(); return r; } diff --git a/src/gb/gb.h b/src/gb/gb.h index c52f63cec..3c88ce6ed 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -2522,6 +2522,7 @@ void gb_assert_handler(char const *prefix, char const *condition, char const *fi va_end(va); } gb_printf_err("\n"); + gb_printf_err("This is a compiler error. Please report this.\n"); } b32 gb_is_power_of_two(isize x) { diff --git a/src/linker.cpp b/src/linker.cpp index 12f016cea..76c795989 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -980,6 +980,10 @@ try_cross_linking:; link_command_line = gb_string_appendc(link_command_line, " -flto=thin"); link_command_line = gb_string_append_fmt(link_command_line, " -flto-jobs=%d ", build_context.thread_count); + if (build_context.ODIN_DEBUG) { + link_command_line = gb_string_appendc(link_command_line, " -g "); + } + if (is_osx && !build_context.minimum_os_version_string_given) { link_command_line = gb_string_appendc(link_command_line, " -Wno-override-module "); } diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index ec7c63b1a..6aabe456f 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -389,7 +389,7 @@ namespace lbAbi386 { gb_internal LB_ABI_INFO(abi_info) { LLVMContextRef c = m->ctx; - lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType); + lbFunctionType *ft = permanent_alloc_item(); ft->ctx = c; ft->args = compute_arg_types(c, arg_types, arg_count); ft->ret = compute_return_type(ft, c, return_type, return_is_defined, return_is_tuple); @@ -471,7 +471,7 @@ namespace lbAbiAmd64Win64 { gb_internal LB_ABI_INFO(abi_info) { LLVMContextRef c = m->ctx; - lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType); + lbFunctionType *ft = permanent_alloc_item(); ft->ctx = c; ft->args = compute_arg_types(c, arg_types, arg_count); ft->ret = compute_return_type(ft, c, return_type, return_is_defined, return_is_tuple); @@ -617,7 +617,7 @@ namespace lbAbiAmd64SysV { gb_internal LB_ABI_INFO(abi_info) { LLVMContextRef c = m->ctx; - lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType); + lbFunctionType *ft = permanent_alloc_item(); ft->ctx = c; ft->calling_convention = calling_convention; @@ -1128,7 +1128,7 @@ namespace lbAbiArm64 { gb_internal LB_ABI_INFO(abi_info) { LLVMContextRef c = m->ctx; - lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType); + lbFunctionType *ft = permanent_alloc_item(); ft->ctx = c; ft->args = compute_arg_types(c, arg_types, arg_count); ft->ret = compute_return_type(ft, c, return_type, return_is_defined, return_is_tuple); @@ -1349,7 +1349,7 @@ namespace lbAbiWasm { gb_internal LB_ABI_INFO(abi_info) { LLVMContextRef c = m->ctx; - lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType); + lbFunctionType *ft = permanent_alloc_item(); ft->ctx = c; ft->calling_convention = calling_convention; ft->args = compute_arg_types(c, arg_types, arg_count, calling_convention, original_type); @@ -1558,7 +1558,7 @@ namespace lbAbiArm32 { gb_internal LB_ABI_INFO(abi_info) { LLVMContextRef c = m->ctx; - lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType); + lbFunctionType *ft = permanent_alloc_item(); ft->ctx = c; ft->args = compute_arg_types(c, arg_types, arg_count, calling_convention); ft->ret = compute_return_type(c, return_type, return_is_defined); @@ -1867,7 +1867,7 @@ namespace lbAbiRiscv64 { } gb_internal LB_ABI_INFO(abi_info) { - lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType); + lbFunctionType *ft = permanent_alloc_item(); ft->ctx = m->ctx; ft->calling_convention = calling_convention; @@ -1889,7 +1889,7 @@ gb_internal LB_ABI_INFO(lb_get_abi_info_internal) { case ProcCC_None: case ProcCC_InlineAsm: { - lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType); + lbFunctionType *ft = permanent_alloc_item(); ft->ctx = c; ft->args = array_make(lb_function_type_args_allocator(), arg_count); for (unsigned i = 0; i < arg_count; i++) { diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 912dcbd7c..70b87f43a 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -66,6 +66,19 @@ gb_internal String get_final_microarchitecture() { gb_internal String get_default_features() { BuildContext *bc = &build_context; + if (bc->microarch == str_lit("native")) { + String features = make_string_c(LLVMGetHostCPUFeatures()); + + // Update the features string so LLVM uses it later. + if (bc->target_features_string.len > 0) { + bc->target_features_string = concatenate3_strings(permanent_allocator(), features, str_lit(","), bc->target_features_string); + } else { + bc->target_features_string = features; + } + + return features; + } + int off = 0; for (int i = 0; i < bc->metrics.arch; i += 1) { off += target_microarch_counts[i]; @@ -2634,7 +2647,7 @@ gb_internal void lb_llvm_module_passes_and_verification(lbGenerator *gen, bool d if (do_threading) { for (auto const &entry : gen->modules) { lbModule *m = entry.value; - auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData); + auto wd = permanent_alloc_item(); wd->m = m; wd->target_machine = m->target_machine; wd->do_threading = true; @@ -2645,7 +2658,7 @@ gb_internal void lb_llvm_module_passes_and_verification(lbGenerator *gen, bool d } else { for (auto const &entry : gen->modules) { lbModule *m = entry.value; - auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData); + auto wd = permanent_alloc_item(); wd->m = m; wd->target_machine = m->target_machine; wd->do_threading = false; @@ -2765,7 +2778,7 @@ gb_internal bool lb_llvm_object_generation(lbGenerator *gen, bool do_threading) array_add(&gen->output_object_paths, filepath_obj); array_add(&gen->output_temp_paths, filepath_ll); - auto *wd = gb_alloc_item(permanent_allocator(), lbLLVMEmitWorker); + auto *wd = permanent_alloc_item(); wd->target_machine = m->target_machine; wd->code_gen_file_type = code_gen_file_type; wd->filepath_obj = filepath_obj; @@ -2933,7 +2946,7 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star AstPackage *pkg = get_runtime_package(m->info); String name = str_lit("exit"); - Entity *e = scope_lookup_current(pkg->scope, name); + Entity *e = scope_lookup_current(pkg->scope, string_interner_insert(name)); if (e == nullptr) { compiler_error("Could not find type declaration for '%.*s.%.*s'\n", LIT(pkg->name), LIT(name)); } @@ -3174,7 +3187,9 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { code_mode); lbModule *m = entry.value; m->target_machine = target_machine; - LLVMSetModuleDataLayout(m->mod, LLVMCreateTargetDataLayout(target_machine)); + LLVMTargetDataRef data_layout = LLVMCreateTargetDataLayout(target_machine); + LLVMSetModuleDataLayout(m->mod, data_layout); + LLVMDisposeTargetData(data_layout); #if LLVM_VERSION_MAJOR >= 18 if (build_context.fast_isel) { @@ -3583,6 +3598,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { lb_add_raddbg_string(m, "type_view: {type: \"[]?\", expr: \"array(data, len)\"}"); lb_add_raddbg_string(m, "type_view: {type: \"string\", expr: \"array(data, len)\"}"); lb_add_raddbg_string(m, "type_view: {type: \"[dynamic]?\", expr: \"rows($, array(data, len), len, cap, allocator)\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"[dynamic;?]?\", expr: \"rows($, array(data, len), len)\"}"); // column major matrices lb_add_raddbg_string(m, "type_view: {type: \"matrix[1, ?]?\", expr: \"columns($.data, $[0])\"}"); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 0f199907d..e4297300f 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -359,6 +359,7 @@ struct lbProcedure { std::atomic is_done; lbAddr return_ptr; + Entity * sret_rvo_entity; // Local aliases of `return_ptr` Array defer_stmts; Array blocks; Array branch_blocks; @@ -486,7 +487,7 @@ gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlo gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block, Ast *node); gb_internal lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t); gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right); -gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, ProcInlining inlining = ProcInlining_none, ProcTailing tailing = ProcTailing_none); +gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, ProcInlining inlining = ProcInlining_none, ProcTailing tailing = ProcTailing_none, lbValue *sret_dst = nullptr); gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t); gb_internal lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x); @@ -494,11 +495,10 @@ gb_internal void lb_emit_jump(lbProcedure *p, lbBlock *target_block); gb_internal void lb_emit_if(lbProcedure *p, lbValue cond, lbBlock *true_block, lbBlock *false_block); gb_internal void lb_start_block(lbProcedure *p, lbBlock *b); -gb_internal lbValue lb_build_call_expr(lbProcedure *p, Ast *expr); +gb_internal lbValue lb_build_call_expr(lbProcedure *p, Ast *expr, lbValue *sret_dst = nullptr); gb_internal lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type); gb_internal void lb_begin_procedure_body(lbProcedure *p); gb_internal void lb_end_procedure_body(lbProcedure *p); -gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, ProcInlining inlining); gb_internal lbAddr lb_find_or_generate_context_ptr(lbProcedure *p); gb_internal lbContextData *lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx); @@ -533,6 +533,7 @@ gb_internal lbValue lb_dynamic_array_elem(lbProcedure *p, lbValue da); gb_internal lbValue lb_dynamic_array_len(lbProcedure *p, lbValue da); gb_internal lbValue lb_dynamic_array_cap(lbProcedure *p, lbValue da); gb_internal lbValue lb_dynamic_array_allocator(lbProcedure *p, lbValue da); +gb_internal lbValue lb_fixed_capacity_dynamic_array_len(lbProcedure *p, lbValue da); gb_internal lbValue lb_map_len(lbProcedure *p, lbValue value); gb_internal lbValue lb_map_cap(lbProcedure *p, lbValue value); gb_internal lbValue lb_soa_struct_len(lbProcedure *p, lbValue value); @@ -647,6 +648,11 @@ gb_internal LLVMTypeRef llvm_array_type(LLVMTypeRef ElementType, uint64_t Elemen #endif } +gb_internal lbValue lb_emit_struct_iv(lbProcedure *p, lbValue agg, lbValue field, i32 index); +gb_internal lbValue lb_build_struct_value(lbProcedure *p, Type *type, lbValue *fields, isize count); +gb_internal lbValue lb_make_slice_value(lbProcedure *p, Type *slice_type, lbValue elem, lbValue len); +gb_internal lbValue lb_make_string_value(lbProcedure *p, Type *string_type, lbValue elem, lbValue len); + gb_internal String lb_internal_gen_name_from_type(char const *prefix, Type *type); diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 9f2052960..762243ee3 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -694,6 +694,28 @@ gb_internal void lb_const_array_spread(lbModule *m, lbConstContext cc, Type *arr res->value = llvm_const_array(m, lb_type(m, elem), elems, cast(unsigned)count); } +gb_internal LLVMValueRef lb_fill_fixed_capacity_dynamic_array(lbModule *m, i64 elem_count, Type *original_type, LLVMValueRef *values, lbConstContext cc) { + Type *bt = base_type(original_type); + GB_ASSERT(bt->kind == Type_FixedCapacityDynamicArray); + Type *elem_type = bt->FixedCapacityDynamicArray.elem; + i64 capacity = bt->FixedCapacityDynamicArray.capacity; + + Type *array_backing_type = alloc_type_array(elem_type, capacity); + LLVMValueRef array_backing = lb_build_constant_array_values(m, array_backing_type, elem_type, cast(isize)capacity, values, cc); + LLVMValueRef array_len = lb_const_int(m, t_int, elem_count).value; + + isize svalue_count = 0; + LLVMValueRef svalues[3] = {}; + svalues[svalue_count++] = array_backing; + i64 padding = bt->FixedCapacityDynamicArray.padding_needed; + if (padding > 0) { + svalues[svalue_count++] = LLVMConstNull(lb_type_padding_filler(m, padding, 1)); + } + svalues[svalue_count++] = array_len; + + return llvm_const_named_struct(m, original_type, svalues, svalue_count); +} + gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lbConstContext cc, Type *value_type) { if (cc.allow_local) { cc.is_rodata = false; @@ -814,7 +836,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb Type *tag_type = union_tag_type(bt); LLVMTypeRef llvm_tag_type = lb_type(m, tag_type); - i64 tag_index = union_variant_index(bt, variant_type); + i64 tag_index = union_variant_index_checked(bt, variant_type); GB_ASSERT(tag_index >= 0); values[value_count++] = LLVMConstInt(llvm_tag_type, tag_index, false); i64 used_size = block_size + type_size_of(tag_type); @@ -1554,6 +1576,108 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->EnumeratedArray.count, values, cc); return res; } + } else if (is_type_fixed_capacity_dynamic_array(type)) { + ast_node(cl, CompoundLit, value.value_compound); + Type *elem_type = type->FixedCapacityDynamicArray.elem; + i64 capacity = type->FixedCapacityDynamicArray.capacity; + isize elem_count = cl->elems.count; + if (elem_count == 0 || !elem_type_can_be_constant(elem_type)) { + return lb_const_nil(m, original_type); + } + if (cl->elems[0]->kind == Ast_FieldValue) { + // TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)capacity); + + i64 max_index = -1; + isize value_index = 0; + for (i64 i = 0; i < capacity; i++) { + bool found = false; + + for (isize j = 0; j < elem_count; j++) { + Ast *elem = cl->elems[j]; + ast_node(fv, FieldValue, elem); + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); + + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op != Token_RangeHalf) { + hi += 1; + } + max_index = gb_max(max_index, hi-1); + + if (lo == i) { + TypeAndValue tav = fv->value->tav; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value; + for (i64 k = lo; k < hi; k++) { + values[value_index++] = val; + } + + found = true; + i += (hi-lo-1); + break; + } + } else { + TypeAndValue index_tav = fv->field->tav; + GB_ASSERT(index_tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(index_tav.value); + + max_index = gb_max(max_index, index); + + if (index == i) { + TypeAndValue tav = fv->value->tav; + LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value; + values[value_index++] = val; + found = true; + break; + } + } + } + + if (!found) { + values[value_index++] = LLVMConstNull(lb_type(m, elem_type)); + } + } + + i64 count = max_index+1; + GB_ASSERT(0 < count); + GB_ASSERT(count <= capacity); + + res.value = lb_fill_fixed_capacity_dynamic_array(m, count, original_type, values, cc); + return res; + } else if (are_types_identical(value.value_compound->tav.type, elem_type)) { + // Compound is of array item type; expand its value to all items in array. + LLVMValueRef* values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)capacity); + + for (isize i = 0; i < capacity; i++) { + values[i] = lb_const_value(m, elem_type, value, cc, elem_type).value; + } + + res.value = lb_fill_fixed_capacity_dynamic_array(m, capacity, original_type, values, cc); + return res; + } else { + // Assume that compound value is an array literal + GB_ASSERT_MSG(elem_count <= capacity, "%td <= %td", elem_count, capacity); + + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)capacity); + + for (isize i = 0; i < elem_count; i++) { + TypeAndValue tav = cl->elems[i]->tav; + GB_ASSERT(tav.mode != Addressing_Invalid); + values[i] = lb_const_value(m, elem_type, tav.value, cc, tav.type).value; + } + for (isize i = elem_count; i < capacity; i++) { + values[i] = LLVMConstNull(lb_type(m, elem_type)); + } + + res.value = lb_fill_fixed_capacity_dynamic_array(m, elem_count, original_type, values, cc); + return res; + } } else if (is_type_simd_vector(type)) { ast_node(cl, CompoundLit, value.value_compound); @@ -1695,11 +1819,12 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb for (isize i = 0; i < elem_count; i++) { ast_node(fv, FieldValue, cl->elems[i]); String name = fv->field->Ident.token.string; + InternedString interned = fv->field->Ident.interned; TypeAndValue tav = fv->value->tav; GB_ASSERT(tav.mode != Addressing_Invalid); - Selection sel = lookup_field(type, name, false); + Selection sel = lookup_field(type, interned, false); GB_ASSERT(!sel.indirect); Entity *f = type->Struct.fields[sel.index[0]]; diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index e9b0f72cb..d3e63cbf7 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -409,6 +409,69 @@ gb_internal LLVMMetadataRef lb_debug_dynamic_array(lbModule *m, Type *type, Stri return final_decl; } +gb_internal LLVMMetadataRef lb_debug_fixed_capacity_dynamic_array(lbModule *m, Type *type, String name, LLVMMetadataRef scope, LLVMMetadataRef file, unsigned line) { + Type *bt = base_type(type); + GB_ASSERT(bt->kind == Type_FixedCapacityDynamicArray); + + unsigned const int_bits = cast(unsigned)(8*build_context.int_size); + + u64 size_in_bits = 8*type_size_of(bt); + u32 align_in_bits = 8*cast(u32)type_align_of(bt); + + LLVMMetadataRef temp_forward_decl = LLVMDIBuilderCreateReplaceableCompositeType( + m->debug_builder, DW_TAG_structure_type, + cast(char const *)name.text, cast(size_t)name.len, + scope, file, line, 0, size_in_bits, align_in_bits, LLVMDIFlagZero, "", 0 + ); + + lb_set_llvm_metadata(m, type, temp_forward_decl); + + unsigned element_count = 2; + LLVMMetadataRef elements[2]; + + // LLVMMetadataRef member_scope = lb_get_llvm_metadata(m, bt->DynamicArray.scope); + LLVMMetadataRef member_scope = nullptr; + + Type *elem_type = alloc_type_array(bt->FixedCapacityDynamicArray.elem, bt->FixedCapacityDynamicArray.capacity); + elements[0] = LLVMDIBuilderCreateMemberType( + m->debug_builder, member_scope, + "data", 4, + file, line, + 8*cast(u64)type_size_of(elem_type), 8*cast(u32)type_align_of(elem_type), + 0, + LLVMDIFlagZero, lb_debug_type(m, elem_type) + ); + + i64 len_offset_in_bits = 8*type_offset_of(bt, 1); + + elements[1] = LLVMDIBuilderCreateMemberType( + m->debug_builder, member_scope, + "len", 3, + file, line, + int_bits, int_bits, + len_offset_in_bits, + LLVMDIFlagZero, lb_debug_type(m, t_int) + ); + + LLVMMetadataRef final_decl = LLVMDIBuilderCreateStructType( + m->debug_builder, scope, + cast(char const *)name.text, cast(size_t)name.len, + file, line, + size_in_bits, align_in_bits, + LLVMDIFlagZero, + nullptr, + elements, element_count, + 0, + nullptr, + "", 0 + ); + + LLVMMetadataReplaceAllUsesWith(temp_forward_decl, final_decl); + lb_set_llvm_metadata(m, type, final_decl); + return final_decl; +} + + gb_internal LLVMMetadataRef lb_debug_union(lbModule *m, Type *type, String name, LLVMMetadataRef scope, LLVMMetadataRef file, unsigned line) { Type *bt = base_type(type); GB_ASSERT(bt->kind == Type_Union); @@ -899,6 +962,7 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { case Type_BitSet: return lb_debug_bitset( m, type, type_to_canonical_string(temporary_allocator(), type), nullptr, nullptr, 0); case Type_Enum: return lb_debug_enum( m, type, type_to_canonical_string(temporary_allocator(), type), nullptr, nullptr, 0); case Type_BitField: return lb_debug_bitfield( m, type, type_to_canonical_string(temporary_allocator(), type), nullptr, nullptr, 0); + case Type_FixedCapacityDynamicArray: return lb_debug_fixed_capacity_dynamic_array(m, type, type_to_canonical_string(temporary_allocator(), type), nullptr, nullptr, 0); case Type_Tuple: if (type->Tuple.variables.count == 1) { diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index fc68561b3..39cdcd54f 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1,4 +1,5 @@ gb_internal lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, bool component_wise); +gb_internal lbValue lb_build_slice_expr_value(lbProcedure *p, Ast *expr); gb_internal LLVMValueRef lb_const_low_bits_mask(LLVMTypeRef type, u64 bit_count) { GB_ASSERT(bit_count <= 64); @@ -251,25 +252,18 @@ gb_internal lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x, LLVMValueRef v0 = LLVMBuildFNeg(p->builder, LLVMBuildExtractValue(p->builder, x.value, 0, ""), ""); LLVMValueRef v1 = LLVMBuildFNeg(p->builder, LLVMBuildExtractValue(p->builder, x.value, 1, ""), ""); - lbAddr addr = lb_add_local_generated(p, x.type, false); - LLVMTypeRef type = llvm_addr_type(p->module, addr.addr); - LLVMBuildStore(p->builder, v0, LLVMBuildStructGEP2(p->builder, type, addr.addr.value, 0, "")); - LLVMBuildStore(p->builder, v1, LLVMBuildStructGEP2(p->builder, type, addr.addr.value, 1, "")); - return lb_addr_load(p, addr); - + Type *et = base_complex_elem_type(x.type); + lbValue fields[2] = {{v0, et}, {v1, et}}; + return lb_build_struct_value(p, x.type, fields, gb_count_of(fields)); } else if (is_type_quaternion(x.type)) { LLVMValueRef v0 = LLVMBuildFNeg(p->builder, LLVMBuildExtractValue(p->builder, x.value, 0, ""), ""); LLVMValueRef v1 = LLVMBuildFNeg(p->builder, LLVMBuildExtractValue(p->builder, x.value, 1, ""), ""); LLVMValueRef v2 = LLVMBuildFNeg(p->builder, LLVMBuildExtractValue(p->builder, x.value, 2, ""), ""); LLVMValueRef v3 = LLVMBuildFNeg(p->builder, LLVMBuildExtractValue(p->builder, x.value, 3, ""), ""); - lbAddr addr = lb_add_local_generated(p, x.type, false); - LLVMTypeRef type = llvm_addr_type(p->module, addr.addr); - LLVMBuildStore(p->builder, v0, LLVMBuildStructGEP2(p->builder, type, addr.addr.value, 0, "")); - LLVMBuildStore(p->builder, v1, LLVMBuildStructGEP2(p->builder, type, addr.addr.value, 1, "")); - LLVMBuildStore(p->builder, v2, LLVMBuildStructGEP2(p->builder, type, addr.addr.value, 2, "")); - LLVMBuildStore(p->builder, v3, LLVMBuildStructGEP2(p->builder, type, addr.addr.value, 3, "")); - return lb_addr_load(p, addr); + Type *et = base_complex_elem_type(x.type); + lbValue fields[4] = {{v0, et}, {v1, et}, {v2, et}, {v3, et}}; + return lb_build_struct_value(p, x.type, fields, gb_count_of(fields)); } else if (is_type_simd_vector(x.type)) { Type *elem = base_array_type(x.type); if (is_type_float(elem)) { @@ -973,7 +967,7 @@ gb_internal lbValue lb_emit_matrix_mul_vector(lbProcedure *p, lbValue lhs, lbVal } for (unsigned row_index = 0; row_index < column_count; row_index++) { - LLVMValueRef value = lb_emit_struct_ev(p, rhs, row_index).value; + LLVMValueRef value = LLVMBuildExtractValue(p->builder, rhs.value, row_index, ""); LLVMValueRef row = llvm_vector_broadcast(p, value, row_count); v_rows[row_index] = row; } @@ -994,13 +988,19 @@ gb_internal lbValue lb_emit_matrix_mul_vector(lbProcedure *p, lbValue lhs, lbVal lbAddr res = lb_add_local_generated(p, type, true); + Type *vector_elem_type = base_array_type(rhs.type); + for (i64 i = 0; i < mt->Matrix.row_count; i++) { for (i64 j = 0; j < mt->Matrix.column_count; j++) { lbValue dst = lb_emit_matrix_epi(p, res.addr, i, 0); lbValue d0 = lb_emit_load(p, dst); lbValue a = lb_emit_matrix_ev(p, lhs, i, j); - lbValue b = lb_emit_struct_ev(p, rhs, cast(i32)j); + + + LLVMValueRef b_value = LLVMBuildExtractValue(p->builder, rhs.value, cast(unsigned)j, ""); + lbValue b = {b_value, vector_elem_type}; + lbValue c = lb_emit_mul_add(p, a, b, d0, elem); lb_emit_store(p, dst, c); } @@ -1049,7 +1049,7 @@ gb_internal lbValue lb_emit_vector_mul_matrix(lbProcedure *p, lbValue lhs, lbVal } for (unsigned column_index = 0; column_index < row_count; column_index++) { - LLVMValueRef value = lb_emit_struct_ev(p, lhs, column_index).value; + LLVMValueRef value = LLVMBuildExtractValue(p->builder, lhs.value, column_index, ""); LLVMValueRef row = llvm_vector_broadcast(p, value, column_count); v_rows[column_index] = row; } @@ -1078,12 +1078,16 @@ gb_internal lbValue lb_emit_vector_mul_matrix(lbProcedure *p, lbValue lhs, lbVal lbAddr res = lb_add_local_generated(p, type, true); + Type *vector_elem_type = base_array_type(rhs.type); + for (i64 j = 0; j < mt->Matrix.column_count; j++) { for (i64 k = 0; k < mt->Matrix.row_count; k++) { lbValue dst = lb_emit_matrix_epi(p, res.addr, 0, j); lbValue d0 = lb_emit_load(p, dst); - lbValue a = lb_emit_struct_ev(p, lhs, cast(i32)k); + LLVMValueRef a_value = LLVMBuildExtractValue(p->builder, lhs.value, cast(unsigned)k, ""); + lbValue a = {a_value, vector_elem_type}; + lbValue b = lb_emit_matrix_ev(p, rhs, k, j); lbValue c = lb_emit_mul_add(p, a, b, d0, elem); lb_emit_store(p, dst, c); @@ -1494,7 +1498,6 @@ gb_internal lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbV } } - lbAddr res = lb_add_local_generated(p, type, false); // NOTE: initialized in full later lbValue a = lb_emit_struct_ev(p, lhs, 0); lbValue b = lb_emit_struct_ev(p, lhs, 1); lbValue c = lb_emit_struct_ev(p, rhs, 0); @@ -1532,10 +1535,9 @@ gb_internal lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbV } } - lb_emit_store(p, lb_emit_struct_ep(p, res.addr, 0), real); - lb_emit_store(p, lb_emit_struct_ep(p, res.addr, 1), imag); - return lb_addr_load(p, res); + lbValue fields[2] = {real, imag}; + return lb_build_struct_value(p, type, fields, gb_count_of(fields)); } else if (is_type_quaternion(type)) { lhs = lb_emit_conv(p, lhs, type); rhs = lb_emit_conv(p, rhs, type); @@ -1548,7 +1550,6 @@ gb_internal lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbV immediate_type = t_f32; } - lbAddr res = lb_add_local_generated(p, type, false); // NOTE: initialized in full later lbValue x0 = lb_emit_struct_ev(p, lhs, 0); lbValue x1 = lb_emit_struct_ev(p, lhs, 1); lbValue x2 = lb_emit_struct_ev(p, lhs, 2); @@ -1576,10 +1577,6 @@ gb_internal lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbV lbValue z2 = lb_emit_arith(p, op, x2, y2, immediate_type); lbValue z3 = lb_emit_arith(p, op, x3, y3, immediate_type); - lbValue d0 = lb_emit_struct_ep(p, res.addr, 0); - lbValue d1 = lb_emit_struct_ep(p, res.addr, 1); - lbValue d2 = lb_emit_struct_ep(p, res.addr, 2); - lbValue d3 = lb_emit_struct_ep(p, res.addr, 3); if (immediate_type != ft) { z0 = lb_emit_conv(p, z0, ft); @@ -1588,12 +1585,8 @@ gb_internal lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbV z3 = lb_emit_conv(p, z3, ft); } - lb_emit_store(p, d0, z0); - lb_emit_store(p, d1, z1); - lb_emit_store(p, d2, z2); - lb_emit_store(p, d3, z3); - - return lb_addr_load(p, res); + lbValue fields[4] = {z0, z1, z2, z3}; + return lb_build_struct_value(p, type, fields, gb_count_of(fields)); } else if (op == Token_Mul) { TEMPORARY_ALLOCATOR_GUARD(); @@ -2231,77 +2224,63 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { if (is_type_complex(src) && is_type_complex(dst)) { Type *ft = base_complex_elem_type(dst); - lbAddr gen = lb_add_local_generated(p, t, false); - lbValue gp = lb_addr_get_ptr(p, gen); lbValue real = lb_emit_conv(p, lb_emit_struct_ev(p, value, 0), ft); lbValue imag = lb_emit_conv(p, lb_emit_struct_ev(p, value, 1), ft); - lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), real); - lb_emit_store(p, lb_emit_struct_ep(p, gp, 1), imag); - return lb_addr_load(p, gen); + + lbValue fields[2] = {real, imag}; + return lb_build_struct_value(p, t, fields, gb_count_of(fields)); } if (is_type_quaternion(src) && is_type_quaternion(dst)) { // @QuaternionLayout Type *ft = base_complex_elem_type(dst); - lbAddr gen = lb_add_local_generated(p, t, false); - lbValue gp = lb_addr_get_ptr(p, gen); lbValue q0 = lb_emit_conv(p, lb_emit_struct_ev(p, value, 0), ft); lbValue q1 = lb_emit_conv(p, lb_emit_struct_ev(p, value, 1), ft); lbValue q2 = lb_emit_conv(p, lb_emit_struct_ev(p, value, 2), ft); lbValue q3 = lb_emit_conv(p, lb_emit_struct_ev(p, value, 3), ft); - lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), q0); - lb_emit_store(p, lb_emit_struct_ep(p, gp, 1), q1); - lb_emit_store(p, lb_emit_struct_ep(p, gp, 2), q2); - lb_emit_store(p, lb_emit_struct_ep(p, gp, 3), q3); - return lb_addr_load(p, gen); + + lbValue fields[4] = {q0, q1, q2, q3}; + return lb_build_struct_value(p, t, fields, gb_count_of(fields)); } if (is_type_integer(src) && is_type_complex(dst)) { Type *ft = base_complex_elem_type(dst); - lbAddr gen = lb_add_local_generated(p, t, true); - lbValue gp = lb_addr_get_ptr(p, gen); lbValue real = lb_emit_conv(p, value, ft); - lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), real); - return lb_addr_load(p, gen); + lbValue fields[2] = {real, lb_const_nil(m, ft)}; + return lb_build_struct_value(p, t, fields, gb_count_of(fields)); } if (is_type_float(src) && is_type_complex(dst)) { Type *ft = base_complex_elem_type(dst); - lbAddr gen = lb_add_local_generated(p, t, true); - lbValue gp = lb_addr_get_ptr(p, gen); lbValue real = lb_emit_conv(p, value, ft); - lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), real); - return lb_addr_load(p, gen); + lbValue fields[2] = {real, lb_const_nil(m, ft)}; + return lb_build_struct_value(p, t, fields, gb_count_of(fields)); } if (is_type_integer(src) && is_type_quaternion(dst)) { Type *ft = base_complex_elem_type(dst); - lbAddr gen = lb_add_local_generated(p, t, true); - lbValue gp = lb_addr_get_ptr(p, gen); lbValue real = lb_emit_conv(p, value, ft); + lbValue zero = lb_const_nil(m, ft); // @QuaternionLayout - lb_emit_store(p, lb_emit_struct_ep(p, gp, 3), real); - return lb_addr_load(p, gen); + lbValue fields[4] = {zero, zero, zero, real}; + return lb_build_struct_value(p, t, fields, gb_count_of(fields)); } if (is_type_float(src) && is_type_quaternion(dst)) { Type *ft = base_complex_elem_type(dst); - lbAddr gen = lb_add_local_generated(p, t, true); - lbValue gp = lb_addr_get_ptr(p, gen); lbValue real = lb_emit_conv(p, value, ft); + lbValue zero = lb_const_nil(m, ft); // @QuaternionLayout - lb_emit_store(p, lb_emit_struct_ep(p, gp, 3), real); - return lb_addr_load(p, gen); + lbValue fields[4] = {zero, zero, zero, real}; + return lb_build_struct_value(p, t, fields, gb_count_of(fields)); } if (is_type_complex(src) && is_type_quaternion(dst)) { Type *ft = base_complex_elem_type(dst); - lbAddr gen = lb_add_local_generated(p, t, true); - lbValue gp = lb_addr_get_ptr(p, gen); lbValue real = lb_emit_conv(p, lb_emit_struct_ev(p, value, 0), ft); lbValue imag = lb_emit_conv(p, lb_emit_struct_ev(p, value, 1), ft); + lbValue zero = lb_const_nil(m, ft); // @QuaternionLayout - lb_emit_store(p, lb_emit_struct_ep(p, gp, 3), real); - lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), imag); - return lb_addr_load(p, gen); + lbValue fields[4] = {imag, zero, zero, real}; + return lb_build_struct_value(p, t, fields, gb_count_of(fields)); } // float <-> integer @@ -2843,8 +2822,6 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { return lb_const_nil(p->module, t); } - lbAddr result = lb_add_local_generated(p, t, true); - Type *st = default_type(src_type); lbValue data = lb_address_from_load_or_generate_local(p, value); @@ -2853,13 +2830,8 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { data = lb_emit_conv(p, data, t_rawptr); lbValue id = lb_typeid(p->module, st); - lbValue any_data = lb_emit_struct_ep(p, result.addr, 0); - lbValue any_id = lb_emit_struct_ep(p, result.addr, 1); - - lb_emit_store(p, any_data, data); - lb_emit_store(p, any_id, id); - - return lb_addr_load(p, result); + lbValue fields[2] = {data, id}; + return lb_build_struct_value(p, t, fields, gb_count_of(fields)); } @@ -2926,10 +2898,13 @@ gb_internal lbValue lb_emit_c_vararg(lbProcedure *p, lbValue arg, Type *type) { gb_internal lbValue lb_compare_records(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right, Type *type) { GB_ASSERT((is_type_struct(type) || is_type_soa_pointer(type) || is_type_union(type)) && is_type_comparable(type)); + lbValue left_ptr = lb_address_from_load_or_generate_local(p, left); lbValue right_ptr = lb_address_from_load_or_generate_local(p, right); + i64 size = type_size_of(type); + lbValue res = {}; - if (type_size_of(type) == 0) { + if (size == 0) { switch (op_kind) { case Token_CmpEq: return lb_const_bool(p->module, t_bool, true); @@ -2938,8 +2913,23 @@ gb_internal lbValue lb_compare_records(lbProcedure *p, TokenKind op_kind, lbValu } GB_PANIC("invalid operator"); } + TEMPORARY_ALLOCATOR_GUARD(); + if (is_type_simple_compare(type)) { + // if (size <= 8) { + // LLVMTypeRef int_type = LLVMIntTypeInContext(p->module->ctx, cast(unsigned)(size*8)); + // LLVMValueRef l = OdinLLVMBuildLoad(p, int_type, LLVMBuildPointerCast(p->builder, left_ptr.value, LLVMPointerType(int_type, 0), "")); + // LLVMValueRef r = OdinLLVMBuildLoad(p, int_type, LLVMBuildPointerCast(p->builder, right_ptr.value, LLVMPointerType(int_type, 0), "")); + + // LLVMIntPredicate pred = (op_kind == Token_CmpEq) ? LLVMIntEQ : LLVMIntNE; + // LLVMValueRef cmp = LLVMBuildICmp(p->builder, pred, l, r, ""); + + // res.value = cmp; + // res.type = t_bool; + // return res; + // } + // TODO(bill): Test to see if this is actually faster!!!! auto args = array_make(temporary_allocator(), 3); args[0] = lb_emit_conv(p, left_ptr, t_rawptr); @@ -2953,6 +2943,7 @@ gb_internal lbValue lb_compare_records(lbProcedure *p, TokenKind op_kind, lbValu args[1] = lb_emit_conv(p, right_ptr, t_rawptr); res = lb_emit_call(p, value, args); } + if (op_kind == Token_NotEq) { res = lb_emit_unary_arith(p, Token_Not, res, res.type); } @@ -4113,10 +4104,18 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) { case_end; case_ast_node(te, TernaryIfExpr, expr); + GB_ASSERT(te->y != nullptr); + Type *type = default_type(type_of_expr(expr)); + if (lb_is_expr_trivial(te->x) && lb_is_expr_trivial(te->y)) { + lbValue cond = lb_build_expr(p, te->cond); + lbValue x = lb_emit_conv(p, lb_build_expr(p, te->x), type); + lbValue y = lb_emit_conv(p, lb_build_expr(p, te->y), type); + return lb_emit_select(p, cond, x, y); + } + LLVMValueRef incoming_values[2] = {}; LLVMBasicBlockRef incoming_blocks[2] = {}; - GB_ASSERT(te->y != nullptr); lbBlock *then = lb_create_block(p, "if.then"); lbBlock *done = lb_create_block(p, "if.done"); // NOTE(bill): Append later lbBlock *else_ = lb_create_block(p, "if.else"); @@ -4124,7 +4123,6 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) { lb_build_cond(p, te->cond, then, else_); lb_start_block(p, then); - Type *type = default_type(type_of_expr(expr)); LLVMTypeRef llvm_type = lb_type(p->module, type); incoming_values[0] = lb_emit_conv(p, lb_build_expr(p, te->x), type).value; @@ -4291,6 +4289,24 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) { return lb_build_expr(p, se->expr); } } + { + Type *src_type = base_type(type_of_expr(se->expr)); + if (is_type_pointer(src_type)) { + src_type = base_type(type_deref(src_type)); + } + + if (is_type_slice(src_type) || + is_type_dynamic_array(src_type) || + is_type_string(src_type) || + is_type_string16(src_type)) { + return lb_build_slice_expr_value(p, expr); + } + + if (is_type_multi_pointer(src_type) && se->high != nullptr) { + return lb_build_slice_expr_value(p, expr); + } + } + return lb_addr_load(p, lb_build_addr(p, expr)); case_end; @@ -4346,9 +4362,9 @@ gb_internal lbAddr lb_get_soa_variable_addr(lbProcedure *p, Entity *e) { } gb_internal lbValue lb_get_using_variable(lbProcedure *p, Entity *e) { GB_ASSERT(e->kind == Entity_Variable && e->flags & EntityFlag_Using); - String name = e->token.string; + InternedString interned = entity_interned_name(e); Entity *parent = e->using_parent; - Selection sel = lookup_field(parent->type, name, false); + Selection sel = lookup_field(parent->type, interned, false); GB_ASSERT(sel.entity != nullptr); lbValue *pv = map_get(&p->module->values, parent); @@ -4363,7 +4379,7 @@ gb_internal lbValue lb_get_using_variable(lbProcedure *p, Entity *e) { } else if (pv != nullptr) { v = *pv; } else { - GB_ASSERT_MSG(e->using_expr != nullptr, "%.*s", LIT(name)); + GB_ASSERT_MSG(e->using_expr != nullptr, "%.*s", LIT(e->token.string)); v = lb_build_addr_ptr(p, e->using_expr); } GB_ASSERT(v.value != nullptr); @@ -4495,6 +4511,7 @@ gb_internal void lb_build_addr_compound_lit_populate(lbProcedure *p, SliceDynamicArray.elem; break; case Type_SimdVector: et = bt->SimdVector.elem; break; case Type_Matrix: et = bt->Matrix.elem; break; + case Type_FixedCapacityDynamicArray: et = bt->FixedCapacityDynamicArray.elem; break; } GB_ASSERT(et != nullptr); @@ -4700,6 +4717,25 @@ gb_internal lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) { return lb_addr(elem); } + case Type_FixedCapacityDynamicArray: { + lbValue array = {}; + array = lb_build_addr_ptr(p, ie->expr); + if (deref) { + array = lb_emit_load(p, array); + } + lbValue index = lb_build_expr(p, ie->index); + index = lb_emit_conv(p, index, t_int); + + lbValue array_ptr = lb_emit_struct_ep(p, array, 0); + lbValue elem = lb_emit_array_ep(p, array_ptr, index); + + auto index_tv = type_and_value_of_expr(ie->index); + lbValue len = lb_emit_struct_ep(p, array, 1); + len = lb_emit_load(p, len); + lb_emit_bounds_check(p, ast_token(ie->index), index, len); + return lb_addr(elem); + } + case Type_EnumeratedArray: { lbValue array = {}; array = lb_build_addr_ptr(p, ie->expr); @@ -4833,6 +4869,216 @@ gb_internal lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) { } +gb_internal lbValue lb_build_slice_expr_value(lbProcedure *p, Ast *expr) { + ast_node(se, SliceExpr, expr); + + if (true) { + // NOTE(bill): Fused nested slice chain: src[a:b][c:d][e:f]... + // Walk a chain of nested SliceExprs down to the root source, then apply + // each slice's bounds check and offset on raw (ptr, len) SSA values, + // building a single aggregate at the end. + + + Ast *inner_ast = unparen_expr(se->expr); + if (inner_ast->kind == Ast_SliceExpr) { + enum {MAX_CHAIN = 8}; // this is more than enough in practice + + // Collect the chain: slices[0] is innermost, slices[n-1] is outer-most `se`. + Ast *chain[MAX_CHAIN] = {}; // bounded depth + isize chain_count = 0; + chain[chain_count++] = expr; // outer-most + + Ast *root = inner_ast; + while (root->kind == Ast_SliceExpr && chain_count < MAX_CHAIN) { + chain[chain_count++] = root; + root = unparen_expr(root->SliceExpr.expr); + } + + Type *root_type = base_type(type_of_expr(root)); + if (is_type_pointer(root_type)) { + root_type = base_type(type_deref(root_type)); + } + if (is_type_slice(root_type) || + is_type_dynamic_array(root_type) || + is_type_string(root_type) || + is_type_string16(root_type)) { + lbModule *m = p->module; + + // Evaluate the root source and extract raw (ptr, len). + lbValue src = lb_build_expr(p, root); + Type *src_type = base_type(src.type); + if (is_type_pointer(src_type)) { + src_type = base_type(type_deref(src_type)); + src = lb_emit_load(p, src); + } + + lbValue cur_ptr = {}; + lbValue cur_len = {}; + if (is_type_slice(src_type)) { + cur_ptr = lb_slice_elem(p, src); + cur_len = lb_slice_len(p, src); + } else if (is_type_dynamic_array(src_type)) { + cur_ptr = lb_dynamic_array_elem(p, src); + cur_len = lb_dynamic_array_len(p, src); + } else { + GB_ASSERT(is_type_string(src_type) || is_type_string16(src_type)); + cur_ptr = lb_string_elem(p, src); + cur_len = lb_string_len(p, src); + } + + // Apply each slice innermost-first (reverse of collection order). + for (isize i = chain_count - 1; i >= 0; i--) { + AstSliceExpr *s = &chain[i]->SliceExpr; + + lbValue lo = lb_const_int(m, t_int, 0); + lbValue hi = {}; + if (s->low != nullptr) { + lo = lb_correct_endianness(p, lb_build_expr(p, s->low)); + } + if (s->high != nullptr) { + hi = lb_correct_endianness(p, lb_build_expr(p, s->high)); + } + if (hi.value == nullptr) { + hi = cur_len; + } + + bool no_indices = s->low == nullptr && s->high == nullptr; + if (!no_indices) { + lb_emit_slice_bounds_check(p, s->open, lo, hi, cur_len, s->low != nullptr); + } + + cur_ptr = (s->low == nullptr) ? cur_ptr : lb_emit_ptr_offset(p, cur_ptr, lo); + cur_len = (s->low == nullptr) ? lb_emit_conv(p, hi, t_int) : lb_emit_arith(p, Token_Sub, hi, lo, t_int); + } + + // Single aggregate construction. + Type *result_type = type_of_expr(expr); + if (is_type_string(result_type)) { + return lb_make_string_value(p, result_type, cur_ptr, cur_len); + } + return lb_make_slice_value(p, result_type, cur_ptr, cur_len); + } + } + } + + lbValue base = lb_build_expr(p, se->expr); + Type *type = base_type(base.type); + + if (is_type_pointer(type)) { + type = base_type(type_deref(type)); + base = lb_emit_load(p, base); + } + + lbValue low = lb_const_int(p->module, t_int, 0); + lbValue high = {}; + + if (se->low != nullptr) { + low = lb_correct_endianness(p, lb_build_expr(p, se->low)); + } + if (se->high != nullptr) { + high = lb_correct_endianness(p, lb_build_expr(p, se->high)); + } + + bool no_indices = se->low == nullptr && se->high == nullptr; + + switch (type->kind) { + case Type_Slice: { + Type *slice_type = type; + lbValue len = lb_slice_len(p, base); + if (high.value == nullptr) high = len; + + if (!no_indices) { + lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr); + } + + lbValue elem = lb_emit_ptr_offset(p, lb_slice_elem(p, base), low); + lbValue new_len = (se->low == nullptr) + ? lb_emit_conv(p, high, t_int) + : lb_emit_arith(p, Token_Sub, high, low, t_int); + + return lb_make_slice_value(p, slice_type, elem, new_len); + } + + case Type_DynamicArray: { + Type *elem_type = type->DynamicArray.elem; + Type *slice_type = alloc_type_slice(elem_type); + + lbValue len = lb_dynamic_array_len(p, base); + if (high.value == nullptr) high = len; + + if (!no_indices) { + lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr); + } + + lbValue elem = lb_emit_ptr_offset(p, lb_dynamic_array_elem(p, base), low); + lbValue new_len = (se->low == nullptr) + ? lb_emit_conv(p, high, t_int) + : lb_emit_arith(p, Token_Sub, high, low, t_int); + + return lb_make_slice_value(p, slice_type, elem, new_len); + } + + case Type_Basic: { + if (is_type_string16(type)) { + GB_ASSERT_MSG(are_types_identical(type, t_string16), "got %s", type_to_string(type)); + lbValue len = lb_string_len(p, base); + if (high.value == nullptr) high = len; + + if (!no_indices) { + lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr); + } + + lbValue elem = lb_emit_ptr_offset(p, lb_string_elem(p, base), low); + lbValue new_len = (se->low == nullptr) + ? lb_emit_conv(p, high, t_int) + : lb_emit_arith(p, Token_Sub, high, low, t_int); + + return lb_make_string_value(p, t_string16, elem, new_len); + } + GB_ASSERT_MSG(are_types_identical(type, t_string), "got %s", type_to_string(type)); + lbValue len = lb_string_len(p, base); + if (high.value == nullptr) high = len; + + if (!no_indices) { + lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr); + } + + lbValue elem = lb_emit_ptr_offset(p, lb_string_elem(p, base), low); + lbValue new_len = (se->low == nullptr) + ? lb_emit_conv(p, high, t_int) + : lb_emit_arith(p, Token_Sub, high, low, t_int); + + return lb_make_string_value(p, t_string, elem, new_len); + } + + case Type_MultiPointer: { + GB_ASSERT(se->high != nullptr); + Type *elem_type = type->MultiPointer.elem; + Type *slice_type = alloc_type_slice(elem_type); + + low = lb_emit_conv(p, low, t_int); + high = lb_emit_conv(p, high, t_int); + + lb_emit_multi_pointer_slice_bounds_check(p, se->open, low, high); + + LLVMValueRef indices[1] = {low.value}; + lbValue ptr = {}; + ptr.value = LLVMBuildGEP2(p->builder, lb_type(p->module, elem_type), base.value, indices, 1, ""); + ptr.type = alloc_type_pointer(elem_type); + + lbValue new_len = (se->low == nullptr) + ? lb_emit_conv(p, high, t_int) + : lb_emit_arith(p, Token_Sub, high, low, t_int); + + return lb_make_slice_value(p, slice_type, ptr, new_len); + } + + default: + GB_PANIC("lb_build_slice_expr_value: unexpected type %s", type_to_string(base.type)); + return {}; + } +} + gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) { ast_node(se, SliceExpr, expr); @@ -4943,6 +5189,31 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) { return slice; } + case Type_FixedCapacityDynamicArray: { + Type *elem_type = type->FixedCapacityDynamicArray.elem; + Type *slice_type = alloc_type_slice(elem_type); + + lbValue len = lb_fixed_capacity_dynamic_array_len(p, base); + if (high.value == nullptr) high = len; + + bool low_const = type_and_value_of_expr(se->low).mode == Addressing_Constant; + bool high_const = type_and_value_of_expr(se->high).mode == Addressing_Constant; + + if (!low_const || !high_const) { + if (!no_indices) { + lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr); + } + } + lbValue data_ptr = lb_addr_get_ptr(p, addr); + lbValue array_ptr = lb_emit_struct_ep(p, data_ptr, 0); + lbValue elem = lb_emit_ptr_offset(p, lb_array_elem(p, array_ptr), low); + lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int); + + lbAddr slice = lb_add_local_generated(p, slice_type, false); + lb_fill_slice(p, slice, elem, new_len); + return slice; + } + case Type_Basic: { if (is_type_string16(type)) { GB_ASSERT_MSG(are_types_identical(type, t_string16), "got %s", type_to_string(type)); @@ -5058,12 +5329,13 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) { Type *et = nullptr; switch (bt->kind) { - case Type_Array: et = bt->Array.elem; break; - case Type_EnumeratedArray: et = bt->EnumeratedArray.elem; break; - case Type_Slice: et = bt->Slice.elem; break; - case Type_BitSet: et = bt->BitSet.elem; break; - case Type_SimdVector: et = bt->SimdVector.elem; break; - case Type_Matrix: et = bt->Matrix.elem; break; + case Type_Array: et = bt->Array.elem; break; + case Type_EnumeratedArray: et = bt->EnumeratedArray.elem; break; + case Type_Slice: et = bt->Slice.elem; break; + case Type_BitSet: et = bt->BitSet.elem; break; + case Type_SimdVector: et = bt->SimdVector.elem; break; + case Type_Matrix: et = bt->Matrix.elem; break; + case Type_FixedCapacityDynamicArray: et = bt->FixedCapacityDynamicArray.elem; break; } String proc_name = {}; @@ -5090,8 +5362,8 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) { for (Ast *elem : cl->elems) { ast_node(fv, FieldValue, elem); - String name = fv->field->Ident.token.string; - Selection sel = lookup_field(bt, name, false); + InternedString interned = fv->field->Ident.interned; + Selection sel = lookup_field(bt, interned, false); GB_ASSERT(sel.is_bit_field); GB_ASSERT(!sel.indirect); GB_ASSERT(sel.index.count == 1); @@ -5284,8 +5556,8 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) { if (elem->kind == Ast_FieldValue) { ast_node(fv, FieldValue, elem); - String name = fv->field->Ident.token.string; - Selection sel = lookup_field(bt, name, false); + InternedString interned = fv->field->Ident.interned; + Selection sel = lookup_field(bt, interned, false); GB_ASSERT(!sel.indirect); elem = fv->value; @@ -5342,11 +5614,20 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) { Type *fet = field_expr.type; GB_ASSERT(fet->kind != Type_Tuple); - // HACK TODO(bill): THIS IS A MASSIVE HACK!!!! if (is_type_union(ft) && !are_types_identical(fet, ft) && !is_type_untyped(fet)) { - GB_ASSERT_MSG(union_variant_index(ft, fet) >= 0, "%s", type_to_string(fet)); + if (union_is_variant_of(ft, fet)) { + // NOTE(bill, 2026-03-15): If it's a direct assignment for a variant to the + // direct union we can just assign it directly to minimize wasted code generation + // + // TODO(bill): Allow this for deeply nested unions too e.g. union{union{T}} + GB_ASSERT_MSG(union_variant_index_checked(ft, fet) >= 0, "%s", type_to_string(fet)); + GB_ASSERT(are_types_identical(type_deref(gep.type), ft)); - lb_emit_store_union_variant(p, gep, field_expr, fet); + lb_emit_store_union_variant(p, gep, field_expr, fet); + } else { + lbValue fv = lb_emit_conv(p, field_expr, ft); + lb_emit_store(p, gep, fv); + } } else { lbValue fv = lb_emit_conv(p, field_expr, ft); lb_emit_store(p, gep, fv); @@ -5445,6 +5726,25 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) { break; } + case Type_FixedCapacityDynamicArray: { + if (cl->elems.count > 0) { + lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr))); + + auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); + + lb_build_addr_compound_lit_populate(p, cl->elems, &temp_data, type); + + lbValue dst_ptr = lb_addr_get_ptr(p, v); + for_array(i, temp_data) { + i32 index = cast(i32)(temp_data[i].elem_index); + temp_data[i].gep = lb_emit_array_epi(p, dst_ptr, index); + } + + lb_build_addr_compound_lit_assign_array(p, temp_data); + } + break; + } + case Type_DynamicArray: { if (cl->elems.count == 0) { break; @@ -5511,12 +5811,12 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) { if (elem->kind == Ast_FieldValue) { ast_node(fv, FieldValue, elem); - Selection sel = lookup_field(bt, fv->field->Ident.token.string, false); + Selection sel = lookup_field(bt, fv->field->Ident.interned, false); index = sel.index[0]; elem = fv->value; } else { TypeAndValue tav = type_and_value_of_expr(elem); - Selection sel = lookup_field(bt, field_names[field_index], false); + Selection sel = lookup_field(bt, string_interner_insert(field_names[field_index]), false); index = sel.index[0]; } @@ -5658,7 +5958,7 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) { case_ast_node(se, SelectorExpr, expr); Ast *sel_node = unparen_expr(se->selector); if (sel_node->kind == Ast_Ident) { - String selector = sel_node->Ident.token.string; + InternedString selector = sel_node->Ident.interned; TypeAndValue tav = type_and_value_of_expr(se->expr); if (tav.mode == Addressing_Invalid) { @@ -5678,7 +5978,7 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) { GB_ASSERT(e->kind == Entity_Procedure); return lb_addr(lb_find_value_from_entity(p->module, e)); } - GB_PANIC("Unreachable %.*s", LIT(selector)); + GB_PANIC("Unreachable %s", selector.cstring()); } if (se->swizzle_count > 0) { diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 572e3990c..b15eb8333 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -166,7 +166,7 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { bool module_per_file = build_context.module_per_file && (build_context.optimization_level <= 0 || build_context.lto_kind != LTO_None); for (auto const &entry : gen->info->packages) { AstPackage *pkg = entry.value; - auto m = gb_alloc_item(permanent_allocator(), lbModule); + auto m = permanent_alloc_item(); m->pkg = pkg; m->gen = gen; m->checker = c; @@ -174,7 +174,7 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { lb_init_module(m, do_threading); if (LLVM_WEAK_MONOMORPHIZATION) { - auto pm = gb_alloc_item(permanent_allocator(), lbModule); + auto pm = permanent_alloc_item(); pm->pkg = pkg; pm->gen = gen; pm->checker = c; @@ -216,7 +216,7 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { } // NOTE(bill): Probably per file is not a good idea, so leave this for later for (AstFile *file : pkg->files) { - auto m = gb_alloc_item(permanent_allocator(), lbModule); + auto m = permanent_alloc_item(); m->file = file; m->pkg = pkg; m->gen = gen; @@ -226,7 +226,7 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { if (LLVM_WEAK_MONOMORPHIZATION) { - auto pm = gb_alloc_item(permanent_allocator(), lbModule); + auto pm = permanent_alloc_item(); pm->file = file; pm->pkg = pkg; pm->gen = gen; @@ -242,7 +242,7 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { } if (LLVM_WEAK_MONOMORPHIZATION) { - lbModule *m = gb_alloc_item(permanent_allocator(), lbModule); + lbModule *m = permanent_alloc_item(); gen->equal_module = m; m->gen = gen; m->checker = c; @@ -498,10 +498,11 @@ gb_internal lbModule *lb_module_of_expr(lbGenerator *gen, Ast *expr) { gb_internal lbModule *lb_module_of_entity_internal(lbGenerator *gen, Entity *e, lbModule *curr_module) { lbModule **found = nullptr; - if (e->kind == Entity_Procedure && - e->decl_info && - e->decl_info->code_gen_module) { - return e->decl_info->code_gen_module; + if (e->kind == Entity_Procedure && e->decl_info) { + lbModule *mod = e->decl_info->code_gen_module.load(std::memory_order_relaxed); + if (mod) { + return mod; + } } if (e->file) { found = map_get(&gen->modules, cast(void *)e->file); @@ -700,11 +701,45 @@ gb_internal void lb_set_file_line_col(lbProcedure *p, Array arr, TokenP arr[2] = lb_const_int(p->module, t_i32, col); } -gb_internal void lb_emit_bounds_check(lbProcedure *p, Token token, lbValue index, lbValue len) { +gb_internal bool lb_bounds_check_short_circuit(lbProcedure *p, lbValue index, lbValue len) { if (build_context.no_bounds_check) { - return; + return true; } if ((p->state_flags & StateFlag_no_bounds_check) != 0) { + return true; + } + + if (LLVMIsConstant(index.value) && LLVMIsConstant(len.value)) { + i64 i = LLVMConstIntGetSExtValue(index.value); + i64 n = LLVMConstIntGetSExtValue(len.value); + if (0 <= i && i < n) { + return true; + } + } + + if (LLVMIsAInstruction(index.value)) { + LLVMOpcode op = LLVMGetInstructionOpcode(index.value); + if (op == LLVMURem) { + LLVMValueRef divisor = LLVMGetOperand(index.value, 1); + if (divisor == len.value) { + return true; + } + } else if (op == LLVMAnd) { + LLVMValueRef mask = LLVMGetOperand(index.value, 1); + if (LLVMIsConstant(mask) && LLVMIsConstant(len.value)) { + i64 m = LLVMConstIntGetSExtValue(mask); + i64 l = LLVMConstIntGetSExtValue(len.value); + if (l > 0 && (l & (l-1)) == 0 && m == l-1) { + return true; + } + } + } + } + return false; +} + +gb_internal void lb_emit_bounds_check(lbProcedure *p, Token token, lbValue index, lbValue len) { + if (lb_bounds_check_short_circuit(p, index, len)) { return; } @@ -755,6 +790,15 @@ gb_internal void lb_emit_multi_pointer_slice_bounds_check(lbProcedure *p, Token return; } + if (LLVMIsConstant(low.value) && LLVMIsConstant(high.value)) { + i64 i = LLVMConstIntGetSExtValue(low.value); + i64 n = LLVMConstIntGetSExtValue(high.value); + if (i < n) { + return; + } + } + + low = lb_emit_conv(p, low, t_int); high = lb_emit_conv(p, high, t_int); @@ -767,12 +811,15 @@ gb_internal void lb_emit_multi_pointer_slice_bounds_check(lbProcedure *p, Token } gb_internal void lb_emit_slice_bounds_check(lbProcedure *p, Token token, lbValue low, lbValue high, lbValue len, bool lower_value_used) { - if (build_context.no_bounds_check) { - return; - } - if ((p->state_flags & StateFlag_no_bounds_check) != 0) { + if (!lower_value_used && lb_bounds_check_short_circuit(p, high, len)) { return; } + // if (lower_value_used && + // lb_bounds_check_short_circuit(p, low, high) && + // lb_bounds_check_short_circuit(p, low, len) && + // lb_bounds_check_short_circuit(p, high, len)) { + // return; + // } high = lb_emit_conv(p, high, t_int); @@ -1497,7 +1544,7 @@ gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) { } gb_internal lbValue lb_const_union_tag(lbModule *m, Type *u, Type *v) { - return lb_const_value(m, union_tag_type(u), exact_value_i64(union_variant_index(u, v))); + return lb_const_value(m, union_tag_type(u), exact_value_i64(union_variant_index_checked(u, v))); } gb_internal lbValue lb_emit_union_tag_ptr(lbProcedure *p, lbValue u) { @@ -1540,13 +1587,17 @@ gb_internal void lb_emit_store_union_variant_tag(lbProcedure *p, lbValue parent, // No tag needed! } else { lbValue tag_ptr = lb_emit_union_tag_ptr(p, parent); - lb_emit_store(p, tag_ptr, lb_const_union_tag(p->module, t, variant_type)); + lbValue tag = lb_const_union_tag(p->module, t, variant_type); + lb_emit_store(p, tag_ptr, tag); } } gb_internal void lb_emit_store_union_variant(lbProcedure *p, lbValue parent, lbValue variant, Type *variant_type) { Type *pt = base_type(type_deref(parent.type)); GB_ASSERT(pt->kind == Type_Union); + + GB_ASSERT_MSG(union_is_variant_of(pt, variant_type), "%s %s", type_to_string(pt), type_to_string(variant_type)); + if (pt->Union.kind == UnionType_shared_nil) { GB_ASSERT(type_size_of(variant_type)); @@ -2234,6 +2285,26 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { } break; + case Type_FixedCapacityDynamicArray: + { + unsigned field_count = 0; + + LLVMTypeRef fields[3] = {}; + m->internal_type_level += 1; + fields[field_count++] = llvm_array_type(lb_type(m, type->FixedCapacityDynamicArray.elem), type->FixedCapacityDynamicArray.capacity); + m->internal_type_level -= 1; + + gb_unused(type_size_of(type)); + if (type->FixedCapacityDynamicArray.padding_needed > 0) { + fields[field_count++] = lb_type_padding_filler(m, type->FixedCapacityDynamicArray.padding_needed, 1); // padding + } + + fields[field_count++] = lb_type(m, t_int); // len + + return LLVMStructTypeInContext(ctx, fields, field_count, false); + } + break; + case Type_Map: init_map_internal_debug_types(type); GB_ASSERT(t_raw_map != nullptr); @@ -2657,7 +2728,7 @@ gb_internal void lb_add_edge(lbBlock *from, lbBlock *to) { gb_internal lbBlock *lb_create_block(lbProcedure *p, char const *name, bool append) { - lbBlock *b = gb_alloc_item(permanent_allocator(), lbBlock); + lbBlock *b = permanent_alloc_item(); b->block = LLVMCreateBasicBlockInContext(p->module->ctx, name); b->appended = false; if (append) { @@ -3335,7 +3406,7 @@ gb_internal lbAddr lb_add_global_generated_from_procedure(lbProcedure *p, Type * gb_internal lbValue lb_find_runtime_value(lbModule *m, String const &name) { AstPackage *p = m->info->runtime_package; - Entity *e = scope_lookup_current(p->scope, name); + Entity *e = scope_lookup_current(p->scope, string_interner_insert(name)); return lb_find_value_from_entity(m, e); } gb_internal lbValue lb_find_package_value(lbModule *m, String const &pkg, String const &name) { @@ -3542,7 +3613,30 @@ gb_internal lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e, bool zero } gb_internal lbAddr lb_add_local_generated(lbProcedure *p, Type *type, bool zero_init) { +#if 0 return lb_add_local(p, type, nullptr, zero_init); +#else + LLVMTypeRef llvm_type = lb_type(p->module, type); + + unsigned alignment = cast(unsigned)gb_max(type_align_of(type), lb_alignof(llvm_type)); + if (is_type_matrix(type)) { + alignment *= 2; // NOTE(bill): Just in case + } + + // This positions in the entry block, emits alloca, then restores position. + LLVMValueRef ptr = llvm_alloca(p, llvm_type, alignment, ""); + + if (zero_init) { + // Emit the zero-init store at the current position (not entry block) + // so it respects control flow. But the alloca itself is in entry. + lb_mem_zero_ptr(p, ptr, type, alignment); + } + + lbValue val = {}; + val.value = ptr; + val.type = alloc_type_pointer(type); + return lb_addr(val); +#endif } gb_internal lbAddr lb_add_local_generated_temp(lbProcedure *p, Type *type, i64 min_alignment) { diff --git a/src/llvm_backend_passes.cpp b/src/llvm_backend_passes.cpp index e9edbace7..744f6af61 100644 --- a/src/llvm_backend_passes.cpp +++ b/src/llvm_backend_passes.cpp @@ -4,6 +4,9 @@ break; case 0: array_add(&passes, "always-inline"); + if (build_context.internal_llvm_mem2reg) { + array_add(&passes, "function(mem2reg)"); + } array_add(&passes, "function(annotation-remarks)"); break; case 1: diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index f72726af1..36d736b0f 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -99,7 +99,7 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i } } - lbProcedure *p = gb_alloc_item(permanent_allocator(), lbProcedure); + lbProcedure *p = permanent_alloc_item(); p->module = m; entity->code_gen_module = m; @@ -395,7 +395,7 @@ gb_internal lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name GB_ASSERT_MSG(found == nullptr, "failed to create dummy procedure for: %.*s", LIT(link_name)); } - lbProcedure *p = gb_alloc_item(permanent_allocator(), lbProcedure); + lbProcedure *p = permanent_alloc_item(); p->module = m; p->name = link_name; @@ -1012,7 +1012,7 @@ gb_internal lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue gb_internal lbValue lb_lookup_runtime_procedure(lbModule *m, String const &name) { AstPackage *pkg = m->info->runtime_package; - Entity *e = scope_lookup_current(pkg->scope, name); + Entity *e = scope_lookup_current(pkg->scope, string_interner_insert(name)); GB_ASSERT_MSG(e != nullptr, "Runtime procedure not found: %s", name); return lb_find_procedure_value_from_entity(m, e); } @@ -1028,15 +1028,14 @@ gb_internal lbValue lb_emit_conjugate(lbProcedure *p, lbValue val, Type *type) { lbValue res = {}; Type *t = val.type; if (is_type_complex(t)) { - res = lb_addr_get_ptr(p, lb_add_local_generated(p, type, false)); lbValue real = lb_emit_struct_ev(p, val, 0); lbValue imag = lb_emit_struct_ev(p, val, 1); imag = lb_emit_unary_arith(p, Token_Sub, imag, imag.type); - lb_emit_store(p, lb_emit_struct_ep(p, res, 0), real); - lb_emit_store(p, lb_emit_struct_ep(p, res, 1), imag); + + lbValue fields[2] = {real, imag}; + return lb_build_struct_value(p, type, fields, gb_count_of(fields)); } else if (is_type_quaternion(t)) { // @QuaternionLayout - res = lb_addr_get_ptr(p, lb_add_local_generated(p, type, false)); lbValue real = lb_emit_struct_ev(p, val, 3); lbValue imag = lb_emit_struct_ev(p, val, 0); lbValue jmag = lb_emit_struct_ev(p, val, 1); @@ -1044,10 +1043,9 @@ gb_internal lbValue lb_emit_conjugate(lbProcedure *p, lbValue val, Type *type) { imag = lb_emit_unary_arith(p, Token_Sub, imag, imag.type); jmag = lb_emit_unary_arith(p, Token_Sub, jmag, jmag.type); kmag = lb_emit_unary_arith(p, Token_Sub, kmag, kmag.type); - lb_emit_store(p, lb_emit_struct_ep(p, res, 3), real); - lb_emit_store(p, lb_emit_struct_ep(p, res, 0), imag); - lb_emit_store(p, lb_emit_struct_ep(p, res, 1), jmag); - lb_emit_store(p, lb_emit_struct_ep(p, res, 2), kmag); + + lbValue fields[4] = {imag, jmag, kmag, real}; + return lb_build_struct_value(p, type, fields, gb_count_of(fields)); } else if (is_type_array_like(t)) { res = lb_addr_get_ptr(p, lb_add_local_generated(p, type, true)); Type *elem_type = base_array_type(t); @@ -1075,7 +1073,7 @@ gb_internal lbValue lb_emit_conjugate(lbProcedure *p, lbValue val, Type *type) { return lb_emit_load(p, res); } -gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, ProcInlining inlining, ProcTailing tailing) { +gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, ProcInlining inlining, ProcTailing tailing, lbValue *sret_dst) { lbModule *m = p->module; Type *pt = base_type(value.type); @@ -1197,7 +1195,12 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array c } if (return_by_pointer) { - lbValue return_ptr = lb_add_local_generated(p, rt, true).addr; + lbValue return_ptr = {}; + if (sret_dst != nullptr) { + return_ptr = *sret_dst; + } else { + return_ptr = lb_add_local_generated(p, rt, true).addr; + } lb_emit_call_internal(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining, tailing); result = lb_emit_load(p, return_ptr); } else if (rt != nullptr) { @@ -2323,6 +2326,8 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu return lb_slice_len(p, v); } else if (is_type_dynamic_array(t)) { return lb_dynamic_array_len(p, v); + } else if (is_type_fixed_capacity_dynamic_array(t)) { + return lb_fixed_capacity_dynamic_array_len(p, v); } else if (is_type_map(t)) { return lb_map_len(p, v); } else if (is_type_soa_struct(t)) { @@ -2344,6 +2349,8 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu GB_PANIC("Unreachable"); } else if (is_type_array(t)) { GB_PANIC("Array lengths are constant"); + } else if (is_type_fixed_capacity_dynamic_array(t)) { + GB_PANIC("Fixed capacity dynamic array capacities are constant"); } else if (is_type_slice(t)) { return lb_slice_len(p, v); } else if (is_type_dynamic_array(t)) { @@ -3365,6 +3372,24 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu return lb_emit_conv(p, res, t); } + case BuiltinProc_likely: + case BuiltinProc_unlikely: + { + Type *t = default_type(tv.type); + lbValue x = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t); + lbValue y = lb_const_bool(p->module, t, id == BuiltinProc_likely); + + char const *name = "llvm.expect"; + + LLVMTypeRef types[1] = {lb_type(p->module, t)}; + lbValue res = {}; + LLVMValueRef args[2] = { x.value, y.value }; + + res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types)); + res.type = t; + return lb_emit_conv(p, res, t); + } + case BuiltinProc_prefetch_read_instruction: case BuiltinProc_prefetch_read_data: case BuiltinProc_prefetch_write_instruction: @@ -4106,13 +4131,13 @@ gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, } -gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr); +gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr, lbValue *sret_dst = nullptr); -gb_internal lbValue lb_build_call_expr(lbProcedure *p, Ast *expr) { +gb_internal lbValue lb_build_call_expr(lbProcedure *p, Ast *expr, lbValue *sret_dst) { expr = unparen_expr(expr); ast_node(ce, CallExpr, expr); - lbValue res = lb_build_call_expr_internal(p, expr); + lbValue res = lb_build_call_expr_internal(p, expr, sret_dst); if (ce->optional_ok_one) { GB_ASSERT(is_type_tuple(res.type)); @@ -4133,7 +4158,7 @@ gb_internal void lb_add_values_to_array(lbProcedure *p, Array *args, lb } } -gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { +gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr, lbValue *sret_dst) { lbModule *m = p->module; TypeAndValue tv = type_and_value_of_expr(expr); @@ -4459,6 +4484,6 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { } } - return lb_emit_call(p, value, call_args, inlining, tailing); + return lb_emit_call(p, value, call_args, inlining, tailing, sret_dst); } diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index 98b45f646..fe09b26c8 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -1,3 +1,128 @@ +#define LB_ENABLE_BASIC_RVO true +#define LB_ENABLE_ADVANCED_RVO build_context.enable_rvo + +// NOTE(bill): @RVO Check if a call expression returns by sret with a return type matching dst_type. +// Returns the callee's function type if eligible for copy elision, nullptr otherwise. +gb_internal lbFunctionType *lb_call_sret_eligible(lbProcedure *p, Ast *call_expr, Type *dst_type) { + GB_ASSERT(call_expr->kind == Ast_CallExpr); + Ast *proc_expr = unparen_expr(call_expr->CallExpr.proc); + TypeAndValue proc_tv = type_and_value_of_expr(proc_expr); + if (proc_tv.mode == Addressing_Type || proc_tv.mode == Addressing_Builtin) { + return nullptr; + } + Type *pt = base_type(proc_tv.type); + if (pt == nullptr || pt->kind != Type_Proc || pt->Proc.results == nullptr) { + return nullptr; + } + lbFunctionType *callee_ft = lb_get_function_type(p->module, pt); + if (callee_ft->ret.kind != lbArg_Indirect) { + return nullptr; + } + Type *callee_ret = reduce_tuple_to_single_type(pt->Proc.results); + if (callee_ret == nullptr || !are_types_identical(dst_type, callee_ret)) { + return nullptr; + } + return callee_ft; +} + +// NOTE(bill): @RVO `sret` scan for `x := call(); ...; return x` pattern. +// When matched, `x` alloca will be the sret pointer itself, eliminating +// the copy from `x` to the sret buffer on return. +gb_internal void lb_scan_for_sret_rvo(lbProcedure *p) { + if (p->body == nullptr || p->body->kind != Ast_BlockStmt) { + return; + } + Type *proc_type = p->type; + if (proc_type->Proc.result_count != 1 || proc_type->Proc.results == nullptr) { + return; + } + lbFunctionType *ft = lb_get_function_type(p->module, proc_type); + if (ft->ret.kind != lbArg_Indirect) { + return; + } + + Slice stmts = p->body->BlockStmt.stmts; + if (stmts.count < 2) { + return; + } + + // Last stmt must be `return x` where x is an identifier + Ast *last = stmts[stmts.count - 1]; + if (last->kind != Ast_ReturnStmt) { + return; + } + Slice results = last->ReturnStmt.results; + if (results.count != 1) { + return; + } + Ast *ret_expr = unparen_expr(results[0]); + if (ret_expr->kind != Ast_Ident) { + return; + } + Entity *ret_entity = entity_of_node(ret_expr); + if (ret_entity == nullptr || ret_entity->kind != Entity_Variable) { + return; + } + + Type *ret_type = reduce_tuple_to_single_type(proc_type->Proc.results); + if (ret_type == nullptr || !are_types_identical(ret_entity->type, ret_type)) { + return; + } + + // Walk backwards from the second-to-last stmt to find `x := call()` + // Everything between must be safe (no reassignment of x, no control flow) + i64 decl_index = -1; + for (i64 i = stmts.count - 2; i >= 0; i--) { + Ast *stmt = stmts[i]; + switch (stmt->kind) { + case Ast_ValueDecl: { + AstValueDecl *vd = &stmt->ValueDecl; + if (!vd->is_mutable) { + // constant decl — safe, keep scanning + continue; + } + if (vd->names.count == 1 && vd->values.count == 1) { + Entity *e = entity_of_node(vd->names[0]); + if (e == ret_entity) { + Ast *rhs = unparen_expr(vd->values[0]); + if (rhs->kind == Ast_CallExpr && lb_call_sret_eligible(p, rhs, e->type)) { + decl_index = i; + } + goto done_scanning; + } + } + // Some other mutable ValueDecl — safe as long as it doesn't involve ret_entity + for (Ast *name : vd->names) { + if (entity_of_node(name) == ret_entity) { + goto done_scanning; + } + } + continue; + } + case Ast_ExprStmt: + // Expression statements (reads, function calls) are safe + continue; + case Ast_AssignStmt: { + // Check if any lhs is our entity + for (Ast *lhs : stmt->AssignStmt.lhs) { + Ast *l = unparen_expr(lhs); + if (l->kind == Ast_Ident && entity_of_node(l) == ret_entity) { + goto done_scanning; + } + } + continue; + } + default: + // Control flow or anything else — bail + goto done_scanning; + } + } +done_scanning: + if (decl_index >= 0) { + p->sret_rvo_entity = ret_entity; + } +} + gb_internal void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd) { if (vd == nullptr || vd->is_mutable) { return; @@ -135,7 +260,7 @@ gb_internal lbBranchBlocks lb_lookup_branch_blocks(lbProcedure *p, Ast *ident) { } gb_internal lbTargetList *lb_push_target_list(lbProcedure *p, Ast *label, lbBlock *break_, lbBlock *continue_, lbBlock *fallthrough_) { - lbTargetList *tl = gb_alloc_item(permanent_allocator(), lbTargetList); + lbTargetList *tl = permanent_alloc_item(); tl->prev = p->target_list; tl->break_ = break_; tl->continue_ = continue_; @@ -362,6 +487,14 @@ gb_internal void lb_build_range_indexed(lbProcedure *p, lbValue expr, Type *val_ } break; } + case Type_FixedCapacityDynamicArray: { + if (val_type != nullptr) { + lbValue data = lb_emit_struct_ep(p, expr, 0); + val = lb_emit_load(p, lb_emit_array_ep(p, data, idx)); + } + break; + } + case Type_Slice: { if (val_type != nullptr) { lbValue elem = lb_slice_elem(p, expr); @@ -1241,6 +1374,15 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc lb_build_range_indexed(p, array, val0_type, count_ptr.addr, &val, &key, &loop, &done, rs->reverse); break; } + case Type_FixedCapacityDynamicArray: { + lbValue array = lb_build_addr_ptr(p, expr); + if (is_type_pointer(type_deref(array.type))) { + array = lb_emit_load(p, array); + } + lbValue count_ptr = lb_emit_struct_ep(p, array, 1); + lb_build_range_indexed(p, array, val0_type, count_ptr, &val, &key, &loop, &done, rs->reverse); + break; + } case Type_DynamicArray: { lbValue count_ptr = {}; lbValue array = lb_build_addr_ptr(p, expr); @@ -1565,6 +1707,19 @@ gb_internal void lb_build_unroll_range_stmt(lbProcedure *p, AstUnrollRangeStmt * break; } + case Type_FixedCapacityDynamicArray: { + lbValue array = lb_build_expr(p, expr); + if (!is_type_pointer(array.type)) { + array = lb_address_from_load_or_generate_local(p, array); + } + GB_ASSERT(is_type_pointer(array.type)); + + count_ptr = lb_emit_struct_ep(p, array, 1); + + data_ptr = lb_emit_conv(p, array, alloc_type_pointer(t->FixedCapacityDynamicArray.elem)); + break; + } + case Type_Array: { lbValue array = lb_build_expr(p, expr); count_ptr = lb_add_local_generated(p, t_int, false).addr; @@ -2137,7 +2292,13 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss lbValue on_val = {}; if (switch_kind == TypeSwitch_Union) { Type *ut = base_type(type_deref(parent.type)); - on_val = lb_const_union_tag(m, ut, case_type); + if (is_type_untyped_nil(case_type)) { + GB_ASSERT(type_has_nil(ut)); + saw_nil = true; + on_val = lb_const_int(m, union_tag_type(ut), 0); + } else { + on_val = lb_const_union_tag(m, ut, case_type); + } } else if (switch_kind == TypeSwitch_Any) { if (is_type_untyped_nil(case_type)) { @@ -2432,6 +2593,45 @@ gb_internal void lb_build_return_stmt(lbProcedure *p, Slice const &return if (return_count == 1) { Entity *e = tuple->variables[0]; + + if (LB_ENABLE_BASIC_RVO && res_count == 1 && return_by_pointer) { + Ast *ret_expr = unparen_expr(return_results[0]); + + // NOTE(bill): @RVO for `return call()` in a procedure which uses `sret` and has no defers + // This forwards the sret pointer directly to the callee + if (p->defer_stmts.count == 0) { + if (ret_expr->kind == Ast_CallExpr && lb_call_sret_eligible(p, ret_expr, e->type)) { + lbValue sret_ptr = p->return_ptr.addr; + lb_build_call_expr(p, ret_expr, &sret_ptr); + if (p->type->Proc.has_named_results && e->token.string != "") { + res = lb_emit_load(p, p->return_ptr.addr); + rw_mutex_shared_lock(&p->module->values_mutex); + lbValue found = map_must_get(&p->module->values, e); + rw_mutex_shared_unlock(&p->module->values_mutex); + lb_emit_store(p, found, lb_emit_conv(p, res, e->type)); + } + LLVMBuildRetVoid(p->builder); + return; + } + } + + // NOTE(bill): @RVO for `x := call(); ...; return x` + if (p->sret_rvo_entity != nullptr) { + if (ret_expr->kind == Ast_Ident) { + Entity *ret_e = entity_of_node(ret_expr); + if (ret_e == p->sret_rvo_entity) { + lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr, pos); + LLVMValueRef instr = LLVMGetLastInstruction(p->curr_block->block); + if (!lb_is_instr_terminating(instr)) { + LLVMBuildRetVoid(p->builder); + } + return; + } + } + } + } + + if (res_count == 0) { rw_mutex_shared_lock(&p->module->values_mutex); lbValue found = map_must_get(&p->module->values, e); @@ -2531,33 +2731,26 @@ gb_internal void lb_build_return_stmt(lbProcedure *p, Slice const &return } else { Type *ret_type = p->type->Proc.results; - // NOTE(bill): Doesn't need to be zero because it will be initialized in the loops - if (return_by_pointer) { - res = p->return_ptr.addr; - } else { - res = lb_add_local_generated(p, ret_type, false).addr; - } - auto result_values = slice_make(temporary_allocator(), results.count); - auto result_eps = slice_make(temporary_allocator(), results.count); - for_array(i, results) { result_values[i] = lb_emit_conv(p, results[i], tuple->variables[i]->type); } - for_array(i, results) { - result_eps[i] = lb_emit_struct_ep(p, res, cast(i32)i); - } - for_array(i, result_eps) { - lb_emit_store(p, result_eps[i], result_values[i]); - } if (return_by_pointer) { + res = p->return_ptr.addr; + auto result_eps = slice_make(temporary_allocator(), results.count); + for_array(i, results) { + result_eps[i] = lb_emit_struct_ep(p, res, cast(i32)i); + } + for_array(i, result_eps) { + lb_emit_store(p, result_eps[i], result_values[i]); + } lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr, pos); LLVMBuildRetVoid(p->builder); return; } - res = lb_emit_load(p, res); + res = lb_build_struct_value(p, ret_type, result_values.data, result_values.count); } } lb_build_return_stmt_internal(p, res, pos); @@ -2866,6 +3059,23 @@ gb_internal void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr } gb_internal void lb_build_assign_stmt(lbProcedure *p, AstAssignStmt *as) { if (as->op.kind == Token_Eq) { + if (LB_ENABLE_ADVANCED_RVO) { + // @RVO for single assignments `x = call()` + if (as->lhs.count == 1 && as->rhs.count == 1 && !is_blank_ident(as->lhs[0])) { + Ast *rhs_expr = unparen_expr(as->rhs[0]); + if (rhs_expr->kind == Ast_CallExpr) { + lbAddr lval = lb_build_addr(p, as->lhs[0]); + if (LLVMIsAAllocaInst(lval.addr.value) && lval.kind == lbAddr_Default) { + if (lb_call_sret_eligible(p, rhs_expr, lb_addr_type(lval))) { + lbValue dst = lval.addr; + lb_build_call_expr(p, rhs_expr, &dst); + return; + } + } + } + } + } + auto lvals = array_make(permanent_allocator(), 0, as->lhs.count); for (Ast *lhs : as->lhs) { @@ -3051,6 +3261,27 @@ gb_internal void lb_build_stmt(lbProcedure *p, Ast *node) { } } } else { + if (LB_ENABLE_ADVANCED_RVO) { + // @RVO: for `x := call()` + if (vd->names.count == 1 && values.count == 1 && !is_blank_ident(vd->names[0])) { + Ast *rhs_expr = unparen_expr(values[0]); + Entity *e = entity_of_node(vd->names[0]); + if (rhs_expr->kind == Ast_CallExpr && e != nullptr && lb_call_sret_eligible(p, rhs_expr, e->type)) { + lbValue dst = {}; + if (e == p->sret_rvo_entity) { + dst = p->return_ptr.addr; + lb_add_entity(p->module, e, dst); + lb_add_debug_local_variable(p, dst.value, e->type, e->token); + } else { + lbAddr local = lb_add_local(p, e->type, e, true); + dst = local.addr; + } + lb_build_call_expr(p, rhs_expr, &dst); + break; + } + } + } + auto lvals_preused = slice_make(temporary_allocator(), vd->names.count); auto lvals = slice_make(temporary_allocator(), vd->names.count); auto inits = array_make(temporary_allocator(), 0, lvals.count); diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index 89c671f7d..b41ba2f15 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -69,6 +69,7 @@ gb_internal u64 lb_typeid_kind(lbModule *m, Type *type, u64 id=0) { case Type_SimdVector: kind = Typeid_Simd_Vector; break; case Type_SoaPointer: kind = Typeid_SoaPointer; break; case Type_BitField: kind = Typeid_Bit_Field; break; + case Type_FixedCapacityDynamicArray: kind = Typeid_Fixed_Capacity_Dynamic_Array; break; } return kind; @@ -644,6 +645,21 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals)); break; } + case Type_FixedCapacityDynamicArray: { + tag_type = t_type_info_fixed_capacity_dynamic_array; + + i64 len_offset = type_offset_of(t, 1); + + LLVMValueRef vals[4] = { + get_type_info_ptr(m, t->FixedCapacityDynamicArray.elem), + lb_const_int(m, t_int, type_size_of(t->FixedCapacityDynamicArray.elem)).value, + lb_const_int(m, t_int, t->FixedCapacityDynamicArray.capacity).value, + lb_const_int(m, t_uintptr, len_offset).value, + }; + + variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals)); + break; + } case Type_Slice: { tag_type = t_type_info_slice; @@ -1048,7 +1064,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ i64 tag_index = 0; if (tag_type != nullptr) { - tag_index = union_variant_index(ut, tag_type); + tag_index = union_variant_index_checked(ut, tag_type); } GB_ASSERT(tag_index <= Typeid__COUNT); diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 8a7bced59..3905adfcd 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -438,6 +438,50 @@ gb_internal lbValue lb_emit_try_has_value(lbProcedure *p, lbValue rhs) { return has_value; } +gb_internal bool lb_is_type_trivial(Type *type) { + Type *bt = base_type(type); + if (is_type_integer(bt) || is_type_float(bt) || is_type_boolean(bt) || + is_type_pointer(bt) || is_type_enum(bt) || is_type_rune(bt) || is_type_typeid((bt))) { + return true; + } + return false; +} + +gb_internal bool lb_is_expr_trivial(Ast *e) { + Type *type = default_type(type_of_expr(e)); + if (lb_is_type_trivial(type)) { + e = unparen_expr(e); + TypeAndValue tav = type_and_value_of_expr(e); + if (tav.mode == Addressing_Constant) { + return true; + } + if (e->kind == Ast_Ident) { + return true; + } + if (e->kind == Ast_SelectorExpr) { + Ast *operand = unparen_expr(e->SelectorExpr.expr); + if (operand && operand->kind == Ast_Ident) { + // If the operand is a pointer, thus deferences it, disallow it + Type *ot = type_of_expr(operand); + if (ot == nullptr || is_type_pointer(ot)) { + return false; + } + return true; + } + } + if (e->kind == Ast_UnaryExpr && e->UnaryExpr.op.kind != Token_And) { + Ast *operand = unparen_expr(e->UnaryExpr.expr); + TypeAndValue otav = type_and_value_of_expr(operand); + if (otav.mode == Addressing_Constant) { + return true; + } + if (operand->kind == Ast_Ident) { + return true; + } + } + } + return false; +} gb_internal lbValue lb_emit_or_else(lbProcedure *p, Ast *arg, Ast *else_expr, TypeAndValue const &tv) { if (arg->state_flags & StateFlag_DirectiveWasFalse) { @@ -467,6 +511,13 @@ gb_internal lbValue lb_emit_or_else(lbProcedure *p, Ast *arg, Ast *else_expr, Ty lb_start_block(p, then); return lb_emit_conv(p, lhs, type); } else { + if (lb_is_type_trivial(type) && lb_is_expr_trivial(else_expr)) { + lbValue has_value = lb_emit_try_has_value(p, rhs); + lbValue then_val = lb_emit_conv(p, lhs, type); + lbValue else_val = lb_emit_conv(p, lb_build_expr(p, else_expr), type); + return lb_emit_select(p, has_value, then_val, else_val); + } + LLVMValueRef incoming_values[2] = {}; LLVMBasicBlockRef incoming_blocks[2] = {}; @@ -1061,6 +1112,17 @@ gb_internal i32 lb_convert_struct_index(lbModule *m, Type *t, i32 index) { break; } } + if (t->kind == Type_FixedCapacityDynamicArray) { + switch (index) { + case 0: return 0; // data + case 1: + if (t->FixedCapacityDynamicArray.padding_needed > 0) { + return 2; + } + return 1; + } + } + return index; } @@ -1234,6 +1296,11 @@ gb_internal lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) { case 2: result_type = t_int; break; case 3: result_type = t_allocator; break; } + } else if (is_type_fixed_capacity_dynamic_array(t)) { + switch (index) { + case 0: result_type = alloc_type_array(t->FixedCapacityDynamicArray.elem, t->FixedCapacityDynamicArray.capacity); break; + case 1: result_type = t_int; break; + } } else if (is_type_map(t)) { init_map_internal_debug_types(t); Type *itp = alloc_type_pointer(t_raw_map); @@ -1399,6 +1466,12 @@ gb_internal lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index) { } break; + case Type_FixedCapacityDynamicArray: + switch (index) { + case 0: result_type = alloc_type_array(t->FixedCapacityDynamicArray.elem, t->FixedCapacityDynamicArray.capacity); break; + case 1: result_type = t_int; break; + } + case Type_Map: { init_map_internal_debug_types(t); @@ -1562,16 +1635,27 @@ gb_internal lbValue lb_emit_array_epi(lbProcedure *p, lbValue s, isize index) { Type *t = s.type; GB_ASSERT(is_type_pointer(t)); Type *st = base_type(type_deref(t)); - GB_ASSERT_MSG(is_type_array(st) || is_type_enumerated_array(st) || is_type_matrix(st), "%s", type_to_string(st)); + GB_ASSERT(0 <= index); + if (is_type_fixed_capacity_dynamic_array(st)) { + lbValue data = lb_emit_struct_ep(p, s, 0); + return lb_emit_epi(p, data, index); + } + + GB_ASSERT_MSG(is_type_array(st) || is_type_enumerated_array(st) || is_type_matrix(st), "%s", type_to_string(st)); return lb_emit_epi(p, s, index); } gb_internal lbValue lb_emit_array_epi(lbModule *m, lbValue s, isize index) { Type *t = s.type; GB_ASSERT(is_type_pointer(t)); Type *st = base_type(type_deref(t)); - GB_ASSERT_MSG(is_type_array(st) || is_type_enumerated_array(st) || is_type_matrix(st), "%s", type_to_string(st)); GB_ASSERT(0 <= index); + if (is_type_fixed_capacity_dynamic_array(st)) { + lbValue data = lb_emit_epi(m, s, 0); + return lb_emit_epi(m, data, index); + } + + GB_ASSERT_MSG(is_type_array(st) || is_type_enumerated_array(st) || is_type_matrix(st), "%s", type_to_string(st)); return lb_emit_epi(m, s, index); } @@ -1658,14 +1742,25 @@ gb_internal lbValue lb_emit_matrix_ep(lbProcedure *p, lbValue s, lbValue row, lb return res; } - gb_internal lbValue lb_emit_matrix_ev(lbProcedure *p, lbValue s, isize row, isize column) { - Type *st = base_type(s.type); - GB_ASSERT_MSG(is_type_matrix(st), "%s", type_to_string(st)); - - lbValue value = lb_address_from_load_or_generate_local(p, s); - lbValue ptr = lb_emit_matrix_epi(p, value, row, column); - return lb_emit_load(p, ptr); + Type *t = s.type; + Type *mt = base_type(t); + GB_ASSERT_MSG(is_type_matrix(mt), "%s", type_to_string(mt)); + + isize stride_elems = matrix_type_stride_in_elems(mt); + + isize index = -1; + + if (mt->Matrix.is_row_major) { + index = column + (row * stride_elems); + } else { + index = row + (column * stride_elems); + } + + lbValue res = {}; + res.value = LLVMBuildExtractValue(p->builder, s.value, cast(unsigned)index, ""); + res.type = base_array_type(mt); + return res; } gb_internal void lb_fill_slice(lbProcedure *p, lbAddr const &slice, lbValue base_elem, lbValue len) { @@ -1691,6 +1786,40 @@ gb_internal void lb_fill_string(lbProcedure *p, lbAddr const &string, lbValue ba lb_emit_store(p, lb_emit_struct_ep(p, ptr, 1), len); } +gb_internal lbValue lb_emit_struct_iv(lbProcedure *p, lbValue agg, lbValue field, i32 index) { + Type *t = base_type(agg.type); + i32 mapped_index = lb_convert_struct_index(p->module, t, index); + lbValue res = {}; + res.value = LLVMBuildInsertValue(p->builder, agg.value, field.value, cast(unsigned)mapped_index, ""); + res.type = agg.type; + return res; +} + +gb_internal lbValue lb_build_struct_value(lbProcedure *p, Type *type, lbValue *fields, isize count) { + LLVMTypeRef llvm_type = lb_type(p->module, type); + lbValue agg = {}; + // agg.value = LLVMGetPoison(llvm_type); + agg.value = LLVMConstNull(llvm_type); + agg.type = type; + for (isize i = 0; i < count; i++) { + agg = lb_emit_struct_iv(p, agg, fields[i], cast(i32)i); + } + return agg; +} + +gb_internal lbValue lb_make_slice_value(lbProcedure *p, Type *slice_type, lbValue elem, lbValue len) { + GB_ASSERT(is_type_slice(slice_type)); + lbValue fields[2] = {elem, len}; + return lb_build_struct_value(p, slice_type, fields, 2); +} + +gb_internal lbValue lb_make_string_value(lbProcedure *p, Type *string_type, lbValue elem, lbValue len) { + GB_ASSERT(is_type_string(string_type)); + lbValue fields[2] = {elem, len}; + return lb_build_struct_value(p, string_type, fields, 2); +} + + gb_internal lbValue lb_string_elem(lbProcedure *p, lbValue string) { Type *t = base_type(string.type); if (t->kind == Type_Basic && t->Basic.kind == Basic_string16) { @@ -1749,6 +1878,11 @@ gb_internal lbValue lb_dynamic_array_cap(lbProcedure *p, lbValue da) { return lb_emit_struct_ev(p, da, 2); } +gb_internal lbValue lb_fixed_capacity_dynamic_array_len(lbProcedure *p, lbValue da) { + GB_ASSERT(is_type_fixed_capacity_dynamic_array(da.type)); + return lb_emit_struct_ev(p, da, 1); +} + gb_internal lbValue lb_map_len(lbProcedure *p, lbValue value) { GB_ASSERT_MSG(is_type_map(value.type) || are_types_identical(value.type, t_raw_map), "%s", type_to_string(value.type)); lbValue len = lb_emit_struct_ev(p, value, 1); diff --git a/src/main.cpp b/src/main.cpp index fdafa0cf2..4d68d3d73 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -411,6 +411,8 @@ enum BuildFlagKind { BuildFlag_InternalByValue, BuildFlag_InternalWeakMonomorphization, BuildFlag_InternalLLVMVerification, + BuildFlag_InternalLLVMMem2Reg, + BuildFlag_InternalEnableRVO, BuildFlag_Tilde, @@ -642,6 +644,8 @@ gb_internal bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_InternalByValue, str_lit("internal-by-value"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalWeakMonomorphization, str_lit("internal-weak-monomorphization"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalLLVMVerification, str_lit("internal-ignore-llvm-verification"), BuildFlagParam_None, Command_all); + add_flag(&build_flags, BuildFlag_InternalLLVMMem2Reg, str_lit("internal-llvm-mem2reg"), BuildFlagParam_None, Command_all); + add_flag(&build_flags, BuildFlag_InternalEnableRVO, str_lit("internal-enable-rvo"), BuildFlagParam_None, Command_all); #if ALLOW_TILDE add_flag(&build_flags, BuildFlag_Tilde, str_lit("tilde"), BuildFlagParam_None, Command__does_build); @@ -1082,7 +1086,7 @@ gb_internal bool parse_build_flags(Array args) { break; } - char const *key = string_intern(name); + char const *key = string_intern_cstring(name); if (map_get(&build_context.defined_values, key) != nullptr) { gb_printf_err("Defined constant '%.*s' already exists\n", LIT(name)); @@ -1634,6 +1638,13 @@ gb_internal bool parse_build_flags(Array args) { case BuildFlag_InternalLLVMVerification: build_context.internal_ignore_llvm_verification = true; break; + case BuildFlag_InternalLLVMMem2Reg: + build_context.internal_llvm_mem2reg = true; + break; + case BuildFlag_InternalEnableRVO: + build_context.enable_rvo = true; + break; + case BuildFlag_Tilde: @@ -4001,8 +4012,8 @@ int main(int arg_count, char const **arg_ptr) { init_universal(); // TODO(bill): prevent compiling without a linker - Parser *parser = gb_alloc_item(permanent_allocator(), Parser); - Checker *checker = gb_alloc_item(permanent_allocator(), Checker); + Parser * parser = permanent_alloc_item(); + Checker *checker = permanent_alloc_item(); bool failed_to_cache_parsing = false; MAIN_TIME_SECTION("parse files"); @@ -4140,7 +4151,7 @@ int main(int arg_count, char const **arg_ptr) { } else #endif { - lbGenerator *gen = gb_alloc_item(permanent_allocator(), lbGenerator); + lbGenerator *gen = permanent_alloc_item(); if (!lb_init_generator(gen, checker)) { return 1; } diff --git a/src/name_canonicalization.cpp b/src/name_canonicalization.cpp index efe115c89..1a4ca6439 100644 --- a/src/name_canonicalization.cpp +++ b/src/name_canonicalization.cpp @@ -559,6 +559,8 @@ gb_internal void write_canonical_parent_prefix(TypeWriter *w, Entity *e) { // no prefix return; } + InternedString interned = entity_interned_name(e); + if (e->parent_proc_decl.load(std::memory_order_relaxed)) { Entity *p = e->parent_proc_decl.load(std::memory_order_relaxed)->entity; write_canonical_parent_prefix(w, p); @@ -569,7 +571,7 @@ gb_internal void write_canonical_parent_prefix(TypeWriter *w, Entity *e) { } type_writer_appendc(w, CANONICAL_NAME_SEPARATOR); - } else if (e->pkg && (scope_lookup_current(e->pkg->scope, e->token.string) == e)) { + } else if (e->pkg && (scope_lookup_current(e->pkg->scope, interned) == e)) { type_writer_append(w, e->pkg->name.text, e->pkg->name.len); if (e->pkg->name == "llvm") { type_writer_appendc(w, "$"); @@ -748,8 +750,8 @@ gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) { return; } - type = default_type(type); - GB_ASSERT(!is_type_untyped(type)); + // type = default_type(type); + // GB_ASSERT(!is_type_untyped(type)); switch (type->kind) { case Type_Basic: @@ -788,6 +790,10 @@ gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) { type_writer_appendc(w, "[dynamic]"); write_type_to_canonical_string(w, type->DynamicArray.elem); return; + case Type_FixedCapacityDynamicArray: + type_writer_append_fmt(w, "[dynamic;%lld]", cast(long long)type->FixedCapacityDynamicArray.capacity); + write_type_to_canonical_string(w, type->FixedCapacityDynamicArray.elem); + return; case Type_SimdVector: type_writer_append_fmt(w, "#simd[%lld]", cast(long long)type->SimdVector.count); write_type_to_canonical_string(w, type->SimdVector.elem); diff --git a/src/parser.cpp b/src/parser.cpp index ca81159b4..584979064 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -456,6 +456,12 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) { break; case Ast_DynamicArrayType: n->DynamicArrayType.elem = clone_ast(n->DynamicArrayType.elem, f); + n->DynamicArrayType.tag = clone_ast(n->DynamicArrayType.tag, f); + break; + case Ast_FixedCapacityDynamicArrayType: + n->FixedCapacityDynamicArrayType.elem = clone_ast(n->FixedCapacityDynamicArrayType.elem, f); + n->FixedCapacityDynamicArrayType.capacity = clone_ast(n->FixedCapacityDynamicArrayType.capacity, f); + n->FixedCapacityDynamicArrayType.tag = clone_ast(n->FixedCapacityDynamicArrayType.tag, f); break; case Ast_StructType: n->StructType.fields = clone_ast_array(n->StructType.fields, f); @@ -767,8 +773,9 @@ gb_internal Ast *ast_matrix_index_expr(AstFile *f, Ast *expr, Token open, Token gb_internal Ast *ast_ident(AstFile *f, Token token) { Ast *result = alloc_ast_node(f, Ast_Ident); - result->Ident.token = token; - result->Ident.hash = string_hash(token.string); + result->Ident.token = token; + result->Ident.hash = string_hash(token.string); + result->Ident.interned = string_interner_insert(token.string); return result; } @@ -785,6 +792,7 @@ gb_internal Ast *ast_uninit(AstFile *f, Token token) { gb_internal ExactValue exact_value_from_token(AstFile *f, Token const &token) { String s = token.string; + string_interner_insert(s); switch (token.kind) { case Token_Rune: if (!unquote_string(ast_allocator(f), &s, 0)) { @@ -841,6 +849,7 @@ gb_internal Ast *ast_basic_directive(AstFile *f, Token token, Token name) { Ast *result = alloc_ast_node(f, Ast_BasicDirective); result->BasicDirective.token = token; result->BasicDirective.name = name; + string_interner_insert(name.string); if (string_starts_with(name.string, str_lit("load"))) { f->seen_load_directive_count++; } @@ -1254,6 +1263,14 @@ gb_internal Ast *ast_dynamic_array_type(AstFile *f, Token token, Ast *elem) { return result; } +gb_internal Ast *ast_fixed_capacity_dynamic_array_type(AstFile *f, Token token, Ast *capacity, Ast *elem) { + Ast *result = alloc_ast_node(f, Ast_FixedCapacityDynamicArrayType); + result->FixedCapacityDynamicArrayType.token = token; + result->FixedCapacityDynamicArrayType.capacity = capacity; + result->FixedCapacityDynamicArrayType.elem = elem; + return result; +} + gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice fields, isize field_count, Ast *polymorphic_params, bool is_packed, bool is_raw_union, bool is_all_or_none, bool is_simple, Ast *align, Ast *min_field_align, Ast *max_field_align, @@ -1462,7 +1479,7 @@ gb_internal CommentGroup *consume_comment_group(AstFile *f, isize n, isize *end_ CommentGroup *comments = nullptr; if (list.count > 0) { - comments = gb_alloc_item(permanent_allocator(), CommentGroup); + comments = permanent_alloc_item(); comments->list = slice_from_array(list); array_add(&f->comments, comments); } @@ -2470,9 +2487,10 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { Ast *original_type = parse_type(f); Ast *type = unparen_expr(original_type); switch (type->kind) { - case Ast_ArrayType: type->ArrayType.tag = tag; break; - case Ast_DynamicArrayType: type->DynamicArrayType.tag = tag; break; - case Ast_PointerType: type->PointerType.tag = tag; break; + case Ast_ArrayType: type->ArrayType.tag = tag; break; + case Ast_DynamicArrayType: type->DynamicArrayType.tag = tag; break; + case Ast_PointerType: type->PointerType.tag = tag; break; + case Ast_FixedCapacityDynamicArrayType: type->FixedCapacityDynamicArrayType.tag = tag; break; default: syntax_error(type, "Expected an array or pointer type after #%.*s, got %.*s", LIT(name.string), LIT(ast_strings[type->kind])); break; @@ -2702,8 +2720,25 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { } else if (f->curr_token.kind == Token_Question) { count_expr = ast_unary_expr(f, expect_token(f, Token_Question), nullptr); } else if (allow_token(f, Token_dynamic)) { + Ast *capacity = nullptr; + if (f->curr_token.kind == Token_Semicolon && f->curr_token.string == ";") { + expect_token(f, Token_Semicolon); + capacity = parse_expr(f, false); + } else if (allow_token(f, Token_Comma) || allow_token(f, Token_Semicolon)) { + String p = token_to_string(f->prev_token); + syntax_error(token_end_of_line(f, f->prev_token), "Expected a semicolon, got a %.*s", LIT(p)); + + capacity = parse_expr(f, false); + } expect_token(f, Token_CloseBracket); - return ast_dynamic_array_type(f, token, parse_type(f)); + + Ast *elem = parse_type(f); + + if (capacity == nullptr) { + return ast_dynamic_array_type(f, token, elem); + } else { + return ast_fixed_capacity_dynamic_array_type(f, token, capacity, elem); + } } else if (f->curr_token.kind != Token_CloseBracket) { f->expr_level++; count_expr = parse_expr(f, false); @@ -3186,6 +3221,7 @@ gb_internal bool is_literal_type(Ast *node) { case Ast_StructType: case Ast_UnionType: case Ast_EnumType: + case Ast_FixedCapacityDynamicArrayType: case Ast_DynamicArrayType: case Ast_MapType: case Ast_BitSetType: @@ -5118,7 +5154,7 @@ gb_internal Ast *parse_import_decl(AstFile *f, ImportDeclKind kind) { } if (file_path.string == "\".\"") { - syntax_error(import_name, "Cannot cyclicly import packages"); + // syntax_error(import_name, "Cannot cyclicly import packages"); } expect_semicolon(f); @@ -5764,7 +5800,7 @@ gb_internal WORKER_TASK_PROC(parser_worker_proc) { ParserWorkerData *wd = cast(ParserWorkerData *)data; ParseFileError err = process_imported_file(wd->parser, wd->imported_file); if (err != ParseFile_None) { - auto *node = gb_alloc_item(permanent_allocator(), ParseFileErrorNode); + auto *node = permanent_alloc_item(); node->err = err; MUTEX_GUARD_BLOCK(&wd->parser->file_error_mutex) { @@ -5784,7 +5820,7 @@ gb_internal WORKER_TASK_PROC(parser_worker_proc) { gb_internal void parser_add_file_to_process(Parser *p, AstPackage *pkg, FileInfo fi, TokenPos pos) { ImportedFile f = {pkg, fi, pos, p->file_to_process_count++}; f.pos.file_id = cast(i32)(f.index+1); - auto wd = gb_alloc_item(permanent_allocator(), ParserWorkerData); + auto wd = permanent_alloc_item(); wd->parser = p; wd->imported_file = f; thread_pool_add_task(parser_worker_proc, wd); @@ -5821,7 +5857,7 @@ gb_internal void parser_add_foreign_file_to_process(Parser *p, AstPackage *pkg, // TODO(bill): Use a better allocator ImportedFile f = {pkg, fi, pos, p->file_to_process_count++}; f.pos.file_id = cast(i32)(f.index+1); - auto wd = gb_alloc_item(permanent_allocator(), ForeignFileWorkerData); + auto wd = permanent_alloc_item(); wd->parser = p; wd->imported_file = f; wd->foreign_kind = kind; @@ -5841,7 +5877,7 @@ gb_internal AstPackage *try_add_import_path(Parser *p, String path, String const path = copy_string(permanent_allocator(), path); - AstPackage *pkg = gb_alloc_item(permanent_allocator(), AstPackage); + AstPackage *pkg = permanent_alloc_item(); pkg->kind = kind; pkg->fullpath = path; array_init(&pkg->files, permanent_allocator()); @@ -6847,7 +6883,7 @@ gb_internal ParseFileError process_imported_file(Parser *p, ImportedFile importe FileInfo fi = imported_file.fi; TokenPos pos = imported_file.pos; - AstFile *file = gb_alloc_item(permanent_allocator(), AstFile); + AstFile *file = permanent_alloc_item(); file->pkg = pkg; file->id = cast(i32)(imported_file.index+1); TokenPos err_pos = {0}; diff --git a/src/parser.hpp b/src/parser.hpp index d3527285d..149cf6330 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -424,9 +424,10 @@ struct AstSplitArgs { #define AST_KINDS \ AST_KIND(Ident, "identifier", struct { \ - Token token; \ - std::atomic entity; \ - u32 hash; \ + Token token; \ + std::atomic entity; \ + u32 hash; \ + InternedString interned; \ }) \ AST_KIND(Implicit, "implicit", Token) \ AST_KIND(Uninit, "uninitialized value", Token) \ @@ -764,8 +765,14 @@ AST_KIND(_TypeBegin, "", bool) \ }) \ AST_KIND(DynamicArrayType, "dynamic array type", struct { \ Token token; \ - Ast *elem; \ - Ast *tag; \ + Ast *elem; \ + Ast *tag; \ + }) \ + AST_KIND(FixedCapacityDynamicArrayType, "fixed capacity dynamic array type", struct { \ + Token token; \ + Ast *capacity; \ + Ast *elem; \ + Ast *tag; \ }) \ AST_KIND(StructType, "struct type", struct { \ Scope *scope; \ diff --git a/src/parser_pos.cpp b/src/parser_pos.cpp index 1ffd3a82f..1d2b5090a 100644 --- a/src/parser_pos.cpp +++ b/src/parser_pos.cpp @@ -107,6 +107,7 @@ gb_internal Token ast_token(Ast *node) { case Ast_MultiPointerType: return node->MultiPointerType.token; case Ast_ArrayType: return node->ArrayType.token; case Ast_DynamicArrayType: return node->DynamicArrayType.token; + case Ast_FixedCapacityDynamicArrayType: return node->FixedCapacityDynamicArrayType.token; case Ast_StructType: return node->StructType.token; case Ast_UnionType: return node->UnionType.token; case Ast_EnumType: return node->EnumType.token; @@ -342,6 +343,7 @@ Token ast_end_token(Ast *node) { case Ast_MultiPointerType: return ast_end_token(node->MultiPointerType.type); case Ast_ArrayType: return ast_end_token(node->ArrayType.elem); case Ast_DynamicArrayType: return ast_end_token(node->DynamicArrayType.elem); + case Ast_FixedCapacityDynamicArrayType: return ast_end_token(node->FixedCapacityDynamicArrayType.elem); case Ast_StructType: if (node->StructType.fields.count > 0) { return ast_end_token(node->StructType.fields[node->StructType.fields.count-1]); diff --git a/src/ptr_set.cpp b/src/ptr_set.cpp index 5b1d2cc19..3d30bb35e 100644 --- a/src/ptr_set.cpp +++ b/src/ptr_set.cpp @@ -1,11 +1,14 @@ +enum {PTR_SET_INLINE_CAP = 16}; + template struct PtrSet { static_assert(TypeIsPointer::value || TypeIsPtrSizedInteger::value, "PtrSet::T must be a pointer"); static constexpr uintptr TOMBSTONE = ~(uintptr)(0ull); - T * keys; - usize count; - usize capacity; + T * keys; + u32 count; + u32 capacity; + T inline_keys[PTR_SET_INLINE_CAP]; }; template gb_internal void ptr_set_init (PtrSet *s, isize capacity = 16); @@ -19,24 +22,31 @@ template gb_internal void ptr_set_clear (PtrSet *s); #define FOR_PTR_SET(element, set_) for (auto *it = &(set_).keys[0], element = it ? *it : nullptr; (set_).keys != nullptr && it < &(set_).keys[(set_).capacity]; it++) if (element = *it, (*it != nullptr && *it != cast(void *)~(uintptr)(0ull))) gb_internal gbAllocator ptr_set_allocator(void) { - return heap_allocator(); + // return heap_allocator(); + return permanent_allocator(); } template gb_internal void ptr_set_init(PtrSet *s, isize capacity) { GB_ASSERT(s->keys == nullptr); if (capacity != 0) { - capacity = next_pow2_isize(gb_max(16, capacity)); - s->keys = gb_alloc_array(ptr_set_allocator(), T, capacity); + capacity = next_pow2_isize(gb_max(PTR_SET_INLINE_CAP, capacity)); + if (capacity > PTR_SET_INLINE_CAP) { + s->keys = gb_alloc_array(ptr_set_allocator(), T, capacity); + } else { + s->keys = s->inline_keys; + } // This memory will be zeroed, no need to explicitly zero it } s->count = 0; - s->capacity = capacity; + s->capacity = cast(u32)capacity; } template gb_internal void ptr_set_destroy(PtrSet *s) { - gb_free(ptr_set_allocator(), s->keys); + if (s->keys != s->inline_keys) { + gb_free(ptr_set_allocator(), s->keys); + } s->keys = nullptr; s->count = 0; s->capacity = 0; @@ -46,16 +56,10 @@ template gb_internal isize ptr_set__find(PtrSet *s, T ptr) { GB_ASSERT(ptr != 0); if (s->count != 0) { - #if 0 - for (usize i = 0; i < s->capacity; i++) { - if (s->keys[i] == ptr) { - return i; - } - } - #else u32 hash = ptr_map_hash_key(ptr); usize mask = s->capacity-1; usize hash_index = cast(usize)hash & mask; + for (usize i = 0; i < s->capacity; i++) { T key = s->keys[hash_index]; if (key == ptr) { @@ -65,14 +69,14 @@ gb_internal isize ptr_set__find(PtrSet *s, T ptr) { } hash_index = (hash_index+1)&mask; } - #endif } return -1; } template gb_internal bool ptr_set__full(PtrSet *s) { - return 0.75f * s->capacity <= s->count; + usize grow_at = s->capacity - (s->capacity>>2); + return s->count >= grow_at; } template @@ -122,10 +126,11 @@ gb_internal bool ptr_set_update(PtrSet *s, T ptr) { // returns true if it pre usize hash_index = (cast(usize)hash) & mask; GB_ASSERT(hash_index < s->capacity); for (usize i = 0; i < s->capacity; i++) { - T *key = &s->keys[hash_index]; - GB_ASSERT(*key != ptr); - if (*key == (T)PtrSet::TOMBSTONE || *key == 0) { - *key = ptr; + T *key_ptr = &s->keys[hash_index]; + T key = *key_ptr; + GB_ASSERT(key != ptr); + if (key == (T)PtrSet::TOMBSTONE || key == 0) { + *key_ptr = ptr; s->count++; return false; } @@ -160,10 +165,11 @@ gb_internal bool ptr_set_update_with_mutex(PtrSet *s, T ptr, RWSpinLock *m) { usize hash_index = (cast(usize)hash) & mask; GB_ASSERT(hash_index < s->capacity); for (usize i = 0; i < s->capacity; i++) { - T *key = &s->keys[hash_index]; - GB_ASSERT(*key != ptr); - if (*key == (T)PtrSet::TOMBSTONE || *key == 0) { - *key = ptr; + T *key_ptr = &s->keys[hash_index]; + T key = *key_ptr; + GB_ASSERT(key != ptr); + if (key == (T)PtrSet::TOMBSTONE || key == 0) { + *key_ptr = ptr; s->count++; return false; } @@ -184,11 +190,36 @@ gb_internal T ptr_set_add(PtrSet *s, T ptr) { template gb_internal void ptr_set_remove(PtrSet *s, T ptr) { isize index = ptr_set__find(s, ptr); - if (index >= 0) { - GB_ASSERT(s->count > 0); - s->keys[index] = (T)PtrSet::TOMBSTONE; - s->count--; + if (index < 0) { + return; } + +#if 0 + u32 mask = s->capacity-1; + u32 i = cast(u32)index; + s->count -= 1; + + for (;;) { + u32 next = (i + 1) & mask; + T key = s->keys[next]; + if (key == 0) { + break; + } + + u32 natural = ptr_map_hash_key(key) & mask; + + if (((next - natural) & mask) == 0) { + break; + } + s->keys[i] = key; + i = next; + } + s->keys[i] = 0; +#else + GB_ASSERT(s->count > 0); + s->keys[index] = (T)PtrSet::TOMBSTONE; + s->count--; +#endif } template diff --git a/src/queue.cpp b/src/queue.cpp index 82f82f3e1..563fbe69e 100644 --- a/src/queue.cpp +++ b/src/queue.cpp @@ -37,7 +37,7 @@ gb_internal void mpsc_destroy(MPSCQueue *q) { template gb_internal MPSCNode *mpsc_alloc_node(MPSCQueue *q, T const &value) { // auto new_node = gb_alloc_item(heap_allocator(), MPSCNode); - auto new_node = gb_alloc_item(permanent_allocator(), MPSCNode); + auto new_node = permanent_alloc_item >(); new_node->value = value; return new_node; } diff --git a/src/string.cpp b/src/string.cpp index c26457acf..09bce205e 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -182,8 +182,9 @@ gb_internal isize string_index_byte(String const &s, u8 x) { } gb_internal gb_inline bool str_eq(String const &a, String const &b) { - if (a.len != b.len) return false; - if (a.len == 0) return true; + if (a.len != b.len) return false; + if (a.len == 0) return true; + if (a.text == b.text) return true; return memcmp(a.text, b.text, a.len) == 0; } gb_internal gb_inline bool str_ne(String const &a, String const &b) { return !str_eq(a, b); } diff --git a/src/string_interner.cpp b/src/string_interner.cpp new file mode 100644 index 000000000..660dbc61d --- /dev/null +++ b/src/string_interner.cpp @@ -0,0 +1,231 @@ +#define STRING_INTERNER_CELL_WIDTH 8 +#define STRING_INTERNER_MUTEX_STRIPE_COUNT 1024 +#define STRING_INTERNER_MUTEX_STRIPE_MASK (STRING_INTERNER_MUTEX_STRIPE_COUNT - 1) +#define STRING_INTERNER_THREAD_LOCAL_SIZE (1024 * 1024 * 2) +#define STRING_INTERN_CACHE_LINE (2*GB_CACHE_LINE_SIZE) + +struct InternedString { + u32 value; + bool operator==(InternedString other) const { + return this->value == other.value; + } + + String string() const; + char const *cstring() const; + u32 hash() const; + + bool is_blank() const; +}; +struct alignas(STRING_INTERN_CACHE_LINE) StringInternCell { + std::atomic hashes [STRING_INTERNER_CELL_WIDTH]; + InternedString offsets[STRING_INTERNER_CELL_WIDTH]; + std::atomic next; +}; + +struct alignas(STRING_INTERN_CACHE_LINE) PaddedMutex { + BlockingMutex m; +}; + +struct alignas(STRING_INTERN_CACHE_LINE) PaddedI64 { + std::atomic value; +}; + +struct StringInterner { + StringInternCell *cells; + u64 cell_mask; + PaddedMutex mutexes[STRING_INTERNER_MUTEX_STRIPE_COUNT]; + StaticArena arena; + PaddedMutex arena_mutex; + bool track_count; + PaddedI64 count; +}; + +gb_internal StringInterner *string_interner_create(); +gb_internal InternedString string_interner_insert(String str, u32 hash=0, u32 *new_hash_=nullptr); +gb_internal String string_interner_load(InternedString interned); + +gb_global StringInterner *g_string_interner; +gb_global InternedString g_interned_blank_ident; + +struct StringInternerThreadLocalArena { + u8 *data; + u64 cursor; +}; +gb_thread_local gb_global StringInternerThreadLocalArena g_interner_arena; + +gb_internal void string_interner_thread_local_arena_init(StringInternerThreadLocalArena *tl_arena); +gb_internal void *string_interner_thread_local_arena_alloc(StringInternerThreadLocalArena *tl_arena, isize size, isize alignment); + +gb_internal void init_string_interner() { + StaticArena arena = {}; + static_arena_init(&arena, 1<<30, STATIC_ARENA_DEFAULT_COMMIT_BLOCK_SIZE); + + StringInterner *interner = cast(StringInterner *)static_arena_alloc(&arena, gb_size_of(StringInterner), STRING_INTERN_CACHE_LINE); + interner->arena = arena; + u64 cell_size = 1llu << 17llu; + u64 cell_mask = cell_size - 1; + interner->cell_mask = cell_mask; + interner->cells = cast(StringInternCell *)static_arena_alloc(&interner->arena, cell_size * gb_size_of(StringInternCell), STRING_INTERN_CACHE_LINE); + interner->track_count = false; + + g_string_interner = interner; + + g_interned_blank_ident = string_interner_insert(str_lit("_")); +} + +gb_internal String string_interner_load(InternedString interned) { + StringInterner* interner = g_string_interner; + if (interned.value == 0) { + return {}; + } + u8 *base = cast(u8 *)interner + interned.value; + u32 str_len = *cast(u32 *)base; + u8 *text = base + 4; + String str = { text, str_len }; + return str; +} + +gb_internal char const *string_interner_load_cstring(InternedString interned) { + StringInterner* interner = g_string_interner; + if (interned.value == 0) { + return ""; + } + u8 *base = cast(u8 *)interner + interned.value; + // u32 str_len = *cast(u32 *)base; + u8 *text = base + 4; + return cast(char const *)text; +} + +String InternedString::string() const { + return string_interner_load(*this); +} +char const *InternedString::cstring() const { + return string_interner_load_cstring(*this); +} +u32 InternedString::hash() const { + String s = string_interner_load(*this); + return string_hash(s); +} +bool InternedString::is_blank() const { + return this->value == g_interned_blank_ident.value; +} + +gb_internal InternedString string_interner_insert(String str, u32 hash, u32 *new_hash_) { + StringInterner* interner = g_string_interner; + if (str.len == 0) { + if (new_hash_) *new_hash_ = string_hash(String{}); + return {}; + } + + if (hash == 0) { + hash = string_hash(str); + } + if (new_hash_) *new_hash_ = hash; + + u64 cell_idx = hash & interner->cell_mask; + StringInternCell *cell = &interner->cells[cell_idx]; + while (true) { + StringInternCell *next = cell->next.load(std::memory_order_acquire); + + for (i32 i = 0; i < STRING_INTERNER_CELL_WIDTH; i += 1) { + if (cell->hashes[i].load(std::memory_order_acquire) == hash) { + String to_compare = string_interner_load(cell->offsets[i]); + if (to_compare == str) { + return cell->offsets[i]; + } + } + } + if (next == nullptr) { + break; + } + cell = next; + } + + u64 mutex_cell = cell_idx & STRING_INTERNER_MUTEX_STRIPE_MASK; + PaddedMutex* m = &interner->mutexes[mutex_cell]; + MUTEX_GUARD(&m->m); + + StringInternCell *load_cell = nullptr; + while (cell) { + for (i32 i = 0; i < STRING_INTERNER_CELL_WIDTH; i += 1) { + if (cell->hashes[i].load(std::memory_order_relaxed) == hash) { + // string check + String to_compare = string_interner_load(cell->offsets[i]); + if (to_compare == str) { + return cell->offsets[i]; + } + } + } + load_cell = cell; + cell = cell->next.load(std::memory_order_relaxed); + } + + u64 data_to_allocate = 4 + str.len + 1; + u8 *data = cast(u8 *)string_interner_thread_local_arena_alloc(&g_interner_arena, data_to_allocate, 8); + u32 str_len = cast(u32)str.len; + gb_memcopy(data, &str_len, 4); + gb_memcopy(&data[4], str.text, str_len); + data[4+str_len] = 0; + InternedString offset = { cast(u32)(cast(u8 *)data - cast(u8 *)interner) }; + + for (i32 i = 0; i < STRING_INTERNER_CELL_WIDTH; i += 1) { + if (load_cell->hashes[i].load(std::memory_order_relaxed) == 0) { + load_cell->offsets[i] = offset; + load_cell->hashes[i].store(hash, std::memory_order_release); + if (interner->track_count) { + interner->count.value.fetch_add(1, std::memory_order_relaxed); + } + return offset; + } + } + + StringInternCell *new_cell = cast(StringInternCell *)string_interner_thread_local_arena_alloc(&g_interner_arena, gb_size_of(StringInternCell), STRING_INTERN_CACHE_LINE); + new_cell->offsets[0] = offset; + new_cell->hashes[0].store(hash, std::memory_order_relaxed); + load_cell->next.store(new_cell, std::memory_order_release); + + if (interner->track_count) { + interner->count.value.fetch_add(1, std::memory_order_relaxed); + } + + return offset; +} + +gb_internal char const *string_intern_cstring(String str, u32 *hash_=nullptr) { + InternedString i = string_interner_insert(str, 0, hash_); + return string_interner_load_cstring(i); +} + + +gb_internal String string_intern_string(String str, u32 *hash_=nullptr) { + InternedString i = string_interner_insert(str, 0, hash_); + return string_interner_load(i); +} + + + + +gb_internal void string_interner_thread_local_arena_init(StringInternerThreadLocalArena *tl_arena) { + *tl_arena = { + nullptr, + STRING_INTERNER_THREAD_LOCAL_SIZE, + }; +} + +gb_internal void *string_interner_thread_local_arena_alloc(StringInternerThreadLocalArena *tl_arena, isize size, isize alignment) { + if (tl_arena->data == nullptr) { + tl_arena->cursor = STRING_INTERNER_THREAD_LOCAL_SIZE; + } + isize new_head = align_formula_isize(tl_arena->cursor, alignment); + isize cursor = new_head + size; + if (cursor > STRING_INTERNER_THREAD_LOCAL_SIZE) { + mutex_lock(&g_string_interner->arena_mutex.m); + tl_arena->data = cast(u8 *)static_arena_alloc(&g_string_interner->arena, STRING_INTERNER_THREAD_LOCAL_SIZE, 4096); + tl_arena->cursor = 0; + mutex_unlock(&g_string_interner->arena_mutex.m); + return string_interner_thread_local_arena_alloc(tl_arena, size, alignment); + } + u8 *return_head = tl_arena->data + new_head; + tl_arena->cursor = cursor; + return return_head; +} \ No newline at end of file diff --git a/src/threading.cpp b/src/threading.cpp index d8ae321f5..5dff13d2e 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -1201,3 +1201,41 @@ void futex_wait(Futex *f, Footex val) { #if defined(GB_SYSTEM_WINDOWS) #pragma warning(pop) #endif + + + +template +struct AtomicFreelist { + T value; + std::atomic *> next; +}; + +template +AtomicFreelist *atomic_freelist_get(std::atomic *> &head) { + AtomicFreelist *elem = nullptr; + + for (;;) { + elem = head.load(std::memory_order_acquire); + if (elem == nullptr) { + return nullptr; + } + + if (head.compare_exchange_weak(elem, elem->next.load(std::memory_order_acquire), std::memory_order_acquire, std::memory_order_relaxed)) { + elem->next.store(nullptr, std::memory_order_relaxed); + return elem; + } + + } +} + +template +void atomic_freelist_put(std::atomic *> &head_list, AtomicFreelist *elem) { + for (;;) { + auto *head = head_list.load(std::memory_order_relaxed); + elem->next.store(head, std::memory_order_relaxed); + if (head_list.compare_exchange_weak(head, elem, std::memory_order_release, std::memory_order_relaxed)) { + return; + } + + } +} \ No newline at end of file diff --git a/src/types.cpp b/src/types.cpp index b11e95452..f4b708e57 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -226,6 +226,7 @@ struct TypeNamed { TYPE_KIND(Generic, struct { \ i64 id; \ String name; \ + InternedString interned_name; \ Type * specialized; \ Scope * scope; \ Entity *entity; \ @@ -248,6 +249,12 @@ struct TypeNamed { }) \ TYPE_KIND(Slice, struct { Type *elem; }) \ TYPE_KIND(DynamicArray, struct { Type *elem; }) \ + TYPE_KIND(FixedCapacityDynamicArray, struct { \ + i64 capacity; \ + Type *generic_capacity; \ + Type *elem; \ + i64 padding_needed; /*-1 if unknown*/ \ + }) \ TYPE_KIND(Map, struct { \ Type *key; \ Type *value; \ @@ -378,6 +385,7 @@ enum Typeid_Kind : u8 { Typeid_Matrix, Typeid_SoaPointer, Typeid_Bit_Field, + Typeid_Fixed_Capacity_Dynamic_Array, Typeid__COUNT @@ -698,6 +706,7 @@ gb_global Type *t_type_info_simd_vector = nullptr; gb_global Type *t_type_info_matrix = nullptr; gb_global Type *t_type_info_soa_pointer = nullptr; gb_global Type *t_type_info_bit_field = nullptr; +gb_global Type *t_type_info_fixed_capacity_dynamic_array = nullptr; gb_global Type *t_type_info_named_ptr = nullptr; gb_global Type *t_type_info_integer_ptr = nullptr; @@ -726,6 +735,7 @@ gb_global Type *t_type_info_simd_vector_ptr = nullptr; gb_global Type *t_type_info_matrix_ptr = nullptr; gb_global Type *t_type_info_soa_pointer_ptr = nullptr; gb_global Type *t_type_info_bit_field_ptr = nullptr; +gb_global Type *t_type_info_fixed_capacity_dynamic_array_ptr = nullptr; gb_global Type *t_allocator = nullptr; gb_global Type *t_allocator_ptr = nullptr; @@ -969,10 +979,7 @@ gb_internal void set_base_type(Type *t, Type *base) { gb_internal Type *alloc_type(TypeKind kind) { - // gbAllocator a = heap_allocator(); - gbAllocator a = permanent_allocator(); - Type *t = gb_alloc_item(a, Type); - gb_zero_item(t); + Type *t = permanent_alloc_item(); t->kind = kind; t->cached_size = -1; t->cached_align = -1; @@ -980,10 +987,11 @@ gb_internal Type *alloc_type(TypeKind kind) { } -gb_internal Type *alloc_type_generic(Scope *scope, i64 id, String name, Type *specialized) { +gb_internal Type *alloc_type_generic(Scope *scope, i64 id, InternedString interned_name, Type *specialized) { Type *t = alloc_type(Type_Generic); t->Generic.id = id; - t->Generic.name = name; + t->Generic.name = interned_name.string(); + t->Generic.interned_name = interned_name; t->Generic.specialized = specialized; t->Generic.scope = scope; return t; @@ -1067,8 +1075,8 @@ gb_internal Type *alloc_type_enumerated_array(Type *elem, Type *index, ExactValu Type *t = alloc_type(Type_EnumeratedArray); t->EnumeratedArray.elem = elem; t->EnumeratedArray.index = index; - t->EnumeratedArray.min_value = gb_alloc_item(permanent_allocator(), ExactValue); - t->EnumeratedArray.max_value = gb_alloc_item(permanent_allocator(), ExactValue); + t->EnumeratedArray.min_value = permanent_alloc_item(); + t->EnumeratedArray.max_value = permanent_alloc_item(); gb_memmove(t->EnumeratedArray.min_value, min_value, gb_size_of(ExactValue)); gb_memmove(t->EnumeratedArray.max_value, max_value, gb_size_of(ExactValue)); t->EnumeratedArray.op = op; @@ -1094,6 +1102,23 @@ gb_internal Type *alloc_type_dynamic_array(Type *elem) { return t; } +gb_internal Type *alloc_type_fixed_capacity_dynamic_array(Type *elem, i64 capacity, Type *generic_capacity = nullptr) { + if (generic_capacity != nullptr) { + Type *t = alloc_type(Type_FixedCapacityDynamicArray); + t->FixedCapacityDynamicArray.elem = elem; + t->FixedCapacityDynamicArray.capacity = capacity; + t->FixedCapacityDynamicArray.generic_capacity = generic_capacity; + t->FixedCapacityDynamicArray.padding_needed = -1; + return t; + } + Type *t = alloc_type(Type_FixedCapacityDynamicArray); + t->FixedCapacityDynamicArray.elem = elem; + t->FixedCapacityDynamicArray.capacity = capacity; + t->FixedCapacityDynamicArray.padding_needed = -1; + return t; +} + + gb_internal Type *alloc_type_struct() { Type *t = alloc_type(Type_Struct); @@ -1115,8 +1140,8 @@ gb_internal Type *alloc_type_union() { gb_internal Type *alloc_type_enum() { Type *t = alloc_type(Type_Enum); - t->Enum.min_value = gb_alloc_item(permanent_allocator(), ExactValue); - t->Enum.max_value = gb_alloc_item(permanent_allocator(), ExactValue); + t->Enum.min_value = permanent_alloc_item(); + t->Enum.max_value = permanent_alloc_item(); return t; } @@ -1658,6 +1683,11 @@ gb_internal bool is_type_dynamic_array(Type *t) { if (t == nullptr) { return false; } return t->kind == Type_DynamicArray; } +gb_internal bool is_type_fixed_capacity_dynamic_array(Type *t) { + t = base_type(t); + if (t == nullptr) { return false; } + return t->kind == Type_FixedCapacityDynamicArray; +} gb_internal bool is_type_slice(Type *t) { t = base_type(t); if (t == nullptr) { return false; } @@ -1687,6 +1717,8 @@ gb_internal Type *base_array_type(Type *t) { return bt->EnumeratedArray.elem; } else if (is_type_simd_vector(bt)) { return bt->SimdVector.elem; + } else if (is_type_fixed_capacity_dynamic_array(bt)) { + return bt->FixedCapacityDynamicArray.elem; } else if (is_type_matrix(bt)) { return bt->Matrix.elem; } @@ -1702,6 +1734,8 @@ gb_internal Type *base_any_array_type(Type *t) { return bt->Slice.elem; } else if (is_type_dynamic_array(bt)) { return bt->DynamicArray.elem; + } else if (is_type_fixed_capacity_dynamic_array(bt)) { + return bt->FixedCapacityDynamicArray.elem; } else if (is_type_enumerated_array(bt)) { return bt->EnumeratedArray.elem; } else if (is_type_simd_vector(bt)) { @@ -2234,6 +2268,7 @@ gb_internal bool is_type_indexable(Type *t) { case Type_Array: case Type_Slice: case Type_DynamicArray: + case Type_FixedCapacityDynamicArray: case Type_Map: return true; case Type_MultiPointer: @@ -2254,6 +2289,7 @@ gb_internal bool is_type_sliceable(Type *t) { case Type_Array: case Type_Slice: case Type_DynamicArray: + case Type_FixedCapacityDynamicArray: return true; case Type_EnumeratedArray: return false; @@ -2394,6 +2430,11 @@ gb_internal bool is_type_polymorphic(Type *t, bool or_specialized=false) { return is_type_polymorphic(t->SimdVector.elem, or_specialized); case Type_DynamicArray: return is_type_polymorphic(t->DynamicArray.elem, or_specialized); + case Type_FixedCapacityDynamicArray: + if (t->FixedCapacityDynamicArray.generic_capacity != nullptr) { + return true; + } + return is_type_polymorphic(t->FixedCapacityDynamicArray.elem, or_specialized); case Type_Slice: return is_type_polymorphic(t->Slice.elem, or_specialized); @@ -2515,6 +2556,11 @@ gb_internal bool type_has_nil(Type *t) { case Type_DynamicArray: case Type_Map: return true; + + case Type_FixedCapacityDynamicArray: + // it's like a normal array, so no, similar to `#soa[N]T + return false; + case Type_Union: return t->Union.kind != UnionType_no_nil; case Type_Struct: @@ -2642,6 +2688,9 @@ gb_internal bool is_type_comparable(Type *t) { case Type_Matrix: return is_type_comparable(t->Matrix.elem); + case Type_FixedCapacityDynamicArray: + return false; + case Type_BitSet: return true; @@ -2688,6 +2737,10 @@ gb_internal bool is_type_simple_compare(Type *t) { case Type_EnumeratedArray: return is_type_simple_compare(t->EnumeratedArray.elem); + case Type_FixedCapacityDynamicArray: + return false; + // return is_type_simple_compare(t->FixedCapacityDynamicArray.elem); + case Type_Basic: if (t->Basic.flags & BasicFlag_SimpleCompare) { return true; @@ -2758,6 +2811,10 @@ gb_internal bool is_type_nearly_simple_compare(Type *t) { case Type_EnumeratedArray: return is_type_nearly_simple_compare(t->EnumeratedArray.elem); + case Type_FixedCapacityDynamicArray: + return false; + // return is_type_nearly_simple_compare(t->FixedCapacityDynamicArray.elem); + case Type_Basic: if (t->Basic.flags & (BasicFlag_SimpleCompare|BasicFlag_Numeric)) { return true; @@ -2838,6 +2895,7 @@ gb_internal bool is_type_load_safe(Type *type) { case Type_DynamicArray: case Type_Proc: case Type_SoaPointer: + case Type_FixedCapacityDynamicArray: return false; case Type_Enum: @@ -2992,6 +3050,10 @@ gb_internal bool are_types_identical_unique_tuples(Type *x, Type *y) { return false; } + // if (x->canonical_hash && y->canonical_hash && x->canonical_hash != y->canonical_hash) { + // return false; + // } + // MUTEX_GUARD(&g_type_mutex); return are_types_identical_internal(x, y, true); } @@ -3006,6 +3068,10 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple return false; } + // if (x->canonical_hash && y->canonical_hash && x->canonical_hash != y->canonical_hash) { + // return false; + // } + #if 0 if (x->kind == Type_Named) { Entity *e = x->Named.type_name; @@ -3047,6 +3113,10 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple case Type_DynamicArray: return are_types_identical(x->DynamicArray.elem, y->DynamicArray.elem); + case Type_FixedCapacityDynamicArray: + return (x->FixedCapacityDynamicArray.capacity == y->FixedCapacityDynamicArray.capacity) && + are_types_identical(x->FixedCapacityDynamicArray.elem, y->FixedCapacityDynamicArray.elem); + case Type_Slice: return are_types_identical(x->Slice.elem, y->Slice.elem); @@ -3312,7 +3382,7 @@ gb_internal bool union_variant_index_types_equal(Type *v, Type *vt) { return false; } -gb_internal i64 union_variant_index(Type *u, Type *v) { +gb_internal i64 union_variant_index_checked(Type *u, Type *v) { u = base_type(u); GB_ASSERT(u->kind == Type_Union); @@ -3326,9 +3396,24 @@ gb_internal i64 union_variant_index(Type *u, Type *v) { } } } - return 0; + GB_PANIC("unfound variant -> %s %s", type_to_string(u), type_to_string(v)); + return -1; } +gb_internal bool union_is_variant_of(Type *u, Type *v) { + u = base_type(u); + GB_ASSERT(u->kind == Type_Union); + + for_array(i, u->Union.variants) { + Type *vt = u->Union.variants[i]; + if (union_variant_index_types_equal(v, vt)) { + return true; + } + } + return false; +} + + gb_internal i64 union_tag_size(Type *u) { u = base_type(u); GB_ASSERT(u->kind == Type_Union); @@ -3486,9 +3571,9 @@ gb_internal ProcTypeOverloadKind are_proc_types_overload_safe(Type *x, Type *y) -gb_internal Selection lookup_field_with_selection(Type *type_, String field_name, bool is_type, Selection sel, bool allow_blank_ident=false); +gb_internal Selection lookup_field_with_selection(Type *type_, InternedString field_name, bool is_type, Selection sel, bool allow_blank_ident=false); -gb_internal Selection lookup_field(Type *type_, String field_name, bool is_type, bool allow_blank_ident=false) { +gb_internal Selection lookup_field(Type *type_, InternedString field_name, bool is_type, bool allow_blank_ident=false) { return lookup_field_with_selection(type_, field_name, is_type, empty_selection, allow_blank_ident); } @@ -3541,13 +3626,13 @@ gb_internal Selection lookup_field_from_index(Type *type, i64 index) { return empty_selection; } -gb_internal Entity *scope_lookup_current(Scope *s, String const &name); +gb_internal Entity *scope_lookup_current(Scope *s, InternedString name, u32 hash=0); gb_internal bool has_type_got_objc_class_attribute(Type *t); -gb_internal Selection lookup_field_with_selection(Type *type_, String field_name, bool is_type, Selection sel, bool allow_blank_ident) { +gb_internal Selection lookup_field_with_selection(Type *type_, InternedString field_name, bool is_type, Selection sel, bool allow_blank_ident) { GB_ASSERT(type_ != nullptr); - if (!allow_blank_ident && is_blank_ident(field_name)) { + if (!allow_blank_ident && field_name.is_blank()) { return empty_selection; } @@ -3569,7 +3654,7 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name defer (mutex_unlock(md->mutex)); for (TypeNameObjCMetadataEntry const &entry : md->type_entries) { GB_ASSERT(entry.entity->kind == Entity_Procedure || entry.entity->kind == Entity_ProcGroup); - if (entry.name == field_name) { + if (entry.interned == field_name) { sel.entity = entry.entity; sel.pseudo_field = true; return sel; @@ -3596,7 +3681,7 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name for_array(i, type->Enum.fields) { Entity *f = type->Enum.fields[i]; GB_ASSERT(f->kind == Entity_Constant); - String str = f->token.string; + auto str = entity_interned_name(f); if (field_name == str) { sel.entity = f; @@ -3647,7 +3732,7 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name defer (mutex_unlock(md->mutex)); for (TypeNameObjCMetadataEntry const &entry : md->value_entries) { GB_ASSERT(entry.entity->kind == Entity_Procedure || entry.entity->kind == Entity_ProcGroup); - if (entry.name == field_name) { + if (entry.interned == field_name) { sel.entity = entry.entity; sel.pseudo_field = true; return sel; @@ -3676,7 +3761,7 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name if (f->kind != Entity_Variable || (f->flags & EntityFlag_Field) == 0) { continue; } - String str = f->token.string; + auto str = entity_interned_name(f); if (field_name == str) { selection_add_index(&sel, i); // HACK(bill): Leaky memory sel.entity = f; @@ -3705,11 +3790,12 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name bool is_soa_of_array = is_soa && is_type_array(type->Struct.soa_elem); if (is_soa_of_array) { - String mapped_field_name = {}; - if (field_name == "r") mapped_field_name = str_lit("x"); - else if (field_name == "g") mapped_field_name = str_lit("y"); - else if (field_name == "b") mapped_field_name = str_lit("z"); - else if (field_name == "a") mapped_field_name = str_lit("w"); + InternedString mapped_field_name = {}; + String n = field_name.string(); + if (n == "r") mapped_field_name = string_interner_insert(str_lit("x")); + else if (n == "g") mapped_field_name = string_interner_insert(str_lit("y")); + else if (n == "b") mapped_field_name = string_interner_insert(str_lit("z")); + else if (n == "a") mapped_field_name = string_interner_insert(str_lit("w")); return lookup_field_with_selection(type, mapped_field_name, is_type, sel, allow_blank_ident); } } else if (type->kind == Type_BitField) { @@ -3718,7 +3804,7 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name if (f->kind != Entity_Variable || (f->flags & EntityFlag_Field) == 0) { continue; } - String str = f->token.string; + auto str = entity_interned_name(f); if (field_name == str) { selection_add_index(&sel, i); // HACK(bill): Leaky memory sel.entity = f; @@ -3736,11 +3822,12 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name gb_local_persist Entity *entity__any_data = alloc_entity_field(nullptr, make_token_ident(data_str), t_rawptr, false, 0); gb_local_persist Entity *entity__any_id = alloc_entity_field(nullptr, make_token_ident(id_str), t_typeid, false, 1); - if (field_name == data_str) { + String n = field_name.string(); + if (n == data_str) { selection_add_index(&sel, 0); sel.entity = entity__any_data; return sel; - } else if (field_name == id_str) { + } else if (n == id_str) { selection_add_index(&sel, 1); sel.entity = entity__any_id; return sel; @@ -3758,19 +3845,21 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name gb_local_persist Entity *entity__x = alloc_entity_field(nullptr, make_token_ident(x), t_f16, false, 0); gb_local_persist Entity *entity__y = alloc_entity_field(nullptr, make_token_ident(y), t_f16, false, 1); gb_local_persist Entity *entity__z = alloc_entity_field(nullptr, make_token_ident(z), t_f16, false, 2); - if (field_name == w) { + + String n = field_name.string(); + if (n == w) { selection_add_index(&sel, 3); sel.entity = entity__w; return sel; - } else if (field_name == x) { + } else if (n == x) { selection_add_index(&sel, 0); sel.entity = entity__x; return sel; - } else if (field_name == y) { + } else if (n == y) { selection_add_index(&sel, 1); sel.entity = entity__y; return sel; - } else if (field_name == z) { + } else if (n == z) { selection_add_index(&sel, 2); sel.entity = entity__z; return sel; @@ -3787,19 +3876,21 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name gb_local_persist Entity *entity__x = alloc_entity_field(nullptr, make_token_ident(x), t_f32, false, 0); gb_local_persist Entity *entity__y = alloc_entity_field(nullptr, make_token_ident(y), t_f32, false, 1); gb_local_persist Entity *entity__z = alloc_entity_field(nullptr, make_token_ident(z), t_f32, false, 2); - if (field_name == w) { + + String n = field_name.string(); + if (n == w) { selection_add_index(&sel, 3); sel.entity = entity__w; return sel; - } else if (field_name == x) { + } else if (n == x) { selection_add_index(&sel, 0); sel.entity = entity__x; return sel; - } else if (field_name == y) { + } else if (n == y) { selection_add_index(&sel, 1); sel.entity = entity__y; return sel; - } else if (field_name == z) { + } else if (n == z) { selection_add_index(&sel, 2); sel.entity = entity__z; return sel; @@ -3816,19 +3907,21 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name gb_local_persist Entity *entity__x = alloc_entity_field(nullptr, make_token_ident(x), t_f64, false, 0); gb_local_persist Entity *entity__y = alloc_entity_field(nullptr, make_token_ident(y), t_f64, false, 1); gb_local_persist Entity *entity__z = alloc_entity_field(nullptr, make_token_ident(z), t_f64, false, 2); - if (field_name == w) { + + String n = field_name.string(); + if (n == w) { selection_add_index(&sel, 3); sel.entity = entity__w; return sel; - } else if (field_name == x) { + } else if (n == x) { selection_add_index(&sel, 0); sel.entity = entity__x; return sel; - } else if (field_name == y) { + } else if (n == y) { selection_add_index(&sel, 1); sel.entity = entity__y; return sel; - } else if (field_name == z) { + } else if (n == z) { selection_add_index(&sel, 2); sel.entity = entity__z; return sel; @@ -3845,19 +3938,21 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name gb_local_persist Entity *entity__x = alloc_entity_field(nullptr, make_token_ident(x), t_untyped_float, false, 0); gb_local_persist Entity *entity__y = alloc_entity_field(nullptr, make_token_ident(y), t_untyped_float, false, 1); gb_local_persist Entity *entity__z = alloc_entity_field(nullptr, make_token_ident(z), t_untyped_float, false, 2); - if (field_name == w) { + + String n = field_name.string(); + if (n == w) { selection_add_index(&sel, 3); sel.entity = entity__w; return sel; - } else if (field_name == x) { + } else if (n == x) { selection_add_index(&sel, 0); sel.entity = entity__x; return sel; - } else if (field_name == y) { + } else if (n == y) { selection_add_index(&sel, 1); sel.entity = entity__y; return sel; - } else if (field_name == z) { + } else if (n == z) { selection_add_index(&sel, 2); sel.entity = entity__z; return sel; @@ -3872,7 +3967,8 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name String allocator_str = str_lit("allocator"); gb_local_persist Entity *entity__allocator = alloc_entity_field(nullptr, make_token_ident(allocator_str), t_allocator, false, 3); - if (field_name == allocator_str) { + String n = field_name.string(); + if (n == allocator_str) { selection_add_index(&sel, 3); sel.entity = entity__allocator; return sel; @@ -3882,7 +3978,8 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name String allocator_str = str_lit("allocator"); gb_local_persist Entity *entity__allocator = alloc_entity_field(nullptr, make_token_ident(allocator_str), t_allocator, false, 2); - if (field_name == allocator_str) { + String n = field_name.string(); + if (n == allocator_str) { selection_add_index(&sel, 2); sel.entity = entity__allocator; return sel; @@ -3890,7 +3987,7 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name #define _ARRAY_FIELD_CASE_IF(_length, _name) \ - if (field_name == (_name)) { \ + if (n == (_name)) { \ selection_add_index(&sel, (_length)-1); \ sel.entity = alloc_entity_array_elem(nullptr, make_token_ident(str_lit(_name)), elem, (_length)-1); \ return sel; \ @@ -3903,7 +4000,7 @@ case (_length): \ } else if (type->kind == Type_Array) { - + String n = field_name.string(); Type *elem = type->Array.elem; if (type->Array.count <= 4) { @@ -3918,8 +4015,9 @@ case (_length): \ } } } else if (type->kind == Type_SimdVector) { - + String n = field_name.string(); Type *elem = type->SimdVector.elem; + if (type->SimdVector.count <= 4) { // HACK(bill): Memory leak switch (type->SimdVector.count) { @@ -4144,9 +4242,22 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) { } case Type_DynamicArray: - // data, count, capacity, allocator + // data, len, cap, allocator return build_context.int_size; + case Type_FixedCapacityDynamicArray: + // data, len + { + Type *elem = t->FixedCapacityDynamicArray.elem; + bool pop = type_path_push(path, elem); + if (path->failure) { + return FAILURE_ALIGNMENT; + } + i64 align = type_align_of_internal(elem, path); + if (pop) type_path_pop(path); + return gb_max(build_context.int_size, align); + } + case Type_Slice: return build_context.int_size; @@ -4419,6 +4530,31 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) { // data + len + cap + allocator(procedure+data) return 3*build_context.int_size + 2*build_context.ptr_size; + case Type_FixedCapacityDynamicArray: + { + // data + len + i64 capacity = t->FixedCapacityDynamicArray.capacity; + Type *elem = t->FixedCapacityDynamicArray.elem; + i64 align = type_align_of_internal(elem, path); + if (path->failure) { + return FAILURE_SIZE; + } + align = gb_max(build_context.int_size, align); + + i64 size = type_size_of(elem); + size *= capacity; + + i64 old_size = size; + size = align_formula(size, build_context.int_size); + + i64 padding = size - old_size; + t->FixedCapacityDynamicArray.padding_needed = padding; + + size += 1*build_context.int_size; + size = align_formula(size, align); + return size; + } + case Type_Map: /* struct { @@ -4644,6 +4780,24 @@ gb_internal i64 type_offset_of(Type *t, i64 index, Type **field_type_) { return 3*build_context.int_size; // allocator } break; + + case Type_FixedCapacityDynamicArray: + switch (index) { + case 0: + if (field_type_) *field_type_ = alloc_type_array(t->FixedCapacityDynamicArray.elem, t->FixedCapacityDynamicArray.capacity); + return 0; // data + + case 1: // len + if (field_type_) *field_type_ = t_int; + { + i64 elem_size = type_size_of(t->FixedCapacityDynamicArray.elem); + i64 offset = 0; + offset = elem_size * t->FixedCapacityDynamicArray.capacity; + offset = align_formula(offset, build_context.int_size); + return offset; + } + } + break; case Type_Union: if (!is_type_union_maybe_pointer(t)) { /* i64 s = */ type_size_of(t); @@ -4711,6 +4865,12 @@ gb_internal i64 type_offset_of_from_selection(Type *type, Selection sel) { case 3: t = t_allocator; break; } break; + case Type_FixedCapacityDynamicArray: + switch (index) { + case 0: t = alloc_type_array(t->FixedCapacityDynamicArray.elem, t->FixedCapacityDynamicArray.capacity); break; + case 1: t = t_int; break; + } + break; } } } @@ -4970,6 +5130,15 @@ gb_internal Type *type_internal_index(Type *t, isize index) { default: GB_PANIC("invalid raw dynamic array index"); }; } + + case Type_FixedCapacityDynamicArray: + { + switch (index) { + case 0: t = alloc_type_array(t->FixedCapacityDynamicArray.elem, t->FixedCapacityDynamicArray.capacity); break; + case 1: return t_int; + default: GB_PANIC("invalid raw fixed capacity dynamic array index"); + }; + } case Type_Struct: return get_struct_field_type(bt, index); case Type_Union: @@ -5068,6 +5237,13 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha str = write_type_to_string(str, type->DynamicArray.elem, shorthand, allow_polymorphic); break; + case Type_FixedCapacityDynamicArray: + str = gb_string_appendc(str, "[dynamic; "); + str = gb_string_appendc(str, gb_bprintf("%lld", cast(long long)type->FixedCapacityDynamicArray.capacity)); + str = gb_string_appendc(str, "]"); + str = write_type_to_string(str, type->FixedCapacityDynamicArray.elem, shorthand, allow_polymorphic); + break; + case Type_Enum: str = gb_string_appendc(str, "enum"); if (type->Enum.base_type != nullptr) { diff --git a/tests/core/encoding/cbor/test_core_cbor.odin b/tests/core/encoding/cbor/test_core_cbor.odin index 7bea69d2e..6e3312779 100644 --- a/tests/core/encoding/cbor/test_core_cbor.odin +++ b/tests/core/encoding/cbor/test_core_cbor.odin @@ -427,6 +427,7 @@ test_unmarshal_map_into_struct_partially :: proc(t: ^testing.T) { } Foo_More :: struct { + foo: bool, bar: struct { hello: string, world: string, @@ -436,6 +437,7 @@ test_unmarshal_map_into_struct_partially :: proc(t: ^testing.T) { baz: int, } more := Foo_More{ + foo = false, bar = { hello = "hello", world = "world", diff --git a/tests/internal/test_fixed_capacity_dynamic_array.odin b/tests/internal/test_fixed_capacity_dynamic_array.odin new file mode 100644 index 000000000..49cf3a8f9 --- /dev/null +++ b/tests/internal/test_fixed_capacity_dynamic_array.odin @@ -0,0 +1,93 @@ +package test_internal + +import "core:testing" + +@(private="file") +slice_equal :: proc(a, b: []int) -> bool { + if len(a) != len(b) { + return false + } + + for a, i in a { + if b[i] != a { + return false + } + } + return true +} + + +@(test) +test_fixed_capacity_dynamic_array_removes :: proc(t: ^testing.T) { + array: [dynamic; 10]int + append(&array, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9) + + ordered_remove(&array, 0) + testing.expect(t, slice_equal(array[:], []int { 1, 2, 3, 4, 5, 6, 7, 8, 9 })) + ordered_remove(&array, 5) + testing.expect(t, slice_equal(array[:], []int { 1, 2, 3, 4, 5, 7, 8, 9 })) + ordered_remove(&array, 6) + testing.expect(t, slice_equal(array[:], []int { 1, 2, 3, 4, 5, 7, 9 })) + unordered_remove(&array, 0) + testing.expect(t, slice_equal(array[:], []int { 9, 2, 3, 4, 5, 7 })) + unordered_remove(&array, 2) + testing.expect(t, slice_equal(array[:], []int { 9, 2, 7, 4, 5 })) + unordered_remove(&array, 4) + testing.expect(t, slice_equal(array[:], []int { 9, 2, 7, 4 })) +} + +@(test) +test_fixed_capacity_dynamic_array_inject_at :: proc(t: ^testing.T) { + array: [dynamic; 13]int + append(&array, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9) + + testing.expect(t, inject_at(&array, 0, 0), "Expected to be able to inject into fixed capacity dynamic array") + testing.expect(t, slice_equal(array[:], []int { 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 })) + testing.expect(t, inject_at(&array, 0, 5), "Expected to be able to inject into fixed capacity dynamic array") + testing.expect(t, slice_equal(array[:], []int { 5, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 })) + testing.expect(t, inject_at(&array, len(array), 0), "Expected to be able to inject into fixed capacity dynamic array") + testing.expect(t, slice_equal(array[:], []int { 5, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 })) +} + +@(test) +test_fixed_capacity_dynamic_array_push_back_elems :: proc(t: ^testing.T) { + array: [dynamic; 2]int + testing.expect(t, slice_equal(array[:], []int { })) + testing.expect(t, append(&array, 0) == 1, "Expected to be able to append to empty fixed capacity dynamic array") + testing.expect(t, slice_equal(array[:], []int { 0 })) + testing.expect(t, append(&array, 1) == 1, "Expected to be able to append to fixed capacity dynamic array") + testing.expect(t, slice_equal(array[:], []int { 0, 1 })) + testing.expect(t, append(&array, 1, 2) == 0, "Expected to fail appending multiple elements beyond capacity of fixed capacity dynamic array") + clear(&array) + testing.expect(t, append(&array, 1) == 1, "Expected to be able to append to fixed capacity dynamic array") + testing.expect(t, append(&array, 2) == 1, "Expected to be able to append to fixed capacity dynamic array") + testing.expect(t, append(&array, 1) != 1, "Expected to fail appending to full fixed capacity dynamic array") + testing.expect(t, append(&array, 1, 2) != 2, "Expected to fail appending multiple elements to full fixed capacity dynamic array") + clear(&array) + testing.expect(t, slice_equal(array[:], []int { })) + testing.expect(t, append(&array, 1, 2, 3) != 3, "Expected to fail appending multiple elements to empty fixed capacity dynamic array") + testing.expect(t, slice_equal(array[:], []int { 1, 2 })) + clear(&array) + testing.expect(t, append(&array, 1, 2) == 2, "Expected to be able to append multiple elements to empty fixed capacity dynamic array") + testing.expect(t, slice_equal(array[:], []int { 1, 2 })) +} + +@(test) +test_fixed_capacity_dynamic_array_resize :: proc(t: ^testing.T) { + array: [dynamic; 4]int + + for i in 0..<4 { + append(&array, i+1) + } + testing.expect(t, slice_equal(array[:], []int{1, 2, 3, 4}), "Expected to initialize the array with 1, 2, 3, 4") + + clear(&array) + testing.expect(t, slice_equal(array[:], []int{}), "Expected to clear the array") + + non_zero_resize(&array, 4) + testing.expect(t, slice_equal(array[:], []int{1, 2, 3, 4}), "Expected non_zero_resize to set length 4 with previous values") + + clear(&array) + resize(&array, 4) + testing.expect(t, slice_equal(array[:], []int{0, 0, 0, 0}), "Expected resize to set length 4 with zeroed values") +}