3 * exception support for ARM64
5 * Copyright 2013 Xamarin Inc
7 * Based on exceptions-arm.c:
10 * Dietmar Maurer (dietmar@ximian.com)
11 * Paolo Molaro (lupus@ximian.com)
13 * (C) 2001 Ximian, Inc.
14 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
18 #include "mini-runtime.h"
19 #include "aot-runtime.h"
21 #include <mono/arch/arm64/arm64-codegen.h>
22 #include <mono/metadata/abi-details.h>
27 mono_arch_get_restore_context (MonoTrampInfo
**info
, gboolean aot
)
30 MonoJumpInfo
*ji
= NULL
;
31 GSList
*unwind_ops
= NULL
;
36 code
= start
= mono_global_codeman_reserve (size
);
38 arm_movx (code
, ARMREG_IP0
, ARMREG_R0
);
42 arm_ldrx (code
, ARMREG_IP1
, ctx_reg
, MONO_STRUCT_OFFSET (MonoContext
, has_fregs
));
44 arm_cbzx (code
, ARMREG_IP1
, 0);
45 for (i
= 0; i
< 32; ++i
)
46 arm_ldrfpx (code
, i
, ctx_reg
, MONO_STRUCT_OFFSET (MonoContext
, fregs
) + (i
* sizeof (MonoContextSimdReg
)));
47 mono_arm_patch (labels
[0], code
, MONO_R_ARM64_CBZ
);
49 // FIXME: Restore less registers
50 // FIXME: fp should be restored later
51 code
= mono_arm_emit_load_regarray (code
, 0xffffffff & ~(1 << ctx_reg
) & ~(1 << ARMREG_SP
), ctx_reg
, MONO_STRUCT_OFFSET (MonoContext
, regs
));
52 /* ip0/ip1 doesn't need to be restored */
54 arm_ldrx (code
, ARMREG_IP1
, ctx_reg
, MONO_STRUCT_OFFSET (MonoContext
, pc
));
56 arm_ldrx (code
, ARMREG_IP0
, ctx_reg
, MONO_STRUCT_OFFSET (MonoContext
, regs
) + (ARMREG_SP
* 8));
57 /* Restore sp, ctx is no longer valid */
58 arm_movspx (code
, ARMREG_SP
, ARMREG_IP0
);
60 arm_brx (code
, ARMREG_IP1
);
64 g_assert ((code
- start
) < size
);
65 mono_arch_flush_icache (start
, code
- start
);
66 MONO_PROFILER_RAISE (jit_code_buffer
, (start
, code
- start
, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING
, NULL
));
69 *info
= mono_tramp_info_create ("restore_context", start
, code
- start
, ji
, unwind_ops
);
75 mono_arch_get_call_filter (MonoTrampInfo
**info
, gboolean aot
)
79 int i
, size
, offset
, gregs_offset
, fregs_offset
, ctx_offset
, num_fregs
, frame_size
;
80 MonoJumpInfo
*ji
= NULL
;
81 GSList
*unwind_ops
= NULL
;
85 start
= code
= mono_global_codeman_reserve (size
);
87 /* Compute stack frame size and offsets */
92 gregs_offset
= offset
;
96 fregs_offset
= offset
;
97 offset
+= num_fregs
* 8;
100 frame_size
= ALIGN_TO (offset
, MONO_ARCH_FRAME_ALIGNMENT
);
103 * We are being called from C code, ctx is in r0, the address to call is in r1.
104 * We need to save state, restore ctx, make the call, then restore the previous state,
105 * returning the value returned by the call.
109 arm_stpx_pre (code
, ARMREG_FP
, ARMREG_LR
, ARMREG_SP
, -frame_size
);
110 arm_movspx (code
, ARMREG_FP
, ARMREG_SP
);
113 arm_strx (code
, ARMREG_R0
, ARMREG_FP
, ctx_offset
);
115 code
= mono_arm_emit_store_regarray (code
, MONO_ARCH_CALLEE_SAVED_REGS
| (1 << ARMREG_FP
), ARMREG_FP
, gregs_offset
);
117 for (i
= 0; i
< num_fregs
; ++i
)
118 arm_strfpx (code
, ARMREG_D8
+ i
, ARMREG_FP
, fregs_offset
+ (i
* 8));
120 /* Load regs from ctx */
121 code
= mono_arm_emit_load_regarray (code
, MONO_ARCH_CALLEE_SAVED_REGS
, ARMREG_R0
, MONO_STRUCT_OFFSET (MonoContext
, regs
));
123 arm_ldrx (code
, ARMREG_IP0
, ARMREG_R0
, MONO_STRUCT_OFFSET (MonoContext
, has_fregs
));
125 arm_cbzx (code
, ARMREG_IP0
, 0);
126 for (i
= 0; i
< num_fregs
; ++i
)
127 arm_ldrfpx (code
, ARMREG_D8
+ i
, ARMREG_R0
, MONO_STRUCT_OFFSET (MonoContext
, fregs
) + ((i
+ 8) * sizeof (MonoContextSimdReg
)));
128 mono_arm_patch (labels
[0], code
, MONO_R_ARM64_CBZ
);
130 arm_ldrx (code
, ARMREG_FP
, ARMREG_R0
, MONO_STRUCT_OFFSET (MonoContext
, regs
) + (ARMREG_FP
* 8));
133 arm_blrx (code
, ARMREG_R1
);
134 /* For filters, the result is in R0 */
137 arm_ldrx (code
, ARMREG_FP
, ARMREG_SP
, gregs_offset
+ (ARMREG_FP
* 8));
139 arm_ldrx (code
, ARMREG_IP0
, ARMREG_FP
, ctx_offset
);
140 /* Save registers back to ctx */
141 /* This isn't strictly neccessary since we don't allocate variables used in eh clauses to registers */
142 code
= mono_arm_emit_store_regarray (code
, MONO_ARCH_CALLEE_SAVED_REGS
, ARMREG_IP0
, MONO_STRUCT_OFFSET (MonoContext
, regs
));
145 code
= mono_arm_emit_load_regarray (code
, MONO_ARCH_CALLEE_SAVED_REGS
, ARMREG_FP
, gregs_offset
);
147 for (i
= 0; i
< num_fregs
; ++i
)
148 arm_ldrfpx (code
, ARMREG_D8
+ i
, ARMREG_FP
, fregs_offset
+ (i
* 8));
150 code
= mono_arm_emit_destroy_frame (code
, frame_size
, (1 << ARMREG_IP0
));
151 arm_retx (code
, ARMREG_LR
);
153 g_assert ((code
- start
) < size
);
154 mono_arch_flush_icache (start
, code
- start
);
155 MONO_PROFILER_RAISE (jit_code_buffer
, (start
, code
- start
, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING
, NULL
));
158 *info
= mono_tramp_info_create ("call_filter", start
, code
- start
, ji
, unwind_ops
);
164 get_throw_trampoline (int size
, gboolean corlib
, gboolean rethrow
, gboolean llvm
, gboolean resume_unwind
, const char *tramp_name
, MonoTrampInfo
**info
, gboolean aot
, gboolean preserve_ips
)
166 guint8
*start
, *code
;
167 MonoJumpInfo
*ji
= NULL
;
168 GSList
*unwind_ops
= NULL
;
169 int i
, offset
, gregs_offset
, fregs_offset
, frame_size
, num_fregs
;
171 code
= start
= mono_global_codeman_reserve (size
);
173 /* We are being called by JITted code, the exception object/type token is in R0 */
175 /* Compute stack frame size and offsets */
180 gregs_offset
= offset
;
184 fregs_offset
= offset
;
185 offset
+= num_fregs
* 8;
186 frame_size
= ALIGN_TO (offset
, MONO_ARCH_FRAME_ALIGNMENT
);
189 arm_stpx_pre (code
, ARMREG_FP
, ARMREG_LR
, ARMREG_SP
, -frame_size
);
190 arm_movspx (code
, ARMREG_FP
, ARMREG_SP
);
193 code
= mono_arm_emit_store_regarray (code
, 0xffffffff, ARMREG_FP
, gregs_offset
);
195 /* The real LR is in R1 */
196 arm_strx (code
, ARMREG_R1
, ARMREG_FP
, gregs_offset
+ (ARMREG_LR
* 8));
198 arm_ldrx (code
, ARMREG_IP0
, ARMREG_FP
, 0);
199 arm_strx (code
, ARMREG_IP0
, ARMREG_FP
, gregs_offset
+ (ARMREG_FP
* 8));
200 arm_addx_imm (code
, ARMREG_IP0
, ARMREG_FP
, frame_size
);
201 arm_strx (code
, ARMREG_IP0
, ARMREG_FP
, gregs_offset
+ (ARMREG_SP
* 8));
203 for (i
= 0; i
< num_fregs
; ++i
)
204 arm_strfpx (code
, ARMREG_D8
+ i
, ARMREG_FP
, fregs_offset
+ (i
* 8));
206 /* Call the C trampoline function */
207 /* Arg1 = exception object/type token */
208 arm_movx (code
, ARMREG_R0
, ARMREG_R0
);
209 /* Arg2 = caller ip */
212 arm_ldrx (code
, ARMREG_R1
, ARMREG_FP
, gregs_offset
+ (ARMREG_LR
* 8));
214 arm_movx (code
, ARMREG_R1
, ARMREG_R1
);
216 arm_ldrx (code
, ARMREG_R1
, ARMREG_FP
, 8);
219 arm_addx_imm (code
, ARMREG_R2
, ARMREG_FP
, gregs_offset
);
221 arm_addx_imm (code
, ARMREG_R3
, ARMREG_FP
, fregs_offset
);
223 arm_movzx (code
, ARMREG_R4
, corlib
? 1 : 0, 0);
224 /* Arg 6 = rethrow */
225 arm_movzx (code
, ARMREG_R5
, rethrow
? 1 : 0, 0);
226 if (!resume_unwind
) {
227 /* Arg 7 = preserve_ips */
228 arm_movzx (code
, ARMREG_R6
, preserve_ips
? 1 : 0, 0);
231 /* Call the function */
233 const char *icall_name
;
236 icall_name
= "mono_arm_resume_unwind";
238 icall_name
= "mono_arm_throw_exception";
240 code
= mono_arm_emit_aotconst (&ji
, code
, start
, ARMREG_LR
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, icall_name
);
245 icall_func
= (gpointer
)mono_arm_resume_unwind
;
247 icall_func
= (gpointer
)mono_arm_throw_exception
;
249 code
= mono_arm_emit_imm64 (code
, ARMREG_LR
, (guint64
)icall_func
);
251 arm_blrx (code
, ARMREG_LR
);
252 /* This shouldn't return */
255 g_assert ((code
- start
) < size
);
256 mono_arch_flush_icache (start
, code
- start
);
257 MONO_PROFILER_RAISE (jit_code_buffer
, (start
, code
- start
, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING
, NULL
));
260 *info
= mono_tramp_info_create (tramp_name
, start
, code
- start
, ji
, unwind_ops
);
266 mono_arch_get_throw_exception (MonoTrampInfo
**info
, gboolean aot
)
268 return get_throw_trampoline (256, FALSE
, FALSE
, FALSE
, FALSE
, "throw_exception", info
, aot
, FALSE
);
272 mono_arch_get_rethrow_exception (MonoTrampInfo
**info
, gboolean aot
)
274 return get_throw_trampoline (256, FALSE
, TRUE
, FALSE
, FALSE
, "rethrow_exception", info
, aot
, FALSE
);
278 mono_arch_get_rethrow_preserve_exception (MonoTrampInfo
**info
, gboolean aot
)
280 return get_throw_trampoline (256, FALSE
, TRUE
, FALSE
, FALSE
, "rethrow_preserve_exception", info
, aot
, TRUE
);
284 mono_arch_get_throw_corlib_exception (MonoTrampInfo
**info
, gboolean aot
)
286 return get_throw_trampoline (256, TRUE
, FALSE
, FALSE
, FALSE
, "throw_corlib_exception", info
, aot
, FALSE
);
290 mono_arm_get_exception_trampolines (gboolean aot
)
293 GSList
*tramps
= NULL
;
295 /* LLVM uses the normal trampolines, but with a different name */
296 get_throw_trampoline (256, TRUE
, FALSE
, FALSE
, FALSE
, "llvm_throw_corlib_exception_trampoline", &info
, aot
, FALSE
);
297 tramps
= g_slist_prepend (tramps
, info
);
299 get_throw_trampoline (256, TRUE
, FALSE
, TRUE
, FALSE
, "llvm_throw_corlib_exception_abs_trampoline", &info
, aot
, FALSE
);
300 tramps
= g_slist_prepend (tramps
, info
);
302 get_throw_trampoline (256, FALSE
, FALSE
, FALSE
, TRUE
, "llvm_resume_unwind_trampoline", &info
, aot
, FALSE
);
303 tramps
= g_slist_prepend (tramps
, info
);
311 mono_arm_get_exception_trampolines (gboolean aot
)
313 g_assert_not_reached ();
317 #endif /* DISABLE_JIT */
320 mono_arch_exceptions_init (void)
326 tramp
= mono_aot_get_trampoline ("llvm_throw_corlib_exception_trampoline");
327 mono_register_jit_icall (tramp
, "llvm_throw_corlib_exception_trampoline", NULL
, TRUE
);
328 tramp
= mono_aot_get_trampoline ("llvm_throw_corlib_exception_abs_trampoline");
329 mono_register_jit_icall (tramp
, "llvm_throw_corlib_exception_abs_trampoline", NULL
, TRUE
);
330 tramp
= mono_aot_get_trampoline ("llvm_resume_unwind_trampoline");
331 mono_register_jit_icall (tramp
, "llvm_resume_unwind_trampoline", NULL
, TRUE
);
333 tramps
= mono_arm_get_exception_trampolines (FALSE
);
334 for (l
= tramps
; l
; l
= l
->next
) {
335 MonoTrampInfo
*info
= (MonoTrampInfo
*)l
->data
;
337 mono_register_jit_icall (info
->code
, g_strdup (info
->name
), NULL
, TRUE
);
338 mono_tramp_info_register (info
, NULL
);
340 g_slist_free (tramps
);
345 * mono_arm_throw_exception:
347 * This function is called by the exception trampolines.
348 * FP_REGS points to the 8 callee saved fp regs.
351 mono_arm_throw_exception (gpointer arg
, host_mgreg_t pc
, host_mgreg_t
*int_regs
, gdouble
*fp_regs
, gboolean corlib
, gboolean rethrow
, gboolean preserve_ips
)
355 MonoObject
*exc
= NULL
;
356 guint32 ex_token_index
, ex_token
;
359 exc
= (MonoObject
*)arg
;
361 ex_token_index
= (guint64
)arg
;
362 ex_token
= MONO_TOKEN_TYPE_DEF
| ex_token_index
;
363 exc
= (MonoObject
*)mono_exception_from_token (mono_defaults
.corlib
, ex_token
);
366 /* Adjust pc so it points into the call instruction */
369 /* Initialize a ctx based on the arguments */
370 memset (&ctx
, 0, sizeof (MonoContext
));
371 memcpy (&(ctx
.regs
[0]), int_regs
, sizeof (host_mgreg_t
) * 32);
372 for (int i
= 0; i
< 8; i
++)
373 *((gdouble
*)&ctx
.fregs
[ARMREG_D8
+ i
]) = fp_regs
[i
];
377 if (mono_object_isinst_checked (exc
, mono_defaults
.exception_class
, error
)) {
378 MonoException
*mono_ex
= (MonoException
*)exc
;
380 mono_ex
->stack_trace
= NULL
;
381 mono_ex
->trace_ips
= NULL
;
382 } else if (preserve_ips
) {
383 mono_ex
->caught_in_unmanaged
= TRUE
;
386 mono_error_assert_ok (error
);
388 mono_handle_exception (&ctx
, exc
);
390 mono_restore_context (&ctx
);
394 mono_arm_resume_unwind (gpointer arg
, host_mgreg_t pc
, host_mgreg_t
*int_regs
, gdouble
*fp_regs
, gboolean corlib
, gboolean rethrow
)
398 /* Adjust pc so it points into the call instruction */
401 /* Initialize a ctx based on the arguments */
402 memset (&ctx
, 0, sizeof (MonoContext
));
403 memcpy (&(ctx
.regs
[0]), int_regs
, sizeof (host_mgreg_t
) * 32);
404 for (int i
= 0; i
< 8; i
++)
405 *((gdouble
*)&ctx
.fregs
[ARMREG_D8
+ i
]) = fp_regs
[i
];
409 mono_resume_unwind (&ctx
);
413 * mono_arch_unwind_frame:
415 * See exceptions-amd64.c for docs;
418 mono_arch_unwind_frame (MonoDomain
*domain
, MonoJitTlsData
*jit_tls
,
419 MonoJitInfo
*ji
, MonoContext
*ctx
,
420 MonoContext
*new_ctx
, MonoLMF
**lmf
,
421 host_mgreg_t
**save_locations
,
422 StackFrameInfo
*frame
)
424 gpointer ip
= MONO_CONTEXT_GET_IP (ctx
);
426 memset (frame
, 0, sizeof (StackFrameInfo
));
432 host_mgreg_t regs
[MONO_MAX_IREGS
+ 8 + 1];
434 guint32 unwind_info_len
;
438 if (ji
->is_trampoline
)
439 frame
->type
= FRAME_TYPE_TRAMPOLINE
;
441 frame
->type
= FRAME_TYPE_MANAGED
;
443 unwind_info
= mono_jinfo_get_unwind_info (ji
, &unwind_info_len
);
445 memcpy (regs
, &new_ctx
->regs
, sizeof (host_mgreg_t
) * 32);
446 /* v8..v15 are callee saved */
447 for (int i
= 0; i
< 8; i
++)
448 (regs
+ MONO_MAX_IREGS
) [i
] = *((host_mgreg_t
*)&new_ctx
->fregs
[8 + i
]);
450 mono_unwind_frame (unwind_info
, unwind_info_len
, (guint8
*)ji
->code_start
,
451 (guint8
*)ji
->code_start
+ ji
->code_size
,
452 (guint8
*)ip
, NULL
, regs
, MONO_MAX_IREGS
+ 8,
453 save_locations
, MONO_MAX_IREGS
, (guint8
**)&cfa
);
455 memcpy (&new_ctx
->regs
, regs
, sizeof (host_mgreg_t
) * 32);
456 for (int i
= 0; i
< 8; i
++)
457 *((host_mgreg_t
*)&new_ctx
->fregs
[8 + i
]) = (regs
+ MONO_MAX_IREGS
) [i
];
459 new_ctx
->pc
= regs
[ARMREG_LR
];
460 new_ctx
->regs
[ARMREG_SP
] = (host_mgreg_t
)(gsize
)cfa
;
462 if (*lmf
&& (*lmf
)->gregs
[MONO_ARCH_LMF_REG_SP
] && (MONO_CONTEXT_GET_SP (ctx
) >= (gpointer
)(*lmf
)->gregs
[MONO_ARCH_LMF_REG_SP
])) {
463 /* remove any unused lmf */
464 *lmf
= (MonoLMF
*)(((gsize
)(*lmf
)->previous_lmf
) & ~3);
467 /* we substract 1, so that the IP points into the call instruction */
472 g_assert ((((guint64
)(*lmf
)->previous_lmf
) & 2) == 0);
474 frame
->type
= FRAME_TYPE_MANAGED_TO_NATIVE
;
476 ji
= mini_jit_info_table_find (domain
, (gpointer
)(*lmf
)->pc
, NULL
);
480 g_assert (MONO_ARCH_LMF_REGS
== ((0x3ff << 19) | (1 << ARMREG_FP
) | (1 << ARMREG_SP
)));
481 memcpy (&new_ctx
->regs
[ARMREG_R19
], &(*lmf
)->gregs
[0], sizeof (host_mgreg_t
) * 10);
482 new_ctx
->regs
[ARMREG_FP
] = (*lmf
)->gregs
[MONO_ARCH_LMF_REG_FP
];
483 new_ctx
->regs
[ARMREG_SP
] = (*lmf
)->gregs
[MONO_ARCH_LMF_REG_SP
];
484 new_ctx
->pc
= (*lmf
)->pc
;
486 /* we substract 1, so that the IP points into the call instruction */
489 *lmf
= (MonoLMF
*)(((gsize
)(*lmf
)->previous_lmf
) & ~3);
500 * Called by resuming from a signal handler.
503 handle_signal_exception (gpointer obj
)
505 MonoJitTlsData
*jit_tls
= mono_tls_get_jit_tls ();
508 memcpy (&ctx
, &jit_tls
->ex_ctx
, sizeof (MonoContext
));
510 mono_handle_exception (&ctx
, (MonoObject
*)obj
);
512 mono_restore_context (&ctx
);
516 * This is the function called from the signal handler
519 mono_arch_handle_exception (void *ctx
, gpointer obj
)
521 #if defined(MONO_CROSS_COMPILE)
522 g_assert_not_reached ();
524 MonoJitTlsData
*jit_tls
;
528 * Resume into the normal stack and handle the exception there.
530 jit_tls
= mono_tls_get_jit_tls ();
532 /* Pass the ctx parameter in TLS */
533 mono_sigctx_to_monoctx (sigctx
, &jit_tls
->ex_ctx
);
534 /* The others in registers */
535 UCONTEXT_REG_R0 (sigctx
) = (gsize
)obj
;
537 UCONTEXT_REG_PC (sigctx
) = (gsize
)handle_signal_exception
;
538 UCONTEXT_REG_SP (sigctx
) = UCONTEXT_REG_SP (sigctx
) - MONO_ARCH_REDZONE_SIZE
;
545 mono_arch_ip_from_context (void *sigctx
)
547 #ifdef MONO_CROSS_COMPILE
548 g_assert_not_reached ();
551 return (gpointer
)UCONTEXT_REG_PC (sigctx
);
556 mono_arch_setup_async_callback (MonoContext
*ctx
, void (*async_cb
)(void *fun
), gpointer user_data
)
558 host_mgreg_t sp
= (host_mgreg_t
)MONO_CONTEXT_GET_SP (ctx
);
561 g_assert (!user_data
);
563 /* Allocate a stack frame */
565 MONO_CONTEXT_SET_SP (ctx
, sp
);
567 mono_arch_setup_resume_sighandler_ctx (ctx
, (gpointer
)async_cb
);
571 * mono_arch_setup_resume_sighandler_ctx:
573 * Setup CTX so execution continues at FUNC.
576 mono_arch_setup_resume_sighandler_ctx (MonoContext
*ctx
, gpointer func
)
578 MONO_CONTEXT_SET_IP (ctx
,func
);
582 mono_arch_undo_ip_adjustment (MonoContext
*ctx
)
588 mono_arch_do_ip_adjustment (MonoContext
*ctx
)