[jit] Add support for unlimited size/number of arguments to the dyn call code (#5612)
authorZoltan Varga <vargaz@gmail.com>
Wed, 27 Sep 2017 01:23:56 +0000 (03:23 +0200)
committerGitHub <noreply@github.com>
Wed, 27 Sep 2017 01:23:56 +0000 (03:23 +0200)
* [jit] Add a mono_arch_dyn_call_get_buf_size () arch specific function which returns the size of the buffer required for a given dyn call. Use this instead of a fixed size buffer, to allow backends to use variable sized buffers later.

* [amd64] Add support for unlimited size/number of arguments to the dyncall code.

* [arm64] Add support for unlimited size/number of arguments to the dyncall code. Part of the fix for #59184.

mono/metadata/object-offsets.h
mono/mini/cpu-arm64.md
mono/mini/method-to-ir.c
mono/mini/mini-amd64.c
mono/mini/mini-amd64.h
mono/mini/mini-arm.c
mono/mini/mini-arm64.c
mono/mini/mini-arm64.h
mono/mini/mini-runtime.c
mono/mini/mini.h
mono/tests/runtime-invoke.cs

index 233f9528ed90ca0a34cdf5377989320fe0dc61c7..8968bf737f5b1a9219671cdf4d0473e92777e330 100644 (file)
@@ -232,6 +232,7 @@ DECL_OFFSET(MonoLMF, pc)
 DECL_OFFSET(MonoLMF, gregs)
 DECL_OFFSET(DynCallArgs, regs)
 DECL_OFFSET(DynCallArgs, fpregs)
+DECL_OFFSET(DynCallArgs, n_stackargs)
 DECL_OFFSET(DynCallArgs, n_fpargs)
 DECL_OFFSET(DynCallArgs, n_fpret)
 #endif
index 230a975dbe348bc43711c24276613744530c5e0f..8b1cf4118cc32f1f5a222e71fa92832c1523fec7 100644 (file)
@@ -360,7 +360,7 @@ long_conv_to_ovf_i4_2: dest:i src1:i src2:i len:36
 vcall2: len:40 clob:c
 vcall2_reg: src1:i len:40 clob:c
 vcall2_membase: src1:b len:40 clob:c
-dyn_call: src1:i src2:i len:192 clob:c
+dyn_call: src1:i src2:i len:216 clob:c
 
 # This is different from the original JIT opcodes
 float_beq: len:32
index 442ec3c0a5c9005eab5da957140d8c8851189215..3ac76cdfd8da9c4ea1327993de1364edac13c075 100644 (file)
@@ -11838,7 +11838,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                        cfg->dyn_call_var->flags |= MONO_INST_VOLATILE;
                                }
 
-                               /* Has to use a call inst since it local regalloc expects it */
+                               /* Has to use a call inst since local regalloc expects it */
                                MONO_INST_NEW_CALL (cfg, call, OP_DYN_CALL);
                                ins = (MonoInst*)call;
                                sp -= 2;
@@ -11847,6 +11847,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                MONO_ADD_INS (cfg->cbb, ins);
 
                                cfg->param_area = MAX (cfg->param_area, cfg->backend->dyn_call_param_area);
+                               /* OP_DYN_CALL might need to allocate a dynamically sized param area */
+                               cfg->flags |= MONO_CFG_HAS_ALLOCA;
 
                                ip += 2;
                                inline_costs += 10 * num_calls++;
index 949f8946ddd1c73cf2a3d56a303448eff7115656..43d0e17babbd6c4d5fa6509eac2eb389aade1999 100644 (file)
@@ -2374,6 +2374,7 @@ mono_arch_emit_setret (MonoCompile *cfg, MonoMethod *method, MonoInst *val)
 typedef struct {
        MonoMethodSignature *sig;
        CallInfo *cinfo;
+       int nstack_args;
 } ArchDynCallInfo;
 
 static gboolean
@@ -2400,10 +2401,7 @@ dyn_call_supported (MonoMethodSignature *sig, CallInfo *cinfo)
                case ArgInFloatSSEReg:
                case ArgInDoubleSSEReg:
                case ArgValuetypeInReg:
-                       break;
                case ArgOnStack:
-                       if (!(ainfo->offset + (ainfo->arg_size / 8) <= DYN_CALL_STACK_ARGS))
-                               return FALSE;
                        break;
                default:
                        return FALSE;
