[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 int32_t;
typedef int int64_t; typedef int int64_t;
typedef int size_t; typedef int size_t;
typedef int long;
typedef int char; 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_nil; // for passing nil into ...
extern type_t *type_default; // default type (float or int) 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_va_list;
extern type_t type_param; extern type_t type_param;

View file

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

View file

@ -47,8 +47,38 @@
#include "tools/qfcc/include/statements.h" #include "tools/qfcc/include/statements.h"
#include "tools/qfcc/include/type.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_type_table;
static hashtab_t *v6p_opcode_void_table; static hashtab_t *v6p_opcode_void_table;
static hashtab_t *v6p_opcode_uint_table;
static v6p_opcode_t *v6p_opcode_map; static v6p_opcode_t *v6p_opcode_map;
static hashtab_t *rua_opcode_type_table; 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; 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 static uintptr_t
rua_get_hash (const void *_op, void *_tab) 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, v6p_opcode_find (const char *name, operand_t *op_a, operand_t *op_b,
operand_t *op_c) 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 *op;
v6p_opcode_t *sop; v6p_opcode_t *sop;
void **op_list; void **op_list;
int i; int i;
search_op.name = name; uint_op = Hash_FindElement (v6p_opcode_uint_table, &search_op);
search_op.type_a = op_a ? low_level_type (op_a->type) : ev_invalid; if (uint_op) {
search_op.type_b = op_b ? low_level_type (op_b->type) : ev_invalid; return v6p_opcode_map + uint_op->op;
search_op.type_c = op_c ? low_level_type (op_c->type) : ev_invalid; }
op = Hash_FindElement (v6p_opcode_type_table, &search_op); op = Hash_FindElement (v6p_opcode_type_table, &search_op.opcode);
if (op) if (op)
return op; return op;
op_list = Hash_FindList (v6p_opcode_void_table, name); 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; return op;
for (i = 0; !op && op_list[i]; i++) { for (i = 0; !op && op_list[i]; i++) {
sop = op_list[i]; sop = op_list[i];
if (check_operand_type (sop->type_a, search_op.type_a) if (check_operand_type (sop->type_a, search_op.opcode.type_a)
&& check_operand_type (sop->type_b, search_op.type_b) && check_operand_type (sop->type_b, search_op.opcode.type_b)
&& check_operand_type (sop->type_c, search_op.type_c)) && check_operand_type (sop->type_c, search_op.opcode.type_c))
op = sop; op = sop;
} }
free (op_list); free (op_list);
@ -217,10 +270,14 @@ v6p_opcode_init (void)
if (v6p_opcode_type_table) { if (v6p_opcode_type_table) {
Hash_FlushTable (v6p_opcode_void_table); Hash_FlushTable (v6p_opcode_void_table);
Hash_FlushTable (v6p_opcode_type_table); Hash_FlushTable (v6p_opcode_type_table);
Hash_FlushTable (v6p_opcode_uint_table);
} else { } else {
v6p_opcode_type_table = Hash_NewTable (1021, 0, 0, 0, 0); v6p_opcode_type_table = Hash_NewTable (1021, 0, 0, 0, 0);
Hash_SetHashCompare (v6p_opcode_type_table, v6p_get_hash, v6p_compare); 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_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; int num_opcodes = 0;
@ -253,6 +310,11 @@ v6p_opcode_init (void)
|| mop->type_c == ev_void) || mop->type_c == ev_void)
Hash_Add (v6p_opcode_void_table, mop); 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 static void

View file

@ -377,7 +377,10 @@ static keyword_t qf_keywords[] = {
{"quaternion", TYPE, &type_quaternion}, {"quaternion", TYPE, &type_quaternion},
{"double", TYPE, &type_double}, {"double", TYPE, &type_double},
{"int", TYPE, &type_int }, {"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 }, {"@function", TYPE, &type_func },
{"@args", ARGS, 0 }, {"@args", ARGS, 0 },

View file

@ -148,6 +148,7 @@ int yylex (void);
%token LOCAL RETURN WHILE DO IF ELSE FOR BREAK CONTINUE ELLIPSIS %token LOCAL RETURN WHILE DO IF ELSE FOR BREAK CONTINUE ELLIPSIS
%token NIL GOTO SWITCH CASE DEFAULT ENUM %token NIL GOTO SWITCH CASE DEFAULT ENUM
%token ARGS TYPEDEF EXTERN STATIC SYSTEM NOSAVE OVERLOAD NOT %token ARGS TYPEDEF EXTERN STATIC SYSTEM NOSAVE OVERLOAD NOT
%token UNSIGNED SIGNED LONG SHORT
%token <op> STRUCT %token <op> STRUCT
%token <type> TYPE %token <type> TYPE
%token <symbol> OBJECT TYPE_NAME %token <symbol> OBJECT TYPE_NAME
@ -253,6 +254,23 @@ spec_merge (specifier_t spec, specifier_t new)
spec.storage = new.storage; spec.storage = new.storage;
spec.is_typedef = new.is_typedef; 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.is_overload |= new.is_overload;
spec.nosave |= new.nosave; spec.nosave |= new.nosave;
return spec; return spec;
@ -261,6 +279,59 @@ spec_merge (specifier_t spec, specifier_t new)
static specifier_t static specifier_t
default_type (specifier_t spec, symbol_t *sym) 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) { if (!spec.type) {
spec.type = type_default; spec.type = type_default;
warning (0, "type defaults to '%s' in declaration of '%s'", warning (0, "type defaults to '%s' in declaration of '%s'",
@ -414,11 +485,10 @@ function_body
: optional_state_expr : optional_state_expr
{ {
symbol_t *sym = $<symbol>0; symbol_t *sym = $<symbol>0;
specifier_t spec = default_type ($<spec>-1, sym);
if (!$<spec>-1.type) sym->type = find_type (append_type (sym->type, spec.type));
$<spec>-1.type = type_default; $<symbol>$ = function_symbol (sym, spec.is_overload, 1);
sym->type = find_type (append_type (sym->type, $<spec>-1.type));
$<symbol>$ = function_symbol (sym, $<spec>-1.is_overload, 1);
} }
save_storage save_storage
{ {
@ -438,12 +508,11 @@ function_body
| '=' '#' expr ';' | '=' '#' expr ';'
{ {
symbol_t *sym = $<symbol>0; symbol_t *sym = $<symbol>0;
specifier_t spec = default_type ($<spec>-1, sym);
if (!$<spec>-1.type) sym->type = find_type (append_type (sym->type, spec.type));
$<spec>-1.type = type_default; sym = function_symbol (sym, spec.is_overload, 1);
sym->type = find_type (append_type (sym->type, $<spec>-1.type)); build_builtin_function (sym, $3, 0, spec.storage);
sym = function_symbol (sym, $<spec>-1.is_overload, 1);
build_builtin_function (sym, $3, 0, $<spec>-1.storage);
} }
; ;
@ -556,6 +625,26 @@ type_specifier
{ {
$$ = make_spec ($1, 0, 0, 0); $$ = 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 | enum_specifier
| struct_specifier | struct_specifier
| TYPE_NAME | TYPE_NAME
@ -768,8 +857,7 @@ struct_decl_list
struct_decl struct_decl
: function_decl : function_decl
{ {
if (!$<spec>0.type) $<spec>0 = default_type ($<spec>-0, $1);
$<spec>0.type = type_default;
$1->type = find_type (append_type ($1->type, $<spec>0.type)); $1->type = find_type (append_type ($1->type, $<spec>0.type));
$1->sy_type = sy_var; $1->sy_type = sy_var;
$1->visibility = current_visibility; $1->visibility = current_visibility;
@ -780,8 +868,7 @@ struct_decl
} }
| var_decl | var_decl
{ {
if (!$<spec>0.type) $<spec>0 = default_type ($<spec>-0, $1);
$<spec>0.type = type_default;
$1->type = find_type (append_type ($1->type, $<spec>0.type)); $1->type = find_type (append_type ($1->type, $<spec>0.type));
$1->sy_type = sy_var; $1->sy_type = sy_var;
$1->visibility = current_visibility; $1->visibility = current_visibility;
@ -900,8 +987,7 @@ qc_var_list
param_declaration param_declaration
: type var_decl : type var_decl
{ {
if (!$1.type) $1 = default_type ($1, $2);
$1.type = type_default;
$2->type = find_type (append_type ($2->type, $1.type)); $2->type = find_type (append_type ($2->type, $1.type));
$$ = new_param (0, $2->type, $2->name); $$ = new_param (0, $2->type, $2->name);
} }
@ -913,8 +999,7 @@ abstract_decl
: type abs_decl : type abs_decl
{ {
$$ = $2; $$ = $2;
if (!$1.type) $1 = default_type ($1, $2);
$1.type = type_default;
$$->type = find_type (append_type ($$->type, $1.type)); $$->type = find_type (append_type ($$->type, $1.type));
} }
| error { $$ = new_symbol (""); } | error { $$ = new_symbol (""); }

View file

@ -76,6 +76,8 @@ type_t type_invalid = { ev_invalid, "invalid" };
type_t *type_nil; type_t *type_nil;
type_t *type_default; type_t *type_default;
type_t *type_long_int;
type_t *type_ulong_uint;
// these will be built up further // these will be built up further
type_t type_va_list = { ev_invalid, 0, 0, ty_struct }; type_t type_va_list = { ev_invalid, 0, 0, ty_struct };
@ -1118,6 +1120,14 @@ init_types (void)
type_nil = &type_quaternion; type_nil = &type_quaternion;
type_default = &type_int; 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) { if (options.code.progsversion == PROG_ID_VERSION) {
// vector can't be part of .zero for v6 progs because for v6 progs, // vector can't be part of .zero for v6 progs because for v6 progs,
// .zero is only one word wide. // .zero is only one word wide.