break;
case STACK_PTR:
case STACK_MP:
+ case STACK_OBJ:
#if SIZEOF_VOID_P == 8
ins->opcode = OP_LCONV_TO_U;
#else
MonoInst *
mono_get_got_var (MonoCompile *cfg)
{
- if (!cfg->compile_aot || !cfg->backend->need_got_var)
+ if (!cfg->compile_aot || !cfg->backend->need_got_var || cfg->llvm_only)
return NULL;
if (!cfg->got_var) {
cfg->got_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_addr_reg, 0, prev_lmf_reg);
}
-static void
-emit_instrumentation_call (MonoCompile *cfg, void *func)
-{
- MonoInst *iargs [1];
-
- /*
- * Avoid instrumenting inlined methods since it can
- * distort profiling results.
- */
- if (cfg->method != cfg->current_method)
- return;
-
- if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE) {
- EMIT_NEW_METHODCONST (cfg, iargs [0], cfg->method);
- mono_emit_jit_icall (cfg, func, iargs);
- }
-}
-
static int
ret_type_to_call_opcode (MonoCompile *cfg, MonoType *type, int calli, int virt)
{
inline static MonoCallInst *
mono_emit_call_args (MonoCompile *cfg, MonoMethodSignature *sig,
- MonoInst **args, int calli, int virtual_, int tail, int rgctx, int unbox_trampoline)
+ MonoInst **args, int calli, int virtual_, int tail, int rgctx, int unbox_trampoline, MonoMethod *target)
{
MonoType *sig_ret;
MonoCallInst *call;
tail = FALSE;
if (tail) {
- emit_instrumentation_call (cfg, mono_profiler_method_leave);
+ mini_profiler_emit_tail_call (cfg, target);
MONO_INST_NEW_CALL (cfg, call, OP_TAILCALL);
} else
MONO_ADD_INS (cfg->cbb, ins);
}
- call = mono_emit_call_args (cfg, sig, args, TRUE, FALSE, FALSE, rgctx_arg ? TRUE : FALSE, FALSE);
+ call = mono_emit_call_args (cfg, sig, args, TRUE, FALSE, FALSE, rgctx_arg ? TRUE : FALSE, FALSE, NULL);
call->inst.sreg1 = addr->dreg;
need_unbox_trampoline = method->klass == mono_defaults.object_class || mono_class_is_interface (method->klass);
- call = mono_emit_call_args (cfg, sig, args, FALSE, virtual_, tail, rgctx_arg ? TRUE : FALSE, need_unbox_trampoline);
+ call = mono_emit_call_args (cfg, sig, args, FALSE, virtual_, tail, rgctx_arg ? TRUE : FALSE, need_unbox_trampoline, method);
#ifndef DISABLE_REMOTING
if (might_be_remote)
g_assert (sig);
- call = mono_emit_call_args (cfg, sig, args, FALSE, FALSE, FALSE, FALSE, FALSE);
+ call = mono_emit_call_args (cfg, sig, args, FALSE, FALSE, FALSE, FALSE, FALSE, NULL);
call->fptr = func;
MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
/* This happens often in argument checking code, eg. throw new FooException... */
/* Avoid relocations and save some space by calling a helper function specialized to mscorlib */
EMIT_NEW_ICONST (cfg, iargs [0], mono_metadata_token_index (klass->type_token));
- return mono_emit_jit_icall (cfg, mono_helper_newobj_mscorlib, iargs);
+ alloc_ftn = mono_helper_newobj_mscorlib;
} else {
MonoVTable *vtable = mono_class_vtable (cfg->domain, klass);
MonoMethod *managed_alloc = NULL;
- gboolean pass_lw;
if (!vtable) {
mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
EMIT_NEW_ICONST (cfg, iargs [1], size);
return mono_emit_method_call (cfg, managed_alloc, iargs, NULL);
}
- alloc_ftn = mono_class_get_allocation_ftn (vtable, for_box, &pass_lw);
- if (pass_lw) {
- guint32 lw = vtable->klass->instance_size;
- lw = ((lw + (sizeof (gpointer) - 1)) & ~(sizeof (gpointer) - 1)) / sizeof (gpointer);
- EMIT_NEW_ICONST (cfg, iargs [0], lw);
- EMIT_NEW_VTABLECONST (cfg, iargs [1], vtable);
- }
- else {
- EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
- }
+ alloc_ftn = ves_icall_object_new_specific;
+ EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
}
return mono_emit_jit_icall (cfg, alloc_ftn, iargs);
/* Set target field */
/* Optimize away setting of NULL target */
if (!MONO_INS_IS_PCONST_NULL (target)) {
+ MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, target->dreg, 0);
+ MONO_EMIT_NEW_COND_EXC (cfg, EQ, "NullReferenceException");
MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, target), target->dreg);
if (cfg->gen_write_barriers) {
dreg = alloc_preg (cfg);
if (g_list_find (cfg->dont_inline, method))
return FALSE;
+ if (mono_profiler_get_call_instrumentation_flags (method))
+ return FALSE;
+
return TRUE;
}
g_assert (ctx);
g_assert (ctx->method_inst);
g_assert (ctx->method_inst->type_argc == 1);
- MonoType *t = mini_get_underlying_type (ctx->method_inst->type_argv [0]);
- MonoClass *klass = mono_class_from_mono_type (t);
+ MonoType *arg_type = ctx->method_inst->type_argv [0];
+ MonoType *t;
+ MonoClass *klass;
ins = NULL;
+ /* Resolve the argument class as possible so we can handle common cases fast */
+ t = mini_get_underlying_type (arg_type);
+ klass = mono_class_from_mono_type (t);
mono_class_init (klass);
if (MONO_TYPE_IS_REFERENCE (t))
EMIT_NEW_ICONST (cfg, ins, 1);
else {
g_assert (cfg->gshared);
- int context_used = mini_class_check_context_used (cfg, klass);
+ /* Have to use the original argument class here */
+ MonoClass *arg_class = mono_class_from_mono_type (arg_type);
+ int context_used = mini_class_check_context_used (cfg, arg_class);
/* This returns 1 or 2 */
- MonoInst *info = mini_emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_CLASS_IS_REF_OR_CONTAINS_REFS);
+ MonoInst *info = mini_emit_get_rgctx_klass (cfg, context_used, arg_class, MONO_RGCTX_INFO_CLASS_IS_REF_OR_CONTAINS_REFS);
int dreg = alloc_ireg (cfg);
EMIT_NEW_BIALU_IMM (cfg, ins, OP_ISUB_IMM, dreg, info->dreg, 1);
}
{
if (method->klass == mono_defaults.string_class) {
/* managed string allocation support */
- if (strcmp (method->name, "InternalAllocateStr") == 0 && !(mono_profiler_events & MONO_PROFILE_ALLOCATIONS) && !(cfg->opt & MONO_OPT_SHARED)) {
+ if (strcmp (method->name, "InternalAllocateStr") == 0 && !(cfg->opt & MONO_OPT_SHARED)) {
MonoInst *iargs [2];
MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass);
MonoMethod *managed_alloc = NULL;
prev_args = cfg->args;
prev_arg_types = cfg->arg_types;
prev_inlined_method = cfg->inlined_method;
- cfg->inlined_method = cmethod;
- cfg->ret_var_set = FALSE;
- cfg->inline_depth ++;
+ prev_ret_var_set = cfg->ret_var_set;
prev_real_offset = cfg->real_offset;
prev_cbb_hash = cfg->cbb_hash;
prev_cil_offset_to_bb = cfg->cil_offset_to_bb;
prev_cbb = cfg->cbb;
prev_current_method = cfg->current_method;
prev_generic_context = cfg->generic_context;
- prev_ret_var_set = cfg->ret_var_set;
prev_disable_inline = cfg->disable_inline;
+ cfg->inlined_method = cmethod;
+ cfg->ret_var_set = FALSE;
+ cfg->inline_depth ++;
+
if (ip && *ip == CEE_CALLVIRT && !(cmethod->flags & METHOD_ATTRIBUTE_STATIC))
virtual_ = TRUE;
cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, header);
}
+static guint32
+mono_type_to_stloc_coerce (MonoType *type)
+{
+ if (type->byref)
+ return 0;
+
+ type = mini_get_underlying_type (type);
+handle_enum:
+ switch (type->type) {
+ case MONO_TYPE_I1:
+ return OP_ICONV_TO_I1;
+ case MONO_TYPE_U1:
+ return OP_ICONV_TO_U1;
+ case MONO_TYPE_I2:
+ return OP_ICONV_TO_I2;
+ case MONO_TYPE_U2:
+ return OP_ICONV_TO_U2;
+ case MONO_TYPE_I4:
+ case MONO_TYPE_U4:
+ case MONO_TYPE_I:
+ case MONO_TYPE_U:
+ case MONO_TYPE_PTR:
+ case MONO_TYPE_FNPTR:
+ case MONO_TYPE_CLASS:
+ case MONO_TYPE_STRING:
+ case MONO_TYPE_OBJECT:
+ case MONO_TYPE_SZARRAY:
+ case MONO_TYPE_ARRAY:
+ case MONO_TYPE_I8:
+ case MONO_TYPE_U8:
+ case MONO_TYPE_R4:
+ case MONO_TYPE_R8:
+ case MONO_TYPE_TYPEDBYREF:
+ case MONO_TYPE_GENERICINST:
+ return 0;
+ case MONO_TYPE_VALUETYPE:
+ if (type->data.klass->enumtype) {
+ type = mono_class_enum_basetype (type->data.klass);
+ goto handle_enum;
+ }
+ return 0;
+ case MONO_TYPE_VAR:
+ case MONO_TYPE_MVAR: //TODO I believe we don't need to handle gsharedvt as there won't be match and, for example, u1 is not covariant to u32
+ return 0;
+ default:
+ g_error ("unknown type 0x%02x in mono_type_to_stloc_coerce", type->type);
+ }
+ return -1;
+}
+
static void
emit_stloc_ir (MonoCompile *cfg, MonoInst **sp, MonoMethodHeader *header, int n)
{
MonoInst *ins;
+ guint32 coerce_op = mono_type_to_stloc_coerce (header->locals [n]);
+
+ if (coerce_op) {
+ if (cfg->cbb->last_ins == sp [0] && sp [0]->opcode == coerce_op) {
+ if (cfg->verbose_level > 2)
+ printf ("Found existing coercing is enough for stloc\n");
+ } else {
+ MONO_INST_NEW (cfg, ins, coerce_op);
+ ins->dreg = alloc_ireg (cfg);
+ ins->sreg1 = sp [0]->dreg;
+ ins->type = STACK_I4;
+ ins->klass = mono_class_from_mono_type (header->locals [n]);
+ MONO_ADD_INS (cfg->cbb, ins);
+ *sp = mono_decompose_opcode (cfg, ins);
+ }
+ }
+
+
guint32 opcode = mono_type_to_regmove (cfg, header->locals [n]);
if ((opcode == OP_MOVE) && cfg->cbb->last_ins == sp [0] &&
((sp [0]->opcode == OP_ICONST) || (sp [0]->opcode == OP_I8CONST))) {
}
}
+static void
+emit_starg_ir (MonoCompile *cfg, MonoInst **sp, int n)
+{
+ MonoInst *ins;
+ guint32 coerce_op = mono_type_to_stloc_coerce (cfg->arg_types [n]);
+
+ if (coerce_op) {
+ if (cfg->cbb->last_ins == sp [0] && sp [0]->opcode == coerce_op) {
+ if (cfg->verbose_level > 2)
+ printf ("Found existing coercing is enough for starg\n");
+ } else {
+ MONO_INST_NEW (cfg, ins, coerce_op);
+ ins->dreg = alloc_ireg (cfg);
+ ins->sreg1 = sp [0]->dreg;
+ ins->type = STACK_I4;
+ ins->klass = mono_class_from_mono_type (cfg->arg_types [n]);
+ MONO_ADD_INS (cfg->cbb, ins);
+ *sp = mono_decompose_opcode (cfg, ins);
+ }
+ }
+
+ EMIT_NEW_ARGSTORE (cfg, ins, n, *sp);
+}
+
/*
* ldloca inhibits many optimizations so try to get rid of it in common
* cases.
seq_points = FALSE;
}
- if (cfg->gen_sdb_seq_points && cfg->method == method) {
+ if (cfg->method == method)
+ cfg->coverage_info = mono_profiler_coverage_alloc (cfg->method, header->code_size);
+
+ if ((cfg->gen_sdb_seq_points && cfg->method == method) || cfg->coverage_info) {
minfo = mono_debug_lookup_method (method);
if (minfo) {
MonoSymSeqPoint *sps;
cfg->dont_inline = g_list_prepend (cfg->dont_inline, method);
if (cfg->method == method) {
-
- if (cfg->prof_options & MONO_PROFILE_INS_COVERAGE)
- cfg->coverage_info = mono_profiler_coverage_alloc (cfg->method, header->code_size);
-
/* ENTRY BLOCK */
NEW_BBLOCK (cfg, start_bblock);
cfg->bb_entry = start_bblock;
tblock->real_offset = clause->handler_offset;
tblock->flags |= BB_EXCEPTION_HANDLER;
+ if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY)
+ mono_create_exvar_for_offset (cfg, clause->handler_offset);
/*
* Linking the try block with the EH block hinders inlining as we won't be able to
* merge the bblocks from inlining and produce an artificial hole for no good reason.
if (sym_seq_points)
mono_bitset_set_fast (seq_point_set_locs, ip - header->code);
- }
- cfg->cbb->real_offset = cfg->real_offset;
+ if ((cfg->method == method) && cfg->coverage_info) {
+ guint32 cil_offset = ip - header->code;
+ gpointer counter = &cfg->coverage_info->data [cil_offset].count;
+ cfg->coverage_info->data [cil_offset].cil_code = ip;
- if ((cfg->method == method) && cfg->coverage_info) {
- guint32 cil_offset = ip - header->code;
- cfg->coverage_info->data [cil_offset].cil_code = ip;
+ if (mono_arch_opcode_supported (OP_ATOMIC_ADD_I4)) {
+ MonoInst *one_ins, *load_ins;
- /* TODO: Use an increment here */
-#if defined(TARGET_X86)
- MONO_INST_NEW (cfg, ins, OP_STORE_MEM_IMM);
- ins->inst_p0 = &(cfg->coverage_info->data [cil_offset].count);
- ins->inst_imm = 1;
- MONO_ADD_INS (cfg->cbb, ins);
-#else
- EMIT_NEW_PCONST (cfg, ins, &(cfg->coverage_info->data [cil_offset].count));
- MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, ins->dreg, 0, 1);
-#endif
+ EMIT_NEW_PCONST (cfg, load_ins, counter);
+ EMIT_NEW_ICONST (cfg, one_ins, 1);
+ MONO_INST_NEW (cfg, ins, OP_ATOMIC_ADD_I4);
+ ins->dreg = mono_alloc_ireg (cfg);
+ ins->inst_basereg = load_ins->dreg;
+ ins->inst_offset = 0;
+ ins->sreg2 = one_ins->dreg;
+ ins->type = STACK_I4;
+ MONO_ADD_INS (cfg->cbb, ins);
+ } else {
+ EMIT_NEW_PCONST (cfg, ins, counter);
+ MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, ins->dreg, 0, 1);
+ }
+ }
}
+ cfg->cbb->real_offset = cfg->real_offset;
+
if (cfg->verbose_level > 3)
printf ("converting (in B%d: stack: %d) %s", cfg->cbb->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
CHECK_ARG (n);
if (!dont_verify_stloc && target_type_is_incompatible (cfg, param_types [ip [1]], *sp))
UNVERIFIED;
- EMIT_NEW_ARGSTORE (cfg, ins, n, *sp);
+ emit_starg_ir (cfg, sp, n);
ip += 2;
break;
case CEE_LDLOC_S:
CHECK_STACK_OVF (1);
n = ip [1];
CHECK_LOCAL (n);
- EMIT_NEW_LOCLOAD (cfg, ins, n);
+ if ((ip [2] == CEE_LDFLD) && ip_in_bb (cfg, cfg->cbb, ip + 2) && MONO_TYPE_ISSTRUCT (header->locals [n])) {
+ /* Avoid loading a struct just to load one of its fields */
+ EMIT_NEW_LOCLOADA (cfg, ins, n);
+ } else {
+ EMIT_NEW_LOCLOAD (cfg, ins, n);
+ }
*sp++ = ins;
ip += 2;
break;
if (cfg->gshared && mono_method_check_context_used (cmethod))
GENERIC_SHARING_FAILURE (CEE_JMP);
- emit_instrumentation_call (cfg, mono_profiler_method_leave);
+ mini_profiler_emit_tail_call (cfg, cmethod);
fsig = mono_method_signature (cmethod);
n = fsig->param_count + fsig->hasthis;
}
for (i = 0; i < n; ++i)
EMIT_NEW_ARGSTORE (cfg, ins, i, sp [i]);
+
+ mini_profiler_emit_tail_call (cfg, cmethod);
+
MONO_INST_NEW (cfg, ins, OP_BR);
MONO_ADD_INS (cfg->cbb, ins);
tblock = start_bblock->out_bb [0];
/* Handle tail calls similarly to normal calls */
tail_call = TRUE;
} else {
- emit_instrumentation_call (cfg, mono_profiler_method_leave);
+ mini_profiler_emit_tail_call (cfg, cmethod);
MONO_INST_NEW_CALL (cfg, call, OP_JMP);
call->tail_call = TRUE;
break;
}
case CEE_RET:
+ mini_profiler_emit_leave (cfg, sig->ret->type != MONO_TYPE_VOID ? sp [-1] : NULL);
+
if (cfg->method != method) {
/* return from inlined method */
/*
cfg->ret_var_set = TRUE;
}
} else {
- emit_instrumentation_call (cfg, mono_profiler_method_leave);
-
if (cfg->lmf_var && cfg->cbb->in_count && !cfg->llvm_only)
emit_pop_lmf (cfg);
if ((handlers = mono_find_final_block (cfg, ip, target, MONO_EXCEPTION_CLAUSE_FINALLY))) {
GList *tmp;
- MonoExceptionClause *clause;
for (tmp = handlers; tmp; tmp = tmp->next) {
- clause = (MonoExceptionClause *)tmp->data;
+ MonoExceptionClause *clause = (MonoExceptionClause *)tmp->data;
+ MonoInst *abort_exc = (MonoInst *)mono_find_exvar_for_offset (cfg, clause->handler_offset);
+ MonoBasicBlock *dont_throw;
+
tblock = cfg->cil_offset_to_bb [clause->handler_offset];
g_assert (tblock);
link_bblock (cfg, cfg->cbb, tblock);
+
+ MONO_EMIT_NEW_PCONST (cfg, abort_exc->dreg, 0);
+
MONO_INST_NEW (cfg, ins, OP_CALL_HANDLER);
ins->inst_target_bb = tblock;
ins->inst_eh_block = clause;
MONO_ADD_INS (cfg->cbb, ins);
cfg->cbb->has_call_handler = 1;
+
+ /* Throw exception if exvar is set */
+ /* FIXME Do we need this for calls from catch/filter ? */
+ NEW_BBLOCK (cfg, dont_throw);
+ MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, abort_exc->dreg, 0);
+ MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, dont_throw);
+ mono_emit_jit_icall (cfg, mono_thread_self_abort, NULL);
+ cfg->cbb->clause_hole = clause;
+
+ MONO_START_BB (cfg, dont_throw);
+ cfg->cbb->clause_hole = clause;
+
if (COMPILE_LLVM (cfg)) {
MonoBasicBlock *target_bb;
case CEE_MONO_LDPTR_CARD_TABLE:
case CEE_MONO_LDPTR_NURSERY_START:
case CEE_MONO_LDPTR_NURSERY_BITS:
- case CEE_MONO_LDPTR_INT_REQ_FLAG: {
+ case CEE_MONO_LDPTR_INT_REQ_FLAG:
+ case CEE_MONO_LDPTR_PROFILER_ALLOCATION_COUNT: {
CHECK_STACK_OVF (1);
switch (ip [1]) {
case CEE_MONO_LDPTR_INT_REQ_FLAG:
ins = mini_emit_runtime_constant (cfg, MONO_PATCH_INFO_INTERRUPTION_REQUEST_FLAG, NULL);
break;
+ case CEE_MONO_LDPTR_PROFILER_ALLOCATION_COUNT:
+ ins = mini_emit_runtime_constant (cfg, MONO_PATCH_INFO_PROFILER_ALLOCATION_COUNT, NULL);
+ break;
default:
g_assert_not_reached ();
break;
if (sp != stack_start)
UNVERIFIED;
+ mini_profiler_emit_leave (cfg, sp [0]);
+
MONO_INST_NEW (cfg, ins, OP_BR);
ins->inst_target_bb = end_bblock;
MONO_ADD_INS (cfg->cbb, ins);
cfg->dyn_call_var->flags |= MONO_INST_VOLATILE;
}
- /* Has to use a call inst since it local regalloc expects it */
+ /* Has to use a call inst since local regalloc expects it */
MONO_INST_NEW_CALL (cfg, call, OP_DYN_CALL);
ins = (MonoInst*)call;
sp -= 2;
MONO_ADD_INS (cfg->cbb, ins);
cfg->param_area = MAX (cfg->param_area, cfg->backend->dyn_call_param_area);
+ /* OP_DYN_CALL might need to allocate a dynamically sized param area */
+ cfg->flags |= MONO_CFG_HAS_ALLOCA;
ip += 2;
inline_costs += 10 * num_calls++;
CHECK_ARG (n);
if (!dont_verify_stloc && target_type_is_incompatible (cfg, param_types [n], *sp))
UNVERIFIED;
- EMIT_NEW_ARGSTORE (cfg, ins, n, *sp);
+ emit_starg_ir (cfg, sp, n);
ip += 4;
break;
case CEE_LDLOC:
CHECK_OPSIZE (4);
n = read16 (ip + 2);
CHECK_LOCAL (n);
- EMIT_NEW_LOCLOAD (cfg, ins, n);
+ if ((ip [4] == CEE_LDFLD) && ip_in_bb (cfg, cfg->cbb, ip + 4) && header->locals [n]->type == MONO_TYPE_VALUETYPE) {
+ /* Avoid loading a struct just to load one of its fields */
+ EMIT_NEW_LOCLOADA (cfg, ins, n);
+ } else {
+ EMIT_NEW_LOCLOAD (cfg, ins, n);
+ }
*sp++ = ins;
ip += 4;
break;
}
cfg->cbb = init_localsbb;
- emit_instrumentation_call (cfg, mono_profiler_method_enter);
+ mini_profiler_emit_enter (cfg);
if (seq_points) {
MonoBasicBlock *bb;