[interp] Fix native to interp transition (#6868)
[mono-project.git] / mono / mini / mini-generic-sharing.c
blob014085b7bc9a5623945038427b430c4a60c48ddf
1 /**
2 * \file
3 * Support functions for generic sharing.
5 * Author:
6 * Mark Probst (mark.probst@gmail.com)
8 * Copyright 2007-2011 Novell, Inc (http://www.novell.com)
9 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
10 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
13 #include <config.h>
15 #include <mono/metadata/class.h>
16 #include <mono/metadata/method-builder.h>
17 #include <mono/metadata/reflection-internals.h>
18 #include <mono/utils/mono-counters.h>
19 #include <mono/utils/atomic.h>
20 #include <mono/utils/unlocked.h>
22 #include "mini.h"
23 #include "aot-runtime.h"
24 #include "mini-runtime.h"
26 #define ALLOW_PARTIAL_SHARING TRUE
27 //#define ALLOW_PARTIAL_SHARING FALSE
29 #if 0
30 #define DEBUG(...) __VA_ARGS__
31 #else
32 #define DEBUG(...)
33 #endif
35 static void
36 mono_class_unregister_image_generic_subclasses (MonoImage *image, gpointer user_data);
38 /* Counters */
39 static gint32 rgctx_template_num_allocated;
40 static gint32 rgctx_template_bytes_allocated;
41 static gint32 rgctx_oti_num_allocated;
42 static gint32 rgctx_oti_bytes_allocated;
43 static gint32 rgctx_oti_num_markers;
44 static gint32 rgctx_oti_num_data;
45 static gint32 rgctx_max_slot_number;
46 static gint32 rgctx_num_allocated;
47 static gint32 rgctx_num_arrays_allocated;
48 static gint32 rgctx_bytes_allocated;
49 static gint32 mrgctx_num_arrays_allocated;
50 static gint32 mrgctx_bytes_allocated;
51 static gint32 gsharedvt_num_trampolines;
53 #define gshared_lock() mono_os_mutex_lock (&gshared_mutex)
54 #define gshared_unlock() mono_os_mutex_unlock (&gshared_mutex)
55 static mono_mutex_t gshared_mutex;
57 static gboolean partial_supported = FALSE;
59 static inline gboolean
60 partial_sharing_supported (void)
62 if (!ALLOW_PARTIAL_SHARING)
63 return FALSE;
64 /* Enable this when AOT compiling or running in full-aot mode */
65 if (mono_aot_only)
66 return TRUE;
67 if (partial_supported)
68 return TRUE;
69 return FALSE;
72 static int
73 type_check_context_used (MonoType *type, gboolean recursive)
75 switch (mono_type_get_type (type)) {
76 case MONO_TYPE_VAR:
77 return MONO_GENERIC_CONTEXT_USED_CLASS;
78 case MONO_TYPE_MVAR:
79 return MONO_GENERIC_CONTEXT_USED_METHOD;
80 case MONO_TYPE_SZARRAY:
81 return mono_class_check_context_used (mono_type_get_class (type));
82 case MONO_TYPE_ARRAY:
83 return mono_class_check_context_used (mono_type_get_array_type (type)->eklass);
84 case MONO_TYPE_CLASS:
85 if (recursive)
86 return mono_class_check_context_used (mono_type_get_class (type));
87 else
88 return 0;
89 case MONO_TYPE_GENERICINST:
90 if (recursive) {
91 MonoGenericClass *gclass = type->data.generic_class;
93 g_assert (mono_class_is_gtd (gclass->container_class));
94 return mono_generic_context_check_used (&gclass->context);
95 } else {
96 return 0;
98 default:
99 return 0;
103 static int
104 inst_check_context_used (MonoGenericInst *inst)
106 int context_used = 0;
107 int i;
109 if (!inst)
110 return 0;
112 for (i = 0; i < inst->type_argc; ++i)
113 context_used |= type_check_context_used (inst->type_argv [i], TRUE);
115 return context_used;
119 * mono_generic_context_check_used:
120 * @context: a generic context
122 * Checks whether the context uses a type variable. Returns an int
123 * with the bit MONO_GENERIC_CONTEXT_USED_CLASS set to reflect whether
124 * the context's class instantiation uses type variables.
127 mono_generic_context_check_used (MonoGenericContext *context)
129 int context_used = 0;
131 context_used |= inst_check_context_used (context->class_inst);
132 context_used |= inst_check_context_used (context->method_inst);
134 return context_used;
138 * mono_class_check_context_used:
139 * @class: a class
141 * Checks whether the class's generic context uses a type variable.
142 * Returns an int with the bit MONO_GENERIC_CONTEXT_USED_CLASS set to
143 * reflect whether the context's class instantiation uses type
144 * variables.
147 mono_class_check_context_used (MonoClass *klass)
149 int context_used = 0;
151 context_used |= type_check_context_used (&klass->this_arg, FALSE);
152 context_used |= type_check_context_used (&klass->byval_arg, FALSE);
154 if (mono_class_is_ginst (klass))
155 context_used |= mono_generic_context_check_used (&mono_class_get_generic_class (klass)->context);
156 else if (mono_class_is_gtd (klass))
157 context_used |= mono_generic_context_check_used (&mono_class_get_generic_container (klass)->context);
159 return context_used;
163 * LOCKING: loader lock
165 static MonoRuntimeGenericContextInfoTemplate*
166 get_info_templates (MonoRuntimeGenericContextTemplate *template_, int type_argc)
168 g_assert (type_argc >= 0);
169 if (type_argc == 0)
170 return template_->infos;
171 return (MonoRuntimeGenericContextInfoTemplate *)g_slist_nth_data (template_->method_templates, type_argc - 1);
175 * LOCKING: loader lock
177 static void
178 set_info_templates (MonoImage *image, MonoRuntimeGenericContextTemplate *template_, int type_argc,
179 MonoRuntimeGenericContextInfoTemplate *oti)
181 g_assert (type_argc >= 0);
182 if (type_argc == 0)
183 template_->infos = oti;
184 else {
185 int length = g_slist_length (template_->method_templates);
186 GSList *list;
188 /* FIXME: quadratic! */
189 while (length < type_argc) {
190 template_->method_templates = g_slist_append_image (image, template_->method_templates, NULL);
191 length++;
194 list = g_slist_nth (template_->method_templates, type_argc - 1);
195 g_assert (list);
196 list->data = oti;
201 * LOCKING: loader lock
203 static int
204 template_get_max_argc (MonoRuntimeGenericContextTemplate *template_)
206 return g_slist_length (template_->method_templates);
210 * LOCKING: loader lock
212 static MonoRuntimeGenericContextInfoTemplate*
213 rgctx_template_get_other_slot (MonoRuntimeGenericContextTemplate *template_, int type_argc, int slot)
215 int i;
216 MonoRuntimeGenericContextInfoTemplate *oti;
218 g_assert (slot >= 0);
220 for (oti = get_info_templates (template_, type_argc), i = 0; i < slot; oti = oti->next, ++i) {
221 if (!oti)
222 return NULL;
225 return oti;
229 * LOCKING: loader lock
231 static int
232 rgctx_template_num_infos (MonoRuntimeGenericContextTemplate *template_, int type_argc)
234 MonoRuntimeGenericContextInfoTemplate *oti;
235 int i;
237 for (i = 0, oti = get_info_templates (template_, type_argc); oti; ++i, oti = oti->next)
240 return i;
243 /* Maps from uninstantiated generic classes to GList's of
244 * uninstantiated generic classes whose parent is the key class or an
245 * instance of the key class.
247 * LOCKING: loader lock
249 static GHashTable *generic_subclass_hash;
252 * LOCKING: templates lock
254 static void
255 class_set_rgctx_template (MonoClass *klass, MonoRuntimeGenericContextTemplate *rgctx_template)
257 if (!klass->image->rgctx_template_hash)
258 klass->image->rgctx_template_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
260 g_hash_table_insert (klass->image->rgctx_template_hash, klass, rgctx_template);
264 * LOCKING: loader lock
266 static MonoRuntimeGenericContextTemplate*
267 class_lookup_rgctx_template (MonoClass *klass)
269 MonoRuntimeGenericContextTemplate *template_;
271 if (!klass->image->rgctx_template_hash)
272 return NULL;
274 template_ = (MonoRuntimeGenericContextTemplate *)g_hash_table_lookup (klass->image->rgctx_template_hash, klass);
276 return template_;
280 * LOCKING: loader lock
282 static void
283 register_generic_subclass (MonoClass *klass)
285 MonoClass *parent = klass->parent;
286 MonoClass *subclass;
287 MonoRuntimeGenericContextTemplate *rgctx_template = class_lookup_rgctx_template (klass);
289 g_assert (rgctx_template);
291 if (mono_class_is_ginst (parent))
292 parent = mono_class_get_generic_class (parent)->container_class;
294 if (!generic_subclass_hash)
295 generic_subclass_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
297 subclass = (MonoClass *)g_hash_table_lookup (generic_subclass_hash, parent);
298 rgctx_template->next_subclass = subclass;
299 g_hash_table_insert (generic_subclass_hash, parent, klass);
302 static void
303 move_subclasses_not_in_image_foreach_func (MonoClass *klass, MonoClass *subclass, MonoImage *image)
305 MonoClass *new_list;
307 if (klass->image == image) {
308 /* The parent class itself is in the image, so all the
309 subclasses must be in the image, too. If not,
310 we're removing an image containing a class which
311 still has a subclass in another image. */
313 while (subclass) {
314 g_assert (subclass->image == image);
315 subclass = class_lookup_rgctx_template (subclass)->next_subclass;
318 return;
321 new_list = NULL;
322 while (subclass) {
323 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
324 MonoClass *next = subclass_template->next_subclass;
326 if (subclass->image != image) {
327 subclass_template->next_subclass = new_list;
328 new_list = subclass;
331 subclass = next;
334 if (new_list)
335 g_hash_table_insert (generic_subclass_hash, klass, new_list);
339 * mono_class_unregister_image_generic_subclasses:
340 * @image: an image
342 * Removes all classes of the image from the generic subclass hash.
343 * Must be called when an image is unloaded.
345 static void
346 mono_class_unregister_image_generic_subclasses (MonoImage *image, gpointer user_data)
348 GHashTable *old_hash;
350 //g_print ("unregistering image %s\n", image->name);
352 if (!generic_subclass_hash)
353 return;
355 mono_loader_lock ();
357 old_hash = generic_subclass_hash;
358 generic_subclass_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
360 g_hash_table_foreach (old_hash, (GHFunc)move_subclasses_not_in_image_foreach_func, image);
362 mono_loader_unlock ();
364 g_hash_table_destroy (old_hash);
367 static MonoRuntimeGenericContextTemplate*
368 alloc_template (MonoClass *klass)
370 gint32 size = sizeof (MonoRuntimeGenericContextTemplate);
372 mono_atomic_inc_i32 (&rgctx_template_num_allocated);
373 mono_atomic_fetch_add_i32 (&rgctx_template_bytes_allocated, size);
375 return (MonoRuntimeGenericContextTemplate *)mono_image_alloc0 (klass->image, size);
378 /* LOCKING: Takes the loader lock */
379 static MonoRuntimeGenericContextInfoTemplate*
380 alloc_oti (MonoImage *image)
382 gint32 size = sizeof (MonoRuntimeGenericContextInfoTemplate);
384 mono_atomic_inc_i32 (&rgctx_oti_num_allocated);
385 mono_atomic_fetch_add_i32 (&rgctx_oti_bytes_allocated, size);
387 return (MonoRuntimeGenericContextInfoTemplate *)mono_image_alloc0 (image, size);
390 #define MONO_RGCTX_SLOT_USED_MARKER ((gpointer)&mono_defaults.object_class->byval_arg)
393 * Return true if this info type has the notion of identify.
395 * Some info types expect that each insert results in a new slot been assigned.
397 static int
398 info_has_identity (MonoRgctxInfoType info_type)
400 return info_type != MONO_RGCTX_INFO_CAST_CACHE;
404 * LOCKING: loader lock
406 #if defined(HOST_ANDROID) && defined(TARGET_ARM)
407 /* work around for HW bug on Nexus9 when running on armv7 */
408 #ifdef __clang__
409 static __attribute__ ((optnone)) void
410 #else
411 /* gcc */
412 static __attribute__ ((optimize("O0"))) void
413 #endif
414 #else
415 static void
416 #endif
417 rgctx_template_set_slot (MonoImage *image, MonoRuntimeGenericContextTemplate *template_, int type_argc,
418 int slot, gpointer data, MonoRgctxInfoType info_type)
420 int i;
421 MonoRuntimeGenericContextInfoTemplate *list = get_info_templates (template_, type_argc);
422 MonoRuntimeGenericContextInfoTemplate **oti = &list;
424 g_assert (slot >= 0);
425 g_assert (data);
427 i = 0;
428 while (i <= slot) {
429 if (i > 0)
430 oti = &(*oti)->next;
431 if (!*oti)
432 *oti = alloc_oti (image);
433 ++i;
436 g_assert (!(*oti)->data);
437 (*oti)->data = data;
438 (*oti)->info_type = info_type;
440 set_info_templates (image, template_, type_argc, list);
442 /* interlocked by loader lock (by definition) */
443 if (data == MONO_RGCTX_SLOT_USED_MARKER)
444 UnlockedIncrement (&rgctx_oti_num_markers);
445 else
446 UnlockedIncrement (&rgctx_oti_num_data);
450 * mono_method_get_declaring_generic_method:
451 * @method: an inflated method
453 * Returns an inflated method's declaring method.
455 MonoMethod*
456 mono_method_get_declaring_generic_method (MonoMethod *method)
458 MonoMethodInflated *inflated;
460 g_assert (method->is_inflated);
462 inflated = (MonoMethodInflated*)method;
464 return inflated->declaring;
468 * mono_class_get_method_generic:
469 * @klass: a class
470 * @method: a method
471 * @error: set on error
473 * Given a class and a generic method, which has to be of an
474 * instantiation of the same class that klass is an instantiation of,
475 * returns the corresponding method in klass. Example:
477 * klass is Gen<string>
478 * method is Gen<object>.work<int>
480 * returns: Gen<string>.work<int>
482 * On error sets @error and returns NULL.
484 MonoMethod*
485 mono_class_get_method_generic (MonoClass *klass, MonoMethod *method, MonoError *error)
487 MonoMethod *declaring, *m;
488 int i;
490 if (method->is_inflated)
491 declaring = mono_method_get_declaring_generic_method (method);
492 else
493 declaring = method;
495 m = NULL;
496 if (mono_class_is_ginst (klass)) {
497 m = mono_class_get_inflated_method (klass, declaring, error);
498 return_val_if_nok (error, NULL);
501 if (!m) {
502 mono_class_setup_methods (klass);
503 if (mono_class_has_failure (klass))
504 return NULL;
505 int mcount = mono_class_get_method_count (klass);
506 for (i = 0; i < mcount; ++i) {
507 m = klass->methods [i];
508 if (m == declaring)
509 break;
510 if (m->is_inflated && mono_method_get_declaring_generic_method (m) == declaring)
511 break;
513 if (i >= mcount)
514 return NULL;
517 if (method != declaring) {
518 MonoGenericContext context;
520 context.class_inst = NULL;
521 context.method_inst = mono_method_get_context (method)->method_inst;
523 m = mono_class_inflate_generic_method_checked (m, &context, error);
524 return_val_if_nok (error, NULL);
527 return m;
530 static gpointer
531 inflate_info (MonoRuntimeGenericContextInfoTemplate *oti, MonoGenericContext *context, MonoClass *klass, gboolean temporary)
533 gpointer data = oti->data;
534 MonoRgctxInfoType info_type = oti->info_type;
535 ERROR_DECL (error);
537 g_assert (data);
539 if (data == MONO_RGCTX_SLOT_USED_MARKER)
540 return MONO_RGCTX_SLOT_USED_MARKER;
542 switch (info_type)
544 case MONO_RGCTX_INFO_STATIC_DATA:
545 case MONO_RGCTX_INFO_KLASS:
546 case MONO_RGCTX_INFO_ELEMENT_KLASS:
547 case MONO_RGCTX_INFO_VTABLE:
548 case MONO_RGCTX_INFO_TYPE:
549 case MONO_RGCTX_INFO_REFLECTION_TYPE:
550 case MONO_RGCTX_INFO_CAST_CACHE:
551 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
552 case MONO_RGCTX_INFO_VALUE_SIZE:
553 case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
554 case MONO_RGCTX_INFO_CLASS_IS_REF_OR_CONTAINS_REFS:
555 case MONO_RGCTX_INFO_MEMCPY:
556 case MONO_RGCTX_INFO_BZERO:
557 case MONO_RGCTX_INFO_LOCAL_OFFSET:
558 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
559 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: {
560 gpointer result = mono_class_inflate_generic_type_with_mempool (temporary ? NULL : klass->image,
561 (MonoType *)data, context, error);
562 mono_error_assert_msg_ok (error, "Could not inflate generic type"); /* FIXME proper error handling */
563 return result;
566 case MONO_RGCTX_INFO_METHOD:
567 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
568 case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER:
569 case MONO_RGCTX_INFO_METHOD_RGCTX:
570 case MONO_RGCTX_INFO_METHOD_CONTEXT:
571 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
572 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE: {
573 MonoMethod *method = (MonoMethod *)data;
574 MonoMethod *inflated_method;
575 MonoType *inflated_type = mono_class_inflate_generic_type_checked (&method->klass->byval_arg, context, error);
576 mono_error_assert_ok (error); /* FIXME don't swallow the error */
578 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
580 mono_metadata_free_type (inflated_type);
582 mono_class_init (inflated_class);
584 g_assert (!method->wrapper_type);
586 if (inflated_class->byval_arg.type == MONO_TYPE_ARRAY ||
587 inflated_class->byval_arg.type == MONO_TYPE_SZARRAY) {
588 inflated_method = mono_method_search_in_array_class (inflated_class,
589 method->name, method->signature);
590 } else {
591 ERROR_DECL (error);
592 inflated_method = mono_class_inflate_generic_method_checked (method, context, error);
593 g_assert (mono_error_ok (error)); /* FIXME don't swallow the error */
595 mono_class_init (inflated_method->klass);
596 g_assert (inflated_method->klass == inflated_class);
597 return inflated_method;
599 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO: {
600 MonoGSharedVtMethodInfo *oinfo = (MonoGSharedVtMethodInfo *)data;
601 MonoGSharedVtMethodInfo *res;
602 MonoDomain *domain = mono_domain_get ();
603 int i;
605 res = (MonoGSharedVtMethodInfo *)mono_domain_alloc0 (domain, sizeof (MonoGSharedVtMethodInfo));
607 res->nlocals = info->nlocals;
608 res->locals_types = g_new0 (MonoType*, info->nlocals);
609 for (i = 0; i < info->nlocals; ++i)
610 res->locals_types [i] = mono_class_inflate_generic_type (info->locals_types [i], context);
612 res->num_entries = oinfo->num_entries;
613 res->entries = (MonoRuntimeGenericContextInfoTemplate *)mono_domain_alloc0 (domain, sizeof (MonoRuntimeGenericContextInfoTemplate) * oinfo->num_entries);
614 for (i = 0; i < oinfo->num_entries; ++i) {
615 MonoRuntimeGenericContextInfoTemplate *otemplate = &oinfo->entries [i];
616 MonoRuntimeGenericContextInfoTemplate *template_ = &res->entries [i];
618 memcpy (template_, otemplate, sizeof (MonoRuntimeGenericContextInfoTemplate));
619 template_->data = inflate_info (template_, context, klass, FALSE);
621 return res;
623 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE:
624 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT: {
625 MonoJumpInfoGSharedVtCall *info = (MonoJumpInfoGSharedVtCall *)data;
626 MonoMethod *method = info->method;
627 MonoMethod *inflated_method;
628 MonoType *inflated_type = mono_class_inflate_generic_type_checked (&method->klass->byval_arg, context, error);
629 mono_error_assert_ok (error); /* FIXME don't swallow the error */
630 WrapperInfo *winfo = NULL;
632 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
633 MonoJumpInfoGSharedVtCall *res;
634 MonoDomain *domain = mono_domain_get ();
636 res = (MonoJumpInfoGSharedVtCall *)mono_domain_alloc0 (domain, sizeof (MonoJumpInfoGSharedVtCall));
637 /* Keep the original signature */
638 res->sig = info->sig;
640 mono_metadata_free_type (inflated_type);
642 mono_class_init (inflated_class);
644 if (method->wrapper_type) {
645 winfo = mono_marshal_get_wrapper_info (method);
647 g_assert (winfo);
648 g_assert (winfo->subtype == WRAPPER_SUBTYPE_SYNCHRONIZED_INNER);
649 method = winfo->d.synchronized_inner.method;
652 if (inflated_class->byval_arg.type == MONO_TYPE_ARRAY ||
653 inflated_class->byval_arg.type == MONO_TYPE_SZARRAY) {
654 inflated_method = mono_method_search_in_array_class (inflated_class,
655 method->name, method->signature);
656 } else {
657 ERROR_DECL (error);
658 inflated_method = mono_class_inflate_generic_method_checked (method, context, error);
659 g_assert (mono_error_ok (error)); /* FIXME don't swallow the error */
661 mono_class_init (inflated_method->klass);
662 g_assert (inflated_method->klass == inflated_class);
664 if (winfo) {
665 g_assert (winfo->subtype == WRAPPER_SUBTYPE_SYNCHRONIZED_INNER);
666 inflated_method = mono_marshal_get_synchronized_inner_wrapper (inflated_method);
669 res->method = inflated_method;
671 return res;
674 case MONO_RGCTX_INFO_CLASS_FIELD:
675 case MONO_RGCTX_INFO_FIELD_OFFSET: {
676 ERROR_DECL (error);
677 MonoClassField *field = (MonoClassField *)data;
678 MonoType *inflated_type = mono_class_inflate_generic_type_checked (&field->parent->byval_arg, context, error);
679 mono_error_assert_ok (error); /* FIXME don't swallow the error */
681 MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
682 int i = field - field->parent->fields;
683 gpointer dummy = NULL;
685 mono_metadata_free_type (inflated_type);
687 mono_class_get_fields (inflated_class, &dummy);
688 g_assert (inflated_class->fields);
690 return &inflated_class->fields [i];
692 case MONO_RGCTX_INFO_SIG_GSHAREDVT_IN_TRAMPOLINE_CALLI:
693 case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI: {
694 MonoMethodSignature *sig = (MonoMethodSignature *)data;
695 MonoMethodSignature *isig;
696 ERROR_DECL (error);
698 isig = mono_inflate_generic_signature (sig, context, error);
699 g_assert (mono_error_ok (error));
700 return isig;
702 case MONO_RGCTX_INFO_VIRT_METHOD_CODE:
703 case MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE: {
704 MonoJumpInfoVirtMethod *info = (MonoJumpInfoVirtMethod *)data;
705 MonoJumpInfoVirtMethod *res;
706 MonoType *t;
707 MonoDomain *domain = mono_domain_get ();
708 ERROR_DECL (error);
710 // FIXME: Temporary
711 res = (MonoJumpInfoVirtMethod *)mono_domain_alloc0 (domain, sizeof (MonoJumpInfoVirtMethod));
712 t = mono_class_inflate_generic_type_checked (&info->klass->byval_arg, context, error);
713 mono_error_assert_ok (error); /* FIXME don't swallow the error */
715 res->klass = mono_class_from_mono_type (t);
716 mono_metadata_free_type (t);
718 res->method = mono_class_inflate_generic_method_checked (info->method, context, error);
719 g_assert (mono_error_ok (error)); /* FIXME don't swallow the error */
721 return res;
723 default:
724 g_assert_not_reached ();
726 /* Not reached, quiet compiler */
727 return NULL;
730 static void
731 free_inflated_info (MonoRgctxInfoType info_type, gpointer info)
733 if (!info)
734 return;
736 switch (info_type) {
737 case MONO_RGCTX_INFO_STATIC_DATA:
738 case MONO_RGCTX_INFO_KLASS:
739 case MONO_RGCTX_INFO_ELEMENT_KLASS:
740 case MONO_RGCTX_INFO_VTABLE:
741 case MONO_RGCTX_INFO_TYPE:
742 case MONO_RGCTX_INFO_REFLECTION_TYPE:
743 case MONO_RGCTX_INFO_CAST_CACHE:
744 mono_metadata_free_type ((MonoType *)info);
745 break;
746 default:
747 break;
751 static MonoRuntimeGenericContextInfoTemplate
752 class_get_rgctx_template_oti (MonoClass *klass, int type_argc, guint32 slot, gboolean temporary, gboolean shared, gboolean *do_free);
754 static MonoClass*
755 class_uninstantiated (MonoClass *klass)
757 if (mono_class_is_ginst (klass))
758 return mono_class_get_generic_class (klass)->container_class;
759 return klass;
763 * get_shared_class:
765 * Return the class used to store information when using generic sharing.
767 static MonoClass*
768 get_shared_class (MonoClass *klass)
770 return class_uninstantiated (klass);
774 * mono_class_get_runtime_generic_context_template:
775 * @class: a class
777 * Looks up or constructs, if necessary, the runtime generic context template for class.
778 * The template is the same for all instantiations of a class.
780 static MonoRuntimeGenericContextTemplate*
781 mono_class_get_runtime_generic_context_template (MonoClass *klass)
783 MonoRuntimeGenericContextTemplate *parent_template, *template_;
784 guint32 i;
786 klass = get_shared_class (klass);
788 mono_loader_lock ();
789 template_ = class_lookup_rgctx_template (klass);
790 mono_loader_unlock ();
792 if (template_)
793 return template_;
795 //g_assert (get_shared_class (class) == class);
797 template_ = alloc_template (klass);
799 mono_loader_lock ();
801 if (klass->parent) {
802 guint32 num_entries;
803 int max_argc, type_argc;
805 parent_template = mono_class_get_runtime_generic_context_template (klass->parent);
806 max_argc = template_get_max_argc (parent_template);
808 for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
809 num_entries = rgctx_template_num_infos (parent_template, type_argc);
811 /* FIXME: quadratic! */
812 for (i = 0; i < num_entries; ++i) {
813 MonoRuntimeGenericContextInfoTemplate oti;
815 oti = class_get_rgctx_template_oti (klass->parent, type_argc, i, FALSE, FALSE, NULL);
816 if (oti.data && oti.data != MONO_RGCTX_SLOT_USED_MARKER) {
817 rgctx_template_set_slot (klass->image, template_, type_argc, i,
818 oti.data, oti.info_type);
824 if (class_lookup_rgctx_template (klass)) {
825 /* some other thread already set the template */
826 template_ = class_lookup_rgctx_template (klass);
827 } else {
828 class_set_rgctx_template (klass, template_);
830 if (klass->parent)
831 register_generic_subclass (klass);
834 mono_loader_unlock ();
836 return template_;
840 * class_get_rgctx_template_oti:
842 * Return the info template of CLASS numbered TYPE_ARGC/SLOT.
843 * temporary signifies whether the inflated info (oti.data) will be
844 * used temporarily, in which case it might be heap-allocated, or
845 * permanently, in which case it will be mempool-allocated. If
846 * temporary is set then *do_free will return whether the returned
847 * data must be freed.
849 * LOCKING: loader lock
851 static MonoRuntimeGenericContextInfoTemplate
852 class_get_rgctx_template_oti (MonoClass *klass, int type_argc, guint32 slot, gboolean temporary, gboolean shared, gboolean *do_free)
854 g_assert ((temporary && do_free) || (!temporary && !do_free));
856 DEBUG (printf ("get slot: %s %d\n", mono_type_full_name (&class->byval_arg), slot));
858 if (mono_class_is_ginst (klass) && !shared) {
859 MonoRuntimeGenericContextInfoTemplate oti;
860 gboolean tmp_do_free;
862 oti = class_get_rgctx_template_oti (mono_class_get_generic_class (klass)->container_class,
863 type_argc, slot, TRUE, FALSE, &tmp_do_free);
864 if (oti.data) {
865 gpointer info = oti.data;
866 oti.data = inflate_info (&oti, &mono_class_get_generic_class (klass)->context, klass, temporary);
867 if (tmp_do_free)
868 free_inflated_info (oti.info_type, info);
870 if (temporary)
871 *do_free = TRUE;
873 return oti;
874 } else {
875 MonoRuntimeGenericContextTemplate *template_;
876 MonoRuntimeGenericContextInfoTemplate *oti;
878 template_ = mono_class_get_runtime_generic_context_template (klass);
879 oti = rgctx_template_get_other_slot (template_, type_argc, slot);
880 g_assert (oti);
882 if (temporary)
883 *do_free = FALSE;
885 return *oti;
889 static gpointer
890 class_type_info (MonoDomain *domain, MonoClass *klass, MonoRgctxInfoType info_type, MonoError *error)
892 error_init (error);
894 switch (info_type) {
895 case MONO_RGCTX_INFO_STATIC_DATA: {
896 MonoVTable *vtable = mono_class_vtable_checked (domain, klass, error);
897 return_val_if_nok (error, NULL);
898 return mono_vtable_get_static_field_data (vtable);
900 case MONO_RGCTX_INFO_KLASS:
901 return klass;
902 case MONO_RGCTX_INFO_ELEMENT_KLASS:
903 return klass->element_class;
904 case MONO_RGCTX_INFO_VTABLE: {
905 MonoVTable *vtable = mono_class_vtable_checked (domain, klass, error);
906 return_val_if_nok (error, NULL);
907 return vtable;
909 case MONO_RGCTX_INFO_CAST_CACHE: {
910 /*First slot is the cache itself, the second the vtable.*/
911 gpointer **cache_data = (gpointer **)mono_domain_alloc0 (domain, sizeof (gpointer) * 2);
912 cache_data [1] = (gpointer *)klass;
913 return cache_data;
915 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
916 return GUINT_TO_POINTER (mono_class_array_element_size (klass));
917 case MONO_RGCTX_INFO_VALUE_SIZE:
918 if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg))
919 return GUINT_TO_POINTER (sizeof (gpointer));
920 else
921 return GUINT_TO_POINTER (mono_class_value_size (klass, NULL));
922 case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
923 if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg))
924 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_REF);
925 else if (mono_class_is_nullable (klass))
926 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_NULLABLE);
927 else
928 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_VTYPE);
929 case MONO_RGCTX_INFO_CLASS_IS_REF_OR_CONTAINS_REFS:
930 mono_class_init (klass);
931 /* Can't return 0 */
932 if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg) || klass->has_references)
933 return GUINT_TO_POINTER (2);
934 else
935 return GUINT_TO_POINTER (1);
936 case MONO_RGCTX_INFO_MEMCPY:
937 case MONO_RGCTX_INFO_BZERO: {
938 static MonoMethod *memcpy_method [17];
939 static MonoMethod *bzero_method [17];
940 MonoJitDomainInfo *domain_info;
941 int size;
942 guint32 align;
944 domain_info = domain_jit_info (domain);
946 if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg)) {
947 size = sizeof (gpointer);
948 align = sizeof (gpointer);
949 } else {
950 size = mono_class_value_size (klass, &align);
953 if (size != 1 && size != 2 && size != 4 && size != 8)
954 size = 0;
955 if (align < size)
956 size = 0;
958 if (info_type == MONO_RGCTX_INFO_MEMCPY) {
959 if (!memcpy_method [size]) {
960 MonoMethod *m;
961 char name [32];
963 if (size == 0)
964 sprintf (name, "memcpy");
965 else
966 sprintf (name, "memcpy_aligned_%d", size);
967 m = mono_class_get_method_from_name (mono_defaults.string_class, name, 3);
968 g_assert (m);
969 mono_memory_barrier ();
970 memcpy_method [size] = m;
972 if (!domain_info->memcpy_addr [size]) {
973 gpointer addr = mono_compile_method_checked (memcpy_method [size], error);
974 mono_memory_barrier ();
975 domain_info->memcpy_addr [size] = (gpointer *)addr;
976 mono_error_assert_ok (error);
978 return domain_info->memcpy_addr [size];
979 } else {
980 if (!bzero_method [size]) {
981 MonoMethod *m;
982 char name [32];
984 if (size == 0)
985 sprintf (name, "bzero");
986 else
987 sprintf (name, "bzero_aligned_%d", size);
988 m = mono_class_get_method_from_name (mono_defaults.string_class, name, 2);
989 g_assert (m);
990 mono_memory_barrier ();
991 bzero_method [size] = m;
993 if (!domain_info->bzero_addr [size]) {
994 gpointer addr = mono_compile_method_checked (bzero_method [size], error);
995 mono_memory_barrier ();
996 domain_info->bzero_addr [size] = (gpointer *)addr;
997 mono_error_assert_ok (error);
999 return domain_info->bzero_addr [size];
1002 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
1003 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: {
1004 MonoMethod *method;
1005 gpointer addr, arg;
1006 MonoJitInfo *ji;
1007 MonoMethodSignature *sig, *gsig;
1008 MonoMethod *gmethod;
1010 if (!mono_class_is_nullable (klass))
1011 /* This can happen since all the entries in MonoGSharedVtMethodInfo are inflated, even those which are not used */
1012 return NULL;
1014 if (info_type == MONO_RGCTX_INFO_NULLABLE_CLASS_BOX)
1015 method = mono_class_get_method_from_name (klass, "Box", 1);
1016 else
1017 method = mono_class_get_method_from_name (klass, "Unbox", 1);
1019 addr = mono_jit_compile_method (method, error);
1020 if (!mono_error_ok (error))
1021 return NULL;
1023 // The caller uses the gsharedvt call signature
1025 if (mono_llvm_only) {
1026 /* FIXME: We have no access to the gsharedvt signature/gsctx used by the caller, so have to construct it ourselves */
1027 gmethod = mini_get_shared_method_full (method, FALSE, TRUE);
1028 sig = mono_method_signature (method);
1029 gsig = mono_method_signature (gmethod);
1031 addr = mini_add_method_wrappers_llvmonly (method, addr, TRUE, FALSE, &arg);
1032 return mini_create_llvmonly_ftndesc (domain, addr, arg);
1035 ji = mini_jit_info_table_find (mono_domain_get (), (char *)mono_get_addr_from_ftnptr (addr), NULL);
1036 g_assert (ji);
1037 if (mini_jit_info_is_gsharedvt (ji))
1038 return mono_create_static_rgctx_trampoline (method, addr);
1039 else {
1040 /* Need to add an out wrapper */
1042 /* FIXME: We have no access to the gsharedvt signature/gsctx used by the caller, so have to construct it ourselves */
1043 gmethod = mini_get_shared_method_full (method, FALSE, TRUE);
1044 sig = mono_method_signature (method);
1045 gsig = mono_method_signature (gmethod);
1047 addr = mini_get_gsharedvt_wrapper (FALSE, addr, sig, gsig, -1, FALSE);
1048 addr = mono_create_static_rgctx_trampoline (method, addr);
1049 return addr;
1052 default:
1053 g_assert_not_reached ();
1055 /* Not reached */
1056 return NULL;
1059 static gboolean
1060 ji_is_gsharedvt (MonoJitInfo *ji)
1062 if (ji && ji->has_generic_jit_info && (mono_jit_info_get_generic_sharing_context (ji)->is_gsharedvt))
1063 return TRUE;
1064 else
1065 return FALSE;
1069 * Describes the information used to construct a gsharedvt arg trampoline.
1071 typedef struct {
1072 gboolean is_in;
1073 gboolean calli;
1074 gint32 vcall_offset;
1075 gpointer addr;
1076 MonoMethodSignature *sig, *gsig;
1077 } GSharedVtTrampInfo;
1079 static guint
1080 tramp_info_hash (gconstpointer key)
1082 GSharedVtTrampInfo *tramp = (GSharedVtTrampInfo *)key;
1084 return (gsize)tramp->addr;
1087 static gboolean
1088 tramp_info_equal (gconstpointer a, gconstpointer b)
1090 GSharedVtTrampInfo *tramp1 = (GSharedVtTrampInfo *)a;
1091 GSharedVtTrampInfo *tramp2 = (GSharedVtTrampInfo *)b;
1093 /* The signatures should be internalized */
1094 return tramp1->is_in == tramp2->is_in && tramp1->calli == tramp2->calli && tramp1->vcall_offset == tramp2->vcall_offset &&
1095 tramp1->addr == tramp2->addr && tramp1->sig == tramp2->sig && tramp1->gsig == tramp2->gsig;
1098 static MonoType*
1099 get_wrapper_shared_type (MonoType *t)
1101 if (t->byref)
1102 return &mono_defaults.int_class->this_arg;
1103 t = mini_get_underlying_type (t);
1105 switch (t->type) {
1106 case MONO_TYPE_I1:
1107 /* This removes any attributes etc. */
1108 return &mono_defaults.sbyte_class->byval_arg;
1109 case MONO_TYPE_U1:
1110 return &mono_defaults.byte_class->byval_arg;
1111 case MONO_TYPE_I2:
1112 return &mono_defaults.int16_class->byval_arg;
1113 case MONO_TYPE_U2:
1114 return &mono_defaults.uint16_class->byval_arg;
1115 case MONO_TYPE_I4:
1116 return &mono_defaults.int32_class->byval_arg;
1117 case MONO_TYPE_U4:
1118 return &mono_defaults.uint32_class->byval_arg;
1119 case MONO_TYPE_OBJECT:
1120 case MONO_TYPE_CLASS:
1121 case MONO_TYPE_SZARRAY:
1122 case MONO_TYPE_ARRAY:
1123 case MONO_TYPE_PTR:
1124 // FIXME: refs and intptr cannot be shared because
1125 // they are treated differently when a method has a vret arg,
1126 // see get_call_info ().
1127 return &mono_defaults.object_class->byval_arg;
1128 //return &mono_defaults.int_class->byval_arg;
1129 case MONO_TYPE_GENERICINST: {
1130 ERROR_DECL (error);
1131 MonoClass *klass;
1132 MonoGenericContext ctx;
1133 MonoGenericContext *orig_ctx;
1134 MonoGenericInst *inst;
1135 MonoType *args [16];
1136 int i;
1138 if (!MONO_TYPE_ISSTRUCT (t))
1139 return get_wrapper_shared_type (&mono_defaults.object_class->byval_arg);
1141 klass = mono_class_from_mono_type (t);
1142 orig_ctx = &mono_class_get_generic_class (klass)->context;
1144 memset (&ctx, 0, sizeof (MonoGenericContext));
1146 inst = orig_ctx->class_inst;
1147 if (inst) {
1148 g_assert (inst->type_argc < 16);
1149 for (i = 0; i < inst->type_argc; ++i)
1150 args [i] = get_wrapper_shared_type (inst->type_argv [i]);
1151 ctx.class_inst = mono_metadata_get_generic_inst (inst->type_argc, args);
1153 inst = orig_ctx->method_inst;
1154 if (inst) {
1155 g_assert (inst->type_argc < 16);
1156 for (i = 0; i < inst->type_argc; ++i)
1157 args [i] = get_wrapper_shared_type (inst->type_argv [i]);
1158 ctx.method_inst = mono_metadata_get_generic_inst (inst->type_argc, args);
1160 klass = mono_class_inflate_generic_class_checked (mono_class_get_generic_class (klass)->container_class, &ctx, error);
1161 mono_error_assert_ok (error); /* FIXME don't swallow the error */
1162 return &klass->byval_arg;
1164 #if SIZEOF_VOID_P == 8
1165 case MONO_TYPE_I8:
1166 return &mono_defaults.int_class->byval_arg;
1167 #endif
1168 default:
1169 break;
1172 //printf ("%s\n", mono_type_full_name (t));
1174 return t;
1177 static MonoMethodSignature*
1178 mini_get_underlying_signature (MonoMethodSignature *sig)
1180 MonoMethodSignature *res = mono_metadata_signature_dup (sig);
1181 int i;
1183 res->ret = get_wrapper_shared_type (sig->ret);
1184 for (i = 0; i < sig->param_count; ++i)
1185 res->params [i] = get_wrapper_shared_type (sig->params [i]);
1186 res->generic_param_count = 0;
1187 res->is_inflated = 0;
1189 return res;
1193 * mini_get_gsharedvt_in_sig_wrapper:
1195 * Return a wrapper to translate between the normal and gsharedvt calling conventions of SIG.
1196 * The returned wrapper has a signature of SIG, plus one extra argument, which is an <addr, rgctx> pair.
1197 * The extra argument is passed the same way as an rgctx to shared methods.
1198 * It calls <addr> using the gsharedvt version of SIG, passing in <rgctx> as an extra argument.
1200 MonoMethod*
1201 mini_get_gsharedvt_in_sig_wrapper (MonoMethodSignature *sig)
1203 MonoMethodBuilder *mb;
1204 MonoMethod *res, *cached;
1205 WrapperInfo *info;
1206 MonoMethodSignature *csig, *gsharedvt_sig;
1207 int i, pindex, retval_var = 0;
1208 static GHashTable *cache;
1210 // FIXME: Memory management
1211 sig = mini_get_underlying_signature (sig);
1213 // FIXME: Normal cache
1214 gshared_lock ();
1215 if (!cache)
1216 cache = g_hash_table_new_full ((GHashFunc)mono_signature_hash, (GEqualFunc)mono_metadata_signature_equal, NULL, NULL);
1217 res = g_hash_table_lookup (cache, sig);
1218 gshared_unlock ();
1219 if (res) {
1220 g_free (sig);
1221 return res;
1224 /* Create the signature for the wrapper */
1225 // FIXME:
1226 csig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 1) * sizeof (MonoType*)));
1227 memcpy (csig, sig, mono_metadata_signature_size (sig));
1228 csig->param_count ++;
1229 csig->params [sig->param_count] = &mono_defaults.int_class->byval_arg;
1231 /* Create the signature for the gsharedvt callconv */
1232 gsharedvt_sig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 2) * sizeof (MonoType*)));
1233 memcpy (gsharedvt_sig, sig, mono_metadata_signature_size (sig));
1234 pindex = 0;
1235 /* The return value is returned using an explicit vret argument */
1236 if (sig->ret->type != MONO_TYPE_VOID) {
1237 gsharedvt_sig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
1238 gsharedvt_sig->ret = &mono_defaults.void_class->byval_arg;
1240 for (i = 0; i < sig->param_count; i++) {
1241 gsharedvt_sig->params [pindex] = sig->params [i];
1242 if (!sig->params [i]->byref) {
1243 gsharedvt_sig->params [pindex] = mono_metadata_type_dup (NULL, gsharedvt_sig->params [pindex]);
1244 gsharedvt_sig->params [pindex]->byref = 1;
1246 pindex ++;
1248 /* Rgctx arg */
1249 gsharedvt_sig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
1250 gsharedvt_sig->param_count = pindex;
1252 // FIXME: Use shared signatures
1253 mb = mono_mb_new (mono_defaults.object_class, sig->hasthis ? "gsharedvt_in_sig" : "gsharedvt_in_sig_static", MONO_WRAPPER_UNKNOWN);
1255 #ifndef DISABLE_JIT
1256 if (sig->ret->type != MONO_TYPE_VOID)
1257 retval_var = mono_mb_add_local (mb, sig->ret);
1259 /* Make the call */
1260 if (sig->hasthis)
1261 mono_mb_emit_ldarg (mb, 0);
1262 if (sig->ret->type != MONO_TYPE_VOID)
1263 mono_mb_emit_ldloc_addr (mb, retval_var);
1264 for (i = 0; i < sig->param_count; i++) {
1265 if (sig->params [i]->byref)
1266 mono_mb_emit_ldarg (mb, i + (sig->hasthis == TRUE));
1267 else
1268 mono_mb_emit_ldarg_addr (mb, i + (sig->hasthis == TRUE));
1270 /* Rgctx arg */
1271 mono_mb_emit_ldarg (mb, sig->param_count + (sig->hasthis ? 1 : 0));
1272 mono_mb_emit_icon (mb, sizeof (gpointer));
1273 mono_mb_emit_byte (mb, CEE_ADD);
1274 mono_mb_emit_byte (mb, CEE_LDIND_I);
1275 /* Method to call */
1276 mono_mb_emit_ldarg (mb, sig->param_count + (sig->hasthis ? 1 : 0));
1277 mono_mb_emit_byte (mb, CEE_LDIND_I);
1278 mono_mb_emit_calli (mb, gsharedvt_sig);
1279 if (sig->ret->type != MONO_TYPE_VOID)
1280 mono_mb_emit_ldloc (mb, retval_var);
1281 mono_mb_emit_byte (mb, CEE_RET);
1282 #endif
1284 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_GSHAREDVT_IN_SIG);
1285 info->d.gsharedvt.sig = sig;
1287 res = mono_mb_create (mb, csig, sig->param_count + 16, info);
1289 gshared_lock ();
1290 cached = g_hash_table_lookup (cache, sig);
1291 if (cached)
1292 res = cached;
1293 else
1294 g_hash_table_insert (cache, sig, res);
1295 gshared_unlock ();
1296 return res;
1300 * mini_get_gsharedvt_out_sig_wrapper:
1302 * Same as in_sig_wrapper, but translate between the gsharedvt and normal signatures.
1304 MonoMethod*
1305 mini_get_gsharedvt_out_sig_wrapper (MonoMethodSignature *sig)
1307 MonoMethodBuilder *mb;
1308 MonoMethod *res, *cached;
1309 WrapperInfo *info;
1310 MonoMethodSignature *normal_sig, *csig;
1311 int i, pindex, args_start, ldind_op, stind_op;
1312 static GHashTable *cache;
1314 // FIXME: Memory management
1315 sig = mini_get_underlying_signature (sig);
1317 // FIXME: Normal cache
1318 gshared_lock ();
1319 if (!cache)
1320 cache = g_hash_table_new_full ((GHashFunc)mono_signature_hash, (GEqualFunc)mono_metadata_signature_equal, NULL, NULL);
1321 res = g_hash_table_lookup (cache, sig);
1322 gshared_unlock ();
1323 if (res) {
1324 g_free (sig);
1325 return res;
1328 /* Create the signature for the wrapper */
1329 // FIXME:
1330 csig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 2) * sizeof (MonoType*)));
1331 memcpy (csig, sig, mono_metadata_signature_size (sig));
1332 pindex = 0;
1333 /* The return value is returned using an explicit vret argument */
1334 if (sig->ret->type != MONO_TYPE_VOID) {
1335 csig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
1336 csig->ret = &mono_defaults.void_class->byval_arg;
1338 args_start = pindex;
1339 if (sig->hasthis)
1340 args_start ++;
1341 for (i = 0; i < sig->param_count; i++) {
1342 csig->params [pindex] = sig->params [i];
1343 if (!sig->params [i]->byref) {
1344 csig->params [pindex] = mono_metadata_type_dup (NULL, csig->params [pindex]);
1345 csig->params [pindex]->byref = 1;
1347 pindex ++;
1349 /* Rgctx arg */
1350 csig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
1351 csig->param_count = pindex;
1353 /* Create the signature for the normal callconv */
1354 normal_sig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 2) * sizeof (MonoType*)));
1355 memcpy (normal_sig, sig, mono_metadata_signature_size (sig));
1356 normal_sig->param_count ++;
1357 normal_sig->params [sig->param_count] = &mono_defaults.int_class->byval_arg;
1359 // FIXME: Use shared signatures
1360 mb = mono_mb_new (mono_defaults.object_class, "gsharedvt_out_sig", MONO_WRAPPER_UNKNOWN);
1362 #ifndef DISABLE_JIT
1363 if (sig->ret->type != MONO_TYPE_VOID)
1364 /* Load return address */
1365 mono_mb_emit_ldarg (mb, sig->hasthis ? 1 : 0);
1367 /* Make the call */
1368 if (sig->hasthis)
1369 mono_mb_emit_ldarg (mb, 0);
1370 for (i = 0; i < sig->param_count; i++) {
1371 if (sig->params [i]->byref) {
1372 mono_mb_emit_ldarg (mb, args_start + i);
1373 } else {
1374 ldind_op = mono_type_to_ldind (sig->params [i]);
1375 mono_mb_emit_ldarg (mb, args_start + i);
1376 // FIXME:
1377 if (ldind_op == CEE_LDOBJ)
1378 mono_mb_emit_op (mb, CEE_LDOBJ, mono_class_from_mono_type (sig->params [i]));
1379 else
1380 mono_mb_emit_byte (mb, ldind_op);
1383 /* Rgctx arg */
1384 mono_mb_emit_ldarg (mb, args_start + sig->param_count);
1385 mono_mb_emit_icon (mb, sizeof (gpointer));
1386 mono_mb_emit_byte (mb, CEE_ADD);
1387 mono_mb_emit_byte (mb, CEE_LDIND_I);
1388 /* Method to call */
1389 mono_mb_emit_ldarg (mb, args_start + sig->param_count);
1390 mono_mb_emit_byte (mb, CEE_LDIND_I);
1391 mono_mb_emit_calli (mb, normal_sig);
1392 if (sig->ret->type != MONO_TYPE_VOID) {
1393 /* Store return value */
1394 stind_op = mono_type_to_stind (sig->ret);
1395 // FIXME:
1396 if (stind_op == CEE_STOBJ)
1397 mono_mb_emit_op (mb, CEE_STOBJ, mono_class_from_mono_type (sig->ret));
1398 else if (stind_op == CEE_STIND_REF)
1399 /* Avoid write barriers, the vret arg points to the stack */
1400 mono_mb_emit_byte (mb, CEE_STIND_I);
1401 else
1402 mono_mb_emit_byte (mb, stind_op);
1404 mono_mb_emit_byte (mb, CEE_RET);
1405 #endif
1407 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_GSHAREDVT_OUT_SIG);
1408 info->d.gsharedvt.sig = sig;
1410 res = mono_mb_create (mb, csig, sig->param_count + 16, info);
1412 gshared_lock ();
1413 cached = g_hash_table_lookup (cache, sig);
1414 if (cached)
1415 res = cached;
1416 else
1417 g_hash_table_insert (cache, sig, res);
1418 gshared_unlock ();
1419 return res;
1423 * mini_get_interp_in_wrapper:
1425 * Return a wrapper which can be used to transition from compiled code to the interpreter.
1426 * The wrapper has the same signature as SIG. It is very similar to a gsharedvt_in wrapper,
1427 * except the 'extra_arg' is passed in the rgctx reg, so this wrapper needs to be
1428 * called through a static rgctx trampoline.
1429 * FIXME: Move this elsewhere.
1431 MonoMethod*
1432 mini_get_interp_in_wrapper (MonoMethodSignature *sig)
1434 MonoMethodBuilder *mb;
1435 MonoMethod *res, *cached;
1436 WrapperInfo *info;
1437 MonoMethodSignature *csig, *entry_sig;
1438 int i, pindex, retval_var = 0;
1439 static GHashTable *cache;
1440 const char *name;
1441 gboolean generic = FALSE;
1443 sig = mini_get_underlying_signature (sig);
1445 gshared_lock ();
1446 if (!cache)
1447 cache = g_hash_table_new_full ((GHashFunc)mono_signature_hash, (GEqualFunc)mono_metadata_signature_equal, NULL, NULL);
1448 res = g_hash_table_lookup (cache, sig);
1449 gshared_unlock ();
1450 if (res) {
1451 g_free (sig);
1452 return res;
1455 if (sig->param_count > 8)
1456 /* Call the generic interpreter entry point, the specialized ones only handle a limited number of arguments */
1457 generic = TRUE;
1459 /* Create the signature for the wrapper */
1460 csig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + (sig->param_count * sizeof (MonoType*)));
1461 memcpy (csig, sig, mono_metadata_signature_size (sig));
1463 /* Create the signature for the callee callconv */
1464 if (generic) {
1466 * The called function has the following signature:
1467 * interp_entry_general (gpointer this_arg, gpointer res, gpointer *args, gpointer rmethod)
1469 entry_sig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + (4 * sizeof (MonoType*)));
1470 entry_sig->ret = &mono_defaults.void_class->byval_arg;
1471 entry_sig->param_count = 4;
1472 entry_sig->params [0] = &mono_defaults.int_class->byval_arg;
1473 entry_sig->params [1] = &mono_defaults.int_class->byval_arg;
1474 entry_sig->params [2] = &mono_defaults.int_class->byval_arg;
1475 entry_sig->params [3] = &mono_defaults.int_class->byval_arg;
1476 name = "interp_in_generic";
1477 generic = TRUE;
1478 } else {
1480 * The called function has the following signature:
1481 * void entry(<optional this ptr>, <optional return ptr>, <arguments>, <extra arg>)
1483 entry_sig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 2) * sizeof (MonoType*)));
1484 memcpy (entry_sig, sig, mono_metadata_signature_size (sig));
1485 pindex = 0;
1486 /* The return value is returned using an explicit vret argument */
1487 if (sig->ret->type != MONO_TYPE_VOID) {
1488 entry_sig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
1489 entry_sig->ret = &mono_defaults.void_class->byval_arg;
1491 for (i = 0; i < sig->param_count; i++) {
1492 entry_sig->params [pindex] = sig->params [i];
1493 if (!sig->params [i]->byref) {
1494 entry_sig->params [pindex] = mono_metadata_type_dup (NULL, entry_sig->params [pindex]);
1495 entry_sig->params [pindex]->byref = 1;
1497 pindex ++;
1499 /* Extra arg */
1500 entry_sig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
1501 entry_sig->param_count = pindex;
1502 name = sig->hasthis ? "interp_in" : "interp_in_static";
1505 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_UNKNOWN);
1508 * This is needed to be able to unwind out of interpreted code to managed.
1509 * When we are called from native code we can't unwind and we might also not
1510 * be attached.
1512 if (!sig->pinvoke)
1513 mb->method->save_lmf = 1;
1515 #ifndef DISABLE_JIT
1516 if (sig->ret->type != MONO_TYPE_VOID)
1517 retval_var = mono_mb_add_local (mb, sig->ret);
1519 /* Make the call */
1520 if (generic) {
1521 /* Collect arguments */
1522 int args_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1524 mono_mb_emit_icon (mb, sizeof (gpointer) * sig->param_count);
1525 mono_mb_emit_byte (mb, CEE_PREFIX1);
1526 mono_mb_emit_byte (mb, CEE_LOCALLOC);
1527 mono_mb_emit_stloc (mb, args_var);
1529 for (i = 0; i < sig->param_count; i++) {
1530 mono_mb_emit_ldloc (mb, args_var);
1531 mono_mb_emit_icon (mb, sizeof (gpointer) * i);
1532 mono_mb_emit_byte (mb, CEE_ADD);
1533 if (sig->params [i]->byref)
1534 mono_mb_emit_ldarg (mb, i + (sig->hasthis == TRUE));
1535 else
1536 mono_mb_emit_ldarg_addr (mb, i + (sig->hasthis == TRUE));
1537 mono_mb_emit_byte (mb, CEE_STIND_I);
1540 if (sig->hasthis)
1541 mono_mb_emit_ldarg (mb, 0);
1542 else
1543 mono_mb_emit_byte (mb, CEE_LDNULL);
1544 if (sig->ret->type != MONO_TYPE_VOID)
1545 mono_mb_emit_ldloc_addr (mb, retval_var);
1546 else
1547 mono_mb_emit_byte (mb, CEE_LDNULL);
1548 mono_mb_emit_ldloc (mb, args_var);
1549 } else {
1550 if (sig->hasthis)
1551 mono_mb_emit_ldarg (mb, 0);
1552 if (sig->ret->type != MONO_TYPE_VOID)
1553 mono_mb_emit_ldloc_addr (mb, retval_var);
1554 for (i = 0; i < sig->param_count; i++) {
1555 if (sig->params [i]->byref)
1556 mono_mb_emit_ldarg (mb, i + (sig->hasthis == TRUE));
1557 else
1558 mono_mb_emit_ldarg_addr (mb, i + (sig->hasthis == TRUE));
1561 /* Extra arg */
1562 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1563 mono_mb_emit_byte (mb, CEE_MONO_GET_RGCTX_ARG);
1564 mono_mb_emit_icon (mb, sizeof (gpointer));
1565 mono_mb_emit_byte (mb, CEE_ADD);
1566 mono_mb_emit_byte (mb, CEE_LDIND_I);
1567 /* Method to call */
1568 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1569 mono_mb_emit_byte (mb, CEE_MONO_GET_RGCTX_ARG);
1570 mono_mb_emit_byte (mb, CEE_LDIND_I);
1571 mono_mb_emit_calli (mb, entry_sig);
1572 if (sig->ret->type != MONO_TYPE_VOID)
1573 mono_mb_emit_ldloc (mb, retval_var);
1574 mono_mb_emit_byte (mb, CEE_RET);
1575 #endif
1577 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_INTERP_IN);
1578 info->d.interp_in.sig = sig;
1580 res = mono_mb_create (mb, csig, sig->param_count + 16, info);
1582 gshared_lock ();
1583 cached = g_hash_table_lookup (cache, sig);
1584 if (cached) {
1585 mono_free_method (res);
1586 res = cached;
1587 } else {
1588 g_hash_table_insert (cache, sig, res);
1590 gshared_unlock ();
1591 mono_mb_free (mb);
1593 return res;
1596 MonoMethodSignature*
1597 mini_get_gsharedvt_out_sig_wrapper_signature (gboolean has_this, gboolean has_ret, int param_count)
1599 MonoMethodSignature *sig = g_malloc0 (sizeof (MonoMethodSignature) + (32 * sizeof (MonoType*)));
1600 int i, pindex;
1602 sig->ret = &mono_defaults.void_class->byval_arg;
1603 sig->sentinelpos = -1;
1604 pindex = 0;
1605 if (has_this)
1606 /* this */
1607 sig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
1608 if (has_ret)
1609 /* vret */
1610 sig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
1611 for (i = 0; i < param_count; ++i)
1612 /* byref arguments */
1613 sig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
1614 /* extra arg */
1615 sig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
1616 sig->param_count = pindex;
1618 return sig;
1622 * mini_get_gsharedvt_wrapper:
1624 * Return a gsharedvt in/out wrapper for calling ADDR.
1626 gpointer
1627 mini_get_gsharedvt_wrapper (gboolean gsharedvt_in, gpointer addr, MonoMethodSignature *normal_sig, MonoMethodSignature *gsharedvt_sig, gint32 vcall_offset, gboolean calli)
1629 ERROR_DECL (error);
1630 gpointer res, info;
1631 MonoDomain *domain = mono_domain_get ();
1632 MonoJitDomainInfo *domain_info;
1633 GSharedVtTrampInfo *tramp_info;
1634 GSharedVtTrampInfo tinfo;
1636 if (mono_llvm_only) {
1637 MonoMethod *wrapper;
1639 if (gsharedvt_in)
1640 wrapper = mini_get_gsharedvt_in_sig_wrapper (normal_sig);
1641 else
1642 wrapper = mini_get_gsharedvt_out_sig_wrapper (normal_sig);
1643 res = mono_compile_method_checked (wrapper, error);
1644 mono_error_assert_ok (error);
1645 return res;
1648 memset (&tinfo, 0, sizeof (tinfo));
1649 tinfo.is_in = gsharedvt_in;
1650 tinfo.calli = calli;
1651 tinfo.vcall_offset = vcall_offset;
1652 tinfo.addr = addr;
1653 tinfo.sig = normal_sig;
1654 tinfo.gsig = gsharedvt_sig;
1656 domain_info = domain_jit_info (domain);
1659 * The arg trampolines might only have a finite number in full-aot, so use a cache.
1661 mono_domain_lock (domain);
1662 if (!domain_info->gsharedvt_arg_tramp_hash)
1663 domain_info->gsharedvt_arg_tramp_hash = g_hash_table_new (tramp_info_hash, tramp_info_equal);
1664 res = g_hash_table_lookup (domain_info->gsharedvt_arg_tramp_hash, &tinfo);
1665 mono_domain_unlock (domain);
1666 if (res)
1667 return res;
1669 info = mono_arch_get_gsharedvt_call_info (addr, normal_sig, gsharedvt_sig, gsharedvt_in, vcall_offset, calli);
1671 if (gsharedvt_in) {
1672 static gpointer tramp_addr;
1673 MonoMethod *wrapper;
1675 if (!tramp_addr) {
1676 wrapper = mono_marshal_get_gsharedvt_in_wrapper ();
1677 addr = mono_compile_method_checked (wrapper, error);
1678 mono_memory_barrier ();
1679 mono_error_assert_ok (error);
1680 tramp_addr = addr;
1682 addr = tramp_addr;
1683 } else {
1684 static gpointer tramp_addr;
1685 MonoMethod *wrapper;
1687 if (!tramp_addr) {
1688 wrapper = mono_marshal_get_gsharedvt_out_wrapper ();
1689 addr = mono_compile_method_checked (wrapper, error);
1690 mono_memory_barrier ();
1691 mono_error_assert_ok (error);
1692 tramp_addr = addr;
1694 addr = tramp_addr;
1697 if (mono_aot_only)
1698 addr = mono_aot_get_gsharedvt_arg_trampoline (info, addr);
1699 else
1700 addr = mono_arch_get_gsharedvt_arg_trampoline (mono_domain_get (), info, addr);
1702 mono_atomic_inc_i32 (&gsharedvt_num_trampolines);
1704 /* Cache it */
1705 tramp_info = (GSharedVtTrampInfo *)mono_domain_alloc0 (domain, sizeof (GSharedVtTrampInfo));
1706 memcpy (tramp_info, &tinfo, sizeof (GSharedVtTrampInfo));
1708 mono_domain_lock (domain);
1709 /* Duplicates are not a problem */
1710 g_hash_table_insert (domain_info->gsharedvt_arg_tramp_hash, tramp_info, addr);
1711 mono_domain_unlock (domain);
1713 return addr;
1717 * instantiate_info:
1719 * Instantiate the info given by OTI for context CONTEXT.
1721 static gpointer
1722 instantiate_info (MonoDomain *domain, MonoRuntimeGenericContextInfoTemplate *oti,
1723 MonoGenericContext *context, MonoClass *klass, MonoError *error)
1725 gpointer data;
1726 gboolean temporary;
1728 error_init (error);
1730 if (!oti->data)
1731 return NULL;
1733 switch (oti->info_type) {
1734 case MONO_RGCTX_INFO_STATIC_DATA:
1735 case MONO_RGCTX_INFO_KLASS:
1736 case MONO_RGCTX_INFO_ELEMENT_KLASS:
1737 case MONO_RGCTX_INFO_VTABLE:
1738 case MONO_RGCTX_INFO_CAST_CACHE:
1739 temporary = TRUE;
1740 break;
1741 default:
1742 temporary = FALSE;
1745 data = inflate_info (oti, context, klass, temporary);
1747 switch (oti->info_type) {
1748 case MONO_RGCTX_INFO_STATIC_DATA:
1749 case MONO_RGCTX_INFO_KLASS:
1750 case MONO_RGCTX_INFO_ELEMENT_KLASS:
1751 case MONO_RGCTX_INFO_VTABLE:
1752 case MONO_RGCTX_INFO_CAST_CACHE:
1753 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
1754 case MONO_RGCTX_INFO_VALUE_SIZE:
1755 case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
1756 case MONO_RGCTX_INFO_CLASS_IS_REF_OR_CONTAINS_REFS:
1757 case MONO_RGCTX_INFO_MEMCPY:
1758 case MONO_RGCTX_INFO_BZERO:
1759 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
1760 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: {
1761 MonoClass *arg_class = mono_class_from_mono_type ((MonoType *)data);
1763 free_inflated_info (oti->info_type, data);
1764 g_assert (arg_class);
1766 /* The class might be used as an argument to
1767 mono_value_copy(), which requires that its GC
1768 descriptor has been computed. */
1769 if (oti->info_type == MONO_RGCTX_INFO_KLASS)
1770 mono_class_compute_gc_descriptor (arg_class);
1772 return class_type_info (domain, arg_class, oti->info_type, error);
1774 case MONO_RGCTX_INFO_TYPE:
1775 return data;
1776 case MONO_RGCTX_INFO_REFLECTION_TYPE: {
1777 MonoReflectionType *ret = mono_type_get_object_checked (domain, (MonoType *)data, error);
1779 return ret;
1781 case MONO_RGCTX_INFO_METHOD:
1782 return data;
1783 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE: {
1784 MonoMethod *m = (MonoMethod*)data;
1785 gpointer addr;
1786 gpointer arg = NULL;
1788 if (mono_llvm_only) {
1789 addr = mono_compile_method_checked (m, error);
1790 return_val_if_nok (error, NULL);
1791 addr = mini_add_method_wrappers_llvmonly (m, addr, FALSE, FALSE, &arg);
1793 /* Returns an ftndesc */
1794 return mini_create_llvmonly_ftndesc (domain, addr, arg);
1795 } else {
1796 addr = mono_compile_method_checked ((MonoMethod *)data, error);
1797 return_val_if_nok (error, NULL);
1798 return mini_add_method_trampoline ((MonoMethod *)data, addr, mono_method_needs_static_rgctx_invoke ((MonoMethod *)data, FALSE), FALSE);
1801 case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER: {
1802 MonoMethod *m = (MonoMethod*)data;
1803 gpointer addr;
1804 gpointer arg = NULL;
1806 g_assert (mono_llvm_only);
1808 addr = mono_compile_method_checked (m, error);
1809 return_val_if_nok (error, NULL);
1811 MonoJitInfo *ji;
1812 gboolean callee_gsharedvt;
1814 ji = mini_jit_info_table_find (mono_domain_get (), (char *)mono_get_addr_from_ftnptr (addr), NULL);
1815 g_assert (ji);
1816 callee_gsharedvt = mini_jit_info_is_gsharedvt (ji);
1817 if (callee_gsharedvt)
1818 callee_gsharedvt = mini_is_gsharedvt_variable_signature (mono_method_signature (jinfo_get_method (ji)));
1819 if (callee_gsharedvt) {
1820 /* No need for a wrapper */
1821 return mini_create_llvmonly_ftndesc (domain, addr, mini_method_get_rgctx (m));
1822 } else {
1823 addr = mini_add_method_wrappers_llvmonly (m, addr, TRUE, FALSE, &arg);
1825 /* Returns an ftndesc */
1826 return mini_create_llvmonly_ftndesc (domain, addr, arg);
1829 case MONO_RGCTX_INFO_VIRT_METHOD_CODE: {
1830 MonoJumpInfoVirtMethod *info = (MonoJumpInfoVirtMethod *)data;
1831 MonoClass *iface_class = info->method->klass;
1832 MonoMethod *method;
1833 int ioffset, slot;
1834 gpointer addr;
1836 mono_class_setup_vtable (info->klass);
1837 // FIXME: Check type load
1838 if (mono_class_is_interface (iface_class)) {
1839 ioffset = mono_class_interface_offset (info->klass, iface_class);
1840 g_assert (ioffset != -1);
1841 } else {
1842 ioffset = 0;
1844 slot = mono_method_get_vtable_slot (info->method);
1845 g_assert (slot != -1);
1846 g_assert (info->klass->vtable);
1847 method = info->klass->vtable [ioffset + slot];
1849 method = mono_class_inflate_generic_method_checked (method, context, error);
1850 return_val_if_nok (error, NULL);
1851 addr = mono_compile_method_checked (method, error);
1852 return_val_if_nok (error, NULL);
1853 return mini_add_method_trampoline (method, addr, mono_method_needs_static_rgctx_invoke (method, FALSE), FALSE);
1855 case MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE: {
1856 MonoJumpInfoVirtMethod *info = (MonoJumpInfoVirtMethod *)data;
1857 MonoClass *iface_class = info->method->klass;
1858 MonoMethod *method;
1859 MonoClass *impl_class;
1860 int ioffset, slot;
1862 mono_class_setup_vtable (info->klass);
1863 // FIXME: Check type load
1864 if (mono_class_is_interface (iface_class)) {
1865 ioffset = mono_class_interface_offset (info->klass, iface_class);
1866 g_assert (ioffset != -1);
1867 } else {
1868 ioffset = 0;
1870 slot = mono_method_get_vtable_slot (info->method);
1871 g_assert (slot != -1);
1872 g_assert (info->klass->vtable);
1873 method = info->klass->vtable [ioffset + slot];
1875 impl_class = method->klass;
1876 if (MONO_TYPE_IS_REFERENCE (&impl_class->byval_arg))
1877 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_REF);
1878 else if (mono_class_is_nullable (impl_class))
1879 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_NULLABLE);
1880 else
1881 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_VTYPE);
1883 #ifndef DISABLE_REMOTING
1884 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK: {
1885 MonoMethod *remoting_invoke_method = mono_marshal_get_remoting_invoke_with_check ((MonoMethod *)data, error);
1886 return_val_if_nok (error, NULL);
1887 return mono_compile_method_checked (remoting_invoke_method, error);
1889 #endif
1890 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE:
1891 return mono_domain_alloc0 (domain, sizeof (gpointer));
1892 case MONO_RGCTX_INFO_CLASS_FIELD:
1893 return data;
1894 case MONO_RGCTX_INFO_FIELD_OFFSET: {
1895 MonoClassField *field = (MonoClassField *)data;
1897 /* The value is offset by 1 */
1898 if (field->parent->valuetype && !(field->type->attrs & FIELD_ATTRIBUTE_STATIC))
1899 return GUINT_TO_POINTER (field->offset - sizeof (MonoObject) + 1);
1900 else
1901 return GUINT_TO_POINTER (field->offset + 1);
1903 case MONO_RGCTX_INFO_METHOD_RGCTX: {
1904 MonoMethodInflated *method = (MonoMethodInflated *)data;
1906 g_assert (method->method.method.is_inflated);
1908 return mini_method_get_rgctx ((MonoMethod*)method);
1910 case MONO_RGCTX_INFO_METHOD_CONTEXT: {
1911 MonoMethodInflated *method = (MonoMethodInflated *)data;
1913 g_assert (method->method.method.is_inflated);
1914 g_assert (method->context.method_inst);
1916 return method->context.method_inst;
1918 case MONO_RGCTX_INFO_SIG_GSHAREDVT_IN_TRAMPOLINE_CALLI: {
1919 MonoMethodSignature *gsig = (MonoMethodSignature *)oti->data;
1920 MonoMethodSignature *sig = (MonoMethodSignature *)data;
1921 gpointer addr;
1924 * This is an indirect call to the address passed by the caller in the rgctx reg.
1926 addr = mini_get_gsharedvt_wrapper (TRUE, NULL, sig, gsig, -1, TRUE);
1927 return addr;
1929 case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI: {
1930 MonoMethodSignature *gsig = (MonoMethodSignature *)oti->data;
1931 MonoMethodSignature *sig = (MonoMethodSignature *)data;
1932 gpointer addr;
1935 * This is an indirect call to the address passed by the caller in the rgctx reg.
1937 addr = mini_get_gsharedvt_wrapper (FALSE, NULL, sig, gsig, -1, TRUE);
1938 return addr;
1940 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE:
1941 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT: {
1942 MonoJumpInfoGSharedVtCall *call_info = (MonoJumpInfoGSharedVtCall *)data;
1943 MonoMethodSignature *call_sig;
1944 MonoMethod *method;
1945 gpointer addr;
1946 MonoJitInfo *callee_ji;
1947 gboolean virtual_ = oti->info_type == MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT;
1948 gint32 vcall_offset;
1949 gboolean callee_gsharedvt;
1951 /* This is the original generic signature used by the caller */
1952 call_sig = call_info->sig;
1953 /* This is the instantiated method which is called */
1954 method = call_info->method;
1956 g_assert (method->is_inflated);
1958 if (mono_llvm_only && (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED))
1959 method = mono_marshal_get_synchronized_wrapper (method);
1961 if (!virtual_) {
1962 addr = mono_compile_method_checked (method, error);
1963 return_val_if_nok (error, NULL);
1964 } else
1965 addr = NULL;
1967 if (virtual_) {
1968 /* Same as in mono_emit_method_call_full () */
1969 if ((method->klass->parent == mono_defaults.multicastdelegate_class) && (!strcmp (method->name, "Invoke"))) {
1970 /* See mono_emit_method_call_full () */
1971 /* The gsharedvt trampoline will recognize this constant */
1972 vcall_offset = MONO_GSHAREDVT_DEL_INVOKE_VT_OFFSET;
1973 } else if (mono_class_is_interface (method->klass)) {
1974 guint32 imt_slot = mono_method_get_imt_slot (method);
1975 vcall_offset = ((gint32)imt_slot - MONO_IMT_SIZE) * SIZEOF_VOID_P;
1976 } else {
1977 vcall_offset = G_STRUCT_OFFSET (MonoVTable, vtable) +
1978 ((mono_method_get_vtable_index (method)) * (SIZEOF_VOID_P));
1980 } else {
1981 vcall_offset = -1;
1984 // FIXME: This loads information in the AOT case
1985 callee_ji = mini_jit_info_table_find (mono_domain_get (), (char *)mono_get_addr_from_ftnptr (addr), NULL);
1986 callee_gsharedvt = ji_is_gsharedvt (callee_ji);
1989 * For gsharedvt calls made out of gsharedvt methods, the callee could end up being a gsharedvt method, or a normal
1990 * non-shared method. The latter call cannot be patched, so instead of using a normal call, we make an indirect
1991 * call through the rgctx, in effect patching the rgctx entry instead of the call site.
1992 * For virtual calls, the caller might be a normal or a gsharedvt method. Since there is only one vtable slot,
1993 * this difference needs to be handed on the caller side. This is currently implemented by adding a gsharedvt-in
1994 * trampoline to all gsharedvt methods and storing this trampoline into the vtable slot. Virtual calls made from
1995 * gsharedvt methods always go through a gsharedvt-out trampoline, so the calling sequence is:
1996 * caller -> out trampoline -> in trampoline -> callee
1997 * This is not very efficient, but it is easy to implement.
1999 if (virtual_ || !callee_gsharedvt) {
2000 MonoMethodSignature *sig, *gsig;
2002 g_assert (method->is_inflated);
2004 sig = mono_method_signature (method);
2005 gsig = call_sig;
2007 if (mono_llvm_only) {
2008 if (mini_is_gsharedvt_variable_signature (call_sig)) {
2009 /* The virtual case doesn't go through this code */
2010 g_assert (!virtual_);
2012 sig = mono_method_signature (jinfo_get_method (callee_ji));
2013 gpointer out_wrapper = mini_get_gsharedvt_wrapper (FALSE, NULL, sig, gsig, -1, FALSE);
2014 MonoFtnDesc *out_wrapper_arg = mini_create_llvmonly_ftndesc (domain, callee_ji->code_start, mini_method_get_rgctx (method));
2016 /* Returns an ftndesc */
2017 addr = mini_create_llvmonly_ftndesc (domain, out_wrapper, out_wrapper_arg);
2018 } else {
2019 addr = mini_create_llvmonly_ftndesc (domain, addr, mini_method_get_rgctx (method));
2021 } else {
2022 addr = mini_get_gsharedvt_wrapper (FALSE, addr, sig, gsig, vcall_offset, FALSE);
2024 #if 0
2025 if (virtual)
2026 printf ("OUT-VCALL: %s\n", mono_method_full_name (method, TRUE));
2027 else
2028 printf ("OUT: %s\n", mono_method_full_name (method, TRUE));
2029 #endif
2030 } else if (callee_gsharedvt) {
2031 MonoMethodSignature *sig, *gsig;
2034 * This is a combination of the out and in cases, since both the caller and the callee are gsharedvt methods.
2035 * The caller and the callee can use different gsharedvt signatures, so we have to add both an out and an in
2036 * trampoline, i.e.:
2037 * class Base<T> {
2038 * public void foo<T1> (T1 t1, T t, object o) {}
2040 * class AClass : Base<long> {
2041 * public void bar<T> (T t, long time, object o) {
2042 * foo (t, time, o);
2045 * Here, the caller uses !!0,long, while the callee uses !!0,!0
2046 * FIXME: Optimize this.
2049 if (mono_llvm_only) {
2050 /* Both wrappers receive an extra <addr, rgctx> argument */
2051 sig = mono_method_signature (method);
2052 gsig = mono_method_signature (jinfo_get_method (callee_ji));
2054 /* Return a function descriptor */
2056 if (mini_is_gsharedvt_variable_signature (call_sig)) {
2058 * This is not an optimization, but its needed, since the concrete signature 'sig'
2059 * might not exist at all in IL, so the AOT compiler cannot generate the wrappers
2060 * for it.
2062 addr = mini_create_llvmonly_ftndesc (domain, callee_ji->code_start, mini_method_get_rgctx (method));
2063 } else if (mini_is_gsharedvt_variable_signature (gsig)) {
2064 gpointer in_wrapper = mini_get_gsharedvt_wrapper (TRUE, callee_ji->code_start, sig, gsig, -1, FALSE);
2066 gpointer in_wrapper_arg = mini_create_llvmonly_ftndesc (domain, callee_ji->code_start, mini_method_get_rgctx (method));
2068 addr = mini_create_llvmonly_ftndesc (domain, in_wrapper, in_wrapper_arg);
2069 } else {
2070 addr = mini_create_llvmonly_ftndesc (domain, addr, mini_method_get_rgctx (method));
2072 } else if (call_sig == mono_method_signature (method)) {
2073 } else {
2074 sig = mono_method_signature (method);
2075 gsig = mono_method_signature (jinfo_get_method (callee_ji));
2077 addr = mini_get_gsharedvt_wrapper (TRUE, callee_ji->code_start, sig, gsig, -1, FALSE);
2079 sig = mono_method_signature (method);
2080 gsig = call_sig;
2082 addr = mini_get_gsharedvt_wrapper (FALSE, addr, sig, gsig, -1, FALSE);
2084 //printf ("OUT-IN-RGCTX: %s\n", mono_method_full_name (method, TRUE));
2088 return addr;
2090 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO: {
2091 MonoGSharedVtMethodInfo *info = (MonoGSharedVtMethodInfo *)data;
2092 MonoGSharedVtMethodRuntimeInfo *res;
2093 MonoType *t;
2094 int i, offset, align, size;
2096 // FIXME:
2097 res = (MonoGSharedVtMethodRuntimeInfo *)g_malloc0 (sizeof (MonoGSharedVtMethodRuntimeInfo) + (info->num_entries * sizeof (gpointer)));
2099 offset = 0;
2100 for (i = 0; i < info->num_entries; ++i) {
2101 MonoRuntimeGenericContextInfoTemplate *template_ = &info->entries [i];
2103 switch (template_->info_type) {
2104 case MONO_RGCTX_INFO_LOCAL_OFFSET:
2105 t = (MonoType *)template_->data;
2107 size = mono_type_size (t, &align);
2109 if (align < sizeof (gpointer))
2110 align = sizeof (gpointer);
2111 if (MONO_TYPE_ISSTRUCT (t) && align < 2 * sizeof (gpointer))
2112 align = 2 * sizeof (gpointer);
2114 // FIXME: Do the same things as alloc_stack_slots
2115 offset += align - 1;
2116 offset &= ~(align - 1);
2117 res->entries [i] = GINT_TO_POINTER (offset);
2118 offset += size;
2119 break;
2120 default:
2121 res->entries [i] = instantiate_info (domain, template_, context, klass, error);
2122 if (!mono_error_ok (error))
2123 return NULL;
2124 break;
2127 res->locals_size = offset;
2129 return res;
2131 default:
2132 g_assert_not_reached ();
2134 /* Not reached */
2135 return NULL;
2139 * LOCKING: loader lock
2141 static void
2142 fill_in_rgctx_template_slot (MonoClass *klass, int type_argc, int index, gpointer data, MonoRgctxInfoType info_type)
2144 MonoRuntimeGenericContextTemplate *template_ = mono_class_get_runtime_generic_context_template (klass);
2145 MonoClass *subclass;
2147 rgctx_template_set_slot (klass->image, template_, type_argc, index, data, info_type);
2149 /* Recurse for all subclasses */
2150 if (generic_subclass_hash)
2151 subclass = (MonoClass *)g_hash_table_lookup (generic_subclass_hash, klass);
2152 else
2153 subclass = NULL;
2155 while (subclass) {
2156 MonoRuntimeGenericContextInfoTemplate subclass_oti;
2157 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
2159 g_assert (subclass_template);
2161 subclass_oti = class_get_rgctx_template_oti (subclass->parent, type_argc, index, FALSE, FALSE, NULL);
2162 g_assert (subclass_oti.data);
2164 fill_in_rgctx_template_slot (subclass, type_argc, index, subclass_oti.data, info_type);
2166 subclass = subclass_template->next_subclass;
2170 const char*
2171 mono_rgctx_info_type_to_str (MonoRgctxInfoType type)
2173 switch (type) {
2174 case MONO_RGCTX_INFO_STATIC_DATA: return "STATIC_DATA";
2175 case MONO_RGCTX_INFO_KLASS: return "KLASS";
2176 case MONO_RGCTX_INFO_ELEMENT_KLASS: return "ELEMENT_KLASS";
2177 case MONO_RGCTX_INFO_VTABLE: return "VTABLE";
2178 case MONO_RGCTX_INFO_TYPE: return "TYPE";
2179 case MONO_RGCTX_INFO_REFLECTION_TYPE: return "REFLECTION_TYPE";
2180 case MONO_RGCTX_INFO_METHOD: return "METHOD";
2181 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO: return "GSHAREDVT_INFO";
2182 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE: return "GENERIC_METHOD_CODE";
2183 case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER: return "GSHAREDVT_OUT_WRAPPER";
2184 case MONO_RGCTX_INFO_CLASS_FIELD: return "CLASS_FIELD";
2185 case MONO_RGCTX_INFO_METHOD_RGCTX: return "METHOD_RGCTX";
2186 case MONO_RGCTX_INFO_METHOD_CONTEXT: return "METHOD_CONTEXT";
2187 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK: return "REMOTING_INVOKE_WITH_CHECK";
2188 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE: return "METHOD_DELEGATE_CODE";
2189 case MONO_RGCTX_INFO_CAST_CACHE: return "CAST_CACHE";
2190 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE: return "ARRAY_ELEMENT_SIZE";
2191 case MONO_RGCTX_INFO_VALUE_SIZE: return "VALUE_SIZE";
2192 case MONO_RGCTX_INFO_CLASS_BOX_TYPE: return "CLASS_BOX_TYPE";
2193 case MONO_RGCTX_INFO_CLASS_IS_REF_OR_CONTAINS_REFS: return "CLASS_IS_REF_OR_CONTAINS_REFS";
2194 case MONO_RGCTX_INFO_FIELD_OFFSET: return "FIELD_OFFSET";
2195 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE: return "METHOD_GSHAREDVT_OUT_TRAMPOLINE";
2196 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT: return "METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT";
2197 case MONO_RGCTX_INFO_SIG_GSHAREDVT_IN_TRAMPOLINE_CALLI: return "SIG_GSHAREDVT_IN_TRAMPOLINE_CALLI";
2198 case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI: return "SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI";
2199 case MONO_RGCTX_INFO_MEMCPY: return "MEMCPY";
2200 case MONO_RGCTX_INFO_BZERO: return "BZERO";
2201 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX: return "NULLABLE_CLASS_BOX";
2202 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: return "NULLABLE_CLASS_UNBOX";
2203 case MONO_RGCTX_INFO_VIRT_METHOD_CODE: return "VIRT_METHOD_CODE";
2204 case MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE: return "VIRT_METHOD_BOX_TYPE";
2205 default:
2206 return "<UNKNOWN RGCTX INFO TYPE>";
2210 G_GNUC_UNUSED static char*
2211 rgctx_info_to_str (MonoRgctxInfoType info_type, gpointer data)
2213 switch (info_type) {
2214 case MONO_RGCTX_INFO_VTABLE:
2215 return mono_type_full_name ((MonoType*)data);
2216 default:
2217 return g_strdup_printf ("<%p>", data);
2222 * LOCKING: loader lock
2224 static int
2225 register_info (MonoClass *klass, int type_argc, gpointer data, MonoRgctxInfoType info_type)
2227 int i;
2228 MonoRuntimeGenericContextTemplate *template_ = mono_class_get_runtime_generic_context_template (klass);
2229 MonoClass *parent;
2230 MonoRuntimeGenericContextInfoTemplate *oti;
2232 for (i = 0, oti = get_info_templates (template_, type_argc); oti; ++i, oti = oti->next) {
2233 if (!oti->data)
2234 break;
2237 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)));
2239 /* Mark the slot as used in all parent classes (until we find
2240 a parent class which already has it marked used). */
2241 parent = klass->parent;
2242 while (parent != NULL) {
2243 MonoRuntimeGenericContextTemplate *parent_template;
2244 MonoRuntimeGenericContextInfoTemplate *oti;
2246 if (mono_class_is_ginst (parent))
2247 parent = mono_class_get_generic_class (parent)->container_class;
2249 parent_template = mono_class_get_runtime_generic_context_template (parent);
2250 oti = rgctx_template_get_other_slot (parent_template, type_argc, i);
2252 if (oti && oti->data)
2253 break;
2255 rgctx_template_set_slot (parent->image, parent_template, type_argc, i,
2256 MONO_RGCTX_SLOT_USED_MARKER, (MonoRgctxInfoType)0);
2258 parent = parent->parent;
2261 /* Fill in the slot in this class and in all subclasses
2262 recursively. */
2263 fill_in_rgctx_template_slot (klass, type_argc, i, data, info_type);
2265 return i;
2268 static gboolean
2269 info_equal (gpointer data1, gpointer data2, MonoRgctxInfoType info_type)
2271 switch (info_type) {
2272 case MONO_RGCTX_INFO_STATIC_DATA:
2273 case MONO_RGCTX_INFO_KLASS:
2274 case MONO_RGCTX_INFO_ELEMENT_KLASS:
2275 case MONO_RGCTX_INFO_VTABLE:
2276 case MONO_RGCTX_INFO_TYPE:
2277 case MONO_RGCTX_INFO_REFLECTION_TYPE:
2278 case MONO_RGCTX_INFO_CAST_CACHE:
2279 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
2280 case MONO_RGCTX_INFO_VALUE_SIZE:
2281 case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
2282 case MONO_RGCTX_INFO_CLASS_IS_REF_OR_CONTAINS_REFS:
2283 case MONO_RGCTX_INFO_MEMCPY:
2284 case MONO_RGCTX_INFO_BZERO:
2285 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
2286 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX:
2287 return mono_class_from_mono_type ((MonoType *)data1) == mono_class_from_mono_type ((MonoType *)data2);
2288 case MONO_RGCTX_INFO_METHOD:
2289 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO:
2290 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
2291 case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER:
2292 case MONO_RGCTX_INFO_CLASS_FIELD:
2293 case MONO_RGCTX_INFO_FIELD_OFFSET:
2294 case MONO_RGCTX_INFO_METHOD_RGCTX:
2295 case MONO_RGCTX_INFO_METHOD_CONTEXT:
2296 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
2297 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE:
2298 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE:
2299 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT:
2300 case MONO_RGCTX_INFO_SIG_GSHAREDVT_IN_TRAMPOLINE_CALLI:
2301 case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI:
2302 return data1 == data2;
2303 case MONO_RGCTX_INFO_VIRT_METHOD_CODE:
2304 case MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE: {
2305 MonoJumpInfoVirtMethod *info1 = (MonoJumpInfoVirtMethod *)data1;
2306 MonoJumpInfoVirtMethod *info2 = (MonoJumpInfoVirtMethod *)data2;
2308 return info1->klass == info2->klass && info1->method == info2->method;
2310 default:
2311 g_assert_not_reached ();
2313 /* never reached */
2314 return FALSE;
2318 * mini_rgctx_info_type_to_patch_info_type:
2320 * Return the type of the runtime object referred to by INFO_TYPE.
2322 MonoJumpInfoType
2323 mini_rgctx_info_type_to_patch_info_type (MonoRgctxInfoType info_type)
2325 switch (info_type) {
2326 case MONO_RGCTX_INFO_STATIC_DATA:
2327 case MONO_RGCTX_INFO_KLASS:
2328 case MONO_RGCTX_INFO_ELEMENT_KLASS:
2329 case MONO_RGCTX_INFO_VTABLE:
2330 case MONO_RGCTX_INFO_TYPE:
2331 case MONO_RGCTX_INFO_REFLECTION_TYPE:
2332 case MONO_RGCTX_INFO_CAST_CACHE:
2333 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
2334 case MONO_RGCTX_INFO_VALUE_SIZE:
2335 case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
2336 case MONO_RGCTX_INFO_CLASS_IS_REF_OR_CONTAINS_REFS:
2337 case MONO_RGCTX_INFO_MEMCPY:
2338 case MONO_RGCTX_INFO_BZERO:
2339 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
2340 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX:
2341 case MONO_RGCTX_INFO_LOCAL_OFFSET:
2342 return MONO_PATCH_INFO_CLASS;
2343 case MONO_RGCTX_INFO_FIELD_OFFSET:
2344 return MONO_PATCH_INFO_FIELD;
2345 default:
2346 g_assert_not_reached ();
2347 return (MonoJumpInfoType)-1;
2351 static int
2352 lookup_or_register_info (MonoClass *klass, int type_argc, gpointer data, MonoRgctxInfoType info_type,
2353 MonoGenericContext *generic_context)
2355 MonoRuntimeGenericContextTemplate *rgctx_template =
2356 mono_class_get_runtime_generic_context_template (klass);
2357 MonoRuntimeGenericContextInfoTemplate *oti_list, *oti;
2358 int i;
2360 klass = get_shared_class (klass);
2362 mono_loader_lock ();
2364 if (info_has_identity (info_type)) {
2365 oti_list = get_info_templates (rgctx_template, type_argc);
2367 for (oti = oti_list, i = 0; oti; oti = oti->next, ++i) {
2368 gpointer inflated_data;
2370 if (oti->info_type != info_type || !oti->data)
2371 continue;
2373 inflated_data = inflate_info (oti, generic_context, klass, TRUE);
2375 if (info_equal (data, inflated_data, info_type)) {
2376 free_inflated_info (info_type, inflated_data);
2377 mono_loader_unlock ();
2378 return i;
2380 free_inflated_info (info_type, inflated_data);
2384 /* We haven't found the info */
2385 i = register_info (klass, type_argc, data, info_type);
2387 /* interlocked by loader lock */
2388 if (i > UnlockedRead (&rgctx_max_slot_number))
2389 UnlockedWrite (&rgctx_max_slot_number, i);
2391 mono_loader_unlock ();
2393 return i;
2397 * mono_method_lookup_or_register_info:
2398 * @method: a method
2399 * @in_mrgctx: whether to put the data into the MRGCTX
2400 * @data: the info data
2401 * @info_type: the type of info to register about data
2402 * @generic_context: a generic context
2404 * Looks up and, if necessary, adds information about data/info_type in
2405 * method's or method's class runtime generic context. Returns the
2406 * encoded slot number.
2408 guint32
2409 mono_method_lookup_or_register_info (MonoMethod *method, gboolean in_mrgctx, gpointer data,
2410 MonoRgctxInfoType info_type, MonoGenericContext *generic_context)
2412 MonoClass *klass = method->klass;
2413 int type_argc = 0, index;
2415 if (in_mrgctx) {
2416 MonoGenericInst *method_inst = mono_method_get_context (method)->method_inst;
2418 if (method_inst) {
2419 g_assert (method->is_inflated && method_inst);
2420 type_argc = method_inst->type_argc;
2421 g_assert (type_argc > 0);
2425 index = lookup_or_register_info (klass, type_argc, data, info_type, generic_context);
2427 //g_print ("rgctx item at index %d argc %d\n", index, type_argc);
2429 if (in_mrgctx)
2430 return MONO_RGCTX_SLOT_MAKE_MRGCTX (index);
2431 else
2432 return MONO_RGCTX_SLOT_MAKE_RGCTX (index);
2436 * mono_class_rgctx_get_array_size:
2437 * @n: The number of the array
2438 * @mrgctx: Whether it's an MRGCTX as opposed to a RGCTX.
2440 * Returns the number of slots in the n'th array of a (M)RGCTX. That
2441 * number includes the slot for linking and - for MRGCTXs - the two
2442 * slots in the first array for additional information.
2445 mono_class_rgctx_get_array_size (int n, gboolean mrgctx)
2447 g_assert (n >= 0 && n < 30);
2449 if (mrgctx)
2450 return 6 << n;
2451 else
2452 return 4 << n;
2456 * LOCKING: domain lock
2458 static gpointer*
2459 alloc_rgctx_array (MonoDomain *domain, int n, gboolean is_mrgctx)
2461 gint32 size = mono_class_rgctx_get_array_size (n, is_mrgctx) * sizeof (gpointer);
2462 gpointer *array = (gpointer *)mono_domain_alloc0 (domain, size);
2464 /* interlocked by domain lock (by definition) */
2465 if (is_mrgctx) {
2466 UnlockedIncrement (&mrgctx_num_arrays_allocated);
2467 UnlockedAdd (&mrgctx_bytes_allocated, size);
2468 } else {
2469 UnlockedIncrement (&rgctx_num_arrays_allocated);
2470 UnlockedAdd (&rgctx_bytes_allocated, size);
2473 return array;
2476 static gpointer
2477 fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContext *rgctx, guint32 slot,
2478 MonoGenericInst *method_inst, gboolean is_mrgctx, MonoError *error)
2480 gpointer info;
2481 int i, first_slot, size;
2482 MonoDomain *domain = class_vtable->domain;
2483 MonoClass *klass = class_vtable->klass;
2484 MonoGenericContext *class_context = mono_class_is_ginst (klass) ? &mono_class_get_generic_class (klass)->context : NULL;
2485 MonoRuntimeGenericContextInfoTemplate oti;
2486 MonoGenericContext context = { class_context ? class_context->class_inst : NULL, method_inst };
2487 int rgctx_index;
2488 gboolean do_free;
2490 error_init (error);
2492 g_assert (rgctx);
2494 mono_domain_lock (domain);
2496 /* First check whether that slot isn't already instantiated.
2497 This might happen because lookup doesn't lock. Allocate
2498 arrays on the way. */
2499 first_slot = 0;
2500 size = mono_class_rgctx_get_array_size (0, is_mrgctx);
2501 if (is_mrgctx)
2502 size -= MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
2503 for (i = 0; ; ++i) {
2504 int offset;
2506 if (is_mrgctx && i == 0)
2507 offset = MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
2508 else
2509 offset = 0;
2511 if (slot < first_slot + size - 1) {
2512 rgctx_index = slot - first_slot + 1 + offset;
2513 info = rgctx [rgctx_index];
2514 if (info) {
2515 mono_domain_unlock (domain);
2516 return info;
2518 break;
2520 if (!rgctx [offset + 0])
2521 rgctx [offset + 0] = alloc_rgctx_array (domain, i + 1, is_mrgctx);
2522 rgctx = (void **)rgctx [offset + 0];
2523 first_slot += size - 1;
2524 size = mono_class_rgctx_get_array_size (i + 1, is_mrgctx);
2527 g_assert (!rgctx [rgctx_index]);
2529 mono_domain_unlock (domain);
2531 oti = class_get_rgctx_template_oti (get_shared_class (klass),
2532 method_inst ? method_inst->type_argc : 0, slot, TRUE, TRUE, &do_free);
2533 /* This might take the loader lock */
2534 info = instantiate_info (domain, &oti, &context, klass, error);
2535 return_val_if_nok (error, NULL);
2536 g_assert (info);
2539 if (method_inst)
2540 g_print ("filling mrgctx slot %d table %d index %d\n", slot, i, rgctx_index);
2543 /*FIXME We should use CAS here, no need to take a lock.*/
2544 mono_domain_lock (domain);
2546 /* Check whether the slot hasn't been instantiated in the
2547 meantime. */
2548 if (rgctx [rgctx_index])
2549 info = rgctx [rgctx_index];
2550 else
2551 rgctx [rgctx_index] = info;
2553 mono_domain_unlock (domain);
2555 if (do_free)
2556 free_inflated_info (oti.info_type, oti.data);
2558 return info;
2562 * mono_class_fill_runtime_generic_context:
2563 * @class_vtable: a vtable
2564 * @slot: a slot index to be instantiated
2566 * Instantiates a slot in the RGCTX, returning its value.
2568 gpointer
2569 mono_class_fill_runtime_generic_context (MonoVTable *class_vtable, guint32 slot, MonoError *error)
2571 MonoDomain *domain = class_vtable->domain;
2572 MonoRuntimeGenericContext *rgctx;
2573 gpointer info;
2575 error_init (error);
2577 mono_domain_lock (domain);
2579 rgctx = class_vtable->runtime_generic_context;
2580 if (!rgctx) {
2581 rgctx = alloc_rgctx_array (domain, 0, FALSE);
2582 class_vtable->runtime_generic_context = rgctx;
2583 UnlockedIncrement (&rgctx_num_allocated); /* interlocked by domain lock */
2586 mono_domain_unlock (domain);
2588 info = fill_runtime_generic_context (class_vtable, rgctx, slot, NULL, FALSE, error);
2590 DEBUG (printf ("get rgctx slot: %s %d -> %p\n", mono_type_full_name (&class_vtable->klass->byval_arg), slot, info));
2592 return info;
2596 * mono_method_fill_runtime_generic_context:
2597 * @mrgctx: an MRGCTX
2598 * @slot: a slot index to be instantiated
2600 * Instantiates a slot in the MRGCTX.
2602 gpointer
2603 mono_method_fill_runtime_generic_context (MonoMethodRuntimeGenericContext *mrgctx, guint32 slot, MonoError *error)
2605 gpointer info;
2607 info = fill_runtime_generic_context (mrgctx->class_vtable, (MonoRuntimeGenericContext*)mrgctx, slot, mrgctx->method_inst, TRUE, error);
2609 return info;
2612 static guint
2613 mrgctx_hash_func (gconstpointer key)
2615 const MonoMethodRuntimeGenericContext *mrgctx = (const MonoMethodRuntimeGenericContext *)key;
2617 return mono_aligned_addr_hash (mrgctx->class_vtable) ^ mono_metadata_generic_inst_hash (mrgctx->method_inst);
2620 static gboolean
2621 mrgctx_equal_func (gconstpointer a, gconstpointer b)
2623 const MonoMethodRuntimeGenericContext *mrgctx1 = (const MonoMethodRuntimeGenericContext *)a;
2624 const MonoMethodRuntimeGenericContext *mrgctx2 = (const MonoMethodRuntimeGenericContext *)b;
2626 return mrgctx1->class_vtable == mrgctx2->class_vtable &&
2627 mono_metadata_generic_inst_equal (mrgctx1->method_inst, mrgctx2->method_inst);
2631 * mini_method_get_mrgctx:
2632 * @class_vtable: a vtable
2633 * @method: an inflated method
2635 * Returns the MRGCTX for METHOD.
2637 * LOCKING: Take the domain lock.
2639 static MonoMethodRuntimeGenericContext*
2640 mini_method_get_mrgctx (MonoVTable *class_vtable, MonoMethod *method)
2642 MonoDomain *domain = class_vtable->domain;
2643 MonoMethodRuntimeGenericContext *mrgctx;
2644 MonoMethodRuntimeGenericContext key;
2645 MonoGenericInst *method_inst = mini_method_get_context (method)->method_inst;
2646 MonoJitDomainInfo *domain_info = domain_jit_info (domain);
2648 g_assert (!mono_class_is_gtd (class_vtable->klass));
2650 mono_domain_lock (domain);
2652 if (!method_inst) {
2653 g_assert (mini_method_is_default_method (method));
2655 if (!domain_info->mrgctx_hash)
2656 domain_info->mrgctx_hash = g_hash_table_new (NULL, NULL);
2657 mrgctx = g_hash_table_lookup (domain_info->mrgctx_hash, method);
2658 } else {
2659 g_assert (!method_inst->is_open);
2661 if (!domain_info->method_rgctx_hash)
2662 domain_info->method_rgctx_hash = g_hash_table_new (mrgctx_hash_func, mrgctx_equal_func);
2664 key.class_vtable = class_vtable;
2665 key.method_inst = method_inst;
2667 mrgctx = (MonoMethodRuntimeGenericContext *)g_hash_table_lookup (domain_info->method_rgctx_hash, &key);
2670 if (!mrgctx) {
2671 //int i;
2673 mrgctx = (MonoMethodRuntimeGenericContext*)alloc_rgctx_array (domain, 0, TRUE);
2674 mrgctx->class_vtable = class_vtable;
2675 mrgctx->method_inst = method_inst;
2677 if (!method_inst)
2678 g_hash_table_insert (domain_info->mrgctx_hash, method, mrgctx);
2679 else
2680 g_hash_table_insert (domain_info->method_rgctx_hash, mrgctx, mrgctx);
2683 g_print ("mrgctx alloced for %s <", mono_type_get_full_name (class_vtable->klass));
2684 for (i = 0; i < method_inst->type_argc; ++i)
2685 g_print ("%s, ", mono_type_full_name (method_inst->type_argv [i]));
2686 g_print (">\n");
2690 mono_domain_unlock (domain);
2692 g_assert (mrgctx);
2694 return mrgctx;
2697 static gboolean
2698 type_is_sharable (MonoType *type, gboolean allow_type_vars, gboolean allow_partial)
2700 if (allow_type_vars && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR)) {
2701 MonoType *constraint = type->data.generic_param->gshared_constraint;
2702 if (!constraint)
2703 return TRUE;
2704 type = constraint;
2707 if (MONO_TYPE_IS_REFERENCE (type))
2708 return TRUE;
2710 /* Allow non ref arguments if they are primitive types or enums (partial sharing). */
2711 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)))
2712 return TRUE;
2714 if (allow_partial && !type->byref && type->type == MONO_TYPE_GENERICINST && MONO_TYPE_ISSTRUCT (type)) {
2715 MonoGenericClass *gclass = type->data.generic_class;
2717 if (gclass->context.class_inst && !mini_generic_inst_is_sharable (gclass->context.class_inst, allow_type_vars, allow_partial))
2718 return FALSE;
2719 if (gclass->context.method_inst && !mini_generic_inst_is_sharable (gclass->context.method_inst, allow_type_vars, allow_partial))
2720 return FALSE;
2721 if (mono_class_is_nullable (mono_class_from_mono_type (type)))
2722 return FALSE;
2723 return TRUE;
2726 return FALSE;
2729 gboolean
2730 mini_generic_inst_is_sharable (MonoGenericInst *inst, gboolean allow_type_vars,
2731 gboolean allow_partial)
2733 int i;
2735 for (i = 0; i < inst->type_argc; ++i) {
2736 if (!type_is_sharable (inst->type_argv [i], allow_type_vars, allow_partial))
2737 return FALSE;
2740 return TRUE;
2744 * mono_is_partially_sharable_inst:
2746 * Return TRUE if INST has ref and non-ref type arguments.
2748 gboolean
2749 mono_is_partially_sharable_inst (MonoGenericInst *inst)
2751 int i;
2752 gboolean has_refs = FALSE, has_non_refs = FALSE;
2754 for (i = 0; i < inst->type_argc; ++i) {
2755 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)
2756 has_refs = TRUE;
2757 else
2758 has_non_refs = TRUE;
2761 return has_refs && has_non_refs;
2765 * mono_generic_context_is_sharable_full:
2766 * @context: a generic context
2768 * Returns whether the generic context is sharable. A generic context
2769 * is sharable iff all of its type arguments are reference type, or some of them have a
2770 * reference type, and ALLOW_PARTIAL is TRUE.
2772 gboolean
2773 mono_generic_context_is_sharable_full (MonoGenericContext *context,
2774 gboolean allow_type_vars,
2775 gboolean allow_partial)
2777 g_assert (context->class_inst || context->method_inst);
2779 if (context->class_inst && !mini_generic_inst_is_sharable (context->class_inst, allow_type_vars, allow_partial))
2780 return FALSE;
2782 if (context->method_inst && !mini_generic_inst_is_sharable (context->method_inst, allow_type_vars, allow_partial))
2783 return FALSE;
2785 return TRUE;
2788 gboolean
2789 mono_generic_context_is_sharable (MonoGenericContext *context, gboolean allow_type_vars)
2791 return mono_generic_context_is_sharable_full (context, allow_type_vars, partial_sharing_supported ());
2795 * mono_method_is_generic_impl:
2796 * @method: a method
2798 * Returns whether the method is either generic or part of a generic
2799 * class.
2801 gboolean
2802 mono_method_is_generic_impl (MonoMethod *method)
2804 if (method->is_inflated)
2805 return TRUE;
2806 /* We don't treat wrappers as generic code, i.e., we never
2807 apply generic sharing to them. This is especially
2808 important for static rgctx invoke wrappers, which only work
2809 if not compiled with sharing. */
2810 if (method->wrapper_type != MONO_WRAPPER_NONE)
2811 return FALSE;
2812 if (mono_class_is_gtd (method->klass))
2813 return TRUE;
2814 return FALSE;
2817 static gboolean
2818 has_constraints (MonoGenericContainer *container)
2820 //int i;
2822 return FALSE;
2824 g_assert (container->type_argc > 0);
2825 g_assert (container->type_params);
2827 for (i = 0; i < container->type_argc; ++i)
2828 if (container->type_params [i].constraints)
2829 return TRUE;
2830 return FALSE;
2834 static gboolean
2835 mini_method_is_open (MonoMethod *method)
2837 if (method->is_inflated) {
2838 MonoGenericContext *ctx = mono_method_get_context (method);
2840 if (ctx->class_inst && ctx->class_inst->is_open)
2841 return TRUE;
2842 if (ctx->method_inst && ctx->method_inst->is_open)
2843 return TRUE;
2845 return FALSE;
2848 /* Lazy class loading functions */
2849 static GENERATE_TRY_GET_CLASS_WITH_CACHE (iasync_state_machine, "System.Runtime.CompilerServices", "IAsyncStateMachine")
2851 static G_GNUC_UNUSED gboolean
2852 is_async_state_machine_class (MonoClass *klass)
2854 MonoClass *iclass;
2856 return FALSE;
2858 iclass = mono_class_try_get_iasync_state_machine_class ();
2860 if (iclass && klass->valuetype && mono_class_is_assignable_from (iclass, klass))
2861 return TRUE;
2862 return FALSE;
2865 static G_GNUC_UNUSED gboolean
2866 is_async_method (MonoMethod *method)
2868 ERROR_DECL (error);
2869 MonoCustomAttrInfo *cattr;
2870 MonoMethodSignature *sig;
2871 gboolean res = FALSE;
2872 MonoClass *attr_class;
2874 return FALSE;
2876 attr_class = mono_class_try_get_iasync_state_machine_class ();
2878 /* Do less expensive checks first */
2879 sig = mono_method_signature (method);
2880 if (attr_class && sig && ((sig->ret->type == MONO_TYPE_VOID) ||
2881 (sig->ret->type == MONO_TYPE_CLASS && !strcmp (sig->ret->data.generic_class->container_class->name, "Task")) ||
2882 (sig->ret->type == MONO_TYPE_GENERICINST && !strcmp (sig->ret->data.generic_class->container_class->name, "Task`1")))) {
2883 //printf ("X: %s\n", mono_method_full_name (method, TRUE));
2884 cattr = mono_custom_attrs_from_method_checked (method, error);
2885 if (!is_ok (error)) {
2886 mono_error_cleanup (error); /* FIXME don't swallow the error? */
2887 return FALSE;
2889 if (cattr) {
2890 if (mono_custom_attrs_has_attr (cattr, attr_class))
2891 res = TRUE;
2892 mono_custom_attrs_free (cattr);
2895 return res;
2899 * mono_method_is_generic_sharable_full:
2900 * @method: a method
2901 * @allow_type_vars: whether to regard type variables as reference types
2902 * @allow_partial: whether to allow partial sharing
2903 * @allow_gsharedvt: whenever to allow sharing over valuetypes
2905 * Returns TRUE iff the method is inflated or part of an inflated
2906 * class, its context is sharable and it has no constraints on its
2907 * type parameters. Otherwise returns FALSE.
2909 gboolean
2910 mono_method_is_generic_sharable_full (MonoMethod *method, gboolean allow_type_vars,
2911 gboolean allow_partial, gboolean allow_gsharedvt)
2913 if (!mono_method_is_generic_impl (method))
2914 return FALSE;
2917 if (!mono_debug_count ())
2918 allow_partial = FALSE;
2921 if (!partial_sharing_supported ())
2922 allow_partial = FALSE;
2924 if (mono_class_is_nullable (method->klass))
2925 // FIXME:
2926 allow_partial = FALSE;
2928 if (method->klass->image->dynamic)
2930 * Enabling this causes corlib test failures because the JIT encounters generic instances whose
2931 * instance_size is 0.
2933 allow_partial = FALSE;
2936 * Generic async methods have an associated state machine class which is a generic struct. This struct
2937 * is too large to be handled by gsharedvt so we make it visible to the AOT compiler by disabling sharing
2938 * of the async method and the state machine class.
2940 if (is_async_state_machine_class (method->klass))
2941 return FALSE;
2943 if (allow_gsharedvt && mini_is_gsharedvt_sharable_method (method)) {
2944 if (is_async_method (method))
2945 return FALSE;
2946 return TRUE;
2949 if (method->is_inflated) {
2950 MonoMethodInflated *inflated = (MonoMethodInflated*)method;
2951 MonoGenericContext *context = &inflated->context;
2953 if (!mono_generic_context_is_sharable_full (context, allow_type_vars, allow_partial))
2954 return FALSE;
2956 g_assert (inflated->declaring);
2958 if (inflated->declaring->is_generic) {
2959 if (has_constraints (mono_method_get_generic_container (inflated->declaring)))
2960 return FALSE;
2964 if (mono_class_is_ginst (method->klass)) {
2965 if (!mono_generic_context_is_sharable_full (&mono_class_get_generic_class (method->klass)->context, allow_type_vars, allow_partial))
2966 return FALSE;
2968 g_assert (mono_class_get_generic_class (method->klass)->container_class &&
2969 mono_class_is_gtd (mono_class_get_generic_class (method->klass)->container_class));
2971 if (has_constraints (mono_class_get_generic_container (mono_class_get_generic_class (method->klass)->container_class)))
2972 return FALSE;
2975 if (mono_class_is_gtd (method->klass) && !allow_type_vars)
2976 return FALSE;
2978 /* This does potentially expensive cattr checks, so do it at the end */
2979 if (is_async_method (method)) {
2980 if (mini_method_is_open (method))
2981 /* The JIT can't compile these without sharing */
2982 return TRUE;
2983 return FALSE;
2986 return TRUE;
2989 gboolean
2990 mono_method_is_generic_sharable (MonoMethod *method, gboolean allow_type_vars)
2992 return mono_method_is_generic_sharable_full (method, allow_type_vars, partial_sharing_supported (), TRUE);
2996 * mono_method_needs_static_rgctx_invoke:
2998 * Return whenever METHOD needs an rgctx argument.
2999 * An rgctx argument is needed when the method is generic sharable, but it doesn't
3000 * have a this argument which can be used to load the rgctx.
3002 gboolean
3003 mono_method_needs_static_rgctx_invoke (MonoMethod *method, gboolean allow_type_vars)
3005 if (!mono_class_generic_sharing_enabled (method->klass))
3006 return FALSE;
3008 if (!mono_method_is_generic_sharable (method, allow_type_vars))
3009 return FALSE;
3011 if (method->is_inflated && mono_method_get_context (method)->method_inst)
3012 return TRUE;
3014 return ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
3015 method->klass->valuetype ||
3016 mini_method_is_default_method (method)) &&
3017 (mono_class_is_ginst (method->klass) || mono_class_is_gtd (method->klass));
3020 static MonoGenericInst*
3021 get_object_generic_inst (int type_argc)
3023 MonoType **type_argv;
3024 int i;
3026 type_argv = (MonoType **)alloca (sizeof (MonoType*) * type_argc);
3028 for (i = 0; i < type_argc; ++i)
3029 type_argv [i] = &mono_defaults.object_class->byval_arg;
3031 return mono_metadata_get_generic_inst (type_argc, type_argv);
3035 * mono_method_construct_object_context:
3036 * @method: a method
3038 * Returns a generic context for method with all type variables for
3039 * class and method instantiated with Object.
3041 MonoGenericContext
3042 mono_method_construct_object_context (MonoMethod *method)
3044 MonoGenericContext object_context;
3046 g_assert (!mono_class_is_ginst (method->klass));
3047 if (mono_class_is_gtd (method->klass)) {
3048 int type_argc = mono_class_get_generic_container (method->klass)->type_argc;
3050 object_context.class_inst = get_object_generic_inst (type_argc);
3051 } else {
3052 object_context.class_inst = NULL;
3055 if (mono_method_get_context_general (method, TRUE)->method_inst) {
3056 int type_argc = mono_method_get_context_general (method, TRUE)->method_inst->type_argc;
3058 object_context.method_inst = get_object_generic_inst (type_argc);
3059 } else {
3060 object_context.method_inst = NULL;
3063 g_assert (object_context.class_inst || object_context.method_inst);
3065 return object_context;
3068 static gboolean gshared_supported;
3070 void
3071 mono_set_generic_sharing_supported (gboolean supported)
3073 gshared_supported = supported;
3077 void
3078 mono_set_partial_sharing_supported (gboolean supported)
3080 partial_supported = supported;
3084 * mono_class_generic_sharing_enabled:
3085 * @class: a class
3087 * Returns whether generic sharing is enabled for class.
3089 * This is a stop-gap measure to slowly introduce generic sharing
3090 * until we have all the issues sorted out, at which time this
3091 * function will disappear and generic sharing will always be enabled.
3093 gboolean
3094 mono_class_generic_sharing_enabled (MonoClass *klass)
3096 if (gshared_supported)
3097 return TRUE;
3098 else
3099 return FALSE;
3102 MonoGenericContext*
3103 mini_method_get_context (MonoMethod *method)
3105 return mono_method_get_context_general (method, TRUE);
3109 * mono_method_check_context_used:
3110 * @method: a method
3112 * Checks whether the method's generic context uses a type variable.
3113 * Returns an int with the bits MONO_GENERIC_CONTEXT_USED_CLASS and
3114 * MONO_GENERIC_CONTEXT_USED_METHOD set to reflect whether the
3115 * context's class or method instantiation uses type variables.
3118 mono_method_check_context_used (MonoMethod *method)
3120 MonoGenericContext *method_context = mini_method_get_context (method);
3121 int context_used = 0;
3123 if (!method_context) {
3124 /* It might be a method of an array of an open generic type */
3125 if (method->klass->rank)
3126 context_used = mono_class_check_context_used (method->klass);
3127 } else {
3128 context_used = mono_generic_context_check_used (method_context);
3129 context_used |= mono_class_check_context_used (method->klass);
3132 return context_used;
3135 static gboolean
3136 generic_inst_equal (MonoGenericInst *inst1, MonoGenericInst *inst2)
3138 int i;
3140 if (!inst1) {
3141 g_assert (!inst2);
3142 return TRUE;
3145 g_assert (inst2);
3147 if (inst1->type_argc != inst2->type_argc)
3148 return FALSE;
3150 for (i = 0; i < inst1->type_argc; ++i)
3151 if (!mono_metadata_type_equal (inst1->type_argv [i], inst2->type_argv [i]))
3152 return FALSE;
3154 return TRUE;
3158 * mono_generic_context_equal_deep:
3159 * @context1: a generic context
3160 * @context2: a generic context
3162 * Returns whether context1's type arguments are equal to context2's
3163 * type arguments.
3165 gboolean
3166 mono_generic_context_equal_deep (MonoGenericContext *context1, MonoGenericContext *context2)
3168 return generic_inst_equal (context1->class_inst, context2->class_inst) &&
3169 generic_inst_equal (context1->method_inst, context2->method_inst);
3173 * mini_class_get_container_class:
3174 * @class: a generic class
3176 * Returns the class's container class, which is the class itself if
3177 * it doesn't have generic_class set.
3179 MonoClass*
3180 mini_class_get_container_class (MonoClass *klass)
3182 if (mono_class_is_ginst (klass))
3183 return mono_class_get_generic_class (klass)->container_class;
3185 g_assert (mono_class_is_gtd (klass));
3186 return klass;
3190 * mini_class_get_context:
3191 * @class: a generic class
3193 * Returns the class's generic context.
3195 MonoGenericContext*
3196 mini_class_get_context (MonoClass *klass)
3198 if (mono_class_is_ginst (klass))
3199 return &mono_class_get_generic_class (klass)->context;
3201 g_assert (mono_class_is_gtd (klass));
3202 return &mono_class_get_generic_container (klass)->context;
3206 * mini_get_basic_type_from_generic:
3207 * @type: a type
3209 * Returns a closed type corresponding to the possibly open type
3210 * passed to it.
3212 static MonoType*
3213 mini_get_basic_type_from_generic (MonoType *type)
3215 if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) && mini_is_gsharedvt_type (type))
3216 return type;
3217 else if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR)) {
3218 MonoType *constraint = type->data.generic_param->gshared_constraint;
3219 /* The gparam constraint encodes the type this gparam can represent */
3220 if (!constraint) {
3221 return &mono_defaults.object_class->byval_arg;
3222 } else {
3223 MonoClass *klass;
3225 g_assert (constraint != &mono_defaults.int_class->parent->byval_arg);
3226 klass = mono_class_from_mono_type (constraint);
3227 return &klass->byval_arg;
3229 } else {
3230 return mini_native_type_replace_type (mono_type_get_basic_type_from_generic (type));
3235 * mini_type_get_underlying_type:
3237 * Return the underlying type of TYPE, taking into account enums, byref, bool, char, ref types and generic
3238 * sharing.
3240 MonoType*
3241 mini_type_get_underlying_type (MonoType *type)
3243 type = mini_native_type_replace_type (type);
3245 if (type->byref)
3246 return &mono_defaults.int_class->byval_arg;
3247 if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) && mini_is_gsharedvt_type (type))
3248 return type;
3249 type = mini_get_basic_type_from_generic (mono_type_get_underlying_type (type));
3250 switch (type->type) {
3251 case MONO_TYPE_BOOLEAN:
3252 return &mono_defaults.byte_class->byval_arg;
3253 case MONO_TYPE_CHAR:
3254 return &mono_defaults.uint16_class->byval_arg;
3255 case MONO_TYPE_STRING:
3256 case MONO_TYPE_CLASS:
3257 case MONO_TYPE_ARRAY:
3258 case MONO_TYPE_SZARRAY:
3259 return &mono_defaults.object_class->byval_arg;
3260 default:
3261 return type;
3266 * mini_type_stack_size:
3267 * @t: a type
3268 * @align: Pointer to an int for returning the alignment
3270 * Returns the type's stack size and the alignment in *align.
3273 mini_type_stack_size (MonoType *t, int *align)
3275 return mono_type_stack_size_internal (t, align, TRUE);
3279 * mini_type_stack_size_full:
3281 * Same as mini_type_stack_size, but handle pinvoke data types as well.
3284 mini_type_stack_size_full (MonoType *t, guint32 *align, gboolean pinvoke)
3286 int size;
3288 //g_assert (!mini_is_gsharedvt_type (t));
3290 if (pinvoke) {
3291 size = mono_type_native_stack_size (t, align);
3292 } else {
3293 int ialign;
3295 if (align) {
3296 size = mini_type_stack_size (t, &ialign);
3297 *align = ialign;
3298 } else {
3299 size = mini_type_stack_size (t, NULL);
3303 return size;
3307 * mono_generic_sharing_init:
3309 * Initialize the module.
3311 void
3312 mono_generic_sharing_init (void)
3314 mono_counters_register ("RGCTX template num allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_template_num_allocated);
3315 mono_counters_register ("RGCTX template bytes allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_template_bytes_allocated);
3316 mono_counters_register ("RGCTX oti num allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_oti_num_allocated);
3317 mono_counters_register ("RGCTX oti bytes allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_oti_bytes_allocated);
3318 mono_counters_register ("RGCTX oti num markers", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_oti_num_markers);
3319 mono_counters_register ("RGCTX oti num data", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_oti_num_data);
3320 mono_counters_register ("RGCTX max slot number", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_max_slot_number);
3321 mono_counters_register ("RGCTX num allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_num_allocated);
3322 mono_counters_register ("RGCTX num arrays allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_num_arrays_allocated);
3323 mono_counters_register ("RGCTX bytes allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_bytes_allocated);
3324 mono_counters_register ("MRGCTX num arrays allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_num_arrays_allocated);
3325 mono_counters_register ("MRGCTX bytes allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_bytes_allocated);
3326 mono_counters_register ("GSHAREDVT num trampolines", MONO_COUNTER_JIT | MONO_COUNTER_INT, &gsharedvt_num_trampolines);
3328 mono_install_image_unload_hook (mono_class_unregister_image_generic_subclasses, NULL);
3330 mono_os_mutex_init_recursive (&gshared_mutex);
3333 void
3334 mono_generic_sharing_cleanup (void)
3336 mono_remove_image_unload_hook (mono_class_unregister_image_generic_subclasses, NULL);
3338 if (generic_subclass_hash)
3339 g_hash_table_destroy (generic_subclass_hash);
3343 * mini_type_var_is_vt:
3345 * Return whenever T is a type variable instantiated with a vtype.
3347 gboolean
3348 mini_type_var_is_vt (MonoType *type)
3350 if (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) {
3351 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);
3352 } else {
3353 g_assert_not_reached ();
3354 return FALSE;
3358 gboolean
3359 mini_type_is_reference (MonoType *type)
3361 type = mini_type_get_underlying_type (type);
3362 return mono_type_is_reference (type);
3365 gboolean
3366 mini_method_is_default_method (MonoMethod *m)
3368 return MONO_CLASS_IS_INTERFACE (m->klass) && !(m->flags & METHOD_ATTRIBUTE_ABSTRACT);
3371 gboolean
3372 mini_method_needs_mrgctx (MonoMethod *m)
3374 if (mono_class_is_ginst (m->klass) && mini_method_is_default_method (m))
3375 return TRUE;
3376 return (mini_method_get_context (m) && mini_method_get_context (m)->method_inst);
3380 * mini_method_get_rgctx:
3382 * Return the RGCTX which needs to be passed to M when it is called.
3384 gpointer
3385 mini_method_get_rgctx (MonoMethod *m)
3387 ERROR_DECL (error);
3388 MonoVTable *vt = mono_class_vtable_checked (mono_domain_get (), m->klass, error);
3389 mono_error_assert_ok (error);
3390 if (mini_method_needs_mrgctx (m))
3391 return mini_method_get_mrgctx (vt, m);
3392 else
3393 return vt;
3397 * mini_type_is_vtype:
3399 * Return whenever T is a vtype, or a type param instantiated with a vtype.
3400 * Should be used in place of MONO_TYPE_ISSTRUCT () which can't handle gsharedvt.
3402 gboolean
3403 mini_type_is_vtype (MonoType *t)
3405 t = mini_type_get_underlying_type (t);
3407 return MONO_TYPE_ISSTRUCT (t) || mini_is_gsharedvt_variable_type (t);
3410 gboolean
3411 mini_class_is_generic_sharable (MonoClass *klass)
3413 if (mono_class_is_ginst (klass) && is_async_state_machine_class (klass))
3414 return FALSE;
3416 return (mono_class_is_ginst (klass) && mono_generic_context_is_sharable (&mono_class_get_generic_class (klass)->context, FALSE));
3419 gboolean
3420 mini_is_gsharedvt_variable_klass (MonoClass *klass)
3422 return mini_is_gsharedvt_variable_type (&klass->byval_arg);
3425 gboolean
3426 mini_is_gsharedvt_gparam (MonoType *t)
3428 /* Matches get_gsharedvt_type () */
3429 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;
3432 static char*
3433 get_shared_gparam_name (MonoTypeEnum constraint, const char *name)
3435 if (constraint == MONO_TYPE_VALUETYPE) {
3436 return g_strdup_printf ("%s_GSHAREDVT", name);
3437 } else if (constraint == MONO_TYPE_OBJECT) {
3438 return g_strdup_printf ("%s_REF", name);
3439 } else if (constraint == MONO_TYPE_GENERICINST) {
3440 return g_strdup_printf ("%s_INST", name);
3441 } else {
3442 MonoType t;
3443 char *tname, *tname2, *res;
3445 memset (&t, 0, sizeof (t));
3446 t.type = constraint;
3447 tname = mono_type_full_name (&t);
3448 tname2 = g_utf8_strup (tname, strlen (tname));
3449 res = g_strdup_printf ("%s_%s", name, tname2);
3450 g_free (tname);
3451 g_free (tname2);
3452 return res;
3456 static guint
3457 shared_gparam_hash (gconstpointer data)
3459 MonoGSharedGenericParam *p = (MonoGSharedGenericParam*)data;
3460 guint hash;
3462 hash = mono_metadata_generic_param_hash (p->parent);
3463 hash = ((hash << 5) - hash) ^ mono_metadata_type_hash (p->param.param.gshared_constraint);
3465 return hash;
3468 static gboolean
3469 shared_gparam_equal (gconstpointer ka, gconstpointer kb)
3471 MonoGSharedGenericParam *p1 = (MonoGSharedGenericParam*)ka;
3472 MonoGSharedGenericParam *p2 = (MonoGSharedGenericParam*)kb;
3474 if (p1 == p2)
3475 return TRUE;
3476 if (p1->parent != p2->parent)
3477 return FALSE;
3478 if (!mono_metadata_type_equal (p1->param.param.gshared_constraint, p2->param.param.gshared_constraint))
3479 return FALSE;
3480 return TRUE;
3484 * mini_get_shared_gparam:
3486 * Create an anonymous gparam from T with a constraint which encodes which types can match it.
3488 MonoType*
3489 mini_get_shared_gparam (MonoType *t, MonoType *constraint)
3491 MonoGenericParam *par = t->data.generic_param;
3492 MonoGSharedGenericParam *copy, key;
3493 MonoType *res;
3494 MonoImage *image = NULL;
3495 char *name;
3497 memset (&key, 0, sizeof (key));
3498 key.parent = par;
3499 key.param.param.gshared_constraint = constraint;
3501 g_assert (mono_generic_param_info (par));
3502 image = get_image_for_generic_param(par);
3505 * Need a cache to ensure the newly created gparam
3506 * is unique wrt T/CONSTRAINT.
3508 mono_image_lock (image);
3509 if (!image->gshared_types) {
3510 image->gshared_types_len = MONO_TYPE_INTERNAL;
3511 image->gshared_types = g_new0 (GHashTable*, image->gshared_types_len);
3513 if (!image->gshared_types [constraint->type])
3514 image->gshared_types [constraint->type] = g_hash_table_new (shared_gparam_hash, shared_gparam_equal);
3515 res = (MonoType *)g_hash_table_lookup (image->gshared_types [constraint->type], &key);
3516 mono_image_unlock (image);
3517 if (res)
3518 return res;
3519 copy = (MonoGSharedGenericParam *)mono_image_alloc0 (image, sizeof (MonoGSharedGenericParam));
3520 memcpy (&copy->param, par, sizeof (MonoGenericParamFull));
3521 copy->param.info.pklass = NULL;
3522 constraint = mono_metadata_type_dup (image, constraint);
3523 name = get_shared_gparam_name (constraint->type, ((MonoGenericParamFull*)copy)->info.name);
3524 copy->param.info.name = mono_image_strdup (image, name);
3525 g_free (name);
3527 copy->param.param.owner = par->owner;
3529 copy->param.param.gshared_constraint = constraint;
3530 copy->parent = par;
3531 res = mono_metadata_type_dup (NULL, t);
3532 res->data.generic_param = (MonoGenericParam*)copy;
3534 if (image) {
3535 mono_image_lock (image);
3536 /* Duplicates are ok */
3537 g_hash_table_insert (image->gshared_types [constraint->type], copy, res);
3538 mono_image_unlock (image);
3541 return res;
3544 static MonoGenericInst*
3545 get_shared_inst (MonoGenericInst *inst, MonoGenericInst *shared_inst, MonoGenericContainer *container, gboolean all_vt, gboolean gsharedvt, gboolean partial);
3547 static MonoType*
3548 get_shared_type (MonoType *t, MonoType *type)
3550 MonoTypeEnum ttype;
3552 if (!type->byref && type->type == MONO_TYPE_GENERICINST && MONO_TYPE_ISSTRUCT (type)) {
3553 ERROR_DECL (error);
3554 MonoGenericClass *gclass = type->data.generic_class;
3555 MonoGenericContext context;
3556 MonoClass *k;
3558 memset (&context, 0, sizeof (context));
3559 if (gclass->context.class_inst)
3560 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);
3561 if (gclass->context.method_inst)
3562 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);
3564 k = mono_class_inflate_generic_class_checked (gclass->container_class, &context, error);
3565 mono_error_assert_ok (error); /* FIXME don't swallow the error */
3567 return mini_get_shared_gparam (t, &k->byval_arg);
3568 } else if (MONO_TYPE_ISSTRUCT (type)) {
3569 return type;
3572 /* Create a type variable with a constraint which encodes which types can match it */
3573 ttype = type->type;
3574 if (type->type == MONO_TYPE_VALUETYPE) {
3575 ttype = mono_class_enum_basetype (type->data.klass)->type;
3576 } else if (MONO_TYPE_IS_REFERENCE (type)) {
3577 ttype = MONO_TYPE_OBJECT;
3578 } else if (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) {
3579 if (type->data.generic_param->gshared_constraint)
3580 return mini_get_shared_gparam (t, type->data.generic_param->gshared_constraint);
3581 ttype = MONO_TYPE_OBJECT;
3585 MonoType t2;
3586 MonoClass *klass;
3588 memset (&t2, 0, sizeof (t2));
3589 t2.type = ttype;
3590 klass = mono_class_from_mono_type (&t2);
3592 return mini_get_shared_gparam (t, &klass->byval_arg);
3596 static MonoType*
3597 get_gsharedvt_type (MonoType *t)
3599 /* Use TypeHandle as the constraint type since its a valuetype */
3600 return mini_get_shared_gparam (t, &mono_defaults.typehandle_class->byval_arg);
3603 static MonoGenericInst*
3604 get_shared_inst (MonoGenericInst *inst, MonoGenericInst *shared_inst, MonoGenericContainer *container, gboolean all_vt, gboolean gsharedvt, gboolean partial)
3606 MonoGenericInst *res;
3607 MonoType **type_argv;
3608 int i;
3610 type_argv = g_new0 (MonoType*, inst->type_argc);
3611 for (i = 0; i < inst->type_argc; ++i) {
3612 if (all_vt || gsharedvt) {
3613 type_argv [i] = get_gsharedvt_type (shared_inst->type_argv [i]);
3614 } else {
3615 /* These types match the ones in mini_generic_inst_is_sharable () */
3616 type_argv [i] = get_shared_type (shared_inst->type_argv [i], inst->type_argv [i]);
3620 res = mono_metadata_get_generic_inst (inst->type_argc, type_argv);
3621 g_free (type_argv);
3622 return res;
3626 * mini_get_shared_method_full:
3628 * Return the method which is actually compiled/registered when doing generic sharing.
3629 * If ALL_VT is true, return the shared method belonging to an all-vtype instantiation.
3630 * If IS_GSHAREDVT is true, treat METHOD as a gsharedvt method even if it fails some constraints.
3631 * METHOD can be a non-inflated generic method.
3633 MonoMethod*
3634 mini_get_shared_method_full (MonoMethod *method, gboolean all_vt, gboolean is_gsharedvt)
3636 ERROR_DECL (error);
3637 MonoGenericContext shared_context;
3638 MonoMethod *declaring_method, *res;
3639 gboolean partial = FALSE;
3640 gboolean gsharedvt = FALSE;
3641 MonoGenericContainer *class_container, *method_container = NULL;
3642 MonoGenericContext *context = mono_method_get_context (method);
3643 MonoGenericInst *inst;
3646 * Instead of creating a shared version of the wrapper, create a shared version of the original
3647 * method and construct a wrapper for it. Otherwise, we could end up with two copies of the
3648 * same wrapper, breaking AOT which assumes wrappers are unique.
3649 * FIXME: Add other cases.
3651 if (method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED) {
3652 MonoMethod *wrapper = mono_marshal_method_from_wrapper (method);
3654 return mono_marshal_get_synchronized_wrapper (mini_get_shared_method_full (wrapper, all_vt, is_gsharedvt));
3656 if (method->wrapper_type == MONO_WRAPPER_DELEGATE_INVOKE) {
3657 WrapperInfo *info = mono_marshal_get_wrapper_info (method);
3659 if (info->subtype == WRAPPER_SUBTYPE_NONE) {
3660 MonoMethod *m = mono_marshal_get_delegate_invoke (mini_get_shared_method_full (info->d.delegate_invoke.method, all_vt, is_gsharedvt), NULL);
3661 return m;
3665 if (method->is_generic || (mono_class_is_gtd (method->klass) && !method->is_inflated)) {
3666 declaring_method = method;
3667 } else {
3668 declaring_method = mono_method_get_declaring_generic_method (method);
3671 /* shared_context is the context containing type variables. */
3672 if (declaring_method->is_generic)
3673 shared_context = mono_method_get_generic_container (declaring_method)->context;
3674 else
3675 shared_context = mono_class_get_generic_container (declaring_method->klass)->context;
3677 if (!is_gsharedvt)
3678 partial = mono_method_is_generic_sharable_full (method, FALSE, TRUE, FALSE);
3680 gsharedvt = is_gsharedvt || (!partial && mini_is_gsharedvt_sharable_method (method));
3682 class_container = mono_class_try_get_generic_container (declaring_method->klass); //FIXME is this a case for a try_get?
3683 method_container = mono_method_get_generic_container (declaring_method);
3686 * Create the shared context by replacing the ref type arguments with
3687 * type parameters, and keeping the rest.
3689 if (context)
3690 inst = context->class_inst;
3691 else
3692 inst = shared_context.class_inst;
3693 if (inst)
3694 shared_context.class_inst = get_shared_inst (inst, shared_context.class_inst, class_container, all_vt, gsharedvt, partial);
3696 if (context)
3697 inst = context->method_inst;
3698 else
3699 inst = shared_context.method_inst;
3700 if (inst)
3701 shared_context.method_inst = get_shared_inst (inst, shared_context.method_inst, method_container, all_vt, gsharedvt, partial);
3703 res = mono_class_inflate_generic_method_checked (declaring_method, &shared_context, error);
3704 g_assert (mono_error_ok (error)); /* FIXME don't swallow the error */
3706 //printf ("%s\n", mono_method_full_name (res, 1));
3708 return res;
3711 MonoMethod*
3712 mini_get_shared_method (MonoMethod *method)
3714 return mini_get_shared_method_full (method, FALSE, FALSE);
3718 mini_get_rgctx_entry_slot (MonoJumpInfoRgctxEntry *entry)
3720 guint32 slot = -1;
3722 switch (entry->data->type) {
3723 case MONO_PATCH_INFO_CLASS:
3724 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));
3725 break;
3726 case MONO_PATCH_INFO_METHOD:
3727 case MONO_PATCH_INFO_METHODCONST:
3728 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));
3729 break;
3730 case MONO_PATCH_INFO_FIELD:
3731 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));
3732 break;
3733 case MONO_PATCH_INFO_SIGNATURE:
3734 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));
3735 break;
3736 case MONO_PATCH_INFO_GSHAREDVT_CALL: {
3737 MonoJumpInfoGSharedVtCall *call_info = (MonoJumpInfoGSharedVtCall *)g_malloc0 (sizeof (MonoJumpInfoGSharedVtCall)); //mono_domain_alloc0 (domain, sizeof (MonoJumpInfoGSharedVtCall));
3739 memcpy (call_info, entry->data->data.gsharedvt, sizeof (MonoJumpInfoGSharedVtCall));
3740 slot = mono_method_lookup_or_register_info (entry->method, entry->in_mrgctx, call_info, entry->info_type, mono_method_get_context (entry->method));
3741 break;
3743 case MONO_PATCH_INFO_GSHAREDVT_METHOD: {
3744 MonoGSharedVtMethodInfo *info;
3745 MonoGSharedVtMethodInfo *oinfo = entry->data->data.gsharedvt_method;
3746 int i;
3748 /* Make a copy into the domain mempool */
3749 info = (MonoGSharedVtMethodInfo *)g_malloc0 (sizeof (MonoGSharedVtMethodInfo)); //mono_domain_alloc0 (domain, sizeof (MonoGSharedVtMethodInfo));
3750 info->method = oinfo->method;
3751 info->num_entries = oinfo->num_entries;
3752 info->entries = (MonoRuntimeGenericContextInfoTemplate *)g_malloc0 (sizeof (MonoRuntimeGenericContextInfoTemplate) * info->num_entries);
3753 for (i = 0; i < oinfo->num_entries; ++i) {
3754 MonoRuntimeGenericContextInfoTemplate *otemplate = &oinfo->entries [i];
3755 MonoRuntimeGenericContextInfoTemplate *template_ = &info->entries [i];
3757 memcpy (template_, otemplate, sizeof (MonoRuntimeGenericContextInfoTemplate));
3759 slot = mono_method_lookup_or_register_info (entry->method, entry->in_mrgctx, info, entry->info_type, mono_method_get_context (entry->method));
3760 break;
3762 case MONO_PATCH_INFO_VIRT_METHOD: {
3763 MonoJumpInfoVirtMethod *info;
3764 MonoJumpInfoVirtMethod *oinfo = entry->data->data.virt_method;
3766 info = (MonoJumpInfoVirtMethod *)g_malloc0 (sizeof (MonoJumpInfoVirtMethod));
3767 memcpy (info, oinfo, sizeof (MonoJumpInfoVirtMethod));
3768 slot = mono_method_lookup_or_register_info (entry->method, entry->in_mrgctx, info, entry->info_type, mono_method_get_context (entry->method));
3769 break;
3771 default:
3772 g_assert_not_reached ();
3773 break;
3776 return slot;
3779 static gboolean gsharedvt_supported;
3781 void
3782 mono_set_generic_sharing_vt_supported (gboolean supported)
3784 /* ensure we do not disable gsharedvt once it's been enabled */
3785 if (!gsharedvt_supported && supported)
3786 gsharedvt_supported = TRUE;
3789 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
3792 * mini_is_gsharedvt_type:
3794 * Return whenever T references type arguments instantiated with gshared vtypes.
3796 gboolean
3797 mini_is_gsharedvt_type (MonoType *t)
3799 int i;
3801 if (t->byref)
3802 return FALSE;
3803 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)
3804 return TRUE;
3805 else if (t->type == MONO_TYPE_GENERICINST) {
3806 MonoGenericClass *gclass = t->data.generic_class;
3807 MonoGenericContext *context = &gclass->context;
3808 MonoGenericInst *inst;
3810 inst = context->class_inst;
3811 if (inst) {
3812 for (i = 0; i < inst->type_argc; ++i)
3813 if (mini_is_gsharedvt_type (inst->type_argv [i]))
3814 return TRUE;
3816 inst = context->method_inst;
3817 if (inst) {
3818 for (i = 0; i < inst->type_argc; ++i)
3819 if (mini_is_gsharedvt_type (inst->type_argv [i]))
3820 return TRUE;
3823 return FALSE;
3824 } else {
3825 return FALSE;
3829 gboolean
3830 mini_is_gsharedvt_klass (MonoClass *klass)
3832 return mini_is_gsharedvt_type (&klass->byval_arg);
3835 gboolean
3836 mini_is_gsharedvt_signature (MonoMethodSignature *sig)
3838 int i;
3840 if (sig->ret && mini_is_gsharedvt_type (sig->ret))
3841 return TRUE;
3842 for (i = 0; i < sig->param_count; ++i) {
3843 if (mini_is_gsharedvt_type (sig->params [i]))
3844 return TRUE;
3846 return FALSE;
3850 * mini_is_gsharedvt_variable_type:
3852 * Return whenever T refers to a GSHAREDVT type whose size differs depending on the values of type parameters.
3854 gboolean
3855 mini_is_gsharedvt_variable_type (MonoType *t)
3857 if (!mini_is_gsharedvt_type (t))
3858 return FALSE;
3859 if (t->type == MONO_TYPE_GENERICINST) {
3860 MonoGenericClass *gclass = t->data.generic_class;
3861 MonoGenericContext *context = &gclass->context;
3862 MonoGenericInst *inst;
3863 int i;
3865 if (t->data.generic_class->container_class->byval_arg.type != MONO_TYPE_VALUETYPE || t->data.generic_class->container_class->enumtype)
3866 return FALSE;
3868 inst = context->class_inst;
3869 if (inst) {
3870 for (i = 0; i < inst->type_argc; ++i)
3871 if (mini_is_gsharedvt_variable_type (inst->type_argv [i]))
3872 return TRUE;
3874 inst = context->method_inst;
3875 if (inst) {
3876 for (i = 0; i < inst->type_argc; ++i)
3877 if (mini_is_gsharedvt_variable_type (inst->type_argv [i]))
3878 return TRUE;
3881 return FALSE;
3883 return TRUE;
3886 static gboolean
3887 is_variable_size (MonoType *t)
3889 int i;
3891 if (t->byref)
3892 return FALSE;
3894 if (t->type == MONO_TYPE_VAR || t->type == MONO_TYPE_MVAR) {
3895 MonoGenericParam *param = t->data.generic_param;
3897 if (param->gshared_constraint && param->gshared_constraint->type != MONO_TYPE_VALUETYPE && param->gshared_constraint->type != MONO_TYPE_GENERICINST)
3898 return FALSE;
3899 if (param->gshared_constraint && param->gshared_constraint->type == MONO_TYPE_GENERICINST)
3900 return is_variable_size (param->gshared_constraint);
3901 return TRUE;
3903 if (t->type == MONO_TYPE_GENERICINST && t->data.generic_class->container_class->byval_arg.type == MONO_TYPE_VALUETYPE) {
3904 MonoGenericClass *gclass = t->data.generic_class;
3905 MonoGenericContext *context = &gclass->context;
3906 MonoGenericInst *inst;
3908 inst = context->class_inst;
3909 if (inst) {
3910 for (i = 0; i < inst->type_argc; ++i)
3911 if (is_variable_size (inst->type_argv [i]))
3912 return TRUE;
3914 inst = context->method_inst;
3915 if (inst) {
3916 for (i = 0; i < inst->type_argc; ++i)
3917 if (is_variable_size (inst->type_argv [i]))
3918 return TRUE;
3922 return FALSE;
3925 gboolean
3926 mini_is_gsharedvt_sharable_inst (MonoGenericInst *inst)
3928 int i;
3929 gboolean has_vt = FALSE;
3931 for (i = 0; i < inst->type_argc; ++i) {
3932 MonoType *type = inst->type_argv [i];
3934 if ((MONO_TYPE_IS_REFERENCE (type) || type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) && !mini_is_gsharedvt_type (type)) {
3935 } else {
3936 has_vt = TRUE;
3940 return has_vt;
3943 gboolean
3944 mini_is_gsharedvt_sharable_method (MonoMethod *method)
3946 MonoMethodSignature *sig;
3949 * A method is gsharedvt if:
3950 * - it has type parameters instantiated with vtypes
3952 if (!gsharedvt_supported)
3953 return FALSE;
3954 if (method->is_inflated) {
3955 MonoMethodInflated *inflated = (MonoMethodInflated*)method;
3956 MonoGenericContext *context = &inflated->context;
3957 MonoGenericInst *inst;
3959 if (context->class_inst && context->method_inst) {
3960 /* At least one inst has to be gsharedvt sharable, and the other normal or gsharedvt sharable */
3961 gboolean vt1 = mini_is_gsharedvt_sharable_inst (context->class_inst);
3962 gboolean vt2 = mini_is_gsharedvt_sharable_inst (context->method_inst);
3964 if ((vt1 && vt2) ||
3965 (vt1 && mini_generic_inst_is_sharable (context->method_inst, TRUE, FALSE)) ||
3966 (vt2 && mini_generic_inst_is_sharable (context->class_inst, TRUE, FALSE)))
3968 else
3969 return FALSE;
3970 } else {
3971 inst = context->class_inst;
3972 if (inst && !mini_is_gsharedvt_sharable_inst (inst))
3973 return FALSE;
3974 inst = context->method_inst;
3975 if (inst && !mini_is_gsharedvt_sharable_inst (inst))
3976 return FALSE;
3978 } else {
3979 return FALSE;
3982 sig = mono_method_signature (mono_method_get_declaring_generic_method (method));
3983 if (!sig)
3984 return FALSE;
3987 if (mini_is_gsharedvt_variable_signature (sig))
3988 return FALSE;
3991 //DEBUG ("GSHAREDVT SHARABLE: %s\n", mono_method_full_name (method, TRUE));
3993 return TRUE;
3997 * mini_is_gsharedvt_variable_signature:
3999 * Return whenever the calling convention used to call SIG varies depending on the values of type parameters used by SIG,
4000 * i.e. FALSE for swap(T[] arr, int i, int j), TRUE for T get_t ().
4002 gboolean
4003 mini_is_gsharedvt_variable_signature (MonoMethodSignature *sig)
4005 int i;
4007 if (sig->ret && is_variable_size (sig->ret))
4008 return TRUE;
4009 for (i = 0; i < sig->param_count; ++i) {
4010 MonoType *t = sig->params [i];
4012 if (is_variable_size (t))
4013 return TRUE;
4015 return FALSE;
4017 #else
4019 gboolean
4020 mini_is_gsharedvt_type (MonoType *t)
4022 return FALSE;
4025 gboolean
4026 mini_is_gsharedvt_klass (MonoClass *klass)
4028 return FALSE;
4031 gboolean
4032 mini_is_gsharedvt_signature (MonoMethodSignature *sig)
4034 return FALSE;
4037 gboolean
4038 mini_is_gsharedvt_variable_type (MonoType *t)
4040 return FALSE;
4043 gboolean
4044 mini_is_gsharedvt_sharable_method (MonoMethod *method)
4046 return FALSE;
4049 gboolean
4050 mini_is_gsharedvt_variable_signature (MonoMethodSignature *sig)
4052 return FALSE;
4055 #endif /* !MONO_ARCH_GSHAREDVT_SUPPORTED */