]> de.git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - intrin.c
More cleanup
[xonotic/gmqcc.git] / intrin.c
index b51b3407c1a623fbf6da39da4597f7df033c1f0e..9ef02077b692eba828741cc7babefad9f928fffd 100644 (file)
--- a/intrin.c
+++ b/intrin.c
@@ -1,37 +1,6 @@
-/*
- * Copyright (C) 2012, 2013
- *     Dale Weiler
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
- * this software and associated documentation files (the "Software"), to deal in
- * the Software without restriction, including without limitation the rights to
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is furnished to do
- * so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
 #include <string.h>
 #include "parser.h"
 
-/*
- * Provides all the "intrinsics" / "builtins" for GMQCC. These can do
- * a few things, they can provide fall back implementations for math
- * functions if the definitions don't exist for some given engine. Or
- * then can determine definitions for existing builtins, and simply
- * wrap back to them instead.  This is like a "portable" intrface that
- * is entered when -fintrin is used (causing all existing builtins to
- * be ignored by the compiler and instead interface through here.
- */
 #define intrin_ctx(I) parser_ctx((I)->parser)
 
 static GMQCC_INLINE ast_function *intrin_value(intrin_t *intrin, ast_value **out, const char *name, qcint_t vtype) {
@@ -422,7 +391,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 +465,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,
@@ -1027,7 +996,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,
@@ -1310,7 +1279,7 @@ static ast_expression *intrin_nan(intrin_t *intrin) {
 
 static ast_expression *intrin_inf(intrin_t *intrin) {
     /*
-     * float nan(void) {
+     * float inf(void) {
      *     float x = 1.0f;
      *     float y = 0.0f;
      *     return x / y;
@@ -1389,8 +1358,18 @@ static ast_expression *intrin_ln(intrin_t *intrin) {
      *       nth   = 0.0f;
      *
      *       while (whole >= base) {
-     *           whole /= base;
-     *           nth++;
+     *           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;
@@ -1426,12 +1405,16 @@ static ast_expression *intrin_ln(intrin_t *intrin) {
     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;
 
@@ -1605,25 +1588,116 @@ static ast_expression *intrin_ln(intrin_t *intrin) {
         )
     );
 
-    /* whole /= base; */
+    /* 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*)base
+            (ast_expression*)base2
         )
     );
 
-    /* nth ++; */
+    /* 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*)intrin->fold->imm_float[1]
+            (ast_expression*)n2
         )
     );
 
@@ -1796,31 +1870,95 @@ static ast_expression *intrin_ln(intrin_t *intrin) {
     return (ast_expression*)value;
 }
 
-#define LOG_VARIANT(NAME, BASE) \
-static ast_expression *intrin_##NAME(intrin_t *intrin) { \
-    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)); \
-    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_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);
 }
-LOG_VARIANT(log, 2.7182818284590452354)
-LOG_VARIANT(log10, 10)
-LOG_VARIANT(log2, 2)
-LOG_VARIANT(logb, 2) /* FLT_RADIX == 2 for now */
-#undef LOG_VARIANT
 
 /*
  * TODO: make static (and handle ast_type_string) here for the builtin
@@ -1846,14 +1984,16 @@ 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_epsilon,          "__builtin_epsilon",          "",         0},
-    {&intrin_nan,              "__builtin_nan",              "",         0},
-    {&intrin_inf,              "__builtin_inf",              "",         0},
-    {&intrin_ln,               "__builtin_ln",               "",         2},
     {&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}
 };