f5600adbde78198f5a9935ef9971316cc1abfb8c
[mono.git] / mono / mini / tramp-amd64-gsharedvt.c
1 /*
2  * tramp-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 #define SRC_REG_SHIFT 0
37 #define SRC_REG_MASK 0xFFFF
38
39 #define SRC_DESCRIPTOR_MARSHAL_SHIFT 16
40 #define SRC_DESCRIPTOR_MARSHAL_MASK 0x0FF
41
42 #define SLOT_COUNT_SHIFT 24
43 #define SLOT_COUNT_MASK 0xFF
44
45 gpointer
46 mono_amd64_start_gsharedvt_call (GSharedVtCallInfo *info, gpointer *caller, gpointer *callee, gpointer mrgctx_reg)
47 {
48         int i;
49
50 #ifdef DEBUG_AMD64_GSHAREDVT
51         printf ("mono_amd64_start_gsharedvt_call info %p caller %p callee %p ctx %p\n", info, caller, callee, mrgctx_reg);
52
53         for (i = 0; i < PARAM_REGS; ++i)
54                 printf ("\treg [%d] -> %p\n", i, caller [i]);
55 #endif
56
57         /* Set vtype ret arg */
58         if (info->vret_slot != -1) {
59                 DEBUG_AMD64_GSHAREDVT_PRINT ("vret handling\n[%d] < &%d (%p)\n", info->vret_arg_reg, info->vret_slot, &callee [info->vret_slot]);
60                 g_assert (info->vret_slot);
61                 callee [info->vret_arg_reg] = &callee [info->vret_slot];
62         }
63
64         for (i = 0; i < info->map_count; ++i) {
65                 int src = info->map [i * 2];
66                 int dst = info->map [(i * 2) + 1];
67                 int arg_marshal = (src >> SRC_DESCRIPTOR_MARSHAL_SHIFT) & SRC_DESCRIPTOR_MARSHAL_MASK;
68
69                 int source_reg = src & SRC_REG_MASK;
70                 int dest_reg = dst & SRC_REG_MASK;
71
72                 DEBUG_AMD64_GSHAREDVT_PRINT ("source %x dest %x marshal %d: ", src, dst, arg_marshal);
73                 switch (arg_marshal) {
74                 case GSHAREDVT_ARG_NONE:
75                         callee [dest_reg] = caller [source_reg];
76                         DEBUG_AMD64_GSHAREDVT_PRINT ("[%d] <- %d (%p) <- (%p)\n", dest_reg, source_reg, &callee [dest_reg], caller [source_reg]);
77                         break;
78                 case GSHAREDVT_ARG_BYVAL_TO_BYREF:
79                         /* gsharedvt argument passed by addr in reg/stack slot */
80                         callee [dest_reg] = &caller [source_reg];
81                         DEBUG_AMD64_GSHAREDVT_PRINT ("[%d] <- &%d (%p) <- (%p)\n", dest_reg, source_reg, &callee [dest_reg], &caller [source_reg]);
82                         break;
83                 case GSHAREDVT_ARG_BYREF_TO_BYVAL: {
84                         int slot_count = (src >> SLOT_COUNT_SHIFT) & SLOT_COUNT_MASK;
85                         int j;
86                         gpointer *addr = caller [source_reg];
87
88                         for (j = 0; j < slot_count; ++j)
89                                 callee [dest_reg + j] = addr [j];
90                         DEBUG_AMD64_GSHAREDVT_PRINT ("[%d] <- [%d] (%d words) (%p) <- (%p)\n", dest_reg, source_reg, slot_count, &callee [dest_reg], &caller [source_reg]);
91                         break;
92                 }
93                 default:
94                         g_error ("cant handle arg marshal %d\n", arg_marshal);
95                 }
96         }
97
98         //Can't handle for now
99         if (info->vcall_offset != -1){
100                 MonoObject *this_obj = caller [0];
101
102                 DEBUG_AMD64_GSHAREDVT_PRINT ("target is a vcall at offset %d\n", info->vcall_offset / 8);
103                 if (G_UNLIKELY (!this_obj))
104                         return NULL;
105                 if (info->vcall_offset == MONO_GSHAREDVT_DEL_INVOKE_VT_OFFSET)
106                         /* delegate invoke */
107                         return ((MonoDelegate*)this_obj)->invoke_impl;
108                 else
109                         return *(gpointer*)((char*)this_obj->vtable + info->vcall_offset);
110         } else if (info->calli) {
111                 /* The address to call is passed in the mrgctx reg */
112                 return mrgctx_reg;
113         } else {
114                 DEBUG_AMD64_GSHAREDVT_PRINT ("target is %p\n", info->addr);
115                 return info->addr;
116         }
117 }
118
119 #ifndef DISABLE_JIT
120
121 // Compiler support
122
123 /*
124  * mono_arch_get_gsharedvt_arg_trampoline:
125  *
126  *   See tramp-x86.c for documentation.
127  */
128 gpointer
129 mono_arch_get_gsharedvt_arg_trampoline (MonoDomain *domain, gpointer arg, gpointer addr)
130 {
131         guint8 *code, *start;
132         int buf_len;
133
134         buf_len = 32;
135
136         start = code = mono_domain_code_reserve (domain, buf_len);
137
138         amd64_mov_reg_imm (code, AMD64_RAX, arg);
139         amd64_jump_code (code, addr);
140         g_assert ((code - start) < buf_len);
141
142         mono_arch_flush_icache (start, code - start);
143         mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL);
144
145         g_assert (0);
146         return start;
147 }
148
149 gpointer
150 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
151 {
152         guint8 *code, *buf;
153         int buf_len, cfa_offset;
154         GSList *unwind_ops = NULL;
155         MonoJumpInfo *ji = NULL;
156         int n_arg_regs, n_arg_fregs, framesize, i;
157         int info_offset, offset, rgctx_arg_reg_offset;
158         int caller_reg_area_offset, callee_reg_area_offset, callee_stack_area_offset;
159         guint8 *br_out, *br [64], *br_ret [64];
160         int b_ret_index;
161         int reg_area_size;
162
163         buf_len = 2048;
164         buf = code = mono_global_codeman_reserve (buf_len);
165
166         /*
167          * We are being called by an gsharedvt arg trampoline, the info argument is in AMD64_RAX.
168          */
169         n_arg_regs = PARAM_REGS;
170         n_arg_fregs = FLOAT_PARAM_REGS;
171
172         /* Compute stack frame size and offsets */
173         offset = 0;
174         /* info reg */
175         info_offset = offset;
176         offset += 8;
177
178         /* rgctx reg */
179         rgctx_arg_reg_offset = offset;
180         offset += 8;
181
182         /*callconv in regs */
183         caller_reg_area_offset = offset;
184         reg_area_size = ALIGN_TO ((n_arg_regs + n_arg_fregs) * 8, MONO_ARCH_FRAME_ALIGNMENT);
185         offset += reg_area_size;
186
187         framesize = offset;
188
189         g_assert (framesize % MONO_ARCH_FRAME_ALIGNMENT == 0);
190         g_assert (reg_area_size % MONO_ARCH_FRAME_ALIGNMENT == 0);
191
192         /* unwind markers 1/3 */
193         cfa_offset = sizeof (gpointer);
194         mono_add_unwind_op_def_cfa (unwind_ops, code, buf, AMD64_RSP, cfa_offset);
195         mono_add_unwind_op_offset (unwind_ops, code, buf, AMD64_RIP, -cfa_offset);
196
197         /* save the old frame pointer */
198         amd64_push_reg (code, AMD64_RBP);
199
200         /* unwind markers 2/3 */
201         cfa_offset += sizeof (gpointer);
202         mono_add_unwind_op_def_cfa_offset (unwind_ops, code, buf, cfa_offset);
203         mono_add_unwind_op_offset (unwind_ops, code, buf, AMD64_RBP, - cfa_offset);
204
205         /* set it as the new frame pointer */
206         amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, sizeof(mgreg_t));
207
208         /* unwind markers 3/3 */
209         mono_add_unwind_op_def_cfa_reg (unwind_ops, code, buf, AMD64_RBP);
210
211         /* setup the frame */
212         amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, framesize);
213         
214         /* save stuff */
215
216         /* save info */
217         amd64_mov_membase_reg (code, AMD64_RSP, info_offset, AMD64_RAX, sizeof (mgreg_t));
218         /* save rgctx */
219         amd64_mov_membase_reg (code, AMD64_RSP, rgctx_arg_reg_offset, MONO_ARCH_RGCTX_REG, sizeof (mgreg_t));
220
221         for (i = 0; i < n_arg_regs; ++i)
222                 amd64_mov_membase_reg (code, AMD64_RSP, caller_reg_area_offset + i * 8, param_regs [i], sizeof (mgreg_t));
223
224         for (i = 0; i < n_arg_fregs; ++i)
225                 amd64_sse_movsd_membase_reg (code, AMD64_RSP, caller_reg_area_offset + (i + n_arg_regs) * 8, i);
226
227         /* TODO Allocate stack area used to pass arguments to the method */
228
229
230         /* Allocate callee register area just below the caller area so it can be accessed from start_gsharedvt_call using negative offsets */
231         /* XXX figure out alignment */
232         callee_reg_area_offset = reg_area_size - ((n_arg_regs + n_arg_fregs) * 8); /* Ensure alignment */
233         callee_stack_area_offset = callee_reg_area_offset + reg_area_size;
234         amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, reg_area_size);
235
236         /* Allocate stack area used to pass arguments to the method */
237         amd64_mov_reg_membase (code, AMD64_R11, AMD64_RAX, MONO_STRUCT_OFFSET (GSharedVtCallInfo, stack_usage), 4);
238         amd64_alu_reg_reg (code, X86_SUB, AMD64_RSP, AMD64_R11);
239
240         /* The stack now looks like this:
241
242         <caller stack params area>
243         <return address>
244         <old frame pointer>
245         <caller registers area>
246         <rgctx>
247         <gsharedvt info>
248         <callee stack area>
249         <callee reg area>
250          */
251
252         /* Call start_gsharedvt_call () */
253         /* arg1 == info */
254         amd64_mov_reg_reg (code, MONO_AMD64_ARG_REG1, AMD64_RAX, sizeof(mgreg_t));
255         /* arg2 = caller stack area */
256         amd64_lea_membase (code, MONO_AMD64_ARG_REG2, AMD64_RBP, -(framesize - caller_reg_area_offset)); 
257
258         /* arg3 == callee stack area */
259         amd64_lea_membase (code, MONO_AMD64_ARG_REG3, AMD64_RSP, callee_reg_area_offset);
260
261         /* arg4 = mrgctx reg */
262         amd64_mov_reg_reg (code, MONO_AMD64_ARG_REG4, MONO_ARCH_RGCTX_REG, sizeof(mgreg_t));
263
264         if (aot) {
265                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_amd64_start_gsharedvt_call");
266                 #ifdef TARGET_WIN32
267                         /* Since we are doing a call as part of setting up stackframe, the reserved shadow stack used by Windows platform is allocated up in
268                         the callee stack area but currently the callee reg area is in between. Windows calling convention dictates that room is made on stack where
269                         callee can save any parameters passed in registers. Since Windows x64 calling convention
270                         uses 4 registers for the first 4 parameters, stack needs to be adjusted before making the call.
271                         NOTE, Windows calling convention assumes that space for all registers have been reserved, regardless
272                         of the number of function parameters actually used.
273                         */
274                         int shadow_reg_size = 0;
275
276                         shadow_reg_size = ALIGN_TO (PARAM_REGS * sizeof(gpointer), MONO_ARCH_FRAME_ALIGNMENT);
277                         amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, shadow_reg_size);
278                         amd64_call_reg (code, AMD64_R11);
279                         amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, shadow_reg_size);
280                 #else
281                         amd64_call_reg (code, AMD64_R11);
282                 #endif
283         } else {
284                 g_error ("no aot");
285         }
286
287         /* Method to call is now on RAX. Restore regs and jump */
288         amd64_mov_reg_reg (code, AMD64_R11, AMD64_RAX, sizeof(mgreg_t));
289
290         for (i = 0; i < n_arg_regs; ++i)
291                 amd64_mov_reg_membase (code, param_regs [i], AMD64_RSP, callee_reg_area_offset + i * 8, sizeof (mgreg_t));
292
293         for (i = 0; i < n_arg_fregs; ++i)
294                 amd64_sse_movsd_reg_membase (code, i, AMD64_RSP, callee_reg_area_offset + (i + n_arg_regs) * 8);
295
296         //load rgctx
297         amd64_mov_reg_membase (code, MONO_ARCH_RGCTX_REG, AMD64_RBP, -(framesize - rgctx_arg_reg_offset), sizeof (mgreg_t));
298
299         /* Clear callee reg area */
300         amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, reg_area_size);
301
302         /* Call the thing */
303         amd64_call_reg (code, AMD64_R11);
304
305         /* Marshal return value. Available registers: R10 and R11 */
306         /* Load info struct */
307         amd64_mov_reg_membase (code, AMD64_R10, AMD64_RBP, -(framesize - info_offset), sizeof (mgreg_t));
308
309         /* Branch to the in/out handling code */
310         amd64_alu_membase_imm_size (code, X86_CMP, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, gsharedvt_in), 1, 4);
311
312         b_ret_index = 0;
313         br_out = code;
314         x86_branch32 (code, X86_CC_NE, 0, TRUE);
315
316         /*
317          * IN CASE
318          */
319
320         /* Load vret_slot */
321         /* Use first input parameter register as scratch since it is volatile on all platforms */
322         amd64_mov_reg_membase (code, MONO_AMD64_ARG_REG1, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_slot), 4);
323         amd64_alu_reg_imm (code, X86_SUB, MONO_AMD64_ARG_REG1, n_arg_regs + n_arg_fregs);
324         amd64_shift_reg_imm (code, X86_SHL, MONO_AMD64_ARG_REG1, 3);
325
326         /* vret address is RBP - (framesize - caller_reg_area_offset) */
327         amd64_mov_reg_reg (code, AMD64_R11, AMD64_RSP, sizeof(mgreg_t));
328         amd64_alu_reg_reg (code, X86_ADD, AMD64_R11, MONO_AMD64_ARG_REG1);
329
330         /* Load ret marshal type */
331         /* Load vret address in R11 */
332         amd64_mov_reg_membase (code, AMD64_R10, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal), 4);
333
334         for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
335                 amd64_alu_reg_imm (code, X86_CMP, AMD64_R10, i);
336                 br [i] = code;
337                 amd64_branch8 (code, X86_CC_EQ, 0, TRUE);
338         }
339         x86_breakpoint (code); /* unhandled case */
340
341         for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
342                 mono_amd64_patch (br [i], code);
343                 switch (i) {
344                 case GSHAREDVT_RET_NONE:
345                         break;
346                 case GSHAREDVT_RET_I1:
347                         amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, TRUE, FALSE);
348                         break;
349                 case GSHAREDVT_RET_U1:
350                         amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, FALSE, FALSE);
351                         break;
352                 case GSHAREDVT_RET_I2:
353                         amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, TRUE, TRUE);
354                         break;
355                 case GSHAREDVT_RET_U2:
356                         amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, FALSE, TRUE);
357                         break;
358                 case GSHAREDVT_RET_I4: // CORRECT
359                 case GSHAREDVT_RET_U4: // THIS IS INCORRECT. WHY IS IT NOT FAILING?
360                         amd64_movsxd_reg_membase (code, AMD64_RAX, AMD64_R11, 0);
361                         break;
362                 case GSHAREDVT_RET_I8:
363                         amd64_mov_reg_membase (code, AMD64_RAX, AMD64_R11, 0, 8);
364                         break;
365                 case GSHAREDVT_RET_IREGS_1:
366                         amd64_mov_reg_membase (code, return_regs [i - GSHAREDVT_RET_IREGS_1], AMD64_R11, 0, 8);
367                         break;
368                 case GSHAREDVT_RET_R8:
369                         amd64_sse_movsd_reg_membase (code, AMD64_XMM0, AMD64_R11, 0);
370                         break;
371                 default:
372                         x86_breakpoint (code); /* can't handle specific case */
373                 }
374
375                 br_ret [b_ret_index ++] = code;
376                 x86_jump32 (code, 0);
377         }
378
379         /*
380          * OUT CASE
381          */
382         mono_amd64_patch (br_out, code);
383
384         /*
385                 Address to write return to is in the original value of the register specified by vret_arg_reg.
386                 This will be either RSI, RDI (System V) or RCX, RDX (Windows) depending on whether this is a static call.
387                 Its location:
388                 We alloc 'framesize' bytes below RBP to save regs, info and rgctx. RSP = RBP - framesize
389                 We store RDI (System V), RCX (Windows) at RSP + caller_reg_area_offset + slot_index_of (register) * 8.
390
391                 address: RBP - framesize + caller_reg_area_offset + 8*slot
392         */
393
394         int caller_vret_offset = caller_reg_area_offset - framesize;
395
396         /* Load vret address in R11 */
397         /* Position to return to is passed as a hidden argument. Load 'vret_arg_slot' to find it */
398         amd64_movsxd_reg_membase (code, AMD64_R11, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_arg_reg));
399
400         // In the GSHAREDVT_RET_NONE case, vret_arg_slot is -1. In this case, skip marshalling.
401         amd64_alu_reg_imm (code, X86_CMP, AMD64_R11, 0);
402         br_ret [b_ret_index ++] = code;
403         amd64_branch32 (code, X86_CC_LT, 0, TRUE);
404
405         /* Compute ret area address in the caller frame, *( ((gpointer *)RBP) [R11+2] ) */
406         amd64_shift_reg_imm (code, X86_SHL, AMD64_R11, 3);
407         amd64_alu_reg_imm (code, X86_ADD, AMD64_R11, caller_vret_offset);
408         amd64_alu_reg_reg (code, X86_ADD, AMD64_R11, AMD64_RBP);
409         amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, 0, sizeof (gpointer));
410
411         /* Load ret marshal type in R10 */
412         amd64_mov_reg_membase (code, AMD64_R10, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal), 4);
413
414         // Switch table for ret_marshal value
415         for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
416                 amd64_alu_reg_imm (code, X86_CMP, AMD64_R10, i);
417                 br [i] = code;
418                 amd64_branch8 (code, X86_CC_EQ, 0, TRUE);
419         }
420         x86_breakpoint (code); /* unhandled case */
421
422         for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
423                 mono_amd64_patch (br [i], code);
424                 switch (i) {
425                 case GSHAREDVT_RET_NONE:
426                         break;
427                 case GSHAREDVT_RET_IREGS_1:
428                         amd64_mov_membase_reg (code, AMD64_R11, 0, return_regs [i - GSHAREDVT_RET_IREGS_1], 8);
429                         break;
430                 case GSHAREDVT_RET_R8:
431                         amd64_sse_movsd_membase_reg (code, AMD64_R11, 0, AMD64_XMM0);
432                         break;
433                 default:
434                         x86_breakpoint (code); /* can't handle specific case */
435                 }
436
437                 br_ret [b_ret_index ++] = code;
438                 x86_jump32 (code, 0);
439         }
440
441         /* exit path */
442         for (i = 0; i < b_ret_index; ++i)
443                 mono_amd64_patch (br_ret [i], code);
444
445         /* Exit code path */
446         amd64_leave (code);
447         amd64_ret (code);
448
449         g_assert ((code - buf) < buf_len);
450
451         if (info)
452                 *info = mono_tramp_info_create ("gsharedvt_trampoline", buf, code - buf, ji, unwind_ops);
453
454         mono_arch_flush_icache (buf, code - buf);
455         return buf;
456 }
457
458 #else
459
460 gpointer
461 mono_arch_get_gsharedvt_arg_trampoline (MonoDomain *domain, gpointer arg, gpointer addr)
462 {
463         g_assert_not_reached ();
464         return NULL;
465 }
466
467 gpointer
468 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
469 {
470         g_assert_not_reached ();
471         return NULL;
472 }
473
474 #endif
475
476 #else
477
478 gpointer
479 mono_amd64_start_gsharedvt_call (GSharedVtCallInfo *info, gpointer *caller, gpointer *callee, gpointer mrgctx_reg)
480 {
481         g_assert_not_reached ();
482         return NULL;
483 }
484
485 #endif