Mon Jan 19 17:44:50 CET 2004 Paolo Molaro <lupus@ximian.com>
[mono.git] / mono / mini / tramp-ppc.c
index b178dd62601bb6a410fc673f3532e284e63be271..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)
@@ -13,6 +13,7 @@
 #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"
 
+typedef enum {
+       MONO_TRAMPOLINE_GENERIC,
+       MONO_TRAMPOLINE_JUMP,
+       MONO_TRAMPOLINE_CLASS_INIT
+} MonoTrampolineType;
+
 /* adapt to mini later... */
 #define mono_jit_share_code (1)
 
@@ -49,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);
@@ -59,7 +66,7 @@ get_unbox_trampoline (MonoMethod *m, gpointer addr)
 }
 
 /* Stack size for trampoline function */
-#define STACK 144
+#define STACK (144 + 8*8)
 
 /* Method-specific trampoline code framgment size */
 #define METHOD_TRAMPOLINE_SIZE 64
@@ -85,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
@@ -176,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);
@@ -193,66 +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;
-
-       /* previously created trampoline code */
-       if (method->info)
-               return method->info;
+       mono_runtime_class_init (vtable);
 
-       /* 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 (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
-               return mono_arch_create_jit_trampoline (mono_marshal_get_synchronized_wrapper (method));
-
-       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
@@ -312,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
@@ -339,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);
@@ -382,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 */
                /*
@@ -407,7 +435,16 @@ 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);
                
@@ -418,28 +455,78 @@ mono_arch_create_jit_trampoline (MonoMethod *method)
                ppc_lwz  (buf, ppc_r0, 4, ppc_r1);
                ppc_mtlr (buf, ppc_r0);
                ppc_blr  (buf); 
+#endif
                
                /* Flush instruction cache, since we've generated code */
-               mono_arch_flush_icache (vc, buf - vc);
+               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
        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);
@@ -496,9 +583,7 @@ x86_magic_trampoline (int eax, int ecx, int edx, int esi, int edi,
        char *o;
        gpointer addr;
 
-       EnterCriticalSection (metadata_section);
        addr = mono_compile_method (m);
-       LeaveCriticalSection (metadata_section);
        g_assert (addr);
 
        /* go to the start of the call instruction
@@ -586,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 */ 
@@ -688,6 +757,82 @@ 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.
  */