[tests] Reenable enum equals test on interpreter (#18673)
[mono-project.git] / mono / mini / llvmonly-runtime.c
blob39fcb2d288820472431add799286902b9a7d87ac
1 /**
2 * \file
3 * llvmonly runtime support code.
5 */
7 #include <config.h>
8 #include "llvmonly-runtime.h"
9 #include "aot-runtime.h"
12 * mini_llvmonly_load_method:
14 * Return the AOT-ed code METHOD, or an interpreter entry for it.
17 gpointer
18 mini_llvmonly_load_method (MonoMethod *method, gboolean caller_gsharedvt, gboolean need_unbox, gpointer *out_arg, MonoError *error)
20 gpointer addr = mono_compile_method_checked (method, error);
21 return_val_if_nok (error, NULL);
23 if (addr) {
24 return mini_llvmonly_add_method_wrappers (method, (gpointer)addr, caller_gsharedvt, need_unbox, out_arg);
25 } else {
26 MonoFtnDesc *desc = mini_get_interp_callbacks ()->create_method_pointer_llvmonly (method, need_unbox, error);
27 return_val_if_nok (error, NULL);
28 *out_arg = desc->arg;
29 return desc->addr;
34 * Same but returns an ftndesc which might be newly allocated.
36 MonoFtnDesc*
37 mini_llvmonly_load_method_ftndesc (MonoMethod *method, gboolean caller_gsharedvt, gboolean need_unbox, MonoError *error)
39 gpointer addr = mono_compile_method_checked (method, error);
40 return_val_if_nok (error, NULL);
42 if (addr) {
43 gpointer arg = NULL;
44 addr = mini_llvmonly_add_method_wrappers (method, (gpointer)addr, caller_gsharedvt, need_unbox, &arg);
45 // FIXME: Cache this
46 return mini_llvmonly_create_ftndesc (mono_domain_get (), addr, arg);
47 } else {
48 MonoFtnDesc *ftndesc = mini_get_interp_callbacks ()->create_method_pointer_llvmonly (method, need_unbox, error);
49 return_val_if_nok (error, NULL);
50 return ftndesc;
55 * Same as load_method, but for delegates.
56 * See mini_llvmonly_get_delegate_arg ().
58 gpointer
59 mini_llvmonly_load_method_delegate (MonoMethod *method, gboolean caller_gsharedvt, gboolean need_unbox, gpointer *out_arg, MonoError *error)
61 gpointer addr = mono_compile_method_checked (method, error);
62 return_val_if_nok (error, NULL);
64 if (addr) {
65 if (need_unbox)
66 addr = mono_aot_get_unbox_trampoline (method, NULL);
67 *out_arg = mini_llvmonly_get_delegate_arg (method, addr);
68 return addr;
69 } else {
70 MonoFtnDesc *desc = mini_get_interp_callbacks ()->create_method_pointer_llvmonly (method, need_unbox, error);
71 return_val_if_nok (error, NULL);
73 g_assert (!caller_gsharedvt);
74 *out_arg = desc->arg;
75 return desc->addr;
79 gpointer
80 mini_llvmonly_get_delegate_arg (MonoMethod *method, gpointer method_ptr)
82 gpointer arg = NULL;
84 if (mono_method_needs_static_rgctx_invoke (method, FALSE))
85 arg = mini_method_get_rgctx (method);
88 * Avoid adding gsharedvt in wrappers since they might not exist if
89 * this delegate is called through a gsharedvt delegate invoke wrapper.
90 * Instead, encode that the method is gsharedvt in del->extra_arg,
91 * the CEE_MONO_CALLI_EXTRA_ARG implementation in the JIT depends on this.
93 g_assert ((((gsize)arg) & 1) == 0);
94 if (method->is_inflated && (mono_aot_get_method_flags ((guint8*)method_ptr) & MONO_AOT_METHOD_FLAG_GSHAREDVT_VARIABLE)) {
95 arg = (gpointer)(((gsize)arg) | 1);
97 return arg;
102 * mini_llvmonly_create_ftndesc:
104 * Create a function descriptor of the form <addr, arg>, which
105 * represents a callee ADDR with ARG as the last argument.
106 * This is used for:
107 * - generic sharing (ARG is the rgctx)
108 * - gsharedvt signature wrappers (ARG is a function descriptor)
110 MonoFtnDesc*
111 mini_llvmonly_create_ftndesc (MonoDomain *domain, gpointer addr, gpointer arg)
113 MonoFtnDesc *ftndesc = (MonoFtnDesc*)mono_domain_alloc0 (mono_domain_get (), 2 * sizeof (gpointer));
114 ftndesc->addr = addr;
115 ftndesc->arg = arg;
117 return ftndesc;
121 * mini_llvmonly_add_method_wrappers:
123 * Add unbox/gsharedvt wrappers around COMPILED_METHOD if needed. Return the wrapper address or COMPILED_METHOD
124 * if no wrapper is needed. Set OUT_ARG to the rgctx/extra argument needed to be passed to the returned method.
126 gpointer
127 mini_llvmonly_add_method_wrappers (MonoMethod *m, gpointer compiled_method, gboolean caller_gsharedvt, gboolean add_unbox_tramp, gpointer *out_arg)
129 gpointer addr;
130 gboolean callee_gsharedvt;
132 *out_arg = NULL;
134 if (m->wrapper_type == MONO_WRAPPER_MANAGED_TO_MANAGED) {
135 WrapperInfo *info = mono_marshal_get_wrapper_info (m);
138 * generic array helpers.
139 * Have to replace the wrappers with the original generic instances.
141 if (info && info->subtype == WRAPPER_SUBTYPE_GENERIC_ARRAY_HELPER) {
142 m = info->d.generic_array_helper.method;
144 } else if (m->wrapper_type == MONO_WRAPPER_OTHER) {
145 WrapperInfo *info = mono_marshal_get_wrapper_info (m);
147 /* Same for synchronized inner wrappers */
148 if (info && info->subtype == WRAPPER_SUBTYPE_SYNCHRONIZED_INNER) {
149 m = info->d.synchronized_inner.method;
153 addr = compiled_method;
155 if (add_unbox_tramp) {
157 * The unbox trampolines call the method directly, so need to add
158 * an rgctx tramp before them.
160 addr = mono_aot_get_unbox_trampoline (m, addr);
163 g_assert (mono_llvm_only);
164 g_assert (out_arg);
166 callee_gsharedvt = mono_aot_get_method_flags ((guint8*)compiled_method) & MONO_AOT_METHOD_FLAG_GSHAREDVT_VARIABLE;
168 if (!caller_gsharedvt && callee_gsharedvt) {
169 MonoMethodSignature *sig, *gsig;
170 MonoJitInfo *ji;
171 MonoMethod *jmethod;
172 gpointer wrapper_addr;
174 ji = mini_jit_info_table_find (mono_domain_get (), (char *)mono_get_addr_from_ftnptr (compiled_method), NULL);
175 g_assert (ji);
176 jmethod = jinfo_get_method (ji);
178 /* Here m is a generic instance, while ji->method is the gsharedvt method implementing it */
180 /* Call from normal/gshared code to gsharedvt code with variable signature */
181 sig = mono_method_signature_internal (m);
182 gsig = mono_method_signature_internal (jmethod);
184 wrapper_addr = mini_get_gsharedvt_wrapper (TRUE, addr, sig, gsig, -1, FALSE);
187 * This is a gsharedvt in wrapper, it gets passed a ftndesc for the gsharedvt method as an argument.
189 *out_arg = mini_llvmonly_create_ftndesc (mono_domain_get (), addr, mini_method_get_rgctx (m));
190 addr = wrapper_addr;
191 //printf ("IN: %s\n", mono_method_full_name (m, TRUE));
194 if (!(*out_arg) && mono_method_needs_static_rgctx_invoke (m, FALSE))
195 *out_arg = mini_method_get_rgctx (m);
197 if (caller_gsharedvt && !callee_gsharedvt) {
199 * The callee uses the gsharedvt calling convention, have to add an out wrapper.
201 gpointer out_wrapper = mini_get_gsharedvt_wrapper (FALSE, NULL, mono_method_signature_internal (m), NULL, -1, FALSE);
202 MonoFtnDesc *out_wrapper_arg = mini_llvmonly_create_ftndesc (mono_domain_get (), addr, *out_arg);
204 addr = out_wrapper;
205 *out_arg = out_wrapper_arg;
208 return addr;
212 typedef struct {
213 MonoVTable *vtable;
214 int slot;
215 } IMTTrampInfo;
217 typedef gpointer (*IMTTrampFunc) (gpointer *arg, MonoMethod *imt_method);
220 * mini_llvmonly_initial_imt_tramp:
222 * This function is called the first time a call is made through an IMT trampoline.
223 * It should have the same signature as the llvmonly_imt_tramp_... functions.
225 static gpointer
226 mini_llvmonly_initial_imt_tramp (gpointer *arg, MonoMethod *imt_method)
228 IMTTrampInfo *info = (IMTTrampInfo*)arg;
229 IMTTrampFunc **imt;
230 IMTTrampFunc *ftndesc;
231 IMTTrampFunc func;
233 mono_vtable_build_imt_slot (info->vtable, info->slot);
235 imt = (IMTTrampFunc**)info->vtable;
236 imt -= MONO_IMT_SIZE;
238 /* Return what the real IMT trampoline returns */
239 ftndesc = imt [info->slot];
240 func = ftndesc [0];
242 if (func == (IMTTrampFunc)mini_llvmonly_initial_imt_tramp)
243 /* Happens when the imt slot contains only a generic virtual method */
244 return NULL;
245 return func ((gpointer *)ftndesc [1], imt_method);
248 /* This is called indirectly through an imt slot. */
249 static gpointer
250 llvmonly_imt_tramp (gpointer *arg, MonoMethod *imt_method)
252 int i = 0;
254 /* arg points to an array created in mini_llvmonly_get_imt_trampoline () */
255 while (arg [i] && arg [i] != imt_method)
256 i += 2;
257 g_assert (arg [i]);
259 return arg [i + 1];
262 /* Optimized versions of mini_llvmonly_imt_trampoline () for different table sizes */
263 static gpointer
264 llvmonly_imt_tramp_1 (gpointer *arg, MonoMethod *imt_method)
266 //g_assert (arg [0] == imt_method);
267 return arg [1];
270 static gpointer
271 llvmonly_imt_tramp_2 (gpointer *arg, MonoMethod *imt_method)
273 //g_assert (arg [0] == imt_method || arg [2] == imt_method);
274 if (arg [0] == imt_method)
275 return arg [1];
276 else
277 return arg [3];
280 static gpointer
281 llvmonly_imt_tramp_3 (gpointer *arg, MonoMethod *imt_method)
283 //g_assert (arg [0] == imt_method || arg [2] == imt_method || arg [4] == imt_method);
284 if (arg [0] == imt_method)
285 return arg [1];
286 else if (arg [2] == imt_method)
287 return arg [3];
288 else
289 return arg [5];
293 * A version of the imt trampoline used for generic virtual/variant iface methods.
294 * Unlikely a normal imt trampoline, its possible that IMT_METHOD is not found
295 * in the search table. The original JIT code had a 'fallback' trampoline it could
296 * call, but we can't do that, so we just return NULL, and the compiled code
297 * will handle it.
299 static gpointer
300 llvmonly_fallback_imt_tramp (gpointer *arg, MonoMethod *imt_method)
302 int i = 0;
304 while (arg [i] && arg [i] != imt_method)
305 i += 2;
306 if (!arg [i])
307 return NULL;
309 return arg [i + 1];
312 gpointer
313 mini_llvmonly_get_imt_trampoline (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count, gpointer fail_tramp)
315 gpointer *buf;
316 gpointer *res;
317 int i, index, real_count;
318 gboolean virtual_generic = FALSE;
321 * Create an array which is passed to the imt trampoline functions.
322 * The array contains MonoMethod-function descriptor pairs, terminated by a NULL entry.
325 real_count = 0;
326 for (i = 0; i < count; ++i) {
327 MonoIMTCheckItem *item = imt_entries [i];
329 if (item->is_equals)
330 real_count ++;
331 if (item->has_target_code)
332 virtual_generic = TRUE;
336 * Initialize all vtable entries reachable from this imt slot, so the compiled
337 * code doesn't have to check it.
339 for (i = 0; i < count; ++i) {
340 MonoIMTCheckItem *item = imt_entries [i];
341 int vt_slot;
343 if (!item->is_equals || item->has_target_code)
344 continue;
345 vt_slot = item->value.vtable_slot;
346 mini_llvmonly_init_vtable_slot (vtable, vt_slot);
349 /* Save the entries into an array */
350 buf = (void **)mono_domain_alloc (domain, (real_count + 1) * 2 * sizeof (gpointer));
351 index = 0;
352 for (i = 0; i < count; ++i) {
353 MonoIMTCheckItem *item = imt_entries [i];
355 if (!item->is_equals)
356 continue;
358 g_assert (item->key);
359 buf [(index * 2)] = item->key;
360 if (item->has_target_code)
361 buf [(index * 2) + 1] = item->value.target_code;
362 else
363 buf [(index * 2) + 1] = vtable->vtable [item->value.vtable_slot];
364 index ++;
366 buf [(index * 2)] = NULL;
367 buf [(index * 2) + 1] = fail_tramp;
370 * Return a function descriptor for a C function with 'buf' as its argument.
371 * It will by called by JITted code.
373 res = (void **)mono_domain_alloc (domain, 2 * sizeof (gpointer));
374 switch (real_count) {
375 case 1:
376 res [0] = (gpointer)llvmonly_imt_tramp_1;
377 break;
378 case 2:
379 res [0] = (gpointer)llvmonly_imt_tramp_2;
380 break;
381 case 3:
382 res [0] = (gpointer)llvmonly_imt_tramp_3;
383 break;
384 default:
385 res [0] = (gpointer)llvmonly_imt_tramp;
386 break;
388 if (virtual_generic || fail_tramp)
389 res [0] = (gpointer)llvmonly_fallback_imt_tramp;
390 res [1] = buf;
392 return res;
395 gpointer
396 mini_llvmonly_get_vtable_trampoline (MonoVTable *vt, int slot_index, int index)
398 if (slot_index < 0) {
399 /* Initialize the IMT trampoline to a 'trampoline' so the generated code doesn't have to initialize it */
400 // FIXME: Memory management
401 gpointer *ftndesc = g_malloc (2 * sizeof (gpointer));
402 IMTTrampInfo *info = g_new0 (IMTTrampInfo, 1);
403 info->vtable = vt;
404 info->slot = index;
405 ftndesc [0] = (gpointer)mini_llvmonly_initial_imt_tramp;
406 ftndesc [1] = info;
407 mono_memory_barrier ();
408 return ftndesc;
409 } else {
410 return NULL;
414 static gboolean
415 is_generic_method_definition (MonoMethod *m)
417 MonoGenericContext *context;
418 if (m->is_generic)
419 return TRUE;
420 if (!m->is_inflated)
421 return FALSE;
423 context = mono_method_get_context (m);
424 if (!context->method_inst)
425 return FALSE;
426 if (context->method_inst == mono_method_get_generic_container (((MonoMethodInflated*)m)->declaring)->context.method_inst)
427 return TRUE;
428 return FALSE;
432 * resolve_vcall:
434 * Return the executable code for calling vt->vtable [slot].
435 * This function is called on a slowpath, so it doesn't need to be fast.
436 * This returns an ftnptr by returning the address part, and the arg in the OUT_ARG
437 * out parameter.
439 static gpointer
440 resolve_vcall (MonoVTable *vt, int slot, MonoMethod *imt_method, gpointer *out_arg, gboolean gsharedvt, MonoError *error)
442 MonoMethod *m, *generic_virtual = NULL;
443 gpointer addr, compiled_method;
444 gboolean need_unbox_tramp = FALSE;
446 error_init (error);
447 /* Same as in common_call_trampoline () */
449 /* Avoid loading metadata or creating a generic vtable if possible */
450 addr = mono_aot_get_method_from_vt_slot (mono_domain_get (), vt, slot, error);
451 return_val_if_nok (error, NULL);
452 if (addr && !m_class_is_valuetype (vt->klass))
453 return mono_create_ftnptr (mono_domain_get (), addr);
455 m = mono_class_get_vtable_entry (vt->klass, slot);
457 if (is_generic_method_definition (m)) {
458 MonoGenericContext context = { NULL, NULL };
459 MonoMethod *declaring;
461 if (m->is_inflated)
462 declaring = mono_method_get_declaring_generic_method (m);
463 else
464 declaring = m;
466 if (mono_class_is_ginst (m->klass))
467 context.class_inst = mono_class_get_generic_class (m->klass)->context.class_inst;
468 else
469 g_assert (!mono_class_is_gtd (m->klass));
471 generic_virtual = imt_method;
472 g_assert (generic_virtual);
473 g_assert (generic_virtual->is_inflated);
474 context.method_inst = ((MonoMethodInflated*)generic_virtual)->context.method_inst;
476 m = mono_class_inflate_generic_method_checked (declaring, &context, error);
477 mono_error_assert_ok (error); /* FIXME don't swallow the error */
480 if (generic_virtual) {
481 if (m_class_is_valuetype (vt->klass))
482 need_unbox_tramp = TRUE;
483 } else {
484 if (m_class_is_valuetype (m->klass))
485 need_unbox_tramp = TRUE;
488 if (m->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
489 m = mono_marshal_get_synchronized_wrapper (m);
491 addr = compiled_method = mini_llvmonly_load_method (m, gsharedvt, need_unbox_tramp, out_arg, error);
492 mono_error_assert_ok (error);
494 if (!gsharedvt && generic_virtual) {
495 // FIXME: This wastes memory since add_generic_virtual_invocation ignores it in a lot of cases
496 MonoFtnDesc *ftndesc = mini_llvmonly_create_ftndesc (mono_domain_get (), addr, out_arg);
498 mono_method_add_generic_virtual_invocation (mono_domain_get (),
499 vt, vt->vtable + slot,
500 generic_virtual, ftndesc);
503 return addr;
506 gpointer
507 mini_llvmonly_resolve_vcall_gsharedvt (MonoObject *this_obj, int slot, MonoMethod *imt_method, gpointer *out_arg)
509 g_assert (this_obj);
511 ERROR_DECL (error);
512 gpointer result = resolve_vcall (this_obj->vtable, slot, imt_method, out_arg, TRUE, error);
513 if (!is_ok (error)) {
514 MonoException *ex = mono_error_convert_to_exception (error);
515 mono_llvm_throw_exception ((MonoObject*)ex);
517 return result;
521 * mini_llvmonly_resolve_generic_virtual_call:
523 * Resolve a generic virtual call.
524 * This function is called on a slowpath, so it doesn't need to be fast.
526 MonoFtnDesc*
527 mini_llvmonly_resolve_generic_virtual_call (MonoVTable *vt, int slot, MonoMethod *generic_virtual)
529 MonoMethod *m;
530 gboolean need_unbox_tramp = FALSE;
531 ERROR_DECL (error);
532 MonoGenericContext context = { NULL, NULL };
533 MonoMethod *declaring;
535 m = mono_class_get_vtable_entry (vt->klass, slot);
537 g_assert (is_generic_method_definition (m));
539 if (m->is_inflated)
540 declaring = mono_method_get_declaring_generic_method (m);
541 else
542 declaring = m;
544 if (mono_class_is_ginst (m->klass))
545 context.class_inst = mono_class_get_generic_class (m->klass)->context.class_inst;
546 else
547 g_assert (!mono_class_is_gtd (m->klass));
549 g_assert (generic_virtual->is_inflated);
550 context.method_inst = ((MonoMethodInflated*)generic_virtual)->context.method_inst;
552 m = mono_class_inflate_generic_method_checked (declaring, &context, error);
553 g_assert (is_ok (error));
555 if (m_class_is_valuetype (vt->klass))
556 need_unbox_tramp = TRUE;
559 * This wastes memory but the memory usage is bounded since
560 * mono_method_add_generic_virtual_invocation () eventually builds an imt trampoline for
561 * this vtable slot so we are not called any more for this instantiation.
563 MonoFtnDesc *ftndesc = mini_llvmonly_load_method_ftndesc (m, FALSE, need_unbox_tramp, error);
564 mono_error_assert_ok (error);
566 mono_method_add_generic_virtual_invocation (mono_domain_get (),
567 vt, vt->vtable + slot,
568 generic_virtual, ftndesc);
569 return ftndesc;
573 * mini_llvmonly_resolve_generic_virtual_iface_call:
575 * Resolve a generic virtual/variant iface call on interfaces.
576 * This function is called on a slowpath, so it doesn't need to be fast.
578 MonoFtnDesc*
579 mini_llvmonly_resolve_generic_virtual_iface_call (MonoVTable *vt, int imt_slot, MonoMethod *generic_virtual)
581 ERROR_DECL (error);
582 MonoMethod *m, *variant_iface;
583 MonoFtnDesc *ftndesc;
584 gpointer aot_addr;
585 gboolean need_unbox_tramp = FALSE;
586 gboolean need_rgctx_tramp;
587 gpointer *imt;
589 imt = (gpointer*)vt - MONO_IMT_SIZE;
591 mini_resolve_imt_method (vt, imt + imt_slot, generic_virtual, &m, &aot_addr, &need_rgctx_tramp, &variant_iface, error);
592 if (!is_ok (error)) {
593 MonoException *ex = mono_error_convert_to_exception (error);
594 mono_llvm_throw_exception ((MonoObject*)ex);
597 if (m_class_is_valuetype (vt->klass))
598 need_unbox_tramp = TRUE;
600 if (m->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
601 m = mono_marshal_get_synchronized_wrapper (m);
604 * This wastes memory but the memory usage is bounded since
605 * mono_method_add_generic_virtual_invocation () eventually builds an imt trampoline for
606 * this vtable slot so we are not called any more for this instantiation.
608 ftndesc = mini_llvmonly_load_method_ftndesc (m, FALSE, need_unbox_tramp, error);
610 mono_method_add_generic_virtual_invocation (mono_domain_get (),
611 vt, imt + imt_slot,
612 variant_iface ? variant_iface : generic_virtual, ftndesc);
613 return ftndesc;
617 * mini_llvmonly_init_vtable_slot:
619 * Initialize slot SLOT of VTABLE.
620 * Return the contents of the vtable slot.
622 gpointer
623 mini_llvmonly_init_vtable_slot (MonoVTable *vtable, int slot)
625 ERROR_DECL (error);
626 gpointer arg = NULL;
627 gpointer addr;
628 gpointer *ftnptr;
630 addr = resolve_vcall (vtable, slot, NULL, &arg, FALSE, error);
631 if (mono_error_set_pending_exception (error))
632 return NULL;
633 ftnptr = mono_domain_alloc0 (vtable->domain, 2 * sizeof (gpointer));
634 ftnptr [0] = addr;
635 ftnptr [1] = arg;
636 mono_memory_barrier ();
638 vtable->vtable [slot] = ftnptr;
640 return ftnptr;
644 * mini_llvmonly_init_delegate:
646 * Initialize a MonoDelegate object.
647 * Similar to mono_delegate_ctor ().
649 void
650 mini_llvmonly_init_delegate (MonoDelegate *del)
652 ERROR_DECL (error);
653 MonoFtnDesc *ftndesc = *(MonoFtnDesc**)del->method_code;
656 * We store a MonoFtnDesc in del->method_code.
657 * It would be better to store an ftndesc in del->method_ptr too,
658 * but we don't have a a structure which could own its memory.
660 if (G_UNLIKELY (!ftndesc)) {
661 MonoMethod *m = del->method;
662 gboolean need_unbox = FALSE;
664 if (m->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
665 m = mono_marshal_get_synchronized_wrapper (m);
667 if (m_class_is_valuetype (m->klass) && mono_method_signature_internal (m)->hasthis)
668 need_unbox = TRUE;
670 gpointer arg = NULL;
671 gpointer addr = mini_llvmonly_load_method_delegate (m, FALSE, need_unbox, &arg, error);
672 if (mono_error_set_pending_exception (error))
673 return;
674 ftndesc = mini_llvmonly_create_ftndesc (mono_domain_get (), addr, arg);
675 mono_memory_barrier ();
676 *del->method_code = (guint8*)ftndesc;
678 del->method_ptr = ftndesc->addr;
679 del->extra_arg = ftndesc->arg;
682 void
683 mini_llvmonly_init_delegate_virtual (MonoDelegate *del, MonoObject *target, MonoMethod *method)
685 ERROR_DECL (error);
686 gpointer addr, arg;
687 gboolean need_unbox;
689 g_assert (target);
691 method = mono_object_get_virtual_method_internal (target, method);
693 if (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
694 method = mono_marshal_get_synchronized_wrapper (method);
695 need_unbox = m_class_is_valuetype (method->klass);
697 del->method = method;
698 addr = mini_llvmonly_load_method_delegate (method, FALSE, need_unbox, &arg, error);
699 if (mono_error_set_pending_exception (error))
700 return;
701 del->method_ptr = addr;
702 del->extra_arg = arg;
706 * resolve_iface_call:
708 * Return the executable code for the iface method IMT_METHOD called on THIS.
709 * This function is called on a slowpath, so it doesn't need to be fast.
710 * This returns an ftnptr by returning the address part, and the arg in the OUT_ARG
711 * out parameter.
713 static gpointer
714 resolve_iface_call (MonoObject *this_obj, int imt_slot, MonoMethod *imt_method, gpointer *out_arg, gboolean caller_gsharedvt, MonoError *error)
716 MonoVTable *vt;
717 gpointer *imt;
718 MonoMethod *impl_method, *generic_virtual = NULL, *variant_iface = NULL;
719 gpointer addr, aot_addr;
720 gboolean need_rgctx_tramp = FALSE, need_unbox_tramp = FALSE;
722 error_init (error);
723 if (!this_obj)
724 /* The caller will handle it */
725 return NULL;
727 vt = this_obj->vtable;
728 imt = (gpointer*)vt - MONO_IMT_SIZE;
730 mini_resolve_imt_method (vt, imt + imt_slot, imt_method, &impl_method, &aot_addr, &need_rgctx_tramp, &variant_iface, error);
731 return_val_if_nok (error, NULL);
733 if (imt_method->is_inflated && ((MonoMethodInflated*)imt_method)->context.method_inst)
734 generic_virtual = imt_method;
736 if (generic_virtual || variant_iface) {
737 if (m_class_is_valuetype (vt->klass)) /*FIXME is this required variant iface?*/
738 need_unbox_tramp = TRUE;
739 } else {
740 if (m_class_is_valuetype (impl_method->klass))
741 need_unbox_tramp = TRUE;
744 addr = mini_llvmonly_load_method (impl_method, caller_gsharedvt, need_unbox_tramp, out_arg, error);
745 mono_error_assert_ok (error);
746 g_assert (addr);
748 if (generic_virtual || variant_iface) {
749 MonoMethod *target = generic_virtual ? generic_virtual : variant_iface;
751 mono_method_add_generic_virtual_invocation (mono_domain_get (),
752 vt, imt + imt_slot,
753 target, addr);
756 return addr;
759 gpointer
760 mini_llvmonly_resolve_iface_call_gsharedvt (MonoObject *this_obj, int imt_slot, MonoMethod *imt_method, gpointer *out_arg)
762 ERROR_DECL (error);
763 gpointer res = resolve_iface_call (this_obj, imt_slot, imt_method, out_arg, TRUE, error);
764 if (!is_ok (error)) {
765 MonoException *ex = mono_error_convert_to_exception (error);
766 mono_llvm_throw_exception ((MonoObject*)ex);
768 return res;
771 static void
772 init_llvmonly_method (MonoAotModule *amodule, guint32 method_index, MonoClass *init_class)
774 ERROR_DECL (error);
775 gboolean res;
777 res = mono_aot_init_llvmonly_method (amodule, method_index, init_class, error);
778 if (!res || !is_ok (error)) {
779 MonoException *ex = mono_error_convert_to_exception (error);
780 if (ex) {
781 /* Its okay to raise in llvmonly mode */
782 if (mono_llvm_only) {
783 mono_llvm_throw_exception ((MonoObject*)ex);
784 } else {
785 mono_set_pending_exception (ex);
791 /* Called from generated code to initialize a method */
792 void
793 mini_llvm_init_method (MonoAotFileInfo *info, gpointer aot_module, guint32 method_index)
795 MonoAotModule *amodule = (MonoAotModule *)aot_module;
797 init_llvmonly_method (amodule, method_index, NULL);
800 /* Same for gshared methods with a this pointer */
801 void
802 mini_llvm_init_gshared_method_this (MonoAotFileInfo *info, gpointer aot_module, guint32 method_index, MonoObject *this_obj)
804 MonoAotModule *amodule = (MonoAotModule *)aot_module;
805 MonoClass *klass;
807 // FIXME:
808 g_assert (this_obj);
809 klass = this_obj->vtable->klass;
811 init_llvmonly_method (amodule, method_index, klass);
814 /* Same for gshared methods with an mrgctx arg */
815 void
816 mini_llvm_init_gshared_method_mrgctx (MonoAotFileInfo *info, gpointer aot_module, guint32 method_index, MonoMethodRuntimeGenericContext *rgctx)
818 MonoAotModule *amodule = (MonoAotModule *)aot_module;
820 init_llvmonly_method (amodule, method_index, rgctx->class_vtable->klass);
823 /* Same for gshared methods with a vtable arg */
824 void
825 mini_llvm_init_gshared_method_vtable (MonoAotFileInfo *info, gpointer aot_module, guint32 method_index, MonoVTable *vtable)
827 MonoAotModule *amodule = (MonoAotModule *)aot_module;
828 MonoClass *klass;
830 klass = vtable->klass;
832 init_llvmonly_method (amodule, method_index, klass);
835 static GENERATE_GET_CLASS_WITH_CACHE (nullref, "System", "NullReferenceException")
837 void
838 mini_llvmonly_throw_nullref_exception (void)
840 MonoClass *klass = mono_class_get_nullref_class ();
842 guint32 ex_token_index = m_class_get_type_token (klass) - MONO_TOKEN_TYPE_DEF;
844 mono_llvm_throw_corlib_exception (ex_token_index);