]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - fold.c
Constant fold intrinsics if their arguments are constant. TODO: reference count intri...
[xonotic/gmqcc.git] / fold.c
diff --git a/fold.c b/fold.c
index b1e89c34940c70037e3953b16eafd420d0afe6f6..360b4f0f86e0cbd5866f2e6dc6fb5f5a0b8533e3 100644 (file)
--- a/fold.c
+++ b/fold.c
@@ -73,6 +73,38 @@ static GMQCC_INLINE vec3_t vec3_neg(vec3_t a) {
     return out;
 }
 
+static GMQCC_INLINE vec3_t vec3_or(vec3_t a, vec3_t b) {
+    vec3_t out;
+    out.x = (qcfloat_t)(((qcint_t)a.x) | ((qcint_t)b.x));
+    out.y = (qcfloat_t)(((qcint_t)a.y) | ((qcint_t)b.y));
+    out.z = (qcfloat_t)(((qcint_t)a.z) | ((qcint_t)b.z));
+    return out;
+}
+
+static GMQCC_INLINE vec3_t vec3_orvf(vec3_t a, qcfloat_t b) {
+    vec3_t out;
+    out.x = (qcfloat_t)(((qcint_t)a.x) | ((qcint_t)b));
+    out.y = (qcfloat_t)(((qcint_t)a.y) | ((qcint_t)b));
+    out.z = (qcfloat_t)(((qcint_t)a.z) | ((qcint_t)b));
+    return out;
+}
+
+static GMQCC_INLINE vec3_t vec3_and(vec3_t a, vec3_t b) {
+    vec3_t out;
+    out.x = (qcfloat_t)(((qcint_t)a.x) & ((qcint_t)b.x));
+    out.y = (qcfloat_t)(((qcint_t)a.y) & ((qcint_t)b.y));
+    out.z = (qcfloat_t)(((qcint_t)a.z) & ((qcint_t)b.z));
+    return out;
+}
+
+static GMQCC_INLINE vec3_t vec3_andvf(vec3_t a, qcfloat_t b) {
+    vec3_t out;
+    out.x = (qcfloat_t)(((qcint_t)a.x) & ((qcint_t)b));
+    out.y = (qcfloat_t)(((qcint_t)a.y) & ((qcint_t)b));
+    out.z = (qcfloat_t)(((qcint_t)a.z) & ((qcint_t)b));
+    return out;
+}
+
 static GMQCC_INLINE vec3_t vec3_xor(vec3_t a, vec3_t b) {
     vec3_t out;
     out.x = (qcfloat_t)(((qcint_t)a.x) ^ ((qcint_t)b.x));
@@ -89,6 +121,14 @@ static GMQCC_INLINE vec3_t vec3_xorvf(vec3_t a, qcfloat_t b) {
     return out;
 }
 
+static GMQCC_INLINE vec3_t vec3_not(vec3_t a) {
+    vec3_t out;
+    out.x = (qcfloat_t)(~((qcint_t)a.x));
+    out.y = (qcfloat_t)(~((qcint_t)a.y));
+    out.z = (qcfloat_t)(~((qcint_t)a.z));
+    return out;
+}
+
 static GMQCC_INLINE qcfloat_t vec3_mulvv(vec3_t a, vec3_t b) {
     return (a.x * b.x + a.y * b.y + a.z * b.z);
 }
@@ -184,6 +224,7 @@ fold_t *fold_init(parser_t *parser) {
     (void)fold_constgen_float (fold, -1.0f);
 
     (void)fold_constgen_vector(fold, vec3_create(0.0f, 0.0f, 0.0f));
+    (void)fold_constgen_vector(fold, vec3_create(-1.0f, -1.0f, -1.0f));
 
     return fold;
 }
@@ -444,14 +485,34 @@ static GMQCC_INLINE ast_expression *fold_op_mod(fold_t *fold, ast_value *a, ast_
 }
 
 static GMQCC_INLINE ast_expression *fold_op_bor(fold_t *fold, ast_value *a, ast_value *b) {
-    if (fold_can_2(a, b))
-        return fold_constgen_float(fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) | ((qcint_t)fold_immvalue_float(b))));
+    if (isfloat(a)) {
+        if (fold_can_2(a, b))
+            return fold_constgen_float(fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) | ((qcint_t)fold_immvalue_float(b))));
+    } else {
+        if (isvector(b)) {
+            if (fold_can_2(a, b))
+                return fold_constgen_vector(fold, vec3_or(fold_immvalue_vector(a), fold_immvalue_vector(b)));
+        } else {
+            if (fold_can_2(a, b))
+                return fold_constgen_vector(fold, vec3_orvf(fold_immvalue_vector(a), fold_immvalue_float(b)));
+        }
+    }
     return NULL;
 }
 
 static GMQCC_INLINE ast_expression *fold_op_band(fold_t *fold, ast_value *a, ast_value *b) {
-    if (fold_can_2(a, b))
-        return fold_constgen_float(fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) & ((qcint_t)fold_immvalue_float(b))));
+    if (isfloat(a)) {
+        if (fold_can_2(a, b))
+            return fold_constgen_float(fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) & ((qcint_t)fold_immvalue_float(b))));
+    } else {
+        if (isvector(b)) {
+            if (fold_can_2(a, b))
+                return fold_constgen_vector(fold, vec3_and(fold_immvalue_vector(a), fold_immvalue_vector(b)));
+        } else {
+            if (fold_can_2(a, b))
+                return fold_constgen_vector(fold, vec3_andvf(fold_immvalue_vector(a), fold_immvalue_float(b)));
+        }
+    }
     return NULL;
 }
 
