]> git.rm.cloudns.org Git - xonotic/gmqcc.git/commitdiff
implement bitfield-liferanges
authorWolfgang Bumiller <wry.git@bumiller.com>
Wed, 9 Apr 2014 14:26:02 +0000 (16:26 +0200)
committerWolfgang Bumiller <wry.git@bumiller.com>
Wed, 9 Apr 2014 14:26:02 +0000 (16:26 +0200)
ast.c
include.mk
ir.c
ir.h
liveness.c [new file with mode: 0644]
liveness.h [new file with mode: 0644]

diff --git a/ast.c b/ast.c
index 8b3f110f3fd18e2ff1a14222ca6bbbe749fef8fa..f4df684759dbbba155c8bee486de1e9154b16882 100644 (file)
--- a/ast.c
+++ b/ast.c
@@ -27,6 +27,7 @@
 #include "gmqcc.h"
 #include "ast.h"
 #include "parser.h"
+#include "liveness.h"
 
 #define ast_instantiate(T, ctx, destroyfn)                          \
     T* self = (T*)mem_a(sizeof(T));                                 \
@@ -1795,7 +1796,7 @@ bool ast_generate_accessors(ast_value *self, ir_builder *ir)
             compile_error(ast_ctx(self), "internal error: not all array values have been generated for `%s`", self->name);
             return false;
         }
-        if (self->ir_values[i]->life) {
+        if (self->ir_values[i]->life.alive->bits) {
             compile_error(ast_ctx(self), "internal error: function containing `%s` already generated", self->name);
             return false;
         }
@@ -1823,7 +1824,8 @@ bool ast_generate_accessors(ast_value *self, ir_builder *ir)
         }
     }
     for (i = 0; i < self->expression.count; ++i) {
-        vec_free(self->ir_values[i]->life);
+        ir_lifemask_clear(&self->ir_values[i]->life);
+        ir_lifemask_init (&self->ir_values[i]->life);
     }
     opts_set(opts.warn, WARN_USED_UNINITIALIZED, warn);
     return true;
index 23c3fb94c45d7562a8deb1670db3e8e19c0e81be..8fb22a6f9bc6f948b9e43d79592e1c4d7a84a069 100644 (file)
@@ -20,7 +20,7 @@ OPTIONAL_CFLAGS  :=
 OPTIONAL_LDFLAGS :=
 
 #objects
-OBJ_C = $(COMMON) main.o lexer.o parser.o code.o ast.o ir.o ftepp.o utf8.o correct.o fold.o intrin.o
+OBJ_C = $(COMMON) main.o lexer.o parser.o code.o ast.o ir.o ftepp.o utf8.o correct.o fold.o intrin.o liveness.o
 OBJ_P = $(COMMON) pak.o
 OBJ_T = $(COMMON) test.o
 OBJ_X = $(COMMON) exec.o
diff --git a/ir.c b/ir.c
index ef782f91e5548c512e17b3054b4e4438cbe7e525..49460d908689b7453eb8bd314ee2780d507b2953 100644 (file)
--- a/ir.c
+++ b/ir.c
@@ -1117,7 +1117,7 @@ ir_value* ir_value_var(const char *name, int storetype, int vtype)
     self->locked      = false;
     self->callparam   = false;
 
-    self->life = NULL;
+    ir_lifemask_init(&self->life);
     return self;
 }
 
@@ -1212,7 +1212,7 @@ void ir_value_delete(ir_value* self)
     }
     vec_free(self->reads);
     vec_free(self->writes);
-    vec_free(self->life);
+    ir_lifemask_clear(&self->life);
     mem_d(self);
 }
 
@@ -1282,217 +1282,30 @@ bool ir_value_set_int(ir_value *self, int i)
 
 bool ir_value_lives(ir_value *self, size_t at)
 {
-    size_t i;
-    for (i = 0; i < vec_size(self->life); ++i)
-    {
-        ir_life_entry_t *life = &self->life[i];
-        if (life->start <= at && at <= life->end)
-            return true;
-        if (life->start > at) /* since it's ordered */
-            return false;
-    }
-    return false;
-}
-
-static bool ir_value_life_insert(ir_value *self, size_t idx, ir_life_entry_t e)
-{
-    size_t k;
-    vec_push(self->life, e);
-    for (k = vec_size(self->life)-1; k > idx; --k)
-        self->life[k] = self->life[k-1];
-    self->life[idx] = e;
-    return true;
+    return ir_bitlist_getbit(self->life.alive, at);
 }
 
