[ci] Bump timeout in ms-test-suite
[mono-project.git] / mono / mini / tramp-arm64-gsharedvt.c
blob175f75ddaab47c94e9875726f6f0b6bab579a731
1 /*
2 * tramp-arm64-gsharedvt.c: gsharedvt support code for arm64
4 * Authors:
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.
9 */
10 #include <mono/metadata/abi-details.h>
12 #include "mini.h"
13 #include "mini-arm64.h"
14 #include "mini-arm64-gsharedvt.h"
17 * GSHAREDVT
19 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
22 * mono_arch_get_gsharedvt_arg_trampoline:
24 * See tramp-x86.c for documentation.
26 gpointer
27 mono_arch_get_gsharedvt_arg_trampoline (MonoDomain *domain, gpointer arg, gpointer addr)
29 guint8 *code, *buf;
30 int buf_len = 40;
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);
46 return buf;
49 gpointer
50 mono_arm_start_gsharedvt_call (GSharedVtCallInfo *info, gpointer *caller, gpointer *callee, gpointer mrgctx_reg)
52 int i;
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;
82 break;
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;
88 break;
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 ();
93 break;
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;
98 break;
99 default:
100 NOT_IMPLEMENTED;
101 break;
104 if (arg_marshal == GSHAREDVT_ARG_BYVAL_TO_BYREF)
105 continue;
107 switch (arg_size) {
108 case GSHAREDVT_ARG_SIZE_I1:
109 *(gint8*)dst_ptr = *(gint8*)src_ptr;
110 break;
111 case GSHAREDVT_ARG_SIZE_U1:
112 *(guint8*)dst_ptr = *(guint8*)src_ptr;
113 break;
114 case GSHAREDVT_ARG_SIZE_I2:
115 *(gint16*)dst_ptr = *(gint16*)src_ptr;
116 break;
117 case GSHAREDVT_ARG_SIZE_U2:
118 *(guint16*)dst_ptr = *(guint16*)src_ptr;
119 break;
120 case GSHAREDVT_ARG_SIZE_I4:
121 *(gint32*)dst_ptr = *(gint32*)src_ptr;
122 break;
123 case GSHAREDVT_ARG_SIZE_U4:
124 *(guint32*)dst_ptr = *(guint32*)src_ptr;
125 break;
126 default:
127 g_assert_not_reached ();
129 continue;
132 switch (arg_marshal) {
133 case GSHAREDVT_ARG_NONE:
134 callee [dst] = caller [src];
135 break;
136 case GSHAREDVT_ARG_BYVAL_TO_BYREF:
137 /* gsharedvt argument passed by addr in reg/stack slot */
138 src = src & 0x3f;
139 callee [dst] = caller + src;
140 break;
141 case GSHAREDVT_ARG_BYVAL_TO_BYREF_HFAR4: {
142 int nslots = (src >> 6) & 0xff;
143 int src_slot = src & 0x3f;
144 int j;
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;
152 break;
154 case GSHAREDVT_ARG_BYREF_TO_BYVAL: {
155 int nslots = (src >> 6) & 0xff;
156 int src_slot = src & 0x3f;
157 int j;
158 gpointer *addr = caller [src_slot];
160 for (j = 0; j < nslots; ++j)
161 callee [dst + j] = addr [j];
162 break;
164 case GSHAREDVT_ARG_BYREF_TO_BYVAL_HFAR4: {
165 int nslots = (src >> 6) & 0xff;
166 int src_slot = src & 0x3f;
167 int j;
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]);
173 break;
175 case GSHAREDVT_ARG_BYREF_TO_BYREF: {
176 int src_slot = src & 0x3f;
178 callee [dst] = caller [src_slot];
179 break;
181 default:
182 g_assert_not_reached ();
183 break;
187 if (info->vcall_offset != -1) {
188 MonoObject *this_obj = caller [0];
190 if (G_UNLIKELY (!this_obj))
191 return NULL;
192 if (info->vcall_offset == MONO_GSHAREDVT_DEL_INVOKE_VT_OFFSET)
193 /* delegate invoke */
194 return ((MonoDelegate*)this_obj)->invoke_impl;
195 else
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 */
199 return mrgctx_reg;
200 } else {
201 return info->addr;
205 #ifndef DISABLE_JIT
207 gpointer
208 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
210 guint8 *code, *buf;
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;
219 buf_len = 2048;
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 */
230 offset = 0;
231 /* frame block */
232 offset += 2 * 8;
233 /* info argument */
234 info_offset = offset;
235 offset += 8;
236 /* saved rgctx */
237 rgctx_arg_reg_offset = offset;
238 offset += 8;
239 /* alignment */
240 offset += 8;
241 /* argument regs */
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);
248 cfa_offset = offset;
250 /* Setup frame */
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);
261 /* Save rgxctx */
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:
284 * <caller frame>
285 * <saved r0-r8>
286 * <our frame>
287 * <saved fp, lr> <- fp
288 * <callee area> <- sp
291 /* Call start_gsharedvt_call () */
292 /* arg1 == info */
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);
301 if (aot)
302 code = mono_arm_emit_aotconst (&ji, code, buf, ARMREG_IP0, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_arm_start_gsharedvt_call");
303 else
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);
310 /* Load rgxctx */
311 arm_ldrx (code, MONO_ARCH_RGCTX_REG, ARMREG_FP, rgctx_arg_reg_offset);
312 /* Load argument registers */
313 // FIXME:
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);
321 /* Make the call */
322 arm_blrx (code, ARMREG_IP1);
324 br_ret_index = 0;
325 bcc_ret_index = 0;
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));
331 br_out = code;
332 arm_cbzx (code, ARMREG_IP1, 0);
334 /* IN CASE */
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);
359 br [i] = code;
360 arm_bcc (code, ARMCOND_EQ, 0);
363 arm_brk (code, 0);
366 * The address of the return value area is in LR, have to load it into
367 * registers.
369 for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
370 mono_arm_patch (br [i], code, MONO_R_ARM64_BCC);
371 switch (i) {
372 case GSHAREDVT_RET_NONE:
373 break;
374 case GSHAREDVT_RET_I8:
375 arm_ldrx (code, ARMREG_R0, ARMREG_LR, 0);
376 break;
377 case GSHAREDVT_RET_I1:
378 arm_ldrsbx (code, ARMREG_R0, ARMREG_LR, 0);
379 break;
380 case GSHAREDVT_RET_U1:
381 arm_ldrb (code, ARMREG_R0, ARMREG_LR, 0);
382 break;
383 case GSHAREDVT_RET_I2:
384 arm_ldrshx (code, ARMREG_R0, ARMREG_LR, 0);
385 break;
386 case GSHAREDVT_RET_U2:
387 arm_ldrh (code, ARMREG_R0, ARMREG_LR, 0);
388 break;
389 case GSHAREDVT_RET_I4:
390 arm_ldrswx (code, ARMREG_R0, ARMREG_LR, 0);
391 break;
392 case GSHAREDVT_RET_U4:
393 arm_ldrw (code, ARMREG_R0, ARMREG_LR, 0);
394 break;
395 case GSHAREDVT_RET_R8:
396 arm_ldrfpx (code, ARMREG_D0, ARMREG_LR, 0);
397 break;
398 case GSHAREDVT_RET_R4:
399 arm_ldrfpw (code, ARMREG_D0, ARMREG_LR, 0);
400 break;
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: {
409 int j;
411 for (j = 0; j < i - GSHAREDVT_RET_IREGS_1 + 1; ++j)
412 arm_ldrx (code, j, ARMREG_LR, j * 8);
413 break;
415 case GSHAREDVT_RET_HFAR8_1:
416 case GSHAREDVT_RET_HFAR8_2:
417 case GSHAREDVT_RET_HFAR8_3:
418 case GSHAREDVT_RET_HFAR8_4: {
419 int j;
421 for (j = 0; j < i - GSHAREDVT_RET_HFAR8_1 + 1; ++j)
422 arm_ldrfpx (code, j, ARMREG_LR, j * 8);
423 break;
425 case GSHAREDVT_RET_HFAR4_1:
426 case GSHAREDVT_RET_HFAR4_2:
427 case GSHAREDVT_RET_HFAR4_3:
428 case GSHAREDVT_RET_HFAR4_4: {
429 int j;
431 for (j = 0; j < i - GSHAREDVT_RET_HFAR4_1 + 1; ++j)
432 arm_ldrfpw (code, j, ARMREG_LR, j * 4);
433 break;
435 default:
436 g_assert_not_reached ();
437 break;
439 br_ret [br_ret_index ++] = code;
440 arm_b (code, 0);
443 /* OUT CASE */
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);
457 br [i] = code;
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
463 * R8.
465 for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
466 mono_arm_patch (br [i], code, MONO_R_ARM64_BCC);
467 switch (i) {
468 case GSHAREDVT_RET_NONE:
469 break;
470 case GSHAREDVT_RET_I8:
471 arm_strx (code, ARMREG_R0, ARMREG_LR, 0);
472 break;
473 case GSHAREDVT_RET_I1:
474 case GSHAREDVT_RET_U1:
475 arm_strb (code, ARMREG_R0, ARMREG_LR, 0);
476 break;
477 case GSHAREDVT_RET_I2:
478 case GSHAREDVT_RET_U2:
479 arm_strh (code, ARMREG_R0, ARMREG_LR, 0);
480 break;
481 case GSHAREDVT_RET_I4:
482 case GSHAREDVT_RET_U4:
483 arm_strw (code, ARMREG_R0, ARMREG_LR, 0);
484 break;
485 case GSHAREDVT_RET_R8:
486 arm_strfpx (code, ARMREG_D0, ARMREG_LR, 0);
487 break;
488 case GSHAREDVT_RET_R4:
489 arm_strfpw (code, ARMREG_D0, ARMREG_LR, 0);
490 break;
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: {
499 int j;
501 for (j = 0; j < i - GSHAREDVT_RET_IREGS_1 + 1; ++j)
502 arm_strx (code, j, ARMREG_LR, j * 8);
503 break;
505 case GSHAREDVT_RET_HFAR8_1:
506 case GSHAREDVT_RET_HFAR8_2:
507 case GSHAREDVT_RET_HFAR8_3:
508 case GSHAREDVT_RET_HFAR8_4: {
509 int j;
511 for (j = 0; j < i - GSHAREDVT_RET_HFAR8_1 + 1; ++j)
512 arm_strfpx (code, j, ARMREG_LR, j * 8);
513 break;
515 case GSHAREDVT_RET_HFAR4_1:
516 case GSHAREDVT_RET_HFAR4_2:
517 case GSHAREDVT_RET_HFAR4_3:
518 case GSHAREDVT_RET_HFAR4_4: {
519 int j;
521 for (j = 0; j < i - GSHAREDVT_RET_HFAR4_1 + 1; ++j)
522 arm_strfpw (code, j, ARMREG_LR, j * 4);
523 break;
525 default:
526 arm_brk (code, i);
527 break;
529 br_ret [br_ret_index ++] = code;
530 arm_b (code, 0);
533 arm_brk (code, 0);
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);
540 /* Normal return */
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);
547 if (info)
548 *info = mono_tramp_info_create ("gsharedvt_trampoline", buf, code - buf, ji, unwind_ops);
550 mono_arch_flush_icache (buf, code - buf);
551 return buf;
554 #else
556 gpointer
557 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
559 g_assert_not_reached ();
560 return NULL;
563 #endif
565 #endif /* MONO_ARCH_GSHAREDVT_SUPPORTED */