]> git.xonotic.org Git - xonotic/gmqcc.git/blob - intrin.c
Better exp algorithm from divVerent
[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 **out, const char *name, qcint_t vtype) {
38     ast_value    *value = NULL;
39     ast_function *func  = NULL;
40     char          buffer[1024];
41     char          stype [1024];
42
43     util_snprintf(buffer, sizeof(buffer), "__builtin_%s", name);
44     util_snprintf(stype,  sizeof(stype),   "<%s>",        type_name[vtype]);
45
46     value                    = ast_value_new(intrin_ctx(intrin), buffer, TYPE_FUNCTION);
47     value->intrinsic         = true;
48     value->expression.next   = (ast_expression*)ast_value_new(intrin_ctx(intrin), stype, vtype);
49     func                     = ast_function_new(intrin_ctx(intrin), buffer, value);
50     value->expression.flags |= AST_FLAG_ERASEABLE;
51
52     *out = value;
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_POW_EPSILON 0.00001f
62
63 /*
64  * since some intrinsics depend on each other there is the possibility
65  * that an intrinsic will fail to get a 'depended' function that a
66  * builtin needs, causing some dependency in the chain to have a NULL
67  * function. This will cause a segmentation fault at code generation,
68  * even though an error was raised. To contiue to allow it (instead
69  * of stopping compilation right away). We need to return from the
70  * parser, before compilation stops after all the collected errors.
71  */
72 static ast_expression *intrin_func_self(intrin_t *intrin, const char *name, const char *from);
73 static ast_expression *intrin_nullfunc(intrin_t *intrin) {
74     ast_value    *value = NULL;
75     ast_function *func  = intrin_value(intrin, &value, NULL, TYPE_VOID);
76     intrin_reg(intrin, value, func);
77     return (ast_expression*)value;
78 }
79
80 static ast_expression *intrin_pow(intrin_t *intrin) {
81     /*
82      *
83      * float pow(float base, float exp) {
84      *     float result;
85      *     float low;
86      *     float high;
87      *     float mid;
88      *     float square;
89      *     float accumulate;
90      *
91      *     if (exp == 0.0)
92      *         return 1;
93      *     if (exp == 1.0)
94      *         return base;
95      *     if (exp < 0)
96      *         return 1.0 / pow(base, -exp);
97      *     if (exp >= 1) {
98      *         result = pow(base, exp / 2);
99      *         return result * result;
100      *     }
101      *
102      *     low        = 0.0f;
103      *     high       = 1.0f;
104      *     square     = sqrt(base);
105      *     accumulate = square;
106      *     mid        = high / 2.0f
107      *
108      *     while (fabs(mid - exp) > QC_POW_EPSILON) {
109      *         square = sqrt(square);
110      *         if (mid < exp) {
111      *             low         = mid;
112      *             accumulate *= square;
113      *         } else {
114      *             high        = mid;
115      *             accumulate *= (1.0f / square);
116      *         }
117      *         mid = (low + high) / 2;
118      *     }
119      *     return accumulate;
120      * }
121      */
122     ast_value    *value = NULL;
123     ast_function *func = intrin_value(intrin, &value, "pow", TYPE_FLOAT);
124
125     /* prepare some calls for later */
126     ast_call *callpow1  = ast_call_new(intrin_ctx(intrin), (ast_expression*)value);                  /* for pow(base, -exp)    */
127     ast_call *callpow2  = ast_call_new(intrin_ctx(intrin), (ast_expression*)value);                  /* for pow(vase, exp / 2) */
128     ast_call *callsqrt1 = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "sqrt", "pow")); /* for sqrt(base)         */
129     ast_call *callsqrt2 = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "sqrt", "pow")); /* for sqrt(square)       */
130     ast_call *callfabs  = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "fabs", "pow")); /* for fabs(mid - exp)    */
131
132     /* prepare some blocks for later */
133     ast_block *expgt1       = ast_block_new(intrin_ctx(intrin));
134     ast_block *midltexp     = ast_block_new(intrin_ctx(intrin));
135     ast_block *midltexpelse = ast_block_new(intrin_ctx(intrin));
136     ast_block *whileblock   = ast_block_new(intrin_ctx(intrin));
137
138     /* float pow(float base, float exp) */
139     ast_value    *base = ast_value_new(intrin_ctx(intrin), "base", TYPE_FLOAT);
140     ast_value    *exp  = ast_value_new(intrin_ctx(intrin), "exp",  TYPE_FLOAT);
141     /* { */
142     ast_block    *body = ast_block_new(intrin_ctx(intrin));
143
144     /*
145      * float result;
146      * float low;
147      * float high;
148      * float square;
149      * float accumulate;
150      * float mid;
151      */
152     ast_value *result     = ast_value_new(intrin_ctx(intrin), "result",     TYPE_FLOAT);
153     ast_value *low        = ast_value_new(intrin_ctx(intrin), "low",        TYPE_FLOAT);
154     ast_value *high       = ast_value_new(intrin_ctx(intrin), "high",       TYPE_FLOAT);
155     ast_value *square     = ast_value_new(intrin_ctx(intrin), "square",     TYPE_FLOAT);
156     ast_value *accumulate = ast_value_new(intrin_ctx(intrin), "accumulate", TYPE_FLOAT);
157     ast_value *mid        = ast_value_new(intrin_ctx(intrin), "mid",        TYPE_FLOAT);
158     vec_push(body->locals, result);
159     vec_push(body->locals, low);
160     vec_push(body->locals, high);
161     vec_push(body->locals, square);
162     vec_push(body->locals, accumulate);
163     vec_push(body->locals, mid);
164
165     vec_push(value->expression.params, base);
166     vec_push(value->expression.params, exp);
167
168     /*
169      * if (exp == 0.0)
170      *     return 1;
171      */
172     vec_push(body->exprs,
173         (ast_expression*)ast_ifthen_new(
174             intrin_ctx(intrin),
175             (ast_expression*)ast_binary_new(
176                 intrin_ctx(intrin),
177                 INSTR_EQ_F,
178                 (ast_expression*)exp,
179                 (ast_expression*)intrin->fold->imm_float[0]
180             ),
181             (ast_expression*)ast_return_new(
182                 intrin_ctx(intrin),
183                 (ast_expression*)intrin->fold->imm_float[1]
184             ),
185             NULL
186         )
187     );
188
189     /*
190      * if (exp == 1.0)
191      *     return base;
192      */
193     vec_push(body->exprs,
194         (ast_expression*)ast_ifthen_new(
195             intrin_ctx(intrin),
196             (ast_expression*)ast_binary_new(
197                 intrin_ctx(intrin),
198                 INSTR_EQ_F,
199                 (ast_expression*)exp,
200                 (ast_expression*)intrin->fold->imm_float[1]
201             ),
202             (ast_expression*)ast_return_new(
203                 intrin_ctx(intrin),
204                 (ast_expression*)base
205             ),
206             NULL
207         )
208     );
209
210     /* <callpow1> = pow(base, -exp) */
211     vec_push(callpow1->params, (ast_expression*)base);
212     vec_push(callpow1->params,
213         (ast_expression*)ast_unary_new(
214             intrin_ctx(intrin),
215             VINSTR_NEG_F,
216             (ast_expression*)exp
217         )
218     );
219
220     /*
221      * if (exp < 0)
222      *     return 1.0 / <callpow1>;
223      */
224     vec_push(body->exprs,
225         (ast_expression*)ast_ifthen_new(
226             intrin_ctx(intrin),
227             (ast_expression*)ast_binary_new(
228                 intrin_ctx(intrin),
229                 INSTR_LT,
230                 (ast_expression*)exp,
231                 (ast_expression*)intrin->fold->imm_float[0]
232             ),
233             (ast_expression*)ast_return_new(
234                 intrin_ctx(intrin),
235                 (ast_expression*)ast_binary_new(
236                     intrin_ctx(intrin),
237                     INSTR_DIV_F,
238                     (ast_expression*)intrin->fold->imm_float[1],
239                     (ast_expression*)callpow1
240                 )
241             ),
242             NULL
243         )
244     );
245
246     /* <callpow2> = pow(base, exp / 2) */
247     vec_push(callpow2->params, (ast_expression*)base);
248     vec_push(callpow2->params,
249         (ast_expression*)ast_binary_new(
250             intrin_ctx(intrin),
251             INSTR_DIV_F,
252             (ast_expression*)exp,
253             (ast_expression*)fold_constgen_float(intrin->fold, 2.0f)
254         )
255     );
256
257     /*
258      * <expgt1> = {
259      *     result = <callpow2>;
260      *     return result * result;
261      * }
262      */
263     vec_push(expgt1->exprs,
264         (ast_expression*)ast_store_new(
265             intrin_ctx(intrin),
266             INSTR_STORE_F,
267             (ast_expression*)result,
268             (ast_expression*)callpow2
269         )
270     );
271     vec_push(expgt1->exprs,
272         (ast_expression*)ast_return_new(
273             intrin_ctx(intrin),
274             (ast_expression*)ast_binary_new(
275                 intrin_ctx(intrin),
276                 INSTR_MUL_F,
277                 (ast_expression*)result,
278                 (ast_expression*)result
279             )
280         )
281     );
282
283     /*
284      * if (exp >= 1) {
285      *     <expgt1>
286      * }
287      */
288     vec_push(body->exprs,
289         (ast_expression*)ast_ifthen_new(
290             intrin_ctx(intrin),
291             (ast_expression*)ast_binary_new(
292                 intrin_ctx(intrin),
293                 INSTR_GE,
294                 (ast_expression*)exp,
295                 (ast_expression*)intrin->fold->imm_float[1]
296             ),
297             (ast_expression*)expgt1,
298             NULL
299         )
300     );
301
302     /*
303      * <callsqrt1> = sqrt(base)
304      */
305     vec_push(callsqrt1->params, (ast_expression*)base);
306
307     /*
308      * low        = 0.0f;
309      * high       = 1.0f;
310      * square     = sqrt(base);
311      * accumulate = square;
312      * mid        = high / 2.0f;
313      */
314     vec_push(body->exprs,
315         (ast_expression*)ast_store_new(intrin_ctx(intrin),
316             INSTR_STORE_F,
317             (ast_expression*)low,
318             (ast_expression*)intrin->fold->imm_float[0]
319         )
320     );
321     vec_push(body->exprs,
322         (ast_expression*)ast_store_new(
323             intrin_ctx(intrin),
324             INSTR_STORE_F,
325             (ast_expression*)high,
326             (ast_expression*)intrin->fold->imm_float[1]
327         )
328     );
329     vec_push(body->exprs,
330         (ast_expression*)ast_store_new(
331             intrin_ctx(intrin),
332             INSTR_STORE_F,
333             (ast_expression*)square,
334             (ast_expression*)callsqrt1
335         )
336     );
337     vec_push(body->exprs,
338         (ast_expression*)ast_store_new(
339             intrin_ctx(intrin),
340             INSTR_STORE_F,
341             (ast_expression*)accumulate,
342             (ast_expression*)square
343         )
344     );
345     vec_push(body->exprs,
346         (ast_expression*)ast_store_new(
347             intrin_ctx(intrin),
348             INSTR_STORE_F,
349             (ast_expression*)mid,
350             (ast_expression*)ast_binary_new(
351                 intrin_ctx(intrin),
352                 INSTR_DIV_F,
353                 (ast_expression*)high,
354                 (ast_expression*)fold_constgen_float(intrin->fold, 2.0f)
355             )
356         )
357     );
358
359     /*
360      * <midltexp> = {
361      *     low         = mid;
362      *     accumulate *= square;
363      * }
364      */
365     vec_push(midltexp->exprs,
366         (ast_expression*)ast_store_new(
367             intrin_ctx(intrin),
368             INSTR_STORE_F,
369             (ast_expression*)low,
370             (ast_expression*)mid
371         )
372     );
373     vec_push(midltexp->exprs,
374         (ast_expression*)ast_binstore_new(
375             intrin_ctx(intrin),
376             INSTR_STORE_F,
377             INSTR_MUL_F,
378             (ast_expression*)accumulate,
379             (ast_expression*)square
380         )
381     );
382
383     /*
384      * <midltexpelse> = {
385      *     high        = mid;
386      *     accumulate *= (1.0 / square);
387      * }
388      */
389     vec_push(midltexpelse->exprs,
390         (ast_expression*)ast_store_new(
391             intrin_ctx(intrin),
392             INSTR_STORE_F,
393             (ast_expression*)high,
394             (ast_expression*)mid
395         )
396     );
397     vec_push(midltexpelse->exprs,
398         (ast_expression*)ast_binstore_new(
399             intrin_ctx(intrin),
400             INSTR_STORE_F,
401             INSTR_MUL_F,
402             (ast_expression*)accumulate,
403             (ast_expression*)ast_binary_new(
404                 intrin_ctx(intrin),
405                 INSTR_DIV_F,
406                 (ast_expression*)intrin->fold->imm_float[1],
407                 (ast_expression*)square
408             )
409         )
410     );
411
412     /*
413      * <callsqrt2> = sqrt(square)
414      */
415     vec_push(callsqrt2->params, (ast_expression*)square);
416
417     /*
418      * <whileblock> = {
419      *     square = <callsqrt2>;
420      *     if (mid < exp)
421      *          <midltexp>;
422      *     else
423      *          <midltexpelse>;
424      *
425      *     mid = (low + high) / 2;
426      * }
427      */
428     vec_push(whileblock->exprs,
429         (ast_expression*)ast_store_new(
430             intrin_ctx(intrin),
431             INSTR_STORE_F,
432             (ast_expression*)square,
433             (ast_expression*)callsqrt2
434         )
435     );
436     vec_push(whileblock->exprs,
437         (ast_expression*)ast_ifthen_new(
438             intrin_ctx(intrin),
439             (ast_expression*)ast_binary_new(
440                 intrin_ctx(intrin),
441                 INSTR_LT,
442                 (ast_expression*)mid,
443                 (ast_expression*)exp
444             ),
445             (ast_expression*)midltexp,
446             (ast_expression*)midltexpelse
447         )
448     );
449     vec_push(whileblock->exprs,
450         (ast_expression*)ast_store_new(
451             intrin_ctx(intrin),
452             INSTR_STORE_F,
453             (ast_expression*)mid,
454             (ast_expression*)ast_binary_new(
455                 intrin_ctx(intrin),
456                 INSTR_DIV_F,
457                 (ast_expression*)ast_binary_new(
458                     intrin_ctx(intrin),
459                     INSTR_ADD_F,
460                     (ast_expression*)low,
461                     (ast_expression*)high
462                 ),
463                 (ast_expression*)fold_constgen_float(intrin->fold, 2.0f)
464             )
465         )
466     );
467
468     /*
469      * <callabs> = fabs(mid - exp)
470      */
471     vec_push(callfabs->params,
472         (ast_expression*)ast_binary_new(
473             intrin_ctx(intrin),
474             INSTR_SUB_F,
475             (ast_expression*)mid,
476             (ast_expression*)exp
477         )
478     );
479
480     /*
481      * while (<callfabs>  > epsilon)
482      *     <whileblock>
483      */
484     vec_push(body->exprs,
485         (ast_expression*)ast_loop_new(
486             intrin_ctx(intrin),
487             /* init */
488             NULL,
489             /* pre condition */
490             (ast_expression*)ast_binary_new(
491                 intrin_ctx(intrin),
492                 INSTR_GT,
493                 (ast_expression*)callfabs,
494                 (ast_expression*)fold_constgen_float(intrin->fold, QC_POW_EPSILON)
495             ),
496             /* pre not */
497             false,
498             /* post condition */
499             NULL,
500             /* post not */
501             false,
502             /* increment expression */
503             NULL,
504             /* code block */
505             (ast_expression*)whileblock
506         )
507     );
508
509     /* return accumulate */
510     vec_push(body->exprs,
511         (ast_expression*)ast_return_new(
512             intrin_ctx(intrin),
513             (ast_expression*)accumulate
514         )
515     );
516
517     /* } */
518     vec_push(func->blocks, body);
519
520     intrin_reg(intrin, value, func);
521     return (ast_expression*)value;
522 }
523
524 static ast_expression *intrin_mod(intrin_t *intrin) {
525     /*
526      * float mod(float a, float b) {
527      *     float div = a / b;
528      *     float sign = (div < 0.0f) ? -1 : 1;
529      *     return a - b * sign * floor(sign * div);
530      * }
531      */
532     ast_value    *value = NULL;
533     ast_call     *call  = ast_call_new (intrin_ctx(intrin), intrin_func_self(intrin, "floor", "mod"));
534     ast_value    *a     = ast_value_new(intrin_ctx(intrin), "a",    TYPE_FLOAT);
535     ast_value    *b     = ast_value_new(intrin_ctx(intrin), "b",    TYPE_FLOAT);
536     ast_value    *div   = ast_value_new(intrin_ctx(intrin), "div",  TYPE_FLOAT);
537     ast_value    *sign  = ast_value_new(intrin_ctx(intrin), "sign", TYPE_FLOAT);
538     ast_block    *body  = ast_block_new(intrin_ctx(intrin));
539     ast_function *func  = intrin_value(intrin, &value, "mod", TYPE_FLOAT);
540
541     vec_push(value->expression.params, a);
542     vec_push(value->expression.params, b);
543
544     vec_push(body->locals, div);
545     vec_push(body->locals, sign);
546
547     /* div = a / b; */
548     vec_push(body->exprs,
549         (ast_expression*)ast_store_new(
550             intrin_ctx(intrin),
551             INSTR_STORE_F,
552             (ast_expression*)div,
553             (ast_expression*)ast_binary_new(
554                 intrin_ctx(intrin),
555                 INSTR_DIV_F,
556                 (ast_expression*)a,
557                 (ast_expression*)b
558             )
559         )
560     );
561
562     /* sign = (div < 0.0f) ? -1 : 1; */
563     vec_push(body->exprs,
564         (ast_expression*)ast_store_new(
565             intrin_ctx(intrin),
566             INSTR_STORE_F,
567             (ast_expression*)sign,
568             (ast_expression*)ast_ternary_new(
569                 intrin_ctx(intrin),
570                 (ast_expression*)ast_binary_new(
571                     intrin_ctx(intrin),
572                     INSTR_LT,
573                     (ast_expression*)div,
574                     (ast_expression*)intrin->fold->imm_float[0]
575                 ),
576                 (ast_expression*)intrin->fold->imm_float[2],
577                 (ast_expression*)intrin->fold->imm_float[1]
578             )
579         )
580     );
581
582     /* floor(sign * div) */
583     vec_push(call->params,
584         (ast_expression*)ast_binary_new(
585             intrin_ctx(intrin),
586             INSTR_MUL_F,
587             (ast_expression*)sign,
588             (ast_expression*)div
589         )
590     );
591
592     /* return a - b * sign * <call> */
593     vec_push(body->exprs,
594         (ast_expression*)ast_return_new(
595             intrin_ctx(intrin),
596             (ast_expression*)ast_binary_new(
597                 intrin_ctx(intrin),
598                 INSTR_SUB_F,
599                 (ast_expression*)a,
600                 (ast_expression*)ast_binary_new(
601                     intrin_ctx(intrin),
602                     INSTR_MUL_F,
603                     (ast_expression*)b,
604                     (ast_expression*)ast_binary_new(
605                         intrin_ctx(intrin),
606                         INSTR_MUL_F,
607                         (ast_expression*)sign,
608                         (ast_expression*)call
609                     )
610                 )
611             )
612         )
613     );
614
615     vec_push(func->blocks, body); /* {{{ body }}} */
616     intrin_reg(intrin, value, func);
617
618     return (ast_expression*)value;
619 }
620
621 static ast_expression *intrin_exp(intrin_t *intrin) {
622     /*
623      * float exp(float x) {
624      *     float sum = 1.0;
625      *     float acc = 1.0;
626      *     float i;
627      *     for (i = 1; i < 200; ++i)
628      *         sum += (acc *= x / i);
629      *
630      *     return sum;
631      * }
632      */
633     ast_value    *value = NULL;
634     ast_value    *x     = ast_value_new(intrin_ctx(intrin), "x",   TYPE_FLOAT);
635     ast_value    *sum   = ast_value_new(intrin_ctx(intrin), "sum", TYPE_FLOAT);
636     ast_value    *acc   = ast_value_new(intrin_ctx(intrin), "acc", TYPE_FLOAT);
637     ast_value    *i     = ast_value_new(intrin_ctx(intrin), "i",   TYPE_FLOAT);
638     ast_block    *body  = ast_block_new(intrin_ctx(intrin));
639     ast_function *func  = intrin_value(intrin, &value, "mexp", TYPE_FLOAT);
640
641     vec_push(value->expression.params, x);
642     vec_push(body->locals, sum);
643     vec_push(body->locals, acc);
644     vec_push(body->locals, i);
645
646     /* sum = 1.0; */
647     vec_push(body->exprs,
648         (ast_expression*)ast_store_new(
649             intrin_ctx(intrin),
650             INSTR_STORE_F,
651             (ast_expression*)sum,
652             (ast_expression*)intrin->fold->imm_float[1]
653         )
654     );
655
656     /* acc = 1.0; */
657     vec_push(body->exprs,
658         (ast_expression*)ast_store_new(
659             intrin_ctx(intrin),
660             INSTR_STORE_F,
661             (ast_expression*)acc,
662             (ast_expression*)intrin->fold->imm_float[1]
663         )
664     );
665
666     /*
667      * for (i = 1; i < 200; ++i)
668      *     sum += (acc *= x / i);
669      */
670     vec_push(body->exprs,
671         (ast_expression*)ast_loop_new(
672             intrin_ctx(intrin),
673             /* i = 1; */
674             (ast_expression*)ast_store_new(
675                 intrin_ctx(intrin),
676                 INSTR_STORE_F,
677                 (ast_expression*)i,
678                 (ast_expression*)intrin->fold->imm_float[1]
679             ),
680             /* i < 200; */
681             (ast_expression*)ast_binary_new(
682                 intrin_ctx(intrin),
683                 INSTR_LT,
684                 (ast_expression*)i,
685                 (ast_expression*)fold_constgen_float(intrin->fold, 200.0f)
686             ),
687             false,
688             NULL,
689             false,
690             /* ++i; */
691             (ast_expression*)ast_binstore_new(
692                 intrin_ctx(intrin),
693                 INSTR_STORE_F,
694                 INSTR_ADD_F,
695                 (ast_expression*)i,
696                 (ast_expression*)intrin->fold->imm_float[1]
697             ),
698             /* sum += (acc *= (x / i)) */
699             (ast_expression*)ast_binstore_new(
700                 intrin_ctx(intrin),
701                 INSTR_STORE_F,
702                 INSTR_ADD_F,
703                 (ast_expression*)sum,
704                 (ast_expression*)ast_binstore_new(
705                     intrin_ctx(intrin),
706                     INSTR_STORE_F,
707                     INSTR_MUL_F,
708                     (ast_expression*)acc,
709                     (ast_expression*)ast_binary_new(
710                         intrin_ctx(intrin),
711                         INSTR_DIV_F,
712                         (ast_expression*)x,
713                         (ast_expression*)i
714                     )
715                 )
716             )
717         )
718     );
719
720     /* return sum; */
721     vec_push(body->exprs,
722         (ast_expression*)ast_return_new(
723             intrin_ctx(intrin),
724             (ast_expression*)sum
725         )
726     );
727
728     vec_push(func->blocks, body); /* {{{ body }}} */
729
730     intrin_reg(intrin, value, func);
731     return (ast_expression*)value;
732 }
733
734 static ast_expression *intrin_exp2(intrin_t *intrin) {
735     /*
736      * float exp2(float x) {
737      *     return pow(2, x);
738      * }
739      */
740     ast_value    *value     = NULL;
741     ast_call     *callpow   = ast_call_new (intrin_ctx(intrin), intrin_func_self(intrin, "pow", "exp2"));
742     ast_value    *arg1      = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
743     ast_block    *body      = ast_block_new(intrin_ctx(intrin));
744     ast_function *func      = intrin_value(intrin, &value, "exp2", TYPE_FLOAT);
745
746     vec_push(value->expression.params, arg1);
747
748     vec_push(callpow->params, (ast_expression*)fold_constgen_float(intrin->fold, 2.0f));
749     vec_push(callpow->params, (ast_expression*)arg1);
750
751     /* return <callpow> */
752     vec_push(body->exprs,
753         (ast_expression*)ast_return_new(
754             intrin_ctx(intrin),
755             (ast_expression*)callpow
756         )
757     );
758
759     vec_push(func->blocks, body);
760
761     intrin_reg(intrin, value, func);
762     return (ast_expression*)value;
763 }
764
765 static ast_expression *intrin_isinf(intrin_t *intrin) {
766     /*
767      * float isinf(float x) {
768      *     return (x != 0.0) && (x + x == x);
769      * }
770      */
771     ast_value    *value = NULL;
772     ast_value    *x     = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
773     ast_block    *body  = ast_block_new(intrin_ctx(intrin));
774     ast_function *func  = intrin_value(intrin, &value, "isinf", TYPE_FLOAT);
775
776     vec_push(body->exprs,
777         (ast_expression*)ast_return_new(
778             intrin_ctx(intrin),
779             (ast_expression*)ast_binary_new(
780                 intrin_ctx(intrin),
781                 INSTR_AND,
782                 (ast_expression*)ast_binary_new(
783                     intrin_ctx(intrin),
784                     INSTR_NE_F,
785                     (ast_expression*)x,
786                     (ast_expression*)intrin->fold->imm_float[0]
787                 ),
788                 (ast_expression*)ast_binary_new(
789                     intrin_ctx(intrin),
790                     INSTR_EQ_F,
791                     (ast_expression*)ast_binary_new(
792                         intrin_ctx(intrin),
793                         INSTR_ADD_F,
794                         (ast_expression*)x,
795                         (ast_expression*)x
796                     ),
797                     (ast_expression*)x
798                 )
799             )
800         )
801     );
802
803     vec_push(value->expression.params, x);
804     vec_push(func->blocks, body);
805
806     intrin_reg(intrin, value, func);
807
808     return (ast_expression*)value;
809 }
810
811 static ast_expression *intrin_isnan(intrin_t *intrin) {
812     /*
813      * float isnan(float x) {
814      *   float local;
815      *   local = x;
816      *
817      *   return (x != local);
818      * }
819      */
820     ast_value    *value  = NULL;
821     ast_value    *arg1   = ast_value_new(intrin_ctx(intrin), "x",     TYPE_FLOAT);
822     ast_value    *local  = ast_value_new(intrin_ctx(intrin), "local", TYPE_FLOAT);
823     ast_block    *body   = ast_block_new(intrin_ctx(intrin));
824     ast_function *func   = intrin_value(intrin, &value, "isnan", TYPE_FLOAT);
825
826     vec_push(body->locals, local);
827     vec_push(body->exprs,
828         (ast_expression*)ast_store_new(
829             intrin_ctx(intrin),
830             INSTR_STORE_F,
831             (ast_expression*)local,
832             (ast_expression*)arg1
833         )
834     );
835
836     vec_push(body->exprs,
837         (ast_expression*)ast_return_new(
838             intrin_ctx(intrin),
839             (ast_expression*)ast_binary_new(
840                 intrin_ctx(intrin),
841                 INSTR_NE_F,
842                 (ast_expression*)arg1,
843                 (ast_expression*)local
844             )
845         )
846     );
847
848     vec_push(value->expression.params, arg1);
849     vec_push(func->blocks, body);
850
851     intrin_reg(intrin, value, func);
852
853     return (ast_expression*)value;
854 }
855
856 static ast_expression *intrin_fabs(intrin_t *intrin) {
857     /*
858      * float fabs(float x) {
859      *     return x < 0 ? -x : x;
860      * }
861      */
862     ast_value    *value  = NULL;
863     ast_value    *arg1   = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
864     ast_block    *body   = ast_block_new(intrin_ctx(intrin));
865     ast_function *func   = intrin_value(intrin, &value, "fabs", TYPE_FLOAT);
866
867     vec_push(body->exprs,
868         (ast_expression*)ast_return_new(
869             intrin_ctx(intrin),
870             (ast_expression*)ast_ternary_new(
871                 intrin_ctx(intrin),
872                 (ast_expression*)ast_binary_new(
873                     intrin_ctx(intrin),
874                     INSTR_LE,
875                     (ast_expression*)arg1,
876                     (ast_expression*)intrin->fold->imm_float[0]
877                 ),
878                 (ast_expression*)ast_unary_new(
879                     intrin_ctx(intrin),
880                     VINSTR_NEG_F,
881                     (ast_expression*)arg1
882                 ),
883                 (ast_expression*)arg1
884             )
885         )
886     );
887
888     vec_push(value->expression.params, arg1);
889     vec_push(func->blocks, body);
890
891     intrin_reg(intrin, value, func);
892
893     return (ast_expression*)value;
894 }
895
896 /*
897  * TODO: make static (and handle ast_type_string) here for the builtin
898  * instead of in SYA parse close.
899  */
900 ast_expression *intrin_debug_typestring(intrin_t *intrin) {
901     (void)intrin;
902     return (ast_expression*)0x1;
903 }
904
905 static const intrin_func_t intrinsics[] = {
906     {&intrin_exp,              "__builtin_exp",              "exp",      1},
907     {&intrin_exp2,             "__builtin_exp2",             "exp2",     1},
908     {&intrin_mod,              "__builtin_mod",              "mod",      2},
909     {&intrin_pow,              "__builtin_pow",              "pow",      2},
910     {&intrin_isnan,            "__builtin_isnan",            "isnan",    1},
911     {&intrin_isinf,            "__builtin_isinf",            "isinf",    1},
912     {&intrin_fabs,             "__builtin_fabs",             "fabs",     1},
913     {&intrin_debug_typestring, "__builtin_debug_typestring", "",         0},
914     {&intrin_nullfunc,         "#nullfunc",                  "",         0}
915 };
916
917 static void intrin_error(intrin_t *intrin, const char *fmt, ...) {
918     va_list ap;
919     va_start(ap, fmt);
920     vcompile_error(intrin->parser->lex->tok.ctx, fmt, ap);
921     va_end(ap);
922 }
923
924 /* exposed */
925 intrin_t *intrin_init(parser_t *parser) {
926     intrin_t *intrin = (intrin_t*)mem_a(sizeof(intrin_t));
927     size_t    i;
928
929     intrin->parser     = parser;
930     intrin->fold       = parser->fold;
931     intrin->intrinsics = NULL;
932     intrin->generated  = NULL;
933
934     vec_append(intrin->intrinsics, GMQCC_ARRAY_COUNT(intrinsics), intrinsics);
935
936     /* populate with null pointers for tracking generation */
937     for (i = 0; i < GMQCC_ARRAY_COUNT(intrinsics); i++)
938         vec_push(intrin->generated, NULL);
939
940     return intrin;
941 }
942
943 void intrin_cleanup(intrin_t *intrin) {
944     vec_free(intrin->intrinsics);
945     vec_free(intrin->generated);
946     mem_d(intrin);
947 }
948
949 ast_expression *intrin_fold(intrin_t *intrin, ast_value *value, ast_expression **exprs) {
950     size_t i;
951     if (!value || !value->name)
952         return NULL;
953     for (i = 0; i < vec_size(intrin->intrinsics); i++)
954         if (!strcmp(value->name, intrin->intrinsics[i].name))
955             return (vec_size(exprs) != intrin->intrinsics[i].args)
956                         ? NULL
957                         : fold_intrin(intrin->fold, value->name + 10, exprs);
958     return NULL;
959 }
960
961 static GMQCC_INLINE ast_expression *intrin_func_try(intrin_t *intrin, size_t offset, const char *compare) {
962     size_t i;
963     for (i = 0; i < vec_size(intrin->intrinsics); i++) {
964         if (strcmp(*(char **)((char *)&intrin->intrinsics[i] + offset), compare))
965             continue;
966         if (intrin->generated[i])
967             return intrin->generated[i];
968         return intrin->generated[i] = intrin->intrinsics[i].intrin(intrin);
969     }
970     return NULL;
971 }
972
973 static ast_expression *intrin_func_self(intrin_t *intrin, const char *name, const char *from) {
974     size_t           i;
975     ast_expression  *find;
976
977     /* try current first */
978     if ((find = parser_find_global(intrin->parser, name)) && ((ast_value*)find)->expression.vtype == TYPE_FUNCTION)
979         for (i = 0; i < vec_size(intrin->parser->functions); ++i)
980             if (((ast_value*)find)->name && !strcmp(intrin->parser->functions[i]->name, ((ast_value*)find)->name) && intrin->parser->functions[i]->builtin < 0)
981                 return find;
982     /* try name second */
983     if ((find = intrin_func_try(intrin, offsetof(intrin_func_t, name),  name)))
984         return find;
985     /* try alias third */
986     if ((find = intrin_func_try(intrin, offsetof(intrin_func_t, alias), name)))
987         return find;
988
989     if (from) {
990         intrin_error(intrin, "need function `%s', compiler depends on it for `__builtin_%s'", name, from);
991         return intrin_func_self(intrin, "#nullfunc", NULL);
992     }
993     return NULL;
994 }
995
996 ast_expression *intrin_func(intrin_t *intrin, const char *name) {
997     return intrin_func_self(intrin, name, NULL);
998 }