self->locked = false;
self->callparam = false;
- self->life = NULL;
+ ir_lifemask_init(&self->life);
return self;
}
}
vec_free(self->reads);
vec_free(self->writes);
- vec_free(self->life);
+ ir_lifemask_clear(&self->life);
mem_d(self);
}
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);
}
/***********************************************************************
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)
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))
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;
{
v = self->values[i];
- if (!vec_size(v->life))
+ if (!vec_size(v->life.alive->bits))
continue;
/* CALL optimization:
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;
* 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);
/* 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);
}
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);
}
{
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;
}
{
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;
}
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 {
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)) {
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)) {
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))
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 ";
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 ";
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];
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))
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);
}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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