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