mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-02-16 17:01:53 +00:00
[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:
parent
420d55406f
commit
23c9a317f8
9 changed files with 118 additions and 81 deletions
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
||||
///@}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue