]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - ir.c
What was I thinking... fixing loop's jump creation so it doesn't try creating multipl...
[xonotic/gmqcc.git] / ir.c
diff --git a/ir.c b/ir.c
index 8e92d4da6b368c9f2577146562b8f88c2b8a4018..8e8ffb7db1926c743f24d7f883ea57e9d1d0419d 100644 (file)
--- a/ir.c
+++ b/ir.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 
+ * Copyright (C) 2012
  *     Wolfgang Bumiller
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy of
@@ -333,14 +333,14 @@ 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));
+            if (ir_value_writes_remove(self->phi[i].value, idx)) GMQCC_SUPRESS_EMPTY_BODY;
         if (ir_value_reads_find(self->phi[i].value, self, &idx))
-            if (ir_value_reads_remove(self->phi[i].value, idx));
+            if (ir_value_reads_remove (self->phi[i].value, idx)) GMQCC_SUPRESS_EMPTY_BODY;
     }
     MEM_VECTOR_CLEAR(self, phi);
-    if (ir_instr_op(self, 0, NULL, false));
-    if (ir_instr_op(self, 1, NULL, false));
-    if (ir_instr_op(self, 2, NULL, false));
+    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;
     mem_d(self);
 }
 
@@ -511,6 +511,7 @@ bool ir_value_life_merge(ir_value *self, size_t s)
     }
     /* nothing found? append */
     if (i == self->life_count) {
+        ir_life_entry_t e;
         if (life && life->end+1 == s)
         {
             /* previous life range can be merged in */
@@ -519,7 +520,6 @@ bool ir_value_life_merge(ir_value *self, size_t s)
         }
         if (life && life->end >= s)
             return false;
-        ir_life_entry_t e;
         e.start = e.end = s;
         if (!ir_value_life_add(self, e))
             return false; /* failing */
@@ -558,6 +558,65 @@ bool ir_value_life_merge(ir_value *self, size_t s)
     return ir_value_life_insert(self, i, new_entry);
 }
 
+bool ir_values_overlap(ir_value *a, ir_value *b)
+{
+    /* For any life entry in A see if it overlaps with
+     * any life entry in B.
+     * Note that the life entries are orderes, so we can make a
+     * more efficient algorithm there than naively translating the
+     * statement above.
+     */
+
+    ir_life_entry_t *la, *lb, *enda, *endb;
+
+    /* first of all, if either has no life range, they cannot clash */
+    if (!a->life_count || !b->life_count)
+        return false;
+
+    la = a->life;
+    lb = b->life;
+    enda = la + a->life_count;
+    endb = lb + b->life_count;
+    while (true)
+    {
+        /* check if the entries overlap, for that,
+         * both must start before the other one ends.
+         */
+#if defined(LIFE_RANGE_WITHOUT_LAST_READ)
+        if (la->start <= lb->end &&
+            lb->start <= la->end)
+#else
+        if (la->start <  lb->end &&
+            lb->start <  la->end)
+#endif
+        {
+            return true;
+        }
+
+        /* entries are ordered
+         * one entry is earlier than the other
+         * that earlier entry will be moved forward
+         */
+        if (la->end < lb->end)
+        {
+            /* order: A B, move A forward
+             * check if we hit the end with A
+             */
+            if (++la == enda)
+                break;
+        }
+        else if (lb->end < la->end)
+        {
+            /* order: B A, move B forward
+             * check if we hit the end with B
+             */
+            if (++lb == endb)
+                break;
+        }
+    }
+    return false;
+}
+
 /***********************************************************************
  *IR main operations
  */
@@ -566,6 +625,7 @@ bool ir_block_create_store_op(ir_block *self, int op, ir_value *target, ir_value
 {
     if (target->store == store_value) {
         fprintf(stderr, "cannot store to an SSA value\n");
+        fprintf(stderr, "trying to store: %s <- %s\n", target->name, what->name);
         return false;
     } else {
         ir_instr *in = ir_instr_new(self, op);
@@ -608,6 +668,9 @@ bool ir_block_create_store(ir_block *self, ir_value *target, ir_value *what)
         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)
@@ -623,6 +686,57 @@ bool ir_block_create_store(ir_block *self, ir_value *target, ir_value *what)
             op = INSTR_STORE_ENT;
 #endif
             break;
+        default:
+            /* Unknown type */
+            return false;
+    }
+    return ir_block_create_store_op(self, op, target, what);
+}
+
+bool ir_block_create_storep(ir_block *self, ir_value *target, ir_value *what)
+{
+    int op = 0;
+    int vtype;
+
+    if (target->vtype != TYPE_POINTER)
+        return false;
+
+    /* storing using pointer - target is a pointer, type must be
+     * inferred from source
+     */
+    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;
     }
     return ir_block_create_store_op(self, op, target, what);
 }
@@ -657,7 +771,7 @@ bool ir_block_create_if(ir_block *self, ir_value *v,
         return false;
     }
     self->final = true;
