Mon Jan 19 17:44:50 CET 2004 Paolo Molaro <lupus@ximian.com>
[mono.git] / mono / mini / tramp-ppc.c
index 039943799edc645dde5f99c0eddf8573e6396b43..b2bfc48fa53099eef9834b28e43a00a748590f06 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * trampoline.c: JIT trampoline code
+ * tramp-ppc.c: JIT trampoline code for PowerPC
  *
  * Authors:
  *   Dietmar Maurer (dietmar@ximian.com)
 #include <glib.h>
 
 #include <mono/metadata/appdomain.h>
+#include <mono/metadata/marshal.h>
 #include <mono/metadata/tabledefs.h>
 #include <mono/arch/ppc/ppc-codegen.h>
+#include <mono/metadata/mono-debug-debugger.h>
 
 #include "mini.h"
 #include "mini-ppc.h"
 
-/* 
- * define for the (broken) debugger breakpoint interface:
- * The debugger should use the hw registers to set the breakpoints.
- */
-#define mono_method_has_breakpoint(a,b) (0)
-#define mono_remove_breakpoint(a)
+typedef enum {
+       MONO_TRAMPOLINE_GENERIC,
+       MONO_TRAMPOLINE_JUMP,
+       MONO_TRAMPOLINE_CLASS_INIT
+} MonoTrampolineType;
 
 /* adapt to mini later... */
 #define mono_jit_share_code (1)
  */
 guint8 *mono_generic_trampoline_code = NULL;
 
-/*
- * Address of a special breakpoint trampoline code for the debugger.
- */
-guint8 *mono_breakpoint_trampoline_code = NULL;
-
 /*
  * get_unbox_trampoline:
  * @m: method pointer
@@ -60,8 +56,8 @@ get_unbox_trampoline (MonoMethod *m, gpointer addr)
            
        start = code = g_malloc (20);
 
-       ppc_load (code, ppc_r11, addr);
-       ppc_mtctr (code, ppc_r11);
+       ppc_load (code, ppc_r0, addr);
+       ppc_mtctr (code, ppc_r0);
        ppc_addi (code, this_pos, this_pos, sizeof (MonoObject));
        ppc_bcctr (code, 20, 0);
        g_assert ((code - start) <= 20);
@@ -69,43 +65,8 @@ get_unbox_trampoline (MonoMethod *m, gpointer addr)
        return start;
 }
 
-/*
- * get_breakpoint_trampoline:
- * @m: method pointer
- * @addr: pointer to native code for @m
- *
- * creates a special trampoline for the debugger which is used to get
- * a breakpoint after compiling a method.
- */
-static gpointer
-get_breakpoint_trampoline (MonoMethod *m, guint32 breakpoint_id, gpointer addr)
-{
-       guint8 *code, *start, *buf;
-
-       if (!mono_breakpoint_trampoline_code) {
-               mono_breakpoint_trampoline_code = buf = g_malloc (8);
-
-               ppc_break (buf);
-       /*      x86_breakpoint (buf);
-               x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 8);
-               x86_ret (buf);*/
-
-               g_assert ((buf - mono_breakpoint_trampoline_code) <= 8);
-       }
-
-       start = code = g_malloc (22);
-       ppc_break (code);
-/*     x86_push_imm (code, addr);
-       x86_push_imm (code, breakpoint_id);
-       x86_push_imm (code, m);
-       x86_jump_code (code, mono_breakpoint_trampoline_code);*/
-       g_assert ((code - start) <= 22);
-
-       return start;
-}
-
 /* Stack size for trampoline function */
-#define STACK 144
+#define STACK (144 + 8*8)
 
 /* Method-specific trampoline code framgment size */
 #define METHOD_TRAMPOLINE_SIZE 64
@@ -131,9 +92,7 @@ ppc_magic_trampoline (MonoMethod *method, guint32 *code, char *sp)
        gpointer addr;
        int reg;
 
