diff --git a/base/runtime/internal.odin b/base/runtime/internal.odin index e08d0e01d..a682f7e76 100644 --- a/base/runtime/internal.odin +++ b/base/runtime/internal.odin @@ -1032,9 +1032,9 @@ quo_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 { invmag2 := 1.0 / (r0*r0 + r1*r1 + r2*r2 + r3*r3) t0 := (r0*q0 + r1*q1 + r2*q2 + r3*q3) * invmag2 - t1 := (r0*q1 - r1*q0 - r2*q3 - r3*q2) * invmag2 + t1 := (r0*q1 - r1*q0 + r2*q3 - r3*q2) * invmag2 t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2 - t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2 + t3 := (r0*q3 + r1*q2 - r2*q1 - r3*q0) * invmag2 return quaternion(w=f16(t0), x=f16(t1), y=f16(t2), z=f16(t3)) } @@ -1046,9 +1046,9 @@ quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 { invmag2 := 1.0 / (r0*r0 + r1*r1 + r2*r2 + r3*r3) t0 := (r0*q0 + r1*q1 + r2*q2 + r3*q3) * invmag2 - t1 := (r0*q1 - r1*q0 - r2*q3 - r3*q2) * invmag2 + t1 := (r0*q1 - r1*q0 + r2*q3 - r3*q2) * invmag2 t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2 - t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2 + t3 := (r0*q3 + r1*q2 - r2*q1 - r3*q0) * invmag2 return quaternion(w=t0, x=t1, y=t2, z=t3) } @@ -1060,9 +1060,9 @@ quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 { invmag2 := 1.0 / (r0*r0 + r1*r1 + r2*r2 + r3*r3) t0 := (r0*q0 + r1*q1 + r2*q2 + r3*q3) * invmag2 - t1 := (r0*q1 - r1*q0 - r2*q3 - r3*q2) * invmag2 + t1 := (r0*q1 - r1*q0 + r2*q3 - r3*q2) * invmag2 t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2 - t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2 + t3 := (r0*q3 + r1*q2 - r2*q1 - r3*q0) * invmag2 return quaternion(w=t0, x=t1, y=t2, z=t3) } diff --git a/base/runtime/random_generator_chacha8_ref.odin b/base/runtime/random_generator_chacha8_ref.odin index b1e812c3f..e3e1d5768 100644 --- a/base/runtime/random_generator_chacha8_ref.odin +++ b/base/runtime/random_generator_chacha8_ref.odin @@ -23,7 +23,7 @@ chacha8rand_refill_ref :: 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]) } s12: u32 // Counter starts at 0. s13, s14, s15: u32 // IV of all 0s. diff --git a/core/crypto/_chacha20/chacha20.odin b/core/crypto/_chacha20/chacha20.odin index 7d94d8a95..40ca9fb25 100644 --- a/core/crypto/_chacha20/chacha20.odin +++ b/core/crypto/_chacha20/chacha20.odin @@ -1,5 +1,6 @@ package _chacha20 +import "base:intrinsics" import "core:crypto" import "core:encoding/endian" import "core:math/bits" @@ -108,11 +109,16 @@ check_counter_limit :: proc(ctx: ^Context, nr_blocks: int) { ctr_ok: bool if ctx._is_ietf_flavor { - ctr_ok = u64(ctx._s[12]) + u64(nr_blocks) <= MAX_CTR_IETF + if intrinsics.unlikely(ctx._s[12] == MAX_CTR_IETF && nr_blocks > 1) { + // Allow the final block. + ctr_ok = false + } else { + ctr_ok = u64(ctx._s[12]) + u64(nr_blocks) <= MAX_CTR_IETF + } } else { ctr := (u64(ctx._s[13]) << 32) | u64(ctx._s[12]) - _, carry := bits.add_u64(ctr, u64(nr_blocks), 0) - ctr_ok = carry == 0 + new_ctr, carry := bits.add_u64(ctr, u64(nr_blocks), 0) + ctr_ok = carry == 0 || new_ctr != 0 // Allow the final block. } ensure(ctr_ok, "crypto/chacha20: maximum (X)ChaCha20 keystream per IV reached") diff --git a/core/crypto/kmac/kmac.odin b/core/crypto/kmac/kmac.odin index f0c27739a..f09176edc 100644 --- a/core/crypto/kmac/kmac.odin +++ b/core/crypto/kmac/kmac.odin @@ -35,6 +35,10 @@ sum :: proc(sec_strength: int, dst, msg, key, domain_sep: []byte) { // strength, key and domain separator over msg and return true if and only if (⟺) the // tag is valid. verify :: proc(sec_strength: int, tag, msg, key, domain_sep: []byte, allocator := context.temp_allocator) -> bool { + if len(tag) < MIN_TAG_SIZE { + return false + } + derived_tag := make([]byte, len(tag), allocator) defer(delete(derived_tag)) diff --git a/core/crypto/mlkem/api.odin b/core/crypto/mlkem/api.odin index 3e3956ba8..6cbbd3249 100644 --- a/core/crypto/mlkem/api.odin +++ b/core/crypto/mlkem/api.odin @@ -269,7 +269,7 @@ params :: proc(k: ^$T) -> Parameters where (T == Encapsulation_Key || T == Decap @(require_results) key_size :: proc(k: ^$T) -> int where (T == Encapsulation_Key || T == Decapsulation_Key) { when T == Encapsulation_Key { - return ENCAPSULATION_KEY_SIZES[k.pke_ek.k] + return ENCAPSULATION_KEY_SIZES[params(k)] } else { return DECAPSULATION_KEY_SEED_SIZE } diff --git a/core/crypto/noise/protocol.odin b/core/crypto/noise/protocol.odin index 7327e638b..4bbe858da 100644 --- a/core/crypto/noise/protocol.odin +++ b/core/crypto/noise/protocol.odin @@ -217,7 +217,7 @@ cipherstate_encrypt_with_ad :: proc(self: ^Cipher_State, ad, plaintext, dst: []b } _encrypt(&self.ctx, self.n, ad, plaintext, dst) self.n += 1 - if self.n == 0 { + if self.n == max(u64) { self.n_exhausted = true } } else { @@ -249,7 +249,7 @@ cipherstate_decrypt_with_ad :: proc(self: ^Cipher_State, ad, ciphertext, dst: [] return nil, status } self.n += 1 - if self.n == 0 { + if self.n == max(u64) { self.n_exhausted = true } } else { diff --git a/core/crypto/pbkdf2/pbkdf2.odin b/core/crypto/pbkdf2/pbkdf2.odin index c27ec4aa2..ef7fda0d5 100644 --- a/core/crypto/pbkdf2/pbkdf2.odin +++ b/core/crypto/pbkdf2/pbkdf2.odin @@ -19,6 +19,8 @@ derive :: proc( iterations: u32, dst: []byte, ) { + ensure(iterations > 0, "crypto/pbkdf2: non-zero iterations required") + h_len := hash.DIGEST_SIZES[hmac_hash] // 1. If dkLen > (2^32 - 1) * hLen, output "derived key too long" diff --git a/core/net/url.odin b/core/net/url.odin index 29028b16c..b5bd6d917 100644 --- a/core/net/url.odin +++ b/core/net/url.odin @@ -19,7 +19,6 @@ package net */ import "core:strings" -import "core:strconv" import "core:unicode/utf8" import "core:encoding/hex" @@ -114,6 +113,8 @@ join_url :: proc(scheme, host, path: string, queries: map[string]string, fragmen } percent_encode :: proc(s: string, allocator := context.allocator) -> string { + HEX_DIGITS_UPPER := "0123456789ABCDEF" // NOTE(michtesar): RFC 3986 §2.1 + b := strings.builder_make(allocator) strings.builder_grow(&b, len(s) + 16) // NOTE(tetra): A reasonable number to allow for the number of things we need to escape. @@ -124,10 +125,9 @@ percent_encode :: proc(s: string, allocator := context.allocator) -> string { case: bytes, n := utf8.encode_rune(ch) for byte in bytes[:n] { - buf: [2]u8 = --- - t := strconv.write_int(buf[:], i64(byte), 16) - strings.write_rune(&b, '%') - strings.write_string(&b, t) + strings.write_byte(&b, '%') + strings.write_byte(&b, HEX_DIGITS_UPPER[byte >> 4]) + strings.write_byte(&b, HEX_DIGITS_UPPER[byte & 0xF]) } } } diff --git a/core/os/path_windows.odin b/core/os/path_windows.odin index 83100ad69..753f1665f 100644 --- a/core/os/path_windows.odin +++ b/core/os/path_windows.odin @@ -84,7 +84,13 @@ _remove_all :: proc(path: string) -> Error { } temp_allocator := TEMP_ALLOCATOR_GUARD({}) - dir := win32_utf8_to_wstring(path, temp_allocator) or_return + + // SHFileOperationW is documented as not thread safe with relative paths. + abs_path := path + if !_is_absolute_path(path) { + abs_path = _get_absolute_path(path, temp_allocator) or_return + } + dir := win32_utf8_to_wstring(abs_path, temp_allocator) or_return empty: [1]u16 diff --git a/core/simd/arm/neon.odin b/core/simd/arm/neon.odin new file mode 100644 index 000000000..512449d27 --- /dev/null +++ b/core/simd/arm/neon.odin @@ -0,0 +1,596 @@ +#+build arm64,arm32 +package simd_arm + +import "core:simd" + +// Count leading sign bits. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vcls_s8) +@(require_results, enable_target_feature = "neon") +vcls_s8 :: #force_inline proc "c" (a: int8x8_t) -> int8x8_t { + return _vcls_s8(a) +} + +// Count leading sign bits. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vcls_s16) +@(require_results, enable_target_feature = "neon") +vcls_s16 :: #force_inline proc "c" (a: int16x4_t) -> int16x4_t { + return _vcls_s16(a) +} + +// Count leading sign bits. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vcls_s32) +@(require_results, enable_target_feature = "neon") +vcls_s32 :: #force_inline proc "c" (a: int32x2_t) -> int32x2_t { + return _vcls_s32(a) +} + +// Count leading sign bits. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vcls_u8) +@(require_results, enable_target_feature = "neon") +vcls_u8 :: #force_inline proc "c" (a: uint8x8_t) -> int8x8_t { + return vcls_s8(transmute(int8x8_t)a) +} + +// Count leading sign bits. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vcls_u16) +@(require_results, enable_target_feature = "neon") +vcls_u16 :: #force_inline proc "c" (a: uint16x4_t) -> int16x4_t { + return vcls_s16(transmute(int16x4_t)a) +} + +// Count leading sign bits. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vcls_u32) +@(require_results, enable_target_feature = "neon") +vcls_u32 :: #force_inline proc "c" (a: uint32x2_t) -> int32x2_t { + return vcls_s32(transmute(int32x2_t)a) +} + +// Count leading sign bits. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vclsq_s8) +@(require_results, enable_target_feature = "neon") +vclsq_s8 :: #force_inline proc "c" (a: int8x16_t) -> int8x16_t { + return _vclsq_s8(a) +} + +// Count leading sign bits. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vclsq_s16) +@(require_results, enable_target_feature = "neon") +vclsq_s16 :: #force_inline proc "c" (a: int16x8_t) -> int16x8_t { + return _vclsq_s16(a) +} + +// Count leading sign bits. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vclsq_s32) +@(require_results, enable_target_feature = "neon") +vclsq_s32 :: #force_inline proc "c" (a: int32x4_t) -> int32x4_t { + return _vclsq_s32(a) +} + +// Count leading sign bits. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vclsq_u8) +@(require_results, enable_target_feature = "neon") +vclsq_u8 :: #force_inline proc "c" (a: uint8x16_t) -> int8x16_t { + return vclsq_s8(transmute(int8x16_t)a) +} + +// Count leading sign bits. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vclsq_u16) +@(require_results, enable_target_feature = "neon") +vclsq_u16 :: #force_inline proc "c" (a: uint16x8_t) -> int16x8_t { + return vclsq_s16(transmute(int16x8_t)a) +} + +// Count leading sign bits. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vclsq_u32) +@(require_results, enable_target_feature = "neon") +vclsq_u32 :: #force_inline proc "c" (a: uint32x4_t) -> int32x4_t { + return vclsq_s32(transmute(int32x4_t)a) +} + +// Count leading zero bits. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vclz_s8) +@(require_results, enable_target_feature = "neon") +vclz_s8 :: #force_inline proc "c" (a: int8x8_t) -> int8x8_t { + return simd.count_leading_zeros(a) +} + +// Count leading zero bits. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vclz_s16) +@(require_results, enable_target_feature = "neon") +vclz_s16 :: #force_inline proc "c" (a: int16x4_t) -> int16x4_t { + return simd.count_leading_zeros(a) +} + +// Count leading zero bits. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vclz_s32) +@(require_results, enable_target_feature = "neon") +vclz_s32 :: #force_inline proc "c" (a: int32x2_t) -> int32x2_t { + return simd.count_leading_zeros(a) +} + +// Count leading zero bits. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vclz_u8) +@(require_results, enable_target_feature = "neon") +vclz_u8 :: #force_inline proc "c" (a: uint8x8_t) -> uint8x8_t { + return transmute(uint8x8_t)vclz_s8(transmute(int8x8_t)a) +} + +// Count leading zero bits. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vclz_u16) +@(require_results, enable_target_feature = "neon") +vclz_u16 :: #force_inline proc "c" (a: uint16x4_t) -> uint16x4_t { + return transmute(uint16x4_t)vclz_s16(transmute(int16x4_t)a) +} + +// Count leading zero bits. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vclz_u32) +@(require_results, enable_target_feature = "neon") +vclz_u32 :: #force_inline proc "c" (a: uint32x2_t) -> uint32x2_t { + return transmute(uint32x2_t)vclz_s32(transmute(int32x2_t)a) +} + +// Count leading zero bits. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vclzq_s8) +@(require_results, enable_target_feature = "neon") +vclzq_s8 :: #force_inline proc "c" (a: int8x16_t) -> int8x16_t { + return simd.count_leading_zeros(a) +} + +// Count leading zero bits. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vclzq_s16) +@(require_results, enable_target_feature = "neon") +vclzq_s16 :: #force_inline proc "c" (a: int16x8_t) -> int16x8_t { + return simd.count_leading_zeros(a) +} + +// Count leading zero bits. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vclzq_s32) +@(require_results, enable_target_feature = "neon") +vclzq_s32 :: #force_inline proc "c" (a: int32x4_t) -> int32x4_t { + return simd.count_leading_zeros(a) +} + +// Count leading zero bits. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vclzq_u8) +@(require_results, enable_target_feature = "neon") +vclzq_u8 :: #force_inline proc "c" (a: uint8x16_t) -> uint8x16_t { + return transmute(uint8x16_t)vclzq_s8(transmute(int8x16_t)a) +} + +// Count leading zero bits. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vclzq_u16) +@(require_results, enable_target_feature = "neon") +vclzq_u16 :: #force_inline proc "c" (a: uint16x8_t) -> uint16x8_t { + return transmute(uint16x8_t)vclzq_s16(transmute(int16x8_t)a) +} + +// Count leading zero bits. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vclzq_u32) +@(require_results, enable_target_feature = "neon") +vclzq_u32 :: #force_inline proc "c" (a: uint32x4_t) -> uint32x4_t { + return transmute(uint32x4_t)vclzq_s32(transmute(int32x4_t)a) +} + +// Population count per byte. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vcnt_s8) +@(require_results, enable_target_feature = "neon") +vcnt_s8 :: #force_inline proc "c" (a: int8x8_t) -> int8x8_t { + return simd.count_ones(a) +} + +// Population count per byte. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vcnt_u8) +@(require_results, enable_target_feature = "neon") +vcnt_u8 :: #force_inline proc "c" (a: uint8x8_t) -> uint8x8_t { + return transmute(uint8x8_t)vcnt_s8(transmute(int8x8_t)a) +} + +// Population count per byte. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vcnt_p8) +@(require_results, enable_target_feature = "neon") +vcnt_p8 :: #force_inline proc "c" (a: poly8x8_t) -> poly8x8_t { + return transmute(poly8x8_t)vcnt_s8(transmute(int8x8_t)a) +} + +// Population count per byte. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vcntq_s8) +@(require_results, enable_target_feature = "neon") +vcntq_s8 :: #force_inline proc "c" (a: int8x16_t) -> int8x16_t { + return simd.count_ones(a) +} + +// Population count per byte. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vcntq_u8) +@(require_results, enable_target_feature = "neon") +vcntq_u8 :: #force_inline proc "c" (a: uint8x16_t) -> uint8x16_t { + return transmute(uint8x16_t)vcntq_s8(transmute(int8x16_t)a) +} + +// Population count per byte. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vcntq_p8) +@(require_results, enable_target_feature = "neon") +vcntq_p8 :: #force_inline proc "c" (a: poly8x16_t) -> poly8x16_t { + return transmute(poly8x16_t)vcntq_s8(transmute(int8x16_t)a) +} + +// Vector bitwise bit clear. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbic_s8) +@(require_results, enable_target_feature = "neon") +vbic_s8 :: #force_inline proc "c" (a: int8x8_t, b: int8x8_t) -> int8x8_t { + c := int8x8_t(-1) + return simd.bit_and(simd.bit_xor(b, c), a) +} + +// Vector bitwise bit clear. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbic_s16) +@(require_results, enable_target_feature = "neon") +vbic_s16 :: #force_inline proc "c" (a: int16x4_t, b: int16x4_t) -> int16x4_t { + c := int16x4_t(-1) + return simd.bit_and(simd.bit_xor(b, c), a) +} + +// Vector bitwise bit clear. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbic_s32) +@(require_results, enable_target_feature = "neon") +vbic_s32 :: #force_inline proc "c" (a: int32x2_t, b: int32x2_t) -> int32x2_t { + c := int32x2_t(-1) + return simd.bit_and(simd.bit_xor(b, c), a) +} + +// Vector bitwise bit clear. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbic_s64) +@(require_results, enable_target_feature = "neon") +vbic_s64 :: #force_inline proc "c" (a: int64x1_t, b: int64x1_t) -> int64x1_t { + c := int64x1_t(-1) + return simd.bit_and(simd.bit_xor(b, c), a) +} + +// Vector bitwise bit clear. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbic_u8) +@(require_results, enable_target_feature = "neon") +vbic_u8 :: #force_inline proc "c" (a: uint8x8_t, b: uint8x8_t) -> uint8x8_t { + c := int8x8_t(-1) + return simd.bit_and(simd.bit_xor(b, transmute(uint8x8_t)c), a) +} + +// Vector bitwise bit clear. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbic_u16) +@(require_results, enable_target_feature = "neon") +vbic_u16 :: #force_inline proc "c" (a: uint16x4_t, b: uint16x4_t) -> uint16x4_t { + c := int16x4_t(-1) + return simd.bit_and(simd.bit_xor(b, transmute(uint16x4_t)c), a) +} + +// Vector bitwise bit clear. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbic_u32) +@(require_results, enable_target_feature = "neon") +vbic_u32 :: #force_inline proc "c" (a: uint32x2_t, b: uint32x2_t) -> uint32x2_t { + c := int32x2_t(-1) + return simd.bit_and(simd.bit_xor(b, transmute(uint32x2_t)c), a) +} + +// Vector bitwise bit clear. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbic_u64) +@(require_results, enable_target_feature = "neon") +vbic_u64 :: #force_inline proc "c" (a: uint64x1_t, b: uint64x1_t) -> uint64x1_t { + c := int64x1_t(-1) + return simd.bit_and(simd.bit_xor(b, transmute(uint64x1_t)c), a) +} + +// Vector bitwise bit clear. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbicq_s8) +@(require_results, enable_target_feature = "neon") +vbicq_s8 :: #force_inline proc "c" (a: int8x16_t, b: int8x16_t) -> int8x16_t { + c := int8x16_t(-1) + return simd.bit_and(simd.bit_xor(b, c), a) +} + +// Vector bitwise bit clear. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbicq_s16) +@(require_results, enable_target_feature = "neon") +vbicq_s16 :: #force_inline proc "c" (a: int16x8_t, b: int16x8_t) -> int16x8_t { + c := int16x8_t(-1) + return simd.bit_and(simd.bit_xor(b, c), a) +} + +// Vector bitwise bit clear. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbicq_s32) +@(require_results, enable_target_feature = "neon") +vbicq_s32 :: #force_inline proc "c" (a: int32x4_t, b: int32x4_t) -> int32x4_t { + c := int32x4_t(-1) + return simd.bit_and(simd.bit_xor(b, c), a) +} + +// Vector bitwise bit clear. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbicq_s64) +@(require_results, enable_target_feature = "neon") +vbicq_s64 :: #force_inline proc "c" (a: int64x2_t, b: int64x2_t) -> int64x2_t { + c := int64x2_t(-1) + return simd.bit_and(simd.bit_xor(b, c), a) +} + +// Vector bitwise bit clear. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbicq_u8) +@(require_results, enable_target_feature = "neon") +vbicq_u8 :: #force_inline proc "c" (a: uint8x16_t, b: uint8x16_t) -> uint8x16_t { + c := int8x16_t(-1) + return simd.bit_and(simd.bit_xor(b, transmute(uint8x16_t)c), a) +} + +// Vector bitwise bit clear. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbicq_u16) +@(require_results, enable_target_feature = "neon") +vbicq_u16 :: #force_inline proc "c" (a: uint16x8_t, b: uint16x8_t) -> uint16x8_t { + c := int16x8_t(-1) + return simd.bit_and(simd.bit_xor(b, transmute(uint16x8_t)c), a) +} + +// Vector bitwise bit clear. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbicq_u32) +@(require_results, enable_target_feature = "neon") +vbicq_u32 :: #force_inline proc "c" (a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { + c := int32x4_t(-1) + return simd.bit_and(simd.bit_xor(b, transmute(uint32x4_t)c), a) +} + +// Vector bitwise bit clear. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbicq_u64) +@(require_results, enable_target_feature = "neon") +vbicq_u64 :: #force_inline proc "c" (a: uint64x2_t, b: uint64x2_t) -> uint64x2_t { + c := int64x2_t(-1) + return simd.bit_and(simd.bit_xor(b, transmute(uint64x2_t)c), a) +} + +// Bitwise Select. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbsl_s8) +@(require_results, enable_target_feature = "neon") +vbsl_s8 :: #force_inline proc "c" (a: uint8x8_t, b: int8x8_t, c: int8x8_t) -> int8x8_t { + not := int8x8_t(-1) + return transmute(int8x8_t)simd.bit_or( + simd.bit_and(a, transmute(uint8x8_t)b), + simd.bit_and(simd.bit_xor(a, transmute(uint8x8_t)not), transmute(uint8x8_t)c), + ) +} + +// Bitwise Select. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbsl_s16) +@(require_results, enable_target_feature = "neon") +vbsl_s16 :: #force_inline proc "c" (a: uint16x4_t, b: int16x4_t, c: int16x4_t) -> int16x4_t { + not := int16x4_t(-1) + return transmute(int16x4_t)simd.bit_or( + simd.bit_and(a, transmute(uint16x4_t)b), + simd.bit_and(simd.bit_xor(a, transmute(uint16x4_t)not), transmute(uint16x4_t)c), + ) +} + +// Bitwise Select. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbsl_s32) +@(require_results, enable_target_feature = "neon") +vbsl_s32 :: #force_inline proc "c" (a: uint32x2_t, b: int32x2_t, c: int32x2_t) -> int32x2_t { + not := int32x2_t(-1) + return transmute(int32x2_t)simd.bit_or( + simd.bit_and(a, transmute(uint32x2_t)b), + simd.bit_and(simd.bit_xor(a, transmute(uint32x2_t)not), transmute(uint32x2_t)c), + ) +} + +// Bitwise Select. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbsl_s64) +@(require_results, enable_target_feature = "neon") +vbsl_s64 :: #force_inline proc "c" (a: uint64x1_t, b: int64x1_t, c: int64x1_t) -> int64x1_t { + not := int64x1_t(-1) + return transmute(int64x1_t)simd.bit_or( + simd.bit_and(a, transmute(uint64x1_t)b), + simd.bit_and(simd.bit_xor(a, transmute(uint64x1_t)not), transmute(uint64x1_t)c), + ) +} + +// Bitwise Select. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbsl_u8) +@(require_results, enable_target_feature = "neon") +vbsl_u8 :: #force_inline proc "c" (a: uint8x8_t, b: uint8x8_t, c: uint8x8_t) -> uint8x8_t { + not := int8x8_t(-1) + return simd.bit_or( + simd.bit_and(a, b), + simd.bit_and(simd.bit_xor(a, transmute(uint8x8_t)not), c), + ) +} + +// Bitwise Select. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbsl_u16) +@(require_results, enable_target_feature = "neon") +vbsl_u16 :: #force_inline proc "c" (a: uint16x4_t, b: uint16x4_t, c: uint16x4_t) -> uint16x4_t { + not := int16x4_t(-1) + return simd.bit_or( + simd.bit_and(a, b), + simd.bit_and(simd.bit_xor(a, transmute(uint16x4_t)not), c), + ) +} + +// Bitwise Select. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbsl_u32) +@(require_results, enable_target_feature = "neon") +vbsl_u32 :: #force_inline proc "c" (a: uint32x2_t, b: uint32x2_t, c: uint32x2_t) -> uint32x2_t { + not := int32x2_t(-1) + return simd.bit_or( + simd.bit_and(a, b), + simd.bit_and(simd.bit_xor(a, transmute(uint32x2_t)not), c), + ) +} + +// Bitwise Select. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbsl_u64) +@(require_results, enable_target_feature = "neon") +vbsl_u64 :: #force_inline proc "c" (a: uint64x1_t, b: uint64x1_t, c: uint64x1_t) -> uint64x1_t { + not := int64x1_t(-1) + return simd.bit_or( + simd.bit_and(a, b), + simd.bit_and(simd.bit_xor(a, transmute(uint64x1_t)not), c), + ) +} + +// Bitwise Select. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbslq_s8) +@(require_results, enable_target_feature = "neon") +vbslq_s8 :: #force_inline proc "c" (a: uint8x16_t, b: int8x16_t, c: int8x16_t) -> int8x16_t { + not := int8x16_t(-1) + return transmute(int8x16_t)simd.bit_or( + simd.bit_and(a, transmute(uint8x16_t)b), + simd.bit_and(simd.bit_xor(a, transmute(uint8x16_t)not), transmute(uint8x16_t)c), + ) +} + +// Bitwise Select. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbslq_s16) +@(require_results, enable_target_feature = "neon") +vbslq_s16 :: #force_inline proc "c" (a: uint16x8_t, b: int16x8_t, c: int16x8_t) -> int16x8_t { + not := int16x8_t(-1) + return transmute(int16x8_t)simd.bit_or( + simd.bit_and(a, transmute(uint16x8_t)b), + simd.bit_and(simd.bit_xor(a, transmute(uint16x8_t)not), transmute(uint16x8_t)c), + ) +} + +// Bitwise Select. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbslq_s32) +@(require_results, enable_target_feature = "neon") +vbslq_s32 :: #force_inline proc "c" (a: uint32x4_t, b: int32x4_t, c: int32x4_t) -> int32x4_t { + not := int32x4_t(-1) + return transmute(int32x4_t)simd.bit_or( + simd.bit_and(a, transmute(uint32x4_t)b), + simd.bit_and(simd.bit_xor(a, transmute(uint32x4_t)not), transmute(uint32x4_t)c), + ) +} + +// Bitwise Select. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbslq_s64) +@(require_results, enable_target_feature = "neon") +vbslq_s64 :: #force_inline proc "c" (a: uint64x2_t, b: int64x2_t, c: int64x2_t) -> int64x2_t { + not := int64x2_t(-1) + return transmute(int64x2_t)simd.bit_or( + simd.bit_and(a, transmute(uint64x2_t)b), + simd.bit_and(simd.bit_xor(a, transmute(uint64x2_t)not), transmute(uint64x2_t)c), + ) +} + +// Bitwise Select. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbslq_u8) +@(require_results, enable_target_feature = "neon") +vbslq_u8 :: #force_inline proc "c" (a: uint8x16_t, b: uint8x16_t, c: uint8x16_t) -> uint8x16_t { + not := int8x16_t(-1) + return simd.bit_or( + simd.bit_and(a, b), + simd.bit_and(simd.bit_xor(a, transmute(uint8x16_t)not), c), + ) +} + +// Bitwise Select. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbslq_u16) +@(require_results, enable_target_feature = "neon") +vbslq_u16 :: #force_inline proc "c" (a: uint16x8_t, b: uint16x8_t, c: uint16x8_t) -> uint16x8_t { + not := int16x8_t(-1) + return simd.bit_or( + simd.bit_and(a, b), + simd.bit_and(simd.bit_xor(a, transmute(uint16x8_t)not), c), + ) +} + +// Bitwise Select. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbslq_u32) +@(require_results, enable_target_feature = "neon") +vbslq_u32 :: #force_inline proc "c" (a: uint32x4_t, b: uint32x4_t, c: uint32x4_t) -> uint32x4_t { + not := int32x4_t(-1) + return simd.bit_or( + simd.bit_and(a, b), + simd.bit_and(simd.bit_xor(a, transmute(uint32x4_t)not), c), + ) +} + +// Bitwise Select. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbslq_u64) +@(require_results, enable_target_feature = "neon") +vbslq_u64 :: #force_inline proc "c" (a: uint64x2_t, b: uint64x2_t, c: uint64x2_t) -> uint64x2_t { + not := int64x2_t(-1) + return simd.bit_or( + simd.bit_and(a, b), + simd.bit_and(simd.bit_xor(a, transmute(uint64x2_t)not), c), + ) +} + +@(private, default_calling_convention = "none") +foreign _ { + @(link_name = "llvm.aarch64.neon.cls.v8i8" when ODIN_ARCH == .arm64 else "llvm.arm.neon.vcls.v8i8") + _vcls_s8 :: proc(a: int8x8_t) -> int8x8_t --- + @(link_name = "llvm.aarch64.neon.cls.v4i16" when ODIN_ARCH == .arm64 else "llvm.arm.neon.vcls.v4i16") + _vcls_s16 :: proc(a: int16x4_t) -> int16x4_t --- + @(link_name = "llvm.aarch64.neon.cls.v2i32" when ODIN_ARCH == .arm64 else "llvm.arm.neon.vcls.v2i32") + _vcls_s32 :: proc(a: int32x2_t) -> int32x2_t --- + @(link_name = "llvm.aarch64.neon.cls.v16i8" when ODIN_ARCH == .arm64 else "llvm.arm.neon.vcls.v16i8") + _vclsq_s8 :: proc(a: int8x16_t) -> int8x16_t --- + @(link_name = "llvm.aarch64.neon.cls.v8i16" when ODIN_ARCH == .arm64 else "llvm.arm.neon.vcls.v8i16") + _vclsq_s16 :: proc(a: int16x8_t) -> int16x8_t --- + @(link_name = "llvm.aarch64.neon.cls.v4i32" when ODIN_ARCH == .arm64 else "llvm.arm.neon.vcls.v4i32") + _vclsq_s32 :: proc(a: int32x4_t) -> int32x4_t --- +} diff --git a/core/simd/arm/pmull.odin b/core/simd/arm/pmull.odin index 8d0707da9..023d4022a 100644 --- a/core/simd/arm/pmull.odin +++ b/core/simd/arm/pmull.odin @@ -3,6 +3,78 @@ package simd_arm import "core:simd" +// Bitwise Select. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbsl_p8) +@(require_results, enable_target_feature = "neon") +vbsl_p8 :: #force_inline proc "c" (a: uint8x8_t, b: poly8x8_t, c: poly8x8_t) -> poly8x8_t { + not := int8x8_t(-1) + return simd.bit_or( + simd.bit_and(a, b), + simd.bit_and(simd.bit_xor(a, transmute(uint8x8_t)not), c), + ) +} + +// Bitwise Select. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbsl_p16) +@(require_results, enable_target_feature = "neon") +vbsl_p16 :: #force_inline proc "c" (a: uint16x4_t, b: poly16x4_t, c: poly16x4_t) -> poly16x4_t { + not := int16x4_t(-1) + return simd.bit_or( + simd.bit_and(a, b), + simd.bit_and(simd.bit_xor(a, transmute(uint16x4_t)not), c), + ) +} + +// Bitwise Select. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbsl_p64) +@(require_results, enable_target_feature = "neon") +vbsl_p64 :: #force_inline proc "c" (a: poly64x1_t, b: poly64x1_t, c: poly64x1_t) -> poly64x1_t { + not := int64x1_t(-1) + return simd.bit_or( + simd.bit_and(a, b), + simd.bit_and(simd.bit_xor(a, transmute(poly64x1_t)not), c), + ) +} + +// Bitwise Select. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbslq_p8) +@(require_results, enable_target_feature = "neon") +vbslq_p8 :: #force_inline proc "c" (a: uint8x16_t, b: poly8x16_t, c: poly8x16_t) -> poly8x16_t { + not := int8x16_t(-1) + return simd.bit_or( + simd.bit_and(a, b), + simd.bit_and(simd.bit_xor(a, transmute(poly8x16_t)not), c), + ) +} + +// Bitwise Select. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbslq_p16) +@(require_results, enable_target_feature = "neon") +vbslq_p16 :: #force_inline proc "c" (a: uint16x8_t, b: poly16x8_t, c: poly16x8_t) -> poly16x8_t { + not := int16x8_t(-1) + return simd.bit_or( + simd.bit_and(a, b), + simd.bit_and(simd.bit_xor(a, transmute(poly16x8_t)not), c), + ) +} + +// Bitwise Select. +// +// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vbslq_p64) +@(require_results, enable_target_feature = "neon") +vbslq_p64 :: #force_inline proc "c" (a: poly64x2_t, b: poly64x2_t, c: poly64x2_t) -> poly64x2_t { + not := int64x2_t(-1) + return simd.bit_or( + simd.bit_and(a, b), + simd.bit_and(simd.bit_xor(a, transmute(poly64x2_t)not), c), + ) +} + // Join two smaller vectors into a single larger vector // // [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vcombine_p8) diff --git a/core/simd/arm/types.odin b/core/simd/arm/types.odin index 9379449e3..f60edeaa8 100644 --- a/core/simd/arm/types.odin +++ b/core/simd/arm/types.odin @@ -2,6 +2,11 @@ package simd_arm // Type aliases to match `arm_neon.h`. +int8_t :: i8 +int16_t :: i16 +int32_t :: i32 +int64_t :: i64 + uint8_t :: u8 uint16_t :: u16 uint32_t :: u32 @@ -12,12 +17,23 @@ poly16_t :: u16 poly64_t :: u64 poly128_t :: u128 -uint8x16_t :: #simd[16]u8 -uint32x4_t :: #simd[4]u32 -uint64x2_t :: #simd[2]u64 +int8x8_t :: #simd[8]int8_t +int8x16_t :: #simd[16]int8_t +int16x4_t :: #simd[4]int16_t +int16x8_t :: #simd[8]int16_t +int32x2_t :: #simd[2]int32_t +int32x4_t :: #simd[4]int32_t +int64x1_t :: #simd[1]int64_t +int64x2_t :: #simd[2]int64_t -int32_t :: i32 -int8x16_t :: #simd[16]i8 +uint8x8_t :: #simd[8]uint8_t +uint8x16_t :: #simd[16]uint8_t +uint16x4_t :: #simd[4]uint16_t +uint16x8_t :: #simd[8]uint16_t +uint32x2_t :: #simd[2]uint32_t +uint32x4_t :: #simd[4]uint32_t +uint64x1_t :: #simd[1]uint64_t +uint64x2_t :: #simd[2]uint64_t poly8x8_t :: #simd[8]poly8_t poly8x16_t :: #simd[16]poly8_t diff --git a/core/simd/simd.odin b/core/simd/simd.odin index 1631c4d23..fcbceb81a 100644 --- a/core/simd/simd.odin +++ b/core/simd/simd.odin @@ -2,7 +2,7 @@ Cross-platform `SIMD` support types and procedures. SIMD (Single Instruction Multiple Data), is a CPU hardware feature that -introduce special registers and instructions which operate on multiple units +introduces special registers and instructions which operate on multiple units of data at the same time, which enables faster data processing for applications with heavy computational workloads. diff --git a/core/thread/thread_pool.odin b/core/thread/thread_pool.odin index 10f1438d7..70734137e 100644 --- a/core/thread/thread_pool.odin +++ b/core/thread/thread_pool.odin @@ -187,6 +187,9 @@ pool_join :: proc(pool: ^Pool) { // // Each task also needs an allocator which it either owns, or which is thread // safe. +// +// Completed tasks remain in the pool until removed with `pool_pop_done`. +// When reusing the pool, call it once for every task added. pool_add_task :: proc(pool: ^Pool, allocator: mem.Allocator, procedure: Task_Proc, data: rawptr, user_index: int = 0) { sync.guard(&pool.mutex) @@ -344,7 +347,10 @@ pool_pop_waiting :: proc(pool: ^Pool) -> (task: Task, got_task: bool) { return } -// Use this to take out finished tasks. +// Remove and return the next completed task, if one is available. +// +// The caller is responsible for processing the result and releasing any +// resources associated with the task. pool_pop_done :: proc(pool: ^Pool) -> (task: Task, got_task: bool) { sync.guard(&pool.mutex) @@ -374,6 +380,9 @@ pool_do_work :: proc(pool: ^Pool, task: Task) { // Process the rest of the tasks, also use this thread for processing, then join // all the pool threads. +// +// Completed tasks are not removed. Retrieve each one with `pool_pop_done`. +// The pool cannot be restarted after this procedure returns. pool_finish :: proc(pool: ^Pool) { for task in pool_pop_waiting(pool) { pool_do_work(pool, task) diff --git a/core/unicode/utf8/grapheme.odin b/core/unicode/utf8/grapheme.odin index d8f6557b6..bcd234972 100644 --- a/core/unicode/utf8/grapheme.odin +++ b/core/unicode/utf8/grapheme.odin @@ -54,6 +54,9 @@ Grapheme_Iterator :: struct { current_sequence: Grapheme_Cluster_Sequence, continue_sequence: bool, + + current_grapheme: Grapheme, + continue_grapheme: bool, } @@ -147,13 +150,13 @@ decode_grapheme_iterate :: proc(it: ^Grapheme_Iterator) -> (text: string, graphe if it.grapheme_count > it.last_grapheme_count { it.width += normalized_east_asian_width(this_rune) - grapheme = Grapheme{ - byte_index, - it.rune_count, - it.width - it.last_width, + if it.continue_grapheme { + grapheme = it.current_grapheme + text = it.str[it.current_grapheme.byte_index:byte_index] + ok = true } - text = it.str[byte_index:][:grapheme.width] - ok = true + it.current_grapheme = Grapheme{byte_index, it.rune_count, it.width - it.last_width} + it.continue_grapheme = true it.last_grapheme_count = it.grapheme_count @@ -385,5 +388,14 @@ decode_grapheme_iterate :: proc(it: ^Grapheme_Iterator) -> (text: string, graphe it.grapheme_count += 1 } + // Flush the remaining grapheme - the loop only flushes when + // a new grapheme is encountered. + if !ok && it.continue_grapheme { + grapheme = it.current_grapheme + text = it.str[it.current_grapheme.byte_index:] + ok = true + it.continue_grapheme = false + } + return -} \ No newline at end of file +} diff --git a/src/big_int.cpp b/src/big_int.cpp index ffb7a6ad6..6db3301da 100644 --- a/src/big_int.cpp +++ b/src/big_int.cpp @@ -694,7 +694,9 @@ gb_internal String big_int_to_string(gbAllocator allocator, BigInt const *x, u64 big_int_dealloc(&r); big_int_dealloc(&b); - for (isize i = first_word_idx; i < buf.count/2; i++) { + // NOTE: only the digits are reversed, not the leading '-'. + isize digit_count = buf.count - first_word_idx; + for (isize i = first_word_idx; i < first_word_idx + digit_count/2; i++) { isize j = buf.count + first_word_idx - i - 1; char tmp = buf[i]; buf[i] = buf[j]; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index e8481a6dc..89a7dc446 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -410,6 +410,15 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E } if (!src->Proc.is_polymorphic || src->Proc.is_poly_specialized) { + // NOTE: polymorphic procedure check not idempotent without this + if (src->Proc.is_poly_specialized && base_entity->Procedure.generated_from_polymorphic) { + if (are_types_identical(src, dst)) { + if (poly_proc_data) { + poly_proc_data->gen_entity = base_entity; + } + return true; + } + } return false; } @@ -457,9 +466,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E scope->flags |= ScopeFlag_Proc; nctx.scope = scope; nctx.allow_polymorphic_types = true; - if (nctx.polymorphic_scope == nullptr) { - nctx.polymorphic_scope = scope; - } + nctx.polymorphic_scope = scope; auto *pt = &src->Proc; @@ -1027,17 +1034,6 @@ gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *o return false; } - // Handle polymorphic procedure used as default parameter - if (operand->mode == Addressing_Value && is_type_proc(type) && is_type_proc(operand->type)) { - Entity *e = entity_from_expr(operand->expr); - if (e != nullptr && e->kind == Entity_Procedure && is_type_polymorphic(e->type) && !is_type_polymorphic(type)) { - // Special case: Allow a polymorphic procedure to be used as default value for concrete proc type - // during the initial check. It will be properly instantiated when actually used. - if (score_) *score_ = assign_score_function(1); - return true; - } - } - i64 score = check_distance_between_types(c, operand, type, allow_array_programming); if (score >= 0) { if (score_) *score_ = assign_score_function(score, is_variadic); @@ -4090,7 +4086,7 @@ gb_internal bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type big_int_shl_eq(&umax, &sz_in_bits); if (is_type_unsigned(src_t) && !is_type_unsigned(dst_t)) { - if (big_int_cmp(&v, &smax) >= 0) { + if (big_int_cmp(&v, &smax) > 0) { big_int_sub_eq(&v, &umax); } } else if (!is_type_unsigned(src_t) && is_type_unsigned(dst_t)) { @@ -7637,7 +7633,9 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, if (max_matched_features > 0) { for_array(i, valids) { - Entity *p = procs[valids[i].index]; + // NOTE: A polymorphic candidate appends its instantiated entity to proc_entities above, + // so valids[i].index can be >= procs.count. + Entity *p = proc_entities[valids[i].index]; Type *t = base_type(p->type); GB_ASSERT(t->kind == Type_Proc); diff --git a/src/check_type.cpp b/src/check_type.cpp index a6511ce22..9414b794e 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1791,6 +1791,7 @@ gb_internal ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_ if (e->kind == Entity_Procedure) { param_value.kind = ParameterValue_Constant; param_value.value = exact_value_procedure(e->identifier); + param_value.proc_entity = e; add_entity_use(ctx, e->identifier, e); } else { if (e->flags & EntityFlag_Param) { @@ -2143,8 +2144,12 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para // This is just to add the error message to determine_type_from_polymorphic which // depends on valid position information op.expr = _params; - op.mode = Addressing_Invalid; - op.type = t_invalid; + + // NOTE(taylbr): Can still have valid type with null expr. Needed for resolving + if (op.mode == Addressing_Invalid || op.type == nullptr) { + op.mode = Addressing_Invalid; + op.type = t_invalid; + } } if (is_type_polymorphic_type) { type = determine_type_from_polymorphic(ctx, type, op); @@ -2841,9 +2846,14 @@ gb_internal i64 check_array_count(CheckerContext *ctx, Operand *o, Ast *e) { } Type *type = core_type(o->type); if (is_type_untyped(type) || is_type_integer(type)) { - if (o->value.kind == ExactValue_Integer) { - BigInt count = o->value.value_integer; - if (big_int_is_neg(&o->value.value_integer)) { + ExactValue value = o->value; + if (value.kind == ExactValue_Float) { + // NOTE: an integral float is a valid count, but it must be range checked as an integer + value = exact_value_to_integer(value); + } + if (value.kind == ExactValue_Integer) { + BigInt count = value.value_integer; + if (big_int_is_neg(&count)) { gbAllocator a = heap_allocator(); String str = big_int_to_string(a, &count); error(e, "Invalid negative array count, %.*s", LIT(str)); @@ -2859,12 +2869,6 @@ gb_internal i64 check_array_count(CheckerContext *ctx, Operand *o, Ast *e) { error(e, "Array count too large, %.*s", LIT(str)); gb_free(a, str.text); return 0; - } else if (o->value.kind == ExactValue_Float) { - u64 u = cast(u64)o->value.value_float; - f64 f = cast(f64)u; - if (f == o->value.value_float) { - return u; - } } } diff --git a/src/checker.cpp b/src/checker.cpp index 0c4b6788f..23ac59a3e 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2050,18 +2050,26 @@ gb_internal bool redeclaration_error(String name, Entity *prev, Entity *found) { // NOTE(bill): Error should have been handled already return false; } + // NOTE: the insertion order is a race between the files of a package, so order the pair by + // position; the later declaration stays the anchor, as it is the one being reported + TokenPos first = prev->token.pos; + TokenPos second = pos; + if (second < first) { + first = pos; + second = prev->token.pos; + } if (found->flags & EntityFlag_Result) { - error(prev->token, + error(second, "Direct shadowing of the named return value '%.*s' in this scope\n" "\tat %s", LIT(name), - token_pos_to_string(pos)); + token_pos_to_string(first)); } else { - error(prev->token, + error(second, "Redeclaration of '%.*s' in this scope\n" "\tat %s", LIT(name), - token_pos_to_string(pos)); + token_pos_to_string(first)); } } return false; diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp index 7a12d0875..67d350f24 100644 --- a/src/docs_writer.cpp +++ b/src/docs_writer.cpp @@ -265,8 +265,12 @@ gb_internal OdinDocPosition odin_doc_token_pos_cast(OdinDocWriter *w, TokenPos c AstFile *file = global_files[pos.file_id]; if (file != nullptr) { OdinDocFileIndex *file_index_found = map_get(&w->file_cache, file); - GB_ASSERT(file_index_found != nullptr); - file_index = *file_index_found; + // NOTE: a documented entity may hold a position in a file belonging to an + // imported package. Such files are only in file_cache when -all-packages is + // used, so fall back to the reserved "no file" index. + if (file_index_found != nullptr) { + file_index = *file_index_found; + } } } diff --git a/src/entity.cpp b/src/entity.cpp index 31f90023b..d3170c823 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -111,6 +111,7 @@ enum ParameterValueKind { struct ParameterValue { ParameterValueKind kind; Ast *original_ast_expr; + Entity *proc_entity; union { ExactValue value; Ast *ast_value; diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 545b415a6..2f82a52cf 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -419,6 +419,12 @@ gb_internal ExactValue exact_value_to_integer(ExactValue v) { case ExactValue_Integer: return v; case ExactValue_Float: { + f64 const min = cast(f64)I64_MIN; // -2^63 + f64 const max = -min; // 2^63, one past I64_MAX + // NOTE: the conversion below is undefined outside of this range, NaN included + if (!(v.value_float >= min && v.value_float < max)) { + break; + } i64 i = cast(i64)v.value_float; f64 f = cast(f64)i; if (f == v.value_float) { diff --git a/src/linker.cpp b/src/linker.cpp index cb3cbe023..98d299eb8 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -155,7 +155,7 @@ try_cross_linking:; String section_name = str_lit("msvc-link"); bool is_windows = build_context.metrics.os == TargetOs_windows; #else - String section_name = str_lit("lld-link"); + String section_name = str_lit("ld-link"); bool is_windows = false; #endif diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 0b2d21c51..d7e4523dd 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -1700,7 +1700,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty } } - res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc); + 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)) { diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 368ee709e..29dd75c9f 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -4924,6 +4924,11 @@ gb_internal void lb_build_addr_compound_lit_populate(lbProcedure *p, SliceSimdVector.elem; break; case Type_Matrix: et = bt->Matrix.elem; break; case Type_FixedCapacityDynamicArray: et = bt->FixedCapacityDynamicArray.elem; break; + case Type_Struct: + if (bt->Struct.soa_kind == StructSoa_Fixed) { + et = bt->Struct.soa_elem; + } + break; } GB_ASSERT(et != nullptr); @@ -5038,18 +5043,17 @@ gb_internal void lb_build_addr_compound_lit_populate(lbProcedure *p, Slice const &temp_data) { for (auto const &td : temp_data) { - if (td.value.value != nullptr) { - if (td.elem_length > 0) { - auto loop_data = lb_loop_start(p, cast(isize)td.elem_length, t_i32); - { - lbValue dst = td.gep; - dst = lb_emit_ptr_offset(p, dst, loop_data.idx); - lb_emit_store(p, dst, td.value); - } - lb_loop_end(p, loop_data); - } else { - lb_emit_store(p, td.gep, td.value); + GB_ASSERT(td.value.value != nullptr); + if (td.elem_length > 0) { + auto loop_data = lb_loop_start(p, cast(isize)td.elem_length, t_i32); + { + lbValue dst = td.gep; + dst = lb_emit_ptr_offset(p, dst, loop_data.idx); + lb_emit_store(p, dst, td.value); } + lb_loop_end(p, loop_data); + } else { + lb_emit_store(p, td.gep, td.value); } } } @@ -6120,7 +6124,35 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) { } case Type_Struct: - lb_build_addr_struct_compound_lit_populate(p, expr, type, v); + if (is_type_soa_struct(type)) { + GB_ASSERT(bt->Struct.soa_kind == StructSoa_Fixed); + if (cl->elems.count == 0) { + break; + } + 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); + for (auto const &td : temp_data) { + GB_ASSERT(td.value.value != nullptr); + lbValue offset = lb_const_int(p->module, t_i32, td.elem_index); + if (td.elem_length > 0) { + auto loop_data = lb_loop_start(p, cast(isize)td.elem_length, t_i32); + { + lbValue index = lb_emit_arith(p, Token_Add, offset, loop_data.idx, t_i32); + lbAddr dst = lb_addr_soa_variable(v.addr, index, td.expr); + lb_addr_store(p, dst, td.value); + } + lb_loop_end(p, loop_data); + } else { + lbValue index = offset; + lbAddr dst = lb_addr_soa_variable(v.addr, index, td.expr); + lb_addr_store(p, dst, td.value); + } + } + } else { + lb_build_addr_struct_compound_lit_populate(p, expr, type, v); + } break; case Type_Map: { diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 41352d490..b528980ec 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1024,10 +1024,16 @@ gb_internal lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue } for_array(i, ft->args) { + // lbArg_Ignore args are not present in the call arguments, so they must not + // advance param_offset (mirrors lb_add_function_type_attributes) + if (ft->args[i].kind == lbArg_Ignore) { + continue; + } LLVMAttributeRef attribute = ft->args[i].attribute; if (attribute != nullptr) { - LLVMAddCallSiteAttribute(ret, param_offset + cast(LLVMAttributeIndex)i, attribute); + LLVMAddCallSiteAttribute(ret, param_offset, attribute); } + param_offset += 1; } switch (inlining) { @@ -4785,6 +4791,12 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const ¶m_value, TypeProc *procedure_type, Ast* call_expression) { switch (param_value.kind) { case ParameterValue_Constant: + if (param_value.proc_entity != nullptr && is_type_proc(parameter_type)) { + lbValue v = lb_find_procedure_value_from_entity(p->module, param_value.proc_entity); + if (v.value != nullptr) { + return lb_emit_conv(p, v, parameter_type); + } + } if (is_type_constant_type(parameter_type)) { auto res = lb_const_value(p->module, parameter_type, param_value.value); return res; @@ -4904,6 +4916,79 @@ gb_internal void lb_add_values_to_array(lbProcedure *p, Array *args, lb } } +gb_internal lbValue lb_build_variadic_slice(lbProcedure *p, Type *slice_type, Slice var_args) { + GB_ASSERT(is_type_slice(slice_type)); + if (var_args.count == 0) { + return lb_const_nil(p->module, slice_type); + } + + Type *elem_type = slice_type->Slice.elem; + lbAddr slice = {}; + + for (auto const &vr : p->variadic_reuses) { + if (are_types_identical(vr.slice_type, slice_type)) { + slice = vr.slice_addr; + break; + } + } + + DeclInfo *d = decl_info_of_entity(p->entity); + if (d != nullptr && slice.addr.value == nullptr) { + for (auto const &vr : d->variadic_reuses) { + if (are_types_identical(vr.slice_type, slice_type)) { + #if LLVM_VERSION_MAJOR >= 13 + // NOTE(bill): No point wasting even more memory, just reuse this stack variable too + if (p->variadic_reuses.count > 0) { + slice = p->variadic_reuses[0].slice_addr; + } else { + slice = lb_add_local_generated(p, slice_type, true); + } + // NOTE(bill): Change the underlying type to match the specific type + slice.addr.type = alloc_type_pointer(slice_type); + #else + slice = lb_add_local_generated(p, slice_type, true); + #endif + array_add(&p->variadic_reuses, lbVariadicReuseSlices{slice_type, slice}); + break; + } + } + } + + lbValue base_array_ptr = p->variadic_reuse_base_array_ptr.addr; + if (base_array_ptr.value == nullptr) { + if (d != nullptr) { + i64 max_bytes = d->variadic_reuse_max_bytes; + i64 max_align = gb_max(d->variadic_reuse_max_align, 16); + p->variadic_reuse_base_array_ptr = lb_add_local_generated(p, alloc_type_array(t_u8, max_bytes), true); + lb_try_update_alignment(p->variadic_reuse_base_array_ptr.addr, cast(unsigned)max_align); + base_array_ptr = p->variadic_reuse_base_array_ptr.addr; + } else { + base_array_ptr = lb_add_local_generated(p, alloc_type_array(elem_type, var_args.count), true).addr; + } + } + + if (slice.addr.value == nullptr) { + slice = lb_add_local_generated(p, slice_type, true); + } + + GB_ASSERT(base_array_ptr.value != nullptr); + GB_ASSERT(slice.addr.value != nullptr); + + base_array_ptr = lb_emit_conv(p, base_array_ptr, alloc_type_pointer(alloc_type_array(elem_type, var_args.count))); + + for_array(i, var_args) { + lbValue addr = lb_emit_array_epi(p, base_array_ptr, cast(i32)i); + lbValue var_arg = lb_emit_conv(p, var_args[i], elem_type); + lb_emit_store(p, addr, var_arg); + } + + lbValue base_elem = lb_emit_array_epi(p, base_array_ptr, 0); + lbValue len = lb_const_int(p->module, t_int, var_args.count); + lb_fill_slice(p, slice, base_elem, len); + + return lb_addr_load(p, slice); +} + gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr, lbValue *sret_dst) { lbModule *m = p->module; @@ -4989,8 +5074,45 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr, lbVal bool vari_expand = (ce->ellipsis.pos.line != 0); bool is_c_vararg = pt->c_vararg; + bool has_tuple_positional_arg = false; + if (pt->variadic && !is_c_vararg && !vari_expand) { + for (Ast *arg : ce->split_args->positional) { + TypeAndValue tav = type_and_value_of_expr(arg); + if (is_type_tuple(tav.type)) { + has_tuple_positional_arg = true; + break; + } + } + } - for_array(i, ce->split_args->positional) { + if (has_tuple_positional_arg) { + auto flat_args = array_make(heap_allocator()); + defer (array_free(&flat_args)); + + for_array(i, ce->split_args->positional) { + Entity *e = pt->params->Tuple.variables[gb_min(i, cast(isize)pt->variadic_index)]; + if (e->kind == Entity_TypeName) { + array_add(&flat_args, lb_const_nil(p->module, e->type)); + } else if (e->kind == Entity_Constant) { + array_add(&flat_args, lb_const_value(p->module, e->type, e->Constant.value)); + } else { + GB_ASSERT(e->kind == Entity_Variable); + lbValue arg = lb_build_expr(p, ce->split_args->positional[i]); + lb_add_values_to_array(p, &flat_args, arg); + } + } + + isize fixed_count = pt->variadic_index; + isize supplied_fixed_count = gb_min(fixed_count, flat_args.count); + for (isize i = 0; i < supplied_fixed_count; i++) { + array_add(&args, flat_args[i]); + } + array_resize(&args, fixed_count); + + Type *slice_type = pt->params->Tuple.variables[pt->variadic_index]->type; + auto var_args = slice(slice_from_array(flat_args), supplied_fixed_count, flat_args.count); + array_add(&args, lb_build_variadic_slice(p, slice_type, var_args)); + } else for_array(i, ce->split_args->positional) { Entity *e = pt->params->Tuple.variables[i]; if (e->kind == Entity_TypeName) { array_add(&args, lb_const_nil(p->module, e->type)); @@ -5032,82 +5154,13 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr, lbVal variadic_args = lb_build_expr(p, variadic[0]); variadic_args = lb_emit_conv(p, variadic_args, slice_type); } else { - Type *elem_type = slice_type->Slice.elem; - auto var_args = array_make(heap_allocator(), 0, variadic.count); defer (array_free(&var_args)); for (Ast *var_arg : variadic) { lbValue v = lb_build_expr(p, var_arg); lb_add_values_to_array(p, &var_args, v); } - isize slice_len = var_args.count; - if (slice_len > 0) { - lbAddr slice = {}; - - for (auto const &vr : p->variadic_reuses) { - if (are_types_identical(vr.slice_type, slice_type)) { - slice = vr.slice_addr; - break; - } - } - - DeclInfo *d = decl_info_of_entity(p->entity); - if (d != nullptr && slice.addr.value == nullptr) { - for (auto const &vr : d->variadic_reuses) { - if (are_types_identical(vr.slice_type, slice_type)) { - #if LLVM_VERSION_MAJOR >= 13 - // NOTE(bill): No point wasting even more memory, just reuse this stack variable too - if (p->variadic_reuses.count > 0) { - slice = p->variadic_reuses[0].slice_addr; - } else { - slice = lb_add_local_generated(p, slice_type, true); - } - // NOTE(bill): Change the underlying type to match the specific type - slice.addr.type = alloc_type_pointer(slice_type); - #else - slice = lb_add_local_generated(p, slice_type, true); - #endif - array_add(&p->variadic_reuses, lbVariadicReuseSlices{slice_type, slice}); - break; - } - } - } - - lbValue base_array_ptr = p->variadic_reuse_base_array_ptr.addr; - if (base_array_ptr.value == nullptr) { - if (d != nullptr) { - i64 max_bytes = d->variadic_reuse_max_bytes; - i64 max_align = gb_max(d->variadic_reuse_max_align, 16); - p->variadic_reuse_base_array_ptr = lb_add_local_generated(p, alloc_type_array(t_u8, max_bytes), true); - lb_try_update_alignment(p->variadic_reuse_base_array_ptr.addr, cast(unsigned)max_align); - base_array_ptr = p->variadic_reuse_base_array_ptr.addr; - } else { - base_array_ptr = lb_add_local_generated(p, alloc_type_array(elem_type, slice_len), true).addr; - } - } - - if (slice.addr.value == nullptr) { - slice = lb_add_local_generated(p, slice_type, true); - } - - GB_ASSERT(base_array_ptr.value != nullptr); - GB_ASSERT(slice.addr.value != nullptr); - - base_array_ptr = lb_emit_conv(p, base_array_ptr, alloc_type_pointer(alloc_type_array(elem_type, slice_len))); - - for (isize i = 0; i < var_args.count; i++) { - lbValue addr = lb_emit_array_epi(p, base_array_ptr, cast(i32)i); - lbValue var_arg = var_args[i]; - var_arg = lb_emit_conv(p, var_arg, elem_type); - lb_emit_store(p, addr, var_arg); - } - - lbValue base_elem = lb_emit_array_epi(p, base_array_ptr, 0); - lbValue len = lb_const_int(p->module, t_int, slice_len); - lb_fill_slice(p, slice, base_elem, len); - - variadic_args = lb_addr_load(p, slice); - } + variadic_args = lb_build_variadic_slice(p, slice_type, slice_from_array(var_args)); } } array_add(&args, variadic_args); diff --git a/src/parser.cpp b/src/parser.cpp index 89a39e047..d981491dd 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -6,13 +6,35 @@ gb_internal bool in_vet_packages(AstFile *file) { if (file == nullptr) { return true; } + if (file->pkg == nullptr) { return true; } + + if (file->pkg_decl == nullptr) { + return true; + } + if (build_context.vet_packages.entries.count == 0) { return true; } - return string_set_exists(&build_context.vet_packages, file->pkg->name); + + String pkg_name = {}; + + if (file->pkg->name.len > 0) { + pkg_name = file->pkg->name; + } else if (file->pkg_decl->kind == Ast_PackageDecl) { + Token name_token = file->pkg_decl->PackageDecl.name; + if (name_token.kind == Token_Ident) { + pkg_name = name_token.string; + } + } + + if (pkg_name.len == 0) { + return true; + } + + return string_set_exists(&build_context.vet_packages, pkg_name); } gb_internal u64 ast_file_vet_flags(AstFile *f) { diff --git a/tests/core/math/rand/test_core_math_rand.odin b/tests/core/math/rand/test_core_math_rand.odin index 5c2f4af84..efc5c1be0 100644 --- a/tests/core/math/rand/test_core_math_rand.odin +++ b/tests/core/math/rand/test_core_math_rand.odin @@ -1,3 +1,4 @@ +#+build !netbsd package test_core_math_rand import "core:math" @@ -10,6 +11,9 @@ Generator :: struct { biased: bool, } +// Disable on NetBSD due to Illegal Instruction on CI, even with `microarch:native` +// `@(test, disable="...")` still runs the test. + @(test) test_prngs :: proc(t: ^testing.T) { gens := []Generator { diff --git a/tests/core/net/test_core_net.odin b/tests/core/net/test_core_net.odin index fbca15bb1..b0cf57cad 100644 --- a/tests/core/net/test_core_net.odin +++ b/tests/core/net/test_core_net.odin @@ -525,6 +525,36 @@ join_url_test :: proc(t: ^testing.T) { } } +@test +percent_encode_test :: proc(t: ^testing.T) { + test_cases := []struct{input, expected: string} { + // Bytes < 0x10 must be zero-padded to two hex digits + {"\n", "%0A"}, + {"\t", "%09"}, + {"\r", "%0D"}, + {"a\nb", "a%0Ab"}, + {"\x00", "%00"}, + + // Bytes >= 0x10 + {" ", "%20"}, + {"😃", "%F0%9F%98%83"}, + + // Unreserved characters pass through unescaped + {"AZaz09-_.~", "AZaz09-_.~"}, + } + + for test in test_cases { + encoded := net.percent_encode(test.input) + defer delete(encoded) + testing.expectf(t, encoded == test.expected, "Expected `net.percent_encode(%q)` to return %q, got %q", test.input, test.expected, encoded) + + decoded, ok := net.percent_decode(encoded) + defer delete(decoded) + testing.expectf(t, ok, "Expected `net.percent_decode(%q)` to succeed", encoded) + testing.expectf(t, decoded == test.input, "Expected percent-encoding roundtrip for %q, got %q", test.input, decoded) + } +} + @test test_udp_echo :: proc(t: ^testing.T) { endpoint := net.Endpoint{address=net.IP4_Address{127, 0, 0, 1}, port=0} diff --git a/tests/core/slice/not_netbsd.odin b/tests/core/slice/not_netbsd.odin new file mode 100644 index 000000000..f33be479d --- /dev/null +++ b/tests/core/slice/not_netbsd.odin @@ -0,0 +1,62 @@ +#+build !netbsd +package test_core_slice + +import "core:slice" +import "core:testing" +import "core:math/rand" + +// Disable on NetBSD due to Illegal Instruction on CI, even with `microarch:native` +// `@(test, disable="...")` still runs the test. + +@(test) +test_unique :: proc(t: ^testing.T) { + for v in UNIQUE_TEST_VECTORS { + assorted := v[0] + expected := v[1] + + uniq := slice.unique(assorted) + testing.expectf(t, slice.equal(uniq, expected), "Expected slice.uniq(%v) == %v, got %v", v[0], v[1], uniq) + } + + for v in UNIQUE_TEST_VECTORS { + assorted := v[0] + expected := v[1] + + uniq := slice.unique_proc(assorted, proc(a, b: int) -> bool { + return a == b + }) + testing.expectf(t, slice.equal(uniq, expected), "Expected slice.unique_proc(%v, ...) == %v, got %v", v[0], v[1], uniq) + } + + r := rand.create(t.seed) + context.random_generator = rand.default_random_generator(&r) + + // 10_000 random tests + for _ in 0..<10_000 { + assorted: [dynamic]i64 + expected: [dynamic]i64 + + // Prime with 1 value + old := rand.int63() + append(&assorted, old) + append(&expected, old) + + // Add 99 additional random values + for _ in 1..<100 { + new := rand.int63() + append(&assorted, new) + if old != new { + append(&expected, new) + } + old = new + } + + original := slice.clone(assorted[:]) + uniq := slice.unique(assorted[:]) + testing.expectf(t, slice.equal(uniq, expected[:]), "Expected slice.uniq(%v) == %v, got %v", original, expected, uniq) + + delete(assorted) + delete(original) + delete(expected) + } +} \ No newline at end of file diff --git a/tests/core/slice/test_core_slice.odin b/tests/core/slice/test_core_slice.odin index 98cb8dbac..b0fb791a6 100644 --- a/tests/core/slice/test_core_slice.odin +++ b/tests/core/slice/test_core_slice.odin @@ -225,59 +225,6 @@ UNIQUE_TEST_VECTORS :: [][2][]int{ {{1,2,4,4,5}, {1,2,4,5}}, } -@test -test_unique :: proc(t: ^testing.T) { - for v in UNIQUE_TEST_VECTORS { - assorted := v[0] - expected := v[1] - - uniq := slice.unique(assorted) - testing.expectf(t, slice.equal(uniq, expected), "Expected slice.uniq(%v) == %v, got %v", v[0], v[1], uniq) - } - - for v in UNIQUE_TEST_VECTORS { - assorted := v[0] - expected := v[1] - - uniq := slice.unique_proc(assorted, proc(a, b: int) -> bool { - return a == b - }) - testing.expectf(t, slice.equal(uniq, expected), "Expected slice.unique_proc(%v, ...) == %v, got %v", v[0], v[1], uniq) - } - - r := rand.create(t.seed) - context.random_generator = rand.default_random_generator(&r) - - // 10_000 random tests - for _ in 0..<10_000 { - assorted: [dynamic]i64 - expected: [dynamic]i64 - - // Prime with 1 value - old := rand.int63() - append(&assorted, old) - append(&expected, old) - - // Add 99 additional random values - for _ in 1..<100 { - new := rand.int63() - append(&assorted, new) - if old != new { - append(&expected, new) - } - old = new - } - - original := slice.clone(assorted[:]) - uniq := slice.unique(assorted[:]) - testing.expectf(t, slice.equal(uniq, expected[:]), "Expected slice.uniq(%v) == %v, got %v", original, expected, uniq) - - delete(assorted) - delete(original) - delete(expected) - } -} - @test test_compare_empty :: proc(t: ^testing.T) { a := []int{} diff --git a/tests/core/unicode/test_core_unicode.odin b/tests/core/unicode/test_core_unicode.odin index 30a40b30b..874f4eebe 100644 --- a/tests/core/unicode/test_core_unicode.odin +++ b/tests/core/unicode/test_core_unicode.odin @@ -9,6 +9,11 @@ Test_Case :: struct { expected_clusters: int, } +Text_Test_Case :: struct { + str: string, + expected_output: []string, +} + run_test_cases :: proc(t: ^testing.T, test_cases: []Test_Case, loc := #caller_location) { failed := 0 for c, i in test_cases { @@ -132,3 +137,32 @@ test_width :: proc(t: ^testing.T) { testing.expect_value(t, width, 50) } } + +@test +test_grapheme_cluster_text :: proc(t: ^testing.T) { + + cases :: []Text_Test_Case { + {"abc", {"a", "b", "c"}}, + {"é", {"é"}}, + {"中", {"中"}}, + {"\U0001F1FA\U0001F1F8", {"\U0001F1FA\U0001F1F8"}}, + {"\U0001F1FA\U0001F1F8\U0001F1EE\U0001F1EA", {"\U0001F1FA\U0001F1F8", "\U0001F1EE\U0001F1EA"}}, + {"\U0001F468‍\U0001F469‍\U0001F467‍\U0001F466", {"\U0001F468‍\U0001F469‍\U0001F467‍\U0001F466"}}, + {"\U0001F44D\U0001F3FD", {"\U0001F44D\U0001F3FD"}}, + {"a\r\nb", {"a", "\r\n", "b"}}, + } + + for c in cases { + it := utf8.decode_grapheme_iterator_make(c.str) + i := 0 + for text, grapheme in utf8.decode_grapheme_iterate(&it) { + if !testing.expectf(t, i < len(c.expected_output), "%q: expected %d clusters, got at least %d", c.str, len(c.expected_output), i + 1) { + break + } + testing.expectf(t, text == c.expected_output[i], "%q cluster %d: expected text %q, got %q", c.str, i, c.expected_output[i], text) + testing.expectf(t, text == c.str[grapheme.byte_index:][:len(text)], "%q cluster %d: text does not start at byte_index %d", c.str, i, grapheme.byte_index) + i += 1 + } + testing.expectf(t, i == len(c.expected_output), "%q: expected %d clusters, got %d", c.str, len(c.expected_output), i) + } +} diff --git a/tests/internal/test_array_count.odin b/tests/internal/test_array_count.odin new file mode 100644 index 000000000..597462c4c --- /dev/null +++ b/tests/internal/test_array_count.odin @@ -0,0 +1,25 @@ +package test_internal + +import "core:testing" + +@(test) +array_count_from_integral_float :: proc(t: ^testing.T) { + testing.expect_value(t, len([3.0]u8{}), 3) + testing.expect_value(t, len([0.0]u8{}), 0) + testing.expect_value(t, size_of([3.0]u32), 12) + + // folded constant expressions + Halved :: 6.0 / 2.0 + Summed :: 1.5 + 1.5 + testing.expect_value(t, len([Halved]u8{}), 3) + testing.expect_value(t, len([Summed]u8{}), 3) + + // NOTE: keep below one BigInt limb: 2^28 on windows, 2^60 on linux + testing.expect_value(t, len([65536.0]struct{}{}), 1 << 16) + + testing.expect_value(t, size_of(matrix[2.0, 3.0]f32), 24) + testing.expect_value(t, size_of(#simd[4.0]u8), 4) + + Sparse :: [2.0]u8 + testing.expect_value(t, len(Sparse{1, 2}), 2) +} diff --git a/tests/issues/run.bat b/tests/issues/run.bat index 8a85ba90a..7d8359a6c 100644 --- a/tests/issues/run.bat +++ b/tests/issues/run.bat @@ -26,6 +26,7 @@ set COMMON=-define:ODIN_TEST_FANCY=false -file -vet -strict-style -ignore-unused ..\..\..\odin build ..\test_issue_5097.odin %COMMON% || exit /b ..\..\..\odin build ..\test_issue_5097-2.odin %COMMON% || exit /b ..\..\..\odin build ..\test_issue_5265.odin %COMMON% || exit /b +..\..\..\odin build ..\test_issue_5573.odin %COMMON% 2>&1 | find /c "Error:" | findstr /x "2" || exit /b ..\..\..\odin test ..\test_issue_5699.odin %COMMON% || exit /b ..\..\..\odin test ..\test_issue_6068.odin %COMMON% || exit /b ..\..\..\odin test ..\test_issue_6101.odin %COMMON% || exit /b @@ -37,9 +38,11 @@ set COMMON=-define:ODIN_TEST_FANCY=false -file -vet -strict-style -ignore-unused ..\..\..\odin test ..\test_pr_6470.odin -define:TEST_EXPECT_FAILURE=true %COMMON% 2>&1 | find /c "Error:" | findstr /x "1" || exit /b ..\..\..\odin test ..\test_pr_6476.odin %COMMON% || exit /b ..\..\..\odin check ..\test_issue_6484.odin -no-entry-point %COMMON% || exit /b +..\..\..\odin test ..\test_issue_6753.odin %COMMON% || exit /b ..\..\..\odin check ..\test_issue_6874.odin %COMMON% 2>&1 | find /c "Error:" | findstr /x "1" || exit /b ..\..\..\odin check ..\test_issue_6979.odin -no-entry-point %COMMON% || exit /b ..\..\..\odin build ..\test_issue_7037.odin %COMMON% -o:none || exit /b +..\..\..\odin build ..\test_issue_7188.odin %COMMON% || exit /b ..\..\..\odin build ..\test_issue_7073-1.odin %COMMON% 2>&1 | find /c "Error:" | findstr /x "2" || exit /b @echo off diff --git a/tests/issues/run.sh b/tests/issues/run.sh index aa66d9886..cca26fe8a 100755 --- a/tests/issues/run.sh +++ b/tests/issues/run.sh @@ -7,10 +7,9 @@ ODIN=../../../odin COMMON="-define:ODIN_TEST_FANCY=false -file -vet -strict-style -ignore-unused-defineables -microarch:native" COMMON_CHECK="-define:ODIN_TEST_FANCY=false -file -vet -strict-style -ignore-unused-defineables" - set -x -$ODIN test ../test_issue_829.odin $COMMON +$ODIN test ../test_issue_829.odin $COMMON $ODIN test ../test_issue_1592.odin $COMMON $ODIN test ../test_issue_1730.odin $COMMON $ODIN test ../test_issue_2056.odin $COMMON @@ -24,7 +23,7 @@ $ODIN test ../test_issue_3435.odin $COMMON $ODIN test ../test_issue_4210.odin $COMMON $ODIN test ../test_issue_4364.odin $COMMON $ODIN test ../test_issue_4584.odin $COMMON -if [[ $($ODIN build ../test_issue_2395.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 2 ]] ; then +if [[ $($ODIN build ../test_issue_2395.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 2 ]]; then echo "SUCCESSFUL 1/1" else echo "SUCCESSFUL 0/1" @@ -34,6 +33,12 @@ $ODIN build ../test_issue_5043.odin $COMMON $ODIN build ../test_issue_5097.odin $COMMON $ODIN build ../test_issue_5097-2.odin $COMMON $ODIN build ../test_issue_5265.odin $COMMON +if [[ $($ODIN build ../test_issue_5573.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 2 ]]; then + echo "SUCCESSFUL 1/1" +else + echo "SUCCESSFUL 0/1" + exit 1 +fi $ODIN test ../test_issue_5699.odin $COMMON $ODIN test ../test_issue_6068.odin $COMMON $ODIN test ../test_issue_6101.odin $COMMON @@ -43,25 +48,25 @@ $ODIN test ../test_issue_6344.odin $COMMON -o:speed $ODIN test ../test_issue_6396.odin $COMMON $ODIN test ../test_pr_6476.odin $COMMON -if [[ $($ODIN build ../test_issue_6240.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 3 ]] ; then +if [[ $($ODIN build ../test_issue_6240.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 3 ]]; then echo "SUCCESSFUL 1/1" else echo "SUCCESSFUL 0/1" exit 1 fi -if [[ $($ODIN build ../test_issue_6401.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 3 ]] ; then +if [[ $($ODIN build ../test_issue_6401.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 3 ]]; then echo "SUCCESSFUL 1/1" else echo "SUCCESSFUL 0/1" exit 1 fi -if [[ $($ODIN build ../test_issue_6594.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 1 ]] ; then +if [[ $($ODIN build ../test_issue_6594.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 1 ]]; then echo "SUCCESSFUL 1/1" else echo "SUCCESSFUL 0/1" exit 1 fi -if [[ $($ODIN build ../test_issue_6621.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 1 ]] ; then +if [[ $($ODIN build ../test_issue_6621.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 1 ]]; then echo "SUCCESSFUL 1/1" else echo "SUCCESSFUL 0/1" @@ -69,14 +74,15 @@ else fi $ODIN test ../test_issue_6419.odin $COMMON $ODIN test ../test_pr_6470.odin $COMMON -if [[ $($ODIN test ../test_pr_6470.odin -define:TEST_EXPECT_FAILURE=true $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 1 ]] ; then +if [[ $($ODIN test ../test_pr_6470.odin -define:TEST_EXPECT_FAILURE=true $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 1 ]]; then echo "SUCCESSFUL 1/1" else echo "SUCCESSFUL 0/1" exit 1 fi $ODIN check ../test_issue_6484.odin -no-entry-point $COMMON_CHECK -if [[ $($ODIN check ../test_issue_6874.odin $COMMON_CHECK 2>&1 >/dev/null | grep -c "Error:") -eq 1 ]] ; then +$ODIN test ../test_issue_6753.odin $COMMON +if [[ $($ODIN check ../test_issue_6874.odin $COMMON_CHECK 2>&1 >/dev/null | grep -c "Error:") -eq 1 ]]; then echo "SUCCESSFUL 1/1" else echo "SUCCESSFUL 0/1" @@ -84,15 +90,17 @@ else fi $ODIN check ../test_issue_6979.odin -no-entry-point $COMMON_CHECK $ODIN build ../test_issue_7037.odin $COMMON -o:none +$ODIN build ../test_issue_7167.odin $COMMON +$ODIN build ../test_issue_7188.odin $COMMON -if [[ $($ODIN build ../test_issue_7108.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 2 ]] ; then +if [[ $($ODIN build ../test_issue_7108.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 2 ]]; then echo "SUCCESSFUL 1/1" else echo "SUCCESSFUL 0/1" exit 1 fi -if [[ $($ODIN build ../test_issue_7073-1.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 2 ]] ; then +if [[ $($ODIN build ../test_issue_7073-1.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 2 ]]; then echo "SUCCESSFUL 1/1" else echo "SUCCESSFUL 0/1" diff --git a/tests/issues/test_issue_5573.odin b/tests/issues/test_issue_5573.odin new file mode 100644 index 000000000..150f95863 --- /dev/null +++ b/tests/issues/test_issue_5573.odin @@ -0,0 +1,18 @@ +// Tests issue #5573 https://github.com/odin-lang/Odin/issues/5573 +package test_issues + +poly :: proc(x: $T) -> string { + return "poly" +} + +takes_concrete :: proc(f: proc(a: int, b: f32, c: rawptr) -> ^int) { +} + +main :: proc() { + // should error - wrong arity, wrong parameter types, wrong return type + mismatched: proc(a: int, b: f32, c: rawptr) -> ^int = poly + _ = mismatched + + // should error - same, as a procedure argument + takes_concrete(poly) +} diff --git a/tests/issues/test_issue_6753.odin b/tests/issues/test_issue_6753.odin new file mode 100644 index 000000000..26531ae31 --- /dev/null +++ b/tests/issues/test_issue_6753.odin @@ -0,0 +1,64 @@ +// test issue for #6753 https://github.com/odin-lang/odin/issues/6753 +package test_issues +import "core:testing" +import "core:fmt" + +foo_concrete :: proc(x: int, g: proc(int) -> int) -> int { + return g(x) +} +foo_impossible :: proc(x: int, g: proc(int, int) -> string) -> string { + return "impossible" +} +foo_group :: proc { + foo_concrete, + foo_impossible, +} + +f_poly :: proc(x: $T) -> T { return x } +foo_poly :: proc(x: $T, g: proc(T) -> T = f_poly) -> T { + return g(x) +} + +@test +test_issue_6753_ambiguous_poly_argument :: proc (t: ^testing.T) { + testing.expect_value(t, foo_group(1, f_poly), 1) // should be no ambiguity whether foo_concrete or foo_impossible +} + +@test +test_issue_6753_default_poly_proc :: proc (t: ^testing.T) { + testing.expect_value(t, foo_poly(1), 1) +} + +@test +test_issue_6753_parapoly_proc_variable :: proc(t: ^testing.T) { + p: proc(int) -> int = f_poly + testing.expect(t, p != nil, "polymorphic procedure was not instantiated") + testing.expect_value(t, p(123), 123) +} + +// -- Fixing above led to some new bugs surfacing -- +@test +test_issue_6753_parapoly_proc_as_argument :: proc(t: ^testing.T) { + testing.expect(t, foo_concrete(123, f_poly) == 123, "failed to pass poly proc as argument") +} + +@test +test_issue_6753_parapoly_with_default_proc_same_generic_type_T :: proc(t: ^testing.T) { + testing.expect_value(t, foo_poly(123), 123) + testing.expect_value(t, foo_poly(123, f_poly), 123) +} + +// all together now +describe :: proc(x: $T) -> string { return fmt.tprintf("#%v", x) } +describe_bytes :: proc(x: []byte) -> string { return "bytes" } + +bar_poly :: proc(x: $T, g: proc(x: T) -> string = describe) -> string { return g(x) } +bar_bytes :: proc(x: []byte, g: proc(x: []byte) -> string) -> string { return g(x) } +bar_group :: proc { bar_poly, bar_bytes } + +@test +test_issue_6753_parapoly_default_in_group :: proc(t: ^testing.T) { + testing.expect_value(t, bar_group(123, describe), "#123") + testing.expect_value(t, bar_group("hi"), "#hi") + testing.expect_value(t, bar_group([]byte{1, 2}, describe_bytes), "bytes") +} diff --git a/tests/issues/test_issue_7167.odin b/tests/issues/test_issue_7167.odin new file mode 100644 index 000000000..6c4171088 --- /dev/null +++ b/tests/issues/test_issue_7167.odin @@ -0,0 +1,9 @@ +// Tests issue #7167 https://github.com/odin-lang/Odin/issues/7167 +package test_issues + +import "core:fmt" +import "core:path/filepath" + +main :: proc() { + fmt.printf(filepath.join({})) +} diff --git a/tests/issues/test_issue_7188.odin b/tests/issues/test_issue_7188.odin new file mode 100644 index 000000000..679494086 --- /dev/null +++ b/tests/issues/test_issue_7188.odin @@ -0,0 +1,13 @@ +package test_issues + +main :: proc() { + val: f32 = 42.0 + // unable to broadcast like ([4][4]f32)(1) or (#soa[4][4]f32)(([4]f32)(1)) + _ = #soa[2][2]f32 { + 0..<2 = val, + } + vtxs := #soa[33][2]f32 { + 0..<33 = val, + } + _ = vtxs.x[32] +} diff --git a/vendor/box3d/lib/linux-amd64/libbox3d.a b/vendor/box3d/lib/linux-amd64/libbox3d.a index 402424fa2..42e9a56cd 100644 --- a/vendor/box3d/lib/linux-amd64/libbox3d.a +++ b/vendor/box3d/lib/linux-amd64/libbox3d.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d698fbbe655db1d1ec6f52dcc23bfed4a2c981fa88040ba449f9f83609edba88 -size 3137140 +oid sha256:ad5491b77cda430ee03adb68bd6b4d56d3f7d8941327643eecc2b636496acda0 +size 1601114 diff --git a/vendor/box3d/src/build.sh b/vendor/box3d/src/build.sh index 60cfd41f1..0513d8ea5 100755 --- a/vendor/box3d/src/build.sh +++ b/vendor/box3d/src/build.sh @@ -70,12 +70,19 @@ Darwin) ;; Linux) LIB_DIR="../lib/linux-$ARCH" - mkdir -p "$LIB_DIR" - $cc -c -O2 -std=c17 -fPIC -Iinclude src/*.c - $ar rcs "$LIB_DIR/$LIB_NAME" ./*.o - rm ./*.o + mkdir -p "$LIB_DIR" build + for src in src/*.c; do + obj="build/$(basename "${src%.c}.o")" + $cc -c -O2 -std=c17 -fPIC -Iinclude "$src" -o "$obj" + done + # Clean up old library in case `ar` is tempted to preserve old symbols + rm -f "$LIB_DIR/$LIB_NAME" + $ar rcs "$LIB_DIR/$LIB_NAME" build/*.o + $ranlib "$LIB_DIR/$LIB_NAME" + rm -rf build ;; *) + echo "Error: Unsupported operating system: $(uname -s)" exit 1 ;;