Merge pull request #4327 from vkargov/vk-abcremedy
[mono.git] / mono / mini / tramp-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 #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         mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, NULL), domain);
146
147         return start;
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 + MONO_MAX_TRAMPOLINE_UNWINDINFO_SIZE);
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         mono_add_unwind_op_fp_alloc (unwind_ops, code, buf, AMD64_RBP, 0);
212
213         /* setup the frame */
214         amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, framesize);
215         
216         /* save stuff */
217
218         /* save info */
219         amd64_mov_membase_reg (code, AMD64_RSP, info_offset, AMD64_RAX, sizeof (mgreg_t));
220         /* save rgctx */
221         amd64_mov_membase_reg (code, AMD64_RSP, rgctx_arg_reg_offset, MONO_ARCH_RGCTX_REG, sizeof (mgreg_t));
222
223         for (i = 0; i < n_arg_regs; ++i)
224                 amd64_mov_membase_reg (code, AMD64_RSP, caller_reg_area_offset + i * 8, param_regs [i], sizeof (mgreg_t));
225
226         for (i = 0; i < n_arg_fregs; ++i)
227                 amd64_sse_movsd_membase_reg (code, AMD64_RSP, caller_reg_area_offset + (i + n_arg_regs) * 8, i);
228
229         /* TODO Allocate stack area used to pass arguments to the method */
230
231
232         /* Allocate callee register area just below the caller area so it can be accessed from start_gsharedvt_call using negative offsets */
233         /* XXX figure out alignment */
234         callee_reg_area_offset = reg_area_size - ((n_arg_regs + n_arg_fregs) * 8); /* Ensure alignment */
235         callee_stack_area_offset = callee_reg_area_offset + reg_area_size;
236         amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, reg_area_size);
237
238         /* Allocate stack area used to pass arguments to the method */
239         amd64_mov_reg_membase (code, AMD64_R11, AMD64_RAX, MONO_STRUCT_OFFSET (GSharedVtCallInfo, stack_usage), 4);
240         amd64_alu_reg_reg (code, X86_SUB, AMD64_RSP, AMD64_R11);
241
242         /* The stack now looks like this:
243
244         <caller stack params area>
245         <return address>
246         <old frame pointer>
247         <caller registers area>
248         <rgctx>
249         <gsharedvt info>
250         <callee stack area>
251         <callee reg area>
252          */
253
254         /* Call start_gsharedvt_call () */
255         /* arg1 == info */
256         amd64_mov_reg_reg (code, MONO_AMD64_ARG_REG1, AMD64_RAX, sizeof(mgreg_t));
257         /* arg2 = caller stack area */
258         amd64_lea_membase (code, MONO_AMD64_ARG_REG2, AMD64_RBP, -(framesize - caller_reg_area_offset)); 
259
260         /* arg3 == callee stack area */
261         amd64_lea_membase (code, MONO_AMD64_ARG_REG3, AMD64_RSP, callee_reg_area_offset);
262
263         /* arg4 = mrgctx reg */
264         amd64_mov_reg_reg (code, MONO_AMD64_ARG_REG4, MONO_ARCH_RGCTX_REG, sizeof(mgreg_t));
265
266         if (aot) {
267                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_amd64_start_gsharedvt_call");
268                 #ifdef TARGET_WIN32
269                         /* Since we are doing a call as part of setting up stackframe, the reserved shadow stack used by Windows platform is allocated up in
270                         the callee stack area but currently the callee reg area is in between. Windows calling convention dictates that room is made on stack where
271                         callee can save any parameters passed in registers. Since Windows x64 calling convention
272                         uses 4 registers for the first 4 parameters, stack needs to be adjusted before making the call.
273                         NOTE, Windows calling convention assumes that space for all registers have been reserved, regardless
274                         of the number of function parameters actually used.
275                         */
276                         int shadow_reg_size = 0;
277
278                         shadow_reg_size = ALIGN_TO (PARAM_REGS * sizeof(gpointer), MONO_ARCH_FRAME_ALIGNMENT);
279                         amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, shadow_reg_size);
280                         amd64_call_reg (code, AMD64_R11);
281                         amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, shadow_reg_size);
282                 #else
283                         amd64_call_reg (code, AMD64_R11);
284                 #endif
285         } else {
286                 amd64_call_code (code, mono_amd64_start_gsharedvt_call);
287         }
288
289         /* Method to call is now on RAX. Restore regs and jump */
290         amd64_mov_reg_reg (code, AMD64_R11, AMD64_RAX, sizeof(mgreg_t));
291
292         for (i = 0; i < n_arg_regs; ++i)
293                 amd64_mov_reg_membase (code, param_regs [i], AMD64_RSP, callee_reg_area_offset + i * 8, sizeof (mgreg_t));
294
295         for (i = 0; i < n_arg_fregs; ++i)
296                 amd64_sse_movsd_reg_membase (code, i, AMD64_RSP, callee_reg_area_offset + (i + n_arg_regs) * 8);
297
298         //load rgctx
299         amd64_mov_reg_membase (code, MONO_ARCH_RGCTX_REG, AMD64_RBP, -(framesize - rgctx_arg_reg_offset), sizeof (mgreg_t));
300
301         /* Clear callee reg area */
302         amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, reg_area_size);
303
304         /* Call the thing */
305         amd64_call_reg (code, AMD64_R11);
306
307         /* Marshal return value. Available registers: R10 and R11 */
308         /* Load info struct */
309         amd64_mov_reg_membase (code, AMD64_R10, AMD64_RBP, -(framesize - info_offset), sizeof (mgreg_t));
310
311         /* Branch to the in/out handling code */
312         amd64_alu_membase_imm_size (code, X86_CMP, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, gsharedvt_in), 1, 4);
313
314         b_ret_index = 0;
315         br_out = code;
316         x86_branch32 (code, X86_CC_NE, 0, TRUE);
317
318         /*
319          * IN CASE
320          */
321
322         /* Load vret_slot */
323         /* Use first input parameter register as scratch since it is volatile on all platforms */
324         amd64_mov_reg_membase (code, MONO_AMD64_ARG_REG1, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_slot), 4);
325         amd64_alu_reg_imm (code, X86_SUB, MONO_AMD64_ARG_REG1, n_arg_regs + n_arg_fregs);
326         amd64_shift_reg_imm (code, X86_SHL, MONO_AMD64_ARG_REG1, 3);
327
328         /* vret address is RBP - (framesize - caller_reg_area_offset) */
329         amd64_mov_reg_reg (code, AMD64_R11, AMD64_RSP, sizeof(mgreg_t));
330         amd64_alu_reg_reg (code, X86_ADD, AMD64_R11, MONO_AMD64_ARG_REG1);
331
332         /* Load ret marshal type */
333         /* Load vret address in R11 */
334         amd64_mov_reg_membase (code, AMD64_R10, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal), 4);
335
336         for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
337                 amd64_alu_reg_imm (code, X86_CMP, AMD64_R10, i);
338                 br [i] = code;
339                 amd64_branch8 (code, X86_CC_EQ, 0, TRUE);
340         }
341         x86_breakpoint (code); /* unhandled case */
342
343         for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
344                 mono_amd64_patch (br [i], code);
345                 switch (i) {
346                 case GSHAREDVT_RET_NONE:
347                         break;
348                 case GSHAREDVT_RET_I1:
349                         amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, TRUE, FALSE);
350                         break;
351                 case GSHAREDVT_RET_U1:
352                         amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, FALSE, FALSE);
353                         break;
354                 case GSHAREDVT_RET_I2:
355                         amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, TRUE, TRUE);
356                         break;
357                 case GSHAREDVT_RET_U2:
358                         amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, FALSE, TRUE);
359                         break;
360                 case GSHAREDVT_RET_I4: // CORRECT
361                 case GSHAREDVT_RET_U4: // THIS IS INCORRECT. WHY IS IT NOT FAILING?
362                         amd64_movsxd_reg_membase (code, AMD64_RAX, AMD64_R11, 0);
363                         break;
364                 case GSHAREDVT_RET_I8:
365                         amd64_mov_reg_membase (code, AMD64_RAX, AMD64_R11, 0, 8);
366                         break;
367                 case GSHAREDVT_RET_IREGS_1:
368                         amd64_mov_reg_membase (code, return_regs [i - GSHAREDVT_RET_IREGS_1], AMD64_R11, 0, 8);
369                         break;
370                 case GSHAREDVT_RET_R8:
371                         amd64_sse_movsd_reg_membase (code, AMD64_XMM0, AMD64_R11, 0);
372                         break;
373                 default:
374                         x86_breakpoint (code); /* can't handle specific case */
375                 }
376
377                 br_ret [b_ret_index ++] = code;
378                 x86_jump32 (code, 0);
379         }
380
381         /*
382          * OUT CASE
383          */
384         mono_amd64_patch (br_out, code);
385
386         /*
387                 Address to write return to is in the original value of the register specified by vret_arg_reg.
388                 This will be either RSI, RDI (System V) or RCX, RDX (Windows) depending on whether this is a static call.
389                 Its location:
390                 We alloc 'framesize' bytes below RBP to save regs, info and rgctx. RSP = RBP - framesize
391                 We store RDI (System V), RCX (Windows) at RSP + caller_reg_area_offset + slot_index_of (register) * 8.
392
393                 address: RBP - framesize + caller_reg_area_offset + 8*slot
394         */
395
396         int caller_vret_offset = caller_reg_area_offset - framesize;
397
398         /* Load vret address in R11 */
399         /* Position to return to is passed as a hidden argument. Load 'vret_arg_slot' to find it */
400         amd64_movsxd_reg_membase (code, AMD64_R11, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_arg_reg));
401
402         // In the GSHAREDVT_RET_NONE case, vret_arg_slot is -1. In this case, skip marshalling.
403         amd64_alu_reg_imm (code, X86_CMP, AMD64_R11, 0);
404         br_ret [b_ret_index ++] = code;
405         amd64_branch32 (code, X86_CC_LT, 0, TRUE);
406
407         /* Compute ret area address in the caller frame, *( ((gpointer *)RBP) [R11+2] ) */
408         amd64_shift_reg_imm (code, X86_SHL, AMD64_R11, 3);
409         amd64_alu_reg_imm (code, X86_ADD, AMD64_R11, caller_vret_offset);
410         amd64_alu_reg_reg (code, X86_ADD, AMD64_R11, AMD64_RBP);
411         amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, 0, sizeof (gpointer));
412
413         /* Load ret marshal type in R10 */
414         amd64_mov_reg_membase (code, AMD64_R10, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal), 4);
415
416         // Switch table for ret_marshal value
417         for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
418                 amd64_alu_reg_imm (code, X86_CMP, AMD64_R10, i);
419                 br [i] = code;
420                 amd64_branch8 (code, X86_CC_EQ, 0, TRUE);
421         }
422         x86_breakpoint (code); /* unhandled case */
423
424         for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
425                 mono_amd64_patch (br [i], code);
426                 switch (i) {
427                 case GSHAREDVT_RET_NONE:
428                         break;
429                 case GSHAREDVT_RET_IREGS_1:
430                         amd64_mov_membase_reg (code, AMD64_R11, 0, return_regs [i - GSHAREDVT_RET_IREGS_1], 8);
431                         break;
432                 case GSHAREDVT_RET_R8:
433                         amd64_sse_movsd_membase_reg (code, AMD64_R11, 0, AMD64_XMM0);
434                         break;
435                 default:
436                         x86_breakpoint (code); /* can't handle specific case */
437                 }
438
439                 br_ret [b_ret_index ++] = code;
440                 x86_jump32 (code, 0);
441         }
442
443         /* exit path */
444         for (i = 0; i < b_ret_index; ++i)
445                 mono_amd64_patch (br_ret [i], code);
446
447         /* Exit code path */
448 #if TARGET_WIN32
449         amd64_lea_membase (code, AMD64_RSP, AMD64_RBP, 0);
450         amd64_pop_reg (code, AMD64_RBP);
451         mono_add_unwind_op_same_value (unwind_ops, code, buf, AMD64_RBP);
452 #else
453         amd64_leave (code);
454 #endif
455         amd64_ret (code);
456
457         g_assert ((code - buf) < buf_len);
458         g_assert_checked (mono_arch_unwindinfo_validate_size (unwind_ops, MONO_MAX_TRAMPOLINE_UNWINDINFO_SIZE));
459
460         if (info)
461                 *info = mono_tramp_info_create ("gsharedvt_trampoline", buf, code - buf, ji, unwind_ops);
462
463         mono_arch_flush_icache (buf, code - buf);
464         return buf;
465 }
466
467 #else
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         g_assert_not_reached ();
480         return NULL;
481 }
482
483 #endif
484
485 #else
486
487 gpointer
488 mono_amd64_start_gsharedvt_call (GSharedVtCallInfo *info, gpointer *caller, gpointer *callee, gpointer mrgctx_reg)
489 {
490         g_assert_not_reached ();
491         return NULL;
492 }
493
494 #endif