]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - ir.c
Implementation of liferange overlap test
[xonotic/gmqcc.git] / ir.c
diff --git a/ir.c b/ir.c
index 7bb1f6d7c4af7b3ab96c4cb6534783dc00b05d11..cbfe304c1fca8ac2c7d4b39f5885a12b84ef10fd 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
@@ -45,7 +45,7 @@ ir_builder* ir_builder_new(const char *modulename)
     /* globals which always exist */
 
     /* for now we give it a vector size */
-    ir_builder_create_global(self, "OFS_RETURN", qc_variant);
+    ir_builder_create_global(self, "OFS_RETURN", TYPE_VARIANT);
 
     return self;
 }
@@ -140,6 +140,7 @@ ir_function* ir_function_new(ir_builder* owner)
 {
     ir_function *self;
     self = (ir_function*)mem_a(sizeof(*self));
+    self->name = NULL;
     if (!ir_function_set_name(self, "<@unnamed>")) {
         mem_d(self);
         return NULL;
@@ -147,7 +148,7 @@ ir_function* ir_function_new(ir_builder* owner)
     self->owner = owner;
     self->context.file = "<@no context>";
     self->context.line = 0;
-    self->retype = qc_void;
+    self->retype = TYPE_VOID;
     MEM_VECTOR_INIT(self, params);
     MEM_VECTOR_INIT(self, blocks);
     MEM_VECTOR_INIT(self, values);
@@ -251,6 +252,7 @@ ir_block* ir_block_new(ir_function* owner, const char *name)
 {
     ir_block *self;
     self = (ir_block*)mem_a(sizeof(*self));
+    self->label = NULL;
     if (!ir_block_set_label(self, name)) {
         mem_d(self);
         return NULL;
@@ -262,7 +264,6 @@ ir_block* ir_block_new(ir_function* owner, const char *name)
     MEM_VECTOR_INIT(self, instr);
     MEM_VECTOR_INIT(self, entries);
     MEM_VECTOR_INIT(self, exits);
-    self->label = NULL;
 
     self->eid = 0;
     self->is_return = false;
@@ -278,7 +279,7 @@ MEM_VEC_FUNCTIONS_ALL(ir_block, ir_value*, living)
 void ir_block_delete(ir_block* self)
 {
     size_t i;
-    mem_d((void*)self->label);
+    mem_d(self->label);
     for (i = 0; i != self->instr_count; ++i)
         ir_instr_delete(self->instr[i]);
     MEM_VECTOR_CLEAR(self, instr);
@@ -332,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);
 }
 
@@ -414,7 +415,7 @@ void ir_value_delete(ir_value* self)
     mem_d((void*)self->name);
     if (self->isconst)
     {
-        if (self->vtype == qc_string)
+        if (self->vtype == TYPE_STRING)
             mem_d((void*)self->constval.vstring);
     }
     MEM_VECTOR_CLEAR(self, reads);
@@ -432,16 +433,16 @@ void ir_value_set_name(ir_value *self, const char *name)
 
 bool ir_value_set_float(ir_value *self, float f)
 {
-    if (self->vtype != qc_float)
+    if (self->vtype != TYPE_FLOAT)
         return false;
     self->constval.vfloat = f;
     self->isconst = true;
     return true;
 }
 
-bool ir_value_set_vector(ir_value *self, vector_t v)
+bool ir_value_set_vector(ir_value *self, vector v)
 {
-    if (self->vtype != qc_vector)
+    if (self->vtype != TYPE_VECTOR)
         return false;
     self->constval.vvec = v;
     self->isconst = true;
@@ -450,21 +451,23 @@ bool ir_value_set_vector(ir_value *self, vector_t v)
 
 bool ir_value_set_string(ir_value *self, const char *str)
 {
-    if (self->vtype != qc_string)
+    if (self->vtype != TYPE_STRING)
         return false;
     self->constval.vstring = util_strdup(str);
     self->isconst = true;
     return true;
 }
 
+#if 0
 bool ir_value_set_int(ir_value *self, int i)
 {
-    if (self->vtype != qc_int)
+    if (self->vtype != TYPE_INTEGER)
         return false;
     self->constval.vint = i;
     self->isconst = true;
     return true;
 }
+#endif
 
 bool ir_value_lives(ir_value *self, size_t at)
 {
@@ -508,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 */
@@ -516,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 */
@@ -555,6 +558,60 @@ 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 (la->start <= lb->end &&
+            lb->start <= la->end)
+        {
+            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
  */
@@ -582,44 +639,98 @@ bool ir_block_create_store(ir_block *self, ir_value *target, ir_value *what)
 {
     int op = 0;
     int vtype;
-    if (target->vtype == qc_variant)
+    if (target->vtype == TYPE_VARIANT)
         vtype = what->vtype;
     else
         vtype = target->vtype;
 
     switch (vtype) {
-        case qc_float:
+        case TYPE_FLOAT:
 #if 0
-            if (what->vtype == qc_int)
+            if (what->vtype == TYPE_INTEGER)
                 op = INSTR_CONV_ITOF;
             else
 #endif
                 op = INSTR_STORE_F;
             break;
-        case qc_vector:
+        case TYPE_VECTOR:
             op = INSTR_STORE_V;
             break;
-        case qc_entity:
+        case TYPE_ENTITY:
             op = INSTR_STORE_ENT;
             break;
-        case qc_string:
+        case TYPE_STRING:
             op = INSTR_STORE_S;
             break;
+        case TYPE_FIELD:
+            op = INSTR_STORE_FLD;
+            break;
 #if 0
-        case qc_int:
-            if (what->vtype == qc_int)
+        case TYPE_INTEGER:
+            if (what->vtype == TYPE_INTEGER)
                 op = INSTR_CONV_FTOI;
             else
                 op = INSTR_STORE_I;
             break;
 #endif
-        case qc_pointer:
+        case TYPE_POINTER:
 #if 0
             op = INSTR_STORE_I;
 #else
             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);
 }
@@ -654,7 +765,7 @@ bool ir_block_create_if(ir_block *self, ir_value *v,
         return false;
     }
     self->final = true;
-    //in = ir_instr_new(self, (v->vtype == qc_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;
@@ -783,7 +894,10 @@ ir_value* ir_block_create_binop(ir_block *self,
                                 const char *label, int opcode,
                                 ir_value *left, ir_value *right)
 {
-    int ot = qc_void;
+    ir_value *out = NULL;
+    ir_instr *in  = NULL;
+
+    int ot = TYPE_VOID;
     switch (opcode) {
         case INSTR_ADD_F:
         case INSTR_SUB_F:
@@ -815,7 +929,7 @@ ir_value* ir_block_create_binop(ir_block *self,
         case INSTR_EQ_I:
         case INSTR_NE_I:
 #endif
-            ot = qc_float;
+            ot = TYPE_FLOAT;
             break;
 #if 0
         case INSTR_ADD_I:
@@ -831,7 +945,7 @@ ir_value* ir_block_create_binop(ir_block *self,
         case INSTR_XOR_I:
         case INSTR_RSHIFT_I:
         case INSTR_LSHIFT_I:
-            ot = qc_int;
+            ot = TYPE_INTEGER;
             break;
 #endif
         case INSTR_ADD_V:
@@ -843,36 +957,36 @@ ir_value* ir_block_create_binop(ir_block *self,
         case INSTR_MUL_IV:
         case INSTR_MUL_VI:
 #endif
-            ot = qc_vector;
+            ot = TYPE_VECTOR;
             break;
 #if 0
         case INSTR_ADD_SF:
-            ot = qc_pointer;
+            ot = TYPE_POINTER;
             break;
 #endif
         default:
-            // ranges:
+            /* ranges: */
             /* boolean operations result in floats */
             if (opcode >= INSTR_EQ_F && opcode <= INSTR_GT)
-                ot = qc_float;
+                ot = TYPE_FLOAT;
             else if (opcode >= INSTR_LE && opcode <= INSTR_GT)
-                ot = qc_float;
+                ot = TYPE_FLOAT;
 #if 0
             else if (opcode >= INSTR_LE_I && opcode <= INSTR_EQ_FI)
-                ot = qc_float;
+                ot = TYPE_FLOAT;
 #endif
             break;
     };
-    if (ot == qc_void) {
+    if (ot == TYPE_VOID) {
         /* The AST or parser were supposed to check this! */
         return NULL;
     }
 
-    ir_value *out = ir_value_out(self->owner, label, store_local, ot);
+    out = ir_value_out(self->owner, label, store_local, ot);
     if (!out)
         return NULL;
 
-    ir_instr *in = ir_instr_new(self, opcode);
+    in = ir_instr_new(self, opcode);
     if (!in) {
         ir_value_delete(out);
         return NULL;
@@ -890,8 +1004,63 @@ ir_value* ir_block_create_binop(ir_block *self,
 
     return out;
 on_error:
-    ir_value_delete(out);
     ir_instr_delete(in);
+    ir_value_delete(out);
+    return NULL;
+}
+
+ir_value* ir_block_create_load_from_ent(ir_block *self, const char *label, ir_value *ent, ir_value *field, int outype)
+{
+    ir_instr *instr;
+    ir_value *out;
+    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;
+    }
+
+    out = ir_value_out(self->owner, label, store_local, outype);
+    if (!out)
+        return NULL;
+
+    instr = ir_instr_new(self, op);
+    if (!instr) {
+        ir_value_delete(out);
+        return NULL;
+    }
+
+    if (!ir_instr_op(instr, 0, out, true) ||
+        !ir_instr_op(instr, 0, ent, false) ||
+        !ir_instr_op(instr, 0, field, false))
+    {
+        goto on_error;
+    }
+
+    if (!ir_block_instr_add(self, instr))
+        goto on_error;
+
+    return out;
+on_error:
+    ir_instr_delete(instr);
+    ir_value_delete(out);
     return NULL;
 }
 
@@ -906,23 +1075,23 @@ ir_value* ir_block_create_add(ir_block *self,
         switch (l) {
             default:
                 return NULL;
-            case qc_float:
+            case TYPE_FLOAT:
                 op = INSTR_ADD_F;
                 break;
 #if 0
-            case qc_int:
+            case TYPE_INTEGER:
                 op = INSTR_ADD_I;
                 break;
 #endif
-            case qc_vector:
+            case TYPE_VECTOR:
                 op = INSTR_ADD_V;
                 break;
         }
     } else {
 #if 0
-        if ( (l == qc_float && r == qc_int) )
+        if ( (l == TYPE_FLOAT && r == TYPE_INTEGER) )
             op = INSTR_ADD_FI;
-        else if ( (l == qc_int && r == qc_float) )
+        else if ( (l == TYPE_INTEGER && r == TYPE_FLOAT) )
             op = INSTR_ADD_IF;
         else
 #endif
@@ -943,23 +1112,23 @@ ir_value* ir_block_create_sub(ir_block *self,
         switch (l) {
             default:
                 return NULL;
-            case qc_float:
+            case TYPE_FLOAT:
                 op = INSTR_SUB_F;
                 break;
 #if 0
-            case qc_int:
+            case TYPE_INTEGER:
                 op = INSTR_SUB_I;
                 break;
 #endif
-            case qc_vector:
+            case TYPE_VECTOR:
                 op = INSTR_SUB_V;
                 break;
         }
     } else {
 #if 0
-        if ( (l == qc_float && r == qc_int) )
+        if ( (l == TYPE_FLOAT && r == TYPE_INTEGER) )
             op = INSTR_SUB_FI;
-        else if ( (l == qc_int && r == qc_float) )
+        else if ( (l == TYPE_INTEGER && r == TYPE_FLOAT) )
             op = INSTR_SUB_IF;
         else
 #endif
@@ -980,31 +1149,31 @@ ir_value* ir_block_create_mul(ir_block *self,
         switch (l) {
             default:
                 return NULL;
-            case qc_float:
+            case TYPE_FLOAT:
                 op = INSTR_MUL_F;
                 break;
 #if 0
-            case qc_int:
+            case TYPE_INTEGER:
                 op = INSTR_MUL_I;
                 break;
 #endif
-            case qc_vector:
+            case TYPE_VECTOR:
                 op = INSTR_MUL_V;
                 break;
         }
     } else {
-        if ( (l == qc_vector && r == qc_float) )
+        if ( (l == TYPE_VECTOR && r == TYPE_FLOAT) )
             op = INSTR_MUL_VF;
-        else if ( (l == qc_float && r == qc_vector) )
+        else if ( (l == TYPE_FLOAT && r == TYPE_VECTOR) )
             op = INSTR_MUL_FV;
 #if 0
-        else if ( (l == qc_vector && r == qc_int) )
+        else if ( (l == TYPE_VECTOR && r == TYPE_INTEGER) )
             op = INSTR_MUL_VI;
-        else if ( (l == qc_int && r == qc_vector) )
+        else if ( (l == TYPE_INTEGER && r == TYPE_VECTOR) )
             op = INSTR_MUL_IV;
-        else if ( (l == qc_float && r == qc_int) )
+        else if ( (l == TYPE_FLOAT && r == TYPE_INTEGER) )
             op = INSTR_MUL_FI;
-        else if ( (l == qc_int && r == qc_float) )
+        else if ( (l == TYPE_INTEGER && r == TYPE_FLOAT) )
             op = INSTR_MUL_IF;
 #endif
         else
@@ -1025,22 +1194,22 @@ ir_value* ir_block_create_div(ir_block *self,
         switch (l) {
             default:
                 return NULL;
-            case qc_float:
+            case TYPE_FLOAT:
                 op = INSTR_DIV_F;
                 break;
 #if 0
-            case qc_int:
+            case TYPE_INTEGER:
                 op = INSTR_DIV_I;
                 break;
 #endif
         }
     } else {
 #if 0
-        if ( (l == qc_vector && r == qc_float) )
+        if ( (l == TYPE_VECTOR && r == TYPE_FLOAT) )
             op = INSTR_DIV_VF;
-        else if ( (l == qc_float && r == qc_int) )
+        else if ( (l == TYPE_FLOAT && r == TYPE_INTEGER) )
             op = INSTR_DIV_FI;
-        else if ( (l == qc_int && r == qc_float) )
+        else if ( (l == TYPE_INTEGER && r == TYPE_FLOAT) )
             op = INSTR_DIV_IF;
         else
 #endif
@@ -1430,7 +1599,7 @@ static bool ir_block_life_propagate(ir_block *self, ir_block *prev, bool *change
         }
         /* (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;
 
         /* new reads: */
@@ -1464,3 +1633,190 @@ on_error:
     MEM_VECTOR_CLEAR(&new_reads, v);
     return false;
 }
+
+/***********************************************************************
+ *IR DEBUG Dump functions...
+ */
+
+#define IND_BUFSZ 1024
+
+const char *qc_opname(int op)
+{
+    if (op < 0) return "<INVALID>";
+    if (op < ( sizeof(asm_instr) / sizeof(asm_instr[0]) ))
+        return asm_instr[op].m;
+    switch (op) {
+        case VINSTR_PHI:  return "PHI";
+        case VINSTR_JUMP: return "JUMP";
+        case VINSTR_COND: return "COND";
+        default:          return "<UNK>";
+    }
+}
+
+void ir_builder_dump(ir_builder *b, int (*oprintf)(const char*, ...))
+{
+       size_t i;
+       char indent[IND_BUFSZ];
+       indent[0] = '\t';
+       indent[1] = 0;
+
+       oprintf("module %s\n", b->name);
+       for (i = 0; i < b->globals_count; ++i)
+       {
+               oprintf("global ");
+               if (b->globals[i]->isconst)
+                       oprintf("%s = ", b->globals[i]->name);
+               ir_value_dump(b->globals[i], oprintf);
+               oprintf("\n");
+       }
+       for (i = 0; i < b->functions_count; ++i)
+               ir_function_dump(b->functions[i], indent, oprintf);
+       oprintf("endmodule %s\n", b->name);
+}
+
+void ir_function_dump(ir_function *f, char *ind,
+                      int (*oprintf)(const char*, ...))
+{
+       size_t i;
+       oprintf("%sfunction %s\n", ind, f->name);
+       strncat(ind, "\t", IND_BUFSZ);
+       if (f->locals_count)
+       {
+               oprintf("%s%i locals:\n", ind, (int)f->locals_count);
+               for (i = 0; i < f->locals_count; ++i) {
+                       oprintf("%s\t", ind);
+                       ir_value_dump(f->locals[i], oprintf);
+                       oprintf("\n");
+               }
+       }
+       if (f->blocks_count)
+       {
+
+               oprintf("%slife passes: %i\n", ind, (int)f->blocks[0]->run_id);
+               for (i = 0; i < f->blocks_count; ++i)
+                       ir_block_dump(f->blocks[i], ind, oprintf);
+
+       }
+       ind[strlen(ind)-1] = 0;
+       oprintf("%sendfunction %s\n", ind, f->name);
+}
+
+void ir_block_dump(ir_block* b, char *ind,
+                   int (*oprintf)(const char*, ...))
+{
+       size_t i;
+       oprintf("%s:%s\n", ind, b->label);
+       strncat(ind, "\t", IND_BUFSZ);
+
+       for (i = 0; i < b->instr_count; ++i)
+               ir_instr_dump(b->instr[i], ind, oprintf);
+       ind[strlen(ind)-1] = 0;
+}
+
+void dump_phi(ir_instr *in, char *ind,
+              int (*oprintf)(const char*, ...))
+{
+       size_t i;
+       oprintf("%s <- phi ", in->_ops[0]->name);
+       for (i = 0; i < in->phi_count; ++i)
+       {
+               oprintf("([%s] : %s) ", in->phi[i].from->label,
+                                       in->phi[i].value->name);
+       }
+       oprintf("\n");
+}
+
+void ir_instr_dump(ir_instr *in, char *ind,
+                       int (*oprintf)(const char*, ...))
+{
+       size_t i;
+       const char *comma = NULL;
+
+       oprintf("%s (%i) ", ind, (int)in->eid);
+
+       if (in->opcode == VINSTR_PHI) {
+               dump_phi(in, ind, oprintf);
+               return;
+       }
+
+       strncat(ind, "\t", IND_BUFSZ);
+
+       if (in->_ops[0] && (in->_ops[1] || in->_ops[2])) {
+               ir_value_dump(in->_ops[0], oprintf);
+               if (in->_ops[1] || in->_ops[2])
+                       oprintf(" <- ");
+       }
+       oprintf("%s\t", qc_opname(in->opcode));
+       if (in->_ops[0] && !(in->_ops[1] || in->_ops[2])) {
+               ir_value_dump(in->_ops[0], oprintf);
+               comma = ",\t";
+       }
+       else
+       {
+               for (i = 1; i != 3; ++i) {
+                       if (in->_ops[i]) {
+                               if (comma)
+                                       oprintf(comma);
+                               ir_value_dump(in->_ops[i], oprintf);
+                               comma = ",\t";
+                       }
+               }
+       }
+       if (in->bops[0]) {
+               if (comma)
+                       oprintf(comma);
+               oprintf("[%s]", in->bops[0]->label);
+               comma = ",\t";
+       }
+       if (in->bops[1])
+               oprintf("%s[%s]", comma, in->bops[1]->label);
+       oprintf("\n");
+       ind[strlen(ind)-1] = 0;
+}
+
+void ir_value_dump(ir_value* v, int (*oprintf)(const char*, ...))
+{
+       if (v->isconst) {
+               switch (v->vtype) {
+                       case TYPE_VOID:
+                               oprintf("(void)");
+                               break;
+                       case TYPE_FLOAT:
+                               oprintf("%g", v->constval.vfloat);
+                               break;
+                       case TYPE_VECTOR:
+                               oprintf("'%g %g %g'",
+                                       v->constval.vvec.x,
+                                       v->constval.vvec.y,
+                                       v->constval.vvec.z);
+                               break;
+                       case TYPE_ENTITY:
+                               oprintf("(entity)");
+                               break;
+                       case TYPE_STRING:
+                               oprintf("\"%s\"", v->constval.vstring);
+                               break;
+#if 0
+                       case TYPE_INTEGER:
+                               oprintf("%i", v->constval.vint);
+                               break;
+#endif
+                       case TYPE_POINTER:
+                               oprintf("&%s",
+                                       v->constval.vpointer->name);
+                               break;
+               }
+       } else {
+               oprintf("%s", v->name);
+       }
+}
+
+void ir_value_dump_life(ir_value *self, int (*oprintf)(const char*,...))
+{
+       size_t i;
+       oprintf("Life of %s:\n", self->name);
+       for (i = 0; i < self->life_count; ++i)
+       {
+               oprintf(" + [%i, %i]\n", self->life[i].start, self->life[i].end);
+       }
+}