Mercurial > vec
changeset 28:c6c99ab1088a
*: add min/max functions and a big big refactor (again)
agh, this time I added a few more implementations (and generally
made the code just a little faster...)
author | Paper <paper@tflc.us> |
---|---|
date | Thu, 24 Apr 2025 00:54:02 -0400 |
parents | d00b95f95dd1 |
children | e59c91d050c0 |
files | CMakeLists.txt include/vec/cpu.h include/vec/impl/gcc.h include/vec/impl/generic.h include/vec/impl/ppc/altivec.h include/vec/impl/x86/avx512bw.h include/vec/impl/x86/avx512dq.h include/vec/impl/x86/avx512f.h include/vec/impl/x86/sse2.h include/vec/impl/x86/sse3.h include/vec/impl/x86/sse41.h include/vec/impl/x86/sse42.h include/vec/vec.h src/cpu.c src/impl/arm/neon.c src/impl/fallback.c src/impl/gcc.c src/impl/generic.c src/impl/ppc/altivec.c src/impl/x86/avx2.c src/impl/x86/avx512bw.c src/impl/x86/avx512dq.c src/impl/x86/avx512f.c src/impl/x86/mmx.c src/impl/x86/sse2.c src/impl/x86/sse3.c src/impl/x86/sse41.c src/impl/x86/sse42.c src/vec.c test/test_arith.h |
diffstat | 30 files changed, 2346 insertions(+), 942 deletions(-) [+] |
line wrap: on
line diff
--- a/CMakeLists.txt Mon Nov 25 00:33:02 2024 -0500 +++ b/CMakeLists.txt Thu Apr 24 00:54:02 2025 -0400 @@ -2,7 +2,13 @@ project(vec VERSION 3.0.0 DESCRIPTION "a tiny C99 SIMD vector library" LANGUAGES C) -add_library(vec SHARED "src/vec.c;src/cpu.c;src/impl/generic.c;src/impl/fallback.c") +add_library(vec "src/vec.c") + +target_sources(vec PRIVATE + "src/cpu.c" + "src/impl/generic.c" + # "src/impl/fallback.c" -- deadcode +) include(CheckCCompilerFlag) @@ -49,10 +55,18 @@ if(COMPILER_HAS_SSE2) set(COMPILER_SSE2_FLAGS "-msse2") endif() + check_c_compiler_flag("-msse3" COMPILER_HAS_SSE3) + if(COMPILER_HAS_SSE3) + set(COMPILER_SSE3_FLAGS "-msse3") + endif() check_c_compiler_flag("-msse4.1" COMPILER_HAS_SSE41) if(COMPILER_HAS_SSE41) set(COMPILER_SSE41_FLAGS "-msse4.1") endif() + check_c_compiler_flag("-msse4.2" COMPILER_HAS_SSE42) + if(COMPILER_HAS_SSE42) + set(COMPILER_SSE42_FLAGS "-msse4.2") + endif() check_c_compiler_flag("-mavx2" COMPILER_HAS_AVX2) if(COMPILER_HAS_AVX2) set(COMPILER_AVX2_FLAGS "-mavx2") @@ -61,6 +75,14 @@ if(COMPILER_HAS_AVX512F) set(COMPILER_AVX512F_FLAGS "-mavx512f") endif() + check_c_compiler_flag("-mavx512bw" COMPILER_HAS_AVX512BW) + if(COMPILER_HAS_AVX512BW) + set(COMPILER_AVX512BW_FLAGS "-mavx512bw") + endif() + check_c_compiler_flag("-mavx512dq" COMPILER_HAS_AVX512DQ) + if(COMPILER_HAS_AVX512DQ) + set(COMPILER_AVX512DQ_FLAGS "-mavx512dq") + endif() endif() if(COMPILER_HAS_ALTIVEC) @@ -87,12 +109,24 @@ target_compile_definitions(vec PRIVATE "-DVEC_COMPILER_HAS_SSE2") endif() +if(COMPILER_HAS_SSE3) + target_sources(vec PRIVATE "src/impl/x86/sse3.c") + set_source_files_properties("src/impl/x86/sse3.c" PROPERTIES COMPILE_FLAGS "${COMPILER_SSE3_FLAGS}") + target_compile_definitions(vec PRIVATE "-DVEC_COMPILER_HAS_SSE3") +endif() + if(COMPILER_HAS_SSE41) target_sources(vec PRIVATE "src/impl/x86/sse41.c") set_source_files_properties("src/impl/x86/sse41.c" PROPERTIES COMPILE_FLAGS "${COMPILER_SSE41_FLAGS}") target_compile_definitions(vec PRIVATE "-DVEC_COMPILER_HAS_SSE41") endif() +if(COMPILER_HAS_SSE42) + target_sources(vec PRIVATE "src/impl/x86/sse42.c") + set_source_files_properties("src/impl/x86/sse42.c" PROPERTIES COMPILE_FLAGS "${COMPILER_SSE42_FLAGS}") + target_compile_definitions(vec PRIVATE "-DVEC_COMPILER_HAS_SSE42") +endif() + if(COMPILER_HAS_AVX2) target_sources(vec PRIVATE "src/impl/x86/avx2.c") set_source_files_properties("src/impl/x86/avx2.c" PROPERTIES COMPILE_FLAGS "${COMPILER_AVX2_FLAGS}") @@ -105,6 +139,17 @@ target_compile_definitions(vec PRIVATE "-DVEC_COMPILER_HAS_AVX512F") endif() +if(COMPILER_HAS_AVX512BW) + target_sources(vec PRIVATE "src/impl/x86/avx512bw.c") + set_source_files_properties("src/impl/x86/avx512bw.c" PROPERTIES COMPILE_FLAGS "${COMPILER_AVX512BW_FLAGS}") + target_compile_definitions(vec PRIVATE "-DVEC_COMPILER_HAS_AVX512BW") +endif() + +if(COMPILER_HAS_AVX512DQ) + target_sources(vec PRIVATE "src/impl/x86/avx512dq.c") + set_source_files_properties("src/impl/x86/avx512dq.c" PROPERTIES COMPILE_FLAGS "${COMPILER_AVX512DQ_FLAGS}") + target_compile_definitions(vec PRIVATE "-DVEC_COMPILER_HAS_AVX512DQ") +endif() ######################################################################### # integer types; it's nice to accommodate for older broken systems that
--- a/include/vec/cpu.h Mon Nov 25 00:33:02 2024 -0500 +++ b/include/vec/cpu.h Thu Apr 24 00:54:02 2025 -0400 @@ -42,6 +42,8 @@ VEC_CPU_HAS_AVX2 = (1 << 9), VEC_CPU_HAS_AVX512F = (1 << 10), VEC_CPU_HAS_NEON = (1 << 11), + VEC_CPU_HAS_AVX512BW = (1 << 12), + VEC_CPU_HAS_AVX512DQ = (1 << 13), }; // NOT thread-safe.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/vec/impl/gcc.h Thu Apr 24 00:54:02 2025 -0400 @@ -0,0 +1,80 @@ +/** + * vec - a tiny SIMD vector library in plain C99 + * + * Copyright (c) 2024 Paper + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +**/ + +/* Generic array-based implementation. */ + +#ifndef VEC_IMPL_GENERIC_H_ +#define VEC_IMPL_GENERIC_H_ + +#include "vec/vec.h" + +// 16-bit +extern const vint8x2_impl vint8x2_impl_generic; +extern const vuint8x2_impl vuint8x2_impl_generic; + +// 32-bit +extern const vint8x4_impl vint8x4_impl_generic; +extern const vuint8x4_impl vuint8x4_impl_generic; +extern const vint16x2_impl vint16x2_impl_generic; +extern const vuint16x2_impl vuint16x2_impl_generic; + +// 64-bit +extern const vint8x8_impl vint8x8_impl_generic; +extern const vuint8x8_impl vuint8x8_impl_generic; +extern const vint16x4_impl vint16x4_impl_generic; +extern const vuint16x4_impl vuint16x4_impl_generic; +extern const vint32x2_impl vint32x2_impl_generic; +extern const vuint32x2_impl vuint32x2_impl_generic; + +// 128-bit +extern const vint8x16_impl vint8x16_impl_generic; +extern const vuint8x16_impl vuint8x16_impl_generic; +extern const vint16x8_impl vint16x8_impl_generic; +extern const vuint16x8_impl vuint16x8_impl_generic; +extern const vint32x4_impl vint32x4_impl_generic; +extern const vuint32x4_impl vuint32x4_impl_generic; +extern const vint64x2_impl vint64x2_impl_generic; +extern const vuint64x2_impl vuint64x2_impl_generic; + +// 256-bit +extern const vint8x32_impl vint8x32_impl_generic; +extern const vuint8x32_impl vuint8x32_impl_generic; +extern const vint16x16_impl vint16x16_impl_generic; +extern const vuint16x16_impl vuint16x16_impl_generic; +extern const vint32x8_impl vint32x8_impl_generic; +extern const vuint32x8_impl vuint32x8_impl_generic; +extern const vint64x4_impl vint64x4_impl_generic; +extern const vuint64x4_impl vuint64x4_impl_generic; + +// 512-bit +extern const vint8x64_impl vint8x64_impl_generic; +extern const vuint8x64_impl vuint8x64_impl_generic; +extern const vint16x32_impl vint16x32_impl_generic; +extern const vuint16x32_impl vuint16x32_impl_generic; +extern const vint32x16_impl vint32x16_impl_generic; +extern const vuint32x16_impl vuint32x16_impl_generic; +extern const vint64x8_impl vint64x8_impl_generic; +extern const vuint64x8_impl vuint64x8_impl_generic; + +#endif /* VEC_IMPL_GENERIC_H_ */
--- a/include/vec/impl/generic.h Mon Nov 25 00:33:02 2024 -0500 +++ b/include/vec/impl/generic.h Thu Apr 24 00:54:02 2025 -0400 @@ -29,65 +29,6 @@ #include "vec/vec.h" -#define VEC_DEFINE_GENERIC_OPERATIONS_SIGN(sign, csign, bits, size) \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_splat(vec_##sign##int##bits x); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_load(const vec_##sign##int##bits in[size]); \ - void v##sign##int##bits##x##size##_generic_store(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_div(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_avg(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_not(v##sign##int##bits##x##size vec); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmplt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmple(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmpeq(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmpge(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmpgt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_lshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_rshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_lrshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2); - -#define VEC_DEFINE_GENERIC_OPERATIONS(bits, size) \ - VEC_DEFINE_GENERIC_OPERATIONS_SIGN( , , bits, size) \ - VEC_DEFINE_GENERIC_OPERATIONS_SIGN(u, U, bits, size) - -// 16-bit -VEC_DEFINE_GENERIC_OPERATIONS(8, 2) - -// 32-bit -VEC_DEFINE_GENERIC_OPERATIONS(8, 4) -VEC_DEFINE_GENERIC_OPERATIONS(16, 2) - -// 64-bit -VEC_DEFINE_GENERIC_OPERATIONS(8, 8) -VEC_DEFINE_GENERIC_OPERATIONS(16, 4) -VEC_DEFINE_GENERIC_OPERATIONS(32, 2) - -// 128-bit -VEC_DEFINE_GENERIC_OPERATIONS(8, 16) -VEC_DEFINE_GENERIC_OPERATIONS(16, 8) -VEC_DEFINE_GENERIC_OPERATIONS(32, 4) -VEC_DEFINE_GENERIC_OPERATIONS(64, 2) - -// 256-bit -VEC_DEFINE_GENERIC_OPERATIONS(8, 32) -VEC_DEFINE_GENERIC_OPERATIONS(16, 16) -VEC_DEFINE_GENERIC_OPERATIONS(32, 8) -VEC_DEFINE_GENERIC_OPERATIONS(64, 4) - -// 512-bit -VEC_DEFINE_GENERIC_OPERATIONS(8, 64) -VEC_DEFINE_GENERIC_OPERATIONS(16, 32) -VEC_DEFINE_GENERIC_OPERATIONS(32, 16) -VEC_DEFINE_GENERIC_OPERATIONS(64, 8) - -#undef VEC_DEFINE_GENERIC_OPERATIONS -#undef VEC_DEFINE_GENERIC_OPERATIONS_SIGN - // 16-bit extern const vint8x2_impl vint8x2_impl_generic; extern const vuint8x2_impl vuint8x2_impl_generic;
--- a/include/vec/impl/ppc/altivec.h Mon Nov 25 00:33:02 2024 -0500 +++ b/include/vec/impl/ppc/altivec.h Thu Apr 24 00:54:02 2025 -0400 @@ -22,8 +22,6 @@ * SOFTWARE. **/ -/* Altivec vector support. */ - #ifndef VEC_IMPL_PPC_ALTIVEC_H_ #define VEC_IMPL_PPC_ALTIVEC_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/vec/impl/x86/avx512bw.h Thu Apr 24 00:54:02 2025 -0400 @@ -0,0 +1,35 @@ +/** + * vec - a tiny SIMD vector library in C99 + * + * Copyright (c) 2024 Paper + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +**/ + +#ifndef VEC_IMPL_X86_AVX512BW_H_ +#define VEC_IMPL_X86_AVX512BW_H_ + +#include "vec/vec.h" + +extern const vint8x64_impl vint8x64_impl_avx512bw; +extern const vint16x32_impl vint16x32_impl_avx512bw; +extern const vuint8x64_impl vuint8x64_impl_avx512bw; +extern const vuint16x32_impl vuint16x32_impl_avx512bw; + +#endif /* VEC_IMPL_X86_AVX512BW_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/vec/impl/x86/avx512dq.h Thu Apr 24 00:54:02 2025 -0400 @@ -0,0 +1,33 @@ +/** + * vec - a tiny SIMD vector library in C99 + * + * Copyright (c) 2024 Paper + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +**/ + +#ifndef VEC_IMPL_X86_AVX512DQ_H_ +#define VEC_IMPL_X86_AVX512DQ_H_ + +#include "vec/vec.h" + +extern const vint64x8_impl vint64x8_impl_avx512dq; +extern const vuint64x8_impl vuint64x8_impl_avx512dq; + +#endif /* VEC_IMPL_X86_AVX512DQ_H_ */
--- a/include/vec/impl/x86/avx512f.h Mon Nov 25 00:33:02 2024 -0500 +++ b/include/vec/impl/x86/avx512f.h Thu Apr 24 00:54:02 2025 -0400 @@ -27,12 +27,8 @@ #include "vec/vec.h" -extern const vint8x64_impl vint8x64_impl_avx512f; -extern const vint16x32_impl vint16x32_impl_avx512f; extern const vint32x16_impl vint32x16_impl_avx512f; extern const vint64x8_impl vint64x8_impl_avx512f; -extern const vuint8x64_impl vuint8x64_impl_avx512f; -extern const vuint16x32_impl vuint16x32_impl_avx512f; extern const vuint32x16_impl vuint32x16_impl_avx512f; extern const vuint64x8_impl vuint64x8_impl_avx512f;
--- a/include/vec/impl/x86/sse2.h Mon Nov 25 00:33:02 2024 -0500 +++ b/include/vec/impl/x86/sse2.h Thu Apr 24 00:54:02 2025 -0400 @@ -27,33 +27,6 @@ #include "vec/vec.h" -// These are only extern because the SSE 4.1 translation unit needs to access it. -#define VEC_DEFINE_SSE2_OPERATIONS_SIGN(sign, csign, bits, size) \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_splat(vec_##sign##int##bits x); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_load_aligned(const vec_##sign##int##bits in[size]); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_load(const vec_##sign##int##bits in[size]); \ - void v##sign##int##bits##x##size##_sse2_store_aligned(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]); \ - void v##sign##int##bits##x##size##_sse2_store(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_cmpeq(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); - -#define VEC_DEFINE_SSE2_OPERATIONS(bits, size) \ - VEC_DEFINE_SSE2_OPERATIONS_SIGN( , , bits, size) \ - VEC_DEFINE_SSE2_OPERATIONS_SIGN(u, U, bits, size) - -VEC_DEFINE_SSE2_OPERATIONS(8, 16) -VEC_DEFINE_SSE2_OPERATIONS(16, 8) -VEC_DEFINE_SSE2_OPERATIONS(32, 4) -VEC_DEFINE_SSE2_OPERATIONS(64, 2) - -#undef VEC_DEFINE_SSE2_OPERATIONS -#undef VEC_DEFINE_SSE2_OPERATIONS_SIGN - extern const vint8x16_impl vint8x16_impl_sse2; extern const vint16x8_impl vint16x8_impl_sse2; extern const vint32x4_impl vint32x4_impl_sse2;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/vec/impl/x86/sse3.h Thu Apr 24 00:54:02 2025 -0400 @@ -0,0 +1,39 @@ +/** + * vec - a tiny SIMD vector library in C99 + * + * Copyright (c) 2024 Paper + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +**/ + +#ifndef VEC_IMPL_X86_SSE3_H_ +#define VEC_IMPL_X86_SSE3_H_ + +#include "vec/vec.h" + +extern const vint8x16_impl vint8x16_impl_sse3; +extern const vint16x8_impl vint16x8_impl_sse3; +extern const vint32x4_impl vint32x4_impl_sse3; +extern const vint64x2_impl vint64x2_impl_sse3; +extern const vuint8x16_impl vuint8x16_impl_sse3; +extern const vuint16x8_impl vuint16x8_impl_sse3; +extern const vuint32x4_impl vuint32x4_impl_sse3; +extern const vuint64x2_impl vuint64x2_impl_sse3; + +#endif /* VEC_IMPL_X86_SSE3_H_ */
--- a/include/vec/impl/x86/sse41.h Mon Nov 25 00:33:02 2024 -0500 +++ b/include/vec/impl/x86/sse41.h Thu Apr 24 00:54:02 2025 -0400 @@ -27,7 +27,13 @@ #include "vec/vec.h" -extern const vint32x4_impl vint32x4_impl_sse41; +extern const vint8x16_impl vint8x16_impl_sse41; +extern const vint16x8_impl vint16x8_impl_sse41; +extern const vint32x4_impl vint32x4_impl_sse41; +extern const vint64x2_impl vint64x2_impl_sse41; +extern const vuint8x16_impl vuint8x16_impl_sse41; +extern const vuint16x8_impl vuint16x8_impl_sse41; extern const vuint32x4_impl vuint32x4_impl_sse41; +extern const vuint64x2_impl vuint64x2_impl_sse41; #endif /* VEC_IMPL_X86_SSE41_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/vec/impl/x86/sse42.h Thu Apr 24 00:54:02 2025 -0400 @@ -0,0 +1,33 @@ +/** + * vec - a tiny SIMD vector library in C99 + * + * Copyright (c) 2024 Paper + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +**/ + +#ifndef VEC_IMPL_X86_SSE42_H_ +#define VEC_IMPL_X86_SSE42_H_ + +#include "vec/vec.h" + +extern const vint64x2_impl vint64x2_impl_sse42; +extern const vuint64x2_impl vuint64x2_impl_sse42; + +#endif /* VEC_IMPL_X86_SSE42_H_ */
--- a/include/vec/vec.h Mon Nov 25 00:33:02 2024 -0500 +++ b/include/vec/vec.h Thu Apr 24 00:54:02 2025 -0400 @@ -84,6 +84,16 @@ [!!sizeof (struct { int __error_if_negative: (x) ? 2 : -1; })] #endif +#if VEC_GNUC_HAS_ATTRIBUTE(__always_inline__, 3, 1, 1) +# define VEC_ALWAYS_INLINE __attribute__((__always_inline__)) +#elif VEC_MSVC_ATLEAST(12, 0, 0) +# define VEC_ALWAYS_INLINE __forceinline +#else +# define VEC_ALWAYS_INLINE +#endif + +#define VEC_FUNC_IMPL static inline VEC_ALWAYS_INLINE + ////////////////////////////////////////////////////////////////////////////// // Detect compiler SIMD support @@ -675,7 +685,6 @@ v##sign##int##bits##x##size (*band)(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ v##sign##int##bits##x##size (*bor)(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ v##sign##int##bits##x##size (*bxor)(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ - v##sign##int##bits##x##size (*bnot)(v##sign##int##bits##x##size vec); \ v##sign##int##bits##x##size (*lshift)(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2); \ v##sign##int##bits##x##size (*rshift)(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2); \ v##sign##int##bits##x##size (*lrshift)(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2); \ @@ -684,6 +693,8 @@ v##sign##int##bits##x##size (*cmpeq)(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ v##sign##int##bits##x##size (*cmpge)(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ v##sign##int##bits##x##size (*cmpgt)(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size (*min)(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + v##sign##int##bits##x##size (*max)(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ } v##sign##int##bits##x##size##_impl; #define VEC_DEFINE_IMPL_STRUCT(bits, size) \ @@ -723,53 +734,56 @@ #undef VEC_DEFINE_IMPL_STRUCT #undef VEC_DEFINE_IMPL_STRUCT_SIGN +/* these are generally read only, unless you REALLY know + * exactly what you're doing. */ + // 16-bit -extern const vint8x2_impl *vint8x2_impl_cpu; -extern const vuint8x2_impl *vuint8x2_impl_cpu; +extern vint8x2_impl vint8x2_impl_cpu; +extern vuint8x2_impl vuint8x2_impl_cpu; // 32-bit -extern const vint8x4_impl *vint8x4_impl_cpu; -extern const vuint8x4_impl *vuint8x4_impl_cpu; -extern const vint16x2_impl *vint16x2_impl_cpu; -extern const vuint16x2_impl *vuint16x2_impl_cpu; +extern vint8x4_impl vint8x4_impl_cpu; +extern vuint8x4_impl vuint8x4_impl_cpu; +extern vint16x2_impl vint16x2_impl_cpu; +extern vuint16x2_impl vuint16x2_impl_cpu; // 64-bit -extern const vint8x8_impl *vint8x8_impl_cpu; -extern const vuint8x8_impl *vuint8x8_impl_cpu; -extern const vint16x4_impl *vint16x4_impl_cpu; -extern const vuint16x4_impl *vuint16x4_impl_cpu; -extern const vint32x2_impl *vint32x2_impl_cpu; -extern const vuint32x2_impl *vuint32x2_impl_cpu; +extern vint8x8_impl vint8x8_impl_cpu; +extern vuint8x8_impl vuint8x8_impl_cpu; +extern vint16x4_impl vint16x4_impl_cpu; +extern vuint16x4_impl vuint16x4_impl_cpu; +extern vint32x2_impl vint32x2_impl_cpu; +extern vuint32x2_impl vuint32x2_impl_cpu; // 128-bit -extern const vint8x16_impl *vint8x16_impl_cpu; -extern const vuint8x16_impl *vuint8x16_impl_cpu; -extern const vint16x8_impl *vint16x8_impl_cpu; -extern const vuint16x8_impl *vuint16x8_impl_cpu; -extern const vint32x4_impl *vint32x4_impl_cpu; -extern const vuint32x4_impl *vuint32x4_impl_cpu; -extern const vint64x2_impl *vint64x2_impl_cpu; -extern const vuint64x2_impl *vuint64x2_impl_cpu; +extern vint8x16_impl vint8x16_impl_cpu; +extern vuint8x16_impl vuint8x16_impl_cpu; +extern vint16x8_impl vint16x8_impl_cpu; +extern vuint16x8_impl vuint16x8_impl_cpu; +extern vint32x4_impl vint32x4_impl_cpu; +extern vuint32x4_impl vuint32x4_impl_cpu; +extern vint64x2_impl vint64x2_impl_cpu; +extern vuint64x2_impl vuint64x2_impl_cpu; // 256-bit -extern const vint8x32_impl *vint8x32_impl_cpu; -extern const vuint8x32_impl *vuint8x32_impl_cpu; -extern const vint16x16_impl *vint16x16_impl_cpu; -extern const vuint16x16_impl *vuint16x16_impl_cpu; -extern const vint32x8_impl *vint32x8_impl_cpu; -extern const vuint32x8_impl *vuint32x8_impl_cpu; -extern const vint64x4_impl *vint64x4_impl_cpu; -extern const vuint64x4_impl *vuint64x4_impl_cpu; +extern vint8x32_impl vint8x32_impl_cpu; +extern vuint8x32_impl vuint8x32_impl_cpu; +extern vint16x16_impl vint16x16_impl_cpu; +extern vuint16x16_impl vuint16x16_impl_cpu; +extern vint32x8_impl vint32x8_impl_cpu; +extern vuint32x8_impl vuint32x8_impl_cpu; +extern vint64x4_impl vint64x4_impl_cpu; +extern vuint64x4_impl vuint64x4_impl_cpu; // 512-bit -extern const vint8x64_impl *vint8x64_impl_cpu; -extern const vuint8x64_impl *vuint8x64_impl_cpu; -extern const vint16x32_impl *vint16x32_impl_cpu; -extern const vuint16x32_impl *vuint16x32_impl_cpu; -extern const vint32x16_impl *vint32x16_impl_cpu; -extern const vuint32x16_impl *vuint32x16_impl_cpu; -extern const vint64x8_impl *vint64x8_impl_cpu; -extern const vuint64x8_impl *vuint64x8_impl_cpu; +extern vint8x64_impl vint8x64_impl_cpu; +extern vuint8x64_impl vuint8x64_impl_cpu; +extern vint16x32_impl vint16x32_impl_cpu; +extern vuint16x32_impl vuint16x32_impl_cpu; +extern vint32x16_impl vint32x16_impl_cpu; +extern vuint32x16_impl vuint32x16_impl_cpu; +extern vint64x8_impl vint64x8_impl_cpu; +extern vuint64x8_impl vuint64x8_impl_cpu; ////////////////////////////////////////////////////////////////////////////// // declared as inline for ! performance : ) @@ -777,112 +791,122 @@ #define VEC_DEFINE_OPERATIONS_SIGN(sign, bits, size) \ inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_splat(vec_##sign##int##bits x) \ { \ - return v##sign##int##bits##x##size##_impl_cpu->splat(x); \ + return v##sign##int##bits##x##size##_impl_cpu.splat(x); \ } \ \ inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_load_aligned(const vec_##sign##int##bits in[size]) \ { \ - return v##sign##int##bits##x##size##_impl_cpu->load_aligned(in); \ + return v##sign##int##bits##x##size##_impl_cpu.load_aligned(in); \ } \ \ inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_load(const vec_##sign##int##bits in[size]) \ { \ - return v##sign##int##bits##x##size##_impl_cpu->load(in); \ + return v##sign##int##bits##x##size##_impl_cpu.load(in); \ } \ \ inline void v##sign##int##bits##x##size##_store_aligned(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ { \ - v##sign##int##bits##x##size##_impl_cpu->store_aligned(vec, out); \ + v##sign##int##bits##x##size##_impl_cpu.store_aligned(vec, out); \ } \ \ inline void v##sign##int##bits##x##size##_store(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ { \ - return v##sign##int##bits##x##size##_impl_cpu->store(vec, out); \ + return v##sign##int##bits##x##size##_impl_cpu.store(vec, out); \ } \ \ inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ - return v##sign##int##bits##x##size##_impl_cpu->add(vec1, vec2); \ + return v##sign##int##bits##x##size##_impl_cpu.add(vec1, vec2); \ } \ \ inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ - return v##sign##int##bits##x##size##_impl_cpu->sub(vec1, vec2); \ + return v##sign##int##bits##x##size##_impl_cpu.sub(vec1, vec2); \ } \ \ inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ - return v##sign##int##bits##x##size##_impl_cpu->mul(vec1, vec2); \ + return v##sign##int##bits##x##size##_impl_cpu.mul(vec1, vec2); \ } \ \ inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_div(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ - return v##sign##int##bits##x##size##_impl_cpu->div(vec1, vec2); \ + return v##sign##int##bits##x##size##_impl_cpu.div(vec1, vec2); \ } \ \ inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_avg(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ - return v##sign##int##bits##x##size##_impl_cpu->avg(vec1, vec2); \ + return v##sign##int##bits##x##size##_impl_cpu.avg(vec1, vec2); \ } \ \ inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ - return v##sign##int##bits##x##size##_impl_cpu->band(vec1, vec2); \ + return v##sign##int##bits##x##size##_impl_cpu.band(vec1, vec2); \ } \ \ inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ - return v##sign##int##bits##x##size##_impl_cpu->bor(vec1, vec2); \ + return v##sign##int##bits##x##size##_impl_cpu.bor(vec1, vec2); \ } \ \ inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ - return v##sign##int##bits##x##size##_impl_cpu->bxor(vec1, vec2); \ + return v##sign##int##bits##x##size##_impl_cpu.bxor(vec1, vec2); \ } \ \ - inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_not(v##sign##int##bits##x##size vec) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_not(v##sign##int##bits##x##size vec) \ { \ - return v##sign##int##bits##x##size##_impl_cpu->bnot(vec); \ + return v##sign##int##bits##x##size##_xor(vec, v##sign##int##bits##x##size##_splat((vec_##sign##int##bits)VEC_UINT##bits##_MAX)); \ } \ \ inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_cmplt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ - return v##sign##int##bits##x##size##_impl_cpu->cmplt(vec1, vec2); \ + return v##sign##int##bits##x##size##_impl_cpu.cmplt(vec1, vec2); \ } \ \ inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_cmple(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ - return v##sign##int##bits##x##size##_impl_cpu->cmple(vec1, vec2); \ + return v##sign##int##bits##x##size##_impl_cpu.cmple(vec1, vec2); \ } \ \ inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_cmpeq(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ - return v##sign##int##bits##x##size##_impl_cpu->cmpeq(vec1, vec2); \ + return v##sign##int##bits##x##size##_impl_cpu.cmpeq(vec1, vec2); \ } \ \ inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_cmpge(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ - return v##sign##int##bits##x##size##_impl_cpu->cmpge(vec1, vec2); \ + return v##sign##int##bits##x##size##_impl_cpu.cmpge(vec1, vec2); \ } \ \ inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_cmpgt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ - return v##sign##int##bits##x##size##_impl_cpu->cmpgt(vec1, vec2); \ + return v##sign##int##bits##x##size##_impl_cpu.cmpgt(vec1, vec2); \ } \ \ inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_lshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ { \ - return v##sign##int##bits##x##size##_impl_cpu->lshift(vec1, vec2); \ + return v##sign##int##bits##x##size##_impl_cpu.lshift(vec1, vec2); \ } \ \ inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_rshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ { \ - return v##sign##int##bits##x##size##_impl_cpu->rshift(vec1, vec2); \ + return v##sign##int##bits##x##size##_impl_cpu.rshift(vec1, vec2); \ } \ \ inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_lrshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ { \ - return v##sign##int##bits##x##size##_impl_cpu->lrshift(vec1, vec2); \ + return v##sign##int##bits##x##size##_impl_cpu.lrshift(vec1, vec2); \ + } \ + \ + inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_min(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return v##sign##int##bits##x##size##_impl_cpu.min(vec1, vec2); \ + } \ + \ + inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_max(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return v##sign##int##bits##x##size##_impl_cpu.max(vec1, vec2); \ } #define VEC_DEFINE_OPERATIONS(bits, size) \
--- a/src/cpu.c Mon Nov 25 00:33:02 2024 -0500 +++ b/src/cpu.c Thu Apr 24 00:54:02 2025 -0400 @@ -362,7 +362,29 @@ if (vec_CPU_OSSavesYMM && (vec_CPU_CPUIDMaxFunction >= 7)) { int a, b, c, d; VEC_CPU_CPUID(7, a, b, c, d); - return b & 0x00000020; + return b & 0x00010000; + (void)a, (void)c, (void)d; + } + return 0; +} + +static inline int vec_CPU_have_AVX512DQ(void) +{ + if (vec_CPU_OSSavesYMM && (vec_CPU_CPUIDMaxFunction >= 7)) { + int a, b, c, d; + VEC_CPU_CPUID(7, a, b, c, d); + return b & 0x00020000; + (void)a, (void)c, (void)d; + } + return 0; +} + +static inline int vec_CPU_have_AVX512BW(void) +{ + if (vec_CPU_OSSavesYMM && (vec_CPU_CPUIDMaxFunction >= 7)) { + int a, b, c, d; + VEC_CPU_CPUID(7, a, b, c, d); + return b & 0x40000000; (void)a, (void)c, (void)d; } return 0; @@ -491,6 +513,10 @@ vec_CPU_features |= VEC_CPU_HAS_AVX2; if (vec_CPU_have_AVX512F()) vec_CPU_features |= VEC_CPU_HAS_AVX512F; + if (vec_CPU_have_AVX512BW()) + vec_CPU_features |= VEC_CPU_HAS_AVX512BW; + if (vec_CPU_have_AVX512DQ()) + vec_CPU_features |= VEC_CPU_HAS_AVX512DQ; if (vec_CPU_have_NEON()) vec_CPU_features |= VEC_CPU_HAS_NEON; }
--- a/src/impl/arm/neon.c Mon Nov 25 00:33:02 2024 -0500 +++ b/src/impl/arm/neon.c Thu Apr 24 00:54:02 2025 -0400 @@ -39,19 +39,19 @@ VEC_STATIC_ASSERT(VEC_ALIGNOF(sign##int##bits##x##size##_t) <= VEC_ALIGNOF(v##sign##int##bits##x##size), "vec: v" #sign "int" #bits "x" #size " alignment needs to be expanded to fit intrinsic type size"); \ VEC_STATIC_ASSERT(sizeof(sign##int##bits##x##size##_t) <= sizeof(v##sign##int##bits##x##size), "vec: v" #sign "int" #bits "x" #size " needs to be expanded to fit intrinsic type size"); \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_load_aligned(const vec_##sign##int##bits in[size]) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_load_aligned(const vec_##sign##int##bits in[size]) \ { \ union v##sign##int##bits##x##size##_impl_data vec; \ vec.neon = vld1_##sign##bits(in); \ return vec.vec; \ } \ \ - static void v##sign##int##bits##x##size##_neon_store_aligned(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ + VEC_FUNC_IMPL void v##sign##int##bits##x##size##_neon_store_aligned(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ { \ vstore_lane_##bits(sign, ((union v##sign##int##bits##x##size##_impl_data *)&vec)->neon, out); \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -60,7 +60,7 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -69,7 +69,7 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -78,7 +78,7 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_lshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_lshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union vuint##bits##x##size##_impl_data *vec2d = (union vuint##bits##x##size##_impl_data *)&vec2; \ @@ -87,7 +87,7 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -96,7 +96,7 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -105,7 +105,7 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -114,29 +114,18 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_neon = { \ - v##sign##int##bits##x##size##_fallback_splat, \ - v##sign##int##bits##x##size##_neon_load_aligned, \ - v##sign##int##bits##x##size##_neon_load_aligned, \ - v##sign##int##bits##x##size##_neon_store_aligned, \ - v##sign##int##bits##x##size##_neon_store_aligned, \ - v##sign##int##bits##x##size##_neon_add, \ - v##sign##int##bits##x##size##_neon_sub, \ - v##sign##int##bits##x##size##_neon_mul, \ - v##sign##int##bits##x##size##_fallback_div, \ - v##sign##int##bits##x##size##_fallback_avg, \ - v##sign##int##bits##x##size##_neon_and, \ - v##sign##int##bits##x##size##_neon_or, \ - v##sign##int##bits##x##size##_neon_xor, \ - v##sign##int##bits##x##size##_fallback_not, \ - v##sign##int##bits##x##size##_neon_lshift, \ - v##sign##int##bits##x##size##_fallback_rshift, \ - v##sign##int##bits##x##size##_fallback_lrshift, \ - v##sign##int##bits##x##size##_fallback_cmplt, \ - v##sign##int##bits##x##size##_fallback_cmple, \ - v##sign##int##bits##x##size##_fallback_cmpeq, \ - v##sign##int##bits##x##size##_fallback_cmpge, \ - v##sign##int##bits##x##size##_fallback_cmpgt, \ + const v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_neon = { \ + .load = v##sign##int##bits##x##size##_neon_load_aligned, \ + .load_aligned = v##sign##int##bits##x##size##_neon_load_aligned, \ + .store = v##sign##int##bits##x##size##_neon_store_aligned, \ + .store_aligned = v##sign##int##bits##x##size##_neon_store_aligned, \ + .add = v##sign##int##bits##x##size##_neon_add, \ + .sub = v##sign##int##bits##x##size##_neon_sub, \ + .mul = v##sign##int##bits##x##size##_neon_mul, \ + .band = v##sign##int##bits##x##size##_neon_and, \ + .bor = v##sign##int##bits##x##size##_neon_or, \ + .bxor = v##sign##int##bits##x##size##_neon_xor, \ + .lshift = v##sign##int##bits##x##size##_neon_lshift, \ }; #define VEC_DEFINE_OPERATIONS(bits, size) \ @@ -244,7 +233,7 @@ // NEON doesn't have native 64-bit multiplication, so we have // to do it ourselves -static inline int64x2_t vmulq_s64(const int64x2_t a, const int64x2_t b) +VEC_FUNC_IMPL int64x2_t vmulq_s64(const int64x2_t a, const int64x2_t b) { const uint32x2_t ac = vreinterpret_u32_s32(vmovn_s64(a)); const uint32x2_t pr = vreinterpret_u32_s32(vmovn_s64(b)); @@ -254,7 +243,7 @@ return vreinterpretq_s64_u64(vmlal_u32(vreinterpretq_u64_s64(vshlq_n_s64(vreinterpretq_s64_u64(vpaddlq_u32(vreinterpretq_u32_s32(hi))), 32)), ac, pr)); } -static inline uint64x2_t vmulq_u64(const uint64x2_t a, const uint64x2_t b) +VEC_FUNC_IMPL uint64x2_t vmulq_u64(const uint64x2_t a, const uint64x2_t b) { const uint32x2_t ac = vmovn_u64(a); const uint32x2_t pr = vmovn_u64(b);
--- a/src/impl/fallback.c Mon Nov 25 00:33:02 2024 -0500 +++ b/src/impl/fallback.c Thu Apr 24 00:54:02 2025 -0400 @@ -1,3 +1,27 @@ +/** + * vec - a tiny SIMD vector library in C99 + * + * Copyright (c) 2024 Paper + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +**/ + #include "vec/impl/fallback.h" #include <string.h> @@ -7,8 +31,8 @@ // memory is unknown or yields incorrect results from the generic functions. // This is *extremely* unlikely; for x86 the layout is exactly the same in // memory as the generic functions (i.e. it is literally stored as an array of -// integers). This is likely true for AltiVec and NEON as well, but those -// aren't tested for now. +// integers). This is also true for AltiVec. This is likely true for NEON as well, +// but that isn't tested for now. #define VEC_FALLBACK_OPERATION(op, sign, csign, bits, size) \ do { \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/impl/gcc.c Thu Apr 24 00:54:02 2025 -0400 @@ -0,0 +1,517 @@ +/** + * vec - a tiny SIMD vector library in C99 + * + * Copyright (c) 2024 Paper + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +**/ + +#include "vec/impl/gcc.h" + +// ----------------------------------------------------------------- + +#define VEC_GENERIC_OPERATION(op, sign, csign, bits, size) \ + do { \ + for (int i = 0; i < size; i++) \ + ((union v##sign##int##bits##x##size##_impl_data *)&vec1)->impl[i] = (op); \ + \ + return vec1; \ + } while (0) + +#define VEC_GENERIC_BUILTIN_OPERATION(op, sign, csign, bits, size) \ + VEC_GENERIC_OPERATION(((union v##sign##int##bits##x##size##_impl_data *)&vec1)->impl[i] op ((union v##sign##int##bits##x##size##_impl_data *)&vec2)->impl[i], sign, csign, bits, size) + +#define VEC_GENERIC_CMP(op, sign, csign, bits, size) \ + VEC_GENERIC_OPERATION((((union v##sign##int##bits##x##size##_impl_data *)&vec1)->impl[i] op ((union v##sign##int##bits##x##size##_impl_data *)&vec2)->impl[i]) ? VEC_UINT##bits##_MAX : 0, sign, csign, bits, size) + +// TODO implement these so we don't waste stack space by doing the +// generics +#define VEC_GENERIC_DEFINE_OPERATIONS_SIGN(sign, csign, bits, size) \ + union v##sign##int##bits##x##size##_impl_data { \ + v##sign##int##bits##x##size vec; \ + vec_##sign##int##bits impl[size]; \ + }; \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_splat(vec_##sign##int##bits x) \ + { \ + v##sign##int##bits##x##size vec; \ + for (int i = 0; i < size; i++) \ + ((union v##sign##int##bits##x##size##_impl_data *)&vec)->impl[i] = x; \ + return vec; \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_load_aligned(const vec_##sign##int##bits in[size]) \ + { \ + v##sign##int##bits##x##size vec; \ + memcpy(&vec, in, sizeof(vec_##sign##int##bits) * size); \ + return vec; \ + } \ + \ + VEC_FUNC_IMPL void v##sign##int##bits##x##size##_generic_store_aligned(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ + { \ + memcpy(out, &vec, sizeof(vec_##sign##int##bits) * size); \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_GENERIC_BUILTIN_OPERATION(+, sign, csign, bits, size); \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_GENERIC_BUILTIN_OPERATION(-, sign, csign, bits, size); \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_GENERIC_BUILTIN_OPERATION(*, sign, csign, bits, size); \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_div(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_GENERIC_OPERATION(((union v##sign##int##bits##x##size##_impl_data *)&vec2)->impl[i] ? (((union v##sign##int##bits##x##size##_impl_data *)&vec1)->impl[i] / ((union v##sign##int##bits##x##size##_impl_data *)&vec2)->impl[i]) : 0, sign, csign, bits, size); \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_avg(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ + \ + for (int i = 0; i < size; i++) \ + vec1d->impl[i] = vec_##sign##avg(vec1d->impl[i], vec2d->impl[i]); \ + \ + return vec1d->vec; \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_GENERIC_BUILTIN_OPERATION(&, sign, csign, bits, size); \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_GENERIC_BUILTIN_OPERATION(|, sign, csign, bits, size); \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_GENERIC_BUILTIN_OPERATION(^, sign, csign, bits, size); \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmplt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_GENERIC_CMP(<, sign, csign, bits, size); \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmple(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return v##sign##int##bits##x##size##_not(v##sign##int##bits##x##size##_cmpgt(vec1, vec2)); \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmpeq(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_GENERIC_CMP(==, sign, csign, bits, size); \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmpge(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + return v##sign##int##bits##x##size##_not(v##sign##int##bits##x##size##_cmplt(vec1, vec2)); \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmpgt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + VEC_GENERIC_CMP(>, sign, csign, bits, size); \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_lshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + VEC_GENERIC_OPERATION(vec_##sign##lshift(((union v##sign##int##bits##x##size##_impl_data *)&vec1)->impl[i], ((union v##sign##int##bits##x##size##_impl_data *)&vec2)->impl[i]), sign, csign, bits, size); \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_rshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + VEC_GENERIC_OPERATION(vec_##sign##rshift(((union v##sign##int##bits##x##size##_impl_data *)&vec1)->impl[i], ((union v##sign##int##bits##x##size##_impl_data *)&vec2)->impl[i]), sign, csign, bits, size); \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_lrshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + VEC_GENERIC_OPERATION(vec_lrshift((vec_uint##bits)(((union v##sign##int##bits##x##size##_impl_data *)&vec1)->impl[i]), ((union v##sign##int##bits##x##size##_impl_data *)&vec2)->impl[i]), sign, csign, bits, size); \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_min(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + v##sign##int##bits##x##size cmplt = v##sign##int##bits##x##size##_cmplt(vec1, vec2); \ + \ + v##sign##int##bits##x##size a = v##sign##int##bits##x##size##_and(vec1, cmplt); \ + v##sign##int##bits##x##size b = v##sign##int##bits##x##size##_and(vec2, v##sign##int##bits##x##size##_not(cmplt)); \ + \ + return v##sign##int##bits##x##size##_or(a, b); \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_max(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + v##sign##int##bits##x##size cmplt = v##sign##int##bits##x##size##_cmpgt(vec1, vec2); \ + \ + v##sign##int##bits##x##size a = v##sign##int##bits##x##size##_and(vec1, cmplt); \ + v##sign##int##bits##x##size b = v##sign##int##bits##x##size##_and(vec2, v##sign##int##bits##x##size##_not(cmplt)); \ + \ + return v##sign##int##bits##x##size##_or(a, b); \ + } \ + \ + const v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_generic = { \ + .splat = v##sign##int##bits##x##size##_generic_splat, \ + .load_aligned = v##sign##int##bits##x##size##_generic_load_aligned, \ + .load = v##sign##int##bits##x##size##_generic_load_aligned, \ + .store_aligned = v##sign##int##bits##x##size##_generic_store_aligned, \ + .store = v##sign##int##bits##x##size##_generic_store_aligned, \ + .add = v##sign##int##bits##x##size##_generic_add, \ + .sub = v##sign##int##bits##x##size##_generic_sub, \ + .mul = v##sign##int##bits##x##size##_generic_mul, \ + .div = v##sign##int##bits##x##size##_generic_div, \ + .avg = v##sign##int##bits##x##size##_generic_avg, \ + .band = v##sign##int##bits##x##size##_generic_and, \ + .bor = v##sign##int##bits##x##size##_generic_or, \ + .bxor = v##sign##int##bits##x##size##_generic_xor, \ + .lshift = v##sign##int##bits##x##size##_generic_lshift, \ + .rshift = v##sign##int##bits##x##size##_generic_rshift, \ + .lrshift = v##sign##int##bits##x##size##_generic_lrshift, \ + .cmplt = v##sign##int##bits##x##size##_generic_cmplt, \ + .cmple = v##sign##int##bits##x##size##_generic_cmple, \ + .cmpeq = v##sign##int##bits##x##size##_generic_cmpeq, \ + .cmpge = v##sign##int##bits##x##size##_generic_cmpge, \ + .cmpgt = v##sign##int##bits##x##size##_generic_cmpgt, \ + .min = v##sign##int##bits##x##size##_generic_min, \ + .max = v##sign##int##bits##x##size##_generic_max, \ + }; + +#define VEC_GENERIC_DEFINE_OPERATIONS(bits, size) \ + VEC_GENERIC_DEFINE_OPERATIONS_SIGN(u, U, bits, size) \ + VEC_GENERIC_DEFINE_OPERATIONS_SIGN( , , bits, size) + +VEC_GENERIC_DEFINE_OPERATIONS(8, 2) +VEC_GENERIC_DEFINE_OPERATIONS(16, 2) +VEC_GENERIC_DEFINE_OPERATIONS(32, 2) +VEC_GENERIC_DEFINE_OPERATIONS(64, 2) + +#undef VEC_GENERIC_DEFINE_OPERATIONS +#undef VEC_GENERIC_DEFINE_OPERATIONS_SIGN + +// ----------------------------------------------------------------- +// now we can just keep doubling the same implementation + +#define VEC_GENERIC_DEFINE_OPERATIONS_SIGN(sign, csign, bits, size, halfsize) \ + union v##sign##int##bits##x##size##_impl_data { \ + v##sign##int##bits##x##size vec; \ + v##sign##int##bits##x##halfsize impl[2]; \ + }; \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_splat(vec_##sign##int##bits x) \ + { \ + union v##sign##int##bits##x##size##_impl_data vec; \ + vec.impl[0] = v##sign##int##bits##x##halfsize##_splat(x); \ + vec.impl[1] = v##sign##int##bits##x##halfsize##_splat(x); \ + return vec.vec; \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_load_aligned(const vec_##sign##int##bits in[size]) \ + { \ + union v##sign##int##bits##x##size##_impl_data vec; \ + vec.impl[0] = v##sign##int##bits##x##halfsize##_load_aligned(in); \ + vec.impl[1] = v##sign##int##bits##x##halfsize##_load_aligned(in + halfsize); \ + return vec.vec; \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_load(const vec_##sign##int##bits in[size]) \ + { \ + union v##sign##int##bits##x##size##_impl_data vec; \ + vec.impl[0] = v##sign##int##bits##x##halfsize##_load(in); \ + vec.impl[1] = v##sign##int##bits##x##halfsize##_load(in + halfsize); \ + return vec.vec; \ + } \ + \ + VEC_FUNC_IMPL void v##sign##int##bits##x##size##_generic_store_aligned(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vecd = (union v##sign##int##bits##x##size##_impl_data *)&vec; \ + \ + v##sign##int##bits##x##halfsize##_store_aligned(vecd->impl[0], out); \ + v##sign##int##bits##x##halfsize##_store_aligned(vecd->impl[1], out + halfsize); \ + } \ + \ + VEC_FUNC_IMPL void v##sign##int##bits##x##size##_generic_store(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vecd = (union v##sign##int##bits##x##size##_impl_data *)&vec; \ + \ + v##sign##int##bits##x##halfsize##_store(vecd->impl[0], out); \ + v##sign##int##bits##x##halfsize##_store(vecd->impl[1], out + halfsize); \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ + \ + vec1d->impl[0] = v##sign##int##bits##x##halfsize##_add(vec1d->impl[0], vec2d->impl[0]); \ + vec1d->impl[1] = v##sign##int##bits##x##halfsize##_add(vec1d->impl[1], vec2d->impl[1]); \ + \ + return vec1d->vec; \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ + \ + vec1d->impl[0] = v##sign##int##bits##x##halfsize##_sub(vec1d->impl[0], vec2d->impl[0]); \ + vec1d->impl[1] = v##sign##int##bits##x##halfsize##_sub(vec1d->impl[1], vec2d->impl[1]); \ + \ + return vec1d->vec; \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ + \ + vec1d->impl[0] = v##sign##int##bits##x##halfsize##_mul(vec1d->impl[0], vec2d->impl[0]); \ + vec1d->impl[1] = v##sign##int##bits##x##halfsize##_mul(vec1d->impl[1], vec2d->impl[1]); \ + \ + return vec1d->vec; \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_div(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ + \ + vec1d->impl[0] = v##sign##int##bits##x##halfsize##_div(vec1d->impl[0], vec2d->impl[0]); \ + vec1d->impl[1] = v##sign##int##bits##x##halfsize##_div(vec1d->impl[1], vec2d->impl[1]); \ + \ + return vec1d->vec; \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_avg(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ + \ + vec1d->impl[0] = v##sign##int##bits##x##halfsize##_avg(vec1d->impl[0], vec2d->impl[0]); \ + vec1d->impl[1] = v##sign##int##bits##x##halfsize##_avg(vec1d->impl[1], vec2d->impl[1]); \ + \ + return vec1d->vec; \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ + \ + vec1d->impl[0] = v##sign##int##bits##x##halfsize##_and(vec1d->impl[0], vec2d->impl[0]); \ + vec1d->impl[1] = v##sign##int##bits##x##halfsize##_and(vec1d->impl[1], vec2d->impl[1]); \ + \ + return vec1d->vec; \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ + \ + vec1d->impl[0] = v##sign##int##bits##x##halfsize##_or(vec1d->impl[0], vec2d->impl[0]); \ + vec1d->impl[1] = v##sign##int##bits##x##halfsize##_or(vec1d->impl[1], vec2d->impl[1]); \ + \ + return vec1d->vec; \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ + \ + vec1d->impl[0] = v##sign##int##bits##x##halfsize##_xor(vec1d->impl[0], vec2d->impl[0]); \ + vec1d->impl[1] = v##sign##int##bits##x##halfsize##_xor(vec1d->impl[1], vec2d->impl[1]); \ + \ + return vec1d->vec; \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_lshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union vuint##bits##x##size##_impl_data *vec2d = (union vuint##bits##x##size##_impl_data *)&vec2; \ + \ + vec1d->impl[0] = v##sign##int##bits##x##halfsize##_lshift(vec1d->impl[0], vec2d->impl[0]); \ + vec1d->impl[1] = v##sign##int##bits##x##halfsize##_lshift(vec1d->impl[1], vec2d->impl[1]); \ + \ + return vec1d->vec; \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_rshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union vuint##bits##x##size##_impl_data *vec2d = (union vuint##bits##x##size##_impl_data *)&vec2; \ + \ + vec1d->impl[0] = v##sign##int##bits##x##halfsize##_rshift(vec1d->impl[0], vec2d->impl[0]); \ + vec1d->impl[1] = v##sign##int##bits##x##halfsize##_rshift(vec1d->impl[1], vec2d->impl[1]); \ + \ + return vec1d->vec; \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_lrshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union vuint##bits##x##size##_impl_data *vec2d = (union vuint##bits##x##size##_impl_data *)&vec2; \ + \ + vec1d->impl[0] = v##sign##int##bits##x##halfsize##_lrshift(vec1d->impl[0], vec2d->impl[0]); \ + vec1d->impl[1] = v##sign##int##bits##x##halfsize##_lrshift(vec1d->impl[1], vec2d->impl[1]); \ + \ + return vec1d->vec; \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmplt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ + \ + vec1d->impl[0] = v##sign##int##bits##x##halfsize##_cmplt(vec1d->impl[0], vec2d->impl[0]); \ + vec1d->impl[1] = v##sign##int##bits##x##halfsize##_cmplt(vec1d->impl[1], vec2d->impl[1]); \ + \ + return vec1d->vec; \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmple(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ + \ + vec1d->impl[0] = v##sign##int##bits##x##halfsize##_cmple(vec1d->impl[0], vec2d->impl[0]); \ + vec1d->impl[1] = v##sign##int##bits##x##halfsize##_cmple(vec1d->impl[1], vec2d->impl[1]); \ + \ + return vec1d->vec; \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmpeq(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ + \ + vec1d->impl[0] = v##sign##int##bits##x##halfsize##_cmpeq(vec1d->impl[0], vec2d->impl[0]); \ + vec1d->impl[1] = v##sign##int##bits##x##halfsize##_cmpeq(vec1d->impl[1], vec2d->impl[1]); \ + \ + return vec1d->vec; \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmpge(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ + \ + vec1d->impl[0] = v##sign##int##bits##x##halfsize##_cmpge(vec1d->impl[0], vec2d->impl[0]); \ + vec1d->impl[1] = v##sign##int##bits##x##halfsize##_cmpge(vec1d->impl[1], vec2d->impl[1]); \ + \ + return vec1d->vec; \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmpgt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ + \ + vec1d->impl[0] = v##sign##int##bits##x##halfsize##_cmpgt(vec1d->impl[0], vec2d->impl[0]); \ + vec1d->impl[1] = v##sign##int##bits##x##halfsize##_cmpgt(vec1d->impl[1], vec2d->impl[1]); \ + \ + return vec1d->vec; \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_min(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ + \ + vec1d->impl[0] = v##sign##int##bits##x##halfsize##_min(vec1d->impl[0], vec2d->impl[0]); \ + vec1d->impl[1] = v##sign##int##bits##x##halfsize##_min(vec1d->impl[1], vec2d->impl[1]); \ + \ + return vec1d->vec; \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_max(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ + \ + vec1d->impl[0] = v##sign##int##bits##x##halfsize##_max(vec1d->impl[0], vec2d->impl[0]); \ + vec1d->impl[1] = v##sign##int##bits##x##halfsize##_max(vec1d->impl[1], vec2d->impl[1]); \ + \ + return vec1d->vec; \ + } \ + \ + const v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_generic = { \ + .splat = v##sign##int##bits##x##size##_generic_splat, \ + .load_aligned = v##sign##int##bits##x##size##_generic_load_aligned, \ + .load = v##sign##int##bits##x##size##_generic_load, \ + .store_aligned = v##sign##int##bits##x##size##_generic_store_aligned, \ + .store = v##sign##int##bits##x##size##_generic_store, \ + .add = v##sign##int##bits##x##size##_generic_add, \ + .sub = v##sign##int##bits##x##size##_generic_sub, \ + .mul = v##sign##int##bits##x##size##_generic_mul, \ + .div = v##sign##int##bits##x##size##_generic_div, \ + .avg = v##sign##int##bits##x##size##_generic_avg, \ + .band = v##sign##int##bits##x##size##_generic_and, \ + .bor = v##sign##int##bits##x##size##_generic_or, \ + .bxor = v##sign##int##bits##x##size##_generic_xor, \ + .lshift = v##sign##int##bits##x##size##_generic_lshift, \ + .rshift = v##sign##int##bits##x##size##_generic_rshift, \ + .lrshift = v##sign##int##bits##x##size##_generic_lrshift, \ + .cmplt = v##sign##int##bits##x##size##_generic_cmplt, \ + .cmple = v##sign##int##bits##x##size##_generic_cmple, \ + .cmpeq = v##sign##int##bits##x##size##_generic_cmpeq, \ + .cmpge = v##sign##int##bits##x##size##_generic_cmpge, \ + .cmpgt = v##sign##int##bits##x##size##_generic_cmpgt, \ + .min = v##sign##int##bits##x##size##_generic_min, \ + .max = v##sign##int##bits##x##size##_generic_max, \ + }; + +#define VEC_GENERIC_DEFINE_OPERATIONS(bits, size, halfsize) \ + VEC_GENERIC_DEFINE_OPERATIONS_SIGN(u, U, bits, size, halfsize) \ + VEC_GENERIC_DEFINE_OPERATIONS_SIGN( , , bits, size, halfsize) + +// 32-bit +VEC_GENERIC_DEFINE_OPERATIONS(8, 4, 2) + +// 64-bit +VEC_GENERIC_DEFINE_OPERATIONS(8, 8, 4) +VEC_GENERIC_DEFINE_OPERATIONS(16, 4, 2) + +// 128-bit +VEC_GENERIC_DEFINE_OPERATIONS(8, 16, 8) +VEC_GENERIC_DEFINE_OPERATIONS(16, 8, 4) +VEC_GENERIC_DEFINE_OPERATIONS(32, 4, 2) + +// 256-bit +VEC_GENERIC_DEFINE_OPERATIONS(8, 32, 16) +VEC_GENERIC_DEFINE_OPERATIONS(16, 16, 8) +VEC_GENERIC_DEFINE_OPERATIONS(32, 8, 4) +VEC_GENERIC_DEFINE_OPERATIONS(64, 4, 2) + +// 512-bit +VEC_GENERIC_DEFINE_OPERATIONS(8, 64, 32) +VEC_GENERIC_DEFINE_OPERATIONS(16, 32, 16) +VEC_GENERIC_DEFINE_OPERATIONS(32, 16, 8) +VEC_GENERIC_DEFINE_OPERATIONS(64, 8, 4) + +#undef VEC_GENERIC_DEFINE_OPERATIONS +#undef VEC_GENERIC_DEFINE_OPERATIONS_SIGN
--- a/src/impl/generic.c Mon Nov 25 00:33:02 2024 -0500 +++ b/src/impl/generic.c Thu Apr 24 00:54:02 2025 -0400 @@ -1,3 +1,27 @@ +/** + * vec - a tiny SIMD vector library in C99 + * + * Copyright (c) 2024 Paper + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +**/ + #include "vec/impl/generic.h" #include <string.h> @@ -26,7 +50,7 @@ vec_##sign##int##bits impl[size]; \ }; \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_splat(vec_##sign##int##bits x) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_splat(vec_##sign##int##bits x) \ { \ v##sign##int##bits##x##size vec; \ for (int i = 0; i < size; i++) \ @@ -34,39 +58,39 @@ return vec; \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_load_aligned(const vec_##sign##int##bits in[size]) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_load_aligned(const vec_##sign##int##bits in[size]) \ { \ v##sign##int##bits##x##size vec; \ memcpy(&vec, in, sizeof(vec_##sign##int##bits) * size); \ return vec; \ } \ \ - void v##sign##int##bits##x##size##_generic_store_aligned(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ + VEC_FUNC_IMPL void v##sign##int##bits##x##size##_generic_store_aligned(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ { \ memcpy(out, &vec, sizeof(vec_##sign##int##bits) * size); \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ VEC_GENERIC_BUILTIN_OPERATION(+, sign, csign, bits, size); \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ VEC_GENERIC_BUILTIN_OPERATION(-, sign, csign, bits, size); \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ VEC_GENERIC_BUILTIN_OPERATION(*, sign, csign, bits, size); \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_div(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_div(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ VEC_GENERIC_OPERATION(((union v##sign##int##bits##x##size##_impl_data *)&vec2)->impl[i] ? (((union v##sign##int##bits##x##size##_impl_data *)&vec1)->impl[i] / ((union v##sign##int##bits##x##size##_impl_data *)&vec2)->impl[i]) : 0, sign, csign, bits, size); \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_avg(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_avg(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -77,89 +101,105 @@ return vec1d->vec; \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ VEC_GENERIC_BUILTIN_OPERATION(&, sign, csign, bits, size); \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ VEC_GENERIC_BUILTIN_OPERATION(|, sign, csign, bits, size); \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ VEC_GENERIC_BUILTIN_OPERATION(^, sign, csign, bits, size); \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_not(v##sign##int##bits##x##size vec) \ - { \ - return v##sign##int##bits##x##size##_generic_xor(vec, v##sign##int##bits##x##size##_generic_splat((vec_##sign##int##bits)VEC_UINT##bits##_MAX)); \ - } \ - \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmplt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmplt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ VEC_GENERIC_CMP(<, sign, csign, bits, size); \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmple(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmple(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ - VEC_GENERIC_CMP(<=, sign, csign, bits, size); \ + return v##sign##int##bits##x##size##_not(v##sign##int##bits##x##size##_cmpgt(vec1, vec2)); \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmpeq(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmpeq(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ VEC_GENERIC_CMP(==, sign, csign, bits, size); \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmpge(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmpge(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ - VEC_GENERIC_CMP(>=, sign, csign, bits, size); \ + return v##sign##int##bits##x##size##_not(v##sign##int##bits##x##size##_cmplt(vec1, vec2)); \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmpgt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmpgt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ VEC_GENERIC_CMP(>, sign, csign, bits, size); \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_lshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_lshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ { \ VEC_GENERIC_OPERATION(vec_##sign##lshift(((union v##sign##int##bits##x##size##_impl_data *)&vec1)->impl[i], ((union v##sign##int##bits##x##size##_impl_data *)&vec2)->impl[i]), sign, csign, bits, size); \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_rshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_rshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ { \ VEC_GENERIC_OPERATION(vec_##sign##rshift(((union v##sign##int##bits##x##size##_impl_data *)&vec1)->impl[i], ((union v##sign##int##bits##x##size##_impl_data *)&vec2)->impl[i]), sign, csign, bits, size); \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_lrshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_lrshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ { \ VEC_GENERIC_OPERATION(vec_lrshift((vec_uint##bits)(((union v##sign##int##bits##x##size##_impl_data *)&vec1)->impl[i]), ((union v##sign##int##bits##x##size##_impl_data *)&vec2)->impl[i]), sign, csign, bits, size); \ } \ \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_min(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + v##sign##int##bits##x##size cmplt = v##sign##int##bits##x##size##_cmplt(vec1, vec2); \ + \ + v##sign##int##bits##x##size a = v##sign##int##bits##x##size##_and(vec1, cmplt); \ + v##sign##int##bits##x##size b = v##sign##int##bits##x##size##_and(vec2, v##sign##int##bits##x##size##_not(cmplt)); \ + \ + return v##sign##int##bits##x##size##_or(a, b); \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_max(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + v##sign##int##bits##x##size cmplt = v##sign##int##bits##x##size##_cmpgt(vec1, vec2); \ + \ + v##sign##int##bits##x##size a = v##sign##int##bits##x##size##_and(vec1, cmplt); \ + v##sign##int##bits##x##size b = v##sign##int##bits##x##size##_and(vec2, v##sign##int##bits##x##size##_not(cmplt)); \ + \ + return v##sign##int##bits##x##size##_or(a, b); \ + } \ + \ const v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_generic = { \ - v##sign##int##bits##x##size##_generic_splat, \ - v##sign##int##bits##x##size##_generic_load_aligned, \ - v##sign##int##bits##x##size##_generic_load_aligned, \ - v##sign##int##bits##x##size##_generic_store_aligned, \ - v##sign##int##bits##x##size##_generic_store_aligned, \ - v##sign##int##bits##x##size##_generic_add, \ - v##sign##int##bits##x##size##_generic_sub, \ - v##sign##int##bits##x##size##_generic_mul, \ - v##sign##int##bits##x##size##_generic_div, \ - v##sign##int##bits##x##size##_generic_avg, \ - v##sign##int##bits##x##size##_generic_and, \ - v##sign##int##bits##x##size##_generic_or, \ - v##sign##int##bits##x##size##_generic_xor, \ - v##sign##int##bits##x##size##_generic_not, \ - v##sign##int##bits##x##size##_generic_lshift, \ - v##sign##int##bits##x##size##_generic_rshift, \ - v##sign##int##bits##x##size##_generic_lrshift, \ - v##sign##int##bits##x##size##_generic_cmplt, \ - v##sign##int##bits##x##size##_generic_cmple, \ - v##sign##int##bits##x##size##_generic_cmpeq, \ - v##sign##int##bits##x##size##_generic_cmpge, \ - v##sign##int##bits##x##size##_generic_cmpgt, \ + .splat = v##sign##int##bits##x##size##_generic_splat, \ + .load_aligned = v##sign##int##bits##x##size##_generic_load_aligned, \ + .load = v##sign##int##bits##x##size##_generic_load_aligned, \ + .store_aligned = v##sign##int##bits##x##size##_generic_store_aligned, \ + .store = v##sign##int##bits##x##size##_generic_store_aligned, \ + .add = v##sign##int##bits##x##size##_generic_add, \ + .sub = v##sign##int##bits##x##size##_generic_sub, \ + .mul = v##sign##int##bits##x##size##_generic_mul, \ + .div = v##sign##int##bits##x##size##_generic_div, \ + .avg = v##sign##int##bits##x##size##_generic_avg, \ + .band = v##sign##int##bits##x##size##_generic_and, \ + .bor = v##sign##int##bits##x##size##_generic_or, \ + .bxor = v##sign##int##bits##x##size##_generic_xor, \ + .lshift = v##sign##int##bits##x##size##_generic_lshift, \ + .rshift = v##sign##int##bits##x##size##_generic_rshift, \ + .lrshift = v##sign##int##bits##x##size##_generic_lrshift, \ + .cmplt = v##sign##int##bits##x##size##_generic_cmplt, \ + .cmple = v##sign##int##bits##x##size##_generic_cmple, \ + .cmpeq = v##sign##int##bits##x##size##_generic_cmpeq, \ + .cmpge = v##sign##int##bits##x##size##_generic_cmpge, \ + .cmpgt = v##sign##int##bits##x##size##_generic_cmpgt, \ + .min = v##sign##int##bits##x##size##_generic_min, \ + .max = v##sign##int##bits##x##size##_generic_max, \ }; #define VEC_GENERIC_DEFINE_OPERATIONS(bits, size) \ @@ -183,7 +223,7 @@ v##sign##int##bits##x##halfsize impl[2]; \ }; \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_splat(vec_##sign##int##bits x) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_splat(vec_##sign##int##bits x) \ { \ union v##sign##int##bits##x##size##_impl_data vec; \ vec.impl[0] = v##sign##int##bits##x##halfsize##_splat(x); \ @@ -191,7 +231,7 @@ return vec.vec; \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_load_aligned(const vec_##sign##int##bits in[size]) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_load_aligned(const vec_##sign##int##bits in[size]) \ { \ union v##sign##int##bits##x##size##_impl_data vec; \ vec.impl[0] = v##sign##int##bits##x##halfsize##_load_aligned(in); \ @@ -199,7 +239,7 @@ return vec.vec; \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_load(const vec_##sign##int##bits in[size]) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_load(const vec_##sign##int##bits in[size]) \ { \ union v##sign##int##bits##x##size##_impl_data vec; \ vec.impl[0] = v##sign##int##bits##x##halfsize##_load(in); \ @@ -207,7 +247,7 @@ return vec.vec; \ } \ \ - void v##sign##int##bits##x##size##_generic_store_aligned(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ + VEC_FUNC_IMPL void v##sign##int##bits##x##size##_generic_store_aligned(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ { \ union v##sign##int##bits##x##size##_impl_data *vecd = (union v##sign##int##bits##x##size##_impl_data *)&vec; \ \ @@ -215,7 +255,7 @@ v##sign##int##bits##x##halfsize##_store_aligned(vecd->impl[1], out + halfsize); \ } \ \ - void v##sign##int##bits##x##size##_generic_store(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ + VEC_FUNC_IMPL void v##sign##int##bits##x##size##_generic_store(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ { \ union v##sign##int##bits##x##size##_impl_data *vecd = (union v##sign##int##bits##x##size##_impl_data *)&vec; \ \ @@ -223,7 +263,7 @@ v##sign##int##bits##x##halfsize##_store(vecd->impl[1], out + halfsize); \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -234,7 +274,7 @@ return vec1d->vec; \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -245,7 +285,7 @@ return vec1d->vec; \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -256,7 +296,7 @@ return vec1d->vec; \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_div(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_div(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -267,7 +307,7 @@ return vec1d->vec; \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_avg(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_avg(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -278,7 +318,7 @@ return vec1d->vec; \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -289,7 +329,7 @@ return vec1d->vec; \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -300,7 +340,7 @@ return vec1d->vec; \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -311,17 +351,7 @@ return vec1d->vec; \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_not(v##sign##int##bits##x##size vec1) \ - { \ - union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ - \ - vec1d->impl[0] = v##sign##int##bits##x##halfsize##_not(vec1d->impl[0]); \ - vec1d->impl[1] = v##sign##int##bits##x##halfsize##_not(vec1d->impl[1]); \ - \ - return vec1d->vec; \ - } \ - \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_lshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_lshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union vuint##bits##x##size##_impl_data *vec2d = (union vuint##bits##x##size##_impl_data *)&vec2; \ @@ -332,7 +362,7 @@ return vec1d->vec; \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_rshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_rshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union vuint##bits##x##size##_impl_data *vec2d = (union vuint##bits##x##size##_impl_data *)&vec2; \ @@ -343,7 +373,7 @@ return vec1d->vec; \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_lrshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_lrshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union vuint##bits##x##size##_impl_data *vec2d = (union vuint##bits##x##size##_impl_data *)&vec2; \ @@ -354,7 +384,7 @@ return vec1d->vec; \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmplt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmplt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -365,7 +395,7 @@ return vec1d->vec; \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmple(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmple(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -376,7 +406,7 @@ return vec1d->vec; \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmpeq(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmpeq(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -387,7 +417,7 @@ return vec1d->vec; \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmpge(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmpge(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -398,7 +428,7 @@ return vec1d->vec; \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmpgt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmpgt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -409,29 +439,52 @@ return vec1d->vec; \ } \ \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_min(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ + \ + vec1d->impl[0] = v##sign##int##bits##x##halfsize##_min(vec1d->impl[0], vec2d->impl[0]); \ + vec1d->impl[1] = v##sign##int##bits##x##halfsize##_min(vec1d->impl[1], vec2d->impl[1]); \ + \ + return vec1d->vec; \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_max(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ + \ + vec1d->impl[0] = v##sign##int##bits##x##halfsize##_max(vec1d->impl[0], vec2d->impl[0]); \ + vec1d->impl[1] = v##sign##int##bits##x##halfsize##_max(vec1d->impl[1], vec2d->impl[1]); \ + \ + return vec1d->vec; \ + } \ + \ const v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_generic = { \ - v##sign##int##bits##x##size##_generic_splat, \ - v##sign##int##bits##x##size##_generic_load_aligned, \ - v##sign##int##bits##x##size##_generic_load, \ - v##sign##int##bits##x##size##_generic_store_aligned, \ - v##sign##int##bits##x##size##_generic_store, \ - v##sign##int##bits##x##size##_generic_add, \ - v##sign##int##bits##x##size##_generic_sub, \ - v##sign##int##bits##x##size##_generic_mul, \ - v##sign##int##bits##x##size##_generic_div, \ - v##sign##int##bits##x##size##_generic_avg, \ - v##sign##int##bits##x##size##_generic_and, \ - v##sign##int##bits##x##size##_generic_or, \ - v##sign##int##bits##x##size##_generic_xor, \ - v##sign##int##bits##x##size##_generic_not, \ - v##sign##int##bits##x##size##_generic_lshift, \ - v##sign##int##bits##x##size##_generic_rshift, \ - v##sign##int##bits##x##size##_generic_lrshift, \ - v##sign##int##bits##x##size##_generic_cmplt, \ - v##sign##int##bits##x##size##_generic_cmple, \ - v##sign##int##bits##x##size##_generic_cmpeq, \ - v##sign##int##bits##x##size##_generic_cmpge, \ - v##sign##int##bits##x##size##_generic_cmpgt, \ + .splat = v##sign##int##bits##x##size##_generic_splat, \ + .load_aligned = v##sign##int##bits##x##size##_generic_load_aligned, \ + .load = v##sign##int##bits##x##size##_generic_load, \ + .store_aligned = v##sign##int##bits##x##size##_generic_store_aligned, \ + .store = v##sign##int##bits##x##size##_generic_store, \ + .add = v##sign##int##bits##x##size##_generic_add, \ + .sub = v##sign##int##bits##x##size##_generic_sub, \ + .mul = v##sign##int##bits##x##size##_generic_mul, \ + .div = v##sign##int##bits##x##size##_generic_div, \ + .avg = v##sign##int##bits##x##size##_generic_avg, \ + .band = v##sign##int##bits##x##size##_generic_and, \ + .bor = v##sign##int##bits##x##size##_generic_or, \ + .bxor = v##sign##int##bits##x##size##_generic_xor, \ + .lshift = v##sign##int##bits##x##size##_generic_lshift, \ + .rshift = v##sign##int##bits##x##size##_generic_rshift, \ + .lrshift = v##sign##int##bits##x##size##_generic_lrshift, \ + .cmplt = v##sign##int##bits##x##size##_generic_cmplt, \ + .cmple = v##sign##int##bits##x##size##_generic_cmple, \ + .cmpeq = v##sign##int##bits##x##size##_generic_cmpeq, \ + .cmpge = v##sign##int##bits##x##size##_generic_cmpge, \ + .cmpgt = v##sign##int##bits##x##size##_generic_cmpgt, \ + .min = v##sign##int##bits##x##size##_generic_min, \ + .max = v##sign##int##bits##x##size##_generic_max, \ }; #define VEC_GENERIC_DEFINE_OPERATIONS(bits, size, halfsize) \
--- a/src/impl/ppc/altivec.c Mon Nov 25 00:33:02 2024 -0500 +++ b/src/impl/ppc/altivec.c Thu Apr 24 00:54:02 2025 -0400 @@ -48,7 +48,7 @@ /* GCC 4.2.1 on Mac OS X doesn't have these for some reason */ #ifdef vec_mul # define VEC_ALTIVEC_DEFINE_MUL(sign, csign, bits, size) \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -57,34 +57,34 @@ return vec1d->vec; \ } # define VEC_ALTIVEC_STRUCT_MUL(sign, csign, bits, size) \ - v##sign##int##bits##x##size##_altivec_mul + .mul = v##sign##int##bits##x##size##_altivec_mul, #else # define VEC_ALTIVEC_DEFINE_MUL(sign, csign, bits, size) # define VEC_ALTIVEC_STRUCT_MUL(sign, csign, bits, size) \ - v##sign##int##bits##x##size##_generic_mul + /* nothing */ #endif #ifdef vec_splats # define VEC_ALTIVEC_DEFINE_SPLAT(sign, csign, bits, size) \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_splat(vec_##sign##int##bits x) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_splat(vec_##sign##int##bits x) \ { \ union v##sign##int##bits##x##size##_impl_data vec; \ vec.altivec = vec_splats(x); \ return vec.vec; \ } # define VEC_ALTIVEC_STRUCT_SPLAT(sign, csign, bits, size) \ - v##sign##int##bits##x##size##_altivec_splat + .splat = v##sign##int##bits##x##size##_altivec_splat, #else # define VEC_ALTIVEC_DEFINE_SPLAT(sign, csign, bits, size) # define VEC_ALTIVEC_STRUCT_SPLAT(sign, csign, bits, size) \ - v##sign##int##bits##x##size##_generic_splat + /* nothing */ #endif #define VEC_ALTIVEC_uRSHIFT vec_sr #define VEC_ALTIVEC_RSHIFT vec_sra #define VEC_ALTIVEC_DEFINE_uLRSHIFT(sign, csign, bits, size) \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_lrshift(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_lrshift(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union vuint##bits##x##size##_impl_data *vec2d = (union vuint##bits##x##size##_impl_data *)&vec2; \ @@ -93,11 +93,10 @@ return vec1d->vec; \ } #define VEC_ALTIVEC_STRUCT_uLRSHIFT(sign, csign, bits, size) \ - v##sign##int##bits##x##size##_altivec_lrshift + .lrshift = v##sign##int##bits##x##size##_altivec_lrshift, #define VEC_ALTIVEC_DEFINE_LRSHIFT(sign, csign, bits, size) -#define VEC_ALTIVEC_STRUCT_LRSHIFT(sign, csign, bits, size) \ - v##sign##int##bits##x##size##_generic_lrshift +#define VEC_ALTIVEC_STRUCT_LRSHIFT(sign, csign, bits, size) /* nothing */ #define VEC_ALTIVEC_CAST_BOOL_8 (vector signed char) #define VEC_ALTIVEC_CAST_BOOL_U8 (vector unsigned char) @@ -109,26 +108,26 @@ /* Since altivec conveniently made their API super user friendly, we can just use * one giant macro to define literally everything */ #define VEC_DEFINE_OPERATIONS_SIGN(sign, csign, bits, size) \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_load_aligned(const vec_##sign##int##bits in[size]) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_load_aligned(const vec_##sign##int##bits in[size]) \ { \ union v##sign##int##bits##x##size##_impl_data vec; \ vec.altivec = vec_ld(0, in); \ return vec.vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_load(const vec_##sign##int##bits in[size]) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_load(const vec_##sign##int##bits in[size]) \ { \ union v##sign##int##bits##x##size##_impl_data vec; \ vec.altivec = vec_perm(vec_ld(0, in), vec_ld(15, in), vec_lvsl(0, in)); \ return vec.vec; \ } \ \ - static void v##sign##int##bits##x##size##_altivec_store_aligned(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ + VEC_FUNC_IMPL void v##sign##int##bits##x##size##_altivec_store_aligned(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ { \ vec_st(((union v##sign##int##bits##x##size##_impl_data *)&vec)->altivec, 0, out); \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -137,7 +136,7 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -148,7 +147,25 @@ \ VEC_ALTIVEC_DEFINE_MUL(sign, csign, bits, size) \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_lshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_min(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union vuint##bits##x##size##_impl_data *vec2d = (union vuint##bits##x##size##_impl_data *)&vec2; \ + \ + vec1d->altivec = vec_min(vec1d->altivec, vec2d->altivec); \ + return vec1d->vec; \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_max(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union vuint##bits##x##size##_impl_data *vec2d = (union vuint##bits##x##size##_impl_data *)&vec2; \ + \ + vec1d->altivec = vec_max(vec1d->altivec, vec2d->altivec); \ + return vec1d->vec; \ + } \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_lshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union vuint##bits##x##size##_impl_data *vec2d = (union vuint##bits##x##size##_impl_data *)&vec2; \ @@ -157,7 +174,7 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_rshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_rshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union vuint##bits##x##size##_impl_data *vec2d = (union vuint##bits##x##size##_impl_data *)&vec2; \ @@ -168,7 +185,7 @@ \ VEC_ALTIVEC_DEFINE_##sign##LRSHIFT(sign, csign, bits, size) \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_avg(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_avg(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -177,7 +194,7 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -186,7 +203,7 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -195,7 +212,7 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -206,7 +223,7 @@ \ VEC_ALTIVEC_DEFINE_SPLAT(sign, csign, bits, size) \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_cmplt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_cmplt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -215,7 +232,7 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_cmple(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_cmple(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -224,7 +241,7 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_cmpeq(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_cmpeq(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -233,7 +250,7 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_cmpge(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_cmpge(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -242,7 +259,7 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_cmpgt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_cmpgt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -252,28 +269,25 @@ } \ \ const v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_altivec = { \ - VEC_ALTIVEC_STRUCT_SPLAT(sign, csign, bits, size), \ - v##sign##int##bits##x##size##_altivec_load_aligned, \ - v##sign##int##bits##x##size##_altivec_load, \ - v##sign##int##bits##x##size##_altivec_store_aligned, \ - v##sign##int##bits##x##size##_generic_store, \ - v##sign##int##bits##x##size##_altivec_add, \ - v##sign##int##bits##x##size##_altivec_sub, \ - VEC_ALTIVEC_STRUCT_MUL(sign, csign, bits, size), \ - v##sign##int##bits##x##size##_generic_div, \ - v##sign##int##bits##x##size##_altivec_avg, \ - v##sign##int##bits##x##size##_altivec_and, \ - v##sign##int##bits##x##size##_altivec_or, \ - v##sign##int##bits##x##size##_altivec_xor, \ - v##sign##int##bits##x##size##_generic_not, \ - v##sign##int##bits##x##size##_altivec_lshift, \ - v##sign##int##bits##x##size##_altivec_rshift, \ - VEC_ALTIVEC_STRUCT_##sign##LRSHIFT(sign, csign, bits, size), \ - v##sign##int##bits##x##size##_altivec_cmplt, \ - v##sign##int##bits##x##size##_altivec_cmple, \ - v##sign##int##bits##x##size##_altivec_cmpeq, \ - v##sign##int##bits##x##size##_altivec_cmpge, \ - v##sign##int##bits##x##size##_altivec_cmpgt, \ + VEC_ALTIVEC_STRUCT_SPLAT(sign, csign, bits, size) \ + .load_aligned = v##sign##int##bits##x##size##_altivec_load_aligned, \ + .load = v##sign##int##bits##x##size##_altivec_load, \ + .store_aligned = v##sign##int##bits##x##size##_altivec_store_aligned, \ + .add = v##sign##int##bits##x##size##_altivec_add, \ + .sub = v##sign##int##bits##x##size##_altivec_sub, \ + VEC_ALTIVEC_STRUCT_MUL(sign, csign, bits, size) \ + .avg = v##sign##int##bits##x##size##_altivec_avg, \ + .band = v##sign##int##bits##x##size##_altivec_and, \ + .bor = v##sign##int##bits##x##size##_altivec_or, \ + .bxor = v##sign##int##bits##x##size##_altivec_xor, \ + .lshift = v##sign##int##bits##x##size##_altivec_lshift, \ + .rshift = v##sign##int##bits##x##size##_altivec_rshift, \ + VEC_ALTIVEC_STRUCT_##sign##LRSHIFT(sign, csign, bits, size) \ + .cmplt = v##sign##int##bits##x##size##_altivec_cmplt, \ + .cmple = v##sign##int##bits##x##size##_altivec_cmple, \ + .cmpeq = v##sign##int##bits##x##size##_altivec_cmpeq, \ + .cmpge = v##sign##int##bits##x##size##_altivec_cmpge, \ + .cmpgt = v##sign##int##bits##x##size##_altivec_cmpgt, \ }; #define VEC_DEFINE_OPERATIONS(bits, size) \
--- a/src/impl/x86/avx2.c Mon Nov 25 00:33:02 2024 -0500 +++ b/src/impl/x86/avx2.c Thu Apr 24 00:54:02 2025 -0400 @@ -23,70 +23,88 @@ **/ #include "vec/impl/x86/avx2.h" -#include "vec/impl/generic.h" #include <immintrin.h> -// this does NOT handle sign bits properly, use with caution -#define VEC_AVX2_OPERATION_8x32_16x16(op, sign) \ - do { \ - union v##sign##int8x32_impl_data *vec1d = (union v##sign##int8x32_impl_data *)&vec1; \ - union v##sign##int8x32_impl_data *vec2d = (union v##sign##int8x32_impl_data *)&vec2; \ +/* ------------------------------------------------------------------------ */ +/* min/max */ + +#define VEC_AVX2_MINMAX_TEMPLATE(SIGN, BITS, SIZE, INTLSIGN, OP) \ + VEC_FUNC_IMPL v##SIGN##int##BITS##x##SIZE v##SIGN##int##BITS##x##SIZE##_avx2_##OP(v##SIGN##int##BITS##x##SIZE vec1, v##SIGN##int##BITS##x##SIZE vec2) \ + { \ + union v##SIGN##int##BITS##x##SIZE##_impl_data *vec1d = (union v##SIGN##int##BITS##x##SIZE##_impl_data *)&vec1; \ + union v##SIGN##int##BITS##x##SIZE##_impl_data *vec2d = (union v##SIGN##int##BITS##x##SIZE##_impl_data *)&vec2; \ \ - /* unpack and multiply */ \ - __m256i dst_even = _mm256_##op##_epi16(vec1d->avx2, vec2d->avx2); \ - __m256i dst_odd = _mm256_##op##_epi16(_mm256_srli_epi16(vec1d->avx2, 8), _mm256_srli_epi16(vec2d->avx2, 8)); \ + vec1d->avx2 = _mm256_##OP##_ep##INTLSIGN##BITS(vec1d->avx2, vec2d->avx2); \ \ - /* repack */ \ - vec1d->avx2 = _mm256_or_si256( \ - _mm256_slli_epi16(dst_odd, 8), \ - _mm256_srli_epi16(_mm256_slli_epi16(dst_even, 8), 8) \ - ); \ return vec1d->vec; \ - } while (0) + } + +#define VEC_AVX2_MINMAX_8x32(OP) VEC_AVX2_MINMAX_TEMPLATE( , 8, 32, i, OP) +#define VEC_AVX2_MINMAX_u8x32(OP) VEC_AVX2_MINMAX_TEMPLATE(u, 8, 32, u, OP) +#define VEC_AVX2_MINMAX_16x16(OP) VEC_AVX2_MINMAX_TEMPLATE( , 16, 16, i, OP) +#define VEC_AVX2_MINMAX_u16x16(OP) VEC_AVX2_MINMAX_TEMPLATE(u, 16, 16, u, OP) +#define VEC_AVX2_MINMAX_32x8(OP) VEC_AVX2_MINMAX_TEMPLATE( , 32, 8, i, OP) +#define VEC_AVX2_MINMAX_u32x8(OP) VEC_AVX2_MINMAX_TEMPLATE(u, 32, 8, u, OP) +#define VEC_AVX2_MINMAX_64x4(OP) /* nothing */ +#define VEC_AVX2_MINMAX_u64x4(OP) /* nothing */ + +#define VEC_AVX2_STRUCT_MINMAX_8x32(OP, SIGN) v##SIGN##int8x32_avx2_##OP +#define VEC_AVX2_STRUCT_MINMAX_16x16(OP, SIGN) v##SIGN##int16x16_avx2_##OP +#define VEC_AVX2_STRUCT_MINMAX_32x8(OP, SIGN) v##SIGN##int32x8_avx2_##OP +#define VEC_AVX2_STRUCT_MINMAX_64x4(OP, SIGN) NULL + +/* reused this for avg */ -#define VEC_AVX2_OPERATION_16x16(op, sign) \ - do { \ - union v##sign##int16x16_impl_data *vec1d = (union v##sign##int16x16_impl_data *)&vec1; \ - union v##sign##int16x16_impl_data *vec2d = (union v##sign##int16x16_impl_data *)&vec2; \ - \ - /* unpack and multiply */ \ - __m256i dst_even = _mm256_##op##_epi32(vec1d->avx2, vec2d->avx2); \ - __m256i dst_odd = _mm256_##op##_epi32(_mm256_srli_epi32(vec1d->avx2, 16), _mm256_srli_epi32(vec2d->avx2, 16)); \ - \ - /* repack */ \ - vec1d->avx2 = _mm256_or_si256( \ - _mm256_slli_epi32(dst_odd, 16), \ - _mm256_srli_epi32(_mm256_slli_epi16(dst_even, 16), 16) \ - ); \ - return vec1d->vec; \ - } while (0) +#define VEC_AVX2_AVG_8x32 /* nothing */ +#define VEC_AVX2_AVG_16x16 /* nothing */ +#define VEC_AVX2_AVG_32x8 /* nothing */ +#define VEC_AVX2_AVG_64x4 /* nothing */ + +#define VEC_AVX2_AVG_u8x32 VEC_AVX2_MINMAX_TEMPLATE(u, 8, 32, u, avg) +#define VEC_AVX2_AVG_u16x16 VEC_AVX2_MINMAX_TEMPLATE(u, 16, 16, u, avg) +#define VEC_AVX2_AVG_u32x8 /* nothing */ +#define VEC_AVX2_AVG_u64x4 /* nothing */ + +#define VEC_AVX2_STRUCT_AVG_8x32 NULL +#define VEC_AVX2_STRUCT_AVG_16x16 NULL +#define VEC_AVX2_STRUCT_AVG_32x8 NULL +#define VEC_AVX2_STRUCT_AVG_64x4 NULL + +#define VEC_AVX2_STRUCT_AVG_u8x32 vuint8x32_avx2_avg +#define VEC_AVX2_STRUCT_AVG_u16x16 vuint16x16_avx2_avg +#define VEC_AVX2_STRUCT_AVG_u32x8 NULL +#define VEC_AVX2_STRUCT_AVG_u64x4 NULL + +/* ------------------------------------------------------------------------ */ // multiplication -#define VEC_AVX2_MUL_8x32(sign) \ - VEC_AVX2_OPERATION_8x32_16x16(mullo, sign) +#define VEC_AVX2_MUL_8x32(sign) /* nothing */ #define VEC_AVX2_MUL_16x16(sign) \ - do { \ + VEC_FUNC_IMPL v##sign##int16x16 v##sign##int16x16_avx2_mul(v##sign##int16x16 vec1, v##sign##int16x16 vec2) \ + { \ union v##sign##int16x16_impl_data *vec1d = (union v##sign##int16x16_impl_data *)&vec1; \ union v##sign##int16x16_impl_data *vec2d = (union v##sign##int16x16_impl_data *)&vec2; \ \ vec1d->avx2 = _mm256_mullo_epi16(vec1d->avx2, vec2d->avx2); \ return vec1d->vec; \ - } while (0) + } #define VEC_AVX2_MUL_32x8(sign) \ - do { \ + VEC_FUNC_IMPL v##sign##int32x8 v##sign##int32x8_avx2_mul(v##sign##int32x8 vec1, v##sign##int32x8 vec2) \ + { \ union v##sign##int32x8_impl_data *vec1d = (union v##sign##int32x8_impl_data *)&vec1; \ union v##sign##int32x8_impl_data *vec2d = (union v##sign##int32x8_impl_data *)&vec2; \ \ vec1d->avx2 = _mm256_mullo_epi32(vec1d->avx2, vec2d->avx2); \ return vec1d->vec; \ - } while (0) + } #define VEC_AVX2_MUL_64x4(sign) \ - do { \ + VEC_FUNC_IMPL v##sign##int64x4 v##sign##int64x4_avx2_mul(v##sign##int64x4 vec1, v##sign##int64x4 vec2) \ + { \ union v##sign##int64x4_impl_data *vec1d = (union v##sign##int64x4_impl_data *)&vec1; \ union v##sign##int64x4_impl_data *vec2d = (union v##sign##int64x4_impl_data *)&vec2; \ \ @@ -100,7 +118,12 @@ \ vec1d->avx2 = _mm256_add_epi64(hi, ac); \ return vec1d->vec; \ - } while (0) + } + +#define VEC_AVX2_STRUCT_MUL_8x32(SIGN) NULL +#define VEC_AVX2_STRUCT_MUL_16x16(SIGN) v##SIGN##int16x16_avx2_mul +#define VEC_AVX2_STRUCT_MUL_32x8(SIGN) v##SIGN##int32x8_avx2_mul +#define VEC_AVX2_STRUCT_MUL_64x4(SIGN) v##SIGN##int64x4_avx2_mul // operations @@ -113,31 +136,31 @@ VEC_STATIC_ASSERT(VEC_ALIGNOF(__m256i) <= VEC_ALIGNOF(v##sign##int##bits##x##size), "vec: v" #sign "int" #bits "x" #size " alignment needs to be expanded to fit intrinsic type size"); \ VEC_STATIC_ASSERT(sizeof(__m256i) <= sizeof(v##sign##int##bits##x##size), "vec: v" #sign "int" #bits "x" #size " needs to be expanded to fit intrinsic type size"); \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_load_aligned(const vec_##sign##int##bits in[size]) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_load_aligned(const vec_##sign##int##bits in[size]) \ { \ union v##sign##int##bits##x##size##_impl_data vec; \ vec.avx2 = _mm256_load_si256((const __m256i *)in); \ return vec.vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_load(const vec_##sign##int##bits in[size]) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_load(const vec_##sign##int##bits in[size]) \ { \ union v##sign##int##bits##x##size##_impl_data vec; \ vec.avx2 = _mm256_loadu_si256((const __m256i *)in); \ return vec.vec; \ } \ \ - static void v##sign##int##bits##x##size##_avx2_store_aligned(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ + VEC_FUNC_IMPL void v##sign##int##bits##x##size##_avx2_store_aligned(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ { \ _mm256_store_si256((__m256i *)out, ((union v##sign##int##bits##x##size##_impl_data*)&vec)->avx2); \ } \ \ - static void v##sign##int##bits##x##size##_avx2_store(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ + VEC_FUNC_IMPL void v##sign##int##bits##x##size##_avx2_store(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ { \ _mm256_storeu_si256((__m256i *)out, ((union v##sign##int##bits##x##size##_impl_data*)&vec)->avx2); \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -146,7 +169,7 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -155,12 +178,9 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ - { \ - VEC_AVX2_MUL_##bits##x##size(sign); \ - } \ + VEC_AVX2_MUL_##bits##x##size(sign) \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -169,7 +189,7 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -178,7 +198,7 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -187,29 +207,25 @@ return vec1d->vec; \ } \ \ + VEC_AVX2_AVG_##sign##bits##x##size \ + \ + VEC_AVX2_MINMAX_##sign##bits##x##size(min) \ + VEC_AVX2_MINMAX_##sign##bits##x##size(max) \ + \ const v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_avx2 = { \ - v##sign##int##bits##x##size##_generic_splat, \ - v##sign##int##bits##x##size##_avx2_load_aligned, \ - v##sign##int##bits##x##size##_avx2_load, \ - v##sign##int##bits##x##size##_avx2_store_aligned, \ - v##sign##int##bits##x##size##_avx2_store, \ - v##sign##int##bits##x##size##_avx2_add, \ - v##sign##int##bits##x##size##_avx2_sub, \ - v##sign##int##bits##x##size##_avx2_mul, \ - v##sign##int##bits##x##size##_generic_div, \ - v##sign##int##bits##x##size##_generic_avg, \ - v##sign##int##bits##x##size##_avx2_and, \ - v##sign##int##bits##x##size##_avx2_or, \ - v##sign##int##bits##x##size##_avx2_xor, \ - v##sign##int##bits##x##size##_generic_not, \ - v##sign##int##bits##x##size##_generic_lshift, \ - v##sign##int##bits##x##size##_generic_rshift, \ - v##sign##int##bits##x##size##_generic_lrshift, \ - v##sign##int##bits##x##size##_generic_cmplt, \ - v##sign##int##bits##x##size##_generic_cmple, \ - v##sign##int##bits##x##size##_generic_cmpeq, \ - v##sign##int##bits##x##size##_generic_cmpge, \ - v##sign##int##bits##x##size##_generic_cmpgt, \ + .load_aligned = v##sign##int##bits##x##size##_avx2_load_aligned, \ + .load = v##sign##int##bits##x##size##_avx2_load, \ + .store_aligned = v##sign##int##bits##x##size##_avx2_store_aligned, \ + .store = v##sign##int##bits##x##size##_avx2_store, \ + .add = v##sign##int##bits##x##size##_avx2_add, \ + .sub = v##sign##int##bits##x##size##_avx2_sub, \ + .mul = VEC_AVX2_STRUCT_MUL_##bits##x##size(sign), \ + .band = v##sign##int##bits##x##size##_avx2_and, \ + .bor = v##sign##int##bits##x##size##_avx2_or, \ + .bxor = v##sign##int##bits##x##size##_avx2_xor, \ + .min = VEC_AVX2_STRUCT_MINMAX_##bits##x##size(min, sign), \ + .max = VEC_AVX2_STRUCT_MINMAX_##bits##x##size(max, sign), \ + .avg = VEC_AVX2_STRUCT_AVG_##sign##bits##x##size, \ }; #define VEC_AVX2_DEFINE_OPERATIONS(bits, size) \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/impl/x86/avx512bw.c Thu Apr 24 00:54:02 2025 -0400 @@ -0,0 +1,139 @@ +/** + * vec - a tiny SIMD vector library in C99 + * + * Copyright (c) 2024 Paper + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +**/ + +#include "vec/impl/x86/avx512bw.h" + +#include <immintrin.h> + +/* ------------------------------------------------------------------------ */ + +#define VEC_AVX512BW_MINMAX_TEMPLATE(SIGN, BITS, SIZE, INTLSIGN, OP) \ + VEC_FUNC_IMPL v##SIGN##int##BITS##x##SIZE v##SIGN##int##BITS##x##SIZE##_avx512bw_##OP(v##SIGN##int##BITS##x##SIZE vec1, v##SIGN##int##BITS##x##SIZE vec2) \ + { \ + union v##SIGN##int##BITS##x##SIZE##_impl_data *vec1d = (union v##SIGN##int##BITS##x##SIZE##_impl_data *)&vec1; \ + union v##SIGN##int##BITS##x##SIZE##_impl_data *vec2d = (union v##SIGN##int##BITS##x##SIZE##_impl_data *)&vec2; \ + \ + vec1d->avx512bw = _mm512_##OP##_ep##INTLSIGN##BITS(vec1d->avx512bw, vec2d->avx512bw); \ + \ + return vec1d->vec; \ + } + +#define VEC_AVX512BW_MINMAX_8x64(OP) VEC_AVX512BW_MINMAX_TEMPLATE( , 8, 64, i, OP) +#define VEC_AVX512BW_MINMAX_u8x64(OP) VEC_AVX512BW_MINMAX_TEMPLATE(u, 8, 64, u, OP) +#define VEC_AVX512BW_MINMAX_16x32(OP) VEC_AVX512BW_MINMAX_TEMPLATE( , 16, 32, i, OP) +#define VEC_AVX512BW_MINMAX_u16x32(OP) VEC_AVX512BW_MINMAX_TEMPLATE(u, 16, 32, u, OP) + +#define VEC_AVX512BW_STRUCT_MINMAX_8x64(OP, SIGN) v##SIGN##int8x64_avx512bw_##OP +#define VEC_AVX512BW_STRUCT_MINMAX_16x32(OP, SIGN) v##SIGN##int16x32_avx512bw_##OP + +/* ------------------------------------------------------------------------ */ + +#define VEC_AVX512BW_OPERATION_EX(name, op, sign, bits, size, secondsign) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512bw_##name(v##sign##int##bits##x##size vec1, v##secondsign##int##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union v##secondsign##int##bits##x##size##_impl_data *vec2d = (union v##secondsign##int##bits##x##size##_impl_data *)&vec2; \ + \ + vec1d->avx512bw = _mm512_##op##_epi##bits(vec1d->avx512bw, vec2d->avx512bw); \ + \ + return vec1d->vec; \ + } + +#define VEC_AVX512BW_OPERATION(name, op, sign, bits, size) \ + VEC_AVX512BW_OPERATION_EX(name, op, sign, bits, size, sign) + +#define VEC_AVX512BW_OPERATION_SHIFT(name, op, sign, bits, size) \ + VEC_AVX512BW_OPERATION_EX(name, op, sign, bits, size, u) + +#define VEC_AVX512BW_ADD_8x64(sign) VEC_AVX512BW_OPERATION(add, add, sign, 8, 64) +#define VEC_AVX512BW_ADD_16x32(sign) VEC_AVX512BW_OPERATION(add, add, sign, 16, 32) + +#define VEC_AVX512BW_SUB_8x64(sign) VEC_AVX512BW_OPERATION(sub, sub, sign, 8, 64) +#define VEC_AVX512BW_SUB_16x32(sign) VEC_AVX512BW_OPERATION(sub, sub, sign, 16, 32) + +#define VEC_AVX512BW_MUL_8x64(sign) /* nothing */ +#define VEC_AVX512BW_MUL_16x32(sign) VEC_AVX512BW_OPERATION(mul, mullo, sign, 16, 32) + +#define VEC_AVX512BW_LSHIFT_8x64(sign) /* nothing */ +#define VEC_AVX512BW_LSHIFT_16x32(sign) VEC_AVX512BW_OPERATION_SHIFT(lshift, sllv, sign, 16, 32) + +#define VEC_AVX512BW_XRSHIFT(name, bits, size, sign, aORl) \ + VEC_AVX512BW_OPERATION_SHIFT(name, sr##aORl##v, sign, bits, size) + +/* always the same, regardless of signedness */ +#define VEC_AVX512BW_LRSHIFT_8x64(sign) /* nothing */ +#define VEC_AVX512BW_LRSHIFT_16x32(sign) VEC_AVX512BW_XRSHIFT(lrshift, 16, 32, sign, l) + +#define VEC_AVX512BW_RSHIFT_8x64(sign) /* nothing */ +#define VEC_AVX512BW_RSHIFT_16x32(sign) VEC_AVX512BW_XRSHIFT(rshift, 16, 32, sign, a) + +#define VEC_AVX512BW_uRSHIFT_8x64(sign) /* nothing */ +#define VEC_AVX512BW_uRSHIFT_16x32(sign) VEC_AVX512BW_XRSHIFT(rshift, 16, 32, sign, l) + +/* ok */ +#define VEC_AVX512BW_STRUCT_ADDSUB_8x64(OP, SIGN) v##SIGN##int8x64_avx512bw_##OP +#define VEC_AVX512BW_STRUCT_ADDSUB_16x32(OP, SIGN) v##SIGN##int16x32_avx512bw_##OP + +#define VEC_AVX512BW_STRUCT_OPERATION_8x64(OP, SIGN) NULL +#define VEC_AVX512BW_STRUCT_OPERATION_16x32(OP, SIGN) v##SIGN##int16x32_avx512bw_##OP + +/* ------------------------------------------------------------------------ */ + +#define VEC_AVX512BW_DEFINE_OPERATIONS_SIGN(sign, bits, size) \ + union v##sign##int##bits##x##size##_impl_data { \ + v##sign##int##bits##x##size vec; \ + __m512i avx512bw; \ + }; \ + \ + VEC_STATIC_ASSERT(VEC_ALIGNOF(__m512i) <= VEC_ALIGNOF(v##sign##int##bits##x##size), "vec: v" #sign "int" #bits "x" #size " alignment needs to be expanded to fit intrinsic type size"); \ + VEC_STATIC_ASSERT(sizeof(__m512i) <= sizeof(v##sign##int##bits##x##size), "vec: v" #sign "int" #bits "x" #size " needs to be expanded to fit intrinsic type size"); \ + \ + VEC_AVX512BW_ADD_##bits##x##size(sign) \ + VEC_AVX512BW_SUB_##bits##x##size(sign) \ + VEC_AVX512BW_MUL_##bits##x##size(sign) \ + \ + VEC_AVX512BW_LSHIFT_##bits##x##size(sign); \ + VEC_AVX512BW_##sign##RSHIFT_##bits##x##size(sign); \ + VEC_AVX512BW_LRSHIFT_##bits##x##size(sign); \ + \ + VEC_AVX512BW_MINMAX_##sign##bits##x##size(min) \ + VEC_AVX512BW_MINMAX_##sign##bits##x##size(max) \ + \ + const v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_avx512bw = { \ + .add = VEC_AVX512BW_STRUCT_ADDSUB_##bits##x##size(add, sign), \ + .sub = VEC_AVX512BW_STRUCT_ADDSUB_##bits##x##size(sub, sign), \ + .mul = VEC_AVX512BW_STRUCT_OPERATION_##bits##x##size(mul, sign), \ + .lshift = VEC_AVX512BW_STRUCT_OPERATION_##bits##x##size(lshift, sign), \ + .rshift = VEC_AVX512BW_STRUCT_OPERATION_##bits##x##size(rshift, sign), \ + .lrshift = VEC_AVX512BW_STRUCT_OPERATION_##bits##x##size(lrshift, sign), \ + .min = VEC_AVX512BW_STRUCT_MINMAX_##bits##x##size(min, sign), \ + .max = VEC_AVX512BW_STRUCT_MINMAX_##bits##x##size(max, sign), \ + }; + +#define VEC_AVX512BW_DEFINE_OPERATIONS(bits, size) \ + VEC_AVX512BW_DEFINE_OPERATIONS_SIGN(u, bits, size) \ + VEC_AVX512BW_DEFINE_OPERATIONS_SIGN( , bits, size) + +VEC_AVX512BW_DEFINE_OPERATIONS(8, 64) +VEC_AVX512BW_DEFINE_OPERATIONS(16, 32)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/impl/x86/avx512dq.c Thu Apr 24 00:54:02 2025 -0400 @@ -0,0 +1,57 @@ +/** + * vec - a tiny SIMD vector library in C99 + * + * Copyright (c) 2024 Paper + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +**/ + +#include "vec/impl/x86/avx512dq.h" + +#include <immintrin.h> + +/* ------------------------------------------------------------------------ */ + +#define VEC_AVX512DQ_DEFINE_OPERATIONS_SIGN(sign, bits, size) \ + union v##sign##int##bits##x##size##_impl_data { \ + v##sign##int##bits##x##size vec; \ + __m512i avx512dq; \ + }; \ + \ + VEC_STATIC_ASSERT(VEC_ALIGNOF(__m512i) <= VEC_ALIGNOF(v##sign##int##bits##x##size), "vec: v" #sign "int" #bits "x" #size " alignment needs to be expanded to fit intrinsic type size"); \ + VEC_STATIC_ASSERT(sizeof(__m512i) <= sizeof(v##sign##int##bits##x##size), "vec: v" #sign "int" #bits "x" #size " needs to be expanded to fit intrinsic type size"); \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512dq_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ + \ + vec1d->avx512dq = _mm512_mullo_epi64(vec1d->avx512dq, vec2d->avx512dq); \ + return vec1d->vec; \ + } \ + \ + const v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_avx512dq = { \ + .mul = v##sign##int##bits##x##size##_avx512dq_mul, \ + }; + +#define VEC_AVX512DQ_DEFINE_OPERATIONS(bits, size) \ + VEC_AVX512DQ_DEFINE_OPERATIONS_SIGN(u, bits, size) \ + VEC_AVX512DQ_DEFINE_OPERATIONS_SIGN( , bits, size) + +VEC_AVX512DQ_DEFINE_OPERATIONS(64, 8)
--- a/src/impl/x86/avx512f.c Mon Nov 25 00:33:02 2024 -0500 +++ b/src/impl/x86/avx512f.c Thu Apr 24 00:54:02 2025 -0400 @@ -23,202 +23,79 @@ **/ #include "vec/impl/x86/avx512f.h" -#include "vec/impl/generic.h" #include <immintrin.h> -// this is a stupid amount of work just to do these operations, is it really worth it ? -// also same note in avx2.c applies here, these do not handle sign bits properly, which -// isn't that big of a deal for regular arithmetic operations, but matters quite a bit -// when doing things like arithmetic shifts. -#define VEC_AVX512F_OPERATION_8x64(op, sign) \ - do { \ - union v##sign##int8x64_impl_data *vec1d = (union v##sign##int8x64_impl_data *)&vec1; \ - union v##sign##int8x64_impl_data *vec2d = (union v##sign##int8x64_impl_data *)&vec2; \ +/* ------------------------------------------------------------------------ */ + +#define VEC_AVX512F_MINMAX_TEMPLATE(SIGN, BITS, SIZE, INTLSIGN, OP) \ + VEC_FUNC_IMPL v##SIGN##int##BITS##x##SIZE v##SIGN##int##BITS##x##SIZE##_avx512f_##OP(v##SIGN##int##BITS##x##SIZE vec1, v##SIGN##int##BITS##x##SIZE vec2) \ + { \ + union v##SIGN##int##BITS##x##SIZE##_impl_data *vec1d = (union v##SIGN##int##BITS##x##SIZE##_impl_data *)&vec1; \ + union v##SIGN##int##BITS##x##SIZE##_impl_data *vec2d = (union v##SIGN##int##BITS##x##SIZE##_impl_data *)&vec2; \ + \ + vec1d->avx512f = _mm512_##OP##_ep##INTLSIGN##BITS(vec1d->avx512f, vec2d->avx512f); \ \ - /* unpack and operate */ \ - __m512i dst_1 = _mm512_##op##_epi32(_mm512_srli_epi32(_mm512_slli_epi32(vec1d->avx512f, 24), 24), _mm512_srli_epi32(_mm512_slli_epi32(vec2d->avx512f, 24), 24)); \ - __m512i dst_2 = _mm512_##op##_epi32(_mm512_srli_epi32(_mm512_slli_epi32(vec1d->avx512f, 16), 24), _mm512_srli_epi32(_mm512_slli_epi32(vec2d->avx512f, 16), 24)); \ - __m512i dst_3 = _mm512_##op##_epi32(_mm512_srli_epi32(_mm512_slli_epi32(vec1d->avx512f, 8), 24), _mm512_srli_epi32(_mm512_slli_epi32(vec2d->avx512f, 8), 24)); \ - __m512i dst_4 = _mm512_##op##_epi32(_mm512_srli_epi32(vec1d->avx512f, 24), _mm512_srli_epi32(vec2d->avx512f, 24)); \ + return vec1d->vec; \ + } + +#define VEC_AVX512F_MINMAX_32x16(OP) VEC_AVX512F_MINMAX_TEMPLATE( , 32, 16, i, OP) +#define VEC_AVX512F_MINMAX_u32x16(OP) VEC_AVX512F_MINMAX_TEMPLATE(u, 32, 16, u, OP) +#define VEC_AVX512F_MINMAX_64x8(OP) VEC_AVX512F_MINMAX_TEMPLATE( , 64, 8, i, OP) +#define VEC_AVX512F_MINMAX_u64x8(OP) VEC_AVX512F_MINMAX_TEMPLATE(u, 64, 8, u, OP) + +#define VEC_AVX512F_STRUCT_MINMAX_32x16(OP, SIGN) v##SIGN##int32x16_avx512f_##OP +#define VEC_AVX512F_STRUCT_MINMAX_64x8(OP, SIGN) v##SIGN##int64x8_avx512f_##OP + +/* ------------------------------------------------------------------------ */ + +#define VEC_AVX512F_OPERATION_EX(name, op, sign, bits, size, secondsign) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_##name(v##sign##int##bits##x##size vec1, v##secondsign##int##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union v##secondsign##int##bits##x##size##_impl_data *vec2d = (union v##secondsign##int##bits##x##size##_impl_data *)&vec2; \ \ - /* repack */ \ - vec1d->avx512f = _mm512_or_si512( \ - _mm512_or_si512( \ - _mm512_srli_epi32(_mm512_slli_epi32(dst_1, 24), 24), \ - _mm512_srli_epi32(_mm512_slli_epi32(dst_2, 24), 16) \ - ), \ - _mm512_or_si512( \ - _mm512_srli_epi32(_mm512_slli_epi32(dst_3, 24), 8), \ - _mm512_slli_epi32(dst_4, 24) \ - ) \ - ); \ + vec1d->avx512f = _mm512_##op##_epi##bits(vec1d->avx512f, vec2d->avx512f); \ \ return vec1d->vec; \ - } while (0) + } -#define VEC_AVX512F_OPERATION_16x32(op, sign) \ - do { \ - union v##sign##int16x32_impl_data *vec1d = (union v##sign##int16x32_impl_data *)&vec1; \ - union v##sign##int16x32_impl_data *vec2d = (union v##sign##int16x32_impl_data *)&vec2; \ - \ - /* unpack and operate; it would be nice if we had an _m512_andi_epi32... */ \ - __m512i dst_1 = _mm512_##op##_epi32(_mm512_srli_epi32(_mm512_slli_epi32(vec1d->avx512f, 16), 16), _mm512_srli_epi32(_mm512_slli_epi32(vec2d->avx512f, 16), 16)); \ - __m512i dst_2 = _mm512_##op##_epi32(_mm512_srli_epi32(vec1d->avx512f, 16), _mm512_srli_epi32(vec2d->avx512f, 16)); \ - \ - /* repack */ \ - vec1d->avx512f = _mm512_or_si512( \ - _mm512_srli_epi32(_mm512_slli_epi32(dst_1, 16), 16), \ - _mm512_slli_epi32(dst_2, 16) \ - ); \ - return vec1d->vec; \ - } while (0) +#define VEC_AVX512F_OPERATION(name, op, sign, bits, size) \ + VEC_AVX512F_OPERATION_EX(name, op, sign, bits, size, sign) -#define VEC_AVX512F_ADD_8x64(sign) \ - VEC_AVX512F_OPERATION_8x64(add, sign) - -#define VEC_AVX512F_ADD_16x32(sign) \ - VEC_AVX512F_OPERATION_16x32(add, sign) - -#define VEC_AVX512F_ADD_32x16(sign) \ - do { \ - union v##sign##int32x16_impl_data *vec1d = (union v##sign##int32x16_impl_data *)&vec1; \ - union v##sign##int32x16_impl_data *vec2d = (union v##sign##int32x16_impl_data *)&vec2; \ - \ - vec1d->avx512f = _mm512_add_epi32(vec1d->avx512f, vec2d->avx512f); \ - return vec1d->vec; \ - } while (0) +#define VEC_AVX512F_OPERATION_SHIFT(name, op, sign, bits, size) \ + VEC_AVX512F_OPERATION_EX(name, op, sign, bits, size, u) -#define VEC_AVX512F_ADD_64x8(sign) \ - do { \ - union v##sign##int64x8_impl_data *vec1d = (union v##sign##int64x8_impl_data *)&vec1; \ - union v##sign##int64x8_impl_data *vec2d = (union v##sign##int64x8_impl_data *)&vec2; \ - \ - vec1d->avx512f = _mm512_add_epi64(vec1d->avx512f, vec2d->avx512f); \ - return vec1d->vec; \ - } while (0) - -#define VEC_AVX512F_SUB_8x64(sign) \ - VEC_AVX512F_OPERATION_8x64(sub, sign) - -#define VEC_AVX512F_SUB_16x32(sign) \ - VEC_AVX512F_OPERATION_16x32(sub, sign) - -#define VEC_AVX512F_SUB_32x16(sign) \ - do { \ - union v##sign##int32x16_impl_data *vec1d = (union v##sign##int32x16_impl_data *)&vec1; \ - union v##sign##int32x16_impl_data *vec2d = (union v##sign##int32x16_impl_data *)&vec2; \ - \ - vec1d->avx512f = _mm512_sub_epi32(vec1d->avx512f, vec2d->avx512f); \ - return vec1d->vec; \ - } while (0) +#define VEC_AVX512F_ADD_32x16(sign) VEC_AVX512F_OPERATION(add, add, sign, 32, 16) +#define VEC_AVX512F_ADD_64x8(sign) VEC_AVX512F_OPERATION(add, add, sign, 64, 8) -#define VEC_AVX512F_SUB_64x8(sign) \ - do { \ - union v##sign##int64x8_impl_data *vec1d = (union v##sign##int64x8_impl_data *)&vec1; \ - union v##sign##int64x8_impl_data *vec2d = (union v##sign##int64x8_impl_data *)&vec2; \ - \ - vec1d->avx512f = _mm512_sub_epi64(vec1d->avx512f, vec2d->avx512f); \ - return vec1d->vec; \ - } while (0) - -#define VEC_AVX512F_MUL_8x64(sign) \ - VEC_AVX512F_OPERATION_8x64(mullo, sign) +#define VEC_AVX512F_SUB_32x16(sign) VEC_AVX512F_OPERATION(sub, sub, sign, 32, 16) +#define VEC_AVX512F_SUB_64x8(sign) VEC_AVX512F_OPERATION(sub, sub, sign, 64, 8) -#define VEC_AVX512F_MUL_16x32(sign) \ - VEC_AVX512F_OPERATION_16x32(mullo, sign) - -#define VEC_AVX512F_MUL_32x16(sign) \ - do { \ - union v##sign##int32x16_impl_data *vec1d = (union v##sign##int32x16_impl_data *)&vec1; \ - union v##sign##int32x16_impl_data *vec2d = (union v##sign##int32x16_impl_data *)&vec2; \ - \ - vec1d->avx512f = _mm512_mullo_epi32(vec1d->avx512f, vec2d->avx512f); \ - return vec1d->vec; \ - } while (0) +#define VEC_AVX512F_MUL_32x16(sign) VEC_AVX512F_OPERATION(mul, mullo, sign, 32, 16) +#define VEC_AVX512F_MUL_64x8(sign) VEC_AVX512F_OPERATION(mul, mullox, sign, 64, 8) -#define VEC_AVX512F_MUL_64x8(sign) \ - do { \ - union v##sign##int64x8_impl_data *vec1d = (union v##sign##int64x8_impl_data *)&vec1; \ - union v##sign##int64x8_impl_data *vec2d = (union v##sign##int64x8_impl_data *)&vec2; \ - \ - __m512i ac = _mm512_mul_epu32(vec1d->avx512f, vec2d->avx512f); \ - __m512i b = _mm512_srli_epi64(vec1d->avx512f, 32); \ - __m512i bc = _mm512_mul_epu32(b, vec2d->avx512f); \ - __m512i d = _mm512_srli_epi64(vec2d->avx512f, 32); \ - __m512i ad = _mm512_mul_epu32(vec1d->avx512f, d); \ - __m512i hi = _mm512_add_epi64(bc, ad); \ - hi = _mm512_slli_epi64(hi, 32); \ - \ - vec1d->avx512f = _mm512_add_epi64(hi, ac); \ - return vec1d->vec; \ - } while (0) - -#define VEC_AVX512F_LSHIFT_8x64(sign) \ - VEC_AVX512F_OPERATION_8x64(sllv, sign) +#define VEC_AVX512F_LSHIFT_32x16(sign) VEC_AVX512F_OPERATION_SHIFT(lshift, sllv, sign, 32, 16) +#define VEC_AVX512F_LSHIFT_64x8(sign) VEC_AVX512F_OPERATION_SHIFT(lshift, sllv, sign, 64, 8) -#define VEC_AVX512F_LSHIFT_16x32(sign) \ - VEC_AVX512F_OPERATION_16x32(sllv, sign) +#define VEC_AVX512F_XRSHIFT(name, bits, size, sign, aORl) \ + VEC_AVX512F_OPERATION_SHIFT(name, sr##aORl##v, sign, bits, size) -#define VEC_AVX512F_LSHIFT_32x16(sign) \ - do { \ - union v##sign##int32x16_impl_data *vec1d = (union v##sign##int32x16_impl_data *)&vec1; \ - union v##sign##int32x16_impl_data *vec2d = (union v##sign##int32x16_impl_data *)&vec2; \ - \ - vec1d->avx512f = _mm512_sllv_epi32(vec1d->avx512f, vec2d->avx512f); \ - return vec1d->vec; \ - } while (0) - -#define VEC_AVX512F_LSHIFT_64x8(sign) \ - do { \ - union v##sign##int64x8_impl_data *vec1d = (union v##sign##int64x8_impl_data *)&vec1; \ - union v##sign##int64x8_impl_data *vec2d = (union v##sign##int64x8_impl_data *)&vec2; \ - \ - vec1d->avx512f = _mm512_sllv_epi64(vec1d->avx512f, vec2d->avx512f); \ - return vec1d->vec; \ - } while (0) +/* always the same, regardless of signedness */ +#define VEC_AVX512F_LRSHIFT_32x16(sign) VEC_AVX512F_XRSHIFT(lrshift, 32, 16, sign, l) +#define VEC_AVX512F_LRSHIFT_64x8(sign) VEC_AVX512F_XRSHIFT(lrshift, 64, 8, sign, l) -#define VEC_AVX512F_lRSHIFT_8x64(sign) \ - VEC_AVX512F_OPERATION_8x64(srlv, sign) - -#define VEC_AVX512F_lRSHIFT_16x32(sign) \ - VEC_AVX512F_OPERATION_16x32(srlv, sign) +#define VEC_AVX512F_RSHIFT_32x16(sign) VEC_AVX512F_XRSHIFT(rshift, 32, 16, sign, a) +#define VEC_AVX512F_RSHIFT_64x8(sign) VEC_AVX512F_XRSHIFT(rshift, 64, 8, sign, a) -#define VEC_AVX512F_aRSHIFT_8x64(sign) \ - do { \ - return v##sign##int8x64_generic_rshift(vec1, vec2); \ - } while (0) - -#define VEC_AVX512F_aRSHIFT_16x32(sign) \ - do { \ - return v##sign##int16x32_generic_rshift(vec1, vec2); \ - } while (0) - -#define VEC_AVX512F_RSHIFT_8x64(sign, aORl) VEC_AVX512F_##aORl##RSHIFT_8x64(sign) -#define VEC_AVX512F_RSHIFT_16x32(sign, aORl) VEC_AVX512F_##aORl##RSHIFT_16x32(sign) +#define VEC_AVX512F_uRSHIFT_32x16(sign) VEC_AVX512F_XRSHIFT(rshift, 32, 16, sign, l) +#define VEC_AVX512F_uRSHIFT_64x8(sign) VEC_AVX512F_XRSHIFT(rshift, 64, 8, sign, l) -#define VEC_AVX512F_RSHIFT_32x16(sign, aORl) \ - do { \ - union v##sign##int32x16_impl_data *vec1d = (union v##sign##int32x16_impl_data *)&vec1; \ - union v##sign##int32x16_impl_data *vec2d = (union v##sign##int32x16_impl_data *)&vec2; \ - \ - vec1d->avx512f = _mm512_sr##aORl##v_epi32(vec1d->avx512f, vec2d->avx512f); \ - return vec1d->vec; \ - } while (0) +/* ok */ +#define VEC_AVX512F_STRUCT_OPERATION_32x16(OP, SIGN) v##SIGN##int32x16_avx512f_##OP +#define VEC_AVX512F_STRUCT_OPERATION_64x8(OP, SIGN) v##SIGN##int64x8_avx512f_##OP -#define VEC_AVX512F_RSHIFT_64x8(sign, aORl) \ - do { \ - union v##sign##int64x8_impl_data *vec1d = (union v##sign##int64x8_impl_data *)&vec1; \ - union v##sign##int64x8_impl_data *vec2d = (union v##sign##int64x8_impl_data *)&vec2; \ - \ - vec1d->avx512f = _mm512_sr##aORl##v_epi64(vec1d->avx512f, vec2d->avx512f); \ - return vec1d->vec; \ - } while (0) - -#define VEC_AVX512F_uRSHIFT_8x64(sign, aORl) VEC_AVX512F_RSHIFT_8x64(sign, l) -#define VEC_AVX512F_uRSHIFT_16x32(sign, aORl) VEC_AVX512F_RSHIFT_16x32(sign, l) -#define VEC_AVX512F_uRSHIFT_32x16(sign, aORl) VEC_AVX512F_RSHIFT_32x16(sign, l) -#define VEC_AVX512F_uRSHIFT_64x8(sign, aORl) VEC_AVX512F_RSHIFT_64x8(sign, l) +/* ------------------------------------------------------------------------ */ #define VEC_AVX512F_DEFINE_OPERATIONS_SIGN(sign, bits, size) \ union v##sign##int##bits##x##size##_impl_data { \ @@ -229,46 +106,35 @@ VEC_STATIC_ASSERT(VEC_ALIGNOF(__m512i) <= VEC_ALIGNOF(v##sign##int##bits##x##size), "vec: v" #sign "int" #bits "x" #size " alignment needs to be expanded to fit intrinsic type size"); \ VEC_STATIC_ASSERT(sizeof(__m512i) <= sizeof(v##sign##int##bits##x##size), "vec: v" #sign "int" #bits "x" #size " needs to be expanded to fit intrinsic type size"); \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_load_aligned(const vec_##sign##int##bits in[size]) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_load_aligned(const vec_##sign##int##bits in[size]) \ { \ union v##sign##int##bits##x##size##_impl_data vec; \ vec.avx512f = _mm512_load_si512((const __m512i *)in); \ return vec.vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_load(const vec_##sign##int##bits in[size]) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_load(const vec_##sign##int##bits in[size]) \ { \ union v##sign##int##bits##x##size##_impl_data vec; \ vec.avx512f = _mm512_loadu_si512((const __m512i *)in); \ return vec.vec; \ } \ \ - static void v##sign##int##bits##x##size##_avx512f_store_aligned(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ + VEC_FUNC_IMPL void v##sign##int##bits##x##size##_avx512f_store_aligned(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ { \ _mm512_store_si512((__m512i *)out, ((union v##sign##int##bits##x##size##_impl_data *)&vec)->avx512f); \ } \ \ - static void v##sign##int##bits##x##size##_avx512f_store(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ + VEC_FUNC_IMPL void v##sign##int##bits##x##size##_avx512f_store(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ { \ _mm512_storeu_si512((__m512i *)out, ((union v##sign##int##bits##x##size##_impl_data *)&vec)->avx512f); \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ - { \ - VEC_AVX512F_ADD_##bits##x##size(sign); \ - } \ + VEC_AVX512F_ADD_##bits##x##size(sign) \ + VEC_AVX512F_SUB_##bits##x##size(sign) \ + VEC_AVX512F_MUL_##bits##x##size(sign) \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ - { \ - VEC_AVX512F_SUB_##bits##x##size(sign); \ - } \ - \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ - { \ - VEC_AVX512F_MUL_##bits##x##size(sign); \ - } \ - \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -277,7 +143,7 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -286,7 +152,7 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -295,51 +161,34 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_lshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ - { \ - VEC_AVX512F_LSHIFT_##bits##x##size(sign); \ - } \ + VEC_AVX512F_LSHIFT_##bits##x##size(sign); \ + VEC_AVX512F_##sign##RSHIFT_##bits##x##size(sign); \ + VEC_AVX512F_LRSHIFT_##bits##x##size(sign); \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_rshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ - { \ - VEC_AVX512F_##sign##RSHIFT_##bits##x##size(sign, a); \ - } \ - \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_lrshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ - { \ - VEC_AVX512F_RSHIFT_##bits##x##size(sign, l); \ - } \ + VEC_AVX512F_MINMAX_##sign##bits##x##size(min) \ + VEC_AVX512F_MINMAX_##sign##bits##x##size(max) \ \ const v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_avx512f = { \ - v##sign##int##bits##x##size##_generic_splat, \ - v##sign##int##bits##x##size##_avx512f_load_aligned, \ - v##sign##int##bits##x##size##_avx512f_load, \ - v##sign##int##bits##x##size##_avx512f_store_aligned, \ - v##sign##int##bits##x##size##_avx512f_store, \ - v##sign##int##bits##x##size##_avx512f_add, \ - v##sign##int##bits##x##size##_avx512f_sub, \ - v##sign##int##bits##x##size##_avx512f_mul, \ - v##sign##int##bits##x##size##_generic_div, \ - v##sign##int##bits##x##size##_generic_avg, \ - v##sign##int##bits##x##size##_avx512f_and, \ - v##sign##int##bits##x##size##_avx512f_or, \ - v##sign##int##bits##x##size##_avx512f_xor, \ - v##sign##int##bits##x##size##_generic_not, \ - v##sign##int##bits##x##size##_avx512f_lshift, \ - v##sign##int##bits##x##size##_avx512f_rshift, \ - v##sign##int##bits##x##size##_avx512f_lrshift, \ - v##sign##int##bits##x##size##_generic_cmplt, \ - v##sign##int##bits##x##size##_generic_cmple, \ - v##sign##int##bits##x##size##_generic_cmpeq, \ - v##sign##int##bits##x##size##_generic_cmpge, \ - v##sign##int##bits##x##size##_generic_cmpgt, \ + .load_aligned = v##sign##int##bits##x##size##_avx512f_load_aligned, \ + .load = v##sign##int##bits##x##size##_avx512f_load, \ + .store_aligned = v##sign##int##bits##x##size##_avx512f_store_aligned, \ + .store = v##sign##int##bits##x##size##_avx512f_store, \ + .add = VEC_AVX512F_STRUCT_OPERATION_##bits##x##size(add, sign), \ + .sub = VEC_AVX512F_STRUCT_OPERATION_##bits##x##size(sub, sign), \ + .mul = VEC_AVX512F_STRUCT_OPERATION_##bits##x##size(mul, sign), \ + .band = v##sign##int##bits##x##size##_avx512f_and, \ + .bor = v##sign##int##bits##x##size##_avx512f_or, \ + .bxor = v##sign##int##bits##x##size##_avx512f_xor, \ + .lshift = VEC_AVX512F_STRUCT_OPERATION_##bits##x##size(lshift, sign), \ + .rshift = VEC_AVX512F_STRUCT_OPERATION_##bits##x##size(rshift, sign), \ + .lrshift = VEC_AVX512F_STRUCT_OPERATION_##bits##x##size(lrshift, sign), \ + .min = VEC_AVX512F_STRUCT_MINMAX_##bits##x##size(min, sign), \ + .max = VEC_AVX512F_STRUCT_MINMAX_##bits##x##size(max, sign), \ }; #define VEC_AVX512F_DEFINE_OPERATIONS(bits, size) \ VEC_AVX512F_DEFINE_OPERATIONS_SIGN(u, bits, size) \ VEC_AVX512F_DEFINE_OPERATIONS_SIGN( , bits, size) -VEC_AVX512F_DEFINE_OPERATIONS(8, 64) -VEC_AVX512F_DEFINE_OPERATIONS(16, 32) VEC_AVX512F_DEFINE_OPERATIONS(32, 16) VEC_AVX512F_DEFINE_OPERATIONS(64, 8)
--- a/src/impl/x86/mmx.c Mon Nov 25 00:33:02 2024 -0500 +++ b/src/impl/x86/mmx.c Thu Apr 24 00:54:02 2025 -0400 @@ -24,46 +24,67 @@ #include "vec/vec.h" #include "vec/impl/x86/mmx.h" -#include "vec/impl/generic.h" #include <mmintrin.h> #include <string.h> -#define VEC_MMX_OPERATION_8x8(op, sign) \ - do { \ - /* unpack and multiply */ \ - union v##sign##int8x8_impl_data *vec1d = (union v##sign##int8x8_impl_data *)&vec1; \ - union v##sign##int8x8_impl_data *vec2d = (union v##sign##int8x8_impl_data *)&vec2; \ - \ - __m64 dst_even = _mm_##op##_pi16(vec1d->mmx, vec2d->mmx); \ - __m64 dst_odd = _mm_##op##_pi16(_mm_srli_pi16(vec1d->mmx, 8), _mm_srli_pi16(vec2d->mmx, 8)); \ - \ - /* repack */ \ - vec1d->mmx = _mm_or_si64( \ - _mm_slli_pi16(dst_odd, 8), \ - _mm_srli_pi16(_mm_slli_pi16(dst_even, 8), 8) \ - ); \ - return vec1d->vec; \ - } while (0) +/* ------------------------------------------------------------------------ */ -// shared between MMX variations -#define VEC_MMX_MUL_8x8(sign) \ - VEC_MMX_OPERATION_8x8(mullo, sign) - +#define VEC_MMX_MUL_8x8(sign) /* nothing */ #define VEC_MMX_MUL_16x4(sign) \ - do { \ + VEC_FUNC_IMPL v##sign##int16x4 v##sign##int16x4_mmx_mul(v##sign##int16x4 vec1, v##sign##int16x4 vec2) \ + { \ union v##sign##int16x4_impl_data *vec1d = (union v##sign##int16x4_impl_data *)&vec1; \ union vuint16x4_impl_data *vec2d = (union vuint16x4_impl_data *)&vec2; \ \ vec1d->mmx = _mm_mullo_pi16(vec1d->mmx, vec2d->mmx); \ return vec1d->vec; \ - } while (0) + } +#define VEC_MMX_MUL_32x2(sign) /* nothing */ + +#define VEC_MMX_STRUCT_MUL_8x8(sign) NULL +#define VEC_MMX_STRUCT_MUL_16x4(sign) v##sign##int16x4_mmx_mul +#define VEC_MMX_STRUCT_MUL_32x8(sign) NULL + +/* ------------------------------------------------------------------------ */ +/* comparison */ -#define VEC_MMX_MUL_32x2(sign) \ - /* TODO implement this for real */ \ - do { \ - return v##sign##int32x2_generic_mul(vec1, vec2); \ - } while (0) +/* helper funcs */ +#define VEC_xMMX_CMP(name, op, sign, bits, size, first, second, VARS, TRANS1, TRANS2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_mmx_##name(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ + VARS \ + \ + TRANS1 \ + \ + vec1d->mmx = _mm_##op##_pi##bits(vec##first##d->mmx, vec##second##d->mmx); \ + \ + TRANS2 \ + \ + return vec1d->vec; \ + } + +#define VEC_MMX_CMP(name, op, bits, size, first, second) \ + VEC_xMMX_CMP(name, op, /* nothing */, bits, size, first, second, /* nothing */, /* nothing */, /* nothing */) + +#define VEC_uMMX_CMP(name, op, bits, size, first, second) \ + VEC_xMMX_CMP(name, op, u, bits, size, first, second, \ + __m64 xor_val = _mm_set1_pi##bits(1u << (bits - 1)); \ + , { \ + vec1d->mmx = _mm_xor_si64(vec1d->mmx, xor_val); \ + vec2d->mmx = _mm_xor_si64(vec2d->mmx, xor_val); \ + }, \ + { \ + /* nothing */ \ + }) + +#define VEC_MMX_CMPEQ(sign, bits, size) VEC_xMMX_CMP(cmpeq, cmpeq, sign, bits, size, 1, 2, , ,) +#define VEC_MMX_CMPLT(sign, bits, size) VEC_##sign##MMX_CMP(cmplt, cmpgt, bits, size, 2, 1) +#define VEC_MMX_CMPGT(sign, bits, size) VEC_##sign##MMX_CMP(cmpgt, cmpgt, bits, size, 1, 2) + +/* ------------------------------------------------------------------------ */ #define VEC_MMX_DEFINE_OPERATIONS_SIGN(sign, bits, size) \ union v##sign##int##bits##x##size##_impl_data { \ @@ -74,19 +95,19 @@ VEC_STATIC_ASSERT(VEC_ALIGNOF(__m64) <= VEC_ALIGNOF(v##sign##int##bits##x##size), "vec: v" #sign "int" #bits "x" #size " alignment needs to be expanded to fit intrinsic type size"); \ VEC_STATIC_ASSERT(sizeof(__m64) <= sizeof(v##sign##int##bits##x##size), "vec: v" #sign "int" #bits "x" #size " needs to be expanded to fit intrinsic type size"); \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_mmx_load_aligned(const vec_##sign##int##bits in[size]) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_mmx_load_aligned(const vec_##sign##int##bits in[size]) \ { \ v##sign##int##bits##x##size vec; \ memcpy(&vec, in, sizeof(vec)); \ return vec; \ } \ \ - static void v##sign##int##bits##x##size##_mmx_store_aligned(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ + VEC_FUNC_IMPL void v##sign##int##bits##x##size##_mmx_store_aligned(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ { \ memcpy(out, &vec, sizeof(vec)); \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_mmx_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_mmx_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -96,7 +117,7 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_mmx_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_mmx_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -106,12 +127,9 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_mmx_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ - { \ - VEC_MMX_MUL_##bits##x##size(sign); \ - } \ + VEC_MMX_MUL_##bits##x##size(sign) \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_mmx_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_mmx_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -121,7 +139,7 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_mmx_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_mmx_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -131,7 +149,7 @@ return vec1d->vec; \ } \ \ - static v##sign##int##bits##x##size v##sign##int##bits##x##size##_mmx_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_mmx_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -141,29 +159,22 @@ return vec1d->vec; \ } \ \ + VEC_MMX_CMPEQ(sign, bits, size) \ + VEC_MMX_CMPLT(sign, bits, size) \ + VEC_MMX_CMPGT(sign, bits, size) \ + \ const v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_mmx = { \ - v##sign##int##bits##x##size##_generic_splat, \ - v##sign##int##bits##x##size##_mmx_load_aligned, \ - v##sign##int##bits##x##size##_mmx_load_aligned, \ - v##sign##int##bits##x##size##_mmx_store_aligned, \ - v##sign##int##bits##x##size##_mmx_store_aligned, \ - v##sign##int##bits##x##size##_mmx_add, \ - v##sign##int##bits##x##size##_mmx_sub, \ - v##sign##int##bits##x##size##_mmx_mul, \ - v##sign##int##bits##x##size##_generic_div, \ - v##sign##int##bits##x##size##_generic_avg, \ - v##sign##int##bits##x##size##_mmx_and, \ - v##sign##int##bits##x##size##_mmx_or, \ - v##sign##int##bits##x##size##_mmx_xor, \ - v##sign##int##bits##x##size##_generic_not, \ - v##sign##int##bits##x##size##_generic_lshift, \ - v##sign##int##bits##x##size##_generic_rshift, \ - v##sign##int##bits##x##size##_generic_lrshift, \ - v##sign##int##bits##x##size##_generic_cmplt, \ - v##sign##int##bits##x##size##_generic_cmple, \ - v##sign##int##bits##x##size##_generic_cmpeq, \ - v##sign##int##bits##x##size##_generic_cmpge, \ - v##sign##int##bits##x##size##_generic_cmpgt, \ + .load_aligned = v##sign##int##bits##x##size##_mmx_load_aligned, \ + .load = v##sign##int##bits##x##size##_mmx_load_aligned, \ + .store_aligned = v##sign##int##bits##x##size##_mmx_store_aligned, \ + .store = v##sign##int##bits##x##size##_mmx_store_aligned, \ + .add = v##sign##int##bits##x##size##_mmx_add, \ + .sub = v##sign##int##bits##x##size##_mmx_sub, \ + .mul = VEC_MMX_STRUCT_MUL_8x8(sign), \ + .band = v##sign##int##bits##x##size##_mmx_and, \ + .bor = v##sign##int##bits##x##size##_mmx_or, \ + .bxor = v##sign##int##bits##x##size##_mmx_xor, \ + .cmpeq = v##sign##int##bits##x##size##_mmx_cmpeq, \ }; #define VEC_MMX_DEFINE_OPERATIONS(bits, size) \
--- a/src/impl/x86/sse2.c Mon Nov 25 00:33:02 2024 -0500 +++ b/src/impl/x86/sse2.c Thu Apr 24 00:54:02 2025 -0400 @@ -23,10 +23,110 @@ **/ #include "vec/impl/x86/sse2.h" -#include "vec/impl/generic.h" #include <emmintrin.h> +#define VEC_SSE2_DEFINE_IMPL_DATA(sign, bits, size) \ + union v##sign##int##bits##x##size##_impl_data { \ + v##sign##int##bits##x##size vec; \ + __m128i sse; \ + }; \ + \ + VEC_STATIC_ASSERT(VEC_ALIGNOF(__m128i) <= VEC_ALIGNOF(v##sign##int##bits##x##size), "vec: v" #sign "int" #bits "x" #size " alignment needs to be expanded to fit intrinsic type size"); \ + VEC_STATIC_ASSERT(sizeof(__m128i) <= sizeof(v##sign##int##bits##x##size), "vec: v" #sign "int" #bits "x" #size " needs to be expanded to fit intrinsic type size"); + +VEC_SSE2_DEFINE_IMPL_DATA( , 8, 16) +VEC_SSE2_DEFINE_IMPL_DATA(u, 8, 16) +VEC_SSE2_DEFINE_IMPL_DATA( , 16, 8) +VEC_SSE2_DEFINE_IMPL_DATA(u, 16, 8) +VEC_SSE2_DEFINE_IMPL_DATA( , 32, 4) +VEC_SSE2_DEFINE_IMPL_DATA(u, 32, 4) +VEC_SSE2_DEFINE_IMPL_DATA( , 64, 2) +VEC_SSE2_DEFINE_IMPL_DATA(u, 64, 2) + +/* eh */ +#define MM_SET1_8(x) _mm_set1_epi8(x) +#define MM_SET1_16(x) _mm_set1_epi16(x) +#define MM_SET1_32(x) _mm_set1_epi32(x) +#define MM_SET1_64(x) _mm_set1_epi64x(x) + +/* ------------------------------------------------------------------------ */ + +/* unfortunately doing this for SSE2 is PREPROCESSOR HELL */ +#define VEC_SSE2_MINMAX_8x16_TEMPLATE(SIGN, OP, VALS, ADDITIONAL1, ADDITIONAL2) \ + VEC_FUNC_IMPL v##SIGN##int8x16 v##SIGN##int8x16_sse2_##OP(v##SIGN##int8x16 vec1, v##SIGN##int8x16 vec2) \ + { \ + union v##SIGN##int8x16_impl_data *vec1d = (union v##SIGN##int8x16_impl_data *)&vec1; \ + union v##SIGN##int8x16_impl_data *vec2d = (union v##SIGN##int8x16_impl_data *)&vec2; \ + VALS \ + \ + ADDITIONAL1 \ + \ + vec1d->sse = _mm_##OP##_epu8(vec1d->sse, vec2d->sse); \ + \ + ADDITIONAL2 \ + \ + return vec1d->vec; \ + } + +/* conveniently, this looks like K&R C ;) */ +#define VEC_SSE2_MINMAX_8x16(OP) \ + VEC_SSE2_MINMAX_8x16_TEMPLATE(/* nothing */, OP, \ + __m128i xor_val = _mm_set1_epi8(0x80u); \ + , { \ + vec1d->sse = _mm_xor_si128(vec1d->sse, xor_val); \ + vec2d->sse = _mm_xor_si128(vec2d->sse, xor_val); \ + }, \ + { \ + vec1d->sse = _mm_xor_si128(vec1d->sse, xor_val); \ + }) + +#define VEC_SSE2_MINMAX_u8x16(OP) \ + VEC_SSE2_MINMAX_8x16_TEMPLATE(u, OP, /* nothing */, /* nothing */, /* nothing */) + +#define VEC_SSE2_MINMAX_16x8_TEMPLATE(SIGN, OP, VALS, ADDITIONAL1, ADDITIONAL2) \ + VEC_FUNC_IMPL v##SIGN##int16x8 v##SIGN##int16x8_sse2_##OP(v##SIGN##int16x8 vec1, v##SIGN##int16x8 vec2) \ + { \ + union v##SIGN##int16x8_impl_data *vec1d = (union v##SIGN##int16x8_impl_data *)&vec1; \ + union v##SIGN##int16x8_impl_data *vec2d = (union v##SIGN##int16x8_impl_data *)&vec2; \ + VALS \ + \ + ADDITIONAL1 \ + \ + vec1d->sse = _mm_##OP##_epi16(vec1d->sse, vec2d->sse); \ + \ + ADDITIONAL2 \ + \ + return vec1d->vec; \ + } + +#define VEC_SSE2_MINMAX_16x8(OP) \ + VEC_SSE2_MINMAX_16x8_TEMPLATE(/* nothing */, OP, /* nothing */, /* nothing */, /* nothing */) + +#define VEC_SSE2_MINMAX_u16x8(OP) \ + VEC_SSE2_MINMAX_16x8_TEMPLATE(u, OP, \ + __m128i xor_val = _mm_set1_epi16(0x8000u); \ + , { \ + vec1d->sse = _mm_xor_si128(vec1d->sse, xor_val); \ + vec2d->sse = _mm_xor_si128(vec2d->sse, xor_val); \ + }, \ + { \ + vec1d->sse = _mm_xor_si128(vec1d->sse, xor_val); \ + }) + +#define VEC_SSE2_MINMAX_32x4(OP) /* none */ +#define VEC_SSE2_MINMAX_64x2(OP) /* none */ +#define VEC_SSE2_MINMAX_u32x4(OP) /* none */ +#define VEC_SSE2_MINMAX_u64x2(OP) /* none */ + +#define VEC_SSE2_STRUCT_MINMAX_8x16(OP, SIGN) v##SIGN##int8x16_sse2_##OP +#define VEC_SSE2_STRUCT_MINMAX_16x8(OP, SIGN) v##SIGN##int16x8_sse2_##OP +#define VEC_SSE2_STRUCT_MINMAX_32x4(OP, SIGN) NULL +#define VEC_SSE2_STRUCT_MINMAX_64x2(OP, SIGN) NULL + +/* ------------------------------------------------------------------------ */ +/* multiplication */ + #define VEC_SSE2_OPERATION_8x16(op, sign) \ do { \ /* unpack and multiply */ \ @@ -92,45 +192,58 @@ return vec1d->vec; \ } while (0) -#define VEC_SSE2_CMPEQ_8x16(sign) \ - do { \ - union v##sign##int8x16_impl_data *vec1d = (union v##sign##int8x16_impl_data *)&vec1; \ - union v##sign##int8x16_impl_data *vec2d = (union v##sign##int8x16_impl_data *)&vec2; \ - \ - vec1d->sse = _mm_cmpeq_epi8(vec1d->sse, vec2d->sse); \ - return vec1d->vec; \ - } while (0) +/* ------------------------------------------------------------------------ */ +/* comparison */ -#define VEC_SSE2_CMPEQ_16x8(sign) \ - do { \ - union v##sign##int16x8_impl_data *vec1d = (union v##sign##int16x8_impl_data *)&vec1; \ - union v##sign##int16x8_impl_data *vec2d = (union v##sign##int16x8_impl_data *)&vec2; \ +/* helper funcs */ +#define VEC_xSSE2_CMP(name, op, sign, bits, size, first, second, VARS, TRANS1, TRANS2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_##name(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ + VARS \ + \ + TRANS1 \ \ - vec1d->sse = _mm_cmpeq_epi16(vec1d->sse, vec2d->sse); \ + vec1d->sse = _mm_##op##_epi##bits(vec##first##d->sse, vec##second##d->sse); \ + \ + TRANS2 \ + \ return vec1d->vec; \ - } while (0) + } + +#define VEC_SSE2_CMP(name, op, bits, size, first, second) \ + VEC_xSSE2_CMP(name, op, /* nothing */, bits, size, first, second, /* nothing */, /* nothing */, /* nothing */) -#define VEC_SSE2_CMPEQ_32x4(sign) \ - do { \ - union v##sign##int32x4_impl_data *vec1d = (union v##sign##int32x4_impl_data *)&vec1; \ - union v##sign##int32x4_impl_data *vec2d = (union v##sign##int32x4_impl_data *)&vec2; \ - \ - vec1d->sse = _mm_cmpeq_epi32(vec1d->sse, vec2d->sse); \ - return vec1d->vec; \ - } while (0) +#define VEC_uSSE2_CMP(name, op, bits, size, first, second) \ + VEC_xSSE2_CMP(name, op, u, bits, size, first, second, \ + __m128i xor_val = MM_SET1_##bits(1u << (bits - 1)); \ + , { \ + vec1d->sse = _mm_xor_si128(vec1d->sse, xor_val); \ + vec2d->sse = _mm_xor_si128(vec2d->sse, xor_val); \ + }, \ + { \ + /* nothing */ \ + }) -// SSE2 doesn't have an intrinsic for 64x2 equality comparison, -// so how can we take a 32x4 comparison result and turn it into -// a 64x2 comparison result? -// -// well, Intel conveniently provided an operation where we can -// shuffle around 32-bit integers (_mm_shuffle_epi32). -// -// this means all we have to do is simply do the 32-bit operation, -// shuffle the parts, and then return a bitwise AND of the result. +/* these are the same for unsigned and signed, for obvious reasons. */ +#define VEC_SSE2_CMPEQ_8x16(sign) VEC_xSSE2_CMP(cmpeq, cmpeq, sign, 8, 16, 1, 2, , ,) +#define VEC_SSE2_CMPEQ_16x8(sign) VEC_xSSE2_CMP(cmpeq, cmpeq, sign, 16, 8, 1, 2, , ,) +#define VEC_SSE2_CMPEQ_32x4(sign) VEC_xSSE2_CMP(cmpeq, cmpeq, sign, 32, 4, 1, 2, , ,) + +/* SSE2 doesn't have an intrinsic for 64x2 equality comparison, + * so how can we take a 32x4 comparison result and turn it into + * a 64x2 comparison result? + * + * well, Intel conveniently provided an operation where we can + * shuffle around 32-bit integers (_mm_shuffle_epi32). + * + * this means all we have to do is simply do the 32-bit operation, + * shuffle the parts, and then return a bitwise AND of the result. */ #define VEC_SSE2_CMPEQ_64x2(sign) \ - do { \ + VEC_FUNC_IMPL v##sign##int64x2 v##sign##int64x2_sse2_cmpeq(v##sign##int64x2 vec1, v##sign##int64x2 vec2) \ + { \ union v##sign##int64x2_impl_data *vec1d = (union v##sign##int64x2_impl_data *)&vec1; \ union v##sign##int64x2_impl_data *vec2d = (union v##sign##int64x2_impl_data *)&vec2; \ \ @@ -140,42 +253,60 @@ vec1d->sse = _mm_and_si128(vec1d->sse, vec2d->sse); \ \ return vec1d->vec; \ - } while (0) + } + +/* ------------------------------------------------------------------------ */ + +#define VEC_SSE2_CMPLT_8x16(sign) VEC_##sign##SSE2_CMP(cmplt, cmpgt, 8, 16, 2, 1) +#define VEC_SSE2_CMPLT_16x8(sign) VEC_##sign##SSE2_CMP(cmplt, cmpgt, 16, 8, 2, 1) +#define VEC_SSE2_CMPLT_32x4(sign) VEC_##sign##SSE2_CMP(cmplt, cmpgt, 32, 4, 2, 1) +#define VEC_SSE2_CMPLT_64x2(sign) /* nothing */ + +#define VEC_SSE2_CMPGT_8x16(sign) VEC_##sign##SSE2_CMP(cmpgt, cmpgt, 8, 16, 1, 2) +#define VEC_SSE2_CMPGT_16x8(sign) VEC_##sign##SSE2_CMP(cmpgt, cmpgt, 16, 8, 1, 2) +#define VEC_SSE2_CMPGT_32x4(sign) VEC_##sign##SSE2_CMP(cmpgt, cmpgt, 32, 4, 1, 2) +#define VEC_SSE2_CMPGT_64x2(sign) /* nothing */ + +#define VEC_SSE2_STRUCT_CMP_8x16(name, sign) v##sign##int8x16_sse2_##name +#define VEC_SSE2_STRUCT_CMP_16x8(name, sign) v##sign##int16x8_sse2_##name +#define VEC_SSE2_STRUCT_CMP_32x4(name, sign) v##sign##int32x4_sse2_##name +#define VEC_SSE2_STRUCT_CMP_64x2(name, sign) NULL + +/* ------------------------------------------------------------------------ */ #define VEC_SSE2_DEFINE_OPERATIONS_SIGN(sign, bits, size) \ - union v##sign##int##bits##x##size##_impl_data { \ - v##sign##int##bits##x##size vec; \ - __m128i sse; \ - }; \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_splat(vec_##sign##int##bits x) \ + { \ + union v##sign##int##bits##x##size##_impl_data vec; \ + vec.sse = MM_SET1_##bits(x); \ + return vec.vec; \ + } \ \ - VEC_STATIC_ASSERT(VEC_ALIGNOF(__m128i) <= VEC_ALIGNOF(v##sign##int##bits##x##size), "vec: v" #sign "int" #bits "x" #size " alignment needs to be expanded to fit intrinsic type size"); \ - VEC_STATIC_ASSERT(sizeof(__m128i) <= sizeof(v##sign##int##bits##x##size), "vec: v" #sign "int" #bits "x" #size " needs to be expanded to fit intrinsic type size"); \ - \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_load_aligned(const vec_##sign##int##bits in[size]) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_load_aligned(const vec_##sign##int##bits in[size]) \ { \ union v##sign##int##bits##x##size##_impl_data vec; \ vec.sse = _mm_load_si128((const __m128i *)in); \ return vec.vec; \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_load(const vec_##sign##int##bits in[size]) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_load(const vec_##sign##int##bits in[size]) \ { \ union v##sign##int##bits##x##size##_impl_data vec; \ vec.sse = _mm_loadu_si128((const __m128i *)in); \ return vec.vec; \ } \ \ - void v##sign##int##bits##x##size##_sse2_store_aligned(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ + VEC_FUNC_IMPL void v##sign##int##bits##x##size##_sse2_store_aligned(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ { \ _mm_store_si128((__m128i *)out, ((union v##sign##int##bits##x##size##_impl_data *)&vec)->sse); \ } \ \ - void v##sign##int##bits##x##size##_sse2_store(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ + VEC_FUNC_IMPL void v##sign##int##bits##x##size##_sse2_store(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ { \ _mm_storeu_si128((__m128i *)out, ((union v##sign##int##bits##x##size##_impl_data *)&vec)->sse); \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -184,7 +315,7 @@ return vec1d->vec; \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -193,12 +324,12 @@ return vec1d->vec; \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ VEC_SSE2_MUL_##bits##x##size(sign); \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -207,7 +338,7 @@ return vec1d->vec; \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -216,7 +347,7 @@ return vec1d->vec; \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ { \ union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ @@ -225,34 +356,30 @@ return vec1d->vec; \ } \ \ - v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_cmpeq(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ - { \ - VEC_SSE2_CMPEQ_##bits##x##size(sign); \ - } \ + VEC_SSE2_CMPEQ_##bits##x##size(sign); \ + VEC_SSE2_CMPLT_##bits##x##size(sign); \ + VEC_SSE2_CMPGT_##bits##x##size(sign); \ + \ + VEC_SSE2_MINMAX_##sign##bits##x##size(min) \ + VEC_SSE2_MINMAX_##sign##bits##x##size(max) \ \ const v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_sse2 = { \ - v##sign##int##bits##x##size##_generic_splat, \ - v##sign##int##bits##x##size##_sse2_load_aligned, \ - v##sign##int##bits##x##size##_sse2_load, \ - v##sign##int##bits##x##size##_sse2_store_aligned, \ - v##sign##int##bits##x##size##_sse2_store, \ - v##sign##int##bits##x##size##_sse2_add, \ - v##sign##int##bits##x##size##_sse2_sub, \ - v##sign##int##bits##x##size##_sse2_mul, \ - v##sign##int##bits##x##size##_generic_div, \ - v##sign##int##bits##x##size##_generic_avg, \ - v##sign##int##bits##x##size##_sse2_and, \ - v##sign##int##bits##x##size##_sse2_or, \ - v##sign##int##bits##x##size##_sse2_xor, \ - v##sign##int##bits##x##size##_generic_not, \ - v##sign##int##bits##x##size##_generic_lshift, \ - v##sign##int##bits##x##size##_generic_rshift, \ - v##sign##int##bits##x##size##_generic_lrshift, \ - v##sign##int##bits##x##size##_generic_cmplt, \ - v##sign##int##bits##x##size##_generic_cmple, \ - v##sign##int##bits##x##size##_sse2_cmpeq, \ - v##sign##int##bits##x##size##_generic_cmpge, \ - v##sign##int##bits##x##size##_generic_cmpgt, \ + .splat = v##sign##int##bits##x##size##_sse2_splat, \ + .load_aligned = v##sign##int##bits##x##size##_sse2_load_aligned, \ + .load = v##sign##int##bits##x##size##_sse2_load, \ + .store_aligned = v##sign##int##bits##x##size##_sse2_store_aligned, \ + .store = v##sign##int##bits##x##size##_sse2_store, \ + .add = v##sign##int##bits##x##size##_sse2_add, \ + .sub = v##sign##int##bits##x##size##_sse2_sub, \ + .mul = v##sign##int##bits##x##size##_sse2_mul, \ + .band = v##sign##int##bits##x##size##_sse2_and, \ + .bor = v##sign##int##bits##x##size##_sse2_or, \ + .bxor = v##sign##int##bits##x##size##_sse2_xor, \ + .cmpeq = v##sign##int##bits##x##size##_sse2_cmpeq, \ + .cmplt = VEC_SSE2_STRUCT_CMP_##bits##x##size(cmplt, sign), \ + .cmpgt = VEC_SSE2_STRUCT_CMP_##bits##x##size(cmpgt, sign), \ + .min = VEC_SSE2_STRUCT_MINMAX_##bits##x##size(min, sign), \ + .max = VEC_SSE2_STRUCT_MINMAX_##bits##x##size(max, sign), \ }; #define VEC_SSE2_DEFINE_OPERATIONS(bits, size) \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/impl/x86/sse3.c Thu Apr 24 00:54:02 2025 -0400 @@ -0,0 +1,58 @@ +/** + * vec - a tiny SIMD vector library in C99 + * + * Copyright (c) 2024 Paper + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +**/ + +#include "vec/impl/x86/sse3.h" + +#include <pmmintrin.h> + +/* SSE3 has a slightly more optimized load function */ + +#define VEC_SSE2_DEFINE_OPERATIONS_SIGN(sign, bits, size) \ + union v##sign##int##bits##x##size##_impl_data { \ + v##sign##int##bits##x##size vec; \ + __m128i sse; \ + }; \ + \ + VEC_STATIC_ASSERT(VEC_ALIGNOF(__m128i) <= VEC_ALIGNOF(v##sign##int##bits##x##size), "vec: v" #sign "int" #bits "x" #size " alignment needs to be expanded to fit intrinsic type size"); \ + VEC_STATIC_ASSERT(sizeof(__m128i) <= sizeof(v##sign##int##bits##x##size), "vec: v" #sign "int" #bits "x" #size " needs to be expanded to fit intrinsic type size"); \ + \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_load(const vec_##sign##int##bits in[size]) \ + { \ + union v##sign##int##bits##x##size##_impl_data vec; \ + vec.sse = _mm_lddqu_si128((const __m128i *)in); \ + return vec.vec; \ + } \ + \ + const v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_sse3 = { \ + .load = v##sign##int##bits##x##size##_sse2_load, \ + }; + +#define VEC_SSE2_DEFINE_OPERATIONS(bits, size) \ + VEC_SSE2_DEFINE_OPERATIONS_SIGN(u, bits, size) \ + VEC_SSE2_DEFINE_OPERATIONS_SIGN( , bits, size) + +VEC_SSE2_DEFINE_OPERATIONS(8, 16) +VEC_SSE2_DEFINE_OPERATIONS(16, 8) +VEC_SSE2_DEFINE_OPERATIONS(32, 4) +VEC_SSE2_DEFINE_OPERATIONS(64, 2)
--- a/src/impl/x86/sse41.c Mon Nov 25 00:33:02 2024 -0500 +++ b/src/impl/x86/sse41.c Thu Apr 24 00:54:02 2025 -0400 @@ -23,54 +23,138 @@ **/ #include "vec/impl/x86/sse41.h" -#include "vec/impl/x86/sse2.h" #include "vec/impl/generic.h" #include <immintrin.h> -// SSE 4.1 provides a real _mm_mullo_epi32 -#define VEC_SSE41_DEFINE_OPERATIONS(sign) \ - union v##sign##int32x4_impl_data { \ - v##sign##int32x4 vec; \ - __m128i sse; \ - }; \ +/* ------------------------------------------------------------------------ */ + +#define VEC_SSE41_MINMAX_TEMPLATE(SIGN, BITS, SIZE, INTLSIGN, OP) \ + VEC_FUNC_IMPL v##SIGN##int##BITS##x##SIZE v##SIGN##int##BITS##x##SIZE##_sse41_##OP(v##SIGN##int##BITS##x##SIZE vec1, v##SIGN##int##BITS##x##SIZE vec2) \ + { \ + union v##SIGN##int##BITS##x##SIZE##_impl_data *vec1d = (union v##SIGN##int##BITS##x##SIZE##_impl_data *)&vec1; \ + union v##SIGN##int##BITS##x##SIZE##_impl_data *vec2d = (union v##SIGN##int##BITS##x##SIZE##_impl_data *)&vec2; \ + \ + vec1d->sse = _mm_##OP##_ep##INTLSIGN##BITS(vec1d->sse, vec2d->sse); \ \ - VEC_STATIC_ASSERT(VEC_ALIGNOF(__m128i) <= VEC_ALIGNOF(v##sign##int32x4), "vec: v" #sign "int32x4 alignment needs to be expanded to fit intrinsic type size"); \ - VEC_STATIC_ASSERT(sizeof(__m128i) <= sizeof(v##sign##int32x4), "vec: v" #sign "int32x4 needs to be expanded to fit intrinsic type size"); \ - \ - static v##sign##int32x4 v##sign##int32x4_sse41_mul(v##sign##int32x4 vec1, v##sign##int32x4 vec2) \ + return vec1d->vec; \ + } + +#define VEC_SSE41_MINMAX_8x16(OP) VEC_SSE41_MINMAX_TEMPLATE( , 8, 16, i, OP) +#define VEC_SSE41_MINMAX_u8x16(OP) VEC_SSE41_MINMAX_TEMPLATE(u, 8, 16, u, OP) +#define VEC_SSE41_MINMAX_16x8(OP) VEC_SSE41_MINMAX_TEMPLATE( , 16, 8, i, OP) +#define VEC_SSE41_MINMAX_u16x8(OP) VEC_SSE41_MINMAX_TEMPLATE(u, 16, 8, u, OP) +#define VEC_SSE41_MINMAX_32x4(OP) VEC_SSE41_MINMAX_TEMPLATE( , 32, 4, i, OP) +#define VEC_SSE41_MINMAX_u32x4(OP) VEC_SSE41_MINMAX_TEMPLATE(u, 32, 4, u, OP) +#define VEC_SSE41_MINMAX_64x2(OP) /* nothing */ +#define VEC_SSE41_MINMAX_u64x2(OP) /* nothing */ + +#define VEC_SSE41_STRUCT_MINMAX_8x16(OP, SIGN) v##SIGN##int8x16_sse41_##OP +#define VEC_SSE41_STRUCT_MINMAX_16x8(OP, SIGN) v##SIGN##int16x8_sse41_##OP +#define VEC_SSE41_STRUCT_MINMAX_32x4(OP, SIGN) v##SIGN##int32x4_sse41_##OP +#define VEC_SSE41_STRUCT_MINMAX_64x2(OP, SIGN) NULL + +/* ------------------------------------------------------------------------ */ +/* multiplication */ + +#define VEC_SSE41_MUL_8x16(sign) +#define VEC_SSE41_MUL_16x8(sign) +#define VEC_SSE41_MUL_32x4(sign) \ + VEC_FUNC_IMPL v##sign##int32x4 v##sign##int32x4_sse41_mul(v##sign##int32x4 vec1, v##sign##int32x4 vec2) \ { \ union v##sign##int32x4_impl_data *vec1d = (union v##sign##int32x4_impl_data *)&vec1; \ union v##sign##int32x4_impl_data *vec2d = (union v##sign##int32x4_impl_data *)&vec2; \ \ vec1d->sse = _mm_mullo_epi32(vec1d->sse, vec2d->sse); \ return vec1d->vec; \ - } \ + } +#define VEC_SSE41_MUL_64x2(sign) + +#define VEC_SSE41_STRUCT_MUL_8x16(SIGN) NULL +#define VEC_SSE41_STRUCT_MUL_16x8(SIGN) NULL +#define VEC_SSE41_STRUCT_MUL_32x4(SIGN) v##SIGN##int32x4_sse41_mul +#define VEC_SSE41_STRUCT_MUL_64x2(SIGN) NULL + +/* ------------------------------------------------------------------------ */ +/* comparison */ + +#define MM_SET1_64(x) _mm_set1_epi64x(x) + +/* helper funcs */ +#define VEC_xSSE41_CMP(name, op, sign, bits, size, first, second, VARS, TRANS1, TRANS2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse41_##name(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ + VARS \ + \ + TRANS1 \ + \ + vec1d->sse = _mm_##op##_epi##bits(vec##first##d->sse, vec##second##d->sse); \ + \ + TRANS2 \ \ - const v##sign##int32x4_impl v##sign##int32x4_impl_sse41 = { \ - v##sign##int32x4_generic_splat, \ - v##sign##int32x4_sse2_load_aligned, \ - v##sign##int32x4_sse2_load, \ - v##sign##int32x4_sse2_store_aligned, \ - v##sign##int32x4_sse2_store, \ - v##sign##int32x4_sse2_add, \ - v##sign##int32x4_sse2_sub, \ - v##sign##int32x4_sse41_mul, \ - v##sign##int32x4_generic_div, \ - v##sign##int32x4_generic_avg, \ - v##sign##int32x4_sse2_and, \ - v##sign##int32x4_sse2_or, \ - v##sign##int32x4_sse2_xor, \ - v##sign##int32x4_generic_not, \ - v##sign##int32x4_generic_lshift, \ - v##sign##int32x4_generic_rshift, \ - v##sign##int32x4_generic_lrshift, \ - v##sign##int32x4_generic_cmplt, \ - v##sign##int32x4_generic_cmple, \ - v##sign##int32x4_sse2_cmpeq, \ - v##sign##int32x4_generic_cmpge, \ - v##sign##int32x4_generic_cmpgt, \ + return vec1d->vec; \ + } + +#define VEC_SSE41_CMP(name, op, bits, size, first, second) \ + VEC_xSSE41_CMP(name, op, /* nothing */, bits, size, first, second, /* nothing */, /* nothing */, /* nothing */) + +#define VEC_uSSE41_CMP(name, op, bits, size, first, second) \ + VEC_xSSE41_CMP(name, op, u, bits, size, first, second, \ + __m128i xor_val = MM_SET1_##bits(UINT64_C(1) << (bits - 1)); \ + , { \ + vec1d->sse = _mm_xor_si128(vec1d->sse, xor_val); \ + vec2d->sse = _mm_xor_si128(vec2d->sse, xor_val); \ + }, \ + { \ + /* nothing */ \ + }) + +/* these are the same for unsigned and signed, for obvious reasons. */ +#define VEC_SSE41_CMPEQ_8x16(sign) /* nothing */ +#define VEC_SSE41_CMPEQ_16x8(sign) /* nothing */ +#define VEC_SSE41_CMPEQ_32x4(sign) /* nothing */ +#define VEC_SSE41_CMPEQ_64x2(sign) VEC_xSSE41_CMP(cmpeq, cmpeq, sign, 64, 2, 1, 2, , ,) + +/* ------------------------------------------------------------------------ */ + +#define VEC_SSE41_STRUCT_CMP_8x16(name, sign) NULL +#define VEC_SSE41_STRUCT_CMP_16x8(name, sign) NULL +#define VEC_SSE41_STRUCT_CMP_32x4(name, sign) NULL +#define VEC_SSE41_STRUCT_CMP_64x2(name, sign) v##sign##int64x2_sse41_##name + +/* ------------------------------------------------------------------------ */ + +// SSE 4.1 provides a real _mm_mullo_epi32 +#define VEC_SSE41_DEFINE_OPERATIONS_SIGN(sign, bits, size) \ + union v##sign##int##bits##x##size##_impl_data { \ + v##sign##int##bits##x##size vec; \ + __m128i sse; \ + }; \ + \ + VEC_STATIC_ASSERT(VEC_ALIGNOF(__m128i) <= VEC_ALIGNOF(v##sign##int##bits##x##size), "vec: v" #sign "int32x4 alignment needs to be expanded to fit intrinsic type size"); \ + VEC_STATIC_ASSERT(sizeof(__m128i) <= sizeof(v##sign##int##bits##x##size), "vec: v" #sign "int32x4 needs to be expanded to fit intrinsic type size"); \ + \ + VEC_SSE41_MUL_##bits##x##size(sign) \ + \ + VEC_SSE41_MINMAX_##sign##bits##x##size(min) \ + VEC_SSE41_MINMAX_##sign##bits##x##size(max) \ + \ + VEC_SSE41_CMPEQ_##bits##x##size(sign); \ + \ + const v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_sse41 = { \ + .mul = VEC_SSE41_STRUCT_MUL_##bits##x##size(sign), \ + .min = VEC_SSE41_STRUCT_MINMAX_##bits##x##size(min, sign), \ + .max = VEC_SSE41_STRUCT_MINMAX_##bits##x##size(max, sign), \ + .cmpeq = VEC_SSE41_STRUCT_CMP_##bits##x##size(cmpeq, sign), \ }; -VEC_SSE41_DEFINE_OPERATIONS() -VEC_SSE41_DEFINE_OPERATIONS(u) +#define VEC_SSE41_DEFINE_OPERATIONS(bits, size) \ + VEC_SSE41_DEFINE_OPERATIONS_SIGN(u, bits, size) \ + VEC_SSE41_DEFINE_OPERATIONS_SIGN( , bits, size) + +VEC_SSE41_DEFINE_OPERATIONS(8, 16) +VEC_SSE41_DEFINE_OPERATIONS(16, 8) +VEC_SSE41_DEFINE_OPERATIONS(32, 4) +VEC_SSE41_DEFINE_OPERATIONS(64, 2)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/impl/x86/sse42.c Thu Apr 24 00:54:02 2025 -0400 @@ -0,0 +1,100 @@ +/** + * vec - a tiny SIMD vector library in C99 + * + * Copyright (c) 2024 Paper + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +**/ + +#include "vec/impl/x86/sse42.h" +#include "vec/impl/generic.h" + +#include <immintrin.h> + +/* ------------------------------------------------------------------------ */ +/* comparison */ + +#define MM_SET1_64(x) _mm_set1_epi64x(x) + +/* helper funcs */ +#define VEC_xSSE42_CMP(name, op, sign, bits, size, first, second, VARS, TRANS1, TRANS2) \ + VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse42_##name(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ + { \ + union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ + union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ + VARS \ + \ + TRANS1 \ + \ + vec1d->sse = _mm_##op##_epi##bits(vec##first##d->sse, vec##second##d->sse); \ + \ + TRANS2 \ + \ + return vec1d->vec; \ + } + +#define VEC_SSE42_CMP(name, op, bits, size, first, second) \ + VEC_xSSE42_CMP(name, op, /* nothing */, bits, size, first, second, /* nothing */, /* nothing */, /* nothing */) + +#define VEC_uSSE42_CMP(name, op, bits, size, first, second) \ + VEC_xSSE42_CMP(name, op, u, bits, size, first, second, \ + __m128i xor_val = MM_SET1_##bits(UINT64_C(1) << (bits - 1)); \ + , { \ + vec1d->sse = _mm_xor_si128(vec1d->sse, xor_val); \ + vec2d->sse = _mm_xor_si128(vec2d->sse, xor_val); \ + }, \ + { \ + /* nothing */ \ + }) + +/* these are the same for unsigned and signed, for obvious reasons. */ +#define VEC_SSE42_CMPEQ_64x2(sign) VEC_xSSE42_CMP(cmpeq, cmpeq, sign, 64, 2, 1, 2, , ,) + +/* ------------------------------------------------------------------------ */ + +#define VEC_SSE42_CMPLT_64x2(sign) VEC_##sign##SSE42_CMP(cmplt, cmpgt, 64, 2, 2, 1) +#define VEC_SSE42_CMPGT_64x2(sign) VEC_##sign##SSE42_CMP(cmpgt, cmpgt, 64, 2, 1, 2) + +#define VEC_SSE42_STRUCT_CMP_64x2(name, sign) v##sign##int64x2_sse42_##name + +/* ------------------------------------------------------------------------ */ + +// SSE 4.1 provides a real _mm_mullo_epi32 +#define VEC_SSE42_DEFINE_OPERATIONS_SIGN(sign, bits, size) \ + union v##sign##int##bits##x##size##_impl_data { \ + v##sign##int##bits##x##size vec; \ + __m128i sse; \ + }; \ + \ + VEC_STATIC_ASSERT(VEC_ALIGNOF(__m128i) <= VEC_ALIGNOF(v##sign##int##bits##x##size), "vec: v" #sign "int32x4 alignment needs to be expanded to fit intrinsic type size"); \ + VEC_STATIC_ASSERT(sizeof(__m128i) <= sizeof(v##sign##int##bits##x##size), "vec: v" #sign "int32x4 needs to be expanded to fit intrinsic type size"); \ + \ + VEC_SSE42_CMPLT_##bits##x##size(sign); \ + VEC_SSE42_CMPGT_##bits##x##size(sign); \ + \ + const v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_sse42 = { \ + .cmplt = VEC_SSE42_STRUCT_CMP_##bits##x##size(cmplt, sign), \ + .cmpgt = VEC_SSE42_STRUCT_CMP_##bits##x##size(cmpgt, sign), \ + }; + +#define VEC_SSE42_DEFINE_OPERATIONS(bits, size) \ + VEC_SSE42_DEFINE_OPERATIONS_SIGN(u, bits, size) \ + VEC_SSE42_DEFINE_OPERATIONS_SIGN( , bits, size) + +VEC_SSE42_DEFINE_OPERATIONS(64, 2)
--- a/src/vec.c Mon Nov 25 00:33:02 2024 -0500 +++ b/src/vec.c Thu Apr 24 00:54:02 2025 -0400 @@ -32,15 +32,27 @@ #ifdef VEC_COMPILER_HAS_SSE2 # include "vec/impl/x86/sse2.h" #endif +#ifdef VEC_COMPILER_HAS_SSE3 +# include "vec/impl/x86/sse3.h" +#endif #ifdef VEC_COMPILER_HAS_SSE41 # include "vec/impl/x86/sse41.h" #endif +#ifdef VEC_COMPILER_HAS_SSE42 +# include "vec/impl/x86/sse42.h" +#endif #ifdef VEC_COMPILER_HAS_AVX2 # include "vec/impl/x86/avx2.h" #endif #ifdef VEC_COMPILER_HAS_AVX512F # include "vec/impl/x86/avx512f.h" #endif +#ifdef VEC_COMPILER_HAS_AVX512BW +# include "vec/impl/x86/avx512bw.h" +#endif +#ifdef VEC_COMPILER_HAS_AVX512DQ +# include "vec/impl/x86/avx512dq.h" +#endif #ifdef VEC_COMPILER_HAS_ALTIVEC # include "vec/impl/ppc/altivec.h" #endif @@ -59,166 +71,284 @@ extern inline vec_uintmax vec_uavg(vec_uintmax x, vec_uintmax y); // 16-bit -const vint8x2_impl *vint8x2_impl_cpu = &vint8x2_impl_generic; -const vuint8x2_impl *vuint8x2_impl_cpu = &vuint8x2_impl_generic; +vint8x2_impl vint8x2_impl_cpu = {0}; +vuint8x2_impl vuint8x2_impl_cpu = {0}; // 32-bit -const vint8x4_impl *vint8x4_impl_cpu = &vint8x4_impl_generic; -const vuint8x4_impl *vuint8x4_impl_cpu = &vuint8x4_impl_generic; -const vint16x2_impl *vint16x2_impl_cpu = &vint16x2_impl_generic; -const vuint16x2_impl *vuint16x2_impl_cpu = &vuint16x2_impl_generic; +vint8x4_impl vint8x4_impl_cpu = {0}; +vuint8x4_impl vuint8x4_impl_cpu = {0}; +vint16x2_impl vint16x2_impl_cpu = {0}; +vuint16x2_impl vuint16x2_impl_cpu = {0}; // 64-bit -const vint8x8_impl *vint8x8_impl_cpu = &vint8x8_impl_generic; -const vuint8x8_impl *vuint8x8_impl_cpu = &vuint8x8_impl_generic; -const vint16x4_impl *vint16x4_impl_cpu = &vint16x4_impl_generic; -const vuint16x4_impl *vuint16x4_impl_cpu = &vuint16x4_impl_generic; -const vint32x2_impl *vint32x2_impl_cpu = &vint32x2_impl_generic; -const vuint32x2_impl *vuint32x2_impl_cpu = &vuint32x2_impl_generic; +vint8x8_impl vint8x8_impl_cpu = {0}; +vuint8x8_impl vuint8x8_impl_cpu = {0}; +vint16x4_impl vint16x4_impl_cpu = {0}; +vuint16x4_impl vuint16x4_impl_cpu = {0}; +vint32x2_impl vint32x2_impl_cpu = {0}; +vuint32x2_impl vuint32x2_impl_cpu = {0}; // 128-bit -const vint8x16_impl *vint8x16_impl_cpu = &vint8x16_impl_generic; -const vuint8x16_impl *vuint8x16_impl_cpu = &vuint8x16_impl_generic; -const vint16x8_impl *vint16x8_impl_cpu = &vint16x8_impl_generic; -const vuint16x8_impl *vuint16x8_impl_cpu = &vuint16x8_impl_generic; -const vint32x4_impl *vint32x4_impl_cpu = &vint32x4_impl_generic; -const vuint32x4_impl *vuint32x4_impl_cpu = &vuint32x4_impl_generic; -const vint64x2_impl *vint64x2_impl_cpu = &vint64x2_impl_generic; -const vuint64x2_impl *vuint64x2_impl_cpu = &vuint64x2_impl_generic; +vint8x16_impl vint8x16_impl_cpu = {0}; +vuint8x16_impl vuint8x16_impl_cpu = {0}; +vint16x8_impl vint16x8_impl_cpu = {0}; +vuint16x8_impl vuint16x8_impl_cpu = {0}; +vint32x4_impl vint32x4_impl_cpu = {0}; +vuint32x4_impl vuint32x4_impl_cpu = {0}; +vint64x2_impl vint64x2_impl_cpu = {0}; +vuint64x2_impl vuint64x2_impl_cpu = {0}; // 256-bit -const vint8x32_impl *vint8x32_impl_cpu = &vint8x32_impl_generic; -const vuint8x32_impl *vuint8x32_impl_cpu = &vuint8x32_impl_generic; -const vint16x16_impl *vint16x16_impl_cpu = &vint16x16_impl_generic; -const vuint16x16_impl *vuint16x16_impl_cpu = &vuint16x16_impl_generic; -const vint32x8_impl *vint32x8_impl_cpu = &vint32x8_impl_generic; -const vuint32x8_impl *vuint32x8_impl_cpu = &vuint32x8_impl_generic; -const vint64x4_impl *vint64x4_impl_cpu = &vint64x4_impl_generic; -const vuint64x4_impl *vuint64x4_impl_cpu = &vuint64x4_impl_generic; +vint8x32_impl vint8x32_impl_cpu = {0}; +vuint8x32_impl vuint8x32_impl_cpu = {0}; +vint16x16_impl vint16x16_impl_cpu = {0}; +vuint16x16_impl vuint16x16_impl_cpu = {0}; +vint32x8_impl vint32x8_impl_cpu = {0}; +vuint32x8_impl vuint32x8_impl_cpu = {0}; +vint64x4_impl vint64x4_impl_cpu = {0}; +vuint64x4_impl vuint64x4_impl_cpu = {0}; // 512-bit -const vint8x64_impl *vint8x64_impl_cpu = &vint8x64_impl_generic; -const vuint8x64_impl *vuint8x64_impl_cpu = &vuint8x64_impl_generic; -const vint16x32_impl *vint16x32_impl_cpu = &vint16x32_impl_generic; -const vuint16x32_impl *vuint16x32_impl_cpu = &vuint16x32_impl_generic; -const vint32x16_impl *vint32x16_impl_cpu = &vint32x16_impl_generic; -const vuint32x16_impl *vuint32x16_impl_cpu = &vuint32x16_impl_generic; -const vint64x8_impl *vint64x8_impl_cpu = &vint64x8_impl_generic; -const vuint64x8_impl *vuint64x8_impl_cpu = &vuint64x8_impl_generic; +vint8x64_impl vint8x64_impl_cpu = {0}; +vuint8x64_impl vuint8x64_impl_cpu = {0}; +vint16x32_impl vint16x32_impl_cpu = {0}; +vuint16x32_impl vuint16x32_impl_cpu = {0}; +vint32x16_impl vint32x16_impl_cpu = {0}; +vuint32x16_impl vuint32x16_impl_cpu = {0}; +vint64x8_impl vint64x8_impl_cpu = {0}; +vuint64x8_impl vuint64x8_impl_cpu = {0}; static int vec_init_spinner = 0; +#define FILL_GIVEN_FUNC_PTR(cpu, impl, func) \ + do { \ + if (!(cpu).func && (impl).func) \ + (cpu).func = (impl).func; \ + } while (0) + +#define FILL_GIVEN_FUNC_PTRS_EX(cpu, impl) \ + do { \ + FILL_GIVEN_FUNC_PTR(cpu, impl, splat); \ + FILL_GIVEN_FUNC_PTR(cpu, impl, load_aligned); \ + FILL_GIVEN_FUNC_PTR(cpu, impl, load); \ + FILL_GIVEN_FUNC_PTR(cpu, impl, store_aligned); \ + FILL_GIVEN_FUNC_PTR(cpu, impl, store); \ + FILL_GIVEN_FUNC_PTR(cpu, impl, add); \ + FILL_GIVEN_FUNC_PTR(cpu, impl, sub); \ + FILL_GIVEN_FUNC_PTR(cpu, impl, mul); \ + FILL_GIVEN_FUNC_PTR(cpu, impl, div); \ + FILL_GIVEN_FUNC_PTR(cpu, impl, avg); \ + FILL_GIVEN_FUNC_PTR(cpu, impl, band); \ + FILL_GIVEN_FUNC_PTR(cpu, impl, bor); \ + FILL_GIVEN_FUNC_PTR(cpu, impl, bxor); \ + FILL_GIVEN_FUNC_PTR(cpu, impl, lshift); \ + FILL_GIVEN_FUNC_PTR(cpu, impl, rshift); \ + FILL_GIVEN_FUNC_PTR(cpu, impl, lrshift); \ + FILL_GIVEN_FUNC_PTR(cpu, impl, cmplt); \ + FILL_GIVEN_FUNC_PTR(cpu, impl, cmple); \ + FILL_GIVEN_FUNC_PTR(cpu, impl, cmpeq); \ + FILL_GIVEN_FUNC_PTR(cpu, impl, cmpge); \ + FILL_GIVEN_FUNC_PTR(cpu, impl, cmpgt); \ + FILL_GIVEN_FUNC_PTR(cpu, impl, min); \ + FILL_GIVEN_FUNC_PTR(cpu, impl, max); \ + } while (0) + +#define FILL_GIVEN_FUNC_PTRS(sign, bits, size, impl) \ + FILL_GIVEN_FUNC_PTRS_EX(v##sign##int##bits##x##size##_impl_cpu, v##sign##int##bits##x##size##_impl_##impl) + // returns 0 or a negative error code on failure int vec_init(void) { // This function is NOT thread safe. However, once vec // is initialized, all of the vector functions are thread-safe. - // - // In fact, it's possible to use vec without calling - // vec_init() at all, but it would be completely useless since - // it would just use a generic implementation without any - // vectorization whatsoever (unless maybe the compiler is - // smart enough to optimize it into vectors) if (vec_init_spinner) return 0; // already initialized, do nothing vec_uint32 cpu = vec_get_CPU_features(); -#ifdef VEC_COMPILER_HAS_ALTIVEC - if (cpu & VEC_CPU_HAS_ALTIVEC) { - vint8x16_impl_cpu = &vint8x16_impl_altivec; - vuint8x16_impl_cpu = &vuint8x16_impl_altivec; - vint16x8_impl_cpu = &vint16x8_impl_altivec; - vuint16x8_impl_cpu = &vuint16x8_impl_altivec; - vint32x4_impl_cpu = &vint32x4_impl_altivec; - vuint32x4_impl_cpu = &vuint32x4_impl_altivec; -#ifdef VEC_COMPILER_HAS_ALTIVEC_VSX - if (cpu & VEC_CPU_HAS_ALTIVEC_VSX) { - vint64x2_impl_cpu = &vint64x2_impl_altivec; - vuint64x2_impl_cpu = &vuint64x2_impl_altivec; - } + /* Okay, this might be a little confusing: + * The way we do this is because of x86. For weird reasons, + * Intel decided to extend their prior CPU extensions to + * where SSE4.1 has some extended features of SSE2, AVX2 + * has some extended features that should've been in SSE + * in general, etc. + * + * For this, I've just decided to keep the function + * definitions private, and fill in as we go, with newer + * intrinsics preferred. Others are arbitrary and are + * mutually exclusive (i.e. Altivec vs NEON). This is simply + * the easiest way to go about it :) */ + + /* --- 512-bit */ +#ifdef VEC_COMPILER_HAS_AVX512DQ + if (cpu & VEC_CPU_HAS_AVX512DQ) { + /* these give us native multiply instructions */ + FILL_GIVEN_FUNC_PTRS( , 64, 8, avx512dq); + FILL_GIVEN_FUNC_PTRS(u, 64, 8, avx512dq); + } #endif +#ifdef VEC_COMPILER_HAS_AVX512BW + if (cpu & VEC_CPU_HAS_AVX512BW) { + FILL_GIVEN_FUNC_PTRS( , 8, 64, avx512bw); + FILL_GIVEN_FUNC_PTRS(u, 8, 64, avx512bw); + FILL_GIVEN_FUNC_PTRS( , 16, 32, avx512bw); + FILL_GIVEN_FUNC_PTRS(u, 16, 32, avx512bw); } #endif #ifdef VEC_COMPILER_HAS_AVX512F if (cpu & VEC_CPU_HAS_AVX512F) { - vint8x64_impl_cpu = &vint8x64_impl_avx512f; - vuint8x64_impl_cpu = &vuint8x64_impl_avx512f; - vint16x32_impl_cpu = &vint16x32_impl_avx512f; - vuint16x32_impl_cpu = &vuint16x32_impl_avx512f; - vint32x16_impl_cpu = &vint32x16_impl_avx512f; - vuint32x16_impl_cpu = &vuint32x16_impl_avx512f; - vint64x8_impl_cpu = &vint64x8_impl_avx512f; - vuint64x8_impl_cpu = &vuint64x8_impl_avx512f; + FILL_GIVEN_FUNC_PTRS( , 32, 16, avx512f); + FILL_GIVEN_FUNC_PTRS(u, 32, 16, avx512f); + FILL_GIVEN_FUNC_PTRS( , 64, 8, avx512f); + FILL_GIVEN_FUNC_PTRS(u, 64, 8, avx512f); + } +#endif + + /* --- 256-bit */ +#ifdef VEC_COMPILER_HAS_AVX2 + if (cpu & VEC_CPU_HAS_AVX2) { + FILL_GIVEN_FUNC_PTRS( , 8, 32, avx2); + FILL_GIVEN_FUNC_PTRS(u, 8, 32, avx2); + FILL_GIVEN_FUNC_PTRS( , 16, 16, avx2); + FILL_GIVEN_FUNC_PTRS(u, 16, 16, avx2); + FILL_GIVEN_FUNC_PTRS( , 32, 8, avx2); + FILL_GIVEN_FUNC_PTRS(u, 32, 8, avx2); + FILL_GIVEN_FUNC_PTRS( , 64, 4, avx2); + FILL_GIVEN_FUNC_PTRS(u, 64, 4, avx2); } #endif -#ifdef VEC_COMPILER_HAS_AVX2 - if (cpu & VEC_CPU_HAS_AVX2) { - vint8x32_impl_cpu = &vint8x32_impl_avx2; - vuint8x32_impl_cpu = &vuint8x32_impl_avx2; - vint16x16_impl_cpu = &vint16x16_impl_avx2; - vuint16x16_impl_cpu = &vuint16x16_impl_avx2; - vint32x8_impl_cpu = &vint32x8_impl_avx2; - vuint32x8_impl_cpu = &vuint32x8_impl_avx2; - vint64x4_impl_cpu = &vint64x4_impl_avx2; - vuint64x4_impl_cpu = &vuint64x4_impl_avx2; + + /* --- 128-bit */ +#ifdef VEC_COMPILER_HAS_SSE42 + if (cpu & VEC_CPU_HAS_SSE41) { + FILL_GIVEN_FUNC_PTRS( , 64, 2, sse42); + FILL_GIVEN_FUNC_PTRS(u, 64, 2, sse42); + } +#endif +#ifdef VEC_COMPILER_HAS_SSE41 + if (cpu & VEC_CPU_HAS_SSE41) { + FILL_GIVEN_FUNC_PTRS( , 8, 16, sse41); + FILL_GIVEN_FUNC_PTRS(u, 8, 16, sse41); + FILL_GIVEN_FUNC_PTRS( , 16, 8, sse41); + FILL_GIVEN_FUNC_PTRS(u, 16, 8, sse41); + FILL_GIVEN_FUNC_PTRS( , 32, 4, sse41); + FILL_GIVEN_FUNC_PTRS(u, 32, 4, sse41); + FILL_GIVEN_FUNC_PTRS( , 64, 2, sse41); + FILL_GIVEN_FUNC_PTRS(u, 64, 2, sse41); + } +#endif +#ifdef VEC_COMPILER_HAS_SSE3 + if (cpu & VEC_CPU_HAS_SSE3) { + FILL_GIVEN_FUNC_PTRS( , 8, 16, sse3); + FILL_GIVEN_FUNC_PTRS(u, 8, 16, sse3); + FILL_GIVEN_FUNC_PTRS( , 16, 8, sse3); + FILL_GIVEN_FUNC_PTRS(u, 16, 8, sse3); + FILL_GIVEN_FUNC_PTRS( , 32, 4, sse3); + FILL_GIVEN_FUNC_PTRS(u, 32, 4, sse3); + FILL_GIVEN_FUNC_PTRS( , 64, 2, sse3); + FILL_GIVEN_FUNC_PTRS(u, 64, 2, sse3); } #endif #ifdef VEC_COMPILER_HAS_SSE2 if (cpu & VEC_CPU_HAS_SSE2) { - vint8x16_impl_cpu = &vint8x16_impl_sse2; - vuint8x16_impl_cpu = &vuint8x16_impl_sse2; - vint16x8_impl_cpu = &vint16x8_impl_sse2; - vuint16x8_impl_cpu = &vuint16x8_impl_sse2; -# ifdef VEC_COMPILER_HAS_SSE41 - if (cpu & VEC_CPU_HAS_SSE41) { - vint32x4_impl_cpu = &vint32x4_impl_sse41; - vuint32x4_impl_cpu = &vuint32x4_impl_sse41; - } else -# endif - { - vint32x4_impl_cpu = &vint32x4_impl_sse2; - vuint32x4_impl_cpu = &vuint32x4_impl_sse2; - } - vint64x2_impl_cpu = &vint64x2_impl_sse2; - vuint64x2_impl_cpu = &vuint64x2_impl_sse2; + FILL_GIVEN_FUNC_PTRS( , 8, 16, sse2); + FILL_GIVEN_FUNC_PTRS(u, 8, 16, sse2); + FILL_GIVEN_FUNC_PTRS( , 16, 8, sse2); + FILL_GIVEN_FUNC_PTRS(u, 16, 8, sse2); + FILL_GIVEN_FUNC_PTRS( , 32, 4, sse2); + FILL_GIVEN_FUNC_PTRS(u, 32, 4, sse2); + FILL_GIVEN_FUNC_PTRS( , 64, 2, sse2); + FILL_GIVEN_FUNC_PTRS(u, 64, 2, sse2); } #endif +#ifdef VEC_COMPILER_HAS_NEON + if (cpu & VEC_CPU_HAS_NEON) { + FILL_GIVEN_FUNC_PTRS( , 8, 16, neon); + FILL_GIVEN_FUNC_PTRS(u, 8, 16, neon); + FILL_GIVEN_FUNC_PTRS( , 16, 8, neon); + FILL_GIVEN_FUNC_PTRS(u, 16, 8, neon); + FILL_GIVEN_FUNC_PTRS( , 32, 4, neon); + FILL_GIVEN_FUNC_PTRS(u, 32, 4, neon); + FILL_GIVEN_FUNC_PTRS( , 64, 2, neon); + FILL_GIVEN_FUNC_PTRS(u, 64, 2, neon); + } +#endif +#ifdef VEC_COMPILER_HAS_ALTIVEC + if (cpu & VEC_CPU_HAS_ALTIVEC) { + FILL_GIVEN_FUNC_PTRS( , 8, 16, altivec); + FILL_GIVEN_FUNC_PTRS(u, 8, 16, altivec); + FILL_GIVEN_FUNC_PTRS( , 16, 8, altivec); + FILL_GIVEN_FUNC_PTRS(u, 16, 8, altivec); + FILL_GIVEN_FUNC_PTRS( , 32, 4, altivec); + FILL_GIVEN_FUNC_PTRS(u, 32, 4, altivec); + } +#endif + + /* --- 64-bit */ #ifdef VEC_COMPILER_HAS_MMX if (cpu & VEC_CPU_HAS_MMX) { - vint8x8_impl_cpu = &vint8x8_impl_mmx; - vuint8x8_impl_cpu = &vuint8x8_impl_mmx; - vint16x4_impl_cpu = &vint16x4_impl_mmx; - vuint16x4_impl_cpu = &vuint16x4_impl_mmx; - vint32x2_impl_cpu = &vint32x2_impl_mmx; - vuint32x2_impl_cpu = &vuint32x2_impl_mmx; + FILL_GIVEN_FUNC_PTRS( , 8, 8, mmx); + FILL_GIVEN_FUNC_PTRS(u, 8, 8, mmx); + FILL_GIVEN_FUNC_PTRS( , 16, 4, mmx); + FILL_GIVEN_FUNC_PTRS(u, 16, 4, mmx); + FILL_GIVEN_FUNC_PTRS( , 32, 2, mmx); + FILL_GIVEN_FUNC_PTRS(u, 32, 2, mmx); } #endif #ifdef VEC_COMPILER_HAS_NEON if (cpu & VEC_CPU_HAS_NEON) { - // 64-bit - vint8x8_impl_cpu = &vint8x8_impl_neon; - vuint8x8_impl_cpu = &vuint8x8_impl_neon; - vint16x4_impl_cpu = &vint16x4_impl_neon; - vuint16x4_impl_cpu = &vuint16x4_impl_neon; - vint32x2_impl_cpu = &vint32x2_impl_neon; - vuint32x2_impl_cpu = &vuint32x2_impl_neon; - - // 128-bit - vint8x16_impl_cpu = &vint8x16_impl_neon; - vuint8x16_impl_cpu = &vuint8x16_impl_neon; - vint16x8_impl_cpu = &vint16x8_impl_neon; - vuint16x8_impl_cpu = &vuint16x8_impl_neon; - vint32x4_impl_cpu = &vint32x4_impl_neon; - vuint32x4_impl_cpu = &vuint32x4_impl_neon; - vint64x2_impl_cpu = &vint64x2_impl_neon; - vuint64x2_impl_cpu = &vuint64x2_impl_neon; + FILL_GIVEN_FUNC_PTRS( , 8, 8, neon); + FILL_GIVEN_FUNC_PTRS(u, 8, 8, neon); + FILL_GIVEN_FUNC_PTRS( , 16, 4, neon); + FILL_GIVEN_FUNC_PTRS(u, 16, 4, neon); + FILL_GIVEN_FUNC_PTRS( , 32, 2, neon); + FILL_GIVEN_FUNC_PTRS(u, 32, 2, neon); } #endif - { - // do nothing, they're already set to generics - } + + /* fill any remaining function pointers with generics */ + FILL_GIVEN_FUNC_PTRS( , 8, 64, generic); + FILL_GIVEN_FUNC_PTRS(u, 8, 64, generic); + FILL_GIVEN_FUNC_PTRS( , 16, 32, generic); + FILL_GIVEN_FUNC_PTRS(u, 16, 32, generic); + FILL_GIVEN_FUNC_PTRS( , 32, 16, generic); + FILL_GIVEN_FUNC_PTRS(u, 32, 16, generic); + FILL_GIVEN_FUNC_PTRS( , 64, 8, generic); + FILL_GIVEN_FUNC_PTRS(u, 64, 8, generic); + + FILL_GIVEN_FUNC_PTRS( , 8, 32, generic); + FILL_GIVEN_FUNC_PTRS(u, 8, 32, generic); + FILL_GIVEN_FUNC_PTRS( , 16, 16, generic); + FILL_GIVEN_FUNC_PTRS(u, 16, 16, generic); + FILL_GIVEN_FUNC_PTRS( , 32, 8, generic); + FILL_GIVEN_FUNC_PTRS(u, 32, 8, generic); + FILL_GIVEN_FUNC_PTRS( , 64, 4, generic); + FILL_GIVEN_FUNC_PTRS(u, 64, 4, generic); + + FILL_GIVEN_FUNC_PTRS( , 8, 16, generic); + FILL_GIVEN_FUNC_PTRS(u, 8, 16, generic); + FILL_GIVEN_FUNC_PTRS( , 16, 8, generic); + FILL_GIVEN_FUNC_PTRS(u, 16, 8, generic); + FILL_GIVEN_FUNC_PTRS( , 32, 4, generic); + FILL_GIVEN_FUNC_PTRS(u, 32, 4, generic); + FILL_GIVEN_FUNC_PTRS( , 64, 2, generic); + FILL_GIVEN_FUNC_PTRS(u, 64, 2, generic); + + FILL_GIVEN_FUNC_PTRS( , 8, 8, generic); + FILL_GIVEN_FUNC_PTRS(u, 8, 8, generic); + FILL_GIVEN_FUNC_PTRS( , 16, 4, generic); + FILL_GIVEN_FUNC_PTRS(u, 16, 4, generic); + FILL_GIVEN_FUNC_PTRS( , 32, 2, generic); + FILL_GIVEN_FUNC_PTRS(u, 32, 2, generic); + + FILL_GIVEN_FUNC_PTRS( , 8, 4, generic); + FILL_GIVEN_FUNC_PTRS(u, 8, 4, generic); + FILL_GIVEN_FUNC_PTRS( , 16, 2, generic); + FILL_GIVEN_FUNC_PTRS(u, 16, 2, generic); + + FILL_GIVEN_FUNC_PTRS( , 8, 2, generic); + FILL_GIVEN_FUNC_PTRS(u, 8, 2, generic); vec_init_spinner++; @@ -241,7 +371,6 @@ extern inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ extern inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ extern inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ - extern inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_not(v##sign##int##bits##x##size vec); \ extern inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_cmplt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ extern inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_cmple(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ extern inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_cmpeq(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ @@ -249,7 +378,9 @@ extern inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_cmpgt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ extern inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_lshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2); \ extern inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_rshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2); \ - extern inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_lrshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2); + extern inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_lrshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2); \ + extern inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_min(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); \ + extern inline v##sign##int##bits##x##size v##sign##int##bits##x##size##_max(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2); #define VEC_DEFINE_OPERATIONS(bits, size) \ VEC_DEFINE_OPERATIONS_SIGN( , bits, size) \
--- a/test/test_arith.h Mon Nov 25 00:33:02 2024 -0500 +++ b/test/test_arith.h Thu Apr 24 00:54:02 2025 -0400 @@ -65,7 +65,9 @@ CREATE_TEST(sign, psign, csign, bits, size, avg, vec_##sign##avg(orig_a[i], orig_b[i])) \ CREATE_TEST_SHIFT(sign, psign, csign, bits, size, rshift, vec_##sign##rshift(orig_a[i], orig_b[i])) \ CREATE_TEST_SHIFT(sign, psign, csign, bits, size, lshift, vec_##sign##lshift(orig_a[i], orig_b[i])) \ - CREATE_TEST_SHIFT(sign, psign, csign, bits, size, lrshift, vec_lrshift((vec_uint##bits)orig_a[i], orig_b[i])) + CREATE_TEST_SHIFT(sign, psign, csign, bits, size, lrshift, vec_lrshift((vec_uint##bits)orig_a[i], orig_b[i])) \ + CREATE_TEST(sign, psign, csign, bits, size, min, (orig_a[i] < orig_b[i]) ? (orig_a[i]) : (orig_b[i])) \ + CREATE_TEST(sign, psign, csign, bits, size, max, (orig_a[i] > orig_b[i]) ? (orig_a[i]) : (orig_b[i])) #define CREATE_TESTS(bits, size) \ CREATE_TESTS_SIGN(, d, , bits, size) \ @@ -117,6 +119,8 @@ ret |= test_arith_v##sign##int##bits##x##size##_or(a, b); \ ret |= test_arith_v##sign##int##bits##x##size##_xor(a, b); \ ret |= test_arith_v##sign##int##bits##x##size##_avg(a, b); \ + ret |= test_arith_v##sign##int##bits##x##size##_min(a, b); \ + ret |= test_arith_v##sign##int##bits##x##size##_max(a, b); \ } \ } \ \