89 changes: 77 additions & 12 deletions mono/mini/method-to-ir.c
Expand Up @@ -158,6 +158,8 @@ static int inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSigna
guchar *ip, guint real_offset, gboolean inline_always);
static MonoInst*
emit_llvmonly_virtual_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, int context_used, MonoInst **sp);
static MonoInst*
convert_value (MonoCompile *cfg, MonoType *type, MonoInst *ins);

/* helper methods signatures */
static MonoMethodSignature *helper_sig_domain_get;
Expand Down Expand Up @@ -542,9 +544,9 @@ add_widen_op (MonoCompile *cfg, MonoInst *ins, MonoInst **arg1_ref, MonoInst **a
MONO_INST_NEW(cfg, cmp, OP_COMPARE); \
cmp->sreg1 = sp [0]->dreg; \
cmp->sreg2 = sp [1]->dreg; \
add_widen_op (cfg, cmp, &sp [0], &sp [1]); \
type_from_op (cfg, cmp, sp [0], sp [1]); \
CHECK_TYPE (cmp); \
add_widen_op (cfg, cmp, &sp [0], &sp [1]); \
type_from_op (cfg, ins, sp [0], sp [1]); \
ins->inst_many_bb = (MonoBasicBlock **)mono_mempool_alloc (cfg->mempool, sizeof(gpointer)*2); \
GET_BBLOCK (cfg, tblock, target); \
Expand Down Expand Up @@ -1101,6 +1103,12 @@ type_from_op (MonoCompile *cfg, MonoInst *ins, MonoInst *src1, MonoInst *src2)
case STACK_R8:
ins->opcode = OP_FCONV_TO_U;
break;
case STACK_R4:
if (SIZEOF_VOID_P == 8)
ins->opcode = OP_RCONV_TO_U8;
else
ins->opcode = OP_RCONV_TO_U4;
break;
}
break;
case CEE_CONV_I8:
Expand Down Expand Up @@ -1423,10 +1431,13 @@ mono_compile_get_interface_var (MonoCompile *cfg, int slot, MonoInst *ins)
{
MonoInst *res;
int pos, vnum;
MonoType *type;

type = type_from_stack_type (ins);

/* inlining can result in deeper stacks */
if (slot >= cfg->header->max_stack)
return mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
if (cfg->inlined_method || slot >= cfg->header->max_stack)
return mono_compile_create_var (cfg, type, OP_LOCAL);

pos = ins->type - 1 + slot * STACK_MAX;

Expand All @@ -1439,11 +1450,11 @@ mono_compile_get_interface_var (MonoCompile *cfg, int slot, MonoInst *ins)
case STACK_OBJ:
if ((vnum = cfg->intvars [pos]))
return cfg->varinfo [vnum];
res = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
res = mono_compile_create_var (cfg, type, OP_LOCAL);
cfg->intvars [pos] = res->inst_c0;
break;
default:
res = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
res = mono_compile_create_var (cfg, type, OP_LOCAL);
}
return res;
}
Expand Down Expand Up @@ -1521,10 +1532,7 @@ handle_stack_args (MonoCompile *cfg, MonoInst **sp, int count)
* the bblock they are inlined to. See bug #58863 for an
* example.
*/
if (cfg->inlined_method)
bb->out_stack [i] = mono_compile_create_var (cfg, type_from_stack_type (sp [i]), OP_LOCAL);
else
bb->out_stack [i] = mono_compile_get_interface_var (cfg, i, sp [i]);
bb->out_stack [i] = mono_compile_get_interface_var (cfg, i, sp [i]);
}
}
}
Expand All @@ -1548,6 +1556,7 @@ handle_stack_args (MonoCompile *cfg, MonoInst **sp, int count)
locals = bb->out_stack;
cfg->cbb = bb;
for (i = 0; i < count; ++i) {
sp [i] = convert_value (cfg, locals [i]->inst_vtype, sp [i]);
EMIT_NEW_TEMPSTORE (cfg, inst, locals [i]->inst_c0, sp [i]);
inst->cil_code = sp [i]->cil_code;
sp [i] = locals [i];
Expand Down Expand Up @@ -1575,6 +1584,7 @@ handle_stack_args (MonoCompile *cfg, MonoInst **sp, int count)
}
if (outb->in_stack != locals) {
for (i = 0; i < count; ++i) {
sp [i] = convert_value (cfg, outb->in_stack [i]->inst_vtype, sp [i]);
EMIT_NEW_TEMPSTORE (cfg, inst, outb->in_stack [i]->inst_c0, sp [i]);
inst->cil_code = sp [i]->cil_code;
sp [i] = locals [i];
Expand Down Expand Up @@ -1938,6 +1948,42 @@ target_type_is_incompatible (MonoCompile *cfg, MonoType *target, MonoInst *arg)
return 1;
}

/*
* convert_value:
*
* Emit some implicit conversions which are not part of the .net spec, but are allowed by MS.NET.
*/
static MonoInst*
convert_value (MonoCompile *cfg, MonoType *type, MonoInst *ins)
{
if (!cfg->r4fp)
return ins;
type = mini_get_underlying_type (type);
switch (type->type) {
case MONO_TYPE_R4:
if (ins->type == STACK_R8) {
int dreg = alloc_freg (cfg);
MonoInst *conv;
EMIT_NEW_UNALU (cfg, conv, OP_FCONV_TO_R4, dreg, ins->dreg);
conv->type = STACK_R4;
return conv;
}
break;
case MONO_TYPE_R8:
if (ins->type == STACK_R4) {
int dreg = alloc_freg (cfg);
MonoInst *conv;
EMIT_NEW_UNALU (cfg, conv, OP_RCONV_TO_R8, dreg, ins->dreg);
conv->type = STACK_R8;
return conv;
}
break;
default:
break;
}
return ins;
}

/*
* Prepare arguments for passing to a function call.
* Return a non-zero value if the arguments can't be passed to the given
Expand Down Expand Up @@ -7922,6 +7968,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
n = (*ip)-CEE_STLOC_0;
CHECK_LOCAL (n);
--sp;
*sp = convert_value (cfg, header->locals [n], *sp);
if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [n], *sp))
UNVERIFIED;
emit_stloc_ir (cfg, sp, header, n);
Expand Down Expand Up @@ -7958,6 +8005,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
--sp;
n = ip [1];
CHECK_ARG (n);
*sp = convert_value (cfg, param_types [ip [1]], *sp);
if (!dont_verify_stloc && target_type_is_incompatible (cfg, param_types [ip [1]], *sp))
UNVERIFIED;
emit_starg_ir (cfg, sp, n);
Expand Down Expand Up @@ -8696,6 +8744,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
constrained_class = NULL;
}

for (int i = 0; i < fsig->param_count; ++i)
sp [i + fsig->hasthis] = convert_value (cfg, fsig->params [i], sp [i + fsig->hasthis]);

if (check_call_signature (cfg, fsig, sp))
UNVERIFIED;

Expand Down Expand Up @@ -9291,6 +9342,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
MonoInst *store;
CHECK_STACK (1);
--sp;
*sp = convert_value (cfg, ret_type, *sp);

if ((method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD || method->wrapper_type == MONO_WRAPPER_NONE) && target_type_is_incompatible (cfg, ret_type, *sp))
UNVERIFIED;
Expand Down Expand Up @@ -9321,6 +9373,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
g_assert (!return_var);
CHECK_STACK (1);
--sp;
*sp = convert_value (cfg, ret_type, *sp);

if ((method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD || method->wrapper_type == MONO_WRAPPER_NONE) && target_type_is_incompatible (cfg, ret_type, *sp))
UNVERIFIED;
Expand Down Expand Up @@ -9621,7 +9674,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
case CEE_STIND_I8:
case CEE_STIND_R4:
case CEE_STIND_R8:
case CEE_STIND_I:
case CEE_STIND_I: {
CHECK_STACK (2);
sp -= 2;

Expand All @@ -9630,6 +9683,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
}

if (*ip == CEE_STIND_R4 && sp [1]->type == STACK_R8)
sp [1] = convert_value (cfg, m_class_get_byval_arg (mono_defaults.single_class), sp [1]);
NEW_STORE_MEMBASE (cfg, ins, stind_to_store_membase (*ip), sp [0]->dreg, 0, sp [1]->dreg);
ins->flags |= ins_flag;
ins_flag = 0;
Expand All @@ -9647,7 +9702,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
inline_costs += 1;
++ip;
break;

}
case CEE_MUL:
CHECK_STACK (2);

Expand Down Expand Up @@ -10072,6 +10127,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
sp [1] = sp [0];
}

for (int i = 0; i < fsig->param_count; ++i)
sp [i + fsig->hasthis] = convert_value (cfg, fsig->params [i], sp [i + fsig->hasthis]);

/* check_call_signature () requires sp[0] to be set */
this_ins.type = STACK_OBJ;
sp [0] = &this_ins;
Expand Down Expand Up @@ -10258,9 +10316,11 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
break;
}

val = convert_value (cfg, m_class_get_byval_arg (klass), val);

if (klass == mono_defaults.void_class)
UNVERIFIED;
if (target_type_is_incompatible (cfg, m_class_get_byval_arg (klass), *sp))
if (target_type_is_incompatible (cfg, m_class_get_byval_arg (klass), val))
UNVERIFIED;
/* frequent check in generic code: box (struct), brtrue */

Expand Down Expand Up @@ -10509,6 +10569,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b

foffset = m_class_is_valuetype (klass) ? field->offset - sizeof (MonoObject): field->offset;
if (op == CEE_STFLD) {
sp [1] = convert_value (cfg, field->type, sp [1]);
if (target_type_is_incompatible (cfg, field->type, sp [1]))
UNVERIFIED;
#ifndef DISABLE_REMOTING
Expand Down Expand Up @@ -11242,6 +11303,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
if (sp [0]->type != STACK_OBJ)
UNVERIFIED;

sp [2] = convert_value (cfg, m_class_get_byval_arg (klass), sp [2]);
emit_array_store (cfg, klass, sp, TRUE);

if (*ip == CEE_STELEM)
Expand All @@ -11261,6 +11323,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
iargs [0] = sp [0];
*sp++ = mono_emit_jit_icall (cfg, mono_ckfinite, iargs);
} else {
sp [0] = convert_value (cfg, m_class_get_byval_arg (mono_defaults.double_class), sp [0]);
MONO_INST_NEW (cfg, ins, OP_CKFINITE);
ins->sreg1 = sp [0]->dreg;
ins->dreg = alloc_freg (cfg);
Expand Down Expand Up @@ -12526,6 +12589,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
CHECK_OPSIZE (4);
n = read16 (ip + 2);
CHECK_ARG (n);
*sp = convert_value (cfg, param_types [n], *sp);
if (!dont_verify_stloc && target_type_is_incompatible (cfg, param_types [n], *sp))
UNVERIFIED;
emit_starg_ir (cfg, sp, n);
Expand Down Expand Up @@ -12568,6 +12632,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
CHECK_OPSIZE (4);
n = read16 (ip + 2);
CHECK_LOCAL (n);
*sp = convert_value (cfg, header->locals [n], *sp);
if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [n], *sp))
UNVERIFIED;
emit_stloc_ir (cfg, sp, header, n);
Expand Down
39 changes: 32 additions & 7 deletions mono/mini/mini-amd64.c
Expand Up @@ -5225,6 +5225,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
amd64_sse_cvtss2si_reg_reg_size (code, ins->dreg, ins->sreg1, 4);
break;
case OP_RCONV_TO_I8:
case OP_RCONV_TO_I:
amd64_sse_cvtss2si_reg_reg_size (code, ins->dreg, ins->sreg1, 8);
break;
case OP_RCONV_TO_R8:
Expand Down Expand Up @@ -5551,8 +5552,18 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
break;
}

if (unordered) {
guchar *unordered_check;
guchar *unordered_check;

switch (ins->opcode) {
case OP_RCEQ:
case OP_RCGT:
unordered_check = code;
x86_branch8 (code, X86_CC_P, 0, FALSE);
amd64_set_reg (code, x86_cond, ins->dreg, FALSE);
amd64_patch (unordered_check, code);
break;
case OP_RCLT_UN:
case OP_RCGT_UN: {
guchar *jump_to_end;

unordered_check = code;
Expand All @@ -5563,8 +5574,14 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
amd64_patch (unordered_check, code);
amd64_inc_reg (code, ins->dreg);
amd64_patch (jump_to_end, code);
} else {
break;
}
case OP_RCLT:
amd64_set_reg (code, x86_cond, ins->dreg, FALSE);
break;
default:
g_assert_not_reached ();
break;
}
break;
}
Expand Down Expand Up @@ -5797,8 +5814,12 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
break;
}
case OP_ATOMIC_LOAD_R4: {
amd64_sse_movss_reg_membase (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
amd64_sse_cvtss2sd_reg_reg (code, ins->dreg, ins->dreg);
if (cfg->r4fp) {
amd64_sse_movss_reg_membase (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
} else {
amd64_sse_movss_reg_membase (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
amd64_sse_cvtss2sd_reg_reg (code, ins->dreg, ins->dreg);
}
break;
}
case OP_ATOMIC_LOAD_R8: {
Expand Down Expand Up @@ -5841,8 +5862,12 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
break;
}
case OP_ATOMIC_STORE_R4: {
amd64_sse_cvtsd2ss_reg_reg (code, MONO_ARCH_FP_SCRATCH_REG, ins->sreg1);
amd64_sse_movss_membase_reg (code, ins->inst_destbasereg, ins->inst_offset, MONO_ARCH_FP_SCRATCH_REG);
if (cfg->r4fp) {
amd64_sse_movss_membase_reg (code, ins->inst_destbasereg, ins->inst_offset, ins->sreg1);
} else {
amd64_sse_cvtsd2ss_reg_reg (code, MONO_ARCH_FP_SCRATCH_REG, ins->sreg1);
amd64_sse_movss_membase_reg (code, ins->inst_destbasereg, ins->inst_offset, MONO_ARCH_FP_SCRATCH_REG);
}

if (ins->backend.memory_barrier_kind == MONO_MEMORY_BARRIER_SEQ)
x86_mfence (code);
Expand Down
1 change: 1 addition & 0 deletions mono/mini/mini-amd64.h
Expand Up @@ -448,6 +448,7 @@ typedef struct {
#define MONO_ARCH_HAVE_PATCH_CODE_NEW 1
#define MONO_ARCH_HAVE_OP_GENERIC_CLASS_INIT 1
#define MONO_ARCH_HAVE_GENERAL_RGCTX_LAZY_FETCH_TRAMPOLINE 1
#define MONO_ARCH_FLOAT32_SUPPORTED 1

#define MONO_ARCH_HAVE_INTERP_PINVOKE_TRAMP

Expand Down
1 change: 1 addition & 0 deletions mono/mini/mini-arm.h
Expand Up @@ -359,6 +359,7 @@ typedef struct MonoCompileArch {
#define MONO_ARCH_HAVE_SDB_TRAMPOLINES 1
#define MONO_ARCH_HAVE_PATCH_CODE_NEW 1
#define MONO_ARCH_HAVE_OP_GENERIC_CLASS_INIT 1
#define MONO_ARCH_FLOAT32_SUPPORTED 1

#define MONO_ARCH_HAVE_INTERP_PINVOKE_TRAMP 1

Expand Down
11 changes: 10 additions & 1 deletion mono/mini/mini-arm64.c
Expand Up @@ -2788,7 +2788,8 @@ mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb)
NULLIFY_INS (ins);
}
break;
case OP_FCOMPARE: {
case OP_FCOMPARE:
case OP_RCOMPARE: {
gboolean swap = FALSE;
int reg;

Expand All @@ -2812,6 +2813,14 @@ mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb)
ins->next->opcode = OP_FBGE;
swap = TRUE;
break;
case OP_RBLT:
ins->next->opcode = OP_RBGT;
swap = TRUE;
break;
case OP_RBLE:
ins->next->opcode = OP_RBGE;
swap = TRUE;
break;
default:
break;
}
Expand Down
2 changes: 1 addition & 1 deletion mono/mini/mini-arm64.h
Expand Up @@ -147,7 +147,7 @@ typedef struct {
#define MONO_ARCH_HAVE_OP_GENERIC_CLASS_INIT 1
#define MONO_ARCH_HAVE_OPCODE_NEEDS_EMULATION 1
#define MONO_ARCH_HAVE_DECOMPOSE_LONG_OPTS 1

#define MONO_ARCH_FLOAT32_SUPPORTED 1
#define MONO_ARCH_HAVE_INTERP_PINVOKE_TRAMP 1

// Does the ABI have a volatile non-parameter register, so tailcall
Expand Down
5 changes: 4 additions & 1 deletion mono/mini/mini-codegen.c
Expand Up @@ -597,7 +597,10 @@ mono_print_ins_index_strbuf (int i, MonoInst *ins)
case OP_VOIDCALL:
case OP_VOIDCALL_MEMBASE:
case OP_TAILCALL:
case OP_TAILCALL_MEMBASE: {
case OP_TAILCALL_MEMBASE:
case OP_RCALL:
case OP_RCALL_REG:
case OP_RCALL_MEMBASE: {
MonoCallInst *call = (MonoCallInst*)ins;
GSList *list;

Expand Down
4 changes: 4 additions & 0 deletions mono/mini/mini.c
Expand Up @@ -3128,6 +3128,10 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, JitFl
try_llvm = mono_use_llvm || llvm;
#endif

#ifndef MONO_ARCH_FLOAT32_SUPPORTED
opts &= ~MONO_OPT_FLOAT32;
#endif

restart_compile:
if (method_is_gshared) {
method_to_compile = method;
Expand Down
10 changes: 10 additions & 0 deletions mono/mini/objects.cs
Expand Up @@ -1897,6 +1897,16 @@ public static int test_0_store_to_magic_iface_array ()

return 0;
}

static volatile bool abool;

public static unsafe int test_0_stind_r4_float32_stack_merge () {
Single* dataPtr = stackalloc Single[4];
abool = true;
dataPtr[0] = abool ? 1.0f : 2.0f;
return dataPtr [0] == 1.0f ? 0 : 1;
}

}

#if __MOBILE__
Expand Down