diff --git a/tools/qfcc/include/expr.h b/tools/qfcc/include/expr.h index e9806a1e1..7ae7b55bf 100644 --- a/tools/qfcc/include/expr.h +++ b/tools/qfcc/include/expr.h @@ -45,6 +45,7 @@ typedef enum { ex_state, ///< state expression (::ex_state_t) ex_bool, ///< short circuit boolean logic expression (::ex_bool_t) ex_label, ///< goto/branch label (::ex_label_t) + ex_labelref, ///< label reference (::ex_labelref_t) ex_block, ///< statement block expression (::ex_block_t) ex_expr, ///< binary expression (::ex_expr_t) ex_uexpr, ///< unary expression (::ex_expr_t) @@ -69,13 +70,17 @@ typedef struct ex_expr_s { } ex_expr_t; typedef struct ex_label_s { - struct ex_label_s *next; ///< next label in global list of labels + struct ex_label_s *next; struct reloc_s *refs; ///< relocations associated with this label struct sblock_s *dest; ///< the location of this label if known const char *name; ///< the name of this label int used; ///< label is used as a target } ex_label_t; +typedef struct { + ex_label_t *label; +} ex_labelref_t; + typedef struct { struct expr_s *head; ///< the first expression in the block struct expr_s **tail; ///< last expression in the block, for appending @@ -179,6 +184,7 @@ typedef struct expr_s { unsigned rvalue:1; ///< the expression is on the right side of = union { ex_label_t label; ///< label expression + ex_labelref_t labelref; ///< label reference expression (&) ex_state_t state; ///< state expression ex_bool_t bool; ///< boolean logic expression ex_block_t block; ///< statement block expression @@ -253,13 +259,21 @@ const char *new_label_name (void); /** Create a new label expression node. - The label name is set using new_label_name(), and the label is linked - into the global list of labels for later resolution. + The label name is set using new_label_name(). \return The new label expression (::ex_label_t) node. */ expr_t *new_label_expr (void); +/** Create a new label reference expression node. + + Used for taking the address of a label (eg. jump tables). + The label's \a used field is incremented. + + \return The new label reference expression (::ex_labelref_t) node. +*/ +expr_t *new_label_ref (ex_label_t *label); + /** Create a new state expression node. The label name is set using new_label_name(), and the label is linked diff --git a/tools/qfcc/source/constfold.c b/tools/qfcc/source/constfold.c index e2bd53bec..21d661734 100644 --- a/tools/qfcc/source/constfold.c +++ b/tools/qfcc/source/constfold.c @@ -1469,7 +1469,7 @@ fold_constants (expr_t *e) if (e2->type == ex_error) return e2; - if (e2->type == ex_label) + if (e2->type == ex_label || e2->type == ex_labelref) return e; t2 = extract_type (e2); diff --git a/tools/qfcc/source/def.c b/tools/qfcc/source/def.c index f5300705d..b09a2b38b 100644 --- a/tools/qfcc/source/def.c +++ b/tools/qfcc/source/def.c @@ -277,6 +277,12 @@ init_elements (struct def_s *def, expr_t *eles) } init_elements (&elements[i], c); continue; + } else if (c->type == ex_labelref) { + def_t loc; + loc.space = elements[i].space; + loc.offset = elements[i].offset; + reloc_def_op (c->e.labelref.label, &loc); + continue; } else if (c->type == ex_value) { if (c->e.value.type == ev_integer && elements[i].type->type == ev_float) diff --git a/tools/qfcc/source/dot_expr.c b/tools/qfcc/source/dot_expr.c index 0d0e859d3..52982839f 100644 --- a/tools/qfcc/source/dot_expr.c +++ b/tools/qfcc/source/dot_expr.c @@ -155,6 +155,19 @@ print_label (expr_t *e, int level, int id) e->e.label.name, e->line); } +static void +print_labelref (expr_t *e, int level, int id) +{ + int indent = level * 2 + 2; + + if (e->next) + printf ("%*s\"e_%p\" -> \"e_%p\" " + "[constraint=false,style=dashed];\n", indent, "", + e, e->next); + printf ("%*s\"e_%p\" [label=\"&%s\\n%d\"];\n", indent, "", e, + e->e.label.name, e->line); +} + static void print_block (expr_t *e, int level, int id) { @@ -364,6 +377,7 @@ _print_expr (expr_t *e, int level, int id) print_state, print_bool, print_label, + print_labelref, print_block, print_subexpr, print_uexpr, diff --git a/tools/qfcc/source/expr.c b/tools/qfcc/source/expr.c index 504c2c537..8f70e0219 100644 --- a/tools/qfcc/source/expr.c +++ b/tools/qfcc/source/expr.c @@ -140,6 +140,8 @@ get_type (expr_t *e) { convert_name (e); switch (e->type) { + case ex_labelref: + return &type_void; case ex_label: case ex_error: return 0; // something went very wrong @@ -306,6 +308,8 @@ copy_expr (expr_t *e) case ex_label: /// Create a fresh label return new_label_expr (); + case ex_labelref: + return new_label_ref (e->e.labelref.label); case ex_block: n = new_expr (); *n = *e; @@ -407,6 +411,18 @@ new_label_expr (void) return l; } +expr_t * +new_label_ref (ex_label_t *label) +{ + + expr_t *l = new_expr (); + + l->type = ex_labelref; + l->e.labelref.label = label; + label->used++; + return l; +} + expr_t * new_block_expr (void) { @@ -607,7 +623,7 @@ new_short_expr (short short_val) int is_constant (expr_t *e) { - if (e->type == ex_nil || e->type == ex_value + if (e->type == ex_nil || e->type == ex_value || e->type == ex_labelref || (e->type == ex_symbol && e->e.symbol->sy_type == sy_const) || (e->type == ex_symbol && e->e.symbol->sy_type == sy_var && e->e.symbol->s.def->constant)) @@ -624,7 +640,7 @@ constant_expr (expr_t *e) if (!is_constant (e)) return e; - if (e->type == ex_nil || e->type == ex_value) + if (e->type == ex_nil || e->type == ex_value || e->type == ex_labelref) return e; if (e->type != ex_symbol) return e; @@ -1623,6 +1639,7 @@ unary_expr (int op, expr_t *e) case ex_value: // should be handled above case ex_error: case ex_label: + case ex_labelref: case ex_state: internal_error (e, 0); case ex_uexpr: @@ -1685,6 +1702,7 @@ unary_expr (int op, expr_t *e) case ex_value: // should be handled above case ex_error: case ex_label: + case ex_labelref: case ex_state: internal_error (e, 0); case ex_bool: @@ -1742,6 +1760,7 @@ unary_expr (int op, expr_t *e) case ex_value: // should be handled above case ex_error: case ex_label: + case ex_labelref: case ex_state: internal_error (e, 0); case ex_uexpr: @@ -2217,6 +2236,8 @@ address_expr (expr_t *e1, expr_t *e2, type_t *t) return error (e1, "invalid type for unary &"); e1->e.block.result = address_expr (e1->e.block.result, e2, t); return e1; + case ex_label: + return new_label_ref (&e1->e.label); default: return error (e1, "invalid type for unary &"); } diff --git a/tools/qfcc/source/statements.c b/tools/qfcc/source/statements.c index 13e70cb07..80d9c518c 100644 --- a/tools/qfcc/source/statements.c +++ b/tools/qfcc/source/statements.c @@ -830,6 +830,7 @@ statement_subexpr (sblock_t *sblock, expr_t *e, operand_t **op) 0, // ex_state 0, // ex_bool 0, // ex_label + 0, // ex_labelref expr_block, // ex_block expr_expr, expr_uexpr, @@ -1107,6 +1108,7 @@ statement_slist (sblock_t *sblock, expr_t *e) statement_state, statement_bool, statement_label, + 0, // ex_labelref statement_block, statement_expr, statement_uexpr, diff --git a/tools/qfcc/source/switch.c b/tools/qfcc/source/switch.c index 1108f6916..c663c96f7 100644 --- a/tools/qfcc/source/switch.c +++ b/tools/qfcc/source/switch.c @@ -327,18 +327,25 @@ build_switch (expr_t *sw, case_node_t *tree, int op, expr_t *sw_val, } else { int low = expr_integer (tree->low); int high = expr_integer (tree->high); - symbol_t *sym; - expr_t *table; - const char *name = new_label_name (); + symbol_t *table_sym; + expr_t *table_expr; + expr_t *table_init; + const char *table_name = new_label_name (); int i; expr_t *range = binary_expr ('-', tree->high, tree->low); range = fold_constants (range); - sym = make_symbol (name, array_type (&type_integer, high - low + 1), - pr.near_data, st_static); - table = new_symbol_expr (sym); - table = new_alias_expr (&type_integer, table); + table_init = new_block_expr (); + for (i = 0; i <= high - low; i++) { + tree->labels[i]->e.label.used++; + append_expr (table_init, address_expr (tree->labels[i], 0, 0)); + } + table_sym = new_symbol (table_name); + initialize_def (table_sym, array_type (&type_integer, high - low + 1), + table_init, pr.near_data, st_static); + table_expr = new_symbol_expr (table_sym); + table_expr = new_alias_expr (&type_integer, table_expr); if (tree->left) { branch = branch_expr (IFB, temp, low_label); @@ -348,7 +355,7 @@ build_switch (expr_t *sw, case_node_t *tree, int op, expr_t *sw_val, cast_expr (&type_uinteger, range)); branch = branch_expr ('i', test, high_label); append_expr (sw, branch); - branch = new_binary_expr ('g', table, temp); + branch = new_binary_expr ('g', table_expr, temp); append_expr (sw, branch); if (tree->left) { append_expr (sw, low_label); @@ -358,13 +365,6 @@ build_switch (expr_t *sw, case_node_t *tree, int op, expr_t *sw_val, append_expr (sw, high_label); build_switch (sw, tree->right, op, sw_val, temp, default_label); } - for (i = 0; i <= high - low; i++) { - def_t loc; - loc.space = sym->s.def->space; - loc.offset = sym->s.def->offset + i; - tree->labels[i]->e.label.used++; - reloc_def_op (&tree->labels[i]->e.label, &loc); - } } }