2010-01-07 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / mini / mini-trampolines.c
blob8ec71e706c588c10a4d7fabde4a55a94e7f4cf77
2 #include <config.h>
3 #include <glib.h>
5 #include <mono/metadata/appdomain.h>
6 #include <mono/metadata/metadata-internals.h>
7 #include <mono/metadata/marshal.h>
8 #include <mono/metadata/tabledefs.h>
9 #include <mono/utils/mono-counters.h>
11 #include "mini.h"
12 #include "debug-mini.h"
15 * Address of the trampoline code. This is used by the debugger to check
16 * whether a method is a trampoline.
18 guint8* mono_trampoline_code [MONO_TRAMPOLINE_NUM];
20 static GHashTable *class_init_hash_addr = NULL;
21 static GHashTable *rgctx_lazy_fetch_trampoline_hash = NULL;
22 static GHashTable *rgctx_lazy_fetch_trampoline_hash_addr = NULL;
24 #define mono_trampolines_lock() EnterCriticalSection (&trampolines_mutex)
25 #define mono_trampolines_unlock() LeaveCriticalSection (&trampolines_mutex)
26 static CRITICAL_SECTION trampolines_mutex;
28 static gpointer
29 get_unbox_trampoline (MonoGenericSharingContext *gsctx, MonoMethod *m, gpointer addr, gboolean need_rgctx_tramp)
31 if (mono_aot_only) {
32 if (need_rgctx_tramp)
33 /*
34 * The unbox trampolines call the method directly, so need to add
35 * an rgctx tramp before them.
37 return mono_create_static_rgctx_trampoline (m, mono_aot_get_unbox_trampoline (m));
38 else
39 return mono_aot_get_unbox_trampoline (m);
40 } else {
41 return mono_arch_get_unbox_trampoline (gsctx, m, addr);
45 #ifdef MONO_ARCH_HAVE_STATIC_RGCTX_TRAMPOLINE
47 typedef struct {
48 MonoMethod *m;
49 gpointer addr;
50 } RgctxTrampInfo;
52 static gint
53 rgctx_tramp_info_equal (gconstpointer ka, gconstpointer kb)
55 const RgctxTrampInfo *i1 = ka;
56 const RgctxTrampInfo *i2 = kb;
58 if (i1->m == i2->m && i1->addr == i2->addr)
59 return 1;
60 else
61 return 0;
64 static guint
65 rgctx_tramp_info_hash (gconstpointer data)
67 const RgctxTrampInfo *info = data;
69 return GPOINTER_TO_UINT (info->m) ^ GPOINTER_TO_UINT (info->addr);
73 * mono_create_static_rgctx_trampoline:
75 * Return a static rgctx trampoline for M which branches to ADDR which should
76 * point to the compiled code of M.
78 * Static rgctx trampolines are used when a shared generic method which doesn't
79 * have a this argument is called indirectly, ie. from code which can't pass in
80 * the rgctx argument. The trampoline sets the rgctx argument and jumps to the
81 * methods code. These trampolines are similar to the unbox trampolines, they
82 * perform the same task as the static rgctx wrappers, but they are smaller/faster,
83 * and can be made to work with full AOT.
85 gpointer
86 mono_create_static_rgctx_trampoline (MonoMethod *m, gpointer addr)
88 gpointer ctx;
89 gpointer res;
90 MonoDomain *domain;
91 RgctxTrampInfo tmp_info;
92 RgctxTrampInfo *info;
94 if (mini_method_get_context (m)->method_inst)
95 ctx = mono_method_lookup_rgctx (mono_class_vtable (mono_domain_get (), m->klass), mini_method_get_context (m)->method_inst);
96 else
97 ctx = mono_class_vtable (mono_domain_get (), m->klass);
99 domain = mono_domain_get ();
102 * In the AOT case, addr might point to either the method, or to an unbox trampoline,
103 * so make the hash keyed on the m+addr pair.
105 mono_domain_lock (domain);
106 if (!domain_jit_info (domain)->static_rgctx_trampoline_hash)
107 domain_jit_info (domain)->static_rgctx_trampoline_hash = g_hash_table_new (rgctx_tramp_info_hash, rgctx_tramp_info_equal);
108 tmp_info.m = m;
109 tmp_info.addr = addr;
110 res = g_hash_table_lookup (domain_jit_info (domain)->static_rgctx_trampoline_hash,
111 &tmp_info);
112 mono_domain_unlock (domain);
113 if (res)
114 return res;
116 if (mono_aot_only)
117 res = mono_aot_get_static_rgctx_trampoline (ctx, addr);
118 else
119 res = mono_arch_get_static_rgctx_trampoline (m, ctx, addr);
121 mono_domain_lock (domain);
122 /* Duplicates inserted while we didn't hold the lock are OK */
123 info = mono_domain_alloc (domain, sizeof (RgctxTrampInfo));
124 info->m = m;
125 info->addr = addr;
126 g_hash_table_insert (domain_jit_info (domain)->static_rgctx_trampoline_hash, info, res);
127 mono_domain_unlock (domain);
129 return res;
131 #else
132 gpointer
133 mono_create_static_rgctx_trampoline (MonoMethod *m, gpointer addr)
136 * This shouldn't happen as all arches which support generic sharing support
137 * static rgctx trampolines as well.
139 g_assert_not_reached ();
141 #endif
143 gpointer*
144 mono_get_vcall_slot_addr (guint8* code, mgreg_t *regs)
146 gpointer vt;
147 int displacement;
148 vt = mono_arch_get_vcall_slot (code, regs, &displacement);
149 if (!vt)
150 return NULL;
151 return (gpointer*)((char*)vt + displacement);
154 #ifdef MONO_ARCH_HAVE_IMT
156 static gpointer*
157 mono_convert_imt_slot_to_vtable_slot (gpointer* slot, mgreg_t *regs, guint8 *code, MonoMethod *method, MonoMethod **impl_method, gboolean *need_rgctx_tramp)
159 MonoObject *this_argument = mono_arch_get_this_arg_from_call (NULL, mono_method_signature (method), regs, code);
160 MonoVTable *vt = this_argument->vtable;
161 int displacement = slot - ((gpointer*)vt);
163 if (displacement > 0) {
164 /* slot is in the vtable, not in the IMT */
165 #if DEBUG_IMT
166 printf ("mono_convert_imt_slot_to_vtable_slot: slot %p is in the vtable, not in the IMT\n", slot);
167 #endif
168 return slot;
169 } else {
170 MonoMethod *imt_method = mono_arch_find_imt_method (regs, code);
171 int interface_offset;
172 int imt_slot = MONO_IMT_SIZE + displacement;
174 /*This has to be variance aware since imt_method can be from an interface that vt->klass doesn't directly implement*/
175 interface_offset = mono_class_interface_offset_with_variance (vt->klass, imt_method->klass);
177 if (interface_offset < 0) {
178 g_print ("%s doesn't implement interface %s\n", mono_type_get_name_full (&vt->klass->byval_arg, 0), mono_type_get_name_full (&imt_method->klass->byval_arg, 0));
179 g_assert_not_reached ();
181 mono_vtable_build_imt_slot (vt, mono_method_get_imt_slot (imt_method));
183 if (impl_method) {
184 MonoMethod *impl;
186 if (imt_method->is_inflated && ((MonoMethodInflated*)imt_method)->context.method_inst) {
187 MonoGenericContext context = { NULL, NULL };
190 * Generic virtual method, imt_method contains the inflated interface
191 * method, need to get the inflated impl method.
193 /* imt_method->slot might not be set */
194 impl = mono_class_get_vtable_entry (vt->klass, interface_offset + mono_method_get_declaring_generic_method (imt_method)->slot);
196 if (impl->klass->generic_class)
197 context.class_inst = impl->klass->generic_class->context.class_inst;
198 context.method_inst = ((MonoMethodInflated*)imt_method)->context.method_inst;
199 impl = mono_class_inflate_generic_method (impl, &context);
200 } else {
201 impl = mono_class_get_vtable_entry (vt->klass, interface_offset + mono_method_get_vtable_slot (imt_method));
204 if (mono_method_needs_static_rgctx_invoke (impl, FALSE))
205 *need_rgctx_tramp = TRUE;
207 *impl_method = impl;
208 #if DEBUG_IMT
209 printf ("mono_convert_imt_slot_to_vtable_slot: method = %s.%s.%s, imt_method = %s.%s.%s\n",
210 method->klass->name_space, method->klass->name, method->name,
211 imt_method->klass->name_space, imt_method->klass->name, imt_method->name);
212 #endif
214 g_assert (imt_slot < MONO_IMT_SIZE);
215 if (vt->imt_collisions_bitmap & (1 << imt_slot)) {
216 int slot = mono_method_get_vtable_index (imt_method);
217 int vtable_offset;
218 gpointer *vtable_slot;
220 g_assert (slot != -1);
221 vtable_offset = interface_offset + slot;
222 vtable_slot = & (vt->vtable [vtable_offset]);
223 #if DEBUG_IMT
224 printf ("mono_convert_imt_slot_to_vtable_slot: slot %p[%d] is in the IMT, and colliding becomes %p[%d] (interface_offset = %d, method->slot = %d)\n", slot, imt_slot, vtable_slot, vtable_offset, interface_offset, imt_method->slot);
225 #endif
226 return vtable_slot;
227 } else {
228 #if DEBUG_IMT
229 printf ("mono_convert_imt_slot_to_vtable_slot: slot %p[%d] is in the IMT, but not colliding\n", slot, imt_slot);
230 #endif
231 return slot;
235 #endif
238 * common_call_trampoline:
240 * The code to handle normal, virtual, and interface method calls and jumps, both
241 * from JITted and LLVM compiled code.
243 static gpointer
244 common_call_trampoline (mgreg_t *regs, guint8 *code, gpointer arg, guint8* tramp, MonoVTable *vt, gpointer *vtable_slot, gboolean need_rgctx_tramp)
246 gpointer addr;
247 gboolean generic_shared = FALSE;
248 MonoMethod *m;
249 MonoMethod *declaring = NULL;
250 MonoMethod *generic_virtual = NULL;
251 int context_used;
252 gboolean proxy = FALSE;
253 gpointer *orig_vtable_slot;
254 MonoJitInfo *ji = NULL;
256 m = arg;
258 orig_vtable_slot = vtable_slot;
260 if (m == MONO_FAKE_VTABLE_METHOD) {
261 int displacement;
262 if (!vt) {
263 int i;
264 MonoJitInfo *ji;
266 ji = mono_jit_info_table_find (mono_domain_get (), (char*)code);
267 if (ji)
268 printf ("Caller: %s\n", mono_method_full_name (ji->method, TRUE));
269 /* Print some debug info */
270 for (i = 0; i < 32; ++i)
271 printf ("0x%x ", code [-32 + i]);
272 printf ("\n");
273 g_assert (vt);
275 displacement = (guint8*)vtable_slot - (guint8*)vt;
276 if (displacement > 0) {
277 int slot = (displacement - G_STRUCT_OFFSET (MonoVTable, vtable)) / sizeof (gpointer);
278 g_assert (slot >= 0);
280 /* Avoid loading metadata or creating a generic vtable if possible */
281 addr = mono_aot_get_method_from_vt_slot (mono_domain_get (), vt, slot);
282 if (addr)
283 addr = mono_create_ftnptr (mono_domain_get (), addr);
284 if (addr && !vt->klass->valuetype) {
285 vtable_slot = mono_get_vcall_slot_addr (code, regs);
286 if (mono_aot_is_got_entry (code, (guint8*)vtable_slot) || mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot)) {
287 *vtable_slot = mono_get_addr_from_ftnptr (addr);
290 return addr;
293 m = mono_class_get_vtable_entry (vt->klass, slot);
294 if (mono_method_needs_static_rgctx_invoke (m, FALSE))
295 need_rgctx_tramp = TRUE;
297 /*g_print ("%s with disp %d: %s at %p\n", vt->klass->name, displacement, m->name, code);*/
298 } else {
299 /* We got here from an interface method: redirect to IMT handling */
300 m = MONO_FAKE_IMT_METHOD;
301 /*g_print ("vtable with disp %d at %p\n", displacement, code);*/
305 /* this is the IMT trampoline */
306 #ifdef MONO_ARCH_HAVE_IMT
307 if (m == MONO_FAKE_IMT_METHOD) {
308 MonoMethod *impl_method;
309 MonoObject *this_arg;
311 /* we get the interface method because mono_convert_imt_slot_to_vtable_slot ()
312 * needs the signature to be able to find the this argument
314 m = mono_arch_find_imt_method (regs, code);
315 vtable_slot = orig_vtable_slot;
316 g_assert (vtable_slot);
318 this_arg = mono_arch_get_this_arg_from_call (NULL, mono_method_signature (m), regs, code);
320 if (this_arg->vtable->klass == mono_defaults.transparent_proxy_class) {
321 /* Use the slow path for now */
322 proxy = TRUE;
323 m = mono_object_get_virtual_method (this_arg, m);
324 } else {
325 vtable_slot = mono_convert_imt_slot_to_vtable_slot (vtable_slot, regs, code, m, &impl_method, &need_rgctx_tramp);
326 /* mono_convert_imt_slot_to_vtable_slot () also gives us the method that is supposed
327 * to be called, so we compile it and go ahead as usual.
329 /*g_print ("imt found method %p (%s) at %p\n", impl_method, impl_method->name, code);*/
330 if (m->is_inflated && ((MonoMethodInflated*)m)->context.method_inst) {
331 /* Generic virtual method */
332 generic_virtual = m;
333 m = impl_method;
334 need_rgctx_tramp = TRUE;
335 } else {
336 m = impl_method;
340 #endif
342 #ifdef MONO_ARCH_LLVM_SUPPORTED
343 if (!vtable_slot && code && !need_rgctx_tramp && mono_method_needs_static_rgctx_invoke (m, FALSE)) {
345 * Call this only if the called method is shared, cause it is slow/loads a lot of
346 * data in AOT.
348 ji = mono_jit_info_table_find (mono_domain_get (), (char*)code);
349 if (ji && ji->from_llvm) {
350 /* LLVM can't pass an rgctx arg */
351 need_rgctx_tramp = TRUE;
354 #endif
356 if (m->is_generic) {
357 MonoGenericContext context = { NULL, NULL };
358 MonoMethod *declaring;
360 if (m->is_inflated)
361 declaring = mono_method_get_declaring_generic_method (m);
362 else
363 declaring = m;
365 if (m->klass->generic_class)
366 context.class_inst = m->klass->generic_class->context.class_inst;
367 else
368 g_assert (!m->klass->generic_container);
370 #ifdef MONO_ARCH_HAVE_IMT
371 generic_virtual = mono_arch_find_imt_method (regs, code);
372 #endif
373 if (generic_virtual) {
374 g_assert (generic_virtual->is_inflated);
375 context.method_inst = ((MonoMethodInflated*)generic_virtual)->context.method_inst;
378 m = mono_class_inflate_generic_method (declaring, &context);
379 /* FIXME: only do this if the method is sharable */
380 need_rgctx_tramp = TRUE;
381 } else if ((context_used = mono_method_check_context_used (m))) {
382 MonoClass *klass = NULL;
383 MonoMethod *actual_method = NULL;
384 MonoVTable *vt = NULL;
385 MonoGenericInst *method_inst = NULL;
387 vtable_slot = NULL;
388 generic_shared = TRUE;
390 g_assert (code);
392 if (m->is_inflated && mono_method_get_context (m)->method_inst) {
393 #ifdef MONO_ARCH_RGCTX_REG
394 MonoMethodRuntimeGenericContext *mrgctx = (MonoMethodRuntimeGenericContext*)mono_arch_find_static_call_vtable (regs, code);
396 klass = mrgctx->class_vtable->klass;
397 method_inst = mrgctx->method_inst;
398 #else
399 g_assert_not_reached ();
400 #endif
401 } else if ((m->flags & METHOD_ATTRIBUTE_STATIC) || m->klass->valuetype) {
402 #ifdef MONO_ARCH_RGCTX_REG
403 MonoVTable *vtable = mono_arch_find_static_call_vtable (regs, code);
405 klass = vtable->klass;
406 #else
407 g_assert_not_reached ();
408 #endif
409 } else {
410 #ifdef MONO_ARCH_HAVE_IMT
411 MonoObject *this_argument = mono_arch_get_this_arg_from_call (NULL, mono_method_signature (m), regs, code);
413 vt = this_argument->vtable;
414 vtable_slot = orig_vtable_slot;
416 g_assert (this_argument->vtable->klass->inited);
417 //mono_class_init (this_argument->vtable->klass);
419 if (!vtable_slot)
420 klass = this_argument->vtable->klass->supertypes [m->klass->idepth - 1];
421 #else
422 NOT_IMPLEMENTED;
423 #endif
426 g_assert (vtable_slot || klass);
428 if (vtable_slot) {
429 int displacement = vtable_slot - ((gpointer*)vt);
431 g_assert_not_reached ();
433 g_assert (displacement > 0);
435 actual_method = vt->klass->vtable [displacement];
438 if (method_inst) {
439 MonoGenericContext context = { NULL, NULL };
441 if (m->is_inflated)
442 declaring = mono_method_get_declaring_generic_method (m);
443 else
444 declaring = m;
446 if (klass->generic_class)
447 context.class_inst = klass->generic_class->context.class_inst;
448 else if (klass->generic_container)
449 context.class_inst = klass->generic_container->context.class_inst;
450 context.method_inst = method_inst;
452 actual_method = mono_class_inflate_generic_method (declaring, &context);
453 } else {
454 actual_method = mono_class_get_method_generic (klass, m);
457 g_assert (klass);
458 g_assert (actual_method);
459 g_assert (actual_method->klass == klass);
461 if (actual_method->is_inflated)
462 declaring = mono_method_get_declaring_generic_method (actual_method);
463 else
464 declaring = NULL;
466 m = actual_method;
469 if (m->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) {
470 MonoJitInfo *ji;
472 if (code)
473 ji = mono_jit_info_table_find (mono_domain_get (), (char*)code);
474 else
475 ji = NULL;
477 /* Avoid recursion */
478 if (!(ji && ji->method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED))
479 m = mono_marshal_get_synchronized_wrapper (m);
482 /* Calls made through delegates on platforms without delegate trampolines */
483 if (!code && mono_method_needs_static_rgctx_invoke (m, FALSE))
484 need_rgctx_tramp = TRUE;
486 addr = mono_compile_method (m);
487 g_assert (addr);
489 mono_debugger_trampoline_compiled (code, m, addr);
491 if (need_rgctx_tramp)
492 addr = mono_create_static_rgctx_trampoline (m, addr);
494 if (generic_virtual) {
495 vtable_slot = orig_vtable_slot;
496 g_assert (vtable_slot);
498 if (vt->klass->valuetype)
499 addr = get_unbox_trampoline (mono_get_generic_context_from_code (code), m, addr, need_rgctx_tramp);
501 mono_method_add_generic_virtual_invocation (mono_domain_get (),
502 vt, vtable_slot,
503 generic_virtual, addr);
505 return addr;
508 /* the method was jumped to */
509 if (!code) {
510 MonoDomain *domain = mono_domain_get ();
512 /* Patch the got entries pointing to this method */
514 * We do this here instead of in mono_codegen () to cover the case when m
515 * was loaded from an aot image.
517 if (domain_jit_info (domain)->jump_target_got_slot_hash) {
518 GSList *list, *tmp;
520 mono_domain_lock (domain);
521 list = g_hash_table_lookup (domain_jit_info (domain)->jump_target_got_slot_hash, m);
522 if (list) {
523 for (tmp = list; tmp; tmp = tmp->next) {
524 gpointer *got_slot = tmp->data;
525 *got_slot = addr;
527 g_hash_table_remove (domain_jit_info (domain)->jump_target_got_slot_hash, m);
528 g_slist_free (list);
530 mono_domain_unlock (domain);
533 return addr;
536 vtable_slot = orig_vtable_slot;
538 if (vtable_slot) {
539 if (m->klass->valuetype)
540 addr = get_unbox_trampoline (mono_get_generic_context_from_code (code), m, addr, need_rgctx_tramp);
541 g_assert (*vtable_slot);
543 if (!proxy && (mono_aot_is_got_entry (code, (guint8*)vtable_slot) || mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot))) {
544 #ifdef MONO_ARCH_HAVE_IMT
545 vtable_slot = mono_convert_imt_slot_to_vtable_slot (vtable_slot, regs, code, m, NULL, &need_rgctx_tramp);
546 #endif
547 *vtable_slot = mono_get_addr_from_ftnptr (addr);
550 else {
551 guint8 *plt_entry = mono_aot_get_plt_entry (code);
553 if (plt_entry) {
554 mono_arch_patch_plt_entry (plt_entry, NULL, regs, addr);
555 } else if (!generic_shared || (m->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
556 mono_domain_lookup_shared_generic (mono_domain_get (), declaring)) {
557 if (generic_shared) {
558 if (m->wrapper_type != MONO_WRAPPER_NONE)
559 m = mono_marshal_method_from_wrapper (m);
560 g_assert (mono_method_is_generic_sharable_impl (m, FALSE));
563 /* Patch calling code */
564 if (plt_entry) {
566 } else {
567 MonoJitInfo *target_ji =
568 mono_jit_info_table_find (mono_domain_get (), mono_get_addr_from_ftnptr (addr));
570 if (!ji)
571 ji = mono_jit_info_table_find (mono_domain_get (), (char*)code);
573 if (mono_method_same_domain (ji, target_ji))
574 mono_arch_patch_callsite (ji->code_start, code, addr);
579 return addr;
583 * mono_magic_trampoline:
585 * This trampoline handles calls from JITted code.
587 gpointer
588 mono_magic_trampoline (mgreg_t *regs, guint8 *code, gpointer arg, guint8* tramp)
590 gpointer *vtable_slot;
591 int displacement;
592 MonoVTable *vt;
594 if (code && !mono_use_llvm)
595 vt = mono_arch_get_vcall_slot (code, regs, &displacement);
596 else
597 vt = NULL;
598 if (vt)
599 vtable_slot = (gpointer*)((char*)vt + displacement);
600 else
601 vtable_slot = NULL;
603 return common_call_trampoline (regs, code, arg, tramp, vt, vtable_slot, FALSE);
606 #ifdef MONO_ARCH_LLVM_SUPPORTED
608 * mono_llvm_vcall_trampoline:
610 * This trampoline handles virtual calls when using LLVM.
612 static gpointer
613 mono_llvm_vcall_trampoline (mgreg_t *regs, guint8 *code, MonoMethod *m, guint8 *tramp)
615 MonoObject *this;
616 MonoVTable *vt;
617 gpointer *vtable_slot;
618 int slot;
621 * We have the method which is called, we need to obtain the vtable slot without
622 * disassembly which is impossible with LLVM.
623 * So we use the this argument.
625 this = mono_arch_get_this_arg_from_call (NULL, mono_method_signature (m), regs, code);
626 g_assert (this);
628 slot = mono_method_get_vtable_slot (m);
630 g_assert (slot != -1);
632 g_assert (this->vtable->klass->vtable [slot] == m);
634 vt = this->vtable;
636 g_assert (!m->is_generic);
638 vtable_slot = &(vt->vtable [slot]);
640 return common_call_trampoline (regs, code, m, tramp, vt, vtable_slot, mono_method_needs_static_rgctx_invoke (m, 0));
642 #endif
644 gpointer
645 mono_generic_virtual_remoting_trampoline (mgreg_t *regs, guint8 *code, MonoMethod *m, guint8 *tramp)
647 MonoGenericContext context = { NULL, NULL };
648 MonoMethod *imt_method, *declaring;
649 gpointer addr;
651 g_assert (m->is_generic);
653 if (m->is_inflated)
654 declaring = mono_method_get_declaring_generic_method (m);
655 else
656 declaring = m;
658 if (m->klass->generic_class)
659 context.class_inst = m->klass->generic_class->context.class_inst;
660 else
661 g_assert (!m->klass->generic_container);
663 #ifdef MONO_ARCH_HAVE_IMT
664 imt_method = mono_arch_find_imt_method (regs, code);
665 if (imt_method->is_inflated)
666 context.method_inst = ((MonoMethodInflated*)imt_method)->context.method_inst;
667 #endif
668 m = mono_class_inflate_generic_method (declaring, &context);
669 m = mono_marshal_get_remoting_invoke_with_check (m);
671 addr = mono_compile_method (m);
672 g_assert (addr);
674 mono_debugger_trampoline_compiled (NULL, m, addr);
676 return addr;
680 * mono_aot_trampoline:
682 * This trampoline handles calls made from AOT code. We try to bypass the
683 * normal JIT compilation logic to avoid loading the metadata for the method.
685 #ifdef MONO_ARCH_AOT_SUPPORTED
686 gpointer
687 mono_aot_trampoline (mgreg_t *regs, guint8 *code, guint8 *token_info,
688 guint8* tramp)
690 MonoImage *image;
691 guint32 token;
692 MonoMethod *method = NULL;
693 gpointer addr;
694 gpointer *vtable_slot;
695 gboolean is_got_entry;
696 guint8 *plt_entry;
697 gboolean need_rgctx_tramp = FALSE;
699 image = *(gpointer*)(gpointer)token_info;
700 token_info += sizeof (gpointer);
701 token = *(guint32*)(gpointer)token_info;
703 addr = mono_aot_get_method_from_token (mono_domain_get (), image, token);
704 if (!addr) {
705 method = mono_get_method (image, token, NULL);
706 g_assert (method);
708 /* Use the generic code */
709 return mono_magic_trampoline (regs, code, method, tramp);
712 addr = mono_create_ftnptr (mono_domain_get (), addr);
714 vtable_slot = mono_get_vcall_slot_addr (code, regs);
715 g_assert (!vtable_slot);
717 /* This is a normal call through a PLT entry */
718 plt_entry = mono_aot_get_plt_entry (code);
719 g_assert (plt_entry);
721 mono_arch_patch_plt_entry (plt_entry, NULL, regs, addr);
723 is_got_entry = FALSE;
726 * Since AOT code is only used in the root domain,
727 * mono_domain_get () != mono_get_root_domain () means the calling method
728 * is AppDomain:InvokeInDomain, so this is the same check as in
729 * mono_method_same_domain () but without loading the metadata for the method.
731 if ((is_got_entry && (mono_domain_get () == mono_get_root_domain ())) || mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot)) {
732 #ifdef MONO_ARCH_HAVE_IMT
733 if (!method)
734 method = mono_get_method (image, token, NULL);
735 vtable_slot = mono_convert_imt_slot_to_vtable_slot (vtable_slot, regs, code, method, NULL, &need_rgctx_tramp);
736 #endif
737 *vtable_slot = addr;
740 return addr;
744 * mono_aot_plt_trampoline:
746 * This trampoline handles calls made from AOT code through the PLT table.
748 gpointer
749 mono_aot_plt_trampoline (mgreg_t *regs, guint8 *code, guint8 *aot_module,
750 guint8* tramp)
752 guint32 plt_info_offset = mono_aot_get_plt_info_offset (regs, code);
754 return mono_aot_plt_resolve (aot_module, plt_info_offset, code);
756 #endif
759 * mono_class_init_trampoline:
761 * This method calls mono_runtime_class_init () to run the static constructor
762 * for the type, then patches the caller code so it is not called again.
764 void
765 mono_class_init_trampoline (mgreg_t *regs, guint8 *code, MonoVTable *vtable, guint8 *tramp)
767 guint8 *plt_entry = mono_aot_get_plt_entry (code);
769 mono_runtime_class_init (vtable);
771 if (plt_entry) {
772 mono_arch_nullify_plt_entry (plt_entry, regs);
773 } else {
774 mono_arch_nullify_class_init_trampoline (code, regs);
779 * mono_generic_class_init_trampoline:
781 * This method calls mono_runtime_class_init () to run the static constructor
782 * for the type.
784 void
785 mono_generic_class_init_trampoline (mgreg_t *regs, guint8 *code, MonoVTable *vtable, guint8 *tramp)
787 mono_runtime_class_init (vtable);
790 static gpointer
791 mono_rgctx_lazy_fetch_trampoline (mgreg_t *regs, guint8 *code, gpointer data, guint8 *tramp)
793 #ifdef MONO_ARCH_VTABLE_REG
794 static gboolean inited = FALSE;
795 static int num_lookups = 0;
796 guint32 slot = GPOINTER_TO_UINT (data);
797 mgreg_t *r = (mgreg_t*)regs;
798 gpointer arg = (gpointer)(gssize)r [MONO_ARCH_VTABLE_REG];
799 guint32 index = MONO_RGCTX_SLOT_INDEX (slot);
800 gboolean mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
802 if (!inited) {
803 mono_counters_register ("RGCTX unmanaged lookups", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_lookups);
804 inited = TRUE;
807 num_lookups++;
809 if (mrgctx)
810 return mono_method_fill_runtime_generic_context (arg, index);
811 else
812 return mono_class_fill_runtime_generic_context (arg, index);
813 #else
814 g_assert_not_reached ();
815 #endif
818 void
819 mono_monitor_enter_trampoline (mgreg_t *regs, guint8 *code, MonoObject *obj, guint8 *tramp)
821 mono_monitor_enter (obj);
824 void
825 mono_monitor_exit_trampoline (mgreg_t *regs, guint8 *code, MonoObject *obj, guint8 *tramp)
827 mono_monitor_exit (obj);
830 #ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
833 * mono_delegate_trampoline:
835 * This trampoline handles calls made to Delegate:Invoke ().
836 * This is called once the first time a delegate is invoked, so it must be fast.
838 gpointer
839 mono_delegate_trampoline (mgreg_t *regs, guint8 *code, gpointer *tramp_data, guint8* tramp)
841 MonoDomain *domain = mono_domain_get ();
842 MonoDelegate *delegate;
843 MonoJitInfo *ji;
844 MonoMethod *m;
845 MonoMethod *method = NULL;
846 gboolean multicast, callvirt;
847 gboolean need_rgctx_tramp = FALSE;
848 MonoMethod *invoke = tramp_data [0];
849 guint8 *impl_this = tramp_data [1];
850 guint8 *impl_nothis = tramp_data [2];
852 /* Obtain the delegate object according to the calling convention */
855 * Avoid calling mono_get_generic_context_from_code () now since it is expensive,
856 * get_this_arg_from_call will call it if needed.
858 delegate = mono_arch_get_this_arg_from_call (NULL, mono_method_signature (invoke), regs, code);
860 if (delegate->method) {
861 method = delegate->method;
864 * delegate->method_ptr == NULL means the delegate was initialized by
865 * mini_delegate_ctor, while != NULL means it is initialized by
866 * mono_delegate_ctor_with_method (). In both cases, we need to add wrappers
867 * (ctor_with_method () does this, but it doesn't store the wrapper back into
868 * delegate->method).
870 if (delegate->target && delegate->target->vtable->klass == mono_defaults.transparent_proxy_class) {
871 #ifndef DISABLE_COM
872 if (((MonoTransparentProxy *)delegate->target)->remote_class->proxy_class != mono_defaults.com_object_class &&
873 !((MonoTransparentProxy *)delegate->target)->remote_class->proxy_class->is_com_object)
874 #endif
875 method = mono_marshal_get_remoting_invoke (method);
877 else if (mono_method_signature (method)->hasthis && method->klass->valuetype)
878 method = mono_marshal_get_unbox_wrapper (method);
879 } else {
880 ji = mono_jit_info_table_find (domain, mono_get_addr_from_ftnptr (delegate->method_ptr));
881 if (ji)
882 method = ji->method;
884 callvirt = !delegate->target && method && mono_method_signature (method)->hasthis;
886 if (method && method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
887 method = mono_marshal_get_synchronized_wrapper (method);
889 if (method && mono_method_needs_static_rgctx_invoke (method, FALSE))
890 need_rgctx_tramp = TRUE;
893 * If the called address is a trampoline, replace it with the compiled method so
894 * further calls don't have to go through the trampoline.
896 if (method && !callvirt) {
897 /* Avoid the overhead of looking up an already compiled method if possible */
898 if (delegate->method_code && *delegate->method_code) {
899 delegate->method_ptr = *delegate->method_code;
900 } else {
901 delegate->method_ptr = mono_compile_method (method);
902 if (need_rgctx_tramp)
903 delegate->method_ptr = mono_create_static_rgctx_trampoline (method, delegate->method_ptr);
904 if (delegate->method_code)
905 *delegate->method_code = delegate->method_ptr;
906 mono_debugger_trampoline_compiled (NULL, method, delegate->method_ptr);
908 } else {
909 if (need_rgctx_tramp)
910 delegate->method_ptr = mono_create_static_rgctx_trampoline (method, delegate->method_ptr);
913 multicast = ((MonoMulticastDelegate*)delegate)->prev != NULL;
914 if (!multicast && !callvirt) {
915 if (method && (method->flags & METHOD_ATTRIBUTE_STATIC) && mono_method_signature (method)->param_count == mono_method_signature (invoke)->param_count + 1)
916 /* Closed static delegate */
917 code = impl_this;
918 else
919 code = delegate->target ? impl_this : impl_nothis;
921 if (code) {
922 delegate->invoke_impl = mono_get_addr_from_ftnptr (code);
923 return code;
927 /* The general, unoptimized case */
928 m = mono_marshal_get_delegate_invoke (invoke, delegate);
929 code = mono_compile_method (m);
930 delegate->invoke_impl = mono_get_addr_from_ftnptr (code);
931 mono_debugger_trampoline_compiled (NULL, m, delegate->invoke_impl);
933 return code;
936 #endif
939 * mono_get_trampoline_func:
941 * Return the C function which needs to be called by the generic trampoline of type
942 * TRAMP_TYPE.
944 gconstpointer
945 mono_get_trampoline_func (MonoTrampolineType tramp_type)
947 switch (tramp_type) {
948 case MONO_TRAMPOLINE_JIT:
949 case MONO_TRAMPOLINE_JUMP:
950 return mono_magic_trampoline;
951 case MONO_TRAMPOLINE_CLASS_INIT:
952 return mono_class_init_trampoline;
953 case MONO_TRAMPOLINE_GENERIC_CLASS_INIT:
954 return mono_generic_class_init_trampoline;
955 case MONO_TRAMPOLINE_RGCTX_LAZY_FETCH:
956 return mono_rgctx_lazy_fetch_trampoline;
957 #ifdef MONO_ARCH_AOT_SUPPORTED
958 case MONO_TRAMPOLINE_AOT:
959 return mono_aot_trampoline;
960 case MONO_TRAMPOLINE_AOT_PLT:
961 return mono_aot_plt_trampoline;
962 #endif
963 #ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
964 case MONO_TRAMPOLINE_DELEGATE:
965 return mono_delegate_trampoline;
966 #endif
967 case MONO_TRAMPOLINE_RESTORE_STACK_PROT:
968 return mono_altstack_restore_prot;
969 case MONO_TRAMPOLINE_GENERIC_VIRTUAL_REMOTING:
970 return mono_generic_virtual_remoting_trampoline;
971 case MONO_TRAMPOLINE_MONITOR_ENTER:
972 return mono_monitor_enter_trampoline;
973 case MONO_TRAMPOLINE_MONITOR_EXIT:
974 return mono_monitor_exit_trampoline;
975 #ifdef MONO_ARCH_LLVM_SUPPORTED
976 case MONO_TRAMPOLINE_LLVM_VCALL:
977 return mono_llvm_vcall_trampoline;
978 #endif
979 default:
980 g_assert_not_reached ();
981 return NULL;
985 void
986 mono_trampolines_init (void)
988 InitializeCriticalSection (&trampolines_mutex);
990 if (mono_aot_only)
991 return;
993 mono_trampoline_code [MONO_TRAMPOLINE_JIT] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_JIT);
994 mono_trampoline_code [MONO_TRAMPOLINE_JUMP] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_JUMP);
995 mono_trampoline_code [MONO_TRAMPOLINE_CLASS_INIT] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_CLASS_INIT);
996 mono_trampoline_code [MONO_TRAMPOLINE_GENERIC_CLASS_INIT] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_GENERIC_CLASS_INIT);
997 mono_trampoline_code [MONO_TRAMPOLINE_RGCTX_LAZY_FETCH] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_RGCTX_LAZY_FETCH);
998 #ifdef MONO_ARCH_AOT_SUPPORTED
999 mono_trampoline_code [MONO_TRAMPOLINE_AOT] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_AOT);
1000 mono_trampoline_code [MONO_TRAMPOLINE_AOT_PLT] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_AOT_PLT);
1001 #endif
1002 #ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
1003 mono_trampoline_code [MONO_TRAMPOLINE_DELEGATE] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_DELEGATE);
1004 #endif
1005 mono_trampoline_code [MONO_TRAMPOLINE_RESTORE_STACK_PROT] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_RESTORE_STACK_PROT);
1006 mono_trampoline_code [MONO_TRAMPOLINE_GENERIC_VIRTUAL_REMOTING] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_GENERIC_VIRTUAL_REMOTING);
1007 mono_trampoline_code [MONO_TRAMPOLINE_MONITOR_ENTER] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_MONITOR_ENTER);
1008 mono_trampoline_code [MONO_TRAMPOLINE_MONITOR_EXIT] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_MONITOR_EXIT);
1009 #ifdef MONO_ARCH_LLVM_SUPPORTED
1010 mono_trampoline_code [MONO_TRAMPOLINE_LLVM_VCALL] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_LLVM_VCALL);
1011 #endif
1014 void
1015 mono_trampolines_cleanup (void)
1017 if (class_init_hash_addr)
1018 g_hash_table_destroy (class_init_hash_addr);
1020 DeleteCriticalSection (&trampolines_mutex);
1023 guint8 *
1024 mono_get_trampoline_code (MonoTrampolineType tramp_type)
1026 g_assert (mono_trampoline_code [tramp_type]);
1028 return mono_trampoline_code [tramp_type];
1031 gpointer
1032 mono_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
1034 if (mono_aot_only)
1035 return mono_aot_create_specific_trampoline (mono_defaults.corlib, arg1, tramp_type, domain, code_len);
1036 else
1037 return mono_arch_create_specific_trampoline (arg1, tramp_type, domain, code_len);
1040 gpointer
1041 mono_create_class_init_trampoline (MonoVTable *vtable)
1043 gpointer code, ptr;
1044 MonoDomain *domain = vtable->domain;
1046 g_assert (!vtable->klass->generic_container);
1048 /* previously created trampoline code */
1049 mono_domain_lock (domain);
1050 ptr =
1051 g_hash_table_lookup (domain_jit_info (domain)->class_init_trampoline_hash,
1052 vtable);
1053 mono_domain_unlock (domain);
1054 if (ptr)
1055 return ptr;
1057 code = mono_create_specific_trampoline (vtable, MONO_TRAMPOLINE_CLASS_INIT, domain, NULL);
1059 ptr = mono_create_ftnptr (domain, code);
1061 /* store trampoline address */
1062 mono_domain_lock (domain);
1063 g_hash_table_insert (domain_jit_info (domain)->class_init_trampoline_hash,
1064 vtable, ptr);
1065 mono_domain_unlock (domain);
1067 mono_trampolines_lock ();
1068 if (!class_init_hash_addr)
1069 class_init_hash_addr = g_hash_table_new (NULL, NULL);
1070 g_hash_table_insert (class_init_hash_addr, ptr, vtable);
1071 mono_trampolines_unlock ();
1073 return ptr;
1076 gpointer
1077 mono_create_generic_class_init_trampoline (void)
1079 #ifdef MONO_ARCH_VTABLE_REG
1080 static gpointer code;
1082 mono_trampolines_lock ();
1084 if (!code) {
1085 if (mono_aot_only)
1086 code = mono_aot_get_named_code ("generic_class_init_trampoline");
1087 else
1088 code = mono_arch_create_generic_class_init_trampoline ();
1091 mono_trampolines_unlock ();
1093 return code;
1094 #else
1095 g_assert_not_reached ();
1096 #endif
1099 gpointer
1100 mono_create_jump_trampoline (MonoDomain *domain, MonoMethod *method, gboolean add_sync_wrapper)
1102 MonoJitInfo *ji;
1103 gpointer code;
1104 guint32 code_size = 0;
1106 code = mono_jit_find_compiled_method_with_jit_info (domain, method, &ji);
1108 * We cannot recover the correct type of a shared generic
1109 * method from its native code address, so we use the
1110 * trampoline instead.
1112 if (code && !ji->has_generic_jit_info)
1113 return code;
1115 mono_domain_lock (domain);
1116 code = g_hash_table_lookup (domain_jit_info (domain)->jump_trampoline_hash, method);
1117 mono_domain_unlock (domain);
1118 if (code)
1119 return code;
1121 code = mono_create_specific_trampoline (method, MONO_TRAMPOLINE_JUMP, mono_domain_get (), &code_size);
1122 g_assert (code_size);
1124 ji = mono_domain_alloc0 (domain, MONO_SIZEOF_JIT_INFO);
1125 ji->code_start = code;
1126 ji->code_size = code_size;
1127 ji->method = method;
1130 * mono_delegate_ctor needs to find the method metadata from the
1131 * trampoline address, so we save it here.
1134 mono_jit_info_table_add (domain, ji);
1136 mono_domain_lock (domain);
1137 g_hash_table_insert (domain_jit_info (domain)->jump_trampoline_hash, method, ji->code_start);
1138 mono_domain_unlock (domain);
1140 return ji->code_start;
1143 gpointer
1144 mono_create_jit_trampoline_in_domain (MonoDomain *domain, MonoMethod *method)
1146 gpointer tramp;
1148 if (mono_aot_only) {
1149 /* Avoid creating trampolines if possible */
1150 gpointer code = mono_jit_find_compiled_method (domain, method);
1152 if (code)
1153 return code;
1156 mono_domain_lock (domain);
1157 tramp = g_hash_table_lookup (domain_jit_info (domain)->jit_trampoline_hash, method);
1158 mono_domain_unlock (domain);
1159 if (tramp)
1160 return tramp;
1162 tramp = mono_create_specific_trampoline (method, MONO_TRAMPOLINE_JIT, domain, NULL);
1164 mono_domain_lock (domain);
1165 g_hash_table_insert (domain_jit_info (domain)->jit_trampoline_hash, method, tramp);
1166 mono_domain_unlock (domain);
1168 mono_jit_stats.method_trampolines++;
1170 return tramp;
1173 gpointer
1174 mono_create_jit_trampoline (MonoMethod *method)
1176 return mono_create_jit_trampoline_in_domain (mono_domain_get (), method);
1179 gpointer
1180 mono_create_jit_trampoline_from_token (MonoImage *image, guint32 token)
1182 gpointer tramp;
1184 MonoDomain *domain = mono_domain_get ();
1185 guint8 *buf, *start;
1187 buf = start = mono_domain_code_reserve (domain, 2 * sizeof (gpointer));
1189 *(gpointer*)(gpointer)buf = image;
1190 buf += sizeof (gpointer);
1191 *(guint32*)(gpointer)buf = token;
1193 tramp = mono_create_specific_trampoline (start, MONO_TRAMPOLINE_AOT, domain, NULL);
1195 mono_jit_stats.method_trampolines++;
1197 return tramp;
1200 gpointer
1201 mono_create_delegate_trampoline (MonoClass *klass)
1203 #ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
1204 MonoDomain *domain = mono_domain_get ();
1205 gpointer ptr;
1206 guint32 code_size = 0;
1207 gpointer *tramp_data;
1208 MonoMethod *invoke;
1210 mono_domain_lock (domain);
1211 ptr = g_hash_table_lookup (domain_jit_info (domain)->delegate_trampoline_hash, klass);
1212 mono_domain_unlock (domain);
1213 if (ptr)
1214 return ptr;
1216 // Precompute the delegate invoke impl and pass it to the delegate trampoline
1217 invoke = mono_get_delegate_invoke (klass);
1218 g_assert (invoke);
1220 tramp_data = mono_domain_alloc (domain, sizeof (gpointer) * 3);
1221 tramp_data [0] = invoke;
1222 tramp_data [1] = mono_arch_get_delegate_invoke_impl (mono_method_signature (invoke), TRUE);
1223 tramp_data [2] = mono_arch_get_delegate_invoke_impl (mono_method_signature (invoke), FALSE);
1225 ptr = mono_create_specific_trampoline (tramp_data, MONO_TRAMPOLINE_DELEGATE, mono_domain_get (), &code_size);
1226 g_assert (code_size);
1228 /* store trampoline address */
1229 mono_domain_lock (domain);
1230 g_hash_table_insert (domain_jit_info (domain)->delegate_trampoline_hash,
1231 klass, ptr);
1232 mono_domain_unlock (domain);
1234 return ptr;
1235 #else
1236 return NULL;
1237 #endif
1240 gpointer
1241 mono_create_rgctx_lazy_fetch_trampoline (guint32 offset)
1243 static gboolean inited = FALSE;
1244 static int num_trampolines = 0;
1246 gpointer tramp, ptr;
1248 if (mono_aot_only)
1249 return mono_aot_get_lazy_fetch_trampoline (offset);
1251 mono_trampolines_lock ();
1252 if (rgctx_lazy_fetch_trampoline_hash)
1253 tramp = g_hash_table_lookup (rgctx_lazy_fetch_trampoline_hash, GUINT_TO_POINTER (offset));
1254 else
1255 tramp = NULL;
1256 mono_trampolines_unlock ();
1257 if (tramp)
1258 return tramp;
1260 tramp = mono_arch_create_rgctx_lazy_fetch_trampoline (offset);
1261 ptr = mono_create_ftnptr (mono_get_root_domain (), tramp);
1263 mono_trampolines_lock ();
1264 if (!rgctx_lazy_fetch_trampoline_hash) {
1265 rgctx_lazy_fetch_trampoline_hash = g_hash_table_new (NULL, NULL);
1266 rgctx_lazy_fetch_trampoline_hash_addr = g_hash_table_new (NULL, NULL);
1268 g_hash_table_insert (rgctx_lazy_fetch_trampoline_hash, GUINT_TO_POINTER (offset), ptr);
1269 g_assert (offset != -1);
1270 g_hash_table_insert (rgctx_lazy_fetch_trampoline_hash_addr, ptr, GUINT_TO_POINTER (offset + 1));
1271 mono_trampolines_unlock ();
1273 if (!inited) {
1274 mono_counters_register ("RGCTX num lazy fetch trampolines",
1275 MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_trampolines);
1276 inited = TRUE;
1278 num_trampolines++;
1280 return ptr;
1283 gpointer
1284 mono_create_monitor_enter_trampoline (void)
1286 static gpointer code;
1288 if (mono_aot_only) {
1289 if (!code)
1290 code = mono_aot_get_named_code ("monitor_enter_trampoline");
1291 return code;
1294 #ifdef MONO_ARCH_MONITOR_OBJECT_REG
1295 mono_trampolines_lock ();
1297 if (!code)
1298 code = mono_arch_create_monitor_enter_trampoline ();
1300 mono_trampolines_unlock ();
1301 #else
1302 code = NULL;
1303 g_assert_not_reached ();
1304 #endif
1306 return code;
1309 gpointer
1310 mono_create_monitor_exit_trampoline (void)
1312 static gpointer code;
1314 if (mono_aot_only) {
1315 if (!code)
1316 code = mono_aot_get_named_code ("monitor_exit_trampoline");
1317 return code;
1320 #ifdef MONO_ARCH_MONITOR_OBJECT_REG
1321 mono_trampolines_lock ();
1323 if (!code)
1324 code = mono_arch_create_monitor_exit_trampoline ();
1326 mono_trampolines_unlock ();
1327 #else
1328 code = NULL;
1329 g_assert_not_reached ();
1330 #endif
1331 return code;
1334 #ifdef MONO_ARCH_LLVM_SUPPORTED
1336 * mono_create_llvm_vcall_trampoline:
1338 * LLVM emits code for virtual calls which mono_get_vcall_slot is unable to
1339 * decode, i.e. only the final branch address is available:
1340 * mov <offset>(%rax), %rax
1341 * <random code inserted by instruction scheduling>
1342 * call *%rax
1344 * To work around this problem, we don't use the common vtable trampoline when
1345 * llvm is enabled. Instead, we use one trampoline per method.
1347 gpointer
1348 mono_create_llvm_vcall_trampoline (MonoMethod *method)
1350 MonoDomain *domain;
1351 gpointer res;
1353 domain = mono_domain_get ();
1355 mono_domain_lock (domain);
1356 res = g_hash_table_lookup (domain_jit_info (domain)->llvm_vcall_trampoline_hash, method);
1357 mono_domain_unlock (domain);
1358 if (res)
1359 return res;
1361 res = mono_create_specific_trampoline (method, MONO_TRAMPOLINE_LLVM_VCALL, domain, NULL);
1363 mono_domain_lock (domain);
1364 g_hash_table_insert (domain_jit_info (domain)->llvm_vcall_trampoline_hash, method, res);
1365 mono_domain_unlock (domain);
1367 return res;
1371 * mono_create_llvm_imt_trampoline:
1373 * LLVM compiled code can't pass in the IMT argument, so we use this trampoline, which
1374 * sets the IMT argument, then branches to the contents of the vtable slot given by
1375 * vt_offset in the vtable which is obtained from the argument list.
1377 gpointer
1378 mono_create_llvm_imt_trampoline (MonoDomain *domain, MonoMethod *m, int vt_offset)
1380 #ifdef MONO_ARCH_HAVE_LLVM_IMT_TRAMPOLINE
1381 return mono_arch_get_llvm_imt_trampoline (domain, m, vt_offset);
1382 #else
1383 g_assert_not_reached ();
1384 return NULL;
1385 #endif
1387 #endif
1389 MonoVTable*
1390 mono_find_class_init_trampoline_by_addr (gconstpointer addr)
1392 MonoVTable *res;
1394 mono_trampolines_lock ();
1395 if (class_init_hash_addr)
1396 res = g_hash_table_lookup (class_init_hash_addr, addr);
1397 else
1398 res = NULL;
1399 mono_trampolines_unlock ();
1400 return res;
1403 guint32
1404 mono_find_rgctx_lazy_fetch_trampoline_by_addr (gconstpointer addr)
1406 int offset;
1408 mono_trampolines_lock ();
1409 if (rgctx_lazy_fetch_trampoline_hash_addr) {
1410 /* We store the real offset + 1 so we can detect when the lookup fails */
1411 offset = GPOINTER_TO_INT (g_hash_table_lookup (rgctx_lazy_fetch_trampoline_hash_addr, addr));
1412 if (offset)
1413 offset -= 1;
1414 else
1415 offset = -1;
1416 } else {
1417 offset = -1;
1419 mono_trampolines_unlock ();
1420 return offset;