diff --git a/.gitattributes b/.gitattributes index 25300b4ef..dd9d1a0c8 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,6 +5,9 @@ Makefile text eol=lf *.sh text eol=lf vendor/box2d/lib/box2d_windows_amd64_avx2.lib filter=lfs diff=lfs merge=lfs -text vendor/box2d/lib/box2d_windows_amd64_sse2.lib filter=lfs diff=lfs merge=lfs -text +vendor/box3d/lib/linux-amd64/libbox3d.a filter=lfs diff=lfs merge=lfs -text +vendor/box3d/lib/darwin/libbox3d.a filter=lfs diff=lfs merge=lfs -text +vendor/box3d/lib/box3d.lib filter=lfs diff=lfs merge=lfs -text vendor/miniaudio/lib/miniaudio.lib filter=lfs diff=lfs merge=lfs -text vendor/sdl3/SDL3.dll filter=lfs diff=lfs merge=lfs -text vendor/sdl3/SDL3.lib filter=lfs diff=lfs merge=lfs -text @@ -31,5 +34,3 @@ vendor/raylib/windows/raylibdll.lib filter=lfs diff=lfs merge=lfs -text vendor/raylib/windows/raygui.dll filter=lfs diff=lfs merge=lfs -text vendor/raylib/windows/raygui.lib filter=lfs diff=lfs merge=lfs -text vendor/raylib/windows/rayguidll.lib filter=lfs diff=lfs merge=lfs -text -vendor/box3d/lib/linux-amd64/libbox3d.a filter=lfs diff=lfs merge=lfs -text -vendor/box3d/lib/darwin/libbox3d.a filter=lfs diff=lfs merge=lfs -text diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 17a00c36a..b3ae67161 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,9 +36,9 @@ jobs: ./odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:netbsd_arm64 ./odin check examples/all/sdl3 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:netbsd_amd64 -no-entry-point ./odin check examples/all/sdl3 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:netbsd_arm64 -no-entry-point - ./odin test tests/core/normal.odin -file -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true - ./odin test tests/core/speed.odin -file -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -o:speed -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true - ./odin test tests/vendor -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true + ./odin test tests/core/normal.odin -file -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -microarch:native + ./odin test tests/core/speed.odin -file -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -o:speed -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -microarch:native + ./odin test tests/vendor -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -microarch:native (cd tests/issues; ./run.sh) ./odin check tests/benchmark -vet -strict-style -no-entry-point diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index e639aadbd..d91bc2e79 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -379,6 +379,7 @@ simd_pairwise_sub :: proc(a, b: #simd[LANES]T) -> #simd[LANES]T where LANES % 2 simd_interleave :: proc(a, ..#simd[LANES/N]T) -> #simd[LANES]T where N >= 1 --- simd_deinterleave :: proc(a: #simd[LANES]T, $N: uint) -> (..#simd[LANES/N]T) where N >= 1, LANES % N == 0 --- // returns N multiple vectors +soa_copy_from_slice :: proc(ptr: ^$A/#soa[dynamic]$E, offset: int, args: []$E) --- // Checks if the current target supports the given target features. // diff --git a/base/runtime/core_builtin.odin b/base/runtime/core_builtin.odin index e65cf56f0..4913b53d9 100644 --- a/base/runtime/core_builtin.odin +++ b/base/runtime/core_builtin.odin @@ -128,7 +128,7 @@ copy :: proc{copy_slice, copy_from_string, copy_from_string16} // 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_dynamic_array :: proc(array: ^$D/[dynamic]$T, #any_int index: int, loc := #caller_location) #no_bounds_check { +unordered_remove_dynamic_array :: proc(#no_alias 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 { @@ -142,7 +142,7 @@ unordered_remove_dynamic_array :: proc(array: ^$D/[dynamic]$T, #any_int index: i // 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_dynamic_array :: proc(array: ^$D/[dynamic]$T, #any_int index: int, loc := #caller_location) #no_bounds_check { +ordered_remove_dynamic_array :: proc(#no_alias 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:]) @@ -155,7 +155,7 @@ ordered_remove_dynamic_array :: proc(array: ^$D/[dynamic]$T, #any_int index: int // Note: This is an O(N) operation. // Note: If the range is out of bounds, this procedure will panic. @builtin -remove_range_dynamic_array :: proc(array: ^$D/[dynamic]$T, #any_int lo, hi: int, loc := #caller_location) #no_bounds_check { +remove_range_dynamic_array :: proc(#no_alias 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 { @@ -236,13 +236,13 @@ remove_range :: proc{ // // Note: If the dynamic array has no elements (`len(array) == 0`), this procedure will panic. @builtin -pop_dynamic_array :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check { +pop_dynamic_array :: proc(#no_alias array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check { assert(len(array) > 0, loc=loc) _pop_dynamic_array_type_erased(&res, (^Raw_Dynamic_Array)(array), size_of(E)) return res } -_pop_dynamic_array_type_erased :: proc(res: rawptr, array: ^Raw_Dynamic_Array, elem_size: int) { +_pop_dynamic_array_type_erased :: proc(res: rawptr, #no_alias 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 @@ -276,7 +276,7 @@ pop :: proc{ // `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_dynamic_array :: proc "contextless" (array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check { +pop_safe_dynamic_array :: proc "contextless" (#no_alias array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check { if len(array) == 0 { return } @@ -310,7 +310,7 @@ pop_safe :: proc{ // // Note: If the dynamic array as no elements (`len(array) == 0`), this procedure will panic. @builtin -pop_front_dynamic_array :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check { +pop_front_dynamic_array :: proc(#no_alias 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 { @@ -348,7 +348,7 @@ pop_front :: proc{ // `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_dynamic_array :: proc "contextless" (array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check { +pop_front_safe_dynamic_array :: proc "contextless" (#no_alias array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check { if len(array) == 0 { return } @@ -590,7 +590,7 @@ make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #a } @(require_results) -_make_dynamic_array_len_cap :: proc(array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (err: Allocator_Error) { +_make_dynamic_array_len_cap :: proc(#no_alias array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (err: Allocator_Error) { make_dynamic_array_error_loc(loc, len, cap) array.allocator = allocator // initialize allocator before just in case it fails to allocate any memory data := mem_alloc_bytes(size_of_elem*cap, align_of_elem, allocator, loc) or_return @@ -716,7 +716,7 @@ when MAP_ENABLED { } } -_append_elem :: #force_no_inline proc(array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, arg_ptr: rawptr, should_zero: bool, loc := #caller_location) -> (num_appended: int, err: Allocator_Error) #optional_allocator_error { +_append_elem :: #force_no_inline proc(#no_alias array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, arg_ptr: rawptr, should_zero: bool, loc := #caller_location) -> (num_appended: int, err: Allocator_Error) #optional_allocator_error { if array == nil { return } @@ -739,7 +739,7 @@ _append_elem :: #force_no_inline proc(array: ^Raw_Dynamic_Array, size_of_elem, a return } -_append_elem_ptr :: #force_no_inline proc(array: ^Raw_Dynamic_Array, arg: rawptr, should_zero: bool, loc := #caller_location) -> (num_appended: int, err: Allocator_Error) #optional_allocator_error { +_append_elem_ptr :: #force_no_inline proc(#no_alias array: ^Raw_Dynamic_Array, arg: rawptr, should_zero: bool, loc := #caller_location) -> (num_appended: int, err: Allocator_Error) #optional_allocator_error { if array == nil { return } @@ -764,7 +764,7 @@ _append_elem_ptr :: #force_no_inline proc(array: ^Raw_Dynamic_Array, arg: rawptr // `append_elem` appends an element to the end of a dynamic array. @builtin -append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (num_appended: int, err: Allocator_Error) #optional_allocator_error { +append_elem :: proc(#no_alias array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (num_appended: int, err: Allocator_Error) #optional_allocator_error { when size_of(E) == 0 { if array == nil { return @@ -806,7 +806,7 @@ append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller // // Note: Prefer using the procedure group `non_zero_append @builtin -non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (num_appended: int, err: Allocator_Error) #optional_allocator_error { +non_zero_append_elem :: proc(#no_alias array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (num_appended: int, err: Allocator_Error) #optional_allocator_error { when size_of(E) == 0 { (^Raw_Dynamic_Array)(array).len += 1 return 1, nil @@ -841,7 +841,7 @@ non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc : } } -_append_elems :: #force_no_inline proc(array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, should_zero: bool, loc := #caller_location, args: rawptr, arg_len: int) -> (num_appended: int, err: Allocator_Error) #optional_allocator_error { +_append_elems :: #force_no_inline proc(#no_alias array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, should_zero: bool, loc := #caller_location, args: rawptr, arg_len: int) -> (num_appended: int, err: Allocator_Error) #optional_allocator_error { if array == nil { return 0, nil } @@ -872,7 +872,7 @@ _append_elems :: #force_no_inline proc(array: ^Raw_Dynamic_Array, size_of_elem, // // Note: Prefer using the procedure group `append`. @builtin -append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (num_appended: int, err: Allocator_Error) #optional_allocator_error { +append_elems :: proc(#no_alias array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (num_appended: int, err: Allocator_Error) #optional_allocator_error { when size_of(E) == 0 { a := (^Raw_Dynamic_Array)(array) a.len += len(args) @@ -886,7 +886,7 @@ append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #ca // // Note: Prefer using the procedure group `non_zero_append @builtin -non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (num_appended: int, err: Allocator_Error) #optional_allocator_error { +non_zero_append_elems :: proc(#no_alias array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (num_appended: int, err: Allocator_Error) #optional_allocator_error { when size_of(E) == 0 { a := (^Raw_Dynamic_Array)(array) a.len += len(args) @@ -897,7 +897,7 @@ non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, l } // The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type -_append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, should_zero: bool, loc := #caller_location) -> (num_appended: int, err: Allocator_Error) #optional_allocator_error { +_append_elem_string :: proc(#no_alias array: ^$T/[dynamic]$E/u8, arg: $A/string, should_zero: bool, loc := #caller_location) -> (num_appended: int, err: Allocator_Error) #optional_allocator_error { return _append_elems((^Raw_Dynamic_Array)(array), 1, 1, should_zero, loc, raw_data(arg), len(arg)) } @@ -905,14 +905,14 @@ _append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, should_ze // // Note: Prefer using the procedure group `append`. @builtin -append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (num_appended: int, err: Allocator_Error) #optional_allocator_error { +append_elem_string :: proc(#no_alias array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (num_appended: int, err: Allocator_Error) #optional_allocator_error { return _append_elem_string(array, arg, true, loc) } // `non_zero_append_elem_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_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (num_appended: int, err: Allocator_Error) #optional_allocator_error { +non_zero_append_elem_string :: proc(#no_alias array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (num_appended: int, err: Allocator_Error) #optional_allocator_error { return _append_elem_string(array, arg, false, loc) } @@ -930,7 +930,7 @@ non_zero_append_elem_fixed_capacity_string :: proc "contextless" (array: ^$T/[dy // // Note: Prefer using the procedure group `append`. @builtin -append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) -> (num_appended: int, err: Allocator_Error) #optional_allocator_error { +append_string :: proc(#no_alias array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) -> (num_appended: int, err: Allocator_Error) #optional_allocator_error { n_arg: int for arg in args { n_arg, err = append(array, ..transmute([]E)(arg), loc=loc) @@ -1026,7 +1026,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_dynamic_array :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { +append_nothing_dynamic_array :: proc(#no_alias array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { if array == nil { return 0, nil } @@ -1059,7 +1059,7 @@ append_nothing :: proc{ // `inject_at_elem` injects an element in a dynamic array at a specified index and moves the previous elements after that index "across" @builtin -inject_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { +inject_at_elem :: proc(#no_alias array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { when !ODIN_NO_BOUNDS_CHECK { ensure(index >= 0, "Index must be positive.", loc) } @@ -1081,7 +1081,7 @@ inject_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcas // `inject_at_elems` injects multiple elements in a dynamic array at a specified index and moves the previous elements after that index "across" @builtin -inject_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { +inject_at_elems :: proc(#no_alias array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { when !ODIN_NO_BOUNDS_CHECK { ensure(index >= 0, "Index must be positive.", loc) } @@ -1108,7 +1108,7 @@ inject_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadca // `inject_at_elem_string` 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 :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { +inject_at_elem_string :: proc(#no_alias array: ^$T/[dynamic]$E/u8, #any_int index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { when !ODIN_NO_BOUNDS_CHECK { ensure(index >= 0, "Index must be positive.", loc) } @@ -1219,7 +1219,7 @@ inject_at :: proc{ // `assign_at_elem` 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 :: proc(array: ^$T/[dynamic]$E, #any_int index: int, arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { +assign_at_elem :: proc(#no_alias array: ^$T/[dynamic]$E, #any_int index: int, arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { if index < len(array) { array[index] = arg ok = true @@ -1235,7 +1235,7 @@ assign_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, arg: E, loc // `assign_at_elems` 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 :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { +assign_at_elems :: proc(#no_alias array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { new_size := index + len(args) if len(args) == 0 { ok = true @@ -1253,7 +1253,7 @@ assign_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadca // `assign_at_elem_string` 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 :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { +assign_at_elem_string :: proc(#no_alias array: ^$T/[dynamic]$E/u8, #any_int index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { new_size := index + len(arg) if len(arg) == 0 { ok = true @@ -1341,7 +1341,7 @@ assign_at :: proc{ // // Note: Prefer the procedure group `clear`. @builtin -clear_dynamic_array :: proc "contextless" (array: ^$T/[dynamic]$E) { +clear_dynamic_array :: proc "contextless" (#no_alias array: ^$T/[dynamic]$E) { if array != nil { (^Raw_Dynamic_Array)(array).len = 0 } @@ -1362,7 +1362,7 @@ clear_fixed_capacity_dynamic_array :: proc "contextless" (array: ^$T/[dynamic; $ // When a memory resize allocation is required, the memory will be asked to be zeroed (i.e. it calls `mem_resize`). // // Note: Prefer the procedure group `reserve`. -_reserve_dynamic_array :: #force_no_inline proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, capacity: int, should_zero: bool, loc := #caller_location) -> Allocator_Error { +_reserve_dynamic_array :: #force_no_inline proc(#no_alias a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, capacity: int, should_zero: bool, loc := #caller_location) -> Allocator_Error { if a == nil { return nil } @@ -1395,7 +1395,7 @@ _reserve_dynamic_array :: #force_no_inline proc(a: ^Raw_Dynamic_Array, size_of_e return nil } -_reserve_dynamic_array_unsafe :: #force_no_inline proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, capacity: int, should_zero: bool, loc := #caller_location) -> Allocator_Error { +_reserve_dynamic_array_unsafe :: #force_no_inline proc(#no_alias a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, capacity: int, should_zero: bool, loc := #caller_location) -> Allocator_Error { if capacity <= a.cap { return nil } @@ -1430,7 +1430,7 @@ _reserve_dynamic_array_unsafe :: #force_no_inline proc(a: ^Raw_Dynamic_Array, si // // Note: Prefer the procedure group `reserve`. @builtin -reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error { +reserve_dynamic_array :: proc(#no_alias array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error { return _reserve_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), capacity, true, loc) } @@ -1440,12 +1440,12 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, lo // // Note: Prefer the procedure group `non_zero_reserve`. @builtin -non_zero_reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error { +non_zero_reserve_dynamic_array :: proc(#no_alias array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error { return _reserve_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), capacity, false, loc) } -_resize_dynamic_array :: #force_no_inline proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, length: int, should_zero: bool, loc := #caller_location) -> Allocator_Error { +_resize_dynamic_array :: #force_no_inline proc(#no_alias a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, length: int, should_zero: bool, loc := #caller_location) -> Allocator_Error { if a == nil { return nil } @@ -1491,7 +1491,7 @@ _resize_dynamic_array :: #force_no_inline proc(a: ^Raw_Dynamic_Array, size_of_el // // Note: Prefer the procedure group `resize` @builtin -resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error { +resize_dynamic_array :: proc(#no_alias array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error { return _resize_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), length, true, loc=loc) } @@ -1501,7 +1501,7 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc : // // Note: Prefer the procedure group `non_zero_resize` @builtin -non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error { +non_zero_resize_dynamic_array :: proc(#no_alias array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error { return _resize_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), length, false, loc=loc) } @@ -1552,11 +1552,11 @@ non_zero_resize_fixed_capacity_dynamic_array :: proc "contextless" (array: ^$T/[ // // Note: Prefer the procedure group `shrink` @builtin -shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int new_cap := -1, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) { +shrink_dynamic_array :: proc(#no_alias array: ^$T/[dynamic]$E, #any_int new_cap := -1, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) { return _shrink_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), new_cap, loc) } -_shrink_dynamic_array :: proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, new_cap := -1, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) { +_shrink_dynamic_array :: proc(#no_alias a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, new_cap := -1, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) { if a == nil { return } diff --git a/base/runtime/core_builtin_soa.odin b/base/runtime/core_builtin_soa.odin index f4cdec08f..61b93efd7 100644 --- a/base/runtime/core_builtin_soa.odin +++ b/base/runtime/core_builtin_soa.odin @@ -50,13 +50,18 @@ Raw_SOA_Footer_Dynamic_Array :: struct { allocator: Allocator, } +// Note: When casting array to access footer/fields +// uintptr(array) lowers to LLVM ptrtoint and captures pointer provenance, +// whcih defeats #no_alias on the array in any code following (including in callers). +// Multipointer indexing lowers to GEP and doesn't capture, so prefer that throughout. + @(builtin, require_results) raw_soa_footer_slice :: proc(array: ^$T/#soa[]$E) -> (footer: ^Raw_SOA_Footer_Slice) { if array == nil { return nil } - field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)) - footer = (^Raw_SOA_Footer_Slice)(uintptr(array) + field_count*size_of(rawptr)) + field_count := len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E) + footer = (^Raw_SOA_Footer_Slice)(&([^]byte)(array)[field_count*size_of(rawptr)]) return } @(builtin, require_results) @@ -64,8 +69,8 @@ raw_soa_footer_dynamic_array :: proc(array: ^$T/#soa[dynamic]$E) -> (footer: ^Ra if array == nil { return nil } - field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)) - footer = (^Raw_SOA_Footer_Dynamic_Array)(uintptr(array) + field_count*size_of(rawptr)) + field_count := len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E) + footer = (^Raw_SOA_Footer_Dynamic_Array)(&([^]byte)(array)[field_count*size_of(rawptr)]) return } raw_soa_footer :: proc{ @@ -118,15 +123,13 @@ make_soa_aligned :: proc($T: typeid/#soa[]$E, #any_int length, alignment: int, a } new_data := raw_data(new_bytes) - data := uintptr(&array) offset := 0 for i in 0.. Allocator_Error { +resize_soa :: proc(#no_alias array: ^$T/#soa[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error { if array == nil { return nil } @@ -215,7 +218,7 @@ resize_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int length: int, loc := #cal } @builtin -non_zero_resize_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error { +non_zero_resize_soa :: proc(#no_alias array: ^$T/#soa[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error { if array == nil { return nil } @@ -225,6 +228,15 @@ non_zero_resize_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int length: int, lo return nil } +// `reserve_soa` will try to reserve memory of a passed SOA dynamic array to the requested element count (setting the `cap`). +// +// For maximizing performance it is recommended to avoid power-of-2 capacities +// when manually reserving memory for SOA arrays, more so for sizes above 512. +// For element types with several fields, prefer padding such capacities by at +// least the number of fields that fit in a CPU cache line. +// Modern cache lines are typically 64 (Intel/AMD/Arm64) or 128 bytes (Apple Silicon), +// so pad requested capacity with e.g. `128 / size_of(field)` (for the smallest field when field sizes differ), +// that is, prefer capacity of 4128 instead of 4096 for 4-byte fields. @builtin reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error { return _reserve_soa(array, capacity, true, loc) @@ -235,7 +247,33 @@ non_zero_reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int capacity: int, return _reserve_soa(array, capacity, false, loc) } -_reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, zero_memory: bool, loc := #caller_location) -> Allocator_Error { + +// Note: capacity and performance +// Each field is stored in contiguous memory with stride between fields capacity * size_of(field) (+ alignment). +// When the stride is a multiple of 4KB, e.g. a power-of-2 capacity >= 1024 with 4-byte fields, +// every same-size field's cache line for a given index maps to the same L1 set, and +// per-element operations degrade (e.g. append_soa, inject_at_soa). +// It is worst past 8 same-size fields where the burst exceeds the common +// L1 8-way associativity and the fields collide (measured up to ~7-8x degradation +// on a type with 18 same-size fields; fields of different sizes advance through +// the sets at different rates, so they don't collide persistently). +// Common SoA iterative flows may also be affected, when the loop touches multiple fields +// and other hot data aliases the same L1 set. +// So, for types with several fields, prefer padding such capacities by at +// least the number of fields that fit in a CPU cache line. +// Modern cache lines are typically 64 (Intel/AMD/Arm64) or 128 bytes (Apple Silicon), +// so pad with e.g. 128 / size_of(field) (for the smallest field when field sizes differ), +// so prefer 4128 instead of 4096 for 4-byte fields. Setting cap to e.g. 4097 is NOT enough +// (it shifts field memory by only 4 bytes (same cache line) and fields keep colliding). +// Note: 4KB stride in the discussion above is the x86 L1 set period (32–48KB/8–12-way); +// Apple Silicon (128KB/8-way) has a 16KB period, so cap 4096 × 4 = 16KB stride +// is still pathological there and the recommended 128-byte pad fixes it. +// Note: struct size also plays a role, but the user facing padding recommendation +// on reserve_soa() will handle up to 256 fields (of 4 bytes) decently on 8-way caches. +// Note: Ideally, this should be handled internally by allowing allocated capacity to exceed requested capacity. +// This would break current runtime tests though, they explicitly test requested cap. +// Note: Capacities produced by append growth all the way from empty (2*cap + 8) are not affected. +_reserve_soa :: #force_no_inline proc(array: ^$T/#soa[dynamic]$E, capacity: int, zero_memory: bool, loc := #caller_location) -> Allocator_Error { if array == nil { return nil } @@ -322,7 +360,7 @@ _reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, zero_memory: boo mem_copy(new_data_elem, old_data_elem, old_size_elem) - (^rawptr)(uintptr(array) + i*size_of(rawptr))^ = new_data_elem + ([^]rawptr)(array)[i] = new_data_elem if zero_memory { mem_zero(rawptr(uintptr(new_data_elem) + uintptr(old_size_elem)), new_size_elem - old_size_elem) @@ -361,7 +399,7 @@ _reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, zero_memory: boo mem_copy(new_data_elem, old_data_elem, type.size * old_cap) - (^rawptr)(uintptr(array) + i*size_of(rawptr))^ = new_data_elem + ([^]rawptr)(array)[i] = new_data_elem old_offset += type.size * old_cap new_offset += type.size * capacity @@ -388,7 +426,7 @@ non_zero_append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, #no_broadcast arg: return _append_soa_elem(array, false, arg, loc) } -_append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, zero_memory: bool, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { +_append_soa_elem :: proc(#no_alias array: ^$T/#soa[dynamic]$E, zero_memory: bool, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { if array == nil { return 0, nil } @@ -399,35 +437,13 @@ _append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, zero_memory: bool, #no_broa err = _reserve_soa(array, cap, zero_memory, loc) // do not 'or_return' here as it could be a partial success } - footer := raw_soa_footer(array) - if size_of(E) > 0 && cap(array)-len(array) > 0 { - ti := type_info_of(T) - ti = type_info_base(ti) - si := &ti.variant.(Type_Info_Struct) - field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)) - - data := (^rawptr)(array)^ - - soa_offset := 0 - item_offset := 0 - - arg_copy := arg - arg_ptr := &arg_copy - - max_align :: align_of(E) - for i in 0.. (n: int, err: Allocator_Error) #optional_allocator_error { +_append_soa_elems :: proc(#no_alias array: ^$T/#soa[dynamic]$E, zero_memory: bool, #no_broadcast args: []E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { if array == nil { return } @@ -464,42 +480,26 @@ _append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, zero_memory: bool, #no_bro footer := raw_soa_footer(array) if size_of(E) > 0 && arg_len > 0 { - ti := type_info_of(typeid_of(T)) - ti = type_info_base(ti) - si := &ti.variant.(Type_Info_Struct) - field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)) - - data := (^rawptr)(array)^ - - soa_offset := 0 - item_offset := 0 - - args_ptr := &args[0] - - max_align :: align_of(E) - for i in 0.. (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { +inject_at_elem_soa :: proc(#no_alias array: ^$T/#soa[dynamic]$E, #any_int index: int, #no_broadcast arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { when !ODIN_NO_BOUNDS_CHECK { ensure(index >= 0, "Index must be positive.", loc) } if array == nil { return } - n := max(len(array), index) + old_len := len(array) + n := max(old_len, index) m :: 1 new_len := n + m - resize_soa(array, new_len, loc) or_return + // The tail shift and the stored element cover every new slot, + // except a gap of [old_len, index) when injecting past the end, which is + // zeroed explicitly below. + non_zero_resize_soa(array, new_len, loc) or_return when size_of(E) != 0 { ti := type_info_base(type_info_of(typeid_of(T))) si := &ti.variant.(Type_Info_Struct) - field_count := len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E) + FIELD_COUNT :: len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E) - item_offset := 0 - - arg_copy := arg - arg_ptr := &arg_copy - - for i in 0.. old_len { // zero the gap left by injecting past the end + mem_zero(rawptr(data + uintptr(old_len * type.size)), (index - old_len) * type.size) + } src := data + uintptr(index * type.size) dst := data + uintptr((index + m) * type.size) mem_copy(rawptr(dst), rawptr(src), (n - index) * type.size) - - mem_copy(rawptr(src), rawptr(uintptr(arg_ptr) + uintptr(item_offset)), type.size) - - item_offset += type.size } + + // store the new element via the compiler's #soa element store lowering + array[index] = arg } ok = true @@ -568,7 +569,7 @@ inject_at_elem_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int index: int, #no_ // `inject_at_elems_soa` injects multiple elements in a dynamic SOA array at a specified index and moves the previous elements after that index "across" @builtin -inject_at_elems_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { +inject_at_elems_soa :: proc(#no_alias array: ^$T/#soa[dynamic]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { when !ODIN_NO_BOUNDS_CHECK { ensure(index >= 0, "Index must be positive.", loc) } @@ -580,11 +581,15 @@ inject_at_elems_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int index: int, #no return } - n := max(len(array), index) + old_len := len(array) + n := max(old_len, index) m := len(args) new_len := n + m - resize_soa(array, new_len, loc) or_return + // The tail shift and the stored elements cover every new slot, + // except a gap of [old_len, index) when injecting past the end, which is + // zeroed explicitly below. + non_zero_resize_soa(array, new_len, loc) or_return when size_of(E) != 0 { ti := type_info_base(type_info_of(typeid_of(T))) @@ -592,14 +597,28 @@ inject_at_elems_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int index: int, #no field_count := len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E) - item_offset := 0 - args_ptr := &args[0] + when !intrinsics.type_is_array(E) { + // E's field offsets come from E's own RTTI (si describes the SOA + // struct, whose fields are multipointers); basing on default struct + // layout here would misplace the fields of #packed elements. + se := &type_info_base(si.soa_base_type).variant.(Type_Info_Struct) + } + for i in 0.. old_len { // zero the gap left by injecting past the end + mem_zero(rawptr(data + uintptr(old_len * type.size)), (index - old_len) * type.size) + } src := data + uintptr(index * type.size) dst := data + uintptr((index + m) * type.size) @@ -607,11 +626,9 @@ inject_at_elems_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int index: int, #no for j in 0.. #soa[dynamic]E { // Note: If you the elements to remain in their order, use `ordered_remove_soa`. // Note: If the index is out of bounds, this procedure will panic. @builtin -unordered_remove_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int index: int, loc := #caller_location) #no_bounds_check { +unordered_remove_soa :: proc(#no_alias array: ^$T/#soa[dynamic]$E, #any_int index: int, loc := #caller_location) #no_bounds_check { bounds_check_error_loc(loc, index, len(array)) if index+1 < len(array) { - ti := type_info_of(typeid_of(T)) - ti = type_info_base(ti) - si := &ti.variant.(Type_Info_Struct) - - field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)) - - data := uintptr(array) - for i in 0.. Allocator_Error { - @(require_results) - ceil_log2 :: #force_inline proc "contextless" (x: uintptr) -> uintptr { - z := intrinsics.count_leading_zeros(x) - if z > 0 && x & (x-1) != 0 { - z -= 1 - } - return size_of(uintptr)*8 - 1 - z - } - if m.allocator.procedure == nil { m.allocator = context.allocator } @@ -545,7 +536,7 @@ map_reserve_dynamic :: #force_no_inline proc "odin" (#no_alias m: ^Raw_Map, #no_ } // ceiling nearest power of two - log2_new_capacity := ceil_log2(new_capacity) + log2_new_capacity := __ceil_log2(new_capacity) log2_min_cap := max(MAP_MIN_LOG2_CAPACITY, log2_new_capacity) @@ -592,21 +583,31 @@ map_shrink_dynamic :: #force_no_inline proc "odin" (#no_alias m: ^Raw_Map, #no_a m.allocator = context.allocator } - log2_capacity := map_log2_cap(m^) // Don't shrink below the minimum. - if log2_capacity <= MAP_MIN_LOG2_CAPACITY { + log2_capacity_current := map_log2_cap(m^) + if log2_capacity_current <= MAP_MIN_LOG2_CAPACITY { return false, nil } + // Cannot shrink the capacity if the number of items in the map would exceed // one minus the current log2 capacity's resize threshold. That is the shrunk // map needs to be within the max load factor. - if uintptr(m.len) >= map_load_factor(log2_capacity - 1) { + load_factor_new_max := map_load_factor(log2_capacity_current - 1) + if m.len >= load_factor_new_max { return false, nil } - shrunk := map_alloc_dynamic(info, log2_capacity - 1, m.allocator) or_return + log2_capacity_new := max(__ceil_log2(m.len), MAP_MIN_LOG2_CAPACITY) + load_factor_new := map_load_factor(log2_capacity_new) - capacity := uintptr(1) << log2_capacity + // The new log2 capacity's load factor needs to contain the current map as well. + if m.len > load_factor_new { + log2_capacity_new += 1 + } + + shrunk := map_alloc_dynamic(info, log2_capacity_new, m.allocator) or_return + + capacity := uintptr(1) << log2_capacity_new ks, vs, hs, _, _ := map_kvh_data_dynamic(m^, info) @@ -914,7 +915,6 @@ __dynamic_map_entry :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_ return } - // IMPORTANT: USED WITHIN THE COMPILER @(private) __dynamic_map_reserve :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, new_capacity: uint, loc := #caller_location) -> Allocator_Error { @@ -924,7 +924,20 @@ __dynamic_map_reserve :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Ma return map_reserve_dynamic(m, info, uintptr(new_capacity), loc) } +@(require_results, private) +__ceil_log2 :: #force_inline proc "contextless" (x: uintptr) -> uintptr { + // NOTE(barney): log2(0) is undefined, but 0 is a reasonable return value. + // Alternatively, 8 could be considered as well. + if x == 0 { + return 0 + } + z := intrinsics.count_leading_zeros(x) + if z > 0 && x & (x-1) != 0 { + z -= 1 + } + return size_of(uintptr)*8 - 1 - z +} // NOTE: the default hashing algorithm derives from fnv64a, with some minor modifications to work for `map` type: // diff --git a/base/runtime/random_generator_chacha8_simd128.odin b/base/runtime/random_generator_chacha8_simd128.odin index d63d92620..b0725b2e6 100644 --- a/base/runtime/random_generator_chacha8_simd128.odin +++ b/base/runtime/random_generator_chacha8_simd128.odin @@ -69,7 +69,7 @@ chacha8rand_refill_simd128 :: proc(r: ^Default_Random_State) { s8_ := intrinsics.byte_swap(k[4]) s9_ := intrinsics.byte_swap(k[5]) s10_ := intrinsics.byte_swap(k[6]) - s11_ := intrinicss.byte_swap(k[7]) + s11_ := intrinsics.byte_swap(k[7]) } // 4-lane ChaCha8. diff --git a/core/compress/gzip/doc.odin b/core/compress/gzip/doc.odin index e4b1929dd..e8185482c 100644 --- a/core/compress/gzip/doc.odin +++ b/core/compress/gzip/doc.odin @@ -53,7 +53,7 @@ Example: if file == "-" { // Read from stdin ctx := &compress.Context_Stream_Input{ - input = os.stdin.stream, + input = os.to_stream(os.stdin), } err = gzip.load(ctx, &buf) } else { diff --git a/core/crypto/_fiat/field_scalarp256r1/field.odin b/core/crypto/_fiat/field_scalarp256r1/field.odin index 802f3382e..a2016992f 100644 --- a/core/crypto/_fiat/field_scalarp256r1/field.odin +++ b/core/crypto/_fiat/field_scalarp256r1/field.odin @@ -38,7 +38,7 @@ fe_from_bytes :: proc "contextless" ( out1: ^Montgomery_Domain_Field_Element, arg1: []byte, ) -> bool { - ensure_contextless(len(out1) <= 64, "p256r1: invalid scalar input buffer") + ensure_contextless(len(arg1) <= 64, "p256r1: invalid scalar input buffer") is_canonical := false s_len := len(arg1) diff --git a/core/crypto/_fiat/field_scalarp384r1/field.odin b/core/crypto/_fiat/field_scalarp384r1/field.odin index d465bae44..8c110b6ad 100644 --- a/core/crypto/_fiat/field_scalarp384r1/field.odin +++ b/core/crypto/_fiat/field_scalarp384r1/field.odin @@ -31,7 +31,7 @@ fe_from_bytes :: proc "contextless" ( out1: ^Montgomery_Domain_Field_Element, arg1: []byte, ) -> bool { - ensure_contextless(len(out1) <= 64, "p384r1: invalid scalar input buffer") + ensure_contextless(len(arg1) <= 64, "p384r1: invalid scalar input buffer") is_canonical := false s_len := len(arg1) diff --git a/core/crypto/argon2id/argon2id.odin b/core/crypto/argon2id/argon2id.odin index e2f5e487b..bad83238e 100644 --- a/core/crypto/argon2id/argon2id.odin +++ b/core/crypto/argon2id/argon2id.odin @@ -28,8 +28,8 @@ MIN_TAG_SIZE :: 4 MAX_TAG_SIZE :: (1 << 32) - 1 // RECOMMENDED_TAG_SIZE is the recommended tag size in bytes. -RECOMMENTED_TAG_SIZE :: 32 // 256-bits -// RECOMMENDNED_SALT_SIZE is the recommended salt size in bytes. +RECOMMENDED_TAG_SIZE :: 32 // 256-bits +// RECOMMENDED_SALT_SIZE is the recommended salt size in bytes. RECOMMENDED_SALT_SIZE :: 16 // 128-bits @(private) diff --git a/core/debug/trace/allocator.odin b/core/debug/trace/allocator.odin index 642561bac..febbcc4e1 100644 --- a/core/debug/trace/allocator.odin +++ b/core/debug/trace/allocator.odin @@ -97,6 +97,8 @@ tracking_allocator_init :: proc( tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) { delete(t.allocation_map) delete(t.bad_free_array) + t.allocation_map = {} + t.bad_free_array = {} } @(no_sanitize_address) diff --git a/core/flags/doc.odin b/core/flags/doc.odin index e17cff21b..4aca753f7 100644 --- a/core/flags/doc.odin +++ b/core/flags/doc.odin @@ -52,8 +52,8 @@ Under the `args` tag, there are the following subtags: - `hidden`: hide this flag from the usage documentation. - `required`: cause verification to fail if this argument is not set. - `manifold=N`: take several arguments at once, UNIX-style only. -- `file`: for `os.Handle` types, file open mode. -- `perms`: for `os.Handle` types, file open permissions. +- `file`: for `^os.File` types, file open mode. +- `perms`: for `^os.File` types, file open permissions. - `indistinct`: allow the setting of distinct types by their base type. `required` may be given a range specifier in the following formats: @@ -71,7 +71,7 @@ arguments it consumes at once. If this number is not specified, it will take as many arguments as can be converted to the underlying element type. -`file` determines the file open mode for an `os.Handle`. +`file` determines the file open mode for an `^os.File`. It accepts a string of flags that can be mixed together: - r: read - w: write @@ -80,7 +80,7 @@ It accepts a string of flags that can be mixed together: - t: truncate, erase the file on open -`perms` determines the file open permissions for an `os.Handle`. +`perms` determines the file open permissions for an `^os.File`. The permissions are represented by three numbers in octal format. The first number is the owner, the second is the group, and the third is other. Read is @@ -119,7 +119,7 @@ Supported Flag Data Types: - all bit_sets - `string` and `cstring` - `rune` -- `os.Handle` +- `^os.File` - `time.Time` - `datetime.DateTime` - `net.Host_Or_Endpoint`, diff --git a/core/mem/tracking_allocator.odin b/core/mem/tracking_allocator.odin index d6462faf1..96a417d7a 100644 --- a/core/mem/tracking_allocator.odin +++ b/core/mem/tracking_allocator.odin @@ -82,6 +82,8 @@ Destroy the tracking allocator. tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) { delete(t.allocation_map) delete(t.bad_free_array) + t.allocation_map = {} + t.bad_free_array = {} } /* diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 2ad291aef..3b2a1481e 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -2651,7 +2651,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { specialization: ^ast.Expr if allow_token(p, .Quo) { specialization = parse_type(p) - end = specialization.pos + end = specialization.end } if is_blank_ident(type) { error(p, type.pos, "invalid polymorphic type definition with a blank identifier") diff --git a/core/os/path.odin b/core/os/path.odin index 22ec2d3f7..9f2f33ebb 100644 --- a/core/os/path.odin +++ b/core/os/path.odin @@ -746,7 +746,7 @@ glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []str if !has_meta(pattern) { // TODO(bill): os.lstat on here to check for error m := make([]string, 1) - m[0] = pattern + m[0] = strings.clone(pattern) return m[:], nil } diff --git a/core/rexcode/docs/x86_api.md b/core/rexcode/docs/x86_api.md index 358367e8e..2f913159d 100644 --- a/core/rexcode/docs/x86_api.md +++ b/core/rexcode/docs/x86_api.md @@ -122,6 +122,7 @@ Memory :: bit_field u64 { addr_size_override: bool | 1, base_class: u8 | 5, index_class: u8 | 5, + disp_is_label: bool | 1, // disp holds a label id -> REL32 relocation } MEM_BASE_RIP :: 30 MEM_BASE_NONE :: 31 MEM_INDEX_NONE :: 31 ``` @@ -131,7 +132,17 @@ MEM_BASE_RIP :: 30 MEM_BASE_NONE :: 31 MEM_INDEX_NONE :: 31 **Convenience constructors** (current names after the in-tree refactor): `mem_base_only(base)`, `mem_base_disp(base, disp)`, `mem_base_index(base, index, scale)`, -`mem_base_index_disp(base, index, scale, disp)`, `mem_rip_disp(disp)`. +`mem_base_index_disp(base, index, scale, disp)`, `mem_rip_disp(disp)`, +`mem_rip_label(label_id)`. + +> **Label addressing.** `mem_rip_label(label_id)` builds a RIP-relative operand +> whose displacement references a **label** rather than a literal: the encoder +> writes a placeholder disp32 and emits a `REL32` relocation for `label_id` at +> that field (addend 0), exactly as the `.RELATIVE` jump/call path does. This is +> how `lea reg, [rip +