#ifndef SIMDJSON_RVV_VLS_SIMD_H #define SIMDJSON_RVV_VLS_SIMD_H #ifndef SIMDJSON_CONDITIONAL_INCLUDE #include "simdjson/rvv-vls/base.h" #include "simdjson/rvv-vls/bitmanipulation.h" #include "simdjson/internal/simdprune_tables.h" #endif // SIMDJSON_CONDITIONAL_INCLUDE namespace simdjson { namespace rvv_vls { namespace { namespace simd { #if __riscv_v_fixed_vlen >= 512 static constexpr size_t VL8 = 512/8; using vint8_t = vint8m1_t __attribute__((riscv_rvv_vector_bits(512))); using vuint8_t = vuint8m1_t __attribute__((riscv_rvv_vector_bits(512))); using vbool_t = vbool8_t __attribute__((riscv_rvv_vector_bits(512/8))); using vbitmask_t = uint64_t; #else static constexpr size_t VL8 = __riscv_v_fixed_vlen/8; using vint8_t = vint8m1_t __attribute__((riscv_rvv_vector_bits(__riscv_v_fixed_vlen))); using vuint8_t = vuint8m1_t __attribute__((riscv_rvv_vector_bits(__riscv_v_fixed_vlen))); using vbool_t = vbool8_t __attribute__((riscv_rvv_vector_bits(__riscv_v_fixed_vlen/8))); #if __riscv_v_fixed_vlen == 128 using vbitmask_t = uint16_t; #elif __riscv_v_fixed_vlen == 256 using vbitmask_t = uint32_t; #endif #endif #if __riscv_v_fixed_vlen == 128 using vuint8x64_t = vuint8m4_t __attribute__((riscv_rvv_vector_bits(512))); using vboolx64_t = vbool2_t __attribute__((riscv_rvv_vector_bits(512/8))); #elif __riscv_v_fixed_vlen == 256 using vuint8x64_t = vuint8m2_t __attribute__((riscv_rvv_vector_bits(512))); using vboolx64_t = vbool4_t __attribute__((riscv_rvv_vector_bits(512/8))); #else using vuint8x64_t = vuint8m1_t __attribute__((riscv_rvv_vector_bits(512))); using vboolx64_t = vbool8_t __attribute__((riscv_rvv_vector_bits(512/8))); #endif template struct simd8; // SIMD byte mask type (returned by things like eq and gt) template<> struct simd8 { vbool_t value; using bitmask_t = vbitmask_t; static constexpr int SIZE = sizeof(value); simdjson_inline simd8(const vbool_t _value) : value(_value) {} simdjson_inline simd8() : simd8(__riscv_vmclr_m_b8(VL8)) {} simdjson_inline simd8(bool _value) : simd8(splat(_value)) {} simdjson_inline operator const vbool_t&() const { return value; } simdjson_inline operator vbool_t&() { return value; } static simdjson_inline simd8 splat(bool _value) { return __riscv_vreinterpret_b8(__riscv_vmv_v_x_u64m1(((uint64_t)!_value)-1, 1)); } simdjson_inline vbitmask_t to_bitmask() const { #if __riscv_v_fixed_vlen == 128 return __riscv_vmv_x(__riscv_vreinterpret_u16m1(value)); #elif __riscv_v_fixed_vlen == 256 return __riscv_vmv_x(__riscv_vreinterpret_u32m1(value)); #else return __riscv_vmv_x(__riscv_vreinterpret_u64m1(value)); #endif } // Bit operations simdjson_inline simd8 operator|(const simd8 other) const { return __riscv_vmor(*this, other, VL8); } simdjson_inline simd8 operator&(const simd8 other) const { return __riscv_vmand(*this, other, VL8); } simdjson_inline simd8 operator^(const simd8 other) const { return __riscv_vmxor(*this, other, VL8); } simdjson_inline simd8 bit_andnot(const simd8 other) const { return __riscv_vmandn(other, *this, VL8); } simdjson_inline simd8 operator~() const { return __riscv_vmnot(*this, VL8); } simdjson_inline simd8& operator|=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast | other; return *this_cast; } simdjson_inline simd8& operator&=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast & other; return *this_cast; } simdjson_inline simd8& operator^=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast ^ other; return *this_cast; } }; // Unsigned bytes template<> struct simd8 { vuint8_t value; static constexpr int SIZE = sizeof(value); simdjson_inline simd8(const vuint8_t _value) : value(_value) {} simdjson_inline simd8() : simd8(zero()) {} simdjson_inline simd8(const uint8_t values[VL8]) : simd8(load(values)) {} simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {} simdjson_inline simd8(simd8 mask) : value(__riscv_vmerge_vxm_u8m1(zero(), -1, (vbool_t)mask, VL8)) {} simdjson_inline operator const vuint8_t&() const { return this->value; } simdjson_inline operator vuint8_t&() { return this->value; } simdjson_inline simd8( uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7, uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15 ) : simd8(vuint8_t{ v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10,v11,v12,v13,v14,v15 }) {} // Repeat 16 values as many times as necessary (usually for lookup tables) simdjson_inline static simd8 repeat_16( uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7, uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15 ) { return simd8( v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10,v11,v12,v13,v14,v15 ); } static simdjson_inline vuint8_t splat(uint8_t _value) { return __riscv_vmv_v_x_u8m1(_value, VL8); } static simdjson_inline vuint8_t zero() { return splat(0); } static simdjson_inline vuint8_t load(const uint8_t values[VL8]) { return __riscv_vle8_v_u8m1(values, VL8); } // Bit operations simdjson_inline simd8 operator|(const simd8 other) const { return __riscv_vor_vv_u8m1( value, other, VL8); } simdjson_inline simd8 operator&(const simd8 other) const { return __riscv_vand_vv_u8m1( value, other, VL8); } simdjson_inline simd8 operator^(const simd8 other) const { return __riscv_vxor_vv_u8m1( value, other, VL8); } simdjson_inline simd8 operator~() const { return __riscv_vnot_v_u8m1(value, VL8); } #if __riscv_zvbb simdjson_inline simd8 bit_andnot(const simd8 other) const { return __riscv_vandn_vv_u8m1(other, value, VL8); } #else simdjson_inline simd8 bit_andnot(const simd8 other) const { return other & ~*this; } #endif simdjson_inline simd8& operator|=(const simd8 other) { value = *this | other; return *this; } simdjson_inline simd8& operator&=(const simd8 other) { value = *this & other; return *this; } simdjson_inline simd8& operator^=(const simd8 other) { value = *this ^ other; return *this; } simdjson_inline simd8 operator==(const simd8 other) const { return __riscv_vmseq(value, other, VL8); } simdjson_inline simd8 operator==(uint8_t other) const { return __riscv_vmseq(value, other, VL8); } template simdjson_inline simd8 prev(const simd8 prev_chunk) const { return __riscv_vslideup(__riscv_vslidedown(prev_chunk, VL8-N, VL8), value, N, VL8); } // Store to array simdjson_inline void store(uint8_t dst[VL8]) const { return __riscv_vse8(dst, value, VL8); } // Saturated math simdjson_inline simd8 saturating_add(const simd8 other) const { return __riscv_vsaddu(value, other, VL8); } simdjson_inline simd8 saturating_sub(const simd8 other) const { return __riscv_vssubu(value, other, VL8); } // Addition/subtraction are the same for signed and unsigned simdjson_inline simd8 operator+(const simd8 other) const { return __riscv_vadd(value, other, VL8); } simdjson_inline simd8 operator-(const simd8 other) const { return __riscv_vsub(value, other, VL8); } simdjson_inline simd8& operator+=(const simd8 other) { value = *this + other; return *this; } simdjson_inline simd8& operator-=(const simd8 other) { value = *this - other; return *this; } // Order-specific operations simdjson_inline simd8 operator<=(const simd8 other) const { return __riscv_vmsleu(value, other, VL8); } simdjson_inline simd8 operator>=(const simd8 other) const { return __riscv_vmsgeu(value, other, VL8); } simdjson_inline simd8 operator<(const simd8 other) const { return __riscv_vmsltu(value, other, VL8); } simdjson_inline simd8 operator>(const simd8 other) const { return __riscv_vmsgtu(value, other, VL8); } // Same as >, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero. simdjson_inline simd8 gt_bits(const simd8 other) const { return simd8(*this > other); } // Same as <, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero. simdjson_inline simd8 lt_bits(const simd8 other) const { return simd8(*this < other); } // Bit-specific operations simdjson_inline bool any_bits_set_anywhere() const { return __riscv_vfirst(__riscv_vmsne(value, 0, VL8), VL8) >= 0; } simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return (*this & bits).any_bits_set_anywhere(); } template simdjson_inline simd8 shr() const { return __riscv_vsrl(value, N, VL8); } template simdjson_inline simd8 shl() const { return __riscv_vsll(value, N, VL8); } // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) template simdjson_inline simd8 lookup_16(simd8 lookup_table) const { return __riscv_vrgather(lookup_table, value, VL8); } // compress inactive elements, to match AVX-512 behavior template simdjson_inline void compress(vbitmask_t mask, L * output) const { mask = (vbitmask_t)~mask; #if __riscv_v_fixed_vlen == 128 vbool8_t m = __riscv_vreinterpret_b8(__riscv_vmv_s_x_u16m1(mask, 1)); #elif __riscv_v_fixed_vlen == 256 vbool8_t m = __riscv_vreinterpret_b8(__riscv_vmv_s_x_u32m1(mask, 1)); #else vbool8_t m = __riscv_vreinterpret_b8(__riscv_vmv_s_x_u64m1(mask, 1)); #endif __riscv_vse8_v_u8m1(output, __riscv_vcompress(value, m, VL8), count_ones(mask)); } template simdjson_inline simd8 lookup_16( L replace0, L replace1, L replace2, L replace3, L replace4, L replace5, L replace6, L replace7, L replace8, L replace9, L replace10, L replace11, L replace12, L replace13, L replace14, L replace15) const { return lookup_16(simd8::repeat_16( replace0, replace1, replace2, replace3, replace4, replace5, replace6, replace7, replace8, replace9, replace10, replace11, replace12, replace13, replace14, replace15 )); } }; // Signed bytes template<> struct simd8 { vint8_t value; static constexpr int SIZE = sizeof(value); simdjson_inline simd8(const vint8_t _value) : value(_value) {} simdjson_inline simd8() : simd8(zero()) {} simdjson_inline simd8(const int8_t values[VL8]) : simd8(load(values)) {} simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {} simdjson_inline operator const vint8_t&() const { return this->value; } simdjson_inline operator vint8_t&() { return this->value; } simdjson_inline simd8( int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7, int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15 ) : simd8(vint8_t{ v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10,v11,v12,v13,v14,v15 }) {} // Repeat 16 values as many times as necessary (usually for lookup tables) simdjson_inline static simd8 repeat_16( int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7, int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15 ) { return simd8( v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10,v11,v12,v13,v14,v15 ); } static simdjson_inline vint8_t splat(int8_t _value) { return __riscv_vmv_v_x_i8m1(_value, VL8); } static simdjson_inline vint8_t zero() { return splat(0); } static simdjson_inline vint8_t load(const int8_t values[VL8]) { return __riscv_vle8_v_i8m1(values, VL8); } simdjson_inline void store(int8_t dst[VL8]) const { return __riscv_vse8(dst, value, VL8); } // Explicit conversion to/from unsigned simdjson_inline explicit simd8(const vuint8_t other): simd8(__riscv_vreinterpret_i8m1(other)) {} simdjson_inline explicit operator simd8() const { return __riscv_vreinterpret_u8m1(value); } // Math simdjson_inline simd8 operator+(const simd8 other) const { return __riscv_vadd(value, other, VL8); } simdjson_inline simd8 operator-(const simd8 other) const { return __riscv_vsub(value, other, VL8); } simdjson_inline simd8& operator+=(const simd8 other) { value = *this + other; return *this; } simdjson_inline simd8& operator-=(const simd8 other) { value = *this - other; return *this; } // Order-sensitive comparisons simdjson_inline simd8 max_val( const simd8 other) const { return __riscv_vmax( value, other, VL8); } simdjson_inline simd8 min_val( const simd8 other) const { return __riscv_vmin( value, other, VL8); } simdjson_inline simd8 operator>( const simd8 other) const { return __riscv_vmsgt(value, other, VL8); } simdjson_inline simd8 operator<( const simd8 other) const { return __riscv_vmslt(value, other, VL8); } simdjson_inline simd8 operator==(const simd8 other) const { return __riscv_vmseq(value, other, VL8); } template simdjson_inline simd8 prev(const simd8 prev_chunk) const { return __riscv_vslideup(__riscv_vslidedown(prev_chunk, VL8-N, VL8), value, N, VL8); } // Perform a lookup assuming no value is larger than 16 template simdjson_inline simd8 lookup_16(simd8 lookup_table) const { return __riscv_vrgather(lookup_table, value, VL8); } template simdjson_inline simd8 lookup_16( L replace0, L replace1, L replace2, L replace3, L replace4, L replace5, L replace6, L replace7, L replace8, L replace9, L replace10, L replace11, L replace12, L replace13, L replace14, L replace15) const { return lookup_16(simd8::repeat_16( replace0, replace1, replace2, replace3, replace4, replace5, replace6, replace7, replace8, replace9, replace10, replace11, replace12, replace13, replace14, replace15 )); } }; template struct simd8x64; template<> struct simd8x64 { static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); vuint8x64_t value; #if __riscv_v_fixed_vlen >= 512 template simd8 get() const { return value; } #else template simd8 get() const { return __riscv_vget_u8m1(value, idx); } #endif simdjson_inline operator const vuint8x64_t&() const { return this->value; } simdjson_inline operator vuint8x64_t&() { return this->value; } simd8x64(const simd8x64& o) = delete; // no copy allowed simd8x64& operator=(const simd8& other) = delete; // no assignment allowed simd8x64() = delete; // no default constructor allowed #if __riscv_v_fixed_vlen == 128 simdjson_inline simd8x64(const uint8_t *ptr, size_t n = 64) : value(__riscv_vle8_v_u8m4(ptr, n)) {} #elif __riscv_v_fixed_vlen == 256 simdjson_inline simd8x64(const uint8_t *ptr, size_t n = 64) : value(__riscv_vle8_v_u8m2(ptr, n)) {} #else simdjson_inline simd8x64(const uint8_t *ptr, size_t n = 64) : value(__riscv_vle8_v_u8m1(ptr, n)) {} #endif simdjson_inline void store(uint8_t ptr[64]) const { __riscv_vse8(ptr, value, 64); } simdjson_inline bool is_ascii() const { #if __riscv_v_fixed_vlen == 128 return __riscv_vfirst(__riscv_vmslt(__riscv_vreinterpret_i8m4(value), 0, 64), 64) < 0; #elif __riscv_v_fixed_vlen == 256 return __riscv_vfirst(__riscv_vmslt(__riscv_vreinterpret_i8m2(value), 0, 64), 64) < 0; #else return __riscv_vfirst(__riscv_vmslt(__riscv_vreinterpret_i8m1(value), 0, 64), 64) < 0; #endif } // compress inactive elements, to match AVX-512 behavior simdjson_inline uint64_t compress(uint64_t mask, uint8_t * output) const { mask = ~mask; #if __riscv_v_fixed_vlen == 128 vboolx64_t m = __riscv_vreinterpret_b2(__riscv_vmv_s_x_u64m1(mask, 1)); #elif __riscv_v_fixed_vlen == 256 vboolx64_t m = __riscv_vreinterpret_b4(__riscv_vmv_s_x_u64m1(mask, 1)); #else vboolx64_t m = __riscv_vreinterpret_b8(__riscv_vmv_s_x_u64m1(mask, 1)); #endif size_t cnt = count_ones(mask); __riscv_vse8(output, __riscv_vcompress(value, m, 64), cnt); return cnt; } simdjson_inline uint64_t eq(const uint8_t m) const { return __riscv_vmv_x(__riscv_vreinterpret_u64m1(__riscv_vmseq(value, m, 64))); } simdjson_inline uint64_t lteq(const uint8_t m) const { return __riscv_vmv_x(__riscv_vreinterpret_u64m1(__riscv_vmsleu(value, m, 64))); } }; // struct simd8x64 } // namespace simd } // unnamed namespace } // namespace rvv_vls } // namespace simdjson #endif // SIMDJSON_RVV_VLS_SIMD_H