[ci] Bump timeout in ms-test-suite
[mono-project.git] / mono / mini / mini-generic-sharing.c
blob563a3a5b5de5caa4e94c8cbc673b04cb1e61ac32
1 /*
2 * mini-generic-sharing.c: Support functions for generic sharing.
4 * Author:
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)
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
12 #include <config.h>
14 #include <mono/metadata/class.h>
15 #include <mono/metadata/method-builder.h>
16 #include <mono/metadata/reflection-internals.h>
17 #include <mono/utils/mono-counters.h>
19 #include "mini.h"
21 #define ALLOW_PARTIAL_SHARING TRUE
22 //#define ALLOW_PARTIAL_SHARING FALSE
24 #if 0
25 #define DEBUG(...) __VA_ARGS__
26 #else
27 #define DEBUG(...)
28 #endif
30 static void
31 mono_class_unregister_image_generic_subclasses (MonoImage *image, gpointer user_data);
33 /* Counters */
34 static int num_templates_allocted;
35 static int num_templates_bytes;
36 static int num_oti_allocted;
37 static int num_oti_bytes;
39 #define gshared_lock() mono_os_mutex_lock (&gshared_mutex)
40 #define gshared_unlock() mono_os_mutex_unlock (&gshared_mutex)
41 static mono_mutex_t gshared_mutex;
43 static gboolean partial_supported = FALSE;
45 static inline gboolean
46 partial_sharing_supported (void)
48 if (!ALLOW_PARTIAL_SHARING)
49 return FALSE;
50 /* Enable this when AOT compiling or running in full-aot mode */
51 if (mono_aot_only)
52 return TRUE;
53 if (partial_supported)
54 return TRUE;
55 return FALSE;
58 static int
59 type_check_context_used (MonoType *type, gboolean recursive)
61 switch (mono_type_get_type (type)) {
62 case MONO_TYPE_VAR:
63 return MONO_GENERIC_CONTEXT_USED_CLASS;
64 case MONO_TYPE_MVAR:
65 return MONO_GENERIC_CONTEXT_USED_METHOD;
66 case MONO_TYPE_SZARRAY:
67 return mono_class_check_context_used (mono_type_get_class (type));
68 case MONO_TYPE_ARRAY:
69 return mono_class_check_context_used (mono_type_get_array_type (type)->eklass);
70 case MONO_TYPE_CLASS:
71 if (recursive)
72 return mono_class_check_context_used (mono_type_get_class (type));
73 else
74 return 0;
75 case MONO_TYPE_GENERICINST:
76 if (recursive) {
77 MonoGenericClass *gclass = type->data.generic_class;
79 g_assert (mono_class_is_gtd (gclass->container_class));
80 return mono_generic_context_check_used (&gclass->context);
81 } else {
82 return 0;
84 default:
85 return 0;
89 static int
90 inst_check_context_used (MonoGenericInst *inst)
92 int context_used = 0;
93 int i;
95 if (!inst)
96 return 0;
98 for (i = 0; i < inst->type_argc; ++i)
99 context_used |= type_check_context_used (inst->type_argv [i], TRUE);
101 return context_used;
105 * mono_generic_context_check_used:
106 * @context: a generic context
108 * Checks whether the context uses a type variable. Returns an int
109 * with the bit MONO_GENERIC_CONTEXT_USED_CLASS set to reflect whether
110 * the context's class instantiation uses type variables.
113 mono_generic_context_check_used (MonoGenericContext *context)
115 int context_used = 0;
117 context_used |= inst_check_context_used (context->class_inst);
118 context_used |= inst_check_context_used (context->method_inst);
120 return context_used;
124 * mono_class_check_context_used:
125 * @class: a class
127 * Checks whether the class's generic context uses a type variable.
128 * Returns an int with the bit MONO_GENERIC_CONTEXT_USED_CLASS set to
129 * reflect whether the context's class instantiation uses type
130 * variables.
133 mono_class_check_context_used (MonoClass *klass)
135 int context_used = 0;
137 context_used |= type_check_context_used (&klass->this_arg, FALSE);
138 context_used |= type_check_context_used (&klass->byval_arg, FALSE);
140 if (mono_class_is_ginst (klass))
141 context_used |= mono_generic_context_check_used (&mono_class_get_generic_class (klass)->context);
142 else if (mono_class_is_gtd (klass))
143 context_used |= mono_generic_context_check_used (&mono_class_get_generic_container (klass)->context);
145 return context_used;
149 * LOCKING: loader lock
151 static MonoRuntimeGenericContextInfoTemplate*
152 get_info_templates (MonoRuntimeGenericContextTemplate *template_, int type_argc)
154 g_assert (type_argc >= 0);
155 if (type_argc == 0)
156 return template_->infos;
157 return (MonoRuntimeGenericContextInfoTemplate *)g_slist_nth_data (template_->method_templates, type_argc - 1);
161 * LOCKING: loader lock
163 static void
164 set_info_templates (MonoImage *image, MonoRuntimeGenericContextTemplate *template_, int type_argc,
165 MonoRuntimeGenericContextInfoTemplate *oti)
167 g_assert (type_argc >= 0);
168 if (type_argc == 0)
169 template_->infos = oti;
170 else {
171 int length = g_slist_length (template_->method_templates);
172 GSList *list;
174 /* FIXME: quadratic! */
175 while (length < type_argc) {
176 template_->method_templates = g_slist_append_image (image, template_->method_templates, NULL);
177 length++;
180 list = g_slist_nth (template_->method_templates, type_argc - 1);
181 g_assert (list);
182 list->data = oti;
187 * LOCKING: loader lock
189 static int
190 template_get_max_argc (MonoRuntimeGenericContextTemplate *template_)
192 return g_slist_length (template_->method_templates);
196 * LOCKING: loader lock
198 static MonoRuntimeGenericContextInfoTemplate*
199 rgctx_template_get_other_slot (MonoRuntimeGenericContextTemplate *template_, int type_argc, int slot)
201 int i;
202 MonoRuntimeGenericContextInfoTemplate *oti;
204 g_assert (slot >= 0);
206 for (oti = get_info_templates (template_, type_argc), i = 0; i < slot; oti = oti->next, ++i) {
207 if (!oti)
208 return NULL;
211 return oti;
215 * LOCKING: loader lock
217 static int
218 rgctx_template_num_infos (MonoRuntimeGenericContextTemplate *template_, int type_argc)
220 MonoRuntimeGenericContextInfoTemplate *oti;
221 int i;
223 for (i = 0, oti = get_info_templates (template_, type_argc); oti; ++i, oti = oti->next)
226 return i;
229 /* Maps from uninstantiated generic classes to GList's of
230 * uninstantiated generic classes whose parent is the key class or an
231 * instance of the key class.
233 * LOCKING: loader lock
235 static GHashTable *generic_subclass_hash;
238 * LOCKING: templates lock
240 static void
241 class_set_rgctx_template (MonoClass *klass, MonoRuntimeGenericContextTemplate *rgctx_template)
243 if (!klass->image->rgctx_template_hash)
244 klass->image->rgctx_template_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
246 g_hash_table_insert (klass->image->rgctx_template_hash, klass, rgctx_template);
250 * LOCKING: loader lock
252 static MonoRuntimeGenericContextTemplate*
253 class_lookup_rgctx_template (MonoClass *klass)
255 MonoRuntimeGenericContextTemplate *template_;
257 if (!klass->image->rgctx_template_hash)
258 return NULL;
260 template_ = (MonoRuntimeGenericContextTemplate *)g_hash_table_lookup (klass->image->rgctx_template_hash, klass);
262 return template_;
266 * LOCKING: loader lock
268 static void
269 register_generic_subclass (MonoClass *klass)
271 MonoClass *parent = klass->parent;
272 MonoClass *subclass;
273 MonoRuntimeGenericContextTemplate *rgctx_template = class_lookup_rgctx_template (klass);
275 g_assert (rgctx_template);
277 if (mono_class_is_ginst (parent))
278 parent = mono_class_get_generic_class (parent)->container_class;
280 if (!generic_subclass_hash)
281 generic_subclass_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
283 subclass = (MonoClass *)g_hash_table_lookup (generic_subclass_hash, parent);
284 rgctx_template->next_subclass = subclass;
285 g_hash_table_insert (generic_subclass_hash, parent, klass);
288 static void
289 move_subclasses_not_in_image_foreach_func (MonoClass *klass, MonoClass *subclass, MonoImage *image)
291 MonoClass *new_list;
293 if (klass->image == image) {
294 /* The parent class itself is in the image, so all the
295 subclasses must be in the image, too. If not,
296 we're removing an image containing a class which
297 still has a subclass in another image. */
299 while (subclass) {
300 g_assert (subclass->image == image);
301 subclass = class_lookup_rgctx_template (subclass)->next_subclass;
304 return;
307 new_list = NULL;
308 while (subclass) {
309 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
310 MonoClass *next = subclass_template->next_subclass;
312 if (subclass->image != image) {
313 subclass_template->next_subclass = new_list;
314 new_list = subclass;
317 subclass = next;
320 if (new_list)
321 g_hash_table_insert (generic_subclass_hash, klass, new_list);
325 * mono_class_unregister_image_generic_subclasses:
326 * @image: an image
328 * Removes all classes of the image from the generic subclass hash.
329 * Must be called when an image is unloaded.
331 static void
332 mono_class_unregister_image_generic_subclasses (MonoImage *image, gpointer user_data)
334 GHashTable *old_hash;
336 //g_print ("unregistering image %s\n", image->name);
338 if (!generic_subclass_hash)
339 return;
341 mono_loader_lock ();
343 old_hash = generic_subclass_hash;
344 generic_subclass_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
346 g_hash_table_foreach (old_hash, (GHFunc)move_subclasses_not_in_image_foreach_func, image);
348 mono_loader_unlock ();
350 g_hash_table_destroy (old_hash);
353 static MonoRuntimeGenericContextTemplate*
354 alloc_template (MonoClass *klass)
356 int size = sizeof (MonoRuntimeGenericContextTemplate);
358 num_templates_allocted++;
359 num_templates_bytes += size;
361 return (MonoRuntimeGenericContextTemplate *)mono_image_alloc0 (klass->image, size);
364 /* LOCKING: Takes the loader lock */
365 static MonoRuntimeGenericContextInfoTemplate*
366 alloc_oti (MonoImage *image)
368 int size = sizeof (MonoRuntimeGenericContextInfoTemplate);
370 num_oti_allocted++;
371 num_oti_bytes += size;
373 return (MonoRuntimeGenericContextInfoTemplate *)mono_image_alloc0 (image, size);
376 #define MONO_RGCTX_SLOT_USED_MARKER ((gpointer)&mono_defaults.object_class->byval_arg)
379 * Return true if this info type has the notion of identify.
381 * Some info types expect that each insert results in a new slot been assigned.
383 static int
384 info_has_identity (MonoRgctxInfoType info_type)
386 return info_type != MONO_RGCTX_INFO_CAST_CACHE;
390 * LOCKING: loader lock
392 static void
393 rgctx_template_set_slot (MonoImage *image, MonoRuntimeGenericContextTemplate *template_, int type_argc,
394 int slot, gpointer data, MonoRgctxInfoType info_type)
396 static gboolean inited = FALSE;
397 static int num_markers = 0;
398 static int num_data = 0;
400 int i;
401 MonoRuntimeGenericContextInfoTemplate *list = get_info_templates (template_, type_argc);
402 MonoRuntimeGenericContextInfoTemplate **oti = &list;
404 if (!inited) {
405 mono_counters_register ("RGCTX oti num markers", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_markers);
406 mono_counters_register ("RGCTX oti num data", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_data);
407 inited = TRUE;
410 g_assert (slot >= 0);
411 g_assert (data);
413 i = 0;
414 while (i <= slot) {
415 if (i > 0)
416 oti = &(*oti)->next;
417 if (!*oti)
418 *oti = alloc_oti (image);
419 ++i;
422 g_assert (!(*oti)->data);
423 (*oti)->data = data;
424 (*oti)->info_type = info_type;
426 set_info_templates (image, template_, type_argc, list);
428 if (data == MONO_RGCTX_SLOT_USED_MARKER)
429 ++num_markers;
430 else
431 ++num_data;
435 * mono_method_get_declaring_generic_method:
436 * @method: an inflated method
438 * Returns an inflated method's declaring method.
440 MonoMethod*
441 mono_method_get_declaring_generic_method (MonoMethod *method)
443 MonoMethodInflated *inflated;
445 g_assert (method->is_inflated);
447 inflated = (MonoMethodInflated*)method;
449 return inflated->declaring;
453 * mono_class_get_method_generic:
454 * @klass: a class
455 * @method: a method
457 * Given a class and a generic method, which has to be of an
458 * instantiation of the same class that klass is an instantiation of,
459 * returns the corresponding method in klass. Example:
461 * klass is Gen<string>
462 * method is Gen<object>.work<int>
464 * returns: Gen<string>.work<int>
466 MonoMethod*
467 mono_class_get_method_generic (MonoClass *klass, MonoMethod *method)
469 MonoMethod *declaring, *m;
470 int i;
472 if (method->is_inflated)
473 declaring = mono_method_get_declaring_generic_method (method);
474 else
475 declaring = method;
477 m = NULL;
478 if (mono_class_is_ginst (klass))
479 m = mono_class_get_inflated_method (klass, declaring);
481 if (!m) {
482 mono_class_setup_methods (klass);
483 if (mono_class_has_failure (klass))
484 return NULL;
485 int mcount = mono_class_get_method_count (klass);
486 for (i = 0; i < mcount; ++i) {
487 m = klass->methods [i];
488 if (m == declaring)
489 break;
490 if (m->is_inflated && mono_method_get_declaring_generic_method (m) == declaring)
491 break;
493 if (i >= mcount)
494 return NULL;
497 if (method != declaring) {
498 MonoError error;
499 MonoGenericContext context;
501 context.class_inst = NULL;
502 context.method_inst = mono_method_get_context (method)->method_inst;
504 m = mono_class_inflate_generic_method_checked (m, &context, &error);
505 g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */
508 return m;
511 static gpointer
512 inflate_info (MonoRuntimeGenericContextInfoTemplate *oti, MonoGenericContext *context, MonoClass *klass, gboolean temporary)
514 gpointer data = oti->data;
515 MonoRgctxInfoType info_type = oti->info_type;
516 MonoError error;
518 g_assert (data);
520 if (data == MONO_RGCTX_SLOT_USED_MARKER)
521 return MONO_RGCTX_SLOT_USED_MARKER;
523 switch (info_type)
525 case MONO_RGCTX_INFO_STATIC_DATA:
526 case MONO_RGCTX_INFO_KLASS:
527 case MONO_RGCTX_INFO_ELEMENT_KLASS:
528 case MONO_RGCTX_INFO_VTABLE:
529 case MONO_RGCTX_INFO_TYPE:
530 case MONO_RGCTX_INFO_REFLECTION_TYPE:
531 case MONO_RGCTX_INFO_CAST_CACHE:
532 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
533 case MONO_RGCTX_INFO_VALUE_SIZE:
534 case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
535 case MONO_RGCTX_INFO_MEMCPY:
536 case MONO_RGCTX_INFO_BZERO:
537 case MONO_RGCTX_INFO_LOCAL_OFFSET:
538 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
539 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: {
540 gpointer result = mono_class_inflate_generic_type_with_mempool (temporary ? NULL : klass->image,
541 (MonoType *)data, context, &error);
542 if (!mono_error_ok (&error)) /*FIXME proper error handling */
543 g_error ("Could not inflate generic type due to %s", mono_error_get_message (&error));
544 return result;
547 case MONO_RGCTX_INFO_METHOD:
548 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
549 case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER:
550 case MONO_RGCTX_INFO_METHOD_RGCTX:
551 case MONO_RGCTX_INFO_METHOD_CONTEXT:
552 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
553 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE: {
554 MonoMethod *method = (MonoMethod *)data;
555 MonoMethod *inflated_method;
556 MonoType *inflated_type = mono_class_inflate_generic_type_checked (&method->klass->byval_arg, context, &error);
557 mono_error_assert_ok (&error); /* FIXME don't swallow the error */
559 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
561 mono_metadata_free_type (inflated_type);
563 mono_class_init (inflated_class);
565 g_assert (!method->wrapper_type);
567 if (inflated_class->byval_arg.type == MONO_TYPE_ARRAY ||
568 inflated_class->byval_arg.type == MONO_TYPE_SZARRAY) {
569 inflated_method = mono_method_search_in_array_class (inflated_class,
570 method->name, method->signature);
571 } else {
572 MonoError error;
573 inflated_method = mono_class_inflate_generic_method_checked (method, context, &error);
574 g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */
576 mono_class_init (inflated_method->klass);
577 g_assert (inflated_method->klass == inflated_class);
578 return inflated_method;
580 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO: {
581 MonoGSharedVtMethodInfo *oinfo = (MonoGSharedVtMethodInfo *)data;
582 MonoGSharedVtMethodInfo *res;
583 MonoDomain *domain = mono_domain_get ();
584 int i;
586 res = (MonoGSharedVtMethodInfo *)mono_domain_alloc0 (domain, sizeof (MonoGSharedVtMethodInfo));
588 res->nlocals = info->nlocals;
589 res->locals_types = g_new0 (MonoType*, info->nlocals);
590 for (i = 0; i < info->nlocals; ++i)
591 res->locals_types [i] = mono_class_inflate_generic_type (info->locals_types [i], context);
593 res->num_entries = oinfo->num_entries;
594 res->entries = (MonoRuntimeGenericContextInfoTemplate *)mono_domain_alloc0 (domain, sizeof (MonoRuntimeGenericContextInfoTemplate) * oinfo->num_entries);
595 for (i = 0; i < oinfo->num_entries; ++i) {
596 MonoRuntimeGenericContextInfoTemplate *otemplate = &oinfo->entries [i];
597 MonoRuntimeGenericContextInfoTemplate *template_ = &res->entries [i];
599 memcpy (template_, otemplate, sizeof (MonoRuntimeGenericContextInfoTemplate));
600 template_->data = inflate_info (template_, context, klass, FALSE);
602 return res;
604 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE:
605 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT: {
606 MonoJumpInfoGSharedVtCall *info = (MonoJumpInfoGSharedVtCall *)data;
607 MonoMethod *method = info->method;
608 MonoMethod *inflated_method;
609 MonoType *inflated_type = mono_class_inflate_generic_type_checked (&method->klass->byval_arg, context, &error);
610 mono_error_assert_ok (&error); /* FIXME don't swallow the error */
611 WrapperInfo *winfo = NULL;
613 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
614 MonoJumpInfoGSharedVtCall *res;
615 MonoDomain *domain = mono_domain_get ();
617 res = (MonoJumpInfoGSharedVtCall *)mono_domain_alloc0 (domain, sizeof (MonoJumpInfoGSharedVtCall));
618 /* Keep the original signature */
619 res->sig = info->sig;
621 mono_metadata_free_type (inflated_type);
623 mono_class_init (inflated_class);
625 if (method->wrapper_type) {
626 winfo = mono_marshal_get_wrapper_info (method);
628 g_assert (winfo);
629 g_assert (winfo->subtype == WRAPPER_SUBTYPE_SYNCHRONIZED_INNER);
630 method = winfo->d.synchronized_inner.method;
633 if (inflated_class->byval_arg.type == MONO_TYPE_ARRAY ||
634 inflated_class->byval_arg.type == MONO_TYPE_SZARRAY) {
635 inflated_method = mono_method_search_in_array_class (inflated_class,
636 method->name, method->signature);
637 } else {
638 MonoError error;
639 inflated_method = mono_class_inflate_generic_method_checked (method, context, &error);
640 g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */
642 mono_class_init (inflated_method->klass);
643 g_assert (inflated_method->klass == inflated_class);
645 if (winfo) {
646 g_assert (winfo->subtype == WRAPPER_SUBTYPE_SYNCHRONIZED_INNER);
647 inflated_method = mono_marshal_get_synchronized_inner_wrapper (inflated_method);
650 res->method = inflated_method;
652 return res;
655 case MONO_RGCTX_INFO_CLASS_FIELD:
656 case MONO_RGCTX_INFO_FIELD_OFFSET: {
657 MonoError error;
658 MonoClassField *field = (MonoClassField *)data;
659 MonoType *inflated_type = mono_class_inflate_generic_type_checked (&field->parent->byval_arg, context, &error);
660 mono_error_assert_ok (&error); /* FIXME don't swallow the error */
662 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
663 int i = field - field->parent->fields;
664 gpointer dummy = NULL;
666 mono_metadata_free_type (inflated_type);
668 mono_class_get_fields (inflated_class, &dummy);
669 g_assert (inflated_class->fields);
671 return &inflated_class->fields [i];
673 case MONO_RGCTX_INFO_SIG_GSHAREDVT_IN_TRAMPOLINE_CALLI:
674 case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI: {
675 MonoMethodSignature *sig = (MonoMethodSignature *)data;
676 MonoMethodSignature *isig;
677 MonoError error;
679 isig = mono_inflate_generic_signature (sig, context, &error);
680 g_assert (mono_error_ok (&error));
681 return isig;
683 case MONO_RGCTX_INFO_VIRT_METHOD_CODE:
684 case MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE: {
685 MonoJumpInfoVirtMethod *info = (MonoJumpInfoVirtMethod *)data;
686 MonoJumpInfoVirtMethod *res;
687 MonoType *t;
688 MonoDomain *domain = mono_domain_get ();
689 MonoError error;
691 // FIXME: Temporary
692 res = (MonoJumpInfoVirtMethod *)mono_domain_alloc0 (domain, sizeof (MonoJumpInfoVirtMethod));
693 t = mono_class_inflate_generic_type_checked (&info->klass->byval_arg, context, &error);
694 mono_error_assert_ok (&error); /* FIXME don't swallow the error */
696 res->klass = mono_class_from_mono_type (t);
697 mono_metadata_free_type (t);
699 res->method = mono_class_inflate_generic_method_checked (info->method, context, &error);
700 g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */
702 return res;
704 default:
705 g_assert_not_reached ();
707 /* Not reached, quiet compiler */
708 return NULL;
711 static void
712 free_inflated_info (MonoRgctxInfoType info_type, gpointer info)
714 if (!info)
715 return;
717 switch (info_type) {
718 case MONO_RGCTX_INFO_STATIC_DATA:
719 case MONO_RGCTX_INFO_KLASS:
720 case MONO_RGCTX_INFO_ELEMENT_KLASS:
721 case MONO_RGCTX_INFO_VTABLE:
722 case MONO_RGCTX_INFO_TYPE:
723 case MONO_RGCTX_INFO_REFLECTION_TYPE:
724 case MONO_RGCTX_INFO_CAST_CACHE:
725 mono_metadata_free_type ((MonoType *)info);
726 break;
727 default:
728 break;
732 static MonoRuntimeGenericContextInfoTemplate
733 class_get_rgctx_template_oti (MonoClass *klass, int type_argc, guint32 slot, gboolean temporary, gboolean shared, gboolean *do_free);
735 static MonoClass*
736 class_uninstantiated (MonoClass *klass)
738 if (mono_class_is_ginst (klass))
739 return mono_class_get_generic_class (klass)->container_class;
740 return klass;
744 * get_shared_class:
746 * Return the class used to store information when using generic sharing.
748 static MonoClass*
749 get_shared_class (MonoClass *klass)
751 return class_uninstantiated (klass);
755 * mono_class_get_runtime_generic_context_template:
756 * @class: a class
758 * Looks up or constructs, if necessary, the runtime generic context template for class.
759 * The template is the same for all instantiations of a class.
761 static MonoRuntimeGenericContextTemplate*
762 mono_class_get_runtime_generic_context_template (MonoClass *klass)
764 MonoRuntimeGenericContextTemplate *parent_template, *template_;
765 guint32 i;
767 klass = get_shared_class (klass);
769 mono_loader_lock ();
770 template_ = class_lookup_rgctx_template (klass);
771 mono_loader_unlock ();
773 if (template_)
774 return template_;
776 //g_assert (get_shared_class (class) == class);
778 template_ = alloc_template (klass);
780 mono_loader_lock ();
782 if (klass->parent) {
783 guint32 num_entries;
784 int max_argc, type_argc;
786 parent_template = mono_class_get_runtime_generic_context_template (klass->parent);
787 max_argc = template_get_max_argc (parent_template);
789 for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
790 num_entries = rgctx_template_num_infos (parent_template, type_argc);
792 /* FIXME: quadratic! */
793 for (i = 0; i < num_entries; ++i) {
794 MonoRuntimeGenericContextInfoTemplate oti;
796 oti = class_get_rgctx_template_oti (klass->parent, type_argc, i, FALSE, FALSE, NULL);
797 if (oti.data && oti.data != MONO_RGCTX_SLOT_USED_MARKER) {
798 rgctx_template_set_slot (klass->image, template_, type_argc, i,
799 oti.data, oti.info_type);
805 if (class_lookup_rgctx_template (klass)) {
806 /* some other thread already set the template */
807 template_ = class_lookup_rgctx_template (klass);
808 } else {
809 class_set_rgctx_template (klass, template_);
811 if (klass->parent)
812 register_generic_subclass (klass);
815 mono_loader_unlock ();
817 return template_;
821 * class_get_rgctx_template_oti:
823 * Return the info template of CLASS numbered TYPE_ARGC/SLOT.
824 * temporary signifies whether the inflated info (oti.data) will be
825 * used temporarily, in which case it might be heap-allocated, or
826 * permanently, in which case it will be mempool-allocated. If
827 * temporary is set then *do_free will return whether the returned
828 * data must be freed.
830 * LOCKING: loader lock
832 static MonoRuntimeGenericContextInfoTemplate
833 class_get_rgctx_template_oti (MonoClass *klass, int type_argc, guint32 slot, gboolean temporary, gboolean shared, gboolean *do_free)
835 g_assert ((temporary && do_free) || (!temporary && !do_free));
837 DEBUG (printf ("get slot: %s %d\n", mono_type_full_name (&class->byval_arg), slot));
839 if (mono_class_is_ginst (klass) && !shared) {
840 MonoRuntimeGenericContextInfoTemplate oti;
841 gboolean tmp_do_free;
843 oti = class_get_rgctx_template_oti (mono_class_get_generic_class (klass)->container_class,
844 type_argc, slot, TRUE, FALSE, &tmp_do_free);
845 if (oti.data) {
846 gpointer info = oti.data;
847 oti.data = inflate_info (&oti, &mono_class_get_generic_class (klass)->context, klass, temporary);
848 if (tmp_do_free)
849 free_inflated_info (oti.info_type, info);
851 if (temporary)
852 *do_free = TRUE;
854 return oti;
855 } else {
856 MonoRuntimeGenericContextTemplate *template_;
857 MonoRuntimeGenericContextInfoTemplate *oti;
859 template_ = mono_class_get_runtime_generic_context_template (klass);
860 oti = rgctx_template_get_other_slot (template_, type_argc, slot);
861 g_assert (oti);
863 if (temporary)
864 *do_free = FALSE;
866 return *oti;
870 static gpointer
871 class_type_info (MonoDomain *domain, MonoClass *klass, MonoRgctxInfoType info_type, MonoError *error)
873 mono_error_init (error);
875 switch (info_type) {
876 case MONO_RGCTX_INFO_STATIC_DATA: {
877 MonoVTable *vtable = mono_class_vtable (domain, klass);
878 if (!vtable) {
879 mono_error_set_for_class_failure (error, klass);
880 return NULL;
882 return mono_vtable_get_static_field_data (vtable);
884 case MONO_RGCTX_INFO_KLASS:
885 return klass;
886 case MONO_RGCTX_INFO_ELEMENT_KLASS:
887 return klass->element_class;
888 case MONO_RGCTX_INFO_VTABLE: {
889 MonoVTable *vtable = mono_class_vtable (domain, klass);
890 if (!vtable) {
891 mono_error_set_for_class_failure (error, klass);
892 return NULL;
894 return vtable;
896 case MONO_RGCTX_INFO_CAST_CACHE: {
897 /*First slot is the cache itself, the second the vtable.*/
898 gpointer **cache_data = (gpointer **)mono_domain_alloc0 (domain, sizeof (gpointer) * 2);
899 cache_data [1] = (gpointer *)klass;
900 return cache_data;
902 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
903 return GUINT_TO_POINTER (mono_class_array_element_size (klass));
904 case MONO_RGCTX_INFO_VALUE_SIZE:
905 if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg))
906 return GUINT_TO_POINTER (sizeof (gpointer));
907 else
908 return GUINT_TO_POINTER (mono_class_value_size (klass, NULL));
909 case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
910 if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg))
911 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_REF);
912 else if (mono_class_is_nullable (klass))
913 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_NULLABLE);
914 else
915 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_VTYPE);
916 case MONO_RGCTX_INFO_MEMCPY:
917 case MONO_RGCTX_INFO_BZERO: {
918 static MonoMethod *memcpy_method [17];
919 static MonoMethod *bzero_method [17];
920 MonoJitDomainInfo *domain_info;
921 int size;
922 guint32 align;
924 domain_info = domain_jit_info (domain);
926 if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg)) {
927 size = sizeof (gpointer);
928 align = sizeof (gpointer);
929 } else {
930 size = mono_class_value_size (klass, &align);
933 if (size != 1 && size != 2 && size != 4 && size != 8)
934 size = 0;
935 if (align < size)
936 size = 0;
938 if (info_type == MONO_RGCTX_INFO_MEMCPY) {
939 if (!memcpy_method [size]) {
940 MonoMethod *m;
941 char name [32];
943 if (size == 0)
944 sprintf (name, "memcpy");
945 else
946 sprintf (name, "memcpy_aligned_%d", size);
947 m = mono_class_get_method_from_name (mono_defaults.string_class, name, 3);
948 g_assert (m);
949 mono_memory_barrier ();
950 memcpy_method [size] = m;
952 if (!domain_info->memcpy_addr [size]) {
953 gpointer addr = mono_compile_method_checked (memcpy_method [size], error);
954 mono_memory_barrier ();
955 domain_info->memcpy_addr [size] = (gpointer *)addr;
956 mono_error_assert_ok (error);
958 return domain_info->memcpy_addr [size];
959 } else {
960 if (!bzero_method [size]) {
961 MonoMethod *m;
962 char name [32];
964 if (size == 0)
965 sprintf (name, "bzero");
966 else
967 sprintf (name, "bzero_aligned_%d", size);
968 m = mono_class_get_method_from_name (mono_defaults.string_class, name, 2);
969 g_assert (m);
970 mono_memory_barrier ();
971 bzero_method [size] = m;
973 if (!domain_info->bzero_addr [size]) {
974 gpointer addr = mono_compile_method_checked (bzero_method [size], error);
975 mono_memory_barrier ();
976 domain_info->bzero_addr [size] = (gpointer *)addr;
977 mono_error_assert_ok (error);
979 return domain_info->bzero_addr [size];
982 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
983 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: {
984 MonoMethod *method;
985 gpointer addr, arg;
986 MonoJitInfo *ji;
987 MonoMethodSignature *sig, *gsig;
988 MonoMethod *gmethod;
990 if (!mono_class_is_nullable (klass))
991 /* This can happen since all the entries in MonoGSharedVtMethodInfo are inflated, even those which are not used */
992 return NULL;
994 if (info_type == MONO_RGCTX_INFO_NULLABLE_CLASS_BOX)
995 method = mono_class_get_method_from_name (klass, "Box", 1);
996 else
997 method = mono_class_get_method_from_name (klass, "Unbox", 1);
999 addr = mono_jit_compile_method (method, error);
1000 if (!mono_error_ok (error))
1001 return NULL;
1003 // The caller uses the gsharedvt call signature
1005 if (mono_llvm_only) {
1006 /* FIXME: We have no access to the gsharedvt signature/gsctx used by the caller, so have to construct it ourselves */
1007 gmethod = mini_get_shared_method_full (method, FALSE, TRUE);
1008 sig = mono_method_signature (method);
1009 gsig = mono_method_signature (gmethod);
1011 addr = mini_add_method_wrappers_llvmonly (method, addr, TRUE, FALSE, &arg);
1012 return mini_create_llvmonly_ftndesc (domain, addr, arg);
1015 ji = mini_jit_info_table_find (mono_domain_get (), (char *)mono_get_addr_from_ftnptr (addr), NULL);
1016 g_assert (ji);
1017 if (mini_jit_info_is_gsharedvt (ji))
1018 return mono_create_static_rgctx_trampoline (method, addr);
1019 else {
1020 /* Need to add an out wrapper */
1022 /* FIXME: We have no access to the gsharedvt signature/gsctx used by the caller, so have to construct it ourselves */
1023 gmethod = mini_get_shared_method_full (method, FALSE, TRUE);
1024 sig = mono_method_signature (method);
1025 gsig = mono_method_signature (gmethod);
1027 addr = mini_get_gsharedvt_wrapper (FALSE, addr, sig, gsig, -1, FALSE);
1028 addr = mono_create_static_rgctx_trampoline (method, addr);
1029 return addr;
1032 default:
1033 g_assert_not_reached ();
1035 /* Not reached */
1036 return NULL;
1039 static gboolean
1040 ji_is_gsharedvt (MonoJitInfo *ji)
1042 if (ji && ji->has_generic_jit_info && (mono_jit_info_get_generic_sharing_context (ji)->is_gsharedvt))
1043 return TRUE;
1044 else
1045 return FALSE;
1049 * Describes the information used to construct a gsharedvt arg trampoline.
1051 typedef struct {
1052 gboolean is_in;
1053 gboolean calli;
1054 gint32 vcall_offset;
1055 gpointer addr;
1056 MonoMethodSignature *sig, *gsig;
1057 } GSharedVtTrampInfo;
1059 static guint
1060 tramp_info_hash (gconstpointer key)
1062 GSharedVtTrampInfo *tramp = (GSharedVtTrampInfo *)key;
1064 return (gsize)tramp->addr;
1067 static gboolean
1068 tramp_info_equal (gconstpointer a, gconstpointer b)
1070 GSharedVtTrampInfo *tramp1 = (GSharedVtTrampInfo *)a;
1071 GSharedVtTrampInfo *tramp2 = (GSharedVtTrampInfo *)b;
1073 /* The signatures should be internalized */
1074 return tramp1->is_in == tramp2->is_in && tramp1->calli == tramp2->calli && tramp1->vcall_offset == tramp2->vcall_offset &&
1075 tramp1->addr == tramp2->addr && tramp1->sig == tramp2->sig && tramp1->gsig == tramp2->gsig;
1078 static MonoType*
1079 get_wrapper_shared_type (MonoType *t)
1081 if (t->byref)
1082 return &mono_defaults.int_class->this_arg;
1083 t = mini_get_underlying_type (t);
1085 switch (t->type) {
1086 case MONO_TYPE_I1:
1087 /* This removes any attributes etc. */
1088 return &mono_defaults.sbyte_class->byval_arg;
1089 case MONO_TYPE_U1:
1090 return &mono_defaults.byte_class->byval_arg;
1091 case MONO_TYPE_I2:
1092 return &mono_defaults.int16_class->byval_arg;
1093 case MONO_TYPE_U2:
1094 return &mono_defaults.uint16_class->byval_arg;
1095 case MONO_TYPE_I4:
1096 return &mono_defaults.int32_class->byval_arg;
1097 case MONO_TYPE_U4:
1098 return &mono_defaults.uint32_class->byval_arg;
1099 case MONO_TYPE_OBJECT:
1100 case MONO_TYPE_CLASS:
1101 case MONO_TYPE_SZARRAY:
1102 case MONO_TYPE_ARRAY:
1103 case MONO_TYPE_PTR:
1104 // FIXME: refs and intptr cannot be shared because
1105 // they are treated differently when a method has a vret arg,
1106 // see get_call_info ().
1107 return &mono_defaults.object_class->byval_arg;
1108 //return &mono_defaults.int_class->byval_arg;
1109 case MONO_TYPE_GENERICINST: {
1110 MonoError error;
1111 MonoClass *klass;
1112 MonoGenericContext ctx;
1113 MonoGenericContext *orig_ctx;
1114 MonoGenericInst *inst;
1115 MonoType *args [16];
1116 int i;
1118 if (!MONO_TYPE_ISSTRUCT (t))
1119 return get_wrapper_shared_type (&mono_defaults.object_class->byval_arg);
1121 klass = mono_class_from_mono_type (t);
1122 orig_ctx = &mono_class_get_generic_class (klass)->context;
1124 memset (&ctx, 0, sizeof (MonoGenericContext));
1126 inst = orig_ctx->class_inst;
1127 if (inst) {
1128 g_assert (inst->type_argc < 16);
1129 for (i = 0; i < inst->type_argc; ++i)
1130 args [i] = get_wrapper_shared_type (inst->type_argv [i]);
1131 ctx.class_inst = mono_metadata_get_generic_inst (inst->type_argc, args);
1133 inst = orig_ctx->method_inst;
1134 if (inst) {
1135 g_assert (inst->type_argc < 16);
1136 for (i = 0; i < inst->type_argc; ++i)
1137 args [i] = get_wrapper_shared_type (inst->type_argv [i]);
1138 ctx.method_inst = mono_metadata_get_generic_inst (inst->type_argc, args);
1140 klass = mono_class_inflate_generic_class_checked (mono_class_get_generic_class (klass)->container_class, &ctx, &error);
1141 mono_error_assert_ok (&error); /* FIXME don't swallow the error */
1142 return &klass->byval_arg;
1144 #if SIZEOF_VOID_P == 8
1145 case MONO_TYPE_I8:
1146 return &mono_defaults.int_class->byval_arg;
1147 #endif
1148 default:
1149 break;
1152 //printf ("%s\n", mono_type_full_name (t));
1154 return t;
1157 static MonoMethodSignature*
1158 mini_get_underlying_signature (MonoMethodSignature *sig)
1160 MonoMethodSignature *res = mono_metadata_signature_dup (sig);
1161 int i;
1163 res->ret = get_wrapper_shared_type (sig->ret);
1164 for (i = 0; i < sig->param_count; ++i)
1165 res->params [i] = get_wrapper_shared_type (sig->params [i]);
1166 res->generic_param_count = 0;
1167 res->is_inflated = 0;
1169 return res;
1173 * mini_get_gsharedvt_in_sig_wrapper:
1175 * Return a wrapper to translate between the normal and gsharedvt calling conventions of SIG.
1176 * The returned wrapper has a signature of SIG, plus one extra argument, which is an <addr, rgctx> pair.
1177 * The extra argument is passed the same way as an rgctx to shared methods.
1178 * It calls <addr> using the gsharedvt version of SIG, passing in <rgctx> as an extra argument.
1180 MonoMethod*
1181 mini_get_gsharedvt_in_sig_wrapper (MonoMethodSignature *sig)
1183 MonoMethodBuilder *mb;
1184 MonoMethod *res, *cached;
1185 WrapperInfo *info;
1186 MonoMethodSignature *csig, *gsharedvt_sig;
1187 int i, pindex, retval_var = 0;
1188 static GHashTable *cache;
1190 // FIXME: Memory management
1191 sig = mini_get_underlying_signature (sig);
1193 // FIXME: Normal cache
1194 if (!cache)
1195 cache = g_hash_table_new_full ((GHashFunc)mono_signature_hash, (GEqualFunc)mono_metadata_signature_equal, NULL, NULL);
1196 gshared_lock ();
1197 res = g_hash_table_lookup (cache, sig);
1198 gshared_unlock ();
1199 if (res) {
1200 g_free (sig);
1201 return res;
1204 /* Create the signature for the wrapper */
1205 // FIXME:
1206 csig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 1) * sizeof (MonoType*)));
1207 memcpy (csig, sig, mono_metadata_signature_size (sig));
1208 csig->param_count ++;
1209 csig->params [sig->param_count] = &mono_defaults.int_class->byval_arg;
1211 /* Create the signature for the gsharedvt callconv */
1212 gsharedvt_sig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 2) * sizeof (MonoType*)));
1213 memcpy (gsharedvt_sig, sig, mono_metadata_signature_size (sig));
1214 pindex = 0;
1215 /* The return value is returned using an explicit vret argument */
1216 if (sig->ret->type != MONO_TYPE_VOID) {
1217 gsharedvt_sig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
1218 gsharedvt_sig->ret = &mono_defaults.void_class->byval_arg;
1220 for (i = 0; i < sig->param_count; i++) {
1221 gsharedvt_sig->params [pindex] = sig->params [i];
1222 if (!sig->params [i]->byref) {
1223 gsharedvt_sig->params [pindex] = mono_metadata_type_dup (NULL, gsharedvt_sig->params [pindex]);
1224 gsharedvt_sig->params [pindex]->byref = 1;
1226 pindex ++;
1228 /* Rgctx arg */
1229 gsharedvt_sig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
1230 gsharedvt_sig->param_count = pindex;
1232 // FIXME: Use shared signatures
1233 mb = mono_mb_new (mono_defaults.object_class, sig->hasthis ? "gsharedvt_in_sig" : "gsharedvt_in_sig_static", MONO_WRAPPER_UNKNOWN);
1235 #ifndef DISABLE_JIT
1236 if (sig->ret->type != MONO_TYPE_VOID)
1237 retval_var = mono_mb_add_local (mb, sig->ret);
1239 /* Make the call */
1240 if (sig->hasthis)
1241 mono_mb_emit_ldarg (mb, 0);
1242 if (sig->ret->type != MONO_TYPE_VOID)
1243 mono_mb_emit_ldloc_addr (mb, retval_var);
1244 for (i = 0; i < sig->param_count; i++) {
1245 if (sig->params [i]->byref)
1246 mono_mb_emit_ldarg (mb, i + (sig->hasthis == TRUE));
1247 else
1248 mono_mb_emit_ldarg_addr (mb, i + (sig->hasthis == TRUE));
1250 /* Rgctx arg */
1251 mono_mb_emit_ldarg (mb, sig->param_count + (sig->hasthis ? 1 : 0));
1252 mono_mb_emit_icon (mb, sizeof (gpointer));
1253 mono_mb_emit_byte (mb, CEE_ADD);
1254 mono_mb_emit_byte (mb, CEE_LDIND_I);
1255 /* Method to call */
1256 mono_mb_emit_ldarg (mb, sig->param_count + (sig->hasthis ? 1 : 0));
1257 mono_mb_emit_byte (mb, CEE_LDIND_I);
1258 mono_mb_emit_calli (mb, gsharedvt_sig);
1259 if (sig->ret->type != MONO_TYPE_VOID)
1260 mono_mb_emit_ldloc (mb, retval_var);
1261 mono_mb_emit_byte (mb, CEE_RET);
1262 #endif
1264 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_GSHAREDVT_IN_SIG);
1265 info->d.gsharedvt.sig = sig;
1267 res = mono_mb_create (mb, csig, sig->param_count + 16, info);
1269 gshared_lock ();
1270 cached = g_hash_table_lookup (cache, sig);
1271 if (cached)
1272 res = cached;
1273 else
1274 g_hash_table_insert (cache, sig, res);
1275 gshared_unlock ();
1276 return res;
1280 * mini_get_gsharedvt_out_sig_wrapper:
1282 * Same as in_sig_wrapper, but translate between the gsharedvt and normal signatures.
1284 MonoMethod*
1285 mini_get_gsharedvt_out_sig_wrapper (MonoMethodSignature *sig)
1287 MonoMethodBuilder *mb;
1288 MonoMethod *res, *cached;
1289 WrapperInfo *info;
1290 MonoMethodSignature *normal_sig, *csig;
1291 int i, pindex, args_start, ldind_op, stind_op;
1292 static GHashTable *cache;
1294 // FIXME: Memory management
1295 sig = mini_get_underlying_signature (sig);
1297 // FIXME: Normal cache
1298 if (!cache)
1299 cache = g_hash_table_new_full ((GHashFunc)mono_signature_hash, (GEqualFunc)mono_metadata_signature_equal, NULL, NULL);
1300 gshared_lock ();
1301 res = g_hash_table_lookup (cache, sig);
1302 gshared_unlock ();
1303 if (res) {
1304 g_free (sig);
1305 return res;
1308 /* Create the signature for the wrapper */
1309 // FIXME:
1310 csig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 2) * sizeof (MonoType*)));
1311 memcpy (csig, sig, mono_metadata_signature_size (sig));
1312 pindex = 0;
1313 /* The return value is returned using an explicit vret argument */
1314 if (sig->ret->type != MONO_TYPE_VOID) {
1315 csig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
1316 csig->ret = &mono_defaults.void_class->byval_arg;
1318 args_start = pindex;
1319 if (sig->hasthis)
1320 args_start ++;
1321 for (i = 0; i < sig->param_count; i++) {
1322 csig->params [pindex] = sig->params [i];
1323 if (!sig->params [i]->byref) {
1324 csig->params [pindex] = mono_metadata_type_dup (NULL, csig->params [pindex]);
1325 csig->params [pindex]->byref = 1;
1327 pindex ++;
1329 /* Rgctx arg */
1330 csig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
1331 csig->param_count = pindex;
1333 /* Create the signature for the normal callconv */
1334 normal_sig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 2) * sizeof (MonoType*)));
1335 memcpy (normal_sig, sig, mono_metadata_signature_size (sig));
1336 normal_sig->param_count ++;
1337 normal_sig->params [sig->param_count] = &mono_defaults.int_class->byval_arg;
1339 // FIXME: Use shared signatures
1340 mb = mono_mb_new (mono_defaults.object_class, "gsharedvt_out_sig", MONO_WRAPPER_UNKNOWN);
1342 #ifndef DISABLE_JIT
1343 if (sig->ret->type != MONO_TYPE_VOID)
1344 /* Load return address */
1345 mono_mb_emit_ldarg (mb, sig->hasthis ? 1 : 0);
1347 /* Make the call */
1348 if (sig->hasthis)
1349 mono_mb_emit_ldarg (mb, 0);
1350 for (i = 0; i < sig->param_count; i++) {
1351 if (sig->params [i]->byref) {
1352 mono_mb_emit_ldarg (mb, args_start + i);
1353 } else {
1354 ldind_op = mono_type_to_ldind (sig->params [i]);
1355 mono_mb_emit_ldarg (mb, args_start + i);
1356 // FIXME:
1357 if (ldind_op == CEE_LDOBJ)
1358 mono_mb_emit_op (mb, CEE_LDOBJ, mono_class_from_mono_type (sig->params [i]));
1359 else
1360 mono_mb_emit_byte (mb, ldind_op);
1363 /* Rgctx arg */
1364 mono_mb_emit_ldarg (mb, args_start + sig->param_count);
1365 mono_mb_emit_icon (mb, sizeof (gpointer));
1366 mono_mb_emit_byte (mb, CEE_ADD);
1367 mono_mb_emit_byte (mb, CEE_LDIND_I);
1368 /* Method to call */
1369 mono_mb_emit_ldarg (mb, args_start + sig->param_count);
1370 mono_mb_emit_byte (mb, CEE_LDIND_I);
1371 mono_mb_emit_calli (mb, normal_sig);
1372 if (sig->ret->type != MONO_TYPE_VOID) {
1373 /* Store return value */
1374 stind_op = mono_type_to_stind (sig->ret);
1375 // FIXME:
1376 if (stind_op == CEE_STOBJ)
1377 mono_mb_emit_op (mb, CEE_STOBJ, mono_class_from_mono_type (sig->ret));
1378 else if (stind_op == CEE_STIND_REF)
1379 /* Avoid write barriers, the vret arg points to the stack */
1380 mono_mb_emit_byte (mb, CEE_STIND_I);
1381 else
1382 mono_mb_emit_byte (mb, stind_op);
1384 mono_mb_emit_byte (mb, CEE_RET);
1385 #endif
1387 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_GSHAREDVT_OUT_SIG);
1388 info->d.gsharedvt.sig = sig;
1390 res = mono_mb_create (mb, csig, sig->param_count + 16, info);
1392 gshared_lock ();
1393 cached = g_hash_table_lookup (cache, sig);
1394 if (cached)
1395 res = cached;
1396 else
1397 g_hash_table_insert (cache, sig, res);
1398 gshared_unlock ();
1399 return res;
1402 MonoMethodSignature*
1403 mini_get_gsharedvt_out_sig_wrapper_signature (gboolean has_this, gboolean has_ret, int param_count)
1405 MonoMethodSignature *sig = g_malloc0 (sizeof (MonoMethodSignature) + (32 * sizeof (MonoType*)));
1406 int i, pindex;
1408 sig->ret = &mono_defaults.void_class->byval_arg;
1409 sig->sentinelpos = -1;
1410 pindex = 0;
1411 if (has_this)
1412 /* this */
1413 sig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
1414 if (has_ret)
1415 /* vret */
1416 sig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
1417 for (i = 0; i < param_count; ++i)
1418 /* byref arguments */
1419 sig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
1420 /* extra arg */
1421 sig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
1422 sig->param_count = pindex;
1424 return sig;
1428 * mini_get_gsharedvt_wrapper:
1430 * Return a gsharedvt in/out wrapper for calling ADDR.
1432 gpointer
1433 mini_get_gsharedvt_wrapper (gboolean gsharedvt_in, gpointer addr, MonoMethodSignature *normal_sig, MonoMethodSignature *gsharedvt_sig, gint32 vcall_offset, gboolean calli)
1435 static gboolean inited = FALSE;
1436 static int num_trampolines;
1437 MonoError error;
1438 gpointer res, info;
1439 MonoDomain *domain = mono_domain_get ();
1440 MonoJitDomainInfo *domain_info;
1441 GSharedVtTrampInfo *tramp_info;
1442 GSharedVtTrampInfo tinfo;
1444 if (!inited) {
1445 mono_counters_register ("GSHAREDVT arg trampolines", MONO_COUNTER_JIT | MONO_COUNTER_INT, &num_trampolines);
1446 inited = TRUE;
1449 if (mono_llvm_only) {
1450 MonoMethod *wrapper;
1452 if (gsharedvt_in)
1453 wrapper = mini_get_gsharedvt_in_sig_wrapper (normal_sig);
1454 else
1455 wrapper = mini_get_gsharedvt_out_sig_wrapper (normal_sig);
1456 res = mono_compile_method_checked (wrapper, &error);
1457 mono_error_assert_ok (&error);
1458 return res;
1461 memset (&tinfo, 0, sizeof (tinfo));
1462 tinfo.is_in = gsharedvt_in;
1463 tinfo.calli = calli;
1464 tinfo.vcall_offset = vcall_offset;
1465 tinfo.addr = addr;
1466 tinfo.sig = normal_sig;
1467 tinfo.gsig = gsharedvt_sig;
1469 domain_info = domain_jit_info (domain);
1472 * The arg trampolines might only have a finite number in full-aot, so use a cache.
1474 mono_domain_lock (domain);
1475 if (!domain_info->gsharedvt_arg_tramp_hash)
1476 domain_info->gsharedvt_arg_tramp_hash = g_hash_table_new (tramp_info_hash, tramp_info_equal);
1477 res = g_hash_table_lookup (domain_info->gsharedvt_arg_tramp_hash, &tinfo);
1478 mono_domain_unlock (domain);
1479 if (res)
1480 return res;
1482 info = mono_arch_get_gsharedvt_call_info (addr, normal_sig, gsharedvt_sig, gsharedvt_in, vcall_offset, calli);
1484 if (gsharedvt_in) {
1485 static gpointer tramp_addr;
1486 MonoMethod *wrapper;
1488 if (!tramp_addr) {
1489 wrapper = mono_marshal_get_gsharedvt_in_wrapper ();
1490 addr = mono_compile_method_checked (wrapper, &error);
1491 mono_memory_barrier ();
1492 mono_error_assert_ok (&error);
1493 tramp_addr = addr;
1495 addr = tramp_addr;
1496 } else {
1497 static gpointer tramp_addr;
1498 MonoMethod *wrapper;
1500 if (!tramp_addr) {
1501 wrapper = mono_marshal_get_gsharedvt_out_wrapper ();
1502 addr = mono_compile_method_checked (wrapper, &error);
1503 mono_memory_barrier ();
1504 mono_error_assert_ok (&error);
1505 tramp_addr = addr;
1507 addr = tramp_addr;
1510 if (mono_aot_only)
1511 addr = mono_aot_get_gsharedvt_arg_trampoline (info, addr);
1512 else
1513 addr = mono_arch_get_gsharedvt_arg_trampoline (mono_domain_get (), info, addr);
1515 num_trampolines ++;
1517 /* Cache it */
1518 tramp_info = (GSharedVtTrampInfo *)mono_domain_alloc0 (domain, sizeof (GSharedVtTrampInfo));
1519 memcpy (tramp_info, &tinfo, sizeof (GSharedVtTrampInfo));
1521 mono_domain_lock (domain);
1522 /* Duplicates are not a problem */
1523 g_hash_table_insert (domain_info->gsharedvt_arg_tramp_hash, tramp_info, addr);
1524 mono_domain_unlock (domain);
1526 return addr;
1530 * instantiate_info:
1532 * Instantiate the info given by OTI for context CONTEXT.
1534 static gpointer
1535 instantiate_info (MonoDomain *domain, MonoRuntimeGenericContextInfoTemplate *oti,
1536 MonoGenericContext *context, MonoClass *klass, MonoError *error)
1538 gpointer data;
1539 gboolean temporary;
1541 mono_error_init (error);
1543 if (!oti->data)
1544 return NULL;
1546 switch (oti->info_type) {
1547 case MONO_RGCTX_INFO_STATIC_DATA:
1548 case MONO_RGCTX_INFO_KLASS:
1549 case MONO_RGCTX_INFO_ELEMENT_KLASS:
1550 case MONO_RGCTX_INFO_VTABLE:
1551 case MONO_RGCTX_INFO_CAST_CACHE:
1552 temporary = TRUE;
1553 break;
1554 default:
1555 temporary = FALSE;
1558 data = inflate_info (oti, context, klass, temporary);
1560 switch (oti->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_CAST_CACHE:
1566 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
1567 case MONO_RGCTX_INFO_VALUE_SIZE:
1568 case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
1569 case MONO_RGCTX_INFO_MEMCPY:
1570 case MONO_RGCTX_INFO_BZERO:
1571 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
1572 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: {
1573 MonoClass *arg_class = mono_class_from_mono_type ((MonoType *)data);
1575 free_inflated_info (oti->info_type, data);
1576 g_assert (arg_class);
1578 /* The class might be used as an argument to
1579 mono_value_copy(), which requires that its GC
1580 descriptor has been computed. */
1581 if (oti->info_type == MONO_RGCTX_INFO_KLASS)
1582 mono_class_compute_gc_descriptor (arg_class);
1584 return class_type_info (domain, arg_class, oti->info_type, error);
1586 case MONO_RGCTX_INFO_TYPE:
1587 return data;
1588 case MONO_RGCTX_INFO_REFLECTION_TYPE: {
1589 MonoReflectionType *ret = mono_type_get_object_checked (domain, (MonoType *)data, error);
1591 return ret;
1593 case MONO_RGCTX_INFO_METHOD:
1594 return data;
1595 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE: {
1596 MonoMethod *m = (MonoMethod*)data;
1597 gpointer addr;
1598 gpointer arg = NULL;
1600 if (mono_llvm_only) {
1601 addr = mono_compile_method_checked (m, error);
1602 return_val_if_nok (error, NULL);
1603 addr = mini_add_method_wrappers_llvmonly (m, addr, FALSE, FALSE, &arg);
1605 /* Returns an ftndesc */
1606 return mini_create_llvmonly_ftndesc (domain, addr, arg);
1607 } else {
1608 addr = mono_compile_method_checked ((MonoMethod *)data, error);
1609 return_val_if_nok (error, NULL);
1610 return mini_add_method_trampoline ((MonoMethod *)data, addr, mono_method_needs_static_rgctx_invoke ((MonoMethod *)data, FALSE), FALSE);
1613 case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER: {
1614 MonoMethod *m = (MonoMethod*)data;
1615 gpointer addr;
1616 gpointer arg = NULL;
1618 g_assert (mono_llvm_only);
1620 addr = mono_compile_method_checked (m, error);
1621 return_val_if_nok (error, NULL);
1623 MonoJitInfo *ji;
1624 gboolean callee_gsharedvt;
1626 ji = mini_jit_info_table_find (mono_domain_get (), (char *)mono_get_addr_from_ftnptr (addr), NULL);
1627 g_assert (ji);
1628 callee_gsharedvt = mini_jit_info_is_gsharedvt (ji);
1629 if (callee_gsharedvt)
1630 callee_gsharedvt = mini_is_gsharedvt_variable_signature (mono_method_signature (jinfo_get_method (ji)));
1631 if (callee_gsharedvt) {
1632 /* No need for a wrapper */
1633 return mini_create_llvmonly_ftndesc (domain, addr, mini_method_get_rgctx (m));
1634 } else {
1635 addr = mini_add_method_wrappers_llvmonly (m, addr, TRUE, FALSE, &arg);
1637 /* Returns an ftndesc */
1638 return mini_create_llvmonly_ftndesc (domain, addr, arg);
1641 case MONO_RGCTX_INFO_VIRT_METHOD_CODE: {
1642 MonoJumpInfoVirtMethod *info = (MonoJumpInfoVirtMethod *)data;
1643 MonoClass *iface_class = info->method->klass;
1644 MonoMethod *method;
1645 int ioffset, slot;
1646 gpointer addr;
1648 mono_class_setup_vtable (info->klass);
1649 // FIXME: Check type load
1650 if (mono_class_is_interface (iface_class)) {
1651 ioffset = mono_class_interface_offset (info->klass, iface_class);
1652 g_assert (ioffset != -1);
1653 } else {
1654 ioffset = 0;
1656 slot = mono_method_get_vtable_slot (info->method);
1657 g_assert (slot != -1);
1658 g_assert (info->klass->vtable);
1659 method = info->klass->vtable [ioffset + slot];
1661 method = mono_class_inflate_generic_method_checked (method, context, error);
1662 return_val_if_nok (error, NULL);
1663 addr = mono_compile_method_checked (method, error);
1664 return_val_if_nok (error, NULL);
1665 return mini_add_method_trampoline (method, addr, mono_method_needs_static_rgctx_invoke (method, FALSE), FALSE);
1667 case MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE: {
1668 MonoJumpInfoVirtMethod *info = (MonoJumpInfoVirtMethod *)data;
1669 MonoClass *iface_class = info->method->klass;
1670 MonoMethod *method;
1671 MonoClass *impl_class;
1672 int ioffset, slot;
1674 mono_class_setup_vtable (info->klass);
1675 // FIXME: Check type load
1676 if (mono_class_is_interface (iface_class)) {
1677 ioffset = mono_class_interface_offset (info->klass, iface_class);
1678 g_assert (ioffset != -1);
1679 } else {
1680 ioffset = 0;
1682 slot = mono_method_get_vtable_slot (info->method);
1683 g_assert (slot != -1);
1684 g_assert (info->klass->vtable);
1685 method = info->klass->vtable [ioffset + slot];
1687 impl_class = method->klass;
1688 if (MONO_TYPE_IS_REFERENCE (&impl_class->byval_arg))
1689 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_REF);
1690 else if (mono_class_is_nullable (impl_class))
1691 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_NULLABLE);
1692 else
1693 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_VTYPE);
1695 #ifndef DISABLE_REMOTING
1696 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
1697 return mono_compile_method_checked (mono_marshal_get_remoting_invoke_with_check ((MonoMethod *)data), error);
1698 #endif
1699 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE:
1700 return mono_domain_alloc0 (domain, sizeof (gpointer));
1701 case MONO_RGCTX_INFO_CLASS_FIELD:
1702 return data;
1703 case MONO_RGCTX_INFO_FIELD_OFFSET: {
1704 MonoClassField *field = (MonoClassField *)data;
1706 /* The value is offset by 1 */
1707 if (field->parent->valuetype && !(field->type->attrs & FIELD_ATTRIBUTE_STATIC))
1708 return GUINT_TO_POINTER (field->offset - sizeof (MonoObject) + 1);
1709 else
1710 return GUINT_TO_POINTER (field->offset + 1);
1712 case MONO_RGCTX_INFO_METHOD_RGCTX: {
1713 MonoMethodInflated *method = (MonoMethodInflated *)data;
1714 MonoVTable *vtable;
1716 g_assert (method->method.method.is_inflated);
1717 g_assert (method->context.method_inst);
1719 vtable = mono_class_vtable (domain, method->method.method.klass);
1720 if (!vtable) {
1721 mono_error_set_for_class_failure (error, method->method.method.klass);
1722 return NULL;
1725 return mono_method_lookup_rgctx (vtable, method->context.method_inst);
1727 case MONO_RGCTX_INFO_METHOD_CONTEXT: {
1728 MonoMethodInflated *method = (MonoMethodInflated *)data;
1730 g_assert (method->method.method.is_inflated);
1731 g_assert (method->context.method_inst);
1733 return method->context.method_inst;
1735 case MONO_RGCTX_INFO_SIG_GSHAREDVT_IN_TRAMPOLINE_CALLI: {
1736 MonoMethodSignature *gsig = (MonoMethodSignature *)oti->data;
1737 MonoMethodSignature *sig = (MonoMethodSignature *)data;
1738 gpointer addr;
1741 * This is an indirect call to the address passed by the caller in the rgctx reg.
1743 addr = mini_get_gsharedvt_wrapper (TRUE, NULL, sig, gsig, -1, TRUE);
1744 return addr;
1746 case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI: {
1747 MonoMethodSignature *gsig = (MonoMethodSignature *)oti->data;
1748 MonoMethodSignature *sig = (MonoMethodSignature *)data;
1749 gpointer addr;
1752 * This is an indirect call to the address passed by the caller in the rgctx reg.
1754 addr = mini_get_gsharedvt_wrapper (FALSE, NULL, sig, gsig, -1, TRUE);
1755 return addr;
1757 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE:
1758 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT: {
1759 MonoJumpInfoGSharedVtCall *call_info = (MonoJumpInfoGSharedVtCall *)data;
1760 MonoMethodSignature *call_sig;
1761 MonoMethod *method;
1762 gpointer addr;
1763 MonoJitInfo *callee_ji;
1764 gboolean virtual_ = oti->info_type == MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT;
1765 gint32 vcall_offset;
1766 gboolean callee_gsharedvt;
1768 /* This is the original generic signature used by the caller */
1769 call_sig = call_info->sig;
1770 /* This is the instantiated method which is called */
1771 method = call_info->method;
1773 g_assert (method->is_inflated);
1775 if (mono_llvm_only && (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED))
1776 method = mono_marshal_get_synchronized_wrapper (method);
1778 if (!virtual_) {
1779 addr = mono_compile_method_checked (method, error);
1780 return_val_if_nok (error, NULL);
1781 } else
1782 addr = NULL;
1784 if (virtual_) {
1785 /* Same as in mono_emit_method_call_full () */
1786 if ((method->klass->parent == mono_defaults.multicastdelegate_class) && (!strcmp (method->name, "Invoke"))) {
1787 /* See mono_emit_method_call_full () */
1788 /* The gsharedvt trampoline will recognize this constant */
1789 vcall_offset = MONO_GSHAREDVT_DEL_INVOKE_VT_OFFSET;
1790 } else if (mono_class_is_interface (method->klass)) {
1791 guint32 imt_slot = mono_method_get_imt_slot (method);
1792 vcall_offset = ((gint32)imt_slot - MONO_IMT_SIZE) * SIZEOF_VOID_P;
1793 } else {
1794 vcall_offset = G_STRUCT_OFFSET (MonoVTable, vtable) +
1795 ((mono_method_get_vtable_index (method)) * (SIZEOF_VOID_P));
1797 } else {
1798 vcall_offset = -1;
1801 // FIXME: This loads information in the AOT case
1802 callee_ji = mini_jit_info_table_find (mono_domain_get (), (char *)mono_get_addr_from_ftnptr (addr), NULL);
1803 callee_gsharedvt = ji_is_gsharedvt (callee_ji);
1806 * For gsharedvt calls made out of gsharedvt methods, the callee could end up being a gsharedvt method, or a normal
1807 * non-shared method. The latter call cannot be patched, so instead of using a normal call, we make an indirect
1808 * call through the rgctx, in effect patching the rgctx entry instead of the call site.
1809 * For virtual calls, the caller might be a normal or a gsharedvt method. Since there is only one vtable slot,
1810 * this difference needs to be handed on the caller side. This is currently implemented by adding a gsharedvt-in
1811 * trampoline to all gsharedvt methods and storing this trampoline into the vtable slot. Virtual calls made from
1812 * gsharedvt methods always go through a gsharedvt-out trampoline, so the calling sequence is:
1813 * caller -> out trampoline -> in trampoline -> callee
1814 * This is not very efficient, but it is easy to implement.
1816 if (virtual_ || !callee_gsharedvt) {
1817 MonoMethodSignature *sig, *gsig;
1819 g_assert (method->is_inflated);
1821 sig = mono_method_signature (method);
1822 gsig = call_sig;
1824 if (mono_llvm_only) {
1825 if (mini_is_gsharedvt_variable_signature (call_sig)) {
1826 /* The virtual case doesn't go through this code */
1827 g_assert (!virtual_);
1829 sig = mono_method_signature (jinfo_get_method (callee_ji));
1830 gpointer out_wrapper = mini_get_gsharedvt_wrapper (FALSE, NULL, sig, gsig, -1, FALSE);
1831 MonoFtnDesc *out_wrapper_arg = mini_create_llvmonly_ftndesc (domain, callee_ji->code_start, mini_method_get_rgctx (method));
1833 /* Returns an ftndesc */
1834 addr = mini_create_llvmonly_ftndesc (domain, out_wrapper, out_wrapper_arg);
1835 } else {
1836 addr = mini_create_llvmonly_ftndesc (domain, addr, mini_method_get_rgctx (method));
1838 } else {
1839 addr = mini_get_gsharedvt_wrapper (FALSE, addr, sig, gsig, vcall_offset, FALSE);
1841 #if 0
1842 if (virtual)
1843 printf ("OUT-VCALL: %s\n", mono_method_full_name (method, TRUE));
1844 else
1845 printf ("OUT: %s\n", mono_method_full_name (method, TRUE));
1846 #endif
1847 } else if (callee_gsharedvt) {
1848 MonoMethodSignature *sig, *gsig;
1851 * This is a combination of the out and in cases, since both the caller and the callee are gsharedvt methods.
1852 * The caller and the callee can use different gsharedvt signatures, so we have to add both an out and an in
1853 * trampoline, i.e.:
1854 * class Base<T> {
1855 * public void foo<T1> (T1 t1, T t, object o) {}
1857 * class AClass : Base<long> {
1858 * public void bar<T> (T t, long time, object o) {
1859 * foo (t, time, o);
1862 * Here, the caller uses !!0,long, while the callee uses !!0,!0
1863 * FIXME: Optimize this.
1866 if (mono_llvm_only) {
1867 /* Both wrappers receive an extra <addr, rgctx> argument */
1868 sig = mono_method_signature (method);
1869 gsig = mono_method_signature (jinfo_get_method (callee_ji));
1871 /* Return a function descriptor */
1873 if (mini_is_gsharedvt_variable_signature (call_sig)) {
1875 * This is not an optimization, but its needed, since the concrete signature 'sig'
1876 * might not exist at all in IL, so the AOT compiler cannot generate the wrappers
1877 * for it.
1879 addr = mini_create_llvmonly_ftndesc (domain, callee_ji->code_start, mini_method_get_rgctx (method));
1880 } else if (mini_is_gsharedvt_variable_signature (gsig)) {
1881 gpointer in_wrapper = mini_get_gsharedvt_wrapper (TRUE, callee_ji->code_start, sig, gsig, -1, FALSE);
1883 gpointer in_wrapper_arg = mini_create_llvmonly_ftndesc (domain, callee_ji->code_start, mini_method_get_rgctx (method));
1885 addr = mini_create_llvmonly_ftndesc (domain, in_wrapper, in_wrapper_arg);
1886 } else {
1887 addr = mini_create_llvmonly_ftndesc (domain, addr, mini_method_get_rgctx (method));
1889 } else if (call_sig == mono_method_signature (method)) {
1890 } else {
1891 sig = mono_method_signature (method);
1892 gsig = mono_method_signature (jinfo_get_method (callee_ji));
1894 addr = mini_get_gsharedvt_wrapper (TRUE, callee_ji->code_start, sig, gsig, -1, FALSE);
1896 sig = mono_method_signature (method);
1897 gsig = call_sig;
1899 addr = mini_get_gsharedvt_wrapper (FALSE, addr, sig, gsig, -1, FALSE);
1901 //printf ("OUT-IN-RGCTX: %s\n", mono_method_full_name (method, TRUE));
1905 return addr;
1907 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO: {
1908 MonoGSharedVtMethodInfo *info = (MonoGSharedVtMethodInfo *)data;
1909 MonoGSharedVtMethodRuntimeInfo *res;
1910 MonoType *t;
1911 int i, offset, align, size;
1913 // FIXME:
1914 res = (MonoGSharedVtMethodRuntimeInfo *)g_malloc0 (sizeof (MonoGSharedVtMethodRuntimeInfo) + (info->num_entries * sizeof (gpointer)));
1916 offset = 0;
1917 for (i = 0; i < info->num_entries; ++i) {
1918 MonoRuntimeGenericContextInfoTemplate *template_ = &info->entries [i];
1920 switch (template_->info_type) {
1921 case MONO_RGCTX_INFO_LOCAL_OFFSET:
1922 t = (MonoType *)template_->data;
1924 size = mono_type_size (t, &align);
1926 if (align < sizeof (gpointer))
1927 align = sizeof (gpointer);
1928 if (MONO_TYPE_ISSTRUCT (t) && align < 2 * sizeof (gpointer))
1929 align = 2 * sizeof (gpointer);
1931 // FIXME: Do the same things as alloc_stack_slots
1932 offset += align - 1;
1933 offset &= ~(align - 1);
1934 res->entries [i] = GINT_TO_POINTER (offset);
1935 offset += size;
1936 break;
1937 default:
1938 res->entries [i] = instantiate_info (domain, template_, context, klass, error);
1939 if (!mono_error_ok (error))
1940 return NULL;
1941 break;
1944 res->locals_size = offset;
1946 return res;
1948 default:
1949 g_assert_not_reached ();
1951 /* Not reached */
1952 return NULL;
1956 * LOCKING: loader lock
1958 static void
1959 fill_in_rgctx_template_slot (MonoClass *klass, int type_argc, int index, gpointer data, MonoRgctxInfoType info_type)
1961 MonoRuntimeGenericContextTemplate *template_ = mono_class_get_runtime_generic_context_template (klass);
1962 MonoClass *subclass;
1964 rgctx_template_set_slot (klass->image, template_, type_argc, index, data, info_type);
1966 /* Recurse for all subclasses */
1967 if (generic_subclass_hash)
1968 subclass = (MonoClass *)g_hash_table_lookup (generic_subclass_hash, klass);
1969 else
1970 subclass = NULL;
1972 while (subclass) {
1973 MonoRuntimeGenericContextInfoTemplate subclass_oti;
1974 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
1976 g_assert (subclass_template);
1978 subclass_oti = class_get_rgctx_template_oti (subclass->parent, type_argc, index, FALSE, FALSE, NULL);
1979 g_assert (subclass_oti.data);
1981 fill_in_rgctx_template_slot (subclass, type_argc, index, subclass_oti.data, info_type);
1983 subclass = subclass_template->next_subclass;
1987 const char*
1988 mono_rgctx_info_type_to_str (MonoRgctxInfoType type)
1990 switch (type) {
1991 case MONO_RGCTX_INFO_STATIC_DATA: return "STATIC_DATA";
1992 case MONO_RGCTX_INFO_KLASS: return "KLASS";
1993 case MONO_RGCTX_INFO_ELEMENT_KLASS: return "ELEMENT_KLASS";
1994 case MONO_RGCTX_INFO_VTABLE: return "VTABLE";
1995 case MONO_RGCTX_INFO_TYPE: return "TYPE";
1996 case MONO_RGCTX_INFO_REFLECTION_TYPE: return "REFLECTION_TYPE";
1997 case MONO_RGCTX_INFO_METHOD: return "METHOD";
1998 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO: return "GSHAREDVT_INFO";
1999 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE: return "GENERIC_METHOD_CODE";
2000 case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER: return "GSHAREDVT_OUT_WRAPPER";
2001 case MONO_RGCTX_INFO_CLASS_FIELD: return "CLASS_FIELD";
2002 case MONO_RGCTX_INFO_METHOD_RGCTX: return "METHOD_RGCTX";
2003 case MONO_RGCTX_INFO_METHOD_CONTEXT: return "METHOD_CONTEXT";
2004 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK: return "REMOTING_INVOKE_WITH_CHECK";
2005 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE: return "METHOD_DELEGATE_CODE";
2006 case MONO_RGCTX_INFO_CAST_CACHE: return "CAST_CACHE";
2007 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE: return "ARRAY_ELEMENT_SIZE";
2008 case MONO_RGCTX_INFO_VALUE_SIZE: return "VALUE_SIZE";
2009 case MONO_RGCTX_INFO_CLASS_BOX_TYPE: return "CLASS_BOX_TYPE";
2010 case MONO_RGCTX_INFO_FIELD_OFFSET: return "FIELD_OFFSET";
2011 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE: return "METHOD_GSHAREDVT_OUT_TRAMPOLINE";
2012 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT: return "METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT";
2013 case MONO_RGCTX_INFO_SIG_GSHAREDVT_IN_TRAMPOLINE_CALLI: return "SIG_GSHAREDVT_IN_TRAMPOLINE_CALLI";
2014 case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI: return "SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI";
2015 case MONO_RGCTX_INFO_MEMCPY: return "MEMCPY";
2016 case MONO_RGCTX_INFO_BZERO: return "BZERO";
2017 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX: return "NULLABLE_CLASS_BOX";
2018 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: return "NULLABLE_CLASS_UNBOX";
2019 case MONO_RGCTX_INFO_VIRT_METHOD_CODE: return "VIRT_METHOD_CODE";
2020 case MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE: return "VIRT_METHOD_BOX_TYPE";
2021 default:
2022 return "<UNKNOWN RGCTX INFO TYPE>";
2026 G_GNUC_UNUSED static char*
2027 rgctx_info_to_str (MonoRgctxInfoType info_type, gpointer data)
2029 switch (info_type) {
2030 case MONO_RGCTX_INFO_VTABLE:
2031 return mono_type_full_name ((MonoType*)data);
2032 default:
2033 return g_strdup_printf ("<%p>", data);
2038 * LOCKING: loader lock
2040 static int
2041 register_info (MonoClass *klass, int type_argc, gpointer data, MonoRgctxInfoType info_type)
2043 int i;
2044 MonoRuntimeGenericContextTemplate *template_ = mono_class_get_runtime_generic_context_template (klass);
2045 MonoClass *parent;
2046 MonoRuntimeGenericContextInfoTemplate *oti;
2048 for (i = 0, oti = get_info_templates (template_, type_argc); oti; ++i, oti = oti->next) {
2049 if (!oti->data)
2050 break;
2053 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)));
2055 /* Mark the slot as used in all parent classes (until we find
2056 a parent class which already has it marked used). */
2057 parent = klass->parent;
2058 while (parent != NULL) {
2059 MonoRuntimeGenericContextTemplate *parent_template;
2060 MonoRuntimeGenericContextInfoTemplate *oti;
2062 if (mono_class_is_ginst (parent))
2063 parent = mono_class_get_generic_class (parent)->container_class;
2065 parent_template = mono_class_get_runtime_generic_context_template (parent);
2066 oti = rgctx_template_get_other_slot (parent_template, type_argc, i);
2068 if (oti && oti->data)
2069 break;
2071 rgctx_template_set_slot (parent->image, parent_template, type_argc, i,
2072 MONO_RGCTX_SLOT_USED_MARKER, (MonoRgctxInfoType)0);
2074 parent = parent->parent;
2077 /* Fill in the slot in this class and in all subclasses
2078 recursively. */
2079 fill_in_rgctx_template_slot (klass, type_argc, i, data, info_type);
2081 return i;
2084 static gboolean
2085 info_equal (gpointer data1, gpointer data2, MonoRgctxInfoType info_type)
2087 switch (info_type) {
2088 case MONO_RGCTX_INFO_STATIC_DATA:
2089 case MONO_RGCTX_INFO_KLASS:
2090 case MONO_RGCTX_INFO_ELEMENT_KLASS:
2091 case MONO_RGCTX_INFO_VTABLE:
2092 case MONO_RGCTX_INFO_TYPE:
2093 case MONO_RGCTX_INFO_REFLECTION_TYPE:
2094 case MONO_RGCTX_INFO_CAST_CACHE:
2095 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
2096 case MONO_RGCTX_INFO_VALUE_SIZE:
2097 case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
2098 case MONO_RGCTX_INFO_MEMCPY:
2099 case MONO_RGCTX_INFO_BZERO:
2100 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
2101 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX:
2102 return mono_class_from_mono_type ((MonoType *)data1) == mono_class_from_mono_type ((MonoType *)data2);
2103 case MONO_RGCTX_INFO_METHOD:
2104 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO:
2105 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
2106 case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER:
2107 case MONO_RGCTX_INFO_CLASS_FIELD:
2108 case MONO_RGCTX_INFO_FIELD_OFFSET:
2109 case MONO_RGCTX_INFO_METHOD_RGCTX:
2110 case MONO_RGCTX_INFO_METHOD_CONTEXT:
2111 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
2112 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE:
2113 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE:
2114 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT:
2115 case MONO_RGCTX_INFO_SIG_GSHAREDVT_IN_TRAMPOLINE_CALLI:
2116 case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI:
2117 return data1 == data2;
2118 case MONO_RGCTX_INFO_VIRT_METHOD_CODE:
2119 case MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE: {
2120 MonoJumpInfoVirtMethod *info1 = (MonoJumpInfoVirtMethod *)data1;
2121 MonoJumpInfoVirtMethod *info2 = (MonoJumpInfoVirtMethod *)data2;
2123 return info1->klass == info2->klass && info1->method == info2->method;
2125 default:
2126 g_assert_not_reached ();
2128 /* never reached */
2129 return FALSE;
2133 * mini_rgctx_info_type_to_patch_info_type:
2135 * Return the type of the runtime object referred to by INFO_TYPE.
2137 MonoJumpInfoType
2138 mini_rgctx_info_type_to_patch_info_type (MonoRgctxInfoType info_type)
2140 switch (info_type) {
2141 case MONO_RGCTX_INFO_STATIC_DATA:
2142 case MONO_RGCTX_INFO_KLASS:
2143 case MONO_RGCTX_INFO_ELEMENT_KLASS:
2144 case MONO_RGCTX_INFO_VTABLE:
2145 case MONO_RGCTX_INFO_TYPE:
2146 case MONO_RGCTX_INFO_REFLECTION_TYPE:
2147 case MONO_RGCTX_INFO_CAST_CACHE:
2148 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
2149 case MONO_RGCTX_INFO_VALUE_SIZE:
2150 case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
2151 case MONO_RGCTX_INFO_MEMCPY:
2152 case MONO_RGCTX_INFO_BZERO:
2153 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
2154 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX:
2155 case MONO_RGCTX_INFO_LOCAL_OFFSET:
2156 return MONO_PATCH_INFO_CLASS;
2157 case MONO_RGCTX_INFO_FIELD_OFFSET:
2158 return MONO_PATCH_INFO_FIELD;
2159 default:
2160 g_assert_not_reached ();
2161 return (MonoJumpInfoType)-1;
2165 static int
2166 lookup_or_register_info (MonoClass *klass, int type_argc, gpointer data, MonoRgctxInfoType info_type,
2167 MonoGenericContext *generic_context)
2169 static gboolean inited = FALSE;
2170 static int max_slot = 0;
2172 MonoRuntimeGenericContextTemplate *rgctx_template =
2173 mono_class_get_runtime_generic_context_template (klass);
2174 MonoRuntimeGenericContextInfoTemplate *oti_list, *oti;
2175 int i;
2177 klass = get_shared_class (klass);
2179 mono_loader_lock ();
2181 if (info_has_identity (info_type)) {
2182 oti_list = get_info_templates (rgctx_template, type_argc);
2184 for (oti = oti_list, i = 0; oti; oti = oti->next, ++i) {
2185 gpointer inflated_data;
2187 if (oti->info_type != info_type || !oti->data)
2188 continue;
2190 inflated_data = inflate_info (oti, generic_context, klass, TRUE);
2192 if (info_equal (data, inflated_data, info_type)) {
2193 free_inflated_info (info_type, inflated_data);
2194 mono_loader_unlock ();
2195 return i;
2197 free_inflated_info (info_type, inflated_data);
2201 /* We haven't found the info */
2202 i = register_info (klass, type_argc, data, info_type);
2204 mono_loader_unlock ();
2206 if (!inited) {
2207 mono_counters_register ("RGCTX max slot number", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &max_slot);
2208 inited = TRUE;
2210 if (i > max_slot)
2211 max_slot = i;
2213 return i;
2217 * mono_method_lookup_or_register_info:
2218 * @method: a method
2219 * @in_mrgctx: whether to put the data into the MRGCTX
2220 * @data: the info data
2221 * @info_type: the type of info to register about data
2222 * @generic_context: a generic context
2224 * Looks up and, if necessary, adds information about data/info_type in
2225 * method's or method's class runtime generic context. Returns the
2226 * encoded slot number.
2228 guint32
2229 mono_method_lookup_or_register_info (MonoMethod *method, gboolean in_mrgctx, gpointer data,
2230 MonoRgctxInfoType info_type, MonoGenericContext *generic_context)
2232 MonoClass *klass = method->klass;
2233 int type_argc, index;
2235 if (in_mrgctx) {
2236 MonoGenericInst *method_inst = mono_method_get_context (method)->method_inst;
2238 g_assert (method->is_inflated && method_inst);
2239 type_argc = method_inst->type_argc;
2240 g_assert (type_argc > 0);
2241 } else {
2242 type_argc = 0;
2245 index = lookup_or_register_info (klass, type_argc, data, info_type, generic_context);
2247 //g_print ("rgctx item at index %d argc %d\n", index, type_argc);
2249 if (in_mrgctx)
2250 return MONO_RGCTX_SLOT_MAKE_MRGCTX (index);
2251 else
2252 return MONO_RGCTX_SLOT_MAKE_RGCTX (index);
2256 * mono_class_rgctx_get_array_size:
2257 * @n: The number of the array
2258 * @mrgctx: Whether it's an MRGCTX as opposed to a RGCTX.
2260 * Returns the number of slots in the n'th array of a (M)RGCTX. That
2261 * number includes the slot for linking and - for MRGCTXs - the two
2262 * slots in the first array for additional information.
2265 mono_class_rgctx_get_array_size (int n, gboolean mrgctx)
2267 g_assert (n >= 0 && n < 30);
2269 if (mrgctx)
2270 return 6 << n;
2271 else
2272 return 4 << n;
2276 * LOCKING: domain lock
2278 static gpointer*
2279 alloc_rgctx_array (MonoDomain *domain, int n, gboolean is_mrgctx)
2281 static gboolean inited = FALSE;
2282 static int rgctx_num_alloced = 0;
2283 static int rgctx_bytes_alloced = 0;
2284 static int mrgctx_num_alloced = 0;
2285 static int mrgctx_bytes_alloced = 0;
2287 int size = mono_class_rgctx_get_array_size (n, is_mrgctx) * sizeof (gpointer);
2288 gpointer *array = (gpointer *)mono_domain_alloc0 (domain, size);
2290 if (!inited) {
2291 mono_counters_register ("RGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_num_alloced);
2292 mono_counters_register ("RGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_bytes_alloced);
2293 mono_counters_register ("MRGCTX num arrays alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_num_alloced);
2294 mono_counters_register ("MRGCTX bytes alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_bytes_alloced);
2295 inited = TRUE;
2298 if (is_mrgctx) {
2299 mrgctx_num_alloced++;
2300 mrgctx_bytes_alloced += size;
2301 } else {
2302 rgctx_num_alloced++;
2303 rgctx_bytes_alloced += size;
2306 return array;
2309 static gpointer
2310 fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContext *rgctx, guint32 slot,
2311 MonoGenericInst *method_inst, MonoError *error)
2313 gpointer info;
2314 int i, first_slot, size;
2315 MonoDomain *domain = class_vtable->domain;
2316 MonoClass *klass = class_vtable->klass;
2317 MonoGenericContext *class_context = mono_class_is_ginst (klass) ? &mono_class_get_generic_class (klass)->context : NULL;
2318 MonoRuntimeGenericContextInfoTemplate oti;
2319 MonoGenericContext context = { class_context ? class_context->class_inst : NULL, method_inst };
2320 int rgctx_index;
2321 gboolean do_free;
2323 mono_error_init (error);
2325 g_assert (rgctx);
2327 mono_domain_lock (domain);
2329 /* First check whether that slot isn't already instantiated.
2330 This might happen because lookup doesn't lock. Allocate
2331 arrays on the way. */
2332 first_slot = 0;
2333 size = mono_class_rgctx_get_array_size (0, method_inst != NULL);
2334 if (method_inst)
2335 size -= MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
2336 for (i = 0; ; ++i) {
2337 int offset;
2339 if (method_inst && i == 0)
2340 offset = MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
2341 else
2342 offset = 0;
2344 if (slot < first_slot + size - 1) {
2345 rgctx_index = slot - first_slot + 1 + offset;
2346 info = rgctx [rgctx_index];
2347 if (info) {
2348 mono_domain_unlock (domain);
2349 return info;
2351 break;
2353 if (!rgctx [offset + 0])
2354 rgctx [offset + 0] = alloc_rgctx_array (domain, i + 1, method_inst != NULL);
2355 rgctx = (void **)rgctx [offset + 0];
2356 first_slot += size - 1;
2357 size = mono_class_rgctx_get_array_size (i + 1, method_inst != NULL);
2360 g_assert (!rgctx [rgctx_index]);
2362 mono_domain_unlock (domain);
2364 oti = class_get_rgctx_template_oti (get_shared_class (klass),
2365 method_inst ? method_inst->type_argc : 0, slot, TRUE, TRUE, &do_free);
2366 /* This might take the loader lock */
2367 info = instantiate_info (domain, &oti, &context, klass, error);
2368 g_assert (info);
2371 if (method_inst)
2372 g_print ("filling mrgctx slot %d table %d index %d\n", slot, i, rgctx_index);
2375 /*FIXME We should use CAS here, no need to take a lock.*/
2376 mono_domain_lock (domain);
2378 /* Check whether the slot hasn't been instantiated in the
2379 meantime. */
2380 if (rgctx [rgctx_index])
2381 info = rgctx [rgctx_index];
2382 else
2383 rgctx [rgctx_index] = info;
2385 mono_domain_unlock (domain);
2387 if (do_free)
2388 free_inflated_info (oti.info_type, oti.data);
2390 return info;
2394 * mono_class_fill_runtime_generic_context:
2395 * @class_vtable: a vtable
2396 * @slot: a slot index to be instantiated
2398 * Instantiates a slot in the RGCTX, returning its value.
2400 gpointer
2401 mono_class_fill_runtime_generic_context (MonoVTable *class_vtable, guint32 slot, MonoError *error)
2403 static gboolean inited = FALSE;
2404 static int num_alloced = 0;
2406 MonoDomain *domain = class_vtable->domain;
2407 MonoRuntimeGenericContext *rgctx;
2408 gpointer info;
2410 mono_error_init (error);
2412 mono_domain_lock (domain);
2414 if (!inited) {
2415 mono_counters_register ("RGCTX num alloced", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_alloced);
2416 inited = TRUE;
2419 rgctx = class_vtable->runtime_generic_context;
2420 if (!rgctx) {
2421 rgctx = alloc_rgctx_array (domain, 0, FALSE);
2422 class_vtable->runtime_generic_context = rgctx;
2423 num_alloced++;
2426 mono_domain_unlock (domain);
2428 info = fill_runtime_generic_context (class_vtable, rgctx, slot, 0, error);
2430 DEBUG (printf ("get rgctx slot: %s %d -> %p\n", mono_type_full_name (&class_vtable->klass->byval_arg), slot, info));
2432 return info;
2436 * mono_method_fill_runtime_generic_context:
2437 * @mrgctx: an MRGCTX
2438 * @slot: a slot index to be instantiated
2440 * Instantiates a slot in the MRGCTX.
2442 gpointer
2443 mono_method_fill_runtime_generic_context (MonoMethodRuntimeGenericContext *mrgctx, guint32 slot, MonoError *error)
2445 gpointer info;
2447 info = fill_runtime_generic_context (mrgctx->class_vtable, (MonoRuntimeGenericContext*)mrgctx, slot, mrgctx->method_inst, error);
2449 return info;
2452 static guint
2453 mrgctx_hash_func (gconstpointer key)
2455 const MonoMethodRuntimeGenericContext *mrgctx = (const MonoMethodRuntimeGenericContext *)key;
2457 return mono_aligned_addr_hash (mrgctx->class_vtable) ^ mono_metadata_generic_inst_hash (mrgctx->method_inst);
2460 static gboolean
2461 mrgctx_equal_func (gconstpointer a, gconstpointer b)
2463 const MonoMethodRuntimeGenericContext *mrgctx1 = (const MonoMethodRuntimeGenericContext *)a;
2464 const MonoMethodRuntimeGenericContext *mrgctx2 = (const MonoMethodRuntimeGenericContext *)b;
2466 return mrgctx1->class_vtable == mrgctx2->class_vtable &&
2467 mono_metadata_generic_inst_equal (mrgctx1->method_inst, mrgctx2->method_inst);
2471 * mono_method_lookup_rgctx:
2472 * @class_vtable: a vtable
2473 * @method_inst: the method inst of a generic method
2475 * Returns the MRGCTX for the generic method(s) with the given
2476 * method_inst of the given class_vtable.
2478 * LOCKING: Take the domain lock.
2480 MonoMethodRuntimeGenericContext*
2481 mono_method_lookup_rgctx (MonoVTable *class_vtable, MonoGenericInst *method_inst)
2483 MonoDomain *domain = class_vtable->domain;
2484 MonoMethodRuntimeGenericContext *mrgctx;
2485 MonoMethodRuntimeGenericContext key;
2487 g_assert (!mono_class_is_gtd (class_vtable->klass));
2488 g_assert (!method_inst->is_open);
2490 mono_domain_lock (domain);
2491 if (!domain->method_rgctx_hash)
2492 domain->method_rgctx_hash = g_hash_table_new (mrgctx_hash_func, mrgctx_equal_func);
2494 key.class_vtable = class_vtable;
2495 key.method_inst = method_inst;
2497 mrgctx = (MonoMethodRuntimeGenericContext *)g_hash_table_lookup (domain->method_rgctx_hash, &key);
2499 if (!mrgctx) {
2500 //int i;
2502 mrgctx = (MonoMethodRuntimeGenericContext*)alloc_rgctx_array (domain, 0, TRUE);
2503 mrgctx->class_vtable = class_vtable;
2504 mrgctx->method_inst = method_inst;
2506 g_hash_table_insert (domain->method_rgctx_hash, mrgctx, mrgctx);
2509 g_print ("mrgctx alloced for %s <", mono_type_get_full_name (class_vtable->klass));
2510 for (i = 0; i < method_inst->type_argc; ++i)
2511 g_print ("%s, ", mono_type_full_name (method_inst->type_argv [i]));
2512 g_print (">\n");
2516 mono_domain_unlock (domain);
2518 g_assert (mrgctx);
2520 return mrgctx;
2523 static gboolean
2524 type_is_sharable (MonoType *type, gboolean allow_type_vars, gboolean allow_partial)
2526 if (allow_type_vars && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR)) {
2527 MonoType *constraint = type->data.generic_param->gshared_constraint;
2528 if (!constraint)
2529 return TRUE;
2530 type = constraint;
2533 if (MONO_TYPE_IS_REFERENCE (type))
2534 return TRUE;
2536 /* Allow non ref arguments if they are primitive types or enums (partial sharing). */
2537 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)))
2538 return TRUE;
2540 if (allow_partial && !type->byref && type->type == MONO_TYPE_GENERICINST && MONO_TYPE_ISSTRUCT (type)) {
2541 MonoGenericClass *gclass = type->data.generic_class;
2543 if (gclass->context.class_inst && !mini_generic_inst_is_sharable (gclass->context.class_inst, allow_type_vars, allow_partial))
2544 return FALSE;
2545 if (gclass->context.method_inst && !mini_generic_inst_is_sharable (gclass->context.method_inst, allow_type_vars, allow_partial))
2546 return FALSE;
2547 if (mono_class_is_nullable (mono_class_from_mono_type (type)))
2548 return FALSE;
2549 return TRUE;
2552 return FALSE;
2555 gboolean
2556 mini_generic_inst_is_sharable (MonoGenericInst *inst, gboolean allow_type_vars,
2557 gboolean allow_partial)
2559 int i;
2561 for (i = 0; i < inst->type_argc; ++i) {
2562 if (!type_is_sharable (inst->type_argv [i], allow_type_vars, allow_partial))
2563 return FALSE;
2566 return TRUE;
2570 * mono_is_partially_sharable_inst:
2572 * Return TRUE if INST has ref and non-ref type arguments.
2574 gboolean
2575 mono_is_partially_sharable_inst (MonoGenericInst *inst)
2577 int i;
2578 gboolean has_refs = FALSE, has_non_refs = FALSE;
2580 for (i = 0; i < inst->type_argc; ++i) {
2581 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)
2582 has_refs = TRUE;
2583 else
2584 has_non_refs = TRUE;
2587 return has_refs && has_non_refs;
2591 * mono_generic_context_is_sharable_full:
2592 * @context: a generic context
2594 * Returns whether the generic context is sharable. A generic context
2595 * is sharable iff all of its type arguments are reference type, or some of them have a
2596 * reference type, and ALLOW_PARTIAL is TRUE.
2598 gboolean
2599 mono_generic_context_is_sharable_full (MonoGenericContext *context,
2600 gboolean allow_type_vars,
2601 gboolean allow_partial)
2603 g_assert (context->class_inst || context->method_inst);
2605 if (context->class_inst && !mini_generic_inst_is_sharable (context->class_inst, allow_type_vars, allow_partial))
2606 return FALSE;
2608 if (context->method_inst && !mini_generic_inst_is_sharable (context->method_inst, allow_type_vars, allow_partial))
2609 return FALSE;
2611 return TRUE;
2614 gboolean
2615 mono_generic_context_is_sharable (MonoGenericContext *context, gboolean allow_type_vars)
2617 return mono_generic_context_is_sharable_full (context, allow_type_vars, partial_sharing_supported ());
2621 * mono_method_is_generic_impl:
2622 * @method: a method
2624 * Returns whether the method is either generic or part of a generic
2625 * class.
2627 gboolean
2628 mono_method_is_generic_impl (MonoMethod *method)
2630 if (method->is_inflated)
2631 return TRUE;
2632 /* We don't treat wrappers as generic code, i.e., we never
2633 apply generic sharing to them. This is especially
2634 important for static rgctx invoke wrappers, which only work
2635 if not compiled with sharing. */
2636 if (method->wrapper_type != MONO_WRAPPER_NONE)
2637 return FALSE;
2638 if (mono_class_is_gtd (method->klass))
2639 return TRUE;
2640 return FALSE;
2643 static gboolean
2644 has_constraints (MonoGenericContainer *container)
2646 //int i;
2648 return FALSE;
2650 g_assert (container->type_argc > 0);
2651 g_assert (container->type_params);
2653 for (i = 0; i < container->type_argc; ++i)
2654 if (container->type_params [i].constraints)
2655 return TRUE;
2656 return FALSE;
2660 static gboolean
2661 mini_method_is_open (MonoMethod *method)
2663 if (method->is_inflated) {
2664 MonoGenericContext *ctx = mono_method_get_context (method);
2666 if (ctx->class_inst && ctx->class_inst->is_open)
2667 return TRUE;
2668 if (ctx->method_inst && ctx->method_inst->is_open)
2669 return TRUE;
2671 return FALSE;
2674 /* Lazy class loading functions */
2675 static GENERATE_TRY_GET_CLASS_WITH_CACHE (iasync_state_machine, System.Runtime.CompilerServices, IAsyncStateMachine)
2677 static G_GNUC_UNUSED gboolean
2678 is_async_state_machine_class (MonoClass *klass)
2680 MonoClass *iclass;
2682 return FALSE;
2684 iclass = mono_class_try_get_iasync_state_machine_class ();
2686 if (iclass && klass->valuetype && mono_class_is_assignable_from (iclass, klass))
2687 return TRUE;
2688 return FALSE;
2691 static G_GNUC_UNUSED gboolean
2692 is_async_method (MonoMethod *method)
2694 MonoError error;
2695 MonoCustomAttrInfo *cattr;
2696 MonoMethodSignature *sig;
2697 gboolean res = FALSE;
2698 MonoClass *attr_class;
2700 return FALSE;
2702 attr_class = mono_class_try_get_iasync_state_machine_class ();
2704 /* Do less expensive checks first */
2705 sig = mono_method_signature (method);
2706 if (attr_class && sig && ((sig->ret->type == MONO_TYPE_VOID) ||
2707 (sig->ret->type == MONO_TYPE_CLASS && !strcmp (sig->ret->data.generic_class->container_class->name, "Task")) ||
2708 (sig->ret->type == MONO_TYPE_GENERICINST && !strcmp (sig->ret->data.generic_class->container_class->name, "Task`1")))) {
2709 //printf ("X: %s\n", mono_method_full_name (method, TRUE));
2710 cattr = mono_custom_attrs_from_method_checked (method, &error);
2711 if (!is_ok (&error)) {
2712 mono_error_cleanup (&error); /* FIXME don't swallow the error? */
2713 return FALSE;
2715 if (cattr) {
2716 if (mono_custom_attrs_has_attr (cattr, attr_class))
2717 res = TRUE;
2718 mono_custom_attrs_free (cattr);
2721 return res;
2725 * mono_method_is_generic_sharable_full:
2726 * @method: a method
2727 * @allow_type_vars: whether to regard type variables as reference types
2728 * @allow_partial: whether to allow partial sharing
2729 * @allow_gsharedvt: whenever to allow sharing over valuetypes
2731 * Returns TRUE iff the method is inflated or part of an inflated
2732 * class, its context is sharable and it has no constraints on its
2733 * type parameters. Otherwise returns FALSE.
2735 gboolean
2736 mono_method_is_generic_sharable_full (MonoMethod *method, gboolean allow_type_vars,
2737 gboolean allow_partial, gboolean allow_gsharedvt)
2739 if (!mono_method_is_generic_impl (method))
2740 return FALSE;
2743 if (!mono_debug_count ())
2744 allow_partial = FALSE;
2747 if (!partial_sharing_supported ())
2748 allow_partial = FALSE;
2750 if (mono_class_is_nullable (method->klass))
2751 // FIXME:
2752 allow_partial = FALSE;
2754 if (method->klass->image->dynamic)
2756 * Enabling this causes corlib test failures because the JIT encounters generic instances whose
2757 * instance_size is 0.
2759 allow_partial = FALSE;
2762 * Generic async methods have an associated state machine class which is a generic struct. This struct
2763 * is too large to be handled by gsharedvt so we make it visible to the AOT compiler by disabling sharing
2764 * of the async method and the state machine class.
2766 if (is_async_state_machine_class (method->klass))
2767 return FALSE;
2769 if (allow_gsharedvt && mini_is_gsharedvt_sharable_method (method)) {
2770 if (is_async_method (method))
2771 return FALSE;
2772 return TRUE;
2775 if (method->is_inflated) {
2776 MonoMethodInflated *inflated = (MonoMethodInflated*)method;
2777 MonoGenericContext *context = &inflated->context;
2779 if (!mono_generic_context_is_sharable_full (context, allow_type_vars, allow_partial))
2780 return FALSE;
2782 g_assert (inflated->declaring);
2784 if (inflated->declaring->is_generic) {
2785 if (has_constraints (mono_method_get_generic_container (inflated->declaring)))
2786 return FALSE;
2790 if (mono_class_is_ginst (method->klass)) {
2791 if (!mono_generic_context_is_sharable_full (&mono_class_get_generic_class (method->klass)->context, allow_type_vars, allow_partial))
2792 return FALSE;
2794 g_assert (mono_class_get_generic_class (method->klass)->container_class &&
2795 mono_class_is_gtd (mono_class_get_generic_class (method->klass)->container_class));
2797 if (has_constraints (mono_class_get_generic_container (mono_class_get_generic_class (method->klass)->container_class)))
2798 return FALSE;
2801 if (mono_class_is_gtd (method->klass) && !allow_type_vars)
2802 return FALSE;
2804 /* This does potentially expensive cattr checks, so do it at the end */
2805 if (is_async_method (method)) {
2806 if (mini_method_is_open (method))
2807 /* The JIT can't compile these without sharing */
2808 return TRUE;
2809 return FALSE;
2812 return TRUE;
2815 gboolean
2816 mono_method_is_generic_sharable (MonoMethod *method, gboolean allow_type_vars)
2818 return mono_method_is_generic_sharable_full (method, allow_type_vars, partial_sharing_supported (), TRUE);
2822 * mono_method_needs_static_rgctx_invoke:
2824 * Return whenever METHOD needs an rgctx argument.
2825 * An rgctx argument is needed when the method is generic sharable, but it doesn't
2826 * have a this argument which can be used to load the rgctx.
2828 gboolean
2829 mono_method_needs_static_rgctx_invoke (MonoMethod *method, gboolean allow_type_vars)
2831 if (!mono_class_generic_sharing_enabled (method->klass))
2832 return FALSE;
2834 if (!mono_method_is_generic_sharable (method, allow_type_vars))
2835 return FALSE;
2837 if (method->is_inflated && mono_method_get_context (method)->method_inst)
2838 return TRUE;
2840 return ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
2841 method->klass->valuetype) &&
2842 (mono_class_is_ginst (method->klass) || mono_class_is_gtd (method->klass));
2845 static MonoGenericInst*
2846 get_object_generic_inst (int type_argc)
2848 MonoType **type_argv;
2849 int i;
2851 type_argv = (MonoType **)alloca (sizeof (MonoType*) * type_argc);
2853 for (i = 0; i < type_argc; ++i)
2854 type_argv [i] = &mono_defaults.object_class->byval_arg;
2856 return mono_metadata_get_generic_inst (type_argc, type_argv);
2860 * mono_method_construct_object_context:
2861 * @method: a method
2863 * Returns a generic context for method with all type variables for
2864 * class and method instantiated with Object.
2866 MonoGenericContext
2867 mono_method_construct_object_context (MonoMethod *method)
2869 MonoGenericContext object_context;
2871 g_assert (!mono_class_is_ginst (method->klass));
2872 if (mono_class_is_gtd (method->klass)) {
2873 int type_argc = mono_class_get_generic_container (method->klass)->type_argc;
2875 object_context.class_inst = get_object_generic_inst (type_argc);
2876 } else {
2877 object_context.class_inst = NULL;
2880 if (mono_method_get_context_general (method, TRUE)->method_inst) {
2881 int type_argc = mono_method_get_context_general (method, TRUE)->method_inst->type_argc;
2883 object_context.method_inst = get_object_generic_inst (type_argc);
2884 } else {
2885 object_context.method_inst = NULL;
2888 g_assert (object_context.class_inst || object_context.method_inst);
2890 return object_context;
2893 static gboolean gshared_supported;
2895 void
2896 mono_set_generic_sharing_supported (gboolean supported)
2898 gshared_supported = supported;
2902 void
2903 mono_set_partial_sharing_supported (gboolean supported)
2905 partial_supported = supported;
2909 * mono_class_generic_sharing_enabled:
2910 * @class: a class
2912 * Returns whether generic sharing is enabled for class.
2914 * This is a stop-gap measure to slowly introduce generic sharing
2915 * until we have all the issues sorted out, at which time this
2916 * function will disappear and generic sharing will always be enabled.
2918 gboolean
2919 mono_class_generic_sharing_enabled (MonoClass *klass)
2921 if (gshared_supported)
2922 return TRUE;
2923 else
2924 return FALSE;
2927 MonoGenericContext*
2928 mini_method_get_context (MonoMethod *method)
2930 return mono_method_get_context_general (method, TRUE);
2934 * mono_method_check_context_used:
2935 * @method: a method
2937 * Checks whether the method's generic context uses a type variable.
2938 * Returns an int with the bits MONO_GENERIC_CONTEXT_USED_CLASS and
2939 * MONO_GENERIC_CONTEXT_USED_METHOD set to reflect whether the
2940 * context's class or method instantiation uses type variables.
2943 mono_method_check_context_used (MonoMethod *method)
2945 MonoGenericContext *method_context = mini_method_get_context (method);
2946 int context_used = 0;
2948 if (!method_context) {
2949 /* It might be a method of an array of an open generic type */
2950 if (method->klass->rank)
2951 context_used = mono_class_check_context_used (method->klass);
2952 } else {
2953 context_used = mono_generic_context_check_used (method_context);
2954 context_used |= mono_class_check_context_used (method->klass);
2957 return context_used;
2960 static gboolean
2961 generic_inst_equal (MonoGenericInst *inst1, MonoGenericInst *inst2)
2963 int i;
2965 if (!inst1) {
2966 g_assert (!inst2);
2967 return TRUE;
2970 g_assert (inst2);
2972 if (inst1->type_argc != inst2->type_argc)
2973 return FALSE;
2975 for (i = 0; i < inst1->type_argc; ++i)
2976 if (!mono_metadata_type_equal (inst1->type_argv [i], inst2->type_argv [i]))
2977 return FALSE;
2979 return TRUE;
2983 * mono_generic_context_equal_deep:
2984 * @context1: a generic context
2985 * @context2: a generic context
2987 * Returns whether context1's type arguments are equal to context2's
2988 * type arguments.
2990 gboolean
2991 mono_generic_context_equal_deep (MonoGenericContext *context1, MonoGenericContext *context2)
2993 return generic_inst_equal (context1->class_inst, context2->class_inst) &&
2994 generic_inst_equal (context1->method_inst, context2->method_inst);
2998 * mini_class_get_container_class:
2999 * @class: a generic class
3001 * Returns the class's container class, which is the class itself if
3002 * it doesn't have generic_class set.
3004 MonoClass*
3005 mini_class_get_container_class (MonoClass *klass)
3007 if (mono_class_is_ginst (klass))
3008 return mono_class_get_generic_class (klass)->container_class;
3010 g_assert (mono_class_is_gtd (klass));
3011 return klass;
3015 * mini_class_get_context:
3016 * @class: a generic class
3018 * Returns the class's generic context.
3020 MonoGenericContext*
3021 mini_class_get_context (MonoClass *klass)
3023 if (mono_class_is_ginst (klass))
3024 return &mono_class_get_generic_class (klass)->context;
3026 g_assert (mono_class_is_gtd (klass));
3027 return &mono_class_get_generic_container (klass)->context;
3031 * mini_get_basic_type_from_generic:
3032 * @type: a type
3034 * Returns a closed type corresponding to the possibly open type
3035 * passed to it.
3037 static MonoType*
3038 mini_get_basic_type_from_generic (MonoType *type)
3040 if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) && mini_is_gsharedvt_type (type))
3041 return type;
3042 else if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR)) {
3043 MonoType *constraint = type->data.generic_param->gshared_constraint;
3044 /* The gparam serial encodes the type this gparam can represent */
3045 if (!constraint) {
3046 return &mono_defaults.object_class->byval_arg;
3047 } else {
3048 MonoClass *klass;
3050 g_assert (constraint != &mono_defaults.int_class->parent->byval_arg);
3051 klass = mono_class_from_mono_type (constraint);
3052 return &klass->byval_arg;
3054 } else {
3055 return mini_native_type_replace_type (mono_type_get_basic_type_from_generic (type));
3060 * mini_type_get_underlying_type:
3062 * Return the underlying type of TYPE, taking into account enums, byref, bool, char and generic
3063 * sharing.
3065 MonoType*
3066 mini_type_get_underlying_type (MonoType *type)
3068 type = mini_native_type_replace_type (type);
3070 if (type->byref)
3071 return &mono_defaults.int_class->byval_arg;
3072 if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) && mini_is_gsharedvt_type (type))
3073 return type;
3074 type = mini_get_basic_type_from_generic (mono_type_get_underlying_type (type));
3075 switch (type->type) {
3076 case MONO_TYPE_BOOLEAN:
3077 return &mono_defaults.byte_class->byval_arg;
3078 case MONO_TYPE_CHAR:
3079 return &mono_defaults.uint16_class->byval_arg;
3080 case MONO_TYPE_STRING:
3081 return &mono_defaults.object_class->byval_arg;
3082 default:
3083 return type;
3088 * mini_type_stack_size:
3089 * @t: a type
3090 * @align: Pointer to an int for returning the alignment
3092 * Returns the type's stack size and the alignment in *align.
3095 mini_type_stack_size (MonoType *t, int *align)
3097 return mono_type_stack_size_internal (t, align, TRUE);
3101 * mini_type_stack_size_full:
3103 * Same as mini_type_stack_size, but handle pinvoke data types as well.
3106 mini_type_stack_size_full (MonoType *t, guint32 *align, gboolean pinvoke)
3108 int size;
3110 //g_assert (!mini_is_gsharedvt_type (t));
3112 if (pinvoke) {
3113 size = mono_type_native_stack_size (t, align);
3114 } else {
3115 int ialign;
3117 if (align) {
3118 size = mini_type_stack_size (t, &ialign);
3119 *align = ialign;
3120 } else {
3121 size = mini_type_stack_size (t, NULL);
3125 return size;
3129 * mono_generic_sharing_init:
3131 * Initialize the module.
3133 void
3134 mono_generic_sharing_init (void)
3136 mono_counters_register ("RGCTX template num allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_templates_allocted);
3137 mono_counters_register ("RGCTX template bytes allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_templates_bytes);
3138 mono_counters_register ("RGCTX oti num allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_oti_allocted);
3139 mono_counters_register ("RGCTX oti bytes allocted", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_oti_bytes);
3141 mono_install_image_unload_hook (mono_class_unregister_image_generic_subclasses, NULL);
3143 mono_os_mutex_init_recursive (&gshared_mutex);
3146 void
3147 mono_generic_sharing_cleanup (void)
3149 mono_remove_image_unload_hook (mono_class_unregister_image_generic_subclasses, NULL);
3151 if (generic_subclass_hash)
3152 g_hash_table_destroy (generic_subclass_hash);
3156 * mini_type_var_is_vt:
3158 * Return whenever T is a type variable instantiated with a vtype.
3160 gboolean
3161 mini_type_var_is_vt (MonoType *type)
3163 if (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) {
3164 return type->data.generic_param->gshared_constraint && (type->data.generic_param->gshared_constraint->type == MONO_TYPE_VALUETYPE || type->data.generic_param->gshared_constraint->type == MONO_TYPE_GENERICINST);
3165 } else {
3166 g_assert_not_reached ();
3167 return FALSE;
3171 gboolean
3172 mini_type_is_reference (MonoType *type)
3174 type = mini_type_get_underlying_type (type);
3175 return mono_type_is_reference (type);
3179 * mini_method_get_rgctx:
3181 * Return the RGCTX which needs to be passed to M when it is called.
3183 gpointer
3184 mini_method_get_rgctx (MonoMethod *m)
3186 if (mini_method_get_context (m)->method_inst)
3187 return mono_method_lookup_rgctx (mono_class_vtable (mono_domain_get (), m->klass), mini_method_get_context (m)->method_inst);
3188 else
3189 return mono_class_vtable (mono_domain_get (), m->klass);
3193 * mini_type_is_vtype:
3195 * Return whenever T is a vtype, or a type param instantiated with a vtype.
3196 * Should be used in place of MONO_TYPE_ISSTRUCT () which can't handle gsharedvt.
3198 gboolean
3199 mini_type_is_vtype (MonoType *t)
3201 t = mini_type_get_underlying_type (t);
3203 return MONO_TYPE_ISSTRUCT (t) || mini_is_gsharedvt_variable_type (t);
3206 gboolean
3207 mini_class_is_generic_sharable (MonoClass *klass)
3209 if (mono_class_is_ginst (klass) && is_async_state_machine_class (klass))
3210 return FALSE;
3212 return (mono_class_is_ginst (klass) && mono_generic_context_is_sharable (&mono_class_get_generic_class (klass)->context, FALSE));
3215 gboolean
3216 mini_is_gsharedvt_variable_klass (MonoClass *klass)
3218 return mini_is_gsharedvt_variable_type (&klass->byval_arg);
3221 gboolean
3222 mini_is_gsharedvt_gparam (MonoType *t)
3224 /* Matches get_gsharedvt_type () */
3225 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;
3228 static char*
3229 get_shared_gparam_name (MonoTypeEnum constraint, const char *name)
3231 if (constraint == MONO_TYPE_VALUETYPE) {
3232 return g_strdup_printf ("%s_GSHAREDVT", name);
3233 } else if (constraint == MONO_TYPE_OBJECT) {
3234 return g_strdup_printf ("%s_REF", name);
3235 } else if (constraint == MONO_TYPE_GENERICINST) {
3236 return g_strdup_printf ("%s_INST", name);
3237 } else {
3238 MonoType t;
3239 char *tname, *tname2, *res;
3241 memset (&t, 0, sizeof (t));
3242 t.type = constraint;
3243 tname = mono_type_full_name (&t);
3244 tname2 = g_utf8_strup (tname, strlen (tname));
3245 res = g_strdup_printf ("%s_%s", name, tname2);
3246 g_free (tname);
3247 g_free (tname2);
3248 return res;
3252 static guint
3253 shared_gparam_hash (gconstpointer data)
3255 MonoGSharedGenericParam *p = (MonoGSharedGenericParam*)data;
3256 guint hash;
3258 hash = mono_metadata_generic_param_hash (p->parent);
3259 hash = ((hash << 5) - hash) ^ mono_metadata_type_hash (p->param.param.gshared_constraint);
3261 return hash;
3264 static gboolean
3265 shared_gparam_equal (gconstpointer ka, gconstpointer kb)
3267 MonoGSharedGenericParam *p1 = (MonoGSharedGenericParam*)ka;
3268 MonoGSharedGenericParam *p2 = (MonoGSharedGenericParam*)kb;
3270 if (p1 == p2)
3271 return TRUE;
3272 if (p1->parent != p2->parent)
3273 return FALSE;
3274 if (!mono_metadata_type_equal (p1->param.param.gshared_constraint, p2->param.param.gshared_constraint))
3275 return FALSE;
3276 return TRUE;
3280 * mini_get_shared_gparam:
3282 * Create an anonymous gparam from T with a constraint which encodes which types can match it.
3284 MonoType*
3285 mini_get_shared_gparam (MonoType *t, MonoType *constraint)
3287 MonoGenericParam *par = t->data.generic_param;
3288 MonoGSharedGenericParam *copy, key;
3289 MonoType *res;
3290 MonoImage *image = NULL;
3291 char *name;
3293 memset (&key, 0, sizeof (key));
3294 key.parent = par;
3295 key.param.param.gshared_constraint = constraint;
3297 g_assert (mono_generic_param_info (par));
3298 image = get_image_for_generic_param(par);
3301 * Need a cache to ensure the newly created gparam
3302 * is unique wrt T/CONSTRAINT.
3304 mono_image_lock (image);
3305 if (!image->gshared_types) {
3306 image->gshared_types_len = MONO_TYPE_INTERNAL;
3307 image->gshared_types = g_new0 (GHashTable*, image->gshared_types_len);
3309 if (!image->gshared_types [constraint->type])
3310 image->gshared_types [constraint->type] = g_hash_table_new (shared_gparam_hash, shared_gparam_equal);
3311 res = (MonoType *)g_hash_table_lookup (image->gshared_types [constraint->type], &key);
3312 mono_image_unlock (image);
3313 if (res)
3314 return res;
3315 copy = (MonoGSharedGenericParam *)mono_image_alloc0 (image, sizeof (MonoGSharedGenericParam));
3316 memcpy (&copy->param, par, sizeof (MonoGenericParamFull));
3317 copy->param.info.pklass = NULL;
3318 name = get_shared_gparam_name (constraint->type, ((MonoGenericParamFull*)copy)->info.name);
3319 copy->param.info.name = mono_image_strdup (image, name);
3320 g_free (name);
3322 copy->param.param.owner = par->owner;
3324 copy->param.param.gshared_constraint = constraint;
3325 copy->parent = par;
3326 res = mono_metadata_type_dup (NULL, t);
3327 res->data.generic_param = (MonoGenericParam*)copy;
3329 if (image) {
3330 mono_image_lock (image);
3331 /* Duplicates are ok */
3332 g_hash_table_insert (image->gshared_types [constraint->type], copy, res);
3333 mono_image_unlock (image);
3336 return res;
3339 static MonoGenericInst*
3340 get_shared_inst (MonoGenericInst *inst, MonoGenericInst *shared_inst, MonoGenericContainer *container, gboolean all_vt, gboolean gsharedvt, gboolean partial);
3342 static MonoType*
3343 get_shared_type (MonoType *t, MonoType *type)
3345 MonoTypeEnum ttype;
3347 if (!type->byref && type->type == MONO_TYPE_GENERICINST && MONO_TYPE_ISSTRUCT (type)) {
3348 MonoError error;
3349 MonoGenericClass *gclass = type->data.generic_class;
3350 MonoGenericContext context;
3351 MonoClass *k;
3353 memset (&context, 0, sizeof (context));
3354 if (gclass->context.class_inst)
3355 context.class_inst = get_shared_inst (gclass->context.class_inst, mono_class_get_generic_container (gclass->container_class)->context.class_inst, NULL, FALSE, FALSE, TRUE);
3356 if (gclass->context.method_inst)
3357 context.method_inst = get_shared_inst (gclass->context.method_inst, mono_class_get_generic_container (gclass->container_class)->context.method_inst, NULL, FALSE, FALSE, TRUE);
3359 k = mono_class_inflate_generic_class_checked (gclass->container_class, &context, &error);
3360 mono_error_assert_ok (&error); /* FIXME don't swallow the error */
3362 return mini_get_shared_gparam (t, &k->byval_arg);
3363 } else if (MONO_TYPE_ISSTRUCT (type)) {
3364 return type;
3367 /* Create a type variable with a constraint which encodes which types can match it */
3368 ttype = type->type;
3369 if (type->type == MONO_TYPE_VALUETYPE) {
3370 ttype = mono_class_enum_basetype (type->data.klass)->type;
3371 } else if (MONO_TYPE_IS_REFERENCE (type)) {
3372 ttype = MONO_TYPE_OBJECT;
3373 } else if (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) {
3374 if (type->data.generic_param->gshared_constraint)
3375 return mini_get_shared_gparam (t, type->data.generic_param->gshared_constraint);
3376 ttype = MONO_TYPE_OBJECT;
3380 MonoType t2;
3381 MonoClass *klass;
3383 memset (&t2, 0, sizeof (t2));
3384 t2.type = ttype;
3385 klass = mono_class_from_mono_type (&t2);
3387 return mini_get_shared_gparam (t, &klass->byval_arg);
3391 static MonoType*
3392 get_gsharedvt_type (MonoType *t)
3394 /* Use TypeHandle as the constraint type since its a valuetype */
3395 return mini_get_shared_gparam (t, &mono_defaults.typehandle_class->byval_arg);
3398 static MonoGenericInst*
3399 get_shared_inst (MonoGenericInst *inst, MonoGenericInst *shared_inst, MonoGenericContainer *container, gboolean all_vt, gboolean gsharedvt, gboolean partial)
3401 MonoGenericInst *res;
3402 MonoType **type_argv;
3403 int i;
3405 type_argv = g_new0 (MonoType*, inst->type_argc);
3406 for (i = 0; i < inst->type_argc; ++i) {
3407 if (all_vt || gsharedvt) {
3408 type_argv [i] = get_gsharedvt_type (shared_inst->type_argv [i]);
3409 } else {
3410 /* These types match the ones in mini_generic_inst_is_sharable () */
3411 type_argv [i] = get_shared_type (shared_inst->type_argv [i], inst->type_argv [i]);
3415 res = mono_metadata_get_generic_inst (inst->type_argc, type_argv);
3416 g_free (type_argv);
3417 return res;
3421 * mini_get_shared_method_full:
3423 * Return the method which is actually compiled/registered when doing generic sharing.
3424 * If ALL_VT is true, return the shared method belonging to an all-vtype instantiation.
3425 * If IS_GSHAREDVT is true, treat METHOD as a gsharedvt method even if it fails some constraints.
3426 * METHOD can be a non-inflated generic method.
3428 MonoMethod*
3429 mini_get_shared_method_full (MonoMethod *method, gboolean all_vt, gboolean is_gsharedvt)
3431 MonoError error;
3432 MonoGenericContext shared_context;
3433 MonoMethod *declaring_method, *res;
3434 gboolean partial = FALSE;
3435 gboolean gsharedvt = FALSE;
3436 MonoGenericContainer *class_container, *method_container = NULL;
3437 MonoGenericContext *context = mono_method_get_context (method);
3438 MonoGenericInst *inst;
3441 * Instead of creating a shared version of the wrapper, create a shared version of the original
3442 * method and construct a wrapper for it. Otherwise, we could end up with two copies of the
3443 * same wrapper, breaking AOT which assumes wrappers are unique.
3444 * FIXME: Add other cases.
3446 if (method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED) {
3447 MonoMethod *wrapper = mono_marshal_method_from_wrapper (method);
3449 return mono_marshal_get_synchronized_wrapper (mini_get_shared_method_full (wrapper, all_vt, is_gsharedvt));
3451 if (method->wrapper_type == MONO_WRAPPER_DELEGATE_INVOKE) {
3452 WrapperInfo *info = mono_marshal_get_wrapper_info (method);
3454 if (info->subtype == WRAPPER_SUBTYPE_NONE) {
3455 MonoMethod *m = mono_marshal_get_delegate_invoke (mini_get_shared_method_full (info->d.delegate_invoke.method, all_vt, is_gsharedvt), NULL);
3456 return m;
3460 if (method->is_generic || (mono_class_is_gtd (method->klass) && !method->is_inflated)) {
3461 declaring_method = method;
3462 } else {
3463 declaring_method = mono_method_get_declaring_generic_method (method);
3466 /* shared_context is the context containing type variables. */
3467 if (declaring_method->is_generic)
3468 shared_context = mono_method_get_generic_container (declaring_method)->context;
3469 else
3470 shared_context = mono_class_get_generic_container (declaring_method->klass)->context;
3472 if (!is_gsharedvt)
3473 partial = mono_method_is_generic_sharable_full (method, FALSE, TRUE, FALSE);
3475 gsharedvt = is_gsharedvt || (!partial && mini_is_gsharedvt_sharable_method (method));
3477 class_container = mono_class_try_get_generic_container (declaring_method->klass); //FIXME is this a case for a try_get?
3478 method_container = mono_method_get_generic_container (declaring_method);
3481 * Create the shared context by replacing the ref type arguments with
3482 * type parameters, and keeping the rest.
3484 if (context)
3485 inst = context->class_inst;
3486 else
3487 inst = shared_context.class_inst;
3488 if (inst)
3489 shared_context.class_inst = get_shared_inst (inst, shared_context.class_inst, class_container, all_vt, gsharedvt, partial);
3491 if (context)
3492 inst = context->method_inst;
3493 else
3494 inst = shared_context.method_inst;
3495 if (inst)
3496 shared_context.method_inst = get_shared_inst (inst, shared_context.method_inst, method_container, all_vt, gsharedvt, partial);
3498 res = mono_class_inflate_generic_method_checked (declaring_method, &shared_context, &error);
3499 g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */
3501 //printf ("%s\n", mono_method_full_name (res, 1));
3503 return res;
3506 MonoMethod*
3507 mini_get_shared_method (MonoMethod *method)
3509 return mini_get_shared_method_full (method, FALSE, FALSE);
3513 mini_get_rgctx_entry_slot (MonoJumpInfoRgctxEntry *entry)
3515 guint32 slot = -1;
3517 switch (entry->data->type) {
3518 case MONO_PATCH_INFO_CLASS:
3519 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));
3520 break;
3521 case MONO_PATCH_INFO_METHOD:
3522 case MONO_PATCH_INFO_METHODCONST:
3523 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));
3524 break;
3525 case MONO_PATCH_INFO_FIELD:
3526 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));
3527 break;
3528 case MONO_PATCH_INFO_SIGNATURE:
3529 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));
3530 break;
3531 case MONO_PATCH_INFO_GSHAREDVT_CALL: {
3532 MonoJumpInfoGSharedVtCall *call_info = (MonoJumpInfoGSharedVtCall *)g_malloc0 (sizeof (MonoJumpInfoGSharedVtCall)); //mono_domain_alloc0 (domain, sizeof (MonoJumpInfoGSharedVtCall));
3534 memcpy (call_info, entry->data->data.gsharedvt, sizeof (MonoJumpInfoGSharedVtCall));
3535 slot = mono_method_lookup_or_register_info (entry->method, entry->in_mrgctx, call_info, entry->info_type, mono_method_get_context (entry->method));
3536 break;
3538 case MONO_PATCH_INFO_GSHAREDVT_METHOD: {
3539 MonoGSharedVtMethodInfo *info;
3540 MonoGSharedVtMethodInfo *oinfo = entry->data->data.gsharedvt_method;
3541 int i;
3543 /* Make a copy into the domain mempool */
3544 info = (MonoGSharedVtMethodInfo *)g_malloc0 (sizeof (MonoGSharedVtMethodInfo)); //mono_domain_alloc0 (domain, sizeof (MonoGSharedVtMethodInfo));
3545 info->method = oinfo->method;
3546 info->num_entries = oinfo->num_entries;
3547 info->entries = (MonoRuntimeGenericContextInfoTemplate *)g_malloc0 (sizeof (MonoRuntimeGenericContextInfoTemplate) * info->num_entries);
3548 for (i = 0; i < oinfo->num_entries; ++i) {
3549 MonoRuntimeGenericContextInfoTemplate *otemplate = &oinfo->entries [i];
3550 MonoRuntimeGenericContextInfoTemplate *template_ = &info->entries [i];
3552 memcpy (template_, otemplate, sizeof (MonoRuntimeGenericContextInfoTemplate));
3554 slot = mono_method_lookup_or_register_info (entry->method, entry->in_mrgctx, info, entry->info_type, mono_method_get_context (entry->method));
3555 break;
3557 case MONO_PATCH_INFO_VIRT_METHOD: {
3558 MonoJumpInfoVirtMethod *info;
3559 MonoJumpInfoVirtMethod *oinfo = entry->data->data.virt_method;
3561 info = (MonoJumpInfoVirtMethod *)g_malloc0 (sizeof (MonoJumpInfoVirtMethod));
3562 memcpy (info, oinfo, sizeof (MonoJumpInfoVirtMethod));
3563 slot = mono_method_lookup_or_register_info (entry->method, entry->in_mrgctx, info, entry->info_type, mono_method_get_context (entry->method));
3564 break;
3566 default:
3567 g_assert_not_reached ();
3568 break;
3571 return slot;
3574 static gboolean gsharedvt_supported;
3576 void
3577 mono_set_generic_sharing_vt_supported (gboolean supported)
3579 gsharedvt_supported = supported;
3582 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
3585 * mini_is_gsharedvt_type:
3587 * Return whenever T references type arguments instantiated with gshared vtypes.
3589 gboolean
3590 mini_is_gsharedvt_type (MonoType *t)
3592 int i;
3594 if (t->byref)
3595 return FALSE;
3596 if ((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)
3597 return TRUE;
3598 else if (t->type == MONO_TYPE_GENERICINST) {
3599 MonoGenericClass *gclass = t->data.generic_class;
3600 MonoGenericContext *context = &gclass->context;
3601 MonoGenericInst *inst;
3603 inst = context->class_inst;
3604 if (inst) {
3605 for (i = 0; i < inst->type_argc; ++i)
3606 if (mini_is_gsharedvt_type (inst->type_argv [i]))
3607 return TRUE;
3609 inst = context->method_inst;
3610 if (inst) {
3611 for (i = 0; i < inst->type_argc; ++i)
3612 if (mini_is_gsharedvt_type (inst->type_argv [i]))
3613 return TRUE;
3616 return FALSE;
3617 } else {
3618 return FALSE;
3622 gboolean
3623 mini_is_gsharedvt_klass (MonoClass *klass)
3625 return mini_is_gsharedvt_type (&klass->byval_arg);
3628 gboolean
3629 mini_is_gsharedvt_signature (MonoMethodSignature *sig)
3631 int i;
3633 if (sig->ret && mini_is_gsharedvt_type (sig->ret))
3634 return TRUE;
3635 for (i = 0; i < sig->param_count; ++i) {
3636 if (mini_is_gsharedvt_type (sig->params [i]))
3637 return TRUE;
3639 return FALSE;
3643 * mini_is_gsharedvt_variable_type:
3645 * Return whenever T refers to a GSHAREDVT type whose size differs depending on the values of type parameters.
3647 gboolean
3648 mini_is_gsharedvt_variable_type (MonoType *t)
3650 if (!mini_is_gsharedvt_type (t))
3651 return FALSE;
3652 if (t->type == MONO_TYPE_GENERICINST) {
3653 MonoGenericClass *gclass = t->data.generic_class;
3654 MonoGenericContext *context = &gclass->context;
3655 MonoGenericInst *inst;
3656 int i;
3658 if (t->data.generic_class->container_class->byval_arg.type != MONO_TYPE_VALUETYPE || t->data.generic_class->container_class->enumtype)
3659 return FALSE;
3661 inst = context->class_inst;
3662 if (inst) {
3663 for (i = 0; i < inst->type_argc; ++i)
3664 if (mini_is_gsharedvt_variable_type (inst->type_argv [i]))
3665 return TRUE;
3667 inst = context->method_inst;
3668 if (inst) {
3669 for (i = 0; i < inst->type_argc; ++i)
3670 if (mini_is_gsharedvt_variable_type (inst->type_argv [i]))
3671 return TRUE;
3674 return FALSE;
3676 return TRUE;
3679 static gboolean
3680 is_variable_size (MonoType *t)
3682 int i;
3684 if (t->byref)
3685 return FALSE;
3687 if (t->type == MONO_TYPE_VAR || t->type == MONO_TYPE_MVAR) {
3688 MonoGenericParam *param = t->data.generic_param;
3690 if (param->gshared_constraint && param->gshared_constraint->type != MONO_TYPE_VALUETYPE && param->gshared_constraint->type != MONO_TYPE_GENERICINST)
3691 return FALSE;
3692 if (param->gshared_constraint && param->gshared_constraint->type == MONO_TYPE_GENERICINST)
3693 return is_variable_size (param->gshared_constraint);
3694 return TRUE;
3696 if (t->type == MONO_TYPE_GENERICINST && t->data.generic_class->container_class->byval_arg.type == MONO_TYPE_VALUETYPE) {
3697 MonoGenericClass *gclass = t->data.generic_class;
3698 MonoGenericContext *context = &gclass->context;
3699 MonoGenericInst *inst;
3701 inst = context->class_inst;
3702 if (inst) {
3703 for (i = 0; i < inst->type_argc; ++i)
3704 if (is_variable_size (inst->type_argv [i]))
3705 return TRUE;
3707 inst = context->method_inst;
3708 if (inst) {
3709 for (i = 0; i < inst->type_argc; ++i)
3710 if (is_variable_size (inst->type_argv [i]))
3711 return TRUE;
3715 return FALSE;
3718 gboolean
3719 mini_is_gsharedvt_sharable_inst (MonoGenericInst *inst)
3721 int i;
3722 gboolean has_vt = FALSE;
3724 for (i = 0; i < inst->type_argc; ++i) {
3725 MonoType *type = inst->type_argv [i];
3727 if ((MONO_TYPE_IS_REFERENCE (type) || type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) && !mini_is_gsharedvt_type (type)) {
3728 } else {
3729 has_vt = TRUE;
3733 return has_vt;
3736 gboolean
3737 mini_is_gsharedvt_sharable_method (MonoMethod *method)
3739 MonoMethodSignature *sig;
3742 * A method is gsharedvt if:
3743 * - it has type parameters instantiated with vtypes
3745 if (!gsharedvt_supported)
3746 return FALSE;
3747 if (method->is_inflated) {
3748 MonoMethodInflated *inflated = (MonoMethodInflated*)method;
3749 MonoGenericContext *context = &inflated->context;
3750 MonoGenericInst *inst;
3752 if (context->class_inst && context->method_inst) {
3753 /* At least one inst has to be gsharedvt sharable, and the other normal or gsharedvt sharable */
3754 gboolean vt1 = mini_is_gsharedvt_sharable_inst (context->class_inst);
3755 gboolean vt2 = mini_is_gsharedvt_sharable_inst (context->method_inst);
3757 if ((vt1 && vt2) ||
3758 (vt1 && mini_generic_inst_is_sharable (context->method_inst, TRUE, FALSE)) ||
3759 (vt2 && mini_generic_inst_is_sharable (context->class_inst, TRUE, FALSE)))
3761 else
3762 return FALSE;
3763 } else {
3764 inst = context->class_inst;
3765 if (inst && !mini_is_gsharedvt_sharable_inst (inst))
3766 return FALSE;
3767 inst = context->method_inst;
3768 if (inst && !mini_is_gsharedvt_sharable_inst (inst))
3769 return FALSE;
3771 } else {
3772 return FALSE;
3775 sig = mono_method_signature (mono_method_get_declaring_generic_method (method));
3776 if (!sig)
3777 return FALSE;
3780 if (mini_is_gsharedvt_variable_signature (sig))
3781 return FALSE;
3784 //DEBUG ("GSHAREDVT SHARABLE: %s\n", mono_method_full_name (method, TRUE));
3786 return TRUE;
3790 * mini_is_gsharedvt_variable_signature:
3792 * Return whenever the calling convention used to call SIG varies depending on the values of type parameters used by SIG,
3793 * i.e. FALSE for swap(T[] arr, int i, int j), TRUE for T get_t ().
3795 gboolean
3796 mini_is_gsharedvt_variable_signature (MonoMethodSignature *sig)
3798 int i;
3800 if (sig->ret && is_variable_size (sig->ret))
3801 return TRUE;
3802 for (i = 0; i < sig->param_count; ++i) {
3803 MonoType *t = sig->params [i];
3805 if (is_variable_size (t))
3806 return TRUE;
3808 return FALSE;
3810 #else
3812 gboolean
3813 mini_is_gsharedvt_type (MonoType *t)
3815 return FALSE;
3818 gboolean
3819 mini_is_gsharedvt_klass (MonoClass *klass)
3821 return FALSE;
3824 gboolean
3825 mini_is_gsharedvt_signature (MonoMethodSignature *sig)
3827 return FALSE;
3830 gboolean
3831 mini_is_gsharedvt_variable_type (MonoType *t)
3833 return FALSE;
3836 gboolean
3837 mini_is_gsharedvt_sharable_method (MonoMethod *method)
3839 return FALSE;
3842 gboolean
3843 mini_is_gsharedvt_variable_signature (MonoMethodSignature *sig)
3845 return FALSE;
3848 #endif /* !MONO_ARCH_GSHAREDVT_SUPPORTED */