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