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