Merge pull request #2820 from kumpera/license-change-rebased
[mono.git] / mono / mini / mini-amd64-gsharedvt.c
1 /*
2  * mini-amd64-gsharedvt.c: libcorkscrew-based native unwinder
3  *
4  * Authors:
5  *   Zoltan Varga <vargaz@gmail.com>
6  *   Rodrigo Kumpera <kumpera@gmail.com>
7  *   Andi McClure <andi.mcclure@xamarin.com>
8  *
9  * Copyright 2015 Xamarin, Inc (http://www.xamarin.com)
10  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
11  */
12 #include <config.h>
13 #include <glib.h>
14
15 #include <mono/metadata/abi-details.h>
16 #include <mono/metadata/appdomain.h>
17 #include <mono/metadata/marshal.h>
18 #include <mono/metadata/tabledefs.h>
19 #include <mono/metadata/mono-debug-debugger.h>
20 #include <mono/metadata/profiler-private.h>
21 #include <mono/metadata/gc-internals.h>
22 #include <mono/arch/amd64/amd64-codegen.h>
23
24 #include <mono/utils/memcheck.h>
25
26 #include "mini.h"
27 #include "mini-amd64.h"
28 #include "mini-amd64-gsharedvt.h"
29 #include "debugger-agent.h"
30
31 #if defined (MONO_ARCH_GSHAREDVT_SUPPORTED)
32
33 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
34
35 gboolean
36 mono_arch_gsharedvt_sig_supported (MonoMethodSignature *sig)
37 {
38         return FALSE;
39 }
40
41 static const char*
42 storage_name (ArgStorage st)
43 {
44         switch (st) {
45         case ArgInIReg: return "ArgInIReg";
46         case ArgInFloatSSEReg: return "ArgInFloatSSEReg";
47         case ArgInDoubleSSEReg: return "ArgInDoubleSSEReg";
48         case ArgOnStack: return "ArgOnStack";
49         case ArgValuetypeInReg: return "ArgValuetypeInReg";
50         case ArgValuetypeAddrInIReg: return "ArgValuetypeAddrInIReg";
51         case ArgGSharedVtInReg: return "ArgGSharedVtInReg";
52         case ArgGSharedVtOnStack: return "ArgGSharedVtOnStack";
53         case ArgNone: return "ArgNone";
54         default: return "unknown";
55         }
56 }
57
58 static char *
59 arg_info_desc (ArgInfo *info)
60 {
61         GString *str = g_string_new ("");
62
63         g_string_append_printf (str, "offset %d reg %s storage %s nregs %d", info->offset, mono_arch_regname (info->reg), storage_name (info->storage), info->nregs);
64         if (info->storage == ArgValuetypeInReg)
65                 g_string_append_printf (str, " {(%s %s), (%s %s)", 
66                         storage_name (info->pair_storage [0]),
67                         mono_arch_regname (info->pair_regs [0]),
68                         storage_name (info->pair_storage [1]),
69                         mono_arch_regname (info->pair_regs [1]));
70
71         return g_string_free (str, FALSE);
72 }
73
74 static inline void
75 add_to_map (GPtrArray *map, int src, int dst)
76 {
77         g_ptr_array_add (map, GUINT_TO_POINTER (src));
78         g_ptr_array_add (map, GUINT_TO_POINTER (dst));
79 }
80
81 /*
82  * Slot mapping:
83  * 0..5  - rdi, rsi, rdx, rcx, r8, r9
84  * 6..13 - xmm0..xmm7
85  * 14..  - stack slots
86  */
87 static inline int
88 map_reg (int reg)
89 {
90         int i = 0;
91         for (i = 0; i < PARAM_REGS; ++i) {
92                 if (param_regs [i] == reg)
93                         return i;
94         }
95         g_error ("Invalid argument register number %d", reg);
96         return -1;
97 }
98
99 static inline int
100 map_freg (int reg)
101 {
102         return reg + PARAM_REGS;
103 }
104
105 static inline int
106 map_stack_slot (int slot)
107 {
108         return slot + PARAM_REGS + FLOAT_PARAM_REGS;
109 }
110
111 /*
112 Format for the source descriptor:
113
114
115 Format for the destination descriptor:
116         bits 0:15  - source register
117         bits 16:23 - return marshal
118         bits 24:32 - slot count
119 */
120 #define SRC_DESCRIPTOR_MARSHAL_SHIFT 16
121 #define SRC_DESCRIPTOR_MARSHAL_MASK 0x0Ff
122
123 #define SLOT_COUNT_SHIFT 24
124 #define SLOT_COUNT_MASK 0xff
125 #define SLOT_BYTE_SIZE 8
126
127 static int
128 get_arg_slots (ArgInfo *ainfo, int **out_slots, gboolean is_source_argument)
129 {
130         int sreg = ainfo->reg;
131         int sslot = ainfo->offset / 8;
132         int *src = NULL;
133         int i, nsrc;
134
135         switch (ainfo->storage) {
136         case ArgInIReg:
137                 nsrc = 1;
138                 src = g_malloc (nsrc * sizeof (int));
139                 src [0] = map_reg (sreg);
140                 break;
141         case ArgValuetypeInReg:
142                 nsrc = ainfo->nregs;
143                 src = g_malloc (nsrc * sizeof (int));
144                 for (i = 0; i < ainfo->nregs; ++i)
145                         src [i] = map_reg (ainfo->pair_regs [i]);
146                 break;
147         case ArgOnStack:
148                 nsrc = ainfo->arg_size / SLOT_BYTE_SIZE;
149                 src = g_malloc (nsrc * sizeof (int));
150                 // is_source_argument adds 2 because we're skipping over the old BBP and the return address
151                 // XXX this is a very fragile setup as changes in alignment for the caller reg array can cause the magic number be 3
152                 for (i = 0; i < nsrc; ++i)
153                         src [i] = map_stack_slot (sslot + i + (is_source_argument ? 2 : 0));
154                 break;
155         case ArgInDoubleSSEReg:
156         case ArgInFloatSSEReg:
157                 nsrc = 1;
158                 src = g_malloc (nsrc * sizeof (int));
159                 src [0] = map_freg (sreg);
160                 break;
161         default:
162                 NOT_IMPLEMENTED;
163                 break;
164         }
165
166         *out_slots = src;
167         return nsrc;
168 }
169
170 // Once src is known, operate on the dst
171 static void
172 handle_marshal_when_src_gsharedvt (ArgInfo *dst_info, int *arg_marshal, int *arg_slots)
173 {
174         switch (dst_info->storage) {
175                 case ArgInIReg:
176                 case ArgInDoubleSSEReg:
177                 case ArgInFloatSSEReg:
178                         *arg_marshal = GSHAREDVT_ARG_BYREF_TO_BYVAL;
179                         *arg_slots = 1;
180                         break;
181                 case ArgOnStack:
182                         *arg_marshal = GSHAREDVT_ARG_BYREF_TO_BYVAL;
183                         g_assert (dst_info->arg_size % SLOT_BYTE_SIZE == 0); // Assert quadword aligned
184                         *arg_slots = dst_info->arg_size / SLOT_BYTE_SIZE;
185                         break;
186                 case ArgValuetypeInReg:
187                         *arg_marshal = GSHAREDVT_ARG_BYREF_TO_BYVAL;
188                         *arg_slots = dst_info->nregs;
189                         break;
190                 default:
191                         NOT_IMPLEMENTED; // Inappropriate value: if dst and src are gsharedvt at once, we shouldn't be here
192                         break;
193         }
194 }
195
196 // Once dst is known, operate on the src
197 static void
198 handle_marshal_when_dst_gsharedvt (ArgInfo *src_info, int *arg_marshal)
199 {
200         switch (src_info->storage) {
201                 case ArgInIReg:
202                 case ArgInDoubleSSEReg:
203                 case ArgInFloatSSEReg:
204                 case ArgValuetypeInReg:
205                 case ArgOnStack:
206                         *arg_marshal = GSHAREDVT_ARG_BYVAL_TO_BYREF;
207                         break;
208                 default:
209                         NOT_IMPLEMENTED; // See above
210                         break;
211         }
212 }
213
214 static void
215 handle_map_when_gsharedvt_in_reg (ArgInfo *reg_info, int *n, int **map)
216 {
217         *n = 1;
218         *map = g_new0 (int, 1);
219         (*map) [0] = map_reg (reg_info->reg);
220 }
221
222 static void
223 handle_map_when_gsharedvt_on_stack (ArgInfo *reg_info, int *n, int **map, gboolean is_source_argument)
224 {
225         *n = 1;
226         *map = g_new0 (int, 1);
227         int sslot = reg_info->offset / SLOT_BYTE_SIZE;
228         (*map) [0] = map_stack_slot (sslot + (is_source_argument ? 2 : 0)); // see get_arg_slots
229 }
230
231 gpointer
232 mono_arch_get_gsharedvt_call_info (gpointer addr, MonoMethodSignature *normal_sig, MonoMethodSignature *gsharedvt_sig, gboolean gsharedvt_in, gint32 vcall_offset, gboolean calli)
233 {
234         GSharedVtCallInfo *info;
235         CallInfo *caller_cinfo, *callee_cinfo;
236         MonoMethodSignature *caller_sig, *callee_sig;
237         int aindex, i;
238         gboolean var_ret = FALSE;
239         CallInfo *cinfo, *gcinfo;
240         MonoMethodSignature *sig, *gsig;
241         GPtrArray *map;
242
243         if (gsharedvt_in) {
244                 caller_sig = normal_sig;
245                 callee_sig = gsharedvt_sig;
246                 caller_cinfo = mono_arch_get_call_info (NULL, caller_sig);
247                 callee_cinfo = mono_arch_get_call_info (NULL, callee_sig);
248         } else {
249                 callee_sig = normal_sig;
250                 caller_sig = gsharedvt_sig;
251                 callee_cinfo = mono_arch_get_call_info (NULL, callee_sig);
252                 caller_cinfo = mono_arch_get_call_info (NULL, caller_sig);
253         }
254
255         /*
256          * If GSHAREDVT_IN is true, this means we are transitioning from normal to gsharedvt code. The caller uses the
257          * normal call signature, while the callee uses the gsharedvt signature.
258          * If GSHAREDVT_IN is false, its the other way around.
259          */
260
261         /* sig/cinfo describes the normal call, while gsig/gcinfo describes the gsharedvt call */
262         if (gsharedvt_in) {
263                 sig = caller_sig;
264                 gsig = callee_sig;
265                 cinfo = caller_cinfo;
266                 gcinfo = callee_cinfo;
267         } else {
268                 sig = callee_sig;
269                 gsig = caller_sig;
270                 cinfo = callee_cinfo;
271                 gcinfo = caller_cinfo;
272         }
273
274         DEBUG_AMD64_GSHAREDVT_PRINT ("source sig: (%s) return (%s)\n", mono_signature_get_desc (caller_sig, FALSE), mono_type_full_name (mono_signature_get_return_type (caller_sig))); // Leak
275         DEBUG_AMD64_GSHAREDVT_PRINT ("dest sig: (%s) return (%s)\n", mono_signature_get_desc (callee_sig, FALSE), mono_type_full_name (mono_signature_get_return_type (callee_sig)));
276
277         if (gcinfo->ret.is_gsharedvt_return_value) {
278                 /*
279                  * The return type is gsharedvt
280                  */
281                 var_ret = TRUE;
282         }
283
284         /*
285          * The stack looks like this:
286          * <arguments>
287          * <trampoline frame>
288          * <call area>
289          * We have to map the stack slots in <arguments> to the stack slots in <call area>.
290          */
291         map = g_ptr_array_new ();
292
293         for (aindex = 0; aindex < cinfo->nargs; ++aindex) {
294                 ArgInfo *src_info = &caller_cinfo->args [aindex];
295                 ArgInfo *dst_info = &callee_cinfo->args [aindex];
296                 int *src = NULL, *dst = NULL;
297                 int nsrc = -1, ndst = -1, nslots = 0;
298
299                 int arg_marshal = GSHAREDVT_ARG_NONE;
300                 int arg_slots = 0; // Size in quadwords
301                 DEBUG_AMD64_GSHAREDVT_PRINT ("-- arg %d in (%s) out (%s)\n", aindex, arg_info_desc (src_info), arg_info_desc (dst_info));
302
303                 switch (src_info->storage) {
304                 case ArgInIReg:
305                 case ArgInDoubleSSEReg:
306                 case ArgInFloatSSEReg:
307                 case ArgValuetypeInReg:
308                 case ArgOnStack:
309                         nsrc = get_arg_slots (src_info, &src, TRUE);
310                         break;
311                 case ArgGSharedVtInReg:
312                         handle_marshal_when_src_gsharedvt (dst_info, &arg_marshal, &arg_slots);
313                         handle_map_when_gsharedvt_in_reg (src_info, &nsrc, &src);
314                         break;
315                 case ArgGSharedVtOnStack:
316                         handle_marshal_when_src_gsharedvt (dst_info, &arg_marshal, &arg_slots);
317                         handle_map_when_gsharedvt_on_stack (src_info, &nsrc, &src, TRUE);
318                         break;
319                 default:
320                         g_error ("Gsharedvt can't handle source arg type %d", (int)src_info->storage); // Inappropriate value: ArgValuetypeAddrInIReg is for returns only
321                 }
322
323                 switch (dst_info->storage) {
324                 case ArgInIReg:
325                 case ArgInDoubleSSEReg:
326                 case ArgInFloatSSEReg:
327                 case ArgOnStack:
328                 case ArgValuetypeInReg:
329                         ndst = get_arg_slots (dst_info, &dst, FALSE);
330                         break;
331                 case ArgGSharedVtInReg:
332                         handle_marshal_when_dst_gsharedvt (src_info, &arg_marshal);
333                         handle_map_when_gsharedvt_in_reg (dst_info, &ndst, &dst);
334                         break;
335                 case ArgGSharedVtOnStack:
336                         handle_marshal_when_dst_gsharedvt (src_info, &arg_marshal);
337                         handle_map_when_gsharedvt_on_stack (dst_info, &ndst, &dst, FALSE);
338                         break;
339                 default:
340                         g_error ("Gsharedvt can't handle dest arg type %d", (int)dst_info->storage); // See above
341                 }
342                 if (nsrc)
343                         src [0] |= (arg_marshal << SRC_DESCRIPTOR_MARSHAL_SHIFT) | (arg_slots << SLOT_COUNT_SHIFT);
344
345                 /* Merge and add to the global list*/
346                 nslots = MIN (nsrc, ndst);
347                 DEBUG_AMD64_GSHAREDVT_PRINT ("nsrc %d ndst %d\n", nsrc, ndst);
348
349                 for (i = 0; i < nslots; ++i)
350                         add_to_map (map, src [i], dst [i]);
351
352                 g_free (src);
353                 g_free (dst);
354         }
355
356         DEBUG_AMD64_GSHAREDVT_PRINT ("-- return in (%s) out (%s) var_ret %d\n", arg_info_desc (&caller_cinfo->ret),  arg_info_desc (&callee_cinfo->ret), var_ret);
357
358         if (cinfo->ret.storage == ArgValuetypeAddrInIReg) {
359                 /* Both the caller and the callee pass the vtype ret address in r8 */
360                 g_assert (cinfo->ret.storage == gcinfo->ret.storage);
361                 add_to_map (map, map_reg (cinfo->ret.reg), map_reg (cinfo->ret.reg));
362         }
363
364         info = mono_domain_alloc0 (mono_domain_get (), sizeof (GSharedVtCallInfo) + (map->len * sizeof (int)));
365         info->addr = addr;
366         info->stack_usage = callee_cinfo->stack_usage;
367         info->ret_marshal = GSHAREDVT_RET_NONE;
368         info->gsharedvt_in = gsharedvt_in ? 1 : 0;
369         info->vret_slot = -1;
370         info->calli = calli;
371
372         if (var_ret) {
373                 g_assert (gcinfo->ret.is_gsharedvt_return_value);
374                 info->vret_arg_reg = map_reg (gcinfo->ret.reg);
375                 DEBUG_AMD64_GSHAREDVT_PRINT ("mapping vreg_arg_reg to %d in reg %s\n", info->vret_arg_reg, mono_arch_regname (gcinfo->ret.reg));
376         } else {
377                 info->vret_arg_reg = -1;
378         }
379
380 #ifdef DEBUG_AMD64_GSHAREDVT
381         printf ("final map:\n");
382         for (i = 0; i < map->len; i += 2) {
383                 printf ("\t[%d] src %x dst %x\n ", 
384                         i / 2,
385                         GPOINTER_TO_UINT (g_ptr_array_index (map, i)),
386                         GPOINTER_TO_UINT (g_ptr_array_index (map, i + 1)));
387         }
388 #endif
389
390         info->vcall_offset = vcall_offset;
391         info->map_count = map->len / 2;
392         for (i = 0; i < map->len; ++i)
393                 info->map [i] = GPOINTER_TO_UINT (g_ptr_array_index (map, i));
394         g_ptr_array_free (map, TRUE);
395
396         /* Compute return value marshalling */
397         if (var_ret) {
398                 /* Compute return value marshalling */
399                 switch (cinfo->ret.storage) {
400                 case ArgInIReg:
401                         if (!gsharedvt_in || sig->ret->byref) {
402                                 info->ret_marshal = GSHAREDVT_RET_IREGS_1;
403                         } else {
404                                 MonoType *ret = sig->ret;
405
406                                 // Unwrap enums
407                                 if (ret->type == MONO_TYPE_VALUETYPE)
408                                         ret = mini_type_get_underlying_type (ret);
409
410                                 switch (ret->type) {
411                                 case MONO_TYPE_I1:
412                                         info->ret_marshal = GSHAREDVT_RET_I1;
413                                         break;
414                                 case MONO_TYPE_BOOLEAN:
415                                 case MONO_TYPE_U1:
416                                         info->ret_marshal = GSHAREDVT_RET_U1;
417                                         break;
418                                 case MONO_TYPE_I2:
419                                         info->ret_marshal = GSHAREDVT_RET_I2;
420                                         break;
421                                 case MONO_TYPE_CHAR:
422                                 case MONO_TYPE_U2:
423                                         info->ret_marshal = GSHAREDVT_RET_U2;
424                                         break;
425                                 case MONO_TYPE_I4:
426                                         info->ret_marshal = GSHAREDVT_RET_I4;
427                                         break;
428                                 case MONO_TYPE_U4:
429                                         info->ret_marshal = GSHAREDVT_RET_U4;
430                                         break;
431                                 case MONO_TYPE_I:
432                                 case MONO_TYPE_U:
433                                 case MONO_TYPE_PTR:
434                                 case MONO_TYPE_FNPTR:
435                                 case MONO_TYPE_CLASS:
436                                 case MONO_TYPE_OBJECT:
437                                 case MONO_TYPE_SZARRAY:
438                                 case MONO_TYPE_ARRAY:
439                                 case MONO_TYPE_STRING:
440                                 case MONO_TYPE_U8:
441                                 case MONO_TYPE_I8:
442                                         info->ret_marshal = GSHAREDVT_RET_I8;
443                                         break;
444
445                                 default:
446                                         g_error ("Gsharedvt can't handle dst type [%d]", (int)sig->ret->type);
447                                 }
448                         }
449                         break;
450                 case ArgValuetypeInReg:
451                         info->ret_marshal = GSHAREDVT_RET_IREGS_1 - 1 + cinfo->ret.nregs;
452                         g_assert (cinfo->ret.nregs == 1); // ABI supports 2-register return but we do not implement this.
453                         break;
454                 case ArgInDoubleSSEReg:
455                 case ArgInFloatSSEReg:
456                         info->ret_marshal = GSHAREDVT_RET_R8;
457                         break;
458                 case ArgValuetypeAddrInIReg:
459                         break;
460                 default:
461                         g_error ("Can't marshal return of storage [%d] %s", (int)cinfo->ret.storage, storage_name (cinfo->ret.storage));
462                 }
463
464                 if (gsharedvt_in && cinfo->ret.storage != ArgValuetypeAddrInIReg) {
465                         /* Allocate stack space for the return value */
466                         info->vret_slot = map_stack_slot (info->stack_usage / sizeof (gpointer));
467                         info->stack_usage += mono_type_stack_size_internal (normal_sig->ret, NULL, FALSE) + sizeof (gpointer);
468                 }
469                 DEBUG_AMD64_GSHAREDVT_PRINT ("RET marshal is %s\n", ret_marshal_name [info->ret_marshal]);
470         }
471
472         info->stack_usage = ALIGN_TO (info->stack_usage, MONO_ARCH_FRAME_ALIGNMENT);
473
474         DEBUG_AMD64_GSHAREDVT_PRINT ("allocated an info at %p stack usage %d\n", info, info->stack_usage);
475         return info;
476 }
477
478 #endif