3 * gsharedvt support code for arm
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.
14 #include <mono/metadata/abi-details.h>
15 #include <mono/metadata/appdomain.h>
16 #include <mono/metadata/marshal.h>
17 #include <mono/metadata/tabledefs.h>
18 #include <mono/metadata/profiler-private.h>
19 #include <mono/arch/arm/arm-codegen.h>
20 #include <mono/arch/arm/arm-vfp-codegen.h>
25 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
27 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
34 mono_arch_gsharedvt_sig_supported (MonoMethodSignature *sig)
37 if (sig->ret && is_variable_size (sig->ret))
44 add_to_map (GPtrArray *map, int src, int dst)
46 g_ptr_array_add (map, GUINT_TO_POINTER (src));
47 g_ptr_array_add (map, GUINT_TO_POINTER (dst));
57 map_stack_slot (int slot)
63 get_arg_slots (ArgInfo *ainfo, int **out_slots)
65 int sreg = ainfo->reg;
66 int sslot = ainfo->offset / 4;
70 switch (ainfo->storage) {
73 src = g_malloc (nsrc * sizeof (int));
74 src [0] = map_reg (sreg);
78 src = g_malloc (nsrc * sizeof (int));
79 src [0] = map_reg (sreg);
80 src [1] = map_reg (sreg + 1);
82 case RegTypeStructByVal:
83 nsrc = ainfo->struct_size / 4;
84 src = g_malloc (nsrc * sizeof (int));
85 g_assert (ainfo->size <= nsrc);
86 for (i = 0; i < ainfo->size; ++i)
87 src [i] = map_reg (sreg + i);
88 for (i = ainfo->size; i < nsrc; ++i)
89 src [i] = map_stack_slot (sslot + (i - ainfo->size));
92 nsrc = ainfo->size / 4;
93 src = g_malloc (nsrc * sizeof (int));
94 for (i = 0; i < nsrc; ++i)
95 src [i] = map_stack_slot (sslot + i);
99 src = g_malloc (nsrc * sizeof (int));
100 src [0] = map_reg (ARMREG_R3);
101 src [1] = map_stack_slot (sslot);
104 g_assert_not_reached ();
113 * mono_arch_get_gsharedvt_call_info:
115 * See mini-x86.c for documentation.
118 mono_arch_get_gsharedvt_call_info (gpointer addr, MonoMethodSignature *normal_sig, MonoMethodSignature *gsharedvt_sig, gboolean gsharedvt_in, gint32 vcall_offset, gboolean calli)
120 GSharedVtCallInfo *info;
121 CallInfo *caller_cinfo, *callee_cinfo;
122 MonoMethodSignature *caller_sig, *callee_sig;
124 gboolean var_ret = FALSE;
125 gboolean have_fregs = FALSE;
126 CallInfo *cinfo, *gcinfo;
127 MonoMethodSignature *sig, *gsig;
131 caller_sig = normal_sig;
132 callee_sig = gsharedvt_sig;
133 caller_cinfo = mono_arch_get_call_info (NULL, caller_sig);
134 callee_cinfo = mono_arch_get_call_info (NULL, callee_sig);
136 callee_sig = normal_sig;
137 callee_cinfo = mono_arch_get_call_info (NULL, callee_sig);
138 caller_sig = gsharedvt_sig;
139 caller_cinfo = mono_arch_get_call_info (NULL, caller_sig);
143 * If GSHAREDVT_IN is true, this means we are transitioning from normal to gsharedvt code. The caller uses the
144 * normal call signature, while the callee uses the gsharedvt signature.
145 * If GSHAREDVT_IN is false, its the other way around.
148 /* sig/cinfo describes the normal call, while gsig/gcinfo describes the gsharedvt call */
152 cinfo = caller_cinfo;
153 gcinfo = callee_cinfo;
157 cinfo = callee_cinfo;
158 gcinfo = caller_cinfo;
161 if (gcinfo->ret.storage == RegTypeStructByAddr && gsig->ret && mini_is_gsharedvt_type (gsig->ret)) {
163 * The return type is gsharedvt
169 * The stack looks like this:
174 * We have to map the stack slots in <arguments> to the stack slots in <call area>.
175 * The argument registers are mapped to slot 0..3, stack slot 0 is mapped to slot 4, etc.
177 map = g_ptr_array_new ();
179 if (cinfo->ret.storage == RegTypeStructByAddr) {
182 * This handles the case when the method returns a normal vtype, and when it returns a type arg, and its instantiated
185 g_assert (caller_cinfo->ret.storage == RegTypeStructByAddr);
186 g_assert (callee_cinfo->ret.storage == RegTypeStructByAddr);
187 add_to_map (map, map_reg (caller_cinfo->ret.reg), map_reg (callee_cinfo->ret.reg));
190 for (aindex = 0; aindex < cinfo->nargs; ++aindex) {
191 ArgInfo *ainfo = &caller_cinfo->args [aindex];
192 ArgInfo *ainfo2 = &callee_cinfo->args [aindex];
193 int *src = NULL, *dst = NULL;
194 int nsrc, ndst, nslots, src_slot, arg_marshal;
196 if (ainfo->storage == RegTypeFP || ainfo2->storage == RegTypeFP) {
202 * The src descriptor looks like this:
204 * - 12 bits number of slots
205 * - 8 bits marshal type (GSHAREDVT_ARG_...)
208 arg_marshal = GSHAREDVT_ARG_NONE;
210 if (ainfo->storage == RegTypeGSharedVtInReg || ainfo->storage == RegTypeGSharedVtOnStack) {
211 /* Pass the value whose address is received in a reg by value */
212 g_assert (ainfo2->storage != RegTypeGSharedVtInReg);
213 ndst = get_arg_slots (ainfo2, &dst);
215 src = g_new0 (int, 1);
216 if (ainfo->storage == RegTypeGSharedVtInReg)
217 src_slot = map_reg (ainfo->reg);
219 src_slot = map_stack_slot (ainfo->offset / 4);
220 g_assert (ndst < 256);
221 g_assert (src_slot < 256);
222 src [0] = (ndst << 8) | src_slot;
224 if (ainfo2->storage == RegTypeGeneral && ainfo2->size != 0 && ainfo2->size != 4) {
225 /* Have to load less than 4 bytes */
226 // FIXME: Signed types
227 switch (ainfo2->size) {
229 arg_marshal = GSHAREDVT_ARG_BYREF_TO_BYVAL_U1;
232 arg_marshal = GSHAREDVT_ARG_BYREF_TO_BYVAL_U2;
235 g_assert_not_reached ();
239 arg_marshal = GSHAREDVT_ARG_BYREF_TO_BYVAL;
242 nsrc = get_arg_slots (ainfo, &src);
244 if (ainfo2->storage == RegTypeGSharedVtInReg) {
245 /* Pass the address of the first src slot in a reg */
246 arg_marshal = GSHAREDVT_ARG_BYVAL_TO_BYREF;
248 dst = g_new0 (int, 1);
249 dst [0] = map_reg (ainfo2->reg);
250 } else if (ainfo2->storage == RegTypeGSharedVtOnStack) {
251 /* Pass the address of the first src slot in a stack slot */
252 arg_marshal = GSHAREDVT_ARG_BYVAL_TO_BYREF;
254 dst = g_new0 (int, 1);
255 dst [0] = map_stack_slot (ainfo2->offset / 4);
257 ndst = get_arg_slots (ainfo2, &dst);
260 src [0] |= (arg_marshal << 24);
261 nslots = MIN (nsrc, ndst);
263 for (i = 0; i < nslots; ++i)
264 add_to_map (map, src [i], dst [i]);
270 info = mono_domain_alloc0 (mono_domain_get (), sizeof (GSharedVtCallInfo) + (map->len * sizeof (int)));
272 info->stack_usage = callee_cinfo->stack_usage;
273 info->ret_marshal = GSHAREDVT_RET_NONE;
274 info->gsharedvt_in = gsharedvt_in ? 1 : 0;
275 info->vret_slot = -1;
278 g_assert (gcinfo->ret.storage == RegTypeStructByAddr);
279 info->vret_arg_reg = gcinfo->ret.reg;
281 info->vret_arg_reg = -1;
283 info->vcall_offset = vcall_offset;
284 info->map_count = map->len / 2;
285 for (i = 0; i < map->len; ++i)
286 info->map [i] = GPOINTER_TO_UINT (g_ptr_array_index (map, i));
287 g_ptr_array_free (map, TRUE);
289 /* Compute return value marshalling */
291 switch (cinfo->ret.storage) {
293 if (gsharedvt_in && !sig->ret->byref && sig->ret->type == MONO_TYPE_I1)
294 info->ret_marshal = GSHAREDVT_RET_I1;
295 else if (gsharedvt_in && !sig->ret->byref && (sig->ret->type == MONO_TYPE_U1 || sig->ret->type == MONO_TYPE_BOOLEAN))
296 info->ret_marshal = GSHAREDVT_RET_U1;
297 else if (gsharedvt_in && !sig->ret->byref && sig->ret->type == MONO_TYPE_I2)
298 info->ret_marshal = GSHAREDVT_RET_I2;
299 else if (gsharedvt_in && !sig->ret->byref && (sig->ret->type == MONO_TYPE_U2 || sig->ret->type == MONO_TYPE_CHAR))
300 info->ret_marshal = GSHAREDVT_RET_U2;
302 info->ret_marshal = GSHAREDVT_RET_IREG;
304 case RegTypeIRegPair:
305 info->ret_marshal = GSHAREDVT_RET_IREGS;
308 if (mono_arm_is_hard_float ()) {
309 if (cinfo->ret.size == 4)
310 info->ret_marshal = GSHAREDVT_RET_VFP_R4;
312 info->ret_marshal = GSHAREDVT_RET_VFP_R8;
314 if (cinfo->ret.size == 4)
315 info->ret_marshal = GSHAREDVT_RET_IREG;
317 info->ret_marshal = GSHAREDVT_RET_IREGS;
320 case RegTypeStructByAddr:
321 info->ret_marshal = GSHAREDVT_RET_NONE;
324 g_assert_not_reached ();
328 if (gsharedvt_in && var_ret && caller_cinfo->ret.storage != RegTypeStructByAddr) {
329 /* Allocate stack space for the return value */
330 info->vret_slot = map_stack_slot (info->stack_usage / sizeof (gpointer));
331 info->stack_usage += mono_type_stack_size_internal (normal_sig->ret, NULL, FALSE) + sizeof (gpointer);
334 info->stack_usage = ALIGN_TO (info->stack_usage, MONO_ARCH_FRAME_ALIGNMENT);
335 info->caller_cinfo = caller_cinfo;
336 info->callee_cinfo = callee_cinfo;
337 info->have_fregs = have_fregs;