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