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"
28 #include "mono/utils/mono-tls-inline.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
)
76 guint32 ins
= *(guint32
*)code
;
77 /* Should be a b/bl */
78 if (((ins
>> 26) & 0x1f) != 0x5)
80 gint32 disp
= ((gint32
)((ins
& 0x3ffffff) << 6)) >> 6;
81 return code
+ (disp
* 4);
85 mono_arch_get_plt_info_offset (guint8
*plt_entry
, host_mgreg_t
*regs
, guint8
*code
)
87 /* The offset is stored as the 5th word of the plt entry */
88 return ((guint32
*)plt_entry
) [4];
94 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type
, MonoTrampInfo
**info
, gboolean aot
)
96 guint8
*code
, *buf
, *tramp
, *labels
[16];
98 int frame_size
, offset
, gregs_offset
, num_fregs
, fregs_offset
, arg_offset
, lmf_offset
, res_offset
;
100 GSList
*unwind_ops
= NULL
;
101 MonoJumpInfo
*ji
= NULL
;
102 const char *tramp_name
;
105 buf
= code
= mono_global_codeman_reserve (buf_len
);
108 * We are getting called by a specific trampoline, ip1 contains the trampoline argument.
111 /* Compute stack frame size and offsets */
116 gregs_offset
= offset
;
119 // FIXME: Save 128 bits
120 /* Only have to save the argument regs */
122 fregs_offset
= offset
;
123 offset
+= num_fregs
* 8;
132 offset
+= sizeof (MonoLMF
);
134 frame_size
= ALIGN_TO (offset
, MONO_ARCH_FRAME_ALIGNMENT
);
136 /* Setup stack frame */
138 mono_add_unwind_op_def_cfa (unwind_ops
, code
, buf
, ARMREG_SP
, 0);
140 arm_subx_imm (code
, ARMREG_SP
, ARMREG_SP
, 256);
142 mono_add_unwind_op_def_cfa_offset (unwind_ops
, code
, buf
, frame_size
- imm
);
144 arm_subx_imm (code
, ARMREG_SP
, ARMREG_SP
, imm
);
145 mono_add_unwind_op_def_cfa_offset (unwind_ops
, code
, buf
, frame_size
);
147 arm_stpx (code
, ARMREG_FP
, ARMREG_LR
, ARMREG_SP
, 0);
148 mono_add_unwind_op_offset (unwind_ops
, code
, buf
, ARMREG_LR
, -frame_size
+ 8);
149 mono_add_unwind_op_offset (unwind_ops
, code
, buf
, ARMREG_FP
, -frame_size
);
151 arm_movspx (code
, ARMREG_FP
, ARMREG_SP
);
152 mono_add_unwind_op_def_cfa_reg (unwind_ops
, code
, buf
, ARMREG_FP
);
155 // FIXME: Optimize this
156 gregs_regset
= ~((1 << ARMREG_FP
) | (1 << ARMREG_SP
));
157 code
= mono_arm_emit_store_regarray (code
, gregs_regset
, ARMREG_FP
, gregs_offset
);
159 for (i
= 0; i
< num_fregs
; ++i
)
160 arm_strfpx (code
, i
, ARMREG_FP
, fregs_offset
+ (i
* 8));
161 /* Save trampoline arg */
162 arm_strx (code
, ARMREG_IP1
, ARMREG_FP
, arg_offset
);
165 arm_addx_imm (code
, ARMREG_IP0
, ARMREG_FP
, lmf_offset
);
166 code
= mono_arm_emit_store_regset (code
, MONO_ARCH_LMF_REGS
, ARMREG_IP0
, MONO_STRUCT_OFFSET (MonoLMF
, gregs
));
168 arm_ldrx (code
, ARMREG_IP1
, ARMREG_FP
, 0);
169 arm_strx (code
, ARMREG_IP1
, ARMREG_IP0
, MONO_STRUCT_OFFSET (MonoLMF
, gregs
) + (MONO_ARCH_LMF_REG_FP
* 8));
171 arm_movx (code
, ARMREG_IP1
, ARMREG_FP
);
174 arm_addx_imm (code
, ARMREG_IP1
, ARMREG_IP1
, 256);
177 arm_addx_imm (code
, ARMREG_IP1
, ARMREG_IP1
, imm
);
178 arm_strx (code
, ARMREG_IP1
, ARMREG_IP0
, MONO_STRUCT_OFFSET (MonoLMF
, gregs
) + (MONO_ARCH_LMF_REG_SP
* 8));
180 if (tramp_type
== MONO_TRAMPOLINE_JUMP
)
181 arm_movx (code
, ARMREG_LR
, ARMREG_RZR
);
183 arm_ldrx (code
, ARMREG_LR
, ARMREG_FP
, 8);
184 arm_strx (code
, ARMREG_LR
, ARMREG_IP0
, MONO_STRUCT_OFFSET (MonoLMF
, pc
));
187 /* Similar to emit_save_lmf () */
189 code
= mono_arm_emit_aotconst (&ji
, code
, buf
, ARMREG_IP0
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, GUINT_TO_POINTER (MONO_JIT_ICALL_mono_get_lmf_addr
));
191 tramp
= (guint8
*)mono_get_lmf_addr
;
192 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, (guint64
)tramp
);
194 arm_blrx (code
, ARMREG_IP0
);
195 /* r0 contains the address of the tls slot holding the current lmf */
197 arm_addx_imm (code
, ARMREG_IP0
, ARMREG_FP
, lmf_offset
);
198 /* lmf->lmf_addr = lmf_addr */
199 arm_strp (code
, ARMREG_R0
, ARMREG_IP0
, MONO_STRUCT_OFFSET (MonoLMF
, lmf_addr
));
200 /* lmf->previous_lmf = *lmf_addr */
201 arm_ldrp (code
, ARMREG_IP1
, ARMREG_R0
, 0);
202 arm_strp (code
, ARMREG_IP1
, ARMREG_IP0
, MONO_STRUCT_OFFSET (MonoLMF
, previous_lmf
));
203 /* *lmf_addr = lmf */
204 arm_strp (code
, ARMREG_IP0
, ARMREG_R0
, 0);
206 /* Call the C trampoline function */
208 arm_addx_imm (code
, ARMREG_R0
, ARMREG_FP
, gregs_offset
);
210 if (tramp_type
== MONO_TRAMPOLINE_JUMP
)
211 arm_movx (code
, ARMREG_R1
, ARMREG_RZR
);
213 arm_ldrx (code
, ARMREG_R1
, ARMREG_FP
, gregs_offset
+ (ARMREG_LR
* 8));
215 if (MONO_TRAMPOLINE_TYPE_HAS_ARG (tramp_type
))
217 arm_ldrx (code
, ARMREG_R2
, ARMREG_FP
, gregs_offset
+ (ARMREG_R0
* 8));
219 arm_ldrx (code
, ARMREG_R2
, ARMREG_FP
, arg_offset
);
220 /* Arg 4 = trampoline addr */
221 arm_movx (code
, ARMREG_R3
, ARMREG_RZR
);
224 code
= mono_arm_emit_aotconst (&ji
, code
, buf
, ARMREG_IP0
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, GINT_TO_POINTER (mono_trampoline_type_to_jit_icall_id (tramp_type
)));
226 tramp
= (guint8
*)mono_get_trampoline_func (tramp_type
);
227 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, (guint64
)tramp
);
229 arm_blrx (code
, ARMREG_IP0
);
231 /* Save the result */
232 arm_strx (code
, ARMREG_R0
, ARMREG_FP
, res_offset
);
235 /* Similar to emit_restore_lmf () */
236 /* Clobbers ip0/ip1 */
238 arm_addx_imm (code
, ARMREG_IP0
, ARMREG_FP
, lmf_offset
);
239 /* ip1 = lmf->previous_lmf */
240 arm_ldrp (code
, ARMREG_IP1
, ARMREG_IP0
, MONO_STRUCT_OFFSET (MonoLMF
, previous_lmf
));
241 /* ip0 = lmf->lmf_addr */
242 arm_ldrp (code
, ARMREG_IP0
, ARMREG_IP0
, MONO_STRUCT_OFFSET (MonoLMF
, lmf_addr
));
243 /* *lmf_addr = previous_lmf */
244 arm_strp (code
, ARMREG_IP1
, ARMREG_IP0
, 0);
246 /* Check for thread interruption */
247 /* This is not perf critical code so no need to check the interrupt flag */
249 code
= mono_arm_emit_aotconst (&ji
, code
, buf
, ARMREG_IP0
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, GUINT_TO_POINTER (MONO_JIT_ICALL_mono_thread_force_interruption_checkpoint_noraise
));
251 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, (guint64
)mono_thread_force_interruption_checkpoint_noraise
);
253 arm_blrx (code
, ARMREG_IP0
);
254 /* Check whenever there is an exception to be thrown */
256 arm_cbnzx (code
, ARMREG_R0
, 0);
261 /* Only have to load the argument regs (r0..r8) and the rgctx reg */
262 code
= mono_arm_emit_load_regarray (code
, 0x1ff | (1 << ARMREG_LR
) | (1 << MONO_ARCH_RGCTX_REG
), ARMREG_FP
, gregs_offset
);
264 for (i
= 0; i
< num_fregs
; ++i
)
265 arm_ldrfpx (code
, i
, ARMREG_FP
, fregs_offset
+ (i
* 8));
267 /* Load the result */
268 arm_ldrx (code
, ARMREG_IP1
, ARMREG_FP
, res_offset
);
270 /* These trampolines return a value */
271 if (tramp_type
== MONO_TRAMPOLINE_RGCTX_LAZY_FETCH
)
272 arm_movx (code
, ARMREG_R0
, ARMREG_IP1
);
275 code
= mono_arm_emit_destroy_frame (code
, frame_size
, ((1 << ARMREG_IP0
)));
277 if (tramp_type
== MONO_TRAMPOLINE_RGCTX_LAZY_FETCH
)
278 arm_retx (code
, ARMREG_LR
);
280 arm_brx (code
, ARMREG_IP1
);
283 mono_arm_patch (labels
[0], code
, MONO_R_ARM64_CBZ
);
286 * We have an exception we want to throw in the caller's frame, so pop
287 * the trampoline frame and throw from the caller.
289 code
= mono_arm_emit_destroy_frame (code
, frame_size
, ((1 << ARMREG_IP0
)));
290 /* We are in the parent frame, the exception is in x0 */
292 * EH is initialized after trampolines, so get the address of the variable
293 * which contains throw_exception, and load it from there.
296 /* Not really a jit icall */
297 code
= mono_arm_emit_aotconst (&ji
, code
, buf
, ARMREG_IP0
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, GUINT_TO_POINTER (MONO_JIT_ICALL_mono_rethrow_preserve_exception
));
299 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, (guint64
)mono_get_rethrow_preserve_exception_addr ());
301 arm_ldrx (code
, ARMREG_IP0
, ARMREG_IP0
, 0);
302 /* lr contains the return address, the trampoline will use it as the throw site */
303 arm_brx (code
, ARMREG_IP0
);
305 g_assert ((code
- buf
) < buf_len
);
306 mono_arch_flush_icache (buf
, code
- buf
);
307 MONO_PROFILER_RAISE (jit_code_buffer
, (buf
, code
- buf
, MONO_PROFILER_CODE_BUFFER_HELPER
, NULL
));
310 tramp_name
= mono_get_generic_trampoline_name (tramp_type
);
311 *info
= mono_tramp_info_create (tramp_name
, buf
, code
- buf
, ji
, unwind_ops
);
318 mono_arch_create_specific_trampoline (gpointer arg1
, MonoTrampolineType tramp_type
, MonoDomain
*domain
, guint32
*code_len
)
320 guint8
*code
, *buf
, *tramp
;
324 * Return a trampoline which calls generic trampoline TRAMP_TYPE passing in ARG1.
325 * Pass the argument in ip1, clobbering ip0.
327 tramp
= mono_get_trampoline_code (tramp_type
);
329 buf
= code
= mono_domain_code_reserve (domain
, buf_len
);
331 code
= mono_arm_emit_imm64 (code
, ARMREG_IP1
, (guint64
)arg1
);
332 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, (guint64
)tramp
);
334 arm_brx (code
, ARMREG_IP0
);
336 g_assert ((code
- buf
) < buf_len
);
337 mono_arch_flush_icache (buf
, code
- buf
);
338 MONO_PROFILER_RAISE (jit_code_buffer
, (buf
, code
- buf
, MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE
, mono_get_generic_trampoline_simple_name (tramp_type
)));
341 *code_len
= code
- buf
;
347 mono_arch_get_unbox_trampoline (MonoMethod
*m
, gpointer addr
)
349 guint8
*code
, *start
;
351 MonoDomain
*domain
= mono_domain_get ();
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
, MONO_ABI_SIZEOF (MonoObject
));
356 arm_brx (code
, ARMREG_IP0
);
358 g_assert ((code
- start
) <= size
);
359 mono_arch_flush_icache (start
, code
- start
);
360 MONO_PROFILER_RAISE (jit_code_buffer
, (start
, code
- start
, MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE
, m
));
365 mono_arch_get_static_rgctx_trampoline (gpointer arg
, gpointer addr
)
367 guint8
*code
, *start
;
368 guint32 buf_len
= 32;
369 MonoDomain
*domain
= mono_domain_get ();
371 start
= code
= mono_domain_code_reserve (domain
, buf_len
);
372 code
= mono_arm_emit_imm64 (code
, MONO_ARCH_RGCTX_REG
, (guint64
)arg
);
373 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, (guint64
)addr
);
374 arm_brx (code
, ARMREG_IP0
);
376 g_assert ((code
- start
) <= buf_len
);
378 mono_arch_flush_icache (start
, code
- start
);
379 MONO_PROFILER_RAISE (jit_code_buffer
, (start
, code
- start
, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE
, NULL
));
385 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot
, MonoTrampInfo
**info
, gboolean aot
)
389 int i
, depth
, index
, njumps
;
391 guint8
**rgctx_null_jumps
;
392 MonoJumpInfo
*ji
= NULL
;
393 GSList
*unwind_ops
= NULL
;
397 is_mrgctx
= MONO_RGCTX_SLOT_IS_MRGCTX (slot
);
398 index
= MONO_RGCTX_SLOT_INDEX (slot
);
400 index
+= MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT
/ sizeof (target_mgreg_t
);
401 for (depth
= 0; ; ++depth
) {
402 int size
= mono_class_rgctx_get_array_size (depth
, is_mrgctx
);
404 if (index
< size
- 1)
409 buf_size
= 64 + 16 * depth
;
410 code
= buf
= mono_global_codeman_reserve (buf_size
);
412 rgctx_null_jumps
= g_malloc0 (sizeof (guint8
*) * (depth
+ 2));
415 /* The vtable/mrgtx is in R0 */
416 g_assert (MONO_ARCH_VTABLE_REG
== ARMREG_R0
);
420 arm_movx (code
, ARMREG_IP1
, ARMREG_R0
);
422 /* load rgctx ptr from vtable */
423 code
= mono_arm_emit_ldrx (code
, ARMREG_IP1
, ARMREG_R0
, MONO_STRUCT_OFFSET (MonoVTable
, runtime_generic_context
));
424 /* is the rgctx ptr null? */
425 /* if yes, jump to actual trampoline */
426 rgctx_null_jumps
[njumps
++] = code
;
427 arm_cbzx (code
, ARMREG_IP1
, 0);
430 for (i
= 0; i
< depth
; ++i
) {
431 /* load ptr to next array */
432 if (is_mrgctx
&& i
== 0) {
433 code
= mono_arm_emit_ldrx (code
, ARMREG_IP1
, ARMREG_IP1
, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT
);
435 code
= mono_arm_emit_ldrx (code
, ARMREG_IP1
, ARMREG_IP1
, 0);
437 /* is the ptr null? */
438 /* if yes, jump to actual trampoline */
439 rgctx_null_jumps
[njumps
++] = code
;
440 arm_cbzx (code
, ARMREG_IP1
, 0);
444 code
= mono_arm_emit_ldrx (code
, ARMREG_IP1
, ARMREG_IP1
, sizeof (target_mgreg_t
) * (index
+ 1));
445 /* is the slot null? */
446 /* if yes, jump to actual trampoline */
447 rgctx_null_jumps
[njumps
++] = code
;
448 arm_cbzx (code
, ARMREG_IP1
, 0);
449 /* otherwise return, result is in IP1 */
450 arm_movx (code
, ARMREG_R0
, ARMREG_IP1
);
451 arm_brx (code
, ARMREG_LR
);
453 g_assert (njumps
<= depth
+ 2);
454 for (i
= 0; i
< njumps
; ++i
)
455 mono_arm_patch (rgctx_null_jumps
[i
], code
, MONO_R_ARM64_CBZ
);
457 g_free (rgctx_null_jumps
);
461 /* Call mono_rgctx_lazy_fetch_trampoline (), passing in the slot as argument */
462 /* The vtable/mrgctx is still in R0 */
464 code
= mono_arm_emit_aotconst (&ji
, code
, buf
, ARMREG_IP0
, MONO_PATCH_INFO_SPECIFIC_TRAMPOLINE_LAZY_FETCH_ADDR
, GUINT_TO_POINTER (slot
));
466 tramp
= (guint8
*)mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot
), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH
, mono_get_root_domain (), &code_len
);
467 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, (guint64
)tramp
);
469 arm_brx (code
, ARMREG_IP0
);
471 mono_arch_flush_icache (buf
, code
- buf
);
472 MONO_PROFILER_RAISE (jit_code_buffer
, (buf
, code
- buf
, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE
, NULL
));
474 g_assert (code
- buf
<= buf_size
);
477 char *name
= mono_get_rgctx_fetch_trampoline_name (slot
);
478 *info
= mono_tramp_info_create (name
, buf
, code
- buf
, ji
, unwind_ops
);
486 mono_arch_create_general_rgctx_lazy_fetch_trampoline (MonoTrampInfo
**info
, gboolean aot
)
490 MonoJumpInfo
*ji
= NULL
;
491 GSList
*unwind_ops
= NULL
;
497 code
= buf
= mono_global_codeman_reserve (tramp_size
);
499 mono_add_unwind_op_def_cfa (unwind_ops
, code
, buf
, ARMREG_SP
, 0);
501 // FIXME: Currently, we always go to the slow path.
502 /* Load trampoline addr */
503 arm_ldrx (code
, ARMREG_IP0
, MONO_ARCH_RGCTX_REG
, 8);
504 /* The vtable/mrgctx is in R0 */
505 g_assert (MONO_ARCH_VTABLE_REG
== ARMREG_R0
);
506 arm_brx (code
, ARMREG_IP0
);
508 mono_arch_flush_icache (buf
, code
- buf
);
509 MONO_PROFILER_RAISE (jit_code_buffer
, (buf
, code
- buf
, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE
, NULL
));
511 g_assert (code
- buf
<= tramp_size
);
514 *info
= mono_tramp_info_create ("rgctx_fetch_trampoline_general", buf
, code
- buf
, ji
, unwind_ops
);
520 * mono_arch_create_sdb_trampoline:
522 * Return a trampoline which captures the current context, passes it to
523 * mini_get_dbg_callbacks ()->single_step_from_context ()/mini_get_dbg_callbacks ()->breakpoint_from_context (),
524 * then restores the (potentially changed) context.
527 mono_arch_create_sdb_trampoline (gboolean single_step
, MonoTrampInfo
**info
, gboolean aot
)
529 int tramp_size
= 512;
530 int offset
, imm
, frame_size
, ctx_offset
;
531 guint64 gregs_regset
;
533 GSList
*unwind_ops
= NULL
;
534 MonoJumpInfo
*ji
= NULL
;
536 code
= buf
= mono_global_codeman_reserve (tramp_size
);
538 /* Compute stack frame size and offsets */
544 offset
+= sizeof (MonoContext
);
545 offset
= ALIGN_TO (offset
, MONO_ARCH_FRAME_ALIGNMENT
);
548 // FIXME: Unwind info
550 /* Setup stack frame */
553 arm_subx_imm (code
, ARMREG_SP
, ARMREG_SP
, 256);
556 arm_subx_imm (code
, ARMREG_SP
, ARMREG_SP
, imm
);
557 arm_stpx (code
, ARMREG_FP
, ARMREG_LR
, ARMREG_SP
, 0);
558 arm_movspx (code
, ARMREG_FP
, ARMREG_SP
);
560 /* Initialize a MonoContext structure on the stack */
561 /* No need to save fregs */
562 gregs_regset
= ~((1 << ARMREG_FP
) | (1 << ARMREG_SP
));
563 code
= mono_arm_emit_store_regarray (code
, gregs_regset
, ARMREG_FP
, ctx_offset
+ G_STRUCT_OFFSET (MonoContext
, regs
));
565 arm_ldrx (code
, ARMREG_IP1
, ARMREG_FP
, 0);
566 arm_strx (code
, ARMREG_IP1
, ARMREG_FP
, ctx_offset
+ G_STRUCT_OFFSET (MonoContext
, regs
) + (ARMREG_FP
* 8));
568 arm_movx (code
, ARMREG_IP1
, ARMREG_FP
);
571 arm_addx_imm (code
, ARMREG_IP1
, ARMREG_IP1
, 256);
574 arm_addx_imm (code
, ARMREG_IP1
, ARMREG_IP1
, imm
);
575 arm_strx (code
, ARMREG_IP1
, ARMREG_FP
, ctx_offset
+ G_STRUCT_OFFSET (MonoContext
, regs
) + (ARMREG_SP
* 8));
577 arm_ldrx (code
, ARMREG_IP1
, ARMREG_FP
, 8);
578 arm_strx (code
, ARMREG_IP1
, ARMREG_FP
, ctx_offset
+ G_STRUCT_OFFSET (MonoContext
, pc
));
580 /* Call the single step/breakpoint function in sdb */
582 arm_addx_imm (code
, ARMREG_R0
, ARMREG_FP
, ctx_offset
);
585 code
= mono_arm_emit_aotconst (&ji
, code
, buf
, ARMREG_IP0
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, GUINT_TO_POINTER (MONO_JIT_ICALL_mono_debugger_agent_single_step_from_context
));
587 code
= mono_arm_emit_aotconst (&ji
, code
, buf
, ARMREG_IP0
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, GUINT_TO_POINTER (MONO_JIT_ICALL_mono_debugger_agent_breakpoint_from_context
));
589 void (*addr
) (MonoContext
*ctx
) = single_step
? mini_get_dbg_callbacks ()->single_step_from_context
: mini_get_dbg_callbacks ()->breakpoint_from_context
;
591 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, (guint64
)addr
);
593 arm_blrx (code
, ARMREG_IP0
);
596 /* Save fp/pc into the frame block */
597 arm_ldrx (code
, ARMREG_IP0
, ARMREG_FP
, ctx_offset
+ G_STRUCT_OFFSET (MonoContext
, regs
) + (ARMREG_FP
* 8));
598 arm_strx (code
, ARMREG_IP0
, ARMREG_FP
, 0);
599 arm_ldrx (code
, ARMREG_IP0
, ARMREG_FP
, ctx_offset
+ G_STRUCT_OFFSET (MonoContext
, pc
));
600 arm_strx (code
, ARMREG_IP0
, ARMREG_FP
, 8);
601 gregs_regset
= ~((1 << ARMREG_FP
) | (1 << ARMREG_SP
));
602 code
= mono_arm_emit_load_regarray (code
, gregs_regset
, ARMREG_FP
, ctx_offset
+ G_STRUCT_OFFSET (MonoContext
, regs
));
604 code
= mono_arm_emit_destroy_frame (code
, frame_size
, ((1 << ARMREG_IP0
) | (1 << ARMREG_IP1
)));
606 arm_retx (code
, ARMREG_LR
);
608 mono_arch_flush_icache (code
, code
- buf
);
609 MONO_PROFILER_RAISE (jit_code_buffer
, (buf
, code
- buf
, MONO_PROFILER_CODE_BUFFER_HELPER
, NULL
));
610 g_assert (code
- buf
<= tramp_size
);
612 const char *tramp_name
= single_step
? "sdb_single_step_trampoline" : "sdb_breakpoint_trampoline";
613 *info
= mono_tramp_info_create (tramp_name
, buf
, code
- buf
, ji
, unwind_ops
);
619 * mono_arch_get_interp_to_native_trampoline:
621 * See tramp-amd64.c for documentation.
624 mono_arch_get_interp_to_native_trampoline (MonoTrampInfo
**info
)
626 #ifndef DISABLE_INTERPRETER
627 guint8
*start
= NULL
, *code
;
628 guint8
*label_start_copy
, *label_exit_copy
;
629 MonoJumpInfo
*ji
= NULL
;
630 GSList
*unwind_ops
= NULL
;
631 int buf_len
, i
, framesize
= 0, off_methodargs
, off_targetaddr
;
633 buf_len
= 512 + 1024;
634 start
= code
= (guint8
*) mono_global_codeman_reserve (buf_len
);
637 framesize
+= 2 * sizeof (host_mgreg_t
);
639 off_methodargs
= framesize
;
640 framesize
+= sizeof (host_mgreg_t
);
642 off_targetaddr
= framesize
;
643 framesize
+= sizeof (host_mgreg_t
);
645 framesize
= ALIGN_TO (framesize
, MONO_ARCH_FRAME_ALIGNMENT
);
647 arm_subx_imm (code
, ARMREG_SP
, ARMREG_SP
, framesize
);
648 arm_stpx (code
, ARMREG_FP
, ARMREG_LR
, ARMREG_SP
, 0);
649 arm_movspx (code
, ARMREG_FP
, ARMREG_SP
);
651 /* save CallContext* onto stack */
652 arm_strx (code
, ARMREG_R1
, ARMREG_FP
, off_methodargs
);
654 /* save target address onto stack */
655 arm_strx (code
, ARMREG_R0
, ARMREG_FP
, off_targetaddr
);
657 /* allocate the stack space necessary for the call */
658 arm_ldrw (code
, ARMREG_R0
, ARMREG_R1
, MONO_STRUCT_OFFSET (CallContext
, stack_size
));
659 arm_movspx (code
, ARMREG_IP0
, ARMREG_SP
);
660 arm_subx (code
, ARMREG_IP0
, ARMREG_IP0
, ARMREG_R0
);
661 arm_movspx (code
, ARMREG_SP
, ARMREG_IP0
);
663 /* copy stack from the CallContext, IP0 = dest, IP1 = source */
664 arm_movspx (code
, ARMREG_IP0
, ARMREG_SP
);
665 arm_ldrp (code
, ARMREG_IP1
, ARMREG_R1
, MONO_STRUCT_OFFSET (CallContext
, stack
));
667 label_start_copy
= code
;
669 arm_cmpx_imm (code
, ARMREG_R0
, 0);
670 label_exit_copy
= code
;
671 arm_bcc (code
, ARMCOND_EQ
, 0);
672 arm_ldrx (code
, ARMREG_R2
, ARMREG_IP1
, 0);
673 arm_strx (code
, ARMREG_R2
, ARMREG_IP0
, 0);
674 arm_addx_imm (code
, ARMREG_IP0
, ARMREG_IP0
, sizeof (host_mgreg_t
));
675 arm_addx_imm (code
, ARMREG_IP1
, ARMREG_IP1
, sizeof (host_mgreg_t
));
676 arm_subx_imm (code
, ARMREG_R0
, ARMREG_R0
, sizeof (host_mgreg_t
));
677 arm_b (code
, label_start_copy
);
678 mono_arm_patch (label_exit_copy
, code
, MONO_R_ARM64_BCC
);
680 /* Load CallContext* into IP0 */
681 arm_ldrx (code
, ARMREG_IP0
, ARMREG_FP
, off_methodargs
);
683 /* set all general purpose registers from CallContext */
684 for (i
= 0; i
< PARAM_REGS
+ 1; i
++)
685 arm_ldrx (code
, i
, ARMREG_IP0
, MONO_STRUCT_OFFSET (CallContext
, gregs
) + i
* sizeof (host_mgreg_t
));
687 /* set all floating registers from CallContext */
688 for (i
= 0; i
< FP_PARAM_REGS
; i
++)
689 arm_ldrfpx (code
, i
, ARMREG_IP0
, MONO_STRUCT_OFFSET (CallContext
, fregs
) + i
* sizeof (double));
691 /* load target addr */
692 arm_ldrx (code
, ARMREG_IP0
, ARMREG_FP
, off_targetaddr
);
694 /* call into native function */
695 arm_blrx (code
, ARMREG_IP0
);
697 /* load CallContext* */
698 arm_ldrx (code
, ARMREG_IP0
, ARMREG_FP
, off_methodargs
);
700 /* set all general purpose registers to CallContext */
701 for (i
= 0; i
< PARAM_REGS
; i
++)
702 arm_strx (code
, i
, ARMREG_IP0
, MONO_STRUCT_OFFSET (CallContext
, gregs
) + i
* sizeof (host_mgreg_t
));
704 /* set all floating registers to CallContext */
705 for (i
= 0; i
< FP_PARAM_REGS
; i
++)
706 arm_strfpx (code
, i
, ARMREG_IP0
, MONO_STRUCT_OFFSET (CallContext
, fregs
) + i
* sizeof (double));
708 arm_movspx (code
, ARMREG_SP
, ARMREG_FP
);
709 arm_ldpx (code
, ARMREG_FP
, ARMREG_LR
, ARMREG_SP
, 0);
710 arm_addx_imm (code
, ARMREG_SP
, ARMREG_SP
, framesize
);
711 arm_retx (code
, ARMREG_LR
);
713 g_assert (code
- start
< buf_len
);
715 mono_arch_flush_icache (start
, code
- start
);
716 MONO_PROFILER_RAISE (jit_code_buffer
, (start
, code
- start
, MONO_PROFILER_CODE_BUFFER_HELPER
, NULL
));
719 *info
= mono_tramp_info_create ("interp_to_native_trampoline", start
, code
- start
, ji
, unwind_ops
);
723 g_assert_not_reached ();
725 #endif /* DISABLE_INTERPRETER */
729 mono_arch_get_native_to_interp_trampoline (MonoTrampInfo
**info
)
731 #ifndef DISABLE_INTERPRETER
732 guint8
*start
= NULL
, *code
;
733 MonoJumpInfo
*ji
= NULL
;
734 GSList
*unwind_ops
= NULL
;
736 int framesize
, offset
, ccontext_offset
;
739 start
= code
= (guint8
*) mono_global_codeman_reserve (buf_len
);
741 /* Allocate frame (FP + LR + CallContext) */
742 offset
= 2 * sizeof (host_mgreg_t
);
743 ccontext_offset
= offset
;
744 offset
+= sizeof (CallContext
);
745 framesize
= ALIGN_TO (offset
, MONO_ARCH_FRAME_ALIGNMENT
);
747 mono_add_unwind_op_def_cfa (unwind_ops
, code
, start
, ARMREG_SP
, 0);
749 arm_subx_imm (code
, ARMREG_SP
, ARMREG_SP
, framesize
);
750 mono_add_unwind_op_def_cfa_offset (unwind_ops
, code
, start
, framesize
);
752 arm_stpx (code
, ARMREG_FP
, ARMREG_LR
, ARMREG_SP
, 0);
753 mono_add_unwind_op_offset (unwind_ops
, code
, start
, ARMREG_LR
, -framesize
+ 8);
754 mono_add_unwind_op_offset (unwind_ops
, code
, start
, ARMREG_FP
, -framesize
);
756 arm_movspx (code
, ARMREG_FP
, ARMREG_SP
);
757 mono_add_unwind_op_def_cfa_reg (unwind_ops
, code
, start
, ARMREG_FP
);
759 /* save all general purpose registers into the CallContext */
760 for (i
= 0; i
< PARAM_REGS
+ 1; i
++)
761 arm_strx (code
, i
, ARMREG_FP
, ccontext_offset
+ MONO_STRUCT_OFFSET (CallContext
, gregs
) + i
* sizeof (host_mgreg_t
));
763 /* save all floating registers into the CallContext */
764 for (i
= 0; i
< FP_PARAM_REGS
; i
++)
765 arm_strfpx (code
, i
, ARMREG_FP
, ccontext_offset
+ MONO_STRUCT_OFFSET (CallContext
, fregs
) + i
* sizeof (double));
767 /* set the stack pointer to the value at call site */
768 arm_addx_imm (code
, ARMREG_R0
, ARMREG_FP
, framesize
);
769 arm_strp (code
, ARMREG_R0
, ARMREG_FP
, ccontext_offset
+ MONO_STRUCT_OFFSET (CallContext
, stack
));
771 /* call interp_entry with the ccontext and rmethod as arguments */
772 arm_addx_imm (code
, ARMREG_R0
, ARMREG_FP
, ccontext_offset
);
773 arm_ldrp (code
, ARMREG_R1
, MONO_ARCH_RGCTX_REG
, MONO_STRUCT_OFFSET (MonoFtnDesc
, arg
));
774 arm_ldrp (code
, ARMREG_IP0
, MONO_ARCH_RGCTX_REG
, MONO_STRUCT_OFFSET (MonoFtnDesc
, addr
));
775 arm_blrx (code
, ARMREG_IP0
);
777 /* load the return values from the context */
778 for (i
= 0; i
< PARAM_REGS
; i
++)
779 arm_ldrx (code
, i
, ARMREG_FP
, ccontext_offset
+ MONO_STRUCT_OFFSET (CallContext
, gregs
) + i
* sizeof (host_mgreg_t
));
781 for (i
= 0; i
< FP_PARAM_REGS
; i
++)
782 arm_ldrfpx (code
, i
, ARMREG_FP
, ccontext_offset
+ MONO_STRUCT_OFFSET (CallContext
, fregs
) + i
* sizeof (double));
784 /* reset stack and return */
785 arm_ldpx (code
, ARMREG_FP
, ARMREG_LR
, ARMREG_SP
, 0);
786 arm_addx_imm (code
, ARMREG_SP
, ARMREG_SP
, framesize
);
787 arm_retx (code
, ARMREG_LR
);
789 g_assert (code
- start
< buf_len
);
791 mono_arch_flush_icache (start
, code
- start
);
792 MONO_PROFILER_RAISE (jit_code_buffer
, (start
, code
- start
, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING
, NULL
));
795 *info
= mono_tramp_info_create ("native_to_interp_trampoline", start
, code
- start
, ji
, unwind_ops
);
799 g_assert_not_reached ();
801 #endif /* DISABLE_INTERPRETER */
804 #else /* DISABLE_JIT */
807 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type
, MonoTrampInfo
**info
, gboolean aot
)
809 g_assert_not_reached ();
814 mono_arch_create_specific_trampoline (gpointer arg1
, MonoTrampolineType tramp_type
, MonoDomain
*domain
, guint32
*code_len
)
816 g_assert_not_reached ();
821 mono_arch_get_unbox_trampoline (MonoMethod
*m
, gpointer addr
)
823 g_assert_not_reached ();
828 mono_arch_get_static_rgctx_trampoline (gpointer arg
, gpointer addr
)
830 g_assert_not_reached ();
835 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot
, MonoTrampInfo
**info
, gboolean aot
)
837 g_assert_not_reached ();
842 mono_arch_get_nullified_class_init_trampoline (MonoTrampInfo
**info
)
844 g_assert_not_reached ();
849 mono_arch_create_sdb_trampoline (gboolean single_step
, MonoTrampInfo
**info
, gboolean aot
)
851 g_assert_not_reached ();
856 mono_arch_get_interp_to_native_trampoline (MonoTrampInfo
**info
)
858 g_assert_not_reached ();
863 mono_arch_get_native_to_interp_trampoline (MonoTrampInfo
**info
)
865 g_assert_not_reached ();
869 #endif /* !DISABLE_JIT */