@@ -537,8 +598,15 @@ static GMQCC_INLINE ast_expression *fold_op_cmp(fold_t *fold, ast_value *a, ast_
 }
 
 static GMQCC_INLINE ast_expression *fold_op_bnot(fold_t *fold, ast_value *a) {
-    if (fold_can_1(a))
-        return fold_constgen_float(fold, ~((qcint_t)fold_immvalue_float(a)));
+    if (isfloat(a)) {
+        if (fold_can_1(a))
+            return fold_constgen_float(fold, ~((qcint_t)fold_immvalue_float(a)));
+    } else {
+        if (isvector(a)) {
+            if (fold_can_1(a))
+                return fold_constgen_vector(fold, vec3_not(fold_immvalue_vector(a)));
+        }
+    }
     return NULL;
 }
 
@@ -605,12 +673,74 @@ ast_expression *fold_op(fold_t *fold, const oper_info *info, ast_expression **op
     return NULL;
 }
 
+#define expect(X)                                                                                        \
+    do {                                                                                                 \
+        if (vec_size(params) != (X)) {                                                                   \
+            compile_error(                                                                               \
+                fold_ctx(fold),                                                                          \
+                "internal error: attempted to constant-fold with invalid paramaters for intrinsic `%s`", \
+                intrin                                                                                   \
+            );                                                                                           \
+            return NULL;                                                                                 \
+        }                                                                                                \
+    } while (0)
+
+ast_expression *fold_intrin(fold_t *fold, const char *intrin, ast_expression **params) {
+    if (!fold)   return NULL;
+    if (!intrin) return NULL;
+
+    if (!strcmp(intrin, "__builtin_exp")) {
+        expect(1);
+        ++opts_optimizationcount[OPTIM_CONST_FOLD];
+        return fold_constgen_float(fold, exp(fold_immvalue_float((ast_value*)params[0])));
+    }
+
+    if (!strcmp(intrin, "__builtin_mod")) {
+        expect(2);
+        ++opts_optimizationcount[OPTIM_CONST_FOLD];
+        return fold_constgen_float(
+                    fold,
+                    fmodf(
+                        fold_immvalue_float((ast_value*)params[0]),
+                        fold_immvalue_float((ast_value*)params[1])
+                    )
+                );
+    }
+
+    if (!strcmp(intrin, "__builtin_pow")) {
+        expect(2);
+        ++opts_optimizationcount[OPTIM_CONST_FOLD];
+        return fold_constgen_float(
+                    fold,
+                    powf(
+                        fold_immvalue_float((ast_value*)params[0]),
+                        fold_immvalue_float((ast_value*)params[1])
+                    )
+                );
+    }
+
+    if (!strcmp(intrin, "__builtin_isnan")) {
+        expect(1);
+        ++opts_optimizationcount[OPTIM_CONST_FOLD];
+        return fold_constgen_float(fold, isnan(fold_immvalue_float((ast_value*)params[0])) != 0.0f);
+    }
+
+    if (!strcmp(intrin, "__builtin_fabs")) {
+        expect(1);
+        ++opts_optimizationcount[OPTIM_CONST_FOLD];
+        return fold_constgen_float(fold, fabs(fold_immvalue_float((ast_value*)params[0])));
+    }
+
+    return NULL;
+}
+
 /*
  * These are all the actual constant folding methods that happen in between
  * the AST/IR stage of the compiler , i.e eliminating branches for const
  * expressions, which is the only supported thing so far. We undefine the
  * testing macros here because an ir_value is differant than an ast_value.
  */
+#undef expect
 #undef isfloat
 #undef isstring
 #undef isvector
@@ -639,8 +769,15 @@ int fold_cond(ir_value *condval, ast_function *func, ast_ifthen *branch) {
         bool                    isfalse = (fold_immvalue_float(condval) == 0.0f && branch->on_false);
         ast_expression         *path    = (istrue)  ? branch->on_true  :
                                           (isfalse) ? branch->on_false : NULL;
-        if (!path)
-            return false;
+        if (!path) {
+            /*
+             * no path to take implies that the evaluation is if(0) and there
+             * is no else block. so eliminate all the code.
+             */
+            ++opts_optimizationcount[OPTIM_CONST_FOLD_DCE];
+            return true;
+        }
+
         if (!(elide = ir_function_create_block(ast_ctx(branch), func->ir_func, ast_function_label(func, ((istrue) ? "ontrue" : "onfalse")))))
             return false;
         if (!(*(cgen = path->codegen))((ast_expression*)path, func, false, &dummy))