2005-05-22 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mono / mini / tramp-amd64.c
index c3941cc447cdc9b5775b6f0ed665823d783f09ab..e08a285835b17f9dbeb9dca184f10ea8d7aa3f89 100644 (file)
 #include "mini.h"
 #include "mini-amd64.h"
 
-typedef enum {
-       MONO_TRAMPOLINE_GENERIC,
-       MONO_TRAMPOLINE_JUMP,
-       MONO_TRAMPOLINE_CLASS_INIT
-} MonoTrampolineType;
-
-/*
- * Address of the trampoline code.  This is used by the debugger to check
- * whether a method is a trampoline.
- */
-guint8 *mono_generic_trampoline_code = NULL;
-
-/*
- * AMD64 processors maintain icache coherency only for pages which are marked
- * executable, so we have to alloc memory through a code manager.
- */
-static CRITICAL_SECTION tramp_codeman_mutex;
-static MonoCodeManager *tramp_codeman;
-
 /*
  * get_unbox_trampoline:
  * @m: method pointer
@@ -58,7 +39,7 @@ get_unbox_trampoline (MonoMethod *m, gpointer addr)
        int this_reg = AMD64_RDI;
        MonoDomain *domain = mono_domain_get ();
 
-       if (!m->signature->ret->byref && MONO_TYPE_ISSTRUCT (m->signature->ret))
+       if (!mono_method_signature (m)->ret->byref && MONO_TYPE_ISSTRUCT (mono_method_signature (m)->ret))
                this_reg = AMD64_RSI;
 
        mono_domain_lock (domain);
@@ -92,9 +73,10 @@ amd64_magic_trampoline (long *regs, guint8 *code, MonoMethod *m, guint8* tramp)
 
        /* the method was jumped to */
        if (!code)
+               /* FIXME: Optimize the case when the call is from a delegate wrapper */
                return addr;
 
-       vtable_slot = mono_amd64_get_vcall_slot_addr (code, regs);
+       vtable_slot = mono_arch_get_vcall_slot_addr (code, (gpointer*)regs);
 
        if (vtable_slot) {
                if (m->klass->valuetype)
@@ -102,19 +84,27 @@ amd64_magic_trampoline (long *regs, guint8 *code, MonoMethod *m, guint8* tramp)
 
                g_assert (*vtable_slot);
 
-               *vtable_slot = addr;
+               if (mono_aot_is_got_entry (code, (guint8*)vtable_slot) || mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot))
+                       *vtable_slot = addr;
        }
        else {
                /* Patch calling code */
 
-               if ((code [-13] == 0x49) && (code [-12] == 0xbb)) {
+               if (((code [-13] == 0x49) && (code [-12] == 0xbb)) ||
+                       (code [-5] == 0xe8)) {
                        MonoJitInfo *ji = 
                                mono_jit_info_table_find (mono_domain_get (), code);
                        MonoJitInfo *target_ji = 
                                mono_jit_info_table_find (mono_domain_get (), addr);
 
                        if (mono_method_same_domain (ji, target_ji)) {
-                               InterlockedExchangePointer ((gpointer*)(code - 11), addr);
+                               if (code [-5] != 0xe8)
+                                       InterlockedExchangePointer ((gpointer*)(code - 11), addr);
+                               else {
+                                       g_assert ((((guint64)(addr)) >> 32) == 0);
+                                       g_assert ((((guint64)(code)) >> 32) == 0);
+                                       InterlockedExchange ((guint32*)(code - 4), ((gint64)addr - (gint64)code));
+                               }
                        }
                }
                else if ((code [-7] == 0x41) && (code [-6] == 0xff) && (code [-5] == 0x15)) {
@@ -129,25 +119,53 @@ amd64_magic_trampoline (long *regs, guint8 *code, MonoMethod *m, guint8* tramp)
                                InterlockedExchangePointer (got_entry, addr);
                        }
                }
-               else {
-                       /* FIXME: handle more cases */
-               }
-
-               /* Patch trampoline */
-               /* FIXME: Make this thread safe */
-               if (!m->klass->valuetype) {
-                       /* 
-                        * Valuetype method trampolines can't be patched since they are used for virtual
-                        * calls which need an unbox trampoline.
-                        */
-                       amd64_mov_reg_imm (tramp, AMD64_R11, addr);
-                       amd64_jump_reg (tramp, AMD64_R11);
-               }
        }
 
        return addr;
 }
 
