[WIP] [WASM] Ongoing AOT work. (#11728)
[mono-project.git] / mono / mini / mini-generic-sharing.c
blob34435b5d95821736b8fb198fac8079d85d6e029d
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/method-builder-ilgen.h>
18 #include <mono/metadata/method-builder-ilgen-internals.h>
19 #include <mono/metadata/reflection-internals.h>
20 #include <mono/metadata/abi-details.h>
21 #include <mono/utils/mono-counters.h>
22 #include <mono/utils/atomic.h>
23 #include <mono/utils/unlocked.h>
25 #include "mini.h"
26 #include "aot-runtime.h"
27 #include "mini-runtime.h"
29 #define ALLOW_PARTIAL_SHARING TRUE
30 //#define ALLOW_PARTIAL_SHARING FALSE
32 #if 0
33 #define DEBUG(...) __VA_ARGS__
34 #else
35 #define DEBUG(...)
36 #endif
38 static void
39 mono_class_unregister_image_generic_subclasses (MonoImage *image, gpointer user_data);
41 /* Counters */
42 static gint32 rgctx_template_num_allocated;
43 static gint32 rgctx_template_bytes_allocated;
44 static gint32 rgctx_oti_num_allocated;
45 static gint32 rgctx_oti_bytes_allocated;
46 static gint32 rgctx_oti_num_markers;
47 static gint32 rgctx_oti_num_data;
48 static gint32 rgctx_max_slot_number;
49 static gint32 rgctx_num_allocated;
50 static gint32 rgctx_num_arrays_allocated;
51 static gint32 rgctx_bytes_allocated;
52 static gint32 mrgctx_num_arrays_allocated;
53 static gint32 mrgctx_bytes_allocated;
54 static gint32 gsharedvt_num_trampolines;
56 #define gshared_lock() mono_os_mutex_lock (&gshared_mutex)
57 #define gshared_unlock() mono_os_mutex_unlock (&gshared_mutex)
58 static mono_mutex_t gshared_mutex;
60 static gboolean partial_supported = FALSE;
62 static inline gboolean
63 partial_sharing_supported (void)
65 if (!ALLOW_PARTIAL_SHARING)
66 return FALSE;
67 /* Enable this when AOT compiling or running in full-aot mode */
68 if (mono_aot_only)
69 return TRUE;
70 if (partial_supported)
71 return TRUE;
72 return FALSE;
75 static int
76 type_check_context_used (MonoType *type, gboolean recursive)
78 switch (mono_type_get_type (type)) {
79 case MONO_TYPE_VAR:
80 return MONO_GENERIC_CONTEXT_USED_CLASS;
81 case MONO_TYPE_MVAR:
82 return MONO_GENERIC_CONTEXT_USED_METHOD;
83 case MONO_TYPE_SZARRAY:
84 return mono_class_check_context_used (mono_type_get_class (type));
85 case MONO_TYPE_ARRAY:
86 return mono_class_check_context_used (mono_type_get_array_type (type)->eklass);
87 case MONO_TYPE_CLASS:
88 if (recursive)
89 return mono_class_check_context_used (mono_type_get_class (type));
90 else
91 return 0;
92 case MONO_TYPE_GENERICINST:
93 if (recursive) {
94 MonoGenericClass *gclass = type->data.generic_class;
96 g_assert (mono_class_is_gtd (gclass->container_class));
97 return mono_generic_context_check_used (&gclass->context);
98 } else {
99 return 0;
101 default:
102 return 0;
106 static int
107 inst_check_context_used (MonoGenericInst *inst)
109 int context_used = 0;
110 int i;
112 if (!inst)
113 return 0;
115 for (i = 0; i < inst->type_argc; ++i)
116 context_used |= type_check_context_used (inst->type_argv [i], TRUE);
118 return context_used;
122 * mono_generic_context_check_used:
123 * @context: a generic context
125 * Checks whether the context uses a type variable. Returns an int
126 * with the bit MONO_GENERIC_CONTEXT_USED_CLASS set to reflect whether
127 * the context's class instantiation uses type variables.
130 mono_generic_context_check_used (MonoGenericContext *context)
132 int context_used = 0;
134 context_used |= inst_check_context_used (context->class_inst);
135 context_used |= inst_check_context_used (context->method_inst);
137 return context_used;
141 * mono_class_check_context_used:
142 * @class: a class
144 * Checks whether the class's generic context uses a type variable.
145 * Returns an int with the bit MONO_GENERIC_CONTEXT_USED_CLASS set to
146 * reflect whether the context's class instantiation uses type
147 * variables.
150 mono_class_check_context_used (MonoClass *klass)
152 int context_used = 0;
154 context_used |= type_check_context_used (m_class_get_this_arg (klass), FALSE);
155 context_used |= type_check_context_used (m_class_get_byval_arg (klass), FALSE);
157 if (mono_class_is_ginst (klass))
158 context_used |= mono_generic_context_check_used (&mono_class_get_generic_class (klass)->context);
159 else if (mono_class_is_gtd (klass))
160 context_used |= mono_generic_context_check_used (&mono_class_get_generic_container (klass)->context);
162 return context_used;
166 * LOCKING: loader lock
168 static MonoRuntimeGenericContextInfoTemplate*
169 get_info_templates (MonoRuntimeGenericContextTemplate *template_, int type_argc)
171 g_assert (type_argc >= 0);
172 if (type_argc == 0)
173 return template_->infos;
174 return (MonoRuntimeGenericContextInfoTemplate *)g_slist_nth_data (template_->method_templates, type_argc - 1);
178 * LOCKING: loader lock
180 static void
181 set_info_templates (MonoImage *image, MonoRuntimeGenericContextTemplate *template_, int type_argc,
182 MonoRuntimeGenericContextInfoTemplate *oti)
184 g_assert (type_argc >= 0);
185 if (type_argc == 0)
186 template_->infos = oti;
187 else {
188 int length = g_slist_length (template_->method_templates);
189 GSList *list;
191 /* FIXME: quadratic! */
192 while (length < type_argc) {
193 template_->method_templates = mono_g_slist_append_image (image, template_->method_templates, NULL);
194 length++;
197 list = g_slist_nth (template_->method_templates, type_argc - 1);
198 g_assert (list);
199 list->data = oti;
204 * LOCKING: loader lock
206 static int
207 template_get_max_argc (MonoRuntimeGenericContextTemplate *template_)
209 return g_slist_length (template_->method_templates);
213 * LOCKING: loader lock
215 static MonoRuntimeGenericContextInfoTemplate*
216 rgctx_template_get_other_slot (MonoRuntimeGenericContextTemplate *template_, int type_argc, int slot)
218 int i;
219 MonoRuntimeGenericContextInfoTemplate *oti;
221 g_assert (slot >= 0);
223 for (oti = get_info_templates (template_, type_argc), i = 0; i < slot; oti = oti->next, ++i) {
224 if (!oti)
225 return NULL;
228 return oti;
232 * LOCKING: loader lock
234 static int
235 rgctx_template_num_infos (MonoRuntimeGenericContextTemplate *template_, int type_argc)
237 MonoRuntimeGenericContextInfoTemplate *oti;
238 int i;
240 for (i = 0, oti = get_info_templates (template_, type_argc); oti; ++i, oti = oti->next)
243 return i;
246 /* Maps from uninstantiated generic classes to GList's of
247 * uninstantiated generic classes whose parent is the key class or an
248 * instance of the key class.
250 * LOCKING: loader lock
252 static GHashTable *generic_subclass_hash;
255 * LOCKING: templates lock
257 static void
258 class_set_rgctx_template (MonoClass *klass, MonoRuntimeGenericContextTemplate *rgctx_template)
260 if (!m_class_get_image (klass)->rgctx_template_hash)
261 m_class_get_image (klass)->rgctx_template_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
263 g_hash_table_insert (m_class_get_image (klass)->rgctx_template_hash, klass, rgctx_template);
267 * LOCKING: loader lock
269 static MonoRuntimeGenericContextTemplate*
270 class_lookup_rgctx_template (MonoClass *klass)
272 MonoRuntimeGenericContextTemplate *template_;
274 if (!m_class_get_image (klass)->rgctx_template_hash)
275 return NULL;
277 template_ = (MonoRuntimeGenericContextTemplate *)g_hash_table_lookup (m_class_get_image (klass)->rgctx_template_hash, klass);
279 return template_;
283 * LOCKING: loader lock
285 static void
286 register_generic_subclass (MonoClass *klass)
288 MonoClass *parent = m_class_get_parent (klass);
289 MonoClass *subclass;
290 MonoRuntimeGenericContextTemplate *rgctx_template = class_lookup_rgctx_template (klass);
292 g_assert (rgctx_template);
294 if (mono_class_is_ginst (parent))
295 parent = mono_class_get_generic_class (parent)->container_class;
297 if (!generic_subclass_hash)
298 generic_subclass_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
300 subclass = (MonoClass *)g_hash_table_lookup (generic_subclass_hash, parent);
301 rgctx_template->next_subclass = subclass;
302 g_hash_table_insert (generic_subclass_hash, parent, klass);
305 static void
306 move_subclasses_not_in_image_foreach_func (MonoClass *klass, MonoClass *subclass, MonoImage *image)
308 MonoClass *new_list;
310 if (m_class_get_image (klass) == image) {
311 /* The parent class itself is in the image, so all the
312 subclasses must be in the image, too. If not,
313 we're removing an image containing a class which
314 still has a subclass in another image. */
316 while (subclass) {
317 g_assert (m_class_get_image (subclass) == image);
318 subclass = class_lookup_rgctx_template (subclass)->next_subclass;
321 return;
324 new_list = NULL;
325 while (subclass) {
326 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
327 MonoClass *next = subclass_template->next_subclass;
329 if (m_class_get_image (subclass) != image) {
330 subclass_template->next_subclass = new_list;
331 new_list = subclass;
334 subclass = next;
337 if (new_list)
338 g_hash_table_insert (generic_subclass_hash, klass, new_list);
342 * mono_class_unregister_image_generic_subclasses:
343 * @image: an image
345 * Removes all classes of the image from the generic subclass hash.
346 * Must be called when an image is unloaded.
348 static void
349 mono_class_unregister_image_generic_subclasses (MonoImage *image, gpointer user_data)
351 GHashTable *old_hash;
353 //g_print ("unregistering image %s\n", image->name);
355 if (!generic_subclass_hash)
356 return;
358 mono_loader_lock ();
360 old_hash = generic_subclass_hash;
361 generic_subclass_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
363 g_hash_table_foreach (old_hash, (GHFunc)move_subclasses_not_in_image_foreach_func, image);
365 mono_loader_unlock ();
367 g_hash_table_destroy (old_hash);
370 static MonoRuntimeGenericContextTemplate*
371 alloc_template (MonoClass *klass)
373 gint32 size = sizeof (MonoRuntimeGenericContextTemplate);
375 mono_atomic_inc_i32 (&rgctx_template_num_allocated);
376 mono_atomic_fetch_add_i32 (&rgctx_template_bytes_allocated, size);
378 return (MonoRuntimeGenericContextTemplate *)mono_image_alloc0 (m_class_get_image (klass), size);
381 /* LOCKING: Takes the loader lock */
382 static MonoRuntimeGenericContextInfoTemplate*
383 alloc_oti (MonoImage *image)
385 gint32 size = sizeof (MonoRuntimeGenericContextInfoTemplate);
387 mono_atomic_inc_i32 (&rgctx_oti_num_allocated);
388 mono_atomic_fetch_add_i32 (&rgctx_oti_bytes_allocated, size);
390 return (MonoRuntimeGenericContextInfoTemplate *)mono_image_alloc0 (image, size);
393 #define MONO_RGCTX_SLOT_USED_MARKER ((gpointer)mono_get_object_type ())
396 * Return true if this info type has the notion of identify.
398 * Some info types expect that each insert results in a new slot been assigned.
400 static int
401 info_has_identity (MonoRgctxInfoType info_type)
403 return info_type != MONO_RGCTX_INFO_CAST_CACHE;
407 * LOCKING: loader lock
409 #if defined(HOST_ANDROID) && defined(TARGET_ARM)
410 /* work around for HW bug on Nexus9 when running on armv7 */
411 #ifdef __clang__
412 static __attribute__ ((optnone)) void
413 #else
414 /* gcc */
415 static __attribute__ ((optimize("O0"))) void
416 #endif
417 #else
418 static void
419 #endif
420 rgctx_template_set_slot (MonoImage *image, MonoRuntimeGenericContextTemplate *template_, int type_argc,
421 int slot, gpointer data, MonoRgctxInfoType info_type)
423 int i;
424 MonoRuntimeGenericContextInfoTemplate *list = get_info_templates (template_, type_argc);
425 MonoRuntimeGenericContextInfoTemplate **oti = &list;
427 g_assert (slot >= 0);
428 g_assert (data);
430 i = 0;
431 while (i <= slot) {
432 if (i > 0)
433 oti = &(*oti)->next;
434 if (!*oti)
435 *oti = alloc_oti (image);
436 ++i;
439 g_assert (!(*oti)->data);
440 (*oti)->data = data;
441 (*oti)->info_type = info_type;
443 set_info_templates (image, template_, type_argc, list);
445 /* interlocked by loader lock (by definition) */
446 if (data == MONO_RGCTX_SLOT_USED_MARKER)
447 UnlockedIncrement (&rgctx_oti_num_markers);
448 else
449 UnlockedIncrement (&rgctx_oti_num_data);
453 * mono_method_get_declaring_generic_method:
454 * @method: an inflated method
456 * Returns an inflated method's declaring method.
458 MonoMethod*
459 mono_method_get_declaring_generic_method (MonoMethod *method)
461 MonoMethodInflated *inflated;
463 g_assert (method->is_inflated);
465 inflated = (MonoMethodInflated*)method;
467 return inflated->declaring;
471 * mono_class_get_method_generic:
472 * @klass: a class
473 * @method: a method
474 * @error: set on error
476 * Given a class and a generic method, which has to be of an
477 * instantiation of the same class that klass is an instantiation of,
478 * returns the corresponding method in klass. Example:
480 * klass is Gen<string>
481 * method is Gen<object>.work<int>
483 * returns: Gen<string>.work<int>
485 * On error sets @error and returns NULL.
487 MonoMethod*
488 mono_class_get_method_generic (MonoClass *klass, MonoMethod *method, MonoError *error)
490 MonoMethod *declaring, *m;
491 int i;
493 if (method->is_inflated)
494 declaring = mono_method_get_declaring_generic_method (method);
495 else
496 declaring = method;
498 m = NULL;
499 if (mono_class_is_ginst (klass)) {
500 m = mono_class_get_inflated_method (klass, declaring, error);
501 return_val_if_nok (error, NULL);
504 if (!m) {
505 mono_class_setup_methods (klass);
506 if (mono_class_has_failure (klass))
507 return NULL;
508 int mcount = mono_class_get_method_count (klass);
509 MonoMethod **klass_methods = m_class_get_methods (klass);
510 for (i = 0; i < mcount; ++i) {
511 m = klass_methods [i];
512 if (m == declaring)
513 break;
514 if (m->is_inflated && mono_method_get_declaring_generic_method (m) == declaring)
515 break;
517 if (i >= mcount)
518 return NULL;
521 if (method != declaring) {
522 MonoGenericContext context;
524 context.class_inst = NULL;
525 context.method_inst = mono_method_get_context (method)->method_inst;
527 m = mono_class_inflate_generic_method_checked (m, &context, error);
528 return_val_if_nok (error, NULL);
531 return m;
534 static gpointer
535 inflate_info (MonoRuntimeGenericContextInfoTemplate *oti, MonoGenericContext *context, MonoClass *klass, gboolean temporary)
537 gpointer data = oti->data;
538 MonoRgctxInfoType info_type = oti->info_type;
539 ERROR_DECL (error);
541 g_assert (data);
543 if (data == MONO_RGCTX_SLOT_USED_MARKER)
544 return MONO_RGCTX_SLOT_USED_MARKER;
546 switch (info_type)
548 case MONO_RGCTX_INFO_STATIC_DATA:
549 case MONO_RGCTX_INFO_KLASS:
550 case MONO_RGCTX_INFO_ELEMENT_KLASS:
551 case MONO_RGCTX_INFO_VTABLE:
552 case MONO_RGCTX_INFO_TYPE:
553 case MONO_RGCTX_INFO_REFLECTION_TYPE:
554 case MONO_RGCTX_INFO_CAST_CACHE:
555 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
556 case MONO_RGCTX_INFO_VALUE_SIZE:
557 case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
558 case MONO_RGCTX_INFO_CLASS_IS_REF_OR_CONTAINS_REFS:
559 case MONO_RGCTX_INFO_MEMCPY:
560 case MONO_RGCTX_INFO_BZERO:
561 case MONO_RGCTX_INFO_LOCAL_OFFSET:
562 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
563 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: {
564 gpointer result = mono_class_inflate_generic_type_with_mempool (temporary ? NULL : m_class_get_image (klass),
565 (MonoType *)data, context, error);
566 mono_error_assert_msg_ok (error, "Could not inflate generic type"); /* FIXME proper error handling */
567 return result;
570 case MONO_RGCTX_INFO_METHOD:
571 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
572 case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER:
573 case MONO_RGCTX_INFO_METHOD_RGCTX:
574 case MONO_RGCTX_INFO_METHOD_CONTEXT:
575 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
576 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE: {
577 MonoMethod *method = (MonoMethod *)data;
578 MonoMethod *inflated_method;
579 MonoType *inflated_type = mono_class_inflate_generic_type_checked (m_class_get_byval_arg (method->klass), context, error);
580 mono_error_assert_ok (error); /* FIXME don't swallow the error */
582 MonoClass *inflated_class = mono_class_from_mono_type_internal (inflated_type);
584 mono_metadata_free_type (inflated_type);
586 mono_class_init (inflated_class);
588 g_assert (!method->wrapper_type);
590 if (m_class_get_byval_arg (inflated_class)->type == MONO_TYPE_ARRAY ||
591 m_class_get_byval_arg (inflated_class)->type == MONO_TYPE_SZARRAY) {
592 inflated_method = mono_method_search_in_array_class (inflated_class,
593 method->name, method->signature);
594 } else {
595 ERROR_DECL (error);
596 inflated_method = mono_class_inflate_generic_method_checked (method, context, error);
597 g_assert (mono_error_ok (error)); /* FIXME don't swallow the error */
599 mono_class_init (inflated_method->klass);
600 g_assert (inflated_method->klass == inflated_class);
601 return inflated_method;
603 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO: {
604 MonoGSharedVtMethodInfo *oinfo = (MonoGSharedVtMethodInfo *)data;
605 MonoGSharedVtMethodInfo *res;
606 MonoDomain *domain = mono_domain_get ();
607 int i;
609 res = (MonoGSharedVtMethodInfo *)mono_domain_alloc0 (domain, sizeof (MonoGSharedVtMethodInfo));
611 res->nlocals = info->nlocals;
612 res->locals_types = g_new0 (MonoType*, info->nlocals);
613 for (i = 0; i < info->nlocals; ++i)
614 res->locals_types [i] = mono_class_inflate_generic_type (info->locals_types [i], context);
616 res->num_entries = oinfo->num_entries;
617 res->entries = (MonoRuntimeGenericContextInfoTemplate *)mono_domain_alloc0 (domain, sizeof (MonoRuntimeGenericContextInfoTemplate) * oinfo->num_entries);
618 for (i = 0; i < oinfo->num_entries; ++i) {
619 MonoRuntimeGenericContextInfoTemplate *otemplate = &oinfo->entries [i];
620 MonoRuntimeGenericContextInfoTemplate *template_ = &res->entries [i];
622 memcpy (template_, otemplate, sizeof (MonoRuntimeGenericContextInfoTemplate));
623 template_->data = inflate_info (template_, context, klass, FALSE);
625 return res;
627 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE:
628 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT: {
629 MonoJumpInfoGSharedVtCall *info = (MonoJumpInfoGSharedVtCall *)data;
630 MonoMethod *method = info->method;
631 MonoMethod *inflated_method;
632 MonoType *inflated_type = mono_class_inflate_generic_type_checked (m_class_get_byval_arg (method->klass), context, error);
633 mono_error_assert_ok (error); /* FIXME don't swallow the error */
634 WrapperInfo *winfo = NULL;
636 MonoClass *inflated_class = mono_class_from_mono_type_internal (inflated_type);
637 MonoJumpInfoGSharedVtCall *res;
638 MonoDomain *domain = mono_domain_get ();
640 res = (MonoJumpInfoGSharedVtCall *)mono_domain_alloc0 (domain, sizeof (MonoJumpInfoGSharedVtCall));
641 /* Keep the original signature */
642 res->sig = info->sig;
644 mono_metadata_free_type (inflated_type);
646 mono_class_init (inflated_class);
648 if (method->wrapper_type) {
649 winfo = mono_marshal_get_wrapper_info (method);
651 g_assert (winfo);
652 g_assert (winfo->subtype == WRAPPER_SUBTYPE_SYNCHRONIZED_INNER);
653 method = winfo->d.synchronized_inner.method;
656 if (m_class_get_byval_arg (inflated_class)->type == MONO_TYPE_ARRAY ||
657 m_class_get_byval_arg (inflated_class)->type == MONO_TYPE_SZARRAY) {
658 inflated_method = mono_method_search_in_array_class (inflated_class,
659 method->name, method->signature);
660 } else {
661 ERROR_DECL (error);
662 inflated_method = mono_class_inflate_generic_method_checked (method, context, error);
663 g_assert (mono_error_ok (error)); /* FIXME don't swallow the error */
665 mono_class_init (inflated_method->klass);
666 g_assert (inflated_method->klass == inflated_class);
668 if (winfo) {
669 g_assert (winfo->subtype == WRAPPER_SUBTYPE_SYNCHRONIZED_INNER);
670 inflated_method = mono_marshal_get_synchronized_inner_wrapper (inflated_method);
673 res->method = inflated_method;
675 return res;
678 case MONO_RGCTX_INFO_CLASS_FIELD:
679 case MONO_RGCTX_INFO_FIELD_OFFSET: {
680 ERROR_DECL (error);
681 MonoClassField *field = (MonoClassField *)data;
682 MonoType *inflated_type = mono_class_inflate_generic_type_checked (m_class_get_byval_arg (field->parent), context, error);
683 mono_error_assert_ok (error); /* FIXME don't swallow the error */
685 MonoClass *inflated_class = mono_class_from_mono_type_internal (inflated_type);
686 int i = field - m_class_get_fields (field->parent);
687 gpointer dummy = NULL;
689 mono_metadata_free_type (inflated_type);
691 mono_class_get_fields_internal (inflated_class, &dummy);
692 g_assert (m_class_get_fields (inflated_class));
694 return &m_class_get_fields (inflated_class) [i];
696 case MONO_RGCTX_INFO_SIG_GSHAREDVT_IN_TRAMPOLINE_CALLI:
697 case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI: {
698 MonoMethodSignature *sig = (MonoMethodSignature *)data;
699 MonoMethodSignature *isig;
700 ERROR_DECL (error);
702 isig = mono_inflate_generic_signature (sig, context, error);
703 g_assert (mono_error_ok (error));
704 return isig;
706 case MONO_RGCTX_INFO_VIRT_METHOD_CODE:
707 case MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE: {
708 MonoJumpInfoVirtMethod *info = (MonoJumpInfoVirtMethod *)data;
709 MonoJumpInfoVirtMethod *res;
710 MonoType *t;
711 MonoDomain *domain = mono_domain_get ();
712 ERROR_DECL (error);
714 // FIXME: Temporary
715 res = (MonoJumpInfoVirtMethod *)mono_domain_alloc0 (domain, sizeof (MonoJumpInfoVirtMethod));
716 t = mono_class_inflate_generic_type_checked (m_class_get_byval_arg (info->klass), context, error);
717 mono_error_assert_ok (error); /* FIXME don't swallow the error */
719 res->klass = mono_class_from_mono_type_internal (t);
720 mono_metadata_free_type (t);
722 res->method = mono_class_inflate_generic_method_checked (info->method, context, error);
723 g_assert (mono_error_ok (error)); /* FIXME don't swallow the error */
725 return res;
727 case MONO_RGCTX_INFO_DELEGATE_TRAMP_INFO: {
728 ERROR_DECL (error);
729 MonoDelegateClassMethodPair *dele_info = (MonoDelegateClassMethodPair*)data;
730 MonoDomain *domain = mono_domain_get ();
732 MonoType *t = mono_class_inflate_generic_type_checked (m_class_get_byval_arg (dele_info->klass), context, error);
733 mono_error_assert_msg_ok (error, "Could not inflate generic type"); /* FIXME proper error handling */
735 MonoClass *klass = mono_class_from_mono_type_internal (t);
736 mono_metadata_free_type (t);
738 MonoMethod *method = mono_class_inflate_generic_method_checked (dele_info->method, context, error);
739 mono_error_assert_msg_ok (error, "Could not inflate generic method"); /* FIXME proper error handling */
741 // FIXME: Temporary
742 MonoDelegateClassMethodPair *res = (MonoDelegateClassMethodPair *)mono_domain_alloc0 (domain, sizeof (MonoDelegateClassMethodPair));
743 res->is_virtual = dele_info->is_virtual;
744 res->method = method;
745 res->klass = klass;
746 return res;
749 default:
750 g_assert_not_reached ();
752 /* Not reached, quiet compiler */
753 return NULL;
756 static void
757 free_inflated_info (MonoRgctxInfoType info_type, gpointer info)
759 if (!info)
760 return;
762 switch (info_type) {
763 case MONO_RGCTX_INFO_STATIC_DATA:
764 case MONO_RGCTX_INFO_KLASS:
765 case MONO_RGCTX_INFO_ELEMENT_KLASS:
766 case MONO_RGCTX_INFO_VTABLE:
767 case MONO_RGCTX_INFO_TYPE:
768 case MONO_RGCTX_INFO_REFLECTION_TYPE:
769 case MONO_RGCTX_INFO_CAST_CACHE:
770 mono_metadata_free_type ((MonoType *)info);
771 break;
772 default:
773 break;
777 static MonoRuntimeGenericContextInfoTemplate
778 class_get_rgctx_template_oti (MonoClass *klass, int type_argc, guint32 slot, gboolean temporary, gboolean shared, gboolean *do_free);
780 static MonoClass*
781 class_uninstantiated (MonoClass *klass)
783 if (mono_class_is_ginst (klass))
784 return mono_class_get_generic_class (klass)->container_class;
785 return klass;
789 * get_shared_class:
791 * Return the class used to store information when using generic sharing.
793 static MonoClass*
794 get_shared_class (MonoClass *klass)
796 return class_uninstantiated (klass);
800 * mono_class_get_runtime_generic_context_template:
801 * @class: a class
803 * Looks up or constructs, if necessary, the runtime generic context template for class.
804 * The template is the same for all instantiations of a class.
806 static MonoRuntimeGenericContextTemplate*
807 mono_class_get_runtime_generic_context_template (MonoClass *klass)
809 MonoRuntimeGenericContextTemplate *parent_template, *template_;
810 guint32 i;
812 klass = get_shared_class (klass);
814 mono_loader_lock ();
815 template_ = class_lookup_rgctx_template (klass);
816 mono_loader_unlock ();
818 if (template_)
819 return template_;
821 //g_assert (get_shared_class (class) == class);
823 template_ = alloc_template (klass);
825 mono_loader_lock ();
827 if (m_class_get_parent (klass)) {
828 guint32 num_entries;
829 int max_argc, type_argc;
831 parent_template = mono_class_get_runtime_generic_context_template (m_class_get_parent (klass));
832 max_argc = template_get_max_argc (parent_template);
834 for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
835 num_entries = rgctx_template_num_infos (parent_template, type_argc);
837 /* FIXME: quadratic! */
838 for (i = 0; i < num_entries; ++i) {
839 MonoRuntimeGenericContextInfoTemplate oti;
841 oti = class_get_rgctx_template_oti (m_class_get_parent (klass), type_argc, i, FALSE, FALSE, NULL);
842 if (oti.data && oti.data != MONO_RGCTX_SLOT_USED_MARKER) {
843 rgctx_template_set_slot (m_class_get_image (klass), template_, type_argc, i,
844 oti.data, oti.info_type);
850 if (class_lookup_rgctx_template (klass)) {
851 /* some other thread already set the template */
852 template_ = class_lookup_rgctx_template (klass);
853 } else {
854 class_set_rgctx_template (klass, template_);
856 if (m_class_get_parent (klass))
857 register_generic_subclass (klass);
860 mono_loader_unlock ();
862 return template_;
866 * class_get_rgctx_template_oti:
868 * Return the info template of CLASS numbered TYPE_ARGC/SLOT.
869 * temporary signifies whether the inflated info (oti.data) will be
870 * used temporarily, in which case it might be heap-allocated, or
871 * permanently, in which case it will be mempool-allocated. If
872 * temporary is set then *do_free will return whether the returned
873 * data must be freed.
875 * LOCKING: loader lock
877 static MonoRuntimeGenericContextInfoTemplate
878 class_get_rgctx_template_oti (MonoClass *klass, int type_argc, guint32 slot, gboolean temporary, gboolean shared, gboolean *do_free)
880 g_assert ((temporary && do_free) || (!temporary && !do_free));
882 DEBUG (printf ("get slot: %s %d\n", mono_type_full_name (m_class_get_byval_arg (class)), slot));
884 if (mono_class_is_ginst (klass) && !shared) {
885 MonoRuntimeGenericContextInfoTemplate oti;
886 gboolean tmp_do_free;
888 oti = class_get_rgctx_template_oti (mono_class_get_generic_class (klass)->container_class,
889 type_argc, slot, TRUE, FALSE, &tmp_do_free);
890 if (oti.data) {
891 gpointer info = oti.data;
892 oti.data = inflate_info (&oti, &mono_class_get_generic_class (klass)->context, klass, temporary);
893 if (tmp_do_free)
894 free_inflated_info (oti.info_type, info);
896 if (temporary)
897 *do_free = TRUE;
899 return oti;
900 } else {
901 MonoRuntimeGenericContextTemplate *template_;
902 MonoRuntimeGenericContextInfoTemplate *oti;
904 template_ = mono_class_get_runtime_generic_context_template (klass);
905 oti = rgctx_template_get_other_slot (template_, type_argc, slot);
906 g_assert (oti);
908 if (temporary)
909 *do_free = FALSE;
911 return *oti;
915 static MonoMethod*
916 get_method_nofail (MonoClass *klass, const char *method_name, int num_params, int flags)
918 MonoMethod *method;
919 ERROR_DECL (error);
920 method = mono_class_get_method_from_name_checked (klass, method_name, num_params, flags, error);
921 mono_error_assert_ok (error);
922 g_assertf (method, "Could not lookup method %s in %s", method_name, m_class_get_name (klass));
923 return method;
926 static gpointer
927 class_type_info (MonoDomain *domain, MonoClass *klass, MonoRgctxInfoType info_type, MonoError *error)
929 error_init (error);
931 switch (info_type) {
932 case MONO_RGCTX_INFO_STATIC_DATA: {
933 MonoVTable *vtable = mono_class_vtable_checked (domain, klass, error);
934 return_val_if_nok (error, NULL);
935 return mono_vtable_get_static_field_data (vtable);
937 case MONO_RGCTX_INFO_KLASS:
938 return klass;
939 case MONO_RGCTX_INFO_ELEMENT_KLASS:
940 return m_class_get_element_class (klass);
941 case MONO_RGCTX_INFO_VTABLE: {
942 MonoVTable *vtable = mono_class_vtable_checked (domain, klass, error);
943 return_val_if_nok (error, NULL);
944 return vtable;
946 case MONO_RGCTX_INFO_CAST_CACHE: {
947 /*First slot is the cache itself, the second the vtable.*/
948 gpointer **cache_data = (gpointer **)mono_domain_alloc0 (domain, sizeof (gpointer) * 2);
949 cache_data [1] = (gpointer *)klass;
950 return cache_data;
952 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
953 return GUINT_TO_POINTER (mono_class_array_element_size (klass));
954 case MONO_RGCTX_INFO_VALUE_SIZE:
955 if (MONO_TYPE_IS_REFERENCE (m_class_get_byval_arg (klass)))
956 return GUINT_TO_POINTER (sizeof (gpointer));
957 else
958 return GUINT_TO_POINTER (mono_class_value_size (klass, NULL));
959 case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
960 if (MONO_TYPE_IS_REFERENCE (m_class_get_byval_arg (klass)))
961 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_REF);
962 else if (mono_class_is_nullable (klass))
963 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_NULLABLE);
964 else
965 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_VTYPE);
966 case MONO_RGCTX_INFO_CLASS_IS_REF_OR_CONTAINS_REFS:
967 mono_class_init (klass);
968 /* Can't return 0 */
969 if (MONO_TYPE_IS_REFERENCE (m_class_get_byval_arg (klass)) || m_class_has_references (klass))
970 return GUINT_TO_POINTER (2);
971 else
972 return GUINT_TO_POINTER (1);
973 case MONO_RGCTX_INFO_MEMCPY:
974 case MONO_RGCTX_INFO_BZERO: {
975 static MonoMethod *memcpy_method [17];
976 static MonoMethod *bzero_method [17];
977 MonoJitDomainInfo *domain_info;
978 int size;
979 guint32 align;
981 domain_info = domain_jit_info (domain);
983 if (MONO_TYPE_IS_REFERENCE (m_class_get_byval_arg (klass))) {
984 size = sizeof (gpointer);
985 align = sizeof (gpointer);
986 } else {
987 size = mono_class_value_size (klass, &align);
990 if (size != 1 && size != 2 && size != 4 && size != 8)
991 size = 0;
992 if (align < size)
993 size = 0;
995 if (info_type == MONO_RGCTX_INFO_MEMCPY) {
996 if (!memcpy_method [size]) {
997 MonoMethod *m;
998 char name [32];
1000 if (size == 0)
1001 sprintf (name, "memcpy");
1002 else
1003 sprintf (name, "memcpy_aligned_%d", size);
1004 m = get_method_nofail (mono_defaults.string_class, name, 3, 0);
1005 g_assert (m);
1006 mono_memory_barrier ();
1007 memcpy_method [size] = m;
1009 if (!domain_info->memcpy_addr [size]) {
1010 gpointer addr = mono_compile_method_checked (memcpy_method [size], error);
1011 mono_memory_barrier ();
1012 domain_info->memcpy_addr [size] = (gpointer *)addr;
1013 mono_error_assert_ok (error);
1015 return domain_info->memcpy_addr [size];
1016 } else {
1017 if (!bzero_method [size]) {
1018 MonoMethod *m;
1019 char name [32];
1021 if (size == 0)
1022 sprintf (name, "bzero");
1023 else
1024 sprintf (name, "bzero_aligned_%d", size);
1025 m = get_method_nofail (mono_defaults.string_class, name, 2, 0);
1026 g_assert (m);
1027 mono_memory_barrier ();
1028 bzero_method [size] = m;
1030 if (!domain_info->bzero_addr [size]) {
1031 gpointer addr = mono_compile_method_checked (bzero_method [size], error);
1032 mono_memory_barrier ();
1033 domain_info->bzero_addr [size] = (gpointer *)addr;
1034 mono_error_assert_ok (error);
1036 return domain_info->bzero_addr [size];
1039 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
1040 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: {
1041 MonoMethod *method;
1042 gpointer addr, arg;
1043 MonoJitInfo *ji;
1044 MonoMethodSignature *sig, *gsig;
1045 MonoMethod *gmethod;
1047 if (!mono_class_is_nullable (klass))
1048 /* This can happen since all the entries in MonoGSharedVtMethodInfo are inflated, even those which are not used */
1049 return NULL;
1051 if (info_type == MONO_RGCTX_INFO_NULLABLE_CLASS_BOX)
1052 method = mono_class_get_method_from_name_checked (klass, "Box", 1, 0, error);
1053 else
1054 method = mono_class_get_method_from_name_checked (klass, "Unbox", 1, 0, error);
1056 return_val_if_nok (error, NULL);
1058 addr = mono_jit_compile_method (method, error);
1059 return_val_if_nok (error, NULL);
1061 // The caller uses the gsharedvt call signature
1063 if (mono_llvm_only) {
1064 /* FIXME: We have no access to the gsharedvt signature/gsctx used by the caller, so have to construct it ourselves */
1065 gmethod = mini_get_shared_method_full (method, SHARE_MODE_GSHAREDVT, error);
1066 if (!gmethod)
1067 return NULL;
1068 sig = mono_method_signature_internal (method);
1069 gsig = mono_method_signature_internal (gmethod);
1071 addr = mini_add_method_wrappers_llvmonly (method, addr, TRUE, FALSE, &arg);
1072 return mini_create_llvmonly_ftndesc (domain, addr, arg);
1075 ji = mini_jit_info_table_find (mono_domain_get (), (char *)mono_get_addr_from_ftnptr (addr), NULL);
1076 g_assert (ji);
1077 if (mini_jit_info_is_gsharedvt (ji))
1078 return mono_create_static_rgctx_trampoline (method, addr);
1079 else {
1080 /* Need to add an out wrapper */
1082 /* FIXME: We have no access to the gsharedvt signature/gsctx used by the caller, so have to construct it ourselves */
1083 gmethod = mini_get_shared_method_full (method, SHARE_MODE_GSHAREDVT, error);
1084 if (!gmethod)
1085 return NULL;
1086 sig = mono_method_signature_internal (method);
1087 gsig = mono_method_signature_internal (gmethod);
1089 addr = mini_get_gsharedvt_wrapper (FALSE, addr, sig, gsig, -1, FALSE);
1090 addr = mono_create_static_rgctx_trampoline (method, addr);
1091 return addr;
1094 default:
1095 g_assert_not_reached ();
1097 /* Not reached */
1098 return NULL;
1101 static gboolean
1102 ji_is_gsharedvt (MonoJitInfo *ji)
1104 if (ji && ji->has_generic_jit_info && (mono_jit_info_get_generic_sharing_context (ji)->is_gsharedvt))
1105 return TRUE;
1106 else
1107 return FALSE;
1111 * Describes the information used to construct a gsharedvt arg trampoline.
1113 typedef struct {
1114 gboolean is_in;
1115 gboolean calli;
1116 gint32 vcall_offset;
1117 gpointer addr;
1118 MonoMethodSignature *sig, *gsig;
1119 } GSharedVtTrampInfo;
1121 static guint
1122 tramp_info_hash (gconstpointer key)
1124 GSharedVtTrampInfo *tramp = (GSharedVtTrampInfo *)key;
1126 return (gsize)tramp->addr;
1129 static gboolean
1130 tramp_info_equal (gconstpointer a, gconstpointer b)
1132 GSharedVtTrampInfo *tramp1 = (GSharedVtTrampInfo *)a;
1133 GSharedVtTrampInfo *tramp2 = (GSharedVtTrampInfo *)b;
1135 /* The signatures should be internalized */
1136 return tramp1->is_in == tramp2->is_in && tramp1->calli == tramp2->calli && tramp1->vcall_offset == tramp2->vcall_offset &&
1137 tramp1->addr == tramp2->addr && tramp1->sig == tramp2->sig && tramp1->gsig == tramp2->gsig;
1140 static GENERATE_GET_CLASS_WITH_CACHE (valuetuple_1, "Mono", "ValueTuple`1");
1141 static GENERATE_GET_CLASS_WITH_CACHE (valuetuple_2, "Mono", "ValueTuple`2");
1142 static GENERATE_GET_CLASS_WITH_CACHE (valuetuple_3, "Mono", "ValueTuple`3");
1143 static GENERATE_GET_CLASS_WITH_CACHE (valuetuple_4, "Mono", "ValueTuple`4");
1144 static GENERATE_GET_CLASS_WITH_CACHE (valuetuple_5, "Mono", "ValueTuple`5");
1146 static MonoType*
1147 get_wrapper_shared_type (MonoType *t);
1148 static MonoType*
1149 get_wrapper_shared_type_full (MonoType *t, gboolean field);
1152 * get_wrapper_shared_vtype:
1154 * Return an instantiation of one of the Mono.ValueTuple types with the same
1155 * layout as the valuetype KLASS.
1157 static MonoType*
1158 get_wrapper_shared_vtype (MonoType *t)
1160 ERROR_DECL (error);
1161 MonoGenericContext ctx;
1162 MonoType *args [16];
1163 MonoClass *klass;
1164 MonoClass *tuple_class = NULL;
1165 int findex = 0;
1167 // FIXME: Map 1 member structs to primitive types on platforms where its supported
1169 klass = mono_class_from_mono_type_internal (t);
1170 if ((mono_class_get_flags (klass) & TYPE_ATTRIBUTE_LAYOUT_MASK) != TYPE_ATTRIBUTE_SEQUENTIAL_LAYOUT)
1171 return NULL;
1172 mono_class_setup_fields (klass);
1174 int num_fields = mono_class_get_field_count (klass);
1175 MonoClassField *klass_fields = m_class_get_fields (klass);
1177 for (int i = 0; i < num_fields; ++i) {
1178 MonoClassField *field = &klass_fields [i];
1180 if (field->type->attrs & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_HAS_FIELD_RVA))
1181 continue;
1182 MonoType *ftype = get_wrapper_shared_type_full (field->type, TRUE);
1183 args [findex ++] = ftype;
1184 if (findex >= 16)
1185 break;
1187 if (findex == 0 || findex > 5)
1188 return NULL;
1190 switch (findex) {
1191 case 1:
1192 tuple_class = mono_class_get_valuetuple_1_class ();
1193 break;
1194 case 2:
1195 tuple_class = mono_class_get_valuetuple_2_class ();
1196 break;
1197 case 3:
1198 tuple_class = mono_class_get_valuetuple_3_class ();
1199 break;
1200 case 4:
1201 tuple_class = mono_class_get_valuetuple_4_class ();
1202 break;
1203 case 5:
1204 tuple_class = mono_class_get_valuetuple_5_class ();
1205 break;
1206 default:
1207 g_assert_not_reached ();
1208 break;
1211 g_assert (tuple_class);
1213 memset (&ctx, 0, sizeof (ctx));
1214 ctx.class_inst = mono_metadata_get_generic_inst (findex, args);
1216 MonoClass *tuple_inst = mono_class_inflate_generic_class_checked (tuple_class, &ctx, error);
1217 mono_error_assert_ok (error);
1219 //printf ("T: %s\n", mono_class_full_name (tuple_inst));
1221 return m_class_get_byval_arg (tuple_inst);
1225 * get_wrapper_shared_type:
1227 * Return a type which is handled identically wrt to calling conventions as T.
1229 static MonoType*
1230 get_wrapper_shared_type_full (MonoType *t, gboolean is_field)
1232 if (t->byref)
1233 return m_class_get_this_arg (mono_defaults.int_class);
1234 t = mini_get_underlying_type (t);
1236 switch (t->type) {
1237 case MONO_TYPE_I1:
1238 /* This removes any attributes etc. */
1239 return m_class_get_byval_arg (mono_defaults.sbyte_class);
1240 case MONO_TYPE_U1:
1241 return m_class_get_byval_arg (mono_defaults.byte_class);
1242 case MONO_TYPE_I2:
1243 return m_class_get_byval_arg (mono_defaults.int16_class);
1244 case MONO_TYPE_U2:
1245 return m_class_get_byval_arg (mono_defaults.uint16_class);
1246 case MONO_TYPE_I4:
1247 return mono_get_int32_type ();
1248 case MONO_TYPE_U4:
1249 return m_class_get_byval_arg (mono_defaults.uint32_class);
1250 case MONO_TYPE_OBJECT:
1251 case MONO_TYPE_CLASS:
1252 case MONO_TYPE_SZARRAY:
1253 case MONO_TYPE_ARRAY:
1254 case MONO_TYPE_PTR:
1255 // FIXME: refs and intptr cannot be shared because
1256 // they are treated differently when a method has a vret arg,
1257 // see get_call_info ().
1258 return mono_get_object_type ();
1259 //return mono_get_int_type ();
1260 case MONO_TYPE_GENERICINST: {
1261 ERROR_DECL (error);
1262 MonoClass *klass;
1263 MonoGenericContext ctx;
1264 MonoGenericContext *orig_ctx;
1265 MonoGenericInst *inst;
1266 MonoType *args [16];
1267 int i;
1269 if (!MONO_TYPE_ISSTRUCT (t))
1270 return get_wrapper_shared_type (mono_get_object_type ());
1272 klass = mono_class_from_mono_type_internal (t);
1273 orig_ctx = &mono_class_get_generic_class (klass)->context;
1275 memset (&ctx, 0, sizeof (MonoGenericContext));
1277 inst = orig_ctx->class_inst;
1278 if (inst) {
1279 g_assert (inst->type_argc < 16);
1280 for (i = 0; i < inst->type_argc; ++i)
1281 args [i] = get_wrapper_shared_type_full (inst->type_argv [i], TRUE);
1282 ctx.class_inst = mono_metadata_get_generic_inst (inst->type_argc, args);
1284 inst = orig_ctx->method_inst;
1285 if (inst) {
1286 g_assert (inst->type_argc < 16);
1287 for (i = 0; i < inst->type_argc; ++i)
1288 args [i] = get_wrapper_shared_type_full (inst->type_argv [i], TRUE);
1289 ctx.method_inst = mono_metadata_get_generic_inst (inst->type_argc, args);
1291 klass = mono_class_inflate_generic_class_checked (mono_class_get_generic_class (klass)->container_class, &ctx, error);
1292 mono_error_assert_ok (error); /* FIXME don't swallow the error */
1294 t = m_class_get_byval_arg (klass);
1295 MonoType *shared_type = get_wrapper_shared_vtype (t);
1296 if (shared_type)
1297 t = shared_type;
1298 return t;
1300 case MONO_TYPE_VALUETYPE: {
1301 MonoType *shared_type = get_wrapper_shared_vtype (t);
1302 if (shared_type)
1303 t = shared_type;
1304 return t;
1306 #if TARGET_SIZEOF_VOID_P == 8
1307 case MONO_TYPE_I8:
1308 return mono_get_int_type ();
1309 #endif
1310 #if TARGET_SIZEOF_VOID_P == 4
1311 case MONO_TYPE_I:
1312 return mono_get_int32_type ();
1313 case MONO_TYPE_U:
1314 return m_class_get_byval_arg (mono_defaults.uint32_class);
1315 #endif
1316 default:
1317 break;
1320 //printf ("%s\n", mono_type_full_name (t));
1321 return t;
1324 static MonoType*
1325 get_wrapper_shared_type (MonoType *t)
1327 return get_wrapper_shared_type_full (t, FALSE);
1330 static MonoMethodSignature*
1331 mini_get_underlying_signature (MonoMethodSignature *sig)
1333 MonoMethodSignature *res = mono_metadata_signature_dup (sig);
1334 int i;
1336 res->ret = get_wrapper_shared_type (sig->ret);
1337 for (i = 0; i < sig->param_count; ++i)
1338 res->params [i] = get_wrapper_shared_type (sig->params [i]);
1339 res->generic_param_count = 0;
1340 res->is_inflated = 0;
1342 return res;
1346 * mini_get_gsharedvt_in_sig_wrapper:
1348 * Return a wrapper to translate between the normal and gsharedvt calling conventions of SIG.
1349 * The returned wrapper has a signature of SIG, plus one extra argument, which is an <addr, rgctx> pair.
1350 * The extra argument is passed the same way as an rgctx to shared methods.
1351 * It calls <addr> using the gsharedvt version of SIG, passing in <rgctx> as an extra argument.
1353 MonoMethod*
1354 mini_get_gsharedvt_in_sig_wrapper (MonoMethodSignature *sig)
1356 MonoMethodBuilder *mb;
1357 MonoMethod *res, *cached;
1358 WrapperInfo *info;
1359 MonoMethodSignature *csig, *gsharedvt_sig;
1360 int i, pindex, retval_var = 0;
1361 static GHashTable *cache;
1363 // FIXME: Memory management
1364 sig = mini_get_underlying_signature (sig);
1366 // FIXME: Normal cache
1367 gshared_lock ();
1368 if (!cache)
1369 cache = g_hash_table_new_full ((GHashFunc)mono_signature_hash, (GEqualFunc)mono_metadata_signature_equal, NULL, NULL);
1370 res = (MonoMethod*)g_hash_table_lookup (cache, sig);
1371 gshared_unlock ();
1372 if (res) {
1373 g_free (sig);
1374 return res;
1377 /* Create the signature for the wrapper */
1378 // FIXME:
1379 csig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 1) * sizeof (MonoType*)));
1380 memcpy (csig, sig, mono_metadata_signature_size (sig));
1381 csig->param_count ++;
1382 csig->params [sig->param_count] = mono_get_int_type ();
1384 /* Create the signature for the gsharedvt callconv */
1385 gsharedvt_sig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 2) * sizeof (MonoType*)));
1386 memcpy (gsharedvt_sig, sig, mono_metadata_signature_size (sig));
1387 pindex = 0;
1388 /* The return value is returned using an explicit vret argument */
1389 if (sig->ret->type != MONO_TYPE_VOID) {
1390 gsharedvt_sig->params [pindex ++] = mono_get_int_type ();
1391 gsharedvt_sig->ret = mono_get_void_type ();
1393 for (i = 0; i < sig->param_count; i++) {
1394 gsharedvt_sig->params [pindex] = sig->params [i];
1395 if (!sig->params [i]->byref) {
1396 gsharedvt_sig->params [pindex] = mono_metadata_type_dup (NULL, gsharedvt_sig->params [pindex]);
1397 gsharedvt_sig->params [pindex]->byref = 1;
1399 pindex ++;
1401 /* Rgctx arg */
1402 gsharedvt_sig->params [pindex ++] = mono_get_int_type ();
1403 gsharedvt_sig->param_count = pindex;
1405 // FIXME: Use shared signatures
1406 mb = mono_mb_new (mono_defaults.object_class, sig->hasthis ? "gsharedvt_in_sig" : "gsharedvt_in_sig_static", MONO_WRAPPER_OTHER);
1408 #ifndef DISABLE_JIT
1409 if (sig->ret->type != MONO_TYPE_VOID)
1410 retval_var = mono_mb_add_local (mb, sig->ret);
1412 /* Make the call */
1413 if (sig->hasthis)
1414 mono_mb_emit_ldarg (mb, 0);
1415 if (sig->ret->type != MONO_TYPE_VOID)
1416 mono_mb_emit_ldloc_addr (mb, retval_var);
1417 for (i = 0; i < sig->param_count; i++) {
1418 if (sig->params [i]->byref)
1419 mono_mb_emit_ldarg (mb, i + (sig->hasthis == TRUE));
1420 else
1421 mono_mb_emit_ldarg_addr (mb, i + (sig->hasthis == TRUE));
1423 /* Rgctx arg */
1424 mono_mb_emit_ldarg (mb, sig->param_count + (sig->hasthis ? 1 : 0));
1425 mono_mb_emit_icon (mb, TARGET_SIZEOF_VOID_P);
1426 mono_mb_emit_byte (mb, CEE_ADD);
1427 mono_mb_emit_byte (mb, CEE_LDIND_I);
1428 /* Method to call */
1429 mono_mb_emit_ldarg (mb, sig->param_count + (sig->hasthis ? 1 : 0));
1430 mono_mb_emit_byte (mb, CEE_LDIND_I);
1431 mono_mb_emit_calli (mb, gsharedvt_sig);
1432 if (sig->ret->type != MONO_TYPE_VOID)
1433 mono_mb_emit_ldloc (mb, retval_var);
1434 mono_mb_emit_byte (mb, CEE_RET);
1435 #endif
1437 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_GSHAREDVT_IN_SIG);
1438 info->d.gsharedvt.sig = sig;
1440 res = mono_mb_create (mb, csig, sig->param_count + 16, info);
1442 gshared_lock ();
1443 cached = (MonoMethod*)g_hash_table_lookup (cache, sig);
1444 if (cached)
1445 res = cached;
1446 else
1447 g_hash_table_insert (cache, sig, res);
1448 gshared_unlock ();
1449 return res;
1453 * mini_get_gsharedvt_out_sig_wrapper:
1455 * Same as in_sig_wrapper, but translate between the gsharedvt and normal signatures.
1457 MonoMethod*
1458 mini_get_gsharedvt_out_sig_wrapper (MonoMethodSignature *sig)
1460 MonoMethodBuilder *mb;
1461 MonoMethod *res, *cached;
1462 WrapperInfo *info;
1463 MonoMethodSignature *normal_sig, *csig;
1464 int i, pindex, args_start, ldind_op, stind_op;
1465 static GHashTable *cache;
1467 // FIXME: Memory management
1468 sig = mini_get_underlying_signature (sig);
1470 // FIXME: Normal cache
1471 gshared_lock ();
1472 if (!cache)
1473 cache = g_hash_table_new_full ((GHashFunc)mono_signature_hash, (GEqualFunc)mono_metadata_signature_equal, NULL, NULL);
1474 res = (MonoMethod*)g_hash_table_lookup (cache, sig);
1475 gshared_unlock ();
1476 if (res) {
1477 g_free (sig);
1478 return res;
1481 /* Create the signature for the wrapper */
1482 // FIXME:
1483 csig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 2) * sizeof (MonoType*)));
1484 memcpy (csig, 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 csig->params [pindex ++] = mono_get_int_type ();
1489 csig->ret = mono_get_void_type ();
1491 args_start = pindex;
1492 if (sig->hasthis)
1493 args_start ++;
1494 for (i = 0; i < sig->param_count; i++) {
1495 csig->params [pindex] = sig->params [i];
1496 if (!sig->params [i]->byref) {
1497 csig->params [pindex] = mono_metadata_type_dup (NULL, csig->params [pindex]);
1498 csig->params [pindex]->byref = 1;
1500 pindex ++;
1502 /* Rgctx arg */
1503 csig->params [pindex ++] = mono_get_int_type ();
1504 csig->param_count = pindex;
1506 /* Create the signature for the normal callconv */
1507 normal_sig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 2) * sizeof (MonoType*)));
1508 memcpy (normal_sig, sig, mono_metadata_signature_size (sig));
1509 normal_sig->param_count ++;
1510 normal_sig->params [sig->param_count] = mono_get_int_type ();
1512 // FIXME: Use shared signatures
1513 mb = mono_mb_new (mono_defaults.object_class, "gsharedvt_out_sig", MONO_WRAPPER_OTHER);
1515 #ifndef DISABLE_JIT
1516 if (sig->ret->type != MONO_TYPE_VOID)
1517 /* Load return address */
1518 mono_mb_emit_ldarg (mb, sig->hasthis ? 1 : 0);
1520 /* Make the call */
1521 if (sig->hasthis)
1522 mono_mb_emit_ldarg (mb, 0);
1523 for (i = 0; i < sig->param_count; i++) {
1524 if (sig->params [i]->byref) {
1525 mono_mb_emit_ldarg (mb, args_start + i);
1526 } else {
1527 ldind_op = mono_type_to_ldind (sig->params [i]);
1528 mono_mb_emit_ldarg (mb, args_start + i);
1529 // FIXME:
1530 if (ldind_op == CEE_LDOBJ)
1531 mono_mb_emit_op (mb, CEE_LDOBJ, mono_class_from_mono_type_internal (sig->params [i]));
1532 else
1533 mono_mb_emit_byte (mb, ldind_op);
1536 /* Rgctx arg */
1537 mono_mb_emit_ldarg (mb, args_start + sig->param_count);
1538 mono_mb_emit_icon (mb, TARGET_SIZEOF_VOID_P);
1539 mono_mb_emit_byte (mb, CEE_ADD);
1540 mono_mb_emit_byte (mb, CEE_LDIND_I);
1541 /* Method to call */
1542 mono_mb_emit_ldarg (mb, args_start + sig->param_count);
1543 mono_mb_emit_byte (mb, CEE_LDIND_I);
1544 mono_mb_emit_calli (mb, normal_sig);
1545 if (sig->ret->type != MONO_TYPE_VOID) {
1546 /* Store return value */
1547 stind_op = mono_type_to_stind (sig->ret);
1548 // FIXME:
1549 if (stind_op == CEE_STOBJ)
1550 mono_mb_emit_op (mb, CEE_STOBJ, mono_class_from_mono_type_internal (sig->ret));
1551 else if (stind_op == CEE_STIND_REF)
1552 /* Avoid write barriers, the vret arg points to the stack */
1553 mono_mb_emit_byte (mb, CEE_STIND_I);
1554 else
1555 mono_mb_emit_byte (mb, stind_op);
1557 mono_mb_emit_byte (mb, CEE_RET);
1558 #endif
1560 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_GSHAREDVT_OUT_SIG);
1561 info->d.gsharedvt.sig = sig;
1563 res = mono_mb_create (mb, csig, sig->param_count + 16, info);
1565 gshared_lock ();
1566 cached = (MonoMethod*)g_hash_table_lookup (cache, sig);
1567 if (cached)
1568 res = cached;
1569 else
1570 g_hash_table_insert (cache, sig, res);
1571 gshared_unlock ();
1572 return res;
1575 static gboolean
1576 signature_equal_pinvoke (MonoMethodSignature *sig1, MonoMethodSignature *sig2)
1578 /* mono_metadata_signature_equal () doesn't do this check */
1579 if (sig1->pinvoke != sig2->pinvoke)
1580 return FALSE;
1581 return mono_metadata_signature_equal (sig1, sig2);
1585 * mini_get_interp_in_wrapper:
1587 * Return a wrapper which can be used to transition from compiled code to the interpreter.
1588 * The wrapper has the same signature as SIG. It is very similar to a gsharedvt_in wrapper,
1589 * except the 'extra_arg' is passed in the rgctx reg, so this wrapper needs to be
1590 * called through a static rgctx trampoline.
1591 * FIXME: Move this elsewhere.
1593 MonoMethod*
1594 mini_get_interp_in_wrapper (MonoMethodSignature *sig)
1596 MonoMethodBuilder *mb;
1597 MonoMethod *res, *cached;
1598 WrapperInfo *info;
1599 MonoMethodSignature *csig, *entry_sig;
1600 int i, pindex, retval_var = 0;
1601 static GHashTable *cache;
1602 const char *name;
1603 gboolean generic = FALSE;
1604 gboolean return_native_struct;
1606 sig = mini_get_underlying_signature (sig);
1608 gshared_lock ();
1609 if (!cache)
1610 cache = g_hash_table_new_full ((GHashFunc)mono_signature_hash, (GEqualFunc)signature_equal_pinvoke, NULL, NULL);
1611 res = (MonoMethod*)g_hash_table_lookup (cache, sig);
1612 gshared_unlock ();
1613 if (res) {
1614 g_free (sig);
1615 return res;
1618 if (sig->param_count > 8)
1619 /* Call the generic interpreter entry point, the specialized ones only handle a limited number of arguments */
1620 generic = TRUE;
1623 * If we need to return a native struct, we can't allocate a local and store it
1624 * there since that assumes a managed representation. Instead we allocate on the
1625 * stack, pass this address to the interp_entry and when we return it we use
1626 * CEE_MONO_LDNATIVEOBJ
1628 return_native_struct = sig->ret->type == MONO_TYPE_VALUETYPE && sig->pinvoke;
1630 /* Create the signature for the wrapper */
1631 csig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + (sig->param_count * sizeof (MonoType*)));
1632 memcpy (csig, sig, mono_metadata_signature_size (sig));
1634 for (i = 0; i < sig->param_count; i++) {
1635 if (sig->params [i]->byref)
1636 csig->params [i] = m_class_get_this_arg (mono_defaults.int_class);
1639 MonoType *int_type = mono_get_int_type ();
1640 /* Create the signature for the callee callconv */
1641 if (generic) {
1643 * The called function has the following signature:
1644 * interp_entry_general (gpointer this_arg, gpointer res, gpointer *args, gpointer rmethod)
1646 entry_sig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + (4 * sizeof (MonoType*)));
1647 entry_sig->ret = mono_get_void_type ();
1648 entry_sig->param_count = 4;
1649 entry_sig->params [0] = int_type;
1650 entry_sig->params [1] = int_type;
1651 entry_sig->params [2] = int_type;
1652 entry_sig->params [3] = int_type;
1653 name = "interp_in_generic";
1654 generic = TRUE;
1655 } else {
1657 * The called function has the following signature:
1658 * void entry(<optional this ptr>, <optional return ptr>, <arguments>, <extra arg>)
1660 entry_sig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 2) * sizeof (MonoType*)));
1661 memcpy (entry_sig, sig, mono_metadata_signature_size (sig));
1662 pindex = 0;
1663 /* The return value is returned using an explicit vret argument */
1664 if (sig->ret->type != MONO_TYPE_VOID) {
1665 entry_sig->params [pindex ++] = int_type;
1666 entry_sig->ret = mono_get_void_type ();
1668 for (i = 0; i < sig->param_count; i++) {
1669 entry_sig->params [pindex] = sig->params [i];
1670 if (!sig->params [i]->byref) {
1671 entry_sig->params [pindex] = mono_metadata_type_dup (NULL, entry_sig->params [pindex]);
1672 entry_sig->params [pindex]->byref = 1;
1674 pindex ++;
1676 /* Extra arg */
1677 entry_sig->params [pindex ++] = int_type;
1678 entry_sig->param_count = pindex;
1679 name = sig->hasthis ? "interp_in" : "interp_in_static";
1682 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_OTHER);
1685 * This is needed to be able to unwind out of interpreted code to managed.
1686 * When we are called from native code we can't unwind and we might also not
1687 * be attached.
1689 if (!sig->pinvoke)
1690 mb->method->save_lmf = 1;
1692 #ifndef DISABLE_JIT
1693 if (return_native_struct) {
1694 retval_var = mono_mb_add_local (mb, int_type);
1695 mono_mb_emit_icon (mb, mono_class_native_size (sig->ret->data.klass, NULL));
1696 mono_mb_emit_byte (mb, CEE_PREFIX1);
1697 mono_mb_emit_byte (mb, CEE_LOCALLOC);
1698 mono_mb_emit_stloc (mb, retval_var);
1699 } else if (sig->ret->type != MONO_TYPE_VOID) {
1700 retval_var = mono_mb_add_local (mb, sig->ret);
1703 /* Make the call */
1704 if (generic) {
1705 /* Collect arguments */
1706 int args_var = mono_mb_add_local (mb, int_type);
1708 mono_mb_emit_icon (mb, TARGET_SIZEOF_VOID_P * sig->param_count);
1709 mono_mb_emit_byte (mb, CEE_PREFIX1);
1710 mono_mb_emit_byte (mb, CEE_LOCALLOC);
1711 mono_mb_emit_stloc (mb, args_var);
1713 for (i = 0; i < sig->param_count; i++) {
1714 mono_mb_emit_ldloc (mb, args_var);
1715 mono_mb_emit_icon (mb, TARGET_SIZEOF_VOID_P * i);
1716 mono_mb_emit_byte (mb, CEE_ADD);
1717 if (sig->params [i]->byref)
1718 mono_mb_emit_ldarg (mb, i + (sig->hasthis == TRUE));
1719 else
1720 mono_mb_emit_ldarg_addr (mb, i + (sig->hasthis == TRUE));
1721 mono_mb_emit_byte (mb, CEE_STIND_I);
1724 if (sig->hasthis)
1725 mono_mb_emit_ldarg (mb, 0);
1726 else
1727 mono_mb_emit_byte (mb, CEE_LDNULL);
1728 if (return_native_struct)
1729 mono_mb_emit_ldloc (mb, retval_var);
1730 else if (sig->ret->type != MONO_TYPE_VOID)
1731 mono_mb_emit_ldloc_addr (mb, retval_var);
1732 else
1733 mono_mb_emit_byte (mb, CEE_LDNULL);
1734 mono_mb_emit_ldloc (mb, args_var);
1735 } else {
1736 if (sig->hasthis)
1737 mono_mb_emit_ldarg (mb, 0);
1738 if (return_native_struct)
1739 mono_mb_emit_ldloc (mb, retval_var);
1740 else if (sig->ret->type != MONO_TYPE_VOID)
1741 mono_mb_emit_ldloc_addr (mb, retval_var);
1742 for (i = 0; i < sig->param_count; i++) {
1743 if (sig->params [i]->byref)
1744 mono_mb_emit_ldarg (mb, i + (sig->hasthis == TRUE));
1745 else
1746 mono_mb_emit_ldarg_addr (mb, i + (sig->hasthis == TRUE));
1749 /* Extra arg */
1750 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1751 mono_mb_emit_byte (mb, CEE_MONO_GET_RGCTX_ARG);
1752 mono_mb_emit_icon (mb, TARGET_SIZEOF_VOID_P);
1753 mono_mb_emit_byte (mb, CEE_ADD);
1754 mono_mb_emit_byte (mb, CEE_LDIND_I);
1755 /* Method to call */
1756 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1757 mono_mb_emit_byte (mb, CEE_MONO_GET_RGCTX_ARG);
1758 mono_mb_emit_byte (mb, CEE_LDIND_I);
1759 mono_mb_emit_calli (mb, entry_sig);
1761 if (return_native_struct) {
1762 mono_mb_emit_ldloc (mb, retval_var);
1763 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1764 mono_mb_emit_op (mb, CEE_MONO_LDNATIVEOBJ, sig->ret->data.klass);
1765 } else if (sig->ret->type != MONO_TYPE_VOID) {
1766 mono_mb_emit_ldloc (mb, retval_var);
1768 mono_mb_emit_byte (mb, CEE_RET);
1769 #endif
1771 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_INTERP_IN);
1772 info->d.interp_in.sig = csig;
1774 res = mono_mb_create (mb, csig, sig->param_count + 16, info);
1776 gshared_lock ();
1777 cached = (MonoMethod*)g_hash_table_lookup (cache, sig);
1778 if (cached) {
1779 mono_free_method (res);
1780 res = cached;
1781 } else {
1782 g_hash_table_insert (cache, sig, res);
1784 gshared_unlock ();
1785 mono_mb_free (mb);
1787 return res;
1791 * This wrapper enables EH to resume directly to the code calling it. It is
1792 * needed so EH can resume directly into jitted code from interp, or into interp
1793 * when it needs to jump over native frames.
1795 static MonoMethod*
1796 mini_create_interp_lmf_wrapper (gpointer target)
1798 MonoMethod* ret;
1799 MonoMethodSignature *sig;
1800 MonoMethodBuilder *mb;
1801 WrapperInfo *info;
1802 MonoType *int_type = mono_get_int_type ();
1804 mb = mono_mb_new (mono_defaults.object_class, "interp_lmf", MONO_WRAPPER_OTHER);
1806 sig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
1807 sig->ret = mono_get_void_type ();;
1808 sig->params [0] = int_type;
1809 sig->params [1] = int_type;
1811 /* This is the only thing that the wrapper needs to do */
1812 mb->method->save_lmf = 1;
1814 #ifndef DISABLE_JIT
1815 mono_mb_emit_byte (mb, CEE_LDARG_0);
1816 mono_mb_emit_byte (mb, CEE_LDARG_1);
1818 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1819 mono_mb_emit_op (mb, CEE_MONO_ICALL, target);
1821 mono_mb_emit_byte (mb, CEE_RET);
1822 #endif
1823 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_INTERP_LMF);
1824 ret = mono_mb_create (mb, sig, 4, info);
1825 mono_mb_free (mb);
1827 return ret;
1830 MonoMethod*
1831 mini_get_interp_lmf_wrapper (void)
1833 static MonoMethod *wrapper = NULL;
1835 if (wrapper)
1836 return wrapper;
1838 wrapper = (MonoMethod*)mini_create_interp_lmf_wrapper ((gpointer)mono_interp_entry_from_trampoline);
1840 return wrapper;
1843 MonoMethodSignature*
1844 mini_get_gsharedvt_out_sig_wrapper_signature (gboolean has_this, gboolean has_ret, int param_count)
1846 MonoMethodSignature *sig = g_malloc0 (sizeof (MonoMethodSignature) + ((param_count + 3) * sizeof (MonoType*)));
1847 int i, pindex;
1848 MonoType *int_type = mono_get_int_type ();
1850 sig->ret = mono_get_void_type ();
1851 sig->sentinelpos = -1;
1852 pindex = 0;
1853 if (has_this)
1854 /* this */
1855 sig->params [pindex ++] = int_type;
1856 if (has_ret)
1857 /* vret */
1858 sig->params [pindex ++] = int_type;
1859 for (i = 0; i < param_count; ++i)
1860 /* byref arguments */
1861 sig->params [pindex ++] = int_type;
1862 /* extra arg */
1863 sig->params [pindex ++] = int_type;
1864 sig->param_count = pindex;
1866 return sig;
1870 * mini_get_gsharedvt_wrapper:
1872 * Return a gsharedvt in/out wrapper for calling ADDR.
1874 gpointer
1875 mini_get_gsharedvt_wrapper (gboolean gsharedvt_in, gpointer addr, MonoMethodSignature *normal_sig, MonoMethodSignature *gsharedvt_sig, gint32 vcall_offset, gboolean calli)
1877 ERROR_DECL (error);
1878 gpointer res, info;
1879 MonoDomain *domain = mono_domain_get ();
1880 MonoJitDomainInfo *domain_info;
1881 GSharedVtTrampInfo *tramp_info;
1882 GSharedVtTrampInfo tinfo;
1884 if (mono_llvm_only) {
1885 MonoMethod *wrapper;
1887 if (gsharedvt_in)
1888 wrapper = mini_get_gsharedvt_in_sig_wrapper (normal_sig);
1889 else
1890 wrapper = mini_get_gsharedvt_out_sig_wrapper (normal_sig);
1891 res = mono_compile_method_checked (wrapper, error);
1892 mono_error_assert_ok (error);
1893 return res;
1896 memset (&tinfo, 0, sizeof (tinfo));
1897 tinfo.is_in = gsharedvt_in;
1898 tinfo.calli = calli;
1899 tinfo.vcall_offset = vcall_offset;
1900 tinfo.addr = addr;
1901 tinfo.sig = normal_sig;
1902 tinfo.gsig = gsharedvt_sig;
1904 domain_info = domain_jit_info (domain);
1907 * The arg trampolines might only have a finite number in full-aot, so use a cache.
1909 mono_domain_lock (domain);
1910 if (!domain_info->gsharedvt_arg_tramp_hash)
1911 domain_info->gsharedvt_arg_tramp_hash = g_hash_table_new (tramp_info_hash, tramp_info_equal);
1912 res = g_hash_table_lookup (domain_info->gsharedvt_arg_tramp_hash, &tinfo);
1913 mono_domain_unlock (domain);
1914 if (res)
1915 return res;
1917 info = mono_arch_get_gsharedvt_call_info (addr, normal_sig, gsharedvt_sig, gsharedvt_in, vcall_offset, calli);
1919 if (gsharedvt_in) {
1920 static gpointer tramp_addr;
1921 MonoMethod *wrapper;
1923 if (!tramp_addr) {
1924 wrapper = mono_marshal_get_gsharedvt_in_wrapper ();
1925 addr = mono_compile_method_checked (wrapper, error);
1926 mono_memory_barrier ();
1927 mono_error_assert_ok (error);
1928 tramp_addr = addr;
1930 addr = tramp_addr;
1931 } else {
1932 static gpointer tramp_addr;
1933 MonoMethod *wrapper;
1935 if (!tramp_addr) {
1936 wrapper = mono_marshal_get_gsharedvt_out_wrapper ();
1937 addr = mono_compile_method_checked (wrapper, error);
1938 mono_memory_barrier ();
1939 mono_error_assert_ok (error);
1940 tramp_addr = addr;
1942 addr = tramp_addr;
1945 if (mono_aot_only)
1946 addr = mono_aot_get_gsharedvt_arg_trampoline (info, addr);
1947 else
1948 addr = mono_arch_get_gsharedvt_arg_trampoline (mono_domain_get (), info, addr);
1950 mono_atomic_inc_i32 (&gsharedvt_num_trampolines);
1952 /* Cache it */
1953 tramp_info = (GSharedVtTrampInfo *)mono_domain_alloc0 (domain, sizeof (GSharedVtTrampInfo));
1954 memcpy (tramp_info, &tinfo, sizeof (GSharedVtTrampInfo));
1956 mono_domain_lock (domain);
1957 /* Duplicates are not a problem */
1958 g_hash_table_insert (domain_info->gsharedvt_arg_tramp_hash, tramp_info, addr);
1959 mono_domain_unlock (domain);
1961 return addr;
1965 * instantiate_info:
1967 * Instantiate the info given by OTI for context CONTEXT.
1969 static gpointer
1970 instantiate_info (MonoDomain *domain, MonoRuntimeGenericContextInfoTemplate *oti,
1971 MonoGenericContext *context, MonoClass *klass, MonoError *error)
1973 gpointer data;
1974 gboolean temporary;
1976 error_init (error);
1978 if (!oti->data)
1979 return NULL;
1981 switch (oti->info_type) {
1982 case MONO_RGCTX_INFO_STATIC_DATA:
1983 case MONO_RGCTX_INFO_KLASS:
1984 case MONO_RGCTX_INFO_ELEMENT_KLASS:
1985 case MONO_RGCTX_INFO_VTABLE:
1986 case MONO_RGCTX_INFO_CAST_CACHE:
1987 temporary = TRUE;
1988 break;
1989 default:
1990 temporary = FALSE;
1993 data = inflate_info (oti, context, klass, temporary);
1995 switch (oti->info_type) {
1996 case MONO_RGCTX_INFO_STATIC_DATA:
1997 case MONO_RGCTX_INFO_KLASS:
1998 case MONO_RGCTX_INFO_ELEMENT_KLASS:
1999 case MONO_RGCTX_INFO_VTABLE:
2000 case MONO_RGCTX_INFO_CAST_CACHE:
2001 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
2002 case MONO_RGCTX_INFO_VALUE_SIZE:
2003 case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
2004 case MONO_RGCTX_INFO_CLASS_IS_REF_OR_CONTAINS_REFS:
2005 case MONO_RGCTX_INFO_MEMCPY:
2006 case MONO_RGCTX_INFO_BZERO:
2007 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
2008 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: {
2009 MonoClass *arg_class = mono_class_from_mono_type_internal ((MonoType *)data);
2011 free_inflated_info (oti->info_type, data);
2012 g_assert (arg_class);
2014 /* The class might be used as an argument to
2015 mono_value_copy(), which requires that its GC
2016 descriptor has been computed. */
2017 if (oti->info_type == MONO_RGCTX_INFO_KLASS)
2018 mono_class_compute_gc_descriptor (arg_class);
2020 return class_type_info (domain, arg_class, oti->info_type, error);
2022 case MONO_RGCTX_INFO_TYPE:
2023 return data;
2024 case MONO_RGCTX_INFO_REFLECTION_TYPE: {
2025 MonoReflectionType *ret = mono_type_get_object_checked (domain, (MonoType *)data, error);
2027 return ret;
2029 case MONO_RGCTX_INFO_METHOD:
2030 return data;
2031 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE: {
2032 MonoMethod *m = (MonoMethod*)data;
2033 gpointer addr;
2034 gpointer arg = NULL;
2036 if (mono_llvm_only) {
2037 addr = mono_compile_method_checked (m, error);
2038 return_val_if_nok (error, NULL);
2039 addr = mini_add_method_wrappers_llvmonly (m, addr, FALSE, FALSE, &arg);
2041 /* Returns an ftndesc */
2042 return mini_create_llvmonly_ftndesc (domain, addr, arg);
2043 } else {
2044 addr = mono_compile_method_checked ((MonoMethod *)data, error);
2045 return_val_if_nok (error, NULL);
2046 return mini_add_method_trampoline ((MonoMethod *)data, addr, mono_method_needs_static_rgctx_invoke ((MonoMethod *)data, FALSE), FALSE);
2049 case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER: {
2050 MonoMethod *m = (MonoMethod*)data;
2051 gpointer addr;
2052 gpointer arg = NULL;
2054 g_assert (mono_llvm_only);
2056 addr = mono_compile_method_checked (m, error);
2057 return_val_if_nok (error, NULL);
2059 MonoJitInfo *ji;
2060 gboolean callee_gsharedvt;
2062 ji = mini_jit_info_table_find (mono_domain_get (), (char *)mono_get_addr_from_ftnptr (addr), NULL);
2063 g_assert (ji);
2064 callee_gsharedvt = mini_jit_info_is_gsharedvt (ji);
2065 if (callee_gsharedvt)
2066 callee_gsharedvt = mini_is_gsharedvt_variable_signature (mono_method_signature_internal (jinfo_get_method (ji)));
2067 if (callee_gsharedvt) {
2068 /* No need for a wrapper */
2069 return mini_create_llvmonly_ftndesc (domain, addr, mini_method_get_rgctx (m));
2070 } else {
2071 addr = mini_add_method_wrappers_llvmonly (m, addr, TRUE, FALSE, &arg);
2073 /* Returns an ftndesc */
2074 return mini_create_llvmonly_ftndesc (domain, addr, arg);
2077 case MONO_RGCTX_INFO_VIRT_METHOD_CODE: {
2078 MonoJumpInfoVirtMethod *info = (MonoJumpInfoVirtMethod *)data;
2079 MonoClass *iface_class = info->method->klass;
2080 MonoMethod *method;
2081 int ioffset, slot;
2082 gpointer addr;
2084 mono_class_setup_vtable (info->klass);
2085 // FIXME: Check type load
2086 if (mono_class_is_interface (iface_class)) {
2087 ioffset = mono_class_interface_offset (info->klass, iface_class);
2088 g_assert (ioffset != -1);
2089 } else {
2090 ioffset = 0;
2092 slot = mono_method_get_vtable_slot (info->method);
2093 g_assert (slot != -1);
2094 g_assert (m_class_get_vtable (info->klass));
2095 method = m_class_get_vtable (info->klass) [ioffset + slot];
2097 method = mono_class_inflate_generic_method_checked (method, context, error);
2098 return_val_if_nok (error, NULL);
2100 addr = mono_compile_method_checked (method, error);
2101 return_val_if_nok (error, NULL);
2102 if (mono_llvm_only) {
2103 gpointer arg = NULL;
2104 addr = mini_add_method_wrappers_llvmonly (method, addr, FALSE, FALSE, &arg);
2106 /* Returns an ftndesc */
2107 return mini_create_llvmonly_ftndesc (domain, addr, arg);
2108 } else {
2109 return mini_add_method_trampoline (method, addr, mono_method_needs_static_rgctx_invoke (method, FALSE), FALSE);
2112 case MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE: {
2113 MonoJumpInfoVirtMethod *info = (MonoJumpInfoVirtMethod *)data;
2114 MonoClass *iface_class = info->method->klass;
2115 MonoMethod *method;
2116 MonoClass *impl_class;
2117 int ioffset, slot;
2119 mono_class_setup_vtable (info->klass);
2120 // FIXME: Check type load
2121 if (mono_class_is_interface (iface_class)) {
2122 ioffset = mono_class_interface_offset (info->klass, iface_class);
2123 g_assert (ioffset != -1);
2124 } else {
2125 ioffset = 0;
2127 slot = mono_method_get_vtable_slot (info->method);
2128 g_assert (slot != -1);
2129 g_assert (m_class_get_vtable (info->klass));
2130 method = m_class_get_vtable (info->klass) [ioffset + slot];
2132 impl_class = method->klass;
2133 if (MONO_TYPE_IS_REFERENCE (m_class_get_byval_arg (impl_class)))
2134 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_REF);
2135 else if (mono_class_is_nullable (impl_class))
2136 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_NULLABLE);
2137 else
2138 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_VTYPE);
2140 #ifndef DISABLE_REMOTING
2141 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK: {
2142 MonoMethod *remoting_invoke_method = mono_marshal_get_remoting_invoke_with_check ((MonoMethod *)data, error);
2143 return_val_if_nok (error, NULL);
2144 return mono_compile_method_checked (remoting_invoke_method, error);
2146 #endif
2147 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE:
2148 return mono_domain_alloc0 (domain, sizeof (gpointer));
2149 case MONO_RGCTX_INFO_CLASS_FIELD:
2150 return data;
2151 case MONO_RGCTX_INFO_FIELD_OFFSET: {
2152 MonoClassField *field = (MonoClassField *)data;
2154 /* The value is offset by 1 */
2155 if (m_class_is_valuetype (field->parent) && !(field->type->attrs & FIELD_ATTRIBUTE_STATIC))
2156 return GUINT_TO_POINTER (field->offset - MONO_ABI_SIZEOF (MonoObject) + 1);
2157 else
2158 return GUINT_TO_POINTER (field->offset + 1);
2160 case MONO_RGCTX_INFO_METHOD_RGCTX: {
2161 MonoMethodInflated *method = (MonoMethodInflated *)data;
2163 g_assert (method->method.method.is_inflated);
2165 return mini_method_get_rgctx ((MonoMethod*)method);
2167 case MONO_RGCTX_INFO_METHOD_CONTEXT: {
2168 MonoMethodInflated *method = (MonoMethodInflated *)data;
2170 g_assert (method->method.method.is_inflated);
2171 g_assert (method->context.method_inst);
2173 return method->context.method_inst;
2175 case MONO_RGCTX_INFO_SIG_GSHAREDVT_IN_TRAMPOLINE_CALLI: {
2176 MonoMethodSignature *gsig = (MonoMethodSignature *)oti->data;
2177 MonoMethodSignature *sig = (MonoMethodSignature *)data;
2178 gpointer addr;
2181 * This is an indirect call to the address passed by the caller in the rgctx reg.
2183 addr = mini_get_gsharedvt_wrapper (TRUE, NULL, sig, gsig, -1, TRUE);
2184 return addr;
2186 case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI: {
2187 MonoMethodSignature *gsig = (MonoMethodSignature *)oti->data;
2188 MonoMethodSignature *sig = (MonoMethodSignature *)data;
2189 gpointer addr;
2192 * This is an indirect call to the address passed by the caller in the rgctx reg.
2194 addr = mini_get_gsharedvt_wrapper (FALSE, NULL, sig, gsig, -1, TRUE);
2195 return addr;
2197 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE:
2198 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT: {
2199 MonoJumpInfoGSharedVtCall *call_info = (MonoJumpInfoGSharedVtCall *)data;
2200 MonoMethodSignature *call_sig;
2201 MonoMethod *method;
2202 gpointer addr;
2203 MonoJitInfo *callee_ji;
2204 gboolean virtual_ = oti->info_type == MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT;
2205 gint32 vcall_offset;
2206 gboolean callee_gsharedvt;
2208 /* This is the original generic signature used by the caller */
2209 call_sig = call_info->sig;
2210 /* This is the instantiated method which is called */
2211 method = call_info->method;
2213 g_assert (method->is_inflated);
2215 if (mono_llvm_only && (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED))
2216 method = mono_marshal_get_synchronized_wrapper (method);
2218 if (!virtual_) {
2219 addr = mono_compile_method_checked (method, error);
2220 return_val_if_nok (error, NULL);
2221 } else
2222 addr = NULL;
2224 if (virtual_) {
2225 /* Same as in mono_emit_method_call_full () */
2226 if ((m_class_get_parent (method->klass) == mono_defaults.multicastdelegate_class) && (!strcmp (method->name, "Invoke"))) {
2227 /* See mono_emit_method_call_full () */
2228 /* The gsharedvt trampoline will recognize this constant */
2229 vcall_offset = MONO_GSHAREDVT_DEL_INVOKE_VT_OFFSET;
2230 } else if (mono_class_is_interface (method->klass)) {
2231 guint32 imt_slot = mono_method_get_imt_slot (method);
2232 vcall_offset = ((gint32)imt_slot - MONO_IMT_SIZE) * TARGET_SIZEOF_VOID_P;
2233 } else {
2234 vcall_offset = G_STRUCT_OFFSET (MonoVTable, vtable) +
2235 ((mono_method_get_vtable_index (method)) * (TARGET_SIZEOF_VOID_P));
2237 } else {
2238 vcall_offset = -1;
2241 // FIXME: This loads information in the AOT case
2242 callee_ji = mini_jit_info_table_find (mono_domain_get (), (char *)mono_get_addr_from_ftnptr (addr), NULL);
2243 callee_gsharedvt = ji_is_gsharedvt (callee_ji);
2246 * For gsharedvt calls made out of gsharedvt methods, the callee could end up being a gsharedvt method, or a normal
2247 * non-shared method. The latter call cannot be patched, so instead of using a normal call, we make an indirect
2248 * call through the rgctx, in effect patching the rgctx entry instead of the call site.
2249 * For virtual calls, the caller might be a normal or a gsharedvt method. Since there is only one vtable slot,
2250 * this difference needs to be handed on the caller side. This is currently implemented by adding a gsharedvt-in
2251 * trampoline to all gsharedvt methods and storing this trampoline into the vtable slot. Virtual calls made from
2252 * gsharedvt methods always go through a gsharedvt-out trampoline, so the calling sequence is:
2253 * caller -> out trampoline -> in trampoline -> callee
2254 * This is not very efficient, but it is easy to implement.
2256 if (virtual_ || !callee_gsharedvt) {
2257 MonoMethodSignature *sig, *gsig;
2259 g_assert (method->is_inflated);
2261 sig = mono_method_signature_internal (method);
2262 gsig = call_sig;
2264 if (mono_llvm_only) {
2265 if (mini_is_gsharedvt_variable_signature (call_sig)) {
2266 /* The virtual case doesn't go through this code */
2267 g_assert (!virtual_);
2269 sig = mono_method_signature_internal (jinfo_get_method (callee_ji));
2270 gpointer out_wrapper = mini_get_gsharedvt_wrapper (FALSE, NULL, sig, gsig, -1, FALSE);
2271 MonoFtnDesc *out_wrapper_arg = mini_create_llvmonly_ftndesc (domain, callee_ji->code_start, mini_method_get_rgctx (method));
2273 /* Returns an ftndesc */
2274 addr = mini_create_llvmonly_ftndesc (domain, out_wrapper, out_wrapper_arg);
2275 } else {
2276 addr = mini_create_llvmonly_ftndesc (domain, addr, mini_method_get_rgctx (method));
2278 } else {
2279 addr = mini_get_gsharedvt_wrapper (FALSE, addr, sig, gsig, vcall_offset, FALSE);
2281 #if 0
2282 if (virtual)
2283 printf ("OUT-VCALL: %s\n", mono_method_full_name (method, TRUE));
2284 else
2285 printf ("OUT: %s\n", mono_method_full_name (method, TRUE));
2286 #endif
2287 } else if (callee_gsharedvt) {
2288 MonoMethodSignature *sig, *gsig;
2291 * This is a combination of the out and in cases, since both the caller and the callee are gsharedvt methods.
2292 * The caller and the callee can use different gsharedvt signatures, so we have to add both an out and an in
2293 * trampoline, i.e.:
2294 * class Base<T> {
2295 * public void foo<T1> (T1 t1, T t, object o) {}
2297 * class AClass : Base<long> {
2298 * public void bar<T> (T t, long time, object o) {
2299 * foo (t, time, o);
2302 * Here, the caller uses !!0,long, while the callee uses !!0,!0
2303 * FIXME: Optimize this.
2306 if (mono_llvm_only) {
2307 /* Both wrappers receive an extra <addr, rgctx> argument */
2308 sig = mono_method_signature_internal (method);
2309 gsig = mono_method_signature_internal (jinfo_get_method (callee_ji));
2311 /* Return a function descriptor */
2313 if (mini_is_gsharedvt_variable_signature (call_sig)) {
2315 * This is not an optimization, but its needed, since the concrete signature 'sig'
2316 * might not exist at all in IL, so the AOT compiler cannot generate the wrappers
2317 * for it.
2319 addr = mini_create_llvmonly_ftndesc (domain, callee_ji->code_start, mini_method_get_rgctx (method));
2320 } else if (mini_is_gsharedvt_variable_signature (gsig)) {
2321 gpointer in_wrapper = mini_get_gsharedvt_wrapper (TRUE, callee_ji->code_start, sig, gsig, -1, FALSE);
2323 gpointer in_wrapper_arg = mini_create_llvmonly_ftndesc (domain, callee_ji->code_start, mini_method_get_rgctx (method));
2325 addr = mini_create_llvmonly_ftndesc (domain, in_wrapper, in_wrapper_arg);
2326 } else {
2327 addr = mini_create_llvmonly_ftndesc (domain, addr, mini_method_get_rgctx (method));
2329 } else if (call_sig == mono_method_signature_internal (method)) {
2330 } else {
2331 sig = mono_method_signature_internal (method);
2332 gsig = mono_method_signature_internal (jinfo_get_method (callee_ji));
2334 addr = mini_get_gsharedvt_wrapper (TRUE, callee_ji->code_start, sig, gsig, -1, FALSE);
2336 sig = mono_method_signature_internal (method);
2337 gsig = call_sig;
2339 addr = mini_get_gsharedvt_wrapper (FALSE, addr, sig, gsig, -1, FALSE);
2341 //printf ("OUT-IN-RGCTX: %s\n", mono_method_full_name (method, TRUE));
2345 return addr;
2347 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO: {
2348 MonoGSharedVtMethodInfo *info = (MonoGSharedVtMethodInfo *)data;
2349 MonoGSharedVtMethodRuntimeInfo *res;
2350 MonoType *t;
2351 int i, offset, align, size;
2353 // FIXME:
2354 res = (MonoGSharedVtMethodRuntimeInfo *)g_malloc0 (sizeof (MonoGSharedVtMethodRuntimeInfo) + (info->num_entries * sizeof (gpointer)));
2356 offset = 0;
2357 for (i = 0; i < info->num_entries; ++i) {
2358 MonoRuntimeGenericContextInfoTemplate *template_ = &info->entries [i];
2360 switch (template_->info_type) {
2361 case MONO_RGCTX_INFO_LOCAL_OFFSET:
2362 t = (MonoType *)template_->data;
2364 size = mono_type_size (t, &align);
2366 if (align < sizeof (gpointer))
2367 align = sizeof (gpointer);
2368 if (MONO_TYPE_ISSTRUCT (t) && align < 2 * sizeof (gpointer))
2369 align = 2 * sizeof (gpointer);
2371 // FIXME: Do the same things as alloc_stack_slots
2372 offset += align - 1;
2373 offset &= ~(align - 1);
2374 res->entries [i] = GINT_TO_POINTER (offset);
2375 offset += size;
2376 break;
2377 default:
2378 res->entries [i] = instantiate_info (domain, template_, context, klass, error);
2379 if (!mono_error_ok (error))
2380 return NULL;
2381 break;
2384 res->locals_size = offset;
2386 return res;
2388 case MONO_RGCTX_INFO_DELEGATE_TRAMP_INFO: {
2389 MonoDelegateClassMethodPair *dele_info = (MonoDelegateClassMethodPair*)data;
2390 gpointer trampoline;
2392 if (dele_info->is_virtual)
2393 trampoline = mono_create_delegate_virtual_trampoline (domain, dele_info->klass, dele_info->method);
2394 else
2395 trampoline = mono_create_delegate_trampoline_info (domain, dele_info->klass, dele_info->method);
2397 g_assert (trampoline);
2398 return trampoline;
2400 default:
2401 g_assert_not_reached ();
2403 /* Not reached */
2404 return NULL;
2408 * LOCKING: loader lock
2410 static void
2411 fill_in_rgctx_template_slot (MonoClass *klass, int type_argc, int index, gpointer data, MonoRgctxInfoType info_type)
2413 MonoRuntimeGenericContextTemplate *template_ = mono_class_get_runtime_generic_context_template (klass);
2414 MonoClass *subclass;
2416 rgctx_template_set_slot (m_class_get_image (klass), template_, type_argc, index, data, info_type);
2418 /* Recurse for all subclasses */
2419 if (generic_subclass_hash)
2420 subclass = (MonoClass *)g_hash_table_lookup (generic_subclass_hash, klass);
2421 else
2422 subclass = NULL;
2424 while (subclass) {
2425 MonoRuntimeGenericContextInfoTemplate subclass_oti;
2426 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
2428 g_assert (subclass_template);
2430 subclass_oti = class_get_rgctx_template_oti (m_class_get_parent (subclass), type_argc, index, FALSE, FALSE, NULL);
2431 g_assert (subclass_oti.data);
2433 fill_in_rgctx_template_slot (subclass, type_argc, index, subclass_oti.data, info_type);
2435 subclass = subclass_template->next_subclass;
2439 const char*
2440 mono_rgctx_info_type_to_str (MonoRgctxInfoType type)
2442 switch (type) {
2443 case MONO_RGCTX_INFO_STATIC_DATA: return "STATIC_DATA";
2444 case MONO_RGCTX_INFO_KLASS: return "KLASS";
2445 case MONO_RGCTX_INFO_ELEMENT_KLASS: return "ELEMENT_KLASS";
2446 case MONO_RGCTX_INFO_VTABLE: return "VTABLE";
2447 case MONO_RGCTX_INFO_TYPE: return "TYPE";
2448 case MONO_RGCTX_INFO_REFLECTION_TYPE: return "REFLECTION_TYPE";
2449 case MONO_RGCTX_INFO_METHOD: return "METHOD";
2450 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO: return "GSHAREDVT_INFO";
2451 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE: return "GENERIC_METHOD_CODE";
2452 case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER: return "GSHAREDVT_OUT_WRAPPER";
2453 case MONO_RGCTX_INFO_CLASS_FIELD: return "CLASS_FIELD";
2454 case MONO_RGCTX_INFO_METHOD_RGCTX: return "METHOD_RGCTX";
2455 case MONO_RGCTX_INFO_METHOD_CONTEXT: return "METHOD_CONTEXT";
2456 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK: return "REMOTING_INVOKE_WITH_CHECK";
2457 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE: return "METHOD_DELEGATE_CODE";
2458 case MONO_RGCTX_INFO_CAST_CACHE: return "CAST_CACHE";
2459 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE: return "ARRAY_ELEMENT_SIZE";
2460 case MONO_RGCTX_INFO_VALUE_SIZE: return "VALUE_SIZE";
2461 case MONO_RGCTX_INFO_CLASS_BOX_TYPE: return "CLASS_BOX_TYPE";
2462 case MONO_RGCTX_INFO_CLASS_IS_REF_OR_CONTAINS_REFS: return "CLASS_IS_REF_OR_CONTAINS_REFS";
2463 case MONO_RGCTX_INFO_FIELD_OFFSET: return "FIELD_OFFSET";
2464 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE: return "METHOD_GSHAREDVT_OUT_TRAMPOLINE";
2465 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT: return "METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT";
2466 case MONO_RGCTX_INFO_SIG_GSHAREDVT_IN_TRAMPOLINE_CALLI: return "SIG_GSHAREDVT_IN_TRAMPOLINE_CALLI";
2467 case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI: return "SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI";
2468 case MONO_RGCTX_INFO_MEMCPY: return "MEMCPY";
2469 case MONO_RGCTX_INFO_BZERO: return "BZERO";
2470 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX: return "NULLABLE_CLASS_BOX";
2471 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: return "NULLABLE_CLASS_UNBOX";
2472 case MONO_RGCTX_INFO_VIRT_METHOD_CODE: return "VIRT_METHOD_CODE";
2473 case MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE: return "VIRT_METHOD_BOX_TYPE";
2474 case MONO_RGCTX_INFO_DELEGATE_TRAMP_INFO: return "DELEGATE_TRAMP_INFO";
2475 default:
2476 return "<UNKNOWN RGCTX INFO TYPE>";
2480 G_GNUC_UNUSED static char*
2481 rgctx_info_to_str (MonoRgctxInfoType info_type, gpointer data)
2483 switch (info_type) {
2484 case MONO_RGCTX_INFO_VTABLE:
2485 return mono_type_full_name ((MonoType*)data);
2486 default:
2487 return g_strdup_printf ("<%p>", data);
2492 * LOCKING: loader lock
2494 static int
2495 register_info (MonoClass *klass, int type_argc, gpointer data, MonoRgctxInfoType info_type)
2497 int i;
2498 MonoRuntimeGenericContextTemplate *template_ = mono_class_get_runtime_generic_context_template (klass);
2499 MonoClass *parent;
2500 MonoRuntimeGenericContextInfoTemplate *oti;
2502 for (i = 0, oti = get_info_templates (template_, type_argc); oti; ++i, oti = oti->next) {
2503 if (!oti->data)
2504 break;
2507 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)));
2509 /* Mark the slot as used in all parent classes (until we find
2510 a parent class which already has it marked used). */
2511 parent = m_class_get_parent (klass);
2512 while (parent != NULL) {
2513 MonoRuntimeGenericContextTemplate *parent_template;
2514 MonoRuntimeGenericContextInfoTemplate *oti;
2516 if (mono_class_is_ginst (parent))
2517 parent = mono_class_get_generic_class (parent)->container_class;
2519 parent_template = mono_class_get_runtime_generic_context_template (parent);
2520 oti = rgctx_template_get_other_slot (parent_template, type_argc, i);
2522 if (oti && oti->data)
2523 break;
2525 rgctx_template_set_slot (m_class_get_image (parent), parent_template, type_argc, i,
2526 MONO_RGCTX_SLOT_USED_MARKER, (MonoRgctxInfoType)0);
2528 parent = m_class_get_parent (parent);
2531 /* Fill in the slot in this class and in all subclasses
2532 recursively. */
2533 fill_in_rgctx_template_slot (klass, type_argc, i, data, info_type);
2535 return i;
2538 static gboolean
2539 info_equal (gpointer data1, gpointer data2, MonoRgctxInfoType info_type)
2541 switch (info_type) {
2542 case MONO_RGCTX_INFO_STATIC_DATA:
2543 case MONO_RGCTX_INFO_KLASS:
2544 case MONO_RGCTX_INFO_ELEMENT_KLASS:
2545 case MONO_RGCTX_INFO_VTABLE:
2546 case MONO_RGCTX_INFO_TYPE:
2547 case MONO_RGCTX_INFO_REFLECTION_TYPE:
2548 case MONO_RGCTX_INFO_CAST_CACHE:
2549 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
2550 case MONO_RGCTX_INFO_VALUE_SIZE:
2551 case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
2552 case MONO_RGCTX_INFO_CLASS_IS_REF_OR_CONTAINS_REFS:
2553 case MONO_RGCTX_INFO_MEMCPY:
2554 case MONO_RGCTX_INFO_BZERO:
2555 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
2556 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX:
2557 return mono_class_from_mono_type_internal ((MonoType *)data1) == mono_class_from_mono_type_internal ((MonoType *)data2);
2558 case MONO_RGCTX_INFO_METHOD:
2559 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO:
2560 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
2561 case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER:
2562 case MONO_RGCTX_INFO_CLASS_FIELD:
2563 case MONO_RGCTX_INFO_FIELD_OFFSET:
2564 case MONO_RGCTX_INFO_METHOD_RGCTX:
2565 case MONO_RGCTX_INFO_METHOD_CONTEXT:
2566 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
2567 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE:
2568 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE:
2569 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT:
2570 case MONO_RGCTX_INFO_SIG_GSHAREDVT_IN_TRAMPOLINE_CALLI:
2571 case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI:
2572 return data1 == data2;
2573 case MONO_RGCTX_INFO_VIRT_METHOD_CODE:
2574 case MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE: {
2575 MonoJumpInfoVirtMethod *info1 = (MonoJumpInfoVirtMethod *)data1;
2576 MonoJumpInfoVirtMethod *info2 = (MonoJumpInfoVirtMethod *)data2;
2578 return info1->klass == info2->klass && info1->method == info2->method;
2580 case MONO_RGCTX_INFO_DELEGATE_TRAMP_INFO: {
2581 MonoDelegateClassMethodPair *dele1 = (MonoDelegateClassMethodPair *)data1;
2582 MonoDelegateClassMethodPair *dele2 = (MonoDelegateClassMethodPair *)data2;
2584 return dele1->is_virtual == dele2->is_virtual && dele1->method == dele2->method && dele1->klass == dele2->klass;
2586 default:
2587 g_assert_not_reached ();
2589 /* never reached */
2590 return FALSE;
2594 * mini_rgctx_info_type_to_patch_info_type:
2596 * Return the type of the runtime object referred to by INFO_TYPE.
2598 MonoJumpInfoType
2599 mini_rgctx_info_type_to_patch_info_type (MonoRgctxInfoType info_type)
2601 switch (info_type) {
2602 case MONO_RGCTX_INFO_STATIC_DATA:
2603 case MONO_RGCTX_INFO_KLASS:
2604 case MONO_RGCTX_INFO_ELEMENT_KLASS:
2605 case MONO_RGCTX_INFO_VTABLE:
2606 case MONO_RGCTX_INFO_TYPE:
2607 case MONO_RGCTX_INFO_REFLECTION_TYPE:
2608 case MONO_RGCTX_INFO_CAST_CACHE:
2609 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
2610 case MONO_RGCTX_INFO_VALUE_SIZE:
2611 case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
2612 case MONO_RGCTX_INFO_CLASS_IS_REF_OR_CONTAINS_REFS:
2613 case MONO_RGCTX_INFO_MEMCPY:
2614 case MONO_RGCTX_INFO_BZERO:
2615 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
2616 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX:
2617 case MONO_RGCTX_INFO_LOCAL_OFFSET:
2618 return MONO_PATCH_INFO_CLASS;
2619 case MONO_RGCTX_INFO_FIELD_OFFSET:
2620 return MONO_PATCH_INFO_FIELD;
2621 default:
2622 g_assert_not_reached ();
2623 return (MonoJumpInfoType)-1;
2627 static int
2628 lookup_or_register_info (MonoClass *klass, int type_argc, gpointer data, MonoRgctxInfoType info_type,
2629 MonoGenericContext *generic_context)
2631 MonoRuntimeGenericContextTemplate *rgctx_template =
2632 mono_class_get_runtime_generic_context_template (klass);
2633 MonoRuntimeGenericContextInfoTemplate *oti_list, *oti;
2634 int i;
2636 klass = get_shared_class (klass);
2638 mono_loader_lock ();
2640 if (info_has_identity (info_type)) {
2641 oti_list = get_info_templates (rgctx_template, type_argc);
2643 for (oti = oti_list, i = 0; oti; oti = oti->next, ++i) {
2644 gpointer inflated_data;
2646 if (oti->info_type != info_type || !oti->data)
2647 continue;
2649 inflated_data = inflate_info (oti, generic_context, klass, TRUE);
2651 if (info_equal (data, inflated_data, info_type)) {
2652 free_inflated_info (info_type, inflated_data);
2653 mono_loader_unlock ();
2654 return i;
2656 free_inflated_info (info_type, inflated_data);
2660 /* We haven't found the info */
2661 i = register_info (klass, type_argc, data, info_type);
2663 /* interlocked by loader lock */
2664 if (i > UnlockedRead (&rgctx_max_slot_number))
2665 UnlockedWrite (&rgctx_max_slot_number, i);
2667 mono_loader_unlock ();
2669 return i;
2673 * mono_method_lookup_or_register_info:
2674 * @method: a method
2675 * @in_mrgctx: whether to put the data into the MRGCTX
2676 * @data: the info data
2677 * @info_type: the type of info to register about data
2678 * @generic_context: a generic context
2680 * Looks up and, if necessary, adds information about data/info_type in
2681 * method's or method's class runtime generic context. Returns the
2682 * encoded slot number.
2684 guint32
2685 mono_method_lookup_or_register_info (MonoMethod *method, gboolean in_mrgctx, gpointer data,
2686 MonoRgctxInfoType info_type, MonoGenericContext *generic_context)
2688 MonoClass *klass = method->klass;
2689 int type_argc = 0, index;
2691 if (in_mrgctx) {
2692 MonoGenericInst *method_inst = mono_method_get_context (method)->method_inst;
2694 if (method_inst) {
2695 g_assert (method->is_inflated && method_inst);
2696 type_argc = method_inst->type_argc;
2697 g_assert (type_argc > 0);
2701 index = lookup_or_register_info (klass, type_argc, data, info_type, generic_context);
2703 //g_print ("rgctx item at index %d argc %d\n", index, type_argc);
2705 if (in_mrgctx)
2706 return MONO_RGCTX_SLOT_MAKE_MRGCTX (index);
2707 else
2708 return MONO_RGCTX_SLOT_MAKE_RGCTX (index);
2712 * mono_class_rgctx_get_array_size:
2713 * @n: The number of the array
2714 * @mrgctx: Whether it's an MRGCTX as opposed to a RGCTX.
2716 * Returns the number of slots in the n'th array of a (M)RGCTX. That
2717 * number includes the slot for linking and - for MRGCTXs - the two
2718 * slots in the first array for additional information.
2721 mono_class_rgctx_get_array_size (int n, gboolean mrgctx)
2723 g_assert (n >= 0 && n < 30);
2725 if (mrgctx)
2726 return 6 << n;
2727 else
2728 return 4 << n;
2732 * LOCKING: domain lock
2734 static gpointer*
2735 alloc_rgctx_array (MonoDomain *domain, int n, gboolean is_mrgctx)
2737 gint32 size = mono_class_rgctx_get_array_size (n, is_mrgctx) * sizeof (gpointer);
2738 gpointer *array = (gpointer *)mono_domain_alloc0 (domain, size);
2740 /* interlocked by domain lock (by definition) */
2741 if (is_mrgctx) {
2742 UnlockedIncrement (&mrgctx_num_arrays_allocated);
2743 UnlockedAdd (&mrgctx_bytes_allocated, size);
2744 } else {
2745 UnlockedIncrement (&rgctx_num_arrays_allocated);
2746 UnlockedAdd (&rgctx_bytes_allocated, size);
2749 return array;
2752 static gpointer
2753 fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContext *rgctx, guint32 slot,
2754 MonoGenericInst *method_inst, gboolean is_mrgctx, MonoError *error)
2756 gpointer info;
2757 int i, first_slot, size;
2758 MonoDomain *domain = class_vtable->domain;
2759 MonoClass *klass = class_vtable->klass;
2760 MonoGenericContext *class_context = mono_class_is_ginst (klass) ? &mono_class_get_generic_class (klass)->context : NULL;
2761 MonoRuntimeGenericContextInfoTemplate oti;
2762 MonoGenericContext context = { class_context ? class_context->class_inst : NULL, method_inst };
2763 int rgctx_index;
2764 gboolean do_free;
2766 error_init (error);
2768 g_assert (rgctx);
2770 mono_domain_lock (domain);
2772 /* First check whether that slot isn't already instantiated.
2773 This might happen because lookup doesn't lock. Allocate
2774 arrays on the way. */
2775 first_slot = 0;
2776 size = mono_class_rgctx_get_array_size (0, is_mrgctx);
2777 if (is_mrgctx)
2778 size -= MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
2779 for (i = 0; ; ++i) {
2780 int offset;
2782 if (is_mrgctx && i == 0)
2783 offset = MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
2784 else
2785 offset = 0;
2787 if (slot < first_slot + size - 1) {
2788 rgctx_index = slot - first_slot + 1 + offset;
2789 info = (MonoRuntimeGenericContext*)rgctx [rgctx_index];
2790 if (info) {
2791 mono_domain_unlock (domain);
2792 return info;
2794 break;
2796 if (!rgctx [offset + 0])
2797 rgctx [offset + 0] = alloc_rgctx_array (domain, i + 1, is_mrgctx);
2798 rgctx = (void **)rgctx [offset + 0];
2799 first_slot += size - 1;
2800 size = mono_class_rgctx_get_array_size (i + 1, is_mrgctx);
2803 g_assert (!rgctx [rgctx_index]);
2805 mono_domain_unlock (domain);
2807 oti = class_get_rgctx_template_oti (get_shared_class (klass),
2808 method_inst ? method_inst->type_argc : 0, slot, TRUE, TRUE, &do_free);
2809 /* This might take the loader lock */
2810 info = (MonoRuntimeGenericContext*)instantiate_info (domain, &oti, &context, klass, error);
2811 return_val_if_nok (error, NULL);
2812 g_assert (info);
2815 if (method_inst)
2816 g_print ("filling mrgctx slot %d table %d index %d\n", slot, i, rgctx_index);
2819 /*FIXME We should use CAS here, no need to take a lock.*/
2820 mono_domain_lock (domain);
2822 /* Check whether the slot hasn't been instantiated in the
2823 meantime. */
2824 if (rgctx [rgctx_index])
2825 info = (MonoRuntimeGenericContext*)rgctx [rgctx_index];
2826 else
2827 rgctx [rgctx_index] = info;
2829 mono_domain_unlock (domain);
2831 if (do_free)
2832 free_inflated_info (oti.info_type, oti.data);
2834 return info;
2838 * mono_class_fill_runtime_generic_context:
2839 * @class_vtable: a vtable
2840 * @slot: a slot index to be instantiated
2842 * Instantiates a slot in the RGCTX, returning its value.
2844 gpointer
2845 mono_class_fill_runtime_generic_context (MonoVTable *class_vtable, guint32 slot, MonoError *error)
2847 MonoDomain *domain = class_vtable->domain;
2848 MonoRuntimeGenericContext *rgctx;
2849 gpointer info;
2851 error_init (error);
2853 mono_domain_lock (domain);
2855 rgctx = class_vtable->runtime_generic_context;
2856 if (!rgctx) {
2857 rgctx = alloc_rgctx_array (domain, 0, FALSE);
2858 class_vtable->runtime_generic_context = rgctx;
2859 UnlockedIncrement (&rgctx_num_allocated); /* interlocked by domain lock */
2862 mono_domain_unlock (domain);
2864 info = fill_runtime_generic_context (class_vtable, rgctx, slot, NULL, FALSE, error);
2866 DEBUG (printf ("get rgctx slot: %s %d -> %p\n", mono_type_full_name (m_class_get_byval_arg (class_vtable->klass)), slot, info));
2868 return info;
2872 * mono_method_fill_runtime_generic_context:
2873 * @mrgctx: an MRGCTX
2874 * @slot: a slot index to be instantiated
2876 * Instantiates a slot in the MRGCTX.
2878 gpointer
2879 mono_method_fill_runtime_generic_context (MonoMethodRuntimeGenericContext *mrgctx, guint32 slot, MonoError *error)
2881 gpointer info;
2883 info = fill_runtime_generic_context (mrgctx->class_vtable, (MonoRuntimeGenericContext*)mrgctx, slot, mrgctx->method_inst, TRUE, error);
2885 return info;
2888 static guint
2889 mrgctx_hash_func (gconstpointer key)
2891 const MonoMethodRuntimeGenericContext *mrgctx = (const MonoMethodRuntimeGenericContext *)key;
2893 return mono_aligned_addr_hash (mrgctx->class_vtable) ^ mono_metadata_generic_inst_hash (mrgctx->method_inst);
2896 static gboolean
2897 mrgctx_equal_func (gconstpointer a, gconstpointer b)
2899 const MonoMethodRuntimeGenericContext *mrgctx1 = (const MonoMethodRuntimeGenericContext *)a;
2900 const MonoMethodRuntimeGenericContext *mrgctx2 = (const MonoMethodRuntimeGenericContext *)b;
2902 return mrgctx1->class_vtable == mrgctx2->class_vtable &&
2903 mono_metadata_generic_inst_equal (mrgctx1->method_inst, mrgctx2->method_inst);
2907 * mini_method_get_mrgctx:
2908 * @class_vtable: a vtable
2909 * @method: an inflated method
2911 * Returns the MRGCTX for METHOD.
2913 * LOCKING: Take the domain lock.
2915 static MonoMethodRuntimeGenericContext*
2916 mini_method_get_mrgctx (MonoVTable *class_vtable, MonoMethod *method)
2918 MonoDomain *domain = class_vtable->domain;
2919 MonoMethodRuntimeGenericContext *mrgctx;
2920 MonoMethodRuntimeGenericContext key;
2921 MonoGenericInst *method_inst = mini_method_get_context (method)->method_inst;
2922 MonoJitDomainInfo *domain_info = domain_jit_info (domain);
2924 g_assert (!mono_class_is_gtd (class_vtable->klass));
2926 mono_domain_lock (domain);
2928 if (!method_inst) {
2929 g_assert (mini_method_is_default_method (method));
2931 if (!domain_info->mrgctx_hash)
2932 domain_info->mrgctx_hash = g_hash_table_new (NULL, NULL);
2933 mrgctx = (MonoMethodRuntimeGenericContext*)g_hash_table_lookup (domain_info->mrgctx_hash, method);
2934 } else {
2935 g_assert (!method_inst->is_open);
2937 if (!domain_info->method_rgctx_hash)
2938 domain_info->method_rgctx_hash = g_hash_table_new (mrgctx_hash_func, mrgctx_equal_func);
2940 key.class_vtable = class_vtable;
2941 key.method_inst = method_inst;
2943 mrgctx = (MonoMethodRuntimeGenericContext *)g_hash_table_lookup (domain_info->method_rgctx_hash, &key);
2946 if (!mrgctx) {
2947 //int i;
2949 mrgctx = (MonoMethodRuntimeGenericContext*)alloc_rgctx_array (domain, 0, TRUE);
2950 mrgctx->class_vtable = class_vtable;
2951 mrgctx->method_inst = method_inst;
2953 if (!method_inst)
2954 g_hash_table_insert (domain_info->mrgctx_hash, method, mrgctx);
2955 else
2956 g_hash_table_insert (domain_info->method_rgctx_hash, mrgctx, mrgctx);
2959 g_print ("mrgctx alloced for %s <", mono_type_get_full_name (class_vtable->klass));
2960 for (i = 0; i < method_inst->type_argc; ++i)
2961 g_print ("%s, ", mono_type_full_name (method_inst->type_argv [i]));
2962 g_print (">\n");
2966 mono_domain_unlock (domain);
2968 g_assert (mrgctx);
2970 return mrgctx;
2973 static gboolean
2974 type_is_sharable (MonoType *type, gboolean allow_type_vars, gboolean allow_partial)
2976 if (allow_type_vars && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR)) {
2977 MonoType *constraint = type->data.generic_param->gshared_constraint;
2978 if (!constraint)
2979 return TRUE;
2980 type = constraint;
2983 if (MONO_TYPE_IS_REFERENCE (type))
2984 return TRUE;
2986 /* Allow non ref arguments if they are primitive types or enums (partial sharing). */
2987 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 && m_class_is_enumtype (type->data.klass))))
2988 return TRUE;
2990 if (allow_partial && !type->byref && type->type == MONO_TYPE_GENERICINST && MONO_TYPE_ISSTRUCT (type)) {
2991 MonoGenericClass *gclass = type->data.generic_class;
2993 if (gclass->context.class_inst && !mini_generic_inst_is_sharable (gclass->context.class_inst, allow_type_vars, allow_partial))
2994 return FALSE;
2995 if (gclass->context.method_inst && !mini_generic_inst_is_sharable (gclass->context.method_inst, allow_type_vars, allow_partial))
2996 return FALSE;
2997 if (mono_class_is_nullable (mono_class_from_mono_type_internal (type)))
2998 return FALSE;
2999 return TRUE;
3002 return FALSE;
3005 gboolean
3006 mini_generic_inst_is_sharable (MonoGenericInst *inst, gboolean allow_type_vars,
3007 gboolean allow_partial)
3009 int i;
3011 for (i = 0; i < inst->type_argc; ++i) {
3012 if (!type_is_sharable (inst->type_argv [i], allow_type_vars, allow_partial))
3013 return FALSE;
3016 return TRUE;
3020 * mono_is_partially_sharable_inst:
3022 * Return TRUE if INST has ref and non-ref type arguments.
3024 gboolean
3025 mono_is_partially_sharable_inst (MonoGenericInst *inst)
3027 int i;
3028 gboolean has_refs = FALSE, has_non_refs = FALSE;
3030 for (i = 0; i < inst->type_argc; ++i) {
3031 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)
3032 has_refs = TRUE;
3033 else
3034 has_non_refs = TRUE;
3037 return has_refs && has_non_refs;
3041 * mono_generic_context_is_sharable_full:
3042 * @context: a generic context
3044 * Returns whether the generic context is sharable. A generic context
3045 * is sharable iff all of its type arguments are reference type, or some of them have a
3046 * reference type, and ALLOW_PARTIAL is TRUE.
3048 gboolean
3049 mono_generic_context_is_sharable_full (MonoGenericContext *context,
3050 gboolean allow_type_vars,
3051 gboolean allow_partial)
3053 g_assert (context->class_inst || context->method_inst);
3055 if (context->class_inst && !mini_generic_inst_is_sharable (context->class_inst, allow_type_vars, allow_partial))
3056 return FALSE;
3058 if (context->method_inst && !mini_generic_inst_is_sharable (context->method_inst, allow_type_vars, allow_partial))
3059 return FALSE;
3061 return TRUE;
3064 gboolean
3065 mono_generic_context_is_sharable (MonoGenericContext *context, gboolean allow_type_vars)
3067 return mono_generic_context_is_sharable_full (context, allow_type_vars, partial_sharing_supported ());
3071 * mono_method_is_generic_impl:
3072 * @method: a method
3074 * Returns whether the method is either generic or part of a generic
3075 * class.
3077 gboolean
3078 mono_method_is_generic_impl (MonoMethod *method)
3080 if (method->is_inflated)
3081 return TRUE;
3082 /* We don't treat wrappers as generic code, i.e., we never
3083 apply generic sharing to them. This is especially
3084 important for static rgctx invoke wrappers, which only work
3085 if not compiled with sharing. */
3086 if (method->wrapper_type != MONO_WRAPPER_NONE)
3087 return FALSE;
3088 if (mono_class_is_gtd (method->klass))
3089 return TRUE;
3090 return FALSE;
3093 static gboolean
3094 has_constraints (MonoGenericContainer *container)
3096 //int i;
3098 return FALSE;
3100 g_assert (container->type_argc > 0);
3101 g_assert (container->type_params);
3103 for (i = 0; i < container->type_argc; ++i)
3104 if (container->type_params [i].constraints)
3105 return TRUE;
3106 return FALSE;
3110 static gboolean
3111 mini_method_is_open (MonoMethod *method)
3113 if (method->is_inflated) {
3114 MonoGenericContext *ctx = mono_method_get_context (method);
3116 if (ctx->class_inst && ctx->class_inst->is_open)
3117 return TRUE;
3118 if (ctx->method_inst && ctx->method_inst->is_open)
3119 return TRUE;
3121 return FALSE;
3124 /* Lazy class loading functions */
3125 static GENERATE_TRY_GET_CLASS_WITH_CACHE (iasync_state_machine, "System.Runtime.CompilerServices", "IAsyncStateMachine")
3127 static G_GNUC_UNUSED gboolean
3128 is_async_state_machine_class (MonoClass *klass)
3130 MonoClass *iclass;
3132 return FALSE;
3134 iclass = mono_class_try_get_iasync_state_machine_class ();
3136 if (iclass && m_class_is_valuetype (klass) && mono_class_is_assignable_from_internal (iclass, klass))
3137 return TRUE;
3138 return FALSE;
3141 static G_GNUC_UNUSED gboolean
3142 is_async_method (MonoMethod *method)
3144 ERROR_DECL (error);
3145 MonoCustomAttrInfo *cattr;
3146 MonoMethodSignature *sig;
3147 gboolean res = FALSE;
3148 MonoClass *attr_class;
3150 return FALSE;
3152 attr_class = mono_class_try_get_iasync_state_machine_class ();
3154 /* Do less expensive checks first */
3155 sig = mono_method_signature_internal (method);
3156 if (attr_class && sig && ((sig->ret->type == MONO_TYPE_VOID) ||
3157 (sig->ret->type == MONO_TYPE_CLASS && !strcmp (m_class_get_name (sig->ret->data.generic_class->container_class), "Task")) ||
3158 (sig->ret->type == MONO_TYPE_GENERICINST && !strcmp (m_class_get_name (sig->ret->data.generic_class->container_class), "Task`1")))) {
3159 //printf ("X: %s\n", mono_method_full_name (method, TRUE));
3160 cattr = mono_custom_attrs_from_method_checked (method, error);
3161 if (!is_ok (error)) {
3162 mono_error_cleanup (error); /* FIXME don't swallow the error? */
3163 return FALSE;
3165 if (cattr) {
3166 if (mono_custom_attrs_has_attr (cattr, attr_class))
3167 res = TRUE;
3168 mono_custom_attrs_free (cattr);
3171 return res;
3175 * mono_method_is_generic_sharable_full:
3176 * @method: a method
3177 * @allow_type_vars: whether to regard type variables as reference types
3178 * @allow_partial: whether to allow partial sharing
3179 * @allow_gsharedvt: whenever to allow sharing over valuetypes
3181 * Returns TRUE iff the method is inflated or part of an inflated
3182 * class, its context is sharable and it has no constraints on its
3183 * type parameters. Otherwise returns FALSE.
3185 gboolean
3186 mono_method_is_generic_sharable_full (MonoMethod *method, gboolean allow_type_vars,
3187 gboolean allow_partial, gboolean allow_gsharedvt)
3189 if (!mono_method_is_generic_impl (method))
3190 return FALSE;
3193 if (!mono_debug_count ())
3194 allow_partial = FALSE;
3197 if (!partial_sharing_supported ())
3198 allow_partial = FALSE;
3200 if (mono_class_is_nullable (method->klass))
3201 // FIXME:
3202 allow_partial = FALSE;
3204 if (m_class_get_image (method->klass)->dynamic)
3206 * Enabling this causes corlib test failures because the JIT encounters generic instances whose
3207 * instance_size is 0.
3209 allow_partial = FALSE;
3212 * Generic async methods have an associated state machine class which is a generic struct. This struct
3213 * is too large to be handled by gsharedvt so we make it visible to the AOT compiler by disabling sharing
3214 * of the async method and the state machine class.
3216 if (is_async_state_machine_class (method->klass))
3217 return FALSE;
3219 if (allow_gsharedvt && mini_is_gsharedvt_sharable_method (method)) {
3220 if (is_async_method (method))
3221 return FALSE;
3222 return TRUE;
3225 if (method->is_inflated) {
3226 MonoMethodInflated *inflated = (MonoMethodInflated*)method;
3227 MonoGenericContext *context = &inflated->context;
3229 if (!mono_generic_context_is_sharable_full (context, allow_type_vars, allow_partial))
3230 return FALSE;
3232 g_assert (inflated->declaring);
3234 if (inflated->declaring->is_generic) {
3235 if (has_constraints (mono_method_get_generic_container (inflated->declaring)))
3236 return FALSE;
3240 if (mono_class_is_ginst (method->klass)) {
3241 if (!mono_generic_context_is_sharable_full (&mono_class_get_generic_class (method->klass)->context, allow_type_vars, allow_partial))
3242 return FALSE;
3244 g_assert (mono_class_get_generic_class (method->klass)->container_class &&
3245 mono_class_is_gtd (mono_class_get_generic_class (method->klass)->container_class));
3247 if (has_constraints (mono_class_get_generic_container (mono_class_get_generic_class (method->klass)->container_class)))
3248 return FALSE;
3251 if (mono_class_is_gtd (method->klass) && !allow_type_vars)
3252 return FALSE;
3254 /* This does potentially expensive cattr checks, so do it at the end */
3255 if (is_async_method (method)) {
3256 if (mini_method_is_open (method))
3257 /* The JIT can't compile these without sharing */
3258 return TRUE;
3259 return FALSE;
3262 return TRUE;
3265 gboolean
3266 mono_method_is_generic_sharable (MonoMethod *method, gboolean allow_type_vars)
3268 return mono_method_is_generic_sharable_full (method, allow_type_vars, partial_sharing_supported (), TRUE);
3272 * mono_method_needs_static_rgctx_invoke:
3274 * Return whenever METHOD needs an rgctx argument.
3275 * An rgctx argument is needed when the method is generic sharable, but it doesn't
3276 * have a this argument which can be used to load the rgctx.
3278 gboolean
3279 mono_method_needs_static_rgctx_invoke (MonoMethod *method, gboolean allow_type_vars)
3281 if (!mono_class_generic_sharing_enabled (method->klass))
3282 return FALSE;
3284 if (!mono_method_is_generic_sharable (method, allow_type_vars))
3285 return FALSE;
3287 if (method->is_inflated && mono_method_get_context (method)->method_inst)
3288 return TRUE;
3290 return ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
3291 m_class_is_valuetype (method->klass) ||
3292 mini_method_is_default_method (method)) &&
3293 (mono_class_is_ginst (method->klass) || mono_class_is_gtd (method->klass));
3296 static MonoGenericInst*
3297 get_object_generic_inst (int type_argc)
3299 MonoType **type_argv;
3300 int i;
3302 type_argv = g_newa (MonoType*, type_argc);
3304 MonoType *object_type = mono_get_object_type ();
3305 for (i = 0; i < type_argc; ++i)
3306 type_argv [i] = object_type;
3308 return mono_metadata_get_generic_inst (type_argc, type_argv);
3312 * mono_method_construct_object_context:
3313 * @method: a method
3315 * Returns a generic context for method with all type variables for
3316 * class and method instantiated with Object.
3318 MonoGenericContext
3319 mono_method_construct_object_context (MonoMethod *method)
3321 MonoGenericContext object_context;
3323 g_assert (!mono_class_is_ginst (method->klass));
3324 if (mono_class_is_gtd (method->klass)) {
3325 int type_argc = mono_class_get_generic_container (method->klass)->type_argc;
3327 object_context.class_inst = get_object_generic_inst (type_argc);
3328 } else {
3329 object_context.class_inst = NULL;
3332 if (mono_method_get_context_general (method, TRUE)->method_inst) {
3333 int type_argc = mono_method_get_context_general (method, TRUE)->method_inst->type_argc;
3335 object_context.method_inst = get_object_generic_inst (type_argc);
3336 } else {
3337 object_context.method_inst = NULL;
3340 g_assert (object_context.class_inst || object_context.method_inst);
3342 return object_context;
3345 static gboolean gshared_supported;
3347 void
3348 mono_set_generic_sharing_supported (gboolean supported)
3350 gshared_supported = supported;
3354 void
3355 mono_set_partial_sharing_supported (gboolean supported)
3357 partial_supported = supported;
3361 * mono_class_generic_sharing_enabled:
3362 * @class: a class
3364 * Returns whether generic sharing is enabled for class.
3366 * This is a stop-gap measure to slowly introduce generic sharing
3367 * until we have all the issues sorted out, at which time this
3368 * function will disappear and generic sharing will always be enabled.
3370 gboolean
3371 mono_class_generic_sharing_enabled (MonoClass *klass)
3373 if (gshared_supported)
3374 return TRUE;
3375 else
3376 return FALSE;
3379 MonoGenericContext*
3380 mini_method_get_context (MonoMethod *method)
3382 return mono_method_get_context_general (method, TRUE);
3386 * mono_method_check_context_used:
3387 * @method: a method
3389 * Checks whether the method's generic context uses a type variable.
3390 * Returns an int with the bits MONO_GENERIC_CONTEXT_USED_CLASS and
3391 * MONO_GENERIC_CONTEXT_USED_METHOD set to reflect whether the
3392 * context's class or method instantiation uses type variables.
3395 mono_method_check_context_used (MonoMethod *method)
3397 MonoGenericContext *method_context = mini_method_get_context (method);
3398 int context_used = 0;
3400 if (!method_context) {
3401 /* It might be a method of an array of an open generic type */
3402 if (m_class_get_rank (method->klass))
3403 context_used = mono_class_check_context_used (method->klass);
3404 } else {
3405 context_used = mono_generic_context_check_used (method_context);
3406 context_used |= mono_class_check_context_used (method->klass);
3409 return context_used;
3412 static gboolean
3413 generic_inst_equal (MonoGenericInst *inst1, MonoGenericInst *inst2)
3415 int i;
3417 if (!inst1) {
3418 g_assert (!inst2);
3419 return TRUE;
3422 g_assert (inst2);
3424 if (inst1->type_argc != inst2->type_argc)
3425 return FALSE;
3427 for (i = 0; i < inst1->type_argc; ++i)
3428 if (!mono_metadata_type_equal (inst1->type_argv [i], inst2->type_argv [i]))
3429 return FALSE;
3431 return TRUE;
3435 * mono_generic_context_equal_deep:
3436 * @context1: a generic context
3437 * @context2: a generic context
3439 * Returns whether context1's type arguments are equal to context2's
3440 * type arguments.
3442 gboolean
3443 mono_generic_context_equal_deep (MonoGenericContext *context1, MonoGenericContext *context2)
3445 return generic_inst_equal (context1->class_inst, context2->class_inst) &&
3446 generic_inst_equal (context1->method_inst, context2->method_inst);
3450 * mini_class_get_container_class:
3451 * @class: a generic class
3453 * Returns the class's container class, which is the class itself if
3454 * it doesn't have generic_class set.
3456 MonoClass*
3457 mini_class_get_container_class (MonoClass *klass)
3459 if (mono_class_is_ginst (klass))
3460 return mono_class_get_generic_class (klass)->container_class;
3462 g_assert (mono_class_is_gtd (klass));
3463 return klass;
3467 * mini_class_get_context:
3468 * @class: a generic class
3470 * Returns the class's generic context.
3472 MonoGenericContext*
3473 mini_class_get_context (MonoClass *klass)
3475 if (mono_class_is_ginst (klass))
3476 return &mono_class_get_generic_class (klass)->context;
3478 g_assert (mono_class_is_gtd (klass));
3479 return &mono_class_get_generic_container (klass)->context;
3483 * mini_get_basic_type_from_generic:
3484 * @type: a type
3486 * Returns a closed type corresponding to the possibly open type
3487 * passed to it.
3489 static MonoType*
3490 mini_get_basic_type_from_generic (MonoType *type)
3492 if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) && mini_is_gsharedvt_type (type))
3493 return type;
3494 else if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR)) {
3495 MonoType *constraint = type->data.generic_param->gshared_constraint;
3496 /* The gparam constraint encodes the type this gparam can represent */
3497 if (!constraint) {
3498 return mono_get_object_type ();
3499 } else {
3500 MonoClass *klass;
3502 g_assert (constraint != m_class_get_byval_arg (m_class_get_parent (mono_defaults.int_class)));
3503 klass = mono_class_from_mono_type_internal (constraint);
3504 return m_class_get_byval_arg (klass);
3506 } else {
3507 return mini_native_type_replace_type (mono_type_get_basic_type_from_generic (type));
3512 * mini_type_get_underlying_type:
3514 * Return the underlying type of TYPE, taking into account enums, byref, bool, char, ref types and generic
3515 * sharing.
3517 MonoType*
3518 mini_type_get_underlying_type (MonoType *type)
3520 type = mini_native_type_replace_type (type);
3522 if (type->byref)
3523 return mono_get_int_type ();
3524 if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) && mini_is_gsharedvt_type (type))
3525 return type;
3526 type = mini_get_basic_type_from_generic (mono_type_get_underlying_type (type));
3527 switch (type->type) {
3528 case MONO_TYPE_BOOLEAN:
3529 return m_class_get_byval_arg (mono_defaults.byte_class);
3530 case MONO_TYPE_CHAR:
3531 return m_class_get_byval_arg (mono_defaults.uint16_class);
3532 case MONO_TYPE_STRING:
3533 case MONO_TYPE_CLASS:
3534 case MONO_TYPE_ARRAY:
3535 case MONO_TYPE_SZARRAY:
3536 return mono_get_object_type ();
3537 default:
3538 return type;
3543 * mini_type_stack_size:
3544 * @t: a type
3545 * @align: Pointer to an int for returning the alignment
3547 * Returns the type's stack size and the alignment in *align.
3550 mini_type_stack_size (MonoType *t, int *align)
3552 return mono_type_stack_size_internal (t, align, TRUE);
3556 * mini_type_stack_size_full:
3558 * Same as mini_type_stack_size, but handle pinvoke data types as well.
3561 mini_type_stack_size_full (MonoType *t, guint32 *align, gboolean pinvoke)
3563 int size;
3565 //g_assert (!mini_is_gsharedvt_type (t));
3567 if (pinvoke) {
3568 size = mono_type_native_stack_size (t, align);
3569 } else {
3570 int ialign;
3572 if (align) {
3573 size = mini_type_stack_size (t, &ialign);
3574 *align = ialign;
3575 } else {
3576 size = mini_type_stack_size (t, NULL);
3580 return size;
3584 * mono_generic_sharing_init:
3586 * Initialize the module.
3588 void
3589 mono_generic_sharing_init (void)
3591 mono_counters_register ("RGCTX template num allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_template_num_allocated);
3592 mono_counters_register ("RGCTX template bytes allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_template_bytes_allocated);
3593 mono_counters_register ("RGCTX oti num allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_oti_num_allocated);
3594 mono_counters_register ("RGCTX oti bytes allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_oti_bytes_allocated);
3595 mono_counters_register ("RGCTX oti num markers", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_oti_num_markers);
3596 mono_counters_register ("RGCTX oti num data", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_oti_num_data);
3597 mono_counters_register ("RGCTX max slot number", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_max_slot_number);
3598 mono_counters_register ("RGCTX num allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_num_allocated);
3599 mono_counters_register ("RGCTX num arrays allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_num_arrays_allocated);
3600 mono_counters_register ("RGCTX bytes allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_bytes_allocated);
3601 mono_counters_register ("MRGCTX num arrays allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_num_arrays_allocated);
3602 mono_counters_register ("MRGCTX bytes allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_bytes_allocated);
3603 mono_counters_register ("GSHAREDVT num trampolines", MONO_COUNTER_JIT | MONO_COUNTER_INT, &gsharedvt_num_trampolines);
3605 mono_install_image_unload_hook (mono_class_unregister_image_generic_subclasses, NULL);
3607 mono_os_mutex_init_recursive (&gshared_mutex);
3610 void
3611 mono_generic_sharing_cleanup (void)
3613 mono_remove_image_unload_hook (mono_class_unregister_image_generic_subclasses, NULL);
3615 g_hash_table_destroy (generic_subclass_hash);
3619 * mini_type_var_is_vt:
3621 * Return whenever T is a type variable instantiated with a vtype.
3623 gboolean
3624 mini_type_var_is_vt (MonoType *type)
3626 if (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) {
3627 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);
3628 } else {
3629 g_assert_not_reached ();
3630 return FALSE;
3634 gboolean
3635 mini_type_is_reference (MonoType *type)
3637 type = mini_type_get_underlying_type (type);
3638 return mono_type_is_reference (type);
3641 gboolean
3642 mini_method_is_default_method (MonoMethod *m)
3644 return MONO_CLASS_IS_INTERFACE_INTERNAL (m->klass) && !(m->flags & METHOD_ATTRIBUTE_ABSTRACT);
3647 gboolean
3648 mini_method_needs_mrgctx (MonoMethod *m)
3650 if (mono_class_is_ginst (m->klass) && mini_method_is_default_method (m))
3651 return TRUE;
3652 return (mini_method_get_context (m) && mini_method_get_context (m)->method_inst);
3656 * mini_method_get_rgctx:
3658 * Return the RGCTX which needs to be passed to M when it is called.
3660 gpointer
3661 mini_method_get_rgctx (MonoMethod *m)
3663 ERROR_DECL (error);
3664 MonoVTable *vt = mono_class_vtable_checked (mono_domain_get (), m->klass, error);
3665 mono_error_assert_ok (error);
3666 if (mini_method_needs_mrgctx (m))
3667 return mini_method_get_mrgctx (vt, m);
3668 else
3669 return vt;
3673 * mini_type_is_vtype:
3675 * Return whenever T is a vtype, or a type param instantiated with a vtype.
3676 * Should be used in place of MONO_TYPE_ISSTRUCT () which can't handle gsharedvt.
3678 gboolean
3679 mini_type_is_vtype (MonoType *t)
3681 t = mini_type_get_underlying_type (t);
3683 return MONO_TYPE_ISSTRUCT (t) || mini_is_gsharedvt_variable_type (t);
3686 gboolean
3687 mini_class_is_generic_sharable (MonoClass *klass)
3689 if (mono_class_is_ginst (klass) && is_async_state_machine_class (klass))
3690 return FALSE;
3692 return (mono_class_is_ginst (klass) && mono_generic_context_is_sharable (&mono_class_get_generic_class (klass)->context, FALSE));
3695 gboolean
3696 mini_is_gsharedvt_variable_klass (MonoClass *klass)
3698 return mini_is_gsharedvt_variable_type (m_class_get_byval_arg (klass));
3701 gboolean
3702 mini_is_gsharedvt_gparam (MonoType *t)
3704 /* Matches get_gsharedvt_type () */
3705 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;
3708 static char*
3709 get_shared_gparam_name (MonoTypeEnum constraint, const char *name)
3711 if (constraint == MONO_TYPE_VALUETYPE) {
3712 return g_strdup_printf ("%s_GSHAREDVT", name);
3713 } else if (constraint == MONO_TYPE_OBJECT) {
3714 return g_strdup_printf ("%s_REF", name);
3715 } else if (constraint == MONO_TYPE_GENERICINST) {
3716 return g_strdup_printf ("%s_INST", name);
3717 } else {
3718 MonoType t;
3719 char *tname, *tname2, *res;
3721 memset (&t, 0, sizeof (t));
3722 t.type = constraint;
3723 tname = mono_type_full_name (&t);
3724 tname2 = g_utf8_strup (tname, strlen (tname));
3725 res = g_strdup_printf ("%s_%s", name, tname2);
3726 g_free (tname);
3727 g_free (tname2);
3728 return res;
3732 static guint
3733 shared_gparam_hash (gconstpointer data)
3735 MonoGSharedGenericParam *p = (MonoGSharedGenericParam*)data;
3736 guint hash;
3738 hash = mono_metadata_generic_param_hash (p->parent);
3739 hash = ((hash << 5) - hash) ^ mono_metadata_type_hash (p->param.gshared_constraint);
3741 return hash;
3744 static gboolean
3745 shared_gparam_equal (gconstpointer ka, gconstpointer kb)
3747 MonoGSharedGenericParam *p1 = (MonoGSharedGenericParam*)ka;
3748 MonoGSharedGenericParam *p2 = (MonoGSharedGenericParam*)kb;
3750 if (p1 == p2)
3751 return TRUE;
3752 if (p1->parent != p2->parent)
3753 return FALSE;
3754 if (!mono_metadata_type_equal (p1->param.gshared_constraint, p2->param.gshared_constraint))
3755 return FALSE;
3756 return TRUE;
3760 * mini_get_shared_gparam:
3762 * Create an anonymous gparam from T with a constraint which encodes which types can match it.
3764 MonoType*
3765 mini_get_shared_gparam (MonoType *t, MonoType *constraint)
3767 MonoGenericParam *par = t->data.generic_param;
3768 MonoGSharedGenericParam *copy, key;
3769 MonoType *res;
3770 MonoImage *image = NULL;
3771 char *name;
3773 memset (&key, 0, sizeof (key));
3774 key.parent = par;
3775 key.param.gshared_constraint = constraint;
3777 g_assert (mono_generic_param_info (par));
3778 image = mono_get_image_for_generic_param(par);
3781 * Need a cache to ensure the newly created gparam
3782 * is unique wrt T/CONSTRAINT.
3784 mono_image_lock (image);
3785 if (!image->gshared_types) {
3786 image->gshared_types_len = MONO_TYPE_INTERNAL;
3787 image->gshared_types = g_new0 (GHashTable*, image->gshared_types_len);
3789 if (!image->gshared_types [constraint->type])
3790 image->gshared_types [constraint->type] = g_hash_table_new (shared_gparam_hash, shared_gparam_equal);
3791 res = (MonoType *)g_hash_table_lookup (image->gshared_types [constraint->type], &key);
3792 mono_image_unlock (image);
3793 if (res)
3794 return res;
3795 copy = (MonoGSharedGenericParam *)mono_image_alloc0 (image, sizeof (MonoGSharedGenericParam));
3796 memcpy (&copy->param, par, sizeof (MonoGenericParamFull));
3797 copy->param.info.pklass = NULL;
3798 constraint = mono_metadata_type_dup (image, constraint);
3799 name = get_shared_gparam_name (constraint->type, ((MonoGenericParamFull*)copy)->info.name);
3800 copy->param.info.name = mono_image_strdup (image, name);
3801 g_free (name);
3803 copy->param.owner = par->owner;
3804 g_assert (!par->owner->is_anonymous);
3806 copy->param.gshared_constraint = constraint;
3807 copy->parent = par;
3808 res = mono_metadata_type_dup (NULL, t);
3809 res->data.generic_param = (MonoGenericParam*)copy;
3811 if (image) {
3812 mono_image_lock (image);
3813 /* Duplicates are ok */
3814 g_hash_table_insert (image->gshared_types [constraint->type], copy, res);
3815 mono_image_unlock (image);
3818 return res;
3821 static MonoGenericInst*
3822 get_shared_inst (MonoGenericInst *inst, MonoGenericInst *shared_inst, MonoGenericContainer *container, gboolean use_gsharedvt);
3824 static MonoType*
3825 get_shared_type (MonoType *t, MonoType *type)
3827 MonoTypeEnum ttype;
3829 if (!type->byref && type->type == MONO_TYPE_GENERICINST && MONO_TYPE_ISSTRUCT (type)) {
3830 ERROR_DECL (error);
3831 MonoGenericClass *gclass = type->data.generic_class;
3832 MonoGenericContext context;
3833 MonoClass *k;
3835 memset (&context, 0, sizeof (context));
3836 if (gclass->context.class_inst)
3837 context.class_inst = get_shared_inst (gclass->context.class_inst, mono_class_get_generic_container (gclass->container_class)->context.class_inst, NULL, FALSE);
3838 if (gclass->context.method_inst)
3839 context.method_inst = get_shared_inst (gclass->context.method_inst, mono_class_get_generic_container (gclass->container_class)->context.method_inst, NULL, FALSE);
3841 k = mono_class_inflate_generic_class_checked (gclass->container_class, &context, error);
3842 mono_error_assert_ok (error); /* FIXME don't swallow the error */
3844 return mini_get_shared_gparam (t, m_class_get_byval_arg (k));
3845 } else if (MONO_TYPE_ISSTRUCT (type)) {
3846 return type;
3849 /* Create a type variable with a constraint which encodes which types can match it */
3850 ttype = type->type;
3851 if (type->type == MONO_TYPE_VALUETYPE) {
3852 ttype = mono_class_enum_basetype_internal (type->data.klass)->type;
3853 } else if (MONO_TYPE_IS_REFERENCE (type)) {
3854 ttype = MONO_TYPE_OBJECT;
3855 } else if (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) {
3856 if (type->data.generic_param->gshared_constraint)
3857 return mini_get_shared_gparam (t, type->data.generic_param->gshared_constraint);
3858 ttype = MONO_TYPE_OBJECT;
3862 MonoType t2;
3863 MonoClass *klass;
3865 memset (&t2, 0, sizeof (t2));
3866 t2.type = ttype;
3867 klass = mono_class_from_mono_type_internal (&t2);
3869 return mini_get_shared_gparam (t, m_class_get_byval_arg (klass));
3873 static MonoType*
3874 get_gsharedvt_type (MonoType *t)
3876 /* Use TypeHandle as the constraint type since its a valuetype */
3877 return mini_get_shared_gparam (t, m_class_get_byval_arg (mono_defaults.typehandle_class));
3880 static MonoGenericInst*
3881 get_shared_inst (MonoGenericInst *inst, MonoGenericInst *shared_inst, MonoGenericContainer *container, gboolean use_gsharedvt)
3883 MonoGenericInst *res;
3884 MonoType **type_argv;
3885 int i;
3887 type_argv = g_new0 (MonoType*, inst->type_argc);
3888 for (i = 0; i < inst->type_argc; ++i) {
3889 if (use_gsharedvt) {
3890 type_argv [i] = get_gsharedvt_type (shared_inst->type_argv [i]);
3891 } else {
3892 /* These types match the ones in mini_generic_inst_is_sharable () */
3893 type_argv [i] = get_shared_type (shared_inst->type_argv [i], inst->type_argv [i]);
3897 res = mono_metadata_get_generic_inst (inst->type_argc, type_argv);
3898 g_free (type_argv);
3899 return res;
3903 * mini_get_shared_method_full:
3904 * \param method the method to find the shared version of.
3905 * \param flags controls what sort of shared version to find
3906 * \param error set if we hit any fatal error
3908 * \returns The method which is actually compiled/registered when doing generic sharing.
3910 * If flags & SHARE_MODE_GSHAREDVT, produce a method using the gsharedvt instantiation.
3911 * \p method can be a non-inflated generic method.
3913 MonoMethod*
3914 mini_get_shared_method_full (MonoMethod *method, GetSharedMethodFlags flags, MonoError *error)
3917 MonoGenericContext shared_context;
3918 MonoMethod *declaring_method;
3919 MonoGenericContainer *class_container, *method_container = NULL;
3920 MonoGenericContext *context = mono_method_get_context (method);
3921 MonoGenericInst *inst;
3923 error_init (error);
3926 * Instead of creating a shared version of the wrapper, create a shared version of the original
3927 * method and construct a wrapper for it. Otherwise, we could end up with two copies of the
3928 * same wrapper, breaking AOT which assumes wrappers are unique.
3929 * FIXME: Add other cases.
3931 if (method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED) {
3932 MonoMethod *wrapper = mono_marshal_method_from_wrapper (method);
3934 MonoMethod *gwrapper = mini_get_shared_method_full (wrapper, flags, error);
3935 return_val_if_nok (error, NULL);
3937 return mono_marshal_get_synchronized_wrapper (gwrapper);
3939 if (method->wrapper_type == MONO_WRAPPER_DELEGATE_INVOKE) {
3940 WrapperInfo *info = mono_marshal_get_wrapper_info (method);
3942 if (info->subtype == WRAPPER_SUBTYPE_NONE) {
3943 MonoMethod *ginvoke = mini_get_shared_method_full (info->d.delegate_invoke.method, flags, error);
3944 return_val_if_nok (error, NULL);
3946 MonoMethod *m = mono_marshal_get_delegate_invoke (ginvoke, NULL);
3947 return m;
3951 if (method->is_generic || (mono_class_is_gtd (method->klass) && !method->is_inflated)) {
3952 declaring_method = method;
3953 } else {
3954 declaring_method = mono_method_get_declaring_generic_method (method);
3957 /* shared_context is the context containing type variables. */
3958 if (declaring_method->is_generic)
3959 shared_context = mono_method_get_generic_container (declaring_method)->context;
3960 else
3961 shared_context = mono_class_get_generic_container (declaring_method->klass)->context;
3963 gboolean use_gsharedvt_inst = FALSE;
3964 if (flags & SHARE_MODE_GSHAREDVT)
3965 use_gsharedvt_inst = TRUE;
3966 else if (!mono_method_is_generic_sharable_full (method, FALSE, TRUE, FALSE))
3967 use_gsharedvt_inst = mini_is_gsharedvt_sharable_method (method);
3969 class_container = mono_class_try_get_generic_container (declaring_method->klass); //FIXME is this a case for a try_get?
3970 method_container = mono_method_get_generic_container (declaring_method);
3973 * Create the shared context by replacing the ref type arguments with
3974 * type parameters, and keeping the rest.
3976 if (context)
3977 inst = context->class_inst;
3978 else
3979 inst = shared_context.class_inst;
3980 if (inst)
3981 shared_context.class_inst = get_shared_inst (inst, shared_context.class_inst, class_container, use_gsharedvt_inst);
3983 if (context)
3984 inst = context->method_inst;
3985 else
3986 inst = shared_context.method_inst;
3987 if (inst)
3988 shared_context.method_inst = get_shared_inst (inst, shared_context.method_inst, method_container, use_gsharedvt_inst);
3990 return mono_class_inflate_generic_method_checked (declaring_method, &shared_context, error);
3994 mini_get_rgctx_entry_slot (MonoJumpInfoRgctxEntry *entry)
3996 guint32 slot = -1;
3998 switch (entry->data->type) {
3999 case MONO_PATCH_INFO_CLASS:
4000 slot = mono_method_lookup_or_register_info (entry->method, entry->in_mrgctx, m_class_get_byval_arg (entry->data->data.klass), entry->info_type, mono_method_get_context (entry->method));
4001 break;
4002 case MONO_PATCH_INFO_METHOD:
4003 case MONO_PATCH_INFO_METHODCONST:
4004 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));
4005 break;
4006 case MONO_PATCH_INFO_FIELD:
4007 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));
4008 break;
4009 case MONO_PATCH_INFO_SIGNATURE:
4010 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));
4011 break;
4012 case MONO_PATCH_INFO_GSHAREDVT_CALL: {
4013 MonoJumpInfoGSharedVtCall *call_info = (MonoJumpInfoGSharedVtCall *)g_malloc0 (sizeof (MonoJumpInfoGSharedVtCall)); //mono_domain_alloc0 (domain, sizeof (MonoJumpInfoGSharedVtCall));
4015 memcpy (call_info, entry->data->data.gsharedvt, sizeof (MonoJumpInfoGSharedVtCall));
4016 slot = mono_method_lookup_or_register_info (entry->method, entry->in_mrgctx, call_info, entry->info_type, mono_method_get_context (entry->method));
4017 break;
4019 case MONO_PATCH_INFO_GSHAREDVT_METHOD: {
4020 MonoGSharedVtMethodInfo *info;
4021 MonoGSharedVtMethodInfo *oinfo = entry->data->data.gsharedvt_method;
4022 int i;
4024 /* Make a copy into the domain mempool */
4025 info = (MonoGSharedVtMethodInfo *)g_malloc0 (sizeof (MonoGSharedVtMethodInfo)); //mono_domain_alloc0 (domain, sizeof (MonoGSharedVtMethodInfo));
4026 info->method = oinfo->method;
4027 info->num_entries = oinfo->num_entries;
4028 info->entries = (MonoRuntimeGenericContextInfoTemplate *)g_malloc0 (sizeof (MonoRuntimeGenericContextInfoTemplate) * info->num_entries);
4029 for (i = 0; i < oinfo->num_entries; ++i) {
4030 MonoRuntimeGenericContextInfoTemplate *otemplate = &oinfo->entries [i];
4031 MonoRuntimeGenericContextInfoTemplate *template_ = &info->entries [i];
4033 memcpy (template_, otemplate, sizeof (MonoRuntimeGenericContextInfoTemplate));
4035 slot = mono_method_lookup_or_register_info (entry->method, entry->in_mrgctx, info, entry->info_type, mono_method_get_context (entry->method));
4036 break;
4038 case MONO_PATCH_INFO_VIRT_METHOD: {
4039 MonoJumpInfoVirtMethod *info;
4040 MonoJumpInfoVirtMethod *oinfo = entry->data->data.virt_method;
4042 info = (MonoJumpInfoVirtMethod *)g_malloc0 (sizeof (MonoJumpInfoVirtMethod));
4043 memcpy (info, oinfo, sizeof (MonoJumpInfoVirtMethod));
4044 slot = mono_method_lookup_or_register_info (entry->method, entry->in_mrgctx, info, entry->info_type, mono_method_get_context (entry->method));
4045 break;
4047 case MONO_PATCH_INFO_DELEGATE_TRAMPOLINE: {
4048 MonoDelegateClassMethodPair *info;
4049 MonoDelegateClassMethodPair *oinfo = entry->data->data.del_tramp;
4051 info = (MonoDelegateClassMethodPair *)g_malloc0 (sizeof (MonoDelegateClassMethodPair));
4052 memcpy (info, oinfo, sizeof (MonoDelegateClassMethodPair));
4053 slot = mono_method_lookup_or_register_info (entry->method, entry->in_mrgctx, info, entry->info_type, mono_method_get_context (entry->method));
4054 break;
4056 default:
4057 g_assert_not_reached ();
4058 break;
4061 return slot;
4064 static gboolean gsharedvt_supported;
4066 void
4067 mono_set_generic_sharing_vt_supported (gboolean supported)
4069 /* ensure we do not disable gsharedvt once it's been enabled */
4070 if (!gsharedvt_supported && supported)
4071 gsharedvt_supported = TRUE;
4074 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
4077 * mini_is_gsharedvt_type:
4079 * Return whenever T references type arguments instantiated with gshared vtypes.
4081 gboolean
4082 mini_is_gsharedvt_type (MonoType *t)
4084 int i;
4086 if (t->byref)
4087 return FALSE;
4088 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)
4089 return TRUE;
4090 else if (t->type == MONO_TYPE_GENERICINST) {
4091 MonoGenericClass *gclass = t->data.generic_class;
4092 MonoGenericContext *context = &gclass->context;
4093 MonoGenericInst *inst;
4095 inst = context->class_inst;
4096 if (inst) {
4097 for (i = 0; i < inst->type_argc; ++i)
4098 if (mini_is_gsharedvt_type (inst->type_argv [i]))
4099 return TRUE;
4101 inst = context->method_inst;
4102 if (inst) {
4103 for (i = 0; i < inst->type_argc; ++i)
4104 if (mini_is_gsharedvt_type (inst->type_argv [i]))
4105 return TRUE;
4108 return FALSE;
4109 } else {
4110 return FALSE;
4114 gboolean
4115 mini_is_gsharedvt_klass (MonoClass *klass)
4117 return mini_is_gsharedvt_type (m_class_get_byval_arg (klass));
4120 gboolean
4121 mini_is_gsharedvt_signature (MonoMethodSignature *sig)
4123 int i;
4125 if (sig->ret && mini_is_gsharedvt_type (sig->ret))
4126 return TRUE;
4127 for (i = 0; i < sig->param_count; ++i) {
4128 if (mini_is_gsharedvt_type (sig->params [i]))
4129 return TRUE;
4131 return FALSE;
4135 * mini_is_gsharedvt_variable_type:
4137 * Return whenever T refers to a GSHAREDVT type whose size differs depending on the values of type parameters.
4139 gboolean
4140 mini_is_gsharedvt_variable_type (MonoType *t)
4142 if (!mini_is_gsharedvt_type (t))
4143 return FALSE;
4144 if (t->type == MONO_TYPE_GENERICINST) {
4145 MonoGenericClass *gclass = t->data.generic_class;
4146 MonoGenericContext *context = &gclass->context;
4147 MonoGenericInst *inst;
4148 int i;
4150 if (m_class_get_byval_arg (t->data.generic_class->container_class)->type != MONO_TYPE_VALUETYPE || m_class_is_enumtype (t->data.generic_class->container_class))
4151 return FALSE;
4153 inst = context->class_inst;
4154 if (inst) {
4155 for (i = 0; i < inst->type_argc; ++i)
4156 if (mini_is_gsharedvt_variable_type (inst->type_argv [i]))
4157 return TRUE;
4159 inst = context->method_inst;
4160 if (inst) {
4161 for (i = 0; i < inst->type_argc; ++i)
4162 if (mini_is_gsharedvt_variable_type (inst->type_argv [i]))
4163 return TRUE;
4166 return FALSE;
4168 return TRUE;
4171 static gboolean
4172 is_variable_size (MonoType *t)
4174 int i;
4176 if (t->byref)
4177 return FALSE;
4179 if (t->type == MONO_TYPE_VAR || t->type == MONO_TYPE_MVAR) {
4180 MonoGenericParam *param = t->data.generic_param;
4182 if (param->gshared_constraint && param->gshared_constraint->type != MONO_TYPE_VALUETYPE && param->gshared_constraint->type != MONO_TYPE_GENERICINST)
4183 return FALSE;
4184 if (param->gshared_constraint && param->gshared_constraint->type == MONO_TYPE_GENERICINST)
4185 return is_variable_size (param->gshared_constraint);
4186 return TRUE;
4188 if (t->type == MONO_TYPE_GENERICINST && m_class_get_byval_arg (t->data.generic_class->container_class)->type == MONO_TYPE_VALUETYPE) {
4189 MonoGenericClass *gclass = t->data.generic_class;
4190 MonoGenericContext *context = &gclass->context;
4191 MonoGenericInst *inst;
4193 inst = context->class_inst;
4194 if (inst) {
4195 for (i = 0; i < inst->type_argc; ++i)
4196 if (is_variable_size (inst->type_argv [i]))
4197 return TRUE;
4199 inst = context->method_inst;
4200 if (inst) {
4201 for (i = 0; i < inst->type_argc; ++i)
4202 if (is_variable_size (inst->type_argv [i]))
4203 return TRUE;
4207 return FALSE;
4210 gboolean
4211 mini_is_gsharedvt_sharable_inst (MonoGenericInst *inst)
4213 int i;
4214 gboolean has_vt = FALSE;
4216 for (i = 0; i < inst->type_argc; ++i) {
4217 MonoType *type = inst->type_argv [i];
4219 if ((MONO_TYPE_IS_REFERENCE (type) || type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) && !mini_is_gsharedvt_type (type)) {
4220 } else {
4221 has_vt = TRUE;
4225 return has_vt;
4228 gboolean
4229 mini_is_gsharedvt_sharable_method (MonoMethod *method)
4231 MonoMethodSignature *sig;
4234 * A method is gsharedvt if:
4235 * - it has type parameters instantiated with vtypes
4237 if (!gsharedvt_supported)
4238 return FALSE;
4239 if (method->is_inflated) {
4240 MonoMethodInflated *inflated = (MonoMethodInflated*)method;
4241 MonoGenericContext *context = &inflated->context;
4242 MonoGenericInst *inst;
4244 if (context->class_inst && context->method_inst) {
4245 /* At least one inst has to be gsharedvt sharable, and the other normal or gsharedvt sharable */
4246 gboolean vt1 = mini_is_gsharedvt_sharable_inst (context->class_inst);
4247 gboolean vt2 = mini_is_gsharedvt_sharable_inst (context->method_inst);
4249 if ((vt1 && vt2) ||
4250 (vt1 && mini_generic_inst_is_sharable (context->method_inst, TRUE, FALSE)) ||
4251 (vt2 && mini_generic_inst_is_sharable (context->class_inst, TRUE, FALSE)))
4253 else
4254 return FALSE;
4255 } else {
4256 inst = context->class_inst;
4257 if (inst && !mini_is_gsharedvt_sharable_inst (inst))
4258 return FALSE;
4259 inst = context->method_inst;
4260 if (inst && !mini_is_gsharedvt_sharable_inst (inst))
4261 return FALSE;
4263 } else {
4264 return FALSE;
4267 sig = mono_method_signature_internal (mono_method_get_declaring_generic_method (method));
4268 if (!sig)
4269 return FALSE;
4272 if (mini_is_gsharedvt_variable_signature (sig))
4273 return FALSE;
4276 //DEBUG ("GSHAREDVT SHARABLE: %s\n", mono_method_full_name (method, TRUE));
4278 return TRUE;
4282 * mini_is_gsharedvt_variable_signature:
4284 * Return whenever the calling convention used to call SIG varies depending on the values of type parameters used by SIG,
4285 * i.e. FALSE for swap(T[] arr, int i, int j), TRUE for T get_t ().
4287 gboolean
4288 mini_is_gsharedvt_variable_signature (MonoMethodSignature *sig)
4290 int i;
4292 if (sig->ret && is_variable_size (sig->ret))
4293 return TRUE;
4294 for (i = 0; i < sig->param_count; ++i) {
4295 MonoType *t = sig->params [i];
4297 if (is_variable_size (t))
4298 return TRUE;
4300 return FALSE;
4303 MonoMethod*
4304 mini_method_to_shared (MonoMethod *method)
4306 if (!mono_method_is_generic_impl (method))
4307 return NULL;
4309 ERROR_DECL (error);
4311 // This pattern is based on add_extra_method_with_depth.
4313 if (mono_method_is_generic_sharable_full (method, TRUE, TRUE, FALSE))
4314 // gshared over reference type
4315 method = mini_get_shared_method_full (method, SHARE_MODE_NONE, error);
4316 else if (mono_method_is_generic_sharable_full (method, FALSE, FALSE, TRUE))
4317 // gshared over valuetype (or primitive?)
4318 method = mini_get_shared_method_full (method, SHARE_MODE_GSHAREDVT, error);
4319 else
4320 return NULL;
4321 mono_error_assert_ok (error);
4322 return method;
4325 #else
4327 gboolean
4328 mini_is_gsharedvt_type (MonoType *t)
4330 return FALSE;
4333 gboolean
4334 mini_is_gsharedvt_klass (MonoClass *klass)
4336 return FALSE;
4339 gboolean
4340 mini_is_gsharedvt_signature (MonoMethodSignature *sig)
4342 return FALSE;
4345 gboolean
4346 mini_is_gsharedvt_variable_type (MonoType *t)
4348 return FALSE;
4351 gboolean
4352 mini_is_gsharedvt_sharable_method (MonoMethod *method)
4354 return FALSE;
4357 gboolean
4358 mini_is_gsharedvt_variable_signature (MonoMethodSignature *sig)
4360 return FALSE;
4363 MonoMethod*
4364 mini_method_to_shared (MonoMethod *method)
4366 return NULL;
4369 #endif /* !MONO_ARCH_GSHAREDVT_SUPPORTED */