Merge pull request #2803 from BrzVlad/feature-conc-pinned-scan
[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         info_offset = -4;
165         mrgctx_offset = -8;
166         callee_reg_area_offset = - (6 * 4);
167         caller_reg_area_offset = cfa_offset - (npushed * sizeof (gpointer));
168         lr_offset = 4;
169         /* Save info struct which is in r0 */
170         ARM_STR_IMM (code, arg_reg, fp, info_offset);
171         /* Save rgctx reg */
172         ARM_STR_IMM (code, MONO_ARCH_RGCTX_REG, fp, mrgctx_offset);
173         /* Allocate callee area */
174         ARM_LDR_IMM (code, ARMREG_IP, arg_reg, MONO_STRUCT_OFFSET (GSharedVtCallInfo, stack_usage));
175         ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_IP);
176         /* Allocate callee register area just below the callee area so it can be accessed from start_gsharedvt_call using negative offsets */
177         ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 4 * sizeof (gpointer));
178
179         /*
180          * The stack now looks like this:
181          * <caller frame>
182          * <saved r0-r3, lr>
183          * <saved fp> <- fp
184          * <our frame>
185          * <callee area> <- sp
186          */
187         g_assert (mono_arm_thumb_supported ());
188
189         /* Call start_gsharedvt_call () */
190         /* 4 arguments, needs 0 stack slot, need to clean it up after the call */
191         args_size = 0 * sizeof (gpointer);
192         ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, args_size);
193         /* arg1 == info */
194         ARM_LDR_IMM (code, ARMREG_R0, fp, info_offset);
195         /* arg2 == caller stack area */
196         ARM_ADD_REG_IMM8 (code, ARMREG_R1, fp, cfa_offset - 4 * sizeof (gpointer));
197         /* arg3 == callee stack area */
198         ARM_ADD_REG_IMM8 (code, ARMREG_R2, ARMREG_SP, args_size);
199         /* arg4 == mrgctx reg */
200         ARM_LDR_IMM (code, ARMREG_R3, fp, mrgctx_offset);
201         /* Make the call */
202         if (aot) {
203                 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_arm_start_gsharedvt_call");
204                 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
205                 ARM_B (code, 0);
206                 *(gpointer*)code = NULL;
207                 code += 4;
208                 ARM_LDR_REG_REG (code, ARMREG_IP, ARMREG_PC, ARMREG_IP);
209         } else {
210                 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
211                 ARM_B (code, 0);
212                 *(gpointer*)code = mono_arm_start_gsharedvt_call;
213                 code += 4;
214         }
215         ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
216         code = emit_bx (code, ARMREG_IP);
217         /* Clean up stack */
218         ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, args_size);
219
220         /* Make the real method call */
221         /* R0 contains the addr to call */
222         ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_R0);
223         /* Load argument registers */
224         ARM_LDM (code, ARMREG_SP, (1 << ARMREG_R0) | (1 << ARMREG_R1) | (1 << ARMREG_R2) | (1 << ARMREG_R3));
225         /* Pop callee register area */
226         ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 4 * sizeof (gpointer));
227         /* Load rgctx */
228         ARM_LDR_IMM (code, MONO_ARCH_RGCTX_REG, fp, mrgctx_offset);
229         /* Make the call */
230 #if 0
231         ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
232         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, addr));
233 #endif
234         /* mono_arch_find_imt_method () depends on this */
235         ARM_ADD_REG_IMM8 (code, ARMREG_LR, ARMREG_PC, 4);
236         ARM_BX (code, ARMREG_IP);
237         *((gpointer*)code) = NULL;
238         code += 4;
239
240         br_ret_index = 0;
241
242         /* Branch between IN/OUT cases */
243         ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
244         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, gsharedvt_in));
245
246         ARM_CMP_REG_IMM8 (code, ARMREG_IP, 1);
247         br_out = code;
248         ARM_B_COND (code, ARMCOND_NE, 0);
249
250         /* IN CASE */
251
252         /* LR == return marshalling type */
253         ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
254         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal));
255
256         /* Continue if no marshalling required */
257         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_NONE);
258         br_ret [br_ret_index ++] = code;
259         ARM_B_COND (code, ARMCOND_EQ, 0);
260
261         /* Compute vret area address in LR */
262         ARM_LDR_IMM (code, ARMREG_LR, fp, info_offset);
263         ARM_LDR_IMM (code, ARMREG_LR, ARMREG_LR, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_slot));
264         /* The slot value is off by 4 */
265         ARM_SUB_REG_IMM8 (code, ARMREG_LR, ARMREG_LR, 4);
266         ARM_SHL_IMM (code, ARMREG_LR, ARMREG_LR, 2);
267         ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ARMREG_SP);
268
269         /* Branch to specific marshalling code */
270         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_IREG);
271         br [0] = code;
272         ARM_B_COND (code, ARMCOND_EQ, 0);
273         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_IREGS);
274         br [1] = code;
275         ARM_B_COND (code, ARMCOND_EQ, 0);
276         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_I1);
277         br [2] = code;
278         ARM_B_COND (code, ARMCOND_EQ, 0);
279         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_U1);
280         br [3] = code;
281         ARM_B_COND (code, ARMCOND_EQ, 0);
282         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_I2);
283         br [4] = code;
284         ARM_B_COND (code, ARMCOND_EQ, 0);
285         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_U2);
286         br [5] = code;
287         ARM_B_COND (code, ARMCOND_EQ, 0);
288         br_ret [br_ret_index ++] = code;
289         ARM_B (code, 0);
290
291         /* IN IREG case */
292         arm_patch (br [0], code);
293         ARM_LDR_IMM (code, ARMREG_R0, ARMREG_LR, 0);
294         br_ret [br_ret_index ++] = code;
295         ARM_B (code, 0);
296         /* IN IREGS case */
297         arm_patch (br [1], code);
298         ARM_LDR_IMM (code, ARMREG_R0, ARMREG_LR, 0);
299         ARM_LDR_IMM (code, ARMREG_R1, ARMREG_LR, 4);
300         br_ret [br_ret_index ++] = code;
301         ARM_B (code, 0);
302         /* I1 case */
303         arm_patch (br [2], code);
304         ARM_LDRSB_IMM (code, ARMREG_R0, ARMREG_LR, 0);
305         br_ret [br_ret_index ++] = code;
306         ARM_B (code, 0);
307         /* U1 case */
308         arm_patch (br [3], code);
309         ARM_LDRB_IMM (code, ARMREG_R0, ARMREG_LR, 0);
310         br_ret [br_ret_index ++] = code;
311         ARM_B (code, 0);
312         /* I2 case */
313         arm_patch (br [4], code);
314         ARM_LDRSH_IMM (code, ARMREG_R0, ARMREG_LR, 0);
315         br_ret [br_ret_index ++] = code;
316         ARM_B (code, 0);
317         /* U2 case */
318         arm_patch (br [5], code);
319         ARM_LDRH_IMM (code, ARMREG_R0, ARMREG_LR, 0);
320         br_ret [br_ret_index ++] = code;
321         ARM_B (code, 0);
322
323         /* OUT CASE */
324         arm_patch (br_out, code);
325
326         /* Marshal return value */
327         ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
328         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal));
329
330         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_IREGS);
331         br [0] = code;
332         ARM_B_COND (code, ARMCOND_NE, 0);
333
334         /* OUT IREGS case */
335         /* Load vtype ret addr from the caller arg regs */
336         ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
337         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_arg_reg));
338         ARM_SHL_IMM (code, ARMREG_IP, ARMREG_IP, 2);
339         ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_IP, fp);
340         ARM_ADD_REG_IMM8 (code, ARMREG_IP, ARMREG_IP, caller_reg_area_offset);
341         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, 0);
342         /* Save both registers for simplicity */
343         ARM_STR_IMM (code, ARMREG_R0, ARMREG_IP, 0);
344         ARM_STR_IMM (code, ARMREG_R1, ARMREG_IP, 4);
345         br_ret [br_ret_index ++] = code;
346         ARM_B (code, 0);
347         arm_patch (br [0], code);
348
349         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_IREG);
350         br [0] = code;
351         ARM_B_COND (code, ARMCOND_NE, 0);
352
353         /* OUT IREG case */
354         /* Load vtype ret addr from the caller arg regs */
355         ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
356         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_arg_reg));
357         ARM_SHL_IMM (code, ARMREG_IP, ARMREG_IP, 2);
358         ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_IP, fp);
359         ARM_ADD_REG_IMM8 (code, ARMREG_IP, ARMREG_IP, caller_reg_area_offset);
360         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, 0);
361         /* Save the return value to the buffer pointed to by the vret addr */
362         ARM_STR_IMM (code, ARMREG_R0, ARMREG_IP, 0);
363         br_ret [br_ret_index ++] = code;
364         ARM_B (code, 0);
365         arm_patch (br [0], code);
366
367         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_U1);
368         br [0] = code;
369         ARM_B_COND (code, ARMCOND_NE, 0);
370
371         /* OUT U1 case */
372         /* Load vtype ret addr from the caller arg regs */
373         ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
374         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_arg_reg));
375         ARM_SHL_IMM (code, ARMREG_IP, ARMREG_IP, 2);
376         ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_IP, fp);
377         ARM_ADD_REG_IMM8 (code, ARMREG_IP, ARMREG_IP, caller_reg_area_offset);
378         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, 0);
379         /* Save the return value to the buffer pointed to by the vret addr */
380         ARM_STRB_IMM (code, ARMREG_R0, ARMREG_IP, 0);
381         br_ret [br_ret_index ++] = code;
382         ARM_B (code, 0);
383         arm_patch (br [0], code);
384
385         /* OUT other cases */
386         br_ret [br_ret_index ++] = code;
387         ARM_B (code, 0);
388
389         for (i = 0; i < br_ret_index; ++i)
390                 arm_patch (br_ret [i], code);
391
392         /* Normal return */
393         /* Restore registers + stack */
394         ARM_MOV_REG_REG (code, ARMREG_SP, fp);
395         ARM_LDM (code, fp, (1 << fp) | (1 << ARMREG_LR));
396         ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, cfa_offset);
397         /* Return */
398         ARM_BX (code, ARMREG_LR);
399
400         g_assert ((code - buf) < buf_len);
401
402         if (info)
403                 *info = mono_tramp_info_create ("gsharedvt_trampoline", buf, code - buf, ji, unwind_ops);
404
405         mono_arch_flush_icache (buf, code - buf);
406         return buf;
407 }
408
409 #else
410
411 gpointer
412 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
413 {
414         g_assert_not_reached ();
415         return NULL;
416 }
417
418 #endif
419
420
421 #else
422
423
424 gpointer
425 mono_arm_start_gsharedvt_call (GSharedVtCallInfo *info, gpointer *caller, gpointer *callee, gpointer mrgctx_reg)
426 {
427         g_assert_not_reached ();
428         return NULL;
429 }
430
431 gpointer
432 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
433 {
434         *info = NULL;
435         return NULL;
436 }
437
438
439 #endif