static gboolean recompute_aliased_variables (MonoCompile *cfg, int *restored_vars) { int i; MonoBasicBlock *bb; MonoInst *ins; int kills = 0; int adds = 0; *restored_vars = 0; for (i = 0; i < cfg->num_varinfo; i++) { MonoInst *var = cfg->varinfo [i]; if (var->flags & MONO_INST_INDIRECT) { if (cfg->verbose_level > 2) { printf ("Killing :"); mono_print_ins (var); } ++kills; } var->flags &= ~MONO_INST_INDIRECT; } if (!kills) return FALSE; for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { for (ins = bb->code; ins; ins = ins->next) { if (ins->opcode == OP_LDADDR) { MonoInst *var; if (cfg->verbose_level > 2) { printf ("Found op :"); mono_print_ins (ins); } var = (MonoInst*)ins->inst_p0; if (!(var->flags & MONO_INST_INDIRECT)) { if (cfg->verbose_level > 1) { printf ("Restoring :"); mono_print_ins (var); } ++adds; } var->flags |= MONO_INST_INDIRECT; } } } *restored_vars = adds; mono_jit_stats.alias_found += kills; mono_jit_stats.alias_removed += kills - adds; if (kills > adds) { if (cfg->verbose_level > 2) { printf ("Method: %s\n", mono_method_full_name (cfg->method, 1)); printf ("Kills %d Adds %d\n", kills, adds); } return TRUE; } return FALSE; }
static gboolean lower_store (MonoCompile *cfg, MonoInst *store, MonoInst *ldaddr) { MonoInst *var = (MonoInst *)ldaddr->inst_p0; MonoType *type = &var->klass->byval_arg; int replaced_op = mono_type_to_store_membase (cfg, type); if (store->opcode == OP_STOREV_MEMBASE && store->klass != var->klass) { if (cfg->verbose_level > 2) printf ("Incompatible store_vtype classes %s x %s\n", store->klass->name, store->klass->name); return FALSE; } if (replaced_op != store->opcode) { if (cfg->verbose_level > 2) printf ("Incompatible store_reg type: expected %s but got %s\n", mono_inst_name (replaced_op), mono_inst_name (store->opcode)); return FALSE; } else { if (cfg->verbose_level > 2) { printf ("mem2reg replacing: "); mono_print_ins (store); } } store->opcode = mono_type_to_regmove (cfg, type); type_to_eval_stack_type (cfg, type, store); store->dreg = var->dreg; mono_jit_stats.stores_eliminated++; return TRUE; }
static gboolean lower_load (MonoCompile *cfg, MonoInst *load, MonoInst *ldaddr) { MonoInst *var = (MonoInst *)ldaddr->inst_p0; MonoType *type = &var->klass->byval_arg; int replaced_op = mono_type_to_load_membase (cfg, type); if (load->opcode == OP_LOADV_MEMBASE && load->klass != var->klass) { if (cfg->verbose_level > 2) printf ("Incompatible load_vtype classes %s x %s\n", load->klass->name, var->klass->name); return FALSE; } if (replaced_op != load->opcode) { if (cfg->verbose_level > 2) printf ("Incompatible load type: expected %s but got %s\n", mono_inst_name (replaced_op), mono_inst_name (load->opcode)); return FALSE; } else { if (cfg->verbose_level > 2) { printf ("mem2reg replacing: "); mono_print_ins (load); } } load->opcode = mono_type_to_regmove (cfg, type); type_to_eval_stack_type (cfg, type, load); load->sreg1 = var->dreg; mono_jit_stats.loads_eliminated++; return TRUE; }
static gboolean lower_store_imm (MonoCompile *cfg, MonoInst *store, MonoInst *ldaddr) { MonoInst *var = (MonoInst *)ldaddr->inst_p0; MonoType *type = &var->klass->byval_arg; int store_op = mono_type_to_store_membase (cfg, type); if (store_op == OP_STOREV_MEMBASE || store_op == OP_STOREX_MEMBASE) return FALSE; switch (store->opcode) { #if SIZEOF_VOID_P == 4 case OP_STORE_MEMBASE_IMM: #endif case OP_STOREI4_MEMBASE_IMM: if (!is_int_stack_size (var->type)) { if (cfg->verbose_level > 2) printf ("Incompatible variable of size != 4\n"); return FALSE; } if (cfg->verbose_level > 2) { printf ("mem2reg replacing: "); mono_print_ins (store); } store->opcode = OP_ICONST; store->type = STACK_I4; store->dreg = var->dreg; store->inst_c0 = store->inst_imm; break; #if SIZEOF_VOID_P == 8 case OP_STORE_MEMBASE_IMM: #endif case OP_STOREI8_MEMBASE_IMM: if (!is_long_stack_size (var->type)) { if (cfg->verbose_level > 2) printf ("Incompatible variable of size != 8\n"); return FALSE; } if (cfg->verbose_level > 2) { printf ("mem2reg replacing: "); mono_print_ins (store); } store->opcode = OP_I8CONST; store->type = STACK_I8; store->dreg = var->dreg; store->inst_l = store->inst_imm; break; default: return FALSE; } mono_jit_stats.stores_eliminated++; return TRUE; }
static gboolean lower_memory_access (MonoCompile *cfg) { MonoBasicBlock *bb; MonoInst *ins, *tmp; gboolean needs_dce = FALSE; GHashTable *addr_loads = g_hash_table_new (NULL, NULL); //FIXME optimize for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { g_hash_table_remove_all (addr_loads); for (ins = bb->code; ins; ins = ins->next) { handle_instruction: switch (ins->opcode) { case OP_LDADDR: g_hash_table_insert (addr_loads, GINT_TO_POINTER (ins->dreg), ins); if (cfg->verbose_level > 2) { printf ("New address: "); mono_print_ins (ins); } break; case OP_MOVE: tmp = (MonoInst*)g_hash_table_lookup (addr_loads, GINT_TO_POINTER (ins->sreg1)); /* Forward propagate known aliases ldaddr R10 <- R8 mov R11 <- R10 */ if (tmp) { g_hash_table_insert (addr_loads, GINT_TO_POINTER (ins->dreg), tmp); if (cfg->verbose_level > 2) { printf ("New alias: "); mono_print_ins (ins); } } else { /* Source value is not a know address, kill the variable. */ if (g_hash_table_remove (addr_loads, GINT_TO_POINTER (ins->dreg))) { if (cfg->verbose_level > 2) { printf ("Killed alias: "); mono_print_ins (ins); } } } break; case OP_LOADV_MEMBASE: case OP_LOAD_MEMBASE: case OP_LOADU1_MEMBASE: case OP_LOADI2_MEMBASE: case OP_LOADU2_MEMBASE: case OP_LOADI4_MEMBASE: case OP_LOADU4_MEMBASE: case OP_LOADI1_MEMBASE: case OP_LOADI8_MEMBASE: #ifndef MONO_ARCH_SOFT_FLOAT_FALLBACK case OP_LOADR4_MEMBASE: #endif case OP_LOADR8_MEMBASE: if (ins->inst_offset != 0) continue; tmp = (MonoInst *)g_hash_table_lookup (addr_loads, GINT_TO_POINTER (ins->sreg1)); if (tmp) { if (cfg->verbose_level > 2) { printf ("Found candidate load:"); mono_print_ins (ins); } if (lower_load (cfg, ins, tmp)) { needs_dce = TRUE; /* Try to propagate known aliases if an OP_MOVE was inserted */ goto handle_instruction; } } break; case OP_STORE_MEMBASE_REG: case OP_STOREI1_MEMBASE_REG: case OP_STOREI2_MEMBASE_REG: case OP_STOREI4_MEMBASE_REG: case OP_STOREI8_MEMBASE_REG: #ifndef MONO_ARCH_SOFT_FLOAT_FALLBACK case OP_STORER4_MEMBASE_REG: #endif case OP_STORER8_MEMBASE_REG: case OP_STOREV_MEMBASE: if (ins->inst_offset != 0) continue; tmp = (MonoInst *)g_hash_table_lookup (addr_loads, GINT_TO_POINTER (ins->dreg)); if (tmp) { if (cfg->verbose_level > 2) { printf ("Found candidate store:"); mono_print_ins (ins); } if (lower_store (cfg, ins, tmp)) { needs_dce = TRUE; /* Try to propagate known aliases if an OP_MOVE was inserted */ goto handle_instruction; } } break; //FIXME missing storei1_membase_imm and storei2_membase_imm case OP_STORE_MEMBASE_IMM: case OP_STOREI4_MEMBASE_IMM: case OP_STOREI8_MEMBASE_IMM: if (ins->inst_offset != 0) continue; tmp = (MonoInst *)g_hash_table_lookup (addr_loads, GINT_TO_POINTER (ins->dreg)); if (tmp) { if (cfg->verbose_level > 2) { printf ("Found candidate store-imm:"); mono_print_ins (ins); } needs_dce |= lower_store_imm (cfg, ins, tmp); } break; case OP_CHECK_THIS: case OP_NOT_NULL: tmp = (MonoInst *)g_hash_table_lookup (addr_loads, GINT_TO_POINTER (ins->sreg1)); if (tmp) { if (cfg->verbose_level > 2) { printf ("Found null check over local: "); mono_print_ins (ins); } NULLIFY_INS (ins); needs_dce = TRUE; } break; } } } g_hash_table_destroy (addr_loads); return needs_dce; }
/** * mono_perform_abc_removal: * @cfg: Control Flow Graph * * Performs the ABC removal from a cfg in SSA form. * It does the following: * - Prepare the evaluation area * - Allocate memory for the relation graph in the evaluation area * (of course, only for variable definitions) and summarize there all * variable definitions * - Allocate memory for the evaluation contexts in the evaluation area * - Recursively process all the BBs in the dominator tree (it is enough * to invoke the processing on the entry BB) * * cfg: the method code */ void mono_perform_abc_removal (MonoCompile *cfg) { MonoVariableRelationsEvaluationArea area; MonoBasicBlock *bb; int i; verbose_level = cfg->verbose_level; if (TRACE_ABC_REMOVAL) { printf ("\nRemoving array bound checks in %s\n", mono_method_full_name (cfg->method, TRUE)); } area.cfg = cfg; area.relations = (MonoSummarizedValueRelation *) mono_mempool_alloc (cfg->mempool, sizeof (MonoSummarizedValueRelation) * (cfg->next_vreg) * 2); area.contexts = (MonoRelationsEvaluationContext *) mono_mempool_alloc (cfg->mempool, sizeof (MonoRelationsEvaluationContext) * (cfg->next_vreg)); area.variable_value_kind = (MonoIntegerValueKind *) mono_mempool_alloc (cfg->mempool, sizeof (MonoIntegerValueKind) * (cfg->next_vreg)); for (i = 0; i < cfg->next_vreg; i++) { area.variable_value_kind [i] = MONO_UNKNOWN_INTEGER_VALUE; area.relations [i].relation = MONO_EQ_RELATION; area.relations [i].relation_is_static_definition = TRUE; MAKE_VALUE_ANY (area.relations [i].related_value); area.relations [i].next = NULL; } for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { MonoInst *ins; if (TRACE_ABC_REMOVAL) printf ("\nABCREM BLOCK %d:\n", bb->block_num); for (ins = bb->code; ins; ins = ins->next) { const char *spec = INS_INFO (ins->opcode); if (spec [MONO_INST_DEST] == ' ' || MONO_IS_STORE_MEMBASE (ins)) continue; if (spec [MONO_INST_DEST] == 'i') { MonoIntegerValueKind effective_value_kind; MonoRelationsEvaluationRange range; MonoSummarizedValueRelation *type_relation; MonoInst *var; if (TRACE_ABC_REMOVAL) mono_print_ins (ins); var = get_vreg_to_inst (cfg, ins->dreg); if (var) area.variable_value_kind [ins->dreg] = type_to_value_kind (var->inst_vtype); effective_value_kind = get_relation_from_ins (&area, ins, &area.relations [ins->dreg], area.variable_value_kind [ins->dreg]); MONO_MAKE_RELATIONS_EVALUATION_RANGE_WEAK (range); apply_value_kind_to_range (&range, area.variable_value_kind [ins->dreg]); apply_value_kind_to_range (&range, effective_value_kind); if (range.upper < INT_MAX) { type_relation = (MonoSummarizedValueRelation *) mono_mempool_alloc (cfg->mempool, sizeof (MonoSummarizedValueRelation)); type_relation->relation = MONO_LE_RELATION; type_relation->related_value.type = MONO_CONSTANT_SUMMARIZED_VALUE; type_relation->related_value.value.constant.value = range.upper; type_relation->relation_is_static_definition = TRUE; type_relation->next = area.relations [ins->dreg].next; area.relations [ins->dreg].next = type_relation; if (TRACE_ABC_REMOVAL) { printf ("[var%d <= %d]", ins->dreg, range.upper); } } if (range.lower > INT_MIN) { type_relation = (MonoSummarizedValueRelation *) mono_mempool_alloc (cfg->mempool, sizeof (MonoSummarizedValueRelation)); type_relation->relation = MONO_GE_RELATION; type_relation->related_value.type = MONO_CONSTANT_SUMMARIZED_VALUE; type_relation->related_value.value.constant.value = range.lower; type_relation->relation_is_static_definition = TRUE; type_relation->next = area.relations [ins->dreg].next; area.relations [ins->dreg].next = type_relation; if (TRACE_ABC_REMOVAL) { printf ("[var%d >= %d]", ins->dreg, range.lower); } } if (TRACE_ABC_REMOVAL) { printf ("Summarized variable %d: ", ins->dreg); print_summarized_value (&(area.relations [ins->dreg].related_value)); printf ("\n"); } } } } /* Add symmetric relations */ for (i = 0; i < cfg->next_vreg; i++) { if (area.relations [i].related_value.type == MONO_VARIABLE_SUMMARIZED_VALUE) { int related_index = cfg->next_vreg + i; int related_variable = area.relations [i].related_value.value.variable.variable; area.relations [related_index].relation = MONO_EQ_RELATION; area.relations [related_index].relation_is_static_definition = TRUE; area.relations [related_index].related_value.type = MONO_VARIABLE_SUMMARIZED_VALUE; area.relations [related_index].related_value.value.variable.variable = i; area.relations [related_index].related_value.value.variable.delta = - area.relations [i].related_value.value.variable.delta; area.relations [related_index].next = area.relations [related_variable].next; area.relations [related_variable].next = &(area.relations [related_index]); if (TRACE_ABC_REMOVAL) { printf ("Added symmetric summarized value for variable variable %d (to %d): ", i, related_variable); print_summarized_value (&(area.relations [related_index].related_value)); printf ("\n"); } } } process_block (cfg, cfg->bblocks [0], &area); }
void mono_if_conversion (MonoCompile *cfg) { #ifdef MONO_ARCH_HAVE_CMOV_OPS MonoBasicBlock *bb; gboolean changed = FALSE; if (!(cfg->opt & MONO_OPT_CMOV)) return; // FIXME: Make this work with extended bblocks /* * This pass requires somewhat optimized IR code so it should be run after * local cprop/deadce. Also, it should be run before dominator computation, since * it changes control flow. */ for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { MonoBasicBlock *bb1, *bb2; restart: /* Look for the IR code generated from cond ? a : b * which is: * BB: * b<cond> [BB1BB2] * BB1: * <var> <- <a> * br BB3 * BB2: * <var> <- <b> * br BB3 */ if (!(bb->out_count == 2 && !bb->extended)) continue; bb1 = bb->out_bb [0]; bb2 = bb->out_bb [1]; if (bb1->in_count == 1 && bb2->in_count == 1 && bb1->out_count == 1 && bb2->out_count == 1 && bb1->out_bb [0] == bb2->out_bb [0]) { MonoInst *compare, *branch, *ins1, *ins2, *cmov, *move, *tmp; MonoBasicBlock *true_bb, *false_bb; gboolean simple, ret; int dreg, tmp_reg; CompType comp_type; if (bb->last_ins && (bb->last_ins->opcode == OP_BR_REG || bb->last_ins->opcode == OP_BR)) continue; /* Find the compare instruction */ if (!bb->last_ins || !bb->last_ins->prev) continue; branch = bb->last_ins; compare = branch->prev; if (!MONO_IS_COND_BRANCH_OP (branch)) /* This can happen if a cond branch is optimized away */ continue; true_bb = branch->inst_true_bb; false_bb = branch->inst_false_bb; /* * Check that bb1 and bb2 are 'simple' and both assign to the same * variable. */ /* FIXME: Get rid of the nops earlier */ ins1 = true_bb->code; while (ins1 && ins1->opcode == OP_NOP) ins1 = ins1->next; ins2 = false_bb->code; while (ins2 && ins2->opcode == OP_NOP) ins2 = ins2->next; if (!(ins1 && ins2 && ins1->dreg == ins2->dreg && ins1->dreg != -1)) continue; simple = TRUE; for (tmp = ins1->next; tmp; tmp = tmp->next) if (!((tmp->opcode == OP_NOP) || (tmp->opcode == OP_BR))) simple = FALSE; for (tmp = ins2->next; tmp; tmp = tmp->next) if (!((tmp->opcode == OP_NOP) || (tmp->opcode == OP_BR))) simple = FALSE; if (!simple) continue; /* We move ins1/ins2 before the compare so they should have no side effect */ if (!(MONO_INS_HAS_NO_SIDE_EFFECT (ins1) && MONO_INS_HAS_NO_SIDE_EFFECT (ins2))) continue; /* Moving ins1/ins2 could change the comparison */ /* FIXME: */ if (!((compare->sreg1 != ins1->dreg) && (compare->sreg2 != ins1->dreg))) continue; /* FIXME: */ comp_type = mono_opcode_to_type (branch->opcode, compare->opcode); if (!((comp_type == CMP_TYPE_I) || (comp_type == CMP_TYPE_L))) continue; /* FIXME: */ /* ins->type might not be set */ if (INS_INFO (ins1->opcode) [MONO_INST_DEST] != 'i') continue; if (cfg->verbose_level > 2) { printf ("\tBranch -> CMove optimization in BB%d on\n", bb->block_num); printf ("\t\t"); mono_print_ins (compare); printf ("\t\t"); mono_print_ins (compare->next); printf ("\t\t"); mono_print_ins (ins1); printf ("\t\t"); mono_print_ins (ins2); } changed = TRUE; //printf ("HIT!\n"); /* Assignments to the return register must remain at the end of bbs */ if (cfg->ret) ret = ins1->dreg == cfg->ret->dreg; else ret = FALSE; tmp_reg = mono_alloc_dreg (cfg, STACK_I4); dreg = ins1->dreg; /* Rewrite ins1 to emit to tmp_reg */ ins1->dreg = tmp_reg; if (ret) { dreg = mono_alloc_dreg (cfg, STACK_I4); ins2->dreg = dreg; } /* Remove ins1/ins2 from bb1/bb2 */ MONO_REMOVE_INS (true_bb, ins1); MONO_REMOVE_INS (false_bb, ins2); /* Move ins1 and ins2 before the comparison */ /* ins1 comes first to avoid ins1 overwriting an argument of ins2 */ mono_bblock_insert_before_ins (bb, compare, ins2); mono_bblock_insert_before_ins (bb, ins2, ins1); /* Add cmov instruction */ MONO_INST_NEW (cfg, cmov, OP_NOP); cmov->dreg = dreg; cmov->sreg1 = dreg; cmov->sreg2 = tmp_reg; switch (mono_opcode_to_type (branch->opcode, compare->opcode)) { case CMP_TYPE_I: cmov->opcode = int_cmov_opcodes [mono_opcode_to_cond (branch->opcode)]; break; case CMP_TYPE_L: cmov->opcode = long_cmov_opcodes [mono_opcode_to_cond (branch->opcode)]; break; default: g_assert_not_reached (); } mono_bblock_insert_after_ins (bb, compare, cmov); if (ret) { /* Add an extra move */ MONO_INST_NEW (cfg, move, OP_MOVE); move->dreg = cfg->ret->dreg; move->sreg1 = dreg; mono_bblock_insert_after_ins (bb, cmov, move); } /* Rewrite the branch */ branch->opcode = OP_BR; branch->inst_target_bb = true_bb->out_bb [0]; mono_link_bblock (cfg, bb, branch->inst_target_bb); /* Reorder bblocks */ mono_unlink_bblock (cfg, bb, true_bb); mono_unlink_bblock (cfg, bb, false_bb); mono_unlink_bblock (cfg, true_bb, true_bb->out_bb [0]); mono_unlink_bblock (cfg, false_bb, false_bb->out_bb [0]); mono_remove_bblock (cfg, true_bb); mono_remove_bblock (cfg, false_bb); /* Merge bb and its successor if possible */ if ((bb->out_bb [0]->in_count == 1) && (bb->out_bb [0] != cfg->bb_exit) && (bb->region == bb->out_bb [0]->region)) { mono_merge_basic_blocks (cfg, bb, bb->out_bb [0]); goto restart; } } /* Look for the IR code generated from if (cond) <var> <- <a> * which is: * BB: * b<cond> [BB1BB2] * BB1: * <var> <- <a> * br BB2 */ if ((bb2->in_count == 1 && bb2->out_count == 1 && bb2->out_bb [0] == bb1) || (bb1->in_count == 1 && bb1->out_count == 1 && bb1->out_bb [0] == bb2)) { MonoInst *compare, *branch, *ins1, *cmov, *tmp; gboolean simple; int dreg, tmp_reg; CompType comp_type; CompRelation cond; MonoBasicBlock *next_bb, *code_bb; /* code_bb is the bblock containing code, next_bb is the successor bblock */ if (bb2->in_count == 1 && bb2->out_count == 1 && bb2->out_bb [0] == bb1) { code_bb = bb2; next_bb = bb1; } else { code_bb = bb1; next_bb = bb2; } ins1 = code_bb->code; if (!ins1) continue; /* Check that code_bb is simple */ simple = TRUE; for (tmp = ins1->next; tmp; tmp = tmp->next) if (!((tmp->opcode == OP_NOP) || (tmp->opcode == OP_BR))) simple = FALSE; if (!simple) continue; /* We move ins1 before the compare so it should have no side effect */ if (!MONO_INS_HAS_NO_SIDE_EFFECT (ins1)) continue; if (bb->last_ins && bb->last_ins->opcode == OP_BR_REG) continue; /* Find the compare instruction */ if (!bb->last_ins || !bb->last_ins->prev) continue; branch = bb->last_ins; compare = branch->prev; if (!MONO_IS_COND_BRANCH_OP (branch)) /* This can happen if a cond branch is optimized away */ continue; /* FIXME: */ comp_type = mono_opcode_to_type (branch->opcode, compare->opcode); if (!((comp_type == CMP_TYPE_I) || (comp_type == CMP_TYPE_L))) continue; /* FIXME: */ /* ins->type might not be set */ if (INS_INFO (ins1->opcode) [MONO_INST_DEST] != 'i') continue; /* FIXME: */ if (cfg->ret && ins1->dreg == cfg->ret->dreg) continue; if (!(cfg->opt & MONO_OPT_DEADCE)) /* * It is possible that dreg is never set before, so we can't use * it as an sreg of the cmov instruction (#582322). */ continue; if (cfg->verbose_level > 2) { printf ("\tBranch -> CMove optimization (2) in BB%d on\n", bb->block_num); printf ("\t\t"); mono_print_ins (compare); printf ("\t\t"); mono_print_ins (compare->next); printf ("\t\t"); mono_print_ins (ins1); } changed = TRUE; //printf ("HIT!\n"); tmp_reg = mono_alloc_dreg (cfg, STACK_I4); dreg = ins1->dreg; /* Rewrite ins1 to emit to tmp_reg */ ins1->dreg = tmp_reg; /* Remove ins1 from code_bb */ MONO_REMOVE_INS (code_bb, ins1); /* Move ins1 before the comparison */ mono_bblock_insert_before_ins (bb, compare, ins1); /* Add cmov instruction */ MONO_INST_NEW (cfg, cmov, OP_NOP); cmov->dreg = dreg; cmov->sreg1 = dreg; cmov->sreg2 = tmp_reg; cond = mono_opcode_to_cond (branch->opcode); if (branch->inst_false_bb == code_bb) cond = mono_negate_cond (cond); switch (mono_opcode_to_type (branch->opcode, compare->opcode)) { case CMP_TYPE_I: cmov->opcode = int_cmov_opcodes [cond]; break; case CMP_TYPE_L: cmov->opcode = long_cmov_opcodes [cond]; break; default: g_assert_not_reached (); } mono_bblock_insert_after_ins (bb, compare, cmov); /* Rewrite the branch */ branch->opcode = OP_BR; branch->inst_target_bb = next_bb; mono_link_bblock (cfg, bb, branch->inst_target_bb); /* Nullify the branch at the end of code_bb */ if (code_bb->code) { branch = code_bb->code; MONO_DELETE_INS (code_bb, branch); } /* Reorder bblocks */ mono_unlink_bblock (cfg, bb, code_bb); mono_unlink_bblock (cfg, code_bb, next_bb); /* Merge bb and its successor if possible */ if ((bb->out_bb [0]->in_count == 1) && (bb->out_bb [0] != cfg->bb_exit) && (bb->region == bb->out_bb [0]->region)) { mono_merge_basic_blocks (cfg, bb, bb->out_bb [0]); /* * bbn might have fallen through to the next bb without a branch, * have to add one now (#474718). * FIXME: Maybe need to do this more generally in * merge_basic_blocks () ? */ if (!(bb->last_ins && MONO_IS_BRANCH_OP (bb->last_ins)) && bb->out_count) { MONO_INST_NEW (cfg, ins1, OP_BR); ins1->inst_target_bb = bb->out_bb [0]; MONO_ADD_INS (bb, ins1); } goto restart; } } } /* * Optimize checks like: if (v < 0 || v > limit) by changing then to unsigned * compares. This isn't really if conversion, but it easier to do here than in * optimize_branches () since the IR is already optimized. */ for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { MonoBasicBlock *bb1, *bb2, *true_bb, *false_bb, *next_bb; MonoInst *branch1, *branch2, *compare1, *ins; /* Look for the IR code generated from if (<var> < 0 || v > <limit>) * after branch opts which is: * BB: * icompare_imm R [0] * int_blt [BB1BB2] * BB2: * icompare_imm R [<limit>] * int_ble [BB3BB1] */ if (!(bb->out_count == 2 && !bb->extended)) continue; bb1 = bb->out_bb [0]; bb2 = bb->out_bb [1]; // FIXME: Add more cases /* Check structure */ if (!(bb1->in_count == 2 && bb1->in_bb [0] == bb && bb1->in_bb [1] == bb2 && bb2->in_count == 1 && bb2->out_count == 2)) continue; next_bb = bb2; /* Check first branch */ branch1 = bb->last_ins; if (!(branch1 && ((branch1->opcode == OP_IBLT) || (branch1->opcode == OP_LBLT)) && (branch1->inst_false_bb == next_bb))) continue; true_bb = branch1->inst_true_bb; /* Check second branch */ branch2 = next_bb->last_ins; if (!branch2) continue; /* mcs sometimes generates inverted branches */ if (((branch2->opcode == OP_IBGT) || (branch2->opcode == OP_LBGT)) && branch2->inst_true_bb == branch1->inst_true_bb) false_bb = branch2->inst_false_bb; else if (((branch2->opcode == OP_IBLE) || (branch2->opcode == OP_LBLE)) && branch2->inst_false_bb == branch1->inst_true_bb) false_bb = branch2->inst_true_bb; else continue; /* Check first compare */ compare1 = bb->last_ins->prev; if (!(compare1 && ((compare1->opcode == OP_ICOMPARE_IMM) || (compare1->opcode == OP_LCOMPARE_IMM)) && compare1->inst_imm == 0)) continue; /* Check second bblock */ ins = next_bb->code; if (!ins) continue; if (((ins->opcode == OP_ICOMPARE_IMM) || (ins->opcode == OP_LCOMPARE_IMM)) && ins->sreg1 == compare1->sreg1 && ins->next == branch2) { /* The second arg must be positive */ if (ins->inst_imm < 0) continue; } else if (((ins->opcode == OP_LDLEN) || (ins->opcode == OP_STRLEN)) && ins->dreg != compare1->sreg1 && ins->next && ins->next->opcode == OP_ICOMPARE && ins->next->sreg1 == compare1->sreg1 && ins->next->sreg2 == ins->dreg && ins->next->next == branch2) { /* Another common case: if (index < 0 || index > arr.Length) */ } else { continue; } if (cfg->verbose_level > 2) { printf ("\tSigned->unsigned compare optimization in BB%d on\n", bb->block_num); printf ("\t\t"); mono_print_ins (compare1); printf ("\t\t"); mono_print_ins (compare1->next); printf ("\t\t"); mono_print_ins (ins); } /* Rewrite the first compare+branch */ MONO_DELETE_INS (bb, compare1); branch1->opcode = OP_BR; mono_unlink_bblock (cfg, bb, branch1->inst_true_bb); mono_unlink_bblock (cfg, bb, branch1->inst_false_bb); branch1->inst_target_bb = next_bb; mono_link_bblock (cfg, bb, next_bb); /* Rewrite the second branch */ branch2->opcode = br_to_br_un (branch2->opcode); mono_merge_basic_blocks (cfg, bb, next_bb); } #if 0 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { MonoBasicBlock *bb1, *bb2; MonoInst *prev, *compare, *branch, *ins1, *ins2, *cmov, *move, *tmp; gboolean simple, ret; int dreg, tmp_reg; CompType comp_type; /* Look for the IR code generated from if (cond) <var> <- <a> * after branch opts which is: * BB: * compare * b<cond> [BB1] * <var> <- <a> * BB1: */ if (!(bb->out_count == 1 && bb->extended && bb->code && bb->code->next && bb->code->next->next)) continue; mono_print_bb (bb, ""); /* Find the compare instruction */ prev = NULL; compare = bb->code; g_assert (compare); while (compare->next->next && compare->next->next != bb->last_ins) { prev = compare; compare = compare->next; } branch = compare->next; if (!MONO_IS_COND_BRANCH_OP (branch)) continue; } #endif if (changed) { if (cfg->opt & MONO_OPT_BRANCH) mono_optimize_branches (cfg); /* Merging bblocks could make some variables local */ mono_handle_global_vregs (cfg); if (cfg->opt & (MONO_OPT_CONSPROP | MONO_OPT_COPYPROP)) mono_local_cprop (cfg); if (cfg->opt & MONO_OPT_DEADCE) mono_local_deadce (cfg); } #endif }
void mono_remove_critical_edges (MonoCompile *cfg) { MonoBasicBlock *bb; MonoBasicBlock *previous_bb; if (cfg->verbose_level > 3) { for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { int i; printf ("remove_critical_edges, BEFORE BB%d (in:", bb->block_num); for (i = 0; i < bb->in_count; i++) { printf (" %d", bb->in_bb [i]->block_num); } printf (") (out:"); for (i = 0; i < bb->out_count; i++) { printf (" %d", bb->out_bb [i]->block_num); } printf (")"); if (bb->last_ins != NULL) { printf (" "); mono_print_ins (bb->last_ins); } printf ("\n"); } } for (previous_bb = cfg->bb_entry, bb = previous_bb->next_bb; bb != NULL; previous_bb = previous_bb->next_bb, bb = bb->next_bb) { if (bb->in_count > 1) { int in_bb_index; for (in_bb_index = 0; in_bb_index < bb->in_count; in_bb_index++) { MonoBasicBlock *in_bb = bb->in_bb [in_bb_index]; /* * Have to remove non-critical edges whose source ends with a BR_REG * ins too, since inserting a computation before the BR_REG could * overwrite the sreg1 of the ins. */ if ((in_bb->out_count > 1) || (in_bb->out_count == 1 && in_bb->last_ins && in_bb->last_ins->opcode == OP_BR_REG)) { MonoBasicBlock *new_bb = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoBasicBlock)); new_bb->block_num = cfg->num_bblocks++; // new_bb->real_offset = bb->real_offset; new_bb->region = bb->region; /* Do not alter the CFG while altering the BB list */ if (mono_bb_is_fall_through (cfg, previous_bb)) { if (previous_bb != cfg->bb_entry) { int i; /* Make sure previous_bb really falls through bb */ for (i = 0; i < previous_bb->out_count; i++) { if (previous_bb->out_bb [i] == bb) { MonoInst *jump; MONO_INST_NEW (cfg, jump, OP_BR); MONO_ADD_INS (previous_bb, jump); jump->cil_code = previous_bb->cil_code; jump->inst_target_bb = bb; break; } } } else { /* We cannot add any inst to the entry BB, so we must */ /* put a new BB in the middle to hold the OP_BR */ MonoInst *jump; MonoBasicBlock *new_bb_after_entry = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoBasicBlock)); new_bb_after_entry->block_num = cfg->num_bblocks++; // new_bb_after_entry->real_offset = bb->real_offset; new_bb_after_entry->region = bb->region; MONO_INST_NEW (cfg, jump, OP_BR); MONO_ADD_INS (new_bb_after_entry, jump); jump->cil_code = bb->cil_code; jump->inst_target_bb = bb; mono_unlink_bblock (cfg, previous_bb, bb); mono_link_bblock (cfg, new_bb_after_entry, bb); mono_link_bblock (cfg, previous_bb, new_bb_after_entry); previous_bb->next_bb = new_bb_after_entry; previous_bb = new_bb_after_entry; if (cfg->verbose_level > 2) { printf ("remove_critical_edges, added helper BB%d jumping to BB%d\n", new_bb_after_entry->block_num, bb->block_num); } } } /* Insert new_bb in the BB list */ previous_bb->next_bb = new_bb; new_bb->next_bb = bb; previous_bb = new_bb; /* Setup in_bb and out_bb */ new_bb->in_bb = mono_mempool_alloc ((cfg)->mempool, sizeof (MonoBasicBlock*)); new_bb->in_bb [0] = in_bb; new_bb->in_count = 1; new_bb->out_bb = mono_mempool_alloc ((cfg)->mempool, sizeof (MonoBasicBlock*)); new_bb->out_bb [0] = bb; new_bb->out_count = 1; /* Relink in_bb and bb to (from) new_bb */ replace_out_block (in_bb, bb, new_bb); replace_out_block_in_code (in_bb, bb, new_bb); replace_in_block (bb, in_bb, new_bb); if (cfg->verbose_level > 2) { printf ("remove_critical_edges, removed critical edge from BB%d to BB%d (added BB%d)\n", in_bb->block_num, bb->block_num, new_bb->block_num); } } } } } if (cfg->verbose_level > 3) { for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { int i; printf ("remove_critical_edges, AFTER BB%d (in:", bb->block_num); for (i = 0; i < bb->in_count; i++) { printf (" %d", bb->in_bb [i]->block_num); } printf (") (out:"); for (i = 0; i < bb->out_count; i++) { printf (" %d", bb->out_bb [i]->block_num); } printf (")"); if (bb->last_ins != NULL) { printf (" "); mono_print_ins (bb->last_ins); } printf ("\n"); } } }