view src/impl/generic.c @ 23:e26874655738

*: huge refactor, new major release (hahaha) I keep finding things that are broken... The problem NOW was that vec would unintentionally build some functions with extended instruction sets, which is Bad and would mean that for all intents and purposes the CPU detection was completely broken. Now vec is no longer header only either. Boohoo. However this gives a lot more flexibility to vec since we no longer want or need to care about C++ crap. The NEON and Altivec implementations have not been updated which means they won't compile hence why they're commented out in the cmake build file.
author Paper <paper@tflc.us>
date Sun, 24 Nov 2024 02:52:40 -0500
parents
children 92156fe32755
line wrap: on
line source

#include "vec/impl/generic.h"

#include <string.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]; \
	}; \
	\
	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; \
	} \
	\
	v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_load_aligned(const vec_##sign##int##bits in[size]) \
	{ \
		v##sign##int##bits##x##size vec; \
		memcpy(&vec, in, sizeof(vec_##sign##int##bits) * size); \
		return vec; \
	} \
	\
	void v##sign##int##bits##x##size##_generic_store_aligned(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \
	{ \
		memcpy(out, &vec, sizeof(vec_##sign##int##bits) * size); \
	} \
	\
	v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \
	{ \
		VEC_GENERIC_BUILTIN_OPERATION(+, sign, csign, bits, size); \
	} \
	\
	v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \
	{ \
		VEC_GENERIC_BUILTIN_OPERATION(-, sign, csign, bits, size); \
	} \
	\
	v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \
	{ \
		VEC_GENERIC_BUILTIN_OPERATION(*, sign, csign, bits, size); \
	} \
	\
	v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_div(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \
	{ \
		VEC_GENERIC_OPERATION(((union v##sign##int##bits##x##size##_impl_data *)&vec2)->impl[i] ? (((union v##sign##int##bits##x##size##_impl_data *)&vec1)->impl[i] / ((union v##sign##int##bits##x##size##_impl_data *)&vec2)->impl[i]) : 0, sign, csign, bits, size); \
	} \
	\
	v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_avg(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \
	{ \
		VEC_GENERIC_OPERATION((((union v##sign##int##bits##x##size##_impl_data *)&vec1)->impl[i] + ((union v##sign##int##bits##x##size##_impl_data *)&vec2)->impl[i] + 1) / 2, sign, csign, bits, size); \
	} \
	\
	v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \
	{ \
		VEC_GENERIC_BUILTIN_OPERATION(&, sign, csign, bits, size); \
	} \
	\
	v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \
	{ \
		VEC_GENERIC_BUILTIN_OPERATION(|, sign, csign, bits, size); \
	} \
	\
	v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \
	{ \
		VEC_GENERIC_BUILTIN_OPERATION(^, sign, csign, bits, size); \
	} \
	\
	v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_not(v##sign##int##bits##x##size vec) \
	{ \
		return v##sign##int##bits##x##size##_generic_xor(vec, v##sign##int##bits##x##size##_generic_splat((vec_##sign##int##bits)VEC_UINT##bits##_MAX)); \
	} \
	\
	v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmplt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \
	{ \
		VEC_GENERIC_CMP(<, sign, csign, bits, size); \
	} \
	\
	v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmple(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \
	{ \
		VEC_GENERIC_CMP(<=, sign, csign, bits, size); \
	} \
	\
	v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmpeq(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \
	{ \
		VEC_GENERIC_CMP(==, sign, csign, bits, size); \
	} \
	\
	v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmpge(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \
	{ \
		VEC_GENERIC_CMP(>=, sign, csign, bits, size); \
	} \
	\
	v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_cmpgt(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \
	{ \
		VEC_GENERIC_CMP(>, sign, csign, bits, size); \
	} \
	\
	v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_lshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \
	{ \
		VEC_GENERIC_OPERATION(vec_##sign##lshift(((union v##sign##int##bits##x##size##_impl_data *)&vec1)->impl[i], ((union v##sign##int##bits##x##size##_impl_data *)&vec2)->impl[i]), sign, csign, bits, size); \
	} \
	\
	v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_rshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \
	{ \
		VEC_GENERIC_OPERATION(vec_##sign##rshift(((union v##sign##int##bits##x##size##_impl_data *)&vec1)->impl[i], ((union v##sign##int##bits##x##size##_impl_data *)&vec2)->impl[i]), sign, csign, bits, size); \
	} \
	\
	v##sign##int##bits##x##size v##sign##int##bits##x##size##_generic_lrshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \
	{ \
		VEC_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); \
	} \
	\
	const v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_generic = { \
		v##sign##int##bits##x##size##_generic_splat, \
		v##sign##int##bits##x##size##_generic_load_aligned, \
		v##sign##int##bits##x##size##_generic_load_aligned, \
		v##sign##int##bits##x##size##_generic_store_aligned, \
		v##sign##int##bits##x##size##_generic_store_aligned, \
		v##sign##int##bits##x##size##_generic_add, \
		v##sign##int##bits##x##size##_generic_sub, \
		v##sign##int##bits##x##size##_generic_mul, \
		v##sign##int##bits##x##size##_generic_div, \
		v##sign##int##bits##x##size##_generic_avg, \
		v##sign##int##bits##x##size##_generic_and, \
		v##sign##int##bits##x##size##_generic_or, \
		v##sign##int##bits##x##size##_generic_xor, \
		v##sign##int##bits##x##size##_generic_not, \
		v##sign##int##bits##x##size##_generic_lshift, \
		v##sign##int##bits##x##size##_generic_rshift, \
		v##sign##int##bits##x##size##_generic_lrshift, \
		v##sign##int##bits##x##size##_generic_cmplt, \
		v##sign##int##bits##x##size##_generic_cmple, \
		v##sign##int##bits##x##size##_generic_cmpeq, \
		v##sign##int##bits##x##size##_generic_cmpge, \
		v##sign##int##bits##x##size##_generic_cmpgt, \
	};

#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]; \
	}; \
	\
	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; \
	} \
	\
	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; \
	} \
	\
	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; \
	} \
	\
	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); \
	} \
	\
	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); \
	} \
	\
	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; \
	} \
	\
	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; \
	} \
	\
	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; \
	} \
	\
	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; \
	} \
	\
	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; \
	} \
	\
	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; \
	} \
	\
	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; \
	} \
	\
	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; \
	} \
	\
	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; \
	\
		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; \
	} \
	\
	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; \
	} \
	\
	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; \
	} \
	\
	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; \
	} \
	\
	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; \
	} \
	\
	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; \
	} \
	\
	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; \
	} \
	\
	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; \
	} \
	\
	const v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_generic = { \
		v##sign##int##bits##x##size##_generic_splat, \
		v##sign##int##bits##x##size##_generic_load_aligned, \
		v##sign##int##bits##x##size##_generic_load, \
		v##sign##int##bits##x##size##_generic_store_aligned, \
		v##sign##int##bits##x##size##_generic_store, \
		v##sign##int##bits##x##size##_generic_add, \
		v##sign##int##bits##x##size##_generic_sub, \
		v##sign##int##bits##x##size##_generic_mul, \
		v##sign##int##bits##x##size##_generic_div, \
		v##sign##int##bits##x##size##_generic_avg, \
		v##sign##int##bits##x##size##_generic_and, \
		v##sign##int##bits##x##size##_generic_or, \
		v##sign##int##bits##x##size##_generic_xor, \
		v##sign##int##bits##x##size##_generic_not, \
		v##sign##int##bits##x##size##_generic_lshift, \
		v##sign##int##bits##x##size##_generic_rshift, \
		v##sign##int##bits##x##size##_generic_lrshift, \
		v##sign##int##bits##x##size##_generic_cmplt, \
		v##sign##int##bits##x##size##_generic_cmple, \
		v##sign##int##bits##x##size##_generic_cmpeq, \
		v##sign##int##bits##x##size##_generic_cmpge, \
		v##sign##int##bits##x##size##_generic_cmpgt, \
	};

#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