-    //in = ir_instr_new(self, (v->vtype == TYPE_STRING ? INSTR_IF_S : INSTR_IF_F));
+    /*in = ir_instr_new(self, (v->vtype == TYPE_STRING ? INSTR_IF_S : INSTR_IF_F));*/
     in = ir_instr_new(self, VINSTR_COND);
     if (!in)
         return false;
@@ -738,7 +852,7 @@ ir_instr* ir_block_create_phi(ir_block *self, const char *label, int ot)
     in = ir_instr_new(self, VINSTR_PHI);
     if (!in)
         return NULL;
-    out = ir_value_out(self->owner, label, store_local, ot);
+    out = ir_value_out(self->owner, label, store_value, ot);
     if (!out) {
         ir_instr_delete(in);
         return NULL;
@@ -854,7 +968,7 @@ ir_value* ir_block_create_binop(ir_block *self,
             break;
 #endif
         default:
-            // ranges:
+            /* ranges: */
             /* boolean operations result in floats */
             if (opcode >= INSTR_EQ_F && opcode <= INSTR_GT)
                 ot = TYPE_FLOAT;
@@ -871,33 +985,82 @@ ir_value* ir_block_create_binop(ir_block *self,
         return NULL;
     }
 
-    ir_value *out = ir_value_out(self->owner, label, store_local, ot);
+    return ir_block_create_general_instr(self, label, opcode, left, right, ot);
+}
+
+ir_value* ir_block_create_general_instr(ir_block *self, const char *label,
+                                        int op, ir_value *a, ir_value *b, int outype)
+{
+    ir_instr *instr;
+    ir_value *out;
+
+    out = ir_value_out(self->owner, label, store_value, outype);
     if (!out)
         return NULL;
 
-    ir_instr *in = ir_instr_new(self, opcode);
-    if (!in) {
+    instr = ir_instr_new(self, op);
+    if (!instr) {
         ir_value_delete(out);
         return NULL;
     }
 
-    if (!ir_instr_op(in, 0, out, true) ||
-        !ir_instr_op(in, 1, left, false) ||
-        !ir_instr_op(in, 2, right, false) )
+    if (!ir_instr_op(instr, 0, out, true) ||
+        !ir_instr_op(instr, 1, a, false) ||
+        !ir_instr_op(instr, 2, b, false) )
     {
         goto on_error;
     }
 
-    if (!ir_block_instr_add(self, in))
+    if (!ir_block_instr_add(self, instr))
         goto on_error;
 
     return out;
 on_error:
+    ir_instr_delete(instr);
     ir_value_delete(out);
-    ir_instr_delete(in);
     return NULL;
 }
 
+ir_value* ir_block_create_fieldaddress(ir_block *self, const char *label, ir_value *ent, ir_value *field)
+{
+    /* Support for various pointer types todo if so desired */
+    if (ent->vtype != TYPE_ENTITY)
+        return NULL;
+
+    if (field->vtype != TYPE_FIELD)
+        return NULL;
+
+    return ir_block_create_general_instr(self, label, INSTR_ADDRESS, ent, field, TYPE_POINTER);
+}
+
+ir_value* ir_block_create_load_from_ent(ir_block *self, const char *label, ir_value *ent, ir_value *field, int outype)
+{
+    int op;
+    if (ent->vtype != TYPE_ENTITY)
+        return NULL;
+
+    /* at some point we could redirect for TYPE_POINTER... but that could lead to carelessness */
+    if (field->vtype != TYPE_FIELD)
+        return NULL;
+
+    switch (outype)
+    {
+        case TYPE_FLOAT:   op = INSTR_LOAD_F;   break;
+        case TYPE_VECTOR:  op = INSTR_LOAD_V;   break;
+        case TYPE_STRING:  op = INSTR_LOAD_S;   break;
+        case TYPE_FIELD:   op = INSTR_LOAD_FLD; break;
+        case TYPE_ENTITY:  op = INSTR_LOAD_ENT; break;
+#if 0
+        case TYPE_POINTER: op = INSTR_LOAD_I;   break;
+        case TYPE_INTEGER: op = INSTR_LOAD_I;   break;
+#endif
+        default:
+            return NULL;
+    }
+
+    return ir_block_create_general_instr(self, label, op, ent, field, outype);
+}
+
 ir_value* ir_block_create_add(ir_block *self,
                               const char *label,
                               ir_value *left, ir_value *right)
@@ -1120,7 +1283,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_local)
+                if (old->store != store_value && old->store != store_local)
                 {
                     /* If it originally wrote to a global we need to store the value
                      * there as welli
@@ -1315,14 +1478,19 @@ static bool ir_block_life_propagate(ir_block *self, ir_block *prev, bool *change
     ir_instr *instr;
     ir_value *value;
     bool  tempbool;
-    size_t i, o, p, rd;
+    size_t i, o, p;
     /* bitmasks which operands are read from or written to */
     size_t read, write;
+#if defined(LIFE_RANGE_WITHOUT_LAST_READ)
+    size_t rd;
     new_reads_t new_reads;
+#endif
     char dbg_ind[16] = { '#', '0' };
     (void)dbg_ind;
 
+#if defined(LIFE_RANGE_WITHOUT_LAST_READ)
     MEM_VECTOR_INIT(&new_reads, v);
+#endif
 
     if (prev)
     {
@@ -1339,16 +1507,19 @@ static bool ir_block_life_propagate(ir_block *self, ir_block *prev, bool *change
         for (p = 0; p < instr->phi_count; ++p)
         {
             value = instr->phi[p].value;
-            /* used this before new_reads - puts the last read into the life range as well
-            if (!ir_block_living_find(self, value, NULL))
-                ir_block_living_add(self, value);
-            */
-            /* fprintf(stderr, "read: %s\n", value->_name); */
+#if ! defined(LIFE_RANGE_WITHOUT_LAST_READ)
+            if (!ir_block_living_find(self, value, NULL) &&
+                !ir_block_living_add(self, value))
+            {
+                goto on_error;
+            }
+#else
             if (!new_reads_t_v_find(&new_reads, value, NULL))
             {
                 if (!new_reads_t_v_add(&new_reads, value))
                     goto on_error;
             }
+#endif
         }
 
         /* See which operands are read and write operands */
@@ -1370,16 +1541,20 @@ static bool ir_block_life_propagate(ir_block *self, ir_block *prev, bool *change
             /* read operands */
             if (read & (1<<o))
             {
-                /* used this before new_reads - puts the last read into the life range as well
-                if (!ir_block_living_find(self, value, NULL))
-                    ir_block_living_add(self, value);
-                */
+#if ! defined(LIFE_RANGE_WITHOUT_LAST_READ)
+                if (!ir_block_living_find(self, value, NULL) &&
+                    !ir_block_living_add(self, value))
+                {
+                    goto on_error;
+                }
+#else
                 /* fprintf(stderr, "read: %s\n", value->_name); */
                 if (!new_reads_t_v_find(&new_reads, value, NULL))
                 {
                     if (!new_reads_t_v_add(&new_reads, value))
                         goto on_error;
                 }
+#endif
             }
 
             /* write operands */
@@ -1389,10 +1564,15 @@ static bool ir_block_life_propagate(ir_block *self, ir_block *prev, bool *change
              */
             if (write & (1<<o))
             {
-                size_t idx, readidx;
+                size_t idx;
                 bool in_living = ir_block_living_find(self, value, &idx);
+#if defined(LIFE_RANGE_WITHOUT_LAST_READ)
+                size_t readidx;
                 bool in_reads = new_reads_t_v_find(&new_reads, value, &readidx);
                 if (!in_living && !in_reads)
+#else
+                if (!in_living)
+#endif
                 {
                     /* If the value isn't alive it hasn't been read before... */
                     /* TODO: See if the warning can be emitted during parsing or AST processing
@@ -1421,21 +1601,25 @@ static bool ir_block_life_propagate(ir_block *self, ir_block *prev, bool *change
                     */
                     *changed = *changed || tempbool;
                     /* Then remove */
+#if ! defined(LIFE_RANGE_WITHOUT_LAST_READ)
                     if (!ir_block_living_remove(self, idx))
                         goto on_error;
+#else
                     if (in_reads)
                     {
                         if (!new_reads_t_v_remove(&new_reads, readidx))
                             goto on_error;
                     }
+#endif
                 }
             }
         }
         /* (A) */
         tempbool = ir_block_living_add_instr(self, instr->eid);
-        //fprintf(stderr, "living added values\n");
+        /*fprintf(stderr, "living added values\n");*/
         *changed = *changed || tempbool;
 
+#if defined(LIFE_RANGE_WITHOUT_LAST_READ)
         /* new reads: */
         for (rd = 0; rd < new_reads.v_count; ++rd)
         {
@@ -1449,6 +1633,7 @@ static bool ir_block_life_propagate(ir_block *self, ir_block *prev, bool *change
             }
         }
         MEM_VECTOR_CLEAR(&new_reads, v);
+#endif
     }
 
     if (self->run_id == self->owner->run_id)
@@ -1464,7 +1649,9 @@ static bool ir_block_life_propagate(ir_block *self, ir_block *prev, bool *change
 
     return true;
 on_error:
+#if defined(LIFE_RANGE_WITHOUT_LAST_READ)
     MEM_VECTOR_CLEAR(&new_reads, v);
+#endif
     return false;
 }
 
@@ -1525,10 +1712,13 @@ void ir_function_dump(ir_function *f, char *ind,
        }
        if (f->blocks_count)
        {
-
-               oprintf("%slife passes: %i\n", ind, (int)f->blocks[0]->run_id);
-               for (i = 0; i < f->blocks_count; ++i)
+               oprintf("%slife passes (check): %i\n", ind, (int)f->run_id);
+               for (i = 0; i < f->blocks_count; ++i) {
+                   if (f->blocks[i]->run_id != f->run_id) {
+                       oprintf("%slife pass check fail! %i != %i\n", ind, (int)f->blocks[i]->run_id, (int)f->run_id);
+                   }
                        ir_block_dump(f->blocks[i], ind, oprintf);
+               }
 
        }
        ind[strlen(ind)-1] = 0;