]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - intrin.c
Constant folding for strings
[xonotic/gmqcc.git] / intrin.c
index a6d35209e00ca128a07ec69da28825d54255112b..9ddb1a2670ad0d2356e2bc795fb796df0e73b969 100644 (file)
--- a/intrin.c
+++ b/intrin.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014, 2015
  *     Dale Weiler
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy of
@@ -422,7 +422,7 @@ static ast_expression *intrin_atanh(intrin_t *intrin) {
         (ast_expression*)ast_binary_new(
             intrin_ctx(intrin),
             INSTR_MUL_F,
-            (ast_expression*)fold_constgen_float(intrin->fold, 0.5),
+            (ast_expression*)fold_constgen_float(intrin->fold, 0.5, false),
             (ast_expression*)calllog
         )
     );
@@ -496,7 +496,7 @@ static ast_expression *intrin_exp(intrin_t *intrin) {
                 intrin_ctx(intrin),
                 INSTR_LT,
                 (ast_expression*)i,
-                (ast_expression*)fold_constgen_float(intrin->fold, 200.0f)
+                (ast_expression*)fold_constgen_float(intrin->fold, 200.0f, false)
             ),
             false,
             NULL,
@@ -539,7 +539,7 @@ static ast_expression *intrin_exp(intrin_t *intrin) {
         )
     );
 
-    vec_push(func->blocks, body); /* {{{ body }}} */
+    vec_push(func->blocks, body);
 
     intrin_reg(intrin, value, func);
     return (ast_expression*)value;
@@ -559,7 +559,7 @@ static ast_expression *intrin_exp2(intrin_t *intrin) {
 
     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*)intrin->fold->imm_float[3]);
     vec_push(callpow->params, (ast_expression*)arg1);
 
     /* return <callpow> */
@@ -784,7 +784,7 @@ static ast_expression *intrin_pow(intrin_t *intrin) {
             intrin_ctx(intrin),
             INSTR_DIV_F,
             (ast_expression*)exp,
-            (ast_expression*)fold_constgen_float(intrin->fold, 2.0f)
+            (ast_expression*)intrin->fold->imm_float[3] /* 2.0f */
         )
     );
 
@@ -860,6 +860,7 @@ static ast_expression *intrin_pow(intrin_t *intrin) {
             (ast_expression*)intrin->fold->imm_float[1]
         )
     );
+
     vec_push(body->exprs,
         (ast_expression*)ast_store_new(
             intrin_ctx(intrin),
@@ -868,6 +869,7 @@ static ast_expression *intrin_pow(intrin_t *intrin) {
             (ast_expression*)callsqrt1
         )
     );
+
     vec_push(body->exprs,
         (ast_expression*)ast_store_new(
             intrin_ctx(intrin),
@@ -885,7 +887,7 @@ static ast_expression *intrin_pow(intrin_t *intrin) {
                 intrin_ctx(intrin),
                 INSTR_DIV_F,
                 (ast_expression*)high,
-                (ast_expression*)fold_constgen_float(intrin->fold, 2.0f)
+                (ast_expression*)intrin->fold->imm_float[3] /* 2.0f */
             )
         )
     );
@@ -994,7 +996,7 @@ static ast_expression *intrin_pow(intrin_t *intrin) {
                     (ast_expression*)low,
                     (ast_expression*)high
                 ),
-                (ast_expression*)fold_constgen_float(intrin->fold, 2.0f)
+                (ast_expression*)intrin->fold->imm_float[3] /* 2.0f */
             )
         )
     );
@@ -1025,7 +1027,7 @@ static ast_expression *intrin_pow(intrin_t *intrin) {
                 intrin_ctx(intrin),
                 INSTR_GT,
                 (ast_expression*)callfabs,
-                (ast_expression*)fold_constgen_float(intrin->fold, QC_POW_EPSILON)
+                (ast_expression*)fold_constgen_float(intrin->fold, QC_POW_EPSILON, false)
             ),
             /* pre not */
             false,
@@ -1192,6 +1194,803 @@ static ast_expression *intrin_fabs(intrin_t *intrin) {
     return (ast_expression*)value;
 }
 
