#pragma once #include #if defined(__x86_64__) && !defined(USE_SIMD_FALLBACK) // SIMD operations on potentially-indeterminate Node16::index[16] bytes. // Implemented in file-level assembly (simd_x86_64.S) because loading and // operating on indeterminate values is UB in C++ but well-defined in // assembly. The caller must mask the returned bitfield to // [0, numChildren) before using it. // // Each function returns a 16-bit bitmask in the low 16 bits of a uint32_t // (upper 16 bits are zero). Bit i is set iff the condition holds at index i. extern "C" { // Returns bit i set iff idx[i] == key uint32_t find_eq_16(const uint8_t idx[16], uint8_t key); // Returns bit i set iff idx[i] >= child uint32_t find_ge_16(const uint8_t idx[16], uint8_t child); // Returns bit i set iff begin <= idx[i] < end uint32_t mask_in_range_16(const uint8_t idx[16], uint8_t begin, uint8_t end); } #elif defined(__aarch64__) && !defined(USE_SIMD_FALLBACK) // SIMD operations on potentially-indeterminate Node16::index[16] bytes. // Implemented in file-level assembly (simd_aarch64.S) because loading and // operating on indeterminate values is UB in C++ but well-defined in // assembly. The caller must mask the returned bitfield to [0, numChildren) // before using it. // // AArch64 has no pmovmskb-equivalent, so (unlike x86-64) each function returns // a 64-bit "nibble mask": nibble i (bits [4i, 4i+4)) is 0xf iff the condition // holds at index i. Bit (4i + 3) is the high bit of byte i's result. Callers // locate a set lane with std::countr_zero(bitfield) / 4 and mask the valid // lanes with (uint64_t(1) << (numChildren * 4)) - 1. extern "C" { // Returns nibble i = 0xf iff idx[i] == key uint64_t find_eq_16(const uint8_t idx[16], uint8_t key); // Returns nibble i = 0xf iff idx[i] >= child uint64_t find_ge_16(const uint8_t idx[16], uint8_t child); // Returns nibble i = 0xf iff begin <= idx[i] < end uint64_t mask_in_range_16(const uint8_t idx[16], uint8_t begin, uint8_t end); } #endif