2008-12-03 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / mini / mini-trampolines.c
1
2 #include <config.h>
3 #include <glib.h>
4
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>
10
11 #ifdef HAVE_VALGRIND_MEMCHECK_H
12 #include <valgrind/memcheck.h>
13 #endif
14
15 #include "mini.h"
16 #include "debug-mini.h"
17
18 /*
19  * Address of the trampoline code.  This is used by the debugger to check
20  * whether a method is a trampoline.
21  */
22 guint8* mono_trampoline_code [MONO_TRAMPOLINE_NUM];
23
24 static GHashTable *class_init_hash_addr = NULL;
25 static GHashTable *delegate_trampoline_hash_addr = NULL;
26 static GHashTable *rgctx_lazy_fetch_trampoline_hash = NULL;
27 static GHashTable *rgctx_lazy_fetch_trampoline_hash_addr = NULL;
28
29 #define mono_trampolines_lock() EnterCriticalSection (&trampolines_mutex)
30 #define mono_trampolines_unlock() LeaveCriticalSection (&trampolines_mutex)
31 static CRITICAL_SECTION trampolines_mutex;
32
33 static gpointer
34 get_unbox_trampoline (MonoGenericSharingContext *gsctx, MonoMethod *m, gpointer addr)
35 {
36         if (mono_aot_only)
37                 return mono_aot_get_unbox_trampoline (m);
38         else
39                 return mono_arch_get_unbox_trampoline (gsctx, m, addr);
40 }
41
42 #ifdef MONO_ARCH_HAVE_IMT
43
44 static gpointer*
45 mono_convert_imt_slot_to_vtable_slot (gpointer* slot, gpointer *regs, guint8 *code, MonoMethod *method, MonoMethod **impl_method)
46 {
47         MonoGenericSharingContext *gsctx = mono_get_generic_context_from_code (code);
48         MonoObject *this_argument = mono_arch_find_this_argument (regs, method, gsctx);
49         MonoVTable *vt = this_argument->vtable;
50         int displacement = slot - ((gpointer*)vt);
51
52         if (displacement > 0) {
53                 /* slot is in the vtable, not in the IMT */
54 #if DEBUG_IMT
55                 printf ("mono_convert_imt_slot_to_vtable_slot: slot %p is in the vtable, not in the IMT\n", slot);
56 #endif
57                 return slot;
58         } else {
59                 MonoMethod *imt_method = mono_arch_find_imt_method (regs, code);
60                 int interface_offset;
61                 int imt_slot = MONO_IMT_SIZE + displacement;
62
63                 interface_offset = mono_class_interface_offset (vt->klass, imt_method->klass);
64
65                 if (interface_offset < 0) {
66                         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));
67                         g_assert_not_reached ();
68                 }
69                 mono_vtable_build_imt_slot (vt, mono_method_get_imt_slot (imt_method));
70
71                 if (impl_method)
72                         *impl_method = mono_class_get_vtable_entry (vt->klass, interface_offset + imt_method->slot);
73 #if DEBUG_IMT
74                 printf ("mono_convert_imt_slot_to_vtable_slot: method = %s.%s.%s, imt_method = %s.%s.%s\n",
75                                 method->klass->name_space, method->klass->name, method->name, 
76                                 imt_method->klass->name_space, imt_method->klass->name, imt_method->name);
77 #endif
78                 g_assert (imt_slot < MONO_IMT_SIZE);
79                 if (vt->imt_collisions_bitmap & (1 << imt_slot)) {
80                         int vtable_offset = interface_offset + mono_method_get_vtable_index (imt_method);
81                         gpointer *vtable_slot = & (vt->vtable [vtable_offset]);
82 #if DEBUG_IMT
83                         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);
84 #endif
85                         return vtable_slot;
86                 } else {
87 #if DEBUG_IMT
88                         printf ("mono_convert_imt_slot_to_vtable_slot: slot %p[%d] is in the IMT, but not colliding\n", slot, imt_slot);
89 #endif
90                         return slot;
91                 }
92         }
93 }
94 #endif
95
96 /**
97  * mono_magic_trampoline:
98  *
99  *   This trampoline handles calls from JITted code.
100  */
101 gpointer
102 mono_magic_trampoline (gssize *regs, guint8 *code, MonoMethod *m, guint8* tramp)
103 {
104         gpointer addr;
105         gpointer *vtable_slot;
106         gboolean generic_shared = FALSE;
107         MonoMethod *declaring = NULL;
108         MonoGenericInst *generic_virtual_method_inst = NULL;
109         int context_used;
110
111 #if MONO_ARCH_COMMON_VTABLE_TRAMPOLINE
112         if (m == MONO_FAKE_VTABLE_METHOD) {
113                 int displacement;
114                 MonoVTable *vt = mono_arch_get_vcall_slot (code, (gpointer*)regs, &displacement);
115                 if (!vt) {
116                         int i;
117
118                         /* Print some debug info */
119                         for (i = 0; i < 32; ++i)
120                                 printf ("0x%x ", code [-32 + i]);
121                         printf ("\n");
122                         g_assert (vt);
123                 }
124                 if (displacement > 0) {
125                         displacement -= G_STRUCT_OFFSET (MonoVTable, vtable);
126                         g_assert (displacement >= 0);
127                         displacement /= sizeof (gpointer);
128
129                         /* Avoid loading metadata or creating a generic vtable if possible */
130                         addr = mono_aot_get_method_from_vt_slot (mono_domain_get (), vt, displacement);
131                         if (addr && !vt->klass->valuetype) {
132                                 vtable_slot = mono_arch_get_vcall_slot_addr (code, (gpointer*)regs);
133                                 if (mono_aot_is_got_entry (code, (guint8*)vtable_slot) || mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot)) {
134                                         *vtable_slot = mono_get_addr_from_ftnptr (addr);
135                                 }
136
137                                 return addr;
138                         }
139
140                         mono_class_setup_vtable (vt->klass);
141                         m = vt->klass->vtable [displacement];
142                         /*g_print ("%s with disp %d: %s at %p\n", vt->klass->name, displacement, m->name, code);*/
143                 } else {
144                         /* We got here from an interface method: redirect to IMT handling */
145                         m = MONO_FAKE_IMT_METHOD;
146                         /*g_print ("vtable with disp %d at %p\n", displacement, code);*/
147                 }
148         }
149 #endif
150         /* this is the IMT trampoline */
151 #ifdef MONO_ARCH_HAVE_IMT
152         if (m == MONO_FAKE_IMT_METHOD) {
153                 MonoMethod *impl_method;
154                 /* we get the interface method because mono_convert_imt_slot_to_vtable_slot ()
155                  * needs the signature to be able to find the this argument
156                  */
157                 m = mono_arch_find_imt_method ((gpointer*)regs, code);
158                 vtable_slot = mono_arch_get_vcall_slot_addr (code, (gpointer*)regs);
159                 g_assert (vtable_slot);
160                 vtable_slot = mono_convert_imt_slot_to_vtable_slot (vtable_slot, (gpointer*)regs, code, m, &impl_method);
161                 /* mono_convert_imt_slot_to_vtable_slot () also gives us the method that is supposed
162                  * to be called, so we compile it and go ahead as usual.
163                  */
164                 /*g_print ("imt found method %p (%s) at %p\n", impl_method, impl_method->name, code);*/
165                 m = impl_method;
166         }
167 #endif
168
169         if (m->is_generic) {
170                 MonoGenericContext context = { NULL, NULL };
171                 MonoMethod *declaring;
172
173                 if (m->is_inflated)
174                         declaring = mono_method_get_declaring_generic_method (m);
175                 else
176                         declaring = m;
177
178                 if (m->klass->generic_class)
179                         context.class_inst = m->klass->generic_class->context.class_inst;
180                 else
181                         g_assert (!m->klass->generic_container);
182
183 #ifdef MONO_ARCH_HAVE_IMT
184                 generic_virtual_method_inst = (MonoGenericInst*)mono_arch_find_imt_method ((gpointer*)regs, code);
185 #endif
186                 context.method_inst = generic_virtual_method_inst;
187
188                 m = mono_class_inflate_generic_method (declaring, &context);
189                 /* FIXME: only do this if the method is sharable */
190                 m = mono_marshal_get_static_rgctx_invoke (m);
191         } else if ((context_used = mono_method_check_context_used (m))) {
192                 MonoClass *klass = NULL;
193                 MonoMethod *actual_method = NULL;
194                 MonoVTable *vt = NULL;
195                 MonoGenericInst *method_inst = NULL;
196
197                 vtable_slot = NULL;
198                 generic_shared = TRUE;
199
200                 g_assert (code);
201
202                 if (m->is_inflated && mono_method_get_context (m)->method_inst) {
203 #ifdef MONO_ARCH_RGCTX_REG
204                         MonoMethodRuntimeGenericContext *mrgctx = (MonoMethodRuntimeGenericContext*)mono_arch_find_static_call_vtable ((gpointer*)regs, code);
205
206                         klass = mrgctx->class_vtable->klass;
207                         method_inst = mrgctx->method_inst;
208 #else
209                         g_assert_not_reached ();
210 #endif
211                 } else if ((m->flags & METHOD_ATTRIBUTE_STATIC) || m->klass->valuetype) {
212 #ifdef MONO_ARCH_RGCTX_REG
213                         MonoVTable *vtable = mono_arch_find_static_call_vtable ((gpointer*)regs, code);
214
215                         klass = vtable->klass;
216 #else
217                         g_assert_not_reached ();
218 #endif
219                 } else {
220 #ifdef MONO_ARCH_HAVE_IMT
221                         MonoObject *this_argument = mono_arch_find_this_argument ((gpointer*)regs, m,
222                                 mono_get_generic_context_from_code (code));
223
224                         vt = this_argument->vtable;
225                         vtable_slot = mono_arch_get_vcall_slot_addr (code, (gpointer*)regs);
226
227                         g_assert (this_argument->vtable->klass->inited);
228                         //mono_class_init (this_argument->vtable->klass);
229
230                         if (!vtable_slot)
231                                 klass = this_argument->vtable->klass->supertypes [m->klass->idepth - 1];
232 #else
233                         NOT_IMPLEMENTED;
234 #endif
235                 }
236
237                 g_assert (vtable_slot || klass);
238
239                 if (vtable_slot) {
240                         int displacement = vtable_slot - ((gpointer*)vt);
241
242                         g_assert_not_reached ();
243
244                         g_assert (displacement > 0);
245
246                         actual_method = vt->klass->vtable [displacement];
247                 }
248
249                 if (method_inst) {
250                         MonoGenericContext context = { NULL, NULL };
251
252                         if (m->is_inflated)
253                                 declaring = mono_method_get_declaring_generic_method (m);
254                         else
255                                 declaring = m;
256
257                         if (klass->generic_class)
258                                 context.class_inst = klass->generic_class->context.class_inst;
259                         else if (klass->generic_container)
260                                 context.class_inst = klass->generic_container->context.class_inst;
261                         context.method_inst = method_inst;
262
263                         actual_method = mono_class_inflate_generic_method (declaring, &context);
264                 } else {
265                         actual_method = mono_class_get_method_generic (klass, m);
266                 }
267
268                 g_assert (klass);
269                 g_assert (actual_method->klass == klass);
270
271                 if (actual_method->is_inflated)
272                         declaring = mono_method_get_declaring_generic_method (actual_method);
273                 else
274                         declaring = NULL;
275
276                 m = actual_method;
277         }
278
279         if (m->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) {
280                 MonoJitInfo *ji;
281
282                 if (code)
283                         ji = mono_jit_info_table_find (mono_domain_get (), (char*)code);
284                 else
285                         ji = NULL;
286
287                 /* Avoid recursion */
288                 if (!(ji && ji->method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED))
289                         m = mono_marshal_get_synchronized_wrapper (m);
290         }
291
292         addr = mono_compile_method (m);
293         g_assert (addr);
294
295         mono_debugger_trampoline_compiled (m, addr);
296
297         if (generic_virtual_method_inst) {
298                 vtable_slot = mono_arch_get_vcall_slot_addr (code, (gpointer*)regs);
299                 g_assert (vtable_slot);
300
301                 mono_method_add_generic_virtual_invocation (mono_domain_get (), vtable_slot,
302                         generic_virtual_method_inst, addr);
303
304                 return addr;
305         }
306
307         /* the method was jumped to */
308         if (!code) {
309                 MonoDomain *domain = mono_domain_get ();
310
311                 /* Patch the got entries pointing to this method */
312                 /* 
313                  * We do this here instead of in mono_codegen () to cover the case when m
314                  * was loaded from an aot image.
315                  */
316                 if (domain_jit_info (domain)->jump_target_got_slot_hash) {
317                         GSList *list, *tmp;
318
319                         mono_domain_lock (domain);
320                         list = g_hash_table_lookup (domain_jit_info (domain)->jump_target_got_slot_hash, m);
321                         if (list) {
322                                 for (tmp = list; tmp; tmp = tmp->next) {
323                                         gpointer *got_slot = tmp->data;
324                                         *got_slot = addr;
325                                 }
326                                 g_hash_table_remove (domain_jit_info (domain)->jump_target_got_slot_hash, m);
327                                 g_slist_free (list);
328                         }
329                         mono_domain_unlock (domain);
330                 }
331
332                 return addr;
333         }
334
335         vtable_slot = mono_arch_get_vcall_slot_addr (code, (gpointer*)regs);
336
337         if (vtable_slot) {
338                 if (m->klass->valuetype)
339                         addr = get_unbox_trampoline (mono_get_generic_context_from_code (code), m, addr);
340
341                 g_assert (*vtable_slot);
342
343                 if (mono_aot_is_got_entry (code, (guint8*)vtable_slot) || mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot)) {
344 #ifdef MONO_ARCH_HAVE_IMT
345                         vtable_slot = mono_convert_imt_slot_to_vtable_slot (vtable_slot, (gpointer*)regs, code, m, NULL);
346 #endif
347                         *vtable_slot = mono_get_addr_from_ftnptr (addr);
348                 }
349         }
350         else {
351                 guint8 *plt_entry = mono_aot_get_plt_entry (code);
352
353                 if (plt_entry) {
354                         mono_arch_patch_plt_entry (plt_entry, addr);
355                 } else if (!generic_shared || (m->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
356                         mono_domain_lookup_shared_generic (mono_domain_get (), declaring)) {
357                         if (generic_shared) {
358                                 if (m->wrapper_type != MONO_WRAPPER_NONE)
359                                         m = mono_marshal_method_from_wrapper (m);
360                                 g_assert (mono_method_is_generic_sharable_impl (m, FALSE));
361                         }
362
363                         /* Patch calling code */
364                         if (plt_entry) {
365
366                         } else {
367                                 MonoJitInfo *ji = 
368                                         mono_jit_info_table_find (mono_domain_get (), (char*)code);
369                                 MonoJitInfo *target_ji = 
370                                         mono_jit_info_table_find (mono_domain_get (), mono_get_addr_from_ftnptr (addr));
371
372                                 if (mono_method_same_domain (ji, target_ji))
373                                         mono_arch_patch_callsite (ji->code_start, code, addr);
374                         }
375                 }
376         }
377
378         return addr;
379 }
380
381 gpointer
382 mono_generic_virtual_remoting_trampoline (gssize *regs, guint8 *code, MonoMethod *m, guint8 *tramp)
383 {
384         MonoGenericContext context = { NULL, NULL };
385         MonoMethod *declaring;
386         gpointer addr;
387
388         g_assert (m->is_generic);
389
390         if (m->is_inflated)
391                 declaring = mono_method_get_declaring_generic_method (m);
392         else
393                 declaring = m;
394
395         if (m->klass->generic_class)
396                 context.class_inst = m->klass->generic_class->context.class_inst;
397         else
398                 g_assert (!m->klass->generic_container);
399
400 #ifdef MONO_ARCH_HAVE_IMT
401         context.method_inst = (MonoGenericInst*)mono_arch_find_imt_method ((gpointer*)regs, code);
402 #endif
403         m = mono_class_inflate_generic_method (declaring, &context);
404         m = mono_marshal_get_remoting_invoke_with_check (m);
405
406         addr = mono_compile_method (m);
407         g_assert (addr);
408
409         mono_debugger_trampoline_compiled (m, addr);
410
411         return addr;
412 }
413
414 /*
415  * mono_aot_trampoline:
416  *
417  *   This trampoline handles calls made from AOT code. We try to bypass the 
418  * normal JIT compilation logic to avoid loading the metadata for the method.
419  */
420 #ifdef MONO_ARCH_HAVE_CREATE_TRAMPOLINE_FROM_TOKEN
421 gpointer
422 mono_aot_trampoline (gssize *regs, guint8 *code, guint8 *token_info, 
423                                          guint8* tramp)
424 {
425         MonoImage *image;
426         guint32 token;
427         MonoMethod *method = NULL;
428         gpointer addr;
429         gpointer *vtable_slot;
430         gboolean is_got_entry;
431         guint8 *plt_entry;
432
433         image = *(gpointer*)(gpointer)token_info;
434         token_info += sizeof (gpointer);
435         token = *(guint32*)(gpointer)token_info;
436
437         addr = mono_aot_get_method_from_token (mono_domain_get (), image, token);
438         if (!addr) {
439                 method = mono_get_method (image, token, NULL);
440                 g_assert (method);
441
442                 /* Use the generic code */
443                 return mono_magic_trampoline (regs, code, method, tramp);
444         }
445
446         vtable_slot = mono_arch_get_vcall_slot_addr (code, (gpointer*)regs);
447         g_assert (!vtable_slot);
448
449         /* This is a normal call through a PLT entry */
450         plt_entry = mono_aot_get_plt_entry (code);
451         g_assert (plt_entry);
452
453         mono_arch_patch_plt_entry (plt_entry, addr);
454
455         is_got_entry = FALSE;
456
457         /*
458          * Since AOT code is only used in the root domain, 
459          * mono_domain_get () != mono_get_root_domain () means the calling method
460          * is AppDomain:InvokeInDomain, so this is the same check as in 
461          * mono_method_same_domain () but without loading the metadata for the method.
462          */
463         if ((is_got_entry && (mono_domain_get () == mono_get_root_domain ())) || mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot)) {
464 #ifdef MONO_ARCH_HAVE_IMT
465                 if (!method)
466                         method = mono_get_method (image, token, NULL);
467                 vtable_slot = mono_convert_imt_slot_to_vtable_slot (vtable_slot, (gpointer*)regs, code, method, NULL);
468 #endif
469                 *vtable_slot = addr;
470         }
471
472         return addr;
473 }
474
475 /*
476  * mono_aot_plt_trampoline:
477  *
478  *   This trampoline handles calls made from AOT code through the PLT table.
479  */
480 gpointer
481 mono_aot_plt_trampoline (gssize *regs, guint8 *code, guint8 *aot_module, 
482                                                  guint8* tramp)
483 {
484         guint32 plt_info_offset = mono_aot_get_plt_info_offset (regs, code);
485
486         return mono_aot_plt_resolve (aot_module, plt_info_offset, code);
487 }
488 #endif
489
490 /**
491  * mono_class_init_trampoline:
492  *
493  * This method calls mono_runtime_class_init () to run the static constructor
494  * for the type, then patches the caller code so it is not called again.
495  */
496 void
497 mono_class_init_trampoline (gssize *regs, guint8 *code, MonoVTable *vtable, guint8 *tramp)
498 {
499         guint8 *plt_entry = mono_aot_get_plt_entry (code);
500
501         mono_runtime_class_init (vtable);
502
503         if (plt_entry) {
504                 mono_arch_nullify_plt_entry (plt_entry);
505         } else {
506                 mono_arch_nullify_class_init_trampoline (code, regs);
507         }
508 }
509
510 /**
511  * mono_generic_class_init_trampoline:
512  *
513  * This method calls mono_runtime_class_init () to run the static constructor
514  * for the type.
515  */
516 void
517 mono_generic_class_init_trampoline (gssize *regs, guint8 *code, MonoVTable *vtable, guint8 *tramp)
518 {
519         g_assert (!vtable->initialized);
520
521         mono_runtime_class_init (vtable);
522 }
523
524 static gpointer
525 mono_rgctx_lazy_fetch_trampoline (gssize *regs, guint8 *code, gpointer data, guint8 *tramp)
526 {
527 #ifdef MONO_ARCH_VTABLE_REG
528         static gboolean inited = FALSE;
529         static int num_lookups = 0;
530         guint32 slot = GPOINTER_TO_UINT (data);
531         gpointer arg = (gpointer)(gssize)regs [MONO_ARCH_VTABLE_REG];
532         guint32 index = MONO_RGCTX_SLOT_INDEX (slot);
533         gboolean mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
534
535         if (!inited) {
536                 mono_counters_register ("RGCTX unmanaged lookups", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_lookups);
537                 inited = TRUE;
538         }
539
540         num_lookups++;
541
542         if (mrgctx)
543                 return mono_method_fill_runtime_generic_context (arg, index);
544         else
545                 return mono_class_fill_runtime_generic_context (arg, index);
546 #else
547         g_assert_not_reached ();
548 #endif
549 }
550
551 void
552 mono_monitor_enter_trampoline (gssize *regs, guint8 *code, MonoObject *obj, guint8 *tramp)
553 {
554         mono_monitor_enter (obj);
555 }
556
557 void
558 mono_monitor_exit_trampoline (gssize *regs, guint8 *code, MonoObject *obj, guint8 *tramp)
559 {
560         mono_monitor_exit (obj);
561 }
562
563 #ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
564
565 /**
566  * mono_delegate_trampoline:
567  *
568  *   This trampoline handles calls made to Delegate:Invoke ().
569  * This is called once the first time a delegate is invoked, so it must be fast.
570  */
571 gpointer
572 mono_delegate_trampoline (gssize *regs, guint8 *code, gpointer *tramp_data, guint8* tramp)
573 {
574         MonoDomain *domain = mono_domain_get ();
575         MonoDelegate *delegate;
576         MonoJitInfo *ji;
577         MonoMethod *m;
578         MonoMethod *method = NULL;
579         gboolean multicast, callvirt;
580         MonoMethod *invoke = tramp_data [0];
581         guint8 *impl_this = tramp_data [1];
582         guint8 *impl_nothis = tramp_data [2];
583
584         /* Obtain the delegate object according to the calling convention */
585
586         /* 
587          * Avoid calling mono_get_generic_context_from_code () now since it is expensive, 
588          * get_this_arg_from_call will call it if needed.
589          */
590         delegate = mono_arch_get_this_arg_from_call (NULL, mono_method_signature (invoke), regs, code);
591
592         if (delegate->method) {
593                 method = delegate->method;
594
595                 /*
596                  * delegate->method_ptr == NULL means the delegate was initialized by 
597                  * mini_delegate_ctor, while != NULL means it is initialized by 
598                  * mono_delegate_ctor_with_method (). In both cases, we need to add wrappers
599                  * (ctor_with_method () does this, but it doesn't store the wrapper back into
600                  * delegate->method).
601                  */
602                 if (delegate->target && delegate->target->vtable->klass == mono_defaults.transparent_proxy_class)
603                         method = mono_marshal_get_remoting_invoke (method);
604                 else if (mono_method_signature (method)->hasthis && method->klass->valuetype)
605                         method = mono_marshal_get_unbox_wrapper (method);
606         } else {
607                 ji = mono_jit_info_table_find (domain, mono_get_addr_from_ftnptr (delegate->method_ptr));
608                 if (ji)
609                         method = ji->method;
610         }
611         callvirt = !delegate->target && method && mono_method_signature (method)->hasthis;
612
613         if (method && method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
614                 method = mono_marshal_get_synchronized_wrapper (method);
615
616         /* 
617          * If the called address is a trampoline, replace it with the compiled method so
618          * further calls don't have to go through the trampoline.
619          */
620         if (method && !callvirt) {
621                 /* Avoid the overhead of looking up an already compiled method if possible */
622                 if (delegate->method_code && *delegate->method_code) {
623                         delegate->method_ptr = *delegate->method_code;
624                 } else {
625                         delegate->method_ptr = mono_compile_method (method);
626                         if (delegate->method_code)
627                                 *delegate->method_code = delegate->method_ptr;
628                         mono_debugger_trampoline_compiled (method, delegate->method_ptr);
629                 }
630         }
631
632         multicast = ((MonoMulticastDelegate*)delegate)->prev != NULL;
633         if (!multicast && !callvirt) {
634                 code = delegate->target ? impl_this : impl_nothis;
635
636                 if (code) {
637                         delegate->invoke_impl = code;
638                         return code;
639                 }
640         }
641
642         /* The general, unoptimized case */
643         m = mono_marshal_get_delegate_invoke (invoke, delegate);
644         code = mono_compile_method (m);
645         delegate->invoke_impl = mono_get_addr_from_ftnptr (code);
646         mono_debugger_trampoline_compiled (m, delegate->invoke_impl);
647
648         return mono_get_addr_from_ftnptr (code);
649 }
650
651 #endif
652
653 /*
654  * mono_get_trampoline_func:
655  *
656  *   Return the C function which needs to be called by the generic trampoline of type
657  * TRAMP_TYPE.
658  */
659 gconstpointer
660 mono_get_trampoline_func (MonoTrampolineType tramp_type)
661 {
662         switch (tramp_type) {
663         case MONO_TRAMPOLINE_JIT:
664         case MONO_TRAMPOLINE_JUMP:
665                 return mono_magic_trampoline;
666         case MONO_TRAMPOLINE_CLASS_INIT:
667                 return mono_class_init_trampoline;
668         case MONO_TRAMPOLINE_GENERIC_CLASS_INIT:
669                 return mono_generic_class_init_trampoline;
670         case MONO_TRAMPOLINE_RGCTX_LAZY_FETCH:
671                 return mono_rgctx_lazy_fetch_trampoline;
672 #ifdef MONO_ARCH_AOT_SUPPORTED
673         case MONO_TRAMPOLINE_AOT:
674                 return mono_aot_trampoline;
675         case MONO_TRAMPOLINE_AOT_PLT:
676                 return mono_aot_plt_trampoline;
677 #endif
678 #ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
679         case MONO_TRAMPOLINE_DELEGATE:
680                 return mono_delegate_trampoline;
681 #endif
682         case MONO_TRAMPOLINE_RESTORE_STACK_PROT:
683                 return mono_altstack_restore_prot;
684         case MONO_TRAMPOLINE_GENERIC_VIRTUAL_REMOTING:
685                 return mono_generic_virtual_remoting_trampoline;
686         case MONO_TRAMPOLINE_MONITOR_ENTER:
687                 return mono_monitor_enter_trampoline;
688         case MONO_TRAMPOLINE_MONITOR_EXIT:
689                 return mono_monitor_exit_trampoline;
690         default:
691                 g_assert_not_reached ();
692                 return NULL;
693         }
694 }
695
696 void
697 mono_trampolines_init (void)
698 {
699         InitializeCriticalSection (&trampolines_mutex);
700
701         if (mono_aot_only)
702                 return;
703
704         mono_trampoline_code [MONO_TRAMPOLINE_JIT] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_JIT);
705         mono_trampoline_code [MONO_TRAMPOLINE_JUMP] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_JUMP);
706         mono_trampoline_code [MONO_TRAMPOLINE_CLASS_INIT] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_CLASS_INIT);
707         mono_trampoline_code [MONO_TRAMPOLINE_GENERIC_CLASS_INIT] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_GENERIC_CLASS_INIT);
708         mono_trampoline_code [MONO_TRAMPOLINE_RGCTX_LAZY_FETCH] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_RGCTX_LAZY_FETCH);
709 #ifdef MONO_ARCH_AOT_SUPPORTED
710         mono_trampoline_code [MONO_TRAMPOLINE_AOT] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_AOT);
711         mono_trampoline_code [MONO_TRAMPOLINE_AOT_PLT] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_AOT_PLT);
712 #endif
713 #ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
714         mono_trampoline_code [MONO_TRAMPOLINE_DELEGATE] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_DELEGATE);
715 #endif
716         mono_trampoline_code [MONO_TRAMPOLINE_RESTORE_STACK_PROT] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_RESTORE_STACK_PROT);
717         mono_trampoline_code [MONO_TRAMPOLINE_GENERIC_VIRTUAL_REMOTING] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_GENERIC_VIRTUAL_REMOTING);
718         mono_trampoline_code [MONO_TRAMPOLINE_MONITOR_ENTER] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_MONITOR_ENTER);
719         mono_trampoline_code [MONO_TRAMPOLINE_MONITOR_EXIT] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_MONITOR_EXIT);
720 }
721
722 void
723 mono_trampolines_cleanup (void)
724 {
725         if (class_init_hash_addr)
726                 g_hash_table_destroy (class_init_hash_addr);
727         if (delegate_trampoline_hash_addr)
728                 g_hash_table_destroy (delegate_trampoline_hash_addr);
729
730         DeleteCriticalSection (&trampolines_mutex);
731 }
732
733 guint8 *
734 mono_get_trampoline_code (MonoTrampolineType tramp_type)
735 {
736         g_assert (mono_trampoline_code [tramp_type]);
737
738         return mono_trampoline_code [tramp_type];
739 }
740
741 gpointer
742 mono_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
743 {
744         if (mono_aot_only)
745                 return mono_aot_create_specific_trampoline (mono_defaults.corlib, arg1, tramp_type, domain, code_len);
746         else
747                 return mono_arch_create_specific_trampoline (arg1, tramp_type, domain, code_len);
748 }
749
750 gpointer
751 mono_create_class_init_trampoline (MonoVTable *vtable)
752 {
753         gpointer code, ptr;
754         MonoDomain *domain = vtable->domain;
755
756         g_assert (!vtable->klass->generic_container);
757
758         /* previously created trampoline code */
759         mono_domain_lock (domain);
760         ptr = 
761                 g_hash_table_lookup (domain_jit_info (domain)->class_init_trampoline_hash,
762                                                                   vtable);
763         mono_domain_unlock (domain);
764         if (ptr)
765                 return ptr;
766
767         code = mono_create_specific_trampoline (vtable, MONO_TRAMPOLINE_CLASS_INIT, domain, NULL);
768
769         ptr = mono_create_ftnptr (domain, code);
770
771         /* store trampoline address */
772         mono_domain_lock (domain);
773         g_hash_table_insert (domain_jit_info (domain)->class_init_trampoline_hash,
774                                                           vtable, ptr);
775         mono_domain_unlock (domain);
776
777         mono_trampolines_lock ();
778         if (!class_init_hash_addr)
779                 class_init_hash_addr = g_hash_table_new (NULL, NULL);
780         g_hash_table_insert (class_init_hash_addr, ptr, vtable);
781         mono_trampolines_unlock ();
782
783         return ptr;
784 }
785
786 gpointer
787 mono_create_generic_class_init_trampoline (void)
788 {
789 #ifdef MONO_ARCH_VTABLE_REG
790         static gpointer code;
791
792         mono_trampolines_lock ();
793
794         if (!code)
795                 code = mono_arch_create_generic_class_init_trampoline ();
796
797         mono_trampolines_unlock ();
798
799         return code;
800 #else
801         g_assert_not_reached ();
802 #endif
803 }
804
805 gpointer
806 mono_create_jump_trampoline (MonoDomain *domain, MonoMethod *method, gboolean add_sync_wrapper)
807 {
808         MonoJitInfo *ji;
809         gpointer code;
810         guint32 code_size = 0;
811
812         code = mono_jit_find_compiled_method (domain, method);
813         if (code)
814                 return code;
815
816         mono_domain_lock (domain);
817         code = g_hash_table_lookup (domain_jit_info (domain)->jump_trampoline_hash, method);
818         mono_domain_unlock (domain);
819         if (code)
820                 return code;
821
822         code = mono_create_specific_trampoline (method, MONO_TRAMPOLINE_JUMP, mono_domain_get (), &code_size);
823         g_assert (code_size);
824
825         mono_domain_lock (domain);
826         ji = mono_domain_alloc0 (domain, sizeof (MonoJitInfo));
827         mono_domain_unlock (domain);
828         ji->code_start = code;
829         ji->code_size = code_size;
830         ji->method = method;
831
832         /*
833          * mono_delegate_ctor needs to find the method metadata from the 
834          * trampoline address, so we save it here.
835          */
836
837         mono_jit_info_table_add (domain, ji);
838
839         mono_domain_lock (domain);
840         g_hash_table_insert (domain_jit_info (domain)->jump_trampoline_hash, method, ji->code_start);
841         mono_domain_unlock (domain);
842
843         return ji->code_start;
844 }
845
846 gpointer
847 mono_create_jit_trampoline_in_domain (MonoDomain *domain, MonoMethod *method)
848 {
849         gpointer tramp;
850
851         if (mono_aot_only) {
852                 /* Avoid creating trampolines if possible */
853                 gpointer code = mono_jit_find_compiled_method (domain, method);
854                 
855                 if (code)
856                         return code;
857         }
858
859         mono_domain_lock (domain);
860         tramp = g_hash_table_lookup (domain_jit_info (domain)->jit_trampoline_hash, method);
861         mono_domain_unlock (domain);
862         if (tramp)
863                 return tramp;
864
865         tramp = mono_create_specific_trampoline (method, MONO_TRAMPOLINE_JIT, domain, NULL);
866         
867         mono_domain_lock (domain);
868         g_hash_table_insert (domain_jit_info (domain)->jit_trampoline_hash, method, tramp);
869         mono_domain_unlock (domain);
870
871         mono_jit_stats.method_trampolines++;
872
873         return tramp;
874 }       
875
876 gpointer
877 mono_create_jit_trampoline (MonoMethod *method)
878 {
879         return mono_create_jit_trampoline_in_domain (mono_domain_get (), method);
880 }
881
882 #ifdef MONO_ARCH_HAVE_CREATE_TRAMPOLINE_FROM_TOKEN
883 gpointer
884 mono_create_jit_trampoline_from_token (MonoImage *image, guint32 token)
885 {
886         gpointer tramp;
887
888         MonoDomain *domain = mono_domain_get ();
889         guint8 *buf, *start;
890
891         mono_domain_lock (domain);
892         buf = start = mono_code_manager_reserve (domain->code_mp, 2 * sizeof (gpointer));
893         mono_domain_unlock (domain);
894
895         *(gpointer*)(gpointer)buf = image;
896         buf += sizeof (gpointer);
897         *(guint32*)(gpointer)buf = token;
898
899         tramp = mono_create_specific_trampoline (start, MONO_TRAMPOLINE_AOT, domain, NULL);
900
901         mono_jit_stats.method_trampolines++;
902
903         return tramp;
904 }       
905 #endif
906
907 gpointer
908 mono_create_delegate_trampoline (MonoClass *klass)
909 {
910 #ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
911         MonoDomain *domain = mono_domain_get ();
912         gpointer ptr;
913         guint32 code_size = 0;
914         gpointer *tramp_data;
915         MonoMethod *invoke;
916
917         mono_domain_lock (domain);
918         ptr = g_hash_table_lookup (domain_jit_info (domain)->delegate_trampoline_hash, klass);
919         mono_domain_unlock (domain);
920         if (ptr)
921                 return ptr;
922
923         // Precompute the delegate invoke impl and pass it to the delegate trampoline
924         invoke = mono_get_delegate_invoke (klass);
925         g_assert (invoke);
926
927         mono_domain_lock (domain );
928         tramp_data = mono_domain_alloc (domain, sizeof (gpointer) * 3);
929         mono_domain_unlock (domain);
930         tramp_data [0] = invoke;
931         if (mono_aot_only) {
932                 tramp_data [1] = NULL;
933                 tramp_data [2] = NULL;
934         } else {
935                 tramp_data [1] = mono_arch_get_delegate_invoke_impl (mono_method_signature (invoke), TRUE);
936                 tramp_data [2] = mono_arch_get_delegate_invoke_impl (mono_method_signature (invoke), FALSE);
937         }
938
939         ptr = mono_create_specific_trampoline (tramp_data, MONO_TRAMPOLINE_DELEGATE, mono_domain_get (), &code_size);
940         g_assert (code_size);
941
942         /* store trampoline address */
943         mono_domain_lock (domain);
944         g_hash_table_insert (domain_jit_info (domain)->delegate_trampoline_hash,
945                                                           klass, ptr);
946         mono_domain_unlock (domain);
947
948         mono_trampolines_lock ();
949         if (!delegate_trampoline_hash_addr)
950                 delegate_trampoline_hash_addr = g_hash_table_new (NULL, NULL);
951         g_hash_table_insert (delegate_trampoline_hash_addr, ptr, klass);
952         mono_trampolines_unlock ();
953
954         return ptr;
955 #else
956         return NULL;
957 #endif
958 }
959
960 gpointer
961 mono_create_rgctx_lazy_fetch_trampoline (guint32 offset)
962 {
963         static gboolean inited = FALSE;
964         static int num_trampolines = 0;
965
966         gpointer tramp, ptr;
967
968         if (mono_aot_only)
969                 return mono_aot_get_lazy_fetch_trampoline (offset);
970
971         mono_trampolines_lock ();
972         if (rgctx_lazy_fetch_trampoline_hash)
973                 tramp = g_hash_table_lookup (rgctx_lazy_fetch_trampoline_hash, GUINT_TO_POINTER (offset));
974         else
975                 tramp = NULL;
976         mono_trampolines_unlock ();
977         if (tramp)
978                 return tramp;
979
980         tramp = mono_arch_create_rgctx_lazy_fetch_trampoline (offset);
981         ptr = mono_create_ftnptr (mono_get_root_domain (), tramp);
982
983         mono_trampolines_lock ();
984         if (!rgctx_lazy_fetch_trampoline_hash) {
985                 rgctx_lazy_fetch_trampoline_hash = g_hash_table_new (NULL, NULL);
986                 rgctx_lazy_fetch_trampoline_hash_addr = g_hash_table_new (NULL, NULL);
987         }
988         g_hash_table_insert (rgctx_lazy_fetch_trampoline_hash, GUINT_TO_POINTER (offset), ptr);
989         g_assert (offset != -1);
990         g_hash_table_insert (rgctx_lazy_fetch_trampoline_hash_addr, ptr, GUINT_TO_POINTER (offset + 1));
991         mono_trampolines_unlock ();
992
993         if (!inited) {
994                 mono_counters_register ("RGCTX num lazy fetch trampolines",
995                                 MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_trampolines);
996                 inited = TRUE;
997         }
998         num_trampolines++;
999
1000         return ptr;
1001 }
1002
1003 gpointer
1004 mono_create_monitor_enter_trampoline (void)
1005 {
1006         static gpointer code;
1007
1008         if (mono_aot_only) {
1009                 if (!code)
1010                         code = mono_aot_get_named_code ("monitor_enter_trampoline");
1011                 return code;
1012         }
1013
1014 #ifdef MONO_ARCH_MONITOR_OBJECT_REG
1015         mono_trampolines_lock ();
1016
1017         if (!code)
1018                 code = mono_arch_create_monitor_enter_trampoline ();
1019
1020         mono_trampolines_unlock ();
1021 #else
1022         code = NULL;
1023         g_assert_not_reached ();
1024 #endif
1025
1026         return code;
1027 }
1028
1029 gpointer
1030 mono_create_monitor_exit_trampoline (void)
1031 {
1032         static gpointer code;
1033
1034         if (mono_aot_only) {
1035                 if (!code)
1036                         code = mono_aot_get_named_code ("monitor_exit_trampoline");
1037                 return code;
1038         }
1039
1040 #ifdef MONO_ARCH_MONITOR_OBJECT_REG
1041         mono_trampolines_lock ();
1042
1043         if (!code)
1044                 code = mono_arch_create_monitor_exit_trampoline ();
1045
1046         mono_trampolines_unlock ();
1047 #else
1048         code = NULL;
1049         g_assert_not_reached ();
1050 #endif
1051         return code;
1052 }
1053
1054 MonoVTable*
1055 mono_find_class_init_trampoline_by_addr (gconstpointer addr)
1056 {
1057         MonoVTable *res;
1058
1059         mono_trampolines_lock ();
1060         if (class_init_hash_addr)
1061                 res = g_hash_table_lookup (class_init_hash_addr, addr);
1062         else
1063                 res = NULL;
1064         mono_trampolines_unlock ();
1065         return res;
1066 }
1067
1068 MonoClass*
1069 mono_find_delegate_trampoline_by_addr (gconstpointer addr)
1070 {
1071         MonoClass *res;
1072
1073         mono_trampolines_lock ();
1074         if (delegate_trampoline_hash_addr)
1075                 res = g_hash_table_lookup (delegate_trampoline_hash_addr, addr);
1076         else
1077                 res = NULL;
1078         mono_trampolines_unlock ();
1079         return res;
1080 }
1081
1082 guint32
1083 mono_find_rgctx_lazy_fetch_trampoline_by_addr (gconstpointer addr)
1084 {
1085         int offset;
1086
1087         mono_trampolines_lock ();
1088         if (rgctx_lazy_fetch_trampoline_hash_addr) {
1089                 /* We store the real offset + 1 so we can detect when the lookup fails */
1090                 offset = GPOINTER_TO_INT (g_hash_table_lookup (rgctx_lazy_fetch_trampoline_hash_addr, addr));
1091                 if (offset)
1092                         offset -= 1;
1093                 else
1094                         offset = -1;
1095         } else {
1096                 offset = -1;
1097         }
1098         mono_trampolines_unlock ();
1099         return offset;
1100 }