2008-03-14 Mark Probst <mark.probst@gmail.com>
authorMark Probst <mark.probst@gmail.com>
Fri, 14 Mar 2008 19:58:04 +0000 (19:58 -0000)
committerMark Probst <mark.probst@gmail.com>
Fri, 14 Mar 2008 19:58:04 +0000 (19:58 -0000)
* mini.c, mini.h: Share static generic code by passing it an
implicit argument pointing to the runtime generic context.

* mini-ops.h, inssel.brg, inssel-long.brg, inssel-long32.brg,
inssel-long32-mips.brg: New opcodes for calling shared static,
which need to be passed the runtime generic context.

* mini-amd64.c, mini-x86.c: Save the runtime generic context
argument on the stack in the prologue if needed.  New function for
finding the runtime generic context argument from the registers
saved by the trampoline.

* mini-amd64.h, mini-x86.h: Specify which register to use for the
runtime generic context argument.

* tramp-amd64.c: Also restore the register used for the runtime
generic context argument.

* mini-trampoline.c: Resolve shared static calls by consulting the
runtime generic context via the new argument.

* generic-sharing.c: Add an argument to sharability-checking
functions that specifies whether type variables should be treated
as sharable type arguments.

* inssel-x86.brg: Removed a superfluous, buggy rule.

* graph.c, local-propagation.c, aliasing.c: Added the new opcodes
to switches.

2008-03-14  Mark Probst  <mark.probst@gmail.com>

* marshal.c, marshal.h, metadata-internals.h, image.c,
wrapper-types.h: New wrapper for invoking a shared static method
without having to pass the runtime generic context argument.

2008-03-14  Mark Probst  <mark.probst@gmail.com>

* generics-sharing.2.cs: New tests for static methods.

* generic-inlining.2.cs: Added.  Tests generic method inlining.

* shared-generic-synchronized.2.cs: Added.  Tests shared
synchronized methods.

* Makefile.am: Added the new tests.  New target for running the
generic code sharing with the optimization enabled.

svn path=/trunk/mono/; revision=98345

30 files changed:
mono/metadata/ChangeLog
mono/metadata/image.c
mono/metadata/marshal.c
mono/metadata/marshal.h
mono/metadata/metadata-internals.h
mono/metadata/wrapper-types.h
mono/mini/ChangeLog
mono/mini/aliasing.c
mono/mini/generic-sharing.c
mono/mini/graph.c
mono/mini/inssel-long.brg
mono/mini/inssel-long32-mips.brg
mono/mini/inssel-long32.brg
mono/mini/inssel-x86.brg
mono/mini/inssel.brg
mono/mini/local-propagation.c
mono/mini/mini-amd64.c
mono/mini/mini-amd64.h
mono/mini/mini-ops.h
mono/mini/mini-trampolines.c
mono/mini/mini-x86.c
mono/mini/mini-x86.h
mono/mini/mini.c
mono/mini/mini.h
mono/mini/tramp-amd64.c
mono/tests/ChangeLog
mono/tests/Makefile.am
mono/tests/generic-inlining.2.cs [new file with mode: 0644]
mono/tests/generics-sharing.2.cs
mono/tests/shared-generic-synchronized.2.cs [new file with mode: 0644]

index f47447c8046569d042ded71f7118d1a3a48d9c67..5b6efa52f6a0744d432ade4299079c961175a594 100644 (file)
@@ -1,3 +1,9 @@
+2008-03-14  Mark Probst  <mark.probst@gmail.com>
+
+       * marshal.c, marshal.h, metadata-internals.h, image.c,
+       wrapper-types.h: New wrapper for invoking a shared static method
+       without having to pass the runtime generic context argument.
+
 2008-03-14 Rodrigo Kumpera  <rkumpera@novell.com>
 
        * icall-def.h: Add missing function PerformanceCounterCategory::GetInstanceNames.
index 7290024f71f785ce12d3560c204169d92641e8bd..3a730bfea065fd790bd76171647a2affbfe8308a 100644 (file)
@@ -1296,6 +1296,8 @@ mono_image_close (MonoImage *image)
        g_hash_table_destroy (image->isinst_cache);
        g_hash_table_destroy (image->castclass_cache);
        g_hash_table_destroy (image->proxy_isinst_cache);
+       if (image->static_rgctx_invoke_cache)
+               g_hash_table_destroy (image->static_rgctx_invoke_cache);
 
        /* The ownership of signatures is not well defined */
        //g_hash_table_foreach (image->memberref_signatures, free_mr_signatures, NULL);
index 9db492ef9d98711298f10afcd42104ad2f74acfd..9b04becdb0ef48b9136834e05930f3d29e905e56 100644 (file)
@@ -4920,6 +4920,58 @@ handle_enum:
        return res;     
 }
 
