2013-09-27 08:48:06 +00:00
|
|
|
/*
|
2024-12-04 15:13:56 +00:00
|
|
|
expr_binary.c
|
2013-09-27 08:48:06 +00:00
|
|
|
|
2024-12-04 15:13:56 +00:00
|
|
|
Binary expression manipulation
|
2013-09-27 08:48:06 +00:00
|
|
|
|
|
|
|
Copyright (C) 2013 Bill Currie <bill@taniwha.org>
|
|
|
|
|
|
|
|
Author: Bill Currie <bill@taniwha.org>
|
|
|
|
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
|
|
|
|
|
2023-08-21 08:37:56 +00:00
|
|
|
#include "tools/qfcc/include/algebra.h"
|
2020-06-21 14:15:17 +00:00
|
|
|
#include "tools/qfcc/include/diagnostic.h"
|
|
|
|
#include "tools/qfcc/include/expr.h"
|
|
|
|
#include "tools/qfcc/include/options.h"
|
2024-04-18 03:41:24 +00:00
|
|
|
#include "tools/qfcc/include/rua-lang.h"
|
2025-01-18 05:05:09 +00:00
|
|
|
#include "tools/qfcc/include/target.h"
|
2020-06-21 14:15:17 +00:00
|
|
|
#include "tools/qfcc/include/type.h"
|
|
|
|
|
2013-09-27 08:48:06 +00:00
|
|
|
typedef struct {
|
2024-12-04 15:13:56 +00:00
|
|
|
bool (*match_a) (const type_t *type);
|
|
|
|
bool (*match_b) (const type_t *type);
|
|
|
|
bool (*match_shape) (const type_t *a, const type_t *b);
|
|
|
|
const type_t *(*res_type) (const type_t *a, const type_t *b);
|
|
|
|
const expr_t *(*process) (int op, const expr_t *e1, const expr_t *e2);
|
|
|
|
bool promote;
|
|
|
|
bool no_implicit;
|
2023-09-25 08:26:37 +00:00
|
|
|
bool (*commutative) (void);
|
|
|
|
bool (*anticommute) (void);
|
2023-09-30 02:06:06 +00:00
|
|
|
bool (*associative) (void);
|
2024-02-01 02:00:27 +00:00
|
|
|
int true_op;
|
2013-09-27 08:48:06 +00:00
|
|
|
} expr_type_t;
|
|
|
|
|
2023-09-27 03:41:31 +00:00
|
|
|
static const expr_t *
|
|
|
|
pointer_arithmetic (int op, const expr_t *e1, const expr_t *e2)
|
2019-06-09 15:36:13 +00:00
|
|
|
{
|
2024-02-13 14:09:28 +00:00
|
|
|
auto t1 = get_type (e1);
|
|
|
|
auto t2 = get_type (e2);
|
2023-09-27 03:41:31 +00:00
|
|
|
const expr_t *ptr = 0;
|
|
|
|
const expr_t *offset = 0;
|
|
|
|
const expr_t *psize;
|
2024-02-13 14:09:28 +00:00
|
|
|
const type_t *ptype = 0;
|
2019-06-09 15:36:13 +00:00
|
|
|
|
2024-10-07 10:40:19 +00:00
|
|
|
if (!is_pointer (t1) && !is_pointer (t2)) {
|
2019-06-09 15:36:13 +00:00
|
|
|
internal_error (e1, "pointer arithmetic on non-pointers");
|
|
|
|
}
|
2024-10-07 10:40:19 +00:00
|
|
|
if (is_pointer (t1) && is_pointer (t2)) {
|
2020-03-17 03:45:55 +00:00
|
|
|
if (op != '-') {
|
|
|
|
return error (e2, "invalid pointer operation");
|
|
|
|
}
|
|
|
|
if (t1 != t2) {
|
|
|
|
return error (e2, "cannot use %c on pointers of different types",
|
|
|
|
op);
|
|
|
|
}
|
2022-01-18 04:21:06 +00:00
|
|
|
e1 = cast_expr (&type_int, e1);
|
|
|
|
e2 = cast_expr (&type_int, e2);
|
2024-08-16 08:23:29 +00:00
|
|
|
psize = new_int_expr (type_size (t1->fldptr.type), false);
|
2020-03-17 03:45:55 +00:00
|
|
|
return binary_expr ('/', binary_expr ('-', e1, e2), psize);
|
2024-10-07 10:40:19 +00:00
|
|
|
} else if (is_pointer (t1)) {
|
2022-01-18 04:21:06 +00:00
|
|
|
offset = cast_expr (&type_int, e2);
|
2022-01-25 14:39:17 +00:00
|
|
|
ptr = e1;
|
2020-03-17 03:45:55 +00:00
|
|
|
ptype = t1;
|
2024-10-07 10:40:19 +00:00
|
|
|
} else if (is_pointer (t2)) {
|
2022-01-18 04:21:06 +00:00
|
|
|
offset = cast_expr (&type_int, e1);
|
2022-01-25 14:39:17 +00:00
|
|
|
ptr = e2;
|
2020-03-17 03:45:55 +00:00
|
|
|
ptype = t2;
|
|
|
|
}
|
2022-01-25 14:39:17 +00:00
|
|
|
// op is known to be + or -
|
2024-08-16 08:23:29 +00:00
|
|
|
psize = new_int_expr (type_size (ptype->fldptr.type), false);
|
2022-01-25 14:39:17 +00:00
|
|
|
offset = unary_expr (op, binary_expr ('*', offset, psize));
|
|
|
|
return offset_pointer_expr (ptr, offset);
|
2019-06-09 15:36:13 +00:00
|
|
|
}
|
|
|
|
|
2023-09-27 03:41:31 +00:00
|
|
|
static const expr_t *
|
|
|
|
pointer_compare (int op, const expr_t *e1, const expr_t *e2)
|
2020-03-17 03:45:55 +00:00
|
|
|
{
|
2024-02-13 14:09:28 +00:00
|
|
|
auto t1 = get_type (e1);
|
|
|
|
auto t2 = get_type (e2);
|
2020-03-17 03:45:55 +00:00
|
|
|
expr_t *e;
|
|
|
|
|
|
|
|
if (!type_assignable (t1, t2)) {
|
|
|
|
return error (e2, "cannot use %s on pointers of different types",
|
|
|
|
get_op_string (op));
|
|
|
|
}
|
2022-01-29 09:23:07 +00:00
|
|
|
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));
|
|
|
|
}
|
2024-11-15 03:36:08 +00:00
|
|
|
e->expr.type = &type_bool;
|
2020-03-17 03:45:55 +00:00
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
2023-09-27 03:41:31 +00:00
|
|
|
static const expr_t *
|
|
|
|
func_compare (int op, const expr_t *e1, const expr_t *e2)
|
2022-01-29 09:59:38 +00:00
|
|
|
{
|
|
|
|
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));
|
|
|
|
}
|
2024-11-15 03:36:08 +00:00
|
|
|
e->expr.type = &type_bool;
|
2023-02-11 11:11:46 +00:00
|
|
|
if (options.code.progsversion == PROG_ID_VERSION) {
|
2023-09-23 09:01:49 +00:00
|
|
|
e->expr.type = &type_float;
|
2023-02-11 11:11:46 +00:00
|
|
|
}
|
2022-01-29 09:59:38 +00:00
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
2023-09-27 03:41:31 +00:00
|
|
|
static const expr_t *
|
2024-12-04 15:13:56 +00:00
|
|
|
entity_compare (int op, const expr_t *e1, const expr_t *e2)
|
2019-06-09 23:44:36 +00:00
|
|
|
{
|
2024-12-04 15:13:56 +00:00
|
|
|
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->expr.type = &type_bool;
|
|
|
|
if (options.code.progsversion == PROG_ID_VERSION) {
|
|
|
|
e->expr.type = &type_float;
|
|
|
|
}
|
|
|
|
return e;
|
2019-06-09 23:44:36 +00:00
|
|
|
}
|
|
|
|
|
2024-12-04 15:13:56 +00:00
|
|
|
static const type_t *
|
|
|
|
promote_type (const type_t *dst, const type_t *src)
|
2022-01-30 01:56:15 +00:00
|
|
|
{
|
2024-12-04 15:13:56 +00:00
|
|
|
if ((is_vector (dst) || is_quaternion (dst))
|
|
|
|
&& type_width (dst) == type_width (src)) {
|
|
|
|
return dst;
|
2022-01-30 01:56:15 +00:00
|
|
|
}
|
2024-12-04 15:13:56 +00:00
|
|
|
return vector_type (base_type (dst), type_width (src));
|
2022-01-30 01:56:15 +00:00
|
|
|
}
|
|
|
|
|
2024-12-04 15:13:56 +00:00
|
|
|
static void
|
|
|
|
promote_exprs (const expr_t **e1, const expr_t **e2)
|
2024-02-17 14:55:44 +00:00
|
|
|
{
|
2024-12-04 15:13:56 +00:00
|
|
|
auto t1 = get_type (*e1);
|
|
|
|
auto t2 = get_type (*e2);
|
|
|
|
|
|
|
|
if (is_enum (t1) && is_enum (t2)) {
|
|
|
|
//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)) {
|
|
|
|
t2 = promote_type (t1, t2);
|
|
|
|
} else if ((is_vector (t2) || is_quaternion (t2)) && is_float (t1)) {
|
|
|
|
t1 = promote_type (t2, t1);
|
|
|
|
} else if (type_promotes (t1, t2)) {
|
|
|
|
t2 = promote_type (t1, t2);
|
|
|
|
} else if (type_promotes (t2, t1)) {
|
|
|
|
t1 = promote_type (t2, t1);
|
|
|
|
} else if (base_type (t1) != base_type (t2)) {
|
|
|
|
internal_error (*e1, "failed to promote types %s %s",
|
|
|
|
get_type_string (t1), get_type_string (t2));
|
|
|
|
}
|
|
|
|
*e1 = cast_expr (t1, *e1);
|
|
|
|
*e2 = cast_expr (t2, *e2);
|
2024-02-17 14:55:44 +00:00
|
|
|
}
|
|
|
|
|
2023-09-27 03:41:31 +00:00
|
|
|
static const expr_t *
|
2024-12-04 15:13:56 +00:00
|
|
|
math_compare (int op, const expr_t *e1, const expr_t *e2)
|
2022-01-30 05:14:15 +00:00
|
|
|
{
|
2024-12-04 15:13:56 +00:00
|
|
|
auto t1 = get_type (e1);
|
|
|
|
auto t2 = get_type (e2);
|
|
|
|
if (is_matrix (t1) || is_matrix (t2)
|
|
|
|
|| type_width (t1) != type_width (t2)) {
|
|
|
|
//FIXME glsl does not support comparison of vectors using operators
|
|
|
|
// (it uses functions)
|
|
|
|
return error (e1, "cannot compare %s and %s",
|
|
|
|
get_type_string (t1), get_type_string (t2));
|
|
|
|
}
|
|
|
|
if (t1 != t2) {
|
|
|
|
if (e1->implicit && type_demotes (t2, t1)) {
|
|
|
|
t1 = promote_type (t2, t1);
|
|
|
|
} else if (e2->implicit && type_demotes (t1, t2)) {
|
|
|
|
t2 = promote_type (t1, t2);
|
|
|
|
}
|
|
|
|
e1 = cast_expr (t1, e1);
|
|
|
|
e2 = cast_expr (t2, e2);
|
|
|
|
}
|
|
|
|
if (!type_compares (t1, t2)) {
|
|
|
|
warning (e2, "comparison between %s and %s",
|
|
|
|
get_type_string (t1),
|
|
|
|
get_type_string (t2));
|
|
|
|
}
|
|
|
|
if (t1 != t2) {
|
|
|
|
promote_exprs (&e1, &e2);
|
|
|
|
t1 = get_type (e1);
|
|
|
|
t2 = get_type (e2);
|
|
|
|
}
|
|
|
|
if (is_vector (t1) || is_quaternion (t1)) {
|
2025-01-18 05:05:09 +00:00
|
|
|
return current_target.vector_compare (op, e1, e2);
|
2024-12-04 15:13:56 +00:00
|
|
|
}
|
2025-01-18 05:05:09 +00:00
|
|
|
|
|
|
|
auto e = new_binary_expr (op, e1, e2);
|
|
|
|
e->expr.type = bool_type (t1);
|
2023-08-17 07:19:37 +00:00
|
|
|
return e;
|
|
|
|
}
|
2022-01-30 05:14:15 +00:00
|
|
|
|
2023-09-27 03:41:31 +00:00
|
|
|
static const expr_t *
|
2024-12-04 15:13:56 +00:00
|
|
|
matrix_binary_expr (int op, const expr_t *a, const expr_t *b)
|
2023-08-17 07:19:37 +00:00
|
|
|
{
|
2024-12-04 15:13:56 +00:00
|
|
|
auto ta = get_type (a);
|
|
|
|
auto tb = get_type (b);
|
|
|
|
|
|
|
|
int rowsa = type_rows (ta);
|
|
|
|
int colsb = type_cols (tb);
|
|
|
|
|
|
|
|
int rowsc, colsc;
|
|
|
|
|
|
|
|
if (is_nonscalar (ta)) {
|
|
|
|
// vectors * matrix treats vector as row matrix, resulting in vector
|
|
|
|
rowsc = colsb;
|
|
|
|
colsc = 1;
|
|
|
|
} else {
|
|
|
|
rowsc = rowsa;
|
|
|
|
colsc = colsb;
|
2023-08-17 07:19:37 +00:00
|
|
|
}
|
2024-12-04 15:13:56 +00:00
|
|
|
|
|
|
|
auto type = matrix_type (base_type (ta), colsc, rowsc);
|
|
|
|
auto e = typed_binary_expr (type, op, a, b);
|
2022-01-30 05:14:15 +00:00
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
2023-09-27 03:41:31 +00:00
|
|
|
static const expr_t *
|
2025-01-13 03:52:12 +00:00
|
|
|
matrix_scalar_mul (int op, const expr_t *a, const expr_t *b)
|
2022-01-30 05:48:49 +00:00
|
|
|
{
|
2024-12-04 15:13:56 +00:00
|
|
|
auto ta = get_type (a);
|
|
|
|
auto tb = get_type (b);
|
|
|
|
|
|
|
|
if (is_scalar (ta)) {
|
|
|
|
// ensure the expression is always matrix * scalar
|
|
|
|
auto te = a;
|
|
|
|
a = b;
|
|
|
|
b = te;
|
|
|
|
ta = tb;
|
2022-01-30 05:48:49 +00:00
|
|
|
}
|
2024-12-04 15:13:56 +00:00
|
|
|
|
|
|
|
op = QC_SCALE;
|
|
|
|
auto e = typed_binary_expr (ta, op, a, b);
|
2022-01-30 05:48:49 +00:00
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
2023-09-27 03:41:31 +00:00
|
|
|
static const expr_t *
|
2024-12-04 15:13:56 +00:00
|
|
|
convert_scalar (const expr_t *scalar, const expr_t *vec)
|
2020-02-14 14:36:36 +00:00
|
|
|
{
|
2024-12-04 15:13:56 +00:00
|
|
|
// expand the scalar to a vector of the same width as vec
|
|
|
|
auto vec_type = get_type (vec);
|
2025-01-13 03:52:12 +00:00
|
|
|
// vec might actually be a matrix, so get its column "width"
|
|
|
|
vec_type = vector_type (base_type (vec_type), type_width (vec_type));
|
2020-02-14 14:36:36 +00:00
|
|
|
|
2024-12-04 15:13:56 +00:00
|
|
|
if (is_constant (scalar)) {
|
|
|
|
int width = type_width (get_type (vec));
|
|
|
|
const expr_t *elements[width];
|
|
|
|
for (int i = 0; i < width; i++) {
|
|
|
|
elements[i] = scalar;
|
2020-02-14 14:36:36 +00:00
|
|
|
}
|
2025-01-13 03:52:12 +00:00
|
|
|
auto scalar_list = new_list_expr (nullptr);
|
2024-12-04 15:13:56 +00:00
|
|
|
list_gather (&scalar_list->list, elements, width);
|
|
|
|
return new_vector_list (scalar_list);
|
2020-02-14 14:36:36 +00:00
|
|
|
}
|
2024-12-04 15:13:56 +00:00
|
|
|
|
|
|
|
return new_extend_expr (scalar, vec_type, 2, false);//2 = copy
|
2020-02-14 14:36:36 +00:00
|
|
|
}
|
|
|
|
|
2025-01-13 03:52:12 +00:00
|
|
|
static const expr_t *
|
|
|
|
matrix_scalar_expr (int op, const expr_t *a, const expr_t *b)
|
|
|
|
{
|
|
|
|
scoped_src_loc (a);
|
|
|
|
bool left = is_scalar (get_type (a));
|
|
|
|
auto mat_type = get_type (left ? b : a);
|
|
|
|
int count = type_cols (mat_type);
|
|
|
|
const expr_t *a_cols[count];
|
|
|
|
const expr_t *b_cols[count];
|
|
|
|
if (left) {
|
|
|
|
a_cols[0] = convert_scalar (a, b);
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
a_cols[i] = a_cols[0];
|
|
|
|
b_cols[i] = get_column (b, i);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
b_cols[0] = convert_scalar (b, a);
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
a_cols[i] = get_column (a, i);
|
|
|
|
b_cols[i] = b_cols[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
auto params = new_list_expr (nullptr);
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
expr_append_expr (params, binary_expr (op, a_cols[i], b_cols[i]));
|
|
|
|
}
|
|
|
|
return constructor_expr (new_type_expr (mat_type), params);
|
|
|
|
}
|
|
|
|
|
2023-12-20 13:49:51 +00:00
|
|
|
static const expr_t *
|
2024-12-04 15:13:56 +00:00
|
|
|
matrix_scalar_div (int op, const expr_t *a, const expr_t *b)
|
2023-12-20 13:49:51 +00:00
|
|
|
{
|
2024-12-04 15:13:56 +00:00
|
|
|
auto ta = get_type (a);
|
2023-12-20 13:49:51 +00:00
|
|
|
|
2024-12-04 15:13:56 +00:00
|
|
|
if (is_vector (ta) || is_quaternion (ta) || is_matrix (ta)) {
|
|
|
|
// There is no vector/float or quaternion/float instruction and adding
|
|
|
|
// one would mean the engine would have to do 1/f every time
|
|
|
|
// similar for matrix
|
|
|
|
auto one = new_float_expr (1, false);
|
|
|
|
return binary_expr ('*', a, binary_expr ('/', one, b));
|
2023-12-20 13:49:51 +00:00
|
|
|
}
|
2024-12-04 15:13:56 +00:00
|
|
|
b = convert_scalar (b, a);
|
|
|
|
auto e = typed_binary_expr (ta, op, a, b);
|
2023-12-20 13:49:51 +00:00
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
2023-09-27 03:41:31 +00:00
|
|
|
static const expr_t *
|
2025-01-13 03:52:12 +00:00
|
|
|
vector_scalar_expr (int op, const expr_t *a, const expr_t *b)
|
|
|
|
{
|
|
|
|
scoped_src_loc (a);
|
|
|
|
bool left = is_scalar (get_type (a));
|
|
|
|
if (left) {
|
|
|
|
a = convert_scalar (a, b);
|
|
|
|
} else {
|
|
|
|
b = convert_scalar (b, a);
|
|
|
|
}
|
|
|
|
return binary_expr (op, a, b);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const expr_t *
|
|
|
|
vector_vector_mul (int op, const expr_t *a, const expr_t *b)
|
2022-02-06 11:12:22 +00:00
|
|
|
{
|
2024-12-04 15:13:56 +00:00
|
|
|
expr_t *e = new_binary_expr ('*', a, b);
|
|
|
|
if (options.math.vector_mult == QC_DOT) {
|
|
|
|
// vector * vector is dot product in v6 progs (ick)
|
|
|
|
e->expr.op = QC_DOT;
|
2023-09-23 09:01:49 +00:00
|
|
|
e->expr.type = &type_float;
|
2024-12-04 15:13:56 +00:00
|
|
|
} else {
|
|
|
|
// component-wise multiplication
|
|
|
|
e->expr.type = &type_vector;
|
2023-02-11 11:11:46 +00:00
|
|
|
}
|
2022-02-06 11:12:22 +00:00
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
2023-09-27 03:41:31 +00:00
|
|
|
static const expr_t *
|
2024-12-04 15:13:56 +00:00
|
|
|
quaternion_quaternion_expr (int op, const expr_t *a, const expr_t *b)
|
2013-09-27 08:48:06 +00:00
|
|
|
{
|
2024-12-04 15:13:56 +00:00
|
|
|
return typed_binary_expr (&type_quaternion, QC_QMUL, a, b);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const expr_t *
|
|
|
|
quaternion_vector_expr (int op, const expr_t *a, const expr_t *b)
|
|
|
|
{
|
|
|
|
return typed_binary_expr (&type_vector, QC_QVMUL, a, b);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const expr_t *
|
|
|
|
vector_quaternion_expr (int op, const expr_t *a, const expr_t *b)
|
|
|
|
{
|
|
|
|
return typed_binary_expr (&type_vector, QC_VQMUL, a, b);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const expr_t *
|
|
|
|
outer_product_expr (int op, const expr_t *a, const expr_t *b)
|
|
|
|
{
|
|
|
|
auto ta = get_type (a);
|
|
|
|
auto tb = get_type (b);
|
|
|
|
if (is_integral (ta) || is_integral (tb)) {
|
|
|
|
warning (a, "integral vectors in outer product");
|
|
|
|
ta = float_type (ta);
|
|
|
|
tb = float_type (tb);
|
|
|
|
a = cast_expr (ta, a);
|
|
|
|
b = cast_expr (tb, b);
|
|
|
|
}
|
|
|
|
int rows = type_width (ta);
|
|
|
|
int cols = type_width (tb);
|
|
|
|
auto type = matrix_type (ta, cols, rows);
|
|
|
|
auto e = typed_binary_expr (type, QC_OUTER, a, b);
|
|
|
|
return e;
|
2013-09-27 08:48:06 +00:00
|
|
|
}
|
|
|
|
|
2024-12-04 15:13:56 +00:00
|
|
|
static const expr_t *
|
|
|
|
dot_product_expr (int op, const expr_t *a, const expr_t *b)
|
|
|
|
{
|
|
|
|
auto ta = get_type (a);
|
|
|
|
auto tb = get_type (b);
|
|
|
|
if (is_integral (ta) || is_integral (tb)) {
|
|
|
|
warning (a, "integral vectors in dot product");
|
|
|
|
ta = float_type (ta);
|
|
|
|
tb = float_type (tb);
|
|
|
|
a = cast_expr (ta, a);
|
|
|
|
b = cast_expr (tb, b);
|
|
|
|
}
|
|
|
|
auto type = base_type (ta);
|
|
|
|
auto e = typed_binary_expr (type, QC_DOT, a, b);
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const type_t *
|
|
|
|
bool_result (const type_t *a, const type_t *b)
|
|
|
|
{
|
|
|
|
return bool_type (a);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
shape_matrix (const type_t *a, const type_t *b)
|
|
|
|
{
|
|
|
|
if (type_cols (a) == type_rows (b)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
error (0, "matrix colums != matrix rows: %d %d",
|
|
|
|
type_cols (a), type_rows (b));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
shape_matvec (const type_t *a, const type_t *b)
|
|
|
|
{
|
|
|
|
if (type_cols (a) == type_width (b)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
error (0, "matrix colums != vectors width: %d %d",
|
|
|
|
type_cols (a), type_width (b));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
shape_vecmat (const type_t *a, const type_t *b)
|
|
|
|
{
|
|
|
|
if (type_width (a) == type_rows (b)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
error (0, "vectors width != matrix rows: %d %d",
|
|
|
|
type_width (a), type_rows (b));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
shape_always (const type_t *a, const type_t *b)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static expr_type_t equality_ops[] = {
|
|
|
|
{ .match_a = is_string, .match_b = is_string,
|
|
|
|
.res_type = bool_result },
|
|
|
|
{ .match_a = is_math, .match_b = is_math,
|
|
|
|
.process = math_compare },
|
|
|
|
{ .match_a = is_entity, .match_b = is_entity,
|
|
|
|
.process = entity_compare },
|
|
|
|
{ .match_a = is_field, .match_b = is_field, },
|
|
|
|
{ .match_a = is_func, .match_b = is_func,
|
|
|
|
.process = func_compare },
|
|
|
|
{ .match_a = is_pointer, .match_b = is_pointer,
|
|
|
|
.process = pointer_compare },
|
|
|
|
{ .match_a = is_handle, .match_b = is_handle, },
|
|
|
|
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
static expr_type_t compare_ops[] = {
|
|
|
|
{ .match_a = is_string, .match_b = is_string,
|
|
|
|
.res_type = bool_result },
|
|
|
|
{ .match_a = is_math, .match_b = is_math,
|
|
|
|
.process = math_compare },
|
|
|
|
{ .match_a = is_pointer, .match_b = is_pointer,
|
|
|
|
.process = pointer_compare },
|
|
|
|
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
static expr_type_t shift_ops[] = {
|
|
|
|
{ .match_a = is_math, .match_b = is_math, },
|
|
|
|
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
static expr_type_t add_ops[] = {
|
|
|
|
{ .match_a = is_ptr, .match_b = is_integral,
|
|
|
|
.process = pointer_arithmetic, },
|
|
|
|
{ .match_a = is_integral, .match_b = is_ptr,
|
|
|
|
.process = pointer_arithmetic, },
|
|
|
|
{ .match_a = is_string, .match_b = is_string, },
|
2025-01-13 03:52:12 +00:00
|
|
|
{ .match_a = is_matrix, .match_b = is_scalar,
|
|
|
|
.match_shape = shape_always,
|
|
|
|
.process = matrix_scalar_expr, },
|
|
|
|
{ .match_a = is_scalar, .match_b = is_matrix,
|
|
|
|
.match_shape = shape_always,
|
|
|
|
.process = matrix_scalar_expr, },
|
|
|
|
{ .match_a = is_nonscalar,.match_b = is_scalar,
|
|
|
|
.match_shape = shape_always,
|
|
|
|
.process = vector_scalar_expr, },
|
|
|
|
{ .match_a = is_scalar, .match_b = is_nonscalar,
|
|
|
|
.match_shape = shape_always,
|
|
|
|
.process = vector_scalar_expr, },
|
2024-12-04 15:13:56 +00:00
|
|
|
{ .match_a = is_math, .match_b = is_math,
|
|
|
|
.promote = true },
|
|
|
|
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
static expr_type_t sub_ops[] = {
|
|
|
|
{ .match_a = is_ptr, .match_b = is_integral,
|
|
|
|
.process = pointer_arithmetic, },
|
|
|
|
{ .match_a = is_ptr, .match_b = is_ptr,
|
|
|
|
.process = pointer_arithmetic, },
|
2025-01-13 03:52:12 +00:00
|
|
|
{ .match_a = is_matrix, .match_b = is_scalar,
|
|
|
|
.match_shape = shape_always,
|
|
|
|
.process = matrix_scalar_expr, },
|
|
|
|
{ .match_a = is_scalar, .match_b = is_matrix,
|
|
|
|
.match_shape = shape_always,
|
|
|
|
.process = matrix_scalar_expr, },
|
|
|
|
{ .match_a = is_nonscalar,.match_b = is_scalar,
|
|
|
|
.match_shape = shape_always,
|
|
|
|
.process = vector_scalar_expr, },
|
|
|
|
{ .match_a = is_scalar, .match_b = is_nonscalar,
|
|
|
|
.match_shape = shape_always,
|
|
|
|
.process = vector_scalar_expr, },
|
2024-12-04 15:13:56 +00:00
|
|
|
{ .match_a = is_math, .match_b = is_math,
|
|
|
|
.promote = true },
|
|
|
|
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
static expr_type_t mul_ops[] = {
|
|
|
|
{ .match_a = is_matrix, .match_b = is_matrix,
|
|
|
|
.match_shape = shape_matrix,
|
|
|
|
.process = matrix_binary_expr, },
|
|
|
|
{ .match_a = is_matrix, .match_b = is_nonscalar,
|
|
|
|
.match_shape = shape_matvec,
|
|
|
|
.process = matrix_binary_expr, },
|
|
|
|
{ .match_a = is_nonscalar, .match_b = is_matrix,
|
|
|
|
.match_shape = shape_vecmat,
|
|
|
|
.process = matrix_binary_expr, },
|
|
|
|
{ .match_a = is_matrix, .match_b = is_scalar,
|
|
|
|
.match_shape = shape_always,
|
2025-01-13 03:52:12 +00:00
|
|
|
.promote = true, .process = matrix_scalar_mul, },
|
2024-12-04 15:13:56 +00:00
|
|
|
{ .match_a = is_scalar, .match_b = is_matrix,
|
|
|
|
.match_shape = shape_always,
|
2025-01-13 03:52:12 +00:00
|
|
|
.promote = true, .process = matrix_scalar_mul, },
|
2024-12-04 15:13:56 +00:00
|
|
|
{ .match_a = is_nonscalar, .match_b = is_scalar,
|
|
|
|
.match_shape = shape_always,
|
2025-01-13 03:52:12 +00:00
|
|
|
.promote = true, .process = matrix_scalar_mul, },
|
2024-12-04 15:13:56 +00:00
|
|
|
{ .match_a = is_scalar, .match_b = is_nonscalar,
|
|
|
|
.match_shape = shape_always,
|
2025-01-13 03:52:12 +00:00
|
|
|
.promote = true, .process = matrix_scalar_mul, },
|
2024-12-04 15:13:56 +00:00
|
|
|
{ .match_a = is_vector, .match_b = is_vector,
|
2025-01-13 03:52:12 +00:00
|
|
|
.process = vector_vector_mul, },
|
2024-12-04 15:13:56 +00:00
|
|
|
{ .match_a = is_quaternion, .match_b = is_quaternion,
|
|
|
|
.process = quaternion_quaternion_expr, },
|
|
|
|
{ .match_a = is_quaternion, .match_b = is_vector,
|
|
|
|
.match_shape = shape_always,
|
|
|
|
.process = quaternion_vector_expr, },
|
|
|
|
{ .match_a = is_vector, .match_b = is_quaternion,
|
|
|
|
.match_shape = shape_always,
|
|
|
|
.process = vector_quaternion_expr, },
|
|
|
|
{ .match_a = is_math, .match_b = is_math,
|
|
|
|
.promote = true },
|
|
|
|
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
static expr_type_t outer_ops[] = {
|
|
|
|
{ .match_a = is_nonscalar, .match_b = is_nonscalar,
|
|
|
|
.process = outer_product_expr, },
|
|
|
|
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
static expr_type_t cross_ops[] = {
|
|
|
|
{ .match_a = is_vector, .match_b = is_vector, },
|
|
|
|
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
static expr_type_t dot_ops[] = {
|
|
|
|
{ .match_a = is_nonscalar, .match_b = is_nonscalar,
|
|
|
|
.process = dot_product_expr, },
|
|
|
|
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
static expr_type_t div_ops[] = {
|
|
|
|
{ .match_a = is_matrix, .match_b = is_scalar,
|
|
|
|
.match_shape = shape_always,
|
|
|
|
.promote = true, .process = matrix_scalar_div, },
|
|
|
|
{ .match_a = is_nonscalar, .match_b = is_scalar,
|
|
|
|
.match_shape = shape_always,
|
|
|
|
.promote = true, .process = matrix_scalar_div, },
|
|
|
|
{ .match_a = is_math, .match_b = is_math,
|
|
|
|
.promote = true },
|
|
|
|
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
static expr_type_t mod_ops[] = {
|
|
|
|
{ .match_a = is_matrix, .match_b = is_matrix, }, // invalid op
|
|
|
|
{ .match_a = is_nonscalar, .match_b = is_scalar,
|
|
|
|
.match_shape = shape_always,
|
|
|
|
.promote = true, .process = matrix_scalar_div, },
|
|
|
|
{ .match_a = is_math, .match_b = is_math,
|
|
|
|
.promote = true },
|
|
|
|
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
static expr_type_t bit_ops[] = {
|
2025-01-18 05:13:40 +00:00
|
|
|
{ .match_a = is_math, .match_b = is_math,
|
|
|
|
.promote = true },
|
2024-12-04 15:13:56 +00:00
|
|
|
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
2025-01-11 04:02:15 +00:00
|
|
|
static expr_type_t bool_ops[] = {
|
|
|
|
{ .match_a = is_boolean, .match_b = is_boolean, },
|
|
|
|
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
2024-12-04 15:13:56 +00:00
|
|
|
#define countof(x) (sizeof(x)/sizeof(x[0]))
|
|
|
|
|
|
|
|
static expr_type_t *expr_types[] = {
|
|
|
|
[QC_EQ] = equality_ops,
|
|
|
|
[QC_NE] = equality_ops,
|
|
|
|
[QC_LE] = compare_ops,
|
|
|
|
[QC_GE] = compare_ops,
|
|
|
|
[QC_LT] = compare_ops,
|
|
|
|
[QC_GT] = compare_ops,
|
|
|
|
[QC_SHL] = shift_ops,
|
|
|
|
[QC_SHR] = shift_ops,
|
|
|
|
['+'] = add_ops,
|
|
|
|
['-'] = sub_ops,
|
|
|
|
['*'] = mul_ops,
|
|
|
|
['/'] = div_ops,
|
|
|
|
['&'] = bit_ops,
|
|
|
|
['|'] = bit_ops,
|
|
|
|
['^'] = bit_ops,
|
|
|
|
['%'] = mod_ops,
|
2025-01-11 04:02:15 +00:00
|
|
|
[QC_AND] = bool_ops,
|
|
|
|
[QC_OR] = bool_ops,
|
|
|
|
[QC_XOR] = bool_ops,
|
2024-12-04 15:13:56 +00:00
|
|
|
[QC_MOD] = mod_ops,
|
|
|
|
[QC_GEOMETRIC] = nullptr, // handled by algebra_binary_expr
|
|
|
|
[QC_HADAMARD] = mul_ops,
|
|
|
|
[QC_CROSS] = cross_ops,
|
|
|
|
[QC_DOT] = dot_ops,
|
|
|
|
[QC_OUTER] = outer_ops,
|
|
|
|
[QC_WEDGE] = nullptr, // handled by algebra_binary_expr
|
|
|
|
[QC_REGRESSIVE] = nullptr, // handled by algebra_binary_expr
|
|
|
|
};
|
|
|
|
|
2023-09-27 03:41:31 +00:00
|
|
|
static const expr_t *
|
|
|
|
reimplement_binary_expr (int op, const expr_t *e1, const expr_t *e2)
|
2013-09-27 08:48:06 +00:00
|
|
|
{
|
|
|
|
if (options.code.progsversion == PROG_ID_VERSION) {
|
|
|
|
switch (op) {
|
|
|
|
case '%':
|
|
|
|
{
|
2024-11-26 05:19:58 +00:00
|
|
|
auto div = paren_expr (binary_expr ('/', e1, e2));
|
|
|
|
auto trn = binary_expr ('&', div, div);
|
|
|
|
return binary_expr ('-', e1, binary_expr ('*', e2, trn));
|
2013-09-27 08:48:06 +00:00
|
|
|
}
|
|
|
|
break;
|
2024-11-26 05:38:55 +00:00
|
|
|
case QC_MOD:
|
|
|
|
{
|
|
|
|
auto div = paren_expr (binary_expr ('/', e1, e2));
|
|
|
|
auto trn = binary_expr ('&', div, div);
|
|
|
|
auto one = binary_expr (QC_GT, trn, div);
|
|
|
|
auto flr = binary_expr ('-', trn, one);
|
|
|
|
return binary_expr ('-', e1, binary_expr ('*', e2, flr));
|
|
|
|
}
|
|
|
|
break;
|
2013-09-27 08:48:06 +00:00
|
|
|
}
|
|
|
|
}
|
2024-11-26 05:19:58 +00:00
|
|
|
return nullptr;
|
2013-09-27 08:48:06 +00:00
|
|
|
}
|
|
|
|
|
2023-09-27 03:41:31 +00:00
|
|
|
static const expr_t *
|
|
|
|
check_precedence (int op, const expr_t *e1, const expr_t *e2)
|
2013-09-27 08:48:06 +00:00
|
|
|
{
|
2023-09-23 09:01:49 +00:00
|
|
|
if (e1->type == ex_uexpr && e1->expr.op == '!' && !e1->paren) {
|
2013-09-27 08:48:06 +00:00
|
|
|
if (options.traditional) {
|
2023-10-24 10:50:31 +00:00
|
|
|
if (op != QC_AND && op != QC_OR && op != '=') {
|
2013-09-27 08:48:06 +00:00
|
|
|
notice (e1, "precedence of `!' and `%s' inverted for "
|
|
|
|
"traditional code", get_op_string (op));
|
2024-09-13 23:31:26 +00:00
|
|
|
e1 = paren_expr (e1->expr.e1);
|
|
|
|
return unary_expr ('!', binary_expr (op, e1, e2));
|
2013-09-27 08:48:06 +00:00
|
|
|
}
|
|
|
|
} 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 == '|')
|
2023-09-23 09:01:49 +00:00
|
|
|
&& (is_math_op (e2->expr.op) || is_compare (e2->expr.op)))
|
2023-02-11 06:29:16 +00:00
|
|
|
|| (op == '='
|
2023-10-24 10:50:31 +00:00
|
|
|
&&(e2->expr.op == QC_OR || e2->expr.op == QC_AND))) {
|
2013-09-27 08:48:06 +00:00
|
|
|
notice (e1, "precedence of `%s' and `%s' inverted for "
|
|
|
|
"traditional code", get_op_string (op),
|
2023-09-23 09:01:49 +00:00
|
|
|
get_op_string (e2->expr.op));
|
|
|
|
e1 = binary_expr (op, e1, e2->expr.e1);
|
2024-09-13 23:31:26 +00:00
|
|
|
e1 = paren_expr (e1);
|
2023-09-23 09:01:49 +00:00
|
|
|
return binary_expr (e2->expr.op, e1, e2->expr.e2);
|
2013-09-27 08:48:06 +00:00
|
|
|
}
|
2023-10-24 10:50:31 +00:00
|
|
|
if (((op == QC_EQ || op == QC_NE) && is_compare (e2->expr.op))
|
|
|
|
|| (op == QC_OR && e2->expr.op == QC_AND)
|
2023-09-23 09:01:49 +00:00
|
|
|
|| (op == '|' && e2->expr.op == '&')) {
|
2013-09-27 08:48:06 +00:00
|
|
|
notice (e1, "precedence of `%s' raised to `%s' for "
|
|
|
|
"traditional code", get_op_string (op),
|
2023-09-23 09:01:49 +00:00
|
|
|
get_op_string (e2->expr.op));
|
|
|
|
e1 = binary_expr (op, e1, e2->expr.e1);
|
2024-09-13 23:31:26 +00:00
|
|
|
e1 = paren_expr (e1);
|
2023-09-23 09:01:49 +00:00
|
|
|
return binary_expr (e2->expr.op, e1, e2->expr.e2);
|
2013-09-27 08:48:06 +00:00
|
|
|
}
|
|
|
|
} else if (e1->type == ex_expr && !e1->paren) {
|
|
|
|
if (((op == '&' || op == '|')
|
2023-09-23 09:01:49 +00:00
|
|
|
&& (is_math_op (e1->expr.op) || is_compare (e1->expr.op)))
|
2023-02-11 06:29:16 +00:00
|
|
|
|| (op == '='
|
2023-10-24 10:50:31 +00:00
|
|
|
&&(e2->expr.op == QC_OR || e2->expr.op == QC_AND))) {
|
2013-09-27 08:48:06 +00:00
|
|
|
notice (e1, "precedence of `%s' and `%s' inverted for "
|
|
|
|
"traditional code", get_op_string (op),
|
2023-09-23 09:01:49 +00:00
|
|
|
get_op_string (e1->expr.op));
|
|
|
|
e2 = binary_expr (op, e1->expr.e2, e2);
|
2024-09-13 23:31:26 +00:00
|
|
|
e1 = paren_expr (e1->expr.e1);
|
|
|
|
return binary_expr (e1->expr.op, e1, e2);
|
2013-09-27 08:48:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (e2->type == ex_expr && !e2->paren) {
|
|
|
|
if ((op == '&' || op == '|' || op == '^')
|
2023-09-23 09:01:49 +00:00
|
|
|
&& (is_math_op (e2->expr.op)
|
|
|
|
|| is_compare (e2->expr.op))) {
|
2013-09-27 08:48:06 +00:00
|
|
|
if (options.warnings.precedence)
|
|
|
|
warning (e2, "suggest parentheses around %s in "
|
|
|
|
"operand of %c",
|
2023-09-23 09:01:49 +00:00
|
|
|
is_compare (e2->expr.op)
|
2013-09-27 08:48:06 +00:00
|
|
|
? "comparison"
|
2023-09-23 09:01:49 +00:00
|
|
|
: get_op_string (e2->expr.op),
|
2013-09-27 08:48:06 +00:00
|
|
|
op);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (e1->type == ex_expr && !e1->paren) {
|
|
|
|
if ((op == '&' || op == '|' || op == '^')
|
2023-09-23 09:01:49 +00:00
|
|
|
&& (is_math_op (e1->expr.op)
|
|
|
|
|| is_compare (e1->expr.op))) {
|
2013-09-27 08:48:06 +00:00
|
|
|
if (options.warnings.precedence)
|
|
|
|
warning (e1, "suggest parentheses around %s in "
|
|
|
|
"operand of %c",
|
2023-09-23 09:01:49 +00:00
|
|
|
is_compare (e1->expr.op)
|
2013-09-27 08:48:06 +00:00
|
|
|
? "comparison"
|
2023-09-23 09:01:49 +00:00
|
|
|
: get_op_string (e1->expr.op),
|
2013-09-27 08:48:06 +00:00
|
|
|
op);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-09-27 03:41:31 +00:00
|
|
|
static int
|
|
|
|
is_call (const expr_t *e)
|
2020-03-26 11:16:52 +00:00
|
|
|
{
|
2023-09-23 09:01:49 +00:00
|
|
|
return e->type == ex_block && e->block.is_call;
|
2020-03-26 11:16:52 +00:00
|
|
|
}
|
|
|
|
|
2023-09-27 03:41:31 +00:00
|
|
|
const expr_t *
|
|
|
|
binary_expr (int op, const expr_t *e1, const expr_t *e2)
|
2013-09-27 08:48:06 +00:00
|
|
|
{
|
2020-03-13 16:24:13 +00:00
|
|
|
// FIXME this is target-specific info and should not be in the
|
|
|
|
// expression tree
|
2023-09-23 09:01:49 +00:00
|
|
|
if (e1->type == ex_alias && is_call (e1->alias.expr)) {
|
2020-03-26 11:16:52 +00:00
|
|
|
// move the alias expression inside the block so the following check
|
|
|
|
// can detect the call and move the temp assignment into the block
|
2023-09-27 03:41:31 +00:00
|
|
|
auto block = (expr_t *) e1->alias.expr;
|
|
|
|
auto ne = new_expr ();
|
|
|
|
*ne = *e1;
|
|
|
|
ne->alias.expr = block->block.result;
|
|
|
|
block->block.result = ne;
|
2020-03-26 11:16:52 +00:00
|
|
|
e1 = block;
|
|
|
|
}
|
2023-09-23 09:01:49 +00:00
|
|
|
if (e1->type == ex_block && e1->block.is_call
|
|
|
|
&& has_function_call (e2) && e1->block.result) {
|
2023-09-27 03:41:31 +00:00
|
|
|
// the temp assignment needs to be inside the block so assignment
|
2020-03-26 11:16:52 +00:00
|
|
|
// code generation doesn't see it when applying right-associativity
|
2023-09-23 09:01:49 +00:00
|
|
|
expr_t *tmp = new_temp_def_expr (get_type (e1->block.result));
|
2023-09-27 03:41:31 +00:00
|
|
|
auto ne = assign_expr (tmp, e1->block.result);
|
|
|
|
auto nb = new_block_expr (e1);
|
|
|
|
append_expr (nb, ne);
|
|
|
|
nb->block.result = tmp;
|
|
|
|
e1 = nb;
|
2013-09-27 14:07:38 +00:00
|
|
|
}
|
2013-09-27 08:48:06 +00:00
|
|
|
if (e1->type == ex_error)
|
|
|
|
return e1;
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
2024-12-04 15:13:56 +00:00
|
|
|
const expr_t *e;
|
2013-09-27 08:48:06 +00:00
|
|
|
if ((e = check_precedence (op, e1, e2)))
|
|
|
|
return e;
|
|
|
|
|
2024-10-26 04:30:59 +00:00
|
|
|
if (is_reference (get_type (e1))) {
|
|
|
|
e1 = pointer_deref (e1);
|
|
|
|
}
|
|
|
|
if (is_reference (get_type (e2))) {
|
|
|
|
e2 = pointer_deref (e2);
|
|
|
|
}
|
|
|
|
|
2024-02-13 14:09:28 +00:00
|
|
|
auto t1 = get_type (e1);
|
|
|
|
auto t2 = get_type (e2);
|
2013-09-27 08:48:06 +00:00
|
|
|
if (!t1 || !t2)
|
|
|
|
internal_error (e1, "expr with no type");
|
|
|
|
|
2023-08-21 08:37:56 +00:00
|
|
|
if (is_algebra (t1) || is_algebra (t2)) {
|
|
|
|
return algebra_binary_expr (op, e1, e2);
|
|
|
|
}
|
|
|
|
|
2023-10-24 10:50:31 +00:00
|
|
|
if (op == QC_EQ || op == QC_NE) {
|
2013-09-27 08:48:06 +00:00
|
|
|
if (e1->type == ex_nil) {
|
|
|
|
t1 = t2;
|
2023-09-27 03:41:31 +00:00
|
|
|
e1 = convert_nil (e1, t1);
|
2013-09-27 08:48:06 +00:00
|
|
|
} else if (e2->type == ex_nil) {
|
|
|
|
t2 = t1;
|
2023-09-27 03:41:31 +00:00
|
|
|
e2 = convert_nil (e2, t2);
|
2013-09-27 08:48:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-07 10:40:19 +00:00
|
|
|
if (is_array (t1) && (is_pointer (t2) || is_integral (t2))) {
|
2024-02-13 14:13:01 +00:00
|
|
|
t1 = pointer_type (dereference_type (t1));
|
2023-05-14 03:39:11 +00:00
|
|
|
e1 = cast_expr (t1, e1);
|
|
|
|
}
|
2024-10-07 10:40:19 +00:00
|
|
|
if (is_array (t2) && (is_pointer (t1) || is_integral (t1))) {
|
2024-02-13 14:13:01 +00:00
|
|
|
t1 = pointer_type (dereference_type (t2));
|
2023-05-14 03:39:11 +00:00
|
|
|
e2 = cast_expr (t2, e2);
|
|
|
|
}
|
2020-02-23 13:28:54 +00:00
|
|
|
|
2024-12-04 15:13:56 +00:00
|
|
|
if ((unsigned) op > countof (expr_types) || !expr_types[op]) {
|
2024-12-06 17:38:00 +00:00
|
|
|
internal_error (e1, "invalid operator: %s", get_op_string (op));
|
2023-10-02 14:33:37 +00:00
|
|
|
}
|
2024-12-04 15:13:56 +00:00
|
|
|
expr_type_t *expr_type = expr_types[op];
|
|
|
|
for (; expr_type->match_a; expr_type++) {
|
|
|
|
if (expr_type->match_a (t1) && expr_type->match_b (t2)) {
|
|
|
|
break;
|
|
|
|
}
|
2023-10-02 14:33:37 +00:00
|
|
|
}
|
2024-12-04 15:13:56 +00:00
|
|
|
if (!expr_type->match_a) {
|
|
|
|
return error (e1, "invalid binary expression");
|
2023-10-02 14:33:37 +00:00
|
|
|
}
|
2024-12-04 15:13:56 +00:00
|
|
|
if (expr_type->match_shape) {
|
|
|
|
scoped_src_loc (e1);//for error messages
|
|
|
|
if (!expr_type->match_shape (t1, t2)) {
|
|
|
|
return new_error_expr ();
|
2022-09-14 05:39:09 +00:00
|
|
|
}
|
2024-12-04 15:13:56 +00:00
|
|
|
} else {
|
|
|
|
if (type_width (t1) != type_width (t2)
|
|
|
|
|| type_cols(t1) != type_cols(t2)) {
|
|
|
|
return error (e1, "operand size mismatch in binary expression");
|
2023-10-30 13:46:43 +00:00
|
|
|
}
|
2024-12-04 15:13:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (expr_type->promote) {
|
|
|
|
if (e1->implicit && type_demotes (t2, t1)) {
|
|
|
|
t1 = promote_type (t2, t1);
|
|
|
|
e1 = cast_expr (t1, e1);
|
|
|
|
} else if (e2->implicit && type_demotes (t1, t2)) {
|
|
|
|
t2 = promote_type (t1, t2);
|
|
|
|
e2 = cast_expr (t2, e2);
|
|
|
|
} else {
|
|
|
|
promote_exprs (&e1, &e2);
|
2022-04-29 06:27:12 +00:00
|
|
|
t1 = get_type (e1);
|
|
|
|
t2 = get_type (e2);
|
|
|
|
}
|
2022-02-03 15:25:31 +00:00
|
|
|
}
|
2024-12-04 15:13:56 +00:00
|
|
|
|
2019-06-09 15:36:13 +00:00
|
|
|
if (expr_type->process) {
|
2024-12-04 15:13:56 +00:00
|
|
|
auto e = expr_type->process (op, e1, e2);
|
2023-09-25 07:03:29 +00:00
|
|
|
return edag_add_expr (e);
|
2019-06-09 15:36:13 +00:00
|
|
|
}
|
2013-09-27 08:48:06 +00:00
|
|
|
|
2024-12-04 15:13:56 +00:00
|
|
|
auto type = t1;
|
|
|
|
if (expr_type->res_type) {
|
|
|
|
type = expr_type->res_type (t1, t2);
|
|
|
|
if (!type) {
|
|
|
|
return new_error_expr ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-27 08:48:06 +00:00
|
|
|
if ((e = reimplement_binary_expr (op, e1, e2)))
|
2023-09-25 07:03:29 +00:00
|
|
|
return edag_add_expr (fold_constants (e));
|
2013-09-27 08:48:06 +00:00
|
|
|
|
2024-02-01 02:00:27 +00:00
|
|
|
if (expr_type->true_op) {
|
|
|
|
op = expr_type->true_op;
|
|
|
|
}
|
|
|
|
|
2023-09-27 03:41:31 +00:00
|
|
|
auto ne = new_binary_expr (op, e1, e2);
|
2024-12-04 15:13:56 +00:00
|
|
|
ne->expr.type = type;
|
2023-09-25 08:26:37 +00:00
|
|
|
if (expr_type->commutative) {
|
2023-09-27 03:41:31 +00:00
|
|
|
ne->expr.commutative = expr_type->commutative ();
|
2023-09-25 08:26:37 +00:00
|
|
|
}
|
|
|
|
if (expr_type->anticommute) {
|
2023-09-27 03:41:31 +00:00
|
|
|
ne->expr.anticommute = expr_type->anticommute ();
|
2023-09-25 08:26:37 +00:00
|
|
|
}
|
2023-10-02 13:32:23 +00:00
|
|
|
if (expr_type->associative) {
|
|
|
|
ne->expr.associative = expr_type->associative ();
|
|
|
|
}
|
2013-09-27 08:48:06 +00:00
|
|
|
if (is_compare (op) || is_logic (op)) {
|
2020-02-23 16:20:24 +00:00
|
|
|
if (options.code.progsversion == PROG_ID_VERSION) {
|
2023-09-27 03:41:31 +00:00
|
|
|
ne->expr.type = &type_float;
|
2020-02-23 16:20:24 +00:00
|
|
|
}
|
2013-09-27 08:48:06 +00:00
|
|
|
}
|
2023-09-27 03:41:31 +00:00
|
|
|
return edag_add_expr (fold_constants (ne));
|
2013-09-27 08:48:06 +00:00
|
|
|
}
|