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
)
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
;
110 const char *tramp_name
;
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
, GUINT_TO_POINTER (MONO_JIT_ICALL_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_strp (code
, ARMREG_R0
, ARMREG_IP0
, MONO_STRUCT_OFFSET (MonoLMF
, lmf_addr
));
208 /* lmf->previous_lmf = *lmf_addr */
209 arm_ldrp (code
, ARMREG_IP1
, ARMREG_R0
, 0);
210 arm_strp (code
, ARMREG_IP1
, ARMREG_IP0
, MONO_STRUCT_OFFSET (MonoLMF
, previous_lmf
));
211 /* *lmf_addr = lmf */
212 arm_strp (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 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
)));
234 tramp
= (guint8
*)mono_get_trampoline_func (tramp_type
);
235 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, (guint64
)tramp
);
237 arm_blrx (code
, ARMREG_IP0
);
239 /* Save the result */
240 arm_strx (code
, ARMREG_R0
, ARMREG_FP
, res_offset
);
243 /* Similar to emit_restore_lmf () */
244 /* Clobbers ip0/ip1 */
246 arm_addx_imm (code
, ARMREG_IP0
, ARMREG_FP
, lmf_offset
);
247 /* ip1 = lmf->previous_lmf */
248 arm_ldrp (code
, ARMREG_IP1
, ARMREG_IP0
, MONO_STRUCT_OFFSET (MonoLMF
, previous_lmf
));
249 /* ip0 = lmf->lmf_addr */
250 arm_ldrp (code
, ARMREG_IP0
, ARMREG_IP0
, MONO_STRUCT_OFFSET (MonoLMF
, lmf_addr
));
251 /* *lmf_addr = previous_lmf */
252 arm_strp (code
, ARMREG_IP1
, ARMREG_IP0
, 0);
254 /* Check for thread interruption */
255 /* This is not perf critical code so no need to check the interrupt flag */
257 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
));
259 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, (guint64
)mono_thread_force_interruption_checkpoint_noraise
);
261 arm_blrx (code
, ARMREG_IP0
);
262 /* Check whenever there is an exception to be thrown */
264 arm_cbnzx (code
, ARMREG_R0
, 0);
269 /* Only have to load the argument regs (r0..r8) and the rgctx reg */
270 code
= mono_arm_emit_load_regarray (code
, 0x1ff | (1 << ARMREG_LR
) | (1 << MONO_ARCH_RGCTX_REG
), ARMREG_FP
, gregs_offset
);
272 for (i
= 0; i
< num_fregs
; ++i
)
273 arm_ldrfpx (code
, i
, ARMREG_FP
, fregs_offset
+ (i
* 8));
275 /* Load the result */
276 arm_ldrx (code
, ARMREG_IP1
, ARMREG_FP
, res_offset
);
278 /* These trampolines return a value */
279 if (tramp_type
== MONO_TRAMPOLINE_RGCTX_LAZY_FETCH
)
280 arm_movx (code
, ARMREG_R0
, ARMREG_IP1
);
283 code
= mono_arm_emit_destroy_frame (code
, frame_size
, ((1 << ARMREG_IP0
)));
285 if (tramp_type
== MONO_TRAMPOLINE_RGCTX_LAZY_FETCH
)
286 arm_retx (code
, ARMREG_LR
);
288 arm_brx (code
, ARMREG_IP1
);
291 mono_arm_patch (labels
[0], code
, MONO_R_ARM64_CBZ
);
294 * We have an exception we want to throw in the caller's frame, so pop
295 * the trampoline frame and throw from the caller.
297 code
= mono_arm_emit_destroy_frame (code
, frame_size
, ((1 << ARMREG_IP0
)));
298 /* We are in the parent frame, the exception is in x0 */
300 * EH is initialized after trampolines, so get the address of the variable
301 * which contains throw_exception, and load it from there.
304 /* Not really a jit icall */
305 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
));
307 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, (guint64
)mono_get_rethrow_preserve_exception_addr ());
309 arm_ldrx (code
, ARMREG_IP0
, ARMREG_IP0
, 0);
310 /* lr contains the return address, the trampoline will use it as the throw site */
311 arm_brx (code
, ARMREG_IP0
);
313 g_assert ((code
- buf
) < buf_len
);
314 mono_arch_flush_icache (buf
, code
- buf
);
315 MONO_PROFILER_RAISE (jit_code_buffer
, (buf
, code
- buf
, MONO_PROFILER_CODE_BUFFER_HELPER
, NULL
));
318 tramp_name
= mono_get_generic_trampoline_name (tramp_type
);
319 *info
= mono_tramp_info_create (tramp_name
, buf
, code
- buf
, ji
, unwind_ops
);
326 mono_arch_create_specific_trampoline (gpointer arg1
, MonoTrampolineType tramp_type
, MonoDomain
*domain
, guint32
*code_len
)
328 guint8
*code
, *buf
, *tramp
;
332 * Return a trampoline which calls generic trampoline TRAMP_TYPE passing in ARG1.
333 * Pass the argument in ip1, clobbering ip0.
335 tramp
= mono_get_trampoline_code (tramp_type
);
337 buf
= code
= mono_domain_code_reserve (domain
, buf_len
);
339 code
= mono_arm_emit_imm64 (code
, ARMREG_IP1
, (guint64
)arg1
);
340 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, (guint64
)tramp
);
342 arm_brx (code
, ARMREG_IP0
);
344 g_assert ((code
- buf
) < buf_len
);
345 mono_arch_flush_icache (buf
, code
- buf
);
346 MONO_PROFILER_RAISE (jit_code_buffer
, (buf
, code
- buf
, MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE
, mono_get_generic_trampoline_simple_name (tramp_type
)));
349 *code_len
= code
- buf
;
355 mono_arch_get_unbox_trampoline (MonoMethod
*m
, gpointer addr
)
357 guint8
*code
, *start
;
359 MonoDomain
*domain
= mono_domain_get ();
361 start
= code
= mono_domain_code_reserve (domain
, size
);
362 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, (guint64
)addr
);
363 arm_addx_imm (code
, ARMREG_R0
, ARMREG_R0
, MONO_ABI_SIZEOF (MonoObject
));
364 arm_brx (code
, ARMREG_IP0
);
366 g_assert ((code
- start
) <= size
);
367 mono_arch_flush_icache (start
, code
- start
);
368 MONO_PROFILER_RAISE (jit_code_buffer
, (start
, code
- start
, MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE
, m
));
373 mono_arch_get_static_rgctx_trampoline (gpointer arg
, gpointer addr
)
375 guint8
*code
, *start
;
376 guint32 buf_len
= 32;
377 MonoDomain
*domain
= mono_domain_get ();
379 start
= code
= mono_domain_code_reserve (domain
, buf_len
);
380 code
= mono_arm_emit_imm64 (code
, MONO_ARCH_RGCTX_REG
, (guint64
)arg
);
381 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, (guint64
)addr
);
382 arm_brx (code
, ARMREG_IP0
);
384 g_assert ((code
- start
) <= buf_len
);
386 mono_arch_flush_icache (start
, code
- start
);
387 MONO_PROFILER_RAISE (jit_code_buffer
, (start
, code
- start
, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE
, NULL
));
393 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot
, MonoTrampInfo
**info
, gboolean aot
)
397 int i
, depth
, index
, njumps
;
399 guint8
**rgctx_null_jumps
;
400 MonoJumpInfo
*ji
= NULL
;
401 GSList
*unwind_ops
= NULL
;
405 is_mrgctx
= MONO_RGCTX_SLOT_IS_MRGCTX (slot
);
406 index
= MONO_RGCTX_SLOT_INDEX (slot
);
408 index
+= MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT
/ sizeof (target_mgreg_t
);
409 for (depth
= 0; ; ++depth
) {
410 int size
= mono_class_rgctx_get_array_size (depth
, is_mrgctx
);
412 if (index
< size
- 1)
417 buf_size
= 64 + 16 * depth
;
418 code
= buf
= mono_global_codeman_reserve (buf_size
);
420 rgctx_null_jumps
= g_malloc0 (sizeof (guint8
*) * (depth
+ 2));
423 /* The vtable/mrgtx is in R0 */
424 g_assert (MONO_ARCH_VTABLE_REG
== ARMREG_R0
);
428 arm_movx (code
, ARMREG_IP1
, ARMREG_R0
);
430 /* load rgctx ptr from vtable */
431 code
= mono_arm_emit_ldrx (code
, ARMREG_IP1
, ARMREG_R0
, MONO_STRUCT_OFFSET (MonoVTable
, runtime_generic_context
));
432 /* is the rgctx ptr null? */
433 /* if yes, jump to actual trampoline */
434 rgctx_null_jumps
[njumps
++] = code
;
435 arm_cbzx (code
, ARMREG_IP1
, 0);
438 for (i
= 0; i
< depth
; ++i
) {
439 /* load ptr to next array */
440 if (is_mrgctx
&& i
== 0) {
441 code
= mono_arm_emit_ldrx (code
, ARMREG_IP1
, ARMREG_IP1
, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT
);
443 code
= mono_arm_emit_ldrx (code
, ARMREG_IP1
, ARMREG_IP1
, 0);
445 /* is the ptr null? */
446 /* if yes, jump to actual trampoline */
447 rgctx_null_jumps
[njumps
++] = code
;
448 arm_cbzx (code
, ARMREG_IP1
, 0);
452 code
= mono_arm_emit_ldrx (code
, ARMREG_IP1
, ARMREG_IP1
, sizeof (target_mgreg_t
) * (index
+ 1));
453 /* is the slot null? */
454 /* if yes, jump to actual trampoline */
455 rgctx_null_jumps
[njumps
++] = code
;
456 arm_cbzx (code
, ARMREG_IP1
, 0);
457 /* otherwise return, result is in IP1 */
458 arm_movx (code
, ARMREG_R0
, ARMREG_IP1
);
459 arm_brx (code
, ARMREG_LR
);
461 g_assert (njumps
<= depth
+ 2);
462 for (i
= 0; i
< njumps
; ++i
)
463 mono_arm_patch (rgctx_null_jumps
[i
], code
, MONO_R_ARM64_CBZ
);
465 g_free (rgctx_null_jumps
);
469 /* Call mono_rgctx_lazy_fetch_trampoline (), passing in the slot as argument */
470 /* The vtable/mrgctx is still in R0 */
472 code
= mono_arm_emit_aotconst (&ji
, code
, buf
, ARMREG_IP0
, MONO_PATCH_INFO_SPECIFIC_TRAMPOLINE_LAZY_FETCH_ADDR
, GUINT_TO_POINTER (slot
));
474 tramp
= (guint8
*)mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot
), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH
, mono_get_root_domain (), &code_len
);
475 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, (guint64
)tramp
);
477 arm_brx (code
, ARMREG_IP0
);
479 mono_arch_flush_icache (buf
, code
- buf
);
480 MONO_PROFILER_RAISE (jit_code_buffer
, (buf
, code
- buf
, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE
, NULL
));
482 g_assert (code
- buf
<= buf_size
);
485 char *name
= mono_get_rgctx_fetch_trampoline_name (slot
);
486 *info
= mono_tramp_info_create (name
, buf
, code
- buf
, ji
, unwind_ops
);
494 mono_arch_create_general_rgctx_lazy_fetch_trampoline (MonoTrampInfo
**info
, gboolean aot
)
498 MonoJumpInfo
*ji
= NULL
;
499 GSList
*unwind_ops
= NULL
;
505 code
= buf
= mono_global_codeman_reserve (tramp_size
);
507 mono_add_unwind_op_def_cfa (unwind_ops
, code
, buf
, ARMREG_SP
, 0);
509 // FIXME: Currently, we always go to the slow path.
510 /* Load trampoline addr */
511 arm_ldrx (code
, ARMREG_IP0
, MONO_ARCH_RGCTX_REG
, 8);
512 /* The vtable/mrgctx is in R0 */
513 g_assert (MONO_ARCH_VTABLE_REG
== ARMREG_R0
);
514 arm_brx (code
, ARMREG_IP0
);
516 mono_arch_flush_icache (buf
, code
- buf
);
517 MONO_PROFILER_RAISE (jit_code_buffer
, (buf
, code
- buf
, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE
, NULL
));
519 g_assert (code
- buf
<= tramp_size
);
522 *info
= mono_tramp_info_create ("rgctx_fetch_trampoline_general", buf
, code
- buf
, ji
, unwind_ops
);
528 * mono_arch_create_sdb_trampoline:
530 * Return a trampoline which captures the current context, passes it to
531 * mini_get_dbg_callbacks ()->single_step_from_context ()/mini_get_dbg_callbacks ()->breakpoint_from_context (),
532 * then restores the (potentially changed) context.
535 mono_arch_create_sdb_trampoline (gboolean single_step
, MonoTrampInfo
**info
, gboolean aot
)
537 int tramp_size
= 512;
538 int offset
, imm
, frame_size
, ctx_offset
;
539 guint64 gregs_regset
;
541 GSList
*unwind_ops
= NULL
;
542 MonoJumpInfo
*ji
= NULL
;
544 code
= buf
= mono_global_codeman_reserve (tramp_size
);
546 /* Compute stack frame size and offsets */
552 offset
+= sizeof (MonoContext
);
553 offset
= ALIGN_TO (offset
, MONO_ARCH_FRAME_ALIGNMENT
);
556 // FIXME: Unwind info
558 /* Setup stack frame */
561 arm_subx_imm (code
, ARMREG_SP
, ARMREG_SP
, 256);
564 arm_subx_imm (code
, ARMREG_SP
, ARMREG_SP
, imm
);
565 arm_stpx (code
, ARMREG_FP
, ARMREG_LR
, ARMREG_SP
, 0);
566 arm_movspx (code
, ARMREG_FP
, ARMREG_SP
);
568 /* Initialize a MonoContext structure on the stack */
569 /* No need to save fregs */
570 gregs_regset
= ~((1 << ARMREG_FP
) | (1 << ARMREG_SP
));
571 code
= mono_arm_emit_store_regarray (code
, gregs_regset
, ARMREG_FP
, ctx_offset
+ G_STRUCT_OFFSET (MonoContext
, regs
));
573 arm_ldrx (code
, ARMREG_IP1
, ARMREG_FP
, 0);
574 arm_strx (code
, ARMREG_IP1
, ARMREG_FP
, ctx_offset
+ G_STRUCT_OFFSET (MonoContext
, regs
) + (ARMREG_FP
* 8));
576 arm_movx (code
, ARMREG_IP1
, ARMREG_FP
);
579 arm_addx_imm (code
, ARMREG_IP1
, ARMREG_IP1
, 256);
582 arm_addx_imm (code
, ARMREG_IP1
, ARMREG_IP1
, imm
);
583 arm_strx (code
, ARMREG_IP1
, ARMREG_FP
, ctx_offset
+ G_STRUCT_OFFSET (MonoContext
, regs
) + (ARMREG_SP
* 8));
585 arm_ldrx (code
, ARMREG_IP1
, ARMREG_FP
, 8);
586 arm_strx (code
, ARMREG_IP1
, ARMREG_FP
, ctx_offset
+ G_STRUCT_OFFSET (MonoContext
, pc
));
588 /* Call the single step/breakpoint function in sdb */
590 arm_addx_imm (code
, ARMREG_R0
, ARMREG_FP
, ctx_offset
);
593 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
));
595 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
));
597 void (*addr
) (MonoContext
*ctx
) = single_step
? mini_get_dbg_callbacks ()->single_step_from_context
: mini_get_dbg_callbacks ()->breakpoint_from_context
;
599 code
= mono_arm_emit_imm64 (code
, ARMREG_IP0
, (guint64
)addr
);
601 arm_blrx (code
, ARMREG_IP0
);
604 /* Save fp/pc into the frame block */
605 arm_ldrx (code
, ARMREG_IP0
, ARMREG_FP
, ctx_offset
+ G_STRUCT_OFFSET (MonoContext
, regs
) + (ARMREG_FP
* 8));
606 arm_strx (code
, ARMREG_IP0
, ARMREG_FP
, 0);
607 arm_ldrx (code
, ARMREG_IP0
, ARMREG_FP
, ctx_offset
+ G_STRUCT_OFFSET (MonoContext
, pc
));
608 arm_strx (code
, ARMREG_IP0
, ARMREG_FP
, 8);
609 gregs_regset
= ~((1 << ARMREG_FP
) | (1 << ARMREG_SP
));
610 code
= mono_arm_emit_load_regarray (code
, gregs_regset
, ARMREG_FP
, ctx_offset
+ G_STRUCT_OFFSET (MonoContext
, regs
));
612 code
= mono_arm_emit_destroy_frame (code
, frame_size
, ((1 << ARMREG_IP0
) | (1 << ARMREG_IP1
)));
614 arm_retx (code
, ARMREG_LR
);
616 mono_arch_flush_icache (code
, code
- buf
);
617 MONO_PROFILER_RAISE (jit_code_buffer
, (buf
, code
- buf
, MONO_PROFILER_CODE_BUFFER_HELPER
, NULL
));
618 g_assert (code
- buf
<= tramp_size
);
620 const char *tramp_name
= single_step
? "sdb_single_step_trampoline" : "sdb_breakpoint_trampoline";
621 *info
= mono_tramp_info_create (tramp_name
, buf
, code
- buf
, ji
, unwind_ops
);
627 * mono_arch_get_interp_to_native_trampoline:
629 * See tramp-amd64.c for documentation.
632 mono_arch_get_interp_to_native_trampoline (MonoTrampInfo
**info
)
634 #ifndef DISABLE_INTERPRETER
635 guint8
*start
= NULL
, *code
;
636 guint8
*label_start_copy
, *label_exit_copy
;
637 MonoJumpInfo
*ji
= NULL
;
638 GSList
*unwind_ops
= NULL
;
639 int buf_len
, i
, framesize
= 0, off_methodargs
, off_targetaddr
;
641 buf_len
= 512 + 1024;
642 start
= code
= (guint8
*) mono_global_codeman_reserve (buf_len
);
645 framesize
+= 2 * sizeof (host_mgreg_t
);
647 off_methodargs
= framesize
;
648 framesize
+= sizeof (host_mgreg_t
);
650 off_targetaddr
= framesize
;
651 framesize
+= sizeof (host_mgreg_t
);
653 framesize
= ALIGN_TO (framesize
, MONO_ARCH_FRAME_ALIGNMENT
);
655 arm_subx_imm (code
, ARMREG_SP
, ARMREG_SP
, framesize
);
656 arm_stpx (code
, ARMREG_FP
, ARMREG_LR
, ARMREG_SP
, 0);
657 arm_movspx (code
, ARMREG_FP
, ARMREG_SP
);
659 /* save CallContext* onto stack */
660 arm_strx (code
, ARMREG_R1
, ARMREG_FP
, off_methodargs
);
662 /* save target address onto stack */
663 arm_strx (code
, ARMREG_R0
, ARMREG_FP
, off_targetaddr
);
665 /* allocate the stack space necessary for the call */
666 arm_ldrw (code
, ARMREG_R0
, ARMREG_R1
, MONO_STRUCT_OFFSET (CallContext
, stack_size
));
667 arm_movspx (code
, ARMREG_IP0
, ARMREG_SP
);
668 arm_subx (code
, ARMREG_IP0
, ARMREG_IP0
, ARMREG_R0
);
669 arm_movspx (code
, ARMREG_SP
, ARMREG_IP0
);
671 /* copy stack from the CallContext, IP0 = dest, IP1 = source */
672 arm_movspx (code
, ARMREG_IP0
, ARMREG_SP
);
673 arm_ldrp (code
, ARMREG_IP1
, ARMREG_R1
, MONO_STRUCT_OFFSET (CallContext
, stack
));
675 label_start_copy
= code
;
677 arm_cmpx_imm (code
, ARMREG_R0
, 0);
678 label_exit_copy
= code
;
679 arm_bcc (code
, ARMCOND_EQ
, 0);
680 arm_ldrx (code
, ARMREG_R2
, ARMREG_IP1
, 0);
681 arm_strx (code
, ARMREG_R2
, ARMREG_IP0
, 0);
682 arm_addx_imm (code
, ARMREG_IP0
, ARMREG_IP0
, sizeof (host_mgreg_t
));
683 arm_addx_imm (code
, ARMREG_IP1
, ARMREG_IP1
, sizeof (host_mgreg_t
));
684 arm_subx_imm (code
, ARMREG_R0
, ARMREG_R0
, sizeof (host_mgreg_t
));
685 arm_b (code
, label_start_copy
);
686 mono_arm_patch (label_exit_copy
, code
, MONO_R_ARM64_BCC
);
688 /* Load CallContext* into IP0 */
689 arm_ldrx (code
, ARMREG_IP0
, ARMREG_FP
, off_methodargs
);
691 /* set all general purpose registers from CallContext */
692 for (i
= 0; i
< PARAM_REGS
+ 1; i
++)
693 arm_ldrx (code
, i
, ARMREG_IP0
, MONO_STRUCT_OFFSET (CallContext
, gregs
) + i
* sizeof (host_mgreg_t
));
695 /* set all floating registers from CallContext */
696 for (i
= 0; i
< FP_PARAM_REGS
; i
++)
697 arm_ldrfpx (code
, i
, ARMREG_IP0
, MONO_STRUCT_OFFSET (CallContext
, fregs
) + i
* sizeof (double));
699 /* load target addr */
700 arm_ldrx (code
, ARMREG_IP0
, ARMREG_FP
, off_targetaddr
);
702 /* call into native function */
703 arm_blrx (code
, ARMREG_IP0
);
705 /* load CallContext* */
706 arm_ldrx (code
, ARMREG_IP0
, ARMREG_FP
, off_methodargs
);
708 /* set all general purpose registers to CallContext */
709 for (i
= 0; i
< PARAM_REGS
; i
++)
710 arm_strx (code
, i
, ARMREG_IP0
, MONO_STRUCT_OFFSET (CallContext
, gregs
) + i
* sizeof (host_mgreg_t
));
712 /* set all floating registers to CallContext */
713 for (i
= 0; i
< FP_PARAM_REGS
; i
++)
714 arm_strfpx (code
, i
, ARMREG_IP0
, MONO_STRUCT_OFFSET (CallContext
, fregs
) + i
* sizeof (double));
716 arm_movspx (code
, ARMREG_SP
, ARMREG_FP
);
717 arm_ldpx (code
, ARMREG_FP
, ARMREG_LR
, ARMREG_SP
, 0);
718 arm_addx_imm (code
, ARMREG_SP
, ARMREG_SP
, framesize
);
719 arm_retx (code
, ARMREG_LR
);
721 g_assert (code
- start
< buf_len
);
723 mono_arch_flush_icache (start
, code
- start
);
724 MONO_PROFILER_RAISE (jit_code_buffer
, (start
, code
- start
, MONO_PROFILER_CODE_BUFFER_HELPER
, NULL
));
727 *info
= mono_tramp_info_create ("interp_to_native_trampoline", start
, code
- start
, ji
, unwind_ops
);
731 g_assert_not_reached ();
733 #endif /* DISABLE_INTERPRETER */
737 mono_arch_get_native_to_interp_trampoline (MonoTrampInfo
**info
)
739 #ifndef DISABLE_INTERPRETER
740 guint8
*start
= NULL
, *code
;
741 MonoJumpInfo
*ji
= NULL
;
742 GSList
*unwind_ops
= NULL
;
744 int framesize
, offset
, ccontext_offset
;
747 start
= code
= (guint8
*) mono_global_codeman_reserve (buf_len
);
749 /* Allocate frame (FP + LR + CallContext) */
750 offset
= 2 * sizeof (host_mgreg_t
);
751 ccontext_offset
= offset
;
752 offset
+= sizeof (CallContext
);
753 framesize
= ALIGN_TO (offset
, MONO_ARCH_FRAME_ALIGNMENT
);
755 mono_add_unwind_op_def_cfa (unwind_ops
, code
, start
, ARMREG_SP
, 0);
757 arm_subx_imm (code
, ARMREG_SP
, ARMREG_SP
, framesize
);
758 mono_add_unwind_op_def_cfa_offset (unwind_ops
, code
, start
, framesize
);
760 arm_stpx (code
, ARMREG_FP
, ARMREG_LR
, ARMREG_SP
, 0);
761 mono_add_unwind_op_offset (unwind_ops
, code
, start
, ARMREG_LR
, -framesize
+ 8);
762 mono_add_unwind_op_offset (unwind_ops
, code
, start
, ARMREG_FP
, -framesize
);
764 arm_movspx (code
, ARMREG_FP
, ARMREG_SP
);
765 mono_add_unwind_op_def_cfa_reg (unwind_ops
, code
, start
, ARMREG_FP
);
767 /* save all general purpose registers into the CallContext */
768 for (i
= 0; i
< PARAM_REGS
+ 1; i
++)
769 arm_strx (code
, i
, ARMREG_FP
, ccontext_offset
+ MONO_STRUCT_OFFSET (CallContext
, gregs
) + i
* sizeof (host_mgreg_t
));
771 /* save all floating registers into the CallContext */
772 for (i
= 0; i
< FP_PARAM_REGS
; i
++)
773 arm_strfpx (code
, i
, ARMREG_FP
, ccontext_offset
+ MONO_STRUCT_OFFSET (CallContext
, fregs
) + i
* sizeof (double));
775 /* set the stack pointer to the value at call site */
776 arm_addx_imm (code
, ARMREG_R0
, ARMREG_FP
, framesize
);
777 arm_strp (code
, ARMREG_R0
, ARMREG_FP
, ccontext_offset
+ MONO_STRUCT_OFFSET (CallContext
, stack
));
779 /* call interp_entry with the ccontext and rmethod as arguments */
780 arm_addx_imm (code
, ARMREG_R0
, ARMREG_FP
, ccontext_offset
);
781 arm_ldrp (code
, ARMREG_R1
, MONO_ARCH_RGCTX_REG
, MONO_STRUCT_OFFSET (MonoFtnDesc
, arg
));
782 arm_ldrp (code
, ARMREG_IP0
, MONO_ARCH_RGCTX_REG
, MONO_STRUCT_OFFSET (MonoFtnDesc
, addr
));
783 arm_blrx (code
, ARMREG_IP0
);
785 /* load the return values from the context */
786 for (i
= 0; i
< PARAM_REGS
; i
++)
787 arm_ldrx (code
, i
, ARMREG_FP
, ccontext_offset
+ MONO_STRUCT_OFFSET (CallContext
, gregs
) + i
* sizeof (host_mgreg_t
));
789 for (i
= 0; i
< FP_PARAM_REGS
; i
++)
790 arm_ldrfpx (code
, i
, ARMREG_FP
, ccontext_offset
+ MONO_STRUCT_OFFSET (CallContext
, fregs
) + i
* sizeof (double));
792 /* reset stack and return */
793 arm_ldpx (code
, ARMREG_FP
, ARMREG_LR
, ARMREG_SP
, 0);
794 arm_addx_imm (code
, ARMREG_SP
, ARMREG_SP
, framesize
);
795 arm_retx (code
, ARMREG_LR
);
797 g_assert (code
- start
< buf_len
);
799 mono_arch_flush_icache (start
, code
- start
);
800 MONO_PROFILER_RAISE (jit_code_buffer
, (start
, code
- start
, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING
, NULL
));
803 *info
= mono_tramp_info_create ("native_to_interp_trampoline", start
, code
- start
, ji
, unwind_ops
);
807 g_assert_not_reached ();
809 #endif /* DISABLE_INTERPRETER */
812 #else /* DISABLE_JIT */
815 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type
, MonoTrampInfo
**info
, gboolean aot
)
817 g_assert_not_reached ();
822 mono_arch_create_specific_trampoline (gpointer arg1
, MonoTrampolineType tramp_type
, MonoDomain
*domain
, guint32
*code_len
)
824 g_assert_not_reached ();
829 mono_arch_get_unbox_trampoline (MonoMethod
*m
, gpointer addr
)
831 g_assert_not_reached ();
836 mono_arch_get_static_rgctx_trampoline (gpointer arg
, gpointer addr
)
838 g_assert_not_reached ();
843 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot
, MonoTrampInfo
**info
, gboolean aot
)
845 g_assert_not_reached ();
850 mono_arch_get_nullified_class_init_trampoline (MonoTrampInfo
**info
)
852 g_assert_not_reached ();
857 mono_arch_create_sdb_trampoline (gboolean single_step
, MonoTrampInfo
**info
, gboolean aot
)
859 g_assert_not_reached ();
864 mono_arch_get_interp_to_native_trampoline (MonoTrampInfo
**info
)
866 g_assert_not_reached ();
871 mono_arch_get_native_to_interp_trampoline (MonoTrampInfo
**info
)
873 g_assert_not_reached ();
877 #endif /* !DISABLE_JIT */