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