[qfcc] Implement automatic casting between same-width vectors

This allows all the tests to build and pass. I'll need to add tests to
ensure warnings happen when they should and that all vec operations are
correct (ouch, that'll be a lot of work), but vectors and quaternions
are working again.
This commit is contained in:
Bill Currie 2022-04-29 15:27:12 +09:00
parent bf53edf5e3
commit 9c8e13aa4c
6 changed files with 102 additions and 24 deletions

View file

@ -2231,8 +2231,9 @@ return_expr (function_t *f, expr_t *e)
}
if (!type_assignable (ret_type, t)) {
if (!options.traditional)
return error (e, "type mismatch for return value of %s",
f->sym->name);
return error (e, "type mismatch for return value of %s: %s -> %s",
f->sym->name, get_type_string (t),
get_type_string (ret_type));
if (options.warnings.traditional)
warning (e, "type mismatch for return value of %s",
f->sym->name);

View file

@ -173,21 +173,20 @@ check_types_compatible (expr_t *dst, expr_t *src)
}
if (type_assignable (dst_type, src_type)) {
if (is_scalar (dst_type) && is_scalar (src_type)) {
if (!src->implicit) {
if (is_double (src_type)) {
warning (dst, "assignment of double to %s (use a cast)\n",
dst_type->name);
}
}
// the types are different but cast-compatible
expr_t *new = cast_expr (dst_type, src);
// the cast was a no-op, so the types are compatible at the
// low level (very true for default type <-> enum)
if (new != src) {
return assign_expr (dst, new);
debug (dst, "casting %s to %s", src_type->name, dst_type->name);
if (!src->implicit && !type_promotes (dst_type, src_type)) {
if (is_double (src_type)) {
warning (dst, "assignment of %s to %s (use a cast)\n",
src_type->name, dst_type->name);
}
}
// the types are different but cast-compatible
expr_t *new = cast_expr (dst_type, src);
// the cast was a no-op, so the types are compatible at the
// low level (very true for default type <-> enum)
if (new != src) {
return assign_expr (dst, new);
}
return 0;
}
// traditional qcc is a little sloppy

View file

@ -642,6 +642,27 @@ static expr_type_t **binary_expr_types[ev_type_count] = {
[ev_double] = double_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
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);
}
static expr_t *
pointer_arithmetic (int op, expr_t *e1, expr_t *e2)
{
@ -1025,12 +1046,55 @@ binary_expr (int op, expr_t *e1, expr_t *e2)
return invalid_binary_expr(op, e1, e2);
if ((t1->width > 1 || t2->width > 1)) {
if (t1 != t2) {
// 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 (type_width (t1) == 1) {
// scalar op vec
if (!(e = convert_scalar (e1, op, e2))) {
return invalid_binary_expr (op, e1, e2);
}
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);
}
e2 = e;
t2 = get_type (e2);
}
if (type_width (t1) != type_width (t2)) {
// vec op vec of different widths
return invalid_binary_expr (op, e1, e2);
}
e = new_binary_expr (op, e1, e2);
e->e.expr.type = t1;
return e;
if (t1 != t2) {
if (is_float (base_type (t1)) && is_double (base_type (t2))
&& e2->implicit) {
e2 = cast_expr (t1, e2);
} else if (is_double (base_type (t1)) && is_float (base_type (t2))
&& e1->implicit) {
e1 = cast_expr (t2, e1);
} else if (type_promotes (base_type (t1), base_type (t2))) {
e2 = cast_expr (t1, e2);
} else if (type_promotes (base_type (t2), base_type (t1))) {
e1 = cast_expr (t2, e1);
} else {
debug (e1, "%d %d\n", e1->implicit, e2->implicit);
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];

View file

@ -97,12 +97,14 @@ new_vector_list (expr_t *expr_list)
}
int all_constant = 1;
int all_implicit = 1;
expr_t *elements[count + 1];
elements[count] = 0;
count = 0;
for (expr_t *e = expr_list; e; e = e->next) {
int cast_width = type_width (get_type (e));
type_t *cast_type = vector_type (ele_type, cast_width);
all_implicit = all_implicit && e->implicit;
elements[count] = cast_expr (cast_type, fold_constants (e));
all_constant = all_constant && is_constant (elements[count]);
count++;
@ -153,7 +155,9 @@ new_vector_list (expr_t *expr_list)
offs += type_size (src_type);
}
return new_value_expr (new_type_value (vec_type, value));
expr_t *vec = new_value_expr (new_type_value (vec_type, value));
vec->implicit = all_implicit;
return vec;
}
for (int i = 0; i < count; i++) {

View file

@ -137,10 +137,12 @@ STRING \"(\\.|[^"\\])*\"
i = strtol (yytext + 2, 0, 2);
else
i = strtol (yytext, 0, 0);
if (*c == 'u' || *c == 'U')
if (*c == 'u' || *c == 'U') {
qc_yylval.expr = new_int_expr (i);//FIXME
else
} else {
qc_yylval.expr = new_int_expr (i);
qc_yylval.expr->implicit = 1;
}
return VALUE;
}

View file

@ -1124,8 +1124,16 @@ type_assignable (const type_t *dst, const type_t *src)
return 1;
return 0;
}
if (!is_ptr (dst) || !is_ptr (src))
return is_scalar (dst) && is_scalar (src);
if (!is_ptr (dst) || !is_ptr (src)) {
if (is_scalar (dst) && is_scalar (src)) {
return 1;
}
if (is_nonscalar (dst) && is_nonscalar (src)
&& type_width (dst) == type_width (src)) {
return 1;
}
return 0;
}
// pointer = pointer
// give the object system first shot because the pointee types might have