]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - intrin.c
Better exp algorithm from divVerent
[xonotic/gmqcc.git] / intrin.c
index 0ab278986bbbb7483e361eade69f98d6cbe05cff..74d952cfacb496f332e7399de2540d52bb9a598e 100644 (file)
--- a/intrin.c
+++ b/intrin.c
@@ -58,7 +58,6 @@ static GMQCC_INLINE void intrin_reg(intrin_t *intrin, ast_value *const value, as
     vec_push(intrin->parser->globals,   (ast_expression*)value);
 }
 
-#define QC_M_E         2.718281828459045f
 #define QC_POW_EPSILON 0.00001f
 
 /*
@@ -90,6 +89,8 @@ static ast_expression *intrin_pow(intrin_t *intrin) {
      *     float accumulate;
      *
      *     if (exp == 0.0)
+     *         return 1;
+     *     if (exp == 1.0)
      *         return base;
      *     if (exp < 0)
      *         return 1.0 / pow(base, -exp);
@@ -122,8 +123,8 @@ static ast_expression *intrin_pow(intrin_t *intrin) {
     ast_function *func = intrin_value(intrin, &value, "pow", TYPE_FLOAT);
 
     /* prepare some calls for later */
-    ast_call *callpow1  = ast_call_new(intrin_ctx(intrin), (ast_expression*)value);      /* for pow(base, -exp)    */
-    ast_call *callpow2  = ast_call_new(intrin_ctx(intrin), (ast_expression*)value);      /* for pow(vase, exp / 2) */
+    ast_call *callpow1  = ast_call_new(intrin_ctx(intrin), (ast_expression*)value);                  /* for pow(base, -exp)    */
+    ast_call *callpow2  = ast_call_new(intrin_ctx(intrin), (ast_expression*)value);                  /* for pow(vase, exp / 2) */
     ast_call *callsqrt1 = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "sqrt", "pow")); /* for sqrt(base)         */
     ast_call *callsqrt2 = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "sqrt", "pow")); /* for sqrt(square)       */
     ast_call *callfabs  = ast_call_new(intrin_ctx(intrin), intrin_func_self(intrin, "fabs", "pow")); /* for fabs(mid - exp)    */
@@ -166,7 +167,7 @@ static ast_expression *intrin_pow(intrin_t *intrin) {
 
     /*
      * if (exp == 0.0)
-     *     return base;
+     *     return 1;
      */
     vec_push(body->exprs,
         (ast_expression*)ast_ifthen_new(
@@ -177,6 +178,27 @@ static ast_expression *intrin_pow(intrin_t *intrin) {
                 (ast_expression*)exp,
                 (ast_expression*)intrin->fold->imm_float[0]
             ),
+            (ast_expression*)ast_return_new(
+                intrin_ctx(intrin),
+                (ast_expression*)intrin->fold->imm_float[1]
+            ),
+            NULL
+        )
+    );
+
+    /*
+     * if (exp == 1.0)
+     *     return base;
+     */
+    vec_push(body->exprs,
+        (ast_expression*)ast_ifthen_new(
+            intrin_ctx(intrin),
+            (ast_expression*)ast_binary_new(
+                intrin_ctx(intrin),
+                INSTR_EQ_F,
+                (ast_expression*)exp,
+                (ast_expression*)intrin->fold->imm_float[1]
+            ),
             (ast_expression*)ast_return_new(
                 intrin_ctx(intrin),
                 (ast_expression*)base
@@ -484,7 +506,7 @@ static ast_expression *intrin_pow(intrin_t *intrin) {
         )
     );
 
-    /* return midvalue */
+    /* return accumulate */
     vec_push(body->exprs,
         (ast_expression*)ast_return_new(
             intrin_ctx(intrin),
@@ -599,45 +621,107 @@ static ast_expression *intrin_mod(intrin_t *intrin) {
 static ast_expression *intrin_exp(intrin_t *intrin) {
     /*
      * float exp(float x) {
-     *     // mul 10 to round increments of 0.1f
-     *     return floor((pow(QC_M_E, x) * 10) + 0.5) / 10;
+     *     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_call     *callpow   = ast_call_new (intrin_ctx(intrin), intrin_func_self(intrin, "pow", "exp"));
-    ast_call     *callfloor = ast_call_new (intrin_ctx(intrin), intrin_func_self(intrin, "floor", "exp"));
-    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, "exp", TYPE_FLOAT);
+    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, arg1);
+    vec_push(value->expression.params, x);
+    vec_push(body->locals, sum);
+    vec_push(body->locals, acc);
+    vec_push(body->locals, i);
 
-    vec_push(callpow->params, (ast_expression*)fold_constgen_float(intrin->fold, QC_M_E));
-    vec_push(callpow->params, (ast_expression*)arg1);
-    vec_push(callfloor->params,
-        (ast_expression*)ast_binary_new(
+    /* 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_ADD_F,
+            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_MUL_F,
-                (ast_expression*)callpow,
-                (ast_expression*)fold_constgen_float(intrin->fold, 10.0f)
+                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]
             ),
-            (ast_expression*)fold_constgen_float(intrin->fold, 0.5f)
+            /* 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 <callfloor> / 10.0f */
+    /* return sum; */
     vec_push(body->exprs,
         (ast_expression*)ast_return_new(
             intrin_ctx(intrin),
-            (ast_expression*)ast_binary_new(
-                intrin_ctx(intrin),
-                INSTR_DIV_F,
-                (ast_expression*)callfloor,
-                (ast_expression*)fold_constgen_float(intrin->fold, 10.0f)
-            )
+            (ast_expression*)sum
         )
     );
 
@@ -678,6 +762,52 @@ static ast_expression *intrin_exp2(intrin_t *intrin) {
     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) {
@@ -778,6 +908,7 @@ static const intrin_func_t intrinsics[] = {
     {&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}
@@ -855,12 +986,11 @@ static ast_expression *intrin_func_self(intrin_t *intrin, const char *name, cons
     if ((find = intrin_func_try(intrin, offsetof(intrin_func_t, alias), name)))
         return find;
 
-    if (from)
+    if (from) {
         intrin_error(intrin, "need function `%s', compiler depends on it for `__builtin_%s'", name, from);
-    else
-        intrin_error(intrin, "need function `%s', compiler depends on it", name);
-
-    return intrin_func(intrin, "#nullfunc");
+        return intrin_func_self(intrin, "#nullfunc", NULL);
+    }
+    return NULL;
 }
 
 ast_expression *intrin_func(intrin_t *intrin, const char *name) {