3 * JIT trampoline code for ARM64
5 * Copyright 2013 Xamarin Inc
7 * Based on tramp-arm.c:
10 * Paolo Molaro (lupus@ximian.com)
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.
19 #include "mini-runtime.h"
20 #include "debugger-agent.h"
22 #include <mono/arch/arm64/arm64-codegen.h>
23 #include <mono/metadata/abi-details.h>
25 #ifndef DISABLE_INTERPRETER
26 #include "interp/interp.h"
31 mono_arch_patch_callsite (guint8
*method_start
, guint8
*code_ptr
, guint8
*addr
)
33 mono_arm_patch (code_ptr
- 4, addr
, MONO_R_ARM64_BL
);
34 mono_arch_flush_icache (code_ptr
- 4, 4);
38 mono_arch_patch_plt_entry (guint8
*code
, gpointer
*got
, host_mgreg_t
*regs
, guint8
*addr
)
45 * Decode the address loaded by the PLT entry emitted by arch_emit_plt_entry () in
50 ins
= ((guint32
*)code
) [0];
51 g_assert (((ins
>> 24) & 0x1f) == 0x10);
52 disp
= (((ins
>> 5) & 0x7ffff) << 2) | ((ins
>> 29) & 0x3);
53 /* FIXME: disp is signed */
54 g_assert ((disp
>> 20) == 0);
56 slot_addr
= ((guint64
)code
+ (disp
<< 12)) & ~0xfff;
58 /* add x16, x16, :lo12:got */
59 ins
= ((guint32
*)code
) [1];
60 g_assert (((ins
>> 22) & 0x3) == 0);
61 slot_addr
+= (ins
>> 10) & 0xfff;
63 /* ldr x16, [x16, <offset>] */
64 ins
= ((guint32
*)code
) [2];
65 g_assert (((ins
>> 24) & 0x3f) == 0x39);
66 slot_addr
+= ((ins
>> 10) & 0xfff) * 8;
68 g_assert (*(guint64
*)slot_addr
);
69 *(gpointer
*)slot_addr
= addr
;
73 mono_arch_get_call_target (guint8
*code
)
80 imm
= *(guint32
*)code
;
81 /* Should be a b/bl */
82 g_assert (((imm
>> 26) & 0x7) == 0x5);
84 disp
= (imm
& 0x3ffffff);
85 if ((disp
>> 25) != 0)
86 /* Negative, sing extend to 32 bits */
87 disp
= disp
| 0xfc000000;
89 return code
+ (disp
* 4);
93 mono_arch_get_plt_info_offset (guint8
*plt_entry
, host_mgreg_t
*regs
, guint8
*code
)
95 /* The offset is stored as the 5th word of the plt entry */
96 return ((guint32
*)plt_entry
) [4];
102 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type
, MonoTrampInfo
**info
, gboolean aot
)
104 guint8
*code
, *buf
, *tramp
, *labels
[16];
106 int frame_size
, offset
, gregs_offset
, num_fregs
, fregs_offset
, arg_offset
, lmf_offset
, res_offset
;
107 guint64 gregs_regset
;
108 GSList
*unwind_ops
= NULL
;
109 MonoJumpInfo
*ji
= NULL
;
113 buf
= code
= mono_global_codeman_reserve (buf_len
);
116 * We are getting called by a specific trampoline, ip1 contains the trampoline argument.
119 /* Compute stack frame size and offsets */
124 gregs_offset
= offset
;
127 // FIXME: Save 128 bits
128 /* Only have to save the argument regs */
130 fregs_offset
= offset
;
131 offset
+= num_fregs
* 8;
140 offset
+= sizeof (MonoLMF
);
142 frame_size
= ALIGN_TO (offset
, MONO_ARCH_FRAME_ALIGNMENT
);
144 /* Setup stack frame */
146 mono_add_unwind_op_def_cfa (unwind_ops
, code
, buf
, ARMREG_SP
, 0);
148 arm_subx_imm (code
, ARMREG_SP
, ARMREG_SP
, 256);
150 mono_add_unwind_op_def_cfa_offset (unwind_ops
, code
, buf
, frame_size
- imm
);
152 arm_subx_imm (code
, ARMREG_SP
, ARMREG_SP
, imm
);
153 mono_add_unwind_op_def_cfa_offset (unwind_ops
, code
, buf
, frame_size
);
155 arm_stpx (code
, ARMREG_FP
, ARMREG_LR
, ARMREG_SP
, 0);
156 mono_add_unwind_op_offset (unwind_ops
, code
, buf
, ARMREG_LR
, -frame_size
+ 8);
157 mono_add_unwind_op_offset (unwind_ops
, code
, buf
, ARMREG_FP
, -frame_size
);
159 arm_movspx (code
, ARMREG_FP
, ARMREG_SP
);
160 mono_add_unwind_op_def_cfa_reg (unwind_ops
, code
, buf
, ARMREG_FP
);
163 // FIXME: Optimize this
164 gregs_regset
= ~((1 << ARMREG_FP
) | (1 << ARMREG_SP
));
165 code
= mono_arm_emit_store_regarray (code
, gregs_regset
, ARMREG_FP
, gregs_offset
);
167 for (i
= 0; i
< num_fregs
; ++i
)
168 arm_strfpx (code
, i
, ARMREG_FP
, fregs_offset
+ (i
* 8));
169 /* Save trampoline arg */
170 arm_strx (code
, ARMREG_IP1
, ARMREG_FP
, arg_offset
);
173 arm_addx_imm (code
, ARMREG_IP0
, ARMREG_FP
, lmf_offset
);
174 code
= mono_arm_emit_store_regset (code
, MONO_ARCH_LMF_REGS
, ARMREG_IP0
, MONO_STRUCT_OFFSET (MonoLMF
, gregs
));
176 arm_ldrx (code
, ARMREG_IP1
, ARMREG_FP
, 0);
177 arm_strx (code
, ARMREG_IP1
, ARMREG_IP0
, MONO_STRUCT_OFFSET (MonoLMF
, gregs
) + (MONO_ARCH_LMF_REG_FP
* 8));
179 arm_movx (code
, ARMREG_IP1
, ARMREG_FP
);
182 arm_addx_imm (code
, ARMREG_IP1
, ARMREG_IP1
, 256);
185 arm_addx_imm (code
, ARMREG_IP1
, ARMREG_IP1
, imm
);
186 arm_strx (code
, ARMREG_IP1
, ARMREG_IP0
, MONO_STRUCT_OFFSET (MonoLMF
, gregs
) + (MONO_ARCH_LMF_REG_SP
* 8));
188 if (tramp_type
== MONO_TRAMPOLINE_JUMP
)
189 arm_movx (code
, ARMREG_LR
, ARMREG_RZR
);
191 arm_ldrx (code
, ARMREG_LR
, ARMREG_FP
, 8);
192 arm_strx (code
, ARMREG_LR
, ARMREG_IP0
, MONO_STRUCT_OFFSET (MonoLMF
, pc
));
195 /* Similar to emit_save_lmf () */
197 code
= mono_arm_emit_aotconst (&ji
, code
, buf
, ARMREG_IP0
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, "mono_get_lmf_addr");
199 tramp
= (guint8
*)mono_get_lmf_addr
;
200 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, (guint64
)tramp
);
202 arm_blrx (code
, ARMREG_IP0
);
203 /* r0 contains the address of the tls slot holding the current lmf */
205 arm_addx_imm (code
, ARMREG_IP0
, ARMREG_FP
, lmf_offset
);
206 /* lmf->lmf_addr = lmf_addr */
207 arm_strx (code
, ARMREG_R0
, ARMREG_IP0
, MONO_STRUCT_OFFSET (MonoLMF
, lmf_addr
));
208 /* lmf->previous_lmf = *lmf_addr */
209 arm_ldrx (code
, ARMREG_IP1
, ARMREG_R0
, 0);
210 arm_strx (code
, ARMREG_IP1
, ARMREG_IP0
, MONO_STRUCT_OFFSET (MonoLMF
, previous_lmf
));
211 /* *lmf_addr = lmf */
212 arm_strx (code
, ARMREG_IP0
, ARMREG_R0
, 0);
214 /* Call the C trampoline function */
216 arm_addx_imm (code
, ARMREG_R0
, ARMREG_FP
, gregs_offset
);
218 if (tramp_type
== MONO_TRAMPOLINE_JUMP
)
219 arm_movx (code
, ARMREG_R1
, ARMREG_RZR
);
221 arm_ldrx (code
, ARMREG_R1
, ARMREG_FP
, gregs_offset
+ (ARMREG_LR
* 8));
223 if (MONO_TRAMPOLINE_TYPE_HAS_ARG (tramp_type
))
225 arm_ldrx (code
, ARMREG_R2
, ARMREG_FP
, gregs_offset
+ (ARMREG_R0
* 8));
227 arm_ldrx (code
, ARMREG_R2
, ARMREG_FP
, arg_offset
);
228 /* Arg 4 = trampoline addr */
229 arm_movx (code
, ARMREG_R3
, ARMREG_RZR
);
232 char *icall_name
= g_strdup_printf ("trampoline_func_%d", tramp_type
);
233 code
= mono_arm_emit_aotconst (&ji
, code
, buf
, ARMREG_IP0
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, icall_name
);
235 tramp
= (guint8
*)mono_get_trampoline_func (tramp_type
);
236 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, (guint64
)tramp
);
238 arm_blrx (code
, ARMREG_IP0
);
240 /* Save the result */
241 arm_strx (code
, ARMREG_R0
, ARMREG_FP
, res_offset
);
244 /* Similar to emit_restore_lmf () */
245 /* Clobbers ip0/ip1 */
247 arm_addx_imm (code
, ARMREG_IP0
, ARMREG_FP
, lmf_offset
);
248 /* ip1 = lmf->previous_lmf */
249 arm_ldrx (code
, ARMREG_IP1
, ARMREG_IP0
, MONO_STRUCT_OFFSET (MonoLMF
, previous_lmf
));
250 /* ip0 = lmf->lmf_addr */
251 arm_ldrx (code
, ARMREG_IP0
, ARMREG_IP0
, MONO_STRUCT_OFFSET (MonoLMF
, lmf_addr
));
252 /* *lmf_addr = previous_lmf */
253 arm_strx (code
, ARMREG_IP1
, ARMREG_IP0
, 0);
255 /* Check for thread interruption */
256 /* This is not perf critical code so no need to check the interrupt flag */
258 code
= mono_arm_emit_aotconst (&ji
, code
, buf
, ARMREG_IP0
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, "mono_thread_force_interruption_checkpoint_noraise");
260 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, (guint64
)mono_thread_force_interruption_checkpoint_noraise
);
262 arm_blrx (code
, ARMREG_IP0
);
263 /* Check whenever there is an exception to be thrown */
265 arm_cbnzx (code
, ARMREG_R0
, 0);
270 /* Only have to load the argument regs (r0..r8) and the rgctx reg */
271 code
= mono_arm_emit_load_regarray (code
, 0x1ff | (1 << ARMREG_LR
) | (1 << MONO_ARCH_RGCTX_REG
), ARMREG_FP
, gregs_offset
);
273 for (i
= 0; i
< num_fregs
; ++i
)
274 arm_ldrfpx (code
, i
, ARMREG_FP
, fregs_offset
+ (i
* 8));
276 /* Load the result */
277 arm_ldrx (code
, ARMREG_IP1
, ARMREG_FP
, res_offset
);
279 /* These trampolines return a value */
280 if (tramp_type
== MONO_TRAMPOLINE_RGCTX_LAZY_FETCH
)
281 arm_movx (code
, ARMREG_R0
, ARMREG_IP1
);
284 code
= mono_arm_emit_destroy_frame (code
, frame_size
, ((1 << ARMREG_IP0
)));
286 if (tramp_type
== MONO_TRAMPOLINE_RGCTX_LAZY_FETCH
)
287 arm_retx (code
, ARMREG_LR
);
289 arm_brx (code
, ARMREG_IP1
);
292 mono_arm_patch (labels
[0], code
, MONO_R_ARM64_CBZ
);
295 * We have an exception we want to throw in the caller's frame, so pop
296 * the trampoline frame and throw from the caller.
298 code
= mono_arm_emit_destroy_frame (code
, frame_size
, ((1 << ARMREG_IP0
)));
299 /* We are in the parent frame, the exception is in x0 */
301 * EH is initialized after trampolines, so get the address of the variable
302 * which contains throw_exception, and load it from there.
305 /* Not really a jit icall */
306 code
= mono_arm_emit_aotconst (&ji
, code
, buf
, ARMREG_IP0
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, "rethrow_preserve_exception_addr");
308 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, (guint64
)mono_get_rethrow_preserve_exception_addr ());
310 arm_ldrx (code
, ARMREG_IP0
, ARMREG_IP0
, 0);
311 /* lr contains the return address, the trampoline will use it as the throw site */
312 arm_brx (code
, ARMREG_IP0
);
314 g_assert ((code
- buf
) < buf_len
);
315 mono_arch_flush_icache (buf
, code
- buf
);
316 MONO_PROFILER_RAISE (jit_code_buffer
, (buf
, code
- buf
, MONO_PROFILER_CODE_BUFFER_HELPER
, NULL
));
319 tramp_name
= mono_get_generic_trampoline_name (tramp_type
);
320 *info
= mono_tramp_info_create (tramp_name
, buf
, code
- buf
, ji
, unwind_ops
);
328 mono_arch_create_specific_trampoline (gpointer arg1
, MonoTrampolineType tramp_type
, MonoDomain
*domain
, guint32
*code_len
)
330 guint8
*code
, *buf
, *tramp
;
334 * Return a trampoline which calls generic trampoline TRAMP_TYPE passing in ARG1.
335 * Pass the argument in ip1, clobbering ip0.
337 tramp
= mono_get_trampoline_code (tramp_type
);
339 buf
= code
= mono_domain_code_reserve (domain
, buf_len
);
341 code
= mono_arm_emit_imm64 (code
, ARMREG_IP1
, (guint64
)arg1
);
342 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, (guint64
)tramp
);
344 arm_brx (code
, ARMREG_IP0
);
346 g_assert ((code
- buf
) < buf_len
);
347 mono_arch_flush_icache (buf
, code
- buf
);
348 MONO_PROFILER_RAISE (jit_code_buffer
, (buf
, code
- buf
, MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE
, mono_get_generic_trampoline_simple_name (tramp_type
)));
351 *code_len
= code
- buf
;
357 mono_arch_get_unbox_trampoline (MonoMethod
*m
, gpointer addr
)
359 guint8
*code
, *start
;
361 MonoDomain
*domain
= mono_domain_get ();
363 start
= code
= mono_domain_code_reserve (domain
, size
);
364 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, (guint64
)addr
);
365 arm_addx_imm (code
, ARMREG_R0
, ARMREG_R0
, MONO_ABI_SIZEOF (MonoObject
));
366 arm_brx (code
, ARMREG_IP0
);
368 g_assert ((code
- start
) <= size
);
369 mono_arch_flush_icache (start
, code
- start
);
370 MONO_PROFILER_RAISE (jit_code_buffer
, (start
, code
- start
, MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE
, m
));
375 mono_arch_get_static_rgctx_trampoline (gpointer arg
, gpointer addr
)
377 guint8
*code
, *start
;
378 guint32 buf_len
= 32;
379 MonoDomain
*domain
= mono_domain_get ();
381 start
= code
= mono_domain_code_reserve (domain
, buf_len
);
382 code
= mono_arm_emit_imm64 (code
, MONO_ARCH_RGCTX_REG
, (guint64
)arg
);
383 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, (guint64
)addr
);
384 arm_brx (code
, ARMREG_IP0
);
386 g_assert ((code
- start
) <= buf_len
);
388 mono_arch_flush_icache (start
, code
- start
);
389 MONO_PROFILER_RAISE (jit_code_buffer
, (start
, code
- start
, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE
, NULL
));
395 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot
, MonoTrampInfo
**info
, gboolean aot
)
399 int i
, depth
, index
, njumps
;
401 guint8
**rgctx_null_jumps
;
402 MonoJumpInfo
*ji
= NULL
;
403 GSList
*unwind_ops
= NULL
;
407 is_mrgctx
= MONO_RGCTX_SLOT_IS_MRGCTX (slot
);
408 index
= MONO_RGCTX_SLOT_INDEX (slot
);
410 index
+= MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT
/ sizeof (target_mgreg_t
);
411 for (depth
= 0; ; ++depth
) {
412 int size
= mono_class_rgctx_get_array_size (depth
, is_mrgctx
);
414 if (index
< size
- 1)
419 buf_size
= 64 + 16 * depth
;
420 code
= buf
= mono_global_codeman_reserve (buf_size
);
422 rgctx_null_jumps
= g_malloc0 (sizeof (guint8
*) * (depth
+ 2));
425 /* The vtable/mrgtx is in R0 */
426 g_assert (MONO_ARCH_VTABLE_REG
== ARMREG_R0
);
430 arm_movx (code
, ARMREG_IP1
, ARMREG_R0
);
432 /* load rgctx ptr from vtable */
433 code
= mono_arm_emit_ldrx (code
, ARMREG_IP1
, ARMREG_R0
, MONO_STRUCT_OFFSET (MonoVTable
, runtime_generic_context
));
434 /* is the rgctx ptr null? */
435 /* if yes, jump to actual trampoline */
436 rgctx_null_jumps
[njumps
++] = code
;
437 arm_cbzx (code
, ARMREG_IP1
, 0);
440 for (i
= 0; i
< depth
; ++i
) {
441 /* load ptr to next array */
442 if (is_mrgctx
&& i
== 0) {
443 code
= mono_arm_emit_ldrx (code
, ARMREG_IP1
, ARMREG_IP1
, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT
);
445 code
= mono_arm_emit_ldrx (code
, ARMREG_IP1
, ARMREG_IP1
, 0);
447 /* is the ptr null? */
448 /* if yes, jump to actual trampoline */
449 rgctx_null_jumps
[njumps
++] = code
;
450 arm_cbzx (code
, ARMREG_IP1
, 0);
454 code
= mono_arm_emit_ldrx (code
, ARMREG_IP1
, ARMREG_IP1
, sizeof (target_mgreg_t
) * (index
+ 1));
455 /* is the slot null? */
456 /* if yes, jump to actual trampoline */
457 rgctx_null_jumps
[njumps
++] = code
;
458 arm_cbzx (code
, ARMREG_IP1
, 0);
459 /* otherwise return, result is in IP1 */
460 arm_movx (code
, ARMREG_R0
, ARMREG_IP1
);
461 arm_brx (code
, ARMREG_LR
);
463 g_assert (njumps
<= depth
+ 2);
464 for (i
= 0; i
< njumps
; ++i
)
465 mono_arm_patch (rgctx_null_jumps
[i
], code
, MONO_R_ARM64_CBZ
);
467 g_free (rgctx_null_jumps
);
471 /* Call mono_rgctx_lazy_fetch_trampoline (), passing in the slot as argument */
472 /* The vtable/mrgctx is still in R0 */
474 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
));
476 tramp
= (guint8
*)mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot
), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH
, mono_get_root_domain (), &code_len
);
477 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, (guint64
)tramp
);
479 arm_brx (code
, ARMREG_IP0
);
481 mono_arch_flush_icache (buf
, code
- buf
);
482 MONO_PROFILER_RAISE (jit_code_buffer
, (buf
, code
- buf
, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE
, NULL
));
484 g_assert (code
- buf
<= buf_size
);
487 char *name
= mono_get_rgctx_fetch_trampoline_name (slot
);
488 *info
= mono_tramp_info_create (name
, buf
, code
- buf
, ji
, unwind_ops
);
496 mono_arch_create_general_rgctx_lazy_fetch_trampoline (MonoTrampInfo
**info
, gboolean aot
)
500 MonoJumpInfo
*ji
= NULL
;
501 GSList
*unwind_ops
= NULL
;
507 code
= buf
= mono_global_codeman_reserve (tramp_size
);
509 mono_add_unwind_op_def_cfa (unwind_ops
, code
, buf
, ARMREG_SP
, 0);
511 // FIXME: Currently, we always go to the slow path.
512 /* Load trampoline addr */
513 arm_ldrx (code
, ARMREG_IP0
, MONO_ARCH_RGCTX_REG
, 8);
514 /* The vtable/mrgctx is in R0 */
515 g_assert (MONO_ARCH_VTABLE_REG
== ARMREG_R0
);
516 arm_brx (code
, ARMREG_IP0
);
518 mono_arch_flush_icache (buf
, code
- buf
);
519 MONO_PROFILER_RAISE (jit_code_buffer
, (buf
, code
- buf
, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE
, NULL
));
521 g_assert (code
- buf
<= tramp_size
);
524 *info
= mono_tramp_info_create ("rgctx_fetch_trampoline_general", buf
, code
- buf
, ji
, unwind_ops
);
530 * mono_arch_create_sdb_trampoline:
532 * Return a trampoline which captures the current context, passes it to
533 * mini_get_dbg_callbacks ()->single_step_from_context ()/mini_get_dbg_callbacks ()->breakpoint_from_context (),
534 * then restores the (potentially changed) context.
537 mono_arch_create_sdb_trampoline (gboolean single_step
, MonoTrampInfo
**info
, gboolean aot
)
539 int tramp_size
= 512;
540 int offset
, imm
, frame_size
, ctx_offset
;
541 guint64 gregs_regset
;
543 GSList
*unwind_ops
= NULL
;
544 MonoJumpInfo
*ji
= NULL
;
546 code
= buf
= mono_global_codeman_reserve (tramp_size
);
548 /* Compute stack frame size and offsets */
554 offset
+= sizeof (MonoContext
);
555 offset
= ALIGN_TO (offset
, MONO_ARCH_FRAME_ALIGNMENT
);
558 // FIXME: Unwind info
560 /* Setup stack frame */
563 arm_subx_imm (code
, ARMREG_SP
, ARMREG_SP
, 256);
566 arm_subx_imm (code
, ARMREG_SP
, ARMREG_SP
, imm
);
567 arm_stpx (code
, ARMREG_FP
, ARMREG_LR
, ARMREG_SP
, 0);
568 arm_movspx (code
, ARMREG_FP
, ARMREG_SP
);
570 /* Initialize a MonoContext structure on the stack */
571 /* No need to save fregs */
572 gregs_regset
= ~((1 << ARMREG_FP
) | (1 << ARMREG_SP
));
573 code
= mono_arm_emit_store_regarray (code
, gregs_regset
, ARMREG_FP
, ctx_offset
+ G_STRUCT_OFFSET (MonoContext
, regs
));
575 arm_ldrx (code
, ARMREG_IP1
, ARMREG_FP
, 0);
576 arm_strx (code
, ARMREG_IP1
, ARMREG_FP
, ctx_offset
+ G_STRUCT_OFFSET (MonoContext
, regs
) + (ARMREG_FP
* 8));
578 arm_movx (code
, ARMREG_IP1
, ARMREG_FP
);
581 arm_addx_imm (code
, ARMREG_IP1
, ARMREG_IP1
, 256);
584 arm_addx_imm (code
, ARMREG_IP1
, ARMREG_IP1
, imm
);
585 arm_strx (code
, ARMREG_IP1
, ARMREG_FP
, ctx_offset
+ G_STRUCT_OFFSET (MonoContext
, regs
) + (ARMREG_SP
* 8));
587 arm_ldrx (code
, ARMREG_IP1
, ARMREG_FP
, 8);
588 arm_strx (code
, ARMREG_IP1
, ARMREG_FP
, ctx_offset
+ G_STRUCT_OFFSET (MonoContext
, pc
));
590 /* Call the single step/breakpoint function in sdb */
592 arm_addx_imm (code
, ARMREG_R0
, ARMREG_FP
, ctx_offset
);
595 code
= mono_arm_emit_aotconst (&ji
, code
, buf
, ARMREG_IP0
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, "debugger_agent_single_step_from_context");
597 code
= mono_arm_emit_aotconst (&ji
, code
, buf
, ARMREG_IP0
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, "debugger_agent_breakpoint_from_context");
599 void (*addr
) (MonoContext
*ctx
) = single_step
? mini_get_dbg_callbacks ()->single_step_from_context
: mini_get_dbg_callbacks ()->breakpoint_from_context
;
601 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, (guint64
)addr
);
603 arm_blrx (code
, ARMREG_IP0
);
606 /* Save fp/pc into the frame block */
607 arm_ldrx (code
, ARMREG_IP0
, ARMREG_FP
, ctx_offset
+ G_STRUCT_OFFSET (MonoContext
, regs
) + (ARMREG_FP
* 8));
608 arm_strx (code
, ARMREG_IP0
, ARMREG_FP
, 0);
609 arm_ldrx (code
, ARMREG_IP0
, ARMREG_FP
, ctx_offset
+ G_STRUCT_OFFSET (MonoContext
, pc
));
610 arm_strx (code
, ARMREG_IP0
, ARMREG_FP
, 8);
611 gregs_regset
= ~((1 << ARMREG_FP
) | (1 << ARMREG_SP
));
612 code
= mono_arm_emit_load_regarray (code
, gregs_regset
, ARMREG_FP
, ctx_offset
+ G_STRUCT_OFFSET (MonoContext
, regs
));
614 code
= mono_arm_emit_destroy_frame (code
, frame_size
, ((1 << ARMREG_IP0
) | (1 << ARMREG_IP1
)));
616 arm_retx (code
, ARMREG_LR
);
618 mono_arch_flush_icache (code
, code
- buf
);
619 MONO_PROFILER_RAISE (jit_code_buffer
, (buf
, code
- buf
, MONO_PROFILER_CODE_BUFFER_HELPER
, NULL
));
620 g_assert (code
- buf
<= tramp_size
);
622 const char *tramp_name
= single_step
? "sdb_single_step_trampoline" : "sdb_breakpoint_trampoline";
623 *info
= mono_tramp_info_create (tramp_name
, buf
, code
- buf
, ji
, unwind_ops
);
629 * mono_arch_get_interp_to_native_trampoline:
631 * See tramp-amd64.c for documentation.
634 mono_arch_get_interp_to_native_trampoline (MonoTrampInfo
**info
)
636 #ifndef DISABLE_INTERPRETER
637 guint8
*start
= NULL
, *code
;
638 guint8
*label_start_copy
, *label_exit_copy
;
639 MonoJumpInfo
*ji
= NULL
;
640 GSList
*unwind_ops
= NULL
;
641 int buf_len
, i
, framesize
= 0, off_methodargs
, off_targetaddr
;
643 buf_len
= 512 + 1024;
644 start
= code
= (guint8
*) mono_global_codeman_reserve (buf_len
);
647 framesize
+= 2 * sizeof (host_mgreg_t
);
649 off_methodargs
= framesize
;
650 framesize
+= sizeof (host_mgreg_t
);
652 off_targetaddr
= framesize
;
653 framesize
+= sizeof (host_mgreg_t
);
655 framesize
= ALIGN_TO (framesize
, MONO_ARCH_FRAME_ALIGNMENT
);
657 arm_subx_imm (code
, ARMREG_SP
, ARMREG_SP
, framesize
);
658 arm_stpx (code
, ARMREG_FP
, ARMREG_LR
, ARMREG_SP
, 0);
659 arm_movspx (code
, ARMREG_FP
, ARMREG_SP
);
661 /* save CallContext* onto stack */
662 arm_strx (code
, ARMREG_R1
, ARMREG_FP
, off_methodargs
);
664 /* save target address onto stack */
665 arm_strx (code
, ARMREG_R0
, ARMREG_FP
, off_targetaddr
);
667 /* allocate the stack space necessary for the call */
668 arm_ldrx (code
, ARMREG_R0
, ARMREG_R1
, MONO_STRUCT_OFFSET (CallContext
, stack_size
));
669 arm_movspx (code
, ARMREG_IP0
, ARMREG_SP
);
670 arm_subx (code
, ARMREG_IP0
, ARMREG_IP0
, ARMREG_R0
);
671 arm_movspx (code
, ARMREG_SP
, ARMREG_IP0
);
673 /* copy stack from the CallContext, IP0 = dest, IP1 = source */
674 arm_movspx (code
, ARMREG_IP0
, ARMREG_SP
);
675 arm_ldrx (code
, ARMREG_IP1
, ARMREG_R1
, MONO_STRUCT_OFFSET (CallContext
, stack
));
677 label_start_copy
= code
;
679 arm_cmpx_imm (code
, ARMREG_R0
, 0);
680 label_exit_copy
= code
;
681 arm_bcc (code
, ARMCOND_EQ
, 0);
682 arm_ldrx (code
, ARMREG_R2
, ARMREG_IP1
, 0);
683 arm_strx (code
, ARMREG_R2
, ARMREG_IP0
, 0);
684 arm_addx_imm (code
, ARMREG_IP0
, ARMREG_IP0
, sizeof (host_mgreg_t
));
685 arm_addx_imm (code
, ARMREG_IP1
, ARMREG_IP1
, sizeof (host_mgreg_t
));
686 arm_subx_imm (code
, ARMREG_R0
, ARMREG_R0
, sizeof (host_mgreg_t
));
687 arm_b (code
, label_start_copy
);
688 mono_arm_patch (label_exit_copy
, code
, MONO_R_ARM64_BCC
);
690 /* Load CallContext* into IP0 */
691 arm_ldrx (code
, ARMREG_IP0
, ARMREG_FP
, off_methodargs
);
693 /* set all general purpose registers from CallContext */
694 for (i
= 0; i
< PARAM_REGS
+ 1; i
++)
695 arm_ldrx (code
, i
, ARMREG_IP0
, MONO_STRUCT_OFFSET (CallContext
, gregs
) + i
* sizeof (host_mgreg_t
));
697 /* set all floating registers from CallContext */
698 for (i
= 0; i
< FP_PARAM_REGS
; i
++)
699 arm_ldrfpx (code
, i
, ARMREG_IP0
, MONO_STRUCT_OFFSET (CallContext
, fregs
) + i
* sizeof (double));
701 /* load target addr */
702 arm_ldrx (code
, ARMREG_IP0
, ARMREG_FP
, off_targetaddr
);
704 /* call into native function */
705 arm_blrx (code
, ARMREG_IP0
);
707 /* load CallContext* */
708 arm_ldrx (code
, ARMREG_IP0
, ARMREG_FP
, off_methodargs
);
710 /* set all general purpose registers to CallContext */
711 for (i
= 0; i
< PARAM_REGS
; i
++)
712 arm_strx (code
, i
, ARMREG_IP0
, MONO_STRUCT_OFFSET (CallContext
, gregs
) + i
* sizeof (host_mgreg_t
));
714 /* set all floating registers to CallContext */
715 for (i
= 0; i
< FP_PARAM_REGS
; i
++)
716 arm_strfpx (code
, i
, ARMREG_IP0
, MONO_STRUCT_OFFSET (CallContext
, fregs
) + i
* sizeof (double));
718 arm_movspx (code
, ARMREG_SP
, ARMREG_FP
);
719 arm_ldpx (code
, ARMREG_FP
, ARMREG_LR
, ARMREG_SP
, 0);
720 arm_addx_imm (code
, ARMREG_SP
, ARMREG_SP
, framesize
);
721 arm_retx (code
, ARMREG_LR
);
723 g_assert (code
- start
< buf_len
);
725 mono_arch_flush_icache (start
, code
- start
);
726 MONO_PROFILER_RAISE (jit_code_buffer
, (start
, code
- start
, MONO_PROFILER_CODE_BUFFER_HELPER
, NULL
));
729 *info
= mono_tramp_info_create ("interp_to_native_trampoline", start
, code
- start
, ji
, unwind_ops
);
733 g_assert_not_reached ();
735 #endif /* DISABLE_INTERPRETER */
739 mono_arch_get_native_to_interp_trampoline (MonoTrampInfo
**info
)
741 #ifndef DISABLE_INTERPRETER
742 guint8
*start
= NULL
, *code
;
743 MonoJumpInfo
*ji
= NULL
;
744 GSList
*unwind_ops
= NULL
;
746 int framesize
, offset
, ccontext_offset
;
749 start
= code
= (guint8
*) mono_global_codeman_reserve (buf_len
);
751 /* Allocate frame (FP + LR + CallContext) */
752 offset
= 2 * sizeof (host_mgreg_t
);
753 ccontext_offset
= offset
;
754 offset
+= sizeof (CallContext
);
755 framesize
= ALIGN_TO (offset
, MONO_ARCH_FRAME_ALIGNMENT
);
757 mono_add_unwind_op_def_cfa (unwind_ops
, code
, start
, ARMREG_SP
, 0);
759 arm_subx_imm (code
, ARMREG_SP
, ARMREG_SP
, framesize
);
760 mono_add_unwind_op_def_cfa_offset (unwind_ops
, code
, start
, framesize
);
762 arm_stpx (code
, ARMREG_FP
, ARMREG_LR
, ARMREG_SP
, 0);
763 mono_add_unwind_op_offset (unwind_ops
, code
, start
, ARMREG_LR
, -framesize
+ 8);
764 mono_add_unwind_op_offset (unwind_ops
, code
, start
, ARMREG_FP
, -framesize
);
766 arm_movspx (code
, ARMREG_FP
, ARMREG_SP
);
767 mono_add_unwind_op_def_cfa_reg (unwind_ops
, code
, start
, ARMREG_FP
);
769 /* save all general purpose registers into the CallContext */
770 for (i
= 0; i
< PARAM_REGS
+ 1; i
++)
771 arm_strx (code
, i
, ARMREG_FP
, ccontext_offset
+ MONO_STRUCT_OFFSET (CallContext
, gregs
) + i
* sizeof (host_mgreg_t
));
773 /* save all floating registers into the CallContext */
774 for (i
= 0; i
< FP_PARAM_REGS
; i
++)
775 arm_strfpx (code
, i
, ARMREG_FP
, ccontext_offset
+ MONO_STRUCT_OFFSET (CallContext
, fregs
) + i
* sizeof (double));
777 /* set the stack pointer to the value at call site */
778 arm_addx_imm (code
, ARMREG_R0
, ARMREG_FP
, framesize
);
779 arm_strx (code
, ARMREG_R0
, ARMREG_FP
, ccontext_offset
+ MONO_STRUCT_OFFSET (CallContext
, stack
));
781 /* call interp_entry with the ccontext and rmethod as arguments */
782 arm_addx_imm (code
, ARMREG_R0
, ARMREG_FP
, ccontext_offset
);
783 arm_ldrx (code
, ARMREG_R1
, MONO_ARCH_RGCTX_REG
, MONO_STRUCT_OFFSET (MonoFtnDesc
, arg
));
784 arm_ldrx (code
, ARMREG_IP0
, MONO_ARCH_RGCTX_REG
, MONO_STRUCT_OFFSET (MonoFtnDesc
, addr
));
785 arm_blrx (code
, ARMREG_IP0
);
787 /* load the return values from the context */
788 for (i
= 0; i
< PARAM_REGS
; i
++)
789 arm_ldrx (code
, i
, ARMREG_FP
, ccontext_offset
+ MONO_STRUCT_OFFSET (CallContext
, gregs
) + i
* sizeof (host_mgreg_t
));
791 for (i
= 0; i
< FP_PARAM_REGS
; i
++)
792 arm_ldrfpx (code
, i
, ARMREG_FP
, ccontext_offset
+ MONO_STRUCT_OFFSET (CallContext
, fregs
) + i
* sizeof (double));
794 /* reset stack and return */
795 arm_ldpx (code
, ARMREG_FP
, ARMREG_LR
, ARMREG_SP
, 0);
796 arm_addx_imm (code
, ARMREG_SP
, ARMREG_SP
, framesize
);
797 arm_retx (code
, ARMREG_LR
);
799 g_assert (code
- start
< buf_len
);
801 mono_arch_flush_icache (start
, code
- start
);
802 MONO_PROFILER_RAISE (jit_code_buffer
, (start
, code
- start
, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING
, NULL
));
805 *info
= mono_tramp_info_create ("native_to_interp_trampoline", start
, code
- start
, ji
, unwind_ops
);
809 g_assert_not_reached ();
811 #endif /* DISABLE_INTERPRETER */
814 #else /* DISABLE_JIT */
817 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type
, MonoTrampInfo
**info
, gboolean aot
)
819 g_assert_not_reached ();
824 mono_arch_create_specific_trampoline (gpointer arg1
, MonoTrampolineType tramp_type
, MonoDomain
*domain
, guint32
*code_len
)
826 g_assert_not_reached ();
831 mono_arch_get_unbox_trampoline (MonoMethod
*m
, gpointer addr
)
833 g_assert_not_reached ();
838 mono_arch_get_static_rgctx_trampoline (gpointer arg
, gpointer addr
)
840 g_assert_not_reached ();
845 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot
, MonoTrampInfo
**info
, gboolean aot
)
847 g_assert_not_reached ();
852 mono_arch_get_nullified_class_init_trampoline (MonoTrampInfo
**info
)
854 g_assert_not_reached ();
859 mono_arch_create_sdb_trampoline (gboolean single_step
, MonoTrampInfo
**info
, gboolean aot
)
861 g_assert_not_reached ();
866 mono_arch_get_interp_to_native_trampoline (MonoTrampInfo
**info
)
868 g_assert_not_reached ();
873 mono_arch_get_native_to_interp_trampoline (MonoTrampInfo
**info
)
875 g_assert_not_reached ();
879 #endif /* !DISABLE_JIT */