view src/impl/generic.c @ 25:92156fe32755

impl/ppc/altivec: update to new implementation the signed average function is wrong; it needs to round up the number when only one of them is odd, but that doesn't necessarily seem to be true because altivec is weird, and that's what we need to emulate the quirks for. ugh. also the altivec backend uses the generic functions instead of fallbacks because it does indeed use the exact same memory structure as the generic implementation...
author Paper <paper@tflc.us>
date Sun, 24 Nov 2024 11:15:59 +0000
parents e26874655738
children
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) \
	{ \
	    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; \
	} \
	\
	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