/* exprtype.c Expression type manipulation Copyright (C) 2013 Bill Currie Author: Bill Currie Date: 2013/06/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 "tools/qfcc/include/diagnostic.h" #include "tools/qfcc/include/expr.h" #include "tools/qfcc/include/options.h" #include "tools/qfcc/include/type.h" #include "tools/qfcc/source/qc-parse.h" typedef struct { int op; type_t *result_type; type_t *a_cast; type_t *b_cast; expr_t *(*process)(int op, expr_t *e1, expr_t *e2); } expr_type_t; static expr_t *pointer_arithmetic (int op, expr_t *e1, expr_t *e2); static expr_t *pointer_compare (int op, expr_t *e1, expr_t *e2); static expr_t *func_compare (int op, expr_t *e1, expr_t *e2); static expr_t *inverse_multiply (int op, expr_t *e1, expr_t *e2); static expr_t *double_compare (int op, expr_t *e1, expr_t *e2); static expr_t *vector_compare (int op, expr_t *e1, expr_t *e2); static expr_t *vector_multiply (int op, expr_t *e1, expr_t *e2); static expr_t *vector_scale (int op, expr_t *e1, expr_t *e2); static expr_t *entity_compare (int op, expr_t *e1, expr_t *e2); static expr_type_t string_string[] = { {'+', &type_string}, {EQ, &type_int}, {NE, &type_int}, {LE, &type_int}, {GE, &type_int}, {LT, &type_int}, {GT, &type_int}, {0, 0} }; static expr_type_t float_float[] = { {'+', &type_float}, {'-', &type_float}, {'*', &type_float}, {'/', &type_float}, {'&', &type_float}, {'|', &type_float}, {'^', &type_float}, {'%', &type_float}, {MOD, &type_float}, {SHL, &type_float}, {SHR, &type_float}, {AND, &type_int}, {OR, &type_int}, {EQ, &type_int}, {NE, &type_int}, {LE, &type_int}, {GE, &type_int}, {LT, &type_int}, {GT, &type_int}, {0, 0} }; static expr_type_t float_vector[] = { {'*', .process = vector_scale }, {0, 0} }; static expr_type_t float_quat[] = { {'*', &type_quaternion}, {0, 0} }; static expr_type_t float_int[] = { {'+', &type_float, 0, &type_float}, {'-', &type_float, 0, &type_float}, {'*', &type_float, 0, &type_float}, {'/', &type_float, 0, &type_float}, {'&', &type_float, 0, &type_float}, {'|', &type_float, 0, &type_float}, {'^', &type_float, 0, &type_float}, {'%', &type_float, 0, &type_float}, {MOD, &type_float, 0, &type_float}, {SHL, &type_float, 0, &type_float}, {SHR, &type_float, 0, &type_float}, {EQ, &type_int, 0, &type_float}, {NE, &type_int, 0, &type_float}, {LE, &type_int, 0, &type_float}, {GE, &type_int, 0, &type_float}, {LT, &type_int, 0, &type_float}, {GT, &type_int, 0, &type_float}, {0, 0} }; #define float_uint float_int #define float_short float_int static expr_type_t float_double[] = { {'+', &type_double, &type_double, 0}, {'-', &type_double, &type_double, 0}, {'*', &type_double, &type_double, 0}, {'/', &type_double, &type_double, 0}, {'%', &type_double, &type_double, 0}, {MOD, &type_double, &type_double, 0}, {EQ, 0, 0, 0, double_compare}, {NE, 0, 0, 0, double_compare}, {LE, 0, 0, 0, double_compare}, {GE, 0, 0, 0, double_compare}, {LT, 0, 0, 0, double_compare}, {GT, 0, 0, 0, double_compare}, {0, 0} }; static expr_type_t vector_float[] = { {'*', .process = vector_scale }, {'/', 0, 0, 0, inverse_multiply}, {0, 0} }; static expr_type_t vector_vector[] = { {'+', &type_vector}, {'-', &type_vector}, {DOT, &type_vector}, {CROSS, &type_vector}, {HADAMARD, &type_vector}, {'*', 0, 0, 0, vector_multiply}, {EQ, 0, 0, 0, vector_compare}, {NE, 0, 0, 0, vector_compare}, {0, 0} }; #define vector_int vector_float #define vector_uint vector_float #define vector_short vector_float static expr_type_t vector_double[] = { {'*', &type_vector, 0, &type_float, vector_scale}, {'/', 0, 0, 0, inverse_multiply}, {0, 0} }; static expr_type_t entity_entity[] = { {EQ, &type_int, 0, 0, entity_compare}, {NE, &type_int, 0, 0, entity_compare}, {0, 0} }; static expr_type_t field_field[] = { {EQ, &type_int}, {NE, &type_int}, {0, 0} }; static expr_type_t func_func[] = { {EQ, 0, 0, 0, func_compare}, {NE, 0, 0, 0, func_compare}, {0, 0} }; static expr_type_t pointer_pointer[] = { {'-', 0, 0, 0, pointer_arithmetic}, {EQ, 0, 0, 0, pointer_compare}, {NE, 0, 0, 0, pointer_compare}, {LE, 0, 0, 0, pointer_compare}, {GE, 0, 0, 0, pointer_compare}, {LT, 0, 0, 0, pointer_compare}, {GT, 0, 0, 0, pointer_compare}, {0, 0} }; static expr_type_t pointer_int[] = { {'+', 0, 0, 0, pointer_arithmetic}, {'-', 0, 0, 0, pointer_arithmetic}, {0, 0} }; #define pointer_uint pointer_int #define pointer_short pointer_int static expr_type_t quat_float[] = { {'*', &type_quaternion}, {'/', 0, 0, 0, inverse_multiply}, {0, 0} }; static expr_type_t quat_vector[] = { {'*', &type_vector}, {0, 0} }; static expr_type_t quat_quat[] = { {'+', &type_quaternion}, {'-', &type_quaternion}, {'*', &type_quaternion}, {EQ, &type_int}, {NE, &type_int}, {0, 0} }; static expr_type_t quat_int[] = { {'*', &type_quaternion, 0, &type_float}, {'/', 0, 0, 0, inverse_multiply}, {0, 0} }; #define quat_uint quat_int #define quat_short quat_int static expr_type_t quat_double[] = { {'*', &type_quaternion}, {'/', 0, 0, 0, inverse_multiply}, {0, 0} }; static expr_type_t int_float[] = { {'+', &type_float, &type_float, 0}, {'-', &type_float, &type_float, 0}, {'*', &type_float, &type_float, 0}, {'/', &type_float, &type_float, 0}, {'&', &type_float, &type_float, 0}, {'|', &type_float, &type_float, 0}, {'^', &type_float, &type_float, 0}, {'%', &type_float, &type_float, 0}, {MOD, &type_float, &type_float, 0}, {SHL, &type_int, 0, &type_int}, //FIXME? {SHR, &type_int, 0, &type_int}, //FIXME? {EQ, &type_int, &type_float, 0}, {NE, &type_int, &type_float, 0}, {LE, &type_int, &type_float, 0}, {GE, &type_int, &type_float, 0}, {LT, &type_int, &type_float, 0}, {GT, &type_int, &type_float, 0}, {0, 0} }; static expr_type_t int_vector[] = { {'*', &type_vector, &type_float, 0, vector_scale}, {0, 0} }; static expr_type_t int_pointer[] = { {'+', 0, 0, 0, pointer_arithmetic}, {0, 0} }; static expr_type_t int_quat[] = { {'*', &type_quaternion, &type_float, 0}, {0, 0} }; static expr_type_t int_int[] = { {'+', &type_int}, {'-', &type_int}, {'*', &type_int}, {'/', &type_int}, {'&', &type_int}, {'|', &type_int}, {'^', &type_int}, {'%', &type_int}, {MOD, &type_int}, {SHL, &type_int}, {SHR, &type_int}, {AND, &type_int}, {OR, &type_int}, {EQ, &type_int}, {NE, &type_int}, {LE, &type_int}, {GE, &type_int}, {LT, &type_int}, {GT, &type_int}, {0, 0} }; static expr_type_t int_uint[] = { {'+', &type_int, 0, &type_int}, {'-', &type_int, 0, &type_int}, {'*', &type_int, 0, &type_int}, {'/', &type_int, 0, &type_int}, {'&', &type_int, 0, &type_int}, {'|', &type_int, 0, &type_int}, {'^', &type_int, 0, &type_int}, {'%', &type_int, 0, &type_int}, {MOD, &type_int, 0, &type_int}, {SHL, &type_int, 0, &type_int}, {SHR, &type_int, 0, &type_int}, {EQ, &type_int, 0, &type_int}, {NE, &type_int, 0, &type_int}, {LE, &type_int, 0, &type_int}, {GE, &type_int, 0, &type_int}, {LT, &type_int, 0, &type_int}, {GT, &type_int, 0, &type_int}, {0, 0} }; static expr_type_t int_short[] = { {'+', &type_int, 0, &type_int}, {'-', &type_int, 0, &type_int}, {'*', &type_int, 0, &type_int}, {'/', &type_int, 0, &type_int}, {'&', &type_int, 0, &type_int}, {'|', &type_int, 0, &type_int}, {'^', &type_int, 0, &type_int}, {'%', &type_int, 0, &type_int}, {MOD, &type_int, 0, &type_int}, {SHL, &type_int, 0, &type_int}, {SHR, &type_int, 0, &type_int}, {EQ, &type_int, 0, &type_int}, {NE, &type_int, 0, &type_int}, {LE, &type_int, 0, &type_int}, {GE, &type_int, 0, &type_int}, {LT, &type_int, 0, &type_int}, {GT, &type_int, 0, &type_int}, {0, 0} }; static expr_type_t int_double[] = { {'+', &type_double, &type_double, 0}, {'-', &type_double, &type_double, 0}, {'*', &type_double, &type_double, 0}, {'/', &type_double, &type_double, 0}, {'%', &type_double, &type_double, 0}, {MOD, &type_double, &type_double, 0}, {EQ, &type_int, &type_double, 0}, {NE, &type_int, &type_double, 0}, {LE, &type_int, &type_double, 0}, {GE, &type_int, &type_double, 0}, {LT, &type_int, &type_double, 0}, {GT, &type_int, &type_double, 0}, {0, 0} }; #define uint_float int_float #define uint_vector int_vector #define uint_pointer int_pointer #define uint_quat int_quat static expr_type_t uint_int[] = { {'+', &type_int, &type_int, &type_int }, {'-', &type_int, &type_int, &type_int }, {'*', &type_int, &type_int, &type_int }, {'/', &type_int, &type_int, &type_int }, {'&', &type_int, &type_int, &type_int }, {'|', &type_int, &type_int, &type_int }, {'^', &type_int, &type_int, &type_int }, {'%', &type_int, &type_int, &type_int }, {MOD, &type_int, &type_int, &type_int }, {SHL, &type_uint, &type_int, &type_int }, {SHR, &type_uint, 0, &type_int }, {EQ, &type_int, &type_int, &type_int }, {NE, &type_int, &type_int, &type_int }, {LE, &type_int, &type_int, &type_int }, {GE, &type_int, &type_int, &type_int }, {LT, &type_int, &type_int, &type_int }, {GT, &type_int, &type_int, &type_int }, {0, 0} }; static expr_type_t uint_uint[] = { {'+', &type_uint}, {'-', &type_uint}, {'*', &type_uint}, {'/', &type_uint}, {'&', &type_uint}, {'|', &type_uint}, {'^', &type_uint}, {'%', &type_uint}, {MOD, &type_uint}, {SHL, &type_uint}, {SHR, &type_uint}, {EQ, &type_int, &type_int, &type_int}, {NE, &type_int, &type_int, &type_int}, {LE, &type_int}, {GE, &type_int}, {LT, &type_int}, {GT, &type_int}, {0, 0} }; #define uint_short uint_int #define uint_double int_double #define short_float int_float #define short_vector int_vector #define short_pointer int_pointer #define short_quat int_quat static expr_type_t short_int[] = { {'+', &type_int, &type_int, 0}, {'-', &type_int, &type_int, 0}, {'*', &type_int, &type_int, 0}, {'/', &type_int, &type_int, 0}, {'&', &type_int, &type_int, 0}, {'|', &type_int, &type_int, 0}, {'^', &type_int, &type_int, 0}, {'%', &type_int, &type_int, 0}, {MOD, &type_int, &type_int, 0}, {SHL, &type_short}, {SHR, &type_short}, {EQ, &type_int, &type_int, 0}, {NE, &type_int, &type_int, 0}, {LE, &type_int, &type_int, 0}, {GE, &type_int, &type_int, 0}, {LT, &type_int, &type_int, 0}, {GT, &type_int, &type_int, 0}, {0, 0} }; static expr_type_t short_uint[] = { {'+', &type_uint, &type_uint, 0}, {'-', &type_uint, &type_uint, 0}, {'*', &type_uint, &type_uint, 0}, {'/', &type_uint, &type_uint, 0}, {'&', &type_uint, &type_uint, 0}, {'|', &type_uint, &type_uint, 0}, {'^', &type_uint, &type_uint, 0}, {'%', &type_uint, &type_uint, 0}, {MOD, &type_uint, &type_uint, 0}, {SHL, &type_short}, {SHR, &type_short}, {EQ, &type_int, &type_uint, 0}, {NE, &type_int, &type_uint, 0}, {LE, &type_int, &type_uint, 0}, {GE, &type_int, &type_uint, 0}, {LT, &type_int, &type_uint, 0}, {GT, &type_int, &type_uint, 0}, {0, 0} }; static expr_type_t short_short[] = { {'+', &type_short}, {'-', &type_short}, {'*', &type_short}, {'/', &type_short}, {'&', &type_short}, {'|', &type_short}, {'^', &type_short}, {'%', &type_short}, {MOD, &type_short}, {SHL, &type_short}, {SHR, &type_short}, {EQ, &type_int}, {NE, &type_int}, {LE, &type_int}, {GE, &type_int}, {LT, &type_int}, {GT, &type_int}, {0, 0} }; #define short_double int_double static expr_type_t double_float[] = { {'+', &type_double, 0, &type_double}, {'-', &type_double, 0, &type_double}, {'*', &type_double, 0, &type_double}, {'/', &type_double, 0, &type_double}, {'%', &type_double, 0, &type_double}, {MOD, &type_double, 0, &type_double}, {EQ, 0, 0, 0, double_compare}, {NE, 0, 0, 0, double_compare}, {LE, 0, 0, 0, double_compare}, {GE, 0, 0, 0, double_compare}, {LT, 0, 0, 0, double_compare}, {GT, 0, 0, 0, double_compare}, {0, 0} }; static expr_type_t double_vector[] = { {'*', &type_vector, &type_float, 0, vector_scale}, {0, 0} }; static expr_type_t double_quat[] = { {'*', &type_quaternion}, {0, 0} }; static expr_type_t double_int[] = { {'+', &type_double, 0, &type_double}, {'-', &type_double, 0, &type_double}, {'*', &type_double, 0, &type_double}, {'/', &type_double, 0, &type_double}, {'%', &type_double, 0, &type_double}, {MOD, &type_double, 0, &type_double}, {EQ, 0, 0, 0, double_compare}, {NE, 0, 0, 0, double_compare}, {LE, 0, 0, 0, double_compare}, {GE, 0, 0, 0, double_compare}, {LT, 0, 0, 0, double_compare}, {GT, 0, 0, 0, double_compare}, {0, 0} }; #define double_uint double_int #define double_short double_int static expr_type_t double_double[] = { {'+', &type_double}, {'-', &type_double}, {'*', &type_double}, {'/', &type_double}, {'%', &type_double}, {MOD, &type_double}, {EQ, &type_int}, {NE, &type_int}, {LE, &type_int}, {GE, &type_int}, {LT, &type_int}, {GT, &type_int}, {0, 0} }; static expr_type_t long_long[] = { {'+', &type_long}, {'-', &type_long}, {'*', &type_long}, {'/', &type_long}, {'&', &type_long}, {'|', &type_long}, {'^', &type_long}, {'%', &type_long}, {MOD, &type_long}, {SHL, &type_long}, {SHR, &type_long}, {EQ, &type_int}, {NE, &type_int}, {LE, &type_int}, {GE, &type_int}, {LT, &type_int}, {GT, &type_int}, {0, 0} }; static expr_type_t ulong_ulong[] = { {'+', &type_long}, {'-', &type_long}, {'*', &type_long}, {'/', &type_long}, {'&', &type_long}, {'|', &type_long}, {'^', &type_long}, {'%', &type_long}, {MOD, &type_long}, {SHL, &type_long}, {SHR, &type_long}, {EQ, &type_int}, {NE, &type_int}, {LE, &type_int}, {GE, &type_int}, {LT, &type_int}, {GT, &type_int}, {0, 0} }; static expr_type_t *string_x[ev_type_count] = { [ev_string] = string_string, }; static expr_type_t *float_x[ev_type_count] = { [ev_float] = float_float, [ev_vector] = float_vector, [ev_quaternion] = float_quat, [ev_int] = float_int, [ev_uint] = float_uint, [ev_short] = float_short, [ev_double] = float_double, }; static expr_type_t *vector_x[ev_type_count] = { [ev_float] = vector_float, [ev_vector] = vector_vector, [ev_int] = vector_int, [ev_uint] = vector_uint, [ev_short] = vector_short, [ev_double] = vector_double, }; static expr_type_t *entity_x[ev_type_count] = { [ev_entity] = entity_entity, }; static expr_type_t *field_x[ev_type_count] = { [ev_field] = field_field, }; static expr_type_t *func_x[ev_type_count] = { [ev_func] = func_func, }; static expr_type_t *pointer_x[ev_type_count] = { [ev_ptr] = pointer_pointer, [ev_int] = pointer_int, [ev_uint] = pointer_uint, [ev_short] = pointer_short, }; static expr_type_t *quat_x[ev_type_count] = { [ev_float] = quat_float, [ev_vector] = quat_vector, [ev_quaternion] = quat_quat, [ev_int] = quat_int, [ev_uint] = quat_uint, [ev_short] = quat_short, [ev_double] = quat_double, }; static expr_type_t *int_x[ev_type_count] = { [ev_float] = int_float, [ev_vector] = int_vector, [ev_ptr] = int_pointer, [ev_quaternion] = int_quat, [ev_int] = int_int, [ev_uint] = int_uint, [ev_short] = int_short, [ev_double] = int_double, }; static expr_type_t *uint_x[ev_type_count] = { [ev_float] = uint_float, [ev_vector] = uint_vector, [ev_ptr] = uint_pointer, [ev_quaternion] = uint_quat, [ev_int] = uint_int, [ev_uint] = uint_uint, [ev_short] = uint_short, [ev_double] = uint_double, }; static expr_type_t *short_x[ev_type_count] = { [ev_float] = short_float, [ev_vector] = short_vector, [ev_ptr] = short_pointer, [ev_quaternion] = short_quat, [ev_int] = short_int, [ev_uint] = short_uint, [ev_short] = short_short, [ev_double] = short_double, }; static expr_type_t *double_x[ev_type_count] = { [ev_float] = double_float, [ev_vector] = double_vector, [ev_quaternion] = double_quat, [ev_int] = double_int, [ev_uint] = double_uint, [ev_short] = double_short, [ev_double] = double_double, }; static expr_type_t *long_x[ev_type_count] = { [ev_long] = long_long, }; static expr_type_t *ulong_x[ev_type_count] = { [ev_ulong] = ulong_ulong, }; static expr_type_t **binary_expr_types[ev_type_count] = { [ev_string] = string_x, [ev_float] = float_x, [ev_vector] = vector_x, [ev_entity] = entity_x, [ev_field] = field_x, [ev_func] = func_x, [ev_ptr] = pointer_x, [ev_quaternion] = quat_x, [ev_int] = int_x, [ev_uint] = uint_x, [ev_short] = short_x, [ev_double] = double_x, [ev_long] = long_x, [ev_ulong] = ulong_x, }; // supported operators for scalar-vector expressions static int scalar_vec_ops[] = { '*', '/', '%', MOD, 0 }; static expr_t * convert_scalar (expr_t *scalar, int op, expr_t *vec) { int *s_op = scalar_vec_ops; while (*s_op && *s_op != op) { s_op++; } if (!*s_op) { return 0; } // expand the scalar to a vector of the same width as vec type_t *vec_type = get_type (vec); if (is_constant (scalar)) { for (int i = 1; i < type_width (get_type (vec)); i++) { expr_t *s = copy_expr (scalar); s->next = scalar; scalar = s; } return new_vector_list (scalar); } return new_extend_expr (scalar, vec_type, 2);//2 = copy } static expr_t * pointer_arithmetic (int op, expr_t *e1, expr_t *e2) { type_t *t1 = get_type (e1); type_t *t2 = get_type (e2); expr_t *ptr = 0; expr_t *offset = 0; expr_t *psize; type_t *ptype = 0; if (!is_ptr (t1) && !is_ptr (t2)) { internal_error (e1, "pointer arithmetic on non-pointers"); } if (is_ptr (t1) && is_ptr (t2)) { if (op != '-') { return error (e2, "invalid pointer operation"); } if (t1 != t2) { return error (e2, "cannot use %c on pointers of different types", op); } e1 = cast_expr (&type_int, e1); e2 = cast_expr (&type_int, e2); psize = new_int_expr (type_size (t1->t.fldptr.type)); return binary_expr ('/', binary_expr ('-', e1, e2), psize); } else if (is_ptr (t1)) { offset = cast_expr (&type_int, e2); ptr = e1; ptype = t1; } else if (is_ptr (t2)) { offset = cast_expr (&type_int, e1); ptr = e2; ptype = t2; } // op is known to be + or - psize = new_int_expr (type_size (ptype->t.fldptr.type)); offset = unary_expr (op, binary_expr ('*', offset, psize)); return offset_pointer_expr (ptr, offset); } static expr_t * pointer_compare (int op, expr_t *e1, expr_t *e2) { type_t *t1 = get_type (e1); type_t *t2 = get_type (e2); expr_t *e; if (!type_assignable (t1, t2)) { return error (e2, "cannot use %s on pointers of different types", get_op_string (op)); } if (options.code.progsversion < PROG_VERSION) { e = new_binary_expr (op, e1, e2); } else { e = new_binary_expr (op, cast_expr (&type_int, e1), cast_expr (&type_int, e2)); } e->e.expr.type = &type_int; return e; } static expr_t * func_compare (int op, expr_t *e1, expr_t *e2) { expr_t *e; if (options.code.progsversion < PROG_VERSION) { e = new_binary_expr (op, e1, e2); } else { e = new_binary_expr (op, new_alias_expr (&type_int, e1), new_alias_expr (&type_int, e2)); } e->e.expr.type = &type_int; if (options.code.progsversion == PROG_ID_VERSION) { e->e.expr.type = &type_float; } return e; } static expr_t * inverse_multiply (int op, expr_t *e1, expr_t *e2) { // There is no vector/float or quaternion/float instruction and adding // one would mean the engine would have to do 1/f every time expr_t *one = new_float_expr (1); return binary_expr ('*', e1, binary_expr ('/', one, e2)); } static expr_t * vector_compare (int op, expr_t *e1, expr_t *e2) { if (options.code.progsversion < PROG_VERSION) { expr_t *e = new_binary_expr (op, e1, e2); e->e.expr.type = &type_int; if (options.code.progsversion == PROG_ID_VERSION) { e->e.expr.type = &type_float; } return e; } int hop = op == EQ ? '&' : '|'; e1 = new_alias_expr (&type_vec3, e1); e2 = new_alias_expr (&type_vec3, e2); expr_t *e = new_binary_expr (op, e1, e2); e->e.expr.type = &type_ivec3; return new_horizontal_expr (hop, e, &type_int); } static expr_t *vector_multiply (int op, expr_t *e1, expr_t *e2) { expr_t *e = new_binary_expr ('*', e1, e2); if (options.math.vector_mult == DOT) { // vector * vector is dot product in v6 progs (ick) e->e.expr.op = DOT; if (options.code.progsversion == PROG_VERSION) { e->e.expr.type = &type_vector; e = new_alias_expr (&type_float, e); } else { e->e.expr.type = &type_float; } } else { // component-wise multiplication e->e.expr.type = &type_vector; } return e; } static expr_t *vector_scale (int op, expr_t *e1, expr_t *e2) { // Ensure the expression is always vector * scalar. The operation is // always commutative, and the Ruamoko ISA supports only vector * scalar // (though v6 does support scalar * vector, one less if). if (is_scalar (get_type (e1))) { expr_t *t = e1; e1 = e2; e2 = t; } expr_t *e = new_binary_expr (SCALE, e1, e2); e->e.expr.type = get_type (e1); return e; } static expr_t * double_compare (int op, expr_t *e1, expr_t *e2) { type_t *t1 = get_type (e1); type_t *t2 = get_type (e2); expr_t *e; if (is_constant (e1) && e1->implicit && is_double (t1) && is_float (t2)) { t1 = &type_float; e1 = cast_expr (t1, e1); } if (is_float (t1) && is_constant (e2) && e2->implicit && is_double (t2)) { t2 = &type_float; e2 = cast_expr (t2, e2); } if (is_double (t1)) { if (is_float (t2)) { warning (e2, "comparison between double and float"); } else if (!is_constant (e2)) { warning (e2, "comparison between double and int"); } e2 = cast_expr (&type_double, e2); } else if (is_double (t2)) { if (is_float (t1)) { warning (e1, "comparison between float and double"); } else if (!is_constant (e1)) { warning (e1, "comparison between int and double"); } e1 = cast_expr (&type_double, e1); } e = new_binary_expr (op, e1, e2); e->e.expr.type = &type_int; return e; } static expr_t * entity_compare (int op, expr_t *e1, expr_t *e2) { if (options.code.progsversion == PROG_VERSION) { e1 = new_alias_expr (&type_int, e1); e2 = new_alias_expr (&type_int, e2); } expr_t *e = new_binary_expr (op, e1, e2); e->e.expr.type = &type_int; if (options.code.progsversion == PROG_ID_VERSION) { e->e.expr.type = &type_float; } return e; } #define invalid_binary_expr(_op, _e1, _e2) \ _invalid_binary_expr(_op, _e1, _e2, __FILE__, __LINE__) static expr_t * _invalid_binary_expr (int op, expr_t *e1, expr_t *e2, const char *file, int line) { type_t *t1, *t2; t1 = get_type (e1); t2 = get_type (e2); return _error (e1, file, line, "invalid binary expression: %s %s %s", get_type_string (t1), get_op_string (op), get_type_string (t2)); } static expr_t * reimplement_binary_expr (int op, expr_t *e1, expr_t *e2) { expr_t *e; if (options.code.progsversion == PROG_ID_VERSION) { switch (op) { case '%': { expr_t *tmp1, *tmp2; e = new_block_expr (); tmp1 = new_temp_def_expr (&type_float); tmp2 = new_temp_def_expr (&type_float); append_expr (e, assign_expr (tmp1, binary_expr ('/', e1, e2))); append_expr (e, assign_expr (tmp2, binary_expr ('&', tmp1, tmp1))); e->e.block.result = binary_expr ('-', e1, binary_expr ('*', e2, tmp2)); return e; } break; } } return 0; } static expr_t * check_precedence (int op, expr_t *e1, expr_t *e2) { if (e1->type == ex_uexpr && e1->e.expr.op == '!' && !e1->paren) { if (options.traditional) { if (op != AND && op != OR && op != '=') { notice (e1, "precedence of `!' and `%s' inverted for " "traditional code", get_op_string (op)); e1->e.expr.e1->paren = 1; return unary_expr ('!', binary_expr (op, e1->e.expr.e1, e2)); } } else if (op == '&' || op == '|') { if (options.warnings.precedence) warning (e1, "ambiguous logic. Suggest explicit parentheses " "with expressions involving ! and %s", get_op_string (op)); } } if (options.traditional) { if (e2->type == ex_expr && !e2->paren) { if (((op == '&' || op == '|') && (is_math_op (e2->e.expr.op) || is_compare (e2->e.expr.op))) || (op == '=' &&(e2->e.expr.op == OR || e2->e.expr.op == AND))) { notice (e1, "precedence of `%s' and `%s' inverted for " "traditional code", get_op_string (op), get_op_string (e2->e.expr.op)); e1 = binary_expr (op, e1, e2->e.expr.e1); e1->paren = 1; return binary_expr (e2->e.expr.op, e1, e2->e.expr.e2); } if (((op == EQ || op == NE) && is_compare (e2->e.expr.op)) || (op == OR && e2->e.expr.op == AND) || (op == '|' && e2->e.expr.op == '&')) { notice (e1, "precedence of `%s' raised to `%s' for " "traditional code", get_op_string (op), get_op_string (e2->e.expr.op)); e1 = binary_expr (op, e1, e2->e.expr.e1); e1->paren = 1; return binary_expr (e2->e.expr.op, e1, e2->e.expr.e2); } } else if (e1->type == ex_expr && !e1->paren) { if (((op == '&' || op == '|') && (is_math_op (e1->e.expr.op) || is_compare (e1->e.expr.op))) || (op == '=' &&(e2->e.expr.op == OR || e2->e.expr.op == AND))) { notice (e1, "precedence of `%s' and `%s' inverted for " "traditional code", get_op_string (op), get_op_string (e1->e.expr.op)); e2 = binary_expr (op, e1->e.expr.e2, e2); e2->paren = 1; return binary_expr (e1->e.expr.op, e1->e.expr.e1, e2); } } } else { if (e2->type == ex_expr && !e2->paren) { if ((op == '&' || op == '|' || op == '^') && (is_math_op (e2->e.expr.op) || is_compare (e2->e.expr.op))) { if (options.warnings.precedence) warning (e2, "suggest parentheses around %s in " "operand of %c", is_compare (e2->e.expr.op) ? "comparison" : get_op_string (e2->e.expr.op), op); } } if (e1->type == ex_expr && !e1->paren) { if ((op == '&' || op == '|' || op == '^') && (is_math_op (e1->e.expr.op) || is_compare (e1->e.expr.op))) { if (options.warnings.precedence) warning (e1, "suggest parentheses around %s in " "operand of %c", is_compare (e1->e.expr.op) ? "comparison" : get_op_string (e1->e.expr.op), op); } } } return 0; } static int is_call (expr_t *e) { return e->type == ex_block && e->e.block.is_call; } static type_t * promote_type (type_t *dst, type_t *src) { if (is_vector (dst) || is_quaternion (dst)) { return dst; } return vector_type (base_type (dst), type_width (src)); } expr_t * binary_expr (int op, expr_t *e1, expr_t *e2) { type_t *t1, *t2; etype_t et1, et2; expr_t *e; expr_type_t *expr_type; convert_name (e1); // FIXME this is target-specific info and should not be in the // expression tree if (e1->type == ex_alias && is_call (e1->e.alias.expr)) { // move the alias expression inside the block so the following check // can detect the call and move the temp assignment into the block expr_t *block = e1->e.alias.expr; e1->e.alias.expr = block->e.block.result; block->e.block.result = e1; e1 = block; } if (e1->type == ex_block && e1->e.block.is_call && has_function_call (e2) && e1->e.block.result) { // the temp assignment needs to be insided the block so assignment // code generation doesn't see it when applying right-associativity expr_t *tmp = new_temp_def_expr (get_type (e1->e.block.result)); e = assign_expr (tmp, e1->e.block.result); append_expr (e1, e); e1->e.block.result = tmp; } if (e1->type == ex_error) return e1; convert_name (e2); if (e2->type == ex_error) return e2; if (e1->type == ex_bool) e1 = convert_from_bool (e1, get_type (e2)); if (e2->type == ex_bool) e2 = convert_from_bool (e2, get_type (e1)); if ((e = check_precedence (op, e1, e2))) return e; t1 = get_type (e1); t2 = get_type (e2); if (!t1 || !t2) internal_error (e1, "expr with no type"); if (op == EQ || op == NE) { if (e1->type == ex_nil) { t1 = t2; convert_nil (e1, t1); } else if (e2->type == ex_nil) { t2 = t1; convert_nil (e2, t2); } } if (is_constant (e1) && is_double (t1) && e1->implicit && is_float (t2)) { t1 = &type_float; e1 = cast_expr (t1, e1); } if (is_constant (e2) && is_double (t2) && e2->implicit && is_float (t1)) { t2 = &type_float; e2 = cast_expr (t2, e2); } et1 = low_level_type (t1); et2 = low_level_type (t2); if (et1 >= ev_type_count || !binary_expr_types[et1]) return invalid_binary_expr(op, e1, e2); if (et2 >= ev_type_count || !binary_expr_types[et1][et2]) return invalid_binary_expr(op, e1, e2); if ((t1->width > 1 || t2->width > 1)) { // vector/quaternion and scalar won't get here as vector and quaternion // are distict types with type.width == 1, but vector and vec3 WILL get // here because of vec3 being float{3} if (t1 != t2) { type_t *pt1 = t1; type_t *pt2 = t2; if (is_float (base_type (t1)) && is_double (base_type (t2)) && e2->implicit) { pt2 = promote_type (t1, t2); } else if (is_double (base_type (t1)) && is_float (base_type (t2)) && e1->implicit) { pt1 = promote_type (t2, t1); } else if (type_promotes (base_type (t1), base_type (t2))) { pt2 = promote_type (t1, t2); } else if (type_promotes (base_type (t2), base_type (t1))) { pt1 = promote_type (t2, t1); } else if (base_type (t1) == base_type (t2)) { if (is_vector (t1) || is_quaternion (t1)) { pt2 = t1; } else if (is_vector (t2) || is_quaternion (t2)) { pt1 = t2; } } else { debug (e1, "%d %d\n", e1->implicit, e2->implicit); return invalid_binary_expr (op, e1, e2); } if (pt1 != t1) { e1 = cast_expr (pt1, e1); t1 = pt1; } if (pt2 != t2) { e2 = cast_expr (pt2, e2); t2 = pt2; } } int scalar_op = 0; if (type_width (t1) == 1) { // scalar op vec if (!(e = convert_scalar (e1, op, e2))) { return invalid_binary_expr (op, e1, e2); } scalar_op = 1; e1 = e; t1 = get_type (e1); } if (type_width (t2) == 1) { // vec op scalar if (!(e = convert_scalar (e2, op, e1))) { return invalid_binary_expr (op, e1, e2); } scalar_op = 1; e2 = e; t2 = get_type (e2); } if (scalar_op && op == '*') { op = HADAMARD; } if (type_width (t1) != type_width (t2)) { // vec op vec of different widths return invalid_binary_expr (op, e1, e2); } t1 = get_type (e1); t2 = get_type (e2); et1 = low_level_type (t1); et2 = low_level_type (t2); // both widths are the same at this point if (t1->width > 1) { e = new_binary_expr (op, e1, e2); e->e.expr.type = t1; return e; } } expr_type = binary_expr_types[et1][et2]; while (expr_type->op && expr_type->op != op) expr_type++; if (!expr_type->op) return invalid_binary_expr(op, e1, e2); if (expr_type->a_cast) e1 = cast_expr (expr_type->a_cast, e1); if (expr_type->b_cast) e2 = cast_expr (expr_type->b_cast, e2); if (expr_type->process) { return fold_constants (expr_type->process (op, e1, e2)); } if ((e = reimplement_binary_expr (op, e1, e2))) return fold_constants (e); e = new_binary_expr (op, e1, e2); e->e.expr.type = expr_type->result_type; if (is_compare (op) || is_logic (op)) { if (options.code.progsversion == PROG_ID_VERSION) { e->e.expr.type = &type_float; } } return fold_constants (e); }