+/*
+ * amd64_aot_trampoline:
+ *
+ *   This trampoline handles calls made from AOT code. We try to bypass the 
+ * normal JIT compilation logic to avoid loading the metadata for the method.
+ */
+static gpointer
+amd64_aot_trampoline (long *regs, guint8 *code, guint8 *token_info, 
+                                         guint8* tramp)
+{
+       MonoImage *image;
+       guint32 token;
+       MonoMethod *method;
+       gpointer addr;
+       gpointer *vtable_slot;
+
+       image = *(gpointer*)token_info;
+       token_info += sizeof (gpointer);
+       token = *(guint32*)token_info;
+
+       /* Later we could avoid allocating the MonoMethod */
+       method = mono_get_method (image, token, NULL);
+       g_assert (method);
+
+       if (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
+               method = mono_marshal_get_synchronized_wrapper (method);
+
+       addr = mono_compile_method (method);
+       g_assert (addr);
+
+       vtable_slot = mono_arch_get_vcall_slot_addr (code, (gpointer*)regs);
+       g_assert (vtable_slot);
+
+       if (method->klass->valuetype)
+               addr = get_unbox_trampoline (method, addr);
+
+       if (mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot))
+               *vtable_slot = addr;
+
+       return addr;
+}
+
 /**
  * amd64_class_init_trampoline:
  *
@@ -182,8 +200,15 @@ amd64_class_init_trampoline (long *regs, guint8 *code, MonoVTable *vtable, guint
                        buf [11] = 0x66;
                        buf [12] = 0x90;
                }
-       }
-       else if (code [0] == 0x90 || code [0] == 0xeb || code [0] == 0x66)
+       } else if (code [-2] == 0xe8) {
+               guint8 *buf = code - 2;
+
+               buf [0] = 0x66;
+               buf [1] = 0x66;
+               buf [2] = 0x90;
+               buf [3] = 0x66;
+               buf [4] = 0x90;
+       } else if (code [0] == 0x90 || code [0] == 0xeb || code [0] == 0x66)
                /* Already changed by another thread */
                ;
        else if ((code [-4] == 0x41) && (code [-3] == 0xff) && (code [-2] == 0x15))
@@ -196,32 +221,19 @@ amd64_class_init_trampoline (long *regs, guint8 *code, MonoVTable *vtable, guint
        }
 }
 
-static guchar*
-create_trampoline_code (MonoTrampolineType tramp_type)
+guchar*
+mono_arch_create_trampoline_code (MonoTrampolineType tramp_type)
 {
        guint8 *buf, *code, *tramp;
        int i, lmf_offset, offset, method_offset, tramp_offset, saved_regs_offset, saved_fpregs_offset, framesize;
-       static guint8* generic_jump_trampoline = NULL;
-       static guint8 *generic_class_init_trampoline = NULL;
-
-       switch (tramp_type) {
-       case MONO_TRAMPOLINE_GENERIC:
-               if (mono_generic_trampoline_code)
-                       return mono_generic_trampoline_code;
-               break;
-       case MONO_TRAMPOLINE_JUMP:
-               if (generic_jump_trampoline)
-                       return generic_jump_trampoline;
-               break;
-       case MONO_TRAMPOLINE_CLASS_INIT:
-               if (generic_class_init_trampoline)
-                       return generic_class_init_trampoline;
-               break;
-       }
+       gboolean has_caller;
+
+       if (tramp_type == MONO_TRAMPOLINE_JUMP)
+               has_caller = FALSE;
+       else
+               has_caller = TRUE;
 
-       EnterCriticalSection (&tramp_codeman_mutex);
-       code = buf = mono_code_manager_reserve (tramp_codeman, 512);
-       LeaveCriticalSection (&tramp_codeman_mutex);
+       code = buf = mono_global_codeman_reserve (512);
 
        framesize = 512 + sizeof (MonoLMF);
        framesize = (framesize + (MONO_ARCH_FRAME_ALIGNMENT - 1)) & ~ (MONO_ARCH_FRAME_ALIGNMENT - 1);
@@ -267,10 +279,10 @@ create_trampoline_code (MonoTrampolineType tramp_type)
        lmf_offset = - offset;
 
        /* Save ip */
-       if (tramp_type == MONO_TRAMPOLINE_JUMP)
-               amd64_mov_reg_imm (code, AMD64_R11, 0);
-       else
+       if (has_caller)
                amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, 8, 8);
+       else
+               amd64_mov_reg_imm (code, AMD64_R11, 0);
        amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, rip), AMD64_R11, 8);
        /* Save fp */
        amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, ebp), AMD64_RBP, 8);
@@ -305,7 +317,10 @@ create_trampoline_code (MonoTrampolineType tramp_type)
        amd64_lea_membase (code, AMD64_RDI, AMD64_RBP, saved_regs_offset);
 
        /* Arg2 is the address of the calling code */
-       amd64_mov_reg_membase (code, AMD64_RSI, AMD64_RBP, 8, 8);
+       if (has_caller)
+               amd64_mov_reg_membase (code, AMD64_RSI, AMD64_RBP, 8, 8);
+       else
+               amd64_mov_reg_imm (code, AMD64_RSI, 0);
 
        /* Arg3 is the method/vtable ptr */
        amd64_mov_reg_membase (code, AMD64_RDX, AMD64_RBP, method_offset, 8);
@@ -315,6 +330,8 @@ create_trampoline_code (MonoTrampolineType tramp_type)
 
        if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
                tramp = (guint8*)amd64_class_init_trampoline;
+       else if (tramp_type == MONO_TRAMPOLINE_AOT)
+               tramp = (guint8*)amd64_aot_trampoline;
        else
                tramp = (guint8*)amd64_magic_trampoline;
 
