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