changeset 31:bf6ad516f1e6

Backed out changeset c6c99ab1088a
author Paper <paper@tflc.us>
date Fri, 25 Apr 2025 17:40:33 -0400
parents 641d8c79b1da
children 0de48dc864ea
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, 943 insertions(+), 2347 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Fri Apr 25 17:40:30 2025 -0400
+++ b/CMakeLists.txt	Fri Apr 25 17:40:33 2025 -0400
@@ -2,13 +2,7 @@
 
 project(vec VERSION 3.0.0 DESCRIPTION "a tiny C99 SIMD vector library" LANGUAGES C)
 
-add_library(vec "src/vec.c")
-
-target_sources(vec PRIVATE
-	"src/cpu.c"
-	"src/impl/generic.c"
-	# "src/impl/fallback.c" -- deadcode
-)
+add_library(vec SHARED "src/vec.c;src/cpu.c;src/impl/generic.c;src/impl/fallback.c")
 
 include(CheckCCompilerFlag)
 
@@ -55,18 +49,10 @@
 	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")
@@ -75,14 +61,6 @@
 	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)
@@ -109,24 +87,12 @@
 	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}")
@@ -139,17 +105,6 @@
 	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	Fri Apr 25 17:40:30 2025 -0400
+++ b/include/vec/cpu.h	Fri Apr 25 17:40:33 2025 -0400
@@ -42,8 +42,6 @@
 	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.
--- a/include/vec/impl/gcc.h	Fri Apr 25 17:40:30 2025 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,80 +0,0 @@
-/**
- * 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	Fri Apr 25 17:40:30 2025 -0400
+++ b/include/vec/impl/generic.h	Fri Apr 25 17:40:33 2025 -0400
@@ -29,6 +29,65 @@
 
 #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	Fri Apr 25 17:40:30 2025 -0400
+++ b/include/vec/impl/ppc/altivec.h	Fri Apr 25 17:40:33 2025 -0400
@@ -22,6 +22,8 @@
  * SOFTWARE.
 **/
 
+/* Altivec vector support. */
+
 #ifndef VEC_IMPL_PPC_ALTIVEC_H_
 #define VEC_IMPL_PPC_ALTIVEC_H_
 
--- a/include/vec/impl/x86/avx512bw.h	Fri Apr 25 17:40:30 2025 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-/**
- * 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_ */
--- a/include/vec/impl/x86/avx512dq.h	Fri Apr 25 17:40:30 2025 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-/**
- * 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	Fri Apr 25 17:40:30 2025 -0400
+++ b/include/vec/impl/x86/avx512f.h	Fri Apr 25 17:40:33 2025 -0400
@@ -27,8 +27,12 @@
 
 #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	Fri Apr 25 17:40:30 2025 -0400
+++ b/include/vec/impl/x86/sse2.h	Fri Apr 25 17:40:33 2025 -0400
@@ -27,6 +27,33 @@
 
 #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;
--- a/include/vec/impl/x86/sse3.h	Fri Apr 25 17:40:30 2025 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-/**
- * 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	Fri Apr 25 17:40:30 2025 -0400
+++ b/include/vec/impl/x86/sse41.h	Fri Apr 25 17:40:33 2025 -0400
@@ -27,13 +27,7 @@
 
 #include "vec/vec.h"
 
-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 vint32x4_impl  vint32x4_impl_sse41;
 extern const vuint32x4_impl vuint32x4_impl_sse41;
-extern const vuint64x2_impl vuint64x2_impl_sse41;
 
 #endif /* VEC_IMPL_X86_SSE41_H_ */
