--- /dev/null
+/*
+ * jit-bitset.h - Bitset routines for the JIT.
+ *
+ * Copyright (C) 2006 Southern Storm Software, Pty Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "jit-internal.h"
+#include "jit-bitset.h"
+
+void
+_jit_bitset_init(_jit_bitset_t *bs)
+{
+ bs->size = 0;
+ bs->bits = 0;
+}
+
+int
+_jit_bitset_allocate(_jit_bitset_t *bs, int size)
+{
+ bs->size = size;
+ if(size > 0)
+ {
+ size = (size + _JIT_BITSET_WORD_BITS - 1) / _JIT_BITSET_WORD_BITS;
+ bs->bits = jit_calloc(size, sizeof(_jit_bitset_word_t));
+ if(!bs->bits)
+ {
+ jit_free(bs);
+ return 0;
+ }
+ }
+ else
+ {
+ bs->bits = 0;
+ }
+ return 1;
+}
+
+int
+ _jit_bitset_is_allocated(_jit_bitset_t *bs)
+{
+ return (bs->bits != 0);
+}
+
+void
+_jit_bitset_free(_jit_bitset_t *bs)
+{
+ if(bs->bits)
+ {
+ jit_free(bs->bits);
+ bs->size = 0;
+ bs->bits = 0;
+ }
+}
+
+void
+_jit_bitset_set_bit(_jit_bitset_t *bs, int bit)
+{
+ int word;
+ word = bit / _JIT_BITSET_WORD_BITS;
+ bit = bit % _JIT_BITSET_WORD_BITS;
+ bs->bits[word] |= bit;
+}
+
+void
+_jit_bitset_clear_bit(_jit_bitset_t *bs, int bit)
+{
+ int word;
+ word = bit / _JIT_BITSET_WORD_BITS;
+ bit = bit % _JIT_BITSET_WORD_BITS;
+ bs->bits[word] &= ~bit;
+}
+
+int
+_jit_bitset_test_bit(_jit_bitset_t *bs, int bit)
+{
+ int word;
+ word = bit / _JIT_BITSET_WORD_BITS;
+ bit = bit % _JIT_BITSET_WORD_BITS;
+ return (bs->bits[word] & bit) != 0;
+}
+
+void
+_jit_bitset_clear(_jit_bitset_t *bs)
+{
+ int i;
+ for(i = 0; i < bs->size; i++)
+ {
+ bs->bits[i] = 0;
+ }
+}
+
+int
+_jit_bitset_empty(_jit_bitset_t *bs)
+{
+ int i;
+ for(i = 0; i < bs->size; i++)
+ {
+ if(bs->bits[i])
+ {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void
+_jit_bitset_add(_jit_bitset_t *dest, _jit_bitset_t *src)
+{
+ int i;
+ for(i = 0; i < dest->size; i++)
+ {
+ dest->bits[i] |= src->bits[i];
+ }
+}
+
+void
+_jit_bitset_sub(_jit_bitset_t *dest, _jit_bitset_t *src)
+{
+ int i;
+ for(i = 0; i < dest->size; i++)
+ {
+ dest->bits[i] &= ~src->bits[i];
+ }
+}
+
+int
+_jit_bitset_copy(_jit_bitset_t *dest, _jit_bitset_t *src)
+{
+ int i;
+ int changed;
+
+ changed = 0;
+ for(i = 0; i < dest->size; i++)
+ {
+ if(dest->bits[i] != src->bits[i])
+ {
+ dest->bits[i] = src->bits[i];
+ changed = 1;
+ }
+ }
+ return changed;
+}
+
+int
+_jit_bitset_equal(_jit_bitset_t *bs1, _jit_bitset_t *bs2)
+{
+ int i;
+ for(i = 0; i < bs1->size; i++)
+ {
+ if(bs1->bits[i] != bs2->bits[i])
+ {
+ return 0;
+ }
+ }
+ return 1;
+}
--- /dev/null
+/*
+ * jit-cfg.c - Control Flow Graph routines for the JIT.
+ *
+ * Copyright (C) 2006 Southern Storm Software, Pty Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "jit-internal.h"
+#include "jit-cfg.h"
+
+static void
+init_node(_jit_node_t node, jit_block_t block)
+{
+ node->block = block;
+ if(block)
+ {
+ jit_block_set_meta(block, _JIT_BLOCK_CFG_NODE, node, 0);
+ }
+ node->flags = 0;
+ node->succs = 0;
+ node->num_succs = 0;
+ node->preds = 0;
+ node->num_preds = 0;
+
+ _jit_bitset_init(&node->live_in);
+ _jit_bitset_init(&node->live_out);
+ _jit_bitset_init(&node->live_use);
+ _jit_bitset_init(&node->live_def);
+
+ node->dfn = -1;
+}
+
+static void
+init_edge(_jit_edge_t edge)
+{
+ edge->src = 0;
+ edge->dst = 0;
+ edge->flags = 0;
+}
+
+static void
+init_value_entry(_jit_value_entry_t value)
+{
+ value->value = 0;
+}
+
+static _jit_node_t
+create_node()
+{
+ _jit_node_t node;
+ node = jit_new(struct _jit_node);
+ if(node)
+ {
+ init_node(node, 0);
+ }
+ return node;
+}
+
+_jit_cfg_t
+create_cfg(jit_function_t func)
+{
+ _jit_cfg_t cfg;
+
+ cfg = jit_new(struct _jit_cfg);
+ if(!cfg)
+ {
+ return 0;
+ }
+
+ cfg->entry = create_node();
+ if(!cfg->entry)
+ {
+ jit_free(cfg);
+ return 0;
+ }
+
+ cfg->exit = create_node();
+ if(!cfg->exit)
+ {
+ jit_free(cfg->entry);
+ jit_free(cfg);
+ return 0;
+ }
+
+ cfg->func = func;
+ cfg->nodes = 0;
+ cfg->num_nodes = 0;
+ cfg->edges = 0;
+ cfg->num_edges = 0;
+ cfg->post_order = 0;
+ cfg->values = 0;
+ cfg->num_values = 0;
+ cfg->max_values = 0;
+
+ return cfg;
+}
+
+static int
+build_nodes(_jit_cfg_t cfg, jit_function_t func)
+{
+ int count;
+ jit_block_t block;
+
+ count = 0;
+ block = 0;
+ while((block = jit_block_next(func, block)) != 0)
+ {
+ ++count;
+ }
+
+ cfg->num_nodes = count;
+ cfg->nodes = jit_malloc(count * sizeof(struct _jit_node));
+ if(!cfg->nodes)
+ {
+ return 0;
+ }
+
+ count = 0;
+ block = 0;
+ while((block = jit_block_next(func, block)) != 0)
+ {
+ init_node(&cfg->nodes[count++], block);
+ }
+
+ return 1;
+}
+
+static _jit_node_t
+get_next_node(_jit_cfg_t cfg, _jit_node_t node)
+{
+ int index = (node - cfg->nodes) + 1;
+ if(index < cfg->num_nodes)
+ {
+ return cfg->nodes + index;
+ }
+ else
+ {
+ return cfg->exit;
+ }
+}
+
+static _jit_node_t
+get_label_node(_jit_cfg_t cfg, jit_label_t label)
+{
+ jit_block_t block;
+ block = jit_block_from_label(cfg->func, label);
+ if(!block)
+ {
+ return 0;
+ }
+ return jit_block_get_meta(block, _JIT_BLOCK_CFG_NODE);
+}
+
+static _jit_node_t
+get_catcher_node(_jit_cfg_t cfg)
+{
+ jit_label_t label;
+ label = cfg->func->builder->catcher_label;
+ if(label == jit_label_undefined)
+ {
+ return cfg->exit;
+ }
+ return get_label_node(cfg, label);
+}
+
+static void
+enum_edge(_jit_cfg_t cfg, _jit_node_t src, _jit_node_t dst, int flags, int create)
+{
+ if(!cfg || !src || !dst)
+ {
+ return;
+ }
+
+ if(create)
+ {
+ cfg->edges[cfg->num_edges].src = src;
+ cfg->edges[cfg->num_edges].dst = dst;
+ cfg->edges[cfg->num_edges].flags = flags;
+ src->succs[src->num_succs] = &cfg->edges[cfg->num_edges];
+ dst->preds[dst->num_preds] = &cfg->edges[cfg->num_edges];
+ }
+
+ ++(cfg->num_edges);
+ ++(src->num_succs);
+ ++(dst->num_preds);
+}
+
+static void
+enum_node_edges(_jit_cfg_t cfg, _jit_node_t node, int create)
+{
+ jit_insn_t insn;
+ jit_label_t label;
+ jit_label_t *labels;
+ int index, num_labels;
+
+ /* TODO: Handle catch, finally, filter blocks and calls. */
+
+ insn = _jit_block_get_last(node->block);
+ if(!insn)
+ {
+ /* empty block: create a fall-through edge */
+ enum_edge(cfg, node, get_next_node(cfg, node), 0, create);
+ }
+ else if(insn->opcode == JIT_OP_BR)
+ {
+ label = (jit_label_t) insn->dest;
+ enum_edge(cfg, node, get_label_node(cfg, label), 0, create);
+ }
+ else if(insn->opcode >= JIT_OP_BR_IFALSE && insn->opcode <= JIT_OP_BR_NFGE_INV)
+ {
+ label = (jit_label_t) insn->dest;
+ enum_edge(cfg, node, get_label_node(cfg, label), 0, create);
+ enum_edge(cfg, node, get_next_node(cfg, node), 0, create);
+ }
+ else if(insn->opcode >= JIT_OP_RETURN && insn->opcode <= JIT_OP_RETURN_SMALL_STRUCT)
+ {
+ enum_edge(cfg, node, cfg->exit, 0, create);
+ }
+ else if(insn->opcode == JIT_OP_THROW)
+ {
+ enum_edge(cfg, node, get_catcher_node(cfg), 0, create);
+ }
+ else if(insn->opcode == JIT_OP_RETHROW)
+ {
+ enum_edge(cfg, node, cfg->exit, 0, create);
+ }
+ else if(insn->opcode == JIT_OP_JUMP_TABLE)
+ {
+ labels = (jit_label_t *) insn->value1->address;
+ num_labels = (int) insn->value2->address;
+ for(index = 0; index < num_labels; index++)
+ {
+ enum_edge(cfg, node, get_label_node(cfg, labels[index]), 0, create);
+ }
+ enum_edge(cfg, node, get_next_node(cfg, node), 0, create);
+ }
+ else
+ {
+ /* otherwise create a fall-through edge */
+ enum_edge(cfg, node, get_next_node(cfg, node), 0, create);
+ }
+}
+
+static void
+enum_all_edges(_jit_cfg_t cfg, jit_function_t func, int create)
+{
+ int index;
+
+ if(cfg->num_nodes == 0)
+ {
+ enum_edge(cfg, cfg->entry, cfg->exit, 0, create);
+ }
+ else
+ {
+ enum_edge(cfg, cfg->entry, &cfg->nodes[0], 0, create);
+ for(index = 0; index < cfg->num_nodes; index++)
+ {
+ enum_node_edges(cfg, &cfg->nodes[index], create);
+ }
+ }
+}
+
+static int
+create_edges(_jit_cfg_t cfg, jit_function_t func)
+{
+ int index;
+
+ if(cfg->num_edges == 0)
+ {
+ return 1;
+ }
+
+ cfg->edges = jit_malloc(cfg->num_edges * sizeof(struct _jit_edge));
+ if(!cfg->edges)
+ {
+ return 0;
+ }
+ for(index = 0; index < cfg->num_edges; index++)
+ {
+ init_edge(&cfg->edges[index]);
+ }
+ for(index = 0; index < cfg->num_nodes; index++)
+ {
+ if(cfg->nodes[index].num_succs > 0)
+ {
+ cfg->nodes[index].succs = jit_calloc(cfg->nodes[index].num_succs,
+ sizeof(_jit_edge_t));
+ if(!cfg->nodes[index].succs)
+ {
+ return 0;
+ }
+ cfg->nodes[index].num_succs = 0;
+ }
+ if(cfg->nodes[index].num_preds > 0)
+ {
+ cfg->nodes[index].preds = jit_calloc(cfg->nodes[index].num_preds,
+ sizeof(_jit_edge_t));
+ if(!cfg->nodes[index].preds)
+ {
+ return 0;
+ }
+ cfg->nodes[index].num_preds = 0;
+ }
+ }
+
+ cfg->num_edges = 0;
+ return 1;
+}
+
+static int
+build_edges(_jit_cfg_t cfg, jit_function_t func)
+{
+ enum_all_edges(cfg, func, 0);
+ if(!create_edges(cfg, func))
+ {
+ return 0;
+ }
+ enum_all_edges(cfg, func, 1);
+ return 1;
+}
+
+static int
+compute_depth_first_order(_jit_cfg_t cfg)
+{
+ struct stack_entry
+ {
+ _jit_node_t node;
+ int index;
+ } *stack;
+ _jit_node_t node;
+ _jit_node_t succ;
+ int post_order_num;
+ int sp;
+ int index;
+
+ if(cfg->post_order)
+ {
+ return 1;
+ }
+
+ stack = jit_malloc((cfg->num_nodes + 1) * sizeof(struct stack_entry));
+ if(!stack)
+ {
+ return 0;
+ }
+
+ cfg->post_order = jit_calloc(cfg->num_nodes, sizeof(_jit_node_t));
+ if(!cfg->post_order)
+ {
+ jit_free(stack);
+ return 0;
+ }
+
+ post_order_num = 0;
+
+ stack[0].node = cfg->entry;
+ stack[0].index = 0;
+ sp = 1;
+
+ while(sp)
+ {
+ node = stack[sp - 1].node;
+ index = stack[sp - 1].index;
+
+ succ = node->succs[index]->dst;
+ if(succ != cfg->exit && (succ->flags & _JIT_NODE_VISITED) == 0)
+ {
+ succ->flags |= _JIT_NODE_VISITED;
+ if(succ->num_succs > 0)
+ {
+ stack[sp].node = succ;
+ stack[sp].index = 0;
+ ++sp;
+ }
+ else
+ {
+ cfg->post_order[post_order_num++] = succ;
+ }
+ }
+ else
+ {
+ if(index < node->num_succs)
+ {
+ stack[sp - 1].index = index + 1;
+ }
+ else
+ {
+ if(node != cfg->entry)
+ {
+ cfg->post_order[post_order_num++] = node;
+ }
+ --sp;
+ }
+ }
+ }
+
+ jit_free(stack);
+ return 1;
+}
+
+static jit_value_t
+get_dest(jit_insn_t insn)
+{
+ if(insn->opcode == JIT_OP_NOP
+ || (insn->flags & JIT_INSN_DEST_OTHER_FLAGS) != 0
+ || (insn->dest && insn->dest->is_constant))
+ {
+ return 0;
+ }
+ return insn->dest;
+}
+
+static jit_value_t
+get_value1(jit_insn_t insn)
+{
+ if(insn->opcode == JIT_OP_NOP
+ || (insn->flags & JIT_INSN_VALUE1_OTHER_FLAGS) != 0
+ || (insn->value1 && insn->value1->is_constant))
+ {
+ return 0;
+ }
+ return insn->value1;
+}
+
+static jit_value_t
+get_value2(jit_insn_t insn)
+{
+ if(insn->opcode == JIT_OP_NOP
+ || (insn->flags & JIT_INSN_VALUE2_OTHER_FLAGS) != 0
+ || (insn->value2 && insn->value2->is_constant))
+ {
+ return 0;
+ }
+ return insn->value2;
+}
+
+static int
+create_value_entry(_jit_cfg_t cfg, jit_value_t value)
+{
+ _jit_value_entry_t values;
+ int max_values;
+
+ if(value->index >= 0)
+ {
+ return 1;
+ }
+
+ if(cfg->num_values == cfg->max_values)
+ {
+ if(cfg->max_values == 0)
+ {
+ max_values = 20;
+ values = jit_malloc(max_values * sizeof(struct _jit_value_entry));
+ }
+ else
+ {
+ max_values += max_values / 2;
+ values = jit_realloc(cfg->values, max_values * sizeof(struct _jit_value_entry));
+ }
+ if(!values)
+ {
+ return 0;
+ }
+ cfg->values = values;
+ cfg->max_values = max_values;
+ }
+
+ value->index = cfg->num_values++;
+ init_value_entry(&cfg->values[value->index]);
+
+ return 1;
+}
+
+static int
+use_value(_jit_cfg_t cfg, _jit_node_t node, jit_value_t value)
+{
+ if(value->index < 0)
+ {
+ return 1;
+ }
+ if(_jit_bitset_is_allocated(&node->live_def)
+ && _jit_bitset_test_bit(&node->live_def, value->index))
+ {
+ return 1;
+ }
+ if(!_jit_bitset_is_allocated(&node->live_use)
+ && !_jit_bitset_allocate(&node->live_use, cfg->num_values))
+ {
+ return 0;
+ }
+ _jit_bitset_set_bit(&node->live_use, value->index);
+ return 1;
+}
+
+static int
+def_value(_jit_cfg_t cfg, _jit_node_t node, jit_value_t value)
+{
+ if(!_jit_bitset_is_allocated(&node->live_def)
+ && !_jit_bitset_allocate(&node->live_def, cfg->num_values))
+ {
+ return 0;
+ }
+ _jit_bitset_set_bit(&node->live_def, value->index);
+ return 1;
+}
+
+static int
+create_value_entries(_jit_cfg_t cfg)
+{
+ int index;
+ _jit_node_t node;
+ jit_insn_iter_t iter;
+ jit_insn_t insn;
+ jit_value_t dest;
+ jit_value_t value1;
+ jit_value_t value2;
+
+ for(index = 0; index < cfg->num_nodes; index++)
+ {
+ node = &cfg->nodes[index];
+ jit_insn_iter_init(&iter, node->block);
+ while((insn = jit_insn_iter_next(&iter)) != 0)
+ {
+ dest = get_dest(insn);
+ value1 = get_value1(insn);
+ value2 = get_value1(insn);
+
+ if(dest && !create_value_entry(cfg, dest))
+ {
+ return 0;
+ }
+ if(value1 && !create_value_entry(cfg, value1))
+ {
+ return 0;
+ }
+ if(value2 && !create_value_entry(cfg, value2))
+ {
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+static int
+compute_local_live_sets(_jit_cfg_t cfg)
+{
+ int index;
+ _jit_node_t node;
+ jit_insn_iter_t iter;
+ jit_insn_t insn;
+ jit_value_t dest;
+ jit_value_t value1;
+ jit_value_t value2;
+
+ for(index = 0; index < cfg->num_nodes; index++)
+ {
+ node = &cfg->nodes[index];
+ jit_insn_iter_init(&iter, node->block);
+ while((insn = jit_insn_iter_next(&iter)) != 0)
+ {
+ dest = get_dest(insn);
+ value1 = get_value1(insn);
+ value2 = get_value2(insn);
+
+ if(value1 && !use_value(cfg, node, value1))
+ {
+ return 0;
+ }
+ if(value2 && !use_value(cfg, node, value2))
+ {
+ return 0;
+ }
+ if(dest)
+ {
+ if((insn->flags & JIT_INSN_DEST_IS_VALUE) != 0)
+ {
+ if(!use_value(cfg, node, dest))
+ {
+ return 0;
+ }
+ }
+ else
+ {
+ if(!def_value(cfg, node, dest))
+ {
+ return 0;
+ }
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
+static int
+compute_global_live_sets(_jit_cfg_t cfg)
+{
+ int change;
+ int index, succ_index;
+ _jit_node_t node;
+ _jit_node_t succ;
+ _jit_bitset_t bitset;
+
+ if(!_jit_bitset_allocate(&bitset, cfg->num_values))
+ {
+ return 0;
+ }
+
+ do
+ {
+ change = 0;
+ for(index = 0; index < cfg->num_nodes; index++)
+ {
+ node = cfg->post_order[index];
+ if(!node)
+ {
+ continue;
+ }
+
+ _jit_bitset_clear(&bitset);
+ for(succ_index = 0; succ_index < node->num_succs; succ_index++)
+ {
+ succ = node->succs[succ_index]->dst;
+ if(_jit_bitset_is_allocated(&succ->live_in))
+ {
+ _jit_bitset_add(&bitset, &succ->live_in);
+ }
+ }
+ if(!_jit_bitset_is_allocated(&node->live_out)
+ && !_jit_bitset_allocate(&node->live_out, cfg->num_values))
+ {
+ _jit_bitset_free(&bitset);
+ return 0;
+ }
+ if(_jit_bitset_copy(&node->live_out, &bitset))
+ {
+ change = 1;
+ }
+
+ _jit_bitset_sub(&bitset, &node->live_def);
+ _jit_bitset_add(&bitset, &node->live_use);
+ if(!_jit_bitset_is_allocated(&node->live_in)
+ && !_jit_bitset_allocate(&node->live_in, cfg->num_values))
+ {
+ _jit_bitset_free(&bitset);
+ return 0;
+ }
+ if(_jit_bitset_copy(&node->live_in, &bitset))
+ {
+ change = 1;
+ }
+ }
+ }
+ while(change);
+
+ _jit_bitset_free(&bitset);
+ return 1;
+}
+
+void
+_jit_cfg_free(_jit_cfg_t cfg)
+{
+ int index;
+
+ if(cfg->nodes)
+ {
+ for(index = 0; index < cfg->num_nodes; index++)
+ {
+ if(cfg->nodes[index].succs)
+ {
+ jit_free(cfg->nodes[index].succs);
+ }
+ if(cfg->nodes[index].preds)
+ {
+ jit_free(cfg->nodes[index].preds);
+ }
+ }
+ jit_free(cfg->nodes);
+ }
+ if(cfg->edges)
+ {
+ jit_free(cfg->edges);
+ }
+ if(cfg->post_order)
+ {
+ jit_free(cfg->post_order);
+ }
+ if(cfg->values)
+ {
+ jit_free(cfg->values);
+ }
+ jit_free(cfg->entry);
+ jit_free(cfg->exit);
+ jit_free(cfg);
+}
+
+_jit_cfg_t
+_jit_cfg_build(jit_function_t func)
+{
+ _jit_cfg_t cfg;
+
+ cfg = create_cfg(func);
+ if(!cfg)
+ {
+ return 0;
+ }
+ if(!build_nodes(cfg, func) || !build_edges(cfg, func))
+ {
+ _jit_cfg_free(cfg);
+ return 0;
+ }
+ if(!compute_depth_first_order(cfg))
+ {
+ _jit_cfg_free(cfg);
+ return 0;
+ }
+
+ return cfg;
+}
+
+int
+_jit_cfg_compute_liveness(_jit_cfg_t cfg)
+{
+ return (create_value_entries(cfg)
+ && compute_local_live_sets(cfg)
+ && compute_global_live_sets(cfg));
+}