tests: xor peephole optimization regression test
[xonotic/gmqcc.git] / fold.cpp
index 306e5ca..a982bf6 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");
@@ -535,10 +535,10 @@ static GMQCC_INLINE void sfloat_init(sfloat_state_t *state) {
  * This file is thus, split into two parts.
  */
 
-#define isfloat(X)      (((ast_expression*)(X))->m_vtype == TYPE_FLOAT)
-#define isvector(X)     (((ast_expression*)(X))->m_vtype == TYPE_VECTOR)
-#define isstring(X)     (((ast_expression*)(X))->m_vtype == TYPE_STRING)
-#define isarray(X)      (((ast_expression*)(X))->m_vtype == TYPE_ARRAY)
+#define isfloat(X)      (((X))->m_vtype == TYPE_FLOAT)
+#define isvector(X)     (((X))->m_vtype == TYPE_VECTOR)
+#define isstring(X)     (((X))->m_vtype == TYPE_STRING)
+#define isarray(X)      (((X))->m_vtype == TYPE_ARRAY)
 #define isfloats(X,Y)   (isfloat  (X) && isfloat (Y))
 
 /*
@@ -902,8 +902,8 @@ bool fold::immediate_true(ast_value *v) {
 
 /* Handy macros to determine if an ast_value can be constant folded. */
 #define fold_can_1(X)  \
-    (ast_istype(((ast_expression*)(X)), ast_value) && (X)->m_hasvalue && ((X)->m_cvq == CV_CONST) && \
-                ((ast_expression*)(X))->m_vtype != TYPE_FUNCTION)
+    (ast_istype(((X)), ast_value) && (X)->m_hasvalue && ((X)->m_cvq == CV_CONST) && \
+                ((X))->m_vtype != TYPE_FUNCTION)
 
 #define fold_can_2(X, Y) (fold_can_1(X) && fold_can_1(Y))
 
@@ -931,14 +931,14 @@ bool fold::generate(ir_builder *ir) {
     // generate globals for immediate folded values
     ast_value *cur;
     for (auto &it : m_imm_float)
-        if (!ast_global_codegen((cur = it), ir, false)) goto err;
+        if (!(cur = it)->generateGlobal(ir, false)) goto err;
     for (auto &it : m_imm_vector)
-        if (!ast_global_codegen((cur = it), ir, false)) goto err;
+        if (!(cur = it)->generateGlobal(ir, false)) goto err;
     for (auto &it : m_imm_string)
-        if (!ast_global_codegen((cur = it), ir, false)) goto err;
+        if (!(cur = it)->generateGlobal(ir, false)) goto err;
     return true;
 err:
-    con_out("failed to generate global %s\n", cur->m_name);
+    con_out("failed to generate global %s\n", cur->m_name.c_str());
     delete ir;
     return false;
 }
@@ -958,9 +958,9 @@ fold::~fold() {
 ast_expression *fold::constgen_float(qcfloat_t value, bool inexact) {
     for (auto &it : m_imm_float)
         if (!memcmp(&it->m_constval.vfloat, &value, sizeof(qcfloat_t)))
-            return (ast_expression*)it;
+            return it;
 
-    ast_value *out  = ast_value_new(ctx(), "#IMMEDIATE", TYPE_FLOAT);
+    ast_value *out  = new ast_value(ctx(), "#IMMEDIATE", TYPE_FLOAT);
     out->m_cvq = CV_CONST;
     out->m_hasvalue = true;
     out->m_inexact = inexact;
@@ -968,22 +968,22 @@ ast_expression *fold::constgen_float(qcfloat_t value, bool inexact) {
 
     m_imm_float.push_back(out);
 
-    return (ast_expression*)out;
+    return out;
 }
 
 ast_expression *fold::constgen_vector(vec3_t value) {
     for (auto &it : m_imm_vector)
         if (vec3_cmp(it->m_constval.vvec, value))
-            return (ast_expression*)it;
+            return it;
 
-    ast_value *out = ast_value_new(ctx(), "#IMMEDIATE", TYPE_VECTOR);
+    ast_value *out = new ast_value(ctx(), "#IMMEDIATE", TYPE_VECTOR);
     out->m_cvq = CV_CONST;
     out->m_hasvalue = true;
     out->m_constval.vvec = value;
 
     m_imm_vector.push_back(out);
 
-    return (ast_expression*)out;
+    return out;
 }
 
 ast_expression *fold::constgen_string(const char *str, bool translate) {
@@ -992,15 +992,15 @@ ast_expression *fold::constgen_string(const char *str, bool translate) {
     size_t hash = util_hthash(table, str);
 
     if ((out = (ast_value*)util_htgeth(table, str, hash)))
-        return (ast_expression*)out;
+        return out;
 
     if (translate) {
         char name[32];
         util_snprintf(name, sizeof(name), "dotranslate_%zu", m_parser->translated++);
-        out = ast_value_new(ctx(), name, TYPE_STRING);
+        out = new ast_value(ctx(), name, TYPE_STRING);
         out->m_flags |= AST_FLAG_INCLUDE_DEF; /* def needs to be included for translatables */
     } else {
-        out = ast_value_new(ctx(), "#IMMEDIATE", TYPE_STRING);
+        out = new ast_value(ctx(), "#IMMEDIATE", TYPE_STRING);
     }
 
     out->m_cvq = CV_CONST;
@@ -1011,7 +1011,11 @@ ast_expression *fold::constgen_string(const char *str, bool translate) {
     m_imm_string.push_back(out);
     util_htseth(table, str, hash, out);
 
-    return (ast_expression*)out;
+    return out;
+}
+
+ast_expression *fold::constgen_string(const std::string &str, bool translate) {
+  return constgen_string(str.c_str(), translate);
 }
 
 typedef union {
@@ -1069,11 +1073,11 @@ ast_expression *fold::op_mul_vec(vec3_t vec, ast_value *sel, const char *set) {
     if (!y && !z) {
         ast_expression *out;
         ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
-        out = (ast_expression*)ast_member_new(ctx(), (ast_expression*)sel, set[0]-'x', nullptr);
+        out = ast_member::make(ctx(), sel, set[0]-'x', "");
         out->m_keep_node = false;
         ((ast_member*)out)->m_rvalue = true;
         if (x != -1.0f)
-            return (ast_expression*)ast_binary_new(ctx(), INSTR_MUL_F, constgen_float(x, false), out);
+            return new ast_binary(ctx(), INSTR_MUL_F, constgen_float(x, false), out);
     }
     return nullptr;
 }
@@ -1181,10 +1185,10 @@ ast_expression *fold::op_div(ast_value *a, ast_value *b) {
             bool inexact = check_except_float(&sfloat_div, a, b);
             return constgen_float(immvalue_float(a) / immvalue_float(b), inexact);
         } else if (fold_can_1(b)) {
-            return (ast_expression*)ast_binary_new(
+            return new ast_binary(
                 ctx(),
                 INSTR_MUL_F,
-                (ast_expression*)a,
+                a,
                 constgen_float(1.0f / immvalue_float(b), false)
             );
         }
@@ -1192,17 +1196,16 @@ ast_expression *fold::op_div(ast_value *a, ast_value *b) {
         if (fold_can_2(a, b)) {
             return constgen_vector(vec3_mulvf(ctx(), immvalue_vector(a), 1.0f / immvalue_float(b)));
         } else {
-            return (ast_expression*)ast_binary_new(
+            return new ast_binary(
                 ctx(),
                 INSTR_MUL_VF,
-                (ast_expression*)a,
+                a,
                 (fold_can_1(b))
-                    ? (ast_expression*)constgen_float(1.0f / immvalue_float(b), false)
-                    : (ast_expression*)ast_binary_new(
-                                            ctx(),
-                                            INSTR_DIV_F,
-                                            (ast_expression*)m_imm_float[1],
-                                            (ast_expression*)b
+                    ? constgen_float(1.0f / immvalue_float(b), false)
+                    : new ast_binary(ctx(),
+                                     INSTR_DIV_F,
+                                     m_imm_float[1],
+                                     b
                     )
             );
         }
@@ -1279,9 +1282,9 @@ ast_expression *fold::op_andor(ast_value *a, ast_value *b, float expr) {
     if (fold_can_2(a, b)) {
         if (OPTS_FLAG(PERL_LOGIC)) {
             if (expr)
-                return immediate_true(a) ? (ast_expression*)a : (ast_expression*)b;
+                return immediate_true(a) ? a : b;
             else
-                return immediate_true(a) ? (ast_expression*)b : (ast_expression*)a;
+                return immediate_true(a) ? b : a;
         } else {
             return constgen_float(
                 ((expr) ? (immediate_true(a) || immediate_true(b))
@@ -1298,8 +1301,8 @@ ast_expression *fold::op_andor(ast_value *a, ast_value *b, float expr) {
 ast_expression *fold::op_tern(ast_value *a, ast_value *b, ast_value *c) {
     if (fold_can_1(a)) {
         return immediate_true(a)
-                    ? (ast_expression*)b
-                    : (ast_expression*)c;
+                    ? b
+                    : c;
     }
     return nullptr;
 }
@@ -1313,9 +1316,9 @@ ast_expression *fold::op_exp(ast_value *a, ast_value *b) {
 ast_expression *fold::op_lteqgt(ast_value *a, ast_value *b) {
     if (fold_can_2(a,b)) {
         check_inexact_float(a, b);
-        if (immvalue_float(a) <  immvalue_float(b)) return (ast_expression*)m_imm_float[2];
-        if (immvalue_float(a) == immvalue_float(b)) return (ast_expression*)m_imm_float[0];
-        if (immvalue_float(a) >  immvalue_float(b)) return (ast_expression*)m_imm_float[1];
+        if (immvalue_float(a) <  immvalue_float(b)) return m_imm_float[2];
+        if (immvalue_float(a) == immvalue_float(b)) return m_imm_float[0];
+        if (immvalue_float(a) >  immvalue_float(b)) return m_imm_float[1];
     }
     return nullptr;
 }
@@ -1323,8 +1326,8 @@ ast_expression *fold::op_lteqgt(ast_value *a, ast_value *b) {
 ast_expression *fold::op_ltgt(ast_value *a, ast_value *b, bool lt) {
     if (fold_can_2(a, b)) {
         check_inexact_float(a, b);
-        return (lt) ? (ast_expression*)m_imm_float[!!(immvalue_float(a) < immvalue_float(b))]
-                    : (ast_expression*)m_imm_float[!!(immvalue_float(a) > immvalue_float(b))];
+        return (lt) ? m_imm_float[!!(immvalue_float(a) < immvalue_float(b))]
+                    : m_imm_float[!!(immvalue_float(a) > immvalue_float(b))];
     }
     return nullptr;
 }
@@ -1335,11 +1338,15 @@ ast_expression *fold::op_cmp(ast_value *a, ast_value *b, bool ne) {
             float la = immvalue_float(a);
             float lb = immvalue_float(b);
             check_inexact_float(a, b);
-            return (ast_expression*)m_imm_float[!(ne ? la == lb : la != lb)];
-        } if (isvector(a) && isvector(b)) {
+            return m_imm_float[ne ? la != lb : la == lb];
+        } else if (isvector(a) && isvector(b)) {
             vec3_t la = immvalue_vector(a);
             vec3_t lb = immvalue_vector(b);
-            return (ast_expression*)m_imm_float[!(ne ? vec3_cmp(la, lb) : !vec3_cmp(la, lb))];
+            bool compare = vec3_cmp(la, lb);
+            return m_imm_float[ne ? !compare : compare];
+        } else if (isstring(a) && isstring(b)) {
+            bool compare = !strcmp(immvalue_string(a), immvalue_string(b));
+            return m_imm_float[ne ? !compare : compare];
         }
     }
     return nullptr;
@@ -1385,8 +1392,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");
@@ -1479,29 +1486,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(const char *intrinsic, ast_expression **arg) {
+ast_expression* fold::intrinsic_inf(void) {
+  return constgen_float(1.0f / 0.0f, false);
+}
+
+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;
 }
@@ -1553,6 +1585,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];
@@ -1565,6 +1598,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];
@@ -1584,6 +1618,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];
@@ -1600,12 +1635,11 @@ ast_expression *fold::binary(lex_ctx_t ctx, int op, ast_expression *left, ast_ex
     ast_expression *ret = superfluous(left, right, op);
     if (ret)
         return ret;
-    return (ast_expression*)ast_binary_new(ctx, op, left, right);
+    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)) {
-        ast_expression_codegen *cgen;
         ir_block               *elide;
         ir_value               *dummy;
         bool                    istrue  = (immvalue_float(condval) != 0.0f && branch->m_on_true);
@@ -1621,9 +1655,9 @@ int fold::cond(ir_value *condval, ast_function *func, ast_ifthen *branch) {
             return true;
         }
 
-        if (!(elide = ir_function_create_block(branch->m_context, func->m_ir_func, ast_function_label(func, ((istrue) ? "ontrue" : "onfalse")))))
+        if (!(elide = ir_function_create_block(branch->m_context, func->m_ir_func, func->makeLabel((istrue) ? "ontrue" : "onfalse"))))
             return false;
-        if (!(*(cgen = path->m_codegen))((ast_expression*)path, func, false, &dummy))
+        if (!path->codegen(func, false, &dummy))
             return false;
         if (!ir_block_create_jump(func->m_curblock, branch->m_context, elide))
             return false;