--- a/include/vec/impl/x86/sse42.h	Fri Apr 25 17:40:30 2025 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-/**
- * 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	Fri Apr 25 17:40:30 2025 -0400
+++ b/include/vec/vec.h	Fri Apr 25 17:40:33 2025 -0400
@@ -84,16 +84,6 @@
 		[!!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
 
@@ -685,6 +675,7 @@
 		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); \
@@ -693,8 +684,6 @@
 		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) \
@@ -734,56 +723,53 @@
 #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 vint8x2_impl   vint8x2_impl_cpu;
-extern vuint8x2_impl  vuint8x2_impl_cpu;
+extern const vint8x2_impl   *vint8x2_impl_cpu;
+extern const vuint8x2_impl  *vuint8x2_impl_cpu;
 
 // 32-bit
-extern vint8x4_impl   vint8x4_impl_cpu;
-extern vuint8x4_impl  vuint8x4_impl_cpu;
-extern vint16x2_impl  vint16x2_impl_cpu;
-extern vuint16x2_impl vuint16x2_impl_cpu;
+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;
 
 // 64-bit
-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;
+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;
 
 // 128-bit
-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;
+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;
 
 // 256-bit
-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;
+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;
 
 // 512-bit
-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;
+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;
 
 //////////////////////////////////////////////////////////////////////////////
 // declared as inline for  ! performance : )
@@ -791,122 +777,112 @@
 #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); \
 	} \
 	\
-	VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_not(v##sign##int##bits##x##size vec) \
+	inline 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##_xor(vec, v##sign##int##bits##x##size##_splat((vec_##sign##int##bits)VEC_UINT##bits##_MAX)); \
+		return v##sign##int##bits##x##size##_impl_cpu->bnot(vec); \
 	} \
 	\
 	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); \
-	} \
-	\
-	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); \
+		return v##sign##int##bits##x##size##_impl_cpu->lrshift(vec1, vec2); \
 	}
 
 #define VEC_DEFINE_OPERATIONS(bits, size) \
--- a/src/cpu.c	Fri Apr 25 17:40:30 2025 -0400
+++ b/src/cpu.c	Fri Apr 25 17:40:33 2025 -0400
@@ -362,29 +362,7 @@
 	if (vec_CPU_OSSavesYMM && (vec_CPU_CPUIDMaxFunction >= 7)) {
 		int a, b, c, d;
 		VEC_CPU_CPUID(7, a, b, c, d);
-		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;
+		return b & 0x00000020;
 		(void)a, (void)c, (void)d;
 	}
 	return 0;
@@ -513,10 +491,6 @@
 			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	Fri Apr 25 17:40:30 2025 -0400
+++ b/src/impl/arm/neon.c	Fri Apr 25 17:40:33 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"); \
 	\
-	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]) \
+	static 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; \
 	} \
 	\
-	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]) \
+	static 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); \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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,18 +114,29 @@
 		return vec1d->vec; \
 	} \
 	\
-	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, \
+	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, \
 	};
 
 #define VEC_DEFINE_OPERATIONS(bits, size) \
@@ -233,7 +244,7 @@
 
 // NEON doesn't have native 64-bit multiplication, so we have
 // to do it ourselves
-VEC_FUNC_IMPL int64x2_t vmulq_s64(const int64x2_t a, const int64x2_t b)
+static inline 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));
@@ -243,7 +254,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));
 }
 
-VEC_FUNC_IMPL uint64x2_t vmulq_u64(const uint64x2_t a, const uint64x2_t b)
+static inline 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	Fri Apr 25 17:40:30 2025 -0400
+++ b/src/impl/fallback.c	Fri Apr 25 17:40:33 2025 -0400
@@ -1,27 +1,3 @@
-/**
- * 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>
@@ -31,8 +7,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 also true for AltiVec. This is likely true for NEON as well,
-// but that isn't tested for now.
+// integers). This is likely true for AltiVec and NEON as well, but those
+// aren't tested for now.
 
 #define VEC_FALLBACK_OPERATION(op, sign, csign, bits, size) \
 	do { \
--- a/src/impl/gcc.c	Fri Apr 25 17:40:30 2025 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,517 +0,0 @@
-/**
- * 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	Fri Apr 25 17:40:30 2025 -0400
+++ b/src/impl/generic.c	Fri Apr 25 17:40:33 2025 -0400
@@ -1,27 +1,3 @@
-/**
- * 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>
@@ -50,7 +26,7 @@
 		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 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++) \
@@ -58,39 +34,39 @@
 		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 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]) \
+	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) \
+	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) \
+	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) \
+	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) \
+	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) \
+	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; \
@@ -101,105 +77,89 @@
 		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) \
+	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) \
+	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) \
+	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) \
+	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_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) \
+	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_GENERIC_CMP(<=, sign, csign, bits, size); \
 	} \
 	\
-	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) \
+	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) \
+	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_GENERIC_CMP(>=, sign, csign, bits, size); \
 	} \
 	\
-	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) \
+	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) \
+	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) \
+	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) \
+	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, \
+		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, \
 	};
 
 #define VEC_GENERIC_DEFINE_OPERATIONS(bits, size) \
@@ -223,7 +183,7 @@
 		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) \
+	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); \
@@ -231,7 +191,7 @@
 		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]) \
+	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); \
@@ -239,7 +199,7 @@
 		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]) \
+	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); \
@@ -247,7 +207,7 @@
 		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]) \
+	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; \
 	\
@@ -255,7 +215,7 @@
 		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]) \
+	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; \
 	\
@@ -263,7 +223,7 @@
 		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) \
+	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; \
@@ -274,7 +234,7 @@
 		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) \
+	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; \
@@ -285,7 +245,7 @@
 		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) \
+	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; \
@@ -296,7 +256,7 @@
 		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) \
+	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; \
@@ -307,7 +267,7 @@
 		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) \
+	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; \
@@ -318,7 +278,7 @@
 		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) \
+	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; \
@@ -329,7 +289,7 @@
 		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) \
+	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; \
@@ -340,7 +300,7 @@
 		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) \
+	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; \
@@ -351,7 +311,17 @@
 		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) \
+	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) \
 	{ \
 		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; \
@@ -362,7 +332,7 @@
 		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) \
+	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; \
@@ -373,7 +343,7 @@
 		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) \
+	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; \
@@ -384,7 +354,7 @@
 		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) \
+	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; \
@@ -395,7 +365,7 @@
 		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) \
+	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; \
@@ -406,7 +376,7 @@
 		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) \
+	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; \
@@ -417,7 +387,7 @@
 		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) \
+	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; \
@@ -428,7 +398,7 @@
 		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) \
+	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; \
@@ -439,52 +409,29 @@
 		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, \
+		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, \
 	};
 
 #define VEC_GENERIC_DEFINE_OPERATIONS(bits, size, halfsize) \
--- a/src/impl/ppc/altivec.c	Fri Apr 25 17:40:30 2025 -0400
+++ b/src/impl/ppc/altivec.c	Fri Apr 25 17:40:33 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) \
-	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) \
+	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) \
 	{ \
 		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) \
-	.mul = v##sign##int##bits##x##size##_altivec_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) \
-    /* nothing */
+    v##sign##int##bits##x##size##_generic_mul
 #endif
 
 #ifdef vec_splats
 # define VEC_ALTIVEC_DEFINE_SPLAT(sign, csign, bits, size) \
