[jit] Allocate the thunks area next to the method code if the backend can compute...
[mono.git] / mono / mini / mini-amd64.c
index 1a31eab4d025032dbed313949f1e334befc62c40..c24fe349514d516044634b0139fa92ccab4fe0c5 100755 (executable)
@@ -32,6 +32,7 @@
 #include <mono/utils/mono-memory-model.h>
 #include <mono/utils/mono-tls.h>
 #include <mono/utils/mono-hwcap-x86.h>
+#include <mono/utils/mono-threads.h>
 
 #include "trace.h"
 #include "ir-emit.h"
@@ -52,7 +53,7 @@ static gboolean optimize_for_xen = TRUE;
 
 #define IS_REX(inst) (((inst) >= 0x40) && ((inst) <= 0x4f))
 
-#ifdef HOST_WIN32
+#ifdef TARGET_WIN32
 /* Under windows, the calling convention is never stdcall */
 #define CALLCONV_IS_STDCALL(call_conv) (FALSE)
 #else
@@ -85,6 +86,9 @@ static int breakpoint_fault_size;
 /* The size of the single step instruction causing the actual fault */
 static int single_step_fault_size;
 
+/* The single step trampoline */
+static gpointer ss_trampoline;
+
 /* Offset between fp and the first argument in the callee */
 #define ARGS_OFFSET 16
 #define GP_SCRATCH_REG AMD64_R11
@@ -523,7 +527,7 @@ typedef struct {
 
 #define DEBUG(a) if (cfg->verbose_level > 1) a
 
-#ifdef HOST_WIN32
+#ifdef TARGET_WIN32
 static AMD64_Reg_No param_regs [] = { AMD64_RCX, AMD64_RDX, AMD64_R8, AMD64_R9 };
 
 static AMD64_Reg_No return_regs [] = { AMD64_RAX, AMD64_RDX };
@@ -551,7 +555,7 @@ add_general (guint32 *gr, guint32 *stack_size, ArgInfo *ainfo)
     }
 }
 
-#ifdef HOST_WIN32
+#ifdef TARGET_WIN32
 #define FLOAT_PARAM_REGS 4
 #else
 #define FLOAT_PARAM_REGS 8
