Wed Oct 6 12:40:28 CEST 2004 Paolo Molaro <lupus@ximian.com>
[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 typedef enum {
27         MONO_TRAMPOLINE_GENERIC,
28         MONO_TRAMPOLINE_JUMP,
29         MONO_TRAMPOLINE_CLASS_INIT
30 } MonoTrampolineType;
31
32 /*
33  * Address of the trampoline code.  This is used by the debugger to check
34  * whether a method is a trampoline.
35  */
36 guint8 *mono_generic_trampoline_code = NULL;
37
38 /*
39  * AMD64 processors maintain icache coherency only for pages which are marked
40  * executable, so we have to alloc memory through a code manager.
41  */
42 static CRITICAL_SECTION tramp_codeman_mutex;
43 static MonoCodeManager *tramp_codeman;
44
45 /*
46  * get_unbox_trampoline:
47  * @m: method pointer
48  * @addr: pointer to native code for @m
49  *
50  * when value type methods are called through the vtable we need to unbox the
51  * this argument. This method returns a pointer to a trampoline which does
52  * unboxing before calling the method
53  */
54 static gpointer
55 get_unbox_trampoline (MonoMethod *m, gpointer addr)
56 {
57         guint8 *code, *start;
58         int this_reg = AMD64_RDI;
59         MonoDomain *domain = mono_domain_get ();
60
61         if (!m->signature->ret->byref && MONO_TYPE_ISSTRUCT (m->signature->ret))
62                 this_reg = AMD64_RSI;
63
64         mono_domain_lock (domain);
65         start = code = mono_code_manager_reserve (domain->code_mp, 20);
66         mono_domain_unlock (domain);
67
68         amd64_alu_reg_imm (code, X86_ADD, this_reg, sizeof (MonoObject));
69         /* FIXME: Optimize this */
70         amd64_mov_reg_imm (code, AMD64_RAX, addr);
71         amd64_jump_reg (code, AMD64_RAX);
72         g_assert ((code - start) < 20);
73
74         mono_arch_flush_icache (start, code - start);
75
76         return start;
77 }
78
79 /**
80  * amd64_magic_trampoline:
81  */
82 static gpointer
83 amd64_magic_trampoline (long *regs, guint8 *code, MonoMethod *m, guint8* tramp)
84 {
85         gpointer addr;
86         gpointer *vtable_slot;
87
88         addr = mono_compile_method (m);
89         g_assert (addr);
90
91         //printf ("ENTER: %s\n", mono_method_full_name (m, TRUE));
92
93         /* the method was jumped to */
94         if (!code)
95                 return addr;
96
97         vtable_slot = mono_amd64_get_vcall_slot_addr (code, regs);
98
99         if (vtable_slot) {
100                 if (m->klass->valuetype)
101                         addr = get_unbox_trampoline (m, addr);
102
103                 g_assert (*vtable_slot);
104
105                 *vtable_slot = addr;
106         }
107         else {
108                 /* Patch calling code */
109
110                 if ((code [-13] == 0x49) && (code [-12] == 0xbb)) {
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                         /* The first part of the condition means an icall without a wrapper */
117                         if ((!target_ji && m->addr) || mono_method_same_domain (ji, target_ji)) {
118                                 InterlockedExchangePointer ((gpointer*)(code - 11), addr);
119                         }
120                 }
121                 else {
122                         /* FIXME: handle more cases */
123
124                         /* Patch trampoline in case the calling code can't be patched */
125                         /* FIXME: Make this thread safe */
126 #if 0
127                         /* FIXME:  This causes nunit-console to fail */
128                         amd64_mov_reg_imm (tramp, AMD64_R11, addr);
129                         amd64_jump_reg (tramp, AMD64_R11);
130 #endif
131                 }
132         }
133
134         return addr;
135 }
136
137 /**
138  * amd64_class_init_trampoline:
139  *
140  * This method calls mono_runtime_class_init () to run the static constructor
141  * for the type, then patches the caller code so it is not called again.
142  */
143 static void
144 amd64_class_init_trampoline (long *regs, guint8 *code, MonoVTable *vtable)
145 {
146         mono_runtime_class_init (vtable);
147
148         code -= 3;
149         if ((code [0] == 0x49) && (code [1] == 0xff)) {
150                 if (!mono_running_on_valgrind ()) {
151                         /* amd64_set_reg_template is 10 bytes long */
152                         guint8* buf = code - 10;
153
154                         /* FIXME: Make this thread safe */
155                         /* Padding code suggested by the AMD64 Opt Manual */
156                         buf [0] = 0x66;
157                         buf [1] = 0x66;
158                         buf [2] = 0x66;
159                         buf [3] = 0x90;
160                         buf [4] = 0x66;
161                         buf [5] = 0x66;
162                         buf [6] = 0x66;
163                         buf [7] = 0x90;
164                         buf [8] = 0x66;
165                         buf [9] = 0x66;
166                         buf [10] = 0x90;
167                         buf [11] = 0x66;
168                         buf [12] = 0x90;
169                 }
170         }
171         else
172                 if (code [0] == 0x90 || code [0] == 0xeb || code [0] == 0x66)
173                         /* Already changed by another thread */
174                         ;
175                 else {
176                         printf ("Invalid trampoline sequence: %x %x %x %x %x %x %x\n", code [0], code [1], code [2], code [3],
177                                 code [4], code [5], code [6]);
178                         g_assert_not_reached ();
179                 }
180 }
181
182 static guchar*
183 create_trampoline_code (MonoTrampolineType tramp_type)
184 {
185         guint8 *buf, *code, *tramp;
186         int i, lmf_offset, offset, method_offset, tramp_offset, saved_regs_offset, saved_fpregs_offset, framesize;
187         static guint8* generic_jump_trampoline = NULL;
188         static guint8 *generic_class_init_trampoline = NULL;
189
190         switch (tramp_type) {
191         case MONO_TRAMPOLINE_GENERIC:
192                 if (mono_generic_trampoline_code)
193                         return mono_generic_trampoline_code;
194                 break;
195         case MONO_TRAMPOLINE_JUMP:
196                 if (generic_jump_trampoline)
197                         return generic_jump_trampoline;
198                 break;
199         case MONO_TRAMPOLINE_CLASS_INIT:
200                 if (generic_class_init_trampoline)
201                         return generic_class_init_trampoline;
202                 break;
203         }
204
205         EnterCriticalSection (&tramp_codeman_mutex);
206         code = buf = mono_code_manager_reserve (tramp_codeman, 512);
207         LeaveCriticalSection (&tramp_codeman_mutex);
208
209         framesize = 512 + sizeof (MonoLMF);
210         framesize = (framesize + (MONO_ARCH_FRAME_ALIGNMENT - 1)) & ~ (MONO_ARCH_FRAME_ALIGNMENT - 1);
211
212         offset = 0;
213
214         /* 
215          * Allocate a new stack frame and transfer the two arguments received on 
216          * the stack to our frame.
217          */
218         amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, 8);
219         amd64_pop_reg (code, AMD64_R11);
220
221         amd64_push_reg (code, AMD64_RBP);
222         amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, 8);
223         amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, framesize);
224
225         /* 
226          * The method is at offset -8 from the new RBP, so no need to
227          * copy it.
228          */
229         offset += 8;
230         method_offset = - offset;
231
232         offset += 8;
233         tramp_offset = - offset;
234         amd64_mov_membase_reg (code, AMD64_RBP, tramp_offset, AMD64_R11, 8);
235
236         /* Save all registers */
237
238         offset += AMD64_NREG * 8;
239         saved_regs_offset = - offset;
240         for (i = 0; i < AMD64_NREG; ++i)
241                 amd64_mov_membase_reg (code, AMD64_RBP, saved_regs_offset + (i * 8), i, 8);
242         offset += 8 * 8;
243         saved_fpregs_offset = - offset;
244         for (i = 0; i < 8; ++i)
245                 amd64_movsd_membase_reg (code, AMD64_RBP, saved_fpregs_offset + (i * 8), i);
246
247         /* Save LMF begin */
248
249         offset += sizeof (MonoLMF);
250         lmf_offset = - offset;
251
252         /* Save ip */
253         if (tramp_type == MONO_TRAMPOLINE_JUMP)
254                 amd64_mov_reg_imm (code, AMD64_R11, 0);
255         else
256                 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, 8, 8);
257         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, rip), AMD64_R11, 8);
258         /* Save fp */
259         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, ebp), AMD64_RBP, 8);
260         /* Save method */
261         if (tramp_type == MONO_TRAMPOLINE_GENERIC)
262                 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, method_offset, 8);
263         else
264                 amd64_mov_reg_imm (code, AMD64_R11, 0);
265         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, method), AMD64_R11, 8);
266         /* Save callee saved regs */
267         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, rbx), AMD64_RBX, 8);
268         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, r12), AMD64_R12, 8);
269         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, r13), AMD64_R13, 8);
270         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, r14), AMD64_R14, 8);
271         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, r15), AMD64_R15, 8);
272
273         amd64_mov_reg_imm (code, AMD64_R11, mono_get_lmf_addr);
274         amd64_call_reg (code, AMD64_R11);
275
276         /* Save lmf_addr */
277         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, lmf_addr), AMD64_RAX, 8);
278         /* Save previous_lmf */
279         amd64_mov_reg_membase (code, AMD64_R11, AMD64_RAX, 0, 8);
280         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, previous_lmf), AMD64_R11, 8);
281         /* Set new lmf */
282         amd64_lea_membase (code, AMD64_R11, AMD64_RBP, lmf_offset);
283         amd64_mov_membase_reg (code, AMD64_RAX, 0, AMD64_R11, 8);
284
285         /* Save LMF end */
286
287         /* Arg1 is the pointer to the saved registers */
288         amd64_lea_membase (code, AMD64_RDI, AMD64_RBP, saved_regs_offset);
289
290         /* Arg2 is the address of the calling code */
291         amd64_mov_reg_membase (code, AMD64_RSI, AMD64_RBP, 8, 8);
292
293         /* Arg3 is the method/vtable ptr */
294         amd64_mov_reg_membase (code, AMD64_RDX, AMD64_RBP, method_offset, 8);
295
296         /* Arg4 is the trampoline address */
297         amd64_mov_reg_membase (code, AMD64_RCX, AMD64_RBP, tramp_offset, 8);
298
299         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
300                 tramp = (guint8*)amd64_class_init_trampoline;
301         else
302                 tramp = (guint8*)amd64_magic_trampoline;
303
304         amd64_mov_reg_imm (code, AMD64_RAX, tramp);
305         amd64_call_reg (code, AMD64_RAX);
306
307         /* Restore LMF */
308
309         amd64_mov_reg_membase (code, AMD64_RCX, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, previous_lmf), 8);
310         amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, lmf_addr), 8);
311         amd64_mov_membase_reg (code, AMD64_R11, 0, AMD64_RCX, 8);
312
313         /* Restore argument registers */
314         for (i = 0; i < AMD64_NREG; ++i)
315                 if (AMD64_IS_ARGUMENT_REG (i))
316                         amd64_mov_reg_membase (code, i, AMD64_RBP, saved_regs_offset + (i * 8), 8);
317
318         for (i = 0; i < 8; ++i)
319                 amd64_movsd_reg_membase (code, i, AMD64_RBP, saved_fpregs_offset + (i * 8));
320
321         /* Restore stack */
322         amd64_leave (code);
323
324         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
325                 amd64_ret (code);
326         else
327                 /* call the compiled method */
328                 amd64_jump_reg (code, X86_EAX);
329
330         g_assert ((code - buf) <= 512);
331
332         mono_arch_flush_icache (buf, code - buf);
333
334         switch (tramp_type) {
335         case MONO_TRAMPOLINE_GENERIC:
336                 mono_generic_trampoline_code = buf;
337                 break;
338         case MONO_TRAMPOLINE_JUMP:
339                 generic_jump_trampoline = buf;
340                 break;
341         case MONO_TRAMPOLINE_CLASS_INIT:
342                 generic_class_init_trampoline = buf;
343                 break;
344         }
345
346         return buf;
347 }
348
349 #define TRAMPOLINE_SIZE 34
350
351 static MonoJitInfo*
352 create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain)
353 {
354         MonoJitInfo *ji;
355         guint8 *code, *buf, *tramp;
356
357         tramp = create_trampoline_code (tramp_type);
358
359         mono_domain_lock (domain);
360         code = buf = mono_code_manager_reserve (domain->code_mp, TRAMPOLINE_SIZE);
361         mono_domain_unlock (domain);
362
363         /* push trampoline address */
364         amd64_lea_membase (code, AMD64_R11, AMD64_RIP, -7);
365         amd64_push_reg (code, AMD64_R11);
366
367         /* push argument */
368         amd64_mov_reg_imm (code, AMD64_R11, arg1);
369         amd64_push_reg (code, AMD64_R11);
370
371         /* FIXME: Optimize this */
372         amd64_mov_reg_imm (code, AMD64_R11, tramp);
373         amd64_jump_reg (code, AMD64_R11);
374
375         g_assert ((code - buf) <= TRAMPOLINE_SIZE);
376
377         ji = g_new0 (MonoJitInfo, 1);
378         ji->code_start = buf;
379         ji->code_size = code - buf;
380
381         mono_jit_stats.method_trampolines++;
382
383         mono_arch_flush_icache (ji->code_start, ji->code_size);
384
385         return ji;
386 }       
387
388 MonoJitInfo*
389 mono_arch_create_jump_trampoline (MonoMethod *method)
390 {
391         MonoJitInfo *ji = create_specific_trampoline (method, MONO_TRAMPOLINE_JUMP, mono_domain_get ());
392
393         ji->method = method;
394         return ji;
395 }
396
397 /**
398  * mono_arch_create_jit_trampoline:
399  * @method: pointer to the method info
400  *
401  * Creates a trampoline function for virtual methods. If the created
402  * code is called it first starts JIT compilation of method,
403  * and then calls the newly created method. I also replaces the
404  * corresponding vtable entry (see amd64_magic_trampoline).
405  * 
406  * Returns: a pointer to the newly created code 
407  */
408 gpointer
409 mono_arch_create_jit_trampoline (MonoMethod *method)
410 {
411         MonoJitInfo *ji;
412         MonoDomain *domain = mono_domain_get ();
413
414         /* Trampoline are domain specific, so cache only the one used in the root domain */
415         if ((domain == mono_get_root_domain ()) && method->info)
416                 return method->info;
417
418         if (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
419                 return mono_arch_create_jit_trampoline (mono_marshal_get_synchronized_wrapper (method));
420
421         ji = create_specific_trampoline (method, MONO_TRAMPOLINE_GENERIC, domain);
422         if (domain == mono_get_root_domain ())
423                 method->info = ji->code_start;
424         g_free (ji);
425
426         return ji->code_start;
427 }
428
429 /**
430  * mono_arch_create_class_init_trampoline:
431  *  @vtable: the type to initialize
432  *
433  * Creates a trampoline function to run a type initializer. 
434  * If the trampoline is called, it calls mono_runtime_class_init with the
435  * given vtable, then patches the caller code so it does not get called any
436  * more.
437  * 
438  * Returns: a pointer to the newly created code 
439  */
440 gpointer
441 mono_arch_create_class_init_trampoline (MonoVTable *vtable)
442 {
443         MonoJitInfo *ji;
444         gpointer code;
445
446         ji = create_specific_trampoline (vtable, MONO_TRAMPOLINE_CLASS_INIT, vtable->domain);
447         code = ji->code_start;
448         g_free (ji);
449
450         return code;
451 }
452
453 void
454 mono_arch_invalidate_method (MonoJitInfo *ji, void *func, gpointer func_arg)
455 {
456         /* FIXME: This is not thread safe */
457         guint8 *code = ji->code_start;
458
459         amd64_mov_reg_imm (code, AMD64_RDI, func_arg);
460         amd64_mov_reg_imm (code, AMD64_R11, func);
461
462         x86_push_imm (code, (guint64)func_arg);
463         amd64_call_reg (code, AMD64_R11);
464 }
465
466 /*
467  * This method is only called when running in the Mono Debugger.
468  */
469 gpointer
470 mono_debugger_create_notification_function (gpointer *notification_address)
471 {
472         guint8 *ptr, *buf;
473
474         EnterCriticalSection (&tramp_codeman_mutex);
475         ptr = buf = mono_code_manager_reserve (tramp_codeman, 16);
476         LeaveCriticalSection (&tramp_codeman_mutex);
477
478         x86_breakpoint (buf);
479         if (notification_address)
480                 *notification_address = buf;
481         x86_ret (buf);
482
483         return ptr;
484 }
485
486 void
487 mono_amd64_tramp_init (void)
488 {
489         InitializeCriticalSection (&tramp_codeman_mutex);
490
491         tramp_codeman = mono_code_manager_new ();
492
493         create_trampoline_code (MONO_TRAMPOLINE_GENERIC);
494         create_trampoline_code (MONO_TRAMPOLINE_JUMP);
495         create_trampoline_code (MONO_TRAMPOLINE_CLASS_INIT);
496 }