2007-09-20 Mark Probst <mark.probst@gmail.com>
authorMark Probst <mark.probst@gmail.com>
Thu, 20 Sep 2007 15:36:56 +0000 (15:36 -0000)
committerMark Probst <mark.probst@gmail.com>
Thu, 20 Sep 2007 15:36:56 +0000 (15:36 -0000)
* mini.c: First generics sharing implementation.  Can only share
in very simple cases.  If sharing doesn't work it falls back to
dedicated compilation.

* mini.h: Flag in MonoCompile to specify whether generic
compilation is shared.  Flags enum for marking which generic inst
of a context is used.  Prototypes for helper functions.

* generic-sharing.c: Helper functions for generic method sharing.

* optflags-def.h: Optimization flag (gshared) for enabling generic
method sharing added.

* Makefile.am: generic-sharing.c added.

2007-09-20  Mark Probst  <mark.probst@gmail.com>

* domain-internals.h: New flag in MonoJitInfo which marks shared
generic methods.  New hash table (shared_generics_hash) in
MonoDomain to keep track of shared generic methods.  Prototypes
for functions to register and lookup shared generic methods.

* domain.c: Support for registering and looking up shared generic
methods via a hash table (shared_generics_hash) in MonoDomain.

* class-internals.h: New exception to signal failure of shared
compilation of a generic method.  New counters for generics
sharing in MonoStats.

2007-09-20  Mark Probst  <mark.probst@gmail.com>

* generics-sharing.2.cs: added

* Makefile.am: added generics-sharing.2.cs

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

13 files changed:
mono/metadata/ChangeLog
mono/metadata/class-internals.h
mono/metadata/domain-internals.h
mono/metadata/domain.c
mono/mini/ChangeLog
mono/mini/Makefile.am
mono/mini/generic-sharing.c [new file with mode: 0644]
mono/mini/mini.c
mono/mini/mini.h
mono/mini/optflags-def.h
mono/tests/ChangeLog
mono/tests/Makefile.am
mono/tests/generics-sharing.2.cs [new file with mode: 0644]

index 47532c6a57b08dce32085a96b14edf6bada45c91..df9c141f6249d00289b49483a6d23e8d0cc37764 100644 (file)
@@ -1,3 +1,16 @@
+2007-09-20  Mark Probst  <mark.probst@gmail.com>
+
+       * domain-internals.h: New flag in MonoJitInfo which marks shared
+       generic methods.  New hash table (shared_generics_hash) in
+       MonoDomain to keep track of shared generic methods.  Prototypes
+       for functions to register and lookup shared generic methods.
+
+       * domain.c: Support for registering and looking up shared generic
+       methods via a hash table (shared_generics_hash) in MonoDomain.
+
+       * class-internals.h: New exception to signal failure of shared
+       compilation of a generic method.  New counters for generics
+       sharing in MonoStats.
 
 Thu Sep 20 16:59:36 CEST 2007 Paolo Molaro <lupus@ximian.com>
 
index 48294de81b9cb73f4dfe1c758830fdeeff8e6133..664043e9a72ce49fc17fa73a85e97380c36e1a7e 100644 (file)
@@ -175,6 +175,7 @@ enum {
        MONO_EXCEPTION_FILE_NOT_FOUND = 8,
        MONO_EXCEPTION_METHOD_ACCESS = 9,
        MONO_EXCEPTION_FIELD_ACCESS = 10,
+       MONO_EXCEPTION_GENERIC_SHARING_FAILED = 11,
        /* add other exception type */
 };
 
@@ -529,6 +530,9 @@ typedef struct {
        gulong jit_info_table_remove_count;
        gulong jit_info_table_lookup_count;
        gulong hazardous_pointer_count;
+       gulong generics_sharable_methods;
+       gulong generics_unsharable_methods;
+       gulong generics_shared_methods;
        gboolean enabled;
 } MonoStats;
 
