diff --git a/tools/qfcc/include/expr.h b/tools/qfcc/include/expr.h index 4d0c41cbc..007ce347a 100644 --- a/tools/qfcc/include/expr.h +++ b/tools/qfcc/include/expr.h @@ -905,6 +905,10 @@ expr_t *sizeof_expr (expr_t *expr, struct type_s *type); expr_t *fold_constants (expr_t *e); +void edag_flush (void); +expr_t *edag_add_expr (expr_t *e); + + ///@} #endif//__expr_h diff --git a/tools/qfcc/include/shared.h b/tools/qfcc/include/shared.h index 81b883de9..102f31b44 100644 --- a/tools/qfcc/include/shared.h +++ b/tools/qfcc/include/shared.h @@ -37,6 +37,7 @@ extern struct expr_s *local_expr; extern enum vis_e current_visibility; extern enum storage_class_e current_storage; extern struct symtab_s *current_symtab; +extern bool no_flush_dag; struct symbol_s *check_redefined (struct symbol_s *sym); extern struct symbol_s *check_undefined (struct symbol_s *sym); diff --git a/tools/qfcc/source/Makemodule.am b/tools/qfcc/source/Makemodule.am index 07f954141..56c6142a2 100644 --- a/tools/qfcc/source/Makemodule.am +++ b/tools/qfcc/source/Makemodule.am @@ -32,6 +32,7 @@ qfcc_SOURCES = \ tools/qfcc/source/expr_bool.c \ tools/qfcc/source/expr_cast.c \ tools/qfcc/source/expr_compound.c \ + tools/qfcc/source/expr_dag.c \ tools/qfcc/source/expr_obj.c \ tools/qfcc/source/expr_vector.c \ tools/qfcc/source/evaluate.c \ diff --git a/tools/qfcc/source/expr.c b/tools/qfcc/source/expr.c index 4c3dc1f1f..24d234a93 100644 --- a/tools/qfcc/source/expr.c +++ b/tools/qfcc/source/expr.c @@ -1398,10 +1398,10 @@ expr_t * new_alias_expr (type_t *type, expr_t *expr) { if (is_ptr (type) && expr->type == ex_address) { - // avoid aliasing a pointer to a pointer (redundant) - expr = copy_expr (expr); - expr->address.type = type; - return expr; + auto new = new_address_expr (type, expr->address.lvalue, + expr->address.offset); + new->address.type = type; + return new; } if (expr->type == ex_alias) { if (expr->alias.offset) { @@ -1422,7 +1422,7 @@ new_alias_expr (type_t *type, expr_t *expr) alias->alias.expr = expr; alias->file = expr->file; alias->line = expr->line; - return alias; + return edag_add_expr (alias); } expr_t * @@ -1444,11 +1444,11 @@ new_offset_alias_expr (type_t *type, expr_t *expr, int offset) expr_t *alias = new_expr (); alias->type = ex_alias; alias->alias.type = type; - alias->alias.expr = expr; - alias->alias.offset = new_int_expr (offset); + alias->alias.expr = edag_add_expr (expr); + alias->alias.offset = edag_add_expr (new_int_expr (offset)); alias->file = expr->file; alias->line = expr->line; - return alias; + return edag_add_expr (alias); } expr_t * diff --git a/tools/qfcc/source/expr_binary.c b/tools/qfcc/source/expr_binary.c index b2e9747cf..065e30f62 100644 --- a/tools/qfcc/source/expr_binary.c +++ b/tools/qfcc/source/expr_binary.c @@ -1185,7 +1185,7 @@ binary_expr (int op, expr_t *e1, expr_t *e2) int scalar_op = 0; if (type_width (t1) == 1) { // scalar op vec - if (!(e = convert_scalar (e1, op, e2))) { + if (!(e = edag_add_expr (convert_scalar (e1, op, e2)))) { return invalid_binary_expr (op, e1, e2); } scalar_op = 1; @@ -1194,7 +1194,7 @@ binary_expr (int op, expr_t *e1, expr_t *e2) } if (type_width (t2) == 1) { // vec op scalar - if (!(e = convert_scalar (e2, op, e1))) { + if (!(e = edag_add_expr (convert_scalar (e2, op, e1)))) { return invalid_binary_expr (op, e1, e2); } scalar_op = 1; @@ -1225,7 +1225,7 @@ binary_expr (int op, expr_t *e1, expr_t *e2) t1 = base_type (t1); } e->expr.type = t1; - return e; + return edag_add_expr (e); } } @@ -1240,11 +1240,12 @@ binary_expr (int op, expr_t *e1, expr_t *e2) 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)); + e = fold_constants (expr_type->process (op, e1, e2)); + return edag_add_expr (e); } if ((e = reimplement_binary_expr (op, e1, e2))) - return fold_constants (e); + return edag_add_expr (fold_constants (e)); e = new_binary_expr (op, e1, e2); e->expr.type = expr_type->result_type; @@ -1253,5 +1254,5 @@ binary_expr (int op, expr_t *e1, expr_t *e2) e->expr.type = &type_float; } } - return fold_constants (e); + return edag_add_expr (fold_constants (e)); } diff --git a/tools/qfcc/source/expr_bool.c b/tools/qfcc/source/expr_bool.c index 910430e88..989634f3b 100644 --- a/tools/qfcc/source/expr_bool.c +++ b/tools/qfcc/source/expr_bool.c @@ -325,5 +325,5 @@ convert_bool (expr_t *e, int block) append_expr (block, e->boolean.e); e->boolean.e = block; } - return e; + return edag_add_expr (e); } diff --git a/tools/qfcc/source/expr_dag.c b/tools/qfcc/source/expr_dag.c new file mode 100644 index 000000000..d726fccb2 --- /dev/null +++ b/tools/qfcc/source/expr_dag.c @@ -0,0 +1,182 @@ +/* + expr_dag.c + + Expression dag support code + + Copyright (C) 2023 Bill Currie + + 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 +#include + +#include "QF/darray.h" +#include "QF/dstring.h" +#include "QF/va.h" + +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/symtab.h" +#include "tools/qfcc/include/type.h" + +typedef struct DARRAY_TYPE (expr_t *) exprset_t; + +static exprset_t expr_dag = DARRAY_STATIC_INIT(32); + +void +edag_flush (void) +{ + expr_dag.size = 0; +} + +expr_t * +edag_add_expr (expr_t *expr) +{ + if (!expr) { + return expr; + } + for (size_t i = 0; i < expr_dag.size; i++) { + expr_t *e = expr_dag.a[i]; + if (e->type != expr->type) { + continue; + } + switch (expr->type) { + case ex_count: + internal_error (expr, "invalid expression type"); + case ex_label: + case ex_labelref: + case ex_error: + case ex_state: + case ex_bool: + case ex_compound: + case ex_memset: + case ex_branch: + case ex_return: + case ex_adjstk: + case ex_with: + case ex_args: + // these are never put in the dag + return expr; + case ex_list: + case ex_block: + if (has_function_call (expr)) { + // FIXME const functions can be dagged (and pure if known to be safe) + return expr; + } + return expr; //FIXME + case ex_expr: + if (e->expr.type == expr->expr.type + && e->expr.op == expr->expr.op + && e->expr.e1 == expr->expr.e1 + && e->expr.e2 == expr->expr.e2) { + return e; + } + break; + case ex_uexpr: + if (e->expr.type == expr->expr.type + && e->expr.op == expr->expr.op + && e->expr.e1 == expr->expr.e1) { + return e; + } + break; + case ex_def: + if (e->def == expr->def) { + return e; + } + break; + case ex_symbol: + if (e->symbol == expr->symbol) { + return e; + } + break; + case ex_temp: + return expr; //FIXME + case ex_vector: + return expr; //FIXME + case ex_selector: + if (e->selector.sel_ref == expr->selector.sel_ref + && e->selector.sel == expr->selector.sel) { + return e; + } + break; + case ex_nil: + if (e->nil == expr->nil) { + return e; + } + break; + case ex_value: + if (e->value == expr->value) { + return e; + } + break; + case ex_alias: + if (e->alias.type == expr->alias.type + && e->alias.expr == expr->alias.expr + && e->alias.offset == expr->alias.offset) { + return e; + } + break; + case ex_address: + if (e->address.type == expr->address.type + && e->address.lvalue == expr->address.lvalue + && e->address.offset == expr->address.offset) { + return e; + } + break; + case ex_assign: + return expr; // FIXME? + case ex_horizontal: + if (e->hop.type == expr->hop.type + && e->hop.vec == expr->hop.vec + && e->hop.op == expr->hop.op) { + return e; + } + break; + case ex_swizzle: + if (e->swizzle.type == expr->swizzle.type + && e->swizzle.src == expr->swizzle.src + && e->swizzle.source[0] == expr->swizzle.source[0] + && e->swizzle.source[1] == expr->swizzle.source[1] + && e->swizzle.source[2] == expr->swizzle.source[2] + && e->swizzle.source[3] == expr->swizzle.source[3] + && e->swizzle.neg == expr->swizzle.neg + && e->swizzle.zero == expr->swizzle.zero) { + return e; + } + break; + case ex_extend: + if (e->extend.type == expr->extend.type + && e->extend.src == expr->extend.src + && e->extend.extend == expr->extend.extend + && e->extend.reverse == expr->extend.reverse) { + return e; + } + break; + case ex_multivec: + return expr; //FIXME ? + } + } + DARRAY_APPEND (&expr_dag, expr); + return expr; +} diff --git a/tools/qfcc/source/qc-parse.y b/tools/qfcc/source/qc-parse.y index 86c399027..9a5673f6c 100644 --- a/tools/qfcc/source/qc-parse.y +++ b/tools/qfcc/source/qc-parse.y @@ -1370,12 +1370,12 @@ array_decl ; decl - : declspecs_ts local_expr initdecls ';' + : declspecs_ts local_expr initdecls seq_semi { $$ = local_expr; local_expr = 0; } - | declspecs_nots local_expr notype_initdecls ';' + | declspecs_nots local_expr notype_initdecls seq_semi { $$ = local_expr; local_expr = 0; @@ -1487,8 +1487,21 @@ pop_scope } ; +flush_dag + : /* empty */ + { + if (!no_flush_dag) { + edag_flush (); + } + } + ; + +seq_semi + : ';' flush_dag + ; + compound_statement - : '{' push_scope statements '}' pop_scope { $$ = $3; } + : '{' push_scope flush_dag statements '}' pop_scope flush_dag { $$ = $4; } ; statements @@ -1496,9 +1509,9 @@ statements { $$ = new_block_expr (); } - | statements statement + | statements flush_dag statement { - $$ = append_expr ($1, $2); + $$ = append_expr ($1, $3); } ; @@ -1543,7 +1556,7 @@ statement { $$ = case_label_expr (switch_block, 0); } - | SWITCH break_label '(' expr switch_block ')' compound_statement + | SWITCH break_label '(' comma_expr switch_block ')' compound_statement { $$ = switch_expr (switch_block, break_label, $7); switch_block = $5; @@ -1554,28 +1567,28 @@ statement expr_t *label = named_label_expr ($2); $$ = goto_expr (label); } - | IF not '(' texpr ')' statement %prec IFX + | IF not '(' texpr ')' flush_dag statement %prec IFX { - $$ = build_if_statement ($2, $4, $6, 0, 0); + $$ = build_if_statement ($2, $4, $7, 0, 0); } - | IF not '(' texpr ')' statement else statement + | IF not '(' texpr ')' flush_dag statement else statement { - $$ = build_if_statement ($2, $4, $6, $7, $8); + $$ = build_if_statement ($2, $4, $7, $8, $9); } | FOR push_scope break_label continue_label - '(' opt_init_semi opt_expr ';' opt_expr ')' statement pop_scope + '(' opt_init_semi opt_expr seq_semi opt_expr ')' flush_dag statement pop_scope { if ($6) { $6 = build_block_expr ($6, false); } - $$ = build_for_statement ($6, $7, $9, $11, + $$ = build_for_statement ($6, $7, $9, $12, break_label, continue_label); break_label = $3; continue_label = $4; } - | WHILE break_label continue_label not '(' texpr ')' statement + | WHILE break_label continue_label not '(' texpr ')' flush_dag statement { - $$ = build_while_statement ($4, $6, $8, break_label, + $$ = build_while_statement ($4, $6, $9, break_label, continue_label); break_label = $2; continue_label = $3; @@ -1629,7 +1642,7 @@ not ; else - : ELSE + : ELSE flush_dag { // this is only to get the the file and line number info $$ = new_nil_expr (); @@ -1673,7 +1686,7 @@ switch_block ; opt_init_semi - : comma_expr ';' + : comma_expr seq_semi | decl /* contains ; */ | ';' { @@ -1726,9 +1739,10 @@ ident_expr ; vector_expr - : '[' expr ',' expr_list ']' + : '[' expr ',' { no_flush_dag = true; } expr_list ']' { - $$ = list_prepend_expr ($4, $2); + $$ = list_prepend_expr ($5, $2); + no_flush_dag = false; } ; @@ -1745,9 +1759,9 @@ expr | expr '=' expr { $$ = assign_expr ($1, $3); } | expr '=' compound_init { $$ = assign_expr ($1, $3); } | expr ASX expr { $$ = asx_expr ($2, $1, $3); } - | expr '?' expr ':' expr { $$ = conditional_expr ($1, $3, $5); } - | expr AND bool_label expr { $$ = bool_expr (AND, $3, $1, $4); } - | expr OR bool_label expr { $$ = bool_expr (OR, $3, $1, $4); } + | expr '?' flush_dag expr ':' expr { $$ = conditional_expr ($1, $4, $6); } + | expr AND flush_dag bool_label expr{ $$ = bool_expr (AND, $4, $1, $5); } + | expr OR flush_dag bool_label expr { $$ = bool_expr (OR, $4, $1, $5); } | expr EQ expr { $$ = binary_expr (EQ, $1, $3); } | expr NE expr { $$ = binary_expr (NE, $1, $3); } | expr LE expr { $$ = binary_expr (LE, $1, $3); } @@ -1789,8 +1803,8 @@ comma_expr ; expr_list - : expr { $$ = new_list_expr ($1); } - | expr_list ',' expr { $$ = list_append_expr ($1, $3); } + : expr { $$ = new_list_expr ($1); } + | expr_list ',' flush_dag expr { $$ = list_append_expr ($1, $4); } ; opt_arg_list diff --git a/tools/qfcc/source/shared.c b/tools/qfcc/source/shared.c index acdbf149f..439abdd75 100644 --- a/tools/qfcc/source/shared.c +++ b/tools/qfcc/source/shared.c @@ -46,6 +46,7 @@ expr_t *local_expr; vis_t current_visibility; storage_class_t current_storage = sc_global; symtab_t *current_symtab; +bool no_flush_dag; /* When defining a new symbol, already existing symbols must be in a different scope. However, when they are in a different scope, we want a