[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:
Bill Currie 2023-08-21 12:32:17 +09:00
parent 27ccad40c9
commit dfb7862419
10 changed files with 193 additions and 39 deletions

View file

@ -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);
///@}

View file

@ -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.

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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"

View file

@ -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;
}

View file

@ -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)
{

View file

@ -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] = {

View file

@ -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);