]> git.rm.cloudns.org Git - xonotic/gmqcc.git/commitdiff
More compiler intrinsics for math functions
authorDale Weiler <killfieldengine@gmail.com>
Sun, 24 Nov 2013 02:14:13 +0000 (21:14 -0500)
committerDale Weiler <killfieldengine@gmail.com>
Sun, 24 Nov 2013 02:14:13 +0000 (21:14 -0500)
fold.c
intrin.c

diff --git a/fold.c b/fold.c
index 823184f0024129337e684328dc20e34090f2b265..60517a7e57ae578f9c8521c26802aeaf8acda119 100644 (file)
--- a/fold.c
+++ b/fold.c
@@ -701,51 +701,69 @@ ast_expression *fold_op(fold_t *fold, const oper_info *info, ast_expression **op
  * folding, primarly: individual functions for each intrinsics to fold,
  * and a generic selection function.
  */
-static GMQCC_INLINE ast_expression *fold_intrin_mod(fold_t *fold, ast_value *lhs, ast_value *rhs) {
-    return fold_constgen_float(
-                fold,
-                fmodf(
-                    fold_immvalue_float(lhs),
-                    fold_immvalue_float(rhs)
-                )
-            );
+static GMQCC_INLINE ast_expression *fold_intrin_isfinite(fold_t *fold, ast_value *a) {
+    return fold_constgen_float(fold, isfinite(fold_immvalue_float(a)));
 }
-
-static GMQCC_INLINE ast_expression *fold_intrin_pow(fold_t *fold, ast_value *lhs, ast_value *rhs) {
-    return fold_constgen_float(
-                fold,
-                powf(
-                    fold_immvalue_float(lhs),
-                    fold_immvalue_float(rhs)
-                )
-            );
+static GMQCC_INLINE ast_expression *fold_intrin_isinf(fold_t *fold, ast_value *a) {
+    return fold_constgen_float(fold, isinff(fold_immvalue_float(a)));
 }
-
-static GMQCC_INLINE ast_expression *fold_intrin_exp(fold_t *fold, ast_value *value) {
-    return fold_constgen_float(fold, exp(fold_immvalue_float(value)));
+static GMQCC_INLINE ast_expression *fold_intrin_isnan(fold_t *fold, ast_value *a) {
+    return fold_constgen_float(fold, isnanf(fold_immvalue_float(a)));
 }
-
-static GMQCC_INLINE ast_expression *fold_intrin_exp2(fold_t *fold, ast_value *value) {
-    return fold_constgen_float(fold, pow(2, fold_immvalue_float(value)));
+static GMQCC_INLINE ast_expression *fold_intrin_isnormal(fold_t *fold, ast_value *a) {
+    return fold_constgen_float(fold, isnormal(fold_immvalue_float(a)));
 }
-
-static GMQCC_INLINE ast_expression *fold_intrin_isnan(fold_t *fold, ast_value *value) {
-    return fold_constgen_float(fold, isnan(fold_immvalue_float(value)) != 0.0f);
+static GMQCC_INLINE ast_expression *fold_intrin_signbit(fold_t *fold, ast_value *a) {
+    return fold_constgen_float(fold, signbit(fold_immvalue_float(a)));
 }
-
-static GMQCC_INLINE ast_expression *fold_intrin_fabs(fold_t *fold, ast_value *value) {
-    return fold_constgen_float(fold, fabs(fold_immvalue_float(value)));
+static GMQCC_INLINE ast_expression *fold_intirn_acosh(fold_t *fold, ast_value *a) {
+    return fold_constgen_float(fold, acoshf(fold_immvalue_float(a)));
+}
+static GMQCC_INLINE ast_expression *fold_intrin_asinh(fold_t *fold, ast_value *a) {
+    return fold_constgen_float(fold, asinhf(fold_immvalue_float(a)));
+}
+static GMQCC_INLINE ast_expression *fold_intrin_atanh(fold_t *fold, ast_value *a) {
+    return fold_constgen_float(fold, atanhf(fold_immvalue_float(a)));
+}
+static GMQCC_INLINE ast_expression *fold_intrin_exp(fold_t *fold, ast_value *a) {
+    return fold_constgen_float(fold, expf(fold_immvalue_float(a)));
+}
+static GMQCC_INLINE ast_expression *fold_intrin_exp2(fold_t *fold, ast_value *a) {
+    return fold_constgen_float(fold, exp2f(fold_immvalue_float(a)));
+}
+static GMQCC_INLINE ast_expression *fold_intrin_expm1(fold_t *fold, ast_value *a) {
+    return fold_constgen_float(fold, expm1f(fold_immvalue_float(a)));
+}
+static GMQCC_INLINE ast_expression *fold_intrin_mod(fold_t *fold, ast_value *lhs, ast_value *rhs) {
+    return fold_constgen_float(fold, fmodf(fold_immvalue_float(lhs), fold_immvalue_float(rhs)));
+}
+static GMQCC_INLINE ast_expression *fold_intrin_pow(fold_t *fold, ast_value *lhs, ast_value *rhs) {
+    return fold_constgen_float(fold, powf(fold_immvalue_float(lhs), fold_immvalue_float(rhs)));
+}
+static GMQCC_INLINE ast_expression *fold_intrin_fabs(fold_t *fold, ast_value *a) {
+    return fold_constgen_float(fold, fabsf(fold_immvalue_float(a)));
 }
 
+
 ast_expression *fold_intrin(fold_t *fold, const char *intrin, ast_expression **arg) {
     ast_expression *ret = NULL;
-
-    if (!strcmp(intrin, "mod"))   ret = fold_intrin_mod  (fold, (ast_value*)arg[0], (ast_value*)arg[1]);
-    if (!strcmp(intrin, "pow"))   ret = fold_intrin_pow  (fold, (ast_value*)arg[0], (ast_value*)arg[1]);
-    if (!strcmp(intrin, "exp"))   ret = fold_intrin_exp  (fold, (ast_value*)arg[0]);
-    if (!strcmp(intrin, "exp2"))  ret = fold_intrin_exp2 (fold, (ast_value*)arg[0]);
-    if (!strcmp(intrin, "isnan")) ret = fold_intrin_isnan(fold, (ast_value*)arg[0]);
-    if (!strcmp(intrin, "fabs"))  ret = fold_intrin_fabs (fold, (ast_value*)arg[0]);
+    ast_value      *a   = (ast_value*)arg[0];
+    ast_value      *b   = (ast_value*)arg[1];
+
+    if (!strcmp(intrin, "isfinite")) ret = fold_intrin_isfinite(fold, a);
+    if (!strcmp(intrin, "isinf"))    ret = fold_intrin_isinf(fold, a);
+    if (!strcmp(intrin, "isnan"))    ret = fold_intrin_isnan(fold, a);
+    if (!strcmp(intrin, "isnormal")) ret = fold_intrin_isnormal(fold, a);
+    if (!strcmp(intrin, "signbit"))  ret = fold_intrin_signbit(fold, a);
+    if (!strcmp(intrin, "acosh"))    ret = fold_intirn_acosh(fold, a);
+    if (!strcmp(intrin, "asinh"))    ret = fold_intrin_asinh(fold, a);
+    if (!strcmp(intrin, "atanh"))    ret = fold_intrin_atanh(fold, a);
+    if (!strcmp(intrin, "exp"))      ret = fold_intrin_exp(fold, a);
+    if (!strcmp(intrin, "exp2"))     ret = fold_intrin_exp2(fold, a);
+    if (!strcmp(intrin, "expm1"))    ret = fold_intrin_expm1(fold, a);
+    if (!strcmp(intrin, "mod"))      ret = fold_intrin_mod(fold, a, b);
+    if (!strcmp(intrin, "pow"))      ret = fold_intrin_pow(fold, a, b);
+    if (!strcmp(intrin, "fabs"))     ret = fold_intrin_fabs(fold, a);
 
     if (ret)
         ++opts_optimizationcount[OPTIM_CONST_FOLD];
index 74d952cfacb496f332e7399de2540d52bb9a598e..cf4398e82d2aabee1050c9c897dee90c25b5dd96 100644 (file)
--- a/intrin.c
+++ b/intrin.c
@@ -77,6 +77,540 @@ static ast_expression *intrin_nullfunc(intrin_t *intrin) {
     return (ast_expression*)value;
 }
 
+static ast_expression *intrin_isfinite(intrin_t *intrin) {
+    /*
+     * float isfinite(float x) {
+     *     return !(isnan(x) || isinf(x));
+     * }
+     */
+    ast_value    *value     = NULL;
+    ast_value    *x         = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
+    ast_function *func      = intrin_value(intrin, &value, NULL, TYPE_FLOAT);
+    ast_call     *callisnan = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "isnan", "isfinite"));
+    ast_call     *callisinf = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "isinf", "isfinite"));
+    ast_block    *block     = ast_block_new(intrin_ctx(intrin));
+
+    /* float x; */
+    vec_push(value->expression.params, x);
+
+    /* <callisnan> = isnan(x); */
+    vec_push(callisnan->params, (ast_expression*)x);
+
+    /* <callisinf> = isinf(x); */
+    vec_push(callisinf->params, (ast_expression*)x);
+
+    /* return (!<callisnan> || <callisinf>); */
+    vec_push(block->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_unary_new(
+                intrin_ctx(intrin),
+                INSTR_NOT_F,
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_OR,
+                    (ast_expression*)callisnan,
+                    (ast_expression*)callisinf
+                )
+            )
+        )
+    );
+
+    vec_push(func->blocks, block);
+    intrin_reg(intrin, value, func);
+
+    return (ast_expression*)value;;
+}
+
+static ast_expression *intrin_isinf(intrin_t *intrin) {
+    /*
+     * float isinf(float x) {
+     *     return (x != 0.0) && (x + x == x);
+     * }
+     */
+    ast_value    *value = NULL;
+    ast_value    *x     = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
+    ast_block    *body  = ast_block_new(intrin_ctx(intrin));
+    ast_function *func  = intrin_value(intrin, &value, "isinf", TYPE_FLOAT);
+
+    vec_push(body->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_AND,
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_NE_F,
+                    (ast_expression*)x,
+                    (ast_expression*)intrin->fold->imm_float[0]
+                ),
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_EQ_F,
+                    (ast_expression*)ast_binary_new(
+                        intrin_ctx(intrin),
+                        INSTR_ADD_F,
+                        (ast_expression*)x,
+                        (ast_expression*)x
+                    ),
+                    (ast_expression*)x
+                )
+            )
+        )
+    );
+
+    vec_push(value->expression.params, x);
+    vec_push(func->blocks, body);
+
+    intrin_reg(intrin, value, func);
+
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_isnan(intrin_t *intrin) {
+    /*
+     * float isnan(float x) {
+     *   float local;
+     *   local = x;
+     *
+     *   return (x != local);
+     * }
+     */
+    ast_value    *value  = NULL;
+    ast_value    *arg1   = ast_value_new(intrin_ctx(intrin), "x",     TYPE_FLOAT);
+    ast_value    *local  = ast_value_new(intrin_ctx(intrin), "local", TYPE_FLOAT);
+    ast_block    *body   = ast_block_new(intrin_ctx(intrin));
+    ast_function *func   = intrin_value(intrin, &value, "isnan", TYPE_FLOAT);
+
+    vec_push(body->locals, local);
+    vec_push(body->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)local,
+            (ast_expression*)arg1
+        )
+    );
+
+    vec_push(body->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_NE_F,
+                (ast_expression*)arg1,
+                (ast_expression*)local
+            )
+        )
+    );
+
+    vec_push(value->expression.params, arg1);
+    vec_push(func->blocks, body);
+
+    intrin_reg(intrin, value, func);
+
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_isnormal(intrin_t *intrin) {
+    /*
+     * float isnormal(float x) {
+     *     return isfinite(x);
+     * }
+     */
+    ast_value    *value         = NULL;
+    ast_call     *callisfinite  = ast_call_new (intrin_ctx(intrin), intrin_func_self(intrin, "isfinite", "isnormal"));
+    ast_value    *x             = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
+    ast_block    *body          = ast_block_new(intrin_ctx(intrin));
+    ast_function *func          = intrin_value(intrin, &value, "isnormal", TYPE_FLOAT);
+
+    vec_push(value->expression.params, x);
+    vec_push(callisfinite->params, (ast_expression*)x);
+
+    /* return <callisfinite> */
+    vec_push(body->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)callisfinite
+        )
+    );
+
+    vec_push(func->blocks, body);
+    intrin_reg(intrin, value, func);
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_signbit(intrin_t *intrin) {
+    /*
+     * float signbit(float x) {
+     *     return (x < 0);
+     * }
+     */
+    ast_value    *value  = NULL;
+    ast_value    *x      = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
+    ast_block    *body   = ast_block_new(intrin_ctx(intrin));
+    ast_function *func   = intrin_value(intrin, &value, "signbit", TYPE_FLOAT);
+
+    vec_push(value->expression.params, x);
+
+    /* return (x < 0); */
+    vec_push(body->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_ternary_new(
+                intrin_ctx(intrin),
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_LT,
+                    (ast_expression*)x,
+                    (ast_expression*)intrin->fold->imm_float[0]
+                ),
+                (ast_expression*)intrin->fold->imm_float[1],
+                (ast_expression*)intrin->fold->imm_float[0]
+            )
+        )
+    );
+
+    vec_push(func->blocks, body);
+    intrin_reg(intrin, value, func);
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_acosh(intrin_t *intrin) {
+    /*
+     * float acosh(float x) {
+     *     return log(x + sqrt((x * x) - 1));
+     * }
+     */
+    ast_value    *value    = NULL;
+    ast_value    *x        = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
+    ast_call     *calllog  = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "log", "acosh"));
+    ast_call     *callsqrt = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "sqrt", "acosh"));
+    ast_block    *body     = ast_block_new(intrin_ctx(intrin));
+    ast_function *func     = intrin_value(intrin, &value, "acosh", TYPE_FLOAT);
+
+    vec_push(value->expression.params, x);
+
+    /* <callsqrt> = sqrt((x * x) - 1); */
+    vec_push(callsqrt->params,
+        (ast_expression*)ast_binary_new(
+            intrin_ctx(intrin),
+            INSTR_SUB_F,
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_MUL_F,
+                (ast_expression*)x,
+                (ast_expression*)x
+            ),
+            (ast_expression*)intrin->fold->imm_float[1]
+        )
+    );
+
+    /* <calllog> = log(x + <callsqrt>); */
+    vec_push(calllog->params,
+        (ast_expression*)ast_binary_new(
+            intrin_ctx(intrin),
+            INSTR_ADD_F,
+            (ast_expression*)x,
+            (ast_expression*)callsqrt
+        )
+    );
+
+    /* return <calllog>; */
+    vec_push(body->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)calllog
+        )
+    );
+
+    vec_push(func->blocks, body);
+    intrin_reg(intrin, value, func);
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_asinh(intrin_t *intrin) {
+    /*
+     * float asinh(float x) {
+     *     return log(x + sqrt((x * x) + 1));
+     * }
+     */
+    ast_value    *value    = NULL;
+    ast_value    *x        = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
+    ast_call     *calllog  = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "log", "asinh"));
+    ast_call     *callsqrt = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "sqrt", "asinh"));
+    ast_block    *body     = ast_block_new(intrin_ctx(intrin));
+    ast_function *func     = intrin_value(intrin, &value, "asinh", TYPE_FLOAT);
+
+    vec_push(value->expression.params, x);
+
+    /* <callsqrt> = sqrt((x * x) + 1); */
+    vec_push(callsqrt->params,
+        (ast_expression*)ast_binary_new(
+            intrin_ctx(intrin),
+            INSTR_ADD_F,
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_MUL_F,
+                (ast_expression*)x,
+                (ast_expression*)x
+            ),
+            (ast_expression*)intrin->fold->imm_float[1]
+        )
+    );
+
+    /* <calllog> = log(x + <callsqrt>); */
+    vec_push(calllog->params,
+        (ast_expression*)ast_binary_new(
+            intrin_ctx(intrin),
+            INSTR_ADD_F,
+            (ast_expression*)x,
+            (ast_expression*)callsqrt
+        )
+    );
+
+    /* return <calllog>; */
+    vec_push(body->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)calllog
+        )
+    );
+
+    vec_push(func->blocks, body);
+    intrin_reg(intrin, value, func);
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_atanh(intrin_t *intrin) {
+    /*
+     * float atanh(float x) {
+     *     return 0.5 * log((1 + x) / (1 - x))
+     * }
+     */
+    ast_value    *value   = NULL;
+    ast_value    *x       = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
+    ast_call     *calllog = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "log", "atanh"));
+    ast_block    *body    = ast_block_new(intrin_ctx(intrin));
+    ast_function *func    = intrin_value(intrin, &value, "atanh", TYPE_FLOAT);
+
+    vec_push(value->expression.params, x);
+
+    /* <callog> = log((1 + x) / (1 - x)); */
+    vec_push(calllog->params,
+        (ast_expression*)ast_binary_new(
+            intrin_ctx(intrin),
+            INSTR_DIV_F,
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_ADD_F,
+                (ast_expression*)intrin->fold->imm_float[1],
+                (ast_expression*)x
+            ),
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_SUB_F,
+                (ast_expression*)intrin->fold->imm_float[1],
+                (ast_expression*)x
+            )
+        )
+    );
+
+    /* return 0.5 * <calllog>; */
+    vec_push(body->exprs,
+        (ast_expression*)ast_binary_new(
+            intrin_ctx(intrin),
+            INSTR_MUL_F,
+            (ast_expression*)fold_constgen_float(intrin->fold, 0.5),
+            (ast_expression*)calllog
+        )
+    );
+
+    vec_push(func->blocks, body);
+    intrin_reg(intrin, value, func);
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_exp(intrin_t *intrin) {
+    /*
+     * float exp(float x) {
+     *     float sum = 1.0;
+     *     float acc = 1.0;
+     *     float i;
+     *     for (i = 1; i < 200; ++i)
+     *         sum += (acc *= x / i);
+     *
+     *     return sum;
+     * }
+     */
+    ast_value    *value = NULL;
+    ast_value    *x     = ast_value_new(intrin_ctx(intrin), "x",   TYPE_FLOAT);
+    ast_value    *sum   = ast_value_new(intrin_ctx(intrin), "sum", TYPE_FLOAT);
+    ast_value    *acc   = ast_value_new(intrin_ctx(intrin), "acc", TYPE_FLOAT);
+    ast_value    *i     = ast_value_new(intrin_ctx(intrin), "i",   TYPE_FLOAT);
+    ast_block    *body  = ast_block_new(intrin_ctx(intrin));
+    ast_function *func  = intrin_value(intrin, &value, "exp", TYPE_FLOAT);
+
+    vec_push(value->expression.params, x);
+    vec_push(body->locals, sum);
+    vec_push(body->locals, acc);
+    vec_push(body->locals, i);
+
+    /* sum = 1.0; */
+    vec_push(body->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)sum,
+            (ast_expression*)intrin->fold->imm_float[1]
+        )
+    );
+
+    /* acc = 1.0; */
+    vec_push(body->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)acc,
+            (ast_expression*)intrin->fold->imm_float[1]
+        )
+    );
+
+    /*
+     * for (i = 1; i < 200; ++i)
+     *     sum += (acc *= x / i);
+     */
+    vec_push(body->exprs,
+        (ast_expression*)ast_loop_new(
+            intrin_ctx(intrin),
+            /* i = 1; */
+            (ast_expression*)ast_store_new(
+                intrin_ctx(intrin),
+                INSTR_STORE_F,
+                (ast_expression*)i,
+                (ast_expression*)intrin->fold->imm_float[1]
+            ),
+            /* i < 200; */
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_LT,
+                (ast_expression*)i,
+                (ast_expression*)fold_constgen_float(intrin->fold, 200.0f)
+            ),
+            false,
+            NULL,
+            false,
+            /* ++i; */
+            (ast_expression*)ast_binstore_new(
+                intrin_ctx(intrin),
+                INSTR_STORE_F,
+                INSTR_ADD_F,
+                (ast_expression*)i,
+                (ast_expression*)intrin->fold->imm_float[1]
+            ),
+            /* sum += (acc *= (x / i)) */
+            (ast_expression*)ast_binstore_new(
+                intrin_ctx(intrin),
+                INSTR_STORE_F,
+                INSTR_ADD_F,
+                (ast_expression*)sum,
+                (ast_expression*)ast_binstore_new(
+                    intrin_ctx(intrin),
+                    INSTR_STORE_F,
+                    INSTR_MUL_F,
+                    (ast_expression*)acc,
+                    (ast_expression*)ast_binary_new(
+                        intrin_ctx(intrin),
+                        INSTR_DIV_F,
+                        (ast_expression*)x,
+                        (ast_expression*)i
+                    )
+                )
+            )
+        )
+    );
+
+    /* return sum; */
+    vec_push(body->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)sum
+        )
+    );
+
+    vec_push(func->blocks, body); /* {{{ body }}} */
+
+    intrin_reg(intrin, value, func);
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_exp2(intrin_t *intrin) {
+    /*
+     * float exp2(float x) {
+     *     return pow(2, x);
+     * }
+     */
+    ast_value    *value     = NULL;
+    ast_call     *callpow   = ast_call_new (intrin_ctx(intrin), intrin_func_self(intrin, "pow", "exp2"));
+    ast_value    *arg1      = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
+    ast_block    *body      = ast_block_new(intrin_ctx(intrin));
+    ast_function *func      = intrin_value(intrin, &value, "exp2", TYPE_FLOAT);
+
+    vec_push(value->expression.params, arg1);
+
+    vec_push(callpow->params, (ast_expression*)fold_constgen_float(intrin->fold, 2.0f));
+    vec_push(callpow->params, (ast_expression*)arg1);
+
+    /* return <callpow> */
+    vec_push(body->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)callpow
+        )
+    );
+
+    vec_push(func->blocks, body);
+
+    intrin_reg(intrin, value, func);
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_expm1(intrin_t *intrin) {
+    /*
+     * float expm1(float x) {
+     *     return exp(x) - 1;
+     * }
+     */
+    ast_value    *value    = NULL;
+    ast_call     *callexp  = ast_call_new (intrin_ctx(intrin), intrin_func_self(intrin, "exp", "expm1"));
+    ast_value    *x        = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
+    ast_block    *body     = ast_block_new(intrin_ctx(intrin));
+    ast_function *func     = intrin_value(intrin, &value, "expm1", TYPE_FLOAT);
+
+    vec_push(value->expression.params, x);
+
+    /* <callexp> = exp(x); */
+    vec_push(callexp->params, (ast_expression*)x);
+
+    /* return <callexp> - 1; */
+    vec_push(body->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_SUB_F,
+                (ast_expression*)callexp,
+                (ast_expression*)intrin->fold->imm_float[1]
+            )
+        )
+    );
+
+    vec_push(func->blocks, body);
+    intrin_reg(intrin, value, func);
+    return (ast_expression*)value;
+}
+
 static ast_expression *intrin_pow(intrin_t *intrin) {
     /*
      *
@@ -612,242 +1146,7 @@ static ast_expression *intrin_mod(intrin_t *intrin) {
         )
     );
 
-    vec_push(func->blocks, body); /* {{{ body }}} */
-    intrin_reg(intrin, value, func);
-
-    return (ast_expression*)value;
-}
-
-static ast_expression *intrin_exp(intrin_t *intrin) {
-    /*
-     * float exp(float x) {
-     *     float sum = 1.0;
-     *     float acc = 1.0;
-     *     float i;
-     *     for (i = 1; i < 200; ++i)
-     *         sum += (acc *= x / i);
-     *
-     *     return sum;
-     * }
-     */
-    ast_value    *value = NULL;
-    ast_value    *x     = ast_value_new(intrin_ctx(intrin), "x",   TYPE_FLOAT);
-    ast_value    *sum   = ast_value_new(intrin_ctx(intrin), "sum", TYPE_FLOAT);
-    ast_value    *acc   = ast_value_new(intrin_ctx(intrin), "acc", TYPE_FLOAT);
-    ast_value    *i     = ast_value_new(intrin_ctx(intrin), "i",   TYPE_FLOAT);
-    ast_block    *body  = ast_block_new(intrin_ctx(intrin));
-    ast_function *func  = intrin_value(intrin, &value, "mexp", TYPE_FLOAT);
-
-    vec_push(value->expression.params, x);
-    vec_push(body->locals, sum);
-    vec_push(body->locals, acc);
-    vec_push(body->locals, i);
-
-    /* sum = 1.0; */
-    vec_push(body->exprs,
-        (ast_expression*)ast_store_new(
-            intrin_ctx(intrin),
-            INSTR_STORE_F,
-            (ast_expression*)sum,
-            (ast_expression*)intrin->fold->imm_float[1]
-        )
-    );
-
-    /* acc = 1.0; */
-    vec_push(body->exprs,
-        (ast_expression*)ast_store_new(
-            intrin_ctx(intrin),
-            INSTR_STORE_F,
-            (ast_expression*)acc,
-            (ast_expression*)intrin->fold->imm_float[1]
-        )
-    );
-
-    /*
-     * for (i = 1; i < 200; ++i)
-     *     sum += (acc *= x / i);
-     */
-    vec_push(body->exprs,
-        (ast_expression*)ast_loop_new(
-            intrin_ctx(intrin),
-            /* i = 1; */
-            (ast_expression*)ast_store_new(
-                intrin_ctx(intrin),
-                INSTR_STORE_F,
-                (ast_expression*)i,
-                (ast_expression*)intrin->fold->imm_float[1]
-            ),
-            /* i < 200; */
-            (ast_expression*)ast_binary_new(
-                intrin_ctx(intrin),
-                INSTR_LT,
-                (ast_expression*)i,
-                (ast_expression*)fold_constgen_float(intrin->fold, 200.0f)
-            ),
-            false,
-            NULL,
-            false,
-            /* ++i; */
-            (ast_expression*)ast_binstore_new(
-                intrin_ctx(intrin),
-                INSTR_STORE_F,
-                INSTR_ADD_F,
-                (ast_expression*)i,
-                (ast_expression*)intrin->fold->imm_float[1]
-            ),
-            /* sum += (acc *= (x / i)) */
-            (ast_expression*)ast_binstore_new(
-                intrin_ctx(intrin),
-                INSTR_STORE_F,
-                INSTR_ADD_F,
-                (ast_expression*)sum,
-                (ast_expression*)ast_binstore_new(
-                    intrin_ctx(intrin),
-                    INSTR_STORE_F,
-                    INSTR_MUL_F,
-                    (ast_expression*)acc,
-                    (ast_expression*)ast_binary_new(
-                        intrin_ctx(intrin),
-                        INSTR_DIV_F,
-                        (ast_expression*)x,
-                        (ast_expression*)i
-                    )
-                )
-            )
-        )
-    );
-
-    /* return sum; */
-    vec_push(body->exprs,
-        (ast_expression*)ast_return_new(
-            intrin_ctx(intrin),
-            (ast_expression*)sum
-        )
-    );
-
-    vec_push(func->blocks, body); /* {{{ body }}} */
-
-    intrin_reg(intrin, value, func);
-    return (ast_expression*)value;
-}
-
-static ast_expression *intrin_exp2(intrin_t *intrin) {
-    /*
-     * float exp2(float x) {
-     *     return pow(2, x);
-     * }
-     */
-    ast_value    *value     = NULL;
-    ast_call     *callpow   = ast_call_new (intrin_ctx(intrin), intrin_func_self(intrin, "pow", "exp2"));
-    ast_value    *arg1      = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
-    ast_block    *body      = ast_block_new(intrin_ctx(intrin));
-    ast_function *func      = intrin_value(intrin, &value, "exp2", TYPE_FLOAT);
-
-    vec_push(value->expression.params, arg1);
-
-    vec_push(callpow->params, (ast_expression*)fold_constgen_float(intrin->fold, 2.0f));
-    vec_push(callpow->params, (ast_expression*)arg1);
-
-    /* return <callpow> */
-    vec_push(body->exprs,
-        (ast_expression*)ast_return_new(
-            intrin_ctx(intrin),
-            (ast_expression*)callpow
-        )
-    );
-
-    vec_push(func->blocks, body);
-
-    intrin_reg(intrin, value, func);
-    return (ast_expression*)value;
-}
-
-static ast_expression *intrin_isinf(intrin_t *intrin) {
-    /*
-     * float isinf(float x) {
-     *     return (x != 0.0) && (x + x == x);
-     * }
-     */
-    ast_value    *value = NULL;
-    ast_value    *x     = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
-    ast_block    *body  = ast_block_new(intrin_ctx(intrin));
-    ast_function *func  = intrin_value(intrin, &value, "isinf", TYPE_FLOAT);
-
-    vec_push(body->exprs,
-        (ast_expression*)ast_return_new(
-            intrin_ctx(intrin),
-            (ast_expression*)ast_binary_new(
-                intrin_ctx(intrin),
-                INSTR_AND,
-                (ast_expression*)ast_binary_new(
-                    intrin_ctx(intrin),
-                    INSTR_NE_F,
-                    (ast_expression*)x,
-                    (ast_expression*)intrin->fold->imm_float[0]
-                ),
-                (ast_expression*)ast_binary_new(
-                    intrin_ctx(intrin),
-                    INSTR_EQ_F,
-                    (ast_expression*)ast_binary_new(
-                        intrin_ctx(intrin),
-                        INSTR_ADD_F,
-                        (ast_expression*)x,
-                        (ast_expression*)x
-                    ),
-                    (ast_expression*)x
-                )
-            )
-        )
-    );
-
-    vec_push(value->expression.params, x);
-    vec_push(func->blocks, body);
-
-    intrin_reg(intrin, value, func);
-
-    return (ast_expression*)value;
-}
-
-static ast_expression *intrin_isnan(intrin_t *intrin) {
-    /*
-     * float isnan(float x) {
-     *   float local;
-     *   local = x;
-     *
-     *   return (x != local);
-     * }
-     */
-    ast_value    *value  = NULL;
-    ast_value    *arg1   = ast_value_new(intrin_ctx(intrin), "x",     TYPE_FLOAT);
-    ast_value    *local  = ast_value_new(intrin_ctx(intrin), "local", TYPE_FLOAT);
-    ast_block    *body   = ast_block_new(intrin_ctx(intrin));
-    ast_function *func   = intrin_value(intrin, &value, "isnan", TYPE_FLOAT);
-
-    vec_push(body->locals, local);
-    vec_push(body->exprs,
-        (ast_expression*)ast_store_new(
-            intrin_ctx(intrin),
-            INSTR_STORE_F,
-            (ast_expression*)local,
-            (ast_expression*)arg1
-        )
-    );
-
-    vec_push(body->exprs,
-        (ast_expression*)ast_return_new(
-            intrin_ctx(intrin),
-            (ast_expression*)ast_binary_new(
-                intrin_ctx(intrin),
-                INSTR_NE_F,
-                (ast_expression*)arg1,
-                (ast_expression*)local
-            )
-        )
-    );
-
-    vec_push(value->expression.params, arg1);
     vec_push(func->blocks, body);
-
     intrin_reg(intrin, value, func);
 
     return (ast_expression*)value;
@@ -903,14 +1202,22 @@ ast_expression *intrin_debug_typestring(intrin_t *intrin) {
 }
 
 static const intrin_func_t intrinsics[] = {
+    {&intrin_isfinite,         "__builtin_isfinite",         "isfinite", 1},
+    {&intrin_isinf,            "__builtin_isinf",            "isinf",    1},
+    {&intrin_isnan,            "__builtin_isnan",            "isnan",    1},
+    {&intrin_isnormal,         "__builtin_isnormal",         "isnormal", 1},
+    {&intrin_signbit,          "__builtin_signbit",          "signbit",  1},
+    {&intrin_acosh,            "__builtin_acosh",            "acosh",    1},
+    {&intrin_asinh,            "__builtin_asinh",            "asinh",    1},
+    {&intrin_atanh,            "__builtin_atanh",            "atanh",    1},
     {&intrin_exp,              "__builtin_exp",              "exp",      1},
     {&intrin_exp2,             "__builtin_exp2",             "exp2",     1},
+    {&intrin_expm1,            "__builtin_expm1",            "expm1",    1},
     {&intrin_mod,              "__builtin_mod",              "mod",      2},
     {&intrin_pow,              "__builtin_pow",              "pow",      2},
-    {&intrin_isnan,            "__builtin_isnan",            "isnan",    1},
-    {&intrin_isinf,            "__builtin_isinf",            "isinf",    1},
     {&intrin_fabs,             "__builtin_fabs",             "fabs",     1},
     {&intrin_debug_typestring, "__builtin_debug_typestring", "",         0},
+
     {&intrin_nullfunc,         "#nullfunc",                  "",         0}
 };