index f5e0fbfeb4400026783ed2c394fdeb18e8ab5c55..52c5cab552c8df5715d4bb71edc80787bea6a933 100644 (file)
@@ -82,7 +82,7 @@ struct _MonoJitInfo {
        gpointer    code_start;
        guint32     used_regs;
        int         code_size;
-       guint32     num_clauses:24;
+       guint32     num_clauses:16;
        /* Whenever the code is domain neutral or 'shared' */
        gboolean    domain_neutral:1;
        gboolean    cas_inited:1;
@@ -92,6 +92,7 @@ struct _MonoJitInfo {
        gboolean    cas_method_assert:1;
        gboolean    cas_method_deny:1;
        gboolean    cas_method_permitonly:1;
+       gboolean    generic_shared:1;
        MonoJitExceptionInfo clauses [MONO_ZERO_LEN_ARRAY];
 };
 
@@ -179,6 +180,8 @@ struct _MonoDomain {
        GHashTable         *finalizable_objects_hash;
        /* Used when accessing 'domain_assemblies' */
        CRITICAL_SECTION    assemblies_lock;
+
+       GHashTable         *shared_generics_hash;
 };
 
 typedef struct  {
@@ -212,6 +215,13 @@ mono_jit_info_table_remove (MonoDomain *domain, MonoJitInfo *ji) MONO_INTERNAL;
 void
 mono_jit_info_add_aot_module (MonoImage *image, gpointer start, gpointer end) MONO_INTERNAL;
 
+MonoJitInfo*
+mono_domain_lookup_shared_generic (MonoDomain *domain, MonoMethod *method) MONO_INTERNAL;
+
+void
+mono_domain_register_shared_generic (MonoDomain *domain, MonoMethod *method, MonoJitInfo *jit_info) MONO_INTERNAL;
+
+
 /* 
  * Installs a new function which is used to return a MonoJitInfo for a method inside
  * an AOT module.
index 082ed848a49e4414c1f1805aebe55401f65d99f4..94dffe642aeca208f311430cb981b050182d372e 100644 (file)
@@ -1068,6 +1068,8 @@ mono_domain_create (void)
        InitializeCriticalSection (&domain->lock);
        InitializeCriticalSection (&domain->assemblies_lock);
 
+       domain->shared_generics_hash = NULL;
+
        mono_debug_domain_create (domain);
 
        mono_appdomains_lock ();
@@ -1645,6 +1647,26 @@ mono_domain_assembly_open (MonoDomain *domain, const char *name)
        return ass;
 }
 
+MonoJitInfo*
+mono_domain_lookup_shared_generic (MonoDomain *domain, MonoMethod *method)
+{
+       if (!domain->shared_generics_hash)
+               return NULL;
+
+       return g_hash_table_lookup (domain->shared_generics_hash, method);
+}
+
+void
+mono_domain_register_shared_generic (MonoDomain *domain, MonoMethod *method, MonoJitInfo *jit_info)
+{
+       if (!domain->shared_generics_hash)
+               domain->shared_generics_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
+
+       g_assert (domain->shared_generics_hash);
+
+       g_hash_table_insert (domain->shared_generics_hash, method, jit_info);
+}
+
 static void
 dynamic_method_info_free (gpointer key, gpointer value, gpointer user_data)
 {
@@ -1758,6 +1780,10 @@ mono_domain_free (MonoDomain *domain, gboolean force)
        domain->jit_trampoline_hash = NULL;
        g_hash_table_destroy (domain->delegate_trampoline_hash);
        domain->delegate_trampoline_hash = NULL;
+       if (domain->shared_generics_hash) {
+               g_hash_table_destroy (domain->shared_generics_hash);
+               domain->shared_generics_hash = NULL;
+       }
 
        DeleteCriticalSection (&domain->assemblies_lock);
        DeleteCriticalSection (&domain->lock);
index 80cce0331572b79943ea11b7b1a4e88be51a8255..1e141715dba4a543ca647c7265ed3bf56b5e1dc3 100644 (file)
@@ -1,3 +1,20 @@
+2007-09-20  Mark Probst  <mark.probst@gmail.com>
+
+       * mini.c: First generics sharing implementation.  Can only share
+       in very simple cases.  If sharing doesn't work it falls back to
+       dedicated compilation.
+
+       * mini.h: Flag in MonoCompile to specify whether generic
+       compilation is shared.  Flags enum for marking which generic inst
+       of a context is used.  Prototypes for helper functions.
+
+       * generic-sharing.c: Helper functions for generic method sharing.
+
+       * optflags-def.h: Optimization flag (gshared) for enabling generic
+       method sharing added.
+
+       * Makefile.am: generic-sharing.c added.
+
 2007-09-19 Daniel Nauck <dna@mono-project.de>
 
        * mini.c (mono_thread_abort): fixed typo in r86014. It should be '==' instead of '!='.
index 0ace7b13e1df0e60e67fe673c16f12823c06079f..231b4f94389407cd43307df8cbd11878a8187f24 100644 (file)
@@ -230,7 +230,8 @@ common_sources = \
        declsec.c       \
        declsec.h       \
        wapihandles.c   \
-       branch-opts.c
+       branch-opts.c   \
+       generic-sharing.c
 
 test_sources =                 \
        basic-calls.cs  \
diff --git a/mono/mini/generic-sharing.c b/mono/mini/generic-sharing.c
new file mode 100644 (file)
index 0000000..233fb06
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * generic-sharing.c: Support functions for generic sharing.
+ *
+ * Author:
+ *   Mark Probst (mark.probst@gmail.com)
+ *
+ * (C) 2007 Novell, Inc.
+ */
+
+#include <config.h>
+
+#include <mono/metadata/class.h>
+
+#include "mini.h"
+
+static gboolean
+generic_inst_uses_type (MonoGenericInst *inst, MonoType *type)
+{
+       int i;
+
+       if (!inst)
+               return FALSE;
+
+       for (i = 0; i < inst->type_argc; ++i)
+               if (mono_metadata_type_equal (type, inst->type_argv [i]))
+                       return TRUE;
+       return FALSE;
+}
+
+static int context_check_context_used (MonoGenericContext *context, MonoGenericContext *shared_context);
+
+static int
+type_check_context_used (MonoType *type, MonoGenericContext *context, gboolean recursive)
+{
+       int context_used = 0;
+
+       if (generic_inst_uses_type (context->class_inst, type))
+               context_used |= MONO_GENERIC_CONTEXT_USED_CLASS;
+       if (generic_inst_uses_type (context->method_inst, type))
+               context_used |= MONO_GENERIC_CONTEXT_USED_METHOD;
+
+       if (recursive) {
+               int t = mono_type_get_type (type);
+
+               if (t == MONO_TYPE_CLASS)
+                       context_used |= mono_class_check_context_used (mono_type_get_class (type), context);
+               else if (t == MONO_TYPE_GENERICINST) {
+                       MonoGenericClass *gclass = type->data.generic_class;
+
+                       context_used |= context_check_context_used (&gclass->context, context);
+                       context_used |= mono_class_check_context_used (gclass->container_class, context);
+               }
+       }
+
+       return context_used;
+}
+
+static int
+inst_check_context_used (MonoGenericInst *inst, MonoGenericContext *context)
+{
+       int context_used = 0;
+       int i;
+
+       if (!inst)
+               return 0;
+
+       for (i = 0; i < inst->type_argc; ++i)
+               context_used |= type_check_context_used (inst->type_argv [i], context, TRUE);
+
+       return context_used;
+}
+
+static int
+context_check_context_used (MonoGenericContext *context, MonoGenericContext *shared_context)
+{
+       int context_used = 0;
+
+       context_used |= inst_check_context_used (context->class_inst, shared_context);
+       context_used |= inst_check_context_used (context->method_inst, shared_context);
+
+       return context_used;
+}
+
+int
+mono_method_check_context_used (MonoMethod *method, MonoGenericContext *context)
+{
+       MonoGenericContext *method_context = mono_method_get_context (method);
+
+       if (!method_context)
+               return 0;
+
+       return context_check_context_used (method_context, context);
+}
+
+int
+mono_class_check_context_used (MonoClass *class, MonoGenericContext *context)
+{
+       int context_used = 0;
+
+       context_used |= type_check_context_used (&class->this_arg, context, FALSE);
+       context_used |= type_check_context_used (&class->byval_arg, context, FALSE);
+
+       if (class->generic_class)
+               context_used |= context_check_context_used (&class->generic_class->context, context);
+
+       return context_used;
+}
+
+static gboolean
+generic_inst_is_sharable (MonoGenericInst *inst)
+{
+       int i;
+
+       for (i = 0; i < inst->type_argc; ++i) {
+               int type = mono_type_get_type (inst->type_argv [i]);
+
+               if (type != MONO_TYPE_CLASS && type != MONO_TYPE_STRING && type != MONO_TYPE_OBJECT &&
+                               type != MONO_TYPE_SZARRAY && type != MONO_TYPE_ARRAY)
+                       return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean
+generic_context_is_sharable (MonoGenericContext *context)
+{
+       g_assert (context->class_inst || context->method_inst);
+
+       if (context->class_inst && !generic_inst_is_sharable (context->class_inst))
+               return FALSE;
+
+       if (context->method_inst && !generic_inst_is_sharable (context->method_inst))
+               return FALSE;
+
+       return TRUE;
+}
+
+gboolean
+mono_method_is_generic_impl (MonoMethod *method)
+{
+       return method->klass->generic_class != NULL && method->is_inflated;
+}
+
+gboolean
+mono_method_is_generic_sharable_impl (MonoMethod *method)
+{
+       if (!mono_method_is_generic_impl (method))
+               return FALSE;
+
+       if (method->is_inflated) {
+               MonoMethodInflated *inflated = (MonoMethodInflated*)method;
+               MonoGenericContext *context = &inflated->context;
+
+               if (!generic_context_is_sharable (context))
+                       return FALSE;
+
+               g_assert (inflated->declaring);
+
+               if (inflated->declaring->generic_container) {
+                       g_assert (inflated->declaring->generic_container->type_params);
+
+                       if (inflated->declaring->generic_container->type_params->constraints)
+                               return FALSE;
+               }
+       }
+
+       if (method->klass->generic_class) {
+               if (!generic_context_is_sharable (&method->klass->generic_class->context))
+                       return FALSE;
+
+               g_assert (method->klass->generic_class->container_class &&
+                               method->klass->generic_class->container_class->generic_container &&
+                               method->klass->generic_class->container_class->generic_container->type_params);
+
+               if (method->klass->generic_class->container_class->generic_container->type_params->constraints)
+                       return FALSE;
+       }
+
+       return TRUE;
+}
+
+static MonoGenericInst*
+share_generic_inst (MonoCompile *cfg, MonoGenericInst *inst)
+{
+       MonoType **type_argv;
+       int i;
+
+       if (!inst)
+               return NULL;
+
+       type_argv = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoType*) * inst->type_argc);
+
+       for (i = 0; i < inst->type_argc; ++i)
+               type_argv [i] = &mono_defaults.object_class->byval_arg;
+
+       return mono_metadata_get_generic_inst (inst->type_argc, type_argv);
+}
+
+MonoGenericContext*
+mono_make_shared_context (MonoCompile *cfg, MonoGenericContext *context)
+{
+       MonoGenericContext *shared = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoGenericContext));
+
+       shared->class_inst = share_generic_inst (cfg, context->class_inst);
+       shared->method_inst = share_generic_inst (cfg, context->method_inst);
+
+       return shared;
+}
index a5b9b30d0985cd602b6cd9138391fca2e61bb897..75fb952bd72fcbeb1c0b84751b3392f50826e3c9 100644 (file)
                g_free (field_fname);   \
                goto exception_exit;    \
        } while (0)
