2 * tramp-amd64-gsharedvt.c: libcorkscrew-based native unwinder
5 * Zoltan Varga <vargaz@gmail.com>
6 * Rodrigo Kumpera <kumpera@gmail.com>
7 * Andi McClure <andi.mcclure@xamarin.com>
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.
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>
24 #include <mono/utils/memcheck.h>
27 #include "mini-amd64.h"
28 #include "mini-amd64-gsharedvt.h"
29 #include "debugger-agent.h"
31 #if defined (MONO_ARCH_GSHAREDVT_SUPPORTED)
33 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
35 #define SRC_REG_SHIFT 0
36 #define SRC_REG_MASK 0xFFFF
38 #define SRC_DESCRIPTOR_MARSHAL_SHIFT 16
39 #define SRC_DESCRIPTOR_MARSHAL_MASK 0x0FF
41 #define SLOT_COUNT_SHIFT 24
42 #define SLOT_COUNT_MASK 0xFF
45 mono_amd64_start_gsharedvt_call (GSharedVtCallInfo *info, gpointer *caller, gpointer *callee, gpointer mrgctx_reg)
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);
52 for (i = 0; i < PARAM_REGS; ++i)
53 printf ("\treg [%d] -> %p\n", i, caller [i]);
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];
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;
68 int source_reg = src & SRC_REG_MASK;
69 int dest_reg = dst & SRC_REG_MASK;
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]);
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]);
82 case GSHAREDVT_ARG_BYREF_TO_BYVAL: {
83 int slot_count = (src >> SLOT_COUNT_SHIFT) & SLOT_COUNT_MASK;
85 gpointer *addr = caller [source_reg];
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]);
93 g_error ("cant handle arg marshal %d\n", arg_marshal);
97 //Can't handle for now
98 if (info->vcall_offset != -1){
99 MonoObject *this_obj = caller [0];
101 DEBUG_AMD64_GSHAREDVT_PRINT ("target is a vcall at offset %d\n", info->vcall_offset / 8);
102 if (G_UNLIKELY (!this_obj))
104 if (info->vcall_offset == MONO_GSHAREDVT_DEL_INVOKE_VT_OFFSET)
105 /* delegate invoke */
106 return ((MonoDelegate*)this_obj)->invoke_impl;
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 */
113 DEBUG_AMD64_GSHAREDVT_PRINT ("target is %p\n", info->addr);
123 * mono_arch_get_gsharedvt_arg_trampoline:
125 * See tramp-x86.c for documentation.
128 mono_arch_get_gsharedvt_arg_trampoline (MonoDomain *domain, gpointer arg, gpointer addr)
130 guint8 *code, *start;
135 start = code = mono_domain_code_reserve (domain, buf_len);
137 amd64_mov_reg_imm (code, AMD64_RAX, arg);
138 amd64_jump_code (code, addr);
139 g_assert ((code - start) < buf_len);
141 mono_arch_flush_icache (start, code - start);
142 mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL);
150 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
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];
164 buf = code = mono_global_codeman_reserve (buf_len);
167 * We are being called by an gsharedvt arg trampoline, the info argument is in AMD64_RAX.
169 n_arg_regs = PARAM_REGS;
170 n_arg_fregs = FLOAT_PARAM_REGS;
172 /* Compute stack frame size and offsets */
175 info_offset = offset;
179 rgctx_arg_reg_offset = offset;
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;
189 g_assert (framesize % MONO_ARCH_FRAME_ALIGNMENT == 0);
190 g_assert (reg_area_size % MONO_ARCH_FRAME_ALIGNMENT == 0);
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);
197 /* save the old frame pointer */
198 amd64_push_reg (code, AMD64_RBP);
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);
205 /* set it as the new frame pointer */
206 amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, sizeof(mgreg_t));
208 /* unwind markers 3/3 */
209 mono_add_unwind_op_def_cfa_reg (unwind_ops, code, buf, AMD64_RBP);
211 /* setup the frame */
212 amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, framesize);
217 amd64_mov_membase_reg (code, AMD64_RSP, info_offset, AMD64_RAX, sizeof (mgreg_t));
219 amd64_mov_membase_reg (code, AMD64_RSP, rgctx_arg_reg_offset, MONO_ARCH_RGCTX_REG, sizeof (mgreg_t));
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));
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);
227 /* TODO Allocate stack area used to pass arguments to the method */
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);
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);
240 /* The stack now looks like this:
242 <caller stack params area>
245 <caller registers area>
252 /* Call start_gsharedvt_call () */
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));
258 /* arg3 == callee stack area */
259 amd64_lea_membase (code, MONO_AMD64_ARG_REG3, AMD64_RSP, callee_reg_area_offset);
261 /* arg4 = mrgctx reg */
262 amd64_mov_reg_reg (code, MONO_AMD64_ARG_REG4, MONO_ARCH_RGCTX_REG, sizeof(mgreg_t));
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);
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));
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));
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);
281 amd64_mov_reg_membase (code, MONO_ARCH_RGCTX_REG, AMD64_RBP, -(framesize - rgctx_arg_reg_offset), sizeof (mgreg_t));
283 /* Clear callee reg area */
284 amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, reg_area_size);
287 amd64_call_reg (code, AMD64_R11);
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));
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);
298 x86_branch32 (code, X86_CC_NE, 0, TRUE);
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);
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);
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);
317 for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
318 amd64_alu_reg_imm (code, X86_CMP, AMD64_R10, i);
320 amd64_branch8 (code, X86_CC_EQ, 0, TRUE);
322 x86_breakpoint (code); /* unhandled case */
324 for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
325 mono_amd64_patch (br [i], code);
327 case GSHAREDVT_RET_NONE:
329 case GSHAREDVT_RET_I1:
330 amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, TRUE, FALSE);
332 case GSHAREDVT_RET_U1:
333 amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, FALSE, FALSE);
335 case GSHAREDVT_RET_I2:
336 amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, TRUE, TRUE);
338 case GSHAREDVT_RET_U2:
339 amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, FALSE, TRUE);
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);
345 case GSHAREDVT_RET_I8:
346 amd64_mov_reg_membase (code, AMD64_RAX, AMD64_R11, 0, 8);
348 case GSHAREDVT_RET_IREGS_1:
349 amd64_mov_reg_membase (code, return_regs [i - GSHAREDVT_RET_IREGS_1], AMD64_R11, 0, 8);
351 case GSHAREDVT_RET_R8:
352 amd64_sse_movsd_reg_membase (code, AMD64_XMM0, AMD64_R11, 0);
355 x86_breakpoint (code); /* can't handle specific case */
358 br_ret [b_ret_index ++] = code;
359 x86_jump32 (code, 0);
365 mono_amd64_patch (br_out, code);
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.
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.
374 address: RBP - framesize + caller_reg_area_offset + 8*slot
377 int caller_vret_offset = caller_reg_area_offset - framesize;
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));
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);
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));
394 /* Load ret marshal type in R10 */
395 amd64_mov_reg_membase (code, AMD64_R10, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal), 4);
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);
401 amd64_branch8 (code, X86_CC_EQ, 0, TRUE);
403 x86_breakpoint (code); /* unhandled case */
405 for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
406 mono_amd64_patch (br [i], code);
408 case GSHAREDVT_RET_NONE:
410 case GSHAREDVT_RET_IREGS_1:
411 amd64_mov_membase_reg (code, AMD64_R11, 0, return_regs [i - GSHAREDVT_RET_IREGS_1], 8);
413 case GSHAREDVT_RET_R8:
414 amd64_sse_movsd_membase_reg (code, AMD64_R11, 0, AMD64_XMM0);
417 x86_breakpoint (code); /* can't handle specific case */
420 br_ret [b_ret_index ++] = code;
421 x86_jump32 (code, 0);
425 for (i = 0; i < b_ret_index; ++i)
426 mono_amd64_patch (br_ret [i], code);
432 g_assert ((code - buf) < buf_len);
435 *info = mono_tramp_info_create ("gsharedvt_trampoline", buf, code - buf, ji, unwind_ops);
437 mono_arch_flush_icache (buf, code - buf);
444 mono_arch_get_gsharedvt_arg_trampoline (MonoDomain *domain, gpointer arg, gpointer addr)
446 g_assert_not_reached ();
451 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
453 g_assert_not_reached ();
462 mono_amd64_start_gsharedvt_call (GSharedVtCallInfo *info, gpointer *caller, gpointer *callee, gpointer mrgctx_reg)
464 g_assert_not_reached ();