comparison 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
comparison
equal deleted inserted replaced
39:f9ca85d2f14c 40:55cadb1fac4b
1 /**
2 * vec - a tiny SIMD vector library in C99
3 *
4 * Copyright (c) 2024-2025 Paper
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 **/
24
25 /* Use this file to generate include/vec/impl/generic.h !!
26 *
27 * `gcc -o gengeneric gengeneric.c` */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <ctype.h>
33
34 #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
35
36 /* ------------------------------------------------------------------------ */
37
38 enum op {
39 /* return vector, take in a integer */
40 OP_SPLAT = 0,
41
42 /* return vector, take in an array */
43 OP_LOAD_ALIGNED,
44 OP_LOAD,
45
46 /* void, take in vector and array */
47 OP_STORE_ALIGNED,
48 OP_STORE,
49
50 /* return vector, takes in two vectors */
51 OP_ADD,
52 OP_SUB,
53 OP_MUL,
54 OP_AND,
55 OP_OR,
56 OP_XOR,
57 OP_CMPLT,
58 OP_CMPEQ,
59 OP_CMPGT,
60 OP_CMPLE,
61 OP_CMPGE,
62 OP_MIN,
63 OP_MAX,
64 OP_AVG,
65
66 /* return vector, takes in a vector and an explicitly unsigned vector */
67 OP_LSHIFT,
68 OP_RSHIFT,
69 OP_LRSHIFT,
70
71 /* return vector, takes in a vector */
72 OP_NOT,
73
74 OP_FINAL_,
75
76 /* operations that have some sort of "caveat" should go here, until
77 * they are fixed or removed */
78
79 OP_DIV, /* this one causes a floating point exception on my machine.
80 * possibly we could change the behavior of divide-by-zero
81 * with some gcc pragma ? --paper */
82 OP_MOD, /* ditto with the above */
83 };
84
85 /* convert op -> string */
86 static struct {
87 const char *u;
88 const char *l;
89 } op_names[] = {
90 [OP_SPLAT] = {"SPLAT", "splat"},
91 [OP_LOAD_ALIGNED] = {"LOAD_ALIGNED", "load_aligned"},
92 [OP_LOAD] = {"LOAD", "load"},
93 [OP_STORE_ALIGNED] = {"STORE_ALIGNED", "store_aligned"},
94 [OP_STORE] = {"STORE", "store"},
95 [OP_ADD] = {"ADD", "add"},
96 [OP_SUB] = {"SUB", "sub"},
97 [OP_MUL] = {"MUL", "mul"},
98 /*[OP_DIV] = {"DIV", "div"},*/
99 [OP_AVG] = {"AVG", "avg"},
100 [OP_AND] = {"AND", "and"},
101 [OP_OR] = {"OR", "or"},
102 [OP_XOR] = {"XOR", "xor"},
103 [OP_NOT] = {"NOT", "not"},
104 [OP_CMPLT] = {"CMPLT", "cmplt"},
105 [OP_CMPEQ] = {"CMPEQ", "cmpeq"},
106 [OP_CMPGT] = {"CMPGT", "cmpgt"},
107 [OP_CMPLE] = {"CMPLE", "cmple"},
108 [OP_CMPGE] = {"CMPGE", "cmpge"},
109 [OP_MIN] = {"MIN", "min"},
110 [OP_MAX] = {"MAX", "max"},
111 [OP_RSHIFT] = {"RSHIFT", "rshift"},
112 [OP_LRSHIFT] = {"LRSHIFT", "lrshift"},
113 [OP_LSHIFT] = {"LSHIFT", "lshift"},
114 };
115
116 #define UPSIGN(x) ((x) ? "" : "U")
117 #define LOSIGN(x) ((x) ? "" : "u")
118
119 static void print_gcc_op(enum op op, int is_signed, int bits, int size)
120 {
121 int i;
122
123 printf("#ifndef V%sINT%dx%d_%s_DEFINED\n", UPSIGN(is_signed), bits, size, op_names[op].u);
124
125 printf("VEC_FUNC_IMPL ");
126
127 /* first; the return value */
128 switch (op) {
129 case OP_SPLAT:
130 case OP_LOAD_ALIGNED:
131 case OP_LOAD:
132 case OP_ADD:
133 case OP_SUB:
134 case OP_MUL:
135 case OP_DIV:
136 case OP_AND:
137 case OP_OR:
138 case OP_XOR:
139 case OP_CMPLT:
140 case OP_CMPEQ:
141 case OP_CMPGT:
142 case OP_CMPLE:
143 case OP_CMPGE:
144 case OP_MIN:
145 case OP_MAX:
146 case OP_AVG:
147 case OP_RSHIFT:
148 case OP_LRSHIFT:
149 case OP_LSHIFT:
150 case OP_NOT:
151 printf("v%sint%dx%d", LOSIGN(is_signed), bits, size);
152 break;
153 case OP_STORE_ALIGNED:
154 case OP_STORE:
155 printf("void");
156 break;
157 }
158
159 /* whitespace and function name */
160 printf(" v%sint%dx%d_%s(", LOSIGN(is_signed), bits, size, op_names[op].l);
161
162 /* parameters */
163 switch (op) {
164 case OP_SPLAT:
165 printf("vec_%sint%d x", LOSIGN(is_signed), bits);
166 break;
167 case OP_LOAD_ALIGNED:
168 case OP_LOAD:
169 printf("const vec_%sint%d x[%d]", LOSIGN(is_signed), bits, size);
170 break;
171 case OP_STORE_ALIGNED:
172 case OP_STORE:
173 printf("v%sint%dx%d vec, vec_%sint%d arr[%d]", LOSIGN(is_signed), bits, size, LOSIGN(is_signed), bits, size);
174 break;
175 case OP_ADD:
176 case OP_SUB:
177 case OP_MUL:
178 case OP_DIV:
179 case OP_AND:
180 case OP_OR:
181 case OP_XOR:
182 case OP_CMPLT:
183 case OP_CMPEQ:
184 case OP_CMPGT:
185 case OP_CMPLE:
186 case OP_CMPGE:
187 case OP_MIN:
188 case OP_MAX:
189 case OP_AVG:
190 printf("v%sint%dx%d vec1, v%sint%dx%d vec2", LOSIGN(is_signed), bits, size, LOSIGN(is_signed), bits, size);
191 break;
192 case OP_RSHIFT:
193 case OP_LRSHIFT:
194 case OP_LSHIFT:
195 printf("v%sint%dx%d vec1, vuint%dx%d vec2", LOSIGN(is_signed), bits, size, bits, size);
196 break;
197 case OP_NOT:
198 printf("v%sint%dx%d vec", LOSIGN(is_signed), bits, size);
199 break;
200 }
201
202 puts(")\n{");
203
204 switch (op) {
205 case OP_SPLAT:
206 printf("\tv%sint%dx%d vec;\n", LOSIGN(is_signed), bits, size);
207 printf("\tvec.gcc = (__typeof__(vec.gcc)){");
208 for (i = 0; i < size; i++)
209 printf("x,");
210 printf("};\n");
211 printf("\treturn vec;\n");
212 break;
213 case OP_LOAD_ALIGNED:
214 printf("\tv%sint%dx%d vec;\n", LOSIGN(is_signed), bits, size);
215 puts("\tvec.gcc = *(__typeof__(vec.gcc) *)x;");
216 printf("\treturn vec;\n");
217 break;
218 case OP_LOAD:
219 printf("\tv%sint%dx%d vec;\n", LOSIGN(is_signed), bits, size);
220 puts("\tmemcpy(&vec, x, sizeof(vec));");
221 printf("\treturn vec;\n");
222 break;
223 case OP_STORE_ALIGNED:
224 puts("\t*(__typeof__(vec.gcc) *)arr = vec.gcc;");
225 break;
226 case OP_STORE:
227 puts("\tmemcpy(arr, &vec, sizeof(vec));");
228 break;
229 case OP_ADD:
230 case OP_SUB:
231 case OP_MUL:
232 case OP_DIV:
233 case OP_AND:
234 case OP_OR:
235 case OP_XOR:
236 case OP_CMPLT:
237 case OP_CMPEQ:
238 case OP_CMPGT:
239 case OP_CMPLE:
240 case OP_CMPGE: {
241 const char *op_builtins[OP_CMPGE - OP_ADD + 1] = {"+", "-", "*", /*"/", */"&", "|", "^", "<", "==", ">", "<=", ">="};
242
243 printf("\tvec1.gcc = (vec1.gcc %s vec2.gcc);\n", op_builtins[op - OP_ADD]);
244 printf("\treturn vec1;\n");
245 break;
246 }
247
248 case OP_LSHIFT:
249 case OP_RSHIFT: {
250 const char *op_builtins[OP_RSHIFT - OP_LSHIFT + 1] = {"<<", ">>"};
251
252 printf("\tvec1.gcc = (vec1.gcc %s vec2.gcc);\n", op_builtins[op - OP_LSHIFT]);
253 printf("\treturn vec1;\n");
254 break;
255 }
256
257 case OP_LRSHIFT: {
258 /* sigh */
259 printf("\tvec1.gcc = (__typeof__(vec1.gcc))((vec_uint%d __attribute__((__vector_size__(%d))))vec1.gcc >> vec2.gcc);\n", bits, bits * size / 8);
260 printf("\treturn vec1;\n");
261 break;
262 }
263 case OP_MIN:
264 case OP_MAX: {
265 const char *op_builtins[OP_MAX - OP_MIN + 1] = {"<", ">"};
266
267 printf("\tv%sint%dx%d mask;\n", LOSIGN(is_signed), bits, size);
268 printf("\tmask.gcc = (vec1.gcc %s vec2.gcc);\n", op_builtins[op - OP_MIN]);
269 printf("\tvec1.gcc = (vec1.gcc & mask.gcc) | (vec2.gcc & ~mask.gcc);\n");
270 printf("\treturn vec1;\n");
271 break;
272 }
273 case OP_AVG:
274 if (is_signed) {
275 printf("\tvint%dx%d ones = vint%dx%d_splat(1);\n", bits, size, bits, size);
276 printf("\t__typeof__(vec1.gcc) x_d_rem = (vec1.gcc % 2);\n");
277 printf("\t__typeof__(vec1.gcc) y_d_rem = (vec2.gcc % 2);\n");
278 printf("\t__typeof__(vec1.gcc) rem_d_quot = ((x_d_rem + y_d_rem) / 2);\n");
279 printf("\t__typeof__(vec1.gcc) rem_d_rem = ((x_d_rem + y_d_rem) % 2);\n");
280 puts("");
281 printf("\tvec1.gcc = ((vec1.gcc / 2) + (vec2.gcc / 2)) + (rem_d_quot) + ((rem_d_rem == 1) & ones.gcc);\n");
282 } else {
283 printf("\tvec1.gcc = (vec1.gcc >> 1) + (vec2.gcc >> 1) + ((vec1.gcc | vec2.gcc) & 1);\n");
284 }
285
286 printf("\treturn vec1;\n");
287 break;
288 case OP_NOT:
289 printf("\tvec.gcc = ~vec.gcc;\n");
290 printf("\treturn vec;\n");
291 break;
292 default:
293 printf("#error implement this operation");
294 break;
295 }
296
297 /* end function definition */
298 puts("}");
299
300 printf("# define V%sINT%dx%d_%s_DEFINED\n", UPSIGN(is_signed), bits, size, op_names[op].u);
301 puts("#endif");
302 }
303
304 static inline void print_ops(int is_signed, int bits, int size)
305 {
306 int i;
307
308 printf("\n\n/* v%sint%dx%d */\n\n", (is_signed ? "u" : ""), bits, size);
309
310 for (i = 0; i < OP_FINAL_; i++)
311 print_gcc_op(i, is_signed, bits, size);
312 }
313
314 static const char *header =
315 "/**\n"
316 " * vec - a tiny SIMD vector library in C99\n"
317 " * \n"
318 " * Copyright (c) 2024-2025 Paper\n"
319 " * \n"
320 " * Permission is hereby granted, free of charge, to any person obtaining a copy\n"
321 " * of this software and associated documentation files (the \"Software\"), to deal\n"
322 " * in the Software without restriction, including without limitation the rights\n"
323 " * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n"
324 " * copies of the Software, and to permit persons to whom the Software is\n"
325 " * furnished to do so, subject to the following conditions:\n"
326 " * \n"
327 " * The above copyright notice and this permission notice shall be included in all\n"
328 " * copies or substantial portions of the Software.\n"
329 " * \n"
330 " * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n"
331 " * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n"
332 " * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n"
333 " * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n"
334 " * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n"
335 " * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n"
336 " * SOFTWARE.\n"
337 "**/\n"
338 "\n"
339 "/* This file is automatically generated! Do not edit it directly!\n"
340 " * Edit the code that generates it in utils/gengcc.c --paper */\n"
341 "\n"
342 "#ifndef VEC_IMPL_GCC_H_\n"
343 "#define VEC_IMPL_GCC_H_\n"
344 "\n";
345
346 static const char *footer =
347 "#endif /* VEC_IMPL_GCC_H_ */\n";
348
349 int main(void)
350 {
351 static struct {
352 int bits, size;
353 } defs[] = {
354 /* -- 8-bit */
355 {8, 2},
356 {8, 4},
357 {8, 8},
358 {8, 16},
359 {8, 32},
360 {8, 64},
361
362 /* -- 16-bit */
363 {16, 2},
364 {16, 4},
365 {16, 8},
366 {16, 16},
367 {16, 32},
368
369 /* -- 32-bit */
370 {32, 2},
371 {32, 4},
372 {32, 8},
373 {32, 16},
374
375 /* -- 64-bit */
376 {64, 2},
377 {64, 4},
378 {64, 8},
379 };
380 int i;
381
382 puts(header);
383
384 for (i = 0; i < ARRAY_SIZE(defs); i++) {
385 print_ops(1, defs[i].bits, defs[i].size);
386 print_ops(0, defs[i].bits, defs[i].size);
387 }
388
389 puts(footer);
390 }