+static ast_expression *intrin_epsilon(intrin_t *intrin) {
+    /*
+     * float epsilon(void) {
+     *     float eps = 1.0f;
+     *     do { eps /= 2.0f; } while ((1.0f + (eps / 2.0f)) != 1.0f);
+     *     return eps;
+     * }
+     */
+    ast_value    *value  = NULL;
+    ast_value    *eps    = ast_value_new(intrin_ctx(intrin), "eps", TYPE_FLOAT);
+    ast_block    *body   = ast_block_new(intrin_ctx(intrin));
+    ast_function *func   = intrin_value(intrin, &value, "epsilon", TYPE_FLOAT);
+
+    vec_push(body->locals, eps);
+
+    /* eps = 1.0f; */
+    vec_push(body->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)eps,
+            (ast_expression*)intrin->fold->imm_float[0]
+        )
+    );
+
+    vec_push(body->exprs,
+        (ast_expression*)ast_loop_new(
+            intrin_ctx(intrin),
+            NULL,
+            NULL,
+            false,
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_NE_F,
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_ADD_F,
+                    (ast_expression*)intrin->fold->imm_float[1],
+                    (ast_expression*)ast_binary_new(
+                        intrin_ctx(intrin),
+                        INSTR_MUL_F,
+                        (ast_expression*)eps,
+                        (ast_expression*)intrin->fold->imm_float[3] /* 2.0f */
+                    )
+                ),
+                (ast_expression*)intrin->fold->imm_float[1]
+            ),
+            false,
+            NULL,
+            (ast_expression*)ast_binstore_new(
+                intrin_ctx(intrin),
+                INSTR_STORE_F,
+                INSTR_DIV_F,
+                (ast_expression*)eps,
+                (ast_expression*)intrin->fold->imm_float[3] /* 2.0f */
+            )
+        )
+    );
+
+    /* return eps; */
+    vec_push(body->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)eps
+        )
+    );
+
+    vec_push(func->blocks, body);
+    intrin_reg(intrin, value, func);
+
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_nan(intrin_t *intrin) {
+    /*
+     * float nan(void) {
+     *     float x = 0.0f;
+     *     return x / x;
+     * }
+     */
+    ast_value    *value  = NULL;
+    ast_value    *x      = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
+    ast_function *func   = intrin_value(intrin, &value, "nan", TYPE_FLOAT);
+    ast_block    *block  = ast_block_new(intrin_ctx(intrin));
+
+    vec_push(block->locals, x);
+
+    vec_push(block->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)x,
+            (ast_expression*)intrin->fold->imm_float[0]
+        )
+    );
+
+    vec_push(block->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_DIV_F,
+                (ast_expression*)x,
+                (ast_expression*)x
+            )
+        )
+    );
+
+    vec_push(func->blocks, block);
+    intrin_reg(intrin, value, func);
+
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_inf(intrin_t *intrin) {
+    /*
+     * float inf(void) {
+     *     float x = 1.0f;
+     *     float y = 0.0f;
+     *     return x / y;
+     * }
+     */
+    ast_value    *value  = NULL;
+    ast_value    *x      = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
+    ast_value    *y      = ast_value_new(intrin_ctx(intrin), "y", TYPE_FLOAT);
+    ast_function *func   = intrin_value(intrin, &value, "inf", TYPE_FLOAT);
+    ast_block    *block  = ast_block_new(intrin_ctx(intrin));
+    size_t        i;
+
+    vec_push(block->locals, x);
+    vec_push(block->locals, y);
+
+    /* to keep code size down */
+    for (i = 0; i <= 1; i++) {
+        vec_push(block->exprs,
+            (ast_expression*)ast_store_new(
+                intrin_ctx(intrin),
+                INSTR_STORE_F,
+                (ast_expression*)((i == 0) ? x : y),
+                (ast_expression*)intrin->fold->imm_float[i]
+            )
+        );
+    }
+
+    vec_push(block->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_DIV_F,
+                (ast_expression*)x,
+                (ast_expression*)y
+            )
+        )
+    );
+
+    vec_push(func->blocks, block);
+    intrin_reg(intrin, value, func);
+
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_ln(intrin_t *intrin) {
+    /*
+     * float log(float power, float base) {
+     *   float whole;
+     *   float nth
+     *   float sign = 1.0f;
+     *   float eps  = epsilon();
+     *
+     *   if (power <= 1.0f || bbase <= 1.0) {
+     *       if (power <= 0.0f || base <= 0.0f)
+     *           return nan();
+     *
+     *       if (power < 1.0f) {
+     *           power = 1.0f / power;
+     *           sign *= -1.0f;
+     *       }
+     *
+     *       if (base < 1.0f) {
+     *           sign *= -1.0f;
+     *           base  = 1.0f / base;
+     *       }
+     *   }
+     *
+     *   float A_i       = 1;
+     *   float B_i       = 0;
+     *   float A_iminus1 = 0;
+     *   float B_iminus1 = 1;
+     *
+     *   for (;;) {
+     *       whole = power;
+     *       nth   = 0.0f;
+     *
+     *       while (whole >= base) {
+     *           float base2    = base;
+     *           float n2       = 1.0f;
+     *           float newbase2 = base2 * base2;
+     *
+     *           while (whole >= newbase2) {
+     *               base2     = newbase2;
+     *               n2       *= 2;
+     *               newbase2 *= newbase2;
+     *           }
+     *
+     *           whole /= base2;
+     *           nth += n2;
+     *       }
+     *
+     *       float b_iplus1 = n;
+     *       float A_iplus1 = b_iplus1 * A_i + A_iminus1;
+     *       float B_iplus1 = b_iplus1 * B_i + B_iminus1;
+     *
+     *       A_iminus1 = A_i;
+     *       B_iminus1 = B_i;
+     *       A_i       = A_iplus1;
+     *       B_i       = B_iplus1;
+     *
+     *       if (whole <= 1.0f + eps)
+     *           break;
+     *
+     *       power = base;
+     *       bower = whole;
+     *   }
+     *   return sign * A_i / B_i;
+     * }
+     */
+
+    ast_value    *value      = NULL;
+    ast_value    *power      = ast_value_new(intrin_ctx(intrin), "power",     TYPE_FLOAT);
+    ast_value    *base       = ast_value_new(intrin_ctx(intrin), "base",      TYPE_FLOAT);
+    ast_value    *whole      = ast_value_new(intrin_ctx(intrin), "whole",     TYPE_FLOAT);
+    ast_value    *nth        = ast_value_new(intrin_ctx(intrin), "nth",       TYPE_FLOAT);
+    ast_value    *sign       = ast_value_new(intrin_ctx(intrin), "sign",      TYPE_FLOAT);
+    ast_value    *A_i        = ast_value_new(intrin_ctx(intrin), "A_i",       TYPE_FLOAT);
+    ast_value    *B_i        = ast_value_new(intrin_ctx(intrin), "B_i",       TYPE_FLOAT);
+    ast_value    *A_iminus1  = ast_value_new(intrin_ctx(intrin), "A_iminus1", TYPE_FLOAT);
+    ast_value    *B_iminus1  = ast_value_new(intrin_ctx(intrin), "B_iminus1", TYPE_FLOAT);
+    ast_value    *b_iplus1   = ast_value_new(intrin_ctx(intrin), "b_iplus1",  TYPE_FLOAT);
+    ast_value    *A_iplus1   = ast_value_new(intrin_ctx(intrin), "A_iplus1",  TYPE_FLOAT);
+    ast_value    *B_iplus1   = ast_value_new(intrin_ctx(intrin), "B_iplus1",  TYPE_FLOAT);
+    ast_value    *eps        = ast_value_new(intrin_ctx(intrin), "eps",       TYPE_FLOAT);
+    ast_value    *base2      = ast_value_new(intrin_ctx(intrin), "base2",     TYPE_FLOAT);
+    ast_value    *n2         = ast_value_new(intrin_ctx(intrin), "n2",        TYPE_FLOAT);
+    ast_value    *newbase2   = ast_value_new(intrin_ctx(intrin), "newbase2",  TYPE_FLOAT);
+    ast_block    *block      = ast_block_new(intrin_ctx(intrin));
+    ast_block    *plt1orblt1 = ast_block_new(intrin_ctx(intrin)); /* (power <= 1.0f || base <= 1.0f) */
+    ast_block    *plt1       = ast_block_new(intrin_ctx(intrin)); /* (power < 1.0f) */
+    ast_block    *blt1       = ast_block_new(intrin_ctx(intrin)); /* (base  < 1.0f) */
+    ast_block    *forloop    = ast_block_new(intrin_ctx(intrin)); /* for(;;) */
+    ast_block    *whileloop  = ast_block_new(intrin_ctx(intrin)); /* while (whole >= base) */
+    ast_block    *nestwhile  = ast_block_new(intrin_ctx(intrin)); /* while (whole >= newbase2) */
+    ast_function *func       = intrin_value(intrin, &value, "ln", TYPE_FLOAT);
+    size_t        i;
+
+    vec_push(value->expression.params, power);
+    vec_push(value->expression.params, base);
+
+    vec_push(block->locals, whole);
+    vec_push(block->locals, nth);
+    vec_push(block->locals, sign);
+    vec_push(block->locals, eps);
+    vec_push(block->locals, A_i);
+    vec_push(block->locals, B_i);
+    vec_push(block->locals, A_iminus1);
+    vec_push(block->locals, B_iminus1);
+
+    /* sign = 1.0f; */
+    vec_push(block->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)sign,
+            (ast_expression*)intrin->fold->imm_float[1]
+        )
+    );
+
+    /* eps = __builtin_epsilon(); */
+    vec_push(block->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)eps,
+            (ast_expression*)ast_call_new(
+                intrin_ctx(intrin),
+                intrin_func_self(intrin, "__builtin_epsilon", "ln")
+            )
+        )
+    );
+
+    /*
+     * A_i       = 1;
+     * B_i       = 0;
+     * A_iminus1 = 0;
+     * B_iminus1 = 1;
+     */
+    for (i = 0; i <= 1; i++) {
+        int j;
+        for (j = 1; j >= 0; j--) {
+            vec_push(block->exprs,
+                (ast_expression*)ast_store_new(
+                    intrin_ctx(intrin),
+                    INSTR_STORE_F,
+                    (ast_expression*)((j) ? ((i) ? B_iminus1 : A_i)
+                                          : ((i) ? A_iminus1 : B_i)),
+                    (ast_expression*)intrin->fold->imm_float[j]
+                )
+            );
+        }
+    }
+
+    /*
+     * <plt1> = {
+     *     power = 1.0f / power;
+     *     sign *= -1.0f;
+     * }
+     * <blt1> = {
+     *     base  = 1.0f / base;
+     *     sign *= -1.0f;
+     * }
+     */
+    for (i = 0; i <= 1; i++) {
+        vec_push(((i) ? blt1 : plt1)->exprs,
+            (ast_expression*)ast_store_new(
+                intrin_ctx(intrin),
+                INSTR_STORE_F,
+                (ast_expression*)((i) ? base : power),
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_DIV_F,
+                    (ast_expression*)intrin->fold->imm_float[1],
+                    (ast_expression*)((i) ? base : power)
+                )
+            )
+        );
+        vec_push(plt1->exprs,
+            (ast_expression*)ast_binstore_new(
+                intrin_ctx(intrin),
+                INSTR_STORE_F,
+                INSTR_MUL_F,
+                (ast_expression*)sign,
+                (ast_expression*)intrin->fold->imm_float[2]
+            )
+        );
+    }
+
+    /*
+     * <plt1orblt1> = {
+     *     if (power <= 0.0 || base <= 0.0f)
+     *         return __builtin_nan();
+     *     if (power < 1.0f)
+     *         <plt1>
+     *     if (base < 1.0f)
+     *         <blt1>
+     * }
+     */
+    vec_push(plt1orblt1->exprs,
+        (ast_expression*)ast_ifthen_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_OR,
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_LE,
+                    (ast_expression*)power,
+                    (ast_expression*)intrin->fold->imm_float[0]
+                ),
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_LE,
+                    (ast_expression*)base,
+                    (ast_expression*)intrin->fold->imm_float[0]
+                )
+            ),
+            (ast_expression*)ast_return_new(
+                intrin_ctx(intrin),
+                (ast_expression*)ast_call_new(
+                    intrin_ctx(intrin),
+                    intrin_func_self(intrin, "__builtin_nan", "ln")
+                )
+            ),
+            NULL
+        )
+    );
+
+    for (i = 0; i <= 1; i++) {
+        vec_push(plt1orblt1->exprs,
+            (ast_expression*)ast_ifthen_new(
+                intrin_ctx(intrin),
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_LT,
+                    (ast_expression*)((i) ? base : power),
+                    (ast_expression*)intrin->fold->imm_float[1]
+                ),
+                (ast_expression*)((i) ? blt1 : plt1),
+                NULL
+            )
+        );
+    }
+
+    vec_push(block->exprs, (ast_expression*)plt1orblt1);
+
+
+    /* whole = power; */
+    vec_push(forloop->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)whole,
+            (ast_expression*)power
+        )
+    );
+
+    /* nth = 0.0f; */
+    vec_push(forloop->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)nth,
+            (ast_expression*)intrin->fold->imm_float[0]
+        )
+    );
+
+    /* base2 = base; */
+    vec_push(whileloop->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)base2,
+            (ast_expression*)base
+        )
+    );
+
+    /* n2 = 1.0f; */
+    vec_push(whileloop->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)n2,
+            (ast_expression*)intrin->fold->imm_float[1]
+        )
+    );
+
+    /* newbase2 = base2 * base2; */
+    vec_push(whileloop->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)newbase2,
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_MUL_F,
+                (ast_expression*)base2,
+                (ast_expression*)base2
+            )
+        )
+    );
+
+    /* while loop locals */
+    vec_push(whileloop->locals, base2);
+    vec_push(whileloop->locals, n2);
+    vec_push(whileloop->locals, newbase2);
+
+    /* base2 = newbase2; */
+    vec_push(nestwhile->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)base2,
+            (ast_expression*)newbase2
+        )
+    );
+
+    /* n2 *= 2; */
+    vec_push(nestwhile->exprs,
+        (ast_expression*)ast_binstore_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            INSTR_MUL_F,
+            (ast_expression*)n2,
+            (ast_expression*)intrin->fold->imm_float[3] /* 2.0f */
+        )
+    );
+
+    /* newbase2 *= newbase2; */
+    vec_push(nestwhile->exprs,
+        (ast_expression*)ast_binstore_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            INSTR_MUL_F,
+            (ast_expression*)newbase2,
+            (ast_expression*)newbase2
+        )
+    );
+
+    /* while (whole >= newbase2) */
+    vec_push(whileloop->exprs,
+        (ast_expression*)ast_loop_new(
+            intrin_ctx(intrin),
+            NULL,
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_GE,
+                (ast_expression*)whole,
+                (ast_expression*)newbase2
+            ),
+            false,
+            NULL,
+            false,
+            NULL,
+            (ast_expression*)nestwhile
+        )
+    );
+
+    /* whole /= base2; */
+    vec_push(whileloop->exprs,
+        (ast_expression*)ast_binstore_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            INSTR_DIV_F,
+            (ast_expression*)whole,
+            (ast_expression*)base2
+        )
+    );
+
+    /* nth += n2; */
+    vec_push(whileloop->exprs,
+        (ast_expression*)ast_binstore_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            INSTR_ADD_F,
+            (ast_expression*)nth,
+            (ast_expression*)n2
+        )
+    );
+
+    /* while (whole >= base) */
+    vec_push(forloop->exprs,
+        (ast_expression*)ast_loop_new(
+            intrin_ctx(intrin),
+            NULL,
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_GE,
+                (ast_expression*)whole,
+                (ast_expression*)base
+            ),
+            false,
+            NULL,
+            false,
+            NULL,
+            (ast_expression*)whileloop
+        )
+    );
+
+    vec_push(forloop->locals, b_iplus1);
+    vec_push(forloop->locals, A_iplus1);
+    vec_push(forloop->locals, B_iplus1);
+
+    /* b_iplus1 = nth; */
+    vec_push(forloop->exprs,
+        (ast_expression*)ast_store_new(
+            intrin_ctx(intrin),
+            INSTR_STORE_F,
+            (ast_expression*)b_iplus1,
+            (ast_expression*)nth
+        )
+    );
+
+    /*
+     * A_iplus1 = b_iplus1 * A_i + A_iminus1;
+     * B_iplus1 = b_iplus1 * B_i + B_iminus1;
+     */
+    for (i = 0; i <= 1; i++) {
+        vec_push(forloop->exprs,
+            (ast_expression*)ast_store_new(
+                intrin_ctx(intrin),
+                INSTR_STORE_F,
+                (ast_expression*)((i) ? B_iplus1 : A_iplus1),
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_ADD_F,
+                    (ast_expression*)ast_binary_new(
+                        intrin_ctx(intrin),
+                        INSTR_MUL_F,
+                        (ast_expression*)b_iplus1,
+                        (ast_expression*) ((i) ? B_i : A_i)
+                    ),
+                    (ast_expression*)((i) ? B_iminus1 : A_iminus1)
+                )
+            )
+        );
+    }
+
+    /*
+     * A_iminus1 = A_i;
+     * B_iminus1 = B_i;
+     */
+    for (i = 0; i <= 1; i++) {
+        vec_push(forloop->exprs,
+            (ast_expression*)ast_store_new(
+                intrin_ctx(intrin),
+                INSTR_STORE_F,
+                (ast_expression*)((i) ? B_iminus1 : A_iminus1),
+                (ast_expression*)((i) ? B_i       : A_i)
+            )
+        );
+    }
+
+    /*
+     * A_i = A_iplus1;
+     * B_i = B_iplus1;
+     */
+    for (i = 0; i <= 1; i++) {
+        vec_push(forloop->exprs,
+            (ast_expression*)ast_store_new(
+                intrin_ctx(intrin),
+                INSTR_STORE_F,
+                (ast_expression*)((i) ? B_i      : A_i),
+                (ast_expression*)((i) ? B_iplus1 : A_iplus1)
+            )
+        );
+    }
+
+    /*
+     * if (whole <= 1.0f + eps)
+     *     break;
+     */
+    vec_push(forloop->exprs,
+        (ast_expression*)ast_ifthen_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_LE,
+                (ast_expression*)whole,
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_ADD_F,
+                    (ast_expression*)intrin->fold->imm_float[1],
+                    (ast_expression*)eps
+                )
+            ),
+            (ast_expression*)ast_breakcont_new(
+                intrin_ctx(intrin),
+                false,
+                0
+            ),
+            NULL
+        )
+    );
+
+    /*
+     * power = base;
+     * base  = whole;
+     */
+    for (i = 0; i <= 1; i++) {
+        vec_push(forloop->exprs,
+            (ast_expression*)ast_store_new(
+                intrin_ctx(intrin),
+                INSTR_STORE_F,
+                (ast_expression*)((i) ? base  : power),
+                (ast_expression*)((i) ? whole : base)
+            )
+        );
+    }
+
+    /* add the for loop block */
+    vec_push(block->exprs,
+        (ast_expression*)ast_loop_new(
+            intrin_ctx(intrin),
+            NULL,
+            /* for(; 1; ) ?? (can this be NULL too?) */
+            (ast_expression*)intrin->fold->imm_float[1],
+            false,
+            NULL,
+            false,
+            NULL,
+            (ast_expression*)forloop
+        )
+    );
+
+    /* return sign * A_i / B_il */
+    vec_push(block->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_MUL_F,
+                (ast_expression*)sign,
+                (ast_expression*)ast_binary_new(
+                    intrin_ctx(intrin),
+                    INSTR_DIV_F,
+                    (ast_expression*)A_i,
+                    (ast_expression*)B_i
+                )
+            )
+        )
+    );
+
+    vec_push(func->blocks, block);
+    intrin_reg(intrin, value, func);
+
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_log_variant(intrin_t *intrin, const char *name, float base) {
+    ast_value    *value  = NULL;
+    ast_call     *callln = ast_call_new (intrin_ctx(intrin), intrin_func_self(intrin, "__builtin_ln", name));
+    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, name, TYPE_FLOAT);
+
+    vec_push(value->expression.params, arg1);
+
+    vec_push(callln->params, (ast_expression*)arg1);
+    vec_push(callln->params, (ast_expression*)fold_constgen_float(intrin->fold, base, false));
+
+    vec_push(body->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)callln
+        )
+    );
+
+    vec_push(func->blocks, body);
+    intrin_reg(intrin, value, func);
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_log(intrin_t *intrin) {
+    return intrin_log_variant(intrin, "log", 2.7182818284590452354);
+}
+static ast_expression *intrin_log10(intrin_t *intrin) {
+    return intrin_log_variant(intrin, "log10", 10);
+}
+static ast_expression *intrin_log2(intrin_t *intrin) {
+    return intrin_log_variant(intrin, "log2", 2);
+}
+static ast_expression *intrin_logb(intrin_t *intrin) {
+    /* FLT_RADIX == 2 for now */
+    return intrin_log_variant(intrin, "log2", 2);
+}
+
+static ast_expression *intrin_shift_variant(intrin_t *intrin, const char *name, size_t instr) {
+    /*
+     * float [shift] (float a, float b) {
+     *   return floor(a [instr] pow(2, b));
+     */
+    ast_value    *value     = NULL;
+    ast_call     *callpow   = ast_call_new (intrin_ctx(intrin), intrin_func_self(intrin, "pow", name));
+    ast_call     *callfloor = ast_call_new (intrin_ctx(intrin), intrin_func_self(intrin, "floor", name));
+    ast_value    *a         = ast_value_new(intrin_ctx(intrin), "a", TYPE_FLOAT);
+    ast_value    *b         = ast_value_new(intrin_ctx(intrin), "b", TYPE_FLOAT);
+    ast_block    *body      = ast_block_new(intrin_ctx(intrin));
+    ast_function *func      = intrin_value(intrin, &value, name, TYPE_FLOAT);
+
+    vec_push(value->expression.params, a);
+    vec_push(value->expression.params, b);
+
+    /* <callpow> = pow(2, b) */
+    vec_push(callpow->params, (ast_expression*)intrin->fold->imm_float[3]);
+    vec_push(callpow->params, (ast_expression*)b);
+
+    /* <callfloor> = floor(a [instr] <callpow>) */
+    vec_push(
+        callfloor->params,
+        (ast_expression*)ast_binary_new(
+            intrin_ctx(intrin),
+            instr,
+            (ast_expression*)a,
+            (ast_expression*)callpow
+        )
+    );
+
+    /* return <callfloor> */
+    vec_push(body->exprs,
+        (ast_expression*)ast_return_new(
+            intrin_ctx(intrin),
+            (ast_expression*)callfloor
+        )
+    );
+
+    vec_push(func->blocks, body);
+    intrin_reg(intrin, value, func);
+    return (ast_expression*)value;
+}
+
+static ast_expression *intrin_lshift(intrin_t *intrin) {
+    return intrin_shift_variant(intrin, "lshift", INSTR_MUL_F);
+}
+
+static ast_expression *intrin_rshift(intrin_t *intrin) {
+    return intrin_shift_variant(intrin, "rshift", INSTR_DIV_F);
+}
+
 /*
  * TODO: make static (and handle ast_type_string) here for the builtin
  * instead of in SYA parse close.
@@ -1216,8 +2015,17 @@ static const intrin_func_t intrinsics[] = {
     {&intrin_mod,              "__builtin_mod",              "mod",      2},
     {&intrin_pow,              "__builtin_pow",              "pow",      2},
     {&intrin_fabs,             "__builtin_fabs",             "fabs",     1},
+    {&intrin_log,              "__builtin_log",              "log",      1},
+    {&intrin_log10,            "__builtin_log10",            "log10",    1},
+    {&intrin_log2,             "__builtin_log2",             "log2",     1},
+    {&intrin_logb,             "__builtin_logb",             "logb",     1},
+    {&intrin_lshift,           "__builtin_lshift",           "",         2},
+    {&intrin_rshift,           "__builtin_rshift",           "",         2},
+    {&intrin_epsilon,          "__builtin_epsilon",          "",         0},
+    {&intrin_nan,              "__builtin_nan",              "",         0},
+    {&intrin_inf,              "__builtin_inf",              "",         0},
+    {&intrin_ln,               "__builtin_ln",               "",         2},
     {&intrin_debug_typestring, "__builtin_debug_typestring", "",         0},
-
     {&intrin_nullfunc,         "#nullfunc",                  "",         0}
 };