ce042a400b84568a9c5cca7c2ce6c56d76f32d6f
[mono.git] / mono / mini / tramp-arm-gsharedvt.c
1 /*
2  * tramp-arm-gsharedvt.c: gsharedvt support code for arm
3  *
4  * Authors:
5  *   Zoltan Varga <vargaz@gmail.com>
6  *
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.
9  */
10 #include <config.h>
11 #include <glib.h>
12
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>
20
21 #include "mini.h"
22 #include "mini-arm.h"
23
24 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
25
26
27 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
28
29 static inline guint8*
30 emit_bx (guint8* code, int reg)
31 {
32         if (mono_arm_thumb_supported ())
33                 ARM_BX (code, reg);
34         else
35                 ARM_MOV_REG_REG (code, ARMREG_PC, reg);
36         return code;
37 }
38
39
40 gpointer
41 mono_arm_start_gsharedvt_call (GSharedVtCallInfo *info, gpointer *caller, gpointer *callee, gpointer mrgctx_reg)
42 {
43         int i;
44
45         /*
46          * The caller/callee regs are mapped to slot 0..3, stack slot 0 is mapped to slot 4, etc.
47          */
48
49         /* Set vtype ret arg */
50         if (info->vret_slot != -1) {
51                 callee [info->vret_arg_reg] = &callee [info->vret_slot];
52         }
53
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;
58
59                 switch (arg_marshal) {
60                 case GSHAREDVT_ARG_NONE:
61                         callee [dst] = caller [src];
62                         break;
63                 case GSHAREDVT_ARG_BYVAL_TO_BYREF:
64                         /* gsharedvt argument passed by addr in reg/stack slot */
65                         src = src & 0xffff;
66                         callee [dst] = caller + src;
67                         break;
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;
72                         int j;
73                         gpointer *addr = caller [src_slot];
74
75                         for (j = 0; j < nslots; ++j)
76                                 callee [dst + j] = addr [j];
77                         break;
78                 }
79                 case GSHAREDVT_ARG_BYREF_TO_BYVAL_I1: {
80                         int src_slot = src & 0xf;
81                         gpointer *addr = caller [src_slot];
82
83                         callee [dst] = GINT_TO_POINTER ((int)*(gint8*)addr);
84                         break;
85                 }
86                 case GSHAREDVT_ARG_BYREF_TO_BYVAL_I2: {
87                         int src_slot = src & 0xf;
88                         gpointer *addr = caller [src_slot];
89
90                         callee [dst] = GINT_TO_POINTER ((int)*(gint16*)addr);
91                         break;
92                 }
93                 case GSHAREDVT_ARG_BYREF_TO_BYVAL_U1: {
94                         int src_slot = src & 0xf;
95                         gpointer *addr = caller [src_slot];
96
97                         callee [dst] = GUINT_TO_POINTER ((guint)*(guint8*)addr);
98                         break;
99                 }
100                 case GSHAREDVT_ARG_BYREF_TO_BYVAL_U2: {
101                         int src_slot = src & 0xf;
102                         gpointer *addr = caller [src_slot];
103
104                         callee [dst] = GUINT_TO_POINTER ((guint)*(guint16*)addr);
105                         break;
106                 }
107                 default:
108                         g_assert_not_reached ();
109                         break;
110                 }
111         }
112
113         if (info->vcall_offset != -1) {
114                 MonoObject *this_obj = caller [0];
115
116                 if (G_UNLIKELY (!this_obj))
117                         return NULL;
118                 if (info->vcall_offset == MONO_GSHAREDVT_DEL_INVOKE_VT_OFFSET)
119                         /* delegate invoke */
120                         return ((MonoDelegate*)this_obj)->invoke_impl;
121                 else
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 */
125                 return mrgctx_reg;
126         } else {
127                 return info->addr;
128         }
129 }
130
131 #ifndef DISABLE_JIT
132
133 gpointer
134 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
135 {
136         guint8 *code, *buf;
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;
143
144         buf_len = 512;
145         buf = code = mono_global_codeman_reserve (buf_len);
146
147         arg_reg = ARMREG_R0;
148         /* Registers pushed by the arg trampoline */
149         npushed = 4;
150
151         // ios abi compatible frame
152         fp = ARMREG_R7;
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));
166         info_offset = -4;
167         mrgctx_offset = -8;
168         callee_reg_area_offset = - (6 * 4);
169         caller_reg_area_offset = cfa_offset - (npushed * sizeof (gpointer));
170         lr_offset = 4;
171         /* Save info struct which is in r0 */
172         ARM_STR_IMM (code, arg_reg, fp, info_offset);
173         /* Save rgctx reg */
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));
180
181         /*
182          * The stack now looks like this:
183          * <caller frame>
184          * <saved r0-r3, lr>
185          * <saved fp> <- fp
186          * <our frame>
187          * <callee area> <- sp
188          */
189         g_assert (mono_arm_thumb_supported ());
190
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);
195         /* arg1 == info */
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);
203         /* Make the call */
204         if (aot) {
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);
207                 ARM_B (code, 0);
208                 *(gpointer*)code = NULL;
209                 code += 4;
210                 ARM_LDR_REG_REG (code, ARMREG_IP, ARMREG_PC, ARMREG_IP);
211         } else {
212                 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
213                 ARM_B (code, 0);
214                 *(gpointer*)code = mono_arm_start_gsharedvt_call;
215                 code += 4;
216         }
217         ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
218         code = emit_bx (code, ARMREG_IP);
219         /* Clean up stack */
220         ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, args_size);
221
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));
229         /* Load rgctx */
230         ARM_LDR_IMM (code, MONO_ARCH_RGCTX_REG, fp, mrgctx_offset);
231         /* Make the call */
232 #if 0
233         ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
234         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, addr));
235 #endif
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;
240         code += 4;
241
242         br_ret_index = 0;
243
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));
247
248         ARM_CMP_REG_IMM8 (code, ARMREG_IP, 1);
249         br_out = code;
250         ARM_B_COND (code, ARMCOND_NE, 0);
251
252         /* IN CASE */
253
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));
257
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);
262
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);
270
271         /* Branch to specific marshalling code */
272         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_IREG);
273         br [0] = code;
274         ARM_B_COND (code, ARMCOND_EQ, 0);
275         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_IREGS);
276         br [1] = code;
277         ARM_B_COND (code, ARMCOND_EQ, 0);
278         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_I1);
279         br [2] = code;
280         ARM_B_COND (code, ARMCOND_EQ, 0);
281         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_U1);
282         br [3] = code;
283         ARM_B_COND (code, ARMCOND_EQ, 0);
284         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_I2);
285         br [4] = code;
286         ARM_B_COND (code, ARMCOND_EQ, 0);
287         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_U2);
288         br [5] = code;
289         ARM_B_COND (code, ARMCOND_EQ, 0);
290         br_ret [br_ret_index ++] = code;
291         ARM_B (code, 0);
292
293         /* IN IREG case */
294         arm_patch (br [0], code);
295         ARM_LDR_IMM (code, ARMREG_R0, ARMREG_LR, 0);
296         br_ret [br_ret_index ++] = code;
297         ARM_B (code, 0);
298         /* IN IREGS case */
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;
303         ARM_B (code, 0);
304         /* I1 case */
305         arm_patch (br [2], code);
306         ARM_LDRSB_IMM (code, ARMREG_R0, ARMREG_LR, 0);
307         br_ret [br_ret_index ++] = code;
308         ARM_B (code, 0);
309         /* U1 case */
310         arm_patch (br [3], code);
311         ARM_LDRB_IMM (code, ARMREG_R0, ARMREG_LR, 0);
312         br_ret [br_ret_index ++] = code;
313         ARM_B (code, 0);
314         /* I2 case */
315         arm_patch (br [4], code);
316         ARM_LDRSH_IMM (code, ARMREG_R0, ARMREG_LR, 0);
317         br_ret [br_ret_index ++] = code;
318         ARM_B (code, 0);
319         /* U2 case */
320         arm_patch (br [5], code);
321         ARM_LDRH_IMM (code, ARMREG_R0, ARMREG_LR, 0);
322         br_ret [br_ret_index ++] = code;
323         ARM_B (code, 0);
324
325         /* OUT CASE */
326         arm_patch (br_out, code);
327
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));
331
332         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_IREGS);
333         br [0] = code;
334         ARM_B_COND (code, ARMCOND_NE, 0);
335
336         /* OUT IREGS case */
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;
348         ARM_B (code, 0);
349         arm_patch (br [0], code);
350
351         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_IREG);
352         br [0] = code;
353         ARM_B_COND (code, ARMCOND_NE, 0);
354
355         /* OUT IREG case */
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;
366         ARM_B (code, 0);
367         arm_patch (br [0], code);
368
369         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_U1);
370         br [0] = code;
371         ARM_B_COND (code, ARMCOND_NE, 0);
372
373         /* OUT U1 case */
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;
384         ARM_B (code, 0);
385         arm_patch (br [0], code);
386
387         /* OUT other cases */
388         br_ret [br_ret_index ++] = code;
389         ARM_B (code, 0);
390
391         for (i = 0; i < br_ret_index; ++i)
392                 arm_patch (br_ret [i], code);
393
394         /* Normal return */
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);
399         /* Return */
400         ARM_BX (code, ARMREG_LR);
401
402         g_assert ((code - buf) < buf_len);
403
404         if (info)
405                 *info = mono_tramp_info_create ("gsharedvt_trampoline", buf, code - buf, ji, unwind_ops);
406
407         mono_arch_flush_icache (buf, code - buf);
408         return buf;
409 }
410
411 #else
412
413 gpointer
414 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
415 {
416         g_assert_not_reached ();
417         return NULL;
418 }
419
420 #endif
421
422
423 #else
424
425
426 gpointer
427 mono_arm_start_gsharedvt_call (GSharedVtCallInfo *info, gpointer *caller, gpointer *callee, gpointer mrgctx_reg)
428 {
429         g_assert_not_reached ();
430         return NULL;
431 }
432
433 gpointer
434 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
435 {
436         *info = NULL;
437         return NULL;
438 }
439
440
441 #endif