Merge pull request #3140 from esdrubal/syscall_details
[mono-project.git] / mono / mini / mini-arm-gsharedvt.c
blob2c7f8812a699661bd095a63d227419d1b91f112e
1 /*
2 * mini-arm-gsharedvt.c: gsharedvt support code for arm
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 <config.h>
11 #include <glib.h>
13 #include <mono/metadata/abi-details.h>
14 #include <mono/metadata/appdomain.h>
15 #include <mono/metadata/marshal.h>
16 #include <mono/metadata/tabledefs.h>
17 #include <mono/metadata/profiler-private.h>
18 #include <mono/arch/arm/arm-codegen.h>
19 #include <mono/arch/arm/arm-vfp-codegen.h>
21 #include "mini.h"
22 #include "mini-arm.h"
24 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
26 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
29 * GSHAREDVT
32 gboolean
33 mono_arch_gsharedvt_sig_supported (MonoMethodSignature *sig)
36 if (sig->ret && is_variable_size (sig->ret))
37 return FALSE;
39 return TRUE;
42 static inline void
43 add_to_map (GPtrArray *map, int src, int dst)
45 g_ptr_array_add (map, GUINT_TO_POINTER (src));
46 g_ptr_array_add (map, GUINT_TO_POINTER (dst));
49 static inline int
50 map_reg (int reg)
52 return reg;
55 static inline int
56 map_stack_slot (int slot)
58 return slot + 4;
61 static int
62 get_arg_slots (ArgInfo *ainfo, int **out_slots)
64 int sreg = ainfo->reg;
65 int sslot = ainfo->offset / 4;
66 int *src = NULL;
67 int i, nsrc;
69 switch (ainfo->storage) {
70 case RegTypeGeneral:
71 nsrc = 1;
72 src = g_malloc (nsrc * sizeof (int));
73 src [0] = map_reg (sreg);
74 break;
75 case RegTypeIRegPair:
76 nsrc = 2;
77 src = g_malloc (nsrc * sizeof (int));
78 src [0] = map_reg (sreg);
79 src [1] = map_reg (sreg + 1);
80 break;
81 case RegTypeStructByVal:
82 nsrc = ainfo->struct_size / 4;
83 src = g_malloc (nsrc * sizeof (int));
84 g_assert (ainfo->size <= nsrc);
85 for (i = 0; i < ainfo->size; ++i)
86 src [i] = map_reg (sreg + i);
87 for (i = ainfo->size; i < nsrc; ++i)
88 src [i] = map_stack_slot (sslot + (i - ainfo->size));
89 break;
90 case RegTypeBase:
91 nsrc = ainfo->size / 4;
92 src = g_malloc (nsrc * sizeof (int));
93 for (i = 0; i < nsrc; ++i)
94 src [i] = map_stack_slot (sslot + i);
95 break;
96 case RegTypeBaseGen:
97 nsrc = 2;
98 src = g_malloc (nsrc * sizeof (int));
99 src [0] = map_reg (ARMREG_R3);
100 src [1] = map_stack_slot (sslot);
101 break;
102 default:
103 g_assert_not_reached ();
104 break;
107 *out_slots = src;
108 return nsrc;
112 * mono_arch_get_gsharedvt_call_info:
114 * See mini-x86.c for documentation.
116 gpointer
117 mono_arch_get_gsharedvt_call_info (gpointer addr, MonoMethodSignature *normal_sig, MonoMethodSignature *gsharedvt_sig, gboolean gsharedvt_in, gint32 vcall_offset, gboolean calli)
119 GSharedVtCallInfo *info;
120 CallInfo *caller_cinfo, *callee_cinfo;
121 MonoMethodSignature *caller_sig, *callee_sig;
122 int aindex, i;
123 gboolean var_ret = FALSE;
124 gboolean have_fregs = FALSE;
125 CallInfo *cinfo, *gcinfo;
126 MonoMethodSignature *sig, *gsig;
127 GPtrArray *map;
129 if (gsharedvt_in) {
130 caller_sig = normal_sig;
131 callee_sig = gsharedvt_sig;
132 caller_cinfo = mono_arch_get_call_info (NULL, caller_sig);
133 callee_cinfo = mono_arch_get_call_info (NULL, callee_sig);
134 } else {
135 callee_sig = normal_sig;
136 callee_cinfo = mono_arch_get_call_info (NULL, callee_sig);
137 caller_sig = gsharedvt_sig;
138 caller_cinfo = mono_arch_get_call_info (NULL, caller_sig);
142 * If GSHAREDVT_IN is true, this means we are transitioning from normal to gsharedvt code. The caller uses the
143 * normal call signature, while the callee uses the gsharedvt signature.
144 * If GSHAREDVT_IN is false, its the other way around.
147 /* sig/cinfo describes the normal call, while gsig/gcinfo describes the gsharedvt call */
148 if (gsharedvt_in) {
149 sig = caller_sig;
150 gsig = callee_sig;
151 cinfo = caller_cinfo;
152 gcinfo = callee_cinfo;
153 } else {
154 sig = callee_sig;
155 gsig = caller_sig;
156 cinfo = callee_cinfo;
157 gcinfo = caller_cinfo;
160 if (gcinfo->ret.storage == RegTypeStructByAddr && gsig->ret && mini_is_gsharedvt_type (gsig->ret)) {
162 * The return type is gsharedvt
164 var_ret = TRUE;
168 * The stack looks like this:
169 * <arguments>
170 * <ret addr>
171 * <saved ebp>
172 * <call area>
173 * We have to map the stack slots in <arguments> to the stack slots in <call area>.
174 * The argument registers are mapped to slot 0..3, stack slot 0 is mapped to slot 4, etc.
176 map = g_ptr_array_new ();
178 if (cinfo->ret.storage == RegTypeStructByAddr) {
180 * Map ret arg.
181 * This handles the case when the method returns a normal vtype, and when it returns a type arg, and its instantiated
182 * with a vtype.
184 g_assert (caller_cinfo->ret.storage == RegTypeStructByAddr);
185 g_assert (callee_cinfo->ret.storage == RegTypeStructByAddr);
186 add_to_map (map, map_reg (caller_cinfo->ret.reg), map_reg (callee_cinfo->ret.reg));
189 for (aindex = 0; aindex < cinfo->nargs; ++aindex) {
190 ArgInfo *ainfo = &caller_cinfo->args [aindex];
191 ArgInfo *ainfo2 = &callee_cinfo->args [aindex];
192 int *src = NULL, *dst = NULL;
193 int nsrc, ndst, nslots, src_slot, arg_marshal;
195 if (ainfo->storage == RegTypeFP || ainfo2->storage == RegTypeFP) {
196 have_fregs = TRUE;
197 continue;
201 * The src descriptor looks like this:
202 * - 4 bits src slot
203 * - 12 bits number of slots
204 * - 8 bits marshal type (GSHAREDVT_ARG_...)
207 arg_marshal = GSHAREDVT_ARG_NONE;
209 if (ainfo->storage == RegTypeGSharedVtInReg || ainfo->storage == RegTypeGSharedVtOnStack) {
210 /* Pass the value whose address is received in a reg by value */
211 g_assert (ainfo2->storage != RegTypeGSharedVtInReg);
212 ndst = get_arg_slots (ainfo2, &dst);
213 nsrc = 1;
214 src = g_new0 (int, 1);
215 if (ainfo->storage == RegTypeGSharedVtInReg)
216 src_slot = map_reg (ainfo->reg);
217 else
218 src_slot = map_stack_slot (ainfo->offset / 4);
219 g_assert (ndst < 256);
220 g_assert (src_slot < 16);
221 src [0] = (ndst << 4) | src_slot;
223 if (ainfo2->storage == RegTypeGeneral && ainfo2->size != 0 && ainfo2->size != 4) {
224 /* Have to load less than 4 bytes */
225 // FIXME: Signed types
226 switch (ainfo2->size) {
227 case 1:
228 arg_marshal = GSHAREDVT_ARG_BYREF_TO_BYVAL_U1;
229 break;
230 case 2:
231 arg_marshal = GSHAREDVT_ARG_BYREF_TO_BYVAL_U2;
232 break;
233 default:
234 g_assert_not_reached ();
235 break;
237 } else {
238 arg_marshal = GSHAREDVT_ARG_BYREF_TO_BYVAL;
240 } else {
241 nsrc = get_arg_slots (ainfo, &src);
243 if (ainfo2->storage == RegTypeGSharedVtInReg) {
244 /* Pass the address of the first src slot in a reg */
245 arg_marshal = GSHAREDVT_ARG_BYVAL_TO_BYREF;
246 ndst = 1;
247 dst = g_new0 (int, 1);
248 dst [0] = map_reg (ainfo2->reg);
249 } else if (ainfo2->storage == RegTypeGSharedVtOnStack) {
250 /* Pass the address of the first src slot in a stack slot */
251 arg_marshal = GSHAREDVT_ARG_BYVAL_TO_BYREF;
252 ndst = 1;
253 dst = g_new0 (int, 1);
254 dst [0] = map_stack_slot (ainfo2->offset / 4);
255 } else {
256 ndst = get_arg_slots (ainfo2, &dst);
258 if (nsrc)
259 src [0] |= (arg_marshal << 16);
260 nslots = MIN (nsrc, ndst);
262 for (i = 0; i < nslots; ++i)
263 add_to_map (map, src [i], dst [i]);
265 g_free (src);
266 g_free (dst);
269 info = mono_domain_alloc0 (mono_domain_get (), sizeof (GSharedVtCallInfo) + (map->len * sizeof (int)));
270 info->addr = addr;
271 info->stack_usage = callee_cinfo->stack_usage;
272 info->ret_marshal = GSHAREDVT_RET_NONE;
273 info->gsharedvt_in = gsharedvt_in ? 1 : 0;
274 info->vret_slot = -1;
275 info->calli = calli;
276 if (var_ret) {
277 g_assert (gcinfo->ret.storage == RegTypeStructByAddr);
278 info->vret_arg_reg = gcinfo->ret.reg;
279 } else {
280 info->vret_arg_reg = -1;
282 info->vcall_offset = vcall_offset;
283 info->map_count = map->len / 2;
284 for (i = 0; i < map->len; ++i)
285 info->map [i] = GPOINTER_TO_UINT (g_ptr_array_index (map, i));
286 g_ptr_array_free (map, TRUE);
288 /* Compute return value marshalling */
289 if (var_ret) {
290 switch (cinfo->ret.storage) {
291 case RegTypeGeneral:
292 if (gsharedvt_in && !sig->ret->byref && sig->ret->type == MONO_TYPE_I1)
293 info->ret_marshal = GSHAREDVT_RET_I1;
294 else if (gsharedvt_in && !sig->ret->byref && (sig->ret->type == MONO_TYPE_U1 || sig->ret->type == MONO_TYPE_BOOLEAN))
295 info->ret_marshal = GSHAREDVT_RET_U1;
296 else if (gsharedvt_in && !sig->ret->byref && sig->ret->type == MONO_TYPE_I2)
297 info->ret_marshal = GSHAREDVT_RET_I2;
298 else if (gsharedvt_in && !sig->ret->byref && (sig->ret->type == MONO_TYPE_U2 || sig->ret->type == MONO_TYPE_CHAR))
299 info->ret_marshal = GSHAREDVT_RET_U2;
300 else
301 info->ret_marshal = GSHAREDVT_RET_IREG;
302 break;
303 case RegTypeIRegPair:
304 info->ret_marshal = GSHAREDVT_RET_IREGS;
305 break;
306 case RegTypeFP:
307 if (mono_arm_is_hard_float ()) {
308 if (cinfo->ret.size == 4)
309 info->ret_marshal = GSHAREDVT_RET_VFP_R4;
310 else
311 info->ret_marshal = GSHAREDVT_RET_VFP_R8;
312 } else {
313 if (cinfo->ret.size == 4)
314 info->ret_marshal = GSHAREDVT_RET_IREG;
315 else
316 info->ret_marshal = GSHAREDVT_RET_IREGS;
318 break;
319 case RegTypeStructByAddr:
320 info->ret_marshal = GSHAREDVT_RET_NONE;
321 break;
322 default:
323 g_assert_not_reached ();
327 if (gsharedvt_in && var_ret && caller_cinfo->ret.storage != RegTypeStructByAddr) {
328 /* Allocate stack space for the return value */
329 info->vret_slot = map_stack_slot (info->stack_usage / sizeof (gpointer));
330 info->stack_usage += mono_type_stack_size_internal (normal_sig->ret, NULL, FALSE) + sizeof (gpointer);
333 info->stack_usage = ALIGN_TO (info->stack_usage, MONO_ARCH_FRAME_ALIGNMENT);
334 info->caller_cinfo = caller_cinfo;
335 info->callee_cinfo = callee_cinfo;
336 info->have_fregs = have_fregs;
338 return info;
341 #endif