-	VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_splat(vec_##sign##int##bits x) \
+	static 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) \
-	.splat = v##sign##int##bits##x##size##_altivec_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) \
-    /* nothing */
+    v##sign##int##bits##x##size##_generic_splat
 #endif
 
 #define VEC_ALTIVEC_uRSHIFT vec_sr
 #define VEC_ALTIVEC_RSHIFT vec_sra
 
 #define VEC_ALTIVEC_DEFINE_uLRSHIFT(sign, csign, bits, size) \
-	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) \
+	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) \
 	{ \
 		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,10 +93,11 @@
 		return vec1d->vec; \
 	}
 #define VEC_ALTIVEC_STRUCT_uLRSHIFT(sign, csign, bits, size) \
-	.lrshift = v##sign##int##bits##x##size##_altivec_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) /* nothing */
+#define VEC_ALTIVEC_STRUCT_LRSHIFT(sign, csign, bits, size) \
+    v##sign##int##bits##x##size##_generic_lrshift
 
 #define VEC_ALTIVEC_CAST_BOOL_8 (vector signed char)
 #define VEC_ALTIVEC_CAST_BOOL_U8 (vector unsigned char)
@@ -108,26 +109,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) \
-	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]) \
+	static 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; \
 	} \
 	\
-	VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_altivec_load(const vec_##sign##int##bits in[size]) \
+	static 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; \
 	} \
 	\
