mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-02-17 01:11:45 +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;
|
||||||
struct ex_value_s *convert_value (struct ex_value_s *value,
|
struct ex_value_s *convert_value (struct ex_value_s *value,
|
||||||
struct type_s *type);
|
struct type_s *type);
|
||||||
|
struct expr_s *evaluate_constexpr (struct expr_s *e);
|
||||||
void setup_value_progs (void);
|
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_short_val (expr_t *e) __attribute__((pure));
|
||||||
int is_integral_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_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.
|
/** 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);
|
void free_operand (operand_t *op);
|
||||||
|
|
||||||
sblock_t *new_sblock (void);
|
sblock_t *new_sblock (void);
|
||||||
|
void free_sblock (sblock_t *sblock);
|
||||||
|
|
||||||
statement_t *new_statement (st_type_t type, const char *opcode,
|
statement_t *new_statement (st_type_t type, const char *opcode,
|
||||||
struct expr_s *expr);
|
struct expr_s *expr);
|
||||||
int statement_is_cond (statement_t *s) __attribute__((pure));
|
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);
|
sblock_t **statement_get_targetlist (statement_t *s);
|
||||||
void sblock_add_statement (sblock_t *sblock, statement_t *statement);
|
void sblock_add_statement (sblock_t *sblock, statement_t *statement);
|
||||||
sblock_t *make_statements (struct expr_s *expr);
|
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 statements_count_temps (sblock_t *sblock);
|
||||||
|
|
||||||
void print_operand (operand_t *op);
|
void print_operand (operand_t *op);
|
||||||
|
|
|
@ -41,6 +41,7 @@ struct ex_value_s;
|
||||||
struct tempop_s;
|
struct tempop_s;
|
||||||
struct type_s;
|
struct type_s;
|
||||||
struct pr_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_string_val (const char *string_val);
|
||||||
struct ex_value_s *new_double_val (double double_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 struct expr_s *src);
|
||||||
const char *get_value_string (const struct ex_value_s *value);
|
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 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 (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);
|
int ReuseString (const char *str);
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
#include <QF/mathlib.h>
|
#include <QF/mathlib.h>
|
||||||
|
|
||||||
#include "tools/qfcc/include/diagnostic.h"
|
#include "tools/qfcc/include/diagnostic.h"
|
||||||
|
#include "tools/qfcc/include/evaluate.h"
|
||||||
#include "tools/qfcc/include/expr.h"
|
#include "tools/qfcc/include/expr.h"
|
||||||
#include "tools/qfcc/include/options.h"
|
#include "tools/qfcc/include/options.h"
|
||||||
#include "tools/qfcc/include/qfcc.h"
|
#include "tools/qfcc/include/qfcc.h"
|
||||||
|
@ -1696,6 +1697,9 @@ fold_constants (expr_t *e)
|
||||||
if (!e1) {
|
if (!e1) {
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
if (is_math (get_type (e1))) {
|
||||||
|
return evaluate_constexpr (e);
|
||||||
|
}
|
||||||
op = e->e.expr.op;
|
op = e->e.expr.op;
|
||||||
t1 = extract_type (e1);
|
t1 = extract_type (e1);
|
||||||
if (t1 >= ev_type_count || !do_unary_op[t1]) {
|
if (t1 >= ev_type_count || !do_unary_op[t1]) {
|
||||||
|
@ -1710,6 +1714,10 @@ fold_constants (expr_t *e)
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_math_val (e1) && is_math_val (e2)) {
|
||||||
|
return evaluate_constexpr (e);
|
||||||
|
}
|
||||||
|
|
||||||
op = e->e.expr.op;
|
op = e->e.expr.op;
|
||||||
|
|
||||||
t1 = extract_type (e1);
|
t1 = extract_type (e1);
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
#include "tools/qfcc/include/diagnostic.h"
|
#include "tools/qfcc/include/diagnostic.h"
|
||||||
#include "tools/qfcc/include/emit.h"
|
#include "tools/qfcc/include/emit.h"
|
||||||
#include "tools/qfcc/include/expr.h"
|
#include "tools/qfcc/include/expr.h"
|
||||||
|
#include "tools/qfcc/include/evaluate.h"
|
||||||
#include "tools/qfcc/include/function.h"
|
#include "tools/qfcc/include/function.h"
|
||||||
#include "tools/qfcc/include/options.h"
|
#include "tools/qfcc/include/options.h"
|
||||||
#include "tools/qfcc/include/reloc.h"
|
#include "tools/qfcc/include/reloc.h"
|
||||||
|
|
|
@ -34,18 +34,19 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.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/progs.h"
|
||||||
|
|
||||||
#include "QF/simd/types.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/evaluate.h"
|
||||||
#include "tools/qfcc/include/diagnostic.h"
|
#include "tools/qfcc/include/diagnostic.h"
|
||||||
|
#include "tools/qfcc/include/dot.h"
|
||||||
#include "tools/qfcc/include/expr.h"
|
#include "tools/qfcc/include/expr.h"
|
||||||
|
#include "tools/qfcc/include/opcodes.h"
|
||||||
#include "tools/qfcc/include/options.h"
|
#include "tools/qfcc/include/options.h"
|
||||||
|
#include "tools/qfcc/include/statements.h"
|
||||||
#include "tools/qfcc/include/type.h"
|
#include "tools/qfcc/include/type.h"
|
||||||
#include "tools/qfcc/include/value.h"
|
#include "tools/qfcc/include/value.h"
|
||||||
|
|
||||||
|
@ -77,6 +78,9 @@ value_debug_handler (prdebug_t event, void *param, void *data)
|
||||||
enum {
|
enum {
|
||||||
vf_null,
|
vf_null,
|
||||||
vf_convert,
|
vf_convert,
|
||||||
|
vf_foldconst,
|
||||||
|
|
||||||
|
vf_num_functions
|
||||||
};
|
};
|
||||||
|
|
||||||
#define BASE(b, base) (((base) & 3) << OP_##b##_SHIFT)
|
#define BASE(b, base) (((base) & 3) << OP_##b##_SHIFT)
|
||||||
|
@ -85,6 +89,7 @@ enum {
|
||||||
static bfunction_t value_functions[] = {
|
static bfunction_t value_functions[] = {
|
||||||
{}, // null function
|
{}, // null function
|
||||||
[vf_convert] = { .first_statement = vf_convert * 16 },
|
[vf_convert] = { .first_statement = vf_convert * 16 },
|
||||||
|
[vf_foldconst] = { .first_statement = vf_foldconst * 16 },
|
||||||
};
|
};
|
||||||
|
|
||||||
static __attribute__((aligned(64)))
|
static __attribute__((aligned(64)))
|
||||||
|
@ -92,6 +97,15 @@ dstatement_t value_statements[] = {
|
||||||
[vf_convert * 16 - 1] = {},
|
[vf_convert * 16 - 1] = {},
|
||||||
{ OP_CONV, 0, 07777, 16 },
|
{ OP_CONV, 0, 07777, 16 },
|
||||||
{ OP_RETURN, 16, 0, 0 },
|
{ 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
|
#define num_globals 16384
|
||||||
|
@ -100,6 +114,13 @@ static __attribute__((aligned(64)))
|
||||||
pr_type_t value_globals[num_globals + 128] = {
|
pr_type_t value_globals[num_globals + 128] = {
|
||||||
[num_globals - stack_size] = { .uint_value = num_globals },
|
[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 = {
|
static dprograms_t value_progs = {
|
||||||
.version = PROG_VERSION,
|
.version = PROG_VERSION,
|
||||||
|
@ -156,3 +177,99 @@ convert_value (ex_value_t *value, type_t *type)
|
||||||
PR_ExecuteProgram (&value_pr, vf_convert);
|
PR_ExecuteProgram (&value_pr, vf_convert);
|
||||||
return new_type_value (type, value_pr.pr_return_buffer);
|
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;
|
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 *
|
expr_t *
|
||||||
new_alias_expr (type_t *type, expr_t *expr)
|
new_alias_expr (type_t *type, expr_t *expr)
|
||||||
{
|
{
|
||||||
|
|
|
@ -331,7 +331,7 @@ free_statement (statement_t *s)
|
||||||
FREE (statements, s);
|
FREE (statements, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
void
|
||||||
free_sblock (sblock_t *sblock)
|
free_sblock (sblock_t *sblock)
|
||||||
{
|
{
|
||||||
while (sblock->statements) {
|
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,
|
static sblock_t *statement_subexpr (sblock_t *sblock, expr_t *e,
|
||||||
operand_t **op);
|
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_symbol (sblock_t *sblock, expr_t *e, operand_t **op);
|
||||||
static sblock_t *expr_def (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;
|
return sblock;
|
||||||
}
|
}
|
||||||
|
|
||||||
static sblock_t *
|
sblock_t *
|
||||||
statement_slist (sblock_t *sblock, expr_t *e)
|
statement_slist (sblock_t *sblock, expr_t *e)
|
||||||
{
|
{
|
||||||
static statement_f sfuncs[ex_count] = {
|
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;
|
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 *
|
def_t *
|
||||||
emit_value (ex_value_t *value, def_t *def)
|
emit_value (ex_value_t *value, def_t *def)
|
||||||
{
|
{
|
||||||
|
@ -647,36 +685,8 @@ emit_value (ex_value_t *value, def_t *def)
|
||||||
} else {
|
} else {
|
||||||
cn = new_def (".imm", type, pr.near_data, sc_static);
|
cn = new_def (".imm", type, pr.near_data, sc_static);
|
||||||
}
|
}
|
||||||
cn->initialized = cn->constant = 1;
|
val.type = type;
|
||||||
cn->nosave = 1;
|
cn = emit_value_core (&val, cn, pr.near_data);
|
||||||
// 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));
|
|
||||||
|
|
||||||
make_def_imm (cn, tab, &val);
|
make_def_imm (cn, tab, &val);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue