Skip to content

Commit

Permalink
[jit] Handle more cases for gsharedvt constrained calls.
Browse files Browse the repository at this point in the history
Hopefully fixes mono#7016.
  • Loading branch information
vargaz committed Feb 16, 2018
1 parent b7385ba commit 9c147de
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 10 deletions.
24 changes: 24 additions & 0 deletions mono/mini/gshared.cs
Expand Up @@ -1107,13 +1107,18 @@ public class CGetType : IGetType {
interface IConstrainedCalls {
Pair<int, int> vtype_ret<T, T2>(T t, T2 t2) where T: IReturnVType;
AnEnum enum_ret<T, T2>(T t, T2 t2) where T: IReturnVType;
int normal_args<T, T2> (T t, T2 t2, int i1, int i2, string s, ref int i3) where T : IConstrained2;
}

public interface IReturnVType {
Pair<int, int> return_vtype ();
AnEnum return_enum ();
}

public interface IConstrained2 {
int normal_args (int i1, int i2, string s, ref int i3);
}

public class CConstrainedCalls : IConstrainedCalls {
[MethodImplAttribute (MethodImplOptions.NoInlining)]
public Pair<int, int> vtype_ret<T, T2>(T t, T2 t2) where T : IReturnVType {
Expand All @@ -1124,6 +1129,10 @@ public class CConstrainedCalls : IConstrainedCalls {
public AnEnum enum_ret<T, T2>(T t, T2 t2) where T : IReturnVType {
return t.return_enum ();
}

public int normal_args<T, T2> (T t, T2 t2, int i1, int i2, string s, ref int i3) where T : IConstrained2 {
return t.normal_args (i1, i2, s, ref i3);
}
}

class ReturnVType : IReturnVType {
Expand All @@ -1135,6 +1144,13 @@ class ReturnVType : IReturnVType {
}
}

class ConstrainedCalls : IConstrained2 {
public int normal_args (int i1, int i2, string s, ref int i3) {
i3 = i3 + 1;
return i1 + i2 + i3 + s.Length;
}
}

public static int test_0_constrained_vtype_ret () {
IConstrainedCalls c = new CConstrainedCalls ();
var r = c.vtype_ret<ReturnVType, int> (new ReturnVType (), 1);
Expand All @@ -1151,6 +1167,14 @@ class ReturnVType : IReturnVType {
return 0;
}

public static int test_14_constrained_normal_args () {
IConstrainedCalls c = new CConstrainedCalls ();

int val = 3;
var r = c.normal_args<ConstrainedCalls, int> (new ConstrainedCalls (), 0, 1, 2, "ABC", ref val);
return r + val;
}

public struct Pair<T1, T2> {
public T1 First;
public T2 Second;
Expand Down
46 changes: 36 additions & 10 deletions mono/mini/method-to-ir.c
Expand Up @@ -3459,6 +3459,7 @@ handle_unbox_nullable (MonoCompile* cfg, MonoInst* val, MonoClass* klass, int co
method = mono_class_get_method_from_name (klass, "UnboxExact", 1);
else
method = mono_class_get_method_from_name (klass, "Unbox", 1);
g_assert (method);

if (context_used) {
MonoInst *rgctx, *addr;
Expand Down Expand Up @@ -4044,15 +4045,31 @@ handle_constrained_gsharedvt_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMe
{
MonoInst *ins = NULL;
gboolean emit_widen = *ref_emit_widen;
gboolean supported;

/*
* Constrained calls need to behave differently at runtime dependending on whenever the receiver is instantiated as ref type or as a vtype.
* This is hard to do with the current call code, since we would have to emit a branch and two different calls. So instead, we
* pack the arguments into an array, and do the rest of the work in in an icall.
*/
if (((cmethod->klass == mono_defaults.object_class) || mono_class_is_interface (cmethod->klass) || (!cmethod->klass->valuetype && cmethod->klass->image != mono_defaults.corlib)) &&
(MONO_TYPE_IS_VOID (fsig->ret) || MONO_TYPE_IS_PRIMITIVE (fsig->ret) || MONO_TYPE_IS_REFERENCE (fsig->ret) || MONO_TYPE_ISSTRUCT (fsig->ret) || mono_class_is_enum (mono_class_from_mono_type (fsig->ret)) || mini_is_gsharedvt_type (fsig->ret)) &&
(fsig->param_count == 0 || (!fsig->hasthis && fsig->param_count == 1) || (fsig->param_count == 1 && (MONO_TYPE_IS_REFERENCE (fsig->params [0]) || fsig->params [0]->byref || mini_is_gsharedvt_type (fsig->params [0]))))) {
supported = ((cmethod->klass == mono_defaults.object_class) || mono_class_is_interface (cmethod->klass) || (!cmethod->klass->valuetype && cmethod->klass->image != mono_defaults.corlib));
if (supported)
supported = (MONO_TYPE_IS_VOID (fsig->ret) || MONO_TYPE_IS_PRIMITIVE (fsig->ret) || MONO_TYPE_IS_REFERENCE (fsig->ret) || MONO_TYPE_ISSTRUCT (fsig->ret) || mono_class_is_enum (mono_class_from_mono_type (fsig->ret)) || mini_is_gsharedvt_type (fsig->ret));
if (supported) {
if (fsig->param_count == 0 || (!fsig->hasthis && fsig->param_count == 1)) {
supported = TRUE;
} else {
/* Allow scalar parameters and a gsharedvt first parameter */
supported = MONO_TYPE_IS_PRIMITIVE (fsig->params [0]) || MONO_TYPE_IS_REFERENCE (fsig->params [0]) || fsig->params [0]->byref || mini_is_gsharedvt_type (fsig->params [0]);
if (supported) {
for (int i = 1; i < fsig->param_count; ++i) {
if (!(fsig->params [i]->byref || MONO_TYPE_IS_PRIMITIVE (fsig->params [i]) || MONO_TYPE_IS_REFERENCE (fsig->params [i])))
supported = FALSE;
}
}
}
}
if (supported) {
MonoInst *args [16];

/*
Expand All @@ -4072,27 +4089,36 @@ handle_constrained_gsharedvt_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMe

/* !fsig->hasthis is for the wrapper for the Object.GetType () icall */
if (fsig->hasthis && fsig->param_count) {
/* Call mono_gsharedvt_constrained_call (gpointer mp, MonoMethod *cmethod, MonoClass *klass, gboolean deref_arg, gpointer *args) */
/* Pass the arguments using a localloc-ed array using the format expected by runtime_invoke () */
MONO_INST_NEW (cfg, ins, OP_LOCALLOC_IMM);
ins->dreg = alloc_preg (cfg);
ins->inst_imm = fsig->param_count * sizeof (mgreg_t);
MONO_ADD_INS (cfg->cbb, ins);
args [4] = ins;

/* Only the first argument is allowed to be gsharedvt */
/* args [3] = deref_arg */
if (mini_is_gsharedvt_type (fsig->params [0])) {
int addr_reg, deref_arg_reg;

int deref_arg_reg;
ins = mini_emit_get_gsharedvt_info_klass (cfg, mono_class_from_mono_type (fsig->params [0]), MONO_RGCTX_INFO_CLASS_BOX_TYPE);
deref_arg_reg = alloc_preg (cfg);
/* deref_arg = BOX_TYPE != MONO_GSHAREDVT_BOX_TYPE_VTYPE */
EMIT_NEW_BIALU_IMM (cfg, args [3], OP_ISUB_IMM, deref_arg_reg, ins->dreg, 1);

EMIT_NEW_VARLOADA_VREG (cfg, ins, sp [1]->dreg, fsig->params [0]);
addr_reg = ins->dreg;
EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args [4]->dreg, 0, addr_reg);
} else {
EMIT_NEW_ICONST (cfg, args [3], 0);
EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args [4]->dreg, 0, sp [1]->dreg);
}

for (int i = 0; i < fsig->param_count; ++i) {
int addr_reg;

if (mini_is_gsharedvt_type (fsig->params [i]) || MONO_TYPE_IS_PRIMITIVE (fsig->params [i])) {
EMIT_NEW_VARLOADA_VREG (cfg, ins, sp [i + 1]->dreg, fsig->params [i]);
addr_reg = ins->dreg;
EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args [4]->dreg, i * sizeof (mgreg_t), addr_reg);
} else {
EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args [4]->dreg, i * sizeof (mgreg_t), sp [i + 1]->dreg);
}
}
} else {
EMIT_NEW_ICONST (cfg, args [3], 0);
Expand Down

0 comments on commit 9c147de

Please sign in to comment.