*
* (C) 2005 Ximian, Inc. http://www.ximian.com
* Copyright 2011 Xamarin Inc. http://www.xamarin.com
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/
- #include "mini.h"
+#include "config.h"
+#include <mono/utils/mono-compiler.h>
#ifndef DISABLE_JIT
-
+
+#include "mini.h"
/*
* Returns true if @bb is a basic block which falls through the next block.
if (bb->region == -1 || !MONO_BBLOCK_IS_IN_REGION (bb, MONO_REGION_TRY))
return NULL;
- exclass = mono_class_from_name (mono_get_corlib (), "System", exname);
+ exclass = mono_class_load_from_name (mono_get_corlib (), "System", exname);
/* search for the handler */
for (i = 0; i < header->num_clauses; ++i) {
clause = &header->clauses [i];
MONO_INST_NEW (cfg, jump, OP_BR);
/* Allocate memory for our branch target */
- jump->inst_i1 = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst));
+ jump->inst_i1 = (MonoInst *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst));
jump->inst_true_bb = targetbb;
if (cfg->verbose_level > 2)
/* Multiple BBs */
- /* Set region */
- for (tmp = first_bb; tmp; tmp = tmp->next_bb)
+ /* Set region/real_offset */
+ for (tmp = first_bb; tmp; tmp = tmp->next_bb) {
tmp->region = bb->region;
+ tmp->real_offset = bb->real_offset;
+ }
/* Split the original bb */
if (ins->next)
bb->has_array_access |= first_bb->has_array_access;
/* Delete the links between the original bb and its successors */
- tmp_bblocks = bb->out_bb;
+ tmp_bblocks = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoBasicBlock*) * bb->out_count);
+ memcpy (tmp_bblocks, bb->out_bb, sizeof (MonoBasicBlock*) * bb->out_count);
count = bb->out_count;
for (i = 0; i < count; ++i)
mono_unlink_bblock (cfg, bb, tmp_bblocks [i]);
#ifdef MONO_ARCH_HAVE_CMOV_OPS
MonoBasicBlock *bb;
gboolean changed = FALSE;
+ int filter = FILTER_NOP | FILTER_IL_SEQ_POINT;
if (!(cfg->opt & MONO_OPT_CMOV))
return;
int dreg, tmp_reg;
CompType comp_type;
- if (bb->last_ins && (bb->last_ins->opcode == OP_BR_REG || bb->last_ins->opcode == OP_BR))
+ branch = mono_bb_last_inst (bb, filter);
+
+ if (!branch || branch->opcode == OP_BR_REG || branch->opcode == OP_BR)
continue;
/* Find the compare instruction */
- if (!bb->last_ins || !bb->last_ins->prev)
+ compare = mono_inst_prev (branch, filter);
+ if (!compare)
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 */
* 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;
+ ins1 = mono_bb_first_inst (true_bb, filter);
+ ins2 = mono_bb_first_inst (false_bb, filter);
+
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)))
+ if (!((tmp->opcode == OP_NOP) || (tmp->opcode == OP_IL_SEQ_POINT) || (tmp->opcode == OP_BR)))
simple = FALSE;
for (tmp = ins2->next; tmp; tmp = tmp->next)
- if (!((tmp->opcode == OP_NOP) || (tmp->opcode == OP_BR)))
+ if (!((tmp->opcode == OP_NOP) || (tmp->opcode == OP_IL_SEQ_POINT) || (tmp->opcode == OP_BR)))
simple = FALSE;
if (!simple)
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 (mono_inst_next (compare, filter));
printf ("\t\t"); mono_print_ins (ins1);
printf ("\t\t"); mono_print_ins (ins2);
}
next_bb = bb2;
}
- ins1 = code_bb->code;
+ ins1 = mono_bb_first_inst (code_bb, filter);
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)))
+ for (tmp = ins1; tmp; tmp = tmp->next)
+ if (!((tmp->opcode == OP_NOP) || (tmp->opcode == OP_IL_SEQ_POINT) || (tmp->opcode == OP_BR)))
simple = FALSE;
if (!simple)
if (!MONO_INS_HAS_NO_SIDE_EFFECT (ins1))
continue;
- if (bb->last_ins && bb->last_ins->opcode == OP_BR_REG)
+ branch = mono_bb_last_inst (bb, filter);
+
+ if (!branch || branch->opcode == OP_BR_REG)
continue;
/* Find the compare instruction */
-
- if (!bb->last_ins || !bb->last_ins->prev)
+ compare = mono_inst_prev (branch, filter);
+ if (!compare)
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 */
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 (mono_inst_next (compare, filter));
printf ("\t\t"); mono_print_ins (ins1);
}
* 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;
+ MonoBasicBlock *bb1, *bb2, *next_bb;
+ MonoInst *branch1, *branch2, *compare1, *ins, *next;
/* Look for the IR code generated from if (<var> < 0 || v > <limit>)
* after branch opts which is:
next_bb = bb2;
/* Check first branch */
- branch1 = bb->last_ins;
+ branch1 = mono_bb_last_inst (bb, filter);
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;
+ branch2 = mono_bb_last_inst (next_bb, filter);
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;
+ compare1 = mono_inst_prev (mono_bb_last_inst (bb, filter), filter);
if (!(compare1 && ((compare1->opcode == OP_ICOMPARE_IMM) || (compare1->opcode == OP_LCOMPARE_IMM)) && compare1->inst_imm == 0))
continue;
/* Check second bblock */
- ins = next_bb->code;
+ ins = mono_bb_first_inst (next_bb, filter);
if (!ins)
continue;
- if (((ins->opcode == OP_ICOMPARE_IMM) || (ins->opcode == OP_LCOMPARE_IMM)) && ins->sreg1 == compare1->sreg1 && ins->next == branch2) {
+ next = mono_inst_next (ins, filter);
+ if (((ins->opcode == OP_ICOMPARE_IMM) || (ins->opcode == OP_LCOMPARE_IMM)) && ins->sreg1 == compare1->sreg1 && 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) {
+ } else if (((ins->opcode == OP_LDLEN) || (ins->opcode == OP_STRLEN)) && ins->dreg != compare1->sreg1 && next && next->opcode == OP_ICOMPARE && next->sreg1 == compare1->sreg1 && next->sreg2 == ins->dreg && mono_inst_next (next, filter) == 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 (mono_inst_next (compare1, filter));
printf ("\t\t"); mono_print_ins (ins);
}
ins->inst_false_bb = repl;
} else if (MONO_IS_JUMP_TABLE (ins)) {
int i;
- MonoJumpInfoBBTable *table = MONO_JUMP_TABLE_FROM_INS (ins);
+ MonoJumpInfoBBTable *table = (MonoJumpInfoBBTable *)MONO_JUMP_TABLE_FROM_INS (ins);
for (i = 0; i < table->table_size; i++ ) {
if (table->table [i] == orig)
table->table [i] = repl;
MONO_BB_FOR_EACH_INS (bb, inst) {
switch (inst->opcode) {
case OP_NOP:
+ case OP_IL_SEQ_POINT:
break;
case OP_BR:
target_bb = inst->inst_target_bb;
MonoBasicBlock *prev_bb;
int i;
+ /* There may be only one control flow edge between two BBs that we merge, and it should connect these BBs together. */
+ g_assert (bb->out_count == 1 && bbn->in_count == 1 && bb->out_bb [0] == bbn && bbn->in_bb [0] == bb);
+
bb->has_array_access |= bbn->has_array_access;
bb->extended |= bbn->extended;
- /* Compute prev_bb if possible to avoid the linear search below */
- prev_bb = NULL;
- for (i = 0; i < bbn->in_count; ++i)
- if (bbn->in_bb [0]->next_bb == bbn)
- prev_bb = bbn->in_bb [0];
-
mono_unlink_bblock (cfg, bb, bbn);
for (i = 0; i < bbn->out_count; ++i)
mono_link_bblock (cfg, bb, bbn->out_bb [i]);
for (inst = bb->code; inst != NULL; inst = inst->next) {
if (MONO_IS_JUMP_TABLE (inst)) {
int i;
- MonoJumpInfoBBTable *table = MONO_JUMP_TABLE_FROM_INS (inst);
+ MonoJumpInfoBBTable *table = (MonoJumpInfoBBTable *)MONO_JUMP_TABLE_FROM_INS (inst);
for (i = 0; i < table->table_size; i++ ) {
/* Might be already NULL from a previous merge */
if (table->table [i])
bb->last_ins = bbn->last_ins;
}
- if (!prev_bb) {
+
+ /* Check if the control flow predecessor is also the linear IL predecessor. */
+ if (bbn->in_bb [0]->next_bb == bbn)
+ prev_bb = bbn->in_bb [0];
+ else
+ /* If it isn't, look for one among all basic blocks. */
for (prev_bb = cfg->bb_entry; prev_bb && prev_bb->next_bb != bbn; prev_bb = prev_bb->next_bb)
;
- }
if (prev_bb) {
prev_bb->next_bb = bbn->next_bb;
} else {
* 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));
+ MonoBasicBlock *new_bb = (MonoBasicBlock *)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;
/* 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));
+ MonoBasicBlock *new_bb_after_entry = (MonoBasicBlock *)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;
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 = (MonoBasicBlock **)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 = (MonoBasicBlock **)mono_mempool_alloc ((cfg)->mempool, sizeof (MonoBasicBlock*));
new_bb->out_bb [0] = bb;
new_bb->out_count = 1;
void
mono_optimize_branches (MonoCompile *cfg)
{
- int i, changed = FALSE;
+ int i, count = 0, changed = FALSE;
MonoBasicBlock *bb, *bbn;
guint32 niterations;
+ MonoInst *bbn_first_inst;
+ int filter = FILTER_IL_SEQ_POINT;
/*
* Some crazy loops could cause the code below to go into an infinite
/* we skip the entry block (exit is handled specially instead ) */
for (previous_bb = cfg->bb_entry, bb = cfg->bb_entry->next_bb; bb; previous_bb = bb, bb = bb->next_bb) {
+ count ++;
+ if (count == 1000) {
+ mono_threads_safepoint ();
+ count = 0;
+ }
/* dont touch code inside exception clauses */
if (bb->region != -1)
continue;
if (bb->last_ins && bb->last_ins->opcode == OP_BR) {
bbn = bb->last_ins->inst_target_bb;
- if (bb->region == bbn->region && bbn->code && bbn->code->opcode == OP_BR &&
- bbn->code->inst_target_bb != bbn &&
- bbn->code->inst_target_bb->region == bb->region) {
+ bbn_first_inst = mono_bb_first_inst (bbn, filter);
+ if (bb->region == bbn->region && bbn_first_inst && bbn_first_inst->opcode == OP_BR &&
+ bbn_first_inst->inst_target_bb != bbn &&
+ bbn_first_inst->inst_target_bb->region == bb->region) {
if (cfg->verbose_level > 2)
- g_print ("branch to branch triggered %d -> %d -> %d\n", bb->block_num, bbn->block_num, bbn->code->inst_target_bb->block_num);
+ g_print ("branch to branch triggered %d -> %d -> %d\n", bb->block_num, bbn->block_num, bbn_first_inst->inst_target_bb->block_num);
replace_in_block (bbn, bb, NULL);
- replace_out_block (bb, bbn, bbn->code->inst_target_bb);
- mono_link_bblock (cfg, bb, bbn->code->inst_target_bb);
- bb->last_ins->inst_target_bb = bbn->code->inst_target_bb;
+ replace_out_block (bb, bbn, bbn_first_inst->inst_target_bb);
+ mono_link_bblock (cfg, bb, bbn_first_inst->inst_target_bb);
+ bb->last_ins->inst_target_bb = bbn_first_inst->inst_target_bb;
changed = TRUE;
continue;
}
continue;
}
bbn = bb->last_ins->inst_true_bb;
- if (bb->region == bbn->region && bbn->code && bbn->code->opcode == OP_BR &&
- bbn->code->inst_target_bb->region == bb->region) {
+ bbn_first_inst = mono_bb_first_inst (bbn, filter);
+ if (bb->region == bbn->region && bbn_first_inst && bbn_first_inst->opcode == OP_BR &&
+ bbn_first_inst->inst_target_bb->region == bb->region) {
if (cfg->verbose_level > 2)
g_print ("cbranch1 to branch triggered %d -> (%d) %d (0x%02x)\n",
- bb->block_num, bbn->block_num, bbn->code->inst_target_bb->block_num,
- bbn->code->opcode);
+ bb->block_num, bbn->block_num, bbn_first_inst->inst_target_bb->block_num,
+ bbn_first_inst->opcode);
/*
* Unlink, then relink bblocks to avoid various
mono_unlink_bblock (cfg, bb, bb->last_ins->inst_true_bb);
mono_unlink_bblock (cfg, bb, bb->last_ins->inst_false_bb);
- bb->last_ins->inst_true_bb = bbn->code->inst_target_bb;
+ bb->last_ins->inst_true_bb = bbn_first_inst->inst_target_bb;
mono_link_bblock (cfg, bb, bb->last_ins->inst_true_bb);
mono_link_bblock (cfg, bb, bb->last_ins->inst_false_bb);
}
bbn = bb->last_ins->inst_false_bb;
- if (bbn && bb->region == bbn->region && bbn->code && bbn->code->opcode == OP_BR &&
- bbn->code->inst_target_bb->region == bb->region) {
+ bbn_first_inst = mono_bb_first_inst (bbn, filter);
+ if (bbn && bb->region == bbn->region && bbn_first_inst && bbn_first_inst->opcode == OP_BR &&
+ bbn_first_inst->inst_target_bb->region == bb->region) {
if (cfg->verbose_level > 2)
g_print ("cbranch2 to branch triggered %d -> (%d) %d (0x%02x)\n",
- bb->block_num, bbn->block_num, bbn->code->inst_target_bb->block_num,
- bbn->code->opcode);
+ bb->block_num, bbn->block_num, bbn_first_inst->inst_target_bb->block_num,
+ bbn_first_inst->opcode);
mono_unlink_bblock (cfg, bb, bb->last_ins->inst_true_bb);
mono_unlink_bblock (cfg, bb, bb->last_ins->inst_false_bb);
- bb->last_ins->inst_false_bb = bbn->code->inst_target_bb;
+ bb->last_ins->inst_false_bb = bbn_first_inst->inst_target_bb;
mono_link_bblock (cfg, bb, bb->last_ins->inst_true_bb);
mono_link_bblock (cfg, bb, bb->last_ins->inst_false_bb);
} while (changed && (niterations > 0));
}
-#endif /* DISABLE_JIT */
+#else /* !DISABLE_JIT */
+
+MONO_EMPTY_SOURCE_FILE (branch_opts);
+
+#endif /* !DISABLE_JIT */