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