2 * tramp-amd64.c: JIT trampoline code for amd64
5 * Dietmar Maurer (dietmar@ximian.com)
6 * Zoltan Varga (vargaz@gmail.com)
8 * (C) 2001 Ximian, Inc.
9 * Copyright 2003-2011 Novell, Inc (http://www.novell.com)
10 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
16 #include <mono/metadata/appdomain.h>
17 #include <mono/metadata/marshal.h>
18 #include <mono/metadata/tabledefs.h>
19 #include <mono/metadata/mono-debug-debugger.h>
20 #include <mono/metadata/monitor.h>
21 #include <mono/metadata/monitor.h>
22 #include <mono/metadata/gc-internal.h>
23 #include <mono/arch/amd64/amd64-codegen.h>
25 #include <mono/utils/memcheck.h>
28 #include "mini-amd64.h"
30 #if defined(__native_client_codegen__) && defined(__native_client__)
32 #include <sys/nacl_syscalls.h>
35 #define IS_REX(inst) (((inst) >= 0x40) && ((inst) <= 0x4f))
37 static guint8
* nullified_class_init_trampoline
;
40 * mono_arch_get_unbox_trampoline:
42 * @addr: pointer to native code for @m
44 * when value type methods are called through the vtable we need to unbox the
45 * this argument. This method returns a pointer to a trampoline which does
46 * unboxing before calling the method
49 mono_arch_get_unbox_trampoline (MonoMethod
*m
, gpointer addr
)
54 MonoDomain
*domain
= mono_domain_get ();
56 this_reg
= mono_arch_get_this_arg_reg (NULL
);
58 start
= code
= mono_domain_code_reserve (domain
, 20);
60 amd64_alu_reg_imm (code
, X86_ADD
, this_reg
, sizeof (MonoObject
));
61 /* FIXME: Optimize this */
62 amd64_mov_reg_imm (code
, AMD64_RAX
, addr
);
63 amd64_jump_reg (code
, AMD64_RAX
);
64 g_assert ((code
- start
) < 20);
66 nacl_domain_code_validate (domain
, &start
, 20, &code
);
68 mono_arch_flush_icache (start
, code
- start
);
74 * mono_arch_get_static_rgctx_trampoline:
76 * Create a trampoline which sets RGCTX_REG to MRGCTX, then jumps to ADDR.
79 mono_arch_get_static_rgctx_trampoline (MonoMethod
*m
, MonoMethodRuntimeGenericContext
*mrgctx
, gpointer addr
)
84 MonoDomain
*domain
= mono_domain_get ();
86 #ifdef MONO_ARCH_NOMAP32BIT
89 /* AOTed code could still have a non-32 bit address */
90 if ((((guint64
)addr
) >> 32) == 0)
96 start
= code
= mono_domain_code_reserve (domain
, buf_len
);
98 amd64_mov_reg_imm (code
, MONO_ARCH_RGCTX_REG
, mrgctx
);
99 amd64_jump_code (code
, addr
);
100 g_assert ((code
- start
) < buf_len
);
102 nacl_domain_code_validate (domain
, &start
, buf_len
, &code
);
103 mono_arch_flush_icache (start
, code
- start
);
109 mono_arch_get_llvm_imt_trampoline (MonoDomain
*domain
, MonoMethod
*m
, int vt_offset
)
111 guint8
*code
, *start
;
117 start
= code
= mono_domain_code_reserve (domain
, buf_len
);
119 this_reg
= mono_arch_get_this_arg_reg (NULL
);
122 amd64_mov_reg_imm (code
, MONO_ARCH_IMT_REG
, m
);
123 /* Load vtable address */
124 amd64_mov_reg_membase (code
, AMD64_RAX
, this_reg
, 0, 8);
125 amd64_jump_membase (code
, AMD64_RAX
, vt_offset
);
128 g_assert ((code
- start
) < buf_len
);
130 nacl_domain_code_validate (domain
, &start
, buf_len
, &code
);
132 mono_arch_flush_icache (start
, code
- start
);
138 * mono_arch_patch_callsite:
140 * Patch the callsite whose address is given by ORIG_CODE so it calls ADDR. ORIG_CODE
141 * points to the pc right after the call.
144 mono_arch_patch_callsite (guint8
*method_start
, guint8
*orig_code
, guint8
*addr
)
146 #if defined(__default_codegen__)
149 gboolean can_write
= mono_breakpoint_clean_code (method_start
, orig_code
, 14, buf
, sizeof (buf
));
153 /* mov 64-bit imm into r11 (followed by call reg?) or direct call*/
154 if (((code
[-13] == 0x49) && (code
[-12] == 0xbb)) || (code
[-5] == 0xe8)) {
155 if (code
[-5] != 0xe8) {
157 InterlockedExchangePointer ((gpointer
*)(orig_code
- 11), addr
);
158 VALGRIND_DISCARD_TRANSLATIONS (orig_code
- 11, sizeof (gpointer
));
161 gboolean disp_32bit
= ((((gint64
)addr
- (gint64
)orig_code
)) < (1 << 30)) && ((((gint64
)addr
- (gint64
)orig_code
)) > -(1 << 30));
163 if ((((guint64
)(addr
)) >> 32) != 0 && !disp_32bit
) {
164 #ifdef MONO_ARCH_NOMAP32BIT
165 /* Print some diagnostics */
166 MonoJitInfo
*ji
= mono_jit_info_table_find (mono_domain_get (), (char*)orig_code
);
168 fprintf (stderr
, "At %s, offset 0x%zx\n", mono_method_full_name (ji
->method
, TRUE
), (guint8
*)orig_code
- (guint8
*)ji
->code_start
);
169 fprintf (stderr
, "Addr: %p\n", addr
);
170 ji
= mono_jit_info_table_find (mono_domain_get (), (char*)addr
);
172 fprintf (stderr
, "Callee: %s\n", mono_method_full_name (ji
->method
, TRUE
));
173 g_assert_not_reached ();
176 * This might happen when calling AOTed code. Create a thunk.
178 guint8
*thunk_start
, *thunk_code
;
180 thunk_start
= thunk_code
= mono_domain_code_reserve (mono_domain_get (), 32);
181 amd64_jump_membase (thunk_code
, AMD64_RIP
, 0);
182 *(guint64
*)thunk_code
= (guint64
)addr
;
184 g_assert ((((guint64
)(addr
)) >> 32) == 0);
185 mono_arch_flush_icache (thunk_start
, thunk_code
- thunk_start
);
189 InterlockedExchange ((gint32
*)(orig_code
- 4), ((gint64
)addr
- (gint64
)orig_code
));
190 VALGRIND_DISCARD_TRANSLATIONS (orig_code
- 5, 4);
194 else if ((code
[-7] == 0x41) && (code
[-6] == 0xff) && (code
[-5] == 0x15)) {
195 /* call *<OFFSET>(%rip) */
196 gpointer
*got_entry
= (gpointer
*)((guint8
*)orig_code
+ (*(guint32
*)(orig_code
- 4)));
198 InterlockedExchangePointer (got_entry
, addr
);
199 VALGRIND_DISCARD_TRANSLATIONS (orig_code
- 5, sizeof (gpointer
));
202 #elif defined(__native_client__)
203 /* These are essentially the same 2 cases as above, modified for NaCl*/
205 /* Target must be bundle-aligned */
206 g_assert (((guint32
)addr
& kNaClAlignmentMask
) == 0);
207 /* Return target must be bundle-aligned */
208 g_assert (((guint32
)orig_code
& kNaClAlignmentMask
) == 0);
210 if (orig_code
[-5] == 0xe8) {
213 gint32 offset
= (gint32
)addr
- (gint32
)orig_code
;
214 guint8 buf
[sizeof(gint32
)];
215 *((gint32
*)(buf
)) = offset
;
216 ret
= nacl_dyncode_modify (orig_code
- sizeof(gint32
), buf
, sizeof(gint32
));
220 else if (is_nacl_call_reg_sequence (orig_code
- 10) && orig_code
[-16] == 0x41 && orig_code
[-15] == 0xbb) {
222 guint8 buf
[sizeof(gint32
)];
223 *((gint32
*)(buf
)) = addr
;
224 /* orig_code[-14] is the start of the immediate. */
225 ret
= nacl_dyncode_modify (orig_code
- 14, buf
, sizeof(gint32
));
229 g_assert_not_reached ();
237 mono_arch_create_llvm_native_thunk (MonoDomain
*domain
, guint8
*addr
)
240 * The caller is LLVM code and the call displacement might exceed 32 bits. We can't determine the caller address, so
241 * we add a thunk every time.
242 * Since the caller is also allocated using the domain code manager, hopefully the displacement will fit into 32 bits.
243 * FIXME: Avoid this if possible if !MONO_ARCH_NOMAP32BIT and ADDR is 32 bits.
245 guint8
*thunk_start
, *thunk_code
;
247 thunk_start
= thunk_code
= mono_domain_code_reserve (mono_domain_get (), 32);
248 amd64_jump_membase (thunk_code
, AMD64_RIP
, 0);
249 *(guint64
*)thunk_code
= (guint64
)addr
;
251 mono_arch_flush_icache (thunk_start
, thunk_code
- thunk_start
);
256 mono_arch_patch_plt_entry (guint8
*code
, gpointer
*got
, mgreg_t
*regs
, guint8
*addr
)
259 gpointer
*plt_jump_table_entry
;
261 #if defined(__default_codegen__)
262 /* A PLT entry: jmp *<DISP>(%rip) */
263 g_assert (code
[0] == 0xff);
264 g_assert (code
[1] == 0x25);
266 disp
= *(gint32
*)(code
+ 2);
268 plt_jump_table_entry
= (gpointer
*)(code
+ 6 + disp
);
269 #elif defined(__native_client_codegen__)
271 /* mov <DISP>(%rip), %r11d */
274 /* Verify the 'mov' */
275 g_assert (code
[0] == 0x45);
276 g_assert (code
[1] == 0x8b);
277 g_assert (code
[2] == 0x1d);
279 disp
= *(gint32
*)(code
+ 3);
281 /* 7 = 3 (mov opcode) + 4 (disp) */
282 /* This needs to resolve to the target of the RIP-relative offset */
283 plt_jump_table_entry
= (gpointer
*)(code
+ 7 + disp
);
285 #endif /* __native_client_codegen__ */
288 InterlockedExchangePointer (plt_jump_table_entry
, addr
);
292 get_vcall_slot (guint8
*code
, mgreg_t
*regs
, int *displacement
)
296 MonoJitInfo
*ji
= NULL
;
299 /* code - 9 might be before the start of the method */
300 /* FIXME: Avoid this expensive call somehow */
301 ji
= mono_jit_info_table_find (mono_domain_get (), (char*)code
);
304 mono_breakpoint_clean_code (ji
? ji
->code_start
: NULL
, code
, 9, buf
, sizeof (buf
));
311 if ((code
[0] == 0x41) && (code
[1] == 0xff) && (code
[2] == 0x15)) {
312 /* call OFFSET(%rip) */
313 g_assert_not_reached ();
314 *displacement
= *(guint32
*)(code
+ 3);
315 return (gpointer
*)(code
+ disp
+ 7);
317 g_assert_not_reached ();
323 get_vcall_slot_addr (guint8
* code
, mgreg_t
*regs
)
327 vt
= get_vcall_slot (code
, regs
, &displacement
);
330 return (gpointer
*)((char*)vt
+ displacement
);
334 mono_arch_nullify_class_init_trampoline (guint8
*code
, mgreg_t
*regs
)
337 MonoJitInfo
*ji
= NULL
;
341 /* code - 7 might be before the start of the method */
342 /* FIXME: Avoid this expensive call somehow */
343 ji
= mono_jit_info_table_find (mono_domain_get (), (char*)code
);
346 can_write
= mono_breakpoint_clean_code (ji
? ji
->code_start
: NULL
, code
, 7, buf
, sizeof (buf
));
352 * A given byte sequence can match more than case here, so we have to be
353 * really careful about the ordering of the cases. Longer sequences
356 if ((buf
[0] == 0x41) && (buf
[1] == 0xff) && (buf
[2] == 0x15)) {
357 gpointer
*vtable_slot
;
359 /* call *<OFFSET>(%rip) */
360 vtable_slot
= get_vcall_slot_addr (code
, regs
);
361 g_assert (vtable_slot
);
363 *vtable_slot
= nullified_class_init_trampoline
;
364 } else if (buf
[2] == 0xe8) {
366 //guint8 *buf = code - 2;
369 * It would be better to replace the call with nops, but that doesn't seem
370 * to work on SMP machines even when the whole call is inside a cache line.
371 * Patching the call address seems to work.
381 mono_arch_patch_callsite (code
- 5, code
, nullified_class_init_trampoline
);
382 } else if ((buf
[5] == 0xff) && x86_modrm_mod (buf
[6]) == 3 && x86_modrm_reg (buf
[6]) == 2) {
384 /* Generated by the LLVM JIT or on platforms without MAP_32BIT set */
385 mono_arch_patch_callsite (code
- 13, code
, nullified_class_init_trampoline
);
386 } else if (buf
[4] == 0x90 || buf
[5] == 0xeb || buf
[6] == 0x66) {
387 /* Already changed by another thread */
390 printf ("Invalid trampoline sequence: %x %x %x %x %x %x %x\n", buf
[0], buf
[1], buf
[2], buf
[3],
391 buf
[4], buf
[5], buf
[6]);
392 g_assert_not_reached ();
397 mono_arch_nullify_plt_entry (guint8
*code
, mgreg_t
*regs
)
399 if (mono_aot_only
&& !nullified_class_init_trampoline
)
400 nullified_class_init_trampoline
= mono_aot_get_trampoline ("nullified_class_init_trampoline");
402 mono_arch_patch_plt_entry (code
, NULL
, regs
, nullified_class_init_trampoline
);
406 stack_unaligned (MonoTrampolineType tramp_type
)
408 printf ("%d\n", tramp_type
);
409 g_assert_not_reached ();
413 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type
, MonoTrampInfo
**info
, gboolean aot
)
415 guint8
*buf
, *code
, *tramp
, *br
[2], *r11_save_code
, *after_r11_save_code
;
416 int i
, lmf_offset
, offset
, res_offset
, arg_offset
, rax_offset
, tramp_offset
, saved_regs_offset
;
417 int saved_fpregs_offset
, rbp_offset
, framesize
, orig_rsp_to_rbp_offset
, cfa_offset
;
419 GSList
*unwind_ops
= NULL
;
420 MonoJumpInfo
*ji
= NULL
;
421 const guint kMaxCodeSize
= NACL_SIZE (600, 600*2);
423 #if defined(__native_client_codegen__)
424 const guint kNaClTrampOffset
= 17;
427 if (tramp_type
== MONO_TRAMPOLINE_JUMP
|| tramp_type
== MONO_TRAMPOLINE_HANDLER_BLOCK_GUARD
)
432 code
= buf
= mono_global_codeman_reserve (kMaxCodeSize
);
434 framesize
= kMaxCodeSize
+ sizeof (MonoLMF
);
435 framesize
= (framesize
+ (MONO_ARCH_FRAME_ALIGNMENT
- 1)) & ~ (MONO_ARCH_FRAME_ALIGNMENT
- 1);
437 orig_rsp_to_rbp_offset
= 0;
438 r11_save_code
= code
;
439 /* Reserve 5 bytes for the mov_membase_reg to save R11 */
441 after_r11_save_code
= code
;
443 // CFA = sp + 16 (the trampoline address is on the stack)
445 mono_add_unwind_op_def_cfa (unwind_ops
, code
, buf
, AMD64_RSP
, 16);
446 // IP saved at CFA - 8
447 mono_add_unwind_op_offset (unwind_ops
, code
, buf
, AMD64_RIP
, -8);
449 /* Pop the return address off the stack */
450 amd64_pop_reg (code
, AMD64_R11
);
451 orig_rsp_to_rbp_offset
+= sizeof(mgreg_t
);
453 cfa_offset
-= sizeof(mgreg_t
);
454 mono_add_unwind_op_def_cfa_offset (unwind_ops
, code
, buf
, cfa_offset
);
457 * Allocate a new stack frame
459 amd64_push_reg (code
, AMD64_RBP
);
460 cfa_offset
+= sizeof(mgreg_t
);
461 mono_add_unwind_op_def_cfa_offset (unwind_ops
, code
, buf
, cfa_offset
);
462 mono_add_unwind_op_offset (unwind_ops
, code
, buf
, AMD64_RBP
, - cfa_offset
);
464 orig_rsp_to_rbp_offset
-= sizeof(mgreg_t
);
465 amd64_mov_reg_reg (code
, AMD64_RBP
, AMD64_RSP
, sizeof(mgreg_t
));
466 mono_add_unwind_op_def_cfa_reg (unwind_ops
, code
, buf
, AMD64_RBP
);
467 amd64_alu_reg_imm (code
, X86_SUB
, AMD64_RSP
, framesize
);
470 rbp_offset
= - offset
;
472 offset
+= sizeof(mgreg_t
);
473 rax_offset
= - offset
;
475 offset
+= sizeof(mgreg_t
);
476 tramp_offset
= - offset
;
478 offset
+= sizeof(gpointer
);
479 arg_offset
= - offset
;
481 /* Compute the trampoline address from the return address */
483 #if defined(__default_codegen__)
484 /* 7 = length of call *<offset>(rip) */
485 amd64_alu_reg_imm (code
, X86_SUB
, AMD64_R11
, 7);
486 #elif defined(__native_client_codegen__)
487 amd64_alu_reg_imm (code
, X86_SUB
, AMD64_R11
, kNaClTrampOffset
);
490 /* 5 = length of amd64_call_membase () */
491 amd64_alu_reg_imm (code
, X86_SUB
, AMD64_R11
, 5);
493 amd64_mov_membase_reg (code
, AMD64_RBP
, tramp_offset
, AMD64_R11
, sizeof(gpointer
));
495 offset
+= sizeof(mgreg_t
);
496 res_offset
= - offset
;
498 /* Save all registers */
500 offset
+= AMD64_NREG
* sizeof(mgreg_t
);
501 saved_regs_offset
= - offset
;
502 for (i
= 0; i
< AMD64_NREG
; ++i
) {
503 if (i
== AMD64_RBP
) {
504 /* RAX is already saved */
505 amd64_mov_reg_membase (code
, AMD64_RAX
, AMD64_RBP
, rbp_offset
, sizeof(mgreg_t
));
506 amd64_mov_membase_reg (code
, AMD64_RBP
, saved_regs_offset
+ (i
* sizeof(mgreg_t
)), AMD64_RAX
, sizeof(mgreg_t
));
507 } else if (i
!= AMD64_R11
) {
508 amd64_mov_membase_reg (code
, AMD64_RBP
, saved_regs_offset
+ (i
* sizeof(mgreg_t
)), i
, sizeof(mgreg_t
));
510 /* We have to save R11 right at the start of
511 the trampoline code because it's used as a
513 amd64_mov_membase_reg (r11_save_code
, AMD64_RSP
, saved_regs_offset
+ orig_rsp_to_rbp_offset
+ (i
* sizeof(mgreg_t
)), i
, sizeof(mgreg_t
));
514 g_assert (r11_save_code
== after_r11_save_code
);
517 offset
+= 8 * sizeof(mgreg_t
);
518 saved_fpregs_offset
= - offset
;
519 for (i
= 0; i
< 8; ++i
)
520 amd64_movsd_membase_reg (code
, AMD64_RBP
, saved_fpregs_offset
+ (i
* sizeof(mgreg_t
)), i
);
522 /* Check that the stack is aligned */
523 #if defined(__default_codegen__)
524 amd64_mov_reg_reg (code
, AMD64_R11
, AMD64_RSP
, sizeof (mgreg_t
));
525 amd64_alu_reg_imm (code
, X86_AND
, AMD64_R11
, 15);
526 amd64_alu_reg_imm (code
, X86_CMP
, AMD64_R11
, 0);
528 amd64_branch_disp (code
, X86_CC_Z
, 0, FALSE
);
530 amd64_mov_reg_imm (code
, AMD64_R11
, 0);
531 amd64_mov_reg_membase (code
, AMD64_R11
, AMD64_R11
, 0, 8);
533 amd64_mov_reg_imm (code
, AMD64_RDI
, tramp_type
);
534 amd64_mov_reg_imm (code
, AMD64_R11
, stack_unaligned
);
535 amd64_call_reg (code
, AMD64_R11
);
537 mono_amd64_patch (br
[0], code
);
538 //amd64_breakpoint (code);
541 if (tramp_type
!= MONO_TRAMPOLINE_GENERIC_CLASS_INIT
&&
542 tramp_type
!= MONO_TRAMPOLINE_MONITOR_ENTER
&&
543 tramp_type
!= MONO_TRAMPOLINE_MONITOR_EXIT
&&
544 tramp_type
!= MONO_TRAMPOLINE_HANDLER_BLOCK_GUARD
) {
545 /* Obtain the trampoline argument which is encoded in the instruction stream */
547 /* Load the GOT offset */
548 amd64_mov_reg_membase (code
, AMD64_R11
, AMD64_RBP
, tramp_offset
, sizeof(gpointer
));
549 #if defined(__default_codegen__)
550 amd64_mov_reg_membase (code
, AMD64_RAX
, AMD64_R11
, 7, 4);
551 #elif defined(__native_client_codegen__)
552 /* The arg is hidden in a "push imm32" instruction, */
553 /* add one to skip the opcode. */
554 amd64_mov_reg_membase (code
, AMD64_RAX
, AMD64_R11
, kNaClTrampOffset
+1, 4);
556 /* Compute the address of the GOT slot */
557 amd64_alu_reg_reg_size (code
, X86_ADD
, AMD64_R11
, AMD64_RAX
, sizeof(gpointer
));
559 amd64_mov_reg_membase (code
, AMD64_R11
, AMD64_R11
, 0, sizeof(gpointer
));
561 amd64_mov_reg_membase (code
, AMD64_R11
, AMD64_RBP
, tramp_offset
, sizeof(gpointer
));
562 #if defined(__default_codegen__)
563 amd64_mov_reg_membase (code
, AMD64_RAX
, AMD64_R11
, 5, 1);
564 amd64_widen_reg (code
, AMD64_RAX
, AMD64_RAX
, TRUE
, FALSE
);
565 amd64_alu_reg_imm_size (code
, X86_CMP
, AMD64_RAX
, 4, 1);
567 x86_branch8 (code
, X86_CC_NE
, 6, FALSE
);
568 /* 32 bit immediate */
569 amd64_mov_reg_membase (code
, AMD64_R11
, AMD64_R11
, 6, 4);
571 x86_jump8 (code
, 10);
572 /* 64 bit immediate */
573 mono_amd64_patch (br
[0], code
);
574 amd64_mov_reg_membase (code
, AMD64_R11
, AMD64_R11
, 6, 8);
575 mono_amd64_patch (br
[1], code
);
576 #elif defined(__native_client_codegen__)
577 /* All args are 32-bit pointers in NaCl */
578 amd64_mov_reg_membase (code
, AMD64_R11
, AMD64_R11
, 6, 4);
581 amd64_mov_membase_reg (code
, AMD64_RBP
, arg_offset
, AMD64_R11
, sizeof(gpointer
));
583 amd64_mov_reg_membase (code
, AMD64_R11
, AMD64_RBP
, saved_regs_offset
+ (MONO_AMD64_ARG_REG1
* sizeof(mgreg_t
)), sizeof(mgreg_t
));
584 amd64_mov_membase_reg (code
, AMD64_RBP
, arg_offset
, AMD64_R11
, sizeof(gpointer
));
589 offset
+= sizeof (MonoLMF
);
590 lmf_offset
= - offset
;
594 amd64_mov_reg_membase (code
, AMD64_R11
, AMD64_RBP
, 8, sizeof(gpointer
));
596 amd64_mov_reg_imm (code
, AMD64_R11
, 0);
597 amd64_mov_membase_reg (code
, AMD64_RBP
, lmf_offset
+ G_STRUCT_OFFSET (MonoLMF
, rip
), AMD64_R11
, sizeof(mgreg_t
));
599 amd64_mov_reg_membase (code
, AMD64_R11
, AMD64_RSP
, framesize
, sizeof(mgreg_t
));
600 amd64_mov_membase_reg (code
, AMD64_RBP
, lmf_offset
+ G_STRUCT_OFFSET (MonoLMF
, rbp
), AMD64_R11
, sizeof(mgreg_t
));
602 amd64_mov_reg_reg (code
, AMD64_R11
, AMD64_RSP
, sizeof(mgreg_t
));
603 amd64_alu_reg_imm (code
, X86_ADD
, AMD64_R11
, framesize
+ 16);
604 amd64_mov_membase_reg (code
, AMD64_RBP
, lmf_offset
+ G_STRUCT_OFFSET (MonoLMF
, rsp
), AMD64_R11
, sizeof(mgreg_t
));
606 if (tramp_type
== MONO_TRAMPOLINE_JIT
|| tramp_type
== MONO_TRAMPOLINE_JUMP
) {
607 amd64_mov_reg_membase (code
, AMD64_R11
, AMD64_RBP
, arg_offset
, sizeof(gpointer
));
608 amd64_mov_membase_reg (code
, AMD64_RBP
, lmf_offset
+ G_STRUCT_OFFSET (MonoLMF
, method
), AMD64_R11
, sizeof(gpointer
));
610 amd64_mov_membase_imm (code
, AMD64_RBP
, lmf_offset
+ G_STRUCT_OFFSET (MonoLMF
, method
), 0, sizeof(gpointer
));
612 /* Save callee saved regs */
614 amd64_mov_membase_reg (code
, AMD64_RBP
, lmf_offset
+ G_STRUCT_OFFSET (MonoLMF
, rdi
), AMD64_RDI
, sizeof(mgreg_t
));
615 amd64_mov_membase_reg (code
, AMD64_RBP
, lmf_offset
+ G_STRUCT_OFFSET (MonoLMF
, rsi
), AMD64_RSI
, sizeof(mgreg_t
));
617 amd64_mov_membase_reg (code
, AMD64_RBP
, lmf_offset
+ G_STRUCT_OFFSET (MonoLMF
, rbx
), AMD64_RBX
, sizeof(mgreg_t
));
618 amd64_mov_membase_reg (code
, AMD64_RBP
, lmf_offset
+ G_STRUCT_OFFSET (MonoLMF
, r12
), AMD64_R12
, sizeof(mgreg_t
));
619 amd64_mov_membase_reg (code
, AMD64_RBP
, lmf_offset
+ G_STRUCT_OFFSET (MonoLMF
, r13
), AMD64_R13
, sizeof(mgreg_t
));
620 amd64_mov_membase_reg (code
, AMD64_RBP
, lmf_offset
+ G_STRUCT_OFFSET (MonoLMF
, r14
), AMD64_R14
, sizeof(mgreg_t
));
621 amd64_mov_membase_reg (code
, AMD64_RBP
, lmf_offset
+ G_STRUCT_OFFSET (MonoLMF
, r15
), AMD64_R15
, sizeof(mgreg_t
));
624 code
= mono_arch_emit_load_aotconst (buf
, code
, &ji
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, "mono_get_lmf_addr");
626 amd64_mov_reg_imm (code
, AMD64_R11
, mono_get_lmf_addr
);
628 amd64_call_reg (code
, AMD64_R11
);
631 amd64_mov_membase_reg (code
, AMD64_RBP
, lmf_offset
+ G_STRUCT_OFFSET (MonoLMF
, lmf_addr
), AMD64_RAX
, sizeof(gpointer
));
632 /* Save previous_lmf */
633 /* Set the lowest bit to 1 to signal that this LMF has the ip field set */
634 amd64_mov_reg_membase (code
, AMD64_R11
, AMD64_RAX
, 0, sizeof(gpointer
));
635 amd64_alu_reg_imm_size (code
, X86_ADD
, AMD64_R11
, 1, sizeof(gpointer
));
636 amd64_mov_membase_reg (code
, AMD64_RBP
, lmf_offset
+ G_STRUCT_OFFSET (MonoLMF
, previous_lmf
), AMD64_R11
, sizeof(gpointer
));
638 amd64_lea_membase (code
, AMD64_R11
, AMD64_RBP
, lmf_offset
);
639 amd64_mov_membase_reg (code
, AMD64_RAX
, 0, AMD64_R11
, sizeof(gpointer
));
643 /* Arg1 is the pointer to the saved registers */
644 amd64_lea_membase (code
, AMD64_ARG_REG1
, AMD64_RBP
, saved_regs_offset
);
646 /* Arg2 is the address of the calling code */
648 amd64_mov_reg_membase (code
, AMD64_ARG_REG2
, AMD64_RBP
, 8, sizeof(gpointer
));
650 amd64_mov_reg_imm (code
, AMD64_ARG_REG2
, 0);
652 /* Arg3 is the method/vtable ptr */
653 amd64_mov_reg_membase (code
, AMD64_ARG_REG3
, AMD64_RBP
, arg_offset
, sizeof(gpointer
));
655 /* Arg4 is the trampoline address */
656 amd64_mov_reg_membase (code
, AMD64_ARG_REG4
, AMD64_RBP
, tramp_offset
, sizeof(gpointer
));
659 char *icall_name
= g_strdup_printf ("trampoline_func_%d", tramp_type
);
660 code
= mono_arch_emit_load_aotconst (buf
, code
, &ji
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, icall_name
);
662 tramp
= (guint8
*)mono_get_trampoline_func (tramp_type
);
663 amd64_mov_reg_imm (code
, AMD64_R11
, tramp
);
665 amd64_call_reg (code
, AMD64_R11
);
667 /* Check for thread interruption */
668 /* This is not perf critical code so no need to check the interrupt flag */
670 * Have to call the _force_ variant, since there could be a protected wrapper on the top of the stack.
672 amd64_mov_membase_reg (code
, AMD64_RBP
, res_offset
, AMD64_RAX
, sizeof(mgreg_t
));
674 code
= mono_arch_emit_load_aotconst (buf
, code
, &ji
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, "mono_thread_force_interruption_checkpoint");
676 amd64_mov_reg_imm (code
, AMD64_R11
, (guint8
*)mono_thread_force_interruption_checkpoint
);
678 amd64_call_reg (code
, AMD64_R11
);
680 amd64_mov_reg_membase (code
, AMD64_RAX
, AMD64_RBP
, res_offset
, sizeof(mgreg_t
));
684 amd64_mov_reg_membase (code
, AMD64_RCX
, AMD64_RBP
, lmf_offset
+ G_STRUCT_OFFSET (MonoLMF
, previous_lmf
), sizeof(gpointer
));
685 amd64_alu_reg_imm_size (code
, X86_SUB
, AMD64_RCX
, 1, sizeof(gpointer
));
686 amd64_mov_reg_membase (code
, AMD64_R11
, AMD64_RBP
, lmf_offset
+ G_STRUCT_OFFSET (MonoLMF
, lmf_addr
), sizeof(gpointer
));
687 amd64_mov_membase_reg (code
, AMD64_R11
, 0, AMD64_RCX
, sizeof(gpointer
));
690 * Save rax to the stack, after the leave instruction, this will become part of
693 amd64_mov_membase_reg (code
, AMD64_RBP
, rax_offset
, AMD64_RAX
, sizeof(mgreg_t
));
695 /* Restore argument registers, r10 (imt method/rgxtx)
696 and rax (needed for direct calls to C vararg functions). */
697 for (i
= 0; i
< AMD64_NREG
; ++i
)
698 if (AMD64_IS_ARGUMENT_REG (i
) || i
== AMD64_R10
|| i
== AMD64_RAX
)
699 amd64_mov_reg_membase (code
, i
, AMD64_RBP
, saved_regs_offset
+ (i
* sizeof(mgreg_t
)), sizeof(mgreg_t
));
701 for (i
= 0; i
< 8; ++i
)
702 amd64_movsd_reg_membase (code
, i
, AMD64_RBP
, saved_fpregs_offset
+ (i
* sizeof(mgreg_t
)));
707 if (MONO_TRAMPOLINE_TYPE_MUST_RETURN (tramp_type
)) {
709 amd64_mov_reg_membase (code
, AMD64_RAX
, AMD64_RSP
, rax_offset
- sizeof(mgreg_t
), sizeof(mgreg_t
));
712 /* call the compiled method using the saved rax */
713 amd64_jump_membase (code
, AMD64_RSP
, rax_offset
- sizeof(mgreg_t
));
716 g_assert ((code
- buf
) <= kMaxCodeSize
);
718 nacl_global_codeman_validate (&buf
, kMaxCodeSize
, &code
);
720 mono_arch_flush_icache (buf
, code
- buf
);
722 if (tramp_type
== MONO_TRAMPOLINE_CLASS_INIT
) {
723 /* Initialize the nullified class init trampoline used in the AOT case */
724 nullified_class_init_trampoline
= mono_arch_get_nullified_class_init_trampoline (NULL
);
728 *info
= mono_tramp_info_create (mono_get_generic_trampoline_name (tramp_type
), buf
, code
- buf
, ji
, unwind_ops
);
734 mono_arch_get_nullified_class_init_trampoline (MonoTrampInfo
**info
)
738 code
= buf
= mono_global_codeman_reserve (16);
741 nacl_global_codeman_validate(&buf
, 16, &code
);
743 mono_arch_flush_icache (buf
, code
- buf
);
746 *info
= mono_tramp_info_create (g_strdup_printf ("nullified_class_init_trampoline"), buf
, code
- buf
, NULL
, NULL
);
748 if (mono_jit_map_is_enabled ())
749 mono_emit_jit_tramp (buf
, code
- buf
, "nullified_class_init_trampoline");
755 mono_arch_create_specific_trampoline (gpointer arg1
, MonoTrampolineType tramp_type
, MonoDomain
*domain
, guint32
*code_len
)
757 guint8
*code
, *buf
, *tramp
;
759 gboolean far_addr
= FALSE
;
761 tramp
= mono_get_trampoline_code (tramp_type
);
763 #if defined(__default_codegen__)
764 if ((((guint64
)arg1
) >> 32) == 0)
769 code
= buf
= mono_domain_code_reserve_align (domain
, size
, 1);
771 if (((gint64
)tramp
- (gint64
)code
) >> 31 != 0 && ((gint64
)tramp
- (gint64
)code
) >> 31 != -1) {
772 #ifndef MONO_ARCH_NOMAP32BIT
773 g_assert_not_reached ();
777 code
= buf
= mono_domain_code_reserve_align (domain
, size
, 1);
779 #elif defined(__native_client_codegen__)
781 /* Aligning the call site below could */
782 /* add up to kNaClAlignment-1 bytes */
783 size
+= (kNaClAlignment
-1);
784 buf
= mono_domain_code_reserve_align (domain
, size
, kNaClAlignment
);
789 amd64_mov_reg_imm (code
, AMD64_R11
, tramp
);
790 amd64_call_reg (code
, AMD64_R11
);
792 amd64_call_code (code
, tramp
);
794 /* The trampoline code will obtain the argument from the instruction stream */
795 #if defined(__default_codegen__)
796 if ((((guint64
)arg1
) >> 32) == 0) {
798 *(guint32
*)(code
+ 1) = (gint64
)arg1
;
802 *(guint64
*)(code
+ 1) = (gint64
)arg1
;
805 #elif defined(__native_client_codegen__)
806 /* For NaCl, all tramp args are 32-bit because they're pointers */
807 *code
= 0x68; /* push imm32 */
808 *(guint32
*)(code
+ 1) = (gint32
)arg1
;
812 g_assert ((code
- buf
) <= size
);
817 nacl_domain_code_validate(domain
, &buf
, size
, &code
);
819 mono_arch_flush_icache (buf
, size
);
825 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot
, MonoTrampInfo
**info
, gboolean aot
)
829 guint8
**rgctx_null_jumps
;
834 MonoJumpInfo
*ji
= NULL
;
835 GSList
*unwind_ops
= NULL
;
837 mrgctx
= MONO_RGCTX_SLOT_IS_MRGCTX (slot
);
838 index
= MONO_RGCTX_SLOT_INDEX (slot
);
840 index
+= MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT
/ sizeof (gpointer
);
841 for (depth
= 0; ; ++depth
) {
842 int size
= mono_class_rgctx_get_array_size (depth
, mrgctx
);
844 if (index
< size
- 1)
849 tramp_size
= NACL_SIZE (64 + 8 * depth
, 128 + 8 * depth
);
851 code
= buf
= mono_global_codeman_reserve (tramp_size
);
853 unwind_ops
= mono_arch_get_cie_program ();
855 rgctx_null_jumps
= g_malloc (sizeof (guint8
*) * (depth
+ 2));
859 amd64_mov_reg_reg (code
, AMD64_RAX
, AMD64_ARG_REG1
, 8);
861 /* load rgctx ptr from vtable */
862 amd64_mov_reg_membase (code
, AMD64_RAX
, AMD64_ARG_REG1
, G_STRUCT_OFFSET (MonoVTable
, runtime_generic_context
), sizeof(gpointer
));
863 /* is the rgctx ptr null? */
864 amd64_test_reg_reg (code
, AMD64_RAX
, AMD64_RAX
);
865 /* if yes, jump to actual trampoline */
866 rgctx_null_jumps
[0] = code
;
867 amd64_branch8 (code
, X86_CC_Z
, -1, 1);
870 for (i
= 0; i
< depth
; ++i
) {
871 /* load ptr to next array */
872 if (mrgctx
&& i
== 0)
873 amd64_mov_reg_membase (code
, AMD64_RAX
, AMD64_RAX
, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT
, sizeof(gpointer
));
875 amd64_mov_reg_membase (code
, AMD64_RAX
, AMD64_RAX
, 0, sizeof(gpointer
));
876 /* is the ptr null? */
877 amd64_test_reg_reg (code
, AMD64_RAX
, AMD64_RAX
);
878 /* if yes, jump to actual trampoline */
879 rgctx_null_jumps
[i
+ 1] = code
;
880 amd64_branch8 (code
, X86_CC_Z
, -1, 1);
884 amd64_mov_reg_membase (code
, AMD64_RAX
, AMD64_RAX
, sizeof (gpointer
) * (index
+ 1), sizeof(gpointer
));
885 /* is the slot null? */
886 amd64_test_reg_reg (code
, AMD64_RAX
, AMD64_RAX
);
887 /* if yes, jump to actual trampoline */
888 rgctx_null_jumps
[depth
+ 1] = code
;
889 amd64_branch8 (code
, X86_CC_Z
, -1, 1);
890 /* otherwise return */
893 for (i
= mrgctx
? 1 : 0; i
<= depth
+ 1; ++i
)
894 mono_amd64_patch (rgctx_null_jumps
[i
], code
);
896 g_free (rgctx_null_jumps
);
898 /* move the rgctx pointer to the VTABLE register */
899 amd64_mov_reg_reg (code
, MONO_ARCH_VTABLE_REG
, AMD64_ARG_REG1
, sizeof(gpointer
));
902 code
= mono_arch_emit_load_aotconst (buf
, code
, &ji
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, g_strdup_printf ("specific_trampoline_lazy_fetch_%u", slot
));
903 amd64_jump_reg (code
, AMD64_R11
);
905 tramp
= mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot
), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH
, mono_get_root_domain (), NULL
);
907 /* jump to the actual trampoline */
908 amd64_jump_code (code
, tramp
);
911 nacl_global_codeman_validate (&buf
, tramp_size
, &code
);
912 mono_arch_flush_icache (buf
, code
- buf
);
914 g_assert (code
- buf
<= tramp_size
);
917 *info
= mono_tramp_info_create (mono_get_rgctx_fetch_trampoline_name (slot
), buf
, code
- buf
, ji
, unwind_ops
);
923 mono_arch_create_generic_class_init_trampoline (MonoTrampInfo
**info
, gboolean aot
)
927 static int byte_offset
= -1;
928 static guint8 bitmask
;
931 GSList
*unwind_ops
= NULL
;
932 MonoJumpInfo
*ji
= NULL
;
936 code
= buf
= mono_global_codeman_reserve (tramp_size
);
939 mono_marshal_find_bitfield_offset (MonoVTable
, initialized
, &byte_offset
, &bitmask
);
941 amd64_test_membase_imm_size (code
, MONO_AMD64_ARG_REG1
, byte_offset
, bitmask
, 1);
943 amd64_branch8 (code
, X86_CC_Z
, -1, 1);
947 x86_patch (jump
, code
);
950 code
= mono_arch_emit_load_aotconst (buf
, code
, &ji
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, "specific_trampoline_generic_class_init");
951 amd64_jump_reg (code
, AMD64_R11
);
953 tramp
= mono_arch_create_specific_trampoline (NULL
, MONO_TRAMPOLINE_GENERIC_CLASS_INIT
, mono_get_root_domain (), NULL
);
955 /* jump to the actual trampoline */
956 amd64_jump_code (code
, tramp
);
959 nacl_global_codeman_validate (&buf
, tramp_size
, &code
);
961 mono_arch_flush_icache (buf
, code
- buf
);
963 g_assert (code
- buf
<= tramp_size
);
966 *info
= mono_tramp_info_create (g_strdup_printf ("generic_class_init_trampoline"), buf
, code
- buf
, ji
, unwind_ops
);
971 #ifdef MONO_ARCH_MONITOR_OBJECT_REG
974 mono_arch_create_monitor_enter_trampoline (MonoTrampInfo
**info
, gboolean aot
)
978 guint8
*jump_obj_null
, *jump_sync_null
, *jump_cmpxchg_failed
, *jump_other_owner
, *jump_tid
, *jump_sync_thin_hash
= NULL
;
980 int owner_offset
, nest_offset
, dummy
;
981 MonoJumpInfo
*ji
= NULL
;
982 GSList
*unwind_ops
= NULL
;
984 g_assert (MONO_ARCH_MONITOR_OBJECT_REG
== AMD64_RDI
);
986 mono_monitor_threads_sync_members_offset (&owner_offset
, &nest_offset
, &dummy
);
987 g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (owner_offset
) == sizeof (gpointer
));
988 g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (nest_offset
) == sizeof (guint32
));
989 owner_offset
= MONO_THREADS_SYNC_MEMBER_OFFSET (owner_offset
);
990 nest_offset
= MONO_THREADS_SYNC_MEMBER_OFFSET (nest_offset
);
994 code
= buf
= mono_global_codeman_reserve (tramp_size
);
996 unwind_ops
= mono_arch_get_cie_program ();
998 if (mono_thread_get_tls_offset () != -1) {
999 /* MonoObject* obj is in RDI */
1001 amd64_test_reg_reg (code
, AMD64_RDI
, AMD64_RDI
);
1002 /* if yes, jump to actual trampoline */
1003 jump_obj_null
= code
;
1004 amd64_branch8 (code
, X86_CC_Z
, -1, 1);
1006 /* load obj->synchronization to RCX */
1007 amd64_mov_reg_membase (code
, AMD64_RCX
, AMD64_RDI
, G_STRUCT_OFFSET (MonoObject
, synchronisation
), 8);
1009 if (mono_gc_is_moving ()) {
1010 /*if bit zero is set it's a thin hash*/
1011 /*FIXME use testb encoding*/
1012 amd64_test_reg_imm (code
, AMD64_RCX
, 0x01);
1013 jump_sync_thin_hash
= code
;
1014 amd64_branch8 (code
, X86_CC_NE
, -1, 1);
1016 /*clear bits used by the gc*/
1017 amd64_alu_reg_imm (code
, X86_AND
, AMD64_RCX
, ~0x3);
1020 /* is synchronization null? */
1021 amd64_test_reg_reg (code
, AMD64_RCX
, AMD64_RCX
);
1022 /* if yes, jump to actual trampoline */
1023 jump_sync_null
= code
;
1024 amd64_branch8 (code
, X86_CC_Z
, -1, 1);
1026 /* load MonoInternalThread* into RDX */
1027 code
= mono_amd64_emit_tls_get (code
, AMD64_RDX
, mono_thread_get_tls_offset ());
1028 /* load TID into RDX */
1029 amd64_mov_reg_membase (code
, AMD64_RDX
, AMD64_RDX
, G_STRUCT_OFFSET (MonoInternalThread
, tid
), 8);
1031 /* is synchronization->owner null? */
1032 amd64_alu_membase_imm_size (code
, X86_CMP
, AMD64_RCX
, owner_offset
, 0, 8);
1033 /* if not, jump to next case */
1035 amd64_branch8 (code
, X86_CC_NZ
, -1, 1);
1037 /* if yes, try a compare-exchange with the TID */
1039 amd64_alu_reg_reg (code
, X86_XOR
, AMD64_RAX
, AMD64_RAX
);
1040 /* compare and exchange */
1041 amd64_prefix (code
, X86_LOCK_PREFIX
);
1042 amd64_cmpxchg_membase_reg_size (code
, AMD64_RCX
, owner_offset
, AMD64_RDX
, 8);
1043 /* if not successful, jump to actual trampoline */
1044 jump_cmpxchg_failed
= code
;
1045 amd64_branch8 (code
, X86_CC_NZ
, -1, 1);
1046 /* if successful, return */
1049 /* next case: synchronization->owner is not null */
1050 x86_patch (jump_tid
, code
);
1051 /* is synchronization->owner == TID? */
1052 amd64_alu_membase_reg_size (code
, X86_CMP
, AMD64_RCX
, owner_offset
, AMD64_RDX
, 8);
1053 /* if not, jump to actual trampoline */
1054 jump_other_owner
= code
;
1055 amd64_branch8 (code
, X86_CC_NZ
, -1, 1);
1056 /* if yes, increment nest */
1057 amd64_inc_membase_size (code
, AMD64_RCX
, nest_offset
, 4);
1061 x86_patch (jump_obj_null
, code
);
1062 if (jump_sync_thin_hash
)
1063 x86_patch (jump_sync_thin_hash
, code
);
1064 x86_patch (jump_sync_null
, code
);
1065 x86_patch (jump_cmpxchg_failed
, code
);
1066 x86_patch (jump_other_owner
, code
);
1069 /* jump to the actual trampoline */
1070 #if MONO_AMD64_ARG_REG1 != AMD64_RDI
1071 amd64_mov_reg_reg (code
, MONO_AMD64_ARG_REG1
, AMD64_RDI
);
1075 code
= mono_arch_emit_load_aotconst (buf
, code
, &ji
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, "specific_trampoline_monitor_enter");
1076 amd64_jump_reg (code
, AMD64_R11
);
1078 tramp
= mono_arch_create_specific_trampoline (NULL
, MONO_TRAMPOLINE_MONITOR_ENTER
, mono_get_root_domain (), NULL
);
1080 /* jump to the actual trampoline */
1081 amd64_jump_code (code
, tramp
);
1084 nacl_global_codeman_validate (&buf
, tramp_size
, &code
);
1086 mono_arch_flush_icache (code
, code
- buf
);
1087 g_assert (code
- buf
<= tramp_size
);
1090 *info
= mono_tramp_info_create (g_strdup_printf ("monitor_enter_trampoline"), buf
, code
- buf
, ji
, unwind_ops
);
1096 mono_arch_create_monitor_exit_trampoline (MonoTrampInfo
**info
, gboolean aot
)
1100 guint8
*jump_obj_null
, *jump_have_waiters
, *jump_sync_null
, *jump_not_owned
, *jump_sync_thin_hash
= NULL
;
1103 int owner_offset
, nest_offset
, entry_count_offset
;
1104 MonoJumpInfo
*ji
= NULL
;
1105 GSList
*unwind_ops
= NULL
;
1107 g_assert (MONO_ARCH_MONITOR_OBJECT_REG
== AMD64_RDI
);
1109 mono_monitor_threads_sync_members_offset (&owner_offset
, &nest_offset
, &entry_count_offset
);
1110 g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (owner_offset
) == sizeof (gpointer
));
1111 g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (nest_offset
) == sizeof (guint32
));
1112 g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (entry_count_offset
) == sizeof (gint32
));
1113 owner_offset
= MONO_THREADS_SYNC_MEMBER_OFFSET (owner_offset
);
1114 nest_offset
= MONO_THREADS_SYNC_MEMBER_OFFSET (nest_offset
);
1115 entry_count_offset
= MONO_THREADS_SYNC_MEMBER_OFFSET (entry_count_offset
);
1119 code
= buf
= mono_global_codeman_reserve (tramp_size
);
1121 unwind_ops
= mono_arch_get_cie_program ();
1123 if (mono_thread_get_tls_offset () != -1) {
1124 /* MonoObject* obj is in RDI */
1126 amd64_test_reg_reg (code
, AMD64_RDI
, AMD64_RDI
);
1127 /* if yes, jump to actual trampoline */
1128 jump_obj_null
= code
;
1129 amd64_branch8 (code
, X86_CC_Z
, -1, 1);
1131 /* load obj->synchronization to RCX */
1132 amd64_mov_reg_membase (code
, AMD64_RCX
, AMD64_RDI
, G_STRUCT_OFFSET (MonoObject
, synchronisation
), 8);
1134 if (mono_gc_is_moving ()) {
1135 /*if bit zero is set it's a thin hash*/
1136 /*FIXME use testb encoding*/
1137 amd64_test_reg_imm (code
, AMD64_RCX
, 0x01);
1138 jump_sync_thin_hash
= code
;
1139 amd64_branch8 (code
, X86_CC_NE
, -1, 1);
1141 /*clear bits used by the gc*/
1142 amd64_alu_reg_imm (code
, X86_AND
, AMD64_RCX
, ~0x3);
1145 /* is synchronization null? */
1146 amd64_test_reg_reg (code
, AMD64_RCX
, AMD64_RCX
);
1147 /* if yes, jump to actual trampoline */
1148 jump_sync_null
= code
;
1149 amd64_branch8 (code
, X86_CC_Z
, -1, 1);
1151 /* next case: synchronization is not null */
1152 /* load MonoInternalThread* into RDX */
1153 code
= mono_amd64_emit_tls_get (code
, AMD64_RDX
, mono_thread_get_tls_offset ());
1154 /* load TID into RDX */
1155 amd64_mov_reg_membase (code
, AMD64_RDX
, AMD64_RDX
, G_STRUCT_OFFSET (MonoInternalThread
, tid
), 8);
1156 /* is synchronization->owner == TID */
1157 amd64_alu_membase_reg_size (code
, X86_CMP
, AMD64_RCX
, owner_offset
, AMD64_RDX
, 8);
1158 /* if no, jump to actual trampoline */
1159 jump_not_owned
= code
;
1160 amd64_branch8 (code
, X86_CC_NZ
, -1, 1);
1162 /* next case: synchronization->owner == TID */
1163 /* is synchronization->nest == 1 */
1164 amd64_alu_membase_imm_size (code
, X86_CMP
, AMD64_RCX
, nest_offset
, 1, 4);
1165 /* if not, jump to next case */
1167 amd64_branch8 (code
, X86_CC_NZ
, -1, 1);
1168 /* if yes, is synchronization->entry_count zero? */
1169 amd64_alu_membase_imm_size (code
, X86_CMP
, AMD64_RCX
, entry_count_offset
, 0, 4);
1170 /* if not, jump to actual trampoline */
1171 jump_have_waiters
= code
;
1172 amd64_branch8 (code
, X86_CC_NZ
, -1 , 1);
1173 /* if yes, set synchronization->owner to null and return */
1174 amd64_mov_membase_imm (code
, AMD64_RCX
, owner_offset
, 0, 8);
1177 /* next case: synchronization->nest is not 1 */
1178 x86_patch (jump_next
, code
);
1179 /* decrease synchronization->nest and return */
1180 amd64_dec_membase_size (code
, AMD64_RCX
, nest_offset
, 4);
1183 x86_patch (jump_obj_null
, code
);
1184 x86_patch (jump_have_waiters
, code
);
1185 x86_patch (jump_not_owned
, code
);
1186 x86_patch (jump_sync_null
, code
);
1189 /* jump to the actual trampoline */
1190 #if MONO_AMD64_ARG_REG1 != AMD64_RDI
1191 amd64_mov_reg_reg (code
, MONO_AMD64_ARG_REG1
, AMD64_RDI
);
1195 code
= mono_arch_emit_load_aotconst (buf
, code
, &ji
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, "specific_trampoline_monitor_exit");
1196 amd64_jump_reg (code
, AMD64_R11
);
1198 tramp
= mono_arch_create_specific_trampoline (NULL
, MONO_TRAMPOLINE_MONITOR_EXIT
, mono_get_root_domain (), NULL
);
1199 amd64_jump_code (code
, tramp
);
1202 nacl_global_codeman_validate (&buf
, tramp_size
, &code
);
1204 mono_arch_flush_icache (code
, code
- buf
);
1205 g_assert (code
- buf
<= tramp_size
);
1208 *info
= mono_tramp_info_create (g_strdup_printf ("monitor_exit_trampoline"), buf
, code
- buf
, ji
, unwind_ops
);
1215 mono_arch_invalidate_method (MonoJitInfo
*ji
, void *func
, gpointer func_arg
)
1217 /* FIXME: This is not thread safe */
1218 guint8
*code
= ji
->code_start
;
1220 amd64_mov_reg_imm (code
, AMD64_ARG_REG1
, func_arg
);
1221 amd64_mov_reg_imm (code
, AMD64_R11
, func
);
1223 x86_push_imm (code
, (guint64
)func_arg
);
1224 amd64_call_reg (code
, AMD64_R11
);
1229 handler_block_trampoline_helper (gpointer
*ptr
)
1231 MonoJitTlsData
*jit_tls
= mono_native_tls_get_value (mono_jit_tls_id
);
1232 *ptr
= jit_tls
->handler_block_return_address
;
1236 mono_arch_create_handler_block_trampoline (void)
1238 guint8
*tramp
= mono_get_trampoline_code (MONO_TRAMPOLINE_HANDLER_BLOCK_GUARD
);
1240 int tramp_size
= 64;
1241 code
= buf
= mono_global_codeman_reserve (tramp_size
);
1244 This trampoline restore the call chain of the handler block then jumps into the code that deals with it.
1247 if (mono_get_jit_tls_offset () != -1) {
1248 code
= mono_amd64_emit_tls_get (code
, AMD64_RDI
, mono_get_jit_tls_offset ());
1249 amd64_mov_reg_membase (code
, AMD64_RDI
, AMD64_RDI
, G_STRUCT_OFFSET (MonoJitTlsData
, handler_block_return_address
), 8);
1250 /* Simulate a call */
1251 amd64_push_reg (code
, AMD64_RAX
);
1252 amd64_jump_code (code
, tramp
);
1254 /*Slow path uses a c helper*/
1255 amd64_mov_reg_reg (code
, AMD64_RDI
, AMD64_RSP
, 8);
1256 amd64_mov_reg_imm (code
, AMD64_RAX
, tramp
);
1257 amd64_push_reg (code
, AMD64_RAX
);
1258 amd64_jump_code (code
, handler_block_trampoline_helper
);
1261 mono_arch_flush_icache (buf
, code
- buf
);
1262 g_assert (code
- buf
<= tramp_size
);
1264 if (mono_jit_map_is_enabled ())
1265 mono_emit_jit_tramp (buf
, code
- buf
, "handler_block_trampoline");
1271 * mono_arch_get_call_target:
1273 * Return the address called by the code before CODE if exists.
1276 mono_arch_get_call_target (guint8
*code
)
1278 if (code
[-5] == 0xe8) {
1279 guint32 disp
= *(guint32
*)(code
- 4);
1280 guint8
*target
= code
+ disp
;
1289 * mono_arch_get_plt_info_offset:
1291 * Return the PLT info offset belonging to the plt entry PLT_ENTRY.
1294 mono_arch_get_plt_info_offset (guint8
*plt_entry
, mgreg_t
*regs
, guint8
*code
)
1296 #if defined(__native_client__) || defined(__native_client_codegen__)
1297 /* 18 = 3 (mov opcode) + 4 (disp) + 10 (nacljmp) + 1 (push opcode) */
1298 /* See aot-compiler.c arch_emit_plt_entry for details. */
1299 return *(guint32
*)(plt_entry
+ 18);
1301 return *(guint32
*)(plt_entry
+ 6);