-	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]) \
+	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_st(((union v##sign##int##bits##x##size##_impl_data *)&vec)->altivec, 0, out); \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
@@ -136,7 +137,7 @@
 		return vec1d->vec; \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
@@ -147,25 +148,7 @@
 	\
 	VEC_ALTIVEC_DEFINE_MUL(sign, csign, bits, size) \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
@@ -174,7 +157,7 @@
 		return vec1d->vec; \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
@@ -185,7 +168,7 @@
 	\
 	VEC_ALTIVEC_DEFINE_##sign##LRSHIFT(sign, csign, bits, size) \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
@@ -194,7 +177,7 @@
 		return vec1d->vec; \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
@@ -203,7 +186,7 @@
 		return vec1d->vec; \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
@@ -212,7 +195,7 @@
 		return vec1d->vec; \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
@@ -223,7 +206,7 @@
 	\
 	VEC_ALTIVEC_DEFINE_SPLAT(sign, csign, bits, size) \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
@@ -232,7 +215,7 @@
 		return vec1d->vec; \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
@@ -241,7 +224,7 @@
 		return vec1d->vec; \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
@@ -250,7 +233,7 @@
 		return vec1d->vec; \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
@@ -259,7 +242,7 @@
 		return vec1d->vec; \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
@@ -269,25 +252,28 @@
 	} \
 	\
 	const v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_altivec = { \
-		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, \
+		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, \
 	};
 
 #define VEC_DEFINE_OPERATIONS(bits, size) \
--- a/src/impl/x86/avx2.c	Fri Apr 25 17:40:30 2025 -0400
+++ b/src/impl/x86/avx2.c	Fri Apr 25 17:40:33 2025 -0400
@@ -23,88 +23,70 @@
 **/
 
 #include "vec/impl/x86/avx2.h"
+#include "vec/impl/generic.h"
 
 #include <immintrin.h>
 
-/* ------------------------------------------------------------------------ */
-/* 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; \
-	\
-		vec1d->avx2 = _mm256_##OP##_ep##INTLSIGN##BITS(vec1d->avx2, vec2d->avx2); \
+// 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; \
 	\
+		/* 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)); \
+	\
+		/* 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; \
-	}
-
-#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 */
+	} while (0)
 
-#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_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
-
-/* ------------------------------------------------------------------------ */
+#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)
 
 // multiplication
 
-#define VEC_AVX2_MUL_8x32(sign) /* nothing */
+#define VEC_AVX2_MUL_8x32(sign) \
+	VEC_AVX2_OPERATION_8x32_16x16(mullo, sign)
 
 #define VEC_AVX2_MUL_16x16(sign) \
-	VEC_FUNC_IMPL v##sign##int16x16 v##sign##int16x16_avx2_mul(v##sign##int16x16 vec1, v##sign##int16x16 vec2) \
-	{ \
+	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; \
 	\
 		vec1d->avx2 = _mm256_mullo_epi16(vec1d->avx2, vec2d->avx2); \
 		return vec1d->vec; \
-	}
+	} while (0)
 
 #define VEC_AVX2_MUL_32x8(sign) \
-	VEC_FUNC_IMPL v##sign##int32x8 v##sign##int32x8_avx2_mul(v##sign##int32x8 vec1, v##sign##int32x8 vec2) \
-	{ \
+	do { \
 		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) \
-	VEC_FUNC_IMPL v##sign##int64x4 v##sign##int64x4_avx2_mul(v##sign##int64x4 vec1, v##sign##int64x4 vec2) \
-	{ \
+	do { \
 		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; \
 	\
@@ -118,12 +100,7 @@
 	\
 		vec1d->avx2 = _mm256_add_epi64(hi, ac); \
 		return vec1d->vec; \
-	}
-
-#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
+	} while (0)
 
 // operations
 
@@ -136,31 +113,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"); \
 	\
-	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]) \
+	static 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; \
 	} \
 	\
-	VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx2_load(const vec_##sign##int##bits in[size]) \
+	static 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; \
 	} \
 	\
-	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]) \
+	static 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); \
 	} \
 	\
-	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]) \
+	static 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); \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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 +146,7 @@
 		return vec1d->vec; \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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,9 +155,12 @@
 		return vec1d->vec; \
 	} \
 	\
