X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Ftramp-x86.c;h=a1961773a488f59f2e9e3fc1762770c3dffd9819;hb=9eb3b536aa7e387374da62201584713bc922ee9d;hp=731dfb5b046fafb853b943f67d5cf8a6ea7f41a0;hpb=cc9fa96a3163b4ac0b2df0726c72963017d802d6;p=mono.git diff --git a/mono/mini/tramp-x86.c b/mono/mini/tramp-x86.c index 731dfb5b046..a1961773a48 100644 --- a/mono/mini/tramp-x86.c +++ b/mono/mini/tramp-x86.c @@ -16,11 +16,10 @@ #include #include #include +#include #include -#ifdef HAVE_VALGRIND_MEMCHECK_H -#include -#endif +#include #include "mini.h" #include "mini-x86.h" @@ -29,6 +28,7 @@ static guint8* nullified_class_init_trampoline; /* * mono_arch_get_unbox_trampoline: + * @gsctx: the generic sharing context * @m: method pointer * @addr: pointer to native code for @m * @@ -37,18 +37,16 @@ static guint8* nullified_class_init_trampoline; * unboxing before calling the method */ gpointer -mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr) +mono_arch_get_unbox_trampoline (MonoGenericSharingContext *gsctx, MonoMethod *m, gpointer addr) { guint8 *code, *start; int this_pos = 4; MonoDomain *domain = mono_domain_get (); - if (!mono_method_signature (m)->ret->byref && MONO_TYPE_ISSTRUCT (mono_method_signature (m)->ret)) + if (MONO_TYPE_ISSTRUCT (mono_method_signature (m)->ret)) this_pos = 8; - mono_domain_lock (domain); - start = code = mono_code_manager_reserve (domain->code_mp, 16); - mono_domain_unlock (domain); + start = code = mono_domain_code_reserve (domain, 16); x86_alu_membase_imm (code, X86_ADD, X86_ESP, this_pos, sizeof (MonoObject)); x86_jump_code (code, addr); @@ -57,6 +55,55 @@ mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr) return start; } +gpointer +mono_arch_get_static_rgctx_trampoline (MonoMethod *m, MonoMethodRuntimeGenericContext *mrgctx, gpointer addr) +{ + guint8 *code, *start; + int buf_len; + + MonoDomain *domain = mono_domain_get (); + + buf_len = 10; + + start = code = mono_domain_code_reserve (domain, buf_len); + + x86_mov_reg_imm (code, MONO_ARCH_RGCTX_REG, mrgctx); + x86_jump_code (code, addr); + g_assert ((code - start) <= buf_len); + + mono_arch_flush_icache (start, code - start); + + return start; +} + +gpointer +mono_arch_get_llvm_imt_trampoline (MonoDomain *domain, MonoMethod *m, int vt_offset) +{ + guint8 *code, *start; + int buf_len; + int this_offset; + + buf_len = 32; + + start = code = mono_domain_code_reserve (domain, buf_len); + + this_offset = mono_x86_get_this_arg_offset (NULL, mono_method_signature (m)); + + /* Set imt arg */ + x86_mov_reg_imm (code, MONO_ARCH_IMT_REG, m); + /* Load this */ + x86_mov_reg_membase (code, X86_EAX, X86_ESP, this_offset + 4, 4); + /* Load vtable address */ + x86_mov_reg_membase (code, X86_EAX, X86_EAX, 0, 4); + x86_jump_membase (code, X86_EAX, vt_offset); + + g_assert ((code - start) < buf_len); + + mono_arch_flush_icache (start, code - start); + + return start; +} + void mono_arch_patch_callsite (guint8 *method_start, guint8 *orig_code, guint8 *addr) { @@ -65,8 +112,6 @@ mono_arch_patch_callsite (guint8 *method_start, guint8 *orig_code, guint8 *addr) gboolean can_write = mono_breakpoint_clean_code (method_start, orig_code, 8, buf, sizeof (buf)); code = buf + 8; - if (mono_running_on_valgrind ()) - can_write = FALSE; /* go to the start of the call instruction * @@ -81,10 +126,8 @@ mono_arch_patch_callsite (guint8 *method_start, guint8 *orig_code, guint8 *addr) if (can_write) { InterlockedExchange ((gint32*)(orig_code + 2), (guint)addr - ((guint)orig_code + 1) - 5); -#ifdef HAVE_VALGRIND_MEMCHECK_H - /* Tell valgrind to recompile the patched code */ - //VALGRIND_DISCARD_TRANSLATIONS (code + 2, code + 6); -#endif + /* Tell valgrind to recompile the patched code */ + VALGRIND_DISCARD_TRANSLATIONS (orig_code + 2, 4); } } else if (code [1] == 0xe9) { /* A PLT entry: jmp */ @@ -98,7 +141,7 @@ mono_arch_patch_callsite (guint8 *method_start, guint8 *orig_code, guint8 *addr) } void -mono_arch_patch_plt_entry (guint8 *code, guint8 *addr) +mono_arch_patch_plt_entry (guint8 *code, gpointer *got, mgreg_t *regs, guint8 *addr) { /* A PLT entry: jmp */ g_assert (code [0] == 0xe9); @@ -108,7 +151,7 @@ mono_arch_patch_plt_entry (guint8 *code, guint8 *addr) } void -mono_arch_nullify_class_init_trampoline (guint8 *code, gssize *regs) +mono_arch_nullify_class_init_trampoline (guint8 *code, mgreg_t *regs) { guint8 buf [16]; gboolean can_write = mono_breakpoint_clean_code (NULL, code, 6, buf, sizeof (buf)); @@ -139,12 +182,10 @@ mono_arch_nullify_class_init_trampoline (guint8 *code, gssize *regs) /* Then atomically change the first 4 bytes to a nop as well */ ops = 0x90909090; InterlockedExchange ((gint32*)code, ops); -#ifdef HAVE_VALGRIND_MEMCHECK_H /* FIXME: the calltree skin trips on the self modifying code above */ /* Tell valgrind to recompile the patched code */ - //VALGRIND_DISCARD_TRANSLATIONS (code, code + 8); -#endif + //VALGRIND_DISCARD_TRANSLATIONS (code, 8); } } else if (code [0] == 0x90 || code [0] == 0xeb) { /* Already changed by another thread */ @@ -153,7 +194,7 @@ mono_arch_nullify_class_init_trampoline (guint8 *code, gssize *regs) /* call *() -> Call made from AOT code */ gpointer *vtable_slot; - vtable_slot = mono_arch_get_vcall_slot_addr (code + 5, (gpointer*)regs); + vtable_slot = mono_get_vcall_slot_addr (code + 5, regs); g_assert (vtable_slot); *vtable_slot = nullified_class_init_trampoline; @@ -165,7 +206,7 @@ mono_arch_nullify_class_init_trampoline (guint8 *code, gssize *regs) } void -mono_arch_nullify_plt_entry (guint8 *code) +mono_arch_nullify_plt_entry (guint8 *code, mgreg_t *regs) { if (!mono_running_on_valgrind ()) { guint32 ops; @@ -197,33 +238,9 @@ mono_arch_create_trampoline_code (MonoTrampolineType tramp_type) * the ret address is at: esp + (pushed_args + 1) * sizeof (gpointer) */ - /* If this is a generic class init the argument is not on the - * stack yet but in MONO_ARCH_VTABLE_REG. We first check - * whether the vtable is already initialized in which case we - * just return. Otherwise we push it and continue. - */ - if (tramp_type == MONO_TRAMPOLINE_GENERIC_CLASS_INIT) { - static int byte_offset = -1; - static guint8 bitmask; - - guint8 *jump; - - if (byte_offset < 0) - mono_marshal_find_bitfield_offset (MonoVTable, initialized, &byte_offset, &bitmask); - - x86_test_membase_imm (buf, MONO_ARCH_VTABLE_REG, byte_offset, bitmask); - jump = buf; - x86_branch8 (buf, X86_CC_Z, -1, 1); - - x86_ret (buf); - - x86_patch (jump, buf); - x86_push_reg (buf, MONO_ARCH_VTABLE_REG); - } - /* Put all registers into an array on the stack * If this code is changed, make sure to update the offset value in - * mono_arch_find_this_argument () in mini-x86.c. + * mono_arch_get_this_arg_from_call () in mini-x86.c. */ x86_push_reg (buf, X86_EDI); x86_push_reg (buf, X86_ESI); @@ -266,7 +283,7 @@ mono_arch_create_trampoline_code (MonoTrampolineType tramp_type) pushed_args ++; /* save method info */ - if ((tramp_type == MONO_TRAMPOLINE_GENERIC) || (tramp_type == MONO_TRAMPOLINE_JUMP)) + if ((tramp_type == MONO_TRAMPOLINE_JIT) || (tramp_type == MONO_TRAMPOLINE_JUMP)) x86_push_membase (buf, X86_ESP, pushed_args * sizeof (gpointer)); else x86_push_imm (buf, 0); @@ -336,9 +353,12 @@ mono_arch_create_trampoline_code (MonoTrampolineType tramp_type) /* Check for thread interruption */ /* This is not perf critical code so no need to check the interrupt flag */ + /* Align the stack on osx */ + x86_alu_reg_imm (buf, X86_SUB, X86_ESP, 3 * 4); x86_push_reg (buf, X86_EAX); x86_call_code (buf, (guint8*)mono_thread_force_interruption_checkpoint); x86_pop_reg (buf, X86_EAX); + x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 3 * 4); /* Restore LMF */ @@ -376,26 +396,36 @@ mono_arch_create_trampoline_code (MonoTrampolineType tramp_type) /* restore LMF end */ + if (!MONO_TRAMPOLINE_TYPE_MUST_RETURN (tramp_type)) { + /* + * Overwrite the method ptr with the address we need to jump to, + * to free %eax. + */ + x86_mov_membase_reg (buf, X86_ESP, pushed_args * sizeof (gpointer), X86_EAX, 4); + } + /* Restore caller saved registers */ x86_mov_reg_membase (buf, X86_ECX, X86_ESP, (pushed_args - pushed_args_caller_saved + X86_ECX) * 4, 4); x86_mov_reg_membase (buf, X86_EDX, X86_ESP, (pushed_args - pushed_args_caller_saved + X86_EDX) * 4, 4); + if ((tramp_type == MONO_TRAMPOLINE_RESTORE_STACK_PROT) || (tramp_type == MONO_TRAMPOLINE_AOT_PLT)) + x86_mov_reg_membase (buf, X86_EAX, X86_ESP, (pushed_args - pushed_args_caller_saved + X86_EAX) * 4, 4); + + if (!MONO_TRAMPOLINE_TYPE_MUST_RETURN (tramp_type)) { + /* Pop saved reg array + stack align */ + x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 9 * 4); + pushed_args -= 9; + g_assert (pushed_args == 0); + } else { + /* Pop saved reg array + stack align + method ptr */ + x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 10 * 4); + pushed_args -= 10; - /* Pop saved reg array + stack align + method ptr */ - x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 10 * 4); - - pushed_args -= 10; - - /* We've popped one more stack item than we've pushed (the - method ptr argument), so we must end up at -1. */ - g_assert (pushed_args == -1); + /* We've popped one more stack item than we've pushed (the + method ptr argument), so we must end up at -1. */ + g_assert (pushed_args == -1); + } - if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT || - tramp_type == MONO_TRAMPOLINE_GENERIC_CLASS_INIT || - tramp_type == MONO_TRAMPOLINE_RGCTX_LAZY_FETCH) - x86_ret (buf); - else - /* call the compiled method */ - x86_jump_reg (buf, X86_EAX); + x86_ret (buf); g_assert ((buf - code) <= 256); @@ -417,9 +447,7 @@ mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_ty tramp = mono_get_trampoline_code (tramp_type); - mono_domain_lock (domain); - code = buf = mono_code_manager_reserve_align (domain->code_mp, TRAMPOLINE_SIZE, 4); - mono_domain_unlock (domain); + code = buf = mono_domain_code_reserve_align (domain, TRAMPOLINE_SIZE, 4); x86_push_imm (buf, arg1); x86_jump_code (buf, tramp); @@ -436,18 +464,20 @@ mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_ty gpointer mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot) { - guint8 *tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_RGCTX_LAZY_FETCH); + guint8 *tramp; guint8 *code, *buf; guint8 **rgctx_null_jumps; int tramp_size; int depth, index; int i; + gboolean mrgctx; - g_assert (tramp); - - index = slot; + mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot); + index = MONO_RGCTX_SLOT_INDEX (slot); + if (mrgctx) + index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer); for (depth = 0; ; ++depth) { - int size = mono_class_rgctx_get_array_size (depth); + int size = mono_class_rgctx_get_array_size (depth, mrgctx); if (index < size - 1) break; @@ -460,19 +490,24 @@ mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot) rgctx_null_jumps = g_malloc (sizeof (guint8*) * (depth + 2)); - /* load vtable ptr */ + /* load vtable/mrgctx ptr */ x86_mov_reg_membase (buf, X86_EAX, X86_ESP, 4, 4); - /* load rgctx ptr from vtable */ - x86_mov_reg_membase (buf, X86_EAX, X86_EAX, G_STRUCT_OFFSET (MonoVTable, runtime_generic_context), 4); - /* is the rgctx ptr null? */ - x86_test_reg_reg (buf, X86_EAX, X86_EAX); - /* if yes, jump to actual trampoline */ - rgctx_null_jumps [0] = buf; - x86_branch8 (buf, X86_CC_Z, -1, 1); + if (!mrgctx) { + /* load rgctx ptr from vtable */ + x86_mov_reg_membase (buf, X86_EAX, X86_EAX, G_STRUCT_OFFSET (MonoVTable, runtime_generic_context), 4); + /* is the rgctx ptr null? */ + x86_test_reg_reg (buf, X86_EAX, X86_EAX); + /* if yes, jump to actual trampoline */ + rgctx_null_jumps [0] = buf; + x86_branch8 (buf, X86_CC_Z, -1, 1); + } for (i = 0; i < depth; ++i) { /* load ptr to next array */ - x86_mov_reg_membase (buf, X86_EAX, X86_EAX, 0, 4); + if (mrgctx && i == 0) + x86_mov_reg_membase (buf, X86_EAX, X86_EAX, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT, 4); + else + x86_mov_reg_membase (buf, X86_EAX, X86_EAX, 0, 4); /* is the ptr null? */ x86_test_reg_reg (buf, X86_EAX, X86_EAX); /* if yes, jump to actual trampoline */ @@ -490,40 +525,16 @@ mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot) /* otherwise return */ x86_ret (buf); - for (i = 0; i <= depth + 1; ++i) + for (i = mrgctx ? 1 : 0; i <= depth + 1; ++i) x86_patch (rgctx_null_jumps [i], buf); g_free (rgctx_null_jumps); - /* - * our stack looks like this (tos on top): - * - * | ret addr | - * | vtable ptr | - * | ... | - * - * the trampoline code expects it to look like this: - * - * | vtable ptr | - * | ret addr | - * | ... | - * - * whereas our caller expects to still have one argument on - * the stack when we return, so we transform the stack into - * this: - * - * | vtable ptr | - * | ret addr | - * | dummy | - * | ... | - * - * which actually only requires us to push the vtable ptr, and - * the "old" vtable ptr becomes the dummy. - */ + x86_mov_reg_membase (buf, MONO_ARCH_VTABLE_REG, X86_ESP, 4, 4); - x86_push_membase (buf, X86_ESP, 4); + tramp = mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH, mono_get_root_domain (), NULL); - x86_mov_reg_imm (buf, X86_EAX, slot); + /* jump to the actual trampoline */ x86_jump_code (buf, tramp); mono_arch_flush_icache (code, buf - code); @@ -533,12 +544,288 @@ mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot) return code; } -guint32 -mono_arch_get_rgctx_lazy_fetch_offset (gpointer *regs) +gpointer +mono_arch_create_generic_class_init_trampoline (void) { - return (guint32)(regs [X86_EAX]); + guint8 *tramp; + guint8 *code, *buf; + static int byte_offset = -1; + static guint8 bitmask; + guint8 *jump; + int tramp_size; + + tramp_size = 64; + + code = buf = mono_global_codeman_reserve (tramp_size); + + if (byte_offset < 0) + mono_marshal_find_bitfield_offset (MonoVTable, initialized, &byte_offset, &bitmask); + + x86_test_membase_imm (code, MONO_ARCH_VTABLE_REG, byte_offset, bitmask); + jump = code; + x86_branch8 (code, X86_CC_Z, -1, 1); + + x86_ret (code); + + x86_patch (jump, code); + + /* Push the vtable so the stack is the same as in a specific trampoline */ + x86_push_reg (code, MONO_ARCH_VTABLE_REG); + + tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_GENERIC_CLASS_INIT); + + /* jump to the actual trampoline */ + x86_jump_code (code, tramp); + + mono_arch_flush_icache (code, code - buf); + + g_assert (code - buf <= tramp_size); + + return buf; } +#ifdef MONO_ARCH_MONITOR_OBJECT_REG +/* + * The code produced by this trampoline is equivalent to this: + * + * if (obj) { + * if (obj->synchronisation) { + * if (obj->synchronisation->owner == 0) { + * if (cmpxch (&obj->synchronisation->owner, TID, 0) == 0) + * return; + * } + * if (obj->synchronisation->owner == TID) { + * ++obj->synchronisation->nest; + * return; + * } + * } + * } + * return full_monitor_enter (); + * + */ +gpointer +mono_arch_create_monitor_enter_trampoline (void) +{ + guint32 code_size; + MonoJumpInfo *ji; + + return mono_arch_create_monitor_enter_trampoline_full (&code_size, &ji, FALSE); +} + +gpointer +mono_arch_create_monitor_enter_trampoline_full (guint32 *code_size, MonoJumpInfo **ji, gboolean aot) +{ + guint8 *tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_MONITOR_ENTER); + guint8 *code, *buf; + guint8 *jump_obj_null, *jump_sync_null, *jump_other_owner, *jump_cmpxchg_failed, *jump_tid; + int tramp_size; + int owner_offset, nest_offset, dummy; + + g_assert (MONO_ARCH_MONITOR_OBJECT_REG == X86_EAX); + + mono_monitor_threads_sync_members_offset (&owner_offset, &nest_offset, &dummy); + g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (owner_offset) == sizeof (gpointer)); + g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (nest_offset) == sizeof (guint32)); + owner_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (owner_offset); + nest_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (nest_offset); + + tramp_size = 64; + + code = buf = mono_global_codeman_reserve (tramp_size); + + if (mono_thread_get_tls_offset () != -1) { + /* MonoObject* obj is in EAX */ + /* is obj null? */ + x86_test_reg_reg (buf, X86_EAX, X86_EAX); + /* if yes, jump to actual trampoline */ + jump_obj_null = buf; + x86_branch8 (buf, X86_CC_Z, -1, 1); + + /* load obj->synchronization to ECX */ + x86_mov_reg_membase (buf, X86_ECX, X86_EAX, G_STRUCT_OFFSET (MonoObject, synchronisation), 4); + /* is synchronization null? */ + x86_test_reg_reg (buf, X86_ECX, X86_ECX); + /* if yes, jump to actual trampoline */ + jump_sync_null = buf; + x86_branch8 (buf, X86_CC_Z, -1, 1); + + /* load MonoInternalThread* into EDX */ + buf = mono_x86_emit_tls_get (buf, X86_EDX, mono_thread_get_tls_offset ()); + /* load TID into EDX */ + x86_mov_reg_membase (buf, X86_EDX, X86_EDX, G_STRUCT_OFFSET (MonoInternalThread, tid), 4); + + /* is synchronization->owner null? */ + x86_alu_membase_imm (buf, X86_CMP, X86_ECX, owner_offset, 0); + /* if not, jump to next case */ + jump_tid = buf; + x86_branch8 (buf, X86_CC_NZ, -1, 1); + + /* if yes, try a compare-exchange with the TID */ + /* free up register EAX, needed for the zero */ + x86_push_reg (buf, X86_EAX); + /* zero EAX */ + x86_alu_reg_reg (buf, X86_XOR, X86_EAX, X86_EAX); + /* compare and exchange */ + x86_prefix (buf, X86_LOCK_PREFIX); + x86_cmpxchg_membase_reg (buf, X86_ECX, owner_offset, X86_EDX); + /* if not successful, jump to actual trampoline */ + jump_cmpxchg_failed = buf; + x86_branch8 (buf, X86_CC_NZ, -1, 1); + /* if successful, pop and return */ + x86_pop_reg (buf, X86_EAX); + x86_ret (buf); + + /* next case: synchronization->owner is not null */ + x86_patch (jump_tid, buf); + /* is synchronization->owner == TID? */ + x86_alu_membase_reg (buf, X86_CMP, X86_ECX, owner_offset, X86_EDX); + /* if not, jump to actual trampoline */ + jump_other_owner = buf; + x86_branch8 (buf, X86_CC_NZ, -1, 1); + /* if yes, increment nest */ + x86_inc_membase (buf, X86_ECX, nest_offset); + /* return */ + x86_ret (buf); + + /* push obj */ + x86_patch (jump_obj_null, buf); + x86_patch (jump_sync_null, buf); + x86_patch (jump_other_owner, buf); + x86_push_reg (buf, X86_EAX); + /* jump to the actual trampoline */ + x86_patch (jump_cmpxchg_failed, buf); + x86_jump_code (buf, tramp); + } else { + /* push obj and jump to the actual trampoline */ + x86_push_reg (buf, X86_EAX); + x86_jump_code (buf, tramp); + } + + mono_arch_flush_icache (buf, buf - code); + g_assert (buf - code <= tramp_size); + + return code; +} + +gpointer +mono_arch_create_monitor_exit_trampoline (void) +{ + guint32 code_size; + MonoJumpInfo *ji; + + return mono_arch_create_monitor_exit_trampoline_full (&code_size, &ji, FALSE); +} + +gpointer +mono_arch_create_monitor_exit_trampoline_full (guint32 *code_size, MonoJumpInfo **ji, gboolean aot) +{ + guint8 *tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_MONITOR_EXIT); + guint8 *code, *buf; + guint8 *jump_obj_null, *jump_have_waiters; + guint8 *jump_next; + int tramp_size; + int owner_offset, nest_offset, entry_count_offset; + + g_assert (MONO_ARCH_MONITOR_OBJECT_REG == X86_EAX); + + mono_monitor_threads_sync_members_offset (&owner_offset, &nest_offset, &entry_count_offset); + g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (owner_offset) == sizeof (gpointer)); + g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (nest_offset) == sizeof (guint32)); + g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (entry_count_offset) == sizeof (gint32)); + owner_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (owner_offset); + nest_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (nest_offset); + entry_count_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (entry_count_offset); + + tramp_size = 64; + + code = buf = mono_global_codeman_reserve (tramp_size); + + if (mono_thread_get_tls_offset () != -1) { + /* MonoObject* obj is in EAX */ + /* is obj null? */ + x86_test_reg_reg (buf, X86_EAX, X86_EAX); + /* if yes, jump to actual trampoline */ + jump_obj_null = buf; + x86_branch8 (buf, X86_CC_Z, -1, 1); + + /* load obj->synchronization to ECX */ + x86_mov_reg_membase (buf, X86_ECX, X86_EAX, G_STRUCT_OFFSET (MonoObject, synchronisation), 4); + /* is synchronization null? */ + x86_test_reg_reg (buf, X86_ECX, X86_ECX); + /* if not, jump to next case */ + jump_next = buf; + x86_branch8 (buf, X86_CC_NZ, -1, 1); + /* if yes, just return */ + x86_ret (buf); + + /* next case: synchronization is not null */ + x86_patch (jump_next, buf); + /* load MonoInternalThread* into EDX */ + buf = mono_x86_emit_tls_get (buf, X86_EDX, mono_thread_get_tls_offset ()); + /* load TID into EDX */ + x86_mov_reg_membase (buf, X86_EDX, X86_EDX, G_STRUCT_OFFSET (MonoInternalThread, tid), 4); + /* is synchronization->owner == TID */ + x86_alu_membase_reg (buf, X86_CMP, X86_ECX, owner_offset, X86_EDX); + /* if yes, jump to next case */ + jump_next = buf; + x86_branch8 (buf, X86_CC_Z, -1, 1); + /* if not, just return */ + x86_ret (buf); + + /* next case: synchronization->owner == TID */ + x86_patch (jump_next, buf); + /* is synchronization->nest == 1 */ + x86_alu_membase_imm (buf, X86_CMP, X86_ECX, nest_offset, 1); + /* if not, jump to next case */ + jump_next = buf; + x86_branch8 (buf, X86_CC_NZ, -1, 1); + /* if yes, is synchronization->entry_count zero? */ + x86_alu_membase_imm (buf, X86_CMP, X86_ECX, entry_count_offset, 0); + /* if not, jump to actual trampoline */ + jump_have_waiters = buf; + x86_branch8 (buf, X86_CC_NZ, -1 , 1); + /* if yes, set synchronization->owner to null and return */ + x86_mov_membase_imm (buf, X86_ECX, owner_offset, 0, 4); + x86_ret (buf); + + /* next case: synchronization->nest is not 1 */ + x86_patch (jump_next, buf); + /* decrease synchronization->nest and return */ + x86_dec_membase (buf, X86_ECX, nest_offset); + x86_ret (buf); + + /* push obj and jump to the actual trampoline */ + x86_patch (jump_obj_null, buf); + x86_patch (jump_have_waiters, buf); + x86_push_reg (buf, X86_EAX); + x86_jump_code (buf, tramp); + } else { + /* push obj and jump to the actual trampoline */ + x86_push_reg (buf, X86_EAX); + x86_jump_code (buf, tramp); + } + + mono_arch_flush_icache (buf, buf - code); + g_assert (buf - code <= tramp_size); + + return code; +} +#else +gpointer +mono_arch_create_monitor_enter_trampoline (void) +{ + g_assert_not_reached (); + return NULL; +} + +gpointer +mono_arch_create_monitor_exit_trampoline (void) +{ + g_assert_not_reached (); + return NULL; +} +#endif + void mono_arch_invalidate_method (MonoJitInfo *ji, void *func, gpointer func_arg) {