diff utils/gengcc.c @ 40:55cadb1fac4b

*: add mod operation, add GCC vector backend need to test it with old gcc though. :)
author Paper <paper@tflc.us>
date Sun, 27 Apr 2025 02:49:53 -0400
parents
children c6e0df09b86f
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/utils/gengcc.c	Sun Apr 27 02:49:53 2025 -0400
@@ -0,0 +1,390 @@
+/**
+ * vec - a tiny SIMD vector library in C99
+ * 
+ * Copyright (c) 2024-2025 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.
+**/
+
+/* Use this file to generate include/vec/impl/generic.h !!
+ *
+ * `gcc -o gengeneric gengeneric.c` */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
+
+/* ------------------------------------------------------------------------ */
+
+enum op {
+	/* return vector, take in a integer */
+	OP_SPLAT = 0,
+
+	/* return vector, take in an array */
+	OP_LOAD_ALIGNED,
+	OP_LOAD,
+
+	/* void, take in vector and array */
+	OP_STORE_ALIGNED,
+	OP_STORE,
+
+	/* return vector, takes in two vectors */
+	OP_ADD,
+	OP_SUB,
+	OP_MUL,
+	OP_AND,
+	OP_OR,
+	OP_XOR,
+	OP_CMPLT,
+	OP_CMPEQ,
+	OP_CMPGT,
+	OP_CMPLE,
+	OP_CMPGE,
+	OP_MIN,
+	OP_MAX,
+	OP_AVG,
+
+	/* return vector, takes in a vector and an explicitly unsigned vector */
+	OP_LSHIFT,
+	OP_RSHIFT,
+	OP_LRSHIFT,
+
+	/* return vector, takes in a vector */
+	OP_NOT,
+
+	OP_FINAL_,
+
+	/* operations that have some sort of "caveat" should go here, until
+	 * they are fixed or removed */
+
+	OP_DIV, /* this one causes a floating point exception on my machine.
+	         * possibly we could change the behavior of divide-by-zero
+	         * with some gcc pragma ?  --paper */
+	OP_MOD, /* ditto with the above */
+};
+
+/* convert op -> string */
+static struct {
+	const char *u;
+	const char *l;
+} op_names[] = {
+	[OP_SPLAT] = {"SPLAT", "splat"},
+	[OP_LOAD_ALIGNED] = {"LOAD_ALIGNED", "load_aligned"},
+	[OP_LOAD] = {"LOAD", "load"},
+	[OP_STORE_ALIGNED] = {"STORE_ALIGNED", "store_aligned"},
+	[OP_STORE] = {"STORE", "store"},
+	[OP_ADD] = {"ADD", "add"},
+	[OP_SUB] = {"SUB", "sub"},
+	[OP_MUL] = {"MUL", "mul"},
+	/*[OP_DIV] = {"DIV", "div"},*/
+	[OP_AVG] = {"AVG", "avg"},
+	[OP_AND] = {"AND", "and"},
+	[OP_OR] = {"OR", "or"},
+	[OP_XOR] = {"XOR", "xor"},
+	[OP_NOT] = {"NOT", "not"},
+	[OP_CMPLT] = {"CMPLT", "cmplt"},
+	[OP_CMPEQ] = {"CMPEQ", "cmpeq"},
+	[OP_CMPGT] = {"CMPGT", "cmpgt"},
+	[OP_CMPLE] = {"CMPLE", "cmple"},
+	[OP_CMPGE] = {"CMPGE", "cmpge"},
+	[OP_MIN] = {"MIN", "min"},
+	[OP_MAX] = {"MAX", "max"},
+	[OP_RSHIFT] = {"RSHIFT", "rshift"},
+	[OP_LRSHIFT] = {"LRSHIFT", "lrshift"},
+	[OP_LSHIFT] = {"LSHIFT", "lshift"},
+};
+
+#define UPSIGN(x) ((x) ? "" : "U")
+#define LOSIGN(x) ((x) ? "" : "u")
+
+static void print_gcc_op(enum op op, int is_signed, int bits, int size)
+{
+	int i;
+
+	printf("#ifndef V%sINT%dx%d_%s_DEFINED\n", UPSIGN(is_signed), bits, size, op_names[op].u);
+
+	printf("VEC_FUNC_IMPL ");
+
+	/* first; the return value */
+	switch (op) {
+	case OP_SPLAT:
+	case OP_LOAD_ALIGNED:
+	case OP_LOAD:
+	case OP_ADD:
+	case OP_SUB:
+	case OP_MUL:
+	case OP_DIV:
+	case OP_AND:
+	case OP_OR:
+	case OP_XOR:
+	case OP_CMPLT:
+	case OP_CMPEQ:
+	case OP_CMPGT:
+	case OP_CMPLE:
+	case OP_CMPGE:
+	case OP_MIN:
+	case OP_MAX:
+	case OP_AVG:
+	case OP_RSHIFT:
+	case OP_LRSHIFT:
+	case OP_LSHIFT:
+	case OP_NOT:
+		printf("v%sint%dx%d", LOSIGN(is_signed), bits, size);
+		break;
+	case OP_STORE_ALIGNED:
+	case OP_STORE:
+		printf("void");
+		break;
+	}
+
+	/* whitespace and function name */
+	printf(" v%sint%dx%d_%s(", LOSIGN(is_signed), bits, size, op_names[op].l);
+
+	/* parameters */
+	switch (op) {
+	case OP_SPLAT:
+		printf("vec_%sint%d x", LOSIGN(is_signed), bits);
+		break;
+	case OP_LOAD_ALIGNED:
+	case OP_LOAD:
+		printf("const vec_%sint%d x[%d]", LOSIGN(is_signed), bits, size);
+		break;
+	case OP_STORE_ALIGNED:
+	case OP_STORE:
+		printf("v%sint%dx%d vec, vec_%sint%d arr[%d]", LOSIGN(is_signed), bits, size, LOSIGN(is_signed), bits, size);
+		break;
+	case OP_ADD:
+	case OP_SUB:
+	case OP_MUL:
+	case OP_DIV:
+	case OP_AND:
+	case OP_OR:
+	case OP_XOR:
+	case OP_CMPLT:
+	case OP_CMPEQ:
+	case OP_CMPGT:
+	case OP_CMPLE:
+	case OP_CMPGE:
+	case OP_MIN:
+	case OP_MAX:
+	case OP_AVG:
+		printf("v%sint%dx%d vec1, v%sint%dx%d vec2", LOSIGN(is_signed), bits, size, LOSIGN(is_signed), bits, size);
+		break;
+	case OP_RSHIFT:
+	case OP_LRSHIFT:
+	case OP_LSHIFT:
+		printf("v%sint%dx%d vec1, vuint%dx%d vec2", LOSIGN(is_signed), bits, size, bits, size);
+		break;
+	case OP_NOT:
+		printf("v%sint%dx%d vec", LOSIGN(is_signed), bits, size);
+		break;
+	}
+
+	puts(")\n{");
+
+	switch (op) {
+	case OP_SPLAT:
+		printf("\tv%sint%dx%d vec;\n", LOSIGN(is_signed), bits, size);
+		printf("\tvec.gcc = (__typeof__(vec.gcc)){");
+		for (i = 0; i < size; i++)
+			printf("x,");
+		printf("};\n");
+		printf("\treturn vec;\n");
+		break;
+	case OP_LOAD_ALIGNED:
+		printf("\tv%sint%dx%d vec;\n", LOSIGN(is_signed), bits, size);
+		puts("\tvec.gcc = *(__typeof__(vec.gcc) *)x;");
+		printf("\treturn vec;\n");
+		break;
+	case OP_LOAD:
+		printf("\tv%sint%dx%d vec;\n", LOSIGN(is_signed), bits, size);
+		puts("\tmemcpy(&vec, x, sizeof(vec));");
+		printf("\treturn vec;\n");
+		break;
+	case OP_STORE_ALIGNED:
+		puts("\t*(__typeof__(vec.gcc) *)arr = vec.gcc;");
+		break;
+	case OP_STORE:
+		puts("\tmemcpy(arr, &vec, sizeof(vec));");
+		break;
+	case OP_ADD:
+	case OP_SUB:
+	case OP_MUL:
+	case OP_DIV:
+	case OP_AND:
+	case OP_OR:
+	case OP_XOR:
+	case OP_CMPLT:
+	case OP_CMPEQ:
+	case OP_CMPGT:
+	case OP_CMPLE:
+	case OP_CMPGE: {
+		const char *op_builtins[OP_CMPGE - OP_ADD + 1] = {"+", "-", "*", /*"/", */"&", "|", "^", "<", "==", ">", "<=", ">="};
+
+		printf("\tvec1.gcc = (vec1.gcc %s vec2.gcc);\n", op_builtins[op - OP_ADD]);
+		printf("\treturn vec1;\n");
+		break;
+	}
+
+	case OP_LSHIFT:
+	case OP_RSHIFT: {
+		const char *op_builtins[OP_RSHIFT - OP_LSHIFT + 1] = {"<<", ">>"};
+
+		printf("\tvec1.gcc = (vec1.gcc %s vec2.gcc);\n", op_builtins[op - OP_LSHIFT]);
+		printf("\treturn vec1;\n");
+		break;
+	}
+
+	case OP_LRSHIFT: {
+		/* sigh */
+		printf("\tvec1.gcc = (__typeof__(vec1.gcc))((vec_uint%d __attribute__((__vector_size__(%d))))vec1.gcc >> vec2.gcc);\n", bits, bits * size / 8);
+		printf("\treturn vec1;\n");
+		break;
+	}
+	case OP_MIN:
+	case OP_MAX: {
+		const char *op_builtins[OP_MAX - OP_MIN + 1] = {"<", ">"};
+
+		printf("\tv%sint%dx%d mask;\n", LOSIGN(is_signed), bits, size);
+		printf("\tmask.gcc = (vec1.gcc %s vec2.gcc);\n", op_builtins[op - OP_MIN]);
+		printf("\tvec1.gcc = (vec1.gcc & mask.gcc) | (vec2.gcc & ~mask.gcc);\n");
+		printf("\treturn vec1;\n");
+		break;
+	}
+	case OP_AVG:
+		if (is_signed) {
+			printf("\tvint%dx%d ones = vint%dx%d_splat(1);\n", bits, size, bits, size);
+			printf("\t__typeof__(vec1.gcc) x_d_rem = (vec1.gcc % 2);\n");
+			printf("\t__typeof__(vec1.gcc) y_d_rem = (vec2.gcc % 2);\n");
+			printf("\t__typeof__(vec1.gcc) rem_d_quot = ((x_d_rem + y_d_rem) / 2);\n");
+			printf("\t__typeof__(vec1.gcc) rem_d_rem = ((x_d_rem + y_d_rem) % 2);\n");
+			puts("");
+			printf("\tvec1.gcc = ((vec1.gcc / 2) + (vec2.gcc / 2)) + (rem_d_quot) + ((rem_d_rem == 1) & ones.gcc);\n");
+		} else {
+			printf("\tvec1.gcc = (vec1.gcc >> 1) + (vec2.gcc >> 1) + ((vec1.gcc | vec2.gcc) & 1);\n");
+		}
+
+		printf("\treturn vec1;\n");
+		break;
+	case OP_NOT:
+		printf("\tvec.gcc = ~vec.gcc;\n");
+		printf("\treturn vec;\n");
+		break;
+	default:
+		printf("#error implement this operation");
+		break;
+	}
+
+	/* end function definition */
+	puts("}");
+
+	printf("# define V%sINT%dx%d_%s_DEFINED\n", UPSIGN(is_signed), bits, size, op_names[op].u);
+	puts("#endif");
+}
+
+static inline void print_ops(int is_signed, int bits, int size)
+{
+	int i;
+
+	printf("\n\n/* v%sint%dx%d */\n\n", (is_signed ? "u" : ""), bits, size);
+
+	for (i = 0; i < OP_FINAL_; i++)
+		print_gcc_op(i, is_signed, bits, size);
+}
+
+static const char *header =
+	"/**\n"
+	" * vec - a tiny SIMD vector library in C99\n"
+	" * \n"
+	" * Copyright (c) 2024-2025 Paper\n"
+	" * \n"
+	" * Permission is hereby granted, free of charge, to any person obtaining a copy\n"
+	" * of this software and associated documentation files (the \"Software\"), to deal\n"
+	" * in the Software without restriction, including without limitation the rights\n"
+	" * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n"
+	" * copies of the Software, and to permit persons to whom the Software is\n"
+	" * furnished to do so, subject to the following conditions:\n"
+	" * \n"
+	" * The above copyright notice and this permission notice shall be included in all\n"
+	" * copies or substantial portions of the Software.\n"
+	" * \n"
+	" * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n"
+	" * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n"
+	" * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n"
+	" * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n"
+	" * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n"
+	" * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n"
+	" * SOFTWARE.\n"
+	"**/\n"
+	"\n"
+	"/* This file is automatically generated! Do not edit it directly!\n"
+	" * Edit the code that generates it in utils/gengcc.c  --paper */\n"
+	"\n"
+	"#ifndef VEC_IMPL_GCC_H_\n"
+	"#define VEC_IMPL_GCC_H_\n"
+	"\n";
+
+static const char *footer = 
+	"#endif /* VEC_IMPL_GCC_H_ */\n";
+
+int main(void)
+{
+	static struct {
+		int bits, size;
+	} defs[] = {
+		/* -- 8-bit */
+		{8, 2},
+		{8, 4},
+		{8, 8},
+		{8, 16},
+		{8, 32},
+		{8, 64},
+
+		/* -- 16-bit */
+		{16, 2},
+		{16, 4},
+		{16, 8},
+		{16, 16},
+		{16, 32},
+
+		/* -- 32-bit */
+		{32, 2},
+		{32, 4},
+		{32, 8},
+		{32, 16},
+
+		/* -- 64-bit */
+		{64, 2},
+		{64, 4},
+		{64, 8},
+	};
+	int i;
+
+	puts(header);
+
+	for (i = 0; i < ARRAY_SIZE(defs); i++) {
+		print_ops(1, defs[i].bits, defs[i].size);
+		print_ops(0, defs[i].bits, defs[i].size);
+	}
+
+	puts(footer);
+}