From c88820c31ef5ae3b10984dafa5ee0e39cdba4a5c Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Mon, 20 Jan 2025 00:55:01 +0900 Subject: [PATCH] [qfcc] Split test_expr per target While there's a bit of code duplication, it very much cleans things up for the various targets, especially v6 progs. However, spirv is not implemented yet, so that's broken again. --- tools/qfcc/include/target.h | 1 + tools/qfcc/source/expr.c | 4 +- tools/qfcc/source/expr_binary.c | 13 ++-- tools/qfcc/source/expr_bool.c | 102 +++++--------------------- tools/qfcc/source/expr_cast.c | 11 +-- tools/qfcc/source/expr_process.c | 1 + tools/qfcc/source/target_rua.c | 116 +++++++++++++++++++++++++++++ tools/qfcc/source/target_spirv.c | 7 ++ tools/qfcc/source/target_v6.c | 122 +++++++++++++++++++++++++++++++ tools/qfcc/source/type.c | 11 ++- 10 files changed, 282 insertions(+), 106 deletions(-) diff --git a/tools/qfcc/include/target.h b/tools/qfcc/include/target.h index ce871da3e..db57a36a0 100644 --- a/tools/qfcc/include/target.h +++ b/tools/qfcc/include/target.h @@ -52,6 +52,7 @@ typedef struct { // for both vector and quaternion types const expr_t *(*vector_compare)(int op, const expr_t *e1, const expr_t *e2); const expr_t *(*shift_op)(int op, const expr_t *e1, const expr_t *e2); + const expr_t *(*test_expr) (const expr_t *expr); bool (*setup_intrinsic_symtab) (symtab_t *symtab); diff --git a/tools/qfcc/source/expr.c b/tools/qfcc/source/expr.c index 86f618ec3..6a3c9cba8 100644 --- a/tools/qfcc/source/expr.c +++ b/tools/qfcc/source/expr.c @@ -111,9 +111,7 @@ get_type (const expr_t *e) type = e->compound.type; break; case ex_bool: - if (options.code.progsversion == PROG_ID_VERSION) - return &type_float; - return &type_int; + return &type_bool; case ex_nil: if (e->nil) { type = e->nil; diff --git a/tools/qfcc/source/expr_binary.c b/tools/qfcc/source/expr_binary.c index 53c81b25f..a5ba2f1f6 100644 --- a/tools/qfcc/source/expr_binary.c +++ b/tools/qfcc/source/expr_binary.c @@ -186,9 +186,11 @@ promote_exprs (const expr_t **e1, const expr_t **e2) //FIXME proper backing type for enum like handle t1 = type_default; t2 = type_default; - } else if ((is_vector (t1) || is_quaternion (t1)) && is_float (t2)) { + } else if ((is_vector (t1) || is_quaternion (t1)) + && (is_float (t2) || is_bool (t2))) { t2 = promote_type (t1, t2); - } else if ((is_vector (t2) || is_quaternion (t2)) && is_float (t1)) { + } else if ((is_vector (t2) || is_quaternion (t2)) + && (is_float (t1) || is_bool (t2))) { t1 = promote_type (t2, t1); } else if (type_promotes (t1, t2)) { t2 = promote_type (t1, t2); @@ -952,7 +954,7 @@ binary_expr (int op, const expr_t *e1, const expr_t *e2) if (expr_type->process) { auto e = expr_type->process (op, e1, e2); - return edag_add_expr (e); + return edag_add_expr (fold_constants (e)); } auto type = t1; @@ -981,10 +983,5 @@ binary_expr (int op, const expr_t *e1, const expr_t *e2) if (expr_type->associative) { ne->expr.associative = expr_type->associative (); } - if (is_compare (op) || is_logic (op)) { - if (options.code.progsversion == PROG_ID_VERSION) { - ne->expr.type = &type_float; - } - } return edag_add_expr (fold_constants (ne)); } diff --git a/tools/qfcc/source/expr_bool.c b/tools/qfcc/source/expr_bool.c index 002259270..34407cd33 100644 --- a/tools/qfcc/source/expr_bool.c +++ b/tools/qfcc/source/expr_bool.c @@ -62,100 +62,36 @@ #include "tools/qfcc/include/strpool.h" #include "tools/qfcc/include/struct.h" #include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/target.h" #include "tools/qfcc/include/type.h" #include "tools/qfcc/include/value.h" const expr_t * test_expr (const expr_t *e) { - const expr_t *new = 0; - - if (e->type == ex_error) + if (is_error (e)) { return e; - + } + if (e->type == ex_bool) { + return e; + } auto type = get_type (e); - if (e->type == ex_error) - return e; - scoped_src_loc (e); - switch (type->type) { - case ev_type_count: - internal_error (e, 0); - case ev_void: - if (options.traditional) { - if (options.warnings.traditional) - warning (e, "void has no value"); - return e; - } - return error (e, "void has no value"); - case ev_string: - if (!options.code.ifstring) - return new_alias_expr (type_default, e); - new = new_string_expr (0); - break; - case ev_long: - case ev_ulong: - if (type->width > 1) { - e = new_horizontal_expr ('|', e, &type_long); - } - e = new_alias_expr (&type_ivec2, e); - return new_horizontal_expr ('|', e, &type_int); - case ev_ushort: - internal_error (e, "ushort not implemented"); - case ev_uint: - case ev_int: - case ev_short: - if (type->width > 1) { - e = new_horizontal_expr ('|', e, &type_int); - } - if (!is_int(type_default)) { - if (is_constant (e)) { - return cast_expr (type_default, e); - } - return new_alias_expr (type_default, e); + if (!type) { + // an error occured while getting the type and was already reported + return new_error_expr (); + } + if (is_void (type)) { + if (options.traditional) { + if (options.warnings.traditional) { + warning (e, "void has no value"); } return e; - case ev_float: - if (options.code.progsversion < PROG_VERSION - && (options.code.fast_float - || options.code.progsversion == PROG_ID_VERSION)) { - if (!is_float(type_default)) { - if (is_constant (e)) { - return cast_expr (type_default, e); - } - return new_alias_expr (type_default, e); - } - return e; - } - new = new_zero_expr (type); - new = binary_expr (QC_NE, e, new); - return test_expr (new); - case ev_double: - new = new_zero_expr (type); - new = binary_expr (QC_NE, e, new); - return test_expr (new); - case ev_vector: - new = new_zero_expr (&type_vector); - break; - case ev_entity: - return new_alias_expr (type_default, e); - case ev_field: - return new_alias_expr (type_default, e); - case ev_func: - return new_alias_expr (type_default, e); - case ev_ptr: - return new_alias_expr (type_default, e); - case ev_quaternion: - new = new_zero_expr (&type_quaternion); - break; - case ev_invalid: - if (is_enum (type)) { - new = new_nil_expr (); - break; - } - return test_error (e, get_type (e)); + } + return error (e, "void has no value"); } - new = binary_expr (QC_NE, e, new); - return new; + e = current_target.test_expr (e); + fold_constants (e); + return edag_add_expr (e); } void diff --git a/tools/qfcc/source/expr_cast.c b/tools/qfcc/source/expr_cast.c index ce19754c4..fb88f0361 100644 --- a/tools/qfcc/source/expr_cast.c +++ b/tools/qfcc/source/expr_cast.c @@ -124,15 +124,8 @@ cast_expr (const type_t *dstType, const expr_t *e) return conditional_expr (e, enum_one, enum_zero); } } - if (is_integral (dstType) && is_boolean (srcType)) { - auto type = dstType; - if (type_size (dstType) != type_size (srcType)) { - type = ev_types[srcType->type]; - } - e = new_alias_expr (type, e); - if (type != dstType) { - e = cast_expr (dstType, e); - } + if (is_boolean (srcType) && srcType->type == dstType->type) { + e = new_alias_expr (dstType, e); return e; } if (is_algebra (dstType) || is_algebra (srcType)) { diff --git a/tools/qfcc/source/expr_process.c b/tools/qfcc/source/expr_process.c index 7d2b8a68c..bbcd9d7c3 100644 --- a/tools/qfcc/source/expr_process.c +++ b/tools/qfcc/source/expr_process.c @@ -816,6 +816,7 @@ static const expr_t * proc_select (const expr_t *expr, rua_ctx_t *ctx) { auto test = expr_process (expr->select.test, ctx); + test = test_expr (test); edag_flush (); auto true_body = expr_process (expr->select.true_body, ctx); edag_flush (); diff --git a/tools/qfcc/source/target_rua.c b/tools/qfcc/source/target_rua.c index 1ce2a2be6..10500e26c 100644 --- a/tools/qfcc/source/target_rua.c +++ b/tools/qfcc/source/target_rua.c @@ -42,6 +42,7 @@ #include "tools/qfcc/include/qfcc.h" #include "tools/qfcc/include/statements.h" #include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/struct.h" #include "tools/qfcc/include/switch.h" #include "tools/qfcc/include/symtab.h" #include "tools/qfcc/include/target.h" @@ -406,6 +407,120 @@ ruamoko_shift_op (int op, const expr_t *e1, const expr_t *e2) return fold_constants (e); } +static const expr_t * +ruamoko_test_expr (const expr_t *expr) +{ + scoped_src_loc (expr); + auto type = get_type (expr); + if (is_bool (type) && is_scalar (type)) { + return expr; + } + if (is_lbool (type) && is_scalar (type)) { + expr = new_alias_expr (&type_ivec2, expr); + expr = fold_constants (expr); + expr = edag_add_expr (expr); + return new_horizontal_expr ('|', expr, &type_int); + } + if (is_boolean (type)) { + // the above is_bool and is_lbool tests ensure a boolean type + // is a vector (there are no bool matrices) + type = base_type (type); + expr = new_horizontal_expr ('|', expr, type); + if (type_size (type) > 1) { + expr = fold_constants (expr); + expr = edag_add_expr (expr); + expr = new_alias_expr (&type_ivec2, expr); + expr = fold_constants (expr); + expr = edag_add_expr (expr); + expr = new_horizontal_expr ('|', expr, &type_bool); + } + return expr; + } + if (is_enum (type)) { + expr_t *zero, *one; + if (!enum_as_bool (type, &zero, &one)) { + warning (expr, "enum doesn't convert to bool"); + } + return new_alias_expr (&type_bool, expr); + } + switch (type->type) { + case ev_float: + case ev_double: + case ev_short: + case ev_ushort: + { + // short and ushort handled with the same code as float/double + // because they have no backing type and and thus constants, which + // fold_constants will take care of. + auto zero = new_zero_expr (type); + auto btype = bool_type (type); + expr = typed_binary_expr (btype, QC_NE, expr, zero); + if (type_width (btype) > 1) { + btype = base_type (btype); + expr = fold_constants (expr); + expr = edag_add_expr (expr); + expr = new_horizontal_expr ('|', expr, btype); + } + if (type_size (btype) > 1) { + expr = fold_constants (expr); + expr = edag_add_expr (expr); + expr = new_alias_expr (&type_ivec2, expr); + expr = fold_constants (expr); + expr = edag_add_expr (expr); + expr = new_horizontal_expr ('|', expr, &type_bool); + } + return expr; + } + case ev_vector: + case ev_quaternion: + { + auto zero = new_zero_expr (type); + auto btype = bool_type (type); + expr = typed_binary_expr (btype, QC_NE, expr, zero); + expr = fold_constants (expr); + expr = edag_add_expr (expr); + expr = new_horizontal_expr ('|', expr, &type_bool); + return expr; + } + case ev_string: + if (!options.code.ifstring) { + return new_alias_expr (&type_bool, expr); + } + return typed_binary_expr (&type_bool, QC_NE, expr, + new_string_expr (0)); + case ev_int: + case ev_uint: + if (type_width (type) > 1) { + expr = new_horizontal_expr ('|', expr, &type_int); + expr = fold_constants (expr); + expr = edag_add_expr (expr); + } + if (is_constant (expr)) { + return new_bool_expr (expr_int (expr)); + } + return new_alias_expr (&type_bool, expr); + case ev_long: + case ev_ulong: + if (type_width (type) > 1) { + expr = new_horizontal_expr ('|', expr, &type_long); + expr = fold_constants (expr); + expr = edag_add_expr (expr); + } + expr = new_alias_expr (&type_ivec2, expr); + return new_horizontal_expr ('|', expr, &type_int); + case ev_entity: + case ev_field: + case ev_func: + case ev_ptr: + return new_alias_expr (&type_bool, expr); + case ev_void: + case ev_invalid: + case ev_type_count: + break; + } + return error (expr, "cannot convert to bool"); +} + target_t ruamoko_target = { .value_too_large = ruamoko_value_too_large, .build_scope = ruamoko_build_scope, @@ -418,4 +533,5 @@ target_t ruamoko_target = { .proc_address = ruamoko_proc_address, .vector_compare = ruamoko_vector_compare, .shift_op = ruamoko_shift_op, + .test_expr = ruamoko_test_expr, }; diff --git a/tools/qfcc/source/target_spirv.c b/tools/qfcc/source/target_spirv.c index 893106e7e..fb1119e95 100644 --- a/tools/qfcc/source/target_spirv.c +++ b/tools/qfcc/source/target_spirv.c @@ -2241,6 +2241,12 @@ spirv_shift_op (int op, const expr_t *e1, const expr_t *e2) return fold_constants (e); } +static const expr_t * +spirv_test_expr (const expr_t *expr) +{ + return error (expr, "not implemented"); +} + target_t spirv_target = { .value_too_large = spirv_value_too_large, .build_scope = spirv_build_scope, @@ -2251,4 +2257,5 @@ target_t spirv_target = { .setup_intrinsic_symtab = spirv_setup_intrinsic_symtab, .vector_compare = spirv_vector_compare, .shift_op = spirv_shift_op, + .test_expr = spirv_test_expr, }; diff --git a/tools/qfcc/source/target_v6.c b/tools/qfcc/source/target_v6.c index 278ced755..d5d387a3d 100644 --- a/tools/qfcc/source/target_v6.c +++ b/tools/qfcc/source/target_v6.c @@ -42,6 +42,7 @@ #include "tools/qfcc/include/qfcc.h" #include "tools/qfcc/include/statements.h" #include "tools/qfcc/include/strpool.h" +#include "tools/qfcc/include/struct.h" #include "tools/qfcc/include/symtab.h" #include "tools/qfcc/include/target.h" #include "tools/qfcc/include/type.h" @@ -254,6 +255,125 @@ v6p_shift_op (int op, const expr_t *e1, const expr_t *e2) return fold_constants (e); } +static const expr_t * +v6_test_expr (const expr_t *expr) +{ + scoped_src_loc (expr); + auto type = get_type (expr); + if (is_bool (type)) { + return expr; + } + if (is_enum (type)) { + expr_t *zero, *one; + if (!enum_as_bool (type, &zero, &one)) { + warning (expr, "enum doesn't convert to bool"); + } + return new_alias_expr (&type_bool, expr); + } + switch (type->type) { + case ev_float: + { + // short and ushort handled with the same code as float/double + // because they have no backing type and and thus constants, which + // fold_constants will take care of. + auto zero = new_zero_expr (type); + auto btype = bool_type (type); + expr = typed_binary_expr (btype, QC_NE, expr, zero); + return expr; + } + case ev_vector: + case ev_quaternion: + { + auto zero = new_zero_expr (type); + return typed_binary_expr (&type_bool, QC_NE, expr, zero); + } + case ev_string: + if (!options.code.ifstring) { + return new_alias_expr (&type_bool, expr); + } + return typed_binary_expr (&type_bool, QC_NE, expr, + new_string_expr (0)); + case ev_entity: + case ev_field: + case ev_func: + case ev_ptr: + return new_alias_expr (&type_bool, expr); + case ev_int: + case ev_uint: + case ev_long: + case ev_ulong: + case ev_double: + case ev_short: + case ev_ushort: + case ev_void: + case ev_invalid: + case ev_type_count: + break; + } + return error (expr, "cannot convert to bool"); +} + +static const expr_t * +v6p_test_expr (const expr_t *expr) +{ + scoped_src_loc (expr); + auto type = get_type (expr); + if (is_bool (type)) { + return expr; + } + if (is_enum (type)) { + expr_t *zero, *one; + if (!enum_as_bool (type, &zero, &one)) { + warning (expr, "enum doesn't convert to bool"); + } + return new_alias_expr (&type_bool, expr); + } + switch (type->type) { + case ev_float: + case ev_double: + case ev_short: + case ev_ushort: + { + // short and ushort handled with the same code as float/double + // because they have no backing type and and thus constants, which + // fold_constants will take care of. + auto zero = new_zero_expr (type); + expr = typed_binary_expr (&type_bool, QC_NE, expr, zero); + return expr; + } + case ev_vector: + case ev_quaternion: + { + auto zero = new_zero_expr (type); + return typed_binary_expr (&type_bool, QC_NE, expr, zero); + } + case ev_string: + if (!options.code.ifstring) { + return new_alias_expr (&type_bool, expr); + } + return typed_binary_expr (&type_bool, QC_NE, expr, + new_string_expr (0)); + case ev_int: + case ev_uint: + if (is_constant (expr)) { + return new_bool_expr (expr_int (expr)); + } + return new_alias_expr (&type_bool, expr); + case ev_entity: + case ev_field: + case ev_func: + case ev_ptr: + return new_alias_expr (&type_bool, expr); + case ev_long: + case ev_ulong: + case ev_void: + case ev_invalid: + case ev_type_count: + break; + } + return error (expr, "cannot convert to bool"); +} + target_t v6_target = { .value_too_large = v6_value_too_large, .build_scope = v6p_build_scope, @@ -267,6 +387,7 @@ target_t v6_target = { .proc_address = ruamoko_proc_address, .vector_compare = v6_vector_compare, .shift_op = v6_shift_op, + .test_expr = v6_test_expr, }; target_t v6p_target = { @@ -282,4 +403,5 @@ target_t v6p_target = { .proc_address = ruamoko_proc_address, .vector_compare = v6p_vector_compare, .shift_op = v6p_shift_op, + .test_expr = v6p_test_expr, }; diff --git a/tools/qfcc/source/type.c b/tools/qfcc/source/type.c index 9cb4f3952..71736d7e8 100644 --- a/tools/qfcc/source/type.c +++ b/tools/qfcc/source/type.c @@ -1130,7 +1130,7 @@ print_type_str (dstring_t *str, const type_t *type) } return; case ty_bool: - dasprintf (str, " %s%s", type->type == ev_int ? "bool" : "lbool", + dasprintf (str, " %s%s", is_bool (type) ? "bool" : "lbool", type->width > 1 ? va (0, "{%d}", type->width) : ""); return; @@ -1505,7 +1505,7 @@ is_bool (const type_t *type) if (type->meta != ty_bool) { return false; } - return type->type == ev_int; + return type->type == ev_int || type->type == ev_float; } bool @@ -2059,6 +2059,11 @@ chain_basic_types (void) if (options.code.progsversion == PROG_VERSION) { type_quaternion.alignment = 4; } + if (options.code.progsversion == PROG_ID_VERSION) { + type_bool.type = ev_float; + } else { + type_bool.type = ev_int; + } chain_type (&type_void); chain_type (&type_string); @@ -2069,6 +2074,7 @@ chain_basic_types (void) chain_type (&type_func); chain_type (&type_ptr); chain_type (&type_floatfield); + chain_type (&type_bool); if (!options.traditional) { chain_type (&type_quaternion); chain_type (&type_int); @@ -2084,7 +2090,6 @@ chain_basic_types (void) #include "tools/qfcc/include/vec_types.h" #define MAT_TYPE(name, type, cols, align_as) chain_type (&type_##name); #include "tools/qfcc/include/mat_types.h" - chain_type (&type_bool); chain_type (&type_bvec2); chain_type (&type_bvec3); chain_type (&type_bvec4);