mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-02-16 17:01:53 +00:00
This fixes the basic vecconst test (extending it to other types breaks because long and ulong are not properly supported yet). The conversion is done by the progs VM rather than writing another 256 conversions (though loops could be used). This works nicely as a test for using the VM to help with compiling.
834 lines
20 KiB
C
834 lines
20 KiB
C
/*
|
|
value.c
|
|
|
|
value handling
|
|
|
|
Copyright (C) 2002 Bill Currie <bill@taniwha.org>
|
|
|
|
Author: Bill Currie <bill@taniwha.org>
|
|
Date: 2002/06/04
|
|
|
|
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
|
|
|
|
#ifdef HAVE_STRING_H
|
|
# include <string.h>
|
|
#endif
|
|
#ifdef HAVE_STRINGS_H
|
|
# include <strings.h>
|
|
#endif
|
|
#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/va.h"
|
|
|
|
#include "QF/simd/types.h"
|
|
|
|
#include "tools/qfcc/include/qfcc.h"
|
|
#include "tools/qfcc/include/def.h"
|
|
#include "tools/qfcc/include/defspace.h"
|
|
#include "tools/qfcc/include/diagnostic.h"
|
|
#include "tools/qfcc/include/emit.h"
|
|
#include "tools/qfcc/include/expr.h"
|
|
#include "tools/qfcc/include/options.h"
|
|
#include "tools/qfcc/include/reloc.h"
|
|
#include "tools/qfcc/include/strpool.h"
|
|
#include "tools/qfcc/include/symtab.h"
|
|
#include "tools/qfcc/include/type.h"
|
|
#include "tools/qfcc/include/value.h"
|
|
|
|
//FIXME I don't like this being here (or aligned_alloc being necessary in
|
|
//the first place, but not at all sure what to do about that)
|
|
#ifdef _WIN32
|
|
#define aligned_alloc(al, sz) _aligned_malloc(sz, al)
|
|
#define free(x) _aligned_free(x)
|
|
#endif
|
|
|
|
typedef struct {
|
|
def_t *def;
|
|
union {
|
|
#define EV_TYPE(type) pr_##type##_t type##_val;
|
|
#include "QF/progs/pr_type_names.h"
|
|
#define VEC_TYPE(type_name, base_type) pr_##type_name##_t type_name##_val;
|
|
#include "tools/qfcc/include/vec_types.h"
|
|
ex_pointer_t pointer;
|
|
} i;
|
|
} immediate_t;
|
|
|
|
static hashtab_t *value_table;
|
|
static ex_value_t *values_freelist;
|
|
|
|
//FIXME this (to setup_value_progs) should be in its own file and more
|
|
//general (good for constant folding, too, and maybe some others).
|
|
static void
|
|
value_debug_handler (prdebug_t event, void *param, void *data)
|
|
{
|
|
progs_t *pr = data;
|
|
switch (event) {
|
|
case prd_trace:
|
|
dstatement_t *st = pr->pr_statements + pr->pr_xstatement;
|
|
PR_PrintStatement (pr, st, 0);
|
|
break;
|
|
case prd_breakpoint:
|
|
case prd_subenter:
|
|
case prd_subexit:
|
|
case prd_runerror:
|
|
case prd_watchpoint:
|
|
case prd_begin:
|
|
case prd_terminate:
|
|
case prd_error:
|
|
case prd_none:
|
|
break;
|
|
}
|
|
}
|
|
|
|
enum {
|
|
vf_null,
|
|
vf_convert,
|
|
};
|
|
|
|
#define BASE(b, base) (((base) & 3) << OP_##b##_SHIFT)
|
|
#define OP(a, b, c, op) ((op) | BASE(A, a) | BASE(B, b) | BASE(C, c))
|
|
|
|
static bfunction_t value_functions[] = {
|
|
{}, // null function
|
|
[vf_convert] = { .first_statement = vf_convert * 16 },
|
|
};
|
|
|
|
static __attribute__((aligned(64)))
|
|
dstatement_t value_statements[] = {
|
|
[vf_convert * 16 - 1] = {},
|
|
{ OP_CONV, 0, 07777, 16 },
|
|
{ OP_RETURN, 16, 0, 0 },
|
|
};
|
|
|
|
#define num_globals 16384
|
|
#define stack_size 8192
|
|
static __attribute__((aligned(64)))
|
|
pr_type_t value_globals[num_globals + 128] = {
|
|
[num_globals - stack_size] = { .uint_value = num_globals },
|
|
};
|
|
|
|
static dprograms_t value_progs = {
|
|
.version = PROG_VERSION,
|
|
.statements = {
|
|
.count = sizeof (value_statements) / sizeof (value_statements[0]),
|
|
},
|
|
};
|
|
static progs_t value_pr = {
|
|
.progs = &value_progs,
|
|
.debug_handler = value_debug_handler,
|
|
.debug_data = &value_pr,
|
|
.pr_trace = 1,
|
|
.pr_trace_depth = -1,
|
|
.function_table = value_functions,
|
|
.pr_statements = value_statements,
|
|
.globals_size = num_globals,
|
|
.pr_globals = value_globals,
|
|
.stack_bottom = num_globals - stack_size + 4,
|
|
.pr_return_buffer = value_globals + num_globals,
|
|
.pr_return = value_globals + num_globals,
|
|
.globals = {
|
|
.stack = (pr_ptr_t *) (value_globals + num_globals - stack_size),
|
|
}
|
|
};
|
|
|
|
static void
|
|
setup_value_progs (void)
|
|
{
|
|
PR_Init (&value_pr);
|
|
PR_Debug_Init (&value_pr);
|
|
}
|
|
|
|
static uintptr_t
|
|
value_get_hash (const void *_val, void *unused)
|
|
{
|
|
const ex_value_t *val = (const ex_value_t *) _val;
|
|
return Hash_Buffer (&val->v, sizeof (val->v)) ^ (uintptr_t) val->type;
|
|
}
|
|
|
|
static int
|
|
value_compare (const void *_val1, const void *_val2, void *unused)
|
|
{
|
|
const ex_value_t *val1 = (const ex_value_t *) _val1;
|
|
const ex_value_t *val2 = (const ex_value_t *) _val2;
|
|
if (val1->type != val2->type)
|
|
return 0;
|
|
return memcmp (&val1->v, &val2->v, sizeof (val1->v)) == 0;
|
|
}
|
|
|
|
static ex_value_t *
|
|
new_value (void)
|
|
{
|
|
ex_value_t *value;
|
|
#define malloc(x) aligned_alloc (__alignof__(ex_value_t), x)
|
|
ALLOC (256, ex_value_t, values, value);
|
|
#undef malloc
|
|
return value;
|
|
}
|
|
|
|
static void
|
|
set_val_type (ex_value_t *val, type_t *type)
|
|
{
|
|
val->type = type;
|
|
val->lltype = low_level_type (type);
|
|
}
|
|
|
|
static ex_value_t *
|
|
find_value (const ex_value_t *val)
|
|
{
|
|
ex_value_t *value;
|
|
|
|
value = Hash_FindElement (value_table, val);
|
|
if (value)
|
|
return value;
|
|
value = new_value ();
|
|
*value = *val;
|
|
Hash_AddElement (value_table, value);
|
|
return value;
|
|
}
|
|
|
|
ex_value_t *
|
|
new_string_val (const char *string_val)
|
|
{
|
|
ex_value_t val;
|
|
memset (&val, 0, sizeof (val));
|
|
set_val_type (&val, &type_string);
|
|
if (string_val)
|
|
val.v.string_val = save_string (string_val);
|
|
return find_value (&val);
|
|
}
|
|
|
|
ex_value_t *
|
|
new_double_val (double double_val)
|
|
{
|
|
ex_value_t val;
|
|
memset (&val, 0, sizeof (val));
|
|
set_val_type (&val, &type_double);
|
|
val.v.double_val = double_val;
|
|
return find_value (&val);
|
|
}
|
|
|
|
ex_value_t *
|
|
new_float_val (float float_val)
|
|
{
|
|
ex_value_t val;
|
|
memset (&val, 0, sizeof (val));
|
|
set_val_type (&val, &type_float);
|
|
val.v.float_val = float_val;
|
|
return find_value (&val);
|
|
}
|
|
|
|
ex_value_t *
|
|
new_vector_val (const float *vector_val)
|
|
{
|
|
ex_value_t val;
|
|
memset (&val, 0, sizeof (val));
|
|
set_val_type (&val, &type_vector);
|
|
VectorCopy (vector_val, val.v.vector_val);
|
|
return find_value (&val);
|
|
}
|
|
|
|
ex_value_t *
|
|
new_entity_val (int entity_val)
|
|
{
|
|
ex_value_t val;
|
|
memset (&val, 0, sizeof (val));
|
|
set_val_type (&val, &type_entity);
|
|
val.v.entity_val = entity_val;
|
|
return find_value (&val);
|
|
}
|
|
|
|
ex_value_t *
|
|
new_field_val (int field_val, type_t *type, def_t *def)
|
|
{
|
|
ex_value_t val;
|
|
memset (&val, 0, sizeof (val));
|
|
set_val_type (&val, field_type (type));
|
|
val.v.pointer.val = field_val;
|
|
val.v.pointer.type = type;
|
|
val.v.pointer.def = def;
|
|
return find_value (&val);
|
|
}
|
|
|
|
ex_value_t *
|
|
new_func_val (int func_val, type_t *type)
|
|
{
|
|
ex_value_t val;
|
|
memset (&val, 0, sizeof (val));
|
|
set_val_type (&val, type);
|
|
val.v.func_val.val = func_val;
|
|
val.v.func_val.type = type;
|
|
return find_value (&val);
|
|
}
|
|
|
|
ex_value_t *
|
|
new_pointer_val (int pointer_val, type_t *type, def_t *def,
|
|
struct operand_s *tempop)
|
|
{
|
|
ex_value_t val;
|
|
if (!type) {
|
|
internal_error (0, "pointer value with no type");
|
|
}
|
|
memset (&val, 0, sizeof (val));
|
|
set_val_type (&val, pointer_type (type));
|
|
val.v.pointer.val = pointer_val;
|
|
val.v.pointer.type = type;
|
|
val.v.pointer.def = def;
|
|
val.v.pointer.tempop = tempop;
|
|
return find_value (&val);
|
|
}
|
|
|
|
ex_value_t *
|
|
new_quaternion_val (const float *quaternion_val)
|
|
{
|
|
ex_value_t val;
|
|
memset (&val, 0, sizeof (val));
|
|
set_val_type (&val, &type_quaternion);
|
|
QuatCopy (quaternion_val, val.v.quaternion_val);
|
|
return find_value (&val);
|
|
}
|
|
|
|
ex_value_t *
|
|
new_int_val (int int_val)
|
|
{
|
|
ex_value_t val;
|
|
memset (&val, 0, sizeof (val));
|
|
set_val_type (&val, &type_int);
|
|
val.v.int_val = int_val;
|
|
return find_value (&val);
|
|
}
|
|
|
|
ex_value_t *
|
|
new_uint_val (int uint_val)
|
|
{
|
|
ex_value_t val;
|
|
memset (&val, 0, sizeof (val));
|
|
set_val_type (&val, &type_uint);
|
|
val.v.uint_val = uint_val;
|
|
return find_value (&val);
|
|
}
|
|
|
|
ex_value_t *
|
|
new_long_val (pr_long_t long_val)
|
|
{
|
|
ex_value_t val = { .v = { .long_val = long_val } };
|
|
set_val_type (&val, &type_long);
|
|
return find_value (&val);
|
|
}
|
|
|
|
ex_value_t *
|
|
new_ulong_val (pr_ulong_t ulong_val)
|
|
{
|
|
ex_value_t val = { .v = { .ulong_val = ulong_val } };
|
|
set_val_type (&val, &type_ulong);
|
|
return find_value (&val);
|
|
}
|
|
|
|
ex_value_t *
|
|
new_short_val (short short_val)
|
|
{
|
|
ex_value_t val;
|
|
memset (&val, 0, sizeof (val));
|
|
set_val_type (&val, &type_short);
|
|
val.v.short_val = short_val;
|
|
return find_value (&val);
|
|
}
|
|
|
|
ex_value_t *
|
|
new_nil_val (type_t *type)
|
|
{
|
|
ex_value_t val;
|
|
memset (&val, 0, sizeof (val));
|
|
set_val_type (&val, type);
|
|
if (val.lltype == ev_void) {
|
|
val.lltype = type_nil->type;
|
|
}
|
|
if (val.lltype == ev_ptr || val.lltype == ev_field )
|
|
val.v.pointer.type = type->t.fldptr.type;
|
|
if (val.lltype == ev_func)
|
|
val.v.func_val.type = type;
|
|
return find_value (&val);
|
|
}
|
|
|
|
ex_value_t *
|
|
new_type_value (const type_t *type, const pr_type_t *data)
|
|
{
|
|
size_t typeSize = type_size (type) * sizeof (pr_type_t);
|
|
ex_value_t val = {};
|
|
set_val_type (&val, (type_t *) type);//FIXME cast
|
|
memcpy (&val.v, data, typeSize);
|
|
return find_value (&val);
|
|
}
|
|
|
|
void
|
|
value_store (pr_type_t *dst, const type_t *dstType, const expr_t *src)
|
|
{
|
|
size_t dstSize = type_size (dstType) * sizeof (pr_type_t);
|
|
|
|
if (src->type == ex_nil) {
|
|
memset (dst, 0, dstSize);
|
|
return;
|
|
}
|
|
if (src->type == ex_symbol && src->e.symbol->sy_type == sy_var) {
|
|
// initialized global def treated as a constant
|
|
// from the tests in cast_expr, the def is known to be constant
|
|
def_t *def = src->e.symbol->s.def;
|
|
memcpy (dst, &D_PACKED (pr_type_t, def), dstSize);
|
|
return;
|
|
}
|
|
ex_value_t *val = 0;
|
|
if (src->type == ex_value) {
|
|
val = src->e.value;
|
|
}
|
|
if (src->type == ex_symbol && src->e.symbol->sy_type == sy_const) {
|
|
val = src->e.symbol->s.value;
|
|
}
|
|
if (!val) {
|
|
internal_error (src, "unexpected constant expression type");
|
|
}
|
|
memcpy (dst, &val->v, dstSize);
|
|
}
|
|
|
|
const char *
|
|
get_value_string (const ex_value_t *value)
|
|
{
|
|
const type_t *type = value->type;
|
|
const char *str = "";
|
|
switch (type->type) {
|
|
case ev_string:
|
|
return va (0, "\"%s\"", quote_string (value->v.string_val));
|
|
case ev_vector:
|
|
case ev_quaternion:
|
|
case ev_float:
|
|
switch (type_width (type)) {
|
|
case 1:
|
|
str = va (0, "%.9g", value->v.float_val);
|
|
break;
|
|
case 2:
|
|
str = va (0, VEC2F_FMT, VEC2_EXP (value->v.vec2_val));
|
|
break;
|
|
case 3:
|
|
str = va (0, "[%.9g, %.9g, %.9g]",
|
|
VectorExpand (value->v.vec3_val));
|
|
break;
|
|
case 4:
|
|
str = va (0, VEC4F_FMT, VEC4_EXP (value->v.vec4_val));
|
|
break;
|
|
}
|
|
return va (0, "%s %s", type->name, str);
|
|
case ev_entity:
|
|
case ev_func:
|
|
return va (0, "%s %d", type->name, value->v.int_val);
|
|
case ev_field:
|
|
if (value->v.pointer.def) {
|
|
int offset = value->v.pointer.val;
|
|
offset += value->v.pointer.def->offset;
|
|
return va (0, "field %d", offset);
|
|
} else {
|
|
return va (0, "field %d", value->v.pointer.val);
|
|
}
|
|
case ev_ptr:
|
|
if (value->v.pointer.def) {
|
|
str = va (0, "<%s>", value->v.pointer.def->name);
|
|
}
|
|
return va (0, "(* %s)[%d]%s",
|
|
value->v.pointer.type
|
|
? get_type_string (value->v.pointer.type) : "???",
|
|
value->v.pointer.val, str);
|
|
case ev_int:
|
|
switch (type_width (type)) {
|
|
case 1:
|
|
str = va (0, "%"PRIi32, value->v.int_val);
|
|
break;
|
|
case 2:
|
|
str = va (0, VEC2I_FMT, VEC2_EXP (value->v.ivec2_val));
|
|
break;
|
|
case 3:
|
|
str = va (0, "[%"PRIi32", %"PRIi32", %"PRIi32"]",
|
|
VectorExpand (value->v.ivec3_val));
|
|
break;
|
|
case 4:
|
|
str = va (0, VEC4I_FMT, VEC4_EXP (value->v.ivec4_val));
|
|
break;
|
|
}
|
|
return va (0, "%s %s", type->name, str);
|
|
case ev_uint:
|
|
switch (type_width (type)) {
|
|
case 1:
|
|
str = va (0, "%"PRIu32, value->v.uint_val);
|
|
break;
|
|
case 2:
|
|
str = va (0, "[%"PRIu32", %"PRIi32"]",
|
|
VEC2_EXP (value->v.uivec2_val));
|
|
break;
|
|
case 3:
|
|
str = va (0, "[%"PRIu32", %"PRIi32", %"PRIi32"]",
|
|
VectorExpand (value->v.uivec3_val));
|
|
break;
|
|
case 4:
|
|
str = va (0, "[%"PRIu32", %"PRIi32", %"PRIi32", %"PRIi32"]",
|
|
VEC4_EXP (value->v.uivec4_val));
|
|
break;
|
|
}
|
|
return va (0, "%s %s", type->name, str);
|
|
case ev_short:
|
|
return va (0, "%s %"PRIi16, type->name, value->v.short_val);
|
|
case ev_ushort:
|
|
return va (0, "%s %"PRIu16, type->name, value->v.ushort_val);
|
|
case ev_double:
|
|
switch (type_width (type)) {
|
|
case 1:
|
|
str = va (0, "%.17g", value->v.double_val);
|
|
break;
|
|
case 2:
|
|
str = va (0, VEC2D_FMT, VEC2_EXP (value->v.dvec2_val));
|
|
break;
|
|
case 3:
|
|
str = va (0, "[%.17g, %.17g, %.17g]",
|
|
VectorExpand (value->v.dvec3_val));
|
|
break;
|
|
case 4:
|
|
str = va (0, VEC4D_FMT, VEC4_EXP (value->v.dvec4_val));
|
|
break;
|
|
}
|
|
return va (0, "%s %s", type->name, str);
|
|
case ev_long:
|
|
switch (type_width (type)) {
|
|
case 1:
|
|
str = va (0, "%"PRIi64, value->v.long_val);
|
|
break;
|
|
case 2:
|
|
str = va (0, VEC2L_FMT, VEC2_EXP (value->v.lvec2_val));
|
|
break;
|
|
case 3:
|
|
str = va (0, "[%"PRIi64", %"PRIi64", %"PRIi64"]",
|
|
VectorExpand (value->v.lvec3_val));
|
|
break;
|
|
case 4:
|
|
str = va (0, VEC4L_FMT, VEC4_EXP (value->v.lvec4_val));
|
|
break;
|
|
}
|
|
return va (0, "%s %s", type->name, str);
|
|
case ev_ulong:
|
|
switch (type_width (type)) {
|
|
case 1:
|
|
str = va (0, "%"PRIu64, value->v.ulong_val);
|
|
break;
|
|
case 2:
|
|
str = va (0, "[%"PRIu64", %"PRIi64"]",
|
|
VEC2_EXP (value->v.ulvec2_val));
|
|
break;
|
|
case 3:
|
|
str = va (0, "[%"PRIu64", %"PRIi64", %"PRIi64"]",
|
|
VectorExpand (value->v.ulvec3_val));
|
|
break;
|
|
case 4:
|
|
str = va (0, "[%"PRIu64", %"PRIi64", %"PRIi64", %"PRIi64"]",
|
|
VEC4_EXP (value->v.ulvec4_val));
|
|
break;
|
|
}
|
|
return va (0, "%s %s", type->name, str);
|
|
case ev_void:
|
|
return "<void>";
|
|
case ev_invalid:
|
|
return "<invalid>";
|
|
case ev_type_count:
|
|
return "<type_count>";
|
|
}
|
|
return "invalid type";
|
|
}
|
|
|
|
static hashtab_t *string_imm_defs;
|
|
static hashtab_t *fldptr_imm_defs;
|
|
static hashtab_t *value_imm_defs;
|
|
|
|
static void
|
|
imm_free (void *_imm, void *unused)
|
|
{
|
|
free (_imm);
|
|
}
|
|
|
|
static __attribute__((pure)) uintptr_t
|
|
imm_get_hash (const void *_imm, void *_tab)
|
|
{
|
|
immediate_t *imm = (immediate_t *) _imm;
|
|
hashtab_t **tab = (hashtab_t **) _tab;
|
|
|
|
if (tab == &string_imm_defs) {
|
|
const char *str = pr.strings->strings + imm->i.string_val;
|
|
return str ? Hash_String (str) : 0;
|
|
} else if (tab == &fldptr_imm_defs) {
|
|
return Hash_Buffer (&imm->i.pointer, sizeof (&imm->i.pointer));
|
|
} else if (tab == &value_imm_defs) {
|
|
size_t size = type_size (imm->def->type) * sizeof (pr_type_t);
|
|
return Hash_Buffer (&imm->i, size) ^ (uintptr_t) imm->def->type;
|
|
} else {
|
|
internal_error (0, "invalid immediate hash table");
|
|
}
|
|
}
|
|
|
|
static __attribute__((pure)) int
|
|
imm_compare (const void *_imm1, const void *_imm2, void *_tab)
|
|
{
|
|
immediate_t *imm1 = (immediate_t *) _imm1;
|
|
immediate_t *imm2 = (immediate_t *) _imm2;
|
|
hashtab_t **tab = (hashtab_t **) _tab;
|
|
|
|
if (tab == &string_imm_defs) {
|
|
const char *str1 = pr.strings->strings + imm1->i.string_val;
|
|
const char *str2 = pr.strings->strings + imm2->i.string_val;
|
|
return (str1 == str2 || (str1 && str2 && !strcmp (str1, str2)));
|
|
} else if (tab == &fldptr_imm_defs) {
|
|
return !memcmp (&imm1->i.pointer, &imm2->i.pointer,
|
|
sizeof (imm1->i.pointer));
|
|
} else if (tab == &value_imm_defs) {
|
|
size_t size = type_size (imm1->def->type) * sizeof (pr_type_t);
|
|
return !memcmp (&imm1->i, &imm2->i, size);
|
|
} else {
|
|
internal_error (0, "invalid immediate hash table");
|
|
}
|
|
}
|
|
|
|
int
|
|
ReuseString (const char *str)
|
|
{
|
|
return strpool_addstr (pr.strings, str);
|
|
}
|
|
|
|
ex_value_t *
|
|
convert_value (ex_value_t *value, type_t *type)
|
|
{
|
|
if (!is_math (type) || !is_math (value->type)) {
|
|
error (0, "unable to convert non-math value");
|
|
return value;
|
|
}
|
|
if (type_width (type) != type_width (value->type)) {
|
|
error (0, "unable to convert between values of different widths");
|
|
return value;
|
|
}
|
|
int from = type_cast_map[base_type (value->type)->type];
|
|
int to = type_cast_map[base_type (type)->type];
|
|
int width = type_width (value->type) - 1;
|
|
int conv = TYPE_CAST_CODE (from, to, width);
|
|
int addr = value_functions[vf_convert].first_statement;
|
|
value_statements[addr + 0].b = conv;
|
|
value_statements[addr + 1].c = width; // width is actual width - 1
|
|
memcpy (value_globals, &value->v,
|
|
type_size (value->type) * sizeof (pr_type_t));
|
|
value_pr.pr_trace = options.verbosity > 1;
|
|
PR_ExecuteProgram (&value_pr, vf_convert);
|
|
return new_type_value (type, value_pr.pr_return_buffer);
|
|
}
|
|
|
|
ex_value_t *
|
|
alias_value (ex_value_t *value, type_t *type)
|
|
{
|
|
ex_value_t new;
|
|
|
|
if (type_size (type) != type_size (ev_types[value->lltype])) {
|
|
error (0, "unable to alias different sized values");
|
|
return value;
|
|
}
|
|
new = *value;
|
|
set_val_type (&new, type);
|
|
return find_value (&new);
|
|
}
|
|
|
|
static immediate_t *
|
|
make_def_imm (def_t *def, hashtab_t *tab, ex_value_t *val)
|
|
{
|
|
immediate_t *imm;
|
|
|
|
imm = aligned_alloc (__alignof__(immediate_t), sizeof (immediate_t));
|
|
memset (imm, 0, sizeof (immediate_t));
|
|
imm->def = def;
|
|
memcpy (&imm->i, &val->v, sizeof (imm->i));
|
|
|
|
Hash_AddElement (tab, imm);
|
|
|
|
return imm;
|
|
}
|
|
|
|
def_t *
|
|
emit_value (ex_value_t *value, def_t *def)
|
|
{
|
|
def_t *cn;
|
|
hashtab_t *tab = 0;
|
|
type_t *type;
|
|
ex_value_t val = *value;
|
|
|
|
if (!string_imm_defs) {
|
|
clear_immediates ();
|
|
}
|
|
cn = 0;
|
|
// if (val.type == ev_void)
|
|
// val.type = type_nil->type;
|
|
switch (val.lltype) {
|
|
case ev_entity:
|
|
case ev_func:
|
|
case ev_int:
|
|
case ev_uint:
|
|
case ev_float:
|
|
case ev_vector:
|
|
case ev_quaternion:
|
|
case ev_double:
|
|
tab = value_imm_defs;
|
|
type = val.type;
|
|
break;
|
|
case ev_field:
|
|
case ev_ptr:
|
|
tab = fldptr_imm_defs;
|
|
type = ev_types[val.lltype];
|
|
break;
|
|
case ev_string:
|
|
val.v.int_val = ReuseString (val.v.string_val);
|
|
tab = string_imm_defs;
|
|
type = &type_string;
|
|
break;
|
|
default:
|
|
internal_error (0, "unexpected value type: %s",
|
|
val.type->type < ev_type_count
|
|
? pr_type_name[val.lltype]
|
|
: va (0, "%d", val.lltype));
|
|
}
|
|
def_t search_def = { .type = type };
|
|
immediate_t search = { .def = &search_def };
|
|
memcpy (&search.i, &val.v, sizeof (search.i));
|
|
immediate_t *imm = Hash_FindElement (tab, &search);
|
|
if (imm && strcmp (imm->def->name, ".zero") == 0) {
|
|
if (def) {
|
|
imm = 0; //FIXME do full def aliasing
|
|
} else {
|
|
symbol_t *sym;
|
|
sym = make_symbol (".zero", &type_zero, 0, sc_extern);
|
|
return sym->s.def;
|
|
}
|
|
}
|
|
if (imm) {
|
|
cn = imm->def;
|
|
if (def) {
|
|
defspace_free_loc (def->space, def->offset, type_size (def->type));
|
|
def->offset = cn->offset;
|
|
def->initialized = def->constant = 1;
|
|
def->nosave = 1;
|
|
def->local = 0;
|
|
cn = def;
|
|
} else {
|
|
if (cn->type != type) {
|
|
def = new_def (".imm", type, pr.near_data, sc_static);
|
|
def->offset = cn->offset;
|
|
cn = def;
|
|
}
|
|
}
|
|
return cn;
|
|
}
|
|
// allocate a new one
|
|
// always share immediates
|
|
if (def) {
|
|
if (def->type != type) {
|
|
cn = new_def (".imm", type, pr.near_data, sc_static);
|
|
cn->offset = def->offset;
|
|
} else {
|
|
cn = 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));
|
|
|
|
make_def_imm (cn, tab, &val);
|
|
|
|
return cn;
|
|
}
|
|
|
|
void
|
|
clear_immediates (void)
|
|
{
|
|
def_t *def;
|
|
ex_value_t zero_val;
|
|
|
|
if (value_table) {
|
|
Hash_FlushTable (value_table);
|
|
Hash_FlushTable (string_imm_defs);
|
|
Hash_FlushTable (fldptr_imm_defs);
|
|
Hash_FlushTable (value_imm_defs);
|
|
} else {
|
|
setup_value_progs ();
|
|
|
|
value_table = Hash_NewTable (16381, 0, 0, 0, 0);
|
|
Hash_SetHashCompare (value_table, value_get_hash, value_compare);
|
|
|
|
string_imm_defs = Hash_NewTable (16381, 0, imm_free,
|
|
&string_imm_defs, 0);
|
|
Hash_SetHashCompare (string_imm_defs, imm_get_hash, imm_compare);
|
|
|
|
fldptr_imm_defs = Hash_NewTable (16381, 0, imm_free,
|
|
&fldptr_imm_defs, 0);
|
|
Hash_SetHashCompare (fldptr_imm_defs, imm_get_hash, imm_compare);
|
|
|
|
value_imm_defs = Hash_NewTable (16381, 0, imm_free,
|
|
&value_imm_defs, 0);
|
|
Hash_SetHashCompare (value_imm_defs, imm_get_hash, imm_compare);
|
|
}
|
|
|
|
def = make_symbol (".zero", &type_zero, 0, sc_extern)->s.def;
|
|
|
|
memset (&zero_val, 0, sizeof (zero_val));
|
|
make_def_imm (def, string_imm_defs, &zero_val);
|
|
make_def_imm (def, fldptr_imm_defs, &zero_val);
|
|
make_def_imm (def, value_imm_defs, &zero_val);
|
|
}
|