-       EnterCriticalSection(metadata_section);
        addr = mono_compile_method(method);
-       LeaveCriticalSection(metadata_section);
        g_assert(addr);
 
        /* Locate the address of the method-specific trampoline. The call using
@@ -222,6 +181,11 @@ ppc_magic_trampoline (MonoMethod *method, guint32 *code, char *sp)
        compiled method */
        
        start = o;
+#if 1
+       /* FIXME: make the patching thread safe */
+       ppc_b (o, 0);
+       ppc_patch (o - 4, addr);
+#else
        ppc_stwu (o, ppc_r1, -16, ppc_r1);
        ppc_mflr (o, ppc_r0);
        ppc_stw  (o, ppc_r31, 12, ppc_r1);
@@ -239,63 +203,60 @@ ppc_magic_trampoline (MonoMethod *method, guint32 *code, char *sp)
        ppc_lwz  (o, ppc_r31, -4, ppc_r11);
        ppc_mr   (o, ppc_r1, ppc_r11);
        ppc_blr  (o);
-       
+#endif 
        mono_arch_flush_icache (start, o - start);
        g_assert(o - start < METHOD_TRAMPOLINE_SIZE);
        
        return addr;
 }
 
-/**
- * arch_create_jit_trampoline:
- * @method: pointer to the method info
- *
- * Creates a trampoline function for virtual methods. If the created
- * code is called it first starts JIT compilation of method,
- * and then calls the newly created method. It also replaces the
- * corresponding vtable entry (see ppc_magic_trampoline).
- *
- * A trampoline consists of two parts: a main fragment, shared by all method
- * trampolines, and some code specific to each method, which hard-codes a
- * reference to that method and then calls the main fragment.
- *
- * The main fragment contains a call to 'ppc_magic_trampoline', which performs
- * call to the JIT compiler and substitutes the method-specific fragment with
- * some code that directly calls the JIT-compiled method.
- * 
- * Returns: a pointer to the newly created code 
- */
-gpointer
-mono_arch_create_jit_trampoline (MonoMethod *method)
+static void
+ppc_class_init_trampoline (void *vtable, guint32 *code, char *sp)
 {
-       guint8 *code, *buf;
-       static guint8 *vc = NULL;
+       mono_runtime_class_init (vtable);
 
-       /* previously created trampoline code */
-       if (method->info)
-               return method->info;
-
-       /* we immediately compile runtime provided functions */
-       if (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) {
-               method->info = mono_compile_method (method);
-               return method->info;
+#if 0
+       /* This is the 'bl' instruction */
+       --code;
+       
+       if (((*code) >> 26) == 18) {
+               ppc_ori (code, 0, 0, 0); /* nop */
+               mono_arch_flush_icache (code, 4);
+               return;
+       } else {
+               g_assert_not_reached ();
        }
+#endif
+}
 
-       /* icalls use method->addr */
-       if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
-           (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
-               MonoMethod *nm;
-               
-               nm = mono_marshal_get_native_wrapper (method);
-               method->info = mono_compile_method (nm);
-               return method->info;
+static guchar*
+create_trampoline_code (MonoTrampolineType tramp_type)
+{
+       guint8 *buf, *code = NULL;
+       static guint8* generic_jump_trampoline = NULL;
+       static guint8 *generic_class_init_trampoline = NULL;
+       int i, offset;
+
+       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;
        }
 
-       if(!vc) {
+       if(!code) {
                /* Now we'll create in 'buf' the PowerPC trampoline code. This
                 is the trampoline code common to all methods  */
                
-               vc = buf = g_malloc(512);
+               code = buf = g_malloc(512);
                
                /*-----------------------------------------------------------
                STEP 0: First create a non-standard function prologue with a
@@ -355,6 +316,13 @@ mono_arch_create_jit_trampoline (MonoMethod *method)
                /* Save 'method' pseudo-parameter - the one passed in r11 */
                ppc_stw  (buf, ppc_r11, STACK - 124, ppc_r1);
 
+               /* Save the FP registers */
+               offset = 124 + 4 + 8;
+               for (i = ppc_f1; i <= ppc_f8; ++i) {
+                       ppc_stfd  (buf, i, STACK - offset, ppc_r1);
+                       offset += 8;
+               }
+
                /*----------------------------------------------------------
                STEP 1: call 'mono_get_lmf_addr()' to get the address of our
                LMF. We'll need to restore it after the call to
@@ -382,19 +350,30 @@ mono_arch_create_jit_trampoline (MonoMethod *method)
                /* Arg 1: MonoMethod *method. It was put in r11 by the
                method-specific trampoline code, and then saved before the call
                to mono_get_lmf_addr()'. Restore r11, by the way :-) */
-               ppc_lwz  (buf, ppc_r3,  STACK - 124, ppc_r1);
+               if (tramp_type == MONO_TRAMPOLINE_JUMP) {
+                       ppc_li (buf, ppc_r3, 0);
+               } else
+                       ppc_lwz  (buf, ppc_r3,  STACK - 124, ppc_r1);
                ppc_lwz  (buf, ppc_r11, STACK - 44,  ppc_r1);
                
                /* Arg 2: code (next address to the instruction that called us) */
-               ppc_lwz  (buf, ppc_r4, STACK + 4, ppc_r1);
+               if (tramp_type == MONO_TRAMPOLINE_JUMP) {
+                       ppc_li (buf, ppc_r4, 0);
+               } else
+                       ppc_lwz  (buf, ppc_r4, STACK + PPC_RET_ADDR_OFFSET, ppc_r1);
                
                /* Arg 3: stack pointer */
                ppc_mr   (buf, ppc_r5, ppc_r1);
                
                /* Calculate call address, restore r0 and call
                'ppc_magic_trampoline'. Return value will be in r3 */
-               ppc_lis  (buf, ppc_r0, (guint32) ppc_magic_trampoline >> 16);
-               ppc_ori  (buf, ppc_r0, ppc_r0, (guint32) ppc_magic_trampoline & 0xffff);
+               if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT) {
+                       ppc_lis  (buf, ppc_r0, (guint32) ppc_class_init_trampoline >> 16);
+                       ppc_ori  (buf, ppc_r0, ppc_r0, (guint32) ppc_class_init_trampoline & 0xffff);
+               } else {
+                       ppc_lis  (buf, ppc_r0, (guint32) ppc_magic_trampoline >> 16);
+                       ppc_ori  (buf, ppc_r0, ppc_r0, (guint32) ppc_magic_trampoline & 0xffff);
+               }
                ppc_mtlr (buf, ppc_r0);
                ppc_lwz  (buf, ppc_r0, STACK - 8,  ppc_r1);
                ppc_blrl (buf);
@@ -425,6 +404,12 @@ mono_arch_create_jit_trampoline (MonoMethod *method)
                ppc_lwz  (buf, ppc_r9,  STACK - 36,  ppc_r1);
                ppc_lwz  (buf, ppc_r10, STACK - 40,  ppc_r1);
                
+               /* Restore the FP registers */
+               offset = 124 + 4 + 8;
+               for (i = ppc_f1; i <= ppc_f8; ++i) {
+                       ppc_lfd  (buf, i, STACK - offset, ppc_r1);
+                       offset += 8;
+               }
                /* We haven't touched any of these, so there's no need to
                restore them */
                /*
@@ -450,39 +435,98 @@ mono_arch_create_jit_trampoline (MonoMethod *method)
                /* Non-standard function epilogue. Instead of doing a proper
                return, we just call the compiled code, so
                that, when it finishes, the method returns here. */
-               
+       
+#if 1
+               /* Restore stack pointer, r31, LR and jump to the code */
+               ppc_lwz  (buf, ppc_r1,  0, ppc_r1);
+               ppc_lwz  (buf, ppc_r31, -4, ppc_r1);
+               ppc_lwz  (buf, ppc_r11, PPC_RET_ADDR_OFFSET, ppc_r1);
+               ppc_mtlr (buf, ppc_r11);
+               ppc_mtctr (buf, ppc_r0);
+               ppc_bcctr (buf, 20, 0);
+#else
                ppc_mtlr (buf, ppc_r0);
                ppc_blrl (buf);
                
-               /* Restore stack pointer, r31, LR and return to caler */
+               /* Restore stack pointer, r31, LR and return to caller */
                ppc_lwz  (buf, ppc_r11,  0, ppc_r1);
                ppc_lwz  (buf, ppc_r31, -4, ppc_r11);
                ppc_mr   (buf, ppc_r1, ppc_r11);
                ppc_lwz  (buf, ppc_r0, 4, ppc_r1);
                ppc_mtlr (buf, ppc_r0);
                ppc_blr  (buf); 
+#endif
                
-               /* Flush instruction cache, sice we've generated code */
-               mono_arch_flush_icache (vc, buf - vc);
+               /* Flush instruction cache, since we've generated code */
+               mono_arch_flush_icache (code, buf - code);
        
                /* Sanity check */
-               g_assert ((buf - vc) <= 512);
+               g_assert ((buf - code) <= 512);
+       }
+
+       switch (tramp_type) {
+       case MONO_TRAMPOLINE_GENERIC:
+               mono_generic_trampoline_code = code;
+               break;
+       case MONO_TRAMPOLINE_JUMP:
+               generic_jump_trampoline = code;
+               break;
+       case MONO_TRAMPOLINE_CLASS_INIT:
+               generic_class_init_trampoline = code;
+               break;
        }
 
+       return code;
+}
+
+/**
+ * arch_create_jit_trampoline:
+ * @method: pointer to the method info
+ *
+ * Creates a trampoline function for virtual methods. If the created
+ * code is called it first starts JIT compilation of method,
+ * and then calls the newly created method. It also replaces the
+ * corresponding vtable entry (see ppc_magic_trampoline).
+ *
+ * A trampoline consists of two parts: a main fragment, shared by all method
+ * trampolines, and some code specific to each method, which hard-codes a
+ * reference to that method and then calls the main fragment.
+ *
+ * The main fragment contains a call to 'ppc_magic_trampoline', which performs
+ * call to the JIT compiler and substitutes the method-specific fragment with
+ * some code that directly calls the JIT-compiled method.
+ * 
+ * Returns: a pointer to the newly created code 
+ */
+gpointer
+mono_arch_create_jit_trampoline (MonoMethod *method)
+{
+       guint8 *code, *buf;
+       static guint8 *vc = NULL;
+
+       /* previously created trampoline code */
+       if (method->info)
+               return method->info;
+
+       if (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
+               return mono_arch_create_jit_trampoline (mono_marshal_get_synchronized_wrapper (method));
+
+       vc = create_trampoline_code (MONO_TRAMPOLINE_GENERIC);
+
        /* This is the method-specific part of the trampoline. Its purpose is
-       to provide the generic part with  the MonoMethod *method pointer. We'll
+       to provide the generic part with the MonoMethod *method pointer. We'll
        use r11 to keep that value, for instance. However, the generic part of
        the trampoline relies on r11 having the same value it had before coming
        here, so we must save it before. */
        code = buf = g_malloc(METHOD_TRAMPOLINE_SIZE);
-       
+
        /* Save r11. There's nothing magic in the '44', its just an arbitrary
        position - see above */
        ppc_stw  (buf, ppc_r11, -44,  ppc_r1);
        
        /* Now save LR - we'll overwrite it now */
        ppc_mflr (buf, ppc_r11);
-       ppc_stw  (buf, ppc_r11, 4, ppc_r1);
+       ppc_stw  (buf, ppc_r11, PPC_RET_ADDR_OFFSET, ppc_r1);
        
        /* Prepare the jump to the generic trampoline code.*/
        ppc_lis  (buf, ppc_r11, (guint32) vc >> 16);
@@ -494,13 +538,13 @@ mono_arch_create_jit_trampoline (MonoMethod *method)
        ppc_ori  (buf, ppc_r11, ppc_r11, (guint32) method & 0xffff);
        ppc_blr  (buf);
        
-       /* Flush instruction cache, sice we've generated code */
+       /* Flush instruction cache, since we've generated code */
        mono_arch_flush_icache (code, buf - code);
                
        /* Sanity check */
        g_assert ((buf - code) <= METHOD_TRAMPOLINE_SIZE);
        
-       /* store trampoline address */
+       /* Store trampoline address */
        method->info = code;
 
        mono_jit_stats.method_trampolines++;
@@ -537,12 +581,9 @@ x86_magic_trampoline (int eax, int ecx, int edx, int esi, int edi,
        guint8 reg;
        gint32 disp;
        char *o;
-       guint32 breakpoint_id;
-       gpointer addr, trampoline;
+       gpointer addr;
 
-       EnterCriticalSection (metadata_section);
        addr = mono_compile_method (m);
-       LeaveCriticalSection (metadata_section);
        g_assert (addr);
 
        /* go to the start of the call instruction
@@ -561,14 +602,8 @@ x86_magic_trampoline (int eax, int ecx, int edx, int esi, int edi,
                        reg = code [1] & 0x07;
                        disp = *((gint32*)(code + 2));
                } else if ((code [1] == 0xe8)) {
-                       breakpoint_id = mono_method_has_breakpoint (m, TRUE);
-                       if (breakpoint_id) {
-                               mono_remove_breakpoint (breakpoint_id);
-                               trampoline = get_breakpoint_trampoline (m, breakpoint_id, addr);
-                       } else
-                               trampoline = addr;
                        *((guint32*)(code + 2)) = (guint)addr - ((guint)code + 1) - 5; 
-                       return trampoline;
+                       return addr;
                } else if ((code [4] == 0xff) && (((code [5] >> 6) & 0x3) == 0) && (((code [5] >> 3) & 0x7) == 2)) {
                        /*
                         * This is a interface call: should check the above code can't catch it earlier 
@@ -610,17 +645,9 @@ x86_magic_trampoline (int eax, int ecx, int edx, int esi, int edi,
        o += disp;
 
        if (m->klass->valuetype) {
-               trampoline = *((gpointer *)o) = get_unbox_trampoline (m, addr);
+               return *((gpointer *)o) = get_unbox_trampoline (m, addr);
        } else {
-               trampoline = *((gpointer *)o) = addr;
-       }
-
-       breakpoint_id = mono_method_has_breakpoint (m, TRUE);
-       if (breakpoint_id) {
-               mono_remove_breakpoint (breakpoint_id);
-               return get_breakpoint_trampoline (m, breakpoint_id, trampoline);
-       } else {
-               return trampoline;
+               return *((gpointer *)o) = addr;
        }
 }
 
@@ -644,22 +671,6 @@ mono_arch_create_jit_trampoline (MonoMethod *method)
        if (method->info)
                return method->info;
 
-       /* we immediately compile runtime provided functions */
-       if (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) {
-               method->info = mono_compile_method (method);
-               return method->info;
-       }
-
-       /* icalls use method->addr */
-       if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
-           (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
-               MonoMethod *nm;
-               
-               nm = mono_marshal_get_native_wrapper (method);
-               method->info = mono_compile_method (nm);
-               return method->info;
-       }
-
        if (!mono_generic_trampoline_code) {
                mono_generic_trampoline_code = buf = g_malloc (256);
                /* save caller save regs because we need to do a call */ 
@@ -746,3 +757,96 @@ mono_arch_create_jit_trampoline (MonoMethod *method)
 
 #endif
 
+/**
+ * mono_arch_create_class_init_trampoline:
+ *  @vtable: the type to initialize
+ *
+ * Creates a trampoline function to run a type initializer. 
+ * If the trampoline is called, it calls mono_runtime_class_init with the
+ * given vtable, then patches the caller code so it does not get called any
+ * more.
+ * 
+ * Returns: a pointer to the newly created code 
+ */
+gpointer
+mono_arch_create_class_init_trampoline (MonoVTable *vtable)
+{
+       guint8 *code, *buf, *tramp;
+
+       tramp = create_trampoline_code (MONO_TRAMPOLINE_CLASS_INIT);
+
+       /* This is the method-specific part of the trampoline. Its purpose is
+       to provide the generic part with the MonoMethod *method pointer. We'll
+       use r11 to keep that value, for instance. However, the generic part of
+       the trampoline relies on r11 having the same value it had before coming
+       here, so we must save it before. */
+       code = buf = g_malloc(METHOD_TRAMPOLINE_SIZE);
+
+#if 1
+       ppc_mflr (buf, ppc_r4);
+       ppc_stw  (buf, ppc_r4, PPC_RET_ADDR_OFFSET, ppc_sp);
+       ppc_stwu (buf, ppc_sp, -32, ppc_sp);
+       ppc_load (buf, ppc_r3, vtable);
+       ppc_load (buf, ppc_r5, 0);
+
+       ppc_load (buf, ppc_r0, ppc_class_init_trampoline);
+       ppc_mtlr (buf, ppc_r0);
+       ppc_blrl (buf);
+
+       ppc_lwz (buf, ppc_r0, 32 + PPC_RET_ADDR_OFFSET, ppc_sp);
+       ppc_mtlr (buf, ppc_r0);
+       ppc_addic (buf, ppc_sp, ppc_sp, 32);
+       ppc_blr (buf);
+#else
+       /* Save r11. There's nothing magic in the '44', its just an arbitrary
+       position - see above */
+       ppc_stw  (buf, ppc_r11, -44,  ppc_r1);
+       
+       /* Now save LR - we'll overwrite it now */
+       ppc_mflr (buf, ppc_r11);
+       ppc_stw  (buf, ppc_r11, 4, ppc_r1);
+       ppc_stw  (buf, ppc_r11, PPC_RET_ADDR_OFFSET, ppc_r1);
+       
+       /* Prepare the jump to the generic trampoline code.*/
+       ppc_lis  (buf, ppc_r11, (guint32) tramp >> 16);
+       ppc_ori  (buf, ppc_r11, ppc_r11, (guint32) tramp & 0xffff);
+       ppc_mtlr (buf, ppc_r11);
+       
+       /* And finally put 'vtable' in r11 and fly! */
+       ppc_lis  (buf, ppc_r11, (guint32) vtable >> 16);
+       ppc_ori  (buf, ppc_r11, ppc_r11, (guint32) vtable & 0xffff);
+       ppc_blrl  (buf);
+       ppc_lwz (buf, ppc_r0, PPC_RET_ADDR_OFFSET, ppc_r1);
+       ppc_mtlr (buf, ppc_r0);
+       ppc_blr (buf);
+
+#endif
+
+       /* Flush instruction cache, since we've generated code */
+       mono_arch_flush_icache (code, buf - code);
+               
+       /* Sanity check */
+       g_assert ((buf - code) <= METHOD_TRAMPOLINE_SIZE);
+
+       mono_jit_stats.method_trampolines++;
+
+       return code;
+}
+
+/*
+ * This method is only called when running in the Mono Debugger.
+ */
+gpointer
+mono_debugger_create_notification_function (gpointer *notification_address)
+{
+       guint8 *ptr, *buf;
+
+       ptr = buf = g_malloc0 (16);
+       ppc_break (buf);
+       if (notification_address)
+               *notification_address = buf;
+       ppc_blr (buf);
+
+       return ptr;
+}
+