]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - fold.cpp
fix bug #188
[xonotic/gmqcc.git] / fold.cpp
index 6d56d8af1d59e0cc95adb389d5337e6a12ee3ced..56ff7f24ada944217ecc54d8b8b153642d8ffdfa 100644 (file)
--- a/fold.cpp
+++ b/fold.cpp
@@ -508,7 +508,7 @@ static GMQCC_INLINE void sfloat_check(lex_ctx_t ctx, sfloat_state_t *state, cons
             compile_error(ctx, "arithmetic overflow in `%s' component", vec);
         if (state->exceptionflags & SFLOAT_UNDERFLOW)
             compile_error(ctx, "arithmetic underflow in `%s' component", vec);
-            return;
+        return;
     }
     if (state->exceptionflags & SFLOAT_DIVBYZERO)
         compile_error(ctx, "division by zero");
@@ -1066,6 +1066,47 @@ bool fold::check_inexact_float(ast_value *a, ast_value *b) {
     return compile_warning(ctx(), WARN_INEXACT_COMPARES, "inexact value in comparison");
 }
 
+uint32_t fold::cond(ast_value* condval, ast_ifthen *branch) {
+    // Optimization is disabled.
+    if (!OPTS_OPTIMIZATION(OPTIM_CONST_FOLD_DCE)) {
+        // Generate code for both.
+        return ON_TRUE | ON_FALSE;
+    }
+
+    // Only float literals can be DCE in conditions.
+    if (!isfloat(condval) || !fold_can_1(condval)) {
+        // Generate code for both.
+        return ON_TRUE | ON_FALSE;
+    }
+
+    qcfloat_t value = immvalue_float(condval);
+
+    bool is_true = value != 0.0f && branch->m_on_true;
+    bool is_false = value == 0.0f && branch->m_on_false;
+
+    ++opts_optimizationcount[OPTIM_CONST_FOLD_DCE];
+
+    // Determine which path we want to take based on constant fold.
+    if (is_true) {
+        // Generate code only for true path.
+        return ON_TRUE;
+    } else if (is_false) {
+        // Generate code only for false path.
+        return ON_FALSE;
+    }
+
+    // Generate code for no paths.
+    return 0;
+}
+
+uint32_t fold::cond_ternary(ast_value *condval, ast_ternary *branch) {
+    return cond(condval, (ast_ifthen*)branch);
+}
+
+uint32_t fold::cond_ifthen(ast_value *condval, ast_ifthen *branch) {
+    return cond(condval, branch);
+}
+
 ast_expression *fold::op_mul_vec(vec3_t vec, ast_value *sel, const char *set) {
     qcfloat_t x = (&vec.x)[set[0]-'x'];
     qcfloat_t y = (&vec.x)[set[1]-'x'];
@@ -1082,7 +1123,6 @@ ast_expression *fold::op_mul_vec(vec3_t vec, ast_value *sel, const char *set) {
     return nullptr;
 }
 
-
 ast_expression *fold::op_neg(ast_value *a) {
     if (isfloat(a)) {
         if (fold_can_1(a)) {
@@ -1392,8 +1432,8 @@ ast_expression *fold::op(const oper_info *info, ast_expression **opexprs) {
         return nullptr;
 
     switch(info->operands) {
-        case 3: if(!c) return nullptr;
-        case 2: if(!b) return nullptr;
+        case 3: if(!c) return nullptr; [[fallthrough]];
+        case 2: if(!b) return nullptr; [[fallthrough]];
         case 1:
         if(!a) {
             compile_error(ctx(), "internal error: fold_op no operands to fold\n");
@@ -1486,29 +1526,54 @@ ast_expression *fold::intrinsic_pow(ast_value *lhs, ast_value *rhs) {
 ast_expression *fold::intrinsic_fabs(ast_value *a) {
     return constgen_float(fabsf(immvalue_float(a)), false);
 }
+ast_expression* fold::intrinsic_nan(void) {
+    return constgen_float(0.0f / 0.0f, false);
+}
+ast_expression* fold::intrinsic_epsilon(void) {
+  static bool calculated = false;
+  static float eps = 1.0f;
+  if (!calculated) {
+    do {
+      eps /= 2.0f;
+    } while ((1.0f + (eps / 2.0f)) != 1.0f);
+    calculated = true;
+  }
+  return constgen_float(eps, false);
+}
+
+ast_expression* fold::intrinsic_inf(void) {
+  return constgen_float(1.0f / 0.0f, false);
+}
 
-ast_expression *fold::intrinsic(const char *intrinsic, ast_expression **arg) {
+ast_expression *fold::intrinsic(const char *intrinsic, size_t n_args, ast_expression **args) {
     ast_expression *ret = nullptr;
-    ast_value *a = (ast_value*)arg[0];
-    ast_value *b = (ast_value*)arg[1];
-
-    if (!strcmp(intrinsic, "isfinite")) ret = intrinsic_isfinite(a);
-    if (!strcmp(intrinsic, "isinf"))    ret = intrinsic_isinf(a);
-    if (!strcmp(intrinsic, "isnan"))    ret = intrinsic_isnan(a);
-    if (!strcmp(intrinsic, "isnormal")) ret = intrinsic_isnormal(a);
-    if (!strcmp(intrinsic, "signbit"))  ret = intrinsic_signbit(a);
-    if (!strcmp(intrinsic, "acosh"))    ret = intrinsic_acosh(a);
-    if (!strcmp(intrinsic, "asinh"))    ret = intrinsic_asinh(a);
-    if (!strcmp(intrinsic, "atanh"))    ret = intrinsic_atanh(a);
-    if (!strcmp(intrinsic, "exp"))      ret = intrinsic_exp(a);
-    if (!strcmp(intrinsic, "exp2"))     ret = intrinsic_exp2(a);
-    if (!strcmp(intrinsic, "expm1"))    ret = intrinsic_expm1(a);
-    if (!strcmp(intrinsic, "mod"))      ret = intrinsic_mod(a, b);
-    if (!strcmp(intrinsic, "pow"))      ret = intrinsic_pow(a, b);
-    if (!strcmp(intrinsic, "fabs"))     ret = intrinsic_fabs(a);
 
-    if (ret)
+    if (n_args) {
+      ast_value *a = (ast_value*)args[0];
+      ast_value *b = (ast_value*)args[1];
+      if (!strcmp(intrinsic, "isfinite")) ret = intrinsic_isfinite(a);
+      if (!strcmp(intrinsic, "isinf"))    ret = intrinsic_isinf(a);
+      if (!strcmp(intrinsic, "isnan"))    ret = intrinsic_isnan(a);
+      if (!strcmp(intrinsic, "isnormal")) ret = intrinsic_isnormal(a);
+      if (!strcmp(intrinsic, "signbit"))  ret = intrinsic_signbit(a);
+      if (!strcmp(intrinsic, "acosh"))    ret = intrinsic_acosh(a);
+      if (!strcmp(intrinsic, "asinh"))    ret = intrinsic_asinh(a);
+      if (!strcmp(intrinsic, "atanh"))    ret = intrinsic_atanh(a);
+      if (!strcmp(intrinsic, "exp"))      ret = intrinsic_exp(a);
+      if (!strcmp(intrinsic, "exp2"))     ret = intrinsic_exp2(a);
+      if (!strcmp(intrinsic, "expm1"))    ret = intrinsic_expm1(a);
+      if (!strcmp(intrinsic, "mod"))      ret = intrinsic_mod(a, b);
+      if (!strcmp(intrinsic, "pow"))      ret = intrinsic_pow(a, b);
+      if (!strcmp(intrinsic, "fabs"))     ret = intrinsic_fabs(a);
+    } else {
+      if (!strcmp(intrinsic, "nan"))      ret = intrinsic_nan();
+      if (!strcmp(intrinsic, "epsilon"))  ret = intrinsic_epsilon();
+      if (!strcmp(intrinsic, "inf"))      ret = intrinsic_inf();
+    }
+
+    if (ret) {
         ++opts_optimizationcount[OPTIM_CONST_FOLD];
+    }
 
     return ret;
 }
@@ -1560,6 +1625,7 @@ ast_expression *fold::superfluous(ast_expression *left, ast_expression *right, i
         case INSTR_DIV_F:
             if (swapped)
                 return nullptr;
+            [[fallthrough]];
         case INSTR_MUL_F:
             if (immvalue_float(load) == 1.0f) {
                 ++opts_optimizationcount[OPTIM_PEEPHOLE];
@@ -1572,6 +1638,7 @@ ast_expression *fold::superfluous(ast_expression *left, ast_expression *right, i
         case INSTR_SUB_F:
             if (swapped)
                 return nullptr;
+            [[fallthrough]];
         case INSTR_ADD_F:
             if (immvalue_float(load) == 0.0f) {
                 ++opts_optimizationcount[OPTIM_PEEPHOLE];
@@ -1591,6 +1658,7 @@ ast_expression *fold::superfluous(ast_expression *left, ast_expression *right, i
         case INSTR_SUB_V:
             if (swapped)
                 return nullptr;
+            [[fallthrough]];
         case INSTR_ADD_V:
             if (vec3_cmp(immvalue_vector(load), vec3_create(0, 0, 0))) {
                 ++opts_optimizationcount[OPTIM_PEEPHOLE];
@@ -1609,45 +1677,3 @@ ast_expression *fold::binary(lex_ctx_t ctx, int op, ast_expression *left, ast_ex
         return ret;
     return new ast_binary(ctx, op, left, right);
 }
-
-int fold::cond(ir_value *condval, ast_function *func, ast_ifthen *branch) {
-    if (isfloat(condval) && fold_can_1(condval) && OPTS_OPTIMIZATION(OPTIM_CONST_FOLD_DCE)) {
-        ir_block               *elide;
-        ir_value               *dummy;
-        bool                    istrue  = (immvalue_float(condval) != 0.0f && branch->m_on_true);
-        bool                    isfalse = (immvalue_float(condval) == 0.0f && branch->m_on_false);
-        ast_expression         *path    = (istrue)  ? branch->m_on_true  :
-                                          (isfalse) ? branch->m_on_false : nullptr;
-        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(branch->m_context, func->m_ir_func, func->makeLabel((istrue) ? "ontrue" : "onfalse"))))
-            return false;
-        if (!path->codegen(func, false, &dummy))
-            return false;
-        if (!ir_block_create_jump(func->m_curblock, branch->m_context, elide))
-            return false;
-        /*
-         * now the branch has been eliminated and the correct block for the constant evaluation
-         * is expanded into the current block for the function.
-         */
-        func->m_curblock = elide;
-        ++opts_optimizationcount[OPTIM_CONST_FOLD_DCE];
-        return true;
-    }
-    return -1; /* nothing done */
-}
-
-int fold::cond_ternary(ir_value *condval, ast_function *func, ast_ternary *branch) {
-    return cond(condval, func, (ast_ifthen*)branch);
-}
-
-int fold::cond_ifthen(ir_value *condval, ast_function *func, ast_ifthen *branch) {
-    return cond(condval, func, branch);
-}