-	VEC_AVX2_MUL_##bits##x##size(sign) \
+	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_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) \
+	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) \
 	{ \
 		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; \
@@ -189,7 +169,7 @@
 		return vec1d->vec; \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
@@ -198,7 +178,7 @@
 		return vec1d->vec; \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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,25 +187,29 @@
 		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 = { \
-		.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, \
+		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, \
 	};
 
 #define VEC_AVX2_DEFINE_OPERATIONS(bits, size) \
--- a/src/impl/x86/avx512bw.c	Fri Apr 25 17:40:30 2025 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,139 +0,0 @@
-/**
- * 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)
--- a/src/impl/x86/avx512dq.c	Fri Apr 25 17:40:30 2025 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-/**
- * 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	Fri Apr 25 17:40:30 2025 -0400
+++ b/src/impl/x86/avx512f.c	Fri Apr 25 17:40:33 2025 -0400
@@ -23,79 +23,202 @@
 **/
 
 #include "vec/impl/x86/avx512f.h"
+#include "vec/impl/generic.h"
 
 #include <immintrin.h>
 
-/* ------------------------------------------------------------------------ */
-
-#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); \
+// 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; \
 	\
-		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; \
+		/* 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)); \
 	\
-		vec1d->avx512f = _mm512_##op##_epi##bits(vec1d->avx512f, vec2d->avx512f); \
+		/* 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) \
+			) \
+		); \
 	\
 		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_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_SHIFT(name, op, sign, bits, size) \
-	VEC_AVX512F_OPERATION_EX(name, op, sign, bits, size, u)
+#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_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_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_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_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_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_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_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_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_XRSHIFT(name, bits, size, sign, aORl) \
-	VEC_AVX512F_OPERATION_SHIFT(name, sr##aORl##v, sign, bits, size)
+#define VEC_AVX512F_LSHIFT_16x32(sign) \
+	VEC_AVX512F_OPERATION_16x32(sllv, sign)
 
-/* 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_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)
 
-#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_lRSHIFT_8x64(sign) \
+	VEC_AVX512F_OPERATION_8x64(srlv, sign)
+
+#define VEC_AVX512F_lRSHIFT_16x32(sign) \
+	VEC_AVX512F_OPERATION_16x32(srlv, 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_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)
 
-/* 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_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)
 
-/* ------------------------------------------------------------------------ */
+#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 { \
@@ -106,35 +229,46 @@
 	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##_avx512f_load_aligned(const vec_##sign##int##bits in[size]) \
+	static 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; \
 	} \
 	\
-	VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_avx512f_load(const vec_##sign##int##bits in[size]) \
+	static 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; \
 	} \
 	\
-	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]) \
+	static 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); \
 	} \
 	\
-	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]) \
+	static 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); \
 	} \
 	\
-	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_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \
+	{ \
+		VEC_AVX512F_ADD_##bits##x##size(sign); \
+	} \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
@@ -143,7 +277,7 @@
 		return vec1d->vec; \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
@@ -152,7 +286,7 @@
 		return vec1d->vec; \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
@@ -161,34 +295,51 @@
 		return vec1d->vec; \
 	} \
 	\
-	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_lshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \
+	{ \
+		VEC_AVX512F_LSHIFT_##bits##x##size(sign); \
+	} \
 	\
-	VEC_AVX512F_MINMAX_##sign##bits##x##size(min) \
-	VEC_AVX512F_MINMAX_##sign##bits##x##size(max) \
+	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); \
+	} \
 	\
 	const v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_avx512f = { \
-		.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), \
+		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, \
 	};
 
 #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	Fri Apr 25 17:40:30 2025 -0400
+++ b/src/impl/x86/mmx.c	Fri Apr 25 17:40:33 2025 -0400
@@ -24,67 +24,46 @@
 
 #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)
 
-#define VEC_MMX_MUL_8x8(sign) /* nothing */
+// shared between MMX variations
+#define VEC_MMX_MUL_8x8(sign) \
+	VEC_MMX_OPERATION_8x8(mullo, sign)
+
 #define VEC_MMX_MUL_16x4(sign) \
-	VEC_FUNC_IMPL v##sign##int16x4 v##sign##int16x4_mmx_mul(v##sign##int16x4 vec1, v##sign##int16x4 vec2) \
-	{ \
+	do { \
 		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; \
-	}
-#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 */
+	} 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_MUL_32x2(sign) \
+	/* TODO implement this for real */ \
+	do { \
+		return v##sign##int32x2_generic_mul(vec1, vec2); \
+	} while (0)
 
 #define VEC_MMX_DEFINE_OPERATIONS_SIGN(sign, bits, size) \
 	union v##sign##int##bits##x##size##_impl_data { \
