2005-03-20 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mono / mini / tramp-x86.c
1 /*
2  * tramp-x86.c: JIT trampoline code for x86
3  *
4  * Authors:
5  *   Dietmar Maurer (dietmar@ximian.com)
6  *
7  * (C) 2001 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12
13 #include <mono/metadata/appdomain.h>
14 #include <mono/metadata/metadata-internals.h>
15 #include <mono/metadata/marshal.h>
16 #include <mono/metadata/tabledefs.h>
17 #include <mono/arch/x86/x86-codegen.h>
18 #include <mono/metadata/mono-debug-debugger.h>
19
20 #ifdef HAVE_VALGRIND_MEMCHECK_H
21 #include <valgrind/memcheck.h>
22 #endif
23
24 #include "mini.h"
25 #include "mini-x86.h"
26
27 /*
28  * get_unbox_trampoline:
29  * @m: method pointer
30  * @addr: pointer to native code for @m
31  *
32  * when value type methods are called through the vtable we need to unbox the
33  * this argument. This method returns a pointer to a trampoline which does
34  * unboxing before calling the method
35  */
36 static gpointer
37 get_unbox_trampoline (MonoMethod *m, gpointer addr)
38 {
39         guint8 *code, *start;
40         int this_pos = 4;
41         MonoDomain *domain = mono_domain_get ();
42
43         if (!mono_method_signature (m)->ret->byref && MONO_TYPE_ISSTRUCT (mono_method_signature (m)->ret))
44                 this_pos = 8;
45             
46         mono_domain_lock (domain);
47         start = code = mono_code_manager_reserve (domain->code_mp, 16);
48         mono_domain_unlock (domain);
49
50         x86_alu_membase_imm (code, X86_ADD, X86_ESP, this_pos, sizeof (MonoObject));
51         x86_jump_code (code, addr);
52         g_assert ((code - start) < 16);
53
54         return start;
55 }
56
57 /**
58  * x86_magic_trampoline:
59  * @eax: saved x86 register 
60  * @ecx: saved x86 register 
61  * @edx: saved x86 register 
62  * @esi: saved x86 register 
63  * @edi: saved x86 register 
64  * @ebx: saved x86 register
65  * @code: pointer into caller code
66  * @method: the method to translate
67  *
68  * This method is called by the trampoline functions for virtual
69  * methods. It inspects the caller code to find the address of the
70  * vtable slot, then calls the JIT compiler and writes the address
71  * of the compiled method back to the vtable. All virtual methods 
72  * are called with: x86_call_membase (inst, basereg, disp). We always
73  * use 32 bit displacement to ensure that the length of the call 
74  * instruction is 6 bytes. We need to get the value of the basereg 
75  * and the constant displacement.
76  */
77 static gpointer
78 x86_magic_trampoline (int eax, int ecx, int edx, int esi, int edi, 
79                       int ebx, guint8 *code, MonoMethod *m)
80 {
81         gpointer addr;
82         gpointer *vtable_slot;
83         int regs [X86_NREG];
84
85         addr = mono_compile_method (m);
86         g_assert (addr);
87
88         /* the method was jumped to */
89         if (!code)
90                 return addr;
91
92         regs [X86_EAX] = eax;
93         regs [X86_ECX] = ecx;
94         regs [X86_EDX] = edx;
95         regs [X86_ESI] = esi;
96         regs [X86_EDI] = edi;
97         regs [X86_EBX] = ebx;
98
99         vtable_slot = mono_arch_get_vcall_slot_addr (code, (gpointer*)regs);
100         if (!vtable_slot) {
101                 /* go to the start of the call instruction
102                  *
103                  * address_byte = (m << 6) | (o << 3) | reg
104                  * call opcode: 0xff address_byte displacement
105                  * 0xff m=1,o=2 imm8
106                  * 0xff m=2,o=2 imm32
107                  */
108                 code -= 6;
109                 if ((code [1] == 0xe8)) {
110                         if (!mono_running_on_valgrind ()) {
111                                 MonoJitInfo *ji = 
112                                         mono_jit_info_table_find (mono_domain_get (), code);
113                                 MonoJitInfo *target_ji = 
114                                         mono_jit_info_table_find (mono_domain_get (), addr);
115
116                                 if (mono_method_same_domain (ji, target_ji)) {
117                                         InterlockedExchange ((gint32*)(code + 2), (guint)addr - ((guint)code + 1) - 5);
118
119 #ifdef HAVE_VALGRIND_MEMCHECK_H
120                                         /* Tell valgrind to recompile the patched code */
121                                         //VALGRIND_DISCARD_TRANSLATIONS (code + 2, code + 6);
122 #endif
123                                 }
124                         }
125                         return addr;
126                 } else {
127                         printf ("Invalid trampoline sequence: %x %x %x %x %x %x %x\n", code [0], code [1], code [2], code [3],
128                                 code [4], code [5], code [6]);
129                         g_assert_not_reached ();
130                 }
131         }
132
133         if (m->klass->valuetype && !mono_aot_is_got_entry (code, (guint8*)vtable_slot))
134                 addr = get_unbox_trampoline (m, addr);
135
136         if (mono_aot_is_got_entry (code, (guint8*)vtable_slot) || mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot))
137                 *vtable_slot = addr;
138
139         return addr;
140 }
141
142 /*
143  * x86_aot_trampoline:
144  *
145  *   This trampoline handles calls made from AOT code. We try to bypass the 
146  * normal JIT compilation logic to avoid loading the metadata for the method.
147  */
148 static gpointer
149 x86_aot_trampoline (int eax, int ecx, int edx, int esi, int edi, 
150                                         int ebx, guint8 *code, guint8 *token_info)
151 {
152         MonoImage *image;
153         guint32 token;
154         MonoMethod *method;
155         gpointer addr;
156         gpointer *vtable_slot;
157         int regs [X86_NREG];
158
159         image = *(gpointer*)token_info;
160         token_info += sizeof (gpointer);
161         token = *(guint32*)token_info;
162
163         /* Later we could avoid allocating the MonoMethod */
164         method = mono_get_method (image, token, NULL);
165         g_assert (method);
166
167         if (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
168                 method = mono_marshal_get_synchronized_wrapper (method);
169
170         addr = mono_compile_method (method);
171         g_assert (addr);
172
173         regs [X86_EAX] = eax;
174         regs [X86_ECX] = ecx;
175         regs [X86_EDX] = edx;
176         regs [X86_ESI] = esi;
177         regs [X86_EDI] = edi;
178         regs [X86_EBX] = ebx;
179
180         vtable_slot = mono_arch_get_vcall_slot_addr (code, (gpointer*)regs);
181         g_assert (vtable_slot);
182
183         if (method->klass->valuetype)
184                 addr = get_unbox_trampoline (method, addr);
185
186         if (mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot))
187                 *vtable_slot = addr;
188
189         return addr;
190 }       
191
192 /**
193  * x86_class_init_trampoline:
194  * @eax: saved x86 register 
195  * @ecx: saved x86 register 
196  * @edx: saved x86 register 
197  * @esi: saved x86 register 
198  * @edi: saved x86 register 
199  * @ebx: saved x86 register
200  * @code: pointer into caller code
201  * @vtable: the type to initialize
202  *
203  * This method calls mono_runtime_class_init () to run the static constructor
204  * for the type, then patches the caller code so it is not called again.
205  */
206 static void
207 x86_class_init_trampoline (int eax, int ecx, int edx, int esi, int edi, 
208                                                    int ebx, guint8 *code, MonoVTable *vtable)
209 {
210         mono_runtime_class_init (vtable);
211
212         code -= 5;
213         if (code [0] == 0xe8) {
214                 if (!mono_running_on_valgrind ()) {
215                         guint32 ops;
216                         /*
217                          * Thread safe code patching using the algorithm from the paper
218                          * 'Practicing JUDO: Java Under Dynamic Optimizations'
219                          */
220                         /* 
221                          * First atomically change the the first 2 bytes of the call to a
222                          * spinning jump.
223                          */
224                         ops = 0xfeeb;
225                         InterlockedExchange ((gint32*)code, ops);
226
227                         /* Then change the other bytes to a nop */
228                         code [2] = 0x90;
229                         code [3] = 0x90;
230                         code [4] = 0x90;
231
232                         /* Then atomically change the first 4 bytes to a nop as well */
233                         ops = 0x90909090;
234                         InterlockedExchange ((guint32*)code, ops);
235
236 #ifdef HAVE_VALGRIND_MEMCHECK_H
237                         /* FIXME: the calltree skin trips on the self modifying code above */
238
239                         /* Tell valgrind to recompile the patched code */
240                         //VALGRIND_DISCARD_TRANSLATIONS (code, code + 8);
241 #endif
242                 }
243         } else if (code [0] == 0x90 || code [0] == 0xeb) {
244                 /* Already changed by another thread */
245                 ;
246         } else if ((code [-1] == 0xff) && (x86_modrm_reg (code [0]) == 0x2)) {
247                 /* call *<OFFSET>(<REG>) -> Call made from AOT code */
248                 /* FIXME: Patch up the trampoline */
249                 ;
250         } else {
251                         printf ("Invalid trampoline sequence: %x %x %x %x %x %x %x\n", code [0], code [1], code [2], code [3],
252                                 code [4], code [5], code [6]);
253                         g_assert_not_reached ();
254                 }
255 }
256
257 guchar*
258 mono_arch_create_trampoline_code (MonoTrampolineType tramp_type)
259 {
260         guint8 *buf, *code;
261
262         code = buf = mono_global_codeman_reserve (256);
263
264         /* save caller save regs because we need to do a call */ 
265         x86_push_reg (buf, X86_EDX);
266         x86_push_reg (buf, X86_EAX);
267         x86_push_reg (buf, X86_ECX);
268
269         /* save LMF begin */
270
271         /* save the IP (caller ip) */
272         if (tramp_type == MONO_TRAMPOLINE_JUMP)
273                 x86_push_imm (buf, 0);
274         else
275                 x86_push_membase (buf, X86_ESP, 16);
276
277         x86_push_reg (buf, X86_EBP);
278         x86_push_reg (buf, X86_ESI);
279         x86_push_reg (buf, X86_EDI);
280         x86_push_reg (buf, X86_EBX);
281
282         /* save method info */
283         x86_push_membase (buf, X86_ESP, 32);
284         /* get the address of lmf for the current thread */
285         x86_call_code (buf, mono_get_lmf_addr);
286         /* push lmf */
287         x86_push_reg (buf, X86_EAX); 
288         /* push *lfm (previous_lmf) */
289         x86_push_membase (buf, X86_EAX, 0);
290         /* *(lmf) = ESP */
291         x86_mov_membase_reg (buf, X86_EAX, 0, X86_ESP, 4);
292         /* save LFM end */
293
294         /* push the method info */
295         x86_push_membase (buf, X86_ESP, 44);
296         /* push the return address onto the stack */
297         if (tramp_type == MONO_TRAMPOLINE_JUMP)
298                 x86_push_imm (buf, 0);
299         else
300                 x86_push_membase (buf, X86_ESP, 52);
301
302         /* save all register values */
303         x86_push_reg (buf, X86_EBX);
304         x86_push_reg (buf, X86_EDI);
305         x86_push_reg (buf, X86_ESI);
306         x86_push_membase (buf, X86_ESP, 64); /* EDX */
307         x86_push_membase (buf, X86_ESP, 64); /* ECX */
308         x86_push_membase (buf, X86_ESP, 64); /* EAX */
309
310         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
311                 x86_call_code (buf, x86_class_init_trampoline);
312         else if (tramp_type == MONO_TRAMPOLINE_AOT)
313                 x86_call_code (buf, x86_aot_trampoline);
314         else
315                 x86_call_code (buf, x86_magic_trampoline);
316         x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 8*4);
317
318         /* restore LMF start */
319         /* ebx = previous_lmf */
320         x86_pop_reg (buf, X86_EBX);
321         /* edi = lmf */
322         x86_pop_reg (buf, X86_EDI);
323         /* *(lmf) = previous_lmf */
324         x86_mov_membase_reg (buf, X86_EDI, 0, X86_EBX, 4);
325         /* discard method info */
326         x86_pop_reg (buf, X86_ESI);
327         /* restore caller saved regs */
328         x86_pop_reg (buf, X86_EBX);
329         x86_pop_reg (buf, X86_EDI);
330         x86_pop_reg (buf, X86_ESI);
331         x86_pop_reg (buf, X86_EBP);
332
333         /* discard save IP */
334         x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 4);             
335         /* restore LMF end */
336
337         x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 16);
338
339         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
340                 x86_ret (buf);
341         else
342                 /* call the compiled method */
343                 x86_jump_reg (buf, X86_EAX);
344
345         g_assert ((buf - code) <= 256);
346
347         return code;
348 }
349
350 #define TRAMPOLINE_SIZE 10
351
352 static MonoJitInfo*
353 create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain)
354 {
355         guint8 *code, *buf, *tramp;
356         MonoJitInfo *ji;
357         
358         tramp = mono_get_trampoline_code (tramp_type);
359
360         mono_domain_lock (domain);
361         code = buf = mono_code_manager_reserve (domain->code_mp, TRAMPOLINE_SIZE);
362         mono_domain_unlock (domain);
363
364         x86_push_imm (buf, arg1);
365         x86_jump_code (buf, tramp);
366         g_assert ((buf - code) <= TRAMPOLINE_SIZE);
367
368         ji = g_new0 (MonoJitInfo, 1);
369         ji->code_start = code;
370         ji->code_size = buf - code;
371
372         mono_arch_flush_icache (ji->code_start, ji->code_size);
373
374         mono_jit_stats.method_trampolines++;
375
376         return ji;
377 }
378
379 MonoJitInfo*
380 mono_arch_create_jump_trampoline (MonoMethod *method)
381 {
382         MonoJitInfo *ji = create_specific_trampoline (method, MONO_TRAMPOLINE_JUMP, mono_domain_get ());
383
384         ji->method = method;
385         return ji;
386 }
387
388 /**
389  * mono_arch_create_jit_trampoline:
390  * @method: pointer to the method info
391  *
392  * Creates a trampoline function for virtual methods. If the created
393  * code is called it first starts JIT compilation of method,
394  * and then calls the newly created method. I also replaces the
395  * corresponding vtable entry (see x86_magic_trampoline).
396  * 
397  * Returns: a pointer to the newly created code 
398  */
399 gpointer
400 mono_arch_create_jit_trampoline (MonoMethod *method)
401 {
402         MonoJitInfo *ji;
403         gpointer code_start;
404
405         ji = create_specific_trampoline (method, MONO_TRAMPOLINE_GENERIC, mono_domain_get ());
406         code_start = ji->code_start;
407         g_free (ji);
408
409         return code_start;
410 }
411
412 gpointer
413 mono_arch_create_jit_trampoline_from_token (MonoImage *image, guint32 token)
414 {
415         MonoDomain *domain = mono_domain_get ();
416         MonoJitInfo *ji;
417         gpointer code_start;
418         guint8 *buf, *start;
419
420         mono_domain_lock (domain);
421         buf = start = mono_code_manager_reserve (domain->code_mp, 2 * sizeof (gpointer));
422         mono_domain_unlock (domain);
423
424         *(gpointer*)buf = image;
425         buf += sizeof (gpointer);
426         *(guint32*)buf = token;
427
428         ji = create_specific_trampoline (start, MONO_TRAMPOLINE_AOT, domain);
429         code_start = ji->code_start;
430         g_free (ji);
431
432         return code_start;
433 }
434
435 /**
436  * mono_arch_create_class_init_trampoline:
437  *  @vtable: the type to initialize
438  *
439  * Creates a trampoline function to run a type initializer. 
440  * If the trampoline is called, it calls mono_runtime_class_init with the
441  * given vtable, then patches the caller code so it does not get called any
442  * more.
443  * 
444  * Returns: a pointer to the newly created code 
445  */
446 gpointer
447 mono_arch_create_class_init_trampoline (MonoVTable *vtable)
448 {
449         MonoJitInfo *ji;
450         gpointer code;
451
452         ji = create_specific_trampoline (vtable, MONO_TRAMPOLINE_CLASS_INIT, vtable->domain);
453         code = ji->code_start;
454         g_free (ji);
455
456         return code;
457 }
458
459 void
460 mono_arch_invalidate_method (MonoJitInfo *ji, void *func, gpointer func_arg)
461 {
462         /* FIXME: This is not thread safe */
463         guint8 *code = ji->code_start;
464
465         x86_push_imm (code, func_arg);
466         x86_call_code (code, (guint8*)func);
467 }
468
469 /*
470  * This method is only called when running in the Mono Debugger.
471  */
472 gpointer
473 mono_debugger_create_notification_function (gpointer *notification_address)
474 {
475         guint8 *ptr, *buf;
476
477         ptr = buf = mono_global_codeman_reserve (16);
478
479         x86_breakpoint (buf);
480         if (notification_address)
481                 *notification_address = buf;
482         x86_ret (buf);
483
484         return ptr;
485 }