Add intrinsics for LSX and LASX

This adds actual intrinsics of SIMD on loongarch64, with considerable performance improvements.

Benchmark on `Loongson-3A6000-HV Not Specified CPU @ 2.5GHz`

Baseline GCC 14 (debian trixie):
```
===========================
Total time (ms) : 17816
Nodes searched  : 2336177
Nodes/second    : 131128
```

With this PR:

GCC 16 (debian sid):
```
./stockfish compiler
Stockfish dev-20260516-52e8d9ef by the Stockfish developers (see AUTHORS file)

Compiled by                : g++ (GNUC) 16.1.0 on Linux
Compilation architecture   : loongarch64-lasx
Compilation settings       : 64bit LASX LSX
Compiler __VERSION__ macro : 16.1.0
===========================
Total time (ms) : 3518
Nodes searched  : 2336177
Nodes/second    : 664063
```
```
./stockfish compiler
Stockfish dev-20260516-52e8d9ef by the Stockfish developers (see AUTHORS file)

Compiled by                : g++ (GNUC) 16.1.0 on Linux
Compilation architecture   : loongarch64-lsx
Compilation settings       : 64bit LSX
Compiler __VERSION__ macro : 16.1.0
===========================
Total time (ms) : 4944
Nodes searched  : 2336177
Nodes/second    : 472527
```

clang 22 (debian sid):
```
./stockfish compiler
Stockfish dev-20260516-52e8d9ef by the Stockfish developers (see AUTHORS file)

Compiled by                : clang++ 22.1.5 on Linux
Compilation architecture   : loongarch64-lasx
Compilation settings       : 64bit LASX LSX
Compiler __VERSION__ macro : Debian Clang 22.1.5 (1)
===========================
Total time (ms) : 3401
Nodes searched  : 2336177
Nodes/second    : 686908
```
```
./stockfish compiler
Stockfish dev-20260516-52e8d9ef by the Stockfish developers (see AUTHORS file)

Compiled by                : clang++ 22.1.5 on Linux
Compilation architecture   : loongarch64-lsx
Compilation settings       : 64bit LSX
Compiler __VERSION__ macro : Debian Clang 22.1.5 (1)
===========================
Total time (ms) : 5501
Nodes searched  : 2336177
Nodes/second    : 424682
```

GCC 14 (debian trixie):
```
./stockfish compiler
Stockfish dev-20260516-52e8d9ef by the Stockfish developers (see AUTHORS file)

Compiled by                : g++ (GNUC) 14.2.0 on Linux
Compilation architecture   : loongarch64-lasx
Compilation settings       : 64bit LASX LSX
Compiler __VERSION__ macro : 14.2.0
===========================
Total time (ms) : 3559
Nodes searched  : 2336177
Nodes/second    : 656413
```
```
./stockfish compiler
Stockfish dev-20260516-52e8d9ef by the Stockfish developers (see AUTHORS file)

Compiled by                : g++ (GNUC) 14.2.0 on Linux
Compilation architecture   : loongarch64-lsx
Compilation settings       : 64bit LSX
Compiler __VERSION__ macro : 14.2.0
===========================
Total time (ms) : 5212
Nodes searched  : 2336177
Nodes/second    : 448230
```

clang 19 (debian trixie):
```
./stockfish compiler
Stockfish dev-20260516-52e8d9ef by the Stockfish developers (see AUTHORS file)

Compiled by                : clang++ 19.1.7 on Linux
Compilation architecture   : loongarch64-lasx
Compilation settings       : 64bit LASX LSX
Compiler __VERSION__ macro : Debian Clang 19.1.7 (3+b1)
===========================
Total time (ms) : 4830
Nodes searched  : 2336177
Nodes/second    : 483680
```
```
./stockfish compiler
Stockfish dev-20260516-52e8d9ef by the Stockfish developers (see AUTHORS file)

Compiled by                : clang++ 19.1.7 on Linux
Compilation architecture   : loongarch64-lsx
Compilation settings       : 64bit LSX
Compiler __VERSION__ macro : Debian Clang 19.1.7 (3+b1)
===========================
Total time (ms) : 5568
Nodes searched  : 2336177
Nodes/second    : 419572
```

closes https://github.com/official-stockfish/Stockfish/pull/6815

