Merge pull request #2977 from BrzVlad/fix-major-log2
[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         mono_arch_flush_icache (start, code - start);
142         mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL);
143
144         g_assert (0);
145         return start;
146 }
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         <calee stack area>
249         <calee 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                 amd64_call_reg (code, AMD64_R11);
267         } else {
268                 g_error ("no aot");
269         }
270
271         /* Method to call is now on RAX. Restore regs and jump */
272         amd64_mov_reg_reg (code, AMD64_R11, AMD64_RAX, sizeof(mgreg_t));
273
274         for (i = 0; i < n_arg_regs; ++i)
275                 amd64_mov_reg_membase (code, param_regs [i], AMD64_RSP, callee_reg_area_offset + i * 8, sizeof (mgreg_t));
276
277         for (i = 0; i < n_arg_fregs; ++i)
278                 amd64_sse_movsd_reg_membase (code, i, AMD64_RSP, callee_reg_area_offset + (i + n_arg_regs) * 8);
279
280         //load rgctx
281         amd64_mov_reg_membase (code, MONO_ARCH_RGCTX_REG, AMD64_RBP, -(framesize - rgctx_arg_reg_offset), sizeof (mgreg_t));
282
283         /* Clear callee reg area */
284         amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, reg_area_size);
285
286         /* Call the thing */
287         amd64_call_reg (code, AMD64_R11);
288
289         /* Marshal return value. Available registers: R10 and R11 */
290         /* Load info struct */
291         amd64_mov_reg_membase (code, AMD64_R10, AMD64_RBP, -(framesize - info_offset), sizeof (mgreg_t));
292
293         /* Branch to the in/out handling code */
294         amd64_alu_membase_imm_size (code, X86_CMP, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, gsharedvt_in), 1, 4);
295
296         b_ret_index = 0;
297         br_out = code;
298         x86_branch32 (code, X86_CC_NE, 0, TRUE);
299
300         /*
301          * IN CASE
302          */
303
304         /* Load vret_slot */
305         amd64_mov_reg_membase (code, AMD64_RDI, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_slot), 4);
306         amd64_alu_reg_imm (code, X86_SUB, AMD64_RDI, n_arg_regs + n_arg_fregs);
307         amd64_shift_reg_imm (code, X86_SHL, AMD64_RDI, 3);
308
309         /* vret address is RBP - (framesize - caller_reg_area_offset) */
310         amd64_mov_reg_reg (code, AMD64_R11, AMD64_RSP, sizeof(mgreg_t));
311         amd64_alu_reg_reg (code, X86_ADD, AMD64_R11, AMD64_RDI);
312
313         /* Load ret marshal type */
314         /* Load vret address in R11 */
315         amd64_mov_reg_membase (code, AMD64_R10, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal), 4);
316
317         for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
318                 amd64_alu_reg_imm (code, X86_CMP, AMD64_R10, i);
319                 br [i] = code;
320                 amd64_branch8 (code, X86_CC_EQ, 0, TRUE);
321         }
322         x86_breakpoint (code); /* unhandled case */
323
324         for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
325                 mono_amd64_patch (br [i], code);
326                 switch (i) {
327                 case GSHAREDVT_RET_NONE:
328                         break;
329                 case GSHAREDVT_RET_I1:
330                         amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, TRUE, FALSE);
331                         break;
332                 case GSHAREDVT_RET_U1:
333                         amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, FALSE, FALSE);
334                         break;
335                 case GSHAREDVT_RET_I2:
336                         amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, TRUE, TRUE);
337                         break;
338                 case GSHAREDVT_RET_U2:
339                         amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, FALSE, TRUE);
340                         break;
341                 case GSHAREDVT_RET_I4: // CORRECT
342                 case GSHAREDVT_RET_U4: // THIS IS INCORRECT. WHY IS IT NOT FAILING?
343                         amd64_movsxd_reg_membase (code, AMD64_RAX, AMD64_R11, 0);
344                         break;
345                 case GSHAREDVT_RET_I8:
346                         amd64_mov_reg_membase (code, AMD64_RAX, AMD64_R11, 0, 8);
347                         break;
348                 case GSHAREDVT_RET_IREGS_1:
349                         amd64_mov_reg_membase (code, return_regs [i - GSHAREDVT_RET_IREGS_1], AMD64_R11, 0, 8);
350                         break;
351                 case GSHAREDVT_RET_R8:
352                         amd64_sse_movsd_reg_membase (code, AMD64_XMM0, AMD64_R11, 0);
353                         break;
354                 default:
355                         x86_breakpoint (code); /* can't handle specific case */
356                 }
357
358                 br_ret [b_ret_index ++] = code;
359                 x86_jump32 (code, 0);
360         }
361
362         /*
363          * OUT CASE
364          */
365         mono_amd64_patch (br_out, code);
366
367         /*
368                 Address to write return to is in the original value of the register specified by vret_arg_reg.
369                 This will be either RSI or RDI depending on whether this is a static call.
370                 Its location:
371                 We alloc 'framesize' bytes below RBP to save regs, info and rgctx. RSP = RBP - framesize
372                 We store rdi at RSP + caller_reg_area_offset + slot_index_of (register) * 8.
373
374                 address: RBP - framesize + caller_reg_area_offset + 8*slot
375         */
376
377         int caller_vret_offset = caller_reg_area_offset - framesize;
378
379         /* Load vret address in R11 */
380         /* Position to return to is passed as a hidden argument. Load 'vret_arg_slot' to find it */
381         amd64_movsxd_reg_membase (code, AMD64_R11, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_arg_reg));
382
383         // In the GSHAREDVT_RET_NONE case, vret_arg_slot is -1. In this case, skip marshalling.
384         amd64_alu_reg_imm (code, X86_CMP, AMD64_R11, 0);
385         br_ret [b_ret_index ++] = code;
386         amd64_branch32 (code, X86_CC_LT, 0, TRUE);
387
388         /* Compute ret area address in the caller frame, *( ((gpointer *)RBP) [R11+2] ) */
389         amd64_shift_reg_imm (code, X86_SHL, AMD64_R11, 3);
390         amd64_alu_reg_imm (code, X86_ADD, AMD64_R11, caller_vret_offset);
391         amd64_alu_reg_reg (code, X86_ADD, AMD64_R11, AMD64_RBP);
392         amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, 0, sizeof (gpointer));
393
394         /* Load ret marshal type in R10 */
395         amd64_mov_reg_membase (code, AMD64_R10, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal), 4);
396
397         // Switch table for ret_marshal value
398         for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
399                 amd64_alu_reg_imm (code, X86_CMP, AMD64_R10, i);
400                 br [i] = code;
401                 amd64_branch8 (code, X86_CC_EQ, 0, TRUE);
402         }
403         x86_breakpoint (code); /* unhandled case */
404
405         for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
406                 mono_amd64_patch (br [i], code);
407                 switch (i) {
408                 case GSHAREDVT_RET_NONE:
409                         break;
410                 case GSHAREDVT_RET_IREGS_1:
411                         amd64_mov_membase_reg (code, AMD64_R11, 0, return_regs [i - GSHAREDVT_RET_IREGS_1], 8);
412                         break;
413                 case GSHAREDVT_RET_R8:
414                         amd64_sse_movsd_membase_reg (code, AMD64_R11, 0, AMD64_XMM0);
415                         break;
416                 default:
417                         x86_breakpoint (code); /* can't handle specific case */
418                 }
419
420                 br_ret [b_ret_index ++] = code;
421                 x86_jump32 (code, 0);
422         }
423
424         /* exit path */
425         for (i = 0; i < b_ret_index; ++i)
426                 mono_amd64_patch (br_ret [i], code);
427
428         /* Exit code path */
429         amd64_leave (code);
430         amd64_ret (code);
431
432         g_assert ((code - buf) < buf_len);
433
434         if (info)
435                 *info = mono_tramp_info_create ("gsharedvt_trampoline", buf, code - buf, ji, unwind_ops);
436
437         mono_arch_flush_icache (buf, code - buf);
438         return buf;
439 }
440
441 #else
442
443 gpointer
444 mono_arch_get_gsharedvt_arg_trampoline (MonoDomain *domain, gpointer arg, gpointer addr)
445 {
446         g_assert_not_reached ();
447         return NULL;
448 }
449
450 gpointer
451 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
452 {
453         g_assert_not_reached ();
454         return NULL;
455 }
456
457 #endif
458
459 #else
460
461 gpointer
462 mono_amd64_start_gsharedvt_call (GSharedVtCallInfo *info, gpointer *caller, gpointer *callee, gpointer mrgctx_reg)
463 {
464         g_assert_not_reached ();
465         return NULL;
466 }
467
468 #endif