Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / mini / tramp-arm-gsharedvt.c
1 /**
2  * \file
3  * gsharedvt support code for arm
4  *
5  * Authors:
6  *   Zoltan Varga <vargaz@gmail.com>
7  *
8  * Copyright 2013 Xamarin, Inc (http://www.xamarin.com)
9  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
10  */
11 #include <config.h>
12 #include <glib.h>
13
14 #include <mono/metadata/abi-details.h>
15 #include <mono/metadata/appdomain.h>
16 #include <mono/metadata/marshal.h>
17 #include <mono/metadata/tabledefs.h>
18 #include <mono/metadata/profiler-private.h>
19 #include <mono/arch/arm/arm-codegen.h>
20 #include <mono/arch/arm/arm-vfp-codegen.h>
21
22 #include "mini.h"
23 #include "mini-arm.h"
24
25 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
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 gpointer
40 mono_arm_start_gsharedvt_call (GSharedVtCallInfo *info, gpointer *caller, gpointer *callee, gpointer mrgctx_reg,
41                                                            double *caller_fregs, double *callee_fregs)
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 >> 24) & 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 >> 8) & 0xff;
71                         int src_slot = src & 0xff;
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 & 0xff;
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 & 0xff;
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 & 0xff;
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 & 0xff;
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         /* The slot based approach above is very complicated, use a nested switch instead for fp regs */
114         // FIXME: Use this for the other cases as well
115         if (info->have_fregs) {
116                 CallInfo *caller_cinfo = info->caller_cinfo;
117                 CallInfo *callee_cinfo = info->callee_cinfo;
118                 int aindex;
119
120                 for (aindex = 0; aindex < caller_cinfo->nargs; ++aindex) {
121                         ArgInfo *ainfo = &caller_cinfo->args [aindex];
122                         ArgInfo *ainfo2 = &callee_cinfo->args [aindex];
123
124                         switch (ainfo->storage) {
125                         case RegTypeFP: {
126                                 switch (ainfo2->storage) {
127                                 case RegTypeFP:
128                                         callee_fregs [ainfo2->reg / 2] = caller_fregs [ainfo->reg / 2];
129                                         break;
130                                 case RegTypeGSharedVtInReg:
131                                         callee [ainfo2->reg] = &caller_fregs [ainfo->reg / 2];
132                                         break;
133                                 case RegTypeGSharedVtOnStack: {
134                                         int sslot = ainfo2->offset / 4;
135                                         callee [sslot + 4] = &caller_fregs [ainfo->reg / 2];
136                                         break;
137                                 }
138                                 default:
139                                         g_assert_not_reached ();
140                                         break;
141                                 }
142                                 break;
143                         }
144                         case RegTypeGSharedVtInReg: {
145                                 switch (ainfo2->storage) {
146                                 case RegTypeFP: {
147                                         callee_fregs [ainfo2->reg / 2] = *(double*)caller [ainfo->reg];
148                                         break;
149                                 }
150                                 default:
151                                         break;
152                                 }
153                                 break;
154                         }
155                         case RegTypeGSharedVtOnStack: {
156                                 switch (ainfo2->storage) {
157                                 case RegTypeFP: {
158                                         int sslot = ainfo->offset / 4;
159                                         callee_fregs [ainfo2->reg / 2] = *(double*)caller [sslot + 4];
160                                         break;
161                                 }
162                                 default:
163                                         break;
164                                 }
165                                 break;
166                         }
167                         default:
168                                 break;
169                         }
170                 }
171         }
172
173         if (info->vcall_offset != -1) {
174                 MonoObject *this_obj = caller [0];
175
176                 if (G_UNLIKELY (!this_obj))
177                         return NULL;
178                 if (info->vcall_offset == MONO_GSHAREDVT_DEL_INVOKE_VT_OFFSET)
179                         /* delegate invoke */
180                         return ((MonoDelegate*)this_obj)->invoke_impl;
181                 else
182                         return *(gpointer*)((char*)this_obj->vtable + info->vcall_offset);
183         } else if (info->calli) {
184                 /* The address to call is passed in the mrgctx reg */
185                 return mrgctx_reg;
186         } else {
187                 return info->addr;
188         }
189 }
190
191 #ifndef DISABLE_JIT
192
193 gpointer
194 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
195 {
196         guint8 *code, *buf;
197         int buf_len, cfa_offset;
198         GSList *unwind_ops = NULL;
199         MonoJumpInfo *ji = NULL;
200         guint8 *br_out, *br [16], *br_ret [16];
201         int i, offset, arg_reg, npushed, info_offset, mrgctx_offset;
202         int caller_reg_area_offset, caller_freg_area_offset, callee_reg_area_offset, callee_freg_area_offset;
203         int lr_offset, fp, br_ret_index, args_size;
204
205         buf_len = 784;
206         buf = code = mono_global_codeman_reserve (buf_len);
207
208         arg_reg = ARMREG_R0;
209         /* Registers pushed by the arg trampoline */
210         npushed = 4;
211
212         // ios abi compatible frame
213         fp = ARMREG_R7;
214         cfa_offset = npushed * sizeof (gpointer);
215         mono_add_unwind_op_def_cfa (unwind_ops, code, buf, ARMREG_SP, cfa_offset);
216         ARM_PUSH (code, (1 << fp) | (1 << ARMREG_LR));
217         cfa_offset += 2 * sizeof (gpointer);
218         mono_add_unwind_op_def_cfa_offset (unwind_ops, code, buf, cfa_offset);
219         mono_add_unwind_op_offset (unwind_ops, code, buf, fp, (- cfa_offset));
220         mono_add_unwind_op_offset (unwind_ops, code, buf, ARMREG_LR, ((- cfa_offset) + 4));
221         ARM_MOV_REG_REG (code, fp, ARMREG_SP);
222         mono_add_unwind_op_def_cfa_reg (unwind_ops, code, buf, fp);
223         /* Allocate stack frame */
224         ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 32 + (16 * sizeof (double)));
225         if (MONO_ARCH_FRAME_ALIGNMENT > 8)
226                 ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, (MONO_ARCH_FRAME_ALIGNMENT - 8));
227         offset = 4;
228         info_offset = -offset;
229         offset += 4;
230         mrgctx_offset = -offset;
231         offset += 4 * 4;
232         callee_reg_area_offset = -offset;
233         offset += 8 * 8;
234         caller_freg_area_offset = -offset;
235         offset += 8 * 8;
236         callee_freg_area_offset = -offset;
237
238         caller_reg_area_offset = cfa_offset - (npushed * sizeof (gpointer));
239         lr_offset = 4;
240         /* Save info struct which is in r0 */
241         ARM_STR_IMM (code, arg_reg, fp, info_offset);
242         /* Save rgctx reg */
243         ARM_STR_IMM (code, MONO_ARCH_RGCTX_REG, fp, mrgctx_offset);
244         /* Allocate callee area */
245         ARM_LDR_IMM (code, ARMREG_IP, arg_reg, MONO_STRUCT_OFFSET (GSharedVtCallInfo, stack_usage));
246         ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_IP);
247         /* Allocate callee register area just below the callee area so the slots are correct */
248         ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 4 * sizeof (gpointer));
249         if (mono_arm_is_hard_float ()) {
250                 /* Save caller fregs */
251                 ARM_SUB_REG_IMM8 (code, ARMREG_IP, fp, -caller_freg_area_offset);
252                 for (i = 0; i < 8; ++i)
253                         ARM_FSTD (code, i * 2, ARMREG_IP, (i * sizeof (double)));
254         }
255
256         /*
257          * The stack now looks like this:
258          * <caller frame>
259          * <saved r0-r3, lr>
260          * <saved fp> <- fp
261          * <our frame>
262          * <callee area> <- sp
263          */
264         g_assert (mono_arm_thumb_supported ());
265
266         /* Call start_gsharedvt_call () */
267         /* 6 arguments, needs 2 stack slot, need to clean it up after the call */
268         args_size = 2 * sizeof (gpointer);
269         ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, args_size);
270         /* arg1 == info */
271         ARM_LDR_IMM (code, ARMREG_R0, fp, info_offset);
272         /* arg2 == caller stack area */
273         ARM_ADD_REG_IMM8 (code, ARMREG_R1, fp, cfa_offset - 4 * sizeof (gpointer));
274         /* arg3 == callee stack area */
275         ARM_ADD_REG_IMM8 (code, ARMREG_R2, ARMREG_SP, args_size);
276         /* arg4 == mrgctx reg */
277         ARM_LDR_IMM (code, ARMREG_R3, fp, mrgctx_offset);
278         /* arg5 == caller freg area */
279         ARM_SUB_REG_IMM8 (code, ARMREG_IP, fp, -caller_freg_area_offset);
280         ARM_STR_IMM (code, ARMREG_IP, ARMREG_SP, 0);
281         /* arg6 == callee freg area */
282         ARM_SUB_REG_IMM8 (code, ARMREG_IP, fp, -callee_freg_area_offset);
283         ARM_STR_IMM (code, ARMREG_IP, ARMREG_SP, 4);
284         /* Make the call */
285         if (aot) {
286                 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_arm_start_gsharedvt_call");
287                 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
288                 ARM_B (code, 0);
289                 *(gpointer*)code = NULL;
290                 code += 4;
291                 ARM_LDR_REG_REG (code, ARMREG_IP, ARMREG_PC, ARMREG_IP);
292         } else {
293                 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
294                 ARM_B (code, 0);
295                 *(gpointer*)code = mono_arm_start_gsharedvt_call;
296                 code += 4;
297         }
298         ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
299         code = emit_bx (code, ARMREG_IP);
300         /* Clean up stack */
301         ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, args_size);
302
303         /* Make the real method call */
304         /* R0 contains the addr to call */
305         ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_R0);
306         /* Load argument registers */
307         ARM_LDM (code, ARMREG_SP, (1 << ARMREG_R0) | (1 << ARMREG_R1) | (1 << ARMREG_R2) | (1 << ARMREG_R3));
308         if (mono_arm_is_hard_float ()) {
309                 /* Load argument fregs */
310                 ARM_SUB_REG_IMM8 (code, ARMREG_LR, fp, -callee_freg_area_offset);
311                 for (i = 0; i < 8; ++i)
312                         ARM_FLDD (code, i * 2, ARMREG_LR, (i * sizeof (double)));
313         }
314         /* Pop callee register area */
315         ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 4 * sizeof (gpointer));
316         /* Load rgctx */
317         ARM_LDR_IMM (code, MONO_ARCH_RGCTX_REG, fp, mrgctx_offset);
318         /* Make the call */
319 #if 0
320         ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
321         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, addr));
322 #endif
323         /* mono_arch_find_imt_method () depends on this */
324         ARM_ADD_REG_IMM8 (code, ARMREG_LR, ARMREG_PC, 4);
325         ARM_BX (code, ARMREG_IP);
326         *((gpointer*)code) = NULL;
327         code += 4;
328
329         br_ret_index = 0;
330
331         /* Branch between IN/OUT cases */
332         ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
333         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, gsharedvt_in));
334
335         ARM_CMP_REG_IMM8 (code, ARMREG_IP, 1);
336         br_out = code;
337         ARM_B_COND (code, ARMCOND_NE, 0);
338
339         /* IN CASE */
340
341         /* LR == return marshalling type */
342         ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
343         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal));
344
345         /* Continue if no marshalling required */
346         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_NONE);
347         br_ret [br_ret_index ++] = code;
348         ARM_B_COND (code, ARMCOND_EQ, 0);
349
350         /* Compute vret area address in LR */
351         ARM_LDR_IMM (code, ARMREG_LR, fp, info_offset);
352         ARM_LDR_IMM (code, ARMREG_LR, ARMREG_LR, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_slot));
353         /* The slot value is off by 4 */
354         ARM_SUB_REG_IMM8 (code, ARMREG_LR, ARMREG_LR, 4);
355         ARM_SHL_IMM (code, ARMREG_LR, ARMREG_LR, 2);
356         ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ARMREG_SP);
357
358         /* Branch to specific marshalling code */
359         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_IREG);
360         br [0] = code;
361         ARM_B_COND (code, ARMCOND_EQ, 0);
362         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_IREGS);
363         br [1] = code;
364         ARM_B_COND (code, ARMCOND_EQ, 0);
365         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_I1);
366         br [2] = code;
367         ARM_B_COND (code, ARMCOND_EQ, 0);
368         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_U1);
369         br [3] = code;
370         ARM_B_COND (code, ARMCOND_EQ, 0);
371         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_I2);
372         br [4] = code;
373         ARM_B_COND (code, ARMCOND_EQ, 0);
374         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_U2);
375         br [5] = code;
376         ARM_B_COND (code, ARMCOND_EQ, 0);
377         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_VFP_R4);
378         br [6] = code;
379         ARM_B_COND (code, ARMCOND_EQ, 0);
380         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_VFP_R8);
381         br [7] = code;
382         ARM_B_COND (code, ARMCOND_EQ, 0);
383         br_ret [br_ret_index ++] = code;
384         ARM_B (code, 0);
385
386         /* IN IREG case */
387         arm_patch (br [0], code);
388         ARM_LDR_IMM (code, ARMREG_R0, ARMREG_LR, 0);
389         br_ret [br_ret_index ++] = code;
390         ARM_B (code, 0);
391         /* IN IREGS case */
392         arm_patch (br [1], code);
393         ARM_LDR_IMM (code, ARMREG_R0, ARMREG_LR, 0);
394         ARM_LDR_IMM (code, ARMREG_R1, ARMREG_LR, 4);
395         br_ret [br_ret_index ++] = code;
396         ARM_B (code, 0);
397         /* I1 case */
398         arm_patch (br [2], code);
399         ARM_LDRSB_IMM (code, ARMREG_R0, ARMREG_LR, 0);
400         br_ret [br_ret_index ++] = code;
401         ARM_B (code, 0);
402         /* U1 case */
403         arm_patch (br [3], code);
404         ARM_LDRB_IMM (code, ARMREG_R0, ARMREG_LR, 0);
405         br_ret [br_ret_index ++] = code;
406         ARM_B (code, 0);
407         /* I2 case */
408         arm_patch (br [4], code);
409         ARM_LDRSH_IMM (code, ARMREG_R0, ARMREG_LR, 0);
410         br_ret [br_ret_index ++] = code;
411         ARM_B (code, 0);
412         /* U2 case */
413         arm_patch (br [5], code);
414         ARM_LDRH_IMM (code, ARMREG_R0, ARMREG_LR, 0);
415         br_ret [br_ret_index ++] = code;
416         ARM_B (code, 0);
417         /* R4 case */
418         arm_patch (br [6], code);
419         ARM_FLDS (code, ARM_VFP_D0, ARMREG_LR, 0);
420         code += 4;
421         br_ret [br_ret_index ++] = code;
422         ARM_B (code, 0);
423         /* R8 case */
424         arm_patch (br [7], code);
425         ARM_FLDD (code, ARM_VFP_D0, ARMREG_LR, 0);
426         code += 4;
427         br_ret [br_ret_index ++] = code;
428         ARM_B (code, 0);
429
430         /* OUT CASE */
431         arm_patch (br_out, code);
432
433         /* Marshal return value */
434         ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
435         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal));
436
437         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_IREGS);
438         br [0] = code;
439         ARM_B_COND (code, ARMCOND_NE, 0);
440
441         /* OUT IREGS case */
442         /* Load vtype ret addr from the caller arg regs */
443         ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
444         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_arg_reg));
445         ARM_SHL_IMM (code, ARMREG_IP, ARMREG_IP, 2);
446         ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_IP, fp);
447         ARM_ADD_REG_IMM8 (code, ARMREG_IP, ARMREG_IP, caller_reg_area_offset);
448         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, 0);
449         /* Save both registers for simplicity */
450         ARM_STR_IMM (code, ARMREG_R0, ARMREG_IP, 0);
451         ARM_STR_IMM (code, ARMREG_R1, ARMREG_IP, 4);
452         br_ret [br_ret_index ++] = code;
453         ARM_B (code, 0);
454         arm_patch (br [0], code);
455
456         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_IREG);
457         br [0] = code;
458         ARM_B_COND (code, ARMCOND_NE, 0);
459
460         /* OUT IREG case */
461         /* Load vtype ret addr from the caller arg regs */
462         ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
463         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_arg_reg));
464         ARM_SHL_IMM (code, ARMREG_IP, ARMREG_IP, 2);
465         ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_IP, fp);
466         ARM_ADD_REG_IMM8 (code, ARMREG_IP, ARMREG_IP, caller_reg_area_offset);
467         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, 0);
468         /* Save the return value to the buffer pointed to by the vret addr */
469         ARM_STR_IMM (code, ARMREG_R0, ARMREG_IP, 0);
470         br_ret [br_ret_index ++] = code;
471         ARM_B (code, 0);
472         arm_patch (br [0], code);
473
474         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_U1);
475         br [0] = code;
476         ARM_B_COND (code, ARMCOND_NE, 0);
477
478         /* OUT U1 case */
479         /* Load vtype ret addr from the caller arg regs */
480         ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
481         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_arg_reg));
482         ARM_SHL_IMM (code, ARMREG_IP, ARMREG_IP, 2);
483         ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_IP, fp);
484         ARM_ADD_REG_IMM8 (code, ARMREG_IP, ARMREG_IP, caller_reg_area_offset);
485         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, 0);
486         /* Save the return value to the buffer pointed to by the vret addr */
487         ARM_STRB_IMM (code, ARMREG_R0, ARMREG_IP, 0);
488         br_ret [br_ret_index ++] = code;
489         ARM_B (code, 0);
490         arm_patch (br [0], code);
491
492         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_VFP_R4);
493         br [0] = code;
494         ARM_B_COND (code, ARMCOND_NE, 0);
495
496         /* OUT R4 case */
497         /* Load vtype ret addr from the caller arg regs */
498         ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
499         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_arg_reg));
500         ARM_SHL_IMM (code, ARMREG_IP, ARMREG_IP, 2);
501         ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_IP, fp);
502         ARM_ADD_REG_IMM8 (code, ARMREG_IP, ARMREG_IP, caller_reg_area_offset);
503         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, 0);
504         /* Save the return value to the buffer pointed to by the vret addr */
505         ARM_FSTS (code, ARM_VFP_D0, ARMREG_IP, 0);
506         br_ret [br_ret_index ++] = code;
507         ARM_B (code, 0);
508         arm_patch (br [0], code);
509
510         ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_VFP_R8);
511         br [0] = code;
512         ARM_B_COND (code, ARMCOND_NE, 0);
513
514         /* OUT R8 case */
515         /* Load vtype ret addr from the caller arg regs */
516         ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
517         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_arg_reg));
518         ARM_SHL_IMM (code, ARMREG_IP, ARMREG_IP, 2);
519         ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_IP, fp);
520         ARM_ADD_REG_IMM8 (code, ARMREG_IP, ARMREG_IP, caller_reg_area_offset);
521         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, 0);
522         /* Save the return value to the buffer pointed to by the vret addr */
523         ARM_FSTD (code, ARM_VFP_D0, ARMREG_IP, 0);
524         br_ret [br_ret_index ++] = code;
525         ARM_B (code, 0);
526         arm_patch (br [0], code);
527
528         /* OUT other cases */
529         br_ret [br_ret_index ++] = code;
530         ARM_B (code, 0);
531
532         for (i = 0; i < br_ret_index; ++i)
533                 arm_patch (br_ret [i], code);
534
535         /* Normal return */
536         /* Restore registers + stack */
537         ARM_MOV_REG_REG (code, ARMREG_SP, fp);
538         ARM_LDM (code, fp, (1 << fp) | (1 << ARMREG_LR));
539         ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, cfa_offset);
540         /* Return */
541         ARM_BX (code, ARMREG_LR);
542
543         g_assert ((code - buf) < buf_len);
544
545         if (info)
546                 *info = mono_tramp_info_create ("gsharedvt_trampoline", buf, code - buf, ji, unwind_ops);
547
548         mono_arch_flush_icache (buf, code - buf);
549         return buf;
550 }
551
552 #else
553
554 gpointer
555 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
556 {
557         g_assert_not_reached ();
558         return NULL;
559 }
560
561 #endif
562
563
564 #else
565
566
567 gpointer
568 mono_arm_start_gsharedvt_call (GSharedVtCallInfo *info, gpointer *caller, gpointer *callee, gpointer mrgctx_reg)
569 {
570         g_assert_not_reached ();
571         return NULL;
572 }
573
574 gpointer
575 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
576 {
577         *info = NULL;
578         return NULL;
579 }
580
581
582 #endif