No functional change
This commit is contained in:
Wencey Wang
2026-05-17 20:42:36 +02:00
committed by Joost VandeVondele
parent 0366d092a8
commit ffffe1bdb5
11 changed files with 385 additions and 18 deletions
+187
View File
@@ -33,6 +33,13 @@
#elif defined(USE_NEON)
#include <arm_neon.h>
#elif defined(USE_LASX)
#include <lasxintrin.h>
#include <lsxintrin.h>
#elif defined(USE_LSX)
#include <lsxintrin.h>
#endif
#include "../types.h"
@@ -226,6 +233,150 @@ inline int16x8_t vaddw_high_s8(int16x8_t a, int8x16_t b) { return vaddw_s8(a, vg
inline int16x8_t vsubw_high_s8(int16x8_t a, int8x16_t b) { return vsubw_s8(a, vget_high_s8(b)); }
#endif
#elif USE_LASX
using vec_t = __m256i;
using vec_i8_t = __m128i;
using vec128_t = __m128i;
using psqt_vec_t = __m256i;
using vec_uint_t = __m256i;
inline __m256i lasx_load256(const __m256i* a) {
return __lasx_xvld(reinterpret_cast<const void*>(a), 0);
}
inline void lasx_store256(__m256i* a, __m256i b) { __lasx_xvst(b, reinterpret_cast<void*>(a), 0); }
inline __m256i lasx_packus_16(__m256i a, __m256i b) {
#if defined(__clang__) && defined(__has_builtin) && __has_builtin(__builtin_lasx_xvssrani_bu_h)
return (__m256i) __builtin_lasx_xvssrani_bu_h((v32i8) b, (v32i8) a, 0);
#else
return __lasx_xvssrani_bu_h(b, a, 0);
#endif
}
inline __m256i lasx_packus_32(__m256i a, __m256i b) {
#if defined(__clang__) && defined(__has_builtin) && __has_builtin(__builtin_lasx_xvssrani_hu_w)
return (__m256i) __builtin_lasx_xvssrani_hu_w((v16i16) b, (v16i16) a, 0);
#else
return __lasx_xvssrani_hu_w(b, a, 0);
#endif
}
#define vec_load(a) lasx_load256(a)
#define vec_store(a, b) lasx_store256(a, b)
#define vec_add_16(a, b) __lasx_xvadd_h(a, b)
#define vec_sub_16(a, b) __lasx_xvsub_h(a, b)
#define vec_mulhi_16(a, b) __lasx_xvmuh_h(a, b)
#define vec_zero() __lasx_xvldi(0)
#define vec_set_16(a) __lasx_xvreplgr2vr_h(a)
#define vec_max_16(a, b) __lasx_xvmax_h(a, b)
#define vec_min_16(a, b) __lasx_xvmin_h(a, b)
#define vec_slli_16(a, b) __lasx_xvslli_h(a, b)
// Inverse permuted at load time
#define vec_packus_16(a, b) lasx_packus_16(a, b)
#define vec_load_psqt(a) lasx_load256(a)
#define vec_store_psqt(a, b) lasx_store256(a, b)
#define vec_add_psqt_32(a, b) __lasx_xvadd_w(a, b)
#define vec_sub_psqt_32(a, b) __lasx_xvsub_w(a, b)
#define vec_zero_psqt() __lasx_xvldi(0)
#define vec_nnz(a) lasx_vec_nnz(a)
#define vec_convert_8_16(a) lasx_cvtepi8_epi16(a)
#define vec128_zero __lsx_vldi(0)
#define vec128_set_16(a) __lsx_vreplgr2vr_h(a)
#define vec128_load(a) (*(a))
#define vec128_storeu(a, b) *(a) = (b)
#define vec128_add(a, b) __lsx_vadd_h(a, b)
#define NumRegistersSIMD 24
#define MaxChunkSize 32
inline __m256i lasx_cvtepi8_epi16(__m128i a) {
#if defined(__has_builtin) && __has_builtin(__builtin_lasx_cast_128)
return __lasx_vext2xv_h_b(__lasx_cast_128(a));
#elif defined(__GNUC__) && !defined(__clang__)
__m256i out;
__asm__("vext2xv.h.b %u0, %u1" : "=f"(out) : "f"(a));
return out;
#else
int64_t lo = (int64_t) __lsx_vpickve2gr_d(a, 0);
int64_t hi = (int64_t) __lsx_vpickve2gr_d(a, 1);
__m256i v = __lasx_xvldi(0);
v = __lasx_xvinsgr2vr_d(v, lo, 0);
v = __lasx_xvinsgr2vr_d(v, hi, 2);
return __lasx_xvsllwil_h_b(v, 0);
#endif
}
inline int lasx_vec_nnz(__m256i a) {
const __m256i cmp = __lasx_xvslt_w(__lasx_xvldi(0), a);
const __m256i msk = __lasx_xvmskltz_w(cmp);
return ((int) __lasx_xvpickve2gr_w(msk, 0) & 0xF)
| (((int) __lasx_xvpickve2gr_w(msk, 4) & 0xF) << 4);
}
#elif USE_LSX
using vec_t = __m128i;
using vec_i8_t = std::uint64_t;
using vec128_t = __m128i;
using psqt_vec_t = __m128i;
using vec_uint_t = __m128i;
inline __m128i lsx_packus_16(__m128i a, __m128i b) {
#if defined(__clang__) && defined(__has_builtin) && __has_builtin(__builtin_lsx_vssrani_bu_h)
return (__m128i) __builtin_lsx_vssrani_bu_h((v16i8) b, (v16i8) a, 0);
#else
return __lsx_vssrani_bu_h(b, a, 0);
#endif
}
inline __m128i lsx_packus_32(__m128i a, __m128i b) {
#if defined(__clang__) && defined(__has_builtin) && __has_builtin(__builtin_lsx_vssrani_hu_w)
return (__m128i) __builtin_lsx_vssrani_hu_w((v8i16) b, (v8i16) a, 0);
#else
return __lsx_vssrani_hu_w(b, a, 0);
#endif
}
#define vec_load(a) (*(a))
#define vec_store(a, b) *(a) = (b)
#define vec_add_16(a, b) __lsx_vadd_h(a, b)
#define vec_sub_16(a, b) __lsx_vsub_h(a, b)
#define vec_mulhi_16(a, b) __lsx_vmuh_h(a, b)
#define vec_zero() __lsx_vldi(0)
#define vec_set_16(a) __lsx_vreplgr2vr_h(a)
#define vec_max_16(a, b) __lsx_vmax_h(a, b)
#define vec_min_16(a, b) __lsx_vmin_h(a, b)
#define vec_slli_16(a, b) __lsx_vslli_h(a, b)
// Inverse permuted at load time
#define vec_packus_16(a, b) lsx_packus_16(a, b)
#define vec_load_psqt(a) (*(a))
#define vec_store_psqt(a, b) *(a) = (b)
#define vec_add_psqt_32(a, b) __lsx_vadd_w(a, b)
#define vec_sub_psqt_32(a, b) __lsx_vsub_w(a, b)
#define vec_zero_psqt() __lsx_vldi(0)
inline int lsx_vec_nnz(__m128i a) {
const __m128i cmp = __lsx_vslt_w(__lsx_vldi(0), a);
const __m128i msk = __lsx_vmskltz_w(cmp);
return ((int) __lsx_vpickve2gr_w(msk, 0) & 0xF);
}
#define vec_nnz(a) lsx_vec_nnz(a)
inline __m128i vec_convert_8_16(std::uint64_t x) {
__m128i v = __lsx_vldrepl_d(reinterpret_cast<const void*>(&x), 0);
return __lsx_vsllwil_h_b(v, 0);
}
#define vec128_zero __lsx_vldi(0)
#define vec128_set_16(a) __lsx_vreplgr2vr_h(a)
#define vec128_load(a) (*(a))
#define vec128_storeu(a, b) *(a) = (b)
#define vec128_add(a, b) __lsx_vadd_h(a, b)
#define NumRegistersSIMD 24
#define MaxChunkSize 16
#else
#undef VECTOR
@@ -382,6 +533,42 @@ dotprod_m128_add_dpbusd_epi32(int32x4_t& acc, int8x16_t a, int8x16_t b) {
}
#endif
#if defined(USE_LASX)
[[maybe_unused]] static int lasx_m256_hadd(__m256i sum, int bias) {
__m256i v = sum;
v = __lasx_xvadd_w(v, __lasx_xvshuf4i_w(v, 0x4E)); // [C,D,A,B] per lane
v = __lasx_xvadd_w(v, __lasx_xvshuf4i_w(v, 0xB1)); // [B,A,D,C] per lane
int lo_sum = (int) __lasx_xvpickve2gr_w(v, 0);
int hi_sum = (int) __lasx_xvpickve2gr_w(v, 4);
return lo_sum + hi_sum + bias;
}
[[maybe_unused]] static void lasx_m256_add_dpbusd_epi32(__m256i& acc, __m256i a, __m256i b) {
__m256i tmp = __lasx_xvmulwev_h_bu_b(a, b);
tmp = __lasx_xvmaddwod_h_bu_b(tmp, a, b);
acc = __lasx_xvadd_w(acc, __lasx_xvhaddw_w_h(tmp, tmp));
}
#endif // USE_LASX
#if defined(USE_LSX)
[[maybe_unused]] static int lsx_m128_hadd(__m128i sum, int bias) {
sum = __lsx_vadd_w(sum, __lsx_vshuf4i_w(sum, 0x4E)); // [C,D,A,B]
sum = __lsx_vadd_w(sum, __lsx_vshuf4i_w(sum, 0xB1)); // [B,A,D,C]
return __lsx_vpickve2gr_w(sum, 0) + bias;
}
[[maybe_unused]] static void lsx_m128_add_dpbusd_epi32(__m128i& acc, __m128i a, __m128i b) {
// tmp[i] = a[2i]*b[2i] + a[2i+1]*b[2i+1]
__m128i tmp = __lsx_vmulwev_h_bu_b(a, b);
tmp = __lsx_vmaddwod_h_bu_b(tmp, a, b);
acc = __lsx_vadd_w(acc, __lsx_vhaddw_w_h(tmp, tmp));
}
#endif // USE_LSX
// Compute optimal SIMD register count for feature transformer accumulation.
template<IndexType TransformedFeatureWidth, IndexType HalfDimensions, IndexType PSQTBuckets>