Revert "[mono][debugger] First PR to implement iCorDebug on mono (#20757)"
[mono-project.git] / mono / mini / mini-amd64-gsharedvt.c
blob6da2539ef198167d5b17af9058245dbd5ae6ec92
1 /**
2 * \file
3 * libcorkscrew-based native unwinder
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>
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.
14 #include <config.h>
15 #include <glib.h>
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>
25 #include <mono/utils/memcheck.h>
27 #include "mini.h"
28 #include "mini-amd64.h"
29 #include "mini-amd64-gsharedvt.h"
30 #include "debugger-agent.h"
32 #if defined (MONO_ARCH_GSHAREDVT_SUPPORTED)
34 gboolean
35 mono_arch_gsharedvt_sig_supported (MonoMethodSignature *sig)
37 return FALSE;
40 static const char*
41 storage_name (ArgStorage st)
43 switch (st) {
44 case ArgInIReg: return "ArgInIReg";
45 case ArgInFloatSSEReg: return "ArgInFloatSSEReg";
46 case ArgInDoubleSSEReg: return "ArgInDoubleSSEReg";
47 case ArgOnStack: return "ArgOnStack";
48 case ArgValuetypeInReg: return "ArgValuetypeInReg";
49 case ArgValuetypeAddrInIReg: return "ArgValuetypeAddrInIReg";
50 case ArgValuetypeAddrOnStack: return "ArgValuetypeAddrOnStack";
51 case ArgGSharedVtInReg: return "ArgGSharedVtInReg";
52 case ArgGSharedVtOnStack: return "ArgGSharedVtOnStack";
53 case ArgNone: return "ArgNone";
54 default: return "unknown";
58 #ifdef DEBUG_AMD64_GSHAREDVT
59 static char *
60 arg_info_desc (ArgInfo *info)
62 GString *str = g_string_new ("");
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]));
72 return g_string_free (str, FALSE);
74 #endif
76 static void
77 add_to_map (GPtrArray *map, int src, int dst)
79 g_ptr_array_add (map, GUINT_TO_POINTER (src));
80 g_ptr_array_add (map, GUINT_TO_POINTER (dst));
84 * Slot mapping:
86 * System V:
87 * 0..5 - rdi, rsi, rdx, rcx, r8, r9
88 * 6..13 - xmm0..xmm7
89 * 14.. - stack slots
91 * Windows:
92 * 0..3 - rcx, rdx, r8, r9
93 * 4..7 - xmm0..xmm3
94 * 8.. - stack slots
97 static int
98 map_reg (int reg)
100 int i = 0;
101 for (i = 0; i < PARAM_REGS; ++i) {
102 if (param_regs [i] == reg)
103 return i;
105 g_error ("Invalid argument register number %d", reg);
106 return -1;
109 static int
110 map_freg (int reg)
112 return reg + PARAM_REGS;
115 static int
116 map_stack_slot (int slot)
118 return slot + PARAM_REGS + FLOAT_PARAM_REGS;
122 Format for the source descriptor:
125 Format for the destination descriptor:
126 bits 0:15 - source register
127 bits 16:23 - return marshal
128 bits 24:32 - slot count
130 #define SRC_DESCRIPTOR_MARSHAL_SHIFT 16
131 #define SRC_DESCRIPTOR_MARSHAL_MASK 0x0ff
133 #define SLOT_COUNT_SHIFT 24
134 #define SLOT_COUNT_MASK 0xff
135 #define SLOT_BYTE_SIZE 8
137 static int
138 get_arg_slots (ArgInfo *ainfo, int **out_slots, gboolean is_source_argument)
140 int sreg = ainfo->reg;
141 int sslot = ainfo->offset / 8;
142 int *src = NULL;
143 int i, nsrc;
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;
188 *out_slots = src;
189 return nsrc;
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)
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;
223 // Once dst is known, operate on the src
224 static void
225 handle_marshal_when_dst_gsharedvt (ArgInfo *src_info, int *arg_marshal)
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;
245 static void
246 handle_map_when_gsharedvt_in_reg (ArgInfo *reg_info, int *n, int **map)
248 *n = 1;
249 *map = g_new0 (int, 1);
250 (*map) [0] = map_reg (reg_info->reg);
253 static void
254 handle_map_when_gsharedvt_on_stack (ArgInfo *reg_info, int *n, int **map, gboolean is_source_argument)
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
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)
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;
272 GPtrArray *map;
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);
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.
292 /* sig/cinfo describes the normal call, while gcinfo describes the gsharedvt call */
293 if (gsharedvt_in) {
294 sig = caller_sig;
295 cinfo = caller_cinfo;
296 gcinfo = callee_cinfo;
297 } else {
298 sig = callee_sig;
299 cinfo = callee_cinfo;
300 gcinfo = caller_cinfo;
303 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_internal (caller_sig))); // Leak
304 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_internal (callee_sig)));
306 if (gcinfo->ret.storage == ArgGsharedvtVariableInReg) {
308 * The return type is gsharedvt
310 var_ret = TRUE;
314 * The stack looks like this:
315 * <arguments>
316 * <trampoline frame>
317 * <call area>
318 * We have to map the stack slots in <arguments> to the stack slots in <call area>.
320 map = g_ptr_array_new ();
322 for (aindex = 0; aindex < cinfo->nargs; ++aindex) {
323 ArgInfo *src_info = &caller_cinfo->args [aindex];
324 ArgInfo *dst_info = &callee_cinfo->args [aindex];
325 int *src = NULL, *dst = NULL;
326 int nsrc = -1, ndst = -1, nslots = 0;
328 int arg_marshal = GSHAREDVT_ARG_NONE;
329 int arg_slots = 0; // Size in quadwords
330 DEBUG_AMD64_GSHAREDVT_PRINT ("-- arg %d in (%s) out (%s)\n", aindex, arg_info_desc (src_info), arg_info_desc (dst_info));
332 switch (src_info->storage) {
333 case ArgInIReg:
334 case ArgInDoubleSSEReg:
335 case ArgInFloatSSEReg:
336 case ArgValuetypeInReg:
337 case ArgOnStack:
338 nsrc = get_arg_slots (src_info, &src, TRUE);
339 break;
340 case ArgGSharedVtInReg:
341 handle_marshal_when_src_gsharedvt (dst_info, &arg_marshal, &arg_slots);
342 handle_map_when_gsharedvt_in_reg (src_info, &nsrc, &src);
343 break;
344 case ArgGSharedVtOnStack:
345 handle_marshal_when_src_gsharedvt (dst_info, &arg_marshal, &arg_slots);
346 handle_map_when_gsharedvt_on_stack (src_info, &nsrc, &src, TRUE);
347 break;
348 case ArgValuetypeAddrInIReg:
349 case ArgValuetypeAddrOnStack:
350 nsrc = get_arg_slots (src_info, &src, TRUE);
351 break;
352 default:
353 g_error ("Gsharedvt can't handle source arg type %d", (int)src_info->storage); // Inappropriate value: ArgValuetypeAddrInIReg is for returns only
356 switch (dst_info->storage) {
357 case ArgInIReg:
358 case ArgInDoubleSSEReg:
359 case ArgInFloatSSEReg:
360 case ArgOnStack:
361 case ArgValuetypeInReg:
362 ndst = get_arg_slots (dst_info, &dst, FALSE);
363 break;
364 case ArgGSharedVtInReg:
365 handle_marshal_when_dst_gsharedvt (src_info, &arg_marshal);
366 handle_map_when_gsharedvt_in_reg (dst_info, &ndst, &dst);
367 break;
368 case ArgGSharedVtOnStack:
369 handle_marshal_when_dst_gsharedvt (src_info, &arg_marshal);
370 handle_map_when_gsharedvt_on_stack (dst_info, &ndst, &dst, FALSE);
371 break;
372 case ArgValuetypeAddrInIReg:
373 case ArgValuetypeAddrOnStack:
374 ndst = get_arg_slots (dst_info, &dst, FALSE);
375 break;
376 default:
377 g_error ("Gsharedvt can't handle dest arg type %d", (int)dst_info->storage); // See above
380 if (arg_marshal == GSHAREDVT_ARG_BYREF_TO_BYVAL && dst_info->byte_arg_size) {
381 /* Have to load less than 4 bytes */
382 switch (dst_info->byte_arg_size) {
383 case 1:
384 arg_marshal = dst_info->is_signed ? GSHAREDVT_ARG_BYREF_TO_BYVAL_I1 : GSHAREDVT_ARG_BYREF_TO_BYVAL_U1;
385 break;
386 case 2:
387 arg_marshal = dst_info->is_signed ? GSHAREDVT_ARG_BYREF_TO_BYVAL_I2 : GSHAREDVT_ARG_BYREF_TO_BYVAL_U2;
388 break;
389 default:
390 arg_marshal = dst_info->is_signed ? GSHAREDVT_ARG_BYREF_TO_BYVAL_I4 : GSHAREDVT_ARG_BYREF_TO_BYVAL_U4;
391 break;
395 if (nsrc)
396 src [0] |= (arg_marshal << SRC_DESCRIPTOR_MARSHAL_SHIFT) | (arg_slots << SLOT_COUNT_SHIFT);
398 /* Merge and add to the global list*/
399 nslots = MIN (nsrc, ndst);
400 DEBUG_AMD64_GSHAREDVT_PRINT ("nsrc %d ndst %d\n", nsrc, ndst);
402 for (i = 0; i < nslots; ++i)
403 add_to_map (map, src [i], dst [i]);
405 g_free (src);
406 g_free (dst);
409 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);
411 if (cinfo->ret.storage == ArgValuetypeAddrInIReg) {
412 /* Both the caller and the callee pass the vtype ret address in r8 (System V) and RCX or RDX (Windows) */
413 g_assert (gcinfo->ret.storage == ArgValuetypeAddrInIReg || gcinfo->ret.storage == ArgGsharedvtVariableInReg);
414 add_to_map (map, map_reg (caller_cinfo->ret.reg), map_reg (callee_cinfo->ret.reg));
417 info = mono_domain_alloc0 (mono_domain_get (), sizeof (GSharedVtCallInfo) + (map->len * sizeof (int)));
418 info->addr = addr;
419 info->stack_usage = callee_cinfo->stack_usage;
420 info->ret_marshal = GSHAREDVT_RET_NONE;
421 info->gsharedvt_in = gsharedvt_in ? 1 : 0;
422 info->vret_slot = -1;
423 info->calli = calli;
425 if (var_ret) {
426 g_assert (gcinfo->ret.storage == ArgGsharedvtVariableInReg);
427 info->vret_arg_reg = map_reg (gcinfo->ret.reg);
428 DEBUG_AMD64_GSHAREDVT_PRINT ("mapping vreg_arg_reg to %d in reg %s\n", info->vret_arg_reg, mono_arch_regname (gcinfo->ret.reg));
429 } else {
430 info->vret_arg_reg = -1;
433 #ifdef DEBUG_AMD64_GSHAREDVT
434 printf ("final map:\n");
435 for (i = 0; i < map->len; i += 2) {
436 printf ("\t[%d] src %x dst %x\n ",
437 i / 2,
438 GPOINTER_TO_UINT (g_ptr_array_index (map, i)),
439 GPOINTER_TO_UINT (g_ptr_array_index (map, i + 1)));
441 #endif
443 info->vcall_offset = vcall_offset;
444 info->map_count = map->len / 2;
445 for (i = 0; i < map->len; ++i)
446 info->map [i] = GPOINTER_TO_UINT (g_ptr_array_index (map, i));
447 g_ptr_array_free (map, TRUE);
449 /* Compute return value marshalling */
450 if (var_ret) {
451 /* Compute return value marshalling */
452 switch (cinfo->ret.storage) {
453 case ArgInIReg:
454 if (!gsharedvt_in || sig->ret->byref) {
455 info->ret_marshal = GSHAREDVT_RET_IREGS_1;
456 } else {
457 MonoType *ret = sig->ret;
459 ret = mini_type_get_underlying_type (ret);
460 switch (ret->type) {
461 case MONO_TYPE_I1:
462 info->ret_marshal = GSHAREDVT_RET_I1;
463 break;
464 case MONO_TYPE_U1:
465 info->ret_marshal = GSHAREDVT_RET_U1;
466 break;
467 case MONO_TYPE_I2:
468 info->ret_marshal = GSHAREDVT_RET_I2;
469 break;
470 case MONO_TYPE_U2:
471 info->ret_marshal = GSHAREDVT_RET_U2;
472 break;
473 case MONO_TYPE_I4:
474 info->ret_marshal = GSHAREDVT_RET_I4;
475 break;
476 case MONO_TYPE_U4:
477 info->ret_marshal = GSHAREDVT_RET_U4;
478 break;
479 case MONO_TYPE_I:
480 case MONO_TYPE_U:
481 case MONO_TYPE_PTR:
482 case MONO_TYPE_FNPTR:
483 case MONO_TYPE_OBJECT:
484 case MONO_TYPE_U8:
485 case MONO_TYPE_I8:
486 info->ret_marshal = GSHAREDVT_RET_I8;
487 break;
488 case MONO_TYPE_GENERICINST:
489 g_assert (!mono_type_generic_inst_is_valuetype (ret));
490 info->ret_marshal = GSHAREDVT_RET_I8;
491 break;
492 default:
493 g_error ("Gsharedvt can't handle dst type [%d]", (int)sig->ret->type);
496 break;
497 case ArgValuetypeInReg:
498 info->ret_marshal = GSHAREDVT_RET_IREGS_1 - 1 + cinfo->ret.nregs;
499 g_assert (cinfo->ret.nregs == 1); // ABI supports 2-register return but we do not implement this.
500 break;
501 case ArgInDoubleSSEReg:
502 case ArgInFloatSSEReg:
503 info->ret_marshal = GSHAREDVT_RET_R8;
504 break;
505 case ArgValuetypeAddrInIReg:
506 break;
507 default:
508 g_error ("Can't marshal return of storage [%d] %s", (int)cinfo->ret.storage, storage_name (cinfo->ret.storage));
511 if (gsharedvt_in && cinfo->ret.storage != ArgValuetypeAddrInIReg) {
512 /* Allocate stack space for the return value */
513 info->vret_slot = map_stack_slot (info->stack_usage / sizeof (target_mgreg_t));
514 info->stack_usage += mono_type_stack_size_internal (normal_sig->ret, NULL, FALSE) + sizeof (target_mgreg_t);
516 DEBUG_AMD64_GSHAREDVT_PRINT ("RET marshal is %s\n", ret_marshal_name [info->ret_marshal]);
519 info->stack_usage = ALIGN_TO (info->stack_usage, MONO_ARCH_FRAME_ALIGNMENT);
521 g_free (callee_cinfo);
522 g_free (caller_cinfo);
524 DEBUG_AMD64_GSHAREDVT_PRINT ("allocated an info at %p stack usage %d\n", info, info->stack_usage);
525 return info;
528 #endif