gpointer mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot) { guint8 *start, *code; MonoJumpInfo *ji = NULL; GSList *unwind_ops = NULL; int i, ctx_reg, size; guint8 *labels [16]; size = 256; code = start = mono_global_codeman_reserve (size); arm_movx (code, ARMREG_IP0, ARMREG_R0); ctx_reg = ARMREG_IP0; /* Restore fregs */ arm_ldrx (code, ARMREG_IP1, ctx_reg, MONO_STRUCT_OFFSET (MonoContext, has_fregs)); labels [0] = code; arm_cbzx (code, ARMREG_IP1, 0); for (i = 0; i < 32; ++i) arm_ldrfpx (code, i, ctx_reg, MONO_STRUCT_OFFSET (MonoContext, fregs) + (i * 8)); mono_arm_patch (labels [0], code, MONO_R_ARM64_CBZ); /* Restore gregs */ // FIXME: Restore less registers // FIXME: fp should be restored later code = mono_arm_emit_load_regarray (code, 0xffffffff & ~(1 << ctx_reg) & ~(1 << ARMREG_SP), ctx_reg, MONO_STRUCT_OFFSET (MonoContext, regs)); /* ip0/ip1 doesn't need to be restored */ /* ip1 = pc */ arm_ldrx (code, ARMREG_IP1, ctx_reg, MONO_STRUCT_OFFSET (MonoContext, pc)); /* ip0 = sp */ arm_ldrx (code, ARMREG_IP0, ctx_reg, MONO_STRUCT_OFFSET (MonoContext, regs) + (ARMREG_SP * 8)); /* Restore sp, ctx is no longer valid */ arm_movspx (code, ARMREG_SP, ARMREG_IP0); /* Branch to pc */ arm_brx (code, ARMREG_IP1); /* Not reached */ arm_brk (code, 0); g_assert ((code - start) < size); mono_arch_flush_icache (start, code - start); mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL); if (info) *info = mono_tramp_info_create ("restore_context", start, code - start, ji, unwind_ops); return start; }
gpointer mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot) { guint8 *code; guint8* start; int i, size, offset, gregs_offset, fregs_offset, ctx_offset, num_fregs, frame_size; MonoJumpInfo *ji = NULL; GSList *unwind_ops = NULL; guint8 *labels [16]; size = 512; start = code = mono_global_codeman_reserve (size); /* Compute stack frame size and offsets */ offset = 0; /* frame block */ offset += 2 * 8; /* gregs */ gregs_offset = offset; offset += 32 * 8; /* fregs */ num_fregs = 8; fregs_offset = offset; offset += num_fregs * 8; ctx_offset = offset; ctx_offset += 8; frame_size = ALIGN_TO (offset, MONO_ARCH_FRAME_ALIGNMENT); /* * We are being called from C code, ctx is in r0, the address to call is in r1. * We need to save state, restore ctx, make the call, then restore the previous state, * returning the value returned by the call. */ /* Setup a frame */ arm_stpx_pre (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, -frame_size); arm_movspx (code, ARMREG_FP, ARMREG_SP); /* Save ctx */ arm_strx (code, ARMREG_R0, ARMREG_FP, ctx_offset); /* Save gregs */ code = mono_arm_emit_store_regarray (code, MONO_ARCH_CALLEE_SAVED_REGS | (1 << ARMREG_FP), ARMREG_FP, gregs_offset); /* Save fregs */ for (i = 0; i < num_fregs; ++i) arm_strfpx (code, ARMREG_D8 + i, ARMREG_FP, fregs_offset + (i * 8)); /* Load regs from ctx */ code = mono_arm_emit_load_regarray (code, MONO_ARCH_CALLEE_SAVED_REGS, ARMREG_R0, MONO_STRUCT_OFFSET (MonoContext, regs)); /* Load fregs */ arm_ldrx (code, ARMREG_IP0, ARMREG_R0, MONO_STRUCT_OFFSET (MonoContext, has_fregs)); labels [0] = code; arm_cbzx (code, ARMREG_IP0, 0); for (i = 0; i < num_fregs; ++i) arm_ldrfpx (code, ARMREG_D8 + i, ARMREG_R0, MONO_STRUCT_OFFSET (MonoContext, fregs) + (i * 8)); mono_arm_patch (labels [0], code, MONO_R_ARM64_CBZ); /* Load fp */ arm_ldrx (code, ARMREG_FP, ARMREG_R0, MONO_STRUCT_OFFSET (MonoContext, regs) + (ARMREG_FP * 8)); /* Make the call */ arm_blrx (code, ARMREG_R1); /* For filters, the result is in R0 */ /* Restore fp */ arm_ldrx (code, ARMREG_FP, ARMREG_SP, gregs_offset + (ARMREG_FP * 8)); /* Load ctx */ arm_ldrx (code, ARMREG_IP0, ARMREG_FP, ctx_offset); /* Save registers back to ctx */ /* This isn't strictly neccessary since we don't allocate variables used in eh clauses to registers */ code = mono_arm_emit_store_regarray (code, MONO_ARCH_CALLEE_SAVED_REGS, ARMREG_IP0, MONO_STRUCT_OFFSET (MonoContext, regs)); /* Restore regs */ code = mono_arm_emit_load_regarray (code, MONO_ARCH_CALLEE_SAVED_REGS, ARMREG_FP, gregs_offset); /* Restore fregs */ for (i = 0; i < num_fregs; ++i) arm_ldrfpx (code, ARMREG_D8 + i, ARMREG_FP, fregs_offset + (i * 8)); /* Destroy frame */ code = mono_arm_emit_destroy_frame (code, frame_size, (1 << ARMREG_IP0)); arm_retx (code, ARMREG_LR); g_assert ((code - start) < size); mono_arch_flush_icache (start, code - start); mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL); if (info) *info = mono_tramp_info_create ("call_filter", start, code - start, ji, unwind_ops); return start; }
gpointer mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot) { guint8 *code, *buf; int buf_len, cfa_offset; GSList *unwind_ops = NULL; MonoJumpInfo *ji = NULL; guint8 *br_out, *br [64], *br_ret [64], *bcc_ret [64]; int i, n_arg_regs, n_arg_fregs, offset, arg_reg, info_offset, rgctx_arg_reg_offset; int caller_reg_area_offset, callee_reg_area_offset, callee_stack_area_offset; int br_ret_index, bcc_ret_index; buf_len = 2048; buf = code = mono_global_codeman_reserve (buf_len); /* * We are being called by an gsharedvt arg trampoline, the info argument is in IP1. */ arg_reg = ARMREG_IP1; n_arg_regs = NUM_GSHAREDVT_ARG_GREGS; n_arg_fregs = NUM_GSHAREDVT_ARG_FREGS; /* Compute stack frame size and offsets */ offset = 0; /* frame block */ offset += 2 * 8; /* info argument */ info_offset = offset; offset += 8; /* saved rgctx */ rgctx_arg_reg_offset = offset; offset += 8; /* alignment */ offset += 8; /* argument regs */ caller_reg_area_offset = offset; offset += (n_arg_regs + n_arg_fregs) * 8; /* We need the argument regs to be saved at the top of the frame */ g_assert (offset % MONO_ARCH_FRAME_ALIGNMENT == 0); cfa_offset = offset; /* Setup frame */ arm_stpx_pre (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, -cfa_offset); mono_add_unwind_op_def_cfa (unwind_ops, code, buf, ARMREG_SP, cfa_offset); mono_add_unwind_op_offset (unwind_ops, code, buf, ARMREG_FP, -cfa_offset + 0); mono_add_unwind_op_offset (unwind_ops, code, buf, ARMREG_LR, -cfa_offset + 8); arm_movspx (code, ARMREG_FP, ARMREG_SP); mono_add_unwind_op_def_cfa_reg (unwind_ops, code, buf, ARMREG_FP); /* Save info argument */ arm_strx (code, arg_reg, ARMREG_FP, info_offset); /* Save rgxctx */ arm_strx (code, MONO_ARCH_RGCTX_REG, ARMREG_FP, rgctx_arg_reg_offset); /* Save argument regs below the stack arguments */ for (i = 0; i < n_arg_regs; ++i) arm_strx (code, i, ARMREG_SP, caller_reg_area_offset + (i * 8)); // FIXME: Only do this if fp regs are used for (i = 0; i < n_arg_fregs; ++i) arm_strfpx (code, i, ARMREG_SP, caller_reg_area_offset + ((n_arg_regs + i) * 8)); /* Allocate callee area */ arm_ldrw (code, ARMREG_IP0, arg_reg, MONO_STRUCT_OFFSET (GSharedVtCallInfo, stack_usage)); arm_movspx (code, ARMREG_LR, ARMREG_SP); arm_subx (code, ARMREG_LR, ARMREG_LR, ARMREG_IP0); arm_movspx (code, ARMREG_SP, ARMREG_LR); /* Allocate callee register area just below the callee area so it can be accessed from start_gsharedvt_call using negative offsets */ /* The + 8 is for alignment */ callee_reg_area_offset = 8; callee_stack_area_offset = callee_reg_area_offset + (n_arg_regs * sizeof (gpointer)); arm_subx_imm (code, ARMREG_SP, ARMREG_SP, ((n_arg_regs + n_arg_fregs) * sizeof (gpointer)) + 8); /* * The stack now looks like this: * <caller frame> * <saved r0-r8> * <our frame> * <saved fp, lr> <- fp * <callee area> <- sp */ /* Call start_gsharedvt_call () */ /* arg1 == info */ arm_ldrx (code, ARMREG_R0, ARMREG_FP, info_offset); /* arg2 = caller stack area */ arm_addx_imm (code, ARMREG_R1, ARMREG_FP, caller_reg_area_offset); /* arg3 == callee stack area */ arm_addx_imm (code, ARMREG_R2, ARMREG_SP, callee_reg_area_offset); /* arg4 = mrgctx reg */ arm_ldrx (code, ARMREG_R3, ARMREG_FP, rgctx_arg_reg_offset); if (aot) code = mono_arm_emit_aotconst (&ji, code, buf, ARMREG_IP0, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_arm_start_gsharedvt_call"); else code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)mono_arm_start_gsharedvt_call); arm_blrx (code, ARMREG_IP0); /* Make the real method call */ /* R0 contains the addr to call */ arm_movx (code, ARMREG_IP1, ARMREG_R0); /* Load rgxctx */ arm_ldrx (code, MONO_ARCH_RGCTX_REG, ARMREG_FP, rgctx_arg_reg_offset); /* Load argument registers */ // FIXME: for (i = 0; i < n_arg_regs; ++i) arm_ldrx (code, i, ARMREG_SP, callee_reg_area_offset + (i * 8)); // FIXME: Only do this if needed for (i = 0; i < n_arg_fregs; ++i) arm_ldrfpx (code, i, ARMREG_SP, callee_reg_area_offset + ((n_arg_regs + i) * 8)); /* Clear callee reg area */ arm_addx_imm (code, ARMREG_SP, ARMREG_SP, ((n_arg_regs + n_arg_fregs) * sizeof (gpointer)) + 8); /* Make the call */ arm_blrx (code, ARMREG_IP1); br_ret_index = 0; bcc_ret_index = 0; // FIXME: Use a switch /* Branch between IN/OUT cases */ arm_ldrx (code, ARMREG_IP1, ARMREG_FP, info_offset); arm_ldrw (code, ARMREG_IP1, ARMREG_IP1, MONO_STRUCT_OFFSET (GSharedVtCallInfo, gsharedvt_in)); br_out = code; arm_cbzx (code, ARMREG_IP1, 0); /* IN CASE */ /* IP1 == return marshalling type */ arm_ldrx (code, ARMREG_IP1, ARMREG_FP, info_offset); arm_ldrw (code, ARMREG_IP1, ARMREG_IP1, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal)); /* Continue if no marshalling required */ // FIXME: Use cmpx_imm code = mono_arm_emit_imm64 (code, ARMREG_IP0, GSHAREDVT_RET_NONE); arm_cmpx (code, ARMREG_IP0, ARMREG_IP1); bcc_ret [bcc_ret_index ++] = code; arm_bcc (code, ARMCOND_EQ, 0); /* Compute vret area address in LR */ arm_ldrx (code, ARMREG_LR, ARMREG_FP, info_offset); arm_ldrw (code, ARMREG_LR, ARMREG_LR, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_slot)); arm_subx_imm (code, ARMREG_LR, ARMREG_LR, n_arg_regs + n_arg_fregs); arm_lslx (code, ARMREG_LR, ARMREG_LR, 3); arm_movspx (code, ARMREG_IP0, ARMREG_SP); arm_addx (code, ARMREG_LR, ARMREG_IP0, ARMREG_LR); /* Branch to specific marshalling code */ for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) { code = mono_arm_emit_imm64 (code, ARMREG_IP0, i); arm_cmpx (code, ARMREG_IP0, ARMREG_IP1); br [i] = code; arm_bcc (code, ARMCOND_EQ, 0); } arm_brk (code, 0); /* * The address of the return value area is in LR, have to load it into * registers. */ for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) { mono_arm_patch (br [i], code, MONO_R_ARM64_BCC); switch (i) { case GSHAREDVT_RET_NONE: break; case GSHAREDVT_RET_I8: arm_ldrx (code, ARMREG_R0, ARMREG_LR, 0); break; case GSHAREDVT_RET_I1: arm_ldrsbx (code, ARMREG_R0, ARMREG_LR, 0); break; case GSHAREDVT_RET_U1: arm_ldrb (code, ARMREG_R0, ARMREG_LR, 0); break; case GSHAREDVT_RET_I2: arm_ldrshx (code, ARMREG_R0, ARMREG_LR, 0); break; case GSHAREDVT_RET_U2: arm_ldrh (code, ARMREG_R0, ARMREG_LR, 0); break; case GSHAREDVT_RET_I4: arm_ldrswx (code, ARMREG_R0, ARMREG_LR, 0); break; case GSHAREDVT_RET_U4: arm_ldrw (code, ARMREG_R0, ARMREG_LR, 0); break; case GSHAREDVT_RET_R8: arm_ldrfpx (code, ARMREG_D0, ARMREG_LR, 0); break; case GSHAREDVT_RET_R4: arm_ldrfpw (code, ARMREG_D0, ARMREG_LR, 0); break; case GSHAREDVT_RET_IREGS_1: case GSHAREDVT_RET_IREGS_2: case GSHAREDVT_RET_IREGS_3: case GSHAREDVT_RET_IREGS_4: case GSHAREDVT_RET_IREGS_5: case GSHAREDVT_RET_IREGS_6: case GSHAREDVT_RET_IREGS_7: case GSHAREDVT_RET_IREGS_8: { int j; for (j = 0; j < i - GSHAREDVT_RET_IREGS_1 + 1; ++j) arm_ldrx (code, j, ARMREG_LR, j * 8); break; } case GSHAREDVT_RET_HFAR8_1: case GSHAREDVT_RET_HFAR8_2: case GSHAREDVT_RET_HFAR8_3: case GSHAREDVT_RET_HFAR8_4: { int j; for (j = 0; j < i - GSHAREDVT_RET_HFAR8_1 + 1; ++j) arm_ldrfpx (code, j, ARMREG_LR, j * 8); break; } case GSHAREDVT_RET_HFAR4_1: case GSHAREDVT_RET_HFAR4_2: case GSHAREDVT_RET_HFAR4_3: case GSHAREDVT_RET_HFAR4_4: { int j; for (j = 0; j < i - GSHAREDVT_RET_HFAR4_1 + 1; ++j) arm_ldrfpw (code, j, ARMREG_LR, j * 4); break; } default: g_assert_not_reached (); break; } br_ret [br_ret_index ++] = code; arm_b (code, 0); } /* OUT CASE */ mono_arm_patch (br_out, code, MONO_R_ARM64_CBZ); /* Compute vret area address in LR */ arm_ldrx (code, ARMREG_LR, ARMREG_FP, caller_reg_area_offset + (ARMREG_R8 * 8)); /* IP1 == return marshalling type */ arm_ldrx (code, ARMREG_IP1, ARMREG_FP, info_offset); arm_ldrw (code, ARMREG_IP1, ARMREG_IP1, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal)); /* Branch to specific marshalling code */ for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) { code = mono_arm_emit_imm64 (code, ARMREG_IP0, i); arm_cmpx (code, ARMREG_IP0, ARMREG_IP1); br [i] = code; arm_bcc (code, ARMCOND_EQ, 0); } /* * The return value is in registers, need to save to the return area passed by the caller in * R8. */ for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) { mono_arm_patch (br [i], code, MONO_R_ARM64_BCC); switch (i) { case GSHAREDVT_RET_NONE: break; case GSHAREDVT_RET_I8: arm_strx (code, ARMREG_R0, ARMREG_LR, 0); break; case GSHAREDVT_RET_I1: case GSHAREDVT_RET_U1: arm_strb (code, ARMREG_R0, ARMREG_LR, 0); break; case GSHAREDVT_RET_I2: case GSHAREDVT_RET_U2: arm_strh (code, ARMREG_R0, ARMREG_LR, 0); break; case GSHAREDVT_RET_I4: case GSHAREDVT_RET_U4: arm_strw (code, ARMREG_R0, ARMREG_LR, 0); break; case GSHAREDVT_RET_R8: arm_strfpx (code, ARMREG_D0, ARMREG_LR, 0); break; case GSHAREDVT_RET_R4: arm_strfpw (code, ARMREG_D0, ARMREG_LR, 0); break; case GSHAREDVT_RET_IREGS_1: case GSHAREDVT_RET_IREGS_2: case GSHAREDVT_RET_IREGS_3: case GSHAREDVT_RET_IREGS_4: case GSHAREDVT_RET_IREGS_5: case GSHAREDVT_RET_IREGS_6: case GSHAREDVT_RET_IREGS_7: case GSHAREDVT_RET_IREGS_8: { int j; for (j = 0; j < i - GSHAREDVT_RET_IREGS_1 + 1; ++j) arm_strx (code, j, ARMREG_LR, j * 8); break; } case GSHAREDVT_RET_HFAR8_1: case GSHAREDVT_RET_HFAR8_2: case GSHAREDVT_RET_HFAR8_3: case GSHAREDVT_RET_HFAR8_4: { int j; for (j = 0; j < i - GSHAREDVT_RET_HFAR8_1 + 1; ++j) arm_strfpx (code, j, ARMREG_LR, j * 8); break; } case GSHAREDVT_RET_HFAR4_1: case GSHAREDVT_RET_HFAR4_2: case GSHAREDVT_RET_HFAR4_3: case GSHAREDVT_RET_HFAR4_4: { int j; for (j = 0; j < i - GSHAREDVT_RET_HFAR4_1 + 1; ++j) arm_strfpw (code, j, ARMREG_LR, j * 4); break; } default: arm_brk (code, i); break; } br_ret [br_ret_index ++] = code; arm_b (code, 0); } arm_brk (code, 0); for (i = 0; i < br_ret_index; ++i) mono_arm_patch (br_ret [i], code, MONO_R_ARM64_B); for (i = 0; i < bcc_ret_index; ++i) mono_arm_patch (bcc_ret [i], code, MONO_R_ARM64_BCC); /* Normal return */ arm_movspx (code, ARMREG_SP, ARMREG_FP); arm_ldpx_post (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, offset); arm_retx (code, ARMREG_LR); g_assert ((code - buf) < buf_len); if (info) *info = mono_tramp_info_create ("gsharedvt_trampoline", buf, code - buf, ji, unwind_ops); mono_arch_flush_icache (buf, code - buf); return buf; }