[qfcc] Add support for unsigned, long, etc

long is ignored for double, and v6p progs are stuck with 32 bits for
longs (don't feel like extending v6p any further), but the basics are
there for Ruamoko.

short is ignored for ints because the minimum size is 32, and signed is
just noise for ints anyway (and no chars, so...).

unsigned, however, is finally implemented properly (or at least seems to
be working correctly: tests pass after getting things compiling again,
and lt.u is used where it should be :)
This commit is contained in:
Bill Currie 2022-01-19 18:08:58 +09:00
parent e11fa77a34
commit df890432b7
7 changed files with 225 additions and 64 deletions

View file

@ -7,5 +7,4 @@ typedef int int16_t;
typedef int int32_t;
typedef int int64_t;
typedef int size_t;
typedef int long;
typedef int char;

View file

@ -104,6 +104,8 @@ extern type_t type_floatfield;
extern type_t *type_nil; // for passing nil into ...
extern type_t *type_default; // default type (float or int)
extern type_t *type_long_int; // supported type for long
extern type_t *type_ulong_uint;// supported type for ulong
extern type_t type_va_list;
extern type_t type_param;

View file

@ -290,23 +290,23 @@ static expr_type_t int_int[] = {
};
static expr_type_t int_uint[] = {
{'+', &type_int},
{'-', &type_int},
{'*', &type_int},
{'/', &type_int},
{'&', &type_int},
{'|', &type_int},
{'^', &type_int},
{'%', &type_int},
{MOD, &type_int},
{SHL, &type_int},
{SHR, &type_int},
{EQ, &type_int},
{NE, &type_int},
{LE, &type_int},
{GE, &type_int},
{LT, &type_int},
{GT, &type_int},
{'+', &type_int, 0, &type_int},
{'-', &type_int, 0, &type_int},
{'*', &type_int, 0, &type_int},
{'/', &type_int, 0, &type_int},
{'&', &type_int, 0, &type_int},
{'|', &type_int, 0, &type_int},
{'^', &type_int, 0, &type_int},
{'%', &type_int, 0, &type_int},
{MOD, &type_int, 0, &type_int},
{SHL, &type_int, 0, &type_int},
{SHR, &type_int, 0, &type_int},
{EQ, &type_int, 0, &type_int},
{NE, &type_int, 0, &type_int},
{LE, &type_int, 0, &type_int},
{GE, &type_int, 0, &type_int},
{LT, &type_int, 0, &type_int},
{GT, &type_int, 0, &type_int},
{0, 0}
};
@ -353,23 +353,23 @@ static expr_type_t int_double[] = {
#define uint_quat int_quat
static expr_type_t uint_int[] = {
{'+', &type_int},
{'-', &type_int},
{'*', &type_int},
{'/', &type_int},
{'&', &type_int},
{'|', &type_int},
{'^', &type_int},
{'%', &type_int},
{MOD, &type_int},
{SHL, &type_uint},
{SHR, &type_uint},
{EQ, &type_int},
{NE, &type_int},
{LE, &type_int},
{GE, &type_int},
{LT, &type_int},
{GT, &type_int},
{'+', &type_int, &type_int, &type_int },
{'-', &type_int, &type_int, &type_int },
{'*', &type_int, &type_int, &type_int },
{'/', &type_int, &type_int, &type_int },
{'&', &type_int, &type_int, &type_int },
{'|', &type_int, &type_int, &type_int },
{'^', &type_int, &type_int, &type_int },
{'%', &type_int, &type_int, &type_int },
{MOD, &type_int, &type_int, &type_int },
{SHL, &type_uint, &type_int, &type_int },
{SHR, &type_uint, 0, &type_int },
{EQ, &type_int, &type_int, &type_int },
{NE, &type_int, &type_int, &type_int },
{LE, &type_int, &type_int, &type_int },
{GE, &type_int, &type_int, &type_int },
{LT, &type_int, &type_int, &type_int },
{GT, &type_int, &type_int, &type_int },
{0, 0}
};
@ -385,8 +385,8 @@ static expr_type_t uint_uint[] = {
{MOD, &type_uint},
{SHL, &type_uint},
{SHR, &type_uint},
{EQ, &type_int},
{NE, &type_int},
{EQ, &type_int, &type_int, &type_int},
{NE, &type_int, &type_int, &type_int},
{LE, &type_int},
{GE, &type_int},
{LT, &type_int},

View file

@ -47,8 +47,38 @@
#include "tools/qfcc/include/statements.h"
#include "tools/qfcc/include/type.h"
typedef struct v6p_uint_opcode_s {
pr_opcode_v6p_e op;
v6p_opcode_t opcode;
} v6p_uint_opcode_t;
static v6p_uint_opcode_t v6p_uint_opcodes[] = {
{OP_LOAD_I_v6p, {".", "load.i", ev_entity, ev_field, ev_uint }},
{OP_LOADBI_I_v6p, {".", "loadbi.i", ev_ptr, ev_short, ev_uint }},
{OP_ADDRESS_I_v6p, {"&", "address.i", ev_uint, ev_invalid, ev_ptr }},
{OP_STORE_I_v6p, {"=", "store.i", ev_uint, ev_uint, ev_invalid }},
{OP_STOREP_I_v6p, {".=", "storep.i", ev_uint, ev_ptr, ev_invalid }},
{OP_STOREB_I_v6p, {".=", "storeb.i", ev_uint, ev_ptr, ev_int }},
{OP_STOREBI_I_v6p, {".=", "storebi.i", ev_uint, ev_ptr, ev_short }},
{OP_IF_v6p, {"<IF>", "if", ev_uint, ev_short, ev_invalid }},
{OP_IFNOT_v6p, {"<IFNOT>", "ifnot", ev_uint, ev_short, ev_invalid }},
{OP_ADD_I_v6p, {"+", "add.i", ev_uint, ev_uint, ev_uint }},
{OP_SUB_I_v6p, {"-", "sub.i", ev_uint, ev_uint, ev_uint }},
{OP_MUL_I_v6p, {"*", "mul.i", ev_uint, ev_uint, ev_uint }},
{OP_DIV_I_v6p, {"/", "div.i", ev_uint, ev_uint, ev_uint }},
{OP_BITAND_I_v6p, {"&", "bitand.i", ev_uint, ev_uint, ev_uint }},
{OP_BITOR_I_v6p, {"|", "bitor.i", ev_uint, ev_uint, ev_uint }},
{OP_BITXOR_I_v6p, {"^", "bitxor.i", ev_uint, ev_uint, ev_uint }},
{OP_REM_I_v6p, {"%", "rem.i", ev_uint, ev_uint, ev_uint }},
{OP_MOD_I_v6p, {"%%", "mod.i", ev_uint, ev_uint, ev_uint }},
{OP_SHL_I_v6p, {"<<", "shl.i", ev_uint, ev_uint, ev_uint }},
{OP_BITNOT_I_v6p, {"<<", "bitnot.i", ev_uint, ev_invalid, ev_int }},
{}
};
static hashtab_t *v6p_opcode_type_table;
static hashtab_t *v6p_opcode_void_table;
static hashtab_t *v6p_opcode_uint_table;
static v6p_opcode_t *v6p_opcode_map;
static hashtab_t *rua_opcode_type_table;
@ -86,6 +116,21 @@ v6p_get_key (const void *op, void *unused)
return ((v6p_opcode_t *) op)->name;
}
static uintptr_t
v6p_uint_get_hash (const void *_op, void *_tab)
{
__auto_type uint_op = (v6p_uint_opcode_t *) _op;
return v6p_get_hash (&uint_op->opcode, _tab);
}
static int
v6p_uint_compare (const void *_opa, const void *_opb, void *data)
{
__auto_type uint_opa = (v6p_uint_opcode_t *) _opa;
__auto_type uint_opb = (v6p_uint_opcode_t *) _opb;
return v6p_compare (&uint_opa->opcode, &uint_opb->opcode, data);
}
static uintptr_t
rua_get_hash (const void *_op, void *_tab)
{
@ -139,17 +184,25 @@ static v6p_opcode_t *
v6p_opcode_find (const char *name, operand_t *op_a, operand_t *op_b,
operand_t *op_c)
{
v6p_opcode_t search_op = {};
v6p_uint_opcode_t search_op = {
.opcode = {
.name = name,
.type_a = op_a ? low_level_type (op_a->type) : ev_invalid,
.type_b = op_b ? low_level_type (op_b->type) : ev_invalid,
.type_c = op_c ? low_level_type (op_c->type) : ev_invalid,
},
};
v6p_uint_opcode_t *uint_op;
v6p_opcode_t *op;
v6p_opcode_t *sop;
void **op_list;
int i;
search_op.name = name;
search_op.type_a = op_a ? low_level_type (op_a->type) : ev_invalid;
search_op.type_b = op_b ? low_level_type (op_b->type) : ev_invalid;
search_op.type_c = op_c ? low_level_type (op_c->type) : ev_invalid;
op = Hash_FindElement (v6p_opcode_type_table, &search_op);
uint_op = Hash_FindElement (v6p_opcode_uint_table, &search_op);
if (uint_op) {
return v6p_opcode_map + uint_op->op;
}
op = Hash_FindElement (v6p_opcode_type_table, &search_op.opcode);
if (op)
return op;
op_list = Hash_FindList (v6p_opcode_void_table, name);
@ -157,9 +210,9 @@ v6p_opcode_find (const char *name, operand_t *op_a, operand_t *op_b,
return op;
for (i = 0; !op && op_list[i]; i++) {
sop = op_list[i];
if (check_operand_type (sop->type_a, search_op.type_a)
&& check_operand_type (sop->type_b, search_op.type_b)
&& check_operand_type (sop->type_c, search_op.type_c))
if (check_operand_type (sop->type_a, search_op.opcode.type_a)
&& check_operand_type (sop->type_b, search_op.opcode.type_b)
&& check_operand_type (sop->type_c, search_op.opcode.type_c))
op = sop;
}
free (op_list);
@ -217,10 +270,14 @@ v6p_opcode_init (void)
if (v6p_opcode_type_table) {
Hash_FlushTable (v6p_opcode_void_table);
Hash_FlushTable (v6p_opcode_type_table);
Hash_FlushTable (v6p_opcode_uint_table);
} else {
v6p_opcode_type_table = Hash_NewTable (1021, 0, 0, 0, 0);
Hash_SetHashCompare (v6p_opcode_type_table, v6p_get_hash, v6p_compare);
v6p_opcode_void_table = Hash_NewTable (1021, v6p_get_key, 0, 0, 0);
v6p_opcode_uint_table = Hash_NewTable (1021, 0, 0, 0, 0);
Hash_SetHashCompare (v6p_opcode_uint_table,
v6p_uint_get_hash, v6p_uint_compare);
}
int num_opcodes = 0;
@ -253,6 +310,11 @@ v6p_opcode_init (void)
|| mop->type_c == ev_void)
Hash_Add (v6p_opcode_void_table, mop);
}
if (options.code.progsversion != PROG_ID_VERSION) {
for (__auto_type uiop = &v6p_uint_opcodes[0]; uiop->op; uiop++) {
Hash_AddElement (v6p_opcode_uint_table, uiop);
}
}
}
static void

View file

@ -377,7 +377,10 @@ static keyword_t qf_keywords[] = {
{"quaternion", TYPE, &type_quaternion},
{"double", TYPE, &type_double},
{"int", TYPE, &type_int },
{"unsigned", TYPE, &type_int },//FIXME
{"unsigned", UNSIGNED, 0 },
{"signed", SIGNED, 0 },
{"long", LONG, 0 },
{"short", SHORT, 0 },
{"@function", TYPE, &type_func },
{"@args", ARGS, 0 },

View file

@ -148,6 +148,7 @@ int yylex (void);
%token LOCAL RETURN WHILE DO IF ELSE FOR BREAK CONTINUE ELLIPSIS
%token NIL GOTO SWITCH CASE DEFAULT ENUM
%token ARGS TYPEDEF EXTERN STATIC SYSTEM NOSAVE OVERLOAD NOT
%token UNSIGNED SIGNED LONG SHORT
%token <op> STRUCT
%token <type> TYPE
%token <symbol> OBJECT TYPE_NAME
@ -253,6 +254,23 @@ spec_merge (specifier_t spec, specifier_t new)
spec.storage = new.storage;
spec.is_typedef = new.is_typedef;
}
if ((new.is_unsigned && spec.is_signed)
|| (new.is_signed && spec.is_unsigned)) {
if (!spec.multi_type) {
error (0, "both signed and unsigned in declaration specifiers");
spec.multi_type = 1;
}
}
if ((new.is_long && spec.is_short) || (new.is_short && spec.is_long)) {
if (!spec.multi_store) {
error (0, "both long and short in declaration specifiers");
spec.multi_store = 1;
}
}
spec.is_signed |= new.is_signed;
spec.is_unsigned |= new.is_unsigned;
spec.is_short |= new.is_short;
spec.is_long |= new.is_long;
spec.is_overload |= new.is_overload;
spec.nosave |= new.nosave;
return spec;
@ -261,6 +279,59 @@ spec_merge (specifier_t spec, specifier_t new)
static specifier_t
default_type (specifier_t spec, symbol_t *sym)
{
if (spec.type) {
if (is_float (spec.type) && !spec.multi_type) {
// no modifieres allowed
if (spec.is_unsigned) {
spec.multi_type = 1;
error (0, "both unsigned and float in declaration specifiers");
} else if (spec.is_signed) {
spec.multi_type = 1;
error (0, "both signed and float in declaration specifiers");
} else if (spec.is_short) {
spec.multi_type = 1;
error (0, "both short and float in declaration specifiers");
} else if (spec.is_long) {
spec.multi_type = 1;
error (0, "both long and float in declaration specifiers");
}
}
if (is_double (spec.type)) {
// long is allowed but ignored
if (spec.is_unsigned) {
spec.multi_type = 1;
error (0, "both unsigned and double in declaration specifiers");
} else if (spec.is_signed) {
spec.multi_type = 1;
error (0, "both signed and double in declaration specifiers");
} else if (spec.is_short) {
spec.multi_type = 1;
error (0, "both short and double in declaration specifiers");
}
}
if (is_int (spec.type)) {
// signed and short are ignored
if (spec.is_unsigned && spec.is_long) {
spec.type = &type_ulong;
} else if (spec.is_long) {
spec.type = &type_long;
}
}
} else {
if (spec.is_long) {
if (spec.is_unsigned) {
spec.type = type_ulong_uint;
} else {
spec.type = type_long_int;
}
} else {
if (spec.is_unsigned) {
spec.type = &type_uint;
} else if (spec.is_signed) {
spec.type = &type_int;
}
}
}
if (!spec.type) {
spec.type = type_default;
warning (0, "type defaults to '%s' in declaration of '%s'",
@ -414,11 +485,10 @@ function_body
: optional_state_expr
{
symbol_t *sym = $<symbol>0;
specifier_t spec = default_type ($<spec>-1, sym);
if (!$<spec>-1.type)
$<spec>-1.type = type_default;
sym->type = find_type (append_type (sym->type, $<spec>-1.type));
$<symbol>$ = function_symbol (sym, $<spec>-1.is_overload, 1);
sym->type = find_type (append_type (sym->type, spec.type));
$<symbol>$ = function_symbol (sym, spec.is_overload, 1);
}
save_storage
{
@ -438,12 +508,11 @@ function_body
| '=' '#' expr ';'
{
symbol_t *sym = $<symbol>0;
specifier_t spec = default_type ($<spec>-1, sym);
if (!$<spec>-1.type)
$<spec>-1.type = type_default;
sym->type = find_type (append_type (sym->type, $<spec>-1.type));
sym = function_symbol (sym, $<spec>-1.is_overload, 1);
build_builtin_function (sym, $3, 0, $<spec>-1.storage);
sym->type = find_type (append_type (sym->type, spec.type));
sym = function_symbol (sym, spec.is_overload, 1);
build_builtin_function (sym, $3, 0, spec.storage);
}
;
@ -556,6 +625,26 @@ type_specifier
{
$$ = make_spec ($1, 0, 0, 0);
}
| UNSIGNED
{
$$ = make_spec (0, current_storage, 0, 0);
$$.is_unsigned = 1;
}
| SIGNED
{
$$ = make_spec (0, current_storage, 0, 0);
$$.is_signed = 1;
}
| LONG
{
$$ = make_spec (0, current_storage, 0, 0);
$$.is_long = 1;
}
| SHORT
{
$$ = make_spec (0, current_storage, 0, 0);
$$.is_short = 1;
}
| enum_specifier
| struct_specifier
| TYPE_NAME
@ -768,8 +857,7 @@ struct_decl_list
struct_decl
: function_decl
{
if (!$<spec>0.type)
$<spec>0.type = type_default;
$<spec>0 = default_type ($<spec>-0, $1);
$1->type = find_type (append_type ($1->type, $<spec>0.type));
$1->sy_type = sy_var;
$1->visibility = current_visibility;
@ -780,8 +868,7 @@ struct_decl
}
| var_decl
{
if (!$<spec>0.type)
$<spec>0.type = type_default;
$<spec>0 = default_type ($<spec>-0, $1);
$1->type = find_type (append_type ($1->type, $<spec>0.type));
$1->sy_type = sy_var;
$1->visibility = current_visibility;
@ -900,8 +987,7 @@ qc_var_list
param_declaration
: type var_decl
{
if (!$1.type)
$1.type = type_default;
$1 = default_type ($1, $2);
$2->type = find_type (append_type ($2->type, $1.type));
$$ = new_param (0, $2->type, $2->name);
}
@ -913,8 +999,7 @@ abstract_decl
: type abs_decl
{
$$ = $2;
if (!$1.type)
$1.type = type_default;
$1 = default_type ($1, $2);
$$->type = find_type (append_type ($$->type, $1.type));
}
| error { $$ = new_symbol (""); }

View file

@ -76,6 +76,8 @@ type_t type_invalid = { ev_invalid, "invalid" };
type_t *type_nil;
type_t *type_default;
type_t *type_long_int;
type_t *type_ulong_uint;
// these will be built up further
type_t type_va_list = { ev_invalid, 0, 0, ty_struct };
@ -1118,6 +1120,14 @@ init_types (void)
type_nil = &type_quaternion;
type_default = &type_int;
type_long_int = &type_long;
type_ulong_uint = &type_ulong;
if (options.code.progsversion < PROG_VERSION) {
// even v6p doesn't support long, and I don't feel like adding it
// use ruamoko :P
type_long_int = &type_int;
type_ulong_uint = &type_uint;
}
if (options.code.progsversion == PROG_ID_VERSION) {
// vector can't be part of .zero for v6 progs because for v6 progs,
// .zero is only one word wide.