@@ -95,19 +74,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"); \
 	\
-	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]) \
+	static 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; \
 	} \
 	\
-	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]) \
+	static 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)); \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
@@ -117,7 +96,7 @@
 		return vec1d->vec; \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
@@ -127,9 +106,12 @@
 		return vec1d->vec; \
 	} \
 	\
-	VEC_MMX_MUL_##bits##x##size(sign) \
+	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_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) \
+	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) \
 	{ \
 		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; \
@@ -139,7 +121,7 @@
 		return vec1d->vec; \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
@@ -149,7 +131,7 @@
 		return vec1d->vec; \
 	} \
 	\
-	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) \
+	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) \
 	{ \
 		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; \
@@ -159,22 +141,29 @@
 		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 = { \
-		.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, \
+		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, \
 	};
 
 #define VEC_MMX_DEFINE_OPERATIONS(bits, size) \
--- a/src/impl/x86/sse2.c	Fri Apr 25 17:40:30 2025 -0400
+++ b/src/impl/x86/sse2.c	Fri Apr 25 17:40:33 2025 -0400
@@ -23,110 +23,10 @@
 **/
 
 #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 */ \
@@ -192,58 +92,45 @@
 		return vec1d->vec; \
 	} while (0)
 
-/* ------------------------------------------------------------------------ */
-/* comparison */
-
-/* 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 \
+#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; \
 	\
-		TRANS1 \
-	\
-		vec1d->sse = _mm_##op##_epi##bits(vec##first##d->sse, vec##second##d->sse); \
-	\
-		TRANS2 \
+		vec1d->sse = _mm_cmpeq_epi8(vec1d->sse, vec2d->sse); \
+		return vec1d->vec; \
+	} while (0)
+
+#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; \
 	\
+		vec1d->sse = _mm_cmpeq_epi16(vec1d->sse, vec2d->sse); \
 		return vec1d->vec; \
-	}
-
-#define VEC_SSE2_CMP(name, op, bits, size, first, second) \
-	VEC_xSSE2_CMP(name, op, /* nothing */, bits, size, first, second, /* nothing */, /* nothing */, /* nothing */)
+	} 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 */ \
-	})
+#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)
 
-/* 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. */
+// 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) \
-	VEC_FUNC_IMPL v##sign##int64x2 v##sign##int64x2_sse2_cmpeq(v##sign##int64x2 vec1, v##sign##int64x2 vec2) \
-	{ \
+	do { \
 		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; \
 	\
@@ -253,60 +140,42 @@
 		vec1d->sse = _mm_and_si128(vec1d->sse, vec2d->sse); \
 	\
 		return vec1d->vec; \
-	}
-
-/* ------------------------------------------------------------------------ */
-
-#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
-
-/* ------------------------------------------------------------------------ */
+	} while (0)
 
 #define VEC_SSE2_DEFINE_OPERATIONS_SIGN(sign, bits, size) \
-	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; \
-	} \
+	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_load_aligned(const vec_##sign##int##bits in[size]) \
+	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]) \
 	{ \
 		union v##sign##int##bits##x##size##_impl_data vec; \
 		vec.sse = _mm_load_si128((const __m128i *)in); \
 		return vec.vec; \
 	} \
 	\
-	VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_sse2_load(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]) \
 	{ \
 		union v##sign##int##bits##x##size##_impl_data vec; \
 		vec.sse = _mm_loadu_si128((const __m128i *)in); \
 		return vec.vec; \
 	} \
 	\
-	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]) \
+	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); \
 	} \
 	\
-	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]) \
+	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); \
 	} \
 	\
-	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) \
+	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; \
@@ -315,7 +184,7 @@
 		return vec1d->vec; \
 	} \
 	\
-	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) \
+	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; \
@@ -324,12 +193,12 @@
 		return vec1d->vec; \
 	} \
 	\
-	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) \
+	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); \
 	} \
 	\
-	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) \
+	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; \
@@ -338,7 +207,7 @@
 		return vec1d->vec; \
 	} \
 	\
-	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) \
+	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; \
@@ -347,7 +216,7 @@
 		return vec1d->vec; \
 	} \
 	\
