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 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE:
538 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT: {
539 MonoMethod *method = data;
540 MonoMethod *inflated_method;
541 MonoType *inflated_type = mono_class_inflate_generic_type (&method->klass->byval_arg, context);
542 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
544 mono_metadata_free_type (inflated_type);
546 mono_class_init (inflated_class);
548 g_assert (!method->wrapper_type);
550 if (inflated_class->byval_arg.type == MONO_TYPE_ARRAY ||
551 inflated_class->byval_arg.type == MONO_TYPE_SZARRAY) {
552 inflated_method = mono_method_search_in_array_class (inflated_class,
553 method->name, method->signature);
555 inflated_method = mono_class_inflate_generic_method (method, context);
557 mono_class_init (inflated_method->klass);
558 g_assert (inflated_method->klass == inflated_class);
559 return inflated_method;
562 case MONO_RGCTX_INFO_CLASS_FIELD:
563 case MONO_RGCTX_INFO_FIELD_OFFSET: {
564 MonoClassField *field = data;
565 MonoType *inflated_type = mono_class_inflate_generic_type (&field->parent->byval_arg, context);
566 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
567 int i = field - field->parent->fields;
568 gpointer dummy = NULL;
570 mono_metadata_free_type (inflated_type);
572 mono_class_get_fields (inflated_class, &dummy);
573 g_assert (inflated_class->fields);
575 return &inflated_class->fields [i];
579 g_assert_not_reached ();
581 /* Not reached, quiet compiler */
586 free_inflated_info (MonoRgctxInfoType info_type, gpointer info)
592 case MONO_RGCTX_INFO_STATIC_DATA:
593 case MONO_RGCTX_INFO_KLASS:
594 case MONO_RGCTX_INFO_VTABLE:
595 case MONO_RGCTX_INFO_TYPE:
596 case MONO_RGCTX_INFO_REFLECTION_TYPE:
597 case MONO_RGCTX_INFO_CAST_CACHE:
598 mono_metadata_free_type (info);
605 static MonoRuntimeGenericContextInfoTemplate
606 class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot, gboolean temporary, gboolean shared, gboolean *do_free);
609 class_uninstantiated (MonoClass *class)
611 if (class->generic_class)
612 return class->generic_class->container_class;
617 generic_inst_is_sharable (MonoGenericInst *inst, gboolean allow_type_vars,
618 gboolean allow_partial)
622 for (i = 0; i < inst->type_argc; ++i) {
623 MonoType *type = inst->type_argv [i];
625 if (MONO_TYPE_IS_REFERENCE (type) || (allow_type_vars && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR)))
629 * Allow non ref arguments, if there is at least one ref argument
631 * FIXME: Allow more types
633 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)))
643 * mono_is_partially_sharable_inst:
645 * Return TRUE if INST has ref and non-ref type arguments.
648 mono_is_partially_sharable_inst (MonoGenericInst *inst)
651 gboolean has_refs = FALSE, has_non_refs = FALSE;
653 for (i = 0; i < inst->type_argc; ++i) {
654 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)
660 return has_refs && has_non_refs;
666 * Return the class used to store information when using generic sharing.
667 * For fully shared classes, it is the generic definition, for partially shared
668 * classes, it is an instance with all ref type arguments replaced by the type parameters
669 * of its generic definition.
672 get_shared_class (MonoClass *class)
675 * FIXME: This conflicts with normal instances. Also, some code in this file
676 * like class_get_rgctx_template_oti treats these as normal generic instances
677 * instead of generic classes.
679 //g_assert_not_reached ();
681 /* The gsharedvt changes break this */
682 if (ALLOW_PARTIAL_SHARING)
683 g_assert_not_reached ();
686 if (class->is_inflated) {
687 MonoGenericContext *context = &class->generic_class->context;
688 MonoGenericContext *container_context;
689 MonoGenericContext shared_context;
690 MonoGenericInst *inst;
691 MonoType **type_argv;
694 inst = context->class_inst;
695 if (mono_is_partially_sharable_inst (inst)) {
696 container_context = &class->generic_class->container_class->generic_container->context;
697 type_argv = g_new0 (MonoType*, inst->type_argc);
698 for (i = 0; i < inst->type_argc; ++i) {
699 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 type_argv [i] = container_context->class_inst->type_argv [i];
702 type_argv [i] = inst->type_argv [i];
705 memset (&shared_context, 0, sizeof (MonoGenericContext));
706 shared_context.class_inst = mono_metadata_get_generic_inst (inst->type_argc, type_argv);
709 return mono_class_inflate_generic_class (class->generic_class->container_class, &shared_context);
710 } else if (!generic_inst_is_sharable (inst, TRUE, FALSE)) {
711 /* Happens for partially shared methods of nono-sharable generic class */
717 return class_uninstantiated (class);
721 * mono_class_get_runtime_generic_context_template:
724 * Looks up or constructs, if necessary, the runtime generic context template for class.
725 * The template is the same for all instantiations of a class.
727 static MonoRuntimeGenericContextTemplate*
728 mono_class_get_runtime_generic_context_template (MonoClass *class)
730 MonoRuntimeGenericContextTemplate *parent_template, *template;
733 class = get_shared_class (class);
736 template = class_lookup_rgctx_template (class);
737 mono_loader_unlock ();
742 //g_assert (get_shared_class (class) == class);
744 template = alloc_template (class);
750 int max_argc, type_argc;
752 parent_template = mono_class_get_runtime_generic_context_template (class->parent);
753 max_argc = template_get_max_argc (parent_template);
755 for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
756 num_entries = rgctx_template_num_infos (parent_template, type_argc);
758 /* FIXME: quadratic! */
759 for (i = 0; i < num_entries; ++i) {
760 MonoRuntimeGenericContextInfoTemplate oti;
762 oti = class_get_rgctx_template_oti (class->parent, type_argc, i, FALSE, FALSE, NULL);
763 if (oti.data && oti.data != MONO_RGCTX_SLOT_USED_MARKER) {
764 rgctx_template_set_slot (class->image, template, type_argc, i,
765 oti.data, oti.info_type);
771 if (class_lookup_rgctx_template (class)) {
772 /* some other thread already set the template */
773 template = class_lookup_rgctx_template (class);
775 class_set_rgctx_template (class, template);
778 register_generic_subclass (class);
781 mono_loader_unlock ();
787 * class_get_rgctx_template_oti:
789 * Return the info template of CLASS numbered TYPE_ARGC/SLOT.
790 * temporary signifies whether the inflated info (oti.data) will be
791 * used temporarily, in which case it might be heap-allocated, or
792 * permanently, in which case it will be mempool-allocated. If
793 * temporary is set then *do_free will return whether the returned
794 * data must be freed.
796 * LOCKING: loader lock
798 static MonoRuntimeGenericContextInfoTemplate
799 class_get_rgctx_template_oti (MonoClass *class, int type_argc, guint32 slot, gboolean temporary, gboolean shared, gboolean *do_free)
801 g_assert ((temporary && do_free) || (!temporary && !do_free));
803 DEBUG (printf ("get slot: %s %d\n", mono_type_full_name (&class->byval_arg), slot));
805 if (class->generic_class && !shared) {
806 MonoRuntimeGenericContextInfoTemplate oti;
807 gboolean tmp_do_free;
809 oti = class_get_rgctx_template_oti (class->generic_class->container_class,
810 type_argc, slot, TRUE, FALSE, &tmp_do_free);
812 gpointer info = oti.data;
813 oti.data = inflate_info (&oti, &class->generic_class->context, class, temporary);
815 free_inflated_info (oti.info_type, info);
822 MonoRuntimeGenericContextTemplate *template;
823 MonoRuntimeGenericContextInfoTemplate *oti;
825 template = mono_class_get_runtime_generic_context_template (class);
826 oti = rgctx_template_get_other_slot (template, type_argc, slot);
837 class_type_info (MonoDomain *domain, MonoClass *class, MonoRgctxInfoType info_type)
840 case MONO_RGCTX_INFO_STATIC_DATA: {
841 MonoVTable *vtable = mono_class_vtable (domain, class);
843 mono_raise_exception (mono_class_get_exception_for_failure (class));
844 return mono_vtable_get_static_field_data (vtable);
846 case MONO_RGCTX_INFO_KLASS:
848 case MONO_RGCTX_INFO_VTABLE: {
849 MonoVTable *vtable = mono_class_vtable (domain, class);
851 mono_raise_exception (mono_class_get_exception_for_failure (class));
854 case MONO_RGCTX_INFO_CAST_CACHE: {
855 /*First slot is the cache itself, the second the vtable.*/
856 gpointer **cache_data = mono_domain_alloc0 (domain, sizeof (gpointer) * 2);
857 cache_data [1] = (gpointer)class;
860 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
861 return GUINT_TO_POINTER (mono_class_array_element_size (class));
862 case MONO_RGCTX_INFO_VALUE_SIZE:
863 if (MONO_TYPE_IS_REFERENCE (&class->byval_arg))
864 return GUINT_TO_POINTER (sizeof (gpointer));
866 return GUINT_TO_POINTER (mono_class_value_size (class, NULL));
867 case MONO_RGCTX_INFO_CLASS_IS_REF:
868 if (MONO_TYPE_IS_REFERENCE (&class->byval_arg))
869 return GUINT_TO_POINTER (1);
871 return GUINT_TO_POINTER (0);
873 g_assert_not_reached ();
880 ji_is_gsharedvt (MonoJitInfo *ji)
882 if (ji && ji->has_generic_jit_info && (mono_jit_info_get_generic_sharing_context (ji)->var_is_vt ||
883 mono_jit_info_get_generic_sharing_context (ji)->mvar_is_vt))
890 instantiate_info (MonoDomain *domain, MonoRuntimeGenericContextInfoTemplate *oti,
891 MonoGenericContext *context, MonoClass *class, guint8 *caller)
899 switch (oti->info_type) {
900 case MONO_RGCTX_INFO_STATIC_DATA:
901 case MONO_RGCTX_INFO_KLASS:
902 case MONO_RGCTX_INFO_VTABLE:
903 case MONO_RGCTX_INFO_CAST_CACHE:
910 data = inflate_info (oti, context, class, temporary);
912 switch (oti->info_type) {
913 case MONO_RGCTX_INFO_STATIC_DATA:
914 case MONO_RGCTX_INFO_KLASS:
915 case MONO_RGCTX_INFO_VTABLE:
916 case MONO_RGCTX_INFO_CAST_CACHE:
917 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
918 case MONO_RGCTX_INFO_VALUE_SIZE:
919 case MONO_RGCTX_INFO_CLASS_IS_REF: {
920 MonoClass *arg_class = mono_class_from_mono_type (data);
922 free_inflated_info (oti->info_type, data);
923 g_assert (arg_class);
925 /* The class might be used as an argument to
926 mono_value_copy(), which requires that its GC
927 descriptor has been computed. */
928 if (oti->info_type == MONO_RGCTX_INFO_KLASS)
929 mono_class_compute_gc_descriptor (arg_class);
931 return class_type_info (domain, arg_class, oti->info_type);
933 case MONO_RGCTX_INFO_TYPE:
935 case MONO_RGCTX_INFO_REFLECTION_TYPE:
936 return mono_type_get_object (domain, data);
937 case MONO_RGCTX_INFO_METHOD:
939 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
941 * We can't create a jump trampoline here, as it cannot be patched.
943 return mono_compile_method (data);
944 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
945 return mono_compile_method (mono_marshal_get_remoting_invoke_with_check (data));
946 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE:
947 return mono_domain_alloc0 (domain, sizeof (gpointer));
948 case MONO_RGCTX_INFO_CLASS_FIELD:
950 case MONO_RGCTX_INFO_FIELD_OFFSET: {
951 MonoClassField *field = data;
953 if (field->parent->valuetype && !(field->type->attrs & FIELD_ATTRIBUTE_STATIC))
954 return GUINT_TO_POINTER (field->offset - sizeof (MonoObject));
956 return GUINT_TO_POINTER (field->offset);
958 case MONO_RGCTX_INFO_METHOD_RGCTX: {
959 MonoMethodInflated *method = data;
962 g_assert (method->method.method.is_inflated);
963 g_assert (method->context.method_inst);
965 vtable = mono_class_vtable (domain, method->method.method.klass);
967 mono_raise_exception (mono_class_get_exception_for_failure (method->method.method.klass));
969 return mono_method_lookup_rgctx (vtable, method->context.method_inst);
971 case MONO_RGCTX_INFO_METHOD_CONTEXT: {
972 MonoMethodInflated *method = data;
974 g_assert (method->method.method.is_inflated);
975 g_assert (method->context.method_inst);
977 return method->context.method_inst;
979 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE:
980 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT: {
981 MonoMethod *caller_method = oti->data;
982 MonoMethod *method = data;
984 MonoJitInfo *caller_ji, *ji;
985 gboolean virtual = oti->info_type == MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT;
987 MonoGenericJitInfo *gji;
989 g_assert (method->is_inflated);
992 addr = mono_compile_method (method);
997 /* Same as in mono_emit_method_call_full () */
998 #ifndef MONO_ARCH_HAVE_IMT
1001 if (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
1002 guint32 imt_slot = mono_method_get_imt_slot (method);
1003 vcall_offset = ((gint32)imt_slot - MONO_IMT_SIZE) * SIZEOF_VOID_P;
1005 vcall_offset = G_STRUCT_OFFSET (MonoVTable, vtable) +
1006 ((mono_method_get_vtable_index (method)) * (SIZEOF_VOID_P));
1013 caller_ji = mini_jit_info_table_find (mono_domain_get (), mono_get_addr_from_ftnptr (caller), NULL);
1014 g_assert (caller_ji);
1015 gji = mono_jit_info_get_generic_jit_info (caller_ji);
1019 * For gsharedvt calls made out of gsharedvt methods, the callee could end up being a gsharedvt method, or a normal
1020 * non-shared method. The latter call cannot be patched, so instead of using a normal call, we make an indirect
1021 * call through the rgctx, in effect patching the rgctx entry instead of the call site.
1022 * For virtual calls, the caller might be a normal or a gsharedvt method. Since there is only one vtable slot,
1023 * this difference needs to be handed on the caller side. This is currently implemented by adding a gsharedvt-in
1024 * trampoline to all gsharedvt methods and storing this trampoline into the vtable slot. Virtual calls made from
1025 * gsharedvt methods always go through a gsharedvt-out trampoline, so the calling sequence is:
1026 * caller -> out trampoline -> in trampoline -> callee
1027 * This is not very efficient, but it is easy to implement.
1029 // FIXME: This loads information from AOT
1030 ji = mini_jit_info_table_find (mono_domain_get (), mono_get_addr_from_ftnptr (addr), NULL);
1031 if (virtual || !ji_is_gsharedvt (ji)) {
1032 static gpointer tramp_addr;
1034 MonoMethod *wrapper;
1036 MonoMethodSignature *sig, *gsig;
1038 g_assert (method->is_inflated);
1040 /* Have to pass TRUE for is_gshared since METHOD might not be gsharedvt but we need its shared version */
1041 gm = mini_get_shared_method_full (method, FALSE, TRUE);
1042 g_assert (gm != method);
1046 sig = mono_method_signature (method);
1047 gsig = mono_method_signature (gm);
1049 info = mono_arch_get_gsharedvt_call_info (addr, sig, gsig, gji->generic_sharing_context, FALSE, vcall_offset);
1052 wrapper = mono_marshal_get_gsharedvt_out_wrapper ();
1053 addr = mono_compile_method (wrapper);
1054 mono_memory_barrier ();
1060 addr = mono_aot_get_gsharedvt_arg_trampoline (info, addr);
1062 addr = mono_arch_get_gsharedvt_arg_trampoline (mono_domain_get (), info, addr);
1066 printf ("OUT-VCALL: %s\n", mono_method_full_name (method, TRUE));
1068 printf ("OUT: %s\n", mono_method_full_name (method, TRUE));
1070 } else if (!mini_is_gsharedvt_variable_signature (mono_method_signature (caller_method)) && ji_is_gsharedvt (ji)) {
1071 /* This is the IN case handled by mini_add_method_trampoline () */
1072 static gpointer tramp_addr;
1074 MonoMethod *wrapper;
1075 MonoMethodSignature *sig, *gsig;
1079 // This is a combination of the normal in and out cases, since the caller is a gsharedvt method.
1080 // A trampoline is not always needed, but its hard to determine when it can be omitted.
1083 // FIXME: This is just a workaround
1084 if (caller_method == method) {
1086 sig = mono_method_signature (method);
1087 gsig = mono_method_signature (ji->method);
1089 info = mono_arch_get_gsharedvt_call_info (ji->code_start, sig, gsig, gji->generic_sharing_context, TRUE, -1);
1092 wrapper = mono_marshal_get_gsharedvt_in_wrapper ();
1093 addr = mono_compile_method (wrapper);
1094 mono_memory_barrier ();
1100 addr = mono_aot_get_gsharedvt_arg_trampoline (info, addr);
1102 addr = mono_arch_get_gsharedvt_arg_trampoline (mono_domain_get (), info, addr);
1104 //printf ("IN-RGCTX: %s\n", mono_method_full_name (method, TRUE));
1111 g_assert_not_reached ();
1118 * LOCKING: loader lock
1121 fill_in_rgctx_template_slot (MonoClass *class, int type_argc, int index, gpointer data, MonoRgctxInfoType info_type)
1123 MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
1124 MonoClass *subclass;
1126 rgctx_template_set_slot (class->image, template, type_argc, index, data, info_type);
1128 /* Recurse for all subclasses */
1129 if (generic_subclass_hash)
1130 subclass = g_hash_table_lookup (generic_subclass_hash, class);
1135 MonoRuntimeGenericContextInfoTemplate subclass_oti;
1136 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
1138 g_assert (subclass_template);
1140 subclass_oti = class_get_rgctx_template_oti (subclass->parent, type_argc, index, FALSE, FALSE, NULL);
1141 g_assert (subclass_oti.data);
1143 fill_in_rgctx_template_slot (subclass, type_argc, index, subclass_oti.data, info_type);
1145 subclass = subclass_template->next_subclass;
1150 mono_rgctx_info_type_to_str (MonoRgctxInfoType type)
1153 case MONO_RGCTX_INFO_STATIC_DATA: return "STATIC_DATA";
1154 case MONO_RGCTX_INFO_KLASS: return "KLASS";
1155 case MONO_RGCTX_INFO_VTABLE: return "VTABLE";
1156 case MONO_RGCTX_INFO_TYPE: return "TYPE";
1157 case MONO_RGCTX_INFO_REFLECTION_TYPE: return "REFLECTION_TYPE";
1158 case MONO_RGCTX_INFO_METHOD: return "METHOD";
1159 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE: return "GENERIC_METHOD_CODE";
1160 case MONO_RGCTX_INFO_CLASS_FIELD: return "CLASS_FIELD";
1161 case MONO_RGCTX_INFO_METHOD_RGCTX: return "METHOD_RGCTX";
1162 case MONO_RGCTX_INFO_METHOD_CONTEXT: return "METHOD_CONTEXT";
1163 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK: return "REMOTING_INVOKE_WITH_CHECK";
1164 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE: return "METHOD_DELEGATE_CODE";
1165 case MONO_RGCTX_INFO_CAST_CACHE: return "CAST_CACHE";
1166 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE: return "ARRAY_ELEMENT_SIZE";
1167 case MONO_RGCTX_INFO_VALUE_SIZE: return "VALUE_SIZE";
1168 case MONO_RGCTX_INFO_CLASS_IS_REF: return "CLASS_IS_REF";
1169 case MONO_RGCTX_INFO_FIELD_OFFSET: return "FIELD_OFFSET";
1170 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE: return "METHOD_GSHAREDVT_OUT_TRAMPOLINE";
1171 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT: return "METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT";
1173 return "<UNKNOWN RGCTX INFO TYPE>";
1177 G_GNUC_UNUSED static char*
1178 rgctx_info_to_str (MonoRgctxInfoType info_type, gpointer data)
1180 switch (info_type) {
1181 case MONO_RGCTX_INFO_VTABLE:
1182 return mono_type_full_name ((MonoType*)data);
1184 return g_strdup_printf ("<%p>", data);
1189 * LOCKING: loader lock
1192 register_info (MonoClass *class, int type_argc, gpointer data, MonoRgctxInfoType info_type)
1195 MonoRuntimeGenericContextTemplate *template = mono_class_get_runtime_generic_context_template (class);
1197 MonoRuntimeGenericContextInfoTemplate *oti;
1199 for (i = 0, oti = get_info_templates (template, type_argc); oti; ++i, oti = oti->next) {
1204 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)));
1206 /* Mark the slot as used in all parent classes (until we find
1207 a parent class which already has it marked used). */
1208 parent = class->parent;
1209 while (parent != NULL) {
1210 MonoRuntimeGenericContextTemplate *parent_template;
1211 MonoRuntimeGenericContextInfoTemplate *oti;
1213 if (parent->generic_class)
1214 parent = parent->generic_class->container_class;
1216 parent_template = mono_class_get_runtime_generic_context_template (parent);
1217 oti = rgctx_template_get_other_slot (parent_template, type_argc, i);
1219 if (oti && oti->data)
1222 rgctx_template_set_slot (parent->image, parent_template, type_argc, i,
1223 MONO_RGCTX_SLOT_USED_MARKER, 0);
1225 parent = parent->parent;
1228 /* Fill in the slot in this class and in all subclasses
1230 fill_in_rgctx_template_slot (class, type_argc, i, data, info_type);
1236 info_equal (gpointer data1, gpointer data2, MonoRgctxInfoType info_type)
1238 switch (info_type) {
1239 case MONO_RGCTX_INFO_STATIC_DATA:
1240 case MONO_RGCTX_INFO_KLASS:
1241 case MONO_RGCTX_INFO_VTABLE:
1242 case MONO_RGCTX_INFO_TYPE:
1243 case MONO_RGCTX_INFO_REFLECTION_TYPE:
1244 case MONO_RGCTX_INFO_CAST_CACHE:
1245 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
1246 case MONO_RGCTX_INFO_VALUE_SIZE:
1247 case MONO_RGCTX_INFO_CLASS_IS_REF:
1248 return mono_class_from_mono_type (data1) == mono_class_from_mono_type (data2);
1249 case MONO_RGCTX_INFO_METHOD:
1250 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
1251 case MONO_RGCTX_INFO_CLASS_FIELD:
1252 case MONO_RGCTX_INFO_FIELD_OFFSET:
1253 case MONO_RGCTX_INFO_METHOD_RGCTX:
1254 case MONO_RGCTX_INFO_METHOD_CONTEXT:
1255 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
1256 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE:
1257 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE:
1258 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT:
1259 return data1 == data2;
1261 g_assert_not_reached ();
1268 lookup_or_register_info (MonoClass *class, int type_argc, gpointer data, MonoRgctxInfoType info_type,
1269 MonoGenericContext *generic_context)
1271 static gboolean inited = FALSE;
1272 static int max_slot = 0;
1274 MonoRuntimeGenericContextTemplate *rgctx_template =
1275 mono_class_get_runtime_generic_context_template (class);
1276 MonoRuntimeGenericContextInfoTemplate *oti_list, *oti;
1279 class = get_shared_class (class);
1281 mono_loader_lock ();
1283 if (info_has_identity (info_type)) {
1284 oti_list = get_info_templates (rgctx_template, type_argc);
1286 for (oti = oti_list, i = 0; oti; oti = oti->next, ++i) {
1287 gpointer inflated_data;
1289 if (oti->info_type != info_type || !oti->data)
1292 inflated_data = inflate_info (oti, generic_context, class, TRUE);
1294 if (info_equal (data, inflated_data, info_type)) {
1295 free_inflated_info (info_type, inflated_data);
1296 mono_loader_unlock ();
1299 free_inflated_info (info_type, inflated_data);
1303 /* We haven't found the info */
1304 i = register_info (class, type_argc, data, info_type);
1306 mono_loader_unlock ();
1309 mono_counters_register ("RGCTX max slot number", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &max_slot);
1319 * mono_method_lookup_or_register_info:
1321 * @in_mrgctx: whether to put the data into the MRGCTX
1322 * @data: the info data
1323 * @info_type: the type of info to register about data
1324 * @generic_context: a generic context
1326 * Looks up and, if necessary, adds information about data/info_type in
1327 * method's or method's class runtime generic context. Returns the
1328 * encoded slot number.
1331 mono_method_lookup_or_register_info (MonoMethod *method, gboolean in_mrgctx, gpointer data,
1332 MonoRgctxInfoType info_type, MonoGenericContext *generic_context)
1334 MonoClass *class = method->klass;
1335 int type_argc, index;
1338 MonoGenericInst *method_inst = mono_method_get_context (method)->method_inst;
1340 g_assert (method->is_inflated && method_inst);
1341 type_argc = method_inst->type_argc;
1342 g_assert (type_argc > 0);
1347 index = lookup_or_register_info (class, type_argc, data, info_type, generic_context);
1349 //g_print ("rgctx item at index %d argc %d\n", index, type_argc);
1352 return MONO_RGCTX_SLOT_MAKE_MRGCTX (index);
1354 return MONO_RGCTX_SLOT_MAKE_RGCTX (index);
1358 * mono_class_rgctx_get_array_size:
1359 * @n: The number of the array
1360 * @mrgctx: Whether it's an MRGCTX as opposed to a RGCTX.
1362 * Returns the number of slots in the n'th array of a (M)RGCTX. That
1363 * number includes the slot for linking and - for MRGCTXs - the two
1364 * slots in the first array for additional information.
1367 mono_class_rgctx_get_array_size (int n, gboolean mrgctx)
1369 g_assert (n >= 0 && n < 30);
1378 * LOCKING: domain lock
1381 alloc_rgctx_array (MonoDomain *domain, int n, gboolean is_mrgctx)
1383 static gboolean inited = FALSE;
1384 static int rgctx_num_alloced = 0;
1385 static int rgctx_bytes_alloced = 0;
1386 static int mrgctx_num_alloced = 0;
1387 static int mrgctx_bytes_alloced = 0;
1389 int size = mono_class_rgctx_get_array_size (n, is_mrgctx) * sizeof (gpointer);
1390 gpointer array = mono_domain_alloc0 (domain, size);
1393 mono_counters_register ("RGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_num_alloced);
1394 mono_counters_register ("RGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_bytes_alloced);
1395 mono_counters_register ("MRGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_num_alloced);
1396 mono_counters_register ("MRGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_bytes_alloced);
1401 mrgctx_num_alloced++;
1402 mrgctx_bytes_alloced += size;
1404 rgctx_num_alloced++;
1405 rgctx_bytes_alloced += size;
1412 fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContext *rgctx, guint8 *caller, guint32 slot,
1413 MonoGenericInst *method_inst)
1416 int i, first_slot, size;
1417 MonoDomain *domain = class_vtable->domain;
1418 MonoClass *class = class_vtable->klass;
1419 MonoGenericContext *class_context = class->generic_class ? &class->generic_class->context : NULL;
1420 MonoRuntimeGenericContextInfoTemplate oti;
1421 MonoGenericContext context = { class_context ? class_context->class_inst : NULL, method_inst };
1427 mono_domain_lock (domain);
1429 /* First check whether that slot isn't already instantiated.
1430 This might happen because lookup doesn't lock. Allocate
1431 arrays on the way. */
1433 size = mono_class_rgctx_get_array_size (0, method_inst != NULL);
1435 size -= MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
1436 for (i = 0; ; ++i) {
1439 if (method_inst && i == 0)
1440 offset = MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
1444 if (slot < first_slot + size - 1) {
1445 rgctx_index = slot - first_slot + 1 + offset;
1446 info = rgctx [rgctx_index];
1448 mono_domain_unlock (domain);
1453 if (!rgctx [offset + 0])
1454 rgctx [offset + 0] = alloc_rgctx_array (domain, i + 1, method_inst != NULL);
1455 rgctx = rgctx [offset + 0];
1456 first_slot += size - 1;
1457 size = mono_class_rgctx_get_array_size (i + 1, method_inst != NULL);
1460 g_assert (!rgctx [rgctx_index]);
1462 mono_domain_unlock (domain);
1464 oti = class_get_rgctx_template_oti (get_shared_class (class),
1465 method_inst ? method_inst->type_argc : 0, slot, TRUE, TRUE, &do_free);
1466 /* This might take the loader lock */
1467 info = instantiate_info (domain, &oti, &context, class, caller);
1471 g_print ("filling mrgctx slot %d table %d index %d\n", slot, i, rgctx_index);
1474 /*FIXME We should use CAS here, no need to take a lock.*/
1475 mono_domain_lock (domain);
1477 /* Check whether the slot hasn't been instantiated in the
1479 if (rgctx [rgctx_index])
1480 info = rgctx [rgctx_index];
1482 rgctx [rgctx_index] = info;
1484 mono_domain_unlock (domain);
1487 free_inflated_info (oti.info_type, oti.data);
1493 * mono_class_fill_runtime_generic_context:
1494 * @class_vtable: a vtable
1495 * @caller: caller method address
1496 * @slot: a slot index to be instantiated
1498 * Instantiates a slot in the RGCTX, returning its value.
1501 mono_class_fill_runtime_generic_context (MonoVTable *class_vtable, guint8 *caller, guint32 slot)
1503 static gboolean inited = FALSE;
1504 static int num_alloced = 0;
1506 MonoDomain *domain = class_vtable->domain;
1507 MonoRuntimeGenericContext *rgctx;
1510 mono_domain_lock (domain);
1513 mono_counters_register ("RGCTX num alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_alloced);
1517 rgctx = class_vtable->runtime_generic_context;
1519 rgctx = alloc_rgctx_array (domain, 0, FALSE);
1520 class_vtable->runtime_generic_context = rgctx;
1524 mono_domain_unlock (domain);
1526 info = fill_runtime_generic_context (class_vtable, rgctx, caller, slot, 0);
1528 DEBUG (printf ("get rgctx slot: %s %d -> %p\n", mono_type_full_name (&class_vtable->klass->byval_arg), slot, info));
1534 * mono_method_fill_runtime_generic_context:
1535 * @mrgctx: an MRGCTX
1536 * @caller: caller method address
1537 * @slot: a slot index to be instantiated
1539 * Instantiates a slot in the MRGCTX.
1542 mono_method_fill_runtime_generic_context (MonoMethodRuntimeGenericContext *mrgctx, guint8* caller, guint32 slot)
1546 info = fill_runtime_generic_context (mrgctx->class_vtable, (MonoRuntimeGenericContext*)mrgctx, caller, slot,
1547 mrgctx->method_inst);
1553 mrgctx_hash_func (gconstpointer key)
1555 const MonoMethodRuntimeGenericContext *mrgctx = key;
1557 return mono_aligned_addr_hash (mrgctx->class_vtable) ^ mono_metadata_generic_inst_hash (mrgctx->method_inst);
1561 mrgctx_equal_func (gconstpointer a, gconstpointer b)
1563 const MonoMethodRuntimeGenericContext *mrgctx1 = a;
1564 const MonoMethodRuntimeGenericContext *mrgctx2 = b;
1566 return mrgctx1->class_vtable == mrgctx2->class_vtable &&
1567 mono_metadata_generic_inst_equal (mrgctx1->method_inst, mrgctx2->method_inst);
1571 * mono_method_lookup_rgctx:
1572 * @class_vtable: a vtable
1573 * @method_inst: the method inst of a generic method
1575 * Returns the MRGCTX for the generic method(s) with the given
1576 * method_inst of the given class_vtable.
1578 * LOCKING: Take the domain lock.
1580 MonoMethodRuntimeGenericContext*
1581 mono_method_lookup_rgctx (MonoVTable *class_vtable, MonoGenericInst *method_inst)
1583 MonoDomain *domain = class_vtable->domain;
1584 MonoMethodRuntimeGenericContext *mrgctx;
1585 MonoMethodRuntimeGenericContext key;
1587 g_assert (!class_vtable->klass->generic_container);
1588 g_assert (!method_inst->is_open);
1590 mono_domain_lock (domain);
1591 if (!domain->method_rgctx_hash)
1592 domain->method_rgctx_hash = g_hash_table_new (mrgctx_hash_func, mrgctx_equal_func);
1594 key.class_vtable = class_vtable;
1595 key.method_inst = method_inst;
1597 mrgctx = g_hash_table_lookup (domain->method_rgctx_hash, &key);
1602 mrgctx = (MonoMethodRuntimeGenericContext*)alloc_rgctx_array (domain, 0, TRUE);
1603 mrgctx->class_vtable = class_vtable;
1604 mrgctx->method_inst = method_inst;
1606 g_hash_table_insert (domain->method_rgctx_hash, mrgctx, mrgctx);
1609 g_print ("mrgctx alloced for %s <", mono_type_get_full_name (class_vtable->klass));
1610 for (i = 0; i < method_inst->type_argc; ++i)
1611 g_print ("%s, ", mono_type_full_name (method_inst->type_argv [i]));
1616 mono_domain_unlock (domain);
1624 * mono_generic_context_is_sharable_full:
1625 * @context: a generic context
1627 * Returns whether the generic context is sharable. A generic context
1628 * is sharable iff all of its type arguments are reference type, or some of them have a
1629 * reference type, and ALLOW_PARTIAL is TRUE.
1632 mono_generic_context_is_sharable_full (MonoGenericContext *context,
1633 gboolean allow_type_vars,
1634 gboolean allow_partial)
1636 g_assert (context->class_inst || context->method_inst);
1638 if (context->class_inst && !generic_inst_is_sharable (context->class_inst, allow_type_vars, allow_partial))
1641 if (context->method_inst && !generic_inst_is_sharable (context->method_inst, allow_type_vars, allow_partial))
1648 mono_generic_context_is_sharable (MonoGenericContext *context, gboolean allow_type_vars)
1650 return mono_generic_context_is_sharable_full (context, allow_type_vars, ALLOW_PARTIAL_SHARING);
1654 * mono_method_is_generic_impl:
1657 * Returns whether the method is either generic or part of a generic
1661 mono_method_is_generic_impl (MonoMethod *method)
1663 if (method->is_inflated)
1665 /* We don't treat wrappers as generic code, i.e., we never
1666 apply generic sharing to them. This is especially
1667 important for static rgctx invoke wrappers, which only work
1668 if not compiled with sharing. */
1669 if (method->wrapper_type != MONO_WRAPPER_NONE)
1671 if (method->klass->generic_container)
1677 has_constraints (MonoGenericContainer *container)
1683 g_assert (container->type_argc > 0);
1684 g_assert (container->type_params);
1686 for (i = 0; i < container->type_argc; ++i)
1687 if (container->type_params [i].constraints)
1694 * mono_method_is_generic_sharable_impl_full:
1696 * @allow_type_vars: whether to regard type variables as reference types
1697 * @allow_partial: whether to allow partial sharing
1698 * @allow_gsharedvt: whenever to allow sharing over valuetypes
1700 * Returns TRUE iff the method is inflated or part of an inflated
1701 * class, its context is sharable and it has no constraints on its
1702 * type parameters. Otherwise returns FALSE.
1705 mono_method_is_generic_sharable_impl_full (MonoMethod *method, gboolean allow_type_vars,
1706 gboolean allow_partial, gboolean allow_gsharedvt)
1708 if (!mono_method_is_generic_impl (method))
1711 if (allow_gsharedvt && mini_is_gsharedvt_sharable_method (method))
1714 if (method->is_inflated) {
1715 MonoMethodInflated *inflated = (MonoMethodInflated*)method;
1716 MonoGenericContext *context = &inflated->context;
1718 if (!mono_generic_context_is_sharable_full (context, allow_type_vars, allow_partial))
1721 g_assert (inflated->declaring);
1723 if (inflated->declaring->is_generic) {
1724 if (has_constraints (mono_method_get_generic_container (inflated->declaring)))
1729 if (method->klass->generic_class) {
1730 if (!mono_generic_context_is_sharable_full (&method->klass->generic_class->context, allow_type_vars, allow_partial))
1733 g_assert (method->klass->generic_class->container_class &&
1734 method->klass->generic_class->container_class->generic_container);
1736 if (has_constraints (method->klass->generic_class->container_class->generic_container))
1740 if (method->klass->generic_container && !allow_type_vars)
1747 mono_method_is_generic_sharable_impl (MonoMethod *method, gboolean allow_type_vars)
1749 return mono_method_is_generic_sharable_impl_full (method, allow_type_vars, ALLOW_PARTIAL_SHARING, TRUE);
1753 mono_method_needs_static_rgctx_invoke (MonoMethod *method, gboolean allow_type_vars)
1755 if (!mono_class_generic_sharing_enabled (method->klass))
1758 if (!mono_method_is_generic_sharable_impl (method, allow_type_vars))
1761 if (method->is_inflated && mono_method_get_context (method)->method_inst)
1764 return ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
1765 method->klass->valuetype) &&
1766 (method->klass->generic_class || method->klass->generic_container);
1769 static MonoGenericInst*
1770 get_object_generic_inst (int type_argc)
1772 MonoType **type_argv;
1775 type_argv = alloca (sizeof (MonoType*) * type_argc);
1777 for (i = 0; i < type_argc; ++i)
1778 type_argv [i] = &mono_defaults.object_class->byval_arg;
1780 return mono_metadata_get_generic_inst (type_argc, type_argv);
1784 * mono_method_construct_object_context:
1787 * Returns a generic context for method with all type variables for
1788 * class and method instantiated with Object.
1791 mono_method_construct_object_context (MonoMethod *method)
1793 MonoGenericContext object_context;
1795 g_assert (!method->klass->generic_class);
1796 if (method->klass->generic_container) {
1797 int type_argc = method->klass->generic_container->type_argc;
1799 object_context.class_inst = get_object_generic_inst (type_argc);
1801 object_context.class_inst = NULL;
1804 if (mono_method_get_context_general (method, TRUE)->method_inst) {
1805 int type_argc = mono_method_get_context_general (method, TRUE)->method_inst->type_argc;
1807 object_context.method_inst = get_object_generic_inst (type_argc);
1809 object_context.method_inst = NULL;
1812 g_assert (object_context.class_inst || object_context.method_inst);
1814 return object_context;
1817 static gboolean gshared_supported;
1818 static gboolean gsharedvt_supported;
1821 mono_set_generic_sharing_supported (gboolean supported)
1823 gshared_supported = supported;
1827 mono_set_generic_sharing_vt_supported (gboolean supported)
1829 gsharedvt_supported = supported;
1833 * mono_class_generic_sharing_enabled:
1836 * Returns whether generic sharing is enabled for class.
1838 * This is a stop-gap measure to slowly introduce generic sharing
1839 * until we have all the issues sorted out, at which time this
1840 * function will disappear and generic sharing will always be enabled.
1843 mono_class_generic_sharing_enabled (MonoClass *class)
1845 static int generic_sharing = MONO_GENERIC_SHARING_NONE;
1846 static gboolean inited = FALSE;
1851 if (gshared_supported)
1852 generic_sharing = MONO_GENERIC_SHARING_ALL;
1854 generic_sharing = MONO_GENERIC_SHARING_NONE;
1856 if ((option = g_getenv ("MONO_GENERIC_SHARING"))) {
1857 if (strcmp (option, "corlib") == 0)
1858 generic_sharing = MONO_GENERIC_SHARING_CORLIB;
1859 else if (strcmp (option, "collections") == 0)
1860 generic_sharing = MONO_GENERIC_SHARING_COLLECTIONS;
1861 else if (strcmp (option, "all") == 0)
1862 generic_sharing = MONO_GENERIC_SHARING_ALL;
1863 else if (strcmp (option, "none") == 0)
1864 generic_sharing = MONO_GENERIC_SHARING_NONE;
1866 g_warning ("Unknown generic sharing option `%s'.", option);
1869 if (!gshared_supported)
1870 generic_sharing = MONO_GENERIC_SHARING_NONE;
1875 switch (generic_sharing) {
1876 case MONO_GENERIC_SHARING_NONE:
1878 case MONO_GENERIC_SHARING_ALL:
1880 case MONO_GENERIC_SHARING_CORLIB :
1881 return class->image == mono_defaults.corlib;
1882 case MONO_GENERIC_SHARING_COLLECTIONS:
1883 if (class->image != mono_defaults.corlib)
1885 while (class->nested_in)
1886 class = class->nested_in;
1887 return g_str_has_prefix (class->name_space, "System.Collections.Generic");
1889 g_assert_not_reached ();
1895 * mono_get_generic_context_from_code:
1897 * Return the runtime generic context belonging to the method whose native code
1900 MonoGenericSharingContext*
1901 mono_get_generic_context_from_code (guint8 *code)
1903 MonoJitInfo *jit_info = mini_jit_info_table_find (mono_domain_get (), (char*)code, NULL);
1905 g_assert (jit_info);
1907 return mono_jit_info_get_generic_sharing_context (jit_info);
1911 mini_method_get_context (MonoMethod *method)
1913 return mono_method_get_context_general (method, TRUE);
1917 * mono_method_check_context_used:
1920 * Checks whether the method's generic context uses a type variable.
1921 * Returns an int with the bits MONO_GENERIC_CONTEXT_USED_CLASS and
1922 * MONO_GENERIC_CONTEXT_USED_METHOD set to reflect whether the
1923 * context's class or method instantiation uses type variables.
1926 mono_method_check_context_used (MonoMethod *method)
1928 MonoGenericContext *method_context = mini_method_get_context (method);
1929 int context_used = 0;
1931 if (!method_context) {
1932 /* It might be a method of an array of an open generic type */
1933 if (method->klass->rank)
1934 context_used = mono_class_check_context_used (method->klass);
1936 context_used = mono_generic_context_check_used (method_context);
1937 context_used |= mono_class_check_context_used (method->klass);
1940 return context_used;
1944 generic_inst_equal (MonoGenericInst *inst1, MonoGenericInst *inst2)
1955 if (inst1->type_argc != inst2->type_argc)
1958 for (i = 0; i < inst1->type_argc; ++i)
1959 if (!mono_metadata_type_equal (inst1->type_argv [i], inst2->type_argv [i]))
1966 * mono_generic_context_equal_deep:
1967 * @context1: a generic context
1968 * @context2: a generic context
1970 * Returns whether context1's type arguments are equal to context2's
1974 mono_generic_context_equal_deep (MonoGenericContext *context1, MonoGenericContext *context2)
1976 return generic_inst_equal (context1->class_inst, context2->class_inst) &&
1977 generic_inst_equal (context1->method_inst, context2->method_inst);
1981 * mini_class_get_container_class:
1982 * @class: a generic class
1984 * Returns the class's container class, which is the class itself if
1985 * it doesn't have generic_class set.
1988 mini_class_get_container_class (MonoClass *class)
1990 if (class->generic_class)
1991 return class->generic_class->container_class;
1993 g_assert (class->generic_container);
1998 * mini_class_get_context:
1999 * @class: a generic class
2001 * Returns the class's generic context.
2004 mini_class_get_context (MonoClass *class)
2006 if (class->generic_class)
2007 return &class->generic_class->context;
2009 g_assert (class->generic_container);
2010 return &class->generic_container->context;
2014 * mini_get_basic_type_from_generic:
2015 * @gsctx: a generic sharing context
2018 * Returns a closed type corresponding to the possibly open type
2022 mini_get_basic_type_from_generic (MonoGenericSharingContext *gsctx, MonoType *type)
2024 /* FIXME: Some callers don't pass in a gsctx, like mono_dyn_call_prepare () */
2026 if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR))
2029 if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) && mini_is_gsharedvt_type_gsctx (gsctx, type))
2030 return mini_get_gsharedvt_alloc_type_gsctx (gsctx, type);
2032 return mono_type_get_basic_type_from_generic (type);
2036 * mini_type_get_underlying_type:
2038 * Return the underlying type of TYPE, taking into account enums, byref and generic
2042 mini_type_get_underlying_type (MonoGenericSharingContext *gsctx, MonoType *type)
2045 return &mono_defaults.int_class->byval_arg;
2046 return mini_get_basic_type_from_generic (gsctx, mono_type_get_underlying_type (type));
2050 * mini_type_stack_size:
2051 * @gsctx: a generic sharing context
2053 * @align: Pointer to an int for returning the alignment
2055 * Returns the type's stack size and the alignment in *align. The
2056 * type is allowed to be open.
2059 mini_type_stack_size (MonoGenericSharingContext *gsctx, MonoType *t, int *align)
2061 gboolean allow_open = TRUE;
2063 // FIXME: Some callers might not pass in a gsctx
2064 //allow_open = gsctx != NULL;
2065 return mono_type_stack_size_internal (t, align, allow_open);
2069 * mini_type_stack_size_full:
2071 * Same as mini_type_stack_size, but handle gsharedvt and pinvoke data types as well.
2074 mini_type_stack_size_full (MonoGenericSharingContext *gsctx, MonoType *t, guint32 *align, gboolean pinvoke)
2079 if (t->type == MONO_TYPE_VAR || t->type == MONO_TYPE_MVAR)
2083 if (mini_is_gsharedvt_type_gsctx (gsctx, t))
2084 t = mini_get_gsharedvt_alloc_type_gsctx (gsctx, t);
2087 size = mono_type_native_stack_size (t, align);
2092 size = mini_type_stack_size (gsctx, t, &ialign);
2095 size = mini_type_stack_size (gsctx, t, NULL);
2103 * mono_generic_sharing_init:
2105 * Register the generic sharing counters.
2108 mono_generic_sharing_init (void)
2110 mono_install_image_unload_hook (mono_class_unregister_image_generic_subclasses, NULL);
2114 mono_generic_sharing_cleanup (void)
2116 mono_remove_image_unload_hook (mono_class_unregister_image_generic_subclasses, NULL);
2118 if (generic_subclass_hash)
2119 g_hash_table_destroy (generic_subclass_hash);
2123 * mini_type_var_is_vt:
2125 * Return whenever T is a type variable instantiated with a vtype.
2128 mini_type_var_is_vt (MonoCompile *cfg, MonoType *type)
2130 if (type->type == MONO_TYPE_VAR) {
2131 if (cfg->generic_sharing_context->var_is_vt && cfg->generic_sharing_context->var_is_vt [type->data.generic_param->num])
2135 } else if (type->type == MONO_TYPE_MVAR) {
2136 if (cfg->generic_sharing_context->mvar_is_vt && cfg->generic_sharing_context->mvar_is_vt [type->data.generic_param->num])
2141 g_assert_not_reached ();
2147 mini_type_is_reference (MonoCompile *cfg, MonoType *type)
2149 if (mono_type_is_reference (type))
2151 if (!cfg->generic_sharing_context)
2153 /*FIXME the probably needs better handle under partial sharing*/
2154 return ((type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) && !mini_type_var_is_vt (cfg, type));
2158 * mini_method_get_rgctx:
2160 * Return the RGCTX which needs to be passed to M when it is called.
2163 mini_method_get_rgctx (MonoMethod *m)
2165 if (mini_method_get_context (m)->method_inst)
2166 return mono_method_lookup_rgctx (mono_class_vtable (mono_domain_get (), m->klass), mini_method_get_context (m)->method_inst);
2168 return mono_class_vtable (mono_domain_get (), m->klass);
2172 * mini_type_is_vtype:
2174 * Return whenever T is a vtype, or a type param instantiated with a vtype.
2175 * Should be used in place of MONO_TYPE_ISSTRUCT () which can't handle gsharedvt.
2178 mini_type_is_vtype (MonoCompile *cfg, MonoType *t)
2180 return MONO_TYPE_ISSTRUCT (t) || mini_is_gsharedvt_variable_type (cfg, t);
2183 #if defined(MONOTOUCH) || defined(MONO_EXTENSIONS)
2185 #include "../../../mono-extensions/mono/mini/mini-generic-sharing-gsharedvt.c"
2190 mini_is_gsharedvt_type_gsctx (MonoGenericSharingContext *gsctx, MonoType *t)
2196 mini_is_gsharedvt_type (MonoCompile *cfg, MonoType *t)
2202 mini_is_gsharedvt_klass (MonoCompile *cfg, MonoClass *klass)
2208 mini_is_gsharedvt_signature (MonoCompile *cfg, MonoMethodSignature *sig)
2214 mini_is_gsharedvt_variable_type (MonoCompile *cfg, MonoType *t)
2220 mini_get_gsharedvt_alloc_type_gsctx (MonoGenericSharingContext *gsctx, MonoType *t)
2226 mini_get_gsharedvt_alloc_type_for_type (MonoCompile *cfg, MonoType *t)
2232 mini_is_gsharedvt_sharable_method (MonoMethod *method)
2238 mini_is_gsharedvt_variable_signature (MonoMethodSignature *sig)
2243 #endif /* !MONOTOUCH */