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 static int num_templates_allocted;
32 static int num_templates_bytes;
33 static int num_oti_allocted;
34 static int num_oti_bytes;
36 static gboolean partial_supported = TRUE;
38 static inline gboolean
39 partial_sharing_supported (void)
41 if (!ALLOW_PARTIAL_SHARING)
43 /* Enable this when AOT compiling or running in full-aot mode */
46 if (partial_supported)
52 type_check_context_used (MonoType *type, gboolean recursive)
54 switch (mono_type_get_type (type)) {
56 return MONO_GENERIC_CONTEXT_USED_CLASS;
58 return MONO_GENERIC_CONTEXT_USED_METHOD;
59 case MONO_TYPE_SZARRAY:
60 return mono_class_check_context_used (mono_type_get_class (type));
62 return mono_class_check_context_used (mono_type_get_array_type (type)->eklass);
65 return mono_class_check_context_used (mono_type_get_class (type));
68 case MONO_TYPE_GENERICINST:
70 MonoGenericClass *gclass = type->data.generic_class;
72 g_assert (gclass->container_class->generic_container);
73 return mono_generic_context_check_used (&gclass->context);
83 inst_check_context_used (MonoGenericInst *inst)
91 for (i = 0; i < inst->type_argc; ++i)
92 context_used |= type_check_context_used (inst->type_argv [i], TRUE);
98 * mono_generic_context_check_used:
99 * @context: a generic context
101 * Checks whether the context uses a type variable. Returns an int
102 * with the bit MONO_GENERIC_CONTEXT_USED_CLASS set to reflect whether
103 * the context's class instantiation uses type variables.
106 mono_generic_context_check_used (MonoGenericContext *context)
108 int context_used = 0;
110 context_used |= inst_check_context_used (context->class_inst);
111 context_used |= inst_check_context_used (context->method_inst);
117 * mono_class_check_context_used:
120 * Checks whether the class's generic context uses a type variable.
121 * Returns an int with the bit MONO_GENERIC_CONTEXT_USED_CLASS set to
122 * reflect whether the context's class instantiation uses type
126 mono_class_check_context_used (MonoClass *klass)
128 int context_used = 0;
130 context_used |= type_check_context_used (&klass->this_arg, FALSE);
131 context_used |= type_check_context_used (&klass->byval_arg, FALSE);
133 if (klass->generic_class)
134 context_used |= mono_generic_context_check_used (&klass->generic_class->context);
135 else if (klass->generic_container)
136 context_used |= mono_generic_context_check_used (&klass->generic_container->context);
142 * LOCKING: loader lock
144 static MonoRuntimeGenericContextInfoTemplate*
145 get_info_templates (MonoRuntimeGenericContextTemplate *template, int type_argc)
147 g_assert (type_argc >= 0);
149 return template->infos;
150 return g_slist_nth_data (template->method_templates, type_argc - 1);
154 * LOCKING: loader lock
157 set_info_templates (MonoImage *image, MonoRuntimeGenericContextTemplate *template, int type_argc,
158 MonoRuntimeGenericContextInfoTemplate *oti)
160 g_assert (type_argc >= 0);
162 template->infos = oti;
164 int length = g_slist_length (template->method_templates);
167 /* FIXME: quadratic! */
168 while (length < type_argc) {
169 template->method_templates = g_slist_append_image (image, template->method_templates, NULL);
173 list = g_slist_nth (template->method_templates, type_argc - 1);
180 * LOCKING: loader lock
183 template_get_max_argc (MonoRuntimeGenericContextTemplate *template)
185 return g_slist_length (template->method_templates);
189 * LOCKING: loader lock
191 static MonoRuntimeGenericContextInfoTemplate*
192 rgctx_template_get_other_slot (MonoRuntimeGenericContextTemplate *template, int type_argc, int slot)
195 MonoRuntimeGenericContextInfoTemplate *oti;
197 g_assert (slot >= 0);
199 for (oti = get_info_templates (template, type_argc), i = 0; i < slot; oti = oti->next, ++i) {
208 * LOCKING: loader lock
211 rgctx_template_num_infos (MonoRuntimeGenericContextTemplate *template, int type_argc)
213 MonoRuntimeGenericContextInfoTemplate *oti;
216 for (i = 0, oti = get_info_templates (template, type_argc); oti; ++i, oti = oti->next)
222 /* Maps from uninstantiated generic classes to GList's of
223 * uninstantiated generic classes whose parent is the key class or an
224 * instance of the key class.
226 * LOCKING: loader lock
228 static GHashTable *generic_subclass_hash;
231 * LOCKING: templates lock
234 class_set_rgctx_template (MonoClass *klass, MonoRuntimeGenericContextTemplate *rgctx_template)
236 if (!klass->image->rgctx_template_hash)
237 klass->image->rgctx_template_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
239 g_hash_table_insert (klass->image->rgctx_template_hash, klass, rgctx_template);
243 * LOCKING: loader lock
245 static MonoRuntimeGenericContextTemplate*
246 class_lookup_rgctx_template (MonoClass *klass)
248 MonoRuntimeGenericContextTemplate *template;
250 if (!klass->image->rgctx_template_hash)
253 template = g_hash_table_lookup (klass->image->rgctx_template_hash, klass);
259 * LOCKING: loader lock
262 register_generic_subclass (MonoClass *klass)
264 MonoClass *parent = klass->parent;
266 MonoRuntimeGenericContextTemplate *rgctx_template = class_lookup_rgctx_template (klass);
268 g_assert (rgctx_template);
270 if (parent->generic_class)
271 parent = parent->generic_class->container_class;
273 if (!generic_subclass_hash)
274 generic_subclass_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
276 subclass = g_hash_table_lookup (generic_subclass_hash, parent);
277 rgctx_template->next_subclass = subclass;
278 g_hash_table_insert (generic_subclass_hash, parent, klass);
282 move_subclasses_not_in_image_foreach_func (MonoClass *klass, MonoClass *subclass, MonoImage *image)
286 if (klass->image == image) {
287 /* The parent class itself is in the image, so all the
288 subclasses must be in the image, too. If not,
289 we're removing an image containing a class which
290 still has a subclass in another image. */
293 g_assert (subclass->image == image);
294 subclass = class_lookup_rgctx_template (subclass)->next_subclass;
302 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
303 MonoClass *next = subclass_template->next_subclass;
305 if (subclass->image != image) {
306 subclass_template->next_subclass = new_list;
314 g_hash_table_insert (generic_subclass_hash, klass, new_list);
318 * mono_class_unregister_image_generic_subclasses:
321 * Removes all classes of the image from the generic subclass hash.
322 * Must be called when an image is unloaded.
325 mono_class_unregister_image_generic_subclasses (MonoImage *image, gpointer user_data)
327 GHashTable *old_hash;
329 //g_print ("unregistering image %s\n", image->name);
331 if (!generic_subclass_hash)
336 old_hash = generic_subclass_hash;
337 generic_subclass_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
339 g_hash_table_foreach (old_hash, (GHFunc)move_subclasses_not_in_image_foreach_func, image);
341 mono_loader_unlock ();
343 g_hash_table_destroy (old_hash);
346 static MonoRuntimeGenericContextTemplate*
347 alloc_template (MonoClass *klass)
349 int size = sizeof (MonoRuntimeGenericContextTemplate);
351 num_templates_allocted++;
352 num_templates_bytes += size;
354 return mono_image_alloc0 (klass->image, size);
357 /* LOCKING: Takes the loader lock */
358 static MonoRuntimeGenericContextInfoTemplate*
359 alloc_oti (MonoImage *image)
361 int size = sizeof (MonoRuntimeGenericContextInfoTemplate);
364 num_oti_bytes += size;
366 return mono_image_alloc0 (image, size);
369 #define MONO_RGCTX_SLOT_USED_MARKER ((gpointer)&mono_defaults.object_class->byval_arg)
372 * Return true if this info type has the notion of identify.
374 * Some info types expect that each insert results in a new slot been assigned.
377 info_has_identity (MonoRgctxInfoType info_type)
379 return info_type != MONO_RGCTX_INFO_CAST_CACHE;
383 * LOCKING: loader lock
386 rgctx_template_set_slot (MonoImage *image, MonoRuntimeGenericContextTemplate *template, int type_argc,
387 int slot, gpointer data, MonoRgctxInfoType info_type)
389 static gboolean inited = FALSE;
390 static int num_markers = 0;
391 static int num_data = 0;
394 MonoRuntimeGenericContextInfoTemplate *list = get_info_templates (template, type_argc);
395 MonoRuntimeGenericContextInfoTemplate **oti = &list;
398 mono_counters_register ("RGCTX oti num markers", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_markers);
399 mono_counters_register ("RGCTX oti num data", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_data);
403 g_assert (slot >= 0);
411 *oti = alloc_oti (image);
415 g_assert (!(*oti)->data);
417 (*oti)->info_type = info_type;
419 set_info_templates (image, template, type_argc, list);
421 if (data == MONO_RGCTX_SLOT_USED_MARKER)
428 * mono_method_get_declaring_generic_method:
429 * @method: an inflated method
431 * Returns an inflated method's declaring method.
434 mono_method_get_declaring_generic_method (MonoMethod *method)
436 MonoMethodInflated *inflated;
438 g_assert (method->is_inflated);
440 inflated = (MonoMethodInflated*)method;
442 return inflated->declaring;
446 * mono_class_get_method_generic:
450 * Given a class and a generic method, which has to be of an
451 * instantiation of the same class that klass is an instantiation of,
452 * returns the corresponding method in klass. Example:
454 * klass is Gen<string>
455 * method is Gen<object>.work<int>
457 * returns: Gen<string>.work<int>
460 mono_class_get_method_generic (MonoClass *klass, MonoMethod *method)
462 MonoMethod *declaring, *m;
465 if (method->is_inflated)
466 declaring = mono_method_get_declaring_generic_method (method);
471 if (klass->generic_class)
472 m = mono_class_get_inflated_method (klass, declaring);
475 mono_class_setup_methods (klass);
476 if (klass->exception_type)
478 for (i = 0; i < klass->method.count; ++i) {
479 m = klass->methods [i];
482 if (m->is_inflated && mono_method_get_declaring_generic_method (m) == declaring)
485 if (i >= klass->method.count)
489 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_checked (m, &context, &error);
497 g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */
504 inflate_info (MonoRuntimeGenericContextInfoTemplate *oti, MonoGenericContext *context, MonoClass *klass, gboolean temporary)
506 gpointer data = oti->data;
507 MonoRgctxInfoType info_type = oti->info_type;
512 if (data == MONO_RGCTX_SLOT_USED_MARKER)
513 return MONO_RGCTX_SLOT_USED_MARKER;
517 case MONO_RGCTX_INFO_STATIC_DATA:
518 case MONO_RGCTX_INFO_KLASS:
519 case MONO_RGCTX_INFO_ELEMENT_KLASS:
520 case MONO_RGCTX_INFO_VTABLE:
521 case MONO_RGCTX_INFO_TYPE:
522 case MONO_RGCTX_INFO_REFLECTION_TYPE:
523 case MONO_RGCTX_INFO_CAST_CACHE:
524 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
525 case MONO_RGCTX_INFO_VALUE_SIZE:
526 case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
527 case MONO_RGCTX_INFO_MEMCPY:
528 case MONO_RGCTX_INFO_BZERO:
529 case MONO_RGCTX_INFO_LOCAL_OFFSET:
530 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
531 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: {
532 gpointer result = mono_class_inflate_generic_type_with_mempool (temporary ? NULL : klass->image,
533 data, context, &error);
534 g_assert (mono_error_ok (&error)); /*FIXME proper error handling*/
538 case MONO_RGCTX_INFO_METHOD:
539 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
540 case MONO_RGCTX_INFO_METHOD_RGCTX:
541 case MONO_RGCTX_INFO_METHOD_CONTEXT:
542 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
543 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE: {
544 MonoMethod *method = data;
545 MonoMethod *inflated_method;
546 MonoType *inflated_type = mono_class_inflate_generic_type (&method->klass->byval_arg, context);
547 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
549 mono_metadata_free_type (inflated_type);
551 mono_class_init (inflated_class);
553 g_assert (!method->wrapper_type);
555 if (inflated_class->byval_arg.type == MONO_TYPE_ARRAY ||
556 inflated_class->byval_arg.type == MONO_TYPE_SZARRAY) {
557 inflated_method = mono_method_search_in_array_class (inflated_class,
558 method->name, method->signature);
561 inflated_method = mono_class_inflate_generic_method_checked (method, context, &error);
562 g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */
564 mono_class_init (inflated_method->klass);
565 g_assert (inflated_method->klass == inflated_class);
566 return inflated_method;
568 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO: {
569 MonoGSharedVtMethodInfo *oinfo = data;
570 MonoGSharedVtMethodInfo *res;
571 MonoDomain *domain = mono_domain_get ();
574 res = mono_domain_alloc0 (domain, sizeof (MonoGSharedVtMethodInfo));
576 res->nlocals = info->nlocals;
577 res->locals_types = g_new0 (MonoType*, info->nlocals);
578 for (i = 0; i < info->nlocals; ++i)
579 res->locals_types [i] = mono_class_inflate_generic_type (info->locals_types [i], context);
581 res->num_entries = oinfo->num_entries;
582 res->entries = mono_domain_alloc0 (domain, sizeof (MonoRuntimeGenericContextInfoTemplate) * oinfo->num_entries);
583 for (i = 0; i < oinfo->num_entries; ++i) {
584 MonoRuntimeGenericContextInfoTemplate *otemplate = &oinfo->entries [i];
585 MonoRuntimeGenericContextInfoTemplate *template = &res->entries [i];
587 memcpy (template, otemplate, sizeof (MonoRuntimeGenericContextInfoTemplate));
588 template->data = inflate_info (template, context, klass, FALSE);
592 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE:
593 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT: {
594 MonoJumpInfoGSharedVtCall *info = data;
595 MonoMethod *method = info->method;
596 MonoMethod *inflated_method;
597 MonoType *inflated_type = mono_class_inflate_generic_type (&method->klass->byval_arg, context);
598 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
599 MonoJumpInfoGSharedVtCall *res;
600 MonoDomain *domain = mono_domain_get ();
602 res = mono_domain_alloc0 (domain, sizeof (MonoJumpInfoGSharedVtCall));
603 /* Keep the original signature */
604 res->sig = info->sig;
606 mono_metadata_free_type (inflated_type);
608 mono_class_init (inflated_class);
610 g_assert (!method->wrapper_type);
612 if (inflated_class->byval_arg.type == MONO_TYPE_ARRAY ||
613 inflated_class->byval_arg.type == MONO_TYPE_SZARRAY) {
614 inflated_method = mono_method_search_in_array_class (inflated_class,
615 method->name, method->signature);
618 inflated_method = mono_class_inflate_generic_method_checked (method, context, &error);
619 g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */
621 mono_class_init (inflated_method->klass);
622 g_assert (inflated_method->klass == inflated_class);
623 res->method = inflated_method;
628 case MONO_RGCTX_INFO_CLASS_FIELD:
629 case MONO_RGCTX_INFO_FIELD_OFFSET: {
630 MonoClassField *field = data;
631 MonoType *inflated_type = mono_class_inflate_generic_type (&field->parent->byval_arg, context);
632 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
633 int i = field - field->parent->fields;
634 gpointer dummy = NULL;
636 mono_metadata_free_type (inflated_type);
638 mono_class_get_fields (inflated_class, &dummy);
639 g_assert (inflated_class->fields);
641 return &inflated_class->fields [i];
643 case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI: {
644 MonoMethodSignature *sig = data;
645 MonoMethodSignature *isig;
648 isig = mono_inflate_generic_signature (sig, context, &error);
649 g_assert (mono_error_ok (&error));
652 case MONO_RGCTX_INFO_VIRT_METHOD_CODE:
653 case MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE: {
654 MonoJumpInfoVirtMethod *info = data;
655 MonoJumpInfoVirtMethod *res;
657 MonoDomain *domain = mono_domain_get ();
661 res = mono_domain_alloc0 (domain, sizeof (MonoJumpInfoVirtMethod));
662 t = mono_class_inflate_generic_type (&info->klass->byval_arg, context);
663 res->klass = mono_class_from_mono_type (t);
664 mono_metadata_free_type (t);
666 res->method = mono_class_inflate_generic_method_checked (info->method, context, &error);
667 g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */
672 g_assert_not_reached ();
674 /* Not reached, quiet compiler */
679 free_inflated_info (MonoRgctxInfoType info_type, gpointer info)
685 case MONO_RGCTX_INFO_STATIC_DATA:
686 case MONO_RGCTX_INFO_KLASS:
687 case MONO_RGCTX_INFO_ELEMENT_KLASS:
688 case MONO_RGCTX_INFO_VTABLE:
689 case MONO_RGCTX_INFO_TYPE:
690 case MONO_RGCTX_INFO_REFLECTION_TYPE:
691 case MONO_RGCTX_INFO_CAST_CACHE:
692 mono_metadata_free_type (info);
699 static MonoRuntimeGenericContextInfoTemplate
700 class_get_rgctx_template_oti (MonoClass *klass, int type_argc, guint32 slot, gboolean temporary, gboolean shared, gboolean *do_free);
703 class_uninstantiated (MonoClass *klass)
705 if (klass->generic_class)
706 return klass->generic_class->container_class;
713 * Return the class used to store information when using generic sharing.
716 get_shared_class (MonoClass *klass)
718 return class_uninstantiated (klass);
722 * mono_class_get_runtime_generic_context_template:
725 * Looks up or constructs, if necessary, the runtime generic context template for class.
726 * The template is the same for all instantiations of a class.
728 static MonoRuntimeGenericContextTemplate*
729 mono_class_get_runtime_generic_context_template (MonoClass *klass)
731 MonoRuntimeGenericContextTemplate *parent_template, *template;
734 klass = get_shared_class (klass);
737 template = class_lookup_rgctx_template (klass);
738 mono_loader_unlock ();
743 //g_assert (get_shared_class (class) == class);
745 template = alloc_template (klass);
751 int max_argc, type_argc;
753 parent_template = mono_class_get_runtime_generic_context_template (klass->parent);
754 max_argc = template_get_max_argc (parent_template);
756 for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
757 num_entries = rgctx_template_num_infos (parent_template, type_argc);
759 /* FIXME: quadratic! */
760 for (i = 0; i < num_entries; ++i) {
761 MonoRuntimeGenericContextInfoTemplate oti;
763 oti = class_get_rgctx_template_oti (klass->parent, type_argc, i, FALSE, FALSE, NULL);
764 if (oti.data && oti.data != MONO_RGCTX_SLOT_USED_MARKER) {
765 rgctx_template_set_slot (klass->image, template, type_argc, i,
766 oti.data, oti.info_type);
772 if (class_lookup_rgctx_template (klass)) {
773 /* some other thread already set the template */
774 template = class_lookup_rgctx_template (klass);
776 class_set_rgctx_template (klass, template);
779 register_generic_subclass (klass);
782 mono_loader_unlock ();
788 * class_get_rgctx_template_oti:
790 * Return the info template of CLASS numbered TYPE_ARGC/SLOT.
791 * temporary signifies whether the inflated info (oti.data) will be
792 * used temporarily, in which case it might be heap-allocated, or
793 * permanently, in which case it will be mempool-allocated. If
794 * temporary is set then *do_free will return whether the returned
795 * data must be freed.
797 * LOCKING: loader lock
799 static MonoRuntimeGenericContextInfoTemplate
800 class_get_rgctx_template_oti (MonoClass *klass, int type_argc, guint32 slot, gboolean temporary, gboolean shared, gboolean *do_free)
802 g_assert ((temporary && do_free) || (!temporary && !do_free));
804 DEBUG (printf ("get slot: %s %d\n", mono_type_full_name (&class->byval_arg), slot));
806 if (klass->generic_class && !shared) {
807 MonoRuntimeGenericContextInfoTemplate oti;
808 gboolean tmp_do_free;
810 oti = class_get_rgctx_template_oti (klass->generic_class->container_class,
811 type_argc, slot, TRUE, FALSE, &tmp_do_free);
813 gpointer info = oti.data;
814 oti.data = inflate_info (&oti, &klass->generic_class->context, klass, temporary);
816 free_inflated_info (oti.info_type, info);
823 MonoRuntimeGenericContextTemplate *template;
824 MonoRuntimeGenericContextInfoTemplate *oti;
826 template = mono_class_get_runtime_generic_context_template (klass);
827 oti = rgctx_template_get_other_slot (template, type_argc, slot);
838 class_type_info (MonoDomain *domain, MonoClass *klass, MonoRgctxInfoType info_type)
841 case MONO_RGCTX_INFO_STATIC_DATA: {
842 MonoVTable *vtable = mono_class_vtable (domain, klass);
844 mono_raise_exception (mono_class_get_exception_for_failure (klass));
845 return mono_vtable_get_static_field_data (vtable);
847 case MONO_RGCTX_INFO_KLASS:
849 case MONO_RGCTX_INFO_ELEMENT_KLASS:
850 return klass->element_class;
851 case MONO_RGCTX_INFO_VTABLE: {
852 MonoVTable *vtable = mono_class_vtable (domain, klass);
854 mono_raise_exception (mono_class_get_exception_for_failure (klass));
857 case MONO_RGCTX_INFO_CAST_CACHE: {
858 /*First slot is the cache itself, the second the vtable.*/
859 gpointer **cache_data = mono_domain_alloc0 (domain, sizeof (gpointer) * 2);
860 cache_data [1] = (gpointer)klass;
863 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
864 return GUINT_TO_POINTER (mono_class_array_element_size (klass));
865 case MONO_RGCTX_INFO_VALUE_SIZE:
866 if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg))
867 return GUINT_TO_POINTER (sizeof (gpointer));
869 return GUINT_TO_POINTER (mono_class_value_size (klass, NULL));
870 case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
871 if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg))
872 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_REF);
873 else if (mono_class_is_nullable (klass))
874 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_NULLABLE);
876 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_VTYPE);
877 case MONO_RGCTX_INFO_MEMCPY:
878 case MONO_RGCTX_INFO_BZERO: {
879 static MonoMethod *memcpy_method [17];
880 static MonoMethod *bzero_method [17];
881 MonoJitDomainInfo *domain_info;
885 domain_info = domain_jit_info (domain);
887 if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg)) {
888 size = sizeof (gpointer);
889 align = sizeof (gpointer);
891 size = mono_class_value_size (klass, &align);
894 if (size != 1 && size != 2 && size != 4 && size != 8)
899 if (info_type == MONO_RGCTX_INFO_MEMCPY) {
900 if (!memcpy_method [size]) {
905 sprintf (name, "memcpy");
907 sprintf (name, "memcpy_aligned_%d", size);
908 m = mono_class_get_method_from_name (mono_defaults.string_class, name, 3);
910 mono_memory_barrier ();
911 memcpy_method [size] = m;
913 if (!domain_info->memcpy_addr [size]) {
914 gpointer addr = mono_compile_method (memcpy_method [size]);
915 mono_memory_barrier ();
916 domain_info->memcpy_addr [size] = addr;
918 return domain_info->memcpy_addr [size];
920 if (!bzero_method [size]) {
925 sprintf (name, "bzero");
927 sprintf (name, "bzero_aligned_%d", size);
928 m = mono_class_get_method_from_name (mono_defaults.string_class, name, 2);
930 mono_memory_barrier ();
931 bzero_method [size] = m;
933 if (!domain_info->bzero_addr [size]) {
934 gpointer addr = mono_compile_method (bzero_method [size]);
935 mono_memory_barrier ();
936 domain_info->bzero_addr [size] = addr;
938 return domain_info->bzero_addr [size];
941 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
942 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: {
947 if (!mono_class_is_nullable (klass))
948 /* This can happen since all the entries in MonoGSharedVtMethodInfo are inflated, even those which are not used */
951 if (info_type == MONO_RGCTX_INFO_NULLABLE_CLASS_BOX)
952 method = mono_class_get_method_from_name (klass, "Box", 1);
954 method = mono_class_get_method_from_name (klass, "Unbox", 1);
956 addr = mono_compile_method (method);
957 // The caller uses the gsharedvt call signature
958 ji = mini_jit_info_table_find (mono_domain_get (), mono_get_addr_from_ftnptr (addr), NULL);
960 if (mini_jit_info_is_gsharedvt (ji))
961 return mono_create_static_rgctx_trampoline (method, addr);
963 MonoMethodSignature *sig, *gsig;
966 /* Need to add an out wrapper */
968 /* FIXME: We have no access to the gsharedvt signature/gsctx used by the caller, so have to construct it ourselves */
969 gmethod = mini_get_shared_method_full (method, FALSE, TRUE);
970 sig = mono_method_signature (method);
971 gsig = mono_method_signature (gmethod);
973 addr = mini_get_gsharedvt_wrapper (FALSE, addr, sig, gsig, -1, FALSE);
974 addr = mono_create_static_rgctx_trampoline (method, addr);
979 g_assert_not_reached ();
986 ji_is_gsharedvt (MonoJitInfo *ji)
988 if (ji && ji->has_generic_jit_info && (mono_jit_info_get_generic_sharing_context (ji)->is_gsharedvt))
995 * Describes the information used to construct a gsharedvt arg trampoline.
1000 gint32 vcall_offset;
1002 MonoMethodSignature *sig, *gsig;
1003 } GSharedVtTrampInfo;
1006 tramp_info_hash (gconstpointer key)
1008 GSharedVtTrampInfo *tramp = (gpointer)key;
1010 return (gsize)tramp->addr;
1014 tramp_info_equal (gconstpointer a, gconstpointer b)
1016 GSharedVtTrampInfo *tramp1 = (gpointer)a;
1017 GSharedVtTrampInfo *tramp2 = (gpointer)b;
1019 /* The signatures should be internalized */
1020 return tramp1->is_in == tramp2->is_in && tramp1->calli == tramp2->calli && tramp1->vcall_offset == tramp2->vcall_offset &&
1021 tramp1->addr == tramp2->addr && tramp1->sig == tramp2->sig && tramp1->gsig == tramp2->gsig;
1025 * mini_get_gsharedvt_wrapper:
1027 * Return a gsharedvt in/out wrapper for calling ADDR.
1030 mini_get_gsharedvt_wrapper (gboolean gsharedvt_in, gpointer addr, MonoMethodSignature *normal_sig, MonoMethodSignature *gsharedvt_sig, gint32 vcall_offset, gboolean calli)
1032 static gboolean inited = FALSE;
1033 static int num_trampolines;
1035 MonoDomain *domain = mono_domain_get ();
1036 MonoJitDomainInfo *domain_info;
1037 GSharedVtTrampInfo *tramp_info;
1038 GSharedVtTrampInfo tinfo;
1041 mono_counters_register ("GSHAREDVT arg trampolines", MONO_COUNTER_JIT | MONO_COUNTER_INT, &num_trampolines);
1045 memset (&tinfo, 0, sizeof (tinfo));
1046 tinfo.is_in = gsharedvt_in;
1047 tinfo.calli = calli;
1048 tinfo.vcall_offset = vcall_offset;
1050 tinfo.sig = normal_sig;
1051 tinfo.gsig = gsharedvt_sig;
1053 domain_info = domain_jit_info (domain);
1056 * The arg trampolines might only have a finite number in full-aot, so use a cache.
1058 mono_domain_lock (domain);
1059 if (!domain_info->gsharedvt_arg_tramp_hash)
1060 domain_info->gsharedvt_arg_tramp_hash = g_hash_table_new (tramp_info_hash, tramp_info_equal);
1061 res = g_hash_table_lookup (domain_info->gsharedvt_arg_tramp_hash, &tinfo);
1062 mono_domain_unlock (domain);
1066 info = mono_arch_get_gsharedvt_call_info (addr, normal_sig, gsharedvt_sig, gsharedvt_in, vcall_offset, calli);
1069 static gpointer tramp_addr;
1070 MonoMethod *wrapper;
1073 wrapper = mono_marshal_get_gsharedvt_in_wrapper ();
1074 addr = mono_compile_method (wrapper);
1075 mono_memory_barrier ();
1080 static gpointer tramp_addr;
1081 MonoMethod *wrapper;
1084 wrapper = mono_marshal_get_gsharedvt_out_wrapper ();
1085 addr = mono_compile_method (wrapper);
1086 mono_memory_barrier ();
1093 addr = mono_aot_get_gsharedvt_arg_trampoline (info, addr);
1095 addr = mono_arch_get_gsharedvt_arg_trampoline (mono_domain_get (), info, addr);
1100 tramp_info = mono_domain_alloc0 (domain, sizeof (GSharedVtTrampInfo));
1101 memcpy (tramp_info, &tinfo, sizeof (GSharedVtTrampInfo));
1103 mono_domain_lock (domain);
1104 /* Duplicates are not a problem */
1105 g_hash_table_insert (domain_info->gsharedvt_arg_tramp_hash, tramp_info, addr);
1106 mono_domain_unlock (domain);
1112 instantiate_info (MonoDomain *domain, MonoRuntimeGenericContextInfoTemplate *oti,
1113 MonoGenericContext *context, MonoClass *klass)
1121 switch (oti->info_type) {
1122 case MONO_RGCTX_INFO_STATIC_DATA:
1123 case MONO_RGCTX_INFO_KLASS:
1124 case MONO_RGCTX_INFO_ELEMENT_KLASS:
1125 case MONO_RGCTX_INFO_VTABLE:
1126 case MONO_RGCTX_INFO_CAST_CACHE:
1133 data = inflate_info (oti, context, klass, temporary);
1135 switch (oti->info_type) {
1136 case MONO_RGCTX_INFO_STATIC_DATA:
1137 case MONO_RGCTX_INFO_KLASS:
1138 case MONO_RGCTX_INFO_ELEMENT_KLASS:
1139 case MONO_RGCTX_INFO_VTABLE:
1140 case MONO_RGCTX_INFO_CAST_CACHE:
1141 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
1142 case MONO_RGCTX_INFO_VALUE_SIZE:
1143 case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
1144 case MONO_RGCTX_INFO_MEMCPY:
1145 case MONO_RGCTX_INFO_BZERO:
1146 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
1147 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: {
1148 MonoClass *arg_class = mono_class_from_mono_type (data);
1150 free_inflated_info (oti->info_type, data);
1151 g_assert (arg_class);
1153 /* The class might be used as an argument to
1154 mono_value_copy(), which requires that its GC
1155 descriptor has been computed. */
1156 if (oti->info_type == MONO_RGCTX_INFO_KLASS)
1157 mono_class_compute_gc_descriptor (arg_class);
1159 return class_type_info (domain, arg_class, oti->info_type);
1161 case MONO_RGCTX_INFO_TYPE:
1163 case MONO_RGCTX_INFO_REFLECTION_TYPE:
1164 return mono_type_get_object (domain, data);
1165 case MONO_RGCTX_INFO_METHOD:
1167 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE: {
1170 addr = mono_compile_method (data);
1171 return mini_add_method_trampoline (data, addr, mono_method_needs_static_rgctx_invoke (data, FALSE), FALSE);
1173 case MONO_RGCTX_INFO_VIRT_METHOD_CODE: {
1174 MonoJumpInfoVirtMethod *info = data;
1175 MonoClass *iface_class = info->method->klass;
1181 mono_class_setup_vtable (info->klass);
1182 // FIXME: Check type load
1183 if (iface_class->flags & TYPE_ATTRIBUTE_INTERFACE) {
1184 ioffset = mono_class_interface_offset (info->klass, iface_class);
1185 g_assert (ioffset != -1);
1189 slot = mono_method_get_vtable_slot (info->method);
1190 g_assert (slot != -1);
1191 g_assert (info->klass->vtable);
1192 method = info->klass->vtable [ioffset + slot];
1194 method = mono_class_inflate_generic_method_checked (method, context, &error);
1196 addr = mono_compile_method (method);
1197 return mini_add_method_trampoline (method, addr, mono_method_needs_static_rgctx_invoke (method, FALSE), FALSE);
1199 case MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE: {
1200 MonoJumpInfoVirtMethod *info = data;
1201 MonoClass *iface_class = info->method->klass;
1203 MonoClass *impl_class;
1206 mono_class_setup_vtable (info->klass);
1207 // FIXME: Check type load
1208 if (iface_class->flags & TYPE_ATTRIBUTE_INTERFACE) {
1209 ioffset = mono_class_interface_offset (info->klass, iface_class);
1210 g_assert (ioffset != -1);
1214 slot = mono_method_get_vtable_slot (info->method);
1215 g_assert (slot != -1);
1216 g_assert (info->klass->vtable);
1217 method = info->klass->vtable [ioffset + slot];
1219 impl_class = method->klass;
1220 if (MONO_TYPE_IS_REFERENCE (&impl_class->byval_arg))
1221 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_REF);
1222 else if (mono_class_is_nullable (impl_class))
1223 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_NULLABLE);
1225 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_VTYPE);
1227 #ifndef DISABLE_REMOTING
1228 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
1229 return mono_compile_method (mono_marshal_get_remoting_invoke_with_check (data));
1231 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE:
1232 return mono_domain_alloc0 (domain, sizeof (gpointer));
1233 case MONO_RGCTX_INFO_CLASS_FIELD:
1235 case MONO_RGCTX_INFO_FIELD_OFFSET: {
1236 MonoClassField *field = data;
1238 /* The value is offset by 1 */
1239 if (field->parent->valuetype && !(field->type->attrs & FIELD_ATTRIBUTE_STATIC))
1240 return GUINT_TO_POINTER (field->offset - sizeof (MonoObject) + 1);
1242 return GUINT_TO_POINTER (field->offset + 1);
1244 case MONO_RGCTX_INFO_METHOD_RGCTX: {
1245 MonoMethodInflated *method = data;
1248 g_assert (method->method.method.is_inflated);
1249 g_assert (method->context.method_inst);
1251 vtable = mono_class_vtable (domain, method->method.method.klass);
1253 mono_raise_exception (mono_class_get_exception_for_failure (method->method.method.klass));
1255 return mono_method_lookup_rgctx (vtable, method->context.method_inst);
1257 case MONO_RGCTX_INFO_METHOD_CONTEXT: {
1258 MonoMethodInflated *method = data;
1260 g_assert (method->method.method.is_inflated);
1261 g_assert (method->context.method_inst);
1263 return method->context.method_inst;
1265 case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI: {
1266 MonoMethodSignature *gsig = oti->data;
1267 MonoMethodSignature *sig = data;
1271 * This is an indirect call to the address passed by the caller in the rgctx reg.
1273 addr = mini_get_gsharedvt_wrapper (FALSE, NULL, sig, gsig, -1, TRUE);
1276 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE:
1277 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT: {
1278 MonoJumpInfoGSharedVtCall *call_info = data;
1279 MonoMethodSignature *call_sig;
1282 MonoJitInfo *callee_ji;
1283 gboolean virtual = oti->info_type == MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT;
1284 gint32 vcall_offset;
1285 gboolean callee_gsharedvt;
1287 /* This is the original generic signature used by the caller */
1288 call_sig = call_info->sig;
1289 /* This is the instantiated method which is called */
1290 method = call_info->method;
1292 g_assert (method->is_inflated);
1295 addr = mono_compile_method (method);
1300 /* Same as in mono_emit_method_call_full () */
1301 if ((method->klass->parent == mono_defaults.multicastdelegate_class) && (!strcmp (method->name, "Invoke"))) {
1302 /* See mono_emit_method_call_full () */
1303 /* The gsharedvt trampoline will recognize this constant */
1304 vcall_offset = MONO_GSHAREDVT_DEL_INVOKE_VT_OFFSET;
1305 } else if (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
1306 guint32 imt_slot = mono_method_get_imt_slot (method);
1307 vcall_offset = ((gint32)imt_slot - MONO_IMT_SIZE) * SIZEOF_VOID_P;
1309 vcall_offset = G_STRUCT_OFFSET (MonoVTable, vtable) +
1310 ((mono_method_get_vtable_index (method)) * (SIZEOF_VOID_P));
1316 // FIXME: This loads information in the AOT case
1317 callee_ji = mini_jit_info_table_find (mono_domain_get (), mono_get_addr_from_ftnptr (addr), NULL);
1318 callee_gsharedvt = ji_is_gsharedvt (callee_ji);
1321 * For gsharedvt calls made out of gsharedvt methods, the callee could end up being a gsharedvt method, or a normal
1322 * non-shared method. The latter call cannot be patched, so instead of using a normal call, we make an indirect
1323 * call through the rgctx, in effect patching the rgctx entry instead of the call site.
1324 * For virtual calls, the caller might be a normal or a gsharedvt method. Since there is only one vtable slot,
1325 * this difference needs to be handed on the caller side. This is currently implemented by adding a gsharedvt-in
1326 * trampoline to all gsharedvt methods and storing this trampoline into the vtable slot. Virtual calls made from
1327 * gsharedvt methods always go through a gsharedvt-out trampoline, so the calling sequence is:
1328 * caller -> out trampoline -> in trampoline -> callee
1329 * This is not very efficient, but it is easy to implement.
1331 if (virtual || !callee_gsharedvt) {
1332 MonoMethodSignature *sig, *gsig;
1334 g_assert (method->is_inflated);
1336 sig = mono_method_signature (method);
1339 addr = mini_get_gsharedvt_wrapper (FALSE, addr, sig, gsig, vcall_offset, FALSE);
1342 printf ("OUT-VCALL: %s\n", mono_method_full_name (method, TRUE));
1344 printf ("OUT: %s\n", mono_method_full_name (method, TRUE));
1346 // } else if (!mini_is_gsharedvt_variable_signature (mono_method_signature (caller_method)) && callee_gsharedvt) {
1347 } else if (callee_gsharedvt) {
1348 MonoMethodSignature *sig, *gsig;
1351 * This is a combination of the out and in cases, since both the caller and the callee are gsharedvt methods.
1352 * The caller and the callee can use different gsharedvt signatures, so we have to add both an out and an in
1355 * public void foo<T1> (T1 t1, T t, object o) {}
1357 * class AClass : Base<long> {
1358 * public void bar<T> (T t, long time, object o) {
1362 * Here, the caller uses !!0,long, while the callee uses !!0,!0
1363 * FIXME: Optimize this.
1366 if (call_sig == mono_method_signature (method)) {
1368 sig = mono_method_signature (method);
1369 gsig = mono_method_signature (jinfo_get_method (callee_ji));
1371 addr = mini_get_gsharedvt_wrapper (TRUE, callee_ji->code_start, sig, gsig, -1, FALSE);
1373 sig = mono_method_signature (method);
1376 addr = mini_get_gsharedvt_wrapper (FALSE, addr, sig, gsig, -1, FALSE);
1378 //printf ("OUT-IN-RGCTX: %s\n", mono_method_full_name (method, TRUE));
1384 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO: {
1385 MonoGSharedVtMethodInfo *info = data;
1386 MonoGSharedVtMethodRuntimeInfo *res;
1388 int i, offset, align, size;
1391 res = g_malloc0 (sizeof (MonoGSharedVtMethodRuntimeInfo) + (info->num_entries * sizeof (gpointer)));
1394 for (i = 0; i < info->num_entries; ++i) {
1395 MonoRuntimeGenericContextInfoTemplate *template = &info->entries [i];
1397 switch (template->info_type) {
1398 case MONO_RGCTX_INFO_LOCAL_OFFSET:
1401 size = mono_type_size (t, &align);
1403 if (align < sizeof (gpointer))
1404 align = sizeof (gpointer);
1405 if (MONO_TYPE_ISSTRUCT (t) && align < 2 * sizeof (gpointer))
1406 align = 2 * sizeof (gpointer);
1408 // FIXME: Do the same things as alloc_stack_slots
1409 offset += align - 1;
1410 offset &= ~(align - 1);
1411 res->entries [i] = GINT_TO_POINTER (offset);
1415 res->entries [i] = instantiate_info (domain, template, context, klass);
1419 res->locals_size = offset;
1424 g_assert_not_reached ();
1431 * LOCKING: loader lock
1434 fill_in_rgctx_template_slot (MonoClass *klass, int type_argc, int index, gpointer data, MonoRgctxInfoType info_type)
1436 MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (klass);
1437 MonoClass *subclass;
1439 rgctx_template_set_slot (klass->image, template, type_argc, index, data, info_type);
1441 /* Recurse for all subclasses */
1442 if (generic_subclass_hash)
1443 subclass = g_hash_table_lookup (generic_subclass_hash, klass);
1448 MonoRuntimeGenericContextInfoTemplate subclass_oti;
1449 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
1451 g_assert (subclass_template);
1453 subclass_oti = class_get_rgctx_template_oti (subclass->parent, type_argc, index, FALSE, FALSE, NULL);
1454 g_assert (subclass_oti.data);
1456 fill_in_rgctx_template_slot (subclass, type_argc, index, subclass_oti.data, info_type);
1458 subclass = subclass_template->next_subclass;
1463 mono_rgctx_info_type_to_str (MonoRgctxInfoType type)
1466 case MONO_RGCTX_INFO_STATIC_DATA: return "STATIC_DATA";
1467 case MONO_RGCTX_INFO_KLASS: return "KLASS";
1468 case MONO_RGCTX_INFO_ELEMENT_KLASS: return "ELEMENT_KLASS";
1469 case MONO_RGCTX_INFO_VTABLE: return "VTABLE";
1470 case MONO_RGCTX_INFO_TYPE: return "TYPE";
1471 case MONO_RGCTX_INFO_REFLECTION_TYPE: return "REFLECTION_TYPE";
1472 case MONO_RGCTX_INFO_METHOD: return "METHOD";
1473 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO: return "GSHAREDVT_INFO";
1474 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE: return "GENERIC_METHOD_CODE";
1475 case MONO_RGCTX_INFO_CLASS_FIELD: return "CLASS_FIELD";
1476 case MONO_RGCTX_INFO_METHOD_RGCTX: return "METHOD_RGCTX";
1477 case MONO_RGCTX_INFO_METHOD_CONTEXT: return "METHOD_CONTEXT";
1478 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK: return "REMOTING_INVOKE_WITH_CHECK";
1479 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE: return "METHOD_DELEGATE_CODE";
1480 case MONO_RGCTX_INFO_CAST_CACHE: return "CAST_CACHE";
1481 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE: return "ARRAY_ELEMENT_SIZE";
1482 case MONO_RGCTX_INFO_VALUE_SIZE: return "VALUE_SIZE";
1483 case MONO_RGCTX_INFO_CLASS_BOX_TYPE: return "CLASS_BOX_TYPE";
1484 case MONO_RGCTX_INFO_FIELD_OFFSET: return "FIELD_OFFSET";
1485 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE: return "METHOD_GSHAREDVT_OUT_TRAMPOLINE";
1486 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT: return "METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT";
1487 case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI: return "SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI";
1488 case MONO_RGCTX_INFO_MEMCPY: return "MEMCPY";
1489 case MONO_RGCTX_INFO_BZERO: return "BZERO";
1490 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX: return "NULLABLE_CLASS_BOX";
1491 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: return "NULLABLE_CLASS_UNBOX";
1492 case MONO_RGCTX_INFO_VIRT_METHOD_CODE: return "VIRT_METHOD_CODE";
1493 case MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE: return "VIRT_METHOD_BOX_TYPE";
1495 return "<UNKNOWN RGCTX INFO TYPE>";
1499 G_GNUC_UNUSED static char*
1500 rgctx_info_to_str (MonoRgctxInfoType info_type, gpointer data)
1502 switch (info_type) {
1503 case MONO_RGCTX_INFO_VTABLE:
1504 return mono_type_full_name ((MonoType*)data);
1506 return g_strdup_printf ("<%p>", data);
1511 * LOCKING: loader lock
1514 register_info (MonoClass *klass, int type_argc, gpointer data, MonoRgctxInfoType info_type)
1517 MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (klass);
1519 MonoRuntimeGenericContextInfoTemplate *oti;
1521 for (i = 0, oti = get_info_templates (template, type_argc); oti; ++i, oti = oti->next) {
1526 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)));
1528 /* Mark the slot as used in all parent classes (until we find
1529 a parent class which already has it marked used). */
1530 parent = klass->parent;
1531 while (parent != NULL) {
1532 MonoRuntimeGenericContextTemplate *parent_template;
1533 MonoRuntimeGenericContextInfoTemplate *oti;
1535 if (parent->generic_class)
1536 parent = parent->generic_class->container_class;
1538 parent_template = mono_class_get_runtime_generic_context_template (parent);
1539 oti = rgctx_template_get_other_slot (parent_template, type_argc, i);
1541 if (oti && oti->data)
1544 rgctx_template_set_slot (parent->image, parent_template, type_argc, i,
1545 MONO_RGCTX_SLOT_USED_MARKER, 0);
1547 parent = parent->parent;
1550 /* Fill in the slot in this class and in all subclasses
1552 fill_in_rgctx_template_slot (klass, type_argc, i, data, info_type);
1558 info_equal (gpointer data1, gpointer data2, MonoRgctxInfoType info_type)
1560 switch (info_type) {
1561 case MONO_RGCTX_INFO_STATIC_DATA:
1562 case MONO_RGCTX_INFO_KLASS:
1563 case MONO_RGCTX_INFO_ELEMENT_KLASS:
1564 case MONO_RGCTX_INFO_VTABLE:
1565 case MONO_RGCTX_INFO_TYPE:
1566 case MONO_RGCTX_INFO_REFLECTION_TYPE:
1567 case MONO_RGCTX_INFO_CAST_CACHE:
1568 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
1569 case MONO_RGCTX_INFO_VALUE_SIZE:
1570 case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
1571 case MONO_RGCTX_INFO_MEMCPY:
1572 case MONO_RGCTX_INFO_BZERO:
1573 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
1574 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX:
1575 return mono_class_from_mono_type (data1) == mono_class_from_mono_type (data2);
1576 case MONO_RGCTX_INFO_METHOD:
1577 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO:
1578 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
1579 case MONO_RGCTX_INFO_CLASS_FIELD:
1580 case MONO_RGCTX_INFO_FIELD_OFFSET:
1581 case MONO_RGCTX_INFO_METHOD_RGCTX:
1582 case MONO_RGCTX_INFO_METHOD_CONTEXT:
1583 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
1584 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE:
1585 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE:
1586 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT:
1587 case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI:
1588 return data1 == data2;
1589 case MONO_RGCTX_INFO_VIRT_METHOD_CODE:
1590 case MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE: {
1591 MonoJumpInfoVirtMethod *info1 = data1;
1592 MonoJumpInfoVirtMethod *info2 = data2;
1594 return info1->klass == info2->klass && info1->method == info2->method;
1597 g_assert_not_reached ();
1604 * mini_rgctx_info_type_to_patch_info_type:
1606 * Return the type of the runtime object referred to by INFO_TYPE.
1609 mini_rgctx_info_type_to_patch_info_type (MonoRgctxInfoType info_type)
1611 switch (info_type) {
1612 case MONO_RGCTX_INFO_STATIC_DATA:
1613 case MONO_RGCTX_INFO_KLASS:
1614 case MONO_RGCTX_INFO_ELEMENT_KLASS:
1615 case MONO_RGCTX_INFO_VTABLE:
1616 case MONO_RGCTX_INFO_TYPE:
1617 case MONO_RGCTX_INFO_REFLECTION_TYPE:
1618 case MONO_RGCTX_INFO_CAST_CACHE:
1619 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
1620 case MONO_RGCTX_INFO_VALUE_SIZE:
1621 case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
1622 case MONO_RGCTX_INFO_MEMCPY:
1623 case MONO_RGCTX_INFO_BZERO:
1624 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
1625 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX:
1626 case MONO_RGCTX_INFO_LOCAL_OFFSET:
1627 return MONO_PATCH_INFO_CLASS;
1628 case MONO_RGCTX_INFO_FIELD_OFFSET:
1629 return MONO_PATCH_INFO_FIELD;
1631 g_assert_not_reached ();
1637 lookup_or_register_info (MonoClass *klass, int type_argc, gpointer data, MonoRgctxInfoType info_type,
1638 MonoGenericContext *generic_context)
1640 static gboolean inited = FALSE;
1641 static int max_slot = 0;
1643 MonoRuntimeGenericContextTemplate *rgctx_template =
1644 mono_class_get_runtime_generic_context_template (klass);
1645 MonoRuntimeGenericContextInfoTemplate *oti_list, *oti;
1648 klass = get_shared_class (klass);
1650 mono_loader_lock ();
1652 if (info_has_identity (info_type)) {
1653 oti_list = get_info_templates (rgctx_template, type_argc);
1655 for (oti = oti_list, i = 0; oti; oti = oti->next, ++i) {
1656 gpointer inflated_data;
1658 if (oti->info_type != info_type || !oti->data)
1661 inflated_data = inflate_info (oti, generic_context, klass, TRUE);
1663 if (info_equal (data, inflated_data, info_type)) {
1664 free_inflated_info (info_type, inflated_data);
1665 mono_loader_unlock ();
1668 free_inflated_info (info_type, inflated_data);
1672 /* We haven't found the info */
1673 i = register_info (klass, type_argc, data, info_type);
1675 mono_loader_unlock ();
1678 mono_counters_register ("RGCTX max slot number", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &max_slot);
1688 * mono_method_lookup_or_register_info:
1690 * @in_mrgctx: whether to put the data into the MRGCTX
1691 * @data: the info data
1692 * @info_type: the type of info to register about data
1693 * @generic_context: a generic context
1695 * Looks up and, if necessary, adds information about data/info_type in
1696 * method's or method's class runtime generic context. Returns the
1697 * encoded slot number.
1700 mono_method_lookup_or_register_info (MonoMethod *method, gboolean in_mrgctx, gpointer data,
1701 MonoRgctxInfoType info_type, MonoGenericContext *generic_context)
1703 MonoClass *klass = method->klass;
1704 int type_argc, index;
1707 MonoGenericInst *method_inst = mono_method_get_context (method)->method_inst;
1709 g_assert (method->is_inflated && method_inst);
1710 type_argc = method_inst->type_argc;
1711 g_assert (type_argc > 0);
1716 index = lookup_or_register_info (klass, type_argc, data, info_type, generic_context);
1718 //g_print ("rgctx item at index %d argc %d\n", index, type_argc);
1721 return MONO_RGCTX_SLOT_MAKE_MRGCTX (index);
1723 return MONO_RGCTX_SLOT_MAKE_RGCTX (index);
1727 * mono_class_rgctx_get_array_size:
1728 * @n: The number of the array
1729 * @mrgctx: Whether it's an MRGCTX as opposed to a RGCTX.
1731 * Returns the number of slots in the n'th array of a (M)RGCTX. That
1732 * number includes the slot for linking and - for MRGCTXs - the two
1733 * slots in the first array for additional information.
1736 mono_class_rgctx_get_array_size (int n, gboolean mrgctx)
1738 g_assert (n >= 0 && n < 30);
1747 * LOCKING: domain lock
1750 alloc_rgctx_array (MonoDomain *domain, int n, gboolean is_mrgctx)
1752 static gboolean inited = FALSE;
1753 static int rgctx_num_alloced = 0;
1754 static int rgctx_bytes_alloced = 0;
1755 static int mrgctx_num_alloced = 0;
1756 static int mrgctx_bytes_alloced = 0;
1758 int size = mono_class_rgctx_get_array_size (n, is_mrgctx) * sizeof (gpointer);
1759 gpointer array = mono_domain_alloc0 (domain, size);
1762 mono_counters_register ("RGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_num_alloced);
1763 mono_counters_register ("RGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_bytes_alloced);
1764 mono_counters_register ("MRGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_num_alloced);
1765 mono_counters_register ("MRGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_bytes_alloced);
1770 mrgctx_num_alloced++;
1771 mrgctx_bytes_alloced += size;
1773 rgctx_num_alloced++;
1774 rgctx_bytes_alloced += size;
1781 fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContext *rgctx, guint32 slot,
1782 MonoGenericInst *method_inst)
1785 int i, first_slot, size;
1786 MonoDomain *domain = class_vtable->domain;
1787 MonoClass *klass = class_vtable->klass;
1788 MonoGenericContext *class_context = klass->generic_class ? &klass->generic_class->context : NULL;
1789 MonoRuntimeGenericContextInfoTemplate oti;
1790 MonoGenericContext context = { class_context ? class_context->class_inst : NULL, method_inst };
1796 mono_domain_lock (domain);
1798 /* First check whether that slot isn't already instantiated.
1799 This might happen because lookup doesn't lock. Allocate
1800 arrays on the way. */
1802 size = mono_class_rgctx_get_array_size (0, method_inst != NULL);
1804 size -= MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
1805 for (i = 0; ; ++i) {
1808 if (method_inst && i == 0)
1809 offset = MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
1813 if (slot < first_slot + size - 1) {
1814 rgctx_index = slot - first_slot + 1 + offset;
1815 info = rgctx [rgctx_index];
1817 mono_domain_unlock (domain);
1822 if (!rgctx [offset + 0])
1823 rgctx [offset + 0] = alloc_rgctx_array (domain, i + 1, method_inst != NULL);
1824 rgctx = rgctx [offset + 0];
1825 first_slot += size - 1;
1826 size = mono_class_rgctx_get_array_size (i + 1, method_inst != NULL);
1829 g_assert (!rgctx [rgctx_index]);
1831 mono_domain_unlock (domain);
1833 oti = class_get_rgctx_template_oti (get_shared_class (klass),
1834 method_inst ? method_inst->type_argc : 0, slot, TRUE, TRUE, &do_free);
1835 /* This might take the loader lock */
1836 info = instantiate_info (domain, &oti, &context, klass);
1841 g_print ("filling mrgctx slot %d table %d index %d\n", slot, i, rgctx_index);
1844 /*FIXME We should use CAS here, no need to take a lock.*/
1845 mono_domain_lock (domain);
1847 /* Check whether the slot hasn't been instantiated in the
1849 if (rgctx [rgctx_index])
1850 info = rgctx [rgctx_index];
1852 rgctx [rgctx_index] = info;
1854 mono_domain_unlock (domain);
1857 free_inflated_info (oti.info_type, oti.data);
1863 * mono_class_fill_runtime_generic_context:
1864 * @class_vtable: a vtable
1865 * @slot: a slot index to be instantiated
1867 * Instantiates a slot in the RGCTX, returning its value.
1870 mono_class_fill_runtime_generic_context (MonoVTable *class_vtable, guint32 slot)
1872 static gboolean inited = FALSE;
1873 static int num_alloced = 0;
1875 MonoDomain *domain = class_vtable->domain;
1876 MonoRuntimeGenericContext *rgctx;
1879 mono_domain_lock (domain);
1882 mono_counters_register ("RGCTX num alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_alloced);
1886 rgctx = class_vtable->runtime_generic_context;
1888 rgctx = alloc_rgctx_array (domain, 0, FALSE);
1889 class_vtable->runtime_generic_context = rgctx;
1893 mono_domain_unlock (domain);
1895 info = fill_runtime_generic_context (class_vtable, rgctx, slot, 0);
1897 DEBUG (printf ("get rgctx slot: %s %d -> %p\n", mono_type_full_name (&class_vtable->klass->byval_arg), slot, info));
1903 * mono_method_fill_runtime_generic_context:
1904 * @mrgctx: an MRGCTX
1905 * @slot: a slot index to be instantiated
1907 * Instantiates a slot in the MRGCTX.
1910 mono_method_fill_runtime_generic_context (MonoMethodRuntimeGenericContext *mrgctx, guint32 slot)
1914 info = fill_runtime_generic_context (mrgctx->class_vtable, (MonoRuntimeGenericContext*)mrgctx, slot, mrgctx->method_inst);
1920 mrgctx_hash_func (gconstpointer key)
1922 const MonoMethodRuntimeGenericContext *mrgctx = key;
1924 return mono_aligned_addr_hash (mrgctx->class_vtable) ^ mono_metadata_generic_inst_hash (mrgctx->method_inst);
1928 mrgctx_equal_func (gconstpointer a, gconstpointer b)
1930 const MonoMethodRuntimeGenericContext *mrgctx1 = a;
1931 const MonoMethodRuntimeGenericContext *mrgctx2 = b;
1933 return mrgctx1->class_vtable == mrgctx2->class_vtable &&
1934 mono_metadata_generic_inst_equal (mrgctx1->method_inst, mrgctx2->method_inst);
1938 * mono_method_lookup_rgctx:
1939 * @class_vtable: a vtable
1940 * @method_inst: the method inst of a generic method
1942 * Returns the MRGCTX for the generic method(s) with the given
1943 * method_inst of the given class_vtable.
1945 * LOCKING: Take the domain lock.
1947 MonoMethodRuntimeGenericContext*
1948 mono_method_lookup_rgctx (MonoVTable *class_vtable, MonoGenericInst *method_inst)
1950 MonoDomain *domain = class_vtable->domain;
1951 MonoMethodRuntimeGenericContext *mrgctx;
1952 MonoMethodRuntimeGenericContext key;
1954 g_assert (!class_vtable->klass->generic_container);
1955 g_assert (!method_inst->is_open);
1957 mono_domain_lock (domain);
1958 if (!domain->method_rgctx_hash)
1959 domain->method_rgctx_hash = g_hash_table_new (mrgctx_hash_func, mrgctx_equal_func);
1961 key.class_vtable = class_vtable;
1962 key.method_inst = method_inst;
1964 mrgctx = g_hash_table_lookup (domain->method_rgctx_hash, &key);
1969 mrgctx = (MonoMethodRuntimeGenericContext*)alloc_rgctx_array (domain, 0, TRUE);
1970 mrgctx->class_vtable = class_vtable;
1971 mrgctx->method_inst = method_inst;
1973 g_hash_table_insert (domain->method_rgctx_hash, mrgctx, mrgctx);
1976 g_print ("mrgctx alloced for %s <", mono_type_get_full_name (class_vtable->klass));
1977 for (i = 0; i < method_inst->type_argc; ++i)
1978 g_print ("%s, ", mono_type_full_name (method_inst->type_argv [i]));
1983 mono_domain_unlock (domain);
1992 generic_inst_is_sharable (MonoGenericInst *inst, gboolean allow_type_vars,
1993 gboolean allow_partial);
1996 type_is_sharable (MonoType *type, gboolean allow_type_vars, gboolean allow_partial)
1998 if (allow_type_vars && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR)) {
1999 MonoType *constraint = type->data.generic_param->gshared_constraint;
2005 if (MONO_TYPE_IS_REFERENCE (type))
2008 /* Allow non ref arguments if they are primitive types or enums (partial sharing). */
2009 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) || (type->type == MONO_TYPE_VALUETYPE && type->data.klass->enumtype)))
2012 if (allow_partial && !type->byref && type->type == MONO_TYPE_GENERICINST && MONO_TYPE_ISSTRUCT (type)) {
2013 MonoGenericClass *gclass = type->data.generic_class;
2015 if (gclass->context.class_inst && !generic_inst_is_sharable (gclass->context.class_inst, allow_type_vars, allow_partial))
2017 if (gclass->context.method_inst && !generic_inst_is_sharable (gclass->context.method_inst, allow_type_vars, allow_partial))
2019 if (mono_class_is_nullable (mono_class_from_mono_type (type)))
2028 generic_inst_is_sharable (MonoGenericInst *inst, gboolean allow_type_vars,
2029 gboolean allow_partial)
2033 for (i = 0; i < inst->type_argc; ++i) {
2034 if (!type_is_sharable (inst->type_argv [i], allow_type_vars, allow_partial))
2042 * mono_is_partially_sharable_inst:
2044 * Return TRUE if INST has ref and non-ref type arguments.
2047 mono_is_partially_sharable_inst (MonoGenericInst *inst)
2050 gboolean has_refs = FALSE, has_non_refs = FALSE;
2052 for (i = 0; i < inst->type_argc; ++i) {
2053 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)
2056 has_non_refs = TRUE;
2059 return has_refs && has_non_refs;
2063 * mono_generic_context_is_sharable_full:
2064 * @context: a generic context
2066 * Returns whether the generic context is sharable. A generic context
2067 * is sharable iff all of its type arguments are reference type, or some of them have a
2068 * reference type, and ALLOW_PARTIAL is TRUE.
2071 mono_generic_context_is_sharable_full (MonoGenericContext *context,
2072 gboolean allow_type_vars,
2073 gboolean allow_partial)
2075 g_assert (context->class_inst || context->method_inst);
2077 if (context->class_inst && !generic_inst_is_sharable (context->class_inst, allow_type_vars, allow_partial))
2080 if (context->method_inst && !generic_inst_is_sharable (context->method_inst, allow_type_vars, allow_partial))
2087 mono_generic_context_is_sharable (MonoGenericContext *context, gboolean allow_type_vars)
2089 return mono_generic_context_is_sharable_full (context, allow_type_vars, partial_sharing_supported ());
2093 * mono_method_is_generic_impl:
2096 * Returns whether the method is either generic or part of a generic
2100 mono_method_is_generic_impl (MonoMethod *method)
2102 if (method->is_inflated)
2104 /* We don't treat wrappers as generic code, i.e., we never
2105 apply generic sharing to them. This is especially
2106 important for static rgctx invoke wrappers, which only work
2107 if not compiled with sharing. */
2108 if (method->wrapper_type != MONO_WRAPPER_NONE)
2110 if (method->klass->generic_container)
2116 has_constraints (MonoGenericContainer *container)
2122 g_assert (container->type_argc > 0);
2123 g_assert (container->type_params);
2125 for (i = 0; i < container->type_argc; ++i)
2126 if (container->type_params [i].constraints)
2133 mini_method_is_open (MonoMethod *method)
2135 if (method->is_inflated) {
2136 MonoGenericContext *ctx = mono_method_get_context (method);
2138 if (ctx->class_inst && ctx->class_inst->is_open)
2140 if (ctx->method_inst && ctx->method_inst->is_open)
2146 static G_GNUC_UNUSED gboolean
2147 is_async_state_machine_class (MonoClass *klass)
2149 static MonoClass *iclass;
2150 static gboolean iclass_set;
2155 iclass = mono_class_from_name (mono_defaults.corlib, "System.Runtime.CompilerServices", "IAsyncStateMachine");
2156 mono_memory_barrier ();
2160 if (iclass && klass->valuetype && mono_class_is_assignable_from (iclass, klass))
2165 static G_GNUC_UNUSED gboolean
2166 is_async_method (MonoMethod *method)
2168 MonoCustomAttrInfo *cattr;
2169 MonoMethodSignature *sig;
2170 gboolean res = FALSE;
2171 static MonoClass *attr_class;
2172 static gboolean attr_class_set;
2176 if (!attr_class_set) {
2177 attr_class = mono_class_from_name (mono_defaults.corlib, "System.Runtime.CompilerServices", "AsyncStateMachineAttribute");
2178 mono_memory_barrier ();
2179 attr_class_set = TRUE;
2182 /* Do less expensive checks first */
2183 sig = mono_method_signature (method);
2184 if (attr_class && sig && ((sig->ret->type == MONO_TYPE_VOID) ||
2185 (sig->ret->type == MONO_TYPE_CLASS && !strcmp (sig->ret->data.generic_class->container_class->name, "Task")) ||
2186 (sig->ret->type == MONO_TYPE_GENERICINST && !strcmp (sig->ret->data.generic_class->container_class->name, "Task`1")))) {
2187 //printf ("X: %s\n", mono_method_full_name (method, TRUE));
2188 cattr = mono_custom_attrs_from_method (method);
2190 if (mono_custom_attrs_has_attr (cattr, attr_class))
2192 mono_custom_attrs_free (cattr);
2199 * mono_method_is_generic_sharable_full:
2201 * @allow_type_vars: whether to regard type variables as reference types
2202 * @allow_partial: whether to allow partial sharing
2203 * @allow_gsharedvt: whenever to allow sharing over valuetypes
2205 * Returns TRUE iff the method is inflated or part of an inflated
2206 * class, its context is sharable and it has no constraints on its
2207 * type parameters. Otherwise returns FALSE.
2210 mono_method_is_generic_sharable_full (MonoMethod *method, gboolean allow_type_vars,
2211 gboolean allow_partial, gboolean allow_gsharedvt)
2213 if (!mono_method_is_generic_impl (method))
2217 if (!mono_debug_count ())
2218 allow_partial = FALSE;
2221 if (!partial_sharing_supported ())
2222 allow_partial = FALSE;
2224 if (mono_class_is_nullable (method->klass))
2226 allow_partial = FALSE;
2228 if (method->klass->image->dynamic)
2230 * Enabling this causes corlib test failures because the JIT encounters generic instances whose
2231 * instance_size is 0.
2233 allow_partial = FALSE;
2236 * Generic async methods have an associated state machine class which is a generic struct. This struct
2237 * is too large to be handled by gsharedvt so we make it visible to the AOT compiler by disabling sharing
2238 * of the async method and the state machine class.
2240 if (is_async_state_machine_class (method->klass))
2243 if (allow_gsharedvt && mini_is_gsharedvt_sharable_method (method)) {
2244 if (is_async_method (method))
2249 if (method->is_inflated) {
2250 MonoMethodInflated *inflated = (MonoMethodInflated*)method;
2251 MonoGenericContext *context = &inflated->context;
2253 if (!mono_generic_context_is_sharable_full (context, allow_type_vars, allow_partial))
2256 g_assert (inflated->declaring);
2258 if (inflated->declaring->is_generic) {
2259 if (has_constraints (mono_method_get_generic_container (inflated->declaring)))
2264 if (method->klass->generic_class) {
2265 if (!mono_generic_context_is_sharable_full (&method->klass->generic_class->context, allow_type_vars, allow_partial))
2268 g_assert (method->klass->generic_class->container_class &&
2269 method->klass->generic_class->container_class->generic_container);
2271 if (has_constraints (method->klass->generic_class->container_class->generic_container))
2275 if (method->klass->generic_container && !allow_type_vars)
2278 /* This does potentially expensive cattr checks, so do it at the end */
2279 if (is_async_method (method)) {
2280 if (mini_method_is_open (method))
2281 /* The JIT can't compile these without sharing */
2290 mono_method_is_generic_sharable (MonoMethod *method, gboolean allow_type_vars)
2292 return mono_method_is_generic_sharable_full (method, allow_type_vars, partial_sharing_supported (), TRUE);
2296 * mono_method_needs_static_rgctx_invoke:
2298 * Return whenever METHOD needs an rgctx argument.
2299 * An rgctx argument is needed when the method is generic sharable, but it doesn't
2300 * have a this argument which can be used to load the rgctx.
2303 mono_method_needs_static_rgctx_invoke (MonoMethod *method, gboolean allow_type_vars)
2305 if (!mono_class_generic_sharing_enabled (method->klass))
2308 if (!mono_method_is_generic_sharable (method, allow_type_vars))
2311 if (method->is_inflated && mono_method_get_context (method)->method_inst)
2314 return ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
2315 method->klass->valuetype) &&
2316 (method->klass->generic_class || method->klass->generic_container);
2319 static MonoGenericInst*
2320 get_object_generic_inst (int type_argc)
2322 MonoType **type_argv;
2325 type_argv = alloca (sizeof (MonoType*) * type_argc);
2327 for (i = 0; i < type_argc; ++i)
2328 type_argv [i] = &mono_defaults.object_class->byval_arg;
2330 return mono_metadata_get_generic_inst (type_argc, type_argv);
2334 * mono_method_construct_object_context:
2337 * Returns a generic context for method with all type variables for
2338 * class and method instantiated with Object.
2341 mono_method_construct_object_context (MonoMethod *method)
2343 MonoGenericContext object_context;
2345 g_assert (!method->klass->generic_class);
2346 if (method->klass->generic_container) {
2347 int type_argc = method->klass->generic_container->type_argc;
2349 object_context.class_inst = get_object_generic_inst (type_argc);
2351 object_context.class_inst = NULL;
2354 if (mono_method_get_context_general (method, TRUE)->method_inst) {
2355 int type_argc = mono_method_get_context_general (method, TRUE)->method_inst->type_argc;
2357 object_context.method_inst = get_object_generic_inst (type_argc);
2359 object_context.method_inst = NULL;
2362 g_assert (object_context.class_inst || object_context.method_inst);
2364 return object_context;
2367 static gboolean gshared_supported;
2368 static gboolean gsharedvt_supported;
2371 mono_set_generic_sharing_supported (gboolean supported)
2373 gshared_supported = supported;
2377 mono_set_generic_sharing_vt_supported (gboolean supported)
2379 gsharedvt_supported = supported;
2383 mono_set_partial_sharing_supported (gboolean supported)
2385 partial_supported = supported;
2389 * mono_class_generic_sharing_enabled:
2392 * Returns whether generic sharing is enabled for class.
2394 * This is a stop-gap measure to slowly introduce generic sharing
2395 * until we have all the issues sorted out, at which time this
2396 * function will disappear and generic sharing will always be enabled.
2399 mono_class_generic_sharing_enabled (MonoClass *klass)
2401 if (gshared_supported)
2408 mini_method_get_context (MonoMethod *method)
2410 return mono_method_get_context_general (method, TRUE);
2414 * mono_method_check_context_used:
2417 * Checks whether the method's generic context uses a type variable.
2418 * Returns an int with the bits MONO_GENERIC_CONTEXT_USED_CLASS and
2419 * MONO_GENERIC_CONTEXT_USED_METHOD set to reflect whether the
2420 * context's class or method instantiation uses type variables.
2423 mono_method_check_context_used (MonoMethod *method)
2425 MonoGenericContext *method_context = mini_method_get_context (method);
2426 int context_used = 0;
2428 if (!method_context) {
2429 /* It might be a method of an array of an open generic type */
2430 if (method->klass->rank)
2431 context_used = mono_class_check_context_used (method->klass);
2433 context_used = mono_generic_context_check_used (method_context);
2434 context_used |= mono_class_check_context_used (method->klass);
2437 return context_used;
2441 generic_inst_equal (MonoGenericInst *inst1, MonoGenericInst *inst2)
2452 if (inst1->type_argc != inst2->type_argc)
2455 for (i = 0; i < inst1->type_argc; ++i)
2456 if (!mono_metadata_type_equal (inst1->type_argv [i], inst2->type_argv [i]))
2463 * mono_generic_context_equal_deep:
2464 * @context1: a generic context
2465 * @context2: a generic context
2467 * Returns whether context1's type arguments are equal to context2's
2471 mono_generic_context_equal_deep (MonoGenericContext *context1, MonoGenericContext *context2)
2473 return generic_inst_equal (context1->class_inst, context2->class_inst) &&
2474 generic_inst_equal (context1->method_inst, context2->method_inst);
2478 * mini_class_get_container_class:
2479 * @class: a generic class
2481 * Returns the class's container class, which is the class itself if
2482 * it doesn't have generic_class set.
2485 mini_class_get_container_class (MonoClass *klass)
2487 if (klass->generic_class)
2488 return klass->generic_class->container_class;
2490 g_assert (klass->generic_container);
2495 * mini_class_get_context:
2496 * @class: a generic class
2498 * Returns the class's generic context.
2501 mini_class_get_context (MonoClass *klass)
2503 if (klass->generic_class)
2504 return &klass->generic_class->context;
2506 g_assert (klass->generic_container);
2507 return &klass->generic_container->context;
2511 * mini_get_basic_type_from_generic:
2514 * Returns a closed type corresponding to the possibly open type
2518 mini_get_basic_type_from_generic (MonoType *type)
2520 if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) && mini_is_gsharedvt_type (type))
2522 else if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR)) {
2523 MonoType *constraint = type->data.generic_param->gshared_constraint;
2524 /* The gparam serial encodes the type this gparam can represent */
2526 return &mono_defaults.object_class->byval_arg;
2530 g_assert (constraint != &mono_defaults.int_class->parent->byval_arg);
2531 klass = mono_class_from_mono_type (constraint);
2532 return &klass->byval_arg;
2535 return mini_native_type_replace_type (mono_type_get_basic_type_from_generic (type));
2540 * mini_type_get_underlying_type:
2542 * Return the underlying type of TYPE, taking into account enums, byref, bool, char and generic
2546 mini_type_get_underlying_type (MonoType *type)
2548 type = mini_native_type_replace_type (type);
2551 return &mono_defaults.int_class->byval_arg;
2552 if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) && mini_is_gsharedvt_type (type))
2554 type = mini_get_basic_type_from_generic (mono_type_get_underlying_type (type));
2555 switch (type->type) {
2556 case MONO_TYPE_BOOLEAN:
2557 return &mono_defaults.byte_class->byval_arg;
2558 case MONO_TYPE_CHAR:
2559 return &mono_defaults.uint16_class->byval_arg;
2566 * mini_type_stack_size:
2568 * @align: Pointer to an int for returning the alignment
2570 * Returns the type's stack size and the alignment in *align.
2573 mini_type_stack_size (MonoType *t, int *align)
2575 return mono_type_stack_size_internal (t, align, TRUE);
2579 * mini_type_stack_size_full:
2581 * Same as mini_type_stack_size, but handle pinvoke data types as well.
2584 mini_type_stack_size_full (MonoType *t, guint32 *align, gboolean pinvoke)
2588 //g_assert (!mini_is_gsharedvt_type (t));
2591 size = mono_type_native_stack_size (t, align);
2596 size = mini_type_stack_size (t, &ialign);
2599 size = mini_type_stack_size (t, NULL);
2607 * mono_generic_sharing_init:
2609 * Register the generic sharing counters.
2612 mono_generic_sharing_init (void)
2614 mono_counters_register ("RGCTX template num allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_templates_allocted);
2615 mono_counters_register ("RGCTX template bytes allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_templates_bytes);
2616 mono_counters_register ("RGCTX oti num allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_oti_allocted);
2617 mono_counters_register ("RGCTX oti bytes allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_oti_bytes);
2619 mono_install_image_unload_hook (mono_class_unregister_image_generic_subclasses, NULL);
2623 mono_generic_sharing_cleanup (void)
2625 mono_remove_image_unload_hook (mono_class_unregister_image_generic_subclasses, NULL);
2627 if (generic_subclass_hash)
2628 g_hash_table_destroy (generic_subclass_hash);
2632 * mini_type_var_is_vt:
2634 * Return whenever T is a type variable instantiated with a vtype.
2637 mini_type_var_is_vt (MonoType *type)
2639 if (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) {
2640 return type->data.generic_param->gshared_constraint && type->data.generic_param->gshared_constraint->type == MONO_TYPE_VALUETYPE;
2642 g_assert_not_reached ();
2648 mini_type_is_reference (MonoType *type)
2650 type = mini_type_get_underlying_type (type);
2651 return mono_type_is_reference (type);
2655 * mini_method_get_rgctx:
2657 * Return the RGCTX which needs to be passed to M when it is called.
2660 mini_method_get_rgctx (MonoMethod *m)
2662 if (mini_method_get_context (m)->method_inst)
2663 return mono_method_lookup_rgctx (mono_class_vtable (mono_domain_get (), m->klass), mini_method_get_context (m)->method_inst);
2665 return mono_class_vtable (mono_domain_get (), m->klass);
2669 * mini_type_is_vtype:
2671 * Return whenever T is a vtype, or a type param instantiated with a vtype.
2672 * Should be used in place of MONO_TYPE_ISSTRUCT () which can't handle gsharedvt.
2675 mini_type_is_vtype (MonoType *t)
2677 t = mini_type_get_underlying_type (t);
2679 return MONO_TYPE_ISSTRUCT (t) || mini_is_gsharedvt_variable_type (t);
2683 mini_class_is_generic_sharable (MonoClass *klass)
2685 if (klass->generic_class && is_async_state_machine_class (klass))
2688 return (klass->generic_class && mono_generic_context_is_sharable (&klass->generic_class->context, FALSE));
2692 mini_is_gsharedvt_variable_klass (MonoClass *klass)
2694 return mini_is_gsharedvt_variable_type (&klass->byval_arg);
2698 mini_is_gsharedvt_gparam (MonoType *t)
2700 /* Matches get_gsharedvt_type () */
2701 return (t->type == MONO_TYPE_VAR || t->type == MONO_TYPE_MVAR) && t->data.generic_param->gshared_constraint && t->data.generic_param->gshared_constraint->type == MONO_TYPE_VALUETYPE;
2705 get_shared_gparam_name (MonoTypeEnum constraint, const char *name)
2707 if (constraint == MONO_TYPE_VALUETYPE) {
2708 return g_strdup_printf ("%s_GSHAREDVT", name);
2709 } else if (constraint == MONO_TYPE_OBJECT) {
2710 return g_strdup_printf ("%s_REF", name);
2711 } else if (constraint == MONO_TYPE_GENERICINST) {
2712 return g_strdup_printf ("%s_INST", name);
2715 char *tname, *tname2, *res;
2717 memset (&t, 0, sizeof (t));
2718 t.type = constraint;
2719 tname = mono_type_full_name (&t);
2720 tname2 = g_utf8_strup (tname, strlen (tname));
2721 res = g_strdup_printf ("%s_%s", name, tname2);
2729 shared_gparam_hash (gconstpointer data)
2731 MonoGSharedGenericParam *p = (MonoGSharedGenericParam*)data;
2734 hash = mono_metadata_generic_param_hash (p->parent);
2735 hash = ((hash << 5) - hash) ^ mono_metadata_type_hash (p->param.param.gshared_constraint);
2741 shared_gparam_equal (gconstpointer ka, gconstpointer kb)
2743 MonoGSharedGenericParam *p1 = (MonoGSharedGenericParam*)ka;
2744 MonoGSharedGenericParam *p2 = (MonoGSharedGenericParam*)kb;
2748 if (p1->parent != p2->parent)
2750 if (!mono_metadata_type_equal (p1->param.param.gshared_constraint, p2->param.param.gshared_constraint))
2756 * mini_get_shared_gparam:
2758 * Create an anonymous gparam from T with a constraint which encodes which types can match it.
2761 mini_get_shared_gparam (MonoType *t, MonoType *constraint)
2763 MonoGenericParam *par = t->data.generic_param;
2764 MonoGSharedGenericParam *copy, key;
2766 MonoImage *image = NULL;
2769 memset (&key, 0, sizeof (key));
2771 key.param.param.gshared_constraint = constraint;
2773 g_assert (mono_generic_param_info (par));
2774 image = get_image_for_generic_param(par);
2777 * Need a cache to ensure the newly created gparam
2778 * is unique wrt T/CONSTRAINT.
2780 mono_image_lock (image);
2781 if (!image->gshared_types) {
2782 image->gshared_types_len = MONO_TYPE_INTERNAL;
2783 image->gshared_types = g_new0 (GHashTable*, image->gshared_types_len);
2785 if (!image->gshared_types [constraint->type])
2786 image->gshared_types [constraint->type] = g_hash_table_new (shared_gparam_hash, shared_gparam_equal);
2787 res = g_hash_table_lookup (image->gshared_types [constraint->type], &key);
2788 mono_image_unlock (image);
2791 copy = mono_image_alloc0 (image, sizeof (MonoGSharedGenericParam));
2792 memcpy (©->param, par, sizeof (MonoGenericParamFull));
2793 name = get_shared_gparam_name (constraint->type, ((MonoGenericParamFull*)copy)->info.name);
2794 copy->param.info.name = mono_image_strdup (image, name);
2797 copy->param.param.owner = par->owner;
2799 copy->param.param.gshared_constraint = constraint;
2801 res = mono_metadata_type_dup (NULL, t);
2802 res->data.generic_param = (MonoGenericParam*)copy;
2805 mono_image_lock (image);
2806 /* Duplicates are ok */
2807 g_hash_table_insert (image->gshared_types [constraint->type], copy, res);
2808 mono_image_unlock (image);
2814 static MonoGenericInst*
2815 get_shared_inst (MonoGenericInst *inst, MonoGenericInst *shared_inst, MonoGenericContainer *container, gboolean all_vt, gboolean gsharedvt, gboolean partial);
2818 get_shared_type (MonoType *t, MonoType *type)
2822 if (!type->byref && type->type == MONO_TYPE_GENERICINST && MONO_TYPE_ISSTRUCT (type)) {
2823 MonoGenericClass *gclass = type->data.generic_class;
2824 MonoGenericContext context;
2827 memset (&context, 0, sizeof (context));
2828 if (gclass->context.class_inst)
2829 context.class_inst = get_shared_inst (gclass->context.class_inst, gclass->container_class->generic_container->context.class_inst, NULL, FALSE, FALSE, TRUE);
2830 if (gclass->context.method_inst)
2831 context.method_inst = get_shared_inst (gclass->context.method_inst, gclass->container_class->generic_container->context.method_inst, NULL, FALSE, FALSE, TRUE);
2833 k = mono_class_inflate_generic_class (gclass->container_class, &context);
2835 return mini_get_shared_gparam (t, &k->byval_arg);
2836 } else if (MONO_TYPE_ISSTRUCT (type)) {
2840 /* Create a type variable with a constraint which encodes which types can match it */
2842 if (type->type == MONO_TYPE_VALUETYPE) {
2843 ttype = mono_class_enum_basetype (type->data.klass)->type;
2844 } else if (MONO_TYPE_IS_REFERENCE (type)) {
2845 ttype = MONO_TYPE_OBJECT;
2846 } else if (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) {
2847 if (type->data.generic_param->gshared_constraint)
2848 return mini_get_shared_gparam (t, type->data.generic_param->gshared_constraint);
2849 ttype = MONO_TYPE_OBJECT;
2856 memset (&t2, 0, sizeof (t2));
2858 klass = mono_class_from_mono_type (&t2);
2860 return mini_get_shared_gparam (t, &klass->byval_arg);
2865 get_gsharedvt_type (MonoType *t)
2867 /* Use TypeHandle as the constraint type since its a valuetype */
2868 return mini_get_shared_gparam (t, &mono_defaults.typehandle_class->byval_arg);
2871 static MonoGenericInst*
2872 get_shared_inst (MonoGenericInst *inst, MonoGenericInst *shared_inst, MonoGenericContainer *container, gboolean all_vt, gboolean gsharedvt, gboolean partial)
2874 MonoGenericInst *res;
2875 MonoType **type_argv;
2878 type_argv = g_new0 (MonoType*, inst->type_argc);
2879 for (i = 0; i < inst->type_argc; ++i) {
2880 if (all_vt || gsharedvt) {
2881 type_argv [i] = get_gsharedvt_type (shared_inst->type_argv [i]);
2883 /* These types match the ones in generic_inst_is_sharable () */
2884 type_argv [i] = get_shared_type (shared_inst->type_argv [i], inst->type_argv [i]);
2888 res = mono_metadata_get_generic_inst (inst->type_argc, type_argv);
2894 * mini_get_shared_method_full:
2896 * Return the method which is actually compiled/registered when doing generic sharing.
2897 * If ALL_VT is true, return the shared method belonging to an all-vtype instantiation.
2898 * If IS_GSHAREDVT is true, treat METHOD as a gsharedvt method even if it fails some constraints.
2899 * METHOD can be a non-inflated generic method.
2902 mini_get_shared_method_full (MonoMethod *method, gboolean all_vt, gboolean is_gsharedvt)
2905 MonoGenericContext shared_context;
2906 MonoMethod *declaring_method, *res;
2907 gboolean partial = FALSE;
2908 gboolean gsharedvt = FALSE;
2909 MonoGenericContainer *class_container, *method_container = NULL;
2910 MonoGenericContext *context = mono_method_get_context (method);
2911 MonoGenericInst *inst;
2913 if (method->is_generic || (method->klass->generic_container && !method->is_inflated)) {
2914 declaring_method = method;
2916 declaring_method = mono_method_get_declaring_generic_method (method);
2919 /* shared_context is the context containing type variables. */
2920 if (declaring_method->is_generic)
2921 shared_context = mono_method_get_generic_container (declaring_method)->context;
2923 shared_context = declaring_method->klass->generic_container->context;
2926 partial = mono_method_is_generic_sharable_full (method, FALSE, TRUE, FALSE);
2928 gsharedvt = is_gsharedvt || (!partial && mini_is_gsharedvt_sharable_method (method));
2930 class_container = declaring_method->klass->generic_container;
2931 method_container = mono_method_get_generic_container (declaring_method);
2934 * Create the shared context by replacing the ref type arguments with
2935 * type parameters, and keeping the rest.
2938 inst = context->class_inst;
2940 inst = shared_context.class_inst;
2942 shared_context.class_inst = get_shared_inst (inst, shared_context.class_inst, class_container, all_vt, gsharedvt, partial);
2945 inst = context->method_inst;
2947 inst = shared_context.method_inst;
2949 shared_context.method_inst = get_shared_inst (inst, shared_context.method_inst, method_container, all_vt, gsharedvt, partial);
2951 res = mono_class_inflate_generic_method_checked (declaring_method, &shared_context, &error);
2952 g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */
2954 //printf ("%s\n", mono_method_full_name (res, 1));
2960 mini_get_shared_method (MonoMethod *method)
2962 return mini_get_shared_method_full (method, FALSE, FALSE);
2966 mini_get_rgctx_entry_slot (MonoJumpInfoRgctxEntry *entry)
2970 switch (entry->data->type) {
2971 case MONO_PATCH_INFO_CLASS:
2972 slot = mono_method_lookup_or_register_info (entry->method, entry->in_mrgctx, &entry->data->data.klass->byval_arg, entry->info_type, mono_method_get_context (entry->method));
2974 case MONO_PATCH_INFO_METHOD:
2975 case MONO_PATCH_INFO_METHODCONST:
2976 slot = mono_method_lookup_or_register_info (entry->method, entry->in_mrgctx, entry->data->data.method, entry->info_type, mono_method_get_context (entry->method));
2978 case MONO_PATCH_INFO_FIELD:
2979 slot = mono_method_lookup_or_register_info (entry->method, entry->in_mrgctx, entry->data->data.field, entry->info_type, mono_method_get_context (entry->method));
2981 case MONO_PATCH_INFO_SIGNATURE:
2982 slot = mono_method_lookup_or_register_info (entry->method, entry->in_mrgctx, entry->data->data.sig, entry->info_type, mono_method_get_context (entry->method));
2984 case MONO_PATCH_INFO_GSHAREDVT_CALL: {
2985 MonoJumpInfoGSharedVtCall *call_info = g_malloc0 (sizeof (MonoJumpInfoGSharedVtCall)); //mono_domain_alloc0 (domain, sizeof (MonoJumpInfoGSharedVtCall));
2987 memcpy (call_info, entry->data->data.gsharedvt, sizeof (MonoJumpInfoGSharedVtCall));
2988 slot = mono_method_lookup_or_register_info (entry->method, entry->in_mrgctx, call_info, entry->info_type, mono_method_get_context (entry->method));
2991 case MONO_PATCH_INFO_GSHAREDVT_METHOD: {
2992 MonoGSharedVtMethodInfo *info;
2993 MonoGSharedVtMethodInfo *oinfo = entry->data->data.gsharedvt_method;
2996 /* Make a copy into the domain mempool */
2997 info = g_malloc0 (sizeof (MonoGSharedVtMethodInfo)); //mono_domain_alloc0 (domain, sizeof (MonoGSharedVtMethodInfo));
2998 info->method = oinfo->method;
2999 info->num_entries = oinfo->num_entries;
3000 info->entries = g_malloc0 (sizeof (MonoRuntimeGenericContextInfoTemplate) * info->num_entries);
3001 for (i = 0; i < oinfo->num_entries; ++i) {
3002 MonoRuntimeGenericContextInfoTemplate *otemplate = &oinfo->entries [i];
3003 MonoRuntimeGenericContextInfoTemplate *template = &info->entries [i];
3005 memcpy (template, otemplate, sizeof (MonoRuntimeGenericContextInfoTemplate));
3007 slot = mono_method_lookup_or_register_info (entry->method, entry->in_mrgctx, info, entry->info_type, mono_method_get_context (entry->method));
3010 case MONO_PATCH_INFO_VIRT_METHOD: {
3011 MonoJumpInfoVirtMethod *info;
3012 MonoJumpInfoVirtMethod *oinfo = entry->data->data.virt_method;
3014 info = g_malloc0 (sizeof (MonoJumpInfoVirtMethod));
3015 memcpy (info, oinfo, sizeof (MonoJumpInfoVirtMethod));
3016 slot = mono_method_lookup_or_register_info (entry->method, entry->in_mrgctx, info, entry->info_type, mono_method_get_context (entry->method));
3020 g_assert_not_reached ();
3027 #if defined(ENABLE_GSHAREDVT)
3029 #include "../../../mono-extensions/mono/mini/mini-generic-sharing-gsharedvt.c"
3034 mini_is_gsharedvt_type (MonoType *t)
3040 mini_is_gsharedvt_klass (MonoClass *klass)
3046 mini_is_gsharedvt_signature (MonoMethodSignature *sig)
3052 mini_is_gsharedvt_variable_type (MonoType *t)
3058 mini_is_gsharedvt_sharable_method (MonoMethod *method)
3064 mini_is_gsharedvt_variable_signature (MonoMethodSignature *sig)
3069 #endif /* !MONOTOUCH */