@@ -2426,6 +2424,7 @@ mono_arch_dyn_call_prepare (MonoMethodSignature *sig)
 {
        ArchDynCallInfo *info;
        CallInfo *cinfo;
+       int i;
 
        cinfo = get_call_info (NULL, sig);
 
@@ -2438,6 +2437,21 @@ mono_arch_dyn_call_prepare (MonoMethodSignature *sig)
        // FIXME: Preprocess the info to speed up get_dyn_call_args ().
        info->sig = sig;
        info->cinfo = cinfo;
+       info->nstack_args = 0;
+
+       for (i = 0; i < cinfo->nargs; ++i) {
+               ArgInfo *ainfo = &cinfo->args [i];
+               switch (ainfo->storage) {
+               case ArgOnStack:
+                       info->nstack_args = MAX (info->nstack_args, ainfo->offset + (ainfo->arg_size / 8));
+                       break;
+               default:
+                       break;
+               }
+       }
+       /* Align to 16 bytes */
+       if (info->nstack_args & 1)
+               info->nstack_args ++;
        
        return (MonoDynCallInfo*)info;
 }
@@ -2456,6 +2470,15 @@ mono_arch_dyn_call_free (MonoDynCallInfo *info)
        g_free (ainfo);
 }
 
+int
+mono_arch_dyn_call_get_buf_size (MonoDynCallInfo *info)
+{
+       ArchDynCallInfo *ainfo = (ArchDynCallInfo*)info;
+
+       /* Extend the 'regs' field dynamically */
+       return sizeof (DynCallArgs) + (ainfo->nstack_args * sizeof (mgreg_t));
+}
+
 #define PTR_TO_GREG(ptr) (mgreg_t)(ptr)
 #define GREG_TO_PTR(greg) (gpointer)(greg)
 
@@ -2474,7 +2497,7 @@ mono_arch_dyn_call_free (MonoDynCallInfo *info)
  * libffi.
  */
 void
