Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / mini / mini-arm-gsharedvt.c
1 /**
2  * \file
3  * gsharedvt support code for arm
4  *
5  * Authors:
6  *   Zoltan Varga <vargaz@gmail.com>
7  *
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.
10  */
11 #include <config.h>
12 #include <glib.h>
13
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>
21
22 #include "mini.h"
23 #include "mini-arm.h"
24
25 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
26
27 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
28
29 /*
30  * GSHAREDVT
31  */
32
33 gboolean
34 mono_arch_gsharedvt_sig_supported (MonoMethodSignature *sig)
35 {
36         /*
37         if (sig->ret && is_variable_size (sig->ret))
38                 return FALSE;
39         */
40         return TRUE;
41 }
42
43 static inline void
44 add_to_map (GPtrArray *map, int src, int dst)
45 {
46         g_ptr_array_add (map, GUINT_TO_POINTER (src));
47         g_ptr_array_add (map, GUINT_TO_POINTER (dst));
48 }
49
50 static inline int
51 map_reg (int reg)
52 {
53         return reg;
54 }
55
56 static inline int
57 map_stack_slot (int slot)
58 {
59         return slot + 4;
60 }
61
62 static int
63 get_arg_slots (ArgInfo *ainfo, int **out_slots)
64 {
65         int sreg = ainfo->reg;
66         int sslot = ainfo->offset / 4;
67         int *src = NULL;
68         int i, nsrc;
69
70         switch (ainfo->storage) {
71         case RegTypeGeneral:
72                 nsrc = 1;
73                 src = g_malloc (nsrc * sizeof (int));
74                 src [0] = map_reg (sreg);
75                 break;
76         case RegTypeIRegPair:
77                 nsrc = 2;
78                 src = g_malloc (nsrc * sizeof (int));
79                 src [0] = map_reg (sreg);
80                 src [1] = map_reg (sreg + 1);
81                 break;
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));
90                 break;
91         case RegTypeBase:
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);
96                 break;
97         case RegTypeBaseGen:
98                 nsrc = 2;
99                 src = g_malloc (nsrc * sizeof (int));
100                 src [0] = map_reg (ARMREG_R3);
101                 src [1] = map_stack_slot (sslot);
102                 break;
103         default:
104                 g_assert_not_reached ();
105                 break;
106         }
107
108         *out_slots = src;
109         return nsrc;
110 }
111
112 /*
113  * mono_arch_get_gsharedvt_call_info:
114  *
115  *   See mini-x86.c for documentation.
116  */
117 gpointer
118 mono_arch_get_gsharedvt_call_info (gpointer addr, MonoMethodSignature *normal_sig, MonoMethodSignature *gsharedvt_sig, gboolean gsharedvt_in, gint32 vcall_offset, gboolean calli)
119 {
120         GSharedVtCallInfo *info;
121         CallInfo *caller_cinfo, *callee_cinfo;
122         MonoMethodSignature *caller_sig, *callee_sig;
123         int aindex, i;
124         gboolean var_ret = FALSE;
125         gboolean have_fregs = FALSE;
126         CallInfo *cinfo, *gcinfo;
127         MonoMethodSignature *sig, *gsig;
128         GPtrArray *map;
129
130         if (gsharedvt_in) {
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);
135         } else {
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);
140         }
141
142         /*
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.
146          */
147
148         /* sig/cinfo describes the normal call, while gsig/gcinfo describes the gsharedvt call */
149         if (gsharedvt_in) {
150                 sig = caller_sig;
151                 gsig = callee_sig;
152                 cinfo = caller_cinfo;
153                 gcinfo = callee_cinfo;
154         } else {
155                 sig = callee_sig;
156                 gsig = caller_sig;
157                 cinfo = callee_cinfo;
158                 gcinfo = caller_cinfo;
159         }
160
161         if (gcinfo->ret.storage == RegTypeStructByAddr && gsig->ret && mini_is_gsharedvt_type (gsig->ret)) {
162                 /*
163                  * The return type is gsharedvt
164                  */
165                 var_ret = TRUE;
166         }
167
168         /*
169          * The stack looks like this:
170          * <arguments>
171          * <ret addr>
172          * <saved ebp>
173          * <call area>
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.
176          */
177         map = g_ptr_array_new ();
178
179         if (cinfo->ret.storage == RegTypeStructByAddr) {
180                 /*
181                  * Map ret arg.
182                  * This handles the case when the method returns a normal vtype, and when it returns a type arg, and its instantiated
183                  * with a vtype.
184                  */
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));
188         }
189
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;
195
196                 if (ainfo->storage == RegTypeFP || ainfo2->storage == RegTypeFP) {
197                         have_fregs = TRUE;
198                         continue;
199                 }
200
201                 /*
202                  * The src descriptor looks like this:
203                  * - 4 bits src slot
204                  * - 12 bits number of slots
205                  * - 8 bits marshal type (GSHAREDVT_ARG_...)
206                  */
207
208                 arg_marshal = GSHAREDVT_ARG_NONE;
209
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);
214                         nsrc = 1;
215                         src = g_new0 (int, 1);
216                         if (ainfo->storage == RegTypeGSharedVtInReg)
217                                 src_slot = map_reg (ainfo->reg);
218                         else
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;
223
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) {
228                                 case 1:
229                                         arg_marshal = GSHAREDVT_ARG_BYREF_TO_BYVAL_U1;
230                                         break;
231                                 case 2:
232                                         arg_marshal = GSHAREDVT_ARG_BYREF_TO_BYVAL_U2;
233                                         break;
234                                 default:
235                                         g_assert_not_reached ();
236                                         break;
237                                 }
238                         } else {
239                                 arg_marshal = GSHAREDVT_ARG_BYREF_TO_BYVAL;
240                         }
241                 } else {
242                         nsrc = get_arg_slots (ainfo, &src);
243                 }
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;
247                         ndst = 1;
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;
253                         ndst = 1;
254                         dst = g_new0 (int, 1);
255                         dst [0] = map_stack_slot (ainfo2->offset / 4);
256                 } else {
257                         ndst = get_arg_slots (ainfo2, &dst);
258                 }
259                 if (nsrc)
260                         src [0] |= (arg_marshal << 24);
261                 nslots = MIN (nsrc, ndst);
262
263                 for (i = 0; i < nslots; ++i)
264                         add_to_map (map, src [i], dst [i]);
265
266                 g_free (src);
267                 g_free (dst);
268         }
269
270         info = mono_domain_alloc0 (mono_domain_get (), sizeof (GSharedVtCallInfo) + (map->len * sizeof (int)));
271         info->addr = addr;
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;
276         info->calli = calli;
277         if (var_ret) {
278                 g_assert (gcinfo->ret.storage == RegTypeStructByAddr);
279                 info->vret_arg_reg = gcinfo->ret.reg;
280         } else {
281                 info->vret_arg_reg = -1;
282         }
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);
288
289         /* Compute return value marshalling */
290         if (var_ret) {
291                 switch (cinfo->ret.storage) {
292                 case RegTypeGeneral:
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;
301                         else
302                                 info->ret_marshal = GSHAREDVT_RET_IREG;
303                         break;
304                 case RegTypeIRegPair:
305                         info->ret_marshal = GSHAREDVT_RET_IREGS;
306                         break;
307                 case RegTypeFP:
308                         if (mono_arm_is_hard_float ()) {
309                                 if (cinfo->ret.size == 4)
310                                         info->ret_marshal = GSHAREDVT_RET_VFP_R4;
311                                 else
312                                         info->ret_marshal = GSHAREDVT_RET_VFP_R8;
313                         } else {
314                                 if (cinfo->ret.size == 4)
315                                         info->ret_marshal = GSHAREDVT_RET_IREG;
316                                 else
317                                         info->ret_marshal = GSHAREDVT_RET_IREGS;
318                         }
319                         break;
320                 case RegTypeStructByAddr:
321                         info->ret_marshal = GSHAREDVT_RET_NONE;
322                         break;
323                 default:
324                         g_assert_not_reached ();
325                 }
326         }
327
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);
332         }
333
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;
338
339         return info;
340 }
341
342 #endif