mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-22 20:41:20 +00:00
[qfcc] Use the progs VM to help with constant folding
Due to joys of pointers and the like, it's a bit of a bolt-on for now, but it works nicely for basic math ops which is what I wanted, and the code is generated from the expression.
This commit is contained in:
parent
27ccad40c9
commit
dfb7862419
10 changed files with 193 additions and 39 deletions
|
@ -40,6 +40,7 @@ struct type_s;
|
|||
struct ex_value_s;
|
||||
struct ex_value_s *convert_value (struct ex_value_s *value,
|
||||
struct type_s *type);
|
||||
struct expr_s *evaluate_constexpr (struct expr_s *e);
|
||||
void setup_value_progs (void);
|
||||
|
||||
///@}
|
||||
|
|
|
@ -743,6 +743,7 @@ int is_uint_val (expr_t *e) __attribute__((pure));
|
|||
int is_short_val (expr_t *e) __attribute__((pure));
|
||||
int is_integral_val (expr_t *e) __attribute__((pure));
|
||||
int is_pointer_val (expr_t *e) __attribute__((pure));
|
||||
int is_math_val (expr_t *e) __attribute__((pure));
|
||||
|
||||
/** Create a reference to the global <code>.self</code> entity variable.
|
||||
|
||||
|
|
|
@ -159,6 +159,8 @@ operand_t *label_operand (struct expr_s *label);
|
|||
void free_operand (operand_t *op);
|
||||
|
||||
sblock_t *new_sblock (void);
|
||||
void free_sblock (sblock_t *sblock);
|
||||
|
||||
statement_t *new_statement (st_type_t type, const char *opcode,
|
||||
struct expr_s *expr);
|
||||
int statement_is_cond (statement_t *s) __attribute__((pure));
|
||||
|
@ -170,6 +172,7 @@ sblock_t *statement_get_target (statement_t *s) __attribute__((pure));
|
|||
sblock_t **statement_get_targetlist (statement_t *s);
|
||||
void sblock_add_statement (sblock_t *sblock, statement_t *statement);
|
||||
sblock_t *make_statements (struct expr_s *expr);
|
||||
sblock_t *statement_slist (sblock_t *sblock, struct expr_s *e);
|
||||
void statements_count_temps (sblock_t *sblock);
|
||||
|
||||
void print_operand (operand_t *op);
|
||||
|
|
|
@ -41,6 +41,7 @@ struct ex_value_s;
|
|||
struct tempop_s;
|
||||
struct type_s;
|
||||
struct pr_type_s;
|
||||
struct operand_s;
|
||||
|
||||
struct ex_value_s *new_string_val (const char *string_val);
|
||||
struct ex_value_s *new_double_val (double double_val);
|
||||
|
@ -66,10 +67,10 @@ void value_store (pr_type_t *dst, const struct type_s *dstType,
|
|||
const struct expr_s *src);
|
||||
const char *get_value_string (const struct ex_value_s *value);
|
||||
|
||||
struct ex_value_s *convert_value (struct ex_value_s *value,
|
||||
struct type_s *type);
|
||||
struct ex_value_s *alias_value (struct ex_value_s *value, struct type_s *type);
|
||||
struct def_s *emit_value (struct ex_value_s *value, struct def_s *def);
|
||||
struct def_s *emit_value_core (struct ex_value_s *value, struct def_s *def,
|
||||
struct defspace_s *defspace);
|
||||
|
||||
int ReuseString (const char *str);
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include <QF/mathlib.h>
|
||||
|
||||
#include "tools/qfcc/include/diagnostic.h"
|
||||
#include "tools/qfcc/include/evaluate.h"
|
||||
#include "tools/qfcc/include/expr.h"
|
||||
#include "tools/qfcc/include/options.h"
|
||||
#include "tools/qfcc/include/qfcc.h"
|
||||
|
@ -1696,6 +1697,9 @@ fold_constants (expr_t *e)
|
|||
if (!e1) {
|
||||
return e;
|
||||
}
|
||||
if (is_math (get_type (e1))) {
|
||||
return evaluate_constexpr (e);
|
||||
}
|
||||
op = e->e.expr.op;
|
||||
t1 = extract_type (e1);
|
||||
if (t1 >= ev_type_count || !do_unary_op[t1]) {
|
||||
|
@ -1710,6 +1714,10 @@ fold_constants (expr_t *e)
|
|||
return e;
|
||||
}
|
||||
|
||||
if (is_math_val (e1) && is_math_val (e2)) {
|
||||
return evaluate_constexpr (e);
|
||||
}
|
||||
|
||||
op = e->e.expr.op;
|
||||
|
||||
t1 = extract_type (e1);
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
#include "tools/qfcc/include/diagnostic.h"
|
||||
#include "tools/qfcc/include/emit.h"
|
||||
#include "tools/qfcc/include/expr.h"
|
||||
#include "tools/qfcc/include/evaluate.h"
|
||||
#include "tools/qfcc/include/function.h"
|
||||
#include "tools/qfcc/include/options.h"
|
||||
#include "tools/qfcc/include/reloc.h"
|
||||
|
|
|
@ -34,18 +34,19 @@
|
|||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
//#include "QF/alloc.h"
|
||||
//#include "QF/dstring.h"
|
||||
//#include "QF/hash.h"
|
||||
//#include "QF/mathlib.h"
|
||||
#include "QF/progs.h"
|
||||
|
||||
#include "QF/simd/types.h"
|
||||
|
||||
#include "tools/qfcc/include/codespace.h"
|
||||
#include "tools/qfcc/include/defspace.h"
|
||||
#include "tools/qfcc/include/evaluate.h"
|
||||
#include "tools/qfcc/include/diagnostic.h"
|
||||
#include "tools/qfcc/include/dot.h"
|
||||
#include "tools/qfcc/include/expr.h"
|
||||
#include "tools/qfcc/include/opcodes.h"
|
||||
#include "tools/qfcc/include/options.h"
|
||||
#include "tools/qfcc/include/statements.h"
|
||||
#include "tools/qfcc/include/type.h"
|
||||
#include "tools/qfcc/include/value.h"
|
||||
|
||||
|
@ -77,6 +78,9 @@ value_debug_handler (prdebug_t event, void *param, void *data)
|
|||
enum {
|
||||
vf_null,
|
||||
vf_convert,
|
||||
vf_foldconst,
|
||||
|
||||
vf_num_functions
|
||||
};
|
||||
|
||||
#define BASE(b, base) (((base) & 3) << OP_##b##_SHIFT)
|
||||
|
@ -85,6 +89,7 @@ enum {
|
|||
static bfunction_t value_functions[] = {
|
||||
{}, // null function
|
||||
[vf_convert] = { .first_statement = vf_convert * 16 },
|
||||
[vf_foldconst] = { .first_statement = vf_foldconst * 16 },
|
||||
};
|
||||
|
||||
static __attribute__((aligned(64)))
|
||||
|
@ -92,6 +97,15 @@ dstatement_t value_statements[] = {
|
|||
[vf_convert * 16 - 1] = {},
|
||||
{ OP_CONV, 0, 07777, 16 },
|
||||
{ OP_RETURN, 16, 0, 0 },
|
||||
|
||||
[vf_foldconst * 16 - 1] = {},
|
||||
|
||||
[vf_num_functions * 16 - 1] = {},
|
||||
};
|
||||
static codespace_t value_codespace = {
|
||||
.code = value_statements,
|
||||
.size = vf_foldconst * 16,
|
||||
.max_size = vf_num_functions * 16,
|
||||
};
|
||||
|
||||
#define num_globals 16384
|
||||
|
@ -100,6 +114,13 @@ static __attribute__((aligned(64)))
|
|||
pr_type_t value_globals[num_globals + 128] = {
|
||||
[num_globals - stack_size] = { .uint_value = num_globals },
|
||||
};
|
||||
static defspace_t value_defspace = {
|
||||
.type = ds_backed,
|
||||
.def_tail = &value_defspace.defs,
|
||||
.data = value_globals,
|
||||
.max_size = num_globals - stack_size,
|
||||
.grow = 0,
|
||||
};
|
||||
|
||||
static dprograms_t value_progs = {
|
||||
.version = PROG_VERSION,
|
||||
|
@ -156,3 +177,99 @@ convert_value (ex_value_t *value, type_t *type)
|
|||
PR_ExecuteProgram (&value_pr, vf_convert);
|
||||
return new_type_value (type, value_pr.pr_return_buffer);
|
||||
}
|
||||
|
||||
static def_t *get_def (operand_t *op);
|
||||
|
||||
static def_t *
|
||||
get_temp_def (operand_t *op)
|
||||
{
|
||||
tempop_t *tempop = &op->tempop;
|
||||
if (tempop->def) {
|
||||
return tempop->def;
|
||||
}
|
||||
if (tempop->alias) {
|
||||
def_t *tdef = get_def (tempop->alias);
|
||||
int offset = tempop->offset;
|
||||
tempop->def = alias_def (tdef, op->type, offset);
|
||||
}
|
||||
if (!tempop->def) {
|
||||
tempop->def = new_def (0, op->type, &value_defspace, sc_static);
|
||||
}
|
||||
return tempop->def;
|
||||
}
|
||||
|
||||
static def_t *
|
||||
get_def (operand_t *op)
|
||||
{
|
||||
if (is_short (op->type)) {
|
||||
auto def = new_def (0, &type_short, 0, sc_extern);
|
||||
def->offset = op->value->v.short_val;
|
||||
return def;
|
||||
}
|
||||
switch (op->op_type) {
|
||||
case op_def:
|
||||
return op->def;
|
||||
case op_value:
|
||||
return emit_value_core (op->value, 0, &value_defspace);
|
||||
case op_temp:
|
||||
return get_temp_def (op);
|
||||
case op_alias:
|
||||
return get_def (op->alias);
|
||||
case op_nil:
|
||||
case op_pseudo:
|
||||
case op_label:
|
||||
break;
|
||||
}
|
||||
internal_error (0, "unexpected operand");
|
||||
}
|
||||
|
||||
expr_t *
|
||||
evaluate_constexpr (expr_t *e)
|
||||
{
|
||||
debug (e, "fold_constants");
|
||||
if (e->type == ex_uexpr) {
|
||||
if (!is_constant (e->e.expr.e1)) {
|
||||
return e;
|
||||
}
|
||||
} else if (e->type == ex_expr) {
|
||||
if (!is_constant (e->e.expr.e1) || !is_constant (e->e.expr.e2)) {
|
||||
return e;
|
||||
}
|
||||
} else {
|
||||
return e;
|
||||
}
|
||||
|
||||
defspace_reset (&value_defspace);
|
||||
auto saved_version = options.code.progsversion;
|
||||
options.code.progsversion = PROG_VERSION;
|
||||
sblock_t sblock = {
|
||||
.tail = &sblock.statements,
|
||||
};
|
||||
auto sb = statement_slist (&sblock, new_return_expr (e));
|
||||
if (sblock.next != sb && sb->statements) {
|
||||
internal_error (e, "statement_slist did too much");
|
||||
}
|
||||
free_sblock (sb);
|
||||
sblock.next = 0;
|
||||
|
||||
value_codespace.size = vf_foldconst * 16;
|
||||
for (auto s = sblock.statements; s; s = s->next) {
|
||||
auto opa = get_def (s->opa);
|
||||
auto opb = get_def (s->opb);
|
||||
auto opc = get_def (s->opc);
|
||||
auto inst = opcode_find (s->opcode, s->opa, s->opb, s->opc);
|
||||
auto ds = codespace_newstatement (&value_codespace);
|
||||
*ds = (dstatement_t) {
|
||||
.op = opcode_get (inst),
|
||||
.a = opa->offset,
|
||||
.b = opb->offset,
|
||||
.c = opc->offset,
|
||||
};
|
||||
}
|
||||
options.code.progsversion = saved_version;
|
||||
value_pr.pr_trace = options.verbosity > 1;
|
||||
PR_ExecuteProgram (&value_pr, vf_foldconst);
|
||||
auto val = new_type_value (e->e.expr.type, value_pr.pr_return_buffer);
|
||||
e = new_value_expr (val);
|
||||
return e;
|
||||
}
|
||||
|
|
|
@ -1241,6 +1241,19 @@ is_pointer_val (expr_t *e)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
is_math_val (expr_t *e)
|
||||
{
|
||||
if (e->type == ex_value && is_math (e->e.value->type)) {
|
||||
return 1;
|
||||
}
|
||||
if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const
|
||||
&& is_math (e->e.symbol->s.value->type)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
expr_t *
|
||||
new_alias_expr (type_t *type, expr_t *expr)
|
||||
{
|
||||
|
|
|
@ -331,7 +331,7 @@ free_statement (statement_t *s)
|
|||
FREE (statements, s);
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
free_sblock (sblock_t *sblock)
|
||||
{
|
||||
while (sblock->statements) {
|
||||
|
@ -662,7 +662,6 @@ typedef sblock_t *(*expr_f) (sblock_t *, expr_t *, operand_t **);
|
|||
|
||||
static sblock_t *statement_subexpr (sblock_t *sblock, expr_t *e,
|
||||
operand_t **op);
|
||||
static sblock_t *statement_slist (sblock_t *sblock, expr_t *e);
|
||||
static sblock_t *expr_symbol (sblock_t *sblock, expr_t *e, operand_t **op);
|
||||
static sblock_t *expr_def (sblock_t *sblock, expr_t *e, operand_t **op);
|
||||
|
||||
|
@ -2263,7 +2262,7 @@ statement_nonexec (sblock_t *sblock, expr_t *e)
|
|||
return sblock;
|
||||
}
|
||||
|
||||
static sblock_t *
|
||||
sblock_t *
|
||||
statement_slist (sblock_t *sblock, expr_t *e)
|
||||
{
|
||||
static statement_f sfuncs[ex_count] = {
|
||||
|
|
|
@ -560,6 +560,44 @@ make_def_imm (def_t *def, hashtab_t *tab, ex_value_t *val)
|
|||
return imm;
|
||||
}
|
||||
|
||||
def_t *
|
||||
emit_value_core (ex_value_t *val, def_t *def, defspace_t *data)
|
||||
{
|
||||
if (!def) {
|
||||
def = new_def (".imm", val->type, data, sc_static);
|
||||
}
|
||||
def->initialized = def->constant = 1;
|
||||
def->nosave = 1;
|
||||
// copy the immediate to the global area
|
||||
switch (val->lltype) {
|
||||
case ev_string:
|
||||
reloc_def_string (def);
|
||||
break;
|
||||
case ev_func:
|
||||
if (val->v.func_val.val) {
|
||||
reloc_t *reloc;
|
||||
reloc = new_reloc (def->space, def->offset, rel_def_func);
|
||||
reloc->next = pr.relocs;
|
||||
pr.relocs = reloc;
|
||||
}
|
||||
break;
|
||||
case ev_field:
|
||||
if (val->v.pointer.def)
|
||||
reloc_def_field_ofs (val->v.pointer.def, def);
|
||||
break;
|
||||
case ev_ptr:
|
||||
if (val->v.pointer.def) {
|
||||
EMIT_DEF_OFS (data, D_INT (def), val->v.pointer.def);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy (D_POINTER (pr_type_t, def), &val->v, 4 * type_size (val->type));
|
||||
return def;
|
||||
}
|
||||
|
||||
def_t *
|
||||
emit_value (ex_value_t *value, def_t *def)
|
||||
{
|
||||
|
@ -647,36 +685,8 @@ emit_value (ex_value_t *value, def_t *def)
|
|||
} else {
|
||||
cn = new_def (".imm", type, pr.near_data, sc_static);
|
||||
}
|
||||
cn->initialized = cn->constant = 1;
|
||||
cn->nosave = 1;
|
||||
// copy the immediate to the global area
|
||||
switch (val.lltype) {
|
||||
case ev_string:
|
||||
reloc_def_string (cn);
|
||||
break;
|
||||
case ev_func:
|
||||
if (val.v.func_val.val) {
|
||||
reloc_t *reloc;
|
||||
reloc = new_reloc (cn->space, cn->offset, rel_def_func);
|
||||
reloc->next = pr.relocs;
|
||||
pr.relocs = reloc;
|
||||
}
|
||||
break;
|
||||
case ev_field:
|
||||
if (val.v.pointer.def)
|
||||
reloc_def_field_ofs (val.v.pointer.def, cn);
|
||||
break;
|
||||
case ev_ptr:
|
||||
if (val.v.pointer.def) {
|
||||
EMIT_DEF_OFS (pr.near_data, D_INT (cn),
|
||||
val.v.pointer.def);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy (D_POINTER (pr_type_t, cn), &val.v, 4 * type_size (type));
|
||||
val.type = type;
|
||||
cn = emit_value_core (&val, cn, pr.near_data);
|
||||
|
||||
make_def_imm (cn, tab, &val);
|
||||
|
||||
|
|
Loading…
Reference in a new issue