#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/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
#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 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:
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)
{
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
this_reg = this->dreg;
- MONO_EMIT_NULL_CHECK (cfg, this_reg);
-
#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);
}
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
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);
+ }
}
mono_emit_jit_icall (cfg, mono_value_copy, iargs);
int rank_reg = alloc_dreg (cfg ,STACK_I4);
obj_reg = sp [0]->dreg;
- MONO_EMIT_NULL_CHECK (cfg, obj_reg);
- 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 */
* 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);
+
+ // FIXME: This doesn't work yet (mcs/tests/gtest-304.cs fails)
+ if (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)) {
+ /* 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);
+
+ // FIXME: This doesn't work yet (mcs/tests/gtest-304.cs fails)
+ if (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)) {
+ 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);
}
}
}
#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)
{
if (strcmp (cmethod->name, "GetType") == 0) {
int dreg = alloc_preg (cfg);
int vt_reg = alloc_preg (cfg);
- MONO_EMIT_NULL_CHECK (cfg, args [0]->dreg);
- 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_NULL_CHECK (cfg, args [0]->dreg);
- 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);
- MONO_EMIT_NULL_CHECK (cfg, args [0]->dreg);
- 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;
} 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;
}
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:
- MONO_EMIT_NULL_CHECK (cfg, ins->sreg1);
- 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_EMIT_NULL_CHECK (cfg, ins->sreg1); \
- 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:
- MONO_EMIT_NULL_CHECK (cfg, ins->sreg1);
- 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;
-
- /* The call sequence might include fp ins */
- restart = TRUE;
- } 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;
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, seq_points;
+ 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_EMIT_NEW_CHECK_THIS (cfg, arg_ins->dreg);
}
+ skip_dead_blocks = !dont_verify;
+ if (skip_dead_blocks) {
+ 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 */
stack_start = sp = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst*) * (header->max_stack + 1));
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
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;
if (pass_mrgctx) {
g_assert (!vtable_arg);
+ if (!cfg->compile_aot) {
+ /*
+ * emit_get_rgctx_method () calls mono_class_vtable () so check
+ * for type load errors before.
+ */
+ 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);
if (!(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
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 (!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) {
+ 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 {
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;
EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, field->type, sp [0]->dreg, foffset);
load->flags |= ins_flag;
+ load->flags |= MONO_INST_FAULT;
*sp++ = load;
}
}
MonoInst *argconst;
MonoMethod *cil_method;
gboolean needs_static_rgctx_invoke;
- int invoke_context_used = 0;
CHECK_STACK_OVF (1);
CHECK_OPSIZE (6);
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))
}
}
+ 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);
exception_exit:
g_assert (cfg->exception_type != MONO_EXCEPTION_NONE);
g_slist_free (class_inits);
+ mono_basic_block_free (bb);
dont_inline = g_list_remove (dont_inline, method);
return -1;
inline_failure:
g_slist_free (class_inits);
+ mono_basic_block_free (bb);
dont_inline = g_list_remove (dont_inline, method);
return -1;
load_error:
g_slist_free (class_inits);
+ mono_basic_block_free (bb);
dont_inline = g_list_remove (dont_inline, method);
cfg->exception_type = MONO_EXCEPTION_TYPE_LOAD;
return -1;
unverified:
g_slist_free (class_inits);
+ mono_basic_block_free (bb);
dont_inline = g_list_remove (dont_inline, method);
set_exception_type_from_invalid_il (cfg, method, ip);
return -1;
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