#include <alloca.h>
#endif
-#ifdef HAVE_VALGRIND_MEMCHECK_H
-#include <valgrind/memcheck.h>
-#endif
+#include <mono/utils/memcheck.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/loader.h>
#include <mono/metadata/threads-types.h>
#include <mono/metadata/security-core-clr.h>
#include <mono/metadata/monitor.h>
+#include <mono/metadata/profiler-private.h>
+#include <mono/metadata/profiler.h>
#include <mono/utils/mono-compiler.h>
+#include <mono/metadata/mono-basic-block.h>
#include "mini.h"
#include "trace.h"
#include "ir-emit.h"
#include "jit-icalls.h"
+#include "jit.h"
+#include "debugger-agent.h"
#define BRANCH_COST 100
#define INLINE_LENGTH_LIMIT 20
extern MonoMethodSignature *helper_sig_generic_class_init_trampoline;
extern MonoMethodSignature *helper_sig_rgctx_lazy_fetch_trampoline;
extern MonoMethodSignature *helper_sig_monitor_enter_exit_trampoline;
+extern MonoMethodSignature *helper_sig_monitor_enter_exit_trampoline_llvm;
/*
* Instruction metadata
#undef MINI_OP
#undef MINI_OP3
-extern GHashTable *jit_icall_name_hash;
-
#define MONO_INIT_VARINFO(vi,id) do { \
(vi)->range.first_use.pos.bid = 0xffff; \
(vi)->reg = -1; \
static int
mono_find_block_region (MonoCompile *cfg, int offset)
{
- MonoMethod *method = cfg->method;
- MonoMethodHeader *header = mono_method_get_header (method);
+ MonoMethodHeader *header = cfg->header;
MonoExceptionClause *clause;
int i;
static GList*
mono_find_final_block (MonoCompile *cfg, unsigned char *ip, unsigned char *target, int type)
{
- MonoMethod *method = cfg->method;
- MonoMethodHeader *header = mono_method_get_header (method);
+ MonoMethodHeader *header = cfg->header;
MonoExceptionClause *clause;
- MonoBasicBlock *handler;
int i;
GList *res = NULL;
clause = &header->clauses [i];
if (MONO_OFFSET_IN_CLAUSE (clause, (ip - header->code)) &&
(!MONO_OFFSET_IN_CLAUSE (clause, (target - header->code)))) {
- if (clause->flags == type) {
- handler = cfg->cil_offset_to_bb [clause->handler_offset];
- g_assert (handler);
- res = g_list_append (res, handler);
- }
+ if (clause->flags == type)
+ res = g_list_append (res, clause);
}
}
return res;
g_hash_table_insert (cfg->spvars, GINT_TO_POINTER (region), var);
}
-static MonoInst *
+MonoInst *
mono_find_exvar_for_offset (MonoCompile *cfg, int offset)
{
return g_hash_table_lookup (cfg->exvars, GINT_TO_POINTER (offset));
static G_GNUC_UNUSED int
type_to_stack_type (MonoType *t)
{
- switch (mono_type_get_underlying_type (t)->type) {
+ t = mono_type_get_underlying_type (t);
+ switch (t->type) {
case MONO_TYPE_I1:
case MONO_TYPE_U1:
case MONO_TYPE_BOOLEAN:
int pos, vnum;
/* inlining can result in deeper stacks */
- if (slot >= mono_method_get_header (cfg->method)->max_stack)
+ if (slot >= cfg->header->max_stack)
return mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
pos = ins->type - 1 + slot * STACK_MAX;
}
}
-/*
- * Emit code which loads into "intf_bit_reg" a nonzero value if the MonoClass
- * stored in "klass_reg" implements the interface "klass".
- */
static void
-mini_emit_load_intf_bit_reg_class (MonoCompile *cfg, int intf_bit_reg, int klass_reg, MonoClass *klass)
+mini_emit_interface_bitmap_check (MonoCompile *cfg, int intf_bit_reg, int base_reg, int offset, MonoClass *klass)
{
int ibitmap_reg = alloc_preg (cfg);
+#ifdef COMPRESSED_INTERFACE_BITMAP
+ MonoInst *args [2];
+ MonoInst *res, *ins;
+ NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, ibitmap_reg, base_reg, offset);
+ MONO_ADD_INS (cfg->cbb, ins);
+ args [0] = ins;
+ if (cfg->compile_aot)
+ EMIT_NEW_AOTCONST (cfg, args [1], MONO_PATCH_INFO_IID, klass);
+ else
+ EMIT_NEW_ICONST (cfg, args [1], klass->interface_id);
+ res = mono_emit_jit_icall (cfg, mono_class_interface_match, args);
+ MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, intf_bit_reg, res->dreg);
+#else
int ibitmap_byte_reg = alloc_preg (cfg);
- MONO_EMIT_NEW_LOAD_MEMBASE (cfg, ibitmap_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, interface_bitmap));
+ MONO_EMIT_NEW_LOAD_MEMBASE (cfg, ibitmap_reg, base_reg, offset);
if (cfg->compile_aot) {
int iid_reg = alloc_preg (cfg);
MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, ibitmap_byte_reg, ibitmap_reg, klass->interface_id >> 3);
MONO_EMIT_NEW_BIALU_IMM (cfg, OP_AND_IMM, intf_bit_reg, ibitmap_byte_reg, 1 << (klass->interface_id & 7));
}
+#endif
+}
+
+/*
+ * Emit code which loads into "intf_bit_reg" a nonzero value if the MonoClass
+ * stored in "klass_reg" implements the interface "klass".
+ */
+static void
+mini_emit_load_intf_bit_reg_class (MonoCompile *cfg, int intf_bit_reg, int klass_reg, MonoClass *klass)
+{
+ mini_emit_interface_bitmap_check (cfg, intf_bit_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, interface_bitmap), klass);
}
/*
static void
mini_emit_load_intf_bit_reg_vtable (MonoCompile *cfg, int intf_bit_reg, int vtable_reg, MonoClass *klass)
{
- int ibitmap_reg = alloc_preg (cfg);
- int ibitmap_byte_reg = alloc_preg (cfg);
-
- MONO_EMIT_NEW_LOAD_MEMBASE (cfg, ibitmap_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, interface_bitmap));
-
- if (cfg->compile_aot) {
- int iid_reg = alloc_preg (cfg);
- int shifted_iid_reg = alloc_preg (cfg);
- int ibitmap_byte_address_reg = alloc_preg (cfg);
- int masked_iid_reg = alloc_preg (cfg);
- int iid_one_bit_reg = alloc_preg (cfg);
- int iid_bit_reg = alloc_preg (cfg);
- MONO_EMIT_NEW_AOTCONST (cfg, iid_reg, klass, MONO_PATCH_INFO_IID);
- MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISHR_IMM, shifted_iid_reg, iid_reg, 3);
- MONO_EMIT_NEW_BIALU (cfg, OP_PADD, ibitmap_byte_address_reg, ibitmap_reg, shifted_iid_reg);
- MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, ibitmap_byte_reg, ibitmap_byte_address_reg, 0);
- MONO_EMIT_NEW_BIALU_IMM (cfg, OP_AND_IMM, masked_iid_reg, iid_reg, 7);
- MONO_EMIT_NEW_ICONST (cfg, iid_one_bit_reg, 1);
- MONO_EMIT_NEW_BIALU (cfg, OP_ISHL, iid_bit_reg, iid_one_bit_reg, masked_iid_reg);
- MONO_EMIT_NEW_BIALU (cfg, OP_IAND, intf_bit_reg, ibitmap_byte_reg, iid_bit_reg);
- } else {
- MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, ibitmap_byte_reg, ibitmap_reg, klass->interface_id >> 3);
- MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, intf_bit_reg, ibitmap_byte_reg, 1 << (klass->interface_id & 7));
- }
+ mini_emit_interface_bitmap_check (cfg, intf_bit_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, interface_bitmap), klass);
}
/*
mini_emit_max_iid_check (cfg, max_iid_reg, klass, false_target);
}
-static void
-mini_emit_isninst_cast (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
+static void
+mini_emit_isninst_cast_inst (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoInst *klass_ins, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
{
int idepth_reg = alloc_preg (cfg);
int stypes_reg = alloc_preg (cfg);
}
MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stypes_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, supertypes));
MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stype, stypes_reg, ((klass->idepth - 1) * SIZEOF_VOID_P));
- if (cfg->compile_aot) {
+ if (klass_ins) {
+ MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, stype, klass_ins->dreg);
+ } else if (cfg->compile_aot) {
int const_reg = alloc_preg (cfg);
MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, stype, const_reg);
MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, true_target);
}
-static void
+static void
+mini_emit_isninst_cast (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
+{
+ mini_emit_isninst_cast_inst (cfg, klass_reg, klass, NULL, false_target, true_target);
+}
+
+static void
mini_emit_iface_cast (MonoCompile *cfg, int vtable_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
{
int intf_reg = alloc_preg (cfg);
/*
* Variant of the above that takes a register to the class, not the vtable.
*/
-static void
+static void
mini_emit_iface_class_cast (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
{
int intf_bit_reg = alloc_preg (cfg);
}
static inline void
-mini_emit_class_check (MonoCompile *cfg, int klass_reg, MonoClass *klass)
+mini_emit_class_check_inst (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoInst *klass_inst)
{
- if (cfg->compile_aot) {
+ if (klass_inst) {
+ MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, klass_inst->dreg);
+ } else if (cfg->compile_aot) {
int const_reg = alloc_preg (cfg);
MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, const_reg);
MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
}
+static inline void
+mini_emit_class_check (MonoCompile *cfg, int klass_reg, MonoClass *klass)
+{
+ return mini_emit_class_check_inst (cfg, klass_reg, klass, NULL);
+}
+
static inline void
mini_emit_class_check_branch (MonoCompile *cfg, int klass_reg, MonoClass *klass, int branch_op, MonoBasicBlock *target)
{
}
MONO_EMIT_NEW_BRANCH_BLOCK (cfg, branch_op, target);
}
+
+static void
+mini_emit_castclass (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoBasicBlock *object_is_null);
-static void
-mini_emit_castclass (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoBasicBlock *object_is_null)
+static void
+mini_emit_castclass_inst (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoInst *klass_inst, MonoBasicBlock *object_is_null)
{
if (klass->rank) {
int rank_reg = alloc_preg (cfg);
int eclass_reg = alloc_preg (cfg);
+ g_assert (!klass_inst);
MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, rank));
MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, klass->rank);
MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
}
MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stypes_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, supertypes));
MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stype, stypes_reg, ((klass->idepth - 1) * SIZEOF_VOID_P));
- mini_emit_class_check (cfg, stype, klass);
+ mini_emit_class_check_inst (cfg, stype, klass, klass_inst);
}
}
+static void
+mini_emit_castclass (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoBasicBlock *object_is_null)
+{
+ return mini_emit_castclass_inst (cfg, obj_reg, klass_reg, klass, NULL, object_is_null);
+}
+
static void
mini_emit_memset (MonoCompile *cfg, int destreg, int offset, int size, int val, int align)
{
if (align == 0)
align = 4;
+ /*FIXME arbitrary hack to avoid unbound code expansion.*/
+ g_assert (size < 10000);
+
if (align < 4) {
/* This could be optimized further if neccesary */
while (size >= 1) {
return ji;
}
-inline static MonoInst*
-mono_emit_jit_icall (MonoCompile *cfg, gconstpointer func, MonoInst **args);
-
inline static MonoCallInst *
mono_emit_call_args (MonoCompile *cfg, MonoMethodSignature *sig,
MonoInst **args, int calli, int virtual, int tail)
call->inst.dreg = alloc_dreg (cfg, call->inst.type);
#ifdef MONO_ARCH_SOFT_FLOAT
- /*
- * If the call has a float argument, we would need to do an r8->r4 conversion using
- * an icall, but that cannot be done during the call sequence since it would clobber
- * the call registers + the stack. So we do it before emitting the call.
- */
- for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
- MonoType *t;
- MonoInst *in = call->args [i];
+ if (COMPILE_SOFT_FLOAT (cfg)) {
+ /*
+ * If the call has a float argument, we would need to do an r8->r4 conversion using
+ * an icall, but that cannot be done during the call sequence since it would clobber
+ * the call registers + the stack. So we do it before emitting the call.
+ */
+ for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
+ MonoType *t;
+ MonoInst *in = call->args [i];
- if (i >= sig->hasthis)
- t = sig->params [i - sig->hasthis];
- else
- t = &mono_defaults.int_class->byval_arg;
- t = mono_type_get_underlying_type (t);
+ if (i >= sig->hasthis)
+ t = sig->params [i - sig->hasthis];
+ else
+ t = &mono_defaults.int_class->byval_arg;
+ t = mono_type_get_underlying_type (t);
- if (!t->byref && t->type == MONO_TYPE_R4) {
- MonoInst *iargs [1];
- MonoInst *conv;
+ if (!t->byref && t->type == MONO_TYPE_R4) {
+ MonoInst *iargs [1];
+ MonoInst *conv;
- iargs [0] = in;
- conv = mono_emit_jit_icall (cfg, mono_fload_r4_arg, iargs);
+ iargs [0] = in;
+ conv = mono_emit_jit_icall (cfg, mono_fload_r4_arg, iargs);
- /* The result will be in an int vreg */
- call->args [i] = conv;
+ /* The result will be in an int vreg */
+ call->args [i] = conv;
+ }
}
}
#endif
#ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
if ((method->klass->parent == mono_defaults.multicastdelegate_class) && (!strcmp (method->name, "Invoke"))) {
+ MONO_EMIT_NULL_CHECK (cfg, this_reg);
+
/* Make a call to delegate->invoke_impl */
call->inst.opcode = callvirt_to_call_membase (call->inst.opcode);
call->inst.inst_basereg = this_reg;
if ((!cfg->compile_aot || enable_for_aot) &&
(!(method->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
(MONO_METHOD_IS_FINAL (method) &&
- method->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK))) {
+ method->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK)) &&
+ !(method->klass->marshalbyref && context_used)) {
/*
* the method is not virtual, we just need to ensure this is not null
* and then we can call the method directly.
*/
if (method->klass->marshalbyref || method->klass == mono_defaults.object_class) {
+ /*
+ * The check above ensures method is not gshared, this is needed since
+ * gshared methods can't have wrappers.
+ */
method = call->method = mono_marshal_get_remoting_invoke_with_check (method);
}
- if (!method->string_ctor) {
- cfg->flags |= MONO_CFG_HAS_CHECK_THIS;
- MONO_EMIT_NEW_UNALU (cfg, OP_CHECK_THIS, -1, this_reg);
- MONO_EMIT_NEW_UNALU (cfg, OP_NOT_NULL, -1, this_reg);
- }
+ if (!method->string_ctor)
+ MONO_EMIT_NEW_CHECK_THIS (cfg, this_reg);
call->inst.opcode = callvirt_to_call (call->inst.opcode);
* it's class or the method itself are sealed.
* But first we need to ensure it's not a null reference.
*/
- cfg->flags |= MONO_CFG_HAS_CHECK_THIS;
- MONO_EMIT_NEW_UNALU (cfg, OP_CHECK_THIS, -1, this_reg);
- MONO_EMIT_NEW_UNALU (cfg, OP_NOT_NULL, -1, this_reg);
+ MONO_EMIT_NEW_CHECK_THIS (cfg, this_reg);
call->inst.opcode = callvirt_to_call (call->inst.opcode);
MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
call->inst.opcode = callvirt_to_call_membase (call->inst.opcode);
vtable_reg = alloc_preg (cfg);
- MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, this_reg, G_STRUCT_OFFSET (MonoObject, vtable));
+ MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, this_reg, G_STRUCT_OFFSET (MonoObject, vtable));
if (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
slot_reg = -1;
#ifdef MONO_ARCH_HAVE_IMT
} else {
slot_reg = vtable_reg;
call->inst.inst_offset = G_STRUCT_OFFSET (MonoVTable, vtable) +
- (mono_method_get_vtable_index (method) * SIZEOF_VOID_P);
+ ((mono_method_get_vtable_index (method)) * (SIZEOF_VOID_P));
#ifdef MONO_ARCH_HAVE_IMT
if (imt_arg) {
g_assert (mono_method_signature (method)->generic_param_count);
mono_emit_rgctx_method_call_full (MonoCompile *cfg, MonoMethod *method, MonoMethodSignature *sig,
MonoInst **args, MonoInst *this, MonoInst *imt_arg, MonoInst *vtable_arg)
{
+#ifdef MONO_ARCH_RGCTX_REG
int rgctx_reg = 0;
+#endif
MonoInst *ins;
MonoCallInst *call;
return ins;
}
-static inline MonoInst*
+MonoInst*
mono_emit_method_call (MonoCompile *cfg, MonoMethod *method, MonoInst **args, MonoInst *this)
{
return mono_emit_method_call_full (cfg, method, mono_method_signature (method), args, this, NULL);
return (MonoInst*)call;
}
-inline static MonoInst*
+MonoInst*
mono_emit_jit_icall (MonoCompile *cfg, gconstpointer func, MonoInst **args)
{
MonoJitICallInfo *info = mono_find_jit_icall_by_addr (func);
if (context_used) {
iargs [2] = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
} else {
- EMIT_NEW_PCONST (cfg, iargs [2], klass);
- mono_class_compute_gc_descriptor (klass);
+ if (cfg->compile_aot) {
+ EMIT_NEW_CLASSCONST (cfg, iargs [2], klass);
+ } else {
+ EMIT_NEW_PCONST (cfg, iargs [2], klass);
+ mono_class_compute_gc_descriptor (klass);
+ }
}
+ /* FIXME: this does the memcpy as well (or
+ should), so we don't need the memcpy
+ afterwards */
mono_emit_jit_icall (cfg, mono_value_copy, iargs);
}
}
return emit_rgctx_fetch (cfg, rgctx, entry);
}
+/*
+ * emit_get_rgctx_method:
+ *
+ * Emit IR to load the property RGCTX_TYPE of CMETHOD. If context_used is 0, emit
+ * normal constants, else emit a load from the rgctx.
+ */
static MonoInst*
emit_get_rgctx_method (MonoCompile *cfg, int context_used,
MonoMethod *cmethod, int rgctx_type)
{
- MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_METHODCONST, cmethod, rgctx_type);
- MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
+ if (!context_used) {
+ MonoInst *ins;
- return emit_rgctx_fetch (cfg, rgctx, entry);
+ switch (rgctx_type) {
+ case MONO_RGCTX_INFO_METHOD:
+ EMIT_NEW_METHODCONST (cfg, ins, cmethod);
+ return ins;
+ case MONO_RGCTX_INFO_METHOD_RGCTX:
+ EMIT_NEW_METHOD_RGCTX_CONST (cfg, ins, cmethod);
+ return ins;
+ default:
+ g_assert_not_reached ();
+ }
+ } else {
+ MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_METHODCONST, cmethod, rgctx_type);
+ MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
+
+ return emit_rgctx_fetch (cfg, rgctx, entry);
+ }
}
static MonoInst*
int rank_reg = alloc_dreg (cfg ,STACK_I4);
obj_reg = sp [0]->dreg;
- MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, G_STRUCT_OFFSET (MonoObject, vtable));
+ MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, obj_reg, G_STRUCT_OFFSET (MonoObject, vtable));
MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, rank));
/* FIXME: generics */
}
}
+// FIXME: This doesn't work yet (class libs tests fail?)
+#define is_complex_isinst(klass) (TRUE || (klass->flags & TYPE_ATTRIBUTE_INTERFACE) || klass->rank || mono_class_is_nullable (klass) || klass->marshalbyref || (klass->flags & TYPE_ATTRIBUTE_SEALED) || mono_class_has_variant_generic_params (klass) || klass->byval_arg.type == MONO_TYPE_VAR || klass->byval_arg.type == MONO_TYPE_MVAR)
+
/*
* Returns NULL and set the cfg exception on error.
*/
static MonoInst*
-handle_castclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src)
+handle_castclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src, int context_used)
{
MonoBasicBlock *is_null_bb;
int obj_reg = src->dreg;
int vtable_reg = alloc_preg (cfg);
+ MonoInst *klass_inst = NULL;
+
+ if (context_used) {
+ MonoInst *args [2];
+
+ klass_inst = emit_get_rgctx_klass (cfg, context_used,
+ klass, MONO_RGCTX_INFO_KLASS);
+
+ if (is_complex_isinst (klass)) {
+ /* Complex case, handle by an icall */
+
+ /* obj */
+ args [0] = src;
+
+ /* klass */
+ args [1] = klass_inst;
+
+ return mono_emit_jit_icall (cfg, mono_object_castclass, args);
+ } else {
+ /* Simple case, handled by the code below */
+ }
+ }
NEW_BBLOCK (cfg, is_null_bb);
MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
} else {
MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, klass));
- mini_emit_castclass (cfg, obj_reg, klass_reg, klass, is_null_bb);
+ mini_emit_castclass_inst (cfg, obj_reg, klass_reg, klass, klass_inst, is_null_bb);
}
}
* Returns NULL and set the cfg exception on error.
*/
static MonoInst*
-handle_isinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src)
+handle_isinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src, int context_used)
{
MonoInst *ins;
MonoBasicBlock *is_null_bb, *false_bb, *end_bb;
int obj_reg = src->dreg;
int vtable_reg = alloc_preg (cfg);
int res_reg = alloc_preg (cfg);
+ MonoInst *klass_inst = NULL;
+
+ if (context_used) {
+ klass_inst = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
+
+ if (is_complex_isinst (klass)) {
+ MonoInst *args [2];
+
+ /* Complex case, handle by an icall */
+
+ /* obj */
+ args [0] = src;
+
+ /* klass */
+ args [1] = klass_inst;
+
+ return mono_emit_jit_icall (cfg, mono_object_isinst, args);
+ } else {
+ /* Simple case, the code below can handle it */
+ }
+ }
NEW_BBLOCK (cfg, is_null_bb);
NEW_BBLOCK (cfg, false_bb);
MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_null_bb);
+ MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, G_STRUCT_OFFSET (MonoObject, vtable));
+
if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
- MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, G_STRUCT_OFFSET (MonoObject, vtable));
+ g_assert (!context_used);
/* the is_null_bb target simply copies the input register to the output */
mini_emit_iface_cast (cfg, vtable_reg, klass, false_bb, is_null_bb);
} else {
int klass_reg = alloc_preg (cfg);
- MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, G_STRUCT_OFFSET (MonoObject, vtable));
-
if (klass->rank) {
int rank_reg = alloc_preg (cfg);
int eclass_reg = alloc_preg (cfg);
+ g_assert (!context_used);
MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, rank));
MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, klass->rank);
MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb);
mini_emit_isninst_cast (cfg, eclass_reg, klass->cast_class, false_bb, is_null_bb);
}
} else if (mono_class_is_nullable (klass)) {
+ g_assert (!context_used);
MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, klass));
/* the is_null_bb target simply copies the input register to the output */
mini_emit_isninst_cast (cfg, klass_reg, klass->cast_class, false_bb, is_null_bb);
} else {
if (!cfg->compile_aot && !(cfg->opt & MONO_OPT_SHARED) && (klass->flags & TYPE_ATTRIBUTE_SEALED)) {
+ g_assert (!context_used);
/* the remoting code is broken, access the class for now */
if (0) {/*FIXME what exactly is broken? This change refers to r39380 from 2005 and mention some remoting fixes were due.*/
MonoVTable *vt = mono_class_vtable (cfg->domain, klass);
} else {
MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, klass));
/* the is_null_bb target simply copies the input register to the output */
- mini_emit_isninst_cast (cfg, klass_reg, klass, false_bb, is_null_bb);
+ mini_emit_isninst_cast_inst (cfg, klass_reg, klass, klass_inst, false_bb, is_null_bb);
}
}
}
* Returns NULL and set the cfg exception on error.
*/
static G_GNUC_UNUSED MonoInst*
-handle_delegate_ctor (MonoCompile *cfg, MonoClass *klass, MonoInst *target, MonoMethod *method)
+handle_delegate_ctor (MonoCompile *cfg, MonoClass *klass, MonoInst *target, MonoMethod *method, int context_used)
{
gpointer *trampoline;
MonoInst *obj, *method_ins, *tramp_ins;
MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, G_STRUCT_OFFSET (MonoDelegate, target), target->dreg);
/* Set method field */
- EMIT_NEW_METHODCONST (cfg, method_ins, method);
+ method_ins = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_METHOD);
MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, G_STRUCT_OFFSET (MonoDelegate, method), method_ins->dreg);
/*
if (!cfg->compile_aot && !method->dynamic) {
MonoInst *code_slot_ins;
- domain = mono_domain_get ();
- mono_domain_lock (domain);
- if (!domain_jit_info (domain)->method_code_hash)
- domain_jit_info (domain)->method_code_hash = g_hash_table_new (NULL, NULL);
- code_slot = g_hash_table_lookup (domain_jit_info (domain)->method_code_hash, method);
- if (!code_slot) {
- code_slot = mono_domain_alloc0 (domain, sizeof (gpointer));
- g_hash_table_insert (domain_jit_info (domain)->method_code_hash, method, code_slot);
+ if (context_used) {
+ code_slot_ins = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_METHOD_DELEGATE_CODE);
+ } else {
+ domain = mono_domain_get ();
+ mono_domain_lock (domain);
+ if (!domain_jit_info (domain)->method_code_hash)
+ domain_jit_info (domain)->method_code_hash = g_hash_table_new (NULL, NULL);
+ code_slot = g_hash_table_lookup (domain_jit_info (domain)->method_code_hash, method);
+ if (!code_slot) {
+ code_slot = mono_domain_alloc0 (domain, sizeof (gpointer));
+ g_hash_table_insert (domain_jit_info (domain)->method_code_hash, method, code_slot);
+ }
+ mono_domain_unlock (domain);
+
+ EMIT_NEW_PCONST (cfg, code_slot_ins, code_slot);
}
- mono_domain_unlock (domain);
-
- EMIT_NEW_PCONST (cfg, code_slot_ins, code_slot);
MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, G_STRUCT_OFFSET (MonoDelegate, method_code), code_slot_ins->dreg);
}
static gboolean
mono_method_check_inlining (MonoCompile *cfg, MonoMethod *method)
{
- MonoMethodHeader *header;
+ MonoMethodHeaderSummary header;
MonoVTable *vtable;
#ifdef MONO_ARCH_SOFT_FLOAT
MonoMethodSignature *sig = mono_method_signature (method);
if (cfg->generic_sharing_context)
return FALSE;
+ if (cfg->inline_depth > 10)
+ return FALSE;
+
#ifdef MONO_ARCH_HAVE_LMF_OPS
if (((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
(method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) &&
return TRUE;
#endif
- if (method->is_inflated)
- /* Avoid inflating the header */
- header = mono_method_get_header (((MonoMethodInflated*)method)->declaring);
- else
- header = mono_method_get_header (method);
- if ((method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
- (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
- (method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING) ||
+ if (!mono_method_get_header_summary (method, &header))
+ return FALSE;
+
+ /*runtime, icall and pinvoke are checked by summary call*/
+ if ((method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING) ||
(method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) ||
- (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
(method->klass->marshalbyref) ||
- !header || header->num_clauses)
+ header.has_clauses)
return FALSE;
/* also consider num_locals? */
inline_limit = INLINE_LENGTH_LIMIT;
inline_limit_inited = TRUE;
}
- if (header->code_size >= inline_limit)
+ if (header.code_size >= inline_limit)
return FALSE;
/*
#if SIZEOF_REGISTER == 8
/* The array reg is 64 bits but the index reg is only 32 */
- index2_reg = alloc_preg (cfg);
- MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, index2_reg, index_reg);
+ if (COMPILE_LLVM (cfg)) {
+ /* Not needed */
+ index2_reg = index_reg;
+ } else {
+ index2_reg = alloc_preg (cfg);
+ MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, index2_reg, index_reg);
+ }
#else
if (index->type == STACK_I8) {
index2_reg = alloc_preg (cfg);
return addr;
}
+static MonoBreakPolicy
+always_insert_breakpoint (MonoMethod *method)
+{
+ return MONO_BREAK_POLICY_ALWAYS;
+}
+
+static MonoBreakPolicyFunc break_policy_func = always_insert_breakpoint;
+
+/**
+ * mono_set_break_policy:
+ * policy_callback: the new callback function
+ *
+ * Allow embedders to decide wherther to actually obey breakpoint instructions
+ * (both break IL instructions and Debugger.Break () method calls), for example
+ * to not allow an app to be aborted by a perfectly valid IL opcode when executing
+ * untrusted or semi-trusted code.
+ *
+ * @policy_callback will be called every time a break point instruction needs to
+ * be inserted with the method argument being the method that calls Debugger.Break()
+ * or has the IL break instruction. The callback should return #MONO_BREAK_POLICY_NEVER
+ * if it wants the breakpoint to not be effective in the given method.
+ * #MONO_BREAK_POLICY_ALWAYS is the default.
+ */
+void
+mono_set_break_policy (MonoBreakPolicyFunc policy_callback)
+{
+ if (policy_callback)
+ break_policy_func = policy_callback;
+ else
+ break_policy_func = always_insert_breakpoint;
+}
+
+static gboolean
+should_insert_brekpoint (MonoMethod *method) {
+ switch (break_policy_func (method)) {
+ case MONO_BREAK_POLICY_ALWAYS:
+ return TRUE;
+ case MONO_BREAK_POLICY_NEVER:
+ return FALSE;
+ case MONO_BREAK_POLICY_ON_DBG:
+ return mono_debug_using_mono_debugger ();
+ default:
+ g_warning ("Incorrect value returned from break policy callback");
+ return FALSE;
+ }
+}
+
static MonoInst*
mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
{
MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, mult_reg, args [1]->dreg, 1);
MONO_EMIT_NEW_BIALU (cfg, OP_PADD, add_reg, mult_reg, args [0]->dreg);
MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, add_reg, G_STRUCT_OFFSET (MonoString, chars), args [2]->dreg);
+ return cfg->cbb->last_ins;
} else
return NULL;
} else if (cmethod->klass == mono_defaults.object_class) {
if (strcmp (cmethod->name, "GetType") == 0) {
int dreg = alloc_preg (cfg);
int vt_reg = alloc_preg (cfg);
- MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vt_reg, args [0]->dreg, G_STRUCT_OFFSET (MonoObject, vtable));
+ MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vt_reg, args [0]->dreg, G_STRUCT_OFFSET (MonoObject, vtable));
EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, vt_reg, G_STRUCT_OFFSET (MonoVTable, type));
type_from_op (ins, NULL, NULL);
if (strcmp (cmethod->name, "get_Rank") == 0) {
int dreg = alloc_ireg (cfg);
int vtable_reg = alloc_preg (cfg);
- MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, vtable_reg,
- args [0]->dreg, G_STRUCT_OFFSET (MonoObject, vtable));
+ MONO_EMIT_NEW_LOAD_MEMBASE_OP_FAULT (cfg, OP_LOAD_MEMBASE, vtable_reg,
+ args [0]->dreg, G_STRUCT_OFFSET (MonoObject, vtable));
EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU1_MEMBASE, dreg,
vtable_reg, G_STRUCT_OFFSET (MonoVTable, rank));
type_from_op (ins, NULL, NULL);
} else if (strcmp (cmethod->name, "get_Length") == 0) {
int dreg = alloc_ireg (cfg);
- EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg,
- args [0]->dreg, G_STRUCT_OFFSET (MonoArray, max_length));
+ EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, ins, OP_LOADI4_MEMBASE, dreg,
+ args [0]->dreg, G_STRUCT_OFFSET (MonoArray, max_length));
type_from_op (ins, NULL, NULL);
return ins;
if (strcmp (cmethod->name, "Enter") == 0) {
MonoCallInst *call;
- call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_ENTER,
- NULL, helper_sig_monitor_enter_exit_trampoline, NULL);
- mono_call_inst_add_outarg_reg (cfg, call, args [0]->dreg,
- MONO_ARCH_MONITOR_OBJECT_REG, FALSE);
+ if (COMPILE_LLVM (cfg)) {
+ /*
+ * Pass the argument normally, the LLVM backend will handle the
+ * calling convention problems.
+ */
+ call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_ENTER, NULL, helper_sig_monitor_enter_exit_trampoline_llvm, args);
+ } else {
+ call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_ENTER,
+ NULL, helper_sig_monitor_enter_exit_trampoline, NULL);
+ mono_call_inst_add_outarg_reg (cfg, call, args [0]->dreg,
+ MONO_ARCH_MONITOR_OBJECT_REG, FALSE);
+ }
return (MonoInst*)call;
} else if (strcmp (cmethod->name, "Exit") == 0) {
MonoCallInst *call;
- call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_EXIT,
- NULL, helper_sig_monitor_enter_exit_trampoline, NULL);
- mono_call_inst_add_outarg_reg (cfg, call, args [0]->dreg,
- MONO_ARCH_MONITOR_OBJECT_REG, FALSE);
+ if (COMPILE_LLVM (cfg)) {
+ call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_EXIT, NULL, helper_sig_monitor_enter_exit_trampoline_llvm, args);
+ } else {
+ call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_EXIT,
+ NULL, helper_sig_monitor_enter_exit_trampoline, NULL);
+ mono_call_inst_add_outarg_reg (cfg, call, args [0]->dreg,
+ MONO_ARCH_MONITOR_OBJECT_REG, FALSE);
+ }
return (MonoInst*)call;
}
#if HAVE_WRITE_BARRIERS
if (is_ref) {
+ MonoInst *dummy_use;
MonoMethod *write_barrier = mono_gc_get_write_barrier ();
mono_emit_method_call (cfg, write_barrier, &args [0], NULL);
+ EMIT_NEW_DUMMY_USE (cfg, dummy_use, args [1]);
}
#endif
}
}
#if HAVE_WRITE_BARRIERS
if (is_ref) {
+ MonoInst *dummy_use;
MonoMethod *write_barrier = mono_gc_get_write_barrier ();
mono_emit_method_call (cfg, write_barrier, &args [0], NULL);
+ EMIT_NEW_DUMMY_USE (cfg, dummy_use, args [1]);
}
#endif
}
} else if (cmethod->klass->image == mono_defaults.corlib) {
if (cmethod->name [0] == 'B' && strcmp (cmethod->name, "Break") == 0
&& strcmp (cmethod->klass->name, "Debugger") == 0) {
- MONO_INST_NEW (cfg, ins, OP_BREAK);
+ if (should_insert_brekpoint (cfg->method))
+ MONO_INST_NEW (cfg, ins, OP_BREAK);
+ else
+ MONO_INST_NEW (cfg, ins, OP_NOP);
MONO_ADD_INS (cfg->cbb, ins);
return ins;
}
if (cmethod->name [0] == 'g' && strcmp (cmethod->name, "get_IsRunningOnWindows") == 0
&& strcmp (cmethod->klass->name, "Environment") == 0) {
-#ifdef PLATFORM_WIN32
+#ifdef TARGET_WIN32
EMIT_NEW_ICONST (cfg, ins, 1);
#else
EMIT_NEW_ICONST (cfg, ins, 0);
{
if (method->klass == mono_defaults.string_class) {
/* managed string allocation support */
- if (strcmp (method->name, "InternalAllocateStr") == 0) {
+ if (strcmp (method->name, "InternalAllocateStr") == 0 && !(mono_profiler_events & MONO_PROFILE_ALLOCATIONS)) {
MonoInst *iargs [2];
MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass);
MonoMethod *managed_alloc = NULL;
mono_jit_stats.inlineable_methods++;
cmethod->inline_info = 1;
}
+
+ /* allocate local variables */
+ cheader = mono_method_get_header (cmethod);
+
+ if (cheader == NULL || mono_loader_get_last_error ()) {
+ if (cheader)
+ mono_metadata_free_mh (cheader);
+ mono_loader_clear_error ();
+ return 0;
+ }
+
/* allocate space to store the return value */
if (!MONO_TYPE_IS_VOID (fsig->ret)) {
rvar = mono_compile_create_var (cfg, fsig->ret, OP_LOCAL);
}
- /* allocate local variables */
- cheader = mono_method_get_header (cmethod);
+
prev_locals = cfg->locals;
cfg->locals = mono_mempool_alloc0 (cfg->mempool, cheader->num_locals * sizeof (MonoInst*));
for (i = 0; i < cheader->num_locals; ++i)
prev_inlined_method = cfg->inlined_method;
cfg->inlined_method = cmethod;
cfg->ret_var_set = FALSE;
+ cfg->inline_depth ++;
prev_real_offset = cfg->real_offset;
prev_cbb_hash = cfg->cbb_hash;
prev_cil_offset_to_bb = cfg->cil_offset_to_bb;
cfg->current_method = prev_current_method;
cfg->generic_context = prev_generic_context;
cfg->ret_var_set = prev_ret_var_set;
+ cfg->inline_depth --;
if ((costs >= 0 && costs < 60) || inline_allways) {
if (cfg->verbose_level > 2)
EMIT_NEW_TEMPLOAD (cfg, ins, rvar->inst_c0);
*sp++ = ins;
}
+ mono_metadata_free_mh (cheader);
return costs + 1;
} else {
if (cfg->verbose_level > 2)
/* This gets rid of the newly added bblocks */
cfg->cbb = prev_cbb;
}
+ mono_metadata_free_mh (cheader);
return 0;
}
{
char *method_fname = mono_method_full_name (method, TRUE);
char *method_code;
+ MonoMethodHeader *header = mono_method_get_header (method);
- if (mono_method_get_header (method)->code_size == 0)
+ if (header->code_size == 0)
method_code = g_strdup ("method body is empty.");
else
method_code = mono_disasm_code_one (NULL, method, ip, NULL);
cfg->exception_message = g_strdup_printf ("Invalid IL code in %s: %s\n", method_fname, method_code);
g_free (method_fname);
g_free (method_code);
+ mono_metadata_free_mh (header);
}
static void
return MONO_TYPE_IS_REFERENCE (type);
}
-/**
- * mono_decompose_array_access_opts:
- *
- * Decompose array access opcodes.
- * This should be in decompose.c, but it emits calls so it has to stay here until
- * the old JIT is gone.
- */
-void
-mono_decompose_array_access_opts (MonoCompile *cfg)
-{
- MonoBasicBlock *bb, *first_bb;
-
- /*
- * Unlike decompose_long_opts, this pass does not alter the CFG of the method so it
- * can be executed anytime. It should be run before decompose_long
- */
-
- /**
- * Create a dummy bblock and emit code into it so we can use the normal
- * code generation macros.
- */
- cfg->cbb = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoBasicBlock));
- first_bb = cfg->cbb;
-
- for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
- MonoInst *ins;
- MonoInst *prev = NULL;
- MonoInst *dest;
- MonoInst *iargs [3];
- gboolean restart;
-
- if (!bb->has_array_access)
- continue;
-
- if (cfg->verbose_level > 3) mono_print_bb (bb, "BEFORE DECOMPOSE-ARRAY-ACCESS-OPTS ");
-
- cfg->cbb->code = cfg->cbb->last_ins = NULL;
- restart = TRUE;
-
- while (restart) {
- restart = FALSE;
-
- for (ins = bb->code; ins; ins = ins->next) {
- switch (ins->opcode) {
- case OP_LDLEN:
- NEW_LOAD_MEMBASE (cfg, dest, OP_LOADI4_MEMBASE, ins->dreg, ins->sreg1,
- G_STRUCT_OFFSET (MonoArray, max_length));
- MONO_ADD_INS (cfg->cbb, dest);
- break;
- case OP_BOUNDS_CHECK:
- MONO_ARCH_EMIT_BOUNDS_CHECK (cfg, ins->sreg1, ins->inst_imm, ins->sreg2);
- break;
- case OP_NEWARR:
- if (cfg->opt & MONO_OPT_SHARED) {
- EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
- EMIT_NEW_CLASSCONST (cfg, iargs [1], ins->inst_newa_class);
- MONO_INST_NEW (cfg, iargs [2], OP_MOVE);
- iargs [2]->dreg = ins->sreg1;
-
- dest = mono_emit_jit_icall (cfg, mono_array_new, iargs);
- dest->dreg = ins->dreg;
- } else {
- MonoVTable *vtable = mono_class_vtable (cfg->domain, mono_array_class_get (ins->inst_newa_class, 1));
- MonoMethod *managed_alloc = mono_gc_get_managed_array_allocator (vtable, 1);
-
- g_assert (vtable); /*This shall not fail since we check for this condition on OP_NEWARR creation*/
- NEW_VTABLECONST (cfg, iargs [0], vtable);
- MONO_ADD_INS (cfg->cbb, iargs [0]);
- MONO_INST_NEW (cfg, iargs [1], OP_MOVE);
- iargs [1]->dreg = ins->sreg1;
-
- if (managed_alloc)
- dest = mono_emit_method_call (cfg, managed_alloc, iargs, NULL);
- else
- dest = mono_emit_jit_icall (cfg, mono_array_new_specific, iargs);
- dest->dreg = ins->dreg;
- }
- break;
- case OP_STRLEN:
- NEW_LOAD_MEMBASE (cfg, dest, OP_LOADI4_MEMBASE, ins->dreg,
- ins->sreg1, G_STRUCT_OFFSET (MonoString, length));
- MONO_ADD_INS (cfg->cbb, dest);
- break;
- default:
- break;
- }
-
- g_assert (cfg->cbb == first_bb);
-
- if (cfg->cbb->code || (cfg->cbb != first_bb)) {
- /* Replace the original instruction with the new code sequence */
-
- mono_replace_ins (cfg, bb, ins, &prev, first_bb, cfg->cbb);
- first_bb->code = first_bb->last_ins = NULL;
- first_bb->in_count = first_bb->out_count = 0;
- cfg->cbb = first_bb;
- }
- else
- prev = ins;
- }
- }
-
- if (cfg->verbose_level > 3) mono_print_bb (bb, "AFTER DECOMPOSE-ARRAY-ACCESS-OPTS ");
- }
-}
-
-typedef union {
- guint32 vali [2];
- gint64 vall;
- double vald;
-} DVal;
-
-#ifdef MONO_ARCH_SOFT_FLOAT
-
-/**
- * mono_decompose_soft_float:
- *
- * Soft float support on ARM. We store each double value in a pair of integer vregs,
- * similar to long support on 32 bit platforms. 32 bit float values require special
- * handling when used as locals, arguments, and in calls.
- * One big problem with soft-float is that there are few r4 test cases in our test suite.
- */
-void
-mono_decompose_soft_float (MonoCompile *cfg)
-{
- MonoBasicBlock *bb, *first_bb;
-
- /*
- * This pass creates long opcodes, so it should be run before decompose_long_opts ().
- */
-
- /**
- * Create a dummy bblock and emit code into it so we can use the normal
- * code generation macros.
- */
- cfg->cbb = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoBasicBlock));
- first_bb = cfg->cbb;
-
- for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
- MonoInst *ins;
- MonoInst *prev = NULL;
- gboolean restart;
-
- if (cfg->verbose_level > 3) mono_print_bb (bb, "BEFORE HANDLE-SOFT-FLOAT ");
-
- cfg->cbb->code = cfg->cbb->last_ins = NULL;
- restart = TRUE;
-
- while (restart) {
- restart = FALSE;
-
- for (ins = bb->code; ins; ins = ins->next) {
- const char *spec = INS_INFO (ins->opcode);
-
- /* Most fp operations are handled automatically by opcode emulation */
-
- switch (ins->opcode) {
- case OP_R8CONST: {
- DVal d;
- d.vald = *(double*)ins->inst_p0;
- MONO_EMIT_NEW_I8CONST (cfg, ins->dreg, d.vall);
- break;
- }
- case OP_R4CONST: {
- DVal d;
- /* We load the r8 value */
- d.vald = *(float*)ins->inst_p0;
- MONO_EMIT_NEW_I8CONST (cfg, ins->dreg, d.vall);
- break;
- }
- case OP_FMOVE:
- ins->opcode = OP_LMOVE;
- break;
- case OP_FGETLOW32:
- ins->opcode = OP_MOVE;
- ins->sreg1 = ins->sreg1 + 1;
- break;
- case OP_FGETHIGH32:
- ins->opcode = OP_MOVE;
- ins->sreg1 = ins->sreg1 + 2;
- break;
- case OP_SETFRET: {
- int reg = ins->sreg1;
-
- ins->opcode = OP_SETLRET;
- ins->dreg = -1;
- ins->sreg1 = reg + 1;
- ins->sreg2 = reg + 2;
- break;
- }
- case OP_LOADR8_MEMBASE:
- ins->opcode = OP_LOADI8_MEMBASE;
- break;
- case OP_STORER8_MEMBASE_REG:
- ins->opcode = OP_STOREI8_MEMBASE_REG;
- break;
- case OP_STORER4_MEMBASE_REG: {
- MonoInst *iargs [2];
- int addr_reg;
-
- /* Arg 1 is the double value */
- MONO_INST_NEW (cfg, iargs [0], OP_ARG);
- iargs [0]->dreg = ins->sreg1;
-
- /* Arg 2 is the address to store to */
- addr_reg = mono_alloc_preg (cfg);
- EMIT_NEW_BIALU_IMM (cfg, iargs [1], OP_PADD_IMM, addr_reg, ins->inst_destbasereg, ins->inst_offset);
- mono_emit_jit_icall (cfg, mono_fstore_r4, iargs);
- restart = TRUE;
- break;
- }
- case OP_LOADR4_MEMBASE: {
- MonoInst *iargs [1];
- MonoInst *conv;
- int addr_reg;
-
- addr_reg = mono_alloc_preg (cfg);
- EMIT_NEW_BIALU_IMM (cfg, iargs [0], OP_PADD_IMM, addr_reg, ins->inst_basereg, ins->inst_offset);
- conv = mono_emit_jit_icall (cfg, mono_fload_r4, iargs);
- conv->dreg = ins->dreg;
- break;
- }
- case OP_FCALL:
- case OP_FCALL_REG:
- case OP_FCALL_MEMBASE: {
- MonoCallInst *call = (MonoCallInst*)ins;
- if (call->signature->ret->type == MONO_TYPE_R4) {
- MonoCallInst *call2;
- MonoInst *iargs [1];
- MonoInst *conv;
-
- /* Convert the call into a call returning an int */
- MONO_INST_NEW_CALL (cfg, call2, OP_CALL);
- memcpy (call2, call, sizeof (MonoCallInst));
- switch (ins->opcode) {
- case OP_FCALL:
- call2->inst.opcode = OP_CALL;
- break;
- case OP_FCALL_REG:
- call2->inst.opcode = OP_CALL_REG;
- break;
- case OP_FCALL_MEMBASE:
- call2->inst.opcode = OP_CALL_MEMBASE;
- break;
- default:
- g_assert_not_reached ();
- }
- call2->inst.dreg = mono_alloc_ireg (cfg);
- MONO_ADD_INS (cfg->cbb, (MonoInst*)call2);
-
- /* FIXME: Optimize this */
-
- /* Emit an r4->r8 conversion */
- EMIT_NEW_VARLOADA_VREG (cfg, iargs [0], call2->inst.dreg, &mono_defaults.int32_class->byval_arg);
- conv = mono_emit_jit_icall (cfg, mono_fload_r4, iargs);
- conv->dreg = ins->dreg;
- } else {
- switch (ins->opcode) {
- case OP_FCALL:
- ins->opcode = OP_LCALL;
- break;
- case OP_FCALL_REG:
- ins->opcode = OP_LCALL_REG;
- break;
- case OP_FCALL_MEMBASE:
- ins->opcode = OP_LCALL_MEMBASE;
- break;
- default:
- g_assert_not_reached ();
- }
- }
- break;
- }
- case OP_FCOMPARE: {
- MonoJitICallInfo *info;
- MonoInst *iargs [2];
- MonoInst *call, *cmp, *br;
-
- /* Convert fcompare+fbcc to icall+icompare+beq */
-
- info = mono_find_jit_opcode_emulation (ins->next->opcode);
- g_assert (info);
-
- /* Create dummy MonoInst's for the arguments */
- MONO_INST_NEW (cfg, iargs [0], OP_ARG);
- iargs [0]->dreg = ins->sreg1;
- MONO_INST_NEW (cfg, iargs [1], OP_ARG);
- iargs [1]->dreg = ins->sreg2;
-
- call = mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, iargs);
-
- MONO_INST_NEW (cfg, cmp, OP_ICOMPARE_IMM);
- cmp->sreg1 = call->dreg;
- cmp->inst_imm = 0;
- MONO_ADD_INS (cfg->cbb, cmp);
-
- MONO_INST_NEW (cfg, br, OP_IBNE_UN);
- br->inst_many_bb = mono_mempool_alloc (cfg->mempool, sizeof (gpointer) * 2);
- br->inst_true_bb = ins->next->inst_true_bb;
- br->inst_false_bb = ins->next->inst_false_bb;
- MONO_ADD_INS (cfg->cbb, br);
-
- /* The call sequence might include fp ins */
- restart = TRUE;
-
- /* Skip fbcc or fccc */
- NULLIFY_INS (ins->next);
- break;
- }
- case OP_FCEQ:
- case OP_FCGT:
- case OP_FCGT_UN:
- case OP_FCLT:
- case OP_FCLT_UN: {
- MonoJitICallInfo *info;
- MonoInst *iargs [2];
- MonoInst *call;
-
- /* Convert fccc to icall+icompare+iceq */
-
- info = mono_find_jit_opcode_emulation (ins->opcode);
- g_assert (info);
-
- /* Create dummy MonoInst's for the arguments */
- MONO_INST_NEW (cfg, iargs [0], OP_ARG);
- iargs [0]->dreg = ins->sreg1;
- MONO_INST_NEW (cfg, iargs [1], OP_ARG);
- iargs [1]->dreg = ins->sreg2;
-
- call = mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, iargs);
-
- MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ICOMPARE_IMM, -1, call->dreg, 1);
- MONO_EMIT_NEW_UNALU (cfg, OP_ICEQ, ins->dreg, -1);
-
- /* The call sequence might include fp ins */
- restart = TRUE;
- break;
- }
- case OP_CKFINITE: {
- MonoInst *iargs [2];
- MonoInst *call, *cmp;
-
- /* Convert to icall+icompare+cond_exc+move */
-
- /* Create dummy MonoInst's for the arguments */
- MONO_INST_NEW (cfg, iargs [0], OP_ARG);
- iargs [0]->dreg = ins->sreg1;
-
- call = mono_emit_jit_icall (cfg, mono_isfinite, iargs);
-
- MONO_INST_NEW (cfg, cmp, OP_ICOMPARE_IMM);
- cmp->sreg1 = call->dreg;
- cmp->inst_imm = 1;
- MONO_ADD_INS (cfg->cbb, cmp);
-
- MONO_EMIT_NEW_COND_EXC (cfg, INE_UN, "ArithmeticException");
-
- /* Do the assignment if the value is finite */
- MONO_EMIT_NEW_UNALU (cfg, OP_FMOVE, ins->dreg, ins->sreg1);
-
- restart = TRUE;
- break;
- }
- default:
- if (spec [MONO_INST_SRC1] == 'f' || spec [MONO_INST_SRC2] == 'f' || spec [MONO_INST_DEST] == 'f') {
- mono_print_ins (ins);
- g_assert_not_reached ();
- }
- break;
- }
-
- g_assert (cfg->cbb == first_bb);
-
- if (cfg->cbb->code || (cfg->cbb != first_bb)) {
- /* Replace the original instruction with the new code sequence */
-
- mono_replace_ins (cfg, bb, ins, &prev, first_bb, cfg->cbb);
- first_bb->code = first_bb->last_ins = NULL;
- first_bb->in_count = first_bb->out_count = 0;
- cfg->cbb = first_bb;
- }
- else
- prev = ins;
- }
- }
-
- if (cfg->verbose_level > 3) mono_print_bb (bb, "AFTER HANDLE-SOFT-FLOAT ");
- }
-
- mono_decompose_long_opts (cfg);
-}
-
-#endif
-
static void
emit_stloc_ir (MonoCompile *cfg, MonoInst **sp, MonoMethodHeader *header, int n)
{
MonoInst *return_var, GList *dont_inline, MonoInst **inline_args,
guint inline_offset, gboolean is_virtual_call)
{
+ MonoError error;
MonoInst *ins, **sp, **stack_start;
MonoBasicBlock *bblock, *tblock = NULL, *init_localsbb = NULL;
+ MonoSimpleBasicBlock *bb = NULL, *original_bb = NULL;
MonoMethod *cmethod, *method_definition;
MonoInst **arg_array;
MonoMethodHeader *header;
GSList *class_inits = NULL;
gboolean dont_verify, dont_verify_stloc, readonly = FALSE;
int context_used;
- gboolean init_locals;
+ gboolean init_locals, seq_points, skip_dead_blocks;
/* serialization and xdomain stuff may need access to private fields and methods */
dont_verify = method->klass->image->assembly->corlib_internal? TRUE: FALSE;
mono_jit_stats.cil_code_size += header->code_size;
init_locals = header->init_locals;
+ seq_points = cfg->gen_seq_points && cfg->method == method;
+
/*
* Methods without init_locals set could cause asserts in various passes
* (#497220).
NEW_ARGLOAD (cfg, arg_ins, 0);
MONO_ADD_INS (cfg->cbb, arg_ins);
- cfg->flags |= MONO_CFG_HAS_CHECK_THIS;
- MONO_EMIT_NEW_UNALU (cfg, OP_CHECK_THIS, -1, arg_ins->dreg);
- MONO_EMIT_NEW_UNALU (cfg, OP_NOT_NULL, -1, arg_ins->dreg);
+ MONO_EMIT_NEW_CHECK_THIS (cfg, arg_ins->dreg);
+ }
+
+ skip_dead_blocks = !dont_verify;
+ if (skip_dead_blocks) {
+ original_bb = bb = mono_basic_block_split (method, &error);
+ if (!mono_error_ok (&error)) {
+ mono_error_cleanup (&error);
+ UNVERIFIED;
+ }
+ g_assert (bb);
}
/* we use a spare stack slot in SWITCH and NEWOBJ and others */
start_new_bblock = 0;
cfg->cbb = bblock;
while (ip < end) {
-
if (cfg->method == method)
cfg->real_offset = ip - header->code;
else
}
}
+ if (skip_dead_blocks) {
+ int ip_offset = ip - header->code;
+
+ if (ip_offset == bb->end)
+ bb = bb->next;
+
+ if (bb->dead) {
+ int op_size = mono_opcode_size (ip, end);
+ g_assert (op_size > 0); /*The BB formation pass must catch all bad ops*/
+
+ if (cfg->verbose_level > 3) printf ("SKIPPING DEAD OP at %x\n", ip_offset);
+
+ if (ip_offset + op_size == bb->end) {
+ MONO_INST_NEW (cfg, ins, OP_NOP);
+ MONO_ADD_INS (bblock, ins);
+ start_new_bblock = 1;
+ }
+
+ ip += op_size;
+ continue;
+ }
+ }
+ /*
+ * Sequence points are points where the debugger can place a breakpoint.
+ * Currently, we generate these automatically at points where the IL
+ * stack is empty.
+ */
+ if (seq_points && sp == stack_start) {
+ NEW_SEQ_POINT (cfg, ins, ip - header->code, TRUE);
+ MONO_ADD_INS (cfg->cbb, ins);
+ }
+
bblock->real_offset = cfg->real_offset;
if ((cfg->method == method) && cfg->coverage_info) {
MONO_ADD_INS (bblock, ins);
break;
case CEE_BREAK:
- MONO_INST_NEW (cfg, ins, OP_BREAK);
+ if (should_insert_brekpoint (cfg->method))
+ MONO_INST_NEW (cfg, ins, OP_BREAK);
+ else
+ MONO_INST_NEW (cfg, ins, OP_NOP);
ip++;
MONO_ADD_INS (bblock, ins);
break;
fsig = mono_metadata_parse_signature (image, token);
n = fsig->param_count + fsig->hasthis;
+
+ if (method->dynamic && fsig->pinvoke) {
+ MonoInst *args [3];
+
+ /*
+ * This is a call through a function pointer using a pinvoke
+ * signature. Have to create a wrapper and call that instead.
+ * FIXME: This is very slow, need to create a wrapper at JIT time
+ * instead based on the signature.
+ */
+ EMIT_NEW_IMAGECONST (cfg, args [0], method->klass->image);
+ EMIT_NEW_PCONST (cfg, args [1], fsig);
+ args [2] = addr;
+ addr = mono_emit_jit_icall (cfg, mono_get_native_calli_wrapper, args);
+ }
} else {
MonoMethod *cil_method;
array_rank = cmethod->klass->rank;
fsig = mono_method_signature (cmethod);
} else {
- if (mono_method_signature (cmethod)->pinvoke) {
+ fsig = mono_method_signature (cmethod);
+
+ if (!fsig)
+ goto load_error;
+
+ if (fsig->pinvoke) {
MonoMethod *wrapper = mono_marshal_get_native_wrapper (cmethod,
check_for_pending_exc, FALSE);
fsig = mono_method_signature (wrapper);
* We have the `constrained.' prefix opcode.
*/
if (constrained_call->valuetype && !cmethod->klass->valuetype) {
- int dreg;
-
/*
* The type parameter is instantiated as a valuetype,
* but that type doesn't override the method we're
* calling, so we need to box `this'.
*/
- dreg = alloc_dreg (cfg, STACK_VTYPE);
- EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADV_MEMBASE, dreg, sp [0]->dreg, 0);
+ EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_call->byval_arg, sp [0]->dreg, 0);
ins->klass = constrained_call;
sp [0] = handle_box (cfg, ins, constrained_call);
CHECK_CFG_EXCEPTION;
if (pass_mrgctx) {
g_assert (!vtable_arg);
- if (context_used) {
- vtable_arg = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD_RGCTX);
- } else {
- EMIT_NEW_METHOD_RGCTX_CONST (cfg, vtable_arg, cmethod);
+ if (!cfg->compile_aot) {
+ /*
+ * emit_get_rgctx_method () calls mono_class_vtable () so check
+ * for type load errors before.
+ */
+ mono_class_setup_vtable (cmethod->klass);
+ CHECK_TYPELOAD (cmethod->klass);
}
+ vtable_arg = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD_RGCTX);
+
if (!(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
MONO_METHOD_IS_FINAL (cmethod)) {
if (virtual)
cmethod, MONO_RGCTX_INFO_METHOD);
}
- if (check_this) {
- MonoInst *check;
-
- MONO_INST_NEW (cfg, check, OP_CHECK_THIS);
- check->sreg1 = sp [0]->dreg;
- MONO_ADD_INS (cfg->cbb, check);
- }
+ if (check_this)
+ MONO_EMIT_NEW_CHECK_THIS (cfg, sp [0]->dreg);
/* Calling virtual generic methods */
if (cmethod && virtual &&
/* Prevent inlining of methods that contain indirect calls */
INLINE_FAILURE;
-#if MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK && !defined(ENABLE_LLVM)
- if (cmethod->wrapper_type == MONO_WRAPPER_NONE && mono_use_imt) {
+#if MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK
+ /* The llvm vcall trampolines doesn't support generic virtual calls yet */
+ if (cmethod->wrapper_type == MONO_WRAPPER_NONE && mono_use_imt && !mono_use_llvm) {
g_assert (!imt_arg);
- if (context_used) {
- imt_arg = emit_get_rgctx_method (cfg, context_used,
- cmethod, MONO_RGCTX_INFO_METHOD);
-
- } else {
+ if (!context_used)
g_assert (cmethod->is_inflated);
- EMIT_NEW_METHODCONST (cfg, imt_arg, cmethod);
- }
+ imt_arg = emit_get_rgctx_method (cfg, context_used,
+ cmethod, MONO_RGCTX_INFO_METHOD);
ins = mono_emit_method_call_full (cfg, cmethod, fsig, sp, sp [0], imt_arg);
} else
#endif
this_arg_temp = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
EMIT_NEW_TEMPLOAD (cfg, iargs [0], this_temp->inst_c0);
- if (context_used) {
- iargs [1] = emit_get_rgctx_method (cfg, context_used,
- cmethod, MONO_RGCTX_INFO_METHOD);
- EMIT_NEW_TEMPLOADA (cfg, iargs [2], this_arg_temp->inst_c0);
- addr = mono_emit_jit_icall (cfg,
- mono_helper_compile_generic_method, iargs);
- } else {
- EMIT_NEW_METHODCONST (cfg, iargs [1], cmethod);
- EMIT_NEW_TEMPLOADA (cfg, iargs [2], this_arg_temp->inst_c0);
- addr = mono_emit_jit_icall (cfg, mono_helper_compile_generic_method, iargs);
- }
+ iargs [1] = emit_get_rgctx_method (cfg, context_used,
+ cmethod, MONO_RGCTX_INFO_METHOD);
+ EMIT_NEW_TEMPLOADA (cfg, iargs [2], this_arg_temp->inst_c0);
+ addr = mono_emit_jit_icall (cfg,
+ mono_helper_compile_generic_method, iargs);
EMIT_NEW_TEMPLOAD (cfg, sp [0], this_arg_temp->inst_c0);
if (cfg->ret) {
MonoType *ret_type = mono_method_signature (method)->ret;
+ if (seq_points) {
+ /*
+ * Place a seq point here too even through the IL stack is not
+ * empty, so a step over on
+ * call <FOO>
+ * ret
+ * will work correctly.
+ */
+ NEW_SEQ_POINT (cfg, ins, ip - header->code, TRUE);
+ MONO_ADD_INS (cfg->cbb, ins);
+ }
+
g_assert (!return_var);
CHECK_STACK (1);
--sp;
}
} else {
#ifdef MONO_ARCH_SOFT_FLOAT
- if (!ret_type->byref && ret_type->type == MONO_TYPE_R4) {
+ if (COMPILE_SOFT_FLOAT (cfg) && !ret_type->byref && ret_type->type == MONO_TYPE_R4) {
MonoInst *iargs [1];
MonoInst *conv;
#if HAVE_WRITE_BARRIERS
if (*ip == CEE_STIND_REF && method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER && !((sp [1]->opcode == OP_PCONST) && (sp [1]->inst_p0 == 0))) {
+ MonoInst *dummy_use;
/* insert call to write barrier */
MonoMethod *write_barrier = mono_gc_get_write_barrier ();
mono_emit_method_call (cfg, write_barrier, sp, NULL);
+ EMIT_NEW_DUMMY_USE (cfg, dummy_use, sp [1]);
}
#endif
#endif
}
else
- ins->inst_p1 = (gpointer)(gssize)(sp [1]->inst_c0);
+ ins->inst_imm = (gssize)(sp [1]->inst_c0);
ins->sreg2 = -1;
/* Might be followed by an instruction added by ADD_WIDEN_OP */
if (!cmethod)
goto load_error;
fsig = mono_method_get_signature (cmethod, image, token);
+ if (!fsig)
+ goto load_error;
mono_save_token_info (cfg, image, token, cmethod);
if (cmethod->klass->valuetype && mono_class_generic_sharing_enabled (cmethod->klass) &&
mono_method_is_generic_sharable_impl (cmethod, TRUE)) {
if (cmethod->is_inflated && mono_method_get_context (cmethod)->method_inst) {
- if (context_used) {
- vtable_arg = emit_get_rgctx_method (cfg, context_used,
- cmethod, MONO_RGCTX_INFO_METHOD_RGCTX);
- } else {
- EMIT_NEW_METHOD_RGCTX_CONST (cfg, vtable_arg, cmethod);
- }
+ mono_class_vtable (cfg->domain, cmethod->klass);
+ CHECK_TYPELOAD (cmethod->klass);
+
+ vtable_arg = emit_get_rgctx_method (cfg, context_used,
+ cmethod, MONO_RGCTX_INFO_METHOD_RGCTX);
} else {
if (context_used) {
vtable_arg = emit_get_rgctx_klass (cfg, context_used,
if (mini_class_is_system_array (cmethod->klass)) {
g_assert (!vtable_arg);
- if (context_used) {
- *sp = emit_get_rgctx_method (cfg, context_used,
- cmethod, MONO_RGCTX_INFO_METHOD);
- } else {
- EMIT_NEW_METHODCONST (cfg, *sp, cmethod);
- }
+ *sp = emit_get_rgctx_method (cfg, context_used,
+ cmethod, MONO_RGCTX_INFO_METHOD);
/* Avoid varargs in the common case */
if (fsig->param_count == 1)
alloc = mono_emit_jit_icall (cfg, mono_array_new_1, sp);
else if (fsig->param_count == 2)
alloc = mono_emit_jit_icall (cfg, mono_array_new_2, sp);
+ else if (fsig->param_count == 3)
+ alloc = mono_emit_jit_icall (cfg, mono_array_new_3, sp);
else
alloc = handle_array_new (cfg, fsig->param_count, sp, ip);
} else if (cmethod->string_ctor) {
if (cfg->generic_sharing_context)
context_used = mono_class_check_context_used (klass);
- if (context_used) {
+ if (!context_used && mono_class_has_variant_generic_params (klass)) {
MonoInst *args [2];
/* obj */
args [0] = *sp;
/* klass */
- args [1] = emit_get_rgctx_klass (cfg, context_used,
- klass, MONO_RGCTX_INFO_KLASS);
+ EMIT_NEW_CLASSCONST (cfg, args [1], klass);
ins = mono_emit_jit_icall (cfg, mono_object_castclass, args);
*sp ++ = ins;
ip += 5;
inline_costs += 2;
- } else if (klass->marshalbyref || klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
+ } else if (!context_used && (klass->marshalbyref || klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
MonoMethod *mono_castclass;
MonoInst *iargs [1];
int costs;
inline_costs += costs;
}
else {
- ins = handle_castclass (cfg, klass, *sp);
+ ins = handle_castclass (cfg, klass, *sp, context_used);
CHECK_CFG_EXCEPTION;
bblock = cfg->cbb;
*sp ++ = ins;
if (cfg->generic_sharing_context)
context_used = mono_class_check_context_used (klass);
- if (context_used) {
+ if (!context_used && mono_class_has_variant_generic_params (klass)) {
MonoInst *args [2];
/* obj */
args [0] = *sp;
/* klass */
- args [1] = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
+ EMIT_NEW_CLASSCONST (cfg, args [1], klass);
*sp = mono_emit_jit_icall (cfg, mono_object_isinst, args);
sp++;
ip += 5;
inline_costs += 2;
- } else if (klass->marshalbyref || klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
+ } else if (!context_used && (klass->marshalbyref || klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
MonoMethod *mono_isinst;
MonoInst *iargs [1];
int costs;
inline_costs += costs;
}
else {
- ins = handle_isinst (cfg, klass, *sp);
+ ins = handle_isinst (cfg, klass, *sp, context_used);
CHECK_CFG_EXCEPTION;
bblock = cfg->cbb;
*sp ++ = ins;
*sp++ = iargs [0];
inline_costs += costs;
} else {
- ins = handle_castclass (cfg, klass, *sp);
+ ins = handle_castclass (cfg, klass, *sp, 0);
CHECK_CFG_EXCEPTION;
bblock = cfg->cbb;
*sp ++ = ins;
field->offset);
iargs [4] = sp [1];
- if (cfg->opt & MONO_OPT_INLINE) {
+ if (cfg->opt & MONO_OPT_INLINE || cfg->compile_aot) {
costs = inline_method (cfg, stfld_wrapper, mono_method_signature (stfld_wrapper),
iargs, ip, cfg->real_offset, dont_inline, TRUE);
g_assert (costs > 0);
} else {
MonoInst *store;
+ MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg);
+
EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, field->type, sp [0]->dreg, foffset, sp [1]->dreg);
#if HAVE_WRITE_BARRIERS
if (mini_type_to_stind (cfg, field->type) == CEE_STIND_REF && !(sp [1]->opcode == OP_PCONST && sp [1]->inst_c0 == 0)) {
/* insert call to write barrier */
MonoMethod *write_barrier = mono_gc_get_write_barrier ();
- MonoInst *iargs [2];
+ MonoInst *iargs [2], *dummy_use;
int dreg;
dreg = alloc_preg (cfg);
EMIT_NEW_BIALU_IMM (cfg, iargs [0], OP_PADD_IMM, dreg, sp [0]->dreg, foffset);
iargs [1] = sp [1];
mono_emit_method_call (cfg, write_barrier, iargs, NULL);
+
+ EMIT_NEW_DUMMY_USE (cfg, dummy_use, sp [1]);
}
#endif
sp [0] = ins;
}
+ MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg);
+
if (*ip == CEE_LDFLDA) {
dreg = alloc_preg (cfg);
EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, field->type, sp [0]->dreg, foffset);
load->flags |= ins_flag;
+ load->flags |= MONO_INST_FAULT;
*sp++ = load;
}
}
is_special_static = mono_class_field_is_special_static (field);
/* Generate IR to compute the field address */
+ if (is_special_static && ((gsize)addr & 0x80000000) == 0 && mono_get_thread_intrinsic (cfg) && !(cfg->opt & MONO_OPT_SHARED) && !context_used) {
+ /*
+ * Fast access to TLS data
+ * Inline version of get_thread_static_data () in
+ * threads.c.
+ */
+ guint32 offset;
+ int idx, static_data_reg, array_reg, dreg;
+ MonoInst *thread_ins;
+
+ // offset &= 0x7fffffff;
+ // idx = (offset >> 24) - 1;
+ // return ((char*) thread->static_data [idx]) + (offset & 0xffffff);
+
+ thread_ins = mono_get_thread_intrinsic (cfg);
+ MONO_ADD_INS (cfg->cbb, thread_ins);
+ static_data_reg = alloc_ireg (cfg);
+ MONO_EMIT_NEW_LOAD_MEMBASE (cfg, static_data_reg, thread_ins->dreg, G_STRUCT_OFFSET (MonoInternalThread, static_data));
+
+ if (cfg->compile_aot) {
+ int offset_reg, offset2_reg, idx_reg;
+
+ /* For TLS variables, this will return the TLS offset */
+ EMIT_NEW_SFLDACONST (cfg, ins, field);
+ offset_reg = ins->dreg;
+ MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, offset_reg, offset_reg, 0x7fffffff);
+ idx_reg = alloc_ireg (cfg);
+ MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISHR_IMM, idx_reg, offset_reg, 24);
+ MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISUB_IMM, idx_reg, idx_reg, 1);
+ MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISHL_IMM, idx_reg, idx_reg, sizeof (gpointer) == 8 ? 3 : 2);
+ MONO_EMIT_NEW_BIALU (cfg, OP_PADD, static_data_reg, static_data_reg, idx_reg);
+ array_reg = alloc_ireg (cfg);
+ MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, static_data_reg, 0);
+ offset2_reg = alloc_ireg (cfg);
+ MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, offset2_reg, offset_reg, 0xffffff);
+ dreg = alloc_ireg (cfg);
+ EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, array_reg, offset2_reg);
+ } else {
+ offset = (gsize)addr & 0x7fffffff;
+ idx = (offset >> 24) - 1;
- if ((cfg->opt & MONO_OPT_SHARED) ||
+ array_reg = alloc_ireg (cfg);
+ MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, static_data_reg, idx * sizeof (gpointer));
+ dreg = alloc_ireg (cfg);
+ EMIT_NEW_BIALU_IMM (cfg, ins, OP_ADD_IMM, dreg, array_reg, (offset & 0xffffff));
+ }
+ } else if ((cfg->opt & MONO_OPT_SHARED) ||
(cfg->compile_aot && is_special_static) ||
(context_used && is_special_static)) {
MonoInst *iargs [2];
else
EMIT_NEW_PCONST (cfg, ins, addr);
} else {
- /*
- * insert call to mono_threads_get_static_data (GPOINTER_TO_UINT (addr))
- * This could be later optimized to do just a couple of
- * memory dereferences with constant offsets.
- */
MonoInst *iargs [1];
EMIT_NEW_ICONST (cfg, iargs [0], GPOINTER_TO_UINT (addr));
ins = mono_emit_jit_icall (cfg, mono_get_special_static_data, iargs);
/*
* If this leave statement is in a catch block, check for a
* pending exception, and rethrow it if necessary.
+ * We avoid doing this in runtime invoke wrappers, since those are called
+ * by native code which excepts the wrapper to catch all exceptions.
*/
for (i = 0; i < header->num_clauses; ++i) {
MonoExceptionClause *clause = &header->clauses [i];
* The ordering of the exception clauses guarantees that we find the
* innermost clause.
*/
- if (MONO_OFFSET_IN_HANDLER (clause, ip - header->code) && (clause->flags == MONO_EXCEPTION_CLAUSE_NONE) && (ip - header->code + ((*ip == CEE_LEAVE) ? 5 : 2)) <= (clause->handler_offset + clause->handler_len)) {
+ if (MONO_OFFSET_IN_HANDLER (clause, ip - header->code) && (clause->flags == MONO_EXCEPTION_CLAUSE_NONE) && (ip - header->code + ((*ip == CEE_LEAVE) ? 5 : 2)) <= (clause->handler_offset + clause->handler_len) && method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE) {
MonoInst *exc_ins;
MonoBasicBlock *dont_throw;
if ((handlers = mono_find_final_block (cfg, ip, target, MONO_EXCEPTION_CLAUSE_FINALLY))) {
GList *tmp;
+ MonoExceptionClause *clause;
+
for (tmp = handlers; tmp; tmp = tmp->next) {
- tblock = tmp->data;
+ clause = tmp->data;
+ tblock = cfg->cil_offset_to_bb [clause->handler_offset];
+ g_assert (tblock);
link_bblock (cfg, bblock, tblock);
MONO_INST_NEW (cfg, ins, OP_CALL_HANDLER);
ins->inst_target_bb = tblock;
+ ins->inst_eh_block = clause;
MONO_ADD_INS (bblock, ins);
bblock->has_call_handler = 1;
+ if (COMPILE_LLVM (cfg)) {
+ MonoBasicBlock *target_bb;
+
+ /*
+ * Link the finally bblock with the target, since it will
+ * conceptually branch there.
+ * FIXME: Have to link the bblock containing the endfinally.
+ */
+ GET_BBLOCK (cfg, target_bb, target);
+ link_bblock (cfg, tblock, target_bb);
+ }
}
g_list_free (handlers);
}
/*
* Optimize the common case of ldftn+delegate creation
*/
-#if defined(MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE) && !defined(HAVE_WRITE_BARRIERS)
- /* FIXME: SGEN support */
- /* FIXME: handle shared static generic methods */
- /* FIXME: handle this in shared code */
- if (!needs_static_rgctx_invoke && !context_used && (sp > stack_start) && (ip + 6 + 5 < end) && ip_in_bb (cfg, bblock, ip + 6) && (ip [6] == CEE_NEWOBJ)) {
+ if ((sp > stack_start) && (ip + 6 + 5 < end) && ip_in_bb (cfg, bblock, ip + 6) && (ip [6] == CEE_NEWOBJ)) {
MonoMethod *ctor_method = mini_get_method (cfg, method, read32 (ip + 7), NULL, generic_context);
if (ctor_method && (ctor_method->klass->parent == mono_defaults.multicastdelegate_class)) {
- MonoInst *target_ins;
MonoMethod *invoke;
+ int invoke_context_used = 0;
invoke = mono_get_delegate_invoke (ctor_method->klass);
if (!invoke || !mono_method_signature (invoke))
goto load_error;
- ip += 6;
- if (cfg->verbose_level > 3)
- g_print ("converting (in B%d: stack: %d) %s", bblock->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
- target_ins = sp [-1];
- sp --;
- *sp = handle_delegate_ctor (cfg, ctor_method->klass, target_ins, cmethod);
- CHECK_CFG_EXCEPTION;
- ip += 5;
- sp ++;
- break;
+ if (cfg->generic_sharing_context)
+ invoke_context_used = mono_method_check_context_used (invoke);
+
+#if defined(MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE) && !defined(HAVE_WRITE_BARRIERS)
+ /* FIXME: SGEN support */
+ if (invoke_context_used == 0) {
+ MonoInst *target_ins;
+
+ ip += 6;
+ if (cfg->verbose_level > 3)
+ g_print ("converting (in B%d: stack: %d) %s", bblock->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
+ target_ins = sp [-1];
+ sp --;
+ *sp = handle_delegate_ctor (cfg, ctor_method->klass, target_ins, cmethod, context_used);
+ CHECK_CFG_EXCEPTION;
+ ip += 5;
+ sp ++;
+ break;
+ }
+#endif
}
}
-#endif
- if (context_used) {
- argconst = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD);
- } else {
- EMIT_NEW_METHODCONST (cfg, argconst, cmethod);
- }
+ argconst = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD);
ins = mono_emit_jit_icall (cfg, mono_ldftn, &argconst);
*sp++ = ins;
--sp;
args [0] = *sp;
- if (context_used) {
- args [1] = emit_get_rgctx_method (cfg, context_used,
- cmethod, MONO_RGCTX_INFO_METHOD);
+ args [1] = emit_get_rgctx_method (cfg, context_used,
+ cmethod, MONO_RGCTX_INFO_METHOD);
+
+ if (context_used)
*sp++ = mono_emit_jit_icall (cfg, mono_ldvirtfn_gshared, args);
- } else {
- EMIT_NEW_METHODCONST (cfg, args [1], cmethod);
+ else
*sp++ = mono_emit_jit_icall (cfg, mono_ldvirtfn, args);
- }
ip += 6;
inline_costs += 10 * num_calls++;
CHECK_STACK_OVF (1);
CHECK_OPSIZE (6);
token = read32 (ip + 2);
- if (mono_metadata_token_table (token) == MONO_TABLE_TYPESPEC) {
+ if (mono_metadata_token_table (token) == MONO_TABLE_TYPESPEC && !method->klass->image->dynamic) {
MonoType *type = mono_type_create_from_typespec (image, token);
token = mono_type_size (type, &ialign);
} else {
readonly = TRUE;
ip += 2;
break;
+
+ case CEE_UNUSED56:
+ case CEE_UNUSED57:
+ case CEE_UNUSED70:
+ case CEE_UNUSED:
+ case CEE_UNUSED99:
+ UNVERIFIED;
+
default:
- g_error ("opcode 0xfe 0x%02x not handled", ip [1]);
+ g_warning ("opcode 0xfe 0x%02x not handled", ip [1]);
+ UNVERIFIED;
}
break;
}
+ case CEE_UNUSED58:
+ case CEE_UNUSED1:
+ UNVERIFIED;
+
default:
- g_error ("opcode 0x%02x not handled", *ip);
+ g_warning ("opcode 0x%02x not handled", *ip);
+ UNVERIFIED;
}
}
if (start_new_bblock != 1)
}
}
+ if (cfg->init_ref_vars && cfg->method == method) {
+ /* Emit initialization for ref vars */
+ // FIXME: Avoid duplication initialization for IL locals.
+ for (i = 0; i < cfg->num_varinfo; ++i) {
+ MonoInst *ins = cfg->varinfo [i];
+
+ if (ins->opcode == OP_LOCAL && ins->type == STACK_OBJ)
+ MONO_EMIT_NEW_PCONST (cfg, ins->dreg, NULL);
+ }
+ }
+
+ /* Add a sequence point for method entry/exit events */
+ if (seq_points) {
+ NEW_SEQ_POINT (cfg, ins, METHOD_ENTRY_IL_OFFSET, FALSE);
+ MONO_ADD_INS (init_localsbb, ins);
+ NEW_SEQ_POINT (cfg, ins, METHOD_EXIT_IL_OFFSET, FALSE);
+ MONO_ADD_INS (cfg->bb_exit, ins);
+ }
+
cfg->ip = NULL;
if (cfg->method == method) {
cfg->exception_type = MONO_EXCEPTION_INVALID_PROGRAM;
cfg->exception_message = g_strdup_printf ("Method %s is too complex.", mname);
g_free (mname);
+ mono_metadata_free_mh (header);
+ mono_basic_block_free (original_bb);
return -1;
}
if ((cfg->verbose_level > 2) && (cfg->method == method))
mono_print_code (cfg, "AFTER METHOD-TO-IR");
+ mono_metadata_free_mh (header);
+ mono_basic_block_free (original_bb);
return inline_costs;
exception_exit:
g_assert (cfg->exception_type != MONO_EXCEPTION_NONE);
- g_slist_free (class_inits);
- dont_inline = g_list_remove (dont_inline, method);
- return -1;
+ goto cleanup;
inline_failure:
- g_slist_free (class_inits);
- dont_inline = g_list_remove (dont_inline, method);
- return -1;
+ goto cleanup;
load_error:
- g_slist_free (class_inits);
- dont_inline = g_list_remove (dont_inline, method);
cfg->exception_type = MONO_EXCEPTION_TYPE_LOAD;
- return -1;
+ goto cleanup;
unverified:
+ set_exception_type_from_invalid_il (cfg, method, ip);
+ goto cleanup;
+
+ cleanup:
g_slist_free (class_inits);
+ mono_basic_block_free (original_bb);
dont_inline = g_list_remove (dont_inline, method);
- set_exception_type_from_invalid_il (cfg, method, ip);
+ mono_metadata_free_mh (header);
return -1;
}
case 'i':
mono_compile_create_var_for_vreg (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL, vreg);
break;
+ case 'l':
+ mono_compile_create_var_for_vreg (cfg, &mono_defaults.int64_class->byval_arg, OP_LOCAL, vreg);
+ break;
case 'f':
mono_compile_create_var_for_vreg (cfg, &mono_defaults.double_class->byval_arg, OP_LOCAL, vreg);
break;
if ((ins->opcode != OP_REGVAR) && !(ins->flags & MONO_INST_IS_DEAD)) {
switch (ins->type) {
-#ifdef MONO_ARCH_SOFT_FLOAT
case STACK_R8:
-#endif
case STACK_I8: {
MonoInst *tree;
+ if (ins->type == STACK_R8 && !COMPILE_SOFT_FLOAT (cfg))
+ break;
+
g_assert (ins->opcode == OP_REGOFFSET);
tree = get_vreg_to_inst (cfg, ins->dreg + 1);
* don't split live ranges, these will precisely describe the live range of
* the variable, i.e. the instruction range where a valid value can be found
* in the variables location.
+ * The live range is computed using the liveness info computed by the liveness pass.
+ * We can't use vmv->range, since that is an abstract live range, and we need
+ * one which is instruction precise.
+ * FIXME: Variables used in out-of-line bblocks have a hole in their live range.
*/
/* FIXME: Only do this if debugging info is requested */
live_range_start = g_new0 (MonoInst*, cfg->next_vreg);
lvreg = 0;
-#ifdef MONO_ARCH_SOFT_FLOAT
- if (store_opcode == OP_STORER8_MEMBASE_REG) {
+ if (COMPILE_SOFT_FLOAT (cfg) && store_opcode == OP_STORER8_MEMBASE_REG) {
regtype = 'l';
store_opcode = OP_STOREI8_MEMBASE_REG;
}
-#endif
ins->dreg = alloc_dreg (cfg, stacktypes [regtype]);
if (cfg->verbose_level > 2)
mono_print_ins_index (1, ins);
}
+
+ /* Extend the live range based on the liveness info */
+ if (cfg->compute_precise_live_ranges && bb->live_out_set && bb->code) {
+ for (i = 0; i < cfg->num_varinfo; i ++) {
+ MonoMethodVar *vi = MONO_VARINFO (cfg, i);
+
+ if (vreg_is_volatile (cfg, vi->vreg))
+ /* The liveness info is incomplete */
+ continue;
+
+ if (mono_bitset_test_fast (bb->live_in_set, i) && !live_range_start [vi->vreg]) {
+ /* Live from at least the first ins of this bb */
+ live_range_start [vi->vreg] = bb->code;
+ live_range_start_bb [vi->vreg] = bb;
+ }
+
+ if (mono_bitset_test_fast (bb->live_out_set, i)) {
+ /* Live at least until the last ins of this bb */
+ live_range_end [vi->vreg] = bb->last_ins;
+ live_range_end_bb [vi->vreg] = bb;
+ }
+ }
+ }
}
#ifdef MONO_ARCH_HAVE_LIVERANGE_OPS
* Emit LIVERANGE_START/LIVERANGE_END opcodes, the backend will implement them
* by storing the current native offset into MonoMethodVar->live_range_start/end.
*/
- for (i = 0; i < cfg->num_varinfo; ++i) {
- int vreg = MONO_VARINFO (cfg, i)->vreg;
- MonoInst *ins;
-
- if (live_range_start [vreg]) {
- MONO_INST_NEW (cfg, ins, OP_LIVERANGE_START);
- ins->inst_c0 = i;
- ins->inst_c1 = vreg;
- mono_bblock_insert_after_ins (live_range_start_bb [vreg], live_range_start [vreg], ins);
- }
- if (live_range_end [vreg]) {
- MONO_INST_NEW (cfg, ins, OP_LIVERANGE_END);
- ins->inst_c0 = i;
- ins->inst_c1 = vreg;
- mono_bblock_insert_after_ins (live_range_end_bb [vreg], live_range_end [vreg], ins);
+ if (cfg->compute_precise_live_ranges && cfg->comp_done & MONO_COMP_LIVENESS) {
+ for (i = 0; i < cfg->num_varinfo; ++i) {
+ int vreg = MONO_VARINFO (cfg, i)->vreg;
+ MonoInst *ins;
+
+ if (live_range_start [vreg]) {
+ MONO_INST_NEW (cfg, ins, OP_LIVERANGE_START);
+ ins->inst_c0 = i;
+ ins->inst_c1 = vreg;
+ mono_bblock_insert_after_ins (live_range_start_bb [vreg], live_range_start [vreg], ins);
+ }
+ if (live_range_end [vreg]) {
+ MONO_INST_NEW (cfg, ins, OP_LIVERANGE_END);
+ ins->inst_c0 = i;
+ ins->inst_c1 = vreg;
+ if (live_range_end [vreg] == live_range_end_bb [vreg]->last_ins)
+ mono_add_ins_to_end (live_range_end_bb [vreg], ins);
+ else
+ mono_bblock_insert_after_ins (live_range_end_bb [vreg], live_range_end [vreg], ins);
+ }
}
}
#endif