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