diff --git a/CMakeLists.txt b/CMakeLists.txt index b8c93144..f8e3eece 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,7 +40,7 @@ set(CMAKE_C_FLAGS_DEBUG "-g -fsanitize=address") set(CMAKE_C_FLAGS_RELEASE "-O3") # Compiler-specific flags -if (CMAKE_CXX_COMPILER_ID MATCHES "^(Apple)?Clang$") +if (CMAKE_CXX_COMPILER_ID MATCHES "^(Apple)?Clang$" AND NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten") if (NOT APPLE) add_compile_options(-march=native) endif () @@ -104,15 +104,25 @@ if (SIMSIMD_BUILD_BENCHMARKS) endif () if (SIMSIMD_BUILD_TESTS) - add_executable(simsimd_test_compile_time scripts/test.c) - target_link_libraries(simsimd_test_compile_time simsimd m) - - add_executable(simsimd_test_run_time scripts/test.c c/lib.c) - target_compile_definitions(simsimd_test_run_time PRIVATE SIMSIMD_DYNAMIC_DISPATCH=1) - target_link_libraries(simsimd_test_run_time simsimd m) + if (CMAKE_SYSTEM_NAME STREQUAL "Emscripten") + add_executable(simsimd_test_compile_time scripts/test.c) + + target_compile_definitions(simsimd_test_compile_time PRIVATE _SIMSIMD_TARGET_ARM=1 SIMSIMD_TARGET_WASM=1) + target_compile_options(simsimd_test_compile_time PRIVATE -msimd128 -mfpu=neon) + + target_link_libraries(simsimd_test_compile_time simsimd m) + else() + add_executable(simsimd_test_compile_time scripts/test.c) + target_compile_definitions(simsimd_test_compile_time PRIVATE SIMSIMD_TARGET_HASWELL=1) + target_link_libraries(simsimd_test_compile_time simsimd m) + + add_executable(simsimd_test_run_time scripts/test.c c/lib.c) + target_compile_definitions(simsimd_test_run_time PRIVATE SIMSIMD_DYNAMIC_DISPATCH=1) + target_link_libraries(simsimd_test_run_time simsimd m) + endif() endif () -if (SIMSIMD_BUILD_SHARED) +if (SIMSIMD_BUILD_SHARED AND NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten") set(SIMSIMD_SOURCES ${SIMSIMD_SOURCES} c/lib.c) add_library(simsimd_shared SHARED ${SIMSIMD_SOURCES}) target_include_directories(simsimd_shared PUBLIC "${PROJECT_SOURCE_DIR}/include") diff --git a/include/simsimd/curved.h b/include/simsimd/curved.h index b3c06bd0..4ce4dfde 100644 --- a/include/simsimd/curved.h +++ b/include/simsimd/curved.h @@ -213,11 +213,10 @@ SIMSIMD_MAKE_COMPLEX_BILINEAR(accurate, bf16c, f64, SIMSIMD_BF16_TO_F32) // sims SIMSIMD_MAKE_MAHALANOBIS(accurate, bf16, f64, SIMSIMD_BF16_TO_F32) // simsimd_mahalanobis_bf16_accurate #if _SIMSIMD_TARGET_ARM -#if SIMSIMD_TARGET_NEON +#if SIMSIMD_TARGET_NEON || SIMSIMD_TARGET_WASM #pragma GCC push_options #pragma GCC target("arch=armv8.2-a+simd") #pragma clang attribute push(__attribute__((target("arch=armv8.2-a+simd"))), apply_to = function) - SIMSIMD_PUBLIC void simsimd_bilinear_f32_neon(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_f32_t const *c, simsimd_size_t n, simsimd_distance_t *result) { float32x4_t sum_vec = vdupq_n_f32(0); diff --git a/include/simsimd/dot.h b/include/simsimd/dot.h index 8246aba2..739d0e0a 100644 --- a/include/simsimd/dot.h +++ b/include/simsimd/dot.h @@ -232,7 +232,7 @@ SIMSIMD_MAKE_COMPLEX_DOT(accurate, bf16c, f64, SIMSIMD_BF16_TO_F32) // simsimd_ SIMSIMD_MAKE_COMPLEX_VDOT(accurate, bf16c, f64, SIMSIMD_BF16_TO_F32) // simsimd_vdot_bf16c_accurate #if _SIMSIMD_TARGET_ARM -#if SIMSIMD_TARGET_NEON +#if SIMSIMD_TARGET_NEON || SIMSIMD_TARGET_WASM #pragma GCC push_options #pragma GCC target("arch=armv8.2-a+simd") #pragma clang attribute push(__attribute__((target("arch=armv8.2-a+simd"))), apply_to = function) @@ -398,7 +398,7 @@ SIMSIMD_PUBLIC void simsimd_dot_u8_neon(simsimd_u8_t const *a_scalars, simsimd_u #pragma GCC pop_options #endif // SIMSIMD_TARGET_NEON_I8 -#if SIMSIMD_TARGET_NEON_F16 +#if SIMSIMD_TARGET_NEON_F16 || SIMSIMD_TARGET_WASM #pragma GCC push_options #pragma GCC target("arch=armv8.2-a+simd+fp16") #pragma clang attribute push(__attribute__((target("arch=armv8.2-a+simd+fp16"))), apply_to = function) diff --git a/include/simsimd/elementwise.h b/include/simsimd/elementwise.h index fb3e72a5..f4499fc9 100644 --- a/include/simsimd/elementwise.h +++ b/include/simsimd/elementwise.h @@ -1942,7 +1942,7 @@ SIMSIMD_PUBLIC void simsimd_fma_u8_sapphire( #endif // _SIMSIMD_TARGET_X86 #if _SIMSIMD_TARGET_ARM -#if SIMSIMD_TARGET_NEON +#if SIMSIMD_TARGET_NEON || SIMSIMD_TARGET_WASM #pragma GCC push_options #pragma GCC target("arch=armv8.2-a+simd") #pragma clang attribute push(__attribute__((target("arch=armv8.2-a+simd"))), apply_to = function) diff --git a/include/simsimd/probability.h b/include/simsimd/probability.h index b043299c..5b0f9deb 100644 --- a/include/simsimd/probability.h +++ b/include/simsimd/probability.h @@ -133,7 +133,7 @@ SIMSIMD_MAKE_KL(accurate, bf16, f64, SIMSIMD_BF16_TO_F32, SIMSIMD_F32_DIVISION_E SIMSIMD_MAKE_JS(accurate, bf16, f64, SIMSIMD_BF16_TO_F32, SIMSIMD_F32_DIVISION_EPSILON) // simsimd_js_bf16_accurate #if _SIMSIMD_TARGET_ARM -#if SIMSIMD_TARGET_NEON +#if SIMSIMD_TARGET_NEON || SIMSIMD_TARGET_WASM #pragma GCC push_options #pragma GCC target("arch=armv8.2-a+simd") #pragma clang attribute push(__attribute__((target("arch=armv8.2-a+simd"))), apply_to = function) diff --git a/include/simsimd/simsimd.h b/include/simsimd/simsimd.h index 56458528..7f8a851b 100644 --- a/include/simsimd/simsimd.h +++ b/include/simsimd/simsimd.h @@ -196,7 +196,7 @@ typedef enum { simsimd_cap_sve_i8_k = 1 << 27, ///< ARM SVE `i8` capability simsimd_cap_sve2_k = 1 << 28, ///< ARM SVE2 capability simsimd_cap_sve2p1_k = 1 << 29, ///< ARM SVE2p1 capability - + simsimd_cap_wasm_simd_k = 1 << 30, ///< WASM capability } simsimd_capability_t; /** @@ -504,6 +504,8 @@ SIMSIMD_PUBLIC simsimd_capability_t _simsimd_capabilities_arm(void) { (simsimd_cap_sve2_k * (supports_sve2)) | // (simsimd_cap_sve2p1_k * (supports_sve2p1)) | // (simsimd_cap_serial_k)); +#elif defined(SIMSIMD_TARGET_WASM) + return simsimd_cap_wasm_simd_k; #else // if !_SIMSIMD_DEFINED_LINUX return simsimd_cap_serial_k; #endif @@ -1465,6 +1467,7 @@ SIMSIMD_PUBLIC int simsimd_uses_sve_f16(void) { return _SIMSIMD_TARGET_ARM && SI SIMSIMD_PUBLIC int simsimd_uses_sve_bf16(void) { return _SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_SVE_BF16; } SIMSIMD_PUBLIC int simsimd_uses_sve_i8(void) { return _SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_SVE_I8; } SIMSIMD_PUBLIC int simsimd_uses_sve2(void) { return _SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_SVE2; } +SIMSIMD_PUBLIC int simsimd_uses_wasm_simd(void) { return _SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_WASM; } SIMSIMD_PUBLIC int simsimd_uses_haswell(void) { return _SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_HASWELL; } SIMSIMD_PUBLIC int simsimd_uses_skylake(void) { return _SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_SKYLAKE; } SIMSIMD_PUBLIC int simsimd_uses_ice(void) { return _SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_ICE; } @@ -1527,7 +1530,7 @@ SIMSIMD_PUBLIC void simsimd_dot_f16(simsimd_f16_t const *a, simsimd_f16_t const simsimd_distance_t *d) { #if SIMSIMD_TARGET_SVE_F16 simsimd_dot_f16_sve(a, b, n, d); -#elif SIMSIMD_TARGET_NEON_F16 +#elif SIMSIMD_TARGET_NEON_F16 || SIMSIMD_TARGET_WASM simsimd_dot_f16_neon(a, b, n, d); #elif SIMSIMD_TARGET_SAPPHIRE simsimd_dot_f16_sapphire(a, b, n, d); @@ -1553,7 +1556,7 @@ SIMSIMD_PUBLIC void simsimd_dot_f32(simsimd_f32_t const *a, simsimd_f32_t const simsimd_distance_t *d) { #if SIMSIMD_TARGET_SVE simsimd_dot_f32_sve(a, b, n, d); -#elif SIMSIMD_TARGET_NEON +#elif SIMSIMD_TARGET_NEON || SIMSIMD_TARGET_WASM simsimd_dot_f32_neon(a, b, n, d); #elif SIMSIMD_TARGET_SKYLAKE simsimd_dot_f32_skylake(a, b, n, d); @@ -1577,7 +1580,7 @@ SIMSIMD_PUBLIC void simsimd_dot_f16c(simsimd_f16c_t const *a, simsimd_f16c_t con simsimd_distance_t *d) { #if SIMSIMD_TARGET_SVE_F16 simsimd_dot_f16c_sve(a, b, n, d); -#elif SIMSIMD_TARGET_NEON_F16 +#elif SIMSIMD_TARGET_NEON_F16 || SIMSIMD_TARGET_WASM simsimd_dot_f16c_neon(a, b, n, d); #elif SIMSIMD_TARGET_SAPPHIRE simsimd_dot_f16c_sapphire(a, b, n, d); @@ -1601,7 +1604,7 @@ SIMSIMD_PUBLIC void simsimd_dot_f32c(simsimd_f32c_t const *a, simsimd_f32c_t con simsimd_distance_t *d) { #if SIMSIMD_TARGET_SVE simsimd_dot_f32c_sve(a, b, n, d); -#elif SIMSIMD_TARGET_NEON +#elif SIMSIMD_TARGET_NEON || SIMSIMD_TARGET_WASM simsimd_dot_f32c_neon(a, b, n, d); #elif SIMSIMD_TARGET_SKYLAKE simsimd_dot_f32c_skylake(a, b, n, d); @@ -1625,7 +1628,7 @@ SIMSIMD_PUBLIC void simsimd_vdot_f16c(simsimd_f16c_t const *a, simsimd_f16c_t co simsimd_distance_t *d) { #if SIMSIMD_TARGET_SVE simsimd_vdot_f16c_sve(a, b, n, d); -#elif SIMSIMD_TARGET_NEON +#elif SIMSIMD_TARGET_NEON || SIMSIMD_TARGET_WASM simsimd_dot_f16c_neon(a, b, n, d); #elif SIMSIMD_TARGET_SAPPHIRE simsimd_dot_f16c_sapphire(a, b, n, d); @@ -1649,7 +1652,7 @@ SIMSIMD_PUBLIC void simsimd_vdot_f32c(simsimd_f32c_t const *a, simsimd_f32c_t co simsimd_distance_t *d) { #if SIMSIMD_TARGET_SVE simsimd_vdot_f32c_sve(a, b, n, d); -#elif SIMSIMD_TARGET_NEON +#elif SIMSIMD_TARGET_NEON || SIMSIMD_TARGET_WASM simsimd_dot_f32c_neon(a, b, n, d); #elif SIMSIMD_TARGET_SKYLAKE simsimd_dot_f32c_skylake(a, b, n, d); @@ -1711,7 +1714,7 @@ SIMSIMD_PUBLIC void simsimd_cos_f16(simsimd_f16_t const *a, simsimd_f16_t const simsimd_distance_t *d) { #if SIMSIMD_TARGET_SVE_F16 simsimd_cos_f16_sve(a, b, n, d); -#elif SIMSIMD_TARGET_NEON_F16 +#elif SIMSIMD_TARGET_NEON_F16 || SIMSIMD_TARGET_WASM simsimd_cos_f16_neon(a, b, n, d); #elif SIMSIMD_TARGET_SAPPHIRE simsimd_cos_f16_sapphire(a, b, n, d); @@ -1739,7 +1742,7 @@ SIMSIMD_PUBLIC void simsimd_cos_f32(simsimd_f32_t const *a, simsimd_f32_t const simsimd_distance_t *d) { #if SIMSIMD_TARGET_SVE simsimd_cos_f32_sve(a, b, n, d); -#elif SIMSIMD_TARGET_NEON +#elif SIMSIMD_TARGET_NEON || SIMSIMD_TARGET_WASM simsimd_cos_f32_neon(a, b, n, d); #elif SIMSIMD_TARGET_SKYLAKE simsimd_cos_f32_skylake(a, b, n, d); @@ -1753,7 +1756,7 @@ SIMSIMD_PUBLIC void simsimd_cos_f64(simsimd_f64_t const *a, simsimd_f64_t const simsimd_distance_t *d) { #if SIMSIMD_TARGET_SVE simsimd_cos_f64_sve(a, b, n, d); -#elif SIMSIMD_TARGET_NEON +#elif SIMSIMD_TARGET_NEON || SIMSIMD_TARGET_WASM simsimd_cos_f64_neon(a, b, n, d); #elif SIMSIMD_TARGET_SKYLAKE simsimd_cos_f64_skylake(a, b, n, d); @@ -1789,7 +1792,7 @@ SIMSIMD_PUBLIC void simsimd_l2sq_f16(simsimd_f16_t const *a, simsimd_f16_t const simsimd_distance_t *d) { #if SIMSIMD_TARGET_SVE_F16 simsimd_l2sq_f16_sve(a, b, n, d); -#elif SIMSIMD_TARGET_NEON_F16 +#elif SIMSIMD_TARGET_NEON_F16 || SIMSIMD_TARGET_WASM simsimd_l2sq_f16_neon(a, b, n, d); #elif SIMSIMD_TARGET_SAPPHIRE simsimd_l2sq_f16_sapphire(a, b, n, d); @@ -1817,7 +1820,7 @@ SIMSIMD_PUBLIC void simsimd_l2sq_f32(simsimd_f32_t const *a, simsimd_f32_t const simsimd_distance_t *d) { #if SIMSIMD_TARGET_SVE simsimd_l2sq_f32_sve(a, b, n, d); -#elif SIMSIMD_TARGET_NEON +#elif SIMSIMD_TARGET_NEON || SIMSIMD_TARGET_WASM simsimd_l2sq_f32_neon(a, b, n, d); #elif SIMSIMD_TARGET_SKYLAKE simsimd_l2sq_f32_skylake(a, b, n, d); @@ -1831,7 +1834,7 @@ SIMSIMD_PUBLIC void simsimd_l2sq_f64(simsimd_f64_t const *a, simsimd_f64_t const simsimd_distance_t *d) { #if SIMSIMD_TARGET_SVE simsimd_l2sq_f64_sve(a, b, n, d); -#elif SIMSIMD_TARGET_NEON +#elif SIMSIMD_TARGET_NEON || SIMSIMD_TARGET_WASM simsimd_l2sq_f64_neon(a, b, n, d); #elif SIMSIMD_TARGET_SKYLAKE simsimd_l2sq_f64_skylake(a, b, n, d); @@ -1935,7 +1938,7 @@ SIMSIMD_PUBLIC void simsimd_hamming_b8(simsimd_b8_t const *a, simsimd_b8_t const simsimd_distance_t *d) { #if SIMSIMD_TARGET_SVE simsimd_hamming_b8_sve(a, b, n, d); -#elif SIMSIMD_TARGET_NEON +#elif SIMSIMD_TARGET_NEON || SIMSIMD_TARGET_WASM simsimd_hamming_b8_neon(a, b, n, d); #elif SIMSIMD_TARGET_ICE simsimd_hamming_b8_ice(a, b, n, d); @@ -1949,7 +1952,7 @@ SIMSIMD_PUBLIC void simsimd_jaccard_b8(simsimd_b8_t const *a, simsimd_b8_t const simsimd_distance_t *d) { #if SIMSIMD_TARGET_SVE simsimd_jaccard_b8_sve(a, b, n, d); -#elif SIMSIMD_TARGET_NEON +#elif SIMSIMD_TARGET_NEON || SIMSIMD_TARGET_WASM simsimd_jaccard_b8_neon(a, b, n, d); #elif SIMSIMD_TARGET_ICE simsimd_jaccard_b8_ice(a, b, n, d); @@ -1990,7 +1993,7 @@ SIMSIMD_PUBLIC void simsimd_kl_bf16(simsimd_bf16_t const *a, simsimd_bf16_t cons } SIMSIMD_PUBLIC void simsimd_kl_f32(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n, simsimd_distance_t *d) { -#if SIMSIMD_TARGET_NEON +#if SIMSIMD_TARGET_NEON || SIMSIMD_TARGET_WASM simsimd_kl_f32_neon(a, b, n, d); #elif SIMSIMD_TARGET_SKYLAKE simsimd_kl_f32_skylake(a, b, n, d); @@ -2018,7 +2021,7 @@ SIMSIMD_PUBLIC void simsimd_js_bf16(simsimd_bf16_t const *a, simsimd_bf16_t cons } SIMSIMD_PUBLIC void simsimd_js_f32(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n, simsimd_distance_t *d) { -#if SIMSIMD_TARGET_NEON +#if SIMSIMD_TARGET_NEON || SIMSIMD_TARGET_WASM simsimd_js_f32_neon(a, b, n, d); #elif SIMSIMD_TARGET_SKYLAKE simsimd_js_f32_skylake(a, b, n, d); diff --git a/include/simsimd/spatial.h b/include/simsimd/spatial.h index 9b3db048..edb8816e 100644 --- a/include/simsimd/spatial.h +++ b/include/simsimd/spatial.h @@ -251,7 +251,7 @@ SIMSIMD_MAKE_L2SQ(accurate, bf16, f64, SIMSIMD_BF16_TO_F32) // simsimd_l2sq_bf16 SIMSIMD_MAKE_L2(accurate, bf16, f64, SIMSIMD_BF16_TO_F32) // simsimd_l2_bf16_accurate #if _SIMSIMD_TARGET_ARM -#if SIMSIMD_TARGET_NEON +#if SIMSIMD_TARGET_NEON || SIMSIMD_TARGET_WASM #pragma GCC push_options #pragma GCC target("arch=armv8.2-a+simd") #pragma clang attribute push(__attribute__((target("arch=armv8.2-a+simd"))), apply_to = function) @@ -395,7 +395,7 @@ SIMSIMD_PUBLIC void simsimd_cos_f64_neon(simsimd_f64_t const *a, simsimd_f64_t c #pragma GCC pop_options #endif // SIMSIMD_TARGET_NEON -#if SIMSIMD_TARGET_NEON_F16 +#if SIMSIMD_TARGET_NEON_F16 || SIMSIMD_TARGET_WASM #pragma GCC push_options #pragma GCC target("arch=armv8.2-a+simd+fp16") #pragma clang attribute push(__attribute__((target("arch=armv8.2-a+simd+fp16"))), apply_to = function) diff --git a/include/simsimd/types.h b/include/simsimd/types.h index 6ea6c772..b8eccfe5 100644 --- a/include/simsimd/types.h +++ b/include/simsimd/types.h @@ -227,7 +227,7 @@ #include #else -#if SIMSIMD_TARGET_NEON +#if SIMSIMD_TARGET_NEON || SIMSIMD_TARGET_WASM #include #endif diff --git a/scripts/test.c b/scripts/test.c index 4d590c74..f3e3322d 100644 --- a/scripts/test.c +++ b/scripts/test.c @@ -69,6 +69,7 @@ void test_utilities(void) { int uses_sapphire = simsimd_uses_sapphire(); int uses_turin = simsimd_uses_turin(); int uses_sierra = simsimd_uses_sierra(); + int uses_wasm = simsimd_uses_wasm_simd(); assert(uses_neon == ((capabilities & simsimd_cap_neon_k) != 0)); assert(uses_sve == ((capabilities & simsimd_cap_sve_k) != 0)); @@ -79,6 +80,7 @@ void test_utilities(void) { assert(uses_sapphire == ((capabilities & simsimd_cap_sapphire_k) != 0)); assert(uses_turin == ((capabilities & simsimd_cap_turin_k) != 0)); assert(uses_sierra == ((capabilities & simsimd_cap_sierra_k) != 0)); + assert(uses_wasm == ((capabilities & simsimd_cap_wasm_simd_k) != 0)); } /**