diff --git a/include/QF/pr_comp.h b/include/QF/pr_comp.h index 02ba11011..f0133d1f8 100644 --- a/include/QF/pr_comp.h +++ b/include/QF/pr_comp.h @@ -211,6 +211,7 @@ typedef enum { OP_IFA, OP_JUMP, + OP_JUMPB, OP_LT_UI, OP_GT_UI, @@ -268,7 +269,7 @@ typedef struct #define PROG_ID_VERSION 6 -#define PROG_VERSION 0x00fff001 // MMmmmRRR 0.fff.001 (hex) +#define PROG_VERSION 0x00fff002 // MMmmmRRR 0.fff.002 (hex) typedef struct { diff --git a/libs/gamecode/engine/pr_edict.c b/libs/gamecode/engine/pr_edict.c index 633e3d6d8..7e8c54b75 100644 --- a/libs/gamecode/engine/pr_edict.c +++ b/libs/gamecode/engine/pr_edict.c @@ -1094,9 +1094,23 @@ PR_LoadProgsFile (progs_t * pr, const char *progsname) ((int *) pr->progs)[i] = LittleLong (((int *) pr->progs)[i]); if (pr->progs->version != PROG_VERSION - && pr->progs->version != PROG_ID_VERSION) - PR_Error (pr, "%s has unrecognised version number (%08x)", - progsname, pr->progs->version); + && pr->progs->version != PROG_ID_VERSION) { + if (pr->progs->version < 0x00fff000) { + PR_Error (pr, "%s has unrecognised version number (%d)", + progsname, pr->progs->version); + } else { + PR_Error (pr, + "%s has unrecognised version number (%02x.%03x.%03x)" + " [%02x.%03x.%03x expected]", + progsname, + pr->progs->version >> 24, + (pr->progs->version >> 12) & 0xfff, + pr->progs->version & 0xfff, + PROG_VERSION >> 24, + (PROG_VERSION >> 12) & 0xfff, + PROG_VERSION & 0xfff); + } + } pr->progs_name = progsname; //XXX is this safe? diff --git a/libs/gamecode/engine/pr_exec.c b/libs/gamecode/engine/pr_exec.c index d385a98d7..8a231dd8c 100644 --- a/libs/gamecode/engine/pr_exec.c +++ b/libs/gamecode/engine/pr_exec.c @@ -623,6 +623,19 @@ PR_ExecuteProgram (progs_t * pr, func_t fnum) } st = &pr->pr_statements[OPA.uinteger_var]; break; + case OP_JUMPB: + //FIXME put bounds checking in + pointer = OPA.integer_var + OPB.integer_var; + ptr = pr->pr_globals + pointer; + pointer = ptr->integer_var; + if (pr_boundscheck->int_val + && (pointer >= pr->progs->numstatements)) { + pr->pr_xstatement = st - pr->pr_statements; + PR_RunError (pr, "Invalid jump destination\n"); + return; + } + st = &pr->pr_statements[pointer]; + break; case OP_CALL0: case OP_CALL1: diff --git a/libs/gamecode/engine/pr_opcode.c b/libs/gamecode/engine/pr_opcode.c index e7e086e1f..d5b7f9c71 100644 --- a/libs/gamecode/engine/pr_opcode.c +++ b/libs/gamecode/engine/pr_opcode.c @@ -166,6 +166,7 @@ opcode_t pr_opcodes[] = { {"", "goto", OP_GOTO, false, ev_integer, ev_void, ev_void, PROG_ID_VERSION}, {"", "jump", OP_JUMP, false, ev_integer, ev_void, ev_void, PROG_VERSION}, + {"", "jumpb", OP_JUMPB, false, ev_pointer, ev_integer, ev_void, PROG_VERSION}, {"&&", "and", OP_AND, false, ev_float, ev_float, ev_integer, PROG_ID_VERSION}, {"||", "or", OP_OR, false, ev_float, ev_float, ev_integer, PROG_ID_VERSION}, diff --git a/tools/qfcc/include/expr.h b/tools/qfcc/include/expr.h index b8e9d2961..92c75f2e0 100644 --- a/tools/qfcc/include/expr.h +++ b/tools/qfcc/include/expr.h @@ -16,12 +16,13 @@ typedef enum { ex_pointer, ex_quaternion, ex_integer, + ex_uinteger, } expr_type; typedef struct { statref_t *refs; dstatement_t *statement; - char *name; + const char *name; } elabel_t; typedef struct { @@ -56,7 +57,7 @@ typedef struct expr_s { def_t *def; temp_t temp; - char *string_val; + const char *string_val; float float_val; float vector_val[3]; int entity_val; @@ -65,6 +66,7 @@ typedef struct expr_s { int pointer_val; float quaternion_val[4]; int integer_val; + int uinteger_val; } e; } expr_t; @@ -76,6 +78,7 @@ type_t *get_type (expr_t *e); etype_t extract_type (expr_t *e); expr_t *new_expr (void); +const char *new_label_name (void); expr_t *new_label_expr (void); expr_t *new_block_expr (void); expr_t *new_binary_expr (int op, expr_t *e1, expr_t *e2); diff --git a/tools/qfcc/include/qfcc.h b/tools/qfcc/include/qfcc.h index c3cea8db7..47d162500 100644 --- a/tools/qfcc/include/qfcc.h +++ b/tools/qfcc/include/qfcc.h @@ -342,6 +342,7 @@ extern type_t type_pointer; extern type_t type_floatfield; extern type_t type_quaternion; extern type_t type_integer; +extern type_t type_uinteger; extern def_t def_void; extern def_t def_string; @@ -353,6 +354,7 @@ extern def_t def_function; extern def_t def_pointer; extern def_t def_quaternion; extern def_t def_integer; +extern def_t def_uinteger; struct function_s { @@ -397,6 +399,8 @@ extern opcode_t *op_ifae; extern opcode_t *op_ifa; extern opcode_t *op_state; extern opcode_t *op_goto; +extern opcode_t *op_jump; +extern opcode_t *op_jumpb; statref_t *PR_NewStatref (dstatement_t *st, int field); void PR_AddStatementRef (def_t *def, dstatement_t *st, int field); @@ -466,6 +470,9 @@ extern def_t *pr_scope; extern int pr_error_count; void PR_NewLine (void); +def_t *PR_GetArray (type_t *etype, const char *name, int size, def_t *scope, + int *allocate); + def_t *PR_GetDef (type_t *type, const char *name, def_t *scope, int *allocate); def_t *PR_NewDef (type_t *type, const char *name, def_t *scope); diff --git a/tools/qfcc/source/emit.c b/tools/qfcc/source/emit.c index ef3fd737a..40740746d 100644 --- a/tools/qfcc/source/emit.c +++ b/tools/qfcc/source/emit.c @@ -288,6 +288,7 @@ emit_sub_expr (expr_t *e, def_t *dest) case ex_pointer: case ex_quaternion: case ex_integer: + case ex_uinteger: d = PR_ReuseConstant (e, 0); break; } @@ -319,6 +320,9 @@ emit_expr (expr_t *e) case 2: ref->statement->c = label->statement - ref->statement; break; + case 3: + *(int*)ref->statement = label->statement - statements; + break; default: abort(); } @@ -366,6 +370,11 @@ emit_expr (expr_t *e) } e->e.expr.e2->e.temp.def = emit_sub_expr (e->e.expr.e1, e->e.expr.e2->e.temp.def); break; + case 'g': + def_a = emit_sub_expr (e->e.expr.e1, 0); + def_b = emit_sub_expr (e->e.expr.e2, 0); + emit_statement (e->line, op_jumpb, def_a, def_b, 0); + break; default: warning (e, "Ignoring useless expression"); break; @@ -399,6 +408,7 @@ emit_expr (expr_t *e) case ex_pointer: case ex_quaternion: case ex_integer: + case ex_uinteger: warning (e, "Ignoring useless expression"); break; case ex_nil: diff --git a/tools/qfcc/source/expr.c b/tools/qfcc/source/expr.c index 16e126204..6fda6beaa 100644 --- a/tools/qfcc/source/expr.c +++ b/tools/qfcc/source/expr.c @@ -61,6 +61,7 @@ etype_t qc_types[] = { ev_pointer, // ex_pointer ev_quaternion, // ex_quaternion ev_integer, // ex_integer + ev_uinteger, // ex_uinteger }; type_t *types[] = { @@ -74,6 +75,7 @@ type_t *types[] = { &type_pointer, &type_quaternion, &type_integer, + &type_uinteger, }; expr_type expr_types[] = { @@ -87,6 +89,7 @@ expr_type expr_types[] = { ex_pointer, // ev_pointer ex_quaternion, // ev_quaternion ex_integer, // ev_integer + ex_uinteger, // ev_uinteger }; type_t * @@ -122,6 +125,7 @@ get_type (expr_t *e) case ex_func: case ex_pointer: case ex_quaternion: + case ex_uinteger: return types[qc_types[e->type]]; } return 0; @@ -287,20 +291,27 @@ num_digits (int val) return num; } -expr_t * -new_label_expr (void) +const char * +new_label_name (void) { static int label = 0; int lnum = ++label; const char *fname = current_func->def->name; int len = 1 + strlen (fname) + 1 + num_digits (lnum) + 1; + char *lname = malloc (len); + if (!lname) + Sys_Error ("new_label_expr: Memory Allocation Failure\n"); + sprintf (lname, "$%s_%d", fname, lnum); + return lname; +} + +expr_t * +new_label_expr (void) +{ expr_t *l = new_expr (); l->type = ex_label; - l->e.label.name = malloc (len); - if (!l->e.label.name) - Sys_Error ("new_label_expr: Memory Allocation Failure\n"); - sprintf (l->e.label.name, "$%s_%d", fname, lnum); + l->e.label.name = new_label_name (); return l; } @@ -482,7 +493,7 @@ do_op_string (int op, expr_t *e1, expr_t *e2) { int len; char *buf; - char *s1, *s2; + const char *s1, *s2; s1 = e1->e.string_val ? e1->e.string_val : ""; s2 = e2->e.string_val ? e2->e.string_val : ""; diff --git a/tools/qfcc/source/pr_def.c b/tools/qfcc/source/pr_def.c index 6de0e63df..92d77e2d6 100644 --- a/tools/qfcc/source/pr_def.c +++ b/tools/qfcc/source/pr_def.c @@ -52,17 +52,10 @@ defs_get_key (void *_def, void *_tab) return ""; } -/* - PR_GetDef - - If type is NULL, it will match any type - If allocate is true, a new def will be allocated if it can't be found -*/ -def_t * -PR_GetDef (type_t *type, const char *name, def_t *scope, int *allocate) +static def_t * +check_for_name (type_t *type, const char *name, def_t *scope, int *allocate) { def_t *def; - char element[MAX_NAME]; if (!defs_by_name) { defs_by_name = Hash_NewTable (16381, defs_get_key, 0, &defs_by_name); @@ -77,9 +70,49 @@ PR_GetDef (type_t *type, const char *name, def_t *scope, int *allocate) if (!allocate || def->scope == scope) return def; } + return 0; +} - if (!allocate) - return NULL; +static inline type_t * +find_type (type_t *type, type_t *aux_type) +{ + type_t new; + memset (&new, 0, sizeof (new)); + new.type = type->type; + new.aux_type = aux_type; + return PR_FindType (&new); +} + +def_t * +PR_GetArray (type_t *etype, const char *name, int size, def_t *scope, + int *allocate) +{ + type_t *type = find_type (&type_pointer, etype); + def_t *def = check_for_name (type, name, scope, allocate); + if (def || !allocate) + return def; + def = PR_NewDef (type, name, scope); + def->ofs = *allocate; + *allocate += pr_type_size [type->type] * size + 1; + pr_global_defs[def->ofs] = def; + G_INT (def->ofs) = def->ofs + 1; + return def; +} + +/* + PR_GetDef + + If type is NULL, it will match any type + If allocate is true, a new def will be allocated if it can't be found +*/ +def_t * +PR_GetDef (type_t *type, const char *name, def_t *scope, int *allocate) +{ + def_t *def = check_for_name (type, name, scope, allocate); + char element[MAX_NAME]; + + if (def || !allocate) + return def; // allocate a new def def = PR_NewDef (type, name, scope); diff --git a/tools/qfcc/source/pr_imm.c b/tools/qfcc/source/pr_imm.c index 8f78a26d6..22c4b2019 100644 --- a/tools/qfcc/source/pr_imm.c +++ b/tools/qfcc/source/pr_imm.c @@ -87,7 +87,8 @@ def_t * PR_ReuseConstant (expr_t *expr, def_t *def) { def_t *cn = 0; - char rep[60], *r = rep; + char rep[60]; + const char *r = rep; hashtab_t *tab = 0; type_t *type; expr_t e = *expr; @@ -131,13 +132,17 @@ PR_ReuseConstant (expr_t *expr, def_t *def) type = &type_pointer; break; case ex_integer: + case ex_uinteger: if (!def || def->type != &type_float) { sprintf (rep, "\001integer:%08X\001", e.e.integer_val); tab = integer_imm_defs; type = &type_integer; break; } - e.e.float_val = e.e.integer_val; + if (e.type == ex_uinteger) + e.e.float_val = e.e.uinteger_val; + else + e.e.float_val = e.e.integer_val; case ex_float: sprintf (rep, "\001float:%08X\001", e.e.integer_val); tab = float_imm_defs; diff --git a/tools/qfcc/source/pr_lex.c b/tools/qfcc/source/pr_lex.c index a7724d692..bdc8827b1 100644 --- a/tools/qfcc/source/pr_lex.c +++ b/tools/qfcc/source/pr_lex.c @@ -65,6 +65,7 @@ type_t type_function = { ev_func, &def_function, NULL, &type_void }; type_t type_pointer = { ev_pointer, &def_pointer }; type_t type_quaternion = { ev_quaternion, &def_quaternion }; type_t type_integer = { ev_integer, &def_integer }; +type_t type_uinteger = { ev_uinteger, &def_uinteger }; type_t type_floatfield = { ev_field, &def_field, NULL, &type_float }; @@ -78,6 +79,7 @@ def_t def_function = { &type_function, "temp" }; def_t def_pointer = { &type_pointer, "temp" }; def_t def_quaternion = { &type_quaternion, "temp"}; def_t def_integer = { &type_integer, "temp"}; +def_t def_uinteger = { &type_uinteger, "temp"}; def_t def_ret, def_parms[MAX_PARMS]; diff --git a/tools/qfcc/source/pr_opcode.c b/tools/qfcc/source/pr_opcode.c index 2d8023e62..09f7c3db7 100644 --- a/tools/qfcc/source/pr_opcode.c +++ b/tools/qfcc/source/pr_opcode.c @@ -40,6 +40,8 @@ opcode_t *op_ifae; opcode_t *op_ifa; opcode_t *op_state; opcode_t *op_goto; +opcode_t *op_jump; +opcode_t *op_jumpb; statref_t * PR_NewStatref (dstatement_t *st, int field) @@ -167,6 +169,10 @@ PR_Opcode_Init_Tables (void) op_state = op; } else if (!strcmp (op->name, "")) { op_goto = op; + } else if (!strcmp (op->name, "")) { + op_jump = op; + } else if (!strcmp (op->name, "")) { + op_jumpb = op; } } } diff --git a/tools/qfcc/source/switch.c b/tools/qfcc/source/switch.c index 82bf5d25d..7332ba15f 100644 --- a/tools/qfcc/source/switch.c +++ b/tools/qfcc/source/switch.c @@ -38,6 +38,14 @@ static const char rcsid[] = #include "scope.h" #include "qc-parse.h" +typedef struct case_node_s { + expr_t *low; + expr_t *high; + expr_t **labels; + expr_t *_label; + struct case_node_s *left, *right; +} case_node_t; + static unsigned long get_hash (void *_cl, void *unused) { @@ -80,7 +88,6 @@ case_label_expr (switch_block_t *switch_block, expr_t *value) return 0; } cl->value = value; - print_expr(value);puts(""); if (Hash_FindElement (switch_block->labels, cl)) { error (value, "duplicate %s", value ? "case" : "default"); free (cl); @@ -128,18 +135,118 @@ label_compare (const void *_a, const void *_b) } } -static void -build_binary_jump_table (expr_t *sw, case_label_t **labels, int op, int base, int count, expr_t *sw_val, expr_t *temp, expr_t *default_label) +static case_node_t * +new_case_node (expr_t *low, expr_t *high) +{ + case_node_t *node = malloc (sizeof (case_node_t)); + + if (!node) + Sys_Error ("out of memory"); + node->low = low; + node->high = high; + if (low == high) { + node->labels = &node->_label; + node->_label = 0; + } else { + int size; + if (low->type != ex_integer) { + error (low, "switch: internal error"); + abort (); + } + size = high->e.integer_val - low->e.integer_val + 1; + node->labels = calloc (size, sizeof (case_node_t *)); + } + node->left = node->right = 0; + return node; +} + +static case_node_t * +balance_case_tree (case_node_t **nodes, int base, int count) +{ + case_node_t *node; + int index = count / 2; + + if (!count) + return 0; + + node = nodes[base + index]; + + node->left = balance_case_tree (nodes, base, index); + + base += index + 1; + count -= index + 1; + node->right = balance_case_tree (nodes, base, count); + + return node; +} + +static case_node_t * +build_case_tree (case_label_t **labels, int count) +{ + case_node_t **nodes; + int i, j, k; + int num_nodes = 0; + + qsort (labels, count, sizeof (*labels), label_compare); + + nodes = (case_node_t **) malloc (count * sizeof (case_node_t*)); + if (!nodes) + Sys_Error ("out of memory"); + + if (labels[0]->value->type == ex_integer) { + for (i = 0; i < count - 1; i = j, num_nodes++) { + for (j = i + 1; j < count; j++) { + if (labels[j]->value->e.integer_val + - labels[j - 1]->value->e.integer_val > 1) + break; + } + nodes[num_nodes] = new_case_node (labels[i]->value, + labels[j - 1]->value); + for (k = i; k < j; k++) + nodes[num_nodes]->labels[labels[k]->value->e.integer_val + - labels[i]->value->e.integer_val] + = labels[k]->label; + } + if (i < count) { + nodes[num_nodes] = new_case_node (labels[i]->value, + labels[i]->value); + nodes[num_nodes]->labels[0] = labels[i]->label; + num_nodes++; + } + } else { + for (i = 0; i < count; i++, num_nodes++) { + nodes[num_nodes] = new_case_node (labels[i]->value, + labels[i]->value); + nodes[num_nodes]->labels[0] = labels[i]->label; + } + } + return balance_case_tree (nodes, 0, num_nodes); +} + +static void +build_switch (expr_t *sw, case_node_t *tree, int op, expr_t *sw_val, + expr_t *temp, expr_t *default_label) { - case_label_t *l; expr_t *test; expr_t *branch; - int index; + expr_t *high_label = default_label; + expr_t *low_label = default_label; - index = count / 2; - l = labels [base + index]; + if (!tree) + return; - test = binary_expr (op, l->value, sw_val); + if (tree->right) { + high_label = new_label_expr (); + high_label->line = sw_val->line; + high_label->file = sw_val->file; + } + if (tree->left) { + low_label = new_label_expr (); + low_label->line = sw_val->line; + low_label->file = sw_val->file; + } + + test = binary_expr (op, sw_val, tree->low); test->line = sw_val->line; test->file = sw_val->file; @@ -147,37 +254,81 @@ build_binary_jump_table (expr_t *sw, case_label_t **labels, int op, int base, in test->line = sw_val->line; test->file = sw_val->file; - branch = new_binary_expr ('n', test, l->label); - branch->line = sw_val->line; - branch->file = sw_val->file; - append_expr (sw, branch); - - if (index) { - expr_t *high_label; - - if (count - (index + 1)) { - high_label = new_label_expr (); - high_label->line = sw_val->line; - high_label->file = sw_val->file; - } else { - high_label = default_label; - } - branch = new_binary_expr (IFA, temp, high_label); + if (tree->low == tree->high) { + printf ("%3d\n", tree->low->e.integer_val); + branch = new_binary_expr ('n', test, tree->labels[0]); branch->line = sw_val->line; branch->file = sw_val->file; append_expr (sw, branch); - build_binary_jump_table (sw, labels, op, base, index, sw_val, temp, default_label); + if (tree->left) { + branch = new_binary_expr (IFA, temp, high_label); + branch->line = sw_val->line; + branch->file = sw_val->file; + append_expr (sw, branch); - if (count - (index + 1)) + build_switch (sw, tree->left, op, sw_val, temp, default_label); + + if (tree->right) + append_expr (sw, high_label); + } + if (tree->right) + build_switch (sw, tree->right, op, sw_val, temp, default_label); + } else { + expr_t *utemp = new_temp_def_expr (&type_uinteger); + int low = tree->low->e.integer_val; + int high = tree->high->e.integer_val; + def_t *def; + expr_t *table = new_expr (); + const char *name = new_label_name (); + int i; + expr_t *range = binary_expr ('-', tree->high, tree->low); + + printf ("%3d %3d\n", low, high); + + def = PR_GetArray (&type_uinteger, name, high - low + 1, 0, + &numpr_globals); + table->type = ex_def; + table->e.def = def; + + append_expr (sw, test); + + if (tree->left) { + branch = new_binary_expr (IFB, temp, low_label); + branch->line = sw_val->line; + branch->file = sw_val->file; + append_expr (sw, branch); + } + tree->high->type = ex_uinteger; + append_expr (sw, new_binary_expr ('b', temp, utemp)); + test = binary_expr (GT, utemp, range); + test->line = sw_val->line; + test->file = sw_val->file; + branch = new_binary_expr ('i', test, high_label); + branch->line = sw_val->line; + branch->file = sw_val->file; + append_expr (sw, branch); + branch = new_binary_expr ('g', table, temp); + branch->line = sw_val->line; + branch->file = sw_val->file; + append_expr (sw, branch); + if (tree->left) { + append_expr (sw, low_label); + build_switch (sw, tree->left, op, sw_val, temp, default_label); + } + if (tree->right) { append_expr (sw, high_label); + build_switch (sw, tree->right, op, sw_val, temp, default_label); + } + for (i = 0; i <= high - low; i++) { + dstatement_t *st; + statref_t *ref; + st = (dstatement_t *) &pr_globals[G_INT (def->ofs) + i]; + ref = PR_NewStatref (st, 3); + ref->next = tree->labels[i]->e.label.refs; + tree->labels[i]->e.label.refs = ref; + } } - - base += index + 1; - count -= index + 1; - - if (count) - build_binary_jump_table (sw, labels, op, base, count, sw_val, temp, default_label); } struct expr_s * @@ -229,12 +380,13 @@ switch_expr (switch_block_t *switch_block, expr_t *break_label, } else { expr_t *temp; int op; + case_node_t *case_tree; if (type == &type_string) temp = new_temp_def_expr (&type_integer); else temp = new_temp_def_expr (type); - qsort (labels, num_labels, sizeof (*labels), label_compare); + case_tree = build_case_tree (labels, num_labels); switch (type->type) { case ev_string: op = NE; @@ -249,7 +401,7 @@ switch_expr (switch_block_t *switch_block, expr_t *break_label, error (0, "internal compiler error in switch"); abort (); } - build_binary_jump_table (sw, labels, op, 0, num_labels, sw_val, temp, default_label->label); + build_switch (sw, case_tree, op, sw_val, temp, default_label->label); } append_expr (sw, default_expr); append_expr (sw, statements);