2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mono / arch / x86 / tramp.c
index e655c18e50da95107813a564417e8a90f9606c21..fab5a55325fa8ff4d140b51a8959f44f1ab714ad 100644 (file)
@@ -3,13 +3,21 @@
  * 
  * Copyright (C) Ximian Inc.
  * 
- * Author: Paolo Molaro (lupus@ximian.com)
+ * Authors: 
+ *   Paolo Molaro (lupus@ximian.com)
+ *   Dietmar Maurer (dietmar@ximian.com)
  * 
  */
 
+#include "config.h"
+#include <stdlib.h>
+#include <string.h>
 #include "x86-codegen.h"
 #include "mono/metadata/class.h"
+#include "mono/metadata/tabledefs.h"
 #include "mono/interpreter/interp.h"
+#include "mono/metadata/appdomain.h"
+#include "mono/metadata/marshal.h"
 
 /*
  * The resulting function takes the form:
 
 #define ARG_SIZE       sizeof (stackval)
 
-static char *
-mono_get_ansi_string (MonoObject *o)
-{
-       MonoStringObject *s = (MonoStringObject *)o;
-       char *as, *vector;
-       int i;
-
-       g_assert (o != NULL);
-
-       if (!s->length)
-               return g_strdup ("");
-
-       vector = s->c_str->vector;
-
-       g_assert (vector != NULL);
-
-       as = g_malloc (s->length + 1);
-
-       /* fixme: replace with a real unicode/ansi conversion */
-       for (i = 0; i < s->length; i++) {
-               as [i] = vector [i*2];
-       }
-
-       as [i] = '\0';
-
-       return as;
-}
-
 MonoPIFunc
