2 * generic-sharing.c: Support functions for generic sharing.
5 * Mark Probst (mark.probst@gmail.com)
7 * Copyright 2007-2011 Novell, Inc (http://www.novell.com)
8 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
13 #include <mono/metadata/class.h>
14 #include <mono/utils/mono-counters.h>
18 //#define ALLOW_PARTIAL_SHARING TRUE
19 #define ALLOW_PARTIAL_SHARING FALSE
22 #define DEBUG(...) __VA_ARGS__
28 mono_class_unregister_image_generic_subclasses (MonoImage *image, gpointer user_data);
31 mini_get_gsharedvt_alloc_type_gsctx (MonoGenericSharingContext *gsctx, MonoType *t);
34 type_check_context_used (MonoType *type, gboolean recursive)
36 switch (mono_type_get_type (type)) {
38 return MONO_GENERIC_CONTEXT_USED_CLASS;
40 return MONO_GENERIC_CONTEXT_USED_METHOD;
41 case MONO_TYPE_SZARRAY:
42 return mono_class_check_context_used (mono_type_get_class (type));
44 return mono_class_check_context_used (mono_type_get_array_type (type)->eklass);
47 return mono_class_check_context_used (mono_type_get_class (type));
50 case MONO_TYPE_GENERICINST:
52 MonoGenericClass *gclass = type->data.generic_class;
54 g_assert (gclass->container_class->generic_container);
55 return mono_generic_context_check_used (&gclass->context);
65 inst_check_context_used (MonoGenericInst *inst)
73 for (i = 0; i < inst->type_argc; ++i)
74 context_used |= type_check_context_used (inst->type_argv [i], TRUE);
80 * mono_generic_context_check_used:
81 * @context: a generic context
83 * Checks whether the context uses a type variable. Returns an int
84 * with the bit MONO_GENERIC_CONTEXT_USED_CLASS set to reflect whether
85 * the context's class instantiation uses type variables.
88 mono_generic_context_check_used (MonoGenericContext *context)
92 context_used |= inst_check_context_used (context->class_inst);
93 context_used |= inst_check_context_used (context->method_inst);
99 * mono_class_check_context_used:
102 * Checks whether the class's generic context uses a type variable.
103 * Returns an int with the bit MONO_GENERIC_CONTEXT_USED_CLASS set to
104 * reflect whether the context's class instantiation uses type
108 mono_class_check_context_used (MonoClass *class)
110 int context_used = 0;
112 context_used |= type_check_context_used (&class->this_arg, FALSE);
113 context_used |= type_check_context_used (&class->byval_arg, FALSE);
115 if (class->generic_class)
116 context_used |= mono_generic_context_check_used (&class->generic_class->context);
117 else if (class->generic_container)
118 context_used |= mono_generic_context_check_used (&class->generic_container->context);
124 * LOCKING: loader lock
126 static MonoRuntimeGenericContextInfoTemplate*
127 get_info_templates (MonoRuntimeGenericContextTemplate *template, int type_argc)
129 g_assert (type_argc >= 0);
131 return template->infos;
132 return g_slist_nth_data (template->method_templates, type_argc - 1);
136 * LOCKING: loader lock
139 set_info_templates (MonoImage *image, MonoRuntimeGenericContextTemplate *template, int type_argc,
140 MonoRuntimeGenericContextInfoTemplate *oti)
142 g_assert (type_argc >= 0);
144 template->infos = oti;
146 int length = g_slist_length (template->method_templates);
149 /* FIXME: quadratic! */
150 while (length < type_argc) {
151 template->method_templates = g_slist_append_image (image, template->method_templates, NULL);
155 list = g_slist_nth (template->method_templates, type_argc - 1);
162 * LOCKING: loader lock
165 template_get_max_argc (MonoRuntimeGenericContextTemplate *template)
167 return g_slist_length (template->method_templates);
171 * LOCKING: loader lock
173 static MonoRuntimeGenericContextInfoTemplate*
174 rgctx_template_get_other_slot (MonoRuntimeGenericContextTemplate *template, int type_argc, int slot)
177 MonoRuntimeGenericContextInfoTemplate *oti;
179 g_assert (slot >= 0);
181 for (oti = get_info_templates (template, type_argc), i = 0; i < slot; oti = oti->next, ++i) {
190 * LOCKING: loader lock
193 rgctx_template_num_infos (MonoRuntimeGenericContextTemplate *template, int type_argc)
195 MonoRuntimeGenericContextInfoTemplate *oti;
198 for (i = 0, oti = get_info_templates (template, type_argc); oti; ++i, oti = oti->next)
204 /* Maps from uninstantiated generic classes to GList's of
205 * uninstantiated generic classes whose parent is the key class or an
206 * instance of the key class.
208 * LOCKING: loader lock
210 static GHashTable *generic_subclass_hash;
213 * LOCKING: templates lock
216 class_set_rgctx_template (MonoClass *class, MonoRuntimeGenericContextTemplate *rgctx_template)
218 if (!class->image->rgctx_template_hash)
219 class->image->rgctx_template_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
221 g_hash_table_insert (class->image->rgctx_template_hash, class, rgctx_template);
225 * LOCKING: loader lock
227 static MonoRuntimeGenericContextTemplate*
228 class_lookup_rgctx_template (MonoClass *class)
230 MonoRuntimeGenericContextTemplate *template;
232 if (!class->image->rgctx_template_hash)
235 template = g_hash_table_lookup (class->image->rgctx_template_hash, class);
241 * LOCKING: loader lock
244 register_generic_subclass (MonoClass *class)
246 MonoClass *parent = class->parent;
248 MonoRuntimeGenericContextTemplate *rgctx_template = class_lookup_rgctx_template (class);
250 g_assert (rgctx_template);
252 if (parent->generic_class)
253 parent = parent->generic_class->container_class;
255 if (!generic_subclass_hash)
256 generic_subclass_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
258 subclass = g_hash_table_lookup (generic_subclass_hash, parent);
259 rgctx_template->next_subclass = subclass;
260 g_hash_table_insert (generic_subclass_hash, parent, class);
264 move_subclasses_not_in_image_foreach_func (MonoClass *class, MonoClass *subclass, MonoImage *image)
268 if (class->image == image) {
269 /* The parent class itself is in the image, so all the
270 subclasses must be in the image, too. If not,
271 we're removing an image containing a class which
272 still has a subclass in another image. */
275 g_assert (subclass->image == image);
276 subclass = class_lookup_rgctx_template (subclass)->next_subclass;
284 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
285 MonoClass *next = subclass_template->next_subclass;
287 if (subclass->image != image) {
288 subclass_template->next_subclass = new_list;
296 g_hash_table_insert (generic_subclass_hash, class, new_list);
300 * mono_class_unregister_image_generic_subclasses:
303 * Removes all classes of the image from the generic subclass hash.
304 * Must be called when an image is unloaded.
307 mono_class_unregister_image_generic_subclasses (MonoImage *image, gpointer user_data)
309 GHashTable *old_hash;
311 //g_print ("unregistering image %s\n", image->name);
313 if (!generic_subclass_hash)
318 old_hash = generic_subclass_hash;
319 generic_subclass_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
321 g_hash_table_foreach (old_hash, (GHFunc)move_subclasses_not_in_image_foreach_func, image);
323 mono_loader_unlock ();
325 g_hash_table_destroy (old_hash);
328 static MonoRuntimeGenericContextTemplate*
329 alloc_template (MonoClass *class)
331 static gboolean inited = FALSE;
332 static int num_allocted = 0;
333 static int num_bytes = 0;
335 int size = sizeof (MonoRuntimeGenericContextTemplate);
338 mono_counters_register ("RGCTX template num allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_allocted);
339 mono_counters_register ("RGCTX template bytes allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_bytes);
346 return mono_image_alloc0 (class->image, size);
349 static MonoRuntimeGenericContextInfoTemplate*
350 alloc_oti (MonoImage *image)
352 static gboolean inited = FALSE;
353 static int num_allocted = 0;
354 static int num_bytes = 0;
356 int size = sizeof (MonoRuntimeGenericContextInfoTemplate);
359 mono_counters_register ("RGCTX oti num allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_allocted);
360 mono_counters_register ("RGCTX oti bytes allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_bytes);
367 return mono_image_alloc0 (image, size);
370 #define MONO_RGCTX_SLOT_USED_MARKER ((gpointer)&mono_defaults.object_class->byval_arg)
373 * Return true if this info type has the notion of identify.
375 * Some info types expect that each insert results in a new slot been assigned.
378 info_has_identity (MonoRgctxInfoType info_type)
380 return info_type != MONO_RGCTX_INFO_CAST_CACHE;
384 * LOCKING: loader lock
387 rgctx_template_set_slot (MonoImage *image, MonoRuntimeGenericContextTemplate *template, int type_argc,
388 int slot, gpointer data, MonoRgctxInfoType info_type)
390 static gboolean inited = FALSE;
391 static int num_markers = 0;
392 static int num_data = 0;
395 MonoRuntimeGenericContextInfoTemplate *list = get_info_templates (template, type_argc);
396 MonoRuntimeGenericContextInfoTemplate **oti = &list;
399 mono_counters_register ("RGCTX oti num markers", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_markers);
400 mono_counters_register ("RGCTX oti num data", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_data);
404 g_assert (slot >= 0);
412 *oti = alloc_oti (image);
416 g_assert (!(*oti)->data);
418 (*oti)->info_type = info_type;
420 set_info_templates (image, template, type_argc, list);
422 if (data == MONO_RGCTX_SLOT_USED_MARKER)
429 * mono_method_get_declaring_generic_method:
430 * @method: an inflated method
432 * Returns an inflated method's declaring method.
435 mono_method_get_declaring_generic_method (MonoMethod *method)
437 MonoMethodInflated *inflated;
439 g_assert (method->is_inflated);
441 inflated = (MonoMethodInflated*)method;
443 return inflated->declaring;
447 * mono_class_get_method_generic:
451 * Given a class and a generic method, which has to be of an
452 * instantiation of the same class that klass is an instantiation of,
453 * returns the corresponding method in klass. Example:
455 * klass is Gen<string>
456 * method is Gen<object>.work<int>
458 * returns: Gen<string>.work<int>
461 mono_class_get_method_generic (MonoClass *klass, MonoMethod *method)
463 MonoMethod *declaring, *m;
466 if (method->is_inflated)
467 declaring = mono_method_get_declaring_generic_method (method);
472 if (klass->generic_class)
473 m = mono_class_get_inflated_method (klass, declaring);
476 mono_class_setup_methods (klass);
477 if (klass->exception_type)
479 for (i = 0; i < klass->method.count; ++i) {
480 m = klass->methods [i];
483 if (m->is_inflated && mono_method_get_declaring_generic_method (m) == declaring)
486 if (i >= klass->method.count)
490 if (method != declaring) {
491 MonoGenericContext context;
493 context.class_inst = NULL;
494 context.method_inst = mono_method_get_context (method)->method_inst;
496 m = mono_class_inflate_generic_method (m, &context);
503 inflate_info (MonoRuntimeGenericContextInfoTemplate *oti, MonoGenericContext *context, MonoClass *class, gboolean temporary)
505 gpointer data = oti->data;
506 MonoRgctxInfoType info_type = oti->info_type;
511 if (data == MONO_RGCTX_SLOT_USED_MARKER)
512 return MONO_RGCTX_SLOT_USED_MARKER;
516 case MONO_RGCTX_INFO_STATIC_DATA:
517 case MONO_RGCTX_INFO_KLASS:
518 case MONO_RGCTX_INFO_VTABLE:
519 case MONO_RGCTX_INFO_TYPE:
520 case MONO_RGCTX_INFO_REFLECTION_TYPE:
521 case MONO_RGCTX_INFO_CAST_CACHE:
522 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
523 case MONO_RGCTX_INFO_VALUE_SIZE:
524 case MONO_RGCTX_INFO_CLASS_IS_REF: {
525 gpointer result = mono_class_inflate_generic_type_with_mempool (temporary ? NULL : class->image,
526 data, context, &error);
527 g_assert (mono_error_ok (&error)); /*FIXME proper error handling*/
531 case MONO_RGCTX_INFO_METHOD:
532 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
533 case MONO_RGCTX_INFO_METHOD_RGCTX:
534 case MONO_RGCTX_INFO_METHOD_CONTEXT:
535 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
536 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE: {
537 MonoMethod *method = data;
538 MonoMethod *inflated_method;
539 MonoType *inflated_type = mono_class_inflate_generic_type (&method->klass->byval_arg, context);
540 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
542 mono_metadata_free_type (inflated_type);
544 mono_class_init (inflated_class);
546 g_assert (!method->wrapper_type);
548 if (inflated_class->byval_arg.type == MONO_TYPE_ARRAY ||
549 inflated_class->byval_arg.type == MONO_TYPE_SZARRAY) {
550 inflated_method = mono_method_search_in_array_class (inflated_class,
551 method->name, method->signature);
553 inflated_method = mono_class_inflate_generic_method (method, context);
555 mono_class_init (inflated_method->klass);
556 g_assert (inflated_method->klass == inflated_class);
557 return inflated_method;
559 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE:
560 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT: {
561 MonoJumpInfoGSharedVtCall *info = data;
562 MonoMethod *method = info->method;
563 MonoMethod *inflated_method;
564 MonoType *inflated_type = mono_class_inflate_generic_type (&method->klass->byval_arg, context);
565 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
566 MonoJumpInfoGSharedVtCall *res;
569 res = g_new0 (MonoJumpInfoGSharedVtCall, 1);
570 /* Keep the original signature */
571 res->sig = info->sig;
573 mono_metadata_free_type (inflated_type);
575 mono_class_init (inflated_class);
577 g_assert (!method->wrapper_type);
579 if (inflated_class->byval_arg.type == MONO_TYPE_ARRAY ||
580 inflated_class->byval_arg.type == MONO_TYPE_SZARRAY) {
581 inflated_method = mono_method_search_in_array_class (inflated_class,
582 method->name, method->signature);
584 inflated_method = mono_class_inflate_generic_method (method, context);
586 mono_class_init (inflated_method->klass);
587 g_assert (inflated_method->klass == inflated_class);
588 res->method = inflated_method;
593 case MONO_RGCTX_INFO_CLASS_FIELD:
594 case MONO_RGCTX_INFO_FIELD_OFFSET: {
595 MonoClassField *field = data;
596 MonoType *inflated_type = mono_class_inflate_generic_type (&field->parent->byval_arg, context);
597 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
598 int i = field - field->parent->fields;
599 gpointer dummy = NULL;
601 mono_metadata_free_type (inflated_type);
603 mono_class_get_fields (inflated_class, &dummy);
604 g_assert (inflated_class->fields);
606 return &inflated_class->fields [i];
608 case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI: {
609 MonoMethodSignature *sig = data;
610 MonoMethodSignature *isig;
613 isig = mono_inflate_generic_signature (sig, context, &error);
614 g_assert (mono_error_ok (&error));
619 g_assert_not_reached ();
621 /* Not reached, quiet compiler */
626 free_inflated_info (MonoRgctxInfoType info_type, gpointer info)
632 case MONO_RGCTX_INFO_STATIC_DATA:
633 case MONO_RGCTX_INFO_KLASS:
634 case MONO_RGCTX_INFO_VTABLE:
635 case MONO_RGCTX_INFO_TYPE:
636 case MONO_RGCTX_INFO_REFLECTION_TYPE:
637 case MONO_RGCTX_INFO_CAST_CACHE:
638 mono_metadata_free_type (info);
645 static MonoRuntimeGenericContextInfoTemplate
646 class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot, gboolean temporary, gboolean shared, gboolean *do_free);
649 class_uninstantiated (MonoClass *class)
651 if (class->generic_class)
652 return class->generic_class->container_class;
657 generic_inst_is_sharable (MonoGenericInst *inst, gboolean allow_type_vars,
658 gboolean allow_partial)
662 for (i = 0; i < inst->type_argc; ++i) {
663 MonoType *type = inst->type_argv [i];
665 if (MONO_TYPE_IS_REFERENCE (type) || (allow_type_vars && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR)))
669 * Allow non ref arguments, if there is at least one ref argument
671 * FIXME: Allow more types
673 if (allow_partial && !type->byref && (((type->type >= MONO_TYPE_BOOLEAN) && (type->type <= MONO_TYPE_R8)) || (type->type == MONO_TYPE_I) || (type->type == MONO_TYPE_U)))
683 * mono_is_partially_sharable_inst:
685 * Return TRUE if INST has ref and non-ref type arguments.
688 mono_is_partially_sharable_inst (MonoGenericInst *inst)
691 gboolean has_refs = FALSE, has_non_refs = FALSE;
693 for (i = 0; i < inst->type_argc; ++i) {
694 if (MONO_TYPE_IS_REFERENCE (inst->type_argv [i]) || inst->type_argv [i]->type == MONO_TYPE_VAR || inst->type_argv [i]->type == MONO_TYPE_MVAR)
700 return has_refs && has_non_refs;
706 * Return the class used to store information when using generic sharing.
707 * For fully shared classes, it is the generic definition, for partially shared
708 * classes, it is an instance with all ref type arguments replaced by the type parameters
709 * of its generic definition.
712 get_shared_class (MonoClass *class)
715 * FIXME: This conflicts with normal instances. Also, some code in this file
716 * like class_get_rgctx_template_oti treats these as normal generic instances
717 * instead of generic classes.
719 //g_assert_not_reached ();
721 /* The gsharedvt changes break this */
722 if (ALLOW_PARTIAL_SHARING)
723 g_assert_not_reached ();
726 if (class->is_inflated) {
727 MonoGenericContext *context = &class->generic_class->context;
728 MonoGenericContext *container_context;
729 MonoGenericContext shared_context;
730 MonoGenericInst *inst;
731 MonoType **type_argv;
734 inst = context->class_inst;
735 if (mono_is_partially_sharable_inst (inst)) {
736 container_context = &class->generic_class->container_class->generic_container->context;
737 type_argv = g_new0 (MonoType*, inst->type_argc);
738 for (i = 0; i < inst->type_argc; ++i) {
739 if (MONO_TYPE_IS_REFERENCE (inst->type_argv [i]) || inst->type_argv [i]->type == MONO_TYPE_VAR || inst->type_argv [i]->type == MONO_TYPE_MVAR)
740 type_argv [i] = container_context->class_inst->type_argv [i];
742 type_argv [i] = inst->type_argv [i];
745 memset (&shared_context, 0, sizeof (MonoGenericContext));
746 shared_context.class_inst = mono_metadata_get_generic_inst (inst->type_argc, type_argv);
749 return mono_class_inflate_generic_class (class->generic_class->container_class, &shared_context);
750 } else if (!generic_inst_is_sharable (inst, TRUE, FALSE)) {
751 /* Happens for partially shared methods of nono-sharable generic class */
757 return class_uninstantiated (class);
761 * mono_class_get_runtime_generic_context_template:
764 * Looks up or constructs, if necessary, the runtime generic context template for class.
765 * The template is the same for all instantiations of a class.
767 static MonoRuntimeGenericContextTemplate*
768 mono_class_get_runtime_generic_context_template (MonoClass *class)
770 MonoRuntimeGenericContextTemplate *parent_template, *template;
773 class = get_shared_class (class);
776 template = class_lookup_rgctx_template (class);
777 mono_loader_unlock ();
782 //g_assert (get_shared_class (class) == class);
784 template = alloc_template (class);
790 int max_argc, type_argc;
792 parent_template = mono_class_get_runtime_generic_context_template (class->parent);
793 max_argc = template_get_max_argc (parent_template);
795 for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
796 num_entries = rgctx_template_num_infos (parent_template, type_argc);
798 /* FIXME: quadratic! */
799 for (i = 0; i < num_entries; ++i) {
800 MonoRuntimeGenericContextInfoTemplate oti;
802 oti = class_get_rgctx_template_oti (class->parent, type_argc, i, FALSE, FALSE, NULL);
803 if (oti.data && oti.data != MONO_RGCTX_SLOT_USED_MARKER) {
804 rgctx_template_set_slot (class->image, template, type_argc, i,
805 oti.data, oti.info_type);
811 if (class_lookup_rgctx_template (class)) {
812 /* some other thread already set the template */
813 template = class_lookup_rgctx_template (class);
815 class_set_rgctx_template (class, template);
818 register_generic_subclass (class);
821 mono_loader_unlock ();
827 * class_get_rgctx_template_oti:
829 * Return the info template of CLASS numbered TYPE_ARGC/SLOT.
830 * temporary signifies whether the inflated info (oti.data) will be
831 * used temporarily, in which case it might be heap-allocated, or
832 * permanently, in which case it will be mempool-allocated. If
833 * temporary is set then *do_free will return whether the returned
834 * data must be freed.
836 * LOCKING: loader lock
838 static MonoRuntimeGenericContextInfoTemplate
839 class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot, gboolean temporary, gboolean shared, gboolean *do_free)
841 g_assert ((temporary && do_free) || (!temporary && !do_free));
843 DEBUG (printf ("get slot: %s %d\n", mono_type_full_name (&class->byval_arg), slot));
845 if (class->generic_class && !shared) {
846 MonoRuntimeGenericContextInfoTemplate oti;
847 gboolean tmp_do_free;
849 oti = class_get_rgctx_template_oti (class->generic_class->container_class,
850 type_argc, slot, TRUE, FALSE, &tmp_do_free);
852 gpointer info = oti.data;
853 oti.data = inflate_info (&oti, &class->generic_class->context, class, temporary);
855 free_inflated_info (oti.info_type, info);
862 MonoRuntimeGenericContextTemplate *template;
863 MonoRuntimeGenericContextInfoTemplate *oti;
865 template = mono_class_get_runtime_generic_context_template (class);
866 oti = rgctx_template_get_other_slot (template, type_argc, slot);
877 class_type_info (MonoDomain *domain, MonoClass *class, MonoRgctxInfoType info_type)
880 case MONO_RGCTX_INFO_STATIC_DATA: {
881 MonoVTable *vtable = mono_class_vtable (domain, class);
883 mono_raise_exception (mono_class_get_exception_for_failure (class));
884 return mono_vtable_get_static_field_data (vtable);
886 case MONO_RGCTX_INFO_KLASS:
888 case MONO_RGCTX_INFO_VTABLE: {
889 MonoVTable *vtable = mono_class_vtable (domain, class);
891 mono_raise_exception (mono_class_get_exception_for_failure (class));
894 case MONO_RGCTX_INFO_CAST_CACHE: {
895 /*First slot is the cache itself, the second the vtable.*/
896 gpointer **cache_data = mono_domain_alloc0 (domain, sizeof (gpointer) * 2);
897 cache_data [1] = (gpointer)class;
900 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
901 return GUINT_TO_POINTER (mono_class_array_element_size (class));
902 case MONO_RGCTX_INFO_VALUE_SIZE:
903 if (MONO_TYPE_IS_REFERENCE (&class->byval_arg))
904 return GUINT_TO_POINTER (sizeof (gpointer));
906 return GUINT_TO_POINTER (mono_class_value_size (class, NULL));
907 case MONO_RGCTX_INFO_CLASS_IS_REF:
908 if (MONO_TYPE_IS_REFERENCE (&class->byval_arg))
909 return GUINT_TO_POINTER (1);
911 return GUINT_TO_POINTER (0);
913 g_assert_not_reached ();
920 ji_is_gsharedvt (MonoJitInfo *ji)
922 if (ji && ji->has_generic_jit_info && (mono_jit_info_get_generic_sharing_context (ji)->var_is_vt ||
923 mono_jit_info_get_generic_sharing_context (ji)->mvar_is_vt))
930 * Describes the information used to construct a gsharedvt arg trampoline.
937 MonoMethodSignature *sig, *gsig;
938 MonoGenericContext gsctx;
939 } GSharedVtTrampInfo;
942 tramp_info_hash (gconstpointer key)
944 GSharedVtTrampInfo *tramp = (gpointer)key;
946 return (gsize)tramp->addr;
950 tramp_info_equal (gconstpointer a, gconstpointer b)
952 GSharedVtTrampInfo *tramp1 = (gpointer)a;
953 GSharedVtTrampInfo *tramp2 = (gpointer)b;
955 /* The signatures should be internalized */
956 return tramp1->is_in == tramp2->is_in && tramp1->calli == tramp2->calli && tramp1->vcall_offset == tramp2->vcall_offset &&
957 tramp1->addr == tramp2->addr && tramp1->sig == tramp2->sig && tramp1->gsig == tramp2->gsig &&
958 tramp1->gsctx.class_inst == tramp2->gsctx.class_inst && tramp1->gsctx.method_inst == tramp2->gsctx.method_inst;
962 * mini_get_gsharedvt_wrapper:
964 * Return a gsharedvt in/out wrapper for calling ADDR.
967 mini_get_gsharedvt_wrapper (gboolean gsharedvt_in, gpointer addr, MonoMethodSignature *normal_sig, MonoMethodSignature *gsharedvt_sig, MonoGenericSharingContext *gsctx,
968 gint32 vcall_offset, gboolean calli)
970 static gboolean inited = FALSE;
971 static int num_trampolines;
973 MonoDomain *domain = mono_domain_get ();
974 MonoJitDomainInfo *domain_info;
975 GSharedVtTrampInfo *tramp_info;
976 GSharedVtTrampInfo tinfo;
979 mono_counters_register ("GSHAREDVT arg trampolines", MONO_COUNTER_JIT | MONO_COUNTER_INT, &num_trampolines);
983 tinfo.is_in = gsharedvt_in;
985 tinfo.vcall_offset = vcall_offset;
987 tinfo.sig = normal_sig;
988 tinfo.gsig = gsharedvt_sig;
989 memcpy (&tinfo.gsctx, gsctx, sizeof (MonoGenericSharingContext));
991 domain_info = domain_jit_info (domain);
994 * The arg trampolines might only have a finite number in full-aot, so use a cache.
996 mono_domain_lock (domain);
997 if (!domain_info->gsharedvt_arg_tramp_hash)
998 domain_info->gsharedvt_arg_tramp_hash = g_hash_table_new (tramp_info_hash, tramp_info_equal);
999 res = g_hash_table_lookup (domain_info->gsharedvt_arg_tramp_hash, &tinfo);
1000 mono_domain_unlock (domain);
1004 info = mono_arch_get_gsharedvt_call_info (addr, normal_sig, gsharedvt_sig, gsctx, gsharedvt_in, vcall_offset, calli);
1007 static gpointer tramp_addr;
1008 MonoMethod *wrapper;
1011 wrapper = mono_marshal_get_gsharedvt_in_wrapper ();
1012 addr = mono_compile_method (wrapper);
1013 mono_memory_barrier ();
1018 static gpointer tramp_addr;
1019 MonoMethod *wrapper;
1022 wrapper = mono_marshal_get_gsharedvt_out_wrapper ();
1023 addr = mono_compile_method (wrapper);
1024 mono_memory_barrier ();
1031 addr = mono_aot_get_gsharedvt_arg_trampoline (info, addr);
1033 addr = mono_arch_get_gsharedvt_arg_trampoline (mono_domain_get (), info, addr);
1038 tramp_info = mono_domain_alloc0 (domain, sizeof (GSharedVtTrampInfo));
1039 memcpy (tramp_info, &tinfo, sizeof (GSharedVtTrampInfo));
1041 mono_domain_lock (domain);
1042 /* Duplicates are not a problem */
1043 g_hash_table_insert (domain_info->gsharedvt_arg_tramp_hash, tramp_info, addr);
1044 mono_domain_unlock (domain);
1050 instantiate_info (MonoDomain *domain, MonoRuntimeGenericContextInfoTemplate *oti,
1051 MonoGenericContext *context, MonoClass *class, guint8 *caller)
1059 switch (oti->info_type) {
1060 case MONO_RGCTX_INFO_STATIC_DATA:
1061 case MONO_RGCTX_INFO_KLASS:
1062 case MONO_RGCTX_INFO_VTABLE:
1063 case MONO_RGCTX_INFO_CAST_CACHE:
1070 data = inflate_info (oti, context, class, temporary);
1072 switch (oti->info_type) {
1073 case MONO_RGCTX_INFO_STATIC_DATA:
1074 case MONO_RGCTX_INFO_KLASS:
1075 case MONO_RGCTX_INFO_VTABLE:
1076 case MONO_RGCTX_INFO_CAST_CACHE:
1077 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
1078 case MONO_RGCTX_INFO_VALUE_SIZE:
1079 case MONO_RGCTX_INFO_CLASS_IS_REF: {
1080 MonoClass *arg_class = mono_class_from_mono_type (data);
1082 free_inflated_info (oti->info_type, data);
1083 g_assert (arg_class);
1085 /* The class might be used as an argument to
1086 mono_value_copy(), which requires that its GC
1087 descriptor has been computed. */
1088 if (oti->info_type == MONO_RGCTX_INFO_KLASS)
1089 mono_class_compute_gc_descriptor (arg_class);
1091 return class_type_info (domain, arg_class, oti->info_type);
1093 case MONO_RGCTX_INFO_TYPE:
1095 case MONO_RGCTX_INFO_REFLECTION_TYPE:
1096 return mono_type_get_object (domain, data);
1097 case MONO_RGCTX_INFO_METHOD:
1099 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE: {
1102 addr = mono_compile_method (data);
1103 return mini_add_method_trampoline (NULL, data, addr, mono_method_needs_static_rgctx_invoke (data, FALSE));
1105 #ifndef DISABLE_REMOTING
1106 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
1107 return mono_compile_method (mono_marshal_get_remoting_invoke_with_check (data));
1109 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE:
1110 return mono_domain_alloc0 (domain, sizeof (gpointer));
1111 case MONO_RGCTX_INFO_CLASS_FIELD:
1113 case MONO_RGCTX_INFO_FIELD_OFFSET: {
1114 MonoClassField *field = data;
1116 if (field->parent->valuetype && !(field->type->attrs & FIELD_ATTRIBUTE_STATIC))
1117 return GUINT_TO_POINTER (field->offset - sizeof (MonoObject));
1119 return GUINT_TO_POINTER (field->offset);
1121 case MONO_RGCTX_INFO_METHOD_RGCTX: {
1122 MonoMethodInflated *method = data;
1125 g_assert (method->method.method.is_inflated);
1126 g_assert (method->context.method_inst);
1128 vtable = mono_class_vtable (domain, method->method.method.klass);
1130 mono_raise_exception (mono_class_get_exception_for_failure (method->method.method.klass));
1132 return mono_method_lookup_rgctx (vtable, method->context.method_inst);
1134 case MONO_RGCTX_INFO_METHOD_CONTEXT: {
1135 MonoMethodInflated *method = data;
1137 g_assert (method->method.method.is_inflated);
1138 g_assert (method->context.method_inst);
1140 return method->context.method_inst;
1142 case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI: {
1143 MonoMethodSignature *gsig = oti->data;
1144 MonoMethodSignature *sig = data;
1146 MonoJitInfo *caller_ji;
1147 MonoGenericJitInfo *gji;
1150 * This is an indirect call to the address passed by the caller in the rgctx reg.
1152 //printf ("CALLI\n");
1155 caller_ji = mini_jit_info_table_find (mono_domain_get (), mono_get_addr_from_ftnptr (caller), NULL);
1156 g_assert (caller_ji);
1157 gji = mono_jit_info_get_generic_jit_info (caller_ji);
1160 addr = mini_get_gsharedvt_wrapper (FALSE, addr, sig, gsig, gji->generic_sharing_context, -1, TRUE);
1164 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE:
1165 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT: {
1166 MonoJumpInfoGSharedVtCall *call_info = data;
1167 MonoMethodSignature *call_sig;
1170 MonoJitInfo *caller_ji, *callee_ji;
1171 gboolean virtual = oti->info_type == MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT;
1172 gint32 vcall_offset;
1173 MonoGenericJitInfo *gji, *callee_gji = NULL;
1174 gboolean callee_gsharedvt;
1176 /* This is the original generic signature used by the caller */
1177 call_sig = call_info->sig;
1178 /* This is the instantiated method which is called */
1179 method = call_info->method;
1181 g_assert (method->is_inflated);
1184 addr = mono_compile_method (method);
1189 /* Same as in mono_emit_method_call_full () */
1190 #ifndef MONO_ARCH_HAVE_IMT
1193 if ((method->klass->parent == mono_defaults.multicastdelegate_class) && (!strcmp (method->name, "Invoke"))) {
1194 /* See mono_emit_method_call_full () */
1195 /* The gsharedvt trampoline will recognize this constant */
1196 vcall_offset = MONO_GSHAREDVT_DEL_INVOKE_VT_OFFSET;
1197 } else if (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
1198 guint32 imt_slot = mono_method_get_imt_slot (method);
1199 vcall_offset = ((gint32)imt_slot - MONO_IMT_SIZE) * SIZEOF_VOID_P;
1201 vcall_offset = G_STRUCT_OFFSET (MonoVTable, vtable) +
1202 ((mono_method_get_vtable_index (method)) * (SIZEOF_VOID_P));
1209 caller_ji = mini_jit_info_table_find (mono_domain_get (), mono_get_addr_from_ftnptr (caller), NULL);
1210 g_assert (caller_ji);
1211 gji = mono_jit_info_get_generic_jit_info (caller_ji);
1214 // FIXME: This loads information in the AOT case
1215 callee_ji = mini_jit_info_table_find (mono_domain_get (), mono_get_addr_from_ftnptr (addr), NULL);
1216 callee_gsharedvt = ji_is_gsharedvt (callee_ji);
1217 if (callee_gsharedvt) {
1218 callee_gji = mono_jit_info_get_generic_jit_info (callee_ji);
1219 g_assert (callee_gji);
1223 * For gsharedvt calls made out of gsharedvt methods, the callee could end up being a gsharedvt method, or a normal
1224 * non-shared method. The latter call cannot be patched, so instead of using a normal call, we make an indirect
1225 * call through the rgctx, in effect patching the rgctx entry instead of the call site.
1226 * For virtual calls, the caller might be a normal or a gsharedvt method. Since there is only one vtable slot,
1227 * this difference needs to be handed on the caller side. This is currently implemented by adding a gsharedvt-in
1228 * trampoline to all gsharedvt methods and storing this trampoline into the vtable slot. Virtual calls made from
1229 * gsharedvt methods always go through a gsharedvt-out trampoline, so the calling sequence is:
1230 * caller -> out trampoline -> in trampoline -> callee
1231 * This is not very efficient, but it is easy to implement.
1233 if (virtual || !callee_gsharedvt) {
1234 MonoMethodSignature *sig, *gsig;
1236 g_assert (method->is_inflated);
1238 sig = mono_method_signature (method);
1241 addr = mini_get_gsharedvt_wrapper (FALSE, addr, sig, gsig, gji->generic_sharing_context, vcall_offset, FALSE);
1244 printf ("OUT-VCALL: %s\n", mono_method_full_name (method, TRUE));
1246 printf ("OUT: %s\n", mono_method_full_name (method, TRUE));
1248 // } else if (!mini_is_gsharedvt_variable_signature (mono_method_signature (caller_method)) && callee_gsharedvt) {
1249 } else if (callee_gsharedvt) {
1250 MonoMethodSignature *sig, *gsig;
1253 * This is a combination of the out and in cases, since both the caller and the callee are gsharedvt methods.
1254 * The caller and the callee can use different gsharedvt signatures, so we have to add both an out and an in
1257 * public void foo<T1> (T1 t1, T t, object o) {}
1259 * class AClass : Base<long> {
1260 * public void bar<T> (T t, long time, object o) {
1264 * Here, the caller uses !!0,long, while the callee uses !!0,!0
1265 * FIXME: Optimize this.
1268 if (call_sig == mono_method_signature (method)) {
1270 sig = mono_method_signature (method);
1271 gsig = mono_method_signature (callee_ji->method);
1273 addr = mini_get_gsharedvt_wrapper (TRUE, callee_ji->code_start, sig, gsig, callee_gji->generic_sharing_context, -1, FALSE);
1275 sig = mono_method_signature (method);
1278 addr = mini_get_gsharedvt_wrapper (FALSE, addr, sig, gsig, gji->generic_sharing_context, -1, FALSE);
1280 //printf ("OUT-IN-RGCTX: %s\n", mono_method_full_name (method, TRUE));
1287 g_assert_not_reached ();
1294 * LOCKING: loader lock
1297 fill_in_rgctx_template_slot (MonoClass *class, int type_argc, int index, gpointer data, MonoRgctxInfoType info_type)
1299 MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
1300 MonoClass *subclass;
1302 rgctx_template_set_slot (class->image, template, type_argc, index, data, info_type);
1304 /* Recurse for all subclasses */
1305 if (generic_subclass_hash)
1306 subclass = g_hash_table_lookup (generic_subclass_hash, class);
1311 MonoRuntimeGenericContextInfoTemplate subclass_oti;
1312 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
1314 g_assert (subclass_template);
1316 subclass_oti = class_get_rgctx_template_oti (subclass->parent, type_argc, index, FALSE, FALSE, NULL);
1317 g_assert (subclass_oti.data);
1319 fill_in_rgctx_template_slot (subclass, type_argc, index, subclass_oti.data, info_type);
1321 subclass = subclass_template->next_subclass;
1326 mono_rgctx_info_type_to_str (MonoRgctxInfoType type)
1329 case MONO_RGCTX_INFO_STATIC_DATA: return "STATIC_DATA";
1330 case MONO_RGCTX_INFO_KLASS: return "KLASS";
1331 case MONO_RGCTX_INFO_VTABLE: return "VTABLE";
1332 case MONO_RGCTX_INFO_TYPE: return "TYPE";
1333 case MONO_RGCTX_INFO_REFLECTION_TYPE: return "REFLECTION_TYPE";
1334 case MONO_RGCTX_INFO_METHOD: return "METHOD";
1335 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE: return "GENERIC_METHOD_CODE";
1336 case MONO_RGCTX_INFO_CLASS_FIELD: return "CLASS_FIELD";
1337 case MONO_RGCTX_INFO_METHOD_RGCTX: return "METHOD_RGCTX";
1338 case MONO_RGCTX_INFO_METHOD_CONTEXT: return "METHOD_CONTEXT";
1339 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK: return "REMOTING_INVOKE_WITH_CHECK";
1340 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE: return "METHOD_DELEGATE_CODE";
1341 case MONO_RGCTX_INFO_CAST_CACHE: return "CAST_CACHE";
1342 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE: return "ARRAY_ELEMENT_SIZE";
1343 case MONO_RGCTX_INFO_VALUE_SIZE: return "VALUE_SIZE";
1344 case MONO_RGCTX_INFO_CLASS_IS_REF: return "CLASS_IS_REF";
1345 case MONO_RGCTX_INFO_FIELD_OFFSET: return "FIELD_OFFSET";
1346 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE: return "METHOD_GSHAREDVT_OUT_TRAMPOLINE";
1347 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT: return "METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT";
1348 case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI: return "SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI";
1350 return "<UNKNOWN RGCTX INFO TYPE>";
1354 G_GNUC_UNUSED static char*
1355 rgctx_info_to_str (MonoRgctxInfoType info_type, gpointer data)
1357 switch (info_type) {
1358 case MONO_RGCTX_INFO_VTABLE:
1359 return mono_type_full_name ((MonoType*)data);
1361 return g_strdup_printf ("<%p>", data);
1366 * LOCKING: loader lock
1369 register_info (MonoClass *class, int type_argc, gpointer data, MonoRgctxInfoType info_type)
1372 MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
1374 MonoRuntimeGenericContextInfoTemplate *oti;
1376 for (i = 0, oti = get_info_templates (template, type_argc); oti; ++i, oti = oti->next) {
1381 DEBUG (printf ("set slot %s, infos [%d] = %s, %s\n", mono_type_get_full_name (class), i, mono_rgctx_info_type_to_str (info_type), rgctx_info_to_str (info_type, data)));
1383 /* Mark the slot as used in all parent classes (until we find
1384 a parent class which already has it marked used). */
1385 parent = class->parent;
1386 while (parent != NULL) {
1387 MonoRuntimeGenericContextTemplate *parent_template;
1388 MonoRuntimeGenericContextInfoTemplate *oti;
1390 if (parent->generic_class)
1391 parent = parent->generic_class->container_class;
1393 parent_template = mono_class_get_runtime_generic_context_template (parent);
1394 oti = rgctx_template_get_other_slot (parent_template, type_argc, i);
1396 if (oti && oti->data)
1399 rgctx_template_set_slot (parent->image, parent_template, type_argc, i,
1400 MONO_RGCTX_SLOT_USED_MARKER, 0);
1402 parent = parent->parent;
1405 /* Fill in the slot in this class and in all subclasses
1407 fill_in_rgctx_template_slot (class, type_argc, i, data, info_type);
1413 info_equal (gpointer data1, gpointer data2, MonoRgctxInfoType info_type)
1415 switch (info_type) {
1416 case MONO_RGCTX_INFO_STATIC_DATA:
1417 case MONO_RGCTX_INFO_KLASS:
1418 case MONO_RGCTX_INFO_VTABLE:
1419 case MONO_RGCTX_INFO_TYPE:
1420 case MONO_RGCTX_INFO_REFLECTION_TYPE:
1421 case MONO_RGCTX_INFO_CAST_CACHE:
1422 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
1423 case MONO_RGCTX_INFO_VALUE_SIZE:
1424 case MONO_RGCTX_INFO_CLASS_IS_REF:
1425 return mono_class_from_mono_type (data1) == mono_class_from_mono_type (data2);
1426 case MONO_RGCTX_INFO_METHOD:
1427 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
1428 case MONO_RGCTX_INFO_CLASS_FIELD:
1429 case MONO_RGCTX_INFO_FIELD_OFFSET:
1430 case MONO_RGCTX_INFO_METHOD_RGCTX:
1431 case MONO_RGCTX_INFO_METHOD_CONTEXT:
1432 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
1433 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE:
1434 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE:
1435 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT:
1436 case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI:
1437 return data1 == data2;
1439 g_assert_not_reached ();
1446 lookup_or_register_info (MonoClass *class, int type_argc, gpointer data, MonoRgctxInfoType info_type,
1447 MonoGenericContext *generic_context)
1449 static gboolean inited = FALSE;
1450 static int max_slot = 0;
1452 MonoRuntimeGenericContextTemplate *rgctx_template =
1453 mono_class_get_runtime_generic_context_template (class);
1454 MonoRuntimeGenericContextInfoTemplate *oti_list, *oti;
1457 class = get_shared_class (class);
1459 mono_loader_lock ();
1461 if (info_has_identity (info_type)) {
1462 oti_list = get_info_templates (rgctx_template, type_argc);
1464 for (oti = oti_list, i = 0; oti; oti = oti->next, ++i) {
1465 gpointer inflated_data;
1467 if (oti->info_type != info_type || !oti->data)
1470 inflated_data = inflate_info (oti, generic_context, class, TRUE);
1472 if (info_equal (data, inflated_data, info_type)) {
1473 free_inflated_info (info_type, inflated_data);
1474 mono_loader_unlock ();
1477 free_inflated_info (info_type, inflated_data);
1481 /* We haven't found the info */
1482 i = register_info (class, type_argc, data, info_type);
1484 mono_loader_unlock ();
1487 mono_counters_register ("RGCTX max slot number", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &max_slot);
1497 * mono_method_lookup_or_register_info:
1499 * @in_mrgctx: whether to put the data into the MRGCTX
1500 * @data: the info data
1501 * @info_type: the type of info to register about data
1502 * @generic_context: a generic context
1504 * Looks up and, if necessary, adds information about data/info_type in
1505 * method's or method's class runtime generic context. Returns the
1506 * encoded slot number.
1509 mono_method_lookup_or_register_info (MonoMethod *method, gboolean in_mrgctx, gpointer data,
1510 MonoRgctxInfoType info_type, MonoGenericContext *generic_context)
1512 MonoClass *class = method->klass;
1513 int type_argc, index;
1516 MonoGenericInst *method_inst = mono_method_get_context (method)->method_inst;
1518 g_assert (method->is_inflated && method_inst);
1519 type_argc = method_inst->type_argc;
1520 g_assert (type_argc > 0);
1525 index = lookup_or_register_info (class, type_argc, data, info_type, generic_context);
1527 //g_print ("rgctx item at index %d argc %d\n", index, type_argc);
1530 return MONO_RGCTX_SLOT_MAKE_MRGCTX (index);
1532 return MONO_RGCTX_SLOT_MAKE_RGCTX (index);
1536 * mono_class_rgctx_get_array_size:
1537 * @n: The number of the array
1538 * @mrgctx: Whether it's an MRGCTX as opposed to a RGCTX.
1540 * Returns the number of slots in the n'th array of a (M)RGCTX. That
1541 * number includes the slot for linking and - for MRGCTXs - the two
1542 * slots in the first array for additional information.
1545 mono_class_rgctx_get_array_size (int n, gboolean mrgctx)
1547 g_assert (n >= 0 && n < 30);
1556 * LOCKING: domain lock
1559 alloc_rgctx_array (MonoDomain *domain, int n, gboolean is_mrgctx)
1561 static gboolean inited = FALSE;
1562 static int rgctx_num_alloced = 0;
1563 static int rgctx_bytes_alloced = 0;
1564 static int mrgctx_num_alloced = 0;
1565 static int mrgctx_bytes_alloced = 0;
1567 int size = mono_class_rgctx_get_array_size (n, is_mrgctx) * sizeof (gpointer);
1568 gpointer array = mono_domain_alloc0 (domain, size);
1571 mono_counters_register ("RGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_num_alloced);
1572 mono_counters_register ("RGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_bytes_alloced);
1573 mono_counters_register ("MRGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_num_alloced);
1574 mono_counters_register ("MRGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_bytes_alloced);
1579 mrgctx_num_alloced++;
1580 mrgctx_bytes_alloced += size;
1582 rgctx_num_alloced++;
1583 rgctx_bytes_alloced += size;
1590 fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContext *rgctx, guint8 *caller, guint32 slot,
1591 MonoGenericInst *method_inst)
1594 int i, first_slot, size;
1595 MonoDomain *domain = class_vtable->domain;
1596 MonoClass *class = class_vtable->klass;
1597 MonoGenericContext *class_context = class->generic_class ? &class->generic_class->context : NULL;
1598 MonoRuntimeGenericContextInfoTemplate oti;
1599 MonoGenericContext context = { class_context ? class_context->class_inst : NULL, method_inst };
1605 mono_domain_lock (domain);
1607 /* First check whether that slot isn't already instantiated.
1608 This might happen because lookup doesn't lock. Allocate
1609 arrays on the way. */
1611 size = mono_class_rgctx_get_array_size (0, method_inst != NULL);
1613 size -= MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
1614 for (i = 0; ; ++i) {
1617 if (method_inst && i == 0)
1618 offset = MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
1622 if (slot < first_slot + size - 1) {
1623 rgctx_index = slot - first_slot + 1 + offset;
1624 info = rgctx [rgctx_index];
1626 mono_domain_unlock (domain);
1631 if (!rgctx [offset + 0])
1632 rgctx [offset + 0] = alloc_rgctx_array (domain, i + 1, method_inst != NULL);
1633 rgctx = rgctx [offset + 0];
1634 first_slot += size - 1;
1635 size = mono_class_rgctx_get_array_size (i + 1, method_inst != NULL);
1638 g_assert (!rgctx [rgctx_index]);
1640 mono_domain_unlock (domain);
1642 oti = class_get_rgctx_template_oti (get_shared_class (class),
1643 method_inst ? method_inst->type_argc : 0, slot, TRUE, TRUE, &do_free);
1644 /* This might take the loader lock */
1645 info = instantiate_info (domain, &oti, &context, class, caller);
1649 g_print ("filling mrgctx slot %d table %d index %d\n", slot, i, rgctx_index);
1652 /*FIXME We should use CAS here, no need to take a lock.*/
1653 mono_domain_lock (domain);
1655 /* Check whether the slot hasn't been instantiated in the
1657 if (rgctx [rgctx_index])
1658 info = rgctx [rgctx_index];
1660 rgctx [rgctx_index] = info;
1662 mono_domain_unlock (domain);
1665 free_inflated_info (oti.info_type, oti.data);
1671 * mono_class_fill_runtime_generic_context:
1672 * @class_vtable: a vtable
1673 * @caller: caller method address
1674 * @slot: a slot index to be instantiated
1676 * Instantiates a slot in the RGCTX, returning its value.
1679 mono_class_fill_runtime_generic_context (MonoVTable *class_vtable, guint8 *caller, guint32 slot)
1681 static gboolean inited = FALSE;
1682 static int num_alloced = 0;
1684 MonoDomain *domain = class_vtable->domain;
1685 MonoRuntimeGenericContext *rgctx;
1688 mono_domain_lock (domain);
1691 mono_counters_register ("RGCTX num alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_alloced);
1695 rgctx = class_vtable->runtime_generic_context;
1697 rgctx = alloc_rgctx_array (domain, 0, FALSE);
1698 class_vtable->runtime_generic_context = rgctx;
1702 mono_domain_unlock (domain);
1704 info = fill_runtime_generic_context (class_vtable, rgctx, caller, slot, 0);
1706 DEBUG (printf ("get rgctx slot: %s %d -> %p\n", mono_type_full_name (&class_vtable->klass->byval_arg), slot, info));
1712 * mono_method_fill_runtime_generic_context:
1713 * @mrgctx: an MRGCTX
1714 * @caller: caller method address
1715 * @slot: a slot index to be instantiated
1717 * Instantiates a slot in the MRGCTX.
1720 mono_method_fill_runtime_generic_context (MonoMethodRuntimeGenericContext *mrgctx, guint8* caller, guint32 slot)
1724 info = fill_runtime_generic_context (mrgctx->class_vtable, (MonoRuntimeGenericContext*)mrgctx, caller, slot,
1725 mrgctx->method_inst);
1731 mrgctx_hash_func (gconstpointer key)
1733 const MonoMethodRuntimeGenericContext *mrgctx = key;
1735 return mono_aligned_addr_hash (mrgctx->class_vtable) ^ mono_metadata_generic_inst_hash (mrgctx->method_inst);
1739 mrgctx_equal_func (gconstpointer a, gconstpointer b)
1741 const MonoMethodRuntimeGenericContext *mrgctx1 = a;
1742 const MonoMethodRuntimeGenericContext *mrgctx2 = b;
1744 return mrgctx1->class_vtable == mrgctx2->class_vtable &&
1745 mono_metadata_generic_inst_equal (mrgctx1->method_inst, mrgctx2->method_inst);
1749 * mono_method_lookup_rgctx:
1750 * @class_vtable: a vtable
1751 * @method_inst: the method inst of a generic method
1753 * Returns the MRGCTX for the generic method(s) with the given
1754 * method_inst of the given class_vtable.
1756 * LOCKING: Take the domain lock.
1758 MonoMethodRuntimeGenericContext*
1759 mono_method_lookup_rgctx (MonoVTable *class_vtable, MonoGenericInst *method_inst)
1761 MonoDomain *domain = class_vtable->domain;
1762 MonoMethodRuntimeGenericContext *mrgctx;
1763 MonoMethodRuntimeGenericContext key;
1765 g_assert (!class_vtable->klass->generic_container);
1766 g_assert (!method_inst->is_open);
1768 mono_domain_lock (domain);
1769 if (!domain->method_rgctx_hash)
1770 domain->method_rgctx_hash = g_hash_table_new (mrgctx_hash_func, mrgctx_equal_func);
1772 key.class_vtable = class_vtable;
1773 key.method_inst = method_inst;
1775 mrgctx = g_hash_table_lookup (domain->method_rgctx_hash, &key);
1780 mrgctx = (MonoMethodRuntimeGenericContext*)alloc_rgctx_array (domain, 0, TRUE);
1781 mrgctx->class_vtable = class_vtable;
1782 mrgctx->method_inst = method_inst;
1784 g_hash_table_insert (domain->method_rgctx_hash, mrgctx, mrgctx);
1787 g_print ("mrgctx alloced for %s <", mono_type_get_full_name (class_vtable->klass));
1788 for (i = 0; i < method_inst->type_argc; ++i)
1789 g_print ("%s, ", mono_type_full_name (method_inst->type_argv [i]));
1794 mono_domain_unlock (domain);
1802 * mono_generic_context_is_sharable_full:
1803 * @context: a generic context
1805 * Returns whether the generic context is sharable. A generic context
1806 * is sharable iff all of its type arguments are reference type, or some of them have a
1807 * reference type, and ALLOW_PARTIAL is TRUE.
1810 mono_generic_context_is_sharable_full (MonoGenericContext *context,
1811 gboolean allow_type_vars,
1812 gboolean allow_partial)
1814 g_assert (context->class_inst || context->method_inst);
1816 if (context->class_inst && !generic_inst_is_sharable (context->class_inst, allow_type_vars, allow_partial))
1819 if (context->method_inst && !generic_inst_is_sharable (context->method_inst, allow_type_vars, allow_partial))
1826 mono_generic_context_is_sharable (MonoGenericContext *context, gboolean allow_type_vars)
1828 return mono_generic_context_is_sharable_full (context, allow_type_vars, ALLOW_PARTIAL_SHARING);
1832 * mono_method_is_generic_impl:
1835 * Returns whether the method is either generic or part of a generic
1839 mono_method_is_generic_impl (MonoMethod *method)
1841 if (method->is_inflated)
1843 /* We don't treat wrappers as generic code, i.e., we never
1844 apply generic sharing to them. This is especially
1845 important for static rgctx invoke wrappers, which only work
1846 if not compiled with sharing. */
1847 if (method->wrapper_type != MONO_WRAPPER_NONE)
1849 if (method->klass->generic_container)
1855 has_constraints (MonoGenericContainer *container)
1861 g_assert (container->type_argc > 0);
1862 g_assert (container->type_params);
1864 for (i = 0; i < container->type_argc; ++i)
1865 if (container->type_params [i].constraints)
1872 * mono_method_is_generic_sharable_impl_full:
1874 * @allow_type_vars: whether to regard type variables as reference types
1875 * @allow_partial: whether to allow partial sharing
1876 * @allow_gsharedvt: whenever to allow sharing over valuetypes
1878 * Returns TRUE iff the method is inflated or part of an inflated
1879 * class, its context is sharable and it has no constraints on its
1880 * type parameters. Otherwise returns FALSE.
1883 mono_method_is_generic_sharable_impl_full (MonoMethod *method, gboolean allow_type_vars,
1884 gboolean allow_partial, gboolean allow_gsharedvt)
1886 if (!mono_method_is_generic_impl (method))
1889 if (allow_gsharedvt && mini_is_gsharedvt_sharable_method (method))
1892 if (method->is_inflated) {
1893 MonoMethodInflated *inflated = (MonoMethodInflated*)method;
1894 MonoGenericContext *context = &inflated->context;
1896 if (!mono_generic_context_is_sharable_full (context, allow_type_vars, allow_partial))
1899 g_assert (inflated->declaring);
1901 if (inflated->declaring->is_generic) {
1902 if (has_constraints (mono_method_get_generic_container (inflated->declaring)))
1907 if (method->klass->generic_class) {
1908 if (!mono_generic_context_is_sharable_full (&method->klass->generic_class->context, allow_type_vars, allow_partial))
1911 g_assert (method->klass->generic_class->container_class &&
1912 method->klass->generic_class->container_class->generic_container);
1914 if (has_constraints (method->klass->generic_class->container_class->generic_container))
1918 if (method->klass->generic_container && !allow_type_vars)
1925 mono_method_is_generic_sharable_impl (MonoMethod *method, gboolean allow_type_vars)
1927 return mono_method_is_generic_sharable_impl_full (method, allow_type_vars, ALLOW_PARTIAL_SHARING, TRUE);
1931 mono_method_needs_static_rgctx_invoke (MonoMethod *method, gboolean allow_type_vars)
1933 if (!mono_class_generic_sharing_enabled (method->klass))
1936 if (!mono_method_is_generic_sharable_impl (method, allow_type_vars))
1939 if (method->is_inflated && mono_method_get_context (method)->method_inst)
1942 return ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
1943 method->klass->valuetype) &&
1944 (method->klass->generic_class || method->klass->generic_container);
1947 static MonoGenericInst*
1948 get_object_generic_inst (int type_argc)
1950 MonoType **type_argv;
1953 type_argv = alloca (sizeof (MonoType*) * type_argc);
1955 for (i = 0; i < type_argc; ++i)
1956 type_argv [i] = &mono_defaults.object_class->byval_arg;
1958 return mono_metadata_get_generic_inst (type_argc, type_argv);
1962 * mono_method_construct_object_context:
1965 * Returns a generic context for method with all type variables for
1966 * class and method instantiated with Object.
1969 mono_method_construct_object_context (MonoMethod *method)
1971 MonoGenericContext object_context;
1973 g_assert (!method->klass->generic_class);
1974 if (method->klass->generic_container) {
1975 int type_argc = method->klass->generic_container->type_argc;
1977 object_context.class_inst = get_object_generic_inst (type_argc);
1979 object_context.class_inst = NULL;
1982 if (mono_method_get_context_general (method, TRUE)->method_inst) {
1983 int type_argc = mono_method_get_context_general (method, TRUE)->method_inst->type_argc;
1985 object_context.method_inst = get_object_generic_inst (type_argc);
1987 object_context.method_inst = NULL;
1990 g_assert (object_context.class_inst || object_context.method_inst);
1992 return object_context;
1995 static gboolean gshared_supported;
1996 static gboolean gsharedvt_supported;
1999 mono_set_generic_sharing_supported (gboolean supported)
2001 gshared_supported = supported;
2005 mono_set_generic_sharing_vt_supported (gboolean supported)
2007 gsharedvt_supported = supported;
2011 * mono_class_generic_sharing_enabled:
2014 * Returns whether generic sharing is enabled for class.
2016 * This is a stop-gap measure to slowly introduce generic sharing
2017 * until we have all the issues sorted out, at which time this
2018 * function will disappear and generic sharing will always be enabled.
2021 mono_class_generic_sharing_enabled (MonoClass *class)
2023 static int generic_sharing = MONO_GENERIC_SHARING_NONE;
2024 static gboolean inited = FALSE;
2029 if (gshared_supported)
2030 generic_sharing = MONO_GENERIC_SHARING_ALL;
2032 generic_sharing = MONO_GENERIC_SHARING_NONE;
2034 if ((option = g_getenv ("MONO_GENERIC_SHARING"))) {
2035 if (strcmp (option, "corlib") == 0)
2036 generic_sharing = MONO_GENERIC_SHARING_CORLIB;
2037 else if (strcmp (option, "collections") == 0)
2038 generic_sharing = MONO_GENERIC_SHARING_COLLECTIONS;
2039 else if (strcmp (option, "all") == 0)
2040 generic_sharing = MONO_GENERIC_SHARING_ALL;
2041 else if (strcmp (option, "none") == 0)
2042 generic_sharing = MONO_GENERIC_SHARING_NONE;
2044 g_warning ("Unknown generic sharing option `%s'.", option);
2047 if (!gshared_supported)
2048 generic_sharing = MONO_GENERIC_SHARING_NONE;
2053 switch (generic_sharing) {
2054 case MONO_GENERIC_SHARING_NONE:
2056 case MONO_GENERIC_SHARING_ALL:
2058 case MONO_GENERIC_SHARING_CORLIB :
2059 return class->image == mono_defaults.corlib;
2060 case MONO_GENERIC_SHARING_COLLECTIONS:
2061 if (class->image != mono_defaults.corlib)
2063 while (class->nested_in)
2064 class = class->nested_in;
2065 return g_str_has_prefix (class->name_space, "System.Collections.Generic");
2067 g_assert_not_reached ();
2073 * mono_get_generic_context_from_code:
2075 * Return the runtime generic context belonging to the method whose native code
2078 MonoGenericSharingContext*
2079 mono_get_generic_context_from_code (guint8 *code)
2081 MonoJitInfo *jit_info = mini_jit_info_table_find (mono_domain_get (), (char*)code, NULL);
2083 g_assert (jit_info);
2085 return mono_jit_info_get_generic_sharing_context (jit_info);
2089 mini_method_get_context (MonoMethod *method)
2091 return mono_method_get_context_general (method, TRUE);
2095 * mono_method_check_context_used:
2098 * Checks whether the method's generic context uses a type variable.
2099 * Returns an int with the bits MONO_GENERIC_CONTEXT_USED_CLASS and
2100 * MONO_GENERIC_CONTEXT_USED_METHOD set to reflect whether the
2101 * context's class or method instantiation uses type variables.
2104 mono_method_check_context_used (MonoMethod *method)
2106 MonoGenericContext *method_context = mini_method_get_context (method);
2107 int context_used = 0;
2109 if (!method_context) {
2110 /* It might be a method of an array of an open generic type */
2111 if (method->klass->rank)
2112 context_used = mono_class_check_context_used (method->klass);
2114 context_used = mono_generic_context_check_used (method_context);
2115 context_used |= mono_class_check_context_used (method->klass);
2118 return context_used;
2122 generic_inst_equal (MonoGenericInst *inst1, MonoGenericInst *inst2)
2133 if (inst1->type_argc != inst2->type_argc)
2136 for (i = 0; i < inst1->type_argc; ++i)
2137 if (!mono_metadata_type_equal (inst1->type_argv [i], inst2->type_argv [i]))
2144 * mono_generic_context_equal_deep:
2145 * @context1: a generic context
2146 * @context2: a generic context
2148 * Returns whether context1's type arguments are equal to context2's
2152 mono_generic_context_equal_deep (MonoGenericContext *context1, MonoGenericContext *context2)
2154 return generic_inst_equal (context1->class_inst, context2->class_inst) &&
2155 generic_inst_equal (context1->method_inst, context2->method_inst);
2159 * mini_class_get_container_class:
2160 * @class: a generic class
2162 * Returns the class's container class, which is the class itself if
2163 * it doesn't have generic_class set.
2166 mini_class_get_container_class (MonoClass *class)
2168 if (class->generic_class)
2169 return class->generic_class->container_class;
2171 g_assert (class->generic_container);
2176 * mini_class_get_context:
2177 * @class: a generic class
2179 * Returns the class's generic context.
2182 mini_class_get_context (MonoClass *class)
2184 if (class->generic_class)
2185 return &class->generic_class->context;
2187 g_assert (class->generic_container);
2188 return &class->generic_container->context;
2192 * mini_get_basic_type_from_generic:
2193 * @gsctx: a generic sharing context
2196 * Returns a closed type corresponding to the possibly open type
2200 mini_get_basic_type_from_generic (MonoGenericSharingContext *gsctx, MonoType *type)
2202 /* FIXME: Some callers don't pass in a gsctx, like mono_dyn_call_prepare () */
2204 if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR))
2207 if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) && mini_is_gsharedvt_type_gsctx (gsctx, type))
2208 return mini_get_gsharedvt_alloc_type_gsctx (gsctx, type);
2210 return mono_type_get_basic_type_from_generic (type);
2214 * mini_type_get_underlying_type:
2216 * Return the underlying type of TYPE, taking into account enums, byref and generic
2220 mini_type_get_underlying_type (MonoGenericSharingContext *gsctx, MonoType *type)
2223 return &mono_defaults.int_class->byval_arg;
2224 return mini_get_basic_type_from_generic (gsctx, mono_type_get_underlying_type (type));
2228 * mini_type_stack_size:
2229 * @gsctx: a generic sharing context
2231 * @align: Pointer to an int for returning the alignment
2233 * Returns the type's stack size and the alignment in *align. The
2234 * type is allowed to be open.
2237 mini_type_stack_size (MonoGenericSharingContext *gsctx, MonoType *t, int *align)
2239 gboolean allow_open = TRUE;
2241 // FIXME: Some callers might not pass in a gsctx
2242 //allow_open = gsctx != NULL;
2243 return mono_type_stack_size_internal (t, align, allow_open);
2247 * mini_type_stack_size_full:
2249 * Same as mini_type_stack_size, but handle gsharedvt and pinvoke data types as well.
2252 mini_type_stack_size_full (MonoGenericSharingContext *gsctx, MonoType *t, guint32 *align, gboolean pinvoke)
2257 if (t->type == MONO_TYPE_VAR || t->type == MONO_TYPE_MVAR)
2261 if (mini_is_gsharedvt_type_gsctx (gsctx, t))
2262 t = mini_get_gsharedvt_alloc_type_gsctx (gsctx, t);
2265 size = mono_type_native_stack_size (t, align);
2270 size = mini_type_stack_size (gsctx, t, &ialign);
2273 size = mini_type_stack_size (gsctx, t, NULL);
2281 * mono_generic_sharing_init:
2283 * Register the generic sharing counters.
2286 mono_generic_sharing_init (void)
2288 mono_install_image_unload_hook (mono_class_unregister_image_generic_subclasses, NULL);
2292 mono_generic_sharing_cleanup (void)
2294 mono_remove_image_unload_hook (mono_class_unregister_image_generic_subclasses, NULL);
2296 if (generic_subclass_hash)
2297 g_hash_table_destroy (generic_subclass_hash);
2301 * mini_type_var_is_vt:
2303 * Return whenever T is a type variable instantiated with a vtype.
2306 mini_type_var_is_vt (MonoCompile *cfg, MonoType *type)
2308 if (type->type == MONO_TYPE_VAR) {
2309 if (cfg->generic_sharing_context->var_is_vt && cfg->generic_sharing_context->var_is_vt [type->data.generic_param->num])
2313 } else if (type->type == MONO_TYPE_MVAR) {
2314 if (cfg->generic_sharing_context->mvar_is_vt && cfg->generic_sharing_context->mvar_is_vt [type->data.generic_param->num])
2319 g_assert_not_reached ();
2325 mini_type_is_reference (MonoCompile *cfg, MonoType *type)
2327 if (mono_type_is_reference (type))
2329 if (!cfg->generic_sharing_context)
2331 /*FIXME the probably needs better handle under partial sharing*/
2332 return ((type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) && !mini_type_var_is_vt (cfg, type));
2336 * mini_method_get_rgctx:
2338 * Return the RGCTX which needs to be passed to M when it is called.
2341 mini_method_get_rgctx (MonoMethod *m)
2343 if (mini_method_get_context (m)->method_inst)
2344 return mono_method_lookup_rgctx (mono_class_vtable (mono_domain_get (), m->klass), mini_method_get_context (m)->method_inst);
2346 return mono_class_vtable (mono_domain_get (), m->klass);
2350 * mini_type_is_vtype:
2352 * Return whenever T is a vtype, or a type param instantiated with a vtype.
2353 * Should be used in place of MONO_TYPE_ISSTRUCT () which can't handle gsharedvt.
2356 mini_type_is_vtype (MonoCompile *cfg, MonoType *t)
2358 return MONO_TYPE_ISSTRUCT (t) || mini_is_gsharedvt_variable_type (cfg, t);
2361 #if defined(MONOTOUCH) || defined(MONO_EXTENSIONS)
2363 #include "../../../mono-extensions/mono/mini/mini-generic-sharing-gsharedvt.c"
2368 mini_is_gsharedvt_type_gsctx (MonoGenericSharingContext *gsctx, MonoType *t)
2374 mini_is_gsharedvt_type (MonoCompile *cfg, MonoType *t)
2380 mini_is_gsharedvt_klass (MonoCompile *cfg, MonoClass *klass)
2386 mini_is_gsharedvt_signature (MonoCompile *cfg, MonoMethodSignature *sig)
2392 mini_is_gsharedvt_variable_type (MonoCompile *cfg, MonoType *t)
2398 mini_get_gsharedvt_alloc_type_gsctx (MonoGenericSharingContext *gsctx, MonoType *t)
2404 mini_get_gsharedvt_alloc_type_for_type (MonoCompile *cfg, MonoType *t)
2410 mini_is_gsharedvt_sharable_method (MonoMethod *method)
2416 mini_is_gsharedvt_variable_signature (MonoMethodSignature *sig)
2421 #endif /* !MONOTOUCH */