-mono_arch_start_dyn_call (MonoDynCallInfo *info, gpointer **args, guint8 *ret, guint8 *buf, int buf_len)
+mono_arch_start_dyn_call (MonoDynCallInfo *info, gpointer **args, guint8 *ret, guint8 *buf)
 {
        ArchDynCallInfo *dinfo = (ArchDynCallInfo*)info;
        DynCallArgs *p = (DynCallArgs*)buf;
@@ -2491,10 +2514,9 @@ mono_arch_start_dyn_call (MonoDynCallInfo *info, gpointer **args, guint8 *ret, g
                param_reg_to_index_inited = 1;
        }
 
-       g_assert (buf_len >= sizeof (DynCallArgs));
-
        p->res = 0;
        p->ret = ret;
+       p->nstack_args = dinfo->nstack_args;
 
        arg_index = 0;
        greg = 0;
@@ -4649,9 +4671,10 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        code = emit_move_return_value (cfg, ins, code);
                        break;
                case OP_DYN_CALL: {
-                       int i;
+                       int i, limit_reg, index_reg, src_reg, dst_reg;
                        MonoInst *var = cfg->dyn_call_var;
                        guint8 *label;
+                       guint8 *buf [16];
 
                        g_assert (var->opcode == OP_REGOFFSET);
 
@@ -4672,15 +4695,38 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                                amd64_sse_movsd_reg_membase (code, i, AMD64_R11, MONO_STRUCT_OFFSET (DynCallArgs, fregs) + (i * sizeof (double)));
                        amd64_patch (label, code);
 
+                       /* Allocate param area */
+                       /* This doesn't need to be freed since OP_DYN_CALL is never called in a loop */
+                       amd64_mov_reg_membase (code, AMD64_RAX, AMD64_R11, MONO_STRUCT_OFFSET (DynCallArgs, nstack_args), 8);
+                       amd64_shift_reg_imm (code, X86_SHL, AMD64_RAX, 3);
+                       amd64_alu_reg_reg (code, X86_SUB, AMD64_RSP, AMD64_RAX);
                        /* Set stack args */
-                       for (i = 0; i < DYN_CALL_STACK_ARGS; ++i) {
-                               amd64_mov_reg_membase (code, AMD64_RAX, AMD64_R11, MONO_STRUCT_OFFSET (DynCallArgs, regs) + ((PARAM_REGS + i) * sizeof(mgreg_t)), sizeof(mgreg_t));
-                               amd64_mov_membase_reg (code, AMD64_RSP, i * sizeof (mgreg_t), AMD64_RAX, sizeof (mgreg_t));
-                       }
+                       /* rax/rcx/rdx/r8/r9 is scratch */
+                       limit_reg = AMD64_RAX;
+                       index_reg = AMD64_RCX;
+                       src_reg = AMD64_R8;
+                       dst_reg = AMD64_R9;
+                       amd64_mov_reg_membase (code, limit_reg, AMD64_R11, MONO_STRUCT_OFFSET (DynCallArgs, nstack_args), 8);
+                       amd64_mov_reg_imm (code, index_reg, 0);
+                       amd64_lea_membase (code, src_reg, AMD64_R11, MONO_STRUCT_OFFSET (DynCallArgs, regs) + ((PARAM_REGS) * sizeof(mgreg_t)));
+                       amd64_mov_reg_reg (code, dst_reg, AMD64_RSP, 8);
+                       buf [0] = code;
+                       x86_jump8 (code, 0);
+                       buf [1] = code;
+                       amd64_mov_reg_membase (code, AMD64_RDX, src_reg, 0, 8);
+                       amd64_mov_membase_reg (code, dst_reg, 0, AMD64_RDX, 8);
+                       amd64_alu_reg_imm (code, X86_ADD, index_reg, 1);
+                       amd64_alu_reg_imm (code, X86_ADD, src_reg, 8);
+                       amd64_alu_reg_imm (code, X86_ADD, dst_reg, 8);
+                       amd64_patch (buf [0], code);
+                       amd64_alu_reg_reg (code, X86_CMP, index_reg, limit_reg);
+                       buf [2] = code;
+                       x86_branch8 (code, X86_CC_LT, 0, FALSE);
+                       amd64_patch (buf [2], buf [1]);
 
                        /* Set argument registers */
                        for (i = 0; i < PARAM_REGS; ++i)
-                               amd64_mov_reg_membase (code, param_regs [i], AMD64_R11, i * sizeof(mgreg_t), sizeof(mgreg_t));
+                               amd64_mov_reg_membase (code, param_regs [i], AMD64_R11, MONO_STRUCT_OFFSET (DynCallArgs, regs) + (i * sizeof(mgreg_t)), sizeof(mgreg_t));
                        
                        /* Make the call */
                        amd64_call_reg (code, AMD64_R10);
index f09f285a17bb26b132a9e30ff6710db8b37ad020..ba8466dfebeaa1d47b59d994c7b2995cb48e3929 100644 (file)
@@ -270,15 +270,15 @@ typedef struct {
        gpointer bp_addrs [MONO_ZERO_LEN_ARRAY];
 } SeqPointInfo;
 
-#define DYN_CALL_STACK_ARGS 6
-
 typedef struct {
-       mgreg_t regs [PARAM_REGS + DYN_CALL_STACK_ARGS];
        mgreg_t res;
        guint8 *ret;
        double fregs [8];
        mgreg_t has_fp;
+       mgreg_t nstack_args;
        guint8 buffer [256];
+       /* This should come last as the structure is dynamically extended */
+       mgreg_t regs [PARAM_REGS];
 } DynCallArgs;
 
 typedef enum {
@@ -429,7 +429,7 @@ typedef struct {
 
 #define MONO_ARCH_GSHARED_SUPPORTED 1
 #define MONO_ARCH_DYN_CALL_SUPPORTED 1
-#define MONO_ARCH_DYN_CALL_PARAM_AREA (DYN_CALL_STACK_ARGS * 8)
+#define MONO_ARCH_DYN_CALL_PARAM_AREA 0
 
 #define MONO_ARCH_LLVM_SUPPORTED 1
 #define MONO_ARCH_HAVE_CARD_TABLE_WBARRIER 1
index 1e56345f03d271d03e69ab814e3cfef87f2f2243..44626141830fe9f5ff2c42f28ae54de1c668f0ee 100644 (file)
@@ -2753,16 +2753,20 @@ mono_arch_dyn_call_free (MonoDynCallInfo *info)
        g_free (ainfo);
 }
 
+int
+mono_arch_dyn_call_get_buf_size (MonoDynCallInfo *info)
+{
+       return sizeof (DynCallArgs);
+}
+
 void
-mono_arch_start_dyn_call (MonoDynCallInfo *info, gpointer **args, guint8 *ret, guint8 *buf, int buf_len)
+mono_arch_start_dyn_call (MonoDynCallInfo *info, gpointer **args, guint8 *ret, guint8 *buf)
 {
        ArchDynCallInfo *dinfo = (ArchDynCallInfo*)info;
        DynCallArgs *p = (DynCallArgs*)buf;
        int arg_index, greg, i, j, pindex;
        MonoMethodSignature *sig = dinfo->sig;
 
-       g_assert (buf_len >= sizeof (DynCallArgs));
-
        p->res = 0;
        p->ret = ret;
        p->has_fpregs = 0;
index 9edb57aa9ef87da6594dea1b82e87867f7ee5dfe..6192f26b9cf042e653ad287c859bb881aafebca0 100644 (file)
@@ -1381,9 +1381,6 @@ dyn_call_supported (CallInfo *cinfo, MonoMethodSignature *sig)
 {
        int i;
 
-       if (sig->hasthis + sig->param_count > PARAM_REGS + DYN_CALL_STACK_ARGS)
-               return FALSE;
-
        // FIXME: Add more cases
        switch (cinfo->ret.storage) {
        case ArgNone:
@@ -1412,10 +1409,7 @@ dyn_call_supported (CallInfo *cinfo, MonoMethodSignature *sig)
                case ArgInFRegR4:
                case ArgHFA:
                case ArgVtypeByRef:
-                       break;
                case ArgOnStack:
-                       if (ainfo->offset >= DYN_CALL_STACK_ARGS * sizeof (mgreg_t))
-                               return FALSE;
                        break;
                default:
                        return FALSE;
@@ -1473,6 +1467,15 @@ mono_arch_dyn_call_free (MonoDynCallInfo *info)
        g_free (ainfo);
 }
 
+int
+mono_arch_dyn_call_get_buf_size (MonoDynCallInfo *info)
+{
+       ArchDynCallInfo *ainfo = (ArchDynCallInfo*)info;
+
+       g_assert (ainfo->cinfo->stack_usage % MONO_ARCH_FRAME_ALIGNMENT == 0);
+       return sizeof (DynCallArgs) + ainfo->cinfo->stack_usage;
+}
+
 static double
 bitcast_r4_to_r8 (float f)
 {
@@ -1490,7 +1493,7 @@ bitcast_r8_to_r4 (double f)
 }
 
 void
-mono_arch_start_dyn_call (MonoDynCallInfo *info, gpointer **args, guint8 *ret, guint8 *buf, int buf_len)
+mono_arch_start_dyn_call (MonoDynCallInfo *info, gpointer **args, guint8 *ret, guint8 *buf)
 {
        ArchDynCallInfo *dinfo = (ArchDynCallInfo*)info;
        DynCallArgs *p = (DynCallArgs*)buf;
@@ -1499,12 +1502,11 @@ mono_arch_start_dyn_call (MonoDynCallInfo *info, gpointer **args, guint8 *ret, g
        CallInfo *cinfo = dinfo->cinfo;
        int buffer_offset = 0;
 
-       g_assert (buf_len >= sizeof (DynCallArgs));
-
        p->res = 0;
        p->ret = ret;
        p->n_fpargs = dinfo->n_fpargs;
        p->n_fpret = dinfo->n_fpret;
+       p->n_stackargs = cinfo->stack_usage / sizeof (mgreg_t);
 
        arg_index = 0;
        greg = 0;
@@ -4175,14 +4177,35 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                                code = emit_ldrfpx (code, ARMREG_D0 + i, ARMREG_LR, MONO_STRUCT_OFFSET (DynCallArgs, fpregs) + (i * 8));
                        arm_patch_rel (labels [0], code, MONO_R_ARM64_BCC);
 
+                       /* Allocate callee area */
+                       code = emit_ldrx (code, ARMREG_R0, ARMREG_LR, MONO_STRUCT_OFFSET (DynCallArgs, n_stackargs));
+                       arm_lslw (code, ARMREG_R0, ARMREG_R0, 3);
+                       arm_movspx (code, ARMREG_R1, ARMREG_SP);
+                       arm_subx (code, ARMREG_R1, ARMREG_R1, ARMREG_R0);
+                       arm_movspx (code, ARMREG_SP, ARMREG_R1);
+
                        /* Set stack args */
-                       for (i = 0; i < DYN_CALL_STACK_ARGS; ++i) {
-                               code = emit_ldrx (code, ARMREG_R0, ARMREG_LR, MONO_STRUCT_OFFSET (DynCallArgs, regs) + ((PARAM_REGS + 1 + i) * sizeof (mgreg_t)));
-                               code = emit_strx (code, ARMREG_R0, ARMREG_SP, i * sizeof (mgreg_t));
-                       }
+                       /* R1 = limit */
+                       code = emit_ldrx (code, ARMREG_R1, ARMREG_LR, MONO_STRUCT_OFFSET (DynCallArgs, n_stackargs));
+                       /* R2 = pointer into 'regs' */
+                       code = emit_imm (code, ARMREG_R2, MONO_STRUCT_OFFSET (DynCallArgs, regs) + ((PARAM_REGS + 1) * sizeof (mgreg_t)));
+                       arm_addx (code, ARMREG_R2, ARMREG_LR, ARMREG_R2);
+                       /* R3 = pointer to stack */
+                       arm_movspx (code, ARMREG_R3, ARMREG_SP);
+                       labels [0] = code;
+                       arm_b (code, code);
+                       labels [1] = code;
+                       code = emit_ldrx (code, ARMREG_R5, ARMREG_R2, 0);
+                       code = emit_strx (code, ARMREG_R5, ARMREG_R3, 0);
+                       code = emit_addx_imm (code, ARMREG_R2, ARMREG_R2, sizeof (mgreg_t));
+                       code = emit_addx_imm (code, ARMREG_R3, ARMREG_R3, sizeof (mgreg_t));
+                       code = emit_subx_imm (code, ARMREG_R1, ARMREG_R1, 1);
+                       arm_patch_rel (labels [0], code, MONO_R_ARM64_B);
+                       arm_cmpw (code, ARMREG_R1, ARMREG_RZR);
+                       arm_bcc (code, ARMCOND_GT, labels [1]);
 
                        /* Set argument registers + r8 */
-                       code = mono_arm_emit_load_regarray (code, 0x1ff, ARMREG_LR, 0);
+                       code = mono_arm_emit_load_regarray (code, 0x1ff, ARMREG_LR, MONO_STRUCT_OFFSET (DynCallArgs, regs));
 
                        /* Make the call */
                        arm_blrx (code, ARMREG_IP1);
index 6268a1064de9266f034eaa7b72f1a3b34a722560..d14301ace80d56e480a728f919b7d198351d05af 100644 (file)
@@ -89,16 +89,15 @@ typedef struct {
 #define PARAM_REGS 8
 #define FP_PARAM_REGS 8
 
-#define DYN_CALL_STACK_ARGS 6
-
 typedef struct {
-       /* The +1 is for r8 */
-       mgreg_t regs [PARAM_REGS + 1 + DYN_CALL_STACK_ARGS];
        mgreg_t res, res2;
        guint8 *ret;
        double fpregs [FP_PARAM_REGS];
-       int n_fpargs, n_fpret;
+       int n_fpargs, n_fpret, n_stackargs;
        guint8 buffer [256];
+       /* This should come last as the structure is dynamically extended */
+       /* The +1 is for r8 */
+       mgreg_t regs [PARAM_REGS + 1];
 } DynCallArgs;
 
 typedef struct {
@@ -141,7 +140,7 @@ typedef struct {
 #define MONO_ARCH_HAVE_EXCEPTIONS_INIT 1
 #define MONO_ARCH_HAVE_GET_TRAMPOLINES 1
 #define MONO_ARCH_DYN_CALL_SUPPORTED 1
-#define MONO_ARCH_DYN_CALL_PARAM_AREA (DYN_CALL_STACK_ARGS * 8)
+#define MONO_ARCH_DYN_CALL_PARAM_AREA 0
 #define MONO_ARCH_SOFT_DEBUG_SUPPORTED 1
 #define MONO_ARCH_GSHAREDVT_SUPPORTED 1
 #define MONO_ARCH_HAVE_SETUP_RESUME_FROM_SIGNAL_HANDLER_CTX 1
index 90d695ae2d63a1028762ac33317db4a9c4f1c541..b433a360d97e7a1cd83f9a9d7989b97105d05fb7 100644 (file)
@@ -2431,8 +2431,11 @@ create_runtime_invoke_info (MonoDomain *domain, MonoMethod *method, gpointer com
                if (mono_class_is_contextbound (method->klass) || !info->compiled_method)
                        supported = FALSE;
 
-               if (supported)
+               if (supported) {
                        info->dyn_call_info = mono_arch_dyn_call_prepare (sig);
+                       if (debug_options.dyn_runtime_invoke)
+                               g_assert (info->dyn_call_info);
+               }
        }
 #endif
 
@@ -2734,8 +2737,8 @@ mono_jit_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObjec
                MonoMethodSignature *sig = mono_method_signature (method);
                gpointer *args;
                static RuntimeInvokeDynamicFunction dyn_runtime_invoke;
-               int i, pindex;
-               guint8 buf [512];
+               int i, pindex, buf_size;
+               guint8 *buf;
                guint8 retval [256];
 
                if (!dyn_runtime_invoke) {
@@ -2764,7 +2767,11 @@ mono_jit_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObjec
 
                //printf ("M: %s\n", mono_method_full_name (method, TRUE));
 
-               mono_arch_start_dyn_call (info->dyn_call_info, (gpointer**)args, retval, buf, sizeof (buf));
+               buf_size = mono_arch_dyn_call_get_buf_size (info->dyn_call_info);
+               buf = g_alloca (buf_size);
+               g_assert (buf);
+
+               mono_arch_start_dyn_call (info->dyn_call_info, (gpointer**)args, retval, buf);
 
                dyn_runtime_invoke (buf, exc, info->compiled_method);
                mono_arch_finish_dyn_call (info->dyn_call_info, buf);
index cf53f940c755bb716b308f42b504dc3a01687000..539125b2f387005ca812adf5d3d01d7c70e42f37 100644 (file)
@@ -2783,7 +2783,8 @@ void      mono_arch_emit_outarg_vt              (MonoCompile *cfg, MonoInst *ins
 void      mono_arch_emit_setret                 (MonoCompile *cfg, MonoMethod *method, MonoInst *val);
 MonoDynCallInfo *mono_arch_dyn_call_prepare     (MonoMethodSignature *sig);
 void      mono_arch_dyn_call_free               (MonoDynCallInfo *info);
-void      mono_arch_start_dyn_call              (MonoDynCallInfo *info, gpointer **args, guint8 *ret, guint8 *buf, int buf_len);
+int       mono_arch_dyn_call_get_buf_size       (MonoDynCallInfo *info);
+void      mono_arch_start_dyn_call              (MonoDynCallInfo *info, gpointer **args, guint8 *ret, guint8 *buf);
 void      mono_arch_finish_dyn_call             (MonoDynCallInfo *info, guint8 *buf);
 MonoInst *mono_arch_emit_inst_for_method        (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args);
 void      mono_arch_decompose_opts              (MonoCompile *cfg, MonoInst *ins);
index 23fec490693ff134df0462fe9125fc60160bbd86..2c34478f83d63d1931908c51f24d1384fbc2f1db 100644 (file)
@@ -29,6 +29,11 @@ enum Enum2
        D
 }
 
+struct AStruct {
+       public int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10;
+       public int a11, a12, a13, a14, a15, a16, a17, a18, a19, a20;
+}
+
 class Tests
 {
        public static Enum1 return_enum1 () {
@@ -47,6 +52,10 @@ class Tests
                return UInt64.MaxValue - 5;
        }
 
+       public static object return_t<T> (T t) {
+               return (object)t;
+       }
+
        static int Main (string[] args)
        {
                return TestDriver.RunTests (typeof (Tests), args);
@@ -279,4 +288,19 @@ class Tests
         else
             return 1;
     }
+
+       public static int test_0_large_arg ()
+       {
+               var arg = new AStruct ();
+               arg.a1 = 1;
+               arg.a2 = 2;
+               arg.a3 = 3;
+               arg.a20 = 20;
+               var res = typeof (Tests).GetMethod ("return_t").MakeGenericMethod (new Type [] { typeof (AStruct) }).Invoke (null, new object [] { arg });
+               var arg2 = (AStruct)res;
+               if (arg2.a20 == 20)
+                       return 0;
+               else
+                       return 1;
+       }
 }