[qfcc] Give alias expressions their own type

While this was a pain to get working, that pain only went to prove the
value of using proper "types" (even if only an enum) for different
expression types: just finding all the places to edit was a chore, and
easy to make mistakes (forgetting bits here and there).

Strangely enough, this exposed a pile of *type* aliasing bugs (next
commit).
This commit is contained in:
Bill Currie 2022-01-08 12:06:52 +09:00
parent 420d55406f
commit 23c9a317f8
9 changed files with 118 additions and 81 deletions

View file

@ -207,6 +207,12 @@ typedef struct ex_value_s {
} v;
} ex_value_t;
typedef struct {
struct type_s *type; ///< type to view the expression
struct expr_s *expr; ///< the expression to alias
struct expr_s *offset; ///< offset for alias
} ex_alias_t;
#define POINTER_VAL(p) (((p).def ? (p).def->offset : 0) + (p).val)
typedef struct expr_s {
@ -233,6 +239,7 @@ typedef struct expr_s {
ex_value_t *value; ///< constant value
element_chain_t compound; ///< compound initializer
ex_memset_t memset; ///< memset expr params
ex_alias_t alias; ///< alias expr params
struct type_s *nil; ///< type for nil if known
} e;
} expr_t;

View file

@ -54,5 +54,6 @@ EX_EXPR(nil) ///< umm, nil, null. nuff said (0 of any type)
EX_EXPR(value) ///< constant value (::ex_value_t)
EX_EXPR(compound) ///< compound initializer
EX_EXPR(memset) ///< memset needs three params...
EX_EXPR(alias) ///< view expression as different type (::ex_alias_t)
///@}

View file

