+/*** vars *******************************************************************/
+
+#define VARS_CATEGORY_SHIFT 29
+#define VARS_INDEX_MASK 0x1FFFFFFF
+
+#define VARS_CATEGORY_LOCAL 0
+#define VARS_CATEGORY_STACK 1
+#define VARS_CATEGORY_OTHERS 2
+
+#define VAR_TYPE_SUBSTITUED 666
+
+typedef struct {
+ varinfo items[9000]; /* XXX hardcoded max */
+ unsigned max;
+ unsigned count;
+ unsigned category;
+} vars_t;
+
+static inline unsigned vars_add_item(vars_t *vs, const varinfo *item) {
+ unsigned i = vs->count;
+ assert(i < vs->max);
+ vs->count += 1;
+ vs->items[i] = *item;
+ return (vs->category << VARS_CATEGORY_SHIFT) | i;
+}
+
+static inline unsigned vars_add(vars_t *vs) {
+ unsigned i = vs->count;
+ assert(i < vs->max);
+ vs->count += 1;
+ return (vs->category << VARS_CATEGORY_SHIFT) | i;
+}
+
+static inline varinfo *vars_back(vars_t *vs) {
+ assert(vs->count > 0);
+ return vs->items + (vs->count - 1);
+}
+
+static inline void vars_init(vars_t *vs, unsigned category) {
+ vs->max = sizeof(vs->items) / sizeof(vs->items[0]);
+ vs->count = 0;
+ assert((category & 0x3) == category);
+ vs->category = category;
+}
+
+static inline unsigned vars_get_category(unsigned varindex) {
+ return (varindex >> VARS_CATEGORY_SHIFT);
+}
+
+static inline unsigned vars_get_index(unsigned varindex) {
+ return (varindex & VARS_INDEX_MASK);
+}
+
+static void vars_subst(vars_t *vs, unsigned varindex, unsigned replacementindex) {
+ varindex = vars_get_index(varindex);
+ replacementindex = vars_get_index(replacementindex);
+
+ vs->items[varindex].type = VAR_TYPE_SUBSTITUED;
+ vs->items[varindex].vv.regoff = replacementindex;
+}
+
+static unsigned vars_resolve_subst(const vars_t *vs, unsigned varindex) {
+#if !defined(NDEBUG)
+ unsigned loop_ctr = 0;
+#endif
+ varindex = vars_get_index(varindex);
+
+ if (vs->items[varindex].type == VAR_TYPE_SUBSTITUED) /*fprintf(stderr, "*")*/;
+
+ while (vs->items[varindex].type == VAR_TYPE_SUBSTITUED) {
+ assert(loop_ctr++ != vs->count);
+ varindex = vs->items[varindex].vv.regoff;
+ }
+
+ return (vs->category << VARS_CATEGORY_SHIFT) | varindex ;
+}
+
+static void vars_copy_to_final(vars_t *vs, varinfo *dst) {
+ const varinfo *it;
+ unsigned subst;
+
+ for (it = vs->items; it != vs->items + vs->count; ++it, ++dst) {
+
+ /* Copy variable. */
+
+ *dst = *it;
+
+ /* Eliminate VAR_TYPE_SUBSTITUED as it leads to problems. */
+
+ if (dst->type == VAR_TYPE_SUBSTITUED) {
+ subst = vars_get_index(vars_resolve_subst(vs, it - vs->items));
+ dst->type = vs->items[subst].type;
+ }
+ }
+}
+
+
+/*** phis *******************************************************************/
+
+typedef struct {
+ instruction *items;
+ unsigned max;
+ unsigned count;
+} phis_t;
+
+static inline void phis_init(phis_t *ps, unsigned max) {
+ ps->max = max;
+ ps->count = 0;
+ ps->items = DMNEW(instruction, max);
+}
+
+static inline instruction *phis_add(phis_t *ps) {
+ unsigned i = ps->count;
+ assert(i < ps->max);
+ ps->count += 1;
+ return ps->items + i;
+}
+
+static inline instruction *phis_get(const phis_t *ps, unsigned i) {
+ assert(i < ps->count);
+ return ps->items + i;
+}
+
+static inline bool phis_contains(const phis_t *ps, const instruction *phi) {
+ return (ps->items <= phi) && (phi < (ps->items + ps->max));
+}
+
+#define FOR_EACH_PHI_FUNCTION_(ps, it) \
+ for ((it) = (ps)->items; (it) != (ps)->items + (ps)->count; ++(it)) \
+
+#define FOR_EACH_PHI_FUNCTION(ps, it) \
+ FOR_EACH_PHI_FUNCTION_(ps, it) if (!phi_is_redundant((it)))
+
+#if !defined(NDEBUG)
+FIXME() inline void phis_print(const phis_t *ps) {
+ const instruction *iptr;
+ FOR_EACH_PHI_FUNCTION(ps, iptr) {
+ phi_print(iptr);
+ }
+}
+#endif
+
+/*** state_array ************************************************************/
+
+typedef struct {
+ instruction **items;
+ unsigned count;
+} state_array_t;
+
+static inline void state_array_init(state_array_t *sa, unsigned count) {
+ sa->items = NULL;
+ sa->count = count;
+}
+
+static inline bool state_array_has_items(const state_array_t *sa) {
+ return (sa->items != NULL);
+}
+
+static inline s4 state_array_get_var(const state_array_t *sa, unsigned index) {
+ assert(index < sa->count);
+ assert(sa->items[index]);
+ return sa->items[index]->dst.varindex;
+}
+
+static inline instruction *state_array_get(const state_array_t *sa, unsigned index) {
+ assert(index < sa->count);
+ return sa->items[index];
+}
+
+static inline void state_array_set(const state_array_t *sa, unsigned index, instruction *value) {
+ assert(index < sa->count);
+ sa->items[index] = value;
+}
+
+static inline void state_array_copy(state_array_t *sa, state_array_t *other) {
+ assert(sa->count == other->count);
+ MCOPY(sa->items, other->items, instruction *, sa->count);
+}
+
+#define state_array_assert_items(sa) assert(state_array_has_items(sa) || (sa->count == 0))
+#define state_array_assert_no_items(sa) assert(! state_array_has_items(sa))
+
+static inline void state_array_allocate_items(state_array_t *sa) {
+ sa->items = DMNEW(instruction *, sa->count);
+ MZERO(sa->items, instruction *, sa->count);
+}
+
+/*** basicblock_chain *******************************************************/
+
+typedef struct {
+ basicblock *first;
+ basicblock *last;
+} basicblock_chain_t;
+
+static void basicblock_chain_init(basicblock_chain_t *bbc) {
+ bbc->first = NULL;
+ bbc->last = NULL;
+}
+
+#define basicblock_chain_clear basicblock_chain_init
+
+static void basicblock_chain_add(basicblock_chain_t *bbc, basicblock *bb) {
+ if (bbc->first == NULL) {
+ assert(bbc->last == NULL);
+ assert(bb->next == NULL);
+ bbc->first = bb;
+ bbc->last = bb;
+ } else {
+ assert(bbc->last->next == NULL);
+ bbc->last->next = bb;
+ bbc->last = bb;
+ }
+}
+
+static inline basicblock *basicblock_chain_front(basicblock_chain_t *bbc) {
+ assert(bbc->first);
+ return bbc->first;
+}
+
+static inline basicblock *basicblock_chain_back(basicblock_chain_t *bbc) {
+ assert(bbc->last);
+ return bbc->last;
+}
+
+static inline bool basicblock_chain_empty(const basicblock_chain_t *bbc) {
+ return bbc->first == NULL;
+}
+
+/*** exception_entry_chain ***************************************************/
+
+typedef struct {
+ exception_entry *first;
+ exception_entry *last;
+} exception_entry_chain_t;
+
+static void exception_entry_chain_init(exception_entry_chain_t *eec) {
+ eec->first = NULL;
+ eec->last = NULL;
+}
+
+#define exception_entry_chain_clear exception_entry_chain_init
+
+static void exception_entry_chain_add(exception_entry_chain_t *eec, exception_entry *ee) {
+ if (eec->first == NULL) {
+ eec->first = ee;
+ eec->last = ee;
+ } else {
+ eec->last->next = ee;
+ eec->last->down = ee;
+ eec->last = ee;
+ }
+}
+
+static inline bool exception_entry_chain_empty(const exception_entry_chain_t *eec) {
+ return eec->first == NULL;
+}
+
+static inline exception_entry *exception_entry_chain_back(exception_entry_chain_t *eec) {
+ assert(eec->last);
+ return eec->last;
+}
+
+static inline exception_entry *exception_entry_chain_front(exception_entry_chain_t *eec) {
+ assert(eec->first);
+ return eec->first;
+}
+
+/*** traversal **************************************************************/
+
+typedef struct {
+ unsigned (*var_num_to_index)(void *vp, s4 var);
+ varinfo *(*index_to_initial_var)(void *vp, unsigned index);
+ varinfo *(*var_num_to_varinfo)(void *vp, s4 var);
+ unsigned (*variables_count)(void *vp);
+} traversal_ops_t;
+
+typedef struct {
+ phis_t *phis;
+ state_array_t *state_array;
+ void *ops_vp;
+ traversal_ops_t *ops;
+} traversal_t;
+
+/*** basicblock_info ********************************************************/
+
+typedef struct basicblock_info {
+ bool visited;
+ bool active;
+ bool traversed;
+ unsigned backward_branches;
+
+ traversal_t *locals;
+ traversal_t *stack;
+
+ basicblock_chain_t *subbasicblocks;
+
+ unsigned complete_predecessors;
+
+#if defined(SSA_VERIFY)
+ unsigned num_phi_elimination;
+#endif
+
+} basicblock_info_t;
+
+/*** traversal **************************************************************/
+
+void traversal_init(traversal_t *t, unsigned count, void *ops_vp, traversal_ops_t *ops) {
+ t->phis = DNEW(phis_t);
+ phis_init(t->phis, count);
+
+ t->state_array = DNEW(state_array_t);
+ state_array_init(t->state_array, count);
+
+ t->ops_vp = ops_vp;
+ t->ops = ops;
+}
+
+instruction *traversal_create_phi(traversal_t *t, vars_t *v, unsigned argcount, s4 index) {
+ instruction *phi = phis_add(t->phis);
+ s4 dst;
+
+ phi_init(phi, argcount, index);
+ dst = vars_add_item(v, t->ops->index_to_initial_var(t->ops_vp, index));
+ phi_set_dst(phi, dst);
+
+ state_array_set(t->state_array, index, phi);
+
+ return phi;
+}
+
+static void traversal_rename_def(traversal_t *t, vars_t *vars, instruction *iptr) {
+ const varinfo *v;
+ unsigned index;
+
+ state_array_assert_items(t->state_array);
+
+ v = t->ops->var_num_to_varinfo(t->ops_vp, iptr->dst.varindex);
+ index = t->ops->var_num_to_index(t->ops_vp, iptr->dst.varindex);