/* * Attempt the removal of bounds checks from a MonoInst. * inst: the MonoInst * area: the current evaluation area (it contains the relation graph and * memory for all the evaluation contexts is already allocated) */ static void remove_abc_from_inst (MonoInst *ins, MonoVariableRelationsEvaluationArea *area) { /* FIXME: Add support for 'constant' arrays and constant indexes */ int array_variable = ins->sreg1; int index_variable = ins->sreg2; MonoRelationsEvaluationContext *array_context = &(area->contexts [array_variable]); MonoRelationsEvaluationContext *index_context = &(area->contexts [index_variable]); clean_contexts (area->contexts, area->cfg->next_vreg); evaluate_relation_with_target_variable (area, index_variable, array_variable, NULL); evaluate_relation_with_target_variable (area, array_variable, array_variable, NULL); if ((index_context->ranges.zero.lower >=0) && ((index_context->ranges.variable.upper < 0)||(index_context->ranges.zero.upper < array_context->ranges.zero.lower))) { if (REPORT_ABC_REMOVAL) { printf ("ARRAY-ACCESS: removed bounds check on array %d with index %d\n", array_variable, index_variable); NULLIFY_INS (ins); } } else { if (TRACE_ABC_REMOVAL) { if (index_context->ranges.zero.lower >= 0) { printf ("ARRAY-ACCESS: Removed lower bound check on array %d with index %d\n", array_variable, index_variable); } if (index_context->ranges.variable.upper < 0) { printf ("ARRAY-ACCESS: Removed upper bound check (through variable) on array %d with index %d\n", array_variable, index_variable); } if (index_context->ranges.zero.upper < array_context->ranges.zero.lower) { printf ("ARRAY-ACCESS: Removed upper bound check (through constant) on array %d with index %d\n", array_variable, index_variable); } } } }
/* * mono_local_cprop: * * A combined local copy and constant propagation pass. */ void mono_local_cprop (MonoCompile *cfg) { MonoBasicBlock *bb; MonoInst **defs; gint32 *def_index; int max; restart: max = cfg->next_vreg; defs = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * (cfg->next_vreg + 1)); def_index = mono_mempool_alloc (cfg->mempool, sizeof (guint32) * (cfg->next_vreg + 1)); for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { MonoInst *ins; int ins_index; int last_call_index; /* Manually init the defs entries used by the bblock */ MONO_BB_FOR_EACH_INS (bb, ins) { int sregs [MONO_MAX_SRC_REGS]; int num_sregs, i; if ((ins->dreg != -1) && (ins->dreg < max)) { defs [ins->dreg] = NULL; #if SIZEOF_REGISTER == 4 defs [ins->dreg + 1] = NULL; #endif } num_sregs = mono_inst_get_src_registers (ins, sregs); for (i = 0; i < num_sregs; ++i) { int sreg = sregs [i]; if (sreg < max) { defs [sreg] = NULL; #if SIZEOF_REGISTER == 4 defs [sreg + 1] = NULL; #endif } } } ins_index = 0; last_call_index = -1; MONO_BB_FOR_EACH_INS (bb, ins) { const char *spec = INS_INFO (ins->opcode); int regtype, srcindex, sreg; int num_sregs; int sregs [MONO_MAX_SRC_REGS]; if (ins->opcode == OP_NOP) { MONO_DELETE_INS (bb, ins); continue; } g_assert (ins->opcode > MONO_CEE_LAST); /* FIXME: Optimize this */ if (ins->opcode == OP_LDADDR) { MonoInst *var = ins->inst_p0; defs [var->dreg] = NULL; /* if (!MONO_TYPE_ISSTRUCT (var->inst_vtype)) break; */ } if (MONO_IS_STORE_MEMBASE (ins)) { sreg = ins->dreg; regtype = 'i'; if ((regtype == 'i') && (sreg != -1) && defs [sreg]) { MonoInst *def = defs [sreg]; if ((def->opcode == OP_MOVE) && (!defs [def->sreg1] || (def_index [def->sreg1] < def_index [sreg])) && !vreg_is_volatile (cfg, def->sreg1)) { int vreg = def->sreg1; if (cfg->verbose_level > 2) printf ("CCOPY: R%d -> R%d\n", sreg, vreg); ins->dreg = vreg; } } } num_sregs = mono_inst_get_src_registers (ins, sregs); for (srcindex = 0; srcindex < num_sregs; ++srcindex) { MonoInst *def; int nregs; nregs = mono_inst_get_src_registers (ins, sregs); regtype = spec [MONO_INST_SRC1 + srcindex]; sreg = sregs [srcindex]; if ((regtype == ' ') || (sreg == -1) || (!defs [sreg])) continue; def = defs [sreg]; /* Copy propagation */ /* * The first check makes sure the source of the copy did not change since * the copy was made. * The second check avoids volatile variables. * The third check avoids copy propagating local vregs through a call, * since the lvreg will be spilled * The fourth check avoids copy propagating a vreg in cases where * it would be eliminated anyway by reverse copy propagation later, * because propagating it would create another use for it, thus making * it impossible to use reverse copy propagation. */ /* Enabling this for floats trips up the fp stack */ /* * Enabling this for floats on amd64 seems to cause a failure in * basic-math.cs, most likely because it gets rid of some r8->r4 * conversions. */ if (MONO_IS_MOVE (def) && (!defs [def->sreg1] || (def_index [def->sreg1] < def_index [sreg])) && !vreg_is_volatile (cfg, def->sreg1) && /* This avoids propagating local vregs across calls */ ((get_vreg_to_inst (cfg, def->sreg1) || !defs [def->sreg1] || (def_index [def->sreg1] >= last_call_index) || (def->opcode == OP_VMOVE))) && !(defs [def->sreg1] && mono_inst_next (defs [def->sreg1], FILTER_IL_SEQ_POINT) == def) && (!MONO_ARCH_USE_FPSTACK || (def->opcode != OP_FMOVE)) && (def->opcode != OP_FMOVE)) { int vreg = def->sreg1; if (cfg->verbose_level > 2) printf ("CCOPY/2: R%d -> R%d\n", sreg, vreg); sregs [srcindex] = vreg; mono_inst_set_src_registers (ins, sregs); /* Allow further iterations */ srcindex = -1; continue; } /* Constant propagation */ /* FIXME: Make is_inst_imm a macro */ /* FIXME: Make is_inst_imm take an opcode argument */ /* is_inst_imm is only needed for binops */ if ((((def->opcode == OP_ICONST) || ((sizeof (gpointer) == 8) && (def->opcode == OP_I8CONST))) && (((srcindex == 0) && (ins->sreg2 == -1)) || mono_arch_is_inst_imm (def->inst_c0))) || (!MONO_ARCH_USE_FPSTACK && (def->opcode == OP_R8CONST))) { guint32 opcode2; /* srcindex == 1 -> binop, ins->sreg2 == -1 -> unop */ if ((srcindex == 1) && (ins->sreg1 != -1) && defs [ins->sreg1] && (defs [ins->sreg1]->opcode == OP_ICONST) && defs [ins->sreg2]) { /* Both arguments are constants, perform cfold */ mono_constant_fold_ins (cfg, ins, defs [ins->sreg1], defs [ins->sreg2], TRUE); } else if ((srcindex == 0) && (ins->sreg2 != -1) && defs [ins->sreg2]) { /* Arg 1 is constant, swap arguments if possible */ int opcode = ins->opcode; mono_constant_fold_ins (cfg, ins, defs [ins->sreg1], defs [ins->sreg2], TRUE); if (ins->opcode != opcode) { /* Allow further iterations */ srcindex = -1; continue; } } else if ((srcindex == 0) && (ins->sreg2 == -1)) { /* Constant unop, perform cfold */ mono_constant_fold_ins (cfg, ins, defs [ins->sreg1], NULL, TRUE); } opcode2 = mono_op_to_op_imm (ins->opcode); if ((opcode2 != -1) && mono_arch_is_inst_imm (def->inst_c0) && ((srcindex == 1) || (ins->sreg2 == -1))) { ins->opcode = opcode2; if ((def->opcode == OP_I8CONST) && (sizeof (gpointer) == 4)) { ins->inst_ls_word = def->inst_ls_word; ins->inst_ms_word = def->inst_ms_word; } else { ins->inst_imm = def->inst_c0; } sregs [srcindex] = -1; mono_inst_set_src_registers (ins, sregs); if ((opcode2 == OP_VOIDCALL) || (opcode2 == OP_CALL) || (opcode2 == OP_LCALL) || (opcode2 == OP_FCALL)) ((MonoCallInst*)ins)->fptr = (gpointer)ins->inst_imm; /* Allow further iterations */ srcindex = -1; continue; } else { /* Special cases */ #if defined(TARGET_X86) || defined(TARGET_AMD64) if ((ins->opcode == OP_X86_LEA) && (srcindex == 1)) { #if SIZEOF_REGISTER == 8 /* FIXME: Use OP_PADD_IMM when the new JIT is done */ ins->opcode = OP_LADD_IMM; #else ins->opcode = OP_ADD_IMM; #endif ins->inst_imm += def->inst_c0 << ins->backend.shift_amount; ins->sreg2 = -1; } #endif opcode2 = mono_load_membase_to_load_mem (ins->opcode); if ((srcindex == 0) && (opcode2 != -1) && mono_arch_is_inst_imm (def->inst_c0)) { ins->opcode = opcode2; ins->inst_imm = def->inst_c0 + ins->inst_offset; ins->sreg1 = -1; } } } else if (((def->opcode == OP_ADD_IMM) || (def->opcode == OP_LADD_IMM)) && (MONO_IS_LOAD_MEMBASE (ins) || MONO_ARCH_IS_OP_MEMBASE (ins->opcode))) { /* ADD_IMM is created by spill_global_vars */ /* * We have to guarantee that def->sreg1 haven't changed since def->dreg * was defined. cfg->frame_reg is assumed to remain constant. */ if ((def->sreg1 == cfg->frame_reg) || ((def->next == ins) && (def->dreg != def->sreg1))) { ins->inst_basereg = def->sreg1; ins->inst_offset += def->inst_imm; } } else if ((ins->opcode == OP_ISUB_IMM) && (def->opcode == OP_IADD_IMM) && (def->next == ins) && (def->dreg != def->sreg1)) { ins->sreg1 = def->sreg1; ins->inst_imm -= def->inst_imm; } else if ((ins->opcode == OP_IADD_IMM) && (def->opcode == OP_ISUB_IMM) && (def->next == ins) && (def->dreg != def->sreg1)) { ins->sreg1 = def->sreg1; ins->inst_imm -= def->inst_imm; } else if (ins->opcode == OP_STOREI1_MEMBASE_REG && (def->opcode == OP_ICONV_TO_U1 || def->opcode == OP_ICONV_TO_I1 || def->opcode == OP_SEXT_I4 || (SIZEOF_REGISTER == 8 && def->opcode == OP_LCONV_TO_U1)) && (!defs [def->sreg1] || (def_index [def->sreg1] < def_index [sreg]))) { /* Avoid needless sign extension */ ins->sreg1 = def->sreg1; } else if (ins->opcode == OP_STOREI2_MEMBASE_REG && (def->opcode == OP_ICONV_TO_U2 || def->opcode == OP_ICONV_TO_I2 || def->opcode == OP_SEXT_I4 || (SIZEOF_REGISTER == 8 && def->opcode == OP_LCONV_TO_I2)) && (!defs [def->sreg1] || (def_index [def->sreg1] < def_index [sreg]))) { /* Avoid needless sign extension */ ins->sreg1 = def->sreg1; } } /* Do strength reduction here */ /* FIXME: Add long/float */ switch (ins->opcode) { case OP_MOVE: case OP_XMOVE: if (ins->dreg == ins->sreg1) { MONO_DELETE_INS (bb, ins); spec = INS_INFO (ins->opcode); } break; case OP_ADD_IMM: case OP_IADD_IMM: case OP_SUB_IMM: case OP_ISUB_IMM: #if SIZEOF_REGISTER == 8 case OP_LADD_IMM: case OP_LSUB_IMM: #endif if (ins->inst_imm == 0) { ins->opcode = OP_MOVE; spec = INS_INFO (ins->opcode); } break; case OP_MUL_IMM: case OP_IMUL_IMM: #if SIZEOF_REGISTER == 8 case OP_LMUL_IMM: #endif if (ins->inst_imm == 0) { ins->opcode = (ins->opcode == OP_LMUL_IMM) ? OP_I8CONST : OP_ICONST; ins->inst_c0 = 0; ins->sreg1 = -1; } else if (ins->inst_imm == 1) { ins->opcode = OP_MOVE; } else if ((ins->opcode == OP_IMUL_IMM) && (ins->inst_imm == -1)) { ins->opcode = OP_INEG; } else if ((ins->opcode == OP_LMUL_IMM) && (ins->inst_imm == -1)) { ins->opcode = OP_LNEG; } else { int power2 = mono_is_power_of_two (ins->inst_imm); if (power2 >= 0) { ins->opcode = (ins->opcode == OP_MUL_IMM) ? OP_SHL_IMM : ((ins->opcode == OP_LMUL_IMM) ? OP_LSHL_IMM : OP_ISHL_IMM); ins->inst_imm = power2; } } spec = INS_INFO (ins->opcode); break; case OP_IREM_UN_IMM: case OP_IDIV_UN_IMM: { int c = ins->inst_imm; int power2 = mono_is_power_of_two (c); if (power2 >= 0) { if (ins->opcode == OP_IREM_UN_IMM) { ins->opcode = OP_IAND_IMM; ins->sreg2 = -1; ins->inst_imm = (1 << power2) - 1; } else if (ins->opcode == OP_IDIV_UN_IMM) { ins->opcode = OP_ISHR_UN_IMM; ins->sreg2 = -1; ins->inst_imm = power2; } } spec = INS_INFO (ins->opcode); break; } case OP_IDIV_IMM: { int c = ins->inst_imm; int power2 = mono_is_power_of_two (c); MonoInst *tmp1, *tmp2, *tmp3, *tmp4; /* FIXME: Move this elsewhere cause its hard to implement it here */ if (power2 == 1) { int r1 = mono_alloc_ireg (cfg); NEW_BIALU_IMM (cfg, tmp1, OP_ISHR_UN_IMM, r1, ins->sreg1, 31); mono_bblock_insert_after_ins (bb, ins, tmp1); NEW_BIALU (cfg, tmp2, OP_IADD, r1, r1, ins->sreg1); mono_bblock_insert_after_ins (bb, tmp1, tmp2); NEW_BIALU_IMM (cfg, tmp3, OP_ISHR_IMM, ins->dreg, r1, 1); mono_bblock_insert_after_ins (bb, tmp2, tmp3); NULLIFY_INS (ins); // We allocated a new vreg, so need to restart goto restart; } else if (power2 > 0) { int r1 = mono_alloc_ireg (cfg); NEW_BIALU_IMM (cfg, tmp1, OP_ISHR_IMM, r1, ins->sreg1, 31); mono_bblock_insert_after_ins (bb, ins, tmp1); NEW_BIALU_IMM (cfg, tmp2, OP_ISHR_UN_IMM, r1, r1, (32 - power2)); mono_bblock_insert_after_ins (bb, tmp1, tmp2); NEW_BIALU (cfg, tmp3, OP_IADD, r1, r1, ins->sreg1); mono_bblock_insert_after_ins (bb, tmp2, tmp3); NEW_BIALU_IMM (cfg, tmp4, OP_ISHR_IMM, ins->dreg, r1, power2); mono_bblock_insert_after_ins (bb, tmp3, tmp4); NULLIFY_INS (ins); // We allocated a new vreg, so need to restart goto restart; } break; } } if (spec [MONO_INST_DEST] != ' ') { MonoInst *def = defs [ins->dreg]; if (def && (def->opcode == OP_ADD_IMM) && (def->sreg1 == cfg->frame_reg) && (MONO_IS_STORE_MEMBASE (ins))) { /* ADD_IMM is created by spill_global_vars */ /* cfg->frame_reg is assumed to remain constant */ ins->inst_destbasereg = def->sreg1; ins->inst_offset += def->inst_imm; } } if ((spec [MONO_INST_DEST] != ' ') && !MONO_IS_STORE_MEMBASE (ins) && !vreg_is_volatile (cfg, ins->dreg)) { defs [ins->dreg] = ins; def_index [ins->dreg] = ins_index; } if (MONO_IS_CALL (ins)) last_call_index = ins_index; ins_index ++; } }
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; }
static MonoInst* emit_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args, const MagicTypeInfo *info) { int i = 0; const char *name = cmethod->name; MonoInst *ins; int type_index, stack_type; if (info->op_index == 2 && cfg->r4fp && SIZEOF_VOID_P == 4) { type_index = 3; stack_type = STACK_R4; } else { type_index = info->op_index; stack_type = info->stack_type; } if (!strcmp ("op_Implicit", name) || !strcmp ("op_Explicit", name)) { int source_size = type_size (cfg, fsig->params [0]); int dest_size = type_size (cfg, fsig->ret); switch (info->big_stack_type) { case STACK_I8: if (!is_int_type (fsig->params [0]) || !is_int_type (fsig->ret)) return NULL; break; case STACK_R8: if (!is_float_type (fsig->params [0]) || !is_float_type (fsig->ret)) return NULL; break; default: g_assert_not_reached (); } //4 -> 4 or 8 -> 8 if (source_size == dest_size) return args [0]; //4 -> 8 if (source_size < dest_size) return emit_widen (cfg, info, args [0]->dreg); //8 -> 4 return emit_narrow (cfg, info, args [0]->dreg); } if (!strcmp (".ctor", name)) { gboolean is_ldaddr = args [0]->opcode == OP_LDADDR; int arg0 = args [1]->dreg; int arg_size = type_size (cfg, fsig->params [0]); if (arg_size > SIZEOF_VOID_P) //8 -> 4 arg0 = emit_narrow (cfg, info, arg0)->dreg; else if (arg_size < SIZEOF_VOID_P) //4 -> 8 arg0 = emit_widen (cfg, info, arg0)->dreg; if (is_ldaddr) { /*Eliminate LDADDR if it's initing a local var*/ int dreg = ((MonoInst*)args [0]->inst_p0)->dreg; NULLIFY_INS (args [0]); EMIT_NEW_UNALU (cfg, ins, info->move, dreg, arg0); cfg->has_indirection = TRUE; } else { EMIT_NEW_STORE_MEMBASE (cfg, ins, info->store_op, args [0]->dreg, 0, arg0); } return ins; } if (!strcmp ("op_Increment", name) || !strcmp ("op_Decrement", name)) { gboolean inc = !strcmp ("op_Increment", name); /* FIXME float inc is too complex to bother with*/ //this is broken with ints too // if (!info->inc_op) return NULL; /* We have IR for inc/dec */ MONO_INST_NEW (cfg, ins, inc ? info->inc_op : info->dec_op); ins->dreg = alloc_dreg (cfg, info->stack_type); ins->sreg1 = args [0]->dreg; ins->inst_imm = 1; ins->type = info->stack_type; MONO_ADD_INS (cfg->cbb, ins); return ins; } for (i = 0; i < sizeof (int_binop) / sizeof (IntIntrisic); ++i) { if (!strcmp (int_binop [i].op_name, name)) { if (!int_binop [i].op_table [info->op_index]) return NULL; g_assert (int_binop [i].op_table [type_index]); MONO_INST_NEW (cfg, ins, int_binop [i].op_table [type_index]); ins->dreg = alloc_dreg (cfg, stack_type); ins->sreg1 = args [0]->dreg; ins->sreg2 = args [1]->dreg; ins->type = stack_type; MONO_ADD_INS (cfg->cbb, ins); return mono_decompose_opcode (cfg, ins); } } for (i = 0; i < sizeof (int_unnop) / sizeof (IntIntrisic); ++i) { if (!strcmp (int_unnop [i].op_name, name)) { g_assert (int_unnop [i].op_table [type_index]); MONO_INST_NEW (cfg, ins, int_unnop [i].op_table [type_index]); ins->dreg = alloc_dreg (cfg, stack_type); ins->sreg1 = args [0]->dreg; ins->type = stack_type; MONO_ADD_INS (cfg->cbb, ins); return ins; } } for (i = 0; i < sizeof (int_cmpop) / sizeof (IntIntrisic); ++i) { if (!strcmp (int_cmpop [i].op_name, name)) { short op_cmp = int_cmpop [i].op_table [type_index]; g_assert (op_cmp); if (info->compare_op) { MONO_INST_NEW (cfg, ins, info->compare_op); ins->dreg = -1; ins->sreg1 = args [0]->dreg; ins->sreg2 = args [1]->dreg; MONO_ADD_INS (cfg->cbb, ins); MONO_INST_NEW (cfg, ins, op_cmp); ins->dreg = alloc_preg (cfg); ins->type = STACK_I4; MONO_ADD_INS (cfg->cbb, ins); } else { MONO_INST_NEW (cfg, ins, op_cmp); guint32 fcmp_dreg = ins->dreg = alloc_ireg (cfg); ins->sreg1 = args [0]->dreg; ins->sreg2 = args [1]->dreg; MONO_ADD_INS (cfg->cbb, ins); if (op_cmp == OP_FCLT_UN || op_cmp == OP_FCGT_UN || op_cmp == OP_RCLT_UN || op_cmp == OP_RCGT_UN) { /* we have to negate the result of this comparison: * - op_GreaterThanOrEqual maps to NOT x OP_FCLT_UN / OP_RCLT_UN * - op_LessThanOrEqual maps to NOT x OP_FCGT_UN / OP_RCGT_UN * * this matches generated bytecode by C# when doing the * same operations on float/double. the `_UN` suffix says * that if an operand is NaN, the result is true. If * OP_FCGE/OP_FCLE is used, it is mapped to instructions * on some architectures that don't detect NaN. For * example, on arm64 the condition `eq` doesn't respect * NaN results of a `fcmp` instruction. */ MONO_INST_NEW (cfg, ins, OP_ICOMPARE_IMM); ins->dreg = -1; ins->sreg1 = fcmp_dreg; ins->inst_imm = 0; MONO_ADD_INS (cfg->cbb, ins); MONO_INST_NEW (cfg, ins, OP_CEQ); ins->dreg = alloc_preg (cfg); ins->type = STACK_I4; MONO_ADD_INS (cfg->cbb, ins); } } return ins; } } return NULL; }
static MonoInst* emit_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args, const MagicTypeInfo *info) { int i = 0; const char *name = cmethod->name; MonoInst *ins; int type_index, stack_type; if (info->op_index == 2 && cfg->r4fp && SIZEOF_VOID_P == 4) { type_index = 3; stack_type = STACK_R4; } else { type_index = info->op_index; stack_type = info->stack_type; } if (!strcmp ("op_Implicit", name) || !strcmp ("op_Explicit", name)) { int source_size = type_size (cfg, fsig->params [0]); int dest_size = type_size (cfg, fsig->ret); switch (info->big_stack_type) { case STACK_I8: if (!is_int_type (fsig->params [0]) || !is_int_type (fsig->ret)) return NULL; break; case STACK_R8: if (!is_float_type (fsig->params [0]) || !is_float_type (fsig->ret)) return NULL; break; default: g_assert_not_reached (); } //4 -> 4 or 8 -> 8 if (source_size == dest_size) return args [0]; //4 -> 8 if (source_size < dest_size) return emit_widen (cfg, info, args [0]->dreg); //8 -> 4 return emit_narrow (cfg, info, args [0]->dreg); } if (!strcmp (".ctor", name)) { gboolean is_ldaddr = args [0]->opcode == OP_LDADDR; int arg0 = args [1]->dreg; int arg_size = type_size (cfg, fsig->params [0]); if (arg_size > SIZEOF_VOID_P) //8 -> 4 arg0 = emit_narrow (cfg, info, arg0)->dreg; else if (arg_size < SIZEOF_VOID_P) //4 -> 8 arg0 = emit_widen (cfg, info, arg0)->dreg; if (is_ldaddr) { /*Eliminate LDADDR if it's initing a local var*/ int dreg = ((MonoInst*)args [0]->inst_p0)->dreg; NULLIFY_INS (args [0]); EMIT_NEW_UNALU (cfg, ins, info->move, dreg, arg0); cfg->has_indirection = TRUE; } else { EMIT_NEW_STORE_MEMBASE (cfg, ins, info->store_op, args [0]->dreg, 0, arg0); } return ins; } if (!strcmp ("op_Increment", name) || !strcmp ("op_Decrement", name)) { gboolean inc = !strcmp ("op_Increment", name); /* FIXME float inc is too complex to bother with*/ //this is broken with ints too // if (!info->inc_op) return NULL; /* We have IR for inc/dec */ MONO_INST_NEW (cfg, ins, inc ? info->inc_op : info->dec_op); ins->dreg = alloc_dreg (cfg, info->stack_type); ins->sreg1 = args [0]->dreg; ins->inst_imm = 1; ins->type = info->stack_type; MONO_ADD_INS (cfg->cbb, ins); return ins; } for (i = 0; i < sizeof (int_binop) / sizeof (IntIntrisic); ++i) { if (!strcmp (int_binop [i].op_name, name)) { if (!int_binop [i].op_table [info->op_index]) return NULL; g_assert (int_binop [i].op_table [type_index]); MONO_INST_NEW (cfg, ins, int_binop [i].op_table [type_index]); ins->dreg = alloc_dreg (cfg, stack_type); ins->sreg1 = args [0]->dreg; ins->sreg2 = args [1]->dreg; ins->type = stack_type; MONO_ADD_INS (cfg->cbb, ins); return mono_decompose_opcode (cfg, ins); } } for (i = 0; i < sizeof (int_unnop) / sizeof (IntIntrisic); ++i) { if (!strcmp (int_unnop [i].op_name, name)) { g_assert (int_unnop [i].op_table [type_index]); MONO_INST_NEW (cfg, ins, int_unnop [i].op_table [type_index]); ins->dreg = alloc_dreg (cfg, stack_type); ins->sreg1 = args [0]->dreg; ins->type = stack_type; MONO_ADD_INS (cfg->cbb, ins); return ins; } } for (i = 0; i < sizeof (int_cmpop) / sizeof (IntIntrisic); ++i) { if (!strcmp (int_cmpop [i].op_name, name)) { g_assert (int_cmpop [i].op_table [type_index]); if (info->compare_op) { MONO_INST_NEW (cfg, ins, info->compare_op); ins->dreg = -1; ins->sreg1 = args [0]->dreg; ins->sreg2 = args [1]->dreg; MONO_ADD_INS (cfg->cbb, ins); MONO_INST_NEW (cfg, ins, int_cmpop [i].op_table [type_index]); ins->dreg = alloc_preg (cfg); ins->type = STACK_I4; MONO_ADD_INS (cfg->cbb, ins); } else { MONO_INST_NEW (cfg, ins, int_cmpop [i].op_table [type_index]); ins->dreg = alloc_ireg (cfg); ins->sreg1 = args [0]->dreg; ins->sreg2 = args [1]->dreg; MONO_ADD_INS (cfg->cbb, ins); } return ins; } } return NULL; }
/* * Process a BB removing bounds checks from array accesses. * It does the following (in sequence): * - Get the BB entry condition * - Add its relations to the relation graph in the evaluation area * - Process all the MonoInst trees in the BB * - Recursively process all the children BBs in the dominator tree * - Remove the relations previously added to the relation graph * * bb: the BB that must be processed * area: the current evaluation area (it contains the relation graph and * memory for all the evaluation contexts is already allocated) */ static void process_block (MonoCompile *cfg, MonoBasicBlock *bb, MonoVariableRelationsEvaluationArea *area) { int inst_index; MonoInst *ins; MonoAdditionalVariableRelationsForBB additional_relations; GSList *dominated_bb, *l; GSList *check_relations = NULL; if (TRACE_ABC_REMOVAL) { printf ("\nProcessing block %d [dfn %d]...\n", bb->block_num, bb->dfn); } if (bb->region != -1) return; get_relations_from_previous_bb (area, bb, &additional_relations); if (TRACE_ABC_REMOVAL) { if (additional_relations.relation1.relation.relation != MONO_ANY_RELATION) { printf ("Adding relation 1 on variable %d: ", additional_relations.relation1.variable); print_summarized_value_relation (&(additional_relations.relation1.relation)); printf ("\n"); } if (additional_relations.relation2.relation.relation != MONO_ANY_RELATION) { printf ("Adding relation 2 on variable %d: ", additional_relations.relation2.variable); print_summarized_value_relation (&(additional_relations.relation2.relation)); printf ("\n"); } } apply_change_to_evaluation_area (area, &(additional_relations.relation1)); apply_change_to_evaluation_area (area, &(additional_relations.relation2)); inst_index = 0; for (ins = bb->code; ins; ins = ins->next) { MonoAdditionalVariableRelation *rel; int array_var, index_var; if (TRACE_ABC_REMOVAL) { printf ("Processing instruction %d\n", inst_index); inst_index++; } if (ins->opcode == OP_BOUNDS_CHECK) { /* Handle OP_LDELEMA2D, too */ if (TRACE_ABC_REMOVAL) { printf ("Attempting check removal...\n"); } array_var = ins->sreg1; index_var = ins->sreg2; remove_abc_from_inst (ins, area); /* We can derive additional relations from the bounds check */ if (ins->opcode != OP_NOP) { rel = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoAdditionalVariableRelation)); rel->variable = index_var; rel->relation.relation = MONO_LT_RELATION; rel->relation.related_value.type = MONO_VARIABLE_SUMMARIZED_VALUE; rel->relation.related_value.value.variable.variable = array_var; rel->relation.related_value.value.variable.delta = 0; apply_change_to_evaluation_area (area, rel); check_relations = g_slist_append_mempool (cfg->mempool, check_relations, rel); rel = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoAdditionalVariableRelation)); rel->variable = index_var; rel->relation.relation = MONO_GE_RELATION; rel->relation.related_value.type = MONO_CONSTANT_SUMMARIZED_VALUE; rel->relation.related_value.value.constant.value = 0; apply_change_to_evaluation_area (area, rel); check_relations = g_slist_append_mempool (cfg->mempool, check_relations, rel); } } if (ins->opcode == OP_CHECK_THIS) { if (eval_non_null (area, ins->sreg1)) { if (REPORT_ABC_REMOVAL) printf ("ARRAY-ACCESS: removed check_this instruction.\n"); NULLIFY_INS (ins); } } if (ins->opcode == OP_NOT_NULL) add_non_null (area, cfg, ins->sreg1, &check_relations); /* * This doesn't work because LLVM can move the non-faulting loads before the faulting * ones (test_0_llvm_moving_faulting_loads ()). * FIXME: This also doesn't work because abcrem equates an array with its length, * so a = new int [100] implies a != null, but a = new int [0] doesn't. */ #if 0 /* * Eliminate MONO_INST_FAULT flags if possible. */ if (COMPILE_LLVM (cfg) && (ins->opcode == OP_LDLEN || ins->opcode == OP_BOUNDS_CHECK || ins->opcode == OP_STRLEN || (MONO_IS_LOAD_MEMBASE (ins) && (ins->flags & MONO_INST_FAULT)) || (MONO_IS_STORE_MEMBASE (ins) && (ins->flags & MONO_INST_FAULT)))) { int reg; if (MONO_IS_STORE_MEMBASE (ins)) reg = ins->inst_destbasereg; else if (MONO_IS_LOAD_MEMBASE (ins)) reg = ins->inst_basereg; else reg = ins->sreg1; if (eval_non_null (area, reg)) { if (REPORT_ABC_REMOVAL) printf ("ARRAY-ACCESS: removed MONO_INST_FAULT flag.\n"); ins->flags &= ~MONO_INST_FAULT; } else { add_non_null (area, cfg, reg, &check_relations); } } #endif } if (TRACE_ABC_REMOVAL) { printf ("Processing block %d [dfn %d] done.\n", bb->block_num, bb->dfn); } for (dominated_bb = bb->dominated; dominated_bb != NULL; dominated_bb = dominated_bb->next) { process_block (cfg, (MonoBasicBlock*) (dominated_bb->data), area); } for (l = check_relations; l; l = l->next) remove_change_from_evaluation_area (l->data); remove_change_from_evaluation_area (&(additional_relations.relation1)); remove_change_from_evaluation_area (&(additional_relations.relation2)); }
void mono_merge_basic_blocks (MonoCompile *cfg, MonoBasicBlock *bb, MonoBasicBlock *bbn) { MonoInst *inst; MonoBasicBlock *prev_bb; int i; bb->has_array_access |= bbn->has_array_access; bb->extended |= bbn->extended; mono_unlink_bblock (cfg, bb, bbn); for (i = 0; i < bbn->out_count; ++i) mono_link_bblock (cfg, bb, bbn->out_bb [i]); while (bbn->out_count) mono_unlink_bblock (cfg, bbn, bbn->out_bb [0]); /* Handle the branch at the end of the bb */ if (bb->has_call_handler) { for (inst = bb->code; inst != NULL; inst = inst->next) { if (inst->opcode == OP_CALL_HANDLER) { g_assert (inst->inst_target_bb == bbn); NULLIFY_INS (inst); } } } if (bb->has_jump_table) { 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); for (i = 0; i < table->table_size; i++ ) { /* Might be already NULL from a previous merge */ if (table->table [i]) g_assert (table->table [i] == bbn); table->table [i] = NULL; } /* Can't nullify this as later instructions depend on it */ } } } if (bb->last_ins && MONO_IS_COND_BRANCH_OP (bb->last_ins)) { g_assert (bb->last_ins->inst_false_bb == bbn); bb->last_ins->inst_false_bb = NULL; bb->extended = TRUE; } else if (bb->last_ins && MONO_IS_BRANCH_OP (bb->last_ins)) { NULLIFY_INS (bb->last_ins); } bb->has_call_handler |= bbn->has_call_handler; bb->has_jump_table |= bbn->has_jump_table; if (bb->last_ins) { if (bbn->code) { bb->last_ins->next = bbn->code; bbn->code->prev = bb->last_ins; bb->last_ins = bbn->last_ins; } } else { bb->code = bbn->code; bb->last_ins = bbn->last_ins; } 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 { /* bbn might not be in the bb list yet */ if (bb->next_bb == bbn) bb->next_bb = bbn->next_bb; } mono_nullify_basic_block (bbn); /* * If bbn fell through to its next bblock, have to add a branch, since bb * will not fall though to the same bblock (#513931). */ if (bb->last_ins && bb->out_count == 1 && bb->out_bb [0] != bb->next_bb && !MONO_IS_BRANCH_OP (bb->last_ins)) { MONO_INST_NEW (cfg, inst, OP_BR); inst->inst_target_bb = bb->out_bb [0]; MONO_ADD_INS (bb, inst); } }