target = ins_offset + *(gint16*)(data + 1);
g_string_append_printf (str, " %u, IR_%04x", *(guint16*)data, target);
}
+ case MintOpPair2:
+ g_string_append_printf (str, " %u <- %u, %u <- %u", data [0], data [1], data [2], data [3]);
+ break;
+ case MintOpPair3:
+ g_string_append_printf (str, " %u <- %u, %u <- %u, %u <- %u", data [0], data [1], data [2], data [3], data [4], data [5]);
+ break;
+ case MintOpPair4:
+ g_string_append_printf (str, " %u <- %u, %u <- %u, %u <- %u, %u <- %u", data [0], data [1], data [2], data [3], data [4], data [5], data [6], data [7]);
break;
default:
g_string_append_printf (str, "unknown arg type\n");
if (ins->info.target_bb->native_offset >= 0) {
// Backwards branch. We can already patch it.
*ip++ = ins->info.target_bb->native_offset - br_offset;
+ } else if (opcode == MINT_BR_S && ins->info.target_bb == td->cbb->next_bb) {
+ // Ignore branch to the next basic block. Revert the added MINT_BR_S.
+ ip--;
} else {
// We don't know the in_offset of the target, add a reloc
Reloc *reloc = (Reloc*)mono_mempool_alloc0 (td->mempool, sizeof (Reloc));
for (int i = size - 1; i < (jit_call2_size - 1); i++)
*ip++ = MINT_NIY;
#endif
+ } else if (opcode >= MINT_MOV_8_2 && opcode <= MINT_MOV_8_4) {
+ // This instruction is not marked as operating on any vars, all instruction slots are
+ // actually vas. Resolve their offset
+ int num_vars = mono_interp_oplen [opcode] - 1;
+ for (int i = 0; i < num_vars; i++)
+ *ip++ = td->locals [ins->data [i]].offset;
} else {
if (mono_interp_op_dregs [opcode])
*ip++ = td->locals [ins->dreg].offset;
for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) {
InterpInst *ins = bb->first_ins;
bb->native_offset = ip - td->new_code;
+ td->cbb = bb;
while (ins) {
ip = emit_compacted_instruction (td, ip, ins);
ins = ins->next;
static InterpInst*
-interp_fold_binop (TransformData *td, LocalValue *local_defs, InterpInst *ins)
+interp_fold_binop (TransformData *td, LocalValue *local_defs, InterpInst *ins, gboolean *folded)
{
int *local_ref_count = td->local_ref_count;
// ins should be a binop, therefore it should have a single dreg and two sregs
LocalValue *val2 = &local_defs [sreg2];
LocalValue result;
+ *folded = FALSE;
+
if (val1->type != LOCAL_VALUE_I4 && val1->type != LOCAL_VALUE_I8)
return ins;
if (val2->type != LOCAL_VALUE_I4 && val2->type != LOCAL_VALUE_I8)
// with a LDC of the constant. We leave alone the sregs of this instruction, for
// deadce to kill the instructions initializing them.
mono_interp_stats.constant_folds++;
-
+ *folded = TRUE;
if (result.type == LOCAL_VALUE_I4)
ins = interp_get_ldc_i4_from_const (td, ins, result.i, dreg);
else if (result.type == LOCAL_VALUE_I8)
} else if (MINT_IS_UNOP_CONDITIONAL_BRANCH (opcode)) {
ins = interp_fold_unop_cond_br (td, bb, local_defs, ins);
} else if (MINT_IS_BINOP (opcode)) {
- ins = interp_fold_binop (td, local_defs, ins);
+ gboolean folded;
+ ins = interp_fold_binop (td, local_defs, ins, &folded);
+ if (!folded) {
+ int sreg = -1;
+ int mov_op;
+ if ((opcode == MINT_MUL_I4 || opcode == MINT_DIV_I4) &&
+ local_defs [ins->sregs [1]].type == LOCAL_VALUE_I4 &&
+ local_defs [ins->sregs [1]].i == 1) {
+ sreg = ins->sregs [0];
+ mov_op = MINT_MOV_4;
+ } else if ((opcode == MINT_MUL_I8 || opcode == MINT_DIV_I8) &&
+ local_defs [ins->sregs [1]].type == LOCAL_VALUE_I8 &&
+ local_defs [ins->sregs [1]].l == 1) {
+ sreg = ins->sregs [0];
+ mov_op = MINT_MOV_8;
+ } else if (opcode == MINT_MUL_I4 &&
+ local_defs [ins->sregs [0]].type == LOCAL_VALUE_I4 &&
+ local_defs [ins->sregs [0]].i == 1) {
+ sreg = ins->sregs [1];
+ mov_op = MINT_MOV_4;
+ } else if (opcode == MINT_MUL_I8 &&
+ local_defs [ins->sregs [0]].type == LOCAL_VALUE_I8 &&
+ local_defs [ins->sregs [0]].l == 1) {
+ sreg = ins->sregs [1];
+ mov_op = MINT_MOV_8;
+ }
+ if (sreg != -1) {
+ ins->opcode = mov_op;
+ ins->sregs [0] = sreg;
+ if (td->verbose_level) {
+ g_print ("Replace idempotent binop :\n\t");
+ dump_interp_inst (ins);
+ }
+ needs_retry = TRUE;
+ }
+ }
} else if (MINT_IS_BINOP_CONDITIONAL_BRANCH (opcode)) {
ins = interp_fold_binop_cond_br (td, bb, local_defs, ins);
} else if (MINT_IS_LDFLD (opcode) && ins->data [0] == 0) {
if (ins->flags & INTERP_INST_FLAG_CALL) {
int *call_args = ins->info.call_args;
if (call_args) {
+ int pair_sregs [MINT_MOV_PAIRS_MAX];
+ int pair_dregs [MINT_MOV_PAIRS_MAX];
+ int num_pairs = 0;
int var = *call_args;
+
while (var != -1) {
if (td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL ||
td->locals [var].flags & INTERP_LOCAL_FLAG_NO_CALL_ARGS) {
int new_var = create_interp_local (td, td->locals [var].type);
td->locals [new_var].call = ins;
td->locals [new_var].flags |= INTERP_LOCAL_FLAG_CALL_ARGS;
- int opcode = get_mov_for_type (mint_type (td->locals [var].type), FALSE);
- InterpInst *new_inst = interp_insert_ins_bb (td, bb, ins->prev, opcode);
- interp_ins_set_dreg (new_inst, new_var);
- interp_ins_set_sreg (new_inst, var);
- if (opcode == MINT_MOV_VT)
- new_inst->data [0] = td->locals [var].size;
- // The arg of the call is no longer global
- *call_args = new_var;
- // Also update liveness for this instruction
- foreach_local_var (td, new_inst, ins_index, set_var_live_range);
- ins_index++;
+
+ int mt = mint_type (td->locals [var].type);
+ if (mt != MINT_TYPE_VT && num_pairs < MINT_MOV_PAIRS_MAX) {
+ pair_sregs [num_pairs] = var;
+ pair_dregs [num_pairs] = new_var;
+ num_pairs++;
+ // The arg of the call is no longer global
+ *call_args = new_var;
+ } else {
+ int opcode = get_mov_for_type (mt, FALSE);
+ InterpInst *new_inst = interp_insert_ins_bb (td, bb, ins->prev, opcode);
+ interp_ins_set_dreg (new_inst, new_var);
+ interp_ins_set_sreg (new_inst, var);
+ if (opcode == MINT_MOV_VT)
+ new_inst->data [0] = td->locals [var].size;
+ // The arg of the call is no longer global
+ *call_args = new_var;
+ // Also update liveness for this instruction
+ foreach_local_var (td, new_inst, ins_index, set_var_live_range);
+ ins_index++;
+ }
} else {
// Flag this var as it has special storage on the call args stack
td->locals [var].call = ins;
call_args++;
var = *call_args;
}
+ if (num_pairs > 0) {
+ int i;
+ for (i = 0; i < num_pairs; i++) {
+ set_var_live_range (td, pair_sregs [i], ins_index);
+ set_var_live_range (td, pair_dregs [i], ins_index);
+ }
+ if (num_pairs == 1) {
+ int mt = mint_type (td->locals [pair_sregs [0]].type);
+ int opcode = get_mov_for_type (mt, FALSE);
+ InterpInst *new_inst = interp_insert_ins_bb (td, bb, ins->prev, opcode);
+ interp_ins_set_dreg (new_inst, pair_dregs [0]);
+ interp_ins_set_sreg (new_inst, pair_sregs [0]);
+ } else {
+ // Squash together multiple moves to the param area into a single opcode
+ int opcode = MINT_MOV_8_2 + num_pairs - 2;
+ InterpInst *new_inst = interp_insert_ins_bb (td, bb, ins->prev, opcode);
+ int k = 0;
+ for (i = 0; i < num_pairs; i++) {
+ new_inst->data [k++] = pair_dregs [i];
+ new_inst->data [k++] = pair_sregs [i];
+ }
+ }
+ ins_index++;
+ }
}
}
// Set live_start and live_end for every referenced local that is not global