#include "mini.h"
#include "inssel.h"
+#include "aliasing.h"
+
+#define SPILL_COST_INCREMENT (1 << (bb->nesting << 1))
//#define DEBUG_LIVENESS
-extern guint8 mono_burg_arity [];
+#if SIZEOF_VOID_P == 8
+#define BITS_PER_CHUNK 64
+#else
+#define BITS_PER_CHUNK 32
+#endif
+
+static void
+optimize_initlocals (MonoCompile *cfg);
/* mono_bitset_mp_new:
*
* allocates a MonoBitSet inside a memory pool
*/
-static MonoBitSet*
+static inline MonoBitSet*
mono_bitset_mp_new (MonoMemPool *mp, guint32 max_size)
{
int size = mono_bitset_alloc_size (max_size, 0);
return mono_bitset_mem_new (mem, max_size, MONO_BITSET_DONT_FREE);
}
+static inline MonoBitSet*
+mono_bitset_mp_new_noinit (MonoMemPool *mp, guint32 max_size)
+{
+ int size = mono_bitset_alloc_size (max_size, 0);
+ gpointer mem;
+
+ mem = mono_mempool_alloc (mp, size);
+ return mono_bitset_mem_new (mem, max_size, MONO_BITSET_DONT_FREE);
+}
+
G_GNUC_UNUSED static void
mono_bitset_print (MonoBitSet *set)
{
if (arity > 1)
update_gen_kill_set (cfg, bb, inst->inst_i1, inst_num);
- if (inst->ssa_op == MONO_SSA_LOAD) {
- int idx = inst->inst_i0->inst_c0;
- MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
- g_assert (idx < max_vars);
- if (bb->region != -1) {
- /*
- * Variables used in exception regions can't be allocated to
- * registers.
- */
- cfg->varinfo [vi->idx]->flags |= MONO_INST_VOLATILE;
+ if ((inst->ssa_op & MONO_SSA_LOAD_STORE) || (inst->opcode == OP_DUMMY_STORE)) {
+ MonoLocalVariableList* affected_variables;
+ MonoLocalVariableList local_affected_variable;
+
+ if (cfg->aliasing_info == NULL) {
+ if ((inst->ssa_op == MONO_SSA_LOAD) || (inst->ssa_op == MONO_SSA_STORE) || (inst->opcode == OP_DUMMY_STORE)) {
+ local_affected_variable.variable_index = inst->inst_i0->inst_c0;
+ local_affected_variable.next = NULL;
+ affected_variables = &local_affected_variable;
+ } else {
+ affected_variables = NULL;
+ }
+ } else {
+ affected_variables = mono_aliasing_get_affected_variables_for_inst_traversing_code (cfg->aliasing_info, inst);
}
- update_live_range (cfg, idx, bb->dfn, inst_num);
- if (!mono_bitset_test (bb->kill_set, idx))
- mono_bitset_set (bb->gen_set, idx);
- vi->spill_costs += 1 + (bb->nesting * 2);
- } else if (inst->ssa_op == MONO_SSA_STORE) {
- int idx = inst->inst_i0->inst_c0;
- MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
- g_assert (idx < max_vars);
- g_assert (inst->inst_i1->opcode != OP_PHI);
- if (bb->region != -1) {
- /*
- * Variables used in exception regions can't be allocated to
- * registers.
- */
- cfg->varinfo [vi->idx]->flags |= MONO_INST_VOLATILE;
+
+ if (inst->ssa_op & MONO_SSA_LOAD) {
+ MonoLocalVariableList* affected_variable = affected_variables;
+ while (affected_variable != NULL) {
+ int idx = affected_variable->variable_index;
+ MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
+ g_assert (idx < max_vars);
+ update_live_range (cfg, idx, bb->dfn, inst_num);
+ if (!mono_bitset_test_fast (bb->kill_set, idx))
+ mono_bitset_set_fast (bb->gen_set, idx);
+ if (inst->ssa_op == MONO_SSA_LOAD)
+ vi->spill_costs += SPILL_COST_INCREMENT;
+
+ affected_variable = affected_variable->next;
+ }
+ } else if ((inst->ssa_op == MONO_SSA_STORE) || (inst->opcode == OP_DUMMY_STORE)) {
+ MonoLocalVariableList* affected_variable = affected_variables;
+ while (affected_variable != NULL) {
+ int idx = affected_variable->variable_index;
+ MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
+ g_assert (idx < max_vars);
+ //if (arity > 0)
+ //g_assert (inst->inst_i1->opcode != OP_PHI);
+ update_live_range (cfg, idx, bb->dfn, inst_num);
+ mono_bitset_set_fast (bb->kill_set, idx);
+ if (inst->ssa_op == MONO_SSA_STORE)
+ vi->spill_costs += SPILL_COST_INCREMENT;
+
+ affected_variable = affected_variable->next;
+ }
+ }
+ } else if (inst->opcode == OP_JMP) {
+ /* Keep arguments live! */
+ int i;
+ for (i = 0; i < cfg->num_varinfo; i++) {
+ if (cfg->varinfo [i]->opcode == OP_ARG) {
+ if (!mono_bitset_test_fast (bb->kill_set, i))
+ mono_bitset_set_fast (bb->gen_set, i);
+ }
}
- update_live_range (cfg, idx, bb->dfn, inst_num);
- mono_bitset_set (bb->kill_set, idx);
- vi->spill_costs += 1 + (bb->nesting * 2);
}
}
if (arity > 1)
update_volatile (cfg, bb, inst->inst_i1, inst_num);
- if ((inst->ssa_op == MONO_SSA_LOAD) || (inst->ssa_op == MONO_SSA_STORE)) {
- int idx = inst->inst_i0->inst_c0;
- MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
- g_assert (idx < max_vars);
- cfg->varinfo [vi->idx]->flags |= MONO_INST_VOLATILE;
+ if (inst->ssa_op & MONO_SSA_LOAD_STORE) {
+ MonoLocalVariableList* affected_variables;
+ MonoLocalVariableList local_affected_variable;
+
+ if (cfg->aliasing_info == NULL) {
+ if ((inst->ssa_op == MONO_SSA_LOAD) || (inst->ssa_op == MONO_SSA_STORE)) {
+ local_affected_variable.variable_index = inst->inst_i0->inst_c0;
+ local_affected_variable.next = NULL;
+ affected_variables = &local_affected_variable;
+ } else {
+ affected_variables = NULL;
+ }
+ } else {
+ affected_variables = mono_aliasing_get_affected_variables_for_inst_traversing_code (cfg->aliasing_info, inst);
+ }
+
+ while (affected_variables != NULL) {
+ int idx = affected_variables->variable_index;
+ MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
+ g_assert (idx < max_vars);
+ cfg->varinfo [vi->idx]->flags |= MONO_INST_VOLATILE;
+
+ affected_variables = affected_variables->next;
+ }
}
}
if (g_slist_find (*visited, bb))
return;
- for (tree_num = 0, inst = bb->code; inst; inst = inst->next, tree_num++) {
+ if (cfg->aliasing_info != NULL)
+ mono_aliasing_initialize_code_traversal (cfg->aliasing_info, bb);
+
+ tree_num = 0;
+ MONO_BB_FOR_EACH_INS (bb, inst) {
update_volatile (cfg, bb, inst, tree_num);
+ tree_num++;
}
*visited = g_slist_append (*visited, bb);
}
}
-static void
-handle_exception_clauses (MonoCompile *cfg)
+void
+mono_liveness_handle_exception_clauses (MonoCompile *cfg)
{
MonoBasicBlock *bb;
GSList *visited = NULL;
* so make them volatile. See bug #42136. This will not be neccessary when
* the back ends could guarantee that the variables will be in the
* correct registers when a handler is called.
+ * This includes try blocks too, since a variable in a try block might be
+ * accessed after an exception handler has been run.
*/
for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
- if (bb->region == -1)
+
+ if (bb->region == -1 || MONO_BBLOCK_IS_IN_REGION (bb, MONO_REGION_TRY))
continue;
visit_bb (cfg, bb, &visited);
void
mono_analyze_liveness (MonoCompile *cfg)
{
- MonoBitSet *old_live_in_set, *old_live_out_set, *tmp_in_set;
- gboolean changes;
+ MonoBitSet *old_live_out_set;
int i, j, max_vars = cfg->num_varinfo;
- int iterations, out_iter, in_iter;
- gboolean *changed_in, *changed_out, *new_changed_in, *in_worklist;
+ int out_iter;
+ gboolean *in_worklist;
MonoBasicBlock **worklist;
- guint32 l_begin, l_end;
- static int count = 0;
+ guint32 l_end;
+ int bitsize;
+ guint8 *mem;
#ifdef DEBUG_LIVENESS
printf ("LIVENESS %s\n", mono_method_full_name (cfg->method, TRUE));
if (max_vars == 0)
return;
+ bitsize = mono_bitset_alloc_size (max_vars, 0);
+ mem = mono_mempool_alloc0 (cfg->mempool, cfg->num_bblocks * bitsize * 4);
+
for (i = 0; i < cfg->num_bblocks; ++i) {
MonoBasicBlock *bb = cfg->bblocks [i];
- bb->gen_set = mono_bitset_mp_new (cfg->mempool, max_vars);
- bb->kill_set = mono_bitset_mp_new (cfg->mempool, max_vars);
- bb->live_in_set = mono_bitset_mp_new (cfg->mempool, max_vars);
- bb->live_out_set = mono_bitset_mp_new (cfg->mempool, max_vars);
+ bb->gen_set = mono_bitset_mem_new (mem, max_vars, MONO_BITSET_DONT_FREE);
+ mem += bitsize;
+ bb->kill_set = mono_bitset_mem_new (mem, max_vars, MONO_BITSET_DONT_FREE);
+ mem += bitsize;
+ /* Initialized later */
+ bb->live_in_set = NULL;
+ bb->live_out_set = mono_bitset_mem_new (mem, max_vars, MONO_BITSET_DONT_FREE);
+ mem += bitsize;
}
for (i = 0; i < max_vars; i ++) {
MONO_VARINFO (cfg, i)->range.first_use.abs_pos = ~ 0;
MonoInst *inst;
int tree_num;
- for (tree_num = 0, inst = bb->code; inst; inst = inst->next, tree_num++) {
- //mono_print_tree (inst); printf ("\n");
+ if (cfg->aliasing_info != NULL)
+ mono_aliasing_initialize_code_traversal (cfg->aliasing_info, bb);
+
+ tree_num = 0;
+ MONO_BB_FOR_EACH_INS (bb, inst) {
+#ifdef DEBUG_LIVENESS
+ mono_print_tree (inst); printf ("\n");
+#endif
update_gen_kill_set (cfg, bb, inst, tree_num);
+ tree_num++;
}
#ifdef DEBUG_LIVENESS
#endif
}
- old_live_in_set = mono_bitset_new (max_vars, 0);
old_live_out_set = mono_bitset_new (max_vars, 0);
- tmp_in_set = mono_bitset_new (max_vars, 0);
- changed_in = g_new0 (gboolean, cfg->num_bblocks + 1);
- changed_out = g_new0 (gboolean, cfg->num_bblocks + 1);
in_worklist = g_new0 (gboolean, cfg->num_bblocks + 1);
- new_changed_in = g_new0 (gboolean, cfg->num_bblocks + 1);
-
- for (i = 0; i < cfg->num_bblocks + 1; ++i) {
- changed_in [i] = TRUE;
- changed_out [i] = TRUE;
- }
- count ++;
-
- worklist = g_new0 (MonoBasicBlock *, cfg->num_bblocks + 1);
- l_begin = 0;
+ worklist = g_new (MonoBasicBlock *, cfg->num_bblocks + 1);
l_end = 0;
- for (i = cfg->num_bblocks - 1; i >= 0; i--) {
+ /*
+ * This is a backward dataflow analysis problem, so we process blocks in
+ * decreasing dfn order, this speeds up the iteration.
+ */
+ for (i = 0; i < cfg->num_bblocks; i ++) {
MonoBasicBlock *bb = cfg->bblocks [i];
worklist [l_end ++] = bb;
in_worklist [bb->dfn] = TRUE;
}
- iterations = 0;
out_iter = 0;
- in_iter = 0;
- do {
- changes = FALSE;
- iterations ++;
- while (l_begin != l_end) {
- MonoBasicBlock *bb = worklist [l_begin ++];
+ while (l_end != 0) {
+ MonoBasicBlock *bb = worklist [--l_end];
+ MonoBasicBlock *out_bb;
+ gboolean changed;
- in_worklist [bb->dfn] = FALSE;
+ in_worklist [bb->dfn] = FALSE;
- if (l_begin == cfg->num_bblocks + 1)
- l_begin = 0;
+#ifdef DEBUG_LIVENESS
+ printf ("P: %d(%d): IN: ", bb->block_num, bb->dfn);
+ for (j = 0; j < bb->in_count; ++j)
+ printf ("BB%d ", bb->in_bb [j]->block_num);
+ printf ("OUT:");
+ for (j = 0; j < bb->out_count; ++j)
+ printf ("BB%d ", bb->out_bb [j]->block_num);
+ printf ("\n");
+#endif
+
+ if (bb->out_count == 0)
+ continue;
- if (bb->out_count > 0) {
- out_iter ++;
- mono_bitset_copyto (bb->live_out_set, old_live_out_set);
+ out_iter ++;
- for (j = 0; j < bb->out_count; j++) {
- MonoBasicBlock *out_bb = bb->out_bb [j];
+ if (!bb->live_in_set) {
+ /* First pass over this bblock */
+ changed = TRUE;
+ }
+ else {
+ changed = FALSE;
+ mono_bitset_copyto (bb->live_out_set, old_live_out_set);
+ }
+
+ for (j = 0; j < bb->out_count; j++) {
+ out_bb = bb->out_bb [j];
- mono_bitset_copyto (out_bb->live_out_set, tmp_in_set);
- mono_bitset_sub (tmp_in_set, out_bb->kill_set);
- mono_bitset_union (tmp_in_set, out_bb->gen_set);
+ if (!out_bb->live_in_set) {
+ out_bb->live_in_set = mono_bitset_mem_new (mem, max_vars, MONO_BITSET_DONT_FREE);
+ mem += bitsize;
- mono_bitset_union (bb->live_out_set, tmp_in_set);
+ mono_bitset_copyto (out_bb->live_out_set, out_bb->live_in_set);
+ mono_bitset_sub (out_bb->live_in_set, out_bb->kill_set);
+ mono_bitset_union (out_bb->live_in_set, out_bb->gen_set);
+ }
- }
+ mono_bitset_union (bb->live_out_set, out_bb->live_in_set);
+ }
- changed_out [bb->dfn] = !mono_bitset_equal (old_live_out_set, bb->live_out_set);
- if (changed_out [bb->dfn]) {
- for (j = 0; j < bb->in_count; j++) {
- MonoBasicBlock *in_bb = bb->in_bb [j];
- /*
- * Some basic blocks do not seem to be in the
- * cfg->bblocks array...
- */
- if (in_bb->live_in_set)
- if (!in_worklist [in_bb->dfn]) {
- worklist [l_end ++] = in_bb;
- if (l_end == cfg->num_bblocks + 1)
- l_end = 0;
- in_worklist [in_bb->dfn] = TRUE;
- }
- }
-
- changes = TRUE;
+ if (changed || !mono_bitset_equal (old_live_out_set, bb->live_out_set)) {
+ if (!bb->live_in_set) {
+ bb->live_in_set = mono_bitset_mem_new (mem, max_vars, MONO_BITSET_DONT_FREE);
+ mem += bitsize;
+ }
+ mono_bitset_copyto (bb->live_out_set, bb->live_in_set);
+ mono_bitset_sub (bb->live_in_set, bb->kill_set);
+ mono_bitset_union (bb->live_in_set, bb->gen_set);
+
+ for (j = 0; j < bb->in_count; j++) {
+ MonoBasicBlock *in_bb = bb->in_bb [j];
+ /*
+ * Some basic blocks do not seem to be in the
+ * cfg->bblocks array...
+ */
+ if (in_bb->gen_set && !in_worklist [in_bb->dfn]) {
+#ifdef DEBUG_LIVENESS
+ printf ("\tADD: %d\n", in_bb->block_num);
+#endif
+ /*
+ * Put the block at the top of the stack, so it
+ * will be processed right away.
+ */
+ worklist [l_end ++] = in_bb;
+ in_worklist [in_bb->dfn] = TRUE;
}
}
}
- } while (changes);
+ }
//printf ("IT: %d %d %d.\n", iterations, in_iter, out_iter);
- mono_bitset_free (old_live_in_set);
mono_bitset_free (old_live_out_set);
- mono_bitset_free (tmp_in_set);
- g_free (changed_in);
- g_free (changed_out);
- g_free (new_changed_in);
g_free (worklist);
g_free (in_worklist);
- for (i = cfg->num_bblocks - 1; i >= 0; i--) {
+ /* Compute live_in_set for bblocks skipped earlier */
+ for (i = 0; i < cfg->num_bblocks; ++i) {
MonoBasicBlock *bb = cfg->bblocks [i];
- mono_bitset_copyto (bb->live_out_set, bb->live_in_set);
- mono_bitset_sub (bb->live_in_set, bb->kill_set);
- mono_bitset_union (bb->live_in_set, bb->gen_set);
+ if (!bb->live_in_set) {
+ bb->live_in_set = mono_bitset_mem_new (mem, max_vars, MONO_BITSET_DONT_FREE);
+ mem += bitsize;
+
+ mono_bitset_copyto (bb->live_out_set, bb->live_in_set);
+ mono_bitset_sub (bb->live_in_set, bb->kill_set);
+ mono_bitset_union (bb->live_in_set, bb->gen_set);
+ }
}
/*
*/
for (i = 0; i < cfg->num_bblocks; ++i) {
MonoBasicBlock *bb = cfg->bblocks [i];
- guint32 rem;
+ guint32 rem, max;
+
+ if (!bb->live_out_set)
+ continue;
- rem = max_vars % 32;
- for (j = 0; j < (max_vars / 32) + 1; ++j) {
- guint32 bits_in;
- guint32 bits_out;
- int k, nbits;
+ rem = max_vars % BITS_PER_CHUNK;
+ max = ((max_vars + (BITS_PER_CHUNK -1)) / BITS_PER_CHUNK);
+ for (j = 0; j < max; ++j) {
+ gsize bits_in;
+ gsize bits_out;
+ int k, end;
- if (j > (max_vars / 32))
- break;
+ bits_in = mono_bitset_get_fast (bb->live_in_set, j);
+ bits_out = mono_bitset_get_fast (bb->live_out_set, j);
+
+ if (j == max)
+ end = (j * BITS_PER_CHUNK) + rem;
else
- if (j == (max_vars / 32))
- nbits = rem;
- else
- nbits = 32;
-
- bits_in = mono_bitset_test_bulk (bb->live_in_set, j * 32);
- bits_out = mono_bitset_test_bulk (bb->live_out_set, j * 32);
- for (k = 0; k < nbits; ++k) {
- if (bits_in & (1 << k))
- update_live_range (cfg, (j * 32) + k, bb->dfn, 0);
- if (bits_out & (1 << k))
- update_live_range (cfg, (j * 32) + k, bb->dfn, 0xffff);
+ end = (j * BITS_PER_CHUNK) + BITS_PER_CHUNK;
+
+ k = (j * BITS_PER_CHUNK);
+ while ((bits_in || bits_out)) {
+ if (bits_in & 1)
+ update_live_range (cfg, k, bb->dfn, 0);
+ if (bits_out & 1)
+ update_live_range (cfg, k, bb->dfn, 0xffff);
+ bits_in >>= 1;
+ bits_out >>= 1;
+ k ++;
}
}
}
- handle_exception_clauses (cfg);
+ /*
+ * Arguments need to have their live ranges extended to the beginning of
+ * the method to account for the arg reg/memory -> global register copies
+ * in the prolog (bug #74992).
+ */
+
+ for (i = 0; i < max_vars; i ++) {
+ MonoMethodVar *vi = MONO_VARINFO (cfg, i);
+ if (cfg->varinfo [vi->idx]->opcode == OP_ARG) {
+ if (vi->range.last_use.abs_pos == 0 && !(cfg->varinfo [vi->idx]->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)))
+ cfg->varinfo [vi->idx]->flags |= MONO_INST_IS_DEAD;
+ vi->range.first_use.abs_pos = 0;
+ }
+ }
#ifdef DEBUG_LIVENESS
for (i = cfg->num_bblocks - 1; i >= 0; i--) {
mono_bitset_print (bb->live_out_set);
}
#endif
+
+ if (!cfg->disable_initlocals_opt)
+ optimize_initlocals (cfg);
}
+static void
+update_used (MonoCompile *cfg, MonoInst *inst, MonoBitSet *used)
+{
+ int arity = mono_burg_arity [inst->opcode];
+
+ if (arity)
+ update_used (cfg, inst->inst_i0, used);
+
+ if (arity > 1)
+ update_used (cfg, inst->inst_i1, used);
+
+ if (inst->ssa_op & MONO_SSA_LOAD_STORE) {
+ if (inst->ssa_op == MONO_SSA_LOAD) {
+ int idx = inst->inst_i0->inst_c0;
+
+ mono_bitset_set_fast (used, idx);
+ }
+ }
+}
+
+/**
+ * optimize_initlocals:
+ *
+ * Try to optimize away some of the redundant initialization code inserted because of
+ * 'locals init' using the liveness information.
+ */
+static void
+optimize_initlocals (MonoCompile *cfg)
+{
+ MonoBitSet *used;
+ MonoInst *ins;
+ MonoBasicBlock *initlocals_bb;
+
+ used = mono_bitset_new (cfg->num_varinfo, 0);
+
+ mono_bitset_clear_all (used);
+ initlocals_bb = cfg->bb_entry->next_bb;
+ MONO_BB_FOR_EACH_INS (initlocals_bb, ins)
+ update_used (cfg, ins, used);
+
+ MONO_BB_FOR_EACH_INS (initlocals_bb, ins) {
+ if (ins->ssa_op == MONO_SSA_STORE) {
+ int idx = ins->inst_i0->inst_c0;
+ MonoInst *var = cfg->varinfo [idx];
+
+ if (var && !mono_bitset_test_fast (used, idx) && !mono_bitset_test_fast (initlocals_bb->live_out_set, var->inst_c0) && (var != cfg->ret) && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT))) {
+ if (ins->inst_i1 && ((ins->inst_i1->opcode == OP_ICONST) || (ins->inst_i1->opcode == OP_I8CONST))) {
+ NULLIFY_INS (ins);
+ ins->ssa_op = MONO_SSA_NOP;
+ MONO_VARINFO (cfg, var->inst_c0)->spill_costs -= 1;
+ }
+ }
+ }
+ }
+
+ g_free (used);
+}