-mono_create_trampoline (MonoMethod *method)
+mono_arch_create_trampoline (MonoMethodSignature *sig, gboolean string_ctor)
 {
-       MonoMethodSignature *sig;
        unsigned char *p, *code_buffer;
-       guint32 local_size = 0, stack_size = 0, code_size = 30;
-       guint32 arg_pos;
+       guint32 stack_size = 0, code_size = 50;
+       guint32 arg_pos, simpletype;
        int i, stringp;
+       static GHashTable *cache = NULL;
+       MonoPIFunc res;
+
+       if (!cache) 
+               cache = g_hash_table_new ((GHashFunc)mono_signature_hash, 
+                                         (GCompareFunc)mono_metadata_signature_equal);
+
+       if ((res = (MonoPIFunc)g_hash_table_lookup (cache, sig)))
+               return res;
 
-       sig = method->signature;
-       
        if (sig->hasthis) {
                stack_size += sizeof (gpointer);
-               code_size += 5;
+               code_size += 10;
        }
        
+       if (sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->byref && !sig->ret->data.klass->enumtype) {
+               stack_size += sizeof (gpointer);
+               code_size += 5;
+       }
+
        for (i = 0; i < sig->param_count; ++i) {
                if (sig->params [i]->byref) {
                        stack_size += sizeof (gpointer);
-                       code_size += i < 10 ? 5 : 8;
+                       code_size += 20;
                        continue;
                }
-               switch (sig->params [i]->type) {
+               simpletype = sig->params [i]->type;
+enum_calc_size:
+               switch (simpletype) {
                case MONO_TYPE_BOOLEAN:
                case MONO_TYPE_CHAR:
                case MONO_TYPE_I1:
@@ -85,22 +78,37 @@ mono_create_trampoline (MonoMethod *method)
                case MONO_TYPE_I:
                case MONO_TYPE_U:
                case MONO_TYPE_PTR:
-               case MONO_TYPE_R4:
                case MONO_TYPE_SZARRAY:
                case MONO_TYPE_CLASS:
                case MONO_TYPE_OBJECT:
+               case MONO_TYPE_STRING:
                        stack_size += 4;
                        code_size += i < 10 ? 5 : 8;
                        break;
-               case MONO_TYPE_STRING:
-                       stack_size += 4;
-                       code_size += 20;
-                       local_size++;
+               case MONO_TYPE_VALUETYPE: {
+                       int size;
+                       if (sig->params [i]->data.klass->enumtype) {
+                               simpletype = sig->params [i]->data.klass->enum_basetype->type;
+                               goto enum_calc_size;
+                       }
+                       if ((size = mono_class_native_size (sig->params [i]->data.klass, NULL)) != 4) {
+                               stack_size += size + 3;
+                               stack_size &= ~3;
+                               code_size += 32;
+                       } else {
+                               stack_size += 4;
+                               code_size += i < 10 ? 5 : 8;
+                       }
                        break;
+               }
                case MONO_TYPE_I8:
                        stack_size += 8;
                        code_size += i < 10 ? 5 : 8;
                        break;
+               case MONO_TYPE_R4:
+                       stack_size += 4;
+                       code_size += i < 10 ? 10 : 13;
+                       break;
                case MONO_TYPE_R8:
                        stack_size += 8;
                        code_size += i < 10 ? 7 : 10;
@@ -121,15 +129,13 @@ mono_create_trampoline (MonoMethod *method)
        x86_push_reg (p, X86_EBP);
        x86_mov_reg_reg (p, X86_EBP, X86_ESP, 4);
        /*
-        * We store some local vars here to handle string pointers.
+        * and align to 16 byte boundary...
         */
-       if (local_size)
-               x86_alu_reg_imm (p, X86_SUB, X86_ESP, local_size * 4);
+       stack_size += 15;
+       stack_size &= ~15;
 
-       /*
-        * We'll need to align to at least 8 bytes boudary... (16 may be better)
-        * x86_alu_reg_imm (p, X86_SUB, X86_ESP, stack_size);
-        */
+       if (stack_size)
+               x86_alu_reg_imm (p, X86_SUB, X86_ESP, stack_size);
 
        /*
         * EDX has the pointer to the args.
@@ -146,50 +152,70 @@ mono_create_trampoline (MonoMethod *method)
                        x86_push_membase (p, X86_EDX, arg_pos);
                        continue;
                }
-               switch (sig->params [i - 1]->type) {
+               simpletype = sig->params [i - 1]->type;
+enum_marshal:
+               switch (simpletype) {
+               case MONO_TYPE_BOOLEAN:
+               case MONO_TYPE_I1:
+               case MONO_TYPE_U1:
+               case MONO_TYPE_I2:
+               case MONO_TYPE_U2:
+               case MONO_TYPE_CHAR:
                case MONO_TYPE_I4:
                case MONO_TYPE_U4:
                case MONO_TYPE_I:
                case MONO_TYPE_U:
                case MONO_TYPE_PTR:
-               case MONO_TYPE_SZARRAY:
-               case MONO_TYPE_CLASS:
                case MONO_TYPE_OBJECT:
-               case MONO_TYPE_R4:
+               case MONO_TYPE_STRING:
                        x86_push_membase (p, X86_EDX, arg_pos);
                        break;
-               case MONO_TYPE_R8:
-                       x86_alu_reg_imm (p, X86_SUB, X86_ESP, 8);
+               case MONO_TYPE_R4:
+                       x86_alu_reg_imm (p, X86_SUB, X86_ESP, 4);
                        x86_fld_membase (p, X86_EDX, arg_pos, TRUE);
-                       x86_fst_membase (p, X86_ESP, 0, TRUE, TRUE);
+                       x86_fst_membase (p, X86_ESP, 0, FALSE, TRUE);
                        break;
-               case MONO_TYPE_STRING:
-                       /*if (frame->method->flags & PINVOKE_ATTRIBUTE_CHAR_SET_ANSI*/
+               case MONO_TYPE_CLASS:
+                       x86_push_membase (p, X86_EDX, arg_pos);
+                       break;
+               case MONO_TYPE_SZARRAY:
                        x86_push_membase (p, X86_EDX, arg_pos);
-                       x86_mov_reg_imm (p, X86_EDX, mono_get_ansi_string);
-                       x86_call_reg (p, X86_EDX);
-                       x86_alu_reg_imm (p, X86_SUB, X86_ESP, 4);
-                       x86_push_reg (p, X86_EAX);
-                       /*
-                        * Store the pointer in a local we'll free later.
-                        */
-                       stringp++;
-                       x86_mov_membase_reg (p, X86_EBP, LOC_POS * stringp, X86_EAX, 4);
-                       /*
-                        * we didn't save the reg: restore it here.
-                        */
-                       x86_mov_reg_membase (p, X86_EDX, X86_EBP, ARGP_POS, 4);
+                       break;
+               case MONO_TYPE_VALUETYPE:
+                       if (!sig->params [i - 1]->data.klass->enumtype) {
+                               int size = mono_class_native_size (sig->params [i - 1]->data.klass, NULL);
+                               if (size == 4) {
+                                       /* it's a structure that fits in 4 bytes, need to push the value pointed to */
+                                       x86_mov_reg_membase (p, X86_EAX, X86_EDX, arg_pos, 4);
+                                       x86_push_regp (p, X86_EAX);
+                               } else {
+                                       int ss = size;
+                                       ss += 3;
+                                       ss &= ~3;
+
+                                       x86_alu_reg_imm (p, X86_SUB, X86_ESP, ss);
+                                       x86_push_imm (p, size);
+                                       x86_push_membase (p, X86_EDX, arg_pos);
+                                       x86_lea_membase (p, X86_EAX, X86_ESP, 2*4);
+                                       x86_push_reg (p, X86_EAX);
+                                       x86_mov_reg_imm (p, X86_EAX, memcpy);
+                                       x86_call_reg (p, X86_EAX);
+                                       x86_alu_reg_imm (p, X86_ADD, X86_ESP, 12);
+                                       /* memcpy might clobber EDX so restore it */
+                                       x86_mov_reg_membase (p, X86_EDX, X86_EBP, ARGP_POS, 4);
+                               }
+                       } else {
+                               /* it's an enum value */
+                               simpletype = sig->params [i - 1]->data.klass->enum_basetype->type;
+                               goto enum_marshal;
+                       }
                        break;
                case MONO_TYPE_I8:
+               case MONO_TYPE_U8:
+               case MONO_TYPE_R8:
                        x86_push_membase (p, X86_EDX, arg_pos + 4);
                        x86_push_membase (p, X86_EDX, arg_pos);
                        break;
-               case MONO_TYPE_BOOLEAN:
-               case MONO_TYPE_CHAR:
-               case MONO_TYPE_I1:
-               case MONO_TYPE_U1:
-               case MONO_TYPE_I2:
-               case MONO_TYPE_U2:
                default:
                        g_error ("Can't trampoline 0x%x", sig->params [i - 1]->type);
                }
@@ -204,6 +230,14 @@ mono_create_trampoline (MonoMethod *method)
                }
        }
 
+       if (sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->byref) {
+               MonoClass *klass = sig->ret->data.klass;
+               if (!klass->enumtype) {
+                       x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
+                       x86_push_membase (p, X86_ECX, 0);
+               }
+       }
+
        /* 
         * Insert call to function 
         */
@@ -216,16 +250,38 @@ mono_create_trampoline (MonoMethod *method)
         * Long integers are in EAX:EDX.
         * FP values are on the FP stack.
         */
-       if (sig->ret->byref) {
+
+       if (sig->ret->byref || string_ctor) {
                x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
                x86_mov_regp_reg (p, X86_ECX, X86_EAX, 4);
        } else {
-               switch (sig->ret->type) {
+               simpletype = sig->ret->type;
+       enum_retvalue:
+               switch (simpletype) {
+               case MONO_TYPE_BOOLEAN:
+               case MONO_TYPE_I1:
+               case MONO_TYPE_U1:
+                       x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
+                       x86_mov_regp_reg (p, X86_ECX, X86_EAX, 1);
+                       break;
+               case MONO_TYPE_CHAR:
+               case MONO_TYPE_I2:
+               case MONO_TYPE_U2:
+                       x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
+                       x86_mov_regp_reg (p, X86_ECX, X86_EAX, 2);
+                       break;
                case MONO_TYPE_I4:
                case MONO_TYPE_U4:
                case MONO_TYPE_I:
                case MONO_TYPE_U:
+               case MONO_TYPE_CLASS:
                case MONO_TYPE_OBJECT:
+               case MONO_TYPE_SZARRAY:
+               case MONO_TYPE_ARRAY:
+                       x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
+                       x86_mov_regp_reg (p, X86_ECX, X86_EAX, 4);
+                       break;
+               case MONO_TYPE_STRING: 
                        x86_mov_reg_membase (p, X86_ECX, X86_EBP, RETVAL_POS, 4);
                        x86_mov_regp_reg (p, X86_ECX, X86_EAX, 4);
                        break;
@@ -242,6 +298,11 @@ mono_create_trampoline (MonoMethod *method)
                        x86_mov_regp_reg (p, X86_ECX, X86_EAX, 4);
                        x86_mov_membase_reg (p, X86_ECX, 4, X86_EDX, 4);
                        break;
+               case MONO_TYPE_VALUETYPE:
+                       if (sig->ret->data.klass->enumtype) {
+                               simpletype = sig->ret->data.klass->enum_basetype->type;
+                               goto enum_retvalue;
+                       }
                case MONO_TYPE_VOID:
                        break;
                default:
@@ -250,20 +311,235 @@ mono_create_trampoline (MonoMethod *method)
        }
 
        /*
-        * free the allocated strings.
+        * Standard epilog.
         */
-       if (local_size)
-               x86_mov_reg_imm (p, X86_EDX, g_free);
-       for (i = 1; i <= local_size; ++i) {
-               x86_push_membase (p, X86_EBP, LOC_POS * i);
-               x86_call_reg (p, X86_EDX);
+       x86_leave (p);
+       x86_ret (p);
+
+       g_assert (p - code_buffer < code_size);
+       res = (MonoPIFunc)g_memdup (code_buffer, p - code_buffer);
+
+       g_hash_table_insert (cache, sig, res);
+
+       return res;
+}
+
+#define MINV_POS  (- sizeof (MonoInvocation))
+#define STACK_POS (MINV_POS - sizeof (stackval) * sig->param_count)
+#define TYPE_OFFSET (G_STRUCT_OFFSET (stackval, type))
+
+/*
+ * Returns a pointer to a native function that can be used to
+ * call the specified method.
+ * The function created will receive the arguments according
+ * to the call convention specified in the method.
+ * This function works by creating a MonoInvocation structure,
+ * filling the fields in and calling ves_exec_method on it.
+ * Still need to figure out how to handle the exception stuff
+ * across the managed/unmanaged boundary.
+ */
+void *
+mono_arch_create_method_pointer (MonoMethod *method)
+{
+       MonoMethodSignature *sig;
+       MonoJitInfo *ji;
+       unsigned char *p, *code_buffer;
+       gint32 local_size;
+       gint32 stackval_pos, arg_pos = 8;
+       int i, size, align, cpos;
+       int *vtbuf;
+
+       sig = method->signature;
+
+       code_buffer = p = alloca (512); /* FIXME: check for overflows... */
+       vtbuf = alloca (sizeof(int)*sig->param_count);
+
+       local_size = sizeof (MonoInvocation) + sizeof (stackval) * (sig->param_count + 1);
+
+       local_size += 7;
+       local_size &= ~7;
+
+       stackval_pos = -local_size;
+
+       cpos = 0;
+       for (i = 0; i < sig->param_count; i++) {
+               MonoType *type = sig->params [i];
+               vtbuf [i] = -1;
+               if (type->type == MONO_TYPE_VALUETYPE) {
+                       MonoClass *klass = type->data.klass;
+                       if (klass->enumtype)
+                               continue;
+                       size = mono_class_native_size (klass, &align);
+                       cpos += align - 1;
+                       cpos &= ~(align - 1);
+                       vtbuf [i] = cpos;
+                       cpos += size;
+               }       
        }
+
+       cpos += 7;
+       cpos &= ~7;
+
+       local_size += cpos;
+
+       /*
+        * Standard function prolog.
+        */
+       x86_push_reg (p, X86_EBP);
+       x86_mov_reg_reg (p, X86_EBP, X86_ESP, 4);
+       x86_alu_reg_imm (p, X86_SUB, X86_ESP, local_size);
+
+       /*
+        * Initialize MonoInvocation fields, first the ones known now.
+        */
+       x86_mov_reg_imm (p, X86_EAX, 0);
+       x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, ex)), X86_EAX, 4);
+       x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, ex_handler)), X86_EAX, 4);
+       x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, parent)), X86_EAX, 4);
+       /*
+        * Set the method pointer.
+        */
+       x86_mov_membase_imm (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, method)), (int)method, 4);
+
+       if (sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->byref && !sig->ret->data.klass->enumtype) 
+               arg_pos += 4;
+
+       /*
+        * Handle this.
+        */
+       if (sig->hasthis) {
+               if (sig->call_convention != MONO_CALL_THISCALL) {
+                       /*
+                        * Grab it from the stack, otherwise it's already in ECX.
+                        */
+                       x86_mov_reg_membase (p, X86_ECX, X86_EBP, arg_pos, 4);
+                       arg_pos += 4;
+               }
+               x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, obj)), X86_ECX, 4);
+       }
+       /*
+        * Handle the arguments. stackval_pos is the posset of the stackval array from EBP.
+        * arg_pos is the offset from EBP to the incoming arg on the stack.
+        * We just call stackval_from_data to handle all the (nasty) issues....
+        */
+       x86_lea_membase (p, X86_EAX, X86_EBP, stackval_pos);
+       x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, stack_args)), X86_EAX, 4);
+       for (i = 0; i < sig->param_count; ++i) {
+               if (vtbuf [i] >= 0) {
+                       x86_lea_membase (p, X86_EAX, X86_EBP, - local_size + vtbuf [i]);
+                       x86_mov_membase_reg (p, X86_EBP, stackval_pos, X86_EAX, 4);
+               }
+               x86_mov_reg_imm (p, X86_ECX, stackval_from_data);
+               x86_lea_membase (p, X86_EDX, X86_EBP, arg_pos);
+               x86_lea_membase (p, X86_EAX, X86_EBP, stackval_pos);
+               x86_push_imm (p, sig->pinvoke);
+               x86_push_reg (p, X86_EDX);
+               x86_push_reg (p, X86_EAX);
+               x86_push_imm (p, sig->params [i]);
+               x86_call_reg (p, X86_ECX);
+               x86_alu_reg_imm (p, X86_SUB, X86_ESP, 16);
+               stackval_pos += sizeof (stackval);
+               /* fixme: alignment */
+               if (sig->pinvoke)
+                       arg_pos += mono_type_native_stack_size (sig->params [i], &align);
+               else
+                       arg_pos += mono_type_stack_size (sig->params [i], &align);
+       }
+
+       /*
+        * Handle the return value storage area.
+        */
+       x86_lea_membase (p, X86_EAX, X86_EBP, stackval_pos);
+       x86_mov_membase_reg (p, X86_EBP, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, retval)), X86_EAX, 4);
+       if (sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->byref) {
+               MonoClass *klass  = sig->ret->data.klass;
+               if (!klass->enumtype) {
+                       x86_mov_reg_membase (p, X86_ECX, X86_EBP, 8, 4);
+                       x86_mov_membase_reg (p, X86_EBP, stackval_pos, X86_ECX, 4);
+               }
+       }
+
+       /*
+        * Call the method.
+        */
+       x86_lea_membase (p, X86_EAX, X86_EBP, MINV_POS);
+       x86_push_reg (p, X86_EAX);
+       x86_mov_reg_imm (p, X86_EDX, ves_exec_method);
+       x86_call_reg (p, X86_EDX);
+       
+       /*
+        * Move the return value to the proper place.
+        */
+       x86_lea_membase (p, X86_EAX, X86_EBP, stackval_pos);
+       if (sig->ret->byref) {
+               x86_mov_reg_membase (p, X86_EAX, X86_EAX, 0, 4);
+       } else {
+               int simpletype = sig->ret->type;        
+       enum_retvalue:
+               switch (sig->ret->type) {
+               case MONO_TYPE_VOID:
+                       break;
+               case MONO_TYPE_BOOLEAN:
+               case MONO_TYPE_I1:
+               case MONO_TYPE_U1:
+                       x86_mov_reg_membase (p, X86_EAX, X86_EAX, 0, 1);
+                       break;
+               case MONO_TYPE_CHAR:
+               case MONO_TYPE_I2:
+               case MONO_TYPE_U2:
+                       x86_mov_reg_membase (p, X86_EAX, X86_EAX, 0, 2);
+                       break;
+               case MONO_TYPE_I4:
+               case MONO_TYPE_U4:
+               case MONO_TYPE_I:
+               case MONO_TYPE_U:
+               case MONO_TYPE_OBJECT:
+               case MONO_TYPE_STRING:
+               case MONO_TYPE_CLASS:
+                       x86_mov_reg_membase (p, X86_EAX, X86_EAX, 0, 4);
+                       break;
+               case MONO_TYPE_I8:
+                       x86_mov_reg_membase (p, X86_EDX, X86_EAX, 4, 4);
+                       x86_mov_reg_membase (p, X86_EAX, X86_EAX, 0, 4);
+                       break;
+               case MONO_TYPE_R8:
+                       x86_fld_membase (p, X86_EAX, 0, TRUE);
+                       break;
+               case MONO_TYPE_VALUETYPE:
+                       if (sig->ret->data.klass->enumtype) {
+                               simpletype = sig->ret->data.klass->enum_basetype->type;
+                               goto enum_retvalue;
+                       }
+               
+                       x86_push_imm (p, sig->pinvoke);
+                       x86_push_membase (p, X86_EBP, stackval_pos);
+                       x86_push_reg (p, X86_EAX);
+                       x86_push_imm (p, sig->ret);
+                       x86_mov_reg_imm (p, X86_ECX, stackval_to_data);
+                       x86_call_reg (p, X86_ECX);
+                       x86_alu_reg_imm (p, X86_SUB, X86_ESP, 16);
+                       
+                       break;
+               default:
+                       g_error ("Type 0x%x not handled yet in thunk creation", sig->ret->type);
+                       break;
+               }
+       }
+       
        /*
         * Standard epilog.
         */
        x86_leave (p);
        x86_ret (p);
 
-       return g_memdup (code_buffer, p - code_buffer);
-}
+       g_assert (p - code_buffer < 512);
+
+       ji = g_new0 (MonoJitInfo, 1);
+       ji->method = method;
+       ji->code_size = p - code_buffer;
+       ji->code_start = g_memdup (code_buffer, p - code_buffer);
 
+       mono_jit_info_table_add (mono_get_root_domain (), ji);
+
+       return ji->code_start;
+}