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