]> git.xonotic.org Git - xonotic/gmqcc.git/blob - intrin.c
26012e78765757dea350ac81e7dd59d0233a962d
[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_VAL(VALUE, NAME, FUNC, STYPE, VTYPE)                    \
36     do {                                                               \
37         (VALUE) = ast_value_new (                                      \
38             parser_ctx(intrin->parser),                                \
39             "__builtin_" NAME,                                         \
40             TYPE_FUNCTION                                              \
41         );                                                             \
42         (VALUE)->expression.next = (ast_expression*)ast_value_new (    \
43             parser_ctx(intrin->parser),                                \
44             STYPE,                                                     \
45             VTYPE                                                      \
46         );                                                             \
47         (FUNC) = ast_function_new (                                    \
48             parser_ctx(intrin->parser),                                \
49             "__builtin_" NAME,                                         \
50             (VALUE)                                                    \
51         );                                                             \
52     } while (0)
53
54 #define INTRIN_REG(FUNC, VALUE)                                        \
55     do {                                                               \
56         vec_push(intrin->parser->functions, (FUNC));                   \
57         vec_push(intrin->parser->globals,   (ast_expression*)(VALUE)); \
58     } while (0)
59
60 #define QC_M_E 2.71828182845905
61
62 static ast_expression *intrin_pow (intrin_t *intrin) {
63     /*
64      * float pow(float x, float y) {
65      *   float local = 1.0f;
66      *   while (y > 0) {
67      *     while (!(y & 1)) {
68      *       y >>= 2;
69      *       x *=  x;
70      *     }
71      *     y--;
72      *     local *= x;
73      *   }
74      *   return local;
75      * }
76      */
77     static ast_value *value = NULL;
78
79     if (!value) {
80         ast_value    *arg1  = ast_value_new(parser_ctx(intrin->parser), "x",     TYPE_FLOAT);
81         ast_value    *arg2  = ast_value_new(parser_ctx(intrin->parser), "y",     TYPE_FLOAT);
82         ast_value    *local = ast_value_new(parser_ctx(intrin->parser), "local", TYPE_FLOAT);
83         ast_block    *body  = ast_block_new(parser_ctx(intrin->parser));
84         ast_block    *l1b   = ast_block_new(parser_ctx(intrin->parser)); /* loop 1 body */
85         ast_block    *l2b   = ast_block_new(parser_ctx(intrin->parser)); /* loop 2 body */
86         ast_loop     *loop1 = NULL;
87         ast_loop     *loop2 = NULL;
88         ast_function *func  = NULL;
89
90         INTRIN_VAL(value, "pow", func, "<float>", TYPE_FLOAT);
91
92         /* arguments */
93         vec_push(value->expression.params, arg1);
94         vec_push(value->expression.params, arg2);
95
96         /* local */
97         vec_push(body->locals, local);
98
99         /* assignment to local of value 1.0f */
100         vec_push(body->exprs,
101             (ast_expression*)ast_store_new (
102                 parser_ctx(intrin->parser),
103                 INSTR_STORE_F,
104                 (ast_expression*)local,
105                 (ast_expression*)intrin->fold->imm_float[1] /* 1 == 1.0f */
106             )
107         );
108
109         /* y >>= 2 */
110         vec_push(l2b->exprs,
111             (ast_expression*)ast_binstore_new (
112                 parser_ctx(intrin->parser),
113                 INSTR_STORE_F,
114                 INSTR_MUL_F,
115                 (ast_expression*)arg2,
116                 (ast_expression*)fold_constgen_float(intrin->parser->fold, 0.25f)
117             )
118         );
119
120         /* x *= x */
121         vec_push(l2b->exprs,
122             (ast_expression*)ast_binstore_new (
123                 parser_ctx(intrin->parser),
124                 INSTR_STORE_F,
125                 INSTR_MUL_F,
126                 (ast_expression*)arg1,
127                 (ast_expression*)arg1
128             )
129         );
130
131         /* while (!(y&1)) */
132         loop2 = ast_loop_new (
133             parser_ctx(intrin->parser),
134             NULL,
135             (ast_expression*)ast_binary_new (
136                 parser_ctx(intrin->parser),
137                 INSTR_AND,
138                 (ast_expression*)arg2,
139                 (ast_expression*)intrin->fold->imm_float[1] /* 1 == 1.0f */
140             ),
141             true, /* ! not */
142             NULL,
143             false,
144             NULL,
145             (ast_expression*)l2b
146         );
147
148         /* push nested loop into loop expressions */
149         vec_push(l1b->exprs, (ast_expression*)loop2);
150
151         /* y-- */
152         vec_push(l1b->exprs,
153             (ast_expression*)ast_binstore_new (
154                 parser_ctx(intrin->parser),
155                 INSTR_STORE_F,
156                 INSTR_SUB_F,
157                 (ast_expression*)arg2,
158                 (ast_expression*)intrin->fold->imm_float[1] /* 1 == 1.0f */
159             )
160         );
161         /* local *= x */
162         vec_push(l1b->exprs,
163             (ast_expression*)ast_binstore_new (
164                 parser_ctx(intrin->parser),
165                 INSTR_STORE_F,
166                 INSTR_MUL_F,
167                 (ast_expression*)local,
168                 (ast_expression*)arg1
169             )
170         );
171
172         /* while (y > 0) */
173         loop1 = ast_loop_new (
174             parser_ctx(intrin->parser),
175             NULL,
176             (ast_expression*)ast_binary_new (
177                 parser_ctx(intrin->parser),
178                 INSTR_GT,
179                 (ast_expression*)arg2,
180                 (ast_expression*)intrin->fold->imm_float[0] /* 0 == 0.0f */
181             ),
182             false,
183             NULL,
184             false,
185             NULL,
186             (ast_expression*)l1b
187         );
188
189         /* push the loop1 into the body for the function */
190         vec_push(body->exprs, (ast_expression*)loop1);
191
192         /* return local; */
193         vec_push(body->exprs,
194             (ast_expression*)ast_return_new (
195                 parser_ctx(intrin->parser),
196                 (ast_expression*)local
197             )
198         );
199
200         /* push block and register intrin for codegen */
201         vec_push(func->blocks, body);
202
203         INTRIN_REG(func, value);
204     }
205
206     return (ast_expression*)value;
207 }
208
209 static ast_expression *intrin_mod(intrin_t *intrin) {
210     /*
211      * float mod(float x, float y) {
212      *   return x - y * floor(x / y);
213      * }
214      */
215     static ast_value *value = NULL;
216
217     if (!value) {
218         ast_call     *call  = ast_call_new (parser_ctx(intrin->parser), intrin_func(intrin, "floor"));
219         ast_value    *arg1  = ast_value_new(parser_ctx(intrin->parser), "x", TYPE_FLOAT);
220         ast_value    *arg2  = ast_value_new(parser_ctx(intrin->parser), "y", TYPE_FLOAT);
221         ast_block    *body  = ast_block_new(parser_ctx(intrin->parser));
222         ast_function *func  = NULL;
223
224         INTRIN_VAL(value, "mod", func, "<float>", TYPE_FLOAT);
225
226         /* floor(x/y) */
227         vec_push(call->params,
228             (ast_expression*)ast_binary_new (
229                 parser_ctx(intrin->parser),
230                 INSTR_DIV_F,
231                 (ast_expression*)arg1,
232                 (ast_expression*)arg2
233             )
234         );
235
236         vec_push(body->exprs,
237             (ast_expression*)ast_return_new(
238                 parser_ctx(intrin->parser),
239                 (ast_expression*)ast_binary_new(
240                     parser_ctx(intrin->parser),
241                     INSTR_SUB_F,
242                     (ast_expression*)arg1,
243                     (ast_expression*)ast_binary_new(
244                         parser_ctx(intrin->parser),
245                         INSTR_MUL_F,
246                         (ast_expression*)arg2,
247                         (ast_expression*)call
248                     )
249                 )
250             )
251         );
252
253         vec_push(value->expression.params, arg1); /* float x (for param) */
254         vec_push(value->expression.params, arg2); /* float y (for param) */
255
256         vec_push(func->blocks,             body); /* {{{ body }}} */
257
258         INTRIN_REG(func, value);
259     }
260
261     return (ast_expression*)value;
262 }
263
264 static ast_expression *intrin_exp(intrin_t *intrin) {
265     /*
266      * float exp(float x) {
267      *     return pow(QC_M_E, x);
268      * }
269      */
270     static ast_value *value = NULL;
271
272     if (!value) {
273         ast_call     *call = ast_call_new (parser_ctx(intrin->parser), intrin_func(intrin, "pow"));
274         ast_value    *arg1 = ast_value_new(parser_ctx(intrin->parser), "x", TYPE_FLOAT);
275         ast_block    *body = ast_block_new(parser_ctx(intrin->parser));
276         ast_function *func = NULL;
277
278         INTRIN_VAL(value, "exp", func, "<float>", TYPE_FLOAT);
279
280         /* push arguments for params to call */
281         vec_push(call->params, (ast_expression*)fold_constgen_float(intrin->fold, QC_M_E));
282         vec_push(call->params, (ast_expression*)arg1);
283
284         /* return pow(QC_M_E, x) */
285         vec_push(body->exprs,
286             (ast_expression*)ast_return_new(
287                 parser_ctx(intrin->parser),
288                 (ast_expression*)call
289             )
290         );
291
292         vec_push(value->expression.params, arg1); /* float x (for param) */
293
294         vec_push(func->blocks,             body); /* {{{ body }}} */
295
296         INTRIN_REG(func, value);
297     }
298
299     return (ast_expression*)value;
300 }
301
302 static ast_expression *intrin_isnan(intrin_t *intrin) {
303     /*
304      * float isnan(float x) {
305      *   float local;
306      *   local = x;
307      *
308      *   return (x != local);
309      * }
310      */
311     static ast_value *value = NULL;
312
313     if (!value) {
314         ast_value    *arg1   = ast_value_new(parser_ctx(intrin->parser), "x",     TYPE_FLOAT);
315         ast_value    *local  = ast_value_new(parser_ctx(intrin->parser), "local", TYPE_FLOAT);
316         ast_block    *body   = ast_block_new(parser_ctx(intrin->parser));
317         ast_function *func   = NULL;
318
319         INTRIN_VAL(value, "isnan", func, "<float>", TYPE_FLOAT);
320
321         vec_push(body->locals, local);
322         vec_push(body->exprs,
323             (ast_expression*)ast_store_new(
324                 parser_ctx(intrin->parser),
325                 INSTR_STORE_F,
326                 (ast_expression*)local,
327                 (ast_expression*)arg1
328             )
329         );
330
331         vec_push(body->exprs,
332             (ast_expression*)ast_return_new(
333                 parser_ctx(intrin->parser),
334                 (ast_expression*)ast_binary_new(
335                     parser_ctx(intrin->parser),
336                     INSTR_NE_F,
337                     (ast_expression*)arg1,
338                     (ast_expression*)local
339                 )
340             )
341         );
342
343         vec_push(value->expression.params, arg1);
344         vec_push(func->blocks, body);
345
346         INTRIN_REG(func, value);
347     }
348
349     return (ast_expression*)value;
350 }
351 #undef INTRIN_REG
352 #undef INTRIN_VAL
353
354 /* 
355  * TODO: make static (and handle ast_type_string) here for the builtin
356  * instead of in SYA parse close.
357  */
358 ast_expression *intrin_debug_typestring(intrin_t *intrin) {
359     (void)intrin;
360     return (ast_expression*)0x1;
361 }
362
363 static const intrin_func_t intrinsics[] = {
364     {&intrin_exp,              "__builtin_exp",              "exp"},
365     {&intrin_mod,              "__builtin_mod",              "mod"},
366     {&intrin_pow,              "__builtin_pow",              "pow"},
367     {&intrin_isnan,            "__builtin_isnan",            "isnan"},
368     {&intrin_debug_typestring, "__builtin_debug_typestring", ""} 
369 };
370
371 static void intrin_error(intrin_t *intrin, const char *fmt, ...) {
372     va_list ap;
373     va_start(ap, fmt);
374     vcompile_error(intrin->parser->lex->tok.ctx, fmt, ap);
375     va_end(ap);
376 }
377
378 /* exposed */
379 intrin_t *intrin_init(parser_t *parser) {
380     intrin_t *intrin = (intrin_t*)mem_a(sizeof(intrin_t));
381     intrin->parser     = parser;
382     intrin->fold       = parser->fold;
383     intrin->intrinsics = NULL;
384
385     vec_append(intrin->intrinsics, sizeof(intrinsics)/sizeof(*intrinsics), intrinsics);
386
387     return intrin;
388 }
389
390 void intrin_cleanup(intrin_t *intrin) {
391     vec_free(intrin->intrinsics);
392     mem_d(intrin);
393 }
394
395 ast_expression *intrin_func(intrin_t *intrin, const char *name) {
396     size_t       i    = 0;
397     void        *find;
398
399     /* try current first */
400     if ((find = (void*)parser_find_global(intrin->parser, name)) && ((ast_value*)find)->expression.vtype == TYPE_FUNCTION)
401         for (i = 0; i < vec_size(intrin->parser->functions); ++i)
402             if (((ast_value*)find)->name && !strcmp(intrin->parser->functions[i]->name, ((ast_value*)find)->name) && intrin->parser->functions[i]->builtin < 0)
403                 return (ast_expression*)find;
404     /* try name second */
405     for (i = 0; i < vec_size(intrin->intrinsics); i++)
406         if (!strcmp(intrin->intrinsics[i].name, name))
407             return intrin->intrinsics[i].intrin(intrin);
408     /* try alias third */
409     for (i = 0; i < vec_size(intrin->intrinsics); i++)
410         if (!strcmp(intrin->intrinsics[i].alias, name))
411             return intrin->intrinsics[i].intrin(intrin);
412
413     intrin_error(intrin, "need function: `%s` compiler depends on it", name);
414     return NULL;
415 }