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 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);
151 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
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];
165 buf = code = mono_global_codeman_reserve (buf_len);
168 * We are being called by an gsharedvt arg trampoline, the info argument is in AMD64_RAX.
170 n_arg_regs = PARAM_REGS;
171 n_arg_fregs = FLOAT_PARAM_REGS;
173 /* Compute stack frame size and offsets */
176 info_offset = offset;
180 rgctx_arg_reg_offset = offset;
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;
190 g_assert (framesize % MONO_ARCH_FRAME_ALIGNMENT == 0);
191 g_assert (reg_area_size % MONO_ARCH_FRAME_ALIGNMENT == 0);
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);
198 /* save the old frame pointer */
199 amd64_push_reg (code, AMD64_RBP);
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);
206 /* set it as the new frame pointer */
207 amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, sizeof(mgreg_t));
209 /* unwind markers 3/3 */
210 mono_add_unwind_op_def_cfa_reg (unwind_ops, code, buf, AMD64_RBP);
212 /* setup the frame */
213 amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, framesize);
218 amd64_mov_membase_reg (code, AMD64_RSP, info_offset, AMD64_RAX, sizeof (mgreg_t));
220 amd64_mov_membase_reg (code, AMD64_RSP, rgctx_arg_reg_offset, MONO_ARCH_RGCTX_REG, sizeof (mgreg_t));
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));
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);
228 /* TODO Allocate stack area used to pass arguments to the method */
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);
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);
241 /* The stack now looks like this:
243 <caller stack params area>
246 <caller registers area>
253 /* Call start_gsharedvt_call () */
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));
259 /* arg3 == callee stack area */
260 amd64_lea_membase (code, MONO_AMD64_ARG_REG3, AMD64_RSP, callee_reg_area_offset);
262 /* arg4 = mrgctx reg */
263 amd64_mov_reg_reg (code, MONO_AMD64_ARG_REG4, MONO_ARCH_RGCTX_REG, sizeof(mgreg_t));
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);
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));
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));
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);
282 amd64_mov_reg_membase (code, MONO_ARCH_RGCTX_REG, AMD64_RBP, -(framesize - rgctx_arg_reg_offset), sizeof (mgreg_t));
284 /* Clear callee reg area */
285 amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, reg_area_size);
288 amd64_call_reg (code, AMD64_R11);
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));
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);
299 x86_branch32 (code, X86_CC_NE, 0, TRUE);
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);
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);
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);
318 for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
319 amd64_alu_reg_imm (code, X86_CMP, AMD64_R10, i);
321 amd64_branch8 (code, X86_CC_EQ, 0, TRUE);
323 x86_breakpoint (code); /* unhandled case */
325 for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
326 mono_amd64_patch (br [i], code);
328 case GSHAREDVT_RET_NONE:
330 case GSHAREDVT_RET_I1:
331 amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, TRUE, FALSE);
333 case GSHAREDVT_RET_U1:
334 amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, FALSE, FALSE);
336 case GSHAREDVT_RET_I2:
337 amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, TRUE, TRUE);
339 case GSHAREDVT_RET_U2:
340 amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, FALSE, TRUE);
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);
346 case GSHAREDVT_RET_I8:
347 amd64_mov_reg_membase (code, AMD64_RAX, AMD64_R11, 0, 8);
349 case GSHAREDVT_RET_IREGS_1:
350 amd64_mov_reg_membase (code, return_regs [i - GSHAREDVT_RET_IREGS_1], AMD64_R11, 0, 8);
352 case GSHAREDVT_RET_R8:
353 amd64_sse_movsd_reg_membase (code, AMD64_XMM0, AMD64_R11, 0);
356 x86_breakpoint (code); /* can't handle specific case */
359 br_ret [b_ret_index ++] = code;
360 x86_jump32 (code, 0);
366 mono_amd64_patch (br_out, code);
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.
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.
375 address: RBP - framesize + caller_reg_area_offset + 8*slot
378 int caller_vret_offset = caller_reg_area_offset - framesize;
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));
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);
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));
395 /* Load ret marshal type in R10 */
396 amd64_mov_reg_membase (code, AMD64_R10, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal), 4);
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);
402 amd64_branch8 (code, X86_CC_EQ, 0, TRUE);
404 x86_breakpoint (code); /* unhandled case */
406 for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
407 mono_amd64_patch (br [i], code);
409 case GSHAREDVT_RET_NONE:
411 case GSHAREDVT_RET_IREGS_1:
412 amd64_mov_membase_reg (code, AMD64_R11, 0, return_regs [i - GSHAREDVT_RET_IREGS_1], 8);
414 case GSHAREDVT_RET_R8:
415 amd64_sse_movsd_membase_reg (code, AMD64_R11, 0, AMD64_XMM0);
418 x86_breakpoint (code); /* can't handle specific case */
421 br_ret [b_ret_index ++] = code;
422 x86_jump32 (code, 0);
426 for (i = 0; i < b_ret_index; ++i)
427 mono_amd64_patch (br_ret [i], code);
433 g_assert ((code - buf) < buf_len);
436 *info = mono_tramp_info_create ("gsharedvt_trampoline", buf, code - buf, ji, unwind_ops);
438 mono_arch_flush_icache (buf, code - buf);
445 mono_arch_get_gsharedvt_arg_trampoline (MonoDomain *domain, gpointer arg, gpointer addr)
447 g_assert_not_reached ();
452 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
454 g_assert_not_reached ();
463 mono_amd64_start_gsharedvt_call (GSharedVtCallInfo *info, gpointer *caller, gpointer *callee, gpointer mrgctx_reg)
465 g_assert_not_reached ();