#include "gmqcc.h"
#include "ir.h"
+/***********************************************************************
+ * Type sizes used at multiple points in the IR codegen
+ */
+
+size_t type_sizeof[TYPE_COUNT] = {
+ 1, /* TYPE_VOID */
+ 1, /* TYPE_STRING */
+ 1, /* TYPE_FLOAT */
+ 3, /* TYPE_VECTOR */
+ 1, /* TYPE_ENTITY */
+ 1, /* TYPE_FIELD */
+ 1, /* TYPE_FUNCTION */
+ 1, /* TYPE_POINTER */
+#if 0
+ 1, /* TYPE_INTEGER */
+#endif
+ 4, /* TYPE_QUATERNION */
+ 16, /* TYPE_MATRIX */
+ 16, /* TYPE_VARIANT */
+};
+
+uint16_t type_store_instr[TYPE_COUNT] = {
+ INSTR_STORE_F, /* should use I when having integer support */
+ INSTR_STORE_S,
+ INSTR_STORE_F,
+ INSTR_STORE_V,
+ INSTR_STORE_ENT,
+ INSTR_STORE_FLD,
+ INSTR_STORE_FNC,
+ INSTR_STORE_ENT, /* should use I */
+#if 0
+ INSTR_STORE_I, /* integer type */
+#endif
+ INSTR_STORE_Q,
+ INSTR_STORE_M,
+
+ INSTR_STORE_M, /* variant, should never be accessed */
+};
+
+uint16_t type_storep_instr[TYPE_COUNT] = {
+ INSTR_STOREP_F, /* should use I when having integer support */
+ INSTR_STOREP_S,
+ INSTR_STOREP_F,
+ INSTR_STOREP_V,
+ INSTR_STOREP_ENT,
+ INSTR_STOREP_FLD,
+ INSTR_STOREP_FNC,
+ INSTR_STOREP_ENT, /* should use I */
+#if 0
+ INSTR_STOREP_ENT, /* integer type */
+#endif
+ INSTR_STOREP_Q,
+ INSTR_STOREP_M,
+
+ INSTR_STOREP_M, /* variant, should never be accessed */
+};
+
+MEM_VEC_FUNCTIONS(ir_value_vector, ir_value*, v)
+
/***********************************************************************
*IR Builder
*/
return NULL;
}
-ir_function* ir_builder_create_function(ir_builder *self, const char *name)
+ir_function* ir_builder_create_function(ir_builder *self, const char *name, int outtype)
{
ir_function *fn = ir_builder_get_function(self, name);
if (fn) {
return NULL;
}
- fn = ir_function_new(self);
+ fn = ir_function_new(self, outtype);
if (!ir_function_set_name(fn, name) ||
!ir_builder_functions_add(self, fn) )
{
ir_function_delete(fn);
return NULL;
}
+
+ fn->value = ir_builder_create_global(self, fn->name, TYPE_FUNCTION);
+ if (!fn->value) {
+ ir_function_delete(fn);
+ return NULL;
+ }
+
+ fn->value->isconst = true;
+ fn->value->outtype = outtype;
+ fn->value->constval.vfunc = fn;
+ fn->value->context = fn->context;
+
return fn;
}
bool ir_function_calculate_liferanges(ir_function*);
bool ir_function_allocate_locals(ir_function*);
-ir_function* ir_function_new(ir_builder* owner)
+ir_function* ir_function_new(ir_builder* owner, int outtype)
{
ir_function *self;
self = (ir_function*)mem_a(sizeof(*self));
self->owner = owner;
self->context.file = "<@no context>";
self->context.line = 0;
- self->retype = TYPE_VOID;
+ self->outtype = outtype;
+ self->value = NULL;
+ self->builtin = 0;
MEM_VECTOR_INIT(self, params);
MEM_VECTOR_INIT(self, blocks);
MEM_VECTOR_INIT(self, values);
MEM_VEC_FUNCTIONS(ir_function, ir_value*, values)
MEM_VEC_FUNCTIONS(ir_function, ir_block*, blocks)
MEM_VEC_FUNCTIONS(ir_function, ir_value*, locals)
+MEM_VEC_FUNCTIONS(ir_function, int, params)
bool ir_function_set_name(ir_function *self, const char *name)
{
ir_value_delete(self->locals[i]);
MEM_VECTOR_CLEAR(self, locals);
+ /* self->value is deleted by the builder */
+
mem_d(self);
}
bool ir_function_finalize(ir_function *self)
{
+ if (self->builtin)
+ return true;
+
if (!ir_function_naive_phi(self))
return false;
self->bops[0] = NULL;
self->bops[1] = NULL;
MEM_VECTOR_INIT(self, phi);
+ MEM_VECTOR_INIT(self, params);
self->eid = 0;
return self;
}
MEM_VEC_FUNCTIONS(ir_instr, ir_phi_entry_t, phi)
+MEM_VEC_FUNCTIONS(ir_instr, ir_value*, params)
void ir_instr_delete(ir_instr *self)
{
for (i = 0; i < self->phi_count; ++i) {
size_t idx;
if (ir_value_writes_find(self->phi[i].value, self, &idx))
- if (ir_value_writes_remove(self->phi[i].value, idx)) GMQCC_SUPRESS_EMPTY_BODY;
+ if (ir_value_writes_remove(self->phi[i].value, idx)) GMQCC_SUPPRESS_EMPTY_BODY;
if (ir_value_reads_find(self->phi[i].value, self, &idx))
- if (ir_value_reads_remove (self->phi[i].value, idx)) GMQCC_SUPRESS_EMPTY_BODY;
+ if (ir_value_reads_remove (self->phi[i].value, idx)) GMQCC_SUPPRESS_EMPTY_BODY;
}
MEM_VECTOR_CLEAR(self, phi);
- if (ir_instr_op(self, 0, NULL, false)) GMQCC_SUPRESS_EMPTY_BODY;
- if (ir_instr_op(self, 1, NULL, false)) GMQCC_SUPRESS_EMPTY_BODY;
- if (ir_instr_op(self, 2, NULL, false)) GMQCC_SUPRESS_EMPTY_BODY;
+ for (i = 0; i < self->params_count; ++i) {
+ size_t idx;
+ if (ir_value_writes_find(self->params[i], self, &idx))
+ if (ir_value_writes_remove(self->params[i], idx)) GMQCC_SUPPRESS_EMPTY_BODY;
+ if (ir_value_reads_find(self->params[i], self, &idx))
+ if (ir_value_reads_remove (self->params[i], idx)) GMQCC_SUPPRESS_EMPTY_BODY;
+ }
+ MEM_VECTOR_CLEAR(self, params);
+ if (ir_instr_op(self, 0, NULL, false)) GMQCC_SUPPRESS_EMPTY_BODY;
+ if (ir_instr_op(self, 1, NULL, false)) GMQCC_SUPPRESS_EMPTY_BODY;
+ if (ir_instr_op(self, 2, NULL, false)) GMQCC_SUPPRESS_EMPTY_BODY;
mem_d(self);
}
self = (ir_value*)mem_a(sizeof(*self));
self->vtype = vtype;
self->fieldtype = TYPE_VOID;
+ self->outtype = TYPE_VOID;
self->store = storetype;
MEM_VECTOR_INIT(self, reads);
MEM_VECTOR_INIT(self, writes);
return true;
}
+bool ir_value_set_func(ir_value *self, int f)
+{
+ if (self->vtype != TYPE_FUNCTION)
+ return false;
+ self->constval.vint = f;
+ self->isconst = true;
+ return true;
+}
+
bool ir_value_set_vector(ir_value *self, vector v)
{
if (self->vtype != TYPE_VECTOR)
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_string(ir_value *self, const char *str)
{
if (self->vtype != TYPE_STRING)
else
vtype = target->vtype;
- switch (vtype) {
- case TYPE_FLOAT:
#if 0
- if (what->vtype == TYPE_INTEGER)
- op = INSTR_CONV_ITOF;
- else
+ if (vtype == TYPE_FLOAT && what->vtype == TYPE_INTEGER)
+ op = INSTR_CONV_ITOF;
+ else if (vtype == TYPE_INTEGER && what->vtype == TYPE_FLOAT)
+ op = INSTR_CONV_FTOI;
#endif
- op = INSTR_STORE_F;
- break;
- case TYPE_VECTOR:
- op = INSTR_STORE_V;
- break;
- case TYPE_ENTITY:
- op = INSTR_STORE_ENT;
- break;
- case TYPE_STRING:
- op = INSTR_STORE_S;
- break;
- case TYPE_FIELD:
- op = INSTR_STORE_FLD;
- break;
-#if 0
- case TYPE_INTEGER:
- if (what->vtype == TYPE_INTEGER)
- op = INSTR_CONV_FTOI;
- else
- op = INSTR_STORE_I;
- break;
-#endif
- case TYPE_POINTER:
-#if 0
- op = INSTR_STORE_I;
-#else
- op = INSTR_STORE_ENT;
-#endif
- break;
- default:
- /* Unknown type */
- return false;
- }
+ op = type_store_instr[vtype];
+
return ir_block_create_store_op(self, op, target, what);
}
*/
vtype = what->vtype;
- switch (vtype) {
- case TYPE_FLOAT:
- op = INSTR_STOREP_F;
- break;
- case TYPE_VECTOR:
- op = INSTR_STOREP_V;
- break;
- case TYPE_ENTITY:
- op = INSTR_STOREP_ENT;
- break;
- case TYPE_STRING:
- op = INSTR_STOREP_S;
- break;
- case TYPE_FIELD:
- op = INSTR_STOREP_FLD;
- break;
-#if 0
- case TYPE_INTEGER:
- op = INSTR_STOREP_I;
- break;
-#endif
- case TYPE_POINTER:
-#if 0
- op = INSTR_STOREP_I;
-#else
- op = INSTR_STOREP_ENT;
-#endif
- break;
- default:
- /* Unknown type */
- return false;
- }
+ op = type_storep_instr[vtype];
return ir_block_create_store_op(self, op, target, what);
}
return ir_instr_phi_add(self, pe);
}
+/* call related code */
+ir_instr* ir_block_create_call(ir_block *self, const char *label, ir_value *func)
+{
+ ir_value *out;
+ ir_instr *in;
+ in = ir_instr_new(self, INSTR_CALL0);
+ if (!in)
+ return NULL;
+ out = ir_value_out(self->owner, label, store_return, func->outtype);
+ if (!out) {
+ ir_instr_delete(in);
+ return NULL;
+ }
+ if (!ir_instr_op(in, 0, out, true) ||
+ !ir_instr_op(in, 1, func, false) ||
+ !ir_block_instr_add(self, in))
+ {
+ ir_instr_delete(in);
+ ir_value_delete(out);
+ return NULL;
+ }
+ return in;
+}
+
+ir_value* ir_call_value(ir_instr *self)
+{
+ return self->_ops[0];
+}
+
+bool ir_call_param(ir_instr* self, ir_value *v)
+{
+ if (!ir_instr_params_add(self, v))
+ return false;
+ if (!ir_value_reads_add(v, self)) {
+ if (!ir_instr_params_remove(self, self->params_count-1))
+ GMQCC_SUPPRESS_EMPTY_BODY;
+ return false;
+ }
+ return true;
+}
+
/* binary op related code */
ir_value* ir_block_create_binop(ir_block *self,
case INSTR_ADD_V:
case INSTR_SUB_V:
case INSTR_MUL_VF:
- case INSTR_MUL_FV:
#if 0
case INSTR_DIV_VF:
case INSTR_MUL_IV:
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;
static bool function_allocator_alloc(function_allocator *alloc, const ir_value *var)
{
ir_value *slot;
- size_t vsize = 1;
+ size_t vsize = type_sizeof[var->vtype];
slot = ir_value_var("reg", store_global, var->vtype);
if (!slot)
return false;
- if (slot->vtype == TYPE_VECTOR || slot->vtype == TYPE_VARIANT)
- vsize = 3;
-
if (!ir_value_life_merge_into(slot, var))
goto localerror;
function_allocator alloc;
+ if (!self->locals_count)
+ return true;
+
MEM_VECTOR_INIT(&alloc, locals);
MEM_VECTOR_INIT(&alloc, sizes);
MEM_VECTOR_INIT(&alloc, positions);
goto error;
/* adjust size for this slot */
- if (v->vtype == TYPE_VECTOR || v->vtype == TYPE_VARIANT)
- alloc.sizes[a] = 3;
+ if (alloc.sizes[a] < type_sizeof[v->vtype])
+ alloc.sizes[a] = type_sizeof[v->vtype];
self->values[i]->code.local = a;
break;
if (!function_allocator_positions_add(&alloc, 0))
goto error;
+ if (alloc.sizes_count)
+ pos = alloc.positions[0] + alloc.sizes[0];
+ else
+ pos = 0;
for (i = 1; i < alloc.sizes_count; ++i)
{
pos = alloc.positions[i-1] + alloc.sizes[i-1];
static bool gen_blocks_recursive(ir_function *func, ir_block *block)
{
prog_section_statement stmt;
- prog_section_statement *stptr;
ir_instr *instr;
ir_block *target;
ir_block *ontrue;
/* otherwise we generate a jump instruction */
stmt.opcode = INSTR_GOTO;
- stmt.o1.s1 = (target->code_start-1) - code_statements_elements;
+ stmt.o1.s1 = (target->code_start) - code_statements_elements;
stmt.o2.s1 = 0;
stmt.o3.s1 = 0;
if (code_statements_add(stmt) < 0)
*/
stmt.o1.u1 = instr->_ops[0]->code.globaladdr;
-
+ stmt.o2.u1 = 0;
stmt.o3.s1 = 0;
+
if (ontrue->generated) {
stmt.opcode = INSTR_IF;
stmt.o2.s1 = (ontrue->code_start-1) - code_statements_elements;
}
/* neither ontrue nor onfalse exist */
stmt.opcode = INSTR_IFNOT;
- stidx = code_statements_elements - 1;
+ stidx = code_statements_elements;
if (code_statements_add(stmt) < 0)
return false;
- stptr = &code_statements_data[stidx];
/* on false we jump, so add ontrue-path */
if (!gen_blocks_recursive(func, ontrue))
return false;
/* fixup the jump address */
- stptr->o2.s1 = (ontrue->code_start-1) - (stidx+1);
+ code_statements_data[stidx].o2.s1 = code_statements_elements - stidx;
/* generate onfalse path */
if (onfalse->generated) {
+ /* fixup the jump address */
+ code_statements_data[stidx].o2.s1 = (onfalse->code_start) - (stidx);
/* may have been generated in the previous recursive call */
stmt.opcode = INSTR_GOTO;
+ stmt.o1.s1 = (onfalse->code_start) - code_statements_elements;
stmt.o2.s1 = 0;
stmt.o3.s1 = 0;
- stmt.o1.s1 = (onfalse->code_start-1) - code_statements_elements;
return (code_statements_add(stmt) >= 0);
}
/* if not, generate now */
}
if (instr->opcode >= INSTR_CALL0 && instr->opcode <= INSTR_CALL8) {
- printf("TODO: call instruction\n");
- return false;
+ /* Trivial call translation:
+ * copy all params to OFS_PARM*
+ * if the output's storetype is not store_return,
+ * add append a STORE instruction!
+ *
+ * NOTES on how to do it better without much trouble:
+ * -) The liferanges!
+ * Simply check the liferange of all parameters for
+ * other CALLs. For each param with no CALL in its
+ * liferange, we can store it in an OFS_PARM at
+ * generation already. This would even include later
+ * reuse.... probably... :)
+ */
+ size_t p;
+ ir_value *retvalue;
+
+ for (p = 0; p < instr->params_count; ++p)
+ {
+ ir_value *param = instr->params[p];
+
+ stmt.opcode = INSTR_STORE_F;
+ stmt.o3.u1 = 0;
+
+ stmt.opcode = type_store_instr[param->vtype];
+ stmt.o1.u1 = param->code.globaladdr;
+ stmt.o2.u1 = OFS_PARM0 + 3 * p;
+ if (code_statements_add(stmt) < 0)
+ return false;
+ }
+ stmt.opcode = INSTR_CALL0 + instr->params_count;
+ if (stmt.opcode > INSTR_CALL8)
+ stmt.opcode = INSTR_CALL8;
+ stmt.o1.u1 = instr->_ops[1]->code.globaladdr;
+ stmt.o2.u1 = 0;
+ stmt.o3.u1 = 0;
+ if (code_statements_add(stmt) < 0)
+ return false;
+
+ retvalue = instr->_ops[0];
+ if (retvalue && retvalue->store != store_return && retvalue->life_count)
+ {
+ /* not to be kept in OFS_RETURN */
+ stmt.opcode = type_store_instr[retvalue->vtype];
+ stmt.o1.u1 = OFS_RETURN;
+ stmt.o2.u1 = retvalue->code.globaladdr;
+ stmt.o3.u1 = 0;
+ if (code_statements_add(stmt) < 0)
+ return false;
+ }
+ continue;
}
if (instr->opcode == INSTR_STATE) {
static bool gen_function_code(ir_function *self)
{
ir_block *block;
+ prog_section_statement stmt;
/* Starting from entry point, we generate blocks "as they come"
* for now. Dead blocks will not be translated obviously.
printf("failed to generate blocks for '%s'\n", self->name);
return false;
}
+
+ /* otherwise code_write crashes since it debug-prints functions until AINSTR_END */
+ stmt.opcode = AINSTR_END;
+ stmt.o1.u1 = 0;
+ stmt.o2.u1 = 0;
+ stmt.o3.u1 = 0;
+ if (code_statements_add(stmt) < 0)
+ return false;
return true;
}
ir_function *irfun;
size_t i;
+ size_t local_var_end;
- if (!global->isconst ||
- !global->constval.vfunc)
+ if (!global->isconst || (!global->constval.vfunc))
{
printf("Invalid state of function-global: not constant: %s\n", global->name);
return false;
for (i = 0;i < 8; ++i) {
if (i >= fun.nargs)
fun.argsize[i] = 0;
- else if (irfun->params[i] == TYPE_VECTOR)
- fun.argsize[i] = 3;
else
- fun.argsize[i] = 1;
+ fun.argsize[i] = type_sizeof[irfun->params[i]];
}
fun.firstlocal = code_globals_elements;
- fun.locals = irfun->locals_count;
+ fun.locals = irfun->allocated_locals + irfun->locals_count;
+
+ local_var_end = 0;
for (i = 0; i < irfun->locals_count; ++i) {
if (!ir_builder_gen_global(ir, irfun->locals[i])) {
printf("Failed to generate global %s\n", irfun->locals[i]->name);
return false;
}
}
+ if (irfun->locals_count) {
+ ir_value *last = irfun->locals[irfun->locals_count-1];
+ local_var_end = last->code.globaladdr;
+ local_var_end += type_sizeof[last->vtype];
+ }
+ for (i = 0; i < irfun->values_count; ++i)
+ {
+ /* generate code.globaladdr for ssa values */
+ ir_value *v = irfun->values[i];
+ v->code.globaladdr = local_var_end + v->code.local;
+ }
+ for (i = 0; i < irfun->locals_count; ++i) {
+ /* fill the locals with zeros */
+ code_globals_add(0);
+ }
- fun.entry = code_statements_elements;
- if (!gen_function_code(irfun)) {
- printf("Failed to generate code for function %s\n", irfun->name);
- return false;
+ if (irfun->builtin)
+ fun.entry = irfun->builtin;
+ else {
+ fun.entry = code_statements_elements;
+ if (!gen_function_code(irfun)) {
+ printf("Failed to generate code for function %s\n", irfun->name);
+ return false;
+ }
}
return (code_functions_add(fun) >= 0);
return global->code.globaladdr >= 0;
}
case TYPE_VECTOR:
+ case TYPE_QUATERNION:
+ case TYPE_MATRIX:
{
+ size_t d;
if (code_defs_add(def) < 0)
return false;
if (global->isconst) {
iptr = (int32_t*)&global->constval.vvec;
global->code.globaladdr = code_globals_add(iptr[0]);
- if (code_globals_add(iptr[1]) < 0 || code_globals_add(iptr[2]) < 0)
+ if (global->code.globaladdr < 0)
return false;
+ for (d = 1; d < type_sizeof[global->vtype]; ++d)
+ {
+ if (code_globals_add(iptr[d]) < 0)
+ return false;
+ }
} else {
global->code.globaladdr = code_globals_add(0);
- if (code_globals_add(0) < 0 || code_globals_add(0) < 0)
+ if (global->code.globaladdr < 0)
return false;
+ for (d = 1; d < type_sizeof[global->vtype]; ++d)
+ {
+ if (code_globals_add(0) < 0)
+ return false;
+ }
}
return global->code.globaladdr >= 0;
}
case TYPE_FUNCTION:
if (code_defs_add(def) < 0)
return false;
+ global->code.globaladdr = code_globals_elements;
code_globals_add(code_functions_elements);
return gen_global_function(self, global);
case TYPE_VARIANT:
code_init();
- /* FIXME: generate TYPE_FUNCTION globals and link them
- * to their ir_function.
- */
-
- for (i = 0; i < self->functions_count; ++i)
- {
- ir_value *funval;
- ir_function *fun = self->functions[i];
-
- funval = ir_builder_create_global(self, fun->name, TYPE_FUNCTION);
- funval->isconst = true;
- funval->constval.vfunc = fun;
- funval->context = fun->context;
- }
-
for (i = 0; i < self->globals_count; ++i)
{
if (!ir_builder_gen_global(self, self->globals[i])) {
int (*oprintf)(const char*, ...))
{
size_t i;
+ if (f->builtin != 0) {
+ oprintf("%sfunction %s = builtin %i\n", ind, f->name, -f->builtin);
+ return;
+ }
oprintf("%sfunction %s\n", ind, f->name);
strncat(ind, "\t", IND_BUFSZ);
if (f->locals_count)
{
if (v->isconst) {
switch (v->vtype) {
+ default:
case TYPE_VOID:
oprintf("(void)");
break;