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