Merge pull request #2802 from BrzVlad/feature-evacuation-opt2
[mono.git] / mono / mini / tramp-arm64-gsharedvt.c
1 /*
2  * tramp-arm64-gsharedvt.c: gsharedvt support code for arm64
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 <mono/metadata/abi-details.h>
11
12 #include "mini.h"
13 #include "mini-arm64.h"
14 #include "mini-arm64-gsharedvt.h"
15
16 /*
17  * GSHAREDVT
18  */
19 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
20
21 /*
22  * mono_arch_get_gsharedvt_arg_trampoline:
23  *
24  *   See tramp-x86.c for documentation.
25  */
26 gpointer
27 mono_arch_get_gsharedvt_arg_trampoline (MonoDomain *domain, gpointer arg, gpointer addr)
28 {
29         guint8 *code, *buf;
30         int buf_len = 40;
31
32         /*
33          * Return a trampoline which calls ADDR passing in ARG.
34          * Pass the argument in ip1, clobbering ip0.
35          */
36         buf = code = mono_global_codeman_reserve (buf_len);
37
38         code = mono_arm_emit_imm64 (code, ARMREG_IP1, (guint64)arg);
39         code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)addr);
40
41         arm_brx (code, ARMREG_IP0);
42
43         g_assert ((code - buf) < buf_len);
44         mono_arch_flush_icache (buf, code - buf);
45
46         return buf;
47 }
48
49 gpointer
50 mono_arm_start_gsharedvt_call (GSharedVtCallInfo *info, gpointer *caller, gpointer *callee, gpointer mrgctx_reg)
51 {
52         int i;
53
54         /* Set vtype ret arg */
55         if (info->vret_slot != -1) {
56                 g_assert (info->vret_slot);
57                 callee [info->vret_arg_reg] = &callee [info->vret_slot];
58         }
59
60         for (i = 0; i < info->map_count; ++i) {
61                 int src = info->map [i * 2];
62                 int dst = info->map [(i * 2) + 1];
63                 int arg_marshal = (src >> 18) & 0xf;
64                 int arg_size = (src >> 22) & 0xf;
65
66                 if (G_UNLIKELY (arg_size)) {
67                         int src_offset = (src >> 26) & 0xf;
68                         int dst_offset = (dst >> 26) & 0xf;
69                         int src_slot, dst_slot;
70                         guint8 *src_ptr, *dst_ptr;
71
72                         /*
73                          * Argument passed in part of a stack slot on ios.
74                          * src_offset/dst_offset is the offset within the stack slot.
75                          */
76                         switch (arg_marshal) {
77                         case GSHAREDVT_ARG_NONE:
78                                 src_slot = src & 0xffff;
79                                 dst_slot = dst & 0xffff;
80                                 src_ptr = (guint8*)(caller + src_slot) + src_offset;
81                                 dst_ptr = (guint8*)(callee + dst_slot) + dst_offset;
82                                 break;
83                         case GSHAREDVT_ARG_BYREF_TO_BYVAL:
84                                 src_slot = src & 0x3f;
85                                 dst_slot = dst & 0xffff;
86                                 src_ptr = caller [src_slot];
87                                 dst_ptr = (guint8*)(callee + dst_slot) + dst_offset;
88                                 break;
89                         case GSHAREDVT_ARG_BYVAL_TO_BYREF_HFAR4:
90                         case GSHAREDVT_ARG_BYREF_TO_BYVAL_HFAR4:
91                         case GSHAREDVT_ARG_BYREF_TO_BYREF:
92                                 g_assert_not_reached ();
93                                 break;
94                         case GSHAREDVT_ARG_BYVAL_TO_BYREF:
95                                 src_slot = src & 0x3f;
96                                 src_ptr = caller + src_slot + src_offset;
97                                 callee [dst] = src_ptr;
98                                 break;
99                         default:
100                                 NOT_IMPLEMENTED;
101                                 break;
102                         }
103
104                         if (arg_marshal == GSHAREDVT_ARG_BYVAL_TO_BYREF)
105                                 continue;
106
107                         switch (arg_size) {
108                         case GSHAREDVT_ARG_SIZE_I1:
109                                 *(gint8*)dst_ptr = *(gint8*)src_ptr;
110                                 break;
111                         case GSHAREDVT_ARG_SIZE_U1:
112                                 *(guint8*)dst_ptr = *(guint8*)src_ptr;
113                                 break;
114                         case GSHAREDVT_ARG_SIZE_I2:
115                                 *(gint16*)dst_ptr = *(gint16*)src_ptr;
116                                 break;
117                         case GSHAREDVT_ARG_SIZE_U2:
118                                 *(guint16*)dst_ptr = *(guint16*)src_ptr;
119                                 break;
120                         case GSHAREDVT_ARG_SIZE_I4:
121                                 *(gint32*)dst_ptr = *(gint32*)src_ptr;
122                                 break;
123                         case GSHAREDVT_ARG_SIZE_U4:
124                                 *(guint32*)dst_ptr = *(guint32*)src_ptr;
125                                 break;
126                         default:
127                                 g_assert_not_reached ();
128                         }
129                         continue;
130                 }
131
132                 switch (arg_marshal) {
133                 case GSHAREDVT_ARG_NONE:
134                         callee [dst] = caller [src];
135                         break;
136                 case GSHAREDVT_ARG_BYVAL_TO_BYREF:
137                         /* gsharedvt argument passed by addr in reg/stack slot */
138                         src = src & 0x3f;
139                         callee [dst] = caller + src;
140                         break;
141                 case GSHAREDVT_ARG_BYVAL_TO_BYREF_HFAR4: {
142                         int nslots = (src >> 6) & 0xff;
143                         int src_slot = src & 0x3f;
144                         int j;
145                         float *dst_arr = (float*)(caller + src_slot);
146
147                         /* The r4 hfa is in separate slots, need to compress them together in place */
148                         for (j = 0; j < nslots; ++j)
149                                 dst_arr [j] = *(float*)(caller + src_slot + j);
150
151                         callee [dst] = caller + src_slot;
152                         break;
153                 }
154                 case GSHAREDVT_ARG_BYREF_TO_BYVAL: {
155                         int nslots = (src >> 6) & 0xff;
156                         int src_slot = src & 0x3f;
157                         int j;
158                         gpointer *addr = caller [src_slot];
159
160                         for (j = 0; j < nslots; ++j)
161                                 callee [dst + j] = addr [j];
162                         break;
163                 }
164                 case GSHAREDVT_ARG_BYREF_TO_BYVAL_HFAR4: {
165                         int nslots = (src >> 6) & 0xff;
166                         int src_slot = src & 0x3f;
167                         int j;
168                         guint32 *addr = (guint32*)(caller [src_slot]);
169
170                         /* addr points to an array of floats, need to load them to registers */
171                         for (j = 0; j < nslots; ++j)
172                                 callee [dst + j] = GUINT_TO_POINTER (addr [j]);
173                         break;
174                 }
175                 case GSHAREDVT_ARG_BYREF_TO_BYREF: {
176                         int src_slot = src & 0x3f;
177
178                         callee [dst] = caller [src_slot];
179                         break;
180                 }
181                 default:
182                         g_assert_not_reached ();
183                         break;
184                 }
185         }
186
187         if (info->vcall_offset != -1) {
188                 MonoObject *this_obj = caller [0];
189
190                 if (G_UNLIKELY (!this_obj))
191                         return NULL;
192                 if (info->vcall_offset == MONO_GSHAREDVT_DEL_INVOKE_VT_OFFSET)
193                         /* delegate invoke */
194                         return ((MonoDelegate*)this_obj)->invoke_impl;
195                 else
196                         return *(gpointer*)((char*)this_obj->vtable + info->vcall_offset);
197         } else if (info->calli) {
198                 /* The address to call is passed in the mrgctx reg */
199                 return mrgctx_reg;
200         } else {
201                 return info->addr;
202         }
203 }
204
205 #ifndef DISABLE_JIT
206
207 gpointer
208 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
209 {
210         guint8 *code, *buf;
211         int buf_len, cfa_offset;
212         GSList *unwind_ops = NULL;
213         MonoJumpInfo *ji = NULL;
214         guint8 *br_out, *br [64], *br_ret [64], *bcc_ret [64];
215         int i, n_arg_regs, n_arg_fregs, offset, arg_reg, info_offset, rgctx_arg_reg_offset;
216         int caller_reg_area_offset, callee_reg_area_offset, callee_stack_area_offset;
217         int br_ret_index, bcc_ret_index;
218
219         buf_len = 2048;
220         buf = code = mono_global_codeman_reserve (buf_len);
221
222         /*
223          * We are being called by an gsharedvt arg trampoline, the info argument is in IP1.
224          */
225         arg_reg = ARMREG_IP1;
226         n_arg_regs = NUM_GSHAREDVT_ARG_GREGS;
227         n_arg_fregs = NUM_GSHAREDVT_ARG_FREGS;
228
229         /* Compute stack frame size and offsets */
230         offset = 0;
231         /* frame block */
232         offset += 2 * 8;
233         /* info argument */
234         info_offset = offset;
235         offset += 8;
236         /* saved rgctx */
237         rgctx_arg_reg_offset = offset;
238         offset += 8;
239         /* alignment */
240         offset += 8;
241         /* argument regs */
242         caller_reg_area_offset = offset;
243         offset += (n_arg_regs + n_arg_fregs) * 8;
244
245         /* We need the argument regs to be saved at the top of the frame */
246         g_assert (offset % MONO_ARCH_FRAME_ALIGNMENT == 0);
247
248         cfa_offset = offset;
249
250         /* Setup frame */
251         arm_stpx_pre (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, -cfa_offset);
252         mono_add_unwind_op_def_cfa (unwind_ops, code, buf, ARMREG_SP, cfa_offset);
253         mono_add_unwind_op_offset (unwind_ops, code, buf, ARMREG_FP, -cfa_offset + 0);
254         mono_add_unwind_op_offset (unwind_ops, code, buf, ARMREG_LR, -cfa_offset + 8);
255         arm_movspx (code, ARMREG_FP, ARMREG_SP);
256         mono_add_unwind_op_def_cfa_reg (unwind_ops, code, buf, ARMREG_FP);
257
258         /* Save info argument */
259         arm_strx (code, arg_reg, ARMREG_FP, info_offset);
260
261         /* Save rgxctx */
262         arm_strx (code, MONO_ARCH_RGCTX_REG, ARMREG_FP, rgctx_arg_reg_offset);
263
264         /* Save argument regs below the stack arguments */
265         for (i = 0; i < n_arg_regs; ++i)
266                 arm_strx (code, i, ARMREG_SP, caller_reg_area_offset + (i * 8));
267         // FIXME: Only do this if fp regs are used
268         for (i = 0; i < n_arg_fregs; ++i)
269                 arm_strfpx (code, i, ARMREG_SP, caller_reg_area_offset + ((n_arg_regs + i) * 8));
270
271         /* Allocate callee area */
272         arm_ldrw (code, ARMREG_IP0, arg_reg, MONO_STRUCT_OFFSET (GSharedVtCallInfo, stack_usage));
273         arm_movspx (code, ARMREG_LR, ARMREG_SP);
274         arm_subx (code, ARMREG_LR, ARMREG_LR, ARMREG_IP0);
275         arm_movspx (code, ARMREG_SP, ARMREG_LR);
276         /* Allocate callee register area just below the callee area so it can be accessed from start_gsharedvt_call using negative offsets */
277         /* The + 8 is for alignment */
278         callee_reg_area_offset = 8;
279         callee_stack_area_offset = callee_reg_area_offset + (n_arg_regs * sizeof (gpointer));
280         arm_subx_imm (code, ARMREG_SP, ARMREG_SP, ((n_arg_regs + n_arg_fregs) * sizeof (gpointer)) + 8);
281
282         /*
283          * The stack now looks like this:
284          * <caller frame>
285          * <saved r0-r8>
286          * <our frame>
287          * <saved fp, lr> <- fp
288          * <callee area> <- sp
289          */
290
291         /* Call start_gsharedvt_call () */
292         /* arg1 == info */
293         arm_ldrx (code, ARMREG_R0, ARMREG_FP, info_offset);
294         /* arg2 = caller stack area */
295         arm_addx_imm (code, ARMREG_R1, ARMREG_FP, caller_reg_area_offset);
296         /* arg3 == callee stack area */
297         arm_addx_imm (code, ARMREG_R2, ARMREG_SP, callee_reg_area_offset);
298         /* arg4 = mrgctx reg */
299         arm_ldrx (code, ARMREG_R3, ARMREG_FP, rgctx_arg_reg_offset);
300
301         if (aot)
302                 code = mono_arm_emit_aotconst (&ji, code, buf, ARMREG_IP0, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_arm_start_gsharedvt_call");
303         else
304                 code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)mono_arm_start_gsharedvt_call);
305         arm_blrx (code, ARMREG_IP0);
306
307         /* Make the real method call */
308         /* R0 contains the addr to call */
309         arm_movx (code, ARMREG_IP1, ARMREG_R0);
310         /* Load rgxctx */
311         arm_ldrx (code, MONO_ARCH_RGCTX_REG, ARMREG_FP, rgctx_arg_reg_offset);
312         /* Load argument registers */
313         // FIXME:
314         for (i = 0; i < n_arg_regs; ++i)
315                 arm_ldrx (code, i, ARMREG_SP, callee_reg_area_offset + (i * 8));
316         // FIXME: Only do this if needed
317         for (i = 0; i < n_arg_fregs; ++i)
318                 arm_ldrfpx (code, i, ARMREG_SP, callee_reg_area_offset + ((n_arg_regs + i) * 8));
319         /* Clear callee reg area */
320         arm_addx_imm (code, ARMREG_SP, ARMREG_SP, ((n_arg_regs + n_arg_fregs) * sizeof (gpointer)) + 8);
321         /* Make the call */
322         arm_blrx (code, ARMREG_IP1);
323
324         br_ret_index = 0;
325         bcc_ret_index = 0;
326
327         // FIXME: Use a switch
328         /* Branch between IN/OUT cases */
329         arm_ldrx (code, ARMREG_IP1, ARMREG_FP, info_offset);
330         arm_ldrw (code, ARMREG_IP1, ARMREG_IP1, MONO_STRUCT_OFFSET (GSharedVtCallInfo, gsharedvt_in));
331         br_out = code;
332         arm_cbzx (code, ARMREG_IP1, 0);
333
334         /* IN CASE */
335
336         /* IP1 == return marshalling type */
337         arm_ldrx (code, ARMREG_IP1, ARMREG_FP, info_offset);
338         arm_ldrw (code, ARMREG_IP1, ARMREG_IP1, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal));
339
340         /* Continue if no marshalling required */
341         // FIXME: Use cmpx_imm
342         code = mono_arm_emit_imm64 (code, ARMREG_IP0, GSHAREDVT_RET_NONE);
343         arm_cmpx (code, ARMREG_IP0, ARMREG_IP1);
344         bcc_ret [bcc_ret_index ++] = code;
345         arm_bcc (code, ARMCOND_EQ, 0);
346
347         /* Compute vret area address in LR */
348         arm_ldrx (code, ARMREG_LR, ARMREG_FP, info_offset);
349         arm_ldrw (code, ARMREG_LR, ARMREG_LR, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_slot));
350         arm_subx_imm (code, ARMREG_LR, ARMREG_LR, n_arg_regs + n_arg_fregs);
351         arm_lslx (code, ARMREG_LR, ARMREG_LR, 3);
352         arm_movspx (code, ARMREG_IP0, ARMREG_SP);
353         arm_addx (code, ARMREG_LR, ARMREG_IP0, ARMREG_LR);
354
355         /* Branch to specific marshalling code */
356         for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
357                 code = mono_arm_emit_imm64 (code, ARMREG_IP0, i);
358                 arm_cmpx (code, ARMREG_IP0, ARMREG_IP1);
359                 br [i] = code;
360                 arm_bcc (code, ARMCOND_EQ, 0);
361         }
362
363         arm_brk (code, 0);
364
365         /*
366          * The address of the return value area is in LR, have to load it into
367          * registers.
368          */
369         for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
370                 mono_arm_patch (br [i], code, MONO_R_ARM64_BCC);
371                 switch (i) {
372                 case GSHAREDVT_RET_NONE:
373                         break;
374                 case GSHAREDVT_RET_I8:
375                         arm_ldrx (code, ARMREG_R0, ARMREG_LR, 0);
376                         break;
377                 case GSHAREDVT_RET_I1:
378                         arm_ldrsbx (code, ARMREG_R0, ARMREG_LR, 0);
379                         break;
380                 case GSHAREDVT_RET_U1:
381                         arm_ldrb (code, ARMREG_R0, ARMREG_LR, 0);
382                         break;
383                 case GSHAREDVT_RET_I2:
384                         arm_ldrshx (code, ARMREG_R0, ARMREG_LR, 0);
385                         break;
386                 case GSHAREDVT_RET_U2:
387                         arm_ldrh (code, ARMREG_R0, ARMREG_LR, 0);
388                         break;
389                 case GSHAREDVT_RET_I4:
390                         arm_ldrswx (code, ARMREG_R0, ARMREG_LR, 0);
391                         break;
392                 case GSHAREDVT_RET_U4:
393                         arm_ldrw (code, ARMREG_R0, ARMREG_LR, 0);
394                         break;
395                 case GSHAREDVT_RET_R8:
396                         arm_ldrfpx (code, ARMREG_D0, ARMREG_LR, 0);
397                         break;
398                 case GSHAREDVT_RET_R4:
399                         arm_ldrfpw (code, ARMREG_D0, ARMREG_LR, 0);
400                         break;
401                 case GSHAREDVT_RET_IREGS_1:
402                 case GSHAREDVT_RET_IREGS_2:
403                 case GSHAREDVT_RET_IREGS_3:
404                 case GSHAREDVT_RET_IREGS_4:
405                 case GSHAREDVT_RET_IREGS_5:
406                 case GSHAREDVT_RET_IREGS_6:
407                 case GSHAREDVT_RET_IREGS_7:
408                 case GSHAREDVT_RET_IREGS_8: {
409                         int j;
410
411                         for (j = 0; j < i - GSHAREDVT_RET_IREGS_1 + 1; ++j)
412                                 arm_ldrx (code, j, ARMREG_LR, j * 8);
413                         break;
414                 }
415                 case GSHAREDVT_RET_HFAR8_1:
416                 case GSHAREDVT_RET_HFAR8_2:
417                 case GSHAREDVT_RET_HFAR8_3:
418                 case GSHAREDVT_RET_HFAR8_4: {
419                         int j;
420
421                         for (j = 0; j < i - GSHAREDVT_RET_HFAR8_1 + 1; ++j)
422                                 arm_ldrfpx (code, j, ARMREG_LR, j * 8);
423                         break;
424                 }
425                 case GSHAREDVT_RET_HFAR4_1:
426                 case GSHAREDVT_RET_HFAR4_2:
427                 case GSHAREDVT_RET_HFAR4_3:
428                 case GSHAREDVT_RET_HFAR4_4: {
429                         int j;
430
431                         for (j = 0; j < i - GSHAREDVT_RET_HFAR4_1 + 1; ++j)
432                                 arm_ldrfpw (code, j, ARMREG_LR, j * 4);
433                         break;
434                 }
435                 default:
436                         g_assert_not_reached ();
437                         break;
438                 }
439                 br_ret [br_ret_index ++] = code;
440                 arm_b (code, 0);
441         }
442
443         /* OUT CASE */
444         mono_arm_patch (br_out, code, MONO_R_ARM64_CBZ);
445
446         /* Compute vret area address in LR */
447         arm_ldrx (code, ARMREG_LR, ARMREG_FP, caller_reg_area_offset + (ARMREG_R8 * 8));
448
449         /* IP1 == return marshalling type */
450         arm_ldrx (code, ARMREG_IP1, ARMREG_FP, info_offset);
451         arm_ldrw (code, ARMREG_IP1, ARMREG_IP1, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal));
452
453         /* Branch to specific marshalling code */
454         for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
455                 code = mono_arm_emit_imm64 (code, ARMREG_IP0, i);
456                 arm_cmpx (code, ARMREG_IP0, ARMREG_IP1);
457                 br [i] = code;
458                 arm_bcc (code, ARMCOND_EQ, 0);
459         }
460
461         /*
462          * The return value is in registers, need to save to the return area passed by the caller in
463          * R8.
464          */
465         for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
466                 mono_arm_patch (br [i], code, MONO_R_ARM64_BCC);
467                 switch (i) {
468                 case GSHAREDVT_RET_NONE:
469                         break;
470                 case GSHAREDVT_RET_I8:
471                         arm_strx (code, ARMREG_R0, ARMREG_LR, 0);
472                         break;
473                 case GSHAREDVT_RET_I1:
474                 case GSHAREDVT_RET_U1:
475                         arm_strb (code, ARMREG_R0, ARMREG_LR, 0);
476                         break;
477                 case GSHAREDVT_RET_I2:
478                 case GSHAREDVT_RET_U2:
479                         arm_strh (code, ARMREG_R0, ARMREG_LR, 0);
480                         break;
481                 case GSHAREDVT_RET_I4:
482                 case GSHAREDVT_RET_U4:
483                         arm_strw (code, ARMREG_R0, ARMREG_LR, 0);
484                         break;
485                 case GSHAREDVT_RET_R8:
486                         arm_strfpx (code, ARMREG_D0, ARMREG_LR, 0);
487                         break;
488                 case GSHAREDVT_RET_R4:
489                         arm_strfpw (code, ARMREG_D0, ARMREG_LR, 0);
490                         break;
491                 case GSHAREDVT_RET_IREGS_1:
492                 case GSHAREDVT_RET_IREGS_2:
493                 case GSHAREDVT_RET_IREGS_3:
494                 case GSHAREDVT_RET_IREGS_4:
495                 case GSHAREDVT_RET_IREGS_5:
496                 case GSHAREDVT_RET_IREGS_6:
497                 case GSHAREDVT_RET_IREGS_7:
498                 case GSHAREDVT_RET_IREGS_8: {
499                         int j;
500
501                         for (j = 0; j < i - GSHAREDVT_RET_IREGS_1 + 1; ++j)
502                                 arm_strx (code, j, ARMREG_LR, j * 8);
503                         break;
504                 }
505                 case GSHAREDVT_RET_HFAR8_1:
506                 case GSHAREDVT_RET_HFAR8_2:
507                 case GSHAREDVT_RET_HFAR8_3:
508                 case GSHAREDVT_RET_HFAR8_4: {
509                         int j;
510
511                         for (j = 0; j < i - GSHAREDVT_RET_HFAR8_1 + 1; ++j)
512                                 arm_strfpx (code, j, ARMREG_LR, j * 8);
513                         break;
514                 }
515                 case GSHAREDVT_RET_HFAR4_1:
516                 case GSHAREDVT_RET_HFAR4_2:
517                 case GSHAREDVT_RET_HFAR4_3:
518                 case GSHAREDVT_RET_HFAR4_4: {
519                         int j;
520
521                         for (j = 0; j < i - GSHAREDVT_RET_HFAR4_1 + 1; ++j)
522                                 arm_strfpw (code, j, ARMREG_LR, j * 4);
523                         break;
524                 }
525                 default:
526                         arm_brk (code, i);
527                         break;
528                 }
529                 br_ret [br_ret_index ++] = code;
530                 arm_b (code, 0);
531         }
532
533         arm_brk (code, 0);
534
535         for (i = 0; i < br_ret_index; ++i)
536                 mono_arm_patch (br_ret [i], code, MONO_R_ARM64_B);
537         for (i = 0; i < bcc_ret_index; ++i)
538                 mono_arm_patch (bcc_ret [i], code, MONO_R_ARM64_BCC);
539
540         /* Normal return */
541         arm_movspx (code, ARMREG_SP, ARMREG_FP);
542         arm_ldpx_post (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, offset);
543         arm_retx (code, ARMREG_LR);
544
545         g_assert ((code - buf) < buf_len);
546
547         if (info)
548                 *info = mono_tramp_info_create ("gsharedvt_trampoline", buf, code - buf, ji, unwind_ops);
549
550         mono_arch_flush_icache (buf, code - buf);
551         return buf;
552 }
553
554 #else
555
556 gpointer
557 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
558 {
559         g_assert_not_reached ();
560         return NULL;
561 }
562
563 #endif
564
565 #endif /* MONO_ARCH_GSHAREDVT_SUPPORTED */