From 2a27c1094d9d61d8d5750140003db0ac6995d5c0 Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Thu, 20 Sep 2007 15:36:56 +0000 Subject: [PATCH] 2007-09-20 Mark Probst * 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 * 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 * generics-sharing.2.cs: added * Makefile.am: added generics-sharing.2.cs svn path=/trunk/mono/; revision=86066 --- mono/metadata/ChangeLog | 13 ++ mono/metadata/class-internals.h | 4 + mono/metadata/domain-internals.h | 12 +- mono/metadata/domain.c | 26 ++++ mono/mini/ChangeLog | 17 +++ mono/mini/Makefile.am | 3 +- mono/mini/generic-sharing.c | 209 +++++++++++++++++++++++++++++++ mono/mini/mini.c | 177 +++++++++++++++++++++++--- mono/mini/mini.h | 22 ++++ mono/mini/optflags-def.h | 1 + mono/tests/ChangeLog | 6 + mono/tests/Makefile.am | 1 + mono/tests/generics-sharing.2.cs | 124 ++++++++++++++++++ 13 files changed, 595 insertions(+), 20 deletions(-) create mode 100644 mono/mini/generic-sharing.c create mode 100644 mono/tests/generics-sharing.2.cs diff --git a/mono/metadata/ChangeLog b/mono/metadata/ChangeLog index 47532c6a57b..df9c141f624 100644 --- a/mono/metadata/ChangeLog +++ b/mono/metadata/ChangeLog @@ -1,3 +1,16 @@ +2007-09-20 Mark Probst + + * 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 diff --git a/mono/metadata/class-internals.h b/mono/metadata/class-internals.h index 48294de81b9..664043e9a72 100644 --- a/mono/metadata/class-internals.h +++ b/mono/metadata/class-internals.h @@ -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; diff --git a/mono/metadata/domain-internals.h b/mono/metadata/domain-internals.h index f5e0fbfeb44..52c5cab552c 100644 --- a/mono/metadata/domain-internals.h +++ b/mono/metadata/domain-internals.h @@ -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. diff --git a/mono/metadata/domain.c b/mono/metadata/domain.c index 082ed848a49..94dffe642ae 100644 --- a/mono/metadata/domain.c +++ b/mono/metadata/domain.c @@ -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); diff --git a/mono/mini/ChangeLog b/mono/mini/ChangeLog index 80cce033157..1e141715dba 100644 --- a/mono/mini/ChangeLog +++ b/mono/mini/ChangeLog @@ -1,3 +1,20 @@ +2007-09-20 Mark Probst + + * 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 * mini.c (mono_thread_abort): fixed typo in r86014. It should be '==' instead of '!='. diff --git a/mono/mini/Makefile.am b/mono/mini/Makefile.am index 0ace7b13e1d..231b4f94389 100644 --- a/mono/mini/Makefile.am +++ b/mono/mini/Makefile.am @@ -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 index 00000000000..233fb060770 --- /dev/null +++ b/mono/mini/generic-sharing.c @@ -0,0 +1,209 @@ +/* + * generic-sharing.c: Support functions for generic sharing. + * + * Author: + * Mark Probst (mark.probst@gmail.com) + * + * (C) 2007 Novell, Inc. + */ + +#include + +#include + +#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; +} diff --git a/mono/mini/mini.c b/mono/mini/mini.c index a5b9b30d098..75fb952bd72 100644 --- a/mono/mini/mini.c +++ b/mono/mini/mini.c @@ -108,6 +108,13 @@ 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); diff --git a/mono/mini/mini.h b/mono/mini/mini.h index 56f19f9b1ed..6e0ab2f1f12 100644 --- a/mono/mini/mini.h +++ b/mono/mini/mini.h @@ -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__ */ diff --git a/mono/mini/optflags-def.h b/mono/mini/optflags-def.h index 126e9c04c14..c33d602a443 100644 --- a/mono/mini/optflags-def.h +++ b/mono/mini/optflags-def.h @@ -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") diff --git a/mono/tests/ChangeLog b/mono/tests/ChangeLog index 56b68faca86..d222a8b8682 100644 --- a/mono/tests/ChangeLog +++ b/mono/tests/ChangeLog @@ -1,3 +1,9 @@ +2007-09-20 Mark Probst + + * generics-sharing.2.cs: added + + * Makefile.am: added generics-sharing.2.cs + 2007-09-19 Marek Habersack * assemblyresolve/Makefile.am (prereq): build the assemblies in diff --git a/mono/tests/Makefile.am b/mono/tests/Makefile.am index 7dd70a1fe0f..313c887f11d 100644 --- a/mono/tests/Makefile.am +++ b/mono/tests/Makefile.am @@ -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 index 00000000000..3558bc9c14f --- /dev/null +++ b/mono/tests/generics-sharing.2.cs @@ -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 { + static T[] arr; + + public static GenA () { + arr = new T [3]; + } + + public GenA () {} + + public GenA newGen () { + return new GenA (); + } + + public GenA newGenInt () { + return new GenA (); + } + + public int getGenField () { + return GenB.field; + } + + public int getNonGenField () { + return NonGen.field; + } + + public T[] getArr () { + return arr; + } + + public T[] newArr () { + return new T [3]; + } + + public GenB>[] newArrNested () { + /* + GenB>[] arr = null; + for (int i = 0; i < 10000000; ++i) + arr = new GenB> [3]; + */ + return new GenB> [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 { + 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 obj) { + EqualityComparer comp = EqualityComparer.Default; + + GenA ga = new GenA (); + + typeCheck ("newGen", ga.newGen (), typeof (GenA)); + typeCheck ("newGenInt", ga.newGenInt (), typeof (GenA)); + typeCheck ("getArr", ga.getArr (), typeof (T[])); + typeCheck ("newArr", ga.newArr (), typeof (T[])); + //ga.newArrNested (); + typeCheck ("newArrNested", ga.newArrNested (), typeof (GenB>[])); + + 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 (new ClassA ()); + work (new ClassB ()); + work (new ClassC ()); + work> (new GenA ()); + work (new int[3]); + work (123); + + if (haveError) + return 1; + return 0; + } +} -- 2.25.1