[wasm] Add a --runtime-arg= argument to runtime-tests.js to allow setting runtime...
[mono-project.git] / mono / mini / mini-arm64.c
blob19c91911b71eb1ac822de2e6f420af15ce8612a0
1 /**
2 * \file
3 * ARM64 backend for the Mono code generator
5 * Copyright 2013 Xamarin, Inc (http://www.xamarin.com)
6 *
7 * Based on mini-arm.c:
9 * Authors:
10 * Paolo Molaro (lupus@ximian.com)
11 * Dietmar Maurer (dietmar@ximian.com)
13 * (C) 2003 Ximian, Inc.
14 * Copyright 2003-2011 Novell, Inc (http://www.novell.com)
15 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
16 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
19 #include "mini.h"
20 #include "cpu-arm64.h"
21 #include "ir-emit.h"
22 #include "aot-runtime.h"
23 #include "mini-runtime.h"
25 #include <mono/arch/arm64/arm64-codegen.h>
26 #include <mono/utils/mono-mmap.h>
27 #include <mono/utils/mono-memory-model.h>
28 #include <mono/metadata/abi-details.h>
30 #include "interp/interp.h"
33 * Documentation:
35 * - ARM(R) Architecture Reference Manual, ARMv8, for ARMv8-A architecture profile (DDI0487A_a_armv8_arm.pdf)
36 * - Procedure Call Standard for the ARM 64-bit Architecture (AArch64) (IHI0055B_aapcs64.pdf)
37 * - ELF for the ARM 64-bit Architecture (IHI0056B_aaelf64.pdf)
39 * Register usage:
40 * - ip0/ip1/lr are used as temporary registers
41 * - r27 is used as the rgctx/imt register
42 * - r28 is used to access arguments passed on the stack
43 * - d15/d16 are used as fp temporary registers
46 #define FP_TEMP_REG ARMREG_D16
47 #define FP_TEMP_REG2 ARMREG_D17
49 #define THUNK_SIZE (4 * 4)
51 /* The single step trampoline */
52 static gpointer ss_trampoline;
54 /* The breakpoint trampoline */
55 static gpointer bp_trampoline;
57 static gboolean ios_abi;
59 static __attribute__ ((__warn_unused_result__)) guint8* emit_load_regset (guint8 *code, guint64 regs, int basereg, int offset);
61 const char*
62 mono_arch_regname (int reg)
64 static const char * rnames[] = {
65 "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9",
66 "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19",
67 "r20", "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "fp",
68 "lr", "sp"
70 if (reg >= 0 && reg < 32)
71 return rnames [reg];
72 return "unknown";
75 const char*
76 mono_arch_fregname (int reg)
78 static const char * rnames[] = {
79 "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9",
80 "d10", "d11", "d12", "d13", "d14", "d15", "d16", "d17", "d18", "d19",
81 "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27", "d28", "d29",
82 "d30", "d31"
84 if (reg >= 0 && reg < 32)
85 return rnames [reg];
86 return "unknown fp";
89 int
90 mono_arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJitArgumentInfo *arg_info)
92 NOT_IMPLEMENTED;
93 return 0;
96 #define MAX_ARCH_DELEGATE_PARAMS 7
98 static gpointer
99 get_delegate_invoke_impl (gboolean has_target, gboolean param_count, guint32 *code_size)
101 guint8 *code, *start;
103 if (has_target) {
104 start = code = mono_global_codeman_reserve (12);
106 /* Replace the this argument with the target */
107 arm_ldrx (code, ARMREG_IP0, ARMREG_R0, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr));
108 arm_ldrx (code, ARMREG_R0, ARMREG_R0, MONO_STRUCT_OFFSET (MonoDelegate, target));
109 arm_brx (code, ARMREG_IP0);
111 g_assert ((code - start) <= 12);
113 mono_arch_flush_icache (start, 12);
114 MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE, NULL));
115 } else {
116 int size, i;
118 size = 8 + param_count * 4;
119 start = code = mono_global_codeman_reserve (size);
121 arm_ldrx (code, ARMREG_IP0, ARMREG_R0, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr));
122 /* slide down the arguments */
123 for (i = 0; i < param_count; ++i)
124 arm_movx (code, i, i + 1);
125 arm_brx (code, ARMREG_IP0);
127 g_assert ((code - start) <= size);
129 mono_arch_flush_icache (start, size);
130 MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE, NULL));
133 if (code_size)
134 *code_size = code - start;
136 return start;
140 * mono_arch_get_delegate_invoke_impls:
142 * Return a list of MonoAotTrampInfo structures for the delegate invoke impl
143 * trampolines.
145 GSList*
146 mono_arch_get_delegate_invoke_impls (void)
148 GSList *res = NULL;
149 guint8 *code;
150 guint32 code_len;
151 int i;
152 char *tramp_name;
154 code = (guint8*)get_delegate_invoke_impl (TRUE, 0, &code_len);
155 res = g_slist_prepend (res, mono_tramp_info_create ("delegate_invoke_impl_has_target", code, code_len, NULL, NULL));
157 for (i = 0; i <= MAX_ARCH_DELEGATE_PARAMS; ++i) {
158 code = (guint8*)get_delegate_invoke_impl (FALSE, i, &code_len);
159 tramp_name = g_strdup_printf ("delegate_invoke_impl_target_%d", i);
160 res = g_slist_prepend (res, mono_tramp_info_create (tramp_name, code, code_len, NULL, NULL));
161 g_free (tramp_name);
164 return res;
167 gpointer
168 mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_target)
170 guint8 *code, *start;
173 * vtypes are returned in registers, or using the dedicated r8 register, so
174 * they can be supported by delegate invokes.
177 if (has_target) {
178 static guint8* cached = NULL;
180 if (cached)
181 return cached;
183 if (mono_ee_features.use_aot_trampolines)
184 start = (guint8*)mono_aot_get_trampoline ("delegate_invoke_impl_has_target");
185 else
186 start = (guint8*)get_delegate_invoke_impl (TRUE, 0, NULL);
187 mono_memory_barrier ();
188 cached = start;
189 return cached;
190 } else {
191 static guint8* cache [MAX_ARCH_DELEGATE_PARAMS + 1] = {NULL};
192 int i;
194 if (sig->param_count > MAX_ARCH_DELEGATE_PARAMS)
195 return NULL;
196 for (i = 0; i < sig->param_count; ++i)
197 if (!mono_is_regsize_var (sig->params [i]))
198 return NULL;
200 code = cache [sig->param_count];
201 if (code)
202 return code;
204 if (mono_ee_features.use_aot_trampolines) {
205 char *name = g_strdup_printf ("delegate_invoke_impl_target_%d", sig->param_count);
206 start = (guint8*)mono_aot_get_trampoline (name);
207 g_free (name);
208 } else {
209 start = (guint8*)get_delegate_invoke_impl (FALSE, sig->param_count, NULL);
211 mono_memory_barrier ();
212 cache [sig->param_count] = start;
213 return start;
216 return NULL;
219 gpointer
220 mono_arch_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig, MonoMethod *method, int offset, gboolean load_imt_reg)
222 return NULL;
225 gpointer
226 mono_arch_get_this_arg_from_call (host_mgreg_t *regs, guint8 *code)
228 return (gpointer)regs [ARMREG_R0];
231 void
232 mono_arch_cpu_init (void)
236 void
237 mono_arch_init (void)
239 mono_aot_register_jit_icall ("mono_arm_throw_exception", mono_arm_throw_exception);
240 mono_aot_register_jit_icall ("mono_arm_resume_unwind", mono_arm_resume_unwind);
242 if (!mono_aot_only)
243 bp_trampoline = mini_get_breakpoint_trampoline ();
245 mono_arm_gsharedvt_init ();
247 #if defined(TARGET_IOS)
248 ios_abi = TRUE;
249 #endif
252 void
253 mono_arch_cleanup (void)
257 guint32
258 mono_arch_cpu_optimizations (guint32 *exclude_mask)
260 *exclude_mask = 0;
261 return 0;
264 guint32
265 mono_arch_cpu_enumerate_simd_versions (void)
267 return 0;
270 void
271 mono_arch_register_lowlevel_calls (void)
275 void
276 mono_arch_finish_init (void)
280 /* The maximum length is 2 instructions */
281 static guint8*
282 emit_imm (guint8 *code, int dreg, int imm)
284 // FIXME: Optimize this
285 if (imm < 0) {
286 gint64 limm = imm;
287 arm_movnx (code, dreg, (~limm) & 0xffff, 0);
288 arm_movkx (code, dreg, (limm >> 16) & 0xffff, 16);
289 } else {
290 arm_movzx (code, dreg, imm & 0xffff, 0);
291 if (imm >> 16)
292 arm_movkx (code, dreg, (imm >> 16) & 0xffff, 16);
295 return code;
298 /* The maximum length is 4 instructions */
299 static guint8*
300 emit_imm64 (guint8 *code, int dreg, guint64 imm)
302 // FIXME: Optimize this
303 arm_movzx (code, dreg, imm & 0xffff, 0);
304 if ((imm >> 16) & 0xffff)
305 arm_movkx (code, dreg, (imm >> 16) & 0xffff, 16);
306 if ((imm >> 32) & 0xffff)
307 arm_movkx (code, dreg, (imm >> 32) & 0xffff, 32);
308 if ((imm >> 48) & 0xffff)
309 arm_movkx (code, dreg, (imm >> 48) & 0xffff, 48);
311 return code;
314 guint8*
315 mono_arm_emit_imm64 (guint8 *code, int dreg, gint64 imm)
317 return emit_imm64 (code, dreg, imm);
321 * emit_imm_template:
323 * Emit a patchable code sequence for constructing a 64 bit immediate.
325 static guint8*
326 emit_imm64_template (guint8 *code, int dreg)
328 arm_movzx (code, dreg, 0, 0);
329 arm_movkx (code, dreg, 0, 16);
330 arm_movkx (code, dreg, 0, 32);
331 arm_movkx (code, dreg, 0, 48);
333 return code;
336 static inline __attribute__ ((__warn_unused_result__)) guint8*
337 emit_addw_imm (guint8 *code, int dreg, int sreg, int imm)
339 if (!arm_is_arith_imm (imm)) {
340 code = emit_imm (code, ARMREG_LR, imm);
341 arm_addw (code, dreg, sreg, ARMREG_LR);
342 } else {
343 arm_addw_imm (code, dreg, sreg, imm);
345 return code;
348 static inline __attribute__ ((__warn_unused_result__)) guint8*
349 emit_addx_imm (guint8 *code, int dreg, int sreg, int imm)
351 if (!arm_is_arith_imm (imm)) {
352 code = emit_imm (code, ARMREG_LR, imm);
353 arm_addx (code, dreg, sreg, ARMREG_LR);
354 } else {
355 arm_addx_imm (code, dreg, sreg, imm);
357 return code;
360 static inline __attribute__ ((__warn_unused_result__)) guint8*
361 emit_subw_imm (guint8 *code, int dreg, int sreg, int imm)
363 if (!arm_is_arith_imm (imm)) {
364 code = emit_imm (code, ARMREG_LR, imm);
365 arm_subw (code, dreg, sreg, ARMREG_LR);
366 } else {
367 arm_subw_imm (code, dreg, sreg, imm);
369 return code;
372 static inline __attribute__ ((__warn_unused_result__)) guint8*
373 emit_subx_imm (guint8 *code, int dreg, int sreg, int imm)
375 if (!arm_is_arith_imm (imm)) {
376 code = emit_imm (code, ARMREG_LR, imm);
377 arm_subx (code, dreg, sreg, ARMREG_LR);
378 } else {
379 arm_subx_imm (code, dreg, sreg, imm);
381 return code;
384 /* Emit sp+=imm. Clobbers ip0/ip1 */
385 static inline __attribute__ ((__warn_unused_result__)) guint8*
386 emit_addx_sp_imm (guint8 *code, int imm)
388 code = emit_imm (code, ARMREG_IP0, imm);
389 arm_movspx (code, ARMREG_IP1, ARMREG_SP);
390 arm_addx (code, ARMREG_IP1, ARMREG_IP1, ARMREG_IP0);
391 arm_movspx (code, ARMREG_SP, ARMREG_IP1);
392 return code;
395 /* Emit sp-=imm. Clobbers ip0/ip1 */
396 static inline __attribute__ ((__warn_unused_result__)) guint8*
397 emit_subx_sp_imm (guint8 *code, int imm)
399 code = emit_imm (code, ARMREG_IP0, imm);
400 arm_movspx (code, ARMREG_IP1, ARMREG_SP);
401 arm_subx (code, ARMREG_IP1, ARMREG_IP1, ARMREG_IP0);
402 arm_movspx (code, ARMREG_SP, ARMREG_IP1);
403 return code;
406 static inline __attribute__ ((__warn_unused_result__)) guint8*
407 emit_andw_imm (guint8 *code, int dreg, int sreg, int imm)
409 // FIXME:
410 code = emit_imm (code, ARMREG_LR, imm);
411 arm_andw (code, dreg, sreg, ARMREG_LR);
413 return code;
416 static inline __attribute__ ((__warn_unused_result__)) guint8*
417 emit_andx_imm (guint8 *code, int dreg, int sreg, int imm)
419 // FIXME:
420 code = emit_imm (code, ARMREG_LR, imm);
421 arm_andx (code, dreg, sreg, ARMREG_LR);
423 return code;
426 static inline __attribute__ ((__warn_unused_result__)) guint8*
427 emit_orrw_imm (guint8 *code, int dreg, int sreg, int imm)
429 // FIXME:
430 code = emit_imm (code, ARMREG_LR, imm);
431 arm_orrw (code, dreg, sreg, ARMREG_LR);
433 return code;
436 static inline __attribute__ ((__warn_unused_result__)) guint8*
437 emit_orrx_imm (guint8 *code, int dreg, int sreg, int imm)
439 // FIXME:
440 code = emit_imm (code, ARMREG_LR, imm);
441 arm_orrx (code, dreg, sreg, ARMREG_LR);
443 return code;
446 static inline __attribute__ ((__warn_unused_result__)) guint8*
447 emit_eorw_imm (guint8 *code, int dreg, int sreg, int imm)
449 // FIXME:
450 code = emit_imm (code, ARMREG_LR, imm);
451 arm_eorw (code, dreg, sreg, ARMREG_LR);
453 return code;
456 static inline __attribute__ ((__warn_unused_result__)) guint8*
457 emit_eorx_imm (guint8 *code, int dreg, int sreg, int imm)
459 // FIXME:
460 code = emit_imm (code, ARMREG_LR, imm);
461 arm_eorx (code, dreg, sreg, ARMREG_LR);
463 return code;
466 static inline __attribute__ ((__warn_unused_result__)) guint8*
467 emit_cmpw_imm (guint8 *code, int sreg, int imm)
469 if (imm == 0) {
470 arm_cmpw (code, sreg, ARMREG_RZR);
471 } else {
472 // FIXME:
473 code = emit_imm (code, ARMREG_LR, imm);
474 arm_cmpw (code, sreg, ARMREG_LR);
477 return code;
480 static inline __attribute__ ((__warn_unused_result__)) guint8*
481 emit_cmpx_imm (guint8 *code, int sreg, int imm)
483 if (imm == 0) {
484 arm_cmpx (code, sreg, ARMREG_RZR);
485 } else {
486 // FIXME:
487 code = emit_imm (code, ARMREG_LR, imm);
488 arm_cmpx (code, sreg, ARMREG_LR);
491 return code;
494 static inline __attribute__ ((__warn_unused_result__)) guint8*
495 emit_strb (guint8 *code, int rt, int rn, int imm)
497 if (arm_is_strb_imm (imm)) {
498 arm_strb (code, rt, rn, imm);
499 } else {
500 g_assert (rt != ARMREG_IP0);
501 g_assert (rn != ARMREG_IP0);
502 code = emit_imm (code, ARMREG_IP0, imm);
503 arm_strb_reg (code, rt, rn, ARMREG_IP0);
505 return code;
508 static inline __attribute__ ((__warn_unused_result__)) guint8*
509 emit_strh (guint8 *code, int rt, int rn, int imm)
511 if (arm_is_strh_imm (imm)) {
512 arm_strh (code, rt, rn, imm);
513 } else {
514 g_assert (rt != ARMREG_IP0);
515 g_assert (rn != ARMREG_IP0);
516 code = emit_imm (code, ARMREG_IP0, imm);
517 arm_strh_reg (code, rt, rn, ARMREG_IP0);
519 return code;
522 static inline __attribute__ ((__warn_unused_result__)) guint8*
523 emit_strw (guint8 *code, int rt, int rn, int imm)
525 if (arm_is_strw_imm (imm)) {
526 arm_strw (code, rt, rn, imm);
527 } else {
528 g_assert (rt != ARMREG_IP0);
529 g_assert (rn != ARMREG_IP0);
530 code = emit_imm (code, ARMREG_IP0, imm);
531 arm_strw_reg (code, rt, rn, ARMREG_IP0);
533 return code;
536 static inline __attribute__ ((__warn_unused_result__)) guint8*
537 emit_strfpw (guint8 *code, int rt, int rn, int imm)
539 if (arm_is_strw_imm (imm)) {
540 arm_strfpw (code, rt, rn, imm);
541 } else {
542 g_assert (rn != ARMREG_IP0);
543 code = emit_imm (code, ARMREG_IP0, imm);
544 arm_addx (code, ARMREG_IP0, rn, ARMREG_IP0);
545 arm_strfpw (code, rt, ARMREG_IP0, 0);
547 return code;
550 static inline __attribute__ ((__warn_unused_result__)) guint8*
551 emit_strfpx (guint8 *code, int rt, int rn, int imm)
553 if (arm_is_strx_imm (imm)) {
554 arm_strfpx (code, rt, rn, imm);
555 } else {
556 g_assert (rn != ARMREG_IP0);
557 code = emit_imm (code, ARMREG_IP0, imm);
558 arm_addx (code, ARMREG_IP0, rn, ARMREG_IP0);
559 arm_strfpx (code, rt, ARMREG_IP0, 0);
561 return code;
564 static inline __attribute__ ((__warn_unused_result__)) guint8*
565 emit_strx (guint8 *code, int rt, int rn, int imm)
567 if (arm_is_strx_imm (imm)) {
568 arm_strx (code, rt, rn, imm);
569 } else {
570 g_assert (rt != ARMREG_IP0);
571 g_assert (rn != ARMREG_IP0);
572 code = emit_imm (code, ARMREG_IP0, imm);
573 arm_strx_reg (code, rt, rn, ARMREG_IP0);
575 return code;
578 static inline __attribute__ ((__warn_unused_result__)) guint8*
579 emit_ldrb (guint8 *code, int rt, int rn, int imm)
581 if (arm_is_pimm12_scaled (imm, 1)) {
582 arm_ldrb (code, rt, rn, imm);
583 } else {
584 g_assert (rt != ARMREG_IP0);
585 g_assert (rn != ARMREG_IP0);
586 code = emit_imm (code, ARMREG_IP0, imm);
587 arm_ldrb_reg (code, rt, rn, ARMREG_IP0);
589 return code;
592 static inline __attribute__ ((__warn_unused_result__)) guint8*
593 emit_ldrsbx (guint8 *code, int rt, int rn, int imm)
595 if (arm_is_pimm12_scaled (imm, 1)) {
596 arm_ldrsbx (code, rt, rn, imm);
597 } else {
598 g_assert (rt != ARMREG_IP0);
599 g_assert (rn != ARMREG_IP0);
600 code = emit_imm (code, ARMREG_IP0, imm);
601 arm_ldrsbx_reg (code, rt, rn, ARMREG_IP0);
603 return code;
606 static inline __attribute__ ((__warn_unused_result__)) guint8*
607 emit_ldrh (guint8 *code, int rt, int rn, int imm)
609 if (arm_is_pimm12_scaled (imm, 2)) {
610 arm_ldrh (code, rt, rn, imm);
611 } else {
612 g_assert (rt != ARMREG_IP0);
613 g_assert (rn != ARMREG_IP0);
614 code = emit_imm (code, ARMREG_IP0, imm);
615 arm_ldrh_reg (code, rt, rn, ARMREG_IP0);
617 return code;
620 static inline __attribute__ ((__warn_unused_result__)) guint8*
621 emit_ldrshx (guint8 *code, int rt, int rn, int imm)
623 if (arm_is_pimm12_scaled (imm, 2)) {
624 arm_ldrshx (code, rt, rn, imm);
625 } else {
626 g_assert (rt != ARMREG_IP0);
627 g_assert (rn != ARMREG_IP0);
628 code = emit_imm (code, ARMREG_IP0, imm);
629 arm_ldrshx_reg (code, rt, rn, ARMREG_IP0);
631 return code;
634 static inline __attribute__ ((__warn_unused_result__)) guint8*
635 emit_ldrswx (guint8 *code, int rt, int rn, int imm)
637 if (arm_is_pimm12_scaled (imm, 4)) {
638 arm_ldrswx (code, rt, rn, imm);
639 } else {
640 g_assert (rt != ARMREG_IP0);
641 g_assert (rn != ARMREG_IP0);
642 code = emit_imm (code, ARMREG_IP0, imm);
643 arm_ldrswx_reg (code, rt, rn, ARMREG_IP0);
645 return code;
648 static inline __attribute__ ((__warn_unused_result__)) guint8*
649 emit_ldrw (guint8 *code, int rt, int rn, int imm)
651 if (arm_is_pimm12_scaled (imm, 4)) {
652 arm_ldrw (code, rt, rn, imm);
653 } else {
654 g_assert (rn != ARMREG_IP0);
655 code = emit_imm (code, ARMREG_IP0, imm);
656 arm_ldrw_reg (code, rt, rn, ARMREG_IP0);
658 return code;
661 static inline __attribute__ ((__warn_unused_result__)) guint8*
662 emit_ldrx (guint8 *code, int rt, int rn, int imm)
664 if (arm_is_pimm12_scaled (imm, 8)) {
665 arm_ldrx (code, rt, rn, imm);
666 } else {
667 g_assert (rn != ARMREG_IP0);
668 code = emit_imm (code, ARMREG_IP0, imm);
669 arm_ldrx_reg (code, rt, rn, ARMREG_IP0);
671 return code;
674 static inline __attribute__ ((__warn_unused_result__)) guint8*
675 emit_ldrfpw (guint8 *code, int rt, int rn, int imm)
677 if (arm_is_pimm12_scaled (imm, 4)) {
678 arm_ldrfpw (code, rt, rn, imm);
679 } else {
680 g_assert (rn != ARMREG_IP0);
681 code = emit_imm (code, ARMREG_IP0, imm);
682 arm_addx (code, ARMREG_IP0, rn, ARMREG_IP0);
683 arm_ldrfpw (code, rt, ARMREG_IP0, 0);
685 return code;
688 static inline __attribute__ ((__warn_unused_result__)) guint8*
689 emit_ldrfpx (guint8 *code, int rt, int rn, int imm)
691 if (arm_is_pimm12_scaled (imm, 8)) {
692 arm_ldrfpx (code, rt, rn, imm);
693 } else {
694 g_assert (rn != ARMREG_IP0);
695 code = emit_imm (code, ARMREG_IP0, imm);
696 arm_addx (code, ARMREG_IP0, rn, ARMREG_IP0);
697 arm_ldrfpx (code, rt, ARMREG_IP0, 0);
699 return code;
702 guint8*
703 mono_arm_emit_ldrx (guint8 *code, int rt, int rn, int imm)
705 return emit_ldrx (code, rt, rn, imm);
708 static guint8*
709 emit_call (MonoCompile *cfg, guint8* code, MonoJumpInfoType patch_type, gconstpointer data)
712 mono_add_patch_info_rel (cfg, code - cfg->native_code, patch_type, data, MONO_R_ARM64_IMM);
713 code = emit_imm64_template (code, ARMREG_LR);
714 arm_blrx (code, ARMREG_LR);
716 mono_add_patch_info_rel (cfg, code - cfg->native_code, patch_type, data, MONO_R_ARM64_BL);
717 arm_bl (code, code);
718 cfg->thunk_area += THUNK_SIZE;
719 return code;
722 static guint8*
723 emit_aotconst_full (MonoCompile *cfg, MonoJumpInfo **ji, guint8 *code, guint8 *start, int dreg, guint32 patch_type, gconstpointer data)
725 if (cfg)
726 mono_add_patch_info (cfg, code - cfg->native_code, (MonoJumpInfoType)patch_type, data);
727 else
728 *ji = mono_patch_info_list_prepend (*ji, code - start, (MonoJumpInfoType)patch_type, data);
729 /* See arch_emit_got_access () in aot-compiler.c */
730 arm_ldrx_lit (code, dreg, 0);
731 arm_nop (code);
732 arm_nop (code);
733 return code;
736 static guint8*
737 emit_aotconst (MonoCompile *cfg, guint8 *code, int dreg, guint32 patch_type, gconstpointer data)
739 return emit_aotconst_full (cfg, NULL, code, NULL, dreg, patch_type, data);
743 * mono_arm_emit_aotconst:
745 * Emit code to load an AOT constant into DREG. Usable from trampolines.
747 guint8*
748 mono_arm_emit_aotconst (gpointer ji, guint8 *code, guint8 *code_start, int dreg, guint32 patch_type, gconstpointer data)
750 return emit_aotconst_full (NULL, (MonoJumpInfo**)ji, code, code_start, dreg, patch_type, data);
753 gboolean
754 mono_arch_have_fast_tls (void)
756 #ifdef TARGET_IOS
757 return FALSE;
758 #else
759 return TRUE;
760 #endif
763 static guint8*
764 emit_tls_get (guint8 *code, int dreg, int tls_offset)
766 arm_mrs (code, dreg, ARM_MRS_REG_TPIDR_EL0);
767 if (tls_offset < 256) {
768 arm_ldrx (code, dreg, dreg, tls_offset);
769 } else {
770 code = emit_addx_imm (code, dreg, dreg, tls_offset);
771 arm_ldrx (code, dreg, dreg, 0);
773 return code;
776 static guint8*
777 emit_tls_set (guint8 *code, int sreg, int tls_offset)
779 int tmpreg = ARMREG_IP0;
781 g_assert (sreg != tmpreg);
782 arm_mrs (code, tmpreg, ARM_MRS_REG_TPIDR_EL0);
783 if (tls_offset < 256) {
784 arm_strx (code, sreg, tmpreg, tls_offset);
785 } else {
786 code = emit_addx_imm (code, tmpreg, tmpreg, tls_offset);
787 arm_strx (code, sreg, tmpreg, 0);
789 return code;
793 * Emits
794 * - mov sp, fp
795 * - ldrp [fp, lr], [sp], !stack_offfset
796 * Clobbers TEMP_REGS.
798 __attribute__ ((__warn_unused_result__)) guint8*
799 mono_arm_emit_destroy_frame (guint8 *code, int stack_offset, guint64 temp_regs)
801 // At least one of these registers must be available, or both.
802 gboolean const temp0 = (temp_regs & (1 << ARMREG_IP0)) != 0;
803 gboolean const temp1 = (temp_regs & (1 << ARMREG_IP1)) != 0;
804 g_assert (temp0 || temp1);
805 int const temp = temp0 ? ARMREG_IP0 : ARMREG_IP1;
807 arm_movspx (code, ARMREG_SP, ARMREG_FP);
809 if (arm_is_ldpx_imm (stack_offset)) {
810 arm_ldpx_post (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, stack_offset);
811 } else {
812 arm_ldpx (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, 0);
813 /* sp += stack_offset */
814 if (temp0 && temp1) {
815 code = emit_addx_sp_imm (code, stack_offset);
816 } else {
817 int imm = stack_offset;
819 /* Can't use addx_sp_imm () since we can't clobber both ip0/ip1 */
820 arm_addx_imm (code, temp, ARMREG_SP, 0);
821 while (imm > 256) {
822 arm_addx_imm (code, temp, temp, 256);
823 imm -= 256;
825 arm_addx_imm (code, ARMREG_SP, temp, imm);
828 return code;
831 #define is_call_imm(diff) ((gint)(diff) >= -33554432 && (gint)(diff) <= 33554431)
833 static guint8*
834 emit_thunk (guint8 *code, gconstpointer target)
836 guint8 *p = code;
838 arm_ldrx_lit (code, ARMREG_IP0, code + 8);
839 arm_brx (code, ARMREG_IP0);
840 *(guint64*)code = (guint64)target;
841 code += sizeof (guint64);
843 mono_arch_flush_icache (p, code - p);
844 return code;
847 static gpointer
848 create_thunk (MonoCompile *cfg, MonoDomain *domain, guchar *code, const guchar *target)
850 MonoJitInfo *ji;
851 MonoThunkJitInfo *info;
852 guint8 *thunks, *p;
853 int thunks_size;
854 guint8 *orig_target;
855 guint8 *target_thunk;
857 if (!domain)
858 domain = mono_domain_get ();
860 if (cfg) {
862 * This can be called multiple times during JITting,
863 * save the current position in cfg->arch to avoid
864 * doing a O(n^2) search.
866 if (!cfg->arch.thunks) {
867 cfg->arch.thunks = cfg->thunks;
868 cfg->arch.thunks_size = cfg->thunk_area;
870 thunks = cfg->arch.thunks;
871 thunks_size = cfg->arch.thunks_size;
872 if (!thunks_size) {
873 g_print ("thunk failed %p->%p, thunk space=%d method %s", code, target, thunks_size, mono_method_full_name (cfg->method, TRUE));
874 g_assert_not_reached ();
877 g_assert (*(guint32*)thunks == 0);
878 emit_thunk (thunks, target);
880 cfg->arch.thunks += THUNK_SIZE;
881 cfg->arch.thunks_size -= THUNK_SIZE;
883 return thunks;
884 } else {
885 ji = mini_jit_info_table_find (domain, (char*)code, NULL);
886 g_assert (ji);
887 info = mono_jit_info_get_thunk_info (ji);
888 g_assert (info);
890 thunks = (guint8*)ji->code_start + info->thunks_offset;
891 thunks_size = info->thunks_size;
893 orig_target = mono_arch_get_call_target (code + 4);
895 mono_domain_lock (domain);
897 target_thunk = NULL;
898 if (orig_target >= thunks && orig_target < thunks + thunks_size) {
899 /* The call already points to a thunk, because of trampolines etc. */
900 target_thunk = orig_target;
901 } else {
902 for (p = thunks; p < thunks + thunks_size; p += THUNK_SIZE) {
903 if (((guint32*)p) [0] == 0) {
904 /* Free entry */
905 target_thunk = p;
906 break;
907 } else if (((guint64*)p) [1] == (guint64)target) {
908 /* Thunk already points to target */
909 target_thunk = p;
910 break;
915 //printf ("THUNK: %p %p %p\n", code, target, target_thunk);
917 if (!target_thunk) {
918 mono_domain_unlock (domain);
919 g_print ("thunk failed %p->%p, thunk space=%d method %s", code, target, thunks_size, cfg ? mono_method_full_name (cfg->method, TRUE) : mono_method_full_name (jinfo_get_method (ji), TRUE));
920 g_assert_not_reached ();
923 emit_thunk (target_thunk, target);
925 mono_domain_unlock (domain);
927 return target_thunk;
931 static void
932 arm_patch_full (MonoCompile *cfg, MonoDomain *domain, guint8 *code, guint8 *target, int relocation)
934 switch (relocation) {
935 case MONO_R_ARM64_B:
936 if (arm_is_bl_disp (code, target)) {
937 arm_b (code, target);
938 } else {
939 gpointer thunk;
941 thunk = create_thunk (cfg, domain, code, target);
942 g_assert (arm_is_bl_disp (code, thunk));
943 arm_b (code, thunk);
945 break;
946 case MONO_R_ARM64_BCC: {
947 int cond;
949 cond = arm_get_bcc_cond (code);
950 arm_bcc (code, cond, target);
951 break;
953 case MONO_R_ARM64_CBZ:
954 arm_set_cbz_target (code, target);
955 break;
956 case MONO_R_ARM64_IMM: {
957 guint64 imm = (guint64)target;
958 int dreg;
960 /* emit_imm64_template () */
961 dreg = arm_get_movzx_rd (code);
962 arm_movzx (code, dreg, imm & 0xffff, 0);
963 arm_movkx (code, dreg, (imm >> 16) & 0xffff, 16);
964 arm_movkx (code, dreg, (imm >> 32) & 0xffff, 32);
965 arm_movkx (code, dreg, (imm >> 48) & 0xffff, 48);
966 break;
968 case MONO_R_ARM64_BL:
969 if (arm_is_bl_disp (code, target)) {
970 arm_bl (code, target);
971 } else {
972 gpointer thunk;
974 thunk = create_thunk (cfg, domain, code, target);
975 g_assert (arm_is_bl_disp (code, thunk));
976 arm_bl (code, thunk);
978 break;
979 default:
980 g_assert_not_reached ();
984 static void
985 arm_patch_rel (guint8 *code, guint8 *target, int relocation)
987 arm_patch_full (NULL, NULL, code, target, relocation);
990 void
991 mono_arm_patch (guint8 *code, guint8 *target, int relocation)
993 arm_patch_rel (code, target, relocation);
996 void
997 mono_arch_patch_code_new (MonoCompile *cfg, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji, gpointer target)
999 guint8 *ip;
1001 ip = ji->ip.i + code;
1003 switch (ji->type) {
1004 case MONO_PATCH_INFO_METHOD_JUMP:
1005 /* ji->relocation is not set by the caller */
1006 arm_patch_full (cfg, domain, ip, (guint8*)target, MONO_R_ARM64_B);
1007 break;
1008 default:
1009 arm_patch_full (cfg, domain, ip, (guint8*)target, ji->relocation);
1010 break;
1014 void
1015 mono_arch_free_jit_tls_data (MonoJitTlsData *tls)
1019 void
1020 mono_arch_flush_register_windows (void)
1024 MonoMethod*
1025 mono_arch_find_imt_method (host_mgreg_t *regs, guint8 *code)
1027 return (MonoMethod*)regs [MONO_ARCH_RGCTX_REG];
1030 MonoVTable*
1031 mono_arch_find_static_call_vtable (host_mgreg_t *regs, guint8 *code)
1033 return (MonoVTable*)regs [MONO_ARCH_RGCTX_REG];
1036 host_mgreg_t
1037 mono_arch_context_get_int_reg (MonoContext *ctx, int reg)
1039 return ctx->regs [reg];
1042 void
1043 mono_arch_context_set_int_reg (MonoContext *ctx, int reg, host_mgreg_t val)
1045 ctx->regs [reg] = val;
1049 * mono_arch_set_target:
1051 * Set the target architecture the JIT backend should generate code for, in the form
1052 * of a GNU target triplet. Only used in AOT mode.
1054 void
1055 mono_arch_set_target (char *mtriple)
1057 if (strstr (mtriple, "darwin") || strstr (mtriple, "ios")) {
1058 ios_abi = TRUE;
1062 static void
1063 add_general (CallInfo *cinfo, ArgInfo *ainfo, int size, gboolean sign)
1065 if (cinfo->gr >= PARAM_REGS) {
1066 ainfo->storage = ArgOnStack;
1067 if (ios_abi) {
1068 /* Assume size == align */
1069 } else {
1070 /* Put arguments into 8 byte aligned stack slots */
1071 size = 8;
1072 sign = FALSE;
1074 cinfo->stack_usage = ALIGN_TO (cinfo->stack_usage, size);
1075 ainfo->offset = cinfo->stack_usage;
1076 ainfo->slot_size = size;
1077 ainfo->sign = sign;
1078 cinfo->stack_usage += size;
1079 } else {
1080 ainfo->storage = ArgInIReg;
1081 ainfo->reg = cinfo->gr;
1082 cinfo->gr ++;
1086 static void
1087 add_fp (CallInfo *cinfo, ArgInfo *ainfo, gboolean single)
1089 int size = single ? 4 : 8;
1091 if (cinfo->fr >= FP_PARAM_REGS) {
1092 ainfo->storage = single ? ArgOnStackR4 : ArgOnStackR8;
1093 if (ios_abi) {
1094 cinfo->stack_usage = ALIGN_TO (cinfo->stack_usage, size);
1095 ainfo->offset = cinfo->stack_usage;
1096 ainfo->slot_size = size;
1097 cinfo->stack_usage += size;
1098 } else {
1099 ainfo->offset = cinfo->stack_usage;
1100 ainfo->slot_size = 8;
1101 /* Put arguments into 8 byte aligned stack slots */
1102 cinfo->stack_usage += 8;
1104 } else {
1105 if (single)
1106 ainfo->storage = ArgInFRegR4;
1107 else
1108 ainfo->storage = ArgInFReg;
1109 ainfo->reg = cinfo->fr;
1110 cinfo->fr ++;
1114 static gboolean
1115 is_hfa (MonoType *t, int *out_nfields, int *out_esize, int *field_offsets)
1117 MonoClass *klass;
1118 gpointer iter;
1119 MonoClassField *field;
1120 MonoType *ftype, *prev_ftype = NULL;
1121 int i, nfields = 0;
1123 klass = mono_class_from_mono_type_internal (t);
1124 iter = NULL;
1125 while ((field = mono_class_get_fields_internal (klass, &iter))) {
1126 if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
1127 continue;
1128 ftype = mono_field_get_type_internal (field);
1129 ftype = mini_get_underlying_type (ftype);
1131 if (MONO_TYPE_ISSTRUCT (ftype)) {
1132 int nested_nfields, nested_esize;
1133 int nested_field_offsets [16];
1135 if (!is_hfa (ftype, &nested_nfields, &nested_esize, nested_field_offsets))
1136 return FALSE;
1137 if (nested_esize == 4)
1138 ftype = m_class_get_byval_arg (mono_defaults.single_class);
1139 else
1140 ftype = m_class_get_byval_arg (mono_defaults.double_class);
1141 if (prev_ftype && prev_ftype->type != ftype->type)
1142 return FALSE;
1143 prev_ftype = ftype;
1144 for (i = 0; i < nested_nfields; ++i) {
1145 if (nfields + i < 4)
1146 field_offsets [nfields + i] = field->offset - MONO_ABI_SIZEOF (MonoObject) + nested_field_offsets [i];
1148 nfields += nested_nfields;
1149 } else {
1150 if (!(!ftype->byref && (ftype->type == MONO_TYPE_R4 || ftype->type == MONO_TYPE_R8)))
1151 return FALSE;
1152 if (prev_ftype && prev_ftype->type != ftype->type)
1153 return FALSE;
1154 prev_ftype = ftype;
1155 if (nfields < 4)
1156 field_offsets [nfields] = field->offset - MONO_ABI_SIZEOF (MonoObject);
1157 nfields ++;
1160 if (nfields == 0 || nfields > 4)
1161 return FALSE;
1162 *out_nfields = nfields;
1163 *out_esize = prev_ftype->type == MONO_TYPE_R4 ? 4 : 8;
1164 return TRUE;
1167 static void
1168 add_valuetype (CallInfo *cinfo, ArgInfo *ainfo, MonoType *t)
1170 int i, size, align_size, nregs, nfields, esize;
1171 int field_offsets [16];
1172 guint32 align;
1174 size = mini_type_stack_size_full (t, &align, cinfo->pinvoke);
1175 align_size = ALIGN_TO (size, 8);
1177 nregs = align_size / 8;
1178 if (is_hfa (t, &nfields, &esize, field_offsets)) {
1180 * The struct might include nested float structs aligned at 8,
1181 * so need to keep track of the offsets of the individual fields.
1183 if (cinfo->fr + nfields <= FP_PARAM_REGS) {
1184 ainfo->storage = ArgHFA;
1185 ainfo->reg = cinfo->fr;
1186 ainfo->nregs = nfields;
1187 ainfo->size = size;
1188 ainfo->esize = esize;
1189 for (i = 0; i < nfields; ++i)
1190 ainfo->foffsets [i] = field_offsets [i];
1191 cinfo->fr += ainfo->nregs;
1192 } else {
1193 ainfo->nfregs_to_skip = FP_PARAM_REGS > cinfo->fr ? FP_PARAM_REGS - cinfo->fr : 0;
1194 cinfo->fr = FP_PARAM_REGS;
1195 size = ALIGN_TO (size, 8);
1196 ainfo->storage = ArgVtypeOnStack;
1197 cinfo->stack_usage = ALIGN_TO (cinfo->stack_usage, align);
1198 ainfo->offset = cinfo->stack_usage;
1199 ainfo->size = size;
1200 ainfo->hfa = TRUE;
1201 ainfo->nregs = nfields;
1202 ainfo->esize = esize;
1203 cinfo->stack_usage += size;
1205 return;
1208 if (align_size > 16) {
1209 ainfo->storage = ArgVtypeByRef;
1210 ainfo->size = size;
1211 return;
1214 if (cinfo->gr + nregs > PARAM_REGS) {
1215 size = ALIGN_TO (size, 8);
1216 ainfo->storage = ArgVtypeOnStack;
1217 cinfo->stack_usage = ALIGN_TO (cinfo->stack_usage, align);
1218 ainfo->offset = cinfo->stack_usage;
1219 ainfo->size = size;
1220 cinfo->stack_usage += size;
1221 cinfo->gr = PARAM_REGS;
1222 } else {
1223 ainfo->storage = ArgVtypeInIRegs;
1224 ainfo->reg = cinfo->gr;
1225 ainfo->nregs = nregs;
1226 ainfo->size = size;
1227 cinfo->gr += nregs;
1231 static void
1232 add_param (CallInfo *cinfo, ArgInfo *ainfo, MonoType *t)
1234 MonoType *ptype;
1236 ptype = mini_get_underlying_type (t);
1237 switch (ptype->type) {
1238 case MONO_TYPE_I1:
1239 add_general (cinfo, ainfo, 1, TRUE);
1240 break;
1241 case MONO_TYPE_U1:
1242 add_general (cinfo, ainfo, 1, FALSE);
1243 break;
1244 case MONO_TYPE_I2:
1245 add_general (cinfo, ainfo, 2, TRUE);
1246 break;
1247 case MONO_TYPE_U2:
1248 add_general (cinfo, ainfo, 2, FALSE);
1249 break;
1250 case MONO_TYPE_I4:
1251 add_general (cinfo, ainfo, 4, TRUE);
1252 break;
1253 case MONO_TYPE_U4:
1254 add_general (cinfo, ainfo, 4, FALSE);
1255 break;
1256 case MONO_TYPE_I:
1257 case MONO_TYPE_U:
1258 case MONO_TYPE_PTR:
1259 case MONO_TYPE_FNPTR:
1260 case MONO_TYPE_OBJECT:
1261 case MONO_TYPE_U8:
1262 case MONO_TYPE_I8:
1263 add_general (cinfo, ainfo, 8, FALSE);
1264 break;
1265 case MONO_TYPE_R8:
1266 add_fp (cinfo, ainfo, FALSE);
1267 break;
1268 case MONO_TYPE_R4:
1269 add_fp (cinfo, ainfo, TRUE);
1270 break;
1271 case MONO_TYPE_VALUETYPE:
1272 case MONO_TYPE_TYPEDBYREF:
1273 add_valuetype (cinfo, ainfo, ptype);
1274 break;
1275 case MONO_TYPE_VOID:
1276 ainfo->storage = ArgNone;
1277 break;
1278 case MONO_TYPE_GENERICINST:
1279 if (!mono_type_generic_inst_is_valuetype (ptype)) {
1280 add_general (cinfo, ainfo, 8, FALSE);
1281 } else if (mini_is_gsharedvt_variable_type (ptype)) {
1283 * Treat gsharedvt arguments as large vtypes
1285 ainfo->storage = ArgVtypeByRef;
1286 ainfo->gsharedvt = TRUE;
1287 } else {
1288 add_valuetype (cinfo, ainfo, ptype);
1290 break;
1291 case MONO_TYPE_VAR:
1292 case MONO_TYPE_MVAR:
1293 g_assert (mini_is_gsharedvt_type (ptype));
1294 ainfo->storage = ArgVtypeByRef;
1295 ainfo->gsharedvt = TRUE;
1296 break;
1297 default:
1298 g_assert_not_reached ();
1299 break;
1304 * get_call_info:
1306 * Obtain information about a call according to the calling convention.
1308 static CallInfo*
1309 get_call_info (MonoMemPool *mp, MonoMethodSignature *sig)
1311 CallInfo *cinfo;
1312 ArgInfo *ainfo;
1313 int n, pstart, pindex;
1315 n = sig->hasthis + sig->param_count;
1317 if (mp)
1318 cinfo = mono_mempool_alloc0 (mp, sizeof (CallInfo) + (sizeof (ArgInfo) * n));
1319 else
1320 cinfo = g_malloc0 (sizeof (CallInfo) + (sizeof (ArgInfo) * n));
1322 cinfo->nargs = n;
1323 cinfo->pinvoke = sig->pinvoke;
1325 /* Return value */
1326 add_param (cinfo, &cinfo->ret, sig->ret);
1327 if (cinfo->ret.storage == ArgVtypeByRef)
1328 cinfo->ret.reg = ARMREG_R8;
1329 /* Reset state */
1330 cinfo->gr = 0;
1331 cinfo->fr = 0;
1332 cinfo->stack_usage = 0;
1334 /* Parameters */
1335 if (sig->hasthis)
1336 add_general (cinfo, cinfo->args + 0, 8, FALSE);
1337 pstart = 0;
1338 for (pindex = pstart; pindex < sig->param_count; ++pindex) {
1339 ainfo = cinfo->args + sig->hasthis + pindex;
1341 if ((sig->call_convention == MONO_CALL_VARARG) && (pindex == sig->sentinelpos)) {
1342 /* Prevent implicit arguments and sig_cookie from
1343 being passed in registers */
1344 cinfo->gr = PARAM_REGS;
1345 cinfo->fr = FP_PARAM_REGS;
1346 /* Emit the signature cookie just before the implicit arguments */
1347 add_param (cinfo, &cinfo->sig_cookie, mono_get_int_type ());
1350 add_param (cinfo, ainfo, sig->params [pindex]);
1351 if (ainfo->storage == ArgVtypeByRef) {
1352 /* Pass the argument address in the next register */
1353 if (cinfo->gr >= PARAM_REGS) {
1354 ainfo->storage = ArgVtypeByRefOnStack;
1355 cinfo->stack_usage = ALIGN_TO (cinfo->stack_usage, 8);
1356 ainfo->offset = cinfo->stack_usage;
1357 cinfo->stack_usage += 8;
1358 } else {
1359 ainfo->reg = cinfo->gr;
1360 cinfo->gr ++;
1365 /* Handle the case where there are no implicit arguments */
1366 if ((sig->call_convention == MONO_CALL_VARARG) && (pindex == sig->sentinelpos)) {
1367 /* Prevent implicit arguments and sig_cookie from
1368 being passed in registers */
1369 cinfo->gr = PARAM_REGS;
1370 cinfo->fr = FP_PARAM_REGS;
1371 /* Emit the signature cookie just before the implicit arguments */
1372 add_param (cinfo, &cinfo->sig_cookie, mono_get_int_type ());
1375 cinfo->stack_usage = ALIGN_TO (cinfo->stack_usage, MONO_ARCH_FRAME_ALIGNMENT);
1377 return cinfo;
1380 static int
1381 arg_need_temp (ArgInfo *ainfo)
1383 if (ainfo->storage == ArgHFA && ainfo->esize == 4)
1384 return ainfo->size;
1385 return 0;
1388 static gpointer
1389 arg_get_storage (CallContext *ccontext, ArgInfo *ainfo)
1391 switch (ainfo->storage) {
1392 case ArgVtypeInIRegs:
1393 case ArgInIReg:
1394 return &ccontext->gregs [ainfo->reg];
1395 case ArgInFReg:
1396 case ArgInFRegR4:
1397 case ArgHFA:
1398 return &ccontext->fregs [ainfo->reg];
1399 case ArgOnStack:
1400 case ArgOnStackR4:
1401 case ArgOnStackR8:
1402 case ArgVtypeOnStack:
1403 return ccontext->stack + ainfo->offset;
1404 case ArgVtypeByRef:
1405 return (gpointer) ccontext->gregs [ainfo->reg];
1406 default:
1407 g_error ("Arg storage type not yet supported");
1411 static void
1412 arg_get_val (CallContext *ccontext, ArgInfo *ainfo, gpointer dest)
1414 g_assert (arg_need_temp (ainfo));
1416 float *dest_float = (float*)dest;
1417 for (int k = 0; k < ainfo->nregs; k++) {
1418 *dest_float = *(float*)&ccontext->fregs [ainfo->reg + k];
1419 dest_float++;
1423 static void
1424 arg_set_val (CallContext *ccontext, ArgInfo *ainfo, gpointer src)
1426 g_assert (arg_need_temp (ainfo));
1428 float *src_float = (float*)src;
1429 for (int k = 0; k < ainfo->nregs; k++) {
1430 *(float*)&ccontext->fregs [ainfo->reg + k] = *src_float;
1431 src_float++;
1435 /* Set arguments in the ccontext (for i2n entry) */
1436 void
1437 mono_arch_set_native_call_context_args (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig)
1439 MonoEECallbacks *interp_cb = mini_get_interp_callbacks ();
1440 CallInfo *cinfo = get_call_info (NULL, sig);
1441 gpointer storage;
1442 ArgInfo *ainfo;
1444 memset (ccontext, 0, sizeof (CallContext));
1446 ccontext->stack_size = ALIGN_TO (cinfo->stack_usage, MONO_ARCH_FRAME_ALIGNMENT);
1447 if (ccontext->stack_size)
1448 ccontext->stack = (guint8*)g_calloc (1, ccontext->stack_size);
1450 if (sig->ret->type != MONO_TYPE_VOID) {
1451 ainfo = &cinfo->ret;
1452 if (ainfo->storage == ArgVtypeByRef) {
1453 storage = interp_cb->frame_arg_to_storage ((MonoInterpFrameHandle)frame, sig, -1);
1454 ccontext->gregs [cinfo->ret.reg] = (gsize)storage;
1458 g_assert (!sig->hasthis);
1460 for (int i = 0; i < sig->param_count; i++) {
1461 ainfo = &cinfo->args [i];
1463 if (ainfo->storage == ArgVtypeByRef) {
1464 ccontext->gregs [ainfo->reg] = (host_mgreg_t)interp_cb->frame_arg_to_storage ((MonoInterpFrameHandle)frame, sig, i);
1465 continue;
1468 int temp_size = arg_need_temp (ainfo);
1470 if (temp_size)
1471 storage = alloca (temp_size); // FIXME? alloca in a loop
1472 else
1473 storage = arg_get_storage (ccontext, ainfo);
1475 interp_cb->frame_arg_to_data ((MonoInterpFrameHandle)frame, sig, i, storage);
1476 if (temp_size)
1477 arg_set_val (ccontext, ainfo, storage);
1480 g_free (cinfo);
1483 /* Set return value in the ccontext (for n2i return) */
1484 void
1485 mono_arch_set_native_call_context_ret (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig)
1487 MonoEECallbacks *interp_cb;
1488 CallInfo *cinfo;
1489 gpointer storage;
1490 ArgInfo *ainfo;
1492 if (sig->ret->type == MONO_TYPE_VOID)
1493 return;
1495 interp_cb = mini_get_interp_callbacks ();
1496 cinfo = get_call_info (NULL, sig);
1497 ainfo = &cinfo->ret;
1499 if (ainfo->storage != ArgVtypeByRef) {
1500 int temp_size = arg_need_temp (ainfo);
1502 if (temp_size)
1503 storage = alloca (temp_size);
1504 else
1505 storage = arg_get_storage (ccontext, ainfo);
1506 memset (ccontext, 0, sizeof (CallContext)); // FIXME
1507 interp_cb->frame_arg_to_data ((MonoInterpFrameHandle)frame, sig, -1, storage);
1508 if (temp_size)
1509 arg_set_val (ccontext, ainfo, storage);
1512 g_free (cinfo);
1515 /* Gets the arguments from ccontext (for n2i entry) */
1516 void
1517 mono_arch_get_native_call_context_args (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig)
1519 MonoEECallbacks *interp_cb = mini_get_interp_callbacks ();
1520 CallInfo *cinfo = get_call_info (NULL, sig);
1521 gpointer storage;
1522 ArgInfo *ainfo;
1524 if (sig->ret->type != MONO_TYPE_VOID) {
1525 ainfo = &cinfo->ret;
1526 if (ainfo->storage == ArgVtypeByRef) {
1527 storage = (gpointer) ccontext->gregs [cinfo->ret.reg];
1528 interp_cb->frame_arg_set_storage ((MonoInterpFrameHandle)frame, sig, -1, storage);
1532 for (int i = 0; i < sig->param_count + sig->hasthis; i++) {
1533 ainfo = &cinfo->args [i];
1534 int temp_size = arg_need_temp (ainfo);
1536 if (temp_size) {
1537 storage = alloca (temp_size); // FIXME? alloca in a loop
1538 arg_get_val (ccontext, ainfo, storage);
1539 } else {
1540 storage = arg_get_storage (ccontext, ainfo);
1542 interp_cb->data_to_frame_arg ((MonoInterpFrameHandle)frame, sig, i, storage);
1545 g_free (cinfo);
1548 /* Gets the return value from ccontext (for i2n exit) */
1549 void
1550 mono_arch_get_native_call_context_ret (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig)
1552 MonoEECallbacks *interp_cb;
1553 CallInfo *cinfo;
1554 ArgInfo *ainfo;
1555 gpointer storage;
1557 if (sig->ret->type == MONO_TYPE_VOID)
1558 return;
1560 interp_cb = mini_get_interp_callbacks ();
1561 cinfo = get_call_info (NULL, sig);
1562 ainfo = &cinfo->ret;
1564 if (ainfo->storage != ArgVtypeByRef) {
1565 int temp_size = arg_need_temp (ainfo);
1567 if (temp_size) {
1568 storage = alloca (temp_size);
1569 arg_get_val (ccontext, ainfo, storage);
1570 } else {
1571 storage = arg_get_storage (ccontext, ainfo);
1573 interp_cb->data_to_frame_arg ((MonoInterpFrameHandle)frame, sig, -1, storage);
1576 g_free (cinfo);
1579 typedef struct {
1580 MonoMethodSignature *sig;
1581 CallInfo *cinfo;
1582 MonoType *rtype;
1583 MonoType **param_types;
1584 int n_fpargs, n_fpret, nullable_area;
1585 } ArchDynCallInfo;
1587 static gboolean
1588 dyn_call_supported (CallInfo *cinfo, MonoMethodSignature *sig)
1590 int i;
1592 // FIXME: Add more cases
1593 switch (cinfo->ret.storage) {
1594 case ArgNone:
1595 case ArgInIReg:
1596 case ArgInFReg:
1597 case ArgInFRegR4:
1598 case ArgVtypeByRef:
1599 break;
1600 case ArgVtypeInIRegs:
1601 if (cinfo->ret.nregs > 2)
1602 return FALSE;
1603 break;
1604 case ArgHFA:
1605 break;
1606 default:
1607 return FALSE;
1610 for (i = 0; i < cinfo->nargs; ++i) {
1611 ArgInfo *ainfo = &cinfo->args [i];
1613 switch (ainfo->storage) {
1614 case ArgInIReg:
1615 case ArgVtypeInIRegs:
1616 case ArgInFReg:
1617 case ArgInFRegR4:
1618 case ArgHFA:
1619 case ArgVtypeByRef:
1620 case ArgVtypeByRefOnStack:
1621 case ArgOnStack:
1622 case ArgVtypeOnStack:
1623 break;
1624 default:
1625 return FALSE;
1629 return TRUE;
1632 MonoDynCallInfo*
1633 mono_arch_dyn_call_prepare (MonoMethodSignature *sig)
1635 ArchDynCallInfo *info;
1636 CallInfo *cinfo;
1637 int i, aindex;
1639 cinfo = get_call_info (NULL, sig);
1641 if (!dyn_call_supported (cinfo, sig)) {
1642 g_free (cinfo);
1643 return NULL;
1646 info = g_new0 (ArchDynCallInfo, 1);
1647 // FIXME: Preprocess the info to speed up start_dyn_call ()
1648 info->sig = sig;
1649 info->cinfo = cinfo;
1650 info->rtype = mini_get_underlying_type (sig->ret);
1651 info->param_types = g_new0 (MonoType*, sig->param_count);
1652 for (i = 0; i < sig->param_count; ++i)
1653 info->param_types [i] = mini_get_underlying_type (sig->params [i]);
1655 switch (cinfo->ret.storage) {
1656 case ArgInFReg:
1657 case ArgInFRegR4:
1658 info->n_fpret = 1;
1659 break;
1660 case ArgHFA:
1661 info->n_fpret = cinfo->ret.nregs;
1662 break;
1663 default:
1664 break;
1667 for (aindex = 0; aindex < sig->param_count; aindex++) {
1668 MonoType *t = info->param_types [aindex];
1670 if (t->byref)
1671 continue;
1673 switch (t->type) {
1674 case MONO_TYPE_GENERICINST:
1675 if (t->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type_internal (t))) {
1676 MonoClass *klass = mono_class_from_mono_type_internal (t);
1677 int size;
1679 /* Nullables need a temporary buffer, its stored at the end of DynCallArgs.regs after the stack args */
1680 size = mono_class_value_size (klass, NULL);
1681 info->nullable_area += size;
1683 break;
1684 default:
1685 break;
1689 return (MonoDynCallInfo*)info;
1692 void
1693 mono_arch_dyn_call_free (MonoDynCallInfo *info)
1695 ArchDynCallInfo *ainfo = (ArchDynCallInfo*)info;
1697 g_free (ainfo->cinfo);
1698 g_free (ainfo->param_types);
1699 g_free (ainfo);
1703 mono_arch_dyn_call_get_buf_size (MonoDynCallInfo *info)
1705 ArchDynCallInfo *ainfo = (ArchDynCallInfo*)info;
1707 g_assert (ainfo->cinfo->stack_usage % MONO_ARCH_FRAME_ALIGNMENT == 0);
1708 return sizeof (DynCallArgs) + ainfo->cinfo->stack_usage + ainfo->nullable_area;
1711 static double
1712 bitcast_r4_to_r8 (float f)
1714 float *p = &f;
1716 return *(double*)p;
1719 static float
1720 bitcast_r8_to_r4 (double f)
1722 double *p = &f;
1724 return *(float*)p;
1727 void
1728 mono_arch_start_dyn_call (MonoDynCallInfo *info, gpointer **args, guint8 *ret, guint8 *buf)
1730 ArchDynCallInfo *dinfo = (ArchDynCallInfo*)info;
1731 DynCallArgs *p = (DynCallArgs*)buf;
1732 int aindex, arg_index, greg, i, pindex;
1733 MonoMethodSignature *sig = dinfo->sig;
1734 CallInfo *cinfo = dinfo->cinfo;
1735 int buffer_offset = 0;
1736 guint8 *nullable_buffer;
1738 p->res = 0;
1739 p->ret = ret;
1740 p->n_fpargs = dinfo->n_fpargs;
1741 p->n_fpret = dinfo->n_fpret;
1742 p->n_stackargs = cinfo->stack_usage / sizeof (host_mgreg_t);
1744 arg_index = 0;
1745 greg = 0;
1746 pindex = 0;
1748 /* Stored after the stack arguments */
1749 nullable_buffer = (guint8*)&(p->regs [PARAM_REGS + 1 + (cinfo->stack_usage / sizeof (host_mgreg_t))]);
1751 if (sig->hasthis)
1752 p->regs [greg ++] = (host_mgreg_t)*(args [arg_index ++]);
1754 if (cinfo->ret.storage == ArgVtypeByRef)
1755 p->regs [ARMREG_R8] = (host_mgreg_t)ret;
1757 for (aindex = pindex; aindex < sig->param_count; aindex++) {
1758 MonoType *t = dinfo->param_types [aindex];
1759 gpointer *arg = args [arg_index ++];
1760 ArgInfo *ainfo = &cinfo->args [aindex + sig->hasthis];
1761 int slot = -1;
1763 if (ainfo->storage == ArgOnStack || ainfo->storage == ArgVtypeOnStack || ainfo->storage == ArgVtypeByRefOnStack) {
1764 slot = PARAM_REGS + 1 + (ainfo->offset / sizeof (host_mgreg_t));
1765 } else {
1766 slot = ainfo->reg;
1769 if (t->byref) {
1770 p->regs [slot] = (host_mgreg_t)*arg;
1771 continue;
1774 if (ios_abi && ainfo->storage == ArgOnStack) {
1775 guint8 *stack_arg = (guint8*)&(p->regs [PARAM_REGS + 1]) + ainfo->offset;
1776 gboolean handled = TRUE;
1778 /* Special case arguments smaller than 1 machine word */
1779 switch (t->type) {
1780 case MONO_TYPE_U1:
1781 *(guint8*)stack_arg = *(guint8*)arg;
1782 break;
1783 case MONO_TYPE_I1:
1784 *(gint8*)stack_arg = *(gint8*)arg;
1785 break;
1786 case MONO_TYPE_U2:
1787 *(guint16*)stack_arg = *(guint16*)arg;
1788 break;
1789 case MONO_TYPE_I2:
1790 *(gint16*)stack_arg = *(gint16*)arg;
1791 break;
1792 case MONO_TYPE_I4:
1793 *(gint32*)stack_arg = *(gint32*)arg;
1794 break;
1795 case MONO_TYPE_U4:
1796 *(guint32*)stack_arg = *(guint32*)arg;
1797 break;
1798 default:
1799 handled = FALSE;
1800 break;
1802 if (handled)
1803 continue;
1806 switch (t->type) {
1807 case MONO_TYPE_OBJECT:
1808 case MONO_TYPE_PTR:
1809 case MONO_TYPE_I:
1810 case MONO_TYPE_U:
1811 case MONO_TYPE_I8:
1812 case MONO_TYPE_U8:
1813 p->regs [slot] = (host_mgreg_t)*arg;
1814 break;
1815 case MONO_TYPE_U1:
1816 p->regs [slot] = *(guint8*)arg;
1817 break;
1818 case MONO_TYPE_I1:
1819 p->regs [slot] = *(gint8*)arg;
1820 break;
1821 case MONO_TYPE_I2:
1822 p->regs [slot] = *(gint16*)arg;
1823 break;
1824 case MONO_TYPE_U2:
1825 p->regs [slot] = *(guint16*)arg;
1826 break;
1827 case MONO_TYPE_I4:
1828 p->regs [slot] = *(gint32*)arg;
1829 break;
1830 case MONO_TYPE_U4:
1831 p->regs [slot] = *(guint32*)arg;
1832 break;
1833 case MONO_TYPE_R4:
1834 p->fpregs [ainfo->reg] = bitcast_r4_to_r8 (*(float*)arg);
1835 p->n_fpargs ++;
1836 break;
1837 case MONO_TYPE_R8:
1838 p->fpregs [ainfo->reg] = *(double*)arg;
1839 p->n_fpargs ++;
1840 break;
1841 case MONO_TYPE_GENERICINST:
1842 if (MONO_TYPE_IS_REFERENCE (t)) {
1843 p->regs [slot] = (host_mgreg_t)*arg;
1844 break;
1845 } else {
1846 if (t->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type_internal (t))) {
1847 MonoClass *klass = mono_class_from_mono_type_internal (t);
1848 guint8 *nullable_buf;
1849 int size;
1852 * Use p->buffer as a temporary buffer since the data needs to be available after this call
1853 * if the nullable param is passed by ref.
1855 size = mono_class_value_size (klass, NULL);
1856 nullable_buf = nullable_buffer + buffer_offset;
1857 buffer_offset += size;
1858 g_assert (buffer_offset <= dinfo->nullable_area);
1860 /* The argument pointed to by arg is either a boxed vtype or null */
1861 mono_nullable_init (nullable_buf, (MonoObject*)arg, klass);
1863 arg = (gpointer*)nullable_buf;
1864 /* Fall though */
1865 } else {
1866 /* Fall though */
1869 case MONO_TYPE_VALUETYPE:
1870 switch (ainfo->storage) {
1871 case ArgVtypeInIRegs:
1872 for (i = 0; i < ainfo->nregs; ++i)
1873 p->regs [slot ++] = ((host_mgreg_t*)arg) [i];
1874 break;
1875 case ArgHFA:
1876 if (ainfo->esize == 4) {
1877 for (i = 0; i < ainfo->nregs; ++i)
1878 p->fpregs [ainfo->reg + i] = bitcast_r4_to_r8 (((float*)arg) [ainfo->foffsets [i] / 4]);
1879 } else {
1880 for (i = 0; i < ainfo->nregs; ++i)
1881 p->fpregs [ainfo->reg + i] = ((double*)arg) [ainfo->foffsets [i] / 8];
1883 p->n_fpargs += ainfo->nregs;
1884 break;
1885 case ArgVtypeByRef:
1886 case ArgVtypeByRefOnStack:
1887 p->regs [slot] = (host_mgreg_t)arg;
1888 break;
1889 case ArgVtypeOnStack:
1890 for (i = 0; i < ainfo->size / 8; ++i)
1891 p->regs [slot ++] = ((host_mgreg_t*)arg) [i];
1892 break;
1893 default:
1894 g_assert_not_reached ();
1895 break;
1897 break;
1898 default:
1899 g_assert_not_reached ();
1904 void
1905 mono_arch_finish_dyn_call (MonoDynCallInfo *info, guint8 *buf)
1907 ArchDynCallInfo *ainfo = (ArchDynCallInfo*)info;
1908 CallInfo *cinfo = ainfo->cinfo;
1909 DynCallArgs *args = (DynCallArgs*)buf;
1910 MonoType *ptype = ainfo->rtype;
1911 guint8 *ret = args->ret;
1912 host_mgreg_t res = args->res;
1913 host_mgreg_t res2 = args->res2;
1914 int i;
1916 if (cinfo->ret.storage == ArgVtypeByRef)
1917 return;
1919 switch (ptype->type) {
1920 case MONO_TYPE_VOID:
1921 *(gpointer*)ret = NULL;
1922 break;
1923 case MONO_TYPE_OBJECT:
1924 case MONO_TYPE_I:
1925 case MONO_TYPE_U:
1926 case MONO_TYPE_PTR:
1927 *(gpointer*)ret = (gpointer)res;
1928 break;
1929 case MONO_TYPE_I1:
1930 *(gint8*)ret = res;
1931 break;
1932 case MONO_TYPE_U1:
1933 *(guint8*)ret = res;
1934 break;
1935 case MONO_TYPE_I2:
1936 *(gint16*)ret = res;
1937 break;
1938 case MONO_TYPE_U2:
1939 *(guint16*)ret = res;
1940 break;
1941 case MONO_TYPE_I4:
1942 *(gint32*)ret = res;
1943 break;
1944 case MONO_TYPE_U4:
1945 *(guint32*)ret = res;
1946 break;
1947 case MONO_TYPE_I8:
1948 case MONO_TYPE_U8:
1949 *(guint64*)ret = res;
1950 break;
1951 case MONO_TYPE_R4:
1952 *(float*)ret = bitcast_r8_to_r4 (args->fpregs [0]);
1953 break;
1954 case MONO_TYPE_R8:
1955 *(double*)ret = args->fpregs [0];
1956 break;
1957 case MONO_TYPE_GENERICINST:
1958 if (MONO_TYPE_IS_REFERENCE (ptype)) {
1959 *(gpointer*)ret = (gpointer)res;
1960 break;
1961 } else {
1962 /* Fall though */
1964 case MONO_TYPE_VALUETYPE:
1965 switch (ainfo->cinfo->ret.storage) {
1966 case ArgVtypeInIRegs:
1967 *(host_mgreg_t*)ret = res;
1968 if (ainfo->cinfo->ret.nregs > 1)
1969 ((host_mgreg_t*)ret) [1] = res2;
1970 break;
1971 case ArgHFA:
1972 /* Use the same area for returning fp values */
1973 if (cinfo->ret.esize == 4) {
1974 for (i = 0; i < cinfo->ret.nregs; ++i)
1975 ((float*)ret) [cinfo->ret.foffsets [i] / 4] = bitcast_r8_to_r4 (args->fpregs [i]);
1976 } else {
1977 for (i = 0; i < cinfo->ret.nregs; ++i)
1978 ((double*)ret) [cinfo->ret.foffsets [i] / 8] = args->fpregs [i];
1980 break;
1981 default:
1982 g_assert_not_reached ();
1983 break;
1985 break;
1986 default:
1987 g_assert_not_reached ();
1991 #if __APPLE__
1992 G_BEGIN_DECLS
1993 void sys_icache_invalidate (void *start, size_t len);
1994 G_END_DECLS
1995 #endif
1997 void
1998 mono_arch_flush_icache (guint8 *code, gint size)
2000 #ifndef MONO_CROSS_COMPILE
2001 #if __APPLE__
2002 sys_icache_invalidate (code, size);
2003 #else
2004 /* Don't rely on GCC's __clear_cache implementation, as it caches
2005 * icache/dcache cache line sizes, that can vary between cores on
2006 * big.LITTLE architectures. */
2007 guint64 end = (guint64) (code + size);
2008 guint64 addr;
2009 /* always go with cacheline size of 4 bytes as this code isn't perf critical
2010 * anyway. Reading the cache line size from a machine register can be racy
2011 * on a big.LITTLE architecture if the cores don't have the same cache line
2012 * sizes. */
2013 const size_t icache_line_size = 4;
2014 const size_t dcache_line_size = 4;
2016 addr = (guint64) code & ~(guint64) (dcache_line_size - 1);
2017 for (; addr < end; addr += dcache_line_size)
2018 asm volatile("dc civac, %0" : : "r" (addr) : "memory");
2019 asm volatile("dsb ish" : : : "memory");
2021 addr = (guint64) code & ~(guint64) (icache_line_size - 1);
2022 for (; addr < end; addr += icache_line_size)
2023 asm volatile("ic ivau, %0" : : "r" (addr) : "memory");
2025 asm volatile ("dsb ish" : : : "memory");
2026 asm volatile ("isb" : : : "memory");
2027 #endif
2028 #endif
2031 #ifndef DISABLE_JIT
2033 gboolean
2034 mono_arch_opcode_needs_emulation (MonoCompile *cfg, int opcode)
2036 NOT_IMPLEMENTED;
2037 return FALSE;
2040 GList *
2041 mono_arch_get_allocatable_int_vars (MonoCompile *cfg)
2043 GList *vars = NULL;
2044 int i;
2046 for (i = 0; i < cfg->num_varinfo; i++) {
2047 MonoInst *ins = cfg->varinfo [i];
2048 MonoMethodVar *vmv = MONO_VARINFO (cfg, i);
2050 /* unused vars */
2051 if (vmv->range.first_use.abs_pos >= vmv->range.last_use.abs_pos)
2052 continue;
2054 if ((ins->flags & (MONO_INST_IS_DEAD|MONO_INST_VOLATILE|MONO_INST_INDIRECT)) ||
2055 (ins->opcode != OP_LOCAL && ins->opcode != OP_ARG))
2056 continue;
2058 if (mono_is_regsize_var (ins->inst_vtype)) {
2059 g_assert (MONO_VARINFO (cfg, i)->reg == -1);
2060 g_assert (i == vmv->idx);
2061 vars = g_list_prepend (vars, vmv);
2065 vars = mono_varlist_sort (cfg, vars, 0);
2067 return vars;
2070 GList *
2071 mono_arch_get_global_int_regs (MonoCompile *cfg)
2073 GList *regs = NULL;
2074 int i;
2076 /* r28 is reserved for cfg->arch.args_reg */
2077 /* r27 is reserved for the imt argument */
2078 for (i = ARMREG_R19; i <= ARMREG_R26; ++i)
2079 regs = g_list_prepend (regs, GUINT_TO_POINTER (i));
2081 return regs;
2084 guint32
2085 mono_arch_regalloc_cost (MonoCompile *cfg, MonoMethodVar *vmv)
2087 MonoInst *ins = cfg->varinfo [vmv->idx];
2089 if (ins->opcode == OP_ARG)
2090 return 1;
2091 else
2092 return 2;
2095 void
2096 mono_arch_create_vars (MonoCompile *cfg)
2098 MonoMethodSignature *sig;
2099 CallInfo *cinfo;
2101 sig = mono_method_signature_internal (cfg->method);
2102 if (!cfg->arch.cinfo)
2103 cfg->arch.cinfo = get_call_info (cfg->mempool, sig);
2104 cinfo = cfg->arch.cinfo;
2106 if (cinfo->ret.storage == ArgVtypeByRef) {
2107 cfg->vret_addr = mono_compile_create_var (cfg, mono_get_int_type (), OP_LOCAL);
2108 cfg->vret_addr->flags |= MONO_INST_VOLATILE;
2111 if (cfg->gen_sdb_seq_points) {
2112 MonoInst *ins;
2114 if (cfg->compile_aot) {
2115 ins = mono_compile_create_var (cfg, mono_get_int_type (), OP_LOCAL);
2116 ins->flags |= MONO_INST_VOLATILE;
2117 cfg->arch.seq_point_info_var = ins;
2120 ins = mono_compile_create_var (cfg, mono_get_int_type (), OP_LOCAL);
2121 ins->flags |= MONO_INST_VOLATILE;
2122 cfg->arch.ss_tramp_var = ins;
2124 ins = mono_compile_create_var (cfg, mono_get_int_type (), OP_LOCAL);
2125 ins->flags |= MONO_INST_VOLATILE;
2126 cfg->arch.bp_tramp_var = ins;
2129 if (cfg->method->save_lmf) {
2130 cfg->create_lmf_var = TRUE;
2131 cfg->lmf_ir = TRUE;
2135 void
2136 mono_arch_allocate_vars (MonoCompile *cfg)
2138 MonoMethodSignature *sig;
2139 MonoInst *ins;
2140 CallInfo *cinfo;
2141 ArgInfo *ainfo;
2142 int i, offset, size, align;
2143 guint32 locals_stack_size, locals_stack_align;
2144 gint32 *offsets;
2147 * Allocate arguments and locals to either register (OP_REGVAR) or to a stack slot (OP_REGOFFSET).
2148 * Compute cfg->stack_offset and update cfg->used_int_regs.
2151 sig = mono_method_signature_internal (cfg->method);
2153 if (!cfg->arch.cinfo)
2154 cfg->arch.cinfo = get_call_info (cfg->mempool, sig);
2155 cinfo = cfg->arch.cinfo;
2158 * The ARM64 ABI always uses a frame pointer.
2159 * The instruction set prefers positive offsets, so fp points to the bottom of the
2160 * frame, and stack slots are at positive offsets.
2161 * If some arguments are received on the stack, their offsets relative to fp can
2162 * not be computed right now because the stack frame might grow due to spilling
2163 * done by the local register allocator. To solve this, we reserve a register
2164 * which points to them.
2165 * The stack frame looks like this:
2166 * args_reg -> <bottom of parent frame>
2167 * <locals etc>
2168 * fp -> <saved fp+lr>
2169 * sp -> <localloc/params area>
2171 cfg->frame_reg = ARMREG_FP;
2172 cfg->flags |= MONO_CFG_HAS_SPILLUP;
2173 offset = 0;
2175 /* Saved fp+lr */
2176 offset += 16;
2178 if (cinfo->stack_usage) {
2179 g_assert (!(cfg->used_int_regs & (1 << ARMREG_R28)));
2180 cfg->arch.args_reg = ARMREG_R28;
2181 cfg->used_int_regs |= 1 << ARMREG_R28;
2184 if (cfg->method->save_lmf) {
2185 /* The LMF var is allocated normally */
2186 } else {
2187 /* Callee saved regs */
2188 cfg->arch.saved_gregs_offset = offset;
2189 for (i = 0; i < 32; ++i)
2190 if ((MONO_ARCH_CALLEE_SAVED_REGS & (1 << i)) && (cfg->used_int_regs & (1 << i)))
2191 offset += 8;
2194 /* Return value */
2195 switch (cinfo->ret.storage) {
2196 case ArgNone:
2197 break;
2198 case ArgInIReg:
2199 case ArgInFReg:
2200 case ArgInFRegR4:
2201 cfg->ret->opcode = OP_REGVAR;
2202 cfg->ret->dreg = cinfo->ret.reg;
2203 break;
2204 case ArgVtypeInIRegs:
2205 case ArgHFA:
2206 /* Allocate a local to hold the result, the epilog will copy it to the correct place */
2207 cfg->ret->opcode = OP_REGOFFSET;
2208 cfg->ret->inst_basereg = cfg->frame_reg;
2209 cfg->ret->inst_offset = offset;
2210 if (cinfo->ret.storage == ArgHFA)
2211 // FIXME:
2212 offset += 64;
2213 else
2214 offset += 16;
2215 break;
2216 case ArgVtypeByRef:
2217 /* This variable will be initalized in the prolog from R8 */
2218 cfg->vret_addr->opcode = OP_REGOFFSET;
2219 cfg->vret_addr->inst_basereg = cfg->frame_reg;
2220 cfg->vret_addr->inst_offset = offset;
2221 offset += 8;
2222 if (G_UNLIKELY (cfg->verbose_level > 1)) {
2223 printf ("vret_addr =");
2224 mono_print_ins (cfg->vret_addr);
2226 break;
2227 default:
2228 g_assert_not_reached ();
2229 break;
2232 /* Arguments */
2233 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
2234 ainfo = cinfo->args + i;
2236 ins = cfg->args [i];
2237 if (ins->opcode == OP_REGVAR)
2238 continue;
2240 ins->opcode = OP_REGOFFSET;
2241 ins->inst_basereg = cfg->frame_reg;
2243 switch (ainfo->storage) {
2244 case ArgInIReg:
2245 case ArgInFReg:
2246 case ArgInFRegR4:
2247 // FIXME: Use nregs/size
2248 /* These will be copied to the stack in the prolog */
2249 ins->inst_offset = offset;
2250 offset += 8;
2251 break;
2252 case ArgOnStack:
2253 case ArgOnStackR4:
2254 case ArgOnStackR8:
2255 case ArgVtypeOnStack:
2256 /* These are in the parent frame */
2257 g_assert (cfg->arch.args_reg);
2258 ins->inst_basereg = cfg->arch.args_reg;
2259 ins->inst_offset = ainfo->offset;
2260 break;
2261 case ArgVtypeInIRegs:
2262 case ArgHFA:
2263 ins->opcode = OP_REGOFFSET;
2264 ins->inst_basereg = cfg->frame_reg;
2265 /* These arguments are saved to the stack in the prolog */
2266 ins->inst_offset = offset;
2267 if (cfg->verbose_level >= 2)
2268 printf ("arg %d allocated to %s+0x%0x.\n", i, mono_arch_regname (ins->inst_basereg), (int)ins->inst_offset);
2269 if (ainfo->storage == ArgHFA)
2270 // FIXME:
2271 offset += 64;
2272 else
2273 offset += 16;
2274 break;
2275 case ArgVtypeByRefOnStack: {
2276 MonoInst *vtaddr;
2278 if (ainfo->gsharedvt) {
2279 ins->opcode = OP_REGOFFSET;
2280 ins->inst_basereg = cfg->arch.args_reg;
2281 ins->inst_offset = ainfo->offset;
2282 break;
2285 /* The vtype address is in the parent frame */
2286 g_assert (cfg->arch.args_reg);
2287 MONO_INST_NEW (cfg, vtaddr, 0);
2288 vtaddr->opcode = OP_REGOFFSET;
2289 vtaddr->inst_basereg = cfg->arch.args_reg;
2290 vtaddr->inst_offset = ainfo->offset;
2292 /* Need an indirection */
2293 ins->opcode = OP_VTARG_ADDR;
2294 ins->inst_left = vtaddr;
2295 break;
2297 case ArgVtypeByRef: {
2298 MonoInst *vtaddr;
2300 if (ainfo->gsharedvt) {
2301 ins->opcode = OP_REGOFFSET;
2302 ins->inst_basereg = cfg->frame_reg;
2303 ins->inst_offset = offset;
2304 offset += 8;
2305 break;
2308 /* The vtype address is in a register, will be copied to the stack in the prolog */
2309 MONO_INST_NEW (cfg, vtaddr, 0);
2310 vtaddr->opcode = OP_REGOFFSET;
2311 vtaddr->inst_basereg = cfg->frame_reg;
2312 vtaddr->inst_offset = offset;
2313 offset += 8;
2315 /* Need an indirection */
2316 ins->opcode = OP_VTARG_ADDR;
2317 ins->inst_left = vtaddr;
2318 break;
2320 default:
2321 g_assert_not_reached ();
2322 break;
2326 /* Allocate these first so they have a small offset, OP_SEQ_POINT depends on this */
2327 // FIXME: Allocate these to registers
2328 ins = cfg->arch.seq_point_info_var;
2329 if (ins) {
2330 size = 8;
2331 align = 8;
2332 offset += align - 1;
2333 offset &= ~(align - 1);
2334 ins->opcode = OP_REGOFFSET;
2335 ins->inst_basereg = cfg->frame_reg;
2336 ins->inst_offset = offset;
2337 offset += size;
2339 ins = cfg->arch.ss_tramp_var;
2340 if (ins) {
2341 size = 8;
2342 align = 8;
2343 offset += align - 1;
2344 offset &= ~(align - 1);
2345 ins->opcode = OP_REGOFFSET;
2346 ins->inst_basereg = cfg->frame_reg;
2347 ins->inst_offset = offset;
2348 offset += size;
2350 ins = cfg->arch.bp_tramp_var;
2351 if (ins) {
2352 size = 8;
2353 align = 8;
2354 offset += align - 1;
2355 offset &= ~(align - 1);
2356 ins->opcode = OP_REGOFFSET;
2357 ins->inst_basereg = cfg->frame_reg;
2358 ins->inst_offset = offset;
2359 offset += size;
2362 /* Locals */
2363 offsets = mono_allocate_stack_slots (cfg, FALSE, &locals_stack_size, &locals_stack_align);
2364 if (locals_stack_align)
2365 offset = ALIGN_TO (offset, locals_stack_align);
2367 for (i = cfg->locals_start; i < cfg->num_varinfo; i++) {
2368 if (offsets [i] != -1) {
2369 ins = cfg->varinfo [i];
2370 ins->opcode = OP_REGOFFSET;
2371 ins->inst_basereg = cfg->frame_reg;
2372 ins->inst_offset = offset + offsets [i];
2373 //printf ("allocated local %d to ", i); mono_print_tree_nl (ins);
2376 offset += locals_stack_size;
2378 offset = ALIGN_TO (offset, MONO_ARCH_FRAME_ALIGNMENT);
2380 cfg->stack_offset = offset;
2383 #ifdef ENABLE_LLVM
2384 LLVMCallInfo*
2385 mono_arch_get_llvm_call_info (MonoCompile *cfg, MonoMethodSignature *sig)
2387 int i, n;
2388 CallInfo *cinfo;
2389 ArgInfo *ainfo;
2390 LLVMCallInfo *linfo;
2392 n = sig->param_count + sig->hasthis;
2394 cinfo = get_call_info (cfg->mempool, sig);
2396 linfo = mono_mempool_alloc0 (cfg->mempool, sizeof (LLVMCallInfo) + (sizeof (LLVMArgInfo) * n));
2398 switch (cinfo->ret.storage) {
2399 case ArgInIReg:
2400 case ArgInFReg:
2401 case ArgInFRegR4:
2402 case ArgNone:
2403 break;
2404 case ArgVtypeByRef:
2405 linfo->ret.storage = LLVMArgVtypeByRef;
2406 break;
2408 // FIXME: This doesn't work yet since the llvm backend represents these types as an i8
2409 // array which is returned in int regs
2411 case ArgHFA:
2412 linfo->ret.storage = LLVMArgFpStruct;
2413 linfo->ret.nslots = cinfo->ret.nregs;
2414 linfo->ret.esize = cinfo->ret.esize;
2415 break;
2416 case ArgVtypeInIRegs:
2417 /* LLVM models this by returning an int */
2418 linfo->ret.storage = LLVMArgVtypeAsScalar;
2419 linfo->ret.nslots = cinfo->ret.nregs;
2420 linfo->ret.esize = cinfo->ret.esize;
2421 break;
2422 default:
2423 g_assert_not_reached ();
2424 break;
2427 for (i = 0; i < n; ++i) {
2428 LLVMArgInfo *lainfo = &linfo->args [i];
2430 ainfo = cinfo->args + i;
2432 lainfo->storage = LLVMArgNone;
2434 switch (ainfo->storage) {
2435 case ArgInIReg:
2436 case ArgInFReg:
2437 case ArgInFRegR4:
2438 case ArgOnStack:
2439 case ArgOnStackR4:
2440 case ArgOnStackR8:
2441 lainfo->storage = LLVMArgNormal;
2442 break;
2443 case ArgVtypeByRef:
2444 case ArgVtypeByRefOnStack:
2445 lainfo->storage = LLVMArgVtypeByRef;
2446 break;
2447 case ArgHFA: {
2448 int j;
2450 lainfo->storage = LLVMArgAsFpArgs;
2451 lainfo->nslots = ainfo->nregs;
2452 lainfo->esize = ainfo->esize;
2453 for (j = 0; j < ainfo->nregs; ++j)
2454 lainfo->pair_storage [j] = LLVMArgInFPReg;
2455 break;
2457 case ArgVtypeInIRegs:
2458 lainfo->storage = LLVMArgAsIArgs;
2459 lainfo->nslots = ainfo->nregs;
2460 break;
2461 case ArgVtypeOnStack:
2462 if (ainfo->hfa) {
2463 int j;
2464 /* Same as above */
2465 lainfo->storage = LLVMArgAsFpArgs;
2466 lainfo->nslots = ainfo->nregs;
2467 lainfo->esize = ainfo->esize;
2468 lainfo->ndummy_fpargs = ainfo->nfregs_to_skip;
2469 for (j = 0; j < ainfo->nregs; ++j)
2470 lainfo->pair_storage [j] = LLVMArgInFPReg;
2471 } else {
2472 lainfo->storage = LLVMArgAsIArgs;
2473 lainfo->nslots = ainfo->size / 8;
2475 break;
2476 default:
2477 g_assert_not_reached ();
2478 break;
2482 return linfo;
2484 #endif
2486 static void
2487 add_outarg_reg (MonoCompile *cfg, MonoCallInst *call, ArgStorage storage, int reg, MonoInst *arg)
2489 MonoInst *ins;
2491 switch (storage) {
2492 case ArgInIReg:
2493 MONO_INST_NEW (cfg, ins, OP_MOVE);
2494 ins->dreg = mono_alloc_ireg_copy (cfg, arg->dreg);
2495 ins->sreg1 = arg->dreg;
2496 MONO_ADD_INS (cfg->cbb, ins);
2497 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, reg, FALSE);
2498 break;
2499 case ArgInFReg:
2500 MONO_INST_NEW (cfg, ins, OP_FMOVE);
2501 ins->dreg = mono_alloc_freg (cfg);
2502 ins->sreg1 = arg->dreg;
2503 MONO_ADD_INS (cfg->cbb, ins);
2504 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, reg, TRUE);
2505 break;
2506 case ArgInFRegR4:
2507 if (COMPILE_LLVM (cfg))
2508 MONO_INST_NEW (cfg, ins, OP_FMOVE);
2509 else if (cfg->r4fp)
2510 MONO_INST_NEW (cfg, ins, OP_RMOVE);
2511 else
2512 MONO_INST_NEW (cfg, ins, OP_ARM_SETFREG_R4);
2513 ins->dreg = mono_alloc_freg (cfg);
2514 ins->sreg1 = arg->dreg;
2515 MONO_ADD_INS (cfg->cbb, ins);
2516 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, reg, TRUE);
2517 break;
2518 default:
2519 g_assert_not_reached ();
2520 break;
2524 static void
2525 emit_sig_cookie (MonoCompile *cfg, MonoCallInst *call, CallInfo *cinfo)
2527 MonoMethodSignature *tmp_sig;
2528 int sig_reg;
2530 if (MONO_IS_TAILCALL_OPCODE (call))
2531 NOT_IMPLEMENTED;
2533 g_assert (cinfo->sig_cookie.storage == ArgOnStack);
2536 * mono_ArgIterator_Setup assumes the signature cookie is
2537 * passed first and all the arguments which were before it are
2538 * passed on the stack after the signature. So compensate by
2539 * passing a different signature.
2541 tmp_sig = mono_metadata_signature_dup (call->signature);
2542 tmp_sig->param_count -= call->signature->sentinelpos;
2543 tmp_sig->sentinelpos = 0;
2544 memcpy (tmp_sig->params, call->signature->params + call->signature->sentinelpos, tmp_sig->param_count * sizeof (MonoType*));
2546 sig_reg = mono_alloc_ireg (cfg);
2547 MONO_EMIT_NEW_SIGNATURECONST (cfg, sig_reg, tmp_sig);
2549 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, cinfo->sig_cookie.offset, sig_reg);
2552 void
2553 mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
2555 MonoMethodSignature *sig;
2556 MonoInst *arg, *vtarg;
2557 CallInfo *cinfo;
2558 ArgInfo *ainfo;
2559 int i;
2561 sig = call->signature;
2563 cinfo = get_call_info (cfg->mempool, sig);
2565 switch (cinfo->ret.storage) {
2566 case ArgVtypeInIRegs:
2567 case ArgHFA:
2568 if (MONO_IS_TAILCALL_OPCODE (call))
2569 break;
2571 * The vtype is returned in registers, save the return area address in a local, and save the vtype into
2572 * the location pointed to by it after call in emit_move_return_value ().
2574 if (!cfg->arch.vret_addr_loc) {
2575 cfg->arch.vret_addr_loc = mono_compile_create_var (cfg, mono_get_int_type (), OP_LOCAL);
2576 /* Prevent it from being register allocated or optimized away */
2577 cfg->arch.vret_addr_loc->flags |= MONO_INST_VOLATILE;
2580 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->arch.vret_addr_loc->dreg, call->vret_var->dreg);
2581 break;
2582 case ArgVtypeByRef:
2583 /* Pass the vtype return address in R8 */
2584 g_assert (!MONO_IS_TAILCALL_OPCODE (call) || call->vret_var == cfg->vret_addr);
2585 MONO_INST_NEW (cfg, vtarg, OP_MOVE);
2586 vtarg->sreg1 = call->vret_var->dreg;
2587 vtarg->dreg = mono_alloc_preg (cfg);
2588 MONO_ADD_INS (cfg->cbb, vtarg);
2590 mono_call_inst_add_outarg_reg (cfg, call, vtarg->dreg, cinfo->ret.reg, FALSE);
2591 break;
2592 default:
2593 break;
2596 for (i = 0; i < cinfo->nargs; ++i) {
2597 ainfo = cinfo->args + i;
2598 arg = call->args [i];
2600 if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
2601 /* Emit the signature cookie just before the implicit arguments */
2602 emit_sig_cookie (cfg, call, cinfo);
2605 switch (ainfo->storage) {
2606 case ArgInIReg:
2607 case ArgInFReg:
2608 case ArgInFRegR4:
2609 add_outarg_reg (cfg, call, ainfo->storage, ainfo->reg, arg);
2610 break;
2611 case ArgOnStack:
2612 switch (ainfo->slot_size) {
2613 case 8:
2614 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, ainfo->offset, arg->dreg);
2615 break;
2616 case 4:
2617 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, ARMREG_SP, ainfo->offset, arg->dreg);
2618 break;
2619 case 2:
2620 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, ARMREG_SP, ainfo->offset, arg->dreg);
2621 break;
2622 case 1:
2623 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, ARMREG_SP, ainfo->offset, arg->dreg);
2624 break;
2625 default:
2626 g_assert_not_reached ();
2627 break;
2629 break;
2630 case ArgOnStackR8:
2631 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, ainfo->offset, arg->dreg);
2632 break;
2633 case ArgOnStackR4:
2634 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, ARMREG_SP, ainfo->offset, arg->dreg);
2635 break;
2636 case ArgVtypeInIRegs:
2637 case ArgVtypeByRef:
2638 case ArgVtypeByRefOnStack:
2639 case ArgVtypeOnStack:
2640 case ArgHFA: {
2641 MonoInst *ins;
2642 guint32 align;
2643 guint32 size;
2645 size = mono_class_value_size (arg->klass, &align);
2647 MONO_INST_NEW (cfg, ins, OP_OUTARG_VT);
2648 ins->sreg1 = arg->dreg;
2649 ins->klass = arg->klass;
2650 ins->backend.size = size;
2651 ins->inst_p0 = call;
2652 ins->inst_p1 = mono_mempool_alloc (cfg->mempool, sizeof (ArgInfo));
2653 memcpy (ins->inst_p1, ainfo, sizeof (ArgInfo));
2654 MONO_ADD_INS (cfg->cbb, ins);
2655 break;
2657 default:
2658 g_assert_not_reached ();
2659 break;
2663 /* Handle the case where there are no implicit arguments */
2664 if (!sig->pinvoke && (sig->call_convention == MONO_CALL_VARARG) && (cinfo->nargs == sig->sentinelpos))
2665 emit_sig_cookie (cfg, call, cinfo);
2667 call->call_info = cinfo;
2668 call->stack_usage = cinfo->stack_usage;
2671 void
2672 mono_arch_emit_outarg_vt (MonoCompile *cfg, MonoInst *ins, MonoInst *src)
2674 MonoCallInst *call = (MonoCallInst*)ins->inst_p0;
2675 ArgInfo *ainfo = (ArgInfo*)ins->inst_p1;
2676 MonoInst *load;
2677 int i;
2679 if (ins->backend.size == 0 && !ainfo->gsharedvt)
2680 return;
2682 switch (ainfo->storage) {
2683 case ArgVtypeInIRegs:
2684 for (i = 0; i < ainfo->nregs; ++i) {
2685 // FIXME: Smaller sizes
2686 MONO_INST_NEW (cfg, load, OP_LOADI8_MEMBASE);
2687 load->dreg = mono_alloc_ireg (cfg);
2688 load->inst_basereg = src->dreg;
2689 load->inst_offset = i * sizeof (target_mgreg_t);
2690 MONO_ADD_INS (cfg->cbb, load);
2691 add_outarg_reg (cfg, call, ArgInIReg, ainfo->reg + i, load);
2693 break;
2694 case ArgHFA:
2695 for (i = 0; i < ainfo->nregs; ++i) {
2696 if (ainfo->esize == 4)
2697 MONO_INST_NEW (cfg, load, OP_LOADR4_MEMBASE);
2698 else
2699 MONO_INST_NEW (cfg, load, OP_LOADR8_MEMBASE);
2700 load->dreg = mono_alloc_freg (cfg);
2701 load->inst_basereg = src->dreg;
2702 load->inst_offset = ainfo->foffsets [i];
2703 MONO_ADD_INS (cfg->cbb, load);
2704 add_outarg_reg (cfg, call, ainfo->esize == 4 ? ArgInFRegR4 : ArgInFReg, ainfo->reg + i, load);
2706 break;
2707 case ArgVtypeByRef:
2708 case ArgVtypeByRefOnStack: {
2709 MonoInst *vtaddr, *load, *arg;
2711 /* Pass the vtype address in a reg/on the stack */
2712 if (ainfo->gsharedvt) {
2713 load = src;
2714 } else {
2715 /* Make a copy of the argument */
2716 vtaddr = mono_compile_create_var (cfg, m_class_get_byval_arg (ins->klass), OP_LOCAL);
2718 MONO_INST_NEW (cfg, load, OP_LDADDR);
2719 load->inst_p0 = vtaddr;
2720 vtaddr->flags |= MONO_INST_INDIRECT;
2721 load->type = STACK_MP;
2722 load->klass = vtaddr->klass;
2723 load->dreg = mono_alloc_ireg (cfg);
2724 MONO_ADD_INS (cfg->cbb, load);
2725 mini_emit_memcpy (cfg, load->dreg, 0, src->dreg, 0, ainfo->size, 8);
2728 if (ainfo->storage == ArgVtypeByRef) {
2729 MONO_INST_NEW (cfg, arg, OP_MOVE);
2730 arg->dreg = mono_alloc_preg (cfg);
2731 arg->sreg1 = load->dreg;
2732 MONO_ADD_INS (cfg->cbb, arg);
2733 add_outarg_reg (cfg, call, ArgInIReg, ainfo->reg, arg);
2734 } else {
2735 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, ainfo->offset, load->dreg);
2737 break;
2739 case ArgVtypeOnStack:
2740 for (i = 0; i < ainfo->size / 8; ++i) {
2741 MONO_INST_NEW (cfg, load, OP_LOADI8_MEMBASE);
2742 load->dreg = mono_alloc_ireg (cfg);
2743 load->inst_basereg = src->dreg;
2744 load->inst_offset = i * 8;
2745 MONO_ADD_INS (cfg->cbb, load);
2746 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, ARMREG_SP, ainfo->offset + (i * 8), load->dreg);
2748 break;
2749 default:
2750 g_assert_not_reached ();
2751 break;
2755 void
2756 mono_arch_emit_setret (MonoCompile *cfg, MonoMethod *method, MonoInst *val)
2758 MonoMethodSignature *sig;
2759 CallInfo *cinfo;
2761 sig = mono_method_signature_internal (cfg->method);
2762 if (!cfg->arch.cinfo)
2763 cfg->arch.cinfo = get_call_info (cfg->mempool, sig);
2764 cinfo = cfg->arch.cinfo;
2766 switch (cinfo->ret.storage) {
2767 case ArgNone:
2768 break;
2769 case ArgInIReg:
2770 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->ret->dreg, val->dreg);
2771 break;
2772 case ArgInFReg:
2773 MONO_EMIT_NEW_UNALU (cfg, OP_FMOVE, cfg->ret->dreg, val->dreg);
2774 break;
2775 case ArgInFRegR4:
2776 if (COMPILE_LLVM (cfg))
2777 MONO_EMIT_NEW_UNALU (cfg, OP_FMOVE, cfg->ret->dreg, val->dreg);
2778 else if (cfg->r4fp)
2779 MONO_EMIT_NEW_UNALU (cfg, OP_RMOVE, cfg->ret->dreg, val->dreg);
2780 else
2781 MONO_EMIT_NEW_UNALU (cfg, OP_ARM_SETFREG_R4, cfg->ret->dreg, val->dreg);
2782 break;
2783 default:
2784 g_assert_not_reached ();
2785 break;
2789 #ifndef DISABLE_JIT
2791 gboolean
2792 mono_arch_tailcall_supported (MonoCompile *cfg, MonoMethodSignature *caller_sig, MonoMethodSignature *callee_sig, gboolean virtual_)
2794 g_assert (caller_sig);
2795 g_assert (callee_sig);
2797 CallInfo *caller_info = get_call_info (NULL, caller_sig);
2798 CallInfo *callee_info = get_call_info (NULL, callee_sig);
2800 gboolean res = IS_SUPPORTED_TAILCALL (callee_info->stack_usage <= caller_info->stack_usage)
2801 && IS_SUPPORTED_TAILCALL (caller_info->ret.storage == callee_info->ret.storage);
2803 // FIXME Limit stack_usage to 1G. emit_ldrx / strx has 32bit limits.
2804 res &= IS_SUPPORTED_TAILCALL (callee_info->stack_usage < (1 << 30));
2805 res &= IS_SUPPORTED_TAILCALL (caller_info->stack_usage < (1 << 30));
2807 // valuetype parameters are the address of a local
2808 const ArgInfo *ainfo;
2809 ainfo = callee_info->args + callee_sig->hasthis;
2810 for (int i = 0; res && i < callee_sig->param_count; ++i) {
2811 res = IS_SUPPORTED_TAILCALL (ainfo [i].storage != ArgVtypeByRef)
2812 && IS_SUPPORTED_TAILCALL (ainfo [i].storage != ArgVtypeByRefOnStack);
2815 g_free (caller_info);
2816 g_free (callee_info);
2818 return res;
2821 #endif
2823 gboolean
2824 mono_arch_is_inst_imm (int opcode, int imm_opcode, gint64 imm)
2826 return (imm >= -((gint64)1<<31) && imm <= (((gint64)1<<31)-1));
2829 void
2830 mono_arch_peephole_pass_1 (MonoCompile *cfg, MonoBasicBlock *bb)
2832 //NOT_IMPLEMENTED;
2835 void
2836 mono_arch_peephole_pass_2 (MonoCompile *cfg, MonoBasicBlock *bb)
2838 //NOT_IMPLEMENTED;
2841 #define ADD_NEW_INS(cfg,dest,op) do { \
2842 MONO_INST_NEW ((cfg), (dest), (op)); \
2843 mono_bblock_insert_before_ins (bb, ins, (dest)); \
2844 } while (0)
2846 void
2847 mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb)
2849 MonoInst *ins, *temp, *last_ins = NULL;
2851 MONO_BB_FOR_EACH_INS (bb, ins) {
2852 switch (ins->opcode) {
2853 case OP_SBB:
2854 case OP_ISBB:
2855 case OP_SUBCC:
2856 case OP_ISUBCC:
2857 if (ins->next && (ins->next->opcode == OP_COND_EXC_C || ins->next->opcode == OP_COND_EXC_IC))
2858 /* ARM sets the C flag to 1 if there was _no_ overflow */
2859 ins->next->opcode = OP_COND_EXC_NC;
2860 break;
2861 case OP_IDIV_IMM:
2862 case OP_IREM_IMM:
2863 case OP_IDIV_UN_IMM:
2864 case OP_IREM_UN_IMM:
2865 case OP_LREM_IMM:
2866 mono_decompose_op_imm (cfg, bb, ins);
2867 break;
2868 case OP_LOCALLOC_IMM:
2869 if (ins->inst_imm > 32) {
2870 ADD_NEW_INS (cfg, temp, OP_ICONST);
2871 temp->inst_c0 = ins->inst_imm;
2872 temp->dreg = mono_alloc_ireg (cfg);
2873 ins->sreg1 = temp->dreg;
2874 ins->opcode = mono_op_imm_to_op (ins->opcode);
2876 break;
2877 case OP_ICOMPARE_IMM:
2878 if (ins->inst_imm == 0 && ins->next && ins->next->opcode == OP_IBEQ) {
2879 ins->next->opcode = OP_ARM64_CBZW;
2880 ins->next->sreg1 = ins->sreg1;
2881 NULLIFY_INS (ins);
2882 } else if (ins->inst_imm == 0 && ins->next && ins->next->opcode == OP_IBNE_UN) {
2883 ins->next->opcode = OP_ARM64_CBNZW;
2884 ins->next->sreg1 = ins->sreg1;
2885 NULLIFY_INS (ins);
2887 break;
2888 case OP_LCOMPARE_IMM:
2889 case OP_COMPARE_IMM:
2890 if (ins->inst_imm == 0 && ins->next && ins->next->opcode == OP_LBEQ) {
2891 ins->next->opcode = OP_ARM64_CBZX;
2892 ins->next->sreg1 = ins->sreg1;
2893 NULLIFY_INS (ins);
2894 } else if (ins->inst_imm == 0 && ins->next && ins->next->opcode == OP_LBNE_UN) {
2895 ins->next->opcode = OP_ARM64_CBNZX;
2896 ins->next->sreg1 = ins->sreg1;
2897 NULLIFY_INS (ins);
2899 break;
2900 case OP_FCOMPARE:
2901 case OP_RCOMPARE: {
2902 gboolean swap = FALSE;
2903 int reg;
2905 if (!ins->next) {
2906 /* Optimized away */
2907 NULLIFY_INS (ins);
2908 break;
2912 * FP compares with unordered operands set the flags
2913 * to NZCV=0011, which matches some non-unordered compares
2914 * as well, like LE, so have to swap the operands.
2916 switch (ins->next->opcode) {
2917 case OP_FBLT:
2918 ins->next->opcode = OP_FBGT;
2919 swap = TRUE;
2920 break;
2921 case OP_FBLE:
2922 ins->next->opcode = OP_FBGE;
2923 swap = TRUE;
2924 break;
2925 case OP_RBLT:
2926 ins->next->opcode = OP_RBGT;
2927 swap = TRUE;
2928 break;
2929 case OP_RBLE:
2930 ins->next->opcode = OP_RBGE;
2931 swap = TRUE;
2932 break;
2933 default:
2934 break;
2936 if (swap) {
2937 reg = ins->sreg1;
2938 ins->sreg1 = ins->sreg2;
2939 ins->sreg2 = reg;
2941 break;
2943 default:
2944 break;
2947 last_ins = ins;
2949 bb->last_ins = last_ins;
2950 bb->max_vreg = cfg->next_vreg;
2953 void
2954 mono_arch_decompose_long_opts (MonoCompile *cfg, MonoInst *long_ins)
2958 static int
2959 opcode_to_armcond (int opcode)
2961 switch (opcode) {
2962 case OP_IBEQ:
2963 case OP_LBEQ:
2964 case OP_FBEQ:
2965 case OP_CEQ:
2966 case OP_ICEQ:
2967 case OP_LCEQ:
2968 case OP_FCEQ:
2969 case OP_RCEQ:
2970 case OP_COND_EXC_IEQ:
2971 case OP_COND_EXC_EQ:
2972 return ARMCOND_EQ;
2973 case OP_IBGE:
2974 case OP_LBGE:
2975 case OP_FBGE:
2976 case OP_ICGE:
2977 case OP_FCGE:
2978 case OP_RCGE:
2979 return ARMCOND_GE;
2980 case OP_IBGT:
2981 case OP_LBGT:
2982 case OP_FBGT:
2983 case OP_CGT:
2984 case OP_ICGT:
2985 case OP_LCGT:
2986 case OP_FCGT:
2987 case OP_RCGT:
2988 case OP_COND_EXC_IGT:
2989 case OP_COND_EXC_GT:
2990 return ARMCOND_GT;
2991 case OP_IBLE:
2992 case OP_LBLE:
2993 case OP_FBLE:
2994 case OP_ICLE:
2995 case OP_FCLE:
2996 case OP_RCLE:
2997 return ARMCOND_LE;
2998 case OP_IBLT:
2999 case OP_LBLT:
3000 case OP_FBLT:
3001 case OP_CLT:
3002 case OP_ICLT:
3003 case OP_LCLT:
3004 case OP_COND_EXC_ILT:
3005 case OP_COND_EXC_LT:
3006 return ARMCOND_LT;
3007 case OP_IBNE_UN:
3008 case OP_LBNE_UN:
3009 case OP_FBNE_UN:
3010 case OP_ICNEQ:
3011 case OP_FCNEQ:
3012 case OP_RCNEQ:
3013 case OP_COND_EXC_INE_UN:
3014 case OP_COND_EXC_NE_UN:
3015 return ARMCOND_NE;
3016 case OP_IBGE_UN:
3017 case OP_LBGE_UN:
3018 case OP_FBGE_UN:
3019 case OP_ICGE_UN:
3020 case OP_COND_EXC_IGE_UN:
3021 case OP_COND_EXC_GE_UN:
3022 return ARMCOND_HS;
3023 case OP_IBGT_UN:
3024 case OP_LBGT_UN:
3025 case OP_FBGT_UN:
3026 case OP_CGT_UN:
3027 case OP_ICGT_UN:
3028 case OP_LCGT_UN:
3029 case OP_FCGT_UN:
3030 case OP_RCGT_UN:
3031 case OP_COND_EXC_IGT_UN:
3032 case OP_COND_EXC_GT_UN:
3033 return ARMCOND_HI;
3034 case OP_IBLE_UN:
3035 case OP_LBLE_UN:
3036 case OP_FBLE_UN:
3037 case OP_ICLE_UN:
3038 case OP_COND_EXC_ILE_UN:
3039 case OP_COND_EXC_LE_UN:
3040 return ARMCOND_LS;
3041 case OP_IBLT_UN:
3042 case OP_LBLT_UN:
3043 case OP_FBLT_UN:
3044 case OP_CLT_UN:
3045 case OP_ICLT_UN:
3046 case OP_LCLT_UN:
3047 case OP_COND_EXC_ILT_UN:
3048 case OP_COND_EXC_LT_UN:
3049 return ARMCOND_LO;
3051 * FCMP sets the NZCV condition bits as follows:
3052 * eq = 0110
3053 * < = 1000
3054 * > = 0010
3055 * unordered = 0011
3056 * ARMCOND_LT is N!=V, so it matches unordered too, so
3057 * fclt and fclt_un need to be special cased.
3059 case OP_FCLT:
3060 case OP_RCLT:
3061 /* N==1 */
3062 return ARMCOND_MI;
3063 case OP_FCLT_UN:
3064 case OP_RCLT_UN:
3065 return ARMCOND_LT;
3066 case OP_COND_EXC_C:
3067 case OP_COND_EXC_IC:
3068 return ARMCOND_CS;
3069 case OP_COND_EXC_OV:
3070 case OP_COND_EXC_IOV:
3071 return ARMCOND_VS;
3072 case OP_COND_EXC_NC:
3073 case OP_COND_EXC_INC:
3074 return ARMCOND_CC;
3075 case OP_COND_EXC_NO:
3076 case OP_COND_EXC_INO:
3077 return ARMCOND_VC;
3078 default:
3079 printf ("%s\n", mono_inst_name (opcode));
3080 g_assert_not_reached ();
3081 return -1;
3085 /* This clobbers LR */
3086 static inline __attribute__ ((__warn_unused_result__)) guint8*
3087 emit_cond_exc (MonoCompile *cfg, guint8 *code, int opcode, const char *exc_name)
3089 int cond;
3091 cond = opcode_to_armcond (opcode);
3092 /* Capture PC */
3093 arm_adrx (code, ARMREG_IP1, code);
3094 mono_add_patch_info_rel (cfg, code - cfg->native_code, MONO_PATCH_INFO_EXC, exc_name, MONO_R_ARM64_BCC);
3095 arm_bcc (code, cond, 0);
3096 return code;
3099 static guint8*
3100 emit_move_return_value (MonoCompile *cfg, guint8 * code, MonoInst *ins)
3102 CallInfo *cinfo;
3103 MonoCallInst *call;
3105 call = (MonoCallInst*)ins;
3106 cinfo = call->call_info;
3107 g_assert (cinfo);
3108 switch (cinfo->ret.storage) {
3109 case ArgNone:
3110 break;
3111 case ArgInIReg:
3112 /* LLVM compiled code might only set the bottom bits */
3113 if (call->signature && mini_get_underlying_type (call->signature->ret)->type == MONO_TYPE_I4)
3114 arm_sxtwx (code, call->inst.dreg, cinfo->ret.reg);
3115 else if (call->inst.dreg != cinfo->ret.reg)
3116 arm_movx (code, call->inst.dreg, cinfo->ret.reg);
3117 break;
3118 case ArgInFReg:
3119 if (call->inst.dreg != cinfo->ret.reg)
3120 arm_fmovd (code, call->inst.dreg, cinfo->ret.reg);
3121 break;
3122 case ArgInFRegR4:
3123 if (cfg->r4fp)
3124 arm_fmovs (code, call->inst.dreg, cinfo->ret.reg);
3125 else
3126 arm_fcvt_sd (code, call->inst.dreg, cinfo->ret.reg);
3127 break;
3128 case ArgVtypeInIRegs: {
3129 MonoInst *loc = cfg->arch.vret_addr_loc;
3130 int i;
3132 /* Load the destination address */
3133 g_assert (loc && loc->opcode == OP_REGOFFSET);
3134 code = emit_ldrx (code, ARMREG_LR, loc->inst_basereg, loc->inst_offset);
3135 for (i = 0; i < cinfo->ret.nregs; ++i)
3136 arm_strx (code, cinfo->ret.reg + i, ARMREG_LR, i * 8);
3137 break;
3139 case ArgHFA: {
3140 MonoInst *loc = cfg->arch.vret_addr_loc;
3141 int i;
3143 /* Load the destination address */
3144 g_assert (loc && loc->opcode == OP_REGOFFSET);
3145 code = emit_ldrx (code, ARMREG_LR, loc->inst_basereg, loc->inst_offset);
3146 for (i = 0; i < cinfo->ret.nregs; ++i) {
3147 if (cinfo->ret.esize == 4)
3148 arm_strfpw (code, cinfo->ret.reg + i, ARMREG_LR, cinfo->ret.foffsets [i]);
3149 else
3150 arm_strfpx (code, cinfo->ret.reg + i, ARMREG_LR, cinfo->ret.foffsets [i]);
3152 break;
3154 case ArgVtypeByRef:
3155 break;
3156 default:
3157 g_assert_not_reached ();
3158 break;
3160 return code;
3164 * emit_branch_island:
3166 * Emit a branch island for the conditional branches from cfg->native_code + start_offset to code.
3168 static guint8*
3169 emit_branch_island (MonoCompile *cfg, guint8 *code, int start_offset)
3171 MonoJumpInfo *ji;
3173 /* Iterate over the patch infos added so far by this bb */
3174 int island_size = 0;
3175 for (ji = cfg->patch_info; ji; ji = ji->next) {
3176 if (ji->ip.i < start_offset)
3177 /* The patch infos are in reverse order, so this means the end */
3178 break;
3179 if (ji->relocation == MONO_R_ARM64_BCC || ji->relocation == MONO_R_ARM64_CBZ)
3180 island_size += 4;
3183 if (island_size) {
3184 code = realloc_code (cfg, island_size);
3186 /* Branch over the island */
3187 arm_b (code, code + 4 + island_size);
3189 for (ji = cfg->patch_info; ji; ji = ji->next) {
3190 if (ji->ip.i < start_offset)
3191 break;
3192 if (ji->relocation == MONO_R_ARM64_BCC || ji->relocation == MONO_R_ARM64_CBZ) {
3193 /* Rewrite the cond branch so it branches to an unconditional branch in the branch island */
3194 arm_patch_rel (cfg->native_code + ji->ip.i, code, ji->relocation);
3195 /* Rewrite the patch so it points to the unconditional branch */
3196 ji->ip.i = code - cfg->native_code;
3197 ji->relocation = MONO_R_ARM64_B;
3198 arm_b (code, code);
3201 set_code_cursor (cfg, code);
3203 return code;
3206 void
3207 mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
3209 MonoInst *ins;
3210 MonoCallInst *call;
3211 guint8 *code = cfg->native_code + cfg->code_len;
3212 int start_offset, max_len, dreg, sreg1, sreg2;
3213 target_mgreg_t imm;
3215 if (cfg->verbose_level > 2)
3216 g_print ("Basic block %d starting at offset 0x%x\n", bb->block_num, bb->native_offset);
3218 start_offset = code - cfg->native_code;
3219 g_assert (start_offset <= cfg->code_size);
3221 MONO_BB_FOR_EACH_INS (bb, ins) {
3222 guint offset = code - cfg->native_code;
3223 set_code_cursor (cfg, code);
3224 max_len = ins_get_size (ins->opcode);
3225 code = realloc_code (cfg, max_len);
3227 if (G_UNLIKELY (cfg->arch.cond_branch_islands && offset - start_offset > 4 * 0x1ffff)) {
3228 /* Emit a branch island for large basic blocks */
3229 code = emit_branch_island (cfg, code, start_offset);
3230 offset = code - cfg->native_code;
3231 start_offset = offset;
3234 mono_debug_record_line_number (cfg, ins, offset);
3236 dreg = ins->dreg;
3237 sreg1 = ins->sreg1;
3238 sreg2 = ins->sreg2;
3239 imm = ins->inst_imm;
3241 switch (ins->opcode) {
3242 case OP_ICONST:
3243 code = emit_imm (code, dreg, ins->inst_c0);
3244 break;
3245 case OP_I8CONST:
3246 code = emit_imm64 (code, dreg, ins->inst_c0);
3247 break;
3248 case OP_MOVE:
3249 if (dreg != sreg1)
3250 arm_movx (code, dreg, sreg1);
3251 break;
3252 case OP_NOP:
3253 case OP_RELAXED_NOP:
3254 break;
3255 case OP_JUMP_TABLE:
3256 mono_add_patch_info_rel (cfg, offset, (MonoJumpInfoType)(gsize)ins->inst_i1, ins->inst_p0, MONO_R_ARM64_IMM);
3257 code = emit_imm64_template (code, dreg);
3258 break;
3259 case OP_BREAK:
3261 * gdb does not like encountering the hw breakpoint ins in the debugged code.
3262 * So instead of emitting a trap, we emit a call a C function and place a
3263 * breakpoint there.
3265 code = emit_call (cfg, code, MONO_PATCH_INFO_JIT_ICALL, (gpointer)"mono_break");
3266 break;
3267 case OP_LOCALLOC: {
3268 guint8 *buf [16];
3270 arm_addx_imm (code, ARMREG_IP0, sreg1, (MONO_ARCH_FRAME_ALIGNMENT - 1));
3271 // FIXME: andx_imm doesn't work yet
3272 code = emit_imm (code, ARMREG_IP1, -MONO_ARCH_FRAME_ALIGNMENT);
3273 arm_andx (code, ARMREG_IP0, ARMREG_IP0, ARMREG_IP1);
3274 //arm_andx_imm (code, ARMREG_IP0, sreg1, - MONO_ARCH_FRAME_ALIGNMENT);
3275 arm_movspx (code, ARMREG_IP1, ARMREG_SP);
3276 arm_subx (code, ARMREG_IP1, ARMREG_IP1, ARMREG_IP0);
3277 arm_movspx (code, ARMREG_SP, ARMREG_IP1);
3279 /* Init */
3280 /* ip1 = pointer, ip0 = end */
3281 arm_addx (code, ARMREG_IP0, ARMREG_IP1, ARMREG_IP0);
3282 buf [0] = code;
3283 arm_cmpx (code, ARMREG_IP1, ARMREG_IP0);
3284 buf [1] = code;
3285 arm_bcc (code, ARMCOND_EQ, 0);
3286 arm_stpx (code, ARMREG_RZR, ARMREG_RZR, ARMREG_IP1, 0);
3287 arm_addx_imm (code, ARMREG_IP1, ARMREG_IP1, 16);
3288 arm_b (code, buf [0]);
3289 arm_patch_rel (buf [1], code, MONO_R_ARM64_BCC);
3291 arm_movspx (code, dreg, ARMREG_SP);
3292 if (cfg->param_area)
3293 code = emit_subx_sp_imm (code, cfg->param_area);
3294 break;
3296 case OP_LOCALLOC_IMM: {
3297 int imm, offset;
3299 imm = ALIGN_TO (ins->inst_imm, MONO_ARCH_FRAME_ALIGNMENT);
3300 g_assert (arm_is_arith_imm (imm));
3301 arm_subx_imm (code, ARMREG_SP, ARMREG_SP, imm);
3303 /* Init */
3304 g_assert (MONO_ARCH_FRAME_ALIGNMENT == 16);
3305 offset = 0;
3306 while (offset < imm) {
3307 arm_stpx (code, ARMREG_RZR, ARMREG_RZR, ARMREG_SP, offset);
3308 offset += 16;
3310 arm_movspx (code, dreg, ARMREG_SP);
3311 if (cfg->param_area)
3312 code = emit_subx_sp_imm (code, cfg->param_area);
3313 break;
3315 case OP_AOTCONST:
3316 code = emit_aotconst (cfg, code, dreg, (MonoJumpInfoType)(gsize)ins->inst_i1, ins->inst_p0);
3317 break;
3318 case OP_OBJC_GET_SELECTOR:
3319 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_OBJC_SELECTOR_REF, ins->inst_p0);
3320 /* See arch_emit_objc_selector_ref () in aot-compiler.c */
3321 arm_ldrx_lit (code, ins->dreg, 0);
3322 arm_nop (code);
3323 arm_nop (code);
3324 break;
3325 case OP_SEQ_POINT: {
3326 MonoInst *info_var = cfg->arch.seq_point_info_var;
3329 * For AOT, we use one got slot per method, which will point to a
3330 * SeqPointInfo structure, containing all the information required
3331 * by the code below.
3333 if (cfg->compile_aot) {
3334 g_assert (info_var);
3335 g_assert (info_var->opcode == OP_REGOFFSET);
3338 if (ins->flags & MONO_INST_SINGLE_STEP_LOC) {
3339 MonoInst *var = cfg->arch.ss_tramp_var;
3341 g_assert (var);
3342 g_assert (var->opcode == OP_REGOFFSET);
3343 /* Load ss_tramp_var */
3344 /* This is equal to &ss_trampoline */
3345 arm_ldrx (code, ARMREG_IP1, var->inst_basereg, var->inst_offset);
3346 /* Load the trampoline address */
3347 arm_ldrx (code, ARMREG_IP1, ARMREG_IP1, 0);
3348 /* Call it if it is non-null */
3349 arm_cbzx (code, ARMREG_IP1, code + 8);
3350 arm_blrx (code, ARMREG_IP1);
3353 mono_add_seq_point (cfg, bb, ins, code - cfg->native_code);
3355 if (cfg->compile_aot) {
3356 const guint32 offset = code - cfg->native_code;
3357 guint32 val;
3359 arm_ldrx (code, ARMREG_IP1, info_var->inst_basereg, info_var->inst_offset);
3360 /* Add the offset */
3361 val = ((offset / 4) * sizeof (guint8*)) + MONO_STRUCT_OFFSET (SeqPointInfo, bp_addrs);
3362 /* Load the info->bp_addrs [offset], which is either 0 or the address of the bp trampoline */
3363 code = emit_ldrx (code, ARMREG_IP1, ARMREG_IP1, val);
3364 /* Skip the load if its 0 */
3365 arm_cbzx (code, ARMREG_IP1, code + 8);
3366 /* Call the breakpoint trampoline */
3367 arm_blrx (code, ARMREG_IP1);
3368 } else {
3369 MonoInst *var = cfg->arch.bp_tramp_var;
3371 g_assert (var);
3372 g_assert (var->opcode == OP_REGOFFSET);
3373 /* Load the address of the bp trampoline into IP0 */
3374 arm_ldrx (code, ARMREG_IP0, var->inst_basereg, var->inst_offset);
3376 * A placeholder for a possible breakpoint inserted by
3377 * mono_arch_set_breakpoint ().
3379 arm_nop (code);
3381 break;
3384 /* BRANCH */
3385 case OP_BR:
3386 mono_add_patch_info_rel (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_target_bb, MONO_R_ARM64_B);
3387 arm_b (code, code);
3388 break;
3389 case OP_BR_REG:
3390 arm_brx (code, sreg1);
3391 break;
3392 case OP_IBEQ:
3393 case OP_IBGE:
3394 case OP_IBGT:
3395 case OP_IBLE:
3396 case OP_IBLT:
3397 case OP_IBNE_UN:
3398 case OP_IBGE_UN:
3399 case OP_IBGT_UN:
3400 case OP_IBLE_UN:
3401 case OP_IBLT_UN:
3402 case OP_LBEQ:
3403 case OP_LBGE:
3404 case OP_LBGT:
3405 case OP_LBLE:
3406 case OP_LBLT:
3407 case OP_LBNE_UN:
3408 case OP_LBGE_UN:
3409 case OP_LBGT_UN:
3410 case OP_LBLE_UN:
3411 case OP_LBLT_UN:
3412 case OP_FBEQ:
3413 case OP_FBNE_UN:
3414 case OP_FBLT:
3415 case OP_FBGT:
3416 case OP_FBGT_UN:
3417 case OP_FBLE:
3418 case OP_FBGE:
3419 case OP_FBGE_UN: {
3420 int cond;
3422 mono_add_patch_info_rel (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_true_bb, MONO_R_ARM64_BCC);
3423 cond = opcode_to_armcond (ins->opcode);
3424 arm_bcc (code, cond, 0);
3425 break;
3427 case OP_FBLT_UN:
3428 mono_add_patch_info_rel (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_true_bb, MONO_R_ARM64_BCC);
3429 /* For fp compares, ARMCOND_LT is lt or unordered */
3430 arm_bcc (code, ARMCOND_LT, 0);
3431 break;
3432 case OP_FBLE_UN:
3433 mono_add_patch_info_rel (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_true_bb, MONO_R_ARM64_BCC);
3434 arm_bcc (code, ARMCOND_EQ, 0);
3435 mono_add_patch_info_rel (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_true_bb, MONO_R_ARM64_BCC);
3436 /* For fp compares, ARMCOND_LT is lt or unordered */
3437 arm_bcc (code, ARMCOND_LT, 0);
3438 break;
3439 case OP_ARM64_CBZW:
3440 mono_add_patch_info_rel (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_true_bb, MONO_R_ARM64_CBZ);
3441 arm_cbzw (code, sreg1, 0);
3442 break;
3443 case OP_ARM64_CBZX:
3444 mono_add_patch_info_rel (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_true_bb, MONO_R_ARM64_CBZ);
3445 arm_cbzx (code, sreg1, 0);
3446 break;
3447 case OP_ARM64_CBNZW:
3448 mono_add_patch_info_rel (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_true_bb, MONO_R_ARM64_CBZ);
3449 arm_cbnzw (code, sreg1, 0);
3450 break;
3451 case OP_ARM64_CBNZX:
3452 mono_add_patch_info_rel (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_true_bb, MONO_R_ARM64_CBZ);
3453 arm_cbnzx (code, sreg1, 0);
3454 break;
3455 /* ALU */
3456 case OP_IADD:
3457 arm_addw (code, dreg, sreg1, sreg2);
3458 break;
3459 case OP_LADD:
3460 arm_addx (code, dreg, sreg1, sreg2);
3461 break;
3462 case OP_ISUB:
3463 arm_subw (code, dreg, sreg1, sreg2);
3464 break;
3465 case OP_LSUB:
3466 arm_subx (code, dreg, sreg1, sreg2);
3467 break;
3468 case OP_IAND:
3469 arm_andw (code, dreg, sreg1, sreg2);
3470 break;
3471 case OP_LAND:
3472 arm_andx (code, dreg, sreg1, sreg2);
3473 break;
3474 case OP_IOR:
3475 arm_orrw (code, dreg, sreg1, sreg2);
3476 break;
3477 case OP_LOR:
3478 arm_orrx (code, dreg, sreg1, sreg2);
3479 break;
3480 case OP_IXOR:
3481 arm_eorw (code, dreg, sreg1, sreg2);
3482 break;
3483 case OP_LXOR:
3484 arm_eorx (code, dreg, sreg1, sreg2);
3485 break;
3486 case OP_INEG:
3487 arm_negw (code, dreg, sreg1);
3488 break;
3489 case OP_LNEG:
3490 arm_negx (code, dreg, sreg1);
3491 break;
3492 case OP_INOT:
3493 arm_mvnw (code, dreg, sreg1);
3494 break;
3495 case OP_LNOT:
3496 arm_mvnx (code, dreg, sreg1);
3497 break;
3498 case OP_IADDCC:
3499 arm_addsw (code, dreg, sreg1, sreg2);
3500 break;
3501 case OP_ADDCC:
3502 case OP_LADDCC:
3503 arm_addsx (code, dreg, sreg1, sreg2);
3504 break;
3505 case OP_ISUBCC:
3506 arm_subsw (code, dreg, sreg1, sreg2);
3507 break;
3508 case OP_LSUBCC:
3509 case OP_SUBCC:
3510 arm_subsx (code, dreg, sreg1, sreg2);
3511 break;
3512 case OP_ICOMPARE:
3513 arm_cmpw (code, sreg1, sreg2);
3514 break;
3515 case OP_COMPARE:
3516 case OP_LCOMPARE:
3517 arm_cmpx (code, sreg1, sreg2);
3518 break;
3519 case OP_IADD_IMM:
3520 code = emit_addw_imm (code, dreg, sreg1, imm);
3521 break;
3522 case OP_LADD_IMM:
3523 case OP_ADD_IMM:
3524 code = emit_addx_imm (code, dreg, sreg1, imm);
3525 break;
3526 case OP_ISUB_IMM:
3527 code = emit_subw_imm (code, dreg, sreg1, imm);
3528 break;
3529 case OP_LSUB_IMM:
3530 code = emit_subx_imm (code, dreg, sreg1, imm);
3531 break;
3532 case OP_IAND_IMM:
3533 code = emit_andw_imm (code, dreg, sreg1, imm);
3534 break;
3535 case OP_LAND_IMM:
3536 case OP_AND_IMM:
3537 code = emit_andx_imm (code, dreg, sreg1, imm);
3538 break;
3539 case OP_IOR_IMM:
3540 code = emit_orrw_imm (code, dreg, sreg1, imm);
3541 break;
3542 case OP_LOR_IMM:
3543 code = emit_orrx_imm (code, dreg, sreg1, imm);
3544 break;
3545 case OP_IXOR_IMM:
3546 code = emit_eorw_imm (code, dreg, sreg1, imm);
3547 break;
3548 case OP_LXOR_IMM:
3549 code = emit_eorx_imm (code, dreg, sreg1, imm);
3550 break;
3551 case OP_ICOMPARE_IMM:
3552 code = emit_cmpw_imm (code, sreg1, imm);
3553 break;
3554 case OP_LCOMPARE_IMM:
3555 case OP_COMPARE_IMM:
3556 if (imm == 0) {
3557 arm_cmpx (code, sreg1, ARMREG_RZR);
3558 } else {
3559 // FIXME: 32 vs 64 bit issues for 0xffffffff
3560 code = emit_imm64 (code, ARMREG_LR, imm);
3561 arm_cmpx (code, sreg1, ARMREG_LR);
3563 break;
3564 case OP_ISHL:
3565 arm_lslvw (code, dreg, sreg1, sreg2);
3566 break;
3567 case OP_LSHL:
3568 arm_lslvx (code, dreg, sreg1, sreg2);
3569 break;
3570 case OP_ISHR:
3571 arm_asrvw (code, dreg, sreg1, sreg2);
3572 break;
3573 case OP_LSHR:
3574 arm_asrvx (code, dreg, sreg1, sreg2);
3575 break;
3576 case OP_ISHR_UN:
3577 arm_lsrvw (code, dreg, sreg1, sreg2);
3578 break;
3579 case OP_LSHR_UN:
3580 arm_lsrvx (code, dreg, sreg1, sreg2);
3581 break;
3582 case OP_ISHL_IMM:
3583 if (imm == 0)
3584 arm_movx (code, dreg, sreg1);
3585 else
3586 arm_lslw (code, dreg, sreg1, imm);
3587 break;
3588 case OP_SHL_IMM:
3589 case OP_LSHL_IMM:
3590 if (imm == 0)
3591 arm_movx (code, dreg, sreg1);
3592 else
3593 arm_lslx (code, dreg, sreg1, imm);
3594 break;
3595 case OP_ISHR_IMM:
3596 if (imm == 0)
3597 arm_movx (code, dreg, sreg1);
3598 else
3599 arm_asrw (code, dreg, sreg1, imm);
3600 break;
3601 case OP_LSHR_IMM:
3602 case OP_SHR_IMM:
3603 if (imm == 0)
3604 arm_movx (code, dreg, sreg1);
3605 else
3606 arm_asrx (code, dreg, sreg1, imm);
3607 break;
3608 case OP_ISHR_UN_IMM:
3609 if (imm == 0)
3610 arm_movx (code, dreg, sreg1);
3611 else
3612 arm_lsrw (code, dreg, sreg1, imm);
3613 break;
3614 case OP_SHR_UN_IMM:
3615 case OP_LSHR_UN_IMM:
3616 if (imm == 0)
3617 arm_movx (code, dreg, sreg1);
3618 else
3619 arm_lsrx (code, dreg, sreg1, imm);
3620 break;
3622 /* 64BIT ALU */
3623 case OP_SEXT_I4:
3624 arm_sxtwx (code, dreg, sreg1);
3625 break;
3626 case OP_ZEXT_I4:
3627 /* Clean out the upper word */
3628 arm_movw (code, dreg, sreg1);
3629 break;
3631 /* MULTIPLY/DIVISION */
3632 case OP_IDIV:
3633 case OP_IREM:
3634 // FIXME: Optimize this
3635 /* Check for zero */
3636 arm_cmpx_imm (code, sreg2, 0);
3637 code = emit_cond_exc (cfg, code, OP_COND_EXC_IEQ, "DivideByZeroException");
3638 /* Check for INT_MIN/-1 */
3639 code = emit_imm (code, ARMREG_IP0, 0x80000000);
3640 arm_cmpx (code, sreg1, ARMREG_IP0);
3641 arm_cset (code, ARMCOND_EQ, ARMREG_IP1);
3642 code = emit_imm (code, ARMREG_IP0, 0xffffffff);
3643 arm_cmpx (code, sreg2, ARMREG_IP0);
3644 arm_cset (code, ARMCOND_EQ, ARMREG_IP0);
3645 arm_andx (code, ARMREG_IP0, ARMREG_IP0, ARMREG_IP1);
3646 arm_cmpx_imm (code, ARMREG_IP0, 1);
3647 code = emit_cond_exc (cfg, code, OP_COND_EXC_IEQ, "OverflowException");
3648 if (ins->opcode == OP_IREM) {
3649 arm_sdivw (code, ARMREG_LR, sreg1, sreg2);
3650 arm_msubw (code, dreg, ARMREG_LR, sreg2, sreg1);
3651 } else {
3652 arm_sdivw (code, dreg, sreg1, sreg2);
3654 break;
3655 case OP_IDIV_UN:
3656 arm_cmpx_imm (code, sreg2, 0);
3657 code = emit_cond_exc (cfg, code, OP_COND_EXC_IEQ, "DivideByZeroException");
3658 arm_udivw (code, dreg, sreg1, sreg2);
3659 break;
3660 case OP_IREM_UN:
3661 arm_cmpx_imm (code, sreg2, 0);
3662 code = emit_cond_exc (cfg, code, OP_COND_EXC_IEQ, "DivideByZeroException");
3663 arm_udivw (code, ARMREG_LR, sreg1, sreg2);
3664 arm_msubw (code, dreg, ARMREG_LR, sreg2, sreg1);
3665 break;
3666 case OP_LDIV:
3667 case OP_LREM:
3668 // FIXME: Optimize this
3669 /* Check for zero */
3670 arm_cmpx_imm (code, sreg2, 0);
3671 code = emit_cond_exc (cfg, code, OP_COND_EXC_IEQ, "DivideByZeroException");
3672 /* Check for INT64_MIN/-1 */
3673 code = emit_imm64 (code, ARMREG_IP0, 0x8000000000000000);
3674 arm_cmpx (code, sreg1, ARMREG_IP0);
3675 arm_cset (code, ARMCOND_EQ, ARMREG_IP1);
3676 code = emit_imm64 (code, ARMREG_IP0, 0xffffffffffffffff);
3677 arm_cmpx (code, sreg2, ARMREG_IP0);
3678 arm_cset (code, ARMCOND_EQ, ARMREG_IP0);
3679 arm_andx (code, ARMREG_IP0, ARMREG_IP0, ARMREG_IP1);
3680 arm_cmpx_imm (code, ARMREG_IP0, 1);
3681 /* 64 bit uses OverflowException */
3682 code = emit_cond_exc (cfg, code, OP_COND_EXC_IEQ, "OverflowException");
3683 if (ins->opcode == OP_LREM) {
3684 arm_sdivx (code, ARMREG_LR, sreg1, sreg2);
3685 arm_msubx (code, dreg, ARMREG_LR, sreg2, sreg1);
3686 } else {
3687 arm_sdivx (code, dreg, sreg1, sreg2);
3689 break;
3690 case OP_LDIV_UN:
3691 arm_cmpx_imm (code, sreg2, 0);
3692 code = emit_cond_exc (cfg, code, OP_COND_EXC_IEQ, "DivideByZeroException");
3693 arm_udivx (code, dreg, sreg1, sreg2);
3694 break;
3695 case OP_LREM_UN:
3696 arm_cmpx_imm (code, sreg2, 0);
3697 code = emit_cond_exc (cfg, code, OP_COND_EXC_IEQ, "DivideByZeroException");
3698 arm_udivx (code, ARMREG_LR, sreg1, sreg2);
3699 arm_msubx (code, dreg, ARMREG_LR, sreg2, sreg1);
3700 break;
3701 case OP_IMUL:
3702 arm_mulw (code, dreg, sreg1, sreg2);
3703 break;
3704 case OP_LMUL:
3705 arm_mulx (code, dreg, sreg1, sreg2);
3706 break;
3707 case OP_IMUL_IMM:
3708 code = emit_imm (code, ARMREG_LR, imm);
3709 arm_mulw (code, dreg, sreg1, ARMREG_LR);
3710 break;
3711 case OP_MUL_IMM:
3712 case OP_LMUL_IMM:
3713 code = emit_imm (code, ARMREG_LR, imm);
3714 arm_mulx (code, dreg, sreg1, ARMREG_LR);
3715 break;
3717 /* CONVERSIONS */
3718 case OP_ICONV_TO_I1:
3719 case OP_LCONV_TO_I1:
3720 arm_sxtbx (code, dreg, sreg1);
3721 break;
3722 case OP_ICONV_TO_I2:
3723 case OP_LCONV_TO_I2:
3724 arm_sxthx (code, dreg, sreg1);
3725 break;
3726 case OP_ICONV_TO_U1:
3727 case OP_LCONV_TO_U1:
3728 arm_uxtbw (code, dreg, sreg1);
3729 break;
3730 case OP_ICONV_TO_U2:
3731 case OP_LCONV_TO_U2:
3732 arm_uxthw (code, dreg, sreg1);
3733 break;
3735 /* CSET */
3736 case OP_CEQ:
3737 case OP_ICEQ:
3738 case OP_LCEQ:
3739 case OP_CLT:
3740 case OP_ICLT:
3741 case OP_LCLT:
3742 case OP_CGT:
3743 case OP_ICGT:
3744 case OP_LCGT:
3745 case OP_CLT_UN:
3746 case OP_ICLT_UN:
3747 case OP_LCLT_UN:
3748 case OP_CGT_UN:
3749 case OP_ICGT_UN:
3750 case OP_LCGT_UN:
3751 case OP_ICNEQ:
3752 case OP_ICGE:
3753 case OP_ICLE:
3754 case OP_ICGE_UN:
3755 case OP_ICLE_UN: {
3756 int cond;
3758 cond = opcode_to_armcond (ins->opcode);
3759 arm_cset (code, cond, dreg);
3760 break;
3762 case OP_FCEQ:
3763 case OP_FCLT:
3764 case OP_FCLT_UN:
3765 case OP_FCGT:
3766 case OP_FCGT_UN:
3767 case OP_FCNEQ:
3768 case OP_FCLE:
3769 case OP_FCGE: {
3770 int cond;
3772 cond = opcode_to_armcond (ins->opcode);
3773 arm_fcmpd (code, sreg1, sreg2);
3774 arm_cset (code, cond, dreg);
3775 break;
3778 /* MEMORY */
3779 case OP_LOADI1_MEMBASE:
3780 code = emit_ldrsbx (code, dreg, ins->inst_basereg, ins->inst_offset);
3781 break;
3782 case OP_LOADU1_MEMBASE:
3783 code = emit_ldrb (code, dreg, ins->inst_basereg, ins->inst_offset);
3784 break;
3785 case OP_LOADI2_MEMBASE:
3786 code = emit_ldrshx (code, dreg, ins->inst_basereg, ins->inst_offset);
3787 break;
3788 case OP_LOADU2_MEMBASE:
3789 code = emit_ldrh (code, dreg, ins->inst_basereg, ins->inst_offset);
3790 break;
3791 case OP_LOADI4_MEMBASE:
3792 code = emit_ldrswx (code, dreg, ins->inst_basereg, ins->inst_offset);
3793 break;
3794 case OP_LOADU4_MEMBASE:
3795 code = emit_ldrw (code, dreg, ins->inst_basereg, ins->inst_offset);
3796 break;
3797 case OP_LOAD_MEMBASE:
3798 case OP_LOADI8_MEMBASE:
3799 code = emit_ldrx (code, dreg, ins->inst_basereg, ins->inst_offset);
3800 break;
3801 case OP_STOREI1_MEMBASE_IMM:
3802 case OP_STOREI2_MEMBASE_IMM:
3803 case OP_STOREI4_MEMBASE_IMM:
3804 case OP_STORE_MEMBASE_IMM:
3805 case OP_STOREI8_MEMBASE_IMM: {
3806 int immreg;
3808 if (imm != 0) {
3809 code = emit_imm (code, ARMREG_LR, imm);
3810 immreg = ARMREG_LR;
3811 } else {
3812 immreg = ARMREG_RZR;
3815 switch (ins->opcode) {
3816 case OP_STOREI1_MEMBASE_IMM:
3817 code = emit_strb (code, immreg, ins->inst_destbasereg, ins->inst_offset);
3818 break;
3819 case OP_STOREI2_MEMBASE_IMM:
3820 code = emit_strh (code, immreg, ins->inst_destbasereg, ins->inst_offset);
3821 break;
3822 case OP_STOREI4_MEMBASE_IMM:
3823 code = emit_strw (code, immreg, ins->inst_destbasereg, ins->inst_offset);
3824 break;
3825 case OP_STORE_MEMBASE_IMM:
3826 case OP_STOREI8_MEMBASE_IMM:
3827 code = emit_strx (code, immreg, ins->inst_destbasereg, ins->inst_offset);
3828 break;
3829 default:
3830 g_assert_not_reached ();
3831 break;
3833 break;
3835 case OP_STOREI1_MEMBASE_REG:
3836 code = emit_strb (code, sreg1, ins->inst_destbasereg, ins->inst_offset);
3837 break;
3838 case OP_STOREI2_MEMBASE_REG:
3839 code = emit_strh (code, sreg1, ins->inst_destbasereg, ins->inst_offset);
3840 break;
3841 case OP_STOREI4_MEMBASE_REG:
3842 code = emit_strw (code, sreg1, ins->inst_destbasereg, ins->inst_offset);
3843 break;
3844 case OP_STORE_MEMBASE_REG:
3845 case OP_STOREI8_MEMBASE_REG:
3846 code = emit_strx (code, sreg1, ins->inst_destbasereg, ins->inst_offset);
3847 break;
3848 case OP_TLS_GET:
3849 code = emit_tls_get (code, dreg, ins->inst_offset);
3850 break;
3851 case OP_TLS_SET:
3852 code = emit_tls_set (code, sreg1, ins->inst_offset);
3853 break;
3854 /* Atomic */
3855 case OP_MEMORY_BARRIER:
3856 arm_dmb (code, ARM_DMB_ISH);
3857 break;
3858 case OP_ATOMIC_ADD_I4: {
3859 guint8 *buf [16];
3861 buf [0] = code;
3862 arm_ldxrw (code, ARMREG_IP0, sreg1);
3863 arm_addx (code, ARMREG_IP0, ARMREG_IP0, sreg2);
3864 arm_stlxrw (code, ARMREG_IP1, ARMREG_IP0, sreg1);
3865 arm_cbnzw (code, ARMREG_IP1, buf [0]);
3867 arm_dmb (code, ARM_DMB_ISH);
3868 arm_movx (code, dreg, ARMREG_IP0);
3869 break;
3871 case OP_ATOMIC_ADD_I8: {
3872 guint8 *buf [16];
3874 buf [0] = code;
3875 arm_ldxrx (code, ARMREG_IP0, sreg1);
3876 arm_addx (code, ARMREG_IP0, ARMREG_IP0, sreg2);
3877 arm_stlxrx (code, ARMREG_IP1, ARMREG_IP0, sreg1);
3878 arm_cbnzx (code, ARMREG_IP1, buf [0]);
3880 arm_dmb (code, ARM_DMB_ISH);
3881 arm_movx (code, dreg, ARMREG_IP0);
3882 break;
3884 case OP_ATOMIC_EXCHANGE_I4: {
3885 guint8 *buf [16];
3887 buf [0] = code;
3888 arm_ldxrw (code, ARMREG_IP0, sreg1);
3889 arm_stlxrw (code, ARMREG_IP1, sreg2, sreg1);
3890 arm_cbnzw (code, ARMREG_IP1, buf [0]);
3892 arm_dmb (code, ARM_DMB_ISH);
3893 arm_movx (code, dreg, ARMREG_IP0);
3894 break;
3896 case OP_ATOMIC_EXCHANGE_I8: {
3897 guint8 *buf [16];
3899 buf [0] = code;
3900 arm_ldxrx (code, ARMREG_IP0, sreg1);
3901 arm_stlxrx (code, ARMREG_IP1, sreg2, sreg1);
3902 arm_cbnzw (code, ARMREG_IP1, buf [0]);
3904 arm_dmb (code, ARM_DMB_ISH);
3905 arm_movx (code, dreg, ARMREG_IP0);
3906 break;
3908 case OP_ATOMIC_CAS_I4: {
3909 guint8 *buf [16];
3911 /* sreg2 is the value, sreg3 is the comparand */
3912 buf [0] = code;
3913 arm_ldxrw (code, ARMREG_IP0, sreg1);
3914 arm_cmpw (code, ARMREG_IP0, ins->sreg3);
3915 buf [1] = code;
3916 arm_bcc (code, ARMCOND_NE, 0);
3917 arm_stlxrw (code, ARMREG_IP1, sreg2, sreg1);
3918 arm_cbnzw (code, ARMREG_IP1, buf [0]);
3919 arm_patch_rel (buf [1], code, MONO_R_ARM64_BCC);
3921 arm_dmb (code, ARM_DMB_ISH);
3922 arm_movx (code, dreg, ARMREG_IP0);
3923 break;
3925 case OP_ATOMIC_CAS_I8: {
3926 guint8 *buf [16];
3928 buf [0] = code;
3929 arm_ldxrx (code, ARMREG_IP0, sreg1);
3930 arm_cmpx (code, ARMREG_IP0, ins->sreg3);
3931 buf [1] = code;
3932 arm_bcc (code, ARMCOND_NE, 0);
3933 arm_stlxrx (code, ARMREG_IP1, sreg2, sreg1);
3934 arm_cbnzw (code, ARMREG_IP1, buf [0]);
3935 arm_patch_rel (buf [1], code, MONO_R_ARM64_BCC);
3937 arm_dmb (code, ARM_DMB_ISH);
3938 arm_movx (code, dreg, ARMREG_IP0);
3939 break;
3941 case OP_ATOMIC_LOAD_I1: {
3942 code = emit_addx_imm (code, ARMREG_LR, ins->inst_basereg, ins->inst_offset);
3943 if (ins->backend.memory_barrier_kind == MONO_MEMORY_BARRIER_SEQ)
3944 arm_dmb (code, ARM_DMB_ISH);
3945 arm_ldarb (code, ins->dreg, ARMREG_LR);
3946 arm_sxtbx (code, ins->dreg, ins->dreg);
3947 break;
3949 case OP_ATOMIC_LOAD_U1: {
3950 code = emit_addx_imm (code, ARMREG_LR, ins->inst_basereg, ins->inst_offset);
3951 if (ins->backend.memory_barrier_kind == MONO_MEMORY_BARRIER_SEQ)
3952 arm_dmb (code, ARM_DMB_ISH);
3953 arm_ldarb (code, ins->dreg, ARMREG_LR);
3954 arm_uxtbx (code, ins->dreg, ins->dreg);
3955 break;
3957 case OP_ATOMIC_LOAD_I2: {
3958 code = emit_addx_imm (code, ARMREG_LR, ins->inst_basereg, ins->inst_offset);
3959 if (ins->backend.memory_barrier_kind == MONO_MEMORY_BARRIER_SEQ)
3960 arm_dmb (code, ARM_DMB_ISH);
3961 arm_ldarh (code, ins->dreg, ARMREG_LR);
3962 arm_sxthx (code, ins->dreg, ins->dreg);
3963 break;
3965 case OP_ATOMIC_LOAD_U2: {
3966 code = emit_addx_imm (code, ARMREG_LR, ins->inst_basereg, ins->inst_offset);
3967 if (ins->backend.memory_barrier_kind == MONO_MEMORY_BARRIER_SEQ)
3968 arm_dmb (code, ARM_DMB_ISH);
3969 arm_ldarh (code, ins->dreg, ARMREG_LR);
3970 arm_uxthx (code, ins->dreg, ins->dreg);
3971 break;
3973 case OP_ATOMIC_LOAD_I4: {
3974 code = emit_addx_imm (code, ARMREG_LR, ins->inst_basereg, ins->inst_offset);
3975 if (ins->backend.memory_barrier_kind == MONO_MEMORY_BARRIER_SEQ)
3976 arm_dmb (code, ARM_DMB_ISH);
3977 arm_ldarw (code, ins->dreg, ARMREG_LR);
3978 arm_sxtwx (code, ins->dreg, ins->dreg);
3979 break;
3981 case OP_ATOMIC_LOAD_U4: {
3982 code = emit_addx_imm (code, ARMREG_LR, ins->inst_basereg, ins->inst_offset);
3983 if (ins->backend.memory_barrier_kind == MONO_MEMORY_BARRIER_SEQ)
3984 arm_dmb (code, ARM_DMB_ISH);
3985 arm_ldarw (code, ins->dreg, ARMREG_LR);
3986 arm_movw (code, ins->dreg, ins->dreg); /* Clear upper half of the register. */
3987 break;
3989 case OP_ATOMIC_LOAD_I8:
3990 case OP_ATOMIC_LOAD_U8: {
3991 code = emit_addx_imm (code, ARMREG_LR, ins->inst_basereg, ins->inst_offset);
3992 if (ins->backend.memory_barrier_kind == MONO_MEMORY_BARRIER_SEQ)
3993 arm_dmb (code, ARM_DMB_ISH);
3994 arm_ldarx (code, ins->dreg, ARMREG_LR);
3995 break;
3997 case OP_ATOMIC_LOAD_R4: {
3998 code = emit_addx_imm (code, ARMREG_LR, ins->inst_basereg, ins->inst_offset);
3999 if (ins->backend.memory_barrier_kind == MONO_MEMORY_BARRIER_SEQ)
4000 arm_dmb (code, ARM_DMB_ISH);
4001 if (cfg->r4fp) {
4002 arm_ldarw (code, ARMREG_LR, ARMREG_LR);
4003 arm_fmov_rx_to_double (code, ins->dreg, ARMREG_LR);
4004 } else {
4005 arm_ldarw (code, ARMREG_LR, ARMREG_LR);
4006 arm_fmov_rx_to_double (code, FP_TEMP_REG, ARMREG_LR);
4007 arm_fcvt_sd (code, ins->dreg, FP_TEMP_REG);
4009 break;
4011 case OP_ATOMIC_LOAD_R8: {
4012 code = emit_addx_imm (code, ARMREG_LR, ins->inst_basereg, ins->inst_offset);
4013 if (ins->backend.memory_barrier_kind == MONO_MEMORY_BARRIER_SEQ)
4014 arm_dmb (code, ARM_DMB_ISH);
4015 arm_ldarx (code, ARMREG_LR, ARMREG_LR);
4016 arm_fmov_rx_to_double (code, ins->dreg, ARMREG_LR);
4017 break;
4019 case OP_ATOMIC_STORE_I1:
4020 case OP_ATOMIC_STORE_U1: {
4021 code = emit_addx_imm (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
4022 arm_stlrb (code, ARMREG_LR, ins->sreg1);
4023 if (ins->backend.memory_barrier_kind == MONO_MEMORY_BARRIER_SEQ)
4024 arm_dmb (code, ARM_DMB_ISH);
4025 break;
4027 case OP_ATOMIC_STORE_I2:
4028 case OP_ATOMIC_STORE_U2: {
4029 code = emit_addx_imm (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
4030 arm_stlrh (code, ARMREG_LR, ins->sreg1);
4031 if (ins->backend.memory_barrier_kind == MONO_MEMORY_BARRIER_SEQ)
4032 arm_dmb (code, ARM_DMB_ISH);
4033 break;
4035 case OP_ATOMIC_STORE_I4:
4036 case OP_ATOMIC_STORE_U4: {
4037 code = emit_addx_imm (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
4038 arm_stlrw (code, ARMREG_LR, ins->sreg1);
4039 if (ins->backend.memory_barrier_kind == MONO_MEMORY_BARRIER_SEQ)
4040 arm_dmb (code, ARM_DMB_ISH);
4041 break;
4043 case OP_ATOMIC_STORE_I8:
4044 case OP_ATOMIC_STORE_U8: {
4045 code = emit_addx_imm (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
4046 arm_stlrx (code, ARMREG_LR, ins->sreg1);
4047 if (ins->backend.memory_barrier_kind == MONO_MEMORY_BARRIER_SEQ)
4048 arm_dmb (code, ARM_DMB_ISH);
4049 break;
4051 case OP_ATOMIC_STORE_R4: {
4052 code = emit_addx_imm (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
4053 if (cfg->r4fp) {
4054 arm_fmov_double_to_rx (code, ARMREG_IP0, ins->sreg1);
4055 arm_stlrw (code, ARMREG_LR, ARMREG_IP0);
4056 } else {
4057 arm_fcvt_ds (code, FP_TEMP_REG, ins->sreg1);
4058 arm_fmov_double_to_rx (code, ARMREG_IP0, FP_TEMP_REG);
4059 arm_stlrw (code, ARMREG_LR, ARMREG_IP0);
4061 if (ins->backend.memory_barrier_kind == MONO_MEMORY_BARRIER_SEQ)
4062 arm_dmb (code, ARM_DMB_ISH);
4063 break;
4065 case OP_ATOMIC_STORE_R8: {
4066 code = emit_addx_imm (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
4067 arm_fmov_double_to_rx (code, ARMREG_IP0, ins->sreg1);
4068 arm_stlrx (code, ARMREG_LR, ARMREG_IP0);
4069 if (ins->backend.memory_barrier_kind == MONO_MEMORY_BARRIER_SEQ)
4070 arm_dmb (code, ARM_DMB_ISH);
4071 break;
4074 /* FP */
4075 case OP_R8CONST: {
4076 guint64 imm = *(guint64*)ins->inst_p0;
4078 if (imm == 0) {
4079 arm_fmov_rx_to_double (code, dreg, ARMREG_RZR);
4080 } else {
4081 code = emit_imm64 (code, ARMREG_LR, imm);
4082 arm_fmov_rx_to_double (code, ins->dreg, ARMREG_LR);
4084 break;
4086 case OP_R4CONST: {
4087 guint64 imm = *(guint32*)ins->inst_p0;
4089 code = emit_imm64 (code, ARMREG_LR, imm);
4090 if (cfg->r4fp) {
4091 arm_fmov_rx_to_double (code, dreg, ARMREG_LR);
4092 } else {
4093 arm_fmov_rx_to_double (code, FP_TEMP_REG, ARMREG_LR);
4094 arm_fcvt_sd (code, dreg, FP_TEMP_REG);
4096 break;
4098 case OP_LOADR8_MEMBASE:
4099 code = emit_ldrfpx (code, dreg, ins->inst_basereg, ins->inst_offset);
4100 break;
4101 case OP_LOADR4_MEMBASE:
4102 if (cfg->r4fp) {
4103 code = emit_ldrfpw (code, dreg, ins->inst_basereg, ins->inst_offset);
4104 } else {
4105 code = emit_ldrfpw (code, FP_TEMP_REG, ins->inst_basereg, ins->inst_offset);
4106 arm_fcvt_sd (code, dreg, FP_TEMP_REG);
4108 break;
4109 case OP_STORER8_MEMBASE_REG:
4110 code = emit_strfpx (code, sreg1, ins->inst_destbasereg, ins->inst_offset);
4111 break;
4112 case OP_STORER4_MEMBASE_REG:
4113 if (cfg->r4fp) {
4114 code = emit_strfpw (code, sreg1, ins->inst_destbasereg, ins->inst_offset);
4115 } else {
4116 arm_fcvt_ds (code, FP_TEMP_REG, sreg1);
4117 code = emit_strfpw (code, FP_TEMP_REG, ins->inst_destbasereg, ins->inst_offset);
4119 break;
4120 case OP_FMOVE:
4121 if (dreg != sreg1)
4122 arm_fmovd (code, dreg, sreg1);
4123 break;
4124 case OP_RMOVE:
4125 if (dreg != sreg1)
4126 arm_fmovs (code, dreg, sreg1);
4127 break;
4128 case OP_MOVE_F_TO_I4:
4129 if (cfg->r4fp) {
4130 arm_fmov_double_to_rx (code, ins->dreg, ins->sreg1);
4131 } else {
4132 arm_fcvt_ds (code, ins->dreg, ins->sreg1);
4133 arm_fmov_double_to_rx (code, ins->dreg, ins->dreg);
4135 break;
4136 case OP_MOVE_I4_TO_F:
4137 if (cfg->r4fp) {
4138 arm_fmov_rx_to_double (code, ins->dreg, ins->sreg1);
4139 } else {
4140 arm_fmov_rx_to_double (code, ins->dreg, ins->sreg1);
4141 arm_fcvt_sd (code, ins->dreg, ins->dreg);
4143 break;
4144 case OP_MOVE_F_TO_I8:
4145 arm_fmov_double_to_rx (code, ins->dreg, ins->sreg1);
4146 break;
4147 case OP_MOVE_I8_TO_F:
4148 arm_fmov_rx_to_double (code, ins->dreg, ins->sreg1);
4149 break;
4150 case OP_FCOMPARE:
4151 arm_fcmpd (code, sreg1, sreg2);
4152 break;
4153 case OP_RCOMPARE:
4154 arm_fcmps (code, sreg1, sreg2);
4155 break;
4156 case OP_FCONV_TO_I1:
4157 arm_fcvtzs_dx (code, dreg, sreg1);
4158 arm_sxtbx (code, dreg, dreg);
4159 break;
4160 case OP_FCONV_TO_U1:
4161 arm_fcvtzu_dx (code, dreg, sreg1);
4162 arm_uxtbw (code, dreg, dreg);
4163 break;
4164 case OP_FCONV_TO_I2:
4165 arm_fcvtzs_dx (code, dreg, sreg1);
4166 arm_sxthx (code, dreg, dreg);
4167 break;
4168 case OP_FCONV_TO_U2:
4169 arm_fcvtzu_dx (code, dreg, sreg1);
4170 arm_uxthw (code, dreg, dreg);
4171 break;
4172 case OP_FCONV_TO_I4:
4173 arm_fcvtzs_dx (code, dreg, sreg1);
4174 arm_sxtwx (code, dreg, dreg);
4175 break;
4176 case OP_FCONV_TO_U4:
4177 arm_fcvtzu_dx (code, dreg, sreg1);
4178 break;
4179 case OP_FCONV_TO_I8:
4180 arm_fcvtzs_dx (code, dreg, sreg1);
4181 break;
4182 case OP_FCONV_TO_U8:
4183 arm_fcvtzu_dx (code, dreg, sreg1);
4184 break;
4185 case OP_FCONV_TO_R4:
4186 if (cfg->r4fp) {
4187 arm_fcvt_ds (code, dreg, sreg1);
4188 } else {
4189 arm_fcvt_ds (code, FP_TEMP_REG, sreg1);
4190 arm_fcvt_sd (code, dreg, FP_TEMP_REG);
4192 break;
4193 case OP_ICONV_TO_R4:
4194 if (cfg->r4fp) {
4195 arm_scvtf_rw_to_s (code, dreg, sreg1);
4196 } else {
4197 arm_scvtf_rw_to_s (code, FP_TEMP_REG, sreg1);
4198 arm_fcvt_sd (code, dreg, FP_TEMP_REG);
4200 break;
4201 case OP_LCONV_TO_R4:
4202 if (cfg->r4fp) {
4203 arm_scvtf_rx_to_s (code, dreg, sreg1);
4204 } else {
4205 arm_scvtf_rx_to_s (code, FP_TEMP_REG, sreg1);
4206 arm_fcvt_sd (code, dreg, FP_TEMP_REG);
4208 break;
4209 case OP_ICONV_TO_R8:
4210 arm_scvtf_rw_to_d (code, dreg, sreg1);
4211 break;
4212 case OP_LCONV_TO_R8:
4213 arm_scvtf_rx_to_d (code, dreg, sreg1);
4214 break;
4215 case OP_ICONV_TO_R_UN:
4216 arm_ucvtf_rw_to_d (code, dreg, sreg1);
4217 break;
4218 case OP_LCONV_TO_R_UN:
4219 arm_ucvtf_rx_to_d (code, dreg, sreg1);
4220 break;
4221 case OP_FADD:
4222 arm_fadd_d (code, dreg, sreg1, sreg2);
4223 break;
4224 case OP_FSUB:
4225 arm_fsub_d (code, dreg, sreg1, sreg2);
4226 break;
4227 case OP_FMUL:
4228 arm_fmul_d (code, dreg, sreg1, sreg2);
4229 break;
4230 case OP_FDIV:
4231 arm_fdiv_d (code, dreg, sreg1, sreg2);
4232 break;
4233 case OP_FREM:
4234 /* Emulated */
4235 g_assert_not_reached ();
4236 break;
4237 case OP_FNEG:
4238 arm_fneg_d (code, dreg, sreg1);
4239 break;
4240 case OP_ARM_SETFREG_R4:
4241 arm_fcvt_ds (code, dreg, sreg1);
4242 break;
4243 case OP_CKFINITE:
4244 /* Check for infinity */
4245 code = emit_imm64 (code, ARMREG_LR, 0x7fefffffffffffffLL);
4246 arm_fmov_rx_to_double (code, FP_TEMP_REG, ARMREG_LR);
4247 arm_fabs_d (code, FP_TEMP_REG2, sreg1);
4248 arm_fcmpd (code, FP_TEMP_REG2, FP_TEMP_REG);
4249 code = emit_cond_exc (cfg, code, OP_COND_EXC_GT, "ArithmeticException");
4250 /* Check for nans */
4251 arm_fcmpd (code, FP_TEMP_REG2, FP_TEMP_REG2);
4252 code = emit_cond_exc (cfg, code, OP_COND_EXC_OV, "ArithmeticException");
4253 arm_fmovd (code, dreg, sreg1);
4254 break;
4256 /* R4 */
4257 case OP_RADD:
4258 arm_fadd_s (code, dreg, sreg1, sreg2);
4259 break;
4260 case OP_RSUB:
4261 arm_fsub_s (code, dreg, sreg1, sreg2);
4262 break;
4263 case OP_RMUL:
4264 arm_fmul_s (code, dreg, sreg1, sreg2);
4265 break;
4266 case OP_RDIV:
4267 arm_fdiv_s (code, dreg, sreg1, sreg2);
4268 break;
4269 case OP_RNEG:
4270 arm_fneg_s (code, dreg, sreg1);
4271 break;
4272 case OP_RCONV_TO_I1:
4273 arm_fcvtzs_sx (code, dreg, sreg1);
4274 arm_sxtbx (code, dreg, dreg);
4275 break;
4276 case OP_RCONV_TO_U1:
4277 arm_fcvtzu_sx (code, dreg, sreg1);
4278 arm_uxtbw (code, dreg, dreg);
4279 break;
4280 case OP_RCONV_TO_I2:
4281 arm_fcvtzs_sx (code, dreg, sreg1);
4282 arm_sxthx (code, dreg, dreg);
4283 break;
4284 case OP_RCONV_TO_U2:
4285 arm_fcvtzu_sx (code, dreg, sreg1);
4286 arm_uxthw (code, dreg, dreg);
4287 break;
4288 case OP_RCONV_TO_I4:
4289 arm_fcvtzs_sx (code, dreg, sreg1);
4290 arm_sxtwx (code, dreg, dreg);
4291 break;
4292 case OP_RCONV_TO_U4:
4293 arm_fcvtzu_sx (code, dreg, sreg1);
4294 break;
4295 case OP_RCONV_TO_I8:
4296 arm_fcvtzs_sx (code, dreg, sreg1);
4297 break;
4298 case OP_RCONV_TO_U8:
4299 arm_fcvtzu_sx (code, dreg, sreg1);
4300 break;
4301 case OP_RCONV_TO_R8:
4302 arm_fcvt_sd (code, dreg, sreg1);
4303 break;
4304 case OP_RCONV_TO_R4:
4305 if (dreg != sreg1)
4306 arm_fmovs (code, dreg, sreg1);
4307 break;
4308 case OP_RCEQ:
4309 case OP_RCLT:
4310 case OP_RCLT_UN:
4311 case OP_RCGT:
4312 case OP_RCGT_UN:
4313 case OP_RCNEQ:
4314 case OP_RCLE:
4315 case OP_RCGE: {
4316 int cond;
4318 cond = opcode_to_armcond (ins->opcode);
4319 arm_fcmps (code, sreg1, sreg2);
4320 arm_cset (code, cond, dreg);
4321 break;
4324 /* CALLS */
4325 case OP_VOIDCALL:
4326 case OP_CALL:
4327 case OP_LCALL:
4328 case OP_FCALL:
4329 case OP_RCALL:
4330 case OP_VCALL2:
4331 call = (MonoCallInst*)ins;
4332 if (ins->flags & MONO_INST_HAS_METHOD)
4333 code = emit_call (cfg, code, MONO_PATCH_INFO_METHOD, call->method);
4334 else
4335 code = emit_call (cfg, code, MONO_PATCH_INFO_ABS, call->fptr);
4336 code = emit_move_return_value (cfg, code, ins);
4337 break;
4338 case OP_VOIDCALL_REG:
4339 case OP_CALL_REG:
4340 case OP_LCALL_REG:
4341 case OP_FCALL_REG:
4342 case OP_RCALL_REG:
4343 case OP_VCALL2_REG:
4344 arm_blrx (code, sreg1);
4345 code = emit_move_return_value (cfg, code, ins);
4346 break;
4347 case OP_VOIDCALL_MEMBASE:
4348 case OP_CALL_MEMBASE:
4349 case OP_LCALL_MEMBASE:
4350 case OP_FCALL_MEMBASE:
4351 case OP_RCALL_MEMBASE:
4352 case OP_VCALL2_MEMBASE:
4353 code = emit_ldrx (code, ARMREG_IP0, ins->inst_basereg, ins->inst_offset);
4354 arm_blrx (code, ARMREG_IP0);
4355 code = emit_move_return_value (cfg, code, ins);
4356 break;
4358 case OP_TAILCALL_PARAMETER:
4359 // This opcode helps compute sizes, i.e.
4360 // of the subsequent OP_TAILCALL, but contributes no code.
4361 g_assert (ins->next);
4362 break;
4364 case OP_TAILCALL:
4365 case OP_TAILCALL_MEMBASE:
4366 case OP_TAILCALL_REG: {
4367 int branch_reg = ARMREG_IP0;
4368 guint64 free_reg = 1 << ARMREG_IP1;
4369 call = (MonoCallInst*)ins;
4371 g_assert (!cfg->method->save_lmf);
4373 max_len += call->stack_usage / sizeof (target_mgreg_t) * ins_get_size (OP_TAILCALL_PARAMETER);
4374 while (G_UNLIKELY (offset + max_len > cfg->code_size)) {
4375 cfg->code_size *= 2;
4376 cfg->native_code = (unsigned char *)mono_realloc_native_code (cfg);
4377 code = cfg->native_code + offset;
4378 cfg->stat_code_reallocs++;
4381 switch (ins->opcode) {
4382 case OP_TAILCALL:
4383 free_reg = (1 << ARMREG_IP0) | (1 << ARMREG_IP1);
4384 break;
4386 case OP_TAILCALL_REG:
4387 g_assert (sreg1 != -1);
4388 g_assert (sreg1 != ARMREG_IP0);
4389 g_assert (sreg1 != ARMREG_IP1);
4390 g_assert (sreg1 != ARMREG_LR);
4391 g_assert (sreg1 != ARMREG_SP);
4392 g_assert (sreg1 != ARMREG_R28);
4393 if ((sreg1 << 1) & MONO_ARCH_CALLEE_SAVED_REGS) {
4394 arm_movx (code, branch_reg, sreg1);
4395 } else {
4396 free_reg = (1 << ARMREG_IP0) | (1 << ARMREG_IP1);
4397 branch_reg = sreg1;
4399 break;
4401 case OP_TAILCALL_MEMBASE:
4402 g_assert (ins->inst_basereg != -1);
4403 g_assert (ins->inst_basereg != ARMREG_IP0);
4404 g_assert (ins->inst_basereg != ARMREG_IP1);
4405 g_assert (ins->inst_basereg != ARMREG_LR);
4406 g_assert (ins->inst_basereg != ARMREG_SP);
4407 g_assert (ins->inst_basereg != ARMREG_R28);
4408 code = emit_ldrx (code, branch_reg, ins->inst_basereg, ins->inst_offset);
4409 break;
4411 default:
4412 g_assert_not_reached ();
4415 // Copy stack arguments.
4416 // FIXME a fixed size memcpy is desirable here,
4417 // at least for larger values of stack_usage.
4418 for (int i = 0; i < call->stack_usage; i += sizeof (target_mgreg_t)) {
4419 code = emit_ldrx (code, ARMREG_LR, ARMREG_SP, i);
4420 code = emit_strx (code, ARMREG_LR, ARMREG_R28, i);
4423 /* Restore registers */
4424 code = emit_load_regset (code, MONO_ARCH_CALLEE_SAVED_REGS & cfg->used_int_regs, ARMREG_FP, cfg->arch.saved_gregs_offset);
4426 /* Destroy frame */
4427 code = mono_arm_emit_destroy_frame (code, cfg->stack_offset, free_reg);
4429 switch (ins->opcode) {
4430 case OP_TAILCALL:
4431 if (cfg->compile_aot) {
4432 /* This is not a PLT patch */
4433 code = emit_aotconst (cfg, code, branch_reg, MONO_PATCH_INFO_METHOD_JUMP, call->method);
4434 } else {
4435 mono_add_patch_info_rel (cfg, code - cfg->native_code, MONO_PATCH_INFO_METHOD_JUMP, call->method, MONO_R_ARM64_B);
4436 arm_b (code, code);
4437 cfg->thunk_area += THUNK_SIZE;
4438 break;
4440 // fallthrough
4441 case OP_TAILCALL_MEMBASE:
4442 case OP_TAILCALL_REG:
4443 arm_brx (code, branch_reg);
4444 break;
4446 default:
4447 g_assert_not_reached ();
4450 ins->flags |= MONO_INST_GC_CALLSITE;
4451 ins->backend.pc_offset = code - cfg->native_code;
4452 break;
4454 case OP_ARGLIST:
4455 g_assert (cfg->arch.cinfo);
4456 code = emit_addx_imm (code, ARMREG_IP0, cfg->arch.args_reg, cfg->arch.cinfo->sig_cookie.offset);
4457 arm_strx (code, ARMREG_IP0, sreg1, 0);
4458 break;
4459 case OP_DYN_CALL: {
4460 MonoInst *var = cfg->dyn_call_var;
4461 guint8 *labels [16];
4462 int i;
4465 * sreg1 points to a DynCallArgs structure initialized by mono_arch_start_dyn_call ().
4466 * sreg2 is the function to call.
4469 g_assert (var->opcode == OP_REGOFFSET);
4471 arm_movx (code, ARMREG_LR, sreg1);
4472 arm_movx (code, ARMREG_IP1, sreg2);
4474 /* Save args buffer */
4475 code = emit_strx (code, ARMREG_LR, var->inst_basereg, var->inst_offset);
4477 /* Set fp argument regs */
4478 code = emit_ldrw (code, ARMREG_R0, ARMREG_LR, MONO_STRUCT_OFFSET (DynCallArgs, n_fpargs));
4479 arm_cmpw (code, ARMREG_R0, ARMREG_RZR);
4480 labels [0] = code;
4481 arm_bcc (code, ARMCOND_EQ, 0);
4482 for (i = 0; i < 8; ++i)
4483 code = emit_ldrfpx (code, ARMREG_D0 + i, ARMREG_LR, MONO_STRUCT_OFFSET (DynCallArgs, fpregs) + (i * 8));
4484 arm_patch_rel (labels [0], code, MONO_R_ARM64_BCC);
4486 /* Allocate callee area */
4487 code = emit_ldrx (code, ARMREG_R0, ARMREG_LR, MONO_STRUCT_OFFSET (DynCallArgs, n_stackargs));
4488 arm_lslw (code, ARMREG_R0, ARMREG_R0, 3);
4489 arm_movspx (code, ARMREG_R1, ARMREG_SP);
4490 arm_subx (code, ARMREG_R1, ARMREG_R1, ARMREG_R0);
4491 arm_movspx (code, ARMREG_SP, ARMREG_R1);
4493 /* Set stack args */
4494 /* R1 = limit */
4495 code = emit_ldrx (code, ARMREG_R1, ARMREG_LR, MONO_STRUCT_OFFSET (DynCallArgs, n_stackargs));
4496 /* R2 = pointer into 'regs' */
4497 code = emit_imm (code, ARMREG_R2, MONO_STRUCT_OFFSET (DynCallArgs, regs) + ((PARAM_REGS + 1) * sizeof (target_mgreg_t)));
4498 arm_addx (code, ARMREG_R2, ARMREG_LR, ARMREG_R2);
4499 /* R3 = pointer to stack */
4500 arm_movspx (code, ARMREG_R3, ARMREG_SP);
4501 labels [0] = code;
4502 arm_b (code, code);
4503 labels [1] = code;
4504 code = emit_ldrx (code, ARMREG_R5, ARMREG_R2, 0);
4505 code = emit_strx (code, ARMREG_R5, ARMREG_R3, 0);
4506 code = emit_addx_imm (code, ARMREG_R2, ARMREG_R2, sizeof (target_mgreg_t));
4507 code = emit_addx_imm (code, ARMREG_R3, ARMREG_R3, sizeof (target_mgreg_t));
4508 code = emit_subx_imm (code, ARMREG_R1, ARMREG_R1, 1);
4509 arm_patch_rel (labels [0], code, MONO_R_ARM64_B);
4510 arm_cmpw (code, ARMREG_R1, ARMREG_RZR);
4511 arm_bcc (code, ARMCOND_GT, labels [1]);
4513 /* Set argument registers + r8 */
4514 code = mono_arm_emit_load_regarray (code, 0x1ff, ARMREG_LR, MONO_STRUCT_OFFSET (DynCallArgs, regs));
4516 /* Make the call */
4517 arm_blrx (code, ARMREG_IP1);
4519 /* Save result */
4520 code = emit_ldrx (code, ARMREG_LR, var->inst_basereg, var->inst_offset);
4521 arm_strx (code, ARMREG_R0, ARMREG_LR, MONO_STRUCT_OFFSET (DynCallArgs, res));
4522 arm_strx (code, ARMREG_R1, ARMREG_LR, MONO_STRUCT_OFFSET (DynCallArgs, res2));
4523 /* Save fp result */
4524 code = emit_ldrw (code, ARMREG_R0, ARMREG_LR, MONO_STRUCT_OFFSET (DynCallArgs, n_fpret));
4525 arm_cmpw (code, ARMREG_R0, ARMREG_RZR);
4526 labels [1] = code;
4527 arm_bcc (code, ARMCOND_EQ, 0);
4528 for (i = 0; i < 8; ++i)
4529 code = emit_strfpx (code, ARMREG_D0 + i, ARMREG_LR, MONO_STRUCT_OFFSET (DynCallArgs, fpregs) + (i * 8));
4530 arm_patch_rel (labels [1], code, MONO_R_ARM64_BCC);
4531 break;
4534 case OP_GENERIC_CLASS_INIT: {
4535 int byte_offset;
4536 guint8 *jump;
4538 byte_offset = MONO_STRUCT_OFFSET (MonoVTable, initialized);
4540 /* Load vtable->initialized */
4541 arm_ldrsbx (code, ARMREG_IP0, sreg1, byte_offset);
4542 jump = code;
4543 arm_cbnzx (code, ARMREG_IP0, 0);
4545 /* Slowpath */
4546 g_assert (sreg1 == ARMREG_R0);
4547 code = emit_call (cfg, code, MONO_PATCH_INFO_JIT_ICALL,
4548 (gpointer)"mono_generic_class_init");
4550 mono_arm_patch (jump, code, MONO_R_ARM64_CBZ);
4551 break;
4554 case OP_CHECK_THIS:
4555 arm_ldrb (code, ARMREG_LR, sreg1, 0);
4556 break;
4557 case OP_NOT_NULL:
4558 case OP_NOT_REACHED:
4559 case OP_DUMMY_USE:
4560 case OP_DUMMY_ICONST:
4561 case OP_DUMMY_I8CONST:
4562 case OP_DUMMY_R8CONST:
4563 case OP_DUMMY_R4CONST:
4564 break;
4565 case OP_IL_SEQ_POINT:
4566 mono_add_seq_point (cfg, bb, ins, code - cfg->native_code);
4567 break;
4569 /* EH */
4570 case OP_COND_EXC_C:
4571 case OP_COND_EXC_IC:
4572 case OP_COND_EXC_OV:
4573 case OP_COND_EXC_IOV:
4574 case OP_COND_EXC_NC:
4575 case OP_COND_EXC_INC:
4576 case OP_COND_EXC_NO:
4577 case OP_COND_EXC_INO:
4578 case OP_COND_EXC_EQ:
4579 case OP_COND_EXC_IEQ:
4580 case OP_COND_EXC_NE_UN:
4581 case OP_COND_EXC_INE_UN:
4582 case OP_COND_EXC_ILT:
4583 case OP_COND_EXC_LT:
4584 case OP_COND_EXC_ILT_UN:
4585 case OP_COND_EXC_LT_UN:
4586 case OP_COND_EXC_IGT:
4587 case OP_COND_EXC_GT:
4588 case OP_COND_EXC_IGT_UN:
4589 case OP_COND_EXC_GT_UN:
4590 case OP_COND_EXC_IGE:
4591 case OP_COND_EXC_GE:
4592 case OP_COND_EXC_IGE_UN:
4593 case OP_COND_EXC_GE_UN:
4594 case OP_COND_EXC_ILE:
4595 case OP_COND_EXC_LE:
4596 case OP_COND_EXC_ILE_UN:
4597 case OP_COND_EXC_LE_UN:
4598 code = emit_cond_exc (cfg, code, ins->opcode, (const char*)ins->inst_p1);
4599 break;
4600 case OP_THROW:
4601 if (sreg1 != ARMREG_R0)
4602 arm_movx (code, ARMREG_R0, sreg1);
4603 code = emit_call (cfg, code, MONO_PATCH_INFO_JIT_ICALL,
4604 (gpointer)"mono_arch_throw_exception");
4605 break;
4606 case OP_RETHROW:
4607 if (sreg1 != ARMREG_R0)
4608 arm_movx (code, ARMREG_R0, sreg1);
4609 code = emit_call (cfg, code, MONO_PATCH_INFO_JIT_ICALL,
4610 (gpointer)"mono_arch_rethrow_exception");
4611 break;
4612 case OP_CALL_HANDLER:
4613 mono_add_patch_info_rel (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_target_bb, MONO_R_ARM64_BL);
4614 arm_bl (code, 0);
4615 cfg->thunk_area += THUNK_SIZE;
4616 for (GList *tmp = ins->inst_eh_blocks; tmp != bb->clause_holes; tmp = tmp->prev)
4617 mono_cfg_add_try_hole (cfg, ((MonoLeaveClause *) tmp->data)->clause, code, bb);
4618 break;
4619 case OP_START_HANDLER: {
4620 MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
4622 /* Save caller address */
4623 code = emit_strx (code, ARMREG_LR, spvar->inst_basereg, spvar->inst_offset);
4626 * Reserve a param area, see test_0_finally_param_area ().
4627 * This is needed because the param area is not set up when
4628 * we are called from EH code.
4630 if (cfg->param_area)
4631 code = emit_subx_sp_imm (code, cfg->param_area);
4632 break;
4634 case OP_ENDFINALLY:
4635 case OP_ENDFILTER: {
4636 MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
4638 if (cfg->param_area)
4639 code = emit_addx_sp_imm (code, cfg->param_area);
4641 if (ins->opcode == OP_ENDFILTER && sreg1 != ARMREG_R0)
4642 arm_movx (code, ARMREG_R0, sreg1);
4644 /* Return to either after the branch in OP_CALL_HANDLER, or to the EH code */
4645 code = emit_ldrx (code, ARMREG_LR, spvar->inst_basereg, spvar->inst_offset);
4646 arm_brx (code, ARMREG_LR);
4647 break;
4649 case OP_GET_EX_OBJ:
4650 if (ins->dreg != ARMREG_R0)
4651 arm_movx (code, ins->dreg, ARMREG_R0);
4652 break;
4653 case OP_LIVERANGE_START: {
4654 if (cfg->verbose_level > 1)
4655 printf ("R%d START=0x%x\n", MONO_VARINFO (cfg, ins->inst_c0)->vreg, (int)(code - cfg->native_code));
4656 MONO_VARINFO (cfg, ins->inst_c0)->live_range_start = code - cfg->native_code;
4657 break;
4659 case OP_LIVERANGE_END: {
4660 if (cfg->verbose_level > 1)
4661 printf ("R%d END=0x%x\n", MONO_VARINFO (cfg, ins->inst_c0)->vreg, (int)(code - cfg->native_code));
4662 MONO_VARINFO (cfg, ins->inst_c0)->live_range_end = code - cfg->native_code;
4663 break;
4665 case OP_GC_SAFE_POINT: {
4666 guint8 *buf [1];
4668 arm_ldrx (code, ARMREG_IP1, ins->sreg1, 0);
4669 /* Call it if it is non-null */
4670 buf [0] = code;
4671 arm_cbzx (code, ARMREG_IP1, 0);
4672 code = emit_call (cfg, code, MONO_PATCH_INFO_JIT_ICALL, "mono_threads_state_poll");
4673 mono_arm_patch (buf [0], code, MONO_R_ARM64_CBZ);
4674 break;
4676 case OP_FILL_PROF_CALL_CTX:
4677 for (int i = 0; i < MONO_MAX_IREGS; i++)
4678 if ((MONO_ARCH_CALLEE_SAVED_REGS & (1 << i)) || i == ARMREG_SP || i == ARMREG_FP)
4679 arm_strx (code, i, ins->sreg1, MONO_STRUCT_OFFSET (MonoContext, regs) + i * sizeof (target_mgreg_t));
4680 break;
4681 default:
4682 g_warning ("unknown opcode %s in %s()\n", mono_inst_name (ins->opcode), __FUNCTION__);
4683 g_assert_not_reached ();
4686 if ((cfg->opt & MONO_OPT_BRANCH) && ((code - cfg->native_code - offset) > max_len)) {
4687 g_warning ("wrong maximal instruction length of instruction %s (expected %d, got %d)",
4688 mono_inst_name (ins->opcode), max_len, code - cfg->native_code - offset);
4689 g_assert_not_reached ();
4692 set_code_cursor (cfg, code);
4695 * If the compiled code size is larger than the bcc displacement (19 bits signed),
4696 * insert branch islands between/inside basic blocks.
4698 if (cfg->arch.cond_branch_islands)
4699 code = emit_branch_island (cfg, code, start_offset);
4702 static guint8*
4703 emit_move_args (MonoCompile *cfg, guint8 *code)
4705 MonoInst *ins;
4706 CallInfo *cinfo;
4707 ArgInfo *ainfo;
4708 int i, part;
4710 cinfo = cfg->arch.cinfo;
4711 g_assert (cinfo);
4712 for (i = 0; i < cinfo->nargs; ++i) {
4713 ainfo = cinfo->args + i;
4714 ins = cfg->args [i];
4716 if (ins->opcode == OP_REGVAR) {
4717 switch (ainfo->storage) {
4718 case ArgInIReg:
4719 arm_movx (code, ins->dreg, ainfo->reg);
4720 break;
4721 case ArgOnStack:
4722 switch (ainfo->slot_size) {
4723 case 1:
4724 if (ainfo->sign)
4725 code = emit_ldrsbx (code, ins->dreg, cfg->arch.args_reg, ainfo->offset);
4726 else
4727 code = emit_ldrb (code, ins->dreg, cfg->arch.args_reg, ainfo->offset);
4728 break;
4729 case 2:
4730 if (ainfo->sign)
4731 code = emit_ldrshx (code, ins->dreg, cfg->arch.args_reg, ainfo->offset);
4732 else
4733 code = emit_ldrh (code, ins->dreg, cfg->arch.args_reg, ainfo->offset);
4734 break;
4735 case 4:
4736 if (ainfo->sign)
4737 code = emit_ldrswx (code, ins->dreg, cfg->arch.args_reg, ainfo->offset);
4738 else
4739 code = emit_ldrw (code, ins->dreg, cfg->arch.args_reg, ainfo->offset);
4740 break;
4741 default:
4742 code = emit_ldrx (code, ins->dreg, cfg->arch.args_reg, ainfo->offset);
4743 break;
4745 break;
4746 default:
4747 g_assert_not_reached ();
4748 break;
4750 } else {
4751 if (ainfo->storage != ArgVtypeByRef && ainfo->storage != ArgVtypeByRefOnStack)
4752 g_assert (ins->opcode == OP_REGOFFSET);
4754 switch (ainfo->storage) {
4755 case ArgInIReg:
4756 /* Stack slots for arguments have size 8 */
4757 code = emit_strx (code, ainfo->reg, ins->inst_basereg, ins->inst_offset);
4758 break;
4759 case ArgInFReg:
4760 code = emit_strfpx (code, ainfo->reg, ins->inst_basereg, ins->inst_offset);
4761 break;
4762 case ArgInFRegR4:
4763 code = emit_strfpw (code, ainfo->reg, ins->inst_basereg, ins->inst_offset);
4764 break;
4765 case ArgOnStack:
4766 case ArgOnStackR4:
4767 case ArgOnStackR8:
4768 case ArgVtypeByRefOnStack:
4769 case ArgVtypeOnStack:
4770 break;
4771 case ArgVtypeByRef: {
4772 MonoInst *addr_arg = ins->inst_left;
4774 if (ainfo->gsharedvt) {
4775 g_assert (ins->opcode == OP_GSHAREDVT_ARG_REGOFFSET);
4776 arm_strx (code, ainfo->reg, ins->inst_basereg, ins->inst_offset);
4777 } else {
4778 g_assert (ins->opcode == OP_VTARG_ADDR);
4779 g_assert (addr_arg->opcode == OP_REGOFFSET);
4780 arm_strx (code, ainfo->reg, addr_arg->inst_basereg, addr_arg->inst_offset);
4782 break;
4784 case ArgVtypeInIRegs:
4785 for (part = 0; part < ainfo->nregs; part ++) {
4786 code = emit_strx (code, ainfo->reg + part, ins->inst_basereg, ins->inst_offset + (part * 8));
4788 break;
4789 case ArgHFA:
4790 for (part = 0; part < ainfo->nregs; part ++) {
4791 if (ainfo->esize == 4)
4792 code = emit_strfpw (code, ainfo->reg + part, ins->inst_basereg, ins->inst_offset + ainfo->foffsets [part]);
4793 else
4794 code = emit_strfpx (code, ainfo->reg + part, ins->inst_basereg, ins->inst_offset + ainfo->foffsets [part]);
4796 break;
4797 default:
4798 g_assert_not_reached ();
4799 break;
4804 return code;
4808 * emit_store_regarray:
4810 * Emit code to store the registers in REGS into the appropriate elements of
4811 * the register array at BASEREG+OFFSET.
4813 static __attribute__ ((__warn_unused_result__)) guint8*
4814 emit_store_regarray (guint8 *code, guint64 regs, int basereg, int offset)
4816 int i;
4818 for (i = 0; i < 32; ++i) {
4819 if (regs & (1 << i)) {
4820 if (i + 1 < 32 && (regs & (1 << (i + 1))) && (i + 1 != ARMREG_SP)) {
4821 arm_stpx (code, i, i + 1, basereg, offset + (i * 8));
4822 i++;
4823 } else if (i == ARMREG_SP) {
4824 arm_movspx (code, ARMREG_IP1, ARMREG_SP);
4825 arm_strx (code, ARMREG_IP1, basereg, offset + (i * 8));
4826 } else {
4827 arm_strx (code, i, basereg, offset + (i * 8));
4831 return code;
4835 * emit_load_regarray:
4837 * Emit code to load the registers in REGS from the appropriate elements of
4838 * the register array at BASEREG+OFFSET.
4840 static __attribute__ ((__warn_unused_result__)) guint8*
4841 emit_load_regarray (guint8 *code, guint64 regs, int basereg, int offset)
4843 int i;
4845 for (i = 0; i < 32; ++i) {
4846 if (regs & (1 << i)) {
4847 if ((regs & (1 << (i + 1))) && (i + 1 != ARMREG_SP)) {
4848 if (offset + (i * 8) < 500)
4849 arm_ldpx (code, i, i + 1, basereg, offset + (i * 8));
4850 else {
4851 code = emit_ldrx (code, i, basereg, offset + (i * 8));
4852 code = emit_ldrx (code, i + 1, basereg, offset + ((i + 1) * 8));
4854 i++;
4855 } else if (i == ARMREG_SP) {
4856 g_assert_not_reached ();
4857 } else {
4858 code = emit_ldrx (code, i, basereg, offset + (i * 8));
4862 return code;
4866 * emit_store_regset:
4868 * Emit code to store the registers in REGS into consecutive memory locations starting
4869 * at BASEREG+OFFSET.
4871 static __attribute__ ((__warn_unused_result__)) guint8*
4872 emit_store_regset (guint8 *code, guint64 regs, int basereg, int offset)
4874 int i, pos;
4876 pos = 0;
4877 for (i = 0; i < 32; ++i) {
4878 if (regs & (1 << i)) {
4879 if ((regs & (1 << (i + 1))) && (i + 1 != ARMREG_SP)) {
4880 arm_stpx (code, i, i + 1, basereg, offset + (pos * 8));
4881 i++;
4882 pos++;
4883 } else if (i == ARMREG_SP) {
4884 arm_movspx (code, ARMREG_IP1, ARMREG_SP);
4885 arm_strx (code, ARMREG_IP1, basereg, offset + (pos * 8));
4886 } else {
4887 arm_strx (code, i, basereg, offset + (pos * 8));
4889 pos++;
4892 return code;
4896 * emit_load_regset:
4898 * Emit code to load the registers in REGS from consecutive memory locations starting
4899 * at BASEREG+OFFSET.
4901 static __attribute__ ((__warn_unused_result__)) guint8*
4902 emit_load_regset (guint8 *code, guint64 regs, int basereg, int offset)
4904 int i, pos;
4906 pos = 0;
4907 for (i = 0; i < 32; ++i) {
4908 if (regs & (1 << i)) {
4909 if ((regs & (1 << (i + 1))) && (i + 1 != ARMREG_SP)) {
4910 arm_ldpx (code, i, i + 1, basereg, offset + (pos * 8));
4911 i++;
4912 pos++;
4913 } else if (i == ARMREG_SP) {
4914 g_assert_not_reached ();
4915 } else {
4916 arm_ldrx (code, i, basereg, offset + (pos * 8));
4918 pos++;
4921 return code;
4924 __attribute__ ((__warn_unused_result__)) guint8*
4925 mono_arm_emit_load_regarray (guint8 *code, guint64 regs, int basereg, int offset)
4927 return emit_load_regarray (code, regs, basereg, offset);
4930 __attribute__ ((__warn_unused_result__)) guint8*
4931 mono_arm_emit_store_regarray (guint8 *code, guint64 regs, int basereg, int offset)
4933 return emit_store_regarray (code, regs, basereg, offset);
4936 __attribute__ ((__warn_unused_result__)) guint8*
4937 mono_arm_emit_store_regset (guint8 *code, guint64 regs, int basereg, int offset)
4939 return emit_store_regset (code, regs, basereg, offset);
4942 /* Same as emit_store_regset, but emit unwind info too */
4943 /* CFA_OFFSET is the offset between the CFA and basereg */
4944 static __attribute__ ((__warn_unused_result__)) guint8*
4945 emit_store_regset_cfa (MonoCompile *cfg, guint8 *code, guint64 regs, int basereg, int offset, int cfa_offset, guint64 no_cfa_regset)
4947 int i, j, pos, nregs;
4948 guint32 cfa_regset = regs & ~no_cfa_regset;
4950 pos = 0;
4951 for (i = 0; i < 32; ++i) {
4952 nregs = 1;
4953 if (regs & (1 << i)) {
4954 if ((regs & (1 << (i + 1))) && (i + 1 != ARMREG_SP)) {
4955 if (offset < 256) {
4956 arm_stpx (code, i, i + 1, basereg, offset + (pos * 8));
4957 } else {
4958 code = emit_strx (code, i, basereg, offset + (pos * 8));
4959 code = emit_strx (code, i + 1, basereg, offset + (pos * 8) + 8);
4961 nregs = 2;
4962 } else if (i == ARMREG_SP) {
4963 arm_movspx (code, ARMREG_IP1, ARMREG_SP);
4964 code = emit_strx (code, ARMREG_IP1, basereg, offset + (pos * 8));
4965 } else {
4966 code = emit_strx (code, i, basereg, offset + (pos * 8));
4969 for (j = 0; j < nregs; ++j) {
4970 if (cfa_regset & (1 << (i + j)))
4971 mono_emit_unwind_op_offset (cfg, code, i + j, (- cfa_offset) + offset + ((pos + j) * 8));
4974 i += nregs - 1;
4975 pos += nregs;
4978 return code;
4982 * emit_setup_lmf:
4984 * Emit code to initialize an LMF structure at LMF_OFFSET.
4985 * Clobbers ip0/ip1.
4987 static guint8*
4988 emit_setup_lmf (MonoCompile *cfg, guint8 *code, gint32 lmf_offset, int cfa_offset)
4991 * The LMF should contain all the state required to be able to reconstruct the machine state
4992 * at the current point of execution. Since the LMF is only read during EH, only callee
4993 * saved etc. registers need to be saved.
4994 * FIXME: Save callee saved fp regs, JITted code doesn't use them, but native code does, and they
4995 * need to be restored during EH.
4998 /* pc */
4999 arm_adrx (code, ARMREG_LR, code);
5000 code = emit_strx (code, ARMREG_LR, ARMREG_FP, lmf_offset + MONO_STRUCT_OFFSET (MonoLMF, pc));
5001 /* gregs + fp + sp */
5002 /* Don't emit unwind info for sp/fp, they are already handled in the prolog */
5003 code = emit_store_regset_cfa (cfg, code, MONO_ARCH_LMF_REGS, ARMREG_FP, lmf_offset + MONO_STRUCT_OFFSET (MonoLMF, gregs), cfa_offset, (1 << ARMREG_FP) | (1 << ARMREG_SP));
5005 return code;
5008 guint8 *
5009 mono_arch_emit_prolog (MonoCompile *cfg)
5011 MonoMethod *method = cfg->method;
5012 MonoMethodSignature *sig;
5013 MonoBasicBlock *bb;
5014 guint8 *code;
5015 int cfa_offset, max_offset;
5017 sig = mono_method_signature_internal (method);
5018 cfg->code_size = 256 + sig->param_count * 64;
5019 code = cfg->native_code = g_malloc (cfg->code_size);
5021 /* This can be unaligned */
5022 cfg->stack_offset = ALIGN_TO (cfg->stack_offset, MONO_ARCH_FRAME_ALIGNMENT);
5025 * - Setup frame
5027 cfa_offset = 0;
5028 mono_emit_unwind_op_def_cfa (cfg, code, ARMREG_SP, 0);
5030 /* Setup frame */
5031 if (arm_is_ldpx_imm (-cfg->stack_offset)) {
5032 arm_stpx_pre (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, -cfg->stack_offset);
5033 } else {
5034 /* sp -= cfg->stack_offset */
5035 /* This clobbers ip0/ip1 */
5036 code = emit_subx_sp_imm (code, cfg->stack_offset);
5037 arm_stpx (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, 0);
5039 cfa_offset += cfg->stack_offset;
5040 mono_emit_unwind_op_def_cfa_offset (cfg, code, cfa_offset);
5041 mono_emit_unwind_op_offset (cfg, code, ARMREG_FP, (- cfa_offset) + 0);
5042 mono_emit_unwind_op_offset (cfg, code, ARMREG_LR, (- cfa_offset) + 8);
5043 arm_movspx (code, ARMREG_FP, ARMREG_SP);
5044 mono_emit_unwind_op_def_cfa_reg (cfg, code, ARMREG_FP);
5045 if (cfg->param_area) {
5046 /* The param area is below the frame pointer */
5047 code = emit_subx_sp_imm (code, cfg->param_area);
5050 if (cfg->method->save_lmf) {
5051 code = emit_setup_lmf (cfg, code, cfg->lmf_var->inst_offset, cfa_offset);
5052 } else {
5053 /* Save gregs */
5054 code = emit_store_regset_cfa (cfg, code, MONO_ARCH_CALLEE_SAVED_REGS & cfg->used_int_regs, ARMREG_FP, cfg->arch.saved_gregs_offset, cfa_offset, 0);
5057 /* Setup args reg */
5058 if (cfg->arch.args_reg) {
5059 /* The register was already saved above */
5060 code = emit_addx_imm (code, cfg->arch.args_reg, ARMREG_FP, cfg->stack_offset);
5063 /* Save return area addr received in R8 */
5064 if (cfg->vret_addr) {
5065 MonoInst *ins = cfg->vret_addr;
5067 g_assert (ins->opcode == OP_REGOFFSET);
5068 code = emit_strx (code, ARMREG_R8, ins->inst_basereg, ins->inst_offset);
5071 /* Save mrgctx received in MONO_ARCH_RGCTX_REG */
5072 if (cfg->rgctx_var) {
5073 MonoInst *ins = cfg->rgctx_var;
5075 g_assert (ins->opcode == OP_REGOFFSET);
5077 code = emit_strx (code, MONO_ARCH_RGCTX_REG, ins->inst_basereg, ins->inst_offset);
5081 * Move arguments to their registers/stack locations.
5083 code = emit_move_args (cfg, code);
5085 /* Initialize seq_point_info_var */
5086 if (cfg->arch.seq_point_info_var) {
5087 MonoInst *ins = cfg->arch.seq_point_info_var;
5089 /* Initialize the variable from a GOT slot */
5090 code = emit_aotconst (cfg, code, ARMREG_IP0, MONO_PATCH_INFO_SEQ_POINT_INFO, cfg->method);
5091 g_assert (ins->opcode == OP_REGOFFSET);
5092 code = emit_strx (code, ARMREG_IP0, ins->inst_basereg, ins->inst_offset);
5094 /* Initialize ss_tramp_var */
5095 ins = cfg->arch.ss_tramp_var;
5096 g_assert (ins->opcode == OP_REGOFFSET);
5098 code = emit_ldrx (code, ARMREG_IP1, ARMREG_IP0, MONO_STRUCT_OFFSET (SeqPointInfo, ss_tramp_addr));
5099 code = emit_strx (code, ARMREG_IP1, ins->inst_basereg, ins->inst_offset);
5100 } else {
5101 MonoInst *ins;
5103 if (cfg->arch.ss_tramp_var) {
5104 /* Initialize ss_tramp_var */
5105 ins = cfg->arch.ss_tramp_var;
5106 g_assert (ins->opcode == OP_REGOFFSET);
5108 code = emit_imm64 (code, ARMREG_IP0, (guint64)&ss_trampoline);
5109 code = emit_strx (code, ARMREG_IP0, ins->inst_basereg, ins->inst_offset);
5112 if (cfg->arch.bp_tramp_var) {
5113 /* Initialize bp_tramp_var */
5114 ins = cfg->arch.bp_tramp_var;
5115 g_assert (ins->opcode == OP_REGOFFSET);
5117 code = emit_imm64 (code, ARMREG_IP0, (guint64)bp_trampoline);
5118 code = emit_strx (code, ARMREG_IP0, ins->inst_basereg, ins->inst_offset);
5122 max_offset = 0;
5123 if (cfg->opt & MONO_OPT_BRANCH) {
5124 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
5125 MonoInst *ins;
5126 bb->max_offset = max_offset;
5128 MONO_BB_FOR_EACH_INS (bb, ins) {
5129 max_offset += ins_get_size (ins->opcode);
5133 if (max_offset > 0x3ffff * 4)
5134 cfg->arch.cond_branch_islands = TRUE;
5136 return code;
5139 void
5140 mono_arch_emit_epilog (MonoCompile *cfg)
5142 CallInfo *cinfo;
5143 int max_epilog_size;
5144 guint8 *code;
5145 int i;
5147 max_epilog_size = 16 + 20*4;
5148 code = realloc_code (cfg, max_epilog_size);
5150 if (cfg->method->save_lmf) {
5151 code = mono_arm_emit_load_regarray (code, MONO_ARCH_CALLEE_SAVED_REGS & cfg->used_int_regs, ARMREG_FP, cfg->lmf_var->inst_offset + MONO_STRUCT_OFFSET (MonoLMF, gregs) - (MONO_ARCH_FIRST_LMF_REG * 8));
5152 } else {
5153 /* Restore gregs */
5154 code = emit_load_regset (code, MONO_ARCH_CALLEE_SAVED_REGS & cfg->used_int_regs, ARMREG_FP, cfg->arch.saved_gregs_offset);
5157 /* Load returned vtypes into registers if needed */
5158 cinfo = cfg->arch.cinfo;
5159 switch (cinfo->ret.storage) {
5160 case ArgVtypeInIRegs: {
5161 MonoInst *ins = cfg->ret;
5163 for (i = 0; i < cinfo->ret.nregs; ++i)
5164 code = emit_ldrx (code, cinfo->ret.reg + i, ins->inst_basereg, ins->inst_offset + (i * 8));
5165 break;
5167 case ArgHFA: {
5168 MonoInst *ins = cfg->ret;
5170 for (i = 0; i < cinfo->ret.nregs; ++i) {
5171 if (cinfo->ret.esize == 4)
5172 code = emit_ldrfpw (code, cinfo->ret.reg + i, ins->inst_basereg, ins->inst_offset + cinfo->ret.foffsets [i]);
5173 else
5174 code = emit_ldrfpx (code, cinfo->ret.reg + i, ins->inst_basereg, ins->inst_offset + cinfo->ret.foffsets [i]);
5176 break;
5178 default:
5179 break;
5182 /* Destroy frame */
5183 code = mono_arm_emit_destroy_frame (code, cfg->stack_offset, (1 << ARMREG_IP0) | (1 << ARMREG_IP1));
5185 arm_retx (code, ARMREG_LR);
5187 g_assert (code - (cfg->native_code + cfg->code_len) < max_epilog_size);
5189 set_code_cursor (cfg, code);
5192 void
5193 mono_arch_emit_exceptions (MonoCompile *cfg)
5195 MonoJumpInfo *ji;
5196 MonoClass *exc_class;
5197 guint8 *code, *ip;
5198 guint8* exc_throw_pos [MONO_EXC_INTRINS_NUM];
5199 guint8 exc_throw_found [MONO_EXC_INTRINS_NUM];
5200 int i, id, size = 0;
5202 for (i = 0; i < MONO_EXC_INTRINS_NUM; i++) {
5203 exc_throw_pos [i] = NULL;
5204 exc_throw_found [i] = 0;
5207 for (ji = cfg->patch_info; ji; ji = ji->next) {
5208 if (ji->type == MONO_PATCH_INFO_EXC) {
5209 i = mini_exception_id_by_name ((const char*)ji->data.target);
5210 if (!exc_throw_found [i]) {
5211 size += 32;
5212 exc_throw_found [i] = TRUE;
5217 code = realloc_code (cfg, size);
5219 /* Emit code to raise corlib exceptions */
5220 for (ji = cfg->patch_info; ji; ji = ji->next) {
5221 if (ji->type != MONO_PATCH_INFO_EXC)
5222 continue;
5224 ip = cfg->native_code + ji->ip.i;
5226 id = mini_exception_id_by_name ((const char*)ji->data.target);
5228 if (exc_throw_pos [id]) {
5229 /* ip points to the bcc () in OP_COND_EXC_... */
5230 arm_patch_rel (ip, exc_throw_pos [id], ji->relocation);
5231 ji->type = MONO_PATCH_INFO_NONE;
5232 continue;
5235 exc_throw_pos [id] = code;
5236 arm_patch_rel (ip, code, ji->relocation);
5238 /* We are being branched to from the code generated by emit_cond_exc (), the pc is in ip1 */
5240 /* r0 = type token */
5241 exc_class = mono_class_load_from_name (mono_defaults.corlib, "System", ji->data.name);
5242 code = emit_imm (code, ARMREG_R0, m_class_get_type_token (exc_class) - MONO_TOKEN_TYPE_DEF);
5243 /* r1 = throw ip */
5244 arm_movx (code, ARMREG_R1, ARMREG_IP1);
5245 /* Branch to the corlib exception throwing trampoline */
5246 ji->ip.i = code - cfg->native_code;
5247 ji->type = MONO_PATCH_INFO_JIT_ICALL;
5248 ji->data.name = "mono_arch_throw_corlib_exception";
5249 ji->relocation = MONO_R_ARM64_BL;
5250 arm_bl (code, 0);
5251 cfg->thunk_area += THUNK_SIZE;
5252 set_code_cursor (cfg, code);
5255 set_code_cursor (cfg, code);
5258 MonoInst*
5259 mono_arch_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5261 return NULL;
5264 guint32
5265 mono_arch_get_patch_offset (guint8 *code)
5267 return 0;
5270 gpointer
5271 mono_arch_build_imt_trampoline (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count,
5272 gpointer fail_tramp)
5274 int i, buf_len, imt_reg;
5275 guint8 *buf, *code;
5277 #if DEBUG_IMT
5278 printf ("building IMT trampoline for class %s %s entries %d code size %d code at %p end %p vtable %p\n", m_class_get_name_space (vtable->klass), m_class_get_name (vtable->klass), count, size, start, ((guint8*)start) + size, vtable);
5279 for (i = 0; i < count; ++i) {
5280 MonoIMTCheckItem *item = imt_entries [i];
5281 printf ("method %d (%p) %s vtable slot %p is_equals %d chunk size %d\n", i, item->key, item->key->name, &vtable->vtable [item->value.vtable_slot], item->is_equals, item->chunk_size);
5283 #endif
5285 buf_len = 0;
5286 for (i = 0; i < count; ++i) {
5287 MonoIMTCheckItem *item = imt_entries [i];
5288 if (item->is_equals) {
5289 gboolean fail_case = !item->check_target_idx && fail_tramp;
5291 if (item->check_target_idx || fail_case) {
5292 if (!item->compare_done || fail_case) {
5293 buf_len += 4 * 4 + 4;
5295 buf_len += 4;
5296 if (item->has_target_code) {
5297 buf_len += 5 * 4;
5298 } else {
5299 buf_len += 6 * 4;
5301 if (fail_case) {
5302 buf_len += 5 * 4;
5304 } else {
5305 buf_len += 6 * 4;
5307 } else {
5308 buf_len += 6 * 4;
5312 if (fail_tramp)
5313 buf = (guint8*)mono_method_alloc_generic_virtual_trampoline (domain, buf_len);
5314 else
5315 buf = mono_domain_code_reserve (domain, buf_len);
5316 code = buf;
5319 * We are called by JITted code, which passes in the IMT argument in
5320 * MONO_ARCH_RGCTX_REG (r27). We need to preserve all caller saved regs
5321 * except ip0/ip1.
5323 imt_reg = MONO_ARCH_RGCTX_REG;
5324 for (i = 0; i < count; ++i) {
5325 MonoIMTCheckItem *item = imt_entries [i];
5327 item->code_target = code;
5329 if (item->is_equals) {
5331 * Check the imt argument against item->key, if equals, jump to either
5332 * item->value.target_code or to vtable [item->value.vtable_slot].
5333 * If fail_tramp is set, jump to it if not-equals.
5335 gboolean fail_case = !item->check_target_idx && fail_tramp;
5337 if (item->check_target_idx || fail_case) {
5338 /* Compare imt_reg with item->key */
5339 if (!item->compare_done || fail_case) {
5340 // FIXME: Optimize this
5341 code = emit_imm64 (code, ARMREG_IP0, (guint64)item->key);
5342 arm_cmpx (code, imt_reg, ARMREG_IP0);
5344 item->jmp_code = code;
5345 arm_bcc (code, ARMCOND_NE, 0);
5346 /* Jump to target if equals */
5347 if (item->has_target_code) {
5348 code = emit_imm64 (code, ARMREG_IP0, (guint64)item->value.target_code);
5349 arm_brx (code, ARMREG_IP0);
5350 } else {
5351 guint64 imm = (guint64)&(vtable->vtable [item->value.vtable_slot]);
5353 code = emit_imm64 (code, ARMREG_IP0, imm);
5354 arm_ldrx (code, ARMREG_IP0, ARMREG_IP0, 0);
5355 arm_brx (code, ARMREG_IP0);
5358 if (fail_case) {
5359 arm_patch_rel (item->jmp_code, code, MONO_R_ARM64_BCC);
5360 item->jmp_code = NULL;
5361 code = emit_imm64 (code, ARMREG_IP0, (guint64)fail_tramp);
5362 arm_brx (code, ARMREG_IP0);
5364 } else {
5365 guint64 imm = (guint64)&(vtable->vtable [item->value.vtable_slot]);
5367 code = emit_imm64 (code, ARMREG_IP0, imm);
5368 arm_ldrx (code, ARMREG_IP0, ARMREG_IP0, 0);
5369 arm_brx (code, ARMREG_IP0);
5371 } else {
5372 code = emit_imm64 (code, ARMREG_IP0, (guint64)item->key);
5373 arm_cmpx (code, imt_reg, ARMREG_IP0);
5374 item->jmp_code = code;
5375 arm_bcc (code, ARMCOND_HS, 0);
5378 /* Patch the branches */
5379 for (i = 0; i < count; ++i) {
5380 MonoIMTCheckItem *item = imt_entries [i];
5381 if (item->jmp_code && item->check_target_idx)
5382 arm_patch_rel (item->jmp_code, imt_entries [item->check_target_idx]->code_target, MONO_R_ARM64_BCC);
5385 g_assert ((code - buf) < buf_len);
5387 mono_arch_flush_icache (buf, code - buf);
5388 MONO_PROFILER_RAISE (jit_code_buffer, (buf, code - buf, MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE, NULL));
5390 return buf;
5393 GSList *
5394 mono_arch_get_trampolines (gboolean aot)
5396 return mono_arm_get_exception_trampolines (aot);
5399 #else /* DISABLE_JIT */
5401 gpointer
5402 mono_arch_build_imt_trampoline (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count,
5403 gpointer fail_tramp)
5405 g_assert_not_reached ();
5406 return NULL;
5409 #endif /* !DISABLE_JIT */
5411 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
5413 void
5414 mono_arch_set_breakpoint (MonoJitInfo *ji, guint8 *ip)
5416 guint8 *code = ip;
5417 guint32 native_offset = ip - (guint8*)ji->code_start;
5419 if (ji->from_aot) {
5420 SeqPointInfo *info = mono_arch_get_seq_point_info (mono_domain_get (), (guint8*)ji->code_start);
5422 g_assert (native_offset % 4 == 0);
5423 g_assert (info->bp_addrs [native_offset / 4] == 0);
5424 info->bp_addrs [native_offset / 4] = (guint8*)mini_get_breakpoint_trampoline ();
5425 } else {
5426 /* ip points to an ldrx */
5427 code += 4;
5428 arm_blrx (code, ARMREG_IP0);
5429 mono_arch_flush_icache (ip, code - ip);
5433 void
5434 mono_arch_clear_breakpoint (MonoJitInfo *ji, guint8 *ip)
5436 guint8 *code = ip;
5438 if (ji->from_aot) {
5439 guint32 native_offset = ip - (guint8*)ji->code_start;
5440 SeqPointInfo *info = mono_arch_get_seq_point_info (mono_domain_get (), (guint8*)ji->code_start);
5442 g_assert (native_offset % 4 == 0);
5443 info->bp_addrs [native_offset / 4] = NULL;
5444 } else {
5445 /* ip points to an ldrx */
5446 code += 4;
5447 arm_nop (code);
5448 mono_arch_flush_icache (ip, code - ip);
5452 void
5453 mono_arch_start_single_stepping (void)
5455 ss_trampoline = mini_get_single_step_trampoline ();
5458 void
5459 mono_arch_stop_single_stepping (void)
5461 ss_trampoline = NULL;
5464 gboolean
5465 mono_arch_is_single_step_event (void *info, void *sigctx)
5467 /* We use soft breakpoints on arm64 */
5468 return FALSE;
5471 gboolean
5472 mono_arch_is_breakpoint_event (void *info, void *sigctx)
5474 /* We use soft breakpoints on arm64 */
5475 return FALSE;
5478 void
5479 mono_arch_skip_breakpoint (MonoContext *ctx, MonoJitInfo *ji)
5481 g_assert_not_reached ();
5484 void
5485 mono_arch_skip_single_step (MonoContext *ctx)
5487 g_assert_not_reached ();
5490 SeqPointInfo*
5491 mono_arch_get_seq_point_info (MonoDomain *domain, guint8 *code)
5493 SeqPointInfo *info;
5494 MonoJitInfo *ji;
5496 // FIXME: Add a free function
5498 mono_domain_lock (domain);
5499 info = (SeqPointInfo*)g_hash_table_lookup (domain_jit_info (domain)->arch_seq_points,
5500 code);
5501 mono_domain_unlock (domain);
5503 if (!info) {
5504 ji = mono_jit_info_table_find (domain, code);
5505 g_assert (ji);
5507 info = g_malloc0 (sizeof (SeqPointInfo) + (ji->code_size / 4) * sizeof(guint8*));
5509 info->ss_tramp_addr = &ss_trampoline;
5511 mono_domain_lock (domain);
5512 g_hash_table_insert (domain_jit_info (domain)->arch_seq_points,
5513 code, info);
5514 mono_domain_unlock (domain);
5517 return info;
5520 #endif /* MONO_ARCH_SOFT_DEBUG_SUPPORTED */
5522 gboolean
5523 mono_arch_opcode_supported (int opcode)
5525 switch (opcode) {
5526 case OP_ATOMIC_ADD_I4:
5527 case OP_ATOMIC_ADD_I8:
5528 case OP_ATOMIC_EXCHANGE_I4:
5529 case OP_ATOMIC_EXCHANGE_I8:
5530 case OP_ATOMIC_CAS_I4:
5531 case OP_ATOMIC_CAS_I8:
5532 case OP_ATOMIC_LOAD_I1:
5533 case OP_ATOMIC_LOAD_I2:
5534 case OP_ATOMIC_LOAD_I4:
5535 case OP_ATOMIC_LOAD_I8:
5536 case OP_ATOMIC_LOAD_U1:
5537 case OP_ATOMIC_LOAD_U2:
5538 case OP_ATOMIC_LOAD_U4:
5539 case OP_ATOMIC_LOAD_U8:
5540 case OP_ATOMIC_LOAD_R4:
5541 case OP_ATOMIC_LOAD_R8:
5542 case OP_ATOMIC_STORE_I1:
5543 case OP_ATOMIC_STORE_I2:
5544 case OP_ATOMIC_STORE_I4:
5545 case OP_ATOMIC_STORE_I8:
5546 case OP_ATOMIC_STORE_U1:
5547 case OP_ATOMIC_STORE_U2:
5548 case OP_ATOMIC_STORE_U4:
5549 case OP_ATOMIC_STORE_U8:
5550 case OP_ATOMIC_STORE_R4:
5551 case OP_ATOMIC_STORE_R8:
5552 return TRUE;
5553 default:
5554 return FALSE;
5558 CallInfo*
5559 mono_arch_get_call_info (MonoMemPool *mp, MonoMethodSignature *sig)
5561 return get_call_info (mp, sig);