]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - ir.c
Merge branch 'master' into blub/bc3
[xonotic/gmqcc.git] / ir.c
diff --git a/ir.c b/ir.c
index 557a821886cd3df4488142c1ef6642e2b2427fa7..fb6f4e90ca330bde92f830b4590abdfa0df265c1 100644 (file)
--- a/ir.c
+++ b/ir.c
  * 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   */
@@ -41,7 +58,9 @@ size_t type_sizeof[TYPE_COUNT] = {
 #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] = {
@@ -54,9 +73,30 @@ 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_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_STORE_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)
@@ -75,6 +115,7 @@ ir_builder* ir_builder_new(const char *modulename)
 
     MEM_VECTOR_INIT(self, functions);
     MEM_VECTOR_INIT(self, globals);
+    MEM_VECTOR_INIT(self, fields);
     self->name = NULL;
     if (!ir_builder_set_name(self, modulename)) {
         mem_d(self);
@@ -90,6 +131,7 @@ ir_builder* ir_builder_new(const char *modulename)
 }
 
 MEM_VEC_FUNCTIONS(ir_builder, ir_value*, globals)
+MEM_VEC_FUNCTIONS(ir_builder, ir_value*, fields)
 MEM_VEC_FUNCTIONS(ir_builder, ir_function*, functions)
 
 void ir_builder_delete(ir_builder* self)
@@ -103,7 +145,11 @@ void ir_builder_delete(ir_builder* self)
     for (i = 0; i != self->globals_count; ++i) {
         ir_value_delete(self->globals[i]);
     }
-    MEM_VECTOR_CLEAR(self, globals);
+    MEM_VECTOR_CLEAR(self, fields);
+    for (i = 0; i != self->fields_count; ++i) {
+        ir_value_delete(self->fields[i]);
+    }
+    MEM_VECTOR_CLEAR(self, fields);
     mem_d(self);
 }
 
@@ -166,9 +212,14 @@ ir_value* ir_builder_get_global(ir_builder *self, const char *name)
 
 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);
@@ -179,6 +230,33 @@ ir_value* ir_builder_create_global(ir_builder *self, const char *name, int vtype
     return ve;
 }
 
+ir_value* ir_builder_get_field(ir_builder *self, const char *name)
+{
+    size_t i;
+    for (i = 0; i < self->fields_count; ++i) {
+        if (!strcmp(self->fields[i]->name, name))
+            return self->fields[i];
+    }
+    return NULL;
+}
+
+
+ir_value* ir_builder_create_field(ir_builder *self, const char *name, int vtype)
+{
+    ir_value *ve = ir_builder_get_field(self, name);
+    if (ve) {
+        return NULL;
+    }
+
+    ve = ir_value_var(name, store_global, TYPE_FIELD);
+    ve->fieldtype = vtype;
+    if (!ir_builder_fields_add(self, ve)) {
+        ir_value_delete(ve);
+        return NULL;
+    }
+    return ve;
+}
+
 /***********************************************************************
  *IR Function
  */
@@ -296,14 +374,21 @@ ir_value* ir_function_get_local(ir_function *self, const char *name)
     return NULL;
 }
 
-ir_value* ir_function_create_local(ir_function *self, const char *name, int vtype)
+ir_value* ir_function_create_local(ir_function *self, const char *name, int vtype, bool param)
 {
     ir_value *ve = ir_function_get_local(self, name);
     if (ve) {
         return NULL;
     }
 
-    ve = ir_value_var(name, store_local, vtype);
+    if (param &&
+        self->locals_count &&
+        self->locals[self->locals_count-1]->store != store_param) {
+        printf("cannot add parameters after adding locals\n");
+        return NULL;
+    }
+
+    ve = ir_value_var(name, (param ? store_param : store_local), vtype);
     if (!ir_function_locals_add(self, ve)) {
         ir_value_delete(ve);
         return NULL;
@@ -552,6 +637,24 @@ bool ir_value_set_vector(ir_value *self, vector v)
     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)
@@ -830,46 +933,14 @@ bool ir_block_create_store(ir_block *self, ir_value *target, ir_value *what)
     else
         vtype = target->vtype;
 
-    switch (vtype) {
-        case TYPE_FLOAT:
-#if 0
-            if (what->vtype == TYPE_INTEGER)
-                op = INSTR_CONV_ITOF;
-            else
-#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;
+    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
-        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);
 }
 
