Merge pull request #2982 from ludovic-henry/fix-cross-monocontext
[mono.git] / mono / mini / mini-arm-gsharedvt.c
1 /*
2  * mini-arm-gsharedvt.c: gsharedvt support code for arm
3  *
4  * Authors:
5  *   Zoltan Varga <vargaz@gmail.com>
6  *
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>
12
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>
20
21 #include "mini.h"
22 #include "mini-arm.h"
23
24 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
25
26 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
27
28 /*
29  * GSHAREDVT
30  */
31
32 gboolean
33 mono_arch_gsharedvt_sig_supported (MonoMethodSignature *sig)
34 {
35         /*
36         if (sig->ret && is_variable_size (sig->ret))
37                 return FALSE;
38         */
39         return TRUE;
40 }
41
42 static inline void
43 add_to_map (GPtrArray *map, int src, int dst)
44 {
45         g_ptr_array_add (map, GUINT_TO_POINTER (src));
46         g_ptr_array_add (map, GUINT_TO_POINTER (dst));
47 }
48
49 static inline int
50 map_reg (int reg)
51 {
52         return reg;
53 }
54
55 static inline int
56 map_stack_slot (int slot)
57 {
58         return slot + 4;
59 }
60
61 static int
62 get_arg_slots (ArgInfo *ainfo, int **out_slots)
63 {
64         int sreg = ainfo->reg;
65         int sslot = ainfo->offset / 4;
66         int *src = NULL;
67         int i, nsrc;
68
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;
105         }
106
107         *out_slots = src;
108         return nsrc;
109 }
110
111 /*
112  * mono_arch_get_gsharedvt_call_info:
113  *
114  *   See mini-x86.c for documentation.
115  */
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)
118 {
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;
128
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);
139         }
140
141         /*
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.
145          */
146
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;
158         }
159
160         if (gcinfo->ret.storage == RegTypeStructByAddr && gsig->ret && mini_is_gsharedvt_type (gsig->ret)) {
161                 /*
162                  * The return type is gsharedvt
163                  */
164                 var_ret = TRUE;
165         }
166
167         /*
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.
175          */
176         map = g_ptr_array_new ();
177
178         if (cinfo->ret.storage == RegTypeStructByAddr) {
179                 /*
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.
183                  */
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));
187         }
188
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;
194
195                 if (ainfo->storage == RegTypeFP || ainfo2->storage == RegTypeFP) {
196                         have_fregs = TRUE;
197                         continue;
198                 }
199
200                 /*
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_...)
205                  */
206
207                 arg_marshal = GSHAREDVT_ARG_NONE;
208
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;
222
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;
236                                 }
237                         } else {
238                                 arg_marshal = GSHAREDVT_ARG_BYREF_TO_BYVAL;
239                         }
240                 } else {
241                         nsrc = get_arg_slots (ainfo, &src);
242                 }
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);
257                 }
258                 if (nsrc)
259                         src [0] |= (arg_marshal << 16);
260                 nslots = MIN (nsrc, ndst);
261
262                 for (i = 0; i < nslots; ++i)
263                         add_to_map (map, src [i], dst [i]);
264
265                 g_free (src);
266                 g_free (dst);
267         }
268
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;
281         }
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);
287
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;
317                         }
318                         break;
319                 case RegTypeStructByAddr:
320                         info->ret_marshal = GSHAREDVT_RET_NONE;
321                         break;
322                 default:
323                         g_assert_not_reached ();
324                 }
325         }
326
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);
331         }
332
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;
337
338         return info;
339 }
340
341 #endif