Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / mini / mini-arm64-gsharedvt.c
1 /**
2  * \file
3  * gsharedvt support code for arm64
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 "mini.h"
12 #include "mini-arm64.h"
13 #include "mini-arm64-gsharedvt.h"
14
15 /*
16  * GSHAREDVT
17  */
18 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
19
20 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
21
22 void
23 mono_arm_gsharedvt_init (void)
24 {
25         mono_aot_register_jit_icall ("mono_arm_start_gsharedvt_call", mono_arm_start_gsharedvt_call);
26 }
27
28 gboolean
29 mono_arch_gsharedvt_sig_supported (MonoMethodSignature *sig)
30 {
31         /*
32         if (sig->ret && is_variable_size (sig->ret))
33                 return FALSE;
34         */
35         return TRUE;
36 }
37
38 static inline void
39 add_to_map (GPtrArray *map, int src, int dst)
40 {
41         g_ptr_array_add (map, GUINT_TO_POINTER (src));
42         g_ptr_array_add (map, GUINT_TO_POINTER (dst));
43 }
44
45 /*
46  * Slot mapping:
47  * 0..8  - r0..r8
48  * 9..16 - d0..d7
49  * 17..  - stack slots
50  */
51
52 static inline int
53 map_reg (int reg)
54 {
55         return reg;
56 }
57
58 static inline int
59 map_freg (int reg)
60 {
61         return reg + NUM_GSHAREDVT_ARG_GREGS;
62 }
63
64 static inline int
65 map_stack_slot (int slot)
66 {
67         return slot + NUM_GSHAREDVT_ARG_GREGS + NUM_GSHAREDVT_ARG_FREGS;
68 }
69
70 static int
71 get_arg_slots (ArgInfo *ainfo, int **out_slots)
72 {
73         int sreg = ainfo->reg;
74         int sslot = ainfo->offset / 8;
75         int *src = NULL;
76         int i, nsrc;
77
78         switch (ainfo->storage) {
79         case ArgInIReg:
80         case ArgVtypeByRef:
81                 nsrc = 1;
82                 src = g_malloc (nsrc * sizeof (int));
83                 src [0] = map_reg (sreg);
84                 break;
85         case ArgVtypeByRefOnStack:
86                 nsrc = 1;
87                 src = g_malloc (nsrc * sizeof (int));
88                 src [0] = map_stack_slot (sslot);
89                 break;
90         case ArgInFReg:
91         case ArgInFRegR4:
92                 nsrc = 1;
93                 src = g_malloc (nsrc * sizeof (int));
94                 src [0] = map_freg (sreg);
95                 break;
96         case ArgHFA:
97                 nsrc = ainfo->nregs;
98                 src = g_malloc (nsrc * sizeof (int));
99                 for (i = 0; i < ainfo->nregs; ++i)
100                         src [i] = map_freg (sreg + i);
101                 break;
102         case ArgVtypeInIRegs:
103                 nsrc = ainfo->nregs;
104                 src = g_malloc (nsrc * sizeof (int));
105                 for (i = 0; i < ainfo->nregs; ++i)
106                         src [i] = map_reg (sreg + i);
107                 break;
108         case ArgOnStack:
109                 nsrc = 1;
110                 src = g_malloc (nsrc * sizeof (int));
111                 src [0] = map_stack_slot (sslot);
112                 break;
113         case ArgVtypeOnStack:
114                 nsrc = ainfo->size / 8;
115                 src = g_malloc (nsrc * sizeof (int));
116                 for (i = 0; i < nsrc; ++i)
117                         src [i] = map_stack_slot (sslot + i);
118                 break;
119         default:
120                 NOT_IMPLEMENTED;
121                 break;
122         }
123
124         *out_slots = src;
125         return nsrc;
126 }
127
128 /*
129  * mono_arch_get_gsharedvt_call_info:
130  *
131  *   See mini-x86.c for documentation.
132  */
133 gpointer
134 mono_arch_get_gsharedvt_call_info (gpointer addr, MonoMethodSignature *normal_sig, MonoMethodSignature *gsharedvt_sig, gboolean gsharedvt_in, gint32 vcall_offset, gboolean calli)
135 {
136         GSharedVtCallInfo *info;
137         CallInfo *caller_cinfo, *callee_cinfo;
138         MonoMethodSignature *caller_sig, *callee_sig;
139         int aindex, i;
140         gboolean var_ret = FALSE;
141         CallInfo *cinfo, *gcinfo;
142         MonoMethodSignature *sig, *gsig;
143         GPtrArray *map;
144
145         if (gsharedvt_in) {
146                 caller_sig = normal_sig;
147                 callee_sig = gsharedvt_sig;
148                 caller_cinfo = mono_arch_get_call_info (NULL, caller_sig);
149                 callee_cinfo = mono_arch_get_call_info (NULL, callee_sig);
150         } else {
151                 callee_sig = normal_sig;
152                 caller_sig = gsharedvt_sig;
153                 callee_cinfo = mono_arch_get_call_info (NULL, callee_sig);
154                 caller_cinfo = mono_arch_get_call_info (NULL, caller_sig);
155         }
156
157         /*
158          * If GSHAREDVT_IN is true, this means we are transitioning from normal to gsharedvt code. The caller uses the
159          * normal call signature, while the callee uses the gsharedvt signature.
160          * If GSHAREDVT_IN is false, its the other way around.
161          */
162
163         /* sig/cinfo describes the normal call, while gsig/gcinfo describes the gsharedvt call */
164         if (gsharedvt_in) {
165                 sig = caller_sig;
166                 gsig = callee_sig;
167                 cinfo = caller_cinfo;
168                 gcinfo = callee_cinfo;
169         } else {
170                 sig = callee_sig;
171                 gsig = caller_sig;
172                 cinfo = callee_cinfo;
173                 gcinfo = caller_cinfo;
174         }
175
176         if (gcinfo->ret.gsharedvt) {
177                 /*
178                  * The return type is gsharedvt
179                  */
180                 var_ret = TRUE;
181         }
182
183         /*
184          * The stack looks like this:
185          * <arguments>
186          * <trampoline frame>
187          * <call area>
188          * We have to map the stack slots in <arguments> to the stack slots in <call area>.
189          */
190         map = g_ptr_array_new ();
191
192         for (aindex = 0; aindex < cinfo->nargs; ++aindex) {
193                 ArgInfo *ainfo = &caller_cinfo->args [aindex];
194                 ArgInfo *ainfo2 = &callee_cinfo->args [aindex];
195                 int *src = NULL, *dst = NULL;
196                 int nsrc, ndst, nslots, src_slot, arg_marshal;
197
198                 /*
199                  * The src descriptor looks like this:
200                  * - 6 bits src slot
201                  * - 12 bits number of slots
202                  * - 4 bits marshal type (GSHAREDVT_ARG_...)
203                  * - 4 bits size/sign descriptor (GSHAREDVT_ARG_SIZE)
204                  * - 4 bits offset inside stack slots
205                  */
206                 arg_marshal = GSHAREDVT_ARG_NONE;
207
208                 if (ainfo->gsharedvt) {
209                         /* Pass the value whose address is received in a reg by value */
210                         g_assert (!ainfo2->gsharedvt);
211                         ndst = get_arg_slots (ainfo2, &dst);
212                         nsrc = 1;
213                         src = g_new0 (int, 1);
214                         if (ainfo->storage == ArgVtypeByRef)
215                                 src_slot = map_reg (ainfo->reg);
216                         else
217                                 src_slot = map_stack_slot (ainfo->offset / 8);
218                         g_assert (ndst < 256);
219                         g_assert (src_slot < 64);
220                         src [0] = (ndst << 6) | src_slot;
221                         if (ainfo2->storage == ArgHFA && ainfo2->esize == 4)
222                                 arg_marshal = GSHAREDVT_ARG_BYREF_TO_BYVAL_HFAR4;
223                         else if (ainfo2->storage == ArgVtypeByRef || ainfo2->storage == ArgVtypeByRefOnStack)
224                                 arg_marshal = GSHAREDVT_ARG_BYREF_TO_BYREF;
225                         else
226                                 arg_marshal = GSHAREDVT_ARG_BYREF_TO_BYVAL;
227                 } else {
228                         nsrc = get_arg_slots (ainfo, &src);
229                 }
230                 if (ainfo2->storage == ArgVtypeByRef && ainfo2->gsharedvt) {
231                         /* Pass the address of the first src slot in a reg */
232                         if (ainfo->storage != ArgVtypeByRef) {
233                                 if (ainfo->storage == ArgHFA && ainfo->esize == 4) {
234                                         arg_marshal = GSHAREDVT_ARG_BYVAL_TO_BYREF_HFAR4;
235                                         g_assert (src [0] < 64);
236                                         g_assert (nsrc < 256);
237                                         src [0] |= (nsrc << 6);
238                                 } else {
239                                         arg_marshal = GSHAREDVT_ARG_BYVAL_TO_BYREF;
240                                 }
241                         }
242                         ndst = 1;
243                         dst = g_new0 (int, 1);
244                         dst [0] = map_reg (ainfo2->reg);
245                 } else if (ainfo2->storage == ArgVtypeByRefOnStack && ainfo2->gsharedvt) {
246                         /* Pass the address of the first src slot in a stack slot */
247                         if (ainfo->storage != ArgVtypeByRef)
248                                 arg_marshal = GSHAREDVT_ARG_BYVAL_TO_BYREF;
249                         ndst = 1;
250                         dst = g_new0 (int, 1);
251                         dst [0] = map_stack_slot (ainfo2->offset / 8);
252                 } else {
253                         ndst = get_arg_slots (ainfo2, &dst);
254                 }
255                 if (nsrc)
256                         src [0] |= (arg_marshal << 18);
257                 if (ainfo->storage == ArgOnStack && ainfo->slot_size != 8) {
258                         GSharedVtArgSize arg_size = GSHAREDVT_ARG_SIZE_NONE;
259
260                         /*
261                          * On IOS, stack arguments smaller than 8 bytes can
262                          * share a stack slot. Encode this information into
263                          * the descriptor.
264                          */
265                         switch (ainfo->slot_size) {
266                         case 1:
267                                 arg_size = ainfo->sign ? GSHAREDVT_ARG_SIZE_I1 : GSHAREDVT_ARG_SIZE_U1;
268                                 break;
269                         case 2:
270                                 arg_size = ainfo->sign ? GSHAREDVT_ARG_SIZE_I2 : GSHAREDVT_ARG_SIZE_U2;
271                                 break;
272                         case 4:
273                                 arg_size = ainfo->sign ? GSHAREDVT_ARG_SIZE_I4 : GSHAREDVT_ARG_SIZE_U4;
274                                 break;
275                         default:
276                                 NOT_IMPLEMENTED;
277                                 break;
278                         }
279                         /* Encode the size/sign */
280                         src [0] |= (arg_size << 22);
281                         /* Encode the offset inside the stack slot */
282                         src [0] |= ((ainfo->offset % 8) << 26);
283                         if (ainfo2->storage == ArgOnStack)
284                                 dst [0] |= ((ainfo2->offset % 8) << 26);
285                 } else if (ainfo2->storage == ArgOnStack && ainfo2->slot_size != 8) {
286                         /* The caller passes in an address, need to store it into a stack slot */
287
288                         GSharedVtArgSize arg_size = GSHAREDVT_ARG_SIZE_NONE;
289                         switch (ainfo2->slot_size) {
290                         case 1:
291                                 arg_size = ainfo2->sign ? GSHAREDVT_ARG_SIZE_I1 : GSHAREDVT_ARG_SIZE_U1;
292                                 break;
293                         case 2:
294                                 arg_size = ainfo2->sign ? GSHAREDVT_ARG_SIZE_I2 : GSHAREDVT_ARG_SIZE_U2;
295                                 break;
296                         case 4:
297                                 arg_size = ainfo2->sign ? GSHAREDVT_ARG_SIZE_I4 : GSHAREDVT_ARG_SIZE_U4;
298                                 break;
299                         default:
300                                 NOT_IMPLEMENTED;
301                                 break;
302                         }
303                         /* Encode the size/sign */
304                         src [0] |= (arg_size << 22);
305                         /* Encode the offset inside the stack slot */
306                         dst [0] |= ((ainfo2->offset % 8) << 26);
307                 }
308                 nslots = MIN (nsrc, ndst);
309
310                 for (i = 0; i < nslots; ++i)
311                         add_to_map (map, src [i], dst [i]);
312
313                 g_free (src);
314                 g_free (dst);
315         }
316
317         if (cinfo->ret.storage == ArgVtypeByRef) {
318                 /* Both the caller and the callee pass the vtype ret address in r8 */
319                 g_assert (cinfo->ret.storage == gcinfo->ret.storage);
320                 add_to_map (map, map_reg (ARMREG_R8), map_reg (ARMREG_R8));
321         }
322
323         info = mono_domain_alloc0 (mono_domain_get (), sizeof (GSharedVtCallInfo) + (map->len * sizeof (int)));
324         info->addr = addr;
325         info->stack_usage = callee_cinfo->stack_usage;
326         info->ret_marshal = GSHAREDVT_RET_NONE;
327         info->gsharedvt_in = gsharedvt_in ? 1 : 0;
328         info->vret_slot = -1;
329         info->calli = calli;
330
331         if (var_ret) {
332                 g_assert (gcinfo->ret.gsharedvt);
333                 info->vret_arg_reg = map_reg (ARMREG_R8);
334         } else {
335                 info->vret_arg_reg = -1;
336         }
337
338         info->vcall_offset = vcall_offset;
339         info->map_count = map->len / 2;
340         for (i = 0; i < map->len; ++i)
341                 info->map [i] = GPOINTER_TO_UINT (g_ptr_array_index (map, i));
342         g_ptr_array_free (map, TRUE);
343
344         /* Compute return value marshalling */
345         if (var_ret) {
346                 switch (cinfo->ret.storage) {
347                 case ArgInIReg:
348                         if (!gsharedvt_in || sig->ret->byref) {
349                                 info->ret_marshal = GSHAREDVT_RET_I8;
350                         } else {
351                                 MonoType *rtype = mini_get_underlying_type (sig->ret);
352
353                                 switch (rtype->type) {
354                                 case MONO_TYPE_I1:
355                                         info->ret_marshal = GSHAREDVT_RET_I1;
356                                         break;
357                                 case MONO_TYPE_U1:
358                                         info->ret_marshal = GSHAREDVT_RET_U1;
359                                         break;
360                                 case MONO_TYPE_I2:
361                                         info->ret_marshal = GSHAREDVT_RET_I2;
362                                         break;
363                                 case MONO_TYPE_U2:
364                                         info->ret_marshal = GSHAREDVT_RET_U2;
365                                         break;
366                                 case MONO_TYPE_I4:
367                                         info->ret_marshal = GSHAREDVT_RET_I4;
368                                         break;
369                                 case MONO_TYPE_U4:
370                                         info->ret_marshal = GSHAREDVT_RET_U4;
371                                         break;
372                                 default:
373                                         info->ret_marshal = GSHAREDVT_RET_I8;
374                                         break;
375                                 }
376                         }
377                         break;
378                 case ArgInFReg:
379                         info->ret_marshal = GSHAREDVT_RET_R8;
380                         break;
381                 case ArgInFRegR4:
382                         info->ret_marshal = GSHAREDVT_RET_R4;
383                         break;
384                 case ArgVtypeInIRegs:
385                         info->ret_marshal = GSHAREDVT_RET_IREGS_1 - 1 + cinfo->ret.nregs;
386                         break;
387                 case ArgHFA:
388                         if (cinfo->ret.esize == 4)
389                                 info->ret_marshal = GSHAREDVT_RET_HFAR4_1 - 1 + cinfo->ret.nregs;
390                         else
391                                 info->ret_marshal = GSHAREDVT_RET_HFAR8_1 - 1 + cinfo->ret.nregs;
392                         break;
393                 case ArgVtypeByRef:
394                         /* No conversion needed */
395                         break;
396                 default:
397                         g_assert_not_reached ();
398                 }
399         }
400
401         if (gsharedvt_in && var_ret && cinfo->ret.storage != ArgVtypeByRef) {
402                 /* Allocate stack space for the return value */
403                 info->vret_slot = map_stack_slot (info->stack_usage / sizeof (gpointer));
404                 info->stack_usage += mono_type_stack_size_internal (normal_sig->ret, NULL, FALSE) + sizeof (gpointer);
405         }
406
407         info->stack_usage = ALIGN_TO (info->stack_usage, MONO_ARCH_FRAME_ALIGNMENT);
408
409         return info;
410 }
411
412 #else
413
414 void
415 mono_arm_gsharedvt_init (void)
416 {
417 }
418
419 #endif /* MONO_ARCH_GSHAREDVT_SUPPORTED */