@@ -348,18 +365,6 @@ create_trampoline_code (MonoTrampolineType tramp_type)
 
        mono_arch_flush_icache (buf, code - buf);
 
-       switch (tramp_type) {
-       case MONO_TRAMPOLINE_GENERIC:
-               mono_generic_trampoline_code = buf;
-               break;
-       case MONO_TRAMPOLINE_JUMP:
-               generic_jump_trampoline = buf;
-               break;
-       case MONO_TRAMPOLINE_CLASS_INIT:
-               generic_class_init_trampoline = buf;
-               break;
-       }
-
        return buf;
 }
 
@@ -370,30 +375,46 @@ create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDo
 {
        MonoJitInfo *ji;
        guint8 *code, *buf, *tramp;
+       int jump_offset;
 
-       tramp = create_trampoline_code (tramp_type);
+       tramp = mono_get_trampoline_code (tramp_type);
 
-       mono_domain_lock (domain);
-       code = buf = mono_code_manager_reserve (domain->code_mp, TRAMPOLINE_SIZE);
-       mono_domain_unlock (domain);
+       code = buf = g_alloca (TRAMPOLINE_SIZE);
 
        /* push trampoline address */
        amd64_lea_membase (code, AMD64_R11, AMD64_RIP, -7);
        amd64_push_reg (code, AMD64_R11);
 
        /* push argument */
-       amd64_mov_reg_imm (code, AMD64_R11, arg1);
-       amd64_push_reg (code, AMD64_R11);
+       if (amd64_is_imm32 ((gint64)arg1))
+               amd64_push_imm (code, (gint64)arg1);
+       else {
+               amd64_mov_reg_imm (code, AMD64_R11, arg1);
+               amd64_push_reg (code, AMD64_R11);
+       }
 
-       /* FIXME: Optimize this */
-       amd64_mov_reg_imm (code, AMD64_R11, tramp);
-       amd64_jump_reg (code, AMD64_R11);
+       jump_offset = code - buf;
+       amd64_jump_disp (code, 0xffffffff);
 
        g_assert ((code - buf) <= TRAMPOLINE_SIZE);
 
        ji = g_new0 (MonoJitInfo, 1);
-       ji->code_start = buf;
+
+       mono_domain_lock (domain);
+       /* 
+        * FIXME: Changing the size to code - buf causes strange crashes during
+        * mcs bootstrap.
+        */
+       ji->code_start = mono_code_manager_reserve (domain->code_mp, TRAMPOLINE_SIZE);
        ji->code_size = code - buf;
+       mono_domain_unlock (domain);
+
+       memcpy (ji->code_start, buf, ji->code_size);
+
+       /* Fix up jump */
+       g_assert ((((gint64)tramp) >> 32) == 0);
+       code = (guint8*)ji->code_start + jump_offset;
+       amd64_jump_disp (code, tramp - code);
 
        mono_jit_stats.method_trampolines++;
 
@@ -435,6 +456,29 @@ mono_arch_create_jit_trampoline (MonoMethod *method)
        return code_start;
 }
 
+gpointer
+mono_arch_create_jit_trampoline_from_token (MonoImage *image, guint32 token)
+{
+       MonoDomain *domain = mono_domain_get ();
+       MonoJitInfo *ji;
+       gpointer code_start;
+       guint8 *buf, *start;
+
+       mono_domain_lock (domain);
+       buf = start = mono_code_manager_reserve (domain->code_mp, 2 * sizeof (gpointer));
+       mono_domain_unlock (domain);
+
+       *(gpointer*)buf = image;
+       buf += sizeof (gpointer);
+       *(guint32*)buf = token;
+
+       ji = create_specific_trampoline (start, MONO_TRAMPOLINE_AOT, domain);
+       code_start = ji->code_start;
+       g_free (ji);
+
+       return code_start;
+}
+
 /**
  * mono_arch_create_class_init_trampoline:
  *  @vtable: the type to initialize
@@ -480,9 +524,7 @@ mono_debugger_create_notification_function (gpointer *notification_address)
 {
        guint8 *ptr, *buf;
 
-       EnterCriticalSection (&tramp_codeman_mutex);
-       ptr = buf = mono_code_manager_reserve (tramp_codeman, 16);
-       LeaveCriticalSection (&tramp_codeman_mutex);
+       ptr = buf = mono_global_codeman_reserve (16);
 
        x86_breakpoint (buf);
        if (notification_address)
@@ -491,15 +533,3 @@ mono_debugger_create_notification_function (gpointer *notification_address)
 
        return ptr;
 }
-
-void
-mono_amd64_tramp_init (void)
-{
-       InitializeCriticalSection (&tramp_codeman_mutex);
-
-       tramp_codeman = mono_code_manager_new ();
-
-       create_trampoline_code (MONO_TRAMPOLINE_GENERIC);
-       create_trampoline_code (MONO_TRAMPOLINE_JUMP);
-       create_trampoline_code (MONO_TRAMPOLINE_CLASS_INIT);
-}