-	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) \
+	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; \
@@ -356,30 +225,34 @@
 		return vec1d->vec; \
 	} \
 	\
-	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) \
+	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); \
+	} \
 	\
 	const v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_sse2 = { \
-		.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), \
+		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, \
 	};
 
 #define VEC_SSE2_DEFINE_OPERATIONS(bits, size) \
--- a/src/impl/x86/sse3.c	Fri Apr 25 17:40:30 2025 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/**
- * 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	Fri Apr 25 17:40:30 2025 -0400
+++ b/src/impl/x86/sse41.c	Fri Apr 25 17:40:33 2025 -0400
@@ -23,138 +23,54 @@
 **/
 
 #include "vec/impl/x86/sse41.h"
+#include "vec/impl/x86/sse2.h"
 #include "vec/impl/generic.h"
 
 #include <immintrin.h>
 
-/* ------------------------------------------------------------------------ */
-
-#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); \
+// 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; \
+	}; \
 	\
-		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) \
+	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) \
 	{ \
 		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 \
+	} \
 	\
-		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), \
+	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, \
 	};
 
-#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)
+VEC_SSE41_DEFINE_OPERATIONS()
+VEC_SSE41_DEFINE_OPERATIONS(u)
--- a/src/impl/x86/sse42.c	Fri Apr 25 17:40:30 2025 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,100 +0,0 @@
-/**
- * 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	Fri Apr 25 17:40:30 2025 -0400
+++ b/src/vec.c	Fri Apr 25 17:40:33 2025 -0400
@@ -32,27 +32,15 @@
 #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
@@ -71,284 +59,166 @@
 extern inline vec_uintmax vec_uavg(vec_uintmax x, vec_uintmax y);
 
 // 16-bit
-vint8x2_impl   vint8x2_impl_cpu   = {0};
-vuint8x2_impl  vuint8x2_impl_cpu  = {0};
+const vint8x2_impl   *vint8x2_impl_cpu   = &vint8x2_impl_generic;
+const vuint8x2_impl  *vuint8x2_impl_cpu  = &vuint8x2_impl_generic;
 
 // 32-bit
-vint8x4_impl   vint8x4_impl_cpu   = {0};
-vuint8x4_impl  vuint8x4_impl_cpu  = {0};
-vint16x2_impl  vint16x2_impl_cpu  = {0};
-vuint16x2_impl vuint16x2_impl_cpu = {0};
+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;
 
 // 64-bit
-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};
+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;
 
 // 128-bit
-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};
+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;
 
 // 256-bit
-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};
+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;
 
 // 512-bit
-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};
+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;
 
 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();
 
-	/* 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);
-	}
+#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;
+		}
 #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) {
-		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);
+		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;
 	}
 #endif
-
-	/* --- 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);
+#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;
 	}
 #endif
 #ifdef VEC_COMPILER_HAS_SSE2
 	if (cpu & VEC_CPU_HAS_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);
+		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;
 	}
 #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) {
-		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);
+		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;
 	}
 #endif
 #ifdef VEC_COMPILER_HAS_NEON
 	if (cpu & VEC_CPU_HAS_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);
+		// 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;
 	}
 #endif
-
-	/* 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);
+	{
+		// do nothing, they're already set to generics
+	}
 
 	vec_init_spinner++;
 
@@ -371,6 +241,7 @@
 	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); \
@@ -378,9 +249,7 @@
 	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##_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);
+	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);
 
 #define VEC_DEFINE_OPERATIONS(bits, size) \
 	VEC_DEFINE_OPERATIONS_SIGN( , bits, size) \
--- a/test/test_arith.h	Fri Apr 25 17:40:30 2025 -0400
+++ b/test/test_arith.h	Fri Apr 25 17:40:33 2025 -0400
@@ -65,9 +65,7 @@
 	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(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]))
+	CREATE_TEST_SHIFT(sign, psign, csign, bits, size, lrshift, vec_lrshift((vec_uint##bits)orig_a[i], orig_b[i]))
 
 #define CREATE_TESTS(bits, size) \
 	CREATE_TESTS_SIGN(, d, , bits, size) \
@@ -119,8 +117,6 @@
 			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); \
 		} \
 	} \
 	\