[runtime] Transition the trampoline code to use memory managers for memory allocation...
[mono-project.git] / mono / mini / mini-generic-sharing.c
blob2ad21ecc1b56c7f8f504926b8174b31644fa69ea
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"
28 #include "llvmonly-runtime.h"
29 #include "interp/interp.h"
31 #define ALLOW_PARTIAL_SHARING TRUE
32 //#define ALLOW_PARTIAL_SHARING FALSE
34 #if 0
35 #define DEBUG(...) __VA_ARGS__
36 #else
37 #define DEBUG(...)
38 #endif
40 static void
41 mono_class_unregister_image_generic_subclasses (MonoImage *image, gpointer user_data);
43 /* Counters */
44 static gint32 rgctx_template_num_allocated;
45 static gint32 rgctx_template_bytes_allocated;
46 static gint32 rgctx_oti_num_allocated;
47 static gint32 rgctx_oti_bytes_allocated;
48 static gint32 rgctx_oti_num_markers;
49 static gint32 rgctx_oti_num_data;
50 static gint32 rgctx_max_slot_number;
51 static gint32 rgctx_num_allocated;
52 static gint32 rgctx_num_arrays_allocated;
53 static gint32 rgctx_bytes_allocated;
54 static gint32 mrgctx_num_arrays_allocated;
55 static gint32 mrgctx_bytes_allocated;
56 static gint32 gsharedvt_num_trampolines;
58 #define gshared_lock() mono_os_mutex_lock (&gshared_mutex)
59 #define gshared_unlock() mono_os_mutex_unlock (&gshared_mutex)
60 static mono_mutex_t gshared_mutex;
62 static gboolean partial_supported = FALSE;
64 static gboolean
65 partial_sharing_supported (void)
67 if (!ALLOW_PARTIAL_SHARING)
68 return FALSE;
69 /* Enable this when AOT compiling or running in full-aot mode */
70 if (mono_aot_only)
71 return TRUE;
72 if (partial_supported)
73 return TRUE;
74 return FALSE;
77 static int
78 type_check_context_used (MonoType *type, gboolean recursive)
80 switch (mono_type_get_type_internal (type)) {
81 case MONO_TYPE_VAR:
82 return MONO_GENERIC_CONTEXT_USED_CLASS;
83 case MONO_TYPE_MVAR:
84 return MONO_GENERIC_CONTEXT_USED_METHOD;
85 case MONO_TYPE_SZARRAY:
86 return mono_class_check_context_used (mono_type_get_class_internal (type));
87 case MONO_TYPE_ARRAY:
88 return mono_class_check_context_used (mono_type_get_array_type (type)->eklass);
89 case MONO_TYPE_CLASS:
90 if (recursive)
91 return mono_class_check_context_used (mono_type_get_class_internal (type));
92 else
93 return 0;
94 case MONO_TYPE_GENERICINST:
95 if (recursive) {
96 MonoGenericClass *gclass = type->data.generic_class;
98 g_assert (mono_class_is_gtd (gclass->container_class));
99 return mono_generic_context_check_used (&gclass->context);
100 } else {
101 return 0;
103 default:
104 return 0;
108 static int
109 inst_check_context_used (MonoGenericInst *inst)
111 int context_used = 0;
112 int i;
114 if (!inst)
115 return 0;
117 for (i = 0; i < inst->type_argc; ++i)
118 context_used |= type_check_context_used (inst->type_argv [i], TRUE);
120 return context_used;
124 * mono_generic_context_check_used:
125 * @context: a generic context
127 * Checks whether the context uses a type variable. Returns an int
128 * with the bit MONO_GENERIC_CONTEXT_USED_CLASS set to reflect whether
129 * the context's class instantiation uses type variables.
132 mono_generic_context_check_used (MonoGenericContext *context)
134 int context_used = 0;
136 context_used |= inst_check_context_used (context->class_inst);
137 context_used |= inst_check_context_used (context->method_inst);
139 return context_used;
143 * mono_class_check_context_used:
144 * @class: a class
146 * Checks whether the class's generic context uses a type variable.
147 * Returns an int with the bit MONO_GENERIC_CONTEXT_USED_CLASS set to
148 * reflect whether the context's class instantiation uses type
149 * variables.
152 mono_class_check_context_used (MonoClass *klass)
154 int context_used = 0;
156 context_used |= type_check_context_used (m_class_get_this_arg (klass), FALSE);
157 context_used |= type_check_context_used (m_class_get_byval_arg (klass), FALSE);
159 if (mono_class_is_ginst (klass))
160 context_used |= mono_generic_context_check_used (&mono_class_get_generic_class (klass)->context);
161 else if (mono_class_is_gtd (klass))
162 context_used |= mono_generic_context_check_used (&mono_class_get_generic_container (klass)->context);
164 return context_used;
168 * LOCKING: loader lock
170 static MonoRuntimeGenericContextInfoTemplate*
171 get_info_templates (MonoRuntimeGenericContextTemplate *template_, int type_argc)
173 g_assert (type_argc >= 0);
174 if (type_argc == 0)
175 return template_->infos;
176 return (MonoRuntimeGenericContextInfoTemplate *)g_slist_nth_data (template_->method_templates, type_argc - 1);
180 * LOCKING: loader lock
182 static void
183 set_info_templates (MonoImage *image, MonoRuntimeGenericContextTemplate *template_, int type_argc,
184 MonoRuntimeGenericContextInfoTemplate *oti)
186 g_assert (type_argc >= 0);
187 if (type_argc == 0)
188 template_->infos = oti;
189 else {
190 int length = g_slist_length (template_->method_templates);
191 GSList *list;
193 /* FIXME: quadratic! */
194 while (length < type_argc) {
195 template_->method_templates = mono_g_slist_append_image (image, template_->method_templates, NULL);
196 length++;
199 list = g_slist_nth (template_->method_templates, type_argc - 1);
200 g_assert (list);
201 list->data = oti;
206 * LOCKING: loader lock
208 static int
209 template_get_max_argc (MonoRuntimeGenericContextTemplate *template_)
211 return g_slist_length (template_->method_templates);
215 * LOCKING: loader lock
217 static MonoRuntimeGenericContextInfoTemplate*
218 rgctx_template_get_other_slot (MonoRuntimeGenericContextTemplate *template_, int type_argc, int slot)
220 int i;
221 MonoRuntimeGenericContextInfoTemplate *oti;
223 g_assert (slot >= 0);
225 for (oti = get_info_templates (template_, type_argc), i = 0; i < slot; oti = oti->next, ++i) {
226 if (!oti)
227 return NULL;
230 return oti;
234 * LOCKING: loader lock
236 static int
237 rgctx_template_num_infos (MonoRuntimeGenericContextTemplate *template_, int type_argc)
239 MonoRuntimeGenericContextInfoTemplate *oti;
240 int i;
242 for (i = 0, oti = get_info_templates (template_, type_argc); oti; ++i, oti = oti->next)
245 return i;
248 /* Maps from uninstantiated generic classes to GList's of
249 * uninstantiated generic classes whose parent is the key class or an
250 * instance of the key class.
252 * LOCKING: loader lock
254 static GHashTable *generic_subclass_hash;
257 * LOCKING: templates lock
259 static void
260 class_set_rgctx_template (MonoClass *klass, MonoRuntimeGenericContextTemplate *rgctx_template)
262 if (!m_class_get_image (klass)->rgctx_template_hash)
263 m_class_get_image (klass)->rgctx_template_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
265 g_hash_table_insert (m_class_get_image (klass)->rgctx_template_hash, klass, rgctx_template);
269 * LOCKING: loader lock
271 static MonoRuntimeGenericContextTemplate*
272 class_lookup_rgctx_template (MonoClass *klass)
274 MonoRuntimeGenericContextTemplate *template_;
276 if (!m_class_get_image (klass)->rgctx_template_hash)
277 return NULL;
279 template_ = (MonoRuntimeGenericContextTemplate *)g_hash_table_lookup (m_class_get_image (klass)->rgctx_template_hash, klass);
281 return template_;
285 * LOCKING: loader lock
287 static void
288 register_generic_subclass (MonoClass *klass)
290 MonoClass *parent = m_class_get_parent (klass);
291 MonoClass *subclass;
292 MonoRuntimeGenericContextTemplate *rgctx_template = class_lookup_rgctx_template (klass);
294 g_assert (rgctx_template);
296 if (mono_class_is_ginst (parent))
297 parent = mono_class_get_generic_class (parent)->container_class;
299 if (!generic_subclass_hash)
300 generic_subclass_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
302 subclass = (MonoClass *)g_hash_table_lookup (generic_subclass_hash, parent);
303 rgctx_template->next_subclass = subclass;
304 g_hash_table_insert (generic_subclass_hash, parent, klass);
307 static void
308 move_subclasses_not_in_image_foreach_func (MonoClass *klass, MonoClass *subclass, MonoImage *image)
310 MonoClass *new_list;
312 if (m_class_get_image (klass) == image) {
313 /* The parent class itself is in the image, so all the
314 subclasses must be in the image, too. If not,
315 we're removing an image containing a class which
316 still has a subclass in another image. */
318 while (subclass) {
319 g_assert (m_class_get_image (subclass) == image);
320 subclass = class_lookup_rgctx_template (subclass)->next_subclass;
323 return;
326 new_list = NULL;
327 while (subclass) {
328 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
329 MonoClass *next = subclass_template->next_subclass;
331 if (m_class_get_image (subclass) != image) {
332 subclass_template->next_subclass = new_list;
333 new_list = subclass;
336 subclass = next;
339 if (new_list)
340 g_hash_table_insert (generic_subclass_hash, klass, new_list);
344 * mono_class_unregister_image_generic_subclasses:
345 * @image: an image
347 * Removes all classes of the image from the generic subclass hash.
348 * Must be called when an image is unloaded.
350 static void
351 mono_class_unregister_image_generic_subclasses (MonoImage *image, gpointer user_data)
353 GHashTable *old_hash;
355 //g_print ("unregistering image %s\n", image->name);
357 if (!generic_subclass_hash)
358 return;
360 mono_loader_lock ();
362 old_hash = generic_subclass_hash;
363 generic_subclass_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
365 g_hash_table_foreach (old_hash, (GHFunc)move_subclasses_not_in_image_foreach_func, image);
367 mono_loader_unlock ();
369 g_hash_table_destroy (old_hash);
372 static MonoRuntimeGenericContextTemplate*
373 alloc_template (MonoClass *klass)
375 gint32 size = sizeof (MonoRuntimeGenericContextTemplate);
377 mono_atomic_inc_i32 (&rgctx_template_num_allocated);
378 mono_atomic_fetch_add_i32 (&rgctx_template_bytes_allocated, size);
380 return (MonoRuntimeGenericContextTemplate *)mono_image_alloc0 (m_class_get_image (klass), size);
383 /* LOCKING: Takes the loader lock */
384 static MonoRuntimeGenericContextInfoTemplate*
385 alloc_oti (MonoImage *image)
387 gint32 size = sizeof (MonoRuntimeGenericContextInfoTemplate);
389 mono_atomic_inc_i32 (&rgctx_oti_num_allocated);
390 mono_atomic_fetch_add_i32 (&rgctx_oti_bytes_allocated, size);
392 return (MonoRuntimeGenericContextInfoTemplate *)mono_image_alloc0 (image, size);
395 #define MONO_RGCTX_SLOT_USED_MARKER ((gpointer)mono_get_object_type ())
398 * Return true if this info type has the notion of identify.
400 * Some info types expect that each insert results in a new slot been assigned.
402 static int
403 info_has_identity (MonoRgctxInfoType info_type)
405 return info_type != MONO_RGCTX_INFO_CAST_CACHE;
409 * LOCKING: loader lock
411 static void
412 rgctx_template_set_slot (MonoImage *image, MonoRuntimeGenericContextTemplate *template_, int type_argc,
413 int slot, gpointer data, MonoRgctxInfoType info_type)
415 int i;
416 MonoRuntimeGenericContextInfoTemplate *list = get_info_templates (template_, type_argc);
417 MonoRuntimeGenericContextInfoTemplate **oti = &list;
419 g_assert (slot >= 0);
420 g_assert (data);
422 i = 0;
423 while (i <= slot) {
424 if (i > 0)
425 oti = &(*oti)->next;
426 if (!*oti)
427 *oti = alloc_oti (image);
428 ++i;
431 g_assert (!(*oti)->data);
432 (*oti)->data = data;
433 (*oti)->info_type = info_type;
435 set_info_templates (image, template_, type_argc, list);
437 /* interlocked by loader lock (by definition) */
438 if (data == MONO_RGCTX_SLOT_USED_MARKER)
439 UnlockedIncrement (&rgctx_oti_num_markers);
440 else
441 UnlockedIncrement (&rgctx_oti_num_data);
445 * mono_method_get_declaring_generic_method:
446 * @method: an inflated method
448 * Returns an inflated method's declaring method.
450 MonoMethod*
451 mono_method_get_declaring_generic_method (MonoMethod *method)
453 MonoMethodInflated *inflated;
455 g_assert (method->is_inflated);
457 inflated = (MonoMethodInflated*)method;
459 return inflated->declaring;
463 * mono_class_get_method_generic:
464 * @klass: a class
465 * @method: a method
466 * @error: set on error
468 * Given a class and a generic method, which has to be of an
469 * instantiation of the same class that klass is an instantiation of,
470 * returns the corresponding method in klass. Example:
472 * klass is Gen<string>
473 * method is Gen<object>.work<int>
475 * returns: Gen<string>.work<int>
477 * On error sets @error and returns NULL.
479 MonoMethod*
480 mono_class_get_method_generic (MonoClass *klass, MonoMethod *method, MonoError *error)
482 MonoMethod *declaring, *m;
483 int i;
485 if (method->is_inflated)
486 declaring = mono_method_get_declaring_generic_method (method);
487 else
488 declaring = method;
490 m = NULL;
491 if (mono_class_is_ginst (klass)) {
492 m = mono_class_get_inflated_method (klass, declaring, error);
493 return_val_if_nok (error, NULL);
496 if (!m) {
497 mono_class_setup_methods (klass);
498 if (mono_class_has_failure (klass))
499 return NULL;
500 int mcount = mono_class_get_method_count (klass);
501 MonoMethod **klass_methods = m_class_get_methods (klass);
502 for (i = 0; i < mcount; ++i) {
503 m = klass_methods [i];
504 if (m == declaring)
505 break;
506 if (m->is_inflated && mono_method_get_declaring_generic_method (m) == declaring)
507 break;
509 if (i >= mcount)
510 return NULL;
513 if (method != declaring) {
514 MonoGenericContext context;
516 context.class_inst = NULL;
517 context.method_inst = mono_method_get_context (method)->method_inst;
519 m = mono_class_inflate_generic_method_checked (m, &context, error);
520 return_val_if_nok (error, NULL);
523 return m;
526 static gpointer
527 inflate_info (MonoRuntimeGenericContextInfoTemplate *oti, MonoGenericContext *context, MonoClass *klass, gboolean temporary)
529 gpointer data = oti->data;
530 MonoRgctxInfoType info_type = oti->info_type;
531 ERROR_DECL (error);
533 g_assert (data);
535 if (data == MONO_RGCTX_SLOT_USED_MARKER)
536 return MONO_RGCTX_SLOT_USED_MARKER;
538 switch (info_type)
540 case MONO_RGCTX_INFO_STATIC_DATA:
541 case MONO_RGCTX_INFO_KLASS:
542 case MONO_RGCTX_INFO_ELEMENT_KLASS:
543 case MONO_RGCTX_INFO_VTABLE:
544 case MONO_RGCTX_INFO_TYPE:
545 case MONO_RGCTX_INFO_REFLECTION_TYPE:
546 case MONO_RGCTX_INFO_CAST_CACHE:
547 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
548 case MONO_RGCTX_INFO_VALUE_SIZE:
549 case MONO_RGCTX_INFO_CLASS_SIZEOF:
550 case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
551 case MONO_RGCTX_INFO_CLASS_IS_REF_OR_CONTAINS_REFS:
552 case MONO_RGCTX_INFO_MEMCPY:
553 case MONO_RGCTX_INFO_BZERO:
554 case MONO_RGCTX_INFO_LOCAL_OFFSET:
555 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
556 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: {
557 gpointer result = mono_class_inflate_generic_type_with_mempool (temporary ? NULL : m_class_get_image (klass),
558 (MonoType *)data, context, error);
559 mono_error_assert_msg_ok (error, "Could not inflate generic type"); /* FIXME proper error handling */
560 return result;
563 case MONO_RGCTX_INFO_METHOD:
564 case MONO_RGCTX_INFO_METHOD_FTNDESC:
565 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
566 case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER:
567 case MONO_RGCTX_INFO_METHOD_RGCTX:
568 case MONO_RGCTX_INFO_METHOD_CONTEXT:
569 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
570 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE: {
571 MonoMethod *method = (MonoMethod *)data;
572 MonoMethod *inflated_method;
573 MonoType *inflated_type = mono_class_inflate_generic_type_checked (m_class_get_byval_arg (method->klass), context, error);
574 mono_error_assert_ok (error); /* FIXME don't swallow the error */
576 MonoClass *inflated_class = mono_class_from_mono_type_internal (inflated_type);
578 mono_metadata_free_type (inflated_type);
580 mono_class_init_internal (inflated_class);
582 g_assert (!method->wrapper_type);
584 if (m_class_get_byval_arg (inflated_class)->type == MONO_TYPE_ARRAY ||
585 m_class_get_byval_arg (inflated_class)->type == MONO_TYPE_SZARRAY) {
586 inflated_method = mono_method_search_in_array_class (inflated_class,
587 method->name, method->signature);
588 } else {
589 ERROR_DECL (error);
590 inflated_method = mono_class_inflate_generic_method_checked (method, context, error);
591 g_assert (is_ok (error)); /* FIXME don't swallow the error */
593 mono_class_init_internal (inflated_method->klass);
594 g_assert (inflated_method->klass == inflated_class);
595 return inflated_method;
597 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO: {
598 MonoGSharedVtMethodInfo *oinfo = (MonoGSharedVtMethodInfo *)data;
599 MonoGSharedVtMethodInfo *res;
600 MonoDomain *domain = mono_domain_get ();
601 int i;
603 res = (MonoGSharedVtMethodInfo *)mono_domain_alloc0 (domain, sizeof (MonoGSharedVtMethodInfo));
605 res->nlocals = info->nlocals;
606 res->locals_types = g_new0 (MonoType*, info->nlocals);
607 for (i = 0; i < info->nlocals; ++i)
608 res->locals_types [i] = mono_class_inflate_generic_type (info->locals_types [i], context);
610 res->num_entries = oinfo->num_entries;
611 res->entries = (MonoRuntimeGenericContextInfoTemplate *)mono_domain_alloc0 (domain, sizeof (MonoRuntimeGenericContextInfoTemplate) * oinfo->num_entries);
612 for (i = 0; i < oinfo->num_entries; ++i) {
613 MonoRuntimeGenericContextInfoTemplate *otemplate = &oinfo->entries [i];
614 MonoRuntimeGenericContextInfoTemplate *template_ = &res->entries [i];
616 memcpy (template_, otemplate, sizeof (MonoRuntimeGenericContextInfoTemplate));
617 template_->data = inflate_info (template_, context, klass, FALSE);
619 return res;
621 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE:
622 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT: {
623 MonoJumpInfoGSharedVtCall *info = (MonoJumpInfoGSharedVtCall *)data;
624 MonoMethod *method = info->method;
625 MonoMethod *inflated_method;
626 MonoType *inflated_type = mono_class_inflate_generic_type_checked (m_class_get_byval_arg (method->klass), context, error);
627 mono_error_assert_ok (error); /* FIXME don't swallow the error */
628 WrapperInfo *winfo = NULL;
630 MonoClass *inflated_class = mono_class_from_mono_type_internal (inflated_type);
631 MonoJumpInfoGSharedVtCall *res;
632 MonoDomain *domain = mono_domain_get ();
634 res = (MonoJumpInfoGSharedVtCall *)mono_domain_alloc0 (domain, sizeof (MonoJumpInfoGSharedVtCall));
635 /* Keep the original signature */
636 res->sig = info->sig;
638 mono_metadata_free_type (inflated_type);
640 mono_class_init_internal (inflated_class);
642 if (method->wrapper_type) {
643 winfo = mono_marshal_get_wrapper_info (method);
645 g_assert (winfo);
646 g_assert (winfo->subtype == WRAPPER_SUBTYPE_SYNCHRONIZED_INNER);
647 method = winfo->d.synchronized_inner.method;
650 if (m_class_get_byval_arg (inflated_class)->type == MONO_TYPE_ARRAY ||
651 m_class_get_byval_arg (inflated_class)->type == MONO_TYPE_SZARRAY) {
652 inflated_method = mono_method_search_in_array_class (inflated_class,
653 method->name, method->signature);
654 } else {
655 ERROR_DECL (error);
656 inflated_method = mono_class_inflate_generic_method_checked (method, context, error);
657 g_assert (is_ok (error)); /* FIXME don't swallow the error */
659 mono_class_init_internal (inflated_method->klass);
660 g_assert (inflated_method->klass == inflated_class);
662 if (winfo) {
663 g_assert (winfo->subtype == WRAPPER_SUBTYPE_SYNCHRONIZED_INNER);
664 inflated_method = mono_marshal_get_synchronized_inner_wrapper (inflated_method);
667 res->method = inflated_method;
669 return res;
672 case MONO_RGCTX_INFO_CLASS_FIELD:
673 case MONO_RGCTX_INFO_FIELD_OFFSET: {
674 ERROR_DECL (error);
675 MonoClassField *field = (MonoClassField *)data;
676 MonoType *inflated_type = mono_class_inflate_generic_type_checked (m_class_get_byval_arg (field->parent), context, error);
677 mono_error_assert_ok (error); /* FIXME don't swallow the error */
679 MonoClass *inflated_class = mono_class_from_mono_type_internal (inflated_type);
680 int i = field - m_class_get_fields (field->parent);
681 gpointer dummy = NULL;
683 mono_metadata_free_type (inflated_type);
685 mono_class_get_fields_internal (inflated_class, &dummy);
686 g_assert (m_class_get_fields (inflated_class));
688 return &m_class_get_fields (inflated_class) [i];
690 case MONO_RGCTX_INFO_SIG_GSHAREDVT_IN_TRAMPOLINE_CALLI:
691 case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI: {
692 MonoMethodSignature *sig = (MonoMethodSignature *)data;
693 MonoMethodSignature *isig;
694 ERROR_DECL (error);
696 isig = mono_inflate_generic_signature (sig, context, error);
697 g_assert (is_ok (error));
698 return isig;
700 case MONO_RGCTX_INFO_VIRT_METHOD_CODE:
701 case MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE: {
702 MonoJumpInfoVirtMethod *info = (MonoJumpInfoVirtMethod *)data;
703 MonoJumpInfoVirtMethod *res;
704 MonoType *t;
705 MonoDomain *domain = mono_domain_get ();
706 ERROR_DECL (error);
708 // FIXME: Temporary
709 res = (MonoJumpInfoVirtMethod *)mono_domain_alloc0 (domain, sizeof (MonoJumpInfoVirtMethod));
710 t = mono_class_inflate_generic_type_checked (m_class_get_byval_arg (info->klass), context, error);
711 mono_error_assert_ok (error); /* FIXME don't swallow the error */
713 res->klass = mono_class_from_mono_type_internal (t);
714 mono_metadata_free_type (t);
716 res->method = mono_class_inflate_generic_method_checked (info->method, context, error);
717 g_assert (is_ok (error)); /* FIXME don't swallow the error */
719 return res;
721 case MONO_RGCTX_INFO_DELEGATE_TRAMP_INFO: {
722 ERROR_DECL (error);
723 MonoDelegateClassMethodPair *dele_info = (MonoDelegateClassMethodPair*)data;
724 MonoDomain *domain = mono_domain_get ();
726 MonoType *t = mono_class_inflate_generic_type_checked (m_class_get_byval_arg (dele_info->klass), context, error);
727 mono_error_assert_msg_ok (error, "Could not inflate generic type"); /* FIXME proper error handling */
729 MonoClass *klass = mono_class_from_mono_type_internal (t);
730 mono_metadata_free_type (t);
732 MonoMethod *method = mono_class_inflate_generic_method_checked (dele_info->method, context, error);
733 mono_error_assert_msg_ok (error, "Could not inflate generic method"); /* FIXME proper error handling */
735 // FIXME: Temporary
736 MonoDelegateClassMethodPair *res = (MonoDelegateClassMethodPair *)mono_domain_alloc0 (domain, sizeof (MonoDelegateClassMethodPair));
737 res->is_virtual = dele_info->is_virtual;
738 res->method = method;
739 res->klass = klass;
740 return res;
743 default:
744 g_assert_not_reached ();
746 /* Not reached, quiet compiler */
747 return NULL;
750 static void
751 free_inflated_info (MonoRgctxInfoType info_type, gpointer info)
753 if (!info)
754 return;
756 switch (info_type) {
757 case MONO_RGCTX_INFO_STATIC_DATA:
758 case MONO_RGCTX_INFO_KLASS:
759 case MONO_RGCTX_INFO_ELEMENT_KLASS:
760 case MONO_RGCTX_INFO_VTABLE:
761 case MONO_RGCTX_INFO_TYPE:
762 case MONO_RGCTX_INFO_REFLECTION_TYPE:
763 case MONO_RGCTX_INFO_CAST_CACHE:
764 mono_metadata_free_type ((MonoType *)info);
765 break;
766 default:
767 break;
771 static MonoRuntimeGenericContextInfoTemplate
772 class_get_rgctx_template_oti (MonoClass *klass, int type_argc, guint32 slot, gboolean temporary, gboolean shared, gboolean *do_free);
774 static MonoClass*
775 class_uninstantiated (MonoClass *klass)
777 if (mono_class_is_ginst (klass))
778 return mono_class_get_generic_class (klass)->container_class;
779 return klass;
783 * get_shared_class:
785 * Return the class used to store information when using generic sharing.
787 static MonoClass*
788 get_shared_class (MonoClass *klass)
790 return class_uninstantiated (klass);
794 * mono_class_get_runtime_generic_context_template:
795 * @class: a class
797 * Looks up or constructs, if necessary, the runtime generic context template for class.
798 * The template is the same for all instantiations of a class.
800 static MonoRuntimeGenericContextTemplate*
801 mono_class_get_runtime_generic_context_template (MonoClass *klass)
803 MonoRuntimeGenericContextTemplate *parent_template, *template_;
804 guint32 i;
806 klass = get_shared_class (klass);
808 mono_loader_lock ();
809 template_ = class_lookup_rgctx_template (klass);
810 mono_loader_unlock ();
812 if (template_)
813 return template_;
815 //g_assert (get_shared_class (class) == class);
817 template_ = alloc_template (klass);
819 mono_loader_lock ();
821 if (m_class_get_parent (klass)) {
822 guint32 num_entries;
823 int max_argc, type_argc;
825 parent_template = mono_class_get_runtime_generic_context_template (m_class_get_parent (klass));
826 max_argc = template_get_max_argc (parent_template);
828 for (type_argc = 0; type_argc <= max_argc; ++type_argc) {
829 num_entries = rgctx_template_num_infos (parent_template, type_argc);
831 /* FIXME: quadratic! */
832 for (i = 0; i < num_entries; ++i) {
833 MonoRuntimeGenericContextInfoTemplate oti;
835 oti = class_get_rgctx_template_oti (m_class_get_parent (klass), type_argc, i, FALSE, FALSE, NULL);
836 if (oti.data && oti.data != MONO_RGCTX_SLOT_USED_MARKER) {
837 rgctx_template_set_slot (m_class_get_image (klass), template_, type_argc, i,
838 oti.data, oti.info_type);
844 if (class_lookup_rgctx_template (klass)) {
845 /* some other thread already set the template */
846 template_ = class_lookup_rgctx_template (klass);
847 } else {
848 class_set_rgctx_template (klass, template_);
850 if (m_class_get_parent (klass))
851 register_generic_subclass (klass);
854 mono_loader_unlock ();
856 return template_;
860 * class_get_rgctx_template_oti:
862 * Return the info template of CLASS numbered TYPE_ARGC/SLOT.
863 * temporary signifies whether the inflated info (oti.data) will be
864 * used temporarily, in which case it might be heap-allocated, or
865 * permanently, in which case it will be mempool-allocated. If
866 * temporary is set then *do_free will return whether the returned
867 * data must be freed.
869 * LOCKING: loader lock
871 static MonoRuntimeGenericContextInfoTemplate
872 class_get_rgctx_template_oti (MonoClass *klass, int type_argc, guint32 slot, gboolean temporary, gboolean shared, gboolean *do_free)
874 g_assert ((temporary && do_free) || (!temporary && !do_free));
876 DEBUG (printf ("get slot: %s %d\n", mono_type_full_name (m_class_get_byval_arg (class)), slot));
878 if (mono_class_is_ginst (klass) && !shared) {
879 MonoRuntimeGenericContextInfoTemplate oti;
880 gboolean tmp_do_free;
882 oti = class_get_rgctx_template_oti (mono_class_get_generic_class (klass)->container_class,
883 type_argc, slot, TRUE, FALSE, &tmp_do_free);
884 if (oti.data) {
885 gpointer info = oti.data;
886 oti.data = inflate_info (&oti, &mono_class_get_generic_class (klass)->context, klass, temporary);
887 if (tmp_do_free)
888 free_inflated_info (oti.info_type, info);
890 if (temporary)
891 *do_free = TRUE;
893 return oti;
894 } else {
895 MonoRuntimeGenericContextTemplate *template_;
896 MonoRuntimeGenericContextInfoTemplate *oti;
898 template_ = mono_class_get_runtime_generic_context_template (klass);
899 oti = rgctx_template_get_other_slot (template_, type_argc, slot);
900 g_assert (oti);
902 if (temporary)
903 *do_free = FALSE;
905 return *oti;
909 // FIXME Consolidate the multiple functions named get_method_nofail.
910 static MonoMethod*
911 get_method_nofail (MonoClass *klass, const char *method_name, int num_params, int flags)
913 MonoMethod *method;
914 ERROR_DECL (error);
915 method = mono_class_get_method_from_name_checked (klass, method_name, num_params, flags, error);
916 mono_error_assert_ok (error);
917 g_assertf (method, "Could not lookup method %s in %s", method_name, m_class_get_name (klass));
918 return method;
921 static gpointer
922 class_type_info (MonoDomain *domain, MonoClass *klass, MonoRgctxInfoType info_type, MonoError *error)
924 error_init (error);
926 switch (info_type) {
927 case MONO_RGCTX_INFO_STATIC_DATA: {
928 MonoVTable *vtable = mono_class_vtable_checked (domain, klass, error);
929 return_val_if_nok (error, NULL);
930 return mono_vtable_get_static_field_data (vtable);
932 case MONO_RGCTX_INFO_KLASS:
933 return klass;
934 case MONO_RGCTX_INFO_ELEMENT_KLASS:
935 return m_class_get_element_class (klass);
936 case MONO_RGCTX_INFO_VTABLE: {
937 MonoVTable *vtable = mono_class_vtable_checked (domain, klass, error);
938 return_val_if_nok (error, NULL);
939 return vtable;
941 case MONO_RGCTX_INFO_CAST_CACHE: {
942 /*First slot is the cache itself, the second the vtable.*/
943 gpointer **cache_data = (gpointer **)mono_domain_alloc0 (domain, sizeof (gpointer) * 2);
944 cache_data [1] = (gpointer *)klass;
945 return cache_data;
947 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
948 return GUINT_TO_POINTER (mono_class_array_element_size (klass));
949 case MONO_RGCTX_INFO_VALUE_SIZE:
950 if (MONO_TYPE_IS_REFERENCE (m_class_get_byval_arg (klass)))
951 return GUINT_TO_POINTER (sizeof (gpointer));
952 else
953 return GUINT_TO_POINTER (mono_class_value_size (klass, NULL));
954 case MONO_RGCTX_INFO_CLASS_SIZEOF: {
955 int align;
956 return GINT_TO_POINTER (mono_type_size (m_class_get_byval_arg (klass), &align));
958 case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
959 if (MONO_TYPE_IS_REFERENCE (m_class_get_byval_arg (klass)))
960 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_REF);
961 else if (mono_class_is_nullable (klass))
962 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_NULLABLE);
963 else
964 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_VTYPE);
965 case MONO_RGCTX_INFO_CLASS_IS_REF_OR_CONTAINS_REFS:
966 mono_class_init_internal (klass);
967 /* Can't return 0 */
968 if (MONO_TYPE_IS_REFERENCE (m_class_get_byval_arg (klass)) || m_class_has_references (klass))
969 return GUINT_TO_POINTER (2);
970 else
971 return GUINT_TO_POINTER (1);
972 case MONO_RGCTX_INFO_MEMCPY:
973 case MONO_RGCTX_INFO_BZERO: {
974 static MonoMethod *memcpy_method [17];
975 static MonoMethod *bzero_method [17];
976 MonoJitDomainInfo *domain_info;
977 int size;
978 guint32 align;
980 domain_info = domain_jit_info (domain);
982 if (MONO_TYPE_IS_REFERENCE (m_class_get_byval_arg (klass))) {
983 size = sizeof (gpointer);
984 align = sizeof (gpointer);
985 } else {
986 size = mono_class_value_size (klass, &align);
989 if (size != 1 && size != 2 && size != 4 && size != 8)
990 size = 0;
991 if (align < size)
992 size = 0;
994 if (info_type == MONO_RGCTX_INFO_MEMCPY) {
995 if (!memcpy_method [size]) {
996 MonoMethod *m;
997 char name [32];
999 if (size == 0)
1000 sprintf (name, "memcpy");
1001 else
1002 sprintf (name, "memcpy_aligned_%d", size);
1003 m = get_method_nofail (mono_defaults.string_class, name, 3, 0);
1004 g_assert (m);
1005 mono_memory_barrier ();
1006 memcpy_method [size] = m;
1008 if (!domain_info->memcpy_addr [size]) {
1009 gpointer addr = mono_compile_method_checked (memcpy_method [size], error);
1010 mono_memory_barrier ();
1011 domain_info->memcpy_addr [size] = (gpointer *)addr;
1012 mono_error_assert_ok (error);
1014 return domain_info->memcpy_addr [size];
1015 } else {
1016 if (!bzero_method [size]) {
1017 MonoMethod *m;
1018 char name [32];
1020 if (size == 0)
1021 sprintf (name, "bzero");
1022 else
1023 sprintf (name, "bzero_aligned_%d", size);
1024 m = get_method_nofail (mono_defaults.string_class, name, 2, 0);
1025 g_assert (m);
1026 mono_memory_barrier ();
1027 bzero_method [size] = m;
1029 if (!domain_info->bzero_addr [size]) {
1030 gpointer addr = mono_compile_method_checked (bzero_method [size], error);
1031 mono_memory_barrier ();
1032 domain_info->bzero_addr [size] = (gpointer *)addr;
1033 mono_error_assert_ok (error);
1035 return domain_info->bzero_addr [size];
1038 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
1039 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: {
1040 MonoMethod *method;
1041 gpointer addr, arg;
1042 MonoJitInfo *ji;
1043 MonoMethodSignature *sig, *gsig;
1044 MonoMethod *gmethod;
1046 if (!mono_class_is_nullable (klass))
1047 /* This can happen since all the entries in MonoGSharedVtMethodInfo are inflated, even those which are not used */
1048 return NULL;
1050 if (info_type == MONO_RGCTX_INFO_NULLABLE_CLASS_BOX)
1051 method = mono_class_get_method_from_name_checked (klass, "Box", 1, 0, error);
1052 else
1053 method = mono_class_get_method_from_name_checked (klass, "Unbox", 1, 0, error);
1055 return_val_if_nok (error, NULL);
1057 addr = mono_jit_compile_method (method, error);
1058 return_val_if_nok (error, NULL);
1060 // The caller uses the gsharedvt call signature
1062 if (mono_llvm_only) {
1063 /* FIXME: We have no access to the gsharedvt signature/gsctx used by the caller, so have to construct it ourselves */
1064 gmethod = mini_get_shared_method_full (method, SHARE_MODE_GSHAREDVT, error);
1065 if (!gmethod)
1066 return NULL;
1067 sig = mono_method_signature_internal (method);
1068 gsig = mono_method_signature_internal (gmethod);
1070 addr = mini_llvmonly_add_method_wrappers (method, addr, TRUE, FALSE, &arg);
1071 return mini_llvmonly_create_ftndesc (domain, addr, arg);
1074 ji = mini_jit_info_table_find (mono_domain_get (), (char *)mono_get_addr_from_ftnptr (addr), NULL);
1075 g_assert (ji);
1076 if (mini_jit_info_is_gsharedvt (ji))
1077 return mono_create_static_rgctx_trampoline (method, addr);
1078 else {
1079 /* Need to add an out wrapper */
1081 /* FIXME: We have no access to the gsharedvt signature/gsctx used by the caller, so have to construct it ourselves */
1082 gmethod = mini_get_shared_method_full (method, SHARE_MODE_GSHAREDVT, error);
1083 if (!gmethod)
1084 return NULL;
1085 sig = mono_method_signature_internal (method);
1086 gsig = mono_method_signature_internal (gmethod);
1088 addr = mini_get_gsharedvt_wrapper (FALSE, addr, sig, gsig, -1, FALSE);
1089 addr = mono_create_static_rgctx_trampoline (method, addr);
1090 return addr;
1093 default:
1094 g_assert_not_reached ();
1096 /* Not reached */
1097 return NULL;
1100 static gboolean
1101 ji_is_gsharedvt (MonoJitInfo *ji)
1103 if (ji && ji->has_generic_jit_info && (mono_jit_info_get_generic_sharing_context (ji)->is_gsharedvt))
1104 return TRUE;
1105 else
1106 return FALSE;
1110 * Describes the information used to construct a gsharedvt arg trampoline.
1112 typedef struct {
1113 gboolean is_in;
1114 gboolean calli;
1115 gint32 vcall_offset;
1116 gpointer addr;
1117 MonoMethodSignature *sig, *gsig;
1118 } GSharedVtTrampInfo;
1120 static guint
1121 tramp_info_hash (gconstpointer key)
1123 GSharedVtTrampInfo *tramp = (GSharedVtTrampInfo *)key;
1125 return (gsize)tramp->addr;
1128 static gboolean
1129 tramp_info_equal (gconstpointer a, gconstpointer b)
1131 GSharedVtTrampInfo *tramp1 = (GSharedVtTrampInfo *)a;
1132 GSharedVtTrampInfo *tramp2 = (GSharedVtTrampInfo *)b;
1134 /* The signatures should be internalized */
1135 return tramp1->is_in == tramp2->is_in && tramp1->calli == tramp2->calli && tramp1->vcall_offset == tramp2->vcall_offset &&
1136 tramp1->addr == tramp2->addr && tramp1->sig == tramp2->sig && tramp1->gsig == tramp2->gsig;
1139 static GENERATE_GET_CLASS_WITH_CACHE (valuetuple_0, "Mono", "ValueTuple");
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);
1173 if (mono_class_has_failure (klass))
1174 return NULL;
1176 int num_fields = mono_class_get_field_count (klass);
1177 MonoClassField *klass_fields = m_class_get_fields (klass);
1179 for (int i = 0; i < num_fields; ++i) {
1180 MonoClassField *field = &klass_fields [i];
1182 if (field->type->attrs & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_HAS_FIELD_RVA))
1183 continue;
1184 MonoType *ftype = get_wrapper_shared_type_full (field->type, TRUE);
1185 if (m_class_is_byreflike (mono_class_from_mono_type_internal (ftype)))
1186 /* Cannot inflate generic params with byreflike types */
1187 return NULL;
1188 args [findex ++] = ftype;
1189 if (findex >= 16)
1190 break;
1192 if (findex > 5)
1193 return NULL;
1195 switch (findex) {
1196 case 0:
1197 tuple_class = mono_class_get_valuetuple_0_class ();
1198 break;
1199 case 1:
1200 tuple_class = mono_class_get_valuetuple_1_class ();
1201 break;
1202 case 2:
1203 tuple_class = mono_class_get_valuetuple_2_class ();
1204 break;
1205 case 3:
1206 tuple_class = mono_class_get_valuetuple_3_class ();
1207 break;
1208 case 4:
1209 tuple_class = mono_class_get_valuetuple_4_class ();
1210 break;
1211 case 5:
1212 tuple_class = mono_class_get_valuetuple_5_class ();
1213 break;
1214 default:
1215 g_assert_not_reached ();
1216 break;
1219 g_assert (tuple_class);
1221 memset (&ctx, 0, sizeof (ctx));
1222 ctx.class_inst = mono_metadata_get_generic_inst (findex, args);
1224 MonoClass *tuple_inst = mono_class_inflate_generic_class_checked (tuple_class, &ctx, error);
1225 mono_error_assert_ok (error);
1227 //printf ("T: %s\n", mono_class_full_name (tuple_inst));
1229 return m_class_get_byval_arg (tuple_inst);
1233 * get_wrapper_shared_type:
1235 * Return a type which is handled identically wrt to calling conventions as T.
1237 static MonoType*
1238 get_wrapper_shared_type_full (MonoType *t, gboolean is_field)
1240 if (t->byref)
1241 return m_class_get_this_arg (mono_defaults.int_class);
1242 t = mini_get_underlying_type (t);
1244 switch (t->type) {
1245 case MONO_TYPE_I1:
1246 /* This removes any attributes etc. */
1247 return m_class_get_byval_arg (mono_defaults.sbyte_class);
1248 case MONO_TYPE_U1:
1249 return m_class_get_byval_arg (mono_defaults.byte_class);
1250 case MONO_TYPE_I2:
1251 return m_class_get_byval_arg (mono_defaults.int16_class);
1252 case MONO_TYPE_U2:
1253 return m_class_get_byval_arg (mono_defaults.uint16_class);
1254 case MONO_TYPE_I4:
1255 return mono_get_int32_type ();
1256 case MONO_TYPE_U4:
1257 return m_class_get_byval_arg (mono_defaults.uint32_class);
1258 case MONO_TYPE_I8:
1259 #if TARGET_SIZEOF_VOID_P == 8
1260 /* Use native int as its already used for byref */
1261 return m_class_get_byval_arg (mono_defaults.int_class);
1262 #else
1263 return m_class_get_byval_arg (mono_defaults.int64_class);
1264 #endif
1265 case MONO_TYPE_U8:
1266 return m_class_get_byval_arg (mono_defaults.uint64_class);
1267 case MONO_TYPE_I:
1268 #if TARGET_SIZEOF_VOID_P == 8
1269 return m_class_get_byval_arg (mono_defaults.int_class);
1270 #else
1271 return m_class_get_byval_arg (mono_defaults.int32_class);
1272 #endif
1273 case MONO_TYPE_U:
1274 #if TARGET_SIZEOF_VOID_P == 8
1275 return m_class_get_byval_arg (mono_defaults.uint64_class);
1276 #else
1277 return m_class_get_byval_arg (mono_defaults.uint32_class);
1278 #endif
1279 case MONO_TYPE_R4:
1280 return m_class_get_byval_arg (mono_defaults.single_class);
1281 case MONO_TYPE_R8:
1282 return m_class_get_byval_arg (mono_defaults.double_class);
1283 case MONO_TYPE_OBJECT:
1284 case MONO_TYPE_CLASS:
1285 case MONO_TYPE_SZARRAY:
1286 case MONO_TYPE_ARRAY:
1287 case MONO_TYPE_PTR:
1288 // FIXME: refs and intptr cannot be shared because
1289 // they are treated differently when a method has a vret arg,
1290 // see get_call_info ().
1291 return mono_get_object_type ();
1292 //return mono_get_int_type ();
1293 case MONO_TYPE_GENERICINST: {
1294 ERROR_DECL (error);
1295 MonoClass *klass;
1296 MonoGenericContext ctx;
1297 MonoGenericContext *orig_ctx;
1298 MonoGenericInst *inst;
1299 MonoType *args [16];
1300 int i;
1302 if (!MONO_TYPE_ISSTRUCT (t))
1303 return get_wrapper_shared_type (mono_get_object_type ());
1305 klass = mono_class_from_mono_type_internal (t);
1306 orig_ctx = &mono_class_get_generic_class (klass)->context;
1308 memset (&ctx, 0, sizeof (MonoGenericContext));
1310 inst = orig_ctx->class_inst;
1311 if (inst) {
1312 g_assert (inst->type_argc < 16);
1313 for (i = 0; i < inst->type_argc; ++i)
1314 args [i] = get_wrapper_shared_type_full (inst->type_argv [i], TRUE);
1315 ctx.class_inst = mono_metadata_get_generic_inst (inst->type_argc, args);
1317 inst = orig_ctx->method_inst;
1318 if (inst) {
1319 g_assert (inst->type_argc < 16);
1320 for (i = 0; i < inst->type_argc; ++i)
1321 args [i] = get_wrapper_shared_type_full (inst->type_argv [i], TRUE);
1322 ctx.method_inst = mono_metadata_get_generic_inst (inst->type_argc, args);
1324 klass = mono_class_inflate_generic_class_checked (mono_class_get_generic_class (klass)->container_class, &ctx, error);
1325 mono_error_assert_ok (error); /* FIXME don't swallow the error */
1327 t = m_class_get_byval_arg (klass);
1328 MonoType *shared_type = get_wrapper_shared_vtype (t);
1329 if (shared_type)
1330 t = shared_type;
1331 return t;
1333 case MONO_TYPE_VALUETYPE: {
1334 MonoType *shared_type = get_wrapper_shared_vtype (t);
1335 if (shared_type)
1336 t = shared_type;
1337 return t;
1339 default:
1340 break;
1343 //printf ("%s\n", mono_type_full_name (t));
1344 return t;
1347 static MonoType*
1348 get_wrapper_shared_type (MonoType *t)
1350 return get_wrapper_shared_type_full (t, FALSE);
1354 /* Returns the intptr type for types that are passed in a single register */
1355 static MonoType*
1356 get_wrapper_shared_type_reg (MonoType *t, gboolean pinvoke)
1358 MonoType *orig_t = t;
1360 t = get_wrapper_shared_type (t);
1361 if (t->byref)
1362 return t;
1364 switch (t->type) {
1365 case MONO_TYPE_BOOLEAN:
1366 case MONO_TYPE_CHAR:
1367 case MONO_TYPE_I1:
1368 case MONO_TYPE_U1:
1369 case MONO_TYPE_I2:
1370 case MONO_TYPE_U2:
1371 case MONO_TYPE_I4:
1372 case MONO_TYPE_U4:
1373 case MONO_TYPE_I:
1374 case MONO_TYPE_U:
1375 #if TARGET_SIZEOF_VOID_P == 8
1376 case MONO_TYPE_I8:
1377 case MONO_TYPE_U8:
1378 return mono_get_int_type ();
1379 #endif
1380 case MONO_TYPE_OBJECT:
1381 case MONO_TYPE_STRING:
1382 case MONO_TYPE_CLASS:
1383 case MONO_TYPE_SZARRAY:
1384 case MONO_TYPE_ARRAY:
1385 case MONO_TYPE_PTR:
1386 return mono_get_int_type ();
1387 case MONO_TYPE_GENERICINST:
1388 if (orig_t->type == MONO_TYPE_VALUETYPE && pinvoke)
1390 * These are translated to instances of Mono.ValueTuple, but generic types
1391 * cannot be passed in pinvoke.
1393 return orig_t;
1394 else
1395 return t;
1396 default:
1397 return t;
1401 static MonoMethodSignature*
1402 mini_get_underlying_reg_signature (MonoMethodSignature *sig)
1404 MonoMethodSignature *res = mono_metadata_signature_dup (sig);
1405 int i;
1407 res->ret = get_wrapper_shared_type_reg (sig->ret, sig->pinvoke);
1408 for (i = 0; i < sig->param_count; ++i)
1409 res->params [i] = get_wrapper_shared_type_reg (sig->params [i], sig->pinvoke);
1410 res->generic_param_count = 0;
1411 res->is_inflated = 0;
1413 return res;
1416 static MonoMethodSignature*
1417 mini_get_underlying_signature (MonoMethodSignature *sig)
1419 MonoMethodSignature *res = mono_metadata_signature_dup (sig);
1420 int i;
1422 res->ret = get_wrapper_shared_type (sig->ret);
1423 for (i = 0; i < sig->param_count; ++i)
1424 res->params [i] = get_wrapper_shared_type (sig->params [i]);
1425 res->generic_param_count = 0;
1426 res->is_inflated = 0;
1428 return res;
1432 * mini_get_gsharedvt_in_sig_wrapper:
1434 * Return a wrapper to translate between the normal and gsharedvt calling conventions of SIG.
1435 * The returned wrapper has a signature of SIG, plus one extra argument, which is an <addr, rgctx> pair.
1436 * The extra argument is passed the same way as an rgctx to shared methods.
1437 * It calls <addr> using the gsharedvt version of SIG, passing in <rgctx> as an extra argument.
1439 MonoMethod*
1440 mini_get_gsharedvt_in_sig_wrapper (MonoMethodSignature *sig)
1442 MonoMethodBuilder *mb;
1443 MonoMethod *res, *cached;
1444 WrapperInfo *info;
1445 MonoMethodSignature *csig, *gsharedvt_sig;
1446 int i, pindex;
1447 static GHashTable *cache;
1449 // FIXME: Memory management
1450 sig = mini_get_underlying_signature (sig);
1452 // FIXME: Normal cache
1453 gshared_lock ();
1454 if (!cache)
1455 cache = g_hash_table_new_full ((GHashFunc)mono_signature_hash, (GEqualFunc)mono_metadata_signature_equal, NULL, NULL);
1456 res = (MonoMethod*)g_hash_table_lookup (cache, sig);
1457 gshared_unlock ();
1458 if (res) {
1459 g_free (sig);
1460 return res;
1463 /* Create the signature for the wrapper */
1464 // FIXME:
1465 csig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 1) * sizeof (MonoType*)));
1466 memcpy (csig, sig, mono_metadata_signature_size (sig));
1467 csig->param_count ++;
1468 csig->params [sig->param_count] = mono_get_int_type ();
1469 #ifdef ENABLE_ILGEN
1470 char ** const param_names = g_new0 (char*, csig->param_count);
1471 for (int i = 0; i < sig->param_count; ++i)
1472 param_names [i] = g_strdup_printf ("%d", i);
1473 param_names [sig->param_count] = g_strdup ("ftndesc");
1474 #endif
1476 /* Create the signature for the gsharedvt callconv */
1477 gsharedvt_sig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 2) * sizeof (MonoType*)));
1478 memcpy (gsharedvt_sig, sig, mono_metadata_signature_size (sig));
1479 pindex = 0;
1480 /* The return value is returned using an explicit vret argument */
1481 if (sig->ret->type != MONO_TYPE_VOID) {
1482 gsharedvt_sig->params [pindex ++] = mono_get_int_type ();
1483 gsharedvt_sig->ret = mono_get_void_type ();
1485 for (i = 0; i < sig->param_count; i++) {
1486 gsharedvt_sig->params [pindex] = sig->params [i];
1487 if (!sig->params [i]->byref) {
1488 gsharedvt_sig->params [pindex] = mono_metadata_type_dup (NULL, gsharedvt_sig->params [pindex]);
1489 gsharedvt_sig->params [pindex]->byref = 1;
1491 pindex ++;
1493 /* Rgctx arg */
1494 gsharedvt_sig->params [pindex ++] = mono_get_int_type ();
1495 gsharedvt_sig->param_count = pindex;
1497 // FIXME: Use shared signatures
1498 mb = mono_mb_new (mono_defaults.object_class, sig->hasthis ? "gsharedvt_in_sig" : "gsharedvt_in_sig_static", MONO_WRAPPER_OTHER);
1499 #ifdef ENABLE_ILGEN
1500 mono_mb_set_param_names (mb, (const char**)param_names);
1501 #endif
1503 #ifndef DISABLE_JIT
1504 int retval_var = 0;
1505 if (sig->ret->type != MONO_TYPE_VOID)
1506 retval_var = mono_mb_add_local (mb, sig->ret);
1508 /* Make the call */
1509 if (sig->hasthis)
1510 mono_mb_emit_ldarg (mb, 0);
1511 if (sig->ret->type != MONO_TYPE_VOID)
1512 mono_mb_emit_ldloc_addr (mb, retval_var);
1513 for (i = 0; i < sig->param_count; i++) {
1514 if (sig->params [i]->byref)
1515 mono_mb_emit_ldarg (mb, i + (sig->hasthis == TRUE));
1516 else
1517 mono_mb_emit_ldarg_addr (mb, i + (sig->hasthis == TRUE));
1519 /* Rgctx arg */
1520 mono_mb_emit_ldarg (mb, sig->param_count + (sig->hasthis ? 1 : 0));
1521 mono_mb_emit_icon (mb, TARGET_SIZEOF_VOID_P);
1522 mono_mb_emit_byte (mb, CEE_ADD);
1523 mono_mb_emit_byte (mb, CEE_LDIND_I);
1524 /* Method to call */
1525 mono_mb_emit_ldarg (mb, sig->param_count + (sig->hasthis ? 1 : 0));
1526 mono_mb_emit_byte (mb, CEE_LDIND_I);
1527 mono_mb_emit_calli (mb, gsharedvt_sig);
1528 if (sig->ret->type != MONO_TYPE_VOID)
1529 mono_mb_emit_ldloc (mb, retval_var);
1530 mono_mb_emit_byte (mb, CEE_RET);
1531 #endif
1533 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_GSHAREDVT_IN_SIG);
1534 info->d.gsharedvt.sig = sig;
1536 res = mono_mb_create (mb, csig, sig->param_count + 16, info);
1537 #ifdef ENABLE_ILGEN
1538 for (int i = 0; i < sig->param_count + 1; ++i)
1539 g_free (param_names [i]);
1540 g_free (param_names);
1541 #endif
1543 gshared_lock ();
1544 cached = (MonoMethod*)g_hash_table_lookup (cache, sig);
1545 if (cached)
1546 res = cached;
1547 else
1548 g_hash_table_insert (cache, sig, res);
1549 gshared_unlock ();
1550 return res;
1554 * mini_get_gsharedvt_out_sig_wrapper:
1556 * Same as in_sig_wrapper, but translate between the gsharedvt and normal signatures.
1558 MonoMethod*
1559 mini_get_gsharedvt_out_sig_wrapper (MonoMethodSignature *sig)
1561 MonoMethodBuilder *mb;
1562 MonoMethod *res, *cached;
1563 WrapperInfo *info;
1564 MonoMethodSignature *normal_sig, *csig;
1565 int i, pindex, args_start;
1566 static GHashTable *cache;
1568 // FIXME: Memory management
1569 sig = mini_get_underlying_signature (sig);
1571 // FIXME: Normal cache
1572 gshared_lock ();
1573 if (!cache)
1574 cache = g_hash_table_new_full ((GHashFunc)mono_signature_hash, (GEqualFunc)mono_metadata_signature_equal, NULL, NULL);
1575 res = (MonoMethod*)g_hash_table_lookup (cache, sig);
1576 gshared_unlock ();
1577 if (res) {
1578 g_free (sig);
1579 return res;
1582 /* Create the signature for the wrapper */
1583 // FIXME:
1584 csig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 2) * sizeof (MonoType*)));
1585 memcpy (csig, sig, mono_metadata_signature_size (sig));
1586 pindex = 0;
1587 char ** const param_names = g_new0 (char*, sig->param_count + 2);
1588 /* The return value is returned using an explicit vret argument */
1589 if (sig->ret->type != MONO_TYPE_VOID) {
1590 csig->params [pindex] = mono_get_int_type ();
1591 csig->ret = mono_get_void_type ();
1592 param_names [pindex] = g_strdup ("vret");
1593 pindex ++;
1595 args_start = pindex;
1596 if (sig->hasthis)
1597 args_start ++;
1598 for (i = 0; i < sig->param_count; i++) {
1599 csig->params [pindex] = sig->params [i];
1600 param_names [pindex] = g_strdup_printf ("%d", i);
1601 if (!sig->params [i]->byref) {
1602 csig->params [pindex] = mono_metadata_type_dup (NULL, csig->params [pindex]);
1603 csig->params [pindex]->byref = 1;
1605 pindex ++;
1607 /* Rgctx arg */
1608 csig->params [pindex] = mono_get_int_type ();
1609 param_names [pindex] = g_strdup ("ftndesc");
1610 pindex ++;
1611 csig->param_count = pindex;
1613 /* Create the signature for the normal callconv */
1614 normal_sig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 2) * sizeof (MonoType*)));
1615 memcpy (normal_sig, sig, mono_metadata_signature_size (sig));
1616 normal_sig->param_count ++;
1617 normal_sig->params [sig->param_count] = mono_get_int_type ();
1619 // FIXME: Use shared signatures
1620 mb = mono_mb_new (mono_defaults.object_class, "gsharedvt_out_sig", MONO_WRAPPER_OTHER);
1621 #ifdef ENABLE_ILGEN
1622 mono_mb_set_param_names (mb, (const char**)param_names);
1623 #endif
1625 #ifndef DISABLE_JIT
1626 int ldind_op, stind_op;
1627 if (sig->ret->type != MONO_TYPE_VOID)
1628 /* Load return address */
1629 mono_mb_emit_ldarg (mb, sig->hasthis ? 1 : 0);
1631 /* Make the call */
1632 if (sig->hasthis)
1633 mono_mb_emit_ldarg (mb, 0);
1634 for (i = 0; i < sig->param_count; i++) {
1635 if (sig->params [i]->byref) {
1636 mono_mb_emit_ldarg (mb, args_start + i);
1637 } else {
1638 ldind_op = mono_type_to_ldind (sig->params [i]);
1639 mono_mb_emit_ldarg (mb, args_start + i);
1640 // FIXME:
1641 if (ldind_op == CEE_LDOBJ)
1642 mono_mb_emit_op (mb, CEE_LDOBJ, mono_class_from_mono_type_internal (sig->params [i]));
1643 else
1644 mono_mb_emit_byte (mb, ldind_op);
1647 /* Rgctx arg */
1648 mono_mb_emit_ldarg (mb, args_start + sig->param_count);
1649 mono_mb_emit_icon (mb, TARGET_SIZEOF_VOID_P);
1650 mono_mb_emit_byte (mb, CEE_ADD);
1651 mono_mb_emit_byte (mb, CEE_LDIND_I);
1652 /* Method to call */
1653 mono_mb_emit_ldarg (mb, args_start + sig->param_count);
1654 mono_mb_emit_byte (mb, CEE_LDIND_I);
1655 mono_mb_emit_calli (mb, normal_sig);
1656 if (sig->ret->type != MONO_TYPE_VOID) {
1657 /* Store return value */
1658 stind_op = mono_type_to_stind (sig->ret);
1659 // FIXME:
1660 if (stind_op == CEE_STOBJ)
1661 mono_mb_emit_op (mb, CEE_STOBJ, mono_class_from_mono_type_internal (sig->ret));
1662 else if (stind_op == CEE_STIND_REF)
1663 /* Avoid write barriers, the vret arg points to the stack */
1664 mono_mb_emit_byte (mb, CEE_STIND_I);
1665 else
1666 mono_mb_emit_byte (mb, stind_op);
1668 mono_mb_emit_byte (mb, CEE_RET);
1669 #endif
1671 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_GSHAREDVT_OUT_SIG);
1672 info->d.gsharedvt.sig = sig;
1674 res = mono_mb_create (mb, csig, sig->param_count + 16, info);
1675 for (int i = 0; i < sig->param_count + 1; ++i)
1676 g_free (param_names [i]);
1677 g_free (param_names);
1679 gshared_lock ();
1680 cached = (MonoMethod*)g_hash_table_lookup (cache, sig);
1681 if (cached)
1682 res = cached;
1683 else
1684 g_hash_table_insert (cache, sig, res);
1685 gshared_unlock ();
1686 return res;
1689 static gboolean
1690 signature_equal_pinvoke (MonoMethodSignature *sig1, MonoMethodSignature *sig2)
1692 /* mono_metadata_signature_equal () doesn't do this check */
1693 if (sig1->pinvoke != sig2->pinvoke)
1694 return FALSE;
1695 return mono_metadata_signature_equal (sig1, sig2);
1699 * mini_get_interp_in_wrapper:
1701 * Return a wrapper which can be used to transition from compiled code to the interpreter.
1702 * The wrapper has the same signature as SIG. It is very similar to a gsharedvt_in wrapper,
1703 * except the 'extra_arg' is passed in the rgctx reg, so this wrapper needs to be
1704 * called through a static rgctx trampoline.
1705 * FIXME: Move this elsewhere.
1707 MonoMethod*
1708 mini_get_interp_in_wrapper (MonoMethodSignature *sig)
1710 MonoMethodBuilder *mb;
1711 MonoMethod *res, *cached;
1712 WrapperInfo *info;
1713 MonoMethodSignature *csig, *entry_sig;
1714 int i, pindex;
1715 static GHashTable *cache;
1716 const char *name;
1717 gboolean generic = FALSE;
1718 gboolean return_native_struct;
1720 sig = mini_get_underlying_reg_signature (sig);
1722 gshared_lock ();
1723 if (!cache)
1724 cache = g_hash_table_new_full ((GHashFunc)mono_signature_hash, (GEqualFunc)signature_equal_pinvoke, NULL, NULL);
1725 res = (MonoMethod*)g_hash_table_lookup (cache, sig);
1726 gshared_unlock ();
1727 if (res) {
1728 g_free (sig);
1729 return res;
1732 if (sig->param_count > MAX_INTERP_ENTRY_ARGS)
1733 /* Call the generic interpreter entry point, the specialized ones only handle a limited number of arguments */
1734 generic = TRUE;
1737 * If we need to return a native struct, we can't allocate a local and store it
1738 * there since that assumes a managed representation. Instead we allocate on the
1739 * stack, pass this address to the interp_entry and when we return it we use
1740 * CEE_MONO_LDNATIVEOBJ
1742 return_native_struct = sig->ret->type == MONO_TYPE_VALUETYPE && sig->pinvoke;
1744 /* Create the signature for the wrapper */
1745 csig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + (sig->param_count * sizeof (MonoType*)));
1746 memcpy (csig, sig, mono_metadata_signature_size (sig));
1748 for (i = 0; i < sig->param_count; i++) {
1749 if (sig->params [i]->byref)
1750 csig->params [i] = m_class_get_this_arg (mono_defaults.int_class);
1753 MonoType *int_type = mono_get_int_type ();
1754 /* Create the signature for the callee callconv */
1755 if (generic) {
1757 * The called function has the following signature:
1758 * interp_entry_general (gpointer this_arg, gpointer res, gpointer *args, gpointer rmethod)
1760 entry_sig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + (4 * sizeof (MonoType*)));
1761 entry_sig->ret = mono_get_void_type ();
1762 entry_sig->param_count = 4;
1763 entry_sig->params [0] = int_type;
1764 entry_sig->params [1] = int_type;
1765 entry_sig->params [2] = int_type;
1766 entry_sig->params [3] = int_type;
1767 name = "interp_in_generic";
1768 generic = TRUE;
1769 } else {
1771 * The called function has the following signature:
1772 * void entry(<optional this ptr>, <optional return ptr>, <arguments>, <extra arg>)
1774 entry_sig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 2) * sizeof (MonoType*)));
1775 memcpy (entry_sig, sig, mono_metadata_signature_size (sig));
1776 pindex = 0;
1777 /* The return value is returned using an explicit vret argument */
1778 if (sig->ret->type != MONO_TYPE_VOID) {
1779 entry_sig->params [pindex ++] = int_type;
1780 entry_sig->ret = mono_get_void_type ();
1782 for (i = 0; i < sig->param_count; i++) {
1783 entry_sig->params [pindex] = sig->params [i];
1784 if (!sig->params [i]->byref) {
1785 entry_sig->params [pindex] = mono_metadata_type_dup (NULL, entry_sig->params [pindex]);
1786 entry_sig->params [pindex]->byref = 1;
1788 pindex ++;
1790 /* Extra arg */
1791 entry_sig->params [pindex ++] = int_type;
1792 entry_sig->param_count = pindex;
1793 name = sig->hasthis ? "interp_in" : "interp_in_static";
1796 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_OTHER);
1799 * This is needed to be able to unwind out of interpreted code to managed.
1800 * When we are called from native code we can't unwind and we might also not
1801 * be attached.
1803 if (!sig->pinvoke)
1804 mb->method->save_lmf = 1;
1806 #ifndef DISABLE_JIT
1807 int retval_var = 0;
1808 if (return_native_struct) {
1809 retval_var = mono_mb_add_local (mb, int_type);
1810 mono_mb_emit_icon (mb, mono_class_native_size (sig->ret->data.klass, NULL));
1811 mono_mb_emit_byte (mb, CEE_PREFIX1);
1812 mono_mb_emit_byte (mb, CEE_LOCALLOC);
1813 mono_mb_emit_stloc (mb, retval_var);
1814 } else if (sig->ret->type != MONO_TYPE_VOID) {
1815 retval_var = mono_mb_add_local (mb, sig->ret);
1818 /* Make the call */
1819 if (generic) {
1820 /* Collect arguments */
1821 int args_var = mono_mb_add_local (mb, int_type);
1823 mono_mb_emit_icon (mb, TARGET_SIZEOF_VOID_P * sig->param_count);
1824 mono_mb_emit_byte (mb, CEE_PREFIX1);
1825 mono_mb_emit_byte (mb, CEE_LOCALLOC);
1826 mono_mb_emit_stloc (mb, args_var);
1828 for (i = 0; i < sig->param_count; i++) {
1829 mono_mb_emit_ldloc (mb, args_var);
1830 mono_mb_emit_icon (mb, TARGET_SIZEOF_VOID_P * i);
1831 mono_mb_emit_byte (mb, CEE_ADD);
1832 if (sig->params [i]->byref)
1833 mono_mb_emit_ldarg (mb, i + (sig->hasthis == TRUE));
1834 else
1835 mono_mb_emit_ldarg_addr (mb, i + (sig->hasthis == TRUE));
1836 mono_mb_emit_byte (mb, CEE_STIND_I);
1839 if (sig->hasthis)
1840 mono_mb_emit_ldarg (mb, 0);
1841 else
1842 mono_mb_emit_byte (mb, CEE_LDNULL);
1843 if (return_native_struct)
1844 mono_mb_emit_ldloc (mb, retval_var);
1845 else if (sig->ret->type != MONO_TYPE_VOID)
1846 mono_mb_emit_ldloc_addr (mb, retval_var);
1847 else
1848 mono_mb_emit_byte (mb, CEE_LDNULL);
1849 mono_mb_emit_ldloc (mb, args_var);
1850 } else {
1851 if (sig->hasthis)
1852 mono_mb_emit_ldarg (mb, 0);
1853 if (return_native_struct)
1854 mono_mb_emit_ldloc (mb, retval_var);
1855 else if (sig->ret->type != MONO_TYPE_VOID)
1856 mono_mb_emit_ldloc_addr (mb, retval_var);
1857 for (i = 0; i < sig->param_count; i++) {
1858 if (sig->params [i]->byref)
1859 mono_mb_emit_ldarg (mb, i + (sig->hasthis == TRUE));
1860 else
1861 mono_mb_emit_ldarg_addr (mb, i + (sig->hasthis == TRUE));
1864 /* Extra arg */
1865 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1866 mono_mb_emit_byte (mb, CEE_MONO_GET_RGCTX_ARG);
1867 mono_mb_emit_icon (mb, TARGET_SIZEOF_VOID_P);
1868 mono_mb_emit_byte (mb, CEE_ADD);
1869 mono_mb_emit_byte (mb, CEE_LDIND_I);
1870 /* Method to call */
1871 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1872 mono_mb_emit_byte (mb, CEE_MONO_GET_RGCTX_ARG);
1873 mono_mb_emit_byte (mb, CEE_LDIND_I);
1874 mono_mb_emit_calli (mb, entry_sig);
1876 if (return_native_struct) {
1877 mono_mb_emit_ldloc (mb, retval_var);
1878 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1879 mono_mb_emit_op (mb, CEE_MONO_LDNATIVEOBJ, sig->ret->data.klass);
1880 } else if (sig->ret->type != MONO_TYPE_VOID) {
1881 mono_mb_emit_ldloc (mb, retval_var);
1883 mono_mb_emit_byte (mb, CEE_RET);
1884 #endif
1886 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_INTERP_IN);
1887 info->d.interp_in.sig = csig;
1889 res = mono_mb_create (mb, csig, sig->param_count + 16, info);
1891 gshared_lock ();
1892 cached = (MonoMethod*)g_hash_table_lookup (cache, sig);
1893 if (cached) {
1894 mono_free_method (res);
1895 res = cached;
1896 } else {
1897 g_hash_table_insert (cache, sig, res);
1899 gshared_unlock ();
1900 mono_mb_free (mb);
1902 return res;
1906 * This wrapper enables EH to resume directly to the code calling it. It is
1907 * needed so EH can resume directly into jitted code from interp, or into interp
1908 * when it needs to jump over native frames.
1910 MonoMethod*
1911 mini_get_interp_lmf_wrapper (const char *name, gpointer target)
1913 static MonoMethod *cache [2];
1914 g_assert (target == (gpointer)mono_interp_to_native_trampoline || target == (gpointer)mono_interp_entry_from_trampoline);
1915 const int index = target == (gpointer)mono_interp_to_native_trampoline;
1916 const MonoJitICallId jit_icall_id = index ? MONO_JIT_ICALL_mono_interp_to_native_trampoline : MONO_JIT_ICALL_mono_interp_entry_from_trampoline;
1918 MonoMethod *res, *cached;
1919 MonoMethodSignature *sig;
1920 MonoMethodBuilder *mb;
1921 WrapperInfo *info;
1923 gshared_lock ();
1925 res = cache [index];
1927 gshared_unlock ();
1929 if (res)
1930 return res;
1932 MonoType *int_type = mono_get_int_type ();
1934 char *wrapper_name = g_strdup_printf ("__interp_lmf_%s", name);
1935 mb = mono_mb_new (mono_defaults.object_class, wrapper_name, MONO_WRAPPER_OTHER);
1937 sig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
1938 sig->ret = mono_get_void_type ();
1939 sig->params [0] = int_type;
1940 sig->params [1] = int_type;
1942 /* This is the only thing that the wrapper needs to do */
1943 mb->method->save_lmf = 1;
1945 #ifndef DISABLE_JIT
1946 mono_mb_emit_byte (mb, CEE_LDARG_0);
1947 mono_mb_emit_byte (mb, CEE_LDARG_1);
1949 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1950 mono_mb_emit_byte (mb, CEE_MONO_ICALL);
1951 mono_mb_emit_i4 (mb, jit_icall_id);
1953 mono_mb_emit_byte (mb, CEE_RET);
1954 #endif
1955 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_INTERP_LMF);
1956 info->d.icall.jit_icall_id = jit_icall_id;
1957 res = mono_mb_create (mb, sig, 4, info);
1959 gshared_lock ();
1960 cached = cache [index];
1961 if (cached) {
1962 mono_free_method (res);
1963 res = cached;
1964 } else {
1965 cache [index] = res;
1967 gshared_unlock ();
1968 mono_mb_free (mb);
1970 g_free (wrapper_name);
1972 return res;
1975 MonoMethodSignature*
1976 mini_get_gsharedvt_out_sig_wrapper_signature (gboolean has_this, gboolean has_ret, int param_count)
1978 MonoMethodSignature *sig = g_malloc0 (sizeof (MonoMethodSignature) + ((param_count + 3) * sizeof (MonoType*)));
1979 int i, pindex;
1980 MonoType *int_type = mono_get_int_type ();
1982 sig->ret = mono_get_void_type ();
1983 sig->sentinelpos = -1;
1984 pindex = 0;
1985 if (has_this)
1986 /* this */
1987 sig->params [pindex ++] = int_type;
1988 if (has_ret)
1989 /* vret */
1990 sig->params [pindex ++] = int_type;
1991 for (i = 0; i < param_count; ++i)
1992 /* byref arguments */
1993 sig->params [pindex ++] = int_type;
1994 /* extra arg */
1995 sig->params [pindex ++] = int_type;
1996 sig->param_count = pindex;
1998 return sig;
2002 * mini_get_gsharedvt_wrapper:
2004 * Return a gsharedvt in/out wrapper for calling ADDR.
2006 gpointer
2007 mini_get_gsharedvt_wrapper (gboolean gsharedvt_in, gpointer addr, MonoMethodSignature *normal_sig, MonoMethodSignature *gsharedvt_sig, gint32 vcall_offset, gboolean calli)
2009 ERROR_DECL (error);
2010 gpointer res, info;
2011 MonoDomain *domain = mono_domain_get ();
2012 MonoJitDomainInfo *domain_info;
2013 GSharedVtTrampInfo *tramp_info;
2014 GSharedVtTrampInfo tinfo;
2016 if (mono_llvm_only) {
2017 MonoMethod *wrapper;
2019 if (gsharedvt_in)
2020 wrapper = mini_get_gsharedvt_in_sig_wrapper (normal_sig);
2021 else
2022 wrapper = mini_get_gsharedvt_out_sig_wrapper (normal_sig);
2023 res = mono_compile_method_checked (wrapper, error);
2024 mono_error_assert_ok (error);
2025 return res;
2028 memset (&tinfo, 0, sizeof (tinfo));
2029 tinfo.is_in = gsharedvt_in;
2030 tinfo.calli = calli;
2031 tinfo.vcall_offset = vcall_offset;
2032 tinfo.addr = addr;
2033 tinfo.sig = normal_sig;
2034 tinfo.gsig = gsharedvt_sig;
2036 domain_info = domain_jit_info (domain);
2039 * The arg trampolines might only have a finite number in full-aot, so use a cache.
2041 mono_domain_lock (domain);
2042 if (!domain_info->gsharedvt_arg_tramp_hash)
2043 domain_info->gsharedvt_arg_tramp_hash = g_hash_table_new (tramp_info_hash, tramp_info_equal);
2044 res = g_hash_table_lookup (domain_info->gsharedvt_arg_tramp_hash, &tinfo);
2045 mono_domain_unlock (domain);
2046 if (res)
2047 return res;
2049 info = mono_arch_get_gsharedvt_call_info (addr, normal_sig, gsharedvt_sig, gsharedvt_in, vcall_offset, calli);
2051 if (gsharedvt_in) {
2052 static gpointer tramp_addr;
2053 MonoMethod *wrapper;
2055 if (!tramp_addr) {
2056 wrapper = mono_marshal_get_gsharedvt_in_wrapper ();
2057 addr = mono_compile_method_checked (wrapper, error);
2058 mono_memory_barrier ();
2059 mono_error_assert_ok (error);
2060 tramp_addr = addr;
2062 addr = tramp_addr;
2063 } else {
2064 static gpointer tramp_addr;
2065 MonoMethod *wrapper;
2067 if (!tramp_addr) {
2068 wrapper = mono_marshal_get_gsharedvt_out_wrapper ();
2069 addr = mono_compile_method_checked (wrapper, error);
2070 mono_memory_barrier ();
2071 mono_error_assert_ok (error);
2072 tramp_addr = addr;
2074 addr = tramp_addr;
2077 if (mono_aot_only)
2078 addr = mono_aot_get_gsharedvt_arg_trampoline (info, addr);
2079 else
2080 addr = mono_arch_get_gsharedvt_arg_trampoline (mono_domain_get (), info, addr);
2082 mono_atomic_inc_i32 (&gsharedvt_num_trampolines);
2084 /* Cache it */
2085 tramp_info = (GSharedVtTrampInfo *)mono_domain_alloc0 (domain, sizeof (GSharedVtTrampInfo));
2086 *tramp_info = tinfo;
2088 mono_domain_lock (domain);
2089 /* Duplicates are not a problem */
2090 g_hash_table_insert (domain_info->gsharedvt_arg_tramp_hash, tramp_info, addr);
2091 mono_domain_unlock (domain);
2093 return addr;
2097 * instantiate_info:
2099 * Instantiate the info given by OTI for context CONTEXT.
2101 static gpointer
2102 instantiate_info (MonoDomain *domain, MonoRuntimeGenericContextInfoTemplate *oti,
2103 MonoGenericContext *context, MonoClass *klass, MonoError *error)
2105 gpointer data;
2106 gboolean temporary;
2108 error_init (error);
2110 if (!oti->data)
2111 return NULL;
2113 switch (oti->info_type) {
2114 case MONO_RGCTX_INFO_STATIC_DATA:
2115 case MONO_RGCTX_INFO_KLASS:
2116 case MONO_RGCTX_INFO_ELEMENT_KLASS:
2117 case MONO_RGCTX_INFO_VTABLE:
2118 case MONO_RGCTX_INFO_CAST_CACHE:
2119 temporary = TRUE;
2120 break;
2121 default:
2122 temporary = FALSE;
2125 data = inflate_info (oti, context, klass, temporary);
2127 switch (oti->info_type) {
2128 case MONO_RGCTX_INFO_STATIC_DATA:
2129 case MONO_RGCTX_INFO_KLASS:
2130 case MONO_RGCTX_INFO_ELEMENT_KLASS:
2131 case MONO_RGCTX_INFO_VTABLE:
2132 case MONO_RGCTX_INFO_CAST_CACHE:
2133 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
2134 case MONO_RGCTX_INFO_VALUE_SIZE:
2135 case MONO_RGCTX_INFO_CLASS_SIZEOF:
2136 case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
2137 case MONO_RGCTX_INFO_CLASS_IS_REF_OR_CONTAINS_REFS:
2138 case MONO_RGCTX_INFO_MEMCPY:
2139 case MONO_RGCTX_INFO_BZERO:
2140 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
2141 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: {
2142 MonoClass *arg_class = mono_class_from_mono_type_internal ((MonoType *)data);
2144 free_inflated_info (oti->info_type, data);
2145 g_assert (arg_class);
2147 /* The class might be used as an argument to
2148 mono_value_copy(), which requires that its GC
2149 descriptor has been computed. */
2150 if (oti->info_type == MONO_RGCTX_INFO_KLASS)
2151 mono_class_compute_gc_descriptor (arg_class);
2153 return class_type_info (domain, arg_class, oti->info_type, error);
2155 case MONO_RGCTX_INFO_TYPE:
2156 return data;
2157 case MONO_RGCTX_INFO_REFLECTION_TYPE: {
2158 MonoReflectionType *ret = mono_type_get_object_checked (domain, (MonoType *)data, error);
2160 return ret;
2162 case MONO_RGCTX_INFO_METHOD:
2163 return data;
2164 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE: {
2165 MonoMethod *m = (MonoMethod*)data;
2166 gpointer addr;
2168 g_assert (!mono_llvm_only);
2169 addr = mono_compile_method_checked (m, error);
2170 return_val_if_nok (error, NULL);
2171 return mini_add_method_trampoline (m, addr, mono_method_needs_static_rgctx_invoke (m, FALSE), FALSE);
2173 case MONO_RGCTX_INFO_METHOD_FTNDESC: {
2174 MonoMethod *m = (MonoMethod*)data;
2176 /* Returns an ftndesc */
2177 g_assert (mono_llvm_only);
2178 MonoJumpInfo ji;
2179 ji.type = MONO_PATCH_INFO_METHOD_FTNDESC;
2180 ji.data.method = m;
2181 return mono_resolve_patch_target (m, domain, NULL, &ji, FALSE, error);
2183 case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER: {
2184 MonoMethod *m = (MonoMethod*)data;
2185 gpointer addr;
2186 gpointer arg = NULL;
2188 g_assert (mono_llvm_only);
2190 addr = mono_compile_method_checked (m, error);
2191 return_val_if_nok (error, NULL);
2193 MonoJitInfo *ji;
2194 gboolean callee_gsharedvt;
2196 ji = mini_jit_info_table_find (mono_domain_get (), (char *)mono_get_addr_from_ftnptr (addr), NULL);
2197 g_assert (ji);
2198 callee_gsharedvt = mini_jit_info_is_gsharedvt (ji);
2199 if (callee_gsharedvt)
2200 callee_gsharedvt = mini_is_gsharedvt_variable_signature (mono_method_signature_internal (jinfo_get_method (ji)));
2201 if (callee_gsharedvt) {
2202 /* No need for a wrapper */
2203 return mini_llvmonly_create_ftndesc (domain, addr, mini_method_get_rgctx (m));
2204 } else {
2205 addr = mini_llvmonly_add_method_wrappers (m, addr, TRUE, FALSE, &arg);
2207 /* Returns an ftndesc */
2208 return mini_llvmonly_create_ftndesc (domain, addr, arg);
2211 case MONO_RGCTX_INFO_VIRT_METHOD_CODE: {
2212 MonoJumpInfoVirtMethod *info = (MonoJumpInfoVirtMethod *)data;
2213 MonoClass *iface_class = info->method->klass;
2214 MonoMethod *method;
2215 int ioffset, slot;
2216 gpointer addr;
2218 mono_class_setup_vtable (info->klass);
2219 // FIXME: Check type load
2220 if (mono_class_is_interface (iface_class)) {
2221 ioffset = mono_class_interface_offset (info->klass, iface_class);
2222 g_assert (ioffset != -1);
2223 } else {
2224 ioffset = 0;
2226 slot = mono_method_get_vtable_slot (info->method);
2227 g_assert (slot != -1);
2228 g_assert (m_class_get_vtable (info->klass));
2229 method = m_class_get_vtable (info->klass) [ioffset + slot];
2231 method = mono_class_inflate_generic_method_checked (method, context, error);
2232 return_val_if_nok (error, NULL);
2234 addr = mono_compile_method_checked (method, error);
2235 return_val_if_nok (error, NULL);
2236 if (mono_llvm_only) {
2237 gpointer arg = NULL;
2238 addr = mini_llvmonly_add_method_wrappers (method, addr, FALSE, FALSE, &arg);
2240 /* Returns an ftndesc */
2241 return mini_llvmonly_create_ftndesc (domain, addr, arg);
2242 } else {
2243 return mini_add_method_trampoline (method, addr, mono_method_needs_static_rgctx_invoke (method, FALSE), FALSE);
2246 case MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE: {
2247 MonoJumpInfoVirtMethod *info = (MonoJumpInfoVirtMethod *)data;
2248 MonoClass *iface_class = info->method->klass;
2249 MonoMethod *method;
2250 MonoClass *impl_class;
2251 int ioffset, slot;
2253 mono_class_setup_vtable (info->klass);
2254 // FIXME: Check type load
2255 if (mono_class_is_interface (iface_class)) {
2256 ioffset = mono_class_interface_offset (info->klass, iface_class);
2257 g_assert (ioffset != -1);
2258 } else {
2259 ioffset = 0;
2261 slot = mono_method_get_vtable_slot (info->method);
2262 g_assert (slot != -1);
2263 g_assert (m_class_get_vtable (info->klass));
2264 method = m_class_get_vtable (info->klass) [ioffset + slot];
2266 impl_class = method->klass;
2267 if (MONO_TYPE_IS_REFERENCE (m_class_get_byval_arg (impl_class)))
2268 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_REF);
2269 else if (mono_class_is_nullable (impl_class))
2270 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_NULLABLE);
2271 else
2272 return GUINT_TO_POINTER (MONO_GSHAREDVT_BOX_TYPE_VTYPE);
2274 #ifndef DISABLE_REMOTING
2275 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK: {
2276 MonoMethod *remoting_invoke_method = mono_marshal_get_remoting_invoke_with_check ((MonoMethod *)data, error);
2277 return_val_if_nok (error, NULL);
2278 return mono_compile_method_checked (remoting_invoke_method, error);
2280 #endif
2281 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE:
2282 return mono_domain_alloc0 (domain, sizeof (gpointer));
2283 case MONO_RGCTX_INFO_CLASS_FIELD:
2284 return data;
2285 case MONO_RGCTX_INFO_FIELD_OFFSET: {
2286 MonoClassField *field = (MonoClassField *)data;
2288 if (mono_class_field_is_special_static (field)) {
2289 gpointer addr;
2291 mono_class_vtable_checked (domain, field->parent, error);
2292 mono_error_assert_ok (error);
2294 /* Return the TLS offset */
2295 g_assert (domain->special_static_fields);
2296 mono_domain_lock (domain);
2297 addr = g_hash_table_lookup (domain->special_static_fields, field);
2298 mono_domain_unlock (domain);
2299 g_assert (addr);
2300 return (guint8*)addr + 1;
2303 /* The value is offset by 1 */
2304 if (m_class_is_valuetype (field->parent) && !(field->type->attrs & FIELD_ATTRIBUTE_STATIC))
2305 return GUINT_TO_POINTER (field->offset - MONO_ABI_SIZEOF (MonoObject) + 1);
2306 else
2307 return GUINT_TO_POINTER (field->offset + 1);
2309 case MONO_RGCTX_INFO_METHOD_RGCTX: {
2310 MonoMethodInflated *method = (MonoMethodInflated *)data;
2312 g_assert (method->method.method.is_inflated);
2314 return mini_method_get_rgctx ((MonoMethod*)method);
2316 case MONO_RGCTX_INFO_METHOD_CONTEXT: {
2317 MonoMethodInflated *method = (MonoMethodInflated *)data;
2319 g_assert (method->method.method.is_inflated);
2320 g_assert (method->context.method_inst);
2322 return method->context.method_inst;
2324 case MONO_RGCTX_INFO_SIG_GSHAREDVT_IN_TRAMPOLINE_CALLI: {
2325 MonoMethodSignature *gsig = (MonoMethodSignature *)oti->data;
2326 MonoMethodSignature *sig = (MonoMethodSignature *)data;
2327 gpointer addr;
2330 * This is an indirect call to the address passed by the caller in the rgctx reg.
2332 addr = mini_get_gsharedvt_wrapper (TRUE, NULL, sig, gsig, -1, TRUE);
2333 return addr;
2335 case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI: {
2336 MonoMethodSignature *gsig = (MonoMethodSignature *)oti->data;
2337 MonoMethodSignature *sig = (MonoMethodSignature *)data;
2338 gpointer addr;
2341 * This is an indirect call to the address passed by the caller in the rgctx reg.
2343 addr = mini_get_gsharedvt_wrapper (FALSE, NULL, sig, gsig, -1, TRUE);
2344 return addr;
2346 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE:
2347 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT: {
2348 MonoJumpInfoGSharedVtCall *call_info = (MonoJumpInfoGSharedVtCall *)data;
2349 MonoMethodSignature *call_sig;
2350 MonoMethod *method;
2351 gpointer addr;
2352 MonoJitInfo *callee_ji;
2353 gboolean virtual_ = oti->info_type == MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT;
2354 gint32 vcall_offset;
2355 gboolean callee_gsharedvt;
2357 /* This is the original generic signature used by the caller */
2358 call_sig = call_info->sig;
2359 /* This is the instantiated method which is called */
2360 method = call_info->method;
2362 g_assert (method->is_inflated);
2364 if (mono_llvm_only && (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED))
2365 method = mono_marshal_get_synchronized_wrapper (method);
2367 if (!virtual_) {
2368 addr = mono_compile_method_checked (method, error);
2369 return_val_if_nok (error, NULL);
2370 } else
2371 addr = NULL;
2373 if (virtual_) {
2374 /* Same as in mono_emit_method_call_full () */
2375 if ((m_class_get_parent (method->klass) == mono_defaults.multicastdelegate_class) && (!strcmp (method->name, "Invoke"))) {
2376 /* See mono_emit_method_call_full () */
2377 /* The gsharedvt trampoline will recognize this constant */
2378 vcall_offset = MONO_GSHAREDVT_DEL_INVOKE_VT_OFFSET;
2379 } else if (mono_class_is_interface (method->klass)) {
2380 guint32 imt_slot = mono_method_get_imt_slot (method);
2381 vcall_offset = ((gint32)imt_slot - MONO_IMT_SIZE) * TARGET_SIZEOF_VOID_P;
2382 } else {
2383 vcall_offset = G_STRUCT_OFFSET (MonoVTable, vtable) +
2384 ((mono_method_get_vtable_index (method)) * (TARGET_SIZEOF_VOID_P));
2386 } else {
2387 vcall_offset = -1;
2390 // FIXME: This loads information in the AOT case
2391 callee_ji = mini_jit_info_table_find (mono_domain_get (), (char *)mono_get_addr_from_ftnptr (addr), NULL);
2392 callee_gsharedvt = ji_is_gsharedvt (callee_ji);
2395 * For gsharedvt calls made out of gsharedvt methods, the callee could end up being a gsharedvt method, or a normal
2396 * non-shared method. The latter call cannot be patched, so instead of using a normal call, we make an indirect
2397 * call through the rgctx, in effect patching the rgctx entry instead of the call site.
2398 * For virtual calls, the caller might be a normal or a gsharedvt method. Since there is only one vtable slot,
2399 * this difference needs to be handed on the caller side. This is currently implemented by adding a gsharedvt-in
2400 * trampoline to all gsharedvt methods and storing this trampoline into the vtable slot. Virtual calls made from
2401 * gsharedvt methods always go through a gsharedvt-out trampoline, so the calling sequence is:
2402 * caller -> out trampoline -> in trampoline -> callee
2403 * This is not very efficient, but it is easy to implement.
2405 if (virtual_ || !callee_gsharedvt) {
2406 MonoMethodSignature *sig, *gsig;
2408 g_assert (method->is_inflated);
2410 sig = mono_method_signature_internal (method);
2411 gsig = call_sig;
2413 if (mono_llvm_only) {
2414 if (mini_is_gsharedvt_variable_signature (call_sig)) {
2415 /* The virtual case doesn't go through this code */
2416 g_assert (!virtual_);
2418 sig = mono_method_signature_internal (jinfo_get_method (callee_ji));
2419 gpointer out_wrapper = mini_get_gsharedvt_wrapper (FALSE, NULL, sig, gsig, -1, FALSE);
2420 MonoFtnDesc *out_wrapper_arg = mini_llvmonly_create_ftndesc (domain, callee_ji->code_start, mini_method_get_rgctx (method));
2422 /* Returns an ftndesc */
2423 addr = mini_llvmonly_create_ftndesc (domain, out_wrapper, out_wrapper_arg);
2424 } else {
2425 addr = mini_llvmonly_create_ftndesc (domain, addr, mini_method_get_rgctx (method));
2427 } else {
2428 addr = mini_get_gsharedvt_wrapper (FALSE, addr, sig, gsig, vcall_offset, FALSE);
2430 #if 0
2431 if (virtual)
2432 printf ("OUT-VCALL: %s\n", mono_method_full_name (method, TRUE));
2433 else
2434 printf ("OUT: %s\n", mono_method_full_name (method, TRUE));
2435 #endif
2436 } else if (callee_gsharedvt) {
2437 MonoMethodSignature *sig, *gsig;
2440 * This is a combination of the out and in cases, since both the caller and the callee are gsharedvt methods.
2441 * The caller and the callee can use different gsharedvt signatures, so we have to add both an out and an in
2442 * trampoline, i.e.:
2443 * class Base<T> {
2444 * public void foo<T1> (T1 t1, T t, object o) {}
2446 * class AClass : Base<long> {
2447 * public void bar<T> (T t, long time, object o) {
2448 * foo (t, time, o);
2451 * Here, the caller uses !!0,long, while the callee uses !!0,!0
2452 * FIXME: Optimize this.
2455 if (mono_llvm_only) {
2456 /* Both wrappers receive an extra <addr, rgctx> argument */
2457 sig = mono_method_signature_internal (method);
2458 gsig = mono_method_signature_internal (jinfo_get_method (callee_ji));
2460 /* Return a function descriptor */
2462 if (mini_is_gsharedvt_variable_signature (call_sig)) {
2464 * This is not an optimization, but its needed, since the concrete signature 'sig'
2465 * might not exist at all in IL, so the AOT compiler cannot generate the wrappers
2466 * for it.
2468 addr = mini_llvmonly_create_ftndesc (domain, callee_ji->code_start, mini_method_get_rgctx (method));
2469 } else if (mini_is_gsharedvt_variable_signature (gsig)) {
2470 gpointer in_wrapper = mini_get_gsharedvt_wrapper (TRUE, callee_ji->code_start, sig, gsig, -1, FALSE);
2472 gpointer in_wrapper_arg = mini_llvmonly_create_ftndesc (domain, callee_ji->code_start, mini_method_get_rgctx (method));
2474 addr = mini_llvmonly_create_ftndesc (domain, in_wrapper, in_wrapper_arg);
2475 } else {
2476 addr = mini_llvmonly_create_ftndesc (domain, addr, mini_method_get_rgctx (method));
2478 } else if (call_sig == mono_method_signature_internal (method)) {
2479 } else {
2480 sig = mono_method_signature_internal (method);
2481 gsig = mono_method_signature_internal (jinfo_get_method (callee_ji));
2483 addr = mini_get_gsharedvt_wrapper (TRUE, callee_ji->code_start, sig, gsig, -1, FALSE);
2485 sig = mono_method_signature_internal (method);
2486 gsig = call_sig;
2488 addr = mini_get_gsharedvt_wrapper (FALSE, addr, sig, gsig, -1, FALSE);
2490 //printf ("OUT-IN-RGCTX: %s\n", mono_method_full_name (method, TRUE));
2494 return addr;
2496 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO: {
2497 MonoGSharedVtMethodInfo *info = (MonoGSharedVtMethodInfo *)data;
2498 MonoGSharedVtMethodRuntimeInfo *res;
2499 MonoType *t;
2500 int i, offset, align, size;
2502 // FIXME:
2503 res = (MonoGSharedVtMethodRuntimeInfo *)g_malloc0 (sizeof (MonoGSharedVtMethodRuntimeInfo) + (info->num_entries * sizeof (gpointer)));
2505 offset = 0;
2506 for (i = 0; i < info->num_entries; ++i) {
2507 MonoRuntimeGenericContextInfoTemplate *template_ = &info->entries [i];
2509 switch (template_->info_type) {
2510 case MONO_RGCTX_INFO_LOCAL_OFFSET:
2511 t = (MonoType *)template_->data;
2513 size = mono_type_size (t, &align);
2515 if (align < sizeof (gpointer))
2516 align = sizeof (gpointer);
2517 if (MONO_TYPE_ISSTRUCT (t) && align < 2 * sizeof (gpointer))
2518 align = 2 * sizeof (gpointer);
2520 // FIXME: Do the same things as alloc_stack_slots
2521 offset += align - 1;
2522 offset &= ~(align - 1);
2523 res->entries [i] = GINT_TO_POINTER (offset);
2524 offset += size;
2525 break;
2526 default:
2527 res->entries [i] = instantiate_info (domain, template_, context, klass, error);
2528 if (!is_ok (error))
2529 return NULL;
2530 break;
2533 res->locals_size = offset;
2535 return res;
2537 case MONO_RGCTX_INFO_DELEGATE_TRAMP_INFO: {
2538 MonoDelegateClassMethodPair *dele_info = (MonoDelegateClassMethodPair*)data;
2539 gpointer trampoline;
2541 if (dele_info->is_virtual)
2542 trampoline = mono_create_delegate_virtual_trampoline (domain, dele_info->klass, dele_info->method);
2543 else
2544 trampoline = mono_create_delegate_trampoline_info (domain, dele_info->klass, dele_info->method);
2546 g_assert (trampoline);
2547 return trampoline;
2549 default:
2550 g_assert_not_reached ();
2552 /* Not reached */
2553 return NULL;
2557 * LOCKING: loader lock
2559 static void
2560 fill_in_rgctx_template_slot (MonoClass *klass, int type_argc, int index, gpointer data, MonoRgctxInfoType info_type)
2562 MonoRuntimeGenericContextTemplate *template_ = mono_class_get_runtime_generic_context_template (klass);
2563 MonoClass *subclass;
2565 rgctx_template_set_slot (m_class_get_image (klass), template_, type_argc, index, data, info_type);
2567 /* Recurse for all subclasses */
2568 if (generic_subclass_hash)
2569 subclass = (MonoClass *)g_hash_table_lookup (generic_subclass_hash, klass);
2570 else
2571 subclass = NULL;
2573 while (subclass) {
2574 MonoRuntimeGenericContextInfoTemplate subclass_oti;
2575 MonoRuntimeGenericContextTemplate *subclass_template = class_lookup_rgctx_template (subclass);
2577 g_assert (subclass_template);
2579 subclass_oti = class_get_rgctx_template_oti (m_class_get_parent (subclass), type_argc, index, FALSE, FALSE, NULL);
2580 g_assert (subclass_oti.data);
2582 fill_in_rgctx_template_slot (subclass, type_argc, index, subclass_oti.data, info_type);
2584 subclass = subclass_template->next_subclass;
2588 const char*
2589 mono_rgctx_info_type_to_str (MonoRgctxInfoType type)
2591 switch (type) {
2592 case MONO_RGCTX_INFO_STATIC_DATA: return "STATIC_DATA";
2593 case MONO_RGCTX_INFO_KLASS: return "KLASS";
2594 case MONO_RGCTX_INFO_ELEMENT_KLASS: return "ELEMENT_KLASS";
2595 case MONO_RGCTX_INFO_VTABLE: return "VTABLE";
2596 case MONO_RGCTX_INFO_TYPE: return "TYPE";
2597 case MONO_RGCTX_INFO_REFLECTION_TYPE: return "REFLECTION_TYPE";
2598 case MONO_RGCTX_INFO_METHOD: return "METHOD";
2599 case MONO_RGCTX_INFO_METHOD_FTNDESC: return "METHOD_FTNDESC";
2600 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO: return "GSHAREDVT_INFO";
2601 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE: return "GENERIC_METHOD_CODE";
2602 case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER: return "GSHAREDVT_OUT_WRAPPER";
2603 case MONO_RGCTX_INFO_CLASS_FIELD: return "CLASS_FIELD";
2604 case MONO_RGCTX_INFO_METHOD_RGCTX: return "METHOD_RGCTX";
2605 case MONO_RGCTX_INFO_METHOD_CONTEXT: return "METHOD_CONTEXT";
2606 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK: return "REMOTING_INVOKE_WITH_CHECK";
2607 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE: return "METHOD_DELEGATE_CODE";
2608 case MONO_RGCTX_INFO_CAST_CACHE: return "CAST_CACHE";
2609 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE: return "ARRAY_ELEMENT_SIZE";
2610 case MONO_RGCTX_INFO_VALUE_SIZE: return "VALUE_SIZE";
2611 case MONO_RGCTX_INFO_CLASS_SIZEOF: return "CLASS_SIZEOF";
2612 case MONO_RGCTX_INFO_CLASS_BOX_TYPE: return "CLASS_BOX_TYPE";
2613 case MONO_RGCTX_INFO_CLASS_IS_REF_OR_CONTAINS_REFS: return "CLASS_IS_REF_OR_CONTAINS_REFS";
2614 case MONO_RGCTX_INFO_FIELD_OFFSET: return "FIELD_OFFSET";
2615 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE: return "METHOD_GSHAREDVT_OUT_TRAMPOLINE";
2616 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT: return "METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT";
2617 case MONO_RGCTX_INFO_SIG_GSHAREDVT_IN_TRAMPOLINE_CALLI: return "SIG_GSHAREDVT_IN_TRAMPOLINE_CALLI";
2618 case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI: return "SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI";
2619 case MONO_RGCTX_INFO_MEMCPY: return "MEMCPY";
2620 case MONO_RGCTX_INFO_BZERO: return "BZERO";
2621 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX: return "NULLABLE_CLASS_BOX";
2622 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: return "NULLABLE_CLASS_UNBOX";
2623 case MONO_RGCTX_INFO_VIRT_METHOD_CODE: return "VIRT_METHOD_CODE";
2624 case MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE: return "VIRT_METHOD_BOX_TYPE";
2625 case MONO_RGCTX_INFO_DELEGATE_TRAMP_INFO: return "DELEGATE_TRAMP_INFO";
2626 default:
2627 return "<UNKNOWN RGCTX INFO TYPE>";
2631 G_GNUC_UNUSED static char*
2632 rgctx_info_to_str (MonoRgctxInfoType info_type, gpointer data)
2634 switch (info_type) {
2635 case MONO_RGCTX_INFO_VTABLE:
2636 return mono_type_full_name ((MonoType*)data);
2637 default:
2638 return g_strdup_printf ("<%p>", data);
2643 * LOCKING: loader lock
2645 static int
2646 register_info (MonoClass *klass, int type_argc, gpointer data, MonoRgctxInfoType info_type)
2648 int i;
2649 MonoRuntimeGenericContextTemplate *template_ = mono_class_get_runtime_generic_context_template (klass);
2650 MonoClass *parent;
2651 MonoRuntimeGenericContextInfoTemplate *oti;
2653 for (i = 0, oti = get_info_templates (template_, type_argc); oti; ++i, oti = oti->next) {
2654 if (!oti->data)
2655 break;
2658 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)));
2660 /* Mark the slot as used in all parent classes (until we find
2661 a parent class which already has it marked used). */
2662 parent = m_class_get_parent (klass);
2663 while (parent != NULL) {
2664 MonoRuntimeGenericContextTemplate *parent_template;
2665 MonoRuntimeGenericContextInfoTemplate *oti;
2667 if (mono_class_is_ginst (parent))
2668 parent = mono_class_get_generic_class (parent)->container_class;
2670 parent_template = mono_class_get_runtime_generic_context_template (parent);
2671 oti = rgctx_template_get_other_slot (parent_template, type_argc, i);
2673 if (oti && oti->data)
2674 break;
2676 rgctx_template_set_slot (m_class_get_image (parent), parent_template, type_argc, i,
2677 MONO_RGCTX_SLOT_USED_MARKER, (MonoRgctxInfoType)0);
2679 parent = m_class_get_parent (parent);
2682 /* Fill in the slot in this class and in all subclasses
2683 recursively. */
2684 fill_in_rgctx_template_slot (klass, type_argc, i, data, info_type);
2686 return i;
2689 static gboolean
2690 info_equal (gpointer data1, gpointer data2, MonoRgctxInfoType info_type)
2692 switch (info_type) {
2693 case MONO_RGCTX_INFO_STATIC_DATA:
2694 case MONO_RGCTX_INFO_KLASS:
2695 case MONO_RGCTX_INFO_ELEMENT_KLASS:
2696 case MONO_RGCTX_INFO_VTABLE:
2697 case MONO_RGCTX_INFO_TYPE:
2698 case MONO_RGCTX_INFO_REFLECTION_TYPE:
2699 case MONO_RGCTX_INFO_CAST_CACHE:
2700 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
2701 case MONO_RGCTX_INFO_VALUE_SIZE:
2702 case MONO_RGCTX_INFO_CLASS_SIZEOF:
2703 case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
2704 case MONO_RGCTX_INFO_CLASS_IS_REF_OR_CONTAINS_REFS:
2705 case MONO_RGCTX_INFO_MEMCPY:
2706 case MONO_RGCTX_INFO_BZERO:
2707 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
2708 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX:
2709 return mono_class_from_mono_type_internal ((MonoType *)data1) == mono_class_from_mono_type_internal ((MonoType *)data2);
2710 case MONO_RGCTX_INFO_METHOD:
2711 case MONO_RGCTX_INFO_METHOD_FTNDESC:
2712 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO:
2713 case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
2714 case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER:
2715 case MONO_RGCTX_INFO_CLASS_FIELD:
2716 case MONO_RGCTX_INFO_FIELD_OFFSET:
2717 case MONO_RGCTX_INFO_METHOD_RGCTX:
2718 case MONO_RGCTX_INFO_METHOD_CONTEXT:
2719 case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
2720 case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE:
2721 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE:
2722 case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT:
2723 case MONO_RGCTX_INFO_SIG_GSHAREDVT_IN_TRAMPOLINE_CALLI:
2724 case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI:
2725 return data1 == data2;
2726 case MONO_RGCTX_INFO_VIRT_METHOD_CODE:
2727 case MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE: {
2728 MonoJumpInfoVirtMethod *info1 = (MonoJumpInfoVirtMethod *)data1;
2729 MonoJumpInfoVirtMethod *info2 = (MonoJumpInfoVirtMethod *)data2;
2731 return info1->klass == info2->klass && info1->method == info2->method;
2733 case MONO_RGCTX_INFO_DELEGATE_TRAMP_INFO: {
2734 MonoDelegateClassMethodPair *dele1 = (MonoDelegateClassMethodPair *)data1;
2735 MonoDelegateClassMethodPair *dele2 = (MonoDelegateClassMethodPair *)data2;
2737 return dele1->is_virtual == dele2->is_virtual && dele1->method == dele2->method && dele1->klass == dele2->klass;
2739 default:
2740 g_assert_not_reached ();
2742 /* never reached */
2743 return FALSE;
2747 * mini_rgctx_info_type_to_patch_info_type:
2749 * Return the type of the runtime object referred to by INFO_TYPE.
2751 MonoJumpInfoType
2752 mini_rgctx_info_type_to_patch_info_type (MonoRgctxInfoType info_type)
2754 switch (info_type) {
2755 case MONO_RGCTX_INFO_STATIC_DATA:
2756 case MONO_RGCTX_INFO_KLASS:
2757 case MONO_RGCTX_INFO_ELEMENT_KLASS:
2758 case MONO_RGCTX_INFO_VTABLE:
2759 case MONO_RGCTX_INFO_TYPE:
2760 case MONO_RGCTX_INFO_REFLECTION_TYPE:
2761 case MONO_RGCTX_INFO_CAST_CACHE:
2762 case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
2763 case MONO_RGCTX_INFO_VALUE_SIZE:
2764 case MONO_RGCTX_INFO_CLASS_SIZEOF:
2765 case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
2766 case MONO_RGCTX_INFO_CLASS_IS_REF_OR_CONTAINS_REFS:
2767 case MONO_RGCTX_INFO_MEMCPY:
2768 case MONO_RGCTX_INFO_BZERO:
2769 case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
2770 case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX:
2771 case MONO_RGCTX_INFO_LOCAL_OFFSET:
2772 return MONO_PATCH_INFO_CLASS;
2773 case MONO_RGCTX_INFO_FIELD_OFFSET:
2774 return MONO_PATCH_INFO_FIELD;
2775 default:
2776 g_assert_not_reached ();
2777 return (MonoJumpInfoType)-1;
2782 * lookup_or_register_info:
2783 * @method: a method
2784 * @in_mrgctx: whether to put the data into the MRGCTX
2785 * @data: the info data
2786 * @info_type: the type of info to register about data
2787 * @generic_context: a generic context
2789 * Looks up and, if necessary, adds information about data/info_type in
2790 * method's or method's class runtime generic context. Returns the
2791 * encoded slot number.
2793 static guint32
2794 lookup_or_register_info (MonoClass *klass, MonoMethod *method, gboolean in_mrgctx, gpointer data,
2795 MonoRgctxInfoType info_type, MonoGenericContext *generic_context)
2797 int type_argc = 0;
2799 if (in_mrgctx) {
2800 klass = method->klass;
2802 MonoGenericInst *method_inst = mono_method_get_context (method)->method_inst;
2804 if (method_inst) {
2805 g_assert (method->is_inflated && method_inst);
2806 type_argc = method_inst->type_argc;
2807 g_assert (type_argc > 0);
2811 MonoRuntimeGenericContextTemplate *rgctx_template =
2812 mono_class_get_runtime_generic_context_template (klass);
2813 MonoRuntimeGenericContextInfoTemplate *oti_list, *oti;
2814 int i, index;
2816 klass = get_shared_class (klass);
2818 mono_loader_lock ();
2820 index = -1;
2821 if (info_has_identity (info_type)) {
2822 oti_list = get_info_templates (rgctx_template, type_argc);
2824 for (oti = oti_list, i = 0; oti; oti = oti->next, ++i) {
2825 gpointer inflated_data;
2827 if (oti->info_type != info_type || !oti->data)
2828 continue;
2830 inflated_data = inflate_info (oti, generic_context, klass, TRUE);
2832 if (info_equal (data, inflated_data, info_type)) {
2833 free_inflated_info (info_type, inflated_data);
2834 index = i;
2835 break;
2837 free_inflated_info (info_type, inflated_data);
2841 /* We haven't found the info */
2842 if (index == -1)
2843 index = register_info (klass, type_argc, data, info_type);
2845 /* interlocked by loader lock */
2846 if (index > UnlockedRead (&rgctx_max_slot_number))
2847 UnlockedWrite (&rgctx_max_slot_number, index);
2849 mono_loader_unlock ();
2851 //g_print ("rgctx item at index %d argc %d\n", index, type_argc);
2853 if (in_mrgctx)
2854 return MONO_RGCTX_SLOT_MAKE_MRGCTX (index);
2855 else
2856 return MONO_RGCTX_SLOT_MAKE_RGCTX (index);
2859 static inline int
2860 class_rgctx_array_size (int n)
2862 return 4 << n;
2865 static inline int
2866 method_rgctx_array_size (int n)
2868 return 6 << n;
2872 * mono_class_rgctx_get_array_size:
2873 * @n: The number of the array
2874 * @mrgctx: Whether it's an MRGCTX as opposed to a RGCTX.
2876 * Returns the number of slots in the n'th array of a (M)RGCTX. That
2877 * number includes the slot for linking and - for MRGCTXs - the two
2878 * slots in the first array for additional information.
2881 mono_class_rgctx_get_array_size (int n, gboolean mrgctx)
2883 g_assert (n >= 0 && n < 30);
2885 if (mrgctx)
2886 return method_rgctx_array_size (n);
2887 else
2888 return class_rgctx_array_size (n);
2892 * LOCKING: domain lock
2894 static gpointer*
2895 alloc_rgctx_array (MonoDomain *domain, int n, gboolean is_mrgctx)
2897 gint32 size = mono_class_rgctx_get_array_size (n, is_mrgctx) * sizeof (gpointer);
2898 gpointer *array = (gpointer *)mono_domain_alloc0 (domain, size);
2900 /* interlocked by domain lock (by definition) */
2901 if (is_mrgctx) {
2902 UnlockedIncrement (&mrgctx_num_arrays_allocated);
2903 UnlockedAdd (&mrgctx_bytes_allocated, size);
2904 } else {
2905 UnlockedIncrement (&rgctx_num_arrays_allocated);
2906 UnlockedAdd (&rgctx_bytes_allocated, size);
2909 return array;
2912 static gpointer
2913 fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContext *rgctx, guint32 slot,
2914 MonoGenericInst *method_inst, gboolean is_mrgctx, MonoError *error)
2916 gpointer info;
2917 int i, first_slot, size;
2918 MonoDomain *domain = class_vtable->domain;
2919 MonoClass *klass = class_vtable->klass;
2920 MonoGenericContext *class_context;
2921 MonoRuntimeGenericContextInfoTemplate oti;
2922 MonoRuntimeGenericContext *orig_rgctx;
2923 int rgctx_index;
2924 gboolean do_free;
2927 * Need a fastpath since this is called without trampolines in llvmonly mode.
2929 orig_rgctx = rgctx;
2930 if (!is_mrgctx) {
2931 first_slot = 0;
2932 size = class_rgctx_array_size (0);
2933 for (i = 0; ; ++i) {
2934 int offset = 0;
2936 if (slot < first_slot + size - 1) {
2937 rgctx_index = slot - first_slot + 1 + offset;
2938 info = (MonoRuntimeGenericContext*)rgctx [rgctx_index];
2939 if (info)
2940 return info;
2941 break;
2943 if (!rgctx [offset + 0])
2944 break;
2945 rgctx = (void **)rgctx [offset + 0];
2946 first_slot += size - 1;
2947 size = class_rgctx_array_size (i + 1);
2949 } else {
2950 first_slot = 0;
2951 size = method_rgctx_array_size (0);
2952 size -= MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
2953 for (i = 0; ; ++i) {
2954 int offset = 0;
2956 if (i == 0)
2957 offset = MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
2959 if (slot < first_slot + size - 1) {
2960 rgctx_index = slot - first_slot + 1 + offset;
2961 info = (MonoRuntimeGenericContext*)rgctx [rgctx_index];
2962 if (info)
2963 return info;
2964 break;
2966 if (!rgctx [offset + 0])
2967 break;
2968 rgctx = (void **)rgctx [offset + 0];
2969 first_slot += size - 1;
2970 size = method_rgctx_array_size (i + 1);
2973 rgctx = orig_rgctx;
2975 class_context = mono_class_is_ginst (klass) ? &mono_class_get_generic_class (klass)->context : NULL;
2976 MonoGenericContext context = { class_context ? class_context->class_inst : NULL, method_inst };
2978 mono_domain_lock (domain);
2980 /* First check whether that slot isn't already instantiated.
2981 This might happen because lookup doesn't lock. Allocate
2982 arrays on the way. */
2983 first_slot = 0;
2984 size = mono_class_rgctx_get_array_size (0, is_mrgctx);
2985 if (is_mrgctx)
2986 size -= MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
2987 for (i = 0; ; ++i) {
2988 int offset;
2990 if (is_mrgctx && i == 0)
2991 offset = MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
2992 else
2993 offset = 0;
2995 if (slot < first_slot + size - 1) {
2996 rgctx_index = slot - first_slot + 1 + offset;
2997 info = (MonoRuntimeGenericContext*)rgctx [rgctx_index];
2998 if (info) {
2999 mono_domain_unlock (domain);
3000 return info;
3002 break;
3004 if (!rgctx [offset + 0]) {
3005 gpointer *array = alloc_rgctx_array (domain, i + 1, is_mrgctx);
3006 /* Make sure that this array is zeroed if other threads access it */
3007 mono_memory_write_barrier ();
3008 rgctx [offset + 0] = array;
3010 rgctx = (void **)rgctx [offset + 0];
3011 first_slot += size - 1;
3012 size = mono_class_rgctx_get_array_size (i + 1, is_mrgctx);
3015 g_assert (!rgctx [rgctx_index]);
3017 mono_domain_unlock (domain);
3019 oti = class_get_rgctx_template_oti (get_shared_class (klass),
3020 method_inst ? method_inst->type_argc : 0, slot, TRUE, TRUE, &do_free);
3021 /* This might take the loader lock */
3022 info = (MonoRuntimeGenericContext*)instantiate_info (domain, &oti, &context, klass, error);
3023 return_val_if_nok (error, NULL);
3024 g_assert (info);
3027 if (method_inst)
3028 g_print ("filling mrgctx slot %d table %d index %d\n", slot, i, rgctx_index);
3031 /*FIXME We should use CAS here, no need to take a lock.*/
3032 mono_domain_lock (domain);
3034 /* Check whether the slot hasn't been instantiated in the
3035 meantime. */
3036 if (rgctx [rgctx_index]) {
3037 info = (MonoRuntimeGenericContext*)rgctx [rgctx_index];
3038 } else {
3039 /* Make sure other threads see the contents of info */
3040 mono_memory_write_barrier ();
3041 rgctx [rgctx_index] = info;
3044 mono_domain_unlock (domain);
3046 if (do_free)
3047 free_inflated_info (oti.info_type, oti.data);
3049 return info;
3053 * mono_class_fill_runtime_generic_context:
3054 * @class_vtable: a vtable
3055 * @slot: a slot index to be instantiated
3057 * Instantiates a slot in the RGCTX, returning its value.
3059 gpointer
3060 mono_class_fill_runtime_generic_context (MonoVTable *class_vtable, guint32 slot, MonoError *error)
3062 MonoDomain *domain = class_vtable->domain;
3063 MonoRuntimeGenericContext *rgctx;
3064 gpointer info;
3066 error_init (error);
3068 rgctx = class_vtable->runtime_generic_context;
3069 if (G_UNLIKELY (!rgctx)) {
3070 mono_domain_lock (domain);
3072 rgctx = class_vtable->runtime_generic_context;
3073 if (!rgctx) {
3074 rgctx = alloc_rgctx_array (domain, 0, FALSE);
3075 /* Make sure that this array is zeroed if other threads access it */
3076 mono_memory_write_barrier ();
3077 class_vtable->runtime_generic_context = rgctx;
3078 UnlockedIncrement (&rgctx_num_allocated); /* interlocked by domain lock */
3081 mono_domain_unlock (domain);
3084 info = fill_runtime_generic_context (class_vtable, rgctx, slot, NULL, FALSE, error);
3086 DEBUG (printf ("get rgctx slot: %s %d -> %p\n", mono_type_full_name (m_class_get_byval_arg (class_vtable->klass)), slot, info));
3088 return info;
3092 * mono_method_fill_runtime_generic_context:
3093 * @mrgctx: an MRGCTX
3094 * @slot: a slot index to be instantiated
3096 * Instantiates a slot in the MRGCTX.
3098 gpointer
3099 mono_method_fill_runtime_generic_context (MonoMethodRuntimeGenericContext *mrgctx, guint32 slot, MonoError *error)
3101 gpointer info;
3103 info = fill_runtime_generic_context (mrgctx->class_vtable, (MonoRuntimeGenericContext*)mrgctx, slot, mrgctx->method_inst, TRUE, error);
3105 return info;
3108 static guint
3109 mrgctx_hash_func (gconstpointer key)
3111 const MonoMethodRuntimeGenericContext *mrgctx = (const MonoMethodRuntimeGenericContext *)key;
3113 return mono_aligned_addr_hash (mrgctx->class_vtable) ^ mono_metadata_generic_inst_hash (mrgctx->method_inst);
3116 static gboolean
3117 mrgctx_equal_func (gconstpointer a, gconstpointer b)
3119 const MonoMethodRuntimeGenericContext *mrgctx1 = (const MonoMethodRuntimeGenericContext *)a;
3120 const MonoMethodRuntimeGenericContext *mrgctx2 = (const MonoMethodRuntimeGenericContext *)b;
3122 return mrgctx1->class_vtable == mrgctx2->class_vtable &&
3123 mono_metadata_generic_inst_equal (mrgctx1->method_inst, mrgctx2->method_inst);
3127 * mini_method_get_mrgctx:
3128 * @class_vtable: a vtable
3129 * @method: an inflated method
3131 * Returns the MRGCTX for METHOD.
3133 * LOCKING: Take the domain lock.
3135 static MonoMethodRuntimeGenericContext*
3136 mini_method_get_mrgctx (MonoVTable *class_vtable, MonoMethod *method)
3138 MonoDomain *domain = class_vtable->domain;
3139 MonoMethodRuntimeGenericContext *mrgctx;
3140 MonoMethodRuntimeGenericContext key;
3141 MonoGenericInst *method_inst = mini_method_get_context (method)->method_inst;
3142 MonoJitDomainInfo *domain_info = domain_jit_info (domain);
3144 g_assert (!mono_class_is_gtd (class_vtable->klass));
3146 mono_domain_lock (domain);
3148 if (!method_inst) {
3149 g_assert (mini_method_is_default_method (method));
3151 if (!domain_info->mrgctx_hash)
3152 domain_info->mrgctx_hash = g_hash_table_new (NULL, NULL);
3153 mrgctx = (MonoMethodRuntimeGenericContext*)g_hash_table_lookup (domain_info->mrgctx_hash, method);
3154 } else {
3155 g_assert (!method_inst->is_open);
3157 if (!domain_info->method_rgctx_hash)
3158 domain_info->method_rgctx_hash = g_hash_table_new (mrgctx_hash_func, mrgctx_equal_func);
3160 key.class_vtable = class_vtable;
3161 key.method_inst = method_inst;
3163 mrgctx = (MonoMethodRuntimeGenericContext *)g_hash_table_lookup (domain_info->method_rgctx_hash, &key);
3166 if (!mrgctx) {
3167 //int i;
3169 mrgctx = (MonoMethodRuntimeGenericContext*)alloc_rgctx_array (domain, 0, TRUE);
3170 mrgctx->class_vtable = class_vtable;
3171 mrgctx->method_inst = method_inst;
3173 if (!method_inst)
3174 g_hash_table_insert (domain_info->mrgctx_hash, method, mrgctx);
3175 else
3176 g_hash_table_insert (domain_info->method_rgctx_hash, mrgctx, mrgctx);
3179 g_print ("mrgctx alloced for %s <", mono_type_get_full_name (class_vtable->klass));
3180 for (i = 0; i < method_inst->type_argc; ++i)
3181 g_print ("%s, ", mono_type_full_name (method_inst->type_argv [i]));
3182 g_print (">\n");
3186 mono_domain_unlock (domain);
3188 g_assert (mrgctx);
3190 return mrgctx;
3193 static gboolean
3194 type_is_sharable (MonoType *type, gboolean allow_type_vars, gboolean allow_partial)
3196 if (allow_type_vars && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR)) {
3197 MonoType *constraint = type->data.generic_param->gshared_constraint;
3198 if (!constraint)
3199 return TRUE;
3200 type = constraint;
3203 if (MONO_TYPE_IS_REFERENCE (type))
3204 return TRUE;
3206 /* Allow non ref arguments if they are primitive types or enums (partial sharing). */
3207 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))))
3208 return TRUE;
3210 if (allow_partial && !type->byref && type->type == MONO_TYPE_GENERICINST && MONO_TYPE_ISSTRUCT (type)) {
3211 MonoGenericClass *gclass = type->data.generic_class;
3213 if (gclass->context.class_inst && !mini_generic_inst_is_sharable (gclass->context.class_inst, allow_type_vars, allow_partial))
3214 return FALSE;
3215 if (gclass->context.method_inst && !mini_generic_inst_is_sharable (gclass->context.method_inst, allow_type_vars, allow_partial))
3216 return FALSE;
3217 if (mono_class_is_nullable (mono_class_from_mono_type_internal (type)))
3218 return FALSE;
3219 return TRUE;
3222 return FALSE;
3225 gboolean
3226 mini_generic_inst_is_sharable (MonoGenericInst *inst, gboolean allow_type_vars,
3227 gboolean allow_partial)
3229 int i;
3231 for (i = 0; i < inst->type_argc; ++i) {
3232 if (!type_is_sharable (inst->type_argv [i], allow_type_vars, allow_partial))
3233 return FALSE;
3236 return TRUE;
3240 * mono_is_partially_sharable_inst:
3242 * Return TRUE if INST has ref and non-ref type arguments.
3244 gboolean
3245 mono_is_partially_sharable_inst (MonoGenericInst *inst)
3247 int i;
3248 gboolean has_refs = FALSE, has_non_refs = FALSE;
3250 for (i = 0; i < inst->type_argc; ++i) {
3251 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)
3252 has_refs = TRUE;
3253 else
3254 has_non_refs = TRUE;
3257 return has_refs && has_non_refs;
3261 * mono_generic_context_is_sharable_full:
3262 * @context: a generic context
3264 * Returns whether the generic context is sharable. A generic context
3265 * is sharable iff all of its type arguments are reference type, or some of them have a
3266 * reference type, and ALLOW_PARTIAL is TRUE.
3268 gboolean
3269 mono_generic_context_is_sharable_full (MonoGenericContext *context,
3270 gboolean allow_type_vars,
3271 gboolean allow_partial)
3273 g_assert (context->class_inst || context->method_inst);
3275 if (context->class_inst && !mini_generic_inst_is_sharable (context->class_inst, allow_type_vars, allow_partial))
3276 return FALSE;
3278 if (context->method_inst && !mini_generic_inst_is_sharable (context->method_inst, allow_type_vars, allow_partial))
3279 return FALSE;
3281 return TRUE;
3284 gboolean
3285 mono_generic_context_is_sharable (MonoGenericContext *context, gboolean allow_type_vars)
3287 return mono_generic_context_is_sharable_full (context, allow_type_vars, partial_sharing_supported ());
3291 * mono_method_is_generic_impl:
3292 * @method: a method
3294 * Returns whether the method is either generic or part of a generic
3295 * class.
3297 gboolean
3298 mono_method_is_generic_impl (MonoMethod *method)
3300 if (method->is_inflated)
3301 return TRUE;
3302 /* We don't treat wrappers as generic code, i.e., we never
3303 apply generic sharing to them. This is especially
3304 important for static rgctx invoke wrappers, which only work
3305 if not compiled with sharing. */
3306 if (method->wrapper_type != MONO_WRAPPER_NONE)
3307 return FALSE;
3308 if (mono_class_is_gtd (method->klass))
3309 return TRUE;
3310 return FALSE;
3313 static gboolean
3314 has_constraints (MonoGenericContainer *container)
3316 //int i;
3318 return FALSE;
3320 g_assert (container->type_argc > 0);
3321 g_assert (container->type_params);
3323 for (i = 0; i < container->type_argc; ++i)
3324 if (container->type_params [i].constraints)
3325 return TRUE;
3326 return FALSE;
3330 static gboolean
3331 mini_method_is_open (MonoMethod *method)
3333 if (method->is_inflated) {
3334 MonoGenericContext *ctx = mono_method_get_context (method);
3336 if (ctx->class_inst && ctx->class_inst->is_open)
3337 return TRUE;
3338 if (ctx->method_inst && ctx->method_inst->is_open)
3339 return TRUE;
3341 return FALSE;
3344 /* Lazy class loading functions */
3345 static GENERATE_TRY_GET_CLASS_WITH_CACHE (iasync_state_machine, "System.Runtime.CompilerServices", "IAsyncStateMachine")
3347 static G_GNUC_UNUSED gboolean
3348 is_async_state_machine_class (MonoClass *klass)
3350 MonoClass *iclass;
3352 return FALSE;
3354 iclass = mono_class_try_get_iasync_state_machine_class ();
3356 if (iclass && m_class_is_valuetype (klass) && mono_class_is_assignable_from_internal (iclass, klass))
3357 return TRUE;
3358 return FALSE;
3361 static G_GNUC_UNUSED gboolean
3362 is_async_method (MonoMethod *method)
3364 ERROR_DECL (error);
3365 MonoCustomAttrInfo *cattr;
3366 MonoMethodSignature *sig;
3367 gboolean res = FALSE;
3368 MonoClass *attr_class;
3370 return FALSE;
3372 attr_class = mono_class_try_get_iasync_state_machine_class ();
3374 /* Do less expensive checks first */
3375 sig = mono_method_signature_internal (method);
3376 if (attr_class && sig && ((sig->ret->type == MONO_TYPE_VOID) ||
3377 (sig->ret->type == MONO_TYPE_CLASS && !strcmp (m_class_get_name (sig->ret->data.generic_class->container_class), "Task")) ||
3378 (sig->ret->type == MONO_TYPE_GENERICINST && !strcmp (m_class_get_name (sig->ret->data.generic_class->container_class), "Task`1")))) {
3379 //printf ("X: %s\n", mono_method_full_name (method, TRUE));
3380 cattr = mono_custom_attrs_from_method_checked (method, error);
3381 if (!is_ok (error)) {
3382 mono_error_cleanup (error); /* FIXME don't swallow the error? */
3383 return FALSE;
3385 if (cattr) {
3386 if (mono_custom_attrs_has_attr (cattr, attr_class))
3387 res = TRUE;
3388 mono_custom_attrs_free (cattr);
3391 return res;
3395 * mono_method_is_generic_sharable_full:
3396 * @method: a method
3397 * @allow_type_vars: whether to regard type variables as reference types
3398 * @allow_partial: whether to allow partial sharing
3399 * @allow_gsharedvt: whenever to allow sharing over valuetypes
3401 * Returns TRUE iff the method is inflated or part of an inflated
3402 * class, its context is sharable and it has no constraints on its
3403 * type parameters. Otherwise returns FALSE.
3405 gboolean
3406 mono_method_is_generic_sharable_full (MonoMethod *method, gboolean allow_type_vars,
3407 gboolean allow_partial, gboolean allow_gsharedvt)
3409 if (!mono_method_is_generic_impl (method))
3410 return FALSE;
3413 if (!mono_debug_count ())
3414 allow_partial = FALSE;
3417 if (!partial_sharing_supported ())
3418 allow_partial = FALSE;
3420 if (mono_class_is_nullable (method->klass))
3421 // FIXME:
3422 allow_partial = FALSE;
3424 if (m_class_get_image (method->klass)->dynamic)
3426 * Enabling this causes corlib test failures because the JIT encounters generic instances whose
3427 * instance_size is 0.
3429 allow_partial = FALSE;
3432 * Generic async methods have an associated state machine class which is a generic struct. This struct
3433 * is too large to be handled by gsharedvt so we make it visible to the AOT compiler by disabling sharing
3434 * of the async method and the state machine class.
3436 if (is_async_state_machine_class (method->klass))
3437 return FALSE;
3439 if (allow_gsharedvt && mini_is_gsharedvt_sharable_method (method)) {
3440 if (is_async_method (method))
3441 return FALSE;
3442 return TRUE;
3445 if (method->is_inflated) {
3446 MonoMethodInflated *inflated = (MonoMethodInflated*)method;
3447 MonoGenericContext *context = &inflated->context;
3449 if (!mono_generic_context_is_sharable_full (context, allow_type_vars, allow_partial))
3450 return FALSE;
3452 g_assert (inflated->declaring);
3454 if (inflated->declaring->is_generic) {
3455 if (has_constraints (mono_method_get_generic_container (inflated->declaring)))
3456 return FALSE;
3460 if (mono_class_is_ginst (method->klass)) {
3461 if (!mono_generic_context_is_sharable_full (&mono_class_get_generic_class (method->klass)->context, allow_type_vars, allow_partial))
3462 return FALSE;
3464 g_assert (mono_class_get_generic_class (method->klass)->container_class &&
3465 mono_class_is_gtd (mono_class_get_generic_class (method->klass)->container_class));
3467 if (has_constraints (mono_class_get_generic_container (mono_class_get_generic_class (method->klass)->container_class)))
3468 return FALSE;
3471 if (mono_class_is_gtd (method->klass) && !allow_type_vars)
3472 return FALSE;
3474 /* This does potentially expensive cattr checks, so do it at the end */
3475 if (is_async_method (method)) {
3476 if (mini_method_is_open (method))
3477 /* The JIT can't compile these without sharing */
3478 return TRUE;
3479 return FALSE;
3482 return TRUE;
3485 gboolean
3486 mono_method_is_generic_sharable (MonoMethod *method, gboolean allow_type_vars)
3488 return mono_method_is_generic_sharable_full (method, allow_type_vars, partial_sharing_supported (), TRUE);
3492 * mono_method_needs_static_rgctx_invoke:
3494 * Return whenever METHOD needs an rgctx argument.
3495 * An rgctx argument is needed when the method is generic sharable, but it doesn't
3496 * have a this argument which can be used to load the rgctx.
3498 gboolean
3499 mono_method_needs_static_rgctx_invoke (MonoMethod *method, gboolean allow_type_vars)
3501 if (!mono_class_generic_sharing_enabled (method->klass))
3502 return FALSE;
3504 if (!mono_method_is_generic_sharable (method, allow_type_vars))
3505 return FALSE;
3507 if (method->is_inflated && mono_method_get_context (method)->method_inst)
3508 return TRUE;
3510 return ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
3511 m_class_is_valuetype (method->klass) ||
3512 mini_method_is_default_method (method)) &&
3513 (mono_class_is_ginst (method->klass) || mono_class_is_gtd (method->klass));
3516 static MonoGenericInst*
3517 get_object_generic_inst (int type_argc)
3519 MonoType **type_argv;
3520 int i;
3522 type_argv = g_newa (MonoType*, type_argc);
3524 MonoType *object_type = mono_get_object_type ();
3525 for (i = 0; i < type_argc; ++i)
3526 type_argv [i] = object_type;
3528 return mono_metadata_get_generic_inst (type_argc, type_argv);
3532 * mono_method_construct_object_context:
3533 * @method: a method
3535 * Returns a generic context for method with all type variables for
3536 * class and method instantiated with Object.
3538 MonoGenericContext
3539 mono_method_construct_object_context (MonoMethod *method)
3541 MonoGenericContext object_context;
3543 g_assert (!mono_class_is_ginst (method->klass));
3544 if (mono_class_is_gtd (method->klass)) {
3545 int type_argc = mono_class_get_generic_container (method->klass)->type_argc;
3547 object_context.class_inst = get_object_generic_inst (type_argc);
3548 } else {
3549 object_context.class_inst = NULL;
3552 if (mono_method_get_context_general (method, TRUE)->method_inst) {
3553 int type_argc = mono_method_get_context_general (method, TRUE)->method_inst->type_argc;
3555 object_context.method_inst = get_object_generic_inst (type_argc);
3556 } else {
3557 object_context.method_inst = NULL;
3560 g_assert (object_context.class_inst || object_context.method_inst);
3562 return object_context;
3565 static gboolean gshared_supported;
3567 void
3568 mono_set_generic_sharing_supported (gboolean supported)
3570 gshared_supported = supported;
3574 void
3575 mono_set_partial_sharing_supported (gboolean supported)
3577 partial_supported = supported;
3581 * mono_class_generic_sharing_enabled:
3582 * @class: a class
3584 * Returns whether generic sharing is enabled for class.
3586 * This is a stop-gap measure to slowly introduce generic sharing
3587 * until we have all the issues sorted out, at which time this
3588 * function will disappear and generic sharing will always be enabled.
3590 gboolean
3591 mono_class_generic_sharing_enabled (MonoClass *klass)
3593 if (gshared_supported)
3594 return TRUE;
3595 else
3596 return FALSE;
3599 MonoGenericContext*
3600 mini_method_get_context (MonoMethod *method)
3602 return mono_method_get_context_general (method, TRUE);
3606 * mono_method_check_context_used:
3607 * @method: a method
3609 * Checks whether the method's generic context uses a type variable.
3610 * Returns an int with the bits MONO_GENERIC_CONTEXT_USED_CLASS and
3611 * MONO_GENERIC_CONTEXT_USED_METHOD set to reflect whether the
3612 * context's class or method instantiation uses type variables.
3615 mono_method_check_context_used (MonoMethod *method)
3617 MonoGenericContext *method_context = mini_method_get_context (method);
3618 int context_used = 0;
3620 if (!method_context) {
3621 /* It might be a method of an array of an open generic type */
3622 if (m_class_get_rank (method->klass))
3623 context_used = mono_class_check_context_used (method->klass);
3624 } else {
3625 context_used = mono_generic_context_check_used (method_context);
3626 context_used |= mono_class_check_context_used (method->klass);
3629 return context_used;
3632 static gboolean
3633 generic_inst_equal (MonoGenericInst *inst1, MonoGenericInst *inst2)
3635 int i;
3637 if (!inst1) {
3638 g_assert (!inst2);
3639 return TRUE;
3642 g_assert (inst2);
3644 if (inst1->type_argc != inst2->type_argc)
3645 return FALSE;
3647 for (i = 0; i < inst1->type_argc; ++i)
3648 if (!mono_metadata_type_equal (inst1->type_argv [i], inst2->type_argv [i]))
3649 return FALSE;
3651 return TRUE;
3655 * mono_generic_context_equal_deep:
3656 * @context1: a generic context
3657 * @context2: a generic context
3659 * Returns whether context1's type arguments are equal to context2's
3660 * type arguments.
3662 gboolean
3663 mono_generic_context_equal_deep (MonoGenericContext *context1, MonoGenericContext *context2)
3665 return generic_inst_equal (context1->class_inst, context2->class_inst) &&
3666 generic_inst_equal (context1->method_inst, context2->method_inst);
3670 * mini_class_get_container_class:
3671 * @class: a generic class
3673 * Returns the class's container class, which is the class itself if
3674 * it doesn't have generic_class set.
3676 MonoClass*
3677 mini_class_get_container_class (MonoClass *klass)
3679 if (mono_class_is_ginst (klass))
3680 return mono_class_get_generic_class (klass)->container_class;
3682 g_assert (mono_class_is_gtd (klass));
3683 return klass;
3687 * mini_class_get_context:
3688 * @class: a generic class
3690 * Returns the class's generic context.
3692 MonoGenericContext*
3693 mini_class_get_context (MonoClass *klass)
3695 if (mono_class_is_ginst (klass))
3696 return &mono_class_get_generic_class (klass)->context;
3698 g_assert (mono_class_is_gtd (klass));
3699 return &mono_class_get_generic_container (klass)->context;
3703 * mini_get_basic_type_from_generic:
3704 * @type: a type
3706 * Returns a closed type corresponding to the possibly open type
3707 * passed to it.
3709 static MonoType*
3710 mini_get_basic_type_from_generic (MonoType *type)
3712 if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) && mini_is_gsharedvt_type (type))
3713 return type;
3714 else if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR)) {
3715 MonoType *constraint = type->data.generic_param->gshared_constraint;
3716 /* The gparam constraint encodes the type this gparam can represent */
3717 if (!constraint) {
3718 return mono_get_object_type ();
3719 } else {
3720 MonoClass *klass;
3722 g_assert (constraint != m_class_get_byval_arg (m_class_get_parent (mono_defaults.int_class)));
3723 klass = mono_class_from_mono_type_internal (constraint);
3724 return m_class_get_byval_arg (klass);
3726 } else {
3727 return mini_native_type_replace_type (mono_type_get_basic_type_from_generic (type));
3732 * mini_type_get_underlying_type:
3734 * Return the underlying type of TYPE, taking into account enums, byref, bool, char, ref types and generic
3735 * sharing.
3737 MonoType*
3738 mini_type_get_underlying_type (MonoType *type)
3740 type = mini_native_type_replace_type (type);
3742 if (type->byref)
3743 return mono_get_int_type ();
3744 if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) && mini_is_gsharedvt_type (type))
3745 return type;
3746 type = mini_get_basic_type_from_generic (mono_type_get_underlying_type (type));
3747 switch (type->type) {
3748 case MONO_TYPE_BOOLEAN:
3749 return m_class_get_byval_arg (mono_defaults.byte_class);
3750 case MONO_TYPE_CHAR:
3751 return m_class_get_byval_arg (mono_defaults.uint16_class);
3752 case MONO_TYPE_STRING:
3753 case MONO_TYPE_CLASS:
3754 case MONO_TYPE_ARRAY:
3755 case MONO_TYPE_SZARRAY:
3756 return mono_get_object_type ();
3757 default:
3758 return type;
3763 * mini_type_stack_size:
3764 * @t: a type
3765 * @align: Pointer to an int for returning the alignment
3767 * Returns the type's stack size and the alignment in *align.
3770 mini_type_stack_size (MonoType *t, int *align)
3772 return mono_type_stack_size_internal (t, align, TRUE);
3776 * mini_type_stack_size_full:
3778 * Same as mini_type_stack_size, but handle pinvoke data types as well.
3781 mini_type_stack_size_full (MonoType *t, guint32 *align, gboolean pinvoke)
3783 int size;
3785 //g_assert (!mini_is_gsharedvt_type (t));
3787 if (pinvoke) {
3788 size = mono_type_native_stack_size (t, align);
3789 } else {
3790 int ialign;
3792 if (align) {
3793 size = mini_type_stack_size (t, &ialign);
3794 *align = ialign;
3795 } else {
3796 size = mini_type_stack_size (t, NULL);
3800 return size;
3804 * mono_generic_sharing_init:
3806 * Initialize the module.
3808 void
3809 mono_generic_sharing_init (void)
3811 mono_counters_register ("RGCTX template num allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_template_num_allocated);
3812 mono_counters_register ("RGCTX template bytes allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_template_bytes_allocated);
3813 mono_counters_register ("RGCTX oti num allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_oti_num_allocated);
3814 mono_counters_register ("RGCTX oti bytes allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_oti_bytes_allocated);
3815 mono_counters_register ("RGCTX oti num markers", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_oti_num_markers);
3816 mono_counters_register ("RGCTX oti num data", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_oti_num_data);
3817 mono_counters_register ("RGCTX max slot number", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_max_slot_number);
3818 mono_counters_register ("RGCTX num allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_num_allocated);
3819 mono_counters_register ("RGCTX num arrays allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_num_arrays_allocated);
3820 mono_counters_register ("RGCTX bytes allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &rgctx_bytes_allocated);
3821 mono_counters_register ("MRGCTX num arrays allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_num_arrays_allocated);
3822 mono_counters_register ("MRGCTX bytes allocated", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &mrgctx_bytes_allocated);
3823 mono_counters_register ("GSHAREDVT num trampolines", MONO_COUNTER_JIT | MONO_COUNTER_INT, &gsharedvt_num_trampolines);
3825 mono_install_image_unload_hook (mono_class_unregister_image_generic_subclasses, NULL);
3827 mono_os_mutex_init_recursive (&gshared_mutex);
3830 void
3831 mono_generic_sharing_cleanup (void)
3833 mono_remove_image_unload_hook (mono_class_unregister_image_generic_subclasses, NULL);
3835 g_hash_table_destroy (generic_subclass_hash);
3839 * mini_type_var_is_vt:
3841 * Return whenever T is a type variable instantiated with a vtype.
3843 gboolean
3844 mini_type_var_is_vt (MonoType *type)
3846 if (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) {
3847 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);
3848 } else {
3849 g_assert_not_reached ();
3850 return FALSE;
3854 gboolean
3855 mini_type_is_reference (MonoType *type)
3857 type = mini_type_get_underlying_type (type);
3858 return mono_type_is_reference (type);
3861 gboolean
3862 mini_method_is_default_method (MonoMethod *m)
3864 return MONO_CLASS_IS_INTERFACE_INTERNAL (m->klass) && !(m->flags & METHOD_ATTRIBUTE_ABSTRACT);
3867 gboolean
3868 mini_method_needs_mrgctx (MonoMethod *m)
3870 if (mono_class_is_ginst (m->klass) && mini_method_is_default_method (m))
3871 return TRUE;
3872 return (mini_method_get_context (m) && mini_method_get_context (m)->method_inst);
3876 * mini_method_get_rgctx:
3878 * Return the RGCTX which needs to be passed to M when it is called.
3880 gpointer
3881 mini_method_get_rgctx (MonoMethod *m)
3883 ERROR_DECL (error);
3884 MonoVTable *vt = mono_class_vtable_checked (mono_domain_get (), m->klass, error);
3885 mono_error_assert_ok (error);
3886 if (mini_method_needs_mrgctx (m))
3887 return mini_method_get_mrgctx (vt, m);
3888 else
3889 return vt;
3893 * mini_type_is_vtype:
3895 * Return whenever T is a vtype, or a type param instantiated with a vtype.
3896 * Should be used in place of MONO_TYPE_ISSTRUCT () which can't handle gsharedvt.
3898 gboolean
3899 mini_type_is_vtype (MonoType *t)
3901 t = mini_type_get_underlying_type (t);
3903 return MONO_TYPE_ISSTRUCT (t) || mini_is_gsharedvt_variable_type (t);
3906 gboolean
3907 mini_class_is_generic_sharable (MonoClass *klass)
3909 if (mono_class_is_ginst (klass) && is_async_state_machine_class (klass))
3910 return FALSE;
3912 return (mono_class_is_ginst (klass) && mono_generic_context_is_sharable (&mono_class_get_generic_class (klass)->context, FALSE));
3915 gboolean
3916 mini_is_gsharedvt_variable_klass (MonoClass *klass)
3918 return mini_is_gsharedvt_variable_type (m_class_get_byval_arg (klass));
3921 gboolean
3922 mini_is_gsharedvt_gparam (MonoType *t)
3924 /* Matches get_gsharedvt_type () */
3925 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;
3928 static char*
3929 get_shared_gparam_name (MonoTypeEnum constraint, const char *name)
3931 if (constraint == MONO_TYPE_VALUETYPE) {
3932 return g_strdup_printf ("%s_GSHAREDVT", name);
3933 } else if (constraint == MONO_TYPE_OBJECT) {
3934 return g_strdup_printf ("%s_REF", name);
3935 } else if (constraint == MONO_TYPE_GENERICINST) {
3936 return g_strdup_printf ("%s_INST", name);
3937 } else {
3938 MonoType t;
3939 char *tname, *tname2, *res;
3941 memset (&t, 0, sizeof (t));
3942 t.type = constraint;
3943 tname = mono_type_full_name (&t);
3944 tname2 = g_utf8_strup (tname, strlen (tname));
3945 res = g_strdup_printf ("%s_%s", name, tname2);
3946 g_free (tname);
3947 g_free (tname2);
3948 return res;
3952 static guint
3953 shared_gparam_hash (gconstpointer data)
3955 MonoGSharedGenericParam *p = (MonoGSharedGenericParam*)data;
3956 guint hash;
3958 hash = mono_metadata_generic_param_hash (p->parent);
3959 hash = ((hash << 5) - hash) ^ mono_metadata_type_hash (p->param.gshared_constraint);
3961 return hash;
3964 static gboolean
3965 shared_gparam_equal (gconstpointer ka, gconstpointer kb)
3967 MonoGSharedGenericParam *p1 = (MonoGSharedGenericParam*)ka;
3968 MonoGSharedGenericParam *p2 = (MonoGSharedGenericParam*)kb;
3970 if (p1 == p2)
3971 return TRUE;
3972 if (p1->parent != p2->parent)
3973 return FALSE;
3974 if (!mono_metadata_type_equal (p1->param.gshared_constraint, p2->param.gshared_constraint))
3975 return FALSE;
3976 return TRUE;
3980 * mini_get_shared_gparam:
3982 * Create an anonymous gparam from T with a constraint which encodes which types can match it.
3984 MonoType*
3985 mini_get_shared_gparam (MonoType *t, MonoType *constraint)
3987 MonoImageSet *set;
3988 MonoGenericParam *par = t->data.generic_param;
3989 MonoGSharedGenericParam *copy, key;
3990 MonoType *res;
3991 MonoImage *image = NULL;
3992 char *name;
3994 set = mono_metadata_merge_image_sets (mono_metadata_get_image_set_for_type (t), mono_metadata_get_image_set_for_type (constraint));
3996 memset (&key, 0, sizeof (key));
3997 key.parent = par;
3998 key.param.gshared_constraint = constraint;
4000 g_assert (mono_generic_param_info (par));
4001 image = mono_get_image_for_generic_param(par);
4004 * Need a cache to ensure the newly created gparam
4005 * is unique wrt T/CONSTRAINT.
4007 mono_image_set_lock (set);
4008 if (!set->gshared_types) {
4009 set->gshared_types_len = MONO_TYPE_INTERNAL;
4010 set->gshared_types = g_new0 (GHashTable*, set->gshared_types_len);
4012 if (!set->gshared_types [constraint->type])
4013 set->gshared_types [constraint->type] = g_hash_table_new (shared_gparam_hash, shared_gparam_equal);
4014 res = (MonoType *)g_hash_table_lookup (set->gshared_types [constraint->type], &key);
4015 mono_image_set_unlock (set);
4016 if (res)
4017 return res;
4018 copy = (MonoGSharedGenericParam *)mono_image_set_alloc0 (set, sizeof (MonoGSharedGenericParam));
4019 memcpy (&copy->param, par, sizeof (MonoGenericParamFull));
4020 copy->param.info.pklass = NULL;
4021 // FIXME:
4022 constraint = mono_metadata_type_dup (NULL, constraint);
4023 name = get_shared_gparam_name (constraint->type, ((MonoGenericParamFull*)copy)->info.name);
4024 copy->param.info.name = mono_image_set_strdup (set, name);
4025 g_free (name);
4027 copy->param.owner = par->owner;
4028 g_assert (!par->owner->is_anonymous);
4030 copy->param.gshared_constraint = constraint;
4031 copy->parent = par;
4032 res = mono_metadata_type_dup (NULL, t);
4033 res->data.generic_param = (MonoGenericParam*)copy;
4035 mono_image_set_lock (set);
4036 /* Duplicates are ok */
4037 g_hash_table_insert (set->gshared_types [constraint->type], copy, res);
4038 mono_image_set_unlock (set);
4040 return res;
4043 static MonoGenericInst*
4044 get_shared_inst (MonoGenericInst *inst, MonoGenericInst *shared_inst, MonoGenericContainer *container, gboolean use_gsharedvt);
4046 static MonoType*
4047 get_shared_type (MonoType *t, MonoType *type)
4049 MonoTypeEnum ttype;
4051 if (!type->byref && type->type == MONO_TYPE_GENERICINST && MONO_TYPE_ISSTRUCT (type)) {
4052 ERROR_DECL (error);
4053 MonoGenericClass *gclass = type->data.generic_class;
4054 MonoGenericContext context;
4055 MonoClass *k;
4057 memset (&context, 0, sizeof (context));
4058 if (gclass->context.class_inst)
4059 context.class_inst = get_shared_inst (gclass->context.class_inst, mono_class_get_generic_container (gclass->container_class)->context.class_inst, NULL, FALSE);
4060 if (gclass->context.method_inst)
4061 context.method_inst = get_shared_inst (gclass->context.method_inst, mono_class_get_generic_container (gclass->container_class)->context.method_inst, NULL, FALSE);
4063 k = mono_class_inflate_generic_class_checked (gclass->container_class, &context, error);
4064 mono_error_assert_ok (error); /* FIXME don't swallow the error */
4066 return mini_get_shared_gparam (t, m_class_get_byval_arg (k));
4067 } else if (MONO_TYPE_ISSTRUCT (type)) {
4068 return type;
4071 /* Create a type variable with a constraint which encodes which types can match it */
4072 ttype = type->type;
4073 if (type->type == MONO_TYPE_VALUETYPE) {
4074 ttype = mono_class_enum_basetype_internal (type->data.klass)->type;
4075 } else if (type->type == MONO_TYPE_GENERICINST && m_class_is_enumtype(type->data.generic_class->container_class)) {
4076 ttype = mono_class_enum_basetype_internal (mono_class_from_mono_type_internal (type))->type;
4077 } else if (MONO_TYPE_IS_REFERENCE (type)) {
4078 ttype = MONO_TYPE_OBJECT;
4079 } else if (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) {
4080 if (type->data.generic_param->gshared_constraint)
4081 return mini_get_shared_gparam (t, type->data.generic_param->gshared_constraint);
4082 ttype = MONO_TYPE_OBJECT;
4086 MonoType t2;
4087 MonoClass *klass;
4089 memset (&t2, 0, sizeof (t2));
4090 t2.type = ttype;
4091 klass = mono_class_from_mono_type_internal (&t2);
4093 return mini_get_shared_gparam (t, m_class_get_byval_arg (klass));
4097 static MonoType*
4098 get_gsharedvt_type (MonoType *t)
4100 /* Use TypeHandle as the constraint type since its a valuetype */
4101 return mini_get_shared_gparam (t, m_class_get_byval_arg (mono_defaults.typehandle_class));
4104 static MonoGenericInst*
4105 get_shared_inst (MonoGenericInst *inst, MonoGenericInst *shared_inst, MonoGenericContainer *container, gboolean use_gsharedvt)
4107 MonoGenericInst *res;
4108 MonoType **type_argv;
4109 int i;
4111 type_argv = g_new0 (MonoType*, inst->type_argc);
4112 for (i = 0; i < inst->type_argc; ++i) {
4113 if (use_gsharedvt) {
4114 type_argv [i] = get_gsharedvt_type (shared_inst->type_argv [i]);
4115 } else {
4116 /* These types match the ones in mini_generic_inst_is_sharable () */
4117 type_argv [i] = get_shared_type (shared_inst->type_argv [i], inst->type_argv [i]);
4121 res = mono_metadata_get_generic_inst (inst->type_argc, type_argv);
4122 g_free (type_argv);
4123 return res;
4127 * mini_get_shared_method_full:
4128 * \param method the method to find the shared version of.
4129 * \param flags controls what sort of shared version to find
4130 * \param error set if we hit any fatal error
4132 * \returns The method which is actually compiled/registered when doing generic sharing.
4134 * If flags & SHARE_MODE_GSHAREDVT, produce a method using the gsharedvt instantiation.
4135 * \p method can be a non-inflated generic method.
4137 MonoMethod*
4138 mini_get_shared_method_full (MonoMethod *method, GetSharedMethodFlags flags, MonoError *error)
4141 MonoGenericContext shared_context;
4142 MonoMethod *declaring_method;
4143 MonoGenericContainer *class_container, *method_container = NULL;
4144 MonoGenericContext *context = mono_method_get_context (method);
4145 MonoGenericInst *inst;
4146 WrapperInfo *info = NULL;
4148 error_init (error);
4151 * Instead of creating a shared version of the wrapper, create a shared version of the original
4152 * method and construct a wrapper for it. Otherwise, we could end up with two copies of the
4153 * same wrapper, breaking AOT which assumes wrappers are unique.
4154 * FIXME: Add other cases.
4156 if (method->wrapper_type)
4157 info = mono_marshal_get_wrapper_info (method);
4158 switch (method->wrapper_type) {
4159 case MONO_WRAPPER_SYNCHRONIZED: {
4160 MonoMethod *wrapper = mono_marshal_method_from_wrapper (method);
4162 MonoMethod *gwrapper = mini_get_shared_method_full (wrapper, flags, error);
4163 return_val_if_nok (error, NULL);
4165 return mono_marshal_get_synchronized_wrapper (gwrapper);
4167 case MONO_WRAPPER_DELEGATE_INVOKE: {
4168 if (info->subtype == WRAPPER_SUBTYPE_NONE) {
4169 MonoMethod *ginvoke = mini_get_shared_method_full (info->d.delegate_invoke.method, flags, error);
4170 return_val_if_nok (error, NULL);
4172 return mono_marshal_get_delegate_invoke (ginvoke, NULL);
4174 break;
4176 case MONO_WRAPPER_DELEGATE_BEGIN_INVOKE:
4177 case MONO_WRAPPER_DELEGATE_END_INVOKE: {
4178 MonoMethod *ginvoke = mini_get_shared_method_full (info->d.delegate_invoke.method, flags, error);
4179 return_val_if_nok (error, NULL);
4181 if (method->wrapper_type == MONO_WRAPPER_DELEGATE_BEGIN_INVOKE)
4182 return mono_marshal_get_delegate_begin_invoke (ginvoke);
4183 else
4184 return mono_marshal_get_delegate_end_invoke (ginvoke);
4186 default:
4187 break;
4190 if (method->is_generic || (mono_class_is_gtd (method->klass) && !method->is_inflated)) {
4191 declaring_method = method;
4192 } else {
4193 declaring_method = mono_method_get_declaring_generic_method (method);
4196 /* shared_context is the context containing type variables. */
4197 if (declaring_method->is_generic)
4198 shared_context = mono_method_get_generic_container (declaring_method)->context;
4199 else
4200 shared_context = mono_class_get_generic_container (declaring_method->klass)->context;
4202 gboolean use_gsharedvt_inst = FALSE;
4203 if (flags & SHARE_MODE_GSHAREDVT)
4204 use_gsharedvt_inst = TRUE;
4205 else if (!mono_method_is_generic_sharable_full (method, FALSE, TRUE, FALSE))
4206 use_gsharedvt_inst = mini_is_gsharedvt_sharable_method (method);
4208 class_container = mono_class_try_get_generic_container (declaring_method->klass); //FIXME is this a case for a try_get?
4209 method_container = mono_method_get_generic_container (declaring_method);
4212 * Create the shared context by replacing the ref type arguments with
4213 * type parameters, and keeping the rest.
4215 if (context)
4216 inst = context->class_inst;
4217 else
4218 inst = shared_context.class_inst;
4219 if (inst)
4220 shared_context.class_inst = get_shared_inst (inst, shared_context.class_inst, class_container, use_gsharedvt_inst);
4222 if (context)
4223 inst = context->method_inst;
4224 else
4225 inst = shared_context.method_inst;
4226 if (inst)
4227 shared_context.method_inst = get_shared_inst (inst, shared_context.method_inst, method_container, use_gsharedvt_inst);
4229 return mono_class_inflate_generic_method_checked (declaring_method, &shared_context, error);
4233 mini_get_rgctx_entry_slot (MonoJumpInfoRgctxEntry *entry)
4235 gpointer entry_data = NULL;
4237 switch (entry->data->type) {
4238 case MONO_PATCH_INFO_CLASS:
4239 entry_data = m_class_get_byval_arg (entry->data->data.klass);
4240 break;
4241 case MONO_PATCH_INFO_METHOD:
4242 case MONO_PATCH_INFO_METHODCONST:
4243 entry_data = entry->data->data.method;
4244 break;
4245 case MONO_PATCH_INFO_FIELD:
4246 entry_data = entry->data->data.field;
4247 break;
4248 case MONO_PATCH_INFO_SIGNATURE:
4249 entry_data = entry->data->data.sig;
4250 break;
4251 case MONO_PATCH_INFO_GSHAREDVT_CALL: {
4252 MonoJumpInfoGSharedVtCall *call_info = (MonoJumpInfoGSharedVtCall *)g_malloc0 (sizeof (MonoJumpInfoGSharedVtCall)); //mono_domain_alloc0 (domain, sizeof (MonoJumpInfoGSharedVtCall));
4254 memcpy (call_info, entry->data->data.gsharedvt, sizeof (MonoJumpInfoGSharedVtCall));
4255 entry_data = call_info;
4256 break;
4258 case MONO_PATCH_INFO_GSHAREDVT_METHOD: {
4259 MonoGSharedVtMethodInfo *info;
4260 MonoGSharedVtMethodInfo *oinfo = entry->data->data.gsharedvt_method;
4261 int i;
4263 /* Make a copy into the domain mempool */
4264 info = (MonoGSharedVtMethodInfo *)g_malloc0 (sizeof (MonoGSharedVtMethodInfo)); //mono_domain_alloc0 (domain, sizeof (MonoGSharedVtMethodInfo));
4265 info->method = oinfo->method;
4266 info->num_entries = oinfo->num_entries;
4267 info->entries = (MonoRuntimeGenericContextInfoTemplate *)g_malloc0 (sizeof (MonoRuntimeGenericContextInfoTemplate) * info->num_entries);
4268 for (i = 0; i < oinfo->num_entries; ++i) {
4269 MonoRuntimeGenericContextInfoTemplate *otemplate = &oinfo->entries [i];
4270 MonoRuntimeGenericContextInfoTemplate *template_ = &info->entries [i];
4272 memcpy (template_, otemplate, sizeof (MonoRuntimeGenericContextInfoTemplate));
4274 entry_data = info;
4275 break;
4277 case MONO_PATCH_INFO_VIRT_METHOD: {
4278 MonoJumpInfoVirtMethod *info;
4279 MonoJumpInfoVirtMethod *oinfo = entry->data->data.virt_method;
4281 info = (MonoJumpInfoVirtMethod *)g_malloc0 (sizeof (MonoJumpInfoVirtMethod));
4282 memcpy (info, oinfo, sizeof (MonoJumpInfoVirtMethod));
4283 entry_data = info;
4284 break;
4286 case MONO_PATCH_INFO_DELEGATE_TRAMPOLINE: {
4287 MonoDelegateClassMethodPair *info;
4288 MonoDelegateClassMethodPair *oinfo = entry->data->data.del_tramp;
4290 info = (MonoDelegateClassMethodPair *)g_malloc0 (sizeof (MonoDelegateClassMethodPair));
4291 memcpy (info, oinfo, sizeof (MonoDelegateClassMethodPair));
4292 entry_data = info;
4293 break;
4295 default:
4296 g_assert_not_reached ();
4297 case MONO_PATCH_INFO_NONE:
4298 break;
4301 if (entry->in_mrgctx)
4302 return lookup_or_register_info (entry->d.method->klass, entry->d.method, entry->in_mrgctx, entry_data, entry->info_type, mono_method_get_context (entry->d.method));
4303 else
4304 return lookup_or_register_info (entry->d.klass, NULL, entry->in_mrgctx, entry_data, entry->info_type, mono_class_get_context (entry->d.klass));
4307 static gboolean gsharedvt_supported;
4309 void
4310 mono_set_generic_sharing_vt_supported (gboolean supported)
4312 /* ensure we do not disable gsharedvt once it's been enabled */
4313 if (!gsharedvt_supported && supported)
4314 gsharedvt_supported = TRUE;
4317 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
4320 * mini_is_gsharedvt_type:
4322 * Return whenever T references type arguments instantiated with gshared vtypes.
4324 gboolean
4325 mini_is_gsharedvt_type (MonoType *t)
4327 int i;
4329 if (t->byref)
4330 return FALSE;
4331 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)
4332 return TRUE;
4333 else if (t->type == MONO_TYPE_GENERICINST) {
4334 MonoGenericClass *gclass = t->data.generic_class;
4335 MonoGenericContext *context = &gclass->context;
4336 MonoGenericInst *inst;
4338 inst = context->class_inst;
4339 if (inst) {
4340 for (i = 0; i < inst->type_argc; ++i)
4341 if (mini_is_gsharedvt_type (inst->type_argv [i]))
4342 return TRUE;
4344 inst = context->method_inst;
4345 if (inst) {
4346 for (i = 0; i < inst->type_argc; ++i)
4347 if (mini_is_gsharedvt_type (inst->type_argv [i]))
4348 return TRUE;
4351 return FALSE;
4352 } else {
4353 return FALSE;
4357 gboolean
4358 mini_is_gsharedvt_klass (MonoClass *klass)
4360 return mini_is_gsharedvt_type (m_class_get_byval_arg (klass));
4363 gboolean
4364 mini_is_gsharedvt_signature (MonoMethodSignature *sig)
4366 int i;
4368 if (sig->ret && mini_is_gsharedvt_type (sig->ret))
4369 return TRUE;
4370 for (i = 0; i < sig->param_count; ++i) {
4371 if (mini_is_gsharedvt_type (sig->params [i]))
4372 return TRUE;
4374 return FALSE;
4378 * mini_is_gsharedvt_variable_type:
4380 * Return whenever T refers to a GSHAREDVT type whose size differs depending on the values of type parameters.
4382 gboolean
4383 mini_is_gsharedvt_variable_type (MonoType *t)
4385 if (!mini_is_gsharedvt_type (t))
4386 return FALSE;
4387 if (t->type == MONO_TYPE_GENERICINST) {
4388 MonoGenericClass *gclass = t->data.generic_class;
4389 MonoGenericContext *context = &gclass->context;
4390 MonoGenericInst *inst;
4391 int i;
4393 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))
4394 return FALSE;
4396 inst = context->class_inst;
4397 if (inst) {
4398 for (i = 0; i < inst->type_argc; ++i)
4399 if (mini_is_gsharedvt_variable_type (inst->type_argv [i]))
4400 return TRUE;
4402 inst = context->method_inst;
4403 if (inst) {
4404 for (i = 0; i < inst->type_argc; ++i)
4405 if (mini_is_gsharedvt_variable_type (inst->type_argv [i]))
4406 return TRUE;
4409 return FALSE;
4411 return TRUE;
4414 static gboolean
4415 is_variable_size (MonoType *t)
4417 int i;
4419 if (t->byref)
4420 return FALSE;
4422 if (t->type == MONO_TYPE_VAR || t->type == MONO_TYPE_MVAR) {
4423 MonoGenericParam *param = t->data.generic_param;
4425 if (param->gshared_constraint && param->gshared_constraint->type != MONO_TYPE_VALUETYPE && param->gshared_constraint->type != MONO_TYPE_GENERICINST)
4426 return FALSE;
4427 if (param->gshared_constraint && param->gshared_constraint->type == MONO_TYPE_GENERICINST)
4428 return is_variable_size (param->gshared_constraint);
4429 return TRUE;
4431 if (t->type == MONO_TYPE_GENERICINST && m_class_get_byval_arg (t->data.generic_class->container_class)->type == MONO_TYPE_VALUETYPE) {
4432 MonoGenericClass *gclass = t->data.generic_class;
4433 MonoGenericContext *context = &gclass->context;
4434 MonoGenericInst *inst;
4436 inst = context->class_inst;
4437 if (inst) {
4438 for (i = 0; i < inst->type_argc; ++i)
4439 if (is_variable_size (inst->type_argv [i]))
4440 return TRUE;
4442 inst = context->method_inst;
4443 if (inst) {
4444 for (i = 0; i < inst->type_argc; ++i)
4445 if (is_variable_size (inst->type_argv [i]))
4446 return TRUE;
4450 return FALSE;
4453 gboolean
4454 mini_is_gsharedvt_sharable_inst (MonoGenericInst *inst)
4456 int i;
4457 gboolean has_vt = FALSE;
4459 for (i = 0; i < inst->type_argc; ++i) {
4460 MonoType *type = inst->type_argv [i];
4462 if ((MONO_TYPE_IS_REFERENCE (type) || type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) && !mini_is_gsharedvt_type (type)) {
4463 } else {
4464 has_vt = TRUE;
4468 return has_vt;
4471 gboolean
4472 mini_is_gsharedvt_inst (MonoGenericInst *inst)
4474 int i;
4476 for (i = 0; i < inst->type_argc; ++i) {
4477 MonoType *type = inst->type_argv [i];
4479 if (mini_is_gsharedvt_type (type))
4480 return TRUE;
4483 return FALSE;
4486 gboolean
4487 mini_is_gsharedvt_sharable_method (MonoMethod *method)
4489 MonoMethodSignature *sig;
4492 * A method is gsharedvt if:
4493 * - it has type parameters instantiated with vtypes
4495 if (!gsharedvt_supported)
4496 return FALSE;
4497 if (method->is_inflated) {
4498 MonoMethodInflated *inflated = (MonoMethodInflated*)method;
4499 MonoGenericContext *context = &inflated->context;
4500 MonoGenericInst *inst;
4502 if (context->class_inst && context->method_inst) {
4503 /* At least one inst has to be gsharedvt sharable, and the other normal or gsharedvt sharable */
4504 gboolean vt1 = mini_is_gsharedvt_sharable_inst (context->class_inst);
4505 gboolean vt2 = mini_is_gsharedvt_sharable_inst (context->method_inst);
4507 if ((vt1 && vt2) ||
4508 (vt1 && mini_generic_inst_is_sharable (context->method_inst, TRUE, FALSE)) ||
4509 (vt2 && mini_generic_inst_is_sharable (context->class_inst, TRUE, FALSE)))
4511 else
4512 return FALSE;
4513 } else {
4514 inst = context->class_inst;
4515 if (inst && !mini_is_gsharedvt_sharable_inst (inst))
4516 return FALSE;
4517 inst = context->method_inst;
4518 if (inst && !mini_is_gsharedvt_sharable_inst (inst))
4519 return FALSE;
4521 } else {
4522 return FALSE;
4525 sig = mono_method_signature_internal (mono_method_get_declaring_generic_method (method));
4526 if (!sig)
4527 return FALSE;
4530 if (mini_is_gsharedvt_variable_signature (sig))
4531 return FALSE;
4534 //DEBUG ("GSHAREDVT SHARABLE: %s\n", mono_method_full_name (method, TRUE));
4536 return TRUE;
4540 * mini_is_gsharedvt_variable_signature:
4542 * Return whenever the calling convention used to call SIG varies depending on the values of type parameters used by SIG,
4543 * i.e. FALSE for swap(T[] arr, int i, int j), TRUE for T get_t ().
4545 gboolean
4546 mini_is_gsharedvt_variable_signature (MonoMethodSignature *sig)
4548 int i;
4550 if (sig->ret && is_variable_size (sig->ret))
4551 return TRUE;
4552 for (i = 0; i < sig->param_count; ++i) {
4553 MonoType *t = sig->params [i];
4555 if (is_variable_size (t))
4556 return TRUE;
4558 return FALSE;
4561 MonoMethod*
4562 mini_method_to_shared (MonoMethod *method)
4564 if (!mono_method_is_generic_impl (method))
4565 return NULL;
4567 ERROR_DECL (error);
4569 // This pattern is based on add_extra_method_with_depth.
4571 if (mono_method_is_generic_sharable_full (method, TRUE, TRUE, FALSE))
4572 // gshared over reference type
4573 method = mini_get_shared_method_full (method, SHARE_MODE_NONE, error);
4574 else if (mono_method_is_generic_sharable_full (method, FALSE, FALSE, TRUE))
4575 // gshared over valuetype (or primitive?)
4576 method = mini_get_shared_method_full (method, SHARE_MODE_GSHAREDVT, error);
4577 else
4578 return NULL;
4579 mono_error_assert_ok (error);
4580 return method;
4583 #else
4585 gboolean
4586 mini_is_gsharedvt_type (MonoType *t)
4588 return FALSE;
4591 gboolean
4592 mini_is_gsharedvt_klass (MonoClass *klass)
4594 return FALSE;
4597 gboolean
4598 mini_is_gsharedvt_signature (MonoMethodSignature *sig)
4600 return FALSE;
4603 gboolean
4604 mini_is_gsharedvt_variable_type (MonoType *t)
4606 return FALSE;
4609 gboolean
4610 mini_is_gsharedvt_sharable_method (MonoMethod *method)
4612 return FALSE;
4615 gboolean
4616 mini_is_gsharedvt_variable_signature (MonoMethodSignature *sig)
4618 return FALSE;
4621 MonoMethod*
4622 mini_method_to_shared (MonoMethod *method)
4624 return NULL;
4627 #endif /* !MONO_ARCH_GSHAREDVT_SUPPORTED */