3 * gsharedvt support code for arm64
6 * Zoltan Varga <vargaz@gmail.com>
8 * Copyright 2013 Xamarin, Inc (http://www.xamarin.com)
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
12 #include "mini-arm64.h"
13 #include "mini-arm64-gsharedvt.h"
18 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
20 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
23 mono_arm_gsharedvt_init (void)
25 mono_aot_register_jit_icall ("mono_arm_start_gsharedvt_call", mono_arm_start_gsharedvt_call);
29 mono_arch_gsharedvt_sig_supported (MonoMethodSignature *sig)
32 if (sig->ret && is_variable_size (sig->ret))
39 add_to_map (GPtrArray *map, int src, int dst)
41 g_ptr_array_add (map, GUINT_TO_POINTER (src));
42 g_ptr_array_add (map, GUINT_TO_POINTER (dst));
61 return reg + NUM_GSHAREDVT_ARG_GREGS;
65 map_stack_slot (int slot)
67 return slot + NUM_GSHAREDVT_ARG_GREGS + NUM_GSHAREDVT_ARG_FREGS;
71 get_arg_slots (ArgInfo *ainfo, int **out_slots)
73 int sreg = ainfo->reg;
74 int sslot = ainfo->offset / 8;
78 switch (ainfo->storage) {
82 src = g_malloc (nsrc * sizeof (int));
83 src [0] = map_reg (sreg);
85 case ArgVtypeByRefOnStack:
87 src = g_malloc (nsrc * sizeof (int));
88 src [0] = map_stack_slot (sslot);
93 src = g_malloc (nsrc * sizeof (int));
94 src [0] = map_freg (sreg);
98 src = g_malloc (nsrc * sizeof (int));
99 for (i = 0; i < ainfo->nregs; ++i)
100 src [i] = map_freg (sreg + i);
102 case ArgVtypeInIRegs:
104 src = g_malloc (nsrc * sizeof (int));
105 for (i = 0; i < ainfo->nregs; ++i)
106 src [i] = map_reg (sreg + i);
110 src = g_malloc (nsrc * sizeof (int));
111 src [0] = map_stack_slot (sslot);
113 case ArgVtypeOnStack:
114 nsrc = ainfo->size / 8;
115 src = g_malloc (nsrc * sizeof (int));
116 for (i = 0; i < nsrc; ++i)
117 src [i] = map_stack_slot (sslot + i);
129 * mono_arch_get_gsharedvt_call_info:
131 * See mini-x86.c for documentation.
134 mono_arch_get_gsharedvt_call_info (gpointer addr, MonoMethodSignature *normal_sig, MonoMethodSignature *gsharedvt_sig, gboolean gsharedvt_in, gint32 vcall_offset, gboolean calli)
136 GSharedVtCallInfo *info;
137 CallInfo *caller_cinfo, *callee_cinfo;
138 MonoMethodSignature *caller_sig, *callee_sig;
140 gboolean var_ret = FALSE;
141 CallInfo *cinfo, *gcinfo;
142 MonoMethodSignature *sig, *gsig;
146 caller_sig = normal_sig;
147 callee_sig = gsharedvt_sig;
148 caller_cinfo = mono_arch_get_call_info (NULL, caller_sig);
149 callee_cinfo = mono_arch_get_call_info (NULL, callee_sig);
151 callee_sig = normal_sig;
152 caller_sig = gsharedvt_sig;
153 callee_cinfo = mono_arch_get_call_info (NULL, callee_sig);
154 caller_cinfo = mono_arch_get_call_info (NULL, caller_sig);
158 * If GSHAREDVT_IN is true, this means we are transitioning from normal to gsharedvt code. The caller uses the
159 * normal call signature, while the callee uses the gsharedvt signature.
160 * If GSHAREDVT_IN is false, its the other way around.
163 /* sig/cinfo describes the normal call, while gsig/gcinfo describes the gsharedvt call */
167 cinfo = caller_cinfo;
168 gcinfo = callee_cinfo;
172 cinfo = callee_cinfo;
173 gcinfo = caller_cinfo;
176 if (gcinfo->ret.gsharedvt) {
178 * The return type is gsharedvt
184 * The stack looks like this:
188 * We have to map the stack slots in <arguments> to the stack slots in <call area>.
190 map = g_ptr_array_new ();
192 for (aindex = 0; aindex < cinfo->nargs; ++aindex) {
193 ArgInfo *ainfo = &caller_cinfo->args [aindex];
194 ArgInfo *ainfo2 = &callee_cinfo->args [aindex];
195 int *src = NULL, *dst = NULL;
196 int nsrc, ndst, nslots, src_slot, arg_marshal;
199 * The src descriptor looks like this:
201 * - 12 bits number of slots
202 * - 4 bits marshal type (GSHAREDVT_ARG_...)
203 * - 4 bits size/sign descriptor (GSHAREDVT_ARG_SIZE)
204 * - 4 bits offset inside stack slots
206 arg_marshal = GSHAREDVT_ARG_NONE;
208 if (ainfo->gsharedvt) {
209 /* Pass the value whose address is received in a reg by value */
210 g_assert (!ainfo2->gsharedvt);
211 ndst = get_arg_slots (ainfo2, &dst);
213 src = g_new0 (int, 1);
214 if (ainfo->storage == ArgVtypeByRef)
215 src_slot = map_reg (ainfo->reg);
217 src_slot = map_stack_slot (ainfo->offset / 8);
218 g_assert (ndst < 256);
219 g_assert (src_slot < 64);
220 src [0] = (ndst << 6) | src_slot;
221 if (ainfo2->storage == ArgHFA && ainfo2->esize == 4)
222 arg_marshal = GSHAREDVT_ARG_BYREF_TO_BYVAL_HFAR4;
223 else if (ainfo2->storage == ArgVtypeByRef || ainfo2->storage == ArgVtypeByRefOnStack)
224 arg_marshal = GSHAREDVT_ARG_BYREF_TO_BYREF;
226 arg_marshal = GSHAREDVT_ARG_BYREF_TO_BYVAL;
228 nsrc = get_arg_slots (ainfo, &src);
230 if (ainfo2->storage == ArgVtypeByRef && ainfo2->gsharedvt) {
231 /* Pass the address of the first src slot in a reg */
232 if (ainfo->storage != ArgVtypeByRef) {
233 if (ainfo->storage == ArgHFA && ainfo->esize == 4) {
234 arg_marshal = GSHAREDVT_ARG_BYVAL_TO_BYREF_HFAR4;
235 g_assert (src [0] < 64);
236 g_assert (nsrc < 256);
237 src [0] |= (nsrc << 6);
239 arg_marshal = GSHAREDVT_ARG_BYVAL_TO_BYREF;
243 dst = g_new0 (int, 1);
244 dst [0] = map_reg (ainfo2->reg);
245 } else if (ainfo2->storage == ArgVtypeByRefOnStack && ainfo2->gsharedvt) {
246 /* Pass the address of the first src slot in a stack slot */
247 if (ainfo->storage != ArgVtypeByRef)
248 arg_marshal = GSHAREDVT_ARG_BYVAL_TO_BYREF;
250 dst = g_new0 (int, 1);
251 dst [0] = map_stack_slot (ainfo2->offset / 8);
253 ndst = get_arg_slots (ainfo2, &dst);
256 src [0] |= (arg_marshal << 18);
257 if (ainfo->storage == ArgOnStack && ainfo->slot_size != 8) {
258 GSharedVtArgSize arg_size = GSHAREDVT_ARG_SIZE_NONE;
261 * On IOS, stack arguments smaller than 8 bytes can
262 * share a stack slot. Encode this information into
265 switch (ainfo->slot_size) {
267 arg_size = ainfo->sign ? GSHAREDVT_ARG_SIZE_I1 : GSHAREDVT_ARG_SIZE_U1;
270 arg_size = ainfo->sign ? GSHAREDVT_ARG_SIZE_I2 : GSHAREDVT_ARG_SIZE_U2;
273 arg_size = ainfo->sign ? GSHAREDVT_ARG_SIZE_I4 : GSHAREDVT_ARG_SIZE_U4;
279 /* Encode the size/sign */
280 src [0] |= (arg_size << 22);
281 /* Encode the offset inside the stack slot */
282 src [0] |= ((ainfo->offset % 8) << 26);
283 if (ainfo2->storage == ArgOnStack)
284 dst [0] |= ((ainfo2->offset % 8) << 26);
285 } else if (ainfo2->storage == ArgOnStack && ainfo2->slot_size != 8) {
286 /* The caller passes in an address, need to store it into a stack slot */
288 GSharedVtArgSize arg_size = GSHAREDVT_ARG_SIZE_NONE;
289 switch (ainfo2->slot_size) {
291 arg_size = ainfo2->sign ? GSHAREDVT_ARG_SIZE_I1 : GSHAREDVT_ARG_SIZE_U1;
294 arg_size = ainfo2->sign ? GSHAREDVT_ARG_SIZE_I2 : GSHAREDVT_ARG_SIZE_U2;
297 arg_size = ainfo2->sign ? GSHAREDVT_ARG_SIZE_I4 : GSHAREDVT_ARG_SIZE_U4;
303 /* Encode the size/sign */
304 src [0] |= (arg_size << 22);
305 /* Encode the offset inside the stack slot */
306 dst [0] |= ((ainfo2->offset % 8) << 26);
308 nslots = MIN (nsrc, ndst);
310 for (i = 0; i < nslots; ++i)
311 add_to_map (map, src [i], dst [i]);
317 if (cinfo->ret.storage == ArgVtypeByRef) {
318 /* Both the caller and the callee pass the vtype ret address in r8 */
319 g_assert (cinfo->ret.storage == gcinfo->ret.storage);
320 add_to_map (map, map_reg (ARMREG_R8), map_reg (ARMREG_R8));
323 info = mono_domain_alloc0 (mono_domain_get (), sizeof (GSharedVtCallInfo) + (map->len * sizeof (int)));
325 info->stack_usage = callee_cinfo->stack_usage;
326 info->ret_marshal = GSHAREDVT_RET_NONE;
327 info->gsharedvt_in = gsharedvt_in ? 1 : 0;
328 info->vret_slot = -1;
332 g_assert (gcinfo->ret.gsharedvt);
333 info->vret_arg_reg = map_reg (ARMREG_R8);
335 info->vret_arg_reg = -1;
338 info->vcall_offset = vcall_offset;
339 info->map_count = map->len / 2;
340 for (i = 0; i < map->len; ++i)
341 info->map [i] = GPOINTER_TO_UINT (g_ptr_array_index (map, i));
342 g_ptr_array_free (map, TRUE);
344 /* Compute return value marshalling */
346 switch (cinfo->ret.storage) {
348 if (!gsharedvt_in || sig->ret->byref) {
349 info->ret_marshal = GSHAREDVT_RET_I8;
351 MonoType *rtype = mini_get_underlying_type (sig->ret);
353 switch (rtype->type) {
355 info->ret_marshal = GSHAREDVT_RET_I1;
358 info->ret_marshal = GSHAREDVT_RET_U1;
361 info->ret_marshal = GSHAREDVT_RET_I2;
364 info->ret_marshal = GSHAREDVT_RET_U2;
367 info->ret_marshal = GSHAREDVT_RET_I4;
370 info->ret_marshal = GSHAREDVT_RET_U4;
373 info->ret_marshal = GSHAREDVT_RET_I8;
379 info->ret_marshal = GSHAREDVT_RET_R8;
382 info->ret_marshal = GSHAREDVT_RET_R4;
384 case ArgVtypeInIRegs:
385 info->ret_marshal = GSHAREDVT_RET_IREGS_1 - 1 + cinfo->ret.nregs;
388 if (cinfo->ret.esize == 4)
389 info->ret_marshal = GSHAREDVT_RET_HFAR4_1 - 1 + cinfo->ret.nregs;
391 info->ret_marshal = GSHAREDVT_RET_HFAR8_1 - 1 + cinfo->ret.nregs;
394 /* No conversion needed */
397 g_assert_not_reached ();
401 if (gsharedvt_in && var_ret && cinfo->ret.storage != ArgVtypeByRef) {
402 /* Allocate stack space for the return value */
403 info->vret_slot = map_stack_slot (info->stack_usage / sizeof (gpointer));
404 info->stack_usage += mono_type_stack_size_internal (normal_sig->ret, NULL, FALSE) + sizeof (gpointer);
407 info->stack_usage = ALIGN_TO (info->stack_usage, MONO_ARCH_FRAME_ALIGNMENT);
415 mono_arm_gsharedvt_init (void)
419 #endif /* MONO_ARCH_GSHAREDVT_SUPPORTED */