svn path=/trunk/mcs/; revision=81777
[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
16 #ifdef MONO_ARCH_HAVE_IMT
17
18 static gpointer*
19 mono_convert_imt_slot_to_vtable_slot (gpointer* slot, gpointer *regs, MonoMethod *method) {
20         MonoObject *this_argument = mono_arch_find_this_argument (regs, method);
21         MonoVTable *vt = this_argument->vtable;
22         int displacement = slot - ((gpointer*)vt);
23         
24         if (displacement > 0) {
25                 /* slot is in the vtable, not in the IMT */
26 #if DEBUG_IMT
27                 printf ("mono_convert_imt_slot_to_vtable_slot: slot %p is in the vtable, not in the IMT\n", slot);
28 #endif
29                 return slot;
30         } else {
31                 MonoMethod *imt_method = mono_arch_find_imt_method (regs);
32                 int interface_offset = mono_class_interface_offset (vt->klass, imt_method->klass);
33                 int imt_slot = MONO_IMT_SIZE + displacement;
34
35 #if DEBUG_IMT
36                 printf ("mono_convert_imt_slot_to_vtable_slot: method = %s.%s.%s, imt_method = %s.%s.%s\n",
37                                 method->klass->name_space, method->klass->name, method->name, 
38                                 imt_method->klass->name_space, imt_method->klass->name, imt_method->name);
39 #endif
40                 g_assert (imt_slot < MONO_IMT_SIZE);
41                 if (vt->imt_collisions_bitmap & (1 << imt_slot)) {
42                         int vtable_offset = interface_offset + imt_method->slot;
43                         gpointer *vtable_slot = & (vt->vtable [vtable_offset]);
44 #if DEBUG_IMT
45                         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);
46 #endif
47                         g_assert (vtable_offset >= 0);
48                         return vtable_slot;
49                 } else {
50 #if DEBUG_IMT
51                         printf ("mono_convert_imt_slot_to_vtable_slot: slot %p[%d] is in the IMT, but not colliding\n", slot, imt_slot);
52 #endif
53                         return slot;
54                 }
55         }
56 }
57 #endif
58
59 /**
60  * mono_magic_trampoline:
61  *
62  *   This trampoline handles calls from JITted code.
63  */
64 gpointer
65 mono_magic_trampoline (gssize *regs, guint8 *code, MonoMethod *m, guint8* tramp)
66 {
67         gpointer addr;
68         gpointer *vtable_slot;
69
70         addr = mono_compile_method (m);
71         g_assert (addr);
72
73         /* the method was jumped to */
74         if (!code)
75                 return addr;
76
77         vtable_slot = mono_arch_get_vcall_slot_addr (code, (gpointer*)regs);
78
79         if (vtable_slot) {
80                 if (m->klass->valuetype)
81                         addr = mono_arch_get_unbox_trampoline (m, addr);
82
83                 g_assert (*vtable_slot);
84
85                 if (mono_aot_is_got_entry (code, (guint8*)vtable_slot) || mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot)) {
86 #ifdef MONO_ARCH_HAVE_IMT
87                         vtable_slot = mono_convert_imt_slot_to_vtable_slot (vtable_slot, (gpointer*)regs, m);
88 #endif
89                         *vtable_slot = mono_get_addr_from_ftnptr (addr);
90                 }
91         }
92         else {
93                 guint8 *plt_entry = mono_aot_get_plt_entry (code);
94
95                 /* Patch calling code */
96                 if (plt_entry) {
97                         mono_arch_patch_plt_entry (plt_entry, addr);
98                 } else {
99                         MonoJitInfo *ji = 
100                                 mono_jit_info_table_find (mono_domain_get (), (char*)code);
101                         MonoJitInfo *target_ji = 
102                                 mono_jit_info_table_find (mono_domain_get (), mono_get_addr_from_ftnptr (addr));
103
104                         if (mono_method_same_domain (ji, target_ji))
105                                 mono_arch_patch_callsite (code, addr);
106                 }
107         }
108
109         return addr;
110 }
111
112 /*
113  * mono_aot_trampoline:
114  *
115  *   This trampoline handles calls made from AOT code. We try to bypass the 
116  * normal JIT compilation logic to avoid loading the metadata for the method.
117  */
118 #ifdef MONO_ARCH_HAVE_CREATE_TRAMPOLINE_FROM_TOKEN
119 gpointer
120 mono_aot_trampoline (gssize *regs, guint8 *code, guint8 *token_info, 
121                                          guint8* tramp)
122 {
123         MonoImage *image;
124         guint32 token;
125         MonoMethod *method = NULL;
126         gpointer addr;
127         gpointer *vtable_slot;
128         gboolean is_got_entry;
129
130         image = *(gpointer*)(gpointer)token_info;
131         token_info += sizeof (gpointer);
132         token = *(guint32*)(gpointer)token_info;
133
134         addr = mono_aot_get_method_from_token (mono_domain_get (), image, token);
135         if (!addr) {
136                 method = mono_get_method (image, token, NULL);
137                 g_assert (method);
138
139                 //printf ("F: %s\n", mono_method_full_name (method, TRUE));
140
141                 if (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
142                         method = mono_marshal_get_synchronized_wrapper (method);
143
144                 addr = mono_compile_method (method);
145                 g_assert (addr);
146         }
147
148         vtable_slot = mono_arch_get_vcall_slot_addr (code, (gpointer*)regs);
149
150         if (vtable_slot) {
151                 is_got_entry = mono_aot_is_got_entry (code, (guint8*)vtable_slot);
152
153                 if (!is_got_entry) {
154                         if (!method)
155                                 method = mono_get_method (image, token, NULL);
156                         if (method->klass->valuetype)
157                                 addr = mono_arch_get_unbox_trampoline (method, addr);
158                 }
159         } else {
160                 /* This is a normal call through a PLT entry */
161                 guint8 *plt_entry = mono_aot_get_plt_entry (code);
162
163                 g_assert (plt_entry);
164
165                 mono_arch_patch_plt_entry (plt_entry, addr);
166
167                 is_got_entry = FALSE;
168         }
169
170         /*
171          * Since AOT code is only used in the root domain, 
172          * mono_domain_get () != mono_get_root_domain () means the calling method
173          * is AppDomain:InvokeInDomain, so this is the same check as in 
174          * mono_method_same_domain () but without loading the metadata for the method.
175          */
176         if ((is_got_entry && (mono_domain_get () == mono_get_root_domain ())) || mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot))
177                 *vtable_slot = addr;
178
179         return addr;
180 }
181
182 /*
183  * mono_aot_plt_trampoline:
184  *
185  *   This trampoline handles calls made from AOT code through the PLT table.
186  */
187 gpointer
188 mono_aot_plt_trampoline (gssize *regs, guint8 *code, guint8 *aot_module, 
189                                                  guint8* tramp)
190 {
191 #ifdef MONO_ARCH_AOT_PLT_OFFSET_REG
192         guint32 plt_info_offset = regs [MONO_ARCH_AOT_PLT_OFFSET_REG];
193 #else
194         guint32 plt_info_offset = -1;
195 #endif
196
197         return mono_aot_plt_resolve (aot_module, plt_info_offset, code);
198 }
199 #endif
200
201 /**
202  * mono_class_init_trampoline:
203  *
204  * This method calls mono_runtime_class_init () to run the static constructor
205  * for the type, then patches the caller code so it is not called again.
206  */
207 void
208 mono_class_init_trampoline (gssize *regs, guint8 *code, MonoVTable *vtable, guint8 *tramp)
209 {
210         guint8 *plt_entry = mono_aot_get_plt_entry (code);
211
212         mono_runtime_class_init (vtable);
213
214         if (!mono_running_on_valgrind ()) {
215                 if (plt_entry) {
216                         mono_arch_nullify_plt_entry (plt_entry);
217                 } else {
218                         mono_arch_nullify_class_init_trampoline (code, regs);
219                 }
220         }
221 }
222
223 #ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
224
225 /**
226  * mono_delegate_trampoline:
227  *
228  *   This trampoline handles calls made to Delegate:Invoke ().
229  */
230 gpointer
231 mono_delegate_trampoline (gssize *regs, guint8 *code, MonoClass *klass, guint8* tramp)
232 {
233         MonoDomain *domain = mono_domain_get ();
234         MonoDelegate *delegate;
235         MonoJitInfo *ji;
236         gpointer iter;
237         MonoMethod *invoke;
238         gboolean multicast;
239
240         /* Find the Invoke method */
241         iter = NULL;
242         while ((invoke = mono_class_get_methods (klass, &iter))) {
243                 if (!strcmp (invoke->name, "Invoke"))
244                         break;
245         }
246         g_assert (invoke);
247
248         /* Obtain the delegate object according to the calling convention */
249
250         delegate = mono_arch_get_this_arg_from_call (mono_method_signature (invoke), regs, code);
251
252         /* 
253          * If the called address is a trampoline, replace it with the compiled method so
254          * further calls don't have to go through the trampoline.
255          */
256         ji = mono_jit_info_table_find (domain, mono_get_addr_from_ftnptr (delegate->method_ptr));
257         if (ji)
258                 delegate->method_ptr = mono_compile_method (ji->method);
259
260         multicast = ((MonoMulticastDelegate*)delegate)->prev != NULL;
261         if (!multicast) {
262                 guint8* code;
263                 GHashTable *cache;
264
265                 mono_domain_lock (domain);
266                 if (delegate->target != NULL) {
267                         if (!domain->delegate_invoke_impl_with_target_hash)
268                                 domain->delegate_invoke_impl_with_target_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
269                         cache = domain->delegate_invoke_impl_with_target_hash;
270                 } else {
271                         if (!domain->delegate_invoke_impl_no_target_hash)
272                                 domain->delegate_invoke_impl_no_target_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
273                         cache = domain->delegate_invoke_impl_no_target_hash;
274                 }
275                 code = g_hash_table_lookup (cache, mono_method_signature (invoke));
276                 mono_domain_unlock (domain);
277                 if (code) {
278                         delegate->invoke_impl = code;
279                         return code;
280                 }
281
282                 code = mono_arch_get_delegate_invoke_impl (mono_method_signature (invoke), delegate->target != NULL);
283
284                 if (code) {
285                         mono_domain_lock (domain);
286                         g_hash_table_insert (cache, mono_method_signature (invoke), code);
287                         mono_domain_unlock (domain);
288
289                         delegate->invoke_impl = code;
290                         return code;
291                 }
292         }
293
294         /* The general, unoptimized case */
295         delegate->invoke_impl = mono_compile_method (mono_marshal_get_delegate_invoke (invoke));
296         return delegate->invoke_impl;
297 }
298
299 #endif
300
301