@@ -886,38 +957,7 @@ bool ir_block_create_storep(ir_block *self, ir_value *target, ir_value *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);
 }
 
@@ -1209,6 +1249,39 @@ ir_value* ir_block_create_binop(ir_block *self,
     return ir_block_create_general_instr(self, label, opcode, left, right, ot);
 }
 
+ir_value* ir_block_create_unary(ir_block *self,
+                                const char *label, int opcode,
+                                ir_value *operand)
+{
+    int ot = TYPE_FLOAT;
+    switch (opcode) {
+        case INSTR_NOT_F:
+        case INSTR_NOT_V:
+        case INSTR_NOT_S:
+        case INSTR_NOT_ENT:
+        case INSTR_NOT_FNC:
+#if 0
+        case INSTR_NOT_I:
+#endif
+            ot = TYPE_FLOAT;
+            break;
+        /* QC doesn't have other unary operations. We expect extensions to fill
+         * the above list, otherwise we assume out-type = in-type, eg for an
+         * unary minus
+         */
+        default:
+            ot = operand->vtype;
+            break;
+    };
+    if (ot == TYPE_VOID) {
+        /* The AST or parser were supposed to check this! */
+        return NULL;
+    }
+
+    /* let's use the general instruction creator and pass NULL for OPB */
+    return ir_block_create_general_instr(self, label, opcode, operand, NULL, ot);
+}
+
 ir_value* ir_block_create_general_instr(ir_block *self, const char *label,
                                         int op, ir_value *a, ir_value *b, int outype)
 {
@@ -1244,6 +1317,8 @@ on_error:
 
 ir_value* ir_block_create_fieldaddress(ir_block *self, const char *label, ir_value *ent, ir_value *field)
 {
+    ir_value *v;
+
     /* Support for various pointer types todo if so desired */
     if (ent->vtype != TYPE_ENTITY)
         return NULL;
@@ -1251,7 +1326,9 @@ ir_value* ir_block_create_fieldaddress(ir_block *self, const char *label, ir_val
     if (field->vtype != TYPE_FIELD)
         return NULL;
 
-    return ir_block_create_general_instr(self, label, INSTR_ADDRESS, ent, field, TYPE_POINTER);
+    v = ir_block_create_general_instr(self, label, INSTR_ADDRESS, ent, field, TYPE_POINTER);
+    v->fieldtype = field->fieldtype;
+    return v;
 }
 
 ir_value* ir_block_create_load_from_ent(ir_block *self, const char *label, ir_value *ent, ir_value *field, int outype)
@@ -1275,6 +1352,8 @@ ir_value* ir_block_create_load_from_ent(ir_block *self, const char *label, ir_va
         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;
     }
@@ -1378,12 +1457,22 @@ ir_value* ir_block_create_mul(ir_block *self,
             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;
@@ -1504,7 +1593,7 @@ static bool ir_block_naive_phi(ir_block *self)
                 if (v->writes[w]->_ops[0] == v)
                     v->writes[w]->_ops[0] = instr->_ops[0];
 
-                if (old->store != store_value && old->store != store_local)
+                if (old->store != store_value && old->store != store_local && old->store != store_param)
                 {
                     /* If it originally wrote to a global we need to store the value
                      * there as welli
@@ -1884,8 +1973,11 @@ static bool ir_block_life_propagate(ir_block *self, ir_block *prev, bool *change
             value = instr->_ops[o];
 
             /* We only care about locals */
+            /* we also calculate parameter liferanges so that locals
+             * can take up parameter slots */
             if (value->store != store_value &&
-                value->store != store_local)
+                value->store != store_local &&
+                value->store != store_param)
                 continue;
 
             /* read operands */
@@ -2050,21 +2142,7 @@ static bool gen_global_field(ir_value *global)
     }
     else
     {
-        prog_section_field fld;
-
-        fld.name = global->code.name;
-        fld.offset = code_fields_elements;
-        fld.type = global->fieldtype;
-
-        if (fld.type == TYPE_VOID) {
-            printf("Field is missing a type: %s\n", global->name);
-            return false;
-        }
-
-        if (code_fields_add(fld) < 0)
-            return false;
-
-        global->code.globaladdr = code_globals_add(fld.offset);
+        global->code.globaladdr = code_globals_add(0);
     }
     if (global->code.globaladdr < 0)
         return false;
@@ -2263,7 +2341,7 @@ tailcall:
                 if (code_statements_add(stmt) < 0)
                     return false;
             }
-            return false;
+            continue;
         }
 
         if (instr->opcode == INSTR_STATE) {
@@ -2291,10 +2369,8 @@ tailcall:
             stmt.o1.u1 = stmt.o3.u1;
             stmt.o3.u1 = 0;
         }
