2007-11-28 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
10 #ifdef HAVE_VALGRIND_MEMCHECK_H
11 #include <valgrind/memcheck.h>
12 #endif
13
14 #include "mini.h"
15 #include "debug-mini.h"
16
17 #ifdef MONO_ARCH_HAVE_IMT
18
19 static gpointer*
20 mono_convert_imt_slot_to_vtable_slot (gpointer* slot, gpointer *regs, guint8 *code, MonoMethod *method, MonoMethod **impl_method)
21 {
22         MonoObject *this_argument = mono_arch_find_this_argument (regs, method);
23         MonoVTable *vt = this_argument->vtable;
24         int displacement = slot - ((gpointer*)vt);
25         
26         if (displacement > 0) {
27                 /* slot is in the vtable, not in the IMT */
28 #if DEBUG_IMT
29                 printf ("mono_convert_imt_slot_to_vtable_slot: slot %p is in the vtable, not in the IMT\n", slot);
30 #endif
31                 return slot;
32         } else {
33                 MonoMethod *imt_method = mono_arch_find_imt_method (regs, code);
34                 int interface_offset;
35                 int imt_slot = MONO_IMT_SIZE + displacement;
36
37                 mono_class_setup_vtable (vt->klass);
38                 interface_offset = mono_class_interface_offset (vt->klass, imt_method->klass);
39
40                 if (interface_offset < 0) {
41                         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));
42                         g_assert_not_reached ();
43                 }
44                 mono_vtable_build_imt_slot (vt, mono_method_get_imt_slot (imt_method));
45
46                 if (impl_method)
47                         *impl_method = vt->klass->vtable [interface_offset + imt_method->slot];
48 #if DEBUG_IMT
49                 printf ("mono_convert_imt_slot_to_vtable_slot: method = %s.%s.%s, imt_method = %s.%s.%s\n",
50                                 method->klass->name_space, method->klass->name, method->name, 
51                                 imt_method->klass->name_space, imt_method->klass->name, imt_method->name);
52 #endif
53                 g_assert (imt_slot < MONO_IMT_SIZE);
54                 if (vt->imt_collisions_bitmap & (1 << imt_slot)) {
55                         int vtable_offset = interface_offset + imt_method->slot;
56                         gpointer *vtable_slot = & (vt->vtable [vtable_offset]);
57 #if DEBUG_IMT
58                         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);
59 #endif
60                         return vtable_slot;
61                 } else {
62 #if DEBUG_IMT
63                         printf ("mono_convert_imt_slot_to_vtable_slot: slot %p[%d] is in the IMT, but not colliding\n", slot, imt_slot);
64 #endif
65                         return slot;
66                 }
67         }
68 }
69 #endif
70
71 /**
72  * mono_magic_trampoline:
73  *
74  *   This trampoline handles calls from JITted code.
75  */
76 gpointer
77 mono_magic_trampoline (gssize *regs, guint8 *code, MonoMethod *m, guint8* tramp)
78 {
79         gpointer addr;
80         gpointer *vtable_slot;
81
82 #if MONO_ARCH_COMMON_VTABLE_TRAMPOLINE
83         if (m == MONO_FAKE_VTABLE_METHOD) {
84                 int displacement;
85                 MonoVTable *vt = mono_arch_get_vcall_slot (code, (gpointer*)regs, &displacement);
86                 g_assert (vt);
87                 if (displacement > 0) {
88                         displacement -= G_STRUCT_OFFSET (MonoVTable, vtable);
89                         g_assert (displacement >= 0);
90                         displacement /= sizeof (gpointer);
91
92                         /* Avoid loading metadata or creating a generic vtable if possible */
93                         addr = mono_aot_get_method_from_vt_slot (mono_domain_get (), vt, displacement);
94                         if (addr && !vt->klass->valuetype) {
95                                 vtable_slot = mono_arch_get_vcall_slot_addr (code, (gpointer*)regs);
96                                 if (mono_aot_is_got_entry (code, (guint8*)vtable_slot) || mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot)) {
97                                         *vtable_slot = mono_get_addr_from_ftnptr (addr);
98                                 }
99
100                                 return addr;
101                         }
102
103                         mono_class_setup_vtable (vt->klass);
104                         m = vt->klass->vtable [displacement];
105                         if (m->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
106                                 m = mono_marshal_get_synchronized_wrapper (m);
107                         /*g_print ("%s with disp %d: %s at %p\n", vt->klass->name, displacement, m->name, code);*/
108                 } else {
109                         /* We got here from an interface method: redirect to IMT handling */
110                         m = MONO_FAKE_IMT_METHOD;
111                         /*g_print ("vtable with disp %d at %p\n", displacement, code);*/
112                 }
113         }
114 #endif
115         /* this is the IMT trampoline */
116 #ifdef MONO_ARCH_HAVE_IMT
117         if (m == MONO_FAKE_IMT_METHOD) {
118                 MonoMethod *impl_method;
119                 /* we get the interface method because mono_convert_imt_slot_to_vtable_slot ()
120                  * needs the signature to be able to find the this argument
121                  */
122                 m = mono_arch_find_imt_method ((gpointer*)regs, code);
123                 vtable_slot = mono_arch_get_vcall_slot_addr (code, (gpointer*)regs);
124                 g_assert (vtable_slot);
125                 vtable_slot = mono_convert_imt_slot_to_vtable_slot (vtable_slot, (gpointer*)regs, code, m, &impl_method);
126                 /* mono_convert_imt_slot_to_vtable_slot () also gives us the method that is supposed
127                  * to be called, so we compile it and go ahead as usual.
128                  */
129                 /*g_print ("imt found method %p (%s) at %p\n", impl_method, impl_method->name, code);*/
130                 m = impl_method;
131         }
132 #endif
133
134         addr = mono_compile_method (m);
135         g_assert (addr);
136
137         mono_debugger_trampoline_compiled (m, addr);
138
139         /* the method was jumped to */
140         if (!code)
141                 return addr;
142
143         vtable_slot = mono_arch_get_vcall_slot_addr (code, (gpointer*)regs);
144
145         if (vtable_slot) {
146                 if (m->klass->valuetype)
147                         addr = mono_arch_get_unbox_trampoline (m, addr);
148
149                 g_assert (*vtable_slot);
150
151                 if (mono_aot_is_got_entry (code, (guint8*)vtable_slot) || mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot)) {
152 #ifdef MONO_ARCH_HAVE_IMT
153                         vtable_slot = mono_convert_imt_slot_to_vtable_slot (vtable_slot, (gpointer*)regs, code, m, NULL);
154 #endif
155                         *vtable_slot = mono_get_addr_from_ftnptr (addr);
156                 }
157         }
158         else {
159                 guint8 *plt_entry = mono_aot_get_plt_entry (code);
160
161                 /* Patch calling code */
162                 if (plt_entry) {
163                         mono_arch_patch_plt_entry (plt_entry, addr);
164                 } else {
165                         MonoJitInfo *ji = 
166                                 mono_jit_info_table_find (mono_domain_get (), (char*)code);
167                         MonoJitInfo *target_ji = 
168                                 mono_jit_info_table_find (mono_domain_get (), mono_get_addr_from_ftnptr (addr));
169
170                         if (mono_method_same_domain (ji, target_ji))
171                                 mono_arch_patch_callsite (code, addr);
172                 }
173         }
174
175         return addr;
176 }
177
178 /*
179  * mono_aot_trampoline:
180  *
181  *   This trampoline handles calls made from AOT code. We try to bypass the 
182  * normal JIT compilation logic to avoid loading the metadata for the method.
183  */
184 #ifdef MONO_ARCH_HAVE_CREATE_TRAMPOLINE_FROM_TOKEN
185 gpointer
186 mono_aot_trampoline (gssize *regs, guint8 *code, guint8 *token_info, 
187                                          guint8* tramp)
188 {
189         MonoImage *image;
190         guint32 token;
191         MonoMethod *method = NULL;
192         gpointer addr;
193         gpointer *vtable_slot;
194         gboolean is_got_entry;
195
196         image = *(gpointer*)(gpointer)token_info;
197         token_info += sizeof (gpointer);
198         token = *(guint32*)(gpointer)token_info;
199
200         addr = mono_aot_get_method_from_token (mono_domain_get (), image, token);
201         if (!addr) {
202                 method = mono_get_method (image, token, NULL);
203                 g_assert (method);
204
205                 //printf ("F: %s\n", mono_method_full_name (method, TRUE));
206
207                 if (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
208                         method = mono_marshal_get_synchronized_wrapper (method);
209
210                 addr = mono_compile_method (method);
211                 g_assert (addr);
212         }
213
214         vtable_slot = mono_arch_get_vcall_slot_addr (code, (gpointer*)regs);
215
216         if (vtable_slot) {
217                 is_got_entry = mono_aot_is_got_entry (code, (guint8*)vtable_slot);
218
219                 if (!is_got_entry) {
220                         if (!method)
221                                 method = mono_get_method (image, token, NULL);
222                         if (method->klass->valuetype)
223                                 addr = mono_arch_get_unbox_trampoline (method, addr);
224                 }
225         } else {
226                 /* This is a normal call through a PLT entry */
227                 guint8 *plt_entry = mono_aot_get_plt_entry (code);
228
229                 g_assert (plt_entry);
230
231                 mono_arch_patch_plt_entry (plt_entry, addr);
232
233                 is_got_entry = FALSE;
234         }
235
236         /*
237          * Since AOT code is only used in the root domain, 
238          * mono_domain_get () != mono_get_root_domain () means the calling method
239          * is AppDomain:InvokeInDomain, so this is the same check as in 
240          * mono_method_same_domain () but without loading the metadata for the method.
241          */
242         if ((is_got_entry && (mono_domain_get () == mono_get_root_domain ())) || mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot)) {
243 #ifdef MONO_ARCH_HAVE_IMT
244                 if (!method)
245                         method = mono_get_method (image, token, NULL);
246                 vtable_slot = mono_convert_imt_slot_to_vtable_slot (vtable_slot, (gpointer*)regs, code, method, NULL);
247 #endif
248                 *vtable_slot = addr;
249         }
250
251         return addr;
252 }
253
254 /*
255  * mono_aot_plt_trampoline:
256  *
257  *   This trampoline handles calls made from AOT code through the PLT table.
258  */
259 gpointer
260 mono_aot_plt_trampoline (gssize *regs, guint8 *code, guint8 *aot_module, 
261                                                  guint8* tramp)
262 {
263 #ifdef MONO_ARCH_AOT_PLT_OFFSET_REG
264         guint32 plt_info_offset = regs [MONO_ARCH_AOT_PLT_OFFSET_REG];
265 #else
266         guint32 plt_info_offset = -1;
267 #endif
268
269         return mono_aot_plt_resolve (aot_module, plt_info_offset, code);
270 }
271 #endif
272
273 /**
274  * mono_class_init_trampoline:
275  *
276  * This method calls mono_runtime_class_init () to run the static constructor
277  * for the type, then patches the caller code so it is not called again.
278  */
279 void
280 mono_class_init_trampoline (gssize *regs, guint8 *code, MonoVTable *vtable, guint8 *tramp)
281 {
282         guint8 *plt_entry = mono_aot_get_plt_entry (code);
283
284         mono_runtime_class_init (vtable);
285
286         if (!mono_running_on_valgrind ()) {
287                 if (plt_entry) {
288                         mono_arch_nullify_plt_entry (plt_entry);
289                 } else {
290                         mono_arch_nullify_class_init_trampoline (code, regs);
291                 }
292         }
293 }
294
295 #ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
296
297 /**
298  * mono_delegate_trampoline:
299  *
300  *   This trampoline handles calls made to Delegate:Invoke ().
301  */
302 gpointer
303 mono_delegate_trampoline (gssize *regs, guint8 *code, MonoClass *klass, guint8* tramp)
304 {
305         MonoDomain *domain = mono_domain_get ();
306         MonoDelegate *delegate;
307         MonoJitInfo *ji;
308         gpointer iter;
309         MonoMethod *invoke, *m;
310         gboolean multicast, callvirt;
311
312         /* Find the Invoke method */
313         iter = NULL;
314         while ((invoke = mono_class_get_methods (klass, &iter))) {
315                 if (!strcmp (invoke->name, "Invoke"))
316                         break;
317         }
318         g_assert (invoke);
319
320         /* Obtain the delegate object according to the calling convention */
321
322         delegate = mono_arch_get_this_arg_from_call (mono_method_signature (invoke), regs, code);
323
324         /* 
325          * If the called address is a trampoline, replace it with the compiled method so
326          * further calls don't have to go through the trampoline.
327          */
328         ji = mono_jit_info_table_find (domain, mono_get_addr_from_ftnptr (delegate->method_ptr));
329         callvirt = !delegate->target && ji && mono_method_signature (ji->method)->hasthis;
330         if (ji && !callvirt) {
331                 delegate->method_ptr = mono_compile_method (ji->method);
332                 mono_debugger_trampoline_compiled (ji->method, delegate->method_ptr);
333         }
334
335         multicast = ((MonoMulticastDelegate*)delegate)->prev != NULL;
336         if (!multicast && !callvirt) {
337                 code = mono_arch_get_delegate_invoke_impl (mono_method_signature (invoke), delegate->target != NULL);
338
339                 if (code) {
340                         delegate->invoke_impl = code;
341                         return code;
342                 }
343         }
344
345         /* The general, unoptimized case */
346         m = mono_marshal_get_delegate_invoke (invoke, delegate);
347         delegate->invoke_impl = mono_compile_method (m);
348         mono_debugger_trampoline_compiled (m, delegate->invoke_impl);
349
350         return delegate->invoke_impl;
351 }
352
353 #endif
354
355