Fix
[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_isfinite(intrin_t *intrin) {
81     /*
82      * float isfinite(float x) {
83      *     return !(isnan(x) || isinf(x));
84      * }
85      */
86     ast_value    *value     = NULL;
87     ast_value    *x         = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
88     ast_function *func      = intrin_value(intrin, &value, "isfinite", TYPE_FLOAT);
89     ast_call     *callisnan = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "isnan", "isfinite"));
90     ast_call     *callisinf = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "isinf", "isfinite"));
91     ast_block    *block     = ast_block_new(intrin_ctx(intrin));
92
93     /* float x; */
94     vec_push(value->expression.params, x);
95
96     /* <callisnan> = isnan(x); */
97     vec_push(callisnan->params, (ast_expression*)x);
98
99     /* <callisinf> = isinf(x); */
100     vec_push(callisinf->params, (ast_expression*)x);
101
102     /* return (!<callisnan> || <callisinf>); */
103     vec_push(block->exprs,
104         (ast_expression*)ast_return_new(
105             intrin_ctx(intrin),
106             (ast_expression*)ast_unary_new(
107                 intrin_ctx(intrin),
108                 INSTR_NOT_F,
109                 (ast_expression*)ast_binary_new(
110                     intrin_ctx(intrin),
111                     INSTR_OR,
112                     (ast_expression*)callisnan,
113                     (ast_expression*)callisinf
114                 )
115             )
116         )
117     );
118
119     vec_push(func->blocks, block);
120     intrin_reg(intrin, value, func);
121
122     return (ast_expression*)value;;
123 }
124
125 static ast_expression *intrin_isinf(intrin_t *intrin) {
126     /*
127      * float isinf(float x) {
128      *     return (x != 0.0) && (x + x == x);
129      * }
130      */
131     ast_value    *value = NULL;
132     ast_value    *x     = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
133     ast_block    *body  = ast_block_new(intrin_ctx(intrin));
134     ast_function *func  = intrin_value(intrin, &value, "isinf", TYPE_FLOAT);
135
136     vec_push(body->exprs,
137         (ast_expression*)ast_return_new(
138             intrin_ctx(intrin),
139             (ast_expression*)ast_binary_new(
140                 intrin_ctx(intrin),
141                 INSTR_AND,
142                 (ast_expression*)ast_binary_new(
143                     intrin_ctx(intrin),
144                     INSTR_NE_F,
145                     (ast_expression*)x,
146                     (ast_expression*)intrin->fold->imm_float[0]
147                 ),
148                 (ast_expression*)ast_binary_new(
149                     intrin_ctx(intrin),
150                     INSTR_EQ_F,
151                     (ast_expression*)ast_binary_new(
152                         intrin_ctx(intrin),
153                         INSTR_ADD_F,
154                         (ast_expression*)x,
155                         (ast_expression*)x
156                     ),
157                     (ast_expression*)x
158                 )
159             )
160         )
161     );
162
163     vec_push(value->expression.params, x);
164     vec_push(func->blocks, body);
165
166     intrin_reg(intrin, value, func);
167
168     return (ast_expression*)value;
169 }
170
171 static ast_expression *intrin_isnan(intrin_t *intrin) {
172     /*
173      * float isnan(float x) {
174      *   float local;
175      *   local = x;
176      *
177      *   return (x != local);
178      * }
179      */
180     ast_value    *value  = NULL;
181     ast_value    *arg1   = ast_value_new(intrin_ctx(intrin), "x",     TYPE_FLOAT);
182     ast_value    *local  = ast_value_new(intrin_ctx(intrin), "local", TYPE_FLOAT);
183     ast_block    *body   = ast_block_new(intrin_ctx(intrin));
184     ast_function *func   = intrin_value(intrin, &value, "isnan", TYPE_FLOAT);
185
186     vec_push(body->locals, local);
187     vec_push(body->exprs,
188         (ast_expression*)ast_store_new(
189             intrin_ctx(intrin),
190             INSTR_STORE_F,
191             (ast_expression*)local,
192             (ast_expression*)arg1
193         )
194     );
195
196     vec_push(body->exprs,
197         (ast_expression*)ast_return_new(
198             intrin_ctx(intrin),
199             (ast_expression*)ast_binary_new(
200                 intrin_ctx(intrin),
201                 INSTR_NE_F,
202                 (ast_expression*)arg1,
203                 (ast_expression*)local
204             )
205         )
206     );
207
208     vec_push(value->expression.params, arg1);
209     vec_push(func->blocks, body);
210
211     intrin_reg(intrin, value, func);
212
213     return (ast_expression*)value;
214 }
215
216 static ast_expression *intrin_isnormal(intrin_t *intrin) {
217     /*
218      * float isnormal(float x) {
219      *     return isfinite(x);
220      * }
221      */
222     ast_value    *value         = NULL;
223     ast_call     *callisfinite  = ast_call_new (intrin_ctx(intrin), intrin_func_self(intrin, "isfinite", "isnormal"));
224     ast_value    *x             = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
225     ast_block    *body          = ast_block_new(intrin_ctx(intrin));
226     ast_function *func          = intrin_value(intrin, &value, "isnormal", TYPE_FLOAT);
227
228     vec_push(value->expression.params, x);
229     vec_push(callisfinite->params, (ast_expression*)x);
230
231     /* return <callisfinite> */
232     vec_push(body->exprs,
233         (ast_expression*)ast_return_new(
234             intrin_ctx(intrin),
235             (ast_expression*)callisfinite
236         )
237     );
238
239     vec_push(func->blocks, body);
240     intrin_reg(intrin, value, func);
241     return (ast_expression*)value;
242 }
243
244 static ast_expression *intrin_signbit(intrin_t *intrin) {
245     /*
246      * float signbit(float x) {
247      *     return (x < 0);
248      * }
249      */
250     ast_value    *value  = NULL;
251     ast_value    *x      = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
252     ast_block    *body   = ast_block_new(intrin_ctx(intrin));
253     ast_function *func   = intrin_value(intrin, &value, "signbit", TYPE_FLOAT);
254
255     vec_push(value->expression.params, x);
256
257     /* return (x < 0); */
258     vec_push(body->exprs,
259         (ast_expression*)ast_return_new(
260             intrin_ctx(intrin),
261             (ast_expression*)ast_ternary_new(
262                 intrin_ctx(intrin),
263                 (ast_expression*)ast_binary_new(
264                     intrin_ctx(intrin),
265                     INSTR_LT,
266                     (ast_expression*)x,
267                     (ast_expression*)intrin->fold->imm_float[0]
268                 ),
269                 (ast_expression*)intrin->fold->imm_float[1],
270                 (ast_expression*)intrin->fold->imm_float[0]
271             )
272         )
273     );
274
275     vec_push(func->blocks, body);
276     intrin_reg(intrin, value, func);
277     return (ast_expression*)value;
278 }
279
280 static ast_expression *intrin_acosh(intrin_t *intrin) {
281     /*
282      * float acosh(float x) {
283      *     return log(x + sqrt((x * x) - 1));
284      * }
285      */
286     ast_value    *value    = NULL;
287     ast_value    *x        = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
288     ast_call     *calllog  = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "log", "acosh"));
289     ast_call     *callsqrt = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "sqrt", "acosh"));
290     ast_block    *body     = ast_block_new(intrin_ctx(intrin));
291     ast_function *func     = intrin_value(intrin, &value, "acosh", TYPE_FLOAT);
292
293     vec_push(value->expression.params, x);
294
295     /* <callsqrt> = sqrt((x * x) - 1); */
296     vec_push(callsqrt->params,
297         (ast_expression*)ast_binary_new(
298             intrin_ctx(intrin),
299             INSTR_SUB_F,
300             (ast_expression*)ast_binary_new(
301                 intrin_ctx(intrin),
302                 INSTR_MUL_F,
303                 (ast_expression*)x,
304                 (ast_expression*)x
305             ),
306             (ast_expression*)intrin->fold->imm_float[1]
307         )
308     );
309
310     /* <calllog> = log(x + <callsqrt>); */
311     vec_push(calllog->params,
312         (ast_expression*)ast_binary_new(
313             intrin_ctx(intrin),
314             INSTR_ADD_F,
315             (ast_expression*)x,
316             (ast_expression*)callsqrt
317         )
318     );
319
320     /* return <calllog>; */
321     vec_push(body->exprs,
322         (ast_expression*)ast_return_new(
323             intrin_ctx(intrin),
324             (ast_expression*)calllog
325         )
326     );
327
328     vec_push(func->blocks, body);
329     intrin_reg(intrin, value, func);
330     return (ast_expression*)value;
331 }
332
333 static ast_expression *intrin_asinh(intrin_t *intrin) {
334     /*
335      * float asinh(float x) {
336      *     return log(x + sqrt((x * x) + 1));
337      * }
338      */
339     ast_value    *value    = NULL;
340     ast_value    *x        = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
341     ast_call     *calllog  = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "log", "asinh"));
342     ast_call     *callsqrt = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "sqrt", "asinh"));
343     ast_block    *body     = ast_block_new(intrin_ctx(intrin));
344     ast_function *func     = intrin_value(intrin, &value, "asinh", TYPE_FLOAT);
345
346     vec_push(value->expression.params, x);
347
348     /* <callsqrt> = sqrt((x * x) + 1); */
349     vec_push(callsqrt->params,
350         (ast_expression*)ast_binary_new(
351             intrin_ctx(intrin),
352             INSTR_ADD_F,
353             (ast_expression*)ast_binary_new(
354                 intrin_ctx(intrin),
355                 INSTR_MUL_F,
356                 (ast_expression*)x,
357                 (ast_expression*)x
358             ),
359             (ast_expression*)intrin->fold->imm_float[1]
360         )
361     );
362
363     /* <calllog> = log(x + <callsqrt>); */
364     vec_push(calllog->params,
365         (ast_expression*)ast_binary_new(
366             intrin_ctx(intrin),
367             INSTR_ADD_F,
368             (ast_expression*)x,
369             (ast_expression*)callsqrt
370         )
371     );
372
373     /* return <calllog>; */
374     vec_push(body->exprs,
375         (ast_expression*)ast_return_new(
376             intrin_ctx(intrin),
377             (ast_expression*)calllog
378         )
379     );
380
381     vec_push(func->blocks, body);
382     intrin_reg(intrin, value, func);
383     return (ast_expression*)value;
384 }
385
386 static ast_expression *intrin_atanh(intrin_t *intrin) {
387     /*
388      * float atanh(float x) {
389      *     return 0.5 * log((1 + x) / (1 - x))
390      * }
391      */
392     ast_value    *value   = NULL;
393     ast_value    *x       = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
394     ast_call     *calllog = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "log", "atanh"));
395     ast_block    *body    = ast_block_new(intrin_ctx(intrin));
396     ast_function *func    = intrin_value(intrin, &value, "atanh", TYPE_FLOAT);
397
398     vec_push(value->expression.params, x);
399
400     /* <callog> = log((1 + x) / (1 - x)); */
401     vec_push(calllog->params,
402         (ast_expression*)ast_binary_new(
403             intrin_ctx(intrin),
404             INSTR_DIV_F,
405             (ast_expression*)ast_binary_new(
406                 intrin_ctx(intrin),
407                 INSTR_ADD_F,
408                 (ast_expression*)intrin->fold->imm_float[1],
409                 (ast_expression*)x
410             ),
411             (ast_expression*)ast_binary_new(
412                 intrin_ctx(intrin),
413                 INSTR_SUB_F,
414                 (ast_expression*)intrin->fold->imm_float[1],
415                 (ast_expression*)x
416             )
417         )
418     );
419
420     /* return 0.5 * <calllog>; */
421     vec_push(body->exprs,
422         (ast_expression*)ast_binary_new(
423             intrin_ctx(intrin),
424             INSTR_MUL_F,
425             (ast_expression*)fold_constgen_float(intrin->fold, 0.5),
426             (ast_expression*)calllog
427         )
428     );
429
430     vec_push(func->blocks, body);
431     intrin_reg(intrin, value, func);
432     return (ast_expression*)value;
433 }
434
435 static ast_expression *intrin_exp(intrin_t *intrin) {
436     /*
437      * float exp(float x) {
438      *     float sum = 1.0;
439      *     float acc = 1.0;
440      *     float i;
441      *     for (i = 1; i < 200; ++i)
442      *         sum += (acc *= x / i);
443      *
444      *     return sum;
445      * }
446      */
447     ast_value    *value = NULL;
448     ast_value    *x     = ast_value_new(intrin_ctx(intrin), "x",   TYPE_FLOAT);
449     ast_value    *sum   = ast_value_new(intrin_ctx(intrin), "sum", TYPE_FLOAT);
450     ast_value    *acc   = ast_value_new(intrin_ctx(intrin), "acc", TYPE_FLOAT);
451     ast_value    *i     = ast_value_new(intrin_ctx(intrin), "i",   TYPE_FLOAT);
452     ast_block    *body  = ast_block_new(intrin_ctx(intrin));
453     ast_function *func  = intrin_value(intrin, &value, "exp", TYPE_FLOAT);
454
455     vec_push(value->expression.params, x);
456     vec_push(body->locals, sum);
457     vec_push(body->locals, acc);
458     vec_push(body->locals, i);
459
460     /* sum = 1.0; */
461     vec_push(body->exprs,
462         (ast_expression*)ast_store_new(
463             intrin_ctx(intrin),
464             INSTR_STORE_F,
465             (ast_expression*)sum,
466             (ast_expression*)intrin->fold->imm_float[1]
467         )
468     );
469
470     /* acc = 1.0; */
471     vec_push(body->exprs,
472         (ast_expression*)ast_store_new(
473             intrin_ctx(intrin),
474             INSTR_STORE_F,
475             (ast_expression*)acc,
476             (ast_expression*)intrin->fold->imm_float[1]
477         )
478     );
479
480     /*
481      * for (i = 1; i < 200; ++i)
482      *     sum += (acc *= x / i);
483      */
484     vec_push(body->exprs,
485         (ast_expression*)ast_loop_new(
486             intrin_ctx(intrin),
487             /* i = 1; */
488             (ast_expression*)ast_store_new(
489                 intrin_ctx(intrin),
490                 INSTR_STORE_F,
491                 (ast_expression*)i,
492                 (ast_expression*)intrin->fold->imm_float[1]
493             ),
494             /* i < 200; */
495             (ast_expression*)ast_binary_new(
496                 intrin_ctx(intrin),
497                 INSTR_LT,
498                 (ast_expression*)i,
499                 (ast_expression*)fold_constgen_float(intrin->fold, 200.0f)
500             ),
501             false,
502             NULL,
503             false,
504             /* ++i; */
505             (ast_expression*)ast_binstore_new(
506                 intrin_ctx(intrin),
507                 INSTR_STORE_F,
508                 INSTR_ADD_F,
509                 (ast_expression*)i,
510                 (ast_expression*)intrin->fold->imm_float[1]
511             ),
512             /* sum += (acc *= (x / i)) */
513             (ast_expression*)ast_binstore_new(
514                 intrin_ctx(intrin),
515                 INSTR_STORE_F,
516                 INSTR_ADD_F,
517                 (ast_expression*)sum,
518                 (ast_expression*)ast_binstore_new(
519                     intrin_ctx(intrin),
520                     INSTR_STORE_F,
521                     INSTR_MUL_F,
522                     (ast_expression*)acc,
523                     (ast_expression*)ast_binary_new(
524                         intrin_ctx(intrin),
525                         INSTR_DIV_F,
526                         (ast_expression*)x,
527                         (ast_expression*)i
528                     )
529                 )
530             )
531         )
532     );
533
534     /* return sum; */
535     vec_push(body->exprs,
536         (ast_expression*)ast_return_new(
537             intrin_ctx(intrin),
538             (ast_expression*)sum
539         )
540     );
541
542     vec_push(func->blocks, body);
543
544     intrin_reg(intrin, value, func);
545     return (ast_expression*)value;
546 }
547
548 static ast_expression *intrin_exp2(intrin_t *intrin) {
549     /*
550      * float exp2(float x) {
551      *     return pow(2, x);
552      * }
553      */
554     ast_value    *value     = NULL;
555     ast_call     *callpow   = ast_call_new (intrin_ctx(intrin), intrin_func_self(intrin, "pow", "exp2"));
556     ast_value    *arg1      = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
557     ast_block    *body      = ast_block_new(intrin_ctx(intrin));
558     ast_function *func      = intrin_value(intrin, &value, "exp2", TYPE_FLOAT);
559
560     vec_push(value->expression.params, arg1);
561
562     vec_push(callpow->params, (ast_expression*)fold_constgen_float(intrin->fold, 2.0f));
563     vec_push(callpow->params, (ast_expression*)arg1);
564
565     /* return <callpow> */
566     vec_push(body->exprs,
567         (ast_expression*)ast_return_new(
568             intrin_ctx(intrin),
569             (ast_expression*)callpow
570         )
571     );
572
573     vec_push(func->blocks, body);
574
575     intrin_reg(intrin, value, func);
576     return (ast_expression*)value;
577 }
578
579 static ast_expression *intrin_expm1(intrin_t *intrin) {
580     /*
581      * float expm1(float x) {
582      *     return exp(x) - 1;
583      * }
584      */
585     ast_value    *value    = NULL;
586     ast_call     *callexp  = ast_call_new (intrin_ctx(intrin), intrin_func_self(intrin, "exp", "expm1"));
587     ast_value    *x        = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
588     ast_block    *body     = ast_block_new(intrin_ctx(intrin));
589     ast_function *func     = intrin_value(intrin, &value, "expm1", TYPE_FLOAT);
590
591     vec_push(value->expression.params, x);
592
593     /* <callexp> = exp(x); */
594     vec_push(callexp->params, (ast_expression*)x);
595
596     /* return <callexp> - 1; */
597     vec_push(body->exprs,
598         (ast_expression*)ast_return_new(
599             intrin_ctx(intrin),
600             (ast_expression*)ast_binary_new(
601                 intrin_ctx(intrin),
602                 INSTR_SUB_F,
603                 (ast_expression*)callexp,
604                 (ast_expression*)intrin->fold->imm_float[1]
605             )
606         )
607     );
608
609     vec_push(func->blocks, body);
610     intrin_reg(intrin, value, func);
611     return (ast_expression*)value;
612 }
613
614 static ast_expression *intrin_pow(intrin_t *intrin) {
615     /*
616      *
617      * float pow(float base, float exp) {
618      *     float result;
619      *     float low;
620      *     float high;
621      *     float mid;
622      *     float square;
623      *     float accumulate;
624      *
625      *     if (exp == 0.0)
626      *         return 1;
627      *     if (exp == 1.0)
628      *         return base;
629      *     if (exp < 0)
630      *         return 1.0 / pow(base, -exp);
631      *     if (exp >= 1) {
632      *         result = pow(base, exp / 2);
633      *         return result * result;
634      *     }
635      *
636      *     low        = 0.0f;
637      *     high       = 1.0f;
638      *     square     = sqrt(base);
639      *     accumulate = square;
640      *     mid        = high / 2.0f
641      *
642      *     while (fabs(mid - exp) > QC_POW_EPSILON) {
643      *         square = sqrt(square);
644      *         if (mid < exp) {
645      *             low         = mid;
646      *             accumulate *= square;
647      *         } else {
648      *             high        = mid;
649      *             accumulate *= (1.0f / square);
650      *         }
651      *         mid = (low + high) / 2;
652      *     }
653      *     return accumulate;
654      * }
655      */
656     ast_value    *value = NULL;
657     ast_function *func = intrin_value(intrin, &value, "pow", TYPE_FLOAT);
658
659     /* prepare some calls for later */
660     ast_call *callpow1  = ast_call_new(intrin_ctx(intrin), (ast_expression*)value);                  /* for pow(base, -exp)    */
661     ast_call *callpow2  = ast_call_new(intrin_ctx(intrin), (ast_expression*)value);                  /* for pow(vase, exp / 2) */
662     ast_call *callsqrt1 = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "sqrt", "pow")); /* for sqrt(base)         */
663     ast_call *callsqrt2 = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "sqrt", "pow")); /* for sqrt(square)       */
664     ast_call *callfabs  = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "fabs", "pow")); /* for fabs(mid - exp)    */
665
666     /* prepare some blocks for later */
667     ast_block *expgt1       = ast_block_new(intrin_ctx(intrin));
668     ast_block *midltexp     = ast_block_new(intrin_ctx(intrin));
669     ast_block *midltexpelse = ast_block_new(intrin_ctx(intrin));
670     ast_block *whileblock   = ast_block_new(intrin_ctx(intrin));
671
672     /* float pow(float base, float exp) */
673     ast_value    *base = ast_value_new(intrin_ctx(intrin), "base", TYPE_FLOAT);
674     ast_value    *exp  = ast_value_new(intrin_ctx(intrin), "exp",  TYPE_FLOAT);
675     /* { */
676     ast_block    *body = ast_block_new(intrin_ctx(intrin));
677
678     /*
679      * float result;
680      * float low;
681      * float high;
682      * float square;
683      * float accumulate;
684      * float mid;
685      */
686     ast_value *result     = ast_value_new(intrin_ctx(intrin), "result",     TYPE_FLOAT);
687     ast_value *low        = ast_value_new(intrin_ctx(intrin), "low",        TYPE_FLOAT);
688     ast_value *high       = ast_value_new(intrin_ctx(intrin), "high",       TYPE_FLOAT);
689     ast_value *square     = ast_value_new(intrin_ctx(intrin), "square",     TYPE_FLOAT);
690     ast_value *accumulate = ast_value_new(intrin_ctx(intrin), "accumulate", TYPE_FLOAT);
691     ast_value *mid        = ast_value_new(intrin_ctx(intrin), "mid",        TYPE_FLOAT);
692     vec_push(body->locals, result);
693     vec_push(body->locals, low);
694     vec_push(body->locals, high);
695     vec_push(body->locals, square);
696     vec_push(body->locals, accumulate);
697     vec_push(body->locals, mid);
698
699     vec_push(value->expression.params, base);
700     vec_push(value->expression.params, exp);
701
702     /*
703      * if (exp == 0.0)
704      *     return 1;
705      */
706     vec_push(body->exprs,
707         (ast_expression*)ast_ifthen_new(
708             intrin_ctx(intrin),
709             (ast_expression*)ast_binary_new(
710                 intrin_ctx(intrin),
711                 INSTR_EQ_F,
712                 (ast_expression*)exp,
713                 (ast_expression*)intrin->fold->imm_float[0]
714             ),
715             (ast_expression*)ast_return_new(
716                 intrin_ctx(intrin),
717                 (ast_expression*)intrin->fold->imm_float[1]
718             ),
719             NULL
720         )
721     );
722
723     /*
724      * if (exp == 1.0)
725      *     return base;
726      */
727     vec_push(body->exprs,
728         (ast_expression*)ast_ifthen_new(
729             intrin_ctx(intrin),
730             (ast_expression*)ast_binary_new(
731                 intrin_ctx(intrin),
732                 INSTR_EQ_F,
733                 (ast_expression*)exp,
734                 (ast_expression*)intrin->fold->imm_float[1]
735             ),
736             (ast_expression*)ast_return_new(
737                 intrin_ctx(intrin),
738                 (ast_expression*)base
739             ),
740             NULL
741         )
742     );
743
744     /* <callpow1> = pow(base, -exp) */
745     vec_push(callpow1->params, (ast_expression*)base);
746     vec_push(callpow1->params,
747         (ast_expression*)ast_unary_new(
748             intrin_ctx(intrin),
749             VINSTR_NEG_F,
750             (ast_expression*)exp
751         )
752     );
753
754     /*
755      * if (exp < 0)
756      *     return 1.0 / <callpow1>;
757      */
758     vec_push(body->exprs,
759         (ast_expression*)ast_ifthen_new(
760             intrin_ctx(intrin),
761             (ast_expression*)ast_binary_new(
762                 intrin_ctx(intrin),
763                 INSTR_LT,
764                 (ast_expression*)exp,
765                 (ast_expression*)intrin->fold->imm_float[0]
766             ),
767             (ast_expression*)ast_return_new(
768                 intrin_ctx(intrin),
769                 (ast_expression*)ast_binary_new(
770                     intrin_ctx(intrin),
771                     INSTR_DIV_F,
772                     (ast_expression*)intrin->fold->imm_float[1],
773                     (ast_expression*)callpow1
774                 )
775             ),
776             NULL
777         )
778     );
779
780     /* <callpow2> = pow(base, exp / 2) */
781     vec_push(callpow2->params, (ast_expression*)base);
782     vec_push(callpow2->params,
783         (ast_expression*)ast_binary_new(
784             intrin_ctx(intrin),
785             INSTR_DIV_F,
786             (ast_expression*)exp,
787             (ast_expression*)fold_constgen_float(intrin->fold, 2.0f)
788         )
789     );
790
791     /*
792      * <expgt1> = {
793      *     result = <callpow2>;
794      *     return result * result;
795      * }
796      */
797     vec_push(expgt1->exprs,
798         (ast_expression*)ast_store_new(
799             intrin_ctx(intrin),
800             INSTR_STORE_F,
801             (ast_expression*)result,
802             (ast_expression*)callpow2
803         )
804     );
805     vec_push(expgt1->exprs,
806         (ast_expression*)ast_return_new(
807             intrin_ctx(intrin),
808             (ast_expression*)ast_binary_new(
809                 intrin_ctx(intrin),
810                 INSTR_MUL_F,
811                 (ast_expression*)result,
812                 (ast_expression*)result
813             )
814         )
815     );
816
817     /*
818      * if (exp >= 1) {
819      *     <expgt1>
820      * }
821      */
822     vec_push(body->exprs,
823         (ast_expression*)ast_ifthen_new(
824             intrin_ctx(intrin),
825             (ast_expression*)ast_binary_new(
826                 intrin_ctx(intrin),
827                 INSTR_GE,
828                 (ast_expression*)exp,
829                 (ast_expression*)intrin->fold->imm_float[1]
830             ),
831             (ast_expression*)expgt1,
832             NULL
833         )
834     );
835
836     /*
837      * <callsqrt1> = sqrt(base)
838      */
839     vec_push(callsqrt1->params, (ast_expression*)base);
840
841     /*
842      * low        = 0.0f;
843      * high       = 1.0f;
844      * square     = sqrt(base);
845      * accumulate = square;
846      * mid        = high / 2.0f;
847      */
848     vec_push(body->exprs,
849         (ast_expression*)ast_store_new(intrin_ctx(intrin),
850             INSTR_STORE_F,
851             (ast_expression*)low,
852             (ast_expression*)intrin->fold->imm_float[0]
853         )
854     );
855     vec_push(body->exprs,
856         (ast_expression*)ast_store_new(
857             intrin_ctx(intrin),
858             INSTR_STORE_F,
859             (ast_expression*)high,
860             (ast_expression*)intrin->fold->imm_float[1]
861         )
862     );
863
864     vec_push(body->exprs,
865         (ast_expression*)ast_store_new(
866             intrin_ctx(intrin),
867             INSTR_STORE_F,
868             (ast_expression*)square,
869             (ast_expression*)callsqrt1
870         )
871     );
872
873     vec_push(body->exprs,
874         (ast_expression*)ast_store_new(
875             intrin_ctx(intrin),
876             INSTR_STORE_F,
877             (ast_expression*)accumulate,
878             (ast_expression*)square
879         )
880     );
881     vec_push(body->exprs,
882         (ast_expression*)ast_store_new(
883             intrin_ctx(intrin),
884             INSTR_STORE_F,
885             (ast_expression*)mid,
886             (ast_expression*)ast_binary_new(
887                 intrin_ctx(intrin),
888                 INSTR_DIV_F,
889                 (ast_expression*)high,
890                 (ast_expression*)fold_constgen_float(intrin->fold, 2.0f)
891             )
892         )
893     );
894
895     /*
896      * <midltexp> = {
897      *     low         = mid;
898      *     accumulate *= square;
899      * }
900      */
901     vec_push(midltexp->exprs,
902         (ast_expression*)ast_store_new(
903             intrin_ctx(intrin),
904             INSTR_STORE_F,
905             (ast_expression*)low,
906             (ast_expression*)mid
907         )
908     );
909     vec_push(midltexp->exprs,
910         (ast_expression*)ast_binstore_new(
911             intrin_ctx(intrin),
912             INSTR_STORE_F,
913             INSTR_MUL_F,
914             (ast_expression*)accumulate,
915             (ast_expression*)square
916         )
917     );
918
919     /*
920      * <midltexpelse> = {
921      *     high        = mid;
922      *     accumulate *= (1.0 / square);
923      * }
924      */
925     vec_push(midltexpelse->exprs,
926         (ast_expression*)ast_store_new(
927             intrin_ctx(intrin),
928             INSTR_STORE_F,
929             (ast_expression*)high,
930             (ast_expression*)mid
931         )
932     );
933     vec_push(midltexpelse->exprs,
934         (ast_expression*)ast_binstore_new(
935             intrin_ctx(intrin),
936             INSTR_STORE_F,
937             INSTR_MUL_F,
938             (ast_expression*)accumulate,
939             (ast_expression*)ast_binary_new(
940                 intrin_ctx(intrin),
941                 INSTR_DIV_F,
942                 (ast_expression*)intrin->fold->imm_float[1],
943                 (ast_expression*)square
944             )
945         )
946     );
947
948     /*
949      * <callsqrt2> = sqrt(square)
950      */
951     vec_push(callsqrt2->params, (ast_expression*)square);
952
953     /*
954      * <whileblock> = {
955      *     square = <callsqrt2>;
956      *     if (mid < exp)
957      *          <midltexp>;
958      *     else
959      *          <midltexpelse>;
960      *
961      *     mid = (low + high) / 2;
962      * }
963      */
964     vec_push(whileblock->exprs,
965         (ast_expression*)ast_store_new(
966             intrin_ctx(intrin),
967             INSTR_STORE_F,
968             (ast_expression*)square,
969             (ast_expression*)callsqrt2
970         )
971     );
972     vec_push(whileblock->exprs,
973         (ast_expression*)ast_ifthen_new(
974             intrin_ctx(intrin),
975             (ast_expression*)ast_binary_new(
976                 intrin_ctx(intrin),
977                 INSTR_LT,
978                 (ast_expression*)mid,
979                 (ast_expression*)exp
980             ),
981             (ast_expression*)midltexp,
982             (ast_expression*)midltexpelse
983         )
984     );
985     vec_push(whileblock->exprs,
986         (ast_expression*)ast_store_new(
987             intrin_ctx(intrin),
988             INSTR_STORE_F,
989             (ast_expression*)mid,
990             (ast_expression*)ast_binary_new(
991                 intrin_ctx(intrin),
992                 INSTR_DIV_F,
993                 (ast_expression*)ast_binary_new(
994                     intrin_ctx(intrin),
995                     INSTR_ADD_F,
996                     (ast_expression*)low,
997                     (ast_expression*)high
998                 ),
999                 (ast_expression*)fold_constgen_float(intrin->fold, 2.0f)
1000             )
1001         )
1002     );
1003
1004     /*
1005      * <callabs> = fabs(mid - exp)
1006      */
1007     vec_push(callfabs->params,
1008         (ast_expression*)ast_binary_new(
1009             intrin_ctx(intrin),
1010             INSTR_SUB_F,
1011             (ast_expression*)mid,
1012             (ast_expression*)exp
1013         )
1014     );
1015
1016     /*
1017      * while (<callfabs>  > epsilon)
1018      *     <whileblock>
1019      */
1020     vec_push(body->exprs,
1021         (ast_expression*)ast_loop_new(
1022             intrin_ctx(intrin),
1023             /* init */
1024             NULL,
1025             /* pre condition */
1026             (ast_expression*)ast_binary_new(
1027                 intrin_ctx(intrin),
1028                 INSTR_GT,
1029                 (ast_expression*)callfabs,
1030                 (ast_expression*)fold_constgen_float(intrin->fold, QC_POW_EPSILON)
1031             ),
1032             /* pre not */
1033             false,
1034             /* post condition */
1035             NULL,
1036             /* post not */
1037             false,
1038             /* increment expression */
1039             NULL,
1040             /* code block */
1041             (ast_expression*)whileblock
1042         )
1043     );
1044
1045     /* return accumulate */
1046     vec_push(body->exprs,
1047         (ast_expression*)ast_return_new(
1048             intrin_ctx(intrin),
1049             (ast_expression*)accumulate
1050         )
1051     );
1052
1053     /* } */
1054     vec_push(func->blocks, body);
1055
1056     intrin_reg(intrin, value, func);
1057     return (ast_expression*)value;
1058 }
1059
1060 static ast_expression *intrin_mod(intrin_t *intrin) {
1061     /*
1062      * float mod(float a, float b) {
1063      *     float div = a / b;
1064      *     float sign = (div < 0.0f) ? -1 : 1;
1065      *     return a - b * sign * floor(sign * div);
1066      * }
1067      */
1068     ast_value    *value = NULL;
1069     ast_call     *call  = ast_call_new (intrin_ctx(intrin), intrin_func_self(intrin, "floor", "mod"));
1070     ast_value    *a     = ast_value_new(intrin_ctx(intrin), "a",    TYPE_FLOAT);
1071     ast_value    *b     = ast_value_new(intrin_ctx(intrin), "b",    TYPE_FLOAT);
1072     ast_value    *div   = ast_value_new(intrin_ctx(intrin), "div",  TYPE_FLOAT);
1073     ast_value    *sign  = ast_value_new(intrin_ctx(intrin), "sign", TYPE_FLOAT);
1074     ast_block    *body  = ast_block_new(intrin_ctx(intrin));
1075     ast_function *func  = intrin_value(intrin, &value, "mod", TYPE_FLOAT);
1076
1077     vec_push(value->expression.params, a);
1078     vec_push(value->expression.params, b);
1079
1080     vec_push(body->locals, div);
1081     vec_push(body->locals, sign);
1082
1083     /* div = a / b; */
1084     vec_push(body->exprs,
1085         (ast_expression*)ast_store_new(
1086             intrin_ctx(intrin),
1087             INSTR_STORE_F,
1088             (ast_expression*)div,
1089             (ast_expression*)ast_binary_new(
1090                 intrin_ctx(intrin),
1091                 INSTR_DIV_F,
1092                 (ast_expression*)a,
1093                 (ast_expression*)b
1094             )
1095         )
1096     );
1097
1098     /* sign = (div < 0.0f) ? -1 : 1; */
1099     vec_push(body->exprs,
1100         (ast_expression*)ast_store_new(
1101             intrin_ctx(intrin),
1102             INSTR_STORE_F,
1103             (ast_expression*)sign,
1104             (ast_expression*)ast_ternary_new(
1105                 intrin_ctx(intrin),
1106                 (ast_expression*)ast_binary_new(
1107                     intrin_ctx(intrin),
1108                     INSTR_LT,
1109                     (ast_expression*)div,
1110                     (ast_expression*)intrin->fold->imm_float[0]
1111                 ),
1112                 (ast_expression*)intrin->fold->imm_float[2],
1113                 (ast_expression*)intrin->fold->imm_float[1]
1114             )
1115         )
1116     );
1117
1118     /* floor(sign * div) */
1119     vec_push(call->params,
1120         (ast_expression*)ast_binary_new(
1121             intrin_ctx(intrin),
1122             INSTR_MUL_F,
1123             (ast_expression*)sign,
1124             (ast_expression*)div
1125         )
1126     );
1127
1128     /* return a - b * sign * <call> */
1129     vec_push(body->exprs,
1130         (ast_expression*)ast_return_new(
1131             intrin_ctx(intrin),
1132             (ast_expression*)ast_binary_new(
1133                 intrin_ctx(intrin),
1134                 INSTR_SUB_F,
1135                 (ast_expression*)a,
1136                 (ast_expression*)ast_binary_new(
1137                     intrin_ctx(intrin),
1138                     INSTR_MUL_F,
1139                     (ast_expression*)b,
1140                     (ast_expression*)ast_binary_new(
1141                         intrin_ctx(intrin),
1142                         INSTR_MUL_F,
1143                         (ast_expression*)sign,
1144                         (ast_expression*)call
1145                     )
1146                 )
1147             )
1148         )
1149     );
1150
1151     vec_push(func->blocks, body);
1152     intrin_reg(intrin, value, func);
1153
1154     return (ast_expression*)value;
1155 }
1156
1157 static ast_expression *intrin_fabs(intrin_t *intrin) {
1158     /*
1159      * float fabs(float x) {
1160      *     return x < 0 ? -x : x;
1161      * }
1162      */
1163     ast_value    *value  = NULL;
1164     ast_value    *arg1   = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
1165     ast_block    *body   = ast_block_new(intrin_ctx(intrin));
1166     ast_function *func   = intrin_value(intrin, &value, "fabs", TYPE_FLOAT);
1167
1168     vec_push(body->exprs,
1169         (ast_expression*)ast_return_new(
1170             intrin_ctx(intrin),
1171             (ast_expression*)ast_ternary_new(
1172                 intrin_ctx(intrin),
1173                 (ast_expression*)ast_binary_new(
1174                     intrin_ctx(intrin),
1175                     INSTR_LE,
1176                     (ast_expression*)arg1,
1177                     (ast_expression*)intrin->fold->imm_float[0]
1178                 ),
1179                 (ast_expression*)ast_unary_new(
1180                     intrin_ctx(intrin),
1181                     VINSTR_NEG_F,
1182                     (ast_expression*)arg1
1183                 ),
1184                 (ast_expression*)arg1
1185             )
1186         )
1187     );
1188
1189     vec_push(value->expression.params, arg1);
1190     vec_push(func->blocks, body);
1191
1192     intrin_reg(intrin, value, func);
1193
1194     return (ast_expression*)value;
1195 }
1196
1197 static ast_expression *intrin_epsilon(intrin_t *intrin) {
1198     /*
1199      * float epsilon(void) {
1200      *     float eps = 1.0f;
1201      *     do { eps /= 2.0f; } while ((1.0f + (eps / 2.0f)) != 1.0f);
1202      *     return eps;
1203      * }
1204      */
1205     ast_value    *value  = NULL;
1206     ast_value    *eps    = ast_value_new(intrin_ctx(intrin), "eps", TYPE_FLOAT);
1207     ast_block    *body   = ast_block_new(intrin_ctx(intrin));
1208     ast_function *func   = intrin_value(intrin, &value, "epsilon", TYPE_FLOAT);
1209
1210     vec_push(body->locals, eps);
1211
1212     /* eps = 1.0f; */
1213     vec_push(body->exprs,
1214         (ast_expression*)ast_store_new(
1215             intrin_ctx(intrin),
1216             INSTR_STORE_F,
1217             (ast_expression*)eps,
1218             (ast_expression*)intrin->fold->imm_float[0]
1219         )
1220     );
1221
1222     vec_push(body->exprs,
1223         (ast_expression*)ast_loop_new(
1224             intrin_ctx(intrin),
1225             NULL,
1226             NULL,
1227             false,
1228             (ast_expression*)ast_binary_new(
1229                 intrin_ctx(intrin),
1230                 INSTR_NE_F,
1231                 (ast_expression*)ast_binary_new(
1232                     intrin_ctx(intrin),
1233                     INSTR_ADD_F,
1234                     (ast_expression*)intrin->fold->imm_float[1],
1235                     (ast_expression*)ast_binary_new(
1236                         intrin_ctx(intrin),
1237                         INSTR_MUL_F,
1238                         (ast_expression*)eps,
1239                         (ast_expression*)fold_constgen_float(intrin->fold, 2.0f)
1240                     )
1241                 ),
1242                 (ast_expression*)intrin->fold->imm_float[1]
1243             ),
1244             false,
1245             NULL,
1246             (ast_expression*)ast_binstore_new(
1247                 intrin_ctx(intrin),
1248                 INSTR_STORE_F,
1249                 INSTR_DIV_F,
1250                 (ast_expression*)eps,
1251                 (ast_expression*)fold_constgen_float(intrin->fold, 2.0f)
1252             )
1253         )
1254     );
1255
1256     /* return eps; */
1257     vec_push(body->exprs,
1258         (ast_expression*)ast_return_new(
1259             intrin_ctx(intrin),
1260             (ast_expression*)eps
1261         )
1262     );
1263
1264     vec_push(func->blocks, body);
1265     intrin_reg(intrin, value, func);
1266
1267     return (ast_expression*)value;
1268 }
1269
1270 static ast_expression *intrin_nan(intrin_t *intrin) {
1271     /*
1272      * float nan(void) {
1273      *     float x = 0.0f;
1274      *     return x / x;
1275      * }
1276      */
1277     ast_value    *value  = NULL;
1278     ast_value    *x      = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
1279     ast_function *func   = intrin_value(intrin, &value, "nan", TYPE_FLOAT);
1280     ast_block    *block  = ast_block_new(intrin_ctx(intrin));
1281
1282     vec_push(block->locals, x);
1283
1284     vec_push(block->exprs,
1285         (ast_expression*)ast_store_new(
1286             intrin_ctx(intrin),
1287             INSTR_STORE_F,
1288             (ast_expression*)x,
1289             (ast_expression*)intrin->fold->imm_float[0]
1290         )
1291     );
1292
1293     vec_push(block->exprs,
1294         (ast_expression*)ast_return_new(
1295             intrin_ctx(intrin),
1296             (ast_expression*)ast_binary_new(
1297                 intrin_ctx(intrin),
1298                 INSTR_DIV_F,
1299                 (ast_expression*)x,
1300                 (ast_expression*)x
1301             )
1302         )
1303     );
1304
1305     vec_push(func->blocks, block);
1306     intrin_reg(intrin, value, func);
1307
1308     return (ast_expression*)value;
1309 }
1310
1311 static ast_expression *intrin_inf(intrin_t *intrin) {
1312     /*
1313      * float nan(void) {
1314      *     float x = 1.0f;
1315      *     float y = 0.0f;
1316      *     return x / y;
1317      * }
1318      */
1319     ast_value    *value  = NULL;
1320     ast_value    *x      = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
1321     ast_value    *y      = ast_value_new(intrin_ctx(intrin), "y", TYPE_FLOAT);
1322     ast_function *func   = intrin_value(intrin, &value, "inf", TYPE_FLOAT);
1323     ast_block    *block  = ast_block_new(intrin_ctx(intrin));
1324     size_t        i;
1325
1326     vec_push(block->locals, x);
1327     vec_push(block->locals, y);
1328
1329     /* to keep code size down */
1330     for (i = 0; i <= 1; i++) {
1331         vec_push(block->exprs,
1332             (ast_expression*)ast_store_new(
1333                 intrin_ctx(intrin),
1334                 INSTR_STORE_F,
1335                 (ast_expression*)((i == 0) ? x : y),
1336                 (ast_expression*)intrin->fold->imm_float[i]
1337             )
1338         );
1339     }
1340
1341     vec_push(block->exprs,
1342         (ast_expression*)ast_return_new(
1343             intrin_ctx(intrin),
1344             (ast_expression*)ast_binary_new(
1345                 intrin_ctx(intrin),
1346                 INSTR_DIV_F,
1347                 (ast_expression*)x,
1348                 (ast_expression*)y
1349             )
1350         )
1351     );
1352
1353     vec_push(func->blocks, block);
1354     intrin_reg(intrin, value, func);
1355
1356     return (ast_expression*)value;
1357 }
1358
1359 /*
1360  * TODO: make static (and handle ast_type_string) here for the builtin
1361  * instead of in SYA parse close.
1362  */
1363 ast_expression *intrin_debug_typestring(intrin_t *intrin) {
1364     (void)intrin;
1365     return (ast_expression*)0x1;
1366 }
1367
1368 static const intrin_func_t intrinsics[] = {
1369     {&intrin_isfinite,         "__builtin_isfinite",         "isfinite", 1},
1370     {&intrin_isinf,            "__builtin_isinf",            "isinf",    1},
1371     {&intrin_isnan,            "__builtin_isnan",            "isnan",    1},
1372     {&intrin_isnormal,         "__builtin_isnormal",         "isnormal", 1},
1373     {&intrin_signbit,          "__builtin_signbit",          "signbit",  1},
1374     {&intrin_acosh,            "__builtin_acosh",            "acosh",    1},
1375     {&intrin_asinh,            "__builtin_asinh",            "asinh",    1},
1376     {&intrin_atanh,            "__builtin_atanh",            "atanh",    1},
1377     {&intrin_exp,              "__builtin_exp",              "exp",      1},
1378     {&intrin_exp2,             "__builtin_exp2",             "exp2",     1},
1379     {&intrin_expm1,            "__builtin_expm1",            "expm1",    1},
1380     {&intrin_mod,              "__builtin_mod",              "mod",      2},
1381     {&intrin_pow,              "__builtin_pow",              "pow",      2},
1382     {&intrin_fabs,             "__builtin_fabs",             "fabs",     1},
1383     {&intrin_epsilon,          "__builtin_epsilon",          "",         0},
1384     {&intrin_nan,              "__builtin_nan",              "",         0},
1385     {&intrin_inf,              "__builtin_inf",              "",         0},
1386     {&intrin_debug_typestring, "__builtin_debug_typestring", "",         0},
1387     {&intrin_nullfunc,         "#nullfunc",                  "",         0}
1388 };
1389
1390 static void intrin_error(intrin_t *intrin, const char *fmt, ...) {
1391     va_list ap;
1392     va_start(ap, fmt);
1393     vcompile_error(intrin->parser->lex->tok.ctx, fmt, ap);
1394     va_end(ap);
1395 }
1396
1397 /* exposed */
1398 intrin_t *intrin_init(parser_t *parser) {
1399     intrin_t *intrin = (intrin_t*)mem_a(sizeof(intrin_t));
1400     size_t    i;
1401
1402     intrin->parser     = parser;
1403     intrin->fold       = parser->fold;
1404     intrin->intrinsics = NULL;
1405     intrin->generated  = NULL;
1406
1407     vec_append(intrin->intrinsics, GMQCC_ARRAY_COUNT(intrinsics), intrinsics);
1408
1409     /* populate with null pointers for tracking generation */
1410     for (i = 0; i < GMQCC_ARRAY_COUNT(intrinsics); i++)
1411         vec_push(intrin->generated, NULL);
1412
1413     return intrin;
1414 }
1415
1416 void intrin_cleanup(intrin_t *intrin) {
1417     vec_free(intrin->intrinsics);
1418     vec_free(intrin->generated);
1419     mem_d(intrin);
1420 }
1421
1422 ast_expression *intrin_fold(intrin_t *intrin, ast_value *value, ast_expression **exprs) {
1423     size_t i;
1424     if (!value || !value->name)
1425         return NULL;
1426     for (i = 0; i < vec_size(intrin->intrinsics); i++)
1427         if (!strcmp(value->name, intrin->intrinsics[i].name))
1428             return (vec_size(exprs) != intrin->intrinsics[i].args)
1429                         ? NULL
1430                         : fold_intrin(intrin->fold, value->name + 10, exprs);
1431     return NULL;
1432 }
1433
1434 static GMQCC_INLINE ast_expression *intrin_func_try(intrin_t *intrin, size_t offset, const char *compare) {
1435     size_t i;
1436     for (i = 0; i < vec_size(intrin->intrinsics); i++) {
1437         if (strcmp(*(char **)((char *)&intrin->intrinsics[i] + offset), compare))
1438             continue;
1439         if (intrin->generated[i])
1440             return intrin->generated[i];
1441         return intrin->generated[i] = intrin->intrinsics[i].intrin(intrin);
1442     }
1443     return NULL;
1444 }
1445
1446 static ast_expression *intrin_func_self(intrin_t *intrin, const char *name, const char *from) {
1447     size_t           i;
1448     ast_expression  *find;
1449
1450     /* try current first */
1451     if ((find = parser_find_global(intrin->parser, name)) && ((ast_value*)find)->expression.vtype == TYPE_FUNCTION)
1452         for (i = 0; i < vec_size(intrin->parser->functions); ++i)
1453             if (((ast_value*)find)->name && !strcmp(intrin->parser->functions[i]->name, ((ast_value*)find)->name) && intrin->parser->functions[i]->builtin < 0)
1454                 return find;
1455     /* try name second */
1456     if ((find = intrin_func_try(intrin, offsetof(intrin_func_t, name),  name)))
1457         return find;
1458     /* try alias third */
1459     if ((find = intrin_func_try(intrin, offsetof(intrin_func_t, alias), name)))
1460         return find;
1461
1462     if (from) {
1463         intrin_error(intrin, "need function `%s', compiler depends on it for `__builtin_%s'", name, from);
1464         return intrin_func_self(intrin, "#nullfunc", NULL);
1465     }
1466     return NULL;
1467 }
1468
1469 ast_expression *intrin_func(intrin_t *intrin, const char *name) {
1470     return intrin_func_self(intrin, name, NULL);
1471 }