From 945006494f0dfaf202dac20432bc432dd577df86 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Wed, 22 Oct 2003 08:05:17 +0000 Subject: [PATCH] short circuit logic for && and || (as per C). forces standard precedence (ie, breaks --traditional) when enabled (no choice at the moment, next fix) --- tools/qfcc/include/expr.h | 17 +++ tools/qfcc/source/emit.c | 125 ++++++++++++++++++++- tools/qfcc/source/expr.c | 207 ++++++++++++++++++++++++++++++----- tools/qfcc/source/qc-parse.y | 111 ++++++++++++------- 4 files changed, 394 insertions(+), 66 deletions(-) diff --git a/tools/qfcc/include/expr.h b/tools/qfcc/include/expr.h index 00b51a25d..43c9905b4 100644 --- a/tools/qfcc/include/expr.h +++ b/tools/qfcc/include/expr.h @@ -34,6 +34,7 @@ typedef enum { ex_error, + ex_bool, ex_label, ex_block, ex_expr, // binary expression @@ -83,6 +84,17 @@ typedef struct { struct def_s *def; } ex_pointer_t; +typedef struct { + int size; + struct expr_s *e[1]; +} ex_list_t; + +typedef struct { + ex_list_t *true_list; + ex_list_t *false_list; + struct expr_s *e; +} ex_bool_t; + #define POINTER_VAL(p) (((p).def ? (p).def->ofs : 0) + (p).val) typedef struct expr_s { @@ -94,6 +106,7 @@ typedef struct expr_s { unsigned rvalue:1; union { ex_label_t label; + ex_bool_t bool; ex_block_t block; struct { int op; @@ -132,6 +145,7 @@ expr_t *new_expr (void); const char *new_label_name (void); expr_t *new_label_expr (void); +expr_t *new_bool_expr (ex_list_t *true_list, ex_list_t *false_list, expr_t *e); expr_t *new_block_expr (void); expr_t *new_binary_expr (int op, expr_t *e1, expr_t *e2); expr_t *new_unary_expr (int op, expr_t *e1); @@ -176,6 +190,9 @@ void convert_short_int (expr_t *e); void convert_short_uint (expr_t *e); expr_t *test_expr (expr_t *e, int test); +void backpatch (ex_list_t *list, expr_t *label); +expr_t *convert_bool (expr_t *e, int block); +expr_t *bool_expr (int op, expr_t *label, expr_t *e1, expr_t *e2); expr_t *binary_expr (int op, expr_t *e1, expr_t *e2); expr_t *asx_expr (int op, expr_t *e1, expr_t *e2); expr_t *unary_expr (int op, expr_t *e); diff --git a/tools/qfcc/source/emit.c b/tools/qfcc/source/emit.c index a9b72ddb0..898d3e6c4 100644 --- a/tools/qfcc/source/emit.c +++ b/tools/qfcc/source/emit.c @@ -434,6 +434,125 @@ emit_deref_expr (expr_t *e, def_t *dest) return d; } +static void +build_bool_block (expr_t *block, expr_t *e) +{ + switch (e->type) { + case ex_bool: + build_bool_block (block, e->e.bool.e); + return; + case ex_label: + e->next = 0; + append_expr (block, e); + return; + case ex_expr: + if (e->e.expr.op == OR || e->e.expr.op == AND) { + build_bool_block (block, e->e.expr.e1); + build_bool_block (block, e->e.expr.e2); + } else if (e->e.expr.op == 'i') { + e->next = 0; + append_expr (block, e); + } else if (e->e.expr.op == 'n') { + e->next = 0; + append_expr (block, e); + } + return; + case ex_uexpr: + if (e->e.expr.op == 'g') { + e->next = 0; + append_expr (block, e); + return; + } + break; + case ex_block: + if (!e->e.block.result) { + expr_t *t; + for (e = e->e.block.head; e; e = t) { + t = e->next; + build_bool_block (block, e); + } + return; + } + break; + default: + ; + } + error (e, "internal error"); + abort (); +} + +static int +is_goto (expr_t *e) +{ + return e && e->type == ex_uexpr && e->e.expr.op == 'g'; +} + +static int +is_if (expr_t *e) +{ + return e && e->type == ex_expr && e->e.expr.op == 'i'; +} + +static int +is_ifnot (expr_t *e) +{ + return e && e->type == ex_expr && e->e.expr.op == 'n'; +} + +static void +emit_bool_expr (expr_t *e) +{ + expr_t *block = new_block_expr (); + expr_t **s; + expr_t *l; + + build_bool_block (block, e); + + s = &block->e.block.head; + while (*s) { + if (is_if (*s) && is_goto ((*s)->next)) { + l = (*s)->e.expr.e2; + for (e = (*s)->next->next; e && e->type == ex_label; e = e->next) { + if (e == l) { + e = *s; + e->e.expr.op = 'n'; + e->e.expr.e2 = e->next->e.expr.e1; + e->next = e->next->next; + break; + } + } + s = &(*s)->next; + } else if (is_ifnot (*s) && is_goto ((*s)->next)) { + l = (*s)->e.expr.e2; + for (e = (*s)->next->next; e && e->type == ex_label; e = e->next) { + if (e == l) { + e = *s; + e->e.expr.op = 'i'; + e->e.expr.e2 = e->next->e.expr.e1; + e->next = e->next->next; + break; + } + } + s = &(*s)->next; + } else if (is_goto (*s)) { + l = (*s)->e.expr.e1; + for (e = (*s)->next; e && e->type == ex_label; e = e->next) { + if (e == l) { + *s = (*s)->next; + l = 0; + break; + } + } + if (l) + s = &(*s)->next; + } else { + s = &(*s)->next; + } + } + + emit_expr (block); +} + def_t * emit_sub_expr (expr_t *e, def_t *dest) { @@ -449,8 +568,9 @@ emit_sub_expr (expr_t *e, def_t *dest) for (e = e->e.block.head; e; e = e->next) emit_expr (e); d = emit_sub_expr (res, dest); - break; } + break; + case ex_bool: case ex_name: case ex_nil: case ex_label: @@ -637,6 +757,9 @@ emit_expr (expr_t *e) label->ofs = pr.code->size; relocate_refs (label->refs, label->ofs); break; + case ex_bool: + emit_bool_expr (e); + break; case ex_block: for (e = e->e.block.head; e; e = e->next) emit_expr (e); diff --git a/tools/qfcc/source/expr.c b/tools/qfcc/source/expr.c index 358c7b6c0..23f4e6b63 100644 --- a/tools/qfcc/source/expr.c +++ b/tools/qfcc/source/expr.c @@ -69,6 +69,7 @@ int lineno_base; etype_t qc_types[] = { ev_void, // ex_error ev_void, // ex_label + ev_void, // ex_bool ev_void, // ex_block ev_void, // ex_expr ev_void, // ex_uexpr @@ -179,6 +180,10 @@ get_type (expr_t *e) case ex_name: case ex_error: return 0; // something went very wrong + case ex_bool: + if (options.code.progsversion == PROG_ID_VERSION) + return &type_float; + return &type_integer; case ex_nil: return &type_void; case ex_block: @@ -352,6 +357,18 @@ new_label_expr (void) return l; } +expr_t * +new_bool_expr (ex_list_t *true_list, ex_list_t *false_list, expr_t *e) +{ + expr_t *b = new_expr (); + + b->type = ex_bool; + b->e.bool.true_list = true_list; + b->e.bool.false_list = false_list; + b->e.bool.e = e; + return b; +} + expr_t * new_block_expr (void) { @@ -654,6 +671,9 @@ print_expr (expr_t *e) case ex_error: printf ("(error)"); break; + case ex_bool: + printf ("bool"); //FIXME + break; case ex_label: printf ("%s", e->e.label.name); break; @@ -1441,6 +1461,113 @@ test_expr (expr_t *e, int test) return new; } +void +backpatch (ex_list_t *list, expr_t *label) +{ + int i; + expr_t *e; + + for (i = 0; i < list->size; i++) { + e = list->e[i]; + if (e->type == ex_uexpr && e->e.expr.op == 'g') + e->e.expr.e1 = label; + else if (e->type == ex_expr && (e->e.expr.op == 'i' + || e->e.expr.op == 'n')) + e->e.expr.e2 = label; + else { + error (e, "internal compiler error"); + abort (); + } + } +} + +static ex_list_t * +merge (ex_list_t *l1, ex_list_t *l2) +{ + ex_list_t *m; + + m = malloc ((size_t)&((ex_list_t *)0)->e[l1->size + l2->size]); + m->size = l1->size + l2->size; + memcpy (m->e, l1->e, l1->size * sizeof (expr_t *)); + memcpy (m->e + l1->size, l2->e, l2->size * sizeof (expr_t *)); + return m; +} + +static ex_list_t * +make_list (expr_t *e) +{ + ex_list_t *m; + + m = malloc ((size_t)&((ex_list_t *) 0)->e[1]); + m->size = 1; + m->e[0] = e; + return m; +} + +expr_t * +convert_bool (expr_t *e, int block) +{ + expr_t *b; + + if (e->type == ex_uexpr && e->e.expr.op == '!') { + e = convert_bool (e->e.expr.e1, 0); + e = unary_expr ('!', e); + } + if (e->type != ex_bool) { + e = test_expr (e, 1); + if (e->type == ex_integer) { + e = new_unary_expr ('g', 0); + if (e->e.integer_val) + e = new_bool_expr (make_list (e), 0, e); + else + e = new_bool_expr (0, make_list (e), e); + } else { + b = new_block_expr (); + append_expr (b, new_binary_expr ('i', e, 0)); + append_expr (b, new_unary_expr ('g', 0)); + e = new_bool_expr (make_list (b->e.block.head), + make_list (b->e.block.head->next), b); + } + } + if (block && e->e.bool.e->type != ex_block) { + expr_t *block = new_block_expr (); + append_expr (block, e->e.bool.e); + e->e.bool.e = block; + } + return e; +} + +expr_t * +bool_expr (int op, expr_t *label, expr_t *e1, expr_t *e2) +{ + expr_t *block; + + e1 = convert_bool (e1, 0); + e2 = convert_bool (e2, 0); + + block = new_block_expr (); + append_expr (block, e1); + append_expr (block, label); + append_expr (block, e2); + + switch (op) { + case OR: + backpatch (e1->e.bool.false_list, label); + return new_bool_expr (merge (e1->e.bool.true_list, + e2->e.bool.true_list), + e2->e.bool.false_list, block); + break; + case AND: + backpatch (e1->e.bool.true_list, label); + return new_bool_expr (e2->e.bool.true_list, + merge (e1->e.bool.false_list, + e2->e.bool.false_list), block); + break; + } + error (e1, "internal error"); + abort (); +} + void convert_int (expr_t *e) { @@ -1607,6 +1734,12 @@ binary_expr (int op, expr_t *e1, expr_t *e2) } } + if (e1->type == ex_bool) + e1 = conditional_expr (e1, new_integer_expr (1), new_integer_expr (0)); + + if (e2->type == ex_bool) + e2 = conditional_expr (e2, new_integer_expr (1), new_integer_expr (0)); + if (e1->type == ex_short) { if (t2 == &type_integer) { convert_short_int (e1); @@ -1798,6 +1931,7 @@ unary_expr (int op, expr_t *e) if (!e->e.block.result) return error (e, "invalid type for unary -"); case ex_expr: + case ex_bool: case ex_def: case ex_temp: { @@ -1844,6 +1978,9 @@ unary_expr (int op, expr_t *e) case ex_name: error (e, "internal error"); abort (); + case ex_bool: + return new_bool_expr (e->e.bool.false_list, + e->e.bool.true_list, e); case ex_block: if (!e->e.block.result) return error (e, "invalid type for unary !"); @@ -1914,6 +2051,7 @@ unary_expr (int op, expr_t *e) return error (e, "invalid type for unary ~"); goto bitnot_expr; case ex_expr: + case ex_bool: case ex_def: case ex_temp: bitnot_expr: @@ -2131,6 +2269,8 @@ function_expr (expr_t *e1, expr_t *e2) expr_t * return_expr (function_t *f, expr_t *e) { + type_t *t = get_type (e); + if (!e) { if (f->def->type->aux_type != &type_void) { if (options.traditional) { @@ -2141,33 +2281,41 @@ return_expr (function_t *f, expr_t *e) return e; } } + return new_unary_expr ('r', 0); } - if (e) { - type_t *t = get_type (e); - if (e->type == ex_error) - return e; - if (f->def->type->aux_type == &type_void) { - if (!options.traditional) - return error (e, "returning a value for a void function"); - warning (e, "returning a value for a void function"); - } - if (f->def->type->aux_type == &type_float && e->type == ex_integer) { - e->type = ex_float; - e->e.float_val = e->e.integer_val; - t = &type_float; - } - if (t == &type_void) { - t = f->def->type->aux_type; - e->type = expr_types[t->type]; - } - if (!type_assignable (f->def->type->aux_type, t)) { - if (!options.traditional) - return error (e, "type mismatch for return value of %s", - f->def->name); - warning (e, "type mismatch for return value of %s", - f->def->name); - } + if (e->type == ex_error) + return e; + if (f->def->type->aux_type == &type_void) { + if (!options.traditional) + return error (e, "returning a value for a void function"); + warning (e, "returning a value for a void function"); + } + if (e->type == ex_bool) { + if (f->def->type->aux_type == &type_float) + e = conditional_expr (e, new_float_expr (1), new_float_expr (0)); + else if (f->def->type->aux_type == &type_integer) + e = conditional_expr (e, new_integer_expr (1), + new_integer_expr (0)); + else if (f->def->type->aux_type == &type_uinteger) + e = conditional_expr (e, new_uinteger_expr (1), + new_uinteger_expr (0)); + } + if (f->def->type->aux_type == &type_float && e->type == ex_integer) { + e->type = ex_float; + e->e.float_val = e->e.integer_val; + t = &type_float; + } + if (t == &type_void) { + t = f->def->type->aux_type; + e->type = expr_types[t->type]; + } + if (!type_assignable (f->def->type->aux_type, t)) { + if (!options.traditional) + return error (e, "type mismatch for return value of %s", + f->def->name); + warning (e, "type mismatch for return value of %s", + f->def->name); } return new_unary_expr ('r', e); } @@ -2179,6 +2327,7 @@ conditional_expr (expr_t *cond, expr_t *e1, expr_t *e2) type_t *type1 = get_type (e1); type_t *type2 = get_type (e2); expr_t *tlabel = new_label_expr (); + expr_t *flabel = new_label_expr (); expr_t *elabel = new_label_expr (); if (cond->type == ex_error) @@ -2188,8 +2337,14 @@ conditional_expr (expr_t *cond, expr_t *e1, expr_t *e2) if (e2->type == ex_error) return e2; + cond = convert_bool (cond, 1); + + backpatch (cond->e.bool.true_list, tlabel); + backpatch (cond->e.bool.false_list, flabel); + block->e.block.result = (type1 == type2) ? new_temp_def_expr (type1) : 0; - append_expr (block, new_binary_expr ('i', test_expr (cond, 1), tlabel)); + append_expr (block, cond); + append_expr (cond->e.bool.e, flabel); if (block->e.block.result) append_expr (block, assign_expr (block->e.block.result, e2)); else diff --git a/tools/qfcc/source/qc-parse.y b/tools/qfcc/source/qc-parse.y index cbbbe1b03..04ec87d4f 100644 --- a/tools/qfcc/source/qc-parse.y +++ b/tools/qfcc/source/qc-parse.y @@ -161,7 +161,7 @@ expr_t *argv_expr (void); %type const opt_expr expr element_list element_list1 element %type string_val opt_state_expr array_decl %type statement statements statement_block -%type break_label continue_label enum_list enum +%type label break_label continue_label enum_list enum %type unary_expr primary cast_expr opt_arg_list arg_list %type begin_function %type save_inits @@ -654,25 +654,23 @@ statement { expr_t *l1 = new_label_expr (); expr_t *l2 = break_label; - expr_t *e; restore_local_inits ($7); free_local_inits ($7); $$ = new_block_expr (); - e = new_binary_expr ('n', test_expr ($5, 1), l2); - e->line = $5->line; - e->file = $5->file; - append_expr ($$, e); + append_expr ($$, new_unary_expr ('g', continue_label)); append_expr ($$, l1); append_expr ($$, $8); append_expr ($$, continue_label); - e = new_binary_expr ('i', test_expr ($5, 1), l1); - e->line = $5->line; - e->file = $5->file; - append_expr ($$, e); - append_expr ($$, l2); + + $5 = convert_bool ($5, 1); + backpatch ($5->e.bool.true_list, l1); + backpatch ($5->e.bool.false_list, l2); + append_expr ($5->e.bool.e, l2); + append_expr ($$, $5); + break_label = $2; continue_label = $3; } @@ -685,8 +683,13 @@ statement append_expr ($$, l1); append_expr ($$, $4); append_expr ($$, continue_label); - append_expr ($$, new_binary_expr ('i', test_expr ($7, 1), l1)); - append_expr ($$, break_label); + + $7 = convert_bool ($7, 1); + backpatch ($7->e.bool.true_list, l1); + backpatch ($7->e.bool.false_list, break_label); + append_expr ($7->e.bool.e, break_label); + append_expr ($$, $7); + break_label = $2; continue_label = $3; } @@ -703,20 +706,22 @@ statement } | IF '(' expr ')' save_inits statement { - expr_t *l1 = new_label_expr (); - expr_t *e; + expr_t *tl = new_label_expr (); + expr_t *fl = new_label_expr (); $$ = new_block_expr (); restore_local_inits ($5); free_local_inits ($5); - e = new_binary_expr ('n', test_expr ($3, 1), l1); - e->line = $3->line; - e->file = $3->file; - append_expr ($$, e); + $3 = convert_bool ($3, 1); + backpatch ($3->e.bool.true_list, tl); + backpatch ($3->e.bool.false_list, fl); + append_expr ($3->e.bool.e, tl); + append_expr ($$, $3); + append_expr ($$, $6); - append_expr ($$, l1); + append_expr ($$, fl); } | IF '(' expr ')' save_inits statement ELSE { @@ -725,8 +730,9 @@ statement } statement { - expr_t *l1 = new_label_expr (); - expr_t *l2 = new_label_expr (); + expr_t *tl = new_label_expr (); + expr_t *fl = new_label_expr (); + expr_t *nl = new_label_expr (); expr_t *e; hashtab_t *merged; hashtab_t *else_ini; @@ -738,19 +744,21 @@ statement restore_local_inits ($5); free_local_inits ($5); - e = new_binary_expr ('n', test_expr ($3, 1), l1); - e->line = $3->line; - e->file = $3->file; - append_expr ($$, e); + $3 = convert_bool ($3, 1); + backpatch ($3->e.bool.true_list, tl); + backpatch ($3->e.bool.false_list, fl); + append_expr ($3->e.bool.e, tl); + append_expr ($$, $3); append_expr ($$, $6); - e = new_unary_expr ('g', l2); + e = new_unary_expr ('g', nl); append_expr ($$, e); - append_expr ($$, l1); + append_expr ($$, fl); append_expr ($$, $9); - append_expr ($$, l2); + append_expr ($$, nl); + merged = merge_local_inits ($8, else_ini); restore_local_inits (merged); free_local_inits (merged); @@ -760,8 +768,14 @@ statement | FOR break_label continue_label '(' opt_expr ';' opt_expr ';' opt_expr ')' save_inits statement { - expr_t *l1 = new_label_expr (); - expr_t *l2 = break_label; + expr_t *tl = new_label_expr (); + expr_t *fl = break_label; + expr_t *l1 = 0; + int line = pr.source_line; + string_t file = pr.source_file; + + pr.source_line = $7->line; + pr.source_file = $7->file; restore_local_inits ($11); free_local_inits ($11); @@ -769,17 +783,29 @@ statement $$ = new_block_expr (); append_expr ($$, $5); - if ($7) - append_expr ($$, new_binary_expr ('n', test_expr ($7, 1), l2)); - append_expr ($$, l1); + if ($7) { + l1 = new_label_expr (); + append_expr ($$, new_unary_expr ('g', l1)); + } + append_expr ($$, tl); append_expr ($$, $12); append_expr ($$, continue_label); append_expr ($$, $9); - if ($5) - append_expr ($$, new_binary_expr ('i', test_expr ($7, 1), l1)); - append_expr ($$, l2); + if ($7) { + append_expr ($$, l1); + $7 = convert_bool ($7, 1); + backpatch ($7->e.bool.true_list, tl); + backpatch ($7->e.bool.false_list, fl); + append_expr ($7->e.bool.e, fl); + append_expr ($$, $7); + } else { + append_expr ($$, fl); + } break_label = $2; continue_label = $3; + + pr.source_line = line; + pr.source_file = file; } | expr ';' { @@ -787,6 +813,13 @@ statement } ; +label + : /* empty */ + { + $$ = new_label_expr (); + } + ; + break_label : /* empty */ { @@ -864,8 +897,8 @@ expr | expr '=' expr { $$ = assign_expr ($1, $3); } | expr ASX expr { $$ = asx_expr ($2, $1, $3); } | expr '?' expr ':' expr { $$ = conditional_expr ($1, $3, $5); } - | expr AND expr { $$ = binary_expr (AND, $1, $3); } - | expr OR expr { $$ = binary_expr (OR, $1, $3); } + | expr AND label expr { $$ = bool_expr (AND, $3, $1, $4); } + | expr OR label expr { $$ = bool_expr (OR, $3, $1, $4); } | expr EQ expr { $$ = binary_expr (EQ, $1, $3); } | expr NE expr { $$ = binary_expr (NE, $1, $3); } | expr LE expr { $$ = binary_expr (LE, $1, $3); }