X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Fliveness.c;h=aabae662bf7db5e9e42f47eb71972cae26df8b8a;hb=8298b36af4f1ed3d37d350fc77bbf01c50082d0d;hp=d4ea60e39d6ae45c1a72844d14c56132715f4dd3;hpb=8efe5cab3eab18bef5ac682dc66904c4da1f7b9b;p=mono.git diff --git a/mono/mini/liveness.c b/mono/mini/liveness.c index d4ea60e39d6..aabae662bf7 100644 --- a/mono/mini/liveness.c +++ b/mono/mini/liveness.c @@ -5,30 +5,32 @@ * Dietmar Maurer (dietmar@ximian.com) * * (C) 2002 Ximian, Inc. + * Copyright 2011 Xamarin, Inc (http://www.xamarin.com) */ #include "mini.h" -#include "inssel.h" -#include "aliasing.h" #define SPILL_COST_INCREMENT (1 << (bb->nesting << 1)) -//#define DEBUG_LIVENESS +#define DEBUG_LIVENESS -#if SIZEOF_VOID_P == 8 -#define BITS_PER_CHUNK 64 -#else -#define BITS_PER_CHUNK 32 +#define BITS_PER_CHUNK MONO_BITSET_BITS_PER_CHUNK + +/* + * The liveness2 pass can't handle long vars on 32 bit platforms because the component + * vars have the same 'idx'. + */ +#if SIZEOF_REGISTER == 8 +#define ENABLE_LIVENESS2 #endif +#ifdef ENABLE_LIVENESS2 static void mono_analyze_liveness2 (MonoCompile *cfg); +#endif static void optimize_initlocals (MonoCompile *cfg); -static void -optimize_initlocals2 (MonoCompile *cfg); - /* mono_bitset_mp_new: * * allocates a MonoBitSet inside a memory pool @@ -51,140 +53,21 @@ G_GNUC_UNUSED static void mono_bitset_print (MonoBitSet *set) { int i; + gboolean first = TRUE; printf ("{"); for (i = 0; i < mono_bitset_size (set); i++) { - if (mono_bitset_test (set, i)) - printf ("%d, ", i); - + if (mono_bitset_test (set, i)) { + if (!first) + printf (", "); + printf ("%d", i); + first = FALSE; + } } printf ("}\n"); } -static inline void -update_live_range (MonoCompile *cfg, int idx, int block_dfn, int tree_pos) -{ - MonoLiveRange *range = &MONO_VARINFO (cfg, idx)->range; - guint32 abs_pos = (block_dfn << 16) | tree_pos; - - if (range->first_use.abs_pos > abs_pos) - range->first_use.abs_pos = abs_pos; - - if (range->last_use.abs_pos < abs_pos) - range->last_use.abs_pos = abs_pos; -} - -static void -update_gen_kill_set (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *inst, int inst_num) -{ - int arity; - int max_vars = cfg->num_varinfo; - - arity = mono_burg_arity [inst->opcode]; - if (arity) - update_gen_kill_set (cfg, bb, inst->inst_i0, inst_num); - - if (arity > 1) - update_gen_kill_set (cfg, bb, inst->inst_i1, inst_num); - - 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); - } - - 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); - } - } - } -} - -static void -update_volatile (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *inst) -{ - int arity = mono_burg_arity [inst->opcode]; - int max_vars = cfg->num_varinfo; - - if (arity) - update_volatile (cfg, bb, inst->inst_i0); - - if (arity > 1) - update_volatile (cfg, bb, inst->inst_i1); - - 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; - } - } -} - static void visit_bb (MonoCompile *cfg, MonoBasicBlock *bb, GSList **visited) { @@ -194,48 +77,50 @@ visit_bb (MonoCompile *cfg, MonoBasicBlock *bb, GSList **visited) if (g_slist_find (*visited, bb)) return; - if (cfg->new_ir) { - for (ins = bb->code; ins; ins = ins->next) { - const char *spec = INS_INFO (ins->opcode); - int regtype, srcindex, sreg; + for (ins = bb->code; ins; ins = ins->next) { + const char *spec = INS_INFO (ins->opcode); + int regtype, srcindex, sreg, num_sregs; + int sregs [MONO_MAX_SRC_REGS]; - if (ins->opcode == OP_NOP) - continue; + if (ins->opcode == OP_NOP) + continue; - /* DREG */ - regtype = spec [MONO_INST_DEST]; - g_assert (((ins->dreg == -1) && (regtype == ' ')) || ((ins->dreg != -1) && (regtype != ' '))); + /* DREG */ + regtype = spec [MONO_INST_DEST]; + g_assert (((ins->dreg == -1) && (regtype == ' ')) || ((ins->dreg != -1) && (regtype != ' '))); - if ((ins->dreg != -1) && get_vreg_to_inst (cfg, ins->dreg)) { - MonoInst *var = get_vreg_to_inst (cfg, ins->dreg); + if ((ins->dreg != -1) && get_vreg_to_inst (cfg, ins->dreg)) { + MonoInst *var = get_vreg_to_inst (cfg, ins->dreg); + int idx = var->inst_c0; + MonoMethodVar *vi = MONO_VARINFO (cfg, idx); + + cfg->varinfo [vi->idx]->flags |= MONO_INST_VOLATILE; + if (SIZEOF_REGISTER == 4 && (var->type == STACK_I8 || (var->type == STACK_R8 && COMPILE_SOFT_FLOAT (cfg)))) { + /* Make the component vregs volatile as well (#612206) */ + get_vreg_to_inst (cfg, var->dreg + 1)->flags |= MONO_INST_VOLATILE; + get_vreg_to_inst (cfg, var->dreg + 2)->flags |= MONO_INST_VOLATILE; + } + } + + /* SREGS */ + num_sregs = mono_inst_get_src_registers (ins, sregs); + for (srcindex = 0; srcindex < num_sregs; ++srcindex) { + sreg = sregs [srcindex]; + + g_assert (sreg != -1); + if (get_vreg_to_inst (cfg, sreg)) { + MonoInst *var = get_vreg_to_inst (cfg, sreg); int idx = var->inst_c0; MonoMethodVar *vi = MONO_VARINFO (cfg, idx); cfg->varinfo [vi->idx]->flags |= MONO_INST_VOLATILE; - } - - /* SREGS */ - for (srcindex = 0; srcindex < 2; ++srcindex) { - regtype = spec [(srcindex == 0) ? MONO_INST_SRC1 : MONO_INST_SRC2]; - sreg = srcindex == 0 ? ins->sreg1 : ins->sreg2; - - g_assert (((sreg == -1) && (regtype == ' ')) || ((sreg != -1) && (regtype != ' '))); - if ((sreg != -1) && get_vreg_to_inst (cfg, sreg)) { - MonoInst *var = get_vreg_to_inst (cfg, sreg); - int idx = var->inst_c0; - MonoMethodVar *vi = MONO_VARINFO (cfg, idx); - - cfg->varinfo [vi->idx]->flags |= MONO_INST_VOLATILE; + if (SIZEOF_REGISTER == 4 && (var->type == STACK_I8 || (var->type == STACK_R8 && COMPILE_SOFT_FLOAT (cfg)))) { + /* Make the component vregs volatile as well (#612206) */ + get_vreg_to_inst (cfg, var->dreg + 1)->flags |= MONO_INST_VOLATILE; + get_vreg_to_inst (cfg, var->dreg + 2)->flags |= MONO_INST_VOLATILE; } } } - } else { - if (cfg->aliasing_info != NULL) - mono_aliasing_initialize_code_traversal (cfg->aliasing_info, bb); - - for (ins = bb->code; ins; ins = ins->next) { - update_volatile (cfg, bb, ins); - } } *visited = g_slist_append (*visited, bb); @@ -254,6 +139,39 @@ mono_liveness_handle_exception_clauses (MonoCompile *cfg) { MonoBasicBlock *bb; GSList *visited = NULL; + MonoMethodHeader *header = cfg->header; + MonoExceptionClause *clause, *clause2; + int i, j; + gboolean *outer_try; + + /* + * Determine which clauses are outer try clauses, i.e. they are not contained in any + * other non-try clause. + */ + outer_try = mono_mempool_alloc0 (cfg->mempool, sizeof (gboolean) * header->num_clauses); + for (i = 0; i < header->num_clauses; ++i) + outer_try [i] = TRUE; + /* Iterate over the clauses backward, so outer clauses come first */ + /* This avoids doing an O(2) search, since we can determine when inner clauses end */ + for (i = header->num_clauses - 1; i >= 0; --i) { + clause = &header->clauses [i]; + + if (clause->flags != 0) { + outer_try [i] = TRUE; + /* Iterate over inner clauses */ + for (j = i - 1; j >= 0; --j) { + clause2 = &header->clauses [j]; + + if (clause2->flags == 0 && MONO_OFFSET_IN_HANDLER (clause, clause2->try_offset)) { + outer_try [j] = FALSE; + break; + } + if (clause2->try_offset < clause->try_offset) + /* End of inner clauses */ + break; + } + } + } /* * Variables in exception handler register cannot be allocated to registers @@ -265,16 +183,22 @@ mono_liveness_handle_exception_clauses (MonoCompile *cfg) */ for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { - if (bb->region == -1 || MONO_BBLOCK_IS_IN_REGION (bb, MONO_REGION_TRY)) + if (bb->region == -1) + continue; + + if (MONO_BBLOCK_IS_IN_REGION (bb, MONO_REGION_TRY) && outer_try [MONO_REGION_CLAUSE_INDEX (bb->region)]) continue; + if (cfg->verbose_level > 2) + printf ("pessimize variables in bb %d.\n", bb->block_num); + visit_bb (cfg, bb, &visited); } g_slist_free (visited); } static inline void -update_live_range2 (MonoMethodVar *var, int abs_pos) +update_live_range (MonoMethodVar *var, int abs_pos) { if (var->range.first_use.abs_pos > abs_pos) var->range.first_use.abs_pos = abs_pos; @@ -293,9 +217,14 @@ analyze_liveness_bb (MonoCompile *cfg, MonoBasicBlock *bb) for (inst_num = 0, ins = bb->code; ins; ins = ins->next, inst_num += 2) { const char *spec = INS_INFO (ins->opcode); + int num_sregs, i; + int sregs [MONO_MAX_SRC_REGS]; #ifdef DEBUG_LIVENESS - printf ("\t"); mono_print_ins (ins); + if (cfg->verbose_level > 1) { + printf ("\t"); + mono_print_ins (ins); + } #endif if (ins->opcode == OP_NOP) @@ -307,46 +236,33 @@ analyze_liveness_bb (MonoCompile *cfg, MonoBasicBlock *bb) MonoMethodVar *vi = MONO_VARINFO (cfg, idx); #ifdef DEBUG_LIVENESS - printf ("\tGEN: R%d(%d)\n", var->dreg, idx); + if (cfg->verbose_level > 1) + printf ("\tGEN: R%d(%d)\n", var->dreg, idx); #endif - update_live_range2 (&vars [idx], abs_pos + inst_num); + update_live_range (&vars [idx], abs_pos + inst_num); if (!mono_bitset_test_fast (bb->kill_set, idx)) mono_bitset_set_fast (bb->gen_set, idx); vi->spill_costs += SPILL_COST_INCREMENT; } /* SREGs must come first, so MOVE r <- r is handled correctly */ - - /* SREG1 */ - sreg = ins->sreg1; - if ((spec [MONO_INST_SRC1] != ' ') && get_vreg_to_inst (cfg, sreg)) { - MonoInst *var = get_vreg_to_inst (cfg, sreg); - int idx = var->inst_c0; - MonoMethodVar *vi = MONO_VARINFO (cfg, idx); - -#ifdef DEBUG_LIVENESS - printf ("\tGEN: R%d(%d)\n", sreg, idx); -#endif - update_live_range2 (&vars [idx], abs_pos + inst_num); - if (!mono_bitset_test_fast (bb->kill_set, idx)) - mono_bitset_set_fast (bb->gen_set, idx); - vi->spill_costs += SPILL_COST_INCREMENT; - } - - /* SREG2 */ - sreg = ins->sreg2; - if ((spec [MONO_INST_SRC2] != ' ') && get_vreg_to_inst (cfg, sreg)) { - MonoInst *var = get_vreg_to_inst (cfg, sreg); - int idx = var->inst_c0; - MonoMethodVar *vi = MONO_VARINFO (cfg, idx); + num_sregs = mono_inst_get_src_registers (ins, sregs); + for (i = 0; i < num_sregs; ++i) { + sreg = sregs [i]; + if ((spec [MONO_INST_SRC1 + i] != ' ') && get_vreg_to_inst (cfg, sreg)) { + MonoInst *var = get_vreg_to_inst (cfg, sreg); + int idx = var->inst_c0; + MonoMethodVar *vi = MONO_VARINFO (cfg, idx); #ifdef DEBUG_LIVENESS - printf ("\tGEN: R%d(%d)\n", sreg, idx); + if (cfg->verbose_level > 1) + printf ("\tGEN: R%d(%d)\n", sreg, idx); #endif - update_live_range2 (&vars [idx], abs_pos + inst_num); - if (!mono_bitset_test_fast (bb->kill_set, idx)) - mono_bitset_set_fast (bb->gen_set, idx); - vi->spill_costs += SPILL_COST_INCREMENT; + update_live_range (&vars [idx], abs_pos + inst_num); + if (!mono_bitset_test_fast (bb->kill_set, idx)) + mono_bitset_set_fast (bb->gen_set, idx); + vi->spill_costs += SPILL_COST_INCREMENT; + } } /* DREG */ @@ -356,15 +272,16 @@ analyze_liveness_bb (MonoCompile *cfg, MonoBasicBlock *bb) MonoMethodVar *vi = MONO_VARINFO (cfg, idx); if (MONO_IS_STORE_MEMBASE (ins)) { - update_live_range2 (&vars [idx], abs_pos + inst_num); + update_live_range (&vars [idx], abs_pos + inst_num); if (!mono_bitset_test_fast (bb->kill_set, idx)) mono_bitset_set_fast (bb->gen_set, idx); vi->spill_costs += SPILL_COST_INCREMENT; } else { #ifdef DEBUG_LIVENESS - printf ("\tKILL: R%d(%d)\n", ins->dreg, idx); + if (cfg->verbose_level > 1) + printf ("\tKILL: R%d(%d)\n", ins->dreg, idx); #endif - update_live_range2 (&vars [idx], abs_pos + inst_num + 1); + update_live_range (&vars [idx], abs_pos + inst_num + 1); mono_bitset_set_fast (bb->kill_set, idx); vi->spill_costs += SPILL_COST_INCREMENT; } @@ -387,7 +304,8 @@ mono_analyze_liveness (MonoCompile *cfg) int bitsize; #ifdef DEBUG_LIVENESS - printf ("LIVENESS %s\n", mono_method_full_name (cfg->method, TRUE)); + if (cfg->verbose_level > 1) + printf ("\nLIVENESS:\n"); #endif g_assert (!(cfg->comp_done & MONO_COMP_LIVENESS)); @@ -407,36 +325,27 @@ mono_analyze_liveness (MonoCompile *cfg) for (i = 0; i < cfg->num_bblocks; ++i) { MonoBasicBlock *bb = cfg->bblocks [i]; - MonoInst *inst; - int tree_num; bb->gen_set = mono_bitset_mp_new (cfg->mempool, bitsize, max_vars); bb->kill_set = mono_bitset_mp_new (cfg->mempool, bitsize, max_vars); - if (cfg->new_ir) { - analyze_liveness_bb (cfg, bb); - } else { - 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 ++; - } + if (cfg->verbose_level > 1) { + printf ("BLOCK BB%d (", bb->block_num); + for (j = 0; j < bb->out_count; j++) + printf ("BB%d, ", bb->out_bb [j]->block_num); + + printf ("):\n"); } +#endif + + analyze_liveness_bb (cfg, bb); #ifdef DEBUG_LIVENESS - printf ("BLOCK BB%d (", bb->block_num); - for (j = 0; j < bb->out_count; j++) - printf ("BB%d, ", bb->out_bb [j]->block_num); - - printf (")\n"); - printf ("GEN BB%d: ", bb->block_num); mono_bitset_print (bb->gen_set); - printf ("KILL BB%d: ", bb->block_num); mono_bitset_print (bb->kill_set); + if (cfg->verbose_level > 1) { + printf ("GEN BB%d: ", bb->block_num); mono_bitset_print (bb->gen_set); + printf ("KILL BB%d: ", bb->block_num); mono_bitset_print (bb->kill_set); + } #endif } @@ -463,6 +372,9 @@ mono_analyze_liveness (MonoCompile *cfg) out_iter = 0; + if (cfg->verbose_level > 1) + printf ("\nITERATION:\n"); + while (l_end != 0) { MonoBasicBlock *bb = worklist [--l_end]; MonoBasicBlock *out_bb; @@ -471,13 +383,15 @@ mono_analyze_liveness (MonoCompile *cfg) in_worklist [bb->dfn] = FALSE; #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"); + if (cfg->verbose_level > 1) { + printf ("P: BB%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 @@ -506,7 +420,11 @@ mono_analyze_liveness (MonoCompile *cfg) mono_bitset_union_fast (out_bb->live_in_set, out_bb->gen_set); } - mono_bitset_union_fast (bb->live_out_set, out_bb->live_in_set); + // FIXME: Do this somewhere else + if (bb->last_ins && bb->last_ins->opcode == OP_NOT_REACHED) { + } else { + mono_bitset_union_fast (bb->live_out_set, out_bb->live_in_set); + } } if (changed || !mono_bitset_equal (old_live_out_set, bb->live_out_set)) { @@ -524,7 +442,8 @@ mono_analyze_liveness (MonoCompile *cfg) */ if (in_bb->gen_set && !in_worklist [in_bb->dfn]) { #ifdef DEBUG_LIVENESS - printf ("\tADD: %d\n", in_bb->block_num); + if (cfg->verbose_level > 1) + printf ("\tADD: %d\n", in_bb->block_num); #endif /* * Put the block at the top of the stack, so it @@ -535,9 +454,15 @@ mono_analyze_liveness (MonoCompile *cfg) } } } + + if (G_UNLIKELY (cfg->verbose_level > 1)) { + printf ("\tLIVE IN BB%d: ", bb->block_num); + mono_bitset_print (bb->live_in_set); + } } #ifdef DEBUG_LIVENESS + if (cfg->verbose_level > 1) printf ("IT: %d %d.\n", cfg->num_bblocks, out_iter); #endif @@ -561,14 +486,13 @@ mono_analyze_liveness (MonoCompile *cfg) for (i = 0; i < cfg->num_bblocks; ++i) { MonoBasicBlock *bb = cfg->bblocks [i]; - guint32 rem, max; + guint32 max; guint32 abs_pos = (bb->dfn << 16); MonoMethodVar *vars = cfg->vars; if (!bb->live_out_set) continue; - 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; @@ -581,9 +505,9 @@ mono_analyze_liveness (MonoCompile *cfg) k = (j * BITS_PER_CHUNK); while ((bits_in || bits_out)) { if (bits_in & 1) - update_live_range2 (&vars [k], abs_pos + 0); + update_live_range (&vars [k], abs_pos + 0); if (bits_out & 1) - update_live_range2 (&vars [k], abs_pos + 0xffff); + update_live_range (&vars [k], abs_pos + 0xffff); bits_in >>= 1; bits_out >>= 1; k ++; @@ -600,35 +524,49 @@ mono_analyze_liveness (MonoCompile *cfg) 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; + if (vi->range.last_use.abs_pos == 0 && !(cfg->varinfo [vi->idx]->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT))) { + /* + * Can't eliminate the this variable in gshared code, since + * it is needed during exception handling to identify the + * method. + * It is better to check for this here instead of marking the variable + * VOLATILE, since that would prevent it from being allocated to + * registers. + */ + if (!cfg->disable_deadce_vars && !(cfg->generic_sharing_context && mono_method_signature (cfg->method)->hasthis && cfg->varinfo [vi->idx] == cfg->args [0])) + 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--) { - MonoBasicBlock *bb = cfg->bblocks [i]; + if (cfg->verbose_level > 1) { + for (i = cfg->num_bblocks - 1; i >= 0; i--) { + MonoBasicBlock *bb = cfg->bblocks [i]; - printf ("LIVE IN BB%d: ", bb->block_num); - mono_bitset_print (bb->live_in_set); - printf ("LIVE OUT BB%d: ", bb->block_num); - mono_bitset_print (bb->live_out_set); + printf ("LIVE IN BB%d: ", bb->block_num); + mono_bitset_print (bb->live_in_set); + printf ("LIVE OUT BB%d: ", bb->block_num); + mono_bitset_print (bb->live_out_set); + } + + for (i = 0; i < max_vars; i ++) { + MonoMethodVar *vi = MONO_VARINFO (cfg, i); + + printf ("V%d: [0x%x - 0x%x]\n", i, vi->range.first_use.abs_pos, vi->range.last_use.abs_pos); + } } #endif - if (cfg->new_ir) { - if (!cfg->disable_initlocals_opt) - optimize_initlocals2 (cfg); + if (!cfg->disable_initlocals_opt) + optimize_initlocals (cfg); - /* This improves code size by about 5% but slows down compilation too much */ - if (cfg->compile_aot) - mono_analyze_liveness2 (cfg); - } - else { - if (!cfg->disable_initlocals_opt) - optimize_initlocals (cfg); - } +#ifdef ENABLE_LIVENESS2 + /* This improves code size by about 5% but slows down compilation too much */ + if (cfg->compile_aot) + mono_analyze_liveness2 (cfg); +#endif } /** @@ -638,7 +576,7 @@ mono_analyze_liveness (MonoCompile *cfg) * 'locals init' using the liveness information. */ static void -optimize_initlocals2 (MonoCompile *cfg) +optimize_initlocals (MonoCompile *cfg) { MonoBitSet *used; MonoInst *ins; @@ -649,12 +587,13 @@ optimize_initlocals2 (MonoCompile *cfg) mono_bitset_clear_all (used); initlocals_bb = cfg->bb_entry->next_bb; for (ins = initlocals_bb->code; ins; ins = ins->next) { - const char *spec = INS_INFO (ins->opcode); + int num_sregs, i; + int sregs [MONO_MAX_SRC_REGS]; + + num_sregs = mono_inst_get_src_registers (ins, sregs); + for (i = 0; i < num_sregs; ++i) + mono_bitset_set_fast (used, sregs [i]); - if (spec [MONO_INST_SRC1] != ' ') - mono_bitset_set_fast (used, ins->sreg1); - if (spec [MONO_INST_SRC2] != ' ') - mono_bitset_set_fast (used, ins->sreg2); if (MONO_IS_STORE_MEMBASE (ins)) mono_bitset_set_fast (used, ins->dreg); } @@ -668,6 +607,8 @@ optimize_initlocals2 (MonoCompile *cfg) if (var && !mono_bitset_test_fast (used, ins->dreg) && !mono_bitset_test_fast (initlocals_bb->live_out_set, var->inst_c0) && (var != cfg->ret) && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT))) { //printf ("DEAD: "); mono_print_ins (ins); + if (cfg->disable_initlocals_opt_refs && var->type == STACK_OBJ) + continue; if ((ins->opcode == OP_ICONST) || (ins->opcode == OP_I8CONST) || (ins->opcode == OP_R8CONST)) { NULLIFY_INS (ins); MONO_VARINFO (cfg, var->inst_c0)->spill_costs -= 1; @@ -823,17 +764,22 @@ mono_linterval_split (MonoCompile *cfg, MonoLiveInterval *interval, MonoLiveInte } } -#if 0 -#define LIVENESS_DEBUG(a) do { a; } while (0) +#if 1 +#define LIVENESS_DEBUG(a) do { if (cfg->verbose_level > 1) do { a; } while (0); } while (0) +#define ENABLE_LIVENESS_DEBUG 1 #else #define LIVENESS_DEBUG(a) #endif +#ifdef ENABLE_LIVENESS2 + static inline void update_liveness2 (MonoCompile *cfg, MonoInst *ins, gboolean set_volatile, int inst_num, gint32 *last_use) { const char *spec = INS_INFO (ins->opcode); int sreg; + int num_sregs, i; + int sregs [MONO_MAX_SRC_REGS]; LIVENESS_DEBUG (printf ("\t%x: ", inst_num); mono_print_ins (ins)); @@ -859,10 +805,11 @@ update_liveness2 (MonoCompile *cfg, MonoInst *ins, gboolean set_volatile, int in } else { /* Try dead code elimination */ - if ((var != cfg->ret) && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)) && ((ins->opcode == OP_ICONST) || (ins->opcode == OP_I8CONST) || (ins->opcode == OP_R8CONST))) { + if ((var != cfg->ret) && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)) && ((ins->opcode == OP_ICONST) || (ins->opcode == OP_I8CONST) || (ins->opcode == OP_R8CONST)) && !(var->flags & MONO_INST_VOLATILE)) { LIVENESS_DEBUG (printf ("\tdead def of R%d, eliminated\n", ins->dreg)); ins->opcode = OP_NOP; - ins->dreg = ins->sreg1 = ins->sreg2 = -1; + ins->dreg = -1; + MONO_INST_NULLIFY_SREGS (ins); return; } @@ -872,27 +819,18 @@ update_liveness2 (MonoCompile *cfg, MonoInst *ins, gboolean set_volatile, int in } } - /* SREG1 */ - sreg = ins->sreg1; - if ((spec [MONO_INST_SRC1] != ' ') && get_vreg_to_inst (cfg, sreg)) { - MonoInst *var = get_vreg_to_inst (cfg, sreg); - int idx = var->inst_c0; - - if (last_use [idx] == 0) { - LIVENESS_DEBUG (printf ("\tlast use of R%d set to %x\n", sreg, inst_num)); - last_use [idx] = inst_num; - } - } - - /* SREG2 */ - sreg = ins->sreg2; - if ((spec [MONO_INST_SRC2] != ' ') && get_vreg_to_inst (cfg, sreg)) { - MonoInst *var = get_vreg_to_inst (cfg, sreg); - int idx = var->inst_c0; + /* SREGs */ + num_sregs = mono_inst_get_src_registers (ins, sregs); + for (i = 0; i < num_sregs; ++i) { + sreg = sregs [i]; + if ((spec [MONO_INST_SRC1 + i] != ' ') && get_vreg_to_inst (cfg, sreg)) { + MonoInst *var = get_vreg_to_inst (cfg, sreg); + int idx = var->inst_c0; - if (last_use [idx] == 0) { - LIVENESS_DEBUG (printf ("\tlast use of R%d set to %x\n", sreg, inst_num)); - last_use [idx] = inst_num; + if (last_use [idx] == 0) { + LIVENESS_DEBUG (printf ("\tlast use of R%d set to %x\n", sreg, inst_num)); + last_use [idx] = inst_num; + } } } } @@ -900,7 +838,7 @@ update_liveness2 (MonoCompile *cfg, MonoInst *ins, gboolean set_volatile, int in static void mono_analyze_liveness2 (MonoCompile *cfg) { - int bnum, idx, i, j, nins, rem, max, max_vars, block_from, block_to, pos, reverse_len; + int bnum, idx, i, j, nins, max, max_vars, block_from, block_to, pos, reverse_len; gint32 *last_use; static guint32 disabled = -1; MonoInst **reverse; @@ -951,7 +889,6 @@ mono_analyze_liveness2 (MonoCompile *cfg) /* For variables in bb->live_out, set last_use to block_to */ - rem = max_vars % BITS_PER_CHUNK; max = ((max_vars + (BITS_PER_CHUNK -1)) / BITS_PER_CHUNK); for (j = 0; j < max; ++j) { gsize bits_out; @@ -1028,61 +965,166 @@ mono_analyze_liveness2 (MonoCompile *cfg) g_free (last_use); } -static void -update_used (MonoCompile *cfg, MonoInst *inst, MonoBitSet *used) +#endif + +#define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1)) + +static inline void +update_liveness_gc (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *ins, gint32 *last_use, MonoMethodVar **vreg_to_varinfo, GSList **callsites) { - int arity = mono_burg_arity [inst->opcode]; + if (ins->opcode == OP_GC_LIVENESS_DEF || ins->opcode == OP_GC_LIVENESS_USE) { + int vreg = ins->inst_c1; + MonoMethodVar *vi = vreg_to_varinfo [vreg]; + int idx = vi->idx; + int pc_offset = ins->backend.pc_offset; - if (arity) - update_used (cfg, inst->inst_i0, used); + LIVENESS_DEBUG (printf ("\t%x: ", pc_offset); mono_print_ins (ins)); - if (arity > 1) - update_used (cfg, inst->inst_i1, used); + if (ins->opcode == OP_GC_LIVENESS_DEF) { + if (last_use [idx] > 0) { + LIVENESS_DEBUG (printf ("\tadd range to R%d: [%x, %x)\n", vreg, pc_offset, last_use [idx])); + last_use [idx] = 0; + } + } else { + if (last_use [idx] == 0) { + LIVENESS_DEBUG (printf ("\tlast use of R%d set to %x\n", vreg, pc_offset)); + last_use [idx] = pc_offset; + } + } + } else if (ins->opcode == OP_GC_PARAM_SLOT_LIVENESS_DEF) { + GCCallSite *last; + + /* Add it to the last callsite */ + g_assert (*callsites); + last = (*callsites)->data; + last->param_slots = g_slist_prepend_mempool (cfg->mempool, last->param_slots, ins); + } else if (ins->flags & MONO_INST_GC_CALLSITE) { + GCCallSite *callsite = mono_mempool_alloc0 (cfg->mempool, sizeof (GCCallSite)); + int i; - if (inst->ssa_op & MONO_SSA_LOAD_STORE) { - if (inst->ssa_op == MONO_SSA_LOAD) { - int idx = inst->inst_i0->inst_c0; + LIVENESS_DEBUG (printf ("\t%x: ", ins->backend.pc_offset); mono_print_ins (ins)); + LIVENESS_DEBUG (printf ("\t\tlive: ")); - mono_bitset_set_fast (used, idx); + callsite->bb = bb; + callsite->liveness = mono_mempool_alloc0 (cfg->mempool, ALIGN_TO (cfg->num_varinfo, 8) / 8); + callsite->pc_offset = ins->backend.pc_offset; + for (i = 0; i < cfg->num_varinfo; ++i) { + if (last_use [i] != 0) { + LIVENESS_DEBUG (printf ("R%d", MONO_VARINFO (cfg, i)->vreg)); + callsite->liveness [i / 8] |= (1 << (i % 8)); + } } + LIVENESS_DEBUG (printf ("\n")); + *callsites = g_slist_prepend_mempool (cfg->mempool, *callsites, callsite); } -} +} -/** - * optimize_initlocals: +static inline int +get_vreg_from_var (MonoCompile *cfg, MonoInst *var) +{ + if (var->opcode == OP_REGVAR) + /* dreg contains a hreg, but inst_c0 still contains the var index */ + return MONO_VARINFO (cfg, var->inst_c0)->vreg; + else + /* dreg still contains the vreg */ + return var->dreg; +} + +/* + * mono_analyze_liveness_gc: * - * Try to optimize away some of the redundant initialization code inserted because of - * 'locals init' using the liveness information. + * Compute liveness bitmaps for each call site. + * This function is a modified version of mono_analyze_liveness2 (). */ -static void -optimize_initlocals (MonoCompile *cfg) +void +mono_analyze_liveness_gc (MonoCompile *cfg) { - MonoBitSet *used; - MonoInst *ins; - MonoBasicBlock *initlocals_bb; + int idx, i, j, nins, max, max_vars, block_from, block_to, pos, reverse_len; + gint32 *last_use; + MonoInst **reverse; + MonoMethodVar **vreg_to_varinfo = NULL; + MonoBasicBlock *bb; + GSList *callsites; - used = mono_bitset_new (cfg->num_varinfo, 0); + LIVENESS_DEBUG (printf ("\n------------ GC LIVENESS: ----------\n")); - 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); + max_vars = cfg->num_varinfo; + last_use = g_new0 (gint32, max_vars); - 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]; + /* + * var->inst_c0 no longer contains the variable index, so compute a mapping now. + */ + vreg_to_varinfo = g_new0 (MonoMethodVar*, cfg->next_vreg); + for (idx = 0; idx < max_vars; ++idx) { + MonoMethodVar *vi = MONO_VARINFO (cfg, 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; + vreg_to_varinfo [vi->vreg] = vi; + } + + reverse_len = 1024; + reverse = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * reverse_len); + + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + MonoInst *ins; + + block_from = bb->real_native_offset; + block_to = bb->native_offset + bb->native_length; + + LIVENESS_DEBUG (printf ("GC LIVENESS BB%d:\n", bb->block_num)); + + if (!bb->code) + continue; + + memset (last_use, 0, max_vars * sizeof (gint32)); + + /* For variables in bb->live_out, set last_use to block_to */ + + max = ((max_vars + (BITS_PER_CHUNK -1)) / BITS_PER_CHUNK); + for (j = 0; j < max; ++j) { + gsize bits_out; + int k; + + if (!bb->live_out_set) + /* The variables used in this bblock are volatile anyway */ + continue; + + bits_out = mono_bitset_get_fast (bb->live_out_set, j); + k = (j * BITS_PER_CHUNK); + while (bits_out) { + if ((bits_out & 1) && cfg->varinfo [k]->flags & MONO_INST_GC_TRACK) { + int vreg = get_vreg_from_var (cfg, cfg->varinfo [k]); + LIVENESS_DEBUG (printf ("Var R%d live at exit, last_use set to %x.\n", vreg, block_to)); + last_use [k] = block_to; } + bits_out >>= 1; + k ++; + } + } + + for (nins = 0, pos = block_from, ins = bb->code; ins; ins = ins->next, ++nins, ++pos) { + if (nins >= reverse_len) { + int new_reverse_len = reverse_len * 2; + MonoInst **new_reverse = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * new_reverse_len); + memcpy (new_reverse, reverse, sizeof (MonoInst*) * reverse_len); + reverse = new_reverse; + reverse_len = new_reverse_len; } + + reverse [nins] = ins; } + + /* Process instructions backwards */ + callsites = NULL; + for (i = nins - 1; i >= 0; --i) { + MonoInst *ins = (MonoInst*)reverse [i]; + + update_liveness_gc (cfg, bb, ins, last_use, vreg_to_varinfo, &callsites); + } + /* The callsites should already be sorted by pc offset because we added them backwards */ + bb->gc_callsites = callsites; } - g_free (used); + g_free (last_use); + g_free (vreg_to_varinfo); }