Merge pull request #2431 from alexanderkyte/tests_with_excludes
[mono.git] / mono / mini / mini-generic-sharing.c
index 58094823d31f585fcebb0294a2c3ba67e9f9a4c0..94312a18f43d315cecdfb76d58ce66d7325a12d2 100644 (file)
@@ -11,6 +11,8 @@
 #include <config.h>
 
 #include <mono/metadata/class.h>
+#include <mono/metadata/method-builder.h>
+#include <mono/metadata/reflection-internals.h>
 #include <mono/utils/mono-counters.h>
 
 #include "mini.h"
@@ -538,6 +540,7 @@ inflate_info (MonoRuntimeGenericContextInfoTemplate *oti, MonoGenericContext *co
 
        case MONO_RGCTX_INFO_METHOD:
        case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
+       case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER:
        case MONO_RGCTX_INFO_METHOD_RGCTX:
        case MONO_RGCTX_INFO_METHOD_CONTEXT:
        case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
@@ -641,6 +644,7 @@ inflate_info (MonoRuntimeGenericContextInfoTemplate *oti, MonoGenericContext *co
 
                return &inflated_class->fields [i];
        }
+       case MONO_RGCTX_INFO_SIG_GSHAREDVT_IN_TRAMPOLINE_CALLI:
        case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI: {
                MonoMethodSignature *sig = (MonoMethodSignature *)data;
                MonoMethodSignature *isig;
@@ -942,8 +946,10 @@ class_type_info (MonoDomain *domain, MonoClass *klass, MonoRgctxInfoType info_ty
        case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
        case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: {
                MonoMethod *method;
-               gpointer addr;
+               gpointer addr, arg;
                MonoJitInfo *ji;
+               MonoMethodSignature *sig, *gsig;
+               MonoMethod *gmethod;
 
                if (!mono_class_is_nullable (klass))
                        /* This can happen since all the entries in MonoGSharedVtMethodInfo are inflated, even those which are not used */
@@ -955,15 +961,24 @@ class_type_info (MonoDomain *domain, MonoClass *klass, MonoRgctxInfoType info_ty
                        method = mono_class_get_method_from_name (klass, "Unbox", 1);
 
                addr = mono_compile_method (method);
+
                // The caller uses the gsharedvt call signature
+
+               if (mono_llvm_only) {
+                       /* FIXME: We have no access to the gsharedvt signature/gsctx used by the caller, so have to construct it ourselves */
+                       gmethod = mini_get_shared_method_full (method, FALSE, TRUE);
+                       sig = mono_method_signature (method);
+                       gsig = mono_method_signature (gmethod);
+
+                       addr = mini_add_method_wrappers_llvmonly (method, addr, TRUE, FALSE, &arg);
+                       return mini_create_llvmonly_ftndesc (domain, addr, arg);
+               }
+
                ji = mini_jit_info_table_find (mono_domain_get (), (char *)mono_get_addr_from_ftnptr (addr), NULL);
                g_assert (ji);
                if (mini_jit_info_is_gsharedvt (ji))
                        return mono_create_static_rgctx_trampoline (method, addr);
                else {
-                       MonoMethodSignature *sig, *gsig;
-                       MonoMethod *gmethod;
-
                        /* Need to add an out wrapper */
 
                        /* FIXME: We have no access to the gsharedvt signature/gsctx used by the caller, so have to construct it ourselves */
@@ -1022,6 +1037,336 @@ tramp_info_equal (gconstpointer a, gconstpointer b)
                tramp1->addr == tramp2->addr && tramp1->sig == tramp2->sig && tramp1->gsig == tramp2->gsig;
 }
 
+static MonoType*
+get_wrapper_shared_type (MonoType *t)
+{
+       if (t->byref)
+               return &mono_defaults.int_class->this_arg;
+       t = mini_get_underlying_type (t);
+
+       switch (t->type) {
+       case MONO_TYPE_I1:
+               /* This removes any attributes etc. */
+               return &mono_defaults.sbyte_class->byval_arg;
+       case MONO_TYPE_U1:
+               return &mono_defaults.byte_class->byval_arg;
+       case MONO_TYPE_I2:
+               return &mono_defaults.int16_class->byval_arg;
+       case MONO_TYPE_U2:
+               return &mono_defaults.uint16_class->byval_arg;
+       case MONO_TYPE_I4:
+               return &mono_defaults.int32_class->byval_arg;
+       case MONO_TYPE_U4:
+               return &mono_defaults.uint32_class->byval_arg;
+       case MONO_TYPE_OBJECT:
+       case MONO_TYPE_CLASS:
+       case MONO_TYPE_SZARRAY:
+       case MONO_TYPE_ARRAY:
+       case MONO_TYPE_PTR:
+               return &mono_defaults.int_class->byval_arg;
+       case MONO_TYPE_GENERICINST: {
+               MonoClass *klass;
+               MonoGenericContext ctx;
+               MonoGenericContext *orig_ctx;
+               MonoGenericInst *inst;
+               MonoType *args [16];
+               int i;
+
+               if (!MONO_TYPE_ISSTRUCT (t))
+                       return &mono_defaults.int_class->byval_arg;
+
+               klass = mono_class_from_mono_type (t);
+               orig_ctx = &klass->generic_class->context;
+
+               memset (&ctx, 0, sizeof (MonoGenericContext));
+
+               inst = orig_ctx->class_inst;
+               if (inst) {
+                       g_assert (inst->type_argc < 16);
+                       for (i = 0; i < inst->type_argc; ++i)
+                               args [i] = get_wrapper_shared_type (inst->type_argv [i]);
+                       ctx.class_inst = mono_metadata_get_generic_inst (inst->type_argc, args);
+               }
+               inst = orig_ctx->method_inst;
+               if (inst) {
+                       g_assert (inst->type_argc < 16);
+                       for (i = 0; i < inst->type_argc; ++i)
+                               args [i] = get_wrapper_shared_type (inst->type_argv [i]);
+                       ctx.method_inst = mono_metadata_get_generic_inst (inst->type_argc, args);
+               }
+               klass = mono_class_inflate_generic_class (klass->generic_class->container_class, &ctx);
+               return &klass->byval_arg;
+       }
+#if SIZEOF_VOID_P == 8
+       case MONO_TYPE_I8:
+               return &mono_defaults.int_class->byval_arg;
+#endif
+       default:
+               break;
+       }
+
+       //printf ("%s\n", mono_type_full_name (t));
+
+       return t;
+}
+
+static MonoMethodSignature*
+mini_get_underlying_signature (MonoMethodSignature *sig)
+{
+       MonoMethodSignature *res = mono_metadata_signature_dup (sig);
+       int i;
+
+       res->ret = get_wrapper_shared_type (sig->ret);
+       for (i = 0; i < sig->param_count; ++i)
+               res->params [i] = get_wrapper_shared_type (sig->params [i]);
+       res->generic_param_count = 0;
+       res->is_inflated = 0;
+
+       return res;
+}
+
+/*
+ * mini_get_gsharedvt_in_sig_wrapper:
+ *
+ *   Return a wrapper to translate between the normal and gsharedvt calling conventions of SIG.
+ * The returned wrapper has a signature of SIG, plus one extra argument, which is an <addr, rgctx> pair.
+ * The extra argument is passed the same way as an rgctx to shared methods.
+ * It calls <addr> using the gsharedvt version of SIG, passing in <rgctx> as an extra argument.
+ */
+MonoMethod*
+mini_get_gsharedvt_in_sig_wrapper (MonoMethodSignature *sig)
+{
+       MonoMethodBuilder *mb;
+       MonoMethod *res;
+       WrapperInfo *info;
+       MonoMethodSignature *csig, *gsharedvt_sig;
+       int i, pindex, retval_var;
+       static GHashTable *cache;
+
+       // FIXME: Memory management
+       sig = mini_get_underlying_signature (sig);
+
+       // FIXME: Normal cache
+       if (!cache)
+               cache = g_hash_table_new_full ((GHashFunc)mono_signature_hash, (GEqualFunc)mono_metadata_signature_equal, NULL, NULL);
+       // FIXME: Locking
+       res = g_hash_table_lookup (cache, sig);
+       if (res) {
+               g_free (sig);
+               return res;
+       }
+
+       /* Create the signature for the wrapper */
+       // FIXME:
+       csig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 1) * sizeof (MonoType*)));
+       memcpy (csig, sig, mono_metadata_signature_size (sig));
+       csig->param_count ++;
+       csig->params [sig->param_count] = &mono_defaults.int_class->byval_arg;
+
+       /* Create the signature for the gsharedvt callconv */
+       gsharedvt_sig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 2) * sizeof (MonoType*)));
+       memcpy (gsharedvt_sig, sig, mono_metadata_signature_size (sig));
+       pindex = 0;
+       /* The return value is returned using an explicit vret argument */
+       if (sig->ret->type != MONO_TYPE_VOID) {
+               gsharedvt_sig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
+               gsharedvt_sig->ret = &mono_defaults.void_class->byval_arg;
+       }
+       for (i = 0; i < sig->param_count; i++) {
+               gsharedvt_sig->params [pindex] = sig->params [i];
+               if (!sig->params [i]->byref) {
+                       gsharedvt_sig->params [pindex] = mono_metadata_type_dup (NULL, gsharedvt_sig->params [pindex]);
+                       gsharedvt_sig->params [pindex]->byref = 1;
+               }
+               pindex ++;
+       }
+       /* Rgctx arg */
+       gsharedvt_sig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
+       gsharedvt_sig->param_count = pindex;
+
+       // FIXME: Use shared signatures
+       mb = mono_mb_new (mono_defaults.object_class, sig->hasthis ? "gsharedvt_in_sig" : "gsharedvt_in_sig_static", MONO_WRAPPER_UNKNOWN);
+
+#ifndef DISABLE_JIT
+       if (sig->ret->type != MONO_TYPE_VOID)
+               retval_var = mono_mb_add_local (mb, sig->ret);
+
+       /* Make the call */
+       if (sig->hasthis)
+               mono_mb_emit_ldarg (mb, 0);
+       if (sig->ret->type != MONO_TYPE_VOID)
+               mono_mb_emit_ldloc_addr (mb, retval_var);
+       for (i = 0; i < sig->param_count; i++) {
+               if (sig->params [i]->byref)
+                       mono_mb_emit_ldarg (mb, i + (sig->hasthis == TRUE));
+               else
+                       mono_mb_emit_ldarg_addr (mb, i + (sig->hasthis == TRUE));
+       }
+       /* Rgctx arg */
+       mono_mb_emit_ldarg (mb, sig->param_count + (sig->hasthis ? 1 : 0));
+       mono_mb_emit_icon (mb, sizeof (gpointer));
+       mono_mb_emit_byte (mb, CEE_ADD);
+       mono_mb_emit_byte (mb, CEE_LDIND_I);
+       /* Method to call */
+       mono_mb_emit_ldarg (mb, sig->param_count + (sig->hasthis ? 1 : 0));
+       mono_mb_emit_byte (mb, CEE_LDIND_I);
+       mono_mb_emit_calli (mb, gsharedvt_sig);
+       if (sig->ret->type != MONO_TYPE_VOID)
+               mono_mb_emit_ldloc (mb, retval_var);
+       mono_mb_emit_byte (mb, CEE_RET);
+#endif
+
+       info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_GSHAREDVT_IN_SIG);
+       info->d.gsharedvt.sig = sig;
+
+       res = mono_mb_create (mb, csig, sig->param_count + 16, info);
+
+       // FIXME: Locking
+       g_hash_table_insert (cache, sig, res);
+
+       return res;
+}
+
+/*
+ * mini_get_gsharedvt_out_sig_wrapper:
+ *
+ *   Same as in_sig_wrapper, but translate between the gsharedvt and normal signatures.
+ */
+MonoMethod*
+mini_get_gsharedvt_out_sig_wrapper (MonoMethodSignature *sig)
+{
+       MonoMethodBuilder *mb;
+       MonoMethod *res;
+       WrapperInfo *info;
+       MonoMethodSignature *normal_sig, *csig;
+       int i, pindex, args_start, ldind_op, stind_op;
+       static GHashTable *cache;
+
+       // FIXME: Memory management
+       sig = mini_get_underlying_signature (sig);
+
+       // FIXME: Normal cache
+       if (!cache)
+               cache = g_hash_table_new_full ((GHashFunc)mono_signature_hash, (GEqualFunc)mono_metadata_signature_equal, NULL, NULL);
+       // FIXME: Locking
+       res = g_hash_table_lookup (cache, sig);
+       if (res) {
+               g_free (sig);
+               return res;
+       }
+
+       /* Create the signature for the wrapper */
+       // FIXME:
+       csig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 2) * sizeof (MonoType*)));
+       memcpy (csig, sig, mono_metadata_signature_size (sig));
+       pindex = 0;
+       /* The return value is returned using an explicit vret argument */
+       if (sig->ret->type != MONO_TYPE_VOID) {
+               csig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
+               csig->ret = &mono_defaults.void_class->byval_arg;
+       }
+       args_start = pindex;
+       if (sig->hasthis)
+               args_start ++;
+       for (i = 0; i < sig->param_count; i++) {
+               csig->params [pindex] = sig->params [i];
+               if (!sig->params [i]->byref) {
+                       csig->params [pindex] = mono_metadata_type_dup (NULL, csig->params [pindex]);
+                       csig->params [pindex]->byref = 1;
+               }
+               pindex ++;
+       }
+       /* Rgctx arg */
+       csig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
+       csig->param_count = pindex;
+
+       /* Create the signature for the normal callconv */
+       normal_sig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 2) * sizeof (MonoType*)));
+       memcpy (normal_sig, sig, mono_metadata_signature_size (sig));
+       normal_sig->param_count ++;
+       normal_sig->params [sig->param_count] = &mono_defaults.int_class->byval_arg;
+
+       // FIXME: Use shared signatures
+       mb = mono_mb_new (mono_defaults.object_class, "gsharedvt_out_sig", MONO_WRAPPER_UNKNOWN);
+
+#ifndef DISABLE_JIT
+       if (sig->ret->type != MONO_TYPE_VOID)
+               /* Load return address */
+               mono_mb_emit_ldarg (mb, sig->hasthis ? 1 : 0);
+
+       /* Make the call */
+       if (sig->hasthis)
+               mono_mb_emit_ldarg (mb, 0);
+       for (i = 0; i < sig->param_count; i++) {
+               if (sig->params [i]->byref) {
+                       mono_mb_emit_ldarg (mb, args_start + i);
+               } else {
+                       ldind_op = mono_type_to_ldind (sig->params [i]);
+                       mono_mb_emit_ldarg (mb, args_start + i);
+                       // FIXME:
+                       if (ldind_op == CEE_LDOBJ)
+                               mono_mb_emit_op (mb, CEE_LDOBJ, mono_class_from_mono_type (sig->params [i]));
+                       else
+                               mono_mb_emit_byte (mb, ldind_op);
+               }
+       }
+       /* Rgctx arg */
+       mono_mb_emit_ldarg (mb, args_start + sig->param_count);
+       mono_mb_emit_icon (mb, sizeof (gpointer));
+       mono_mb_emit_byte (mb, CEE_ADD);
+       mono_mb_emit_byte (mb, CEE_LDIND_I);
+       /* Method to call */
+       mono_mb_emit_ldarg (mb, args_start + sig->param_count);
+       mono_mb_emit_byte (mb, CEE_LDIND_I);
+       mono_mb_emit_calli (mb, normal_sig);
+       if (sig->ret->type != MONO_TYPE_VOID) {
+               /* Store return value */
+               stind_op = mono_type_to_stind (sig->ret);
+               // FIXME:
+               if (stind_op == CEE_STOBJ)
+                       mono_mb_emit_op (mb, CEE_STOBJ, mono_class_from_mono_type (sig->ret));
+               else
+                       mono_mb_emit_byte (mb, stind_op);
+       }
+       mono_mb_emit_byte (mb, CEE_RET);
+#endif
+
+       info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_GSHAREDVT_OUT_SIG);
+       info->d.gsharedvt.sig = sig;
+
+       res = mono_mb_create (mb, csig, sig->param_count + 16, info);
+
+       // FIXME: Locking
+       g_hash_table_insert (cache, sig, res);
+
+       return res;
+}
+
+MonoMethodSignature*
+mini_get_gsharedvt_out_sig_wrapper_signature (gboolean has_this, gboolean has_ret, int param_count)
+{
+       MonoMethodSignature *sig = g_malloc0 (sizeof (MonoMethodSignature) + (32 * sizeof (MonoType*)));
+       int i, pindex;
+
+       sig->ret = &mono_defaults.void_class->byval_arg;
+       sig->sentinelpos = -1;
+       pindex = 0;
+       if (has_this)
+               /* this */
+               sig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
+       if (has_ret)
+               /* vret */
+               sig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
+       for (i = 0; i < param_count; ++i)
+               /* byref arguments */
+               sig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
+       /* extra arg */
+       sig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
+       sig->param_count = pindex;
+
+       return sig;
+}
+
 /*
  * mini_get_gsharedvt_wrapper:
  *
@@ -1043,6 +1388,17 @@ mini_get_gsharedvt_wrapper (gboolean gsharedvt_in, gpointer addr, MonoMethodSign
                inited = TRUE;
        }
 
+       if (mono_llvm_only) {
+               MonoMethod *wrapper;
+
+               if (gsharedvt_in)
+                       wrapper = mini_get_gsharedvt_in_sig_wrapper (normal_sig);
+               else
+                       wrapper = mini_get_gsharedvt_out_sig_wrapper (normal_sig);
+               res = mono_compile_method (wrapper);
+               return res;
+       }
+
        memset (&tinfo, 0, sizeof (tinfo));
        tinfo.is_in = gsharedvt_in;
        tinfo.calli = calli;
@@ -1113,6 +1469,7 @@ static gpointer
 instantiate_info (MonoDomain *domain, MonoRuntimeGenericContextInfoTemplate *oti,
                                  MonoGenericContext *context, MonoClass *klass)
 {
+       MonoError error;
        gpointer data;
        gboolean temporary;
 
@@ -1161,15 +1518,56 @@ instantiate_info (MonoDomain *domain, MonoRuntimeGenericContextInfoTemplate *oti
        }
        case MONO_RGCTX_INFO_TYPE:
                return data;
-       case MONO_RGCTX_INFO_REFLECTION_TYPE:
-               return mono_type_get_object (domain, (MonoType *)data);
+       case MONO_RGCTX_INFO_REFLECTION_TYPE: {
+               MonoReflectionType *ret = mono_type_get_object_checked (domain, (MonoType *)data, &error);
+               mono_error_raise_exception (&error); /* FIXME don't raise here */
+
+               return ret;
+       }
        case MONO_RGCTX_INFO_METHOD:
                return data;
        case MONO_RGCTX_INFO_GENERIC_METHOD_CODE: {
+               MonoMethod *m = (MonoMethod*)data;
+               gpointer addr;
+               gpointer arg = NULL;
+
+               if (mono_llvm_only) {
+                       addr = mono_compile_method (m);
+                       addr = mini_add_method_wrappers_llvmonly (m, addr, FALSE, FALSE, &arg);
+
+                       /* Returns an ftndesc */
+                       return mini_create_llvmonly_ftndesc (domain, addr, arg);
+               } else {
+                       addr = mono_compile_method ((MonoMethod *)data);
+                       return mini_add_method_trampoline ((MonoMethod *)data, addr, mono_method_needs_static_rgctx_invoke ((MonoMethod *)data, FALSE), FALSE);
+               }
+       }
+       case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER: {
+               MonoMethod *m = (MonoMethod*)data;
                gpointer addr;
+               gpointer arg = NULL;
+
+               g_assert (mono_llvm_only);
+
+               addr = mono_compile_method (m);
+
+               MonoJitInfo *ji;
+               gboolean callee_gsharedvt;
 
-               addr = mono_compile_method ((MonoMethod *)data);
-               return mini_add_method_trampoline ((MonoMethod *)data, addr, mono_method_needs_static_rgctx_invoke ((MonoMethod *)data, FALSE), FALSE);
+               ji = mini_jit_info_table_find (mono_domain_get (), (char *)mono_get_addr_from_ftnptr (addr), NULL);
+               g_assert (ji);
+               callee_gsharedvt = mini_jit_info_is_gsharedvt (ji);
+               if (callee_gsharedvt)
+                       callee_gsharedvt = mini_is_gsharedvt_variable_signature (mono_method_signature (jinfo_get_method (ji)));
+               if (callee_gsharedvt) {
+                       /* No need for a wrapper */
+                       return mini_create_llvmonly_ftndesc (domain, addr, mini_method_get_rgctx (m));
+               } else {
+                       addr = mini_add_method_wrappers_llvmonly (m, addr, TRUE, FALSE, &arg);
+
+                       /* Returns an ftndesc */
+                       return mini_create_llvmonly_ftndesc (domain, addr, arg);
+               }
        }
        case MONO_RGCTX_INFO_VIRT_METHOD_CODE: {
                MonoJumpInfoVirtMethod *info = (MonoJumpInfoVirtMethod *)data;
@@ -1263,6 +1661,17 @@ instantiate_info (MonoDomain *domain, MonoRuntimeGenericContextInfoTemplate *oti
 
                return method->context.method_inst;
        }
+       case MONO_RGCTX_INFO_SIG_GSHAREDVT_IN_TRAMPOLINE_CALLI: {
+               MonoMethodSignature *gsig = (MonoMethodSignature *)oti->data;
+               MonoMethodSignature *sig = (MonoMethodSignature *)data;
+               gpointer addr;
+
+               /*
+                * This is an indirect call to the address passed by the caller in the rgctx reg.
+                */
+               addr = mini_get_gsharedvt_wrapper (TRUE, NULL, sig, gsig, -1, TRUE);
+               return addr;
+       }
        case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI: {
                MonoMethodSignature *gsig = (MonoMethodSignature *)oti->data;
                MonoMethodSignature *sig = (MonoMethodSignature *)data;
@@ -1337,14 +1746,29 @@ instantiate_info (MonoDomain *domain, MonoRuntimeGenericContextInfoTemplate *oti
                        sig = mono_method_signature (method);
                        gsig = call_sig;
 
-                       addr = mini_get_gsharedvt_wrapper (FALSE, addr, sig, gsig, vcall_offset, FALSE);
+                       if (mono_llvm_only) {
+                               if (mini_is_gsharedvt_variable_signature (call_sig)) {
+                                       /* The virtual case doesn't go through this code */
+                                       g_assert (!virtual_);
+
+                                       sig = mono_method_signature (jinfo_get_method (callee_ji));
+                                       gpointer out_wrapper = mini_get_gsharedvt_wrapper (FALSE, NULL, sig, gsig, -1, FALSE);
+                                       MonoFtnDesc *out_wrapper_arg = mini_create_llvmonly_ftndesc (domain, callee_ji->code_start, mini_method_get_rgctx (method));
+
+                                       /* Returns an ftndesc */
+                                       addr = mini_create_llvmonly_ftndesc (domain, out_wrapper, out_wrapper_arg);
+                               } else {
+                                       addr = mini_create_llvmonly_ftndesc (domain, addr, mini_method_get_rgctx (method));
+                               }
+                       } else {
+                               addr = mini_get_gsharedvt_wrapper (FALSE, addr, sig, gsig, vcall_offset, FALSE);
+                       }
 #if 0
                        if (virtual)
                                printf ("OUT-VCALL: %s\n", mono_method_full_name (method, TRUE));
                        else
                                printf ("OUT: %s\n", mono_method_full_name (method, TRUE));
 #endif
-                       //              } else if (!mini_is_gsharedvt_variable_signature (mono_method_signature (caller_method)) && callee_gsharedvt) {
                } else if (callee_gsharedvt) {
                        MonoMethodSignature *sig, *gsig;
 
@@ -1364,7 +1788,30 @@ instantiate_info (MonoDomain *domain, MonoRuntimeGenericContextInfoTemplate *oti
                         * FIXME: Optimize this.
                         */
 
-                       if (call_sig == mono_method_signature (method)) {
+                       if (mono_llvm_only) {
+                               /* Both wrappers receive an extra <addr, rgctx> argument */
+                               sig = mono_method_signature (method);
+                               gsig = mono_method_signature (jinfo_get_method (callee_ji));
+
+                               /* Return a function descriptor */
+
+                               if (mini_is_gsharedvt_variable_signature (call_sig)) {
+                                       /*
+                                        * This is not an optimization, but its needed, since the concrete signature 'sig'
+                                        * might not exist at all in IL, so the AOT compiler cannot generate the wrappers
+                                        * for it.
+                                        */
+                                       addr = mini_create_llvmonly_ftndesc (domain, callee_ji->code_start, mini_method_get_rgctx (method));
+                               } else if (mini_is_gsharedvt_variable_signature (gsig)) {
+                                       gpointer in_wrapper = mini_get_gsharedvt_wrapper (TRUE, callee_ji->code_start, sig, gsig, -1, FALSE);
+
+                                       gpointer in_wrapper_arg = mini_create_llvmonly_ftndesc (domain, callee_ji->code_start, mini_method_get_rgctx (method));
+
+                                       addr = mini_create_llvmonly_ftndesc (domain, in_wrapper, in_wrapper_arg);
+                               } else {
+                                       addr = mini_create_llvmonly_ftndesc (domain, addr, mini_method_get_rgctx (method));
+                               }
+                       } else if (call_sig == mono_method_signature (method)) {
                        } else {
                                sig = mono_method_signature (method);
                                gsig = mono_method_signature (jinfo_get_method (callee_ji)); 
@@ -1473,6 +1920,7 @@ mono_rgctx_info_type_to_str (MonoRgctxInfoType type)
        case MONO_RGCTX_INFO_METHOD: return "METHOD";
        case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO: return "GSHAREDVT_INFO";
        case MONO_RGCTX_INFO_GENERIC_METHOD_CODE: return "GENERIC_METHOD_CODE";
+       case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER: return "GSHAREDVT_OUT_WRAPPER";
        case MONO_RGCTX_INFO_CLASS_FIELD: return "CLASS_FIELD";
        case MONO_RGCTX_INFO_METHOD_RGCTX: return "METHOD_RGCTX";
        case MONO_RGCTX_INFO_METHOD_CONTEXT: return "METHOD_CONTEXT";
@@ -1485,6 +1933,7 @@ mono_rgctx_info_type_to_str (MonoRgctxInfoType type)
        case MONO_RGCTX_INFO_FIELD_OFFSET: return "FIELD_OFFSET";
        case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE: return "METHOD_GSHAREDVT_OUT_TRAMPOLINE";
        case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT: return "METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT";
+       case MONO_RGCTX_INFO_SIG_GSHAREDVT_IN_TRAMPOLINE_CALLI: return "SIG_GSHAREDVT_IN_TRAMPOLINE_CALLI";
        case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI: return "SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI";
        case MONO_RGCTX_INFO_MEMCPY: return "MEMCPY";
        case MONO_RGCTX_INFO_BZERO: return "BZERO";
@@ -1577,6 +2026,7 @@ info_equal (gpointer data1, gpointer data2, MonoRgctxInfoType info_type)
        case MONO_RGCTX_INFO_METHOD:
        case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO:
        case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
+       case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER:
        case MONO_RGCTX_INFO_CLASS_FIELD:
        case MONO_RGCTX_INFO_FIELD_OFFSET:
        case MONO_RGCTX_INFO_METHOD_RGCTX:
@@ -1585,6 +2035,7 @@ info_equal (gpointer data1, gpointer data2, MonoRgctxInfoType info_type)
        case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE:
        case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE:
        case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT:
+       case MONO_RGCTX_INFO_SIG_GSHAREDVT_IN_TRAMPOLINE_CALLI:
        case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI:
                return data1 == data2;
        case MONO_RGCTX_INFO_VIRT_METHOD_CODE:
@@ -2558,6 +3009,8 @@ mini_type_get_underlying_type (MonoType *type)
                return &mono_defaults.byte_class->byval_arg;
        case MONO_TYPE_CHAR:
                return &mono_defaults.uint16_class->byval_arg;
+       case MONO_TYPE_STRING:
+               return &mono_defaults.object_class->byval_arg;
        default:
                return type;
        }
@@ -2638,7 +3091,7 @@ gboolean
 mini_type_var_is_vt (MonoType *type)
 {
        if (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) {
-               return type->data.generic_param->gshared_constraint && type->data.generic_param->gshared_constraint->type == MONO_TYPE_VALUETYPE;
+               return type->data.generic_param->gshared_constraint && (type->data.generic_param->gshared_constraint->type == MONO_TYPE_VALUETYPE || type->data.generic_param->gshared_constraint->type == MONO_TYPE_GENERICINST);
        } else {
                g_assert_not_reached ();
                return FALSE;