2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mono / mini / tramp-amd64.c
1 /*
2  * tramp-amd64.c: JIT trampoline code for amd64
3  *
4  * Authors:
5  *   Dietmar Maurer (dietmar@ximian.com)
6  *   Zoltan Varga (vargaz@gmail.com)
7  *
8  * (C) 2001 Ximian, Inc.
9  */
10
11 #include <config.h>
12 #include <glib.h>
13
14 #include <mono/metadata/appdomain.h>
15 #include <mono/metadata/marshal.h>
16 #include <mono/metadata/tabledefs.h>
17 #include <mono/metadata/mono-debug-debugger.h>
18 #include <mono/metadata/monitor.h>
19 #include <mono/arch/amd64/amd64-codegen.h>
20
21 #ifdef HAVE_VALGRIND_MEMCHECK_H
22 #include <valgrind/memcheck.h>
23 #endif
24
25 #include "mini.h"
26 #include "mini-amd64.h"
27
28 #define IS_REX(inst) (((inst) >= 0x40) && ((inst) <= 0x4f))
29
30 static guint8* nullified_class_init_trampoline;
31
32 /*
33  * mono_arch_get_unbox_trampoline:
34  * @gsctx: the generic sharing context
35  * @m: method pointer
36  * @addr: pointer to native code for @m
37  *
38  * when value type methods are called through the vtable we need to unbox the
39  * this argument. This method returns a pointer to a trampoline which does
40  * unboxing before calling the method
41  */
42 gpointer
43 mono_arch_get_unbox_trampoline (MonoGenericSharingContext *gsctx, MonoMethod *m, gpointer addr)
44 {
45         guint8 *code, *start;
46         int this_reg;
47
48         MonoDomain *domain = mono_domain_get ();
49
50         this_reg = mono_arch_get_this_arg_reg (mono_method_signature (m), gsctx, NULL);
51
52         start = code = mono_domain_code_reserve (domain, 20);
53
54         amd64_alu_reg_imm (code, X86_ADD, this_reg, sizeof (MonoObject));
55         /* FIXME: Optimize this */
56         amd64_mov_reg_imm (code, AMD64_RAX, addr);
57         amd64_jump_reg (code, AMD64_RAX);
58         g_assert ((code - start) < 20);
59
60         mono_arch_flush_icache (start, code - start);
61
62         return start;
63 }
64
65 /*
66  * mono_arch_get_static_rgctx_trampoline:
67  *
68  *   Create a trampoline which sets RGCTX_REG to MRGCTX, then jumps to ADDR.
69  */
70 gpointer
71 mono_arch_get_static_rgctx_trampoline (MonoMethod *m, MonoMethodRuntimeGenericContext *mrgctx, gpointer addr)
72 {
73         guint8 *code, *start;
74         int buf_len;
75
76         MonoDomain *domain = mono_domain_get ();
77
78 #ifdef MONO_ARCH_NOMAP32BIT
79         buf_len = 32;
80 #else
81         /* AOTed code could still have a non-32 bit address */
82         if ((((guint64)addr) >> 32) == 0)
83                 buf_len = 16;
84         else
85                 buf_len = 30;
86 #endif
87
88         start = code = mono_domain_code_reserve (domain, buf_len);
89
90         amd64_mov_reg_imm (code, MONO_ARCH_RGCTX_REG, mrgctx);
91         amd64_jump_code (code, addr);
92         g_assert ((code - start) < buf_len);
93
94         mono_arch_flush_icache (start, code - start);
95
96         return start;
97 }
98
99 /*
100  * mono_arch_patch_callsite:
101  *
102  *   Patch the callsite whose address is given by ORIG_CODE so it calls ADDR. ORIG_CODE
103  * points to the pc right after the call.
104  */
105 void
106 mono_arch_patch_callsite (guint8 *method_start, guint8 *orig_code, guint8 *addr)
107 {
108         guint8 *code;
109         guint8 buf [16];
110         gboolean can_write = mono_breakpoint_clean_code (method_start, orig_code, 14, buf, sizeof (buf));
111
112         code = buf + 14;
113
114         if (((code [-13] == 0x49) && (code [-12] == 0xbb)) || (code [-5] == 0xe8)) {
115                 if (code [-5] != 0xe8) {
116                         if (can_write) {
117                                 InterlockedExchangePointer ((gpointer*)(orig_code - 11), addr);
118 #ifdef HAVE_VALGRIND_MEMCHECK_H
119                                 VALGRIND_DISCARD_TRANSLATIONS (orig_code - 11, sizeof (gpointer));
120 #endif
121                         }
122                 } else {
123                         if ((((guint64)(addr)) >> 32) != 0) {
124                                 /* Print some diagnostics */
125                                 MonoJitInfo *ji = mono_jit_info_table_find (mono_domain_get (), (char*)orig_code);
126                                 if (ji)
127                                         fprintf (stderr, "At %s, offset 0x%zx\n", mono_method_full_name (ji->method, TRUE), (guint8*)orig_code - (guint8*)ji->code_start);
128                                 fprintf (stderr, "Addr: %p\n", addr);
129                                 ji = mono_jit_info_table_find (mono_domain_get (), (char*)addr);
130                                 if (ji)
131                                         fprintf (stderr, "Callee: %s\n", mono_method_full_name (ji->method, TRUE));
132                                 g_assert_not_reached ();
133                         }
134                         g_assert ((((guint64)(orig_code)) >> 32) == 0);
135                         if (can_write) {
136                                 InterlockedExchange ((gint32*)(orig_code - 4), ((gint64)addr - (gint64)orig_code));
137 #ifdef HAVE_VALGRIND_MEMCHECK_H
138                                 VALGRIND_DISCARD_TRANSLATIONS (orig_code - 5, 4);
139 #endif
140                         }
141                 }
142         }
143         else if ((code [-7] == 0x41) && (code [-6] == 0xff) && (code [-5] == 0x15)) {
144                 /* call *<OFFSET>(%rip) */
145                 gpointer *got_entry = (gpointer*)((guint8*)orig_code + (*(guint32*)(orig_code - 4)));
146                 if (can_write) {
147                         InterlockedExchangePointer (got_entry, addr);
148 #ifdef HAVE_VALGRIND_MEMCHECK_H
149                         VALGRIND_DISCARD_TRANSLATIONS (orig_code - 5, sizeof (gpointer));
150 #endif
151                 }
152         }
153 }
154
155 void
156 mono_arch_patch_plt_entry (guint8 *code, gpointer *got, mgreg_t *regs, guint8 *addr)
157 {
158         gint32 disp;
159         gpointer *plt_jump_table_entry;
160
161         /* A PLT entry: jmp *<DISP>(%rip) */
162         g_assert (code [0] == 0xff);
163         g_assert (code [1] == 0x25);
164
165         disp = *(gint32*)(code + 2);
166
167         plt_jump_table_entry = (gpointer*)(code + 6 + disp);
168
169         InterlockedExchangePointer (plt_jump_table_entry, addr);
170 }
171
172 void
173 mono_arch_nullify_class_init_trampoline (guint8 *code, mgreg_t *regs)
174 {
175         guint8 buf [16];
176         gboolean can_write = mono_breakpoint_clean_code (NULL, code, 7, buf, sizeof (buf));
177
178         if (!can_write)
179                 return;
180
181         code -= 3;
182
183         /* 
184          * A given byte sequence can match more than case here, so we have to be
185          * really careful about the ordering of the cases. Longer sequences
186          * come first.
187          */
188         if ((code [-4] == 0x41) && (code [-3] == 0xff) && (code [-2] == 0x15)) {
189                 gpointer *vtable_slot;
190
191                 /* call *<OFFSET>(%rip) */
192                 vtable_slot = mono_get_vcall_slot_addr (code + 3, regs);
193                 g_assert (vtable_slot);
194
195                 *vtable_slot = nullified_class_init_trampoline;
196         } else if (code [-2] == 0xe8) {
197                 /* call <TARGET> */
198                 //guint8 *buf = code - 2;
199
200                 /* 
201                  * It would be better to replace the call with nops, but that doesn't seem
202                  * to work on SMP machines even when the whole call is inside a cache line.
203                  * Patching the call address seems to work.
204                  */
205                 /*
206                 buf [0] = 0x66;
207                 buf [1] = 0x66;
208                 buf [2] = 0x90;
209                 buf [3] = 0x66;
210                 buf [4] = 0x90;
211                 */
212
213                 mono_arch_patch_callsite (code - 2, code - 2 + 5, nullified_class_init_trampoline);
214         } else if ((code [0] == 0x41) && (code [1] == 0xff)) {
215                 /* call <REG> */
216                 /* happens on machines without MAP_32BIT like freebsd */
217                 /* amd64_set_reg_template is 10 bytes long */
218                 guint8* buf = code - 10;
219
220                 /* FIXME: Make this thread safe */
221                 /* Padding code suggested by the AMD64 Opt Manual */
222                 buf [0] = 0x66;
223                 buf [1] = 0x66;
224                 buf [2] = 0x66;
225                 buf [3] = 0x90;
226                 buf [4] = 0x66;
227                 buf [5] = 0x66;
228                 buf [6] = 0x66;
229                 buf [7] = 0x90;
230                 buf [8] = 0x66;
231                 buf [9] = 0x66;
232                 buf [10] = 0x90;
233                 buf [11] = 0x66;
234                 buf [12] = 0x90;
235         } else if (code [0] == 0x90 || code [0] == 0xeb || code [0] == 0x66) {
236                 /* Already changed by another thread */
237                 ;
238         } else {
239                 printf ("Invalid trampoline sequence: %x %x %x %x %x %x %x\n", code [0], code [1], code [2], code [3],
240                         code [4], code [5], code [6]);
241                 g_assert_not_reached ();
242         }
243 }
244
245 void
246 mono_arch_nullify_plt_entry (guint8 *code, mgreg_t *regs)
247 {
248         if (mono_aot_only && !nullified_class_init_trampoline)
249                 nullified_class_init_trampoline = mono_aot_get_named_code ("nullified_class_init_trampoline");
250
251         mono_arch_patch_plt_entry (code, NULL, regs, nullified_class_init_trampoline);
252 }
253
254 guchar*
255 mono_arch_create_trampoline_code (MonoTrampolineType tramp_type)
256 {
257         MonoJumpInfo *ji;
258         guint32 code_size;
259         guchar *code;
260         GSList *unwind_ops, *l;
261
262         code = mono_arch_create_trampoline_code_full (tramp_type, &code_size, &ji, &unwind_ops, FALSE);
263
264         mono_save_trampoline_xdebug_info ("<generic_trampoline>", code, code_size, unwind_ops);
265
266         for (l = unwind_ops; l; l = l->next)
267                 g_free (l->data);
268         g_slist_free (unwind_ops);
269
270         return code;
271 }
272
273 guchar*
274 mono_arch_create_trampoline_code_full (MonoTrampolineType tramp_type, guint32 *code_size, MonoJumpInfo **ji, GSList **out_unwind_ops, gboolean aot)
275 {
276         guint8 *buf, *code, *tramp, *br [2], *r11_save_code, *after_r11_save_code;
277         int i, lmf_offset, offset, res_offset, arg_offset, rax_offset, tramp_offset, saved_regs_offset;
278         int saved_fpregs_offset, rbp_offset, framesize, orig_rsp_to_rbp_offset, cfa_offset;
279         gboolean has_caller;
280         GSList *unwind_ops = NULL;
281
282         if (tramp_type == MONO_TRAMPOLINE_JUMP)
283                 has_caller = FALSE;
284         else
285                 has_caller = TRUE;
286
287         code = buf = mono_global_codeman_reserve (538);
288
289         *ji = NULL;
290
291         framesize = 538 + sizeof (MonoLMF);
292         framesize = (framesize + (MONO_ARCH_FRAME_ALIGNMENT - 1)) & ~ (MONO_ARCH_FRAME_ALIGNMENT - 1);
293
294         orig_rsp_to_rbp_offset = 0;
295         r11_save_code = code;
296         /* Reserve 5 bytes for the mov_membase_reg to save R11 */
297         code += 5;
298         after_r11_save_code = code;
299
300         // CFA = sp + 16 (the trampoline address is on the stack)
301         cfa_offset = 16;
302         mono_add_unwind_op_def_cfa (unwind_ops, code, buf, AMD64_RSP, 16);
303         // IP saved at CFA - 8
304         mono_add_unwind_op_offset (unwind_ops, code, buf, AMD64_RIP, -8);
305
306         /* Pop the return address off the stack */
307         amd64_pop_reg (code, AMD64_R11);
308         orig_rsp_to_rbp_offset += 8;
309
310         cfa_offset -= 8;
311         mono_add_unwind_op_def_cfa_offset (unwind_ops, code, buf, cfa_offset);
312
313         /* 
314          * Allocate a new stack frame
315          */
316         amd64_push_reg (code, AMD64_RBP);
317         cfa_offset += 8;
318         mono_add_unwind_op_def_cfa_offset (unwind_ops, code, buf, cfa_offset);
319         mono_add_unwind_op_offset (unwind_ops, code, buf, AMD64_RBP, - cfa_offset);
320
321         orig_rsp_to_rbp_offset -= 8;
322         amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, 8);
323         mono_add_unwind_op_def_cfa_reg (unwind_ops, code, buf, AMD64_RBP);
324         amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, framesize);
325
326         offset = 0;
327         rbp_offset = - offset;
328
329         offset += 8;
330         rax_offset = - offset;
331
332         offset += 8;
333         tramp_offset = - offset;
334
335         offset += 8;
336         arg_offset = - offset;
337
338         /* Compute the trampoline address from the return address */
339         if (aot) {
340                 /* 7 = length of call *<offset>(rip) */
341                 amd64_alu_reg_imm (code, X86_SUB, AMD64_R11, 7);
342         } else {
343                 /* 5 = length of amd64_call_membase () */
344                 amd64_alu_reg_imm (code, X86_SUB, AMD64_R11, 5);
345         }
346         amd64_mov_membase_reg (code, AMD64_RBP, tramp_offset, AMD64_R11, 8);
347
348         offset += 8;
349         res_offset = - offset;
350
351         /* Save all registers */
352
353         offset += AMD64_NREG * 8;
354         saved_regs_offset = - offset;
355         for (i = 0; i < AMD64_NREG; ++i) {
356                 if (i == AMD64_RBP) {
357                         /* RAX is already saved */
358                         amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RBP, rbp_offset, 8);
359                         amd64_mov_membase_reg (code, AMD64_RBP, saved_regs_offset + (i * 8), AMD64_RAX, 8);
360                 } else if (i != AMD64_R11) {
361                         amd64_mov_membase_reg (code, AMD64_RBP, saved_regs_offset + (i * 8), i, 8);
362                 } else {
363                         /* We have to save R11 right at the start of
364                            the trampoline code because it's used as a
365                            scratch register */
366                         amd64_mov_membase_reg (r11_save_code, AMD64_RSP, saved_regs_offset + orig_rsp_to_rbp_offset + (i * 8), i, 8);
367                         g_assert (r11_save_code == after_r11_save_code);
368                 }
369         }
370         offset += 8 * 8;
371         saved_fpregs_offset = - offset;
372         for (i = 0; i < 8; ++i)
373                 amd64_movsd_membase_reg (code, AMD64_RBP, saved_fpregs_offset + (i * 8), i);
374
375         if (tramp_type != MONO_TRAMPOLINE_GENERIC_CLASS_INIT &&
376                         tramp_type != MONO_TRAMPOLINE_MONITOR_ENTER &&
377                         tramp_type != MONO_TRAMPOLINE_MONITOR_EXIT) {
378                 /* Obtain the trampoline argument which is encoded in the instruction stream */
379                 if (aot) {
380                         /* Load the GOT offset */
381                         amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, tramp_offset, 8);
382                         amd64_mov_reg_membase (code, AMD64_RAX, AMD64_R11, 7, 4);
383                         /* Compute the address of the GOT slot */
384                         amd64_alu_reg_reg_size (code, X86_ADD, AMD64_R11, AMD64_RAX, 8);
385                         /* Load the value */
386                         amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, 0, 8);
387                 } else {                        
388                         amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, tramp_offset, 8);
389                         amd64_mov_reg_membase (code, AMD64_RAX, AMD64_R11, 5, 1);
390                         amd64_widen_reg (code, AMD64_RAX, AMD64_RAX, TRUE, FALSE);
391                         amd64_alu_reg_imm_size (code, X86_CMP, AMD64_RAX, 4, 1);
392                         br [0] = code;
393                         x86_branch8 (code, X86_CC_NE, 6, FALSE);
394                         /* 32 bit immediate */
395                         amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, 6, 4);
396                         br [1] = code;
397                         x86_jump8 (code, 10);
398                         /* 64 bit immediate */
399                         mono_amd64_patch (br [0], code);
400                         amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, 6, 8);
401                         mono_amd64_patch (br [1], code);
402                 }
403                 amd64_mov_membase_reg (code, AMD64_RBP, arg_offset, AMD64_R11, 8);
404         } else {
405                 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, saved_regs_offset + (MONO_AMD64_ARG_REG1 * 8), 8);
406                 amd64_mov_membase_reg (code, AMD64_RBP, arg_offset, AMD64_R11, 8);
407         }
408
409         /* Save LMF begin */
410
411         offset += sizeof (MonoLMF);
412         lmf_offset = - offset;
413
414         /* Save ip */
415         if (has_caller)
416                 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, 8, 8);
417         else
418                 amd64_mov_reg_imm (code, AMD64_R11, 0);
419         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, rip), AMD64_R11, 8);
420         /* Save fp */
421         amd64_mov_reg_membase (code, AMD64_R11, AMD64_RSP, framesize, 8);
422         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, rbp), AMD64_R11, 8);
423         /* Save sp */
424         amd64_mov_reg_reg (code, AMD64_R11, AMD64_RSP, 8);
425         amd64_alu_reg_imm (code, X86_ADD, AMD64_R11, framesize + 16);
426         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, rsp), AMD64_R11, 8);
427         /* Save method */
428         if (tramp_type == MONO_TRAMPOLINE_JIT || tramp_type == MONO_TRAMPOLINE_JUMP) {
429                 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, arg_offset, 8);
430                 amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, method), AMD64_R11, 8);
431         } else {
432                 amd64_mov_membase_imm (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, method), 0, 8);
433         }
434         /* Save callee saved regs */
435 #ifdef PLATFORM_WIN32
436         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, rdi), AMD64_RDI, 8);
437         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, rsi), AMD64_RSI, 8);
438 #endif
439         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, rbx), AMD64_RBX, 8);
440         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, r12), AMD64_R12, 8);
441         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, r13), AMD64_R13, 8);
442         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, r14), AMD64_R14, 8);
443         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, r15), AMD64_R15, 8);
444
445         if (aot) {
446                 *ji = mono_patch_info_list_prepend (*ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_get_lmf_addr");
447                 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RIP, 0, 8);
448         } else {
449                 amd64_mov_reg_imm (code, AMD64_R11, mono_get_lmf_addr);
450         }
451         amd64_call_reg (code, AMD64_R11);
452
453         /* Save lmf_addr */
454         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, lmf_addr), AMD64_RAX, 8);
455         /* Save previous_lmf */
456         /* Set the lowest bit to 1 to signal that this LMF has the ip field set */
457         amd64_mov_reg_membase (code, AMD64_R11, AMD64_RAX, 0, 8);
458         amd64_alu_reg_imm_size (code, X86_ADD, AMD64_R11, 1, 8);
459         amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, previous_lmf), AMD64_R11, 8);
460         /* Set new lmf */
461         amd64_lea_membase (code, AMD64_R11, AMD64_RBP, lmf_offset);
462         amd64_mov_membase_reg (code, AMD64_RAX, 0, AMD64_R11, 8);
463
464         /* Save LMF end */
465
466         /* Arg1 is the pointer to the saved registers */
467         amd64_lea_membase (code, AMD64_ARG_REG1, AMD64_RBP, saved_regs_offset);
468
469         /* Arg2 is the address of the calling code */
470         if (has_caller)
471                 amd64_mov_reg_membase (code, AMD64_ARG_REG2, AMD64_RBP, 8, 8);
472         else
473                 amd64_mov_reg_imm (code, AMD64_ARG_REG2, 0);
474
475         /* Arg3 is the method/vtable ptr */
476         amd64_mov_reg_membase (code, AMD64_ARG_REG3, AMD64_RBP, arg_offset, 8);
477
478         /* Arg4 is the trampoline address */
479         amd64_mov_reg_membase (code, AMD64_ARG_REG4, AMD64_RBP, tramp_offset, 8);
480
481         if (aot) {
482                 char *icall_name = g_strdup_printf ("trampoline_func_%d", tramp_type);
483                 *ji = mono_patch_info_list_prepend (*ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, icall_name);
484                 amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RIP, 0, 8);
485         } else {
486                 tramp = (guint8*)mono_get_trampoline_func (tramp_type);
487                 amd64_mov_reg_imm (code, AMD64_RAX, tramp);
488         }
489         amd64_call_reg (code, AMD64_RAX);
490
491         /* Check for thread interruption */
492         /* This is not perf critical code so no need to check the interrupt flag */
493         /* 
494          * Have to call the _force_ variant, since there could be a protected wrapper on the top of the stack.
495          */
496         amd64_mov_membase_reg (code, AMD64_RBP, res_offset, AMD64_RAX, 8);
497         if (aot) {
498                 *ji = mono_patch_info_list_prepend (*ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_thread_force_interruption_checkpoint");
499                 amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RIP, 0, 8);
500         } else {
501                 amd64_mov_reg_imm (code, AMD64_RAX, (guint8*)mono_thread_force_interruption_checkpoint);
502         }
503         amd64_call_reg (code, AMD64_RAX);
504         amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RBP, res_offset, 8);      
505
506         /* Restore LMF */
507
508         amd64_mov_reg_membase (code, AMD64_RCX, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, previous_lmf), 8);
509         amd64_alu_reg_imm_size (code, X86_SUB, AMD64_RCX, 1, 8);
510         amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, lmf_addr), 8);
511         amd64_mov_membase_reg (code, AMD64_R11, 0, AMD64_RCX, 8);
512
513         /* 
514          * Save rax to the stack, after the leave instruction, this will become part of
515          * the red zone.
516          */
517         amd64_mov_membase_reg (code, AMD64_RBP, rax_offset, AMD64_RAX, 8);
518
519         /* Restore argument registers, r10 (needed to pass rgctx to
520            static shared generic methods), r11 (imt register for
521            interface calls), and rax (needed for direct calls to C vararg functions). */
522         for (i = 0; i < AMD64_NREG; ++i)
523                 if (AMD64_IS_ARGUMENT_REG (i) || i == AMD64_R10 || i == AMD64_R11 || i == AMD64_RAX)
524                         amd64_mov_reg_membase (code, i, AMD64_RBP, saved_regs_offset + (i * 8), 8);
525
526         for (i = 0; i < 8; ++i)
527                 amd64_movsd_reg_membase (code, i, AMD64_RBP, saved_fpregs_offset + (i * 8));
528
529         /* Restore stack */
530         amd64_leave (code);
531
532         if (MONO_TRAMPOLINE_TYPE_MUST_RETURN (tramp_type)) {
533                 /* Load result */
534                 amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RSP, rax_offset - 0x8, 8);
535                 amd64_ret (code);
536         } else {
537                 /* call the compiled method using the saved rax */
538                 amd64_jump_membase (code, AMD64_RSP, rax_offset - 0x8);
539         }
540
541         g_assert ((code - buf) <= 538);
542
543         mono_arch_flush_icache (buf, code - buf);
544
545         *code_size = code - buf;
546
547         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT) {
548                 guint32 code_len;
549
550                 /* Initialize the nullified class init trampoline used in the AOT case */
551                 nullified_class_init_trampoline = mono_arch_get_nullified_class_init_trampoline (&code_len);
552         }
553
554         *out_unwind_ops = unwind_ops;
555         
556         return buf;
557 }
558
559 gpointer
560 mono_arch_get_nullified_class_init_trampoline (guint32 *code_len)
561 {
562         guint8 *code, *buf;
563
564         code = buf = mono_global_codeman_reserve (16);
565         amd64_ret (code);
566
567         mono_arch_flush_icache (buf, code - buf);
568
569         *code_len = code - buf;
570
571         return buf;
572 }
573
574 gpointer
575 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
576 {
577         guint8 *code, *buf, *tramp;
578         int size;
579
580         tramp = mono_get_trampoline_code (tramp_type);
581
582         if ((((guint64)arg1) >> 32) == 0)
583                 size = 5 + 1 + 4;
584         else
585                 size = 5 + 1 + 8;
586
587         code = buf = mono_domain_code_reserve_align (domain, size, 1);
588
589         amd64_call_code (code, tramp);
590         /* The trampoline code will obtain the argument from the instruction stream */
591         if ((((guint64)arg1) >> 32) == 0) {
592                 *code = 0x4;
593                 *(guint32*)(code + 1) = (gint64)arg1;
594                 code += 5;
595         } else {
596                 *code = 0x8;
597                 *(guint64*)(code + 1) = (gint64)arg1;
598                 code += 9;
599         }
600
601         g_assert ((code - buf) <= size);
602
603         if (code_len)
604                 *code_len = size;
605
606         mono_arch_flush_icache (buf, size);
607
608         return buf;
609 }       
610
611 gpointer
612 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot)
613 {
614         guint32 code_size;
615         MonoJumpInfo *ji;
616
617         return mono_arch_create_rgctx_lazy_fetch_trampoline_full (slot, &code_size, &ji, FALSE);
618 }
619
620 gpointer
621 mono_arch_create_rgctx_lazy_fetch_trampoline_full (guint32 slot, guint32 *code_size, MonoJumpInfo **ji, gboolean aot)
622 {
623         guint8 *tramp;
624         guint8 *code, *buf;
625         guint8 **rgctx_null_jumps;
626         int tramp_size;
627         int depth, index;
628         int i;
629         gboolean mrgctx;
630
631         *ji = NULL;
632
633         mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
634         index = MONO_RGCTX_SLOT_INDEX (slot);
635         if (mrgctx)
636                 index += sizeof (MonoMethodRuntimeGenericContext) / sizeof (gpointer);
637         for (depth = 0; ; ++depth) {
638                 int size = mono_class_rgctx_get_array_size (depth, mrgctx);
639
640                 if (index < size - 1)
641                         break;
642                 index -= size - 1;
643         }
644
645         tramp_size = 64 + 8 * depth;
646
647         code = buf = mono_global_codeman_reserve (tramp_size);
648
649         rgctx_null_jumps = g_malloc (sizeof (guint8*) * (depth + 2));
650
651         if (mrgctx) {
652                 /* get mrgctx ptr */
653                 amd64_mov_reg_reg (code, AMD64_RAX, AMD64_ARG_REG1, 8);
654         } else {
655                 /* load rgctx ptr from vtable */
656                 amd64_mov_reg_membase (code, AMD64_RAX, AMD64_ARG_REG1, G_STRUCT_OFFSET (MonoVTable, runtime_generic_context), 8);
657                 /* is the rgctx ptr null? */
658                 amd64_test_reg_reg (code, AMD64_RAX, AMD64_RAX);
659                 /* if yes, jump to actual trampoline */
660                 rgctx_null_jumps [0] = code;
661                 amd64_branch8 (code, X86_CC_Z, -1, 1);
662         }
663
664         for (i = 0; i < depth; ++i) {
665                 /* load ptr to next array */
666                 if (mrgctx && i == 0)
667                         amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RAX, sizeof (MonoMethodRuntimeGenericContext), 8);
668                 else
669                         amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RAX, 0, 8);
670                 /* is the ptr null? */
671                 amd64_test_reg_reg (code, AMD64_RAX, AMD64_RAX);
672                 /* if yes, jump to actual trampoline */
673                 rgctx_null_jumps [i + 1] = code;
674                 amd64_branch8 (code, X86_CC_Z, -1, 1);
675         }
676
677         /* fetch slot */
678         amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RAX, sizeof (gpointer) * (index + 1), 8);
679         /* is the slot null? */
680         amd64_test_reg_reg (code, AMD64_RAX, AMD64_RAX);
681         /* if yes, jump to actual trampoline */
682         rgctx_null_jumps [depth + 1] = code;
683         amd64_branch8 (code, X86_CC_Z, -1, 1);
684         /* otherwise return */
685         amd64_ret (code);
686
687         for (i = mrgctx ? 1 : 0; i <= depth + 1; ++i)
688                 x86_patch (rgctx_null_jumps [i], code);
689
690         g_free (rgctx_null_jumps);
691
692         /* move the rgctx pointer to the VTABLE register */
693         amd64_mov_reg_reg (code, MONO_ARCH_VTABLE_REG, AMD64_ARG_REG1, 8);
694
695         if (aot) {
696                 *ji = mono_patch_info_list_prepend (*ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, g_strdup_printf ("specific_trampoline_lazy_fetch_%u", slot));
697                 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RIP, 0, 8);
698                 amd64_jump_reg (code, AMD64_R11);
699         } else {
700                 tramp = mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH, mono_get_root_domain (), NULL);
701
702                 /* jump to the actual trampoline */
703                 amd64_jump_code (code, tramp);
704         }
705
706         mono_arch_flush_icache (buf, code - buf);
707
708         g_assert (code - buf <= tramp_size);
709
710         *code_size = code - buf;
711
712         return buf;
713 }
714
715 gpointer
716 mono_arch_create_generic_class_init_trampoline (void)
717 {
718         guint32 code_size;
719         MonoJumpInfo *ji;
720
721         return mono_arch_create_generic_class_init_trampoline_full (&code_size, &ji, FALSE);
722 }
723
724 gpointer
725 mono_arch_create_generic_class_init_trampoline_full (guint32 *code_size, MonoJumpInfo **ji, gboolean aot)
726 {
727         guint8 *tramp;
728         guint8 *code, *buf;
729         static int byte_offset = -1;
730         static guint8 bitmask;
731         guint8 *jump;
732         int tramp_size;
733
734         *ji = NULL;
735
736         tramp_size = 64;
737
738         code = buf = mono_global_codeman_reserve (tramp_size);
739
740         if (byte_offset < 0)
741                 mono_marshal_find_bitfield_offset (MonoVTable, initialized, &byte_offset, &bitmask);
742
743         amd64_test_membase_imm_size (code, MONO_AMD64_ARG_REG1, byte_offset, bitmask, 1);
744         jump = code;
745         amd64_branch8 (code, X86_CC_Z, -1, 1);
746
747         amd64_ret (code);
748
749         x86_patch (jump, code);
750
751         if (aot) {
752                 *ji = mono_patch_info_list_prepend (*ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "specific_trampoline_generic_class_init");
753                 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RIP, 0, 8);
754                 amd64_jump_reg (code, AMD64_R11);
755         } else {
756                 tramp = mono_arch_create_specific_trampoline (NULL, MONO_TRAMPOLINE_GENERIC_CLASS_INIT, mono_get_root_domain (), NULL);
757
758                 /* jump to the actual trampoline */
759                 amd64_jump_code (code, tramp);
760         }
761
762         mono_arch_flush_icache (buf, code - buf);
763
764         g_assert (code - buf <= tramp_size);
765
766         *code_size = code - buf;
767
768         return buf;
769 }
770
771 #ifdef MONO_ARCH_MONITOR_OBJECT_REG
772
773 gpointer
774 mono_arch_create_monitor_enter_trampoline (void)
775 {
776         guint32 code_size;
777         MonoJumpInfo *ji;
778
779         return mono_arch_create_monitor_enter_trampoline_full (&code_size, &ji, FALSE);
780 }
781
782 gpointer
783 mono_arch_create_monitor_enter_trampoline_full (guint32 *code_size, MonoJumpInfo **ji, gboolean aot)
784 {
785
786         guint8 *tramp;
787         guint8 *code, *buf;
788         guint8 *jump_obj_null, *jump_sync_null, *jump_cmpxchg_failed, *jump_other_owner, *jump_tid;
789         int tramp_size;
790         int owner_offset, nest_offset, dummy;
791
792         *ji = NULL;
793
794         g_assert (MONO_ARCH_MONITOR_OBJECT_REG == AMD64_RDI);
795
796         mono_monitor_threads_sync_members_offset (&owner_offset, &nest_offset, &dummy);
797         g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (owner_offset) == sizeof (gpointer));
798         g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (nest_offset) == sizeof (guint32));
799         owner_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (owner_offset);
800         nest_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (nest_offset);
801
802         tramp_size = 96;
803
804         code = buf = mono_global_codeman_reserve (tramp_size);
805
806         if (mono_thread_get_tls_offset () != -1) {
807                 /* MonoObject* obj is in RDI */
808                 /* is obj null? */
809                 amd64_test_reg_reg (code, AMD64_RDI, AMD64_RDI);
810                 /* if yes, jump to actual trampoline */
811                 jump_obj_null = code;
812                 amd64_branch8 (code, X86_CC_Z, -1, 1);
813
814                 /* load obj->synchronization to RCX */
815                 amd64_mov_reg_membase (code, AMD64_RCX, AMD64_RDI, G_STRUCT_OFFSET (MonoObject, synchronisation), 8);
816                 /* is synchronization null? */
817                 amd64_test_reg_reg (code, AMD64_RCX, AMD64_RCX);
818                 /* if yes, jump to actual trampoline */
819                 jump_sync_null = code;
820                 amd64_branch8 (code, X86_CC_Z, -1, 1);
821
822                 /* load MonoThread* into RDX */
823                 code = mono_amd64_emit_tls_get (code, AMD64_RDX, mono_thread_get_tls_offset ());
824                 /* load TID into RDX */
825                 amd64_mov_reg_membase (code, AMD64_RDX, AMD64_RDX, G_STRUCT_OFFSET (MonoThread, tid), 8);
826
827                 /* is synchronization->owner null? */
828                 amd64_alu_membase_imm_size (code, X86_CMP, AMD64_RCX, owner_offset, 0, 8);
829                 /* if not, jump to next case */
830                 jump_tid = code;
831                 amd64_branch8 (code, X86_CC_NZ, -1, 1);
832
833                 /* if yes, try a compare-exchange with the TID */
834                 /* zero RAX */
835                 amd64_alu_reg_reg (code, X86_XOR, AMD64_RAX, AMD64_RAX);
836                 /* compare and exchange */
837                 amd64_prefix (code, X86_LOCK_PREFIX);
838                 amd64_cmpxchg_membase_reg_size (code, AMD64_RCX, owner_offset, AMD64_RDX, 8);
839                 /* if not successful, jump to actual trampoline */
840                 jump_cmpxchg_failed = code;
841                 amd64_branch8 (code, X86_CC_NZ, -1, 1);
842                 /* if successful, return */
843                 amd64_ret (code);
844
845                 /* next case: synchronization->owner is not null */
846                 x86_patch (jump_tid, code);
847                 /* is synchronization->owner == TID? */
848                 amd64_alu_membase_reg_size (code, X86_CMP, AMD64_RCX, owner_offset, AMD64_RDX, 8);
849                 /* if not, jump to actual trampoline */
850                 jump_other_owner = code;
851                 amd64_branch8 (code, X86_CC_NZ, -1, 1);
852                 /* if yes, increment nest */
853                 amd64_inc_membase_size (code, AMD64_RCX, nest_offset, 4);
854                 /* return */
855                 amd64_ret (code);
856
857                 x86_patch (jump_obj_null, code);
858                 x86_patch (jump_sync_null, code);
859                 x86_patch (jump_cmpxchg_failed, code);
860                 x86_patch (jump_other_owner, code);
861         }
862
863         /* jump to the actual trampoline */
864 #if MONO_AMD64_ARG_REG1 != AMD64_RDI
865         amd64_mov_reg_reg (code, MONO_AMD64_ARG_REG1, AMD64_RDI);
866 #endif
867
868         if (aot) {
869                 *ji = mono_patch_info_list_prepend (*ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "specific_trampoline_monitor_enter");
870                 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RIP, 0, 8);
871                 amd64_jump_reg (code, AMD64_R11);
872         } else {
873                 tramp = mono_arch_create_specific_trampoline (NULL, MONO_TRAMPOLINE_MONITOR_ENTER, mono_get_root_domain (), NULL);
874
875                 /* jump to the actual trampoline */
876                 amd64_jump_code (code, tramp);
877         }
878
879         mono_arch_flush_icache (code, code - buf);
880         g_assert (code - buf <= tramp_size);
881
882         *code_size = code - buf;
883
884         return buf;
885 }
886
887 gpointer
888 mono_arch_create_monitor_exit_trampoline (void)
889 {
890         guint32 code_size;
891         MonoJumpInfo *ji;
892
893         return mono_arch_create_monitor_exit_trampoline_full (&code_size, &ji, FALSE);
894 }
895
896 gpointer
897 mono_arch_create_monitor_exit_trampoline_full (guint32 *code_size, MonoJumpInfo **ji, gboolean aot)
898 {
899         guint8 *tramp;
900         guint8 *code, *buf;
901         guint8 *jump_obj_null, *jump_have_waiters;
902         guint8 *jump_next;
903         int tramp_size;
904         int owner_offset, nest_offset, entry_count_offset;
905
906         *ji = NULL;
907
908         g_assert (MONO_ARCH_MONITOR_OBJECT_REG == AMD64_RDI);
909
910         mono_monitor_threads_sync_members_offset (&owner_offset, &nest_offset, &entry_count_offset);
911         g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (owner_offset) == sizeof (gpointer));
912         g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (nest_offset) == sizeof (guint32));
913         g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (entry_count_offset) == sizeof (gint32));
914         owner_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (owner_offset);
915         nest_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (nest_offset);
916         entry_count_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (entry_count_offset);
917
918         tramp_size = 94;
919
920         code = buf = mono_global_codeman_reserve (tramp_size);
921
922         if (mono_thread_get_tls_offset () != -1) {
923                 /* MonoObject* obj is in RDI */
924                 /* is obj null? */
925                 amd64_test_reg_reg (code, AMD64_RDI, AMD64_RDI);
926                 /* if yes, jump to actual trampoline */
927                 jump_obj_null = code;
928                 amd64_branch8 (code, X86_CC_Z, -1, 1);
929
930                 /* load obj->synchronization to RCX */
931                 amd64_mov_reg_membase (code, AMD64_RCX, AMD64_RDI, G_STRUCT_OFFSET (MonoObject, synchronisation), 8);
932                 /* is synchronization null? */
933                 amd64_test_reg_reg (code, AMD64_RCX, AMD64_RCX);
934                 /* if not, jump to next case */
935                 jump_next = code;
936                 amd64_branch8 (code, X86_CC_NZ, -1, 1);
937                 /* if yes, just return */
938                 amd64_ret (code);
939
940                 /* next case: synchronization is not null */
941                 x86_patch (jump_next, code);
942                 /* load MonoThread* into RDX */
943                 code = mono_amd64_emit_tls_get (code, AMD64_RDX, mono_thread_get_tls_offset ());
944                 /* load TID into RDX */
945                 amd64_mov_reg_membase (code, AMD64_RDX, AMD64_RDX, G_STRUCT_OFFSET (MonoThread, tid), 8);
946                 /* is synchronization->owner == TID */
947                 amd64_alu_membase_reg_size (code, X86_CMP, AMD64_RCX, owner_offset, AMD64_RDX, 8);
948                 /* if yes, jump to next case */
949                 jump_next = code;
950                 amd64_branch8 (code, X86_CC_Z, -1, 1);
951                 /* if not, just return */
952                 amd64_ret (code);
953
954                 /* next case: synchronization->owner == TID */
955                 x86_patch (jump_next, code);
956                 /* is synchronization->nest == 1 */
957                 amd64_alu_membase_imm_size (code, X86_CMP, AMD64_RCX, nest_offset, 1, 4);
958                 /* if not, jump to next case */
959                 jump_next = code;
960                 amd64_branch8 (code, X86_CC_NZ, -1, 1);
961                 /* if yes, is synchronization->entry_count zero? */
962                 amd64_alu_membase_imm_size (code, X86_CMP, AMD64_RCX, entry_count_offset, 0, 4);
963                 /* if not, jump to actual trampoline */
964                 jump_have_waiters = code;
965                 amd64_branch8 (code, X86_CC_NZ, -1 , 1);
966                 /* if yes, set synchronization->owner to null and return */
967                 amd64_mov_membase_imm (code, AMD64_RCX, owner_offset, 0, 8);
968                 amd64_ret (code);
969
970                 /* next case: synchronization->nest is not 1 */
971                 x86_patch (jump_next, code);
972                 /* decrease synchronization->nest and return */
973                 amd64_dec_membase_size (code, AMD64_RCX, nest_offset, 4);
974                 amd64_ret (code);
975
976                 x86_patch (jump_obj_null, code);
977                 x86_patch (jump_have_waiters, code);
978         }
979
980         /* jump to the actual trampoline */
981 #if MONO_AMD64_ARG_REG1 != AMD64_RDI
982         amd64_mov_reg_reg (code, MONO_AMD64_ARG_REG1, AMD64_RDI);
983 #endif
984
985         if (aot) {
986                 *ji = mono_patch_info_list_prepend (*ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "specific_trampoline_monitor_exit");
987                 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RIP, 0, 8);
988                 amd64_jump_reg (code, AMD64_R11);
989         } else {
990                 tramp = mono_arch_create_specific_trampoline (NULL, MONO_TRAMPOLINE_MONITOR_EXIT, mono_get_root_domain (), NULL);
991                 amd64_jump_code (code, tramp);
992         }
993
994         mono_arch_flush_icache (code, code - buf);
995         g_assert (code - buf <= tramp_size);
996
997         *code_size = code - buf;
998
999         return buf;
1000 }
1001 #endif
1002
1003 void
1004 mono_arch_invalidate_method (MonoJitInfo *ji, void *func, gpointer func_arg)
1005 {
1006         /* FIXME: This is not thread safe */
1007         guint8 *code = ji->code_start;
1008
1009         amd64_mov_reg_imm (code, AMD64_ARG_REG1, func_arg);
1010         amd64_mov_reg_imm (code, AMD64_R11, func);
1011
1012         x86_push_imm (code, (guint64)func_arg);
1013         amd64_call_reg (code, AMD64_R11);
1014 }