diff --git a/.gitattributes b/.gitattributes index e375d2543..d523ee98a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -10,3 +10,7 @@ vendor/sdl3/SDL3.dll filter=lfs diff=lfs merge=lfs -text vendor/sdl3/SDL3.lib filter=lfs diff=lfs merge=lfs -text vendor/sdl3/mixer/*.dll filter=lfs diff=lfs merge=lfs -text vendor/sdl3/mixer/*.lib filter=lfs diff=lfs merge=lfs -text +vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.dll filter=lfs diff=lfs merge=lfs -text +vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.dll.lib filter=lfs diff=lfs merge=lfs -text +vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.lib filter=lfs diff=lfs merge=lfs -text +vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.pdb filter=lfs diff=lfs merge=lfs -text diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dd49b8ff8..8876d4f02 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -140,8 +140,9 @@ jobs: - name: Optimized Core library tests run: ./odin test tests/core/speed.odin -o:speed -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 -sanitize:address - name: Wycheproof tests - run: ./odin test tests/core/crypto/wycheproof -vet -vet-tabs -strict-style -vet-style -vet-cast -warnings-as-errors -disallow-do -o:speed + - name: Noise Protocol Framework tests + run: ./odin test tests/core/crypto/noise -vet -vet-tabs -strict-style -vet-style -vet-cast -warnings-as-errors -disallow-do -o:speed - name: Vendor library tests run: ./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 -sanitize:address if: matrix.os != 'macos-15-intel' && matrix.os != 'macos-latest' @@ -244,6 +245,11 @@ jobs: run: | call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" odin test tests/core/crypto/wycheproof -vet -vet-tabs -strict-style -vet-style -vet-cast -warnings-as-errors -disallow-do -o:speed -microarch:native + - name: Noise Protocol Framework tests + shell: cmd + run: | + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + odin test tests/core/crypto/noise -vet -vet-tabs -strict-style -vet-style -vet-cast -warnings-as-errors -disallow-do -o:speed -microarch:native - name: Vendor library tests shell: cmd run: | diff --git a/base/runtime/internal.odin b/base/runtime/internal.odin index db17cb033..bb9fc4b36 100644 --- a/base/runtime/internal.odin +++ b/base/runtime/internal.odin @@ -23,6 +23,12 @@ HAS_HARDWARE_SIMD :: false when (ODIN_ARCH == .amd64 || ODIN_ARCH == .i386) && ! false when (ODIN_ARCH == .riscv64) && !intrinsics.has_target_feature("v") else true +// Size of a native SIMD register for the current compilation target +NATIVE_SIMD_BIT_WIDTH :: + 512 when (ODIN_ARCH == .amd64) && intrinsics.has_target_feature("avx512f") else + 256 when (ODIN_ARCH == .amd64) && (intrinsics.has_target_feature("avx2") || intrinsics.has_target_feature("avx")) else + // Fallback for no hardware SIMD, but also SSE, NEON, SVE, RVV and WASM SIMD128. + 128 @(private) byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte #no_bounds_check { diff --git a/core/container/xar/xar.odin b/core/container/xar/xar.odin index 08b2b2d32..9f0b6e90c 100644 --- a/core/container/xar/xar.odin +++ b/core/container/xar/xar.odin @@ -375,7 +375,7 @@ array_pop_safe :: proc(x: ^$X/Array($T, $SHIFT)) -> (val: T, ok: bool) { Example: - import "core:encoding/xar" + import "core:container/xar" unordered_remove_example :: proc() { x: xar.Array(int, 4) diff --git a/core/crypto/crypto.odin b/core/crypto/crypto.odin index f4ddbfbe7..aa5a67b8f 100644 --- a/core/crypto/crypto.odin +++ b/core/crypto/crypto.odin @@ -16,7 +16,8 @@ HAS_RAND_BYTES :: runtime.HAS_RAND_BYTES // // The execution time of this routine is constant regardless of the contents // of the slices being compared, as long as the length of the slices is equal. -// If the length of the two slices is dif and only if (⟺)erent, it will early-return 0. +// If and only if (⟺) the length of the two slices is diferent, it will +// early-return 0. compare_constant_time :: proc "contextless" (a, b: []byte) -> int { // If the length of the slices is different, early return. // diff --git a/core/crypto/ecdh/ecdh.odin b/core/crypto/ecdh/ecdh.odin index 6a8f6e466..f5106d152 100644 --- a/core/crypto/ecdh/ecdh.odin +++ b/core/crypto/ecdh/ecdh.odin @@ -222,6 +222,40 @@ private_key_generate_public :: proc(priv_key: ^Private_Key) { priv_key._pub_key._curve = priv_key._curve } +// private_key_set sets priv_key to src. +private_key_set :: proc(priv_key, src: ^Private_Key) { + if src == nil || src._curve == .Invalid { + private_key_clear(priv_key) + return + } + + priv_key._curve = src._curve + + reflect.set_union_variant_typeid( + priv_key._impl, + _PRIV_IMPL_IDS[priv_key._curve], + ) + + #partial switch priv_key._curve { + case .SECP256R1: + secec.sc_set(&priv_key._impl.(secec.Scalar_p256r1), &src._impl.(secec.Scalar_p256r1)) + case .SECP384R1: + secec.sc_set(&priv_key._impl.(secec.Scalar_p384r1), &src._impl.(secec.Scalar_p384r1)) + case .X25519: + priv_buf := &(priv_key._impl.(X25519_Buf)) + src_buf := &(src._impl.(X25519_Buf)) + copy(priv_buf[:], src_buf[:]) + case .X448: + priv_buf := &(priv_key._impl.(X448_Buf)) + src_buf := &(src._impl.(X448_Buf)) + copy(priv_buf[:], src_buf[:]) + case: + panic("crypto/ecdh: invalid curve") + } + + public_key_set(&priv_key._pub_key, &src._pub_key) +} + // private_key_bytes sets dst to byte-encoding of priv_key. private_key_bytes :: proc(priv_key: ^Private_Key, dst: []byte) { ensure(priv_key._curve != .Invalid, "crypto/ecdh: uninitialized private key") @@ -325,6 +359,16 @@ public_key_set_bytes :: proc(pub_key: ^Public_Key, curve: Curve, b: []byte) -> b return true } +// public_key_set sets pub_key to src. +public_key_set :: proc(pub_key, src: ^Public_Key) { + if src == nil || src._curve == .Invalid { + public_key_clear(pub_key) + return + } + + pub_key^ = src^ +} + // public_key_set_priv sets pub_key to the public component of priv_key. public_key_set_priv :: proc(pub_key: ^Public_Key, priv_key: ^Private_Key) { ensure(priv_key._curve != .Invalid, "crypto/ecdh: uninitialized private key") diff --git a/core/crypto/ecdsa/ecdsa.odin b/core/crypto/ecdsa/ecdsa.odin index 6c71feef7..350bab3ec 100644 --- a/core/crypto/ecdsa/ecdsa.odin +++ b/core/crypto/ecdsa/ecdsa.odin @@ -194,6 +194,32 @@ private_key_bytes :: proc(priv_key: ^Private_Key, dst: []byte) { } } +// private_key_set sets priv_key to src. +private_key_set :: proc(priv_key, src: ^Private_Key) { + if src == nil || src._curve == .Invalid { + private_key_clear(priv_key) + return + } + + priv_key._curve = src._curve + + reflect.set_union_variant_typeid( + priv_key._impl, + _PRIV_IMPL_IDS[priv_key._curve], + ) + + #partial switch priv_key._curve { + case .SECP256R1: + secec.sc_set(&priv_key._impl.(secec.Scalar_p256r1), &src._impl.(secec.Scalar_p256r1)) + case .SECP384R1: + secec.sc_set(&priv_key._impl.(secec.Scalar_p384r1), &src._impl.(secec.Scalar_p384r1)) + case: + panic("crypto/ecdh: invalid curve") + } + + public_key_set(&priv_key._pub_key, &src._pub_key) +} + // private_key_equal returns true if and only if (⟺) the private keys are equal, // in constant time. private_key_equal :: proc(p, q: ^Private_Key) -> bool { @@ -262,6 +288,16 @@ public_key_set_bytes :: proc(pub_key: ^Public_Key, curve: Curve, b: []byte) -> b return true } +// public_key_set sets pub_key to src. +public_key_set :: proc(pub_key, src: ^Public_Key) { + if src == nil || src._curve == .Invalid { + public_key_clear(pub_key) + return + } + + pub_key^ = src^ +} + // public_key_set_priv sets pub_key to the public component of priv_key. public_key_set_priv :: proc(pub_key: ^Public_Key, priv_key: ^Private_Key) { ensure(priv_key._curve != .Invalid, "crypto/ecdsa: uninitialized private key") diff --git a/core/crypto/ed25519/ed25519.odin b/core/crypto/ed25519/ed25519.odin index 2020c0633..164e9805b 100644 --- a/core/crypto/ed25519/ed25519.odin +++ b/core/crypto/ed25519/ed25519.odin @@ -97,6 +97,21 @@ private_key_set_bytes :: proc(priv_key: ^Private_Key, b: []byte) -> bool { return true } +// private_key_set sets priv_key to src. +private_key_set :: proc(priv_key, src: ^Private_Key) { + if src == nil || !src._is_initialized { + private_key_clear(priv_key) + return + } + + copy(priv_key._b[:], src._b[:]) + grp.sc_set(&priv_key._s, &src._s) + copy(priv_key._hdigest2[:], src._hdigest2[:]) + public_key_set(&priv_key._pub_key, &src._pub_key) + + priv_key._is_initialized = true +} + // private_key_bytes sets dst to byte-encoding of priv_key. private_key_bytes :: proc(priv_key: ^Private_Key, dst: []byte) { ensure(priv_key._is_initialized, "crypto/ed25519: uninitialized private key") @@ -186,6 +201,16 @@ public_key_set_bytes :: proc "contextless" (pub_key: ^Public_Key, b: []byte) -> return true } +// public_key_set sets pub_key to src. +public_key_set :: proc(pub_key, src: ^Public_Key) { + if src == nil || !src._is_initialized { + public_key_clear(pub_key) + return + } + + pub_key^ = src^ +} + // public_key_set_priv sets pub_key to the public component of priv_key. public_key_set_priv :: proc(pub_key: ^Public_Key, priv_key: ^Private_Key) { ensure(priv_key._is_initialized, "crypto/ed25519: uninitialized private key") @@ -212,6 +237,11 @@ public_key_equal :: proc(pub_key, other: ^Public_Key) -> bool { return crypto.compare_constant_time(pub_key._b[:], other._b[:]) == 1 } +// public_key_clear clears pub_key to the uninitialized state. +public_key_clear :: proc "contextless" (pub_key: ^Public_Key) { + crypto.zero_explicit(pub_key, size_of(Public_Key)) +} + // verify returns true if and only if (⟺) sig is a valid signature by pub_key over msg. // // The optional `allow_small_order_A` parameter will make this diff --git a/core/crypto/noise/api.odin b/core/crypto/noise/api.odin new file mode 100644 index 000000000..bae58a399 --- /dev/null +++ b/core/crypto/noise/api.odin @@ -0,0 +1,590 @@ +package noise + +import "base:runtime" +import "core:crypto/aead" +import "core:crypto/ecdh" +import "core:crypto/hash" +import "core:strings" + +// MAX_PACKET_SIZE is the maximum Noise message size, including TAG_SIZE +// if relevant (`seal_message`, `open_message`). +MAX_PACKET_SIZE :: 65535 + +// PSK_SIZE is the size of an optional handshake pre-shared symmetric key. +PSK_SIZE :: 32 +// TAG_SIZE is the size of the AEAD authentication tag. +TAG_SIZE :: 16 +// MAX_STEP_MSG_SIZE is the maximum per-handshake step message size, +// excluding the optional payload. +// +// `e` is DH_LEN, `s` is either DH_LEN or DH_LEN + TAG_SIZE, and there +// is a maximum of one per each message, and a possible mandatory tag. +MAX_STEP_MSG_SIZE :: (MAX_DH_SIZE*2)+TAG_SIZE+TAG_SIZE + +// Status is the status of Noise protocol operation. +Status :: enum { + Ok, + + // States + Handshake_Pending, + Handshake_Complete, + Handshake_Split, + Handshake_Failed, + + // Errors + Invalid_Protocol_String, + Invalid_Pre_Shared_Key, + Invalid_DH_Key, + No_Self_Identity, + No_Peer_Identity, + Unexpected_Peer_Identity, + Unexpected_Pre_Shared_Key, + + DH_Failure, + Invalid_Handshake_Message, + + Decryption_Failure, + IV_Exhausted, + Invalid_Cipher_State, + Invalid_Destination_Buffer, + Invalid_Payload_Message, + Max_Packet_Size, + + Out_Of_Memory, +} + +// Handshake_State is the per-handshake state. +Handshake_State :: struct { + s: ecdh.Private_Key, + e: ecdh.Private_Key, + rs: ecdh.Public_Key, + re: ecdh.Public_Key, + psk: [PSK_SIZE]byte, + + symmetric_state: Symmetric_State, + message_pattern: ^Message_Pattern, + current_message: int, + + status: Status, + + initiator: bool, + pre_set_e: bool, +} + +// Cipher_States are the keyed AEAD instances and associated state, +// derived from a successful handshake. +Cipher_States :: struct { + c1_i_to_r: Cipher_State, + c2_r_to_i: Cipher_State, + + initiator: bool, +} + +// handshake_init initializes a Handshake_State with the provided parameters. +// The relevant values are copied into the Handshake_State instance, and +// can be discarded/sanitized right after handshake_init returns (eg: psk). +// +// Note: While this implementation supports setting `e`, this is primarily +// intended for testing, or cases where the runtime cryptographic entropy +// source is unavailable. Use of this functionality is STRONGLY +// discouraged. +@(require_results) +handshake_init :: proc( + self: ^Handshake_State, + initiator: bool, + prologue: []byte, + s: ^ecdh.Private_Key, // Our static key + rs: ^ecdh.Public_Key, // Peer static key + protocol_name: string, + psk: []byte = nil, + _e: ^ecdh.Private_Key = nil, // Our ephemeral key (for testing/RNG-less systems) +) -> Status { + return handshakestate_initialize( + self, + initiator, + prologue, + s, + _e, + rs, + nil, + protocol_name, + psk, + ) +} + +// handshake_initiator_step takes an input_message received from the responder +// if any and an optional payload to be sent to the responder, and performs +// one step of the Noise handshake process, returning the message to be sent +// to the responder if any, the payload received from the responder if any, +// and the status of the handshake. +// +// The output message MUST be sent to the responder even if the status code +// returned is .Handshake_Complete. +// +// If the dst parameter is provided, the message and payload will be written +// to dst, otherwise new buffers will be allocated. +@(require_results) +handshake_initiator_step :: proc( + self: ^Handshake_State, + input_message: []byte, + payload: []byte = nil, + dst: []byte = nil, + allocator := context.allocator, +) -> ([]byte, []byte, Status) { + output_message: []byte + payload_buffer: []byte + status: Status + + dst := dst + if input_message == nil { + output_message, status = handshakestate_write_message(self, payload, dst, allocator) + } else { + payload_buffer, status = handshakestate_read_message(self, input_message, dst, allocator) + if status == .Handshake_Pending { + if dst != nil { + dst = dst[len(payload_buffer):] + } + output_message, status = handshakestate_write_message(self, payload, dst, allocator) + } + } + + return output_message, payload_buffer, status +} + +// handshake_responder_step takes a input_message received from the initiator, +// and and an optional payload to be sent to the initiator, and performs +// one step of the Noise handshake process, returning the message to be sent +// to the initiator if any, the payload received from the initiator if any, +// and the status of the handshake. +// +// The output message MUST be sent to the initiator even if the status code +// returned is .Handshake_Complete. +// +// If the dst parameter is provided, the message and payload will be written +// to dst, otherwise new buffers will be allocated. +@(require_results) +handshake_responder_step :: proc( + self: ^Handshake_State, + input_message: []byte, + payload: []byte = nil, + dst: []byte = nil, + allocator := context.allocator, +) -> ([]byte, []byte, Status) { + output_message: []byte + + if input_message == nil { + return nil, nil, .Invalid_Handshake_Message + } + + dst := dst + payload_buffer, status := handshakestate_read_message(self, input_message, dst, allocator) + if status == .Handshake_Pending { + if dst != nil { + dst = dst[len(payload_buffer):] + } + output_message, status = handshakestate_write_message(self, payload, dst, allocator) + } + + return output_message, payload_buffer, status +} + +// handshake_write_message calls the Noise HandshakeState's WriteMessage +// function directly. In most cases you are better off using +// handshake_initiator_step or handshake_responder_step. +// +// If the dst parameter is provided, the message and payload will be written +// to dst, otherwise new buffers will be allocated. +@(require_results) +handshake_write_message :: proc( + self: ^Handshake_State, + payload: []byte, + dst: []byte = nil, + allocator := context.allocator, +) -> ([]byte, Status) { + return handshakestate_write_message(self, payload, dst, allocator) +} + +// handshake_read_message calls the Noise HandshakeState's ReadMessage +// function directly. In most cases you are better off using +// handshake_initiator_step or handshake_responder_step. +// +// If the dst parameter is provided, the message and payload will be written +// to dst, otherwise new buffers will be allocated. +@(require_results) +handshake_read_message :: proc( + self: ^Handshake_State, + message: []byte, + dst: []byte = nil, + allocator := context.allocator, +) -> ([]byte, Status) { + return handshakestate_read_message(self, message, dst, allocator) +} + +// handshake_split initializes a Cipher_States instance from a completed +// handshake. This can be called once and only once per Handshake_State +// instance. +@(require_results) +handshake_split :: proc(self: ^Handshake_State, cipher_states: ^Cipher_States) -> Status { + if self.status != .Handshake_Complete { + return self.status + } + + symmetricstate_split(&self.symmetric_state, cipher_states) + if self.message_pattern.is_one_way { + cipherstate_reset(&cipher_states.c2_r_to_i) + cipher_states.c2_r_to_i.is_invalid = true + } + cipher_states.initiator = self.initiator + self.status = .Handshake_Split + + return .Ok +} + +// handshake_peer_identity returns the peer's static DH key used by +// a completed handshake. +// +// This returns a pointer to the Handshake_State's copy of the peer's +// public key, that will get wiped by handshake_reset. If the key is +// needed after a call to handshake_reset, it must be copied. +@(require_results) +handshake_peer_identity :: proc(self: ^Handshake_State) -> (^ecdh.Public_Key, Status) { + #partial switch self.status { + case .Handshake_Complete, .Handshake_Split: + case: + return nil, self.status + } + + if ecdh.curve(&self.rs) == .Invalid { + return nil, .No_Peer_Identity + } + + return &self.rs, .Ok +} + +// handshake_hash returns the handshake transcript hash of a completed +// handshake, for the purposes of channel binding. See 11.2 of the +// specification for details on usage. +// +// This returns a slice to an internal buffer that will get wiped by +// handshake_reset. If the hash is needed after a call to handshake_reset, +// the slice must be copied. +@(require_results) +handshake_hash :: proc(self: ^Handshake_State) -> ([]byte, Status) { + #partial switch self.status { + case .Handshake_Complete, .Handshake_Split: + case: + return nil, self.status + } + + return symmetricstate_get_handshake_hash(&self.symmetric_state), .Ok +} + +// handshake_reset sanitizes the Handshake_State. It is both safe and +// recommended to call this as soon as practical (after any calls to +// handshake_peer_identity, handshake_hash, and handshake_split are +// complete). +handshake_reset :: proc(self: ^Handshake_State) { + handshakestate_reset(self) +} + +// seal_message encrypts the provided data, authenticates the aad and +// ciphertext, and returns the resulting ciphertext. The ciphertext +// will ALWAYS be `len(plaintext) + TAG_SIZE` bytes in length. +// +// If the dst parameter is provided, the ciphertext will be written +// to dst, otherwise a new buffer will be allocated. +@(require_results) +seal_message :: proc(self: ^Cipher_States, aad, plaintext: []byte, dst: []byte = nil, allocator := context.allocator) -> ([]byte, Status) { + data_len := len(plaintext) + + dst := dst + did_alloc: bool + switch { + case dst == nil: + err: runtime.Allocator_Error + dst, err = make([]byte, data_len + TAG_SIZE, allocator) + if err != nil { + return nil, .Out_Of_Memory + } + did_alloc = true + case: + if len(dst) != data_len + TAG_SIZE { + return nil, .Invalid_Destination_Buffer + } + } + + status: Status + switch self.initiator { + case true: + dst, status = cipherstate_encrypt_with_ad(&self.c1_i_to_r, aad, plaintext, dst) + case false: + dst, status = cipherstate_encrypt_with_ad(&self.c2_r_to_i, aad, plaintext, dst) + } + if status != .Ok && did_alloc { + delete(dst, allocator) + dst = nil + } + + return dst, status +} + +// open_message authenticates the aad and ciphertext, decrypts the +// ciphertext and returns the resulting plaintext. The plaintext will +// ALWAYS be `len(ciphertext) - TAG_SIZE` bytes in length. +// +// If the dst parameter is provided, the plaintext will be written to +// dst, otherwise a new buffer will be allocated. +@(require_results) +open_message :: proc(self: ^Cipher_States, aad, ciphertext: []byte, dst: []byte = nil, allocator := context.allocator) -> ([]byte, Status) { + if len(ciphertext) < TAG_SIZE { + return nil, .Invalid_Payload_Message + } + + data_len := len(ciphertext) - TAG_SIZE + + dst := dst + did_alloc: bool + switch { + case dst == nil: + if data_len > 0 { + err: runtime.Allocator_Error + dst, err = make([]byte, data_len, allocator) + if err != nil { + return nil, .Out_Of_Memory + } + did_alloc = true + } + case: + if len(dst) != data_len { + return nil, .Invalid_Destination_Buffer + } + } + + status: Status + switch self.initiator { + case true: + dst, status = cipherstate_decrypt_with_ad(&self.c2_r_to_i, aad, ciphertext, dst) + case false: + dst, status = cipherstate_decrypt_with_ad(&self.c1_i_to_r, aad, ciphertext, dst) + } + if status != .Ok && did_alloc { + delete(dst, allocator) + dst = nil + } + + return dst, status +} + +// cipherstates_rekey updates the selected AEAD key, using a one way function. +// See 11.3 of the specification for examples of usage. +// +// Note: If one side updates the seal_key, the other side must update +// the non-seal_key and vice versa. +@(require_results) +cipherstates_rekey :: proc(self: ^Cipher_States, seal_key: bool) -> Status { + cs := cipherstates_cs(self, seal_key) + if cs.is_invalid { + return .Invalid_Cipher_State + } + if !cipherstate_has_key(cs) { + return .Handshake_Pending + } + + cipherstate_rekey(cs) + + return .Ok +} + +// cipherstates_set_n sets the interal counter used to generate the AEAD +// IV to an explicit value. This can be used to deal with out-of-order +// transport messages. See 11.4 of the specification. +// +// WARNING: Reusing n across different aad/messages with the same Cipher_States +// will result in catastrophic loss of security. +@(require_results) +cipherstates_set_n :: proc(self: ^Cipher_States, seal_key: bool, n: u64) -> Status { + cs := cipherstates_cs(self, seal_key) + if cs.is_invalid { + return .Invalid_Cipher_State + } + if !cipherstate_has_key(cs) { + return .Handshake_Pending + } + + cs.n = n + + return .Ok +} + +// cipherstates_n returns the interal counter used to generate the AEAD +// IV. This can be used to deal with out-of-order transport messages. +// See 11.4 of the specification. +// +// WARNING: Reusing n across different aad/messages with the same Cipher_States +// will result in catastrophic loss of security. +@(require_results) +cipherstates_n :: proc(self: ^Cipher_States, seal_key: bool, n: u64) -> (u64, Status) { + cs := cipherstates_cs(self, seal_key) + if cs.is_invalid { + return 0, .Invalid_Cipher_State + } + if !cipherstate_has_key(cs) { + return 0, .Handshake_Pending + } + + return cs.n, .Ok +} + +// cipherstates_reset sanitizes the Cipher_States. +cipherstates_reset :: proc(self: ^Cipher_States) { + self.initiator = false + cipherstate_reset(&self.c1_i_to_r) + cipherstate_reset(&self.c2_r_to_i) +} + +@(private = "file") +cipherstates_cs :: proc(self: ^Cipher_States, seal_key: bool) -> ^Cipher_State { + switch self.initiator { + case true: + switch seal_key { + case true: + return &self.c1_i_to_r + case false: + return &self.c2_r_to_i + } + case false: + switch seal_key { + case true: + return &self.c2_r_to_i + case false: + return &self.c1_i_to_r + } + } + unreachable() +} + +// split_protocol_string splits a protocol string into individual components. +@(require_results) +split_protocol_string :: proc(protocol_name: string) -> (Handshake_Pattern, ecdh.Curve, aead.Algorithm, hash.Algorithm, Status) { + str := protocol_name + + if len(str) > 255 { + return .Invalid, .Invalid, .Invalid, .Invalid, .Invalid_Protocol_String + } + + s, ok := strings.split_by_byte_iterator(&str, '_') + if !ok || s != "Noise" { + return .Invalid, .Invalid, .Invalid, .Invalid, .Invalid_Protocol_String + } + + if s, ok = strings.split_by_byte_iterator(&str, '_'); !ok { + return .Invalid, .Invalid, .Invalid, .Invalid, .Invalid_Protocol_String + } + + pattern: Handshake_Pattern + switch s { + case "N" : pattern = .N + case "K" : pattern = .K + case "X" : pattern = .X + case "XX": pattern = .XX + case "NK": pattern = .NK + case "NN": pattern = .NN + case "KN": pattern = .KN + case "KK": pattern = .KK + case "NX": pattern = .NX + case "KX": pattern = .KX + case "XN": pattern = .XN + case "IN": pattern = .IN + case "XK": pattern = .XK + case "IK": pattern = .IK + case "IX": pattern = .IX + case "NK1": pattern = .NK1 + case "NX1": pattern = .NX1 + case "X1N": pattern = .X1N + case "X1K": pattern = .X1K + case "XK1": pattern = .XK1 + case "X1K1": pattern = .X1K1 + case "X1X": pattern = .X1X + case "XX1": pattern = .XX1 + case "X1X1": pattern = .X1X1 + case "K1N": pattern = .K1N + case "K1K": pattern = .K1K + case "KK1": pattern = .KK1 + case "K1K1": pattern = .K1K1 + case "K1X": pattern = .K1X + case "KX1": pattern = .KX1 + case "K1X1": pattern = .K1X1 + case "I1N": pattern = .I1N + case "I1K": pattern = .I1K + case "IK1": pattern = .IK1 + case "I1K1": pattern = .I1K1 + case "I1X": pattern = .I1X + case "IX1": pattern = .IX1 + case "I1X1": pattern = .I1X1 + case "Npsk0": pattern = .Npsk0 + case "Kpsk0": pattern = .Kpsk0 + case "Xpsk1": pattern = .Xpsk1 + case "NNpsk0": pattern = .NNpsk0 + case "NNpsk2": pattern = .NNpsk2 + case "NKpsk0": pattern = .NKpsk0 + case "NKpsk2": pattern = .NKpsk2 + case "NXpsk2": pattern = .NXpsk2 + case "XNpsk3": pattern = .XNpsk3 + case "XKpsk3": pattern = .XKpsk3 + case "XXpsk3": pattern = .XXpsk3 + case "KNpsk0": pattern = .KNpsk0 + case "KNpsk2": pattern = .KNpsk2 + case "KKpsk0": pattern = .KKpsk0 + case "KKpsk2": pattern = .KKpsk2 + case "KXpsk2": pattern = .KXpsk2 + case "INpsk1": pattern = .INpsk1 + case "INpsk2": pattern = .INpsk2 + case "IKpsk1": pattern = .IKpsk1 + case "IKpsk2": pattern = .IKpsk2 + case "IXpsk2": pattern = .IXpsk2 + case: pattern = .Invalid + } + + if s, ok = strings.split_by_byte_iterator(&str, '_'); !ok { + return .Invalid, .Invalid, .Invalid, .Invalid, .Invalid_Protocol_String + } + dh: ecdh.Curve + switch s { + case "25519": dh = .X25519 + case "448": dh = .X448 + case: dh = .Invalid + } + + if s, ok = strings.split_by_byte_iterator(&str, '_'); !ok { + return .Invalid, .Invalid, .Invalid, .Invalid, .Invalid_Protocol_String + } + cipher: aead.Algorithm + switch s { + case "AESGCM": cipher = .AES_GCM_256 + case "ChaChaPoly": cipher = .CHACHA20POLY1305 + case: cipher = .Invalid + } + + if s, ok = strings.split_by_byte_iterator(&str, '_'); !ok { + return .Invalid, .Invalid, .Invalid, .Invalid, .Invalid_Protocol_String + } + hash: hash.Algorithm + switch s { + case "SHA512": hash = .SHA512 + case "SHA256": hash = .SHA256 + case "BLAKE2s": hash = .BLAKE2S + case "BLAKE2b": hash = .BLAKE2B + case: hash = .Invalid + } + + status: Status + + if len(str) != 0 { + status = .Invalid_Protocol_String + } + if pattern == .Invalid || dh == .Invalid || cipher == .Invalid || hash == .Invalid { + status = .Invalid_Protocol_String + } + + return pattern, dh, cipher, hash, status +} diff --git a/core/crypto/noise/doc.odin b/core/crypto/noise/doc.odin new file mode 100644 index 000000000..734a1dc6e --- /dev/null +++ b/core/crypto/noise/doc.odin @@ -0,0 +1,35 @@ +/* +An implementation of the Noise Protocol Framework (Revision 34). + +The `fallback` modifier and multi-PSK patterns are not supported +for the sake of simplicity. + +See: +- [[ https://noiseprotocol.org/ ]] +*/ +package noise + +// In general, to complete a noise handshake you must: +// +// - If you are initiating the connection, call `handshake_initiator_step` +// passing `nil` as the `input_message` parameter. +// +// - Send the resulting `[]byte` to the responder (generally a server) via +// the method of your choice. This MUST be done even if the status code +// returned is `.Handshake_Complete`. +// +// - If the status code returned by `handshake_initiator_step` was +// `.Handshake_Complete`, the handshake completed successfully, +// and it is now possible to validate the peer identity, obtain the +// handshake transcript hash, and most usefully call `handshake_split` +// to populate the `Cipher_States` struct that will be used to +// encrypt/decrypt data. +// +// Otherwise, read the response from the responder and feed the response +// data as the `input_message` to the next `handshake_initiator_step` +// until it returns `.Handshake_Complete`. +// +// - If you are the responder, the method is much the same, except you +// must pass a valid `input_message` received from an initiator to the +// first call to `handshake_responder_step`. Repeat until the returned +// status is `.Handshake_Complete`. diff --git a/core/crypto/noise/patterns.odin b/core/crypto/noise/patterns.odin new file mode 100644 index 000000000..14e5b6498 --- /dev/null +++ b/core/crypto/noise/patterns.odin @@ -0,0 +1,1098 @@ +package noise + +import "core:slice" + +@(private) +Pre_Token :: enum { + res_s, + ini_s, +} + +@(private) +Token :: enum { + e, + s, + ee, + es, + se, + ss, + psk, +} + +@(private) +Message_Pattern :: struct { + pre_messages: []Pre_Token, + messages: [][]Token, + is_psk: bool, + is_one_way: bool, +} + +// Handshake_Pattern is the list of currently supported Noise Handshake +// Patterns. +Handshake_Pattern :: enum { + Invalid, + + // One way patterns + N, + K, + X, + + // Fundamental patterns + XX, + NK, + NN, + KN, + KK, + NX, + KX, + XN, + IN, + XK, + IK, + IX, + + // Deferred patterns + NK1, + NX1, + X1N, + X1K, + XK1, + X1K1, + X1X, + XX1, + X1X1, + K1N, + K1K, + KK1, + K1K1, + K1X, + KX1, + K1X1, + I1N, + I1K, + IK1, + I1K1, + I1X, + IX1, + I1X1, + + // Recommended PSK patterns + Npsk0, + Kpsk0, + Xpsk1, + NNpsk0, + NNpsk2, + NKpsk0, + NKpsk2, + NXpsk2, + XNpsk3, + XKpsk3, + XXpsk3, + KNpsk0, + KNpsk2, + KKpsk0, + KKpsk2, + KXpsk2, + INpsk1, + INpsk2, + IKpsk1, + IKpsk2, + IXpsk2, +} + +@(require_results) +pattern_requires_initiator_s :: proc(pattern: Handshake_Pattern) -> (pre: bool, hs: bool) { + p := HANDSHAKE_PATTERNS[pattern] + if slice.contains(p.pre_messages, Pre_Token.ini_s) { + pre = true + } + for msg, i in p.messages { + if i & 1 != 0 { + continue + } + if slice.contains(msg, Token.s) { + hs = true + break + } + } + return pre, hs +} + +@(require_results) +pattern_requires_responder_s :: proc(pattern: Handshake_Pattern) -> (pre: bool, hs: bool) { + p := HANDSHAKE_PATTERNS[pattern] + if slice.contains(p.pre_messages, Pre_Token.res_s) { + pre = true + } + for msg, i in p.messages { + if i & 1 == 0 { + continue + } + if slice.contains(msg, Token.s) { + hs = true + break + } + } + return pre, hs +} + +@(require_results) +pattern_is_psk :: proc(pattern: Handshake_Pattern) -> bool { + return HANDSHAKE_PATTERNS[pattern].is_psk +} + +@(require_results) +pattern_is_one_way :: proc(pattern: Handshake_Pattern) -> bool { + return HANDSHAKE_PATTERNS[pattern].is_one_way +} + +@(require_results) +pattern_num_messages :: proc(pattern: Handshake_Pattern) -> int { + return len(HANDSHAKE_PATTERNS[pattern].messages) +} + +@(private) +HANDSHAKE_PATTERNS := [Handshake_Pattern]^Message_Pattern { + .Invalid = nil, + .N = &PATTERN_N, + .K = &PATTERN_K, + .X = &PATTERN_X, + + .XX = &PATTERN_XX, + .NK = &PATTERN_NK, + .NN = &PATTERN_NN, + .KN = &PATTERN_KN, + .KK = &PATTERN_KK, + .NX = &PATTERN_NX, + .KX = &PATTERN_KX, + .XN = &PATTERN_XN, + .IN = &PATTERN_IN, + .XK = &PATTERN_XK, + .IK = &PATTERN_IK, + .IX = &PATTERN_IX, + + .NK1 = &PATTERN_NK1, + .NX1 = &PATTERN_NX1, + .X1N = &PATTERN_X1N, + .X1K = &PATTERN_X1K, + .XK1 = &PATTERN_XK1, + .X1K1 = &PATTERN_X1K1, + .X1X = &PATTERN_X1X, + .XX1 = &PATTERN_XX1, + .X1X1 = &PATTERN_X1X1, + .K1N = &PATTERN_K1N, + .K1K = & PATTERN_K1K, + .KK1 = &PATTERN_KK1, + .K1K1 = &PATTERN_K1K1, + .K1X = &PATTERN_K1X, + .KX1 = &PATTERN_KX1, + .K1X1 = &PATTERN_K1X1, + .I1N = &PATTERN_I1N, + .I1K = &PATTERN_I1K, + .IK1 = &PATTERN_IK1, + .I1K1 = &PATTERN_I1K1, + .I1X = &PATTERN_I1X, + .IX1 = &PATTERN_IX1, + .I1X1 = &PATTERN_I1X1, + + .Npsk0 = &PATTERN_Npsk0, + .Kpsk0 = &PATTERN_Kpsk0, + .Xpsk1 = &PATTERN_Xpsk1, + .NNpsk0 = &PATTERN_NNpsk0, + .NNpsk2 = &PATTERN_NNpsk2, + .NKpsk0 = &PATTERN_NKpsk0, + .NKpsk2 = &PATTERN_NKpsk2, + .NXpsk2 = &PATTERN_NXpsk2, + .XNpsk3 = &PATTERN_XNpsk3, + .XKpsk3 = &PATTERN_XKpsk3, + .XXpsk3 = &PATTERN_XXpsk3, + .KNpsk0 = &PATTERN_KNpsk0, + .KNpsk2 = &PATTERN_KNpsk2, + .KKpsk0 = &PATTERN_KKpsk0, + .KKpsk2 = &PATTERN_KKpsk2, + .KXpsk2 = &PATTERN_KXpsk2, + .INpsk1 = &PATTERN_INpsk1, + .INpsk2 = &PATTERN_INpsk2, + .IKpsk1 = &PATTERN_IKpsk1, + .IKpsk2 = &PATTERN_IKpsk2, + .IXpsk2 = &PATTERN_IXpsk2, +} + +// ------------- ONE WAY PATTERNS --------------------------------------------------------- + +// N: +// <- s +// ... +// -> e, es +@(private,rodata) +PATTERN_N : Message_Pattern = { + pre_messages = {.res_s}, + messages = { + {.e, .es}, + }, + is_one_way = true, +} + +// K: +// -> s +// <- s +// ... +// -> e, es, ss +@(private,rodata) +PATTERN_K : Message_Pattern = { + pre_messages = {.ini_s, .res_s}, + messages = { + {.e, .es, .ss}, + }, + is_one_way = true, +} + +// X: +// <- s +// ... +// -> e, es, s, ss +@(private,rodata) +PATTERN_X : Message_Pattern = { + pre_messages = {.res_s}, + messages = { + {.e, .es, .s, .ss}, + }, + is_one_way = true, +} + +// ---------------------------------------------------------------------------------------- + +// ------------- FUNDAMENTAL PATTERNS ----------------------------------------------------- + +// XX: +// -> e +// <- e, ee, s, es +// -> s, se +@(private,rodata) +PATTERN_XX : Message_Pattern = { + pre_messages = nil, + messages = { + {.e}, + {.e, .ee, .s, .es}, + {.s, .se}, + }, +} + +// NK: +// <- s +// ... +// -> e, es +// <- e, ee +@(private,rodata) +PATTERN_NK : Message_Pattern = { + pre_messages = {.res_s}, + messages = { + {.e, .es}, + {.e, .ee}, + }, +} + +// NN: +// -> e +// <- e, ee +@(private,rodata) +PATTERN_NN : Message_Pattern = { + pre_messages = nil, + messages = { + {.e}, + {.e, .ee}, + }, +} + +// KN: +// -> s +// ... +// -> e +// <- e, ee, se +@(private,rodata) +PATTERN_KN : Message_Pattern = { + pre_messages = {.ini_s}, + messages = { + {.e,}, + {.e, .ee, .se}, + }, +} + +// KK: +// -> s +// <- s +// ... +// -> e, es, ss +// <- e, ee, se +@(private,rodata) +PATTERN_KK : Message_Pattern = { + pre_messages = {.ini_s, .res_s}, + messages = { + {.e, .es, .ss}, + {.e, .ee, .se}, + }, +} + +// NX: +// -> e +// <- e, ee, s, es +@(private,rodata) +PATTERN_NX : Message_Pattern = { + pre_messages = nil, + messages = { + {.e}, + {.e, .ee, .s, .es}, + }, +} + +// KX: +// -> s +// ... +// -> e +// <- e, ee, se, s, es +@(private,rodata) +PATTERN_KX : Message_Pattern = { + pre_messages = {.ini_s}, + messages = { + {.e}, + {.e, .ee, .se, .s, .es}, + }, +} + +// XN: +// -> e +// <- e, ee +// -> s, se +@(private,rodata) +PATTERN_XN : Message_Pattern = { + pre_messages = nil, + messages = { + {.e}, + {.e, .ee}, + {.s, .se}, + }, +} + +// IN: +// -> e, s +// <- e, ee, se +@(private,rodata) +PATTERN_IN : Message_Pattern = { + pre_messages = nil, + messages = { + {.e, .s}, + {.e, .ee, .se}, + }, +} + +// XK: +// <- s +// ... +// -> e, es +// <- e, ee +// -> s, se +@(private,rodata) +PATTERN_XK : Message_Pattern = { + pre_messages = {.res_s}, + messages = { + {.e, .es}, + {.e, .ee}, + {.s, .se}, + }, +} + +// IK: +// <- s +// ... +// -> e, es, s, ss +// <- e, ee, se +@(private,rodata) +PATTERN_IK : Message_Pattern = { + pre_messages = {.res_s}, + messages = { + {.e, .es, .s, .ss}, + {.e, .ee, .se}, + }, +} + +// IX: +// -> e, s +// <- e, ee, se, s, es +@(private,rodata) +PATTERN_IX : Message_Pattern = { + pre_messages = nil, + messages = { + {.e, .s}, + {.e, .ee, .se, .s, .es}, + }, +} + +// ---------------------------------------------------------------------------------------- + +// ------------- DEFERRED PATTERNS -------------------------------------------------------- + +// NK1: +// <- s +// ... +// -> e +// <- e, ee, es +@(private,rodata) +PATTERN_NK1 : Message_Pattern = { + pre_messages = {.res_s}, + messages = { + {.e}, + {.e, .ee, .es}, + }, +} + +// NX1: +// -> e +// <- e, ee, s +// -> es +@(private,rodata) +PATTERN_NX1 : Message_Pattern = { + pre_messages = nil, + messages = { + {.e}, + {.e, .ee, .s}, + {.es}, + }, +} + +// X1N: +// -> e +// <- e, ee +// -> s +// <- se +@(private,rodata) +PATTERN_X1N : Message_Pattern = { + pre_messages = nil, + messages = { + {.e}, + {.e, .ee}, + {.s}, + {.se}, + }, +} + +// X1K: +// <- s +// ... +// -> e, es +// <- e, ee +// -> s +// <- se +@(private,rodata) +PATTERN_X1K : Message_Pattern = { + pre_messages = {.res_s}, + messages = { + {.e, .es}, + {.e, .ee}, + {.s}, + {.se}, + }, +} + +// XK1: +// <- s +// ... +// -> e +// <- e, ee, es +// -> s, se +@(private,rodata) +PATTERN_XK1 : Message_Pattern = { + pre_messages = {.res_s}, + messages = { + {.e}, + {.e, .ee, .es}, + {.s, .se}, + }, +} + +// X1K1: +// <- s +// ... +// -> e +// <- e, ee, es +// -> s +// <- se +@(private,rodata) +PATTERN_X1K1 : Message_Pattern = { + pre_messages = {.res_s}, + messages = { + {.e}, + {.e, .ee, .es}, + {.s}, + {.se}, + }, +} + +// X1X: +// -> e +// <- e, ee, s, es +// -> s +// <- se +@(private,rodata) +PATTERN_X1X : Message_Pattern = { + pre_messages = nil, + messages = { + {.e}, + {.e, .ee, .s, .es}, + {.s}, + {.se}, + }, +} + +// XX1: +// -> e +// <- e, ee, s +// -> es, s, se +@(private,rodata) +PATTERN_XX1 : Message_Pattern = { + pre_messages = nil, + messages = { + {.e}, + {.e, .ee, .s}, + {.es, .s, .se}, + }, +} + +// X1X1: +// -> e +// <- e, ee, s +// -> es, s +// <- se +@(private,rodata) +PATTERN_X1X1 : Message_Pattern = { + pre_messages = nil, + messages = { + {.e}, + {.e, .ee, .s}, + {.es, .s}, + {.se}, + }, +} + +// K1N: +// -> s +// ... +// -> e +// <- e, ee +// -> se +@(private,rodata) +PATTERN_K1N : Message_Pattern = { + pre_messages = {.ini_s}, + messages = { + {.e,}, + {.e, .ee}, + {.se}, + }, +} + +// K1K: +// -> s +// <- s +// ... +// -> e, es +// <- e, ee +// -> se +@(private,rodata) +PATTERN_K1K : Message_Pattern = { + pre_messages = {.ini_s, .res_s}, + messages = { + {.e, .es}, + {.e, .ee}, + {.se}, + }, +} + +// KK1: +// -> s +// <- s +// ... +// -> e +// <- e, ee, se, es +@(private,rodata) +PATTERN_KK1 : Message_Pattern = { + pre_messages = {.ini_s, .res_s}, + messages = { + {.e}, + {.e, .ee, .se, .es}, + }, +} + +// K1K1: +// -> s +// <- s +// ... +// -> e +// <- e, ee, es +// -> se +@(private,rodata) +PATTERN_K1K1 : Message_Pattern = { + pre_messages = {.ini_s, .res_s}, + messages = { + {.e}, + {.e, .ee, .es}, + {.se}, + }, +} + +// K1X: +// -> s +// ... +// -> e +// <- e, ee, s, es +// -> se +@(private,rodata) +PATTERN_K1X : Message_Pattern = { + pre_messages = {.ini_s}, + messages = { + {.e}, + {.e, .ee, .s, .es}, + {.se}, + }, +} + +// KX1: +// -> s +// ... +// -> e +// <- e, ee, se, s +// -> es +@(private,rodata) +PATTERN_KX1 : Message_Pattern = { + pre_messages = {.ini_s}, + messages = { + {.e}, + {.e, .ee, .se, .s}, + {.es}, + }, +} + +// K1X1: +// -> s +// ... +// -> e +// <- e, ee, s +// -> se, es +@(private,rodata) +PATTERN_K1X1 : Message_Pattern = { + pre_messages = {.ini_s}, + messages = { + {.e}, + {.e, .ee, .s}, + {.se, .es}, + }, +} + +// I1N: +// -> e, s +// <- e, ee +// -> se +@(private,rodata) +PATTERN_I1N : Message_Pattern = { + pre_messages = nil, + messages = { + {.e, .s}, + {.e, .ee}, + {.se}, + }, +} + +// I1K: +// <- s +// ... +// -> e, es, s +// <- e, ee +// -> se +@(private,rodata) +PATTERN_I1K : Message_Pattern = { + pre_messages = {.res_s}, + messages = { + {.e, .es, .s}, + {.e, .ee}, + {.se}, + }, +} + +// IK1: +// <- s +// ... +// -> e, s +// <- e, ee, se, es +@(private,rodata) +PATTERN_IK1 : Message_Pattern = { + pre_messages = {.res_s}, + messages = { + {.e, .s}, + {.e, .ee, .se, .es}, + }, +} + +// I1K1: +// <- s +// ... +// -> e, s +// <- e, ee, es +// -> se +@(private,rodata) +PATTERN_I1K1 : Message_Pattern = { + pre_messages = {.res_s}, + messages = { + {.e, .s}, + {.e, .ee, .es}, + {.se}, + }, +} + +// I1X: +// -> e, s +// <- e, ee, s, es +// -> se +@(private,rodata) +PATTERN_I1X : Message_Pattern = { + pre_messages = nil, + messages = { + {.e, .s}, + {.e, .ee, .s, .es}, + {.se}, + }, +} + +// IX1: +// -> e, s +// <- e, ee, se, s +// -> es +@(private,rodata) +PATTERN_IX1 : Message_Pattern = { + pre_messages = nil, + messages = { + {.e, .s}, + {.e, .ee, .se, .s}, + {.es}, + }, +} + +// I1X1: +// -> e, s +// <- e, ee, s +// -> se, es +@(private,rodata) +PATTERN_I1X1 : Message_Pattern = { + pre_messages = nil, + messages = { + {.e, .s}, + {.e, .ee, .s}, + {.se, .es}, + }, +} + +// ---------------------------------------------------------------------------------------- + +// ------------- PSK PATTERNS ------------------------------------------------------------- + +// Npsk0: +// <- s +// ... +// -> psk, e, es +@(private,rodata) +PATTERN_Npsk0 : Message_Pattern = { + pre_messages = {.res_s}, + messages = { + {.psk, .e, .es}, + }, + is_psk = true, + is_one_way = true, +} + +// K: +// -> s +// <- s +// ... +// -> psk, e, es, ss +@(private,rodata) +PATTERN_Kpsk0 : Message_Pattern = { + pre_messages = {.ini_s, .res_s}, + messages = { + {.psk, .e, .es, .ss}, + }, + is_psk = true, + is_one_way = true, +} + +// X: +// <- s +// ... +// -> e, es, s, ss, psk +@(private,rodata) +PATTERN_Xpsk1 : Message_Pattern = { + pre_messages = {.res_s}, + messages = { + {.e, .es, .s, .ss, .psk}, + }, + is_psk = true, + is_one_way = true, +} + +// NNpsk0: +// -> psk, e +// <- e, ee +@(private,rodata) +PATTERN_NNpsk0 : Message_Pattern = { + pre_messages = nil, + messages = { + {.psk, .e}, + {.e, .ee}, + }, + is_psk = true, +} + +// NNpsk2: +// -> e +// <- e, ee, psk +@(private,rodata) +PATTERN_NNpsk2 : Message_Pattern = { + pre_messages = nil, + messages = { + {.e}, + {.e, .ee, .psk}, + }, + is_psk = true, +} + +// NKpsk0: +// <- s +// ... +// -> psk, e, es +// <- e, ee +@(private,rodata) +PATTERN_NKpsk0 : Message_Pattern = { + pre_messages = {.res_s}, + messages = { + {.psk, .e, .es}, + {.e, .ee}, + }, + is_psk = true, +} + +// NKpsk2: +// <- s +// ... +// -> e, es +// <- e, ee, psk +@(private,rodata) +PATTERN_NKpsk2 : Message_Pattern = { + pre_messages = {.res_s}, + messages = { + {.e, .es}, + {.e, .ee, .psk}, + }, + is_psk = true, +} + +// NXpsk2: +// -> e +// <- e, ee, s, es, psk +@(private,rodata) +PATTERN_NXpsk2 : Message_Pattern = { + pre_messages = nil, + messages = { + {.e}, + {.e, .ee, .s, .es, .psk}, + }, + is_psk = true, +} + +// XNpsk3: +// -> e +// <- e, ee +// -> s, se, psk +@(private,rodata) +PATTERN_XNpsk3 : Message_Pattern = { + pre_messages = nil, + messages = { + {.e}, + {.e, .ee}, + {.s, .se, .psk}, + }, + is_psk = true, +} + +// XKpsk3: +// <- s +// ... +// -> e, es +// <- e, ee +// -> s, se, psk +@(private,rodata) +PATTERN_XKpsk3 : Message_Pattern = { + pre_messages = {.res_s}, + messages = { + {.e, .es}, + {.e, .ee}, + {.s, .se, .psk}, + }, + is_psk = true, +} + +// XXpsk3: +// -> e +// <- e, ee, s, es +// -> s, se, psk +@(private,rodata) +PATTERN_XXpsk3 : Message_Pattern = { + pre_messages = nil, + messages = { + {.e}, + {.e, .ee, .s, .es}, + {.s, .se, .psk}, + }, + is_psk = true, +} + +// KNpsk0: +// -> s +// ... +// -> psk, e +// <- e, ee, se +@(private,rodata) +PATTERN_KNpsk0 : Message_Pattern = { + pre_messages = {.ini_s}, + messages = { + {.psk, .e}, + {.e, .ee, .se}, + }, + is_psk = true, +} + +// KNpsk2: +// -> s +// ... +// -> e +// <- e, ee, se, psk +@(private,rodata) +PATTERN_KNpsk2 : Message_Pattern = { + pre_messages = {.ini_s}, + messages = { + {.e}, + {.e, .ee, .se, .psk}, + }, + is_psk = true, +} + +// KKpsk0: +// -> s +// <- s +// ... +// -> psk, e, es, ss +// <- e, ee, se +@(private,rodata) +PATTERN_KKpsk0 : Message_Pattern = { + pre_messages = {.ini_s, .res_s}, + messages = { + {.psk, .e, .es, .ss}, + {.e, .ee, .se}, + }, + is_psk = true, +} + +// KKpsk2: +// -> s +// <- s +// ... +// -> e, es, ss +// <- e, ee, se, psk +@(private,rodata) +PATTERN_KKpsk2 : Message_Pattern = { + pre_messages = {.ini_s, .res_s}, + messages = { + {.e, .es, .ss}, + {.e, .ee, .se, .psk}, + }, + is_psk = true, +} + +// KXpsk2: +// -> s +// ... +// -> e +// <- e, ee, se, s, es, psk +@(private,rodata) +PATTERN_KXpsk2 : Message_Pattern = { + pre_messages = {.ini_s}, + messages = { + {.e}, + {.e, .ee, .se, .s, .es, .psk}, + }, + is_psk = true, +} + +// INpsk1: +// -> e, s, psk +// <- e, ee, se +@(private,rodata) +PATTERN_INpsk1 : Message_Pattern = { + pre_messages = nil, + messages = { + {.e, .s, .psk}, + {.e, .ee, .se}, + }, + is_psk = true, +} + +// INpsk2: +// -> e, s +// <- e, ee, se, psk +@(private,rodata) +PATTERN_INpsk2 : Message_Pattern = { + pre_messages = nil, + messages = { + {.e, .s}, + {.e, .ee, .se, .psk}, + }, + is_psk = true, +} + +// IKpsk1: +// <- s +// ... +// -> e, es, s, ss, psk +// <- e, ee, se +@(private,rodata) +PATTERN_IKpsk1 : Message_Pattern = { + pre_messages = {.res_s}, + messages = { + {.e, .es, .s, .ss, .psk}, + {.e, .ee, .se}, + }, + is_psk = true, +} + +// IKpsk2: +// <- s +// ... +// -> e, es, s, ss +// <- e, ee, se, psk +@(private,rodata) +PATTERN_IKpsk2 : Message_Pattern = { + pre_messages = {.res_s}, + messages = { + {.e, .es, .s, .ss}, + {.e, .ee, .se, .psk}, + }, + is_psk = true, +} + +// IXpsk2: +// -> e, s +// <- e, ee, se, s, es, psk +@(private,rodata) +PATTERN_IXpsk2 : Message_Pattern = { + pre_messages = nil, + messages = { + {.e, .s}, + {.e, .ee, .se, .s, .es, .psk}, + }, + is_psk = true, +} diff --git a/core/crypto/noise/protocol.odin b/core/crypto/noise/protocol.odin new file mode 100644 index 000000000..883376a42 --- /dev/null +++ b/core/crypto/noise/protocol.odin @@ -0,0 +1,982 @@ +#+private +package noise + +import "base:runtime" +import "core:crypto" +import "core:crypto/aead" +import "core:crypto/ecdh" +import "core:crypto/hash" +import "core:crypto/hkdf" +import "core:encoding/endian" +import "core:slice" + +AEAD_KEY_SIZE :: 32 + +MIN_DH_SIZE :: 32 +MAX_DH_SIZE :: 56 +MAX_HASH_SIZE :: 64 + +Protocol :: struct { + handshake_pattern: Handshake_Pattern, + dh: ecdh.Curve, + cipher: aead.Algorithm, + hash: hash.Algorithm, +} + +Symmetric_State :: struct { + protocol: Protocol, + cipher_state: Cipher_State, + + _ck: [MAX_HASH_SIZE]byte, + _h: [MAX_HASH_SIZE]byte, +} + +Cipher_State :: struct { + ctx: aead.Context, + n: u64, + n_exhausted: bool, + is_invalid: bool, +} + +@(require_results) +dh_len :: proc(protocol: ^Protocol) -> int { + return ecdh.PUBLIC_KEY_SIZES[protocol.dh] +} + +@(require_results) +hash_len :: proc(protocol: ^Protocol) -> int { + return hash.DIGEST_SIZES[protocol.hash] +} + +// Generates a new Diffie-Hellman key pair. A DH key pair consists of +// public_key and private_key elements. public_key represents an encoding +// of a DH public key into a byte sequence of length DHLEN. The public_key +// encoding details are specific to each set of DH functions. +generate_keypair :: proc(protocol: ^Protocol, private_key: ^ecdh.Private_Key) { + #partial switch protocol.dh { + case .X25519, .X448: + case: panic("crypto/noise: unsupported DH curve in protocol") + } + + ecdh.private_key_generate(private_key, protocol.dh) +} + +// Performs a Diffie-Hellman calculation between the private key in key_pair +// and the public_key and returns an output sequence of bytes of length DHLEN. +// For security, the Gap-DH problem based on this function must be unsolvable +// by any practical cryptanalytic adversary [2]. +// +// The public_key either encodes some value which is a generator in a +// large prime-order group (which value may have multiple equivalent +// encodings), or is an invalid value. Implementations must handle invalid +// public keys either by returning some output which is purely a function +// of the public key and does not depend on the private key, or by signaling +// an error to the caller. +// +// The DH function may define more specific rules for handling invalid values. +@(require_results) +_dh :: proc(our_private_key: ^ecdh.Private_Key, their_public_key: ^ecdh.Public_Key, dst: []byte) -> Status { + if ok := ecdh.ecdh(our_private_key, their_public_key, dst); !ok { + return .DH_Failure + } + return .Ok +} + +// Encrypts plaintext using the cipher key k of 32 bytes and an 8-byte +// unsigned integer nonce n which must be unique for the key k. +// Returns the ciphertext. Encryption must be done with an "AEAD" +// encryption mode with the associated data(AD) (using the terminology +// from [1]) and returns a ciphertext that is the same size as the plaintext +// plus 16 bytes for authentication data. The entire ciphertext must be +// indistinguishable from random if the key is secret (note that this is +// an additional requirement that isn't necessarily met by all AEAD schemes). +_encrypt :: proc(ctx: ^aead.Context, n: u64, ad, plaintext, dst: []byte) { + pt_len := len(plaintext) + ensure(len(dst) == pt_len + TAG_SIZE, "crypto/noise: invalid AEAD encrypt destination") + + iv: [12]byte + #partial switch aead.algorithm(ctx) { + case .AES_GCM_256: endian.unchecked_put_u64be(iv[4:], n) + case .CHACHA20POLY1305: endian.unchecked_put_u64le(iv[4:], n) + } + + ciphertext, tag := dst[:pt_len], dst[pt_len:] + aead.seal_ctx(ctx, ciphertext, tag, iv[:], ad, plaintext) +} + +// Decrypts ciphertext using a cipher key k of 32 bytes, an 8-byte unsigned +// integer nonce n, and associated data ad. Returns the plaintext, unless +// authentication fails, in which case an error is signaled to the caller. +@(require_results) +_decrypt :: proc(ctx: ^aead.Context, n: u64, ad, ciphertext, dst: []byte) -> Status { + if len(ciphertext) < TAG_SIZE { + return .Decryption_Failure + } + + iv: [12]byte + #partial switch aead.algorithm(ctx) { + case .AES_GCM_256: endian.unchecked_put_u64be(iv[4:], n) + case .CHACHA20POLY1305: endian.unchecked_put_u64le(iv[4:], n) + } + + ct_len := len(ciphertext) - TAG_SIZE + ct, tag := ciphertext[:ct_len], ciphertext[ct_len:] + if ok := aead.open_ctx(ctx, dst, iv[:], ad, ct, tag); !ok { + return .Decryption_Failure + } + + return .Ok +} + +// Hashes some arbitrary-length data with a collision-resistant cryptographic +// hash function and returns an output of HASHLEN bytes. +_hash :: proc(dst: []byte, protocol: ^Protocol, data: ..[]byte) { + ctx: hash.Context + hash.init(&ctx, protocol.hash) + + for datum in data { + hash.update(&ctx, datum) + } + + hash.final(&ctx, dst) +} + +// Takes a chaining_key byte sequence of length HASHLEN, and an +// input_key_material byte sequence with length either zero bytes, +// 32 bytes, or DHLEN bytes. Returns a pair or triple of byte sequences +// each of length HASHLEN, depending on whether num_outputs is two or three: +// - Sets temp_key = HMAC-HASH(chaining_key, input_key_material). +// - Sets output1 = HMAC-HASH(temp_key, byte(0x01)). +// - Sets output2 = HMAC-HASH(temp_key, output1 || byte(0x02)). +// - If num_outputs == 2 then returns the pair (output1, output2). +// - Sets output3 = HMAC-HASH(temp_key, output2 || byte(0x03)). +// - Returns the triple (output1, output2, output3). +// +// Note that temp_key, output1, output2, and output3 are all HASHLEN +// bytes in length. Also note that the HKDF() function is simply HKDF +// from [4] with the chaining_key as HKDF salt, and zero-length HKDF info. +@(require_results) +_hkdf :: proc(dst, chaining_key, input_key_material: []byte, protocol: ^Protocol) -> ([]byte, []byte, []byte) { + assert(len(input_key_material) == 0 || len(input_key_material) == 32 || len(input_key_material) == dh_len(protocol)) + + hkdf.extract_and_expand(protocol.hash, chaining_key, input_key_material, nil, dst) + + h_len := hash_len(protocol) + assert(len(dst) == h_len * 2 || len(dst) == h_len * 3) + + r1, r2 := dst[:h_len], dst[h_len:h_len*2] + if len(dst) == h_len * 2 { + return r1, r2, nil + } + return r1, r2, dst[h_len*2:] +} + +// Sets k = key. Sets n = 0. +cipherstate_initialize_key :: proc(self: ^Cipher_State, key: []byte, protocol: ^Protocol) { + k_len := len(key) + switch { + case k_len == 0: + // k = empty + aead.reset(&self.ctx) + self.n = 0 + case k_len < AEAD_KEY_SIZE: + panic("crypto/noise: invalid AEAD key size") + case: + aead.init(&self.ctx, protocol.cipher, key[:AEAD_KEY_SIZE]) + self.n = 0 + } +} + +// Returns true if k is non-empty, false otherwise. +@(require_results) +cipherstate_has_key :: proc(self: ^Cipher_State) -> bool { + return aead.algorithm(&self.ctx) != .Invalid +} + +// If k is non-empty returns ENCRYPT(k, n++, ad, plaintext). Otherwise +// returns plaintext. +@(require_results) +cipherstate_encrypt_with_ad :: proc(self: ^Cipher_State, ad, plaintext, dst: []byte) -> ([]byte, Status) { + if self.is_invalid { + return nil, .Invalid_Cipher_State + } + if self.n_exhausted { + return nil, .IV_Exhausted + } + + pt_len := len(plaintext) + if pt_len > MAX_PACKET_SIZE - 16 { + return nil, .Max_Packet_Size + } + + if cipherstate_has_key(self) { + if len(dst) != pt_len + TAG_SIZE { + return nil, .Invalid_Destination_Buffer + } + _encrypt(&self.ctx, self.n, ad, plaintext, dst) + self.n += 1 + if self.n == 0 { + self.n_exhausted = true + } + } else { + if len(dst) != pt_len { + return nil, .Invalid_Destination_Buffer + } + if raw_data(dst) != raw_data(plaintext) { + copy(dst, plaintext) + } + } + + return dst, .Ok +} + +// If k is non-empty returns DECRYPT(k, n++, ad, ciphertext). Otherwise +// returns ciphertext. If an authentication failure occurs in DECRYPT() +// then n is not incremented and an error is signaled to the caller. +@(require_results) +cipherstate_decrypt_with_ad :: proc(self: ^Cipher_State, ad, ciphertext, dst: []byte) -> ([]byte, Status) { + if self.is_invalid { + return nil, .Invalid_Cipher_State + } + if self.n_exhausted { + return nil, .IV_Exhausted + } + + if cipherstate_has_key(self) { + if status := _decrypt(&self.ctx, self.n, ad, ciphertext, dst); status != .Ok { + return nil, status + } + self.n += 1 + if self.n == 0 { + self.n_exhausted = true + } + } else { + if len(dst) != len(ciphertext) { + return nil, .Invalid_Destination_Buffer + } + if raw_data(dst) != raw_data(ciphertext) { + copy(dst, ciphertext) + } + } + + return dst, .Ok +} + +// Sets k = REKEY(k). +cipherstate_rekey :: proc(self: ^Cipher_State) { + if cipherstate_has_key(self) { + algorithm := aead.algorithm(&self.ctx) + + // The "sensible" way to implement this is to inlike REKEY(k), + // so we do. + // + // Returns a new 32-byte cipher key as a pseudorandom function + // of k. If this function is not specifically defined for some + // set of cipher functions, then it defaults to returning the + // first 32 bytes from `ENCRYPT(k, maxnonce, zerolen, zeros)`, + // where maxnonce equals (2^64)-1, zerolen is a zero-length + // byte sequence, and zeros is a sequence of 32 bytes filled + // with zeros. + + zeroes: [AEAD_KEY_SIZE + TAG_SIZE]byte + defer crypto.zero_explicit(&zeroes, size_of(zeroes)) + + // 1 2 3 4 5 6 7 8 + n: u64 = 0xFF_FF_FF_FF_FF_FF_FF_FF + _encrypt(&self.ctx, n, nil, zeroes[:AEAD_KEY_SIZE], zeroes[:]) + aead.init(&self.ctx, algorithm, zeroes[:AEAD_KEY_SIZE]) + } +} + +cipherstate_reset :: proc(self: ^Cipher_State) { + aead.reset(&self.ctx) + crypto.zero_explicit(self, size_of(Cipher_State)) +} + +// Takes an arbitrary-length protocol_name byte sequence (see Section 8). +// Executes the following steps: +// - If protocol_name is less than or equal to HASHLEN bytes in length, +// sets h equal to protocol_name with zero bytes appended to make +// HASHLEN bytes. +// - Otherwise sets h = HASH(protocol_name). +// - Sets ck = h. +// - Calls InitializeKey(empty). +@(require_results) +symmetricstate_initialize :: proc(ss: ^Symmetric_State, protocol_name: string) -> Status { + if status := protocol_from_string(&ss.protocol, protocol_name); status != .Ok { + return status + } + + cipherstate_initialize_key(&ss.cipher_state, nil, &ss.protocol) + + h_len := hash_len(&ss.protocol) + h := ss._h[:h_len] + if len(protocol_name) <= h_len { + copy(h, protocol_name) + } else { + _hash(h, &ss.protocol, transmute([]byte)protocol_name) + } + + copy(ss._ck[:h_len], h) + + return .Ok +} + +// Sets h = HASH(h || data). +symmetricstate_mix_hash :: proc(self: ^Symmetric_State, data: ..[]byte) { + h := self._h[:hash_len(&self.protocol)] + if len(data) == 1 { + _hash(h, &self.protocol, h, data[0]) + } else if len(data) == 2 { + _hash(h, &self.protocol, h, data[0], data[1]) + } else if len(data) == 3 { + _hash(h, &self.protocol, h, data[0], data[1], data[2]) + } else { + panic("crypto/noise: invalid MixHash inputs") + } +} + +// Executes the following steps: +// - Sets ck, temp_k = HKDF(ck, input_key_material, 2). +// - If HASHLEN is 64, then truncates temp_k to 32 bytes. +// - Calls InitializeKey(temp_k). +symmetricstate_mix_key :: proc(self: ^Symmetric_State, input_key_material: []byte) { + h_len := hash_len(&self.protocol) + + dst_len := h_len * 2 + dst: [2*MAX_HASH_SIZE]byte = --- + defer crypto.zero_explicit(&dst, dst_len) + + ck, temp_k, _ := _hkdf(dst[:dst_len], self._ck[:h_len], input_key_material, &self.protocol) + copy(self._ck[:], ck) + cipherstate_initialize_key(&self.cipher_state, temp_k, &self.protocol) +} + +// This function is used for handling pre-shared symmetric keys, as described +// in Section 9. It executes the following steps: +// - Sets ck, temp_h, temp_k = HKDF(ck, input_key_material, 3). +// - Calls MixHash(temp_h). +// - If HASHLEN is 64, then truncates temp_k to 32 bytes. +// - Calls InitializeKey(temp_k). +symmetricstate_mix_key_and_hash :: proc(self: ^Symmetric_State, input_key_material: []byte) { + h_len := hash_len(&self.protocol) + + dst_len := h_len * 3 + dst: [3*MAX_HASH_SIZE]byte = --- + defer crypto.zero_explicit(&dst, dst_len) + + ck, temp_h, temp_k := _hkdf(dst[:dst_len], self._ck[:h_len], input_key_material, &self.protocol) + copy(self._ck[:], ck) + symmetricstate_mix_hash(self, temp_h) + cipherstate_initialize_key(&self.cipher_state, temp_k, &self.protocol) +} + +// Returns h. This function should only be called at the end of a handshake, +// i.e. after the Split() function has been called. +// +// This function is used for channel binding, as described in Section 11.2 +@(require_results) +symmetricstate_get_handshake_hash :: proc(self: ^Symmetric_State) -> []byte { + return self._h[:hash_len(&self.protocol)] +} + +// Sets ciphertext = EncryptWithAd(h, plaintext), calls MixHash(ciphertext), +// and returns ciphertext. +// +// Note that if k is empty, the EncryptWithAd() call will set ciphertext +// equal to plaintext. +@(require_results) +symmetricstate_encrypt_and_hash :: proc(self: ^Symmetric_State, plaintext, dst: []byte) -> ([]byte, Status) { + ciphertext, status := cipherstate_encrypt_with_ad(&self.cipher_state, self._h[:hash_len(&self.protocol)], plaintext, dst) + if status != .Ok { + return nil, status + } + symmetricstate_mix_hash(self, ciphertext) + return ciphertext, status +} + +// Sets plaintext = DecryptWithAd(h, ciphertext), calls MixHash(ciphertext), +// and returns plaintext. +// +// Note that if k is empty, the DecryptWithAd() call will set plaintext +// equal to ciphertext. +@(require_results) +symmetricstate_decrypt_and_hash :: proc(self: ^Symmetric_State, ciphertext, dst: []byte) -> ([]byte, Status) { + h_len := hash_len(&self.protocol) + + h: [MAX_HASH_SIZE]byte = --- + copy(h[:], self._h[:h_len]) + defer crypto.zero_explicit(&h, size_of(h)) + + // We reverse the order to save having to copy the ciphertext, in + // the case that ciphertext and dst alias. + symmetricstate_mix_hash(self, ciphertext) + return cipherstate_decrypt_with_ad(&self.cipher_state, h[:h_len], ciphertext, dst) +} + +// Returns a pair of CipherState objects for encrypting transport messages. +// Executes the following steps, where zerolen is a zero-length byte sequence: +// - Sets temp_k1, temp_k2 = HKDF(ck, zerolen, 2). +// - If HASHLEN is 64, then truncates temp_k1 and temp_k2 to 32 bytes. +// - Creates two new CipherState objects c1 and c2. +// - Calls c1.InitializeKey(temp_k1) and c2.InitializeKey(temp_k2). +// - Returns the pair (c1, c2). +symmetricstate_split :: proc(self: ^Symmetric_State, cipher_states: ^Cipher_States) { + h_len := hash_len(&self.protocol) + + dst_len := h_len * 2 + dst: [2*MAX_HASH_SIZE]byte = --- + defer crypto.zero_explicit(&dst, dst_len) + + temp_k1, temp_k2, _ := _hkdf(dst[:dst_len], self._ck[:h_len], nil, &self.protocol) + cipherstate_initialize_key(&cipher_states.c1_i_to_r, temp_k1, &self.protocol) + cipherstate_initialize_key(&cipher_states.c2_r_to_i, temp_k2, &self.protocol) +} + +symmetricstate_reset :: proc(self: ^Symmetric_State) { + cipherstate_reset(&self.cipher_state) + + crypto.zero_explicit(self, size_of(Symmetric_State)) +} + +// Takes a valid handshake_pattern (see Section 7) and an initiator boolean +// specifying this party's role as either initiator or responder. +// Takes a prologue byte sequence which may be zero-length, or which may +// contain context information that both parties want to confirm is identical +// (see Section 6). +// +// Takes a set of DH key pairs (s, e) and public keys (rs, re) for +// initializing local variables, any of which may be empty. Public keys +// are only passed in if the handshake_pattern uses pre-messages +// (see Section 7). The ephemeral values (e, re) are typically left empty, +// since they are created and exchanged during the handshake; but there +// are exceptions (see Section 10). +// +// Performs the following steps: +// - Derives a protocol_name byte sequence by combining the names for +// the handshake pattern and crypto functions, as specified in Section 8. +// - Calls InitializeSymmetric(protocol_name). +// - Calls MixHash(prologue). +// - Sets the initiator, s, e, rs, and re variables to the corresponding +// arguments. +// - Calls MixHash() once for each public key listed in the pre-messages +// from handshake_pattern, with the specified public key as input +// (see Section 7 for an explanation of pre-messages). +// - If both initiator and responder have pre-messages, the initiator's +// public keys are hashed first. +// - If multiple public keys are listed in either party's pre-message, +// the public keys are hashed in the order that they are listed. +// - Sets message_pattern to the message patterns from handshake_pattern. +@(require_results) +handshakestate_initialize :: proc( + handshake_state: ^Handshake_State, + initiator: bool, + prologue: []byte, + s: ^ecdh.Private_Key, + e: ^ecdh.Private_Key, // Only set for testing. + rs: ^ecdh.Public_Key, + re: ^ecdh.Public_Key, // Only set for testing. + protocol_name: string, + psk: []byte = nil, +) -> Status { + crypto.zero_explicit(handshake_state, size_of(Handshake_State)) + + symmetric_state := &handshake_state.symmetric_state + status: Status + do_init: { + if status = symmetricstate_initialize(symmetric_state, protocol_name); status != .Ok { + break do_init + } + + curve := symmetric_state.protocol.dh + if s != nil && ecdh.curve(s) != curve { + status = .Invalid_DH_Key + break do_init + } + if e != nil && ecdh.curve(e) != curve { + status = .Invalid_DH_Key + break do_init + } + if rs != nil && ecdh.curve(rs) != curve { + status = .Invalid_DH_Key + break do_init + } + if re != nil && ecdh.curve(re) != curve { + status = .Invalid_DH_Key + break do_init + } + + // Check if we will require s later down the line. + s_pre, s_hs: bool + if initiator { + s_pre, s_hs = pattern_requires_initiator_s(symmetric_state.protocol.handshake_pattern) + } else { + s_pre, s_hs = pattern_requires_responder_s(symmetric_state.protocol.handshake_pattern) + } + if (s_pre || s_hs) && s == nil { + status = .No_Self_Identity + break do_init + } + + message_pattern := HANDSHAKE_PATTERNS[symmetric_state.protocol.handshake_pattern] + if message_pattern.pre_messages != nil { + if initiator { + if slice.contains(message_pattern.pre_messages, Pre_Token.res_s) { + if rs == nil { + status = .No_Peer_Identity + break do_init + } + } + } else { + if slice.contains(message_pattern.pre_messages, Pre_Token.ini_s) { + if rs == nil { + status = .No_Peer_Identity + break do_init + } + } + } + } else { + if rs != nil { + status = .Unexpected_Peer_Identity + break do_init + } + } + + symmetricstate_mix_hash(symmetric_state, prologue) + + // In all supported patterns, `ini_s` will always precede `res_s`. + if message_pattern.pre_messages != nil { + tmp: [MAX_DH_SIZE]byte = --- + d_len := dh_len(&symmetric_state.protocol) + dst := tmp[:d_len] + + if initiator { + if slice.contains(message_pattern.pre_messages, Pre_Token.ini_s) { + ecdh.public_key_bytes(&s._pub_key, dst) + symmetricstate_mix_hash(symmetric_state, dst) + } + if slice.contains(message_pattern.pre_messages, Pre_Token.res_s) { + ecdh.public_key_bytes(rs, dst) + symmetricstate_mix_hash(symmetric_state, dst) + } + } else { + if slice.contains(message_pattern.pre_messages, Pre_Token.ini_s) { + ecdh.public_key_bytes(rs, dst) + symmetricstate_mix_hash(symmetric_state, dst) + } + if slice.contains(message_pattern.pre_messages, Pre_Token.res_s) { + ecdh.public_key_bytes(&s._pub_key, dst) + symmetricstate_mix_hash(symmetric_state, dst) + } + } + } + if message_pattern.is_psk { + if len(psk) != PSK_SIZE { + status = .Invalid_Pre_Shared_Key + break do_init + } + } else if len(psk) != 0 { + status = .Unexpected_Pre_Shared_Key + break do_init + } + } + if status != .Ok { + symmetricstate_reset(symmetric_state) + return status + } + + if s != nil { + ecdh.private_key_set(&handshake_state.s, s) + } + if e != nil { + ecdh.private_key_set(&handshake_state.e, e) + handshake_state.pre_set_e = true + } + if rs != nil { + ecdh.public_key_set(&handshake_state.rs, rs) + } + if re != nil { + ecdh.public_key_set(&handshake_state.re, re) + } + copy(handshake_state.psk[:], psk) + handshake_state.message_pattern = HANDSHAKE_PATTERNS[symmetric_state.protocol.handshake_pattern] + handshake_state.current_message = 0 + handshake_state.status = .Handshake_Pending + handshake_state.initiator = initiator + + return .Ok +} + +handshakestate_reset :: proc(self: ^Handshake_State) { + symmetricstate_reset(&self.symmetric_state) + ecdh.private_key_clear(&self.s) + ecdh.private_key_clear(&self.e) + + crypto.zero_explicit(self, size_of(Handshake_State)) +} + +// Takes a payload byte sequence which may be zero-length, and a +// message_buffer to write the output into. +// Performs the following steps, aborting if any EncryptAndHash() call +// returns an error: +// - Fetches and deletes the next message pattern from message_pattern, +// then sequentially processes each token from the message pattern: +// - For "e": Sets e (which must be empty) to GENERATE_KEYPAIR(). +// Appends e.public_key to the buffer. Calls MixHash(e.public_key). +// - For "s": Appends EncryptAndHash(s.public_key) to the buffer. +// - For "ee": Calls MixKey(DH(e, re)). +// - For "es": Calls MixKey(DH(e, rs)) if initiator, MixKey(DH(s, re)) +// if responder. +// - For "se": Calls MixKey(DH(s, re)) if initiator, MixKey(DH(e, rs)) +// if responder. +// - For "ss": Calls MixKey(DH(s, rs)). +// - Appends EncryptAndHash(payload) to the buffer. +// – (SKIPPED) If there are no more message patterns returns two new +// CipherState objects by calling Split(). +// +// Calling Split() is left to a separate function, although it is technically +// part of the specification. +@(require_results) +handshakestate_write_message :: proc(self: ^Handshake_State, payload, dst: []byte, allocator := context.allocator) -> ([]byte, Status) { + ensure(self.status == .Handshake_Pending, "crypto/noise: invalid state for WriteMessage") + + protocol := &self.symmetric_state.protocol + d_len := dh_len(protocol) + + pattern_buf: [dynamic; MAX_STEP_MSG_SIZE]byte + dh_buf: [MAX_DH_SIZE]byte = --- + defer crypto.zero_explicit(&dh_buf, size_of(dh_buf)) + + pattern := self.message_pattern.messages[self.current_message] + for token in pattern { + switch token { + case .e: + switch self.pre_set_e { + case true: + // Note: "which must be empty", but we allow pre-generated `e` + // for testing/rng-less systems. + self.pre_set_e = false + case false: + if ecdh.curve(&self.e) != .Invalid { + panic("crypto/noise: e was not empty when processing token 'e' during WriteMessage") + } + generate_keypair(protocol, &self.e) + } + e_public := dh_buf[:d_len] + ecdh.public_key_bytes(&self.e._pub_key, e_public) + n := append(&pattern_buf, ..e_public) + ensure(n == d_len, "crypto/noise: truncated append `e`") + + symmetricstate_mix_hash(&self.symmetric_state, e_public) + if self.message_pattern.is_psk { + symmetricstate_mix_key(&self.symmetric_state, e_public) + } + + case .s: + s_public := dh_buf[:d_len] + ecdh.public_key_bytes(&self.s._pub_key, s_public) + + tmp: [MAX_DH_SIZE+TAG_SIZE]byte = --- + dh_buf := tmp[:d_len+TAG_SIZE] + if !cipherstate_has_key(&self.symmetric_state.cipher_state) { + dh_buf = tmp[:d_len] + } + ct, status := symmetricstate_encrypt_and_hash(&self.symmetric_state, s_public, dh_buf) + if status != .Ok { + self.status = .Handshake_Failed + return nil, status + } + n := append(&pattern_buf, ..ct) + ensure(n == len(ct), "crypto/noise: truncated append `s`") + + case .ee: + dh := dh_buf[:d_len] + if status := _dh(&self.e, &self.re, dh); status != .Ok { + self.status = .Handshake_Failed + return nil, status + } + symmetricstate_mix_key(&self.symmetric_state, dh) + + case .es: + dh := dh_buf[:d_len] + if self.initiator { + if status := _dh(&self.e, &self.rs, dh); status != .Ok { + self.status = .Handshake_Failed + return nil, status + } + symmetricstate_mix_key(&self.symmetric_state, dh) + } else { + if status := _dh(&self.s, &self.re, dh); status != .Ok { + self.status = .Handshake_Failed + return nil, status + } + symmetricstate_mix_key(&self.symmetric_state, dh) + } + + case .se: + dh := dh_buf[:d_len] + if self.initiator { + if status := _dh(&self.s, &self.re, dh); status != .Ok { + self.status = .Handshake_Failed + return nil, status + } + symmetricstate_mix_key(&self.symmetric_state, dh) + } else { + if status := _dh(&self.e, &self.rs, dh); status != .Ok { + self.status = .Handshake_Failed + return nil, status + } + symmetricstate_mix_key(&self.symmetric_state, dh) + } + + case .ss: + dh := dh_buf[:d_len] + if status := _dh(&self.s, &self.rs, dh); status != .Ok { + self.status = .Handshake_Failed + return nil, status + } + symmetricstate_mix_key(&self.symmetric_state, dh) + + case .psk: + symmetricstate_mix_key_and_hash(&self.symmetric_state, self.psk[:]) + } + } + self.current_message += 1 // Advance after the current message is successful. + + pattern_len := len(pattern_buf) + payload_len := len(payload) + msg_len := pattern_len + payload_len + if cipherstate_has_key(&self.symmetric_state.cipher_state) { + msg_len += TAG_SIZE + } + + msg: []byte + if msg_len != 0 { + did_alloc: bool + if dst != nil { + if len(dst) < msg_len { + self.status = .Handshake_Failed + return nil, .Out_Of_Memory + } + msg = dst[:msg_len] + } else { + err: runtime.Allocator_Error + msg, err = make([]byte, msg_len, allocator) + if err != nil { + self.status = .Handshake_Failed + return nil, .Out_Of_Memory + } + did_alloc = true + } + + copy(msg, pattern_buf[:]) + ciphertext := msg[pattern_len:] + if _, status := symmetricstate_encrypt_and_hash(&self.symmetric_state, payload, ciphertext); status != .Ok { + if did_alloc { + delete(msg) + } + self.status = .Handshake_Failed + return nil, status + } + } + + if self.current_message == len(self.message_pattern.messages) { + self.current_message = -1 + self.status = .Handshake_Complete + } + + return msg, self.status +} + +// Takes a byte sequence containing a Noise handshake message, and a +// payload_buffer to write the message's plaintext payload into. +// Performs the following steps, aborting if any DecryptAndHash() +// call returns an error: +// - Fetches and deletes the next message pattern from message_pattern, +// then sequentially processes each token from the message pattern: +// - For "e": Sets re (which must be empty) to the next DHLEN bytes +// from the message. Calls MixHash(re.public_key). +// - For "s": Sets temp to the next DHLEN + 16 bytes of the message +// if HasKey() == True, or to the next DHLEN bytes otherwise. +// Sets rs (which must be empty) to DecryptAndHash(temp). +// - For "ee": Calls MixKey(DH(e, re)). +// - For "es": Calls MixKey(DH(e, rs)) if initiator, MixKey(DH(s, re)) +// if responder. +// - For "se": Calls MixKey(DH(s, re)) if initiator, MixKey(DH(e, rs)) +// if responder. +// -For "ss": Calls MixKey(DH(s, rs)). +// - Calls DecryptAndHash() on the remaining bytes of the message and stores +// the output into payload_buffer. +// – (SKIPPED) If there are no more message patterns returns two new +// CipherState objects by calling Split(). +// +// Calling Split() is left to a separate function, although it is technically +// part of the specification. +@(require_results) +handshakestate_read_message :: proc(self: ^Handshake_State, message, dst: []byte, allocator := context.allocator) -> ([]byte, Status) { + ensure(self.status == .Handshake_Pending, "crypto/noise: invalid state for ReadMessage") + + protocol := &self.symmetric_state.protocol + d_len := dh_len(&self.symmetric_state.protocol) + + dh_buf: [MAX_DH_SIZE]byte = --- + defer crypto.zero_explicit(&dh_buf, size_of(dh_buf)) + + msg := message + + pattern := self.message_pattern.messages[self.current_message] + for token in pattern { + switch token { + case .e: + if len(msg) < d_len { + return nil, .Invalid_Handshake_Message + } + re := msg[:d_len] + + if ecdh.curve(&self.re) != .Invalid { + panic("crypto/noise: re was not empty when processing token 'e' during ReadMessage") + } + + ecdh.public_key_set_bytes(&self.re, protocol.dh, re) + symmetricstate_mix_hash(&self.symmetric_state, re) + if self.message_pattern.is_psk { + symmetricstate_mix_key(&self.symmetric_state, re) + } + msg = msg[d_len:] + + case .s: + rs_len := d_len + if cipherstate_has_key(&self.symmetric_state.cipher_state) { + rs_len += TAG_SIZE + } + if len(msg) < rs_len { + self.status = .Handshake_Failed + return nil, .Invalid_Handshake_Message + } + + rs := dh_buf[:d_len] + if _, status := symmetricstate_decrypt_and_hash(&self.symmetric_state, msg[:rs_len], rs); status != .Ok { + self.status = .Handshake_Failed + return nil, status + } + + if ecdh.curve(&self.rs) != .Invalid { + panic("crypto/noise: rs was not empty when processing token 's' during ReadMessage") + } + + ecdh.public_key_set_bytes(&self.rs, protocol.dh, rs) + msg = msg[rs_len:] + + case .ee: + dh := dh_buf[:d_len] + if status := _dh(&self.e, &self.re, dh); status != .Ok { + self.status = .Handshake_Failed + return nil, status + } + symmetricstate_mix_key(&self.symmetric_state, dh) + + case .es: + dh := dh_buf[:d_len] + if self.initiator { + if status := _dh(&self.e, &self.rs, dh); status != .Ok { + self.status = .Handshake_Failed + return nil, status + } + symmetricstate_mix_key(&self.symmetric_state, dh) + } else { + if status := _dh(&self.s, &self.re, dh); status != .Ok { + self.status = .Handshake_Failed + return nil, status + } + symmetricstate_mix_key(&self.symmetric_state, dh) + } + + case .se: + dh := dh_buf[:d_len] + if self.initiator { + if status := _dh(&self.s, &self.re, dh); status != .Ok { + self.status = .Handshake_Failed + return nil, status + } + symmetricstate_mix_key(&self.symmetric_state, dh) + } else { + if status := _dh(&self.e, &self.rs, dh); status != .Ok { + self.status = .Handshake_Failed + return nil, status + } + symmetricstate_mix_key(&self.symmetric_state, dh) + } + + case .ss: + dh := dh_buf[:d_len] + if status := _dh(&self.s, &self.rs, dh); status != .Ok { + self.status = .Handshake_Failed + return nil, status + } + symmetricstate_mix_key(&self.symmetric_state, dh) + + case .psk: + symmetricstate_mix_key_and_hash(&self.symmetric_state, self.psk[:]) + } + } + self.current_message += 1 // Advance after the current message is successful. + + payload: []byte + payload_len := len(msg) + if cipherstate_has_key(&self.symmetric_state.cipher_state) { + if payload_len < TAG_SIZE { + self.status = .Handshake_Failed + return nil, self.status + } + payload_len -= TAG_SIZE + } + + did_alloc: bool + if dst != nil { + if len(dst) < payload_len { + self.status = .Handshake_Failed + return nil, .Out_Of_Memory + } + payload = dst[:payload_len] + } else if payload_len > 0 { + err: runtime.Allocator_Error + payload, err = make([]byte, payload_len, allocator) + if err != nil { + self.status = .Handshake_Failed + return nil, .Out_Of_Memory + } + did_alloc = true + } + + if _, status := symmetricstate_decrypt_and_hash(&self.symmetric_state, msg, payload); status != .Ok { + if did_alloc { + delete(payload) + } + self.status = .Handshake_Failed + return nil, self.status + } + + if self.current_message == len(self.message_pattern.messages) { + self.current_message = -1 + self.status = .Handshake_Complete + } + + return payload, self.status +} + +@(require_results) +protocol_from_string :: proc(self: ^Protocol, protocol_name: string) -> Status { + self^ = Protocol{} + + pattern, dh, cipher, hash, status := split_protocol_string(protocol_name) + if status != .Ok { + return status + } + + self.handshake_pattern = pattern + self.dh = dh + self.cipher = cipher + self.hash = hash + + return .Ok +} diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 4ecb19c21..3118e7e5e 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -3132,12 +3132,14 @@ fmt_map :: proc(fi: ^Info, v: any, info: runtime.Type_Info_Map, verb: rune) { value := runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index) fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, verb) - if hash { - io.write_string(fi.writer, " = ", &fi.n) - } else { - io.write_string(fi.writer, "=", &fi.n) + if info.value.size > 0 { + if hash { + io.write_string(fi.writer, " = ", &fi.n) + } else { + io.write_string(fi.writer, "=", &fi.n) + } + fmt_arg(fi, any{rawptr(value), info.value.id}, verb) } - fmt_arg(fi, any{rawptr(value), info.value.id}, verb) if do_trailing_comma { io.write_string(fi.writer, ",\n", &fi.n) } } diff --git a/core/fmt/fmt_os.odin b/core/fmt/fmt_os.odin index 0305b5bac..0fd97f143 100644 --- a/core/fmt/fmt_os.odin +++ b/core/fmt/fmt_os.odin @@ -14,7 +14,6 @@ import "core:bufio" fprint :: proc(f: ^os.File, args: ..any, sep := " ", flush := true) -> int { buf: [1024]byte b: bufio.Writer - defer bufio.writer_flush(&b) bufio.writer_init_with_buf(&b, os.to_stream(f), buf[:]) w := bufio.writer_to_writer(&b) @@ -25,7 +24,6 @@ fprint :: proc(f: ^os.File, args: ..any, sep := " ", flush := true) -> int { fprintln :: proc(f: ^os.File, args: ..any, sep := " ", flush := true) -> int { buf: [1024]byte b: bufio.Writer - defer bufio.writer_flush(&b) bufio.writer_init_with_buf(&b, os.to_stream(f), buf[:]) @@ -36,7 +34,6 @@ fprintln :: proc(f: ^os.File, args: ..any, sep := " ", flush := true) -> int { fprintf :: proc(f: ^os.File, fmt: string, args: ..any, flush := true, newline := false) -> int { buf: [1024]byte b: bufio.Writer - defer bufio.writer_flush(&b) bufio.writer_init_with_buf(&b, os.to_stream(f), buf[:]) @@ -50,7 +47,6 @@ fprintfln :: proc(f: ^os.File, fmt: string, args: ..any, flush := true) -> int { fprint_type :: proc(f: ^os.File, info: ^runtime.Type_Info, flush := true) -> (n: int, err: io.Error) { buf: [1024]byte b: bufio.Writer - defer bufio.writer_flush(&b) bufio.writer_init_with_buf(&b, os.to_stream(f), buf[:]) @@ -60,7 +56,6 @@ fprint_type :: proc(f: ^os.File, info: ^runtime.Type_Info, flush := true) -> (n: fprint_typeid :: proc(f: ^os.File, id: typeid, flush := true) -> (n: int, err: io.Error) { buf: [1024]byte b: bufio.Writer - defer bufio.writer_flush(&b) bufio.writer_init_with_buf(&b, os.to_stream(f), buf[:]) diff --git a/core/math/linalg/extended.odin b/core/math/linalg/extended.odin index 22c37dd0e..0470054c3 100644 --- a/core/math/linalg/extended.odin +++ b/core/math/linalg/extended.odin @@ -1,6 +1,7 @@ package linalg import "base:builtin" +import "base:intrinsics" import "core:math" @(require_results) @@ -413,26 +414,12 @@ pow :: proc "contextless" (x, e: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) { @(require_results) ceil :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) { - when IS_ARRAY(T) { - for i in 0.. (out: T) where IS_FLOAT(ELEM_TYPE(T)) { - when IS_ARRAY(T) { - for i in 0.. (out: T) where IS_FLOAT(ELEM_TYPE(T)) { return } +@(require_results) +trunc :: proc "contextless" (x: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) { + return _from_simd4(T, intrinsics.simd_trunc(_to_simd4(x))) +} + @(require_results) fract :: proc "contextless" (x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) { f := #force_inline floor(x) @@ -613,3 +605,46 @@ not :: proc "contextless" (x: $A/[$N]bool) -> (out: A) { } return } + + +@(require_results) +_to_simd4 :: #force_inline proc "contextless" (a: $T) -> (out: #simd[4]ELEM_TYPE(T)) where IS_NUMERIC(ELEM_TYPE(T)) #no_bounds_check { + when IS_ARRAY(T) { + when len(T) == 1 { + _a: [4]ELEM_TYPE(T) + _a.x = a.x + return transmute(#simd[4]ELEM_TYPE(T))_a + } else when len(T) == 2 { + _a: [4]ELEM_TYPE(T) + _a.xy = a + return transmute(#simd[4]ELEM_TYPE(T))_a + } else when len(T) == 3 { + _a: [4]ELEM_TYPE(T) + _a.xyz = a + return transmute(#simd[4]ELEM_TYPE(T))_a + } else { + return transmute(#simd[4]ELEM_TYPE(T))a + } + } else { + _a: [4]ELEM_TYPE(T) + _a.x = a + return transmute(#simd[4]ELEM_TYPE(T))_a + } +} + +@(require_results) +_from_simd4 :: #force_inline proc "contextless" ($T: typeid, a: $V/#simd[4]$E) -> T where IS_NUMERIC(ELEM_TYPE(T)) #no_bounds_check { + when IS_ARRAY(T) { + when len(T) == 1 { + return (transmute([4]ELEM_TYPE(T))a).x + } else when len(T) == 2 { + return (transmute([4]ELEM_TYPE(T))a).xy + } else when len(T) == 3 { + return (transmute([4]ELEM_TYPE(T))a).xyz + } else { + return transmute([4]ELEM_TYPE(T))a + } + } else { + return (transmute([4]ELEM_TYPE(T))a).x + } +} diff --git a/core/math/linalg/general.odin b/core/math/linalg/general.odin index ea3a4e84a..956fdb919 100644 --- a/core/math/linalg/general.odin +++ b/core/math/linalg/general.odin @@ -46,11 +46,23 @@ scalar_dot :: proc "contextless" (a, b: $T) -> T where IS_FLOAT(T), !IS_ARRAY(T) @(require_results) vector_dot :: proc "contextless" (a, b: $T/[$N]$E) -> (c: E) where IS_NUMERIC(E) #no_bounds_check { - for i in 0.. (c: f16) { return a.w*b.w + a.x*b.x + a.y*b.y + a.z*b.z @@ -86,11 +98,8 @@ vector_cross2 :: proc "contextless" (a, b: $T/[2]$E) -> E where IS_NUMERIC(E) { } @(require_results) -vector_cross3 :: proc "contextless" (a, b: $T/[3]$E) -> (c: T) where IS_NUMERIC(E) { - c[0] = a[1]*b[2] - b[1]*a[2] - c[1] = a[2]*b[0] - b[2]*a[0] - c[2] = a[0]*b[1] - b[0]*a[1] - return +vector_cross3 :: proc "contextless" (a, b: $T/[3]$E) -> (c: T) where IS_NUMERIC(E) #no_bounds_check { + return a.yzx*b.zxy - b.yzx*a.zxy } @(require_results) @@ -130,12 +139,12 @@ normalize0 :: proc{vector_normalize0, quaternion_normalize0} @(require_results) vector_length :: proc "contextless" (v: $T/[$N]$E) -> E where IS_FLOAT(E) { - return math.sqrt(dot(v, v)) + return #force_inline math.sqrt(#force_inline dot(v, v)) } @(require_results) vector_length2 :: proc "contextless" (v: $T/[$N]$E) -> E where IS_NUMERIC(E) { - return dot(v, v) + return #force_inline dot(v, v) } @(require_results) diff --git a/core/mem/virtual/arena.odin b/core/mem/virtual/arena.odin index bcf3ee702..1ee7cba6c 100644 --- a/core/mem/virtual/arena.odin +++ b/core/mem/virtual/arena.odin @@ -141,9 +141,9 @@ arena_alloc_unguarded :: proc(arena: ^Arena, size: uint, alignment: uint, loc := needed := mem.align_forward_uint(size, alignment) needed = max(needed, arena.default_commit_size) - block_size := max(needed, arena.minimum_block_size) + alignment + block_size := max(needed, arena.minimum_block_size) - new_block := memory_block_alloc(needed, block_size) or_return + new_block := memory_block_alloc(needed, block_size, alignment) or_return new_block.prev = arena.curr_block arena.curr_block = new_block arena.total_reserved += new_block.reserved diff --git a/core/mem/virtual/doc.odin b/core/mem/virtual/doc.odin index b5f0944c7..2c7e046ea 100644 --- a/core/mem/virtual/doc.odin +++ b/core/mem/virtual/doc.odin @@ -65,8 +65,8 @@ Example: import vmem "core:mem/virtual" main :: proc() { - data, err := virtual.map_file_from_path(#file, {.Read}) - defer virtual.unmap_file(data) + data, err := vmem.map_file_from_path(#file, {.Read}) + defer vmem.unmap_file(data) fmt.printfln("Error: %v", err) fmt.printfln("Data: %s", data) } diff --git a/core/mem/virtual/virtual.odin b/core/mem/virtual/virtual.odin index d37c61267..a97e00731 100644 --- a/core/mem/virtual/virtual.odin +++ b/core/mem/virtual/virtual.odin @@ -16,8 +16,8 @@ platform_memory_init :: proc "contextless" () { Allocator_Error :: mem.Allocator_Error @(require_results, no_sanitize_address) -reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) { - return _reserve(size) +reserve :: proc "contextless" (size: uint, address_hint := uintptr(0)) -> (data: []byte, err: Allocator_Error) { + return _reserve(size, address_hint) } @(no_sanitize_address) @@ -115,7 +115,7 @@ memory_block_alloc :: proc(committed, reserved: uint, alignment: uint = 0, flags } pmblock.block.committed = committed - pmblock.block.reserved = reserved + pmblock.block.reserved = total_size - uint(base_offset) return &pmblock.block, nil diff --git a/core/mem/virtual/virtual_darwin.odin b/core/mem/virtual/virtual_darwin.odin index 0635c83d4..63f7f0771 100644 --- a/core/mem/virtual/virtual_darwin.odin +++ b/core/mem/virtual/virtual_darwin.odin @@ -2,8 +2,8 @@ package mem_virtual import "core:sys/posix" -_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) { - result := posix.mmap(nil, size, {}, {.ANONYMOUS, .PRIVATE}) +_reserve :: proc "contextless" (size: uint, address_hint: uintptr) -> (data: []byte, err: Allocator_Error) { + result := posix.mmap(rawptr(address_hint), size, {}, {.ANONYMOUS, .PRIVATE}) if result == posix.MAP_FAILED { assert_contextless(posix.errno() == .ENOMEM) return nil, .Out_Of_Memory diff --git a/core/mem/virtual/virtual_freebsd.odin b/core/mem/virtual/virtual_freebsd.odin index af0f31733..d055d6052 100644 --- a/core/mem/virtual/virtual_freebsd.odin +++ b/core/mem/virtual/virtual_freebsd.odin @@ -2,14 +2,14 @@ package mem_virtual import "core:sys/posix" -_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) { +_reserve :: proc "contextless" (size: uint, address_hint: uintptr) -> (data: []byte, err: Allocator_Error) { PROT_MAX :: proc "contextless" (flags: posix.Prot_Flags) -> posix.Prot_Flags { _PROT_MAX_SHIFT :: 16 return transmute(posix.Prot_Flags)(transmute(i32)flags << _PROT_MAX_SHIFT) } - result := posix.mmap(nil, size, PROT_MAX({.READ, .WRITE, .EXEC}), {.ANONYMOUS, .PRIVATE}) + result := posix.mmap(rawptr(address_hint), size, PROT_MAX({.READ, .WRITE, .EXEC}), {.ANONYMOUS, .PRIVATE}) if result == posix.MAP_FAILED { assert_contextless(posix.errno() == .ENOMEM) return nil, .Out_Of_Memory diff --git a/core/mem/virtual/virtual_linux.odin b/core/mem/virtual/virtual_linux.odin index 144a8dc59..92f55c4a3 100644 --- a/core/mem/virtual/virtual_linux.odin +++ b/core/mem/virtual/virtual_linux.odin @@ -4,8 +4,8 @@ package mem_virtual import "core:sys/linux" -_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) { - addr, errno := linux.mmap(0, size, {}, {.PRIVATE, .ANONYMOUS}) +_reserve :: proc "contextless" (size: uint, address_hint: uintptr) -> (data: []byte, err: Allocator_Error) { + addr, errno := linux.mmap(address_hint, size, {}, {.PRIVATE, .ANONYMOUS}) if errno == .ENOMEM { return nil, .Out_Of_Memory } else if errno == .EINVAL { @@ -68,4 +68,4 @@ _map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags) _unmap_file :: proc "contextless" (data: []byte) { _release(raw_data(data), uint(len(data))) -} \ No newline at end of file +} diff --git a/core/mem/virtual/virtual_netbsd.odin b/core/mem/virtual/virtual_netbsd.odin index 588625cc7..afd2e27f4 100644 --- a/core/mem/virtual/virtual_netbsd.odin +++ b/core/mem/virtual/virtual_netbsd.odin @@ -2,13 +2,13 @@ package mem_virtual import "core:sys/posix" -_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) { +_reserve :: proc "contextless" (size: uint, address_hint: uintptr) -> (data: []byte, err: Allocator_Error) { PROT_MPROTECT :: proc "contextless" (flags: posix.Prot_Flags) -> posix.Prot_Flags { return transmute(posix.Prot_Flags)(transmute(i32)flags << 3) } - result := posix.mmap(nil, size, PROT_MPROTECT({.READ, .WRITE, .EXEC}), {.ANONYMOUS, .PRIVATE}) + result := posix.mmap(rawptr(address_hint), size, PROT_MPROTECT({.READ, .WRITE, .EXEC}), {.ANONYMOUS, .PRIVATE}) if result == posix.MAP_FAILED { assert_contextless(posix.errno() == .ENOMEM) return nil, .Out_Of_Memory diff --git a/core/mem/virtual/virtual_openbsd.odin b/core/mem/virtual/virtual_openbsd.odin index 83f7ca9ca..41adf359c 100644 --- a/core/mem/virtual/virtual_openbsd.odin +++ b/core/mem/virtual/virtual_openbsd.odin @@ -2,8 +2,8 @@ package mem_virtual import "core:sys/posix" -_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) { - result := posix.mmap(nil, size, {}, {.ANONYMOUS, .PRIVATE}) +_reserve :: proc "contextless" (size: uint, address_hint: uintptr) -> (data: []byte, err: Allocator_Error) { + result := posix.mmap(rawptr(address_hint), size, {}, {.ANONYMOUS, .PRIVATE}) if result == posix.MAP_FAILED { assert_contextless(posix.errno() == .ENOMEM) return nil, .Out_Of_Memory diff --git a/core/mem/virtual/virtual_other.odin b/core/mem/virtual/virtual_other.odin index 8a2e1a61d..6f9c1327a 100644 --- a/core/mem/virtual/virtual_other.odin +++ b/core/mem/virtual/virtual_other.odin @@ -7,7 +7,7 @@ #+build !windows package mem_virtual -_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) { +_reserve :: proc "contextless" (size: uint, address_hint: uintptr) -> (data: []byte, err: Allocator_Error) { return nil, nil } @@ -35,4 +35,4 @@ _map_file :: proc "contextless" (f: any, size: i64, flags: Map_File_Flags) -> (d _unmap_file :: proc "contextless" (data: []byte) { -} \ No newline at end of file +} diff --git a/core/mem/virtual/virtual_windows.odin b/core/mem/virtual/virtual_windows.odin index 0866ebfa1..14fc35f62 100644 --- a/core/mem/virtual/virtual_windows.odin +++ b/core/mem/virtual/virtual_windows.odin @@ -87,8 +87,8 @@ foreign Kernel32 { } @(no_sanitize_address) -_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) { - result := VirtualAlloc(nil, size, MEM_RESERVE, PAGE_READWRITE) +_reserve :: proc "contextless" (size: uint, address_hint: uintptr) -> (data: []byte, err: Allocator_Error) { + result := VirtualAlloc(rawptr(address_hint), size, MEM_RESERVE, PAGE_READWRITE) if result == nil { err = .Out_Of_Memory return @@ -191,4 +191,4 @@ _map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags) @(no_sanitize_address) _unmap_file :: proc "contextless" (data: []byte) { UnmapViewOfFile(raw_data(data)) -} \ No newline at end of file +} diff --git a/core/nbio/impl_posix.odin b/core/nbio/impl_posix.odin index 0d3f57e9c..da72ed1fd 100644 --- a/core/nbio/impl_posix.odin +++ b/core/nbio/impl_posix.odin @@ -743,6 +743,11 @@ poll_exec :: proc(op: ^Operation) -> Op_Result { return .Done } + if .EOF in op._impl.flags { + op.poll.result = .Ready + return .Done + } + filter: kq.Filter switch op.poll.event { case .Receive: filter = .Read diff --git a/core/os/path.odin b/core/os/path.odin index 0a0c8356a..22ec2d3f7 100644 --- a/core/os/path.odin +++ b/core/os/path.odin @@ -413,10 +413,15 @@ e.g. 'name.tar.gz' -> 'name.tar' 'name.txt' -> 'name' +Returns an empty string if the path is empty Returns an empty string if there is no stem. e.g: '.gitignore'. Returns an empty string if there's a trailing path separator. */ stem :: proc(path: string) -> string { + if path == "" { + return "" + } + // If the last character is a path separator, there is no file. if is_path_separator(path[len(path) - 1]) { return "" diff --git a/core/os/stat_windows.odin b/core/os/stat_windows.odin index 51bd57d5b..35f2ae86b 100644 --- a/core/os/stat_windows.odin +++ b/core/os/stat_windows.odin @@ -377,17 +377,25 @@ _volume_name_len :: proc(path: string) -> (length: int) { // UNC path, minimum version of the volume is `\\h\s` for host, share. // Can also contain an IP address in the host position. + // (path might be purely path separators) slash_count := 0 + found_host: bool for i in prefix.. 0 { + if _is_path_separator(path[i]) { slash_count += 1 - if slash_count == 2 { + if slash_count == 2 && found_host { return i } + } else { + found_host = true + // Found a host but no trailing slash `\\h\s` + if slash_count == 1 && i == len(path)-1 { + return len(path) + } } } - return len(path) + return 0 } \ No newline at end of file diff --git a/core/path/filepath/path.odin b/core/path/filepath/path.odin index e9f22772c..09c0187bc 100644 --- a/core/path/filepath/path.odin +++ b/core/path/filepath/path.odin @@ -2,6 +2,7 @@ // To process paths such as URLs that depend on forward slashes regardless of the OS, use the slashpath package. package filepath +import "base:runtime" import "core:os" import "core:strings" @@ -32,7 +33,15 @@ Join all `elems` with the system's path separator and normalize the result. For example, `join_path({"/home", "foo", "bar.txt"})` will result in `"/home/foo/bar.txt"`. */ -join :: os.join_path +join :: proc( + elems: []string, + allocator := context.allocator, +) -> ( + joined: string, + err: runtime.Allocator_Error, +) { + return os.join_path(elems, allocator) +} /* Returns leading volume name. @@ -136,7 +145,15 @@ long_ext :: os.long_ext If the result of the path is an empty string, the returned path with be `"."`. */ -clean :: os.clean_path +clean :: proc( + path: string, + allocator := context.allocator, +) -> ( + cleaned: string, + err: runtime.Allocator_Error, +) { + return os.clean_path(path, allocator) +} /* Returns the result of replacing each path separator character in the path @@ -144,7 +161,16 @@ with the specific character `new_sep`. *Allocates Using Provided Allocator* */ -replace_path_separators := os.replace_path_separators +replace_separators :: proc( + path: string, + new_sep: rune, + allocator := context.allocator, +) -> ( + new_path: string, + err: os.Error, +) { + return os.replace_path_separators(path, new_sep, allocator) +} /* Return true if `path` is an absolute path as opposed to a relative one. @@ -156,7 +182,16 @@ Get the absolute path to `path` with respect to the process's current directory. *Allocates Using Provided Allocator* */ -abs :: os.get_absolute_path +@(require_results) +abs :: proc( + path: string, + allocator := context.allocator, +) -> ( + absolute_path: string, + error: os.Error, +) { + return os.get_absolute_path(path, allocator) +} Relative_Error :: enum { None, @@ -249,17 +284,7 @@ rel :: proc(base_path, target_path: string, allocator := context.allocator) -> ( `dir` calls `clean` on the path and trailing separators are removed. If the path is empty or consists purely of separators, then `"."` is returned. */ -dir :: proc(path: string, allocator := context.allocator) -> string { - i := len(path) > 0 ? len(path) - 1 : 0 - for i > 0 && !is_separator(path[i]) { - i -= 1 - } - res, dir_err := clean(path[:i], allocator) - - if dir_err != nil { return "" } - return res -} - +dir :: os.dir // Splits the PATH-like `path` string, returning an array of its separated components (delete after use). diff --git a/core/simd/x86/avx.odin b/core/simd/x86/avx.odin new file mode 100644 index 000000000..5b0383526 --- /dev/null +++ b/core/simd/x86/avx.odin @@ -0,0 +1,1854 @@ +#+build i386, amd64 +package simd_x86 + +import "base:intrinsics" + +// Adds packed double-precision (64-bit) floating-point elements in `a` and `b`. +@(require_results, enable_target_feature="avx") +_mm256_add_pd :: #force_inline proc "c" (a, b: __m256d) -> __m256d { + return intrinsics.simd_add(a, b) +} + +// Adds packed single-precision (32-bit) floating-point elements in `a` and `b`. +@(require_results, enable_target_feature="avx") +_mm256_add_ps :: #force_inline proc "c" (a, b: __m256) -> __m256 { + return intrinsics.simd_add(a, b) +} + +// Computes the bitwise AND of a packed double-precision (64-bit) floating-point elements in `a` and `b`. +@(require_results, enable_target_feature="avx") +_mm256_and_pd :: #force_inline proc "c" (a, b: __m256d) -> __m256d { + a := transmute(#simd[4]u64)a + b := transmute(#simd[4]u64)b + return transmute(__m256d)intrinsics.simd_bit_and(a, b) +} + +// Computes the bitwise AND of packed single-precision (32-bit) floating-point elements in `a` and `b`. +@(require_results, enable_target_feature="avx") +_mm256_and_ps :: #force_inline proc "c" (a, b: __m256) -> __m256 { + a := transmute(#simd[8]u32)a + b := transmute(#simd[8]u32)b + return transmute(__m256)intrinsics.simd_bit_and(a, b) +} + +// Computes the bitwise OR packed double-precision (64-bit) floating-point elements in `a` and `b`. +@(require_results, enable_target_feature="avx") +_mm256_or_pd :: #force_inline proc "c" (a, b: __m256d) -> __m256d { + a := transmute(#simd[4]u64)a + b := transmute(#simd[4]u64)b + return transmute(__m256d)intrinsics.simd_bit_or(a, b) +} + +// Computes the bitwise OR packed single-precision (32-bit) floating-point elements in `a` and `b`. +@(require_results, enable_target_feature="avx") +_mm256_or_ps :: #force_inline proc "c" (a, b: __m256) -> __m256 { + a := transmute(#simd[8]u32)a + b := transmute(#simd[8]u32)b + return transmute(__m256)intrinsics.simd_bit_or(a, b) +} + +// Shuffles double-precision (64-bit) floating-point elements within 128-bit lanes using the control in `imm8`. +@(require_results, enable_target_feature="avx") +_mm256_shuffle_pd :: #force_inline proc "c" (a, b: __m256d, $MASK: u8) -> __m256d { + return intrinsics.simd_shuffle( + a, + b, + MASK & 1, + ((MASK >> 1) & 1) + 4, + ((MASK >> 2) & 1) + 2, + ((MASK >> 3) & 1) + 6, + ) +} + + +// Shuffles single-precision (32-bit) floating-point elements in `a` within 128-bit lanes using the control in `imm8`. +@(require_results, enable_target_feature="avx") +_mm256_shuffle_ps :: #force_inline proc "c" (a, b: __m256, $MASK: u8) -> __m256 { + return intrinsics.simd_shuffle( + a, + b, + MASK & 0b11, + (MASK >> 2) & 0b11, + ((MASK >> 4) & 0b11) + 8, + ((MASK >> 6) & 0b11) + 8, + (MASK & 0b11) + 4, + ((MASK >> 2) & 0b11) + 4, + ((MASK >> 4) & 0b11) + 12, + ((MASK >> 6) & 0b11) + 12, + ) +} + + + +// Computes the bitwise NOT of packed double-precision (64-bit) floating-point elements in `a`, and then AND with `b`. +@(require_results, enable_target_feature="avx") +_mm256_andnot_pd :: #force_inline proc "c" (a, b: __m256d) -> __m256d { + a := transmute(#simd[4]u64)a + b := transmute(#simd[4]u64)b + return transmute(__m256d)intrinsics.simd_bit_and(intrinsics.simd_bit_xor((#simd[4]u64)(0), a), b) +} + +// Computes the bitwise NOT of packed single-precision (32-bit) floating-point elements in `a` and then AND with `b`. +@(require_results, enable_target_feature="avx") +_mm256_andnot_ps :: #force_inline proc "c" (a, b: __m256) -> __m256 { + a := transmute(#simd[8]u32)a + b := transmute(#simd[8]u32)b + return transmute(__m256)intrinsics.simd_bit_and(intrinsics.simd_bit_xor((#simd[8]u32)(0), a), b) +} + + + +// Compares packed double-precision (64-bit) floating-point elements in `a` and `b`, and returns packed maximum values +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_max_pd) +@(require_results, enable_target_feature="avx") +_mm256_max_pd :: #force_inline proc "c" (a, b: __m256d) -> __m256d { + return llvm_vmaxpd(a, b) +} + +// Compares packed single-precision (32-bit) floating-point elements in `a` and `b`, and returns packed maximum values +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_max_ps) +@(require_results, enable_target_feature="avx") +_mm256_max_ps :: #force_inline proc "c" (a, b: __m256) -> __m256 { + return llvm_vmaxps(a, b) +} + +// Compares packed double-precision (64-bit) floating-point elements in `a` and `b`, and returns packed minimum values +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_min_pd) +@(require_results, enable_target_feature="avx") +_mm256_min_pd :: #force_inline proc "c" (a, b: __m256d) -> __m256d { + return llvm_vminpd(a, b) +} + +// Compares packed single-precision (32-bit) floating-point elements in `a` and `b`, and returns packed minimum values +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_min_ps) +@(require_results, enable_target_feature="avx") +_mm256_min_ps :: #force_inline proc "c" (a, b: __m256) -> __m256 { + return llvm_vminps(a, b) +} + + + +// Multiplies packed double-precision (64-bit) floating-point elements in `a` and `b`. +@(require_results, enable_target_feature="avx") +_mm256_mul_pd :: #force_inline proc "c" (a, b: __m256d) -> __m256d { + return intrinsics.simd_mul(a, b) +} + +// Multiplies packed single-precision (32-bit) floating-point elements in `a` and `b`. +@(require_results, enable_target_feature="avx") +_mm256_mul_ps :: #force_inline proc "c" (a, b: __m256) -> __m256 { + return intrinsics.simd_mul(a, b) +} + +// Alternatively adds and subtracts packed double-precision (64-bit) floating-point elements in `a` to/from packed elements in `b`. +@(require_results, enable_target_feature="avx") +_mm256_addsub_pd :: #force_inline proc "c" (a, b: __m256d) -> __m256d { + add := intrinsics.simd_add(a, b) + sub := intrinsics.simd_sub(a, b) + return intrinsics.simd_shuffle(add, sub, 4, 1, 6, 3) +} + + +// Alternatively adds and subtracts packed single-precision (32-bit) floating-point elements in `a` to/from packed elements in `b`. +@(require_results, enable_target_feature="avx") +_mm256_addsub_ps :: #force_inline proc "c" (a, b: __m256) -> __m256 { + add := intrinsics.simd_add(a, b) + sub := intrinsics.simd_sub(a, b) + return intrinsics.simd_shuffle(add, sub, 8, 1, 10, 3, 12, 5, 14, 7) +} + + +// Subtracts packed double-precision (64-bit) floating-point elements in `b` +// from packed elements in `a`. +@(require_results, enable_target_feature="avx") +_mm256_sub_pd :: #force_inline proc "c" (a, b: __m256d) -> __m256d { + return intrinsics.simd_sub(a, b) +} + +// Subtracts packed single-precision (32-bit) floating-point elements in `b` +// from packed elements in `a`. +@(require_results, enable_target_feature="avx") +_mm256_sub_ps :: #force_inline proc "c" (a, b: __m256) -> __m256 { + return intrinsics.simd_sub(a, b) +} + +// Computes the division of each of the 8 packed 32-bit floating-point elements +// in `a` by the corresponding packed elements in `b`. +@(require_results, enable_target_feature="avx") +_mm256_div_ps :: #force_inline proc "c" (a, b: __m256) -> __m256 { + return intrinsics.simd_div(a, b) +} + +// Computes the division of each of the 4 packed 64-bit floating-point elements +// in `a` by the corresponding packed elements in `b`. +@(require_results, enable_target_feature="avx") +_mm256_div_pd :: #force_inline proc "c" (a, b: __m256d) -> __m256d { + return intrinsics.simd_div(a, b) +} + + + +// Rounds packed double-precision (64-bit) floating point elements in `a` +// according to the flag `ROUNDING`. The value of `ROUNDING` may be as follows: +// +// - `0x00`: Round to the nearest whole number. +// - `0x01`: Round down, toward negative infinity. +// - `0x02`: Round up, toward positive infinity. +// - `0x03`: Truncate the values. +// +// For a complete list of options, check [the LLVM docs][llvm_docs]. +// +// [llvm_docs]: https://github.com/llvm-mirror/clang/blob/dcd8d797b20291f1a6b3e0ddda085aa2bbb382a8/lib/Headers/avxintrin.h#L382 +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_round_pd) +@(require_results, enable_target_feature="avx") +_mm256_round_pd :: #force_inline proc "c" (a: __m256d, $ROUNDING: u8) -> __m256d where ROUNDING < 16 { + return llvm_roundpd256(a, ROUNDING) +} + +// Rounds packed double-precision (64-bit) floating point elements in `a` +// toward positive infinity. +@(require_results, enable_target_feature="avx") +_mm256_ceil_pd :: #force_inline proc "c" (a: __m256d) -> __m256d { + return intrinsics.simd_ceil(a) +} + +// Rounds packed double-precision (64-bit) floating point elements in `a` +// toward negative infinity. +@(require_results, enable_target_feature="avx") +_mm256_floor_pd :: #force_inline proc "c" (a: __m256d) -> __m256d { + return intrinsics.simd_floor(a) +} + + + +// Rounds packed single-precision (32-bit) floating point elements in `a` +// according to the flag `ROUNDING`. The value of `ROUNDING` may be as follows: +// +// - `0x00`: Round to the nearest whole number. +// - `0x01`: Round down, toward negative infinity. +// - `0x02`: Round up, toward positive infinity. +// - `0x03`: Truncate the values. +// +// For a complete list of options, check [the LLVM docs][llvm_docs]. +// +// [llvm_docs]: https://github.com/llvm-mirror/clang/blob/dcd8d797b20291f1a6b3e0ddda085aa2bbb382a8/lib/Headers/avxintrin.h#L382 +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_round_ps) +@(require_results, enable_target_feature="avx") +_mm256_round_ps :: #force_inline proc(a: __m256, $ROUNDING: u8) -> __m256 where ROUNDING < 16 { + return llvm_roundps256(a, u32(ROUNDING)) +} + +// Rounds packed single-precision (32-bit) floating point elements in `a` +// toward positive infinity. +@(require_results, enable_target_feature="avx") +_mm256_ceil_ps :: #force_inline proc "c" (a: __m256) -> __m256 { + return intrinsics.simd_ceil(a) +} + +// Rounds packed single-precision (32-bit) floating point elements in `a` +// toward negative infinity. +@(require_results, enable_target_feature="avx") +_mm256_floor_ps :: #force_inline proc "c" (a: __m256) -> __m256 { + return intrinsics.simd_floor(a) +} + +// Returns the square root of packed single-precision (32-bit) floating point elements in `a`. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_sqrt_ps) +@(require_results, enable_target_feature="avx") +_mm256_sqrt_ps :: #force_inline proc "c" (a: __m256) -> __m256 { + return intrinsics.sqrt(a) +} + +// Returns the square root of packed double-precision (64-bit) floating point elements in `a`. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_sqrt_pd) +@(require_results, enable_target_feature="avx") +_mm256_sqrt_pd :: #force_inline proc "c" (a: __m256d) -> __m256d { + return intrinsics.sqrt(a) +} + + + +// Blends packed double-precision (64-bit) floating-point elements from +// `a` and `b` using control mask `imm8`. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_blend_pd) +@(require_results, enable_target_feature="avx") +_mm256_blend_pd :: #force_inline proc "c" (a, b: __m256d, $IIM4: u32) -> __m256d where IMM4 < 16 { + return intrinsics.simd_shuffle( + a, + b, + ((IMM4 >> 0) & 1) * 4 + 0, + ((IMM4 >> 1) & 1) * 4 + 1, + ((IMM4 >> 2) & 1) * 4 + 2, + ((IMM4 >> 3) & 1) * 4 + 3, + ) +} + +// Blends packed single-precision (32-bit) floating-point elements from +// `a` and `b` using control mask `imm8`. +@(require_results, enable_target_feature="avx") +_mm256_blend_ps :: #force_inline proc "c" (a, b: __m256, $IMM8: u8) -> __m256 { + return intrinsics.simd_shuffle( + a, + b, + ((IMM8 >> 0) & 1) * 8 + 0, + ((IMM8 >> 1) & 1) * 8 + 1, + ((IMM8 >> 2) & 1) * 8 + 2, + ((IMM8 >> 3) & 1) * 8 + 3, + ((IMM8 >> 4) & 1) * 8 + 4, + ((IMM8 >> 5) & 1) * 8 + 5, + ((IMM8 >> 6) & 1) * 8 + 6, + ((IMM8 >> 7) & 1) * 8 + 7, + ) +} + + + +// Blends packed double-precision (64-bit) floating-point elements from +// `a` and `b` using `c` as a mask. +@(require_results, enable_target_feature="avx") +_mm256_blendv_pd :: #force_inline proc "c" (a, b: __m256d, c: __m256d) -> __m256d { + mask := intrinsics.simd_lanes_lt(transmute(#simd[4]i64)c, 0) + return intrinsics.simd_select(mask, b, a) +} + +// Blends packed single-precision (32-bit) floating-point elements from +// `a` and `b` using `c` as a mask. +@(require_results, enable_target_feature="avx") +_mm256_blendv_ps :: #force_inline proc "c" (a, b: __m256, c: __m256) -> __m256 { + mask := intrinsics.simd_lanes_lt(transmute(#simd[8]i32)c, 0) + return intrinsics.simd_select(mask, b, a) +} + + + +// Conditionally multiplies the packed single-precision (32-bit) floating-point elements in `a` and `b` using the high 4 bits in `imm8`, +// sum the four products, and conditionally return the sum +// using the low 4 bits of `imm8`. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_dp_ps) +@(require_results, enable_target_feature="avx") +_mm256_dp_ps :: #force_inline proc "c" (a, b: __m256, $IMM8: i8) -> __m256 { + return llvm_vdpps(a, b, IMM8) +} + +// Horizontal addition of adjacent pairs in the two packed vectors +// of 4 64-bit floating points `a` and `b`. +// In the result, sums of elements from `a` are returned in even locations, +// while sums of elements from `b` are returned in odd locations. +@(require_results, enable_target_feature="avx") +_mm256_hadd_pd :: #force_inline proc "c" (a, b: __m256d) -> __m256d { + even := intrinsics.simd_shuffle(a, b, 0, 4, 2, 6) + odd := intrinsics.simd_shuffle(a, b, 1, 5, 3, 7) + return intrinsics.simd_add(even, odd) +} + + + +// Horizontal addition of adjacent pairs in the two packed vectors +// of 8 32-bit floating points `a` and `b`. +// In the result, sums of elements from `a` are returned in locations of +// indices 0, 1, 4, 5; while sums of elements from `b` are locations +// 2, 3, 6, 7. +@(require_results, enable_target_feature="avx") +_mm256_hadd_ps :: #force_inline proc "c" (a, b: __m256) -> __m256 { + even := intrinsics.simd_shuffle(a, b, 0, 2, 8, 10, 4, 6, 12, 14) + odd := intrinsics.simd_shuffle(a, b, 1, 3, 9, 11, 5, 7, 13, 15) + return intrinsics.simd_add(even, odd) +} + +// Horizontal subtraction of adjacent pairs in the two packed vectors +// of 4 64-bit floating points `a` and `b`. +// In the result, sums of elements from `a` are returned in even locations, +// while sums of elements from `b` are returned in odd locations. +@(require_results, enable_target_feature="avx") +_mm256_hsub_pd :: #force_inline proc "c" (a, b: __m256d) -> __m256d { + even := intrinsics.simd_shuffle(a, b, 0, 4, 2, 6) + odd := intrinsics.simd_shuffle(a, b, 1, 5, 3, 7) + return intrinsics.simd_sub(even, odd) +} + +// Horizontal subtraction of adjacent pairs in the two packed vectors +// of 8 32-bit floating points `a` and `b`. +// In the result, sums of elements from `a` are returned in locations of +// indices 0, 1, 4, 5; while sums of elements from `b` are locations +// 2, 3, 6, 7. +@(require_results, enable_target_feature="avx") +_mm256_hsub_ps :: #force_inline proc "c" (a, b: __m256) -> __m256 { + even := intrinsics.simd_shuffle(a, b, 0, 2, 8, 10, 4, 6, 12, 14) + odd := intrinsics.simd_shuffle(a, b, 1, 3, 9, 11, 5, 7, 13, 15) + return intrinsics.simd_sub(even, odd) +} + +// Computes the bitwise XOR of packed double-precision (64-bit) floating-point elements in `a` and `b`. +@(require_results, enable_target_feature="avx") +_mm256_xor_pd :: #force_inline proc "c" (a, b: __m256d) -> __m256d { + a := transmute(#simd[4]u64)a + b := transmute(#simd[4]u64)b + return transmute(__m256d)intrinsics.simd_bit_xor(a, b) +} + +// Computes the bitwise XOR of packed single-precision (32-bit) floating-point elements in `a` and `b`. +@(require_results, enable_target_feature="avx") +_mm256_xor_ps :: #force_inline proc "c" (a, b: __m256) -> __m256 { + a := transmute(#simd[8]u32)a + b := transmute(#simd[8]u32)b + return transmute(__m256)intrinsics.simd_bit_xor(a, b) +} + + + +_CMP_EQ_OQ :: 0x00 // Equal (ordered, non-signaling) +_CMP_LT_OS :: 0x01 // Less-than (ordered, signaling) +_CMP_LE_OS :: 0x02 // Less-than-or-equal (ordered, signaling) +_CMP_UNORD_Q :: 0x03 // Unordered (non-signaling) +_CMP_NEQ_UQ :: 0x04 // Not-equal (unordered, non-signaling) +_CMP_NLT_US :: 0x05 // Not-less-than (unordered, signaling) +_CMP_NLE_US :: 0x06 // Not-less-than-or-equal (unordered, signaling) +_CMP_ORD_Q :: 0x07 // Ordered (non-signaling) +_CMP_EQ_UQ :: 0x08 // Equal (unordered, non-signaling) +_CMP_NGE_US :: 0x09 // Not-greater-than-or-equal (unordered, signaling) +_CMP_NGT_US :: 0x0a // Not-greater-than (unordered, signaling) +_CMP_FALSE_OQ :: 0x0b // False (ordered, non-signaling) +_CMP_NEQ_OQ :: 0x0c // Not-equal (ordered, non-signaling) +_CMP_GE_OS :: 0x0d // Greater-than-or-equal (ordered, signaling) +_CMP_GT_OS :: 0x0e // Greater-than (ordered, signaling) +_CMP_TRUE_UQ :: 0x0f // True (unordered, non-signaling) +_CMP_EQ_OS :: 0x10 // Equal (ordered, signaling) +_CMP_LT_OQ :: 0x11 // Less-than (ordered, non-signaling) +_CMP_LE_OQ :: 0x12 // Less-than-or-equal (ordered, non-signaling) +_CMP_UNORD_S :: 0x13 // Unordered (signaling) +_CMP_NEQ_US :: 0x14 // Not-equal (unordered, signaling) +_CMP_NLT_UQ :: 0x15 // Not-less-than (unordered, non-signaling) +_CMP_NLE_UQ :: 0x16 // Not-less-than-or-equal (unordered, non-signaling) +_CMP_ORD_S :: 0x17 // Ordered (signaling) +_CMP_EQ_US :: 0x18 // Equal (unordered, signaling) +_CMP_NGE_UQ :: 0x19 // Not-greater-than-or-equal (unordered, non-signaling) +_CMP_NGT_UQ :: 0x1a // Not-greater-than (unordered, non-signaling) +_CMP_FALSE_OS :: 0x1b // False (ordered, signaling) +_CMP_NEQ_OS :: 0x1c // Not-equal (ordered, signaling) +_CMP_GE_OQ :: 0x1d // Greater-than-or-equal (ordered, non-signaling) +_CMP_GT_OQ :: 0x1e // Greater-than (ordered, non-signaling) +_CMP_TRUE_US :: 0x1f // True (unordered, signaling) + + + +// Compares packed double-precision (64-bit) floating-point elements in `a` and `b` based on the comparison operand specified by `IMM5`. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmp_pd) +@(require_results, enable_target_feature="avx") +_mm_cmp_pd :: #force_inline proc "c" (a, b: __m128d, $IMM5: u8) -> __m128d where IMM5 < 32 { + return llvm_vcmppd(a, b, IMM5) +} + +// Compares packed double-precision (64-bit) floating-point elements in `a` and `b` based on the comparison operand specified by `IMM5`. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_cmp_pd) +@(require_results, enable_target_feature="avx") +_mm256_cmp_pd :: #force_inline proc "c" (a, b: __m256d, $IMM5: u8) -> __m256d where IMM5 < 32 { + return llvm_vcmppd256(a, b, IMM5) +} + +// Compares packed single-precision (32-bit) floating-point elements in `a` and `b` based on the comparison operand specified by `IMM5`. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmp_ps) +@(require_results, enable_target_feature="avx") +_mm_cmp_ps :: #force_inline proc "c" (a: __m128, b: __m128, $IMM5: u8) -> __m128 where IMM5 < 32 { + return llvm_vcmpps(a, b, IMM5) +} + +// Compares packed single-precision (32-bit) floating-point elements in `a` and `b` based on the comparison operand specified by `IMM5`. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_cmp_ps) +@(require_results, enable_target_feature="avx") +_mm256_cmp_ps :: #force_inline proc "c" (a, b: __m256, $IMM5: u8) -> __m256 where IMM5 < 32 { + return llvm_vcmpps256(a, b, IMM5) +} + +// Compares the lower double-precision (64-bit) floating-point element in +// `a` and `b` based on the comparison operand specified by `IMM5`, +// store the result in the lower element of returned vector, +// and copies the upper element from `a` to the upper element of returned +// vector. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmp_sd) +@(require_results, enable_target_feature="avx") +_mm_cmp_sd :: #force_inline proc "c" (a, b: __m128d, $IMM5: u8) -> __m128d where IMM5 < 32 { + return llvm_vcmpsd(a, b, IMM5) +} + +// Compares the lower single-precision (32-bit) floating-point element in +// `a` and `b` based on the comparison operand specified by `IMM5`, +// store the result in the lower element of returned vector, +// and copies the upper 3 packed elements from `a` to the upper elements of +// returned vector. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cmp_ss) +@(require_results, enable_target_feature="avx") +_mm_cmp_ss :: #force_inline proc "c" (a: __m128, b: __m128, $IMM5: u8) -> __m128 where IMM5 < 32 { + return llvm_vcmpss(a, b, IMM5) +} + +// Converts packed 32-bit integers in `a` to packed double-precision (64-bit) floating-point elements. +@(require_results, enable_target_feature="avx") +_mm256_cvtepi32_pd :: #force_inline proc "c" (a: __m128i) -> __m256d { + return __m256d(transmute(#simd[4]i32)a) +} + +// Converts packed 32-bit integers in `a` to packed single-precision (32-bit) floating-point elements. +@(require_results, enable_target_feature="avx") +_mm256_cvtepi32_ps :: #force_inline proc "c" (a: __m256i) -> __m256 { + return __m256(transmute(#simd[8]i32)a) +} + +// Converts packed double-precision (64-bit) floating-point elements in `a` to packed single-precision (32-bit) floating-point elements. +@(require_results, enable_target_feature="avx") +_mm256_cvtpd_ps :: #force_inline proc "c" (a: __m256d) -> __m128 { + return __m128(a) +} + +// Converts packed single-precision (32-bit) floating-point elements in `a` to packed 32-bit integers. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_cvtps_epi32) +@(require_results, enable_target_feature="avx") +_mm256_cvtps_epi32 :: #force_inline proc "c" (a: __m256) -> __m256i { + return transmute(__m256i)llvm_vcvtps2dq(a) +} + +// Converts packed single-precision (32-bit) floating-point elements in `a` to packed double-precision (64-bit) floating-point elements. +@(require_results, enable_target_feature="avx") +_mm256_cvtps_pd :: #force_inline proc "c" (a: __m128) -> __m256d { + return __m256d(a) +} + +// Returns the first element of the input vector of `[4 x double]`. +@(require_results, enable_target_feature="avx") +_mm256_cvtsd_f64 :: #force_inline proc "c" (a: __m256d) -> f64 { + return intrinsics.simd_extract(a, 0) +} + +// Converts packed double-precision (64-bit) floating-point elements in `a` to packed 32-bit integers with truncation. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_cvttpd_epi32) +@(require_results, enable_target_feature="avx") +_mm256_cvttpd_epi32 :: #force_inline proc "c" (a: __m256d) -> __m128i { + return transmute(__m128i)llvm_vcvttpd2dq(a) +} + +// Converts packed double-precision (64-bit) floating-point elements in `a` to packed 32-bit integers. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_cvtpd_epi32) +@(require_results, enable_target_feature="avx") +_mm256_cvtpd_epi32 :: #force_inline proc "c" (a: __m256d) -> __m128i { + return transmute(__m128i)llvm_vcvtpd2dq(a) +} + +// Converts packed single-precision (32-bit) floating-point elements in `a` to packed 32-bit integers with truncation. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_cvttps_epi32) +@(require_results, enable_target_feature="avx") +_mm256_cvttps_epi32 :: #force_inline proc "c" (a: __m256) -> __m256i { + return transmute(__m256i)llvm_vcvttps2dq(a) +} + + + +// Extracts 128 bits (composed of 4 packed single-precision (32-bit) floating-point elements) from `a`, selected with `imm8`. +@(require_results, enable_target_feature="avx") +_mm256_extractf128_ps :: #force_inline proc "c" (a: __m256, $IMM1: u8) -> __m128 where IMM1 < 2 { + when IMM1 == 0 { + return intrinsics.simd_shuffle(a, _mm256_undefined_ps(), 0, 1, 2, 3) + } else { + return intrinsics.simd_shuffle(a, _mm256_undefined_ps(), 4, 5, 6, 7) + } +} + +// Extracts 128 bits (composed of 2 packed double-precision (64-bit) floating-point elements) from `a`, selected with `imm8`. +@(require_results, enable_target_feature="avx") +_mm256_extractf128_pd :: #force_inline proc "c" (a: __m256d, $IMM1: u8) -> __m128d where IMM1 < 2 { + when IMM1 == 0 { + return intrinsics.simd_shuffle(a, _mm256_undefined_pd(), 0, 1) + } else { + return intrinsics.simd_shuffle(a, _mm256_undefined_pd(), 2, 3) + } +} + +// Extracts 128 bits (composed of integer data) from `a`, selected with `imm8`. +@(require_results, enable_target_feature="avx") +_mm256_extractf128_si256 :: #force_inline proc "c" (a: __m256i, $IMM1: u8) -> __m128i where IMM1 < 2 { + when IMM1 == 0 { + dst := intrinsics.simd_shuffle(transmute(#simd[4]i64)a, (#simd[4]i64)(0), 0, 1) + return transmute(__m128i)dst + } else { + dst := intrinsics.simd_shuffle(transmute(#simd[4]i64)a, (#simd[4]i64)(0), 2, 3) + return transmute(__m128i)dst + } +} + +// Extracts a 32-bit integer from `a`, selected with `INDEX`. +@(require_results, enable_target_feature="avx") +_mm256_extract_epi32 :: #force_inline proc "c" (a: __m256i, $INDEX: u8) -> i32 where INDEX < 8 { + return intrinsics.simd_extract(transmute(#simd[8]i32)a, INDEX) +} + +@(require_results, enable_target_feature="avx") +_mm256_cvtsi256_si32 :: #force_inline proc "c" (a: __m256i) -> i32 { + return intrinsics.simd_extract(transmute(#simd[8]i32)a, 0) +} + +// Zeroes the contents of all XMM or YMM registers. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_zeroall) +@(enable_target_feature="avx") +_mm256_zeroall :: #force_inline proc "c" () { + llvm_vzeroall() +} + +// Zeroes the upper 128 bits of all YMM registers; the lower 128-bits of the registers are unmodified. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_zeroupper) +@(enable_target_feature="avx") +_mm256_zeroupper :: #force_inline proc "c" () { + llvm_vzeroupper() +} + +// Shuffles single-precision (32-bit) floating-point elements in `a` within 128-bit lanes using the control in `b`. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_permutevar_ps) +@(require_results, enable_target_feature="avx") +_mm256_permutevar_ps :: #force_inline proc "c" (a: __m256, b: __m256i) -> __m256 { + return llvm_vpermilps256(a, transmute(#simd[8]i32)b) +} + +// Shuffles single-precision (32-bit) floating-point elements in `a` using the control in `b`. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_permutevar_ps) +@(require_results, enable_target_feature="avx") +_mm_permutevar_ps :: #force_inline proc "c" (a: __m128, b: __m128i) -> __m128 { + return llvm_vpermilps(a, transmute(#simd[4]i32)b) +} + +// Shuffles single-precision (32-bit) floating-point elements in `a` within 128-bit lanes using the control in `imm8`. +@(require_results, enable_target_feature="avx") +_mm256_permute_ps :: #force_inline proc "c" (a: __m256, $IMM8: u8) -> __m256 { + return intrinsics.simd_shuffle( + a, + _mm256_undefined_ps(), + (IMM8 >> 0) & 0b11, + (IMM8 >> 2) & 0b11, + (IMM8 >> 4) & 0b11, + (IMM8 >> 6) & 0b11, + ((IMM8 >> 0) & 0b11) + 4, + ((IMM8 >> 2) & 0b11) + 4, + ((IMM8 >> 4) & 0b11) + 4, + ((IMM8 >> 6) & 0b11) + 4, + ) +} + +// Shuffles single-precision (32-bit) floating-point elements in `a` using the control in `imm8`. +@(require_results, enable_target_feature="avx") +_mm_permute_ps :: #force_inline proc "c" (a: __m128, $IMM8: u8) -> __m128 { + return intrinsics.simd_shuffle( + a, + _mm_undefined_ps(), + (IMM8 >> 0) & 0b11, + (IMM8 >> 2) & 0b11, + (IMM8 >> 4) & 0b11, + (IMM8 >> 6) & 0b11, + ) +} + +// Shuffles double-precision (64-bit) floating-point elements in `a` within 256-bit lanes using the control in `b`. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_permutevar_pd) +@(require_results, enable_target_feature="avx") +_mm256_permutevar_pd :: #force_inline proc "c" (a: __m256d, b: __m256i) -> __m256d { + return llvm_vpermilpd256(a, b) +} + +// Shuffles double-precision (64-bit) floating-point elements in `a` using the control in `b`. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_permutevar_pd) +@(require_results, enable_target_feature="avx") +_mm_permutevar_pd :: #force_inline proc "c" (a: __m128d, b: __m128i) -> __m128d { + return llvm_vpermilpd(a, b) +} + +// Shuffles double-precision (64-bit) floating-point elements in `a` within 128-bit lanes using the control in `imm8`. +@(require_results, enable_target_feature="avx") +_mm256_permute_pd :: #force_inline proc "c" (a: __m256d, $IMM4: u8) -> __m256d where IMM4 < 16 { + return intrinsics.simd_shuffle( + a, + _mm256_undefined_pd(), + ((IMM4 >> 0) & 1), + ((IMM4 >> 1) & 1), + ((IMM4 >> 2) & 1) + 2, + ((IMM4 >> 3) & 1) + 2, + ) +} + +// Shuffles double-precision (64-bit) floating-point elements in `a` using the control in `imm8`. +@(require_results, enable_target_feature="avx") +_mm_permute_pd :: #force_inline proc "c" (a: __m128d, $IMM2: u8) -> __m128d where IMM2 < 4 { + return intrinsics.simd_shuffle( + a, + _mm_undefined_pd(), + (IMM2) & 1, + (IMM2 >> 1) & 1, + ) +} + + + +// Shuffles 256 bits (composed of 8 packed single-precision (32-bit) floating-point elements) selected by `imm8` from `a` and `b`. +@(require_results, enable_target_feature="avx") +_mm256_permute2f128_ps :: #force_inline proc "c" (a, b: __m256, $IMM8: u8) -> __m256 { + return _mm256_castsi256_ps(_mm256_permute2f128_si256( + _mm256_castps_si256(a), + _mm256_castps_si256(b), + IMM8, + )) +} + +// Shuffles 256 bits (composed of 4 packed double-precision (64-bit) floating-point elements) selected by `imm8` from `a` and `b`. +@(require_results, enable_target_feature="avx") +_mm256_permute2f128_pd :: #force_inline proc "c" (a, b: __m256d, $IMM8: u8) -> __m256d { + _mm256_castsi256_pd(_mm256_permute2f128_si256( + _mm256_castpd_si256(a), + _mm256_castpd_si256(b), + IMM8, + )) +} + +// Shuffles 128-bits (composed of integer data) selected by `imm8` from `a` and `b`. +@(require_results, enable_target_feature="avx") +_mm256_permute2f128_si256 :: #force_inline proc "c" (a, b: __m256i, $IMM8: u8) -> __m256i { + r := intrinsics.simd_shuffle( + a, + b, + 2 * ((IMM8 & 0xf) & 0b11) + 0, + 2 * ((IMM8 & 0xf) & 0b11) + 1, + + 2 * (((IMM8 & 0xf0) >> 4) & 0b11) + 0, + 2 * (((IMM8 & 0xf0) >> 4) & 0b11) + 1, + ) + return intrinsics.simd_shuffle( + r, + __m256i(0), + + 4 if ((IMM8 & 0xf) & 0b1000) != 0 else 0, + 4 if ((IMM8 & 0xf) & 0b1000) != 0 else 1, + + 4 if (((IMM8 & 0xf0)>>4) & 0b1000) != 0 else 2, + 4 if (((IMM8 & 0xf0)>>4) & 0b1000) != 0 else 3, + ) +} + +// Broadcasts a single-precision (32-bit) floating-point element from memory to all elements of the returned vector. +@(require_results, enable_target_feature="avx") +_mm256_broadcast_ss :: #force_inline proc "c" (f: ^f32) -> __m256 { + return _mm256_set1_ps(f^) +} + +// Broadcasts a single-precision (32-bit) floating-point element from memory to all elements of the returned vector. +@(require_results, enable_target_feature="sse,avx") +_mm_broadcast_ss :: #force_inline proc "c" (f: ^f32) -> __m128 { + return _mm_set1_ps(f^) +} + +// Broadcasts a double-precision (64-bit) floating-point element from memory to all elements of the returned vector. +@(require_results, enable_target_feature="avx") +_mm256_broadcast_sd :: #force_inline proc "c" (f: ^f64) -> __m256d { + return _mm256_set1_pd(f^) +} + +// Broadcasts 128 bits from memory (composed of 4 packed single-precision (32-bit) floating-point elements) to all elements of the returned vector. +@(require_results, enable_target_feature="sse,avx") +_mm256_broadcast_ps :: #force_inline proc "c" (a: ^__m128) -> __m256 { + return intrinsics.simd_shuffle(a^, _mm_setzero_ps(), 0, 1, 2, 3, 0, 1, 2, 3) +} + +// Broadcasts 128 bits from memory (composed of 2 packed double-precision (64-bit) floating-point elements) to all elements of the returned vector. +@(require_results, enable_target_feature="sse2,avx") +_mm256_broadcast_pd :: #force_inline proc "c" (a: ^__m128d) -> __m256d { + return intrinsics.simd_shuffle(a^, _mm_setzero_pd(), 0, 1, 0, 1) +} + +// Copies `a` to result, then inserts 128 bits (composed of 4 packed +// single-precision (32-bit) floating-point elements) from `b` into result +// at the location specified by `imm8`. +@(require_results, enable_target_feature="sse,avx") +_mm256_insertf128_ps :: #force_inline proc "c" (a: __m256, b: __m128, $IMM1: u8) -> __m256 where IMM1 < 2 { + when IMM1 == 0 { + return intrinsics.simd_shuffle( + a, + _mm256_castps128_ps256(b), + 8, 9, 10, 11, 4, 5, 6, 7, + ) + } else { + return intrinsics.simd_shuffle( + a, + _mm256_castps128_ps256(b), + 0, 1, 2, 3, 8, 9, 10, 11, + ) + } +} + +// Copies `a` to result, then inserts 128 bits (composed of 2 packed +// double-precision (64-bit) floating-point elements) from `b` into result +// at the location specified by `imm8`. +@(require_results, enable_target_feature="sse2,avx") +_mm256_insertf128_pd :: #force_inline proc "c" (a: __m256d, b: __m128d, $IMM1: u8) -> __m256d where IMM1 < 2 { + when IMM1 == 0 { + return intrinsics.simd_shuffle( + a, + _mm256_castpd128_pd256(b), + 4, 5, 2, 3, + ) + } else { + return intrinsics.simd_shuffle( + a, + _mm256_castpd128_pd256(b), + 0, 1, 4, 5, + ) + } +} + +// Copies `a` to result, then inserts 128 bits from `b` into result at the location specified by `imm8`. +@(require_results, enable_target_feature="avx") +_mm256_insertf128_si256 :: #force_inline proc "c" (a: __m256i, b: __m128i, $IMM1: u8) -> __m256i where IMM1 < 2 { + when IMM1 == 0 { + return intrinsics.simd_shuffle( + a, + _mm256_castsi128_si256(b), + 4, 5, 2, 3, + ) + } else { + return intrinsics.simd_shuffle( + a, + _mm256_castsi128_si256(b), + 0, 1, 4, 5, + ) + } +} + +// Copies `a` to result, and inserts the 8-bit integer `i` into result at the location specified by `index`. +@(require_results, enable_target_feature="avx") +_mm256_insert_epi8 :: #force_inline proc "c" (a: __m256i, i: i8, $INDEX: u8) -> __m256i where INDEX < 32 { + return transmute(__m256i)intrinsics.simd_replace(transmute(#simd[32]i8)a, INDEX, i) +} + +// Copies `a` to result, and inserts the 16-bit integer `i` into result at the location specified by `index`. +@(require_results, enable_target_feature="avx") +_mm256_insert_epi16 :: #force_inline proc "c" (a: __m256i, i: i16, $INDEX: u8) -> __m256i where INDEX < 16 { + return transmute(__m256i)intrinsics.simd_replace(transmute(#simd[16]i16)a, INDEX, i) +} + +// Copies `a` to result, and inserts the 32-bit integer `i` into result at the location specified by `index`. +@(require_results, enable_target_feature="avx") +_mm256_insert_epi32 :: #force_inline proc "c" (a: __m256i, i: i32, $INDEX: u8) -> __m256i where INDEX < 8 { + return transmute(__m256i)intrinsics.simd_replace(transmute(#simd[8]i32)a, INDEX, i) +} + + + +// Loads 256-bits (composed of 4 packed double-precision (64-bit) floating-point elements) from memory into result. +// `mem_addr` must be aligned on a 32-byte boundary or a +// general-protection exception may be generated. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_load_pd) +@(require_results, enable_target_feature="avx") +_mm256_load_pd :: #force_inline proc "c" (mem_addr: ^f64) -> __m256d { + return (^__m256d)(mem_addr)^ +} + +// Stores 256-bits (composed of 4 packed double-precision (64-bit) floating-point elements) from `a` into memory. +// `mem_addr` must be aligned on a 32-byte boundary or a +// general-protection exception may be generated. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_store_pd) +@(enable_target_feature="avx") +_mm256_store_pd :: #force_inline proc "c" (mem_addr: ^f64, a: __m256d) { + (^__m256d)(mem_addr)^ = a +} + +// Loads 256-bits (composed of 8 packed single-precision (32-bit) floating-point elements) from memory into result. +// `mem_addr` must be aligned on a 32-byte boundary or a +// general-protection exception may be generated. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_load_ps) +@(require_results, enable_target_feature="avx") +_mm256_load_ps :: #force_inline proc "c" (mem_addr: ^f32) -> __m256 { + return (^__m256)(mem_addr)^ +} + +// Stores 256-bits (composed of 8 packed single-precision (32-bit) floating-point elements) from `a` into memory. +// `mem_addr` must be aligned on a 32-byte boundary or a +// general-protection exception may be generated. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_store_ps) +@(enable_target_feature="avx") +_mm256_store_ps :: #force_inline proc "c" (mem_addr: ^f32, a: __m256) { + (^__m256)(mem_addr)^ = a +} + +// Loads 256-bits (composed of 4 packed double-precision (64-bit) floating-point elements) from memory into result. +// `mem_addr` does not need to be aligned on any particular boundary. +@(enable_target_feature="avx") +_mm256_loadu_pd :: #force_inline proc "c" (mem_addr: ^f64) -> __m256d { + return intrinsics.unaligned_load((^__m256d)(mem_addr)) +} + +// Stores 256-bits (composed of 4 packed double-precision (64-bit) floating-point elements) from `a` into memory. +// `mem_addr` does not need to be aligned on any particular boundary. +@(enable_target_feature="avx") +_mm256_storeu_pd :: #force_inline proc "c" (mem_addr: ^f64, a: __m256d) { + intrinsics.unaligned_store((^__m256d)(mem_addr), a) +} + +// Loads 256-bits (composed of 8 packed single-precision (32-bit) floating-point elements) from memory into result. +// `mem_addr` does not need to be aligned on any particular boundary. +@(require_results, enable_target_feature="avx") +_mm256_loadu_ps :: #force_inline proc "c" (mem_addr: ^f32) -> __m256 { + return intrinsics.unaligned_load((^__m256)(mem_addr)) +} + +// Stores 256-bits (composed of 8 packed single-precision (32-bit) floating-point elements) from `a` into memory. +// `mem_addr` does not need to be aligned on any particular boundary. +@(enable_target_feature="avx") +_mm256_storeu_ps :: #force_inline proc "c" (mem_addr: ^f32, a: __m256) { + intrinsics.unaligned_store((^__m256)(mem_addr), a) +} + +// Loads 256-bits of integer data from memory into result. +// `mem_addr` must be aligned on a 32-byte boundary or a +// general-protection exception may be generated. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_load_si256) +@(require_results, enable_target_feature="avx") +_mm256_load_si256 :: #force_inline proc "c" (mem_addr: ^__m256i) -> __m256i { + return mem_addr^ +} + +// Stores 256-bits of integer data from `a` into memory. +// `mem_addr` must be aligned on a 32-byte boundary or a +// general-protection exception may be generated. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_store_si256) +@(enable_target_feature="avx") +_mm256_store_si256 :: #force_inline proc "c" (mem_addr: ^__m256i, a: __m256i) { + mem_addr^ = a +} + +// Loads 256-bits of integer data from memory into result. +// `mem_addr` does not need to be aligned on any particular boundary. +@(require_results, enable_target_feature="avx") +_mm256_loadu_si256 :: #force_inline proc "c" (mem_addr: ^__m256i) -> __m256i { + return intrinsics.unaligned_load(mem_addr) +} + +// Stores 256-bits of integer data from `a` into memory. +// `mem_addr` does not need to be aligned on any particular boundary. +@(enable_target_feature="avx") +_mm256_storeu_si256 :: #force_inline proc "c" (mem_addr: ^__m256i, a: __m256i) { + intrinsics.unaligned_store(mem_addr, a) +} + +// Loads packed double-precision (64-bit) floating-point elements from memory +// into result using `mask` (elements are zeroed out when the high bit of the +// corresponding element is not set). +@(require_results, enable_target_feature="avx") +_mm256_maskload_pd :: #force_inline proc "c" (mem_addr: ^f64, mask: __m256i) -> __m256d { + mask_mask := intrinsics.simd_shr(mask, 63) + return intrinsics.simd_masked_load(mem_addr, _mm256_setzero_pd(), mask_mask) +} + +// Stores packed double-precision (64-bit) floating-point elements from `a` +// into memory using `mask`. +@(enable_target_feature="avx") +_mm256_maskstore_pd :: #force_inline proc "c" (mem_addr: ^f64, mask: __m256i, a: __m256d) { + mask_mask := intrinsics.simd_shr(mask, 63) + intrinsics.simd_masked_store(mem_addr, a, mask_mask) +} + + +// Loads packed double-precision (64-bit) floating-point elements from memory +// into result using `mask` (elements are zeroed out when the high bit of the +// corresponding element is not set). +@(require_results, enable_target_feature="sse2,avx") +_mm_maskload_pd :: #force_inline proc "c" (mem_addr: ^f64, mask: __m128i) -> __m128d { + mask_mask := intrinsics.simd_shr(mask, 63) + return intrinsics.simd_masked_load(mem_addr, _mm_setzero_pd(), mask_mask) +} + +// Stores packed double-precision (64-bit) floating-point elements from `a` +// into memory using `mask`. +@(enable_target_feature="avx") +_mm_maskstore_pd :: #force_inline proc "c" (mem_addr: ^f64, mask: __m128i, a: __m128d) { + mask_mask := intrinsics.simd_shr(mask, 63) + intrinsics.simd_masked_store(mem_addr, a, mask_mask) +} + +// Loads packed single-precision (32-bit) floating-point elements from memory +// into result using `mask` (elements are zeroed out when the high bit of the +// corresponding element is not set). +@(require_results, enable_target_feature="avx") +_mm256_maskload_ps :: #force_inline proc "c" (mem_addr: ^f32, mask: __m256i) -> __m256 { + mask_mask := intrinsics.simd_shr(transmute(#simd[8]i32)mask, 31) + return intrinsics.simd_masked_load(mem_addr, _mm256_setzero_ps(), mask_mask) +} + +// Stores packed single-precision (32-bit) floating-point elements from `a` +// into memory using `mask`. +@(enable_target_feature="avx") +_mm256_maskstore_ps :: #force_inline proc "c" (mem_addr: ^f32, mask: __m256i, a: __m256) { + mask_mask := intrinsics.simd_shr(transmute(#simd[8]i32)mask, 31) + intrinsics.simd_masked_store(mem_addr, a, mask_mask) +} + +// Loads packed single-precision (32-bit) floating-point elements from memory +// into result using `mask` (elements are zeroed out when the high bit of the +// corresponding element is not set). +@(require_results, enable_target_feature="sse,avx") +_mm_maskload_ps :: #force_inline proc "c" (mem_addr: ^f32, mask: __m128i) -> __m128 { + mask_mask := intrinsics.simd_shr(transmute(#simd[4]i32)mask, 31) + return intrinsics.simd_masked_load(mem_addr, _mm_setzero_ps(), mask_mask) +} + +// Stores packed single-precision (32-bit) floating-point elements from `a` +// into memory using `mask`. +@(enable_target_feature="avx") +_mm_maskstore_ps :: #force_inline proc "c" (mem_addr: ^f32, mask: __m128i, a: __m128) { + mask_mask := intrinsics.simd_shr(transmute(#simd[4]i32)mask, 31) + intrinsics.simd_masked_store(mem_addr, a, mask_mask) +} + + + + +// Duplicate odd-indexed single-precision (32-bit) floating-point elements from `a`, and returns the results. +@(require_results, enable_target_feature="avx") +_mm256_movehdup_ps :: #force_inline proc "c" (a: __m256) -> __m256 { + return intrinsics.simd_shuffle(a, a, 1, 1, 3, 3, 5, 5, 7, 7) +} + +// Duplicate even-indexed single-precision (32-bit) floating-point elements from `a`, and returns the results. +@(require_results, enable_target_feature="avx") +_mm256_moveldup_ps :: #force_inline proc "c" (a: __m256) -> __m256 { + return intrinsics.simd_shuffle(a, a, 0, 0, 2, 2, 4, 4, 6, 6) +} + +// Duplicate even-indexed double-precision (64-bit) floating-point elements from `a`, and returns the results. +@(require_results, enable_target_feature="avx") +_mm256_movedup_pd :: #force_inline proc "c" (a: __m256d) -> __m256d { + return intrinsics.simd_shuffle(a, a, 0, 0, 2, 2) +} + + +// Loads 256-bits of integer data from unaligned memory into result. +// This intrinsic may perform better than `_mm256_loadu_si256` when the +// data crosses a cache line boundary. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_lddqu_si256) +@(require_results, enable_target_feature="avx") +_mm256_lddqu_si256 :: #force_inline proc "c" (mem_addr: ^__m256i) -> __m256i { + return transmute(__m256i)llvm_vlddqu(mem_addr) +} + +/* +// Moves integer data from a 256-bit integer vector to a 32-byte +// aligned memory location. To minimize caching, the data is flagged as +// non-temporal (unlikely to be used again soon) +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_stream_si256) +// +// # Safety of non-temporal stores +// +// After using this intrinsic, but before any other access to the memory that this intrinsic +// mutates, a call to [`_mm_sfence`] must be performed by the thread that used the intrinsic. In +// particular, functions that call this intrinsic should generally call `_mm_sfence` before they +// return. +// +// See [`_mm_sfence`] for details. +@(enable_target_feature="avx") +_mm256_stream_si256 :: #force_inline proc "c" (mem_addr: ^__m256i, a: __m256i) { + panic_contextless("TODO: _mm256_stream_si256") +} + +// Moves double-precision values from a 256-bit vector of `[4 x double]` +// to a 32-byte aligned memory location. To minimize caching, the data is +// flagged as non-temporal (unlikely to be used again soon). +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_stream_pd) +// +// # Safety of non-temporal stores +// +// After using this intrinsic, but before any other access to the memory that this intrinsic +// mutates, a call to [`_mm_sfence`] must be performed by the thread that used the intrinsic. In +// particular, functions that call this intrinsic should generally call `_mm_sfence` before they +// return. +// +// See [`_mm_sfence`] for details. +@(enable_target_feature="avx") +_mm256_stream_pd :: #force_inline proc "c" (mem_addr: ^f64, a: __m256d) { + panic_contextless("TODO: _mm256_stream_pd") +} + +// Moves single-precision floating point values from a 256-bit vector +// of `[8 x float]` to a 32-byte aligned memory location. To minimize +// caching, the data is flagged as non-temporal (unlikely to be used again +// soon). +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_stream_ps) +// +// # Safety of non-temporal stores +// +// After using this intrinsic, but before any other access to the memory that this intrinsic +// mutates, a call to [`_mm_sfence`] must be performed by the thread that used the intrinsic. In +// particular, functions that call this intrinsic should generally call `_mm_sfence` before they +// return. +// +// See [`_mm_sfence`] for details. +@(enable_target_feature="avx") +_mm256_stream_ps :: #force_inline proc "c" (mem_addr: ^f32, a: __m256) { + panic_contextless("TODO: _mm256_stream_ps") +} +*/ + +// Computes the approximate reciprocal of packed single-precision (32-bit) floating-point elements in `a`, and returns the results. The maximum +// relative error for this approximation is less than 1.5*2^-12. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_rcp_ps) +@(require_results, enable_target_feature="avx") +_mm256_rcp_ps :: #force_inline proc "c" (a: __m256) -> __m256 { + return llvm_vrcpps(a) +} + +// Computes the approximate reciprocal square root of packed single-precision +// (32-bit) floating-point elements in `a`, and returns the results. +// The maximum relative error for this approximation is less than 1.5*2^-12. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_rsqrt_ps) +@(require_results, enable_target_feature="avx") +_mm256_rsqrt_ps :: #force_inline proc "c" (a: __m256) -> __m256 { + return llvm_vrsqrtps(a) +} + + + +// Unpacks and interleave double-precision (64-bit) floating-point elements +// from the high half of each 128-bit lane in `a` and `b`. +@(require_results, enable_target_feature="avx") +_mm256_unpackhi_pd :: #force_inline proc "c" (a, b: __m256d) -> __m256d { + return intrinsics.simd_shuffle(a, b, 1, 5, 3, 7) +} + +// Unpacks and interleave single-precision (32-bit) floating-point elements +// from the high half of each 128-bit lane in `a` and `b`. +@(require_results, enable_target_feature="avx") +_mm256_unpackhi_ps :: #force_inline proc "c" (a, b: __m256) -> __m256 { + return intrinsics.simd_shuffle(a, b, 2, 10, 3, 11, 6, 14, 7, 15) +} + +// Unpacks and interleave double-precision (64-bit) floating-point elements +// from the low half of each 128-bit lane in `a` and `b`. +@(require_results, enable_target_feature="avx") +_mm256_unpacklo_pd :: #force_inline proc "c" (a, b: __m256d) -> __m256d { + return intrinsics.simd_shuffle(a, b, 0, 4, 2, 6) +} + +// Unpacks and interleave single-precision (32-bit) floating-point elements +// from the low half of each 128-bit lane in `a` and `b`. +@(require_results, enable_target_feature="avx") +_mm256_unpacklo_ps :: #force_inline proc "c" (a, b: __m256) -> __m256 { + return intrinsics.simd_shuffle(a, b, 0, 8, 1, 9, 4, 12, 5, 13) +} + +// Computes the bitwise AND of 256 bits (representing integer data) in `a` and +// `b`, and set `ZF` to 1 if the result is zero, otherwise set `ZF` to 0. +// Computes the bitwise NOT of `a` and then AND with `b`, and set `CF` to 1 if +// the result is zero, otherwise set `CF` to 0. Return the `ZF` value. +@(require_results, enable_target_feature="avx") +_mm256_testz_si256 :: #force_inline proc "c" (a, b: __m256i) -> i32 { + r := intrinsics.simd_bit_and(a, b) + return i32(0 == intrinsics.simd_reduce_or(r)) +} + +// Computes the bitwise AND of 256 bits (representing integer data) in `a` and +// `b`, and set `ZF` to 1 if the result is zero, otherwise set `ZF` to 0. +// Computes the bitwise NOT of `a` and then AND with `b`, and set `CF` to 1 if +// the result is zero, otherwise set `CF` to 0. Return the `CF` value. +@(require_results, enable_target_feature="avx") +_mm256_testc_si256 :: #force_inline proc "c" (a, b: __m256i) -> i32 { + r := intrinsics.simd_bit_and(intrinsics.simd_bit_xor(a, __m256i(~i64(0))), b) + return i32(0 == intrinsics.simd_reduce_or(r)) +} + + + +// Computes the bitwise AND of 256 bits (representing integer data) in `a` and +// `b`, and set `ZF` to 1 if the result is zero, otherwise set `ZF` to 0. +// Computes the bitwise NOT of `a` and then AND with `b`, and set `CF` to 1 if +// the result is zero, otherwise set `CF` to 0. Return 1 if both the `ZF` and +// `CF` values are zero, otherwise return 0. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_testnzc_si256) +@(require_results, enable_target_feature="avx") +_mm256_testnzc_si256 :: #force_inline proc "c" (a, b: __m256i) -> i32 { + return llvm_ptestnzc256(a, b) +} + +// Computes the bitwise AND of 256 bits (representing double-precision (64-bit) floating-point elements) in `a` and `b`, producing an intermediate 256-bit +// value, and set `ZF` to 1 if the sign bit of each 64-bit element in the +// intermediate value is zero, otherwise set `ZF` to 0. Compute the bitwise +// NOT of `a` and then AND with `b`, producing an intermediate value, and set +// `CF` to 1 if the sign bit of each 64-bit element in the intermediate value +// is zero, otherwise set `CF` to 0. Return the `ZF` value. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_testz_pd) +@(require_results, enable_target_feature="avx") +_mm256_testz_pd :: #force_inline proc "c" (a, b: __m256d) -> i32 { + return llvm_vtestzpd256(a, b) +} + +// Computes the bitwise AND of 256 bits (representing double-precision (64-bit) floating-point elements) in `a` and `b`, producing an intermediate 256-bit +// value, and set `ZF` to 1 if the sign bit of each 64-bit element in the +// intermediate value is zero, otherwise set `ZF` to 0. Compute the bitwise +// NOT of `a` and then AND with `b`, producing an intermediate value, and set +// `CF` to 1 if the sign bit of each 64-bit element in the intermediate value +// is zero, otherwise set `CF` to 0. Return the `CF` value. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_testc_pd) +@(require_results, enable_target_feature="avx") +_mm256_testc_pd :: #force_inline proc "c" (a, b: __m256d) -> i32 { + return llvm_vtestcpd256(a, b) +} + +// Computes the bitwise AND of 256 bits (representing double-precision (64-bit) floating-point elements) in `a` and `b`, producing an intermediate 256-bit +// value, and set `ZF` to 1 if the sign bit of each 64-bit element in the +// intermediate value is zero, otherwise set `ZF` to 0. Compute the bitwise +// NOT of `a` and then AND with `b`, producing an intermediate value, and set +// `CF` to 1 if the sign bit of each 64-bit element in the intermediate value +// is zero, otherwise set `CF` to 0. Return 1 if both the `ZF` and `CF` values +// are zero, otherwise return 0. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_testnzc_pd) +@(require_results, enable_target_feature="avx") +_mm256_testnzc_pd :: #force_inline proc "c" (a, b: __m256d) -> i32 { + return llvm_vtestnzcpd256(a, b) +} + +// Computes the bitwise AND of 128 bits (representing double-precision (64-bit) floating-point elements) in `a` and `b`, producing an intermediate 128-bit +// value, and set `ZF` to 1 if the sign bit of each 64-bit element in the +// intermediate value is zero, otherwise set `ZF` to 0. Compute the bitwise +// NOT of `a` and then AND with `b`, producing an intermediate value, and set +// `CF` to 1 if the sign bit of each 64-bit element in the intermediate value +// is zero, otherwise set `CF` to 0. Return the `ZF` value. +@(require_results, enable_target_feature="sse2,avx") +_mm_testz_pd :: #force_inline proc "c" (a, b: __m128d) -> i32 { + r := intrinsics.simd_lanes_lt(transmute(__m128i)_mm_and_pd(a, b), __m128i(0)) + return i32(0 == intrinsics.simd_reduce_or(r)) +} + +// Computes the bitwise AND of 128 bits (representing double-precision (64-bit) floating-point elements) in `a` and `b`, producing an intermediate 128-bit +// value, and set `ZF` to 1 if the sign bit of each 64-bit element in the +// intermediate value is zero, otherwise set `ZF` to 0. Compute the bitwise +// NOT of `a` and then AND with `b`, producing an intermediate value, and set +// `CF` to 1 if the sign bit of each 64-bit element in the intermediate value +// is zero, otherwise set `CF` to 0. Return the `CF` value. +@(require_results, enable_target_feature="sse2,avx") +_mm_testc_pd :: #force_inline proc "c" (a, b: __m128d) -> i32 { + r := intrinsics.simd_lanes_lt(transmute(__m128i)_mm_andnot_pd(a, b), __m128i(0)) + return i32(0 == intrinsics.simd_reduce_or(r)) +} + +// Computes the bitwise AND of 128 bits (representing double-precision (64-bit) floating-point elements) in `a` and `b`, producing an intermediate 128-bit +// value, and set `ZF` to 1 if the sign bit of each 64-bit element in the +// intermediate value is zero, otherwise set `ZF` to 0. Compute the bitwise +// NOT of `a` and then AND with `b`, producing an intermediate value, and set +// `CF` to 1 if the sign bit of each 64-bit element in the intermediate value +// is zero, otherwise set `CF` to 0. Return 1 if both the `ZF` and `CF` values +// are zero, otherwise return 0. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_testnzc_pd) +@(require_results, enable_target_feature="avx") +_mm_testnzc_pd :: #force_inline proc "c" (a, b: __m128d) -> i32 { + return llvm_vtestnzcpd(a, b) +} + +// Computes the bitwise AND of 256 bits (representing single-precision (32-bit) floating-point elements) in `a` and `b`, producing an intermediate 256-bit +// value, and set `ZF` to 1 if the sign bit of each 32-bit element in the +// intermediate value is zero, otherwise set `ZF` to 0. Compute the bitwise +// NOT of `a` and then AND with `b`, producing an intermediate value, and set +// `CF` to 1 if the sign bit of each 32-bit element in the intermediate value +// is zero, otherwise set `CF` to 0. Return the `ZF` value. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_testz_ps) +@(require_results, enable_target_feature="avx") +_mm256_testz_ps :: #force_inline proc "c" (a, b: __m256) -> i32 { + return llvm_vtestzps256(a, b) +} + +// Computes the bitwise AND of 256 bits (representing single-precision (32-bit) floating-point elements) in `a` and `b`, producing an intermediate 256-bit +// value, and set `ZF` to 1 if the sign bit of each 32-bit element in the +// intermediate value is zero, otherwise set `ZF` to 0. Compute the bitwise +// NOT of `a` and then AND with `b`, producing an intermediate value, and set +// `CF` to 1 if the sign bit of each 32-bit element in the intermediate value +// is zero, otherwise set `CF` to 0. Return the `CF` value. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_testc_ps) +@(require_results, enable_target_feature="avx") +_mm256_testc_ps :: #force_inline proc "c" (a, b: __m256) -> i32 { + return llvm_vtestcps256(a, b) +} + +// Computes the bitwise AND of 256 bits (representing single-precision (32-bit) floating-point elements) in `a` and `b`, producing an intermediate 256-bit +// value, and set `ZF` to 1 if the sign bit of each 32-bit element in the +// intermediate value is zero, otherwise set `ZF` to 0. Compute the bitwise +// NOT of `a` and then AND with `b`, producing an intermediate value, and set +// `CF` to 1 if the sign bit of each 32-bit element in the intermediate value +// is zero, otherwise set `CF` to 0. Return 1 if both the `ZF` and `CF` values +// are zero, otherwise return 0. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_testnzc_ps) +@(require_results, enable_target_feature="avx") +_mm256_testnzc_ps :: #force_inline proc "c" (a, b: __m256) -> i32 { + return llvm_vtestnzcps256(a, b) +} + +// Computes the bitwise AND of 128 bits (representing single-precision (32-bit) floating-point elements) in `a` and `b`, producing an intermediate 128-bit +// value, and set `ZF` to 1 if the sign bit of each 32-bit element in the +// intermediate value is zero, otherwise set `ZF` to 0. Compute the bitwise +// NOT of `a` and then AND with `b`, producing an intermediate value, and set +// `CF` to 1 if the sign bit of each 32-bit element in the intermediate value +// is zero, otherwise set `CF` to 0. Return the `ZF` value. +@(require_results, enable_target_feature="sse,avx") +_mm_testz_ps :: #force_inline proc "c" (a: __m128, b: __m128) -> i32 { + r := intrinsics.simd_lanes_lt(transmute(#simd[4]i32)_mm_and_ps(a, b), (#simd[4]i32)(0)) + return i32(0 == intrinsics.simd_reduce_or(r)) +} + +// Computes the bitwise AND of 128 bits (representing single-precision (32-bit) floating-point elements) in `a` and `b`, producing an intermediate 128-bit +// value, and set `ZF` to 1 if the sign bit of each 32-bit element in the +// intermediate value is zero, otherwise set `ZF` to 0. Compute the bitwise +// NOT of `a` and then AND with `b`, producing an intermediate value, and set +// `CF` to 1 if the sign bit of each 32-bit element in the intermediate value +// is zero, otherwise set `CF` to 0. Return the `CF` value. +@(require_results, enable_target_feature="sse,avx") +_mm_testc_ps :: #force_inline proc "c" (a: __m128, b: __m128) -> i32 { + r := intrinsics.simd_lanes_lt(transmute(#simd[4]i32)_mm_andnot_ps(a, b), (#simd[4]i32)(0)) + return i32(0 == intrinsics.simd_reduce_or(r)) +} + +// Computes the bitwise AND of 128 bits (representing single-precision (32-bit) floating-point elements) in `a` and `b`, producing an intermediate 128-bit +// value, and set `ZF` to 1 if the sign bit of each 32-bit element in the +// intermediate value is zero, otherwise set `ZF` to 0. Compute the bitwise +// NOT of `a` and then AND with `b`, producing an intermediate value, and set +// `CF` to 1 if the sign bit of each 32-bit element in the intermediate value +// is zero, otherwise set `CF` to 0. Return 1 if both the `ZF` and `CF` values +// are zero, otherwise return 0. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_testnzc_ps) +@(require_results, enable_target_feature="avx") +_mm_testnzc_ps :: #force_inline proc "c" (a: __m128, b: __m128) -> i32 { + return llvm_vtestnzcps(a, b) +} + +// Sets each bit of the returned mask based on the most significant bit of the +// corresponding packed double-precision (64-bit) floating-point element in +// `a`. +@(require_results, enable_target_feature="avx") +_mm256_movemask_pd :: #force_inline proc "c" (a: __m256d) -> i32 { + mask := intrinsics.simd_lanes_lt(transmute(#simd[4]i64)a, (#simd[4]i64)(0)) + return i32(transmute(u8)intrinsics.simd_extract_lsbs(mask)) +} + +// Sets each bit of the returned mask based on the most significant bit of the +// corresponding packed single-precision (32-bit) floating-point element in +// `a`. +@(require_results, enable_target_feature="avx") +_mm256_movemask_ps :: #force_inline proc "c" (a: __m256) -> i32 { + // Propagate the highest bit to the rest, because simd_bitmask + // requires all-1 or all-0. + mask := intrinsics.simd_lanes_lt(transmute(#simd[8]i32)a, (#simd[8]i32)(0)) + return i32(transmute(u8)intrinsics.simd_extract_lsbs(mask)) +} + +// Returns vector of type __m256d with all elements set to zero. +@(require_results, enable_target_feature="avx") +_mm256_setzero_pd :: #force_inline proc "c" () -> __m256d { + return 0 +} + +// Returns vector of type __m256 with all elements set to zero. +@(require_results, enable_target_feature="avx") +_mm256_setzero_ps :: #force_inline proc "c" () -> __m256 { + return 0 +} + +// Returns vector of type __m256i with all elements set to zero. +@(require_results, enable_target_feature="avx") +_mm256_setzero_si256 :: #force_inline proc "c" () -> __m256i { + return 0 +} + +// Sets packed double-precision (64-bit) floating-point elements in returned +// vector with the supplied values. +@(require_results, enable_target_feature="avx") +_mm256_set_pd :: #force_inline proc "c" (a: f64, b: f64, c: f64, d: f64) -> __m256d { + return _mm256_setr_pd(d, c, b, a) +} + +// Sets packed single-precision (32-bit) floating-point elements in returned +// vector with the supplied values. +@(require_results, enable_target_feature="avx") +_mm256_set_ps :: #force_inline proc "c" ( + a: f32, + b: f32, + c: f32, + d: f32, + e: f32, + f: f32, + g: f32, + h: f32, +) -> __m256 { + return _mm256_setr_ps(h, g, f, e, d, c, b, a) +} + +// Sets packed 8-bit integers in returned vector with the supplied values. +@(require_results, enable_target_feature="avx") +_mm256_set_epi8 :: #force_inline proc "c" ( + e00, e01, e02, e03, e04, e05, e06, e07: i8, + e08, e09, e10, e11, e12, e13, e14, e15: i8, + e16, e17, e18, e19, e20, e21, e22, e23: i8, + e24, e25, e26, e27, e28, e29, e30, e31: i8, +) -> __m256i { + return _mm256_setr_epi8( + e31, e30, e29, e28, e27, e26, e25, e24, + e23, e22, e21, e20, e19, e18, e17, e16, + e15, e14, e13, e12, e11, e10, e09, e08, + e07, e06, e05, e04, e03, e02, e01, e00, + ) +} + +// Sets packed 16-bit integers in returned vector with the supplied values. +@(require_results, enable_target_feature="avx") +_mm256_set_epi16 :: #force_inline proc "c" ( + e00, e01, e02, e03, e04, e05, e06, e07: i16, + e08, e09, e10, e11, e12, e13, e14, e15: i16, +) -> __m256i { + return _mm256_setr_epi16( + e15, e14, e13, e12, + e11, e10, e09, e08, + e07, e06, e05, e04, + e03, e02, e01, e00, + ) +} + +// Sets packed 32-bit integers in returned vector with the supplied values. +@(require_results, enable_target_feature="avx") +_mm256_set_epi32 :: #force_inline proc "c" (e0, e1, e2, e3, e4, e5, e6, e7: i32) -> __m256i { + return _mm256_setr_epi32(e7, e6, e5, e4, e3, e2, e1, e0) +} + +// Sets packed 64-bit integers in returned vector with the supplied values. +@(require_results, enable_target_feature="avx") +_mm256_set_epi64x :: #force_inline proc "c" (a: i64, b: i64, c: i64, d: i64) -> __m256i { + return _mm256_setr_epi64x(d, c, b, a) +} + +// Sets packed double-precision (64-bit) floating-point elements in returned +// vector with the supplied values in reverse order. +@(require_results, enable_target_feature="avx") +_mm256_setr_pd :: #force_inline proc "c" (a: f64, b: f64, c: f64, d: f64) -> __m256d { + return __m256d{a, b, c, d} +} + +// Sets packed single-precision (32-bit) floating-point elements in returned +// vector with the supplied values in reverse order. +@(require_results, enable_target_feature="avx") +_mm256_setr_ps :: #force_inline proc "c" (a, b, c, d, e, f, g, h: f32) -> __m256 { + return __m256{a, b, c, d, e, f, g, h} +} + +// Sets packed 8-bit integers in returned vector with the supplied values in +// reverse order. +@(require_results, enable_target_feature="avx") +_mm256_setr_epi8 :: #force_inline proc "c" ( + e00, e01, e02, e03, e04, e05, e06, e07: i8, + e08, e09, e10, e11, e12, e13, e14, e15: i8, + e16, e17, e18, e19, e20, e21, e22, e23: i8, + e24, e25, e26, e27, e28, e29, e30, e31: i8, +) -> __m256i { + return transmute(__m256i)#simd[32]i8{ + e00, e01, e02, e03, e04, e05, e06, e07, + e08, e09, e10, e11, e12, e13, e14, e15, + e16, e17, e18, e19, e20, e21, e22, e23, + e24, e25, e26, e27, e28, e29, e30, e31, + } +} + +// Sets packed 16-bit integers in returned vector with the supplied values in +// reverse order. +@(require_results, enable_target_feature="avx") +_mm256_setr_epi16 :: #force_inline proc "c" ( + e00, e01, e02, e03, e04, e05, e06, e07: i16, + e08, e09, e10, e11, e12, e13, e14, e15: i16, +) -> __m256i { + return transmute(__m256i)#simd[16]i16{ + e00, e01, e02, e03, + e04, e05, e06, e07, + e08, e09, e10, e11, + e12, e13, e14, e15, + } +} + +// Sets packed 32-bit integers in returned vector with the supplied values in +// reverse order. +@(require_results, enable_target_feature="avx") +_mm256_setr_epi32 :: #force_inline proc "c" (e0, e1, e2, e3, e4, e5, e6, e7: i32) -> __m256i { + return transmute(__m256i)#simd[8]i32{e0, e1, e2, e3, e4, e5, e6, e7} +} + +// Sets packed 64-bit integers in returned vector with the supplied values in +// reverse order. +@(require_results, enable_target_feature="avx") +_mm256_setr_epi64x :: #force_inline proc "c" (a: i64, b: i64, c: i64, d: i64) -> __m256i { + return {a, b, c, d} +} + +// Broadcasts double-precision (64-bit) floating-point value `a` to all elements of returned vector. +@(require_results, enable_target_feature="avx") +_mm256_set1_pd :: #force_inline proc "c" (a: f64) -> __m256d { + return a +} + +// Broadcasts single-precision (32-bit) floating-point value `a` to all elements of returned vector. +@(require_results, enable_target_feature="avx") +_mm256_set1_ps :: #force_inline proc "c" (a: f32) -> __m256 { + return a +} + +// Broadcasts 8-bit integer `a` to all elements of returned vector. +// This intrinsic may generate the `vpbroadcastb`. +@(require_results, enable_target_feature="avx") +_mm256_set1_epi8 :: #force_inline proc "c" (a: i8) -> __m256i { + return transmute(__m256i)(#simd[32]i8)(a) +} + +// Broadcasts 16-bit integer `a` to all elements of returned vector. +// This intrinsic may generate the `vpbroadcastw`. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_set1_epi16) +@(require_results, enable_target_feature="avx") +_mm256_set1_epi16 :: #force_inline proc "c" (a: i16) -> __m256i { + return transmute(__m256i)(#simd[16]i16)(a) +} + +// Broadcasts 32-bit integer `a` to all elements of returned vector. +// This intrinsic may generate the `vpbroadcastd`. +@(require_results, enable_target_feature="avx") +_mm256_set1_epi32 :: #force_inline proc "c" (a: i32) -> __m256i { + return transmute(__m256i)(#simd[8]i32)(a) +} + +// Broadcasts 64-bit integer `a` to all elements of returned vector. +// This intrinsic may generate the `vpbroadcastq`. +// +// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_set1_epi64x) +@(require_results, enable_target_feature="avx") +_mm256_set1_epi64x :: #force_inline proc "c" (a: i64) -> __m256i { + return a +} + +// Cast vector of type __m256d to type __m256. +@(require_results, enable_target_feature="avx") +_mm256_castpd_ps :: #force_inline proc "c" (a: __m256d) -> __m256 { + return transmute(__m256)a +} + +// Cast vector of type __m256 to type __m256d. +@(require_results, enable_target_feature="avx") +_mm256_castps_pd :: #force_inline proc "c" (a: __m256) -> __m256d { + return transmute(__m256d)a +} + +// Casts vector of type __m256 to type __m256i. +@(require_results, enable_target_feature="avx") +_mm256_castps_si256 :: #force_inline proc "c" (a: __m256) -> __m256i { + return transmute(__m256i)a +} + +// Casts vector of type __m256i to type __m256. +@(require_results, enable_target_feature="avx") +_mm256_castsi256_ps :: #force_inline proc "c" (a: __m256i) -> __m256 { + return transmute(__m256)a +} + +// Casts vector of type __m256d to type __m256i. +@(require_results, enable_target_feature="avx") +_mm256_castpd_si256 :: #force_inline proc "c" (a: __m256d) -> __m256i { + return transmute(__m256i)a +} + +// Casts vector of type __m256i to type __m256d. +@(require_results, enable_target_feature="avx") +_mm256_castsi256_pd :: #force_inline proc "c" (a: __m256i) -> __m256d { + return transmute(__m256d)a +} + +// Casts vector of type __m256 to type __m128. +@(require_results, enable_target_feature="avx") +_mm256_castps256_ps128 :: #force_inline proc "c" (a: __m256) -> __m128 { + return intrinsics.simd_shuffle(a, a, 0, 1, 2, 3) +} + +// Casts vector of type __m256d to type __m128d. +@(require_results, enable_target_feature="avx") +_mm256_castpd256_pd128 :: #force_inline proc "c" (a: __m256d) -> __m128d { + return intrinsics.simd_shuffle(a, a, 0, 1) +} + +// Casts vector of type __m256i to type __m128i. +@(require_results, enable_target_feature="avx") +_mm256_castsi256_si128 :: #force_inline proc "c" (a: __m256i) -> __m128i { + return intrinsics.simd_shuffle(a, a, 0, 1) +} + +// Casts vector of type __m128 to type __m256; +// the upper 128 bits of the result are indeterminate. +// +// In the Intel documentation, the upper bits are declared to be "undefined". +@(require_results, enable_target_feature="sse,avx") +_mm256_castps128_ps256 :: #force_inline proc "c" (a: __m128) -> __m256 { + return intrinsics.simd_shuffle(a, _mm_undefined_ps(), 0, 1, 2, 3, 4, 4, 4, 4) +} + +// Casts vector of type __m128d to type __m256d; +// the upper 128 bits of the result are indeterminate. +// +// In the Intel documentation, the upper bits are declared to be "undefined". +@(require_results, enable_target_feature="sse2,avx") +_mm256_castpd128_pd256 :: #force_inline proc "c" (a: __m128d) -> __m256d { + return intrinsics.simd_shuffle(a, _mm_undefined_pd(), 0, 1, 2, 2) +} + +// Casts vector of type __m128i to type __m256i; +// the upper 128 bits of the result are indeterminate. +// +// In the Intel documentation, the upper bits are declared to be "undefined". +@(require_results, enable_target_feature="avx") +_mm256_castsi128_si256 :: #force_inline proc "c" (a: __m128i) -> __m256i { + return intrinsics.simd_shuffle(a, __m128i(0), 0, 1, 2, 2) +} + +// Constructs a 256-bit floating-point vector of `[8 x float]` from a +// 128-bit floating-point vector of `[4 x float]`. The lower 128 bits contain +// the value of the source vector. The upper 128 bits are set to zero. +@(require_results, enable_target_feature="sse,avx") +_mm256_zextps128_ps256 :: #force_inline proc "c" (a: __m128) -> __m256 { + return intrinsics.simd_shuffle(a, _mm_setzero_ps(), 0, 1, 2, 3, 4, 5, 6, 7) +} + +// Constructs a 256-bit integer vector from a 128-bit integer vector. +// The lower 128 bits contain the value of the source vector. The upper +// 128 bits are set to zero. +@(require_results, enable_target_feature="avx") +_mm256_zextsi128_si256 :: #force_inline proc "c" (a: __m128i) -> __m256i { + return intrinsics.simd_shuffle(a, __m128i(0), 0, 1, 2, 3) +} + +// Constructs a 256-bit floating-point vector of `[4 x double]` from a +// 128-bit floating-point vector of `[2 x double]`. The lower 128 bits +// contain the value of the source vector. The upper 128 bits are set +// to zero. +@(require_results, enable_target_feature="sse2,avx") +_mm256_zextpd128_pd256 :: #force_inline proc "c" (a: __m128d) -> __m256d { + return intrinsics.simd_shuffle(a, _mm_setzero_pd(), 0, 1, 2, 3) +} + +// Returns vector of type `__m256` with indeterminate elements. +// Despite using the word "undefined" (following Intel's naming scheme), this non-deterministically +@(require_results, enable_target_feature="avx") +_mm256_undefined_ps :: #force_inline proc "c" () -> __m256 { + return 0 +} + +// Returns vector of type `__m256d` with indeterminate elements. +// Despite using the word "undefined" (following Intel's naming scheme), this non-deterministically +@(require_results, enable_target_feature="avx") +_mm256_undefined_pd :: #force_inline proc "c" () -> __m256d { + return 0 +} + +// Returns vector of type __m256i with with indeterminate elements. +// Despite using the word "undefined" (following Intel's naming scheme), this non-deterministically +@(require_results, enable_target_feature="avx") +_mm256_undefined_si256 :: #force_inline proc "c" () -> __m256i { + return 0 +} + +// Sets packed __m256 returned vector with the supplied values. +@(require_results, enable_target_feature="avx") +_mm256_set_m128 :: #force_inline proc "c" (hi: __m128, lo: __m128) -> __m256 { + return intrinsics.simd_shuffle(lo, hi, 0, 1, 2, 3, 4, 5, 6, 7) +} + +// Sets packed __m256d returned vector with the supplied values. +@(require_results, enable_target_feature="avx") +_mm256_set_m128d :: #force_inline proc "c" (hi: __m128d, lo: __m128d) -> __m256d { + hi := transmute(__m128)hi + lo := transmute(__m128)lo + return transmute(__m256d)_mm256_set_m128(hi, lo) +} + +// Sets packed __m256i returned vector with the supplied values. +@(require_results, enable_target_feature="avx") +_mm256_set_m128i :: #force_inline proc "c" (hi: __m128i, lo: __m128i) -> __m256i { + hi := transmute(__m128)hi + lo := transmute(__m128)lo + return transmute(__m256i)_mm256_set_m128(hi, lo) +} + +// Sets packed __m256 returned vector with the supplied values. +@(require_results, enable_target_feature="avx") +_mm256_setr_m128 :: #force_inline proc "c" (lo: __m128, hi: __m128) -> __m256 { + return _mm256_set_m128(hi, lo) +} + +// Sets packed __m256d returned vector with the supplied values. +@(require_results, enable_target_feature="avx") +_mm256_setr_m128d :: #force_inline proc "c" (lo: __m128d, hi: __m128d) -> __m256d { + return _mm256_set_m128d(hi, lo) +} + +// Sets packed __m256i returned vector with the supplied values. +@(require_results, enable_target_feature="avx") +_mm256_setr_m128i :: #force_inline proc "c" (lo: __m128i, hi: __m128i) -> __m256i { + return _mm256_set_m128i(hi, lo) +} + +// Loads two 128-bit values (composed of 4 packed single-precision (32-bit) floating-point elements) from memory, and combine them into a 256-bit value. +// `hiaddr` and `loaddr` do not need to be aligned on any particular boundary. +@(require_results, enable_target_feature="sse,avx") +_mm256_loadu2_m128 :: #force_inline proc "c" (hiaddr, loaddr: ^f32) -> __m256 { + a := _mm256_castps128_ps256(_mm_loadu_ps(loaddr)) + return _mm256_insertf128_ps(a, _mm_loadu_ps(hiaddr), 1) +} + +// Loads two 128-bit values (composed of 2 packed double-precision (64-bit) floating-point elements) from memory, and combine them into a 256-bit value. +// `hiaddr` and `loaddr` do not need to be aligned on any particular boundary. +@(require_results, enable_target_feature="sse2,avx") +_mm256_loadu2_m128d :: #force_inline proc "c" (hiaddr, loaddr: ^f64) -> __m256d { + a := _mm256_castpd128_pd256(_mm_loadu_pd(loaddr)) + return _mm256_insertf128_pd(a, _mm_loadu_pd(hiaddr), 1) +} + +// Loads two 128-bit values (composed of integer data) from memory, and combine them into a 256-bit value. +// `hiaddr` and `loaddr` do not need to be aligned on any particular boundary. +@(require_results, enable_target_feature="sse2,avx") +_mm256_loadu2_m128i :: #force_inline proc "c" (hiaddr, loaddr: ^__m128i) -> __m256i { + a := _mm256_castsi128_si256(_mm_loadu_si128(loaddr)) + return _mm256_insertf128_si256(a, _mm_loadu_si128(hiaddr), 1) +} + +// Stores the high and low 128-bit halves (each composed of 4 packed +// single-precision (32-bit) floating-point elements) from `a` into memory two +// different 128-bit locations. +// `hiaddr` and `loaddr` do not need to be aligned on any particular boundary. +@(enable_target_feature="sse,avx") +_mm256_storeu2_m128 :: #force_inline proc "c" (hiaddr, loaddr: ^f32, a: __m256) { + lo := _mm256_castps256_ps128(a) + _mm_storeu_ps(loaddr, lo) + hi := _mm256_extractf128_ps(a, 1) + _mm_storeu_ps(hiaddr, hi) +} + +// Stores the high and low 128-bit halves (each composed of 2 packed +// double-precision (64-bit) floating-point elements) from `a` into memory two +// different 128-bit locations. +// `hiaddr` and `loaddr` do not need to be aligned on any particular boundary. +@(enable_target_feature="sse2,avx") +_mm256_storeu2_m128d :: #force_inline proc "c" (hiaddr, loaddr: ^f64, a: __m256d) { + lo := _mm256_castpd256_pd128(a) + _mm_storeu_pd(loaddr, lo) + hi := _mm256_extractf128_pd(a, 1) + _mm_storeu_pd(hiaddr, hi) +} + +// Stores the high and low 128-bit halves (each composed of integer data) from +// `a` into memory two different 128-bit locations. +// `hiaddr` and `loaddr` do not need to be aligned on any particular boundary. +@(enable_target_feature="sse2,avx") +_mm256_storeu2_m128i :: #force_inline proc "c" (hiaddr, loaddr: ^__m128i, a: __m256i) { + lo := _mm256_castsi256_si128(a) + _mm_storeu_si128(loaddr, lo) + hi := _mm256_extractf128_si256(a, 1) + _mm_storeu_si128(hiaddr, hi) +} + +// Returns the first element of the input vector of `[8 x float]`. +@(require_results, enable_target_feature="avx") +_mm256_cvtss_f32 :: #force_inline proc "c" (a: __m256) -> f32 { + return intrinsics.simd_extract(a, 0) +} + + + +@(require_results, enable_target_feature="avx") +_mm256_insert_epi64 :: #force_inline proc "c" (a: __m256i, i: i64, $idx: u32) -> __m256i { + return intrinsics.simd_replace(transmute(#simd[4]i64)a, idx, i) +} + +@(require_results, enable_target_feature="avx") +_mm256_extract_epi64 :: #force_inline proc "c" (a: __m256i, $idx: u32) -> i64 { + return intrinsics.simd_extract(transmute(#simd[4]i64)a, idx) +} + + +@(private, default_calling_convention="none") +foreign _ { + @(link_name="llvm.x86.avx.round.pd.256") llvm_roundpd256 :: proc(a: __m256d, #const b: u32) -> __m256d --- + @(link_name="llvm.x86.avx.round.ps.256") llvm_roundps256 :: proc(a: __m256, #const b: u32) -> __m256 --- + @(link_name="llvm.x86.avx.dp.ps.256") llvm_vdpps :: proc(a, b: __m256, #const imm8: u8) -> __m256 --- + @(link_name="llvm.x86.sse2.cmp.pd") llvm_vcmppd :: proc(a, b: __m128d, #const imm8: u8) -> __m128d --- + @(link_name="llvm.x86.avx.cmp.pd.256") llvm_vcmppd256 :: proc(a, b: __m256d, imm8: u8) -> __m256d --- + @(link_name="llvm.x86.sse.cmp.ps") llvm_vcmpps :: proc(a: __m128, b: __m128, #const imm8: u8) -> __m128 --- + @(link_name="llvm.x86.avx.cmp.ps.256") llvm_vcmpps256 :: proc(a, b: __m256, imm8: u8) -> __m256 --- + @(link_name="llvm.x86.sse2.cmp.sd") llvm_vcmpsd :: proc(a, b: __m128d, #const imm8: u8) -> __m128d --- + @(link_name="llvm.x86.sse.cmp.ss") llvm_vcmpss :: proc(a: __m128, b: __m128, #const imm8: u8) -> __m128 --- + @(link_name="llvm.x86.avx.cvt.ps2dq.256") llvm_vcvtps2dq :: proc(a: __m256) -> #simd[8]i32 --- + @(link_name="llvm.x86.avx.cvtt.pd2dq.256") llvm_vcvttpd2dq :: proc(a: __m256d) -> #simd[4]i32 --- + @(link_name="llvm.x86.avx.cvt.pd2dq.256") llvm_vcvtpd2dq :: proc(a: __m256d) -> #simd[4]i32 --- + @(link_name="llvm.x86.avx.cvtt.ps2dq.256") llvm_vcvttps2dq :: proc(a: __m256) -> #simd[8]i32 --- + @(link_name="llvm.x86.avx.vzeroall") llvm_vzeroall :: proc() --- + @(link_name="llvm.x86.avx.vzeroupper") llvm_vzeroupper :: proc() --- + @(link_name="llvm.x86.avx.vpermilvar.ps.256") llvm_vpermilps256 :: proc(a: __m256, b: #simd[8]i32) -> __m256 --- + @(link_name="llvm.x86.avx.vpermilvar.ps") llvm_vpermilps :: proc(a: __m128, b: #simd[4]i32) -> __m128 --- + @(link_name="llvm.x86.avx.vpermilvar.pd.256") llvm_vpermilpd256 :: proc(a: __m256d, b: #simd[4]i64) -> __m256d --- + @(link_name="llvm.x86.avx.vpermilvar.pd") llvm_vpermilpd :: proc(a: __m128d, b: #simd[2]i64) -> __m128d --- + @(link_name="llvm.x86.avx.ldu.dq.256") llvm_vlddqu :: proc(mem_addr: rawptr) -> #simd[32]i8 --- + @(link_name="llvm.x86.avx.rcp.ps.256") llvm_vrcpps :: proc(a: __m256) -> __m256 --- + @(link_name="llvm.x86.avx.rsqrt.ps.256") llvm_vrsqrtps :: proc(a: __m256) -> __m256 --- + @(link_name="llvm.x86.avx.ptestnzc.256") llvm_ptestnzc256 :: proc(a: #simd[4]i64, b: #simd[4]i64) -> i32 --- + @(link_name="llvm.x86.avx.vtestz.pd.256") llvm_vtestzpd256 :: proc(a, b: __m256d) -> i32 --- + @(link_name="llvm.x86.avx.vtestc.pd.256") llvm_vtestcpd256 :: proc(a, b: __m256d) -> i32 --- + @(link_name="llvm.x86.avx.vtestnzc.pd.256") llvm_vtestnzcpd256 :: proc(a, b: __m256d) -> i32 --- + @(link_name="llvm.x86.avx.vtestnzc.pd") llvm_vtestnzcpd :: proc(a, b: __m128d) -> i32 --- + @(link_name="llvm.x86.avx.vtestz.ps.256") llvm_vtestzps256 :: proc(a, b: __m256) -> i32 --- + @(link_name="llvm.x86.avx.vtestc.ps.256") llvm_vtestcps256 :: proc(a, b: __m256) -> i32 --- + @(link_name="llvm.x86.avx.vtestnzc.ps.256") llvm_vtestnzcps256 :: proc(a, b: __m256) -> i32 --- + @(link_name="llvm.x86.avx.vtestnzc.ps") llvm_vtestnzcps :: proc(a: __m128, b: __m128) -> i32 --- + @(link_name="llvm.x86.avx.min.ps.256") llvm_vminps :: proc(a, b: __m256) -> __m256 --- + @(link_name="llvm.x86.avx.max.ps.256") llvm_vmaxps :: proc(a, b: __m256) -> __m256 --- + @(link_name="llvm.x86.avx.min.pd.256") llvm_vminpd :: proc(a, b: __m256d) -> __m256d --- + @(link_name="llvm.x86.avx.max.pd.256") llvm_vmaxpd :: proc(a, b: __m256d) -> __m256d --- +} diff --git a/core/sys/freebsd/constants.odin b/core/sys/freebsd/constants.odin index 3188b32d6..692cfc0fe 100644 --- a/core/sys/freebsd/constants.odin +++ b/core/sys/freebsd/constants.odin @@ -3,3 +3,17 @@ package sys_freebsd /* Get window size */ TIOCGWINSZ :: 0x40087468 +/* + Standard input file descriptor +*/ +STDIN_FILENO :: Fd(0) + +/* + Standard output file descriptor +*/ +STDOUT_FILENO :: Fd(1) + +/* + Standard error file descriptor +*/ +STDERR_FILENO :: Fd(2) diff --git a/core/sys/freebsd/syscalls.odin b/core/sys/freebsd/syscalls.odin index 3d62a975e..ad10a9d62 100644 --- a/core/sys/freebsd/syscalls.odin +++ b/core/sys/freebsd/syscalls.odin @@ -23,6 +23,7 @@ SYS_recvfrom : uintptr : 29 SYS_accept : uintptr : 30 SYS_getpeername: uintptr : 31 SYS_getsockname: uintptr : 32 +SYS_ioctl : uintptr : 54 SYS_fcntl : uintptr : 92 SYS_fsync : uintptr : 95 SYS_socket : uintptr : 97 @@ -634,3 +635,13 @@ accept4_nil :: proc "contextless" (s: Fd, flags: Socket_Flags = {}) -> (Fd, Errn } accept4 :: proc { accept4_nil, accept4_T } + +ioctl :: proc "contextless" (fd: Fd, request: c.ulong, arg: uintptr) -> (int, Errno) { + result, ok := intrinsics.syscall_bsd(SYS_ioctl, cast(uintptr)fd, arg) + + if !ok { + return -1, cast(Errno)result + } + + return cast(int)result, nil +} diff --git a/core/sys/linux/sys.odin b/core/sys/linux/sys.odin index 6e9c8ab91..9bb7b122d 100644 --- a/core/sys/linux/sys.odin +++ b/core/sys/linux/sys.odin @@ -2207,7 +2207,7 @@ when ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 { Available since Linux 1.0. */ adjtimex :: proc "contextless" (buf: ^Timex) -> (Clock_State, Errno) { - ret := syscall(SYS_adjtimex) + ret := syscall(SYS_adjtimex, buf) return errno_unwrap(ret, Clock_State) } diff --git a/core/sys/windows/ioringapi.odin b/core/sys/windows/ioringapi.odin new file mode 100644 index 000000000..0e3b89722 --- /dev/null +++ b/core/sys/windows/ioringapi.odin @@ -0,0 +1,106 @@ +#+build windows +package sys_windows + +foreign import ioringapi "system:kernel32.lib" + +HIORING :: distinct rawptr + +IORING_SQE_FLAG :: enum u32 { + DRAIN_PRECEDING_OPS = 0, // 0x00000001 +} +IORING_SQE_FLAGS :: bit_set[IORING_SQE_FLAG; u32] + +// Reserved for future use; currently no flags defined +IORING_CREATE_REQUIRED_FLAG :: enum u32 {} +IORING_CREATE_REQUIRED_FLAGS :: bit_set[IORING_CREATE_REQUIRED_FLAG; u32] + +IORING_CREATE_ADVISORY_FLAG :: enum u32 { + SKIP_BUILDER_PARAM_CHECKS = 0, // 0x00000001 +} +IORING_CREATE_ADVISORY_FLAGS :: bit_set[IORING_CREATE_ADVISORY_FLAG; u32] + +IORING_CREATE_FLAGS :: struct { + Required: IORING_CREATE_REQUIRED_FLAGS, + Advisory: IORING_CREATE_ADVISORY_FLAGS, +} + +IORING_INFO :: struct { + Version: IORING_VERSION, + Flags: IORING_CREATE_FLAGS, + SubmissionQueueSize: UINT32, + CompletionQueueSize: UINT32, +} + +IORING_CAPABILITIES :: struct { + MaxVersion: IORING_VERSION, + MaxSubmissionQueueSize: UINT32, + MaxCompletionQueueSize: UINT32, + FeatureFlags: IORING_FEATURE_FLAGS, +} + +IORING_REF_KIND :: enum i32 { + RAW, + REGISTERED, +} + +IORING_HANDLE_REF :: struct { + Kind: IORING_REF_KIND, + HandleUnion: struct #raw_union { + Handle: HANDLE, + Index: UINT32, + }, +} + +IORING_BUFFER_REF :: struct { + Kind: IORING_REF_KIND, + BufferUnion: struct #raw_union { + Address: rawptr, + IndexAndOffset: IORING_REGISTERED_BUFFER, + }, +} + +IORING_CQE :: struct { + UserData: UINT_PTR, + ResultCode: HRESULT, + Information: ULONG_PTR, +} + +// Types below are from winbase.h and winnt.h +FILE_WRITE_FLAG :: enum u32 { + WRITE_THROUGH = 0, // 0x000000001 +} +FILE_WRITE_FLAGS :: bit_set[FILE_WRITE_FLAG ;u32] + +FILE_FLUSH_MODE :: enum i32 { + DEFAULT, + DATA, + MIN_METADATA, + NO_SYNC, +} + +FILE_SEGMENT_ELEMENT :: struct #raw_union { + Buffer: PVOID64, + Alignment: ULONGLONG, +} + + +@(default_calling_convention="system") +foreign ioringapi { + QueryIoRingCapabilities :: proc(capabilities: ^IORING_CAPABILITIES) -> HRESULT --- + IsIoRingOpSupported :: proc(ioRing: HIORING, op: IORING_OP_CODE) -> BOOL --- + CreateIoRing :: proc(ioringVersion: IORING_VERSION, flags: IORING_CREATE_FLAGS, submissionQueueSize: UINT32, completionQueueSize: UINT32, h: ^HIORING) -> HRESULT --- + GetIoRingInfo :: proc(ioRing: HIORING, info: ^IORING_INFO) -> HRESULT --- + SubmitIoRing :: proc(ioRing: HIORING, waitOperations: UINT32, milliseconds: UINT32, submittedEntries: ^UINT32) -> HRESULT --- + CloseIoRing :: proc(ioRing: HIORING) -> HRESULT --- + PopIoRingCompletion :: proc(ioRing: HIORING, cqe: ^IORING_CQE) -> HRESULT --- + SetIoRingCompletionEvent :: proc(ioRing: HIORING, hEvent: HANDLE) -> HRESULT --- + + BuildIoRingCancelRequest :: proc(ioRing: HIORING, file: IORING_HANDLE_REF, opToCancel: UINT_PTR, userData: UINT_PTR) -> HRESULT --- + BuildIoRingReadFile :: proc(ioRing: HIORING, fileRef: IORING_HANDLE_REF, dataRef: IORING_BUFFER_REF, numberOfBytesToRead: UINT32, fileOffset: UINT64, userData: UINT_PTR, sqeFlags: IORING_SQE_FLAGS) -> HRESULT --- + BuildIoRingRegisterFileHandles :: proc(ioRing: HIORING, count: UINT32, handles: [^]HANDLE, userData: UINT_PTR) -> HRESULT --- + BuildIoRingRegisterBuffers :: proc(ioRing: HIORING, count: UINT32, buffers: [^]IORING_BUFFER_INFO, userData: UINT_PTR) -> HRESULT --- + BuildIoRingWriteFile :: proc(ioRing: HIORING, fileRef: IORING_HANDLE_REF, bufferRef: IORING_BUFFER_REF, numberOfBytesToWrite: UINT32, fileOffset: UINT64, writeFlags: FILE_WRITE_FLAGS, userData: UINT_PTR, sqeFlags: IORING_SQE_FLAGS) -> HRESULT --- + BuildIoRingFlushFile :: proc(ioRing: HIORING, fileRef: IORING_HANDLE_REF, flushMode: FILE_FLUSH_MODE, userData: UINT_PTR, sqeFlags: IORING_SQE_FLAGS) -> HRESULT --- + BuildIoRingReadFileScatter :: proc(ioRing: HIORING, fileRef: IORING_HANDLE_REF, segmentCount: UINT32, segmentArray: [^]FILE_SEGMENT_ELEMENT, numberOfBytesToRead: UINT32, fileOffset: UINT64, userData: UINT_PTR, sqeFlags: IORING_SQE_FLAGS) -> HRESULT --- + BuildIoRingWriteFileGather :: proc(ioRing: HIORING, fileRef: IORING_HANDLE_REF, segmentCount: UINT32, segmentArray: [^]FILE_SEGMENT_ELEMENT, numberOfBytesToWrite: UINT32, fileOffset: UINT64, writeFlags: FILE_WRITE_FLAGS, userData: UINT_PTR, sqeFlags: IORING_SQE_FLAGS) -> HRESULT --- +} diff --git a/core/sys/windows/ntdll.odin b/core/sys/windows/ntdll.odin index 8362bb9df..41deaa1c4 100644 --- a/core/sys/windows/ntdll.odin +++ b/core/sys/windows/ntdll.odin @@ -50,6 +50,22 @@ foreign ntdll_lib { EaBuffer: PVOID, EaLength: ULONG, ) -> NTSTATUS --- + + + NtAssociateWaitCompletionPacket :: proc( + WaitCompletionPacketHandle: HANDLE, + IoCompletionHandle: HANDLE, + TargetObjectHandle: HANDLE, + KeyContext: PVOID, + ApcContext: PVOID, + IoStatus: NTSTATUS, + IoStatusInformation: ULONG_PTR, + AlreadySignaled: ^BOOLEAN, + ) -> NTSTATUS --- + + NtDelayExecution :: proc(Alertable: BOOL, DelayInterval: PLARGE_INTEGER) -> NTSTATUS --- + + ZwSetTimerResolution :: proc(RequestedResolution: ULONG, Set: BOOLEAN, ActualResolution: PULONG) -> NTSTATUS --- } diff --git a/core/sys/windows/ntioring_x.odin b/core/sys/windows/ntioring_x.odin new file mode 100644 index 000000000..18626069b --- /dev/null +++ b/core/sys/windows/ntioring_x.odin @@ -0,0 +1,42 @@ +#+build windows +package sys_windows + + +IORING_SUBMIT_WAIT_ALL :: max(u32) + +IORING_VERSION :: enum i32 { + INVALID, + _1, + _2, + _3 = 300, + _4 = 400, +} + +IORING_FEATURE_FLAG :: enum u32 { + UM_EMULATION = 0, // 0x00000001 + SET_COMPLETION_EVENT = 1, // 0x00000002 +} + +IORING_FEATURE_FLAGS :: bit_set[IORING_FEATURE_FLAG; u32] + +IORING_OP_CODE :: enum i32 { + NOP, + READ, + REGISTER_FILES, + REGISTER_BUFFERS, + CANCEL, + WRITE, + FLUSH, + READ_SCATTER, + WRITE_GATHER, +} + +IORING_BUFFER_INFO :: struct { + Address: rawptr, + Length: u32, +} + +IORING_REGISTERED_BUFFER :: struct { + BufferIndex: u32, + Offset: u32, +} diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin index 54ebd9481..0cde413f3 100644 --- a/core/sys/windows/types.odin +++ b/core/sys/windows/types.odin @@ -132,6 +132,7 @@ LPSTARTUPINFOW :: ^STARTUPINFOW LPTRACKMOUSEEVENT :: ^TRACKMOUSEEVENT VOID :: rawptr PVOID :: rawptr +PVOID64 :: rawptr LPVOID :: rawptr PINT :: ^INT LPINT :: ^INT diff --git a/core/testing/doc.odin b/core/testing/doc.odin index 85dc80cc3..73b907933 100644 --- a/core/testing/doc.odin +++ b/core/testing/doc.odin @@ -3,44 +3,42 @@ The implementation of the `odin test` runner and procedures user tests can use f Defineables through `#config`: -```odin -// Specify how many threads to use when running tests. -TEST_THREADS : int : #config(ODIN_TEST_THREADS, 0) -// Track the memory used by each test. -TRACKING_MEMORY : bool : #config(ODIN_TEST_TRACK_MEMORY, true) -// Always report how much memory is used, even when there are no leaks or bad frees. -ALWAYS_REPORT_MEMORY : bool : #config(ODIN_TEST_ALWAYS_REPORT_MEMORY, false) -// Treat memory leaks and bad frees as errors. -FAIL_ON_BAD_MEMORY : bool : #config(ODIN_TEST_FAIL_ON_BAD_MEMORY, false) -// Specify how much memory each thread allocator starts with. -PER_THREAD_MEMORY : int : #config(ODIN_TEST_THREAD_MEMORY, mem. ROLLBACK_STACK_DEFAULT_BLOCK_SIZE) -// Select a specific set of tests to run by name. -// Each test is separated by a comma and may optionally include the package name. -// This may be useful when running tests on multiple packages with `-all-packages`. -// The format is: `package.test_name,test_name_only,...` -TEST_NAMES : string : #config(ODIN_TEST_NAMES, "") -// Show the fancy animated progress report. -// This requires terminal color support, as well as STDOUT to not be redirected to a file. -FANCY_OUTPUT : bool : #config(ODIN_TEST_FANCY, true) -// Copy failed tests to the clipboard when done. -USE_CLIPBOARD : bool : #config(ODIN_TEST_CLIPBOARD, false) -// How many test results to show at a time per package. -PROGRESS_WIDTH : int : #config(ODIN_TEST_PROGRESS_WIDTH, 24) -// This is the random seed that will be sent to each test. -// If it is unspecified, it will be set to the system cycle counter at startup. -SHARED_RANDOM_SEED : u64 : #config(ODIN_TEST_RANDOM_SEED, 0) -// Set the lowest log level for this test run. -LOG_LEVEL_DEFAULT : string : "debug" when ODIN_DEBUG else "info" -LOG_LEVEL : string : #config(ODIN_TEST_LOG_LEVEL, LOG_LEVEL_DEFAULT) -// Report a message at the info level when a test has changed its state. -LOG_STATE_CHANGES : bool : #config(ODIN_TEST_LOG_STATE_CHANGES, false) -// Show only the most necessary logging information. -USING_SHORT_LOGS : bool : #config(ODIN_TEST_SHORT_LOGS, false) -// Output a report of the tests to the given path. -JSON_REPORT : string : #config(ODIN_TEST_JSON_REPORT, "") -// Print the full file path for failed test cases on a new line -// in a way that's friendly to regex capture for an editor's "go to error". -GO_TO_ERROR : bool : #config(ODIN_TEST_GO_TO_ERROR, false) -``` + // Specify how many threads to use when running tests. + TEST_THREADS : int : #config(ODIN_TEST_THREADS, 0) + // Track the memory used by each test. + TRACKING_MEMORY : bool : #config(ODIN_TEST_TRACK_MEMORY, true) + // Always report how much memory is used, even when there are no leaks or bad frees. + ALWAYS_REPORT_MEMORY : bool : #config(ODIN_TEST_ALWAYS_REPORT_MEMORY, false) + // Treat memory leaks and bad frees as errors. + FAIL_ON_BAD_MEMORY : bool : #config(ODIN_TEST_FAIL_ON_BAD_MEMORY, false) + // Specify how much memory each thread allocator starts with. + PER_THREAD_MEMORY : int : #config(ODIN_TEST_THREAD_MEMORY, mem. ROLLBACK_STACK_DEFAULT_BLOCK_SIZE) + // Select a specific set of tests to run by name. + // Each test is separated by a comma and may optionally include the package name. + // This may be useful when running tests on multiple packages with `-all-packages`. + // The format is: `package.test_name,test_name_only,...` + TEST_NAMES : string : #config(ODIN_TEST_NAMES, "") + // Show the fancy animated progress report. + // This requires terminal color support, as well as STDOUT to not be redirected to a file. + FANCY_OUTPUT : bool : #config(ODIN_TEST_FANCY, true) + // Copy failed tests to the clipboard when done. + USE_CLIPBOARD : bool : #config(ODIN_TEST_CLIPBOARD, false) + // How many test results to show at a time per package. + PROGRESS_WIDTH : int : #config(ODIN_TEST_PROGRESS_WIDTH, 24) + // This is the random seed that will be sent to each test. + // If it is unspecified, it will be set to the system cycle counter at startup. + SHARED_RANDOM_SEED : u64 : #config(ODIN_TEST_RANDOM_SEED, 0) + // Set the lowest log level for this test run. + LOG_LEVEL_DEFAULT : string : "debug" when ODIN_DEBUG else "info" + LOG_LEVEL : string : #config(ODIN_TEST_LOG_LEVEL, LOG_LEVEL_DEFAULT) + // Report a message at the info level when a test has changed its state. + LOG_STATE_CHANGES : bool : #config(ODIN_TEST_LOG_STATE_CHANGES, false) + // Show only the most necessary logging information. + USING_SHORT_LOGS : bool : #config(ODIN_TEST_SHORT_LOGS, false) + // Output a report of the tests to the given path. + JSON_REPORT : string : #config(ODIN_TEST_JSON_REPORT, "") + // Print the full file path for failed test cases on a new line + // in a way that's friendly to regex capture for an editor's "go to error". + GO_TO_ERROR : bool : #config(ODIN_TEST_GO_TO_ERROR, false) */ -package testing \ No newline at end of file +package testing diff --git a/core/testing/signal_handler_windows.odin b/core/testing/signal_handler_windows.odin index 8843dde92..f9d0f85a7 100644 --- a/core/testing/signal_handler_windows.odin +++ b/core/testing/signal_handler_windows.odin @@ -134,7 +134,7 @@ This is a dire bug and should be reported to the Odin developers. } signal := local_test_expected_failures.signal switch signal { - case libc.SIGILL: passed = code == win32.EXCEPTION_ILLEGAL_INSTRUCTION + case libc.SIGILL: passed = code == win32.EXCEPTION_ILLEGAL_INSTRUCTION || code == win32.EXCEPTION_ARRAY_BOUNDS_EXCEEDED case libc.SIGSEGV: passed = code == win32.EXCEPTION_ACCESS_VIOLATION case libc.SIGFPE: switch code { @@ -163,6 +163,7 @@ _setup_signal_handler :: proc() { // For tests: // Catch the following: // - Asserts and panics; + // - Out of Bounds exeptions; // - Arithmetic errors; and // - Segmentation faults (illegal memory access). win32.AddVectoredExceptionHandler(0, stop_test_callback) @@ -194,10 +195,11 @@ _should_stop_test :: proc() -> (test_index: int, reason: Stop_Reason, ok: bool) reason = .Successful_Stop } else { switch intrinsics.atomic_load(&stop_test_signal) { - case win32.EXCEPTION_ILLEGAL_INSTRUCTION: reason = .Illegal_Instruction - case win32.EXCEPTION_ACCESS_VIOLATION: reason = .Segmentation_Fault - case win32.EXCEPTION_BREAKPOINT: reason = .Unhandled_Trap - case win32.EXCEPTION_SINGLE_STEP: reason = .Unhandled_Trap + case win32.EXCEPTION_ARRAY_BOUNDS_EXCEEDED: reason = .Illegal_Instruction + case win32.EXCEPTION_ILLEGAL_INSTRUCTION: reason = .Illegal_Instruction + case win32.EXCEPTION_ACCESS_VIOLATION: reason = .Segmentation_Fault + case win32.EXCEPTION_BREAKPOINT: reason = .Unhandled_Trap + case win32.EXCEPTION_SINGLE_STEP: reason = .Unhandled_Trap case win32.EXCEPTION_FLT_DENORMAL_OPERAND ..= win32.EXCEPTION_INT_OVERFLOW: reason = .Arithmetic_Error diff --git a/examples/all/all_js.odin b/examples/all/all_js.odin index 3e45565e0..74cdb5fd3 100644 --- a/examples/all/all_js.odin +++ b/examples/all/all_js.odin @@ -43,6 +43,7 @@ package all @(require) import "core:crypto/legacy/keccak" @(require) import "core:crypto/legacy/md5" @(require) import "core:crypto/legacy/sha1" +@(require) import cnoise "core:crypto/noise" @(require) import "core:crypto/pbkdf2" @(require) import "core:crypto/poly1305" @(require) import "core:crypto/ristretto255" diff --git a/examples/all/all_main.odin b/examples/all/all_main.odin index 65e4b917c..973ee423e 100644 --- a/examples/all/all_main.odin +++ b/examples/all/all_main.odin @@ -48,6 +48,7 @@ package all @(require) import "core:crypto/legacy/keccak" @(require) import "core:crypto/legacy/md5" @(require) import "core:crypto/legacy/sha1" +@(require) import cnoise "core:crypto/noise" @(require) import "core:crypto/pbkdf2" @(require) import "core:crypto/poly1305" @(require) import "core:crypto/ristretto255" diff --git a/src/build_settings.cpp b/src/build_settings.cpp index c12107bf7..c327230f3 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -578,7 +578,7 @@ struct BuildContext { bool internal_by_value; bool internal_weak_monomorphization; bool internal_ignore_llvm_verification; - bool internal_llvm_mem2reg; + bool internal_llvm_no_sroa; bool enable_rvo; @@ -2215,18 +2215,18 @@ gb_internal bool check_target_feature_is_enabled(String const &feature, String * } if (feature_str == "") break; - if (!string_set_exists(&build_context.target_features_set, str)) { - String plus_str = concatenate_strings(temporary_allocator(), make_string_c("+"), feature_str); - - if (want_enabled && !string_set_exists(&build_context.target_features_set, plus_str)) { - if (not_enabled) *not_enabled = str; - return false; - } - } - + String plus_str = concatenate_strings(temporary_allocator(), make_string_c("+"), feature_str); String minus_str = concatenate_strings(temporary_allocator(), make_string_c("-"), feature_str); - - if (!want_enabled && !string_set_exists(&build_context.target_features_set, minus_str)) { + + bool has_raw = string_set_exists(&build_context.target_features_set, feature_str); + bool has_plus = string_set_exists(&build_context.target_features_set, plus_str); + bool has_minus = string_set_exists(&build_context.target_features_set, minus_str); + + // NOTE(jakubtomsu): this way "feature" and "+feature" is ALWAYS equivalent, + // and also allows the minus sign to do a final override. + bool is_enabled = (has_plus || has_raw) && !has_minus; + + if (want_enabled != is_enabled) { if (not_enabled) *not_enabled = str; return false; } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 5d740c6d2..d6987a332 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1130,8 +1130,8 @@ gb_internal void check_unroll_range_stmt(CheckerContext *ctx, Ast *node, u32 mod } } - check_stmt(ctx, irs->body, mod_flags); - + u32 new_flags = mod_flags & ~Stmt_BreakAllowed & ~Stmt_ContinueAllowed; + check_stmt(ctx, irs->body, new_flags); } gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { diff --git a/src/check_type.cpp b/src/check_type.cpp index 137cdb96f..a0a4bc1f2 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2840,6 +2840,12 @@ 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/common.cpp b/src/common.cpp index 86ebb0fa8..89964309b 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -9,7 +9,9 @@ #if defined(GB_SYSTEM_WINDOWS) -#define NOMINMAX 1 +#define NOMINMAX 1 +#define WINDOWS_LEAN_AND_MEAN 1 +#define VC_EXTRALEAN 1 #include #undef NOMINMAX #endif diff --git a/src/common_memory.cpp b/src/common_memory.cpp index 38c316733..c8fe7c3d7 100644 --- a/src/common_memory.cpp +++ b/src/common_memory.cpp @@ -2,6 +2,25 @@ #include #endif +#ifdef __ANDROID__ + +// Bionic may not provide aligned_alloc. +// Provide a fallback using posix_memalign and ensure the alignment +// satisfies its requirement (multiple of pointer size). +static void* android_aligned_alloc(size_t align, size_t size) { + void* allocated; + + const size_t ptr_size = sizeof(void*); + align = (align + (ptr_size - 1)) / ptr_size * ptr_size; + + if (posix_memalign(&allocated, align, size) != 0) + return NULL; + return allocated; +} +#define aligned_alloc android_aligned_alloc + +#endif + template gb_internal gb_inline U bit_cast(V &v) { return reinterpret_cast(v); } diff --git a/src/docs.cpp b/src/docs.cpp index de7bdafd1..1d8886edb 100644 --- a/src/docs.cpp +++ b/src/docs.cpp @@ -289,7 +289,7 @@ gb_internal void print_doc_package(CheckerInfo *info, AstPackage *pkg) { } curr_file = e->file; String filename = remove_directory_from_path(curr_file->fullpath); - print_doc_line(1, "file: %s", filename); + print_doc_line(1, "file: %.*s", LIT(filename)); } } else { if (curr_entity_kind != e->kind) { diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index d691cdb39..8bb1b057a 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -4680,6 +4680,8 @@ gb_internal lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) { } } lbValue val = lb_emit_ptr_offset(p, field, index); + // make sure it's ^T and not [^]T + val.type = alloc_type_multi_pointer_to_pointer(val.type); return lb_addr(val); } @@ -6092,8 +6094,8 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) { if (sub_sel.index.count > 0) { item = lb_emit_deep_field_gep(p, item, sub_sel); } - // make sure it's ^T and not [^]T - item.type = alloc_type_multi_pointer_to_pointer(item.type); + // make sure it's ^T and not [^]T + item.type = alloc_type_multi_pointer_to_pointer(item.type); return lb_addr(item); } else if (addr.kind == lbAddr_Swizzle) { diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 8feadf6a4..8068670f0 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -701,13 +701,20 @@ gb_internal void lb_set_file_line_col(lbProcedure *p, Array arr, TokenP arr[2] = lb_const_int(p->module, t_i32, col); } -gb_internal bool lb_bounds_check_short_circuit(lbProcedure *p, lbValue index, lbValue len) { +gb_internal bool lb_bounds_check_disabled(lbProcedure *p) { if (build_context.no_bounds_check) { return true; } if ((p->state_flags & StateFlag_no_bounds_check) != 0) { return true; } + return false; +} + +gb_internal bool lb_bounds_check_short_circuit(lbProcedure *p, lbValue index, lbValue len) { + if (lb_bounds_check_disabled(p)) { + return true; + } if (LLVMIsConstant(index.value) && LLVMIsConstant(len.value)) { i64 i = LLVMConstIntGetSExtValue(index.value); @@ -757,10 +764,7 @@ gb_internal void lb_emit_bounds_check(lbProcedure *p, Token token, lbValue index } gb_internal void lb_emit_matrix_bounds_check(lbProcedure *p, Token token, lbValue row_index, lbValue column_index, lbValue row_count, lbValue column_count) { - if (build_context.no_bounds_check) { - return; - } - if ((p->state_flags & StateFlag_no_bounds_check) != 0) { + if (lb_bounds_check_disabled(p)) { return; } @@ -783,10 +787,7 @@ gb_internal void lb_emit_matrix_bounds_check(lbProcedure *p, Token token, lbValu gb_internal void lb_emit_multi_pointer_slice_bounds_check(lbProcedure *p, Token token, lbValue low, lbValue high) { - if (build_context.no_bounds_check) { - return; - } - if ((p->state_flags & StateFlag_no_bounds_check) != 0) { + if (lb_bounds_check_disabled(p)) { return; } @@ -811,6 +812,9 @@ gb_internal void lb_emit_multi_pointer_slice_bounds_check(lbProcedure *p, Token } gb_internal void lb_emit_slice_bounds_check(lbProcedure *p, Token token, lbValue low, lbValue high, lbValue len, bool lower_value_used) { + if (lb_bounds_check_disabled(p)) { + return; + } if (!lower_value_used && lb_bounds_check_short_circuit(p, high, len)) { return; } diff --git a/src/llvm_backend_passes.cpp b/src/llvm_backend_passes.cpp index 5001e29b0..50d450c52 100644 --- a/src/llvm_backend_passes.cpp +++ b/src/llvm_backend_passes.cpp @@ -3,9 +3,17 @@ array_add(&passes, "function(annotation-remarks)"); break; case 0: - array_add(&passes, "always-inline"); - if (build_context.internal_llvm_mem2reg) { - array_add(&passes, "function(mem2reg)"); + if (build_context.internal_llvm_no_sroa) { + // Old -o:minimal behavior + array_add(&passes, "always-inline"); + } else { + array_add(&passes, "annotation2metadata"); + array_add(&passes, "inferattrs"); + array_add(&passes, "forceattrs"); + array_add(&passes, "function(sroa,early-cse<>)"); + array_add(&passes, "always-inline"); + array_add(&passes, "function(sroa,instsimplify,simplifycfg)"); + // array_add(&passes, "verify"); } array_add(&passes, "function(annotation-remarks)"); break; diff --git a/src/main.cpp b/src/main.cpp index 18381f5ee..53735704d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -402,7 +402,7 @@ enum BuildFlagKind { BuildFlag_InternalByValue, BuildFlag_InternalWeakMonomorphization, BuildFlag_InternalLLVMVerification, - BuildFlag_InternalLLVMMem2Reg, + BuildFlag_InternalLLVMNoSROA, BuildFlag_InternalEnableRVO, BuildFlag_Sanitize, @@ -634,7 +634,7 @@ gb_internal bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_InternalByValue, str_lit("internal-by-value"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalWeakMonomorphization, str_lit("internal-weak-monomorphization"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalLLVMVerification, str_lit("internal-ignore-llvm-verification"), BuildFlagParam_None, Command_all); - add_flag(&build_flags, BuildFlag_InternalLLVMMem2Reg, str_lit("internal-llvm-mem2reg"), BuildFlagParam_None, Command_all); + add_flag(&build_flags, BuildFlag_InternalLLVMNoSROA, str_lit("internal-llvm-no-sroa"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalEnableRVO, str_lit("internal-enable-rvo"), BuildFlagParam_None, Command_all); @@ -1628,8 +1628,8 @@ gb_internal bool parse_build_flags(Array args) { case BuildFlag_InternalLLVMVerification: build_context.internal_ignore_llvm_verification = true; break; - case BuildFlag_InternalLLVMMem2Reg: - build_context.internal_llvm_mem2reg = true; + case BuildFlag_InternalLLVMNoSROA: + build_context.internal_llvm_no_sroa = true; break; case BuildFlag_InternalEnableRVO: build_context.enable_rvo = true; @@ -3973,15 +3973,16 @@ int main(int arg_count, char const **arg_ptr) { for (;;) { String item = string_split_iterator(&target_it, ','); if (item == "") break; - - if (*item.text == '+' || *item.text == '-') { - item.text++; - item.len--; + + String stripped_item = item; + if (*stripped_item.text == '+' || *stripped_item.text == '-') { + stripped_item.text++; + stripped_item.len--; } String invalid; - if (!check_target_feature_is_valid_for_target_arch(item, &invalid) && item != str_lit("help")) { - if (item != str_lit("?")) { + if (!check_target_feature_is_valid_for_target_arch(stripped_item, &invalid) && stripped_item != str_lit("help")) { + if (stripped_item != str_lit("?")) { gb_printf_err("Unkown target feature '%.*s'.\n", LIT(invalid)); } gb_printf("Possible -target-features for target %.*s are:\n", LIT(target_arch_names[build_context.metrics.arch])); @@ -4005,8 +4006,25 @@ int main(int arg_count, char const **arg_ptr) { return 1; } - - string_set_add(&build_context.target_features_set, item); + + // Ensure the feature name always has +/- prefix. If there isn't, default to '+' + String feature_str = item; + if (*feature_str.text != '+' && *feature_str.text != '-') { + feature_str = concatenate_strings(temporary_allocator(), make_string_c("+"), feature_str); + } + + // Ensure there is only a single entry for each feature in the target set. + // If the negative exists, override the existing value with the current one. + String neg_feature_str = clone_string(temporary_allocator(), feature_str); + switch (*neg_feature_str.text) { + case '+': *neg_feature_str.text = '-'; break; + case '-': *neg_feature_str.text = '+'; break; + default: GB_ASSERT(false); break; + } + + string_set_remove(&build_context.target_features_set, neg_feature_str); + + string_set_add(&build_context.target_features_set, feature_str); } } diff --git a/src/string.cpp b/src/string.cpp index a4fa85871..977327bcd 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -508,6 +508,13 @@ gb_internal String filename_without_directory(String s) { return substring(s, gb_max(j+1, 0), s.len); } +gb_internal String clone_string(gbAllocator a, String const &x) { + u8 *data = gb_alloc_array(a, u8, x.len+1); + gb_memmove(data, x.text, x.len); + data[x.len] = 0; + return make_string(data, x.len); +} + gb_internal String concatenate_strings(gbAllocator a, String const &x, String const &y) { isize len = x.len+y.len; u8 *data = gb_alloc_array(a, u8, len+1); diff --git a/tests/core/assets/Noise/.gitignore b/tests/core/assets/Noise/.gitignore new file mode 100644 index 000000000..2211df63d --- /dev/null +++ b/tests/core/assets/Noise/.gitignore @@ -0,0 +1 @@ +*.txt diff --git a/tests/core/crypto/common/common.odin b/tests/core/crypto/common/common.odin new file mode 100644 index 000000000..8cef8ce86 --- /dev/null +++ b/tests/core/crypto/common/common.odin @@ -0,0 +1,25 @@ +package test_crypto_common + +import "core:bytes" +import "core:encoding/hex" + +// Common helpers for cryptography tests. + +Hex_Bytes :: string + +hexbytes_compare :: proc(x: Hex_Bytes, b: []byte, allocator := context.allocator) -> bool { + dst := hexbytes_decode(x) + defer delete(dst) + + return bytes.equal(dst, b) +} + +hexbytes_decode :: proc(x: Hex_Bytes, allocator := context.allocator) -> []byte { + dst, ok := hex.decode(transmute([]byte)(x), allocator) + if !ok { + panic("Hex_Bytes: invalid hex encoding") + } + + return dst +} + diff --git a/tests/core/crypto/noise/main.odin b/tests/core/crypto/noise/main.odin new file mode 100644 index 000000000..e97dbfe1e --- /dev/null +++ b/tests/core/crypto/noise/main.odin @@ -0,0 +1,440 @@ +package test_noise + +import "core:crypto/ecdh" +import "core:crypto/noise" +import "core:log" +import "core:mem" +import "core:os" +import "core:strings" +import "core:testing" + +import "../common" + +ARENA_SIZE :: 8 * 1024 * 1024 + +BASE_PATH :: ODIN_ROOT + "tests/core/assets/Noise" + +@(test) +print_test_vector_path :: proc(t: ^testing.T) { + log.infof("noise path: %s", BASE_PATH) +} + +@(test) +test_vectors_snow :: proc(t: ^testing.T) { + arena: mem.Arena + arena_backing := make([]byte, ARENA_SIZE) + defer delete(arena_backing) + mem.arena_init(&arena, arena_backing) + context.allocator = mem.arena_allocator(&arena) + + log.debug("noise/snow: starting") + + F :: "snow.txt" + fn, _ := os.join_path([]string{BASE_PATH, F}, context.allocator) + defer delete(fn) + test_vectors: Test_Vectors + testing.expectf(t, load(&test_vectors, fn), "unable to load {}", fn) + + run_test_vectors(t, F, &test_vectors) +} + +@(test) +test_vectors_noise_c_basic :: proc(t: ^testing.T) { + arena: mem.Arena + arena_backing := make([]byte, ARENA_SIZE) + defer delete(arena_backing) + mem.arena_init(&arena, arena_backing) + context.allocator = mem.arena_allocator(&arena) + + log.debug("noise/noise-c-basic: starting") + + F :: "noise-c-basic.txt" + fn, _ := os.join_path([]string{BASE_PATH, F}, context.allocator) + defer delete(fn) + test_vectors: Test_Vectors + testing.expectf(t, load(&test_vectors, fn), "unable to load {}", fn) + + run_test_vectors(t, F, &test_vectors) +} + +@(test) +test_vectors_cacophony :: proc(t: ^testing.T) { + arena: mem.Arena + arena_backing := make([]byte, ARENA_SIZE) + defer delete(arena_backing) + mem.arena_init(&arena, arena_backing) + context.allocator = mem.arena_allocator(&arena) + + log.debug("noise/cacophony: starting") + + F :: "cacophony.txt" + fn, _ := os.join_path([]string{BASE_PATH, F}, context.allocator) + defer delete(fn) + test_vectors: Test_Vectors + testing.expectf(t, load(&test_vectors, fn), "unable to load {}", fn) + + run_test_vectors(t, F, &test_vectors) +} + +run_test_vectors :: proc(t: ^testing.T, f: string, tvs: ^Test_Vectors) { + num_ran, num_passed, num_failed, num_skipped: int + tv_loop: for &v, i in tvs.vectors { + num_ran += 1 + + protocol_name: string + switch { + case v.protocol_name != "": + protocol_name = v.protocol_name + case v.name != "": + // Old test vector format used by the C impl. + v.protocol_name = v.name + protocol_name = v.name + } + + // Skip unsupported test vectors + if v.fail { + num_skipped += 1 + log.debugf("%s[%d]: %s - skipped, fail tests not supported", f, i, protocol_name) + continue + } + if v.fallback { + num_skipped += 1 + log.debugf("%s[%d]: %s - skipped, fallback patterns not supported", f, i, protocol_name) + continue + } + if strings.has_prefix(protocol_name, "NoisePSK") { + num_skipped += 1 + log.debugf("%s[%d]: %s - skipped, Old PSK not supported", f, i, protocol_name) + continue + } + if len(v.init_psks) > 1 || len(v.resp_psks) > 1 { + num_skipped += 1 + log.debugf("%s[%d]: %s - skipped, Multi-PSK not supported", f, i, protocol_name) + continue + } + + // Initialize Handshake_Statuses + pattern, dh, _, _, status := noise.split_protocol_string(protocol_name) + if !testing.expectf(t, status == .Ok, "%s[%d]: failed to parse protocol '%s': %v", f, i, protocol_name, status) { + num_failed += 1 + continue + } + ini_hs, res_hs: noise.Handshake_State + if !testing.expect(t, handshake_states_from_tv(t, &ini_hs, &res_hs, &v, dh, f, i)) { + num_failed += 1 + continue + } + defer noise.handshake_reset(&ini_hs) + defer noise.handshake_reset(&res_hs) + + // Play back the messages + if !testing.expectf( + t, + replay_messages_from_tv_rw(t, &ini_hs, &v, pattern, f), + "%s[%d]: %s - failed to playback messages (initiator)", f, i, protocol_name, + ) { + num_failed += 1 + continue + } + if !testing.expectf( + t, + replay_messages_from_tv_rw(t, &res_hs, &v, pattern, f), + "%s[%d]: %s - failed to playback messages (responder)", f, i, protocol_name, + ) { + num_failed += 1 + continue + } + + // Check handshake hash/peer identities. + if v.handshake_hash != "" { + ini_hash, _ := noise.handshake_hash(&ini_hs) + res_hash, _ := noise.handshake_hash(&res_hs) + if !testing.expectf( + t, + common.hexbytes_compare(v.handshake_hash, ini_hash), + "%s[%d]: %s - invalid initiator handshake hash: %x expected: %s", f, i, protocol_name, ini_hash, v.handshake_hash, + ) { + num_failed += 1 + continue + } + if !testing.expectf( + t, + common.hexbytes_compare(v.handshake_hash, res_hash), + "%s[%d]: %s - invalid responder handshake hash: %x expected: %s", f, i, protocol_name, res_hash, v.handshake_hash, + ) { + num_failed += 1 + continue + } + } + if ecdh.curve(&ini_hs.s) != .Invalid { + pub_key, _ := noise.handshake_peer_identity(&res_hs) + if !testing.expectf( + t, + pub_key != nil && ecdh.public_key_equal(&ini_hs.s._pub_key, pub_key), + "%s[%d]: %s - invalid initiator static public key known by responder", f, i, protocol_name, + ) { + num_failed += 1 + continue + } + } + if ecdh.curve(&res_hs.s) != .Invalid { + pub_key, _ := noise.handshake_peer_identity(&ini_hs) + if !testing.expectf( + t, + pub_key != nil && ecdh.public_key_equal(&res_hs.s._pub_key, pub_key), + "%s[%d]: %s - invalid responder static public key known by initiator", f, i, protocol_name, + ) { + num_failed += 1 + continue + } + } + + log.debugf("%s[%d]: %s - Passed", f, i, protocol_name) + num_passed += 1 + } + + assert(num_ran == len(tvs.vectors)) + assert(num_passed + num_failed + num_skipped == num_ran) + + log.infof( + "%s: ran %d, passed %d, failed %d, skipped %d", + f, + num_ran, + num_passed, + num_failed, + num_skipped, + ) +} + +handshake_states_from_tv :: proc( + t: ^testing.T, + ini_hs, res_hs: ^noise.Handshake_State, + v: ^Vector, + dh: ecdh.Curve, + f: string, + i: int, +) -> bool { + protocol_name := v.protocol_name + + ini_static, ini_ephemeral: ecdh.Private_Key + res_static, res_ephemeral: ecdh.Private_Key + ini_res_static, res_ini_static: ecdh.Public_Key + + ini_s, ini_e: ^ecdh.Private_Key + ini_s_p, ini_e_p: ^ecdh.Public_Key + ini_r_s: ^ecdh.Public_Key + if len(v.init_static) != 0 { + if !testing.expectf( + t, + ecdh.private_key_set_bytes(&ini_static, dh, common.hexbytes_decode(v.init_static)), + "%s[%d]: %s - failed to parse init_static", f, i, protocol_name, + ) { + return false + } + ini_s = &ini_static + ini_s_p = &ini_s._pub_key + } + if len(v.init_ephemeral) != 0 { + if !testing.expectf( + t, + ecdh.private_key_set_bytes(&ini_ephemeral, dh, common.hexbytes_decode(v.init_ephemeral)), + "%s[%d]: %s - failed to parse init_ephemeral", f, i, protocol_name, + ) { + return false + } + ini_e = &ini_ephemeral + ini_e_p = &ini_e._pub_key + } + if len(v.init_remote_static) != 0 { + if !testing.expectf( + t, + ecdh.public_key_set_bytes(&ini_res_static, dh, common.hexbytes_decode(v.init_remote_static)), + "%s[%d]: %s - failed to parse init_remote_static", f, i, protocol_name, + ) { + return false + } + ini_r_s = &ini_res_static + } + + res_s, res_e: ^ecdh.Private_Key + res_s_p, res_e_p: ^ecdh.Public_Key + res_i_s: ^ecdh.Public_Key + if len(v.resp_static) != 0 { + if !testing.expectf( + t, + ecdh.private_key_set_bytes(&res_static, dh, common.hexbytes_decode(v.resp_static)), + "%s[%d]: %s - failed to parse resp_static", f, i, protocol_name, + ) { + return false + } + res_s = &res_static + res_s_p = &res_s._pub_key + } + if len(v.resp_ephemeral) != 0 { + if !testing.expectf( + t, + ecdh.private_key_set_bytes(&res_ephemeral, dh, common.hexbytes_decode(v.resp_ephemeral)), + "%s[%d]: %s - failed to parse resp_ephemeral", f, i, protocol_name, + ) { + return false + } + res_e = &res_ephemeral + res_e_p = &res_e._pub_key + } + if len(v.resp_remote_static) != 0 { + if !testing.expectf( + t, + ecdh.public_key_set_bytes(&res_ini_static, dh, common.hexbytes_decode(v.resp_remote_static)), + "%s[%d]: %s - failed to parse remote_init_static", f, i, protocol_name, + ) { + return false + } + res_i_s = &res_ini_static + } + + ini_psk, res_psk: []byte + if len(v.init_psks) > 0 { + ini_psk = common.hexbytes_decode(v.init_psks[0]) + } + if len(v.resp_psks) > 0 { + res_psk = common.hexbytes_decode(v.resp_psks[0]) + } + + status := noise.handshake_init( + ini_hs, + true, + common.hexbytes_decode(v.init_prologue), + ini_s, + ini_r_s, + protocol_name, + ini_psk, + ini_e, + ) + if !testing.expectf( + t, + status == .Ok, + "%s[%d]: %s - failed to initialize ini_hs: %v", f, i, protocol_name, status, + ) { + return false + } + + status = noise.handshake_init( + res_hs, + false, + common.hexbytes_decode(v.resp_prologue), + res_s, + res_i_s, + protocol_name, + res_psk, + res_e, + ) + if !testing.expectf( + t, + status == .Ok, + "%s[%d]: %s - failed to initialize res_hs: %v", f, i, protocol_name, status, + ) { + return false + } + + return true +} + +replay_messages_from_tv_rw :: proc( + t: ^testing.T, + hs: ^noise.Handshake_State, + v: ^Vector, + pattern: noise.Handshake_Pattern, + f: string, +) -> bool { + protocol_name := v.protocol_name + pattern_is_one_way := noise.pattern_is_one_way(pattern) + is_initiator := hs.initiator + + cs: noise.Cipher_States + + defer noise.cipherstates_reset(&cs) + + hs_done: bool + for &msg, i in &v.messages { + dst: []byte + status: noise.Status + expected: common.Hex_Bytes + + switch hs_done { + case false: + if (i & 1 == 0) == is_initiator { + dst, status = noise.handshake_write_message(hs, common.hexbytes_decode(msg.payload)) + expected = msg.ciphertext + } else { + dst, status = noise.handshake_read_message(hs, common.hexbytes_decode(msg.ciphertext)) + expected = msg.payload + } + defer delete(dst) + + if !testing.expectf( + t, + status == .Handshake_Pending || status == .Handshake_Complete, + "%s: %s[%d] - unexpected handshake status: %v", f, protocol_name, i, status, + ) { + return false + } + if !testing.expectf( + t, + common.hexbytes_compare(expected, dst), + "%s: %s[%d] - unexpected message/payload: %x expected: %s", f, protocol_name, i, dst, expected, + ) { + return false + } + if status == .Handshake_Complete { + status = noise.handshake_split(hs, &cs) + if !testing.expectf( + t, + status == .Ok, + "%s: %s[%d] - handshake_split failed: %v", f, protocol_name, i, status, + ) { + return false + } + hs_done = true + } + case true: + // The messages that use the derived cipherstates just follow the + // handshake message(s), and the flow continues. + if pattern_is_one_way { + // Except one-way patterns which go from initiator to responder. + if is_initiator { + dst, status = noise.seal_message(&cs, nil, common.hexbytes_decode(msg.payload)) + expected = msg.ciphertext + } else { + dst, status = noise.open_message(&cs, nil, common.hexbytes_decode(msg.ciphertext)) + expected = msg.payload + } + } else { + if (i & 1 == 0) == is_initiator { + dst, status = noise.seal_message(&cs, nil, common.hexbytes_decode(msg.payload)) + expected = msg.ciphertext + } else { + dst, status = noise.open_message(&cs, nil, common.hexbytes_decode(msg.ciphertext)) + expected = msg.payload + } + } + defer delete(dst) + + if !testing.expectf( + t, + status == .Ok, + "%s: %s[%d] - seal/open failed: %v", f, protocol_name, i, status, + ) { + return false + } + if !testing.expectf( + t, + common.hexbytes_compare(expected, dst), + "%s: %s[%d] - unexpected ciphertext/plaintext: %x expected: %s", f, protocol_name, i, dst, expected, + ) { + return false + } + } + } + + return true +} diff --git a/tests/core/crypto/noise/schemas.odin b/tests/core/crypto/noise/schemas.odin new file mode 100644 index 000000000..9eb6bdbd6 --- /dev/null +++ b/tests/core/crypto/noise/schemas.odin @@ -0,0 +1,56 @@ +package test_noise + +import "core:encoding/json" +import "core:log" +import "core:os" + +import "../common" + +Message :: struct { + payload: common.Hex_Bytes `json:"payload"`, + ciphertext: common.Hex_Bytes `json:"ciphertext"`, +} + +Vector :: struct { + name: string `json:"name"`, + + protocol_name: string `json:"protocol_name"`, + fail: bool `json:"fail"`, + fallback: bool `json:"fallback"`, + fallback_pattern: string `json:"fallback_pattern"`, + + init_prologue: common.Hex_Bytes `json:"init_prologue"`, + init_psks: []common.Hex_Bytes `json:"init_psks"`, + init_static: common.Hex_Bytes `json:"init_static"`, + init_ephemeral: common.Hex_Bytes `json:"init_ephemeral"`, + init_remote_static: common.Hex_Bytes `json:"init_remote_static"`, + + resp_prologue: common.Hex_Bytes `json:"resp_prologue"`, + resp_psks: []common.Hex_Bytes `json:"resp_psks"`, + resp_static: common.Hex_Bytes `json:"resp_static"`, + resp_ephemeral: common.Hex_Bytes `json:"resp_ephemeral"`, + resp_remote_static: common.Hex_Bytes `json:"resp_remote_static"`, + + handshake_hash: common.Hex_Bytes `json:"handshake_hash"`, + + messages: []Message `json:"messages"`, +} + +Test_Vectors :: struct { + vectors: []Vector `json:"vectors"`, +} + +load :: proc(tvs: ^Test_Vectors, fn: string) -> bool { + raw_json, err := os.read_entire_file_from_path(fn, context.allocator) + if err != os.ERROR_NONE { + log.error("failed to load raw JSON") + return false + } + + if err := json.unmarshal(raw_json, tvs); err != nil { + log.errorf("failed to parse JSON: %v", err) + return false + } + + return true +} diff --git a/tests/core/crypto/test_core_crypto_noise.odin b/tests/core/crypto/test_core_crypto_noise.odin new file mode 100644 index 000000000..6371900ca --- /dev/null +++ b/tests/core/crypto/test_core_crypto_noise.odin @@ -0,0 +1,306 @@ +package test_core_crypto + +import "core:bytes" +import "core:crypto" +import "core:crypto/aead" +import "core:crypto/ecdh" +import "core:crypto/hash" +import "core:crypto/noise" +import "core:fmt" +import "core:log" +import "core:math/rand" +import "core:testing" + +@(private = "file") +DH_CURVES :: []ecdh.Curve { + .X25519, + .X448, +} +@(private = "file") +CIPHERS :: []aead.Algorithm{ + .AES_GCM_256, + .CHACHA20POLY1305, +} +@(private = "file") +HASHES :: []hash.Algorithm{ + .SHA256, + .SHA512, + .BLAKE2S, + .BLAKE2B, +} + +@(test) +test_supported_protocols :: proc(t: ^testing.T) { + if !crypto.HAS_RAND_BYTES { + log.info("rand_bytes not supported - skipping") + return + } + + protocol: Test_Protocol + for pattern in noise.Handshake_Pattern { + if pattern == .Invalid { + continue + } + protocol.handshake_pattern = pattern + for dh in DH_CURVES { + protocol.dh = dh + for cipher in CIPHERS { + protocol.cipher = cipher + for hash in HASHES { + protocol.hash = hash + if !testing.expectf( + t, + test_noise_one_protocol(t, &protocol, context.temp_allocator), + "Failed protocol: %v", protocol, + ) { + testing.fail(t) + break + } + } + } + } + } +} + +@(private = "file") +test_noise_one_protocol :: proc(t: ^testing.T, protocol: ^Test_Protocol, allocator := context.allocator) -> bool { + protocol_name := test_protocol_string(protocol, allocator) + defer delete(protocol_name, allocator) + + log.debugf("crypto/noise: %s", protocol_name) + + is_one_way := noise.pattern_is_one_way(protocol.handshake_pattern) + + initiator_s, responder_s: ecdh.Private_Key + ini_s, res_s: ^ecdh.Private_Key + ini_s_pub, res_s_pub: ^ecdh.Public_Key + + pre, hs := noise.pattern_requires_initiator_s(protocol.handshake_pattern) + if pre || hs { + if !testing.expect(t, ecdh.private_key_generate(&initiator_s, protocol.dh), "failed to generate initiator s") { + return false + } + ini_s = &initiator_s + if pre { + ini_s_pub = &initiator_s._pub_key + } + } + pre, hs = noise.pattern_requires_responder_s(protocol.handshake_pattern) + if pre || hs { + if !testing.expect(t, ecdh.private_key_generate(&responder_s, protocol.dh), "failed to generate responder s") { + return false + } + res_s = &responder_s + if pre { + res_s_pub = &responder_s._pub_key + } + } + + psk_buf: [32]byte = --- + psk: []byte + if noise.pattern_is_psk(protocol.handshake_pattern) { + crypto.rand_bytes(psk_buf[:]) + psk = psk_buf[:] + } + + ini_hs, res_hs: noise.Handshake_State + status := noise.handshake_init(&ini_hs, true, nil, ini_s, res_s_pub, protocol_name, psk) + if !testing.expectf(t, status == .Ok, "failed to initialize initiator Handshake_State: %v", status) { + return false + } + status = noise.handshake_init(&res_hs, false, nil, res_s, ini_s_pub, protocol_name, psk) + if !testing.expectf(t, status == .Ok, "failed to initialize responder Handshake_State: %v", status) { + return false + } + + ini_status, res_status: noise.Status + ini_msg, res_msg: []byte + ini_payload, res_payload: []byte + hs_msg_buf: [noise.MAX_STEP_MSG_SIZE]byte + for i := 0; ; i += 1 { + if ini_status == .Handshake_Complete && res_status == .Handshake_Complete { + break + } + + // Test the allocation path + res_msg, res_payload, ini_status = noise.handshake_initiator_step(&ini_hs, ini_msg, allocator = allocator) + ini_msg = nil + + if ini_status == .Handshake_Complete && res_status == .Handshake_Complete { + break + } + + if !testing.expectf(t, len(res_payload) == 0, "step %d: unexpected responder payload: %x", i, res_payload) { + return false + } + if !testing.expectf(t, ini_status == .Handshake_Pending || ini_status == .Handshake_Complete, "step %d: initiator step failed: %v", i, ini_status) { + return false + } + + // Test the non-allocation path + ini_msg, ini_payload, res_status = noise.handshake_responder_step(&res_hs, res_msg, nil, dst = hs_msg_buf[:]) + delete(res_msg, allocator) + res_msg = nil + + if !testing.expectf(t, len(ini_payload) == 0, "step %d: unexpected initiator payload: %x", i, ini_payload) { + return false + } + if !testing.expectf(t, res_status == .Handshake_Pending || res_status == .Handshake_Complete, "step %d: responder step failed: %v", i, res_status) { + return false + } + } + delete(res_msg, allocator) + + hs_pub: ^ecdh.Public_Key + if ini_s != nil { + hs_pub, status = noise.handshake_peer_identity(&res_hs) + if !testing.expect(t, status == .Ok) { + return false + } + if !testing.expectf(t, ecdh.public_key_equal(&ini_s._pub_key, hs_pub), "responder has incorrect initiator identity") { + return false + } + } + if res_s != nil { + hs_pub, status = noise.handshake_peer_identity(&ini_hs) + if !testing.expect(t, status == .Ok) { + return false + } + if !testing.expectf(t, ecdh.public_key_equal(&res_s._pub_key, hs_pub), "initiator has incorrect responder identity") { + return false + } + } + + h1, h2: []byte + h1, status = noise.handshake_hash(&ini_hs) + if !testing.expect(t, status == .Ok) { + return false + } + h2, status = noise.handshake_hash(&res_hs) + if !testing.expect(t, status == .Ok) { + return false + } + if !testing.expectf(t, bytes.equal(h1, h2), "handshake hash mismatch: %x != %x", h1, h2) { + return false + } + + ini_cs, res_cs: noise.Cipher_States + if !testing.expectf(t, .Ok == noise.handshake_split(&ini_hs, &ini_cs), "failed to split initiator: %v") { + return false + } + if !testing.expectf(t, .Ok == noise.handshake_split(&res_hs, &res_cs), "failed to split responder: %v") { + return false + } + + noise.handshake_reset(&ini_hs) + noise.handshake_reset(&res_hs) + + if !testing.expect(t, test_messages(t, &ini_cs, &res_cs, is_one_way, allocator), "message tests failed") { + return false + } + + noise.cipherstates_reset(&ini_cs) + noise.cipherstates_reset(&res_cs) + + return true +} + +@(private = "file") +test_messages :: proc(t: ^testing.T, ini_cs, res_cs: ^noise.Cipher_States, is_one_way: bool, allocator := context.allocator) -> bool { + ad_buf: [256]byte = --- + payload_buf: [noise.MAX_PACKET_SIZE-noise.TAG_SIZE]byte = --- + + for i in 0..<10 { + ad := ad_buf[:rand.int_max(len(ad_buf))] + payload := payload_buf[:rand.int_max(len(payload_buf))] + + _ = rand.read(payload) + _ = rand.read(ad) + + // Initiator -> Responder (allocate buffers) + tx_msg, status := noise.seal_message(ini_cs, ad, payload, allocator = allocator) + defer delete(tx_msg, allocator) + if !testing.expectf(t, status == .Ok, "i->r %d: seal failed: %v", i, status) { + return false + } + + rx_dst: []byte + rx_dst, status = noise.open_message(res_cs, ad, tx_msg, allocator = allocator) + defer delete(rx_dst, allocator) + if !testing.expectf(t, status == .Ok, "i->r %d: open failed: %v", i, status) { + return false + } + + if !testing.expectf(t, bytes.equal(rx_dst, payload), "i->r %d: payload mismatch") { + return false + } + + if i == 5 { + status = noise.cipherstates_rekey(ini_cs, true) + if !testing.expectf(t, status == .Ok, "i %d: rekey failed: %v", i, status) { + return false + } + status = noise.cipherstates_rekey(res_cs, false) + if !testing.expectf(t, status == .Ok, "r %d: rekey failed: %v", i, status) { + return false + } + } + + if is_one_way { + continue + } + + // Responder -> Initiator (reuse allocated buffers) + tx_msg, status = noise.seal_message(res_cs, ad, payload, tx_msg) + if !testing.expectf(t, status == .Ok, "r->i %d: seal failed: %v", i, status) { + return false + } + + _, status = noise.open_message(ini_cs, ad, tx_msg, rx_dst) + if !testing.expectf(t, status == .Ok, "r->i %d: open failed: %v", i, status) { + return false + } + + if !testing.expectf(t, bytes.equal(rx_dst, payload), "r-i %d: payload mismatch") { + return false + } + } + + return true +} + +@(private = "file") +Test_Protocol :: struct { + handshake_pattern: noise.Handshake_Pattern, + dh: ecdh.Curve, + cipher: aead.Algorithm, + hash: hash.Algorithm, +} + +@(private = "file") +test_protocol_string :: proc(protocol: ^Test_Protocol, allocator := context.allocator) -> string { + dh: string + #partial switch protocol.dh { + case .X25519: dh = "25519" + case .X448: dh = "448" + case: panic("crypto/noise: unsupported DH") + } + + cipher: string + #partial switch protocol.cipher { + case .AES_GCM_256: cipher = "AESGCM" + case .CHACHA20POLY1305: cipher = "ChaChaPoly" + case: panic("crypto/noise: unsupported cipher") + } + + hash: string + #partial switch protocol.hash { + case .SHA256: hash = "SHA256" + case .SHA512: hash = "SHA512" + case .BLAKE2S: hash = "BLAKE2s" + case .BLAKE2B: hash = "BLAKE2b" + case: panic("crypto/noise: unsupported hash") + } + + return fmt.aprintf("Noise_%v_%v_%v_%v", protocol.handshake_pattern, dh, cipher, hash, allocator = allocator) +} diff --git a/tests/core/crypto/wycheproof/helpers.odin b/tests/core/crypto/wycheproof/helpers.odin index fb6b3ddf9..13bf4ab4f 100644 --- a/tests/core/crypto/wycheproof/helpers.odin +++ b/tests/core/crypto/wycheproof/helpers.odin @@ -4,8 +4,6 @@ import "core:crypto/hash" import "core:fmt" import "core:strings" -panic_fn :: proc(arg: any) - hash_name_to_algorithm :: proc(alg_str: string) -> (hash.Algorithm, bool) { alg_enums := [][hash.Algorithm]string { hash.ALGORITHM_NAMES, diff --git a/tests/core/crypto/wycheproof/helpers_generic.odin b/tests/core/crypto/wycheproof/helpers_generic.odin deleted file mode 100644 index 381005dfa..000000000 --- a/tests/core/crypto/wycheproof/helpers_generic.odin +++ /dev/null @@ -1,10 +0,0 @@ -#+build !linux -package test_wycheproof - -case_should_panic :: proc(fn: panic_fn, fn_arg: any, panic_str: string) -> bool { - panic("helpers: testing for panic is unsupported on this target") -} - -can_test_panic :: proc() -> bool { - return false -} diff --git a/tests/core/crypto/wycheproof/helpers_linux.odin b/tests/core/crypto/wycheproof/helpers_linux.odin deleted file mode 100644 index b9f1c0632..000000000 --- a/tests/core/crypto/wycheproof/helpers_linux.odin +++ /dev/null @@ -1,71 +0,0 @@ -#+build linux -package test_wycheproof - -import "core:log" -import "core:os" -import "core:strings" -import "core:sys/linux" - -@(private) -PIPE_BUF :: 4096 - -case_should_panic :: proc(fn: panic_fn, fn_arg: any, panic_str: string) -> bool { - stderr_pipe: [2]linux.Fd - - if err := linux.pipe2(&stderr_pipe, linux.Open_Flags{}); err != .NONE { - log.errorf("panic_case: failed to create pipe: %v", err) - return false - } - - pid, err := linux.fork() - switch { - case err != .NONE: - log.errorf("panic_case: failed to fork: %v", err) - return false - case pid == 0: - // In the child, redirect stderr to the pipe, run the function that - // is supposed to panic, and exit normally. - linux.dup2(stderr_pipe[1], 2) - fn(fn_arg) - os.exit(0) - } - - // Parent. - defer linux.close(stderr_pipe[0]) - defer linux.close(stderr_pipe[1]) - - // Wait for the child to terminate, and ensure it terminated - // abnormally (SIGILL/SIGTRAP). - wait_status: u32 - linux.wait4(pid, &wait_status, linux.Wait_Options{}, nil) - if !linux.WIFSIGNALED(wait_status) { - log.errorf("panic_case: child did not terminate via signal: %x", wait_status) - return false - } - term_sig := linux.Signal(linux.WTERMSIG(wait_status)) - if term_sig != .SIGILL && term_sig != .SIGTRAP { - log.errorf("panic_case: child terminated via wrong signal: %v", term_sig) - return false - } - - // Consume the child's stderr output from the pipe buffer. - // - // Note: POSIX requires PIPE_BUF be >= 512 bytes, Linux defaults - // to 4096 bytes. Either is sufficient to buffer output for our - // test cases. - buf: [PIPE_BUF]byte - n, _ := linux.read(stderr_pipe[0], buf[:]) - if n == 0 { - log.errorf("panic_case: child stderr empty") - return false - } - s := string(buf[:n]) - - log.debugf("panic case: child stderr: '%s'", s) - - return strings.contains(s, panic_str) -} - -can_test_panic :: proc() -> bool { - return true -} diff --git a/tests/core/crypto/wycheproof/main.odin b/tests/core/crypto/wycheproof/main.odin index a75a6cd54..dc8ab9237 100644 --- a/tests/core/crypto/wycheproof/main.odin +++ b/tests/core/crypto/wycheproof/main.odin @@ -25,6 +25,8 @@ import "core:crypto/pbkdf2" import "core:crypto/siphash" import "core:crypto/deoxysii" +import "../common" + // Covered: // - crypto/aegis // - aegis128L_test.json @@ -95,8 +97,6 @@ print_test_vector_path :: proc(t: ^testing.T) { log.infof("wycheproof path: %s", BASE_PATH) } -test_proc :: proc(_: string) -> bool - supported_aegis_impls :: proc() -> [dynamic]aes.Implementation { impls := make([dynamic]aes.Implementation, 0, 2, context.temp_allocator) append(&impls, aes.Implementation.Portable) @@ -167,12 +167,12 @@ test_aead_aegis_impl :: proc( ) } - key := hexbytes_decode(test_vector.key) - iv := hexbytes_decode(test_vector.iv) - aad := hexbytes_decode(test_vector.aad) - msg := hexbytes_decode(test_vector.msg) - ct := hexbytes_decode(test_vector.ct) - tag := hexbytes_decode(test_vector.tag) + key := common.hexbytes_decode(test_vector.key) + iv := common.hexbytes_decode(test_vector.iv) + aad := common.hexbytes_decode(test_vector.aad) + msg := common.hexbytes_decode(test_vector.msg) + ct := common.hexbytes_decode(test_vector.ct) + tag := common.hexbytes_decode(test_vector.tag) if len(iv) == 0 { log.infof( @@ -192,7 +192,7 @@ test_aead_aegis_impl :: proc( tag_ := make([]byte, len(tag)) aegis.seal(&ctx, ct_, tag_, iv, aad, msg) - ok := hexbytes_compare(test_vector.ct, ct_) + ok := common.hexbytes_compare(test_vector.ct, ct_) if !result_check(test_vector.result, ok) { x := transmute(string)(hex.encode(ct_)) log.errorf( @@ -206,7 +206,7 @@ test_aead_aegis_impl :: proc( continue } - ok = hexbytes_compare(test_vector.tag, tag_) + ok = common.hexbytes_compare(test_vector.tag, tag_) if !result_check(test_vector.result, ok) { x := transmute(string)(hex.encode(tag_)) log.errorf( @@ -229,7 +229,7 @@ test_aead_aegis_impl :: proc( continue } - if ok && !hexbytes_compare(test_vector.msg, msg_) { + if ok && !common.hexbytes_compare(test_vector.msg, msg_) { x := transmute(string)(hex.encode(msg_)) log.errorf( "aead/aegis/%v/%d: decrypt msg: expected %s actual %s", @@ -317,12 +317,12 @@ test_aead_aes_gcm_impl :: proc( ) } - key := hexbytes_decode(test_vector.key) - iv := hexbytes_decode(test_vector.iv) - aad := hexbytes_decode(test_vector.aad) - msg := hexbytes_decode(test_vector.msg) - ct := hexbytes_decode(test_vector.ct) - tag := hexbytes_decode(test_vector.tag) + key := common.hexbytes_decode(test_vector.key) + iv := common.hexbytes_decode(test_vector.iv) + aad := common.hexbytes_decode(test_vector.aad) + msg := common.hexbytes_decode(test_vector.msg) + ct := common.hexbytes_decode(test_vector.ct) + tag := common.hexbytes_decode(test_vector.tag) if len(iv) == 0 { log.infof( @@ -342,7 +342,7 @@ test_aead_aes_gcm_impl :: proc( tag_ := make([]byte, len(tag)) aes.seal_gcm(&ctx, ct_, tag_, iv, aad, msg) - ok := hexbytes_compare(test_vector.ct, ct_) + ok := common.hexbytes_compare(test_vector.ct, ct_) if !result_check(test_vector.result, ok) { x := transmute(string)(hex.encode(ct_)) log.errorf( @@ -356,7 +356,7 @@ test_aead_aes_gcm_impl :: proc( continue } - ok = hexbytes_compare(test_vector.tag, tag_) + ok = common.hexbytes_compare(test_vector.tag, tag_) if !result_check(test_vector.result, ok) { x := transmute(string)(hex.encode(tag_)) log.errorf( @@ -379,7 +379,7 @@ test_aead_aes_gcm_impl :: proc( continue } - if ok && !hexbytes_compare(test_vector.msg, msg_) { + if ok && !common.hexbytes_compare(test_vector.msg, msg_) { x := transmute(string)(hex.encode(msg_)) log.errorf( "aead/aes-gcm/%v/%d: decrypt msg: expected %s actual %s", @@ -488,12 +488,12 @@ test_aead_chacha20_poly1305_impl :: proc( ) } - key := hexbytes_decode(test_vector.key) - iv := hexbytes_decode(test_vector.iv) - aad := hexbytes_decode(test_vector.aad) - msg := hexbytes_decode(test_vector.msg) - ct := hexbytes_decode(test_vector.ct) - tag := hexbytes_decode(test_vector.tag) + key := common.hexbytes_decode(test_vector.key) + iv := common.hexbytes_decode(test_vector.iv) + aad := common.hexbytes_decode(test_vector.aad) + msg := common.hexbytes_decode(test_vector.msg) + ct := common.hexbytes_decode(test_vector.ct) + tag := common.hexbytes_decode(test_vector.tag) if slice.contains(test_vector.flags, FLAG_INVALID_NONCE_SIZE) { log.infof( @@ -519,7 +519,7 @@ test_aead_chacha20_poly1305_impl :: proc( tag_ := make([]byte, len(tag)) chacha20poly1305.seal(&ctx, ct_, tag_, iv, aad, msg) - ok := hexbytes_compare(test_vector.ct, ct_) + ok := common.hexbytes_compare(test_vector.ct, ct_) if !result_check(test_vector.result, ok) { x := transmute(string)(hex.encode(ct_)) log.errorf( @@ -534,7 +534,7 @@ test_aead_chacha20_poly1305_impl :: proc( continue } - ok = hexbytes_compare(test_vector.tag, tag_) + ok = common.hexbytes_compare(test_vector.tag, tag_) if !result_check(test_vector.result, ok) { x := transmute(string)(hex.encode(tag_)) log.errorf( @@ -562,7 +562,7 @@ test_aead_chacha20_poly1305_impl :: proc( continue } - if ok && !hexbytes_compare(test_vector.msg, msg_) { + if ok && !common.hexbytes_compare(test_vector.msg, msg_) { x := transmute(string)(hex.encode(msg_)) log.errorf( "aead/%s/%v/%d: decrypt msg: expected %s actual %s", @@ -628,7 +628,7 @@ test_eddsa_ed25519 :: proc(t: ^testing.T) { num_ran, num_passed, num_failed, num_skipped: int for &test_group, i in test_vectors.test_groups { mem.free_all() // Probably don't need this, but be safe. - pk_bytes := hexbytes_decode(test_group.public_key.pk) + pk_bytes := common.hexbytes_decode(test_group.public_key.pk) pk: ed25519.Public_Key pk_ok := ed25519.public_key_set_bytes(&pk, pk_bytes) @@ -652,8 +652,8 @@ test_eddsa_ed25519 :: proc(t: ^testing.T) { log.debugf("eddsa/ed25519/%d: %+v", test_vector.tc_id, test_vector.flags) } - msg := hexbytes_decode(test_vector.msg) - sig := hexbytes_decode(test_vector.sig) + msg := common.hexbytes_decode(test_vector.msg) + sig := common.hexbytes_decode(test_vector.sig) verify_ok := ed25519.verify(&pk, msg, sig) result_ok := result_check(test_vector.result, verify_ok) @@ -747,7 +747,7 @@ test_ecdsa_impl :: proc(t: ^testing.T, test_vectors: ^Test_Vectors(Ecdsa_Test_Gr num_ran, num_passed, num_failed, num_skipped: int for &test_group, i in test_vectors.test_groups { - pk_bytes := hexbytes_decode(test_group.public_key.uncompressed) + pk_bytes := common.hexbytes_decode(test_group.public_key.uncompressed) pk: ecdsa.Public_Key pk_ok := ecdsa.public_key_set_bytes(&pk, curve_alg, pk_bytes) @@ -773,8 +773,8 @@ test_ecdsa_impl :: proc(t: ^testing.T, test_vectors: ^Test_Vectors(Ecdsa_Test_Gr log.debugf("ecdsa/%s/%s/%d: %+v", curve_str, hash_str, test_vector.tc_id, test_vector.flags) } - msg := hexbytes_decode(test_vector.msg) - sig := hexbytes_decode(test_vector.sig) + msg := common.hexbytes_decode(test_vector.msg) + sig := common.hexbytes_decode(test_vector.sig) verify_ok := ecdsa.verify_asn1(&pk, hash_alg, msg, sig) result_ok := result_check(test_vector.result, verify_ok) @@ -876,9 +876,9 @@ test_hkdf_impl :: proc(test_vectors: ^Test_Vectors(Hkdf_Test_Group)) -> bool { log.debugf("hkdf/%s/%d: %+v", alg_str, test_vector.tc_id, test_vector.flags) } - ikm := hexbytes_decode(test_vector.ikm) - salt := hexbytes_decode(test_vector.salt) - info := hexbytes_decode(test_vector.info) + ikm := common.hexbytes_decode(test_vector.ikm) + salt := common.hexbytes_decode(test_vector.salt) + info := common.hexbytes_decode(test_vector.info) if slice.contains(test_vector.flags, FLAG_SIZE_TOO_LARGE) { log.infof( @@ -893,7 +893,7 @@ test_hkdf_impl :: proc(test_vectors: ^Test_Vectors(Hkdf_Test_Group)) -> bool { okm_ := make([]byte, test_vector.size) hkdf.extract_and_expand(alg, salt, ikm, info, okm_) - ok = hexbytes_compare(test_vector.okm, okm_) + ok = common.hexbytes_compare(test_vector.okm, okm_) if !result_check(test_vector.result, ok) { x := transmute(string)(hex.encode(okm_)) log.errorf( @@ -1001,8 +1001,8 @@ test_mac_impl :: proc(test_vectors: ^Test_Vectors(Mac_Test_Group)) -> bool { log.debugf("%s/%d: %+v", alg_str, test_vector.tc_id, test_vector.flags) } - key := hexbytes_decode(test_vector.key) - msg := hexbytes_decode(test_vector.msg) + key := common.hexbytes_decode(test_vector.key) + msg := common.hexbytes_decode(test_vector.msg) tag_ := make([]byte, len(test_vector.tag) / 2) @@ -1037,7 +1037,7 @@ test_mac_impl :: proc(test_vectors: ^Test_Vectors(Mac_Test_Group)) -> bool { siphash.sum_4_8(msg, key, tag_) } - ok = hexbytes_compare(test_vector.tag, tag_) + ok = common.hexbytes_compare(test_vector.tag, tag_) if !result_check(test_vector.result, ok) { x := transmute(string)(hex.encode(tag_)) log.errorf( @@ -1146,13 +1146,13 @@ test_pbkdf2_impl :: proc( continue } - password := hexbytes_decode(test_vector.password) - salt := hexbytes_decode(test_vector.salt) + password := common.hexbytes_decode(test_vector.password) + salt := common.hexbytes_decode(test_vector.salt) dk_ := make([]byte, test_vector.dk_len) pbkdf2.derive(alg, password, salt, test_vector.iteration_count, dk_) - ok = hexbytes_compare(test_vector.dk, dk_) + ok = common.hexbytes_compare(test_vector.dk, dk_) if !result_check(test_vector.result, ok) { x := transmute(string)(hex.encode(dk_)) log.errorf( @@ -1255,8 +1255,8 @@ test_ecdh_impl :: proc( log.debugf("ecdh/%s/%d: %+v", alg_str, test_vector.tc_id, test_vector.flags) } - raw_pub := hexbytes_decode(test_vector.public) - raw_priv := hexbytes_decode(test_vector.private) + raw_pub := common.hexbytes_decode(test_vector.public) + raw_priv := common.hexbytes_decode(test_vector.private) curve: ecdh.Curve priv_key: ecdh.Private_Key @@ -1366,7 +1366,7 @@ test_ecdh_impl :: proc( continue } - ok = hexbytes_compare(test_vector.shared, shared) + ok = common.hexbytes_compare(test_vector.shared, shared) // "acceptable" results are fine from here because we have // checked for the all-zero shared secret XDH case already. if !result_check(test_vector.result, ok, false) { diff --git a/tests/core/crypto/wycheproof/schemas.odin b/tests/core/crypto/wycheproof/schemas.odin index a07186dcc..645f0f085 100644 --- a/tests/core/crypto/wycheproof/schemas.odin +++ b/tests/core/crypto/wycheproof/schemas.odin @@ -1,28 +1,10 @@ package test_wycheproof -import "core:bytes" -import "core:encoding/hex" @(require) import "core:encoding/json" @(require) import "core:log" @(require) import "core:os" -Hex_Bytes :: string - -hexbytes_compare :: proc(x: Hex_Bytes, b: []byte, allocator := context.allocator) -> bool { - dst := hexbytes_decode(x) - defer delete(dst) - - return bytes.equal(dst, b) -} - -hexbytes_decode :: proc(x: Hex_Bytes, allocator := context.allocator) -> []byte { - dst, ok := hex.decode(transmute([]byte)(x), allocator) - if !ok { - panic("wycheproof/common/Hex_Bytes: invalid hex encoding") - } - - return dst -} +import "../common" Result :: string @@ -90,16 +72,16 @@ Aead_Test_Group :: struct { } Aead_Test_Vector :: struct { - tc_id: int `json:"tcId"`, - comment: string `json:"comment"`, - key: Hex_Bytes `json:"key"`, - iv: Hex_Bytes `json:"iv"`, - aad: Hex_Bytes `json:"aad"`, - msg: Hex_Bytes `json:"msg"`, - ct: Hex_Bytes `json:"ct"`, - tag: Hex_Bytes `json:"tag"`, - result: Result `json:"result"`, - flags: []string `json:"flags"`, + tc_id: int `json:"tcId"`, + comment: string `json:"comment"`, + key: common.Hex_Bytes `json:"key"`, + iv: common.Hex_Bytes `json:"iv"`, + aad: common.Hex_Bytes `json:"aad"`, + msg: common.Hex_Bytes `json:"msg"`, + ct: common.Hex_Bytes `json:"ct"`, + tag: common.Hex_Bytes `json:"tag"`, + result: Result `json:"result"`, + flags: []string `json:"flags"`, } Hkdf_Test_Group :: struct { @@ -108,15 +90,15 @@ Hkdf_Test_Group :: struct { } Hkdf_Test_Vector :: struct { - tc_id: int `json:"tcId"`, - comment: string `json:"comment"`, - ikm: Hex_Bytes `json:"ikm"`, - salt: Hex_Bytes `json:"salt"`, - info: Hex_Bytes `json:"info"`, - size: int `json:"size"`, - okm: Hex_Bytes `json:"okm"`, - result: Result `json:"result"`, - flags: []string `json:"flags"`, + tc_id: int `json:"tcId"`, + comment: string `json:"comment"`, + ikm: common.Hex_Bytes `json:"ikm"`, + salt: common.Hex_Bytes `json:"salt"`, + info: common.Hex_Bytes `json:"info"`, + size: int `json:"size"`, + okm: common.Hex_Bytes `json:"okm"`, + result: Result `json:"result"`, + flags: []string `json:"flags"`, } Mac_Test_Group :: struct { @@ -126,13 +108,13 @@ Mac_Test_Group :: struct { } Mac_Test_Vector :: struct { - tc_id: int `json:"tcId"`, - comment: string `json:"comment"`, - key: Hex_Bytes `json:"key"`, - msg: Hex_Bytes `json:"msg"`, - tag: Hex_Bytes `json:"tag"`, - result: Result `json:"result"`, - flags: []string `json:"flags"`, + tc_id: int `json:"tcId"`, + comment: string `json:"comment"`, + key: common.Hex_Bytes `json:"key"`, + msg: common.Hex_Bytes `json:"msg"`, + tag: common.Hex_Bytes `json:"tag"`, + result: Result `json:"result"`, + flags: []string `json:"flags"`, } Ecdh_Test_Group :: struct { @@ -141,18 +123,18 @@ Ecdh_Test_Group :: struct { } Ecdh_Test_Vector :: struct { - tc_id: int `json:"tcId"`, - comment: string `json:"comment"`, - public: Hex_Bytes `json:"public"`, - private: Hex_Bytes `json:"private"`, - shared: Hex_Bytes `json:"shared"`, - result: Result `json:"result"`, - flags: []string `json:"flags"`, + tc_id: int `json:"tcId"`, + comment: string `json:"comment"`, + public: common.Hex_Bytes `json:"public"`, + private: common.Hex_Bytes `json:"private"`, + shared: common.Hex_Bytes `json:"shared"`, + result: Result `json:"result"`, + flags: []string `json:"flags"`, } Eddsa_Test_Group :: struct { public_key: Eddsa_Key `json:"publicKey"`, - public_key_der: Hex_Bytes `json:"publicKeyDer"`, + public_key_der: common.Hex_Bytes `json:"publicKeyDer"`, public_key_pem: string `json:"publicKeyPem"`, public_key_jwk: Eddsa_Jwk `json:"publicKeyJwk"`, type: string `json:"type"`, @@ -160,10 +142,10 @@ Eddsa_Test_Group :: struct { } Eddsa_Key :: struct { - type: string `json:"type"`, - curve: string `json:"curve"`, - key_size: int `json:"keySize"`, - pk: Hex_Bytes `json:"pk"`, + type: string `json:"type"`, + curve: string `json:"curve"`, + key_size: int `json:"keySize"`, + pk: common.Hex_Bytes `json:"pk"`, } Eddsa_Jwk :: struct { @@ -174,17 +156,17 @@ Eddsa_Jwk :: struct { } Ecdsa_Key :: struct { - type: string `json:"type"`, - curve: string `json:"curve"`, - key_size: int `json:"keySize"`, - uncompressed: Hex_Bytes `json:"uncompressed"`, - wx: Hex_Bytes `json:"wx"`, - wy: Hex_Bytes `json:"wy"`, + type: string `json:"type"`, + curve: string `json:"curve"`, + key_size: int `json:"keySize"`, + uncompressed: common.Hex_Bytes `json:"uncompressed"`, + wx: common.Hex_Bytes `json:"wx"`, + wy: common.Hex_Bytes `json:"wy"`, } Ecdsa_Test_Group :: struct { public_key: Ecdsa_Key `json:"publicKey"`, - public_key_der: Hex_Bytes `json:"publicKeyDer"`, + public_key_der: common.Hex_Bytes `json:"publicKeyDer"`, public_key_pem: string `json:"publicKeyPem"`, type: string `json:"type"`, sha: string `json:"sha"`, @@ -192,12 +174,12 @@ Ecdsa_Test_Group :: struct { } Dsa_Test_Vector :: struct { - tc_id: int `json:"tcId"`, - comment: string `json:"comment"`, - msg: Hex_Bytes `json:"msg"`, - sig: Hex_Bytes `json:"sig"`, - result: Result `json:"result"`, - flags: []string `json:"flags"`, + tc_id: int `json:"tcId"`, + comment: string `json:"comment"`, + msg: common.Hex_Bytes `json:"msg"`, + sig: common.Hex_Bytes `json:"sig"`, + result: Result `json:"result"`, + flags: []string `json:"flags"`, } Pbkdf_Test_Group :: struct { @@ -206,13 +188,13 @@ Pbkdf_Test_Group :: struct { } Pbkdf_Test_Vector :: struct { - tc_id: int `json:"tcId"`, - comment: string `json:"comment"`, - password: Hex_Bytes `json:"password"`, - salt: Hex_Bytes `json:"salt"`, - iteration_count: u32 `json:"iterationCount"`, - dk_len: int `json:"dkLen"`, - dk: Hex_Bytes `json:"dk"`, - result: Result `json:"result"`, - flags: []string `json:"flags"`, + tc_id: int `json:"tcId"`, + comment: string `json:"comment"`, + password: common.Hex_Bytes `json:"password"`, + salt: common.Hex_Bytes `json:"salt"`, + iteration_count: u32 `json:"iterationCount"`, + dk_len: int `json:"dkLen"`, + dk: common.Hex_Bytes `json:"dk"`, + result: Result `json:"result"`, + flags: []string `json:"flags"`, } diff --git a/tests/core/download_assets.py b/tests/core/download_assets.py index 46a6c0e86..e7cc0220d 100644 --- a/tests/core/download_assets.py +++ b/tests/core/download_assets.py @@ -10,7 +10,9 @@ import sys import io sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') -TEST_SUITES = ['PNG', 'XML', 'BMP', 'JPG', 'Wycheproof'] +# The OpenSSL CLI tool installed can be used to easily generate digests: +# $ openssl sha3-512 -mac HMAC -macopt key:"$HMAC_KEY" $FILE_NAME +TEST_SUITES = ['PNG', 'XML', 'BMP', 'JPG', 'Wycheproof', 'Noise'] ASSETS_BASE_URL = "https://raw.githubusercontent.com/odin-lang/test-assets/master/{}/{}" HMAC_KEY = "https://odin-lang.org" HMAC_HASH = hashlib.sha3_512 @@ -625,6 +627,14 @@ HMAC_DIGESTS = { 'x448_pem_test.json': "718ef327a5e8cc3a34467974193a051efe89323352f32f248c874457f28f6a2836cb6c2c15f40044df96428051591766fc0b44771236145a17835e714360ca51", 'x448_test.json': "ca811349ede46fc253d656c26b058e2fe903aae8a225d7f6703bef3dec122df4c48af190a070df4c4066690f50ed4996d69089794b5484b57f2c4233216f98f5", 'xchacha20_poly1305_test.json': "5debf381018af54fa2f9041044a257f8faff85b9a2eda314f21a05a55de2aacd2767a4962ecc58f69ed65404dc7d2b1eb8526038e7641610ce3a988456838fce", + + 'LICENSE-APACHE-snow.txt': "6d94a2c13b32d3c0db347511a19f71e5c2ec15025f3f3fdb333ae72e3c671b0b4d8796a9adf3e9b1ec941acea28a054e7108914ee93ebae673d894d2ac4a7cad", + 'LICENSE-MIT-snow.txt': "c0d9d5f399729560ffbabcb46a1e282e9a214d436a59b86dd8765556290a93c204c126234401f935f6a18aff50d69cc8af4decc6f0bb1574f899412c77fb9b31", + 'LICENSE-cacophony.txt': "cab56e8608950b3e9d5c0d8262f8c67e34254569726e78968896a964bab84e67fd66ccce43653118152fba5a3e670e0de23c725ecd85f65cacce4d987b1258e8", + 'LICENSE-noise-c.txt': "ca6ef9d13d0832659efa04f03d06998af3050b0f65886f1b4f33ef4bd9b6df639fcaac0c73a7c28bc47c502e7915e30608863340245eac7620ab50ee66e99ff6", + 'cacophony.txt': "d2c492f02287ee562a9cf43a4cd2a055f5235734779d091bc404aede4c37a552029e76c12e873632e52423804ad24b86138cd3d4fe54506108571d9f2f925033", + 'noise-c-basic.txt': "7e25c5d692c53dd045060f27a562332178ca030d17687fc2ab58216b4298fb577ddc9d703db11c920fb04c28604fdc1a30337f58aad3617e07201a62c3a9b295", + 'snow.txt': "35088303de90e6bac22656e1e0ac4a5ea73d8cdb5ada23a078d703e14714ffe19e08437bf46108e0419acd8b5be6fa797a68a621c20cdd063d94a4a970aa8634", } def try_download_file(url, out_file): diff --git a/tests/core/testing/test_core_testing.odin b/tests/core/testing/test_core_testing.odin index a323971dc..ed3a29173 100644 --- a/tests/core/testing/test_core_testing.odin +++ b/tests/core/testing/test_core_testing.odin @@ -50,3 +50,9 @@ test_expected_signal :: proc(t: ^testing.T) { testing.expect_signal(t, libc.SIGILL) libc.raise(libc.SIGILL) } + +@test +test_array_bounds_trap_signal :: proc(t: ^testing.T) { + testing.expect_signal(t, libc.SIGILL) + _ = make([]u8, -1) +} diff --git a/tests/issues/run.sh b/tests/issues/run.sh index 17b1b75e0..6eb2e09d3 100755 --- a/tests/issues/run.sh +++ b/tests/issues/run.sh @@ -36,6 +36,8 @@ $ODIN test ../test_issue_5699.odin $COMMON $ODIN test ../test_issue_6068.odin $COMMON $ODIN test ../test_issue_6101.odin $COMMON $ODIN test ../test_issue_6165.odin $COMMON +$ODIN test ../test_issue_6344.odin $COMMON +$ODIN test ../test_issue_6344.odin $COMMON -o:speed $ODIN test ../test_issue_6396.odin $COMMON $ODIN test ../test_pr_6476.odin $COMMON diff --git a/tests/issues/test_issue_6344.odin b/tests/issues/test_issue_6344.odin new file mode 100644 index 000000000..834ca25db --- /dev/null +++ b/tests/issues/test_issue_6344.odin @@ -0,0 +1,72 @@ +// Tests issue #6344 https://github.com/odin-lang/Odin/issues/6344 +package test_issues + +import "core:testing" + +@(test) +test_soa_dynamic_field_write :: proc(t: ^testing.T) { + V :: struct { + x: f32, + y: f32, + } + + array := make(#soa[dynamic]V, 4, 8) + defer delete(array) + + for i in 0 ..< 4 { + array[i] = V{f32(i), f32(i) * 2} + } + + // Simple write through field-first indexing (was: compiler panic) + for i in 0 ..< 4 { + array.x[i] = f32(i) * 10 + } + testing.expect_value(t, array[0].x, 0) + testing.expect_value(t, array[1].x, 10) + testing.expect_value(t, array[2].x, 20) + testing.expect_value(t, array[3].x, 30) + + // Compound write through field-first indexing (was: compiler panic) + for i in 0 ..< 4 { + array.x[i] += array.y[i] + } + testing.expect_value(t, array[0].x, 0) + testing.expect_value(t, array[1].x, 12) + testing.expect_value(t, array[2].x, 24) + testing.expect_value(t, array[3].x, 36) +} + +@(test) +test_soa_slice_field_write :: proc(t: ^testing.T) { + V :: struct { + x: f32, + y: f32, + } + + array := make(#soa[dynamic]V, 4, 8) + defer delete(array) + + for i in 0 ..< 4 { + array[i] = V{f32(i), f32(i) * 2} + } + + slice := array[:] + + // Write through slice field-first indexing (was: compiler panic) + for i in 0 ..< 4 { + slice.x[i] = f32(i) * 10 + } + testing.expect_value(t, array[0].x, 0) + testing.expect_value(t, array[1].x, 10) + testing.expect_value(t, array[2].x, 20) + testing.expect_value(t, array[3].x, 30) + + // Compound write through slice field-first indexing + for i in 0 ..< 4 { + slice.x[i] += slice.y[i] + } + testing.expect_value(t, array[0].x, 0) + testing.expect_value(t, array[1].x, 12) + testing.expect_value(t, array[2].x, 24) + testing.expect_value(t, array[3].x, 36) +} diff --git a/vendor/directx/d3d12/d3d12.odin b/vendor/directx/d3d12/d3d12.odin index 3519dafaf..d17591f6a 100644 --- a/vendor/directx/d3d12/d3d12.odin +++ b/vendor/directx/d3d12/d3d12.odin @@ -5458,12 +5458,12 @@ TEXTURE_BARRIER_FLAGS :: enum i32 { } BARRIER_SUBRESOURCE_RANGE :: struct { - IndexOrFirstMipLevel: uint, - NumMipLevels: uint, - FirstArraySlice: uint, - NumArraySlices: uint, - FirstPlane: uint, - NumPlanes: uint, + IndexOrFirstMipLevel: u32, + NumMipLevels: u32, + FirstArraySlice: u32, + NumArraySlices: u32, + FirstPlane: u32, + NumPlanes: u32, } GLOBAL_BARRIER :: struct { diff --git a/vendor/sdl3/mixer/sdl3_mixer.odin b/vendor/sdl3/mixer/sdl3_mixer.odin index 36e5ecd1e..276f94c8e 100644 --- a/vendor/sdl3/mixer/sdl3_mixer.odin +++ b/vendor/sdl3/mixer/sdl3_mixer.odin @@ -76,8 +76,8 @@ foreign lib { Quit :: proc() --- GetNumAudioDecoders :: proc() -> c.int --- GetAudioDecoder :: proc(index: c.int) -> cstring --- - CreateMixerDevice :: proc(devid: SDL.AudioDeviceID, #by_ptr spec: SDL.AudioSpec) -> ^Mixer --- - CreateMixer :: proc(#by_ptr spec: SDL.AudioSpec) -> ^Mixer --- + CreateMixerDevice :: proc(devid: SDL.AudioDeviceID, spec: Maybe(^SDL.AudioSpec)) -> ^Mixer --- + CreateMixer :: proc(spec: Maybe(^SDL.AudioSpec)) -> ^Mixer --- DestroyMixer :: proc(mixer: ^Mixer) --- GetMixerProperties :: proc(mixer: ^Mixer) -> SDL.PropertiesID --- GetMixerFormat :: proc(mixer: ^Mixer, spec: ^SDL.AudioSpec) -> c.bool --- @@ -145,8 +145,8 @@ foreign lib { SetTrackFrequencyRatio :: proc(track: ^Track, ratio: c.float) -> c.bool --- GetTrackFrequencyRatio :: proc(track: ^Track) -> c.float --- SetTrackOutputChannelMap :: proc(track: ^Track, chmap: [^]c.int, count: c.int) -> c.bool --- - SetTrackStereo :: proc(track: ^Track, #by_ptr gains: StereoGains) -> c.bool --- - SetTrack3DPosition :: proc(track: ^Track, #by_ptr position: Point3D) -> c.bool --- + SetTrackStereo :: proc(track: ^Track, gains: Maybe(^StereoGains)) -> c.bool --- + SetTrack3DPosition :: proc(track: ^Track, position: Maybe(^Point3D)) -> c.bool --- GetTrack3DPosition :: proc(track: ^Track, position: ^Point3D) -> c.bool --- CreateGroup :: proc(mixer: ^Mixer) -> ^Group --- DestroyGroup :: proc(group: ^Group) --- diff --git a/vendor/vulkan/_gen/create_vulkan_odin_wrapper.py b/vendor/vulkan/_gen/create_vulkan_odin_wrapper.py index 4900d52b7..6e5eea2c0 100644 --- a/vendor/vulkan/_gen/create_vulkan_odin_wrapper.py +++ b/vendor/vulkan/_gen/create_vulkan_odin_wrapper.py @@ -899,6 +899,7 @@ load_proc_addresses :: proc{ with open("../core.odin", 'w', encoding='utf-8') as f: f.write(BASE) f.write(PACKAGE_LINE) + f.write("\n") f.write(""" // Core API API_VERSION_1_0 :: (1<<22) | (0<<12) | (0) @@ -907,10 +908,38 @@ API_VERSION_1_2 :: (1<<22) | (2<<12) | (0) API_VERSION_1_3 :: (1<<22) | (3<<12) | (0) API_VERSION_1_4 :: (1<<22) | (4<<12) | (0) +MAKE_API_VERSION :: proc "contextless" (variant, major, minor, patch: u32) -> u32 { +\treturn (variant<<29) | (major<<22) | (minor<<12) | (patch) +} + MAKE_VERSION :: proc "contextless" (major, minor, patch: u32) -> u32 { \treturn (major<<22) | (minor<<12) | (patch) } +API_VERSION_MAJOR :: proc "contextless" (version: u32) -> u32 { +\treturn (version>>22) & 0x7F +} + +VERSION_MAJOR :: proc "contextless" (version: u32) -> u32 { +\treturn (version>>22) +} + +API_VERSION_MINOR :: proc "contextless" (version: u32) -> u32 { +\treturn (version>>12) & 0x3FF +} + +VERSION_MINOR :: API_VERSION_MINOR + +API_VERSION_PATCH :: proc "contextless" (version: u32) -> u32 { +\treturn (version & 0xFFF) +} + +VERSION_PATCH :: API_VERSION_PATCH + +API_VERSION_VARIANT :: proc "contextless" (version: u32) -> u32 { +\treturn (version>>29) +} + // Base types Flags :: distinct u32 Flags64 :: distinct u64 @@ -973,12 +1002,13 @@ MAKE_VIDEO_STD_VERSION :: MAKE_VERSION parse_flags_def(f) with open("../enums.odin", 'w', encoding='utf-8') as f: f.write(PACKAGE_LINE) - f.write("\n") + f.write("\n\n") parse_enums(f) parse_fake_enums(f) f.write("\n\n") with open("../structs.odin", 'w', encoding='utf-8') as f: f.write(PACKAGE_LINE) + f.write("\n") f.write(""" import "core:c" @@ -1040,7 +1070,7 @@ MTLCommandQueue_id :: rawptr f.write("\n\n") with open("../procedures.odin", 'w', encoding='utf-8') as f: f.write(PACKAGE_LINE) - f.write("\n") + f.write("\n\n") parse_procedures(f) f.write("\n") group_functions(f) diff --git a/vendor/vulkan/core.odin b/vendor/vulkan/core.odin index c3cb59a16..3cc166dc8 100644 --- a/vendor/vulkan/core.odin +++ b/vendor/vulkan/core.odin @@ -7,10 +7,38 @@ API_VERSION_1_2 :: (1<<22) | (2<<12) | (0) API_VERSION_1_3 :: (1<<22) | (3<<12) | (0) API_VERSION_1_4 :: (1<<22) | (4<<12) | (0) +MAKE_API_VERSION :: proc "contextless" (variant, major, minor, patch: u32) -> u32 { + return (variant<<29) | (major<<22) | (minor<<12) | (patch) +} + MAKE_VERSION :: proc "contextless" (major, minor, patch: u32) -> u32 { return (major<<22) | (minor<<12) | (patch) } +API_VERSION_MAJOR :: proc "contextless" (version: u32) -> u32 { + return (version>>22) & 0x7F +} + +VERSION_MAJOR :: proc "contextless" (version: u32) -> u32 { + return (version>>22) +} + +API_VERSION_MINOR :: proc "contextless" (version: u32) -> u32 { + return (version>>12) & 0x3FF +} + +VERSION_MINOR :: API_VERSION_MINOR + +API_VERSION_PATCH :: proc "contextless" (version: u32) -> u32 { + return (version & 0xFFF) +} + +VERSION_PATCH :: API_VERSION_PATCH + +API_VERSION_VARIANT :: proc "contextless" (version: u32) -> u32 { + return (version>>29) +} + // Base types Flags :: distinct u32 Flags64 :: distinct u64 diff --git a/vendor/wgpu/doc.odin b/vendor/wgpu/doc.odin index 693062788..5f72b9147 100644 --- a/vendor/wgpu/doc.odin +++ b/vendor/wgpu/doc.odin @@ -12,8 +12,8 @@ You can find a number of examples on [[Odin's official examples repository; http **Getting the wgpu-native libraries** For native support (not the browser), some libraries are required. Fortunately this is -extremely easy, just download them from the [[releases on GitHub; https://github.com/gfx-rs/wgpu-native/releases/tag/v27.0.2.0]]. -the bindings are for v27.0.2.0 at the moment. +extremely easy, just download them from the [[releases on GitHub; https://github.com/gfx-rs/wgpu-native/releases/tag/v29.0.0.0]]. +the bindings are for v29.0.0.0 at the moment. These are expected in the `lib` folder under the same name as they are released (just unzipped). By default it will look for a static release version (`wgpu-OS-ARCH-release.a|lib`), diff --git a/vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.dll b/vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.dll index 08518a94e..574cdda82 100644 Binary files a/vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.dll and b/vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.dll differ diff --git a/vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.dll.lib b/vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.dll.lib index 55589fea0..33443a527 100644 Binary files a/vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.dll.lib and b/vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.dll.lib differ diff --git a/vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.lib b/vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.lib index 1948c8fca..7c089da19 100644 Binary files a/vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.lib and b/vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.lib differ diff --git a/vendor/wgpu/wgpu.js b/vendor/wgpu/wgpu.js index df3be2e2d..d916de26d 100644 --- a/vendor/wgpu/wgpu.js +++ b/vendor/wgpu/wgpu.js @@ -4,7 +4,7 @@ const STATUS_SUCCESS = 1; const STATUS_ERROR = 2; const ENUMS = { - FeatureName: [undefined, "depth-clip-control", "depth32float-stencil8", "timestamp-query", "texture-compression-bc", "texture-compression-bc-sliced-3d", "texture-compression-etc2", "texture-compression-astc", "texture-compression-astc-sliced-3d", "indirect-first-instance", "shader-f16", "rg11b10ufloat-renderable", "bgra8unorm-storage", "float32-filterable", "float32-blendable", "clip-distances", "dual-source-blending" ], + FeatureName: [undefined, "core-features-and-limits", "depth-clip-control", "depth32float-stencil8", "texture-compression-bc", "texture-compression-bc-sliced-3d", "texture-compression-etc2", "texture-compression-astc", "texture-compression-astc-sliced-3d", "timestamp-query", "indirect-first-instance", "shader-f16", "rg11b10ufloat-renderable", "bgra8unorm-storage", "float32-filterable", "float32-blendable", "clip-distances", "dual-source-blending", "subgroups", "texture-formats-tier1", "texture-formats-tier2", "primitive-index", "texture-component-swizzle" ], StoreOp: [undefined, "store", "discard", ], LoadOp: [undefined, "load", "clear", ], BufferBindingType: [null, undefined, "uniform", "storage", "read-only-storage", ], @@ -12,9 +12,9 @@ const ENUMS = { TextureSampleType: [null, undefined, "float", "unfilterable-float", "depth", "sint", "uint", ], TextureViewDimension: [undefined, "1d", "2d", "2d-array", "cube", "cube-array", "3d", ], StorageTextureAccess: [null, undefined, "write-only", "read-only", "read-write", ], - TextureFormat: [undefined, "r8unorm", "r8snorm", "r8uint", "r8sint", "r16uint", "r16sint", "r16float", "rg8unorm", "rg8snorm", "rg8uint", "rg8sint", "r32float", "r32uint", "r32sint", "rg16uint", "rg16sint", "rg16float", "rgba8unorm", "rgba8unorm-srgb", "rgba8snorm", "rgba8uint", "rgba8sint", "bgra8unorm", "bgra8unorm-srgb", "rgb10a2uint", "rgb10a2unorm", "rg11b10ufloat", "rgb9e5ufloat", "rg32float", "rg32uint", "rg32sint", "rgba16uint", "rgba16sint", "rgba16float", "rgba32float", "rgba32uint", "rgba32sint", "stencil8", "depth16unorm", "depth24plus", "depth24plus-stencil8", "depth32float", "depth32float-stencil8", "bc1-rgba-unorm", "bc1-rgba-unorm-srgb", "bc2-rgba-unorm", "bc2-rgba-unorm-srgb", "bc3-rgba-unorm", "bc3-rgba-unorm-srgb", "bc4-r-unorm", "bc4-r-snorm", "bc5-rg-unorm", "bc5-rg-snorm", "bc6h-rgb-ufloat", "bc6h-rgb-float", "bc7-rgba-unorm", "bc7-rgba-unorm-srgb", "etc2-rgb8unorm", "etc2-rgb8unorm-srgb", "etc2-rgb8a1unorm", "etc2-rgb8a1unorm-srgb", "etc2-rgba8unorm", "etc2-rgba8unorm-srgb", "eac-r11unorm", "eac-r11snorm", "eac-rg11unorm", "eac-rg11snorm", "astc-4x4-unorm", "astc-4x4-unorm-srgb", "astc-5x4-unorm", "astc-5x4-unorm-srgb", "astc-5x5-unorm", "astc-5x5-unorm-srgb", "astc-6x5-unorm", "astc-6x5-unorm-srgb", "astc-6x6-unorm", "astc-6x6-unorm-srgb", "astc-8x5-unorm", "astc-8x5-unorm-srgb", "astc-8x6-unorm", "astc-8x6-unorm-srgb", "astc-8x8-unorm", "astc-8x8-unorm-srgb", "astc-10x5-unorm", "astc-10x5-unorm-srgb", "astc-10x6-unorm", "astc-10x6-unorm-srgb", "astc-10x8-unorm", "astc-10x8-unorm-srgb", "astc-10x10-unorm", "astc-10x10-unorm-srgb", "astc-12x10-unorm", "astc-12x10-unorm-srgb", "astc-12x12-unorm", "astc-12x12-unorm-srgb", ], + TextureFormat: [undefined, "r8unorm", "r8snorm", "r8uint", "r8sint", "r16unorm", "r16snorm", "r16uint", "r16sint", "r16float", "rg8unorm", "rg8snorm", "rg8uint", "rg8sint", "r32float", "r32uint", "r32sint", "rg16unorm", "rg16snorm", "rg16uint", "rg16sint", "rg16float", "rgba8unorm", "rgba8unorm-srgb", "rgba8snorm", "rgba8uint", "rgba8sint", "bgra8unorm", "bgra8unorm-srgb", "rgb10a2uint", "rgb10a2unorm", "rg11b10ufloat", "rgb9e5ufloat", "rg32float", "rg32uint", "rg32sint", "rgba16unorm", "rgba16snorm", "rgba16uint", "rgba16sint", "rgba16float", "rgba32float", "rgba32uint", "rgba32sint", "stencil8", "depth16unorm", "depth24plus", "depth24plus-stencil8", "depth32float", "depth32float-stencil8", "bc1-rgba-unorm", "bc1-rgba-unorm-srgb", "bc2-rgba-unorm", "bc2-rgba-unorm-srgb", "bc3-rgba-unorm", "bc3-rgba-unorm-srgb", "bc4-r-unorm", "bc4-r-snorm", "bc5-rg-unorm", "bc5-rg-snorm", "bc6h-rgb-ufloat", "bc6h-rgb-float", "bc7-rgba-unorm", "bc7-rgba-unorm-srgb", "etc2-rgb8unorm", "etc2-rgb8unorm-srgb", "etc2-rgb8a1unorm", "etc2-rgb8a1unorm-srgb", "etc2-rgba8unorm", "etc2-rgba8unorm-srgb", "eac-r11unorm", "eac-r11snorm", "eac-rg11unorm", "eac-rg11snorm", "astc-4x4-unorm", "astc-4x4-unorm-srgb", "astc-5x4-unorm", "astc-5x4-unorm-srgb", "astc-5x5-unorm", "astc-5x5-unorm-srgb", "astc-6x5-unorm", "astc-6x5-unorm-srgb", "astc-6x6-unorm", "astc-6x6-unorm-srgb", "astc-8x5-unorm", "astc-8x5-unorm-srgb", "astc-8x6-unorm", "astc-8x6-unorm-srgb", "astc-8x8-unorm", "astc-8x8-unorm-srgb", "astc-10x5-unorm", "astc-10x5-unorm-srgb", "astc-10x6-unorm", "astc-10x6-unorm-srgb", "astc-10x8-unorm", "astc-10x8-unorm-srgb", "astc-10x10-unorm", "astc-10x10-unorm-srgb", "astc-12x10-unorm", "astc-12x10-unorm-srgb", "astc-12x12-unorm", "astc-12x12-unorm-srgb", ], QueryType: [undefined, "occlusion", "timestamp", ], - VertexStepMode: [null, undefined, "vertex", "instance", ], + VertexStepMode: [undefined, "vertex", "instance", ], VertexFormat: [undefined, "uint8", "uint8x2", "uint8x4", "sint8", "sint8x2", "sint8x4", "unorm8", "unorm8x2", "unorm8x4", "snorm8", "snorm8x2", "snorm8x4", "uint16", "uint16x2", "uint16x4", "sint16", "sint16x2", "sint16x4", "unorm16", "unorm16x2", "unorm16x4", "snorm16", "snorm16x2", "snorm16x4", "float16", "float16x2", "float16x4", "float32", "float32x2", "float32x3", "float32x4", "uint32", "uint32x2", "uint32x3", "uint32x4", "sint32", "sint32x2", "sint32x3", "sint32x4", "unorm10-10-2", "unorm8x4-bgra" ], PrimitiveTopology: [undefined, "point-list", "line-list", "line-strip", "triangle-list", "triangle-strip", ], IndexFormat: [undefined, "uint16", "uint32", ], @@ -26,7 +26,7 @@ const ENUMS = { CompareFunction: [undefined, "never", "less", "equal", "less-equal", "greater", "not-equal", "greater-equal", "always", ], TextureDimension: [undefined, "1d", "2d", "3d", ], ErrorType: [undefined, "no-error", "validation", "out-of-memory", "internal", "unknown", ], - WGSLLanguageFeatureName: [undefined, "readonly_and_readwrite_storage_textures", "packed_4x8_integer_dot_product", "unrestricted_pointer_parameters", "pointer_composite_access", ], + WGSLLanguageFeatureName: [undefined, "readonly_and_readwrite_storage_textures", "packed_4x8_integer_dot_product", "unrestricted_pointer_parameters", "pointer_composite_access", "uniform_buffer_standard_layout", "subgroup_id", "texture_and_sampler_let", "subgroup_uniformity", "texture_formats_tier1" ], PowerPreference: [undefined, "low-power", "high-performance", ], CompositeAlphaMode: ["auto", "opaque", "premultiplied", "unpremultiplied", "inherit", ], StencilOperation: [undefined, "keep", "zero", "replace", "invert", "increment-clamp", "decrement-clamp", "increment-wrap", "decrement-wrap", ], @@ -34,20 +34,25 @@ const ENUMS = { BlendFactor: [undefined, "zero", "one", "src", "one-minus-src", "src-alpha", "one-minus-src-alpha", "dst", "one-minus-dst", "dst-alpha", "one-minus-dst-alpha", "src-alpha-saturated", "constant", "one-minus-constant", "src1", "one-minus-src1", "src1-alpha", "one-minus-src1-alpha" ], PresentMode: [undefined, "fifo", "fifo-relaxed", "immediate", "mailbox", ], TextureAspect: [undefined, "all", "stencil-only", "depth-only"], - DeviceLostReason: [undefined, "unknown", "destroyed", "instance-dropped", "failed-creation"], + DeviceLostReason: [undefined, "unknown", "destroyed", "callback-cancelled", "failed-creation"], BufferMapState: [undefined, "unmapped", "pending", "mapped"], OptionalBool: [false, true, undefined], + ComponentSwizzle: [undefined, "0", "1", "r", "g", "b", "a"], + PredefinedColorSpace: [undefined, "srgb", "display-p3"], + ToneMappingMode: [undefined, "standard", "extended"], + FeatureLevel: [undefined, "compatibility", "core"], + TextureViewDimensions: [undefined, "1d", "2d", "2d-array", "cube", "cube-array", "3d"], // WARN: used with indexOf to pass to WASM, if we would pass to JS, this needs to use official naming convention (not like Odin enums) like the ones above. BackendType: [undefined, null, "WebGPU", "D3D11", "D3D12", "Metal", "Vulkan", "OpenGL", "OpenGLES"], AdapterType: [undefined, "DiscreteGPU", "IntegratedGPU", "CPU", "Unknown"], - RequestDeviceStatus: [undefined, "Success", "InstanceDropped", "Error", "Unknown"], - MapAsyncStatus: [undefined, "Success", "InstanceDropped", "Error", "Aborted", "Unknown"], - CreatePipelineAsyncStatus: [undefined, "Success", "InstanceDropped", "ValidationError", "InternalError", "Unknown"], - PopErrorScopeStatus: [undefined, "Success", "InstanceDropped", "EmptyStack"], - RequestAdapterStatus: [undefined, "Success", "InstanceDropped", "Unavailable", "Error", "Unknown"], - QueueWorkDoneStatus: [undefined, "Success", "InstanceDropped", "Error", "Unknown"], - CompilationInfoRequestStatus: [undefined, "Success", "InstanceDropped", "Error", "Unknown"], + RequestDeviceStatus: [undefined, "Success", "CallbackCancelled", "Error"], + MapAsyncStatus: [undefined, "Success", "CallbackCancelled", "Error", "Aborted"], + CreatePipelineAsyncStatus: [undefined, "Success", "CallbackCancelled", "ValidationError", "InternalError"], + PopErrorScopeStatus: [undefined, "Success", "CallbackCancelled", "Error"], + RequestAdapterStatus: [undefined, "Success", "CallbackCancelled", "Unavailable", "Error"], + QueueWorkDoneStatus: [undefined, "Success", "CallbackCancelled", "Error"], + CompilationInfoRequestStatus: [undefined, "Success", "CallbackCancelled"], }; /** @@ -71,7 +76,7 @@ class WebGPUInterface { StorageTextureBindingLayout: [16, 4], StringView: [2*this.mem.intSize, this.mem.intSize], ConstantEntry: [this.mem.intSize === 8 ? 32 : 24, 8], - ProgrammableStageDescriptor: [8 + this.mem.intSize*4, this.mem.intSize], + ComputeState: [8 + this.mem.intSize*4, this.mem.intSize], VertexBufferLayout: [16 + this.mem.intSize*2, 8], VertexAttribute: [24, 8], VertexState: [8 + this.mem.intSize*6, this.mem.intSize], @@ -87,7 +92,7 @@ class WebGPUInterface { UncapturedErrorCallbackInfo: [16, 4], RenderPassColorAttachment: [56, 8], BindGroupEntry: [40, 8], - BindGroupLayoutEntry: [80, 8], + BindGroupLayoutEntry: [88, 8], Extent3D: [12, 4], CompilationMessage: [this.mem.intSize == 8 ? 64 : 48, 8], }; @@ -158,6 +163,9 @@ class WebGPUInterface { /** @type {WebGPUObjectManager} */ this.textureViews = new WebGPUObjectManager("TextureView", this.mem); + /** @type {WebGPUObjectManager} */ + this.externalTextures = new WebGPUObjectManager("ExternalTexture", this.mem); + this.zeroMessageAddr = 0; } @@ -341,21 +349,38 @@ class WebGPUInterface { return STATUS_SUCCESS; } - genericGetAdapterInfo(infoPtr) { + /** + * @param {number} infoPtr + * @param {GPUAdapterInfo} info + */ + genericGetAdapterInfo(infoPtr, info) { this.assert(infoPtr != 0); const off = this.struct(infoPtr); off(4); // nextInChain - off(this.sizes.StringView); // vendor - off(this.sizes.StringView); // architecture - off(this.sizes.StringView); // device - off(this.sizes.StringView); // description + + const storeString = (start, str) => { + const len = new TextEncoder().encode(str).length; + const strAddr = this.mem.exports.wgpu_alloc(len); + this.mem.storeString(strAddr, str); + + this.mem.storeI32(start, strAddr); + this.mem.storeUint(start + this.mem.intSize, len); + }; + + storeString(off(this.sizes.StringView), info.vendor); + storeString(off(this.sizes.StringView), info.architecture); + storeString(off(this.sizes.StringView), info.device); + storeString(off(this.sizes.StringView), info.description); this.mem.storeI32(off(4), ENUMS.BackendType.indexOf("WebGPU")); this.mem.storeI32(off(4), ENUMS.AdapterType.indexOf("Unknown")); - // NOTE: I don't think getting the other fields in this struct is possible. - // `adapter.requestAdapterInfo` is deprecated. + off(4); // vendorID + off(4); // deviceID + + this.mem.storeI32(off(4), info.subGroupMinSize); + this.mem.storeI32(off(4), info.subGroupMaxSize); return STATUS_SUCCESS; } @@ -461,15 +486,15 @@ class WebGPUInterface { /** * @param {number} ptr - * @returns {GPUComputePassTimestampWrites} */ - ComputePassTimestampWritesPtr(ptr) { + PassTimestampWritesPtr(ptr) { const start = this.mem.loadPtr(ptr); if (start == 0) { return undefined; } const off = this.struct(start); + off(4); // nextInChain return { querySet: this.querySets.get(this.mem.loadPtr(off(4))), beginningOfPassWriteIndex: this.mem.loadU32(off(4)), @@ -529,7 +554,7 @@ class WebGPUInterface { } const off = this.struct(start); - + off(4); // nextInChain return { view: this.textureViews.get(this.mem.loadPtr(off(4))), depthLoadOp: this.enumeration("LoadOp", off(4)), @@ -556,14 +581,6 @@ class WebGPUInterface { return this.querySets.get(ptr); } - /** - * @param {number} ptr - * @returns {GPURenderPassTimestampWrites} - */ - RenderPassTimestampWritesPtr(ptr) { - return this.ComputePassTimestampWritesPtr(ptr); - } - /** * @param {number} start * @returns {GPUOrigin3D} @@ -626,12 +643,13 @@ class WebGPUInterface { off(4); const entry = { - binding: this.mem.loadU32(off(4)), - visibility: this.mem.loadU64(off(8)), - buffer: this.BufferBindingLayout(off(this.sizes.BufferBindingLayout)), - sampler: this.SamplerBindingLayout(off(this.sizes.SamplerBindingLayout)), - texture: this.TextureBindingLayout(off(this.sizes.TextureBindingLayout)), - storageTexture: this.StorageTextureBindingLayout(off(this.sizes.StorageTextureBindingLayout)), + binding: this.mem.loadU32(off(4)), + visibility: this.mem.loadU64(off(8)), + bindingArraySize: this.mem.loadU32(off(4)), + buffer: this.BufferBindingLayout(off(this.sizes.BufferBindingLayout)), + sampler: this.SamplerBindingLayout(off(this.sizes.SamplerBindingLayout)), + texture: this.TextureBindingLayout(off(this.sizes.TextureBindingLayout)), + storageTexture: this.StorageTextureBindingLayout(off(this.sizes.StorageTextureBindingLayout)), }; if (!entry.buffer.type) { entry.buffer = undefined; @@ -696,9 +714,9 @@ class WebGPUInterface { /** * @param {number} start - * @returns {GPUProgrammableStage} + * @returns {GPUComputeState} */ - ProgrammableStageDescriptor(start) { + ComputeState(start) { const off = this.struct(start); off(4); @@ -749,7 +767,7 @@ class WebGPUInterface { return { label: label, layout: layoutIdx > 0 ? this.pipelineLayouts.get(layoutIdx) : "auto", - compute: this.ProgrammableStageDescriptor(off(this.sizes.ProgrammableStageDescriptor)), + compute: this.ComputeState(off(this.sizes.ComputeState)), }; } @@ -793,11 +811,9 @@ class WebGPUInterface { */ VertexBufferLayout(start) { const off = this.struct(start); + off(4); // nextInChain const stepMode = this.enumeration("VertexStepMode", off(4)); - if (stepMode == null) { - return null; - } return { arrayStride: this.mem.loadU64(off(8)), @@ -817,6 +833,7 @@ class WebGPUInterface { */ VertexAttribute(start) { const off = this.struct(start); + off(4); // nextInChain return { format: this.enumeration("VertexFormat", off(4)), offset: this.mem.loadU64(off(8)), @@ -1117,12 +1134,26 @@ class WebGPUInterface { }, /** - * @param {number} capabilitiesPtr - * @returns {number} + * @param {number} featuresPtr */ - wgpuGetInstanceCapabilities: (capabilitiesPtr) => { + wgpuGetInstanceFeatures: (featuresPtr) => { // TODO: implement (futures). - return STATUS_ERROR; + }, + + /** + * @param {number} limitsPtr + */ + wgpuGetInstanceLimits: (limitsPtr) => { + // TODO: implement (futures). + }, + + /** + * @param {number} feature + * @returns {boolean} + */ + wgpuHasInstanceFeature: (feature) => { + // TODO: implement (futures). + return false; }, /** @@ -1389,6 +1420,30 @@ class WebGPUInterface { return BigInt(0); }, + /** + * @param {number} bufferIdx + * @param {number|BigInt} offset + * @param {number} ptr + * @param {number|BigInt} size + * @return {number} + */ + wgpuBufferReadMappedRange: (bufferIdx, offset, ptr, size) => { + const buffer = this.buffers.get(bufferIdx); + offset = this.unwrapBigInt(offset); + size = this.unwrapBigInt(size); + + this.assert(!buffer.mapping, "buffer already mapped"); + + const range = buffer.buffer.getMappedRange(offset, size); + + const mapping = new Uint8Array(this.mem.memory.buffer, ptr, size); + mapping.set(new Uint8Array(range)); + + buffer.mapping = { range: range, ptr: ptr, size: range.byteLength }; + + return STATUS_SUCCESS; + }, + /** * @param {number} bufferIdx * @param {number} labelPtr @@ -1415,6 +1470,30 @@ class WebGPUInterface { buffer.mapping = null; }, + /** + * @param {number} bufferIdx + * @param {number|BigInt} offset + * @param {number} ptr + * @param {number|BigInt} size + * @return {number} + */ + wgpuBufferWriteMappedRange: (bufferIdx, offset, ptr, size) => { + const buffer = this.buffers.get(bufferIdx); + offset = this.unwrapBigInt(offset); + size = this.unwrapBigInt(size); + + this.assert(!buffer.mapping, "buffer already mapped"); + + const range = buffer.buffer.getMappedRange(offset, size); + + const mapping = new Uint8Array(this.mem.memory.buffer, ptr, size); + (new Uint8Array(range)).set(mapping); + + buffer.mapping = { range: range, ptr: ptr, size: range.byteLength }; + + return STATUS_SUCCESS; + }, + ...this.buffers.interface(), /* ---------------------- CommandBuffer ---------------------- */ @@ -1438,7 +1517,7 @@ class WebGPUInterface { off(4); descriptor = { label: this.StringView(off(this.sizes.StringView)), - timestampWrites: this.ComputePassTimestampWritesPtr(off(4)), + timestampWrites: this.PassTimestampWritesPtr(off(4)), }; } @@ -1478,7 +1557,7 @@ class WebGPUInterface { ), depthStencilAttachment: this.RenderPassDepthStencilAttachmentPtr(off(4)), occlusionQuerySet: this.QuerySet(off(4)), - timestampWrites: this.RenderPassTimestampWritesPtr(off(4)), + timestampWrites: this.PassTimestampWritesPtr(off(4)), maxDrawCount: maxDrawCount, }; @@ -1881,7 +1960,7 @@ class WebGPUInterface { device.createComputePipelineAsync(this.ComputePipelineDescriptor(descriptorPtr)) .catch((e) => { const messageAddr = this.makeMessageArg(e.message); - this.callCallback(callbackInfo, [ENUMS.CreatePipelineAsyncStatus.indexOf("Unknown"), 0, messageAddr]); + this.callCallback(callbackInfo, [ENUMS.CreatePipelineAsyncStatus.indexOf("ValidationError"), 0, messageAddr]); this.mem.exports.wgpu_free(messageAddr); }) .then((computePipeline) => { @@ -2185,6 +2264,11 @@ class WebGPUInterface { const callbackInfo = this.CallbackInfo(callbackInfoPtr); device.popErrorScope() + .catch((e) => { + const messageAddr = this.makeMessageArg(e.message); + this.callCallback(callbackInfo, [ENUMS.PopErrorScopeStatus.indexOf("Error"), ENUMS.ErrorType.indexOf("unknown"), messageAddr]); + this.mem.exports.wgpu_free(messageAddr); + }) .then((error) => { if (!error) { this.callCallback(callbackInfo, [ENUMS.PopErrorScopeStatus.indexOf("Success"), ENUMS.ErrorType.indexOf("no-error"), this.zeroMessageArg()]); @@ -2222,6 +2306,10 @@ class WebGPUInterface { ...this.devices.interface(true), + /* ---------------------- ExternalTexture ---------------------- */ + + ...this.externalTextures.interface(true), + /* ---------------------- Instance ---------------------- */ /** @@ -2261,7 +2349,6 @@ class WebGPUInterface { /** * @param {number} instanceIdx * @param {number} featurePtr - * @returns {number} */ wgpuInstanceGetWGSLLanguageFeatures: (instanceIdx, featuresPtr) => { this.assert(featuresPtr != 0); @@ -2292,8 +2379,6 @@ class WebGPUInterface { for (let i = 0; i < availableFeatures.length; i += 1) { this.mem.storeI32(off(4), availableFeatures[i]); } - - return STATUS_SUCCESS; }, /** @@ -2325,11 +2410,22 @@ class WebGPUInterface { let options; if (optionsPtr != 0) { const off = this.struct(optionsPtr); - off(4); // nextInChain - off(4); // featureLevel + + let xrCompatible = undefined; + const nextInChain = this.mem.loadPtr(off(4)); + if (nextInChain != 0) { + const nextInChainType = this.mem.loadI32(nextInChain + 4); + // RequestAdapterWebXROptions = 0x0000000B, + if (nextInChainType == 0x0000000B) { + xrCompatible = this.mem.loadB32(nextInChain + 8); + } + } + options = { + featureLevel: this.enumeration("FeatureLevel", off(4)), powerPreference: this.enumeration("PowerPreference", off(4)), forceFallbackAdapter: this.mem.loadB32(off(4)), + xrCompatible: xrCompatible, }; } @@ -2405,10 +2501,12 @@ class WebGPUInterface { queue.onSubmittedWorkDone() .catch((e) => { console.warn(e); - this.callCallback(callbackInfo, [ENUMS.QueueWorkDoneStatus.indexOf("Error")]); + const messageAddr = this.makeMessageArg(e.message); + this.callCallback(callbackInfo, [ENUMS.QueueWorkDoneStatus.indexOf("Error"), messageAddr]); + this.mem.exports.wgpu_free(messageAddr); }) .then(() => { - this.callCallback(callbackInfo, [ENUMS.QueueWorkDoneStatus.indexOf("Success")]); + this.callCallback(callbackInfo, [ENUMS.QueueWorkDoneStatus.indexOf("Success"), this.zeroMessageArg()]); }); // TODO: futures? @@ -2920,7 +3018,7 @@ class WebGPUInterface { shaderModule.getCompilationInfo() .catch((e) => { console.warn(e); - this.callCallback(callbackInfo, [ENUMS.CompilationInfoRequestStatus.indexOf("Error"), null]); + this.callCallback(callbackInfo, [ENUMS.CompilationInfoRequestStatus.indexOf("CallbackCancelled"), null]); }) .then((compilationInfo) => { const ptrsToFree = []; @@ -2975,6 +3073,11 @@ class WebGPUInterface { this.mem.exports.wgpu_free(supportedFeaturesPtr); }, + /* ---------------------- SupportedInstanceFeatures ---------------------- */ + + wgpuSupportedInstanceFeaturesFreeMembers: (supportedFeaturesCount, supportedFeaturesPtr) => { + }, + /* ---------------------- SupportedWGSLLanguageFeatures ---------------------- */ wgpuSupportedWGSLLanguageFeaturesFreeMembers: (supportedFeaturesCount, supportedFeaturesPtr) => { @@ -2992,7 +3095,23 @@ class WebGPUInterface { const context = surface.getContext("webgpu"); const off = this.struct(configPtr); - off(4); + + let colorSpace = undefined; + let toneMapping = undefined; + const nextInChain = this.mem.loadPtr(off(4)); + if (nextInChain != 0) { + const colorManagementOff = this.struct(nextInChain); + colorManagementOff(4); // next + const nextInChainType = this.mem.loadI32(colorManagementOff(4)); + // SurfaceColorManagement = 0x0000000A, + if (nextInChainType == 0x0000000A) { + colorSpace = this.enumeration("PredefinedColorSpace", colorManagementOff(4)) ?? "srgb"; + toneMapping = { + mode: this.enumeration("ToneMappingMode", colorManagementOff(4)) ?? "standard", + }; + } + } + const device = this.devices.get(this.mem.loadPtr(off(4))); const format = this.enumeration("TextureFormat", off(4)); const usage = this.mem.loadU64(off(8)); @@ -3019,6 +3138,8 @@ class WebGPUInterface { viewFormats: viewFormats, alphaMode: alphaMode, presentMode: presentMode, + colorSpace: colorSpace, + toneMapping: toneMapping, }; context.configure(config); @@ -3071,7 +3192,7 @@ class WebGPUInterface { const textureIdx = this.textures.create(texture); this.mem.storeI32(texturePtr + 4, textureIdx); - // TODO: determine suboptimal and/or status. + // TODO: determine status somehow? }, /** @@ -3130,7 +3251,27 @@ class WebGPUInterface { let descriptor; if (descriptorPtr != 0) { const off = this.struct(descriptorPtr); - off(4); + + let swizzle = undefined; + const nextInChain = this.mem.loadPtr(off(4)); + if (nextInChain != 0) { + const swizzleOff = this.struct(nextInChain); + swizzleOff(4); // next + const nextInChainType = this.mem.loadI32(swizzleOff(4)); + // TextureComponentSwizzle = 0x00000016, + if (nextInChainType == 0x00000016) { + const r = this.enumeration("ComponentSwizzle", swizzleOff(4)); + this.assert(r !== undefined); + const g = this.enumeration("ComponentSwizzle", swizzleOff(4)); + this.assert(g !== undefined); + const b = this.enumeration("ComponentSwizzle", swizzleOff(4)); + this.assert(b !== undefined); + const a = this.enumeration("ComponentSwizzle", swizzleOff(4)); + this.assert(a !== undefined); + swizzle = r + g + b + a; + } + } + descriptor = { label: this.StringView(off(this.sizes.StringView)), format: this.enumeration("TextureFormat", off(4)), @@ -3141,6 +3282,7 @@ class WebGPUInterface { arrayLayerCount: this.mem.loadU32(off(4)), aspect: this.enumeration("TextureAspect", off(4)), usage: this.mem.loadU64(off(8)), + swizzle: swizzle, }; if (descriptor.arrayLayerCount == 0xFFFFFFFF) { descriptor.arrayLayerCount = undefined; @@ -3216,6 +3358,15 @@ class WebGPUInterface { return texture.sampleCount; }, + /** + * @param {number} textureIdx + * @returns {number} + */ + wgpuTextureGetTextureBindingViewDimension: (textureIdx) => { + const texture = this.textures.get(textureIdx); + return ENUMS.TextureViewDimension.indexOf(texture.textureBindingViewDimension); + }, + /** * @param {number} textureIdx * @returns {number} diff --git a/vendor/wgpu/wgpu.odin b/vendor/wgpu/wgpu.odin index 6da164ae0..796cebec7 100644 --- a/vendor/wgpu/wgpu.odin +++ b/vendor/wgpu/wgpu.odin @@ -13,7 +13,7 @@ when ODIN_OS == .Windows { @(private) LIB :: "lib/wgpu-windows-" + ARCH + "-msvc-" + TYPE + "/lib/wgpu_native" + EXT when !#exists(LIB) { - #panic("Could not find the compiled WGPU Native library at '" + #directory + LIB + "', these can be downloaded from https://github.com/gfx-rs/wgpu-native/releases/tag/v27.0.2.0, make sure to read the docs at '" + #directory + "doc.odin'") + #panic("Could not find the compiled WGPU Native library at '" + #directory + LIB + "', these can be downloaded from https://github.com/gfx-rs/wgpu-native/releases/tag/v29.0.0.0, make sure to read the docs at '" + #directory + "doc.odin'") } @(export) @@ -39,7 +39,7 @@ when ODIN_OS == .Windows { @(private) LIB :: "lib/wgpu-macos-" + ARCH + "-" + TYPE + "/lib/libwgpu_native" + EXT when !#exists(LIB) { - #panic("Could not find the compiled WGPU Native library at '" + #directory + LIB + "', these can be downloaded from https://github.com/gfx-rs/wgpu-native/releases/tag/v27.0.2.0, make sure to read the docs at '" + #directory + "doc.odin'") + #panic("Could not find the compiled WGPU Native library at '" + #directory + LIB + "', these can be downloaded from https://github.com/gfx-rs/wgpu-native/releases/tag/v29.0.0.0, make sure to read the docs at '" + #directory + "doc.odin'") } @(export) @@ -56,7 +56,7 @@ when ODIN_OS == .Windows { @(private) LIB :: "lib/wgpu-linux-" + ARCH + "-" + TYPE + "/lib/libwgpu_native" + EXT when !#exists(LIB) { - #panic("Could not find the compiled WGPU Native library at '" + #directory + LIB + "', these can be downloaded from https://github.com/gfx-rs/wgpu-native/releases/tag/v27.0.2.0, make sure to read the docs at '" + #directory + "doc.odin'") + #panic("Could not find the compiled WGPU Native library at '" + #directory + LIB + "', these can be downloaded from https://github.com/gfx-rs/wgpu-native/releases/tag/v29.0.0.0, make sure to read the docs at '" + #directory + "doc.odin'") } @(export) @@ -81,6 +81,7 @@ WHOLE_MAP_SIZE :: max(uint) WHOLE_SIZE :: max(u64) Flags :: u64 +Bool :: b32 StringView :: string @@ -108,6 +109,7 @@ ShaderModule :: distinct rawptr Surface :: distinct rawptr Texture :: distinct rawptr TextureView :: distinct rawptr +ExternalTexture :: distinct rawptr AdapterType :: enum i32 { DiscreteGPU = 0x00000001, @@ -198,9 +200,7 @@ CompareFunction :: enum i32 { CompilationInfoRequestStatus :: enum i32 { Success = 0x00000001, - InstanceDropped = 0x00000002, - Error = 0x00000003, - Unknown = 0x00000004, + CallbackCancelled = 0x00000002, } CompilationMessageType :: enum i32 { @@ -209,6 +209,16 @@ CompilationMessageType :: enum i32 { Info = 0x00000003, } +ComponentSwizzle :: enum i32 { + Undefined, + Zero, + One, + R, + G, + B, + A, +} + CompositeAlphaMode :: enum i32 { Auto = 0x00000000, Opaque = 0x00000001, @@ -219,10 +229,9 @@ CompositeAlphaMode :: enum i32 { CreatePipelineAsyncStatus :: enum i32 { Success = 0x00000001, - InstanceDropped = 0x00000002, + CallbackCancelled = 0x00000002, ValidationError = 0x00000003, InternalError = 0x00000004, - Unknown = 0x00000005, } CullMode :: enum i32 { @@ -236,7 +245,7 @@ DeviceLostReason :: enum i32 { Undefined = 0x00000000, Unknown = 0x00000001, Destroyed = 0x00000002, - InstanceDropped = 0x00000003, + CallbackCancelled = 0x00000003, FailedCreation = 0x00000004, } @@ -255,6 +264,7 @@ ErrorType :: enum i32 { } FeatureLevel :: enum i32 { + Undefined, Compatibility = 0x00000001, Core = 0x00000002, } @@ -262,25 +272,31 @@ FeatureLevel :: enum i32 { FeatureName :: enum i32 { // WebGPU. Undefined = 0x00000000, - DepthClipControl = 0x00000001, - Depth32FloatStencil8 = 0x00000002, - TimestampQuery = 0x00000003, + CoreFeaturesAndLimits = 0x00000001, + DepthClipControl = 0x00000002, + Depth32FloatStencil8 = 0x00000003, TextureCompressionBC = 0x00000004, TextureCompressionBCSliced3D = 0x00000005, TextureCompressionETC2 = 0x00000006, TextureCompressionASTC = 0x00000007, TextureCompressionASTCSliced3D = 0x00000008, - IndirectFirstInstance = 0x00000009, - ShaderF16 = 0x0000000A, - RG11B10UfloatRenderable = 0x0000000B, - BGRA8UnormStorage = 0x0000000C, - Float32Filterable = 0x0000000D, - Float32Blendable = 0x0000000E, - ClipDistances = 0x0000000F, - DualSourceBlending = 0x00000010, + TimestampQuery = 0x00000009, + IndirectFirstInstance = 0x0000000A, + ShaderF16 = 0x0000000B, + RG11B10UfloatRenderable = 0x0000000C, + BGRA8UnormStorage = 0x0000000D, + Float32Filterable = 0x0000000E, + Float32Blendable = 0x0000000F, + ClipDistances = 0x00000010, + DualSourceBlending = 0x00000011, + Subgroups = 0x00000012, + TextureFormatsTier1 = 0x00000013, + TextureFormatsTier2 = 0x00000014, + PrimitiveIndex = 0x00000015, + TextureComponentSwizzle = 0x00000016, // Native. - PushConstants = 0x00030001, + Immediates = 0x00030001, TextureAdapterSpecificFormatFeatures, MultiDrawIndirectCount = 0x00030004, VertexWritableStorage, @@ -308,8 +324,7 @@ FeatureName :: enum i32 { RayQuery = 0x0003001C, ShaderF64, ShaderI16, - ShaderPrimitiveIndex, - ShaderEarlyDepthTest, + ShaderEarlyDepthTest = 0x00030020, Subgroup, SubgroupVertex, SubgroupBarrier, @@ -336,6 +351,12 @@ IndexFormat :: enum i32 { Uint32 = 0x00000002, } +InstanceFeatureName :: enum i32 { + TimedWaitAny = 1, + ShaderSourceSPIRV = 2, + MultipleDevicesPerAdapter = 3, +} + LoadOp :: enum i32 { Undefined = 0x00000000, Load = 0x00000001, @@ -344,10 +365,9 @@ LoadOp :: enum i32 { MapAsyncStatus :: enum i32 { Success = 0x00000001, - InstanceDropped = 0x00000002, + CallbackCancelled = 0x00000002, Error = 0x00000003, Aborted = 0x00000004, - Unknown = 0x00000005, } MipmapFilterMode :: enum i32 { @@ -364,8 +384,8 @@ OptionalBool :: enum i32 { PopErrorScopeStatus :: enum i32 { Success = 0x00000001, - InstanceDropped = 0x00000002, - EmptyStack = 0x00000003, + CallbackCancelled = 0x00000002, + Error = 0x00000003, } PowerPreference :: enum i32 { @@ -374,6 +394,11 @@ PowerPreference :: enum i32 { HighPerformance = 0x00000002, } +PredefinedColorSpace :: enum i32 { + SRGB = 1, + DisplayP3, +} + PresentMode :: enum i32 { Undefined = 0x00000000, Fifo = 0x00000001, @@ -402,54 +427,21 @@ QueryType :: enum i32 { QueueWorkDoneStatus :: enum i32 { Success = 0x00000001, - InstanceDropped = 0x00000002, + CallbackCancelled = 0x00000002, Error = 0x00000003, - Unknown = 0x00000004, } RequestAdapterStatus :: enum i32 { Success = 0x00000001, - InstanceDropped = 0x00000002, + CallbackCancelled = 0x00000002, Unavailable = 0x00000003, Error = 0x00000004, - Unknown = 0x00000005, } RequestDeviceStatus :: enum i32 { Success = 0x00000001, - InstanceDropped = 0x00000002, + CallbackCancelled = 0x00000002, Error = 0x00000003, - Unknown = 0x00000004, -} - -SType :: enum i32 { - // WebGPU. - ShaderSourceSPIRV = 0x00000001, - ShaderSourceWGSL = 0x00000002, - RenderPassMaxDrawCount = 0x00000003, - SurfaceSourceMetalLayer = 0x00000004, - SurfaceSourceWindowsHWND = 0x00000005, - SurfaceSourceXlibWindow = 0x00000006, - SurfaceSourceWaylandSurface = 0x00000007, - SurfaceSourceAndroidNativeWindow = 0x00000008, - SurfaceSourceXCBWindow = 0x00000009, - - // Native. - DeviceExtras = 0x00030001, - NativeLimits, - PipelineLayoutExtras, - ShaderSourceGLSL, - SupportedLimitsExtras, - InstanceExtras, - BindGroupEntryExtras, - BindGroupLayoutEntryExtras, - QuerySetDescriptorExtras, - SurfaceConfigurationExtras, - SurfaceSourceSwapChainPanel, - PrimitiveStateExtras, - - // Odin. - SurfaceSourceCanvasHTMLSelector = 0x00040001, } SamplerBindingType :: enum i32 { @@ -491,15 +483,53 @@ StoreOp :: enum i32 { Discard = 0x00000002, } +SType :: enum i32 { + // WebGPU. + ShaderSourceSPIRV = 0x00000001, + ShaderSourceWGSL = 0x00000002, + RenderPassMaxDrawCount = 0x00000003, + SurfaceSourceMetalLayer = 0x00000004, + SurfaceSourceWindowsHWND = 0x00000005, + SurfaceSourceXlibWindow = 0x00000006, + SurfaceSourceWaylandSurface = 0x00000007, + SurfaceSourceAndroidNativeWindow = 0x00000008, + SurfaceSourceXCBWindow = 0x00000009, + SurfaceColorManagement = 0x0000000A, + RequestAdapterWebXROptions = 0x0000000B, + TextureComponentSwizzleDescriptor = 0x0000000C, + ExternalTextureBindingLayout = 0x0000000D, + ExternalTextureBindingEntry = 0x0000000E, + CompatibilityModeLimits = 0x0000000F, + TextureBindingViewDimension = 0x00000010, + + // Native. + DeviceExtras = 0x00030001, + NativeLimits, + PipelineLayoutExtras, + ShaderSourceGLSL, + SupportedLimitsExtras, + InstanceExtras, + BindGroupEntryExtras, + BindGroupLayoutEntryExtras, + QuerySetDescriptorExtras, + SurfaceConfigurationExtras, + SurfaceSourceSwapChainPanel, + PrimitiveStateExtras, + + // Odin. + SurfaceSourceCanvasHTMLSelector = 0x00040001, +} + SurfaceGetCurrentTextureStatus :: enum i32 { SuccessOptimal = 0x00000001, SuccessSuboptimal = 0x00000002, Timeout = 0x00000003, Outdated = 0x00000004, Lost = 0x00000005, - OutOfMemory = 0x00000006, - DeviceLost = 0x00000007, - Error = 0x00000008, + Error = 0x00000006, + + // Native. + Occluded = 0x00030001, } TextureAspect :: enum i32 { @@ -522,108 +552,113 @@ TextureFormat :: enum i32 { R8Snorm = 0x00000002, R8Uint = 0x00000003, R8Sint = 0x00000004, - R16Uint = 0x00000005, - R16Sint = 0x00000006, - R16Float = 0x00000007, - RG8Unorm = 0x00000008, - RG8Snorm = 0x00000009, - RG8Uint = 0x0000000A, - RG8Sint = 0x0000000B, - R32Float = 0x0000000C, - R32Uint = 0x0000000D, - R32Sint = 0x0000000E, - RG16Uint = 0x0000000F, - RG16Sint = 0x00000010, - RG16Float = 0x00000011, - RGBA8Unorm = 0x00000012, - RGBA8UnormSrgb = 0x00000013, - RGBA8Snorm = 0x00000014, - RGBA8Uint = 0x00000015, - RGBA8Sint = 0x00000016, - BGRA8Unorm = 0x00000017, - BGRA8UnormSrgb = 0x00000018, - RGB10A2Uint = 0x00000019, - RGB10A2Unorm = 0x0000001A, - RG11B10Ufloat = 0x0000001B, - RGB9E5Ufloat = 0x0000001C, - RG32Float = 0x0000001D, - RG32Uint = 0x0000001E, - RG32Sint = 0x0000001F, - RGBA16Uint = 0x00000020, - RGBA16Sint = 0x00000021, - RGBA16Float = 0x00000022, - RGBA32Float = 0x00000023, - RGBA32Uint = 0x00000024, - RGBA32Sint = 0x00000025, - Stencil8 = 0x00000026, - Depth16Unorm = 0x00000027, - Depth24Plus = 0x00000028, - Depth24PlusStencil8 = 0x00000029, - Depth32Float = 0x0000002A, - Depth32FloatStencil8 = 0x0000002B, - BC1RGBAUnorm = 0x0000002C, - BC1RGBAUnormSrgb = 0x0000002D, - BC2RGBAUnorm = 0x0000002E, - BC2RGBAUnormSrgb = 0x0000002F, - BC3RGBAUnorm = 0x00000030, - BC3RGBAUnormSrgb = 0x00000031, - BC4RUnorm = 0x00000032, - BC4RSnorm = 0x00000033, - BC5RGUnorm = 0x00000034, - BC5RGSnorm = 0x00000035, - BC6HRGBUfloat = 0x00000036, - BC6HRGBFloat = 0x00000037, - BC7RGBAUnorm = 0x00000038, - BC7RGBAUnormSrgb = 0x00000039, - ETC2RGB8Unorm = 0x0000003A, - ETC2RGB8UnormSrgb = 0x0000003B, - ETC2RGB8A1Unorm = 0x0000003C, - ETC2RGB8A1UnormSrgb = 0x0000003D, - ETC2RGBA8Unorm = 0x0000003E, - ETC2RGBA8UnormSrgb = 0x0000003F, - EACR11Unorm = 0x00000040, - EACR11Snorm = 0x00000041, - EACRG11Unorm = 0x00000042, - EACRG11Snorm = 0x00000043, - ASTC4x4Unorm = 0x00000044, - ASTC4x4UnormSrgb = 0x00000045, - ASTC5x4Unorm = 0x00000046, - ASTC5x4UnormSrgb = 0x00000047, - ASTC5x5Unorm = 0x00000048, - ASTC5x5UnormSrgb = 0x00000049, - ASTC6x5Unorm = 0x0000004A, - ASTC6x5UnormSrgb = 0x0000004B, - ASTC6x6Unorm = 0x0000004C, - ASTC6x6UnormSrgb = 0x0000004D, - ASTC8x5Unorm = 0x0000004E, - ASTC8x5UnormSrgb = 0x0000004F, - ASTC8x6Unorm = 0x00000050, - ASTC8x6UnormSrgb = 0x00000051, - ASTC8x8Unorm = 0x00000052, - ASTC8x8UnormSrgb = 0x00000053, - ASTC10x5Unorm = 0x00000054, - ASTC10x5UnormSrgb = 0x00000055, - ASTC10x6Unorm = 0x00000056, - ASTC10x6UnormSrgb = 0x00000057, - ASTC10x8Unorm = 0x00000058, - ASTC10x8UnormSrgb = 0x00000059, - ASTC10x10Unorm = 0x0000005A, - ASTC10x10UnormSrgb = 0x0000005B, - ASTC12x10Unorm = 0x0000005C, - ASTC12x10UnormSrgb = 0x0000005D, - ASTC12x12Unorm = 0x0000005E, - ASTC12x12UnormSrgb = 0x0000005F, + R16Unorm = 0x00000005, + R16Snorm = 0x00000006, + R16Uint = 0x00000007, + R16Sint = 0x00000008, + R16Float = 0x00000009, + RG8Unorm = 0x0000000A, + RG8Snorm = 0x0000000B, + RG8Uint = 0x0000000C, + RG8Sint = 0x0000000D, + R32Float = 0x0000000E, + R32Uint = 0x0000000F, + R32Sint = 0x00000010, + RG16Unorm = 0x00000011, + RG16Snorm = 0x00000012, + RG16Uint = 0x00000013, + RG16Sint = 0x00000014, + RG16Float = 0x00000015, + RGBA8Unorm = 0x00000016, + RGBA8UnormSrgb = 0x00000017, + RGBA8Snorm = 0x00000018, + RGBA8Uint = 0x00000019, + RGBA8Sint = 0x0000001A, + BGRA8Unorm = 0x0000001B, + BGRA8UnormSrgb = 0x0000001C, + RGB10A2Uint = 0x0000001D, + RGB10A2Unorm = 0x0000001E, + RG11B10Ufloat = 0x0000001F, + RGB9E5Ufloat = 0x00000020, + RG32Float = 0x00000021, + RG32Uint = 0x00000022, + RG32Sint = 0x00000023, + RGBA16Unorm = 0x00000024, + RGBA16Snorm = 0x00000025, + RGBA16Uint = 0x00000026, + RGBA16Sint = 0x00000027, + RGBA16Float = 0x00000028, + RGBA32Float = 0x00000029, + RGBA32Uint = 0x0000002A, + RGBA32Sint = 0x0000002B, + Stencil8 = 0x0000002C, + Depth16Unorm = 0x0000002D, + Depth24Plus = 0x0000002E, + Depth24PlusStencil8 = 0x0000002F, + Depth32Float = 0x00000030, + Depth32FloatStencil8 = 0x00000031, + BC1RGBAUnorm = 0x00000032, + BC1RGBAUnormSrgb = 0x00000033, + BC2RGBAUnorm = 0x00000034, + BC2RGBAUnormSrgb = 0x00000035, + BC3RGBAUnorm = 0x00000036, + BC3RGBAUnormSrgb = 0x00000037, + BC4RUnorm = 0x00000038, + BC4RSnorm = 0x00000039, + BC5RGUnorm = 0x0000003A, + BC5RGSnorm = 0x0000003B, + BC6HRGBUfloat = 0x0000003C, + BC6HRGBFloat = 0x0000003D, + BC7RGBAUnorm = 0x0000003E, + BC7RGBAUnormSrgb = 0x0000003F, + ETC2RGB8Unorm = 0x00000040, + ETC2RGB8UnormSrgb = 0x00000041, + ETC2RGB8A1Unorm = 0x00000042, + ETC2RGB8A1UnormSrgb = 0x00000043, + ETC2RGBA8Unorm = 0x00000044, + ETC2RGBA8UnormSrgb = 0x00000045, + EACR11Unorm = 0x00000046, + EACR11Snorm = 0x00000047, + EACRG11Unorm = 0x00000048, + EACRG11Snorm = 0x00000049, + ASTC4x4Unorm = 0x0000004A, + ASTC4x4UnormSrgb = 0x0000004B, + ASTC5x4Unorm = 0x0000004C, + ASTC5x4UnormSrgb = 0x0000004D, + ASTC5x5Unorm = 0x0000004E, + ASTC5x5UnormSrgb = 0x0000004F, + ASTC6x5Unorm = 0x00000050, + ASTC6x5UnormSrgb = 0x00000051, + ASTC6x6Unorm = 0x00000052, + ASTC6x6UnormSrgb = 0x00000053, + ASTC8x5Unorm = 0x00000054, + ASTC8x5UnormSrgb = 0x00000055, + ASTC8x6Unorm = 0x00000056, + ASTC8x6UnormSrgb = 0x00000057, + ASTC8x8Unorm = 0x00000058, + ASTC8x8UnormSrgb = 0x00000059, + ASTC10x5Unorm = 0x0000005A, + ASTC10x5UnormSrgb = 0x0000005B, + ASTC10x6Unorm = 0x0000005C, + ASTC10x6UnormSrgb = 0x0000005D, + ASTC10x8Unorm = 0x0000005E, + ASTC10x8UnormSrgb = 0x0000005F, + ASTC10x10Unorm = 0x00000060, + ASTC10x10UnormSrgb = 0x00000061, + ASTC12x10Unorm = 0x00000062, + ASTC12x10UnormSrgb = 0x00000063, + ASTC12x12Unorm = 0x00000064, + ASTC12x12UnormSrgb = 0x00000065, // Native. // From FeatureName.TextureFormat16bitNorm - R16Unorm = 0x00030001, - R16Snorm, + NativeR16Unorm = 0x00030001, + NativeR16Snorm, Rg16Unorm, Rg16Snorm, Rgba16Unorm, Rgba16Snorm, - // From FeatureName.TextureFormatNv12 NV12, P010, } @@ -648,6 +683,11 @@ TextureViewDimension :: enum i32 { _3D = 0x00000006, } +ToneMappingMode :: enum i32 { + Standard = 1, + Extended, +} + VertexFormat :: enum i32 { Uint8 = 0x00000001, Uint8x2 = 0x00000002, @@ -693,10 +733,15 @@ VertexFormat :: enum i32 { } VertexStepMode :: enum i32 { - VertexBufferNotUsed = 0x00000000, - Undefined = 0x00000001, - Vertex = 0x00000002, - Instance = 0x00000003, + Undefined = 0x00000000, + Vertex = 0x00000001, + Instance = 0x00000002, +} + +WaitStatus :: enum i32 { + Success = 0x00000001, + TimedOut = 0x00000002, + Error = 0x00000003, } WGSLLanguageFeatureName :: enum i32 { @@ -704,14 +749,11 @@ WGSLLanguageFeatureName :: enum i32 { Packed4x8IntegerDotProduct = 0x00000002, UnrestrictedPointerParameters = 0x00000003, PointerCompositeAccess = 0x00000004, -} - -WaitStatus :: enum i32 { - Success = 0x00000001, - TimedOut = 0x00000002, - UnsupportedTimeout = 0x00000003, - UnsupportedCount = 0x00000004, - UnsupportedMixedSource = 0x00000005, + UniformBufferStandardLayout = 0x00000005, + SubgroupId = 0x00000006, + TextureAndSamplerLet = 0x00000007, + SubgroupUniformity = 0x00000008, + TextureFormatsTier1 = 0x00000009, } BufferUsage :: enum i32 { @@ -751,11 +793,12 @@ ShaderStage :: enum i32 { ShaderStageFlags :: bit_set[ShaderStage; Flags] TextureUsage :: enum i32 { - CopySrc = 0x00000000, - CopyDst = 0x00000001, - TextureBinding = 0x00000002, - StorageBinding = 0x00000003, - RenderAttachment = 0x00000004, + CopySrc, + CopyDst, + TextureBinding, + StorageBinding, + RenderAttachment, + TransientAttachment, } TextureUsageFlags :: bit_set[TextureUsage; Flags] @@ -767,7 +810,7 @@ CreateComputePipelineAsyncCallback :: #type proc "c" (status: CreatePipelineAsyn CreateRenderPipelineAsyncCallback :: #type proc "c" (status: CreatePipelineAsyncStatus, pipeline: RenderPipeline, message: StringView, userdata1: rawptr, userdata2: rawptr) DeviceLostCallback :: #type proc "c" (device: ^Device, reason: DeviceLostReason, message: StringView, userdata1: rawptr, userdata2: rawptr) PopErrorScopeCallback :: #type proc "c" (status: PopErrorScopeStatus, type: ErrorType, message: StringView, userdata1: rawptr, userdata2: rawptr) -QueueWorkDoneCallback :: #type proc "c" (status: QueueWorkDoneStatus, userdata1: rawptr, userdata2: rawptr) +QueueWorkDoneCallback :: #type proc "c" (status: QueueWorkDoneStatus, message: StringView, userdata1: rawptr, userdata2: rawptr) RequestAdapterCallback :: #type proc "c" (status: RequestAdapterStatus, adapter: Adapter, message: StringView, userdata1: rawptr, userdata2: rawptr) RequestDeviceCallback :: #type proc "c" (status: RequestDeviceStatus, adapter: Device, message: StringView, userdata1: rawptr, userdata2: rawptr) UncapturedErrorCallback :: #type proc "c" (device: ^Device, type: ErrorType, message: StringView, userdata1: rawptr, userdata2: rawptr) @@ -777,11 +820,6 @@ ChainedStruct :: struct { sType: SType, } -ChainedStructOut :: struct { - next: ^ChainedStructOut, - sType: SType, -} - BufferMapCallbackInfo :: struct { nextInChain: /* const */ ^ChainedStruct, mode: CallbackMode, @@ -862,7 +900,7 @@ UncapturedErrorCallbackInfo :: struct { } AdapterInfo :: struct { - nextInChain: ^ChainedStructOut, + nextInChain: ^ChainedStruct, vendor: StringView, architecture: StringView, device: StringView, @@ -871,16 +909,8 @@ AdapterInfo :: struct { adapterType: AdapterType, vendorID: u32, deviceID: u32, -} - -BindGroupEntry :: struct { - nextInChain: ^ChainedStruct, - binding: u32, - /* NULLABLE */ buffer: Buffer, - offset: u64, - size: u64, - /* NULLABLE */ sampler: Sampler, - /* NULLABLE */ textureView: TextureView, + subgroupMinSize: u32, + subgroupMaxSize: u32, } BlendComponent :: struct { @@ -916,6 +946,14 @@ CommandEncoderDescriptor :: struct { label: StringView, } +CompatibilityModeLimits :: struct { + using chain: ChainedStruct, + maxStorageBuffersInVertexStage: u32, + maxStorageTexturesInVertexStage: u32, + maxStorageBuffersInFragmentStage: u32, + maxStorageTexturesInFragmentStage: u32, +} + CompilationMessage :: struct { nextInChain: ^ChainedStruct, message: StringView, @@ -926,12 +964,6 @@ CompilationMessage :: struct { length: u64, } -ComputePassTimestampWrites :: struct { - querySet: QuerySet, - beginningOfPassWriteIndex: u32, - endOfPassWriteIndex: u32, -} - ConstantEntry :: struct { nextInChain: /* const */ ^ChainedStruct, key: StringView, @@ -944,18 +976,383 @@ Extent3D :: struct { depthOrArrayLayers: u32, } +/* +Chained in a `BindGroupEntry`. +*/ +ExternalTextureBindingEntry :: struct { + using chain: ChainedStruct, + externalTexture: ExternalTexture, +} + +/* +Chained in a `BindGroupLayoutEntry`. +*/ +ExternalTextureBindingLayout :: struct { + using chain: ChainedStruct, +} + Future :: struct { id: u64, } -InstanceCapabilities :: struct { - nextInChain: ^ChainedStructOut, - timedWaitAnyEnable: b32, +InstanceLimits :: struct { + nextInChain: ^ChainedStruct, timedWaitAnyMaxCount: uint, } +MultisampleState :: struct { + nextInChain: ^ChainedStruct, + count: u32, + mask: u32, + alphaToCoverageEnabled: b32, +} + +Origin3D :: struct { + x: u32, + y: u32, + z: u32, +} + +PassTimestampWrites :: struct { + nextInChain: ^ChainedStruct, + querySet: QuerySet, + beginningOfPassWriteIndex: u32, + endOfPassWriteIndex: u32, +} + +PipelineLayoutDescriptor :: struct { + nextInChain: ^ChainedStruct, + label: StringView, + bindGroupLayoutCount: uint, + bindGroupLayouts: [^]BindGroupLayout `fmt:"v,bindGroupLayoutCount"`, + immediateSize: u32, +} + +PrimitiveState :: struct { + nextInChain: ^ChainedStruct, + topology: PrimitiveTopology, + stripIndexFormat: IndexFormat, + frontFace: FrontFace, + cullMode: CullMode, + unclippedDepth: b32, +} + +QuerySetDescriptor :: struct { + nextInChain: ^ChainedStruct, + label: StringView, + type: QueryType, + count: u32, +} + +QueueDescriptor :: struct { + nextInChain: ^ChainedStruct, + label: StringView, +} + +RenderBundleDescriptor :: struct { + nextInChain: ^ChainedStruct, + label: StringView, +} + +RenderBundleEncoderDescriptor :: struct { + nextInChain: ^ChainedStruct, + label: StringView, + colorFormatCount: uint, + colorFormats: /* const */ [^]TextureFormat `fmt:"v,colorFormatCount"`, + depthStencilFormat: TextureFormat, + sampleCount: u32, + depthReadOnly: b32, + stencilReadOnly: b32, +} + +RenderPassDepthStencilAttachment :: struct { + nextInChain: ^ChainedStruct, + view: TextureView, + depthLoadOp: LoadOp, + depthStoreOp: StoreOp, + depthClearValue: f32, + depthReadOnly: b32, + stencilLoadOp: LoadOp, + stencilStoreOp: StoreOp, + stencilClearValue: u32, + stencilReadOnly: b32, +} + +RenderPassMaxDrawCount :: struct { + using chain: ChainedStruct, + maxDrawCount: u64, +} + +/* +Chained in a `RequestAdapterOptions`. +*/ +RequestAdapterWebXROptions :: struct { + using chain: ChainedStruct, + xrCompatible: b32, +} + +SamplerBindingLayout :: struct { + nextInChain: ^ChainedStruct, + type: SamplerBindingType, +} + +SamplerDescriptor :: struct { + nextInChain: ^ChainedStruct, + label: StringView, + addressModeU: AddressMode, + addressModeV: AddressMode, + addressModeW: AddressMode, + magFilter: FilterMode, + minFilter: FilterMode, + mipmapFilter: MipmapFilterMode, + lodMinClamp: f32, + lodMaxClamp: f32, + compare: CompareFunction, + maxAnisotropy: u16, +} + +ShaderSourceSPIRV :: struct { + using chain: ChainedStruct, + codeSize: u32, + code: /* const */ [^]u32 `fmt:"v,codeSize"`, +} + +ShaderSourceWGSL :: struct { + using chain: ChainedStruct, + code: StringView, +} + +StencilFaceState :: struct { + compare: CompareFunction, + failOp: StencilOperation, + depthFailOp: StencilOperation, + passOp: StencilOperation, +} + +StorageTextureBindingLayout :: struct { + nextInChain: ^ChainedStruct, + access: StorageTextureAccess, + format: TextureFormat, + viewDimension: TextureViewDimension, +} + +SupportedFeatures :: struct { + featureCount: uint, + features: /* const */ [^]FeatureName `fmt:"v,featureCount"`, +} + +SupportedInstanceFeatures :: struct { + featureCount: uint, + features: /* const */ [^]InstanceFeatureName `fmt:"v,featureCount"`, +} + +SupportedWGSLLanguageFeatures :: struct { + featureCount: uint, + features: /* const */ [^]WGSLLanguageFeatureName `fmt:"v,featureCount"`, +} + +SurfaceCapabilities :: struct { + nextInChain: ^ChainedStruct, + usages: TextureUsageFlags, + formatCount: uint, + formats: /* const */ [^]TextureFormat `fmt:"v,formatCount"`, + presentModeCount: uint, + presentModes: /* const */ [^]PresentMode `fmt:"v,presentModeCount"`, + alphaModeCount: uint, + alphaModes: /* const */ [^]CompositeAlphaMode `fmt:"v,alphaModeCount"`, +} + +/* +Chained in a `SurfaceConfiguration`. +*/ +SurfaceColorManagement :: struct { + using chain: ChainedStruct, + colorSpace: PredefinedColorSpace, + toneMappingMode: ToneMappingMode, +} + +SurfaceConfiguration :: struct { + nextInChain: ^ChainedStruct, + device: Device, + format: TextureFormat, + usage: TextureUsageFlags, + width: u32, + height: u32, + viewFormatCount: uint, + viewFormats: /* const */ [^]TextureFormat `fmt:"v,viewFormatCount"`, + alphaMode: CompositeAlphaMode, + presentMode: PresentMode, +} + +/* +Chained in a `SurfaceDescriptor`. +*/ +SurfaceSourceAndroidNativeWindow :: struct { + using chain: ChainedStruct, + window: rawptr, +} + +/* +Chained in a `SurfaceDescriptor`. +*/ +SurfaceSourceCanvasHTMLSelector :: struct { + using chain: ChainedStruct, + selector: StringView, +} + +/* +Chained in a `SurfaceDescriptor`. +*/ +SurfaceSourceMetalLayer :: struct { + using chain: ChainedStruct, + layer: rawptr, +} + +/* +Chained in a `SurfaceDescriptor`. +*/ +SurfaceSourceWaylandSurface :: struct { + using chain: ChainedStruct, + display: rawptr, + surface: rawptr, +} + +/* +Chained in a `SurfaceDescriptor`. +*/ +SurfaceSourceWindowsHWND :: struct { + using chain: ChainedStruct, + hinstance: rawptr, + hwnd: rawptr, +} + +/* +Chained in a `SurfaceDescriptor`. +*/ +SurfaceSourceXcbWindow :: struct { + using chain: ChainedStruct, + connection: rawptr, + window: u32, +} + +/* +Chained in a `SurfaceDescriptor`. +*/ +SurfaceSourceXlibWindow :: struct { + using chain: ChainedStruct, + display: rawptr, + window: u64, +} + +SurfaceTexture :: struct { + nextInChain: ^ChainedStruct, + texture: Texture, + status: SurfaceGetCurrentTextureStatus, +} + +TexelCopyBufferLayout :: struct { + offset: u64, + bytesPerRow: u32, + rowsPerImage: u32, +} + +TextureBindingLayout :: struct { + nextInChain: ^ChainedStruct, + sampleType: TextureSampleType, + viewDimension: TextureViewDimension, + multisampled: b32, +} + +TextureBindingViewDimension :: struct { + using chain: ChainedStruct, + textureBindingViewDimension: TextureViewDimension, +} + +TextureComponentSwizzle :: struct { + r, g, b, a: ComponentSwizzle, +} + +VertexAttribute :: struct { + nextInChain: ^ChainedStruct, + format: VertexFormat, + offset: u64, + shaderLocation: u32, +} + +BindGroupEntry :: struct { + nextInChain: ^ChainedStruct, + binding: u32, + /* NULLABLE */ buffer: Buffer, + offset: u64, + size: u64, + /* NULLABLE */ sampler: Sampler, + /* NULLABLE */ textureView: TextureView, +} + +BindGroupLayoutEntry :: struct { + nextInChain: ^ChainedStruct, + binding: u32, + visibility: ShaderStageFlags, + bindingArraySize: u32, + buffer: BufferBindingLayout, + sampler: SamplerBindingLayout, + texture: TextureBindingLayout, + storageTexture: StorageTextureBindingLayout, +} + +BlendState :: struct { + color: BlendComponent, + alpha: BlendComponent, +} + +CompilationInfo :: struct { + nextInChain: ^ChainedStruct, + messageCount: uint, + messages: /* const */ [^]CompilationMessage `fmt:"v,messageCount"`, +} + +ComputePassDescriptor :: struct { + nextInChain: ^ChainedStruct, + label: StringView, + /* NULLABLE */ timestampWrites: /* const */ ^PassTimestampWrites, +} + +ComputeState :: struct { + nextInChain: ^ChainedStruct, + module: ShaderModule, + entryPoint: StringView, + constantCount: uint, + constants: [^]ConstantEntry `fmt:"v,constantCount"`, +} + +DepthStencilState :: struct { + nextInChain: ^ChainedStruct, + format: TextureFormat, + depthWriteEnabled: OptionalBool, + depthCompare: CompareFunction, + stencilFront: StencilFaceState, + stencilBack: StencilFaceState, + stencilReadMask: u32, + stencilWriteMask: u32, + depthBias: i32, + depthBiasSlopeScale: f32, + depthBiasClamp: f32, +} + +FutureWaitInfo :: struct { + future: Future, + completed: b32, +} + +InstanceDescriptor :: struct { + nextInChain: ^ChainedStruct, + requiredFeatureCount: uint, + requiredFeatures: [^]InstanceFeatureName `fmt:"v,requiredFeatureCount"`, + /* NULLABLE */ requiredLimits: ^InstanceLimits, +} + Limits :: struct { - nextInChain: ^ChainedStructOut, + nextInChain: ^ChainedStruct, maxTextureDimension1D: u32, maxTextureDimension2D: u32, maxTextureDimension3D: u32, @@ -987,90 +1384,21 @@ Limits :: struct { maxComputeWorkgroupSizeY: u32, maxComputeWorkgroupSizeZ: u32, maxComputeWorkgroupsPerDimension: u32, + maxImmediateSize: u32, } -MultisampleState :: struct { - nextInChain: /* const */ ^ChainedStruct, - count: u32, - mask: u32, - alphaToCoverageEnabled: b32, -} - -Origin3D :: struct { - x: u32, - y: u32, - z: u32, -} - -PipelineLayoutDescriptor :: struct { - nextInChain: /* const */ ^ChainedStruct, - label: StringView, - bindGroupLayoutCount: uint, - bindGroupLayouts: [^]BindGroupLayout `fmt:"v,bindGroupLayoutCount"`, -} - -PrimitiveState :: struct { - nextInChain: /* const */ ^ChainedStruct, - topology: PrimitiveTopology, - stripIndexFormat: IndexFormat, - frontFace: FrontFace, - cullMode: CullMode, - unclippedDepth: b32, -} - -QuerySetDescriptor :: struct { - nextInChain: /* const */ ^ChainedStruct, - label: StringView, - type: QueryType, - count: u32, -} - -QueueDescriptor :: struct { - nextInChain: /* const */ ^ChainedStruct, - label: StringView, -} - -RenderBundleDescriptor :: struct { - nextInChain: /* const */ ^ChainedStruct, - label: StringView, -} - -RenderBundleEncoderDescriptor :: struct { - nextInChain: /* const */ ^ChainedStruct, - label: StringView, - colorFormatCount: uint, - colorFormats: /* const */ [^]TextureFormat `fmt:"v,colorFormatCount"`, - depthStencilFormat: TextureFormat, - sampleCount: u32, - depthReadOnly: b32, - stencilReadOnly: b32, -} - -RenderPassDepthStencilAttachment :: struct { - view: TextureView, - depthLoadOp: LoadOp, - depthStoreOp: StoreOp, - depthClearValue: f32, - depthReadOnly: b32, - stencilLoadOp: LoadOp, - stencilStoreOp: StoreOp, - stencilClearValue: u32, - stencilReadOnly: b32, -} - -RenderPassMaxDrawCount :: struct { - using chain: ChainedStruct, - maxDrawCount: u64, -} - -RenderPassTimestampWrites :: struct { - querySet: QuerySet, - beginningOfPassWriteIndex: u32, - endOfPassWriteIndex: u32, +RenderPassColorAttachment :: struct { + nextInChain: ^ChainedStruct, + /* NULLABLE */ view: TextureView, + depthSlice: u32, + /* NULLABLE */ resolveTarget: TextureView, + loadOp: LoadOp, + storeOp: StoreOp, + clearValue: Color, } RequestAdapterOptions :: struct { - nextInChain: /* const */ ^ChainedStruct, + nextInChain: ^ChainedStruct, featureLevel: FeatureLevel, powerPreference: PowerPreference, forceFallbackAdapter: b32, @@ -1078,260 +1406,16 @@ RequestAdapterOptions :: struct { /* NULLABLE */ compatibleSurface: Surface, } -SamplerBindingLayout :: struct { - nextInChain: /* const */ ^ChainedStruct, - type: SamplerBindingType, -} - -SamplerDescriptor :: struct { - nextInChain: /* const */ ^ChainedStruct, - label: StringView, - addressModeU: AddressMode, - addressModeV: AddressMode, - addressModeW: AddressMode, - magFilter: FilterMode, - minFilter: FilterMode, - mipmapFilter: MipmapFilterMode, - lodMinClamp: f32, - lodMaxClamp: f32, - compare: CompareFunction, - maxAnisotropy: u16, -} - ShaderModuleDescriptor :: struct { - nextInChain: /* const */ ^ChainedStruct, + nextInChain: ^ChainedStruct, label: StringView, } -ShaderSourceSPIRV :: struct { - using chain: ChainedStruct, - codeSize: u32, - code: /* const */ [^]u32 `fmt:"v,codeSize"`, -} - -ShaderSourceWGSL :: struct { - using chain: ChainedStruct, - code: StringView, -} - -StencilFaceState :: struct { - compare: CompareFunction, - failOp: StencilOperation, - depthFailOp: StencilOperation, - passOp: StencilOperation, -} - -StorageTextureBindingLayout :: struct { - nextInChain: /* const */ ^ChainedStruct, - access: StorageTextureAccess, - format: TextureFormat, - viewDimension: TextureViewDimension, -} - -SupportedFeatures :: struct { - featureCount: uint, - features: /* const */ [^]FeatureName `fmt:"v,featureCount"`, -} - -SupportedWGSLLanguageFeatures :: struct { - featureCount: uint, - features: /* const */ [^]WGSLLanguageFeatureName `fmt:"v,featureCount"`, -} - -SurfaceCapabilities :: struct { - nextInChain: ^ChainedStructOut, - usages: TextureUsageFlags, - formatCount: uint, - formats: /* const */ [^]TextureFormat `fmt:"v,formatCount"`, - presentModeCount: uint, - presentModes: /* const */ [^]PresentMode `fmt:"v,presentModeCount"`, - alphaModeCount: uint, - alphaModes: /* const */ [^]CompositeAlphaMode `fmt:"v,alphaModeCount"`, -} - -SurfaceConfiguration :: struct { - nextInChain: /* const */ ^ChainedStruct, - device: Device, - format: TextureFormat, - usage: TextureUsageFlags, - width: u32, - height: u32, - viewFormatCount: uint, - viewFormats: /* const */ [^]TextureFormat `fmt:"v,viewFormatCount"`, - alphaMode: CompositeAlphaMode, - presentMode: PresentMode, -} - SurfaceDescriptor :: struct { - nextInChain: /* const */ ^ChainedStruct, + nextInChain: ^ChainedStruct, label: StringView, } -SurfaceSourceAndroidNativeWindow :: struct { - using chain: ChainedStruct, - window: rawptr, -} - -SurfaceSourceCanvasHTMLSelector :: struct { - using chain: ChainedStruct, - selector: StringView, -} - -SurfaceSourceMetalLayer :: struct { - using chain: ChainedStruct, - layer: rawptr, -} - -SurfaceSourceWaylandSurface :: struct { - using chain: ChainedStruct, - display: rawptr, - surface: rawptr, -} - -SurfaceSourceWindowsHWND :: struct { - using chain: ChainedStruct, - hinstance: rawptr, - hwnd: rawptr, -} - -SurfaceSourceXcbWindow :: struct { - using chain: ChainedStruct, - connection: rawptr, - window: u32, -} - -SurfaceSourceXlibWindow :: struct { - using chain: ChainedStruct, - display: rawptr, - window: u64, -} - -SurfaceTexture :: struct { - nextInChain: ^ChainedStructOut, - texture: Texture, - status: SurfaceGetCurrentTextureStatus, -} - -TexelCopyBufferLayout :: struct { - offset: u64, - bytesPerRow: u32, - rowsPerImage: u32, -} - -TextureBindingLayout :: struct { - nextInChain: /* const */ ^ChainedStruct, - sampleType: TextureSampleType, - viewDimension: TextureViewDimension, - multisampled: b32, -} - -TextureViewDescriptor :: struct { - nextInChain: /* const */ ^ChainedStruct, - label: StringView, - format: TextureFormat, - dimension: TextureViewDimension, - baseMipLevel: u32, - mipLevelCount: u32, - baseArrayLayer: u32, - arrayLayerCount: u32, - aspect: TextureAspect, - usage: TextureUsageFlags, -} - -VertexAttribute :: struct { - format: VertexFormat, - offset: u64, - shaderLocation: u32, -} - -BindGroupDescriptor :: struct { - nextInChain: /* const */ ^ChainedStruct, - label: StringView, - layout: BindGroupLayout, - entryCount: uint, - entries: /* const */ [^]BindGroupEntry `fmt:"v,entryCount"`, -} - -BindGroupLayoutEntry :: struct { - nextInChain: /* const */ ^ChainedStruct, - binding: u32, - visibility: ShaderStageFlags, - buffer: BufferBindingLayout, - sampler: SamplerBindingLayout, - texture: TextureBindingLayout, - storageTexture: StorageTextureBindingLayout, -} - -BlendState :: struct { - color: BlendComponent, - alpha: BlendComponent, -} - -CompilationInfo :: struct { - nextInChain: /* const */ ^ChainedStruct, - messageCount: uint, - messages: /* const */ [^]CompilationMessage `fmt:"v,messageCount"`, -} - -ComputePassDescriptor :: struct { - nextInChain: /* const */ ^ChainedStruct, - label: StringView, - /* NULLABLE */ timestampWrites: /* const */ ^ComputePassTimestampWrites, -} - -DepthStencilState :: struct { - nextInChain: /* const */ ^ChainedStruct, - format: TextureFormat, - depthWriteEnabled: OptionalBool, - depthCompare: CompareFunction, - stencilFront: StencilFaceState, - stencilBack: StencilFaceState, - stencilReadMask: u32, - stencilWriteMask: u32, - depthBias: i32, - depthBiasSlopeScale: f32, - depthBiasClamp: f32, -} - -DeviceDescriptor :: struct { - nextInChain: /* const */ ^ChainedStruct, - label: StringView, - requiredFeatureCount: uint, - requiredFeatures: /* const */ [^]FeatureName `fmt:"v,requiredFeatureCount"`, - /* NULLABLE */ requiredLimits: /* const */ ^Limits, - defaultQueue: QueueDescriptor, - deviceLostCallbackInfo: DeviceLostCallbackInfo, - uncapturedErrorCallbackInfo: UncapturedErrorCallbackInfo, -} - -FutureWaitInfo :: struct { - future: Future, - completed: b32, -} - -InstanceDescriptor :: struct { - nextInChain: /* const */ ^ChainedStruct, - features: InstanceCapabilities, -} - -ProgrammableStageDescriptor :: struct { - nextInChain: /* const */ ^ChainedStruct, - module: ShaderModule, - entryPoint: StringView, - constantCount: uint, - constants: [^]ConstantEntry `fmt:"v,constantCount"`, -} - -RenderPassColorAttachment :: struct { - nextInChain: /* const */ ^ChainedStruct, - /* NULLABLE */ view: TextureView, - depthSlice: u32, - /* NULLABLE */ resolveTarget: TextureView, - loadOp: LoadOp, - storeOp: StoreOp, - clearValue: Color, -} - TexelCopyBufferInfo :: struct { layout: TexelCopyBufferLayout, buffer: Buffer, @@ -1344,8 +1428,16 @@ TexelCopyTextureInfo :: struct { aspect: TextureAspect, } +/* +Chained in a `TextureViewDescriptor`. +*/ +TextureComponentSwizzleDescriptor :: struct { + using chain: ChainedStruct, + swizzle: TextureComponentSwizzle, +} + TextureDescriptor :: struct { - nextInChain: /* const */ ^ChainedStruct, + nextInChain: ^ChainedStruct, label: StringView, usage: TextureUsageFlags, dimension: TextureDimension, @@ -1358,14 +1450,23 @@ TextureDescriptor :: struct { } VertexBufferLayout :: struct { + nextInChain: ^ChainedStruct, stepMode: VertexStepMode, arrayStride: u64, attributeCount: uint, attributes: /* const */ [^]VertexAttribute `fmt:"v,attributeCount"`, } +BindGroupDescriptor :: struct { + nextInChain: ^ChainedStruct, + label: StringView, + layout: BindGroupLayout, + entryCount: uint, + entries: /* const */ [^]BindGroupEntry `fmt:"v,entryCount"`, +} + BindGroupLayoutDescriptor :: struct { - nextInChain: /* const */ ^ChainedStruct, + nextInChain: ^ChainedStruct, label: StringView, entryCount: uint, entries: /* const */ [^]BindGroupLayoutEntry `fmt:"v,entryCount"`, @@ -1379,24 +1480,48 @@ ColorTargetState :: struct { } ComputePipelineDescriptor :: struct { - nextInChain: /* const */ ^ChainedStruct, + nextInChain: ^ChainedStruct, label: StringView, /* NULLABLE */ layout: PipelineLayout, - compute: ProgrammableStageDescriptor, + compute: ComputeState, +} + +DeviceDescriptor :: struct { + nextInChain: ^ChainedStruct, + label: StringView, + requiredFeatureCount: uint, + requiredFeatures: /* const */ [^]FeatureName `fmt:"v,requiredFeatureCount"`, + /* NULLABLE */ requiredLimits: /* const */ ^Limits, + defaultQueue: QueueDescriptor, + deviceLostCallbackInfo: DeviceLostCallbackInfo, + uncapturedErrorCallbackInfo: UncapturedErrorCallbackInfo, } RenderPassDescriptor :: struct { - nextInChain: /* const */ ^ChainedStruct, + nextInChain: ^ChainedStruct, label: StringView, colorAttachmentCount: uint, colorAttachments: /* const */ [^]RenderPassColorAttachment `fmt:"v,colorAttachmentCount"`, /* NULLABLE */ depthStencilAttachment: /* const */ ^RenderPassDepthStencilAttachment, /* NULLABLE */ occlusionQuerySet: QuerySet, - /* NULLABLE */ timestampWrites: /* const */ ^RenderPassTimestampWrites, + /* NULLABLE */ timestampWrites: /* const */ ^PassTimestampWrites, +} + +TextureViewDescriptor :: struct { + nextInChain: ^ChainedStruct, + label: StringView, + format: TextureFormat, + dimension: TextureViewDimension, + baseMipLevel: u32, + mipLevelCount: u32, + baseArrayLayer: u32, + arrayLayerCount: u32, + aspect: TextureAspect, + usage: TextureUsageFlags, } VertexState :: struct { - nextInChain: /* const */ ^ChainedStruct, + nextInChain: ^ChainedStruct, module: ShaderModule, entryPoint: StringView, constantCount: uint, @@ -1406,7 +1531,7 @@ VertexState :: struct { } FragmentState :: struct { - nextInChain: /* const */ ^ChainedStruct, + nextInChain: ^ChainedStruct, module: ShaderModule, entryPoint: StringView, constantCount: uint, @@ -1416,7 +1541,7 @@ FragmentState :: struct { } RenderPipelineDescriptor :: struct { - nextInChain: /* const */ ^ChainedStruct, + nextInChain: ^ChainedStruct, label: StringView, /* NULLABLE */ layout: PipelineLayout, vertex: VertexState, @@ -1430,8 +1555,10 @@ RenderPipelineDescriptor :: struct { foreign libwgpu { @(link_name="wgpuCreateInstance") RawCreateInstance :: proc(/* NULLABLE */ descriptor: /* const */ ^InstanceDescriptor = nil) -> Instance --- - @(link_name="wgpuGetInstanceCapabilities") - RawGetInstanceCapabilities :: proc(capabilities: ^InstanceCapabilities) -> Status --- + GetInstanceFeatures :: proc(features: ^SupportedInstanceFeatures) --- + @(link_name="wgpuGetInstanceLimits") + RawGetInstanceLimits :: proc(limits: ^InstanceLimits) -> Status --- + HasInstanceFeature :: proc(feature: InstanceFeatureName) -> b32 --- GetProcAddress :: proc(procName: StringView) -> Proc --- // Methods of Adapter @@ -1463,14 +1590,16 @@ foreign libwgpu { BufferDestroy :: proc(buffer: Buffer) --- @(link_name="wgpuBufferGetConstMappedRange") RawBufferGetConstMappedRange :: proc(buffer: Buffer, offset: uint, size: uint) -> /* const */ rawptr --- - BufferGetMapState :: proc(buffer: Buffer) -> BufferMapState --- @(link_name="wgpuBufferGetMappedRange") RawBufferGetMappedRange :: proc(buffer: Buffer, offset: uint, size: uint) -> rawptr --- + BufferGetMapState :: proc(buffer: Buffer) -> BufferMapState --- BufferGetSize :: proc(buffer: Buffer) -> u64 --- BufferGetUsage :: proc(buffer: Buffer) -> BufferUsageFlags --- BufferMapAsync :: proc(buffer: Buffer, mode: MapModeFlags, offset: uint, size: uint, callbackInfo: BufferMapCallbackInfo) -> Future --- + BufferReadMappedRange :: proc(buffer: Buffer, offset: uint, data: rawptr, size: uint) -> Status --- BufferSetLabel :: proc(buffer: Buffer, label: StringView) --- BufferUnmap :: proc(buffer: Buffer) --- + BufferWriteMappedRange :: proc(buffer: Buffer, offset: uint, data: rawptr, size: uint) -> Status --- BufferAddRef :: proc(buffer: Buffer) --- BufferRelease :: proc(buffer: Buffer) --- @@ -1548,10 +1677,14 @@ foreign libwgpu { DeviceAddRef :: proc(device: Device) --- DeviceRelease :: proc(device: Device) --- + // Methods of ExternalTexture + ExternalTextureSetLabel :: proc(externalTexture: ExternalTexture, label: StringView) --- + ExternalTextureAddRef :: proc(externalTexture: ExternalTexture) --- + ExternalTextureRelease :: proc(externalTexture: ExternalTexture) --- + // Methods of Instance InstanceCreateSurface :: proc(instance: Instance, descriptor: /* const */ ^SurfaceDescriptor) -> Surface --- - @(link_name="wgpuInstanceGetWGSLLanguageFeatures") - RawInstanceGetWGSLLanguageFeatures :: proc(instance: Instance, features: ^SupportedWGSLLanguageFeatures) -> Status --- + InstanceGetWGSLLanguageFeatures :: proc(instance: Instance, features: ^SupportedWGSLLanguageFeatures) --- InstanceHasWGSLLanguageFeature :: proc(instance: Instance, feature: WGSLLanguageFeatureName) -> b32 --- InstanceProcessEvents :: proc(instance: Instance) --- InstanceRequestAdapter :: proc(instance: Instance, /* NULLABLE */ options: /* const */ ^RequestAdapterOptions, callbackInfo: RequestAdapterCallbackInfo) -> Future --- @@ -1651,6 +1784,9 @@ foreign libwgpu { // Methods of SupportedFeatures SupportedFeaturesFreeMembers :: proc(supportedFeatures: SupportedFeatures) --- + // Methods of SupportedInstanceFeatures + SupportedInstanceFeaturesFreeMembers :: proc(SupportedInstanceFeatures: SupportedInstanceFeatures) --- + // Methods of SupportedWGSLLanguageFeatures SupportedWGSLLanguageFeaturesFreeMembers :: proc(supportedWGSLLanguageFeatures: SupportedWGSLLanguageFeatures) --- @@ -1678,6 +1814,7 @@ foreign libwgpu { TextureGetHeight :: proc(texture: Texture) -> u32 --- TextureGetMipLevelCount :: proc(texture: Texture) -> u32 --- TextureGetSampleCount :: proc(texture: Texture) -> u32 --- + TextureGetTextureBindingViewDimension :: proc(texture: Texture) -> TextureViewDimension --- TextureGetUsage :: proc(texture: Texture) -> TextureUsageFlags --- TextureGetWidth :: proc(texture: Texture) -> u32 --- TextureSetLabel :: proc(texture: Texture, label: StringView) --- @@ -1709,13 +1846,8 @@ CreateInstance :: proc "c" (/* NULLABLE */ descriptor: /* const */ ^InstanceDesc return RawCreateInstance(descriptor) } -GetInstanceCapabilities :: proc "c" () -> (capabilities: InstanceCapabilities, status: Status) { - status = RawGetInstanceCapabilities(&capabilities) - return -} - -InstanceGetWGSLLanguageFeatures :: proc "c" (instance: Instance) -> (features: SupportedWGSLLanguageFeatures, status: Status) { - status = RawInstanceGetWGSLLanguageFeatures(instance, &features) +GetInstanceLimits :: proc "c" () -> (limits: InstanceLimits, status: Status) { + status = RawGetInstanceLimits(&limits) return } diff --git a/vendor/wgpu/wgpu_native.odin b/vendor/wgpu/wgpu_native.odin index c58a8c90e..b1658d714 100644 --- a/vendor/wgpu/wgpu_native.odin +++ b/vendor/wgpu/wgpu_native.odin @@ -11,6 +11,8 @@ foreign libwgpu { @(link_name="wgpuQueueSubmitForIndex") RawQueueSubmitForIndex :: proc(queue: Queue, commandCount: uint, commands: [^]CommandBuffer) -> SubmissionIndex --- + QueueGetTimestampPeriod :: proc(queue: Queue) -> f32 --- + // Returns true if the queue is empty, or false if there are more queue submissions still in flight. DevicePoll :: proc(device: Device, wait: b32, /* NULLABLE */ submissionIndex: /* const */ ^SubmissionIndex = nil) -> b32 --- DeviceCreateShaderModuleSpirV :: proc(device: Device, descriptor: ^ShaderModuleDescriptorSpirV) -> ShaderModule --- @@ -21,9 +23,13 @@ foreign libwgpu { GetVersion :: proc() -> u32 --- - RenderPassEncoderSetPushConstants :: proc(encoder: RenderPassEncoder, stages: ShaderStageFlags, offset: u32, sizeBytes: u32, data: rawptr) --- - ComputePassEncoderSetPushConstants :: proc(encoder: ComputePassEncoder, offset: u32, sizeBytes: u32, data: rawptr) --- - RenderBundleEncoderSetPushConstants :: proc(encoder: RenderBundleEncoder, stages: ShaderStageFlags, offset: u32, sizeBytes: u32, data: rawptr) --- + DeviceGetNativeMetalDevice :: proc(device: Device) -> rawptr --- + DeviceGetNativeMetalCommandQueue :: proc(device: Device) -> rawptr --- + DeviceGetNativeMetalTexture :: proc(device: Device) -> rawptr --- + + RenderPassEncoderSetImmediates :: proc(encoder: RenderPassEncoder, stages: ShaderStageFlags, offset: u32, sizeBytes: u32, data: rawptr) --- + ComputePassEncoderSetImmediates :: proc(encoder: ComputePassEncoder, offset: u32, sizeBytes: u32, data: rawptr) --- + RenderBundleEncoderSetImmediates :: proc(encoder: RenderBundleEncoder, stages: ShaderStageFlags, offset: u32, sizeBytes: u32, data: rawptr) --- RenderPassEncoderMultiDrawIndirect :: proc(encoder: RenderPassEncoder, buffer: Buffer, offset: u64, count: u32) --- RenderPassEncoderMultiDrawIndexedIndirect :: proc(encoder: RenderPassEncoder, buffer: Buffer, offset: u64, count: u32) --- @@ -38,6 +44,9 @@ foreign libwgpu { ComputePassEncoderWriteTimestamp :: proc(computePassEncoder: ComputePassEncoder, querySet: QuerySet, queryIndex: u32) --- RenderPassEncoderWriteTimestamp :: proc(renderPassEncoder: RenderPassEncoder, querySet: QuerySet, queryIndex: u32) --- + + DeviceStartGraphicsDebuggerCapture :: proc(device: Device) -> b32 --- + DeviceStopGraphicsDebuggerCapture :: proc(device: Device) --- } GenerateReport :: proc "c" (instance: Instance) -> (report: GlobalReport) { diff --git a/vendor/wgpu/wgpu_native_types.odin b/vendor/wgpu/wgpu_native_types.odin index ad6ce704e..931aa96db 100644 --- a/vendor/wgpu/wgpu_native_types.odin +++ b/vendor/wgpu/wgpu_native_types.odin @@ -2,8 +2,8 @@ package wgpu import "base:runtime" -BINDINGS_VERSION :: [4]u8{27, 0, 2, 0} -BINDINGS_VERSION_STRING :: "27.0.2.0" +BINDINGS_VERSION :: [4]u8{29, 0, 0, 0} +BINDINGS_VERSION_STRING :: "29.0.0.0" LogLevel :: enum i32 { Off, @@ -19,21 +19,29 @@ InstanceBackend :: enum i32 { GL, Metal, DX12, - DX11, - BrowserWebGPU, + // DX11, + BrowserWebGPU = 5, } InstanceBackendFlags :: bit_set[InstanceBackend; Flags] InstanceBackendFlags_All :: InstanceBackendFlags{} InstanceBackendFlags_Primary :: InstanceBackendFlags{ .Vulkan, .Metal, .DX12, .BrowserWebGPU } -InstanceBackendFlags_Secondary :: InstanceBackendFlags{ .GL, .DX11 } +InstanceBackendFlags_Secondary :: InstanceBackendFlags{ .GL } InstanceFlag :: enum i32 { Debug, Validation, DiscardHalLabels, + AllowUnderlyingNoncompliantAdapter, + GPUBasedValidation, + ValidationIndirectCall, + AutomaticTimestampNormalization, + Default = 24, + Debugging, + AdvancedDebugging, + WithEnv, } InstanceFlags :: bit_set[InstanceFlag; Flags] -InstanceFlags_Default :: InstanceFlags{} +InstanceFlags_Empty :: InstanceFlags{} Dx12Compiler :: enum i32 { Undefined, @@ -72,6 +80,42 @@ GLFenceBehaviour :: enum i32 { AutoFinish, } +Dx12SwapchainKind :: enum i32 { + Undefined, + DxgiFromHwnd, + DxgiFromVisual, +} + +NativeDisplayHandleType :: enum i32 { + None, + Xlib, + Xcb, + Wayland, +} + +XlibDisplayHandle :: struct { + display: rawptr, + screen: i32, +} + +XcbDisplayHandle :: struct { + connection: rawptr, + screen: i32, +} + +WaylandDisplayHandle :: struct { + display: rawptr, +} + +NativeDisplayHandle :: struct { + type: NativeDisplayHandleType, + using data: struct #raw_union { + xlib: XlibDisplayHandle, + xcb: XcbDisplayHandle, + wayland: WaylandDisplayHandle, + }, +} + InstanceExtras :: struct { using chain: ChainedStruct, backends: InstanceBackendFlags, @@ -81,8 +125,10 @@ InstanceExtras :: struct { glFenceBehaviour: GLFenceBehaviour, dxcPath: StringView, dcxMaxShaderModel: DxcMaxShaderModel, + dx12PresentationSystem: Dx12SwapchainKind, budgetForDeviceCreation: ^u8, budgetForDeviceLoss: ^u8, + displayHandle: NativeDisplayHandle, } DeviceExtras :: struct { @@ -91,21 +137,15 @@ DeviceExtras :: struct { } NativeLimits :: struct { - using chain: ChainedStructOut, - maxPushConstantSize: u32, + using chain: ChainedStruct, + maxImmediateSize: u32, maxNonSamplerBindings: u32, -} - -PushConstantRange :: struct { - stages: ShaderStageFlags, - start: u32, - end: u32, + maxBindingArrayElementsPerShaderStage: u32, } PipelineLayoutExtras :: struct { using chain: ChainedStruct, - pushConstantRangeCount: uint, - pushConstantRanges: [^]PushConstantRange `fmt:"v,pushConstantRangeCount"`, + immediateDataSize: u32, } SubmissionIndex :: distinct u64 diff --git a/vendor/zlib/zlib.odin b/vendor/zlib/zlib.odin index 4c2ce712a..bc6b22cb7 100644 --- a/vendor/zlib/zlib.odin +++ b/vendor/zlib/zlib.odin @@ -222,7 +222,7 @@ foreign zlib { @(default_calling_convention="c") foreign zlib { deflateInit_ :: proc(strm: z_streamp, level: c.int, version: cstring, stream_size: c.int) -> c.int --- - inflateInit_ :: proc(strm: z_streamp, level: c.int, version: cstring, stream_size: c.int) -> c.int --- + inflateInit_ :: proc(strm: z_streamp, version: cstring, stream_size: c.int) -> c.int --- deflateInit2_ :: proc(strm: z_streamp, level, method, windowBits, memLevel, strategy: c.int, version: cstring, stream_size: c.int) -> c.int --- inflateInit2_ :: proc(strm: z_streamp, windowBits: c.int, version: cstring, stream_size: c.int) -> c.int --- inflateBackInit_ :: proc(strm: z_streamp, windowBits: c.int, window: [^]c.uchar, version: cstring, stream_size: c.int) -> c.int --- @@ -236,8 +236,8 @@ deflateInit :: #force_inline proc "c" (strm: z_streamp, level: c.int) -> c.int { return deflateInit_(strm, level, VERSION, c.int(size_of(z_stream))) } -inflateInit :: #force_inline proc "c" (strm: z_streamp, level: c.int) -> c.int { - return inflateInit_(strm, level, VERSION, c.int(size_of(z_stream))) +inflateInit :: #force_inline proc "c" (strm: z_streamp) -> c.int { + return inflateInit_(strm, VERSION, c.int(size_of(z_stream))) } deflateInit2 :: #force_inline proc "c" (strm: z_streamp, level, method, windowBits, memLevel, strategy: c.int) -> c.int {