[ci] Bump timeout in ms-test-suite
[mono-project.git] / mono / mini / mini-arm64-gsharedvt.c
blobdf24c53c6581c444f8c8c02ce774215617ad4f0b
1 /*
2 * mini-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 "mini.h"
11 #include "mini-arm64.h"
12 #include "mini-arm64-gsharedvt.h"
15 * GSHAREDVT
17 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
19 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
21 void
22 mono_arm_gsharedvt_init (void)
24 mono_aot_register_jit_icall ("mono_arm_start_gsharedvt_call", mono_arm_start_gsharedvt_call);
27 gboolean
28 mono_arch_gsharedvt_sig_supported (MonoMethodSignature *sig)
31 if (sig->ret && is_variable_size (sig->ret))
32 return FALSE;
34 return TRUE;
37 static inline void
38 add_to_map (GPtrArray *map, int src, int dst)
40 g_ptr_array_add (map, GUINT_TO_POINTER (src));
41 g_ptr_array_add (map, GUINT_TO_POINTER (dst));
45 * Slot mapping:
46 * 0..8 - r0..r8
47 * 9..16 - d0..d7
48 * 17.. - stack slots
51 static inline int
52 map_reg (int reg)
54 return reg;
57 static inline int
58 map_freg (int reg)
60 return reg + NUM_GSHAREDVT_ARG_GREGS;
63 static inline int
64 map_stack_slot (int slot)
66 return slot + NUM_GSHAREDVT_ARG_GREGS + NUM_GSHAREDVT_ARG_FREGS;
69 static int
70 get_arg_slots (ArgInfo *ainfo, int **out_slots)
72 int sreg = ainfo->reg;
73 int sslot = ainfo->offset / 8;
74 int *src = NULL;
75 int i, nsrc;
77 switch (ainfo->storage) {
78 case ArgInIReg:
79 case ArgVtypeByRef:
80 nsrc = 1;
81 src = g_malloc (nsrc * sizeof (int));
82 src [0] = map_reg (sreg);
83 break;
84 case ArgVtypeByRefOnStack:
85 nsrc = 1;
86 src = g_malloc (nsrc * sizeof (int));
87 src [0] = map_stack_slot (sslot);
88 break;
89 case ArgInFReg:
90 case ArgInFRegR4:
91 nsrc = 1;
92 src = g_malloc (nsrc * sizeof (int));
93 src [0] = map_freg (sreg);
94 break;
95 case ArgHFA:
96 nsrc = ainfo->nregs;
97 src = g_malloc (nsrc * sizeof (int));
98 for (i = 0; i < ainfo->nregs; ++i)
99 src [i] = map_freg (sreg + i);
100 break;
101 case ArgVtypeInIRegs:
102 nsrc = ainfo->nregs;
103 src = g_malloc (nsrc * sizeof (int));
104 for (i = 0; i < ainfo->nregs; ++i)
105 src [i] = map_reg (sreg + i);
106 break;
107 case ArgOnStack:
108 nsrc = 1;
109 src = g_malloc (nsrc * sizeof (int));
110 src [0] = map_stack_slot (sslot);
111 break;
112 case ArgVtypeOnStack:
113 nsrc = ainfo->size / 8;
114 src = g_malloc (nsrc * sizeof (int));
115 for (i = 0; i < nsrc; ++i)
116 src [i] = map_stack_slot (sslot + i);
117 break;
118 default:
119 NOT_IMPLEMENTED;
120 break;
123 *out_slots = src;
124 return nsrc;
128 * mono_arch_get_gsharedvt_call_info:
130 * See mini-x86.c for documentation.
132 gpointer
133 mono_arch_get_gsharedvt_call_info (gpointer addr, MonoMethodSignature *normal_sig, MonoMethodSignature *gsharedvt_sig, gboolean gsharedvt_in, gint32 vcall_offset, gboolean calli)
135 GSharedVtCallInfo *info;
136 CallInfo *caller_cinfo, *callee_cinfo;
137 MonoMethodSignature *caller_sig, *callee_sig;
138 int aindex, i;
139 gboolean var_ret = FALSE;
140 CallInfo *cinfo, *gcinfo;
141 MonoMethodSignature *sig, *gsig;
142 GPtrArray *map;
144 if (gsharedvt_in) {
145 caller_sig = normal_sig;
146 callee_sig = gsharedvt_sig;
147 caller_cinfo = mono_arch_get_call_info (NULL, caller_sig);
148 callee_cinfo = mono_arch_get_call_info (NULL, callee_sig);
149 } else {
150 callee_sig = normal_sig;
151 caller_sig = gsharedvt_sig;
152 callee_cinfo = mono_arch_get_call_info (NULL, callee_sig);
153 caller_cinfo = mono_arch_get_call_info (NULL, caller_sig);
157 * If GSHAREDVT_IN is true, this means we are transitioning from normal to gsharedvt code. The caller uses the
158 * normal call signature, while the callee uses the gsharedvt signature.
159 * If GSHAREDVT_IN is false, its the other way around.
162 /* sig/cinfo describes the normal call, while gsig/gcinfo describes the gsharedvt call */
163 if (gsharedvt_in) {
164 sig = caller_sig;
165 gsig = callee_sig;
166 cinfo = caller_cinfo;
167 gcinfo = callee_cinfo;
168 } else {
169 sig = callee_sig;
170 gsig = caller_sig;
171 cinfo = callee_cinfo;
172 gcinfo = caller_cinfo;
175 if (gcinfo->ret.gsharedvt) {
177 * The return type is gsharedvt
179 var_ret = TRUE;
183 * The stack looks like this:
184 * <arguments>
185 * <trampoline frame>
186 * <call area>
187 * We have to map the stack slots in <arguments> to the stack slots in <call area>.
189 map = g_ptr_array_new ();
191 for (aindex = 0; aindex < cinfo->nargs; ++aindex) {
192 ArgInfo *ainfo = &caller_cinfo->args [aindex];
193 ArgInfo *ainfo2 = &callee_cinfo->args [aindex];
194 int *src = NULL, *dst = NULL;
195 int nsrc, ndst, nslots, src_slot, arg_marshal;
198 * The src descriptor looks like this:
199 * - 6 bits src slot
200 * - 12 bits number of slots
201 * - 4 bits marshal type (GSHAREDVT_ARG_...)
202 * - 4 bits size/sign descriptor (GSHAREDVT_ARG_SIZE)
203 * - 4 bits offset inside stack slots
205 arg_marshal = GSHAREDVT_ARG_NONE;
207 if (ainfo->gsharedvt) {
208 /* Pass the value whose address is received in a reg by value */
209 g_assert (!ainfo2->gsharedvt);
210 ndst = get_arg_slots (ainfo2, &dst);
211 nsrc = 1;
212 src = g_new0 (int, 1);
213 if (ainfo->storage == ArgVtypeByRef)
214 src_slot = map_reg (ainfo->reg);
215 else
216 src_slot = map_stack_slot (ainfo->offset / 8);
217 g_assert (ndst < 256);
218 g_assert (src_slot < 64);
219 src [0] = (ndst << 6) | src_slot;
220 if (ainfo2->storage == ArgHFA && ainfo2->esize == 4)
221 arg_marshal = GSHAREDVT_ARG_BYREF_TO_BYVAL_HFAR4;
222 else if (ainfo2->storage == ArgVtypeByRef || ainfo2->storage == ArgVtypeByRefOnStack)
223 arg_marshal = GSHAREDVT_ARG_BYREF_TO_BYREF;
224 else
225 arg_marshal = GSHAREDVT_ARG_BYREF_TO_BYVAL;
226 } else {
227 nsrc = get_arg_slots (ainfo, &src);
229 if (ainfo2->storage == ArgVtypeByRef && ainfo2->gsharedvt) {
230 /* Pass the address of the first src slot in a reg */
231 if (ainfo->storage != ArgVtypeByRef) {
232 if (ainfo->storage == ArgHFA && ainfo->esize == 4) {
233 arg_marshal = GSHAREDVT_ARG_BYVAL_TO_BYREF_HFAR4;
234 g_assert (src [0] < 64);
235 g_assert (nsrc < 256);
236 src [0] |= (nsrc << 6);
237 } else {
238 arg_marshal = GSHAREDVT_ARG_BYVAL_TO_BYREF;
241 ndst = 1;
242 dst = g_new0 (int, 1);
243 dst [0] = map_reg (ainfo2->reg);
244 } else if (ainfo2->storage == ArgVtypeByRefOnStack && ainfo2->gsharedvt) {
245 /* Pass the address of the first src slot in a stack slot */
246 if (ainfo->storage != ArgVtypeByRef)
247 arg_marshal = GSHAREDVT_ARG_BYVAL_TO_BYREF;
248 ndst = 1;
249 dst = g_new0 (int, 1);
250 dst [0] = map_stack_slot (ainfo2->offset / 8);
251 } else {
252 ndst = get_arg_slots (ainfo2, &dst);
254 if (nsrc)
255 src [0] |= (arg_marshal << 18);
256 if (ainfo->storage == ArgOnStack && ainfo->slot_size != 8) {
257 GSharedVtArgSize arg_size = GSHAREDVT_ARG_SIZE_NONE;
260 * On IOS, stack arguments smaller than 8 bytes can
261 * share a stack slot. Encode this information into
262 * the descriptor.
264 switch (ainfo->slot_size) {
265 case 1:
266 arg_size = ainfo->sign ? GSHAREDVT_ARG_SIZE_I1 : GSHAREDVT_ARG_SIZE_U1;
267 break;
268 case 2:
269 arg_size = ainfo->sign ? GSHAREDVT_ARG_SIZE_I2 : GSHAREDVT_ARG_SIZE_U2;
270 break;
271 case 4:
272 arg_size = ainfo->sign ? GSHAREDVT_ARG_SIZE_I4 : GSHAREDVT_ARG_SIZE_U4;
273 break;
274 default:
275 NOT_IMPLEMENTED;
276 break;
278 /* Encode the size/sign */
279 src [0] |= (arg_size << 22);
280 /* Encode the offset inside the stack slot */
281 src [0] |= ((ainfo->offset % 8) << 26);
282 if (ainfo2->storage == ArgOnStack)
283 dst [0] |= ((ainfo2->offset % 8) << 26);
284 } else if (ainfo2->storage == ArgOnStack && ainfo2->slot_size != 8) {
285 /* The caller passes in an address, need to store it into a stack slot */
287 GSharedVtArgSize arg_size = GSHAREDVT_ARG_SIZE_NONE;
288 switch (ainfo2->slot_size) {
289 case 1:
290 arg_size = ainfo2->sign ? GSHAREDVT_ARG_SIZE_I1 : GSHAREDVT_ARG_SIZE_U1;
291 break;
292 case 2:
293 arg_size = ainfo2->sign ? GSHAREDVT_ARG_SIZE_I2 : GSHAREDVT_ARG_SIZE_U2;
294 break;
295 case 4:
296 arg_size = ainfo2->sign ? GSHAREDVT_ARG_SIZE_I4 : GSHAREDVT_ARG_SIZE_U4;
297 break;
298 default:
299 NOT_IMPLEMENTED;
300 break;
302 /* Encode the size/sign */
303 src [0] |= (arg_size << 22);
304 /* Encode the offset inside the stack slot */
305 dst [0] |= ((ainfo2->offset % 8) << 26);
307 nslots = MIN (nsrc, ndst);
309 for (i = 0; i < nslots; ++i)
310 add_to_map (map, src [i], dst [i]);
312 g_free (src);
313 g_free (dst);
316 if (cinfo->ret.storage == ArgVtypeByRef) {
317 /* Both the caller and the callee pass the vtype ret address in r8 */
318 g_assert (cinfo->ret.storage == gcinfo->ret.storage);
319 add_to_map (map, map_reg (ARMREG_R8), map_reg (ARMREG_R8));
322 info = mono_domain_alloc0 (mono_domain_get (), sizeof (GSharedVtCallInfo) + (map->len * sizeof (int)));
323 info->addr = addr;
324 info->stack_usage = callee_cinfo->stack_usage;
325 info->ret_marshal = GSHAREDVT_RET_NONE;
326 info->gsharedvt_in = gsharedvt_in ? 1 : 0;
327 info->vret_slot = -1;
328 info->calli = calli;
330 if (var_ret) {
331 g_assert (gcinfo->ret.gsharedvt);
332 info->vret_arg_reg = map_reg (ARMREG_R8);
333 } else {
334 info->vret_arg_reg = -1;
337 info->vcall_offset = vcall_offset;
338 info->map_count = map->len / 2;
339 for (i = 0; i < map->len; ++i)
340 info->map [i] = GPOINTER_TO_UINT (g_ptr_array_index (map, i));
341 g_ptr_array_free (map, TRUE);
343 /* Compute return value marshalling */
344 if (var_ret) {
345 switch (cinfo->ret.storage) {
346 case ArgInIReg:
347 if (!gsharedvt_in || sig->ret->byref) {
348 info->ret_marshal = GSHAREDVT_RET_I8;
349 } else {
350 switch (sig->ret->type) {
351 case MONO_TYPE_I1:
352 info->ret_marshal = GSHAREDVT_RET_I1;
353 break;
354 case MONO_TYPE_U1:
355 case MONO_TYPE_BOOLEAN:
356 info->ret_marshal = GSHAREDVT_RET_U1;
357 break;
358 case MONO_TYPE_I2:
359 info->ret_marshal = GSHAREDVT_RET_I2;
360 break;
361 case MONO_TYPE_U2:
362 case MONO_TYPE_CHAR:
363 info->ret_marshal = GSHAREDVT_RET_U2;
364 break;
365 case MONO_TYPE_I4:
366 info->ret_marshal = GSHAREDVT_RET_I4;
367 break;
368 case MONO_TYPE_U4:
369 info->ret_marshal = GSHAREDVT_RET_U4;
370 break;
371 default:
372 info->ret_marshal = GSHAREDVT_RET_I8;
373 break;
376 break;
377 case ArgInFReg:
378 info->ret_marshal = GSHAREDVT_RET_R8;
379 break;
380 case ArgInFRegR4:
381 info->ret_marshal = GSHAREDVT_RET_R4;
382 break;
383 case ArgVtypeInIRegs:
384 info->ret_marshal = GSHAREDVT_RET_IREGS_1 - 1 + cinfo->ret.nregs;
385 break;
386 case ArgHFA:
387 if (cinfo->ret.esize == 4)
388 info->ret_marshal = GSHAREDVT_RET_HFAR4_1 - 1 + cinfo->ret.nregs;
389 else
390 info->ret_marshal = GSHAREDVT_RET_HFAR8_1 - 1 + cinfo->ret.nregs;
391 break;
392 case ArgVtypeByRef:
393 /* No conversion needed */
394 break;
395 default:
396 g_assert_not_reached ();
400 if (gsharedvt_in && var_ret && cinfo->ret.storage != ArgVtypeByRef) {
401 /* Allocate stack space for the return value */
402 info->vret_slot = map_stack_slot (info->stack_usage / sizeof (gpointer));
403 info->stack_usage += mono_type_stack_size_internal (normal_sig->ret, NULL, FALSE) + sizeof (gpointer);
406 info->stack_usage = ALIGN_TO (info->stack_usage, MONO_ARCH_FRAME_ALIGNMENT);
408 return info;
411 #else
413 void
414 mono_arm_gsharedvt_init (void)
418 #endif /* MONO_ARCH_GSHAREDVT_SUPPORTED */