return cp;
}
+ static ast_expression* ast_shallow_type(lex_ctx ctx, int vtype)
+ {
+ ast_instantiate(ast_expression, ctx, ast_expression_delete_full);
+ self->expression.codegen = NULL;
+ self->expression.next = NULL;
+ self->expression.vtype = vtype;
+ return self;
+ }
+
static ast_expression* ast_type_copy(lex_ctx ctx, const ast_expression *ex)
{
size_t i;
self->left = left;
self->right = right;
+ if (op >= INSTR_EQ_F && op <= INSTR_GT)
+ self->expression.vtype = TYPE_FLOAT;
+ else if (op == INSTR_AND || op == INSTR_OR ||
+ op == INSTR_BITAND || op == INSTR_BITOR)
+ self->expression.vtype = TYPE_FLOAT;
+ else if (op == INSTR_MUL_VF || op == INSTR_MUL_FV)
+ self->expression.vtype = TYPE_VECTOR;
+ else if (op == INSTR_MUL_V)
+ self->expression.vtype = TYPE_FLOAT;
+ else
+ self->expression.vtype = left->expression.vtype;
+
return self;
}
return NULL;
}
+ if (owner->expression.vtype != TYPE_FLOAT &&
+ owner->expression.vtype != TYPE_FIELD) {
+ printf("ast_member on an invalid owner of type %i\n", (int)owner->expression.vtype);
+ mem_d(self);
+ return NULL;
+ }
+
ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_member_codegen);
- self->expression.vtype = TYPE_FLOAT;
- self->expression.next = NULL;
+ if (owner->expression.vtype == TYPE_VECTOR) {
+ self->expression.vtype = TYPE_FLOAT;
+ self->expression.next = NULL;
+ } else {
+ self->expression.vtype = TYPE_FIELD;
+ self->expression.next = ast_shallow_type(ctx, TYPE_FLOAT);
+ }
self->owner = owner;
self->field = field;
return true;
}
+ if (self->expression.vtype == TYPE_FIELD) {
+ v = ir_builder_create_field(ir, self->name, self->expression.next->expression.vtype);
+ if (!v)
+ return false;
+ if (self->isconst) {
+ printf("TODO: constant field pointers with value\n");
+ goto error;
+ }
+ self->ir_v = v;
+ return true;
+ }
+
v = ir_builder_create_global(ir, self->name, self->expression.vtype);
- if (!v)
+ if (!v) {
+ printf("ir_builder_create_global failed\n");
return false;
+ }
if (self->isconst) {
switch (self->expression.vtype)
bool ast_member_codegen(ast_member *self, ast_function *func, bool lvalue, ir_value **out)
{
ast_expression_codegen *cgen;
- ir_value *vec, *field;
+ ir_value *vec;
cgen = self->owner->expression.codegen;
if (!(*cgen)((ast_expression*)(self->owner), func, true, &vec))
return false;
- if (vec->vtype != TYPE_VECTOR)
+ if (vec->vtype != TYPE_VECTOR &&
+ !(vec->vtype == TYPE_FIELD && self->owner->expression.next->expression.vtype == TYPE_VECTOR))
+ {
return false;
+ }
*out = ir_value_vector_member(vec, self->field);
typedef char uintptr_size_is_correct[sizeof(intptr_t) == sizeof(int*)?1:-1];
typedef char intptr_size_is_correct [sizeof(uintptr_t)== sizeof(int*)?1:-1];
-/*===================================================================*/
-/*============================ lex.c ================================*/
-/*===================================================================*/
-typedef struct lex_file_t {
- FILE *file; /* file handler */
- char *name; /* name of file */
- char peek [5];
- char lastok[8192];
-
- int last; /* last token */
- int current; /* current token */
- int length; /* bytes left to parse */
- int size; /* never changes (size of file) */
- int line; /* what line are we on? */
-} lex_file;
-
-/*
- * It's important that this table never exceed 32 keywords, the ascii
- * table starts at 33 (and we don't want conflicts)
- */
-enum {
- TOKEN_DO ,
- TOKEN_ELSE ,
- TOKEN_IF ,
- TOKEN_WHILE ,
- TOKEN_BREAK ,
- TOKEN_CONTINUE ,
- TOKEN_RETURN ,
- TOKEN_GOTO ,
- TOKEN_FOR , /* extension */
- TOKEN_TYPEDEF , /* extension */
-
- /* ensure the token types are out of the */
- /* bounds of anyothers that may conflict. */
- TOKEN_FLOAT = 110,
- TOKEN_VECTOR ,
- TOKEN_STRING ,
- TOKEN_ENTITY ,
- TOKEN_VOID
-};
-
-/*
- * Lexer state constants, these are numbers for where exactly in
- * the lexing the lexer is at. Or where it decided to stop if a lexer
- * error occurs. These numbers must be > where the ascii-table ends
- * and > the last type token which is TOKEN_VOID
- */
-enum {
- LEX_COMMENT = 1128,
- LEX_CHRLIT ,
- LEX_STRLIT ,
- LEX_IDENT
-};
-
-int lex_token (lex_file *);
-void lex_reset (lex_file *);
-void lex_close (lex_file *);
-void lex_parse (lex_file *);
-lex_file *lex_include(lex_file *, const char *);
-void lex_init (const char *, lex_file **);
-
-/*===================================================================*/
-/*========================== error.c ================================*/
-/*===================================================================*/
-#define ERROR_LEX (SHRT_MAX+0)
-#define ERROR_PARSE (SHRT_MAX+1)
-#define ERROR_INTERNAL (SHRT_MAX+2)
-#define ERROR_COMPILER (SHRT_MAX+3)
-#define ERROR_PREPRO (SHRT_MAX+4)
-int error(lex_file *, int, const char *, ...);
-
-/*===================================================================*/
-/*========================== parse.c ================================*/
-/*===================================================================*/
-int parse_gen(lex_file *);
-
-/*===================================================================*/
-/*========================== typedef.c ==============================*/
-/*===================================================================*/
-typedef struct typedef_node_t {
- char *name;
-} typedef_node;
-
-void typedef_init();
-void typedef_clear();
-typedef_node *typedef_find(const char *);
-int typedef_add (lex_file *file, const char *, const char *);
-
-
/*===================================================================*/
/*=========================== util.c ================================*/
/*===================================================================*/
TYPE_FIELD ,
TYPE_FUNCTION ,
TYPE_POINTER ,
- /* TYPE_INTEGER , */
+ TYPE_INTEGER ,
+ TYPE_QUATERNION ,
+ TYPE_MATRIX ,
TYPE_VARIANT ,
TYPE_COUNT
};
+extern const char *type_name[TYPE_COUNT];
+
extern size_t type_sizeof[TYPE_COUNT];
extern uint16_t type_store_instr[TYPE_COUNT];
/* could use type_store_instr + INSTR_STOREP_F - INSTR_STORE_F
INSTR_DONE,
INSTR_MUL_F,
INSTR_MUL_V,
- INSTR_MUL_FV,
INSTR_MUL_VF,
+ INSTR_MUL_FV,
INSTR_DIV_F,
INSTR_ADD_F,
INSTR_ADD_V,
INSTR_BITAND,
INSTR_BITOR,
+/* warning: will be reordered */
+ INSTR_MUL_Q,
+ INSTR_MUL_QF,
+ INSTR_MUL_M,
+ INSTR_MUL_MF,
+ INSTR_EQ_Q,
+ INSTR_EQ_M,
+ INSTR_NE_Q,
+ INSTR_NE_M,
+ INSTR_LOAD_Q,
+ INSTR_LOAD_M,
+ INSTR_STORE_Q,
+ INSTR_STORE_M,
+ INSTR_STOREP_Q,
+ INSTR_STOREP_M,
+ INSTR_INV_Q,
+ INSTR_INV_M,
/*
* Virtual instructions used by the assembler
* keep at the end but before virtual instructions
{ "DONE" , 1, 4 },
{ "MUL_F" , 3, 5 },
{ "MUL_V" , 3, 5 },
- { "MUL_FV" , 3, 6 },
{ "MUL_VF" , 3, 6 },
+ { "MUL_FV" , 3, 6 },
{ "DIV" , 0, 3 },
{ "ADD_F" , 3, 5 },
{ "ADD_V" , 3, 5 },
{ "OR" , 0, 2 },
{ "BITAND" , 0, 6 },
{ "BITOR" , 0, 5 },
+
+ { "MUL_Q" , 3, 5 },
+ { "MUL_QF" , 3, 6 },
+ { "MUL_M" , 3, 5 },
+ { "MUL_MF" , 3, 6 },
+ { "EQ_Q" , 0, 4 },
+ { "EQ_M" , 0, 4 },
+ { "NE_Q" , 0, 4 },
+ { "NE_M" , 0, 4 },
+ { "FIELD_Q" , 0, 7 },
+ { "FIELD_M" , 0, 7 },
+ { "STORE_Q" , 0, 7 },
+ { "STORE_M" , 0, 7 },
+ { "STOREP_Q" , 0, 8 },
+ { "STOREP_M" , 0, 8 },
+ { "INV_Q" , 0, 5 },
+ { "INV_M" , 0, 5 },
+
{ "END" , 0, 3 } /* virtual assembler instruction */
};
(owner)->mem##_alloc = 0; \
}
+#define MEM_VECTOR_MOVE(from, mem, to, tm) \
+{ \
+ (to)->tm = (from)->mem; \
+ (to)->tm##_count = (from)->mem##_count; \
+ (to)->tm##_alloc = (from)->mem##_alloc; \
+ (from)->mem = NULL; \
+ (from)->mem##_count = 0; \
+ (from)->mem##_alloc = 0; \
+}
+
#define MEM_VEC_FUNCTIONS(Tself, Twhat, mem) \
MEM_VEC_FUN_REMOVE(Tself, Twhat, mem) \
MEM_VEC_FUN_ADD(Tself, Twhat, mem)
float x, y, z;
} vector;
+typedef float matrix[4][4]; /* OpenGL layout */
+typedef float quaternion[4]; /* order: x, y, z, w */
+#define MATRIX(axis, elem) ((4*(axis)) + (elem))
+#define QUAT_X 0
+#define QUAT_Y 1
+#define QUAT_Z 2
+#define QUAT_W 3
+
/*
* A shallow copy of a lex_file to remember where which ast node
* came from.
MEM_VECTOR_MAKE(char, strings);
MEM_VECTOR_MAKE(qcint, globals);
MEM_VECTOR_MAKE(qcint, entitydata);
+ MEM_VECTOR_MAKE(bool, entitypool);
size_t tempstring_start;
size_t tempstring_at;
* Type sizes used at multiple points in the IR codegen
*/
+const char *type_name[TYPE_COUNT] = {
+ "void",
+ "string",
+ "float",
+ "vector",
+ "entity",
+ "field",
+ "function",
+ "pointer",
+#if 0
+ "integer",
+#endif
+ "quaternion",
+ "matrix",
+ "variant"
+};
+
size_t type_sizeof[TYPE_COUNT] = {
1, /* TYPE_VOID */
1, /* TYPE_STRING */
#if 0
1, /* TYPE_INTEGER */
#endif
- 3, /* TYPE_VARIANT */
+ 4, /* TYPE_QUATERNION */
+ 16, /* TYPE_MATRIX */
+ 16, /* TYPE_VARIANT */
};
uint16_t type_store_instr[TYPE_COUNT] = {
INSTR_STORE_FNC,
INSTR_STORE_ENT, /* should use I */
#if 0
- INSTR_STORE_ENT, /* integer type */
+ INSTR_STORE_I, /* integer type */
#endif
- INSTR_STORE_V, /* variant, should never be accessed */
+ INSTR_STORE_Q,
+ INSTR_STORE_M,
+
+ INSTR_STORE_M, /* variant, should never be accessed */
};
uint16_t type_storep_instr[TYPE_COUNT] = {
#if 0
INSTR_STOREP_ENT, /* integer type */
#endif
- INSTR_STOREP_V, /* variant, should never be accessed */
+ INSTR_STOREP_Q,
+ INSTR_STOREP_M,
+
+ INSTR_STOREP_M, /* variant, should never be accessed */
};
MEM_VEC_FUNCTIONS(ir_value_vector, ir_value*, v)
ir_value* ir_builder_create_global(ir_builder *self, const char *name, int vtype)
{
- ir_value *ve = ir_builder_get_global(self, name);
- if (ve) {
- return NULL;
+ ir_value *ve;
+
+ if (name && name[0] != '#')
+ {
+ ve = ir_builder_get_global(self, name);
+ if (ve) {
+ return NULL;
+ }
}
ve = ir_value_var(name, store_global, vtype);
int32_t ir_value_code_addr(const ir_value *self)
{
+ if (self->store == store_return)
+ return OFS_RETURN + self->code.addroffset;
return self->code.globaladdr + self->code.addroffset;
}
if (self->members[member])
return self->members[member];
- m = ir_value_var(self->name, self->store, TYPE_FLOAT);
- if (!m)
- return NULL;
- m->context = self->context;
+ if (self->vtype == TYPE_VECTOR)
+ {
+ m = ir_value_var(self->name, self->store, TYPE_FLOAT);
+ if (!m)
+ return NULL;
+ m->context = self->context;
- self->members[member] = m;
- m->code.addroffset = member;
+ self->members[member] = m;
+ m->code.addroffset = member;
+ }
+ else if (self->vtype == TYPE_FIELD)
+ {
+ if (self->fieldtype != TYPE_VECTOR)
+ return NULL;
+ m = ir_value_var(self->name, self->store, TYPE_FIELD);
+ if (!m)
+ return NULL;
+ m->fieldtype = TYPE_FLOAT;
+ m->context = self->context;
+
+ self->members[member] = m;
+ m->code.addroffset = member;
+ }
return m;
}
return true;
}
+bool ir_value_set_quaternion(ir_value *self, quaternion v)
+{
+ if (self->vtype != TYPE_QUATERNION)
+ return false;
+ memcpy(&self->constval.vquat, v, sizeof(self->constval.vquat));
+ self->isconst = true;
+ return true;
+}
+
+bool ir_value_set_matrix(ir_value *self, matrix v)
+{
+ if (self->vtype != TYPE_MATRIX)
+ return false;
+ memcpy(&self->constval.vmat, v, sizeof(self->constval.vmat));
+ self->isconst = true;
+ return true;
+}
+
+ bool ir_value_set_field(ir_value *self, ir_value *fld)
+ {
+ if (self->vtype != TYPE_FIELD)
+ return false;
+ self->constval.vpointer = fld;
+ self->isconst = true;
+ return true;
+ }
+
bool ir_value_set_string(ir_value *self, const char *str)
{
if (self->vtype != TYPE_STRING)
bool ir_block_create_store_op(ir_block *self, int op, ir_value *target, ir_value *what)
{
- if (target->store == store_value) {
+ ir_instr *in = ir_instr_new(self, op);
+ if (!in)
+ return false;
+
+ if (target->store == store_value &&
+ (op < INSTR_STOREP_F || op > INSTR_STOREP_FNC))
+ {
fprintf(stderr, "cannot store to an SSA value\n");
fprintf(stderr, "trying to store: %s <- %s\n", target->name, what->name);
+ fprintf(stderr, "instruction: %s\n", asm_instr[op].m);
+ return false;
+ }
+
+ if (!ir_instr_op(in, 0, target, true) ||
+ !ir_instr_op(in, 1, what, false) ||
+ !ir_block_instr_add(self, in) )
+ {
return false;
- } else {
- ir_instr *in = ir_instr_new(self, op);
- if (!in)
- return false;
- if (!ir_instr_op(in, 0, target, true) ||
- !ir_instr_op(in, 1, what, false) ||
- !ir_block_instr_add(self, in) )
- {
- return false;
- }
- return true;
}
+ return true;
}
bool ir_block_create_store(ir_block *self, ir_value *target, ir_value *what)
#endif
op = type_store_instr[vtype];
+ if (OPTS_FLAG(ADJUST_VECTOR_FIELDS)) {
+ if (op == INSTR_STORE_FLD && what->fieldtype == TYPE_VECTOR)
+ op = INSTR_STORE_V;
+ }
+
return ir_block_create_store_op(self, op, target, what);
}
vtype = what->vtype;
op = type_storep_instr[vtype];
+ if (OPTS_FLAG(ADJUST_VECTOR_FIELDS)) {
+ if (op == INSTR_STOREP_FLD && what->fieldtype == TYPE_VECTOR)
+ op = INSTR_STOREP_V;
+ }
+
return ir_block_create_store_op(self, op, target, what);
}
case TYPE_POINTER: op = INSTR_LOAD_I; break;
case TYPE_INTEGER: op = INSTR_LOAD_I; break;
#endif
+ case TYPE_QUATERNION: op = INSTR_LOAD_Q; break;
+ case TYPE_MATRIX: op = INSTR_LOAD_M; break;
default:
return NULL;
}
case TYPE_VECTOR:
op = INSTR_MUL_V;
break;
+ case TYPE_QUATERNION:
+ op = INSTR_MUL_Q;
+ break;
+ case TYPE_MATRIX:
+ op = INSTR_MUL_M;
+ break;
}
} else {
if ( (l == TYPE_VECTOR && r == TYPE_FLOAT) )
op = INSTR_MUL_VF;
else if ( (l == TYPE_FLOAT && r == TYPE_VECTOR) )
op = INSTR_MUL_FV;
+ else if ( (l == TYPE_QUATERNION && r == TYPE_FLOAT) )
+ op = INSTR_MUL_QF;
+ else if ( (l == TYPE_MATRIX && r == TYPE_FLOAT) )
+ op = INSTR_MUL_MF;
#if 0
else if ( (l == TYPE_VECTOR && r == TYPE_INTEGER) )
op = INSTR_MUL_VI;
/* copy the field's value */
ir_value_code_setaddr(global, code_globals_add(code_globals_data[fld->code.globaladdr]));
+ if (global->fieldtype == TYPE_VECTOR) {
+ code_globals_add(code_globals_data[fld->code.globaladdr]+1);
+ code_globals_add(code_globals_data[fld->code.globaladdr]+2);
+ }
}
else
{
ir_value_code_setaddr(global, code_globals_add(0));
+ if (global->fieldtype == TYPE_VECTOR) {
+ code_globals_add(0);
+ code_globals_add(0);
+ }
}
if (global->code.globaladdr < 0)
return false;
stmt.o1.u1 = stmt.o3.u1;
stmt.o3.u1 = 0;
}
- else if (stmt.opcode >= INSTR_STORE_F &&
- stmt.opcode <= INSTR_STORE_FNC)
+ else if ((stmt.opcode >= INSTR_STORE_F &&
+ stmt.opcode <= INSTR_STORE_FNC) ||
+ (stmt.opcode >= INSTR_STOREP_F &&
+ stmt.opcode <= INSTR_STOREP_FNC))
{
/* 2-operand instructions with A -> B */
stmt.o2.u1 = stmt.o3.u1;
return global->code.globaladdr >= 0;
}
case TYPE_VECTOR:
+ case TYPE_QUATERNION:
+ case TYPE_MATRIX:
{
size_t d;
if (code_defs_add(def) < 0)
def.type = field->vtype;
def.offset = code_globals_elements;
- def.name = field->code.name = code_genstring(field->name);
+
+ /* create a global named the same as the field */
+ if (opts_standard == COMPILER_GMQCC) {
+ /* in our standard, the global gets a dot prefix */
+ size_t len = strlen(field->name);
+ char name[1024];
+
+ /* we really don't want to have to allocate this, and 1024
+ * bytes is more than enough for a variable/field name
+ */
+ if (len+2 >= sizeof(name)) {
+ printf("invalid field name size: %u\n", (unsigned int)len);
+ return false;
+ }
+
+ name[0] = '.';
+ strcpy(name+1, field->name); /* no strncpy - we used strlen above */
+ name[len+1] = 0;
+
+ def.name = code_genstring(name);
+ fld.name = def.name + 1; /* we reuse that string table entry */
+ } else {
+ /* in plain QC, there cannot be a global with the same name,
+ * and so we also name the global the same.
+ * FIXME: fteqcc should create a global as well
+ * check if it actually uses the same name. Probably does
+ */
+ def.name = code_genstring(field->name);
+ fld.name = def.name;
+ }
+
+ field->code.name = def.name;
if (code_defs_add(def) < 0)
return false;
- fld.name = def.name;
- fld.offset = code_fields_elements;
fld.type = field->fieldtype;
if (fld.type == TYPE_VOID) {
return false;
}
+ fld.offset = code_alloc_field(type_sizeof[field->fieldtype]);
+
if (code_fields_add(fld) < 0)
return false;
- if (!code_globals_add(code_alloc_field(type_sizeof[field->fieldtype])))
+ ir_value_code_setaddr(field, code_globals_elements);
+ if (!code_globals_add(fld.offset))
return false;
+ if (fld.type == TYPE_VECTOR) {
+ if (!code_globals_add(fld.offset+1))
+ return false;
+ if (!code_globals_add(fld.offset+2))
+ return false;
+ }
- ir_value_code_setaddr(field, code_globals_add(fld.offset));
return field->code.globaladdr >= 0;
}
{
if (v->isconst) {
switch (v->vtype) {
+ default:
case TYPE_VOID:
oprintf("(void)");
break;