-        else if ((stmt.opcode >= INSTR_STORE_F    &&
-                  stmt.opcode <= INSTR_STORE_FNC)    ||
-                 (stmt.opcode >= INSTR_NOT_F      &&
-                  stmt.opcode <= INSTR_NOT_FNC))
+        else if (stmt.opcode >= INSTR_STORE_F &&
+                 stmt.opcode <= INSTR_STORE_FNC)
         {
             /* 2-operand instructions with A -> B */
             stmt.o2.u1 = stmt.o3.u1;
@@ -2310,6 +2386,7 @@ tailcall:
 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.
@@ -2327,6 +2404,14 @@ static bool gen_function_code(ir_function *self)
         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;
 }
 
@@ -2354,10 +2439,8 @@ static bool gen_global_function(ir_builder *ir, ir_value *global)
     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;
@@ -2401,6 +2484,7 @@ static bool gen_global_function(ir_builder *ir, ir_value *global)
 
 static bool ir_builder_gen_global(ir_builder *self, ir_value *global)
 {
+    size_t           i;
     int32_t         *iptr;
     prog_section_def def;
 
@@ -2444,32 +2528,46 @@ static bool ir_builder_gen_global(ir_builder *self, ir_value *global)
         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:
         /* assume biggest type */
             global->code.globaladdr = code_globals_add(0);
-            code_globals_add(0);
-            code_globals_add(0);
+            for (i = 1; i < type_sizeof[TYPE_VARIANT]; ++i)
+                code_globals_add(0);
             return true;
     default:
         /* refuse to create 'void' type or any other fancy business. */
@@ -2478,12 +2576,50 @@ static bool ir_builder_gen_global(ir_builder *self, ir_value *global)
     }
 }
 
+static bool ir_builder_gen_field(ir_builder *self, ir_value *field)
+{
+    prog_section_def def;
+    prog_section_field fld;
+
+    def.type   = field->vtype;
+    def.offset = code_globals_elements;
+    def.name   = field->code.name = code_genstring(field->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) {
+        printf("field is missing a type: %s - don't know its size\n", field->name);
+        return false;
+    }
+
+    if (code_fields_add(fld) < 0)
+        return false;
+
+    if (!code_globals_add(code_alloc_field(type_sizeof[field->fieldtype])))
+        return false;
+
+    field->code.globaladdr = code_globals_add(fld.offset);
+    return field->code.globaladdr >= 0;
+}
+
 bool ir_builder_generate(ir_builder *self, const char *filename)
 {
     size_t i;
 
     code_init();
 
+    for (i = 0; i < self->fields_count; ++i)
+    {
+        if (!ir_builder_gen_field(self, self->fields[i])) {
+            return false;
+        }
+    }
+
     for (i = 0; i < self->globals_count; ++i)
     {
         if (!ir_builder_gen_global(self, self->globals[i])) {
@@ -2539,6 +2675,10 @@ void ir_function_dump(ir_function *f, char *ind,
                       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)
@@ -2642,6 +2782,7 @@ void ir_value_dump(ir_value* v, int (*oprintf)(const char*, ...))
 {
        if (v->isconst) {
                switch (v->vtype) {
+                   default:
                        case TYPE_VOID:
                                oprintf("(void)");
                                break;