2 * mini-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.
11 #include "mini-arm64.h"
12 #include "mini-arm64-gsharedvt.h"
17 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
19 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
22 mono_arm_gsharedvt_init (void)
24 mono_aot_register_jit_icall ("mono_arm_start_gsharedvt_call", mono_arm_start_gsharedvt_call);
28 mono_arch_gsharedvt_sig_supported (MonoMethodSignature *sig)
31 if (sig->ret && is_variable_size (sig->ret))
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));
60 return reg + NUM_GSHAREDVT_ARG_GREGS;
64 map_stack_slot (int slot)
66 return slot + NUM_GSHAREDVT_ARG_GREGS + NUM_GSHAREDVT_ARG_FREGS;
70 get_arg_slots (ArgInfo *ainfo, int **out_slots)
72 int sreg = ainfo->reg;
73 int sslot = ainfo->offset / 8;
77 switch (ainfo->storage) {
81 src = g_malloc (nsrc * sizeof (int));
82 src [0] = map_reg (sreg);
84 case ArgVtypeByRefOnStack:
86 src = g_malloc (nsrc * sizeof (int));
87 src [0] = map_stack_slot (sslot);
92 src = g_malloc (nsrc * sizeof (int));
93 src [0] = map_freg (sreg);
97 src = g_malloc (nsrc * sizeof (int));
98 for (i = 0; i < ainfo->nregs; ++i)
99 src [i] = map_freg (sreg + i);
101 case ArgVtypeInIRegs:
103 src = g_malloc (nsrc * sizeof (int));
104 for (i = 0; i < ainfo->nregs; ++i)
105 src [i] = map_reg (sreg + i);
109 src = g_malloc (nsrc * sizeof (int));
110 src [0] = map_stack_slot (sslot);
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);
128 * mono_arch_get_gsharedvt_call_info:
130 * See mini-x86.c for documentation.
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;
139 gboolean var_ret = FALSE;
140 CallInfo *cinfo, *gcinfo;
141 MonoMethodSignature *sig, *gsig;
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);
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 */
166 cinfo = caller_cinfo;
167 gcinfo = callee_cinfo;
171 cinfo = callee_cinfo;
172 gcinfo = caller_cinfo;
175 if (gcinfo->ret.gsharedvt) {
177 * The return type is gsharedvt
183 * The stack looks like this:
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:
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);
212 src = g_new0 (int, 1);
213 if (ainfo->storage == ArgVtypeByRef)
214 src_slot = map_reg (ainfo->reg);
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;
225 arg_marshal = GSHAREDVT_ARG_BYREF_TO_BYVAL;
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);
238 arg_marshal = GSHAREDVT_ARG_BYVAL_TO_BYREF;
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;
249 dst = g_new0 (int, 1);
250 dst [0] = map_stack_slot (ainfo2->offset / 8);
252 ndst = get_arg_slots (ainfo2, &dst);
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
264 switch (ainfo->slot_size) {
266 arg_size = ainfo->sign ? GSHAREDVT_ARG_SIZE_I1 : GSHAREDVT_ARG_SIZE_U1;
269 arg_size = ainfo->sign ? GSHAREDVT_ARG_SIZE_I2 : GSHAREDVT_ARG_SIZE_U2;
272 arg_size = ainfo->sign ? GSHAREDVT_ARG_SIZE_I4 : GSHAREDVT_ARG_SIZE_U4;
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) {
290 arg_size = ainfo2->sign ? GSHAREDVT_ARG_SIZE_I1 : GSHAREDVT_ARG_SIZE_U1;
293 arg_size = ainfo2->sign ? GSHAREDVT_ARG_SIZE_I2 : GSHAREDVT_ARG_SIZE_U2;
296 arg_size = ainfo2->sign ? GSHAREDVT_ARG_SIZE_I4 : GSHAREDVT_ARG_SIZE_U4;
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]);
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)));
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;
331 g_assert (gcinfo->ret.gsharedvt);
332 info->vret_arg_reg = map_reg (ARMREG_R8);
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 */
345 switch (cinfo->ret.storage) {
347 if (!gsharedvt_in || sig->ret->byref) {
348 info->ret_marshal = GSHAREDVT_RET_I8;
350 switch (sig->ret->type) {
352 info->ret_marshal = GSHAREDVT_RET_I1;
355 case MONO_TYPE_BOOLEAN:
356 info->ret_marshal = GSHAREDVT_RET_U1;
359 info->ret_marshal = GSHAREDVT_RET_I2;
363 info->ret_marshal = GSHAREDVT_RET_U2;
366 info->ret_marshal = GSHAREDVT_RET_I4;
369 info->ret_marshal = GSHAREDVT_RET_U4;
372 info->ret_marshal = GSHAREDVT_RET_I8;
378 info->ret_marshal = GSHAREDVT_RET_R8;
381 info->ret_marshal = GSHAREDVT_RET_R4;
383 case ArgVtypeInIRegs:
384 info->ret_marshal = GSHAREDVT_RET_IREGS_1 - 1 + cinfo->ret.nregs;
387 if (cinfo->ret.esize == 4)
388 info->ret_marshal = GSHAREDVT_RET_HFAR4_1 - 1 + cinfo->ret.nregs;
390 info->ret_marshal = GSHAREDVT_RET_HFAR8_1 - 1 + cinfo->ret.nregs;
393 /* No conversion needed */
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);
414 mono_arm_gsharedvt_init (void)
418 #endif /* MONO_ARCH_GSHAREDVT_SUPPORTED */