Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / mini / tramp-arm64.c
1 /**
2  * \file
3  * JIT trampoline code for ARM64
4  *
5  * Copyright 2013 Xamarin Inc
6  *
7  * Based on tramp-arm.c:
8  * 
9  * Authors:
10  *   Paolo Molaro (lupus@ximian.com)
11  *
12  * (C) 2001-2003 Ximian, Inc.
13  * Copyright 2003-2011 Novell Inc
14  * Copyright 2011 Xamarin Inc
15  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
16  */
17
18 #include "mini.h"
19 #include "debugger-agent.h"
20
21 #include <mono/arch/arm64/arm64-codegen.h>
22 #include <mono/metadata/abi-details.h>
23
24 #ifdef ENABLE_INTERPRETER
25 #include "interp/interp.h"
26 #endif
27
28
29 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
30
31 void
32 mono_arch_patch_callsite (guint8 *method_start, guint8 *code_ptr, guint8 *addr)
33 {
34         mono_arm_patch (code_ptr - 4, addr, MONO_R_ARM64_BL);
35         mono_arch_flush_icache (code_ptr - 4, 4);
36 }
37
38 void
39 mono_arch_patch_plt_entry (guint8 *code, gpointer *got, mgreg_t *regs, guint8 *addr)
40 {
41         guint32 ins;
42         guint64 slot_addr;
43         int disp;
44
45         /* 
46          * Decode the address loaded by the PLT entry emitted by arch_emit_plt_entry () in
47          * aot-compiler.c
48          */
49
50         /* adrp */
51         ins = ((guint32*)code) [0];
52         g_assert (((ins >> 24) & 0x1f) == 0x10);
53         disp = (((ins >> 5) & 0x7ffff) << 2) | ((ins >> 29) & 0x3);
54         /* FIXME: disp is signed */
55         g_assert ((disp >> 20) == 0);
56
57         slot_addr = ((guint64)code + (disp << 12)) & ~0xfff;
58
59         /* add x16, x16, :lo12:got */
60         ins = ((guint32*)code) [1];
61         g_assert (((ins >> 22) & 0x3) == 0);
62         slot_addr += (ins >> 10) & 0xfff;
63
64         /* ldr x16, [x16, <offset>] */
65         ins = ((guint32*)code) [2];
66         g_assert (((ins >> 24) & 0x3f) == 0x39);
67         slot_addr += ((ins >> 10) & 0xfff) * 8;
68
69         g_assert (*(guint64*)slot_addr);
70         *(gpointer*)slot_addr = addr;
71 }
72
73 guint8*
74 mono_arch_get_call_target (guint8 *code)
75 {
76         guint32 imm;
77         int disp;
78
79         code -= 4;
80
81         imm = *(guint32*)code;
82         /* Should be a b/bl */
83         g_assert (((imm >> 26) & 0x7) == 0x5);
84
85         disp = (imm & 0x3ffffff);
86         if ((disp >> 25) != 0)
87                 /* Negative, sing extend to 32 bits */
88                 disp = disp | 0xfc000000;
89
90         return code + (disp * 4);
91 }
92
93 guint32
94 mono_arch_get_plt_info_offset (guint8 *plt_entry, mgreg_t *regs, guint8 *code)
95 {
96         /* The offset is stored as the 5th word of the plt entry */
97         return ((guint32*)plt_entry) [4];
98 }
99
100 #ifndef DISABLE_JIT
101
102 guchar*
103 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot)
104 {
105         guint8 *code, *buf, *tramp, *labels [16];
106         int i, buf_len, imm;
107         int frame_size, offset, gregs_offset, num_fregs, fregs_offset, arg_offset, lmf_offset, res_offset;
108         guint64 gregs_regset;
109         GSList *unwind_ops = NULL;
110         MonoJumpInfo *ji = NULL;
111         char *tramp_name;
112
113         buf_len = 768;
114         buf = code = mono_global_codeman_reserve (buf_len);
115
116         /*
117          * We are getting called by a specific trampoline, ip1 contains the trampoline argument.
118          */
119
120         /* Compute stack frame size and offsets */
121         offset = 0;
122         /* frame block */
123         offset += 2 * 8;
124         /* gregs */
125         gregs_offset = offset;
126         offset += 32 * 8;
127         /* fregs */
128         // FIXME: Save 128 bits
129         /* Only have to save the argument regs */
130         num_fregs = 8;
131         fregs_offset = offset;
132         offset += num_fregs * 8;
133         /* arg */
134         arg_offset = offset;
135         offset += 8;
136         /* result */
137         res_offset = offset;
138         offset += 8;
139         /* LMF */
140         lmf_offset = offset;
141         offset += sizeof (MonoLMF);
142         //offset += 22 * 8;
143         frame_size = ALIGN_TO (offset, MONO_ARCH_FRAME_ALIGNMENT);
144
145         /* Setup stack frame */
146         imm = frame_size;
147         while (imm > 256) {
148                 arm_subx_imm (code, ARMREG_SP, ARMREG_SP, 256);
149                 imm -= 256;
150         }
151         arm_subx_imm (code, ARMREG_SP, ARMREG_SP, imm);
152         arm_stpx (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, 0);
153         arm_movspx (code, ARMREG_FP, ARMREG_SP);
154
155         /* Save gregs */
156         // FIXME: Optimize this
157         gregs_regset = ~((1 << ARMREG_FP) | (1 << ARMREG_SP));
158         code = mono_arm_emit_store_regarray (code, gregs_regset, ARMREG_FP, gregs_offset);
159         /* Save fregs */
160         for (i = 0; i < num_fregs; ++i)
161                 arm_strfpx (code, i, ARMREG_FP, fregs_offset + (i * 8));
162         /* Save trampoline arg */
163         arm_strx (code, ARMREG_IP1, ARMREG_FP, arg_offset);
164
165         /* Setup LMF */
166         arm_addx_imm (code, ARMREG_IP0, ARMREG_FP, lmf_offset);
167         code = mono_arm_emit_store_regset (code, MONO_ARCH_LMF_REGS, ARMREG_IP0, MONO_STRUCT_OFFSET (MonoLMF, gregs));
168         /* Save caller fp */
169         arm_ldrx (code, ARMREG_IP1, ARMREG_FP, 0);
170         arm_strx (code, ARMREG_IP1, ARMREG_IP0, MONO_STRUCT_OFFSET (MonoLMF, gregs) + (MONO_ARCH_LMF_REG_FP * 8));
171         /* Save caller sp */
172         arm_movx (code, ARMREG_IP1, ARMREG_FP);
173         imm = frame_size;
174         while (imm > 256) {
175                 arm_addx_imm (code, ARMREG_IP1, ARMREG_IP1, 256);
176                 imm -= 256;
177         }
178         arm_addx_imm (code, ARMREG_IP1, ARMREG_IP1, imm);
179         arm_strx (code, ARMREG_IP1, ARMREG_IP0, MONO_STRUCT_OFFSET (MonoLMF, gregs) + (MONO_ARCH_LMF_REG_SP * 8));
180         /* Save caller pc */
181         if (tramp_type == MONO_TRAMPOLINE_JUMP)
182                 arm_movx (code, ARMREG_LR, ARMREG_RZR);
183         else
184                 arm_ldrx (code, ARMREG_LR, ARMREG_FP, 8);
185         arm_strx (code, ARMREG_LR, ARMREG_IP0, MONO_STRUCT_OFFSET (MonoLMF, pc));
186
187         /* Save LMF */
188         /* Similar to emit_save_lmf () */
189         if (aot) {
190                 code = mono_arm_emit_aotconst (&ji, code, buf, ARMREG_IP0, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_get_lmf_addr");
191         } else {
192                 tramp = (guint8*)mono_get_lmf_addr;
193                 code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)tramp);
194         }
195         arm_blrx (code, ARMREG_IP0);
196         /* r0 contains the address of the tls slot holding the current lmf */
197         /* ip0 = lmf */
198         arm_addx_imm (code, ARMREG_IP0, ARMREG_FP, lmf_offset);
199         /* lmf->lmf_addr = lmf_addr */
200         arm_strx (code, ARMREG_R0, ARMREG_IP0, MONO_STRUCT_OFFSET (MonoLMF, lmf_addr));
201         /* lmf->previous_lmf = *lmf_addr */
202         arm_ldrx (code, ARMREG_IP1, ARMREG_R0, 0);
203         arm_strx (code, ARMREG_IP1, ARMREG_IP0, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
204         /* *lmf_addr = lmf */
205         arm_strx (code, ARMREG_IP0, ARMREG_R0, 0);
206
207         /* Call the C trampoline function */
208         /* Arg 1 = gregs */
209         arm_addx_imm (code, ARMREG_R0, ARMREG_FP, gregs_offset);
210         /* Arg 2 = caller */
211         if (tramp_type == MONO_TRAMPOLINE_JUMP)
212                 arm_movx (code, ARMREG_R1, ARMREG_RZR);
213         else
214                 arm_ldrx (code, ARMREG_R1, ARMREG_FP, gregs_offset + (ARMREG_LR * 8));
215         /* Arg 3 = arg */
216         if (MONO_TRAMPOLINE_TYPE_HAS_ARG (tramp_type))
217                 /* Passed in r0 */
218                 arm_ldrx (code, ARMREG_R2, ARMREG_FP, gregs_offset + (ARMREG_R0 * 8));
219         else
220                 arm_ldrx (code, ARMREG_R2, ARMREG_FP, arg_offset);
221         /* Arg 4 = trampoline addr */
222         arm_movx (code, ARMREG_R3, ARMREG_RZR);
223
224         if (aot) {
225                 char *icall_name = g_strdup_printf ("trampoline_func_%d", tramp_type);
226                 code = mono_arm_emit_aotconst (&ji, code, buf, ARMREG_IP0, MONO_PATCH_INFO_JIT_ICALL_ADDR, icall_name);
227         } else {
228                 tramp = (guint8*)mono_get_trampoline_func (tramp_type);
229                 code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)tramp);
230         }
231         arm_blrx (code, ARMREG_IP0);
232
233         /* Save the result */
234         arm_strx (code, ARMREG_R0, ARMREG_FP, res_offset);
235
236         /* Restore LMF */
237         /* Similar to emit_restore_lmf () */
238         /* Clobbers ip0/ip1 */
239         /* ip0 = lmf */
240         arm_addx_imm (code, ARMREG_IP0, ARMREG_FP, lmf_offset);
241         /* ip1 = lmf->previous_lmf */
242         arm_ldrx (code, ARMREG_IP1, ARMREG_IP0, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
243         /* ip0 = lmf->lmf_addr */
244         arm_ldrx (code, ARMREG_IP0, ARMREG_IP0, MONO_STRUCT_OFFSET (MonoLMF, lmf_addr));
245         /* *lmf_addr = previous_lmf */
246         arm_strx (code, ARMREG_IP1, ARMREG_IP0, 0);
247
248         /* Check for thread interruption */
249         /* This is not perf critical code so no need to check the interrupt flag */
250         if (aot) {
251                 code = mono_arm_emit_aotconst (&ji, code, buf, ARMREG_IP0, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_thread_force_interruption_checkpoint_noraise");
252         } else {
253                 code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)mono_thread_force_interruption_checkpoint_noraise);
254         }
255         arm_blrx (code, ARMREG_IP0);
256         /* Check whenever there is an exception to be thrown */
257         labels [0] = code;
258         arm_cbnzx (code, ARMREG_R0, 0);
259
260         /* Normal case */
261
262         /* Restore gregs */
263         /* Only have to load the argument regs (r0..r8) and the rgctx reg */
264         code = mono_arm_emit_load_regarray (code, 0x1ff | (1 << ARMREG_LR) | (1 << MONO_ARCH_RGCTX_REG), ARMREG_FP, gregs_offset);
265         /* Restore fregs */
266         for (i = 0; i < num_fregs; ++i)
267                 arm_ldrfpx (code, i, ARMREG_FP, fregs_offset + (i * 8));
268
269         /* Load the result */
270         arm_ldrx (code, ARMREG_IP1, ARMREG_FP, res_offset);
271
272         /* These trampolines return a value */
273         if (tramp_type == MONO_TRAMPOLINE_RGCTX_LAZY_FETCH)
274                 arm_movx (code, ARMREG_R0, ARMREG_IP1);
275
276         /* Cleanup frame */
277         code = mono_arm_emit_destroy_frame (code, frame_size, ((1 << ARMREG_IP0)));
278
279         if (tramp_type == MONO_TRAMPOLINE_RGCTX_LAZY_FETCH)
280                 arm_retx (code, ARMREG_LR);
281         else
282                 arm_brx (code, ARMREG_IP1);
283
284         /* Exception case */
285         mono_arm_patch (labels [0], code, MONO_R_ARM64_CBZ);
286
287         /*
288          * We have an exception we want to throw in the caller's frame, so pop
289          * the trampoline frame and throw from the caller.
290          */
291         code = mono_arm_emit_destroy_frame (code, frame_size, ((1 << ARMREG_IP0)));
292         /* We are in the parent frame, the exception is in x0 */
293         /*
294          * EH is initialized after trampolines, so get the address of the variable
295          * which contains throw_exception, and load it from there.
296          */
297         if (aot) {
298                 /* Not really a jit icall */
299                 code = mono_arm_emit_aotconst (&ji, code, buf, ARMREG_IP0, MONO_PATCH_INFO_JIT_ICALL_ADDR, "throw_exception_addr");
300         } else {
301                 code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)mono_get_throw_exception_addr ());
302         }
303         arm_ldrx (code, ARMREG_IP0, ARMREG_IP0, 0);
304         /* lr contains the return address, the trampoline will use it as the throw site */
305         arm_brx (code, ARMREG_IP0);
306
307         g_assert ((code - buf) < buf_len);
308         mono_arch_flush_icache (buf, code - buf);
309
310         if (info) {
311                 tramp_name = mono_get_generic_trampoline_name (tramp_type);
312                 *info = mono_tramp_info_create (tramp_name, buf, code - buf, ji, unwind_ops);
313                 g_free (tramp_name);
314         }
315
316         return buf;
317 }
318
319 gpointer
320 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
321 {
322         guint8 *code, *buf, *tramp;
323         int buf_len = 64;
324
325         /*
326          * Return a trampoline which calls generic trampoline TRAMP_TYPE passing in ARG1.
327          * Pass the argument in ip1, clobbering ip0.
328          */
329         tramp = mono_get_trampoline_code (tramp_type);
330
331         buf = code = mono_global_codeman_reserve (buf_len);
332
333         code = mono_arm_emit_imm64 (code, ARMREG_IP1, (guint64)arg1);
334         code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)tramp);
335
336         arm_brx (code, ARMREG_IP0);
337
338         g_assert ((code - buf) < buf_len);
339         mono_arch_flush_icache (buf, code - buf);
340         if (code_len)
341                 *code_len = code - buf;
342
343         return buf;
344 }
345
346 gpointer
347 mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr)
348 {
349         guint8 *code, *start;
350         guint32 size = 32;
351         MonoDomain *domain = mono_domain_get ();
352
353         start = code = mono_domain_code_reserve (domain, size);
354         code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)addr);
355         arm_addx_imm (code, ARMREG_R0, ARMREG_R0, sizeof (MonoObject));
356         arm_brx (code, ARMREG_IP0);
357
358         g_assert ((code - start) <= size);
359         mono_arch_flush_icache (start, code - start);
360         return start;
361 }
362
363 gpointer
364 mono_arch_get_static_rgctx_trampoline (gpointer arg, gpointer addr)
365 {
366         guint8 *code, *start;
367         guint32 buf_len = 32;
368         MonoDomain *domain = mono_domain_get ();
369
370         start = code = mono_domain_code_reserve (domain, buf_len);
371         code = mono_arm_emit_imm64 (code, MONO_ARCH_RGCTX_REG, (guint64)arg);
372         code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)addr);
373         arm_brx (code, ARMREG_IP0);
374
375         g_assert ((code - start) <= buf_len);
376
377         mono_arch_flush_icache (start, code - start);
378
379         return start;
380 }
381
382 gpointer
383 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
384 {
385         guint8 *code, *buf;
386         int buf_size;
387         int i, depth, index, njumps;
388         gboolean is_mrgctx;
389         guint8 **rgctx_null_jumps;
390         MonoJumpInfo *ji = NULL;
391         GSList *unwind_ops = NULL;
392         guint8 *tramp;
393         guint32 code_len;
394
395         is_mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
396         index = MONO_RGCTX_SLOT_INDEX (slot);
397         if (is_mrgctx)
398                 index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
399         for (depth = 0; ; ++depth) {
400                 int size = mono_class_rgctx_get_array_size (depth, is_mrgctx);
401
402                 if (index < size - 1)
403                         break;
404                 index -= size - 1;
405         }
406
407         buf_size = 64 + 16 * depth;
408         code = buf = mono_global_codeman_reserve (buf_size);
409
410         rgctx_null_jumps = g_malloc0 (sizeof (guint8*) * (depth + 2));
411         njumps = 0;
412
413         /* The vtable/mrgtx is in R0 */
414         g_assert (MONO_ARCH_VTABLE_REG == ARMREG_R0);
415
416         if (is_mrgctx) {
417                 /* get mrgctx ptr */
418                 arm_movx (code, ARMREG_IP1, ARMREG_R0);
419         } else {
420                 /* load rgctx ptr from vtable */
421                 code = mono_arm_emit_ldrx (code, ARMREG_IP1, ARMREG_R0, MONO_STRUCT_OFFSET (MonoVTable, runtime_generic_context));
422                 /* is the rgctx ptr null? */
423                 /* if yes, jump to actual trampoline */
424                 rgctx_null_jumps [njumps ++] = code;
425                 arm_cbzx (code, ARMREG_IP1, 0);
426         }
427
428         for (i = 0; i < depth; ++i) {
429                 /* load ptr to next array */
430                 if (is_mrgctx && i == 0) {
431                         code = mono_arm_emit_ldrx (code, ARMREG_IP1, ARMREG_IP1, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT);
432                 } else {
433                         code = mono_arm_emit_ldrx (code, ARMREG_IP1, ARMREG_IP1, 0);
434                 }
435                 /* is the ptr null? */
436                 /* if yes, jump to actual trampoline */
437                 rgctx_null_jumps [njumps ++] = code;
438                 arm_cbzx (code, ARMREG_IP1, 0);
439         }
440
441         /* fetch slot */
442         code = mono_arm_emit_ldrx (code, ARMREG_IP1, ARMREG_IP1, sizeof (gpointer) * (index + 1));
443         /* is the slot null? */
444         /* if yes, jump to actual trampoline */
445         rgctx_null_jumps [njumps ++] = code;
446         arm_cbzx (code, ARMREG_IP1, 0);
447         /* otherwise return, result is in IP1 */
448         arm_movx (code, ARMREG_R0, ARMREG_IP1);
449         arm_brx (code, ARMREG_LR);
450
451         g_assert (njumps <= depth + 2);
452         for (i = 0; i < njumps; ++i)
453                 mono_arm_patch (rgctx_null_jumps [i], code, MONO_R_ARM64_CBZ);
454
455         g_free (rgctx_null_jumps);
456
457         /* Slowpath */
458
459         /* Call mono_rgctx_lazy_fetch_trampoline (), passing in the slot as argument */
460         /* The vtable/mrgctx is still in R0 */
461         if (aot) {
462                 code = mono_arm_emit_aotconst (&ji, code, buf, ARMREG_IP0, MONO_PATCH_INFO_JIT_ICALL_ADDR, g_strdup_printf ("specific_trampoline_lazy_fetch_%u", slot));
463         } else {
464                 tramp = mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH, mono_get_root_domain (), &code_len);
465                 code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)tramp);
466         }
467         arm_brx (code, ARMREG_IP0);
468
469         mono_arch_flush_icache (buf, code - buf);
470
471         g_assert (code - buf <= buf_size);
472
473         if (info) {
474                 char *name = mono_get_rgctx_fetch_trampoline_name (slot);
475                 *info = mono_tramp_info_create (name, buf, code - buf, ji, unwind_ops);
476                 g_free (name);
477         }
478
479         return buf;
480 }
481
482 gpointer
483 mono_arch_create_general_rgctx_lazy_fetch_trampoline (MonoTrampInfo **info, gboolean aot)
484 {
485         guint8 *code, *buf;
486         int tramp_size;
487         MonoJumpInfo *ji = NULL;
488         GSList *unwind_ops = NULL;
489
490         g_assert (aot);
491
492         tramp_size = 32;
493
494         code = buf = mono_global_codeman_reserve (tramp_size);
495
496         mono_add_unwind_op_def_cfa (unwind_ops, code, buf, ARMREG_SP, 0);
497
498         // FIXME: Currently, we always go to the slow path.
499         /* Load trampoline addr */
500         arm_ldrx (code, ARMREG_IP0, MONO_ARCH_RGCTX_REG, 8);
501         /* The vtable/mrgctx is in R0 */
502         g_assert (MONO_ARCH_VTABLE_REG == ARMREG_R0);
503         arm_brx (code, ARMREG_IP0);
504
505         mono_arch_flush_icache (buf, code - buf);
506
507         g_assert (code - buf <= tramp_size);
508
509         if (info)
510                 *info = mono_tramp_info_create ("rgctx_fetch_trampoline_general", buf, code - buf, ji, unwind_ops);
511
512         return buf;
513 }
514
515 /*
516  * mono_arch_create_sdb_trampoline:
517  *
518  *   Return a trampoline which captures the current context, passes it to
519  * debugger_agent_single_step_from_context ()/debugger_agent_breakpoint_from_context (),
520  * then restores the (potentially changed) context.
521  */
522 guint8*
523 mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gboolean aot)
524 {
525         int tramp_size = 512;
526         int offset, imm, frame_size, ctx_offset;
527         guint64 gregs_regset;
528         guint8 *code, *buf;
529         GSList *unwind_ops = NULL;
530         MonoJumpInfo *ji = NULL;
531
532         code = buf = mono_global_codeman_reserve (tramp_size);
533
534         /* Compute stack frame size and offsets */
535         offset = 0;
536         /* frame block */
537         offset += 2 * 8;
538         /* MonoContext */
539         ctx_offset = offset;
540         offset += sizeof (MonoContext);
541         offset = ALIGN_TO (offset, MONO_ARCH_FRAME_ALIGNMENT);
542         frame_size = offset;
543
544         // FIXME: Unwind info
545
546         /* Setup stack frame */
547         imm = frame_size;
548         while (imm > 256) {
549                 arm_subx_imm (code, ARMREG_SP, ARMREG_SP, 256);
550                 imm -= 256;
551         }
552         arm_subx_imm (code, ARMREG_SP, ARMREG_SP, imm);
553         arm_stpx (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, 0);
554         arm_movspx (code, ARMREG_FP, ARMREG_SP);
555
556         /* Initialize a MonoContext structure on the stack */
557         /* No need to save fregs */
558         gregs_regset = ~((1 << ARMREG_FP) | (1 << ARMREG_SP));
559         code = mono_arm_emit_store_regarray (code, gregs_regset, ARMREG_FP, ctx_offset + G_STRUCT_OFFSET (MonoContext, regs));
560         /* Save caller fp */
561         arm_ldrx (code, ARMREG_IP1, ARMREG_FP, 0);
562         arm_strx (code, ARMREG_IP1, ARMREG_FP, ctx_offset + G_STRUCT_OFFSET (MonoContext, regs) + (ARMREG_FP * 8));
563         /* Save caller sp */
564         arm_movx (code, ARMREG_IP1, ARMREG_FP);
565         imm = frame_size;
566         while (imm > 256) {
567                 arm_addx_imm (code, ARMREG_IP1, ARMREG_IP1, 256);
568                 imm -= 256;
569         }
570         arm_addx_imm (code, ARMREG_IP1, ARMREG_IP1, imm);
571         arm_strx (code, ARMREG_IP1, ARMREG_FP, ctx_offset + G_STRUCT_OFFSET (MonoContext, regs) + (ARMREG_SP * 8));
572         /* Save caller ip */
573         arm_ldrx (code, ARMREG_IP1, ARMREG_FP, 8);
574         arm_strx (code, ARMREG_IP1, ARMREG_FP, ctx_offset + G_STRUCT_OFFSET (MonoContext, pc));
575
576         /* Call the single step/breakpoint function in sdb */
577         /* Arg1 = ctx */
578         arm_addx_imm (code, ARMREG_R0, ARMREG_FP, ctx_offset);
579         if (aot) {
580                 if (single_step)
581                         code = mono_arm_emit_aotconst (&ji, code, buf, ARMREG_IP0, MONO_PATCH_INFO_JIT_ICALL_ADDR, "debugger_agent_single_step_from_context");
582                 else
583                         code = mono_arm_emit_aotconst (&ji, code, buf, ARMREG_IP0, MONO_PATCH_INFO_JIT_ICALL_ADDR, "debugger_agent_breakpoint_from_context");
584         } else {
585                 gpointer addr = single_step ? debugger_agent_single_step_from_context : debugger_agent_breakpoint_from_context;
586
587                 code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)addr);
588         }
589         arm_blrx (code, ARMREG_IP0);
590
591         /* Restore ctx */
592         /* Save fp/pc into the frame block */
593         arm_ldrx (code, ARMREG_IP0, ARMREG_FP, ctx_offset + G_STRUCT_OFFSET (MonoContext, regs) + (ARMREG_FP * 8));
594         arm_strx (code, ARMREG_IP0, ARMREG_FP, 0);
595         arm_ldrx (code, ARMREG_IP0, ARMREG_FP, ctx_offset + G_STRUCT_OFFSET (MonoContext, pc));
596         arm_strx (code, ARMREG_IP0, ARMREG_FP, 8);
597         gregs_regset = ~((1 << ARMREG_FP) | (1 << ARMREG_SP));
598         code = mono_arm_emit_load_regarray (code, gregs_regset, ARMREG_FP, ctx_offset + G_STRUCT_OFFSET (MonoContext, regs));
599
600         code = mono_arm_emit_destroy_frame (code, frame_size, ((1 << ARMREG_IP0) | (1 << ARMREG_IP1)));
601
602         arm_retx (code, ARMREG_LR);
603
604         mono_arch_flush_icache (code, code - buf);
605         g_assert (code - buf <= tramp_size);
606
607         const char *tramp_name = single_step ? "sdb_single_step_trampoline" : "sdb_breakpoint_trampoline";
608         *info = mono_tramp_info_create (tramp_name, buf, code - buf, ji, unwind_ops);
609
610         return buf;
611 }
612
613 /*
614  * mono_arch_get_enter_icall_trampoline:
615  *
616  *   See tramp-amd64.c for documentation.
617  */
618 gpointer
619 mono_arch_get_enter_icall_trampoline (MonoTrampInfo **info)
620 {
621 #ifdef ENABLE_INTERPRETER
622         const int gregs_num = INTERP_ICALL_TRAMP_IARGS;
623         const int fregs_num = INTERP_ICALL_TRAMP_FARGS;
624
625         guint8 *start = NULL, *code, *label_gexits [gregs_num], *label_fexits [fregs_num], *label_leave_tramp [3], *label_is_float_ret;
626         MonoJumpInfo *ji = NULL;
627         GSList *unwind_ops = NULL;
628         int buf_len, i, framesize = 0, off_methodargs, off_targetaddr;
629
630         buf_len = 512 + 1024;
631         start = code = (guint8 *) mono_global_codeman_reserve (buf_len);
632
633         /* save FP and LR */
634         framesize += 2 * sizeof (mgreg_t);
635
636         off_methodargs = framesize;
637         framesize += sizeof (mgreg_t);
638
639         off_targetaddr = framesize;
640         framesize += sizeof (mgreg_t);
641
642         framesize = ALIGN_TO (framesize, MONO_ARCH_FRAME_ALIGNMENT);
643
644         /* allocate space on stack for argument passing */
645         const int stack_space = ALIGN_TO (((gregs_num - ARMREG_R7) * sizeof (mgreg_t)), MONO_ARCH_FRAME_ALIGNMENT);
646
647         arm_subx_imm (code, ARMREG_SP, ARMREG_SP, stack_space + framesize);
648         arm_stpx (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, stack_space);
649         arm_addx_imm (code, ARMREG_FP, ARMREG_SP, stack_space);
650
651         /* save InterpMethodArguments* onto stack */
652         arm_strx (code, ARMREG_R1, ARMREG_FP, off_methodargs);
653
654         /* save target address onto stack */
655         arm_strx (code, ARMREG_R0, ARMREG_FP, off_targetaddr);
656
657         /* load pointer to InterpMethodArguments* into IP0 */
658         arm_movx (code, ARMREG_IP0, ARMREG_R1);
659
660         /* move flen into R9 */
661         arm_ldrx (code, ARMREG_R9, ARMREG_IP0, MONO_STRUCT_OFFSET (InterpMethodArguments, flen));
662         /* load pointer to fargs into R10 */
663         arm_ldrx (code, ARMREG_R10, ARMREG_IP0, MONO_STRUCT_OFFSET (InterpMethodArguments, fargs));
664
665         for (i = 0; i < fregs_num; ++i) {
666                 arm_cmpx_imm (code, ARMREG_R9, 0);
667                 label_fexits [i] = code;
668                 arm_bcc (code, ARMCOND_EQ, 0);
669
670                 g_assert (i <= ARMREG_D7); /* otherwise, need to pass args on stack */
671                 arm_ldrfpx (code, i, ARMREG_R10, i * sizeof (double));
672                 arm_subx_imm (code, ARMREG_R9, ARMREG_R9, 1);
673         }
674
675         for (i = 0; i < fregs_num; i++)
676                 mono_arm_patch (label_fexits [i], code, MONO_R_ARM64_BCC);
677
678         /* move ilen into R9 */
679         arm_ldrx (code, ARMREG_R9, ARMREG_IP0, MONO_STRUCT_OFFSET (InterpMethodArguments, ilen));
680         /* load pointer to iargs into R10 */
681         arm_ldrx (code, ARMREG_R10, ARMREG_IP0, MONO_STRUCT_OFFSET (InterpMethodArguments, iargs));
682
683         int stack_offset = 0;
684         for (i = 0; i < gregs_num; i++) {
685                 arm_cmpx_imm (code, ARMREG_R9, 0);
686                 label_gexits [i] = code;
687                 arm_bcc (code, ARMCOND_EQ, 0);
688
689                 if (i <= ARMREG_R7) {
690                         arm_ldrx (code, i, ARMREG_R10, i * sizeof (mgreg_t));
691                 } else {
692                         arm_ldrx (code, ARMREG_R11, ARMREG_R10, i * sizeof (mgreg_t));
693                         arm_strx (code, ARMREG_R11, ARMREG_SP, stack_offset);
694                         stack_offset += sizeof (mgreg_t);
695                 }
696                 arm_subx_imm (code, ARMREG_R9, ARMREG_R9, 1);
697         }
698
699         for (i = 0; i < gregs_num; i++)
700                 mono_arm_patch (label_gexits [i], code, MONO_R_ARM64_BCC);
701
702         /* load target addr */
703         arm_ldrx (code, ARMREG_R11, ARMREG_FP, off_targetaddr);
704
705         /* call into native function */
706         arm_blrx (code, ARMREG_R11);
707
708         /* load InterpMethodArguments */
709         arm_ldrx (code, ARMREG_IP0, ARMREG_FP, off_methodargs);
710
711         /* load is_float_ret */
712         arm_ldrx (code, ARMREG_R11, ARMREG_IP0, MONO_STRUCT_OFFSET (InterpMethodArguments, is_float_ret));
713
714         /* check if a float return value is expected */
715         arm_cmpx_imm (code, ARMREG_R11, 0);
716         label_is_float_ret = code;
717         arm_bcc (code, ARMCOND_NE, 0);
718
719         /* greg return */
720         /* load retval */
721         arm_ldrx (code, ARMREG_R11, ARMREG_IP0, MONO_STRUCT_OFFSET (InterpMethodArguments, retval));
722
723         arm_cmpx_imm (code, ARMREG_R11, 0);
724         label_leave_tramp [0] = code;
725         arm_bcc (code, ARMCOND_EQ, 0);
726
727         /* store greg result */
728         arm_strx (code, ARMREG_R0, ARMREG_R11, 0);
729
730         label_leave_tramp [1] = code;
731         arm_bcc (code, ARMCOND_AL, 0);
732
733         /* freg return */
734         mono_arm_patch (label_is_float_ret, code, MONO_R_ARM64_BCC);
735         /* load retval */
736         arm_ldrx (code, ARMREG_R11, ARMREG_IP0, MONO_STRUCT_OFFSET (InterpMethodArguments, retval));
737
738         arm_cmpx_imm (code, ARMREG_R11, 0);
739         label_leave_tramp [2] = code;
740         arm_bcc (code, ARMCOND_EQ, 0);
741
742         /* store freg result */
743         arm_strfpx (code, ARMREG_D0, ARMREG_R11, 0);
744
745         for (i = 0; i < 3; i++)
746                 mono_arm_patch (label_leave_tramp [i], code, MONO_R_ARM64_BCC);
747
748         arm_movspx (code, ARMREG_SP, ARMREG_FP);
749         arm_ldpx (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, 0);
750         arm_addx_imm (code, ARMREG_SP, ARMREG_SP, framesize);
751         arm_retx (code, ARMREG_LR);
752
753         g_assert (code - start < buf_len);
754
755         mono_arch_flush_icache (start, code - start);
756         MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL));
757
758         if (info)
759                 *info = mono_tramp_info_create ("enter_icall_trampoline", start, code - start, ji, unwind_ops);
760
761         return start;
762 #else
763         g_assert_not_reached ();
764         return NULL;
765 #endif /* ENABLE_INTERPRETER */
766 }
767
768 #else /* DISABLE_JIT */
769
770 guchar*
771 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot)
772 {
773         g_assert_not_reached ();
774         return NULL;
775 }
776
777 gpointer
778 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
779 {
780         g_assert_not_reached ();
781         return NULL;
782 }
783
784 gpointer
785 mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr)
786 {
787         g_assert_not_reached ();
788         return NULL;
789 }
790
791 gpointer
792 mono_arch_get_static_rgctx_trampoline (gpointer arg, gpointer addr)
793 {
794         g_assert_not_reached ();
795         return NULL;
796 }
797
798 gpointer
799 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
800 {
801         g_assert_not_reached ();
802         return NULL;
803 }
804
805 gpointer
806 mono_arch_get_nullified_class_init_trampoline (MonoTrampInfo **info)
807 {
808         g_assert_not_reached ();
809         return NULL;
810 }
811
812 guint8*
813 mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gboolean aot)
814 {
815         g_assert_not_reached ();
816         return NULL;
817 }
818
819 gpointer
820 mono_arch_get_enter_icall_trampoline (MonoTrampInfo **info)
821 {
822         g_assert_not_reached ();
823         return NULL;
824 }
825 #endif /* !DISABLE_JIT */