]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - ir.c
ir_function_create_block now takes a lex_ctx instead of copying from the function...
[xonotic/gmqcc.git] / ir.c
diff --git a/ir.c b/ir.c
index 1fd94a08a13fc482409d51c58c1d8a91c61ed606..f0a8a84fbd6d51e618b21d0e8aac80f7bb998755 100644 (file)
--- a/ir.c
+++ b/ir.c
@@ -171,6 +171,28 @@ uint16_t type_ne_instr[TYPE_COUNT] = {
     AINSTR_END, /* array  */
 };
 
+uint16_t type_not_instr[TYPE_COUNT] = {
+    INSTR_NOT_F, /* should use I when having integer support */
+    INSTR_NOT_S,
+    INSTR_NOT_F,
+    INSTR_NOT_V,
+    INSTR_NOT_ENT,
+    INSTR_NOT_ENT,
+    INSTR_NOT_FNC,
+    INSTR_NOT_ENT, /* should use I */
+#if 0
+    INSTR_NOT_I, /* integer type */
+#else
+    INSTR_NOT_F,
+#endif
+
+    INSTR_NOT_V, /* variant, should never be accessed */
+
+    AINSTR_END, /* struct */
+    AINSTR_END, /* union  */
+    AINSTR_END, /* array  */
+};
+
 static void irerror(lex_ctx ctx, const char *msg, ...)
 {
     va_list ap;
@@ -262,6 +284,9 @@ ir_builder* ir_builder_new(const char *modulename)
     self->extparams   = NULL;
     self->filenames   = NULL;
     self->filestrings = NULL;
+    self->htglobals   = util_htnew(IR_HT_SIZE);
+    self->htfields    = util_htnew(IR_HT_SIZE);
+    self->htfunctions = util_htnew(IR_HT_SIZE);
 
     self->str_immediate = 0;
     self->name = NULL;
@@ -276,6 +301,9 @@ ir_builder* ir_builder_new(const char *modulename)
 void ir_builder_delete(ir_builder* self)
 {
     size_t i;
+    util_htdel(self->htglobals);
+    util_htdel(self->htfields);
+    util_htdel(self->htfunctions);
     mem_d((void*)self->name);
     for (i = 0; i != vec_size(self->functions); ++i) {
         ir_function_delete_quick(self->functions[i]);
@@ -308,12 +336,7 @@ bool ir_builder_set_name(ir_builder *self, const char *name)
 
 ir_function* ir_builder_get_function(ir_builder *self, const char *name)
 {
-    size_t i;
-    for (i = 0; i < vec_size(self->functions); ++i) {
-        if (!strcmp(name, self->functions[i]->name))
-            return self->functions[i];
-    }
-    return NULL;
+    return (ir_function*)util_htget(self->htfunctions, name);
 }
 
 ir_function* ir_builder_create_function(ir_builder *self, const char *name, int outtype)
@@ -330,6 +353,7 @@ ir_function* ir_builder_create_function(ir_builder *self, const char *name, int
         return NULL;
     }
     vec_push(self->functions, fn);
+    util_htset(self->htfunctions, name, fn);
 
     fn->value = ir_builder_create_global(self, fn->name, TYPE_FUNCTION);
     if (!fn->value) {
@@ -347,12 +371,7 @@ ir_function* ir_builder_create_function(ir_builder *self, const char *name, int
 
 ir_value* ir_builder_get_global(ir_builder *self, const char *name)
 {
-    size_t i;
-    for (i = 0; i < vec_size(self->globals); ++i) {
-        if (!strcmp(self->globals[i]->name, name))
-            return self->globals[i];
-    }
-    return NULL;
+    return (ir_value*)util_htget(self->htglobals, name);
 }
 
 ir_value* ir_builder_create_global(ir_builder *self, const char *name, int vtype)
@@ -369,17 +388,13 @@ ir_value* ir_builder_create_global(ir_builder *self, const char *name, int vtype
 
     ve = ir_value_var(name, store_global, vtype);
     vec_push(self->globals, ve);
+    util_htset(self->htglobals, name, ve);
     return ve;
 }
 
 ir_value* ir_builder_get_field(ir_builder *self, const char *name)
 {
-    size_t i;
-    for (i = 0; i < vec_size(self->fields); ++i) {
-        if (!strcmp(self->fields[i]->name, name))
-            return self->fields[i];
-    }
-    return NULL;
+    return (ir_value*)util_htget(self->htfields, name);
 }
 
 
@@ -393,6 +408,7 @@ ir_value* ir_builder_create_field(ir_builder *self, const char *name, int vtype)
     ve = ir_value_var(name, store_global, TYPE_FIELD);
     ve->fieldtype = vtype;
     vec_push(self->fields, ve);
+    util_htset(self->htfields, name, ve);
     return ve;
 }
 
@@ -432,8 +448,6 @@ ir_function* ir_function_new(ir_builder* owner, int outtype)
     self->values = NULL;
     self->locals = NULL;
 
-    self->max_parameters = 0;
-
     self->code_function_def = -1;
     self->allocated_locals = 0;
 
@@ -502,10 +516,10 @@ void ir_function_collect_value(ir_function *self, ir_value *v)
     vec_push(self->values, v);
 }
 
-ir_block* ir_function_create_block(ir_function *self, const char *label)
+ir_block* ir_function_create_block(lex_ctx ctx, ir_function *self, const char *label)
 {
     ir_block* bn = ir_block_new(self, label);
-    memcpy(&bn->context, &self->context, sizeof(self->context));
+    bn->context = ctx;
     vec_push(self->blocks, bn);
     return bn;
 }
@@ -522,31 +536,15 @@ bool ir_function_finalize(ir_function *self)
 
     if (!ir_function_calculate_liferanges(self))
         return false;
-
     if (!ir_function_allocate_locals(self))
         return false;
     return true;
 }
 
-ir_value* ir_function_get_local(ir_function *self, const char *name)
-{
-    size_t i;
-    for (i = 0; i < vec_size(self->locals); ++i) {
-        if (!strcmp(self->locals[i]->name, name))
-            return self->locals[i];
-    }
-    return NULL;
-}
-
 ir_value* ir_function_create_local(ir_function *self, const char *name, int vtype, bool param)
 {
     ir_value *ve;
 
-    /*
-    if (ir_function_get_local(self, name))
-        return NULL;
-    */
-
     if (param &&
         vec_size(self->locals) &&
         self->locals[vec_size(self->locals)-1]->store != store_param) {
@@ -656,6 +654,8 @@ ir_instr* ir_instr_new(ir_block* owner, int op)
     self->params = NULL;
 
     self->eid = 0;
+
+    self->likely = true;
     return self;
 }
 
@@ -1135,7 +1135,12 @@ bool ir_values_overlap(const ir_value *a, const ir_value *b)
 
 bool ir_block_create_store_op(ir_block *self, int op, ir_value *target, ir_value *what)
 {
-    ir_instr *in = ir_instr_new(self, op);
+    ir_instr *in;
+    if (self->final) {
+        irerror(self->context, "unreachable statement (%s)", self->label);
+        return false;
+    }
+    in = ir_instr_new(self, op);
     if (!in)
         return false;
 
@@ -1208,7 +1213,7 @@ bool ir_block_create_return(ir_block *self, ir_value *v)
 {
     ir_instr *in;
     if (self->final) {
-        irerror(self->context, "block already ended (%s)", self->label);
+        irerror(self->context, "unreachable statement (%s)", self->label);
         return false;
     }
     self->final = true;
@@ -1229,7 +1234,7 @@ bool ir_block_create_if(ir_block *self, ir_value *v,
 {
     ir_instr *in;
     if (self->final) {
-        irerror(self->context, "block already ended (%s)", self->label);
+        irerror(self->context, "unreachable statement (%s)", self->label);
         return false;
     }
     self->final = true;
@@ -1259,7 +1264,7 @@ bool ir_block_create_jump(ir_block *self, ir_block *to)
 {
     ir_instr *in;
     if (self->final) {
-        irerror(self->context, "block already ended (%s)", self->label);
+        irerror(self->context, "unreachable statement (%s)", self->label);
         return false;
     }
     self->final = true;
@@ -1279,7 +1284,7 @@ bool ir_block_create_goto(ir_block *self, ir_block *to)
 {
     ir_instr *in;
     if (self->final) {
-        irerror(self->context, "block already ended (%s)", self->label);
+        irerror(self->context, "unreachable statement (%s)", self->label);
         return false;
     }
     self->final = true;
@@ -1765,6 +1770,7 @@ bool ir_function_naive_phi(ir_function *self)
     return true;
 }
 
+#if 0
 static bool ir_naive_phi_emit_store(ir_block *block, size_t iid, ir_value *old, ir_value *what)
 {
     ir_instr *instr;
@@ -1782,10 +1788,11 @@ static bool ir_naive_phi_emit_store(ir_block *block, size_t iid, ir_value *old,
 
     return true;
 }
+#endif
 
 static bool ir_block_naive_phi(ir_block *self)
 {
-    size_t i, p, w;
+    size_t i, p; /*, w;*/
     /* FIXME: optionally, create_phi can add the phis
      * to a list so we don't need to loop through blocks
      * - anyway: "don't optimize YET"
@@ -1801,6 +1808,32 @@ static bool ir_block_naive_phi(ir_block *self)
 
         for (p = 0; p < vec_size(instr->phi); ++p)
         {
+            ir_value *v = instr->phi[p].value;
+            ir_block *b = instr->phi[p].from;
+
+            if (v->store == store_value &&
+                vec_size(v->reads) == 1 &&
+                vec_size(v->writes) == 1)
+            {
+                /* replace the value */
+                if (!ir_instr_op(v->writes[0], 0, instr->_ops[0], true))
+                    return false;
+            }
+            else
+            {
+                /* force a move instruction */
+                ir_instr *prevjump = vec_last(b->instr);
+                vec_pop(b->instr);
+                b->final = false;
+                instr->_ops[0]->store = store_global;
+                if (!ir_block_create_store(b, instr->_ops[0], v))
+                    return false;
+                instr->_ops[0]->store = store_value;
+                vec_push(b->instr, prevjump);
+                b->final = true;
+            }
+
+#if 0
             ir_value *v = instr->phi[p].value;
             for (w = 0; w < vec_size(v->writes); ++w) {
                 ir_value *old;
@@ -1850,6 +1883,7 @@ static bool ir_block_naive_phi(ir_block *self)
                     }
                 }
             }
+#endif
         }
         ir_instr_delete(instr);
     }
@@ -2115,6 +2149,9 @@ static bool ir_block_living_add_instr(ir_block *self, size_t eid)
 static bool ir_block_life_prop_previous(ir_block* self, ir_block *prev, bool *changed)
 {
     size_t i;
+
+    (void)changed;
+
     /* values which have been read in a previous iteration are now
      * in the "living" array even if the previous block doesn't use them.
      * So we have to remove whatever does not exist in the previous block.
@@ -2469,6 +2506,13 @@ tailcall:
             }
             /* neither ontrue nor onfalse exist */
             stmt.opcode = INSTR_IFNOT;
+            if (!instr->likely) {
+                /* Honor the likelyhood hint */
+                ir_block *tmp = onfalse;
+                stmt.opcode = INSTR_IF;
+                onfalse = ontrue;
+                ontrue = tmp;
+            }
             stidx = vec_size(code_statements);
             vec_push(code_statements, stmt);
             /* on false we jump, so add ontrue-path */
@@ -2480,6 +2524,16 @@ tailcall:
             if (onfalse->generated) {
                 /* fixup the jump address */
                 code_statements[stidx].o2.s1 = (onfalse->code_start) - (stidx);
+                stmt.opcode = vec_last(code_statements).opcode;
+                if (stmt.opcode == INSTR_GOTO ||
+                    stmt.opcode == INSTR_IF ||
+                    stmt.opcode == INSTR_IFNOT ||
+                    stmt.opcode == INSTR_RETURN ||
+                    stmt.opcode == INSTR_DONE)
+                {
+                    /* no use jumping from here */
+                    return true;
+                }
                 /* may have been generated in the previous recursive call */
                 stmt.opcode = INSTR_GOTO;
                 stmt.o1.s1 = (onfalse->code_start) - vec_size(code_statements);
@@ -2507,10 +2561,13 @@ tailcall:
              *      generation already. This would even include later
              *      reuse.... probably... :)
              */
-            size_t p;
+            size_t p, first;
             ir_value *retvalue;
 
-            for (p = 0; p < vec_size(instr->params); ++p)
+            first = vec_size(instr->params);
+            if (first > 8)
+                first = 8;
+            for (p = 0; p < first; ++p)
             {
                 ir_value *param = instr->params[p];
 
@@ -2525,6 +2582,33 @@ tailcall:
                 stmt.o2.u1 = OFS_PARM0 + 3 * p;
                 vec_push(code_statements, stmt);
             }
+            /* Now handle extparams */
+            first = vec_size(instr->params);
+            for (; p < first; ++p)
+            {
+                ir_builder *ir = func->owner;
+                ir_value *param = instr->params[p];
+                ir_value *targetparam;
+
+                if (p-8 >= vec_size(ir->extparams)) {
+                    irerror(instr->context, "Not enough extparam-globals have been created");
+                    return false;
+                }
+
+                targetparam = ir->extparams[p-8];
+
+                stmt.opcode = INSTR_STORE_F;
+                stmt.o3.u1 = 0;
+
+                if (param->vtype == TYPE_FIELD)
+                    stmt.opcode = field_store_instr[param->fieldtype];
+                else
+                    stmt.opcode = type_store_instr[param->vtype];
+                stmt.o1.u1 = ir_value_code_addr(param);
+                stmt.o2.u1 = ir_value_code_addr(targetparam);
+                vec_push(code_statements, stmt);
+            }
+
             stmt.opcode = INSTR_CALL0 + vec_size(instr->params);
             if (stmt.opcode > INSTR_CALL8)
                 stmt.opcode = INSTR_CALL8;
@@ -2659,6 +2743,8 @@ static bool gen_global_function(ir_builder *ir, ir_value *global)
     fun.file    = ir_builder_filestring(ir, global->context.file);
     fun.profile = 0; /* always 0 */
     fun.nargs   = vec_size(irfun->params);
+    if (fun.nargs > 8)
+        fun.nargs = 8;
 
     for (i = 0;i < 8; ++i) {
         if (i >= fun.nargs)
@@ -2711,7 +2797,7 @@ static void ir_gen_extparam(ir_builder *ir)
     ir_value        *global;
     char             name[128];
 
-    snprintf(name, sizeof(name), "EXTPARM%i", (int)(vec_size(ir->extparams)+8));
+    snprintf(name, sizeof(name), "EXTPARM#%i", (int)(vec_size(ir->extparams)+8));
     global = ir_value_var(name, store_global, TYPE_VECTOR);
 
     def.name = code_genstring(name);
@@ -2729,18 +2815,19 @@ static void ir_gen_extparam(ir_builder *ir)
 
 static bool gen_function_extparam_copy(ir_function *self)
 {
-    size_t i, ext;
+    size_t i, ext, numparams;
 
     ir_builder *ir = self->owner;
     ir_value   *ep;
     prog_section_statement stmt;
 
-    if (!self->max_parameters)
+    numparams = vec_size(self->params);
+    if (!numparams)
         return true;
 
     stmt.opcode = INSTR_STORE_F;
     stmt.o3.s1 = 0;
-    for (i = 8; i < self->max_parameters; ++i) {
+    for (i = 8; i < numparams; ++i) {
         ext = i - 8;
         if (ext >= vec_size(ir->extparams))
             ir_gen_extparam(ir);
@@ -2766,6 +2853,8 @@ static bool gen_global_function_code(ir_builder *ir, ir_value *global)
     prog_section_function *fundef;
     ir_function           *irfun;
 
+    (void)ir;
+
     irfun = global->constval.vfunc;
     if (!irfun) {
         irwarning(global->context, WARN_IMPLICIT_FUNCTION_POINTER,
@@ -2940,8 +3029,10 @@ 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 = vec_size(code_globals);
+    (void)self;
+
+    def.type   = (uint16_t)field->vtype;
+    def.offset = (uint16_t)vec_size(code_globals);
 
     /* create a global named the same as the field */
     if (opts_standard == COMPILER_GMQCC) {
@@ -3029,6 +3120,11 @@ bool ir_builder_generate(ir_builder *self, const char *filename)
         }
     }
 
+    if (vec_size(code_globals) >= 65536) {
+        irerror(vec_last(self->globals)->context, "This progs file would require more globals than the metadata can handle. Bailing out.");
+        return false;
+    }
+
     /* DP errors if the last instruction is not an INSTR_DONE
      * and for debugging purposes we add an additional AINSTR_END
      * to the end of functions, so here it goes:
@@ -3057,7 +3153,7 @@ bool ir_builder_generate(ir_builder *self, const char *filename)
 const char *qc_opname(int op)
 {
     if (op < 0) return "<INVALID>";
-    if (op < ( sizeof(asm_instr) / sizeof(asm_instr[0]) ))
+    if (op < (int)( sizeof(asm_instr) / sizeof(asm_instr[0]) ))
         return asm_instr[op].m;
     switch (op) {
         case VINSTR_PHI:  return "PHI";
@@ -3153,8 +3249,7 @@ void ir_block_dump(ir_block* b, char *ind,
     ind[strlen(ind)-1] = 0;
 }
 
-void dump_phi(ir_instr *in, char *ind,
-              int (*oprintf)(const char*, ...))
+void dump_phi(ir_instr *in, int (*oprintf)(const char*, ...))
 {
     size_t i;
     oprintf("%s <- phi ", in->_ops[0]->name);
@@ -3175,7 +3270,7 @@ void ir_instr_dump(ir_instr *in, char *ind,
     oprintf("%s (%i) ", ind, (int)in->eid);
 
     if (in->opcode == VINSTR_PHI) {
-        dump_phi(in, ind, oprintf);
+        dump_phi(in, oprintf);
         return;
     }
 
@@ -3224,6 +3319,26 @@ void ir_instr_dump(ir_instr *in, char *ind,
     ind[strlen(ind)-1] = 0;
 }
 
+void ir_value_dump_string(const char *str, int (*oprintf)(const char*, ...))
+{
+    oprintf("\"");
+    for (; *str; ++str) {
+        switch (*str) {
+            case '\n': oprintf("\\n"); break;
+            case '\r': oprintf("\\r"); break;
+            case '\t': oprintf("\\t"); break;
+            case '\v': oprintf("\\v"); break;
+            case '\f': oprintf("\\f"); break;
+            case '\b': oprintf("\\b"); break;
+            case '\a': oprintf("\\a"); break;
+            case '\\': oprintf("\\\\"); break;
+            case '"': oprintf("\\\""); break;
+            default: oprintf("%c", *str); break;
+        }
+    }
+    oprintf("\"");
+}
+
 void ir_value_dump(ir_value* v, int (*oprintf)(const char*, ...))
 {
     if (v->isconst) {
@@ -3248,7 +3363,7 @@ void ir_value_dump(ir_value* v, int (*oprintf)(const char*, ...))
                 oprintf("(entity)");
                 break;
             case TYPE_STRING:
-                oprintf("\"%s\"", v->constval.vstring);
+                ir_value_dump_string(v->constval.vstring, oprintf);
                 break;
 #if 0
             case TYPE_INTEGER: