diff --git a/tools/qfcc/include/expr.h b/tools/qfcc/include/expr.h index 2b3887ae1..3c2f2a555 100644 --- a/tools/qfcc/include/expr.h +++ b/tools/qfcc/include/expr.h @@ -205,6 +205,8 @@ typedef struct ex_value_s { unsigned uint_val; ///< unsigned int constant int16_t short_val; ///< short constant uint16_t ushort_val; ///< unsigned short constant +#define VEC_TYPE(type_name, base_type) pr_##type_name##_t type_name##_val; +#include "tools/qfcc/include/vec_types.h" } v; } ex_value_t; @@ -309,7 +311,6 @@ expr_t *type_mismatch (expr_t *e1, expr_t *e2, int op); expr_t *param_mismatch (expr_t *e, int param, const char *fn, struct type_s *t1, struct type_s *t2); -expr_t *cast_error (expr_t *e, struct type_s *t1, struct type_s *t2); expr_t *test_error (expr_t *e, struct type_s *t); extern expr_t *local_expr; @@ -640,6 +641,7 @@ unsigned expr_uint (expr_t *e) __attribute__((pure)); */ expr_t *new_short_expr (short short_val); short expr_short (expr_t *e) __attribute__((pure)); +unsigned short expr_ushort (expr_t *e) __attribute__((pure)); int expr_integral (expr_t *e) __attribute__((pure)); diff --git a/tools/qfcc/source/Makemodule.am b/tools/qfcc/source/Makemodule.am index 0406cea3e..a070fad67 100644 --- a/tools/qfcc/source/Makemodule.am +++ b/tools/qfcc/source/Makemodule.am @@ -28,6 +28,7 @@ qfcc_SOURCES = \ tools/qfcc/source/expr_assign.c \ tools/qfcc/source/expr_binary.c \ tools/qfcc/source/expr_bool.c \ + tools/qfcc/source/expr_cast.c \ tools/qfcc/source/expr_compound.c \ tools/qfcc/source/expr_obj.c \ tools/qfcc/source/expr_vector.c \ diff --git a/tools/qfcc/source/expr.c b/tools/qfcc/source/expr.c index 8033d6435..388ab8e14 100644 --- a/tools/qfcc/source/expr.c +++ b/tools/qfcc/source/expr.c @@ -214,16 +214,9 @@ extract_type (expr_t *e) expr_t * type_mismatch (expr_t *e1, expr_t *e2, int op) { - dstring_t *t1 = dstring_newstr (); - dstring_t *t2 = dstring_newstr (); - - print_type_str (t1, get_type (e1)); - print_type_str (t2, get_type (e2)); - e1 = error (e1, "type mismatch: %s %s %s", - t1->str, get_op_string (op), t2->str); - dstring_delete (t1); - dstring_delete (t2); + get_type_string (get_type (e1)), get_op_string (op), + get_type_string (get_type (e2))); return e1; } @@ -243,21 +236,6 @@ param_mismatch (expr_t *e, int param, const char *fn, type_t *t1, type_t *t2) return e; } -expr_t * -cast_error (expr_t *e, type_t *t1, type_t *t2) -{ - dstring_t *s1 = dstring_newstr (); - dstring_t *s2 = dstring_newstr (); - - print_type_str (s1, t1); - print_type_str (s2, t2); - - e = error (e, "cannot cast from %s to %s", s1->str, s2->str); - dstring_delete (s1); - dstring_delete (s2); - return e; -} - expr_t * test_error (expr_t *e, type_t *t) { @@ -1145,6 +1123,22 @@ expr_short (expr_t *e) internal_error (e, "not a short constant"); } +unsigned short +expr_ushort (expr_t *e) +{ + if (e->type == ex_nil) { + return 0; + } + if (e->type == ex_value && e->e.value->lltype == ev_ushort) { + return e->e.value->v.ushort_val; + } + if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const + && e->e.symbol->type->type == ev_ushort) { + return e->e.symbol->s.value->v.ushort_val; + } + internal_error (e, "not a ushort constant"); +} + int is_integral_val (expr_t *e) { @@ -2872,83 +2866,6 @@ think_expr (symbol_t *think_sym) return new_symbol_expr (think_sym); } -expr_t * -cast_expr (type_t *dstType, expr_t *e) -{ - expr_t *c; - type_t *srcType; - - convert_name (e); - - if (e->type == ex_error) - return e; - - dstType = (type_t *) unalias_type (dstType); //FIXME cast - srcType = get_type (e); - - if (dstType == srcType) - return e; - - if ((dstType == type_default && is_enum (srcType)) - || (is_enum (dstType) && srcType == type_default)) - return e; - if ((is_ptr (dstType) && is_string (srcType)) - || (is_string (dstType) && is_ptr (srcType))) { - c = new_alias_expr (dstType, e); - return c; - } - if (!(is_ptr (dstType) && (is_ptr (srcType) || is_integral (srcType) - || is_array (srcType))) - && !(is_integral (dstType) && is_ptr (srcType)) - && !(is_func (dstType) && is_func (srcType)) - && !(is_scalar (dstType) && is_scalar (srcType))) { - return cast_error (e, srcType, dstType); - } - if (is_array (srcType)) { - return address_expr (e, dstType->t.fldptr.type); - } - if (is_constant (e) && is_scalar (dstType) && is_scalar (srcType)) { - ex_value_t *val = 0; - if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const) { - val = e->e.symbol->s.value; - } else if (e->type == ex_symbol - && e->e.symbol->sy_type == sy_var) { - // initialized global def treated as a constant - // from the tests above, the def is known to be constant - // and of one of the three storable scalar types - def_t *def = e->e.symbol->s.def; - if (is_float (def->type)) { - val = new_float_val (D_FLOAT (def)); - } else if (is_double (def->type)) { - val = new_double_val (D_DOUBLE (def)); - } else if (is_integral (def->type)) { - val = new_int_val (D_INT (def)); - } - } else if (e->type == ex_value) { - val = e->e.value; - } else if (e->type == ex_nil) { - convert_nil (e, dstType); - return e; - } - if (!val) - internal_error (e, "unexpected constant expression type"); - e->e.value = convert_value (val, dstType); - e->type = ex_value; - c = e; - } else if (is_integral (dstType) && is_integral (srcType)) { - c = new_alias_expr (dstType, e); - } else if (is_scalar (dstType) && is_scalar (srcType)) { - c = new_unary_expr ('C', e); - c->e.expr.type = dstType; - } else if (e->type == ex_uexpr && e->e.expr.op == '.') { - e->e.expr.type = dstType; - c = e; - } else { - c = new_alias_expr (dstType, e); - } - return c; -} - expr_t * encode_expr (type_t *type) { diff --git a/tools/qfcc/source/expr_cast.c b/tools/qfcc/source/expr_cast.c new file mode 100644 index 000000000..9ec9e9d1a --- /dev/null +++ b/tools/qfcc/source/expr_cast.c @@ -0,0 +1,148 @@ +/* + expr_cast.c + + expression casting + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/04/27 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "QF/mathlib.h" + +#include "tools/qfcc/include/qfcc.h" +#include "tools/qfcc/include/def.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/type.h" +#include "tools/qfcc/include/value.h" + +static expr_t * +cast_error (expr_t *e, type_t *t1, type_t *t2) +{ + e = error (e, "cannot cast from %s to %s", get_type_string (t1), + get_type_string (t2)); + return e; +} + +static void +do_conversion (pr_type_t *dst_value, type_t *dstType, + pr_type_t *src_value, type_t *srcType, expr_t *expr) +{ + int from = type_cast_map[base_type (srcType)->type]; + int to = type_cast_map[base_type (dstType)->type]; + int width = type_width (srcType) - 1; + int conversion = TYPE_CAST_CODE (from, to, width); +#define OPA(type) (*((pr_##type##_t *) (src_value))) +#define OPC(type) (*((pr_##type##_t *) (dst_value))) + switch (conversion) { +#include "libs/gamecode/pr_convert.cinc" + default: + internal_error (expr, "invalid conversion code: %04o", conversion); + } +} + +static expr_t * +cast_math (type_t *dstType, type_t *srcType, expr_t *expr) +{ + pr_type_t src_value[type_size (srcType)]; + pr_type_t dst_value[type_size (dstType)]; + + value_store (src_value, srcType, expr); + + do_conversion (dst_value, dstType, src_value, srcType, expr); + + expr_t *val = new_expr (); + val->type = ex_value; + val->e.value = new_type_value (dstType, dst_value); + return val; +} + +expr_t * +cast_expr (type_t *dstType, expr_t *e) +{ + expr_t *c; + type_t *srcType; + + convert_name (e); + + if (e->type == ex_error) + return e; + + dstType = (type_t *) unalias_type (dstType); //FIXME cast + srcType = get_type (e); + + if (dstType == srcType) + return e; + + if ((dstType == type_default && is_enum (srcType)) + || (is_enum (dstType) && srcType == type_default)) + return e; + if ((is_ptr (dstType) && is_string (srcType)) + || (is_string (dstType) && is_ptr (srcType))) { + c = new_alias_expr (dstType, e); + return c; + } + if (!(is_ptr (dstType) && (is_ptr (srcType) || is_integral (srcType) + || is_array (srcType))) + && !(is_integral (dstType) && is_ptr (srcType)) + && !(is_func (dstType) && is_func (srcType)) + && !(is_math (dstType) && is_math (srcType) + && type_width (dstType) == type_width (srcType)) + && !((is_int (dstType) || is_uint (dstType)) + && (is_short (srcType) || is_ushort (srcType)) + // [u]short is always width 0 + && type_width (dstType) == 1)) { + return cast_error (e, srcType, dstType); + } + if (is_array (srcType)) { + return address_expr (e, dstType->t.fldptr.type); + } + if (is_short (srcType)) { + e = new_int_expr (expr_short (e)); + srcType = &type_int; + } else if (is_ushort (srcType)) { + e = new_int_expr (expr_ushort (e)); + srcType = &type_int; + } + if (is_constant (e) && is_math (dstType) && is_math (srcType)) { + return cast_math (dstType, srcType, e); + } else if (is_integral (dstType) && is_integral (srcType)) { + c = new_alias_expr (dstType, e); + } else if (is_scalar (dstType) && is_scalar (srcType)) { + c = new_unary_expr ('C', e); + c->e.expr.type = dstType; + } else if (e->type == ex_uexpr && e->e.expr.op == '.') { + e->e.expr.type = dstType; + c = e; + } else { + c = new_alias_expr (dstType, e); + } + return c; +}