]> git.xonotic.org Git - xonotic/gmqcc.git/blob - intrin.c
Some more platform / compiler specific code refactoring.
[xonotic/gmqcc.git] / intrin.c
1 /*
2  * Copyright (C) 2012, 2013
3  *     Dale Weiler
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of
6  * this software and associated documentation files (the "Software"), to deal in
7  * the Software without restriction, including without limitation the rights to
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9  * of the Software, and to permit persons to whom the Software is furnished to do
10  * so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  */
23 #include <string.h>
24
25 #include "parser.h"
26 #include "platform.h"
27
28 /*
29  * Provides all the "intrinsics" / "builtins" for GMQCC. These can do
30  * a few things, they can provide fall back implementations for math
31  * functions if the definitions don't exist for some given engine. Or
32  * then can determine definitions for existing builtins, and simply
33  * wrap back to them instead.  This is like a "portable" intrface that
34  * is entered when -fintrin is used (causing all existing builtins to
35  * be ignored by the compiler and instead interface through here.
36  */
37 #define intrin_ctx(I) parser_ctx((I)->parser)
38
39 static GMQCC_INLINE ast_function *intrin_value(intrin_t *intrin, ast_value **value, const char *name, qcint_t vtype) {
40     ast_function *func = NULL;
41     char          buffer[1024];
42     char          stype [1024];
43
44     platform_snprintf(buffer, sizeof(buffer), "__builtin_%s", name);
45     platform_snprintf(stype,  sizeof(stype),   "<%s>",        type_name[vtype]);
46
47     *value                      = ast_value_new(intrin_ctx(intrin), buffer, TYPE_FUNCTION);
48     (*value)->intrinsic         = true;
49     (*value)->expression.next   = (ast_expression*)ast_value_new(intrin_ctx(intrin), stype, vtype);
50      func                       = ast_function_new(intrin_ctx(intrin), buffer, *value);
51     (*value)->expression.flags |= AST_FLAG_ERASEABLE;
52
53     return func;
54 }
55
56 static GMQCC_INLINE void intrin_reg(intrin_t *intrin, ast_value *const value, ast_function *const func) {
57     vec_push(intrin->parser->functions, func);
58     vec_push(intrin->parser->globals,   (ast_expression*)value);
59 }
60
61 #define QC_M_E 2.71828182845905f
62
63 static ast_expression *intrin_pow (intrin_t *intrin) {
64     /*
65      * float pow(float x, float y) {
66      *   float local = 1.0f;
67      *   while (y > 0) {
68      *     while (!(y & 1)) {
69      *       y >>= 2;
70      *       x *=  x;
71      *     }
72      *     y--;
73      *     local *= x;
74      *   }
75      *   return local;
76      * }
77      */
78     static ast_value *value = NULL;
79
80     if (!value) {
81         ast_value    *arg1  = ast_value_new(parser_ctx(intrin->parser), "x",     TYPE_FLOAT);
82         ast_value    *arg2  = ast_value_new(parser_ctx(intrin->parser), "y",     TYPE_FLOAT);
83         ast_value    *local = ast_value_new(parser_ctx(intrin->parser), "local", TYPE_FLOAT);
84         ast_block    *body  = ast_block_new(parser_ctx(intrin->parser));
85         ast_block    *l1b   = ast_block_new(parser_ctx(intrin->parser)); /* loop 1 body */
86         ast_block    *l2b   = ast_block_new(parser_ctx(intrin->parser)); /* loop 2 body */
87         ast_loop     *loop1 = NULL;
88         ast_loop     *loop2 = NULL;
89         ast_function *func  = intrin_value(intrin, &value, "pow", TYPE_FLOAT);
90
91         /* arguments */
92         vec_push(value->expression.params, arg1);
93         vec_push(value->expression.params, arg2);
94
95         /* local */
96         vec_push(body->locals, local);
97
98         /* assignment to local of value 1.0f */
99         vec_push(body->exprs,
100             (ast_expression*)ast_store_new (
101                 parser_ctx(intrin->parser),
102                 INSTR_STORE_F,
103                 (ast_expression*)local,
104                 (ast_expression*)intrin->fold->imm_float[1] /* 1 == 1.0f */
105             )
106         );
107
108         /* y >>= 2 */
109         vec_push(l2b->exprs,
110             (ast_expression*)ast_binstore_new (
111                 parser_ctx(intrin->parser),
112                 INSTR_STORE_F,
113                 INSTR_MUL_F,
114                 (ast_expression*)arg2,
115                 (ast_expression*)fold_constgen_float(intrin->parser->fold, 0.25f)
116             )
117         );
118
119         /* x *= x */
120         vec_push(l2b->exprs,
121             (ast_expression*)ast_binstore_new (
122                 parser_ctx(intrin->parser),
123                 INSTR_STORE_F,
124                 INSTR_MUL_F,
125                 (ast_expression*)arg1,
126                 (ast_expression*)arg1
127             )
128         );
129
130         /* while (!(y&1)) */
131         loop2 = ast_loop_new (
132             parser_ctx(intrin->parser),
133             NULL,
134             (ast_expression*)ast_binary_new (
135                 parser_ctx(intrin->parser),
136                 INSTR_AND,
137                 (ast_expression*)arg2,
138                 (ast_expression*)intrin->fold->imm_float[1] /* 1 == 1.0f */
139             ),
140             true, /* ! not */
141             NULL,
142             false,
143             NULL,
144             (ast_expression*)l2b
145         );
146
147         /* push nested loop into loop expressions */
148         vec_push(l1b->exprs, (ast_expression*)loop2);
149
150         /* y-- */
151         vec_push(l1b->exprs,
152             (ast_expression*)ast_binstore_new (
153                 parser_ctx(intrin->parser),
154                 INSTR_STORE_F,
155                 INSTR_SUB_F,
156                 (ast_expression*)arg2,
157                 (ast_expression*)intrin->fold->imm_float[1] /* 1 == 1.0f */
158             )
159         );
160         /* local *= x */
161         vec_push(l1b->exprs,
162             (ast_expression*)ast_binstore_new (
163                 parser_ctx(intrin->parser),
164                 INSTR_STORE_F,
165                 INSTR_MUL_F,
166                 (ast_expression*)local,
167                 (ast_expression*)arg1
168             )
169         );
170
171         /* while (y > 0) */
172         loop1 = ast_loop_new (
173             parser_ctx(intrin->parser),
174             NULL,
175             (ast_expression*)ast_binary_new (
176                 parser_ctx(intrin->parser),
177                 INSTR_GT,
178                 (ast_expression*)arg2,
179                 (ast_expression*)intrin->fold->imm_float[0] /* 0 == 0.0f */
180             ),
181             false,
182             NULL,
183             false,
184             NULL,
185             (ast_expression*)l1b
186         );
187
188         /* push the loop1 into the body for the function */
189         vec_push(body->exprs, (ast_expression*)loop1);
190
191         /* return local; */
192         vec_push(body->exprs,
193             (ast_expression*)ast_return_new (
194                 parser_ctx(intrin->parser),
195                 (ast_expression*)local
196             )
197         );
198
199         /* push block and register intrin for codegen */
200         vec_push(func->blocks, body);
201
202         intrin_reg(intrin, value, func);
203     }
204
205     return (ast_expression*)value;
206 }
207
208 static ast_expression *intrin_mod(intrin_t *intrin) {
209     /*
210      * float mod(float x, float y) {
211      *   return x - y * floor(x / y);
212      * }
213      */
214     static ast_value *value = NULL;
215
216     if (!value) {
217         ast_call     *call  = ast_call_new (parser_ctx(intrin->parser), intrin_func(intrin, "floor"));
218         ast_value    *arg1  = ast_value_new(parser_ctx(intrin->parser), "x", TYPE_FLOAT);
219         ast_value    *arg2  = ast_value_new(parser_ctx(intrin->parser), "y", TYPE_FLOAT);
220         ast_block    *body  = ast_block_new(parser_ctx(intrin->parser));
221         ast_function *func  = intrin_value(intrin, &value, "mod", TYPE_FLOAT);
222
223         /* floor(x/y) */
224         vec_push(call->params,
225             (ast_expression*)ast_binary_new (
226                 parser_ctx(intrin->parser),
227                 INSTR_DIV_F,
228                 (ast_expression*)arg1,
229                 (ast_expression*)arg2
230             )
231         );
232
233         vec_push(body->exprs,
234             (ast_expression*)ast_return_new(
235                 parser_ctx(intrin->parser),
236                 (ast_expression*)ast_binary_new(
237                     parser_ctx(intrin->parser),
238                     INSTR_SUB_F,
239                     (ast_expression*)arg1,
240                     (ast_expression*)ast_binary_new(
241                         parser_ctx(intrin->parser),
242                         INSTR_MUL_F,
243                         (ast_expression*)arg2,
244                         (ast_expression*)call
245                     )
246                 )
247             )
248         );
249
250         vec_push(value->expression.params, arg1); /* float x (for param) */
251         vec_push(value->expression.params, arg2); /* float y (for param) */
252
253         vec_push(func->blocks,             body); /* {{{ body }}} */
254
255         intrin_reg(intrin, value, func);
256     }
257
258     return (ast_expression*)value;
259 }
260
261 static ast_expression *intrin_exp(intrin_t *intrin) {
262     /*
263      * float exp(float x) {
264      *     return pow(QC_M_E, x);
265      * }
266      */
267     static ast_value *value = NULL;
268
269     if (!value) {
270         ast_call     *call = ast_call_new (parser_ctx(intrin->parser), intrin_func(intrin, "pow"));
271         ast_value    *arg1 = ast_value_new(parser_ctx(intrin->parser), "x", TYPE_FLOAT);
272         ast_block    *body = ast_block_new(parser_ctx(intrin->parser));
273         ast_function *func = intrin_value(intrin, &value, "exp", TYPE_FLOAT);
274
275         /* push arguments for params to call */
276         vec_push(call->params, (ast_expression*)fold_constgen_float(intrin->fold, QC_M_E));
277         vec_push(call->params, (ast_expression*)arg1);
278
279         /* return pow(QC_M_E, x) */
280         vec_push(body->exprs,
281             (ast_expression*)ast_return_new(
282                 parser_ctx(intrin->parser),
283                 (ast_expression*)call
284             )
285         );
286
287         vec_push(value->expression.params, arg1); /* float x (for param) */
288
289         vec_push(func->blocks,             body); /* {{{ body }}} */
290
291         intrin_reg(intrin, value, func);
292     }
293
294     return (ast_expression*)value;
295 }
296
297 static ast_expression *intrin_isnan(intrin_t *intrin) {
298     /*
299      * float isnan(float x) {
300      *   float local;
301      *   local = x;
302      *
303      *   return (x != local);
304      * }
305      */
306     static ast_value *value = NULL;
307
308     if (!value) {
309         ast_value    *arg1   = ast_value_new(parser_ctx(intrin->parser), "x",     TYPE_FLOAT);
310         ast_value    *local  = ast_value_new(parser_ctx(intrin->parser), "local", TYPE_FLOAT);
311         ast_block    *body   = ast_block_new(parser_ctx(intrin->parser));
312         ast_function *func   = intrin_value(intrin, &value, "isnan", TYPE_FLOAT);
313
314         vec_push(body->locals, local);
315         vec_push(body->exprs,
316             (ast_expression*)ast_store_new(
317                 parser_ctx(intrin->parser),
318                 INSTR_STORE_F,
319                 (ast_expression*)local,
320                 (ast_expression*)arg1
321             )
322         );
323
324         vec_push(body->exprs,
325             (ast_expression*)ast_return_new(
326                 parser_ctx(intrin->parser),
327                 (ast_expression*)ast_binary_new(
328                     parser_ctx(intrin->parser),
329                     INSTR_NE_F,
330                     (ast_expression*)arg1,
331                     (ast_expression*)local
332                 )
333             )
334         );
335
336         vec_push(value->expression.params, arg1);
337         vec_push(func->blocks, body);
338
339         intrin_reg(intrin, value, func);
340     }
341
342     return (ast_expression*)value;
343 }
344
345 static ast_expression *intrin_fabs(intrin_t *intrin) {
346     /*
347      * float fabs(float x) {
348      *     return x < 0 ? -x : x;
349      * }
350      */
351     static ast_value *value = NULL;
352     if (!value) {
353         ast_value    *arg1   = ast_value_new(parser_ctx(intrin->parser), "x",     TYPE_FLOAT);
354         ast_block    *body   = ast_block_new(parser_ctx(intrin->parser));
355         ast_function *func   = intrin_value(intrin, &value, "fabs", TYPE_FLOAT);
356
357         vec_push(body->exprs,
358             (ast_expression*)ast_return_new(
359                 parser_ctx(intrin->parser),
360                 (ast_expression*)ast_ternary_new(
361                     parser_ctx(intrin->parser),
362                     (ast_expression*)ast_binary_new(
363                         parser_ctx(intrin->parser),
364                         INSTR_LE,
365                         (ast_expression*)arg1,
366                         (ast_expression*)intrin->fold->imm_float[0]
367                     ),
368                     (ast_expression*)ast_binary_new(
369                         parser_ctx(intrin->parser),
370                         INSTR_SUB_F,
371                         (ast_expression*)intrin->fold->imm_float[0],
372                         (ast_expression*)arg1
373                     ),
374                     (ast_expression*)arg1
375                 )
376             )
377         );
378
379         vec_push(value->expression.params, arg1);
380         vec_push(func->blocks, body);
381
382         intrin_reg(intrin, value, func);
383     }
384
385     return (ast_expression*)value;
386 }
387
388 /*
389  * TODO: make static (and handle ast_type_string) here for the builtin
390  * instead of in SYA parse close.
391  */
392 ast_expression *intrin_debug_typestring(intrin_t *intrin) {
393     (void)intrin;
394     return (ast_expression*)0x1;
395 }
396
397 static const intrin_func_t intrinsics[] = {
398     {&intrin_exp,              "__builtin_exp",              "exp",   1},
399     {&intrin_mod,              "__builtin_mod",              "mod",   2},
400     {&intrin_pow,              "__builtin_pow",              "pow",   2},
401     {&intrin_isnan,            "__builtin_isnan",            "isnan", 1},
402     {&intrin_fabs,             "__builtin_fabs",             "fabs",  1},
403     {&intrin_debug_typestring, "__builtin_debug_typestring", "",      0}
404 };
405
406 static void intrin_error(intrin_t *intrin, const char *fmt, ...) {
407     va_list ap;
408     va_start(ap, fmt);
409     vcompile_error(intrin->parser->lex->tok.ctx, fmt, ap);
410     va_end(ap);
411 }
412
413 /* exposed */
414 intrin_t *intrin_init(parser_t *parser) {
415     intrin_t *intrin = (intrin_t*)mem_a(sizeof(intrin_t));
416     intrin->parser     = parser;
417     intrin->fold       = parser->fold;
418     intrin->intrinsics = NULL;
419
420     vec_append(intrin->intrinsics, sizeof(intrinsics)/sizeof(*intrinsics), intrinsics);
421
422     return intrin;
423 }
424
425 void intrin_cleanup(intrin_t *intrin) {
426     vec_free(intrin->intrinsics);
427     mem_d(intrin);
428 }
429
430 ast_expression *intrin_fold(intrin_t *intrin, ast_value *value, ast_expression **exprs) {
431     size_t i;
432
433     if (!value || !value->name)
434         return NULL;
435
436     for (i = 0; i < vec_size(intrin->intrinsics); i++) {
437         if (!strcmp(value->name, intrin->intrinsics[i].name)) {
438             if (intrin->intrinsics[i].args != vec_size(exprs))
439                 return NULL;
440             /* +10 to skip the "__builtin_" substring in the string */
441             return fold_intrin(intrin->fold, value->name + 10, exprs);
442         }
443     }
444
445     return NULL;
446 }
447
448 ast_expression *intrin_func(intrin_t *intrin, const char *name) {
449     size_t       i    = 0;
450     void        *find;
451
452     /* try current first */
453     if ((find = (void*)parser_find_global(intrin->parser, name)) && ((ast_value*)find)->expression.vtype == TYPE_FUNCTION)
454         for (i = 0; i < vec_size(intrin->parser->functions); ++i)
455             if (((ast_value*)find)->name && !strcmp(intrin->parser->functions[i]->name, ((ast_value*)find)->name) && intrin->parser->functions[i]->builtin < 0)
456                 return (ast_expression*)find;
457     /* try name second */
458     for (i = 0; i < vec_size(intrin->intrinsics); i++)
459         if (!strcmp(intrin->intrinsics[i].name, name))
460             return intrin->intrinsics[i].intrin(intrin);
461     /* try alias third */
462     for (i = 0; i < vec_size(intrin->intrinsics); i++)
463         if (!strcmp(intrin->intrinsics[i].alias, name))
464             return intrin->intrinsics[i].intrin(intrin);
465
466     intrin_error(intrin, "need function: `%s` compiler depends on it", name);
467     return NULL;
468 }