+#define GENERIC_SHARING_FAILURE do {   \
+               if (cfg->generic_shared) {      \
+                       /* g_print ("sharing failed for method %s.%s in %s:%d\n", method->klass->name, method->name, __FILE__, __LINE__); */ \
+                       cfg->exception_type = MONO_EXCEPTION_GENERIC_SHARING_FAILED;    \
+                       goto exception_exit;    \
+               }                       \
+       } while (0)
 
 /* 
  * this is used to determine when some branch optimizations are possible: we exclude FP compares
@@ -135,7 +142,7 @@ static void dec_foreach (MonoInst *tree, MonoCompile *cfg);
 
 static int mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_bblock, MonoBasicBlock *end_bblock, 
                   int locals_offset, MonoInst *return_var, GList *dont_inline, MonoInst **inline_args, 
-                  guint inline_offset, gboolean is_virtual_call);
+                  guint inline_offset, gboolean is_virtual_call, MonoGenericContext *shared_context);
 
 /* helper methods signature */
 static MonoMethodSignature *helper_sig_class_init_trampoline = NULL;
@@ -3508,7 +3515,7 @@ static gboolean check_inline_caller_method_name_limit (MonoMethod *caller_method
 
 static int
 inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoBasicBlock *bblock, MonoInst **sp,
-               guchar *ip, guint real_offset, GList *dont_inline, MonoBasicBlock **last_b, gboolean inline_allways)
+               guchar *ip, guint real_offset, GList *dont_inline, MonoBasicBlock **last_b, gboolean inline_allways, MonoGenericContext *shared_context)
 {
        MonoInst *ins, *rvar = NULL;
        MonoMethodHeader *cheader;
@@ -3566,7 +3573,7 @@ inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig,
        prev_cil_offset_to_bb_len = cfg->cil_offset_to_bb_len;
        prev_cil_start = cfg->cil_start;
 
-       costs = mono_method_to_ir (cfg, cmethod, sbblock, ebblock, new_locals_offset, rvar, dont_inline, sp, real_offset, *ip == CEE_CALLVIRT);
+       costs = mono_method_to_ir (cfg, cmethod, sbblock, ebblock, new_locals_offset, rvar, dont_inline, sp, real_offset, *ip == CEE_CALLVIRT, shared_context);
 
        cfg->inlined_method = prev_inlined_method;
        cfg->cil_offset_to_bb = prev_cil_offset_to_bb;
@@ -3985,7 +3992,7 @@ set_exception_type_from_invalid_il (MonoCompile *cfg, MonoMethod *method, unsign
 static int
 mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_bblock, MonoBasicBlock *end_bblock, 
                   int locals_offset, MonoInst *return_var, GList *dont_inline, MonoInst **inline_args, 
-                  guint inline_offset, gboolean is_virtual_call)
+                  guint inline_offset, gboolean is_virtual_call, MonoGenericContext *shared_context)
 {
        MonoInst *zero_int32, *zero_int64, *zero_ptr, *zero_obj, *zero_r8;
        MonoInst *ins, **sp, **stack_start;
@@ -4044,7 +4051,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
        end = ip + header->code_size;
        mono_jit_stats.cil_code_size += header->code_size;
 
-       if (sig->is_inflated)
+       if (cfg->generic_shared) {
+               g_assert (shared_context);
+               generic_context = shared_context;
+       } else if (sig->is_inflated)
                generic_context = mono_method_get_context (method);
        else if (generic_container)
                generic_context = &generic_container->context;
@@ -4684,6 +4694,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        if (!cmethod)
                                goto load_error;
 
+                       if (cfg->generic_shared && mono_method_check_context_used (cmethod, generic_context))
+                               GENERIC_SHARING_FAILURE;
+
                        if (mono_security_get_mode () == MONO_SECURITY_MODE_CAS) {
                                if (check_linkdemand (cfg, method, cmethod, bblock, ip))
                                        INLINE_FAILURE;
@@ -4780,6 +4793,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        if (cmethod && cmethod->klass->generic_container)
                                UNVERIFIED;
 
+                       if (cfg->generic_shared && cmethod && mono_method_check_context_used (cmethod, generic_context))
+                               GENERIC_SHARING_FAILURE;
+
                        CHECK_STACK (n);
 
                        //g_assert (!virtual || fsig->hasthis);
@@ -4953,7 +4969,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                        allways = TRUE;
                                }
 
-                               if ((costs = inline_method (cfg, cmethod, fsig, bblock, sp, ip, real_offset, dont_inline, &ebblock, allways))) {
+                               if ((costs = inline_method (cfg, cmethod, fsig, bblock, sp, ip, real_offset, dont_inline, &ebblock, allways, shared_context))) {
                                        ip += 5;
                                        real_offset += 5;
 
@@ -5743,7 +5759,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        MonoInst *iargs [2];
                        MonoMethodSignature *fsig;
                        int temp;
-                       
+
                        CHECK_OPSIZE (5);
                        token = read32 (ip + 1);
                        cmethod = mini_get_method (method, token, NULL, generic_context);
@@ -5756,6 +5772,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        if (!mono_class_init (cmethod->klass))
                                goto load_error;
 
+                       if (cfg->generic_shared && mono_method_check_context_used (cmethod, generic_context))
+                               GENERIC_SHARING_FAILURE;
+
                        if (mono_security_get_mode () == MONO_SECURITY_MODE_CAS) {
                                if (check_linkdemand (cfg, method, cmethod, bblock, ip))
                                        INLINE_FAILURE;
@@ -5816,7 +5835,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                    !g_list_find (dont_inline, cmethod)) {
                                        int costs;
                                        MonoBasicBlock *ebblock;
-                                       if ((costs = inline_method (cfg, cmethod, fsig, bblock, sp, ip, real_offset, dont_inline, &ebblock, FALSE))) {
+                                       if ((costs = inline_method (cfg, cmethod, fsig, bblock, sp, ip, real_offset, dont_inline, &ebblock, FALSE, shared_context))) {
 
                                                ip += 5;
                                                real_offset += 5;
@@ -5865,6 +5884,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        if (sp [0]->type != STACK_OBJ)
                                UNVERIFIED;
 
+                       if (cfg->generic_shared && mono_class_check_context_used (klass, generic_context))
+                               GENERIC_SHARING_FAILURE;
+
                        /* Needed by the code generated in inssel.brg */
                        mono_get_got_var (cfg);
 
@@ -5880,7 +5902,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                iargs [0] = sp [0];
                                
                                costs = inline_method (cfg, mono_isinst, mono_method_signature (mono_isinst), bblock, 
-                                                          iargs, ip, real_offset, dont_inline, &ebblock, TRUE);
+                                                      iargs, ip, real_offset, dont_inline, &ebblock, TRUE, shared_context);
                        
                                g_assert (costs > 0);
                                
@@ -5920,6 +5942,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        klass = mini_get_class (method, token, generic_context);
                        CHECK_TYPELOAD (klass);
 
+                       if (cfg->generic_shared && mono_class_check_context_used (klass, generic_context))
+                               GENERIC_SHARING_FAILURE;
+
                        if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg)) {
                                /* CASTCLASS */
                                if (klass->marshalbyref || klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
@@ -5933,7 +5958,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                        iargs [0] = sp [0];
                                        
                                        costs = inline_method (cfg, mono_castclass, mono_method_signature (mono_castclass), bblock, 
-                                                                  iargs, ip, real_offset, dont_inline, &ebblock, TRUE);
+                                                       iargs, ip, real_offset, dont_inline, &ebblock, TRUE, shared_context);
                                
                                        g_assert (costs > 0);
                                        
@@ -6017,6 +6042,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        klass = mini_get_class (method, token, generic_context);
                        CHECK_TYPELOAD (klass);
 
+                       if (cfg->generic_shared && mono_class_check_context_used (klass, generic_context))
+                               GENERIC_SHARING_FAILURE;
+
                        if (mono_class_is_nullable (klass)) {
                                int v = handle_unbox_nullable (cfg, bblock, *sp, ip, klass);
                                NEW_TEMPLOAD (cfg, *sp, v);
@@ -6056,6 +6084,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        if (sp [0]->type != STACK_OBJ)
                                UNVERIFIED;
 
+                       if (cfg->generic_shared && mono_class_check_context_used (klass, generic_context))
+                               GENERIC_SHARING_FAILURE;
+
                        /* Needed by the code generated in inssel.brg */
                        mono_get_got_var (cfg);
                
@@ -6071,7 +6102,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                iargs [0] = sp [0];
                                
                                costs = inline_method (cfg, mono_castclass, mono_method_signature (mono_castclass), bblock, 
-                                                          iargs, ip, real_offset, dont_inline, &ebblock, TRUE);
+                                               iargs, ip, real_offset, dont_inline, &ebblock, TRUE, shared_context);
                        
                                g_assert (costs > 0);
                                
@@ -6167,7 +6198,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
 
                                        if (cfg->opt & MONO_OPT_INLINE) {
                                                costs = inline_method (cfg, stfld_wrapper, mono_method_signature (stfld_wrapper), bblock, 
-                                                                      iargs, ip, real_offset, dont_inline, &ebblock, TRUE);
+                                                               iargs, ip, real_offset, dont_inline, &ebblock, TRUE, shared_context);
                                                g_assert (costs > 0);
                                                      
                                                ip += 5;
@@ -6247,7 +6278,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                        NEW_ICONST (cfg, iargs [3], klass->valuetype ? field->offset - sizeof (MonoObject) : field->offset);
                                        if ((cfg->opt & MONO_OPT_INLINE) && !MONO_TYPE_ISSTRUCT (mono_method_signature (wrapper)->ret)) {
                                                costs = inline_method (cfg, wrapper, mono_method_signature (wrapper), bblock, 
-                                                                      iargs, ip, real_offset, dont_inline, &ebblock, TRUE);
+                                                               iargs, ip, real_offset, dont_inline, &ebblock, TRUE, shared_context);
                                                g_assert (costs > 0);
                                                      
                                                ip += 5;
@@ -6327,6 +6358,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        if (!dont_verify && !cfg->skip_visibility && !mono_method_can_access_field (method, field))
                                FIELD_ACCESS_FAILURE;
 
+                       if (cfg->generic_shared && mono_class_check_context_used (klass, generic_context))
+                               GENERIC_SHARING_FAILURE;
+
                        g_assert (!(field->type->attrs & FIELD_ATTRIBUTE_LITERAL));
 
                        if ((*ip) == CEE_STSFLD)
@@ -6532,6 +6566,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        break;
                case CEE_BOX: {
                        MonoInst *val;
+
                        CHECK_STACK (1);
                        --sp;
                        val = *sp;
@@ -6540,6 +6575,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        klass = mini_get_class (method, token, generic_context);
                        CHECK_TYPELOAD (klass);
 
+                       if (cfg->generic_shared && mono_class_check_context_used (klass, generic_context))
+                               GENERIC_SHARING_FAILURE;
+
                        if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg)) {
                                *sp++ = val;
                                ip += 5;
@@ -6591,7 +6629,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        inline_costs += 1;
                        break;
                }
-               case CEE_NEWARR:
+               case CEE_NEWARR: {
+                       int context_used;
+
                        CHECK_STACK (1);
                        MONO_INST_NEW (cfg, ins, *ip);
                        ins->cil_code = ip;
@@ -6612,6 +6652,15 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
 
                        klass = mini_get_class (method, token, generic_context);
                        CHECK_TYPELOAD (klass);
+
+                       if (cfg->generic_shared)
+                               context_used = mono_class_check_context_used (klass, generic_context);
+                       else
+                               context_used = 0;
+
+                       if (context_used)
+                               GENERIC_SHARING_FAILURE;
+
                        ins->inst_newa_class = klass;
                        ins->inst_newa_len = *sp;
                        ins->type = STACK_OBJ;
@@ -6664,6 +6713,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        }
                        inline_costs += 1;
                        break;
+               }
                case CEE_LDLEN:
                        CHECK_STACK (1);
                        --sp;
@@ -6956,6 +7006,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        mono_class_init (klass);
                        ins->cil_code = ip;
 
+                       if (cfg->generic_shared && mono_class_check_context_used (klass, generic_context))
+                               GENERIC_SHARING_FAILURE;
+
                        loc = mono_compile_create_var (cfg, &mono_defaults.typed_reference_class->byval_arg, OP_LOCAL);
                        NEW_TEMPLOADA (cfg, ins->inst_right, loc->inst_c0);
 
@@ -6986,6 +7039,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        }
                        else {
                                handle = mono_ldtoken (image, n, &handle_class, generic_context);
+                               if (cfg->generic_shared &&
+                                               mono_class_check_context_used (handle_class, generic_context))
+                                       GENERIC_SHARING_FAILURE;
                        }
                        if (!handle)
                                goto load_error;
@@ -6995,6 +7051,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                int temp;
                                MonoInst *res, *store, *addr, *vtvar, *iargs [3];
 
+                               GENERIC_SHARING_FAILURE;
+
                                vtvar = mono_compile_create_var (cfg, &handle_class->byval_arg, OP_LOCAL); 
 
                                NEW_IMAGECONST (cfg, iargs [0], image);
@@ -7434,6 +7492,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                        goto load_error;
                                mono_class_init (cmethod->klass);
 
+                               if (cfg->generic_shared && mono_method_check_context_used (cmethod, generic_context))
+                                       GENERIC_SHARING_FAILURE;
+
                                cil_method = cmethod;
                                if (!dont_verify && !cfg->skip_visibility && !mono_method_can_access_method (method, cmethod))
                                        METHOD_ACCESS_FAILURE;
@@ -7471,6 +7532,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                        goto load_error;
                                mono_class_init (cmethod->klass);
 
+                               if (cfg->generic_shared && mono_method_check_context_used (cmethod, generic_context))
+                                       GENERIC_SHARING_FAILURE;
+
                                if (mono_security_get_mode () == MONO_SECURITY_MODE_CAS) {
                                        if (check_linkdemand (cfg, method, cmethod, bblock, ip))
                                                INLINE_FAILURE;
@@ -7654,6 +7718,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                token = read32 (ip + 2);
                                klass = mini_get_class (method, token, generic_context);
                                CHECK_TYPELOAD (klass);
+
+                               if (cfg->generic_shared && mono_class_check_context_used (klass, generic_context))
+                                       GENERIC_SHARING_FAILURE;
+
                                if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg)) {
                                        MonoInst *store, *load;
                                        NEW_PCONST (cfg, load, NULL);
@@ -7745,6 +7813,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                break;
                        }
                        case CEE_SIZEOF:
