return alloc_ireg (cfg);
}
+guint32
+mono_alloc_lreg (MonoCompile *cfg)
+{
+ return alloc_lreg (cfg);
+}
+
guint32
mono_alloc_freg (MonoCompile *cfg)
{
if (align == 0)
align = 4;
- if ((size <= 4) && (size <= align)) {
+ if ((size <= SIZEOF_REGISTER) && (size <= align)) {
switch (size) {
case 1:
MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI1_MEMBASE_IMM, destreg, offset, val);
}
#else
lmf_ins = mono_get_lmf_addr_intrinsic (cfg);
- if (lmf_ins)
+ if (lmf_ins) {
MONO_ADD_INS (cfg->cbb, lmf_ins);
- else
+ } else {
+#ifdef TARGET_IOS
+ MonoInst *args [16], *jit_tls_ins, *ins;
+
+ /* Inline mono_get_lmf_addr () */
+ /* jit_tls = pthread_getspecific (mono_jit_tls_id); lmf_addr = &jit_tls->lmf; */
+
+ /* Load mono_jit_tls_id */
+ EMIT_NEW_AOTCONST (cfg, args [0], MONO_PATCH_INFO_JIT_TLS_ID, NULL);
+ /* call pthread_getspecific () */
+ jit_tls_ins = mono_emit_jit_icall (cfg, pthread_getspecific, args);
+ /* lmf_addr = &jit_tls->lmf */
+ EMIT_NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, cfg->lmf_addr_var->dreg, jit_tls_ins->dreg, G_STRUCT_OFFSET (MonoJitTlsData, lmf));
+ lmf_ins = ins;
+#else
lmf_ins = mono_emit_jit_icall (cfg, mono_get_lmf_addr, NULL);
+#endif
+ }
#endif
lmf_ins->dreg = cfg->lmf_addr_var->dreg;
mono_emit_calli (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **args, MonoInst *addr, MonoInst *imt_arg, MonoInst *rgctx_arg)
{
MonoCallInst *call;
+ MonoInst *ins;
int rgctx_reg = -1;
+ gboolean check_sp = FALSE;
+
+ if (cfg->check_pinvoke_callconv && cfg->method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) {
+ WrapperInfo *info = mono_marshal_get_wrapper_info (cfg->method);
+
+ if (info && info->subtype == WRAPPER_SUBTYPE_PINVOKE)
+ check_sp = TRUE;
+ }
if (rgctx_arg) {
rgctx_reg = mono_alloc_preg (cfg);
MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, rgctx_reg, rgctx_arg->dreg);
}
+ if (check_sp) {
+ if (!cfg->stack_inbalance_var)
+ cfg->stack_inbalance_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
+
+ MONO_INST_NEW (cfg, ins, OP_GET_SP);
+ ins->dreg = cfg->stack_inbalance_var->dreg;
+ MONO_ADD_INS (cfg->cbb, ins);
+ }
+
call = mono_emit_call_args (cfg, sig, args, TRUE, FALSE, FALSE, rgctx_arg ? TRUE : FALSE, FALSE);
call->inst.sreg1 = addr->dreg;
MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
+ if (check_sp) {
+ int sp_reg;
+
+ sp_reg = mono_alloc_preg (cfg);
+
+ MONO_INST_NEW (cfg, ins, OP_GET_SP);
+ ins->dreg = sp_reg;
+ MONO_ADD_INS (cfg->cbb, ins);
+
+ /* Restore the stack so we don't crash when throwing the exception */
+ MONO_INST_NEW (cfg, ins, OP_SET_SP);
+ ins->sreg1 = cfg->stack_inbalance_var->dreg;
+ MONO_ADD_INS (cfg->cbb, ins);
+
+ MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, cfg->stack_inbalance_var->dreg, sp_reg);
+ MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "ExecutionEngineException");
+ }
+
if (rgctx_arg)
set_rgctx_arg (cfg, call, rgctx_reg, rgctx_arg);
else
eklass = mono_class_from_mono_type (fsig->ret);
-
if (is_set) {
return emit_array_store (cfg, eklass, args, FALSE);
} else {
}
}
+static gboolean
+is_unsafe_mov_compatible (MonoClass *param_klass, MonoClass *return_klass)
+{
+ uint32_t align;
+
+ //Only allow for valuetypes
+ if (!param_klass->valuetype || !return_klass->valuetype)
+ return FALSE;
+
+ //That are blitable
+ if (param_klass->has_references || return_klass->has_references)
+ return FALSE;
+
+ /* Avoid mixing structs and primitive types/enums, they need to be handled differently in the JIT */
+ if ((MONO_TYPE_ISSTRUCT (¶m_klass->byval_arg) && !MONO_TYPE_ISSTRUCT (&return_klass->byval_arg)) ||
+ (!MONO_TYPE_ISSTRUCT (¶m_klass->byval_arg) && MONO_TYPE_ISSTRUCT (&return_klass->byval_arg)))
+ return FALSE;
+
+ if (param_klass->byval_arg.type == MONO_TYPE_R4 || param_klass->byval_arg.type == MONO_TYPE_R8 ||
+ return_klass->byval_arg.type == MONO_TYPE_R4 || return_klass->byval_arg.type == MONO_TYPE_R8)
+ return FALSE;
+
+ //And have the same size
+ if (mono_class_value_size (param_klass, &align) != mono_class_value_size (return_klass, &align))
+ return FALSE;
+ return TRUE;
+}
+
+static MonoInst*
+emit_array_unsafe_mov (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args)
+{
+ MonoClass *param_klass = mono_class_from_mono_type (fsig->params [0]);
+ MonoClass *return_klass = mono_class_from_mono_type (fsig->ret);
+
+ //Valuetypes that are semantically equivalent
+ if (is_unsafe_mov_compatible (param_klass, return_klass))
+ return args [0];
+
+ //Arrays of valuetypes that are semantically equivalent
+ if (param_klass->rank == 1 && return_klass->rank == 1 && is_unsafe_mov_compatible (param_klass->element_class, return_klass->element_class))
+ return args [0];
+
+ return NULL;
+}
+
static MonoInst*
mini_emit_inst_for_ctor (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
{
if (cmethod->klass == mono_defaults.array_class) {
if (strcmp (cmethod->name, "UnsafeStore") == 0)
return emit_array_unsafe_access (cfg, fsig, args, TRUE);
- if (strcmp (cmethod->name, "UnsafeLoad") == 0)
+ else if (strcmp (cmethod->name, "UnsafeLoad") == 0)
return emit_array_unsafe_access (cfg, fsig, args, FALSE);
+ else if (strcmp (cmethod->name, "UnsafeMov") == 0)
+ return emit_array_unsafe_mov (cfg, fsig, args);
}
return NULL;
opcode = OP_ATOMIC_ADD_NEW_I8;
#endif
if (opcode) {
+ if (!mono_arch_opcode_supported (opcode))
+ return NULL;
MONO_INST_NEW (cfg, ins_iconst, OP_ICONST);
ins_iconst->inst_c0 = 1;
ins_iconst->dreg = mono_alloc_ireg (cfg);
opcode = OP_ATOMIC_ADD_NEW_I8;
#endif
if (opcode) {
+ if (!mono_arch_opcode_supported (opcode))
+ return NULL;
MONO_INST_NEW (cfg, ins_iconst, OP_ICONST);
ins_iconst->inst_c0 = -1;
ins_iconst->dreg = mono_alloc_ireg (cfg);
else if (fsig->params [0]->type == MONO_TYPE_I8)
opcode = OP_ATOMIC_ADD_NEW_I8;
#endif
-
if (opcode) {
+ if (!mono_arch_opcode_supported (opcode))
+ return NULL;
MONO_INST_NEW (cfg, ins, opcode);
ins->dreg = mono_alloc_ireg (cfg);
ins->inst_basereg = args [0]->dreg;
else
return NULL;
+ if (!mono_arch_opcode_supported (opcode))
+ return NULL;
+
MONO_INST_NEW (cfg, ins, opcode);
ins->dreg = is_ref ? mono_alloc_ireg_ref (cfg) : mono_alloc_ireg (cfg);
ins->inst_basereg = args [0]->dreg;
else if (sizeof (gpointer) == 8 && fsig->params [1]->type == MONO_TYPE_I8)
size = 8;
if (size == 4) {
+ if (!mono_arch_opcode_supported (OP_ATOMIC_CAS_I4))
+ return NULL;
MONO_INST_NEW (cfg, ins, OP_ATOMIC_CAS_I4);
ins->dreg = is_ref ? alloc_ireg_ref (cfg) : alloc_ireg (cfg);
ins->sreg1 = args [0]->dreg;
ins->sreg3 = args [2]->dreg;
ins->type = STACK_I4;
MONO_ADD_INS (cfg->cbb, ins);
+ cfg->has_atomic_cas_i4 = TRUE;
} else if (size == 8) {
+ if (!mono_arch_opcode_supported (OP_ATOMIC_CAS_I8))
+ return NULL;
MONO_INST_NEW (cfg, ins, OP_ATOMIC_CAS_I8);
ins->dreg = is_ref ? alloc_ireg_ref (cfg) : alloc_ireg (cfg);
ins->sreg1 = args [0]->dreg;
if (args [0]->opcode == OP_GOT_ENTRY) {
pi = args [0]->inst_p1;
g_assert (pi->opcode == OP_PATCH_INFO);
- g_assert ((int)pi->inst_p1 == MONO_PATCH_INFO_LDSTR);
+ g_assert (GPOINTER_TO_INT (pi->inst_p1) == MONO_PATCH_INFO_LDSTR);
ji = pi->inst_p0;
} else {
- g_assert ((int)args [0]->inst_p1 == MONO_PATCH_INFO_LDSTR);
+ g_assert (GPOINTER_TO_INT (args [0]->inst_p1) == MONO_PATCH_INFO_LDSTR);
ji = args [0]->inst_p0;
}
NEW_LOAD_MEMBASE (cfg, ins, ldind_to_load_membase (*ip), dreg, sp [0]->dreg, 0);
ins->type = ldind_type [*ip - CEE_LDIND_I1];
ins->flags |= ins_flag;
- ins_flag = 0;
MONO_ADD_INS (bblock, ins);
*sp++ = ins;
- if (ins->flags & MONO_INST_VOLATILE) {
+ if (ins_flag & MONO_INST_VOLATILE) {
/* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
/* FIXME it's questionable if acquire semantics require full barrier or just LoadLoad*/
emit_memory_barrier (cfg, FullBarrier);
}
+ ins_flag = 0;
++ip;
break;
case CEE_STIND_REF:
CHECK_STACK (2);
sp -= 2;
- NEW_STORE_MEMBASE (cfg, ins, stind_to_store_membase (*ip), sp [0]->dreg, 0, sp [1]->dreg);
- ins->flags |= ins_flag;
- ins_flag = 0;
-
- if (ins->flags & MONO_INST_VOLATILE) {
+ if (ins_flag & MONO_INST_VOLATILE) {
/* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
/* FIXME it's questionable if acquire semantics require full barrier or just LoadLoad*/
emit_memory_barrier (cfg, FullBarrier);
}
+ NEW_STORE_MEMBASE (cfg, ins, stind_to_store_membase (*ip), sp [0]->dreg, 0, sp [1]->dreg);
+ ins->flags |= ins_flag;
+ ins_flag = 0;
+
MONO_ADD_INS (bblock, ins);
if (cfg->gen_write_barriers && *ip == CEE_STIND_REF && method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER && !((sp [1]->opcode == OP_PCONST) && (sp [1]->inst_p0 == 0)))
EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, sp [0]->dreg, 0);
ins->dreg = cfg->locals [loc_index]->dreg;
+ ins->flags |= ins_flag;
ip += 5;
ip += stloc_len;
+ if (ins_flag & MONO_INST_VOLATILE) {
+ /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
+ /* FIXME it's questionable if acquire semantics require full barrier or just LoadLoad*/
+ emit_memory_barrier (cfg, FullBarrier);
+ }
+ ins_flag = 0;
break;
}
/* Optimize the ldobj+stobj combination */
/* The reference case ends up being a load+store anyway */
- if (((ip [5] == CEE_STOBJ) && ip_in_bb (cfg, bblock, ip + 5) && read32 (ip + 6) == token) && !generic_class_is_reference_type (cfg, klass)) {
+ /* Skip this if the operation is volatile. */
+ if (((ip [5] == CEE_STOBJ) && ip_in_bb (cfg, bblock, ip + 5) && read32 (ip + 6) == token) && !generic_class_is_reference_type (cfg, klass) && !(ins_flag & MONO_INST_VOLATILE)) {
CHECK_STACK (1);
sp --;
}
EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, sp [0]->dreg, 0);
+ ins->flags |= ins_flag;
*sp++ = ins;
+ if (ins_flag & MONO_INST_VOLATILE) {
+ /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
+ /* FIXME it's questionable if acquire semantics require full barrier or just LoadLoad*/
+ emit_memory_barrier (cfg, FullBarrier);
+ }
+
ip += 5;
ins_flag = 0;
inline_costs += 1;
addr = emit_get_rgctx_gsharedvt_call (cfg, context_used, fsig, cmethod, MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE);
mono_emit_calli (cfg, fsig, sp, addr, NULL, vtable_arg);
} else if (context_used &&
- (!mono_method_is_generic_sharable (cmethod, TRUE) ||
- !mono_class_generic_sharing_enabled (cmethod->klass))) {
+ ((!mono_method_is_generic_sharable (cmethod, TRUE) ||
+ !mono_class_generic_sharing_enabled (cmethod->klass)) || cfg->gsharedvt)) {
MonoInst *cmethod_addr;
+ /* Generic calls made out of gsharedvt methods cannot be patched, so use an indirect call */
+
cmethod_addr = emit_get_rgctx_method (cfg, context_used,
cmethod, MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
/* Generate IR to do the actual load/store operation */
+ if ((op == CEE_STFLD || op == CEE_STSFLD) && (ins_flag & MONO_INST_VOLATILE)) {
+ /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
+ /* FIXME it's questionable if acquire semantics require full barrier or just LoadLoad*/
+ emit_memory_barrier (cfg, FullBarrier);
+ }
+
if (op == CEE_LDSFLDA) {
ins->klass = mono_class_from_mono_type (ftype);
ins->type = STACK_PTR;
*sp++ = load;
}
}
+
+ if ((op == CEE_LDFLD || op == CEE_LDSFLD) && (ins_flag & MONO_INST_VOLATILE)) {
+ /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
+ /* FIXME it's questionable if acquire semantics require full barrier or just LoadLoad*/
+ emit_memory_barrier (cfg, FullBarrier);
+ }
+
ins_flag = 0;
ip += 5;
break;
token = read32 (ip + 1);
klass = mini_get_class (method, token, generic_context);
CHECK_TYPELOAD (klass);
+ if (ins_flag & MONO_INST_VOLATILE) {
+ /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
+ /* FIXME it's questionable if acquire semantics require full barrier or just LoadLoad*/
+ emit_memory_barrier (cfg, FullBarrier);
+ }
/* FIXME: should check item at sp [1] is compatible with the type of the store. */
EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, sp [0]->dreg, 0, sp [1]->dreg);
+ ins->flags |= ins_flag;
if (cfg->gen_write_barriers && cfg->method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER &&
generic_class_is_reference_type (cfg, klass)) {
/* insert call to write barrier */
CHECK_STACK (3);
sp -= 3;
- if ((ip [1] == CEE_CPBLK) && (cfg->opt & MONO_OPT_INTRINS) && (sp [2]->opcode == OP_ICONST) && ((n = sp [2]->inst_c0) <= sizeof (gpointer) * 5)) {
+ /* Skip optimized paths for volatile operations. */
+ if ((ip [1] == CEE_CPBLK) && !(ins_flag & MONO_INST_VOLATILE) && (cfg->opt & MONO_OPT_INTRINS) && (sp [2]->opcode == OP_ICONST) && ((n = sp [2]->inst_c0) <= sizeof (gpointer) * 5)) {
mini_emit_memcpy (cfg, sp [0]->dreg, 0, sp [1]->dreg, 0, sp [2]->inst_c0, 0);
- } else if ((ip [1] == CEE_INITBLK) && (cfg->opt & MONO_OPT_INTRINS) && (sp [2]->opcode == OP_ICONST) && ((n = sp [2]->inst_c0) <= sizeof (gpointer) * 5) && (sp [1]->opcode == OP_ICONST) && (sp [1]->inst_c0 == 0)) {
+ } else if ((ip [1] == CEE_INITBLK) && !(ins_flag & MONO_INST_VOLATILE) && (cfg->opt & MONO_OPT_INTRINS) && (sp [2]->opcode == OP_ICONST) && ((n = sp [2]->inst_c0) <= sizeof (gpointer) * 5) && (sp [1]->opcode == OP_ICONST) && (sp [1]->inst_c0 == 0)) {
/* emit_memset only works when val == 0 */
mini_emit_memset (cfg, sp [0]->dreg, 0, sp [2]->inst_c0, sp [1]->inst_c0, 0);
} else {
+ MonoInst *call;
iargs [0] = sp [0];
iargs [1] = sp [1];
iargs [2] = sp [2];
if (ip [1] == CEE_CPBLK) {
+ /*
+ * FIXME: It's unclear whether we should be emitting both the acquire
+ * and release barriers for cpblk. It is technically both a load and
+ * store operation, so it seems like that's the sensible thing to do.
+ */
MonoMethod *memcpy_method = get_memcpy_method ();
- mono_emit_method_call (cfg, memcpy_method, iargs, NULL);
+ if (ins_flag & MONO_INST_VOLATILE) {
+ /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
+ /* FIXME it's questionable if acquire semantics require full barrier or just LoadLoad*/
+ emit_memory_barrier (cfg, FullBarrier);
+ }
+ call = mono_emit_method_call (cfg, memcpy_method, iargs, NULL);
+ call->flags |= ins_flag;
+ if (ins_flag & MONO_INST_VOLATILE) {
+ /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
+ /* FIXME it's questionable if acquire semantics require full barrier or just LoadLoad*/
+ emit_memory_barrier (cfg, FullBarrier);
+ }
} else {
MonoMethod *memset_method = get_memset_method ();
- mono_emit_method_call (cfg, memset_method, iargs, NULL);
+ if (ins_flag & MONO_INST_VOLATILE) {
+ /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
+ /* FIXME it's questionable if acquire semantics require full barrier or just LoadLoad*/
+ emit_memory_barrier (cfg, FullBarrier);
+ }
+ call = mono_emit_method_call (cfg, memset_method, iargs, NULL);
+ call->flags |= ins_flag;
}
}
ip += 2;
+ ins_flag = 0;
inline_costs += 1;
break;
}