2 * tramp-arm-gsharedvt.c: gsharedvt support code for arm
5 * Zoltan Varga <vargaz@gmail.com>
7 * Copyright 2013 Xamarin, Inc (http://www.xamarin.com)
8 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
13 #include <mono/metadata/abi-details.h>
14 #include <mono/metadata/appdomain.h>
15 #include <mono/metadata/marshal.h>
16 #include <mono/metadata/tabledefs.h>
17 #include <mono/metadata/profiler-private.h>
18 #include <mono/arch/arm/arm-codegen.h>
19 #include <mono/arch/arm/arm-vfp-codegen.h>
24 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
27 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
30 emit_bx (guint8* code, int reg)
32 if (mono_arm_thumb_supported ())
35 ARM_MOV_REG_REG (code, ARMREG_PC, reg);
41 mono_arm_start_gsharedvt_call (GSharedVtCallInfo *info, gpointer *caller, gpointer *callee, gpointer mrgctx_reg)
46 * The caller/callee regs are mapped to slot 0..3, stack slot 0 is mapped to slot 4, etc.
49 /* Set vtype ret arg */
50 if (info->vret_slot != -1) {
51 callee [info->vret_arg_reg] = &callee [info->vret_slot];
54 for (i = 0; i < info->map_count; ++i) {
55 int src = info->map [i * 2];
56 int dst = info->map [(i * 2) + 1];
57 int arg_marshal = (src >> 16) & 0xff;
59 switch (arg_marshal) {
60 case GSHAREDVT_ARG_NONE:
61 callee [dst] = caller [src];
63 case GSHAREDVT_ARG_BYVAL_TO_BYREF:
64 /* gsharedvt argument passed by addr in reg/stack slot */
66 callee [dst] = caller + src;
68 case GSHAREDVT_ARG_BYREF_TO_BYVAL: {
69 /* gsharedvt argument passed by value */
70 int nslots = (src >> 4) & 0xff;
71 int src_slot = src & 0xf;
73 gpointer *addr = caller [src_slot];
75 for (j = 0; j < nslots; ++j)
76 callee [dst + j] = addr [j];
79 case GSHAREDVT_ARG_BYREF_TO_BYVAL_I1: {
80 int src_slot = src & 0xf;
81 gpointer *addr = caller [src_slot];
83 callee [dst] = GINT_TO_POINTER ((int)*(gint8*)addr);
86 case GSHAREDVT_ARG_BYREF_TO_BYVAL_I2: {
87 int src_slot = src & 0xf;
88 gpointer *addr = caller [src_slot];
90 callee [dst] = GINT_TO_POINTER ((int)*(gint16*)addr);
93 case GSHAREDVT_ARG_BYREF_TO_BYVAL_U1: {
94 int src_slot = src & 0xf;
95 gpointer *addr = caller [src_slot];
97 callee [dst] = GUINT_TO_POINTER ((guint)*(guint8*)addr);
100 case GSHAREDVT_ARG_BYREF_TO_BYVAL_U2: {
101 int src_slot = src & 0xf;
102 gpointer *addr = caller [src_slot];
104 callee [dst] = GUINT_TO_POINTER ((guint)*(guint16*)addr);
108 g_assert_not_reached ();
113 if (info->vcall_offset != -1) {
114 MonoObject *this_obj = caller [0];
116 if (G_UNLIKELY (!this_obj))
118 if (info->vcall_offset == MONO_GSHAREDVT_DEL_INVOKE_VT_OFFSET)
119 /* delegate invoke */
120 return ((MonoDelegate*)this_obj)->invoke_impl;
122 return *(gpointer*)((char*)this_obj->vtable + info->vcall_offset);
123 } else if (info->calli) {
124 /* The address to call is passed in the mrgctx reg */
134 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
137 int buf_len, cfa_offset;
138 GSList *unwind_ops = NULL;
139 MonoJumpInfo *ji = NULL;
140 guint8 *br_out, *br [16], *br_ret [16];
141 int i, arg_reg, npushed, info_offset, mrgctx_offset, caller_reg_area_offset, callee_reg_area_offset;
142 int lr_offset, fp, br_ret_index, args_size;
145 buf = code = mono_global_codeman_reserve (buf_len);
148 /* Registers pushed by the arg trampoline */
151 // ios abi compatible frame
153 cfa_offset = npushed * sizeof (gpointer);
154 mono_add_unwind_op_def_cfa (unwind_ops, code, buf, ARMREG_SP, cfa_offset);
155 ARM_PUSH (code, (1 << fp) | (1 << ARMREG_LR));
156 cfa_offset += 2 * sizeof (gpointer);
157 mono_add_unwind_op_def_cfa_offset (unwind_ops, code, buf, cfa_offset);
158 mono_add_unwind_op_offset (unwind_ops, code, buf, fp, (- cfa_offset));
159 mono_add_unwind_op_offset (unwind_ops, code, buf, ARMREG_LR, ((- cfa_offset) + 4));
160 ARM_MOV_REG_REG (code, fp, ARMREG_SP);
161 mono_add_unwind_op_def_cfa_reg (unwind_ops, code, buf, fp);
162 /* Allocate stack frame */
163 ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 32);
164 if (MONO_ARCH_FRAME_ALIGNMENT > 8)
165 ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, (MONO_ARCH_FRAME_ALIGNMENT - 8));
168 callee_reg_area_offset = - (6 * 4);
169 caller_reg_area_offset = cfa_offset - (npushed * sizeof (gpointer));
171 /* Save info struct which is in r0 */
172 ARM_STR_IMM (code, arg_reg, fp, info_offset);
174 ARM_STR_IMM (code, MONO_ARCH_RGCTX_REG, fp, mrgctx_offset);
175 /* Allocate callee area */
176 ARM_LDR_IMM (code, ARMREG_IP, arg_reg, MONO_STRUCT_OFFSET (GSharedVtCallInfo, stack_usage));
177 ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_IP);
178 /* Allocate callee register area just below the callee area so it can be accessed from start_gsharedvt_call using negative offsets */
179 ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 4 * sizeof (gpointer));
182 * The stack now looks like this:
187 * <callee area> <- sp
189 g_assert (mono_arm_thumb_supported ());
191 /* Call start_gsharedvt_call () */
192 /* 4 arguments, needs 0 stack slot, need to clean it up after the call */
193 args_size = 0 * sizeof (gpointer);
194 ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, args_size);
196 ARM_LDR_IMM (code, ARMREG_R0, fp, info_offset);
197 /* arg2 == caller stack area */
198 ARM_ADD_REG_IMM8 (code, ARMREG_R1, fp, cfa_offset - 4 * sizeof (gpointer));
199 /* arg3 == callee stack area */
200 ARM_ADD_REG_IMM8 (code, ARMREG_R2, ARMREG_SP, args_size);
201 /* arg4 == mrgctx reg */
202 ARM_LDR_IMM (code, ARMREG_R3, fp, mrgctx_offset);
205 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_arm_start_gsharedvt_call");
206 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
208 *(gpointer*)code = NULL;
210 ARM_LDR_REG_REG (code, ARMREG_IP, ARMREG_PC, ARMREG_IP);
212 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
214 *(gpointer*)code = mono_arm_start_gsharedvt_call;
217 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
218 code = emit_bx (code, ARMREG_IP);
220 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, args_size);
222 /* Make the real method call */
223 /* R0 contains the addr to call */
224 ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_R0);
225 /* Load argument registers */
226 ARM_LDM (code, ARMREG_SP, (1 << ARMREG_R0) | (1 << ARMREG_R1) | (1 << ARMREG_R2) | (1 << ARMREG_R3));
227 /* Pop callee register area */
228 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 4 * sizeof (gpointer));
230 ARM_LDR_IMM (code, MONO_ARCH_RGCTX_REG, fp, mrgctx_offset);
233 ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
234 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, addr));
236 /* mono_arch_find_imt_method () depends on this */
237 ARM_ADD_REG_IMM8 (code, ARMREG_LR, ARMREG_PC, 4);
238 ARM_BX (code, ARMREG_IP);
239 *((gpointer*)code) = NULL;
244 /* Branch between IN/OUT cases */
245 ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
246 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, gsharedvt_in));
248 ARM_CMP_REG_IMM8 (code, ARMREG_IP, 1);
250 ARM_B_COND (code, ARMCOND_NE, 0);
254 /* LR == return marshalling type */
255 ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
256 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal));
258 /* Continue if no marshalling required */
259 ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_NONE);
260 br_ret [br_ret_index ++] = code;
261 ARM_B_COND (code, ARMCOND_EQ, 0);
263 /* Compute vret area address in LR */
264 ARM_LDR_IMM (code, ARMREG_LR, fp, info_offset);
265 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_LR, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_slot));
266 /* The slot value is off by 4 */
267 ARM_SUB_REG_IMM8 (code, ARMREG_LR, ARMREG_LR, 4);
268 ARM_SHL_IMM (code, ARMREG_LR, ARMREG_LR, 2);
269 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ARMREG_SP);
271 /* Branch to specific marshalling code */
272 ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_IREG);
274 ARM_B_COND (code, ARMCOND_EQ, 0);
275 ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_IREGS);
277 ARM_B_COND (code, ARMCOND_EQ, 0);
278 ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_I1);
280 ARM_B_COND (code, ARMCOND_EQ, 0);
281 ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_U1);
283 ARM_B_COND (code, ARMCOND_EQ, 0);
284 ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_I2);
286 ARM_B_COND (code, ARMCOND_EQ, 0);
287 ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_U2);
289 ARM_B_COND (code, ARMCOND_EQ, 0);
290 br_ret [br_ret_index ++] = code;
294 arm_patch (br [0], code);
295 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_LR, 0);
296 br_ret [br_ret_index ++] = code;
299 arm_patch (br [1], code);
300 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_LR, 0);
301 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_LR, 4);
302 br_ret [br_ret_index ++] = code;
305 arm_patch (br [2], code);
306 ARM_LDRSB_IMM (code, ARMREG_R0, ARMREG_LR, 0);
307 br_ret [br_ret_index ++] = code;
310 arm_patch (br [3], code);
311 ARM_LDRB_IMM (code, ARMREG_R0, ARMREG_LR, 0);
312 br_ret [br_ret_index ++] = code;
315 arm_patch (br [4], code);
316 ARM_LDRSH_IMM (code, ARMREG_R0, ARMREG_LR, 0);
317 br_ret [br_ret_index ++] = code;
320 arm_patch (br [5], code);
321 ARM_LDRH_IMM (code, ARMREG_R0, ARMREG_LR, 0);
322 br_ret [br_ret_index ++] = code;
326 arm_patch (br_out, code);
328 /* Marshal return value */
329 ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
330 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal));
332 ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_IREGS);
334 ARM_B_COND (code, ARMCOND_NE, 0);
337 /* Load vtype ret addr from the caller arg regs */
338 ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
339 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_arg_reg));
340 ARM_SHL_IMM (code, ARMREG_IP, ARMREG_IP, 2);
341 ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_IP, fp);
342 ARM_ADD_REG_IMM8 (code, ARMREG_IP, ARMREG_IP, caller_reg_area_offset);
343 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, 0);
344 /* Save both registers for simplicity */
345 ARM_STR_IMM (code, ARMREG_R0, ARMREG_IP, 0);
346 ARM_STR_IMM (code, ARMREG_R1, ARMREG_IP, 4);
347 br_ret [br_ret_index ++] = code;
349 arm_patch (br [0], code);
351 ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_IREG);
353 ARM_B_COND (code, ARMCOND_NE, 0);
356 /* Load vtype ret addr from the caller arg regs */
357 ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
358 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_arg_reg));
359 ARM_SHL_IMM (code, ARMREG_IP, ARMREG_IP, 2);
360 ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_IP, fp);
361 ARM_ADD_REG_IMM8 (code, ARMREG_IP, ARMREG_IP, caller_reg_area_offset);
362 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, 0);
363 /* Save the return value to the buffer pointed to by the vret addr */
364 ARM_STR_IMM (code, ARMREG_R0, ARMREG_IP, 0);
365 br_ret [br_ret_index ++] = code;
367 arm_patch (br [0], code);
369 ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_U1);
371 ARM_B_COND (code, ARMCOND_NE, 0);
374 /* Load vtype ret addr from the caller arg regs */
375 ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
376 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_arg_reg));
377 ARM_SHL_IMM (code, ARMREG_IP, ARMREG_IP, 2);
378 ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_IP, fp);
379 ARM_ADD_REG_IMM8 (code, ARMREG_IP, ARMREG_IP, caller_reg_area_offset);
380 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, 0);
381 /* Save the return value to the buffer pointed to by the vret addr */
382 ARM_STRB_IMM (code, ARMREG_R0, ARMREG_IP, 0);
383 br_ret [br_ret_index ++] = code;
385 arm_patch (br [0], code);
387 /* OUT other cases */
388 br_ret [br_ret_index ++] = code;
391 for (i = 0; i < br_ret_index; ++i)
392 arm_patch (br_ret [i], code);
395 /* Restore registers + stack */
396 ARM_MOV_REG_REG (code, ARMREG_SP, fp);
397 ARM_LDM (code, fp, (1 << fp) | (1 << ARMREG_LR));
398 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, cfa_offset);
400 ARM_BX (code, ARMREG_LR);
402 g_assert ((code - buf) < buf_len);
405 *info = mono_tramp_info_create ("gsharedvt_trampoline", buf, code - buf, ji, unwind_ops);
407 mono_arch_flush_icache (buf, code - buf);
414 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
416 g_assert_not_reached ();
427 mono_arm_start_gsharedvt_call (GSharedVtCallInfo *info, gpointer *caller, gpointer *callee, gpointer mrgctx_reg)
429 g_assert_not_reached ();
434 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)