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");
* 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))
/*
/* 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))
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 = new ast_value(ctx(), "#IMMEDIATE", TYPE_FLOAT);
out->m_cvq = CV_CONST;
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 = new ast_value(ctx(), "#IMMEDIATE", TYPE_VECTOR);
out->m_cvq = CV_CONST;
m_imm_vector.push_back(out);
- return (ast_expression*)out;
+ return out;
}
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];
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 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'];
if (!y && !z) {
ast_expression *out;
++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
- out = ast_member::make(ctx(), (ast_expression*)sel, set[0]-'x', "");
+ 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 nullptr;
}
-
ast_expression *fold::op_neg(ast_value *a) {
if (isfloat(a)) {
if (fold_can_1(a)) {
return new ast_binary(
ctx(),
INSTR_MUL_F,
- (ast_expression*)a,
+ a,
constgen_float(1.0f / immvalue_float(b), false)
);
}
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)
+ ? constgen_float(1.0f / immvalue_float(b), false)
: new ast_binary(ctx(),
INSTR_DIV_F,
- (ast_expression*)m_imm_float[1],
- (ast_expression*)b
+ m_imm_float[1],
+ b
)
);
}
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))
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;
}
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;
}
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;
}
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;
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");
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;
}
case INSTR_DIV_F:
if (swapped)
return nullptr;
+ [[fallthrough]];
case INSTR_MUL_F:
if (immvalue_float(load) == 1.0f) {
++opts_optimizationcount[OPTIM_PEEPHOLE];
case INSTR_SUB_F:
if (swapped)
return nullptr;
+ [[fallthrough]];
case INSTR_ADD_F:
if (immvalue_float(load) == 0.0f) {
++opts_optimizationcount[OPTIM_PEEPHOLE];
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];
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);
-}