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_GSHAREDVT_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 ();
94 case GSHAREDVT_ARG_BYVAL_TO_BYREF
:
95 src_slot
= src
& 0x3f;
96 src_ptr
= caller
+ src_slot
+ src_offset
;
97 callee
[dst
] = src_ptr
;
104 if (arg_marshal
== GSHAREDVT_ARG_BYVAL_TO_BYREF
)
108 case GSHAREDVT_ARG_SIZE_I1
:
109 *(gint8
*)dst_ptr
= *(gint8
*)src_ptr
;
111 case GSHAREDVT_ARG_SIZE_U1
:
112 *(guint8
*)dst_ptr
= *(guint8
*)src_ptr
;
114 case GSHAREDVT_ARG_SIZE_I2
:
115 *(gint16
*)dst_ptr
= *(gint16
*)src_ptr
;
117 case GSHAREDVT_ARG_SIZE_U2
:
118 *(guint16
*)dst_ptr
= *(guint16
*)src_ptr
;
120 case GSHAREDVT_ARG_SIZE_I4
:
121 *(gint32
*)dst_ptr
= *(gint32
*)src_ptr
;
123 case GSHAREDVT_ARG_SIZE_U4
:
124 *(guint32
*)dst_ptr
= *(guint32
*)src_ptr
;
127 g_assert_not_reached ();
132 switch (arg_marshal
) {
133 case GSHAREDVT_ARG_NONE
:
134 callee
[dst
] = caller
[src
];
136 case GSHAREDVT_ARG_BYVAL_TO_BYREF
:
137 /* gsharedvt argument passed by addr in reg/stack slot */
139 callee
[dst
] = caller
+ src
;
141 case GSHAREDVT_ARG_BYVAL_TO_BYREF_HFAR4
: {
142 int nslots
= (src
>> 6) & 0xff;
143 int src_slot
= src
& 0x3f;
145 float *dst_arr
= (float*)(caller
+ src_slot
);
147 /* The r4 hfa is in separate slots, need to compress them together in place */
148 for (j
= 0; j
< nslots
; ++j
)
149 dst_arr
[j
] = *(float*)(caller
+ src_slot
+ j
);
151 callee
[dst
] = caller
+ src_slot
;
154 case GSHAREDVT_ARG_BYREF_TO_BYVAL
: {
155 int nslots
= (src
>> 6) & 0xff;
156 int src_slot
= src
& 0x3f;
158 gpointer
*addr
= caller
[src_slot
];
160 for (j
= 0; j
< nslots
; ++j
)
161 callee
[dst
+ j
] = addr
[j
];
164 case GSHAREDVT_ARG_BYREF_TO_BYVAL_HFAR4
: {
165 int nslots
= (src
>> 6) & 0xff;
166 int src_slot
= src
& 0x3f;
168 guint32
*addr
= (guint32
*)(caller
[src_slot
]);
170 /* addr points to an array of floats, need to load them to registers */
171 for (j
= 0; j
< nslots
; ++j
)
172 callee
[dst
+ j
] = GUINT_TO_POINTER (addr
[j
]);
175 case GSHAREDVT_ARG_BYREF_TO_BYREF
: {
176 int src_slot
= src
& 0x3f;
178 callee
[dst
] = caller
[src_slot
];
182 g_assert_not_reached ();
187 if (info
->vcall_offset
!= -1) {
188 MonoObject
*this_obj
= caller
[0];
190 if (G_UNLIKELY (!this_obj
))
192 if (info
->vcall_offset
== MONO_GSHAREDVT_DEL_INVOKE_VT_OFFSET
)
193 /* delegate invoke */
194 return ((MonoDelegate
*)this_obj
)->invoke_impl
;
196 return *(gpointer
*)((char*)this_obj
->vtable
+ info
->vcall_offset
);
197 } else if (info
->calli
) {
198 /* The address to call is passed in the mrgctx reg */
208 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo
**info
, gboolean aot
)
211 int buf_len
, cfa_offset
;
212 GSList
*unwind_ops
= NULL
;
213 MonoJumpInfo
*ji
= NULL
;
214 guint8
*br_out
, *br
[64], *br_ret
[64], *bcc_ret
[64];
215 int i
, n_arg_regs
, n_arg_fregs
, offset
, arg_reg
, info_offset
, rgctx_arg_reg_offset
;
216 int caller_reg_area_offset
, callee_reg_area_offset
, callee_stack_area_offset
;
217 int br_ret_index
, bcc_ret_index
;
220 buf
= code
= mono_global_codeman_reserve (buf_len
);
223 * We are being called by an gsharedvt arg trampoline, the info argument is in IP1.
225 arg_reg
= ARMREG_IP1
;
226 n_arg_regs
= NUM_GSHAREDVT_ARG_GREGS
;
227 n_arg_fregs
= NUM_GSHAREDVT_ARG_FREGS
;
229 /* Compute stack frame size and offsets */
234 info_offset
= offset
;
237 rgctx_arg_reg_offset
= offset
;
242 caller_reg_area_offset
= offset
;
243 offset
+= (n_arg_regs
+ n_arg_fregs
) * 8;
245 /* We need the argument regs to be saved at the top of the frame */
246 g_assert (offset
% MONO_ARCH_FRAME_ALIGNMENT
== 0);
251 arm_stpx_pre (code
, ARMREG_FP
, ARMREG_LR
, ARMREG_SP
, -cfa_offset
);
252 mono_add_unwind_op_def_cfa (unwind_ops
, code
, buf
, ARMREG_SP
, cfa_offset
);
253 mono_add_unwind_op_offset (unwind_ops
, code
, buf
, ARMREG_FP
, -cfa_offset
+ 0);
254 mono_add_unwind_op_offset (unwind_ops
, code
, buf
, ARMREG_LR
, -cfa_offset
+ 8);
255 arm_movspx (code
, ARMREG_FP
, ARMREG_SP
);
256 mono_add_unwind_op_def_cfa_reg (unwind_ops
, code
, buf
, ARMREG_FP
);
258 /* Save info argument */
259 arm_strx (code
, arg_reg
, ARMREG_FP
, info_offset
);
262 arm_strx (code
, MONO_ARCH_RGCTX_REG
, ARMREG_FP
, rgctx_arg_reg_offset
);
264 /* Save argument regs below the stack arguments */
265 for (i
= 0; i
< n_arg_regs
; ++i
)
266 arm_strx (code
, i
, ARMREG_SP
, caller_reg_area_offset
+ (i
* 8));
267 // FIXME: Only do this if fp regs are used
268 for (i
= 0; i
< n_arg_fregs
; ++i
)
269 arm_strfpx (code
, i
, ARMREG_SP
, caller_reg_area_offset
+ ((n_arg_regs
+ i
) * 8));
271 /* Allocate callee area */
272 arm_ldrw (code
, ARMREG_IP0
, arg_reg
, MONO_STRUCT_OFFSET (GSharedVtCallInfo
, stack_usage
));
273 arm_movspx (code
, ARMREG_LR
, ARMREG_SP
);
274 arm_subx (code
, ARMREG_LR
, ARMREG_LR
, ARMREG_IP0
);
275 arm_movspx (code
, ARMREG_SP
, ARMREG_LR
);
276 /* Allocate callee register area just below the callee area so it can be accessed from start_gsharedvt_call using negative offsets */
277 /* The + 8 is for alignment */
278 callee_reg_area_offset
= 8;
279 callee_stack_area_offset
= callee_reg_area_offset
+ (n_arg_regs
* sizeof (gpointer
));
280 arm_subx_imm (code
, ARMREG_SP
, ARMREG_SP
, ((n_arg_regs
+ n_arg_fregs
) * sizeof (gpointer
)) + 8);
283 * The stack now looks like this:
287 * <saved fp, lr> <- fp
288 * <callee area> <- sp
291 /* Call start_gsharedvt_call () */
293 arm_ldrx (code
, ARMREG_R0
, ARMREG_FP
, info_offset
);
294 /* arg2 = caller stack area */
295 arm_addx_imm (code
, ARMREG_R1
, ARMREG_FP
, caller_reg_area_offset
);
296 /* arg3 == callee stack area */
297 arm_addx_imm (code
, ARMREG_R2
, ARMREG_SP
, callee_reg_area_offset
);
298 /* arg4 = mrgctx reg */
299 arm_ldrx (code
, ARMREG_R3
, ARMREG_FP
, rgctx_arg_reg_offset
);
302 code
= mono_arm_emit_aotconst (&ji
, code
, buf
, ARMREG_IP0
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, "mono_arm_start_gsharedvt_call");
304 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, (guint64
)mono_arm_start_gsharedvt_call
);
305 arm_blrx (code
, ARMREG_IP0
);
307 /* Make the real method call */
308 /* R0 contains the addr to call */
309 arm_movx (code
, ARMREG_IP1
, ARMREG_R0
);
311 arm_ldrx (code
, MONO_ARCH_RGCTX_REG
, ARMREG_FP
, rgctx_arg_reg_offset
);
312 /* Load argument registers */
314 for (i
= 0; i
< n_arg_regs
; ++i
)
315 arm_ldrx (code
, i
, ARMREG_SP
, callee_reg_area_offset
+ (i
* 8));
316 // FIXME: Only do this if needed
317 for (i
= 0; i
< n_arg_fregs
; ++i
)
318 arm_ldrfpx (code
, i
, ARMREG_SP
, callee_reg_area_offset
+ ((n_arg_regs
+ i
) * 8));
319 /* Clear callee reg area */
320 arm_addx_imm (code
, ARMREG_SP
, ARMREG_SP
, ((n_arg_regs
+ n_arg_fregs
) * sizeof (gpointer
)) + 8);
322 arm_blrx (code
, ARMREG_IP1
);
327 // FIXME: Use a switch
328 /* Branch between IN/OUT cases */
329 arm_ldrx (code
, ARMREG_IP1
, ARMREG_FP
, info_offset
);
330 arm_ldrw (code
, ARMREG_IP1
, ARMREG_IP1
, MONO_STRUCT_OFFSET (GSharedVtCallInfo
, gsharedvt_in
));
332 arm_cbzx (code
, ARMREG_IP1
, 0);
336 /* IP1 == return marshalling type */
337 arm_ldrx (code
, ARMREG_IP1
, ARMREG_FP
, info_offset
);
338 arm_ldrw (code
, ARMREG_IP1
, ARMREG_IP1
, MONO_STRUCT_OFFSET (GSharedVtCallInfo
, ret_marshal
));
340 /* Continue if no marshalling required */
341 // FIXME: Use cmpx_imm
342 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, GSHAREDVT_RET_NONE
);
343 arm_cmpx (code
, ARMREG_IP0
, ARMREG_IP1
);
344 bcc_ret
[bcc_ret_index
++] = code
;
345 arm_bcc (code
, ARMCOND_EQ
, 0);
347 /* Compute vret area address in LR */
348 arm_ldrx (code
, ARMREG_LR
, ARMREG_FP
, info_offset
);
349 arm_ldrw (code
, ARMREG_LR
, ARMREG_LR
, MONO_STRUCT_OFFSET (GSharedVtCallInfo
, vret_slot
));
350 arm_subx_imm (code
, ARMREG_LR
, ARMREG_LR
, n_arg_regs
+ n_arg_fregs
);
351 arm_lslx (code
, ARMREG_LR
, ARMREG_LR
, 3);
352 arm_movspx (code
, ARMREG_IP0
, ARMREG_SP
);
353 arm_addx (code
, ARMREG_LR
, ARMREG_IP0
, ARMREG_LR
);
355 /* Branch to specific marshalling code */
356 for (i
= GSHAREDVT_RET_NONE
; i
< GSHAREDVT_RET_NUM
; ++i
) {
357 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, i
);
358 arm_cmpx (code
, ARMREG_IP0
, ARMREG_IP1
);
360 arm_bcc (code
, ARMCOND_EQ
, 0);
366 * The address of the return value area is in LR, have to load it into
369 for (i
= GSHAREDVT_RET_NONE
; i
< GSHAREDVT_RET_NUM
; ++i
) {
370 mono_arm_patch (br
[i
], code
, MONO_R_ARM64_BCC
);
372 case GSHAREDVT_RET_NONE
:
374 case GSHAREDVT_RET_I8
:
375 arm_ldrx (code
, ARMREG_R0
, ARMREG_LR
, 0);
377 case GSHAREDVT_RET_I1
:
378 arm_ldrsbx (code
, ARMREG_R0
, ARMREG_LR
, 0);
380 case GSHAREDVT_RET_U1
:
381 arm_ldrb (code
, ARMREG_R0
, ARMREG_LR
, 0);
383 case GSHAREDVT_RET_I2
:
384 arm_ldrshx (code
, ARMREG_R0
, ARMREG_LR
, 0);
386 case GSHAREDVT_RET_U2
:
387 arm_ldrh (code
, ARMREG_R0
, ARMREG_LR
, 0);
389 case GSHAREDVT_RET_I4
:
390 arm_ldrswx (code
, ARMREG_R0
, ARMREG_LR
, 0);
392 case GSHAREDVT_RET_U4
:
393 arm_ldrw (code
, ARMREG_R0
, ARMREG_LR
, 0);
395 case GSHAREDVT_RET_R8
:
396 arm_ldrfpx (code
, ARMREG_D0
, ARMREG_LR
, 0);
398 case GSHAREDVT_RET_R4
:
399 arm_ldrfpw (code
, ARMREG_D0
, ARMREG_LR
, 0);
401 case GSHAREDVT_RET_IREGS_1
:
402 case GSHAREDVT_RET_IREGS_2
:
403 case GSHAREDVT_RET_IREGS_3
:
404 case GSHAREDVT_RET_IREGS_4
:
405 case GSHAREDVT_RET_IREGS_5
:
406 case GSHAREDVT_RET_IREGS_6
:
407 case GSHAREDVT_RET_IREGS_7
:
408 case GSHAREDVT_RET_IREGS_8
: {
411 for (j
= 0; j
< i
- GSHAREDVT_RET_IREGS_1
+ 1; ++j
)
412 arm_ldrx (code
, j
, ARMREG_LR
, j
* 8);
415 case GSHAREDVT_RET_HFAR8_1
:
416 case GSHAREDVT_RET_HFAR8_2
:
417 case GSHAREDVT_RET_HFAR8_3
:
418 case GSHAREDVT_RET_HFAR8_4
: {
421 for (j
= 0; j
< i
- GSHAREDVT_RET_HFAR8_1
+ 1; ++j
)
422 arm_ldrfpx (code
, j
, ARMREG_LR
, j
* 8);
425 case GSHAREDVT_RET_HFAR4_1
:
426 case GSHAREDVT_RET_HFAR4_2
:
427 case GSHAREDVT_RET_HFAR4_3
:
428 case GSHAREDVT_RET_HFAR4_4
: {
431 for (j
= 0; j
< i
- GSHAREDVT_RET_HFAR4_1
+ 1; ++j
)
432 arm_ldrfpw (code
, j
, ARMREG_LR
, j
* 4);
436 g_assert_not_reached ();
439 br_ret
[br_ret_index
++] = code
;
444 mono_arm_patch (br_out
, code
, MONO_R_ARM64_CBZ
);
446 /* Compute vret area address in LR */
447 arm_ldrx (code
, ARMREG_LR
, ARMREG_FP
, caller_reg_area_offset
+ (ARMREG_R8
* 8));
449 /* IP1 == return marshalling type */
450 arm_ldrx (code
, ARMREG_IP1
, ARMREG_FP
, info_offset
);
451 arm_ldrw (code
, ARMREG_IP1
, ARMREG_IP1
, MONO_STRUCT_OFFSET (GSharedVtCallInfo
, ret_marshal
));
453 /* Branch to specific marshalling code */
454 for (i
= GSHAREDVT_RET_NONE
; i
< GSHAREDVT_RET_NUM
; ++i
) {
455 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, i
);
456 arm_cmpx (code
, ARMREG_IP0
, ARMREG_IP1
);
458 arm_bcc (code
, ARMCOND_EQ
, 0);
462 * The return value is in registers, need to save to the return area passed by the caller in
465 for (i
= GSHAREDVT_RET_NONE
; i
< GSHAREDVT_RET_NUM
; ++i
) {
466 mono_arm_patch (br
[i
], code
, MONO_R_ARM64_BCC
);
468 case GSHAREDVT_RET_NONE
:
470 case GSHAREDVT_RET_I8
:
471 arm_strx (code
, ARMREG_R0
, ARMREG_LR
, 0);
473 case GSHAREDVT_RET_I1
:
474 case GSHAREDVT_RET_U1
:
475 arm_strb (code
, ARMREG_R0
, ARMREG_LR
, 0);
477 case GSHAREDVT_RET_I2
:
478 case GSHAREDVT_RET_U2
:
479 arm_strh (code
, ARMREG_R0
, ARMREG_LR
, 0);
481 case GSHAREDVT_RET_I4
:
482 case GSHAREDVT_RET_U4
:
483 arm_strw (code
, ARMREG_R0
, ARMREG_LR
, 0);
485 case GSHAREDVT_RET_R8
:
486 arm_strfpx (code
, ARMREG_D0
, ARMREG_LR
, 0);
488 case GSHAREDVT_RET_R4
:
489 arm_strfpw (code
, ARMREG_D0
, ARMREG_LR
, 0);
491 case GSHAREDVT_RET_IREGS_1
:
492 case GSHAREDVT_RET_IREGS_2
:
493 case GSHAREDVT_RET_IREGS_3
:
494 case GSHAREDVT_RET_IREGS_4
:
495 case GSHAREDVT_RET_IREGS_5
:
496 case GSHAREDVT_RET_IREGS_6
:
497 case GSHAREDVT_RET_IREGS_7
:
498 case GSHAREDVT_RET_IREGS_8
: {
501 for (j
= 0; j
< i
- GSHAREDVT_RET_IREGS_1
+ 1; ++j
)
502 arm_strx (code
, j
, ARMREG_LR
, j
* 8);
505 case GSHAREDVT_RET_HFAR8_1
:
506 case GSHAREDVT_RET_HFAR8_2
:
507 case GSHAREDVT_RET_HFAR8_3
:
508 case GSHAREDVT_RET_HFAR8_4
: {
511 for (j
= 0; j
< i
- GSHAREDVT_RET_HFAR8_1
+ 1; ++j
)
512 arm_strfpx (code
, j
, ARMREG_LR
, j
* 8);
515 case GSHAREDVT_RET_HFAR4_1
:
516 case GSHAREDVT_RET_HFAR4_2
:
517 case GSHAREDVT_RET_HFAR4_3
:
518 case GSHAREDVT_RET_HFAR4_4
: {
521 for (j
= 0; j
< i
- GSHAREDVT_RET_HFAR4_1
+ 1; ++j
)
522 arm_strfpw (code
, j
, ARMREG_LR
, j
* 4);
529 br_ret
[br_ret_index
++] = code
;
535 for (i
= 0; i
< br_ret_index
; ++i
)
536 mono_arm_patch (br_ret
[i
], code
, MONO_R_ARM64_B
);
537 for (i
= 0; i
< bcc_ret_index
; ++i
)
538 mono_arm_patch (bcc_ret
[i
], code
, MONO_R_ARM64_BCC
);
541 arm_movspx (code
, ARMREG_SP
, ARMREG_FP
);
542 arm_ldpx_post (code
, ARMREG_FP
, ARMREG_LR
, ARMREG_SP
, offset
);
543 arm_retx (code
, ARMREG_LR
);
545 g_assert ((code
- buf
) < buf_len
);
548 *info
= mono_tramp_info_create ("gsharedvt_trampoline", buf
, code
- buf
, ji
, unwind_ops
);
550 mono_arch_flush_icache (buf
, code
- buf
);
557 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo
**info
, gboolean aot
)
559 g_assert_not_reached ();
565 #endif /* MONO_ARCH_GSHAREDVT_SUPPORTED */