3 * ARM backend for the Mono code generator
6 * Paolo Molaro (lupus@ximian.com)
7 * Dietmar Maurer (dietmar@ximian.com)
9 * (C) 2003 Ximian, Inc.
10 * Copyright 2003-2011 Novell, Inc (http://www.novell.com)
11 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
12 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
17 #include <mono/metadata/abi-details.h>
18 #include <mono/metadata/appdomain.h>
19 #include <mono/metadata/profiler-private.h>
20 #include <mono/metadata/debug-helpers.h>
21 #include <mono/utils/mono-mmap.h>
22 #include <mono/utils/mono-hwcap.h>
23 #include <mono/utils/mono-memory-model.h>
24 #include <mono/utils/mono-threads-coop.h>
30 #include "debugger-agent.h"
32 #include "mono/arch/arm/arm-vfp-codegen.h"
34 /* Sanity check: This makes no sense */
35 #if defined(ARM_FPU_NONE) && (defined(ARM_FPU_VFP) || defined(ARM_FPU_VFP_HARD))
36 #error "ARM_FPU_NONE is defined while one of ARM_FPU_VFP/ARM_FPU_VFP_HARD is defined"
40 * IS_SOFT_FLOAT: Is full software floating point used?
41 * IS_HARD_FLOAT: Is full hardware floating point used?
42 * IS_VFP: Is hardware floating point with software ABI used?
44 * These are not necessarily constants, e.g. IS_SOFT_FLOAT and
45 * IS_VFP may delegate to mono_arch_is_soft_float ().
48 #if defined(ARM_FPU_VFP_HARD)
49 #define IS_SOFT_FLOAT (FALSE)
50 #define IS_HARD_FLOAT (TRUE)
52 #elif defined(ARM_FPU_NONE)
53 #define IS_SOFT_FLOAT (mono_arch_is_soft_float ())
54 #define IS_HARD_FLOAT (FALSE)
55 #define IS_VFP (!mono_arch_is_soft_float ())
57 #define IS_SOFT_FLOAT (FALSE)
58 #define IS_HARD_FLOAT (FALSE)
62 #define THUNK_SIZE (3 * 4)
64 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
67 void sys_icache_invalidate (void *start
, size_t len
);
70 /* This mutex protects architecture specific caches */
71 #define mono_mini_arch_lock() mono_os_mutex_lock (&mini_arch_mutex)
72 #define mono_mini_arch_unlock() mono_os_mutex_unlock (&mini_arch_mutex)
73 static mono_mutex_t mini_arch_mutex
;
75 static gboolean v5_supported
= FALSE
;
76 static gboolean v6_supported
= FALSE
;
77 static gboolean v7_supported
= FALSE
;
78 static gboolean v7s_supported
= FALSE
;
79 static gboolean v7k_supported
= FALSE
;
80 static gboolean thumb_supported
= FALSE
;
81 static gboolean thumb2_supported
= FALSE
;
83 * Whenever to use the ARM EABI
85 static gboolean eabi_supported
= FALSE
;
88 * Whenever to use the iphone ABI extensions:
89 * http://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIReference/index.html
90 * Basically, r7 is used as a frame pointer and it should point to the saved r7 + lr.
91 * This is required for debugging/profiling tools to work, but it has some overhead so it should
92 * only be turned on in debug builds.
94 static gboolean iphone_abi
= FALSE
;
97 * The FPU we are generating code for. This is NOT runtime configurable right now,
98 * since some things like MONO_ARCH_CALLEE_FREGS still depend on defines.
100 static MonoArmFPU arm_fpu
;
102 #if defined(ARM_FPU_VFP_HARD)
104 * On armhf, d0-d7 are used for argument passing and d8-d15
105 * must be preserved across calls, which leaves us no room
106 * for scratch registers. So we use d14-d15 but back up their
107 * previous contents to a stack slot before using them - see
108 * mono_arm_emit_vfp_scratch_save/_restore ().
110 static int vfp_scratch1
= ARM_VFP_D14
;
111 static int vfp_scratch2
= ARM_VFP_D15
;
114 * On armel, d0-d7 do not need to be preserved, so we can
115 * freely make use of them as scratch registers.
117 static int vfp_scratch1
= ARM_VFP_D0
;
118 static int vfp_scratch2
= ARM_VFP_D1
;
123 static gpointer single_step_tramp
, breakpoint_tramp
;
126 * The code generated for sequence points reads from this location, which is
127 * made read-only when single stepping is enabled.
129 static gpointer ss_trigger_page
;
131 /* Enabled breakpoints read from this trigger page */
132 static gpointer bp_trigger_page
;
136 * floating point support: on ARM it is a mess, there are at least 3
137 * different setups, each of which binary incompat with the other.
138 * 1) FPA: old and ugly, but unfortunately what current distros use
139 * the double binary format has the two words swapped. 8 double registers.
140 * Implemented usually by kernel emulation.
141 * 2) softfloat: the compiler emulates all the fp ops. Usually uses the
142 * ugly swapped double format (I guess a softfloat-vfp exists, too, though).
143 * 3) VFP: the new and actually sensible and useful FP support. Implemented
144 * in HW or kernel-emulated, requires new tools. I think this is what symbian uses.
146 * We do not care about FPA. We will support soft float and VFP.
148 int mono_exc_esp_offset
= 0;
150 #define arm_is_imm12(v) ((v) > -4096 && (v) < 4096)
151 #define arm_is_imm8(v) ((v) > -256 && (v) < 256)
152 #define arm_is_fpimm8(v) ((v) >= -1020 && (v) <= 1020)
154 #define LDR_MASK ((0xf << ARMCOND_SHIFT) | (3 << 26) | (1 << 22) | (1 << 20) | (15 << 12))
155 #define LDR_PC_VAL ((ARMCOND_AL << ARMCOND_SHIFT) | (1 << 26) | (0 << 22) | (1 << 20) | (15 << 12))
156 #define IS_LDR_PC(val) (((val) & LDR_MASK) == LDR_PC_VAL)
158 //#define DEBUG_IMT 0
161 static void mono_arch_compute_omit_fp (MonoCompile
*cfg
);
165 emit_aotconst (MonoCompile
*cfg
, guint8
*code
, int dreg
, int patch_type
, gpointer data
);
168 mono_arch_regname (int reg
)
170 static const char * rnames
[] = {
171 "arm_r0", "arm_r1", "arm_r2", "arm_r3", "arm_v1",
172 "arm_v2", "arm_v3", "arm_v4", "arm_v5", "arm_v6",
173 "arm_v7", "arm_fp", "arm_ip", "arm_sp", "arm_lr",
176 if (reg
>= 0 && reg
< 16)
182 mono_arch_fregname (int reg
)
184 static const char * rnames
[] = {
185 "arm_f0", "arm_f1", "arm_f2", "arm_f3", "arm_f4",
186 "arm_f5", "arm_f6", "arm_f7", "arm_f8", "arm_f9",
187 "arm_f10", "arm_f11", "arm_f12", "arm_f13", "arm_f14",
188 "arm_f15", "arm_f16", "arm_f17", "arm_f18", "arm_f19",
189 "arm_f20", "arm_f21", "arm_f22", "arm_f23", "arm_f24",
190 "arm_f25", "arm_f26", "arm_f27", "arm_f28", "arm_f29",
193 if (reg
>= 0 && reg
< 32)
201 emit_big_add (guint8
*code
, int dreg
, int sreg
, int imm
)
203 int imm8
, rot_amount
;
204 if ((imm8
= mono_arm_is_rotated_imm8 (imm
, &rot_amount
)) >= 0) {
205 ARM_ADD_REG_IMM (code
, dreg
, sreg
, imm8
, rot_amount
);
209 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, imm
);
210 ARM_ADD_REG_REG (code
, dreg
, sreg
, ARMREG_IP
);
212 code
= mono_arm_emit_load_imm (code
, dreg
, imm
);
213 ARM_ADD_REG_REG (code
, dreg
, dreg
, sreg
);
219 emit_ldr_imm (guint8
*code
, int dreg
, int sreg
, int imm
)
221 if (!arm_is_imm12 (imm
)) {
222 g_assert (dreg
!= sreg
);
223 code
= emit_big_add (code
, dreg
, sreg
, imm
);
224 ARM_LDR_IMM (code
, dreg
, dreg
, 0);
226 ARM_LDR_IMM (code
, dreg
, sreg
, imm
);
231 /* If dreg == sreg, this clobbers IP */
233 emit_sub_imm (guint8
*code
, int dreg
, int sreg
, int imm
)
235 int imm8
, rot_amount
;
236 if ((imm8
= mono_arm_is_rotated_imm8 (imm
, &rot_amount
)) >= 0) {
237 ARM_SUB_REG_IMM (code
, dreg
, sreg
, imm8
, rot_amount
);
241 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, imm
);
242 ARM_SUB_REG_REG (code
, dreg
, sreg
, ARMREG_IP
);
244 code
= mono_arm_emit_load_imm (code
, dreg
, imm
);
245 ARM_SUB_REG_REG (code
, dreg
, dreg
, sreg
);
251 emit_memcpy (guint8
*code
, int size
, int dreg
, int doffset
, int sreg
, int soffset
)
253 /* we can use r0-r3, since this is called only for incoming args on the stack */
254 if (size
> sizeof (gpointer
) * 4) {
256 code
= emit_big_add (code
, ARMREG_R0
, sreg
, soffset
);
257 code
= emit_big_add (code
, ARMREG_R1
, dreg
, doffset
);
258 start_loop
= code
= mono_arm_emit_load_imm (code
, ARMREG_R2
, size
);
259 ARM_LDR_IMM (code
, ARMREG_R3
, ARMREG_R0
, 0);
260 ARM_STR_IMM (code
, ARMREG_R3
, ARMREG_R1
, 0);
261 ARM_ADD_REG_IMM8 (code
, ARMREG_R0
, ARMREG_R0
, 4);
262 ARM_ADD_REG_IMM8 (code
, ARMREG_R1
, ARMREG_R1
, 4);
263 ARM_SUBS_REG_IMM8 (code
, ARMREG_R2
, ARMREG_R2
, 4);
264 ARM_B_COND (code
, ARMCOND_NE
, 0);
265 arm_patch (code
- 4, start_loop
);
268 if (arm_is_imm12 (doffset
) && arm_is_imm12 (doffset
+ size
) &&
269 arm_is_imm12 (soffset
) && arm_is_imm12 (soffset
+ size
)) {
271 ARM_LDR_IMM (code
, ARMREG_LR
, sreg
, soffset
);
272 ARM_STR_IMM (code
, ARMREG_LR
, dreg
, doffset
);
278 code
= emit_big_add (code
, ARMREG_R0
, sreg
, soffset
);
279 code
= emit_big_add (code
, ARMREG_R1
, dreg
, doffset
);
280 doffset
= soffset
= 0;
282 ARM_LDR_IMM (code
, ARMREG_LR
, ARMREG_R0
, soffset
);
283 ARM_STR_IMM (code
, ARMREG_LR
, ARMREG_R1
, doffset
);
289 g_assert (size
== 0);
294 emit_call_reg (guint8
*code
, int reg
)
297 ARM_BLX_REG (code
, reg
);
299 ARM_MOV_REG_REG (code
, ARMREG_LR
, ARMREG_PC
);
303 ARM_MOV_REG_REG (code
, ARMREG_PC
, reg
);
309 emit_call_seq (MonoCompile
*cfg
, guint8
*code
)
311 if (cfg
->method
->dynamic
) {
312 ARM_LDR_IMM (code
, ARMREG_IP
, ARMREG_PC
, 0);
314 *(gpointer
*)code
= NULL
;
316 code
= emit_call_reg (code
, ARMREG_IP
);
320 cfg
->thunk_area
+= THUNK_SIZE
;
325 mono_arm_patchable_b (guint8
*code
, int cond
)
327 ARM_B_COND (code
, cond
, 0);
332 mono_arm_patchable_bl (guint8
*code
, int cond
)
334 ARM_BL_COND (code
, cond
, 0);
338 #if defined(__ARM_EABI__) && defined(__linux__) && !defined(PLATFORM_ANDROID) && !defined(MONO_CROSS_COMPILE)
339 #define HAVE_AEABI_READ_TP 1
342 #ifdef HAVE_AEABI_READ_TP
343 gpointer
__aeabi_read_tp (void);
347 mono_arch_have_fast_tls (void)
349 #ifdef HAVE_AEABI_READ_TP
350 static gboolean have_fast_tls
= FALSE
;
351 static gboolean inited
= FALSE
;
353 if (mini_get_debug_options ()->use_fallback_tls
)
357 return have_fast_tls
;
362 tp1
= __aeabi_read_tp ();
363 asm volatile("mrc p15, 0, %0, c13, c0, 3" : "=r" (tp2
));
365 have_fast_tls
= tp1
&& tp1
== tp2
;
368 return have_fast_tls
;
375 emit_tls_get (guint8
*code
, int dreg
, int tls_offset
)
377 g_assert (v7_supported
);
378 ARM_MRC (code
, 15, 0, dreg
, 13, 0, 3);
379 ARM_LDR_IMM (code
, dreg
, dreg
, tls_offset
);
384 emit_tls_set (guint8
*code
, int sreg
, int tls_offset
)
386 int tp_reg
= (sreg
!= ARMREG_R0
) ? ARMREG_R0
: ARMREG_R1
;
387 g_assert (v7_supported
);
388 ARM_MRC (code
, 15, 0, tp_reg
, 13, 0, 3);
389 ARM_STR_IMM (code
, sreg
, tp_reg
, tls_offset
);
396 * Emit code to push an LMF structure on the LMF stack.
397 * On arm, this is intermixed with the initialization of other fields of the structure.
400 emit_save_lmf (MonoCompile
*cfg
, guint8
*code
, gint32 lmf_offset
)
404 if (mono_arch_have_fast_tls () && mono_tls_get_tls_offset (TLS_KEY_LMF_ADDR
) != -1) {
405 code
= emit_tls_get (code
, ARMREG_R0
, mono_tls_get_tls_offset (TLS_KEY_LMF_ADDR
));
407 mono_add_patch_info (cfg
, code
- cfg
->native_code
, MONO_PATCH_INFO_INTERNAL_METHOD
,
408 (gpointer
)"mono_tls_get_lmf_addr");
409 code
= emit_call_seq (cfg
, code
);
411 /* we build the MonoLMF structure on the stack - see mini-arm.h */
412 /* lmf_offset is the offset from the previous stack pointer,
413 * alloc_size is the total stack space allocated, so the offset
414 * of MonoLMF from the current stack ptr is alloc_size - lmf_offset.
415 * The pointer to the struct is put in r1 (new_lmf).
416 * ip is used as scratch
417 * The callee-saved registers are already in the MonoLMF structure
419 code
= emit_big_add (code
, ARMREG_R1
, ARMREG_SP
, lmf_offset
);
420 /* r0 is the result from mono_get_lmf_addr () */
421 ARM_STR_IMM (code
, ARMREG_R0
, ARMREG_R1
, MONO_STRUCT_OFFSET (MonoLMF
, lmf_addr
));
422 /* new_lmf->previous_lmf = *lmf_addr */
423 ARM_LDR_IMM (code
, ARMREG_IP
, ARMREG_R0
, MONO_STRUCT_OFFSET (MonoLMF
, previous_lmf
));
424 ARM_STR_IMM (code
, ARMREG_IP
, ARMREG_R1
, MONO_STRUCT_OFFSET (MonoLMF
, previous_lmf
));
425 /* *(lmf_addr) = r1 */
426 ARM_STR_IMM (code
, ARMREG_R1
, ARMREG_R0
, MONO_STRUCT_OFFSET (MonoLMF
, previous_lmf
));
427 /* Skip method (only needed for trampoline LMF frames) */
428 ARM_STR_IMM (code
, ARMREG_SP
, ARMREG_R1
, MONO_STRUCT_OFFSET (MonoLMF
, sp
));
429 ARM_STR_IMM (code
, ARMREG_FP
, ARMREG_R1
, MONO_STRUCT_OFFSET (MonoLMF
, fp
));
430 /* save the current IP */
431 ARM_MOV_REG_REG (code
, ARMREG_IP
, ARMREG_PC
);
432 ARM_STR_IMM (code
, ARMREG_IP
, ARMREG_R1
, MONO_STRUCT_OFFSET (MonoLMF
, ip
));
434 for (i
= 0; i
< sizeof (MonoLMF
); i
+= sizeof (mgreg_t
))
435 mini_gc_set_slot_type_from_fp (cfg
, lmf_offset
+ i
, SLOT_NOREF
);
446 emit_float_args (MonoCompile
*cfg
, MonoCallInst
*inst
, guint8
*code
, int *max_len
, guint
*offset
)
450 for (list
= inst
->float_args
; list
; list
= list
->next
) {
451 FloatArgData
*fad
= list
->data
;
452 MonoInst
*var
= get_vreg_to_inst (cfg
, fad
->vreg
);
453 gboolean imm
= arm_is_fpimm8 (var
->inst_offset
);
455 /* 4+1 insns for emit_big_add () and 1 for FLDS. */
461 if (*offset
+ *max_len
> cfg
->code_size
) {
462 cfg
->code_size
+= *max_len
;
463 cfg
->native_code
= g_realloc (cfg
->native_code
, cfg
->code_size
);
465 code
= cfg
->native_code
+ *offset
;
469 code
= emit_big_add (code
, ARMREG_LR
, var
->inst_basereg
, var
->inst_offset
);
470 ARM_FLDS (code
, fad
->hreg
, ARMREG_LR
, 0);
472 ARM_FLDS (code
, fad
->hreg
, var
->inst_basereg
, var
->inst_offset
);
474 *offset
= code
- cfg
->native_code
;
481 mono_arm_emit_vfp_scratch_save (MonoCompile
*cfg
, guint8
*code
, int reg
)
485 g_assert (reg
== vfp_scratch1
|| reg
== vfp_scratch2
);
487 inst
= (MonoInst
*) cfg
->arch
.vfp_scratch_slots
[reg
== vfp_scratch1
? 0 : 1];
490 if (!arm_is_fpimm8 (inst
->inst_offset
)) {
491 code
= emit_big_add (code
, ARMREG_LR
, inst
->inst_basereg
, inst
->inst_offset
);
492 ARM_FSTD (code
, reg
, ARMREG_LR
, 0);
494 ARM_FSTD (code
, reg
, inst
->inst_basereg
, inst
->inst_offset
);
501 mono_arm_emit_vfp_scratch_restore (MonoCompile
*cfg
, guint8
*code
, int reg
)
505 g_assert (reg
== vfp_scratch1
|| reg
== vfp_scratch2
);
507 inst
= (MonoInst
*) cfg
->arch
.vfp_scratch_slots
[reg
== vfp_scratch1
? 0 : 1];
510 if (!arm_is_fpimm8 (inst
->inst_offset
)) {
511 code
= emit_big_add (code
, ARMREG_LR
, inst
->inst_basereg
, inst
->inst_offset
);
512 ARM_FLDD (code
, reg
, ARMREG_LR
, 0);
514 ARM_FLDD (code
, reg
, inst
->inst_basereg
, inst
->inst_offset
);
523 * Emit code to pop an LMF structure from the LMF stack.
526 emit_restore_lmf (MonoCompile
*cfg
, guint8
*code
, gint32 lmf_offset
)
530 if (lmf_offset
< 32) {
531 basereg
= cfg
->frame_reg
;
536 code
= emit_big_add (code
, ARMREG_R2
, cfg
->frame_reg
, lmf_offset
);
539 /* ip = previous_lmf */
540 ARM_LDR_IMM (code
, ARMREG_IP
, basereg
, offset
+ MONO_STRUCT_OFFSET (MonoLMF
, previous_lmf
));
542 ARM_LDR_IMM (code
, ARMREG_LR
, basereg
, offset
+ MONO_STRUCT_OFFSET (MonoLMF
, lmf_addr
));
543 /* *(lmf_addr) = previous_lmf */
544 ARM_STR_IMM (code
, ARMREG_IP
, ARMREG_LR
, MONO_STRUCT_OFFSET (MonoLMF
, previous_lmf
));
549 #endif /* #ifndef DISABLE_JIT */
552 * mono_arch_get_argument_info:
553 * @csig: a method signature
554 * @param_count: the number of parameters to consider
555 * @arg_info: an array to store the result infos
557 * Gathers information on parameters such as size, alignment and
558 * padding. arg_info should be large enought to hold param_count + 1 entries.
560 * Returns the size of the activation frame.
563 mono_arch_get_argument_info (MonoMethodSignature
*csig
, int param_count
, MonoJitArgumentInfo
*arg_info
)
565 int k
, frame_size
= 0;
566 guint32 size
, align
, pad
;
570 t
= mini_get_underlying_type (csig
->ret
);
571 if (MONO_TYPE_ISSTRUCT (t
)) {
572 frame_size
+= sizeof (gpointer
);
576 arg_info
[0].offset
= offset
;
579 frame_size
+= sizeof (gpointer
);
583 arg_info
[0].size
= frame_size
;
585 for (k
= 0; k
< param_count
; k
++) {
586 size
= mini_type_stack_size_full (csig
->params
[k
], &align
, csig
->pinvoke
);
588 /* ignore alignment for now */
591 frame_size
+= pad
= (align
- (frame_size
& (align
- 1))) & (align
- 1);
592 arg_info
[k
].pad
= pad
;
594 arg_info
[k
+ 1].pad
= 0;
595 arg_info
[k
+ 1].size
= size
;
597 arg_info
[k
+ 1].offset
= offset
;
601 align
= MONO_ARCH_FRAME_ALIGNMENT
;
602 frame_size
+= pad
= (align
- (frame_size
& (align
- 1))) & (align
- 1);
603 arg_info
[k
].pad
= pad
;
608 #define MAX_ARCH_DELEGATE_PARAMS 3
611 get_delegate_invoke_impl (MonoTrampInfo
**info
, gboolean has_target
, gboolean param_count
)
613 guint8
*code
, *start
;
614 GSList
*unwind_ops
= mono_arch_get_cie_program ();
617 start
= code
= mono_global_codeman_reserve (12);
619 /* Replace the this argument with the target */
620 ARM_LDR_IMM (code
, ARMREG_IP
, ARMREG_R0
, MONO_STRUCT_OFFSET (MonoDelegate
, method_ptr
));
621 ARM_LDR_IMM (code
, ARMREG_R0
, ARMREG_R0
, MONO_STRUCT_OFFSET (MonoDelegate
, target
));
622 ARM_MOV_REG_REG (code
, ARMREG_PC
, ARMREG_IP
);
624 g_assert ((code
- start
) <= 12);
626 mono_arch_flush_icache (start
, 12);
630 size
= 8 + param_count
* 4;
631 start
= code
= mono_global_codeman_reserve (size
);
633 ARM_LDR_IMM (code
, ARMREG_IP
, ARMREG_R0
, MONO_STRUCT_OFFSET (MonoDelegate
, method_ptr
));
634 /* slide down the arguments */
635 for (i
= 0; i
< param_count
; ++i
) {
636 ARM_MOV_REG_REG (code
, (ARMREG_R0
+ i
), (ARMREG_R0
+ i
+ 1));
638 ARM_MOV_REG_REG (code
, ARMREG_PC
, ARMREG_IP
);
640 g_assert ((code
- start
) <= size
);
642 mono_arch_flush_icache (start
, size
);
646 *info
= mono_tramp_info_create ("delegate_invoke_impl_has_target", start
, code
- start
, NULL
, unwind_ops
);
648 char *name
= g_strdup_printf ("delegate_invoke_impl_target_%d", param_count
);
649 *info
= mono_tramp_info_create (name
, start
, code
- start
, NULL
, unwind_ops
);
653 MONO_PROFILER_RAISE (jit_code_buffer
, (start
, code
- start
, MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE
, NULL
));
659 * mono_arch_get_delegate_invoke_impls:
661 * Return a list of MonoAotTrampInfo structures for the delegate invoke impl
665 mono_arch_get_delegate_invoke_impls (void)
671 get_delegate_invoke_impl (&info
, TRUE
, 0);
672 res
= g_slist_prepend (res
, info
);
674 for (i
= 0; i
<= MAX_ARCH_DELEGATE_PARAMS
; ++i
) {
675 get_delegate_invoke_impl (&info
, FALSE
, i
);
676 res
= g_slist_prepend (res
, info
);
683 mono_arch_get_delegate_invoke_impl (MonoMethodSignature
*sig
, gboolean has_target
)
685 guint8
*code
, *start
;
688 /* FIXME: Support more cases */
689 sig_ret
= mini_get_underlying_type (sig
->ret
);
690 if (MONO_TYPE_ISSTRUCT (sig_ret
))
694 static guint8
* cached
= NULL
;
695 mono_mini_arch_lock ();
697 mono_mini_arch_unlock ();
702 start
= mono_aot_get_trampoline ("delegate_invoke_impl_has_target");
705 start
= get_delegate_invoke_impl (&info
, TRUE
, 0);
706 mono_tramp_info_register (info
, NULL
);
709 mono_mini_arch_unlock ();
712 static guint8
* cache
[MAX_ARCH_DELEGATE_PARAMS
+ 1] = {NULL
};
715 if (sig
->param_count
> MAX_ARCH_DELEGATE_PARAMS
)
717 for (i
= 0; i
< sig
->param_count
; ++i
)
718 if (!mono_is_regsize_var (sig
->params
[i
]))
721 mono_mini_arch_lock ();
722 code
= cache
[sig
->param_count
];
724 mono_mini_arch_unlock ();
729 char *name
= g_strdup_printf ("delegate_invoke_impl_target_%d", sig
->param_count
);
730 start
= mono_aot_get_trampoline (name
);
734 start
= get_delegate_invoke_impl (&info
, FALSE
, sig
->param_count
);
735 mono_tramp_info_register (info
, NULL
);
737 cache
[sig
->param_count
] = start
;
738 mono_mini_arch_unlock ();
746 mono_arch_get_delegate_virtual_invoke_impl (MonoMethodSignature
*sig
, MonoMethod
*method
, int offset
, gboolean load_imt_reg
)
752 mono_arch_get_this_arg_from_call (mgreg_t
*regs
, guint8
*code
)
754 return (gpointer
)regs
[ARMREG_R0
];
758 * Initialize the cpu to execute managed code.
761 mono_arch_cpu_init (void)
763 i8_align
= MONO_ABI_ALIGNOF (gint64
);
764 #ifdef MONO_CROSS_COMPILE
765 /* Need to set the alignment of i8 since it can different on the target */
766 #ifdef TARGET_ANDROID
768 mono_type_set_alignment (MONO_TYPE_I8
, i8_align
);
774 * Initialize architecture specific code.
777 mono_arch_init (void)
781 #ifdef TARGET_WATCHOS
782 mini_get_debug_options ()->soft_breakpoints
= TRUE
;
785 mono_os_mutex_init_recursive (&mini_arch_mutex
);
786 if (mini_get_debug_options ()->soft_breakpoints
) {
788 breakpoint_tramp
= mini_get_breakpoint_trampoline ();
790 ss_trigger_page
= mono_valloc (NULL
, mono_pagesize (), MONO_MMAP_READ
|MONO_MMAP_32BIT
, MONO_MEM_ACCOUNT_OTHER
);
791 bp_trigger_page
= mono_valloc (NULL
, mono_pagesize (), MONO_MMAP_READ
|MONO_MMAP_32BIT
, MONO_MEM_ACCOUNT_OTHER
);
792 mono_mprotect (bp_trigger_page
, mono_pagesize (), 0);
795 mono_aot_register_jit_icall ("mono_arm_throw_exception", mono_arm_throw_exception
);
796 mono_aot_register_jit_icall ("mono_arm_throw_exception_by_token", mono_arm_throw_exception_by_token
);
797 mono_aot_register_jit_icall ("mono_arm_resume_unwind", mono_arm_resume_unwind
);
798 #if defined(MONO_ARCH_GSHAREDVT_SUPPORTED)
799 mono_aot_register_jit_icall ("mono_arm_start_gsharedvt_call", mono_arm_start_gsharedvt_call
);
801 mono_aot_register_jit_icall ("mono_arm_unaligned_stack", mono_arm_unaligned_stack
);
802 #if defined(__ARM_EABI__)
803 eabi_supported
= TRUE
;
806 #if defined(ARM_FPU_VFP_HARD)
807 arm_fpu
= MONO_ARM_FPU_VFP_HARD
;
809 arm_fpu
= MONO_ARM_FPU_VFP
;
811 #if defined(ARM_FPU_NONE) && !defined(TARGET_IOS)
813 * If we're compiling with a soft float fallback and it
814 * turns out that no VFP unit is available, we need to
815 * switch to soft float. We don't do this for iOS, since
816 * iOS devices always have a VFP unit.
818 if (!mono_hwcap_arm_has_vfp
)
819 arm_fpu
= MONO_ARM_FPU_NONE
;
822 * This environment variable can be useful in testing
823 * environments to make sure the soft float fallback
824 * works. Most ARM devices have VFP units these days, so
825 * normally soft float code would not be exercised much.
827 char *soft
= g_getenv ("MONO_ARM_FORCE_SOFT_FLOAT");
829 if (soft
&& !strncmp (soft
, "1", 1))
830 arm_fpu
= MONO_ARM_FPU_NONE
;
835 v5_supported
= mono_hwcap_arm_is_v5
;
836 v6_supported
= mono_hwcap_arm_is_v6
;
837 v7_supported
= mono_hwcap_arm_is_v7
;
840 * On weird devices, the hwcap code may fail to detect
841 * the ARM version. In that case, we can at least safely
842 * assume the version the runtime was compiled for.
854 #if defined(TARGET_IOS)
855 /* iOS is special-cased here because we don't yet
856 have a way to properly detect CPU features on it. */
857 thumb_supported
= TRUE
;
860 thumb_supported
= mono_hwcap_arm_has_thumb
;
861 thumb2_supported
= mono_hwcap_arm_has_thumb2
;
864 /* Format: armv(5|6|7[s])[-thumb[2]] */
865 cpu_arch
= g_getenv ("MONO_CPU_ARCH");
867 /* Do this here so it overrides any detection. */
869 if (strncmp (cpu_arch
, "armv", 4) == 0) {
870 v5_supported
= cpu_arch
[4] >= '5';
871 v6_supported
= cpu_arch
[4] >= '6';
872 v7_supported
= cpu_arch
[4] >= '7';
873 v7s_supported
= strncmp (cpu_arch
, "armv7s", 6) == 0;
874 v7k_supported
= strncmp (cpu_arch
, "armv7k", 6) == 0;
877 thumb_supported
= strstr (cpu_arch
, "thumb") != NULL
;
878 thumb2_supported
= strstr (cpu_arch
, "thumb2") != NULL
;
884 * Cleanup architecture specific code.
887 mono_arch_cleanup (void)
892 * This function returns the optimizations supported on this cpu.
895 mono_arch_cpu_optimizations (guint32
*exclude_mask
)
897 /* no arm-specific optimizations yet */
903 * This function test for all SIMD functions supported.
905 * Returns a bitmask corresponding to all supported versions.
909 mono_arch_cpu_enumerate_simd_versions (void)
911 /* SIMD is currently unimplemented */
916 mono_arm_is_hard_float (void)
918 return arm_fpu
== MONO_ARM_FPU_VFP_HARD
;
924 mono_arch_opcode_needs_emulation (MonoCompile
*cfg
, int opcode
)
926 if (v7s_supported
|| v7k_supported
) {
940 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
942 mono_arch_is_soft_float (void)
944 return arm_fpu
== MONO_ARM_FPU_NONE
;
949 is_regsize_var (MonoType
*t
)
953 t
= mini_get_underlying_type (t
);
960 case MONO_TYPE_FNPTR
:
962 case MONO_TYPE_OBJECT
:
964 case MONO_TYPE_GENERICINST
:
965 if (!mono_type_generic_inst_is_valuetype (t
))
968 case MONO_TYPE_VALUETYPE
:
975 mono_arch_get_allocatable_int_vars (MonoCompile
*cfg
)
980 for (i
= 0; i
< cfg
->num_varinfo
; i
++) {
981 MonoInst
*ins
= cfg
->varinfo
[i
];
982 MonoMethodVar
*vmv
= MONO_VARINFO (cfg
, i
);
985 if (vmv
->range
.first_use
.abs_pos
>= vmv
->range
.last_use
.abs_pos
)
988 if (ins
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
) || (ins
->opcode
!= OP_LOCAL
&& ins
->opcode
!= OP_ARG
))
991 /* we can only allocate 32 bit values */
992 if (is_regsize_var (ins
->inst_vtype
)) {
993 g_assert (MONO_VARINFO (cfg
, i
)->reg
== -1);
994 g_assert (i
== vmv
->idx
);
995 vars
= mono_varlist_insert_sorted (cfg
, vars
, vmv
, FALSE
);
1003 mono_arch_get_global_int_regs (MonoCompile
*cfg
)
1007 mono_arch_compute_omit_fp (cfg
);
1010 * FIXME: Interface calls might go through a static rgctx trampoline which
1011 * sets V5, but it doesn't save it, so we need to save it ourselves, and
1014 if (cfg
->flags
& MONO_CFG_HAS_CALLS
)
1015 cfg
->uses_rgctx_reg
= TRUE
;
1017 if (cfg
->arch
.omit_fp
)
1018 regs
= g_list_prepend (regs
, GUINT_TO_POINTER (ARMREG_FP
));
1019 regs
= g_list_prepend (regs
, GUINT_TO_POINTER (ARMREG_V1
));
1020 regs
= g_list_prepend (regs
, GUINT_TO_POINTER (ARMREG_V2
));
1021 regs
= g_list_prepend (regs
, GUINT_TO_POINTER (ARMREG_V3
));
1023 /* V4=R7 is used as a frame pointer, but V7=R10 is preserved */
1024 regs
= g_list_prepend (regs
, GUINT_TO_POINTER (ARMREG_V7
));
1026 regs
= g_list_prepend (regs
, GUINT_TO_POINTER (ARMREG_V4
));
1027 if (!(cfg
->compile_aot
|| cfg
->uses_rgctx_reg
|| COMPILE_LLVM (cfg
)))
1028 /* V5 is reserved for passing the vtable/rgctx/IMT method */
1029 regs
= g_list_prepend (regs
, GUINT_TO_POINTER (ARMREG_V5
));
1030 /*regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V6));*/
1031 /*regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V7));*/
1037 * mono_arch_regalloc_cost:
1039 * Return the cost, in number of memory references, of the action of
1040 * allocating the variable VMV into a register during global register
1044 mono_arch_regalloc_cost (MonoCompile
*cfg
, MonoMethodVar
*vmv
)
1050 #endif /* #ifndef DISABLE_JIT */
1053 mono_arch_flush_icache (guint8
*code
, gint size
)
1055 #if defined(MONO_CROSS_COMPILE)
1057 sys_icache_invalidate (code
, size
);
1059 __builtin___clear_cache (code
, code
+ size
);
1066 add_general (guint
*gr
, guint
*stack_size
, ArgInfo
*ainfo
, gboolean simple
)
1069 if (*gr
> ARMREG_R3
) {
1071 ainfo
->offset
= *stack_size
;
1072 ainfo
->reg
= ARMREG_SP
; /* in the caller */
1073 ainfo
->storage
= RegTypeBase
;
1076 ainfo
->storage
= RegTypeGeneral
;
1083 split
= i8_align
== 4;
1088 if (*gr
== ARMREG_R3
&& split
) {
1089 /* first word in r3 and the second on the stack */
1090 ainfo
->offset
= *stack_size
;
1091 ainfo
->reg
= ARMREG_SP
; /* in the caller */
1092 ainfo
->storage
= RegTypeBaseGen
;
1094 } else if (*gr
>= ARMREG_R3
) {
1095 if (eabi_supported
) {
1096 /* darwin aligns longs to 4 byte only */
1097 if (i8_align
== 8) {
1102 ainfo
->offset
= *stack_size
;
1103 ainfo
->reg
= ARMREG_SP
; /* in the caller */
1104 ainfo
->storage
= RegTypeBase
;
1107 if (eabi_supported
) {
1108 if (i8_align
== 8 && ((*gr
) & 1))
1111 ainfo
->storage
= RegTypeIRegPair
;
1120 add_float (guint
*fpr
, guint
*stack_size
, ArgInfo
*ainfo
, gboolean is_double
, gint
*float_spare
)
1123 * If we're calling a function like this:
1125 * void foo(float a, double b, float c)
1127 * We pass a in s0 and b in d1. That leaves us
1128 * with s1 being unused. The armhf ABI recognizes
1129 * this and requires register assignment to then
1130 * use that for the next single-precision arg,
1131 * i.e. c in this example. So float_spare either
1132 * tells us which reg to use for the next single-
1133 * precision arg, or it's -1, meaning use *fpr.
1135 * Note that even though most of the JIT speaks
1136 * double-precision, fpr represents single-
1137 * precision registers.
1139 * See parts 5.5 and 6.1.2 of the AAPCS for how
1143 if (*fpr
< ARM_VFP_F16
|| (!is_double
&& *float_spare
>= 0)) {
1144 ainfo
->storage
= RegTypeFP
;
1148 * If we're passing a double-precision value
1149 * and *fpr is odd (e.g. it's s1, s3, ...)
1150 * we need to use the next even register. So
1151 * we mark the current *fpr as a spare that
1152 * can be used for the next single-precision
1156 *float_spare
= *fpr
;
1161 * At this point, we have an even register
1162 * so we assign that and move along.
1166 } else if (*float_spare
>= 0) {
1168 * We're passing a single-precision value
1169 * and it looks like a spare single-
1170 * precision register is available. Let's
1174 ainfo
->reg
= *float_spare
;
1178 * If we hit this branch, we're passing a
1179 * single-precision value and we can simply
1180 * use the next available register.
1188 * We've exhausted available floating point
1189 * regs, so pass the rest on the stack.
1197 ainfo
->offset
= *stack_size
;
1198 ainfo
->reg
= ARMREG_SP
;
1199 ainfo
->storage
= RegTypeBase
;
1206 is_hfa (MonoType
*t
, int *out_nfields
, int *out_esize
)
1210 MonoClassField
*field
;
1211 MonoType
*ftype
, *prev_ftype
= NULL
;
1214 klass
= mono_class_from_mono_type (t
);
1216 while ((field
= mono_class_get_fields (klass
, &iter
))) {
1217 if (field
->type
->attrs
& FIELD_ATTRIBUTE_STATIC
)
1219 ftype
= mono_field_get_type (field
);
1220 ftype
= mini_get_underlying_type (ftype
);
1222 if (MONO_TYPE_ISSTRUCT (ftype
)) {
1223 int nested_nfields
, nested_esize
;
1225 if (!is_hfa (ftype
, &nested_nfields
, &nested_esize
))
1227 if (nested_esize
== 4)
1228 ftype
= &mono_defaults
.single_class
->byval_arg
;
1230 ftype
= &mono_defaults
.double_class
->byval_arg
;
1231 if (prev_ftype
&& prev_ftype
->type
!= ftype
->type
)
1234 nfields
+= nested_nfields
;
1236 if (!(!ftype
->byref
&& (ftype
->type
== MONO_TYPE_R4
|| ftype
->type
== MONO_TYPE_R8
)))
1238 if (prev_ftype
&& prev_ftype
->type
!= ftype
->type
)
1244 if (nfields
== 0 || nfields
> 4)
1246 *out_nfields
= nfields
;
1247 *out_esize
= prev_ftype
->type
== MONO_TYPE_R4
? 4 : 8;
1252 get_call_info (MonoMemPool
*mp
, MonoMethodSignature
*sig
)
1254 guint i
, gr
, fpr
, pstart
;
1256 int n
= sig
->hasthis
+ sig
->param_count
;
1260 guint32 stack_size
= 0;
1262 gboolean is_pinvoke
= sig
->pinvoke
;
1263 gboolean vtype_retaddr
= FALSE
;
1266 cinfo
= mono_mempool_alloc0 (mp
, sizeof (CallInfo
) + (sizeof (ArgInfo
) * n
));
1268 cinfo
= g_malloc0 (sizeof (CallInfo
) + (sizeof (ArgInfo
) * n
));
1275 t
= mini_get_underlying_type (sig
->ret
);
1286 case MONO_TYPE_FNPTR
:
1287 case MONO_TYPE_OBJECT
:
1288 cinfo
->ret
.storage
= RegTypeGeneral
;
1289 cinfo
->ret
.reg
= ARMREG_R0
;
1293 cinfo
->ret
.storage
= RegTypeIRegPair
;
1294 cinfo
->ret
.reg
= ARMREG_R0
;
1298 cinfo
->ret
.storage
= RegTypeFP
;
1300 if (t
->type
== MONO_TYPE_R4
)
1301 cinfo
->ret
.size
= 4;
1303 cinfo
->ret
.size
= 8;
1305 if (IS_HARD_FLOAT
) {
1306 cinfo
->ret
.reg
= ARM_VFP_F0
;
1308 cinfo
->ret
.reg
= ARMREG_R0
;
1311 case MONO_TYPE_GENERICINST
:
1312 if (!mono_type_generic_inst_is_valuetype (t
)) {
1313 cinfo
->ret
.storage
= RegTypeGeneral
;
1314 cinfo
->ret
.reg
= ARMREG_R0
;
1317 if (mini_is_gsharedvt_variable_type (t
)) {
1318 cinfo
->ret
.storage
= RegTypeStructByAddr
;
1322 case MONO_TYPE_VALUETYPE
:
1323 case MONO_TYPE_TYPEDBYREF
:
1324 if (IS_HARD_FLOAT
&& sig
->pinvoke
&& is_hfa (t
, &nfields
, &esize
)) {
1325 cinfo
->ret
.storage
= RegTypeHFA
;
1327 cinfo
->ret
.nregs
= nfields
;
1328 cinfo
->ret
.esize
= esize
;
1331 int native_size
= mono_class_native_size (mono_class_from_mono_type (t
), &align
);
1334 #ifdef TARGET_WATCHOS
1339 if (native_size
<= max_size
) {
1340 cinfo
->ret
.storage
= RegTypeStructByVal
;
1341 cinfo
->ret
.struct_size
= native_size
;
1342 cinfo
->ret
.nregs
= ALIGN_TO (native_size
, 4) / 4;
1344 cinfo
->ret
.storage
= RegTypeStructByAddr
;
1347 cinfo
->ret
.storage
= RegTypeStructByAddr
;
1352 case MONO_TYPE_MVAR
:
1353 g_assert (mini_is_gsharedvt_type (t
));
1354 cinfo
->ret
.storage
= RegTypeStructByAddr
;
1356 case MONO_TYPE_VOID
:
1359 g_error ("Can't handle as return value 0x%x", sig
->ret
->type
);
1362 vtype_retaddr
= cinfo
->ret
.storage
== RegTypeStructByAddr
;
1367 * To simplify get_this_arg_reg () and LLVM integration, emit the vret arg after
1368 * the first argument, allowing 'this' to be always passed in the first arg reg.
1369 * Also do this if the first argument is a reference type, since virtual calls
1370 * are sometimes made using calli without sig->hasthis set, like in the delegate
1373 if (vtype_retaddr
&& !is_pinvoke
&& (sig
->hasthis
|| (sig
->param_count
> 0 && MONO_TYPE_IS_REFERENCE (mini_get_underlying_type (sig
->params
[0]))))) {
1375 add_general (&gr
, &stack_size
, cinfo
->args
+ 0, TRUE
);
1377 add_general (&gr
, &stack_size
, &cinfo
->args
[sig
->hasthis
+ 0], TRUE
);
1381 cinfo
->ret
.reg
= gr
;
1383 cinfo
->vret_arg_index
= 1;
1387 add_general (&gr
, &stack_size
, cinfo
->args
+ 0, TRUE
);
1390 if (vtype_retaddr
) {
1391 cinfo
->ret
.reg
= gr
;
1396 DEBUG(g_print("params: %d\n", sig
->param_count
));
1397 for (i
= pstart
; i
< sig
->param_count
; ++i
) {
1398 ArgInfo
*ainfo
= &cinfo
->args
[n
];
1400 if ((sig
->call_convention
== MONO_CALL_VARARG
) && (i
== sig
->sentinelpos
)) {
1401 /* Prevent implicit arguments and sig_cookie from
1402 being passed in registers */
1405 /* Emit the signature cookie just before the implicit arguments */
1406 add_general (&gr
, &stack_size
, &cinfo
->sig_cookie
, TRUE
);
1408 DEBUG(g_print("param %d: ", i
));
1409 if (sig
->params
[i
]->byref
) {
1410 DEBUG(g_print("byref\n"));
1411 add_general (&gr
, &stack_size
, ainfo
, TRUE
);
1415 t
= mini_get_underlying_type (sig
->params
[i
]);
1419 cinfo
->args
[n
].size
= 1;
1420 add_general (&gr
, &stack_size
, ainfo
, TRUE
);
1424 cinfo
->args
[n
].size
= 2;
1425 add_general (&gr
, &stack_size
, ainfo
, TRUE
);
1429 cinfo
->args
[n
].size
= 4;
1430 add_general (&gr
, &stack_size
, ainfo
, TRUE
);
1435 case MONO_TYPE_FNPTR
:
1436 case MONO_TYPE_OBJECT
:
1437 cinfo
->args
[n
].size
= sizeof (gpointer
);
1438 add_general (&gr
, &stack_size
, ainfo
, TRUE
);
1440 case MONO_TYPE_GENERICINST
:
1441 if (!mono_type_generic_inst_is_valuetype (t
)) {
1442 cinfo
->args
[n
].size
= sizeof (gpointer
);
1443 add_general (&gr
, &stack_size
, ainfo
, TRUE
);
1446 if (mini_is_gsharedvt_variable_type (t
)) {
1447 /* gsharedvt arguments are passed by ref */
1448 g_assert (mini_is_gsharedvt_type (t
));
1449 add_general (&gr
, &stack_size
, ainfo
, TRUE
);
1450 switch (ainfo
->storage
) {
1451 case RegTypeGeneral
:
1452 ainfo
->storage
= RegTypeGSharedVtInReg
;
1455 ainfo
->storage
= RegTypeGSharedVtOnStack
;
1458 g_assert_not_reached ();
1463 case MONO_TYPE_TYPEDBYREF
:
1464 case MONO_TYPE_VALUETYPE
: {
1467 int nwords
, nfields
, esize
;
1470 if (IS_HARD_FLOAT
&& sig
->pinvoke
&& is_hfa (t
, &nfields
, &esize
)) {
1471 if (fpr
+ nfields
< ARM_VFP_F16
) {
1472 ainfo
->storage
= RegTypeHFA
;
1474 ainfo
->nregs
= nfields
;
1475 ainfo
->esize
= esize
;
1486 if (t
->type
== MONO_TYPE_TYPEDBYREF
) {
1487 size
= sizeof (MonoTypedRef
);
1488 align
= sizeof (gpointer
);
1490 MonoClass
*klass
= mono_class_from_mono_type (sig
->params
[i
]);
1492 size
= mono_class_native_size (klass
, &align
);
1494 size
= mini_type_stack_size_full (t
, &align
, FALSE
);
1496 DEBUG(g_print ("load %d bytes struct\n", size
));
1498 #ifdef TARGET_WATCHOS
1499 /* Watchos pass large structures by ref */
1500 /* We only do this for pinvoke to make gsharedvt/dyncall simpler */
1501 if (sig
->pinvoke
&& size
> 16) {
1502 add_general (&gr
, &stack_size
, ainfo
, TRUE
);
1503 switch (ainfo
->storage
) {
1504 case RegTypeGeneral
:
1505 ainfo
->storage
= RegTypeStructByAddr
;
1508 ainfo
->storage
= RegTypeStructByAddrOnStack
;
1511 g_assert_not_reached ();
1520 align_size
+= (sizeof (gpointer
) - 1);
1521 align_size
&= ~(sizeof (gpointer
) - 1);
1522 nwords
= (align_size
+ sizeof (gpointer
) -1 ) / sizeof (gpointer
);
1523 ainfo
->storage
= RegTypeStructByVal
;
1524 ainfo
->struct_size
= size
;
1525 ainfo
->align
= align
;
1526 /* FIXME: align stack_size if needed */
1527 if (eabi_supported
) {
1528 if (align
>= 8 && (gr
& 1))
1531 if (gr
> ARMREG_R3
) {
1533 ainfo
->vtsize
= nwords
;
1535 int rest
= ARMREG_R3
- gr
+ 1;
1536 int n_in_regs
= rest
>= nwords
? nwords
: rest
;
1538 ainfo
->size
= n_in_regs
;
1539 ainfo
->vtsize
= nwords
- n_in_regs
;
1542 nwords
-= n_in_regs
;
1544 if (sig
->call_convention
== MONO_CALL_VARARG
)
1545 /* This matches the alignment in mono_ArgIterator_IntGetNextArg () */
1546 stack_size
= ALIGN_TO (stack_size
, align
);
1547 ainfo
->offset
= stack_size
;
1548 /*g_print ("offset for arg %d at %d\n", n, stack_size);*/
1549 stack_size
+= nwords
* sizeof (gpointer
);
1555 add_general (&gr
, &stack_size
, ainfo
, FALSE
);
1561 add_float (&fpr
, &stack_size
, ainfo
, FALSE
, &float_spare
);
1563 add_general (&gr
, &stack_size
, ainfo
, TRUE
);
1569 add_float (&fpr
, &stack_size
, ainfo
, TRUE
, &float_spare
);
1571 add_general (&gr
, &stack_size
, ainfo
, FALSE
);
1574 case MONO_TYPE_MVAR
:
1575 /* gsharedvt arguments are passed by ref */
1576 g_assert (mini_is_gsharedvt_type (t
));
1577 add_general (&gr
, &stack_size
, ainfo
, TRUE
);
1578 switch (ainfo
->storage
) {
1579 case RegTypeGeneral
:
1580 ainfo
->storage
= RegTypeGSharedVtInReg
;
1583 ainfo
->storage
= RegTypeGSharedVtOnStack
;
1586 g_assert_not_reached ();
1590 g_error ("Can't handle 0x%x", sig
->params
[i
]->type
);
1595 /* Handle the case where there are no implicit arguments */
1596 if ((sig
->call_convention
== MONO_CALL_VARARG
) && (i
== sig
->sentinelpos
)) {
1597 /* Prevent implicit arguments and sig_cookie from
1598 being passed in registers */
1601 /* Emit the signature cookie just before the implicit arguments */
1602 add_general (&gr
, &stack_size
, &cinfo
->sig_cookie
, TRUE
);
1605 /* align stack size to 8 */
1606 DEBUG (g_print (" stack size: %d (%d)\n", (stack_size
+ 15) & ~15, stack_size
));
1607 stack_size
= (stack_size
+ 7) & ~7;
1609 cinfo
->stack_usage
= stack_size
;
1615 mono_arch_tail_call_supported (MonoCompile
*cfg
, MonoMethodSignature
*caller_sig
, MonoMethodSignature
*callee_sig
)
1617 MonoType
*callee_ret
;
1621 c1
= get_call_info (NULL
, caller_sig
);
1622 c2
= get_call_info (NULL
, callee_sig
);
1625 * Tail calls with more callee stack usage than the caller cannot be supported, since
1626 * the extra stack space would be left on the stack after the tail call.
1628 res
= c1
->stack_usage
>= c2
->stack_usage
;
1629 callee_ret
= mini_get_underlying_type (callee_sig
->ret
);
1630 if (callee_ret
&& MONO_TYPE_ISSTRUCT (callee_ret
) && c2
->ret
.storage
!= RegTypeStructByVal
)
1631 /* An address on the callee's stack is passed as the first argument */
1634 if (c2
->stack_usage
> 16 * 4)
1646 debug_omit_fp (void)
1649 return mono_debug_count ();
1656 * mono_arch_compute_omit_fp:
1657 * Determine whether the frame pointer can be eliminated.
1660 mono_arch_compute_omit_fp (MonoCompile
*cfg
)
1662 MonoMethodSignature
*sig
;
1663 MonoMethodHeader
*header
;
1667 if (cfg
->arch
.omit_fp_computed
)
1670 header
= cfg
->header
;
1672 sig
= mono_method_signature (cfg
->method
);
1674 if (!cfg
->arch
.cinfo
)
1675 cfg
->arch
.cinfo
= get_call_info (cfg
->mempool
, sig
);
1676 cinfo
= cfg
->arch
.cinfo
;
1679 * FIXME: Remove some of the restrictions.
1681 cfg
->arch
.omit_fp
= TRUE
;
1682 cfg
->arch
.omit_fp_computed
= TRUE
;
1684 if (cfg
->disable_omit_fp
)
1685 cfg
->arch
.omit_fp
= FALSE
;
1686 if (!debug_omit_fp ())
1687 cfg
->arch
.omit_fp
= FALSE
;
1689 if (cfg->method->save_lmf)
1690 cfg->arch.omit_fp = FALSE;
1692 if (cfg
->flags
& MONO_CFG_HAS_ALLOCA
)
1693 cfg
->arch
.omit_fp
= FALSE
;
1694 if (header
->num_clauses
)
1695 cfg
->arch
.omit_fp
= FALSE
;
1696 if (cfg
->param_area
)
1697 cfg
->arch
.omit_fp
= FALSE
;
1698 if (!sig
->pinvoke
&& (sig
->call_convention
== MONO_CALL_VARARG
))
1699 cfg
->arch
.omit_fp
= FALSE
;
1700 if ((mono_jit_trace_calls
!= NULL
&& mono_trace_eval (cfg
->method
)))
1701 cfg
->arch
.omit_fp
= FALSE
;
1702 for (i
= 0; i
< sig
->param_count
+ sig
->hasthis
; ++i
) {
1703 ArgInfo
*ainfo
= &cinfo
->args
[i
];
1705 if (ainfo
->storage
== RegTypeBase
|| ainfo
->storage
== RegTypeBaseGen
|| ainfo
->storage
== RegTypeStructByVal
) {
1707 * The stack offset can only be determined when the frame
1710 cfg
->arch
.omit_fp
= FALSE
;
1715 for (i
= cfg
->locals_start
; i
< cfg
->num_varinfo
; i
++) {
1716 MonoInst
*ins
= cfg
->varinfo
[i
];
1719 locals_size
+= mono_type_size (ins
->inst_vtype
, &ialign
);
1724 * Set var information according to the calling convention. arm version.
1725 * The locals var stuff should most likely be split in another method.
1728 mono_arch_allocate_vars (MonoCompile
*cfg
)
1730 MonoMethodSignature
*sig
;
1731 MonoMethodHeader
*header
;
1734 int i
, offset
, size
, align
, curinst
;
1739 sig
= mono_method_signature (cfg
->method
);
1741 if (!cfg
->arch
.cinfo
)
1742 cfg
->arch
.cinfo
= get_call_info (cfg
->mempool
, sig
);
1743 cinfo
= cfg
->arch
.cinfo
;
1744 sig_ret
= mini_get_underlying_type (sig
->ret
);
1746 mono_arch_compute_omit_fp (cfg
);
1748 if (cfg
->arch
.omit_fp
)
1749 cfg
->frame_reg
= ARMREG_SP
;
1751 cfg
->frame_reg
= ARMREG_FP
;
1753 cfg
->flags
|= MONO_CFG_HAS_SPILLUP
;
1755 /* allow room for the vararg method args: void* and long/double */
1756 if (mono_jit_trace_calls
!= NULL
&& mono_trace_eval (cfg
->method
))
1757 cfg
->param_area
= MAX (cfg
->param_area
, sizeof (gpointer
)*8);
1759 header
= cfg
->header
;
1761 /* See mono_arch_get_global_int_regs () */
1762 if (cfg
->flags
& MONO_CFG_HAS_CALLS
)
1763 cfg
->uses_rgctx_reg
= TRUE
;
1765 if (cfg
->frame_reg
!= ARMREG_SP
)
1766 cfg
->used_int_regs
|= 1 << cfg
->frame_reg
;
1768 if (cfg
->compile_aot
|| cfg
->uses_rgctx_reg
|| COMPILE_LLVM (cfg
))
1769 /* V5 is reserved for passing the vtable/rgctx/IMT method */
1770 cfg
->used_int_regs
|= (1 << MONO_ARCH_IMT_REG
);
1774 if (!MONO_TYPE_ISSTRUCT (sig_ret
) && cinfo
->ret
.storage
!= RegTypeStructByAddr
) {
1775 if (sig_ret
->type
!= MONO_TYPE_VOID
) {
1776 cfg
->ret
->opcode
= OP_REGVAR
;
1777 cfg
->ret
->inst_c0
= ARMREG_R0
;
1780 /* local vars are at a positive offset from the stack pointer */
1782 * also note that if the function uses alloca, we use FP
1783 * to point at the local variables.
1785 offset
= 0; /* linkage area */
1786 /* align the offset to 16 bytes: not sure this is needed here */
1788 //offset &= ~(8 - 1);
1790 /* add parameter area size for called functions */
1791 offset
+= cfg
->param_area
;
1794 if (cfg
->flags
& MONO_CFG_HAS_FPOUT
)
1797 /* allow room to save the return value */
1798 if (mono_jit_trace_calls
!= NULL
&& mono_trace_eval (cfg
->method
))
1801 switch (cinfo
->ret
.storage
) {
1802 case RegTypeStructByVal
:
1804 /* Allocate a local to hold the result, the epilog will copy it to the correct place */
1805 offset
= ALIGN_TO (offset
, 8);
1806 cfg
->ret
->opcode
= OP_REGOFFSET
;
1807 cfg
->ret
->inst_basereg
= cfg
->frame_reg
;
1808 cfg
->ret
->inst_offset
= offset
;
1809 if (cinfo
->ret
.storage
== RegTypeStructByVal
)
1810 offset
+= cinfo
->ret
.nregs
* sizeof (gpointer
);
1814 case RegTypeStructByAddr
:
1815 ins
= cfg
->vret_addr
;
1816 offset
+= sizeof(gpointer
) - 1;
1817 offset
&= ~(sizeof(gpointer
) - 1);
1818 ins
->inst_offset
= offset
;
1819 ins
->opcode
= OP_REGOFFSET
;
1820 ins
->inst_basereg
= cfg
->frame_reg
;
1821 if (G_UNLIKELY (cfg
->verbose_level
> 1)) {
1822 g_print ("vret_addr =");
1823 mono_print_ins (cfg
->vret_addr
);
1825 offset
+= sizeof(gpointer
);
1831 /* Allocate these first so they have a small offset, OP_SEQ_POINT depends on this */
1832 if (cfg
->arch
.seq_point_info_var
) {
1835 ins
= cfg
->arch
.seq_point_info_var
;
1839 offset
+= align
- 1;
1840 offset
&= ~(align
- 1);
1841 ins
->opcode
= OP_REGOFFSET
;
1842 ins
->inst_basereg
= cfg
->frame_reg
;
1843 ins
->inst_offset
= offset
;
1846 if (cfg
->arch
.ss_trigger_page_var
) {
1849 ins
= cfg
->arch
.ss_trigger_page_var
;
1852 offset
+= align
- 1;
1853 offset
&= ~(align
- 1);
1854 ins
->opcode
= OP_REGOFFSET
;
1855 ins
->inst_basereg
= cfg
->frame_reg
;
1856 ins
->inst_offset
= offset
;
1860 if (cfg
->arch
.seq_point_ss_method_var
) {
1863 ins
= cfg
->arch
.seq_point_ss_method_var
;
1866 offset
+= align
- 1;
1867 offset
&= ~(align
- 1);
1868 ins
->opcode
= OP_REGOFFSET
;
1869 ins
->inst_basereg
= cfg
->frame_reg
;
1870 ins
->inst_offset
= offset
;
1873 if (cfg
->arch
.seq_point_bp_method_var
) {
1876 ins
= cfg
->arch
.seq_point_bp_method_var
;
1879 offset
+= align
- 1;
1880 offset
&= ~(align
- 1);
1881 ins
->opcode
= OP_REGOFFSET
;
1882 ins
->inst_basereg
= cfg
->frame_reg
;
1883 ins
->inst_offset
= offset
;
1887 if (cfg
->has_atomic_exchange_i4
|| cfg
->has_atomic_cas_i4
|| cfg
->has_atomic_add_i4
) {
1888 /* Allocate a temporary used by the atomic ops */
1892 /* Allocate a local slot to hold the sig cookie address */
1893 offset
+= align
- 1;
1894 offset
&= ~(align
- 1);
1895 cfg
->arch
.atomic_tmp_offset
= offset
;
1898 cfg
->arch
.atomic_tmp_offset
= -1;
1901 cfg
->locals_min_stack_offset
= offset
;
1903 curinst
= cfg
->locals_start
;
1904 for (i
= curinst
; i
< cfg
->num_varinfo
; ++i
) {
1907 ins
= cfg
->varinfo
[i
];
1908 if ((ins
->flags
& MONO_INST_IS_DEAD
) || ins
->opcode
== OP_REGVAR
|| ins
->opcode
== OP_REGOFFSET
)
1911 t
= ins
->inst_vtype
;
1912 if (cfg
->gsharedvt
&& mini_is_gsharedvt_variable_type (t
))
1915 /* inst->backend.is_pinvoke indicates native sized value types, this is used by the
1916 * pinvoke wrappers when they call functions returning structure */
1917 if (ins
->backend
.is_pinvoke
&& MONO_TYPE_ISSTRUCT (t
) && t
->type
!= MONO_TYPE_TYPEDBYREF
) {
1918 size
= mono_class_native_size (mono_class_from_mono_type (t
), &ualign
);
1922 size
= mono_type_size (t
, &align
);
1924 /* FIXME: if a structure is misaligned, our memcpy doesn't work,
1925 * since it loads/stores misaligned words, which don't do the right thing.
1927 if (align
< 4 && size
>= 4)
1929 if (ALIGN_TO (offset
, align
) > ALIGN_TO (offset
, 4))
1930 mini_gc_set_slot_type_from_fp (cfg
, ALIGN_TO (offset
, 4), SLOT_NOREF
);
1931 offset
+= align
- 1;
1932 offset
&= ~(align
- 1);
1933 ins
->opcode
= OP_REGOFFSET
;
1934 ins
->inst_offset
= offset
;
1935 ins
->inst_basereg
= cfg
->frame_reg
;
1937 //g_print ("allocating local %d to %d\n", i, inst->inst_offset);
1940 cfg
->locals_max_stack_offset
= offset
;
1944 ins
= cfg
->args
[curinst
];
1945 if (ins
->opcode
!= OP_REGVAR
) {
1946 ins
->opcode
= OP_REGOFFSET
;
1947 ins
->inst_basereg
= cfg
->frame_reg
;
1948 offset
+= sizeof (gpointer
) - 1;
1949 offset
&= ~(sizeof (gpointer
) - 1);
1950 ins
->inst_offset
= offset
;
1951 offset
+= sizeof (gpointer
);
1956 if (sig
->call_convention
== MONO_CALL_VARARG
) {
1960 /* Allocate a local slot to hold the sig cookie address */
1961 offset
+= align
- 1;
1962 offset
&= ~(align
- 1);
1963 cfg
->sig_cookie
= offset
;
1967 for (i
= 0; i
< sig
->param_count
; ++i
) {
1968 ainfo
= cinfo
->args
+ i
;
1970 ins
= cfg
->args
[curinst
];
1972 switch (ainfo
->storage
) {
1974 offset
= ALIGN_TO (offset
, 8);
1975 ins
->opcode
= OP_REGOFFSET
;
1976 ins
->inst_basereg
= cfg
->frame_reg
;
1977 /* These arguments are saved to the stack in the prolog */
1978 ins
->inst_offset
= offset
;
1979 if (cfg
->verbose_level
>= 2)
1980 g_print ("arg %d allocated to %s+0x%0x.\n", i
, mono_arch_regname (ins
->inst_basereg
), (int)ins
->inst_offset
);
1988 if (ins
->opcode
!= OP_REGVAR
) {
1989 ins
->opcode
= OP_REGOFFSET
;
1990 ins
->inst_basereg
= cfg
->frame_reg
;
1991 size
= mini_type_stack_size_full (sig
->params
[i
], &ualign
, sig
->pinvoke
);
1993 /* FIXME: if a structure is misaligned, our memcpy doesn't work,
1994 * since it loads/stores misaligned words, which don't do the right thing.
1996 if (align
< 4 && size
>= 4)
1998 /* The code in the prolog () stores words when storing vtypes received in a register */
1999 if (MONO_TYPE_ISSTRUCT (sig
->params
[i
]))
2001 if (ALIGN_TO (offset
, align
) > ALIGN_TO (offset
, 4))
2002 mini_gc_set_slot_type_from_fp (cfg
, ALIGN_TO (offset
, 4), SLOT_NOREF
);
2003 offset
+= align
- 1;
2004 offset
&= ~(align
- 1);
2005 ins
->inst_offset
= offset
;
2011 /* align the offset to 8 bytes */
2012 if (ALIGN_TO (offset
, 8) > ALIGN_TO (offset
, 4))
2013 mini_gc_set_slot_type_from_fp (cfg
, ALIGN_TO (offset
, 4), SLOT_NOREF
);
2018 cfg
->stack_offset
= offset
;
2022 mono_arch_create_vars (MonoCompile
*cfg
)
2024 MonoMethodSignature
*sig
;
2028 sig
= mono_method_signature (cfg
->method
);
2030 if (!cfg
->arch
.cinfo
)
2031 cfg
->arch
.cinfo
= get_call_info (cfg
->mempool
, sig
);
2032 cinfo
= cfg
->arch
.cinfo
;
2034 if (IS_HARD_FLOAT
) {
2035 for (i
= 0; i
< 2; i
++) {
2036 MonoInst
*inst
= mono_compile_create_var (cfg
, &mono_defaults
.double_class
->byval_arg
, OP_LOCAL
);
2037 inst
->flags
|= MONO_INST_VOLATILE
;
2039 cfg
->arch
.vfp_scratch_slots
[i
] = (gpointer
) inst
;
2043 if (cinfo
->ret
.storage
== RegTypeStructByVal
)
2044 cfg
->ret_var_is_local
= TRUE
;
2046 if (cinfo
->ret
.storage
== RegTypeStructByAddr
) {
2047 cfg
->vret_addr
= mono_compile_create_var (cfg
, &mono_defaults
.int_class
->byval_arg
, OP_ARG
);
2048 if (G_UNLIKELY (cfg
->verbose_level
> 1)) {
2049 g_print ("vret_addr = ");
2050 mono_print_ins (cfg
->vret_addr
);
2054 if (cfg
->gen_sdb_seq_points
) {
2055 if (cfg
->compile_aot
) {
2056 MonoInst
*ins
= mono_compile_create_var (cfg
, &mono_defaults
.int_class
->byval_arg
, OP_LOCAL
);
2057 ins
->flags
|= MONO_INST_VOLATILE
;
2058 cfg
->arch
.seq_point_info_var
= ins
;
2060 if (!cfg
->soft_breakpoints
) {
2061 /* Allocate a separate variable for this to save 1 load per seq point */
2062 ins
= mono_compile_create_var (cfg
, &mono_defaults
.int_class
->byval_arg
, OP_LOCAL
);
2063 ins
->flags
|= MONO_INST_VOLATILE
;
2064 cfg
->arch
.ss_trigger_page_var
= ins
;
2067 if (cfg
->soft_breakpoints
) {
2070 ins
= mono_compile_create_var (cfg
, &mono_defaults
.int_class
->byval_arg
, OP_LOCAL
);
2071 ins
->flags
|= MONO_INST_VOLATILE
;
2072 cfg
->arch
.seq_point_ss_method_var
= ins
;
2074 ins
= mono_compile_create_var (cfg
, &mono_defaults
.int_class
->byval_arg
, OP_LOCAL
);
2075 ins
->flags
|= MONO_INST_VOLATILE
;
2076 cfg
->arch
.seq_point_bp_method_var
= ins
;
2082 emit_sig_cookie (MonoCompile
*cfg
, MonoCallInst
*call
, CallInfo
*cinfo
)
2084 MonoMethodSignature
*tmp_sig
;
2087 if (call
->tail_call
)
2090 g_assert (cinfo
->sig_cookie
.storage
== RegTypeBase
);
2093 * mono_ArgIterator_Setup assumes the signature cookie is
2094 * passed first and all the arguments which were before it are
2095 * passed on the stack after the signature. So compensate by
2096 * passing a different signature.
2098 tmp_sig
= mono_metadata_signature_dup (call
->signature
);
2099 tmp_sig
->param_count
-= call
->signature
->sentinelpos
;
2100 tmp_sig
->sentinelpos
= 0;
2101 memcpy (tmp_sig
->params
, call
->signature
->params
+ call
->signature
->sentinelpos
, tmp_sig
->param_count
* sizeof (MonoType
*));
2103 sig_reg
= mono_alloc_ireg (cfg
);
2104 MONO_EMIT_NEW_SIGNATURECONST (cfg
, sig_reg
, tmp_sig
);
2106 MONO_EMIT_NEW_STORE_MEMBASE (cfg
, OP_STORE_MEMBASE_REG
, ARMREG_SP
, cinfo
->sig_cookie
.offset
, sig_reg
);
2111 mono_arch_get_llvm_call_info (MonoCompile
*cfg
, MonoMethodSignature
*sig
)
2116 LLVMCallInfo
*linfo
;
2118 n
= sig
->param_count
+ sig
->hasthis
;
2120 cinfo
= get_call_info (cfg
->mempool
, sig
);
2122 linfo
= mono_mempool_alloc0 (cfg
->mempool
, sizeof (LLVMCallInfo
) + (sizeof (LLVMArgInfo
) * n
));
2125 * LLVM always uses the native ABI while we use our own ABI, the
2126 * only difference is the handling of vtypes:
2127 * - we only pass/receive them in registers in some cases, and only
2128 * in 1 or 2 integer registers.
2130 switch (cinfo
->ret
.storage
) {
2131 case RegTypeGeneral
:
2134 case RegTypeIRegPair
:
2136 case RegTypeStructByAddr
:
2137 /* Vtype returned using a hidden argument */
2138 linfo
->ret
.storage
= LLVMArgVtypeRetAddr
;
2139 linfo
->vret_arg_index
= cinfo
->vret_arg_index
;
2142 case RegTypeStructByVal
:
2143 /* LLVM models this by returning an int array */
2144 linfo
->ret
.storage
= LLVMArgAsIArgs
;
2145 linfo
->ret
.nslots
= cinfo
->ret
.nregs
;
2149 linfo
->ret
.storage
= LLVMArgFpStruct
;
2150 linfo
->ret
.nslots
= cinfo
->ret
.nregs
;
2151 linfo
->ret
.esize
= cinfo
->ret
.esize
;
2154 cfg
->exception_message
= g_strdup_printf ("unknown ret conv (%d)", cinfo
->ret
.storage
);
2155 cfg
->disable_llvm
= TRUE
;
2159 for (i
= 0; i
< n
; ++i
) {
2160 LLVMArgInfo
*lainfo
= &linfo
->args
[i
];
2161 ainfo
= cinfo
->args
+ i
;
2163 lainfo
->storage
= LLVMArgNone
;
2165 switch (ainfo
->storage
) {
2166 case RegTypeGeneral
:
2167 case RegTypeIRegPair
:
2169 case RegTypeBaseGen
:
2171 lainfo
->storage
= LLVMArgNormal
;
2173 case RegTypeStructByVal
:
2174 lainfo
->storage
= LLVMArgAsIArgs
;
2175 if (eabi_supported
&& ainfo
->align
== 8) {
2176 /* LLVM models this by passing an int64 array */
2177 lainfo
->nslots
= ALIGN_TO (ainfo
->struct_size
, 8) / 8;
2180 lainfo
->nslots
= ainfo
->struct_size
/ sizeof (gpointer
);
2184 case RegTypeStructByAddr
:
2185 case RegTypeStructByAddrOnStack
:
2186 lainfo
->storage
= LLVMArgVtypeByRef
;
2191 lainfo
->storage
= LLVMArgAsFpArgs
;
2192 lainfo
->nslots
= ainfo
->nregs
;
2193 lainfo
->esize
= ainfo
->esize
;
2194 for (j
= 0; j
< ainfo
->nregs
; ++j
)
2195 lainfo
->pair_storage
[j
] = LLVMArgInFPReg
;
2199 cfg
->exception_message
= g_strdup_printf ("ainfo->storage (%d)", ainfo
->storage
);
2200 cfg
->disable_llvm
= TRUE
;
2210 mono_arch_emit_call (MonoCompile
*cfg
, MonoCallInst
*call
)
2213 MonoMethodSignature
*sig
;
2217 sig
= call
->signature
;
2218 n
= sig
->param_count
+ sig
->hasthis
;
2220 cinfo
= get_call_info (cfg
->mempool
, sig
);
2222 switch (cinfo
->ret
.storage
) {
2223 case RegTypeStructByVal
:
2225 if (cinfo
->ret
.storage
== RegTypeStructByVal
&& cinfo
->ret
.nregs
== 1) {
2226 /* The JIT will transform this into a normal call */
2227 call
->vret_in_reg
= TRUE
;
2230 if (call
->inst
.opcode
== OP_TAILCALL
)
2233 * The vtype is returned in registers, save the return area address in a local, and save the vtype into
2234 * the location pointed to by it after call in emit_move_return_value ().
2236 if (!cfg
->arch
.vret_addr_loc
) {
2237 cfg
->arch
.vret_addr_loc
= mono_compile_create_var (cfg
, &mono_defaults
.int_class
->byval_arg
, OP_LOCAL
);
2238 /* Prevent it from being register allocated or optimized away */
2239 ((MonoInst
*)cfg
->arch
.vret_addr_loc
)->flags
|= MONO_INST_VOLATILE
;
2242 MONO_EMIT_NEW_UNALU (cfg
, OP_MOVE
, ((MonoInst
*)cfg
->arch
.vret_addr_loc
)->dreg
, call
->vret_var
->dreg
);
2244 case RegTypeStructByAddr
: {
2246 MONO_INST_NEW (cfg
, vtarg
, OP_MOVE
);
2247 vtarg
->sreg1
= call
->vret_var
->dreg
;
2248 vtarg
->dreg
= mono_alloc_preg (cfg
);
2249 MONO_ADD_INS (cfg
->cbb
, vtarg
);
2251 mono_call_inst_add_outarg_reg (cfg
, call
, vtarg
->dreg
, cinfo
->ret
.reg
, FALSE
);
2258 for (i
= 0; i
< n
; ++i
) {
2259 ArgInfo
*ainfo
= cinfo
->args
+ i
;
2262 if (i
>= sig
->hasthis
)
2263 t
= sig
->params
[i
- sig
->hasthis
];
2265 t
= &mono_defaults
.int_class
->byval_arg
;
2266 t
= mini_get_underlying_type (t
);
2268 if ((sig
->call_convention
== MONO_CALL_VARARG
) && (i
== sig
->sentinelpos
)) {
2269 /* Emit the signature cookie just before the implicit arguments */
2270 emit_sig_cookie (cfg
, call
, cinfo
);
2273 in
= call
->args
[i
];
2275 switch (ainfo
->storage
) {
2276 case RegTypeGeneral
:
2277 case RegTypeIRegPair
:
2278 if (!t
->byref
&& ((t
->type
== MONO_TYPE_I8
) || (t
->type
== MONO_TYPE_U8
))) {
2279 MONO_INST_NEW (cfg
, ins
, OP_MOVE
);
2280 ins
->dreg
= mono_alloc_ireg (cfg
);
2281 ins
->sreg1
= MONO_LVREG_LS (in
->dreg
);
2282 MONO_ADD_INS (cfg
->cbb
, ins
);
2283 mono_call_inst_add_outarg_reg (cfg
, call
, ins
->dreg
, ainfo
->reg
, FALSE
);
2285 MONO_INST_NEW (cfg
, ins
, OP_MOVE
);
2286 ins
->dreg
= mono_alloc_ireg (cfg
);
2287 ins
->sreg1
= MONO_LVREG_MS (in
->dreg
);
2288 MONO_ADD_INS (cfg
->cbb
, ins
);
2289 mono_call_inst_add_outarg_reg (cfg
, call
, ins
->dreg
, ainfo
->reg
+ 1, FALSE
);
2290 } else if (!t
->byref
&& ((t
->type
== MONO_TYPE_R8
) || (t
->type
== MONO_TYPE_R4
))) {
2291 if (ainfo
->size
== 4) {
2292 if (IS_SOFT_FLOAT
) {
2293 /* mono_emit_call_args () have already done the r8->r4 conversion */
2294 /* The converted value is in an int vreg */
2295 MONO_INST_NEW (cfg
, ins
, OP_MOVE
);
2296 ins
->dreg
= mono_alloc_ireg (cfg
);
2297 ins
->sreg1
= in
->dreg
;
2298 MONO_ADD_INS (cfg
->cbb
, ins
);
2299 mono_call_inst_add_outarg_reg (cfg
, call
, ins
->dreg
, ainfo
->reg
, FALSE
);
2303 cfg
->param_area
= MAX (cfg
->param_area
, 8);
2304 MONO_EMIT_NEW_STORE_MEMBASE (cfg
, OP_STORER4_MEMBASE_REG
, ARMREG_SP
, (cfg
->param_area
- 8), in
->dreg
);
2305 creg
= mono_alloc_ireg (cfg
);
2306 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg
, OP_LOAD_MEMBASE
, creg
, ARMREG_SP
, (cfg
->param_area
- 8));
2307 mono_call_inst_add_outarg_reg (cfg
, call
, creg
, ainfo
->reg
, FALSE
);
2310 if (IS_SOFT_FLOAT
) {
2311 MONO_INST_NEW (cfg
, ins
, OP_FGETLOW32
);
2312 ins
->dreg
= mono_alloc_ireg (cfg
);
2313 ins
->sreg1
= in
->dreg
;
2314 MONO_ADD_INS (cfg
->cbb
, ins
);
2315 mono_call_inst_add_outarg_reg (cfg
, call
, ins
->dreg
, ainfo
->reg
, FALSE
);
2317 MONO_INST_NEW (cfg
, ins
, OP_FGETHIGH32
);
2318 ins
->dreg
= mono_alloc_ireg (cfg
);
2319 ins
->sreg1
= in
->dreg
;
2320 MONO_ADD_INS (cfg
->cbb
, ins
);
2321 mono_call_inst_add_outarg_reg (cfg
, call
, ins
->dreg
, ainfo
->reg
+ 1, FALSE
);
2325 cfg
->param_area
= MAX (cfg
->param_area
, 8);
2326 MONO_EMIT_NEW_STORE_MEMBASE (cfg
, OP_STORER8_MEMBASE_REG
, ARMREG_SP
, (cfg
->param_area
- 8), in
->dreg
);
2327 creg
= mono_alloc_ireg (cfg
);
2328 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg
, OP_LOAD_MEMBASE
, creg
, ARMREG_SP
, (cfg
->param_area
- 8));
2329 mono_call_inst_add_outarg_reg (cfg
, call
, creg
, ainfo
->reg
, FALSE
);
2330 creg
= mono_alloc_ireg (cfg
);
2331 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg
, OP_LOAD_MEMBASE
, creg
, ARMREG_SP
, (cfg
->param_area
- 8 + 4));
2332 mono_call_inst_add_outarg_reg (cfg
, call
, creg
, ainfo
->reg
+ 1, FALSE
);
2335 cfg
->flags
|= MONO_CFG_HAS_FPOUT
;
2337 MONO_INST_NEW (cfg
, ins
, OP_MOVE
);
2338 ins
->dreg
= mono_alloc_ireg (cfg
);
2339 ins
->sreg1
= in
->dreg
;
2340 MONO_ADD_INS (cfg
->cbb
, ins
);
2342 mono_call_inst_add_outarg_reg (cfg
, call
, ins
->dreg
, ainfo
->reg
, FALSE
);
2345 case RegTypeStructByVal
:
2346 case RegTypeGSharedVtInReg
:
2347 case RegTypeGSharedVtOnStack
:
2349 case RegTypeStructByAddr
:
2350 case RegTypeStructByAddrOnStack
:
2351 MONO_INST_NEW (cfg
, ins
, OP_OUTARG_VT
);
2352 ins
->opcode
= OP_OUTARG_VT
;
2353 ins
->sreg1
= in
->dreg
;
2354 ins
->klass
= in
->klass
;
2355 ins
->inst_p0
= call
;
2356 ins
->inst_p1
= mono_mempool_alloc (cfg
->mempool
, sizeof (ArgInfo
));
2357 memcpy (ins
->inst_p1
, ainfo
, sizeof (ArgInfo
));
2358 mono_call_inst_add_outarg_vt (cfg
, call
, ins
);
2359 MONO_ADD_INS (cfg
->cbb
, ins
);
2362 if (!t
->byref
&& ((t
->type
== MONO_TYPE_I8
) || (t
->type
== MONO_TYPE_U8
))) {
2363 MONO_EMIT_NEW_STORE_MEMBASE (cfg
, OP_STOREI8_MEMBASE_REG
, ARMREG_SP
, ainfo
->offset
, in
->dreg
);
2364 } else if (!t
->byref
&& ((t
->type
== MONO_TYPE_R4
) || (t
->type
== MONO_TYPE_R8
))) {
2365 if (t
->type
== MONO_TYPE_R8
) {
2366 MONO_EMIT_NEW_STORE_MEMBASE (cfg
, OP_STORER8_MEMBASE_REG
, ARMREG_SP
, ainfo
->offset
, in
->dreg
);
2369 MONO_EMIT_NEW_STORE_MEMBASE (cfg
, OP_STOREI4_MEMBASE_REG
, ARMREG_SP
, ainfo
->offset
, in
->dreg
);
2371 MONO_EMIT_NEW_STORE_MEMBASE (cfg
, OP_STORER4_MEMBASE_REG
, ARMREG_SP
, ainfo
->offset
, in
->dreg
);
2374 MONO_EMIT_NEW_STORE_MEMBASE (cfg
, OP_STORE_MEMBASE_REG
, ARMREG_SP
, ainfo
->offset
, in
->dreg
);
2377 case RegTypeBaseGen
:
2378 if (!t
->byref
&& ((t
->type
== MONO_TYPE_I8
) || (t
->type
== MONO_TYPE_U8
))) {
2379 MONO_EMIT_NEW_STORE_MEMBASE (cfg
, OP_STORE_MEMBASE_REG
, ARMREG_SP
, ainfo
->offset
, (G_BYTE_ORDER
== G_BIG_ENDIAN
) ? MONO_LVREG_LS (in
->dreg
) : MONO_LVREG_MS (in
->dreg
));
2380 MONO_INST_NEW (cfg
, ins
, OP_MOVE
);
2381 ins
->dreg
= mono_alloc_ireg (cfg
);
2382 ins
->sreg1
= G_BYTE_ORDER
== G_BIG_ENDIAN
? MONO_LVREG_MS (in
->dreg
) : MONO_LVREG_LS (in
->dreg
);
2383 MONO_ADD_INS (cfg
->cbb
, ins
);
2384 mono_call_inst_add_outarg_reg (cfg
, call
, ins
->dreg
, ARMREG_R3
, FALSE
);
2385 } else if (!t
->byref
&& (t
->type
== MONO_TYPE_R8
)) {
2388 /* This should work for soft-float as well */
2390 cfg
->param_area
= MAX (cfg
->param_area
, 8);
2391 MONO_EMIT_NEW_STORE_MEMBASE (cfg
, OP_STORER8_MEMBASE_REG
, ARMREG_SP
, (cfg
->param_area
- 8), in
->dreg
);
2392 creg
= mono_alloc_ireg (cfg
);
2393 mono_call_inst_add_outarg_reg (cfg
, call
, creg
, ARMREG_R3
, FALSE
);
2394 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg
, OP_LOAD_MEMBASE
, creg
, ARMREG_SP
, (cfg
->param_area
- 8));
2395 creg
= mono_alloc_ireg (cfg
);
2396 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg
, OP_LOAD_MEMBASE
, creg
, ARMREG_SP
, (cfg
->param_area
- 4));
2397 MONO_EMIT_NEW_STORE_MEMBASE (cfg
, OP_STORE_MEMBASE_REG
, ARMREG_SP
, ainfo
->offset
, creg
);
2398 cfg
->flags
|= MONO_CFG_HAS_FPOUT
;
2400 g_assert_not_reached ();
2404 int fdreg
= mono_alloc_freg (cfg
);
2406 if (ainfo
->size
== 8) {
2407 MONO_INST_NEW (cfg
, ins
, OP_FMOVE
);
2408 ins
->sreg1
= in
->dreg
;
2410 MONO_ADD_INS (cfg
->cbb
, ins
);
2412 mono_call_inst_add_outarg_reg (cfg
, call
, ins
->dreg
, ainfo
->reg
, TRUE
);
2417 * Mono's register allocator doesn't speak single-precision registers that
2418 * overlap double-precision registers (i.e. armhf). So we have to work around
2419 * the register allocator and load the value from memory manually.
2421 * So we create a variable for the float argument and an instruction to store
2422 * the argument into the variable. We then store the list of these arguments
2423 * in call->float_args. This list is then used by emit_float_args later to
2424 * pass the arguments in the various call opcodes.
2426 * This is not very nice, and we should really try to fix the allocator.
2429 MonoInst
*float_arg
= mono_compile_create_var (cfg
, &mono_defaults
.single_class
->byval_arg
, OP_LOCAL
);
2431 /* Make sure the instruction isn't seen as pointless and removed.
2433 float_arg
->flags
|= MONO_INST_VOLATILE
;
2435 MONO_EMIT_NEW_UNALU (cfg
, OP_FMOVE
, float_arg
->dreg
, in
->dreg
);
2437 /* We use the dreg to look up the instruction later. The hreg is used to
2438 * emit the instruction that loads the value into the FP reg.
2440 fad
= mono_mempool_alloc0 (cfg
->mempool
, sizeof (FloatArgData
));
2441 fad
->vreg
= float_arg
->dreg
;
2442 fad
->hreg
= ainfo
->reg
;
2444 call
->float_args
= g_slist_append_mempool (cfg
->mempool
, call
->float_args
, fad
);
2447 call
->used_iregs
|= 1 << ainfo
->reg
;
2448 cfg
->flags
|= MONO_CFG_HAS_FPOUT
;
2452 g_assert_not_reached ();
2456 /* Handle the case where there are no implicit arguments */
2457 if (!sig
->pinvoke
&& (sig
->call_convention
== MONO_CALL_VARARG
) && (n
== sig
->sentinelpos
))
2458 emit_sig_cookie (cfg
, call
, cinfo
);
2460 call
->call_info
= cinfo
;
2461 call
->stack_usage
= cinfo
->stack_usage
;
2465 add_outarg_reg (MonoCompile
*cfg
, MonoCallInst
*call
, ArgStorage storage
, int reg
, MonoInst
*arg
)
2471 MONO_INST_NEW (cfg
, ins
, OP_FMOVE
);
2472 ins
->dreg
= mono_alloc_freg (cfg
);
2473 ins
->sreg1
= arg
->dreg
;
2474 MONO_ADD_INS (cfg
->cbb
, ins
);
2475 mono_call_inst_add_outarg_reg (cfg
, call
, ins
->dreg
, reg
, TRUE
);
2478 g_assert_not_reached ();
2484 mono_arch_emit_outarg_vt (MonoCompile
*cfg
, MonoInst
*ins
, MonoInst
*src
)
2486 MonoCallInst
*call
= (MonoCallInst
*)ins
->inst_p0
;
2488 ArgInfo
*ainfo
= ins
->inst_p1
;
2489 int ovf_size
= ainfo
->vtsize
;
2490 int doffset
= ainfo
->offset
;
2491 int struct_size
= ainfo
->struct_size
;
2492 int i
, soffset
, dreg
, tmpreg
;
2494 switch (ainfo
->storage
) {
2495 case RegTypeGSharedVtInReg
:
2496 case RegTypeStructByAddr
:
2498 mono_call_inst_add_outarg_reg (cfg
, call
, src
->dreg
, ainfo
->reg
, FALSE
);
2500 case RegTypeGSharedVtOnStack
:
2501 case RegTypeStructByAddrOnStack
:
2502 /* Pass by addr on stack */
2503 MONO_EMIT_NEW_STORE_MEMBASE (cfg
, OP_STORE_MEMBASE_REG
, ARMREG_SP
, ainfo
->offset
, src
->dreg
);
2506 for (i
= 0; i
< ainfo
->nregs
; ++i
) {
2507 if (ainfo
->esize
== 4)
2508 MONO_INST_NEW (cfg
, load
, OP_LOADR4_MEMBASE
);
2510 MONO_INST_NEW (cfg
, load
, OP_LOADR8_MEMBASE
);
2511 load
->dreg
= mono_alloc_freg (cfg
);
2512 load
->inst_basereg
= src
->dreg
;
2513 load
->inst_offset
= i
* ainfo
->esize
;
2514 MONO_ADD_INS (cfg
->cbb
, load
);
2516 if (ainfo
->esize
== 4) {
2519 /* See RegTypeFP in mono_arch_emit_call () */
2520 MonoInst
*float_arg
= mono_compile_create_var (cfg
, &mono_defaults
.single_class
->byval_arg
, OP_LOCAL
);
2521 float_arg
->flags
|= MONO_INST_VOLATILE
;
2522 MONO_EMIT_NEW_UNALU (cfg
, OP_FMOVE
, float_arg
->dreg
, load
->dreg
);
2524 fad
= mono_mempool_alloc0 (cfg
->mempool
, sizeof (FloatArgData
));
2525 fad
->vreg
= float_arg
->dreg
;
2526 fad
->hreg
= ainfo
->reg
+ i
;
2528 call
->float_args
= g_slist_append_mempool (cfg
->mempool
, call
->float_args
, fad
);
2530 add_outarg_reg (cfg
, call
, RegTypeFP
, ainfo
->reg
+ (i
* 2), load
);
2536 for (i
= 0; i
< ainfo
->size
; ++i
) {
2537 dreg
= mono_alloc_ireg (cfg
);
2538 switch (struct_size
) {
2540 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg
, OP_LOADU1_MEMBASE
, dreg
, src
->dreg
, soffset
);
2543 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg
, OP_LOADU2_MEMBASE
, dreg
, src
->dreg
, soffset
);
2546 tmpreg
= mono_alloc_ireg (cfg
);
2547 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg
, OP_LOADU1_MEMBASE
, dreg
, src
->dreg
, soffset
);
2548 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg
, OP_LOADU1_MEMBASE
, tmpreg
, src
->dreg
, soffset
+ 1);
2549 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_SHL_IMM
, tmpreg
, tmpreg
, 8);
2550 MONO_EMIT_NEW_BIALU (cfg
, OP_IOR
, dreg
, dreg
, tmpreg
);
2551 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg
, OP_LOADU1_MEMBASE
, tmpreg
, src
->dreg
, soffset
+ 2);
2552 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_SHL_IMM
, tmpreg
, tmpreg
, 16);
2553 MONO_EMIT_NEW_BIALU (cfg
, OP_IOR
, dreg
, dreg
, tmpreg
);
2556 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, dreg
, src
->dreg
, soffset
);
2559 mono_call_inst_add_outarg_reg (cfg
, call
, dreg
, ainfo
->reg
+ i
, FALSE
);
2560 soffset
+= sizeof (gpointer
);
2561 struct_size
-= sizeof (gpointer
);
2563 //g_print ("vt size: %d at R%d + %d\n", doffset, vt->inst_basereg, vt->inst_offset);
2565 mini_emit_memcpy (cfg
, ARMREG_SP
, doffset
, src
->dreg
, soffset
, MIN (ovf_size
* sizeof (gpointer
), struct_size
), struct_size
< 4 ? 1 : 4);
2571 mono_arch_emit_setret (MonoCompile
*cfg
, MonoMethod
*method
, MonoInst
*val
)
2573 MonoType
*ret
= mini_get_underlying_type (mono_method_signature (method
)->ret
);
2576 if (ret
->type
== MONO_TYPE_I8
|| ret
->type
== MONO_TYPE_U8
) {
2579 if (COMPILE_LLVM (cfg
)) {
2580 MONO_EMIT_NEW_UNALU (cfg
, OP_MOVE
, cfg
->ret
->dreg
, val
->dreg
);
2582 MONO_INST_NEW (cfg
, ins
, OP_SETLRET
);
2583 ins
->sreg1
= MONO_LVREG_LS (val
->dreg
);
2584 ins
->sreg2
= MONO_LVREG_MS (val
->dreg
);
2585 MONO_ADD_INS (cfg
->cbb
, ins
);
2590 case MONO_ARM_FPU_NONE
:
2591 if (ret
->type
== MONO_TYPE_R8
) {
2594 MONO_INST_NEW (cfg
, ins
, OP_SETFRET
);
2595 ins
->dreg
= cfg
->ret
->dreg
;
2596 ins
->sreg1
= val
->dreg
;
2597 MONO_ADD_INS (cfg
->cbb
, ins
);
2600 if (ret
->type
== MONO_TYPE_R4
) {
2601 /* Already converted to an int in method_to_ir () */
2602 MONO_EMIT_NEW_UNALU (cfg
, OP_MOVE
, cfg
->ret
->dreg
, val
->dreg
);
2606 case MONO_ARM_FPU_VFP
:
2607 case MONO_ARM_FPU_VFP_HARD
:
2608 if (ret
->type
== MONO_TYPE_R8
|| ret
->type
== MONO_TYPE_R4
) {
2611 MONO_INST_NEW (cfg
, ins
, OP_SETFRET
);
2612 ins
->dreg
= cfg
->ret
->dreg
;
2613 ins
->sreg1
= val
->dreg
;
2614 MONO_ADD_INS (cfg
->cbb
, ins
);
2619 g_assert_not_reached ();
2623 MONO_EMIT_NEW_UNALU (cfg
, OP_MOVE
, cfg
->ret
->dreg
, val
->dreg
);
2626 #endif /* #ifndef DISABLE_JIT */
2629 mono_arch_is_inst_imm (gint64 imm
)
2635 MonoMethodSignature
*sig
;
2638 MonoType
**param_types
;
2642 dyn_call_supported (CallInfo
*cinfo
, MonoMethodSignature
*sig
)
2646 if (sig
->hasthis
+ sig
->param_count
> PARAM_REGS
+ DYN_CALL_STACK_ARGS
)
2649 switch (cinfo
->ret
.storage
) {
2651 case RegTypeGeneral
:
2652 case RegTypeIRegPair
:
2653 case RegTypeStructByAddr
:
2664 for (i
= 0; i
< cinfo
->nargs
; ++i
) {
2665 ArgInfo
*ainfo
= &cinfo
->args
[i
];
2668 switch (ainfo
->storage
) {
2669 case RegTypeGeneral
:
2670 case RegTypeIRegPair
:
2671 case RegTypeBaseGen
:
2675 if (ainfo
->offset
>= (DYN_CALL_STACK_ARGS
* sizeof (gpointer
)))
2678 case RegTypeStructByVal
:
2679 if (ainfo
->size
== 0)
2680 last_slot
= PARAM_REGS
+ (ainfo
->offset
/ 4) + ainfo
->vtsize
;
2682 last_slot
= ainfo
->reg
+ ainfo
->size
+ ainfo
->vtsize
;
2683 if (last_slot
>= PARAM_REGS
+ DYN_CALL_STACK_ARGS
)
2691 // FIXME: Can't use cinfo only as it doesn't contain info about I8/float */
2692 for (i
= 0; i
< sig
->param_count
; ++i
) {
2693 MonoType
*t
= sig
->params
[i
];
2698 t
= mini_get_underlying_type (t
);
2721 mono_arch_dyn_call_prepare (MonoMethodSignature
*sig
)
2723 ArchDynCallInfo
*info
;
2727 cinfo
= get_call_info (NULL
, sig
);
2729 if (!dyn_call_supported (cinfo
, sig
)) {
2734 info
= g_new0 (ArchDynCallInfo
, 1);
2735 // FIXME: Preprocess the info to speed up start_dyn_call ()
2737 info
->cinfo
= cinfo
;
2738 info
->rtype
= mini_get_underlying_type (sig
->ret
);
2739 info
->param_types
= g_new0 (MonoType
*, sig
->param_count
);
2740 for (i
= 0; i
< sig
->param_count
; ++i
)
2741 info
->param_types
[i
] = mini_get_underlying_type (sig
->params
[i
]);
2743 return (MonoDynCallInfo
*)info
;
2747 mono_arch_dyn_call_free (MonoDynCallInfo
*info
)
2749 ArchDynCallInfo
*ainfo
= (ArchDynCallInfo
*)info
;
2751 g_free (ainfo
->cinfo
);
2756 mono_arch_start_dyn_call (MonoDynCallInfo
*info
, gpointer
**args
, guint8
*ret
, guint8
*buf
, int buf_len
)
2758 ArchDynCallInfo
*dinfo
= (ArchDynCallInfo
*)info
;
2759 DynCallArgs
*p
= (DynCallArgs
*)buf
;
2760 int arg_index
, greg
, i
, j
, pindex
;
2761 MonoMethodSignature
*sig
= dinfo
->sig
;
2763 g_assert (buf_len
>= sizeof (DynCallArgs
));
2773 if (sig
->hasthis
|| dinfo
->cinfo
->vret_arg_index
== 1) {
2774 p
->regs
[greg
++] = (mgreg_t
)*(args
[arg_index
++]);
2779 if (dinfo
->cinfo
->ret
.storage
== RegTypeStructByAddr
)
2780 p
->regs
[greg
++] = (mgreg_t
)ret
;
2782 for (i
= pindex
; i
< sig
->param_count
; i
++) {
2783 MonoType
*t
= dinfo
->param_types
[i
];
2784 gpointer
*arg
= args
[arg_index
++];
2785 ArgInfo
*ainfo
= &dinfo
->cinfo
->args
[i
+ sig
->hasthis
];
2788 if (ainfo
->storage
== RegTypeGeneral
|| ainfo
->storage
== RegTypeIRegPair
|| ainfo
->storage
== RegTypeStructByVal
) {
2790 } else if (ainfo
->storage
== RegTypeFP
) {
2791 } else if (ainfo
->storage
== RegTypeBase
) {
2792 slot
= PARAM_REGS
+ (ainfo
->offset
/ 4);
2793 } else if (ainfo
->storage
== RegTypeBaseGen
) {
2794 /* slot + 1 is the first stack slot, so the code below will work */
2797 g_assert_not_reached ();
2801 p
->regs
[slot
] = (mgreg_t
)*arg
;
2806 case MONO_TYPE_OBJECT
:
2810 p
->regs
[slot
] = (mgreg_t
)*arg
;
2813 p
->regs
[slot
] = *(guint8
*)arg
;
2816 p
->regs
[slot
] = *(gint8
*)arg
;
2819 p
->regs
[slot
] = *(gint16
*)arg
;
2822 p
->regs
[slot
] = *(guint16
*)arg
;
2825 p
->regs
[slot
] = *(gint32
*)arg
;
2828 p
->regs
[slot
] = *(guint32
*)arg
;
2832 p
->regs
[slot
++] = (mgreg_t
)arg
[0];
2833 p
->regs
[slot
] = (mgreg_t
)arg
[1];
2836 if (ainfo
->storage
== RegTypeFP
) {
2837 float f
= *(float*)arg
;
2838 p
->fpregs
[ainfo
->reg
/ 2] = *(double*)&f
;
2841 p
->regs
[slot
] = *(mgreg_t
*)arg
;
2845 if (ainfo
->storage
== RegTypeFP
) {
2846 p
->fpregs
[ainfo
->reg
/ 2] = *(double*)arg
;
2849 p
->regs
[slot
++] = (mgreg_t
)arg
[0];
2850 p
->regs
[slot
] = (mgreg_t
)arg
[1];
2853 case MONO_TYPE_GENERICINST
:
2854 if (MONO_TYPE_IS_REFERENCE (t
)) {
2855 p
->regs
[slot
] = (mgreg_t
)*arg
;
2858 if (t
->type
== MONO_TYPE_GENERICINST
&& mono_class_is_nullable (mono_class_from_mono_type (t
))) {
2859 MonoClass
*klass
= mono_class_from_mono_type (t
);
2860 guint8
*nullable_buf
;
2863 size
= mono_class_value_size (klass
, NULL
);
2864 nullable_buf
= g_alloca (size
);
2865 g_assert (nullable_buf
);
2867 /* The argument pointed to by arg is either a boxed vtype or null */
2868 mono_nullable_init (nullable_buf
, (MonoObject
*)arg
, klass
);
2870 arg
= (gpointer
*)nullable_buf
;
2876 case MONO_TYPE_VALUETYPE
:
2877 g_assert (ainfo
->storage
== RegTypeStructByVal
);
2879 if (ainfo
->size
== 0)
2880 slot
= PARAM_REGS
+ (ainfo
->offset
/ 4);
2884 for (j
= 0; j
< ainfo
->size
+ ainfo
->vtsize
; ++j
)
2885 p
->regs
[slot
++] = ((mgreg_t
*)arg
) [j
];
2888 g_assert_not_reached ();
2894 mono_arch_finish_dyn_call (MonoDynCallInfo
*info
, guint8
*buf
)
2896 ArchDynCallInfo
*ainfo
= (ArchDynCallInfo
*)info
;
2897 DynCallArgs
*p
= (DynCallArgs
*)buf
;
2898 MonoType
*ptype
= ainfo
->rtype
;
2899 guint8
*ret
= p
->ret
;
2900 mgreg_t res
= p
->res
;
2901 mgreg_t res2
= p
->res2
;
2903 switch (ptype
->type
) {
2904 case MONO_TYPE_VOID
:
2905 *(gpointer
*)ret
= NULL
;
2907 case MONO_TYPE_OBJECT
:
2911 *(gpointer
*)ret
= (gpointer
)res
;
2917 *(guint8
*)ret
= res
;
2920 *(gint16
*)ret
= res
;
2923 *(guint16
*)ret
= res
;
2926 *(gint32
*)ret
= res
;
2929 *(guint32
*)ret
= res
;
2933 /* This handles endianness as well */
2934 ((gint32
*)ret
) [0] = res
;
2935 ((gint32
*)ret
) [1] = res2
;
2937 case MONO_TYPE_GENERICINST
:
2938 if (MONO_TYPE_IS_REFERENCE (ptype
)) {
2939 *(gpointer
*)ret
= (gpointer
)res
;
2944 case MONO_TYPE_VALUETYPE
:
2945 g_assert (ainfo
->cinfo
->ret
.storage
== RegTypeStructByAddr
);
2951 *(float*)ret
= *(float*)&p
->fpregs
[0];
2953 *(float*)ret
= *(float*)&res
;
2955 case MONO_TYPE_R8
: {
2959 if (IS_HARD_FLOAT
) {
2960 *(double*)ret
= p
->fpregs
[0];
2965 *(double*)ret
= *(double*)®s
;
2970 g_assert_not_reached ();
2977 * Allow tracing to work with this interface (with an optional argument)
2981 mono_arch_instrument_prolog (MonoCompile
*cfg
, void *func
, void *p
, gboolean enable_arguments
)
2985 code
= mono_arm_emit_load_imm (code
, ARMREG_R0
, (guint32
)cfg
->method
);
2986 ARM_MOV_REG_IMM8 (code
, ARMREG_R1
, 0); /* NULL ebp for now */
2987 code
= mono_arm_emit_load_imm (code
, ARMREG_R2
, (guint32
)func
);
2988 code
= emit_call_reg (code
, ARMREG_R2
);
3002 mono_arch_instrument_epilog_full (MonoCompile
*cfg
, void *func
, void *p
, gboolean enable_arguments
, gboolean preserve_argument_registers
)
3005 int save_mode
= SAVE_NONE
;
3007 MonoMethod
*method
= cfg
->method
;
3008 MonoType
*ret_type
= mini_get_underlying_type (mono_method_signature (method
)->ret
);
3009 int rtype
= ret_type
->type
;
3010 int save_offset
= cfg
->param_area
;
3014 offset
= code
- cfg
->native_code
;
3015 /* we need about 16 instructions */
3016 if (offset
> (cfg
->code_size
- 16 * 4)) {
3017 cfg
->code_size
*= 2;
3018 cfg
->native_code
= g_realloc (cfg
->native_code
, cfg
->code_size
);
3019 code
= cfg
->native_code
+ offset
;
3022 case MONO_TYPE_VOID
:
3023 /* special case string .ctor icall */
3024 if (strcmp (".ctor", method
->name
) && method
->klass
== mono_defaults
.string_class
)
3025 save_mode
= SAVE_ONE
;
3027 save_mode
= SAVE_NONE
;
3031 save_mode
= SAVE_TWO
;
3035 save_mode
= SAVE_ONE_FP
;
3037 save_mode
= SAVE_ONE
;
3041 save_mode
= SAVE_TWO_FP
;
3043 save_mode
= SAVE_TWO
;
3045 case MONO_TYPE_GENERICINST
:
3046 if (!mono_type_generic_inst_is_valuetype (ret_type
)) {
3047 save_mode
= SAVE_ONE
;
3051 case MONO_TYPE_VALUETYPE
:
3052 save_mode
= SAVE_STRUCT
;
3055 save_mode
= SAVE_ONE
;
3059 switch (save_mode
) {
3061 ARM_STR_IMM (code
, ARMREG_R0
, cfg
->frame_reg
, save_offset
);
3062 ARM_STR_IMM (code
, ARMREG_R1
, cfg
->frame_reg
, save_offset
+ 4);
3063 if (enable_arguments
) {
3064 ARM_MOV_REG_REG (code
, ARMREG_R2
, ARMREG_R1
);
3065 ARM_MOV_REG_REG (code
, ARMREG_R1
, ARMREG_R0
);
3069 ARM_STR_IMM (code
, ARMREG_R0
, cfg
->frame_reg
, save_offset
);
3070 if (enable_arguments
) {
3071 ARM_MOV_REG_REG (code
, ARMREG_R1
, ARMREG_R0
);
3075 ARM_FSTS (code
, ARM_VFP_F0
, cfg
->frame_reg
, save_offset
);
3076 if (enable_arguments
) {
3077 ARM_FMRS (code
, ARMREG_R1
, ARM_VFP_F0
);
3081 ARM_FSTD (code
, ARM_VFP_D0
, cfg
->frame_reg
, save_offset
);
3082 if (enable_arguments
) {
3083 ARM_FMDRR (code
, ARMREG_R1
, ARMREG_R2
, ARM_VFP_D0
);
3087 if (enable_arguments
) {
3088 /* FIXME: get the actual address */
3089 ARM_MOV_REG_REG (code
, ARMREG_R1
, ARMREG_R0
);
3097 code
= mono_arm_emit_load_imm (code
, ARMREG_R0
, (guint32
)cfg
->method
);
3098 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, (guint32
)func
);
3099 code
= emit_call_reg (code
, ARMREG_IP
);
3101 switch (save_mode
) {
3103 ARM_LDR_IMM (code
, ARMREG_R0
, cfg
->frame_reg
, save_offset
);
3104 ARM_LDR_IMM (code
, ARMREG_R1
, cfg
->frame_reg
, save_offset
+ 4);
3107 ARM_LDR_IMM (code
, ARMREG_R0
, cfg
->frame_reg
, save_offset
);
3110 ARM_FLDS (code
, ARM_VFP_F0
, cfg
->frame_reg
, save_offset
);
3113 ARM_FLDD (code
, ARM_VFP_D0
, cfg
->frame_reg
, save_offset
);
3124 * The immediate field for cond branches is big enough for all reasonable methods
3126 #define EMIT_COND_BRANCH_FLAGS(ins,condcode) \
3127 if (0 && ins->inst_true_bb->native_offset) { \
3128 ARM_B_COND (code, (condcode), (code - cfg->native_code + ins->inst_true_bb->native_offset) & 0xffffff); \
3130 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_true_bb); \
3131 ARM_B_COND (code, (condcode), 0); \
3134 #define EMIT_COND_BRANCH(ins,cond) EMIT_COND_BRANCH_FLAGS(ins, branch_cc_table [(cond)])
3136 /* emit an exception if condition is fail
3138 * We assign the extra code used to throw the implicit exceptions
3139 * to cfg->bb_exit as far as the big branch handling is concerned
3141 #define EMIT_COND_SYSTEM_EXCEPTION_FLAGS(condcode,exc_name) \
3143 mono_add_patch_info (cfg, code - cfg->native_code, \
3144 MONO_PATCH_INFO_EXC, exc_name); \
3145 ARM_BL_COND (code, (condcode), 0); \
3148 #define EMIT_COND_SYSTEM_EXCEPTION(cond,exc_name) EMIT_COND_SYSTEM_EXCEPTION_FLAGS(branch_cc_table [(cond)], (exc_name))
3151 mono_arch_peephole_pass_1 (MonoCompile
*cfg
, MonoBasicBlock
*bb
)
3156 mono_arch_peephole_pass_2 (MonoCompile
*cfg
, MonoBasicBlock
*bb
)
3160 MONO_BB_FOR_EACH_INS_SAFE (bb
, n
, ins
) {
3161 MonoInst
*last_ins
= mono_inst_prev (ins
, FILTER_IL_SEQ_POINT
);
3163 switch (ins
->opcode
) {
3166 /* Already done by an arch-independent pass */
3168 case OP_LOAD_MEMBASE
:
3169 case OP_LOADI4_MEMBASE
:
3171 * OP_STORE_MEMBASE_REG reg, offset(basereg)
3172 * OP_LOAD_MEMBASE offset(basereg), reg
3174 if (last_ins
&& (last_ins
->opcode
== OP_STOREI4_MEMBASE_REG
3175 || last_ins
->opcode
== OP_STORE_MEMBASE_REG
) &&
3176 ins
->inst_basereg
== last_ins
->inst_destbasereg
&&
3177 ins
->inst_offset
== last_ins
->inst_offset
) {
3178 if (ins
->dreg
== last_ins
->sreg1
) {
3179 MONO_DELETE_INS (bb
, ins
);
3182 //static int c = 0; g_print ("MATCHX %s %d\n", cfg->method->name,c++);
3183 ins
->opcode
= OP_MOVE
;
3184 ins
->sreg1
= last_ins
->sreg1
;
3188 * Note: reg1 must be different from the basereg in the second load
3189 * OP_LOAD_MEMBASE offset(basereg), reg1
3190 * OP_LOAD_MEMBASE offset(basereg), reg2
3192 * OP_LOAD_MEMBASE offset(basereg), reg1
3193 * OP_MOVE reg1, reg2
3195 } if (last_ins
&& (last_ins
->opcode
== OP_LOADI4_MEMBASE
3196 || last_ins
->opcode
== OP_LOAD_MEMBASE
) &&
3197 ins
->inst_basereg
!= last_ins
->dreg
&&
3198 ins
->inst_basereg
== last_ins
->inst_basereg
&&
3199 ins
->inst_offset
== last_ins
->inst_offset
) {
3201 if (ins
->dreg
== last_ins
->dreg
) {
3202 MONO_DELETE_INS (bb
, ins
);
3205 ins
->opcode
= OP_MOVE
;
3206 ins
->sreg1
= last_ins
->dreg
;
3209 //g_assert_not_reached ();
3213 * OP_STORE_MEMBASE_IMM imm, offset(basereg)
3214 * OP_LOAD_MEMBASE offset(basereg), reg
3216 * OP_STORE_MEMBASE_IMM imm, offset(basereg)
3217 * OP_ICONST reg, imm
3219 } else if (last_ins
&& (last_ins
->opcode
== OP_STOREI4_MEMBASE_IMM
3220 || last_ins
->opcode
== OP_STORE_MEMBASE_IMM
) &&
3221 ins
->inst_basereg
== last_ins
->inst_destbasereg
&&
3222 ins
->inst_offset
== last_ins
->inst_offset
) {
3223 //static int c = 0; g_print ("MATCHX %s %d\n", cfg->method->name,c++);
3224 ins
->opcode
= OP_ICONST
;
3225 ins
->inst_c0
= last_ins
->inst_imm
;
3226 g_assert_not_reached (); // check this rule
3230 case OP_LOADU1_MEMBASE
:
3231 case OP_LOADI1_MEMBASE
:
3232 if (last_ins
&& (last_ins
->opcode
== OP_STOREI1_MEMBASE_REG
) &&
3233 ins
->inst_basereg
== last_ins
->inst_destbasereg
&&
3234 ins
->inst_offset
== last_ins
->inst_offset
) {
3235 ins
->opcode
= (ins
->opcode
== OP_LOADI1_MEMBASE
) ? OP_ICONV_TO_I1
: OP_ICONV_TO_U1
;
3236 ins
->sreg1
= last_ins
->sreg1
;
3239 case OP_LOADU2_MEMBASE
:
3240 case OP_LOADI2_MEMBASE
:
3241 if (last_ins
&& (last_ins
->opcode
== OP_STOREI2_MEMBASE_REG
) &&
3242 ins
->inst_basereg
== last_ins
->inst_destbasereg
&&
3243 ins
->inst_offset
== last_ins
->inst_offset
) {
3244 ins
->opcode
= (ins
->opcode
== OP_LOADI2_MEMBASE
) ? OP_ICONV_TO_I2
: OP_ICONV_TO_U2
;
3245 ins
->sreg1
= last_ins
->sreg1
;
3249 ins
->opcode
= OP_MOVE
;
3253 if (ins
->dreg
== ins
->sreg1
) {
3254 MONO_DELETE_INS (bb
, ins
);
3258 * OP_MOVE sreg, dreg
3259 * OP_MOVE dreg, sreg
3261 if (last_ins
&& last_ins
->opcode
== OP_MOVE
&&
3262 ins
->sreg1
== last_ins
->dreg
&&
3263 ins
->dreg
== last_ins
->sreg1
) {
3264 MONO_DELETE_INS (bb
, ins
);
3273 * the branch_cc_table should maintain the order of these
3287 branch_cc_table
[] = {
3301 #define ADD_NEW_INS(cfg,dest,op) do { \
3302 MONO_INST_NEW ((cfg), (dest), (op)); \
3303 mono_bblock_insert_before_ins (bb, ins, (dest)); \
3307 map_to_reg_reg_op (int op
)
3316 case OP_COMPARE_IMM
:
3318 case OP_ICOMPARE_IMM
:
3332 case OP_LOAD_MEMBASE
:
3333 return OP_LOAD_MEMINDEX
;
3334 case OP_LOADI4_MEMBASE
:
3335 return OP_LOADI4_MEMINDEX
;
3336 case OP_LOADU4_MEMBASE
:
3337 return OP_LOADU4_MEMINDEX
;
3338 case OP_LOADU1_MEMBASE
:
3339 return OP_LOADU1_MEMINDEX
;
3340 case OP_LOADI2_MEMBASE
:
3341 return OP_LOADI2_MEMINDEX
;
3342 case OP_LOADU2_MEMBASE
:
3343 return OP_LOADU2_MEMINDEX
;
3344 case OP_LOADI1_MEMBASE
:
3345 return OP_LOADI1_MEMINDEX
;
3346 case OP_STOREI1_MEMBASE_REG
:
3347 return OP_STOREI1_MEMINDEX
;
3348 case OP_STOREI2_MEMBASE_REG
:
3349 return OP_STOREI2_MEMINDEX
;
3350 case OP_STOREI4_MEMBASE_REG
:
3351 return OP_STOREI4_MEMINDEX
;
3352 case OP_STORE_MEMBASE_REG
:
3353 return OP_STORE_MEMINDEX
;
3354 case OP_STORER4_MEMBASE_REG
:
3355 return OP_STORER4_MEMINDEX
;
3356 case OP_STORER8_MEMBASE_REG
:
3357 return OP_STORER8_MEMINDEX
;
3358 case OP_STORE_MEMBASE_IMM
:
3359 return OP_STORE_MEMBASE_REG
;
3360 case OP_STOREI1_MEMBASE_IMM
:
3361 return OP_STOREI1_MEMBASE_REG
;
3362 case OP_STOREI2_MEMBASE_IMM
:
3363 return OP_STOREI2_MEMBASE_REG
;
3364 case OP_STOREI4_MEMBASE_IMM
:
3365 return OP_STOREI4_MEMBASE_REG
;
3367 g_assert_not_reached ();
3371 * Remove from the instruction list the instructions that can't be
3372 * represented with very simple instructions with no register
3376 mono_arch_lowering_pass (MonoCompile
*cfg
, MonoBasicBlock
*bb
)
3378 MonoInst
*ins
, *temp
, *last_ins
= NULL
;
3379 int rot_amount
, imm8
, low_imm
;
3381 MONO_BB_FOR_EACH_INS (bb
, ins
) {
3383 switch (ins
->opcode
) {
3387 case OP_COMPARE_IMM
:
3388 case OP_ICOMPARE_IMM
:
3402 if ((imm8
= mono_arm_is_rotated_imm8 (ins
->inst_imm
, &rot_amount
)) < 0) {
3403 int opcode2
= mono_op_imm_to_op (ins
->opcode
);
3404 ADD_NEW_INS (cfg
, temp
, OP_ICONST
);
3405 temp
->inst_c0
= ins
->inst_imm
;
3406 temp
->dreg
= mono_alloc_ireg (cfg
);
3407 ins
->sreg2
= temp
->dreg
;
3409 g_error ("mono_op_imm_to_op failed for %s\n", mono_inst_name (ins
->opcode
));
3410 ins
->opcode
= opcode2
;
3412 if (ins
->opcode
== OP_SBB
|| ins
->opcode
== OP_ISBB
|| ins
->opcode
== OP_SUBCC
)
3418 if (ins
->inst_imm
== 1) {
3419 ins
->opcode
= OP_MOVE
;
3422 if (ins
->inst_imm
== 0) {
3423 ins
->opcode
= OP_ICONST
;
3427 imm8
= mono_is_power_of_two (ins
->inst_imm
);
3429 ins
->opcode
= OP_SHL_IMM
;
3430 ins
->inst_imm
= imm8
;
3433 ADD_NEW_INS (cfg
, temp
, OP_ICONST
);
3434 temp
->inst_c0
= ins
->inst_imm
;
3435 temp
->dreg
= mono_alloc_ireg (cfg
);
3436 ins
->sreg2
= temp
->dreg
;
3437 ins
->opcode
= OP_IMUL
;
3443 if (ins
->next
&& (ins
->next
->opcode
== OP_COND_EXC_C
|| ins
->next
->opcode
== OP_COND_EXC_IC
))
3444 /* ARM sets the C flag to 1 if there was _no_ overflow */
3445 ins
->next
->opcode
= OP_COND_EXC_NC
;
3448 case OP_IDIV_UN_IMM
:
3450 case OP_IREM_UN_IMM
: {
3451 int opcode2
= mono_op_imm_to_op (ins
->opcode
);
3452 ADD_NEW_INS (cfg
, temp
, OP_ICONST
);
3453 temp
->inst_c0
= ins
->inst_imm
;
3454 temp
->dreg
= mono_alloc_ireg (cfg
);
3455 ins
->sreg2
= temp
->dreg
;
3457 g_error ("mono_op_imm_to_op failed for %s\n", mono_inst_name (ins
->opcode
));
3458 ins
->opcode
= opcode2
;
3461 case OP_LOCALLOC_IMM
:
3462 ADD_NEW_INS (cfg
, temp
, OP_ICONST
);
3463 temp
->inst_c0
= ins
->inst_imm
;
3464 temp
->dreg
= mono_alloc_ireg (cfg
);
3465 ins
->sreg1
= temp
->dreg
;
3466 ins
->opcode
= OP_LOCALLOC
;
3468 case OP_LOAD_MEMBASE
:
3469 case OP_LOADI4_MEMBASE
:
3470 case OP_LOADU4_MEMBASE
:
3471 case OP_LOADU1_MEMBASE
:
3472 /* we can do two things: load the immed in a register
3473 * and use an indexed load, or see if the immed can be
3474 * represented as an ad_imm + a load with a smaller offset
3475 * that fits. We just do the first for now, optimize later.
3477 if (arm_is_imm12 (ins
->inst_offset
))
3479 ADD_NEW_INS (cfg
, temp
, OP_ICONST
);
3480 temp
->inst_c0
= ins
->inst_offset
;
3481 temp
->dreg
= mono_alloc_ireg (cfg
);
3482 ins
->sreg2
= temp
->dreg
;
3483 ins
->opcode
= map_to_reg_reg_op (ins
->opcode
);
3485 case OP_LOADI2_MEMBASE
:
3486 case OP_LOADU2_MEMBASE
:
3487 case OP_LOADI1_MEMBASE
:
3488 if (arm_is_imm8 (ins
->inst_offset
))
3490 ADD_NEW_INS (cfg
, temp
, OP_ICONST
);
3491 temp
->inst_c0
= ins
->inst_offset
;
3492 temp
->dreg
= mono_alloc_ireg (cfg
);
3493 ins
->sreg2
= temp
->dreg
;
3494 ins
->opcode
= map_to_reg_reg_op (ins
->opcode
);
3496 case OP_LOADR4_MEMBASE
:
3497 case OP_LOADR8_MEMBASE
:
3498 if (arm_is_fpimm8 (ins
->inst_offset
))
3500 low_imm
= ins
->inst_offset
& 0x1ff;
3501 if ((imm8
= mono_arm_is_rotated_imm8 (ins
->inst_offset
& ~0x1ff, &rot_amount
)) >= 0) {
3502 ADD_NEW_INS (cfg
, temp
, OP_ADD_IMM
);
3503 temp
->inst_imm
= ins
->inst_offset
& ~0x1ff;
3504 temp
->sreg1
= ins
->inst_basereg
;
3505 temp
->dreg
= mono_alloc_ireg (cfg
);
3506 ins
->inst_basereg
= temp
->dreg
;
3507 ins
->inst_offset
= low_imm
;
3511 ADD_NEW_INS (cfg
, temp
, OP_ICONST
);
3512 temp
->inst_c0
= ins
->inst_offset
;
3513 temp
->dreg
= mono_alloc_ireg (cfg
);
3515 ADD_NEW_INS (cfg
, add_ins
, OP_IADD
);
3516 add_ins
->sreg1
= ins
->inst_basereg
;
3517 add_ins
->sreg2
= temp
->dreg
;
3518 add_ins
->dreg
= mono_alloc_ireg (cfg
);
3520 ins
->inst_basereg
= add_ins
->dreg
;
3521 ins
->inst_offset
= 0;
3524 case OP_STORE_MEMBASE_REG
:
3525 case OP_STOREI4_MEMBASE_REG
:
3526 case OP_STOREI1_MEMBASE_REG
:
3527 if (arm_is_imm12 (ins
->inst_offset
))
3529 ADD_NEW_INS (cfg
, temp
, OP_ICONST
);
3530 temp
->inst_c0
= ins
->inst_offset
;
3531 temp
->dreg
= mono_alloc_ireg (cfg
);
3532 ins
->sreg2
= temp
->dreg
;
3533 ins
->opcode
= map_to_reg_reg_op (ins
->opcode
);
3535 case OP_STOREI2_MEMBASE_REG
:
3536 if (arm_is_imm8 (ins
->inst_offset
))
3538 ADD_NEW_INS (cfg
, temp
, OP_ICONST
);
3539 temp
->inst_c0
= ins
->inst_offset
;
3540 temp
->dreg
= mono_alloc_ireg (cfg
);
3541 ins
->sreg2
= temp
->dreg
;
3542 ins
->opcode
= map_to_reg_reg_op (ins
->opcode
);
3544 case OP_STORER4_MEMBASE_REG
:
3545 case OP_STORER8_MEMBASE_REG
:
3546 if (arm_is_fpimm8 (ins
->inst_offset
))
3548 low_imm
= ins
->inst_offset
& 0x1ff;
3549 if ((imm8
= mono_arm_is_rotated_imm8 (ins
->inst_offset
& ~ 0x1ff, &rot_amount
)) >= 0 && arm_is_fpimm8 (low_imm
)) {
3550 ADD_NEW_INS (cfg
, temp
, OP_ADD_IMM
);
3551 temp
->inst_imm
= ins
->inst_offset
& ~0x1ff;
3552 temp
->sreg1
= ins
->inst_destbasereg
;
3553 temp
->dreg
= mono_alloc_ireg (cfg
);
3554 ins
->inst_destbasereg
= temp
->dreg
;
3555 ins
->inst_offset
= low_imm
;
3559 ADD_NEW_INS (cfg
, temp
, OP_ICONST
);
3560 temp
->inst_c0
= ins
->inst_offset
;
3561 temp
->dreg
= mono_alloc_ireg (cfg
);
3563 ADD_NEW_INS (cfg
, add_ins
, OP_IADD
);
3564 add_ins
->sreg1
= ins
->inst_destbasereg
;
3565 add_ins
->sreg2
= temp
->dreg
;
3566 add_ins
->dreg
= mono_alloc_ireg (cfg
);
3568 ins
->inst_destbasereg
= add_ins
->dreg
;
3569 ins
->inst_offset
= 0;
3572 case OP_STORE_MEMBASE_IMM
:
3573 case OP_STOREI1_MEMBASE_IMM
:
3574 case OP_STOREI2_MEMBASE_IMM
:
3575 case OP_STOREI4_MEMBASE_IMM
:
3576 ADD_NEW_INS (cfg
, temp
, OP_ICONST
);
3577 temp
->inst_c0
= ins
->inst_imm
;
3578 temp
->dreg
= mono_alloc_ireg (cfg
);
3579 ins
->sreg1
= temp
->dreg
;
3580 ins
->opcode
= map_to_reg_reg_op (ins
->opcode
);
3582 goto loop_start
; /* make it handle the possibly big ins->inst_offset */
3585 gboolean swap
= FALSE
;
3589 /* Optimized away */
3594 /* Some fp compares require swapped operands */
3595 switch (ins
->next
->opcode
) {
3597 ins
->next
->opcode
= OP_FBLT
;
3601 ins
->next
->opcode
= OP_FBLT_UN
;
3605 ins
->next
->opcode
= OP_FBGE
;
3609 ins
->next
->opcode
= OP_FBGE_UN
;
3617 ins
->sreg1
= ins
->sreg2
;
3626 bb
->last_ins
= last_ins
;
3627 bb
->max_vreg
= cfg
->next_vreg
;
3631 mono_arch_decompose_long_opts (MonoCompile
*cfg
, MonoInst
*long_ins
)
3635 if (long_ins
->opcode
== OP_LNEG
) {
3637 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_ARM_RSBS_IMM
, MONO_LVREG_LS (ins
->dreg
), MONO_LVREG_LS (ins
->sreg1
), 0);
3638 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_ARM_RSC_IMM
, MONO_LVREG_MS (ins
->dreg
), MONO_LVREG_MS (ins
->sreg1
), 0);
3644 emit_float_to_int (MonoCompile
*cfg
, guchar
*code
, int dreg
, int sreg
, int size
, gboolean is_signed
)
3646 /* sreg is a float, dreg is an integer reg */
3648 code
= mono_arm_emit_vfp_scratch_save (cfg
, code
, vfp_scratch1
);
3650 ARM_TOSIZD (code
, vfp_scratch1
, sreg
);
3652 ARM_TOUIZD (code
, vfp_scratch1
, sreg
);
3653 ARM_FMRS (code
, dreg
, vfp_scratch1
);
3654 code
= mono_arm_emit_vfp_scratch_restore (cfg
, code
, vfp_scratch1
);
3658 ARM_AND_REG_IMM8 (code
, dreg
, dreg
, 0xff);
3659 else if (size
== 2) {
3660 ARM_SHL_IMM (code
, dreg
, dreg
, 16);
3661 ARM_SHR_IMM (code
, dreg
, dreg
, 16);
3665 ARM_SHL_IMM (code
, dreg
, dreg
, 24);
3666 ARM_SAR_IMM (code
, dreg
, dreg
, 24);
3667 } else if (size
== 2) {
3668 ARM_SHL_IMM (code
, dreg
, dreg
, 16);
3669 ARM_SAR_IMM (code
, dreg
, dreg
, 16);
3676 emit_r4_to_int (MonoCompile
*cfg
, guchar
*code
, int dreg
, int sreg
, int size
, gboolean is_signed
)
3678 /* sreg is a float, dreg is an integer reg */
3680 code
= mono_arm_emit_vfp_scratch_save (cfg
, code
, vfp_scratch1
);
3682 ARM_TOSIZS (code
, vfp_scratch1
, sreg
);
3684 ARM_TOUIZS (code
, vfp_scratch1
, sreg
);
3685 ARM_FMRS (code
, dreg
, vfp_scratch1
);
3686 code
= mono_arm_emit_vfp_scratch_restore (cfg
, code
, vfp_scratch1
);
3690 ARM_AND_REG_IMM8 (code
, dreg
, dreg
, 0xff);
3691 else if (size
== 2) {
3692 ARM_SHL_IMM (code
, dreg
, dreg
, 16);
3693 ARM_SHR_IMM (code
, dreg
, dreg
, 16);
3697 ARM_SHL_IMM (code
, dreg
, dreg
, 24);
3698 ARM_SAR_IMM (code
, dreg
, dreg
, 24);
3699 } else if (size
== 2) {
3700 ARM_SHL_IMM (code
, dreg
, dreg
, 16);
3701 ARM_SAR_IMM (code
, dreg
, dreg
, 16);
3707 #endif /* #ifndef DISABLE_JIT */
3709 #define is_call_imm(diff) ((gint)(diff) >= -33554432 && (gint)(diff) <= 33554431)
3712 emit_thunk (guint8
*code
, gconstpointer target
)
3716 ARM_LDR_IMM (code
, ARMREG_IP
, ARMREG_PC
, 0);
3717 if (thumb_supported
)
3718 ARM_BX (code
, ARMREG_IP
);
3720 ARM_MOV_REG_REG (code
, ARMREG_PC
, ARMREG_IP
);
3721 *(guint32
*)code
= (guint32
)target
;
3723 mono_arch_flush_icache (p
, code
- p
);
3727 handle_thunk (MonoCompile
*cfg
, MonoDomain
*domain
, guchar
*code
, const guchar
*target
)
3729 MonoJitInfo
*ji
= NULL
;
3730 MonoThunkJitInfo
*info
;
3733 guint8
*orig_target
;
3734 guint8
*target_thunk
;
3737 domain
= mono_domain_get ();
3741 * This can be called multiple times during JITting,
3742 * save the current position in cfg->arch to avoid
3743 * doing a O(n^2) search.
3745 if (!cfg
->arch
.thunks
) {
3746 cfg
->arch
.thunks
= cfg
->thunks
;
3747 cfg
->arch
.thunks_size
= cfg
->thunk_area
;
3749 thunks
= cfg
->arch
.thunks
;
3750 thunks_size
= cfg
->arch
.thunks_size
;
3752 g_print ("thunk failed %p->%p, thunk space=%d method %s", code
, target
, thunks_size
, mono_method_full_name (cfg
->method
, TRUE
));
3753 g_assert_not_reached ();
3756 g_assert (*(guint32
*)thunks
== 0);
3757 emit_thunk (thunks
, target
);
3758 arm_patch (code
, thunks
);
3760 cfg
->arch
.thunks
+= THUNK_SIZE
;
3761 cfg
->arch
.thunks_size
-= THUNK_SIZE
;
3763 ji
= mini_jit_info_table_find (domain
, (char*)code
, NULL
);
3765 info
= mono_jit_info_get_thunk_info (ji
);
3768 thunks
= (guint8
*)ji
->code_start
+ info
->thunks_offset
;
3769 thunks_size
= info
->thunks_size
;
3771 orig_target
= mono_arch_get_call_target (code
+ 4);
3773 mono_mini_arch_lock ();
3775 target_thunk
= NULL
;
3776 if (orig_target
>= thunks
&& orig_target
< thunks
+ thunks_size
) {
3777 /* The call already points to a thunk, because of trampolines etc. */
3778 target_thunk
= orig_target
;
3780 for (p
= thunks
; p
< thunks
+ thunks_size
; p
+= THUNK_SIZE
) {
3781 if (((guint32
*)p
) [0] == 0) {
3785 } else if (((guint32
*)p
) [2] == (guint32
)target
) {
3786 /* Thunk already points to target */
3793 //g_print ("THUNK: %p %p %p\n", code, target, target_thunk);
3795 if (!target_thunk
) {
3796 mono_mini_arch_unlock ();
3797 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
));
3798 g_assert_not_reached ();
3801 emit_thunk (target_thunk
, target
);
3802 arm_patch (code
, target_thunk
);
3803 mono_arch_flush_icache (code
, 4);
3805 mono_mini_arch_unlock ();
3810 arm_patch_general (MonoCompile
*cfg
, MonoDomain
*domain
, guchar
*code
, const guchar
*target
)
3812 guint32
*code32
= (void*)code
;
3813 guint32 ins
= *code32
;
3814 guint32 prim
= (ins
>> 25) & 7;
3815 guint32 tval
= GPOINTER_TO_UINT (target
);
3817 //g_print ("patching 0x%08x (0x%08x) to point to 0x%08x\n", code, ins, target);
3818 if (prim
== 5) { /* 101b */
3819 /* the diff starts 8 bytes from the branch opcode */
3820 gint diff
= target
- code
- 8;
3822 gint tmask
= 0xffffffff;
3823 if (tval
& 1) { /* entering thumb mode */
3824 diff
= target
- 1 - code
- 8;
3825 g_assert (thumb_supported
);
3826 tbits
= 0xf << 28; /* bl->blx bit pattern */
3827 g_assert ((ins
& (1 << 24))); /* it must be a bl, not b instruction */
3828 /* this low bit of the displacement is moved to bit 24 in the instruction encoding */
3832 tmask
= ~(1 << 24); /* clear the link bit */
3833 /*g_print ("blx to thumb: target: %p, code: %p, diff: %d, mask: %x\n", target, code, diff, tmask);*/
3838 if (diff
<= 33554431) {
3840 ins
= (ins
& 0xff000000) | diff
;
3842 *code32
= ins
| tbits
;
3846 /* diff between 0 and -33554432 */
3847 if (diff
>= -33554432) {
3849 ins
= (ins
& 0xff000000) | (diff
& ~0xff000000);
3851 *code32
= ins
| tbits
;
3856 handle_thunk (cfg
, domain
, code
, target
);
3861 * The alternative call sequences looks like this:
3863 * ldr ip, [pc] // loads the address constant
3864 * b 1f // jumps around the constant
3865 * address constant embedded in the code
3870 * There are two cases for patching:
3871 * a) at the end of method emission: in this case code points to the start
3872 * of the call sequence
3873 * b) during runtime patching of the call site: in this case code points
3874 * to the mov pc, ip instruction
3876 * We have to handle also the thunk jump code sequence:
3880 * address constant // execution never reaches here
3882 if ((ins
& 0x0ffffff0) == 0x12fff10) {
3883 /* Branch and exchange: the address is constructed in a reg
3884 * We can patch BX when the code sequence is the following:
3885 * ldr ip, [pc, #0] ; 0x8
3892 guint8
*emit
= (guint8
*)ccode
;
3893 ARM_LDR_IMM (emit
, ARMREG_IP
, ARMREG_PC
, 0);
3895 ARM_MOV_REG_REG (emit
, ARMREG_LR
, ARMREG_PC
);
3896 ARM_BX (emit
, ARMREG_IP
);
3898 /*patching from magic trampoline*/
3899 if (ins
== ccode
[3]) {
3900 g_assert (code32
[-4] == ccode
[0]);
3901 g_assert (code32
[-3] == ccode
[1]);
3902 g_assert (code32
[-1] == ccode
[2]);
3903 code32
[-2] = (guint32
)target
;
3906 /*patching from JIT*/
3907 if (ins
== ccode
[0]) {
3908 g_assert (code32
[1] == ccode
[1]);
3909 g_assert (code32
[3] == ccode
[2]);
3910 g_assert (code32
[4] == ccode
[3]);
3911 code32
[2] = (guint32
)target
;
3914 g_assert_not_reached ();
3915 } else if ((ins
& 0x0ffffff0) == 0x12fff30) {
3923 guint8
*emit
= (guint8
*)ccode
;
3924 ARM_LDR_IMM (emit
, ARMREG_IP
, ARMREG_PC
, 0);
3926 ARM_BLX_REG (emit
, ARMREG_IP
);
3928 g_assert (code32
[-3] == ccode
[0]);
3929 g_assert (code32
[-2] == ccode
[1]);
3930 g_assert (code32
[0] == ccode
[2]);
3932 code32
[-1] = (guint32
)target
;
3935 guint32
*tmp
= ccode
;
3936 guint8
*emit
= (guint8
*)tmp
;
3937 ARM_LDR_IMM (emit
, ARMREG_IP
, ARMREG_PC
, 0);
3938 ARM_MOV_REG_REG (emit
, ARMREG_LR
, ARMREG_PC
);
3939 ARM_MOV_REG_REG (emit
, ARMREG_PC
, ARMREG_IP
);
3940 ARM_BX (emit
, ARMREG_IP
);
3941 if (ins
== ccode
[2]) {
3942 g_assert_not_reached (); // should be -2 ...
3943 code32
[-1] = (guint32
)target
;
3946 if (ins
== ccode
[0]) {
3947 /* handles both thunk jump code and the far call sequence */
3948 code32
[2] = (guint32
)target
;
3951 g_assert_not_reached ();
3953 // g_print ("patched with 0x%08x\n", ins);
3957 arm_patch (guchar
*code
, const guchar
*target
)
3959 arm_patch_general (NULL
, NULL
, code
, target
);
3963 * Return the >= 0 uimm8 value if val can be represented with a byte + rotation
3964 * (with the rotation amount in *rot_amount. rot_amount is already adjusted
3965 * to be used with the emit macros.
3966 * Return -1 otherwise.
3969 mono_arm_is_rotated_imm8 (guint32 val
, gint
*rot_amount
)
3972 for (i
= 0; i
< 31; i
+= 2) {
3973 res
= (val
<< (32 - i
)) | (val
>> i
);
3976 *rot_amount
= i
? 32 - i
: 0;
3983 * Emits in code a sequence of instructions that load the value 'val'
3984 * into the dreg register. Uses at most 4 instructions.
3987 mono_arm_emit_load_imm (guint8
*code
, int dreg
, guint32 val
)
3989 int imm8
, rot_amount
;
3991 ARM_LDR_IMM (code
, dreg
, ARMREG_PC
, 0);
3992 /* skip the constant pool */
3998 if (mini_get_debug_options()->single_imm_size
&& v7_supported
) {
3999 ARM_MOVW_REG_IMM (code
, dreg
, val
& 0xffff);
4000 ARM_MOVT_REG_IMM (code
, dreg
, (val
>> 16) & 0xffff);
4004 if ((imm8
= mono_arm_is_rotated_imm8 (val
, &rot_amount
)) >= 0) {
4005 ARM_MOV_REG_IMM (code
, dreg
, imm8
, rot_amount
);
4006 } else if ((imm8
= mono_arm_is_rotated_imm8 (~val
, &rot_amount
)) >= 0) {
4007 ARM_MVN_REG_IMM (code
, dreg
, imm8
, rot_amount
);
4010 ARM_MOVW_REG_IMM (code
, dreg
, val
& 0xffff);
4012 ARM_MOVT_REG_IMM (code
, dreg
, (val
>> 16) & 0xffff);
4016 ARM_MOV_REG_IMM8 (code
, dreg
, (val
& 0xFF));
4018 ARM_ADD_REG_IMM (code
, dreg
, dreg
, (val
& 0xFF00) >> 8, 24);
4020 if (val
& 0xFF0000) {
4021 ARM_ADD_REG_IMM (code
, dreg
, dreg
, (val
& 0xFF0000) >> 16, 16);
4023 if (val
& 0xFF000000) {
4024 ARM_ADD_REG_IMM (code
, dreg
, dreg
, (val
& 0xFF000000) >> 24, 8);
4026 } else if (val
& 0xFF00) {
4027 ARM_MOV_REG_IMM (code
, dreg
, (val
& 0xFF00) >> 8, 24);
4028 if (val
& 0xFF0000) {
4029 ARM_ADD_REG_IMM (code
, dreg
, dreg
, (val
& 0xFF0000) >> 16, 16);
4031 if (val
& 0xFF000000) {
4032 ARM_ADD_REG_IMM (code
, dreg
, dreg
, (val
& 0xFF000000) >> 24, 8);
4034 } else if (val
& 0xFF0000) {
4035 ARM_MOV_REG_IMM (code
, dreg
, (val
& 0xFF0000) >> 16, 16);
4036 if (val
& 0xFF000000) {
4037 ARM_ADD_REG_IMM (code
, dreg
, dreg
, (val
& 0xFF000000) >> 24, 8);
4040 //g_assert_not_reached ();
4046 mono_arm_thumb_supported (void)
4048 return thumb_supported
;
4052 mono_arm_eabi_supported (void)
4054 return eabi_supported
;
4058 mono_arm_i8_align (void)
4066 emit_move_return_value (MonoCompile
*cfg
, MonoInst
*ins
, guint8
*code
)
4071 call
= (MonoCallInst
*)ins
;
4072 cinfo
= call
->call_info
;
4074 switch (cinfo
->ret
.storage
) {
4075 case RegTypeStructByVal
:
4077 MonoInst
*loc
= cfg
->arch
.vret_addr_loc
;
4080 if (cinfo
->ret
.storage
== RegTypeStructByVal
&& cinfo
->ret
.nregs
== 1) {
4081 /* The JIT treats this as a normal call */
4085 /* Load the destination address */
4086 g_assert (loc
&& loc
->opcode
== OP_REGOFFSET
);
4088 if (arm_is_imm12 (loc
->inst_offset
)) {
4089 ARM_LDR_IMM (code
, ARMREG_LR
, loc
->inst_basereg
, loc
->inst_offset
);
4091 code
= mono_arm_emit_load_imm (code
, ARMREG_LR
, loc
->inst_offset
);
4092 ARM_LDR_REG_REG (code
, ARMREG_LR
, loc
->inst_basereg
, ARMREG_LR
);
4095 if (cinfo
->ret
.storage
== RegTypeStructByVal
) {
4096 int rsize
= cinfo
->ret
.struct_size
;
4098 for (i
= 0; i
< cinfo
->ret
.nregs
; ++i
) {
4099 g_assert (rsize
>= 0);
4104 ARM_STRB_IMM (code
, i
, ARMREG_LR
, i
* 4);
4107 ARM_STRH_IMM (code
, i
, ARMREG_LR
, i
* 4);
4110 ARM_STR_IMM (code
, i
, ARMREG_LR
, i
* 4);
4116 for (i
= 0; i
< cinfo
->ret
.nregs
; ++i
) {
4117 if (cinfo
->ret
.esize
== 4)
4118 ARM_FSTS (code
, cinfo
->ret
.reg
+ i
, ARMREG_LR
, i
* 4);
4120 ARM_FSTD (code
, cinfo
->ret
.reg
+ (i
* 2), ARMREG_LR
, i
* 8);
4129 switch (ins
->opcode
) {
4132 case OP_FCALL_MEMBASE
:
4134 MonoType
*sig_ret
= mini_get_underlying_type (((MonoCallInst
*)ins
)->signature
->ret
);
4135 if (sig_ret
->type
== MONO_TYPE_R4
) {
4136 if (IS_HARD_FLOAT
) {
4137 ARM_CVTS (code
, ins
->dreg
, ARM_VFP_F0
);
4139 ARM_FMSR (code
, ins
->dreg
, ARMREG_R0
);
4140 ARM_CVTS (code
, ins
->dreg
, ins
->dreg
);
4143 if (IS_HARD_FLOAT
) {
4144 ARM_CPYD (code
, ins
->dreg
, ARM_VFP_D0
);
4146 ARM_FMDRR (code
, ARMREG_R0
, ARMREG_R1
, ins
->dreg
);
4153 case OP_RCALL_MEMBASE
: {
4158 sig_ret
= mini_get_underlying_type (((MonoCallInst
*)ins
)->signature
->ret
);
4159 g_assert (sig_ret
->type
== MONO_TYPE_R4
);
4160 if (IS_HARD_FLOAT
) {
4161 ARM_CPYS (code
, ins
->dreg
, ARM_VFP_F0
);
4163 ARM_FMSR (code
, ins
->dreg
, ARMREG_R0
);
4164 ARM_CPYS (code
, ins
->dreg
, ins
->dreg
);
4176 mono_arch_output_basic_block (MonoCompile
*cfg
, MonoBasicBlock
*bb
)
4181 guint8
*code
= cfg
->native_code
+ cfg
->code_len
;
4182 MonoInst
*last_ins
= NULL
;
4183 guint last_offset
= 0;
4185 int imm8
, rot_amount
;
4187 /* we don't align basic blocks of loops on arm */
4189 if (cfg
->verbose_level
> 2)
4190 g_print ("Basic block %d starting at offset 0x%x\n", bb
->block_num
, bb
->native_offset
);
4192 cpos
= bb
->max_offset
;
4194 if (mono_break_at_bb_method
&& mono_method_desc_full_match (mono_break_at_bb_method
, cfg
->method
) && bb
->block_num
== mono_break_at_bb_bb_num
) {
4195 mono_add_patch_info (cfg
, code
- cfg
->native_code
, MONO_PATCH_INFO_INTERNAL_METHOD
,
4196 (gpointer
)"mono_break");
4197 code
= emit_call_seq (cfg
, code
);
4200 MONO_BB_FOR_EACH_INS (bb
, ins
) {
4201 offset
= code
- cfg
->native_code
;
4203 max_len
= ((guint8
*)ins_get_spec (ins
->opcode
))[MONO_INST_LEN
];
4205 if (offset
> (cfg
->code_size
- max_len
- 16)) {
4206 cfg
->code_size
*= 2;
4207 cfg
->native_code
= g_realloc (cfg
->native_code
, cfg
->code_size
);
4208 code
= cfg
->native_code
+ offset
;
4210 // if (ins->cil_code)
4211 // g_print ("cil code\n");
4212 mono_debug_record_line_number (cfg
, ins
, offset
);
4214 switch (ins
->opcode
) {
4215 case OP_MEMORY_BARRIER
:
4217 ARM_MOV_REG_IMM8 (code
, ARMREG_R0
, 0);
4218 ARM_MCR (code
, 15, 0, ARMREG_R0
, 7, 10, 5);
4222 code
= emit_tls_get (code
, ins
->dreg
, ins
->inst_offset
);
4225 code
= emit_tls_set (code
, ins
->sreg1
, ins
->inst_offset
);
4227 case OP_ATOMIC_EXCHANGE_I4
:
4228 case OP_ATOMIC_CAS_I4
:
4229 case OP_ATOMIC_ADD_I4
: {
4233 g_assert (v7_supported
);
4236 if (ins
->sreg1
!= ARMREG_IP
&& ins
->sreg2
!= ARMREG_IP
&& ins
->sreg3
!= ARMREG_IP
)
4238 else if (ins
->sreg1
!= ARMREG_R0
&& ins
->sreg2
!= ARMREG_R0
&& ins
->sreg3
!= ARMREG_R0
)
4240 else if (ins
->sreg1
!= ARMREG_R1
&& ins
->sreg2
!= ARMREG_R1
&& ins
->sreg3
!= ARMREG_R1
)
4244 g_assert (cfg
->arch
.atomic_tmp_offset
!= -1);
4245 ARM_STR_IMM (code
, tmpreg
, cfg
->frame_reg
, cfg
->arch
.atomic_tmp_offset
);
4247 switch (ins
->opcode
) {
4248 case OP_ATOMIC_EXCHANGE_I4
:
4250 ARM_DMB (code
, ARM_DMB_SY
);
4251 ARM_LDREX_REG (code
, ARMREG_LR
, ins
->sreg1
);
4252 ARM_STREX_REG (code
, tmpreg
, ins
->sreg2
, ins
->sreg1
);
4253 ARM_CMP_REG_IMM (code
, tmpreg
, 0, 0);
4255 ARM_B_COND (code
, ARMCOND_NE
, 0);
4256 arm_patch (buf
[1], buf
[0]);
4258 case OP_ATOMIC_CAS_I4
:
4259 ARM_DMB (code
, ARM_DMB_SY
);
4261 ARM_LDREX_REG (code
, ARMREG_LR
, ins
->sreg1
);
4262 ARM_CMP_REG_REG (code
, ARMREG_LR
, ins
->sreg3
);
4264 ARM_B_COND (code
, ARMCOND_NE
, 0);
4265 ARM_STREX_REG (code
, tmpreg
, ins
->sreg2
, ins
->sreg1
);
4266 ARM_CMP_REG_IMM (code
, tmpreg
, 0, 0);
4268 ARM_B_COND (code
, ARMCOND_NE
, 0);
4269 arm_patch (buf
[2], buf
[0]);
4270 arm_patch (buf
[1], code
);
4272 case OP_ATOMIC_ADD_I4
:
4274 ARM_DMB (code
, ARM_DMB_SY
);
4275 ARM_LDREX_REG (code
, ARMREG_LR
, ins
->sreg1
);
4276 ARM_ADD_REG_REG (code
, ARMREG_LR
, ARMREG_LR
, ins
->sreg2
);
4277 ARM_STREX_REG (code
, tmpreg
, ARMREG_LR
, ins
->sreg1
);
4278 ARM_CMP_REG_IMM (code
, tmpreg
, 0, 0);
4280 ARM_B_COND (code
, ARMCOND_NE
, 0);
4281 arm_patch (buf
[1], buf
[0]);
4284 g_assert_not_reached ();
4287 ARM_DMB (code
, ARM_DMB_SY
);
4288 if (tmpreg
!= ins
->dreg
)
4289 ARM_LDR_IMM (code
, tmpreg
, cfg
->frame_reg
, cfg
->arch
.atomic_tmp_offset
);
4290 ARM_MOV_REG_REG (code
, ins
->dreg
, ARMREG_LR
);
4293 case OP_ATOMIC_LOAD_I1
:
4294 case OP_ATOMIC_LOAD_U1
:
4295 case OP_ATOMIC_LOAD_I2
:
4296 case OP_ATOMIC_LOAD_U2
:
4297 case OP_ATOMIC_LOAD_I4
:
4298 case OP_ATOMIC_LOAD_U4
:
4299 case OP_ATOMIC_LOAD_R4
:
4300 case OP_ATOMIC_LOAD_R8
: {
4301 if (ins
->backend
.memory_barrier_kind
== MONO_MEMORY_BARRIER_SEQ
)
4302 ARM_DMB (code
, ARM_DMB_SY
);
4304 code
= mono_arm_emit_load_imm (code
, ARMREG_LR
, ins
->inst_offset
);
4306 switch (ins
->opcode
) {
4307 case OP_ATOMIC_LOAD_I1
:
4308 ARM_LDRSB_REG_REG (code
, ins
->dreg
, ins
->inst_basereg
, ARMREG_LR
);
4310 case OP_ATOMIC_LOAD_U1
:
4311 ARM_LDRB_REG_REG (code
, ins
->dreg
, ins
->inst_basereg
, ARMREG_LR
);
4313 case OP_ATOMIC_LOAD_I2
:
4314 ARM_LDRSH_REG_REG (code
, ins
->dreg
, ins
->inst_basereg
, ARMREG_LR
);
4316 case OP_ATOMIC_LOAD_U2
:
4317 ARM_LDRH_REG_REG (code
, ins
->dreg
, ins
->inst_basereg
, ARMREG_LR
);
4319 case OP_ATOMIC_LOAD_I4
:
4320 case OP_ATOMIC_LOAD_U4
:
4321 ARM_LDR_REG_REG (code
, ins
->dreg
, ins
->inst_basereg
, ARMREG_LR
);
4323 case OP_ATOMIC_LOAD_R4
:
4325 ARM_ADD_REG_REG (code
, ARMREG_LR
, ins
->inst_basereg
, ARMREG_LR
);
4326 ARM_FLDS (code
, ins
->dreg
, ARMREG_LR
, 0);
4328 code
= mono_arm_emit_vfp_scratch_save (cfg
, code
, vfp_scratch1
);
4329 ARM_ADD_REG_REG (code
, ARMREG_LR
, ins
->inst_basereg
, ARMREG_LR
);
4330 ARM_FLDS (code
, vfp_scratch1
, ARMREG_LR
, 0);
4331 ARM_CVTS (code
, ins
->dreg
, vfp_scratch1
);
4332 code
= mono_arm_emit_vfp_scratch_restore (cfg
, code
, vfp_scratch1
);
4335 case OP_ATOMIC_LOAD_R8
:
4336 ARM_ADD_REG_REG (code
, ARMREG_LR
, ins
->inst_basereg
, ARMREG_LR
);
4337 ARM_FLDD (code
, ins
->dreg
, ARMREG_LR
, 0);
4341 if (ins
->backend
.memory_barrier_kind
!= MONO_MEMORY_BARRIER_NONE
)
4342 ARM_DMB (code
, ARM_DMB_SY
);
4345 case OP_ATOMIC_STORE_I1
:
4346 case OP_ATOMIC_STORE_U1
:
4347 case OP_ATOMIC_STORE_I2
:
4348 case OP_ATOMIC_STORE_U2
:
4349 case OP_ATOMIC_STORE_I4
:
4350 case OP_ATOMIC_STORE_U4
:
4351 case OP_ATOMIC_STORE_R4
:
4352 case OP_ATOMIC_STORE_R8
: {
4353 if (ins
->backend
.memory_barrier_kind
!= MONO_MEMORY_BARRIER_NONE
)
4354 ARM_DMB (code
, ARM_DMB_SY
);
4356 code
= mono_arm_emit_load_imm (code
, ARMREG_LR
, ins
->inst_offset
);
4358 switch (ins
->opcode
) {
4359 case OP_ATOMIC_STORE_I1
:
4360 case OP_ATOMIC_STORE_U1
:
4361 ARM_STRB_REG_REG (code
, ins
->sreg1
, ins
->inst_destbasereg
, ARMREG_LR
);
4363 case OP_ATOMIC_STORE_I2
:
4364 case OP_ATOMIC_STORE_U2
:
4365 ARM_STRH_REG_REG (code
, ins
->sreg1
, ins
->inst_destbasereg
, ARMREG_LR
);
4367 case OP_ATOMIC_STORE_I4
:
4368 case OP_ATOMIC_STORE_U4
:
4369 ARM_STR_REG_REG (code
, ins
->sreg1
, ins
->inst_destbasereg
, ARMREG_LR
);
4371 case OP_ATOMIC_STORE_R4
:
4373 ARM_ADD_REG_REG (code
, ARMREG_LR
, ins
->inst_destbasereg
, ARMREG_LR
);
4374 ARM_FSTS (code
, ins
->sreg1
, ARMREG_LR
, 0);
4376 code
= mono_arm_emit_vfp_scratch_save (cfg
, code
, vfp_scratch1
);
4377 ARM_ADD_REG_REG (code
, ARMREG_LR
, ins
->inst_destbasereg
, ARMREG_LR
);
4378 ARM_CVTD (code
, vfp_scratch1
, ins
->sreg1
);
4379 ARM_FSTS (code
, vfp_scratch1
, ARMREG_LR
, 0);
4380 code
= mono_arm_emit_vfp_scratch_restore (cfg
, code
, vfp_scratch1
);
4383 case OP_ATOMIC_STORE_R8
:
4384 ARM_ADD_REG_REG (code
, ARMREG_LR
, ins
->inst_destbasereg
, ARMREG_LR
);
4385 ARM_FSTD (code
, ins
->sreg1
, ARMREG_LR
, 0);
4389 if (ins
->backend
.memory_barrier_kind
== MONO_MEMORY_BARRIER_SEQ
)
4390 ARM_DMB (code
, ARM_DMB_SY
);
4394 ARM_SMULL_REG_REG (code
, ins
->backend
.reg3
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4397 ARM_UMULL_REG_REG (code
, ins
->backend
.reg3
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4399 case OP_STOREI1_MEMBASE_IMM
:
4400 code
= mono_arm_emit_load_imm (code
, ARMREG_LR
, ins
->inst_imm
& 0xFF);
4401 g_assert (arm_is_imm12 (ins
->inst_offset
));
4402 ARM_STRB_IMM (code
, ARMREG_LR
, ins
->inst_destbasereg
, ins
->inst_offset
);
4404 case OP_STOREI2_MEMBASE_IMM
:
4405 code
= mono_arm_emit_load_imm (code
, ARMREG_LR
, ins
->inst_imm
& 0xFFFF);
4406 g_assert (arm_is_imm8 (ins
->inst_offset
));
4407 ARM_STRH_IMM (code
, ARMREG_LR
, ins
->inst_destbasereg
, ins
->inst_offset
);
4409 case OP_STORE_MEMBASE_IMM
:
4410 case OP_STOREI4_MEMBASE_IMM
:
4411 code
= mono_arm_emit_load_imm (code
, ARMREG_LR
, ins
->inst_imm
);
4412 g_assert (arm_is_imm12 (ins
->inst_offset
));
4413 ARM_STR_IMM (code
, ARMREG_LR
, ins
->inst_destbasereg
, ins
->inst_offset
);
4415 case OP_STOREI1_MEMBASE_REG
:
4416 g_assert (arm_is_imm12 (ins
->inst_offset
));
4417 ARM_STRB_IMM (code
, ins
->sreg1
, ins
->inst_destbasereg
, ins
->inst_offset
);
4419 case OP_STOREI2_MEMBASE_REG
:
4420 g_assert (arm_is_imm8 (ins
->inst_offset
));
4421 ARM_STRH_IMM (code
, ins
->sreg1
, ins
->inst_destbasereg
, ins
->inst_offset
);
4423 case OP_STORE_MEMBASE_REG
:
4424 case OP_STOREI4_MEMBASE_REG
:
4425 /* this case is special, since it happens for spill code after lowering has been called */
4426 if (arm_is_imm12 (ins
->inst_offset
)) {
4427 ARM_STR_IMM (code
, ins
->sreg1
, ins
->inst_destbasereg
, ins
->inst_offset
);
4429 code
= mono_arm_emit_load_imm (code
, ARMREG_LR
, ins
->inst_offset
);
4430 ARM_STR_REG_REG (code
, ins
->sreg1
, ins
->inst_destbasereg
, ARMREG_LR
);
4433 case OP_STOREI1_MEMINDEX
:
4434 ARM_STRB_REG_REG (code
, ins
->sreg1
, ins
->inst_destbasereg
, ins
->sreg2
);
4436 case OP_STOREI2_MEMINDEX
:
4437 ARM_STRH_REG_REG (code
, ins
->sreg1
, ins
->inst_destbasereg
, ins
->sreg2
);
4439 case OP_STORE_MEMINDEX
:
4440 case OP_STOREI4_MEMINDEX
:
4441 ARM_STR_REG_REG (code
, ins
->sreg1
, ins
->inst_destbasereg
, ins
->sreg2
);
4444 g_assert_not_reached ();
4446 case OP_LOAD_MEMINDEX
:
4447 case OP_LOADI4_MEMINDEX
:
4448 case OP_LOADU4_MEMINDEX
:
4449 ARM_LDR_REG_REG (code
, ins
->dreg
, ins
->inst_basereg
, ins
->sreg2
);
4451 case OP_LOADI1_MEMINDEX
:
4452 ARM_LDRSB_REG_REG (code
, ins
->dreg
, ins
->inst_basereg
, ins
->sreg2
);
4454 case OP_LOADU1_MEMINDEX
:
4455 ARM_LDRB_REG_REG (code
, ins
->dreg
, ins
->inst_basereg
, ins
->sreg2
);
4457 case OP_LOADI2_MEMINDEX
:
4458 ARM_LDRSH_REG_REG (code
, ins
->dreg
, ins
->inst_basereg
, ins
->sreg2
);
4460 case OP_LOADU2_MEMINDEX
:
4461 ARM_LDRH_REG_REG (code
, ins
->dreg
, ins
->inst_basereg
, ins
->sreg2
);
4463 case OP_LOAD_MEMBASE
:
4464 case OP_LOADI4_MEMBASE
:
4465 case OP_LOADU4_MEMBASE
:
4466 /* this case is special, since it happens for spill code after lowering has been called */
4467 if (arm_is_imm12 (ins
->inst_offset
)) {
4468 ARM_LDR_IMM (code
, ins
->dreg
, ins
->inst_basereg
, ins
->inst_offset
);
4470 code
= mono_arm_emit_load_imm (code
, ARMREG_LR
, ins
->inst_offset
);
4471 ARM_LDR_REG_REG (code
, ins
->dreg
, ins
->inst_basereg
, ARMREG_LR
);
4474 case OP_LOADI1_MEMBASE
:
4475 g_assert (arm_is_imm8 (ins
->inst_offset
));
4476 ARM_LDRSB_IMM (code
, ins
->dreg
, ins
->inst_basereg
, ins
->inst_offset
);
4478 case OP_LOADU1_MEMBASE
:
4479 g_assert (arm_is_imm12 (ins
->inst_offset
));
4480 ARM_LDRB_IMM (code
, ins
->dreg
, ins
->inst_basereg
, ins
->inst_offset
);
4482 case OP_LOADU2_MEMBASE
:
4483 g_assert (arm_is_imm8 (ins
->inst_offset
));
4484 ARM_LDRH_IMM (code
, ins
->dreg
, ins
->inst_basereg
, ins
->inst_offset
);
4486 case OP_LOADI2_MEMBASE
:
4487 g_assert (arm_is_imm8 (ins
->inst_offset
));
4488 ARM_LDRSH_IMM (code
, ins
->dreg
, ins
->inst_basereg
, ins
->inst_offset
);
4490 case OP_ICONV_TO_I1
:
4491 ARM_SHL_IMM (code
, ins
->dreg
, ins
->sreg1
, 24);
4492 ARM_SAR_IMM (code
, ins
->dreg
, ins
->dreg
, 24);
4494 case OP_ICONV_TO_I2
:
4495 ARM_SHL_IMM (code
, ins
->dreg
, ins
->sreg1
, 16);
4496 ARM_SAR_IMM (code
, ins
->dreg
, ins
->dreg
, 16);
4498 case OP_ICONV_TO_U1
:
4499 ARM_AND_REG_IMM8 (code
, ins
->dreg
, ins
->sreg1
, 0xff);
4501 case OP_ICONV_TO_U2
:
4502 ARM_SHL_IMM (code
, ins
->dreg
, ins
->sreg1
, 16);
4503 ARM_SHR_IMM (code
, ins
->dreg
, ins
->dreg
, 16);
4507 ARM_CMP_REG_REG (code
, ins
->sreg1
, ins
->sreg2
);
4509 case OP_COMPARE_IMM
:
4510 case OP_ICOMPARE_IMM
:
4511 imm8
= mono_arm_is_rotated_imm8 (ins
->inst_imm
, &rot_amount
);
4512 g_assert (imm8
>= 0);
4513 ARM_CMP_REG_IMM (code
, ins
->sreg1
, imm8
, rot_amount
);
4517 * gdb does not like encountering the hw breakpoint ins in the debugged code.
4518 * So instead of emitting a trap, we emit a call a C function and place a
4521 //*(int*)code = 0xef9f0001;
4524 mono_add_patch_info (cfg
, code
- cfg
->native_code
, MONO_PATCH_INFO_INTERNAL_METHOD
,
4525 (gpointer
)"mono_break");
4526 code
= emit_call_seq (cfg
, code
);
4528 case OP_RELAXED_NOP
:
4533 case OP_DUMMY_STORE
:
4534 case OP_DUMMY_ICONST
:
4535 case OP_DUMMY_R8CONST
:
4536 case OP_NOT_REACHED
:
4539 case OP_IL_SEQ_POINT
:
4540 mono_add_seq_point (cfg
, bb
, ins
, code
- cfg
->native_code
);
4542 case OP_SEQ_POINT
: {
4544 MonoInst
*info_var
= cfg
->arch
.seq_point_info_var
;
4545 MonoInst
*ss_trigger_page_var
= cfg
->arch
.ss_trigger_page_var
;
4546 MonoInst
*ss_method_var
= cfg
->arch
.seq_point_ss_method_var
;
4547 MonoInst
*bp_method_var
= cfg
->arch
.seq_point_bp_method_var
;
4549 int dreg
= ARMREG_LR
;
4552 if (cfg
->soft_breakpoints
) {
4553 g_assert (!cfg
->compile_aot
);
4558 * For AOT, we use one got slot per method, which will point to a
4559 * SeqPointInfo structure, containing all the information required
4560 * by the code below.
4562 if (cfg
->compile_aot
) {
4563 g_assert (info_var
);
4564 g_assert (info_var
->opcode
== OP_REGOFFSET
);
4567 if (!cfg
->soft_breakpoints
&& !cfg
->compile_aot
) {
4569 * Read from the single stepping trigger page. This will cause a
4570 * SIGSEGV when single stepping is enabled.
4571 * We do this _before_ the breakpoint, so single stepping after
4572 * a breakpoint is hit will step to the next IL offset.
4574 g_assert (((guint64
)(gsize
)ss_trigger_page
>> 32) == 0);
4577 /* Single step check */
4578 if (ins
->flags
& MONO_INST_SINGLE_STEP_LOC
) {
4579 if (cfg
->soft_breakpoints
) {
4580 /* Load the address of the sequence point method variable. */
4581 var
= ss_method_var
;
4583 g_assert (var
->opcode
== OP_REGOFFSET
);
4584 code
= emit_ldr_imm (code
, dreg
, var
->inst_basereg
, var
->inst_offset
);
4585 /* Read the value and check whether it is non-zero. */
4586 ARM_LDR_IMM (code
, dreg
, dreg
, 0);
4587 ARM_CMP_REG_IMM (code
, dreg
, 0, 0);
4588 /* Call it conditionally. */
4589 ARM_BLX_REG_COND (code
, ARMCOND_NE
, dreg
);
4591 if (cfg
->compile_aot
) {
4592 /* Load the trigger page addr from the variable initialized in the prolog */
4593 var
= ss_trigger_page_var
;
4595 g_assert (var
->opcode
== OP_REGOFFSET
);
4596 code
= emit_ldr_imm (code
, dreg
, var
->inst_basereg
, var
->inst_offset
);
4598 ARM_LDR_IMM (code
, dreg
, ARMREG_PC
, 0);
4600 *(int*)code
= (int)ss_trigger_page
;
4603 ARM_LDR_IMM (code
, dreg
, dreg
, 0);
4607 mono_add_seq_point (cfg
, bb
, ins
, code
- cfg
->native_code
);
4609 /* Breakpoint check */
4610 if (cfg
->compile_aot
) {
4611 guint32 offset
= code
- cfg
->native_code
;
4615 code
= emit_ldr_imm (code
, dreg
, var
->inst_basereg
, var
->inst_offset
);
4616 /* Add the offset */
4617 val
= ((offset
/ 4) * sizeof (guint8
*)) + MONO_STRUCT_OFFSET (SeqPointInfo
, bp_addrs
);
4618 /* Load the info->bp_addrs [offset], which is either 0 or the address of a trigger page */
4619 if (arm_is_imm12 ((int)val
)) {
4620 ARM_LDR_IMM (code
, dreg
, dreg
, val
);
4622 ARM_ADD_REG_IMM (code
, dreg
, dreg
, (val
& 0xFF), 0);
4624 ARM_ADD_REG_IMM (code
, dreg
, dreg
, (val
& 0xFF00) >> 8, 24);
4626 ARM_ADD_REG_IMM (code
, dreg
, dreg
, (val
& 0xFF0000) >> 16, 16);
4627 g_assert (!(val
& 0xFF000000));
4629 ARM_LDR_IMM (code
, dreg
, dreg
, 0);
4631 /* What is faster, a branch or a load ? */
4632 ARM_CMP_REG_IMM (code
, dreg
, 0, 0);
4633 /* The breakpoint instruction */
4634 if (cfg
->soft_breakpoints
)
4635 ARM_BLX_REG_COND (code
, ARMCOND_NE
, dreg
);
4637 ARM_LDR_IMM_COND (code
, dreg
, dreg
, 0, ARMCOND_NE
);
4638 } else if (cfg
->soft_breakpoints
) {
4639 /* Load the address of the breakpoint method into ip. */
4640 var
= bp_method_var
;
4642 g_assert (var
->opcode
== OP_REGOFFSET
);
4643 g_assert (arm_is_imm12 (var
->inst_offset
));
4644 ARM_LDR_IMM (code
, dreg
, var
->inst_basereg
, var
->inst_offset
);
4647 * A placeholder for a possible breakpoint inserted by
4648 * mono_arch_set_breakpoint ().
4653 * A placeholder for a possible breakpoint inserted by
4654 * mono_arch_set_breakpoint ().
4656 for (i
= 0; i
< 4; ++i
)
4661 * Add an additional nop so skipping the bp doesn't cause the ip to point
4662 * to another IL offset.
4670 ARM_ADDS_REG_REG (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4673 ARM_ADD_REG_REG (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4677 ARM_ADCS_REG_REG (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4680 imm8
= mono_arm_is_rotated_imm8 (ins
->inst_imm
, &rot_amount
);
4681 g_assert (imm8
>= 0);
4682 ARM_ADDS_REG_IMM (code
, ins
->dreg
, ins
->sreg1
, imm8
, rot_amount
);
4686 imm8
= mono_arm_is_rotated_imm8 (ins
->inst_imm
, &rot_amount
);
4687 g_assert (imm8
>= 0);
4688 ARM_ADD_REG_IMM (code
, ins
->dreg
, ins
->sreg1
, imm8
, rot_amount
);
4692 imm8
= mono_arm_is_rotated_imm8 (ins
->inst_imm
, &rot_amount
);
4693 g_assert (imm8
>= 0);
4694 ARM_ADCS_REG_IMM (code
, ins
->dreg
, ins
->sreg1
, imm8
, rot_amount
);
4697 ARM_ADD_REG_REG (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4698 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
4700 case OP_IADD_OVF_UN
:
4701 ARM_ADD_REG_REG (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4702 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
4705 ARM_SUB_REG_REG (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4706 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
4708 case OP_ISUB_OVF_UN
:
4709 ARM_SUB_REG_REG (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4710 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_TRUE, PPC_BR_EQ, "OverflowException");
4712 case OP_ADD_OVF_CARRY
:
4713 ARM_ADCS_REG_REG (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4714 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
4716 case OP_ADD_OVF_UN_CARRY
:
4717 ARM_ADCS_REG_REG (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4718 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
4720 case OP_SUB_OVF_CARRY
:
4721 ARM_SBCS_REG_REG (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4722 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
4724 case OP_SUB_OVF_UN_CARRY
:
4725 ARM_SBCS_REG_REG (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4726 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_TRUE, PPC_BR_EQ, "OverflowException");
4730 ARM_SUBS_REG_REG (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4733 imm8
= mono_arm_is_rotated_imm8 (ins
->inst_imm
, &rot_amount
);
4734 g_assert (imm8
>= 0);
4735 ARM_SUBS_REG_IMM (code
, ins
->dreg
, ins
->sreg1
, imm8
, rot_amount
);
4738 ARM_SUB_REG_REG (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4742 ARM_SBCS_REG_REG (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4746 imm8
= mono_arm_is_rotated_imm8 (ins
->inst_imm
, &rot_amount
);
4747 g_assert (imm8
>= 0);
4748 ARM_SUB_REG_IMM (code
, ins
->dreg
, ins
->sreg1
, imm8
, rot_amount
);
4752 imm8
= mono_arm_is_rotated_imm8 (ins
->inst_imm
, &rot_amount
);
4753 g_assert (imm8
>= 0);
4754 ARM_SBCS_REG_IMM (code
, ins
->dreg
, ins
->sreg1
, imm8
, rot_amount
);
4756 case OP_ARM_RSBS_IMM
:
4757 imm8
= mono_arm_is_rotated_imm8 (ins
->inst_imm
, &rot_amount
);
4758 g_assert (imm8
>= 0);
4759 ARM_RSBS_REG_IMM (code
, ins
->dreg
, ins
->sreg1
, imm8
, rot_amount
);
4761 case OP_ARM_RSC_IMM
:
4762 imm8
= mono_arm_is_rotated_imm8 (ins
->inst_imm
, &rot_amount
);
4763 g_assert (imm8
>= 0);
4764 ARM_RSC_REG_IMM (code
, ins
->dreg
, ins
->sreg1
, imm8
, rot_amount
);
4767 ARM_AND_REG_REG (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4771 imm8
= mono_arm_is_rotated_imm8 (ins
->inst_imm
, &rot_amount
);
4772 g_assert (imm8
>= 0);
4773 ARM_AND_REG_IMM (code
, ins
->dreg
, ins
->sreg1
, imm8
, rot_amount
);
4776 g_assert (v7s_supported
|| v7k_supported
);
4777 ARM_SDIV (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4780 g_assert (v7s_supported
|| v7k_supported
);
4781 ARM_UDIV (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4784 g_assert (v7s_supported
|| v7k_supported
);
4785 ARM_SDIV (code
, ARMREG_LR
, ins
->sreg1
, ins
->sreg2
);
4786 ARM_MLS (code
, ins
->dreg
, ARMREG_LR
, ins
->sreg2
, ins
->sreg1
);
4789 g_assert (v7s_supported
|| v7k_supported
);
4790 ARM_UDIV (code
, ARMREG_LR
, ins
->sreg1
, ins
->sreg2
);
4791 ARM_MLS (code
, ins
->dreg
, ARMREG_LR
, ins
->sreg2
, ins
->sreg1
);
4795 g_assert_not_reached ();
4797 ARM_ORR_REG_REG (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4801 imm8
= mono_arm_is_rotated_imm8 (ins
->inst_imm
, &rot_amount
);
4802 g_assert (imm8
>= 0);
4803 ARM_ORR_REG_IMM (code
, ins
->dreg
, ins
->sreg1
, imm8
, rot_amount
);
4806 ARM_EOR_REG_REG (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4810 imm8
= mono_arm_is_rotated_imm8 (ins
->inst_imm
, &rot_amount
);
4811 g_assert (imm8
>= 0);
4812 ARM_EOR_REG_IMM (code
, ins
->dreg
, ins
->sreg1
, imm8
, rot_amount
);
4815 ARM_SHL_REG (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4820 ARM_SHL_IMM (code
, ins
->dreg
, ins
->sreg1
, (ins
->inst_imm
& 0x1f));
4821 else if (ins
->dreg
!= ins
->sreg1
)
4822 ARM_MOV_REG_REG (code
, ins
->dreg
, ins
->sreg1
);
4825 ARM_SAR_REG (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4830 ARM_SAR_IMM (code
, ins
->dreg
, ins
->sreg1
, (ins
->inst_imm
& 0x1f));
4831 else if (ins
->dreg
!= ins
->sreg1
)
4832 ARM_MOV_REG_REG (code
, ins
->dreg
, ins
->sreg1
);
4835 case OP_ISHR_UN_IMM
:
4837 ARM_SHR_IMM (code
, ins
->dreg
, ins
->sreg1
, (ins
->inst_imm
& 0x1f));
4838 else if (ins
->dreg
!= ins
->sreg1
)
4839 ARM_MOV_REG_REG (code
, ins
->dreg
, ins
->sreg1
);
4842 ARM_SHR_REG (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4845 ARM_MVN_REG_REG (code
, ins
->dreg
, ins
->sreg1
);
4848 ARM_RSB_REG_IMM8 (code
, ins
->dreg
, ins
->sreg1
, 0);
4851 if (ins
->dreg
== ins
->sreg2
)
4852 ARM_MUL_REG_REG (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4854 ARM_MUL_REG_REG (code
, ins
->dreg
, ins
->sreg2
, ins
->sreg1
);
4857 g_assert_not_reached ();
4860 /* FIXME: handle ovf/ sreg2 != dreg */
4861 ARM_MUL_REG_REG (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4862 /* FIXME: MUL doesn't set the C/O flags on ARM */
4864 case OP_IMUL_OVF_UN
:
4865 /* FIXME: handle ovf/ sreg2 != dreg */
4866 ARM_MUL_REG_REG (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
4867 /* FIXME: MUL doesn't set the C/O flags on ARM */
4870 code
= mono_arm_emit_load_imm (code
, ins
->dreg
, ins
->inst_c0
);
4873 /* Load the GOT offset */
4874 mono_add_patch_info (cfg
, offset
, (MonoJumpInfoType
)ins
->inst_i1
, ins
->inst_p0
);
4875 ARM_LDR_IMM (code
, ins
->dreg
, ARMREG_PC
, 0);
4877 *(gpointer
*)code
= NULL
;
4879 /* Load the value from the GOT */
4880 ARM_LDR_REG_REG (code
, ins
->dreg
, ARMREG_PC
, ins
->dreg
);
4882 case OP_OBJC_GET_SELECTOR
:
4883 mono_add_patch_info (cfg
, offset
, MONO_PATCH_INFO_OBJC_SELECTOR_REF
, ins
->inst_p0
);
4884 ARM_LDR_IMM (code
, ins
->dreg
, ARMREG_PC
, 0);
4886 *(gpointer
*)code
= NULL
;
4888 ARM_LDR_REG_REG (code
, ins
->dreg
, ARMREG_PC
, ins
->dreg
);
4890 case OP_ICONV_TO_I4
:
4891 case OP_ICONV_TO_U4
:
4893 if (ins
->dreg
!= ins
->sreg1
)
4894 ARM_MOV_REG_REG (code
, ins
->dreg
, ins
->sreg1
);
4897 int saved
= ins
->sreg2
;
4898 if (ins
->sreg2
== ARM_LSW_REG
) {
4899 ARM_MOV_REG_REG (code
, ARMREG_LR
, ins
->sreg2
);
4902 if (ins
->sreg1
!= ARM_LSW_REG
)
4903 ARM_MOV_REG_REG (code
, ARM_LSW_REG
, ins
->sreg1
);
4904 if (saved
!= ARM_MSW_REG
)
4905 ARM_MOV_REG_REG (code
, ARM_MSW_REG
, saved
);
4909 if (IS_VFP
&& ins
->dreg
!= ins
->sreg1
)
4910 ARM_CPYD (code
, ins
->dreg
, ins
->sreg1
);
4913 if (IS_VFP
&& ins
->dreg
!= ins
->sreg1
)
4914 ARM_CPYS (code
, ins
->dreg
, ins
->sreg1
);
4916 case OP_MOVE_F_TO_I4
:
4918 ARM_FMRS (code
, ins
->dreg
, ins
->sreg1
);
4920 code
= mono_arm_emit_vfp_scratch_save (cfg
, code
, vfp_scratch1
);
4921 ARM_CVTD (code
, vfp_scratch1
, ins
->sreg1
);
4922 ARM_FMRS (code
, ins
->dreg
, vfp_scratch1
);
4923 code
= mono_arm_emit_vfp_scratch_restore (cfg
, code
, vfp_scratch1
);
4926 case OP_MOVE_I4_TO_F
:
4928 ARM_FMSR (code
, ins
->dreg
, ins
->sreg1
);
4930 ARM_FMSR (code
, ins
->dreg
, ins
->sreg1
);
4931 ARM_CVTS (code
, ins
->dreg
, ins
->dreg
);
4934 case OP_FCONV_TO_R4
:
4937 ARM_CVTD (code
, ins
->dreg
, ins
->sreg1
);
4939 ARM_CVTD (code
, ins
->dreg
, ins
->sreg1
);
4940 ARM_CVTS (code
, ins
->dreg
, ins
->dreg
);
4945 MonoCallInst
*call
= (MonoCallInst
*)ins
;
4948 * The stack looks like the following:
4949 * <caller argument area>
4952 * <callee argument area>
4953 * Need to copy the arguments from the callee argument area to
4954 * the caller argument area, and pop the frame.
4956 if (call
->stack_usage
) {
4957 int i
, prev_sp_offset
= 0;
4959 /* Compute size of saved registers restored below */
4961 prev_sp_offset
= 2 * 4;
4963 prev_sp_offset
= 1 * 4;
4964 for (i
= 0; i
< 16; ++i
) {
4965 if (cfg
->used_int_regs
& (1 << i
))
4966 prev_sp_offset
+= 4;
4969 code
= emit_big_add (code
, ARMREG_IP
, cfg
->frame_reg
, cfg
->stack_usage
+ prev_sp_offset
);
4971 /* Copy arguments on the stack to our argument area */
4972 for (i
= 0; i
< call
->stack_usage
; i
+= sizeof (mgreg_t
)) {
4973 ARM_LDR_IMM (code
, ARMREG_LR
, ARMREG_SP
, i
);
4974 ARM_STR_IMM (code
, ARMREG_LR
, ARMREG_IP
, i
);
4979 * Keep in sync with mono_arch_emit_epilog
4981 g_assert (!cfg
->method
->save_lmf
);
4983 code
= emit_big_add (code
, ARMREG_SP
, cfg
->frame_reg
, cfg
->stack_usage
);
4985 if (cfg
->used_int_regs
)
4986 ARM_POP (code
, cfg
->used_int_regs
);
4987 ARM_POP (code
, (1 << ARMREG_R7
) | (1 << ARMREG_LR
));
4989 ARM_POP (code
, cfg
->used_int_regs
| (1 << ARMREG_LR
));
4992 mono_add_patch_info (cfg
, (guint8
*) code
- cfg
->native_code
, MONO_PATCH_INFO_METHOD_JUMP
, call
->method
);
4993 if (cfg
->compile_aot
) {
4994 ARM_LDR_IMM (code
, ARMREG_IP
, ARMREG_PC
, 0);
4996 *(gpointer
*)code
= NULL
;
4998 ARM_LDR_REG_REG (code
, ARMREG_PC
, ARMREG_PC
, ARMREG_IP
);
5000 code
= mono_arm_patchable_b (code
, ARMCOND_AL
);
5001 cfg
->thunk_area
+= THUNK_SIZE
;
5006 /* ensure ins->sreg1 is not NULL */
5007 ARM_LDRB_IMM (code
, ARMREG_LR
, ins
->sreg1
, 0);
5010 g_assert (cfg
->sig_cookie
< 128);
5011 ARM_LDR_IMM (code
, ARMREG_IP
, cfg
->frame_reg
, cfg
->sig_cookie
);
5012 ARM_STR_IMM (code
, ARMREG_IP
, ins
->sreg1
, 0);
5022 call
= (MonoCallInst
*)ins
;
5025 code
= emit_float_args (cfg
, call
, code
, &max_len
, &offset
);
5027 if (ins
->flags
& MONO_INST_HAS_METHOD
)
5028 mono_add_patch_info (cfg
, offset
, MONO_PATCH_INFO_METHOD
, call
->method
);
5030 mono_add_patch_info (cfg
, offset
, MONO_PATCH_INFO_ABS
, call
->fptr
);
5031 code
= emit_call_seq (cfg
, code
);
5032 ins
->flags
|= MONO_INST_GC_CALLSITE
;
5033 ins
->backend
.pc_offset
= code
- cfg
->native_code
;
5034 code
= emit_move_return_value (cfg
, ins
, code
);
5041 case OP_VOIDCALL_REG
:
5044 code
= emit_float_args (cfg
, (MonoCallInst
*)ins
, code
, &max_len
, &offset
);
5046 code
= emit_call_reg (code
, ins
->sreg1
);
5047 ins
->flags
|= MONO_INST_GC_CALLSITE
;
5048 ins
->backend
.pc_offset
= code
- cfg
->native_code
;
5049 code
= emit_move_return_value (cfg
, ins
, code
);
5051 case OP_FCALL_MEMBASE
:
5052 case OP_RCALL_MEMBASE
:
5053 case OP_LCALL_MEMBASE
:
5054 case OP_VCALL_MEMBASE
:
5055 case OP_VCALL2_MEMBASE
:
5056 case OP_VOIDCALL_MEMBASE
:
5057 case OP_CALL_MEMBASE
: {
5058 g_assert (ins
->sreg1
!= ARMREG_LR
);
5059 call
= (MonoCallInst
*)ins
;
5062 code
= emit_float_args (cfg
, call
, code
, &max_len
, &offset
);
5063 if (!arm_is_imm12 (ins
->inst_offset
)) {
5064 /* sreg1 might be IP */
5065 ARM_MOV_REG_REG (code
, ARMREG_LR
, ins
->sreg1
);
5066 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, ins
->inst_offset
);
5067 ARM_ADD_REG_REG (code
, ARMREG_IP
, ARMREG_IP
, ARMREG_LR
);
5068 ARM_MOV_REG_REG (code
, ARMREG_LR
, ARMREG_PC
);
5069 ARM_LDR_IMM (code
, ARMREG_PC
, ARMREG_IP
, 0);
5071 ARM_MOV_REG_REG (code
, ARMREG_LR
, ARMREG_PC
);
5072 ARM_LDR_IMM (code
, ARMREG_PC
, ins
->sreg1
, ins
->inst_offset
);
5074 ins
->flags
|= MONO_INST_GC_CALLSITE
;
5075 ins
->backend
.pc_offset
= code
- cfg
->native_code
;
5076 code
= emit_move_return_value (cfg
, ins
, code
);
5079 case OP_GENERIC_CLASS_INIT
: {
5083 byte_offset
= MONO_STRUCT_OFFSET (MonoVTable
, initialized
);
5085 g_assert (arm_is_imm8 (byte_offset
));
5086 ARM_LDRSB_IMM (code
, ARMREG_IP
, ins
->sreg1
, byte_offset
);
5087 ARM_CMP_REG_IMM (code
, ARMREG_IP
, 0, 0);
5089 ARM_B_COND (code
, ARMCOND_NE
, 0);
5091 /* Uninitialized case */
5092 g_assert (ins
->sreg1
== ARMREG_R0
);
5094 mono_add_patch_info (cfg
, code
- cfg
->native_code
, MONO_PATCH_INFO_INTERNAL_METHOD
,
5095 (gpointer
)"mono_generic_class_init");
5096 code
= emit_call_seq (cfg
, code
);
5098 /* Initialized case */
5099 arm_patch (jump
, code
);
5103 /* round the size to 8 bytes */
5104 ARM_ADD_REG_IMM8 (code
, ins
->dreg
, ins
->sreg1
, (MONO_ARCH_FRAME_ALIGNMENT
- 1));
5105 ARM_BIC_REG_IMM8 (code
, ins
->dreg
, ins
->dreg
, (MONO_ARCH_FRAME_ALIGNMENT
- 1));
5106 ARM_SUB_REG_REG (code
, ARMREG_SP
, ARMREG_SP
, ins
->dreg
);
5107 /* memzero the area: dreg holds the size, sp is the pointer */
5108 if (ins
->flags
& MONO_INST_INIT
) {
5109 guint8
*start_loop
, *branch_to_cond
;
5110 ARM_MOV_REG_IMM8 (code
, ARMREG_LR
, 0);
5111 branch_to_cond
= code
;
5114 ARM_STR_REG_REG (code
, ARMREG_LR
, ARMREG_SP
, ins
->dreg
);
5115 arm_patch (branch_to_cond
, code
);
5116 /* decrement by 4 and set flags */
5117 ARM_SUBS_REG_IMM8 (code
, ins
->dreg
, ins
->dreg
, sizeof (mgreg_t
));
5118 ARM_B_COND (code
, ARMCOND_GE
, 0);
5119 arm_patch (code
- 4, start_loop
);
5121 ARM_MOV_REG_REG (code
, ins
->dreg
, ARMREG_SP
);
5122 if (cfg
->param_area
)
5123 code
= emit_sub_imm (code
, ARMREG_SP
, ARMREG_SP
, ALIGN_TO (cfg
->param_area
, MONO_ARCH_FRAME_ALIGNMENT
));
5128 MonoInst
*var
= cfg
->dyn_call_var
;
5131 g_assert (var
->opcode
== OP_REGOFFSET
);
5132 g_assert (arm_is_imm12 (var
->inst_offset
));
5134 /* lr = args buffer filled by mono_arch_get_dyn_call_args () */
5135 ARM_MOV_REG_REG (code
, ARMREG_LR
, ins
->sreg1
);
5137 ARM_MOV_REG_REG (code
, ARMREG_IP
, ins
->sreg2
);
5139 /* Save args buffer */
5140 ARM_STR_IMM (code
, ARMREG_LR
, var
->inst_basereg
, var
->inst_offset
);
5142 /* Set stack slots using R0 as scratch reg */
5143 /* MONO_ARCH_DYN_CALL_PARAM_AREA gives the size of stack space available */
5144 for (i
= 0; i
< DYN_CALL_STACK_ARGS
; ++i
) {
5145 ARM_LDR_IMM (code
, ARMREG_R0
, ARMREG_LR
, (PARAM_REGS
+ i
) * sizeof (mgreg_t
));
5146 ARM_STR_IMM (code
, ARMREG_R0
, ARMREG_SP
, i
* sizeof (mgreg_t
));
5149 /* Set fp argument registers */
5150 if (IS_HARD_FLOAT
) {
5151 ARM_LDR_IMM (code
, ARMREG_R0
, ARMREG_LR
, MONO_STRUCT_OFFSET (DynCallArgs
, has_fpregs
));
5152 ARM_CMP_REG_IMM (code
, ARMREG_R0
, 0, 0);
5154 ARM_B_COND (code
, ARMCOND_EQ
, 0);
5155 for (i
= 0; i
< FP_PARAM_REGS
; ++i
) {
5156 int offset
= MONO_STRUCT_OFFSET (DynCallArgs
, fpregs
) + (i
* sizeof (double));
5157 g_assert (arm_is_fpimm8 (offset
));
5158 ARM_FLDD (code
, i
* 2, ARMREG_LR
, offset
);
5160 arm_patch (buf
[0], code
);
5163 /* Set argument registers */
5164 for (i
= 0; i
< PARAM_REGS
; ++i
)
5165 ARM_LDR_IMM (code
, i
, ARMREG_LR
, i
* sizeof (mgreg_t
));
5168 ARM_MOV_REG_REG (code
, ARMREG_LR
, ARMREG_PC
);
5169 ARM_MOV_REG_REG (code
, ARMREG_PC
, ARMREG_IP
);
5172 ARM_LDR_IMM (code
, ARMREG_IP
, var
->inst_basereg
, var
->inst_offset
);
5173 ARM_STR_IMM (code
, ARMREG_R0
, ARMREG_IP
, MONO_STRUCT_OFFSET (DynCallArgs
, res
));
5174 ARM_STR_IMM (code
, ARMREG_R1
, ARMREG_IP
, MONO_STRUCT_OFFSET (DynCallArgs
, res2
));
5176 ARM_FSTD (code
, ARM_VFP_D0
, ARMREG_IP
, MONO_STRUCT_OFFSET (DynCallArgs
, fpregs
));
5180 if (ins
->sreg1
!= ARMREG_R0
)
5181 ARM_MOV_REG_REG (code
, ARMREG_R0
, ins
->sreg1
);
5182 mono_add_patch_info (cfg
, code
- cfg
->native_code
, MONO_PATCH_INFO_INTERNAL_METHOD
,
5183 (gpointer
)"mono_arch_throw_exception");
5184 code
= emit_call_seq (cfg
, code
);
5188 if (ins
->sreg1
!= ARMREG_R0
)
5189 ARM_MOV_REG_REG (code
, ARMREG_R0
, ins
->sreg1
);
5190 mono_add_patch_info (cfg
, code
- cfg
->native_code
, MONO_PATCH_INFO_INTERNAL_METHOD
,
5191 (gpointer
)"mono_arch_rethrow_exception");
5192 code
= emit_call_seq (cfg
, code
);
5195 case OP_START_HANDLER
: {
5196 MonoInst
*spvar
= mono_find_spvar_for_region (cfg
, bb
->region
);
5197 int param_area
= ALIGN_TO (cfg
->param_area
, MONO_ARCH_FRAME_ALIGNMENT
);
5200 /* Reserve a param area, see filter-stack.exe */
5202 if ((i
= mono_arm_is_rotated_imm8 (param_area
, &rot_amount
)) >= 0) {
5203 ARM_SUB_REG_IMM (code
, ARMREG_SP
, ARMREG_SP
, i
, rot_amount
);
5205 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, param_area
);
5206 ARM_SUB_REG_REG (code
, ARMREG_SP
, ARMREG_SP
, ARMREG_IP
);
5210 if (arm_is_imm12 (spvar
->inst_offset
)) {
5211 ARM_STR_IMM (code
, ARMREG_LR
, spvar
->inst_basereg
, spvar
->inst_offset
);
5213 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, spvar
->inst_offset
);
5214 ARM_STR_REG_REG (code
, ARMREG_LR
, spvar
->inst_basereg
, ARMREG_IP
);
5218 case OP_ENDFILTER
: {
5219 MonoInst
*spvar
= mono_find_spvar_for_region (cfg
, bb
->region
);
5220 int param_area
= ALIGN_TO (cfg
->param_area
, MONO_ARCH_FRAME_ALIGNMENT
);
5223 /* Free the param area */
5225 if ((i
= mono_arm_is_rotated_imm8 (param_area
, &rot_amount
)) >= 0) {
5226 ARM_ADD_REG_IMM (code
, ARMREG_SP
, ARMREG_SP
, i
, rot_amount
);
5228 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, param_area
);
5229 ARM_ADD_REG_REG (code
, ARMREG_SP
, ARMREG_SP
, ARMREG_IP
);
5233 if (ins
->sreg1
!= ARMREG_R0
)
5234 ARM_MOV_REG_REG (code
, ARMREG_R0
, ins
->sreg1
);
5235 if (arm_is_imm12 (spvar
->inst_offset
)) {
5236 ARM_LDR_IMM (code
, ARMREG_IP
, spvar
->inst_basereg
, spvar
->inst_offset
);
5238 g_assert (ARMREG_IP
!= spvar
->inst_basereg
);
5239 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, spvar
->inst_offset
);
5240 ARM_LDR_REG_REG (code
, ARMREG_IP
, spvar
->inst_basereg
, ARMREG_IP
);
5242 ARM_MOV_REG_REG (code
, ARMREG_PC
, ARMREG_IP
);
5245 case OP_ENDFINALLY
: {
5246 MonoInst
*spvar
= mono_find_spvar_for_region (cfg
, bb
->region
);
5247 int param_area
= ALIGN_TO (cfg
->param_area
, MONO_ARCH_FRAME_ALIGNMENT
);
5250 /* Free the param area */
5252 if ((i
= mono_arm_is_rotated_imm8 (param_area
, &rot_amount
)) >= 0) {
5253 ARM_ADD_REG_IMM (code
, ARMREG_SP
, ARMREG_SP
, i
, rot_amount
);
5255 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, param_area
);
5256 ARM_ADD_REG_REG (code
, ARMREG_SP
, ARMREG_SP
, ARMREG_IP
);
5260 if (arm_is_imm12 (spvar
->inst_offset
)) {
5261 ARM_LDR_IMM (code
, ARMREG_IP
, spvar
->inst_basereg
, spvar
->inst_offset
);
5263 g_assert (ARMREG_IP
!= spvar
->inst_basereg
);
5264 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, spvar
->inst_offset
);
5265 ARM_LDR_REG_REG (code
, ARMREG_IP
, spvar
->inst_basereg
, ARMREG_IP
);
5267 ARM_MOV_REG_REG (code
, ARMREG_PC
, ARMREG_IP
);
5270 case OP_CALL_HANDLER
:
5271 mono_add_patch_info (cfg
, code
- cfg
->native_code
, MONO_PATCH_INFO_BB
, ins
->inst_target_bb
);
5272 code
= mono_arm_patchable_bl (code
, ARMCOND_AL
);
5273 cfg
->thunk_area
+= THUNK_SIZE
;
5274 mono_cfg_add_try_hole (cfg
, ins
->inst_eh_block
, code
, bb
);
5277 if (ins
->dreg
!= ARMREG_R0
)
5278 ARM_MOV_REG_REG (code
, ins
->dreg
, ARMREG_R0
);
5282 ins
->inst_c0
= code
- cfg
->native_code
;
5285 /*if (ins->inst_target_bb->native_offset) {
5287 //x86_jump_code (code, cfg->native_code + ins->inst_target_bb->native_offset);
5289 mono_add_patch_info (cfg
, offset
, MONO_PATCH_INFO_BB
, ins
->inst_target_bb
);
5290 code
= mono_arm_patchable_b (code
, ARMCOND_AL
);
5294 ARM_MOV_REG_REG (code
, ARMREG_PC
, ins
->sreg1
);
5298 * In the normal case we have:
5299 * ldr pc, [pc, ins->sreg1 << 2]
5302 * ldr lr, [pc, ins->sreg1 << 2]
5304 * After follows the data.
5305 * FIXME: add aot support.
5307 mono_add_patch_info (cfg
, offset
, MONO_PATCH_INFO_SWITCH
, ins
->inst_p0
);
5308 max_len
+= 4 * GPOINTER_TO_INT (ins
->klass
);
5309 if (offset
+ max_len
> (cfg
->code_size
- 16)) {
5310 cfg
->code_size
+= max_len
;
5311 cfg
->code_size
*= 2;
5312 cfg
->native_code
= g_realloc (cfg
->native_code
, cfg
->code_size
);
5313 code
= cfg
->native_code
+ offset
;
5315 ARM_LDR_REG_REG_SHIFT (code
, ARMREG_PC
, ARMREG_PC
, ins
->sreg1
, ARMSHIFT_LSL
, 2);
5317 code
+= 4 * GPOINTER_TO_INT (ins
->klass
);
5321 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 0, ARMCOND_NE
);
5322 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 1, ARMCOND_EQ
);
5326 ARM_MOV_REG_IMM8 (code
, ins
->dreg
, 0);
5327 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 1, ARMCOND_LT
);
5331 ARM_MOV_REG_IMM8 (code
, ins
->dreg
, 0);
5332 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 1, ARMCOND_LO
);
5336 ARM_MOV_REG_IMM8 (code
, ins
->dreg
, 0);
5337 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 1, ARMCOND_GT
);
5341 ARM_MOV_REG_IMM8 (code
, ins
->dreg
, 0);
5342 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 1, ARMCOND_HI
);
5345 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 1, ARMCOND_NE
);
5346 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 0, ARMCOND_EQ
);
5349 ARM_MOV_REG_IMM8 (code
, ins
->dreg
, 1);
5350 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 0, ARMCOND_LT
);
5353 ARM_MOV_REG_IMM8 (code
, ins
->dreg
, 1);
5354 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 0, ARMCOND_GT
);
5357 ARM_MOV_REG_IMM8 (code
, ins
->dreg
, 1);
5358 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 0, ARMCOND_LO
);
5361 ARM_MOV_REG_IMM8 (code
, ins
->dreg
, 1);
5362 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 0, ARMCOND_HI
);
5364 case OP_COND_EXC_EQ
:
5365 case OP_COND_EXC_NE_UN
:
5366 case OP_COND_EXC_LT
:
5367 case OP_COND_EXC_LT_UN
:
5368 case OP_COND_EXC_GT
:
5369 case OP_COND_EXC_GT_UN
:
5370 case OP_COND_EXC_GE
:
5371 case OP_COND_EXC_GE_UN
:
5372 case OP_COND_EXC_LE
:
5373 case OP_COND_EXC_LE_UN
:
5374 EMIT_COND_SYSTEM_EXCEPTION (ins
->opcode
- OP_COND_EXC_EQ
, ins
->inst_p1
);
5376 case OP_COND_EXC_IEQ
:
5377 case OP_COND_EXC_INE_UN
:
5378 case OP_COND_EXC_ILT
:
5379 case OP_COND_EXC_ILT_UN
:
5380 case OP_COND_EXC_IGT
:
5381 case OP_COND_EXC_IGT_UN
:
5382 case OP_COND_EXC_IGE
:
5383 case OP_COND_EXC_IGE_UN
:
5384 case OP_COND_EXC_ILE
:
5385 case OP_COND_EXC_ILE_UN
:
5386 EMIT_COND_SYSTEM_EXCEPTION (ins
->opcode
- OP_COND_EXC_IEQ
, ins
->inst_p1
);
5389 case OP_COND_EXC_IC
:
5390 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_CS
, ins
->inst_p1
);
5392 case OP_COND_EXC_OV
:
5393 case OP_COND_EXC_IOV
:
5394 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_VS
, ins
->inst_p1
);
5396 case OP_COND_EXC_NC
:
5397 case OP_COND_EXC_INC
:
5398 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_CC
, ins
->inst_p1
);
5400 case OP_COND_EXC_NO
:
5401 case OP_COND_EXC_INO
:
5402 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_VC
, ins
->inst_p1
);
5414 EMIT_COND_BRANCH (ins
, ins
->opcode
- OP_IBEQ
);
5417 /* floating point opcodes */
5419 if (cfg
->compile_aot
) {
5420 ARM_FLDD (code
, ins
->dreg
, ARMREG_PC
, 0);
5422 *(guint32
*)code
= ((guint32
*)(ins
->inst_p0
))[0];
5424 *(guint32
*)code
= ((guint32
*)(ins
->inst_p0
))[1];
5427 /* FIXME: we can optimize the imm load by dealing with part of
5428 * the displacement in LDFD (aligning to 512).
5430 code
= mono_arm_emit_load_imm (code
, ARMREG_LR
, (guint32
)ins
->inst_p0
);
5431 ARM_FLDD (code
, ins
->dreg
, ARMREG_LR
, 0);
5435 if (cfg
->compile_aot
) {
5436 ARM_FLDS (code
, ins
->dreg
, ARMREG_PC
, 0);
5438 *(guint32
*)code
= ((guint32
*)(ins
->inst_p0
))[0];
5441 ARM_CVTS (code
, ins
->dreg
, ins
->dreg
);
5443 code
= mono_arm_emit_load_imm (code
, ARMREG_LR
, (guint32
)ins
->inst_p0
);
5444 ARM_FLDS (code
, ins
->dreg
, ARMREG_LR
, 0);
5446 ARM_CVTS (code
, ins
->dreg
, ins
->dreg
);
5449 case OP_STORER8_MEMBASE_REG
:
5450 /* This is generated by the local regalloc pass which runs after the lowering pass */
5451 if (!arm_is_fpimm8 (ins
->inst_offset
)) {
5452 code
= mono_arm_emit_load_imm (code
, ARMREG_LR
, ins
->inst_offset
);
5453 ARM_ADD_REG_REG (code
, ARMREG_LR
, ARMREG_LR
, ins
->inst_destbasereg
);
5454 ARM_FSTD (code
, ins
->sreg1
, ARMREG_LR
, 0);
5456 ARM_FSTD (code
, ins
->sreg1
, ins
->inst_destbasereg
, ins
->inst_offset
);
5459 case OP_LOADR8_MEMBASE
:
5460 /* This is generated by the local regalloc pass which runs after the lowering pass */
5461 if (!arm_is_fpimm8 (ins
->inst_offset
)) {
5462 code
= mono_arm_emit_load_imm (code
, ARMREG_LR
, ins
->inst_offset
);
5463 ARM_ADD_REG_REG (code
, ARMREG_LR
, ARMREG_LR
, ins
->inst_basereg
);
5464 ARM_FLDD (code
, ins
->dreg
, ARMREG_LR
, 0);
5466 ARM_FLDD (code
, ins
->dreg
, ins
->inst_basereg
, ins
->inst_offset
);
5469 case OP_STORER4_MEMBASE_REG
:
5470 g_assert (arm_is_fpimm8 (ins
->inst_offset
));
5472 ARM_FSTS (code
, ins
->sreg1
, ins
->inst_destbasereg
, ins
->inst_offset
);
5474 code
= mono_arm_emit_vfp_scratch_save (cfg
, code
, vfp_scratch1
);
5475 ARM_CVTD (code
, vfp_scratch1
, ins
->sreg1
);
5476 ARM_FSTS (code
, vfp_scratch1
, ins
->inst_destbasereg
, ins
->inst_offset
);
5477 code
= mono_arm_emit_vfp_scratch_restore (cfg
, code
, vfp_scratch1
);
5480 case OP_LOADR4_MEMBASE
:
5482 ARM_FLDS (code
, ins
->dreg
, ins
->inst_basereg
, ins
->inst_offset
);
5484 g_assert (arm_is_fpimm8 (ins
->inst_offset
));
5485 code
= mono_arm_emit_vfp_scratch_save (cfg
, code
, vfp_scratch1
);
5486 ARM_FLDS (code
, vfp_scratch1
, ins
->inst_basereg
, ins
->inst_offset
);
5487 ARM_CVTS (code
, ins
->dreg
, vfp_scratch1
);
5488 code
= mono_arm_emit_vfp_scratch_restore (cfg
, code
, vfp_scratch1
);
5491 case OP_ICONV_TO_R_UN
: {
5492 g_assert_not_reached ();
5495 case OP_ICONV_TO_R4
:
5497 ARM_FMSR (code
, ins
->dreg
, ins
->sreg1
);
5498 ARM_FSITOS (code
, ins
->dreg
, ins
->dreg
);
5500 code
= mono_arm_emit_vfp_scratch_save (cfg
, code
, vfp_scratch1
);
5501 ARM_FMSR (code
, vfp_scratch1
, ins
->sreg1
);
5502 ARM_FSITOS (code
, vfp_scratch1
, vfp_scratch1
);
5503 ARM_CVTS (code
, ins
->dreg
, vfp_scratch1
);
5504 code
= mono_arm_emit_vfp_scratch_restore (cfg
, code
, vfp_scratch1
);
5507 case OP_ICONV_TO_R8
:
5508 code
= mono_arm_emit_vfp_scratch_save (cfg
, code
, vfp_scratch1
);
5509 ARM_FMSR (code
, vfp_scratch1
, ins
->sreg1
);
5510 ARM_FSITOD (code
, ins
->dreg
, vfp_scratch1
);
5511 code
= mono_arm_emit_vfp_scratch_restore (cfg
, code
, vfp_scratch1
);
5515 MonoType
*sig_ret
= mini_get_underlying_type (mono_method_signature (cfg
->method
)->ret
);
5516 if (sig_ret
->type
== MONO_TYPE_R4
) {
5518 if (IS_HARD_FLOAT
) {
5519 if (ins
->sreg1
!= ARM_VFP_D0
)
5520 ARM_CPYS (code
, ARM_VFP_D0
, ins
->sreg1
);
5522 ARM_FMRS (code
, ARMREG_R0
, ins
->sreg1
);
5525 ARM_CVTD (code
, ARM_VFP_F0
, ins
->sreg1
);
5528 ARM_FMRS (code
, ARMREG_R0
, ARM_VFP_F0
);
5532 ARM_CPYD (code
, ARM_VFP_D0
, ins
->sreg1
);
5534 ARM_FMRRD (code
, ARMREG_R0
, ARMREG_R1
, ins
->sreg1
);
5538 case OP_FCONV_TO_I1
:
5539 code
= emit_float_to_int (cfg
, code
, ins
->dreg
, ins
->sreg1
, 1, TRUE
);
5541 case OP_FCONV_TO_U1
:
5542 code
= emit_float_to_int (cfg
, code
, ins
->dreg
, ins
->sreg1
, 1, FALSE
);
5544 case OP_FCONV_TO_I2
:
5545 code
= emit_float_to_int (cfg
, code
, ins
->dreg
, ins
->sreg1
, 2, TRUE
);
5547 case OP_FCONV_TO_U2
:
5548 code
= emit_float_to_int (cfg
, code
, ins
->dreg
, ins
->sreg1
, 2, FALSE
);
5550 case OP_FCONV_TO_I4
:
5552 code
= emit_float_to_int (cfg
, code
, ins
->dreg
, ins
->sreg1
, 4, TRUE
);
5554 case OP_FCONV_TO_U4
:
5556 code
= emit_float_to_int (cfg
, code
, ins
->dreg
, ins
->sreg1
, 4, FALSE
);
5558 case OP_FCONV_TO_I8
:
5559 case OP_FCONV_TO_U8
:
5560 g_assert_not_reached ();
5561 /* Implemented as helper calls */
5563 case OP_LCONV_TO_R_UN
:
5564 g_assert_not_reached ();
5565 /* Implemented as helper calls */
5567 case OP_LCONV_TO_OVF_I4_2
: {
5568 guint8
*high_bit_not_set
, *valid_negative
, *invalid_negative
, *valid_positive
;
5570 * Valid ints: 0xffffffff:8000000 to 00000000:0x7f000000
5573 ARM_CMP_REG_IMM8 (code
, ins
->sreg1
, 0);
5574 high_bit_not_set
= code
;
5575 ARM_B_COND (code
, ARMCOND_GE
, 0); /*branch if bit 31 of the lower part is not set*/
5577 ARM_CMN_REG_IMM8 (code
, ins
->sreg2
, 1); /*This have the same effect as CMP reg, 0xFFFFFFFF */
5578 valid_negative
= code
;
5579 ARM_B_COND (code
, ARMCOND_EQ
, 0); /*branch if upper part == 0xFFFFFFFF (lower part has bit 31 set) */
5580 invalid_negative
= code
;
5581 ARM_B_COND (code
, ARMCOND_AL
, 0);
5583 arm_patch (high_bit_not_set
, code
);
5585 ARM_CMP_REG_IMM8 (code
, ins
->sreg2
, 0);
5586 valid_positive
= code
;
5587 ARM_B_COND (code
, ARMCOND_EQ
, 0); /*branch if upper part == 0 (lower part has bit 31 clear)*/
5589 arm_patch (invalid_negative
, code
);
5590 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_AL
, "OverflowException");
5592 arm_patch (valid_negative
, code
);
5593 arm_patch (valid_positive
, code
);
5595 if (ins
->dreg
!= ins
->sreg1
)
5596 ARM_MOV_REG_REG (code
, ins
->dreg
, ins
->sreg1
);
5600 ARM_VFP_ADDD (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
5603 ARM_VFP_SUBD (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
5606 ARM_VFP_MULD (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
5609 ARM_VFP_DIVD (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
5612 ARM_NEGD (code
, ins
->dreg
, ins
->sreg1
);
5616 g_assert_not_reached ();
5620 ARM_CMPD (code
, ins
->sreg1
, ins
->sreg2
);
5626 ARM_CMPS (code
, ins
->sreg1
, ins
->sreg2
);
5631 ARM_CMPD (code
, ins
->sreg1
, ins
->sreg2
);
5634 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 0, ARMCOND_NE
);
5635 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 1, ARMCOND_EQ
);
5639 ARM_CMPD (code
, ins
->sreg1
, ins
->sreg2
);
5642 ARM_MOV_REG_IMM8 (code
, ins
->dreg
, 0);
5643 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 1, ARMCOND_MI
);
5647 ARM_CMPD (code
, ins
->sreg1
, ins
->sreg2
);
5650 ARM_MOV_REG_IMM8 (code
, ins
->dreg
, 0);
5651 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 1, ARMCOND_MI
);
5652 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 1, ARMCOND_VS
);
5656 ARM_CMPD (code
, ins
->sreg2
, ins
->sreg1
);
5659 ARM_MOV_REG_IMM8 (code
, ins
->dreg
, 0);
5660 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 1, ARMCOND_MI
);
5664 ARM_CMPD (code
, ins
->sreg2
, ins
->sreg1
);
5667 ARM_MOV_REG_IMM8 (code
, ins
->dreg
, 0);
5668 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 1, ARMCOND_MI
);
5669 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 1, ARMCOND_VS
);
5673 ARM_CMPD (code
, ins
->sreg1
, ins
->sreg2
);
5676 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 1, ARMCOND_NE
);
5677 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 0, ARMCOND_EQ
);
5681 ARM_CMPD (code
, ins
->sreg1
, ins
->sreg2
);
5684 ARM_MOV_REG_IMM8 (code
, ins
->dreg
, 1);
5685 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 0, ARMCOND_MI
);
5689 ARM_CMPD (code
, ins
->sreg2
, ins
->sreg1
);
5692 ARM_MOV_REG_IMM8 (code
, ins
->dreg
, 1);
5693 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 0, ARMCOND_MI
);
5696 /* ARM FPA flags table:
5697 * N Less than ARMCOND_MI
5698 * Z Equal ARMCOND_EQ
5699 * C Greater Than or Equal ARMCOND_CS
5700 * V Unordered ARMCOND_VS
5703 EMIT_COND_BRANCH (ins
, OP_IBEQ
- OP_IBEQ
);
5706 EMIT_COND_BRANCH (ins
, OP_IBNE_UN
- OP_IBEQ
);
5709 EMIT_COND_BRANCH_FLAGS (ins
, ARMCOND_MI
); /* N set */
5712 EMIT_COND_BRANCH_FLAGS (ins
, ARMCOND_VS
); /* V set */
5713 EMIT_COND_BRANCH_FLAGS (ins
, ARMCOND_MI
); /* N set */
5719 g_assert_not_reached ();
5723 EMIT_COND_BRANCH_FLAGS (ins
, ARMCOND_GE
);
5725 /* FPA requires EQ even thou the docs suggests that just CS is enough */
5726 EMIT_COND_BRANCH_FLAGS (ins
, ARMCOND_EQ
);
5727 EMIT_COND_BRANCH_FLAGS (ins
, ARMCOND_CS
);
5731 EMIT_COND_BRANCH_FLAGS (ins
, ARMCOND_VS
); /* V set */
5732 EMIT_COND_BRANCH_FLAGS (ins
, ARMCOND_GE
);
5737 code
= mono_arm_emit_vfp_scratch_save (cfg
, code
, vfp_scratch1
);
5738 code
= mono_arm_emit_vfp_scratch_save (cfg
, code
, vfp_scratch2
);
5740 ARM_ABSD (code
, vfp_scratch2
, ins
->sreg1
);
5741 ARM_FLDD (code
, vfp_scratch1
, ARMREG_PC
, 0);
5743 *(guint32
*)code
= 0xffffffff;
5745 *(guint32
*)code
= 0x7fefffff;
5747 ARM_CMPD (code
, vfp_scratch2
, vfp_scratch1
);
5749 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_GT
, "OverflowException");
5750 ARM_CMPD (code
, ins
->sreg1
, ins
->sreg1
);
5752 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_VS
, "OverflowException");
5753 ARM_CPYD (code
, ins
->dreg
, ins
->sreg1
);
5755 code
= mono_arm_emit_vfp_scratch_restore (cfg
, code
, vfp_scratch1
);
5756 code
= mono_arm_emit_vfp_scratch_restore (cfg
, code
, vfp_scratch2
);
5761 case OP_RCONV_TO_I1
:
5762 code
= emit_r4_to_int (cfg
, code
, ins
->dreg
, ins
->sreg1
, 1, TRUE
);
5764 case OP_RCONV_TO_U1
:
5765 code
= emit_r4_to_int (cfg
, code
, ins
->dreg
, ins
->sreg1
, 1, FALSE
);
5767 case OP_RCONV_TO_I2
:
5768 code
= emit_r4_to_int (cfg
, code
, ins
->dreg
, ins
->sreg1
, 2, TRUE
);
5770 case OP_RCONV_TO_U2
:
5771 code
= emit_r4_to_int (cfg
, code
, ins
->dreg
, ins
->sreg1
, 2, FALSE
);
5773 case OP_RCONV_TO_I4
:
5774 code
= emit_r4_to_int (cfg
, code
, ins
->dreg
, ins
->sreg1
, 4, TRUE
);
5776 case OP_RCONV_TO_U4
:
5777 code
= emit_r4_to_int (cfg
, code
, ins
->dreg
, ins
->sreg1
, 4, FALSE
);
5779 case OP_RCONV_TO_R4
:
5781 if (ins
->dreg
!= ins
->sreg1
)
5782 ARM_CPYS (code
, ins
->dreg
, ins
->sreg1
);
5784 case OP_RCONV_TO_R8
:
5786 ARM_CVTS (code
, ins
->dreg
, ins
->sreg1
);
5789 ARM_VFP_ADDS (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
5792 ARM_VFP_SUBS (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
5795 ARM_VFP_MULS (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
5798 ARM_VFP_DIVS (code
, ins
->dreg
, ins
->sreg1
, ins
->sreg2
);
5801 ARM_NEGS (code
, ins
->dreg
, ins
->sreg1
);
5805 ARM_CMPS (code
, ins
->sreg1
, ins
->sreg2
);
5808 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 0, ARMCOND_NE
);
5809 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 1, ARMCOND_EQ
);
5813 ARM_CMPS (code
, ins
->sreg1
, ins
->sreg2
);
5816 ARM_MOV_REG_IMM8 (code
, ins
->dreg
, 0);
5817 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 1, ARMCOND_MI
);
5821 ARM_CMPS (code
, ins
->sreg1
, ins
->sreg2
);
5824 ARM_MOV_REG_IMM8 (code
, ins
->dreg
, 0);
5825 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 1, ARMCOND_MI
);
5826 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 1, ARMCOND_VS
);
5830 ARM_CMPS (code
, ins
->sreg2
, ins
->sreg1
);
5833 ARM_MOV_REG_IMM8 (code
, ins
->dreg
, 0);
5834 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 1, ARMCOND_MI
);
5838 ARM_CMPS (code
, ins
->sreg2
, ins
->sreg1
);
5841 ARM_MOV_REG_IMM8 (code
, ins
->dreg
, 0);
5842 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 1, ARMCOND_MI
);
5843 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 1, ARMCOND_VS
);
5847 ARM_CMPS (code
, ins
->sreg1
, ins
->sreg2
);
5850 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 1, ARMCOND_NE
);
5851 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 0, ARMCOND_EQ
);
5855 ARM_CMPS (code
, ins
->sreg1
, ins
->sreg2
);
5858 ARM_MOV_REG_IMM8 (code
, ins
->dreg
, 1);
5859 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 0, ARMCOND_MI
);
5863 ARM_CMPS (code
, ins
->sreg2
, ins
->sreg1
);
5866 ARM_MOV_REG_IMM8 (code
, ins
->dreg
, 1);
5867 ARM_MOV_REG_IMM8_COND (code
, ins
->dreg
, 0, ARMCOND_MI
);
5870 case OP_GC_LIVENESS_DEF
:
5871 case OP_GC_LIVENESS_USE
:
5872 case OP_GC_PARAM_SLOT_LIVENESS_DEF
:
5873 ins
->backend
.pc_offset
= code
- cfg
->native_code
;
5875 case OP_GC_SPILL_SLOT_LIVENESS_DEF
:
5876 ins
->backend
.pc_offset
= code
- cfg
->native_code
;
5877 bb
->spill_slot_defs
= g_slist_prepend_mempool (cfg
->mempool
, bb
->spill_slot_defs
, ins
);
5879 case OP_GC_SAFE_POINT
: {
5882 g_assert (mono_threads_is_coop_enabled ());
5884 ARM_LDR_IMM (code
, ARMREG_IP
, ins
->sreg1
, 0);
5885 ARM_CMP_REG_IMM (code
, ARMREG_IP
, 0, 0);
5887 ARM_B_COND (code
, ARMCOND_EQ
, 0);
5888 mono_add_patch_info (cfg
, code
- cfg
->native_code
, MONO_PATCH_INFO_INTERNAL_METHOD
, "mono_threads_state_poll");
5889 code
= emit_call_seq (cfg
, code
);
5890 arm_patch (buf
[0], code
);
5893 case OP_FILL_PROF_CALL_CTX
:
5894 for (int i
= 0; i
< ARMREG_MAX
; i
++)
5895 if ((MONO_ARCH_CALLEE_SAVED_REGS
& (1 << i
)) || i
== ARMREG_SP
|| i
== ARMREG_FP
)
5896 ARM_STR_IMM (code
, i
, ins
->sreg1
, MONO_STRUCT_OFFSET (MonoContext
, regs
) + i
* sizeof (mgreg_t
));
5899 g_warning ("unknown opcode %s in %s()\n", mono_inst_name (ins
->opcode
), __FUNCTION__
);
5900 g_assert_not_reached ();
5903 if ((cfg
->opt
& MONO_OPT_BRANCH
) && ((code
- cfg
->native_code
- offset
) > max_len
)) {
5904 g_warning ("wrong maximal instruction length of instruction %s (expected %d, got %d)",
5905 mono_inst_name (ins
->opcode
), max_len
, code
- cfg
->native_code
- offset
);
5906 g_assert_not_reached ();
5912 last_offset
= offset
;
5915 cfg
->code_len
= code
- cfg
->native_code
;
5918 #endif /* DISABLE_JIT */
5921 mono_arch_register_lowlevel_calls (void)
5923 /* The signature doesn't matter */
5924 mono_register_jit_icall (mono_arm_throw_exception
, "mono_arm_throw_exception", mono_create_icall_signature ("void"), TRUE
);
5925 mono_register_jit_icall (mono_arm_throw_exception_by_token
, "mono_arm_throw_exception_by_token", mono_create_icall_signature ("void"), TRUE
);
5926 mono_register_jit_icall (mono_arm_unaligned_stack
, "mono_arm_unaligned_stack", mono_create_icall_signature ("void"), TRUE
);
5929 #define patch_lis_ori(ip,val) do {\
5930 guint16 *__lis_ori = (guint16*)(ip); \
5931 __lis_ori [1] = (((guint32)(val)) >> 16) & 0xffff; \
5932 __lis_ori [3] = ((guint32)(val)) & 0xffff; \
5936 mono_arch_patch_code_new (MonoCompile
*cfg
, MonoDomain
*domain
, guint8
*code
, MonoJumpInfo
*ji
, gpointer target
)
5938 unsigned char *ip
= ji
->ip
.i
+ code
;
5940 if (ji
->type
== MONO_PATCH_INFO_SWITCH
) {
5944 case MONO_PATCH_INFO_SWITCH
: {
5945 gpointer
*jt
= (gpointer
*)(ip
+ 8);
5947 /* jt is the inlined jump table, 2 instructions after ip
5948 * In the normal case we store the absolute addresses,
5949 * otherwise the displacements.
5951 for (i
= 0; i
< ji
->data
.table
->table_size
; i
++)
5952 jt
[i
] = code
+ (int)ji
->data
.table
->table
[i
];
5955 case MONO_PATCH_INFO_IP
:
5956 g_assert_not_reached ();
5957 patch_lis_ori (ip
, ip
);
5959 case MONO_PATCH_INFO_METHOD_REL
:
5960 g_assert_not_reached ();
5961 *((gpointer
*)(ip
)) = target
;
5963 case MONO_PATCH_INFO_METHODCONST
:
5964 case MONO_PATCH_INFO_CLASS
:
5965 case MONO_PATCH_INFO_IMAGE
:
5966 case MONO_PATCH_INFO_FIELD
:
5967 case MONO_PATCH_INFO_VTABLE
:
5968 case MONO_PATCH_INFO_IID
:
5969 case MONO_PATCH_INFO_SFLDA
:
5970 case MONO_PATCH_INFO_LDSTR
:
5971 case MONO_PATCH_INFO_TYPE_FROM_HANDLE
:
5972 case MONO_PATCH_INFO_LDTOKEN
:
5973 g_assert_not_reached ();
5974 /* from OP_AOTCONST : lis + ori */
5975 patch_lis_ori (ip
, target
);
5977 case MONO_PATCH_INFO_R4
:
5978 case MONO_PATCH_INFO_R8
:
5979 g_assert_not_reached ();
5980 *((gconstpointer
*)(ip
+ 2)) = target
;
5982 case MONO_PATCH_INFO_EXC_NAME
:
5983 g_assert_not_reached ();
5984 *((gconstpointer
*)(ip
+ 1)) = target
;
5986 case MONO_PATCH_INFO_NONE
:
5987 case MONO_PATCH_INFO_BB_OVF
:
5988 case MONO_PATCH_INFO_EXC_OVF
:
5989 /* everything is dealt with at epilog output time */
5992 arm_patch_general (cfg
, domain
, ip
, target
);
5998 mono_arm_unaligned_stack (MonoMethod
*method
)
6000 g_assert_not_reached ();
6006 * Stack frame layout:
6008 * ------------------- fp
6009 * MonoLMF structure or saved registers
6010 * -------------------
6012 * -------------------
6014 * -------------------
6015 * optional 8 bytes for tracing
6016 * -------------------
6017 * param area size is cfg->param_area
6018 * ------------------- sp
6021 mono_arch_emit_prolog (MonoCompile
*cfg
)
6023 MonoMethod
*method
= cfg
->method
;
6025 MonoMethodSignature
*sig
;
6027 int alloc_size
, orig_alloc_size
, pos
, max_offset
, i
, rot_amount
, part
;
6032 int prev_sp_offset
, reg_offset
;
6034 if (mono_jit_trace_calls
!= NULL
&& mono_trace_eval (method
))
6037 sig
= mono_method_signature (method
);
6038 cfg
->code_size
= 256 + sig
->param_count
* 64;
6039 code
= cfg
->native_code
= g_malloc (cfg
->code_size
);
6041 mono_emit_unwind_op_def_cfa (cfg
, code
, ARMREG_SP
, 0);
6043 alloc_size
= cfg
->stack_offset
;
6049 * The iphone uses R7 as the frame pointer, and it points at the saved
6054 * We can't use r7 as a frame pointer since it points into the middle of
6055 * the frame, so we keep using our own frame pointer.
6056 * FIXME: Optimize this.
6058 ARM_PUSH (code
, (1 << ARMREG_R7
) | (1 << ARMREG_LR
));
6059 prev_sp_offset
+= 8; /* r7 and lr */
6060 mono_emit_unwind_op_def_cfa_offset (cfg
, code
, prev_sp_offset
);
6061 mono_emit_unwind_op_offset (cfg
, code
, ARMREG_R7
, (- prev_sp_offset
) + 0);
6062 ARM_MOV_REG_REG (code
, ARMREG_R7
, ARMREG_SP
);
6065 if (!method
->save_lmf
) {
6067 /* No need to push LR again */
6068 if (cfg
->used_int_regs
)
6069 ARM_PUSH (code
, cfg
->used_int_regs
);
6071 ARM_PUSH (code
, cfg
->used_int_regs
| (1 << ARMREG_LR
));
6072 prev_sp_offset
+= 4;
6074 for (i
= 0; i
< 16; ++i
) {
6075 if (cfg
->used_int_regs
& (1 << i
))
6076 prev_sp_offset
+= 4;
6078 mono_emit_unwind_op_def_cfa_offset (cfg
, code
, prev_sp_offset
);
6080 for (i
= 0; i
< 16; ++i
) {
6081 if ((cfg
->used_int_regs
& (1 << i
))) {
6082 mono_emit_unwind_op_offset (cfg
, code
, i
, (- prev_sp_offset
) + reg_offset
);
6083 mini_gc_set_slot_type_from_cfa (cfg
, (- prev_sp_offset
) + reg_offset
, SLOT_NOREF
);
6087 mono_emit_unwind_op_offset (cfg
, code
, ARMREG_LR
, -4);
6088 mini_gc_set_slot_type_from_cfa (cfg
, -4, SLOT_NOREF
);
6090 ARM_MOV_REG_REG (code
, ARMREG_IP
, ARMREG_SP
);
6091 ARM_PUSH (code
, 0x5ff0);
6092 prev_sp_offset
+= 4 * 10; /* all but r0-r3, sp and pc */
6093 mono_emit_unwind_op_def_cfa_offset (cfg
, code
, prev_sp_offset
);
6095 for (i
= 0; i
< 16; ++i
) {
6096 if ((i
> ARMREG_R3
) && (i
!= ARMREG_SP
) && (i
!= ARMREG_PC
)) {
6097 /* The original r7 is saved at the start */
6098 if (!(iphone_abi
&& i
== ARMREG_R7
))
6099 mono_emit_unwind_op_offset (cfg
, code
, i
, (- prev_sp_offset
) + reg_offset
);
6103 g_assert (reg_offset
== 4 * 10);
6104 pos
+= sizeof (MonoLMF
) - (4 * 10);
6108 orig_alloc_size
= alloc_size
;
6109 // align to MONO_ARCH_FRAME_ALIGNMENT bytes
6110 if (alloc_size
& (MONO_ARCH_FRAME_ALIGNMENT
- 1)) {
6111 alloc_size
+= MONO_ARCH_FRAME_ALIGNMENT
- 1;
6112 alloc_size
&= ~(MONO_ARCH_FRAME_ALIGNMENT
- 1);
6115 /* the stack used in the pushed regs */
6116 alloc_size
+= ALIGN_TO (prev_sp_offset
, MONO_ARCH_FRAME_ALIGNMENT
) - prev_sp_offset
;
6117 cfg
->stack_usage
= alloc_size
;
6119 if ((i
= mono_arm_is_rotated_imm8 (alloc_size
, &rot_amount
)) >= 0) {
6120 ARM_SUB_REG_IMM (code
, ARMREG_SP
, ARMREG_SP
, i
, rot_amount
);
6122 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, alloc_size
);
6123 ARM_SUB_REG_REG (code
, ARMREG_SP
, ARMREG_SP
, ARMREG_IP
);
6125 mono_emit_unwind_op_def_cfa_offset (cfg
, code
, prev_sp_offset
+ alloc_size
);
6127 if (cfg
->frame_reg
!= ARMREG_SP
) {
6128 ARM_MOV_REG_REG (code
, cfg
->frame_reg
, ARMREG_SP
);
6129 mono_emit_unwind_op_def_cfa_reg (cfg
, code
, cfg
->frame_reg
);
6131 //g_print ("prev_sp_offset: %d, alloc_size:%d\n", prev_sp_offset, alloc_size);
6132 prev_sp_offset
+= alloc_size
;
6134 for (i
= 0; i
< alloc_size
- orig_alloc_size
; i
+= 4)
6135 mini_gc_set_slot_type_from_cfa (cfg
, (- prev_sp_offset
) + orig_alloc_size
+ i
, SLOT_NOREF
);
6137 /* compute max_offset in order to use short forward jumps
6138 * we could skip do it on arm because the immediate displacement
6139 * for jumps is large enough, it may be useful later for constant pools
6142 for (bb
= cfg
->bb_entry
; bb
; bb
= bb
->next_bb
) {
6143 MonoInst
*ins
= bb
->code
;
6144 bb
->max_offset
= max_offset
;
6146 MONO_BB_FOR_EACH_INS (bb
, ins
)
6147 max_offset
+= ((guint8
*)ins_get_spec (ins
->opcode
))[MONO_INST_LEN
];
6150 /* stack alignment check */
6154 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_SP);
6155 code = mono_arm_emit_load_imm (code, ARMREG_IP, MONO_ARCH_FRAME_ALIGNMENT -1);
6156 ARM_AND_REG_REG (code, ARMREG_LR, ARMREG_LR, ARMREG_IP);
6157 ARM_CMP_REG_IMM (code, ARMREG_LR, 0, 0);
6159 ARM_B_COND (code, ARMCOND_EQ, 0);
6160 if (cfg->compile_aot)
6161 ARM_MOV_REG_IMM8 (code, ARMREG_R0, 0);
6163 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->method);
6164 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD, "mono_arm_unaligned_stack");
6165 code = emit_call_seq (cfg, code);
6166 arm_patch (buf [0], code);
6170 /* store runtime generic context */
6171 if (cfg
->rgctx_var
) {
6172 MonoInst
*ins
= cfg
->rgctx_var
;
6174 g_assert (ins
->opcode
== OP_REGOFFSET
);
6176 if (arm_is_imm12 (ins
->inst_offset
)) {
6177 ARM_STR_IMM (code
, MONO_ARCH_RGCTX_REG
, ins
->inst_basereg
, ins
->inst_offset
);
6179 code
= mono_arm_emit_load_imm (code
, ARMREG_LR
, ins
->inst_offset
);
6180 ARM_STR_REG_REG (code
, MONO_ARCH_RGCTX_REG
, ins
->inst_basereg
, ARMREG_LR
);
6184 /* load arguments allocated to register from the stack */
6187 cinfo
= get_call_info (NULL
, sig
);
6189 if (cinfo
->ret
.storage
== RegTypeStructByAddr
) {
6190 ArgInfo
*ainfo
= &cinfo
->ret
;
6191 inst
= cfg
->vret_addr
;
6192 g_assert (arm_is_imm12 (inst
->inst_offset
));
6193 ARM_STR_IMM (code
, ainfo
->reg
, inst
->inst_basereg
, inst
->inst_offset
);
6196 if (sig
->call_convention
== MONO_CALL_VARARG
) {
6197 ArgInfo
*cookie
= &cinfo
->sig_cookie
;
6199 /* Save the sig cookie address */
6200 g_assert (cookie
->storage
== RegTypeBase
);
6202 g_assert (arm_is_imm12 (prev_sp_offset
+ cookie
->offset
));
6203 g_assert (arm_is_imm12 (cfg
->sig_cookie
));
6204 ARM_ADD_REG_IMM8 (code
, ARMREG_IP
, cfg
->frame_reg
, prev_sp_offset
+ cookie
->offset
);
6205 ARM_STR_IMM (code
, ARMREG_IP
, cfg
->frame_reg
, cfg
->sig_cookie
);
6208 for (i
= 0; i
< sig
->param_count
+ sig
->hasthis
; ++i
) {
6209 ArgInfo
*ainfo
= cinfo
->args
+ i
;
6210 inst
= cfg
->args
[pos
];
6212 if (cfg
->verbose_level
> 2)
6213 g_print ("Saving argument %d (type: %d)\n", i
, ainfo
->storage
);
6215 if (inst
->opcode
== OP_REGVAR
) {
6216 if (ainfo
->storage
== RegTypeGeneral
)
6217 ARM_MOV_REG_REG (code
, inst
->dreg
, ainfo
->reg
);
6218 else if (ainfo
->storage
== RegTypeFP
) {
6219 g_assert_not_reached ();
6220 } else if (ainfo
->storage
== RegTypeBase
) {
6221 if (arm_is_imm12 (prev_sp_offset
+ ainfo
->offset
)) {
6222 ARM_LDR_IMM (code
, inst
->dreg
, ARMREG_SP
, (prev_sp_offset
+ ainfo
->offset
));
6224 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, prev_sp_offset
+ ainfo
->offset
);
6225 ARM_LDR_REG_REG (code
, inst
->dreg
, ARMREG_SP
, ARMREG_IP
);
6228 g_assert_not_reached ();
6230 if (cfg
->verbose_level
> 2)
6231 g_print ("Argument %d assigned to register %s\n", pos
, mono_arch_regname (inst
->dreg
));
6233 switch (ainfo
->storage
) {
6235 for (part
= 0; part
< ainfo
->nregs
; part
++) {
6236 if (ainfo
->esize
== 4)
6237 ARM_FSTS (code
, ainfo
->reg
+ part
, inst
->inst_basereg
, inst
->inst_offset
+ (part
* ainfo
->esize
));
6239 ARM_FSTD (code
, ainfo
->reg
+ (part
* 2), inst
->inst_basereg
, inst
->inst_offset
+ (part
* ainfo
->esize
));
6242 case RegTypeGeneral
:
6243 case RegTypeIRegPair
:
6244 case RegTypeGSharedVtInReg
:
6245 case RegTypeStructByAddr
:
6246 switch (ainfo
->size
) {
6248 if (arm_is_imm12 (inst
->inst_offset
))
6249 ARM_STRB_IMM (code
, ainfo
->reg
, inst
->inst_basereg
, inst
->inst_offset
);
6251 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, inst
->inst_offset
);
6252 ARM_STRB_REG_REG (code
, ainfo
->reg
, inst
->inst_basereg
, ARMREG_IP
);
6256 if (arm_is_imm8 (inst
->inst_offset
)) {
6257 ARM_STRH_IMM (code
, ainfo
->reg
, inst
->inst_basereg
, inst
->inst_offset
);
6259 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, inst
->inst_offset
);
6260 ARM_STRH_REG_REG (code
, ainfo
->reg
, inst
->inst_basereg
, ARMREG_IP
);
6264 if (arm_is_imm12 (inst
->inst_offset
)) {
6265 ARM_STR_IMM (code
, ainfo
->reg
, inst
->inst_basereg
, inst
->inst_offset
);
6267 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, inst
->inst_offset
);
6268 ARM_STR_REG_REG (code
, ainfo
->reg
, inst
->inst_basereg
, ARMREG_IP
);
6270 if (arm_is_imm12 (inst
->inst_offset
+ 4)) {
6271 ARM_STR_IMM (code
, ainfo
->reg
+ 1, inst
->inst_basereg
, inst
->inst_offset
+ 4);
6273 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, inst
->inst_offset
+ 4);
6274 ARM_STR_REG_REG (code
, ainfo
->reg
+ 1, inst
->inst_basereg
, ARMREG_IP
);
6278 if (arm_is_imm12 (inst
->inst_offset
)) {
6279 ARM_STR_IMM (code
, ainfo
->reg
, inst
->inst_basereg
, inst
->inst_offset
);
6281 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, inst
->inst_offset
);
6282 ARM_STR_REG_REG (code
, ainfo
->reg
, inst
->inst_basereg
, ARMREG_IP
);
6287 case RegTypeBaseGen
:
6288 if (arm_is_imm12 (prev_sp_offset
+ ainfo
->offset
)) {
6289 ARM_LDR_IMM (code
, ARMREG_LR
, ARMREG_SP
, (prev_sp_offset
+ ainfo
->offset
));
6291 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, prev_sp_offset
+ ainfo
->offset
);
6292 ARM_LDR_REG_REG (code
, ARMREG_LR
, ARMREG_SP
, ARMREG_IP
);
6294 if (arm_is_imm12 (inst
->inst_offset
+ 4)) {
6295 ARM_STR_IMM (code
, ARMREG_LR
, inst
->inst_basereg
, inst
->inst_offset
+ 4);
6296 ARM_STR_IMM (code
, ARMREG_R3
, inst
->inst_basereg
, inst
->inst_offset
);
6298 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, inst
->inst_offset
+ 4);
6299 ARM_STR_REG_REG (code
, ARMREG_LR
, inst
->inst_basereg
, ARMREG_IP
);
6300 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, inst
->inst_offset
);
6301 ARM_STR_REG_REG (code
, ARMREG_R3
, inst
->inst_basereg
, ARMREG_IP
);
6305 case RegTypeGSharedVtOnStack
:
6306 case RegTypeStructByAddrOnStack
:
6307 if (arm_is_imm12 (prev_sp_offset
+ ainfo
->offset
)) {
6308 ARM_LDR_IMM (code
, ARMREG_LR
, ARMREG_SP
, (prev_sp_offset
+ ainfo
->offset
));
6310 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, prev_sp_offset
+ ainfo
->offset
);
6311 ARM_LDR_REG_REG (code
, ARMREG_LR
, ARMREG_SP
, ARMREG_IP
);
6314 switch (ainfo
->size
) {
6316 if (arm_is_imm8 (inst
->inst_offset
)) {
6317 ARM_STRB_IMM (code
, ARMREG_LR
, inst
->inst_basereg
, inst
->inst_offset
);
6319 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, inst
->inst_offset
);
6320 ARM_STRB_REG_REG (code
, ARMREG_LR
, inst
->inst_basereg
, ARMREG_IP
);
6324 if (arm_is_imm8 (inst
->inst_offset
)) {
6325 ARM_STRH_IMM (code
, ARMREG_LR
, inst
->inst_basereg
, inst
->inst_offset
);
6327 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, inst
->inst_offset
);
6328 ARM_STRH_REG_REG (code
, ARMREG_LR
, inst
->inst_basereg
, ARMREG_IP
);
6332 if (arm_is_imm12 (inst
->inst_offset
)) {
6333 ARM_STR_IMM (code
, ARMREG_LR
, inst
->inst_basereg
, inst
->inst_offset
);
6335 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, inst
->inst_offset
);
6336 ARM_STR_REG_REG (code
, ARMREG_LR
, inst
->inst_basereg
, ARMREG_IP
);
6338 if (arm_is_imm12 (prev_sp_offset
+ ainfo
->offset
+ 4)) {
6339 ARM_LDR_IMM (code
, ARMREG_LR
, ARMREG_SP
, (prev_sp_offset
+ ainfo
->offset
+ 4));
6341 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, prev_sp_offset
+ ainfo
->offset
+ 4);
6342 ARM_LDR_REG_REG (code
, ARMREG_LR
, ARMREG_SP
, ARMREG_IP
);
6344 if (arm_is_imm12 (inst
->inst_offset
+ 4)) {
6345 ARM_STR_IMM (code
, ARMREG_LR
, inst
->inst_basereg
, inst
->inst_offset
+ 4);
6347 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, inst
->inst_offset
+ 4);
6348 ARM_STR_REG_REG (code
, ARMREG_LR
, inst
->inst_basereg
, ARMREG_IP
);
6352 if (arm_is_imm12 (inst
->inst_offset
)) {
6353 ARM_STR_IMM (code
, ARMREG_LR
, inst
->inst_basereg
, inst
->inst_offset
);
6355 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, inst
->inst_offset
);
6356 ARM_STR_REG_REG (code
, ARMREG_LR
, inst
->inst_basereg
, ARMREG_IP
);
6362 int imm8
, rot_amount
;
6364 if ((imm8
= mono_arm_is_rotated_imm8 (inst
->inst_offset
, &rot_amount
)) == -1) {
6365 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, inst
->inst_offset
);
6366 ARM_ADD_REG_REG (code
, ARMREG_IP
, ARMREG_IP
, inst
->inst_basereg
);
6368 ARM_ADD_REG_IMM (code
, ARMREG_IP
, inst
->inst_basereg
, imm8
, rot_amount
);
6370 if (ainfo
->size
== 8)
6371 ARM_FSTD (code
, ainfo
->reg
, ARMREG_IP
, 0);
6373 ARM_FSTS (code
, ainfo
->reg
, ARMREG_IP
, 0);
6376 case RegTypeStructByVal
: {
6377 int doffset
= inst
->inst_offset
;
6381 size
= mini_type_stack_size_full (inst
->inst_vtype
, NULL
, sig
->pinvoke
);
6382 for (cur_reg
= 0; cur_reg
< ainfo
->size
; ++cur_reg
) {
6383 if (arm_is_imm12 (doffset
)) {
6384 ARM_STR_IMM (code
, ainfo
->reg
+ cur_reg
, inst
->inst_basereg
, doffset
);
6386 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, doffset
);
6387 ARM_STR_REG_REG (code
, ainfo
->reg
+ cur_reg
, inst
->inst_basereg
, ARMREG_IP
);
6389 soffset
+= sizeof (gpointer
);
6390 doffset
+= sizeof (gpointer
);
6392 if (ainfo
->vtsize
) {
6393 /* FIXME: handle overrun! with struct sizes not multiple of 4 */
6394 //g_print ("emit_memcpy (prev_sp_ofs: %d, ainfo->offset: %d, soffset: %d)\n", prev_sp_offset, ainfo->offset, soffset);
6395 code
= emit_memcpy (code
, ainfo
->vtsize
* sizeof (gpointer
), inst
->inst_basereg
, doffset
, ARMREG_SP
, prev_sp_offset
+ ainfo
->offset
);
6400 g_assert_not_reached ();
6407 if (method
->save_lmf
)
6408 code
= emit_save_lmf (cfg
, code
, alloc_size
- lmf_offset
);
6411 code
= mono_arch_instrument_prolog (cfg
, mono_trace_enter_method
, code
, TRUE
);
6413 if (cfg
->arch
.seq_point_info_var
) {
6414 MonoInst
*ins
= cfg
->arch
.seq_point_info_var
;
6416 /* Initialize the variable from a GOT slot */
6417 mono_add_patch_info (cfg
, code
- cfg
->native_code
, MONO_PATCH_INFO_SEQ_POINT_INFO
, cfg
->method
);
6418 ARM_LDR_IMM (code
, ARMREG_R0
, ARMREG_PC
, 0);
6420 *(gpointer
*)code
= NULL
;
6422 ARM_LDR_REG_REG (code
, ARMREG_R0
, ARMREG_PC
, ARMREG_R0
);
6424 g_assert (ins
->opcode
== OP_REGOFFSET
);
6426 if (arm_is_imm12 (ins
->inst_offset
)) {
6427 ARM_STR_IMM (code
, ARMREG_R0
, ins
->inst_basereg
, ins
->inst_offset
);
6429 code
= mono_arm_emit_load_imm (code
, ARMREG_LR
, ins
->inst_offset
);
6430 ARM_STR_REG_REG (code
, ARMREG_R0
, ins
->inst_basereg
, ARMREG_LR
);
6434 /* Initialize ss_trigger_page_var */
6435 if (!cfg
->soft_breakpoints
) {
6436 MonoInst
*info_var
= cfg
->arch
.seq_point_info_var
;
6437 MonoInst
*ss_trigger_page_var
= cfg
->arch
.ss_trigger_page_var
;
6438 int dreg
= ARMREG_LR
;
6441 g_assert (info_var
->opcode
== OP_REGOFFSET
);
6443 code
= emit_ldr_imm (code
, dreg
, info_var
->inst_basereg
, info_var
->inst_offset
);
6444 /* Load the trigger page addr */
6445 ARM_LDR_IMM (code
, dreg
, dreg
, MONO_STRUCT_OFFSET (SeqPointInfo
, ss_trigger_page
));
6446 ARM_STR_IMM (code
, dreg
, ss_trigger_page_var
->inst_basereg
, ss_trigger_page_var
->inst_offset
);
6450 if (cfg
->arch
.seq_point_ss_method_var
) {
6451 MonoInst
*ss_method_ins
= cfg
->arch
.seq_point_ss_method_var
;
6452 MonoInst
*bp_method_ins
= cfg
->arch
.seq_point_bp_method_var
;
6454 g_assert (ss_method_ins
->opcode
== OP_REGOFFSET
);
6455 g_assert (arm_is_imm12 (ss_method_ins
->inst_offset
));
6457 if (cfg
->compile_aot
) {
6458 MonoInst
*info_var
= cfg
->arch
.seq_point_info_var
;
6459 int dreg
= ARMREG_LR
;
6461 g_assert (info_var
->opcode
== OP_REGOFFSET
);
6462 g_assert (arm_is_imm12 (info_var
->inst_offset
));
6464 ARM_LDR_IMM (code
, dreg
, info_var
->inst_basereg
, info_var
->inst_offset
);
6465 ARM_LDR_IMM (code
, dreg
, dreg
, MONO_STRUCT_OFFSET (SeqPointInfo
, ss_tramp_addr
));
6466 ARM_STR_IMM (code
, dreg
, ss_method_ins
->inst_basereg
, ss_method_ins
->inst_offset
);
6468 g_assert (bp_method_ins
->opcode
== OP_REGOFFSET
);
6469 g_assert (arm_is_imm12 (bp_method_ins
->inst_offset
));
6471 ARM_MOV_REG_REG (code
, ARMREG_LR
, ARMREG_PC
);
6473 *(gpointer
*)code
= &single_step_tramp
;
6475 *(gpointer
*)code
= breakpoint_tramp
;
6478 ARM_LDR_IMM (code
, ARMREG_IP
, ARMREG_LR
, 0);
6479 ARM_STR_IMM (code
, ARMREG_IP
, ss_method_ins
->inst_basereg
, ss_method_ins
->inst_offset
);
6480 ARM_LDR_IMM (code
, ARMREG_IP
, ARMREG_LR
, 4);
6481 ARM_STR_IMM (code
, ARMREG_IP
, bp_method_ins
->inst_basereg
, bp_method_ins
->inst_offset
);
6485 cfg
->code_len
= code
- cfg
->native_code
;
6486 g_assert (cfg
->code_len
< cfg
->code_size
);
6493 mono_arch_emit_epilog (MonoCompile
*cfg
)
6495 MonoMethod
*method
= cfg
->method
;
6496 int pos
, i
, rot_amount
;
6497 int max_epilog_size
= 16 + 20*4;
6501 if (cfg
->method
->save_lmf
)
6502 max_epilog_size
+= 128;
6504 if (mono_jit_trace_calls
!= NULL
)
6505 max_epilog_size
+= 50;
6507 while (cfg
->code_len
+ max_epilog_size
> (cfg
->code_size
- 16)) {
6508 cfg
->code_size
*= 2;
6509 cfg
->native_code
= g_realloc (cfg
->native_code
, cfg
->code_size
);
6510 cfg
->stat_code_reallocs
++;
6514 * Keep in sync with OP_JMP
6516 code
= cfg
->native_code
+ cfg
->code_len
;
6518 /* Save the uwind state which is needed by the out-of-line code */
6519 mono_emit_unwind_op_remember_state (cfg
, code
);
6521 if (mono_jit_trace_calls
!= NULL
&& mono_trace_eval (method
)) {
6522 code
= mono_arch_instrument_epilog (cfg
, mono_trace_leave_method
, code
, TRUE
);
6526 /* Load returned vtypes into registers if needed */
6527 cinfo
= cfg
->arch
.cinfo
;
6528 switch (cinfo
->ret
.storage
) {
6529 case RegTypeStructByVal
: {
6530 MonoInst
*ins
= cfg
->ret
;
6532 if (cinfo
->ret
.nregs
== 1) {
6533 if (arm_is_imm12 (ins
->inst_offset
)) {
6534 ARM_LDR_IMM (code
, ARMREG_R0
, ins
->inst_basereg
, ins
->inst_offset
);
6536 code
= mono_arm_emit_load_imm (code
, ARMREG_LR
, ins
->inst_offset
);
6537 ARM_LDR_REG_REG (code
, ARMREG_R0
, ins
->inst_basereg
, ARMREG_LR
);
6540 for (i
= 0; i
< cinfo
->ret
.nregs
; ++i
) {
6541 int offset
= ins
->inst_offset
+ (i
* 4);
6542 if (arm_is_imm12 (offset
)) {
6543 ARM_LDR_IMM (code
, i
, ins
->inst_basereg
, offset
);
6545 code
= mono_arm_emit_load_imm (code
, ARMREG_LR
, offset
);
6546 ARM_LDR_REG_REG (code
, i
, ins
->inst_basereg
, ARMREG_LR
);
6553 MonoInst
*ins
= cfg
->ret
;
6555 for (i
= 0; i
< cinfo
->ret
.nregs
; ++i
) {
6556 if (cinfo
->ret
.esize
== 4)
6557 ARM_FLDS (code
, cinfo
->ret
.reg
+ i
, ins
->inst_basereg
, ins
->inst_offset
+ (i
* cinfo
->ret
.esize
));
6559 ARM_FLDD (code
, cinfo
->ret
.reg
+ (i
* 2), ins
->inst_basereg
, ins
->inst_offset
+ (i
* cinfo
->ret
.esize
));
6567 if (method
->save_lmf
) {
6568 int lmf_offset
, reg
, sp_adj
, regmask
, nused_int_regs
= 0;
6569 /* all but r0-r3, sp and pc */
6570 pos
+= sizeof (MonoLMF
) - (MONO_ARM_NUM_SAVED_REGS
* sizeof (mgreg_t
));
6573 code
= emit_restore_lmf (cfg
, code
, cfg
->stack_usage
- lmf_offset
);
6575 /* This points to r4 inside MonoLMF->iregs */
6576 sp_adj
= (sizeof (MonoLMF
) - MONO_ARM_NUM_SAVED_REGS
* sizeof (mgreg_t
));
6578 regmask
= 0x9ff0; /* restore lr to pc */
6579 /* Skip caller saved registers not used by the method */
6580 while (!(cfg
->used_int_regs
& (1 << reg
)) && reg
< ARMREG_FP
) {
6581 regmask
&= ~(1 << reg
);
6586 /* Restored later */
6587 regmask
&= ~(1 << ARMREG_PC
);
6588 /* point sp at the registers to restore: 10 is 14 -4, because we skip r0-r3 */
6589 code
= emit_big_add (code
, ARMREG_SP
, cfg
->frame_reg
, cfg
->stack_usage
- lmf_offset
+ sp_adj
);
6590 for (i
= 0; i
< 16; i
++) {
6591 if (regmask
& (1 << i
))
6594 mono_emit_unwind_op_def_cfa (cfg
, code
, ARMREG_SP
, ((iphone_abi
? 3 : 0) + nused_int_regs
) * 4);
6596 ARM_POP (code
, regmask
);
6598 for (i
= 0; i
< 16; i
++) {
6599 if (regmask
& (1 << i
))
6600 mono_emit_unwind_op_same_value (cfg
, code
, i
);
6602 /* Restore saved r7, restore LR to PC */
6603 /* Skip lr from the lmf */
6604 mono_emit_unwind_op_def_cfa_offset (cfg
, code
, 3 * 4);
6605 ARM_ADD_REG_IMM (code
, ARMREG_SP
, ARMREG_SP
, sizeof (gpointer
), 0);
6606 mono_emit_unwind_op_def_cfa_offset (cfg
, code
, 2 * 4);
6607 ARM_POP (code
, (1 << ARMREG_R7
) | (1 << ARMREG_PC
));
6610 int i
, nused_int_regs
= 0;
6612 for (i
= 0; i
< 16; i
++) {
6613 if (cfg
->used_int_regs
& (1 << i
))
6617 if ((i
= mono_arm_is_rotated_imm8 (cfg
->stack_usage
, &rot_amount
)) >= 0) {
6618 ARM_ADD_REG_IMM (code
, ARMREG_SP
, cfg
->frame_reg
, i
, rot_amount
);
6620 code
= mono_arm_emit_load_imm (code
, ARMREG_IP
, cfg
->stack_usage
);
6621 ARM_ADD_REG_REG (code
, ARMREG_SP
, cfg
->frame_reg
, ARMREG_IP
);
6624 if (cfg
->frame_reg
!= ARMREG_SP
) {
6625 mono_emit_unwind_op_def_cfa_reg (cfg
, code
, ARMREG_SP
);
6629 /* Restore saved gregs */
6630 if (cfg
->used_int_regs
) {
6631 mono_emit_unwind_op_def_cfa_offset (cfg
, code
, (2 + nused_int_regs
) * 4);
6632 ARM_POP (code
, cfg
->used_int_regs
);
6633 for (i
= 0; i
< 16; i
++) {
6634 if (cfg
->used_int_regs
& (1 << i
))
6635 mono_emit_unwind_op_same_value (cfg
, code
, i
);
6638 mono_emit_unwind_op_def_cfa_offset (cfg
, code
, 2 * 4);
6639 /* Restore saved r7, restore LR to PC */
6640 ARM_POP (code
, (1 << ARMREG_R7
) | (1 << ARMREG_PC
));
6642 mono_emit_unwind_op_def_cfa_offset (cfg
, code
, (nused_int_regs
+ 1) * 4);
6643 ARM_POP (code
, cfg
->used_int_regs
| (1 << ARMREG_PC
));
6647 /* Restore the unwind state to be the same as before the epilog */
6648 mono_emit_unwind_op_restore_state (cfg
, code
);
6650 cfg
->code_len
= code
- cfg
->native_code
;
6652 g_assert (cfg
->code_len
< cfg
->code_size
);
6657 mono_arch_emit_exceptions (MonoCompile
*cfg
)
6659 MonoJumpInfo
*patch_info
;
6662 guint8
* exc_throw_pos
[MONO_EXC_INTRINS_NUM
];
6663 guint8 exc_throw_found
[MONO_EXC_INTRINS_NUM
];
6664 int max_epilog_size
= 50;
6666 for (i
= 0; i
< MONO_EXC_INTRINS_NUM
; i
++) {
6667 exc_throw_pos
[i
] = NULL
;
6668 exc_throw_found
[i
] = 0;
6671 /* count the number of exception infos */
6674 * make sure we have enough space for exceptions
6676 for (patch_info
= cfg
->patch_info
; patch_info
; patch_info
= patch_info
->next
) {
6677 if (patch_info
->type
== MONO_PATCH_INFO_EXC
) {
6678 i
= mini_exception_id_by_name (patch_info
->data
.target
);
6679 if (!exc_throw_found
[i
]) {
6680 max_epilog_size
+= 32;
6681 exc_throw_found
[i
] = TRUE
;
6686 while (cfg
->code_len
+ max_epilog_size
> (cfg
->code_size
- 16)) {
6687 cfg
->code_size
*= 2;
6688 cfg
->native_code
= g_realloc (cfg
->native_code
, cfg
->code_size
);
6689 cfg
->stat_code_reallocs
++;
6692 code
= cfg
->native_code
+ cfg
->code_len
;
6694 /* add code to raise exceptions */
6695 for (patch_info
= cfg
->patch_info
; patch_info
; patch_info
= patch_info
->next
) {
6696 switch (patch_info
->type
) {
6697 case MONO_PATCH_INFO_EXC
: {
6698 MonoClass
*exc_class
;
6699 unsigned char *ip
= patch_info
->ip
.i
+ cfg
->native_code
;
6701 i
= mini_exception_id_by_name (patch_info
->data
.target
);
6702 if (exc_throw_pos
[i
]) {
6703 arm_patch (ip
, exc_throw_pos
[i
]);
6704 patch_info
->type
= MONO_PATCH_INFO_NONE
;
6707 exc_throw_pos
[i
] = code
;
6709 arm_patch (ip
, code
);
6711 exc_class
= mono_class_load_from_name (mono_defaults
.corlib
, "System", patch_info
->data
.name
);
6713 ARM_MOV_REG_REG (code
, ARMREG_R1
, ARMREG_LR
);
6714 ARM_LDR_IMM (code
, ARMREG_R0
, ARMREG_PC
, 0);
6715 patch_info
->type
= MONO_PATCH_INFO_INTERNAL_METHOD
;
6716 patch_info
->data
.name
= "mono_arch_throw_corlib_exception";
6717 patch_info
->ip
.i
= code
- cfg
->native_code
;
6719 cfg
->thunk_area
+= THUNK_SIZE
;
6720 *(guint32
*)(gpointer
)code
= exc_class
->type_token
- MONO_TOKEN_TYPE_DEF
;
6730 cfg
->code_len
= code
- cfg
->native_code
;
6732 g_assert (cfg
->code_len
< cfg
->code_size
);
6736 #endif /* #ifndef DISABLE_JIT */
6739 mono_arch_finish_init (void)
6744 mono_arch_free_jit_tls_data (MonoJitTlsData
*tls
)
6749 mono_arch_emit_inst_for_method (MonoCompile
*cfg
, MonoMethod
*cmethod
, MonoMethodSignature
*fsig
, MonoInst
**args
)
6756 mono_arch_print_tree (MonoInst
*tree
, int arity
)
6766 mono_arch_get_patch_offset (guint8
*code
)
6773 mono_arch_flush_register_windows (void)
6778 mono_arch_find_imt_method (mgreg_t
*regs
, guint8
*code
)
6780 return (MonoMethod
*)regs
[MONO_ARCH_IMT_REG
];
6784 mono_arch_find_static_call_vtable (mgreg_t
*regs
, guint8
*code
)
6786 return (MonoVTable
*) regs
[MONO_ARCH_RGCTX_REG
];
6790 mono_arch_get_cie_program (void)
6794 mono_add_unwind_op_def_cfa (l
, (guint8
*)NULL
, (guint8
*)NULL
, ARMREG_SP
, 0);
6799 /* #define ENABLE_WRONG_METHOD_CHECK 1 */
6800 #define BASE_SIZE (6 * 4)
6801 #define BSEARCH_ENTRY_SIZE (4 * 4)
6802 #define CMP_SIZE (3 * 4)
6803 #define BRANCH_SIZE (1 * 4)
6804 #define CALL_SIZE (2 * 4)
6805 #define WMC_SIZE (8 * 4)
6806 #define DISTANCE(A, B) (((gint32)(B)) - ((gint32)(A)))
6809 arm_emit_value_and_patch_ldr (arminstr_t
*code
, arminstr_t
*target
, guint32 value
)
6811 guint32 delta
= DISTANCE (target
, code
);
6813 g_assert (delta
>= 0 && delta
<= 0xFFF);
6814 *target
= *target
| delta
;
6819 #ifdef ENABLE_WRONG_METHOD_CHECK
6821 mini_dump_bad_imt (int input_imt
, int compared_imt
, int pc
)
6823 g_print ("BAD IMT comparing %x with expected %x at ip %x", input_imt
, compared_imt
, pc
);
6829 mono_arch_build_imt_trampoline (MonoVTable
*vtable
, MonoDomain
*domain
, MonoIMTCheckItem
**imt_entries
, int count
,
6830 gpointer fail_tramp
)
6833 arminstr_t
*code
, *start
;
6834 gboolean large_offsets
= FALSE
;
6835 guint32
**constant_pool_starts
;
6836 arminstr_t
*vtable_target
= NULL
;
6837 int extra_space
= 0;
6838 #ifdef ENABLE_WRONG_METHOD_CHECK
6844 constant_pool_starts
= g_new0 (guint32
*, count
);
6846 for (i
= 0; i
< count
; ++i
) {
6847 MonoIMTCheckItem
*item
= imt_entries
[i
];
6848 if (item
->is_equals
) {
6849 gboolean fail_case
= !item
->check_target_idx
&& fail_tramp
;
6851 if (item
->has_target_code
|| !arm_is_imm12 (DISTANCE (vtable
, &vtable
->vtable
[item
->value
.vtable_slot
]))) {
6852 item
->chunk_size
+= 32;
6853 large_offsets
= TRUE
;
6856 if (item
->check_target_idx
|| fail_case
) {
6857 if (!item
->compare_done
|| fail_case
)
6858 item
->chunk_size
+= CMP_SIZE
;
6859 item
->chunk_size
+= BRANCH_SIZE
;
6861 #ifdef ENABLE_WRONG_METHOD_CHECK
6862 item
->chunk_size
+= WMC_SIZE
;
6866 item
->chunk_size
+= 16;
6867 large_offsets
= TRUE
;
6869 item
->chunk_size
+= CALL_SIZE
;
6871 item
->chunk_size
+= BSEARCH_ENTRY_SIZE
;
6872 imt_entries
[item
->check_target_idx
]->compare_done
= TRUE
;
6874 size
+= item
->chunk_size
;
6878 size
+= 4 * count
; /* The ARM_ADD_REG_IMM to pop the stack */
6881 code
= mono_method_alloc_generic_virtual_trampoline (domain
, size
);
6883 code
= mono_domain_code_reserve (domain
, size
);
6886 unwind_ops
= mono_arch_get_cie_program ();
6889 g_print ("Building IMT trampoline for class %s %s entries %d code size %d code at %p end %p vtable %p fail_tramp %p\n", vtable
->klass
->name_space
, vtable
->klass
->name
, count
, size
, start
, ((guint8
*)start
) + size
, vtable
, fail_tramp
);
6890 for (i
= 0; i
< count
; ++i
) {
6891 MonoIMTCheckItem
*item
= imt_entries
[i
];
6892 g_print ("method %d (%p) %s vtable slot %p is_equals %d chunk size %d\n", i
, item
->key
, ((MonoMethod
*)item
->key
)->name
, &vtable
->vtable
[item
->value
.vtable_slot
], item
->is_equals
, item
->chunk_size
);
6896 if (large_offsets
) {
6897 ARM_PUSH4 (code
, ARMREG_R0
, ARMREG_R1
, ARMREG_IP
, ARMREG_PC
);
6898 mono_add_unwind_op_def_cfa_offset (unwind_ops
, code
, start
, 4 * sizeof (mgreg_t
));
6900 ARM_PUSH2 (code
, ARMREG_R0
, ARMREG_R1
);
6901 mono_add_unwind_op_def_cfa_offset (unwind_ops
, code
, start
, 2 * sizeof (mgreg_t
));
6903 ARM_LDR_IMM (code
, ARMREG_R0
, ARMREG_LR
, -4);
6904 vtable_target
= code
;
6905 ARM_LDR_IMM (code
, ARMREG_IP
, ARMREG_PC
, 0);
6906 ARM_MOV_REG_REG (code
, ARMREG_R0
, ARMREG_V5
);
6908 for (i
= 0; i
< count
; ++i
) {
6909 MonoIMTCheckItem
*item
= imt_entries
[i
];
6910 arminstr_t
*imt_method
= NULL
, *vtable_offset_ins
= NULL
, *target_code_ins
= NULL
;
6911 gint32 vtable_offset
;
6913 item
->code_target
= (guint8
*)code
;
6915 if (item
->is_equals
) {
6916 gboolean fail_case
= !item
->check_target_idx
&& fail_tramp
;
6918 if (item
->check_target_idx
|| fail_case
) {
6919 if (!item
->compare_done
|| fail_case
) {
6921 ARM_LDR_IMM (code
, ARMREG_R1
, ARMREG_PC
, 0);
6922 ARM_CMP_REG_REG (code
, ARMREG_R0
, ARMREG_R1
);
6924 item
->jmp_code
= (guint8
*)code
;
6925 ARM_B_COND (code
, ARMCOND_NE
, 0);
6927 /*Enable the commented code to assert on wrong method*/
6928 #ifdef ENABLE_WRONG_METHOD_CHECK
6930 ARM_LDR_IMM (code
, ARMREG_R1
, ARMREG_PC
, 0);
6931 ARM_CMP_REG_REG (code
, ARMREG_R0
, ARMREG_R1
);
6933 ARM_B_COND (code
, ARMCOND_EQ
, 0);
6935 /* Define this if your system is so bad that gdb is failing. */
6936 #ifdef BROKEN_DEV_ENV
6937 ARM_MOV_REG_REG (code
, ARMREG_R2
, ARMREG_PC
);
6939 arm_patch (code
- 1, mini_dump_bad_imt
);
6943 arm_patch (cond
, code
);
6947 if (item
->has_target_code
) {
6948 /* Load target address */
6949 target_code_ins
= code
;
6950 ARM_LDR_IMM (code
, ARMREG_R1
, ARMREG_PC
, 0);
6951 /* Save it to the fourth slot */
6952 ARM_STR_IMM (code
, ARMREG_R1
, ARMREG_SP
, 3 * sizeof (gpointer
));
6953 /* Restore registers and branch */
6954 ARM_POP4 (code
, ARMREG_R0
, ARMREG_R1
, ARMREG_IP
, ARMREG_PC
);
6956 code
= arm_emit_value_and_patch_ldr (code
, target_code_ins
, (gsize
)item
->value
.target_code
);
6958 vtable_offset
= DISTANCE (vtable
, &vtable
->vtable
[item
->value
.vtable_slot
]);
6959 if (!arm_is_imm12 (vtable_offset
)) {
6961 * We need to branch to a computed address but we don't have
6962 * a free register to store it, since IP must contain the
6963 * vtable address. So we push the two values to the stack, and
6964 * load them both using LDM.
6966 /* Compute target address */
6967 vtable_offset_ins
= code
;
6968 ARM_LDR_IMM (code
, ARMREG_R1
, ARMREG_PC
, 0);
6969 ARM_LDR_REG_REG (code
, ARMREG_R1
, ARMREG_IP
, ARMREG_R1
);
6970 /* Save it to the fourth slot */
6971 ARM_STR_IMM (code
, ARMREG_R1
, ARMREG_SP
, 3 * sizeof (gpointer
));
6972 /* Restore registers and branch */
6973 ARM_POP4 (code
, ARMREG_R0
, ARMREG_R1
, ARMREG_IP
, ARMREG_PC
);
6975 code
= arm_emit_value_and_patch_ldr (code
, vtable_offset_ins
, vtable_offset
);
6977 ARM_POP2 (code
, ARMREG_R0
, ARMREG_R1
);
6978 if (large_offsets
) {
6979 mono_add_unwind_op_def_cfa_offset (unwind_ops
, code
, start
, 2 * sizeof (mgreg_t
));
6980 ARM_ADD_REG_IMM8 (code
, ARMREG_SP
, ARMREG_SP
, 2 * sizeof (gpointer
));
6982 mono_add_unwind_op_def_cfa_offset (unwind_ops
, code
, start
, 0);
6983 ARM_LDR_IMM (code
, ARMREG_PC
, ARMREG_IP
, vtable_offset
);
6988 arm_patch (item
->jmp_code
, (guchar
*)code
);
6990 target_code_ins
= code
;
6991 /* Load target address */
6992 ARM_LDR_IMM (code
, ARMREG_R1
, ARMREG_PC
, 0);
6993 /* Save it to the fourth slot */
6994 ARM_STR_IMM (code
, ARMREG_R1
, ARMREG_SP
, 3 * sizeof (gpointer
));
6995 /* Restore registers and branch */
6996 ARM_POP4 (code
, ARMREG_R0
, ARMREG_R1
, ARMREG_IP
, ARMREG_PC
);
6998 code
= arm_emit_value_and_patch_ldr (code
, target_code_ins
, (gsize
)fail_tramp
);
6999 item
->jmp_code
= NULL
;
7003 code
= arm_emit_value_and_patch_ldr (code
, imt_method
, (guint32
)item
->key
);
7005 /*must emit after unconditional branch*/
7006 if (vtable_target
) {
7007 code
= arm_emit_value_and_patch_ldr (code
, vtable_target
, (guint32
)vtable
);
7008 item
->chunk_size
+= 4;
7009 vtable_target
= NULL
;
7012 /*We reserve the space for bsearch IMT values after the first entry with an absolute jump*/
7013 constant_pool_starts
[i
] = code
;
7015 code
+= extra_space
;
7019 ARM_LDR_IMM (code
, ARMREG_R1
, ARMREG_PC
, 0);
7020 ARM_CMP_REG_REG (code
, ARMREG_R0
, ARMREG_R1
);
7022 item
->jmp_code
= (guint8
*)code
;
7023 ARM_B_COND (code
, ARMCOND_HS
, 0);
7028 for (i
= 0; i
< count
; ++i
) {
7029 MonoIMTCheckItem
*item
= imt_entries
[i
];
7030 if (item
->jmp_code
) {
7031 if (item
->check_target_idx
)
7032 arm_patch (item
->jmp_code
, imt_entries
[item
->check_target_idx
]->code_target
);
7034 if (i
> 0 && item
->is_equals
) {
7036 arminstr_t
*space_start
= constant_pool_starts
[i
];
7037 for (j
= i
- 1; j
>= 0 && !imt_entries
[j
]->is_equals
; --j
) {
7038 space_start
= arm_emit_value_and_patch_ldr (space_start
, (arminstr_t
*)imt_entries
[j
]->code_target
, (guint32
)imt_entries
[j
]->key
);
7045 char *buff
= g_strdup_printf ("thunk_for_class_%s_%s_entries_%d", vtable
->klass
->name_space
, vtable
->klass
->name
, count
);
7046 mono_disassemble_code (NULL
, (guint8
*)start
, size
, buff
);
7051 g_free (constant_pool_starts
);
7053 mono_arch_flush_icache ((guint8
*)start
, size
);
7054 MONO_PROFILER_RAISE (jit_code_buffer
, (start
, code
- start
, MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE
, NULL
));
7055 mono_stats
.imt_trampolines_size
+= code
- start
;
7057 g_assert (DISTANCE (start
, code
) <= size
);
7059 mono_tramp_info_register (mono_tramp_info_create (NULL
, (guint8
*)start
, DISTANCE (start
, code
), NULL
, unwind_ops
), domain
);
7065 mono_arch_context_get_int_reg (MonoContext
*ctx
, int reg
)
7067 return ctx
->regs
[reg
];
7071 mono_arch_context_set_int_reg (MonoContext
*ctx
, int reg
, mgreg_t val
)
7073 ctx
->regs
[reg
] = val
;
7077 * mono_arch_get_trampolines:
7079 * Return a list of MonoTrampInfo structures describing arch specific trampolines
7083 mono_arch_get_trampolines (gboolean aot
)
7085 return mono_arm_get_exception_trampolines (aot
);
7088 #if defined(MONO_ARCH_SOFT_DEBUG_SUPPORTED)
7090 * mono_arch_set_breakpoint:
7092 * Set a breakpoint at the native code corresponding to JI at NATIVE_OFFSET.
7093 * The location should contain code emitted by OP_SEQ_POINT.
7096 mono_arch_set_breakpoint (MonoJitInfo
*ji
, guint8
*ip
)
7099 guint32 native_offset
= ip
- (guint8
*)ji
->code_start
;
7100 MonoDebugOptions
*opt
= mini_get_debug_options ();
7103 SeqPointInfo
*info
= mono_arch_get_seq_point_info (mono_domain_get (), ji
->code_start
);
7105 if (!breakpoint_tramp
)
7106 breakpoint_tramp
= mini_get_breakpoint_trampoline ();
7108 g_assert (native_offset
% 4 == 0);
7109 g_assert (info
->bp_addrs
[native_offset
/ 4] == 0);
7110 info
->bp_addrs
[native_offset
/ 4] = opt
->soft_breakpoints
? breakpoint_tramp
: bp_trigger_page
;
7111 } else if (opt
->soft_breakpoints
) {
7113 ARM_BLX_REG (code
, ARMREG_LR
);
7114 mono_arch_flush_icache (code
- 4, 4);
7116 int dreg
= ARMREG_LR
;
7118 /* Read from another trigger page */
7119 ARM_LDR_IMM (code
, dreg
, ARMREG_PC
, 0);
7121 *(int*)code
= (int)bp_trigger_page
;
7123 ARM_LDR_IMM (code
, dreg
, dreg
, 0);
7125 mono_arch_flush_icache (code
- 16, 16);
7128 /* This is currently implemented by emitting an SWI instruction, which
7129 * qemu/linux seems to convert to a SIGILL.
7131 *(int*)code
= (0xef << 24) | 8;
7133 mono_arch_flush_icache (code
- 4, 4);
7139 * mono_arch_clear_breakpoint:
7141 * Clear the breakpoint at IP.
7144 mono_arch_clear_breakpoint (MonoJitInfo
*ji
, guint8
*ip
)
7146 MonoDebugOptions
*opt
= mini_get_debug_options ();
7151 guint32 native_offset
= ip
- (guint8
*)ji
->code_start
;
7152 SeqPointInfo
*info
= mono_arch_get_seq_point_info (mono_domain_get (), ji
->code_start
);
7154 if (!breakpoint_tramp
)
7155 breakpoint_tramp
= mini_get_breakpoint_trampoline ();
7157 g_assert (native_offset
% 4 == 0);
7158 g_assert (info
->bp_addrs
[native_offset
/ 4] == (opt
->soft_breakpoints
? breakpoint_tramp
: bp_trigger_page
));
7159 info
->bp_addrs
[native_offset
/ 4] = 0;
7160 } else if (opt
->soft_breakpoints
) {
7163 mono_arch_flush_icache (code
- 4, 4);
7165 for (i
= 0; i
< 4; ++i
)
7168 mono_arch_flush_icache (ip
, code
- ip
);
7173 * mono_arch_start_single_stepping:
7175 * Start single stepping.
7178 mono_arch_start_single_stepping (void)
7180 if (ss_trigger_page
)
7181 mono_mprotect (ss_trigger_page
, mono_pagesize (), 0);
7183 single_step_tramp
= mini_get_single_step_trampoline ();
7187 * mono_arch_stop_single_stepping:
7189 * Stop single stepping.
7192 mono_arch_stop_single_stepping (void)
7194 if (ss_trigger_page
)
7195 mono_mprotect (ss_trigger_page
, mono_pagesize (), MONO_MMAP_READ
);
7197 single_step_tramp
= NULL
;
7201 #define DBG_SIGNAL SIGBUS
7203 #define DBG_SIGNAL SIGSEGV
7207 * mono_arch_is_single_step_event:
7209 * Return whenever the machine state in SIGCTX corresponds to a single
7213 mono_arch_is_single_step_event (void *info
, void *sigctx
)
7215 siginfo_t
*sinfo
= info
;
7217 if (!ss_trigger_page
)
7220 /* Sometimes the address is off by 4 */
7221 if (sinfo
->si_addr
>= ss_trigger_page
&& (guint8
*)sinfo
->si_addr
<= (guint8
*)ss_trigger_page
+ 128)
7228 * mono_arch_is_breakpoint_event:
7230 * Return whenever the machine state in SIGCTX corresponds to a breakpoint event.
7233 mono_arch_is_breakpoint_event (void *info
, void *sigctx
)
7235 siginfo_t
*sinfo
= info
;
7237 if (!ss_trigger_page
)
7240 if (sinfo
->si_signo
== DBG_SIGNAL
) {
7241 /* Sometimes the address is off by 4 */
7242 if (sinfo
->si_addr
>= bp_trigger_page
&& (guint8
*)sinfo
->si_addr
<= (guint8
*)bp_trigger_page
+ 128)
7252 * mono_arch_skip_breakpoint:
7254 * See mini-amd64.c for docs.
7257 mono_arch_skip_breakpoint (MonoContext
*ctx
, MonoJitInfo
*ji
)
7259 MONO_CONTEXT_SET_IP (ctx
, (guint8
*)MONO_CONTEXT_GET_IP (ctx
) + 4);
7263 * mono_arch_skip_single_step:
7265 * See mini-amd64.c for docs.
7268 mono_arch_skip_single_step (MonoContext
*ctx
)
7270 MONO_CONTEXT_SET_IP (ctx
, (guint8
*)MONO_CONTEXT_GET_IP (ctx
) + 4);
7273 #endif /* MONO_ARCH_SOFT_DEBUG_SUPPORTED */
7276 * mono_arch_get_seq_point_info:
7278 * See mini-amd64.c for docs.
7281 mono_arch_get_seq_point_info (MonoDomain
*domain
, guint8
*code
)
7286 // FIXME: Add a free function
7288 mono_domain_lock (domain
);
7289 info
= g_hash_table_lookup (domain_jit_info (domain
)->arch_seq_points
,
7291 mono_domain_unlock (domain
);
7294 ji
= mono_jit_info_table_find (domain
, (char*)code
);
7297 info
= g_malloc0 (sizeof (SeqPointInfo
) + ji
->code_size
);
7299 info
->ss_trigger_page
= ss_trigger_page
;
7300 info
->bp_trigger_page
= bp_trigger_page
;
7301 info
->ss_tramp_addr
= &single_step_tramp
;
7303 mono_domain_lock (domain
);
7304 g_hash_table_insert (domain_jit_info (domain
)->arch_seq_points
,
7306 mono_domain_unlock (domain
);
7313 mono_arch_init_lmf_ext (MonoLMFExt
*ext
, gpointer prev_lmf
)
7315 ext
->lmf
.previous_lmf
= prev_lmf
;
7316 /* Mark that this is a MonoLMFExt */
7317 ext
->lmf
.previous_lmf
= (gpointer
)(((gssize
)ext
->lmf
.previous_lmf
) | 2);
7318 ext
->lmf
.sp
= (gssize
)ext
;
7322 * mono_arch_set_target:
7324 * Set the target architecture the JIT backend should generate code for, in the form
7325 * of a GNU target triplet. Only used in AOT mode.
7328 mono_arch_set_target (char *mtriple
)
7330 /* The GNU target triple format is not very well documented */
7331 if (strstr (mtriple
, "armv7")) {
7332 v5_supported
= TRUE
;
7333 v6_supported
= TRUE
;
7334 v7_supported
= TRUE
;
7336 if (strstr (mtriple
, "armv6")) {
7337 v5_supported
= TRUE
;
7338 v6_supported
= TRUE
;
7340 if (strstr (mtriple
, "armv7s")) {
7341 v7s_supported
= TRUE
;
7343 if (strstr (mtriple
, "armv7k")) {
7344 v7k_supported
= TRUE
;
7346 if (strstr (mtriple
, "thumbv7s")) {
7347 v5_supported
= TRUE
;
7348 v6_supported
= TRUE
;
7349 v7_supported
= TRUE
;
7350 v7s_supported
= TRUE
;
7351 thumb_supported
= TRUE
;
7352 thumb2_supported
= TRUE
;
7354 if (strstr (mtriple
, "darwin") || strstr (mtriple
, "ios")) {
7355 v5_supported
= TRUE
;
7356 v6_supported
= TRUE
;
7357 thumb_supported
= TRUE
;
7360 if (strstr (mtriple
, "gnueabi"))
7361 eabi_supported
= TRUE
;
7365 mono_arch_opcode_supported (int opcode
)
7368 case OP_ATOMIC_ADD_I4
:
7369 case OP_ATOMIC_EXCHANGE_I4
:
7370 case OP_ATOMIC_CAS_I4
:
7371 case OP_ATOMIC_LOAD_I1
:
7372 case OP_ATOMIC_LOAD_I2
:
7373 case OP_ATOMIC_LOAD_I4
:
7374 case OP_ATOMIC_LOAD_U1
:
7375 case OP_ATOMIC_LOAD_U2
:
7376 case OP_ATOMIC_LOAD_U4
:
7377 case OP_ATOMIC_STORE_I1
:
7378 case OP_ATOMIC_STORE_I2
:
7379 case OP_ATOMIC_STORE_I4
:
7380 case OP_ATOMIC_STORE_U1
:
7381 case OP_ATOMIC_STORE_U2
:
7382 case OP_ATOMIC_STORE_U4
:
7383 return v7_supported
;
7384 case OP_ATOMIC_LOAD_R4
:
7385 case OP_ATOMIC_LOAD_R8
:
7386 case OP_ATOMIC_STORE_R4
:
7387 case OP_ATOMIC_STORE_R8
:
7388 return v7_supported
&& IS_VFP
;
7395 mono_arch_get_call_info (MonoMemPool
*mp
, MonoMethodSignature
*sig
)
7397 return get_call_info (mp
, sig
);
7401 mono_arch_get_get_tls_tramp (void)
7407 emit_aotconst (MonoCompile
*cfg
, guint8
*code
, int dreg
, int patch_type
, gpointer data
)
7410 mono_add_patch_info (cfg
, code
- cfg
->native_code
, patch_type
, data
);
7411 ARM_LDR_IMM (code
, dreg
, ARMREG_PC
, 0);
7413 *(gpointer
*)code
= NULL
;
7415 /* Load the value from the GOT */
7416 ARM_LDR_REG_REG (code
, dreg
, ARMREG_PC
, dreg
);
7421 mono_arm_emit_aotconst (gpointer ji_list
, guint8
*code
, guint8
*buf
, int dreg
, int patch_type
, gconstpointer data
)
7423 MonoJumpInfo
**ji
= (MonoJumpInfo
**)ji_list
;
7425 *ji
= mono_patch_info_list_prepend (*ji
, code
- buf
, patch_type
, data
);
7426 ARM_LDR_IMM (code
, dreg
, ARMREG_PC
, 0);
7428 *(gpointer
*)code
= NULL
;
7430 ARM_LDR_REG_REG (code
, dreg
, ARMREG_PC
, dreg
);