changeset 0:02a517e4c492

*: initial commit
author Paper <paper@paper.us.eu.org>
date Tue, 22 Oct 2024 01:22:41 -0400
parents
children 1d9d2308c1d2
files LICENSE README include/vec/impl/altivec.h include/vec/impl/gcc.h include/vec/impl/generic.h include/vec/impl/sse2.h include/vec/vec.h
diffstat 7 files changed, 850 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LICENSE	Tue Oct 22 01:22:41 2024 -0400
@@ -0,0 +1,21 @@
+MIT License
+
+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.
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README	Tue Oct 22 01:22:41 2024 -0400
@@ -0,0 +1,46 @@
+vec - a tiny SIMD vector header-only library written in C99
+
+it comes with an extremely basic (and somewhat lacking) API,
+where there are eight supported vector types, all 128-bit:
+
+	vint8x16  - 16 signed 8-bit integers
+	vint16x8  - 8 signed 16-bit integers
+	vint32x4  - 4 signed 32-bit integers
+	vint64x2  - 2 signed 64-bit integers
+	vuint8x16 - 16 unsigned 8-bit integers
+	vuint16x8 - 8 unsigned 16-bit integers
+	vuint32x4 - 4 unsigned 32-bit integers
+	vuint32x4 - 2 unsigned 64-bit integers
+
+all of these have many operations that are prefixed with the
+name of the type and an underscore, for example:
+
+	vint8x16 vint8x16_splat(uint8_t x)
+	- creates a vint8x16 where all of the values are filled
+	  with the value of `x'
+
+the current supported operations are:
+
+	v[u]intAxB splat([u]intA_t x)
+		creates a vector with all of the values are filled with
+		the value of `x'
+
+	v[u]intAxB load(const [u]intA_t x[B])
+		copies the values from the memory address stored at `x';
+		the address is NOT required to be aligned
+
+	void store(v[u]intAxB vec, [u]intA_t x[B])
+		copies the values from the vector into the memory address
+		stored at `x'
+
+		like with load(), this does not require address alignment
+
+	v[u]intAxB add(v[u]intAxB vec1, v[u]intAxB vec2)
+		adds the value of `vec1' and `vec2' and returns it
+
+	v[u]intAxB sub(v[u]intAxB vec1, v[u]intAxB vec2)
+		subtracts the value of `vec2' from `vec1' and returns it
+
+	v[u]intAxB mul(v[u]intAxB vec1, v[u]intAxB vec2)
+		multiplies the values of `vec1' and `vec2' together and
+		returns it
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/vec/impl/altivec.h	Tue Oct 22 01:22:41 2024 -0400
@@ -0,0 +1,145 @@
+/**
+ * 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.
+**/
+
+/* Altivec vector support. */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <altivec.h>
+
+#define VEC_ALTIVEC_ALIGNMENT 16
+
+/* 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, bits, size) \
+	static inline VEC_ALWAYS_INLINE v##sign##int##bits##x##size v##sign##int##bits##x##size##_splat(sign##int##bits##_t i) \
+	{ \
+		return vec_splats(i); \
+	} \
+	\
+	static inline VEC_ALWAYS_INLINE v##sign##int##bits##x##size v##sign##int##bits##x##size##_load(const sign##int##bits##_t in[size]) \
+	{ \
+		return vec_perm(vec_ld(0, in), vec_ld(VEC_ALTIVEC_ALIGNMENT, in), vec_lvsl(0, in)); \
+	} \
+	\
+	static inline VEC_ALWAYS_INLINE void v##sign##int##bits##x##size##_store(v##sign##int##bits##x##size vec, sign##int##bits##_t out[size]) \
+	{ \
+		VEC_ALIGNED_ARRAY(sign##int##bits##_t, aligned_out, size, VEC_ALTIVEC_ALIGNMENT); \
+		vec_st(vec, 0, aligned_out); \
+		memcpy(out, aligned_out, size * sizeof(*aligned_out)); \
+	} \
+	\
+	static inline VEC_ALWAYS_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 vec_add(vec1, vec2); \
+	} \
+	\
+	static inline VEC_ALWAYS_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 vec_sub(vec1, vec2); \
+	} \
+	\
+	static inline VEC_ALWAYS_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 vec_mul(vec1, vec2); \
+	}
+
+#ifndef VEC_VUINT8X16
+# define VEC_VUINT8X16
+typedef vector unsigned char vuint8x16;
+# define VUINT8x16_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \
+	(vuint8x16){ a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p }
+VEC_DEFINE_OPERATIONS(u, 8, 16)
+# define VUINT8x16_ALIGNED(x) ((x) % VEC_ALTIVEC_ALIGNMENT == 0)
+#endif /* VEC_VUINT8X16 */
+
+#ifndef VEC_VINT8X16
+# define VEC_VINT8X16
+typedef vector signed char vint8x16;
+# define VINT8x16_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \
+	(vint8x16){ a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p }
+VEC_DEFINE_OPERATIONS(, 8, 16)
+# define VINT8x16_ALIGNED(x) ((x) % VEC_ALTIVEC_ALIGNMENT == 0)
+#endif /* VEC_VINT8X16 */
+
+#ifndef VEC_VUINT16X8
+# define VEC_VUINT16X8
+typedef vector unsigned short vuint16x8;
+# define VUINT16x8_CONSTANT(a, b, c, d, e, f, g, h) \
+	(vuint16x8){ a, b, c, d, e, f, g, h }
+VEC_DEFINE_OPERATIONS(u, 16, 8)
+# define VUINT16x8_ALIGNED(x) ((x) % VEC_ALTIVEC_ALIGNMENT == 0)
+#endif /* VEC_VUINT16X8 */
+
+#ifndef VEC_VINT16X8
+# define VEC_VINT16X8
+typedef vector signed short vint16x8;
+# define VINT16x8_CONSTANT(a, b, c, d, e, f, g, h) \
+	(vint16x8){ a, b, c, d, e, f, g, h }
+VEC_DEFINE_OPERATIONS(, 16, 8)
+# define VINT16x8_ALIGNED(x) ((x) % VEC_ALTIVEC_ALIGNMENT == 0)
+#endif /* VEC_VINT16X8 */
+
+#ifndef VEC_VUINT32X4
+# define VEC_VUINT32X4
+typedef vector unsigned int vuint32x4;
+# define VUINT32x4_CONSTANT(a, b, c, d) \
+	(vuint32x4){ a, b, c, d }
+VEC_DEFINE_OPERATIONS(u, 32, 4)
+# define VUINT32x4_ALIGNED(x) ((x) % VEC_ALTIVEC_ALIGNMENT == 0)
+#endif /* VEC_VUINT32X4 */
+
+#ifndef VEC_VINT32X4
+# define VEC_VINT32X4
+typedef vector signed int vint32x4;
+# define VINT32x4_CONSTANT(a, b, c, d) \
+	(vint32x4){ a, b, c, d }
+VEC_DEFINE_OPERATIONS(, 32, 4)
+# define VINT32x4_ALIGNED(x) ((x) % VEC_ALTIVEC_ALIGNMENT == 0)
+#endif /* VEC_VINT32X4 */
+
+#if defined(__POWER8__) && defined(__VSX__)
+
+# ifndef VEC_VUINT64X2
+#  define VEC_VUINT64X2
+typedef vector unsigned long long vuint64x2;
+#  define VUINT64x2_CONSTANT(a, b) \
+	(vuint64x2){ a, b }
+VEC_DEFINE_OPERATIONS(u, 64, 2)
+#  define VUINT64x2_ALIGNED(x) ((x) % VEC_ALTIVEC_ALIGNMENT == 0)
+# endif /* VEC_VUINT64X2 */
+
+# ifndef VEC_VINT64X2
+#  define VEC_VINT64X2
+typedef vector signed long long vint64x2;
+#  define VINT64x2_CONSTANT(a, b) \
+	(vint64x2){ a, b }
+VEC_DEFINE_OPERATIONS(, 64, 2)
+#  define VINT64x2_ALIGNED(x) ((x) % VEC_ALTIVEC_ALIGNMENT == 0)
+# endif /* VEC_VINT64X2 */
+
+#endif /* defined(__POWER8__) && defined(__VSX__) */
+
+#undef VEC_DEFINE_OPERATIONS
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/vec/impl/gcc.h	Tue Oct 22 01:22:41 2024 -0400
@@ -0,0 +1,137 @@
+/**
+ * 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.
+**/
+
+/* GCC built in vectors */
+
+#include <stdint.h>
+#include <string.h>
+
+#define VEC_DEFINE_OPERATIONS(sign, bits, size) \
+	static inline VEC_ALWAYS_INLINE v##sign##int##bits##x##size v##sign##int##bits##x##size##_splat(sign##int##bits##_t x) \
+	{ \
+        v##sign##int##bits##x##size vec; \
+        for (int i = 0; i < size; i++) vec[i] = x; \
+		return vec; \
+	} \
+	\
+	static inline VEC_ALWAYS_INLINE v##sign##int##bits##x##size v##sign##int##bits##x##size##_load(const sign##int##bits##_t in[size]) \
+	{ \
+		v##sign##int##bits##x##size vec; \
+		memcpy(&vec, in, sizeof(vec)); \
+		return vec; \
+	} \
+	\
+	static inline VEC_ALWAYS_INLINE void v##sign##int##bits##x##size##_store(v##sign##int##bits##x##size vec, sign##int##bits##_t out[size]) \
+	{ \
+		memcpy(out, &vec, sizeof(vec)); \
+	} \
+	\
+	static inline VEC_ALWAYS_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 vec1 + vec2; \
+	} \
+	\
+	static inline VEC_ALWAYS_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 vec1 - vec2; \
+	} \
+	\
+	static inline VEC_ALWAYS_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 vec1 * vec2; \
+	}
+
+#ifndef VEC_VINT8X16
+# define VEC_VINT8X16
+typedef int8_t vint8x16 __attribute__((__vector_size__(16)));
+# define VINT8x16_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \
+	(vint8x16){ a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p }
+VEC_DEFINE_OPERATIONS(, 8, 16)
+# define VINT8x16_ALIGNED 1
+#endif
+
+#ifndef VEC_VINT16X8
+# define VEC_VINT16X8
+typedef int16_t vint16x8 __attribute__((__vector_size__(16)));
+# define VINT16x8_CONSTANT(a, b, c, d, e, f, g, h) \
+	(vint16x8){ a, b, c, d, e, f, g, h }
+VEC_DEFINE_OPERATIONS(, 16, 8)
+# define VINT16x8_ALIGNED 1
+#endif
+
+#ifndef VEC_VINT32X4
+# define VEC_VINT32X4
+typedef int32_t vint32x4 __attribute__((__vector_size__(16)));
+# define VINT32x4_CONSTANT(a, b, c, d) \
+	(vint32x4){ a, b, c, d }
+VEC_DEFINE_OPERATIONS(, 32, 4)
+# define VINT32x4_ALIGNED 1
+#endif
+
+#ifndef VEC_VINT64X2
+# define VEC_VINT64X2
+typedef int64_t vint64x2 __attribute__((__vector_size__(16)));
+# define VINT64x2_CONSTANT(a, b) \
+	(vint64x2){ a, b }
+VEC_DEFINE_OPERATIONS(, 64, 2)
+# define VINT64x2_ALIGNED 1
+#endif
+
+#ifndef VEC_VUINT8X16
+# define VEC_VUINT8X16
+typedef uint8_t vuint8x16 __attribute__((__vector_size__(16)));
+# define VUINT8x16_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \
+	(vuint8x16){ a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p }
+VEC_DEFINE_OPERATIONS(u, 8, 16)
+# define VINT8x16_ALIGNED 1
+#endif
+
+#ifndef VEC_VUINT16X8
+# define VEC_VUINT16X8
+typedef uint16_t vuint16x8 __attribute__((__vector_size__(16)));
+# define VUINT16x8_CONSTANT(a, b, c, d, e, f, g, h) \
+	(vuint16x8){ a, b, c, d, e, f, g, h }
+VEC_DEFINE_OPERATIONS(u, 16, 8)
+# define VINT16x8_ALIGNED 1
+#endif
+
+#ifndef VEC_VUINT32X4
+# define VEC_VUINT32X4
+typedef uint32_t vuint32x4 __attribute__((__vector_size__(16)));
+# define VUINT32x4_CONSTANT(a, b, c, d) \
+	(vuint32x4){ a, b, c, d }
+VEC_DEFINE_OPERATIONS(u, 32, 4)
+# define VINT32x4_ALIGNED 1
+#endif
+
+#ifndef VEC_VUINT64X2
+# define VEC_VUINT64X4
+typedef uint64_t vuint64x2 __attribute__((__vector_size__(16)));
+# define VUINT64x2_CONSTANT(a, b) \
+	(vuint64x2){ a, b }
+VEC_DEFINE_OPERATIONS(u, 64, 2)
+# define VINT64x2_ALIGNED 1
+#endif
+
+#undef VEC_DEFINE_OPERATIONS
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/vec/impl/generic.h	Tue Oct 22 01:22:41 2024 -0400
@@ -0,0 +1,149 @@
+/**
+ * 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. */
+
+#include <stdint.h>
+#include <string.h>
+
+#define VEC_DEFINE_STRUCT(sign, bits, size) \
+    typedef struct { \
+        sign##int##bits##_t arr[size]; \
+    } v##sign##int##bits##x##size;
+
+#define VEC_DEFINE_OPERATIONS(sign, bits, size) \
+	static inline VEC_ALWAYS_INLINE v##sign##int##bits##x##size v ## sign ## int ## bits ## x ## size ## _splat(sign ## int ## bits ## _t x) \
+	{ \
+        v##sign##int##bits##x##size vec; \
+        for (int i = 0; i < size; i++) vec.arr[i] = x; \
+		return vec; \
+	} \
+	\
+	static inline VEC_ALWAYS_INLINE v ## sign ## int ## bits ## x ## size v ## sign ## int ## bits ## x ## size ## _load(const sign ## int ## bits ## _t in[size]) \
+	{ \
+        v##sign##int##bits##x##size vec; \
+        memcpy(vec.arr, in, sizeof(vec.arr)); \
+		return vec; \
+	} \
+	\
+	static inline VEC_ALWAYS_INLINE void v ## sign ## int ## bits ## x ## size ## _store(v ## sign ## int ## bits ## x ## size vec, sign ## int ## bits ## _t out[size]) \
+	{ \
+        memcpy(out, vec.arr, sizeof(vec.arr)); \
+	} \
+	\
+	static inline VEC_ALWAYS_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) \
+	{ \
+        v##sign##int##bits##x##size vec; \
+        for (int i = 0; i < size; i++) vec.arr[i] = vec1.arr[i] + vec2.arr[i]; \
+		return vec; \
+	} \
+	\
+	static inline VEC_ALWAYS_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) \
+	{ \
+        v##sign##int##bits##x##size vec; \
+        for (int i = 0; i < size; i++) vec.arr[i] = vec1.arr[i] - vec2.arr[i]; \
+		return vec; \
+	} \
+	\
+	static inline VEC_ALWAYS_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) \
+	{ \
+        v##sign##int##bits##x##size vec; \
+        for (int i = 0; i < size; i++) vec.arr[i] = vec1.arr[i] * vec2.arr[i]; \
+		return vec; \
+	}
+
+#ifndef VEC_VINT8X16
+# define VEC_VINT8X16
+VEC_DEFINE_STRUCT(, 8, 16)
+# define VINT8x16_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \
+	((vint8x16){ .arr = { a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p } })
+VEC_DEFINE_OPERATIONS(, 8, 16)
+# define VINT8x16_ALIGNED 1
+#endif
+
+#ifndef VEC_VINT16X8
+# define VEC_VINT16X8
+VEC_DEFINE_STRUCT(, 16, 8)
+# define VINT16x8_CONSTANT(a, b, c, d, e, f, g, h) \
+	((vint16x8){ .arr = { a, b, c, d, e, f, g, h } })
+VEC_DEFINE_OPERATIONS(, 16, 8)
+# define VINT16x8_ALIGNED 1
+#endif
+
+#ifndef VEC_VINT32X4
+# define VEC_VINT32X4
+VEC_DEFINE_STRUCT(, 32, 4)
+# define VINT32x4_CONSTANT(a, b, c, d) \
+	((vint32x4){ .arr = { a, b, c, d } })
+VEC_DEFINE_OPERATIONS(, 32, 4)
+# define VINT32x4_ALIGNED 1
+#endif
+
+#ifndef VEC_VINT64X2
+# define VEC_VINT64X2
+VEC_DEFINE_STRUCT(, 64, 2)
+# define VINT64x2_CONSTANT(a, b) \
+	((vint64x2){ .arr = { a, b } })
+VEC_DEFINE_OPERATIONS(, 64, 2)
+# define VINT64x2_ALIGNED 1
+#endif
+
+#ifndef VEC_VUINT8X16
+# define VEC_VUINT8X16
+VEC_DEFINE_STRUCT(u, 8, 16)
+# define VUINT8x16_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \
+	((vuint8x16){ .arr = { a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p } })
+VEC_DEFINE_OPERATIONS(u, 8, 16)
+# define VINT8x16_ALIGNED 1
+#endif
+
+#ifndef VEC_VUINT16X8
+# define VEC_VUINT16X8
+VEC_DEFINE_STRUCT(u, 16, 8)
+# define VUINT16x8_CONSTANT(a, b, c, d, e, f, g, h) \
+	((vuint16x8){ .arr = { a, b, c, d, e, f, g, h } })
+VEC_DEFINE_OPERATIONS(u, 16, 8)
+# define VINT16x8_ALIGNED 1
+#endif
+
+#ifndef VEC_VUINT32X4
+# define VEC_VUINT32X4
+VEC_DEFINE_STRUCT(u, 32, 4)
+# define VUINT32x4_CONSTANT(a, b, c, d) \
+	((vuint32x4){ .arr = { a, b, c, d } })
+VEC_DEFINE_OPERATIONS(u, 32, 4)
+# define VINT32x4_ALIGNED 1
+#endif
+
+#ifndef VEC_VUINT64X2
+# define VEC_VUINT64X2
+VEC_DEFINE_STRUCT(u, 64, 2)
+# define VUINT64x2_CONSTANT(a, b) \
+	((vuint64x2){ .arr = { a, b } })
+VEC_DEFINE_OPERATIONS(u, 64, 2)
+# define VINT64x2_ALIGNED 1
+#endif
+
+#undef VEC_DEFINE_STRUCT
+#undef VEC_DEFINE_OPERATIONS
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/vec/impl/sse2.h	Tue Oct 22 01:22:41 2024 -0400
@@ -0,0 +1,255 @@
+/**
+ * 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.
+**/
+
+#include <emmintrin.h>
+
+#define VEC_DEFINE_OPERATIONS(sign, bits, size) \
+	static inline VEC_ALWAYS_INLINE v##sign##int##bits##x##size v##sign##int##bits##x##size##_load(const sign##int##bits##_t in[size]) \
+	{ \
+		return _mm_loadu_si128((const __m128i *)in); \
+	} \
+	\
+	static inline VEC_ALWAYS_INLINE void v##sign##int##bits##x##size##_store(v##sign##int##bits##x##size vec, sign##int##bits##_t out[size]) \
+	{ \
+		memcpy(out, &vec, sizeof(vec)); \
+	} \
+	\
+	static inline VEC_ALWAYS_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 _mm_add_epi##bits(vec1, vec2); \
+	} \
+	\
+	static inline VEC_ALWAYS_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 _mm_sub_epi##bits(vec1, vec2); \
+	}
+
+#ifndef VEC_VINT8X16
+# define VEC_VINT8X16
+typedef __m128i vint8x16;
+# define VINT8x16_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \
+	(_mm_setr_epi8(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p))
+VEC_DEFINE_OPERATIONS(, 8, 16)
+# define VINT8x16_ALIGNED 1
+static inline VEC_ALWAYS_INLINE vint8x16 vint8x16_mul(vint8x16 vec1, vint8x16 vec2)
+{
+	// unpack and multiply
+	__m128i dst_even = _mm_mullo_epi16(vec1, vec2);
+	__m128i dst_odd = _mm_mullo_epi16(_mm_srli_epi16(vec1, 8), _mm_srli_epi16(vec2, 8));
+
+	// repack
+	return _mm_or_si128(_mm_slli_epi16(dst_odd, 8), _mm_srli_epi16(_mm_slli_epi16(dst_even, 8), 8));
+}
+static inline VEC_ALWAYS_INLINE vint8x16 vint8x16_splat(int8_t c)
+{
+	return VINT8x16_CONSTANT(c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c);
+}
+#endif
+
+#ifndef VEC_VINT16X8
+# define VEC_VINT16X8
+typedef __m128i vint16x8;
+# define VINT16x8_CONSTANT(a, b, c, d, e, f, g, h) \
+	(_mm_setr_epi16(a, b, c, d, e, f, g, h))
+VEC_DEFINE_OPERATIONS(, 16, 8)
+# define VINT16x8_ALIGNED 1
+static inline VEC_ALWAYS_INLINE vint16x8 vint16x8_mul(vint16x8 vec1, vint16x8 vec2)
+{
+	return _mm_mullo_epi16(vec1, vec2);
+}
+static inline VEC_ALWAYS_INLINE vint16x8 vint16x8_splat(int16_t c)
+{
+	return VINT16x8_CONSTANT(c, c, c, c, c, c, c, c);
+}
+#endif
+
+#ifndef VEC_VINT32X4
+# define VEC_VINT32X4
+typedef __m128i vint32x4;
+# define VINT32x4_CONSTANT(a, b, c, d) \
+	(_mm_setr_epi32(a, b, c, d))
+VEC_DEFINE_OPERATIONS(, 32, 4)
+# define VINT32x4_ALIGNED 1
+static inline VEC_ALWAYS_INLINE vint32x4 vint32x4_mul(vint32x4 a, vint32x4 b)
+{
+	__m128i a13    = _mm_shuffle_epi32(a, 0xF5);        // (-,a3,-,a1)
+	__m128i b13    = _mm_shuffle_epi32(b, 0xF5);        // (-,b3,-,b1)
+	__m128i prod02 = _mm_mul_epu32(a, b);               // (-,a2*b2,-,a0*b0)
+	__m128i prod13 = _mm_mul_epu32(a13, b13);           // (-,a3*b3,-,a1*b1)
+	__m128i prod01 = _mm_unpacklo_epi32(prod02,prod13); // (-,-,a1*b1,a0*b0) 
+	__m128i prod23 = _mm_unpackhi_epi32(prod02,prod13); // (-,-,a3*b3,a2*b2) 
+	return _mm_unpacklo_epi64(prod01, prod23);          // (ab3,ab2,ab1,ab0)
+}
+static inline VEC_ALWAYS_INLINE vint32x4 vint32x4_splat(int32_t c)
+{
+	return VINT32x4_CONSTANT(c, c, c, c);
+}
+#endif
+
+#ifndef VEC_VINT64X2
+# define VEC_VINT64X2
+typedef __m128i vint64x2;
+static inline VEC_ALWAYS_INLINE vint64x2 VINT64x2_CONSTANT(int64_t a, int64_t b)
+{
+	return _mm_setr_epi32(b, b >> 32, a, a >> 32);
+}
+VEC_DEFINE_OPERATIONS(, 64, 2)
+# define VINT64x2_ALIGNED 1
+static inline VEC_ALWAYS_INLINE vint64x2 vint64x2_mul(vint64x2 ab, vint64x2 cd)
+{
+	/* ac = (ab & 0xFFFFFFFF) * (cd & 0xFFFFFFFF); */
+	__m128i ac = _mm_mul_epu32(ab, cd);
+
+	/* b = ab >> 32; */
+	__m128i b = _mm_srli_epi64(ab, 32);
+
+	/* bc = b * (cd & 0xFFFFFFFF); */
+	__m128i bc = _mm_mul_epu32(b, cd);
+
+	/* d = cd >> 32; */
+	__m128i d = _mm_srli_epi64(cd, 32);
+
+	/* ad = (ab & 0xFFFFFFFF) * d; */
+	__m128i ad = _mm_mul_epu32(ab, d);
+
+	/* high = bc + ad; */
+	__m128i high = _mm_add_epi64(bc, ad);
+
+	/* high <<= 32; */
+	high = _mm_slli_epi64(high, 32);
+
+	/* return ac + high; */
+	return _mm_add_epi64(high, ac);
+}
+static inline VEC_ALWAYS_INLINE vint64x2 vint64x2_splat(int64_t c)
+{
+	return VINT64x2_CONSTANT(c, c);
+}
+#endif
+
+#ifndef VEC_VUINT8X16
+# define VEC_VUINT8X16
+typedef __m128i vuint8x16;
+# define VUINT8x16_CONSTANT(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \
+	(_mm_setr_epi8(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p))
+VEC_DEFINE_OPERATIONS(u, 8, 16)
+# define VINT8x16_ALIGNED 1
+static inline VEC_ALWAYS_INLINE vint8x16 vuint8x16_mul(vuint8x16 vec1, vuint8x16 vec2)
+{
+	// unpack and multiply
+	__m128i dst_even = _mm_mullo_epi16(vec1, vec2);
+	__m128i dst_odd = _mm_mullo_epi16(_mm_srli_epi16(vec1, 8), _mm_srli_epi16(vec2, 8));
+
+	// repack
+	return _mm_or_si128(_mm_slli_epi16(dst_odd, 8), _mm_srli_epi16(_mm_slli_epi16(dst_even, 8), 8));
+}
+static inline VEC_ALWAYS_INLINE vuint8x16 vuint8x16_splat(uint8_t c)
+{
+	return VUINT8x16_CONSTANT(c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c);
+}
+#endif
+
+#ifndef VEC_VUINT16X8
+# define VEC_VUINT16X8
+typedef __m128i vuint16x8;
+# define VUINT16x8_CONSTANT(a, b, c, d, e, f, g, h) \
+	(_mm_setr_epi16(a, b, c, d, e, f, g, h))
+VEC_DEFINE_OPERATIONS(u, 16, 8)
+# define VINT16x8_ALIGNED 1
+static inline VEC_ALWAYS_INLINE vuint16x8 vuint16x8_mul(vuint16x8 vec1, vuint16x8 vec2)
+{
+	return _mm_mullo_epi16(vec1, vec2);
+}
+static inline VEC_ALWAYS_INLINE vuint16x8 vuint16x8_splat(uint16_t c)
+{
+	return VUINT16x8_CONSTANT(c, c, c, c, c, c, c, c);
+}
+#endif
+
+#ifndef VEC_VUINT32X4
+# define VEC_VUINT32X4
+typedef __m128i vuint32x4;
+# define VUINT32x4_CONSTANT(a, b, c, d) \
+	(_mm_setr_epi32(a, b, c, d))
+VEC_DEFINE_OPERATIONS(u, 32, 4)
+# define VUINT32x4_ALIGNED 1
+static inline VEC_ALWAYS_INLINE vuint32x4 vuint32x4_mul(vuint32x4 a, vuint32x4 b)
+{
+	/* this was stolen from... somewhere :) */
+	__m128i a13    = _mm_shuffle_epi32(a, 0xF5);        // (-,a3,-,a1)
+	__m128i b13    = _mm_shuffle_epi32(b, 0xF5);        // (-,b3,-,b1)
+	__m128i prod02 = _mm_mul_epu32(a, b);               // (-,a2*b2,-,a0*b0)
+	__m128i prod13 = _mm_mul_epu32(a13, b13);           // (-,a3*b3,-,a1*b1)
+	__m128i prod01 = _mm_unpacklo_epi32(prod02,prod13); // (-,-,a1*b1,a0*b0) 
+	__m128i prod23 = _mm_unpackhi_epi32(prod02,prod13); // (-,-,a3*b3,a2*b2)
+	return _mm_unpacklo_epi64(prod01, prod23);          // (ab3,ab2,ab1,ab0)
+}
+static inline VEC_ALWAYS_INLINE vuint32x4 vuint32x4_splat(int32_t c)
+{
+	return VUINT32x4_CONSTANT(c, c, c, c);
+}
+#endif
+
+#ifndef VEC_VUINT64X2
+# define VEC_VUINT64X2
+typedef __m128i vuint64x2;
+static inline VEC_ALWAYS_INLINE vint64x2 VUINT64x2_CONSTANT(int64_t a, int64_t b)
+{
+	return _mm_setr_epi32(b, b >> 32, a, a >> 32);
+}
+VEC_DEFINE_OPERATIONS(u, 64, 2)
+# define VUINT64x2_ALIGNED 1
+static inline VEC_ALWAYS_INLINE vuint64x2 vuint64x2_mul(vuint64x2 ab, vuint64x2 cd)
+{
+	/* ac = (ab & 0xFFFFFFFF) * (cd & 0xFFFFFFFF); */
+	__m128i ac = _mm_mul_epu32(ab, cd);
+
+	/* b = ab >> 32; */
+	__m128i b = _mm_srli_epi64(ab, 32);
+
+	/* bc = b * (cd & 0xFFFFFFFF); */
+	__m128i bc = _mm_mul_epu32(b, cd);
+
+	/* d = cd >> 32; */
+	__m128i d = _mm_srli_epi64(cd, 32);
+
+	/* ad = (ab & 0xFFFFFFFF) * d; */
+	__m128i ad = _mm_mul_epu32(ab, d);
+
+	/* high = bc + ad; */
+	__m128i high = _mm_add_epi64(bc, ad);
+
+	/* high <<= 32; */
+	high = _mm_slli_epi64(high, 32);
+
+	/* return ac + high; */
+	return _mm_add_epi64(high, ac);
+}
+static inline VEC_ALWAYS_INLINE vuint64x2 vuint64x2_splat(uint64_t c)
+{
+	return VUINT64x2_CONSTANT(c, c);
+}
+#endif
+
+#undef VEC_DEFINE_OPERATIONS
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/vec/vec.h	Tue Oct 22 01:22:41 2024 -0400
@@ -0,0 +1,97 @@
+/**
+ * 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_VEC_H_
+#define VEC_VEC_H_
+
+#define VEC_SEMVER_ATLEAST(a, b, c, x, y, z) \
+	(((a) >= (x)) && \
+	 ((a) > x || (b) >= (y)) && \
+	 ((a) > x || (b) > (y) || (c) >= (z)))
+
+#define VEC_GCC_ATLEAST(x, y, z) \
+	VEC_SEMVER_ATLEAST(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__, x, y, z)
+
+/* GCC/clang attributes */
+#if defined(__has_attribute)
+# if __has_attribute(__always_inline__)
+#  define VEC_ALWAYS_INLINE __attribute__((__always_inline__))
+# endif
+# if __has_attribute(__aligned__)
+#  define VEC_ALIGNED(x) __attribute__((__aligned__(x)))
+# endif
+# if __has_attribute(__vector_size__)
+#  define VEC_HAVE_GCC_VECTORS
+# endif
+#endif
+
+#ifndef VEC_HAVE_GCC_VECTORS
+# if __GNUC__ >= 4 || __clang_major__ >= 3
+#  define VEC_HAVE_GCC_VECTORS
+# endif
+#endif
+
+#ifndef VEC_ALIGNED
+# if VEC_GCC_ATLEAST(2, 7, 0)
+#  define VEC_ALIGNED(x) __attribute__((aligned(x)))
+# endif
+#endif
+
+#ifndef VEC_ALWAYS_INLINE
+# if VEC_GCC_ATLEAST(3, 1, 0)
+#  define VEC_ALWAYS_INLINE(x) __attribute__((always_inline))
+# endif
+#endif
+
+#ifndef VEC_ALWAYS_INLINE
+# define VEC_ALWAYS_INLINE
+#endif
+
+#ifdef VEC_ALIGNED
+# define VEC_ALIGNED_ARRAY(type, var, size, align) \
+	VEC_ALIGNED(align) type var[size]
+#else
+/* allocate more than necessary to align */
+# define VEC_ALIGNED_ARRAY(type, var, size, align) \
+	type var##_unaligned_[size + align - 1]; \
+	type *var = (type *)((((intptr_t)var##_unaligned_ + align - 1) / align) * align)
+#endif
+
+/* POWER altivec */
+#ifdef __ALTIVEC__
+# include "impl/altivec.h"
+#endif
+
+/* x86_64 SSE2+ */
+#ifdef __SSE2__
+# include "impl/sse2.h"
+#endif
+
+#ifdef VEC_HAVE_GCC_VECTORS
+# include "impl/gcc.h"
+#endif
+
+#include "impl/generic.h"
+
+#endif /* VEC_VEC_H_ */