X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Ftramp-arm64.c;h=45e18d880aad9724d70a4bffa49cb106393ceebb;hb=ea4e4a9ef6fc42570a23026adbe826cf7248290e;hp=5088fea22978419b52d1251e9be4633be87e6c81;hpb=1c385f99e1c5e5c76e03c49e838ac29739a2e9e2;p=mono.git diff --git a/mono/mini/tramp-arm64.c b/mono/mini/tramp-arm64.c index 5088fea2297..45e18d880aa 100644 --- a/mono/mini/tramp-arm64.c +++ b/mono/mini/tramp-arm64.c @@ -1,5 +1,6 @@ -/* - * tramp-arm64.c: JIT trampoline code for ARM64 +/** + * \file + * JIT trampoline code for ARM64 * * Copyright 2013 Xamarin Inc * @@ -20,6 +21,11 @@ #include #include +#ifdef ENABLE_INTERPRETER +#include "interp/interp.h" +#endif + + #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1)) void @@ -73,8 +79,7 @@ mono_arch_get_call_target (guint8 *code) code -= 4; imm = *(guint32*)code; - /* Should be a bl */ - g_assert (((imm >> 31) & 0x1) == 0x1); + /* Should be a b/bl */ g_assert (((imm >> 26) & 0x7) == 0x5); disp = (imm & 0x3ffffff); @@ -92,14 +97,21 @@ mono_arch_get_plt_info_offset (guint8 *plt_entry, mgreg_t *regs, guint8 *code) return ((guint32*)plt_entry) [4]; } +gpointer +mono_arm_handler_block_trampoline_helper (gpointer *ptr) +{ + MonoJitTlsData *jit_tls = mono_tls_get_jit_tls (); + return jit_tls->handler_block_return_address; +} + #ifndef DISABLE_JIT guchar* mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot) { - guint8 *code, *buf, *tramp; + guint8 *code, *buf, *tramp, *labels [16]; int i, buf_len, imm; - int frame_size, offset, gregs_offset, num_fregs, fregs_offset, arg_offset, lmf_offset; + int frame_size, offset, gregs_offset, num_fregs, fregs_offset, arg_offset, lmf_offset, res_offset; guint64 gregs_regset; GSList *unwind_ops = NULL; MonoJumpInfo *ji = NULL; @@ -128,6 +140,9 @@ mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInf /* arg */ arg_offset = offset; offset += 8; + /* result */ + res_offset = offset; + offset += 8; /* LMF */ lmf_offset = offset; offset += sizeof (MonoLMF); @@ -222,6 +237,9 @@ mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInf } arm_blrx (code, ARMREG_IP0); + /* Save the result */ + arm_strx (code, ARMREG_R0, ARMREG_FP, res_offset); + /* Restore LMF */ /* Similar to emit_restore_lmf () */ /* Clobbers ip0/ip1 */ @@ -234,8 +252,19 @@ mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInf /* *lmf_addr = previous_lmf */ arm_strx (code, ARMREG_IP1, ARMREG_IP0, 0); - /* Save the result to ip1 */ - arm_movx (code, ARMREG_IP1, ARMREG_R0); + /* Check for thread interruption */ + /* This is not perf critical code so no need to check the interrupt flag */ + if (aot) { + code = mono_arm_emit_aotconst (&ji, code, buf, ARMREG_IP0, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_thread_force_interruption_checkpoint_noraise"); + } else { + code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)mono_thread_force_interruption_checkpoint_noraise); + } + arm_blrx (code, ARMREG_IP0); + /* Check whenever there is an exception to be thrown */ + labels [0] = code; + arm_cbnzx (code, ARMREG_R0, 0); + + /* Normal case */ /* Restore gregs */ /* Only have to load the argument regs (r0..r8) and the rgctx reg */ @@ -244,6 +273,9 @@ mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInf for (i = 0; i < num_fregs; ++i) arm_ldrfpx (code, i, ARMREG_FP, fregs_offset + (i * 8)); + /* Load the result */ + arm_ldrx (code, ARMREG_IP1, ARMREG_FP, res_offset); + /* These trampolines return a value */ if (tramp_type == MONO_TRAMPOLINE_RGCTX_LAZY_FETCH) arm_movx (code, ARMREG_R0, ARMREG_IP1); @@ -256,6 +288,29 @@ mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInf else arm_brx (code, ARMREG_IP1); + /* Exception case */ + mono_arm_patch (labels [0], code, MONO_R_ARM64_CBZ); + + /* + * We have an exception we want to throw in the caller's frame, so pop + * the trampoline frame and throw from the caller. + */ + code = mono_arm_emit_destroy_frame (code, frame_size, ((1 << ARMREG_IP0))); + /* We are in the parent frame, the exception is in x0 */ + /* + * EH is initialized after trampolines, so get the address of the variable + * which contains throw_exception, and load it from there. + */ + if (aot) { + /* Not really a jit icall */ + code = mono_arm_emit_aotconst (&ji, code, buf, ARMREG_IP0, MONO_PATCH_INFO_JIT_ICALL_ADDR, "throw_exception_addr"); + } else { + code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)mono_get_throw_exception_addr ()); + } + arm_ldrx (code, ARMREG_IP0, ARMREG_IP0, 0); + /* lr contains the return address, the trampoline will use it as the throw site */ + arm_brx (code, ARMREG_IP0); + g_assert ((code - buf) < buf_len); mono_arch_flush_icache (buf, code - buf); @@ -313,14 +368,14 @@ mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr) } gpointer -mono_arch_get_static_rgctx_trampoline (MonoMethod *m, MonoMethodRuntimeGenericContext *mrgctx, gpointer addr) +mono_arch_get_static_rgctx_trampoline (gpointer arg, gpointer addr) { guint8 *code, *start; guint32 buf_len = 32; MonoDomain *domain = mono_domain_get (); start = code = mono_domain_code_reserve (domain, buf_len); - code = mono_arm_emit_imm64 (code, MONO_ARCH_RGCTX_REG, (guint64)mrgctx); + code = mono_arm_emit_imm64 (code, MONO_ARCH_RGCTX_REG, (guint64)arg); code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)addr); arm_brx (code, ARMREG_IP0); @@ -464,6 +519,52 @@ mono_arch_create_general_rgctx_lazy_fetch_trampoline (MonoTrampInfo **info, gboo return buf; } +gpointer +mono_arch_create_handler_block_trampoline (MonoTrampInfo **info, gboolean aot) +{ + guint8 *tramp; + guint8 *code, *buf; + int tramp_size = 64; + MonoJumpInfo *ji = NULL; + GSList *unwind_ops = NULL; + + code = buf = mono_global_codeman_reserve (tramp_size); + + unwind_ops = NULL; + + /* + This trampoline restore the call chain of the handler block then jumps into the code that deals with it. + */ + + /* + * We are in a method frame after the call emitted by OP_CALL_HANDLER. + */ + if (aot) + code = mono_arm_emit_aotconst (&ji, code, buf, ARMREG_IP0, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_arm_handler_block_trampoline_helper"); + else + code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)mono_arm_handler_block_trampoline_helper); + /* Set it as the return address so the trampoline will return to it */ + arm_movx (code, ARMREG_LR, ARMREG_IP0); + + /* Call the trampoline */ + if (aot) { + char *name = g_strdup_printf ("trampoline_func_%d", MONO_TRAMPOLINE_HANDLER_BLOCK_GUARD); + code = mono_arm_emit_aotconst (&ji, code, buf, ARMREG_IP0, MONO_PATCH_INFO_JIT_ICALL_ADDR, name); + } else { + tramp = mono_arch_create_specific_trampoline (NULL, MONO_TRAMPOLINE_HANDLER_BLOCK_GUARD, NULL, NULL); + code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)tramp); + } + arm_brx (code, ARMREG_IP0); + + mono_arch_flush_icache (buf, code - buf); + MONO_PROFILER_RAISE (jit_code_buffer, (buf, code - buf, MONO_PROFILER_CODE_BUFFER_HELPER, NULL)); + g_assert (code - buf <= tramp_size); + + *info = mono_tramp_info_create ("handler_block_trampoline", buf, code - buf, ji, unwind_ops); + + return buf; +} + /* * mono_arch_create_sdb_trampoline: * @@ -562,6 +663,161 @@ mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gbo return buf; } +/* + * mono_arch_get_enter_icall_trampoline: + * + * See tramp-amd64.c for documentation. + */ +gpointer +mono_arch_get_enter_icall_trampoline (MonoTrampInfo **info) +{ +#ifdef ENABLE_INTERPRETER + const int gregs_num = INTERP_ICALL_TRAMP_IARGS; + const int fregs_num = INTERP_ICALL_TRAMP_FARGS; + + guint8 *start = NULL, *code, *label_gexits [gregs_num], *label_fexits [fregs_num], *label_leave_tramp [3], *label_is_float_ret; + MonoJumpInfo *ji = NULL; + GSList *unwind_ops = NULL; + int buf_len, i, framesize = 0, off_methodargs, off_targetaddr; + + buf_len = 512 + 1024; + start = code = (guint8 *) mono_global_codeman_reserve (buf_len); + + /* save FP and LR */ + framesize += 2 * sizeof (mgreg_t); + + off_methodargs = framesize; + framesize += sizeof (mgreg_t); + + off_targetaddr = framesize; + framesize += sizeof (mgreg_t); + + framesize = ALIGN_TO (framesize, MONO_ARCH_FRAME_ALIGNMENT); + + /* allocate space on stack for argument passing */ + const int stack_space = ALIGN_TO (((gregs_num - ARMREG_R7) * sizeof (mgreg_t)), MONO_ARCH_FRAME_ALIGNMENT); + + arm_subx_imm (code, ARMREG_SP, ARMREG_SP, stack_space + framesize); + arm_stpx (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, stack_space); + arm_addx_imm (code, ARMREG_FP, ARMREG_SP, stack_space); + + /* save InterpMethodArguments* onto stack */ + arm_strx (code, ARMREG_R1, ARMREG_FP, off_methodargs); + + /* save target address onto stack */ + arm_strx (code, ARMREG_R0, ARMREG_FP, off_targetaddr); + + /* load pointer to InterpMethodArguments* into IP0 */ + arm_movx (code, ARMREG_IP0, ARMREG_R1); + + /* move flen into R9 */ + arm_ldrx (code, ARMREG_R9, ARMREG_IP0, MONO_STRUCT_OFFSET (InterpMethodArguments, flen)); + /* load pointer to fargs into R10 */ + arm_ldrx (code, ARMREG_R10, ARMREG_IP0, MONO_STRUCT_OFFSET (InterpMethodArguments, fargs)); + + for (i = 0; i < fregs_num; ++i) { + arm_cmpx_imm (code, ARMREG_R9, 0); + label_fexits [i] = code; + arm_bcc (code, ARMCOND_EQ, 0); + + g_assert (i <= ARMREG_D7); /* otherwise, need to pass args on stack */ + arm_ldrfpx (code, i, ARMREG_R10, i * sizeof (double)); + arm_subx_imm (code, ARMREG_R9, ARMREG_R9, 1); + } + + for (i = 0; i < fregs_num; i++) + mono_arm_patch (label_fexits [i], code, MONO_R_ARM64_BCC); + + /* move ilen into R9 */ + arm_ldrx (code, ARMREG_R9, ARMREG_IP0, MONO_STRUCT_OFFSET (InterpMethodArguments, ilen)); + /* load pointer to iargs into R10 */ + arm_ldrx (code, ARMREG_R10, ARMREG_IP0, MONO_STRUCT_OFFSET (InterpMethodArguments, iargs)); + + int stack_offset = 0; + for (i = 0; i < gregs_num; i++) { + arm_cmpx_imm (code, ARMREG_R9, 0); + label_gexits [i] = code; + arm_bcc (code, ARMCOND_EQ, 0); + + if (i <= ARMREG_R7) { + arm_ldrx (code, i, ARMREG_R10, i * sizeof (mgreg_t)); + } else { + arm_ldrx (code, ARMREG_R11, ARMREG_R10, i * sizeof (mgreg_t)); + arm_strx (code, ARMREG_R11, ARMREG_SP, stack_offset); + stack_offset += sizeof (mgreg_t); + } + arm_subx_imm (code, ARMREG_R9, ARMREG_R9, 1); + } + + for (i = 0; i < gregs_num; i++) + mono_arm_patch (label_gexits [i], code, MONO_R_ARM64_BCC); + + /* load target addr */ + arm_ldrx (code, ARMREG_R11, ARMREG_FP, off_targetaddr); + + /* call into native function */ + arm_blrx (code, ARMREG_R11); + + /* load InterpMethodArguments */ + arm_ldrx (code, ARMREG_IP0, ARMREG_FP, off_methodargs); + + /* load is_float_ret */ + arm_ldrx (code, ARMREG_R11, ARMREG_IP0, MONO_STRUCT_OFFSET (InterpMethodArguments, is_float_ret)); + + /* check if a float return value is expected */ + arm_cmpx_imm (code, ARMREG_R11, 0); + label_is_float_ret = code; + arm_bcc (code, ARMCOND_NE, 0); + + /* greg return */ + /* load retval */ + arm_ldrx (code, ARMREG_R11, ARMREG_IP0, MONO_STRUCT_OFFSET (InterpMethodArguments, retval)); + + arm_cmpx_imm (code, ARMREG_R11, 0); + label_leave_tramp [0] = code; + arm_bcc (code, ARMCOND_EQ, 0); + + /* store greg result */ + arm_strx (code, ARMREG_R0, ARMREG_R11, 0); + + label_leave_tramp [1] = code; + arm_bcc (code, ARMCOND_AL, 0); + + /* freg return */ + mono_arm_patch (label_is_float_ret, code, MONO_R_ARM64_BCC); + /* load retval */ + arm_ldrx (code, ARMREG_R11, ARMREG_IP0, MONO_STRUCT_OFFSET (InterpMethodArguments, retval)); + + arm_cmpx_imm (code, ARMREG_R11, 0); + label_leave_tramp [2] = code; + arm_bcc (code, ARMCOND_EQ, 0); + + /* store freg result */ + arm_strfpx (code, ARMREG_D0, ARMREG_R11, 0); + + for (i = 0; i < 3; i++) + mono_arm_patch (label_leave_tramp [i], code, MONO_R_ARM64_BCC); + + arm_movspx (code, ARMREG_SP, ARMREG_FP); + arm_ldpx (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, 0); + arm_addx_imm (code, ARMREG_SP, ARMREG_SP, framesize); + arm_retx (code, ARMREG_LR); + + g_assert (code - start < buf_len); + + mono_arch_flush_icache (start, code - start); + MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL)); + + if (info) + *info = mono_tramp_info_create ("enter_icall_trampoline", start, code - start, ji, unwind_ops); + + return start; +#else + g_assert_not_reached (); + return NULL; +#endif /* ENABLE_INTERPRETER */ +} + #else /* DISABLE_JIT */ guchar* @@ -578,6 +834,13 @@ mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_ty return NULL; } +gpointer +mono_arch_create_handler_block_trampoline (MonoTrampInfo **info, gboolean aot) +{ + g_assert_not_reached (); + return NULL; +} + gpointer mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr) { @@ -586,7 +849,7 @@ mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr) } gpointer -mono_arch_get_static_rgctx_trampoline (MonoMethod *m, MonoMethodRuntimeGenericContext *mrgctx, gpointer addr) +mono_arch_get_static_rgctx_trampoline (gpointer arg, gpointer addr) { g_assert_not_reached (); return NULL; @@ -613,4 +876,10 @@ mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gbo return NULL; } +gpointer +mono_arch_get_enter_icall_trampoline (MonoTrampInfo **info) +{ + g_assert_not_reached (); + return NULL; +} #endif /* !DISABLE_JIT */