+                               GENERIC_SHARING_FAILURE;
+
                                CHECK_STACK_OVF (1);
                                CHECK_OPSIZE (6);
                                token = read32 (ip + 2);
@@ -10534,11 +10604,21 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool
        MonoJitInfo *jinfo;
        int dfn = 0, i, code_size_ratio;
        gboolean deadce_has_run = FALSE;
+       gboolean try_generic_shared = (opts & MONO_OPT_GSHARED) && mono_method_is_generic_sharable_impl (method);
+       MonoGenericContext *shared_context;
 
        mono_jit_stats.methods_compiled++;
        if (mono_profiler_get_events () & MONO_PROFILE_JIT_COMPILATION)
                mono_profiler_method_jit (method);
 
+       if (opts & MONO_OPT_GSHARED) {
+               if (try_generic_shared)
+                       mono_stats.generics_sharable_methods++;
+               else if (mono_method_is_generic_impl (method))
+                       mono_stats.generics_unsharable_methods++;
+       }
+
+ restart_compile:
        cfg = g_new0 (MonoCompile, 1);
        cfg->method = method;
        cfg->mempool = mono_mempool_new ();
@@ -10549,6 +10629,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool
        cfg->verbose_level = mini_verbose;
        cfg->compile_aot = compile_aot;
        cfg->skip_visibility = method->skip_visibility;
