2004-07-26 Atsushi Enomoto <atsushi@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 /* 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)
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                 /* FIXME: Fill in vtable slot */
97         }
98         else
99                 /* FIXME: Patch calling code */
100                 ;
101
102         return addr;
103 }
104
105 /**
106  * amd64_class_init_trampoline:
107  *
108  * This method calls mono_runtime_class_init () to run the static constructor
109  * for the type, then patches the caller code so it is not called again.
110  */
111 static void
112 amd64_class_init_trampoline (long *regs, guint8 *code, MonoVTable *vtable)
113 {
114         mono_runtime_class_init (vtable);
115
116         /* FIXME: patch calling code */
117
118 #if 0
119         code -= 5;
120         if (code [0] == 0xe8) {
121                 if (!mono_running_on_valgrind ()) {
122                         guint32 ops;
123                         /*
124                          * Thread safe code patching using the algorithm from the paper
125                          * 'Practicing JUDO: Java Under Dynamic Optimizations'
126                          */
127                         /* 
128                          * First atomically change the the first 2 bytes of the call to a
129                          * spinning jump.
130                          */
131                         ops = 0xfeeb;
132                         InterlockedExchange ((gint32*)code, ops);
133
134                         /* Then change the other bytes to a nop */
135                         code [2] = 0x90;
136                         code [3] = 0x90;
137                         code [4] = 0x90;
138
139                         /* Then atomically change the first 4 bytes to a nop as well */
140                         ops = 0x90909090;
141                         InterlockedExchange ((guint32*)code, ops);
142
143 #ifdef HAVE_VALGRIND_MEMCHECK_H
144                         /* FIXME: the calltree skin trips on the self modifying code above */
145
146                         /* Tell valgrind to recompile the patched code */
147                         //VALGRIND_DISCARD_TRANSLATIONS (code, code + 8);
148 #endif
149                 }
150         }
151         else
152                 if (code [0] == 0x90 || code [0] == 0xeb)
153                         /* Already changed by another thread */
154                         ;
155                 else {
156                         printf ("Invalid trampoline sequence: %x %x %x %x %x %x %x\n", code [0], code [1], code [2], code [3],
157                                 code [4], code [5], code [6]);
158                         g_assert_not_reached ();
159                 }
160 #endif
161 }
162
163 static guchar*
164 create_trampoline_code (MonoTrampolineType tramp_type)
165 {
166         guint8 *buf, *code, *tramp;
167         int i;
168         static guint8* generic_jump_trampoline = NULL;
169         static guint8 *generic_class_init_trampoline = NULL;
170
171         switch (tramp_type) {
172         case MONO_TRAMPOLINE_GENERIC:
173                 if (mono_generic_trampoline_code)
174                         return mono_generic_trampoline_code;
175                 break;
176         case MONO_TRAMPOLINE_JUMP:
177                 if (generic_jump_trampoline)
178                         return generic_jump_trampoline;
179                 break;
180         case MONO_TRAMPOLINE_CLASS_INIT:
181                 if (generic_class_init_trampoline)
182                         return generic_class_init_trampoline;
183                 break;
184         }
185
186         code = buf = g_malloc (256);
187
188         /* Allocate some stack space where we can save stuff */
189         amd64_alu_reg_imm (buf, X86_SUB, X86_ESP, 512);
190
191         /* Save the method/vtable received in RAX */
192         amd64_mov_membase_reg (buf, X86_ESP, 512 - 8, AMD64_RAX, 8);
193
194         /* FIXME: save lmf */
195
196         /* FIXME: Save fp regs */
197
198         /* Save argument registers */
199
200         for (i = 0; i < AMD64_NREG; ++i)
201                 amd64_mov_membase_reg (buf, X86_ESP, (i * 8), i, 8);
202
203         /* Arg1 is the pointer to the saved registers */
204         amd64_lea_membase (buf, AMD64_RDI, AMD64_RSP, 0);
205
206         /* Arg2 is the address of the calling code */
207         amd64_mov_reg_membase (buf, AMD64_RSI, X86_ESP, 512, 8);
208
209         /* Arg3 is the method/vtable ptr */
210         amd64_mov_reg_membase (buf, AMD64_RDX, X86_ESP, 512 - 8, 8);
211
212         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
213                 tramp = amd64_class_init_trampoline;
214         else
215                 tramp = amd64_magic_trampoline;
216
217         amd64_mov_reg_imm (buf, AMD64_RAX, tramp);
218         amd64_call_reg (buf, AMD64_RAX);
219
220         /* Restore argument registers */
221         for (i = 0; i < AMD64_NREG; ++i)
222                 if (AMD64_IS_ARGUMENT_REG (i))
223                         amd64_mov_reg_membase (buf, i, X86_ESP, (i * 8), 8);
224
225         /* Restore stack */
226         amd64_alu_reg_imm (buf, X86_ADD, X86_ESP, 512);
227
228         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
229                 amd64_ret (buf);
230         else
231                 /* call the compiled method */
232                 amd64_jump_reg (buf, X86_EAX);
233
234 #if 0
235         /* save caller save regs because we need to do a call */ 
236         x86_push_reg (buf, X86_EDX);
237         x86_push_reg (buf, X86_EAX);
238         x86_push_reg (buf, X86_ECX);
239
240         /* save LMF begin */
241
242         /* save the IP (caller ip) */
243         if (tramp_type == MONO_TRAMPOLINE_JUMP)
244                 x86_push_imm (buf, 0);
245         else
246                 x86_push_membase (buf, X86_ESP, 16);
247
248         x86_push_reg (buf, X86_EBP);
249         x86_push_reg (buf, X86_ESI);
250         x86_push_reg (buf, X86_EDI);
251         x86_push_reg (buf, X86_EBX);
252
253         /* save method info */
254         x86_push_membase (buf, X86_ESP, 32);
255         /* get the address of lmf for the current thread */
256         x86_call_code (buf, mono_get_lmf_addr);
257         /* push lmf */
258         x86_push_reg (buf, X86_EAX); 
259         /* push *lfm (previous_lmf) */
260         x86_push_membase (buf, X86_EAX, 0);
261         /* *(lmf) = ESP */
262         x86_mov_membase_reg (buf, X86_EAX, 0, X86_ESP, 4);
263         /* save LFM end */
264
265         /* push the method info */
266         x86_push_membase (buf, X86_ESP, 44);
267         /* push the return address onto the stack */
268         if (tramp_type == MONO_TRAMPOLINE_JUMP)
269                 x86_push_imm (buf, 0);
270         else
271                 x86_push_membase (buf, X86_ESP, 52);
272
273         /* save all register values */
274         x86_push_reg (buf, X86_EBX);
275         x86_push_reg (buf, X86_EDI);
276         x86_push_reg (buf, X86_ESI);
277         x86_push_membase (buf, X86_ESP, 64); /* EDX */
278         x86_push_membase (buf, X86_ESP, 64); /* ECX */
279         x86_push_membase (buf, X86_ESP, 64); /* EAX */
280
281         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
282                 x86_call_code (buf, amd64_class_init_trampoline);
283         else
284                 x86_call_code (buf, amd64_magic_trampoline);
285         x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 8*4);
286
287         /* restore LMF start */
288         /* ebx = previous_lmf */
289         x86_pop_reg (buf, X86_EBX);
290         /* edi = lmf */
291         x86_pop_reg (buf, X86_EDI);
292         /* *(lmf) = previous_lmf */
293         x86_mov_membase_reg (buf, X86_EDI, 0, X86_EBX, 4);
294         /* discard method info */
295         x86_pop_reg (buf, X86_ESI);
296         /* restore caller saved regs */
297         x86_pop_reg (buf, X86_EBX);
298         x86_pop_reg (buf, X86_EDI);
299         x86_pop_reg (buf, X86_ESI);
300         x86_pop_reg (buf, X86_EBP);
301
302         /* discard save IP */
303         x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 4);             
304         /* restore LMF end */
305
306         x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 16);
307 #endif
308
309         g_assert ((buf - code) <= 256);
310
311         switch (tramp_type) {
312         case MONO_TRAMPOLINE_GENERIC:
313                 mono_generic_trampoline_code = code;
314                 break;
315         case MONO_TRAMPOLINE_JUMP:
316                 generic_jump_trampoline = code;
317                 break;
318         case MONO_TRAMPOLINE_CLASS_INIT:
319                 generic_class_init_trampoline = code;
320                 break;
321         }
322
323         return code;
324 }
325
326 #define TRAMPOLINE_SIZE 24
327
328 static MonoJitInfo*
329 create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain)
330 {
331         MonoJitInfo *ji;
332         guint8 *code, *buf, *tramp;
333
334         tramp = create_trampoline_code (tramp_type);
335
336         mono_domain_lock (domain);
337         code = buf = mono_code_manager_reserve (domain->code_mp, TRAMPOLINE_SIZE);
338         mono_domain_unlock (domain);
339
340         amd64_mov_reg_imm (code, AMD64_RAX, arg1);
341         /* FIXME: optimize this */
342         amd64_mov_reg_imm (code, AMD64_R11, tramp);
343         amd64_jump_reg (code, AMD64_R11);
344
345         g_assert ((code - buf) <= TRAMPOLINE_SIZE);
346
347         ji = g_new0 (MonoJitInfo, 1);
348         ji->code_start = buf;
349         ji->code_size = (code - buf) * 4;
350
351         mono_jit_stats.method_trampolines++;
352
353         mono_arch_flush_icache (ji->code_start, ji->code_size);
354
355         return ji;
356 }       
357
358 MonoJitInfo*
359 mono_arch_create_jump_trampoline (MonoMethod *method)
360 {
361         MonoJitInfo *ji = create_specific_trampoline (method, MONO_TRAMPOLINE_JUMP, mono_domain_get ());
362
363         ji->method = method;
364         return ji;
365 }
366
367 /**
368  * mono_arch_create_jit_trampoline:
369  * @method: pointer to the method info
370  *
371  * Creates a trampoline function for virtual methods. If the created
372  * code is called it first starts JIT compilation of method,
373  * and then calls the newly created method. I also replaces the
374  * corresponding vtable entry (see amd64_magic_trampoline).
375  * 
376  * Returns: a pointer to the newly created code 
377  */
378 gpointer
379 mono_arch_create_jit_trampoline (MonoMethod *method)
380 {
381         MonoJitInfo *ji;
382
383         /* previously created trampoline code */
384         if (method->info)
385                 return method->info;
386
387         if (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
388                 return mono_arch_create_jit_trampoline (mono_marshal_get_synchronized_wrapper (method));
389
390         ji = create_specific_trampoline (method, MONO_TRAMPOLINE_GENERIC, mono_domain_get ());
391         method->info = ji->code_start;
392         g_free (ji);
393
394         return method->info;
395 }
396
397 /**
398  * mono_arch_create_class_init_trampoline:
399  *  @vtable: the type to initialize
400  *
401  * Creates a trampoline function to run a type initializer. 
402  * If the trampoline is called, it calls mono_runtime_class_init with the
403  * given vtable, then patches the caller code so it does not get called any
404  * more.
405  * 
406  * Returns: a pointer to the newly created code 
407  */
408 gpointer
409 mono_arch_create_class_init_trampoline (MonoVTable *vtable)
410 {
411         MonoJitInfo *ji;
412         gpointer code;
413
414         ji = create_specific_trampoline (vtable, MONO_TRAMPOLINE_CLASS_INIT, vtable->domain);
415         code = ji->code_start;
416         g_free (ji);
417
418         return code;
419 }
420
421 /*
422  * This method is only called when running in the Mono Debugger.
423  */
424 gpointer
425 mono_debugger_create_notification_function (gpointer *notification_address)
426 {
427         guint8 *ptr, *buf;
428
429         ptr = buf = g_malloc0 (16);
430         x86_breakpoint (buf);
431         if (notification_address)
432                 *notification_address = buf;
433         x86_ret (buf);
434
435         return ptr;
436 }
437