mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-07 05:30:31 +00:00
638f4445cc
Don't use the true void return in traditional mode. Prefer the true void return over "done" at the end of functions. Don't emit the above if the last emitted statement is a return and there is no label at the end of the function.
917 lines
22 KiB
C
917 lines
22 KiB
C
/*
|
|
emit.c
|
|
|
|
statement emittion
|
|
|
|
Copyright (C) 2001 Bill Currie <bill@taniwha.org>
|
|
|
|
Author: Bill Currie <bill@taniwha.org>
|
|
Date: 2001/07/26
|
|
|
|
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
|
|
|
|
static __attribute__ ((used)) const char rcsid[] =
|
|
"$Id$";
|
|
|
|
#ifdef HAVE_STRING_H
|
|
# include <string.h>
|
|
#endif
|
|
#ifdef HAVE_STRINGS_H
|
|
# include <strings.h>
|
|
#endif
|
|
#include <stdlib.h>
|
|
|
|
#include <QF/mathlib.h>
|
|
#include <QF/va.h>
|
|
|
|
#include "codespace.h"
|
|
#include "def.h"
|
|
#include "debug.h"
|
|
#include "emit.h"
|
|
#include "expr.h"
|
|
#include "function.h"
|
|
#include "immediate.h"
|
|
#include "opcodes.h"
|
|
#include "options.h"
|
|
#include "qfcc.h"
|
|
#include "reloc.h"
|
|
#include "type.h"
|
|
#include "qc-parse.h"
|
|
|
|
static expr_t zero;
|
|
|
|
static void
|
|
add_statement_ref (def_t *def, dstatement_t *st, int field)
|
|
{
|
|
if (def) {
|
|
int st_ofs = st - pr.code->code;
|
|
|
|
def->users--;
|
|
def->used = 1;
|
|
if (def->parent)
|
|
def->parent->used = 1;
|
|
|
|
if (def->alias) {
|
|
def = def->alias;
|
|
def->users--;
|
|
def->used = 1;
|
|
if (def->parent)
|
|
def->parent->used = 1;
|
|
reloc_op_def_ofs (def, st_ofs, field);
|
|
} else
|
|
reloc_op_def (def, st_ofs, field);
|
|
}
|
|
}
|
|
|
|
def_t *
|
|
emit_statement (expr_t *e, opcode_t *op, def_t *var_a, def_t *var_b,
|
|
def_t *var_c)
|
|
{
|
|
dstatement_t *statement;
|
|
def_t *ret;
|
|
|
|
if (!op) {
|
|
error (e, "ice ice baby");
|
|
abort ();
|
|
}
|
|
if (options.code.debug && current_func->aux) {
|
|
pr_uint_t line = (e ? e->line : pr.source_line) - lineno_base;
|
|
|
|
if (line != pr.linenos[pr.num_linenos - 1].line) {
|
|
pr_lineno_t *lineno = new_lineno ();
|
|
|
|
lineno->line = line;
|
|
lineno->fa.addr = pr.code->size;
|
|
}
|
|
}
|
|
statement = codespace_newstatement (pr.code);
|
|
statement->op = op->opcode;
|
|
statement->a = var_a ? var_a->ofs : 0;
|
|
statement->b = var_b ? var_b->ofs : 0;
|
|
statement->c = 0;
|
|
if (op->type_c == ev_invalid || op->right_associative
|
|
|| op->opcode == OP_DONE/*stupid opcode*/) {
|
|
// ifs, gotos, and assignments don't need vars allocated
|
|
if (var_c)
|
|
statement->c = var_c->ofs;
|
|
ret = var_a;
|
|
} else { // allocate result space
|
|
if (!var_c) {
|
|
var_c = get_tempdef (ev_types[op->type_c], current_scope);
|
|
var_c->users += 2;
|
|
}
|
|
statement->c = var_c->ofs;
|
|
ret = var_c;
|
|
}
|
|
#if 0
|
|
printf ("%s %s(%d:%d) %s(%d:%d) %s(%d:%d)\n", op->opname,
|
|
var_a ? var_a->name : "", var_a ? var_a->users : 99, statement->a,
|
|
var_b ? var_b->name : "", var_b ? var_b->users : 99, statement->b,
|
|
var_c ? var_c->name : "", var_c ? var_c->users : 99, statement->c);
|
|
#endif
|
|
|
|
add_statement_ref (var_a, statement, 0);
|
|
add_statement_ref (var_b, statement, 1);
|
|
add_statement_ref (var_c, statement, 2);
|
|
|
|
if (op->right_associative)
|
|
return var_a;
|
|
return var_c;
|
|
}
|
|
|
|
static void
|
|
emit_branch (expr_t *_e, opcode_t *op, expr_t *e, expr_t *l)
|
|
{
|
|
dstatement_t *st;
|
|
reloc_t *ref;
|
|
def_t *def = 0;
|
|
int ofs;
|
|
|
|
if (e)
|
|
def = emit_sub_expr (e, 0);
|
|
ofs = pr.code->size;
|
|
emit_statement (_e, op, def, 0, 0);
|
|
st = &pr.code->code[ofs];
|
|
if (l->e.label.ofs) {
|
|
if (op == op_goto)
|
|
st->a = l->e.label.ofs - ofs;
|
|
else
|
|
st->b = l->e.label.ofs - ofs;
|
|
} else {
|
|
ref = new_reloc (ofs, op == op_goto ? rel_op_a_op : rel_op_b_op);
|
|
ref->next = l->e.label.refs;
|
|
l->e.label.refs = ref;
|
|
}
|
|
}
|
|
|
|
static def_t *
|
|
vector_call (expr_t *earg, expr_t *parm, int ind)
|
|
{
|
|
expr_t *a, *v, *n;
|
|
int i;
|
|
static const char *names[] = {"x", "y", "z"};
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
n = new_name_expr (names[i]);
|
|
v = new_float_expr (earg->e.vector_val[i]);
|
|
a = assign_expr (binary_expr ('.', parm, n), v);
|
|
parm = new_param_expr (get_type (earg), ind);
|
|
a->line = earg->line;
|
|
a->file = earg->file;
|
|
emit_expr (a);
|
|
}
|
|
return emit_sub_expr (parm, 0);
|
|
}
|
|
|
|
static def_t *
|
|
emit_function_call (expr_t *e, def_t *dest)
|
|
{
|
|
def_t *func = emit_sub_expr (e->e.expr.e1, 0);
|
|
def_t *ret;
|
|
def_t *arg;
|
|
def_t *p;
|
|
def_t *a[2] = {0, 0};
|
|
expr_t *earg;
|
|
expr_t *parm;
|
|
opcode_t *op;
|
|
type_t *t1 = &type_invalid;
|
|
type_t *t2 = &type_invalid;
|
|
int count = 0, ind;
|
|
const char *pref = "";
|
|
|
|
for (earg = e->e.expr.e2; earg; earg = earg->next)
|
|
count++;
|
|
ind = count;
|
|
for (earg = e->e.expr.e2; earg; earg = earg->next) {
|
|
ind--;
|
|
parm = new_param_expr (get_type (earg), ind);
|
|
if (options.code.progsversion != PROG_ID_VERSION && ind < 2) {
|
|
pref = "R";
|
|
if (ind == 1)
|
|
t2 = &type_void;
|
|
if (ind == 0)
|
|
t1 = &type_void;
|
|
if (options.code.vector_calls && earg->type == ex_vector) {
|
|
a[ind] = vector_call (earg, parm, ind);
|
|
} else {
|
|
a[ind] = emit_sub_expr (earg, emit_sub_expr (parm, 0));
|
|
}
|
|
continue;
|
|
}
|
|
if (is_struct (get_type (parm))) {
|
|
expr_t *a = assign_expr (parm, earg);
|
|
a->line = e->line;
|
|
a->file = e->file;
|
|
emit_expr (a);
|
|
} else {
|
|
if (options.code.vector_calls && earg->type == ex_vector) {
|
|
vector_call (earg, parm, ind);
|
|
} else {
|
|
p = emit_sub_expr (parm, 0);
|
|
arg = emit_sub_expr (earg, p);
|
|
if (arg != p) {
|
|
op = opcode_find ("=", arg->type, arg->type, &type_invalid);
|
|
emit_statement (e, op, arg, p, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
op = opcode_find (va ("<%sCALL%d>", pref, count), &type_function, t1, t2);
|
|
emit_statement (e, op, func, a[0], a[1]);
|
|
|
|
ret = emit_sub_expr (new_ret_expr (func->type->t.func.type), 0);
|
|
if (dest) {
|
|
op = opcode_find ("=", dest->type, ret->type, &type_invalid);
|
|
emit_statement (e, op, ret, dest, 0);
|
|
return dest;
|
|
} else {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
static def_t *
|
|
emit_assign_expr (int oper, expr_t *e)
|
|
{
|
|
def_t *def_a, *def_b, *def_c, *a, *b;
|
|
opcode_t *op;
|
|
expr_t *e1 = e->e.expr.e1;
|
|
expr_t *e2 = e->e.expr.e2;
|
|
const char *operator = get_op_string (oper);
|
|
|
|
if (e1->type == ex_temp && e1->e.temp.users < 2) {
|
|
e1->e.temp.users--;
|
|
return 0;
|
|
}
|
|
if (oper == '=') {
|
|
def_a = emit_sub_expr (e1, 0);
|
|
if (def_a->constant) {
|
|
if (options.code.cow) {
|
|
int size = type_size (def_a->type);
|
|
int ofs = new_location (def_a->type, pr.near_data);
|
|
|
|
memcpy (G_POINTER (void, ofs), G_POINTER (void, def_a->ofs),
|
|
size);
|
|
def_a->ofs = ofs;
|
|
def_a->constant = 0;
|
|
def_a->nosave = 1;
|
|
if (options.warnings.cow)
|
|
warning (e1, "assignment to constant %s (Moooooooo!)",
|
|
def_a->name);
|
|
} else {
|
|
if (options.traditional) {
|
|
if (options.warnings.cow)
|
|
warning (e1, "assignment to constant %s (Moooooooo!)",
|
|
def_a->name);
|
|
} else
|
|
error (e1, "assignment to constant %s", def_a->name);
|
|
}
|
|
}
|
|
def_b = emit_sub_expr (e2, def_a);
|
|
a = def_a;
|
|
if (a->alias)
|
|
a = a->alias;
|
|
b = def_b;
|
|
if (b->alias)
|
|
b = b->alias;
|
|
if (b != a) {
|
|
op = opcode_find (operator, def_b->type, def_a->type,
|
|
&type_invalid);
|
|
emit_statement (e, op, def_b, def_a, 0);
|
|
}
|
|
return def_a;
|
|
} else {
|
|
def_b = emit_sub_expr (e2, 0);
|
|
if (e->rvalue && def_b->managed)
|
|
def_b->users++;
|
|
if (e1->type == ex_expr && extract_type (e1->e.expr.e1) == ev_pointer
|
|
&& e1->e.expr.e1->type < ex_nil) {
|
|
def_a = emit_sub_expr (e1->e.expr.e1, 0);
|
|
def_c = emit_sub_expr (e1->e.expr.e2, 0);
|
|
op = opcode_find (operator, def_b->type, def_a->type, def_c->type);
|
|
} else {
|
|
def_a = emit_sub_expr (e1, 0);
|
|
def_c = 0;
|
|
op = opcode_find (operator, def_b->type, def_a->type, &type_invalid);
|
|
}
|
|
emit_statement (e, op, def_b, def_a, def_c);
|
|
return def_b;
|
|
}
|
|
}
|
|
|
|
static def_t *
|
|
emit_bind_expr (expr_t *e1, expr_t *e2)
|
|
{
|
|
type_t *t1 = get_type (e1);
|
|
type_t *t2 = get_type (e2);
|
|
def_t *def;
|
|
|
|
if (!e2 || e2->type != ex_temp) {
|
|
error (e1, "internal error");
|
|
abort ();
|
|
}
|
|
def = emit_sub_expr (e1, e2->e.temp.def);
|
|
if (t1 != t2) {
|
|
def_t *tmp = new_def (t2, 0, def->scope);
|
|
|
|
tmp->ofs = 0;
|
|
tmp->alias = def;
|
|
tmp->users = e2->e.temp.users;
|
|
tmp->freed = 1; // don't free this offset when freeing def
|
|
def = tmp;
|
|
}
|
|
e2->e.temp.def = def;
|
|
return e2->e.temp.def;
|
|
}
|
|
|
|
static def_t *
|
|
emit_move_expr (expr_t *e)
|
|
{
|
|
expr_t *e1 = e->e.expr.e1;
|
|
expr_t *e2 = e->e.expr.e2;
|
|
expr_t *size_expr;
|
|
def_t *size, *src, *dst;
|
|
type_t *src_type, *dst_type;
|
|
opcode_t *op;
|
|
|
|
dst_type = get_type (e1);
|
|
src_type = get_type (e2);
|
|
src = emit_sub_expr (e2, 0);
|
|
dst = emit_sub_expr (e1, 0);
|
|
|
|
if (is_struct (dst_type) && is_struct (src_type)) {
|
|
size_expr = new_short_expr (type_size (dst->type));
|
|
dst_type = src_type = &type_void;
|
|
} else if (is_struct (dst_type)) {
|
|
if (dst->alias)
|
|
dst = dst->alias;
|
|
dst = emit_sub_expr (address_expr (new_def_expr (dst), 0, 0), 0);
|
|
dst_type = dst->type;
|
|
size_expr = new_integer_expr (type_size (dst_type));
|
|
} else if (is_struct (src_type)) {
|
|
if (src->alias)
|
|
src = src->alias;
|
|
src = emit_sub_expr (address_expr (new_def_expr (src), 0, 0), 0);
|
|
src_type = src->type;
|
|
size_expr = new_integer_expr (type_size (dst_type->t.fldptr.type));
|
|
} else {
|
|
size_expr = new_integer_expr (type_size (dst_type->t.fldptr.type));
|
|
}
|
|
size = emit_sub_expr (size_expr, 0);
|
|
|
|
op = opcode_find ("<MOVE>", src_type, size->type, dst_type);
|
|
return emit_statement (e, op, src, size, dst);
|
|
}
|
|
|
|
static def_t *
|
|
emit_address_expr (expr_t *e)
|
|
{
|
|
def_t *def_a, *def_b, *d;
|
|
opcode_t *op;
|
|
|
|
def_a = emit_sub_expr (e->e.expr.e1, 0);
|
|
def_b = emit_sub_expr (e->e.expr.e2, 0);
|
|
op = opcode_find ("&", def_a->type, def_b->type, 0);
|
|
d = emit_statement (e, op, def_a, def_b, 0);
|
|
return d;
|
|
}
|
|
|
|
static def_t *
|
|
emit_deref_expr (expr_t *e, def_t *dest)
|
|
{
|
|
def_t *d;
|
|
type_t *type = e->e.expr.type;
|
|
def_t *z;
|
|
opcode_t *op;
|
|
|
|
e = e->e.expr.e1;
|
|
if (e->type == ex_pointer) {
|
|
if (e->e.pointer.def) {
|
|
d = new_def (e->e.pointer.type, 0, e->e.pointer.def->scope);
|
|
d->local = e->e.pointer.def->local;
|
|
d->ofs = e->e.pointer.val;
|
|
d->alias = e->e.pointer.def;
|
|
} else if (e->e.pointer.val >= 0 && e->e.pointer.val < 65536) {
|
|
d = new_def (e->e.pointer.type, 0, current_scope);
|
|
d->ofs = e->e.pointer.val;
|
|
} else {
|
|
d = ReuseConstant (e, 0);
|
|
zero.type = ex_short;
|
|
z = emit_sub_expr (&zero, 0);
|
|
op = opcode_find (".", d->type, z->type, dest->type);
|
|
d = emit_statement (e, op, d, z, dest);
|
|
}
|
|
return d;
|
|
}
|
|
if (e->type == ex_uexpr && e->e.expr.op == '&'
|
|
&& e->e.expr.e1->type == ex_def) {
|
|
d = new_def (e->e.expr.type->t.fldptr.type, 0, current_scope);
|
|
d->alias = e->e.expr.e1->e.def;
|
|
d->local = d->alias->local;
|
|
d->ofs = d->alias->ofs;
|
|
return d;
|
|
}
|
|
if (!dest) {
|
|
dest = get_tempdef (type, current_scope);
|
|
dest->line = e->line;
|
|
dest->file = e->file;
|
|
dest->users += 2;
|
|
}
|
|
if (is_struct (dest->type)) {
|
|
expr_t *d = new_def_expr (dest);
|
|
expr_t *m = new_move_expr (d, e, dest->type);
|
|
d->line = dest->line;
|
|
d->file = dest->file;
|
|
m->line = e->line;
|
|
m->file = e->file;
|
|
emit_sub_expr (m, 0);
|
|
return dest;
|
|
}
|
|
|
|
if (e->type == ex_expr
|
|
&& e->e.expr.op == '&'
|
|
&& e->e.expr.e1->type < ex_nil)
|
|
e->e.expr.op = '.';
|
|
if (e->type == ex_uexpr && e->e.expr.op == '.')
|
|
d = emit_sub_expr (e, 0);
|
|
else
|
|
d = emit_sub_expr (e, dest);
|
|
|
|
if (dest && d != dest) {
|
|
zero.type = ex_short;
|
|
z = emit_sub_expr (&zero, 0);
|
|
op = opcode_find (".", d->type, z->type, dest->type);
|
|
d = emit_statement (e, op, d, z, dest);
|
|
} else {
|
|
if (!d->name)
|
|
d->type = type;
|
|
}
|
|
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);
|
|
free_tempdefs ();
|
|
return;
|
|
case ex_label:
|
|
e->next = 0;
|
|
append_expr (block, e);
|
|
free_tempdefs ();
|
|
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);
|
|
}
|
|
free_tempdefs ();
|
|
return;
|
|
case ex_uexpr:
|
|
if (e->e.expr.op == 'g') {
|
|
e->next = 0;
|
|
append_expr (block, e);
|
|
free_tempdefs ();
|
|
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);
|
|
}
|
|
free_tempdefs ();
|
|
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)
|
|
{
|
|
opcode_t *op;
|
|
const char *operator;
|
|
def_t *def_a, *def_b, *d = 0;
|
|
def_t *tmp = 0;
|
|
|
|
switch (e->type) {
|
|
case ex_block:
|
|
if (e->e.block.result) {
|
|
expr_t *res = e->e.block.result;
|
|
for (e = e->e.block.head; e; e = e->next)
|
|
emit_expr (e);
|
|
d = emit_sub_expr (res, dest);
|
|
}
|
|
break;
|
|
case ex_state:
|
|
case ex_bool:
|
|
case ex_name:
|
|
case ex_nil:
|
|
case ex_label:
|
|
case ex_error:
|
|
error (e, "internal error");
|
|
abort ();
|
|
case ex_expr:
|
|
if (e->e.expr.op == 'M') {
|
|
d = emit_move_expr (e);
|
|
break;
|
|
}
|
|
if (e->e.expr.op == 'b') {
|
|
d = emit_bind_expr (e->e.expr.e1, e->e.expr.e2);
|
|
break;
|
|
}
|
|
if (e->e.expr.op == 'c') {
|
|
d = emit_function_call (e, dest);
|
|
break;
|
|
}
|
|
if (e->e.expr.op == '=' || e->e.expr.op == PAS) {
|
|
d = emit_assign_expr (e->e.expr.op, e);
|
|
if (!d->managed)
|
|
d->users++;
|
|
break;
|
|
}
|
|
if (e->e.expr.op == '&' && e->e.expr.type->type == ev_pointer) {
|
|
d = emit_address_expr (e);
|
|
break;
|
|
}
|
|
if (e->e.expr.e1->type == ex_block
|
|
&& e->e.expr.e1->e.block.is_call) {
|
|
def_b = emit_sub_expr (e->e.expr.e2, 0);
|
|
def_a = emit_sub_expr (e->e.expr.e1, 0);
|
|
} else {
|
|
def_a = emit_sub_expr (e->e.expr.e1, 0);
|
|
def_b = emit_sub_expr (e->e.expr.e2, 0);
|
|
}
|
|
operator = get_op_string (e->e.expr.op);
|
|
if (!dest) {
|
|
dest = get_tempdef (e->e.expr.type, current_scope);
|
|
dest->file = e->file;
|
|
dest->line = e->line;
|
|
dest->users += 2;
|
|
}
|
|
op = opcode_find (operator, def_a->type, def_b->type,
|
|
dest->type);
|
|
d = emit_statement (e, op, def_a, def_b, dest);
|
|
break;
|
|
case ex_uexpr:
|
|
switch (e->e.expr.op) {
|
|
case '!':
|
|
operator = "!";
|
|
def_a = emit_sub_expr (e->e.expr.e1, 0);
|
|
def_b = &def_invalid;
|
|
break;
|
|
case '~':
|
|
operator = "~";
|
|
def_a = emit_sub_expr (e->e.expr.e1, 0);
|
|
def_b = &def_invalid;
|
|
break;
|
|
case '-':
|
|
zero.type = expr_types[extract_type (e->e.expr.e1)];
|
|
|
|
operator = "-";
|
|
def_a = ReuseConstant (&zero, 0);
|
|
def_b = emit_sub_expr (e->e.expr.e1, 0);
|
|
if (!dest) {
|
|
dest = get_tempdef (e->e.expr.type, current_scope);
|
|
dest->file = e->file;
|
|
dest->line = e->line;
|
|
dest->users += 2;
|
|
}
|
|
break;
|
|
case '&':
|
|
zero.type = ex_short;
|
|
|
|
operator = "&";
|
|
if (e->e.expr.e1->type == ex_expr
|
|
&& e->e.expr.e1->e.expr.op == '.') {
|
|
tmp = get_tempdef (e->e.expr.type, current_scope);
|
|
tmp->file = e->file;
|
|
tmp->line = e->line;
|
|
tmp->users += 2;
|
|
def_b = emit_sub_expr (&zero, 0);
|
|
} else {
|
|
def_b = &def_void;
|
|
}
|
|
def_a = emit_sub_expr (e->e.expr.e1, tmp);
|
|
if (!dest) {
|
|
dest = get_tempdef (e->e.expr.type, current_scope);
|
|
dest->file = e->file;
|
|
dest->line = e->line;
|
|
dest->users += 2;
|
|
}
|
|
break;
|
|
case '.':
|
|
return emit_deref_expr (e, dest);
|
|
case 'C':
|
|
def_a = emit_sub_expr (e->e.expr.e1, 0);
|
|
if ((def_a->type->type == ev_pointer
|
|
&& e->e.expr.type->type == ev_pointer)
|
|
|| (def_a->type->type == ev_func
|
|
&& e->e.expr.type->type == ev_func)) {
|
|
return def_a;
|
|
}
|
|
if ((def_a->type->type == ev_pointer
|
|
&& e->e.expr.type->type == ev_integer)
|
|
|| (def_a->type->type == ev_integer
|
|
&& e->e.expr.type->type == ev_pointer)) {
|
|
def_t *tmp;
|
|
tmp = new_def (e->e.expr.type, 0, def_a->scope);
|
|
tmp->ofs = 0;
|
|
tmp->alias = def_a;
|
|
tmp->users = def_a->users;
|
|
tmp->freed = 1;
|
|
return tmp;
|
|
}
|
|
def_b = &def_invalid;
|
|
if (!dest) {
|
|
dest = get_tempdef (e->e.expr.type, current_scope);
|
|
dest->file = e->file;
|
|
dest->line = e->line;
|
|
dest->users = 2;
|
|
}
|
|
operator = "=";
|
|
break;
|
|
default:
|
|
abort ();
|
|
}
|
|
op = opcode_find (operator, def_a->type, def_b->type,
|
|
dest ? dest->type : 0);
|
|
d = emit_statement (e, op, def_a, def_b, dest);
|
|
break;
|
|
case ex_def:
|
|
d = e->e.def;
|
|
break;
|
|
case ex_temp:
|
|
if (!e->e.temp.def) {
|
|
if (dest)
|
|
e->e.temp.def = dest;
|
|
else {
|
|
e->e.temp.def = get_tempdef (e->e.temp.type, current_scope);
|
|
e->e.temp.def->line = e->line;
|
|
e->e.temp.def->file = e->file;
|
|
}
|
|
e->e.temp.def->users = e->e.temp.users;
|
|
e->e.temp.def->expr = e;
|
|
e->e.temp.def->managed = 1;
|
|
}
|
|
d = e->e.temp.def;
|
|
break;
|
|
case ex_pointer:
|
|
case ex_string:
|
|
case ex_float:
|
|
case ex_vector:
|
|
case ex_entity:
|
|
case ex_field:
|
|
case ex_func:
|
|
case ex_quaternion:
|
|
case ex_integer:
|
|
case ex_uinteger:
|
|
d = ReuseConstant (e, 0);
|
|
break;
|
|
case ex_short:
|
|
d = new_def (&type_short, 0, 0);
|
|
d->ofs = e->e.short_val;
|
|
d->absolute = 1;
|
|
d->users = 1;
|
|
break;
|
|
}
|
|
free_tempdefs ();
|
|
return d;
|
|
}
|
|
|
|
void
|
|
emit_expr (expr_t *e)
|
|
{
|
|
def_t *def;
|
|
def_t *def_a;
|
|
def_t *def_b;
|
|
def_t *def_c;
|
|
ex_label_t *label;
|
|
opcode_t *op;
|
|
|
|
//printf ("%d ", e->line);
|
|
//print_expr (e);
|
|
//puts ("");
|
|
switch (e->type) {
|
|
case ex_error:
|
|
break;
|
|
case ex_state:
|
|
def_a = emit_sub_expr (e->e.state.frame, 0);
|
|
def_b = emit_sub_expr (e->e.state.think, 0);
|
|
if (e->e.state.step) {
|
|
def_c = emit_sub_expr (e->e.state.step, 0);
|
|
op = op_state_f;
|
|
} else {
|
|
def_c = 0;
|
|
op = op_state;
|
|
}
|
|
emit_statement (e, op, def_a, def_b, def_c);
|
|
break;
|
|
case ex_bool:
|
|
emit_bool_expr (e);
|
|
break;
|
|
case ex_label:
|
|
label = &e->e.label;
|
|
label->ofs = pr.code->size;
|
|
relocate_refs (label->refs, label->ofs);
|
|
break;
|
|
case ex_block:
|
|
for (e = e->e.block.head; e; e = e->next)
|
|
emit_expr (e);
|
|
break;
|
|
case ex_expr:
|
|
switch (e->e.expr.op) {
|
|
case 'M':
|
|
emit_move_expr (e);
|
|
break;
|
|
case PAS:
|
|
case '=':
|
|
emit_assign_expr (e->e.expr.op, e);
|
|
break;
|
|
case 'n':
|
|
emit_branch (e, op_ifnot, e->e.expr.e1, e->e.expr.e2);
|
|
break;
|
|
case 'i':
|
|
emit_branch (e, op_if, e->e.expr.e1, e->e.expr.e2);
|
|
break;
|
|
case IFBE:
|
|
emit_branch (e, op_ifbe, e->e.expr.e1, e->e.expr.e2);
|
|
break;
|
|
case IFB:
|
|
emit_branch (e, op_ifb, e->e.expr.e1, e->e.expr.e2);
|
|
break;
|
|
case IFAE:
|
|
emit_branch (e, op_ifae, e->e.expr.e1, e->e.expr.e2);
|
|
break;
|
|
case IFA:
|
|
emit_branch (e, op_ifa, e->e.expr.e1, e->e.expr.e2);
|
|
break;
|
|
case 'c':
|
|
emit_function_call (e, 0);
|
|
break;
|
|
case 'b':
|
|
emit_bind_expr (e->e.expr.e1, e->e.expr.e2);
|
|
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, op_jumpb, def_a, def_b, 0);
|
|
break;
|
|
default:
|
|
if (options.warnings.executable)
|
|
warning (e, "Non-executable statement; "
|
|
"executing programmer instead.");
|
|
break;
|
|
}
|
|
break;
|
|
case ex_uexpr:
|
|
switch (e->e.expr.op) {
|
|
case 'r':
|
|
def = 0;
|
|
if (e->e.expr.e1)
|
|
def = emit_sub_expr (e->e.expr.e1, 0);
|
|
if (!def && !options.traditional && op_return_v)
|
|
emit_statement (e, op_return_v, 0, 0, 0);
|
|
else
|
|
emit_statement (e, op_return, def, 0, 0);
|
|
break;
|
|
case 'g':
|
|
emit_branch (e, op_goto, 0, e->e.expr.e1);
|
|
break;
|
|
default:
|
|
if (options.warnings.executable)
|
|
warning (e, "Non-executable statement; "
|
|
"executing programmer instead.");
|
|
emit_expr (e->e.expr.e1);
|
|
break;
|
|
}
|
|
break;
|
|
case ex_def:
|
|
case ex_temp:
|
|
case ex_string:
|
|
case ex_float:
|
|
case ex_vector:
|
|
case ex_entity:
|
|
case ex_field:
|
|
case ex_func:
|
|
case ex_pointer:
|
|
case ex_quaternion:
|
|
case ex_integer:
|
|
case ex_uinteger:
|
|
case ex_short:
|
|
case ex_name:
|
|
case ex_nil:
|
|
if (options.warnings.executable)
|
|
warning (e, "Non-executable statement; "
|
|
"executing programmer instead.");
|
|
break;
|
|
}
|
|
free_tempdefs ();
|
|
}
|