Flush
[mono.git] / mono / jit / emit-x86.c
index 95b19bb6842ae1fb388ab26c4879104737d4c39e..bbeb063383d03203698b71619751883ce50e694c 100644 (file)
 #include "jit.h"
 #include "helpers.h"
 #include "codegen.h"
-#include "debug.h"
 
 
 //#define DEBUG_REGALLOC
 //#define DEBUG_SPILLS
 
+const char *
+arch_get_reg_name (int regnum)
+{
+       switch (regnum) {
+       case 0:
+               return "EAX";
+       case 1:
+               return "ECX";
+       case 2:
+               return "EDX";
+       case 3:
+               return "EBX";
+       case 4:
+               return "ESP";
+       case 5:
+               return "EBP";
+       case 6:
+               return "ESI";
+       case 7:
+               return "EDI";
+       }
+
+       g_assert_not_reached ();
+       return NULL;
+}
+
+
 /* 
  * we may want a x86-specific header or we 
  * can just declare it extern in x86.brg.
@@ -39,6 +65,9 @@ int mono_x86_have_cmov = 0;
 static int 
 cpuid (int id, int* p_eax, int* p_ebx, int* p_ecx, int* p_edx)
 {
+#ifdef PIC
+       return 0;
+#else
        int have_cpuid = 0;
        __asm__  __volatile__ (
                "pushfl\n"
@@ -64,6 +93,7 @@ cpuid (int id, int* p_eax, int* p_ebx, int* p_ecx, int* p_edx)
                return 1;
        }
        return 0;
+#endif
 }
 
 void
@@ -78,62 +108,123 @@ mono_cpu_detect (void) {
        }
 }
 
+/*
+ * arch_get_argument_info:
+ * @csig:  a method signature
+ * @param_count: the number of parameters to consider
+ * @arg_info: an array to store the result infos
+ *
+ * Gathers information on parameters such as size, alignment and
+ * padding. arg_info should be large enought to hold param_count + 1 entries. 
+ *
+ * Returns the size of the activation frame.
+ */
+int
+arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJitArgumentInfo *arg_info)
+{
+       int k, frame_size = 0;
+       int size, align, pad;
+       int offset = 8;
+
+       if (MONO_TYPE_ISSTRUCT (csig->ret)) { 
+               frame_size += sizeof (gpointer);
+               offset += 4;
+       }
+
+       arg_info [0].offset = offset;
+
+       if (csig->hasthis) {
+               frame_size += sizeof (gpointer);
+               offset += 4;
+       }
+
+       arg_info [0].size = frame_size;
+
+       for (k = 0; k < param_count; k++) {
+               
+               if (csig->pinvoke)
+                       size = mono_type_native_stack_size (csig->params [k], &align);
+               else
+                       size = mono_type_stack_size (csig->params [k], &align);
+               
+               frame_size += pad = (align - (frame_size & (align - 1))) & (align - 1); 
+               arg_info [k].pad = pad;
+               frame_size += size;
+               arg_info [k + 1].pad = 0;
+               arg_info [k + 1].size = size;
+               offset += pad;
+               arg_info [k + 1].offset = offset;
+               offset += size;
+       }
+
+       align = MONO_FRAME_ALIGNMENT;
+       frame_size += pad = (align - (frame_size & (align - 1))) & (align - 1);
+       arg_info [k].pad = pad;
+
+       return frame_size;
+}
+
 static void
 enter_method (MonoMethod *method, char *ebp)
 {
        int i, j;
        MonoClass *class;
        MonoObject *o;
-       char *tmpsig;
+       MonoJitArgumentInfo *arg_info;
+       MonoMethodSignature *sig;
+       char *fname;
 
-       tmpsig = mono_signature_get_desc(method->signature, TRUE);
-       printf ("ENTER: %s.%s::%s (%s)\n(", method->klass->name_space,
-               method->klass->name, method->name, tmpsig);
-       g_free (tmpsig);
+       fname = mono_method_full_name (method, TRUE);
+       printf ("ENTER: %s\n(", fname);
+       g_free (fname);
        
-       if (((int)ebp & 3) != 0) {
+       if (((int)ebp & (MONO_FRAME_ALIGNMENT - 1)) != 0) {
                g_error ("unaligned stack detected (%p)", ebp);
        }
 
-       ebp += 8;
+       sig = method->signature;
 
-       if (ISSTRUCT (method->signature->ret)) {
-               int size, align;
-               
-               g_assert (!method->signature->ret->byref);
+       if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE)
+               g_assert (!sig->pinvoke);
 
-               size = mono_type_stack_size (method->signature->ret, &align);
+       arg_info = alloca (sizeof (MonoJitArgumentInfo) * (sig->param_count + 1));
+
+       arch_get_argument_info (sig, sig->param_count, arg_info);
+
+       if (MONO_TYPE_ISSTRUCT (method->signature->ret)) {
+               g_assert (!method->signature->ret->byref);
 
-               printf ("VALUERET:%p, ", *((gpointer *)ebp));
-               ebp += sizeof (gpointer);
+               printf ("VALUERET:%p, ", *((gpointer *)(ebp + 8)));
        }
 
        if (method->signature->hasthis) {
+               gpointer *this = (gpointer *)(ebp + arg_info [0].offset);
                if (method->klass->valuetype) {
-                       printf ("value:%p, ", *((gpointer *)ebp));
+                       printf ("value:%p, ", *this);
                } else {
-                       o = *((MonoObject **)ebp);
+                       o = *((MonoObject **)this);
 
-                       g_assert (o);
-
-                       class = o->vtable->klass;
+                       if (o) {
+                               class = o->vtable->klass;
 
-                       if (class == mono_defaults.string_class) {
-                               printf ("this:[STRING:%p:%s], ", o, mono_string_to_utf8 ((MonoString *)o));
-                       } else {
-                               printf ("this:%p[%s.%s], ", o, class->name_space, class->name);
-                       }
+                               if (class == mono_defaults.string_class) {
+                                       printf ("this:[STRING:%p:%s], ", o, mono_string_to_utf8 ((MonoString *)o));
+                               } else {
+                                       printf ("this:%p[%s.%s], ", o, class->name_space, class->name);
+                               }
+                       } else 
+                               printf ("this:NULL, ");
                }
-               ebp += sizeof (gpointer);
        }
 
        for (i = 0; i < method->signature->param_count; ++i) {
-               MonoType *type = method->signature->params [i];
-               int size, align;
-               size = mono_type_stack_size (type, &align);
+               gpointer *cpos = (gpointer *)(ebp + arg_info [i + 1].offset);
+               int size = arg_info [i + 1].size;
 
+               MonoType *type = method->signature->params [i];
+               
                if (type->byref) {
-                       printf ("[BYREF:%p], ", *((gpointer *)ebp)); 
+                       printf ("[BYREF:%p], ", *cpos); 
                } else switch (type->type) {
                        
                case MONO_TYPE_BOOLEAN:
@@ -146,10 +237,10 @@ enter_method (MonoMethod *method, char *ebp)
                case MONO_TYPE_U4:
                case MONO_TYPE_I:
                case MONO_TYPE_U:
-                       printf ("%d, ", *((int *)(ebp)));
+                       printf ("%d, ", *((int *)(cpos)));
                        break;
                case MONO_TYPE_STRING: {
-                       MonoString *s = *((MonoString **)ebp);
+                       MonoString *s = *((MonoString **)cpos);
                        if (s) {
                                g_assert (((MonoObject *)s)->vtable->klass == mono_defaults.string_class);
                                printf ("[STRING:%p:%s], ", s, mono_string_to_utf8 (s));
@@ -159,7 +250,7 @@ enter_method (MonoMethod *method, char *ebp)
                }
                case MONO_TYPE_CLASS:
                case MONO_TYPE_OBJECT: {
-                       o = *((MonoObject **)ebp);
+                       o = *((MonoObject **)cpos);
                        if (o) {
                                class = o->vtable->klass;
                    
@@ -170,7 +261,7 @@ enter_method (MonoMethod *method, char *ebp)
                                } else
                                        printf ("[%s.%s:%p], ", class->name_space, class->name, o);
                        } else {
-                               printf ("%p, ", *((gpointer *)(ebp)));                          
+                               printf ("%p, ", *((gpointer *)(cpos)));                         
                        }
                        break;
                }
@@ -178,30 +269,26 @@ enter_method (MonoMethod *method, char *ebp)
                case MONO_TYPE_FNPTR:
                case MONO_TYPE_ARRAY:
                case MONO_TYPE_SZARRAY:
-                       printf ("%p, ", *((gpointer *)(ebp)));
+                       printf ("%p, ", *((gpointer *)(cpos)));
                        break;
                case MONO_TYPE_I8:
-                       printf ("%lld, ", *((gint64 *)(ebp)));
+                       printf ("%lld, ", *((gint64 *)(cpos)));
                        break;
                case MONO_TYPE_R4:
-                       printf ("%f, ", *((float *)(ebp)));
+                       printf ("%f, ", *((float *)(cpos)));
                        break;
                case MONO_TYPE_R8:
-                       printf ("%f, ", *((double *)(ebp)));
+                       printf ("%f, ", *((double *)(cpos)));
                        break;
                case MONO_TYPE_VALUETYPE: 
                        printf ("[");
                        for (j = 0; j < size; j++)
-                               printf ("%02x,", *((guint8*)ebp +j));
+                               printf ("%02x,", *((guint8*)cpos +j));
                        printf ("], ");
                        break;
                default:
                        printf ("XX, ");
                }
-
-               g_assert (align == 4 || align == 8);
-               ebp += size + align - 1;
-               ebp = (gpointer)((unsigned)ebp & ~(align - 1));
        }
 
        printf (")\n");
@@ -211,9 +298,11 @@ static void
 leave_method (MonoMethod *method, int edx, int eax, double test)
 {
        gint64 l;
+       char *fname;
 
-       printf ("LEAVE: %s.%s::%s ", method->klass->name_space,
-               method->klass->name, method->name);
+       fname = mono_method_full_name (method, TRUE);
+       printf ("LEAVE: %s", fname);
+       g_free (fname);
 
        switch (method->signature->ret->type) {
        case MONO_TYPE_VOID:
@@ -254,6 +343,8 @@ leave_method (MonoMethod *method, int edx, int eax, double test)
                                printf ("[BOOLEAN:%p:%d]", o, *((guint8 *)o + sizeof (MonoObject)));            
                        } else if  (o->vtable->klass == mono_defaults.int32_class) {
                                printf ("[INT32:%p:%d]", o, *((gint32 *)((char *)o + sizeof (MonoObject))));    
+                       } else if  (o->vtable->klass == mono_defaults.int64_class) {
+                               printf ("[INT64:%p:%lld]", o, *((gint64 *)((char *)o + sizeof (MonoObject))));  
                        } else
                                printf ("[%s.%s:%p]", o->vtable->klass->name_space, o->vtable->klass->name, o);
                } else
@@ -294,28 +385,69 @@ arch_emit_prologue (MonoFlowGraph *cfg)
 {
        MonoMethod *method = cfg->method;
        MonoMethodHeader *header = ((MonoMethodNormal *)method)->header;
-       int i, j, k, alloc_size;
+       int i, j, k, alloc_size, pos;
 
        x86_push_reg (cfg->code, X86_EBP);
        x86_mov_reg_reg (cfg->code, X86_EBP, X86_ESP, 4);
 
        alloc_size = cfg->locals_size;
+       pos = 0;
 
-       if (mono_regset_reg_used (cfg->rs, X86_EBX)) {
-               x86_push_reg (cfg->code, X86_EBX);
-               alloc_size -= 4;
-       }
+       if (method->save_lmf) {
+               
+               pos += sizeof (MonoLMF);
 
-       if (mono_regset_reg_used (cfg->rs, X86_EDI)) {
+               /* save the current IP */
+               cfg->lmfip_offset = cfg->code + 1 - cfg->start;
+               x86_push_imm (cfg->code, 0);
+               /* save all caller saved regs */
+               x86_push_reg (cfg->code, X86_EBX);
                x86_push_reg (cfg->code, X86_EDI);
-               alloc_size -= 4;
-       }
-
-       if (mono_regset_reg_used (cfg->rs, X86_ESI)) {
                x86_push_reg (cfg->code, X86_ESI);
-               alloc_size -= 4;
+               x86_push_reg (cfg->code, X86_EBP);
+
+               /* save method info */
+               x86_push_imm (cfg->code, method);
+       
+               /* get the address of lmf for the current thread */
+               mono_add_jump_info (cfg, cfg->code, MONO_JUMP_INFO_ABS, mono_get_lmf_addr);
+               x86_call_code (cfg->code, 0);
+
+               /* push lmf */
+               x86_push_reg (cfg->code, X86_EAX); 
+               /* push *lfm (previous_lmf) */
+               x86_push_membase (cfg->code, X86_EAX, 0);
+               /* *(lmf) = ESP */
+               x86_mov_membase_reg (cfg->code, X86_EAX, 0, X86_ESP, 4);
+       } else {
+
+#if 0
+               /* activation frame alignment check */
+               x86_mov_reg_reg (cfg->code, X86_EAX, X86_ESP, 4);
+               x86_alu_reg_imm (cfg->code, X86_AND, X86_EAX, MONO_FRAME_ALIGNMENT - 1);
+               x86_alu_reg_imm (cfg->code, X86_CMP, X86_EAX, 0);
+               x86_branch32 (cfg->code, X86_CC_EQ, 1, FALSE);
+               x86_breakpoint (cfg->code);
+#endif
+
+               if (mono_regset_reg_used (cfg->rs, X86_EBX)) {
+                       x86_push_reg (cfg->code, X86_EBX);
+                       pos += 4;
+               }
+
+               if (mono_regset_reg_used (cfg->rs, X86_EDI)) {
+                       x86_push_reg (cfg->code, X86_EDI);
+                       pos += 4;
+               }
+
+               if (mono_regset_reg_used (cfg->rs, X86_ESI)) {
+                       x86_push_reg (cfg->code, X86_ESI);
+                       pos += 4;
+               }
        }
 
+       alloc_size -= pos;
+
        if (alloc_size)
                x86_alu_reg_imm (cfg->code, X86_SUB, X86_ESP, alloc_size);
 
@@ -473,24 +605,85 @@ arch_emit_epilogue (MonoFlowGraph *cfg)
                x86_pop_reg (cfg->code, X86_EAX);
        }
 
-       pos = -4;
-       if (mono_regset_reg_used (cfg->rs, X86_EBX)) {
-               x86_mov_reg_membase (cfg->code, X86_EBX, X86_EBP, pos, 4);
-               pos -= 4;
+       pos = 0;
+       
+       if (cfg->method->save_lmf) {
+               pos = -sizeof (MonoLMF);
+       } else {
+               if (mono_regset_reg_used (cfg->rs, X86_EBX)) {
+                       pos -= 4;
        }
-       if (mono_regset_reg_used (cfg->rs, X86_EDI)) {
-               x86_mov_reg_membase (cfg->code, X86_EDI, X86_EBP, pos, 4);
-               pos -= 4;
+               if (mono_regset_reg_used (cfg->rs, X86_EDI)) {
+                       pos -= 4;
+               }
+               if (mono_regset_reg_used (cfg->rs, X86_ESI)) {
+                       pos -= 4;
+               }
        }
-       if (mono_regset_reg_used (cfg->rs, X86_ESI)) {
-               x86_mov_reg_membase (cfg->code, X86_ESI, X86_EBP, pos, 4);
-               pos -= 4;
+       if (pos)
+               x86_lea_membase (cfg->code, X86_ESP, X86_EBP, pos);
+
+       if (cfg->method->save_lmf) {
+               /* ebx = previous_lmf */
+               x86_pop_reg (cfg->code, X86_EBX);
+               /* edi = lmf */
+               x86_pop_reg (cfg->code, X86_EDI);
+               /* *(lmf) = previous_lmf */
+               x86_mov_membase_reg (cfg->code, X86_EDI, 0, X86_EBX, 4);
+
+               /* discard method info */
+               x86_pop_reg (cfg->code, X86_ESI);
+
+               /* restore caller saved regs */
+               x86_pop_reg (cfg->code, X86_EBP);
+               x86_pop_reg (cfg->code, X86_ESI);
+               x86_pop_reg (cfg->code, X86_EDI);
+               x86_pop_reg (cfg->code, X86_EBX);
+
+       } else {
+
+               if (mono_regset_reg_used (cfg->rs, X86_ESI)) {
+                       x86_pop_reg (cfg->code, X86_ESI);
+               }
+               if (mono_regset_reg_used (cfg->rs, X86_EDI)) {
+                       x86_pop_reg (cfg->code, X86_EDI);
+               }
+               if (mono_regset_reg_used (cfg->rs, X86_EBX)) {
+                       x86_pop_reg (cfg->code, X86_EBX);
+               }
        }
 
        x86_leave (cfg->code);
        x86_ret (cfg->code);
 }
 
+static void
+init_varinfo (MonoFlowGraph *cfg, MonoVarInfo *vi)
+{
+       vi->range.last_use.abs_pos = 0;
+       vi->range.first_use.pos.bid = 0xffff;
+       vi->range.first_use.pos.tid = 0;        
+       vi->isvolatile = 0;
+       vi->reg = -1;
+       vi->varnum = cfg->varinfo->len;
+}
+
+int
+arch_allocate_arg (MonoFlowGraph *cfg, MonoJitArgumentInfo *info, MonoValueType type)
+{
+       MonoVarInfo vi;
+
+       mono_jit_stats.allocate_var++;
+
+       init_varinfo (cfg, &vi);
+       vi.isvolatile = 1;
+       
+       SET_VARINFO (vi, type, MONO_ARGVAR, info->offset, info->size);
+       g_array_append_val (cfg->varinfo, vi);
+
+       return cfg->varinfo->len - 1;
+}
+
 int
 arch_allocate_var (MonoFlowGraph *cfg, int size, int align, MonoVarType vartype, MonoValueType type)
 {
@@ -498,43 +691,17 @@ arch_allocate_var (MonoFlowGraph *cfg, int size, int align, MonoVarType vartype,
 
        mono_jit_stats.allocate_var++;
 
-       vi.range.last_use.abs_pos = 0;
-       vi.range.first_use.pos.bid = 0xffff;
-       vi.range.first_use.pos.tid = 0; 
-       vi.isvolatile = 0;
-       vi.reg = -1;
-       vi.varnum = cfg->varinfo->len;
+       init_varinfo (cfg, &vi);
 
        if (size != sizeof (gpointer))
                vi.isvolatile = 1;
        
-       switch (vartype) {
-       case MONO_TEMPVAR:
-       case MONO_LOCALVAR: {
-               cfg->locals_size += size;
-               cfg->locals_size += align - 1;
-               cfg->locals_size &= ~(align - 1);
-
-               SET_VARINFO (vi, type, vartype, - cfg->locals_size, size);
-               g_array_append_val (cfg->varinfo, vi);
-               break;
-       }
-       case MONO_ARGVAR: {
-               int arg_start = 8 + cfg->has_vtarg*4;
+       cfg->locals_size += size;
+       cfg->locals_size += align - 1;
+       cfg->locals_size &= ~(align - 1);
 
-               g_assert ((align & 3) == 0);
-
-               SET_VARINFO (vi, type, vartype, cfg->args_size + arg_start, size);
-               g_array_append_val (cfg->varinfo, vi);
-               
-               cfg->args_size += size;
-               cfg->args_size += 3;
-               cfg->args_size &= ~3;
-               break;
-       }
-       default:
-               g_assert_not_reached ();
-       }
+       SET_VARINFO (vi, type, vartype, - cfg->locals_size, size);
+       g_array_append_val (cfg->varinfo, vi);
 
        return cfg->varinfo->len - 1;
 }
@@ -560,9 +727,8 @@ mono_label_cfg (MonoFlowGraph *cfg)
                        mbstate =  mono_burg_label (t1, cfg);
 
                        if (!mbstate) {
-                               if (mono_debug_format != MONO_DEBUG_FORMAT_NONE)
-                                       return FALSE;
-                               g_warning ("tree does not match");
+                               g_warning ("tree does not match in %s: 0x%04x",
+                                          mono_method_full_name (cfg->method, TRUE), t1->cli_addr);
                                mono_print_ctree (cfg, t1); printf ("\n\n");
 
                                mono_print_forest (cfg, forest);
@@ -793,6 +959,7 @@ tree_allocate_regs (MonoFlowGraph *cfg, MBTree *tree, int goal, MonoRegSet *rs,
               
        default:
                /* do nothing */
+               break;
        }
 
 #ifdef DEBUG_REGALLOC
@@ -1037,6 +1204,12 @@ mono_compute_branches (MonoFlowGraph *cfg)
                }
                x86_patch (ip, target);
        }
+
+       /* patch the IP in the LMF saving code */
+       if (cfg->lmfip_offset) {
+               *((guint32 *)(cfg->start + cfg->lmfip_offset)) =  
+                       (gint32)(cfg->start + cfg->lmfip_offset);
+       }
 }
 
 void
@@ -1058,6 +1231,7 @@ arch_jit_compile_cfg (MonoDomain *target_domain, MonoFlowGraph *cfg)
        MonoJitInfo *ji;
        guint32 ls_used_mask = 0;
        MonoMethod *method = cfg->method;
+       int offset, gap;
 
        ji = mono_mempool_alloc0 (target_domain->mp, sizeof (MonoJitInfo));
                
@@ -1073,11 +1247,10 @@ arch_jit_compile_cfg (MonoDomain *target_domain, MonoFlowGraph *cfg)
                mono_linear_scan (cfg, &ls_used_mask);
                cfg->rs->used_mask |= ls_used_mask;
        }
-
+       
        if (mono_jit_dump_forest) {
                int i;
-               printf ("FOREST %s.%s:%s\n", method->klass->name_space,
-                       method->klass->name, method->name);
+               printf ("FOREST %s\n", mono_method_full_name (method, TRUE));
                for (i = 0; i < cfg->block_count; i++) {
                        printf ("BLOCK %d:\n", i);
                        mono_print_forest (cfg, cfg->bblocks [i].forest);
@@ -1099,6 +1272,21 @@ arch_jit_compile_cfg (MonoDomain *target_domain, MonoFlowGraph *cfg)
        arch_emit_epilogue (cfg);               
        cfg->epilogue_end = cfg->code - cfg->start;
 
+       offset = cfg->code - cfg->start;
+       gap = cfg->code_size - offset;
+       if (gap > 0) {
+               char *org = cfg->start;
+#if 0
+               cfg->start = g_realloc (cfg->start, offset);
+#else
+               cfg->start = mono_mempool_alloc (target_domain->code_mp, offset);
+               memcpy (cfg->start, org, offset);
+               g_free (org);
+#endif
+               cfg->code_size = offset;
+               cfg->code = cfg->start + offset;
+       }
+
        mono_compute_branches (cfg);
 
        ji->code_size = cfg->code - cfg->start;