-static bool ir_value_life_merge(ir_value *self, size_t s)
+GMQCC_INLINE static bool ir_value_life_merge(ir_value *self, size_t s, bool wr)
 {
-    size_t i;
-    const size_t vs = vec_size(self->life);
-    ir_life_entry_t *life = NULL;
-    ir_life_entry_t *before = NULL;
-    ir_life_entry_t new_entry;
-
-    /* Find the first range >= s */
-    for (i = 0; i < vs; ++i)
-    {
-        before = life;
-        life = &self->life[i];
-        if (life->start > s)
-            break;
-    }
-    /* nothing found? append */
-    if (i == vs) {
-        ir_life_entry_t e;
-        if (life && life->end+1 == s)
-        {
-            /* previous life range can be merged in */
-            life->end++;
-            return true;
-        }
-        if (life && life->end >= s)
-            return false;
-        e.start = e.end = s;
-        vec_push(self->life, e);
-        return true;
-    }
-    /* found */
-    if (before)
-    {
-        if (before->end + 1 == s &&
-            life->start - 1 == s)
-        {
-            /* merge */
-            before->end = life->end;
-            vec_remove(self->life, i, 1);
-            return true;
-        }
-        if (before->end + 1 == s)
-        {
-            /* extend before */
-            before->end++;
-            return true;
-        }
-        /* already contained */
-        if (before->end >= s)
-            return false;
-    }
-    /* extend */
-    if (life->start - 1 == s)
-    {
-        life->start--;
-        return true;
-    }
-    /* insert a new entry */
-    new_entry.start = new_entry.end = s;
-    return ir_value_life_insert(self, i, new_entry);
-}
-
-static bool ir_value_life_merge_into(ir_value *self, const ir_value *other)
-{
-    size_t i, myi;
-
-    if (!vec_size(other->life))
-        return true;
-
-    if (!vec_size(self->life)) {
-        size_t count = vec_size(other->life);
-        ir_life_entry_t *life = vec_add(self->life, count);
-        memcpy(life, other->life, count * sizeof(*life));
-        return true;
-    }
-
-    myi = 0;
-    for (i = 0; i < vec_size(other->life); ++i)
-    {
-        const ir_life_entry_t *life = &other->life[i];
-        while (true)
-        {
-            ir_life_entry_t *entry = &self->life[myi];
-
-            if (life->end+1 < entry->start)
-            {
-                /* adding an interval before entry */
-                if (!ir_value_life_insert(self, myi, *life))
-                    return false;
-                ++myi;
-                break;
-            }
-
-            if (life->start <  entry->start &&
-                life->end+1 >= entry->start)
-            {
-                /* starts earlier and overlaps */
-                entry->start = life->start;
-            }
-
-            if (life->end   >  entry->end &&
-                life->start <= entry->end+1)
-            {
-                /* ends later and overlaps */
-                entry->end = life->end;
-            }
-
-            /* see if our change combines it with the next ranges */
-            while (myi+1 < vec_size(self->life) &&
-                   entry->end+1 >= self->life[1+myi].start)
-            {
-                /* overlaps with (myi+1) */
-                if (entry->end < self->life[1+myi].end)
-                    entry->end = self->life[1+myi].end;
-                vec_remove(self->life, myi+1, 1);
-                entry = &self->life[myi];
-            }
-
-            /* see if we're after the entry */
-            if (life->start > entry->end)
-            {
-                ++myi;
-                /* append if we're at the end */
-                if (myi >= vec_size(self->life)) {
-                    vec_push(self->life, *life);
-                    break;
-                }
-                /* otherweise check the next range */
-                continue;
-            }
-            break;
-        }
+    bool was_set = ir_bitlist_getbit(self->life.alive, s);
+    ir_bitlist_setbit(self->life.alive, s);
+    if (wr) {
+        /* avoid pointlife issues by not marking a write-only assignment as
+         * dying here.
+         */
+        if (ir_bitlist_getbit(self->life.alive, s+1))
+            ir_bitlist_setbit  (self->life.dies, s);
+        else /* still need to perform the write to cause an allocation */
+            ir_bitlist_unsetbit(self->life.dies, s);
     }
-    return true;
+    else
+        ir_bitlist_unsetbit(self->life.dies,  s);
+    return !was_set;
 }
 
 static bool ir_values_overlap(const ir_value *a, const 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 (!vec_size(a->life) || !vec_size(b->life))
-        return false;
-
-    la = a->life;
-    lb = b->life;
-    enda = la + vec_size(a->life);
-    endb = lb + vec_size(b->life);
-    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->start < lb->start)
-        {
-            /* order: A B, move A forward
-             * check if we hit the end with A
-             */
-            if (++la == enda)
-                break;
-        }
-        else /* if (lb->start < la->start)  actually <= */
-        {
-            /* order: B A, move B forward
-             * check if we hit the end with B
-             */
-            if (++lb == endb)
-                break;
-        }
-    }
-    return false;
+    return ir_lifemask_overlaps(&a->life, &b->life);
 }
 
 /***********************************************************************
@@ -2143,18 +1956,13 @@ static bool function_allocator_alloc(function_allocator *alloc, ir_value *var)
     if (!slot)
         return false;
 
-    if (!ir_value_life_merge_into(slot, var))
-        goto localerror;
+    ir_lifemask_merge(&slot->life, &var->life);
 
     vec_push(alloc->locals, slot);
     vec_push(alloc->sizes, vsize);
     vec_push(alloc->unique, var->unique_life);
 
     return true;
-
-localerror:
-    ir_value_delete(slot);
-    return false;
 }
 
 static bool ir_function_allocator_assign(ir_function *self, function_allocator *alloc, ir_value *v)
@@ -2185,8 +1993,7 @@ static bool ir_function_allocator_assign(ir_function *self, function_allocator *
         if (ir_values_overlap(v, slot))
             continue;
 
-        if (!ir_value_life_merge_into(slot, v))
-            return false;
+        ir_lifemask_merge(&slot->life, &v->life);
 
         /* adjust size for this slot */
         if (alloc->sizes[a] < ir_value_sizeof(v))