+/*
+ * mono_marshal_get_static_rgctx_invoke:
+ * @method: a method
+ *
+ * Generates a wrapper for calling a static method.  We need this for
+ * ldftn when we do generic code sharing.  Instead of producing the
+ * address of the static method we produce the address of a wrapper
+ * for the method because the wrapper passes the runtime generic
+ * context argument which calli cannot do.
+ */
+MonoMethod *
+mono_marshal_get_static_rgctx_invoke (MonoMethod *method)
+{
+       MonoMethodBuilder *mb;
+       MonoMethod *res;
+       MonoClass *target_klass = method->klass;
+       MonoMethodSignature *sig = mono_method_signature (method);
+       int i;
+       char *name;
+       GHashTable *cache;
+       MonoImage *image = method->klass->image;
+
+       if (!(cache = image->static_rgctx_invoke_cache)) {
+               mono_marshal_lock ();
+               if (!(cache = image->static_rgctx_invoke_cache)) {
+                       cache = image->static_rgctx_invoke_cache =
+                               g_hash_table_new (mono_aligned_addr_hash, NULL);
+               }
+               mono_marshal_unlock ();
+       }
+
+       if ((res = mono_marshal_find_in_cache (cache, method)))
+               return res;
+
+       name = mono_signature_to_name (mono_method_signature (method), "static_rgctx_invoke");
+       mb = mono_mb_new (target_klass, name, MONO_WRAPPER_STATIC_RGCTX_INVOKE);
+       g_free (name);
+
+       for (i = 0; i < sig->param_count; i++)
+               mono_mb_emit_ldarg (mb, i);
+       mono_mb_emit_op (mb, CEE_CALL, method);
+       mono_mb_emit_byte (mb, CEE_RET);
+
+       res = mono_mb_create_and_cache (cache, method, mb, mono_method_signature (method), sig->param_count + 4);
+       res->skip_visibility = TRUE;
+       res->flags = method->flags;
+
+       mono_mb_free (mb);
+
+       return res;
+}
+
 static void
 mono_mb_emit_auto_layout_exception (MonoMethodBuilder *mb, MonoClass *klass)
 {
index b19ddbe5b85ad081bb7b31382878f4018794dc3a..32bde8e768d20dd01aade2319c232ed4c22a8850 100644 (file)
@@ -134,6 +134,9 @@ mono_marshal_get_delegate_invoke (MonoMethod *method, MonoDelegate *del) MONO_IN
 MonoMethod *
 mono_marshal_get_runtime_invoke (MonoMethod *method) MONO_INTERNAL;
 
+MonoMethod *
+mono_marshal_get_static_rgctx_invoke (MonoMethod *method) MONO_INTERNAL;
+
 MonoMethod *
 mono_marshal_get_managed_wrapper (MonoMethod *method, MonoClass *delegate_klass, MonoObject *this) MONO_INTERNAL;
 
index 3fd63b2f73ed30c0273734d361310269e188f858..6737d9d113192542121f3242bc4b79721cf793bc 100644 (file)
@@ -188,6 +188,7 @@ struct _MonoImage {
        GHashTable *unbox_wrapper_cache;
        GHashTable *cominterop_invoke_cache;
        GHashTable *cominterop_wrapper_cache;
+       GHashTable *static_rgctx_invoke_cache; /* LOCKING: marshal lock */
 
        /*
         * indexed by MonoClass pointers
index 9da0d59ed66e28bd13b7b225e95414fa38665a9a..53adbf786b027e378bd3e6483681ccb71445e88f 100644 (file)
@@ -27,3 +27,4 @@ WRAPPER(UNKNOWN, "unknown")
 WRAPPER(COMINTEROP_INVOKE, "cominterop-invoke")
 WRAPPER(COMINTEROP, "cominterop")
 WRAPPER(ALLOC, "alloc")
+WRAPPER(STATIC_RGCTX_INVOKE, "static-rgctx-invoke")
index 4a0b3795214025d5253ddf6da87a42766f4356a8..f200dd14da544771d09950370d553bd3d5540182 100644 (file)
@@ -1,3 +1,35 @@
+2008-03-14  Mark Probst  <mark.probst@gmail.com>
+
+       * mini.c, mini.h: Share static generic code by passing it an
+       implicit argument pointing to the runtime generic context.
+
+       * mini-ops.h, inssel.brg, inssel-long.brg, inssel-long32.brg,
+       inssel-long32-mips.brg: New opcodes for calling shared static,
+       which need to be passed the runtime generic context.
+
+       * mini-amd64.c, mini-x86.c: Save the runtime generic context
+       argument on the stack in the prologue if needed.  New function for
+       finding the runtime generic context argument from the registers
+       saved by the trampoline.
+
+       * mini-amd64.h, mini-x86.h: Specify which register to use for the
+       runtime generic context argument.
+
+       * tramp-amd64.c: Also restore the register used for the runtime
+       generic context argument.
+
+       * mini-trampoline.c: Resolve shared static calls by consulting the
+       runtime generic context via the new argument.
+
+       * generic-sharing.c: Add an argument to sharability-checking
+       functions that specifies whether type variables should be treated
+       as sharable type arguments.
+
+       * inssel-x86.brg: Removed a superfluous, buggy rule.
+
+       * graph.c, local-propagation.c, aliasing.c: Added the new opcodes
+       to switches.
+
 2008-03-14  Martin Baulig  <martin@ximian.com>
 
        * debug-debugger.c (main_thread_handler): Call
index b30e21cbcefbb4ea979c354bc5af047fa4a8724d..b34da1fa6cbda7c53db168689747e4769bf924f4 100644 (file)
@@ -168,7 +168,12 @@ print_tree_node (MonoInst *tree) {
        case OP_VCALLVIRT:
        case OP_VOIDCALL:
        case OP_VOIDCALLVIRT:
-       case OP_TRAMPCALL_VTABLE: {
+       case OP_TRAMPCALL_VTABLE:
+       case OP_CALL_RGCTX:
+       case OP_FCALL_RGCTX:
+       case OP_VOIDCALL_RGCTX:
+       case OP_LCALL_RGCTX:
+       case OP_VCALL_RGCTX: {
                MonoCallInst *call = (MonoCallInst*)tree;
                if (call->method)
                        printf ("[%s]", call->method->name);
index f218ca6e24d650eed98a1d062e03e446b77426df..98c0c642516f9bc8b20ed8f6af64766fbb8286f6 100644 (file)
@@ -59,13 +59,22 @@ mono_method_check_context_used (MonoMethod *method)
 }
 
 static gboolean
-generic_inst_is_sharable (MonoGenericInst *inst)
+generic_inst_is_sharable (MonoGenericInst *inst, gboolean allow_type_vars)
 {
        int i;
 
        for (i = 0; i < inst->type_argc; ++i) {
-               if (!MONO_TYPE_IS_REFERENCE (inst->type_argv [i]))
-                       return FALSE;
+               MonoType *type = inst->type_argv [i];
+               int type_type;
+
+               if (MONO_TYPE_IS_REFERENCE (type))
+                       continue;
+
+               type_type = mono_type_get_type (type);
+               if (allow_type_vars && (type_type == MONO_TYPE_VAR || type_type == MONO_TYPE_MVAR))
+                       continue;
+
+               return FALSE;
        }
 
        return TRUE;
@@ -79,14 +88,14 @@ generic_inst_is_sharable (MonoGenericInst *inst)
  * is sharable iff all of its type arguments are reference type.
  */
 gboolean
-mono_generic_context_is_sharable (MonoGenericContext *context)
+mono_generic_context_is_sharable (MonoGenericContext *context, gboolean allow_type_vars)
 {
        g_assert (context->class_inst || context->method_inst);
 
-       if (context->class_inst && !generic_inst_is_sharable (context->class_inst))
+       if (context->class_inst && !generic_inst_is_sharable (context->class_inst, allow_type_vars))
                return FALSE;
 
-       if (context->method_inst && !generic_inst_is_sharable (context->method_inst))
+       if (context->method_inst && !generic_inst_is_sharable (context->method_inst, allow_type_vars))
                return FALSE;
 
        return TRUE;
@@ -123,7 +132,7 @@ mono_method_is_generic_sharable_impl (MonoMethod *method)
                MonoMethodInflated *inflated = (MonoMethodInflated*)method;
                MonoGenericContext *context = &inflated->context;
 
-               if (!mono_generic_context_is_sharable (context))
+               if (!mono_generic_context_is_sharable (context, FALSE))
                        return FALSE;
 
                g_assert (inflated->declaring);
@@ -137,7 +146,7 @@ mono_method_is_generic_sharable_impl (MonoMethod *method)
        }
 
        if (method->klass->generic_class) {
-               if (!mono_generic_context_is_sharable (&method->klass->generic_class->context))
+               if (!mono_generic_context_is_sharable (&method->klass->generic_class->context, FALSE))
                        return FALSE;
 
                g_assert (method->klass->generic_class->container_class &&
index bdd403805e539f6101ab8c990c51532ed7834f5b..7cef00fbee7b9793d075f49340cf47aff8f9df13 100644 (file)
@@ -236,6 +236,11 @@ mono_print_label (FILE *fp, MonoInst *tree) {
        case OP_VCALL_REG:
        case OP_VOIDCALL_REG:
        case OP_TRAMPCALL_VTABLE:
+       case OP_CALL_RGCTX:
+       case OP_FCALL_RGCTX:
+       case OP_VOIDCALL_RGCTX:
+       case OP_LCALL_RGCTX:
+       case OP_VCALL_RGCTX:
                mono_print_label (fp, tree->inst_left);
                break;
        case CEE_BNE_UN:
index d9d430987988f2f1a7aa2b811b427a868af95309..6a5de805b5ba4c5713ee6ab5ed6078690f198e6d 100644 (file)
@@ -350,6 +350,13 @@ reg: OP_LCALL {
        mono_bblock_add_inst (s->cbb, tree);
 }
 
+reg: OP_LCALL_RGCTX (reg) {
+       emit_rgctx_argument (s, tree, state->left->reg1, OP_LCALL);
+
+       tree->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, &call->inst);
+}
+
 reg: OP_LCALL_REG (reg) {
        tree->sreg1 = state->left->reg1;
        tree->dreg = state->reg1;
index c8e6baf874490b69ccda2cf361bcef91a654285e..5f001ef323f74ef60c914d263fecf647f6efc22f 100644 (file)
@@ -1179,6 +1179,13 @@ lreg: OP_LCALL {
        mono_bblock_add_inst (s->cbb, tree);
 }
 
+lreg: OP_LCALL_RGCTX (reg) {
+       emit_rgctx_argument (s, tree, state->left->reg1, OP_LCALL);
+
+       tree->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
 lreg: OP_LCALL_REG (reg) {
        tree->sreg1 = state->left->reg1;
        tree->dreg = state->reg1;
index 2ab8a223f4ca696fdbdc9bae496f5975944e1f98..db51d362b5ec7b04000e141878f34f4c6cdc00ae 100644 (file)
@@ -827,6 +827,13 @@ lreg: OP_LCALL {
        mono_bblock_add_inst (s->cbb, tree);
 }
 
+lreg: OP_LCALL_RGCTX (reg) {
+       emit_rgctx_argument (s, tree, state->left->reg1, OP_LCALL);
+
+       tree->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
 lreg: OP_LCALL_REG (reg) {
        tree->sreg1 = state->left->reg1;
        tree->dreg = state->reg1;
index 46e58e020efe88c96374d58413006b13a0b45e6b..863f9d577c0d8f80f9e9ce52d0b8561c9bed03d0 100644 (file)
@@ -740,8 +740,7 @@ reg: OP_ATOMIC_EXCHANGE_I4 (base, reg) {
 
 # Optimized call instructions
 reg: OP_CALL_REG (CEE_LDIND_I (base)),
-freg: OP_FCALL_REG (CEE_LDIND_I (base)),
-reg: OP_LCALL_REG (CEE_LDIND_I (base)) {
+freg: OP_FCALL_REG (CEE_LDIND_I (base)) {
        tree->opcode = call_reg_to_call_membase (tree->opcode);
        tree->inst_basereg = state->left->left->tree->inst_basereg;
        tree->inst_offset = state->left->left->tree->inst_offset;
index 8f054c3cc4237b534c45979c252f4e575ce73b91..58d6bb011883eaea6ae4133923b6190f94bda82e 100644 (file)
@@ -938,6 +938,33 @@ stmt: OP_TRAMPCALL_VTABLE (reg) {
        #endif
 }
 
+reg: OP_CALL_RGCTX (reg) {
+       emit_rgctx_argument (s, tree, state->left->reg1, OP_CALL);
+
+       tree->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+freg: OP_FCALL_RGCTX (reg) {
+       emit_rgctx_argument (s, tree, state->left->reg1, OP_FCALL);
+
+       tree->dreg = state->reg1;
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_VOIDCALL_RGCTX (reg) {
+       emit_rgctx_argument (s, tree, state->left->reg1, OP_VOIDCALL);
+
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_VCALL_RGCTX (reg, reg) {
+       emit_rgctx_argument (s, tree, state->right->reg1, OP_VCALL);
+
+       mono_arch_emit_this_vret_args (s, (MonoCallInst*)tree, -1, -1, state->left->reg1);
+       mono_bblock_add_inst (s->cbb, tree);
+}
+
 stmt: OP_SAVE_LMF,
 stmt: OP_RESTORE_LMF {
        mono_bblock_add_inst (s->cbb, tree);
@@ -1863,6 +1890,23 @@ mini_emit_virtual_call (MonoCompile *cfg, void *st, MonoInst *tree, int novirtop
        mono_bblock_add_inst (cfg->cbb, tree);
 }
 
+static void
+emit_rgctx_argument (MonoCompile *s, MonoInst *tree, int reg, int new_opcode)
+{
+#ifdef MONO_ARCH_RGCTX_REG
+       MonoCallInst *call = (MonoCallInst*)tree;
+       int rgctx_reg = mono_regstate_next_int (s->rs);
+
+       MONO_EMIT_NEW_UNALU (s, OP_MOVE, rgctx_reg, reg);
+
+       call->inst.opcode = new_opcode;
+
+       mono_call_inst_add_outarg_reg (s, call, rgctx_reg, MONO_ARCH_RGCTX_REG, FALSE);
+#else
+       g_assert_not_reached ();
+#endif
+}
+
 /*
  * Emit code for ISINST/CASTCLASS
  */
index f435a4dd765e9bb533ab58b43718658690472bef..f051caf7f6cbab63d049ea2d1cfed30a70dd4a66 100644 (file)
@@ -717,7 +717,12 @@ mono_cprop_invalidate_values (MonoInst *tree, TreeMover *tree_mover, MonoInst **
        case OP_VOIDCALL_REG:
        case OP_VOIDCALLVIRT:
        case OP_VOIDCALL:
-       case OP_TRAMPCALL_VTABLE: {
+       case OP_TRAMPCALL_VTABLE:
+       case OP_CALL_RGCTX:
+       case OP_FCALL_RGCTX:
+       case OP_VOIDCALL_RGCTX:
+       case OP_LCALL_RGCTX:
+       case OP_VCALL_RGCTX: {
                MonoCallInst *call = (MonoCallInst *)tree;
                MonoMethodSignature *sig = call->signature;
                int i, byref = FALSE;
index d57e7c89c392bd0e3dbd7006be2b9676f8e2dce3..a07f58546995156ca680ba987554d1a84467b2ef 100644 (file)
@@ -4540,9 +4540,11 @@ mono_arch_emit_prolog (MonoCompile *cfg)
         * - push rbp, mov rbp, rsp
         * - save callee saved regs using pushes
         * - allocate frame
+        * - save rgctx if needed
         * - save lmf if needed
         * FP not present:
         * - allocate frame
+        * - save rgctx if needed
         * - save lmf if needed
         * - save callee saved regs using moves
         */
@@ -4646,6 +4648,14 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                        }
        }
 
+       /* store runtime generic context */
+       if (cfg->rgctx_var) {
+               g_assert (cfg->rgctx_var->opcode == OP_REGOFFSET &&
+                               (cfg->rgctx_var->inst_basereg == AMD64_RBP || cfg->rgctx_var->inst_basereg == AMD64_RSP));
+
+               amd64_mov_membase_reg (code, cfg->rgctx_var->inst_basereg, cfg->rgctx_var->inst_offset, MONO_ARCH_RGCTX_REG, 8);
+       }
+
        /* compute max_offset in order to use short forward jumps */
        max_offset = 0;
        if (cfg->opt & MONO_OPT_BRANCH) {
@@ -5982,6 +5992,12 @@ mono_arch_find_this_argument (gpointer *regs, MonoMethod *method, MonoGenericSha
 }
 #endif
 
+MonoRuntimeGenericContext*
+mono_arch_find_static_call_rgctx (gpointer *regs, guint8 *code)
+{
+       return (MonoRuntimeGenericContext*) regs [MONO_ARCH_RGCTX_REG];
+}
+
 MonoInst*
 mono_arch_get_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
 {
index c988132c0647bb150e288b4b7a1a117f6546cf30..d605aea46e4ad5d5b4411a48397144bfe5c56ac7 100644 (file)
@@ -275,6 +275,12 @@ typedef struct {
 #define MONO_ARCH_HAVE_IMT 1
 #define MONO_ARCH_IMT_REG AMD64_R11
 #define MONO_ARCH_VTABLE_REG AMD64_R11
+/*
+ * We use r10 for the rgctx register rather than r11 because r11 is
+ * used by the trampoline as a scratch register and hence might be
+ * clobbered across method call boundaries.
+ */
+#define MONO_ARCH_RGCTX_REG AMD64_R10
 #define MONO_ARCH_COMMON_VTABLE_TRAMPOLINE 1
 #define MONO_ARCH_HAVE_NOTIFY_PENDING_EXC 1
 #define MONO_ARCH_ENABLE_NORMALIZE_OPCODES 1
index 2f5278bf4ae1bcb00221710ea80dce10146cdc8a..69fbea9f8169eaa8a471690d652e46c05ae9364e 100644 (file)
@@ -38,6 +38,11 @@ MINI_OP(OP_VOIDCALLVIRT,     "voidcallvirt")
 MINI_OP(OP_VOIDCALL_REG,       "voidcall_reg")
 MINI_OP(OP_VOIDCALL_MEMBASE,   "voidcall_membase")
 MINI_OP(OP_TRAMPCALL_VTABLE,   "trampcall_vtable")
+MINI_OP(OP_VOIDCALL_RGCTX,     "voidcall_rgctx")
+MINI_OP(OP_LCALL_RGCTX,                "lcall_rgctx")
+MINI_OP(OP_VCALL_RGCTX,                "vcall_rgctx")
+MINI_OP(OP_CALL_RGCTX,         "call_rgctx")
+MINI_OP(OP_FCALL_RGCTX,                "fcall_rgctx")
 MINI_OP(OP_FCALL,      "fcall")
 MINI_OP(OP_FCALLVIRT,  "fcallvirt")
 MINI_OP(OP_FCALL_REG,  "fcall_reg")
index b65dc72deca45665e1bf7e324db7ca1296bd4c74..ffba86986da4cd27398234c9e326eef8ba8bdb59 100644 (file)
@@ -156,7 +156,13 @@ mono_magic_trampoline (gssize *regs, guint8 *code, MonoMethod *m, guint8* tramp)
 
 
                if (m->flags & METHOD_ATTRIBUTE_STATIC) {
+#ifdef MONO_ARCH_RGCTX_REG
+                       MonoRuntimeGenericContext *rgctx = mono_arch_find_static_call_rgctx ((gpointer*)regs, code);
+
+                       klass = rgctx->vtable->klass;
+#else
                        g_assert_not_reached ();
+#endif
                } else {
 #ifdef MONO_ARCH_HAVE_IMT
                        MonoObject *this_argument = mono_arch_find_this_argument ((gpointer*)regs, m,
index 2a0b71439cca616cf45d09f7fc74db64bcb8f633..b13e91d6494ab197dc9ab7d17004be02564d2f78 100644 (file)
@@ -3931,6 +3931,13 @@ mono_arch_emit_prolog (MonoCompile *cfg)
        if (mono_jit_trace_calls != NULL && mono_trace_eval (method))
                code = mono_arch_instrument_prolog (cfg, mono_trace_enter_method, code, TRUE);
 
+       /* store runtime generic context */
+       if (cfg->rgctx_var) {
+               g_assert (cfg->rgctx_var->opcode == OP_REGOFFSET && cfg->rgctx_var->inst_basereg == X86_EBP);
+
+               x86_mov_membase_reg (code, X86_EBP, cfg->rgctx_var->inst_offset, MONO_ARCH_RGCTX_REG, 4);
+       }
+
        /* load arguments allocated to register from the stack */
        sig = mono_method_signature (method);
        pos = 0;
@@ -4454,6 +4461,12 @@ mono_arch_find_this_argument (gpointer *regs, MonoMethod *method, MonoGenericSha
 }
 #endif
 
+MonoRuntimeGenericContext*
+mono_arch_find_static_call_rgctx (gpointer *regs, guint8 *code)
+{
+       return (MonoRuntimeGenericContext*) regs [MONO_ARCH_RGCTX_REG];
+}
+
 MonoInst*
 mono_arch_get_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
 {
index a2f1b2321ef6e06fcd5854639b7ebb7229f60945..f3933995acb99cf9d5e6cf10c275b79fd893529f 100644 (file)
@@ -276,6 +276,7 @@ typedef struct {
 #define MONO_ARCH_IMT_REG X86_EDX
 #define MONO_ARCH_VTABLE_REG X86_EDX
 #define MONO_ARCH_COMMON_VTABLE_TRAMPOLINE 1
+#define MONO_ARCH_RGCTX_REG X86_EDX
 #define MONO_ARCH_ENABLE_NORMALIZE_OPCODES 1
 
 #if !defined(__APPLE__)
index 71e12008103724eb6da2e9618d2dff720aea1ff0..1621005e9d93a68dc574b2e303c7a36916c3fde0 100644 (file)
        } while (0)
 #define GENERIC_SHARING_FAILURE(opcode) do {           \
                if (cfg->generic_sharing_context) {     \
-                       if (!(method->flags & METHOD_ATTRIBUTE_STATIC)) \
-                               /*g_print ("sharing failed for method %s.%s.%s/%d opcode %s line %d\n", method->klass->name_space, method->klass->name, method->name, method->signature->param_count, mono_opcode_name ((opcode)), __LINE__)*/; \
+                       /*g_print ("sharing failed for method %s.%s.%s/%d opcode %s line %d\n", method->klass->name_space, method->klass->name, method->name, method->signature->param_count, mono_opcode_name ((opcode)), __LINE__)*/; \
                        cfg->exception_type = MONO_EXCEPTION_GENERIC_SHARING_FAILED;    \
                        goto exception_exit;    \
                }                       \
@@ -1803,6 +1802,20 @@ mono_get_got_var (MonoCompile *cfg)
 #endif
 }
 
+static MonoInst *
+mono_get_rgctx_var (MonoCompile *cfg)
+{
+       g_assert (cfg->generic_sharing_context);
+
+       if (!cfg->rgctx_var) {
+               cfg->rgctx_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
+               /* force the var to be stack allocated */
+               cfg->rgctx_var->flags |= MONO_INST_INDIRECT;
+       }
+
+       return cfg->rgctx_var;
+}
+
 MonoInst*
 mono_compile_create_var (MonoCompile *cfg, MonoType *type, int opcode)
 {
@@ -2597,7 +2610,7 @@ mono_spill_call (MonoCompile *cfg, MonoBasicBlock *bblock, MonoCallInst *call, M
                        temp->backend.is_pinvoke = sig->pinvoke;
 
                        NEW_TEMPLOADA (cfg, loada, temp->inst_c0);
-                       if (call->inst.opcode == OP_VCALL)
+                       if (call->inst.opcode == OP_VCALL || call->inst.opcode == OP_VCALL_RGCTX)
                                ins->inst_left = loada;
                        else
                                ins->inst_right = loada; /* a virtual or indirect call */
@@ -2776,6 +2789,44 @@ mono_emit_jit_icall (MonoCompile *cfg, MonoBasicBlock *bblock, gconstpointer fun
        return mono_emit_native_call (cfg, bblock, mono_icall_get_wrapper (info), info->sig, args, ip, FALSE, FALSE);
 }
 
+static MonoCallInst*
+mono_emit_rgctx_method_call (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethod *method, MonoMethodSignature *sig,
+               MonoInst **args, MonoInst *rgctx_arg, const guint8 *ip, MonoInst *this)
+{
+       MonoCallInst *call = mono_emit_method_call_full (cfg, bblock, method, sig, args, ip, this, FALSE);
+
+       if (rgctx_arg) {
+               switch (call->inst.opcode) {
+               case OP_CALL: call->inst.opcode = OP_CALL_RGCTX; break;
+               case OP_VOIDCALL: call->inst.opcode = OP_VOIDCALL_RGCTX; break;
+               case OP_FCALL: call->inst.opcode = OP_FCALL_RGCTX; break;
+               case OP_LCALL: call->inst.opcode = OP_LCALL_RGCTX; break;
+               case OP_VCALL: call->inst.opcode = OP_VCALL_RGCTX; break;
+               default: g_assert_not_reached ();
+               }
+
+               if (call->inst.opcode != OP_VCALL_RGCTX) {
+                       g_assert (!call->inst.inst_left);
+                       call->inst.inst_left = rgctx_arg;
+               } else {
+                       g_assert (!call->inst.inst_right);
+                       call->inst.inst_right = rgctx_arg;
+               }
+       }
+
+       return call;
+}
+
+inline static int
+mono_emit_rgctx_method_call_spilled (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethod *method,  
+               MonoMethodSignature *signature, MonoInst **args, MonoInst *rgctx_arg, const guint8 *ip,
+               MonoInst *this)
+{
+       MonoCallInst *call = mono_emit_rgctx_method_call (cfg, bblock, method, signature, args, rgctx_arg, ip, this);
+
+       return mono_spill_call (cfg, bblock, call, signature, method->string_ctor, ip, FALSE);
+}
+
 static void
 mono_emulate_opcode (MonoCompile *cfg, MonoInst *tree, MonoInst **iargs, MonoJitICallInfo *info)
 {
@@ -4361,6 +4412,21 @@ get_runtime_generic_context_from_this (MonoCompile *cfg, MonoInst *this, unsigne
        return rgc_ptr;
 }
 
+static MonoInst*
+get_runtime_generic_context (MonoCompile *cfg, MonoMethod *method, MonoInst *this, unsigned char *ip)
+{
+       if (method->flags & METHOD_ATTRIBUTE_STATIC) {
+               MonoInst *rgctx_loc, *rgctx_var;
+
+               rgctx_loc = mono_get_rgctx_var (cfg);
+               NEW_TEMPLOAD (cfg, rgctx_var, rgctx_loc->inst_c0);
+
+               return rgctx_var;
+       } else {
+               return get_runtime_generic_context_from_this (cfg, this, ip);
+       }
+}
+
 static gpointer
 create_rgctx_lazy_fetch_trampoline (guint32 offset)
 {
@@ -4764,8 +4830,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        if ((method->flags & METHOD_ATTRIBUTE_STATIC) &&
                                        clause->flags != MONO_EXCEPTION_CLAUSE_FILTER &&
                                        clause->data.catch_class &&
+                                       cfg->generic_sharing_context &&
                                        mono_class_check_context_used (clause->data.catch_class)) {
-                               GENERIC_SHARING_FAILURE (CEE_NOP);
+                               mono_get_rgctx_var (cfg);
                        }
 
                        GET_BBLOCK (cfg, try_bb, ip + clause->try_offset);
@@ -5384,8 +5451,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        MonoMethodSignature *fsig = NULL;
                        int temp, array_rank = 0;
                        int virtual = *ip == CEE_CALLVIRT;
+                       MonoInst *rgctx_arg = NULL;
                        gboolean no_spill;
                        int context_used = 0;
+                       gboolean pass_rgctx = FALSE;
 
                        CHECK_OPSIZE (5);
                        token = read32 (ip + 1);
@@ -5511,6 +5580,34 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        if (*ip != CEE_CALLI && check_call_signature (cfg, fsig, sp))
                                UNVERIFIED;
 
+                       if (cmethod && (cmethod->flags & METHOD_ATTRIBUTE_STATIC) &&
+                                       (cmethod->klass->generic_class || cmethod->klass->generic_container)) {
+                               gboolean sharing_enabled = mono_class_generic_sharing_enabled (cmethod->klass);
+                               MonoGenericContext *context = mini_class_get_context (cmethod->klass);
+                               gboolean context_sharable = mono_generic_context_is_sharable (context, TRUE);
+
+                               /*
+                                * Pass rgctx iff target method might
+                                * be shared, which means that sharing
+                                * is enabled for its class and its
+                                * context is sharable.
+                                *
+                                * Cancel sharing of compiled method
+                                * iff we don't know the target method
+                                * statically (assuming that we share
+                                * all sharable methods), which means
+                                * that the method depends on the
+                                * caller's context, sharing is
+                                * enabled for its class but its
+                                * context is not sharable.
+                                */
+                               if (sharing_enabled && context_sharable)
+                                       pass_rgctx = TRUE;
+
+                               if (sharing_enabled && !context_sharable && mono_method_check_context_used (cmethod))
+                                       GENERIC_SHARING_FAILURE (*ip);
+                       }
+
                        if (cfg->generic_sharing_context && cmethod) {
                                MonoGenericContext *cmethod_context = mono_method_get_context (cmethod);
 
@@ -5522,12 +5619,41 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                if (context_used &&
                                                ((cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE) ||
                                                (cmethod_context && cmethod_context->method_inst) ||
-                                               cmethod->klass->valuetype ||
-                                               (cmethod->flags & METHOD_ATTRIBUTE_STATIC))) {
+                                               cmethod->klass->valuetype)) {
                                        GENERIC_SHARING_FAILURE (*ip);
                                }
                        }
 
+                       if (pass_rgctx) {
+                               if (context_used) {
+                                       MonoInst *this = NULL, *rgctx, *vtable, *field_offset, *field_addr;
+
+                                       if (!(method->flags & METHOD_ATTRIBUTE_STATIC))
+                                               NEW_ARGLOAD (cfg, this, 0);
+                                       rgctx = get_runtime_generic_context (cfg, method, this, ip);
+                                       vtable = get_runtime_generic_context_ptr (cfg, method, bblock, cmethod->klass,
+                                               token, MINI_TOKEN_SOURCE_METHOD, generic_context,
+                                               rgctx, MONO_RGCTX_INFO_VTABLE, ip);
+
+                                       NEW_ICONST (cfg, field_offset, G_STRUCT_OFFSET (MonoVTable, runtime_generic_context));
+
+                                       MONO_INST_NEW (cfg, field_addr, OP_PADD);
+                                       field_addr->cil_code = ip;
+                                       field_addr->inst_left = vtable;
+                                       field_addr->inst_right = field_offset;
+                                       field_addr->type = STACK_PTR;
+
+                                       MONO_INST_NEW (cfg, rgctx_arg, CEE_LDIND_I);
+                                       rgctx_arg->cil_code = ip;
+                                       rgctx_arg->inst_left = field_addr;
+                                       rgctx_arg->type = STACK_PTR;
+                               } else {
+                                       MonoVTable *vtable = mono_class_vtable (cfg->domain, cmethod->klass);
+
+                                       NEW_PCONST (cfg, rgctx_arg, vtable->runtime_generic_context);
+                               }
+                       }
+
                        if (cmethod && virtual && 
                            (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) && 
                            !((cmethod->flags & METHOD_ATTRIBUTE_FINAL) && 
@@ -5572,6 +5698,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                break;
                        }
 
+                       /* FIXME: runtime generic context pointer for jumps? */
                        if ((ins_flag & MONO_INST_TAILCALL) && cmethod && (*ip == CEE_CALL) &&
                                 (mono_metadata_signature_equal (mono_method_signature (method), mono_method_signature (cmethod)))) {
                                int i;
@@ -5680,7 +5807,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        inline_costs += 10 * num_calls++;
 
                        /* tail recursion elimination */
-                       if ((cfg->opt & MONO_OPT_TAILC) && *ip == CEE_CALL && cmethod == method && ip [5] == CEE_RET) {
+                       if ((cfg->opt & MONO_OPT_TAILC) && *ip == CEE_CALL && cmethod == method && ip [5] == CEE_RET &&
+                                       !rgctx_arg) {
                                gboolean has_vtargs = FALSE;
                                int i;
                                
@@ -5727,6 +5855,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                no_spill = FALSE;
 
                        if (*ip == CEE_CALLI) {
+                               g_assert (!rgctx_arg);
                                /* Prevent inlining of methods with indirect calls */
                                INLINE_FAILURE;
                                if (no_spill) {
@@ -5805,10 +5934,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                                sp++;
                                        }
                                } else if (no_spill) {
-                                       ins = (MonoInst*)mono_emit_method_call (cfg, bblock, cmethod, fsig, sp, ip, virtual ? sp [0] : NULL);
+                                       ins = (MonoInst*)mono_emit_rgctx_method_call (cfg, bblock, cmethod, fsig, sp, rgctx_arg, ip, virtual ? sp [0] : NULL);
                                        *sp++ = ins;
                                } else {
-                                       if ((temp = mono_emit_method_call_spilled (cfg, bblock, cmethod, fsig, sp, ip, virtual ? sp [0] : NULL)) != -1) {
+                                       if ((temp = mono_emit_rgctx_method_call_spilled (cfg, bblock, cmethod, fsig, sp, rgctx_arg, ip, virtual ? sp [0] : NULL)) != -1) {
                                                MonoInst *load;
                                                NEW_TEMPLOAD (cfg, load, temp);
 
@@ -7142,12 +7271,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                        GENERIC_SHARING_FAILURE (*ip);
 
                                if (context_used) {
-                                       relation = mono_class_generic_class_relation (klass, MONO_RGCTX_INFO_VTABLE, method->klass, generic_context, NULL);
-
-                                       if (!(method->flags & METHOD_ATTRIBUTE_STATIC) /*&& relation != MINI_GENERIC_CLASS_RELATION_OTHER*/)
-                                               shared_access = TRUE;
-                                       else
-                                               GENERIC_SHARING_FAILURE (*ip);
+                                       relation = mono_class_generic_class_relation (klass, MONO_RGCTX_INFO_VTABLE,
+                                               method->klass, generic_context, NULL);
+                                       shared_access = TRUE;
                                }
                        }
 
@@ -7178,18 +7304,20 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                if (mono_class_needs_cctor_run (klass, method)) {
                                        MonoMethodSignature *sig = helper_sig_generic_class_init_trampoline;
                                        MonoCallInst *call;
-                                       MonoInst *this, *vtable;
+                                       MonoInst *vtable;
+                                       MonoInst *this = NULL;
 
-                                       NEW_ARGLOAD (cfg, this, 0);
+                                       if (!(method->flags & METHOD_ATTRIBUTE_STATIC))
+                                               NEW_ARGLOAD (cfg, this, 0);
 
-                                       if (relation == MINI_GENERIC_CLASS_RELATION_SELF) {
+                                       if (relation == MINI_GENERIC_CLASS_RELATION_SELF && this) {
                                                MONO_INST_NEW (cfg, vtable, CEE_LDIND_I);
                                                vtable->cil_code = ip;
                                                vtable->inst_left = this;
                                                vtable->type = STACK_PTR;
                                                vtable->klass = klass;
                                        } else {
-                                               MonoInst *rgctx = get_runtime_generic_context_from_this (cfg, this, ip);
+                                               MonoInst *rgctx = get_runtime_generic_context (cfg, method, this, ip);
 
                                                vtable = get_runtime_generic_context_ptr (cfg, method, bblock, klass,
                                                        token, MINI_TOKEN_SOURCE_FIELD, generic_context,
@@ -7211,8 +7339,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                 *   super_info.static_data + field->offset
                                 */
 
-                               NEW_ARGLOAD (cfg, this, 0);
-                               rgctx = get_runtime_generic_context_from_this (cfg, this, ip);
+                               if (!(method->flags & METHOD_ATTRIBUTE_STATIC))
+                                       NEW_ARGLOAD (cfg, this, 0);
+                               rgctx = get_runtime_generic_context (cfg, method, this, ip);
                                static_data = get_runtime_generic_context_ptr (cfg, method, bblock, klass,
                                        token, MINI_TOKEN_SOURCE_FIELD, generic_context,
                                        rgctx, MONO_RGCTX_INFO_STATIC_DATA, ip);
@@ -7521,16 +7650,12 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                if (context_used & MONO_GENERIC_CONTEXT_USED_METHOD || klass->valuetype)
                                        GENERIC_SHARING_FAILURE (CEE_NEWARR);
 
-                               if (context_used) {
-                                       if (!(method->flags & METHOD_ATTRIBUTE_STATIC))
-                                               shared_access = TRUE;
-                                       else
-                                               GENERIC_SHARING_FAILURE (CEE_NEWARR);
-                               }
+                               if (context_used)
+                                       shared_access = TRUE;
                        }
 
                        if (shared_access) {
-                               MonoInst *this, *rgctx;
+                               MonoInst *this = NULL, *rgctx;
                                MonoInst *args [3];
                                int temp;
 
@@ -7538,8 +7663,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                NEW_DOMAINCONST (cfg, args [0]);
 
                                /* klass */
-                               NEW_ARGLOAD (cfg, this, 0);
-                               rgctx = get_runtime_generic_context_from_this (cfg, this, ip);
+                               if (!(method->flags & METHOD_ATTRIBUTE_STATIC))
+                                       NEW_ARGLOAD (cfg, this, 0);
+                               rgctx = get_runtime_generic_context (cfg, method, this, ip);
                                args [1] = get_runtime_generic_context_ptr (cfg, method, bblock, klass,
                                        token, MINI_TOKEN_SOURCE_CLASS, generic_context, rgctx, MONO_RGCTX_INFO_KLASS, ip);
 
@@ -8396,6 +8522,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                MonoInst *argconst;
                                MonoMethod *cil_method, *ctor_method;
                                int temp;
+                               gboolean is_shared;
 
                                CHECK_STACK_OVF (1);
                                CHECK_OPSIZE (6);
@@ -8408,6 +8535,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                if (cfg->generic_sharing_context && mono_method_check_context_used (cmethod))
                                        GENERIC_SHARING_FAILURE (CEE_LDFTN);
 
+                               is_shared = (cmethod->flags & METHOD_ATTRIBUTE_STATIC) &&
+                                       (cmethod->klass->generic_class || cmethod->klass->generic_container) &&
+                                       mono_class_generic_sharing_enabled (cmethod->klass);
+
                                cil_method = cmethod;
                                if (!dont_verify && !cfg->skip_visibility && !mono_method_can_access_method (method, cmethod))
                                        METHOD_ACCESS_FAILURE;
@@ -8424,7 +8555,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                 */
 #if defined(MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE) && !defined(HAVE_WRITE_BARRIERS)
                                /* FIXME: SGEN support */
-                               if ((sp > stack_start) && (ip + 6 + 5 < end) && ip_in_bb (cfg, bblock, ip + 6) && (ip [6] == CEE_NEWOBJ) && (ctor_method = mini_get_method (method, read32 (ip + 7), NULL, generic_context)) && (ctor_method->klass->parent == mono_defaults.multicastdelegate_class)) {
+                               /* FIXME: handle shared static generic methods */
+                               if (!is_shared && (sp > stack_start) && (ip + 6 + 5 < end) && ip_in_bb (cfg, bblock, ip + 6) && (ip [6] == CEE_NEWOBJ) && (ctor_method = mini_get_method (method, read32 (ip + 7), NULL, generic_context)) && (ctor_method->klass->parent == mono_defaults.multicastdelegate_class)) {
                                        MonoInst *target_ins;
 
                                        ip += 6;
@@ -8441,7 +8573,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
 
                                handle_loaded_temps (cfg, bblock, stack_start, sp);
 
-                               NEW_METHODCONST (cfg, argconst, cmethod);
+                               if (is_shared)
+                                       NEW_METHODCONST (cfg, argconst, mono_marshal_get_static_rgctx_invoke (cmethod));
+                               else
+                                       NEW_METHODCONST (cfg, argconst, cmethod);
                                if (method->wrapper_type != MONO_WRAPPER_SYNCHRONIZED)
                                        temp = mono_emit_jit_icall (cfg, bblock, mono_ldftn, &argconst, ip);
                                else
@@ -10204,9 +10339,13 @@ mono_resolve_patch_target (MonoMethod *method, MonoDomain *domain, guint8 *code,
        case MONO_PATCH_INFO_METHOD:
                if (patch_info->data.method == method) {
                        target = code;
-               } else
+               } else {
                        /* get the trampoline to the method from the domain */
-                       target = mono_create_jit_trampoline (patch_info->data.method);
+                       if (method->wrapper_type == MONO_WRAPPER_STATIC_RGCTX_INVOKE)
+                               target = mono_ldftn_nosync (patch_info->data.method);
+                       else
+                               target = mono_create_jit_trampoline (patch_info->data.method);
+               }
                break;
        case MONO_PATCH_INFO_SWITCH: {
                gpointer *jump_table;
@@ -11993,7 +12132,13 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool
        jinfo->cas_inited = FALSE; /* initialization delayed at the first stalk walk using this method */
        jinfo->num_clauses = header->num_clauses;
 
-       if (cfg->generic_sharing_context && !(method_to_compile->flags & METHOD_ATTRIBUTE_STATIC)) {
+       /*
+        * Static methods only get a generic JIT info if they use the
+        * rgctx variable (which they are forced to if they have any
+        * open catch clauses).
+        */
+       if (cfg->generic_sharing_context &&
+                       (cfg->rgctx_var || !(method_to_compile->flags & METHOD_ATTRIBUTE_STATIC))) {
                MonoInst *inst;
                MonoGenericJitInfo *gi;
 
@@ -12004,9 +12149,12 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool
 
                gi->generic_sharing_context = cfg->generic_sharing_context;
 
-               g_assert (!(method_to_compile->flags & METHOD_ATTRIBUTE_STATIC));
-
-               inst = cfg->varinfo [0];
+               if (method_to_compile->flags & METHOD_ATTRIBUTE_STATIC) {
+                       inst = cfg->rgctx_var;
+                       g_assert (inst->opcode == OP_REGOFFSET);
+               } else {
+                       inst = cfg->varinfo [0];
+               }
 
                if (inst->opcode == OP_REGVAR) {
                        gi->this_in_reg = 1;
@@ -12480,6 +12628,7 @@ mono_jit_find_compiled_method (MonoDomain *domain, MonoMethod *method)
 static MonoObject*
 mono_jit_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObject **exc)
 {
+       MonoMethod *to_compile;
        MonoMethod *invoke;
        MonoObject *(*runtime_invoke) (MonoObject *this, void **params, MonoObject **exc, void* compiled_method);
        void* compiled_method;
@@ -12489,6 +12638,14 @@ mono_jit_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObjec
                return NULL;
        }
 
+       if ((method->flags & METHOD_ATTRIBUTE_STATIC) &&
+                       mono_class_generic_sharing_enabled (method->klass) &&
+                       mono_method_is_generic_sharable_impl (method)) {
+               to_compile = mono_marshal_get_static_rgctx_invoke (method);
+       } else {
+               to_compile = method;
+       }
+
        invoke = mono_marshal_get_runtime_invoke (method);
        runtime_invoke = mono_jit_compile_method (invoke);
        
@@ -12505,7 +12662,7 @@ mono_jit_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObjec
                 */
                compiled_method = NULL;
        } else {
-               compiled_method = mono_jit_compile_method (method);
+               compiled_method = mono_jit_compile_method (to_compile);
        }
        return runtime_invoke (obj, params, exc, compiled_method);
 }
index 68c8303d549cd854af3b3800541f56b111f03b06..0e16b5fda61a9041de51cf36ba84b0308811c398 100644 (file)
@@ -765,6 +765,7 @@ typedef struct {
        MonoMethod      *inlined_method; /* the method which is currently inlined */
        MonoInst        *domainvar; /* a cache for the current domain */
        MonoInst        *got_var; /* Global Offset Table variable */
+       MonoInst        *rgctx_var; /* Runtime generic context variable (for static generic methods) */
        MonoInst        **args;
 
        /* 
@@ -1263,6 +1264,7 @@ gpointer mono_arch_get_delegate_invoke_impl     (MonoMethodSignature *sig, gbool
 gpointer mono_arch_create_specific_trampoline   (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len) MONO_INTERNAL;
 void        mono_arch_emit_imt_argument         (MonoCompile *cfg, MonoCallInst *call) MONO_INTERNAL;
 MonoMethod* mono_arch_find_imt_method           (gpointer *regs, guint8 *code) MONO_INTERNAL;
+MonoRuntimeGenericContext* mono_arch_find_static_call_rgctx (gpointer *regs, guint8 *code) MONO_INTERNAL;
 MonoObject* mono_arch_find_this_argument        (gpointer *regs, MonoMethod *method, MonoGenericSharingContext *gsctx) MONO_INTERNAL;
 gpointer    mono_arch_build_imt_thunk           (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count) MONO_INTERNAL;
 void    mono_arch_notify_pending_exc (void) MONO_INTERNAL;
@@ -1351,7 +1353,7 @@ int mono_method_check_context_used (MonoMethod *method) MONO_INTERNAL;
 
 gboolean mono_generic_context_equal_deep (MonoGenericContext *context1, MonoGenericContext *context2) MONO_INTERNAL;
 
-gboolean mono_generic_context_is_sharable (MonoGenericContext *context) MONO_INTERNAL;
+gboolean mono_generic_context_is_sharable (MonoGenericContext *context, gboolean allow_type_vars) MONO_INTERNAL;
 
 gboolean mono_method_is_generic_impl (MonoMethod *method) MONO_INTERNAL;
 gboolean mono_method_is_generic_sharable_impl (MonoMethod *method) MONO_INTERNAL;
index 3cad47ad4cdc902463c24dc02a5f308717ac08b7..b40325509f4f28951a773da508f38c99ab9cecd9 100644 (file)
@@ -388,9 +388,10 @@ mono_arch_create_trampoline_code (MonoTrampolineType tramp_type)
        amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, lmf_addr), 8);
        amd64_mov_membase_reg (code, AMD64_R11, 0, AMD64_RCX, 8);
 
-       /* Restore argument registers */
+       /* Restore argument registers and r10 (needed to pass rgctx to
+          static shared generic methods). */
        for (i = 0; i < AMD64_NREG; ++i)
-               if (AMD64_IS_ARGUMENT_REG (i))
+               if (AMD64_IS_ARGUMENT_REG (i) || i == AMD64_R10)
                        amd64_mov_reg_membase (code, i, AMD64_RBP, saved_regs_offset + (i * 8), 8);
 
        for (i = 0; i < 8; ++i)
index 198a6e2bcb65d6752a345b52a9719f107ea7c35f..71fa3c78768f8d64111cf279fd3d54bccd917830 100644 (file)
@@ -1,3 +1,15 @@
+2008-03-14  Mark Probst  <mark.probst@gmail.com>
+
+       * generics-sharing.2.cs: New tests for static methods.
+
+       * generic-inlining.2.cs: Added.  Tests generic method inlining.
+
+       * shared-generic-synchronized.2.cs: Added.  Tests shared
+       synchronized methods.
+
+       * Makefile.am: Added the new tests.  New target for running the
+       generic code sharing with the optimization enabled.
+
 2008-03-04  Mark Probst  <mark.probst@gmail.com>
 
        * generics-sharing.2.c: Added test for open catch clauses in
index 111d6bfc506e452eb3ee9e5f6ceb56a976d53e07..25ec8a54dff1b3673bc15731d8d0f0bedd6d586c 100644 (file)
@@ -253,6 +253,8 @@ BASE_TEST_CS_SRC=           \
        generics-invoke-byref.2.cs      \
        generic-signature-compare.2.cs  \
        generics-sharing.2.cs   \
+       shared-generic-synchronized.2.cs        \
+       generic-inlining.2.cs   \
        recursive-generics.2.cs \
        bug-80392.2.cs          \
        dynamic-method-access.2.cs      \
@@ -370,14 +372,14 @@ EXTRA_DIST=test-driver $(TEST_CS_SRC) $(TEST_IL_SRC) \
 
 # mkbundle works on ppc, but the pkg-config POC doesn't when run with make test
 if POWERPC
-test:  assemblyresolve/test/asm.dll testjit test-type-load test-inline-call-stack test-bug-80307 test-bug-81673 test-bug-81691 test-bug-81466 test-bug-324535
+test:  assemblyresolve/test/asm.dll testjit test-type-load test-inline-call-stack test-bug-80307 test-bug-81673 test-bug-81691 test-bug-81466 test-bug-324535 test-generic-sharing
 else
 # Can't use mkbundle on win32 since there is no static build there
 # Can't run test-unhandled-exception on Windows because of all the debug popups...
 if PLATFORM_WIN32
-test:   assemblyresolve/test/asm.dll testjit test-type-load test-inline-call-stack test-bug-80307 test-bug-81673 test-bug-81691 test-bug-81466 test-bug-324535
+test:   assemblyresolve/test/asm.dll testjit test-type-load test-inline-call-stack test-bug-80307 test-bug-81673 test-bug-81691 test-bug-81466 test-bug-324535 test-generic-sharing
 else
-test:  assemblyresolve/test/asm.dll testjit testbundle test-type-load test-inline-call-stack test-iomap-regression test-bug-80307 test-bug-81673 test-bug-81691 test-bug-81466 test-bug-324535 custom-modifiers
+test:  assemblyresolve/test/asm.dll testjit testbundle test-type-load test-inline-call-stack test-iomap-regression test-bug-80307 test-bug-81673 test-bug-81691 test-bug-81466 test-bug-324535 custom-modifiers test-generic-sharing
 endif
 endif
 
@@ -633,6 +635,10 @@ test-coreclr-security : coreclr-security.exe
        $(RUNTIME21) --security=core-clr-test coreclr-security.exe
 endif
 
+test-generic-sharing : generics-sharing.2.exe shared-generic-synchronized.2.exe
+       $(RUNTIME) -O=gshared generics-sharing.2.exe
+       $(RUNTIME) -O=gshared shared-generic-synchronized.2.exe
+
 EXTRA_DIST += async-exceptions.cs
 async-exceptions.exe : async-exceptions.cs
        $(MCS) -out:async-exceptions.exe $(srcdir)/async-exceptions.cs
diff --git a/mono/tests/generic-inlining.2.cs b/mono/tests/generic-inlining.2.cs
new file mode 100644 (file)
index 0000000..c112b97
--- /dev/null
@@ -0,0 +1,55 @@
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+
+public class Gen<T> {
+       public T[] method () {
+               return new T[3];
+       }
+
+       public static T[] staticMethod () {
+               return new T[3];
+       }
+
+       public S[] genericMethod<S> () {
+               return new S[3];
+       }
+}
+
+public class main {
+       static bool callMethod<T> (Gen<T> g) {
+               return g.method ().GetType () == typeof (T[]);
+       }
+
+       static bool callStaticMethod<T> () {
+               return Gen<T>.staticMethod ().GetType () == typeof (T[]);
+       }
+
+       static bool callGenericMethod<T,S> (Gen<T> g) {
+               return g.genericMethod<S> ().GetType () == typeof (S[]);
+       }
+
+       [MethodImpl (MethodImplOptions.NoInlining)]
+       static bool work<T,S> () {
+               Gen<T> g = new Gen<T> ();
+
+               if (!callMethod<T> (g))
+                       return false;
+               if (!callStaticMethod<T> ())
+                       return false;
+               if (!callGenericMethod<T,S> (g))
+                       return false;
+               return true;
+       }
+
+       public static int Main () {
+               if (!work<string,string> ())
+                       return 1;
+               if (!work<int,int> ())
+                       return 1;
+               if (!work<string,int> ())
+                       return 1;
+               if (!work<int,string> ())
+                       return 1;
+               return 0;
+       }
+}
index ced7ada8fdbcf11cf51b06628f35bdb98f6831e8..aa2a5b85c9c04d40fe877c7c2a03080fd97731b9 100644 (file)
@@ -1,5 +1,10 @@
 using System;
 using System.Collections.Generic;
+using System.Reflection;
+
+namespace GenericSharingTest {
+
+public delegate int IntVoidDelegate ();
 
 public class ClassA {}
 public class ClassB {}
@@ -15,6 +20,47 @@ public class NonGen {
        }
 }
 
+public class GenBi<S,T> {
+       public static int field = 123;
+       public static float floatField = 1.0f;
+
+       public static int staticMethod (int x) {
+               return x + field;
+       }
+
+       public static void staticVoidMethod (int x) {
+               field = x;
+       }
+
+       public static float staticFloatMethod () {
+               return floatField;
+       }
+
+       public static long staticLongMethod (long x) {
+               return x + field;
+       }
+
+       public static GenStruct<T> staticValueMethod (int x) {
+               return new GenStruct<T> (x);
+       }
+}
+
+public struct GenStruct<T> {
+       public int field;
+       public int dummy1;
+       public int dummy2;
+       public int dummy3;
+
+       public GenStruct (int f) {
+               field = f;
+               dummy1 = dummy2 = dummy3 = 0;
+       }
+
+       public int method (int x) {
+               return x + field;
+       }
+}
+
 public class GenA<T> {
        public static T[] arr;
 
@@ -75,13 +121,73 @@ public class GenA<T> {
 
        public void except () {
                try {
-                       NonGen.doThrow();
+                       NonGen.doThrow ();
                }
-               catch (GenExc<T>)
-               {
+               catch (GenExc<T>) {
                        //Console.WriteLine("exception thrown");
                }
        }
+
+       public static void staticExcept () {
+               try {
+                       NonGen.doThrow ();
+               }
+               catch (GenExc<T>) {
+                       Console.WriteLine("exception thrown and caught");
+               }
+       }
+
+       public static int staticField = 54321;
+
+       public static int staticMethod () {
+               return staticField;
+       }
+
+       public static int staticMethodCaller () {
+               return staticMethod ();
+       }
+
+       public static float staticFloatField = 1.0f;
+
+       public static float staticFloatMethod () {
+               return staticFloatField;
+       }
+
+       public static int staticBiCaller (int x) {
+               return GenBi<int,T>.staticMethod (x);
+       }
+
+       public static void staticBiVoidCaller (int x) {
+               GenBi<int,T>.staticVoidMethod (x);
+       }
+
+       public static float staticBiFloatCaller () {
+               return GenBi<int,T>.staticFloatMethod ();
+       }
+
+       public static GenStruct<T> staticBiValueCaller (int x) {
+               return GenBi<int,T>.staticValueMethod (x);
+       }
+
+       public static int staticSharedBiCaller (int x) {
+               return GenBi<T,T>.staticMethod (x);
+       }
+
+       public static void staticSharedBiVoidCaller (int x) {
+               GenBi<T,T>.staticVoidMethod (x);
+       }
+
+       public static float staticSharedBiFloatCaller () {
+               return GenBi<T,T>.staticFloatMethod ();
+       }
+
+       public static GenStruct<T> staticSharedBiValueCaller (int x) {
+               return GenBi<T,T>.staticValueMethod (x);
+       }
+
+       public static long staticBiLongCaller (long x) {
+               return GenBi<int, T>.staticLongMethod (x);
+       }
 }
 
 public class GenB<T> {
@@ -229,6 +335,10 @@ public class main {
                        error ("object from " + method + " should have type " + t.ToString () + " but has type " + obj.GetType ().ToString ());
        }
 
+       public static int callStaticMethod<T> () {
+               return GenA<T>.staticMethod ();
+       }
+
        public static void work<T> (T obj, bool mustCatch) {
                EqualityComparer<T> comp = EqualityComparer<T>.Default;
 
@@ -256,6 +366,50 @@ public class main {
                if (!comp.Equals (ga.cast (obj), obj))
                        error ("cast");
 
+               if (callStaticMethod<T> () != 54321)
+                       error ("staticMethod");
+
+               GenBi<int,T>.field = 123;
+               if (GenA<T>.staticBiCaller (123) != 246)
+                       error ("staticBiCaller");
+               GenA<T>.staticBiVoidCaller (1234);
+               if (GenBi<int,T>.field != 1234)
+                       error ("staticBiVoidCaller");
+               if (GenA<T>.staticBiFloatCaller () != 1.0f)
+                       error ("staticBiFloatCaller");
+               if (GenA<T>.staticBiLongCaller (123) != 123 + 1234)
+                       error ("staticBiLongCaller");
+               GenStruct<T> gs = GenA<T>.staticBiValueCaller (987);
+               if (gs.field != 987)
+                       error ("staticBiValueCaller");
+
+               GenBi<T,T>.field = 123;
+               if (GenA<T>.staticSharedBiCaller (123) != 246)
+                       error ("staticSharedBiCaller");
+               GenA<T>.staticSharedBiVoidCaller (1234);
+               if (GenBi<T,T>.field != 1234)
+                       error ("staticSharedBiVoidCaller");
+               if (GenA<T>.staticSharedBiFloatCaller () != 1.0f)
+                       error ("staticSharedBiFloatCaller");
+               GenStruct<T> gss = GenA<T>.staticSharedBiValueCaller (987);
+               if (gss.field != 987)
+                       error ("staticSharedBiValueCaller");
+
+               IntVoidDelegate ivdel = new IntVoidDelegate (GenA<T>.staticMethod);
+               if (ivdel () != 54321)
+                       error ("staticMethod delegate");
+
+               Type gatype = typeof (GenA<T>);
+               MethodInfo staticMethodInfo = gatype.GetMethod ("staticMethod");
+               if ((Convert.ToInt32 (staticMethodInfo.Invoke (null, null))) != 54321)
+                       error ("staticMethod reflection");
+
+               if (GenA<T>.staticMethodCaller () != 54321)
+                       error ("staticMethodCaller");
+
+               if (GenA<T>.staticFloatMethod () != 1.0)
+                       error ("staticFloatMethod");
+
                new GenADeriv<T> ();
 
                if (mustCatch) {
@@ -266,11 +420,22 @@ public class main {
                        } catch (GenExc<ClassA>) {
                                didCatch = true;
                        }
-
                        if (!didCatch)
                                error ("except");
-               } else
+
+                       didCatch = false;
+
+                       try {
+                               GenA<T>.staticExcept ();
+                       } catch (GenExc<ClassA>) {
+                               didCatch = true;
+                       }
+                       if (!didCatch)
+                               error ("staticExcept");
+               } else {
                        ga.except ();
+                       GenA<T>.staticExcept ();
+               }
 
                MyDict<T, ClassB> dtb = new MyDict<T, ClassB> (obj, new ClassB ());
 
@@ -373,3 +538,5 @@ public class main {
                return 0;
        }
 }
+
+}
diff --git a/mono/tests/shared-generic-synchronized.2.cs b/mono/tests/shared-generic-synchronized.2.cs
new file mode 100644 (file)
index 0000000..b7ff647
--- /dev/null
@@ -0,0 +1,92 @@
+//
+//  shared-generic-synchronized.2.cs:
+//
+//    Tests for the 'synchronized' method attribute in shared generic methods
+//
+
+using System;
+using System.Threading;
+using System.Runtime.CompilerServices;
+
+public class Test<T> {
+
+       [MethodImplAttribute(MethodImplOptions.Synchronized)]
+       public int test () {
+               Monitor.Exit (this);
+               Monitor.Enter (this);
+               return 2 + 2;
+       }
+
+       [MethodImplAttribute(MethodImplOptions.Synchronized)]
+       public static int test_static () {
+               Monitor.Exit (typeof (Test<T>));
+               Monitor.Enter (typeof (Test<T>));
+               return 2 + 2;
+       }
+
+       [MethodImplAttribute(MethodImplOptions.Synchronized)]
+       public int test_exception () {
+               Monitor.Exit (this);
+               throw new Exception ("A");
+       }
+
+       [MethodImplAttribute(MethodImplOptions.Synchronized)]
+       public virtual int test_virtual () {
+               Monitor.Exit (this);
+               Monitor.Enter (this);
+               return 2 + 2;
+       }
+}
+
+class main {
+       public delegate int Delegate1 ();
+
+       static public int Main (String[] args) {
+               Test<string> b = new Test<string> ();
+               int res;
+
+               Console.WriteLine ("Test1...");
+               b.test ();
+               Console.WriteLine ("Test2...");
+               Test<string>.test_static ();
+               Console.WriteLine ("Test3...");
+               try {
+                       b.test_exception ();
+               }
+               catch (SynchronizationLockException ex) {
+                       return 1;
+               }
+               catch (Exception ex) {
+                       // OK
+               }
+
+               Console.WriteLine ("Test4...");
+               b.test_virtual ();
+
+               Console.WriteLine ("Test5...");
+               Delegate1 d = new Delegate1 (b.test);
+               res = d ();
+
+               Console.WriteLine ("Test6...");
+               d = new Delegate1 (Test<string>.test_static);
+               res = d ();
+
+               Console.WriteLine ("Test7...");
+               d = new Delegate1 (b.test_virtual);
+               res = d ();
+
+               Console.WriteLine ("Test8...");
+               d = new Delegate1 (b.test_exception);
+               try {
+                       d ();
+               }
+               catch (SynchronizationLockException ex) {
+                       return 2;
+               }
+               catch (Exception ex) {
+                       // OK
+               }
+
+               return 0;
+       }
+}