2005-03-29 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mono / mini / tramp-amd64.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/marshal.h>
15 #include <mono/metadata/tabledefs.h>
16 #include <mono/arch/amd64/amd64-codegen.h>
17 #include <mono/metadata/mono-debug-debugger.h>
18
19 #ifdef HAVE_VALGRIND_MEMCHECK_H
20 #include <valgrind/memcheck.h>
21 #endif
22
23 #include "mini.h"
24 #include "mini-amd64.h"
25
26 /*
27  * get_unbox_trampoline:
28  * @m: method pointer
29  * @addr: pointer to native code for @m
30  *
31  * when value type methods are called through the vtable we need to unbox the
32  * this argument. This method returns a pointer to a trampoline which does
33  * unboxing before calling the method
34  */
35 static gpointer
36 get_unbox_trampoline (MonoMethod *m, gpointer addr)
37 {
38         guint8 *code, *start;
39         int this_reg = AMD64_RDI;
40         MonoDomain *domain = mono_domain_get ();
41
42         if (!mono_method_signature (m)->ret->byref && MONO_TYPE_ISSTRUCT (mono_method_signature (m)->ret))
43                 this_reg = AMD64_RSI;
44
45         mono_domain_lock (domain);
46         start = code = mono_code_manager_reserve (domain->code_mp, 20);
47         mono_domain_unlock (domain);
48
49         amd64_alu_reg_imm (code, X86_ADD, this_reg, sizeof (MonoObject));
50         /* FIXME: Optimize this */
51         amd64_mov_reg_imm (code, AMD64_RAX, addr);
52         amd64_jump_reg (code, AMD64_RAX);
53         g_assert ((code - start) < 20);
54
55         mono_arch_flush_icache (start, code - start);
56
57         return start;
58 }
59
60 /**
61  * amd64_magic_trampoline:
62  */
63 static gpointer
64 amd64_magic_trampoline (long *regs, guint8 *code, MonoMethod *m, guint8* tramp)
65 {
66         gpointer addr;
67         gpointer *vtable_slot;
68
69         addr = mono_compile_method (m);
70         g_assert (addr);
71
72         //printf ("ENTER: %s\n", mono_method_full_name (m, TRUE));
73
74         /* the method was jumped to */
75         if (!code)
76                 /* FIXME: Optimize the case when the call is from a delegate wrapper */
77                 return addr;
78
79         vtable_slot = mono_arch_get_vcall_slot_addr (code, (gpointer*)regs);
80
81         if (vtable_slot) {
82                 if (m->klass->valuetype)
83                         addr = get_unbox_trampoline (m, addr);
84
85                 g_assert (*vtable_slot);
86
87                 if (mono_aot_is_got_entry (code, (guint8*)vtable_slot) || mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot))
88                         *vtable_slot = addr;
89         }
90         else {
91                 /* Patch calling code */
92
93                 if (((code [-13] == 0x49) && (code [-12] == 0xbb)) ||
94                         (code [-5] == 0xe8)) {
95                         MonoJitInfo *ji = 
96                                 mono_jit_info_table_find (mono_domain_get (), code);
97                         MonoJitInfo *target_ji = 
98                                 mono_jit_info_table_find (mono_domain_get (), addr);
99
100                         if (mono_method_same_domain (ji, target_ji)) {
101                                 if (code [-5] != 0xe8)
102                                         InterlockedExchangePointer ((gpointer*)(code - 11), addr);
103                                 else {
104                                         g_assert ((((guint64)(addr)) >> 32) == 0);
105                                         g_assert ((((guint64)(code)) >> 32) == 0);
106                                         InterlockedExchange ((guint32*)(code - 4), ((gint64)addr - (gint64)code));
107                                 }
108                         }
109                 }
110                 else if ((code [-7] == 0x41) && (code [-6] == 0xff) && (code [-5] == 0x15)) {
111                         /* call *<OFFSET>(%rip) */
112                         MonoJitInfo *ji = 
113                                 mono_jit_info_table_find (mono_domain_get (), code);
114                         MonoJitInfo *target_ji = 
115                                 mono_jit_info_table_find (mono_domain_get (), addr);
116
117                         if (mono_method_same_domain (ji, target_ji)) {
118                                 gpointer *got_entry = (gpointer*)((guint8*)code + (*(guint32*)(code - 4)));
119                                 InterlockedExchangePointer (got_entry, addr);
120                         }
121                 }
122         }
123
124         return addr;
125 }
126
127 /*
128  * amd64_aot_trampoline:
129  *
130  *   This trampoline handles calls made from AOT code. We try to bypass the 
131  * normal JIT compilation logic to avoid loading the metadata for the method.
132  */
133 static gpointer
134 amd64_aot_trampoline (long *regs, guint8 *code, guint8 *token_info, 
135                                           guint8* tramp)
136 {
137         MonoImage *image;
138         guint32 token;
139         MonoMethod *method;
140         gpointer addr;
141         gpointer *vtable_slot;
142
143         image = *(gpointer*)token_info;
144         token_info += sizeof (gpointer);
145         token = *(guint32*)token_info;
146
147         /* Later we could avoid allocating the MonoMethod */
148         method = mono_get_method (image, token, NULL);
149         g_assert (method);
150
151         if (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
152                 method = mono_marshal_get_synchronized_wrapper (method);
153
154         addr = mono_compile_method (method);
155         g_assert (addr);
156
157         vtable_slot = mono_arch_get_vcall_slot_addr (code, (gpointer*)regs);
158         g_assert (vtable_slot);
159
160         if (method->klass->valuetype)
161                 addr = get_unbox_trampoline (method, addr);
162
163         if (mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot))
164                 *vtable_slot = addr;
165
166         return addr;
167 }
168
169 /**
170  * amd64_class_init_trampoline:
171  *
172  * This method calls mono_runtime_class_init () to run the static constructor
173  * for the type, then patches the caller code so it is not called again.
174  */
175 static void
176 amd64_class_init_trampoline (long *regs, guint8 *code, MonoVTable *vtable, guint8 *tramp)
177 {
178         mono_runtime_class_init (vtable);
179
180         code -= 3;
181
182         if ((code [0] == 0x49) && (code [1] == 0xff)) {
183                 if (!mono_running_on_valgrind ()) {
184                         /* amd64_set_reg_template is 10 bytes long */
185                         guint8* buf = code - 10;
186
187                         /* FIXME: Make this thread safe */
188                         /* Padding code suggested by the AMD64 Opt Manual */
189                         buf [0] = 0x66;
190                         buf [1] = 0x66;
191                         buf [2] = 0x66;
192                         buf [3] = 0x90;
193                         buf [4] = 0x66;
194                         buf [5] = 0x66;
195                         buf [6] = 0x66;
196                         buf [7] = 0x90;
197                         buf [8] = 0x66;
198                         buf [9] = 0x66;
199                         buf [10] = 0x90;
200                         buf [11] = 0x66;
201                         buf [12] = 0x90;
202                 }
203         } else if (code [-2] == 0xe8) {
204                 guint8 *buf = code - 2;
205
206                 buf [0] = 0x66;
207                 buf [1] = 0x66;
208                 buf [2] = 0x90;
209                 buf [3] = 0x66;
210                 buf [4] = 0x90;
211         } else if (code [0] == 0x90 || code [0] == 0xeb || code [0] == 0x66)
212                 /* Already changed by another thread */
213                 ;
214         else if ((code [-4] == 0x41) && (code [-3] == 0xff) && (code [-2] == 0x15))
215                 /* call *<OFFSET>(%rip) */
216                 ;
217         else {
218                 printf ("Invalid trampoline sequence: %x %x %x %x %x %x %x\n", code [0], code [1], code [2], code [3],
219                                 code [4], code [5], code [6]);
220                 g_assert_not_reached ();
221         }
222 }
223
224 guchar*
225 mono_arch_create_trampoline_code (MonoTrampolineType tramp_type)
226 {
227         guint8 *buf, *code, *tramp;
228         int i, lmf_offset, offset, method_offset, tramp_offset, saved_regs_offset, saved_fpregs_offset, framesize;
229         gboolean has_caller;
230
231         if (tramp_type == MONO_TRAMPOLINE_JUMP)
232                 has_caller = FALSE;
233         else
234                 has_caller = TRUE;
235
236         code = buf = mono_global_codeman_reserve (512);
237
238         framesize = 512 + sizeof (MonoLMF);
239         framesize = (framesize + (MONO_ARCH_FRAME_ALIGNMENT - 1)) & ~ (MONO_ARCH_FRAME_ALIGNMENT - 1);
240
241         offset = 0;
242
243         /* 
244          * Allocate a new stack frame and transfer the two arguments received on 
245          * the stack to our frame.
246          */
247         amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, 8);
248         amd64_pop_reg (code, AMD64_R11);
249
250         amd64_push_reg (code, AMD64_RBP);
251         amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, 8);
252         amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, framesize);
253
254         /* 
255          * The method is at offset -8 from the new RBP, so no need to
256          * copy it.
257          */
258         offset += 8;
259         method_offset = - offset;
260
261         offset += 8;
262         tramp_offset = - offset;
263         amd64_mov_membase_reg (code, AMD64_RBP, tramp_offset, AMD64_R11, 8);
264
265         /* Save all registers */
266
267         offset += AMD64_NREG * 8;
268         saved_regs_offset = - offset;
269         for (i = 0; i < AMD64_NREG; ++i)
270                 amd64_mov_membase_reg (code, AMD64_RBP, saved_regs_offset + (i * 8), i, 8);
271         offset += 8 * 8;
272         saved_fpregs_offset = - offset;
273         for (i = 0; i < 8; ++i)
274                 amd64_movsd_membase_reg (code, AMD64_RBP, saved_fpregs_offset + (i * 8), i);
275
276         /* Save LMF begin */
277
278         offset += sizeof (MonoLMF);
279         lmf_offset = - offset;
280
281         /* Save ip */
282         if (has_caller)
283                 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, 8, 8);
284         else
285                 amd64_mov_reg_imm (code, AMD64_R11, 0);
286         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, rip), AMD64_R11, 8);
287         /* Save fp */
288         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, ebp), AMD64_RBP, 8);
289         /* Save method */
290         if (tramp_type == MONO_TRAMPOLINE_GENERIC)
291                 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, method_offset, 8);
292         else
293                 amd64_mov_reg_imm (code, AMD64_R11, 0);
294         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, method), AMD64_R11, 8);
295         /* Save callee saved regs */
296         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, rbx), AMD64_RBX, 8);
297         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, r12), AMD64_R12, 8);
298         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, r13), AMD64_R13, 8);
299         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, r14), AMD64_R14, 8);
300         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, r15), AMD64_R15, 8);
301
302         amd64_mov_reg_imm (code, AMD64_R11, mono_get_lmf_addr);
303         amd64_call_reg (code, AMD64_R11);
304
305         /* Save lmf_addr */
306         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, lmf_addr), AMD64_RAX, 8);
307         /* Save previous_lmf */
308         amd64_mov_reg_membase (code, AMD64_R11, AMD64_RAX, 0, 8);
309         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, previous_lmf), AMD64_R11, 8);
310         /* Set new lmf */
311         amd64_lea_membase (code, AMD64_R11, AMD64_RBP, lmf_offset);
312         amd64_mov_membase_reg (code, AMD64_RAX, 0, AMD64_R11, 8);
313
314         /* Save LMF end */
315
316         /* Arg1 is the pointer to the saved registers */
317         amd64_lea_membase (code, AMD64_RDI, AMD64_RBP, saved_regs_offset);
318
319         /* Arg2 is the address of the calling code */
320         if (has_caller)
321                 amd64_mov_reg_membase (code, AMD64_RSI, AMD64_RBP, 8, 8);
322         else
323                 amd64_mov_reg_imm (code, AMD64_RSI, 0);
324
325         /* Arg3 is the method/vtable ptr */
326         amd64_mov_reg_membase (code, AMD64_RDX, AMD64_RBP, method_offset, 8);
327
328         /* Arg4 is the trampoline address */
329         amd64_mov_reg_membase (code, AMD64_RCX, AMD64_RBP, tramp_offset, 8);
330
331         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
332                 tramp = (guint8*)amd64_class_init_trampoline;
333         else if (tramp_type == MONO_TRAMPOLINE_AOT)
334                 tramp = (guint8*)amd64_aot_trampoline;
335         else
336                 tramp = (guint8*)amd64_magic_trampoline;
337
338         amd64_mov_reg_imm (code, AMD64_RAX, tramp);
339         amd64_call_reg (code, AMD64_RAX);
340
341         /* Restore LMF */
342
343         amd64_mov_reg_membase (code, AMD64_RCX, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, previous_lmf), 8);
344         amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, lmf_addr), 8);
345         amd64_mov_membase_reg (code, AMD64_R11, 0, AMD64_RCX, 8);
346
347         /* Restore argument registers */
348         for (i = 0; i < AMD64_NREG; ++i)
349                 if (AMD64_IS_ARGUMENT_REG (i))
350                         amd64_mov_reg_membase (code, i, AMD64_RBP, saved_regs_offset + (i * 8), 8);
351
352         for (i = 0; i < 8; ++i)
353                 amd64_movsd_reg_membase (code, i, AMD64_RBP, saved_fpregs_offset + (i * 8));
354
355         /* Restore stack */
356         amd64_leave (code);
357
358         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
359                 amd64_ret (code);
360         else
361                 /* call the compiled method */
362                 amd64_jump_reg (code, X86_EAX);
363
364         g_assert ((code - buf) <= 512);
365
366         mono_arch_flush_icache (buf, code - buf);
367
368         return buf;
369 }
370
371 #define TRAMPOLINE_SIZE 34
372
373 static MonoJitInfo*
374 create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain)
375 {
376         MonoJitInfo *ji;
377         guint8 *code, *buf, *tramp;
378         int jump_offset;
379
380         tramp = mono_get_trampoline_code (tramp_type);
381
382         code = buf = g_alloca (TRAMPOLINE_SIZE);
383
384         /* push trampoline address */
385         amd64_lea_membase (code, AMD64_R11, AMD64_RIP, -7);
386         amd64_push_reg (code, AMD64_R11);
387
388         /* push argument */
389         if (amd64_is_imm32 ((gint64)arg1))
390                 amd64_push_imm (code, (gint64)arg1);
391         else {
392                 amd64_mov_reg_imm (code, AMD64_R11, arg1);
393                 amd64_push_reg (code, AMD64_R11);
394         }
395
396         jump_offset = code - buf;
397         amd64_jump_disp (code, 0xffffffff);
398
399         g_assert ((code - buf) <= TRAMPOLINE_SIZE);
400
401         ji = g_new0 (MonoJitInfo, 1);
402
403         mono_domain_lock (domain);
404         /* 
405          * FIXME: Changing the size to code - buf causes strange crashes during
406          * mcs bootstrap.
407          */
408         ji->code_start = mono_code_manager_reserve (domain->code_mp, TRAMPOLINE_SIZE);
409         ji->code_size = code - buf;
410         mono_domain_unlock (domain);
411
412         memcpy (ji->code_start, buf, ji->code_size);
413
414         /* Fix up jump */
415         g_assert ((((gint64)tramp) >> 32) == 0);
416         code = (guint8*)ji->code_start + jump_offset;
417         amd64_jump_disp (code, tramp - code);
418
419         mono_jit_stats.method_trampolines++;
420
421         mono_arch_flush_icache (ji->code_start, ji->code_size);
422
423         return ji;
424 }       
425
426 MonoJitInfo*
427 mono_arch_create_jump_trampoline (MonoMethod *method)
428 {
429         MonoJitInfo *ji = create_specific_trampoline (method, MONO_TRAMPOLINE_JUMP, mono_domain_get ());
430
431         ji->method = method;
432         return ji;
433 }
434
435 /**
436  * mono_arch_create_jit_trampoline:
437  * @method: pointer to the method info
438  *
439  * Creates a trampoline function for virtual methods. If the created
440  * code is called it first starts JIT compilation of method,
441  * and then calls the newly created method. I also replaces the
442  * corresponding vtable entry (see amd64_magic_trampoline).
443  * 
444  * Returns: a pointer to the newly created code 
445  */
446 gpointer
447 mono_arch_create_jit_trampoline (MonoMethod *method)
448 {
449         MonoJitInfo *ji;
450         gpointer code_start;
451
452         ji = create_specific_trampoline (method, MONO_TRAMPOLINE_GENERIC, mono_domain_get ());
453         code_start = ji->code_start;
454         g_free (ji);
455
456         return code_start;
457 }
458
459 gpointer
460 mono_arch_create_jit_trampoline_from_token (MonoImage *image, guint32 token)
461 {
462         MonoDomain *domain = mono_domain_get ();
463         MonoJitInfo *ji;
464         gpointer code_start;
465         guint8 *buf, *start;
466
467         mono_domain_lock (domain);
468         buf = start = mono_code_manager_reserve (domain->code_mp, 2 * sizeof (gpointer));
469         mono_domain_unlock (domain);
470
471         *(gpointer*)buf = image;
472         buf += sizeof (gpointer);
473         *(guint32*)buf = token;
474
475         ji = create_specific_trampoline (start, MONO_TRAMPOLINE_AOT, domain);
476         code_start = ji->code_start;
477         g_free (ji);
478
479         return code_start;
480 }
481
482 /**
483  * mono_arch_create_class_init_trampoline:
484  *  @vtable: the type to initialize
485  *
486  * Creates a trampoline function to run a type initializer. 
487  * If the trampoline is called, it calls mono_runtime_class_init with the
488  * given vtable, then patches the caller code so it does not get called any
489  * more.
490  * 
491  * Returns: a pointer to the newly created code 
492  */
493 gpointer
494 mono_arch_create_class_init_trampoline (MonoVTable *vtable)
495 {
496         MonoJitInfo *ji;
497         gpointer code;
498
499         ji = create_specific_trampoline (vtable, MONO_TRAMPOLINE_CLASS_INIT, vtable->domain);
500         code = ji->code_start;
501         g_free (ji);
502
503         return code;
504 }
505
506 void
507 mono_arch_invalidate_method (MonoJitInfo *ji, void *func, gpointer func_arg)
508 {
509         /* FIXME: This is not thread safe */
510         guint8 *code = ji->code_start;
511
512         amd64_mov_reg_imm (code, AMD64_RDI, func_arg);
513         amd64_mov_reg_imm (code, AMD64_R11, func);
514
515         x86_push_imm (code, (guint64)func_arg);
516         amd64_call_reg (code, AMD64_R11);
517 }
518
519 /*
520  * This method is only called when running in the Mono Debugger.
521  */
522 gpointer
523 mono_debugger_create_notification_function (gpointer *notification_address)
524 {
525         guint8 *ptr, *buf;
526
527         ptr = buf = mono_global_codeman_reserve (16);
528
529         x86_breakpoint (buf);
530         if (notification_address)
531                 *notification_address = buf;
532         x86_ret (buf);
533
534         return ptr;
535 }