+       cfg->generic_shared = try_generic_shared;
        cfg->token_info_hash = g_hash_table_new (NULL, NULL);
        if (!header) {
                cfg->exception_type = MONO_EXCEPTION_INVALID_PROGRAM;
@@ -10571,7 +10652,19 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool
         */
        mono_compile_create_vars (cfg);
 
-       if ((i = mono_method_to_ir (cfg, method, NULL, NULL, cfg->locals_start, NULL, NULL, NULL, 0, FALSE)) < 0) {
+       if (try_generic_shared)
+               shared_context = mono_make_shared_context (cfg, mono_method_get_context (method));
+       else
+               shared_context = NULL;
+
+       if ((i = mono_method_to_ir (cfg, method, NULL, NULL, cfg->locals_start, NULL, NULL, NULL, 0, FALSE, shared_context)) < 0) {
+               if (try_generic_shared && cfg->exception_type == MONO_EXCEPTION_GENERIC_SHARING_FAILED) {
+                       mono_destroy_compile (cfg);
+                       try_generic_shared = FALSE;
+                       goto restart_compile;
+               }
+               g_assert (cfg->exception_type != MONO_EXCEPTION_GENERIC_SHARING_FAILED);
+
                if (cfg->prof_options & MONO_PROFILE_JIT_COMPILATION)
                        mono_profiler_method_end_jit (method, NULL, MONO_PROFILE_FAILED);
                /* cfg contains the details of the failure, so let the caller cleanup */
@@ -10802,6 +10895,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool
        jinfo->used_regs = cfg->used_int_regs;
        jinfo->domain_neutral = (cfg->opt & MONO_OPT_SHARED) != 0;
        jinfo->cas_inited = FALSE; /* initialization delayed at the first stalk walk using this method */
+       jinfo->generic_shared = cfg->generic_shared;
 
        if (header->num_clauses) {
                int i;
@@ -10873,6 +10967,42 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool
        return cfg;
 }
 
+static MonoMethod*
+method_get_declaring_generic_method (MonoMethod *method)
+{
+       MonoMethodInflated *inflated;
+
+       g_assert (method->is_inflated);
+
+       inflated = (MonoMethodInflated*)method;
+
+       return inflated->declaring;
+}
+
+static MonoJitInfo*
+lookup_generic_method (MonoDomain *domain, MonoMethod *method)
+{
+       MonoMethod *open_method;
+
+       if (!mono_method_is_generic_sharable_impl (method))
+               return NULL;
+
+       open_method = method_get_declaring_generic_method (method);
+
+       return mono_domain_lookup_shared_generic (domain, open_method);
+}
+
+static MonoJitInfo*
+lookup_method (MonoDomain *domain, MonoMethod *method)
+{
+       MonoJitInfo *ji = mono_internal_hash_table_lookup (&domain->jit_code_hash, method);
+
+       if (ji != NULL)
+               return ji;
+
+       return lookup_generic_method (domain, method);
+}
+
 static gpointer
 mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, int opt)
 {
@@ -11022,7 +11152,7 @@ mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, in
        /* Check if some other thread already did the job. In this case, we can
        discard the code this thread generated. */
 
-       if ((info = mono_internal_hash_table_lookup (&target_domain->jit_code_hash, method))) {
+       if ((info = lookup_method (target_domain, method))) {
                /* We can't use a domain specific method in another domain */
                if ((target_domain == mono_domain_get ()) || info->domain_neutral) {
                        code = info->code_start;
@@ -11033,6 +11163,13 @@ mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, in
        if (code == NULL) {
                mono_internal_hash_table_insert (&target_domain->jit_code_hash, method, cfg->jit_info);
                code = cfg->native_code;
+
+               if (cfg->generic_shared && mono_method_is_generic_sharable_impl (method)) {
+                       /* g_print ("inserting method %s.%s.%s\n", method->klass->name_space, method->klass->name, method->name); */
+                       mono_domain_register_shared_generic (target_domain, 
+                               method_get_declaring_generic_method (method), cfg->jit_info);
+                       mono_stats.generics_shared_methods++;
+               }
        }
 
        mono_destroy_compile (cfg);
@@ -11090,7 +11227,7 @@ mono_jit_compile_method_with_opt (MonoMethod *method, guint32 opt)
 
        mono_domain_lock (target_domain);
 
-       if ((info = mono_internal_hash_table_lookup (&target_domain->jit_code_hash, method))) {
+       if ((info = lookup_method (target_domain, method))) {
                /* We can't use a domain specific method in another domain */
                if (! ((domain != target_domain) && !info->domain_neutral)) {
                        mono_domain_unlock (target_domain);
@@ -11196,7 +11333,7 @@ mono_jit_find_compiled_method (MonoDomain *domain, MonoMethod *method)
 
        mono_domain_lock (target_domain);
 
-       if ((info = mono_internal_hash_table_lookup (&target_domain->jit_code_hash, method))) {
+       if ((info = lookup_method (target_domain, method))) {
                /* We can't use a domain specific method in another domain */
                if (! ((domain != target_domain) && !info->domain_neutral)) {
                        mono_domain_unlock (target_domain);
@@ -12224,6 +12361,10 @@ print_jit_stats (void)
                g_print ("Generics metadata size: %ld\n", mono_stats.generics_metadata_size);
                g_print ("Generics virtual invokes: %ld\n", mono_jit_stats.generic_virtual_invocations);
 
+               g_print ("Sharable generic methods: %ld\n", mono_stats.generics_sharable_methods);
+               g_print ("Unsharable generic methods: %ld\n", mono_stats.generics_unsharable_methods);
+               g_print ("Shared generic methods: %ld\n", mono_stats.generics_shared_methods);
+
                g_print ("Dynamic code allocs:    %ld\n", mono_stats.dynamic_code_alloc_count);
                g_print ("Dynamic code bytes:     %ld\n", mono_stats.dynamic_code_bytes_count);
                g_print ("Dynamic code frees:     %ld\n", mono_stats.dynamic_code_frees_count);
index 56f19f9b1ed539b85b41ea69ddf0c0559432519f..6e0ab2f1f120c2cbd8b335124391bdf94e100d06 100644 (file)
@@ -608,6 +608,7 @@ typedef struct {
        guint            dont_verify_stack_merge : 1;
        guint            unverifiable : 1;
        guint            skip_visibility : 1;
+       guint            generic_shared : 1;
        gpointer         debug_info;
        guint32          lmf_offset;
        guint16          *intvars;
@@ -828,6 +829,16 @@ enum {
        MONO_EXC_INTRINS_NUM
 };
 
+/*
+ * Flags for which contexts were used in inflating a generic.
+ */
+enum {
+       MONO_GENERIC_CONTEXT_USED_CLASS = 1,
+       MONO_GENERIC_CONTEXT_USED_METHOD = 2
+};
+
+#define MONO_GENERIC_CONTEXT_USED_BOTH         (MONO_GENERIC_CONTEXT_USED_CLASS | MONO_GENERIC_CONTEXT_USED_METHOD)
+
 typedef void (*MonoInstFunc) (MonoInst *tree, gpointer data);
 
 /* main function */
@@ -1101,4 +1112,15 @@ int mini_wapi_hps     (int argc, char **argv);
 int mini_wapi_semdel  (int argc, char **argv);
 int mini_wapi_seminfo (int argc, char **argv);
 
+/* Generic sharing */
+
+int mono_method_check_context_used (MonoMethod *method, MonoGenericContext *context) MONO_INTERNAL;
+int mono_class_check_context_used (MonoClass *class, MonoGenericContext *context) MONO_INTERNAL;
+
+gboolean mono_method_is_generic_impl (MonoMethod *method) MONO_INTERNAL;
+gboolean mono_method_is_generic_sharable_impl (MonoMethod *method) MONO_INTERNAL;
+
+MonoGenericContext* mono_make_shared_context (MonoCompile *cfg, MonoGenericContext *context) MONO_INTERNAL;
+
+
 #endif /* __MONO_MINI_H__ */
index 126e9c04c145aec9a380b1239d01719b0c02e313..c33d602a4432f03fad1ee005aa53228dd7530af3 100644 (file)
@@ -22,3 +22,4 @@ OPTFLAG(EXCEPTION,20, "exception",  "Optimize exception catch blocks")
 OPTFLAG(SSA      ,21, "ssa",        "Use plain SSA form")
 OPTFLAG(TREEPROP ,22, "treeprop",   "Tree propagation")
 OPTFLAG(SSE2     ,23, "sse2",       "SSE2 instructions on x86")
+OPTFLAG(GSHARED  ,24, "gshared",    "Share generics")
index 56b68faca8637e0ad6dd7ca62eeb291f1ec5b8e5..d222a8b868241f4410e001bb35731b88d4e28b0b 100644 (file)
@@ -1,3 +1,9 @@
+2007-09-20  Mark Probst  <mark.probst@gmail.com>
+
+       * generics-sharing.2.cs: added
+
+       * Makefile.am: added generics-sharing.2.cs
+
 2007-09-19  Marek Habersack  <mhabersack@novell.com>
 
        * assemblyresolve/Makefile.am (prereq): build the assemblies in
index 7dd70a1fe0fe92dc0615c8ed1932256fbf4605cd..313c887f11d7f816af1ec796b4dfa55a3e5263e8 100644 (file)
@@ -295,6 +295,7 @@ TEST_CS2_SRC = \
        module-cctor-loader.2.cs        \
        generics-invoke-byref.2.cs      \
        generic-signature-compare.2.cs  \
+       generics-sharing.2.cs   \
        bug-80392.2.cs          \
        bug-82194.2.cs
 
diff --git a/mono/tests/generics-sharing.2.cs b/mono/tests/generics-sharing.2.cs
new file mode 100644 (file)
index 0000000..3558bc9
--- /dev/null
@@ -0,0 +1,124 @@
+using System;
+using System.Collections.Generic;
+
+public class ClassA {}
+public class ClassB {}
+public class ClassC {}
+
+public class NonGen {
+       public static int field = 123;
+}
+
+public class GenA<T> {
+       static T[] arr;
+
+       public static GenA () {
+               arr = new T [3];
+       }
+
+       public GenA () {}
+
+       public GenA<T> newGen () {
+               return new GenA<T> ();
+       }
+
+       public GenA<int> newGenInt () {
+               return new GenA<int> ();
+       }
+
+       public int getGenField () {
+               return GenB<ClassA>.field;
+       }
+
+       public int getNonGenField () {
+               return NonGen.field;
+       }
+
+       public T[] getArr () {
+               return arr;
+       }
+
+       public T[] newArr () {
+               return new T [3];
+       }
+
+       public GenB<GenB<T>>[] newArrNested () {
+               /*
+               GenB<GenB<T>>[] arr = null;
+               for (int i = 0; i < 10000000; ++i)
+                       arr = new GenB<GenB<T>> [3];
+               */
+               return new GenB<GenB<T>> [3];
+       }
+
+       public int hash (T obj) {
+               return obj.GetHashCode ();
+       }
+
+       public T ident (T obj) {
+               return obj;
+       }
+
+       public T cast (Object obj) {
+               return (T)obj;
+       }
+}
+
+public class GenB<T> {
+       public static int field = 345;
+}
+
+public class main {
+       static bool haveError = false;
+
+       public static void error (string message) {
+               haveError = true;
+               Console.WriteLine (message);
+       }
+
+       public static void typeCheck (String method, Object obj, Type t) {
+               if (obj.GetType () != t)
+                       error ("object from " + method + " should have type " + t.ToString () + " but has type " + obj.GetType ().ToString ());
+       }
+
+       public static void work<T> (T obj) {
+               EqualityComparer<T> comp = EqualityComparer<T>.Default;
+
+               GenA<T> ga = new GenA<T> ();
+
+               typeCheck ("newGen", ga.newGen (), typeof (GenA<T>));
+               typeCheck ("newGenInt", ga.newGenInt (), typeof (GenA<int>));
+               typeCheck ("getArr", ga.getArr (), typeof (T[]));
+               typeCheck ("newArr", ga.newArr (), typeof (T[]));
+               //ga.newArrNested ();
+               typeCheck ("newArrNested", ga.newArrNested (), typeof (GenB<GenB<T>>[]));
+
+               if (ga.getGenField () != 345)
+                       error ("getGenField");
+
+               if (ga.getNonGenField () != 123)
+                       error ("getNonGenField");
+
+               ga.hash (obj);
+
+               if (!comp.Equals (ga.ident (obj), obj))
+                       error ("ident");
+
+               if (!comp.Equals (ga.cast (obj), obj))
+                       error ("cast");
+       }
+
+       public static int Main ()
+       {
+               work<ClassA> (new ClassA ());
+               work<ClassB> (new ClassB ());
+               work<ClassC> (new ClassC ());
+               work<GenA<ClassA>> (new GenA<ClassA> ());
+               work<int[]> (new int[3]);
+               work<int> (123);
+
+               if (haveError)
+                       return 1;
+               return 0;
+       }
+}