2 * tramp-arm64-gsharedvt.c: gsharedvt support code for arm64
5 * Zoltan Varga <vargaz@gmail.com>
7 * Copyright 2013 Xamarin, Inc (http://www.xamarin.com)
8 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
10 #include <mono/metadata/abi-details.h>
13 #include "mini-arm64.h"
14 #include "mini-arm64-gsharedvt.h"
19 #ifdef MONO_ARCH_GSHARED_SUPPORTED
22 * mono_arch_get_gsharedvt_arg_trampoline:
24 * See tramp-x86.c for documentation.
27 mono_arch_get_gsharedvt_arg_trampoline (MonoDomain *domain, gpointer arg, gpointer addr)
33 * Return a trampoline which calls ADDR passing in ARG.
34 * Pass the argument in ip1, clobbering ip0.
36 buf = code = mono_global_codeman_reserve (buf_len);
38 code = mono_arm_emit_imm64 (code, ARMREG_IP1, (guint64)arg);
39 code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)addr);
41 arm_brx (code, ARMREG_IP0);
43 g_assert ((code - buf) < buf_len);
44 mono_arch_flush_icache (buf, code - buf);
50 mono_arm_start_gsharedvt_call (GSharedVtCallInfo *info, gpointer *caller, gpointer *callee, gpointer mrgctx_reg)
54 /* Set vtype ret arg */
55 if (info->vret_slot != -1) {
56 g_assert (info->vret_slot);
57 callee [info->vret_arg_reg] = &callee [info->vret_slot];
60 for (i = 0; i < info->map_count; ++i) {
61 int src = info->map [i * 2];
62 int dst = info->map [(i * 2) + 1];
63 int arg_marshal = (src >> 18) & 0xf;
64 int arg_size = (src >> 22) & 0xf;
66 if (G_UNLIKELY (arg_size)) {
67 int src_offset = (src >> 26) & 0xf;
68 int dst_offset = (dst >> 26) & 0xf;
69 int src_slot, dst_slot;
70 guint8 *src_ptr, *dst_ptr;
73 * Argument passed in part of a stack slot on ios.
74 * src_offset/dst_offset is the offset within the stack slot.
76 switch (arg_marshal) {
77 case GSHAREDVT_ARG_NONE:
78 src_slot = src & 0xffff;
79 dst_slot = dst & 0xffff;
80 src_ptr = (guint8*)(caller + src_slot) + src_offset;
81 dst_ptr = (guint8*)(callee + dst_slot) + dst_offset;
83 case GSHAREDVT_ARG_BYREF_TO_BYVAL:
84 src_slot = src & 0x3f;
85 dst_slot = dst & 0xffff;
86 src_ptr = caller [src_slot];
87 dst_ptr = (guint8*)(callee + dst_slot) + dst_offset;
89 case GSHAREDVT_ARG_BYVAL_TO_BYREF_HFAR4:
90 case GSHAREDVT_ARG_BYREF_TO_BYVAL_HFAR4:
91 case GSHAREDVT_ARG_BYREF_TO_BYREF:
92 g_assert_not_reached ();
100 case GSHAREDVT_ARG_SIZE_I1:
101 *(gint8*)dst_ptr = *(gint8*)src_ptr;
103 case GSHAREDVT_ARG_SIZE_U1:
104 *(guint8*)dst_ptr = *(guint8*)src_ptr;
106 case GSHAREDVT_ARG_SIZE_I2:
107 *(gint16*)dst_ptr = *(gint16*)src_ptr;
109 case GSHAREDVT_ARG_SIZE_U2:
110 *(guint16*)dst_ptr = *(guint16*)src_ptr;
112 case GSHAREDVT_ARG_SIZE_I4:
113 *(gint32*)dst_ptr = *(gint32*)src_ptr;
115 case GSHAREDVT_ARG_SIZE_U4:
116 *(guint32*)dst_ptr = *(guint32*)src_ptr;
119 g_assert_not_reached ();
124 switch (arg_marshal) {
125 case GSHAREDVT_ARG_NONE:
126 callee [dst] = caller [src];
128 case GSHAREDVT_ARG_BYVAL_TO_BYREF:
129 /* gsharedvt argument passed by addr in reg/stack slot */
131 callee [dst] = caller + src;
133 case GSHAREDVT_ARG_BYVAL_TO_BYREF_HFAR4: {
134 int nslots = (src >> 6) & 0xff;
135 int src_slot = src & 0x3f;
137 float *dst_arr = (float*)(caller + src_slot);
139 /* The r4 hfa is in separate slots, need to compress them together in place */
140 for (j = 0; j < nslots; ++j)
141 dst_arr [j] = *(float*)(caller + src_slot + j);
143 callee [dst] = caller + src_slot;
146 case GSHAREDVT_ARG_BYREF_TO_BYVAL: {
147 int nslots = (src >> 6) & 0xff;
148 int src_slot = src & 0x3f;
150 gpointer *addr = caller [src_slot];
152 for (j = 0; j < nslots; ++j)
153 callee [dst + j] = addr [j];
156 case GSHAREDVT_ARG_BYREF_TO_BYVAL_HFAR4: {
157 int nslots = (src >> 6) & 0xff;
158 int src_slot = src & 0x3f;
160 guint32 *addr = (guint32*)(caller [src_slot]);
162 /* addr points to an array of floats, need to load them to registers */
163 for (j = 0; j < nslots; ++j)
164 callee [dst + j] = GUINT_TO_POINTER (addr [j]);
167 case GSHAREDVT_ARG_BYREF_TO_BYREF: {
168 int src_slot = src & 0x3f;
170 callee [dst] = caller [src_slot];
174 g_assert_not_reached ();
179 if (info->vcall_offset != -1) {
180 MonoObject *this_obj = caller [0];
182 if (G_UNLIKELY (!this_obj))
184 if (info->vcall_offset == MONO_GSHAREDVT_DEL_INVOKE_VT_OFFSET)
185 /* delegate invoke */
186 return ((MonoDelegate*)this_obj)->invoke_impl;
188 return *(gpointer*)((char*)this_obj->vtable + info->vcall_offset);
189 } else if (info->calli) {
190 /* The address to call is passed in the mrgctx reg */
200 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
203 int buf_len, cfa_offset;
204 GSList *unwind_ops = NULL;
205 MonoJumpInfo *ji = NULL;
206 guint8 *br_out, *br [64], *br_ret [64], *bcc_ret [64];
207 int i, n_arg_regs, n_arg_fregs, offset, arg_reg, info_offset, rgctx_arg_reg_offset;
208 int caller_reg_area_offset, callee_reg_area_offset, callee_stack_area_offset;
209 int br_ret_index, bcc_ret_index;
212 buf = code = mono_global_codeman_reserve (buf_len);
215 * We are being called by an gsharedvt arg trampoline, the info argument is in IP1.
217 arg_reg = ARMREG_IP1;
218 n_arg_regs = NUM_GSHAREDVT_ARG_GREGS;
219 n_arg_fregs = NUM_GSHAREDVT_ARG_FREGS;
221 /* Compute stack frame size and offsets */
226 info_offset = offset;
229 rgctx_arg_reg_offset = offset;
234 caller_reg_area_offset = offset;
235 offset += (n_arg_regs + n_arg_fregs) * 8;
237 /* We need the argument regs to be saved at the top of the frame */
238 g_assert (offset % MONO_ARCH_FRAME_ALIGNMENT == 0);
243 arm_stpx_pre (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, -cfa_offset);
244 mono_add_unwind_op_def_cfa (unwind_ops, code, buf, ARMREG_SP, cfa_offset);
245 mono_add_unwind_op_offset (unwind_ops, code, buf, ARMREG_FP, -cfa_offset + 0);
246 mono_add_unwind_op_offset (unwind_ops, code, buf, ARMREG_LR, -cfa_offset + 8);
247 arm_movspx (code, ARMREG_FP, ARMREG_SP);
248 mono_add_unwind_op_def_cfa_reg (unwind_ops, code, buf, ARMREG_FP);
250 /* Save info argument */
251 arm_strx (code, arg_reg, ARMREG_FP, info_offset);
254 arm_strx (code, MONO_ARCH_RGCTX_REG, ARMREG_FP, rgctx_arg_reg_offset);
256 /* Save argument regs below the stack arguments */
257 for (i = 0; i < n_arg_regs; ++i)
258 arm_strx (code, i, ARMREG_SP, caller_reg_area_offset + (i * 8));
259 // FIXME: Only do this if fp regs are used
260 for (i = 0; i < n_arg_fregs; ++i)
261 arm_strfpx (code, i, ARMREG_SP, caller_reg_area_offset + ((n_arg_regs + i) * 8));
263 /* Allocate callee area */
264 arm_ldrw (code, ARMREG_IP0, arg_reg, MONO_STRUCT_OFFSET (GSharedVtCallInfo, stack_usage));
265 arm_movspx (code, ARMREG_LR, ARMREG_SP);
266 arm_subx (code, ARMREG_LR, ARMREG_LR, ARMREG_IP0);
267 arm_movspx (code, ARMREG_SP, ARMREG_LR);
268 /* Allocate callee register area just below the callee area so it can be accessed from start_gsharedvt_call using negative offsets */
269 /* The + 8 is for alignment */
270 callee_reg_area_offset = 8;
271 callee_stack_area_offset = callee_reg_area_offset + (n_arg_regs * sizeof (gpointer));
272 arm_subx_imm (code, ARMREG_SP, ARMREG_SP, ((n_arg_regs + n_arg_fregs) * sizeof (gpointer)) + 8);
275 * The stack now looks like this:
279 * <saved fp, lr> <- fp
280 * <callee area> <- sp
283 /* Call start_gsharedvt_call () */
285 arm_ldrx (code, ARMREG_R0, ARMREG_FP, info_offset);
286 /* arg2 = caller stack area */
287 arm_addx_imm (code, ARMREG_R1, ARMREG_FP, caller_reg_area_offset);
288 /* arg3 == callee stack area */
289 arm_addx_imm (code, ARMREG_R2, ARMREG_SP, callee_reg_area_offset);
290 /* arg4 = mrgctx reg */
291 arm_ldrx (code, ARMREG_R3, ARMREG_FP, rgctx_arg_reg_offset);
294 code = mono_arm_emit_aotconst (&ji, code, buf, ARMREG_IP0, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_arm_start_gsharedvt_call");
296 code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)mono_arm_start_gsharedvt_call);
297 arm_blrx (code, ARMREG_IP0);
299 /* Make the real method call */
300 /* R0 contains the addr to call */
301 arm_movx (code, ARMREG_IP1, ARMREG_R0);
303 arm_ldrx (code, MONO_ARCH_RGCTX_REG, ARMREG_FP, rgctx_arg_reg_offset);
304 /* Load argument registers */
306 for (i = 0; i < n_arg_regs; ++i)
307 arm_ldrx (code, i, ARMREG_SP, callee_reg_area_offset + (i * 8));
308 // FIXME: Only do this if needed
309 for (i = 0; i < n_arg_fregs; ++i)
310 arm_ldrfpx (code, i, ARMREG_SP, callee_reg_area_offset + ((n_arg_regs + i) * 8));
311 /* Clear callee reg area */
312 arm_addx_imm (code, ARMREG_SP, ARMREG_SP, ((n_arg_regs + n_arg_fregs) * sizeof (gpointer)) + 8);
314 arm_blrx (code, ARMREG_IP1);
319 // FIXME: Use a switch
320 /* Branch between IN/OUT cases */
321 arm_ldrx (code, ARMREG_IP1, ARMREG_FP, info_offset);
322 arm_ldrw (code, ARMREG_IP1, ARMREG_IP1, MONO_STRUCT_OFFSET (GSharedVtCallInfo, gsharedvt_in));
324 arm_cbzx (code, ARMREG_IP1, 0);
328 /* IP1 == return marshalling type */
329 arm_ldrx (code, ARMREG_IP1, ARMREG_FP, info_offset);
330 arm_ldrw (code, ARMREG_IP1, ARMREG_IP1, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal));
332 /* Continue if no marshalling required */
333 // FIXME: Use cmpx_imm
334 code = mono_arm_emit_imm64 (code, ARMREG_IP0, GSHAREDVT_RET_NONE);
335 arm_cmpx (code, ARMREG_IP0, ARMREG_IP1);
336 bcc_ret [bcc_ret_index ++] = code;
337 arm_bcc (code, ARMCOND_EQ, 0);
339 /* Compute vret area address in LR */
340 arm_ldrx (code, ARMREG_LR, ARMREG_FP, info_offset);
341 arm_ldrw (code, ARMREG_LR, ARMREG_LR, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_slot));
342 arm_subx_imm (code, ARMREG_LR, ARMREG_LR, n_arg_regs + n_arg_fregs);
343 arm_lslx (code, ARMREG_LR, ARMREG_LR, 3);
344 arm_movspx (code, ARMREG_IP0, ARMREG_SP);
345 arm_addx (code, ARMREG_LR, ARMREG_IP0, ARMREG_LR);
347 /* Branch to specific marshalling code */
348 for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
349 code = mono_arm_emit_imm64 (code, ARMREG_IP0, i);
350 arm_cmpx (code, ARMREG_IP0, ARMREG_IP1);
352 arm_bcc (code, ARMCOND_EQ, 0);
358 * The address of the return value area is in LR, have to load it into
361 for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
362 mono_arm_patch (br [i], code, MONO_R_ARM64_BCC);
364 case GSHAREDVT_RET_NONE:
366 case GSHAREDVT_RET_I8:
367 arm_ldrx (code, ARMREG_R0, ARMREG_LR, 0);
369 case GSHAREDVT_RET_I1:
370 arm_ldrsbx (code, ARMREG_R0, ARMREG_LR, 0);
372 case GSHAREDVT_RET_U1:
373 arm_ldrb (code, ARMREG_R0, ARMREG_LR, 0);
375 case GSHAREDVT_RET_I2:
376 arm_ldrshx (code, ARMREG_R0, ARMREG_LR, 0);
378 case GSHAREDVT_RET_U2:
379 arm_ldrh (code, ARMREG_R0, ARMREG_LR, 0);
381 case GSHAREDVT_RET_I4:
382 arm_ldrswx (code, ARMREG_R0, ARMREG_LR, 0);
384 case GSHAREDVT_RET_U4:
385 arm_ldrw (code, ARMREG_R0, ARMREG_LR, 0);
387 case GSHAREDVT_RET_R8:
388 arm_ldrfpx (code, ARMREG_D0, ARMREG_LR, 0);
390 case GSHAREDVT_RET_R4:
391 arm_ldrfpw (code, ARMREG_D0, ARMREG_LR, 0);
393 case GSHAREDVT_RET_IREGS_1:
394 case GSHAREDVT_RET_IREGS_2:
395 case GSHAREDVT_RET_IREGS_3:
396 case GSHAREDVT_RET_IREGS_4:
397 case GSHAREDVT_RET_IREGS_5:
398 case GSHAREDVT_RET_IREGS_6:
399 case GSHAREDVT_RET_IREGS_7:
400 case GSHAREDVT_RET_IREGS_8: {
403 for (j = 0; j < i - GSHAREDVT_RET_IREGS_1 + 1; ++j)
404 arm_ldrx (code, j, ARMREG_LR, j * 8);
407 case GSHAREDVT_RET_HFAR8_1:
408 case GSHAREDVT_RET_HFAR8_2:
409 case GSHAREDVT_RET_HFAR8_3:
410 case GSHAREDVT_RET_HFAR8_4: {
413 for (j = 0; j < i - GSHAREDVT_RET_HFAR8_1 + 1; ++j)
414 arm_ldrfpx (code, j, ARMREG_LR, j * 8);
417 case GSHAREDVT_RET_HFAR4_1:
418 case GSHAREDVT_RET_HFAR4_2:
419 case GSHAREDVT_RET_HFAR4_3:
420 case GSHAREDVT_RET_HFAR4_4: {
423 for (j = 0; j < i - GSHAREDVT_RET_HFAR4_1 + 1; ++j)
424 arm_ldrfpw (code, j, ARMREG_LR, j * 4);
428 g_assert_not_reached ();
431 br_ret [br_ret_index ++] = code;
436 mono_arm_patch (br_out, code, MONO_R_ARM64_CBZ);
438 /* Compute vret area address in LR */
439 arm_ldrx (code, ARMREG_LR, ARMREG_FP, caller_reg_area_offset + (ARMREG_R8 * 8));
441 /* IP1 == return marshalling type */
442 arm_ldrx (code, ARMREG_IP1, ARMREG_FP, info_offset);
443 arm_ldrw (code, ARMREG_IP1, ARMREG_IP1, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal));
445 /* Branch to specific marshalling code */
446 for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
447 code = mono_arm_emit_imm64 (code, ARMREG_IP0, i);
448 arm_cmpx (code, ARMREG_IP0, ARMREG_IP1);
450 arm_bcc (code, ARMCOND_EQ, 0);
454 * The return value is in registers, need to save to the return area passed by the caller in
457 for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
458 mono_arm_patch (br [i], code, MONO_R_ARM64_BCC);
460 case GSHAREDVT_RET_NONE:
462 case GSHAREDVT_RET_I8:
463 arm_strx (code, ARMREG_R0, ARMREG_LR, 0);
465 case GSHAREDVT_RET_I1:
466 case GSHAREDVT_RET_U1:
467 arm_strb (code, ARMREG_R0, ARMREG_LR, 0);
469 case GSHAREDVT_RET_I2:
470 case GSHAREDVT_RET_U2:
471 arm_strh (code, ARMREG_R0, ARMREG_LR, 0);
473 case GSHAREDVT_RET_I4:
474 case GSHAREDVT_RET_U4:
475 arm_strw (code, ARMREG_R0, ARMREG_LR, 0);
477 case GSHAREDVT_RET_R8:
478 arm_strfpx (code, ARMREG_D0, ARMREG_LR, 0);
480 case GSHAREDVT_RET_R4:
481 arm_strfpw (code, ARMREG_D0, ARMREG_LR, 0);
483 case GSHAREDVT_RET_IREGS_1:
484 case GSHAREDVT_RET_IREGS_2:
485 case GSHAREDVT_RET_IREGS_3:
486 case GSHAREDVT_RET_IREGS_4:
487 case GSHAREDVT_RET_IREGS_5:
488 case GSHAREDVT_RET_IREGS_6:
489 case GSHAREDVT_RET_IREGS_7:
490 case GSHAREDVT_RET_IREGS_8: {
493 for (j = 0; j < i - GSHAREDVT_RET_IREGS_1 + 1; ++j)
494 arm_strx (code, j, ARMREG_LR, j * 8);
497 case GSHAREDVT_RET_HFAR8_1:
498 case GSHAREDVT_RET_HFAR8_2:
499 case GSHAREDVT_RET_HFAR8_3:
500 case GSHAREDVT_RET_HFAR8_4: {
503 for (j = 0; j < i - GSHAREDVT_RET_HFAR8_1 + 1; ++j)
504 arm_strfpx (code, j, ARMREG_LR, j * 8);
507 case GSHAREDVT_RET_HFAR4_1:
508 case GSHAREDVT_RET_HFAR4_2:
509 case GSHAREDVT_RET_HFAR4_3:
510 case GSHAREDVT_RET_HFAR4_4: {
513 for (j = 0; j < i - GSHAREDVT_RET_HFAR4_1 + 1; ++j)
514 arm_strfpw (code, j, ARMREG_LR, j * 4);
521 br_ret [br_ret_index ++] = code;
527 for (i = 0; i < br_ret_index; ++i)
528 mono_arm_patch (br_ret [i], code, MONO_R_ARM64_B);
529 for (i = 0; i < bcc_ret_index; ++i)
530 mono_arm_patch (bcc_ret [i], code, MONO_R_ARM64_BCC);
533 arm_movspx (code, ARMREG_SP, ARMREG_FP);
534 arm_ldpx_post (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, offset);
535 arm_retx (code, ARMREG_LR);
537 g_assert ((code - buf) < buf_len);
540 *info = mono_tramp_info_create ("gsharedvt_trampoline", buf, code - buf, ji, unwind_ops);
542 mono_arch_flush_icache (buf, code - buf);
549 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
551 g_assert_not_reached ();
560 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
568 mono_arch_get_gsharedvt_arg_trampoline (MonoDomain *domain, gpointer arg, gpointer addr)
570 g_assert_not_reached ();
574 #endif /* MONO_ARCH_GSHARED_SUPPORTED */