@ -1718,7 +1718,7 @@ fold_constants (expr_t *e)
return e;
}
op = e->e.expr.op;
if (op == 'A' || op == 'g' || op == 'r')
if (op == 'g' || op == 'r')
return e;
t1 = extract_type (e1);
if (t1 >= ev_type_count || !do_unary_op[t1]) {
@ -1734,7 +1734,7 @@ fold_constants (expr_t *e)
}
op = e->e.expr.op;
if (op == 'A' || op == 'i' || op == 'n' || op == 'c' || op == 's') {
if (op == 'i' || op == 'n' || op == 'c' || op == 's') {
return e;
}

View file

@ -608,12 +608,11 @@ initialize_def (symbol_t *sym, expr_t *init, defspace_t *space,
error (init, "non-constant initializier");
return;
}
while ((init->type == ex_uexpr || init->type == ex_expr)
&& init->e.expr.op == 'A') {
if (init->type == ex_expr) {
offset += expr_integer (init->e.expr.e2);
while (init->type == ex_alias) {
if (init->e.alias.offset) {
offset += expr_integer (init->e.alias.offset);
}
init = init->e.expr.e1;
init = init->e.alias.expr;
}
if (init->type != ex_value) { //FIXME enum etc
internal_error (0, "initializier not a value");

View file

@ -100,7 +100,6 @@ get_op_string (int op)
case 'r': return "<return>";
case 's': return "<state>";
case 'c': return "<call>";
case 'A': return "<alias>";
case 'C': return "<cast>";
case 'M': return "<move>";
case 'm': return "<move>";
@ -314,36 +313,43 @@ print_subexpr (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"r\"];\n", indent, "", e,
e->e.expr.e2);
}
if (e->e.expr.op == 'A') {
dstring_t *typestr = dstring_newstr();
print_type_str (typestr, e->e.expr.type);
dasprintf (dstr, "%*se_%p [label=\"%s (%s)\\n%d\"];\n", indent, "", e,
get_op_string (e->e.expr.op), typestr->str, e->line);
dstring_delete (typestr);
} else {
dasprintf (dstr, "%*se_%p [label=\"%s\\n%d\"];\n", indent, "", e,
get_op_string (e->e.expr.op), e->line);
dasprintf (dstr, "%*se_%p [label=\"%s\\n%d\"];\n", indent, "", e,
get_op_string (e->e.expr.op), e->line);
}
static void
print_alias (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
{
int indent = level * 2 + 2;
_print_expr (dstr, e->e.alias.expr, level, id, next);
dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"a\"];\n", indent, "", e,
e->e.alias.expr);
if (e->e.alias.offset) {
_print_expr (dstr, e->e.alias.offset, level, id, next);
dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"o\"];\n", indent, "", e,
e->e.alias.offset);
}
dstring_t *typestr = dstring_newstr();
print_type_str (typestr, e->e.alias.type);
dasprintf (dstr, "%*se_%p [label=\"%s (%s)\\n%d\"];\n", indent, "", e,
"<alias>", typestr->str, e->line);
dstring_delete (typestr);
}
static void
print_uexpr (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
{
int indent = level * 2 + 2;
dstring_t *typestr = dstring_newstr();
if (e->e.expr.op != 'g' && e->e.expr.e1)
_print_expr (dstr, e->e.expr.e1, level, id, next);
if (e->e.expr.op == 'A') {
dstring_copystr (typestr, "\\n");
print_type_str (typestr, e->e.expr.type);
}
if (e->e.expr.op != 'r' || e->e.expr.e1)
dasprintf (dstr, "%*se_%p -> \"e_%p\";\n", indent, "", e,
e->e.expr.e1);
dasprintf (dstr, "%*se_%p [label=\"%s%s\\n%d\"];\n", indent, "", e,
get_op_string (e->e.expr.op), typestr->str, e->line);
dstring_delete (typestr);
dasprintf (dstr, "%*se_%p [label=\"%s\\n%d\"];\n", indent, "", e,
get_op_string (e->e.expr.op), e->line);
}
static void
@ -574,6 +580,7 @@ _print_expr (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
[ex_value] = print_value,
[ex_compound] = print_compound,
[ex_memset] = print_memset,
[ex_alias] = print_alias,
};
int indent = level * 2 + 2;
@ -585,7 +592,7 @@ _print_expr (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
return;
e->printid = id;
if ((int) e->type < 0 || e->type > ex_memset || !print_funcs[e->type]) {
if ((int) e->type < 0 || e->type >= ex_count || !print_funcs[e->type]) {
dasprintf (dstr, "%*se_%p [label=\"(bad expr type)\\n%d\"];\n",
indent, "", e, e->line);
return;

View file

@ -258,6 +258,8 @@ get_type (expr_t *e)
return e->e.vector.type;
case ex_selector:
return &type_SEL;
case ex_alias:
return e->e.alias.type;
case ex_count:
internal_error (e, "invalid expression");
}
@ -315,7 +317,7 @@ cast_error (expr_t *e, type_t *t1, type_t *t2)
print_type_str (s1, t1);
print_type_str (s2, t2);
e = error (e, "cannot cast from %s to %s", s1->str, s2->str);
e = error (e, "cannot cast from %s to %s", s1->str, s2->str);
dstring_delete (s1);
dstring_delete (s2);
return e;
@ -469,6 +471,12 @@ copy_expr (expr_t *e)
n->e.memset.val = copy_expr (e->e.memset.val);
n->e.memset.count = copy_expr (e->e.memset.count);
return n;
case ex_alias:
n = new_expr ();
*n = *e;
n->e.alias.expr = copy_expr (e->e.alias.expr);
n->e.alias.offset = copy_expr (e->e.alias.offset);
return n;
case ex_count:
break;
}
@ -875,9 +883,8 @@ new_short_expr (short short_val)
int
is_constant (expr_t *e)
{
while ((e->type == ex_uexpr || e->type == ex_expr)
&& e->e.expr.op == 'A') {
e = e->e.expr.e1;
while (e->type == ex_alias) {
e = e->e.alias.expr;
}
if (e->type == ex_nil || e->type == ex_value || e->type == ex_labelref
|| (e->type == ex_symbol && e->e.symbol->sy_type == sy_const)
@ -1231,15 +1238,14 @@ is_pointer_val (expr_t *e)
expr_t *
new_alias_expr (type_t *type, expr_t *expr)
{
expr_t *alias;
alias = new_unary_expr ('A', expr);
alias->e.expr.type = type;
//if (expr->type == ex_uexpr && expr->e.expr.op == 'A')
// bug (alias, "aliasing an alias expression");
if (expr->type == ex_expr && expr->e.expr.op == 'A') {
if (expr->type == ex_alias) {
return new_offset_alias_expr (type, expr, 0);
}
expr_t *alias = new_expr ();
alias->type = ex_alias;
alias->e.alias.type = type;
alias->e.alias.expr = expr;
alias->file = expr->file;
alias->line = expr->line;
return alias;
@ -1248,18 +1254,24 @@ new_alias_expr (type_t *type, expr_t *expr)
expr_t *
new_offset_alias_expr (type_t *type, expr_t *expr, int offset)
{
expr_t *alias;
if (expr->type == ex_expr && expr->e.expr.op == 'A') {
expr_t *ofs_expr = expr->e.expr.e2;
expr = expr->e.expr.e1;
if (expr->type == ex_alias && expr->e.alias.offset) {
expr_t *ofs_expr = expr->e.alias.offset;
if (!is_constant (ofs_expr)) {
internal_error (ofs_expr, "non-constant offset for alias expr");
}
offset += expr_integer (ofs_expr);
if (expr->e.alias.expr->type == ex_alias) {
internal_error (expr, "alias expr of alias expr");
}
expr = expr->e.alias.expr;
}
alias = new_binary_expr ('A', expr, new_integer_expr (offset));
alias->e.expr.type = type;
expr_t *alias = new_expr ();
alias->type = ex_alias;
alias->e.alias.type = type;
alias->e.alias.expr = expr;
alias->e.alias.offset = new_integer_expr (offset);
alias->file = expr->file;
alias->line = expr->line;
return alias;
@ -1576,9 +1588,27 @@ has_function_call (expr_t *e)
case ex_uexpr:
if (e->e.expr.op != 'g')
return has_function_call (e->e.expr.e1);
default:
return 0;
case ex_alias:
return has_function_call (e->e.alias.expr);
case ex_error:
case ex_state:
case ex_label:
case ex_labelref:
case ex_def:
case ex_symbol:
case ex_temp:
case ex_vector:
case ex_selector:
case ex_nil:
case ex_value:
case ex_compound:
case ex_memset:
return 0;
case ex_count:
break;
}
internal_error (e, "invalid expression type");
}
expr_t *
@ -1683,6 +1713,7 @@ unary_expr (int op, expr_t *e)
case ex_bool:
case ex_temp:
case ex_vector:
case ex_alias:
{
expr_t *n = new_unary_expr (op, e);
@ -1766,6 +1797,7 @@ unary_expr (int op, expr_t *e)
case ex_symbol:
case ex_temp:
case ex_vector:
case ex_alias:
{
expr_t *n = new_unary_expr (op, e);
@ -1842,6 +1874,7 @@ unary_expr (int op, expr_t *e)
case ex_symbol:
case ex_temp:
case ex_vector:
case ex_alias:
bitnot_expr:
if (options.code.progsversion == PROG_ID_VERSION) {
expr_t *n1 = new_integer_expr (-1);
@ -2364,16 +2397,6 @@ address_expr (expr_t *e1, expr_t *e2, type_t *t)
e = e1->e.expr.e2;
break;
}
if (e1->e.expr.op == 'A') {
if (!t)
t = e1->e.expr.type;
if (e2) {
e2 = binary_expr ('+', e1->e.expr.e2, e2);
} else {
e2 = e1->e.expr.e2;
}
return address_expr (e1->e.expr.e1, e2, t);
}
return error (e1, "invalid type for unary &");
case ex_uexpr:
if (e1->e.expr.op == '.') {
@ -2384,11 +2407,6 @@ address_expr (expr_t *e1, expr_t *e2, type_t *t)
}
break;
}
if (e1->e.expr.op == 'A') {
if (!t)
t = e1->e.expr.type;
return address_expr (e1->e.expr.e1, e2, t);
}
return error (e1, "invalid type for unary &");
case ex_label:
return new_label_ref (&e1->e.label);
@ -2396,6 +2414,18 @@ address_expr (expr_t *e1, expr_t *e2, type_t *t)
e = new_unary_expr ('&', e1);
e->e.expr.type = pointer_type (t);
break;
case ex_alias:
if (!t) {
t = e1->e.alias.type;
}
if (e1->e.alias.offset) {
if (e2) {
e2 = binary_expr ('+', e1->e.alias.offset, e2);
} else {
e2 = e1->e.alias.offset;
}
}
return address_expr (e1->e.alias.expr, e2, t);
default:
return error (e1, "invalid type for unary &");
}
@ -2416,8 +2446,11 @@ address_expr (expr_t *e1, expr_t *e2, type_t *t)
e = new_binary_expr ('&', e, e2);
}
}
if (e->type == ex_expr || e->type == ex_uexpr)
if (e->type == ex_expr || e->type == ex_uexpr) {
e->e.expr.type = pointer_type (t);
} else if (e->type == ex_alias) {
e->e.alias.type = pointer_type (t);
}
}
}
return e;

View file

@ -112,17 +112,13 @@ is_lvalue (const expr_t *expr)
if (expr->e.expr.op == '.') {
return 1;
}
if (expr->e.expr.op == 'A') {
return is_lvalue (expr->e.expr.e1);
}
break;
case ex_alias:
return is_lvalue (expr->e.alias.expr);
case ex_uexpr:
if (expr->e.expr.op == '.') {
return 1;
}
if (expr->e.expr.op == 'A') {
return is_lvalue (expr->e.expr.e1);
}
break;
case ex_memset:
case ex_compound:

View file

@ -970,12 +970,11 @@ binary_expr (int op, expr_t *e1, expr_t *e2)
e1 = convert_vector (e1);
// FIXME this is target-specific info and should not be in the
// expression tree
if ((e1->type == ex_expr || e1->type == ex_uexpr) && e1->e.expr.op == 'A'
&& is_call (e1->e.expr.e1)) {
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.expr.e1;
e1->e.expr.e1 = block->e.block.result;
expr_t *block = e1->e.alias.expr;
e1->e.alias.expr = block->e.block.result;
block->e.block.result = e1;
e1 = block;
}

View file

@ -1202,11 +1202,11 @@ expr_alias (sblock_t *sblock, expr_t *e, operand_t **op)
def_t *def;
int offset = 0;
if (e->type == ex_expr) {
offset = expr_integer (e->e.expr.e2);
if (e->e.alias.offset) {
offset = expr_integer (e->e.alias.offset);
}
type = e->e.expr.type;
sblock = statement_subexpr (sblock, e->e.expr.e1, &aop);
type = e->e.alias.type;
sblock = statement_subexpr (sblock, e->e.alias.expr, &aop);
if (type_compatible (aop->type, type)) {
//FIXME type_compatible??? shouldn't that be type_size ==?
if (offset) {
@ -1268,9 +1268,6 @@ expr_expr (sblock_t *sblock, expr_t *e, operand_t **op)
case 'M':
sblock = expr_move (sblock, e, op);
break;
case 'A':
sblock = expr_alias (sblock, e, op);
break;
default:
opcode = convert_op (e->e.expr.op);
if (!opcode)
@ -1338,9 +1335,6 @@ expr_uexpr (sblock_t *sblock, expr_t *e, operand_t **op)
case '.':
sblock = expr_deref (sblock, e, op);
break;
case 'A':
sblock = expr_alias (sblock, e, op);
break;
case 'C':
sblock = expr_cast (sblock, e, op);
break;
@ -1529,6 +1523,7 @@ statement_subexpr (sblock_t *sblock, expr_t *e, operand_t **op)
[ex_nil] = expr_nil,
[ex_value] = expr_value,
[ex_selector] = expr_selector,
[ex_alias] = expr_alias,
};
if (!e) {
*op = 0;