@@ -615,7 +619,7 @@ merge_argument_class_from_type (MonoGenericSharingContext *gsctx, MonoType *type
                break;
        case MONO_TYPE_R4:
        case MONO_TYPE_R8:
-#ifdef HOST_WIN32
+#ifdef TARGET_WIN32
                class2 = ARG_CLASS_INTEGER;
 #else
                class2 = ARG_CLASS_SSE;
@@ -748,7 +752,7 @@ add_valuetype (MonoGenericSharingContext *gsctx, MonoMethodSignature *sig, ArgIn
 
        klass = mono_class_from_mono_type (type);
        size = mini_type_stack_size_full (gsctx, &klass->byval_arg, NULL, sig->pinvoke);
-#ifndef HOST_WIN32
+#ifndef TARGET_WIN32
        if (!sig->pinvoke && ((is_return && (size == 8)) || (!is_return && (size <= 16)))) {
                /* We pass and return vtypes of size 8 in a register */
        } else if (!sig->pinvoke || (size == 0) || (size > 16)) {
@@ -830,7 +834,7 @@ add_valuetype (MonoGenericSharingContext *gsctx, MonoMethodSignature *sig, ArgIn
                g_assert (info);
                g_assert (fields);
 
-#ifndef HOST_WIN32
+#ifndef TARGET_WIN32
                if (info->native_size > 16) {
                        ainfo->offset = *stack_size;
                        *stack_size += ALIGN_TO (info->native_size, 8);
@@ -1003,7 +1007,7 @@ get_call_info (MonoGenericSharingContext *gsctx, MonoMemPool *mp, MonoMethodSign
        gr = 0;
        fr = 0;
 
-#ifdef HOST_WIN32
+#ifdef TARGET_WIN32
        /* Reserve space where the callee can save the argument registers */
        stack_size = 4 * sizeof (mgreg_t);
 #endif
@@ -1112,7 +1116,7 @@ get_call_info (MonoGenericSharingContext *gsctx, MonoMemPool *mp, MonoMethodSign
                ArgInfo *ainfo = &cinfo->args [sig->hasthis + i];
                MonoType *ptype;
 
-#ifdef HOST_WIN32
+#ifdef TARGET_WIN32
                /* The float param registers and other param registers must be the same index on Windows x64.*/
                if (gr > fr)
                        fr = gr;
@@ -1537,7 +1541,7 @@ mono_arch_get_global_int_regs (MonoCompile *cfg)
 #ifndef __native_client_codegen__
                regs = g_list_prepend (regs, (gpointer)AMD64_R15);
 #endif
-#ifdef HOST_WIN32
+#ifdef TARGET_WIN32
                regs = g_list_prepend (regs, (gpointer)AMD64_RDI);
                regs = g_list_prepend (regs, (gpointer)AMD64_RSI);
 #endif
@@ -2005,13 +2009,17 @@ mono_arch_create_vars (MonoCompile *cfg)
                }
        }
 
-       if (cfg->gen_seq_points_debug_data) {
+       if (cfg->gen_sdb_seq_points) {
                MonoInst *ins;
 
                if (cfg->compile_aot) {
                        MonoInst *ins = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
                        ins->flags |= MONO_INST_VOLATILE;
                        cfg->arch.seq_point_info_var = ins;
+
+                       ins = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
+                       ins->flags |= MONO_INST_VOLATILE;
+                       cfg->arch.ss_tramp_var = ins;
                }
 
            ins = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
@@ -2024,7 +2032,7 @@ mono_arch_create_vars (MonoCompile *cfg)
 
        if (cfg->method->save_lmf) {
                cfg->lmf_ir = TRUE;
-#if !defined(HOST_WIN32)
+#if !defined(TARGET_WIN32)
                if (mono_get_lmf_tls_offset () != -1 && !optimize_for_xen)
                        cfg->lmf_ir_mono_lmf = TRUE;
 #endif
@@ -2995,12 +3003,12 @@ emit_call_body (MonoCompile *cfg, guint8 *code, guint32 patch_type, gconstpointe
 static inline guint8*
 emit_call (MonoCompile *cfg, guint8 *code, guint32 patch_type, gconstpointer data, gboolean win64_adjust_stack)
 {
-#ifdef HOST_WIN32
+#ifdef TARGET_WIN32
        if (win64_adjust_stack)
                amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 32);
 #endif
        code = emit_call_body (cfg, code, patch_type, data);
-#ifdef HOST_WIN32
+#ifdef TARGET_WIN32
        if (win64_adjust_stack)
                amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, 32);
 #endif 
@@ -3340,7 +3348,7 @@ mono_emit_stack_alloc (MonoCompile *cfg, guchar *code, MonoInst* tree)
        int sreg = tree->sreg1;
        int need_touch = FALSE;
 
-#if defined(HOST_WIN32)
+#if defined(TARGET_WIN32)
        need_touch = TRUE;
 #elif defined(MONO_ARCH_SIGSEGV_ON_ALTSTACK)
        if (!tree->flags & MONO_INST_INIT)
@@ -3584,7 +3592,7 @@ mono_amd64_get_tls_gs_offset (void)
 guint8*
 mono_amd64_emit_tls_get (guint8* code, int dreg, int tls_offset)
 {
-#ifdef HOST_WIN32
+#ifdef TARGET_WIN32
        if (tls_offset < 64) {
                x86_prefix (code, X86_GS_PREFIX);
                amd64_mov_reg_mem (code, dreg, (tls_offset * 8) + 0x1480, 8);
@@ -3650,7 +3658,7 @@ emit_tls_get_reg (guint8* code, int dreg, int offset_reg)
 static guint8*
 amd64_emit_tls_set (guint8 *code, int sreg, int tls_offset)
 {
-#ifdef HOST_WIN32
+#ifdef TARGET_WIN32
        g_assert_not_reached ();
 #elif defined(__APPLE__)
        x86_prefix (code, X86_GS_PREFIX);
@@ -3667,7 +3675,7 @@ static guint8*
 amd64_emit_tls_set_reg (guint8 *code, int sreg, int offset_reg)
 {
        /* offset_reg contains a value translated by mono_arch_translate_tls_offset () */
-#ifdef HOST_WIN32
+#ifdef TARGET_WIN32
        g_assert_not_reached ();
 #elif defined(__APPLE__)
        x86_prefix (code, X86_GS_PREFIX);
@@ -4169,17 +4177,33 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                case OP_SEQ_POINT: {
                        int i;
 
-                       /* 
-                        * Read from the single stepping trigger page. This will cause a
-                        * SIGSEGV when single stepping is enabled.
-                        * We do this _before_ the breakpoint, so single stepping after
-                        * a breakpoint is hit will step to the next IL offset.
-                        */
                        if (ins->flags & MONO_INST_SINGLE_STEP_LOC) {
-                               MonoInst *var = cfg->arch.ss_trigger_page_var;
+                               if (cfg->compile_aot) {
+                                       MonoInst *var = cfg->arch.ss_tramp_var;
+                                       guint8 *label;
+
+                                       /* Load ss_tramp_var */
+                                       amd64_mov_reg_membase (code, AMD64_R11, var->inst_basereg, var->inst_offset, 8);
+                                       /* Load the trampoline address */
+                                       amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, 0, 8);
+                                       /* Call it if it is non-null */
+                                       amd64_test_reg_reg (code, AMD64_R11, AMD64_R11);
+                                       label = code;
+                                       amd64_branch8 (code, X86_CC_Z, 0, FALSE);
+                                       amd64_call_reg (code, AMD64_R11);
+                                       amd64_patch (label, code);
+                               } else {
+                                       /* 
+                                        * Read from the single stepping trigger page. This will cause a
+                                        * SIGSEGV when single stepping is enabled.
+                                        * We do this _before_ the breakpoint, so single stepping after
+                                        * a breakpoint is hit will step to the next IL offset.
+                                        */
+                                       MonoInst *var = cfg->arch.ss_trigger_page_var;
 
-                               amd64_mov_reg_membase (code, AMD64_R11, var->inst_basereg, var->inst_offset, 8);
-                               amd64_alu_membase_imm_size (code, X86_CMP, AMD64_R11, 0, 0, 4);
+                                       amd64_mov_reg_membase (code, AMD64_R11, var->inst_basereg, var->inst_offset, 8);
+                                       amd64_alu_membase_imm_size (code, X86_CMP, AMD64_R11, 0, 0, 4);
+                               }
                        }
 
                        /* 
@@ -4191,13 +4215,19 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                                guint32 offset = code - cfg->native_code;
                                guint32 val;
                                MonoInst *info_var = cfg->arch.seq_point_info_var;
+                               guint8 *label;
 
                                /* Load info var */
                                amd64_mov_reg_membase (code, AMD64_R11, info_var->inst_basereg, info_var->inst_offset, 8);
                                val = ((offset) * sizeof (guint8*)) + MONO_STRUCT_OFFSET (SeqPointInfo, bp_addrs);
-                               /* Load the info->bp_addrs [offset], which is either a valid address or the address of a trigger page */
+                               /* Load the info->bp_addrs [offset], which is either NULL or the address of the breakpoint trampoline */
                                amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, val, 8);
-                               amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, 0, 8);
+                               amd64_test_reg_reg (code, AMD64_R11, AMD64_R11);
+                               label = code;
+                               amd64_branch8 (code, X86_CC_Z, 0, FALSE);
+                               /* Call the trampoline */
+                               amd64_call_reg (code, AMD64_R11);
+                               amd64_patch (label, code);
                        } else {
                                /* 
                                 * A placeholder for a possible breakpoint inserted by
@@ -4965,7 +4995,10 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        amd64_ret (code);
                        break;
                }
-
+               case OP_GET_EX_OBJ:
+                       if (ins->dreg != AMD64_RAX)
+                               amd64_mov_reg_reg (code, ins->dreg, AMD64_RAX, sizeof (gpointer));
+                       break;
                case OP_LABEL:
                        ins->inst_c0 = code - cfg->native_code;
                        break;
@@ -6523,22 +6556,28 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        MONO_VARINFO (cfg, ins->inst_c0)->live_range_end = code - cfg->native_code;
                        break;
                }
-               case OP_NACL_GC_SAFE_POINT: {
-#if defined(__native_client_codegen__) && defined(__native_client_gc__)
-                       if (cfg->compile_aot)
-                               code = emit_call (cfg, code, MONO_PATCH_INFO_ABS, (gpointer)mono_nacl_gc, TRUE);
-                       else {
-                               guint8 *br [1];
+               case OP_GC_SAFE_POINT: {
+                       gpointer polling_func = NULL;
+                       int compare_val = 0;
+                       guint8 *br [1];
 
-                               amd64_mov_reg_imm_size (code, AMD64_R11, (gpointer)&__nacl_thread_suspension_needed, 4);
-                               amd64_test_membase_imm_size (code, AMD64_R11, 0, 0xFFFFFFFF, 4);
-                               br[0] = code; x86_branch8 (code, X86_CC_EQ, 0, FALSE);
-                               code = emit_call (cfg, code, MONO_PATCH_INFO_ABS, (gpointer)mono_nacl_gc, TRUE);
-                               amd64_patch (br[0], code);
-                       }
+#if defined (USE_COOP_GC)
+                       polling_func = (gpointer)mono_threads_state_poll;
+                       compare_val = 1;
+#elif defined(__native_client_codegen__) && defined(__native_client_gc__)
+                       polling_func = (gpointer)mono_nacl_gc;
+                       compare_val = 0xFFFFFFFF;
 #endif
+                       if (!polling_func)
+                               break;
+
+                       amd64_test_membase_imm_size (code, ins->sreg1, 0, compare_val, 4);
+                       br[0] = code; x86_branch8 (code, X86_CC_EQ, 0, FALSE);
+                       code = emit_call (cfg, code, MONO_PATCH_INFO_ABS, polling_func, TRUE);
+                       amd64_patch (br[0], code);
                        break;
                }
+
                case OP_GC_LIVENESS_DEF:
                case OP_GC_LIVENESS_USE:
                case OP_GC_PARAM_SLOT_LIVENESS_DEF:
@@ -6745,7 +6784,7 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                mono_emit_unwind_op_def_cfa_offset (cfg, code, cfa_offset);
                mono_emit_unwind_op_offset (cfg, code, AMD64_RBP, - cfa_offset);
                async_exc_point (code);
-#ifdef HOST_WIN32
+#ifdef TARGET_WIN32
                mono_arch_unwindinfo_add_push_nonvol (&cfg->arch.unwindinfo, cfg->native_code, code, AMD64_RBP);
 #endif
                /* These are handled automatically by the stack marking code */
@@ -6754,7 +6793,7 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, sizeof(mgreg_t));
                mono_emit_unwind_op_def_cfa_reg (cfg, code, AMD64_RBP);
                async_exc_point (code);
-#ifdef HOST_WIN32
+#ifdef TARGET_WIN32
                mono_arch_unwindinfo_add_set_fpreg (&cfg->arch.unwindinfo, cfg->native_code, code, AMD64_RBP);
 #endif
        }
@@ -6795,7 +6834,7 @@ mono_arch_emit_prolog (MonoCompile *cfg)
        /* Allocate stack frame */
        if (alloc_size) {
                /* See mono_emit_stack_alloc */
-#if defined(HOST_WIN32) || defined(MONO_ARCH_SIGSEGV_ON_ALTSTACK)
+#if defined(TARGET_WIN32) || defined(MONO_ARCH_SIGSEGV_ON_ALTSTACK)
                guint32 remaining_size = alloc_size;
                /*FIXME handle unbounded code expansion, we should use a loop in case of more than X interactions*/
                guint32 required_code_size = ((remaining_size / 0x1000) + 1) * 10; /*10 is the max size of amd64_alu_reg_imm + amd64_test_membase_reg*/
@@ -6815,7 +6854,7 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                                mono_emit_unwind_op_def_cfa_offset (cfg, code, cfa_offset);
                        }
                        async_exc_point (code);
-#ifdef HOST_WIN32
+#ifdef TARGET_WIN32
                        if (cfg->arch.omit_fp) 
                                mono_arch_unwindinfo_add_alloc_stack (&cfg->arch.unwindinfo, cfg->native_code, code, 0x1000);
 #endif
@@ -6830,7 +6869,7 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                                mono_emit_unwind_op_def_cfa_offset (cfg, code, cfa_offset);
                                async_exc_point (code);
                        }
-#ifdef HOST_WIN32
+#ifdef TARGET_WIN32
                        if (cfg->arch.omit_fp) 
                                mono_arch_unwindinfo_add_alloc_stack (&cfg->arch.unwindinfo, cfg->native_code, code, remaining_size);
 #endif
@@ -7184,7 +7223,7 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                }
        }
 
-       if (cfg->gen_seq_points_debug_data) {
+       if (cfg->gen_sdb_seq_points) {
                MonoInst *info_var = cfg->arch.seq_point_info_var;
 
                /* Initialize seq_point_info_var */
@@ -7197,18 +7236,23 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                        amd64_mov_membase_reg (code, info_var->inst_basereg, info_var->inst_offset, AMD64_R11, 8);
                }
 
-               /* Initialize ss_trigger_page_var */
-               ins = cfg->arch.ss_trigger_page_var;
-
-               g_assert (ins->opcode == OP_REGOFFSET);
-
                if (cfg->compile_aot) {
+                       /* Initialize ss_tramp_var */
+                       ins = cfg->arch.ss_tramp_var;
+                       g_assert (ins->opcode == OP_REGOFFSET);
+
                        amd64_mov_reg_membase (code, AMD64_R11, info_var->inst_basereg, info_var->inst_offset, 8);
-                       amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, MONO_STRUCT_OFFSET (SeqPointInfo, ss_trigger_page), 8);
+                       amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, MONO_STRUCT_OFFSET (SeqPointInfo, ss_tramp_addr), 8);
+                       amd64_mov_membase_reg (code, ins->inst_basereg, ins->inst_offset, AMD64_R11, 8);
                } else {
+                       /* Initialize ss_trigger_page_var */
+                       ins = cfg->arch.ss_trigger_page_var;
+
+                       g_assert (ins->opcode == OP_REGOFFSET);
+
                        amd64_mov_reg_imm (code, AMD64_R11, (guint64)ss_trigger_page);
+                       amd64_mov_membase_reg (code, ins->inst_basereg, ins->inst_offset, AMD64_R11, 8);
                }
-               amd64_mov_membase_reg (code, ins->inst_basereg, ins->inst_offset, AMD64_R11, 8);
        }
 
        cfg->code_len = code - cfg->native_code;
@@ -7919,7 +7963,7 @@ get_delegate_invoke_impl (gboolean has_target, guint32 param_count, guint32 *cod
                        /* We have to shift the arguments left */
                        amd64_mov_reg_reg (code, AMD64_RAX, AMD64_ARG_REG1, 8);
                        for (i = 0; i < param_count; ++i) {
-#ifdef HOST_WIN32
+#ifdef TARGET_WIN32
                                if (i < 3)
                                        amd64_mov_reg_reg (code, param_regs [i], param_regs [i + 1], 8);
                                else
@@ -7935,6 +7979,7 @@ get_delegate_invoke_impl (gboolean has_target, guint32 param_count, guint32 *cod
        }
 
        nacl_global_codeman_validate (&start, 64, &code);
+       mono_arch_flush_icache (start, code - start);
 
        if (code_len)
                *code_len = code - start;
@@ -8482,7 +8527,7 @@ mono_arch_set_breakpoint (MonoJitInfo *ji, guint8 *ip)
                SeqPointInfo *info = mono_arch_get_seq_point_info (mono_domain_get (), ji->code_start);
 
                g_assert (info->bp_addrs [native_offset] == 0);
-               info->bp_addrs [native_offset] = bp_trigger_page;
+               info->bp_addrs [native_offset] = mini_get_breakpoint_trampoline ();
        } else {
                /* 
                 * In production, we will use int3 (has to fix the size in the md 
@@ -8516,8 +8561,7 @@ mono_arch_clear_breakpoint (MonoJitInfo *ji, guint8 *ip)
                guint32 native_offset = ip - (guint8*)ji->code_start;
                SeqPointInfo *info = mono_arch_get_seq_point_info (mono_domain_get (), ji->code_start);
 
-               g_assert (info->bp_addrs [native_offset] == 0);
-               info->bp_addrs [native_offset] = info;
+               info->bp_addrs [native_offset] = NULL;
        } else {
                for (i = 0; i < breakpoint_size; ++i)
                        x86_nop (code);
@@ -8553,8 +8597,7 @@ void
 mono_arch_skip_breakpoint (MonoContext *ctx, MonoJitInfo *ji)
 {
        if (ji->from_aot) {
-               /* amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, 0, 8) */
-               MONO_CONTEXT_SET_IP (ctx, (guint8*)MONO_CONTEXT_GET_IP (ctx) + 3);
+               /* The breakpoint instruction is a call */
        } else {
                MONO_CONTEXT_SET_IP (ctx, (guint8*)MONO_CONTEXT_GET_IP (ctx) + breakpoint_fault_size);
        }
@@ -8569,6 +8612,7 @@ void
 mono_arch_start_single_stepping (void)
 {
        mono_mprotect (ss_trigger_page, mono_pagesize (), 0);
+       ss_trampoline = mini_get_single_step_trampoline ();
 }
        
 /*
@@ -8580,6 +8624,7 @@ void
 mono_arch_stop_single_stepping (void)
 {
        mono_mprotect (ss_trigger_page, mono_pagesize (), MONO_MMAP_READ);
+       ss_trampoline = NULL;
 }
 
 /*
@@ -8630,7 +8675,6 @@ mono_arch_get_seq_point_info (MonoDomain *domain, guint8 *code)
 {
        SeqPointInfo *info;
        MonoJitInfo *ji;
-       int i;
 
        // FIXME: Add a free function
 
@@ -8646,11 +8690,7 @@ mono_arch_get_seq_point_info (MonoDomain *domain, guint8 *code)
                // FIXME: Optimize the size
                info = g_malloc0 (sizeof (SeqPointInfo) + (ji->code_size * sizeof (gpointer)));
 
-               info->ss_trigger_page = ss_trigger_page;
-               info->bp_trigger_page = bp_trigger_page;
-               /* Initialize to a valid address */
-               for (i = 0; i < ji->code_size; ++i)
-                       info->bp_addrs [i] = info;
+               info->ss_tramp_addr = &ss_trampoline;
 
                mono_domain_lock (domain);
                g_hash_table_insert (domain_jit_info (domain)->arch_seq_points,