@@ -2242,7 +2049,7 @@ bool ir_function_allocate_locals(ir_function *self)
     for (; i < vec_size(self->locals); ++i)
     {
         v = self->locals[i];
-        if (!vec_size(v->life))
+        if (!vec_size(v->life.alive->bits))
             continue;
         if (!ir_function_allocator_assign(self, (v->locked || !opt_gt ? &lockalloc : &globalloc), v))
             goto error;
@@ -2253,7 +2060,7 @@ bool ir_function_allocate_locals(ir_function *self)
     {
         v = self->values[i];
 
-        if (!vec_size(v->life))
+        if (!vec_size(v->life.alive->bits))
             continue;
 
         /* CALL optimization:
@@ -2418,7 +2225,7 @@ static bool ir_block_living_add_instr(ir_block *self, size_t eid)
     bool         changed = false;
     for (i = 0; i != vs; ++i)
     {
-        if (ir_value_life_merge(self->living[i], eid))
+        if (ir_value_life_merge(self->living[i], eid, false))
             changed = true;
     }
     return changed;
@@ -2507,14 +2314,14 @@ static bool ir_block_life_propagate(ir_block *self, bool *changed)
                      * since this function is run multiple times.
                      */
                     /* con_err( "Value only written %s\n", value->name); */
-                    if (ir_value_life_merge(value, instr->eid))
+                    if (ir_value_life_merge(value, instr->eid, true))
                         *changed = true;
                 } else {
                     /* since 'living' won't contain it
                      * anymore, merge the value, since
                      * (A) doesn't.
                      */
-                    if (ir_value_life_merge(value, instr->eid))
+                    if (ir_value_life_merge(value, instr->eid, true))
                         *changed = true;
                     /* Then remove */
                     vec_remove(self->living, idx, 1);
@@ -2522,7 +2329,7 @@ static bool ir_block_life_propagate(ir_block *self, bool *changed)
                 /* Removing a vector removes all members */
                 for (mem = 0; mem < 3; ++mem) {
                     if (value->members[mem] && vec_ir_value_find(self->living, value->members[mem], &idx)) {
-                        if (ir_value_life_merge(value->members[mem], instr->eid))
+                        if (ir_value_life_merge(value->members[mem], instr->eid, true))
                             *changed = true;
                         vec_remove(self->living, idx, 1);
                     }
@@ -2535,7 +2342,7 @@ static bool ir_block_life_propagate(ir_block *self, bool *changed)
                             break;
                     }
                     if (mem == 3 && vec_ir_value_find(self->living, value, &idx)) {
-                        if (ir_value_life_merge(value, instr->eid))
+                        if (ir_value_life_merge(value, instr->eid, true))
                             *changed = true;
                         vec_remove(self->living, idx, 1);
                     }
@@ -2556,9 +2363,9 @@ static bool ir_block_life_propagate(ir_block *self, bool *changed)
         {
             value = instr->_ops[2];
             /* the float source will get an additional lifetime */
-            if (ir_value_life_merge(value, instr->eid+1))
+            if (ir_value_life_merge(value, instr->eid+1, false))
                 *changed = true;
-            if (value->memberof && ir_value_life_merge(value->memberof, instr->eid+1))
+            if (value->memberof && ir_value_life_merge(value->memberof, instr->eid+1, false))
                 *changed = true;
         }
 
@@ -2571,9 +2378,9 @@ static bool ir_block_life_propagate(ir_block *self, bool *changed)
         {
             value = instr->_ops[1];
             /* the float source will get an additional lifetime */
-            if (ir_value_life_merge(value, instr->eid+1))
+            if (ir_value_life_merge(value, instr->eid+1, false))
                 *changed = true;
-            if (value->memberof && ir_value_life_merge(value->memberof, instr->eid+1))
+            if (value->memberof && ir_value_life_merge(value->memberof, instr->eid+1, false))
                 *changed = true;
         }
 
@@ -2652,14 +2459,28 @@ static bool ir_block_life_propagate(ir_block *self, bool *changed)
     return true;
 }
 
+static GMQCC_INLINE size_t ir_bitlist_find_first(const ir_bitlist_t *self)
+{
+    size_t i, size = vec_size(self->bits);
+    /* FIXME: optimize? only executed when a warning is issued though... */
+    for (i = 0; i != size; ++i) {
+        size_t bit;
+        for (bit = 0; bit != GMQCC_BL_BITS; ++bit) {
+            if (self->bits[i] & (1<<bit))
+                return bit + i*GMQCC_BL_BITS;
+        }
+    }
+    return 0;
+}
+
 bool ir_function_calculate_liferanges(ir_function *self)
 {
-    size_t i, s;
+    size_t i, s, firstinstr;
     bool changed;
 
     /* parameters live at 0 */
     for (i = 0; i < vec_size(self->params); ++i)
-        if (!ir_value_life_merge(self->locals[i], 0))
+        if (!ir_value_life_merge(self->locals[i], 0, true))
             compile_error(self->context, "internal error: failed value-life merging");
 
     do {
@@ -2681,8 +2502,9 @@ bool ir_function_calculate_liferanges(ir_function *self)
                 continue;
             self->flags |= IR_FLAG_HAS_UNINITIALIZED;
             /* find the instruction reading from it */
+            firstinstr = ir_bitlist_find_first(v->life.alive); /* used here and later at (FI) */
             for (s = 0; s < vec_size(v->reads); ++s) {
-                if (v->reads[s]->eid == v->life[0].end)
+                if (v->reads[s]->eid == firstinstr)
                     break;
             }
             if (s < vec_size(v->reads)) {
@@ -2700,7 +2522,7 @@ bool ir_function_calculate_liferanges(ir_function *self)
             if (v->memberof) {
                 ir_value *vec = v->memberof;
                 for (s = 0; s < vec_size(vec->reads); ++s) {
-                    if (vec->reads[s]->eid == v->life[0].end)
+                    if (vec->reads[s]->eid == firstinstr) /* (FI) */
                         break;
                 }
                 if (s < vec_size(vec->reads)) {
@@ -3171,7 +2993,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
 
             retvalue = instr->_ops[0];
             if (retvalue && retvalue->store != store_return &&
-                (retvalue->store == store_global || vec_size(retvalue->life)))
+                (retvalue->store == store_global || vec_size(retvalue->life.alive->bits)))
             {
                 /* not to be kept in OFS_RETURN */
                 if (retvalue->vtype == TYPE_FIELD && OPTS_FLAG(ADJUST_VECTOR_FIELDS))
@@ -4082,7 +3904,7 @@ void ir_function_dump(ir_function *f, char *ind,
     oprintf("%sliferanges:\n", ind);
     for (i = 0; i < vec_size(f->locals); ++i) {
         const char *attr = "";
-        size_t l, m;
+        size_t m;
         ir_value *v = f->locals[i];
         if (v->unique_life && v->locked)
             attr = "unique,locked ";
@@ -4094,26 +3916,20 @@ void ir_function_dump(ir_function *f, char *ind,
                 storenames[v->store],
                 attr, (v->callparam ? "callparam " : ""),
                 (int)v->code.local);
-        if (!v->life)
+        if (!v->life.alive->bits)
             oprintf("[null]");
-        for (l = 0; l < vec_size(v->life); ++l) {
-            oprintf("[%i,%i] ", v->life[l].start, v->life[l].end);
-        }
-        oprintf("\n");
+        ir_lifemask_dump(&v->life, ind, oprintf);
         for (m = 0; m < 3; ++m) {
             ir_value *vm = v->members[m];
             if (!vm)
                 continue;
             oprintf("%s\t%s: @%i ", ind, vm->name, (int)vm->code.local);
-            for (l = 0; l < vec_size(vm->life); ++l) {
-                oprintf("[%i,%i] ", vm->life[l].start, vm->life[l].end);
-            }
-            oprintf("\n");
+            ir_lifemask_dump(&vm->life, ind, oprintf);
         }
     }
     for (i = 0; i < vec_size(f->values); ++i) {
         const char *attr = "";
-        size_t l, m;
+        size_t m;
         ir_value *v = f->values[i];
         if (v->unique_life && v->locked)
             attr = "unique,locked ";
@@ -4125,11 +3941,9 @@ void ir_function_dump(ir_function *f, char *ind,
                 storenames[v->store],
                 attr, (v->callparam ? "callparam " : ""),
                 (int)v->code.local);
-        if (!v->life)
+        if (!v->life.alive->bits)
             oprintf("[null]");
-        for (l = 0; l < vec_size(v->life); ++l) {
-            oprintf("[%i,%i] ", v->life[l].start, v->life[l].end);
-        }
+        ir_lifemask_dump(&v->life, ind, oprintf);
         oprintf("\n");
         for (m = 0; m < 3; ++m) {
             ir_value *vm = v->members[m];
@@ -4142,10 +3956,7 @@ void ir_function_dump(ir_function *f, char *ind,
             else if (vm->locked)
                 attr = "locked ";
             oprintf("%s\t%s: %s@%i ", ind, vm->name, attr, (int)vm->code.local);
-            for (l = 0; l < vec_size(vm->life); ++l) {
-                oprintf("[%i,%i] ", vm->life[l].start, vm->life[l].end);
-            }
-            oprintf("\n");
+            ir_lifemask_dump(&vm->life, ind, oprintf);
         }
     }
     if (vec_size(f->blocks))
@@ -4307,10 +4118,6 @@ void ir_value_dump(ir_value* v, int (*oprintf)(const char*, ...))
 
 void ir_value_dump_life(const ir_value *self, int (*oprintf)(const char*,...))
 {
-    size_t i;
     oprintf("Life of %12s:", self->name);
-    for (i = 0; i < vec_size(self->life); ++i)
-    {
-        oprintf(" + [%i, %i]\n", self->life[i].start, self->life[i].end);
-    }
+    ir_lifemask_dump(&self->life, "  ", oprintf);
 }
diff --git a/ir.h b/ir.h
index d0fd7879c5c20509dd29f25eb11588ebb0ea015a..7ac02d1f5b71b0a575840983bd9d528a38d0efad 100644 (file)
--- a/ir.h
+++ b/ir.h
@@ -23,6 +23,7 @@
 #ifndef GMQCC_IR_HDR
 #define GMQCC_IR_HDR
 #include "gmqcc.h"
+#include "liveness.h"
 
 /*
  * Type large enough to hold all the possible IR flags. This should be
@@ -36,12 +37,6 @@ typedef struct ir_block_s    ir_block;
 typedef struct ir_function_s ir_function;
 typedef struct ir_builder_s  ir_builder;
 
-typedef struct {
-    /* both inclusive */
-    size_t start;
-    size_t end;
-} ir_life_entry_t;
-
 enum {
     IR_FLAG_HAS_ARRAYS           = 1 << 0,
     IR_FLAG_HAS_UNINITIALIZED    = 1 << 1,
@@ -99,7 +94,7 @@ struct ir_value_s {
     bool locked;           /* temps living during a CALL must be locked */
     bool callparam;
 
-    ir_life_entry_t *life; /* For the temp allocator */
+    ir_lifemask_t life; /* For the temp allocator */
 };
 
 /*
diff --git a/liveness.c b/liveness.c
new file mode 100644 (file)
index 0000000..1f3f7d6
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2012, 2013, 2014
+ *     Wolfgang Bumiller
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdlib.h>
+
+#include "gmqcc.h"
+#include "liveness.h"
+
+ir_bitlist_t *ir_bitlist_new() {
+    ir_bitlist_t *list = (ir_bitlist_t*)mem_a(sizeof(ir_bitlist_t));
+    list->bits = NULL;
+    return list;
+}
+
+void ir_bitlist_delete(ir_bitlist_t *self) {
+    if (self->bits)
+        vec_free(self->bits);
+    mem_d(self);
+}
+
+static GMQCC_INLINE void ir_bitlist_allocindex(ir_bitlist_t *self, size_t index) {
+    size_t size  = vec_size(self->bits);
+    while (size++ <= index)
+        vec_push(self->bits, 0);
+}
+
+void ir_bitlist_setbit(ir_bitlist_t *self, size_t bit) {
+    size_t index = bit / GMQCC_BL_BITS;
+    ir_bitlist_allocindex(self, index);
+    self->bits[index] |= (1 << (bit % GMQCC_BL_BITS));
+}
+
+void ir_bitlist_unsetbit(ir_bitlist_t *self, size_t bit) {
+    size_t index = bit / GMQCC_BL_BITS;
+    ir_bitlist_allocindex(self, index);
+    self->bits[index] &= ~(1 << (bit % GMQCC_BL_BITS));
+}
+
+bool ir_bitlist_getbit(const ir_bitlist_t *self, size_t bit) {
+    size_t        index = bit / GMQCC_BL_BITS;
+    GMQCC_BL_TYPE mask  = 1 << (bit % GMQCC_BL_BITS);
+    if (index >= vec_size(self->bits))
+        return false;
+    return !!(self->bits[index] & mask);
+}
+
+void ir_bitlist_setrange(ir_bitlist_t *self, size_t from, size_t to) {
+    size_t index_from, bit_from, index_to, bit_to;
+    GMQCC_BL_TYPE mask;
+
+    if (from > to) {
+        con_err("ir_bitlist_setrange: bad bits\n");
+        abort();
+    }
+
+    ++to;
+    index_from = from / GMQCC_BL_BITS;
+    bit_from   = from % GMQCC_BL_BITS;
+    index_to   = to   / GMQCC_BL_BITS;
+    bit_to     = to   % GMQCC_BL_BITS;
+
+    ir_bitlist_allocindex(self, index_to);
+
+    mask = GMQCC_BL_FULL;
+
+    if (index_from == index_to) {
+        mask <<= bit_from;
+        mask <<= GMQCC_BL_BITS - bit_to;
+        mask >>= GMQCC_BL_BITS - bit_to;
+        self->bits[index_from] |= mask;
+        return;
+    }
+
+    /* first chunk: */
+    self->bits[index_from] |= mask << bit_from;
+    /* filled with all ones */
+    for (++index_from; index_from != index_to; ++index_from)
+        self->bits[index_from] |= mask;
+    /* last chunk */
+    mask <<= GMQCC_BL_BITS - bit_to;
+    mask >>= GMQCC_BL_BITS - bit_to;
+    self->bits[index_to] |= mask;
+}
+
+static void ir_bitlist_dump(const ir_bitlist_t *self,
+                            int (*oprintf)(const char*, ...))
+{
+    size_t i;
+    size_t size = vec_size(self->bits);
+    if (!size)
+        oprintf("<empty>");
+    for (i = 0; i != size; ++i) {
+        size_t b;
+        for (b = 0; b != GMQCC_BL_BITS; ++b)
+            oprintf( (self->bits[i] & (1UL<<b)) ? "1" : "0" );
+    }
+    oprintf("\n");
+}
+
+void ir_lifemask_init(ir_lifemask_t *self) {
+    self->alive = ir_bitlist_new();
+    self->dies  = ir_bitlist_new();
+    /*self->mask  = NULL;*/
+}
+
+void ir_lifemask_clear(ir_lifemask_t *self) {
+    ir_bitlist_delete(self->alive);
+    ir_bitlist_delete(self->dies);
+    /*if (self->mask)
+        ir_bitlist_delete(self->mask);*/
+}
+
+void ir_lifemask_merge(ir_lifemask_t *self, const ir_lifemask_t *other) {
+    size_t i;
+    size_t other_alive_size = vec_size(other->alive->bits);
+    if (!other_alive_size)
+        return;
+
+    ir_bitlist_allocindex(self->alive, other_alive_size-1);
+    ir_bitlist_allocindex(self->dies,  other_alive_size-1);
+    /*if (self->mask)
+        ir_bitlist_allocindex(self->mask,  other_alive_size-1);*/
+
+    for (i = 0; i != other_alive_size; ++i) {
+        self->alive->bits[i] |= other->alive->bits[i];
+        self->dies->bits[i]  &= ~self->alive->bits[i];
+        self->dies->bits[i]  |= ~other->alive->bits[i] & other->dies->bits[i];
+
+        /*if (self->mask)
+            self->mask->bits[i] = self->alive->bits[i] & ~self->dies->bits[i];*/
+    }
+}
+
+/*
+void ir_lifemask_setmask(ir_lifemask_t *self) {
+    size_t i;
+    size_t size;
+
+    if (!self->mask)
+        self->mask = ir_bitlist_new();
+
+    size = vec_size(self->alive->bits);
+    if (!size)
+        return;
+
+    ir_bitlist_allocindex(self->mask, size-1);
+    for (i = 0; i != size; ++i)
+        self->mask->bits[i] = self->alive->bits[i] & ~self->dies->bits[i];
+}
+*/
+
+bool ir_lifemask_overlaps(const ir_lifemask_t *a, const ir_lifemask_t *b) {
+    size_t i;
+    size_t size   = vec_size(a->alive->bits),
+           size_b = vec_size(b->alive->bits);
+    if (size > size_b)
+        size = size_b;
+    for (i = 0; i != size; ++i) {
+        GMQCC_BL_TYPE mask_a = a->alive->bits[i] & ~a->dies->bits[i];
+        GMQCC_BL_TYPE mask_b = b->alive->bits[i] & ~b->dies->bits[i];
+        if (mask_a & mask_b)
+            return true;
+    }
+    return false;
+}
+
+void ir_lifemask_dump(const ir_lifemask_t *self, const char *ind,
+                      int (*oprintf)(const char*, ...))
+{
+    oprintf("{\n%s  ", ind);
+    ir_bitlist_dump(self->alive, oprintf);
+    oprintf("%s  ", ind);
+    ir_bitlist_dump(self->dies, oprintf);
+    /*oprintf("%s  ", ind);
+    ir_bitlist_dump(self->mask, oprintf);*/
+    oprintf("%s}\n", ind);
+}
+
+#ifdef LIVETEST
+#include <stdio.h>
+void test_liveness() {
+    con_init();
+    ir_bitlist_t *bl = ir_bitlist_new();
+    ir_bitlist_dump(bl);
+    ir_bitlist_setbit(bl, 1);
+    ir_bitlist_dump(bl);
+    ir_bitlist_setbit(bl, 2);
+    ir_bitlist_dump(bl);
+    ir_bitlist_setrange(bl, 4, 6);
+    ir_bitlist_dump(bl);
+    ir_bitlist_setrange(bl, 8, 9);
+    ir_bitlist_dump(bl);
+    ir_bitlist_setrange(bl, 15, 17);
+    ir_bitlist_dump(bl);
+    ir_bitlist_delete(bl);
+
+    ir_lifemask_t ma, mb;
+    ir_lifemask_init(&ma);
+    ir_lifemask_init(&mb);
+
+    ir_bitlist_setrange(ma.alive, 4, 6);
+    ir_bitlist_setbit(ma.dies,  4);
+    ir_lifemask_dump(&ma);
+
+    ir_bitlist_setrange(mb.alive, 6, 8);
+    ir_bitlist_setbit(mb.dies,  6);
+    ir_lifemask_dump(&mb);
+    con_out(ir_lifemask_overlaps(&ma, &mb) ? "WRONG OVERLAP\n" : "Ok\n");
+
+    ir_bitlist_setrange(mb.alive, 9, 12);
+    ir_bitlist_setbit(mb.dies,  9);
+    ir_lifemask_dump(&mb);
+    con_out(ir_lifemask_overlaps(&ma, &mb) ? "WRONG OVERLAP\n" : "Ok\n");
+
+    ir_bitlist_setrange(mb.alive, 5, 7);
+    ir_bitlist_unsetbit(mb.dies,  6);
+    ir_bitlist_setbit(mb.dies,  5);
+    ir_lifemask_dump(&mb);
+    con_out(ir_lifemask_overlaps(&ma, &mb) ? "overlap\n" : "WRONG ! OVERLAPPING\n");
+
+    ir_lifemask_clear(&ma);
+    ir_lifemask_clear(&mb);
+}
+
+int main() {
+    test_liveness();
+    return 0;
+}
+#endif
diff --git a/liveness.h b/liveness.h
new file mode 100644 (file)
index 0000000..0b51cf6
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012, 2013, 2014
+ *     Wolfgang Bumiller
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef GMQCC_LIVENESS_HDR
+#define GMQCC_LIVENESS_HDR
+
+#define GMQCC_BL_BITS 8
+#define GMQCC_BL_FULL 0xF
+#define GMQCC_BL_MAKETYPE1(TY) uint##TY##_t
+#define GMQCC_BL_MAKETYPE(TY) GMQCC_BL_MAKETYPE1(TY)
+#define GMQCC_BL_TYPE GMQCC_BL_MAKETYPE(GMQCC_BL_BITS)
+typedef struct {
+    GMQCC_BL_TYPE *bits; /* vector */
+} ir_bitlist_t;
+
+ir_bitlist_t *ir_bitlist_new     (void);
+void          ir_bitlist_delete  (ir_bitlist_t*);
+void          ir_bitlist_setbit  (ir_bitlist_t*,       size_t bit);
+void          ir_bitlist_unsetbit(ir_bitlist_t*,       size_t bit);
+bool          ir_bitlist_getbit  (const ir_bitlist_t*, size_t bit);
+/* precondition: from <= to */
+void          ir_bitlist_setrange(ir_bitlist_t*, size_t from, size_t to);
+
+typedef struct {
+    /* note that the following two lists always have the same size */
+
+    ir_bitlist_t *alive; /* read or generally alive */
+    ir_bitlist_t *dies;  /* instructions which only WRITE to this value */
+
+    /* When a value is written to without being read from it dies. This is
+     * used to mask out that specific instruction before checking whether
+     * a value's lifetime overlaps with another's.
+     */
+} ir_lifemask_t;
+
+void ir_lifemask_init    (ir_lifemask_t*);
+void ir_lifemask_clear   (ir_lifemask_t*);
+void ir_lifemask_merge   (ir_lifemask_t*, const ir_lifemask_t*);
+
+/*void ir_lifemask_setmask (ir_lifemask_t*);*/
+bool ir_lifemask_overlaps(const ir_lifemask_t*, const ir_lifemask_t*);
+
+/* debug */
+void ir_lifemask_dump(const ir_lifemask_t *self, const char *ind,
+                      int (*oprintf)(const char*, ...));
+
+#endif