[runtime] Counters for allocated virtual memory.
[mono-project.git] / mono / mini / mini-mips.c
blob3bce59c9cb79054c06203e335079863bc285a096
1 /*
2 * mini-mips.c: MIPS backend for the Mono code generator
4 * Authors:
5 * Mark Mason (mason@broadcom.com)
7 * Based on mini-ppc.c by
8 * Paolo Molaro (lupus@ximian.com)
9 * Dietmar Maurer (dietmar@ximian.com)
11 * (C) 2006 Broadcom
12 * (C) 2003 Ximian, Inc.
14 #include "mini.h"
15 #include <string.h>
16 #include <asm/cachectl.h>
18 #include <mono/metadata/abi-details.h>
19 #include <mono/metadata/appdomain.h>
20 #include <mono/metadata/debug-helpers.h>
21 #include <mono/utils/mono-mmap.h>
22 #include <mono/utils/mono-hwcap.h>
24 #include <mono/arch/mips/mips-codegen.h>
26 #include "mini-mips.h"
27 #include "cpu-mips.h"
28 #include "trace.h"
29 #include "ir-emit.h"
31 #define SAVE_FP_REGS 0
33 #define ALWAYS_SAVE_RA 1 /* call-handler & switch currently clobber ra */
35 #define PROMOTE_R4_TO_R8 1 /* promote single values in registers to doubles */
36 #define USE_MUL 0 /* use mul instead of mult/mflo for multiply
37 remember to update cpu-mips.md if you change this */
39 /* Emit a call sequence to 'v', using 'D' as a scratch register if necessary */
40 #define mips_call(c,D,v) do { \
41 guint32 _target = (guint32)(v); \
42 if (1 || ((v) == NULL) || ((_target & 0xfc000000) != (((guint32)(c)) & 0xfc000000))) { \
43 mips_load_const (c, D, _target); \
44 mips_jalr (c, D, mips_ra); \
45 } \
46 else { \
47 mips_jumpl (c, _target >> 2); \
48 } \
49 mips_nop (c); \
50 } while (0)
52 enum {
53 TLS_MODE_DETECT,
54 TLS_MODE_FAILED,
55 TLS_MODE_LTHREADS,
56 TLS_MODE_NPTL
59 /* This mutex protects architecture specific caches */
60 #define mono_mini_arch_lock() mono_os_mutex_lock (&mini_arch_mutex)
61 #define mono_mini_arch_unlock() mono_os_mutex_unlock (&mini_arch_mutex)
62 static mono_mutex_t mini_arch_mutex;
64 int mono_exc_esp_offset = 0;
65 static int tls_mode = TLS_MODE_DETECT;
66 static int lmf_pthread_key = -1;
67 static int monothread_key = -1;
69 /* Whenever the host is little-endian */
70 static int little_endian;
71 /* Index of ms word/register */
72 static int ls_word_idx;
73 /* Index of ls word/register */
74 static int ms_word_idx;
75 /* Same for offsets */
76 static int ls_word_offset;
77 static int ms_word_offset;
80 * The code generated for sequence points reads from this location, which is
81 * made read-only when single stepping is enabled.
83 static gpointer ss_trigger_page;
85 /* Enabled breakpoints read from this trigger page */
86 static gpointer bp_trigger_page;
88 #undef DEBUG
89 #define DEBUG(a) if (cfg->verbose_level > 1) a
90 #undef DEBUG
91 #define DEBUG(a) a
92 #undef DEBUG
93 #define DEBUG(a)
95 #define EMIT_SYSTEM_EXCEPTION_NAME(exc_name) \
96 do { \
97 code = mips_emit_exc_by_name (code, exc_name); \
98 cfg->bb_exit->max_offset += 16; \
99 } while (0)
102 #define emit_linuxthreads_tls(code,dreg,key) do {\
103 int off1, off2; \
104 off1 = offsets_from_pthread_key ((key), &off2); \
105 g_assert_not_reached (); \
106 ppc_lwz ((code), (dreg), off1, ppc_r2); \
107 ppc_lwz ((code), (dreg), off2, (dreg)); \
108 } while (0);
111 #define emit_tls_access(code,dreg,key) do { \
112 switch (tls_mode) { \
113 case TLS_MODE_LTHREADS: emit_linuxthreads_tls(code,dreg,key); break; \
114 default: g_assert_not_reached (); \
116 } while (0)
118 #define MONO_EMIT_NEW_LOAD_R8(cfg,dr,addr) do { \
119 MonoInst *inst; \
120 MONO_INST_NEW ((cfg), (inst), OP_R8CONST); \
121 inst->type = STACK_R8; \
122 inst->dreg = (dr); \
123 inst->inst_p0 = (void*)(addr); \
124 mono_bblock_add_inst (cfg->cbb, inst); \
125 } while (0)
127 #define ins_is_compare(ins) ((ins) && (((ins)->opcode == OP_COMPARE) \
128 || ((ins)->opcode == OP_ICOMPARE) \
129 || ((ins)->opcode == OP_LCOMPARE)))
130 #define ins_is_compare_imm(ins) ((ins) && (((ins)->opcode == OP_COMPARE_IMM) \
131 || ((ins)->opcode == OP_ICOMPARE_IMM) \
132 || ((ins)->opcode == OP_LCOMPARE_IMM)))
134 #define INS_REWRITE(ins, op, _s1, _s2) do { \
135 int s1 = _s1; \
136 int s2 = _s2; \
137 ins->opcode = (op); \
138 ins->sreg1 = (s1); \
139 ins->sreg2 = (s2); \
140 } while (0);
142 #define INS_REWRITE_IMM(ins, op, _s1, _imm) do { \
143 int s1 = _s1; \
144 ins->opcode = (op); \
145 ins->sreg1 = (s1); \
146 ins->inst_imm = (_imm); \
147 } while (0);
150 typedef struct InstList InstList;
152 struct InstList {
153 InstList *prev;
154 InstList *next;
155 MonoInst *data;
158 typedef enum {
159 ArgInIReg,
160 ArgOnStack,
161 ArgInFReg,
162 ArgStructByVal,
163 ArgStructByAddr
164 } ArgStorage;
166 typedef struct {
167 gint32 offset;
168 guint16 vtsize; /* in param area */
169 guint8 reg;
170 ArgStorage storage;
171 guint8 size : 4; /* 1, 2, 4, 8, or regs used by ArgStructByVal */
172 } ArgInfo;
174 typedef struct {
175 int nargs;
176 int gr;
177 int fr;
178 gboolean gr_passed;
179 gboolean on_stack;
180 gboolean vtype_retaddr;
181 int stack_size;
182 guint32 stack_usage;
183 guint32 struct_ret;
184 ArgInfo ret;
185 ArgInfo sig_cookie;
186 ArgInfo args [1];
187 } CallInfo;
189 void patch_lui_addiu(guint32 *ip, guint32 val);
190 guint8 *mono_arch_emit_epilog_sub (MonoCompile *cfg, guint8 *code);
191 guint8 *mips_emit_cond_branch (MonoCompile *cfg, guint8 *code, int op, MonoInst *ins);
192 void mips_adjust_stackframe(MonoCompile *cfg);
193 void mono_arch_emit_this_vret_args (MonoCompile *cfg, MonoCallInst *inst, int this_reg, int this_type, int vt_reg);
194 MonoInst *mono_arch_get_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args);
197 /* Not defined in asm/cachectl.h */
198 int cacheflush(char *addr, int nbytes, int cache);
200 void
201 mono_arch_flush_icache (guint8 *code, gint size)
203 /* Linux/MIPS specific */
204 cacheflush ((char*)code, size, BCACHE);
207 void
208 mono_arch_flush_register_windows (void)
212 gboolean
213 mono_arch_is_inst_imm (gint64 imm)
215 return TRUE;
218 static guint8 *
219 mips_emit_exc_by_name(guint8 *code, const char *name)
221 gpointer addr;
222 MonoClass *exc_class;
224 exc_class = mono_class_load_from_name (mono_defaults.corlib, "System", name);
226 mips_load_const (code, mips_a0, exc_class->type_token);
227 addr = mono_get_throw_corlib_exception ();
228 mips_call (code, mips_t9, addr);
229 return code;
233 guint8 *
234 mips_emit_load_const(guint8 *code, int dreg, mgreg_t v)
236 if (mips_is_imm16 (v))
237 mips_addiu (code, dreg, mips_zero, ((guint32)v) & 0xffff);
238 else {
239 #if SIZEOF_REGISTER == 8
240 if (v != (long) v) {
241 /* v is not a sign-extended 32-bit value */
242 mips_lui (code, dreg, mips_zero, (guint32)((v >> (32+16)) & 0xffff));
243 mips_ori (code, dreg, dreg, (guint32)((v >> (32)) & 0xffff));
244 mips_dsll (code, dreg, dreg, 16);
245 mips_ori (code, dreg, dreg, (guint32)((v >> (16)) & 0xffff));
246 mips_dsll (code, dreg, dreg, 16);
247 mips_ori (code, dreg, dreg, (guint32)(v & 0xffff));
248 return code;
250 #endif
251 if (((guint32)v) & (1 << 15)) {
252 mips_lui (code, dreg, mips_zero, (((guint32)v)>>16)+1);
254 else {
255 mips_lui (code, dreg, mips_zero, (((guint32)v)>>16));
257 if (((guint32)v) & 0xffff)
258 mips_addiu (code, dreg, dreg, ((guint32)v) & 0xffff);
260 return code;
263 guint8 *
264 mips_emit_cond_branch (MonoCompile *cfg, guint8 *code, int op, MonoInst *ins)
266 g_assert (ins);
267 if (cfg->arch.long_branch) {
268 int br_offset = 5;
270 /* Invert test and emit branch around jump */
271 switch (op) {
272 case OP_MIPS_BEQ:
273 mips_bne (code, ins->sreg1, ins->sreg2, br_offset);
274 mips_nop (code);
275 break;
276 case OP_MIPS_BNE:
277 mips_beq (code, ins->sreg1, ins->sreg2, br_offset);
278 mips_nop (code);
279 break;
280 case OP_MIPS_BGEZ:
281 mips_bltz (code, ins->sreg1, br_offset);
282 mips_nop (code);
283 break;
284 case OP_MIPS_BGTZ:
285 mips_blez (code, ins->sreg1, br_offset);
286 mips_nop (code);
287 break;
288 case OP_MIPS_BLEZ:
289 mips_bgtz (code, ins->sreg1, br_offset);
290 mips_nop (code);
291 break;
292 case OP_MIPS_BLTZ:
293 mips_bgez (code, ins->sreg1, br_offset);
294 mips_nop (code);
295 break;
296 default:
297 g_assert_not_reached ();
299 mono_add_patch_info (cfg, code - cfg->native_code,
300 MONO_PATCH_INFO_BB, ins->inst_true_bb);
301 mips_lui (code, mips_at, mips_zero, 0);
302 mips_addiu (code, mips_at, mips_at, 0);
303 mips_jr (code, mips_at);
304 mips_nop (code);
306 else {
307 mono_add_patch_info (cfg, code - cfg->native_code,
308 MONO_PATCH_INFO_BB, ins->inst_true_bb);
309 switch (op) {
310 case OP_MIPS_BEQ:
311 mips_beq (code, ins->sreg1, ins->sreg2, 0);
312 mips_nop (code);
313 break;
314 case OP_MIPS_BNE:
315 mips_bne (code, ins->sreg1, ins->sreg2, 0);
316 mips_nop (code);
317 break;
318 case OP_MIPS_BGEZ:
319 mips_bgez (code, ins->sreg1, 0);
320 mips_nop (code);
321 break;
322 case OP_MIPS_BGTZ:
323 mips_bgtz (code, ins->sreg1, 0);
324 mips_nop (code);
325 break;
326 case OP_MIPS_BLEZ:
327 mips_blez (code, ins->sreg1, 0);
328 mips_nop (code);
329 break;
330 case OP_MIPS_BLTZ:
331 mips_bltz (code, ins->sreg1, 0);
332 mips_nop (code);
333 break;
334 default:
335 g_assert_not_reached ();
338 return (code);
341 /* XXX - big-endian dependent? */
342 void
343 patch_lui_addiu(guint32 *ip, guint32 val)
345 guint16 *__lui_addiu = (guint16*)(void *)(ip);
347 #if 0
348 printf ("patch_lui_addiu ip=0x%08x (0x%08x, 0x%08x) to point to 0x%08x\n",
349 ip, ((guint32 *)ip)[0], ((guint32 *)ip)[1], val);
350 fflush (stdout);
351 #endif
352 if (((guint32)(val)) & (1 << 15))
353 __lui_addiu [MINI_LS_WORD_IDX] = ((((guint32)(val)) >> 16) & 0xffff) + 1;
354 else
355 __lui_addiu [MINI_LS_WORD_IDX] = (((guint32)(val)) >> 16) & 0xffff;
356 __lui_addiu [MINI_LS_WORD_IDX + 2] = ((guint32)(val)) & 0xffff;
357 mono_arch_flush_icache ((guint8 *)ip, 8);
360 guint32 trap_target;
361 void
362 mips_patch (guint32 *code, guint32 target)
364 guint32 ins = *code;
365 guint32 op = ins >> 26;
366 guint32 diff, offset;
368 g_assert (trap_target != target);
369 //printf ("patching 0x%08x (0x%08x) to point to 0x%08x\n", code, ins, target);
370 switch (op) {
371 case 0x00: /* jr ra */
372 if (ins == 0x3e00008)
373 break;
374 g_assert_not_reached ();
375 break;
376 case 0x02: /* j */
377 case 0x03: /* jal */
378 g_assert (!(target & 0x03));
379 g_assert ((target & 0xfc000000) == (((guint32)code) & 0xfc000000));
380 ins = (ins & 0xfc000000) | (((target) >> 2) & 0x03ffffff);
381 *code = ins;
382 mono_arch_flush_icache ((guint8 *)code, 4);
383 break;
384 case 0x01: /* BLTZ */
385 case 0x04: /* BEQ */
386 case 0x05: /* BNE */
387 case 0x06: /* BLEZ */
388 case 0x07: /* BGTZ */
389 case 0x11: /* bc1t */
390 diff = target - (guint32)(code + 1);
391 g_assert (((diff & 0x0003ffff) == diff) || ((diff | 0xfffc0000) == diff));
392 g_assert (!(diff & 0x03));
393 offset = ((gint32)diff) >> 2;
394 if (((int)offset) != ((int)(short)offset))
395 g_assert (((int)offset) == ((int)(short)offset));
396 ins = (ins & 0xffff0000) | (offset & 0x0000ffff);
397 *code = ins;
398 mono_arch_flush_icache ((guint8 *)code, 4);
399 break;
400 case 0x0f: /* LUI / ADDIU pair */
401 g_assert ((code[1] >> 26) == 0x9);
402 patch_lui_addiu (code, target);
403 mono_arch_flush_icache ((guint8 *)code, 8);
404 break;
406 default:
407 printf ("unknown op 0x%02x (0x%08x) @ %p\n", op, ins, code);
408 g_assert_not_reached ();
412 #if 0
413 static int
414 offsets_from_pthread_key (guint32 key, int *offset2)
416 int idx1 = key / 32;
417 int idx2 = key % 32;
418 *offset2 = idx2 * sizeof (gpointer);
419 return 284 + idx1 * sizeof (gpointer);
421 #endif
423 static void mono_arch_compute_omit_fp (MonoCompile *cfg);
425 const char*
426 mono_arch_regname (int reg) {
427 #if _MIPS_SIM == _ABIO32
428 static const char * rnames[] = {
429 "zero", "at", "v0", "v1",
430 "a0", "a1", "a2", "a3",
431 "t0", "t1", "t2", "t3",
432 "t4", "t5", "t6", "t7",
433 "s0", "s1", "s2", "s3",
434 "s4", "s5", "s6", "s7",
435 "t8", "t9", "k0", "k1",
436 "gp", "sp", "fp", "ra"
438 #elif _MIPS_SIM == _ABIN32
439 static const char * rnames[] = {
440 "zero", "at", "v0", "v1",
441 "a0", "a1", "a2", "a3",
442 "a4", "a5", "a6", "a7",
443 "t0", "t1", "t2", "t3",
444 "s0", "s1", "s2", "s3",
445 "s4", "s5", "s6", "s7",
446 "t8", "t9", "k0", "k1",
447 "gp", "sp", "fp", "ra"
449 #endif
450 if (reg >= 0 && reg < 32)
451 return rnames [reg];
452 return "unknown";
455 const char*
456 mono_arch_fregname (int reg) {
457 static const char * rnames[] = {
458 "f0", "f1", "f2", "f3",
459 "f4", "f5", "f6", "f7",
460 "f8", "f9", "f10", "f11",
461 "f12", "f13", "f14", "f15",
462 "f16", "f17", "f18", "f19",
463 "f20", "f21", "f22", "f23",
464 "f24", "f25", "f26", "f27",
465 "f28", "f29", "f30", "f31"
467 if (reg >= 0 && reg < 32)
468 return rnames [reg];
469 return "unknown";
472 /* this function overwrites at */
473 static guint8*
474 emit_memcpy (guint8 *code, int size, int dreg, int doffset, int sreg, int soffset)
476 /* XXX write a loop, not an unrolled loop */
477 while (size > 0) {
478 mips_lw (code, mips_at, sreg, soffset);
479 mips_sw (code, mips_at, dreg, doffset);
480 size -= 4;
481 soffset += 4;
482 doffset += 4;
484 return code;
488 * mono_arch_get_argument_info:
489 * @csig: a method signature
490 * @param_count: the number of parameters to consider
491 * @arg_info: an array to store the result infos
493 * Gathers information on parameters such as size, alignment and
494 * padding. arg_info should be large enought to hold param_count + 1 entries.
496 * Returns the size of the activation frame.
499 mono_arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJitArgumentInfo *arg_info)
501 int k, frame_size = 0;
502 guint32 size, align, pad;
503 int offset = 0;
505 if (MONO_TYPE_ISSTRUCT (csig->ret)) {
506 frame_size += sizeof (gpointer);
507 offset += 4;
510 arg_info [0].offset = offset;
512 if (csig->hasthis) {
513 frame_size += sizeof (gpointer);
514 offset += 4;
517 arg_info [0].size = frame_size;
519 for (k = 0; k < param_count; k++) {
520 size = mini_type_stack_size_full (csig->params [k], &align, csig->pinvoke);
522 /* ignore alignment for now */
523 align = 1;
525 frame_size += pad = (align - (frame_size & (align - 1))) & (align - 1);
526 arg_info [k].pad = pad;
527 frame_size += size;
528 arg_info [k + 1].pad = 0;
529 arg_info [k + 1].size = size;
530 offset += pad;
531 arg_info [k + 1].offset = offset;
532 offset += size;
535 align = MONO_ARCH_FRAME_ALIGNMENT;
536 frame_size += pad = (align - (frame_size & (align - 1))) & (align - 1);
537 arg_info [k].pad = pad;
539 return frame_size;
542 /* The delegate object plus 3 params */
543 #define MAX_ARCH_DELEGATE_PARAMS (4 - 1)
545 static gpointer
546 get_delegate_invoke_impl (MonoTrampInfo **info, gboolean has_target, gboolean param_count)
548 guint8 *code, *start;
550 if (has_target) {
551 start = code = mono_global_codeman_reserve (16);
553 /* Replace the this argument with the target */
554 mips_lw (code, mips_temp, mips_a0, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr));
555 mips_lw (code, mips_a0, mips_a0, MONO_STRUCT_OFFSET (MonoDelegate, target));
556 mips_jr (code, mips_temp);
557 mips_nop (code);
559 g_assert ((code - start) <= 16);
561 mono_arch_flush_icache (start, 16);
562 } else {
563 int size, i;
565 size = 16 + param_count * 4;
566 start = code = mono_global_codeman_reserve (size);
568 mips_lw (code, mips_temp, mips_a0, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr));
569 /* slide down the arguments */
570 for (i = 0; i < param_count; ++i) {
571 mips_move (code, mips_a0 + i, mips_a0 + i + 1);
573 mips_jr (code, mips_temp);
574 mips_nop (code);
576 g_assert ((code - start) <= size);
578 mono_arch_flush_icache (start, size);
581 if (has_target) {
582 *info = mono_tramp_info_create ("delegate_invoke_impl_has_target", start, code - start, NULL, NULL);
583 } else {
584 char *name = g_strdup_printf ("delegate_invoke_impl_target_%d", param_count);
585 *info = mono_tramp_info_create (name, start, code - start, NULL, NULL);
586 g_free (name);
589 return start;
593 * mono_arch_get_delegate_invoke_impls:
595 * Return a list of MonoAotTrampInfo structures for the delegate invoke impl
596 * trampolines.
598 GSList*
599 mono_arch_get_delegate_invoke_impls (void)
601 GSList *res = NULL;
602 MonoTrampInfo *info;
603 int i;
605 get_delegate_invoke_impl (&info, TRUE, 0);
606 res = g_slist_prepend (res, info);
608 for (i = 0; i <= MAX_ARCH_DELEGATE_PARAMS; ++i) {
609 get_delegate_invoke_impl (&info, FALSE, i);
610 res = g_slist_prepend (res, info);
613 return res;
616 gpointer
617 mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_target)
619 guint8 *code, *start;
621 /* FIXME: Support more cases */
622 if (MONO_TYPE_ISSTRUCT (sig->ret))
623 return NULL;
625 if (has_target) {
626 static guint8* cached = NULL;
627 mono_mini_arch_lock ();
628 if (cached) {
629 mono_mini_arch_unlock ();
630 return cached;
633 if (mono_aot_only) {
634 start = mono_aot_get_trampoline ("delegate_invoke_impl_has_target");
635 } else {
636 MonoTrampInfo *info;
637 start = get_delegate_invoke_impl (&info, TRUE, 0);
638 mono_tramp_info_register (info, NULL);
640 cached = start;
641 mono_mini_arch_unlock ();
642 return cached;
643 } else {
644 static guint8* cache [MAX_ARCH_DELEGATE_PARAMS + 1] = {NULL};
645 int i;
647 if (sig->param_count > MAX_ARCH_DELEGATE_PARAMS)
648 return NULL;
649 for (i = 0; i < sig->param_count; ++i)
650 if (!mono_is_regsize_var (sig->params [i]))
651 return NULL;
653 mono_mini_arch_lock ();
654 code = cache [sig->param_count];
655 if (code) {
656 mono_mini_arch_unlock ();
657 return code;
660 if (mono_aot_only) {
661 char *name = g_strdup_printf ("delegate_invoke_impl_target_%d", sig->param_count);
662 start = mono_aot_get_trampoline (name);
663 g_free (name);
664 } else {
665 MonoTrampInfo *info;
666 start = get_delegate_invoke_impl (&info, FALSE, sig->param_count);
667 mono_tramp_info_register (info, NULL);
669 cache [sig->param_count] = start;
670 mono_mini_arch_unlock ();
671 return start;
674 return NULL;
677 gpointer
678 mono_arch_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig, MonoMethod *method, int offset, gboolean load_imt_reg)
680 return NULL;
683 gpointer
684 mono_arch_get_this_arg_from_call (mgreg_t *regs, guint8 *code)
686 g_assert(regs);
687 return (gpointer)regs [mips_a0];
691 * Initialize the cpu to execute managed code.
693 void
694 mono_arch_cpu_init (void)
696 #if TARGET_BYTE_ORDER == G_LITTLE_ENDIAN
697 little_endian = 1;
698 ls_word_idx = 0;
699 ms_word_idx = 1;
700 #else
701 ls_word_idx = 1;
702 ms_word_idx = 0;
703 #endif
705 ls_word_offset = ls_word_idx * 4;
706 ms_word_offset = ms_word_idx * 4;
710 * Initialize architecture specific code.
712 void
713 mono_arch_init (void)
715 mono_os_mutex_init_recursive (&mini_arch_mutex);
717 ss_trigger_page = mono_valloc (NULL, mono_pagesize (), MONO_MMAP_READ|MONO_MMAP_32BIT, MONO_MEM_ACCOUNT_OTHER);
718 bp_trigger_page = mono_valloc (NULL, mono_pagesize (), MONO_MMAP_READ|MONO_MMAP_32BIT, MONO_MEM_ACCOUNT_OTHER);
719 mono_mprotect (bp_trigger_page, mono_pagesize (), 0);
723 * Cleanup architecture specific code.
725 void
726 mono_arch_cleanup (void)
728 mono_os_mutex_destroy (&mini_arch_mutex);
732 * This function returns the optimizations supported on this cpu.
734 guint32
735 mono_arch_cpu_optimizations (guint32 *exclude_mask)
737 guint32 opts = 0;
739 /* no mips-specific optimizations yet */
740 *exclude_mask = 0;
741 return opts;
745 * This function test for all SIMD functions supported.
747 * Returns a bitmask corresponding to all supported versions.
750 guint32
751 mono_arch_cpu_enumerate_simd_versions (void)
753 /* SIMD is currently unimplemented */
754 return 0;
757 GList *
758 mono_arch_get_allocatable_int_vars (MonoCompile *cfg)
760 GList *vars = NULL;
761 int i;
763 for (i = 0; i < cfg->num_varinfo; i++) {
764 MonoInst *ins = cfg->varinfo [i];
765 MonoMethodVar *vmv = MONO_VARINFO (cfg, i);
767 /* unused vars */
768 if (vmv->range.first_use.abs_pos >= vmv->range.last_use.abs_pos)
769 continue;
771 if (ins->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT) || (ins->opcode != OP_LOCAL && ins->opcode != OP_ARG))
772 continue;
774 /* we can only allocate 32 bit values */
775 if (mono_is_regsize_var (ins->inst_vtype)) {
776 g_assert (MONO_VARINFO (cfg, i)->reg == -1);
777 g_assert (i == vmv->idx);
778 vars = mono_varlist_insert_sorted (cfg, vars, vmv, FALSE);
782 return vars;
785 GList *
786 mono_arch_get_global_int_regs (MonoCompile *cfg)
788 GList *regs = NULL;
790 regs = g_list_prepend (regs, (gpointer)mips_s0);
791 regs = g_list_prepend (regs, (gpointer)mips_s1);
792 regs = g_list_prepend (regs, (gpointer)mips_s2);
793 regs = g_list_prepend (regs, (gpointer)mips_s3);
794 regs = g_list_prepend (regs, (gpointer)mips_s4);
795 //regs = g_list_prepend (regs, (gpointer)mips_s5);
796 regs = g_list_prepend (regs, (gpointer)mips_s6);
797 regs = g_list_prepend (regs, (gpointer)mips_s7);
799 return regs;
803 * mono_arch_regalloc_cost:
805 * Return the cost, in number of memory references, of the action of
806 * allocating the variable VMV into a register during global register
807 * allocation.
809 guint32
810 mono_arch_regalloc_cost (MonoCompile *cfg, MonoMethodVar *vmv)
812 /* FIXME: */
813 return 2;
816 static void
817 args_onto_stack (CallInfo *info)
819 g_assert (!info->on_stack);
820 g_assert (info->stack_size <= MIPS_STACK_PARAM_OFFSET);
821 info->on_stack = TRUE;
822 info->stack_size = MIPS_STACK_PARAM_OFFSET;
825 #if _MIPS_SIM == _ABIO32
827 * O32 calling convention version
830 static void
831 add_int32_arg (CallInfo *info, ArgInfo *ainfo) {
832 /* First, see if we need to drop onto the stack */
833 if (!info->on_stack && info->gr > MIPS_LAST_ARG_REG)
834 args_onto_stack (info);
836 /* Now, place the argument */
837 if (info->on_stack) {
838 ainfo->storage = ArgOnStack;
839 ainfo->reg = mips_sp; /* in the caller */
840 ainfo->offset = info->stack_size;
842 else {
843 ainfo->storage = ArgInIReg;
844 ainfo->reg = info->gr;
845 info->gr += 1;
846 info->gr_passed = TRUE;
848 info->stack_size += 4;
851 static void
852 add_int64_arg (CallInfo *info, ArgInfo *ainfo) {
853 /* First, see if we need to drop onto the stack */
854 if (!info->on_stack && info->gr+1 > MIPS_LAST_ARG_REG)
855 args_onto_stack (info);
857 /* Now, place the argument */
858 if (info->on_stack) {
859 g_assert (info->stack_size % 4 == 0);
860 info->stack_size += (info->stack_size % 8);
862 ainfo->storage = ArgOnStack;
863 ainfo->reg = mips_sp; /* in the caller */
864 ainfo->offset = info->stack_size;
866 else {
867 // info->gr must be a0 or a2
868 info->gr += (info->gr - MIPS_FIRST_ARG_REG) % 2;
869 g_assert(info->gr <= MIPS_LAST_ARG_REG);
871 ainfo->storage = ArgInIReg;
872 ainfo->reg = info->gr;
873 info->gr += 2;
874 info->gr_passed = TRUE;
876 info->stack_size += 8;
879 static void
880 add_float32_arg (CallInfo *info, ArgInfo *ainfo) {
881 /* First, see if we need to drop onto the stack */
882 if (!info->on_stack && info->gr > MIPS_LAST_ARG_REG)
883 args_onto_stack (info);
885 /* Now, place the argument */
886 if (info->on_stack) {
887 ainfo->storage = ArgOnStack;
888 ainfo->reg = mips_sp; /* in the caller */
889 ainfo->offset = info->stack_size;
891 else {
892 /* Only use FP regs for args if no int args passed yet */
893 if (!info->gr_passed && info->fr <= MIPS_LAST_FPARG_REG) {
894 ainfo->storage = ArgInFReg;
895 ainfo->reg = info->fr;
896 /* Even though it's a single-precision float, it takes up two FP regs */
897 info->fr += 2;
898 /* FP and GP slots do not overlap */
899 info->gr += 1;
901 else {
902 /* Passing single-precision float arg in a GP register
903 * such as: func (0, 1.0, 2, 3);
904 * In this case, only one 'gr' register is consumed.
906 ainfo->storage = ArgInIReg;
907 ainfo->reg = info->gr;
909 info->gr += 1;
910 info->gr_passed = TRUE;
913 info->stack_size += 4;
916 static void
917 add_float64_arg (CallInfo *info, ArgInfo *ainfo) {
918 /* First, see if we need to drop onto the stack */
919 if (!info->on_stack && info->gr+1 > MIPS_LAST_ARG_REG)
920 args_onto_stack (info);
922 /* Now, place the argument */
923 if (info->on_stack) {
924 g_assert(info->stack_size % 4 == 0);
925 info->stack_size += (info->stack_size % 8);
927 ainfo->storage = ArgOnStack;
928 ainfo->reg = mips_sp; /* in the caller */
929 ainfo->offset = info->stack_size;
931 else {
932 /* Only use FP regs for args if no int args passed yet */
933 if (!info->gr_passed && info->fr <= MIPS_LAST_FPARG_REG) {
934 ainfo->storage = ArgInFReg;
935 ainfo->reg = info->fr;
936 info->fr += 2;
937 /* FP and GP slots do not overlap */
938 info->gr += 2;
940 else {
941 // info->gr must be a0 or a2
942 info->gr += (info->gr - MIPS_FIRST_ARG_REG) % 2;
943 g_assert(info->gr <= MIPS_LAST_ARG_REG);
945 ainfo->storage = ArgInIReg;
946 ainfo->reg = info->gr;
947 info->gr += 2;
948 info->gr_passed = TRUE;
951 info->stack_size += 8;
953 #elif _MIPS_SIM == _ABIN32
955 * N32 calling convention version
958 static void
959 add_int32_arg (CallInfo *info, ArgInfo *ainfo) {
960 /* First, see if we need to drop onto the stack */
961 if (!info->on_stack && info->gr > MIPS_LAST_ARG_REG)
962 args_onto_stack (info);
964 /* Now, place the argument */
965 if (info->on_stack) {
966 ainfo->storage = ArgOnStack;
967 ainfo->reg = mips_sp; /* in the caller */
968 ainfo->offset = info->stack_size;
969 info->stack_size += SIZEOF_REGISTER;
971 else {
972 ainfo->storage = ArgInIReg;
973 ainfo->reg = info->gr;
974 info->gr += 1;
975 info->gr_passed = TRUE;
979 static void
980 add_int64_arg (CallInfo *info, ArgInfo *ainfo) {
981 /* First, see if we need to drop onto the stack */
982 if (!info->on_stack && info->gr > MIPS_LAST_ARG_REG)
983 args_onto_stack (info);
985 /* Now, place the argument */
986 if (info->on_stack) {
987 g_assert (info->stack_size % 4 == 0);
988 info->stack_size += (info->stack_size % 8);
990 ainfo->storage = ArgOnStack;
991 ainfo->reg = mips_sp; /* in the caller */
992 ainfo->offset = info->stack_size;
993 info->stack_size += SIZEOF_REGISTER;
995 else {
996 g_assert (info->gr <= MIPS_LAST_ARG_REG);
998 ainfo->storage = ArgInIReg;
999 ainfo->reg = info->gr;
1000 info->gr += 1;
1001 info->gr_passed = TRUE;
1005 static void
1006 add_float32_arg (CallInfo *info, ArgInfo *ainfo) {
1007 /* First, see if we need to drop onto the stack */
1008 if (!info->on_stack) {
1009 if (info->gr > MIPS_LAST_ARG_REG)
1010 args_onto_stack (info);
1011 else if (info->fr > MIPS_LAST_FPARG_REG)
1012 args_onto_stack (info);
1015 /* Now, place the argument */
1016 if (info->on_stack) {
1017 ainfo->storage = ArgOnStack;
1018 ainfo->reg = mips_sp; /* in the caller */
1019 ainfo->offset = info->stack_size;
1020 info->stack_size += FREG_SIZE;
1022 else {
1023 ainfo->storage = ArgInFReg;
1024 ainfo->reg = info->fr;
1025 info->fr += 1;
1026 /* FP and GP slots do not overlap */
1027 info->gr += 1;
1031 static void
1032 add_float64_arg (CallInfo *info, ArgInfo *ainfo) {
1033 /* First, see if we need to drop onto the stack */
1034 if (!info->on_stack) {
1035 if (info->gr > MIPS_LAST_ARG_REG)
1036 args_onto_stack (info);
1037 else if (info->fr > MIPS_LAST_FPARG_REG)
1038 args_onto_stack (info);
1041 /* Now, place the argument */
1042 if (info->on_stack) {
1043 g_assert(info->stack_size % 4 == 0);
1044 info->stack_size += (info->stack_size % 8);
1046 ainfo->storage = ArgOnStack;
1047 ainfo->reg = mips_sp; /* in the caller */
1048 ainfo->offset = info->stack_size;
1049 info->stack_size += FREG_SIZE;
1051 else {
1052 ainfo->storage = ArgInFReg;
1053 ainfo->reg = info->fr;
1054 info->fr += 1;
1055 /* FP and GP slots do not overlap */
1056 info->gr += 1;
1059 #endif
1061 static CallInfo*
1062 get_call_info (MonoMemPool *mp, MonoMethodSignature *sig)
1064 guint i;
1065 int n = sig->hasthis + sig->param_count;
1066 int pstart;
1067 MonoType* simpletype;
1068 CallInfo *cinfo;
1069 gboolean is_pinvoke = sig->pinvoke;
1071 if (mp)
1072 cinfo = mono_mempool_alloc0 (mp, sizeof (CallInfo) + (sizeof (ArgInfo) * n));
1073 else
1074 cinfo = g_malloc0 (sizeof (CallInfo) + (sizeof (ArgInfo) * n));
1076 cinfo->fr = MIPS_FIRST_FPARG_REG;
1077 cinfo->gr = MIPS_FIRST_ARG_REG;
1078 cinfo->stack_size = 0;
1080 DEBUG(printf("calculate_sizes\n"));
1082 cinfo->vtype_retaddr = MONO_TYPE_ISSTRUCT (sig->ret) ? TRUE : FALSE;
1083 pstart = 0;
1084 n = 0;
1085 #if 0
1086 /* handle returning a struct */
1087 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
1088 cinfo->struct_ret = cinfo->gr;
1089 add_int32_arg (cinfo, &cinfo->ret);
1092 if (sig->hasthis) {
1093 add_int32_arg (cinfo, cinfo->args + n);
1094 n++;
1096 #else
1098 * To simplify get_this_arg_reg () and LLVM integration, emit the vret arg after
1099 * the first argument, allowing 'this' to be always passed in the first arg reg.
1100 * Also do this if the first argument is a reference type, since virtual calls
1101 * are sometimes made using calli without sig->hasthis set, like in the delegate
1102 * invoke wrappers.
1104 if (cinfo->vtype_retaddr && !is_pinvoke && (sig->hasthis || (sig->param_count > 0 && MONO_TYPE_IS_REFERENCE (mini_get_underlying_type (sig->params [0]))))) {
1105 if (sig->hasthis) {
1106 add_int32_arg (cinfo, cinfo->args + n);
1107 n ++;
1108 } else {
1109 add_int32_arg (cinfo, cinfo->args + sig->hasthis);
1110 pstart = 1;
1111 n ++;
1113 add_int32_arg (cinfo, &cinfo->ret);
1114 cinfo->struct_ret = cinfo->ret.reg;
1115 } else {
1116 /* this */
1117 if (sig->hasthis) {
1118 add_int32_arg (cinfo, cinfo->args + n);
1119 n ++;
1122 if (cinfo->vtype_retaddr) {
1123 add_int32_arg (cinfo, &cinfo->ret);
1124 cinfo->struct_ret = cinfo->ret.reg;
1127 #endif
1129 DEBUG(printf("params: %d\n", sig->param_count));
1130 for (i = pstart; i < sig->param_count; ++i) {
1131 if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
1132 /* Prevent implicit arguments and sig_cookie from
1133 being passed in registers */
1134 args_onto_stack (cinfo);
1135 /* Emit the signature cookie just before the implicit arguments */
1136 add_int32_arg (cinfo, &cinfo->sig_cookie);
1138 DEBUG(printf("param %d: ", i));
1139 simpletype = mini_get_underlying_type (sig->params [i]);
1140 switch (simpletype->type) {
1141 case MONO_TYPE_BOOLEAN:
1142 case MONO_TYPE_I1:
1143 case MONO_TYPE_U1:
1144 DEBUG(printf("1 byte\n"));
1145 cinfo->args [n].size = 1;
1146 add_int32_arg (cinfo, &cinfo->args[n]);
1147 n++;
1148 break;
1149 case MONO_TYPE_CHAR:
1150 case MONO_TYPE_I2:
1151 case MONO_TYPE_U2:
1152 DEBUG(printf("2 bytes\n"));
1153 cinfo->args [n].size = 2;
1154 add_int32_arg (cinfo, &cinfo->args[n]);
1155 n++;
1156 break;
1157 case MONO_TYPE_I4:
1158 case MONO_TYPE_U4:
1159 DEBUG(printf("4 bytes\n"));
1160 cinfo->args [n].size = 4;
1161 add_int32_arg (cinfo, &cinfo->args[n]);
1162 n++;
1163 break;
1164 case MONO_TYPE_I:
1165 case MONO_TYPE_U:
1166 case MONO_TYPE_PTR:
1167 case MONO_TYPE_FNPTR:
1168 case MONO_TYPE_CLASS:
1169 case MONO_TYPE_OBJECT:
1170 case MONO_TYPE_STRING:
1171 case MONO_TYPE_SZARRAY:
1172 case MONO_TYPE_ARRAY:
1173 cinfo->args [n].size = sizeof (gpointer);
1174 add_int32_arg (cinfo, &cinfo->args[n]);
1175 n++;
1176 break;
1177 case MONO_TYPE_GENERICINST:
1178 if (!mono_type_generic_inst_is_valuetype (simpletype)) {
1179 cinfo->args [n].size = sizeof (gpointer);
1180 add_int32_arg (cinfo, &cinfo->args[n]);
1181 n++;
1182 break;
1184 /* Fall through */
1185 case MONO_TYPE_TYPEDBYREF:
1186 case MONO_TYPE_VALUETYPE: {
1187 int j;
1188 int nwords = 0;
1189 int has_offset = FALSE;
1190 ArgInfo dummy_arg;
1191 gint size, alignment;
1192 MonoClass *klass;
1194 if (simpletype->type == MONO_TYPE_TYPEDBYREF) {
1195 size = sizeof (MonoTypedRef);
1196 alignment = sizeof (gpointer);
1197 } else {
1198 klass = mono_class_from_mono_type (sig->params [i]);
1199 if (is_pinvoke)
1200 size = mono_class_native_size (klass, NULL);
1201 else
1202 size = mono_class_value_size (klass, NULL);
1203 alignment = mono_class_min_align (klass);
1205 #if MIPS_PASS_STRUCTS_BY_VALUE
1206 /* Need to do alignment if struct contains long or double */
1207 if (alignment > 4) {
1208 /* Drop onto stack *before* looking at
1209 stack_size, if required. */
1210 if (!cinfo->on_stack && cinfo->gr > MIPS_LAST_ARG_REG)
1211 args_onto_stack (cinfo);
1212 if (cinfo->stack_size & (alignment - 1)) {
1213 add_int32_arg (cinfo, &dummy_arg);
1215 g_assert (!(cinfo->stack_size & (alignment - 1)));
1218 #if 0
1219 g_printf ("valuetype struct size=%d offset=%d align=%d\n",
1220 mono_class_native_size (sig->params [i]->data.klass, NULL),
1221 cinfo->stack_size, alignment);
1222 #endif
1223 nwords = (size + sizeof (gpointer) -1 ) / sizeof (gpointer);
1224 g_assert (cinfo->args [n].size == 0);
1225 g_assert (cinfo->args [n].vtsize == 0);
1226 for (j = 0; j < nwords; ++j) {
1227 if (j == 0) {
1228 add_int32_arg (cinfo, &cinfo->args [n]);
1229 if (cinfo->on_stack)
1230 has_offset = TRUE;
1231 } else {
1232 add_int32_arg (cinfo, &dummy_arg);
1233 if (!has_offset && cinfo->on_stack) {
1234 cinfo->args [n].offset = dummy_arg.offset;
1235 has_offset = TRUE;
1238 if (cinfo->on_stack)
1239 cinfo->args [n].vtsize += 1;
1240 else
1241 cinfo->args [n].size += 1;
1243 //g_printf ("\tstack_size=%d vtsize=%d\n", cinfo->args [n].size, cinfo->args[n].vtsize);
1244 cinfo->args [n].storage = ArgStructByVal;
1245 #else
1246 add_int32_arg (cinfo, &cinfo->args[n]);
1247 cinfo->args [n].storage = ArgStructByAddr;
1248 #endif
1249 n++;
1250 break;
1252 case MONO_TYPE_U8:
1253 case MONO_TYPE_I8:
1254 DEBUG(printf("8 bytes\n"));
1255 cinfo->args [n].size = 8;
1256 add_int64_arg (cinfo, &cinfo->args[n]);
1257 n++;
1258 break;
1259 case MONO_TYPE_R4:
1260 DEBUG(printf("R4\n"));
1261 cinfo->args [n].size = 4;
1262 add_float32_arg (cinfo, &cinfo->args[n]);
1263 n++;
1264 break;
1265 case MONO_TYPE_R8:
1266 DEBUG(printf("R8\n"));
1267 cinfo->args [n].size = 8;
1268 add_float64_arg (cinfo, &cinfo->args[n]);
1269 n++;
1270 break;
1271 default:
1272 g_error ("Can't trampoline 0x%x", sig->params [i]->type);
1276 /* Handle the case where there are no implicit arguments */
1277 if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
1278 /* Prevent implicit arguments and sig_cookie from
1279 being passed in registers */
1280 args_onto_stack (cinfo);
1281 /* Emit the signature cookie just before the implicit arguments */
1282 add_int32_arg (cinfo, &cinfo->sig_cookie);
1286 simpletype = mini_get_underlying_type (sig->ret);
1287 switch (simpletype->type) {
1288 case MONO_TYPE_BOOLEAN:
1289 case MONO_TYPE_I1:
1290 case MONO_TYPE_U1:
1291 case MONO_TYPE_I2:
1292 case MONO_TYPE_U2:
1293 case MONO_TYPE_CHAR:
1294 case MONO_TYPE_I4:
1295 case MONO_TYPE_U4:
1296 case MONO_TYPE_I:
1297 case MONO_TYPE_U:
1298 case MONO_TYPE_PTR:
1299 case MONO_TYPE_FNPTR:
1300 case MONO_TYPE_CLASS:
1301 case MONO_TYPE_OBJECT:
1302 case MONO_TYPE_SZARRAY:
1303 case MONO_TYPE_ARRAY:
1304 case MONO_TYPE_STRING:
1305 cinfo->ret.reg = mips_v0;
1306 break;
1307 case MONO_TYPE_U8:
1308 case MONO_TYPE_I8:
1309 cinfo->ret.reg = mips_v0;
1310 break;
1311 case MONO_TYPE_R4:
1312 case MONO_TYPE_R8:
1313 cinfo->ret.reg = mips_f0;
1314 cinfo->ret.storage = ArgInFReg;
1315 break;
1316 case MONO_TYPE_GENERICINST:
1317 if (!mono_type_generic_inst_is_valuetype (simpletype)) {
1318 cinfo->ret.reg = mips_v0;
1319 break;
1321 break;
1322 case MONO_TYPE_VALUETYPE:
1323 case MONO_TYPE_TYPEDBYREF:
1324 break;
1325 case MONO_TYPE_VOID:
1326 break;
1327 default:
1328 g_error ("Can't handle as return value 0x%x", sig->ret->type);
1332 /* align stack size to 16 */
1333 cinfo->stack_size = (cinfo->stack_size + MIPS_STACK_ALIGNMENT - 1) & ~(MIPS_STACK_ALIGNMENT - 1);
1335 cinfo->stack_usage = cinfo->stack_size;
1336 return cinfo;
1339 static gboolean
1340 debug_omit_fp (void)
1342 #if 0
1343 return mono_debug_count ();
1344 #else
1345 return TRUE;
1346 #endif
1350 * mono_arch_compute_omit_fp:
1352 * Determine whenever the frame pointer can be eliminated.
1354 static void
1355 mono_arch_compute_omit_fp (MonoCompile *cfg)
1357 MonoMethodSignature *sig;
1358 MonoMethodHeader *header;
1359 int i, locals_size;
1360 CallInfo *cinfo;
1362 if (cfg->arch.omit_fp_computed)
1363 return;
1365 header = cfg->header;
1367 sig = mono_method_signature (cfg->method);
1369 if (!cfg->arch.cinfo)
1370 cfg->arch.cinfo = get_call_info (cfg->mempool, sig);
1371 cinfo = cfg->arch.cinfo;
1374 * FIXME: Remove some of the restrictions.
1376 cfg->arch.omit_fp = TRUE;
1377 cfg->arch.omit_fp_computed = TRUE;
1379 if (cfg->disable_omit_fp)
1380 cfg->arch.omit_fp = FALSE;
1381 if (!debug_omit_fp ())
1382 cfg->arch.omit_fp = FALSE;
1383 if (cfg->method->save_lmf)
1384 cfg->arch.omit_fp = FALSE;
1385 if (cfg->flags & MONO_CFG_HAS_ALLOCA)
1386 cfg->arch.omit_fp = FALSE;
1387 if (header->num_clauses)
1388 cfg->arch.omit_fp = FALSE;
1389 if (!sig->pinvoke && (sig->call_convention == MONO_CALL_VARARG))
1390 cfg->arch.omit_fp = FALSE;
1391 if ((mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method)) ||
1392 (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE))
1393 cfg->arch.omit_fp = FALSE;
1395 * On MIPS, fp points to the bottom of the frame, so it can be eliminated even if
1396 * there are stack arguments.
1399 if (cinfo->stack_usage)
1400 cfg->arch.omit_fp = FALSE;
1403 locals_size = 0;
1404 for (i = cfg->locals_start; i < cfg->num_varinfo; i++) {
1405 MonoInst *ins = cfg->varinfo [i];
1406 int ialign;
1408 locals_size += mono_type_size (ins->inst_vtype, &ialign);
1411 //printf ("D: %s %d\n", cfg->method->name, cfg->arch.omit_fp);
1415 * Set var information according to the calling convention. mips version.
1416 * The locals var stuff should most likely be split in another method.
1418 void
1419 mono_arch_allocate_vars (MonoCompile *cfg)
1421 MonoMethodSignature *sig;
1422 MonoMethodHeader *header;
1423 MonoInst *inst;
1424 int i, offset, size, align, curinst;
1425 int frame_reg = mips_sp;
1426 guint32 iregs_to_save = 0;
1427 #if SAVE_FP_REGS
1428 guint32 fregs_to_restore;
1429 #endif
1430 CallInfo *cinfo;
1432 sig = mono_method_signature (cfg->method);
1434 if (!cfg->arch.cinfo)
1435 cfg->arch.cinfo = get_call_info (cfg->mempool, sig);
1436 cinfo = cfg->arch.cinfo;
1438 mono_arch_compute_omit_fp (cfg);
1440 /* spill down, we'll fix it in a separate pass */
1441 // cfg->flags |= MONO_CFG_HAS_SPILLUP;
1443 /* allow room for the vararg method args: void* and long/double */
1444 if (mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method))
1445 cfg->param_area = MAX (cfg->param_area, sizeof (gpointer)*8);
1447 /* this is bug #60332: remove when #59509 is fixed, so no weird vararg
1448 * call convs needs to be handled this way.
1450 if (cfg->flags & MONO_CFG_HAS_VARARGS)
1451 cfg->param_area = MAX (cfg->param_area, sizeof (gpointer)*8);
1453 /* gtk-sharp and other broken code will dllimport vararg functions even with
1454 * non-varargs signatures. Since there is little hope people will get this right
1455 * we assume they won't.
1457 if (cfg->method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE)
1458 cfg->param_area = MAX (cfg->param_area, sizeof (gpointer)*8);
1460 /* a0-a3 always present */
1461 cfg->param_area = MAX (cfg->param_area, MIPS_STACK_PARAM_OFFSET);
1463 header = cfg->header;
1465 if (cfg->arch.omit_fp)
1466 frame_reg = mips_sp;
1467 else
1468 frame_reg = mips_fp;
1469 cfg->frame_reg = frame_reg;
1470 if (frame_reg != mips_sp) {
1471 cfg->used_int_regs |= 1 << frame_reg;
1474 offset = 0;
1475 curinst = 0;
1476 if (!MONO_TYPE_ISSTRUCT (sig->ret)) {
1477 /* FIXME: handle long and FP values */
1478 switch (mini_get_underlying_type (sig->ret)->type) {
1479 case MONO_TYPE_VOID:
1480 break;
1481 case MONO_TYPE_R4:
1482 case MONO_TYPE_R8:
1483 cfg->ret->opcode = OP_REGVAR;
1484 cfg->ret->inst_c0 = cfg->ret->dreg = mips_f0;
1485 break;
1486 default:
1487 cfg->ret->opcode = OP_REGVAR;
1488 cfg->ret->inst_c0 = mips_v0;
1489 break;
1492 /* Space for outgoing parameters, including a0-a3 */
1493 offset += cfg->param_area;
1495 /* allow room to save the return value (if it's a struct) */
1496 if (mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method))
1497 offset += 8;
1499 /* Now handle the local variables */
1501 curinst = cfg->locals_start;
1502 for (i = curinst; i < cfg->num_varinfo; ++i) {
1503 inst = cfg->varinfo [i];
1504 if ((inst->flags & MONO_INST_IS_DEAD) || inst->opcode == OP_REGVAR)
1505 continue;
1507 /* inst->backend.is_pinvoke indicates native sized value types, this is used by the
1508 * pinvoke wrappers when they call functions returning structure
1510 if (inst->backend.is_pinvoke && MONO_TYPE_ISSTRUCT (inst->inst_vtype) && inst->inst_vtype->type != MONO_TYPE_TYPEDBYREF)
1511 size = mono_class_native_size (mono_class_from_mono_type (inst->inst_vtype), (unsigned int *) &align);
1512 else
1513 size = mono_type_size (inst->inst_vtype, &align);
1515 offset += align - 1;
1516 offset &= ~(align - 1);
1517 inst->inst_offset = offset;
1518 inst->opcode = OP_REGOFFSET;
1519 inst->inst_basereg = frame_reg;
1520 offset += size;
1521 // g_print ("allocating local %d to %d\n", i, inst->inst_offset);
1524 /* Space for LMF (if needed) */
1525 if (cfg->method->save_lmf) {
1526 /* align the offset to 16 bytes */
1527 offset = (offset + MIPS_STACK_ALIGNMENT - 1) & ~(MIPS_STACK_ALIGNMENT - 1);
1528 cfg->arch.lmf_offset = offset;
1529 offset += sizeof (MonoLMF);
1532 if (sig->call_convention == MONO_CALL_VARARG) {
1533 size = 4;
1534 align = 4;
1536 /* Allocate a local slot to hold the sig cookie address */
1537 offset += align - 1;
1538 offset &= ~(align - 1);
1539 cfg->sig_cookie = offset;
1540 offset += size;
1543 offset += SIZEOF_REGISTER - 1;
1544 offset &= ~(SIZEOF_REGISTER - 1);
1546 /* Space for saved registers */
1547 cfg->arch.iregs_offset = offset;
1548 iregs_to_save = (cfg->used_int_regs & MONO_ARCH_CALLEE_SAVED_REGS);
1549 if (iregs_to_save) {
1550 for (i = MONO_MAX_IREGS-1; i >= 0; --i) {
1551 if (iregs_to_save & (1 << i)) {
1552 offset += SIZEOF_REGISTER;
1557 /* saved float registers */
1558 #if SAVE_FP_REGS
1559 fregs_to_restore = (cfg->used_float_regs & MONO_ARCH_CALLEE_SAVED_FREGS);
1560 if (fregs_to_restore) {
1561 for (i = MONO_MAX_FREGS-1; i >= 0; --i) {
1562 if (fregs_to_restore & (1 << i)) {
1563 offset += sizeof(double);
1567 #endif
1569 #if _MIPS_SIM == _ABIO32
1570 /* Now add space for saving the ra */
1571 offset += SIZEOF_VOID_P;
1573 /* change sign? */
1574 offset = (offset + MIPS_STACK_ALIGNMENT - 1) & ~(MIPS_STACK_ALIGNMENT - 1);
1575 cfg->stack_offset = offset;
1576 cfg->arch.local_alloc_offset = cfg->stack_offset;
1577 #endif
1580 * Now allocate stack slots for the int arg regs (a0 - a3)
1581 * On MIPS o32, these are just above the incoming stack pointer
1582 * Even if the arg has been assigned to a regvar, it gets a stack slot
1585 /* Return struct-by-value results in a hidden first argument */
1586 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
1587 cfg->vret_addr->opcode = OP_REGOFFSET;
1588 cfg->vret_addr->inst_c0 = mips_a0;
1589 cfg->vret_addr->inst_offset = offset;
1590 cfg->vret_addr->inst_basereg = frame_reg;
1591 offset += SIZEOF_REGISTER;
1594 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
1595 inst = cfg->args [i];
1596 if (inst->opcode != OP_REGVAR) {
1597 MonoType *arg_type;
1599 if (sig->hasthis && (i == 0))
1600 arg_type = &mono_defaults.object_class->byval_arg;
1601 else
1602 arg_type = sig->params [i - sig->hasthis];
1604 inst->opcode = OP_REGOFFSET;
1605 size = mono_type_size (arg_type, &align);
1607 if (size < SIZEOF_REGISTER) {
1608 size = SIZEOF_REGISTER;
1609 align = SIZEOF_REGISTER;
1611 inst->inst_basereg = frame_reg;
1612 offset = (offset + align - 1) & ~(align - 1);
1613 inst->inst_offset = offset;
1614 offset += size;
1615 if (cfg->verbose_level > 1)
1616 printf ("allocating param %d to fp[%d]\n", i, inst->inst_offset);
1618 else {
1619 #if _MIPS_SIM == _ABIO32
1620 /* o32: Even a0-a3 get stack slots */
1621 size = SIZEOF_REGISTER;
1622 align = SIZEOF_REGISTER;
1623 inst->inst_basereg = frame_reg;
1624 offset = (offset + align - 1) & ~(align - 1);
1625 inst->inst_offset = offset;
1626 offset += size;
1627 if (cfg->verbose_level > 1)
1628 printf ("allocating param %d to fp[%d]\n", i, inst->inst_offset);
1629 #endif
1632 #if _MIPS_SIM == _ABIN32
1633 /* Now add space for saving the ra */
1634 offset += SIZEOF_VOID_P;
1636 /* change sign? */
1637 offset = (offset + MIPS_STACK_ALIGNMENT - 1) & ~(MIPS_STACK_ALIGNMENT - 1);
1638 cfg->stack_offset = offset;
1639 cfg->arch.local_alloc_offset = cfg->stack_offset;
1640 #endif
1643 void
1644 mono_arch_create_vars (MonoCompile *cfg)
1646 MonoMethodSignature *sig;
1648 sig = mono_method_signature (cfg->method);
1650 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
1651 cfg->vret_addr = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_ARG);
1652 if (G_UNLIKELY (cfg->verbose_level > 1)) {
1653 printf ("vret_addr = ");
1654 mono_print_ins (cfg->vret_addr);
1659 /* Fixme: we need an alignment solution for enter_method and mono_arch_call_opcode,
1660 * currently alignment in mono_arch_call_opcode is computed without arch_get_argument_info
1664 * take the arguments and generate the arch-specific
1665 * instructions to properly call the function in call.
1666 * This includes pushing, moving arguments to the right register
1667 * etc.
1668 * Issue: who does the spilling if needed, and when?
1670 static void
1671 emit_sig_cookie (MonoCompile *cfg, MonoCallInst *call, CallInfo *cinfo)
1673 MonoMethodSignature *tmp_sig;
1674 MonoInst *sig_arg;
1676 if (call->tail_call)
1677 NOT_IMPLEMENTED;
1679 /* FIXME: Add support for signature tokens to AOT */
1680 cfg->disable_aot = TRUE;
1683 * mono_ArgIterator_Setup assumes the signature cookie is
1684 * passed first and all the arguments which were before it are
1685 * passed on the stack after the signature. So compensate by
1686 * passing a different signature.
1688 tmp_sig = mono_metadata_signature_dup (call->signature);
1689 tmp_sig->param_count -= call->signature->sentinelpos;
1690 tmp_sig->sentinelpos = 0;
1691 memcpy (tmp_sig->params, call->signature->params + call->signature->sentinelpos, tmp_sig->param_count * sizeof (MonoType*));
1693 MONO_INST_NEW (cfg, sig_arg, OP_ICONST);
1694 sig_arg->dreg = mono_alloc_ireg (cfg);
1695 sig_arg->inst_p0 = tmp_sig;
1696 MONO_ADD_INS (cfg->cbb, sig_arg);
1698 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, mips_sp, cinfo->sig_cookie.offset, sig_arg->dreg);
1701 void
1702 mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
1704 MonoInst *in, *ins;
1705 MonoMethodSignature *sig;
1706 int i, n;
1707 CallInfo *cinfo;
1708 int is_virtual = 0;
1710 sig = call->signature;
1711 n = sig->param_count + sig->hasthis;
1713 cinfo = get_call_info (cfg->mempool, sig);
1714 if (cinfo->struct_ret)
1715 call->used_iregs |= 1 << cinfo->struct_ret;
1717 for (i = 0; i < n; ++i) {
1718 ArgInfo *ainfo = cinfo->args + i;
1719 MonoType *t;
1721 if (i >= sig->hasthis)
1722 t = sig->params [i - sig->hasthis];
1723 else
1724 t = &mono_defaults.int_class->byval_arg;
1725 t = mini_get_underlying_type (t);
1727 if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
1728 /* Emit the signature cookie just before the implicit arguments */
1729 emit_sig_cookie (cfg, call, cinfo);
1732 if (is_virtual && i == 0) {
1733 /* the argument will be attached to the call instrucion */
1734 in = call->args [i];
1735 call->used_iregs |= 1 << ainfo->reg;
1736 continue;
1738 in = call->args [i];
1739 if (ainfo->storage == ArgInIReg) {
1740 #if SIZEOF_REGISTER == 4
1741 if (!t->byref && ((t->type == MONO_TYPE_I8) || (t->type == MONO_TYPE_U8))) {
1742 MONO_INST_NEW (cfg, ins, OP_MOVE);
1743 ins->dreg = mono_alloc_ireg (cfg);
1744 ins->sreg1 = MONO_LVREG_LS (in->dreg);
1745 MONO_ADD_INS (cfg->cbb, ins);
1746 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg + ls_word_idx, FALSE);
1748 MONO_INST_NEW (cfg, ins, OP_MOVE);
1749 ins->dreg = mono_alloc_ireg (cfg);
1750 ins->sreg1 = MONO_LVREG_MS (in->dreg);
1751 MONO_ADD_INS (cfg->cbb, ins);
1752 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg + ms_word_idx, FALSE);
1753 } else
1754 #endif
1755 if (!t->byref && (t->type == MONO_TYPE_R4)) {
1756 int freg;
1758 #if PROMOTE_R4_TO_R8
1759 /* ??? - convert to single first? */
1760 MONO_INST_NEW (cfg, ins, OP_MIPS_CVTSD);
1761 ins->dreg = mono_alloc_freg (cfg);
1762 ins->sreg1 = in->dreg;
1763 MONO_ADD_INS (cfg->cbb, ins);
1764 freg = ins->dreg;
1765 #else
1766 freg = in->dreg;
1767 #endif
1768 /* trying to load float value into int registers */
1769 MONO_INST_NEW (cfg, ins, OP_MIPS_MFC1S);
1770 ins->dreg = mono_alloc_ireg (cfg);
1771 ins->sreg1 = freg;
1772 MONO_ADD_INS (cfg->cbb, ins);
1773 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1774 } else if (!t->byref && (t->type == MONO_TYPE_R8)) {
1775 /* trying to load float value into int registers */
1776 MONO_INST_NEW (cfg, ins, OP_MIPS_MFC1D);
1777 ins->dreg = mono_alloc_ireg (cfg);
1778 ins->sreg1 = in->dreg;
1779 MONO_ADD_INS (cfg->cbb, ins);
1780 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1781 } else {
1782 MONO_INST_NEW (cfg, ins, OP_MOVE);
1783 ins->dreg = mono_alloc_ireg (cfg);
1784 ins->sreg1 = in->dreg;
1785 MONO_ADD_INS (cfg->cbb, ins);
1786 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1788 } else if (ainfo->storage == ArgStructByAddr) {
1789 MONO_INST_NEW (cfg, ins, OP_OUTARG_VT);
1790 ins->opcode = OP_OUTARG_VT;
1791 ins->sreg1 = in->dreg;
1792 ins->klass = in->klass;
1793 ins->inst_p0 = call;
1794 ins->inst_p1 = mono_mempool_alloc (cfg->mempool, sizeof (ArgInfo));
1795 memcpy (ins->inst_p1, ainfo, sizeof (ArgInfo));
1796 MONO_ADD_INS (cfg->cbb, ins);
1797 } else if (ainfo->storage == ArgStructByVal) {
1798 /* this is further handled in mono_arch_emit_outarg_vt () */
1799 MONO_INST_NEW (cfg, ins, OP_OUTARG_VT);
1800 ins->opcode = OP_OUTARG_VT;
1801 ins->sreg1 = in->dreg;
1802 ins->klass = in->klass;
1803 ins->inst_p0 = call;
1804 ins->inst_p1 = mono_mempool_alloc (cfg->mempool, sizeof (ArgInfo));
1805 memcpy (ins->inst_p1, ainfo, sizeof (ArgInfo));
1806 MONO_ADD_INS (cfg->cbb, ins);
1807 } else if (ainfo->storage == ArgOnStack) {
1808 if (!t->byref && ((t->type == MONO_TYPE_I8) || (t->type == MONO_TYPE_U8))) {
1809 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, mips_sp, ainfo->offset, in->dreg);
1810 } else if (!t->byref && ((t->type == MONO_TYPE_R4) || (t->type == MONO_TYPE_R8))) {
1811 if (t->type == MONO_TYPE_R8)
1812 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, mips_sp, ainfo->offset, in->dreg);
1813 else
1814 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, mips_sp, ainfo->offset, in->dreg);
1815 } else {
1816 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, mips_sp, ainfo->offset, in->dreg);
1818 } else if (ainfo->storage == ArgInFReg) {
1819 if (t->type == MONO_TYPE_VALUETYPE) {
1820 /* this is further handled in mono_arch_emit_outarg_vt () */
1821 MONO_INST_NEW (cfg, ins, OP_OUTARG_VT);
1822 ins->opcode = OP_OUTARG_VT;
1823 ins->sreg1 = in->dreg;
1824 ins->klass = in->klass;
1825 ins->inst_p0 = call;
1826 ins->inst_p1 = mono_mempool_alloc (cfg->mempool, sizeof (ArgInfo));
1827 memcpy (ins->inst_p1, ainfo, sizeof (ArgInfo));
1828 MONO_ADD_INS (cfg->cbb, ins);
1830 cfg->flags |= MONO_CFG_HAS_FPOUT;
1831 } else {
1832 int dreg = mono_alloc_freg (cfg);
1834 if (ainfo->size == 4) {
1835 MONO_EMIT_NEW_UNALU (cfg, OP_MIPS_CVTSD, dreg, in->dreg);
1836 } else {
1837 MONO_INST_NEW (cfg, ins, OP_FMOVE);
1838 ins->dreg = dreg;
1839 ins->sreg1 = in->dreg;
1840 MONO_ADD_INS (cfg->cbb, ins);
1843 mono_call_inst_add_outarg_reg (cfg, call, dreg, ainfo->reg, TRUE);
1844 cfg->flags |= MONO_CFG_HAS_FPOUT;
1846 } else {
1847 g_assert_not_reached ();
1851 /* Handle the case where there are no implicit arguments */
1852 if (!sig->pinvoke && (sig->call_convention == MONO_CALL_VARARG) && (n == sig->sentinelpos))
1853 emit_sig_cookie (cfg, call, cinfo);
1855 if (cinfo->struct_ret) {
1856 MonoInst *vtarg;
1858 MONO_INST_NEW (cfg, vtarg, OP_MOVE);
1859 vtarg->sreg1 = call->vret_var->dreg;
1860 vtarg->dreg = mono_alloc_preg (cfg);
1861 MONO_ADD_INS (cfg->cbb, vtarg);
1863 mono_call_inst_add_outarg_reg (cfg, call, vtarg->dreg, cinfo->struct_ret, FALSE);
1865 #if 0
1867 * Reverse the call->out_args list.
1870 MonoInst *prev = NULL, *list = call->out_args, *next;
1871 while (list) {
1872 next = list->next;
1873 list->next = prev;
1874 prev = list;
1875 list = next;
1877 call->out_args = prev;
1879 #endif
1880 call->stack_usage = cinfo->stack_usage;
1881 cfg->param_area = MAX (cfg->param_area, cinfo->stack_usage);
1882 #if _MIPS_SIM == _ABIO32
1883 /* a0-a3 always present */
1884 cfg->param_area = MAX (cfg->param_area, 4 * SIZEOF_REGISTER);
1885 #endif
1886 cfg->param_area = (cfg->param_area + MIPS_STACK_ALIGNMENT - 1) & ~(MIPS_STACK_ALIGNMENT - 1);
1887 cfg->flags |= MONO_CFG_HAS_CALLS;
1889 * should set more info in call, such as the stack space
1890 * used by the args that needs to be added back to esp
1894 void
1895 mono_arch_emit_outarg_vt (MonoCompile *cfg, MonoInst *ins, MonoInst *src)
1897 MonoCallInst *call = (MonoCallInst*)ins->inst_p0;
1898 ArgInfo *ainfo = ins->inst_p1;
1899 int ovf_size = ainfo->vtsize;
1900 int doffset = ainfo->offset;
1901 int i, soffset, dreg;
1903 if (ainfo->storage == ArgStructByVal) {
1904 #if 0
1905 if (cfg->verbose_level > 0) {
1906 char* nm = mono_method_full_name (cfg->method, TRUE);
1907 g_print ("Method %s outarg_vt struct doffset=%d ainfo->size=%d ovf_size=%d\n",
1908 nm, doffset, ainfo->size, ovf_size);
1909 g_free (nm);
1911 #endif
1913 soffset = 0;
1914 for (i = 0; i < ainfo->size; ++i) {
1915 dreg = mono_alloc_ireg (cfg);
1916 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, src->dreg, soffset);
1917 mono_call_inst_add_outarg_reg (cfg, call, dreg, ainfo->reg + i, FALSE);
1918 soffset += SIZEOF_REGISTER;
1920 if (ovf_size != 0) {
1921 mini_emit_memcpy (cfg, mips_sp, doffset, src->dreg, soffset, ovf_size * sizeof (gpointer), 0);
1923 } else if (ainfo->storage == ArgInFReg) {
1924 int tmpr = mono_alloc_freg (cfg);
1926 if (ainfo->size == 4)
1927 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADR4_MEMBASE, tmpr, src->dreg, 0);
1928 else
1929 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADR8_MEMBASE, tmpr, src->dreg, 0);
1930 dreg = mono_alloc_freg (cfg);
1931 MONO_EMIT_NEW_UNALU (cfg, OP_FMOVE, dreg, tmpr);
1932 mono_call_inst_add_outarg_reg (cfg, call, dreg, ainfo->reg, TRUE);
1933 } else {
1934 MonoInst *vtcopy = mono_compile_create_var (cfg, &src->klass->byval_arg, OP_LOCAL);
1935 MonoInst *load;
1936 guint32 size;
1938 /* FIXME: alignment? */
1939 if (call->signature->pinvoke) {
1940 size = mono_type_native_stack_size (&src->klass->byval_arg, NULL);
1941 vtcopy->backend.is_pinvoke = 1;
1942 } else {
1943 size = mini_type_stack_size (&src->klass->byval_arg, NULL);
1945 if (size > 0)
1946 g_assert (ovf_size > 0);
1948 EMIT_NEW_VARLOADA (cfg, load, vtcopy, vtcopy->inst_vtype);
1949 mini_emit_memcpy (cfg, load->dreg, 0, src->dreg, 0, size, 0);
1951 if (ainfo->offset)
1952 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, mips_at, ainfo->offset, load->dreg);
1953 else
1954 mono_call_inst_add_outarg_reg (cfg, call, load->dreg, ainfo->reg, FALSE);
1958 void
1959 mono_arch_emit_setret (MonoCompile *cfg, MonoMethod *method, MonoInst *val)
1961 MonoType *ret = mini_get_underlying_type (mono_method_signature (method)->ret);
1963 if (!ret->byref) {
1964 #if (SIZEOF_REGISTER == 4)
1965 if (ret->type == MONO_TYPE_I8 || ret->type == MONO_TYPE_U8) {
1966 MonoInst *ins;
1968 MONO_INST_NEW (cfg, ins, OP_SETLRET);
1969 ins->sreg1 = MONO_LVREG_LS (val->dreg);
1970 ins->sreg2 = MONO_LVREG_MS (val->dreg);
1971 MONO_ADD_INS (cfg->cbb, ins);
1972 return;
1974 #endif
1975 if (ret->type == MONO_TYPE_R8) {
1976 MONO_EMIT_NEW_UNALU (cfg, OP_FMOVE, cfg->ret->dreg, val->dreg);
1977 return;
1979 if (ret->type == MONO_TYPE_R4) {
1980 MONO_EMIT_NEW_UNALU (cfg, OP_MIPS_CVTSD, cfg->ret->dreg, val->dreg);
1981 return;
1984 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->ret->dreg, val->dreg);
1987 void
1988 mono_arch_peephole_pass_1 (MonoCompile *cfg, MonoBasicBlock *bb)
1990 MonoInst *ins, *n, *last_ins = NULL;
1992 if (cfg->verbose_level > 2)
1993 g_print ("Basic block %d peephole pass 1\n", bb->block_num);
1995 ins = bb->code;
1996 MONO_BB_FOR_EACH_INS_SAFE (bb, n, ins) {
1997 if (cfg->verbose_level > 2)
1998 mono_print_ins_index (0, ins);
2000 switch (ins->opcode) {
2001 #if 0
2002 case OP_LOAD_MEMBASE:
2003 case OP_LOADI4_MEMBASE:
2005 * OP_IADD reg2, reg1, const1
2006 * OP_LOAD_MEMBASE const2(reg2), reg3
2007 * ->
2008 * OP_LOAD_MEMBASE (const1+const2)(reg1), reg3
2010 if (last_ins && (last_ins->opcode == OP_IADD_IMM || last_ins->opcode == OP_ADD_IMM) && (last_ins->dreg == ins->inst_basereg) && (last_ins->sreg1 != last_ins->dreg)){
2011 int const1 = last_ins->inst_imm;
2012 int const2 = ins->inst_offset;
2014 if (mips_is_imm16 (const1 + const2)) {
2015 ins->inst_basereg = last_ins->sreg1;
2016 ins->inst_offset = const1 + const2;
2019 break;
2020 #endif
2023 last_ins = ins;
2024 ins = ins->next;
2026 bb->last_ins = last_ins;
2029 void
2030 mono_arch_peephole_pass_2 (MonoCompile *cfg, MonoBasicBlock *bb)
2032 MonoInst *ins, *n, *last_ins = NULL;
2033 ins = bb->code;
2035 MONO_BB_FOR_EACH_INS_SAFE (bb, n, ins) {
2036 MonoInst *last_ins = ins->prev;
2038 switch (ins->opcode) {
2039 case OP_MUL_IMM:
2040 /* remove unnecessary multiplication with 1 */
2041 if (ins->inst_imm == 1) {
2042 if (ins->dreg != ins->sreg1) {
2043 ins->opcode = OP_MOVE;
2044 } else {
2045 MONO_DELETE_INS (bb, ins);
2046 continue;
2048 } else {
2049 int power2 = mono_is_power_of_two (ins->inst_imm);
2050 if (power2 > 0) {
2051 ins->opcode = OP_SHL_IMM;
2052 ins->inst_imm = power2;
2055 break;
2056 case OP_LOAD_MEMBASE:
2057 case OP_LOADI4_MEMBASE:
2059 * OP_STORE_MEMBASE_REG reg, offset(basereg)
2060 * OP_LOAD_MEMBASE offset(basereg), reg
2062 if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_REG
2063 || last_ins->opcode == OP_STORE_MEMBASE_REG) &&
2064 ins->inst_basereg == last_ins->inst_destbasereg &&
2065 ins->inst_offset == last_ins->inst_offset) {
2066 if (ins->dreg == last_ins->sreg1) {
2067 MONO_DELETE_INS (bb, ins);
2068 continue;
2069 } else {
2070 //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
2071 ins->opcode = OP_MOVE;
2072 ins->sreg1 = last_ins->sreg1;
2074 break;
2077 * Note: reg1 must be different from the basereg in the second load
2078 * OP_LOAD_MEMBASE offset(basereg), reg1
2079 * OP_LOAD_MEMBASE offset(basereg), reg2
2080 * -->
2081 * OP_LOAD_MEMBASE offset(basereg), reg1
2082 * OP_MOVE reg1, reg2
2084 if (last_ins && (last_ins->opcode == OP_LOADI4_MEMBASE
2085 || last_ins->opcode == OP_LOAD_MEMBASE) &&
2086 ins->inst_basereg != last_ins->dreg &&
2087 ins->inst_basereg == last_ins->inst_basereg &&
2088 ins->inst_offset == last_ins->inst_offset) {
2090 if (ins->dreg == last_ins->dreg) {
2091 MONO_DELETE_INS (bb, ins);
2092 continue;
2093 } else {
2094 ins->opcode = OP_MOVE;
2095 ins->sreg1 = last_ins->dreg;
2098 //g_assert_not_reached ();
2099 break;
2101 #if 0
2103 * OP_STORE_MEMBASE_IMM imm, offset(basereg)
2104 * OP_LOAD_MEMBASE offset(basereg), reg
2105 * -->
2106 * OP_STORE_MEMBASE_IMM imm, offset(basereg)
2107 * OP_ICONST reg, imm
2109 if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_IMM
2110 || last_ins->opcode == OP_STORE_MEMBASE_IMM) &&
2111 ins->inst_basereg == last_ins->inst_destbasereg &&
2112 ins->inst_offset == last_ins->inst_offset) {
2113 //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
2114 ins->opcode = OP_ICONST;
2115 ins->inst_c0 = last_ins->inst_imm;
2116 g_assert_not_reached (); // check this rule
2117 break;
2119 #endif
2120 break;
2121 case OP_LOADU1_MEMBASE:
2122 case OP_LOADI1_MEMBASE:
2123 if (last_ins && (last_ins->opcode == OP_STOREI1_MEMBASE_REG) &&
2124 ins->inst_basereg == last_ins->inst_destbasereg &&
2125 ins->inst_offset == last_ins->inst_offset) {
2126 ins->opcode = (ins->opcode == OP_LOADI1_MEMBASE) ? OP_ICONV_TO_I1 : OP_ICONV_TO_U1;
2127 ins->sreg1 = last_ins->sreg1;
2129 break;
2130 case OP_LOADU2_MEMBASE:
2131 case OP_LOADI2_MEMBASE:
2132 if (last_ins && (last_ins->opcode == OP_STOREI2_MEMBASE_REG) &&
2133 ins->inst_basereg == last_ins->inst_destbasereg &&
2134 ins->inst_offset == last_ins->inst_offset) {
2135 ins->opcode = (ins->opcode == OP_LOADI2_MEMBASE) ? OP_ICONV_TO_I2 : OP_ICONV_TO_U2;
2136 ins->sreg1 = last_ins->sreg1;
2138 break;
2139 case OP_ICONV_TO_I4:
2140 case OP_ICONV_TO_U4:
2141 case OP_MOVE:
2142 ins->opcode = OP_MOVE;
2144 * OP_MOVE reg, reg
2146 if (ins->dreg == ins->sreg1) {
2147 MONO_DELETE_INS (bb, ins);
2148 continue;
2151 * OP_MOVE sreg, dreg
2152 * OP_MOVE dreg, sreg
2154 if (last_ins && last_ins->opcode == OP_MOVE &&
2155 ins->sreg1 == last_ins->dreg &&
2156 ins->dreg == last_ins->sreg1) {
2157 MONO_DELETE_INS (bb, ins);
2158 continue;
2160 break;
2162 last_ins = ins;
2163 ins = ins->next;
2165 bb->last_ins = last_ins;
2168 void
2169 mono_arch_decompose_long_opts (MonoCompile *cfg, MonoInst *ins)
2171 int tmp1 = -1;
2172 int tmp2 = -1;
2173 int tmp3 = -1;
2174 int tmp4 = -1;
2175 int tmp5 = -1;
2177 switch (ins->opcode) {
2178 #if 0
2179 case OP_LCOMPARE:
2180 case OP_LCOMPARE_IMM:
2181 mono_print_ins (ins);
2182 g_assert_not_reached ();
2183 #endif
2184 case OP_LADD:
2185 tmp1 = mono_alloc_ireg (cfg);
2186 MONO_EMIT_NEW_BIALU (cfg, OP_IADD, ins->dreg+1, ins->sreg1+1, ins->sreg2+1);
2187 MONO_EMIT_NEW_BIALU (cfg, OP_MIPS_SLTU, tmp1, ins->dreg+1, ins->sreg1+1);
2188 MONO_EMIT_NEW_BIALU (cfg, OP_IADD, ins->dreg+2, ins->sreg1+2, ins->sreg2+2);
2189 MONO_EMIT_NEW_BIALU (cfg, OP_IADD, ins->dreg+2, ins->dreg+2, tmp1);
2190 NULLIFY_INS(ins);
2191 break;
2193 case OP_LADD_IMM:
2194 tmp1 = mono_alloc_ireg (cfg);
2195 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IADD_IMM, ins->dreg+1, ins->sreg1+1, ins->inst_ls_word);
2196 MONO_EMIT_NEW_BIALU (cfg, OP_MIPS_SLTU, tmp1, ins->dreg+1, ins->sreg1+1);
2197 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IADD_IMM, ins->dreg+2, ins->sreg1+2, ins->inst_ms_word);
2198 MONO_EMIT_NEW_BIALU (cfg, OP_IADD, ins->dreg+2, ins->dreg+2, tmp1);
2199 NULLIFY_INS(ins);
2200 break;
2202 case OP_LSUB:
2203 tmp1 = mono_alloc_ireg (cfg);
2204 MONO_EMIT_NEW_BIALU (cfg, OP_ISUB, ins->dreg+1, ins->sreg1+1, ins->sreg2+1);
2205 MONO_EMIT_NEW_BIALU (cfg, OP_MIPS_SLTU, tmp1, ins->sreg1+1, ins->dreg+1);
2206 MONO_EMIT_NEW_BIALU (cfg, OP_ISUB, ins->dreg+2, ins->sreg1+2, ins->sreg2+2);
2207 MONO_EMIT_NEW_BIALU (cfg, OP_ISUB, ins->dreg+2, ins->dreg+2, tmp1);
2208 NULLIFY_INS(ins);
2209 break;
2211 case OP_LSUB_IMM:
2212 tmp1 = mono_alloc_ireg (cfg);
2213 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISUB_IMM, ins->dreg+1, ins->sreg1+1, ins->inst_ls_word);
2214 MONO_EMIT_NEW_BIALU (cfg, OP_MIPS_SLTU, tmp1, ins->sreg1+1, ins->dreg+1);
2215 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISUB_IMM, ins->dreg+2, ins->sreg1+2, ins->inst_ms_word);
2216 MONO_EMIT_NEW_BIALU (cfg, OP_ISUB, ins->dreg+2, ins->dreg+2, tmp1);
2217 NULLIFY_INS(ins);
2218 break;
2220 case OP_LMUL:
2221 case OP_LDIV:
2222 case OP_LDIV_UN:
2223 case OP_LREM:
2224 case OP_LREM_UN:
2225 case OP_LSHL:
2226 case OP_LSHR:
2227 case OP_LSHR_UN:
2228 mono_print_ins (ins);
2229 g_assert_not_reached ();
2231 case OP_LNEG:
2232 tmp1 = mono_alloc_ireg (cfg);
2233 MONO_EMIT_NEW_BIALU (cfg, OP_ISUB, ins->dreg+1, mips_zero, ins->sreg1+1);
2234 MONO_EMIT_NEW_BIALU (cfg, OP_MIPS_SLTU, tmp1, mips_zero, ins->dreg+1);
2235 MONO_EMIT_NEW_BIALU (cfg, OP_ISUB, ins->dreg+2, mips_zero, ins->sreg1+2);
2236 MONO_EMIT_NEW_BIALU (cfg, OP_ISUB, ins->dreg+2, ins->dreg+2, tmp1);
2237 NULLIFY_INS(ins);
2238 break;
2240 #if 0
2241 case OP_LNOT:
2242 #endif
2243 #if 0
2244 case OP_LCONV_TO_I1:
2245 case OP_LCONV_TO_I2:
2246 case OP_LCONV_TO_I4:
2247 case OP_LCONV_TO_I8:
2248 case OP_LCONV_TO_R4:
2249 case OP_LCONV_TO_R8:
2250 case OP_LCONV_TO_U4:
2251 case OP_LCONV_TO_U8:
2252 case OP_LCONV_TO_U2:
2253 case OP_LCONV_TO_U1:
2254 case OP_LCONV_TO_I:
2255 case OP_LCONV_TO_OVF_I:
2256 case OP_LCONV_TO_OVF_U:
2257 #endif
2258 mono_print_ins (ins);
2259 g_assert_not_reached ();
2261 case OP_LADD_OVF:
2262 tmp1 = mono_alloc_ireg (cfg);
2263 tmp2 = mono_alloc_ireg (cfg);
2264 tmp3 = mono_alloc_ireg (cfg);
2265 tmp4 = mono_alloc_ireg (cfg);
2266 tmp5 = mono_alloc_ireg (cfg);
2268 MONO_EMIT_NEW_BIALU (cfg, OP_IADD, ins->dreg+1, ins->sreg1+1, ins->sreg2+1);
2270 /* tmp1 holds the carry from the low 32-bit to the high 32-bits */
2271 MONO_EMIT_NEW_BIALU (cfg, OP_MIPS_SLTU, tmp5, ins->dreg+1, ins->sreg1+1);
2273 /* add the high 32-bits, and add in the carry from the low 32-bits */
2274 MONO_EMIT_NEW_BIALU (cfg, OP_IADD, ins->dreg+2, ins->sreg1+2, ins->sreg2+2);
2275 MONO_EMIT_NEW_BIALU (cfg, OP_IADD, ins->dreg+2, tmp5, ins->dreg+2);
2277 /* Overflow happens if
2278 * neg + neg = pos or
2279 * pos + pos = neg
2280 * XOR of the high bits returns 0 if the signs match
2281 * XOR of that with the high bit of the result return 1 if overflow.
2284 /* tmp1 = 0 if the signs of the two inputs match, 1 otherwise */
2285 MONO_EMIT_NEW_BIALU (cfg, OP_IXOR, tmp1, ins->sreg1+2, ins->sreg2+2);
2287 /* set tmp2 = 0 if bit31 of results matches is different than the operands */
2288 MONO_EMIT_NEW_BIALU (cfg, OP_IXOR, tmp2, ins->dreg+2, ins->sreg2+2);
2289 MONO_EMIT_NEW_UNALU (cfg, OP_INOT, tmp2, tmp2);
2291 /* OR(tmp1, tmp2) = 0 if both conditions are true */
2292 MONO_EMIT_NEW_BIALU (cfg, OP_IOR, tmp3, tmp2, tmp1);
2293 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHR_IMM, tmp4, tmp3, 31);
2295 /* Now, if (tmp4 == 0) then overflow */
2296 MONO_EMIT_NEW_COMPARE_EXC (cfg, EQ, tmp4, mips_zero, "OverflowException");
2297 NULLIFY_INS(ins);
2298 break;
2300 case OP_LADD_OVF_UN:
2301 tmp1 = mono_alloc_ireg (cfg);
2302 tmp2 = mono_alloc_ireg (cfg);
2304 MONO_EMIT_NEW_BIALU (cfg, OP_IADD, ins->dreg+1, ins->sreg1+1, ins->sreg2+1);
2305 MONO_EMIT_NEW_BIALU (cfg, OP_MIPS_SLTU, tmp1, ins->dreg+1, ins->sreg1+1);
2306 MONO_EMIT_NEW_BIALU (cfg, OP_IADD, ins->dreg+2, ins->sreg1+2, ins->sreg2+2);
2307 MONO_EMIT_NEW_BIALU (cfg, OP_IADD, ins->dreg+2, tmp1, ins->dreg+2);
2308 MONO_EMIT_NEW_BIALU (cfg, OP_MIPS_SLTU, tmp2, ins->dreg+2, ins->sreg1+2);
2309 MONO_EMIT_NEW_COMPARE_EXC (cfg, NE_UN, tmp2, mips_zero, "OverflowException");
2310 NULLIFY_INS(ins);
2311 break;
2313 case OP_LMUL_OVF:
2314 case OP_LMUL_OVF_UN:
2315 mono_print_ins (ins);
2316 g_assert_not_reached ();
2318 case OP_LSUB_OVF:
2319 tmp1 = mono_alloc_ireg (cfg);
2320 tmp2 = mono_alloc_ireg (cfg);
2321 tmp3 = mono_alloc_ireg (cfg);
2322 tmp4 = mono_alloc_ireg (cfg);
2323 tmp5 = mono_alloc_ireg (cfg);
2325 MONO_EMIT_NEW_BIALU (cfg, OP_ISUB, ins->dreg+1, ins->sreg1+1, ins->sreg2+1);
2327 MONO_EMIT_NEW_BIALU (cfg, OP_MIPS_SLTU, tmp5, ins->sreg1+1, ins->dreg+1);
2328 MONO_EMIT_NEW_BIALU (cfg, OP_ISUB, ins->dreg+2, ins->sreg1+2, ins->sreg2+2);
2329 MONO_EMIT_NEW_BIALU (cfg, OP_ISUB, ins->dreg+2, ins->dreg+2, tmp5);
2331 /* Overflow happens if
2332 * neg - pos = pos or
2333 * pos - neg = neg
2334 * XOR of bit31 of the lhs & rhs = 1 if the signs are different
2336 * tmp1 = (lhs ^ rhs)
2337 * tmp2 = (lhs ^ result)
2338 * if ((tmp1 < 0) & (tmp2 < 0)) then overflow
2341 MONO_EMIT_NEW_BIALU (cfg, OP_IXOR, tmp1, ins->sreg1+2, ins->sreg2+2);
2342 MONO_EMIT_NEW_BIALU (cfg, OP_IXOR, tmp2, ins->sreg1+2, ins->dreg+2);
2343 MONO_EMIT_NEW_BIALU (cfg, OP_IAND, tmp3, tmp2, tmp1);
2344 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHR_IMM, tmp4, tmp3, 31);
2346 /* Now, if (tmp4 == 1) then overflow */
2347 MONO_EMIT_NEW_COMPARE_EXC (cfg, NE_UN, tmp4, mips_zero, "OverflowException");
2348 NULLIFY_INS(ins);
2349 break;
2351 case OP_LSUB_OVF_UN:
2352 tmp1 = mono_alloc_ireg (cfg);
2353 tmp2 = mono_alloc_ireg (cfg);
2355 MONO_EMIT_NEW_BIALU (cfg, OP_ISUB, ins->dreg+1, ins->sreg1+1, ins->sreg2+1);
2356 MONO_EMIT_NEW_BIALU (cfg, OP_MIPS_SLTU, tmp1, ins->sreg1+1, ins->dreg+1);
2357 MONO_EMIT_NEW_BIALU (cfg, OP_ISUB, ins->dreg+2, ins->sreg1+2, ins->sreg2+2);
2358 MONO_EMIT_NEW_BIALU (cfg, OP_ISUB, ins->dreg+2, ins->dreg+2, tmp1);
2360 MONO_EMIT_NEW_BIALU (cfg, OP_MIPS_SLTU, tmp2, ins->sreg1+2, ins->dreg+2);
2361 MONO_EMIT_NEW_COMPARE_EXC (cfg, NE_UN, tmp2, mips_zero, "OverflowException");
2362 NULLIFY_INS(ins);
2363 break;
2364 #if 0
2365 case OP_LCONV_TO_OVF_I1_UN:
2366 case OP_LCONV_TO_OVF_I2_UN:
2367 case OP_LCONV_TO_OVF_I4_UN:
2368 case OP_LCONV_TO_OVF_I8_UN:
2369 case OP_LCONV_TO_OVF_U1_UN:
2370 case OP_LCONV_TO_OVF_U2_UN:
2371 case OP_LCONV_TO_OVF_U4_UN:
2372 case OP_LCONV_TO_OVF_U8_UN:
2373 case OP_LCONV_TO_OVF_I_UN:
2374 case OP_LCONV_TO_OVF_U_UN:
2375 case OP_LCONV_TO_OVF_I1:
2376 case OP_LCONV_TO_OVF_U1:
2377 case OP_LCONV_TO_OVF_I2:
2378 case OP_LCONV_TO_OVF_U2:
2379 case OP_LCONV_TO_OVF_I4:
2380 case OP_LCONV_TO_OVF_U4:
2381 case OP_LCONV_TO_OVF_I8:
2382 case OP_LCONV_TO_OVF_U8:
2383 #endif
2384 case OP_LCEQ:
2385 case OP_LCGT:
2386 case OP_LCGT_UN:
2387 case OP_LCLT:
2388 case OP_LCLT_UN:
2389 #if 0
2390 case OP_LCONV_TO_R_UN:
2391 case OP_LCONV_TO_U:
2392 #endif
2393 case OP_LMUL_IMM:
2394 case OP_LSHL_IMM:
2395 case OP_LSHR_IMM:
2396 case OP_LSHR_UN_IMM:
2397 case OP_LDIV_IMM:
2398 case OP_LDIV_UN_IMM:
2399 case OP_LREM_IMM:
2400 case OP_LREM_UN_IMM:
2401 case OP_LBEQ:
2402 case OP_LBGE:
2403 case OP_LBGT:
2404 case OP_LBLE:
2405 case OP_LBLT:
2406 case OP_LBNE_UN:
2407 case OP_LBGE_UN:
2408 case OP_LBGT_UN:
2409 case OP_LBLE_UN:
2410 case OP_LBLT_UN:
2411 mono_print_ins (ins);
2412 g_assert_not_reached ();
2413 #if 0
2414 case OP_LCONV_TO_R8_2:
2415 case OP_LCONV_TO_R4_2:
2416 case OP_LCONV_TO_R_UN_2:
2417 #endif
2418 case OP_LCONV_TO_OVF_I4_2:
2419 tmp1 = mono_alloc_ireg (cfg);
2421 /* Overflows if reg2 != sign extension of reg1 */
2422 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHR_IMM, tmp1, ins->sreg1, 31);
2423 MONO_EMIT_NEW_COMPARE_EXC (cfg, NE_UN, ins->sreg2, tmp1, "OverflowException");
2424 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, ins->dreg, ins->sreg1);
2425 NULLIFY_INS(ins);
2426 break;
2428 case OP_LMIN_UN:
2429 case OP_LMAX_UN:
2430 case OP_LMIN:
2431 case OP_LMAX:
2432 mono_print_ins (ins);
2433 g_assert_not_reached ();
2435 default:
2436 break;
2440 void
2441 mono_arch_decompose_opts (MonoCompile *cfg, MonoInst *ins)
2443 int tmp1 = -1;
2444 int tmp2 = -1;
2445 int tmp3 = -1;
2446 int tmp4 = -1;
2447 int tmp5 = -1;
2449 switch (ins->opcode) {
2450 case OP_IADD_OVF:
2451 tmp1 = mono_alloc_ireg (cfg);
2452 tmp2 = mono_alloc_ireg (cfg);
2453 tmp3 = mono_alloc_ireg (cfg);
2454 tmp4 = mono_alloc_ireg (cfg);
2455 tmp5 = mono_alloc_ireg (cfg);
2457 /* add the operands */
2459 MONO_EMIT_NEW_BIALU (cfg, OP_IADD, ins->dreg, ins->sreg1, ins->sreg2);
2461 /* Overflow happens if
2462 * neg + neg = pos or
2463 * pos + pos = neg
2465 * (bit31s of operands match) AND (bit31 of operand != bit31 of result)
2466 * XOR of the high bit returns 0 if the signs match
2467 * XOR of that with the high bit of the result return 1 if overflow.
2470 /* tmp1 = 0 if the signs of the two inputs match, 1 otherwise */
2471 MONO_EMIT_NEW_BIALU (cfg, OP_IXOR, tmp1, ins->sreg1, ins->sreg2);
2473 /* set tmp2 = 0 if bit31 of results matches is different than the operands */
2474 MONO_EMIT_NEW_BIALU (cfg, OP_IXOR, tmp2, ins->dreg, ins->sreg2);
2475 MONO_EMIT_NEW_UNALU (cfg, OP_INOT, tmp3, tmp2);
2477 /* OR(tmp1, tmp2) = 0 if both conditions are true */
2478 MONO_EMIT_NEW_BIALU (cfg, OP_IOR, tmp4, tmp3, tmp1);
2480 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHR_IMM, tmp5, tmp4, 31);
2482 /* Now, if (tmp5 == 0) then overflow */
2483 MONO_EMIT_NEW_COMPARE_EXC (cfg, EQ, tmp5, mips_zero, "OverflowException");
2484 /* Make decompse and method-to-ir.c happy, last insn writes dreg */
2485 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, ins->dreg, ins->dreg);
2486 NULLIFY_INS(ins);
2487 break;
2489 case OP_IADD_OVF_UN:
2490 tmp1 = mono_alloc_ireg (cfg);
2492 MONO_EMIT_NEW_BIALU (cfg, OP_IADD, ins->dreg, ins->sreg1, ins->sreg2);
2493 MONO_EMIT_NEW_BIALU (cfg, OP_MIPS_SLTU, tmp1, ins->dreg, ins->sreg1);
2494 MONO_EMIT_NEW_COMPARE_EXC (cfg, NE_UN, tmp1, mips_zero, "OverflowException");
2495 /* Make decompse and method-to-ir.c happy, last insn writes dreg */
2496 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, ins->dreg, ins->dreg);
2497 NULLIFY_INS(ins);
2498 break;
2500 case OP_ISUB_OVF:
2501 tmp1 = mono_alloc_ireg (cfg);
2502 tmp2 = mono_alloc_ireg (cfg);
2503 tmp3 = mono_alloc_ireg (cfg);
2504 tmp4 = mono_alloc_ireg (cfg);
2505 tmp5 = mono_alloc_ireg (cfg);
2507 /* add the operands */
2509 MONO_EMIT_NEW_BIALU (cfg, OP_ISUB, ins->dreg, ins->sreg1, ins->sreg2);
2511 /* Overflow happens if
2512 * neg - pos = pos or
2513 * pos - neg = neg
2514 * XOR of bit31 of the lhs & rhs = 1 if the signs are different
2516 * tmp1 = (lhs ^ rhs)
2517 * tmp2 = (lhs ^ result)
2518 * if ((tmp1 < 0) & (tmp2 < 0)) then overflow
2521 /* tmp3 = 1 if the signs of the two inputs differ */
2522 MONO_EMIT_NEW_BIALU (cfg, OP_IXOR, tmp1, ins->sreg1, ins->sreg2);
2523 MONO_EMIT_NEW_BIALU (cfg, OP_IXOR, tmp2, ins->sreg1, ins->dreg);
2524 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_MIPS_SLTI, tmp3, tmp1, 0);
2525 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_MIPS_SLTI, tmp4, tmp2, 0);
2526 MONO_EMIT_NEW_BIALU (cfg, OP_IAND, tmp5, tmp4, tmp3);
2528 MONO_EMIT_NEW_COMPARE_EXC (cfg, NE_UN, tmp5, mips_zero, "OverflowException");
2529 /* Make decompse and method-to-ir.c happy, last insn writes dreg */
2530 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, ins->dreg, ins->dreg);
2531 NULLIFY_INS(ins);
2532 break;
2534 case OP_ISUB_OVF_UN:
2535 tmp1 = mono_alloc_ireg (cfg);
2537 MONO_EMIT_NEW_BIALU (cfg, OP_ISUB, ins->dreg, ins->sreg1, ins->sreg2);
2538 MONO_EMIT_NEW_BIALU (cfg, OP_MIPS_SLTU, tmp1, ins->sreg1, ins->dreg);
2539 MONO_EMIT_NEW_COMPARE_EXC (cfg, NE_UN, tmp1, mips_zero, "OverflowException");
2540 /* Make decompse and method-to-ir.c happy, last insn writes dreg */
2541 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, ins->dreg, ins->dreg);
2542 NULLIFY_INS(ins);
2543 break;
2547 static int
2548 map_to_reg_reg_op (int op)
2550 switch (op) {
2551 case OP_ADD_IMM:
2552 return OP_IADD;
2553 case OP_SUB_IMM:
2554 return OP_ISUB;
2555 case OP_AND_IMM:
2556 return OP_IAND;
2557 case OP_COMPARE_IMM:
2558 return OP_COMPARE;
2559 case OP_ICOMPARE_IMM:
2560 return OP_ICOMPARE;
2561 case OP_LCOMPARE_IMM:
2562 return OP_LCOMPARE;
2563 case OP_ADDCC_IMM:
2564 return OP_IADDCC;
2565 case OP_ADC_IMM:
2566 return OP_IADC;
2567 case OP_SUBCC_IMM:
2568 return OP_ISUBCC;
2569 case OP_SBB_IMM:
2570 return OP_ISBB;
2571 case OP_OR_IMM:
2572 return OP_IOR;
2573 case OP_XOR_IMM:
2574 return OP_IXOR;
2575 case OP_MUL_IMM:
2576 return OP_IMUL;
2577 case OP_LOAD_MEMBASE:
2578 return OP_LOAD_MEMINDEX;
2579 case OP_LOADI4_MEMBASE:
2580 return OP_LOADI4_MEMINDEX;
2581 case OP_LOADU4_MEMBASE:
2582 return OP_LOADU4_MEMINDEX;
2583 case OP_LOADU1_MEMBASE:
2584 return OP_LOADU1_MEMINDEX;
2585 case OP_LOADI2_MEMBASE:
2586 return OP_LOADI2_MEMINDEX;
2587 case OP_LOADU2_MEMBASE:
2588 return OP_LOADU2_MEMINDEX;
2589 case OP_LOADI1_MEMBASE:
2590 return OP_LOADI1_MEMINDEX;
2591 case OP_LOADR4_MEMBASE:
2592 return OP_LOADR4_MEMINDEX;
2593 case OP_LOADR8_MEMBASE:
2594 return OP_LOADR8_MEMINDEX;
2595 case OP_STOREI1_MEMBASE_REG:
2596 return OP_STOREI1_MEMINDEX;
2597 case OP_STOREI2_MEMBASE_REG:
2598 return OP_STOREI2_MEMINDEX;
2599 case OP_STOREI4_MEMBASE_REG:
2600 return OP_STOREI4_MEMINDEX;
2601 case OP_STORE_MEMBASE_REG:
2602 return OP_STORE_MEMINDEX;
2603 case OP_STORER4_MEMBASE_REG:
2604 return OP_STORER4_MEMINDEX;
2605 case OP_STORER8_MEMBASE_REG:
2606 return OP_STORER8_MEMINDEX;
2607 case OP_STORE_MEMBASE_IMM:
2608 return OP_STORE_MEMBASE_REG;
2609 case OP_STOREI1_MEMBASE_IMM:
2610 return OP_STOREI1_MEMBASE_REG;
2611 case OP_STOREI2_MEMBASE_IMM:
2612 return OP_STOREI2_MEMBASE_REG;
2613 case OP_STOREI4_MEMBASE_IMM:
2614 return OP_STOREI4_MEMBASE_REG;
2615 case OP_STOREI8_MEMBASE_IMM:
2616 return OP_STOREI8_MEMBASE_REG;
2618 if (mono_op_imm_to_op (op) == -1)
2619 g_error ("mono_op_imm_to_op failed for %s\n", mono_inst_name (op));
2620 return mono_op_imm_to_op (op);
2623 static int
2624 map_to_mips_op (int op)
2626 switch (op) {
2627 case OP_FBEQ:
2628 return OP_MIPS_FBEQ;
2629 case OP_FBGE:
2630 return OP_MIPS_FBGE;
2631 case OP_FBGT:
2632 return OP_MIPS_FBGT;
2633 case OP_FBLE:
2634 return OP_MIPS_FBLE;
2635 case OP_FBLT:
2636 return OP_MIPS_FBLT;
2637 case OP_FBNE_UN:
2638 return OP_MIPS_FBNE;
2639 case OP_FBGE_UN:
2640 return OP_MIPS_FBGE_UN;
2641 case OP_FBGT_UN:
2642 return OP_MIPS_FBGT_UN;
2643 case OP_FBLE_UN:
2644 return OP_MIPS_FBLE_UN;
2645 case OP_FBLT_UN:
2646 return OP_MIPS_FBLT_UN;
2648 case OP_FCEQ:
2649 case OP_FCGT:
2650 case OP_FCGT_UN:
2651 case OP_FCLT:
2652 case OP_FCLT_UN:
2653 default:
2654 g_warning ("unknown opcode %s in %s()\n", mono_inst_name (op), __FUNCTION__);
2655 g_assert_not_reached ();
2659 #define NEW_INS(cfg,after,dest,op) do { \
2660 MONO_INST_NEW((cfg), (dest), (op)); \
2661 mono_bblock_insert_after_ins (bb, (after), (dest)); \
2662 } while (0)
2664 #define INS(pos,op,_dreg,_sreg1,_sreg2) do { \
2665 MonoInst *temp; \
2666 MONO_INST_NEW(cfg, temp, (op)); \
2667 mono_bblock_insert_after_ins (bb, (pos), temp); \
2668 temp->dreg = (_dreg); \
2669 temp->sreg1 = (_sreg1); \
2670 temp->sreg2 = (_sreg2); \
2671 pos = temp; \
2672 } while (0)
2674 #define INS_IMM(pos,op,_dreg,_sreg1,_imm) do { \
2675 MonoInst *temp; \
2676 MONO_INST_NEW(cfg, temp, (op)); \
2677 mono_bblock_insert_after_ins (bb, (pos), temp); \
2678 temp->dreg = (_dreg); \
2679 temp->sreg1 = (_sreg1); \
2680 temp->inst_c0 = (_imm); \
2681 pos = temp; \
2682 } while (0)
2685 * Remove from the instruction list the instructions that can't be
2686 * represented with very simple instructions with no register
2687 * requirements.
2689 void
2690 mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb)
2692 MonoInst *ins, *next, *temp, *last_ins = NULL;
2693 int imm;
2695 #if 1
2696 if (cfg->verbose_level > 2) {
2697 int idx = 0;
2699 g_print ("BASIC BLOCK %d (before lowering)\n", bb->block_num);
2700 MONO_BB_FOR_EACH_INS (bb, ins) {
2701 mono_print_ins_index (idx++, ins);
2705 #endif
2707 MONO_BB_FOR_EACH_INS (bb, ins) {
2708 loop_start:
2709 switch (ins->opcode) {
2710 case OP_COMPARE:
2711 case OP_ICOMPARE:
2712 case OP_LCOMPARE:
2713 next = ins->next;
2714 /* Branch opts can eliminate the branch */
2715 if (!next || (!(MONO_IS_COND_BRANCH_OP (next) || MONO_IS_COND_EXC (next) || MONO_IS_SETCC (next)))) {
2716 NULLIFY_INS(ins);
2717 break;
2719 break;
2721 case OP_COMPARE_IMM:
2722 case OP_ICOMPARE_IMM:
2723 case OP_LCOMPARE_IMM:
2724 next = ins->next;
2725 /* Branch opts can eliminate the branch */
2726 if (!next || (!(MONO_IS_COND_BRANCH_OP (next) || MONO_IS_COND_EXC (next) || MONO_IS_SETCC (next)))) {
2727 NULLIFY_INS(ins);
2728 break;
2730 if (ins->inst_imm) {
2731 NEW_INS (cfg, last_ins, temp, OP_ICONST);
2732 temp->inst_c0 = ins->inst_imm;
2733 temp->dreg = mono_alloc_ireg (cfg);
2734 ins->sreg2 = temp->dreg;
2735 last_ins = temp;
2737 else {
2738 ins->sreg2 = mips_zero;
2740 if (ins->opcode == OP_COMPARE_IMM)
2741 ins->opcode = OP_COMPARE;
2742 else if (ins->opcode == OP_ICOMPARE_IMM)
2743 ins->opcode = OP_ICOMPARE;
2744 else if (ins->opcode == OP_LCOMPARE_IMM)
2745 ins->opcode = OP_LCOMPARE;
2746 goto loop_start;
2748 case OP_IDIV_UN_IMM:
2749 case OP_IDIV_IMM:
2750 case OP_IREM_IMM:
2751 case OP_IREM_UN_IMM:
2752 NEW_INS (cfg, last_ins, temp, OP_ICONST);
2753 temp->inst_c0 = ins->inst_imm;
2754 temp->dreg = mono_alloc_ireg (cfg);
2755 ins->sreg2 = temp->dreg;
2756 if (ins->opcode == OP_IDIV_IMM)
2757 ins->opcode = OP_IDIV;
2758 else if (ins->opcode == OP_IREM_IMM)
2759 ins->opcode = OP_IREM;
2760 else if (ins->opcode == OP_IDIV_UN_IMM)
2761 ins->opcode = OP_IDIV_UN;
2762 else if (ins->opcode == OP_IREM_UN_IMM)
2763 ins->opcode = OP_IREM_UN;
2764 last_ins = temp;
2765 /* handle rem separately */
2766 goto loop_start;
2768 #if 0
2769 case OP_AND_IMM:
2770 case OP_OR_IMM:
2771 case OP_XOR_IMM:
2772 if ((ins->inst_imm & 0xffff0000) && (ins->inst_imm & 0xffff)) {
2773 NEW_INS (cfg, last_ins, temp, OP_ICONST);
2774 temp->inst_c0 = ins->inst_imm;
2775 temp->dreg = mono_alloc_ireg (cfg);
2776 ins->sreg2 = temp->dreg;
2777 ins->opcode = map_to_reg_reg_op (ins->opcode);
2779 break;
2780 #endif
2781 case OP_AND_IMM:
2782 case OP_IAND_IMM:
2783 case OP_OR_IMM:
2784 case OP_IOR_IMM:
2785 case OP_XOR_IMM:
2786 case OP_IXOR_IMM:
2787 /* unsigned 16 bit immediate */
2788 if (ins->inst_imm & 0xffff0000) {
2789 NEW_INS (cfg, last_ins, temp, OP_ICONST);
2790 temp->inst_c0 = ins->inst_imm;
2791 temp->dreg = mono_alloc_ireg (cfg);
2792 ins->sreg2 = temp->dreg;
2793 ins->opcode = map_to_reg_reg_op (ins->opcode);
2795 break;
2797 case OP_IADD_IMM:
2798 case OP_ADD_IMM:
2799 case OP_ADDCC_IMM:
2800 /* signed 16 bit immediate */
2801 if (!mips_is_imm16 (ins->inst_imm)) {
2802 NEW_INS (cfg, last_ins, temp, OP_ICONST);
2803 temp->inst_c0 = ins->inst_imm;
2804 temp->dreg = mono_alloc_ireg (cfg);
2805 ins->sreg2 = temp->dreg;
2806 ins->opcode = map_to_reg_reg_op (ins->opcode);
2808 break;
2810 case OP_SUB_IMM:
2811 case OP_ISUB_IMM:
2812 if (!mips_is_imm16 (-ins->inst_imm)) {
2813 NEW_INS (cfg, last_ins, temp, OP_ICONST);
2814 temp->inst_c0 = ins->inst_imm;
2815 temp->dreg = mono_alloc_ireg (cfg);
2816 ins->sreg2 = temp->dreg;
2817 ins->opcode = map_to_reg_reg_op (ins->opcode);
2819 break;
2821 case OP_MUL_IMM:
2822 case OP_IMUL_IMM:
2823 if (ins->inst_imm == 1) {
2824 ins->opcode = OP_MOVE;
2825 break;
2827 if (ins->inst_imm == 0) {
2828 ins->opcode = OP_ICONST;
2829 ins->inst_c0 = 0;
2830 break;
2832 imm = mono_is_power_of_two (ins->inst_imm);
2833 if (imm > 0) {
2834 ins->opcode = OP_SHL_IMM;
2835 ins->inst_imm = imm;
2836 break;
2838 NEW_INS (cfg, last_ins, temp, OP_ICONST);
2839 temp->inst_c0 = ins->inst_imm;
2840 temp->dreg = mono_alloc_ireg (cfg);
2841 ins->sreg2 = temp->dreg;
2842 ins->opcode = map_to_reg_reg_op (ins->opcode);
2843 break;
2845 case OP_LOCALLOC_IMM:
2846 NEW_INS (cfg, last_ins, temp, OP_ICONST);
2847 temp->inst_c0 = ins->inst_imm;
2848 temp->dreg = mono_alloc_ireg (cfg);
2849 ins->sreg1 = temp->dreg;
2850 ins->opcode = OP_LOCALLOC;
2851 break;
2853 case OP_LOADR4_MEMBASE:
2854 case OP_STORER4_MEMBASE_REG:
2855 /* we can do two things: load the immed in a register
2856 * and use an indexed load, or see if the immed can be
2857 * represented as an ad_imm + a load with a smaller offset
2858 * that fits. We just do the first for now, optimize later.
2860 if (mips_is_imm16 (ins->inst_offset))
2861 break;
2862 NEW_INS (cfg, last_ins, temp, OP_ICONST);
2863 temp->inst_c0 = ins->inst_offset;
2864 temp->dreg = mono_alloc_ireg (cfg);
2865 ins->sreg2 = temp->dreg;
2866 ins->opcode = map_to_reg_reg_op (ins->opcode);
2867 break;
2869 case OP_STORE_MEMBASE_IMM:
2870 case OP_STOREI1_MEMBASE_IMM:
2871 case OP_STOREI2_MEMBASE_IMM:
2872 case OP_STOREI4_MEMBASE_IMM:
2873 case OP_STOREI8_MEMBASE_IMM:
2874 if (!ins->inst_imm) {
2875 ins->sreg1 = mips_zero;
2876 ins->opcode = map_to_reg_reg_op (ins->opcode);
2878 else {
2879 NEW_INS (cfg, last_ins, temp, OP_ICONST);
2880 temp->inst_c0 = ins->inst_imm;
2881 temp->dreg = mono_alloc_ireg (cfg);
2882 ins->sreg1 = temp->dreg;
2883 ins->opcode = map_to_reg_reg_op (ins->opcode);
2884 last_ins = temp;
2885 goto loop_start; /* make it handle the possibly big ins->inst_offset */
2887 break;
2889 case OP_FCOMPARE:
2890 next = ins->next;
2891 /* Branch opts can eliminate the branch */
2892 if (!next || (!(MONO_IS_COND_BRANCH_OP (next) || MONO_IS_COND_EXC (next) || MONO_IS_SETCC (next)))) {
2893 NULLIFY_INS(ins);
2894 break;
2896 g_assert(next);
2899 * remap compare/branch and compare/set
2900 * to MIPS specific opcodes.
2902 next->opcode = map_to_mips_op (next->opcode);
2903 next->sreg1 = ins->sreg1;
2904 next->sreg2 = ins->sreg2;
2905 NULLIFY_INS(ins);
2906 break;
2908 #if 0
2909 case OP_R8CONST:
2910 case OP_R4CONST:
2911 NEW_INS (cfg, last_ins, temp, OP_ICONST);
2912 temp->inst_c0 = (guint32)ins->inst_p0;
2913 temp->dreg = mono_alloc_ireg (cfg);
2914 ins->inst_basereg = temp->dreg;
2915 ins->inst_offset = 0;
2916 ins->opcode = ins->opcode == OP_R4CONST? OP_LOADR4_MEMBASE: OP_LOADR8_MEMBASE;
2917 last_ins = temp;
2918 /* make it handle the possibly big ins->inst_offset
2919 * later optimize to use lis + load_membase
2921 goto loop_start;
2922 #endif
2923 case OP_IBEQ:
2924 g_assert (ins_is_compare(last_ins));
2925 INS_REWRITE(ins, OP_MIPS_BEQ, last_ins->sreg1, last_ins->sreg2);
2926 NULLIFY_INS(last_ins);
2927 break;
2929 case OP_IBNE_UN:
2930 g_assert (ins_is_compare(last_ins));
2931 INS_REWRITE(ins, OP_MIPS_BNE, last_ins->sreg1, last_ins->sreg2);
2932 NULLIFY_INS(last_ins);
2933 break;
2935 case OP_IBGE:
2936 g_assert (ins_is_compare(last_ins));
2937 INS_REWRITE(last_ins, OP_MIPS_SLT, last_ins->sreg1, last_ins->sreg2);
2938 last_ins->dreg = mono_alloc_ireg (cfg);
2939 INS_REWRITE(ins, OP_MIPS_BEQ, last_ins->dreg, mips_zero);
2940 break;
2942 case OP_IBGE_UN:
2943 g_assert (ins_is_compare(last_ins));
2944 INS_REWRITE(last_ins, OP_MIPS_SLTU, last_ins->sreg1, last_ins->sreg2);
2945 last_ins->dreg = mono_alloc_ireg (cfg);
2946 INS_REWRITE(ins, OP_MIPS_BEQ, last_ins->dreg, mips_zero);
2947 break;
2949 case OP_IBLT:
2950 g_assert (ins_is_compare(last_ins));
2951 INS_REWRITE(last_ins, OP_MIPS_SLT, last_ins->sreg1, last_ins->sreg2);
2952 last_ins->dreg = mono_alloc_ireg (cfg);
2953 INS_REWRITE(ins, OP_MIPS_BNE, last_ins->dreg, mips_zero);
2954 break;
2956 case OP_IBLT_UN:
2957 g_assert (ins_is_compare(last_ins));
2958 INS_REWRITE(last_ins, OP_MIPS_SLTU, last_ins->sreg1, last_ins->sreg2);
2959 last_ins->dreg = mono_alloc_ireg (cfg);
2960 INS_REWRITE(ins, OP_MIPS_BNE, last_ins->dreg, mips_zero);
2961 break;
2963 case OP_IBLE:
2964 g_assert (ins_is_compare(last_ins));
2965 INS_REWRITE(last_ins, OP_MIPS_SLT, last_ins->sreg2, last_ins->sreg1);
2966 last_ins->dreg = mono_alloc_ireg (cfg);
2967 INS_REWRITE(ins, OP_MIPS_BEQ, last_ins->dreg, mips_zero);
2968 break;
2970 case OP_IBLE_UN:
2971 g_assert (ins_is_compare(last_ins));
2972 INS_REWRITE(last_ins, OP_MIPS_SLTU, last_ins->sreg2, last_ins->sreg1);
2973 last_ins->dreg = mono_alloc_ireg (cfg);
2974 INS_REWRITE(ins, OP_MIPS_BEQ, last_ins->dreg, mips_zero);
2975 break;
2977 case OP_IBGT:
2978 g_assert (ins_is_compare(last_ins));
2979 INS_REWRITE(last_ins, OP_MIPS_SLT, last_ins->sreg2, last_ins->sreg1);
2980 last_ins->dreg = mono_alloc_ireg (cfg);
2981 INS_REWRITE(ins, OP_MIPS_BNE, last_ins->dreg, mips_zero);
2982 break;
2984 case OP_IBGT_UN:
2985 g_assert (ins_is_compare(last_ins));
2986 INS_REWRITE(last_ins, OP_MIPS_SLTU, last_ins->sreg2, last_ins->sreg1);
2987 last_ins->dreg = mono_alloc_ireg (cfg);
2988 INS_REWRITE(ins, OP_MIPS_BNE, last_ins->dreg, mips_zero);
2989 break;
2991 case OP_CEQ:
2992 case OP_ICEQ:
2993 g_assert (ins_is_compare(last_ins));
2994 last_ins->opcode = OP_IXOR;
2995 last_ins->dreg = mono_alloc_ireg(cfg);
2996 INS_REWRITE_IMM(ins, OP_MIPS_SLTIU, last_ins->dreg, 1);
2997 break;
2999 case OP_CLT:
3000 case OP_ICLT:
3001 INS_REWRITE(ins, OP_MIPS_SLT, last_ins->sreg1, last_ins->sreg2);
3002 NULLIFY_INS(last_ins);
3003 break;
3006 case OP_CLT_UN:
3007 case OP_ICLT_UN:
3008 INS_REWRITE(ins, OP_MIPS_SLTU, last_ins->sreg1, last_ins->sreg2);
3009 NULLIFY_INS(last_ins);
3010 break;
3012 case OP_CGT:
3013 case OP_ICGT:
3014 g_assert (ins_is_compare(last_ins));
3015 INS_REWRITE(ins, OP_MIPS_SLT, last_ins->sreg2, last_ins->sreg1);
3016 MONO_DELETE_INS(bb, last_ins);
3017 break;
3019 case OP_CGT_UN:
3020 case OP_ICGT_UN:
3021 g_assert (ins_is_compare(last_ins));
3022 INS_REWRITE(ins, OP_MIPS_SLTU, last_ins->sreg2, last_ins->sreg1);
3023 MONO_DELETE_INS(bb, last_ins);
3024 break;
3026 case OP_COND_EXC_EQ:
3027 case OP_COND_EXC_IEQ:
3028 g_assert (ins_is_compare(last_ins));
3029 INS_REWRITE(ins, OP_MIPS_COND_EXC_EQ, last_ins->sreg1, last_ins->sreg2);
3030 MONO_DELETE_INS(bb, last_ins);
3031 break;
3033 case OP_COND_EXC_GE:
3034 case OP_COND_EXC_IGE:
3035 g_assert (ins_is_compare(last_ins));
3036 INS_REWRITE(ins, OP_MIPS_COND_EXC_GE, last_ins->sreg1, last_ins->sreg2);
3037 MONO_DELETE_INS(bb, last_ins);
3038 break;
3040 case OP_COND_EXC_GT:
3041 case OP_COND_EXC_IGT:
3042 g_assert (ins_is_compare(last_ins));
3043 INS_REWRITE(ins, OP_MIPS_COND_EXC_GT, last_ins->sreg1, last_ins->sreg2);
3044 MONO_DELETE_INS(bb, last_ins);
3045 break;
3047 case OP_COND_EXC_LE:
3048 case OP_COND_EXC_ILE:
3049 g_assert (ins_is_compare(last_ins));
3050 INS_REWRITE(ins, OP_MIPS_COND_EXC_LE, last_ins->sreg1, last_ins->sreg2);
3051 MONO_DELETE_INS(bb, last_ins);
3052 break;
3054 case OP_COND_EXC_LT:
3055 case OP_COND_EXC_ILT:
3056 g_assert (ins_is_compare(last_ins));
3057 INS_REWRITE(ins, OP_MIPS_COND_EXC_LT, last_ins->sreg1, last_ins->sreg2);
3058 MONO_DELETE_INS(bb, last_ins);
3059 break;
3061 case OP_COND_EXC_NE_UN:
3062 case OP_COND_EXC_INE_UN:
3063 g_assert (ins_is_compare(last_ins));
3064 INS_REWRITE(ins, OP_MIPS_COND_EXC_NE_UN, last_ins->sreg1, last_ins->sreg2);
3065 MONO_DELETE_INS(bb, last_ins);
3066 break;
3068 case OP_COND_EXC_GE_UN:
3069 case OP_COND_EXC_IGE_UN:
3070 g_assert (ins_is_compare(last_ins));
3071 INS_REWRITE(ins, OP_MIPS_COND_EXC_GE_UN, last_ins->sreg1, last_ins->sreg2);
3072 MONO_DELETE_INS(bb, last_ins);
3073 break;
3075 case OP_COND_EXC_GT_UN:
3076 case OP_COND_EXC_IGT_UN:
3077 g_assert (ins_is_compare(last_ins));
3078 INS_REWRITE(ins, OP_MIPS_COND_EXC_GT_UN, last_ins->sreg1, last_ins->sreg2);
3079 MONO_DELETE_INS(bb, last_ins);
3080 break;
3082 case OP_COND_EXC_LE_UN:
3083 case OP_COND_EXC_ILE_UN:
3084 g_assert (ins_is_compare(last_ins));
3085 INS_REWRITE(ins, OP_MIPS_COND_EXC_LE_UN, last_ins->sreg1, last_ins->sreg2);
3086 MONO_DELETE_INS(bb, last_ins);
3087 break;
3089 case OP_COND_EXC_LT_UN:
3090 case OP_COND_EXC_ILT_UN:
3091 g_assert (ins_is_compare(last_ins));
3092 INS_REWRITE(ins, OP_MIPS_COND_EXC_LT_UN, last_ins->sreg1, last_ins->sreg2);
3093 MONO_DELETE_INS(bb, last_ins);
3094 break;
3096 case OP_COND_EXC_OV:
3097 case OP_COND_EXC_IOV: {
3098 int tmp1, tmp2, tmp3, tmp4, tmp5;
3099 MonoInst *pos = last_ins;
3101 /* Overflow happens if
3102 * neg + neg = pos or
3103 * pos + pos = neg
3105 * (bit31s of operands match) AND (bit31 of operand
3106 * != bit31 of result)
3107 * XOR of the high bit returns 0 if the signs match
3108 * XOR of that with the high bit of the result return 1
3109 * if overflow.
3111 g_assert (last_ins->opcode == OP_IADC);
3113 tmp1 = mono_alloc_ireg (cfg);
3114 tmp2 = mono_alloc_ireg (cfg);
3115 tmp3 = mono_alloc_ireg (cfg);
3116 tmp4 = mono_alloc_ireg (cfg);
3117 tmp5 = mono_alloc_ireg (cfg);
3119 /* tmp1 = 0 if the signs of the two inputs match, else 1 */
3120 INS (pos, OP_IXOR, tmp1, last_ins->sreg1, last_ins->sreg2);
3122 /* set tmp2 = 0 if bit31 of results matches is different than the operands */
3123 INS (pos, OP_IXOR, tmp2, last_ins->dreg, last_ins->sreg2);
3124 INS (pos, OP_INOT, tmp3, tmp2, -1);
3126 /* OR(tmp1, tmp2) = 0 if both conditions are true */
3127 INS (pos, OP_IOR, tmp4, tmp3, tmp1);
3128 INS_IMM (pos, OP_SHR_IMM, tmp5, tmp4, 31);
3130 /* Now, if (tmp5 == 0) then overflow */
3131 INS_REWRITE(ins, OP_MIPS_COND_EXC_EQ, tmp5, mips_zero);
3132 ins->dreg = -1;
3133 break;
3136 case OP_COND_EXC_NO:
3137 case OP_COND_EXC_INO:
3138 g_assert_not_reached ();
3139 break;
3141 case OP_COND_EXC_C:
3142 case OP_COND_EXC_IC:
3143 g_assert_not_reached ();
3144 break;
3146 case OP_COND_EXC_NC:
3147 case OP_COND_EXC_INC:
3148 g_assert_not_reached ();
3149 break;
3152 last_ins = ins;
3154 bb->last_ins = last_ins;
3155 bb->max_vreg = cfg->next_vreg;
3157 #if 1
3158 if (cfg->verbose_level > 2) {
3159 int idx = 0;
3161 g_print ("BASIC BLOCK %d (after lowering)\n", bb->block_num);
3162 MONO_BB_FOR_EACH_INS (bb, ins) {
3163 mono_print_ins_index (idx++, ins);
3167 #endif
3171 static guchar*
3172 emit_float_to_int (MonoCompile *cfg, guchar *code, int dreg, int sreg, int size, gboolean is_signed)
3174 /* sreg is a float, dreg is an integer reg. mips_at is used as scratch */
3175 #if 1
3176 mips_truncwd (code, mips_ftemp, sreg);
3177 #else
3178 mips_cvtwd (code, mips_ftemp, sreg);
3179 #endif
3180 mips_mfc1 (code, dreg, mips_ftemp);
3181 if (!is_signed) {
3182 if (size == 1)
3183 mips_andi (code, dreg, dreg, 0xff);
3184 else if (size == 2) {
3185 mips_sll (code, dreg, dreg, 16);
3186 mips_srl (code, dreg, dreg, 16);
3188 } else {
3189 if (size == 1) {
3190 mips_sll (code, dreg, dreg, 24);
3191 mips_sra (code, dreg, dreg, 24);
3193 else if (size == 2) {
3194 mips_sll (code, dreg, dreg, 16);
3195 mips_sra (code, dreg, dreg, 16);
3198 return code;
3202 * emit_load_volatile_arguments:
3204 * Load volatile arguments from the stack to the original input registers.
3205 * Required before a tail call.
3207 static guint8 *
3208 emit_load_volatile_arguments(MonoCompile *cfg, guint8 *code)
3210 MonoMethod *method = cfg->method;
3211 MonoMethodSignature *sig;
3212 MonoInst *inst;
3213 CallInfo *cinfo;
3214 int i;
3216 sig = mono_method_signature (method);
3218 if (!cfg->arch.cinfo)
3219 cfg->arch.cinfo = get_call_info (cfg->mempool, sig);
3220 cinfo = cfg->arch.cinfo;
3222 if (cinfo->struct_ret) {
3223 ArgInfo *ainfo = &cinfo->ret;
3224 inst = cfg->vret_addr;
3225 mips_lw (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3228 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
3229 ArgInfo *ainfo = cinfo->args + i;
3230 inst = cfg->args [i];
3231 if (inst->opcode == OP_REGVAR) {
3232 if (ainfo->storage == ArgInIReg)
3233 MIPS_MOVE (code, ainfo->reg, inst->dreg);
3234 else if (ainfo->storage == ArgInFReg)
3235 g_assert_not_reached();
3236 else if (ainfo->storage == ArgOnStack) {
3237 /* do nothing */
3238 } else
3239 g_assert_not_reached ();
3240 } else {
3241 if (ainfo->storage == ArgInIReg) {
3242 g_assert (mips_is_imm16 (inst->inst_offset));
3243 switch (ainfo->size) {
3244 case 1:
3245 mips_lb (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3246 break;
3247 case 2:
3248 mips_lh (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3249 break;
3250 case 0: /* XXX */
3251 case 4:
3252 mips_lw (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3253 break;
3254 case 8:
3255 mips_lw (code, ainfo->reg, inst->inst_basereg, inst->inst_offset + ls_word_offset);
3256 mips_lw (code, ainfo->reg + 1, inst->inst_basereg, inst->inst_offset + ms_word_offset);
3257 break;
3258 default:
3259 g_assert_not_reached ();
3260 break;
3262 } else if (ainfo->storage == ArgOnStack) {
3263 /* do nothing */
3264 } else if (ainfo->storage == ArgInFReg) {
3265 g_assert (mips_is_imm16 (inst->inst_offset));
3266 if (ainfo->size == 8) {
3267 #if _MIPS_SIM == _ABIO32
3268 mips_lwc1 (code, ainfo->reg, inst->inst_basereg, inst->inst_offset + ls_word_offset);
3269 mips_lwc1 (code, ainfo->reg+1, inst->inst_basereg, inst->inst_offset + ms_word_offset);
3270 #elif _MIPS_SIM == _ABIN32
3271 mips_ldc1 (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3272 #endif
3274 else if (ainfo->size == 4)
3275 mips_lwc1 (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3276 else
3277 g_assert_not_reached ();
3278 } else if (ainfo->storage == ArgStructByVal) {
3279 int i;
3280 int doffset = inst->inst_offset;
3282 g_assert (mips_is_imm16 (inst->inst_offset));
3283 g_assert (mips_is_imm16 (inst->inst_offset + ainfo->size * sizeof (gpointer)));
3284 for (i = 0; i < ainfo->size; ++i) {
3285 mips_lw (code, ainfo->reg + i, inst->inst_basereg, doffset);
3286 doffset += SIZEOF_REGISTER;
3288 } else if (ainfo->storage == ArgStructByAddr) {
3289 g_assert (mips_is_imm16 (inst->inst_offset));
3290 mips_lw (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3291 } else
3292 g_assert_not_reached ();
3296 return code;
3299 static guint8*
3300 emit_reserve_param_area (MonoCompile *cfg, guint8 *code)
3302 int size = cfg->param_area;
3304 size += MONO_ARCH_FRAME_ALIGNMENT - 1;
3305 size &= -MONO_ARCH_FRAME_ALIGNMENT;
3307 if (!size)
3308 return code;
3309 #if 0
3310 ppc_lwz (code, ppc_r0, 0, ppc_sp);
3311 if (ppc_is_imm16 (-size)) {
3312 ppc_stwu (code, ppc_r0, -size, ppc_sp);
3313 } else {
3314 ppc_load (code, ppc_r12, -size);
3315 ppc_stwux (code, ppc_r0, ppc_sp, ppc_r12);
3317 #endif
3318 return code;
3321 static guint8*
3322 emit_unreserve_param_area (MonoCompile *cfg, guint8 *code)
3324 int size = cfg->param_area;
3326 size += MONO_ARCH_FRAME_ALIGNMENT - 1;
3327 size &= -MONO_ARCH_FRAME_ALIGNMENT;
3329 if (!size)
3330 return code;
3331 #if 0
3332 ppc_lwz (code, ppc_r0, 0, ppc_sp);
3333 if (ppc_is_imm16 (size)) {
3334 ppc_stwu (code, ppc_r0, size, ppc_sp);
3335 } else {
3336 ppc_load (code, ppc_r12, size);
3337 ppc_stwux (code, ppc_r0, ppc_sp, ppc_r12);
3339 #endif
3340 return code;
3343 void
3344 mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
3346 MonoInst *ins;
3347 MonoCallInst *call;
3348 guint offset;
3349 guint8 *code = cfg->native_code + cfg->code_len;
3350 MonoInst *last_ins = NULL;
3351 guint last_offset = 0;
3352 int max_len, cpos;
3353 int ins_cnt = 0;
3355 /* we don't align basic blocks of loops on mips */
3357 if (cfg->verbose_level > 2)
3358 g_print ("Basic block %d starting at offset 0x%x\n", bb->block_num, bb->native_offset);
3360 cpos = bb->max_offset;
3362 #if 0
3363 if (cfg->prof_options & MONO_PROFILE_COVERAGE) {
3364 MonoCoverageInfo *cov = mono_get_coverage_info (cfg->method);
3365 g_assert (!mono_compile_aot);
3366 cpos += 20;
3367 if (bb->cil_code)
3368 cov->data [bb->dfn].iloffset = bb->cil_code - cfg->cil_code;
3369 /* this is not thread save, but good enough */
3370 /* fixme: howto handle overflows? */
3371 mips_load_const (code, mips_at, &cov->data [bb->dfn].count);
3372 mips_lw (code, mips_temp, mips_at, 0);
3373 mips_addiu (code, mips_temp, mips_temp, 1);
3374 mips_sw (code, mips_temp, mips_at, 0);
3376 #endif
3377 MONO_BB_FOR_EACH_INS (bb, ins) {
3378 offset = code - cfg->native_code;
3380 max_len = ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
3382 if (offset > (cfg->code_size - max_len - 16)) {
3383 cfg->code_size *= 2;
3384 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
3385 code = cfg->native_code + offset;
3387 mono_debug_record_line_number (cfg, ins, offset);
3388 if (cfg->verbose_level > 2) {
3389 g_print (" @ 0x%x\t", offset);
3390 mono_print_ins_index (ins_cnt++, ins);
3392 /* Check for virtual regs that snuck by */
3393 g_assert ((ins->dreg >= -1) && (ins->dreg < 32));
3395 switch (ins->opcode) {
3396 case OP_RELAXED_NOP:
3397 case OP_NOP:
3398 case OP_DUMMY_USE:
3399 case OP_DUMMY_STORE:
3400 case OP_NOT_REACHED:
3401 case OP_NOT_NULL:
3402 break;
3403 case OP_IL_SEQ_POINT:
3404 mono_add_seq_point (cfg, bb, ins, code - cfg->native_code);
3405 break;
3406 case OP_SEQ_POINT: {
3407 if (ins->flags & MONO_INST_SINGLE_STEP_LOC) {
3408 guint32 addr = (guint32)ss_trigger_page;
3410 mips_load_const (code, mips_t9, addr);
3411 mips_lw (code, mips_t9, mips_t9, 0);
3414 mono_add_seq_point (cfg, bb, ins, code - cfg->native_code);
3417 * A placeholder for a possible breakpoint inserted by
3418 * mono_arch_set_breakpoint ().
3420 /* mips_load_const () + mips_lw */
3421 mips_nop (code);
3422 mips_nop (code);
3423 mips_nop (code);
3424 break;
3426 case OP_TLS_GET:
3427 g_assert_not_reached();
3428 #if 0
3429 emit_tls_access (code, ins->dreg, ins->inst_offset);
3430 #endif
3431 break;
3432 case OP_BIGMUL:
3433 mips_mult (code, ins->sreg1, ins->sreg2);
3434 mips_mflo (code, ins->dreg);
3435 mips_mfhi (code, ins->dreg+1);
3436 break;
3437 case OP_BIGMUL_UN:
3438 mips_multu (code, ins->sreg1, ins->sreg2);
3439 mips_mflo (code, ins->dreg);
3440 mips_mfhi (code, ins->dreg+1);
3441 break;
3442 case OP_MEMORY_BARRIER:
3443 mips_sync (code, 0);
3444 break;
3445 case OP_STOREI1_MEMBASE_IMM:
3446 mips_load_const (code, mips_temp, ins->inst_imm);
3447 if (mips_is_imm16 (ins->inst_offset)) {
3448 mips_sb (code, mips_temp, ins->inst_destbasereg, ins->inst_offset);
3449 } else {
3450 mips_load_const (code, mips_at, ins->inst_offset);
3451 mips_sb (code, mips_temp, mips_at, ins->inst_destbasereg);
3453 break;
3454 case OP_STOREI2_MEMBASE_IMM:
3455 mips_load_const (code, mips_temp, ins->inst_imm);
3456 if (mips_is_imm16 (ins->inst_offset)) {
3457 mips_sh (code, mips_temp, ins->inst_destbasereg, ins->inst_offset);
3458 } else {
3459 mips_load_const (code, mips_at, ins->inst_offset);
3460 mips_sh (code, mips_temp, mips_at, ins->inst_destbasereg);
3462 break;
3463 case OP_STOREI8_MEMBASE_IMM:
3464 mips_load_const (code, mips_temp, ins->inst_imm);
3465 if (mips_is_imm16 (ins->inst_offset)) {
3466 mips_sd (code, mips_temp, ins->inst_destbasereg, ins->inst_offset);
3467 } else {
3468 mips_load_const (code, mips_at, ins->inst_offset);
3469 mips_sd (code, mips_temp, mips_at, ins->inst_destbasereg);
3471 break;
3472 case OP_STORE_MEMBASE_IMM:
3473 case OP_STOREI4_MEMBASE_IMM:
3474 mips_load_const (code, mips_temp, ins->inst_imm);
3475 if (mips_is_imm16 (ins->inst_offset)) {
3476 mips_sw (code, mips_temp, ins->inst_destbasereg, ins->inst_offset);
3477 } else {
3478 mips_load_const (code, mips_at, ins->inst_offset);
3479 mips_sw (code, mips_temp, mips_at, ins->inst_destbasereg);
3481 break;
3482 case OP_STOREI1_MEMBASE_REG:
3483 if (mips_is_imm16 (ins->inst_offset)) {
3484 mips_sb (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
3485 } else {
3486 mips_load_const (code, mips_at, ins->inst_offset);
3487 mips_addu (code, mips_at, mips_at, ins->inst_destbasereg);
3488 mips_sb (code, ins->sreg1, mips_at, 0);
3490 break;
3491 case OP_STOREI2_MEMBASE_REG:
3492 if (mips_is_imm16 (ins->inst_offset)) {
3493 mips_sh (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
3494 } else {
3495 mips_load_const (code, mips_at, ins->inst_offset);
3496 mips_addu (code, mips_at, mips_at, ins->inst_destbasereg);
3497 mips_sh (code, ins->sreg1, mips_at, 0);
3499 break;
3500 case OP_STORE_MEMBASE_REG:
3501 case OP_STOREI4_MEMBASE_REG:
3502 if (mips_is_imm16 (ins->inst_offset)) {
3503 mips_sw (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
3504 } else {
3505 mips_load_const (code, mips_at, ins->inst_offset);
3506 mips_addu (code, mips_at, mips_at, ins->inst_destbasereg);
3507 mips_sw (code, ins->sreg1, mips_at, 0);
3509 break;
3510 case OP_STOREI8_MEMBASE_REG:
3511 if (mips_is_imm16 (ins->inst_offset)) {
3512 mips_sd (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
3513 } else {
3514 mips_load_const (code, mips_at, ins->inst_offset);
3515 mips_addu (code, mips_at, mips_at, ins->inst_destbasereg);
3516 mips_sd (code, ins->sreg1, mips_at, 0);
3518 break;
3519 case OP_LOADU4_MEM:
3520 g_assert_not_reached ();
3521 //x86_mov_reg_imm (code, ins->dreg, ins->inst_p0);
3522 //x86_mov_reg_membase (code, ins->dreg, ins->dreg, 0, 4);
3523 break;
3524 case OP_LOADI8_MEMBASE:
3525 if (mips_is_imm16 (ins->inst_offset)) {
3526 mips_ld (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3527 } else {
3528 mips_load_const (code, mips_at, ins->inst_offset);
3529 mips_addu (code, mips_at, mips_at, ins->inst_basereg);
3530 mips_ld (code, ins->dreg, mips_at, 0);
3532 break;
3533 case OP_LOAD_MEMBASE:
3534 case OP_LOADI4_MEMBASE:
3535 case OP_LOADU4_MEMBASE:
3536 g_assert (ins->dreg != -1);
3537 if (mips_is_imm16 (ins->inst_offset)) {
3538 mips_lw (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3539 } else {
3540 mips_load_const (code, mips_at, ins->inst_offset);
3541 mips_addu (code, mips_at, mips_at, ins->inst_basereg);
3542 mips_lw (code, ins->dreg, mips_at, 0);
3544 break;
3545 case OP_LOADI1_MEMBASE:
3546 if (mips_is_imm16 (ins->inst_offset)) {
3547 mips_lb (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3548 } else {
3549 mips_load_const (code, mips_at, ins->inst_offset);
3550 mips_addu (code, mips_at, mips_at, ins->inst_basereg);
3551 mips_lb (code, ins->dreg, mips_at, 0);
3553 break;
3554 case OP_LOADU1_MEMBASE:
3555 if (mips_is_imm16 (ins->inst_offset)) {
3556 mips_lbu (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3557 } else {
3558 mips_load_const (code, mips_at, ins->inst_offset);
3559 mips_addu (code, mips_at, mips_at, ins->inst_basereg);
3560 mips_lbu (code, ins->dreg, mips_at, 0);
3562 break;
3563 case OP_LOADI2_MEMBASE:
3564 if (mips_is_imm16 (ins->inst_offset)) {
3565 mips_lh (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3566 } else {
3567 mips_load_const (code, mips_at, ins->inst_offset);
3568 mips_addu (code, mips_at, mips_at, ins->inst_basereg);
3569 mips_lh (code, ins->dreg, mips_at, 0);
3571 break;
3572 case OP_LOADU2_MEMBASE:
3573 if (mips_is_imm16 (ins->inst_offset)) {
3574 mips_lhu (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3575 } else {
3576 mips_load_const (code, mips_at, ins->inst_offset);
3577 mips_addu (code, mips_at, mips_at, ins->inst_basereg);
3578 mips_lhu (code, ins->dreg, mips_at, 0);
3580 break;
3581 case OP_ICONV_TO_I1:
3582 mips_sll (code, mips_at, ins->sreg1, 24);
3583 mips_sra (code, ins->dreg, mips_at, 24);
3584 break;
3585 case OP_ICONV_TO_I2:
3586 mips_sll (code, mips_at, ins->sreg1, 16);
3587 mips_sra (code, ins->dreg, mips_at, 16);
3588 break;
3589 case OP_ICONV_TO_U1:
3590 mips_andi (code, ins->dreg, ins->sreg1, 0xff);
3591 break;
3592 case OP_ICONV_TO_U2:
3593 mips_sll (code, mips_at, ins->sreg1, 16);
3594 mips_srl (code, ins->dreg, mips_at, 16);
3595 break;
3596 case OP_MIPS_SLT:
3597 mips_slt (code, ins->dreg, ins->sreg1, ins->sreg2);
3598 break;
3599 case OP_MIPS_SLTI:
3600 g_assert (mips_is_imm16 (ins->inst_imm));
3601 mips_slti (code, ins->dreg, ins->sreg1, ins->inst_imm);
3602 break;
3603 case OP_MIPS_SLTU:
3604 mips_sltu (code, ins->dreg, ins->sreg1, ins->sreg2);
3605 break;
3606 case OP_MIPS_SLTIU:
3607 g_assert (mips_is_imm16 (ins->inst_imm));
3608 mips_sltiu (code, ins->dreg, ins->sreg1, ins->inst_imm);
3609 break;
3610 case OP_BREAK:
3612 * gdb does not like encountering the hw breakpoint ins in the debugged code.
3613 * So instead of emitting a trap, we emit a call a C function and place a
3614 * breakpoint there.
3616 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
3617 (gpointer)"mono_break");
3618 mips_load (code, mips_t9, 0x1f1f1f1f);
3619 mips_jalr (code, mips_t9, mips_ra);
3620 mips_nop (code);
3621 break;
3622 case OP_IADD:
3623 mips_addu (code, ins->dreg, ins->sreg1, ins->sreg2);
3624 break;
3625 case OP_LADD:
3626 mips_daddu (code, ins->dreg, ins->sreg1, ins->sreg2);
3627 break;
3629 case OP_ADD_IMM:
3630 case OP_IADD_IMM:
3631 g_assert (mips_is_imm16 (ins->inst_imm));
3632 mips_addiu (code, ins->dreg, ins->sreg1, ins->inst_imm);
3633 break;
3634 case OP_LADD_IMM:
3635 g_assert (mips_is_imm16 (ins->inst_imm));
3636 mips_daddiu (code, ins->dreg, ins->sreg1, ins->inst_imm);
3637 break;
3639 case OP_ISUB:
3640 mips_subu (code, ins->dreg, ins->sreg1, ins->sreg2);
3641 break;
3642 case OP_LSUB:
3643 mips_dsubu (code, ins->dreg, ins->sreg1, ins->sreg2);
3644 break;
3646 case OP_ISUB_IMM:
3647 case OP_SUB_IMM:
3648 // we add the negated value
3649 g_assert (mips_is_imm16 (-ins->inst_imm));
3650 mips_addiu (code, ins->dreg, ins->sreg1, -ins->inst_imm);
3651 break;
3653 case OP_LSUB_IMM:
3654 // we add the negated value
3655 g_assert (mips_is_imm16 (-ins->inst_imm));
3656 mips_daddiu (code, ins->dreg, ins->sreg1, -ins->inst_imm);
3657 break;
3659 case OP_IAND:
3660 case OP_LAND:
3661 mips_and (code, ins->dreg, ins->sreg1, ins->sreg2);
3662 break;
3664 case OP_AND_IMM:
3665 case OP_IAND_IMM:
3666 case OP_LAND_IMM:
3667 g_assert (!(ins->inst_imm & 0xffff0000));
3668 mips_andi (code, ins->dreg, ins->sreg1, ins->inst_imm);
3669 break;
3671 case OP_IDIV:
3672 case OP_IREM: {
3673 guint32 *divisor_is_m1;
3674 guint32 *dividend_is_minvalue;
3675 guint32 *divisor_is_zero;
3677 mips_load_const (code, mips_at, -1);
3678 divisor_is_m1 = (guint32 *)(void *)code;
3679 mips_bne (code, ins->sreg2, mips_at, 0);
3680 mips_lui (code, mips_at, mips_zero, 0x8000);
3681 dividend_is_minvalue = (guint32 *)(void *)code;
3682 mips_bne (code, ins->sreg1, mips_at, 0);
3683 mips_nop (code);
3685 /* Divide Int32.MinValue by -1 -- throw exception */
3686 EMIT_SYSTEM_EXCEPTION_NAME("OverflowException");
3688 mips_patch (divisor_is_m1, (guint32)code);
3689 mips_patch (dividend_is_minvalue, (guint32)code);
3691 /* Put divide in branch delay slot (NOT YET) */
3692 divisor_is_zero = (guint32 *)(void *)code;
3693 mips_bne (code, ins->sreg2, mips_zero, 0);
3694 mips_nop (code);
3696 /* Divide by zero -- throw exception */
3697 EMIT_SYSTEM_EXCEPTION_NAME("DivideByZeroException");
3699 mips_patch (divisor_is_zero, (guint32)code);
3700 mips_div (code, ins->sreg1, ins->sreg2);
3701 if (ins->opcode == OP_IDIV)
3702 mips_mflo (code, ins->dreg);
3703 else
3704 mips_mfhi (code, ins->dreg);
3705 break;
3707 case OP_IDIV_UN:
3708 case OP_IREM_UN: {
3709 guint32 *divisor_is_zero = (guint32 *)(void *)code;
3711 /* Put divide in branch delay slot (NOT YET) */
3712 mips_bne (code, ins->sreg2, mips_zero, 0);
3713 mips_nop (code);
3715 /* Divide by zero -- throw exception */
3716 EMIT_SYSTEM_EXCEPTION_NAME("DivideByZeroException");
3718 mips_patch (divisor_is_zero, (guint32)code);
3719 mips_divu (code, ins->sreg1, ins->sreg2);
3720 if (ins->opcode == OP_IDIV_UN)
3721 mips_mflo (code, ins->dreg);
3722 else
3723 mips_mfhi (code, ins->dreg);
3724 break;
3726 case OP_DIV_IMM:
3727 g_assert_not_reached ();
3728 #if 0
3729 ppc_load (code, ppc_r12, ins->inst_imm);
3730 ppc_divwod (code, ins->dreg, ins->sreg1, ppc_r12);
3731 ppc_mfspr (code, ppc_r0, ppc_xer);
3732 ppc_andisd (code, ppc_r0, ppc_r0, (1<<14));
3733 /* FIXME: use OverflowException for 0x80000000/-1 */
3734 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "DivideByZeroException");
3735 #endif
3736 g_assert_not_reached();
3737 break;
3738 case OP_REM_IMM:
3739 g_assert_not_reached ();
3740 case OP_IOR:
3741 mips_or (code, ins->dreg, ins->sreg1, ins->sreg2);
3742 break;
3743 case OP_OR_IMM:
3744 case OP_IOR_IMM:
3745 g_assert (!(ins->inst_imm & 0xffff0000));
3746 mips_ori (code, ins->dreg, ins->sreg1, ins->inst_imm);
3747 break;
3748 case OP_IXOR:
3749 mips_xor (code, ins->dreg, ins->sreg1, ins->sreg2);
3750 break;
3751 case OP_XOR_IMM:
3752 case OP_IXOR_IMM:
3753 /* unsigned 16-bit immediate */
3754 g_assert (!(ins->inst_imm & 0xffff0000));
3755 mips_xori (code, ins->dreg, ins->sreg1, ins->inst_imm);
3756 break;
3757 case OP_ISHL:
3758 mips_sllv (code, ins->dreg, ins->sreg1, ins->sreg2);
3759 break;
3760 case OP_SHL_IMM:
3761 case OP_ISHL_IMM:
3762 mips_sll (code, ins->dreg, ins->sreg1, ins->inst_imm & 0x1f);
3763 break;
3764 case OP_ISHR:
3765 mips_srav (code, ins->dreg, ins->sreg1, ins->sreg2);
3766 break;
3767 case OP_LSHR:
3768 mips_dsrav (code, ins->dreg, ins->sreg1, ins->sreg2);
3769 break;
3770 case OP_SHR_IMM:
3771 case OP_ISHR_IMM:
3772 mips_sra (code, ins->dreg, ins->sreg1, ins->inst_imm & 0x1f);
3773 break;
3774 case OP_LSHR_IMM:
3775 mips_dsra (code, ins->dreg, ins->sreg1, ins->inst_imm & 0x3f);
3776 break;
3777 case OP_SHR_UN_IMM:
3778 case OP_ISHR_UN_IMM:
3779 mips_srl (code, ins->dreg, ins->sreg1, ins->inst_imm & 0x1f);
3780 break;
3781 case OP_LSHR_UN_IMM:
3782 mips_dsrl (code, ins->dreg, ins->sreg1, ins->inst_imm & 0x3f);
3783 break;
3784 case OP_ISHR_UN:
3785 mips_srlv (code, ins->dreg, ins->sreg1, ins->sreg2);
3786 break;
3787 case OP_LSHR_UN:
3788 mips_dsrlv (code, ins->dreg, ins->sreg1, ins->sreg2);
3789 break;
3790 case OP_INOT:
3791 case OP_LNOT:
3792 mips_nor (code, ins->dreg, mips_zero, ins->sreg1);
3793 break;
3794 case OP_INEG:
3795 mips_subu (code, ins->dreg, mips_zero, ins->sreg1);
3796 break;
3797 case OP_LNEG:
3798 mips_dsubu (code, ins->dreg, mips_zero, ins->sreg1);
3799 break;
3800 case OP_IMUL:
3801 #if USE_MUL
3802 mips_mul (code, ins->dreg, ins->sreg1, ins->sreg2);
3803 #else
3804 mips_mult (code, ins->sreg1, ins->sreg2);
3805 mips_mflo (code, ins->dreg);
3806 mips_nop (code);
3807 mips_nop (code);
3808 #endif
3809 break;
3810 #if SIZEOF_REGISTER == 8
3811 case OP_LMUL:
3812 mips_dmult (code, ins->sreg1, ins->sreg2);
3813 mips_mflo (code, ins->dreg);
3814 break;
3815 #endif
3816 case OP_IMUL_OVF: {
3817 guint32 *patch;
3818 mips_mult (code, ins->sreg1, ins->sreg2);
3819 mips_mflo (code, ins->dreg);
3820 mips_mfhi (code, mips_at);
3821 mips_nop (code);
3822 mips_nop (code);
3823 mips_sra (code, mips_temp, ins->dreg, 31);
3824 patch = (guint32 *)(void *)code;
3825 mips_beq (code, mips_temp, mips_at, 0);
3826 mips_nop (code);
3827 EMIT_SYSTEM_EXCEPTION_NAME("OverflowException");
3828 mips_patch (patch, (guint32)code);
3829 break;
3831 case OP_IMUL_OVF_UN: {
3832 guint32 *patch;
3833 mips_mult (code, ins->sreg1, ins->sreg2);
3834 mips_mflo (code, ins->dreg);
3835 mips_mfhi (code, mips_at);
3836 mips_nop (code);
3837 mips_nop (code);
3838 patch = (guint32 *)(void *)code;
3839 mips_beq (code, mips_at, mips_zero, 0);
3840 mips_nop (code);
3841 EMIT_SYSTEM_EXCEPTION_NAME("OverflowException");
3842 mips_patch (patch, (guint32)code);
3843 break;
3845 case OP_ICONST:
3846 mips_load_const (code, ins->dreg, ins->inst_c0);
3847 break;
3848 #if SIZEOF_REGISTER == 8
3849 case OP_I8CONST:
3850 mips_load_const (code, ins->dreg, ins->inst_c0);
3851 break;
3852 #endif
3853 case OP_AOTCONST:
3854 mono_add_patch_info (cfg, offset, (MonoJumpInfoType)ins->inst_i1, ins->inst_p0);
3855 mips_load (code, ins->dreg, 0);
3856 break;
3858 case OP_MIPS_MTC1S:
3859 mips_mtc1 (code, ins->dreg, ins->sreg1);
3860 break;
3861 case OP_MIPS_MTC1S_2:
3862 mips_mtc1 (code, ins->dreg, ins->sreg1);
3863 mips_mtc1 (code, ins->dreg+1, ins->sreg2);
3864 break;
3865 case OP_MIPS_MFC1S:
3866 mips_mfc1 (code, ins->dreg, ins->sreg1);
3867 break;
3868 case OP_MIPS_MTC1D:
3869 mips_dmtc1 (code, ins->dreg, ins->sreg1);
3870 break;
3871 case OP_MIPS_MFC1D:
3872 #if 0
3873 mips_dmfc1 (code, ins->dreg, ins->sreg1);
3874 #else
3875 mips_mfc1 (code, ins->dreg, ins->sreg1 + ls_word_idx);
3876 mips_mfc1 (code, ins->dreg+1, ins->sreg1 + ms_word_idx);
3877 #endif
3878 break;
3880 case OP_ICONV_TO_I4:
3881 case OP_ICONV_TO_U4:
3882 case OP_MOVE:
3883 if (ins->dreg != ins->sreg1)
3884 MIPS_MOVE (code, ins->dreg, ins->sreg1);
3885 break;
3886 #if SIZEOF_REGISTER == 8
3887 case OP_ZEXT_I4:
3888 mips_dsll (code, ins->dreg, ins->sreg1, 32);
3889 mips_dsrl (code, ins->dreg, ins->dreg, 32);
3890 break;
3891 case OP_SEXT_I4:
3892 mips_dsll (code, ins->dreg, ins->sreg1, 32);
3893 mips_dsra (code, ins->dreg, ins->dreg, 32);
3894 break;
3895 #endif
3896 case OP_SETLRET: {
3897 int lsreg = mips_v0 + ls_word_idx;
3898 int msreg = mips_v0 + ms_word_idx;
3900 /* Get sreg1 into lsreg, sreg2 into msreg */
3902 if (ins->sreg1 == msreg) {
3903 if (ins->sreg1 != mips_at)
3904 MIPS_MOVE (code, mips_at, ins->sreg1);
3905 if (ins->sreg2 != msreg)
3906 MIPS_MOVE (code, msreg, ins->sreg2);
3907 MIPS_MOVE (code, lsreg, mips_at);
3909 else {
3910 if (ins->sreg2 != msreg)
3911 MIPS_MOVE (code, msreg, ins->sreg2);
3912 if (ins->sreg1 != lsreg)
3913 MIPS_MOVE (code, lsreg, ins->sreg1);
3915 break;
3917 case OP_FMOVE:
3918 if (ins->dreg != ins->sreg1) {
3919 mips_fmovd (code, ins->dreg, ins->sreg1);
3921 break;
3922 case OP_MOVE_F_TO_I4:
3923 mips_cvtsd (code, mips_ftemp, ins->sreg1);
3924 mips_mfc1 (code, ins->dreg, mips_ftemp);
3925 break;
3926 case OP_MOVE_I4_TO_F:
3927 mips_mtc1 (code, ins->dreg, ins->sreg1);
3928 mips_cvtds (code, ins->dreg, ins->dreg);
3929 break;
3930 case OP_MIPS_CVTSD:
3931 /* Convert from double to float and leave it there */
3932 mips_cvtsd (code, ins->dreg, ins->sreg1);
3933 break;
3934 case OP_FCONV_TO_R4:
3935 #if 0
3936 mips_cvtsd (code, ins->dreg, ins->sreg1);
3937 #else
3938 /* Just a move, no precision change */
3939 if (ins->dreg != ins->sreg1) {
3940 mips_fmovd (code, ins->dreg, ins->sreg1);
3942 #endif
3943 break;
3944 case OP_JMP:
3945 code = emit_load_volatile_arguments(cfg, code);
3948 * Pop our stack, then jump to specified method (tail-call)
3949 * Keep in sync with mono_arch_emit_epilog
3951 code = mono_arch_emit_epilog_sub (cfg, code);
3953 mono_add_patch_info (cfg, (guint8*) code - cfg->native_code,
3954 MONO_PATCH_INFO_METHOD_JUMP, ins->inst_p0);
3955 mips_load (code, mips_t9, 0);
3956 mips_jr (code, mips_t9);
3957 mips_nop (code);
3958 break;
3959 case OP_CHECK_THIS:
3960 /* ensure ins->sreg1 is not NULL */
3961 mips_lw (code, mips_zero, ins->sreg1, 0);
3962 break;
3963 case OP_ARGLIST: {
3964 g_assert (mips_is_imm16 (cfg->sig_cookie));
3965 mips_lw (code, mips_at, cfg->frame_reg, cfg->sig_cookie);
3966 mips_sw (code, mips_at, ins->sreg1, 0);
3967 break;
3969 case OP_FCALL:
3970 case OP_LCALL:
3971 case OP_VCALL:
3972 case OP_VCALL2:
3973 case OP_VOIDCALL:
3974 case OP_CALL:
3975 case OP_FCALL_REG:
3976 case OP_LCALL_REG:
3977 case OP_VCALL_REG:
3978 case OP_VCALL2_REG:
3979 case OP_VOIDCALL_REG:
3980 case OP_CALL_REG:
3981 case OP_FCALL_MEMBASE:
3982 case OP_LCALL_MEMBASE:
3983 case OP_VCALL_MEMBASE:
3984 case OP_VCALL2_MEMBASE:
3985 case OP_VOIDCALL_MEMBASE:
3986 case OP_CALL_MEMBASE:
3987 call = (MonoCallInst*)ins;
3988 switch (ins->opcode) {
3989 case OP_FCALL:
3990 case OP_LCALL:
3991 case OP_VCALL:
3992 case OP_VCALL2:
3993 case OP_VOIDCALL:
3994 case OP_CALL:
3995 if (ins->flags & MONO_INST_HAS_METHOD) {
3996 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_METHOD, call->method);
3997 mips_load (code, mips_t9, call->method);
3999 else {
4000 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_ABS, call->fptr);
4001 mips_load (code, mips_t9, call->fptr);
4003 mips_jalr (code, mips_t9, mips_ra);
4004 mips_nop (code);
4005 break;
4006 case OP_FCALL_REG:
4007 case OP_LCALL_REG:
4008 case OP_VCALL_REG:
4009 case OP_VCALL2_REG:
4010 case OP_VOIDCALL_REG:
4011 case OP_CALL_REG:
4012 MIPS_MOVE (code, mips_t9, ins->sreg1);
4013 mips_jalr (code, mips_t9, mips_ra);
4014 mips_nop (code);
4015 break;
4016 case OP_FCALL_MEMBASE:
4017 case OP_LCALL_MEMBASE:
4018 case OP_VCALL_MEMBASE:
4019 case OP_VCALL2_MEMBASE:
4020 case OP_VOIDCALL_MEMBASE:
4021 case OP_CALL_MEMBASE:
4022 mips_lw (code, mips_t9, ins->sreg1, ins->inst_offset);
4023 mips_jalr (code, mips_t9, mips_ra);
4024 mips_nop (code);
4025 break;
4027 #if PROMOTE_R4_TO_R8
4028 /* returned an FP R4 (single), promote to R8 (double) in place */
4029 switch (ins->opcode) {
4030 case OP_FCALL:
4031 case OP_FCALL_REG:
4032 case OP_FCALL_MEMBASE:
4033 if (call->signature->ret->type == MONO_TYPE_R4)
4034 mips_cvtds (code, mips_f0, mips_f0);
4035 break;
4036 default:
4037 break;
4039 #endif
4040 break;
4041 case OP_LOCALLOC: {
4042 int area_offset = cfg->param_area;
4044 /* Round up ins->sreg1, mips_at ends up holding size */
4045 mips_addiu (code, mips_at, ins->sreg1, 31);
4046 mips_addiu (code, mips_temp, mips_zero, ~31);
4047 mips_and (code, mips_at, mips_at, mips_temp);
4049 mips_subu (code, mips_sp, mips_sp, mips_at);
4050 g_assert (mips_is_imm16 (area_offset));
4051 mips_addiu (code, ins->dreg, mips_sp, area_offset);
4053 if (ins->flags & MONO_INST_INIT) {
4054 guint32 *buf;
4056 buf = (guint32*)(void*)code;
4057 mips_beq (code, mips_at, mips_zero, 0);
4058 mips_nop (code);
4060 mips_move (code, mips_temp, ins->dreg);
4061 mips_sb (code, mips_zero, mips_temp, 0);
4062 mips_addiu (code, mips_at, mips_at, -1);
4063 mips_bne (code, mips_at, mips_zero, -3);
4064 mips_addiu (code, mips_temp, mips_temp, 1);
4066 mips_patch (buf, (guint32)code);
4068 break;
4070 case OP_THROW: {
4071 gpointer addr = mono_arch_get_throw_exception(NULL, FALSE);
4072 mips_move (code, mips_a0, ins->sreg1);
4073 mips_call (code, mips_t9, addr);
4074 mips_break (code, 0xfc);
4075 break;
4077 case OP_RETHROW: {
4078 gpointer addr = mono_arch_get_rethrow_exception(NULL, FALSE);
4079 mips_move (code, mips_a0, ins->sreg1);
4080 mips_call (code, mips_t9, addr);
4081 mips_break (code, 0xfb);
4082 break;
4084 case OP_START_HANDLER: {
4086 * The START_HANDLER instruction marks the beginning of
4087 * a handler block. It is called using a call
4088 * instruction, so mips_ra contains the return address.
4089 * Since the handler executes in the same stack frame
4090 * as the method itself, we can't use save/restore to
4091 * save the return address. Instead, we save it into
4092 * a dedicated variable.
4094 MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
4095 g_assert (spvar->inst_basereg != mips_sp);
4096 code = emit_reserve_param_area (cfg, code);
4098 if (mips_is_imm16 (spvar->inst_offset)) {
4099 mips_sw (code, mips_ra, spvar->inst_basereg, spvar->inst_offset);
4100 } else {
4101 mips_load_const (code, mips_at, spvar->inst_offset);
4102 mips_addu (code, mips_at, mips_at, spvar->inst_basereg);
4103 mips_sw (code, mips_ra, mips_at, 0);
4105 break;
4107 case OP_ENDFILTER: {
4108 MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
4109 g_assert (spvar->inst_basereg != mips_sp);
4110 code = emit_unreserve_param_area (cfg, code);
4112 if (ins->sreg1 != mips_v0)
4113 MIPS_MOVE (code, mips_v0, ins->sreg1);
4114 if (mips_is_imm16 (spvar->inst_offset)) {
4115 mips_lw (code, mips_ra, spvar->inst_basereg, spvar->inst_offset);
4116 } else {
4117 mips_load_const (code, mips_at, spvar->inst_offset);
4118 mips_addu (code, mips_at, mips_at, spvar->inst_basereg);
4119 mips_lw (code, mips_ra, mips_at, 0);
4121 mips_jr (code, mips_ra);
4122 mips_nop (code);
4123 break;
4125 case OP_ENDFINALLY: {
4126 MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
4127 g_assert (spvar->inst_basereg != mips_sp);
4128 code = emit_unreserve_param_area (cfg, code);
4129 mips_lw (code, mips_t9, spvar->inst_basereg, spvar->inst_offset);
4130 mips_jalr (code, mips_t9, mips_ra);
4131 mips_nop (code);
4132 break;
4134 case OP_CALL_HANDLER:
4135 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_target_bb);
4136 mips_lui (code, mips_t9, mips_zero, 0);
4137 mips_addiu (code, mips_t9, mips_t9, 0);
4138 mips_jalr (code, mips_t9, mips_ra);
4139 mips_nop (code);
4140 /*FIXME should it be before the NOP or not? Does MIPS has a delay slot like sparc?*/
4141 mono_cfg_add_try_hole (cfg, ins->inst_eh_block, code, bb);
4142 break;
4143 case OP_LABEL:
4144 ins->inst_c0 = code - cfg->native_code;
4145 break;
4146 case OP_BR:
4147 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_target_bb);
4148 if (cfg->arch.long_branch) {
4149 mips_lui (code, mips_at, mips_zero, 0);
4150 mips_addiu (code, mips_at, mips_at, 0);
4151 mips_jr (code, mips_at);
4152 mips_nop (code);
4154 else {
4155 mips_beq (code, mips_zero, mips_zero, 0);
4156 mips_nop (code);
4158 break;
4159 case OP_BR_REG:
4160 mips_jr (code, ins->sreg1);
4161 mips_nop (code);
4162 break;
4163 case OP_SWITCH: {
4164 int i;
4166 max_len += 4 * GPOINTER_TO_INT (ins->klass);
4167 if (offset > (cfg->code_size - max_len - 16)) {
4168 cfg->code_size += max_len;
4169 cfg->code_size *= 2;
4170 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
4171 code = cfg->native_code + offset;
4173 g_assert (ins->sreg1 != -1);
4174 mips_sll (code, mips_at, ins->sreg1, 2);
4175 if (1 || !(cfg->flags & MONO_CFG_HAS_CALLS))
4176 MIPS_MOVE (code, mips_t8, mips_ra);
4177 mips_bgezal (code, mips_zero, 1); /* bal */
4178 mips_nop (code);
4179 mips_addu (code, mips_t9, mips_ra, mips_at);
4180 /* Table is 16 or 20 bytes from target of bal above */
4181 if (1 || !(cfg->flags & MONO_CFG_HAS_CALLS)) {
4182 MIPS_MOVE (code, mips_ra, mips_t8);
4183 mips_lw (code, mips_t9, mips_t9, 20);
4185 else
4186 mips_lw (code, mips_t9, mips_t9, 16);
4187 mips_jalr (code, mips_t9, mips_t8);
4188 mips_nop (code);
4189 for (i = 0; i < GPOINTER_TO_INT (ins->klass); ++i)
4190 mips_emit32 (code, 0xfefefefe);
4191 break;
4193 case OP_CEQ:
4194 case OP_ICEQ:
4195 mips_addiu (code, ins->dreg, mips_zero, 1);
4196 mips_beq (code, mips_at, mips_zero, 2);
4197 mips_nop (code);
4198 MIPS_MOVE (code, ins->dreg, mips_zero);
4199 break;
4200 case OP_CLT:
4201 case OP_CLT_UN:
4202 case OP_ICLT:
4203 case OP_ICLT_UN:
4204 mips_addiu (code, ins->dreg, mips_zero, 1);
4205 mips_bltz (code, mips_at, 2);
4206 mips_nop (code);
4207 MIPS_MOVE (code, ins->dreg, mips_zero);
4208 break;
4209 case OP_CGT:
4210 case OP_CGT_UN:
4211 case OP_ICGT:
4212 case OP_ICGT_UN:
4213 mips_addiu (code, ins->dreg, mips_zero, 1);
4214 mips_bgtz (code, mips_at, 2);
4215 mips_nop (code);
4216 MIPS_MOVE (code, ins->dreg, mips_zero);
4217 break;
4219 case OP_MIPS_COND_EXC_EQ:
4220 case OP_MIPS_COND_EXC_GE:
4221 case OP_MIPS_COND_EXC_GT:
4222 case OP_MIPS_COND_EXC_LE:
4223 case OP_MIPS_COND_EXC_LT:
4224 case OP_MIPS_COND_EXC_NE_UN:
4225 case OP_MIPS_COND_EXC_GE_UN:
4226 case OP_MIPS_COND_EXC_GT_UN:
4227 case OP_MIPS_COND_EXC_LE_UN:
4228 case OP_MIPS_COND_EXC_LT_UN:
4230 case OP_MIPS_COND_EXC_OV:
4231 case OP_MIPS_COND_EXC_NO:
4232 case OP_MIPS_COND_EXC_C:
4233 case OP_MIPS_COND_EXC_NC:
4235 case OP_MIPS_COND_EXC_IEQ:
4236 case OP_MIPS_COND_EXC_IGE:
4237 case OP_MIPS_COND_EXC_IGT:
4238 case OP_MIPS_COND_EXC_ILE:
4239 case OP_MIPS_COND_EXC_ILT:
4240 case OP_MIPS_COND_EXC_INE_UN:
4241 case OP_MIPS_COND_EXC_IGE_UN:
4242 case OP_MIPS_COND_EXC_IGT_UN:
4243 case OP_MIPS_COND_EXC_ILE_UN:
4244 case OP_MIPS_COND_EXC_ILT_UN:
4246 case OP_MIPS_COND_EXC_IOV:
4247 case OP_MIPS_COND_EXC_INO:
4248 case OP_MIPS_COND_EXC_IC:
4249 case OP_MIPS_COND_EXC_INC: {
4250 guint32 *skip;
4251 guint32 *throw;
4253 /* If the condition is true, raise the exception */
4255 /* need to reverse test to skip around exception raising */
4257 /* For the moment, branch around a branch to avoid reversing
4258 the tests. */
4260 /* Remember, an unpatched branch to 0 branches to the delay slot */
4261 switch (ins->opcode) {
4262 case OP_MIPS_COND_EXC_EQ:
4263 throw = (guint32 *)(void *)code;
4264 mips_beq (code, ins->sreg1, ins->sreg2, 0);
4265 mips_nop (code);
4266 break;
4268 case OP_MIPS_COND_EXC_NE_UN:
4269 throw = (guint32 *)(void *)code;
4270 mips_bne (code, ins->sreg1, ins->sreg2, 0);
4271 mips_nop (code);
4272 break;
4274 case OP_MIPS_COND_EXC_LE_UN:
4275 mips_sltu (code, mips_at, ins->sreg2, ins->sreg1);
4276 throw = (guint32 *)(void *)code;
4277 mips_beq (code, mips_at, mips_zero, 0);
4278 mips_nop (code);
4279 break;
4281 case OP_MIPS_COND_EXC_GT:
4282 mips_slt (code, mips_at, ins->sreg2, ins->sreg1);
4283 throw = (guint32 *)(void *)code;
4284 mips_bne (code, mips_at, mips_zero, 0);
4285 mips_nop (code);
4286 break;
4288 case OP_MIPS_COND_EXC_GT_UN:
4289 mips_sltu (code, mips_at, ins->sreg2, ins->sreg1);
4290 throw = (guint32 *)(void *)code;
4291 mips_bne (code, mips_at, mips_zero, 0);
4292 mips_nop (code);
4293 break;
4295 case OP_MIPS_COND_EXC_LT:
4296 mips_slt (code, mips_at, ins->sreg1, ins->sreg2);
4297 throw = (guint32 *)(void *)code;
4298 mips_bne (code, mips_at, mips_zero, 0);
4299 mips_nop (code);
4300 break;
4302 case OP_MIPS_COND_EXC_LT_UN:
4303 mips_sltu (code, mips_at, ins->sreg1, ins->sreg2);
4304 throw = (guint32 *)(void *)code;
4305 mips_bne (code, mips_at, mips_zero, 0);
4306 mips_nop (code);
4307 break;
4309 default:
4310 /* Not yet implemented */
4311 g_warning ("NYI conditional exception %s\n", mono_inst_name (ins->opcode));
4312 g_assert_not_reached ();
4314 skip = (guint32 *)(void *)code;
4315 mips_beq (code, mips_zero, mips_zero, 0);
4316 mips_nop (code);
4317 mips_patch (throw, (guint32)code);
4318 code = mips_emit_exc_by_name (code, ins->inst_p1);
4319 mips_patch (skip, (guint32)code);
4320 cfg->bb_exit->max_offset += 24;
4321 break;
4323 case OP_MIPS_BEQ:
4324 case OP_MIPS_BNE:
4325 case OP_MIPS_BGEZ:
4326 case OP_MIPS_BGTZ:
4327 case OP_MIPS_BLEZ:
4328 case OP_MIPS_BLTZ:
4329 code = mips_emit_cond_branch (cfg, code, ins->opcode, ins);
4330 break;
4332 /* floating point opcodes */
4333 case OP_R8CONST:
4334 #if 0
4335 if (((guint32)ins->inst_p0) & (1 << 15))
4336 mips_lui (code, mips_at, mips_zero, (((guint32)ins->inst_p0)>>16)+1);
4337 else
4338 mips_lui (code, mips_at, mips_zero, (((guint32)ins->inst_p0)>>16));
4339 mips_ldc1 (code, ins->dreg, mips_at, ((guint32)ins->inst_p0) & 0xffff);
4340 #else
4341 mips_load_const (code, mips_at, ins->inst_p0);
4342 mips_lwc1 (code, ins->dreg, mips_at, ls_word_offset);
4343 mips_lwc1 (code, ins->dreg+1, mips_at, ms_word_offset);
4344 #endif
4345 break;
4346 case OP_R4CONST:
4347 if (((guint32)ins->inst_p0) & (1 << 15))
4348 mips_lui (code, mips_at, mips_zero, (((guint32)ins->inst_p0)>>16)+1);
4349 else
4350 mips_lui (code, mips_at, mips_zero, (((guint32)ins->inst_p0)>>16));
4351 mips_lwc1 (code, ins->dreg, mips_at, ((guint32)ins->inst_p0) & 0xffff);
4352 #if PROMOTE_R4_TO_R8
4353 mips_cvtds (code, ins->dreg, ins->dreg);
4354 #endif
4355 break;
4356 case OP_STORER8_MEMBASE_REG:
4357 if (mips_is_imm16 (ins->inst_offset)) {
4358 #if _MIPS_SIM == _ABIO32
4359 mips_swc1 (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset + ls_word_offset);
4360 mips_swc1 (code, ins->sreg1+1, ins->inst_destbasereg, ins->inst_offset + ms_word_offset);
4361 #elif _MIPS_SIM == _ABIN32
4362 mips_sdc1 (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
4363 #endif
4364 } else {
4365 mips_load_const (code, mips_at, ins->inst_offset);
4366 mips_addu (code, mips_at, mips_at, ins->inst_destbasereg);
4367 mips_swc1 (code, ins->sreg1, mips_at, ls_word_offset);
4368 mips_swc1 (code, ins->sreg1+1, mips_at, ms_word_offset);
4370 break;
4371 case OP_LOADR8_MEMBASE:
4372 if (mips_is_imm16 (ins->inst_offset)) {
4373 #if _MIPS_SIM == _ABIO32
4374 mips_lwc1 (code, ins->dreg, ins->inst_basereg, ins->inst_offset + ls_word_offset);
4375 mips_lwc1 (code, ins->dreg+1, ins->inst_basereg, ins->inst_offset + ms_word_offset);
4376 #elif _MIPS_SIM == _ABIN32
4377 mips_ldc1 (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
4378 #endif
4379 } else {
4380 mips_load_const (code, mips_at, ins->inst_offset);
4381 mips_addu (code, mips_at, mips_at, ins->inst_basereg);
4382 mips_lwc1 (code, ins->dreg, mips_at, ls_word_offset);
4383 mips_lwc1 (code, ins->dreg+1, mips_at, ms_word_offset);
4385 break;
4386 case OP_STORER4_MEMBASE_REG:
4387 g_assert (mips_is_imm16 (ins->inst_offset));
4388 #if PROMOTE_R4_TO_R8
4389 /* Need to convert ins->sreg1 to single-precision first */
4390 mips_cvtsd (code, mips_ftemp, ins->sreg1);
4391 mips_swc1 (code, mips_ftemp, ins->inst_destbasereg, ins->inst_offset);
4392 #else
4393 mips_swc1 (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
4394 #endif
4395 break;
4396 case OP_MIPS_LWC1:
4397 g_assert (mips_is_imm16 (ins->inst_offset));
4398 mips_lwc1 (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
4399 break;
4400 case OP_LOADR4_MEMBASE:
4401 g_assert (mips_is_imm16 (ins->inst_offset));
4402 mips_lwc1 (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
4403 #if PROMOTE_R4_TO_R8
4404 /* Convert to double precision in place */
4405 mips_cvtds (code, ins->dreg, ins->dreg);
4406 #endif
4407 break;
4408 case OP_LOADR4_MEMINDEX:
4409 mips_addu (code, mips_at, ins->inst_basereg, ins->sreg2);
4410 mips_lwc1 (code, ins->dreg, mips_at, 0);
4411 break;
4412 case OP_LOADR8_MEMINDEX:
4413 mips_addu (code, mips_at, ins->inst_basereg, ins->sreg2);
4414 #if _MIPS_SIM == _ABIO32
4415 mips_lwc1 (code, ins->dreg, mips_at, ls_word_offset);
4416 mips_lwc1 (code, ins->dreg+1, mips_at, ms_word_offset);
4417 #elif _MIPS_SIM == _ABIN32
4418 mips_ldc1 (code, ins->dreg, mips_at, 0);
4419 #endif
4420 break;
4421 case OP_STORER4_MEMINDEX:
4422 mips_addu (code, mips_at, ins->inst_destbasereg, ins->sreg2);
4423 #if PROMOTE_R4_TO_R8
4424 /* Need to convert ins->sreg1 to single-precision first */
4425 mips_cvtsd (code, mips_ftemp, ins->sreg1);
4426 mips_swc1 (code, mips_ftemp, mips_at, 0);
4427 #else
4428 mips_swc1 (code, ins->sreg1, mips_at, 0);
4429 #endif
4430 break;
4431 case OP_STORER8_MEMINDEX:
4432 mips_addu (code, mips_at, ins->inst_destbasereg, ins->sreg2);
4433 #if _MIPS_SIM == _ABIO32
4434 mips_swc1 (code, ins->sreg1, mips_at, ls_word_offset);
4435 mips_swc1 (code, ins->sreg1+1, mips_at, ms_word_offset);
4436 #elif _MIPS_SIM == _ABIN32
4437 mips_sdc1 (code, ins->sreg1, mips_at, 0);
4438 #endif
4439 break;
4440 case OP_ICONV_TO_R_UN: {
4441 static const guint64 adjust_val = 0x41F0000000000000ULL;
4443 /* convert unsigned int to double */
4444 mips_mtc1 (code, mips_ftemp, ins->sreg1);
4445 mips_bgez (code, ins->sreg1, 5);
4446 mips_cvtdw (code, ins->dreg, mips_ftemp);
4448 mips_load (code, mips_at, (guint32) &adjust_val);
4449 mips_ldc1 (code, mips_ftemp, mips_at, 0);
4450 mips_faddd (code, ins->dreg, ins->dreg, mips_ftemp);
4451 /* target is here */
4452 break;
4454 case OP_ICONV_TO_R4:
4455 mips_mtc1 (code, mips_ftemp, ins->sreg1);
4456 mips_cvtsw (code, ins->dreg, mips_ftemp);
4457 mips_cvtds (code, ins->dreg, ins->dreg);
4458 break;
4459 case OP_ICONV_TO_R8:
4460 mips_mtc1 (code, mips_ftemp, ins->sreg1);
4461 mips_cvtdw (code, ins->dreg, mips_ftemp);
4462 break;
4463 case OP_FCONV_TO_I1:
4464 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 1, TRUE);
4465 break;
4466 case OP_FCONV_TO_U1:
4467 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 1, FALSE);
4468 break;
4469 case OP_FCONV_TO_I2:
4470 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 2, TRUE);
4471 break;
4472 case OP_FCONV_TO_U2:
4473 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 2, FALSE);
4474 break;
4475 case OP_FCONV_TO_I4:
4476 case OP_FCONV_TO_I:
4477 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, TRUE);
4478 break;
4479 case OP_FCONV_TO_U4:
4480 case OP_FCONV_TO_U:
4481 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, FALSE);
4482 break;
4483 case OP_SQRT:
4484 mips_fsqrtd (code, ins->dreg, ins->sreg1);
4485 break;
4486 case OP_FADD:
4487 mips_faddd (code, ins->dreg, ins->sreg1, ins->sreg2);
4488 break;
4489 case OP_FSUB:
4490 mips_fsubd (code, ins->dreg, ins->sreg1, ins->sreg2);
4491 break;
4492 case OP_FMUL:
4493 mips_fmuld (code, ins->dreg, ins->sreg1, ins->sreg2);
4494 break;
4495 case OP_FDIV:
4496 mips_fdivd (code, ins->dreg, ins->sreg1, ins->sreg2);
4497 break;
4498 case OP_FNEG:
4499 mips_fnegd (code, ins->dreg, ins->sreg1);
4500 break;
4501 case OP_FCEQ:
4502 mips_fcmpd (code, MIPS_FPU_EQ, ins->sreg1, ins->sreg2);
4503 mips_addiu (code, ins->dreg, mips_zero, 1);
4504 mips_fbtrue (code, 2);
4505 mips_nop (code);
4506 MIPS_MOVE (code, ins->dreg, mips_zero);
4507 break;
4508 case OP_FCLT:
4509 mips_fcmpd (code, MIPS_FPU_LT, ins->sreg1, ins->sreg2);
4510 mips_addiu (code, ins->dreg, mips_zero, 1);
4511 mips_fbtrue (code, 2);
4512 mips_nop (code);
4513 MIPS_MOVE (code, ins->dreg, mips_zero);
4514 break;
4515 case OP_FCLT_UN:
4516 /* Less than, or Unordered */
4517 mips_fcmpd (code, MIPS_FPU_ULT, ins->sreg1, ins->sreg2);
4518 mips_addiu (code, ins->dreg, mips_zero, 1);
4519 mips_fbtrue (code, 2);
4520 mips_nop (code);
4521 MIPS_MOVE (code, ins->dreg, mips_zero);
4522 break;
4523 case OP_FCGT:
4524 mips_fcmpd (code, MIPS_FPU_ULE, ins->sreg1, ins->sreg2);
4525 MIPS_MOVE (code, ins->dreg, mips_zero);
4526 mips_fbtrue (code, 2);
4527 mips_nop (code);
4528 mips_addiu (code, ins->dreg, mips_zero, 1);
4529 break;
4530 case OP_FCGT_UN:
4531 /* Greater than, or Unordered */
4532 mips_fcmpd (code, MIPS_FPU_OLE, ins->sreg1, ins->sreg2);
4533 MIPS_MOVE (code, ins->dreg, mips_zero);
4534 mips_fbtrue (code, 2);
4535 mips_nop (code);
4536 mips_addiu (code, ins->dreg, mips_zero, 1);
4537 break;
4538 case OP_MIPS_FBEQ:
4539 case OP_MIPS_FBNE:
4540 case OP_MIPS_FBLT:
4541 case OP_MIPS_FBLT_UN:
4542 case OP_MIPS_FBGT:
4543 case OP_MIPS_FBGT_UN:
4544 case OP_MIPS_FBGE:
4545 case OP_MIPS_FBGE_UN:
4546 case OP_MIPS_FBLE:
4547 case OP_MIPS_FBLE_UN: {
4548 int cond = 0;
4549 gboolean is_true = TRUE, is_ordered = FALSE;
4550 guint32 *buf = NULL;
4552 switch (ins->opcode) {
4553 case OP_MIPS_FBEQ:
4554 cond = MIPS_FPU_EQ;
4555 is_true = TRUE;
4556 break;
4557 case OP_MIPS_FBNE:
4558 cond = MIPS_FPU_EQ;
4559 is_true = FALSE;
4560 break;
4561 case OP_MIPS_FBLT:
4562 cond = MIPS_FPU_LT;
4563 is_true = TRUE;
4564 is_ordered = TRUE;
4565 break;
4566 case OP_MIPS_FBLT_UN:
4567 cond = MIPS_FPU_ULT;
4568 is_true = TRUE;
4569 break;
4570 case OP_MIPS_FBGT:
4571 cond = MIPS_FPU_LE;
4572 is_true = FALSE;
4573 is_ordered = TRUE;
4574 break;
4575 case OP_MIPS_FBGT_UN:
4576 cond = MIPS_FPU_OLE;
4577 is_true = FALSE;
4578 break;
4579 case OP_MIPS_FBGE:
4580 cond = MIPS_FPU_LT;
4581 is_true = FALSE;
4582 is_ordered = TRUE;
4583 break;
4584 case OP_MIPS_FBGE_UN:
4585 cond = MIPS_FPU_OLT;
4586 is_true = FALSE;
4587 break;
4588 case OP_MIPS_FBLE:
4589 cond = MIPS_FPU_OLE;
4590 is_true = TRUE;
4591 is_ordered = TRUE;
4592 break;
4593 case OP_MIPS_FBLE_UN:
4594 cond = MIPS_FPU_ULE;
4595 is_true = TRUE;
4596 break;
4597 default:
4598 g_assert_not_reached ();
4601 if (is_ordered) {
4602 /* Skip the check if unordered */
4603 mips_fcmpd (code, MIPS_FPU_UN, ins->sreg1, ins->sreg2);
4604 mips_nop (code);
4605 buf = (guint32*)code;
4606 mips_fbtrue (code, 0);
4607 mips_nop (code);
4610 mips_fcmpd (code, cond, ins->sreg1, ins->sreg2);
4611 mips_nop (code);
4612 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_true_bb);
4613 if (is_true)
4614 mips_fbtrue (code, 0);
4615 else
4616 mips_fbfalse (code, 0);
4617 mips_nop (code);
4619 if (is_ordered)
4620 mips_patch (buf, (guint32)code);
4621 break;
4623 case OP_CKFINITE: {
4624 guint32 *branch_patch;
4626 mips_mfc1 (code, mips_at, ins->sreg1+1);
4627 mips_srl (code, mips_at, mips_at, 16+4);
4628 mips_andi (code, mips_at, mips_at, 2047);
4629 mips_addiu (code, mips_at, mips_at, -2047);
4631 branch_patch = (guint32 *)(void *)code;
4632 mips_bne (code, mips_at, mips_zero, 0);
4633 mips_nop (code);
4635 EMIT_SYSTEM_EXCEPTION_NAME("OverflowException");
4636 mips_patch (branch_patch, (guint32)code);
4637 mips_fmovd (code, ins->dreg, ins->sreg1);
4638 break;
4640 case OP_JUMP_TABLE:
4641 mono_add_patch_info (cfg, offset, (MonoJumpInfoType)ins->inst_c1, ins->inst_p0);
4642 mips_load (code, ins->dreg, 0x0f0f0f0f);
4643 break;
4644 case OP_GC_SAFE_POINT:
4645 break;
4648 default:
4649 g_warning ("unknown opcode %s in %s()\n", mono_inst_name (ins->opcode), __FUNCTION__);
4650 g_assert_not_reached ();
4653 if ((cfg->opt & MONO_OPT_BRANCH) && ((code - cfg->native_code - offset) > max_len)) {
4654 g_warning ("wrong maximal instruction length of instruction %s (expected %d, got %d)",
4655 mono_inst_name (ins->opcode), max_len, code - cfg->native_code - offset);
4656 g_assert_not_reached ();
4659 cpos += max_len;
4661 last_ins = ins;
4662 last_offset = offset;
4665 cfg->code_len = code - cfg->native_code;
4668 void
4669 mono_arch_register_lowlevel_calls (void)
4673 void
4674 mono_arch_patch_code (MonoCompile *cfg, MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji, gboolean run_cctors, MonoError *error)
4676 MonoJumpInfo *patch_info;
4678 mono_error_init (error);
4680 for (patch_info = ji; patch_info; patch_info = patch_info->next) {
4681 unsigned char *ip = patch_info->ip.i + code;
4682 const unsigned char *target = NULL;
4684 switch (patch_info->type) {
4685 case MONO_PATCH_INFO_IP:
4686 patch_lui_addiu ((guint32 *)(void *)ip, (guint32)ip);
4687 continue;
4688 case MONO_PATCH_INFO_SWITCH: {
4689 gpointer *table = (gpointer *)patch_info->data.table->table;
4690 int i;
4692 patch_lui_addiu ((guint32 *)(void *)ip, (guint32)table);
4694 for (i = 0; i < patch_info->data.table->table_size; i++) {
4695 table [i] = (int)patch_info->data.table->table [i] + code;
4697 continue;
4699 case MONO_PATCH_INFO_METHODCONST:
4700 case MONO_PATCH_INFO_CLASS:
4701 case MONO_PATCH_INFO_IMAGE:
4702 case MONO_PATCH_INFO_FIELD:
4703 case MONO_PATCH_INFO_VTABLE:
4704 case MONO_PATCH_INFO_IID:
4705 case MONO_PATCH_INFO_SFLDA:
4706 case MONO_PATCH_INFO_LDSTR:
4707 case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
4708 case MONO_PATCH_INFO_LDTOKEN:
4709 case MONO_PATCH_INFO_R4:
4710 case MONO_PATCH_INFO_R8:
4711 /* from OP_AOTCONST : lui + addiu */
4712 target = mono_resolve_patch_target (method, domain, code, patch_info, run_cctors, error);
4713 return_if_nok (error);
4715 patch_lui_addiu ((guint32 *)(void *)ip, (guint32)target);
4716 continue;
4717 #if 0
4718 case MONO_PATCH_INFO_EXC_NAME:
4719 g_assert_not_reached ();
4720 *((gconstpointer *)(void *)(ip + 1)) = patch_info->data.name;
4721 continue;
4722 #endif
4723 case MONO_PATCH_INFO_NONE:
4724 /* everything is dealt with at epilog output time */
4725 continue;
4726 default:
4727 target = mono_resolve_patch_target (method, domain, code, patch_info, run_cctors, error);
4728 return_if_nok (error);
4730 mips_patch ((guint32 *)(void *)ip, (guint32)target);
4731 break;
4737 * Allow tracing to work with this interface (with an optional argument)
4739 * This code is expected to be inserted just after the 'real' prolog code,
4740 * and before the first basic block. We need to allocate a 2nd, temporary
4741 * stack frame so that we can preserve f12-f15 as well as a0-a3.
4744 void*
4745 mono_arch_instrument_prolog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments)
4747 guchar *code = p;
4748 int offset = cfg->arch.tracing_offset;
4750 mips_nop (code);
4751 mips_nop (code);
4752 mips_nop (code);
4754 MIPS_SW (code, mips_a0, mips_sp, offset + 0*SIZEOF_REGISTER);
4755 MIPS_SW (code, mips_a1, mips_sp, offset + 1*SIZEOF_REGISTER);
4756 MIPS_SW (code, mips_a2, mips_sp, offset + 2*SIZEOF_REGISTER);
4757 MIPS_SW (code, mips_a3, mips_sp, offset + 3*SIZEOF_REGISTER);
4758 #if _MIPS_SIM == _ABIN32
4759 NOT_IMPLEMENTED;
4760 /* FIXME: Need a separate region for these */
4761 MIPS_SW (code, mips_a4, mips_sp, offset + 4*SIZEOF_REGISTER);
4762 MIPS_SW (code, mips_a5, mips_sp, offset + 5*SIZEOF_REGISTER);
4763 MIPS_SW (code, mips_a6, mips_sp, offset + 6*SIZEOF_REGISTER);
4764 MIPS_SW (code, mips_a7, mips_sp, offset + 7*SIZEOF_REGISTER);
4766 #endif
4768 mips_load_const (code, mips_a0, cfg->method);
4769 mips_addiu (code, mips_a1, mips_sp, offset);
4770 mips_call (code, mips_t9, func);
4771 mips_nop (code);
4773 MIPS_LW (code, mips_a0, mips_sp, offset + 0*SIZEOF_REGISTER);
4774 MIPS_LW (code, mips_a1, mips_sp, offset + 1*SIZEOF_REGISTER);
4775 MIPS_LW (code, mips_a2, mips_sp, offset + 2*SIZEOF_REGISTER);
4776 MIPS_LW (code, mips_a3, mips_sp, offset + 3*SIZEOF_REGISTER);
4777 #if _MIPS_SIM == _ABIN32
4778 NOT_IMPLEMENTED;
4780 MIPS_LW (code, mips_a4, mips_sp, offset + 4*SIZEOF_REGISTER);
4781 MIPS_LW (code, mips_a5, mips_sp, offset + 5*SIZEOF_REGISTER);
4782 MIPS_LW (code, mips_a6, mips_sp, offset + 6*SIZEOF_REGISTER);
4783 MIPS_LW (code, mips_a7, mips_sp, offset + 7*SIZEOF_REGISTER);
4785 #endif
4787 mips_nop (code);
4788 mips_nop (code);
4789 mips_nop (code);
4790 return code;
4793 void
4794 mips_adjust_stackframe(MonoCompile *cfg)
4796 MonoBasicBlock *bb;
4797 int delta, threshold, i;
4798 MonoMethodSignature *sig;
4799 int ra_offset;
4801 if (cfg->stack_offset == cfg->arch.local_alloc_offset)
4802 return;
4804 /* adjust cfg->stack_offset for account for down-spilling */
4805 cfg->stack_offset += SIZEOF_REGISTER;
4807 /* re-align cfg->stack_offset if needed (due to var spilling) */
4808 cfg->stack_offset = (cfg->stack_offset + MIPS_STACK_ALIGNMENT - 1) & ~(MIPS_STACK_ALIGNMENT - 1);
4809 delta = cfg->stack_offset - cfg->arch.local_alloc_offset;
4810 if (cfg->verbose_level > 2) {
4811 g_print ("mips_adjust_stackframe:\n");
4812 g_print ("\tspillvars allocated 0x%x -> 0x%x\n", cfg->arch.local_alloc_offset, cfg->stack_offset);
4814 threshold = cfg->arch.local_alloc_offset;
4815 ra_offset = cfg->stack_offset - sizeof(gpointer);
4816 if (cfg->verbose_level > 2) {
4817 g_print ("\tra_offset %d/0x%x delta %d/0x%x\n", ra_offset, ra_offset, delta, delta);
4820 sig = mono_method_signature (cfg->method);
4821 if (sig && sig->ret && MONO_TYPE_ISSTRUCT (sig->ret)) {
4822 cfg->vret_addr->inst_offset += delta;
4824 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
4825 MonoInst *inst = cfg->args [i];
4827 inst->inst_offset += delta;
4831 * loads and stores based off the frame reg that (used to) lie
4832 * above the spill var area need to be increased by 'delta'
4833 * to make room for the spill vars.
4835 /* Need to find loads and stores to adjust that
4836 * are above where the spillvars were inserted, but
4837 * which are not the spillvar references themselves.
4839 * Idea - since all offsets from fp are positive, make
4840 * spillvar offsets negative to begin with so we can spot
4841 * them here.
4844 #if 1
4845 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
4846 int ins_cnt = 0;
4847 MonoInst *ins;
4849 if (cfg->verbose_level > 2) {
4850 g_print ("BASIC BLOCK %d:\n", bb->block_num);
4852 MONO_BB_FOR_EACH_INS (bb, ins) {
4853 int adj_c0 = 0;
4854 int adj_imm = 0;
4856 if (cfg->verbose_level > 2) {
4857 mono_print_ins_index (ins_cnt, ins);
4859 /* The == mips_sp tests catch FP spills */
4860 if (MONO_IS_LOAD_MEMBASE(ins) && ((ins->inst_basereg == mips_fp) ||
4861 (ins->inst_basereg == mips_sp))) {
4862 switch (ins->opcode) {
4863 case OP_LOADI8_MEMBASE:
4864 case OP_LOADR8_MEMBASE:
4865 adj_c0 = 8;
4866 break;
4867 default:
4868 adj_c0 = 4;
4869 break;
4871 } else if (MONO_IS_STORE_MEMBASE(ins) && ((ins->dreg == mips_fp) ||
4872 (ins->dreg == mips_sp))) {
4873 switch (ins->opcode) {
4874 case OP_STOREI8_MEMBASE_REG:
4875 case OP_STORER8_MEMBASE_REG:
4876 case OP_STOREI8_MEMBASE_IMM:
4877 adj_c0 = 8;
4878 break;
4879 default:
4880 adj_c0 = 4;
4881 break;
4884 if (((ins->opcode == OP_ADD_IMM) || (ins->opcode == OP_IADD_IMM)) && (ins->sreg1 == cfg->frame_reg))
4885 adj_imm = 1;
4886 if (adj_c0) {
4887 if (ins->inst_c0 >= threshold) {
4888 ins->inst_c0 += delta;
4889 if (cfg->verbose_level > 2) {
4890 g_print ("adj");
4891 mono_print_ins_index (ins_cnt, ins);
4894 else if (ins->inst_c0 < 0) {
4895 /* Adj_c0 holds the size of the datatype. */
4896 ins->inst_c0 = - ins->inst_c0 - adj_c0;
4897 if (cfg->verbose_level > 2) {
4898 g_print ("spill");
4899 mono_print_ins_index (ins_cnt, ins);
4902 g_assert (ins->inst_c0 != ra_offset);
4904 if (adj_imm) {
4905 if (ins->inst_imm >= threshold) {
4906 ins->inst_imm += delta;
4907 if (cfg->verbose_level > 2) {
4908 g_print ("adj");
4909 mono_print_ins_index (ins_cnt, ins);
4912 g_assert (ins->inst_c0 != ra_offset);
4915 ++ins_cnt;
4918 #endif
4922 * Stack frame layout:
4924 * ------------------- sp + cfg->stack_usage + cfg->param_area
4925 * param area incoming
4926 * ------------------- sp + cfg->stack_usage + MIPS_STACK_PARAM_OFFSET
4927 * a0-a3 incoming
4928 * ------------------- sp + cfg->stack_usage
4929 * ra
4930 * ------------------- sp + cfg->stack_usage-4
4931 * spilled regs
4932 * ------------------- sp +
4933 * MonoLMF structure optional
4934 * ------------------- sp + cfg->arch.lmf_offset
4935 * saved registers s0-s8
4936 * ------------------- sp + cfg->arch.iregs_offset
4937 * locals
4938 * ------------------- sp + cfg->param_area
4939 * param area outgoing
4940 * ------------------- sp + MIPS_STACK_PARAM_OFFSET
4941 * a0-a3 outgoing
4942 * ------------------- sp
4943 * red zone
4945 guint8 *
4946 mono_arch_emit_prolog (MonoCompile *cfg)
4948 MonoMethod *method = cfg->method;
4949 MonoMethodSignature *sig;
4950 MonoInst *inst;
4951 int alloc_size, pos, i, max_offset;
4952 int alloc2_size = 0;
4953 guint8 *code;
4954 CallInfo *cinfo;
4955 int tracing = 0;
4956 guint32 iregs_to_save = 0;
4957 #if SAVE_FP_REGS
4958 guint32 fregs_to_save = 0;
4959 #endif
4960 /* lmf_offset is the offset of the LMF from our stack pointer. */
4961 guint32 lmf_offset = cfg->arch.lmf_offset;
4962 int cfa_offset = 0;
4963 MonoBasicBlock *bb;
4965 if (mono_jit_trace_calls != NULL && mono_trace_eval (method))
4966 tracing = 1;
4968 if (tracing)
4969 cfg->flags |= MONO_CFG_HAS_CALLS;
4971 sig = mono_method_signature (method);
4972 cfg->code_size = 768 + sig->param_count * 20;
4973 code = cfg->native_code = g_malloc (cfg->code_size);
4976 * compute max_offset in order to use short forward jumps.
4978 max_offset = 0;
4979 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
4980 MonoInst *ins = bb->code;
4981 bb->max_offset = max_offset;
4983 if (cfg->prof_options & MONO_PROFILE_COVERAGE)
4984 max_offset += 6;
4986 MONO_BB_FOR_EACH_INS (bb, ins)
4987 max_offset += ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
4989 if (max_offset > 0xffff)
4990 cfg->arch.long_branch = TRUE;
4993 * Currently, fp points to the bottom of the frame on MIPS, unlike other platforms.
4994 * This means that we have to adjust the offsets inside instructions which reference
4995 * arguments received on the stack, since the initial offset doesn't take into
4996 * account spill slots.
4998 mips_adjust_stackframe (cfg);
5000 /* Offset between current sp and the CFA */
5001 cfa_offset = 0;
5002 mono_emit_unwind_op_def_cfa (cfg, code, mips_sp, cfa_offset);
5004 /* stack_offset should not be changed here. */
5005 alloc_size = cfg->stack_offset;
5006 cfg->stack_usage = alloc_size;
5008 iregs_to_save = (cfg->used_int_regs & MONO_ARCH_CALLEE_SAVED_REGS);
5009 #if SAVE_FP_REGS
5010 #if 0
5011 fregs_to_save = (cfg->used_float_regs & MONO_ARCH_CALLEE_SAVED_FREGS);
5012 #else
5013 fregs_to_save = MONO_ARCH_CALLEE_SAVED_FREGS;
5014 fregs_to_save |= (fregs_to_save << 1);
5015 #endif
5016 #endif
5017 /* If the stack size is too big, save 1024 bytes to start with
5018 * so the prologue can use imm16(reg) addressing, then allocate
5019 * the rest of the frame.
5021 if (alloc_size > ((1 << 15) - 1024)) {
5022 alloc2_size = alloc_size - 1024;
5023 alloc_size = 1024;
5025 if (alloc_size) {
5026 g_assert (mips_is_imm16 (-alloc_size));
5027 mips_addiu (code, mips_sp, mips_sp, -alloc_size);
5028 cfa_offset = alloc_size;
5029 mono_emit_unwind_op_def_cfa_offset (cfg, code, cfa_offset);
5032 if ((cfg->flags & MONO_CFG_HAS_CALLS) || ALWAYS_SAVE_RA) {
5033 int offset = alloc_size + MIPS_RET_ADDR_OFFSET;
5034 if (mips_is_imm16(offset))
5035 mips_sw (code, mips_ra, mips_sp, offset);
5036 else {
5037 g_assert_not_reached ();
5039 /* sp = cfa - cfa_offset, so sp + offset = cfa - cfa_offset + offset */
5040 mono_emit_unwind_op_offset (cfg, code, mips_ra, offset - cfa_offset);
5043 /* XXX - optimize this later to not save all regs if LMF constructed */
5044 pos = cfg->arch.iregs_offset - alloc2_size;
5046 if (iregs_to_save) {
5047 /* save used registers in own stack frame (at pos) */
5048 for (i = MONO_MAX_IREGS-1; i >= 0; --i) {
5049 if (iregs_to_save & (1 << i)) {
5050 g_assert (pos < (int)(cfg->stack_usage - sizeof(gpointer)));
5051 g_assert (mips_is_imm16(pos));
5052 MIPS_SW (code, i, mips_sp, pos);
5053 mono_emit_unwind_op_offset (cfg, code, i, pos - cfa_offset);
5054 pos += SIZEOF_REGISTER;
5059 // FIXME: Don't save registers twice if there is an LMF
5060 // s8 has to be special cased since it is overwritten with the updated value
5061 // below
5062 if (method->save_lmf) {
5063 for (i = MONO_MAX_IREGS-1; i >= 0; --i) {
5064 int offset = lmf_offset + G_STRUCT_OFFSET(MonoLMF, iregs[i]);
5065 g_assert (mips_is_imm16(offset));
5066 if (MIPS_LMF_IREGMASK & (1 << i))
5067 MIPS_SW (code, i, mips_sp, offset);
5071 #if SAVE_FP_REGS
5072 /* Save float registers */
5073 if (fregs_to_save) {
5074 for (i = MONO_MAX_FREGS-1; i >= 0; --i) {
5075 if (fregs_to_save & (1 << i)) {
5076 g_assert (pos < cfg->stack_usage - MIPS_STACK_ALIGNMENT);
5077 g_assert (mips_is_imm16(pos));
5078 mips_swc1 (code, i, mips_sp, pos);
5079 pos += sizeof (gulong);
5084 if (method->save_lmf) {
5085 for (i = MONO_MAX_FREGS-1; i >= 0; --i) {
5086 int offset = lmf_offset + G_STRUCT_OFFSET(MonoLMF, fregs[i]);
5087 g_assert (mips_is_imm16(offset));
5088 mips_swc1 (code, i, mips_sp, offset);
5092 #endif
5093 if (cfg->frame_reg != mips_sp) {
5094 MIPS_MOVE (code, cfg->frame_reg, mips_sp);
5095 mono_emit_unwind_op_def_cfa (cfg, code, cfg->frame_reg, cfa_offset);
5097 if (method->save_lmf) {
5098 int offset = lmf_offset + G_STRUCT_OFFSET(MonoLMF, iregs[cfg->frame_reg]);
5099 g_assert (mips_is_imm16(offset));
5100 MIPS_SW (code, cfg->frame_reg, mips_sp, offset);
5104 /* store runtime generic context */
5105 if (cfg->rgctx_var) {
5106 MonoInst *ins = cfg->rgctx_var;
5108 g_assert (ins->opcode == OP_REGOFFSET);
5110 g_assert (mips_is_imm16 (ins->inst_offset));
5111 mips_sw (code, MONO_ARCH_RGCTX_REG, ins->inst_basereg, ins->inst_offset);
5114 /* load arguments allocated to register from the stack */
5115 pos = 0;
5117 if (!cfg->arch.cinfo)
5118 cfg->arch.cinfo = get_call_info (cfg->mempool, sig);
5119 cinfo = cfg->arch.cinfo;
5121 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
5122 ArgInfo *ainfo = &cinfo->ret;
5123 inst = cfg->vret_addr;
5124 if (inst->opcode == OP_REGVAR)
5125 MIPS_MOVE (code, inst->dreg, ainfo->reg);
5126 else if (mips_is_imm16 (inst->inst_offset)) {
5127 mips_sw (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
5128 } else {
5129 mips_load_const (code, mips_at, inst->inst_offset);
5130 mips_addu (code, mips_at, mips_at, inst->inst_basereg);
5131 mips_sw (code, ainfo->reg, mips_at, 0);
5135 if (sig->call_convention == MONO_CALL_VARARG) {
5136 ArgInfo *cookie = &cinfo->sig_cookie;
5137 int offset = alloc_size + cookie->offset;
5139 /* Save the sig cookie address */
5140 g_assert (cookie->storage == ArgOnStack);
5142 g_assert (mips_is_imm16(offset));
5143 mips_addi (code, mips_at, cfg->frame_reg, offset);
5144 mips_sw (code, mips_at, cfg->frame_reg, cfg->sig_cookie - alloc2_size);
5147 /* Keep this in sync with emit_load_volatile_arguments */
5148 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
5149 ArgInfo *ainfo = cinfo->args + i;
5150 inst = cfg->args [pos];
5152 if (cfg->verbose_level > 2)
5153 g_print ("Saving argument %d (type: %d)\n", i, ainfo->storage);
5154 if (inst->opcode == OP_REGVAR) {
5155 /* Argument ends up in a register */
5156 if (ainfo->storage == ArgInIReg)
5157 MIPS_MOVE (code, inst->dreg, ainfo->reg);
5158 else if (ainfo->storage == ArgInFReg) {
5159 g_assert_not_reached();
5160 #if 0
5161 ppc_fmr (code, inst->dreg, ainfo->reg);
5162 #endif
5164 else if (ainfo->storage == ArgOnStack) {
5165 int offset = cfg->stack_usage + ainfo->offset;
5166 g_assert (mips_is_imm16(offset));
5167 mips_lw (code, inst->dreg, mips_sp, offset);
5168 } else
5169 g_assert_not_reached ();
5171 if (cfg->verbose_level > 2)
5172 g_print ("Argument %d assigned to register %s\n", pos, mono_arch_regname (inst->dreg));
5173 } else {
5174 /* Argument ends up on the stack */
5175 if (ainfo->storage == ArgInIReg) {
5176 int basereg_offset;
5177 /* Incoming parameters should be above this frame */
5178 if (cfg->verbose_level > 2)
5179 g_print ("stack slot at %d of %d+%d\n",
5180 inst->inst_offset, alloc_size, alloc2_size);
5181 /* g_assert (inst->inst_offset >= alloc_size); */
5182 g_assert (inst->inst_basereg == cfg->frame_reg);
5183 basereg_offset = inst->inst_offset - alloc2_size;
5184 g_assert (mips_is_imm16 (basereg_offset));
5185 switch (ainfo->size) {
5186 case 1:
5187 mips_sb (code, ainfo->reg, inst->inst_basereg, basereg_offset);
5188 break;
5189 case 2:
5190 mips_sh (code, ainfo->reg, inst->inst_basereg, basereg_offset);
5191 break;
5192 case 0: /* XXX */
5193 case 4:
5194 mips_sw (code, ainfo->reg, inst->inst_basereg, basereg_offset);
5195 break;
5196 case 8:
5197 #if (SIZEOF_REGISTER == 4)
5198 mips_sw (code, ainfo->reg, inst->inst_basereg, basereg_offset + ls_word_offset);
5199 mips_sw (code, ainfo->reg + 1, inst->inst_basereg, basereg_offset + ms_word_offset);
5200 #elif (SIZEOF_REGISTER == 8)
5201 mips_sd (code, ainfo->reg, inst->inst_basereg, basereg_offset);
5202 #endif
5203 break;
5204 default:
5205 g_assert_not_reached ();
5206 break;
5208 } else if (ainfo->storage == ArgOnStack) {
5210 * Argument comes in on the stack, and ends up on the stack
5211 * 1 and 2 byte args are passed as 32-bit quantities, but used as
5212 * 8 and 16 bit quantities. Shorten them in place.
5214 g_assert (mips_is_imm16 (inst->inst_offset));
5215 switch (ainfo->size) {
5216 case 1:
5217 mips_lw (code, mips_at, inst->inst_basereg, inst->inst_offset);
5218 mips_sb (code, mips_at, inst->inst_basereg, inst->inst_offset);
5219 break;
5220 case 2:
5221 mips_lw (code, mips_at, inst->inst_basereg, inst->inst_offset);
5222 mips_sh (code, mips_at, inst->inst_basereg, inst->inst_offset);
5223 break;
5224 case 0: /* XXX */
5225 case 4:
5226 case 8:
5227 break;
5228 default:
5229 g_assert_not_reached ();
5231 } else if (ainfo->storage == ArgInFReg) {
5232 g_assert (mips_is_imm16 (inst->inst_offset));
5233 g_assert (mips_is_imm16 (inst->inst_offset+4));
5234 if (ainfo->size == 8) {
5235 #if _MIPS_SIM == _ABIO32
5236 mips_swc1 (code, ainfo->reg, inst->inst_basereg, inst->inst_offset + ls_word_offset);
5237 mips_swc1 (code, ainfo->reg+1, inst->inst_basereg, inst->inst_offset + ms_word_offset);
5238 #elif _MIPS_SIM == _ABIN32
5239 mips_sdc1 (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
5240 #endif
5242 else if (ainfo->size == 4)
5243 mips_swc1 (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
5244 else
5245 g_assert_not_reached ();
5246 } else if (ainfo->storage == ArgStructByVal) {
5247 int i;
5248 int doffset = inst->inst_offset;
5250 g_assert (mips_is_imm16 (inst->inst_offset));
5251 g_assert (mips_is_imm16 (inst->inst_offset + ainfo->size * sizeof (gpointer)));
5252 /* Push the argument registers into their stack slots */
5253 for (i = 0; i < ainfo->size; ++i) {
5254 g_assert (mips_is_imm16(doffset));
5255 MIPS_SW (code, ainfo->reg + i, inst->inst_basereg, doffset);
5256 doffset += SIZEOF_REGISTER;
5258 } else if (ainfo->storage == ArgStructByAddr) {
5259 g_assert (mips_is_imm16 (inst->inst_offset));
5260 /* FIXME: handle overrun! with struct sizes not multiple of 4 */
5261 code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, inst->inst_offset, ainfo->reg, 0);
5262 } else
5263 g_assert_not_reached ();
5265 pos++;
5268 if (method->save_lmf) {
5269 mips_load_const (code, mips_at, MIPS_LMF_MAGIC1);
5270 mips_sw (code, mips_at, mips_sp, lmf_offset + G_STRUCT_OFFSET(MonoLMF, magic));
5272 if (lmf_pthread_key != -1) {
5273 g_assert_not_reached();
5274 #if 0
5275 emit_tls_access (code, mips_temp, lmf_pthread_key);
5276 #endif
5277 if (G_STRUCT_OFFSET (MonoJitTlsData, lmf)) {
5278 int offset = G_STRUCT_OFFSET (MonoJitTlsData, lmf);
5279 g_assert (mips_is_imm16(offset));
5280 mips_addiu (code, mips_a0, mips_temp, offset);
5282 } else {
5283 /* This can/will clobber the a0-a3 registers */
5284 mips_call (code, mips_t9, (gpointer)mono_get_lmf_addr);
5287 /* mips_v0 is the result from mono_get_lmf_addr () (MonoLMF **) */
5288 g_assert (mips_is_imm16(lmf_offset + G_STRUCT_OFFSET(MonoLMF, lmf_addr)));
5289 mips_sw (code, mips_v0, mips_sp, lmf_offset + G_STRUCT_OFFSET(MonoLMF, lmf_addr));
5290 /* new_lmf->previous_lmf = *lmf_addr */
5291 mips_lw (code, mips_at, mips_v0, 0);
5292 g_assert (mips_is_imm16(lmf_offset + G_STRUCT_OFFSET(MonoLMF, previous_lmf)));
5293 mips_sw (code, mips_at, mips_sp, lmf_offset + G_STRUCT_OFFSET(MonoLMF, previous_lmf));
5294 /* *(lmf_addr) = sp + lmf_offset */
5295 g_assert (mips_is_imm16(lmf_offset));
5296 mips_addiu (code, mips_at, mips_sp, lmf_offset);
5297 mips_sw (code, mips_at, mips_v0, 0);
5299 /* save method info */
5300 mips_load_const (code, mips_at, method);
5301 g_assert (mips_is_imm16(lmf_offset + G_STRUCT_OFFSET(MonoLMF, method)));
5302 mips_sw (code, mips_at, mips_sp, lmf_offset + G_STRUCT_OFFSET(MonoLMF, method));
5304 /* save the current IP */
5305 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_IP, NULL);
5306 mips_load_const (code, mips_at, 0x01010101);
5307 mips_sw (code, mips_at, mips_sp, lmf_offset + G_STRUCT_OFFSET(MonoLMF, eip));
5310 if (alloc2_size) {
5311 if (mips_is_imm16 (-alloc2_size)) {
5312 mips_addu (code, mips_sp, mips_sp, -alloc2_size);
5314 else {
5315 mips_load_const (code, mips_at, -alloc2_size);
5316 mips_addu (code, mips_sp, mips_sp, mips_at);
5318 alloc_size += alloc2_size;
5319 cfa_offset += alloc2_size;
5320 if (cfg->frame_reg != mips_sp)
5321 MIPS_MOVE (code, cfg->frame_reg, mips_sp);
5322 else
5323 mono_emit_unwind_op_def_cfa_offset (cfg, code, cfa_offset);
5326 if (tracing) {
5327 #if _MIPS_SIM == _ABIO32
5328 cfg->arch.tracing_offset = cfg->stack_offset;
5329 #elif _MIPS_SIM == _ABIN32
5330 /* no stack slots by default for argument regs, reserve a special block */
5331 g_assert_not_reached ();
5332 #endif
5333 code = mono_arch_instrument_prolog (cfg, mono_trace_enter_method, code, TRUE);
5336 cfg->code_len = code - cfg->native_code;
5337 g_assert (cfg->code_len < cfg->code_size);
5339 return code;
5342 enum {
5343 SAVE_NONE,
5344 SAVE_STRUCT,
5345 SAVE_ONE,
5346 SAVE_TWO,
5347 SAVE_FP
5350 void*
5351 mono_arch_instrument_epilog_full (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments, gboolean preserve_argument_registers)
5353 guchar *code = p;
5354 int save_mode = SAVE_NONE;
5355 int offset;
5356 MonoMethod *method = cfg->method;
5357 int rtype = mini_get_underlying_type (mono_method_signature (method)->ret)->type;
5358 int save_offset = MIPS_STACK_PARAM_OFFSET;
5360 g_assert ((save_offset & (MIPS_STACK_ALIGNMENT-1)) == 0);
5362 offset = code - cfg->native_code;
5363 /* we need about 16 instructions */
5364 if (offset > (cfg->code_size - 16 * 4)) {
5365 cfg->code_size *= 2;
5366 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
5367 code = cfg->native_code + offset;
5369 mips_nop (code);
5370 mips_nop (code);
5371 switch (rtype) {
5372 case MONO_TYPE_VOID:
5373 /* special case string .ctor icall */
5374 if (strcmp (".ctor", method->name) && method->klass == mono_defaults.string_class)
5375 save_mode = SAVE_ONE;
5376 else
5377 save_mode = SAVE_NONE;
5378 break;
5379 case MONO_TYPE_R4:
5380 case MONO_TYPE_R8:
5381 save_mode = SAVE_FP;
5382 break;
5383 case MONO_TYPE_VALUETYPE:
5384 save_mode = SAVE_STRUCT;
5385 break;
5386 case MONO_TYPE_I8:
5387 case MONO_TYPE_U8:
5388 #if SIZEOF_REGISTER == 4
5389 save_mode = SAVE_TWO;
5390 #elif SIZEOF_REGISTER == 8
5391 save_mode = SAVE_ONE;
5392 #endif
5393 break;
5394 default:
5395 save_mode = SAVE_ONE;
5396 break;
5399 mips_addiu (code, mips_sp, mips_sp, -32);
5400 g_assert (mips_is_imm16(save_offset));
5401 switch (save_mode) {
5402 case SAVE_TWO:
5403 mips_sw (code, mips_v0, mips_sp, save_offset);
5404 g_assert (mips_is_imm16(save_offset + SIZEOF_REGISTER));
5405 mips_sw (code, mips_v1, mips_sp, save_offset + SIZEOF_REGISTER);
5406 if (enable_arguments) {
5407 MIPS_MOVE (code, mips_a1, mips_v0);
5408 MIPS_MOVE (code, mips_a2, mips_v1);
5410 break;
5411 case SAVE_ONE:
5412 MIPS_SW (code, mips_v0, mips_sp, save_offset);
5413 if (enable_arguments) {
5414 MIPS_MOVE (code, mips_a1, mips_v0);
5416 break;
5417 case SAVE_FP:
5418 mips_sdc1 (code, mips_f0, mips_sp, save_offset);
5419 mips_ldc1 (code, mips_f12, mips_sp, save_offset);
5420 mips_lw (code, mips_a0, mips_sp, save_offset);
5421 g_assert (mips_is_imm16(save_offset + SIZEOF_REGISTER));
5422 mips_lw (code, mips_a1, mips_sp, save_offset + SIZEOF_REGISTER);
5423 break;
5424 case SAVE_STRUCT:
5425 case SAVE_NONE:
5426 default:
5427 break;
5429 mips_load_const (code, mips_a0, cfg->method);
5430 mips_call (code, mips_t9, func);
5432 switch (save_mode) {
5433 case SAVE_TWO:
5434 mips_lw (code, mips_v0, mips_sp, save_offset);
5435 g_assert (mips_is_imm16(save_offset + SIZEOF_REGISTER));
5436 mips_lw (code, mips_v1, mips_sp, save_offset + SIZEOF_REGISTER);
5437 break;
5438 case SAVE_ONE:
5439 MIPS_LW (code, mips_v0, mips_sp, save_offset);
5440 break;
5441 case SAVE_FP:
5442 mips_ldc1 (code, mips_f0, mips_sp, save_offset);
5443 break;
5444 case SAVE_STRUCT:
5445 case SAVE_NONE:
5446 default:
5447 break;
5449 mips_addiu (code, mips_sp, mips_sp, 32);
5450 mips_nop (code);
5451 mips_nop (code);
5452 return code;
5455 guint8 *
5456 mono_arch_emit_epilog_sub (MonoCompile *cfg, guint8 *code)
5458 MonoMethod *method = cfg->method;
5459 int pos = 0, i;
5460 int max_epilog_size = 16 + 20*4;
5461 int alloc2_size = 0;
5462 guint32 iregs_to_restore;
5463 #if SAVE_FP_REGS
5464 guint32 fregs_to_restore;
5465 #endif
5467 if (cfg->method->save_lmf)
5468 max_epilog_size += 128;
5470 if (mono_jit_trace_calls != NULL)
5471 max_epilog_size += 50;
5473 if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE)
5474 max_epilog_size += 50;
5476 if (code)
5477 pos = code - cfg->native_code;
5478 while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) {
5479 cfg->code_size *= 2;
5480 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
5481 cfg->stat_code_reallocs++;
5485 * Keep in sync with OP_JMP
5487 if (code)
5488 code = cfg->native_code + pos;
5489 else
5490 code = cfg->native_code + cfg->code_len;
5492 if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) {
5493 code = mono_arch_instrument_epilog (cfg, mono_trace_leave_method, code, TRUE);
5495 if (cfg->frame_reg != mips_sp) {
5496 MIPS_MOVE (code, mips_sp, cfg->frame_reg);
5498 /* If the stack frame is really large, deconstruct it in two steps */
5499 if (cfg->stack_usage > ((1 << 15) - 1024)) {
5500 alloc2_size = cfg->stack_usage - 1024;
5501 /* partially deconstruct the stack */
5502 mips_load_const (code, mips_at, alloc2_size);
5503 mips_addu (code, mips_sp, mips_sp, mips_at);
5505 pos = cfg->arch.iregs_offset - alloc2_size;
5506 iregs_to_restore = (cfg->used_int_regs & MONO_ARCH_CALLEE_SAVED_REGS);
5507 if (iregs_to_restore) {
5508 for (i = MONO_MAX_IREGS-1; i >= 0; --i) {
5509 if (iregs_to_restore & (1 << i)) {
5510 g_assert (mips_is_imm16(pos));
5511 MIPS_LW (code, i, mips_sp, pos);
5512 pos += SIZEOF_REGISTER;
5517 #if SAVE_FP_REGS
5518 #if 0
5519 fregs_to_restore = (cfg->used_float_regs & MONO_ARCH_CALLEE_SAVED_FREGS);
5520 #else
5521 fregs_to_restore = MONO_ARCH_CALLEE_SAVED_FREGS;
5522 fregs_to_restore |= (fregs_to_restore << 1);
5523 #endif
5524 if (fregs_to_restore) {
5525 for (i = MONO_MAX_FREGS-1; i >= 0; --i) {
5526 if (fregs_to_restore & (1 << i)) {
5527 g_assert (pos < cfg->stack_usage - MIPS_STACK_ALIGNMENT);
5528 g_assert (mips_is_imm16(pos));
5529 mips_lwc1 (code, i, mips_sp, pos);
5530 pos += FREG_SIZE
5534 #endif
5536 /* Unlink the LMF if necessary */
5537 if (method->save_lmf) {
5538 int lmf_offset = cfg->arch.lmf_offset;
5540 /* t0 = current_lmf->previous_lmf */
5541 g_assert (mips_is_imm16(lmf_offset + G_STRUCT_OFFSET(MonoLMF, previous_lmf)));
5542 mips_lw (code, mips_temp, mips_sp, lmf_offset + G_STRUCT_OFFSET(MonoLMF, previous_lmf));
5543 /* t1 = lmf_addr */
5544 g_assert (mips_is_imm16(lmf_offset + G_STRUCT_OFFSET(MonoLMF, lmf_addr)));
5545 mips_lw (code, mips_t1, mips_sp, lmf_offset + G_STRUCT_OFFSET(MonoLMF, lmf_addr));
5546 /* (*lmf_addr) = previous_lmf */
5547 mips_sw (code, mips_temp, mips_t1, 0);
5550 #if 0
5551 /* Restore the fp */
5552 mips_lw (code, mips_fp, mips_sp, cfg->stack_usage + MIPS_FP_ADDR_OFFSET);
5553 #endif
5554 /* Restore ra */
5555 if ((cfg->flags & MONO_CFG_HAS_CALLS) || ALWAYS_SAVE_RA) {
5556 g_assert (mips_is_imm16(cfg->stack_usage - alloc2_size + MIPS_RET_ADDR_OFFSET));
5557 mips_lw (code, mips_ra, mips_sp, cfg->stack_usage - alloc2_size + MIPS_RET_ADDR_OFFSET);
5559 /* Restore the stack pointer */
5560 g_assert (mips_is_imm16(cfg->stack_usage - alloc2_size));
5561 mips_addiu (code, mips_sp, mips_sp, cfg->stack_usage - alloc2_size);
5563 /* Caller will emit either return or tail-call sequence */
5565 cfg->code_len = code - cfg->native_code;
5567 g_assert (cfg->code_len < cfg->code_size);
5568 return (code);
5571 void
5572 mono_arch_emit_epilog (MonoCompile *cfg)
5574 guint8 *code;
5576 code = mono_arch_emit_epilog_sub (cfg, NULL);
5578 mips_jr (code, mips_ra);
5579 mips_nop (code);
5581 cfg->code_len = code - cfg->native_code;
5583 g_assert (cfg->code_len < cfg->code_size);
5586 /* remove once throw_exception_by_name is eliminated */
5587 #if 0
5588 static int
5589 exception_id_by_name (const char *name)
5591 if (strcmp (name, "IndexOutOfRangeException") == 0)
5592 return MONO_EXC_INDEX_OUT_OF_RANGE;
5593 if (strcmp (name, "OverflowException") == 0)
5594 return MONO_EXC_OVERFLOW;
5595 if (strcmp (name, "ArithmeticException") == 0)
5596 return MONO_EXC_ARITHMETIC;
5597 if (strcmp (name, "DivideByZeroException") == 0)
5598 return MONO_EXC_DIVIDE_BY_ZERO;
5599 if (strcmp (name, "InvalidCastException") == 0)
5600 return MONO_EXC_INVALID_CAST;
5601 if (strcmp (name, "NullReferenceException") == 0)
5602 return MONO_EXC_NULL_REF;
5603 if (strcmp (name, "ArrayTypeMismatchException") == 0)
5604 return MONO_EXC_ARRAY_TYPE_MISMATCH;
5605 if (strcmp (name, "ArgumentException") == 0)
5606 return MONO_EXC_ARGUMENT;
5607 g_error ("Unknown intrinsic exception %s\n", name);
5608 return 0;
5610 #endif
5612 void
5613 mono_arch_emit_exceptions (MonoCompile *cfg)
5615 #if 0
5616 MonoJumpInfo *patch_info;
5617 int i;
5618 guint8 *code;
5619 const guint8* exc_throw_pos [MONO_EXC_INTRINS_NUM] = {NULL};
5620 guint8 exc_throw_found [MONO_EXC_INTRINS_NUM] = {0};
5621 int max_epilog_size = 50;
5623 /* count the number of exception infos */
5626 * make sure we have enough space for exceptions
5627 * 24 is the simulated call to throw_exception_by_name
5629 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
5630 #if 0
5631 if (patch_info->type == MONO_PATCH_INFO_EXC) {
5632 i = exception_id_by_name (patch_info->data.target);
5633 g_assert (i < MONO_EXC_INTRINS_NUM);
5634 if (!exc_throw_found [i]) {
5635 max_epilog_size += 12;
5636 exc_throw_found [i] = TRUE;
5639 #endif
5642 while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) {
5643 cfg->code_size *= 2;
5644 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
5645 cfg->stat_code_reallocs++;
5648 code = cfg->native_code + cfg->code_len;
5650 /* add code to raise exceptions */
5651 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
5652 switch (patch_info->type) {
5653 case MONO_PATCH_INFO_EXC: {
5654 #if 0
5655 //unsigned char *ip = patch_info->ip.i + cfg->native_code;
5657 i = exception_id_by_name (patch_info->data.target);
5658 g_assert (i >= 0 && i < MONO_EXC_INTRINS_NUM);
5659 if (!exc_throw_pos [i]) {
5660 guint32 addr;
5662 exc_throw_pos [i] = code;
5663 //g_print ("exc: writing stub at %p\n", code);
5664 mips_load_const (code, mips_a0, patch_info->data.target);
5665 addr = (guint32) mono_arch_get_throw_exception_by_name ();
5666 mips_load_const (code, mips_t9, addr);
5667 mips_jr (code, mips_t9);
5668 mips_nop (code);
5670 //g_print ("exc: patch %p to %p\n", ip, exc_throw_pos[i]);
5672 /* Turn into a Relative patch, pointing at code stub */
5673 patch_info->type = MONO_PATCH_INFO_METHOD_REL;
5674 patch_info->data.offset = exc_throw_pos[i] - cfg->native_code;
5675 #else
5676 g_assert_not_reached();
5677 #endif
5678 break;
5680 default:
5681 /* do nothing */
5682 break;
5686 cfg->code_len = code - cfg->native_code;
5688 g_assert (cfg->code_len < cfg->code_size);
5689 #endif
5693 * Thread local storage support
5695 static void
5696 setup_tls_access (void)
5698 guint32 ptk;
5699 //guint32 *ins, *code;
5701 if (tls_mode == TLS_MODE_FAILED)
5702 return;
5704 if (g_getenv ("MONO_NO_TLS")) {
5705 tls_mode = TLS_MODE_FAILED;
5706 return;
5709 if (tls_mode == TLS_MODE_DETECT) {
5710 /* XXX */
5711 tls_mode = TLS_MODE_FAILED;
5712 return;
5713 #if 0
5715 ins = (guint32*)pthread_getspecific;
5716 /* uncond branch to the real method */
5717 if ((*ins >> 26) == 18) {
5718 gint32 val;
5719 val = (*ins & ~3) << 6;
5720 val >>= 6;
5721 if (*ins & 2) {
5722 /* absolute */
5723 ins = (guint32*)val;
5724 } else {
5725 ins = (guint32*) ((char*)ins + val);
5728 code = &cmplwi_1023;
5729 ppc_cmpli (code, 0, 0, ppc_r3, 1023);
5730 code = &li_0x48;
5731 ppc_li (code, ppc_r4, 0x48);
5732 code = &blr_ins;
5733 ppc_blr (code);
5734 if (*ins == cmplwi_1023) {
5735 int found_lwz_284 = 0;
5736 for (ptk = 0; ptk < 20; ++ptk) {
5737 ++ins;
5738 if (!*ins || *ins == blr_ins)
5739 break;
5740 if ((guint16)*ins == 284 && (*ins >> 26) == 32) {
5741 found_lwz_284 = 1;
5742 break;
5745 if (!found_lwz_284) {
5746 tls_mode = TLS_MODE_FAILED;
5747 return;
5749 tls_mode = TLS_MODE_LTHREADS;
5750 } else if (*ins == li_0x48) {
5751 ++ins;
5752 /* uncond branch to the real method */
5753 if ((*ins >> 26) == 18) {
5754 gint32 val;
5755 val = (*ins & ~3) << 6;
5756 val >>= 6;
5757 if (*ins & 2) {
5758 /* absolute */
5759 ins = (guint32*)val;
5760 } else {
5761 ins = (guint32*) ((char*)ins + val);
5763 code = &val;
5764 ppc_li (code, ppc_r0, 0x7FF2);
5765 if (ins [1] == val) {
5766 /* Darwin on G4, implement */
5767 tls_mode = TLS_MODE_FAILED;
5768 return;
5769 } else {
5770 code = &val;
5771 ppc_mfspr (code, ppc_r3, 104);
5772 if (ins [1] != val) {
5773 tls_mode = TLS_MODE_FAILED;
5774 return;
5776 tls_mode = TLS_MODE_DARWIN_G5;
5778 } else {
5779 tls_mode = TLS_MODE_FAILED;
5780 return;
5782 } else {
5783 tls_mode = TLS_MODE_FAILED;
5784 return;
5786 #endif
5788 if (lmf_pthread_key == -1) {
5789 ptk = mono_jit_tls_id;
5790 if (ptk < 1024) {
5791 /*g_print ("MonoLMF at: %d\n", ptk);*/
5792 /*if (!try_offset_access (mono_get_lmf_addr (), ptk)) {
5793 init_tls_failed = 1;
5794 return;
5796 lmf_pthread_key = ptk;
5799 if (monothread_key == -1) {
5800 ptk = mono_thread_get_tls_key ();
5801 if (ptk < 1024) {
5802 monothread_key = ptk;
5803 /*g_print ("thread inited: %d\n", ptk);*/
5804 } else {
5805 /*g_print ("thread not inited yet %d\n", ptk);*/
5810 void
5811 mono_arch_finish_init (void)
5813 setup_tls_access ();
5816 void
5817 mono_arch_free_jit_tls_data (MonoJitTlsData *tls)
5821 void
5822 mono_arch_emit_this_vret_args (MonoCompile *cfg, MonoCallInst *inst, int this_reg, int this_type, int vt_reg)
5824 int this_dreg = mips_a0;
5826 if (vt_reg != -1)
5827 this_dreg = mips_a1;
5829 /* add the this argument */
5830 if (this_reg != -1) {
5831 MonoInst *this_ins;
5832 MONO_INST_NEW (cfg, this_ins, OP_MOVE);
5833 this_ins->type = this_type;
5834 this_ins->sreg1 = this_reg;
5835 this_ins->dreg = mono_alloc_ireg (cfg);
5836 mono_bblock_add_inst (cfg->cbb, this_ins);
5837 mono_call_inst_add_outarg_reg (cfg, inst, this_ins->dreg, this_dreg, FALSE);
5840 if (vt_reg != -1) {
5841 MonoInst *vtarg;
5842 MONO_INST_NEW (cfg, vtarg, OP_MOVE);
5843 vtarg->type = STACK_MP;
5844 vtarg->sreg1 = vt_reg;
5845 vtarg->dreg = mono_alloc_ireg (cfg);
5846 mono_bblock_add_inst (cfg->cbb, vtarg);
5847 mono_call_inst_add_outarg_reg (cfg, inst, vtarg->dreg, mips_a0, FALSE);
5851 MonoInst*
5852 mono_arch_get_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5854 MonoInst *ins = NULL;
5856 return ins;
5859 MonoInst*
5860 mono_arch_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5862 return NULL;
5865 gboolean
5866 mono_arch_print_tree (MonoInst *tree, int arity)
5868 return 0;
5871 mgreg_t
5872 mono_arch_context_get_int_reg (MonoContext *ctx, int reg)
5874 return ctx->sc_regs [reg];
5877 #define ENABLE_WRONG_METHOD_CHECK 0
5879 #define MIPS_LOAD_SEQUENCE_LENGTH 8
5880 #define CMP_SIZE (MIPS_LOAD_SEQUENCE_LENGTH + 4)
5881 #define BR_SIZE 8
5882 #define LOADSTORE_SIZE 4
5883 #define JUMP_IMM_SIZE 16
5884 #define JUMP_IMM32_SIZE (MIPS_LOAD_SEQUENCE_LENGTH + 8)
5885 #define LOAD_CONST_SIZE 8
5886 #define JUMP_JR_SIZE 8
5889 * LOCKING: called with the domain lock held
5891 gpointer
5892 mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count,
5893 gpointer fail_tramp)
5895 int i;
5896 int size = 0;
5897 guint8 *code, *start, *patch;
5899 for (i = 0; i < count; ++i) {
5900 MonoIMTCheckItem *item = imt_entries [i];
5902 if (item->is_equals) {
5903 if (item->check_target_idx) {
5904 item->chunk_size += LOAD_CONST_SIZE + BR_SIZE + JUMP_JR_SIZE;
5905 if (item->has_target_code)
5906 item->chunk_size += LOAD_CONST_SIZE;
5907 else
5908 item->chunk_size += LOADSTORE_SIZE;
5909 } else {
5910 if (fail_tramp) {
5911 item->chunk_size += LOAD_CONST_SIZE + BR_SIZE + JUMP_IMM32_SIZE +
5912 LOADSTORE_SIZE + JUMP_IMM32_SIZE;
5913 if (!item->has_target_code)
5914 item->chunk_size += LOADSTORE_SIZE;
5915 } else {
5916 item->chunk_size += LOADSTORE_SIZE + JUMP_JR_SIZE;
5917 #if ENABLE_WRONG_METHOD_CHECK
5918 item->chunk_size += CMP_SIZE + BR_SIZE + 4;
5919 #endif
5922 } else {
5923 item->chunk_size += CMP_SIZE + BR_SIZE;
5924 imt_entries [item->check_target_idx]->compare_done = TRUE;
5926 size += item->chunk_size;
5928 /* the initial load of the vtable address */
5929 size += MIPS_LOAD_SEQUENCE_LENGTH;
5930 if (fail_tramp) {
5931 code = mono_method_alloc_generic_virtual_thunk (domain, size);
5932 } else {
5933 code = mono_domain_code_reserve (domain, size);
5935 start = code;
5937 /* t7 points to the vtable */
5938 mips_load_const (code, mips_t7, (gsize)(& (vtable->vtable [0])));
5940 for (i = 0; i < count; ++i) {
5941 MonoIMTCheckItem *item = imt_entries [i];
5943 item->code_target = code;
5944 if (item->is_equals) {
5945 if (item->check_target_idx) {
5946 mips_load_const (code, mips_temp, (gsize)item->key);
5947 item->jmp_code = code;
5948 mips_bne (code, mips_temp, MONO_ARCH_IMT_REG, 0);
5949 mips_nop (code);
5950 if (item->has_target_code) {
5951 mips_load_const (code, mips_t9,
5952 item->value.target_code);
5954 else {
5955 mips_lw (code, mips_t9, mips_t7,
5956 (sizeof (gpointer) * item->value.vtable_slot));
5958 mips_jr (code, mips_t9);
5959 mips_nop (code);
5960 } else {
5961 if (fail_tramp) {
5962 mips_load_const (code, mips_temp, (gsize)item->key);
5963 patch = code;
5964 mips_bne (code, mips_temp, MONO_ARCH_IMT_REG, 0);
5965 mips_nop (code);
5966 if (item->has_target_code) {
5967 mips_load_const (code, mips_t9,
5968 item->value.target_code);
5969 } else {
5970 g_assert (vtable);
5971 mips_load_const (code, mips_at,
5972 & (vtable->vtable [item->value.vtable_slot]));
5973 mips_lw (code, mips_t9, mips_at, 0);
5975 mips_jr (code, mips_t9);
5976 mips_nop (code);
5977 mips_patch ((guint32 *)(void *)patch, (guint32)code);
5978 mips_load_const (code, mips_t9, fail_tramp);
5979 mips_jr (code, mips_t9);
5980 mips_nop (code);
5981 } else {
5982 /* enable the commented code to assert on wrong method */
5983 #if ENABLE_WRONG_METHOD_CHECK
5984 ppc_load (code, ppc_r0, (guint32)item->key);
5985 ppc_compare_log (code, 0, MONO_ARCH_IMT_REG, ppc_r0);
5986 patch = code;
5987 ppc_bc (code, PPC_BR_FALSE, PPC_BR_EQ, 0);
5988 #endif
5989 mips_lw (code, mips_t9, mips_t7,
5990 (sizeof (gpointer) * item->value.vtable_slot));
5991 mips_jr (code, mips_t9);
5992 mips_nop (code);
5994 #if ENABLE_WRONG_METHOD_CHECK
5995 ppc_patch (patch, code);
5996 ppc_break (code);
5997 #endif
6000 } else {
6001 mips_load_const (code, mips_temp, (gulong)item->key);
6002 mips_slt (code, mips_temp, MONO_ARCH_IMT_REG, mips_temp);
6004 item->jmp_code = code;
6005 mips_beq (code, mips_temp, mips_zero, 0);
6006 mips_nop (code);
6009 /* patch the branches to get to the target items */
6010 for (i = 0; i < count; ++i) {
6011 MonoIMTCheckItem *item = imt_entries [i];
6012 if (item->jmp_code && item->check_target_idx) {
6013 mips_patch ((guint32 *)item->jmp_code,
6014 (guint32)imt_entries [item->check_target_idx]->code_target);
6018 if (!fail_tramp)
6019 mono_stats.imt_thunks_size += code - start;
6020 g_assert (code - start <= size);
6021 mono_arch_flush_icache (start, size);
6023 mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, NULL), domain);
6025 return start;
6028 MonoMethod*
6029 mono_arch_find_imt_method (mgreg_t *regs, guint8 *code)
6031 return (MonoMethod*) regs [MONO_ARCH_IMT_REG];
6034 MonoVTable*
6035 mono_arch_find_static_call_vtable (mgreg_t *regs, guint8 *code)
6037 return (MonoVTable*) regs [MONO_ARCH_RGCTX_REG];
6040 /* Soft Debug support */
6041 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
6044 * mono_arch_set_breakpoint:
6046 * See mini-amd64.c for docs.
6048 void
6049 mono_arch_set_breakpoint (MonoJitInfo *ji, guint8 *ip)
6051 guint8 *code = ip;
6052 guint32 addr = (guint32)bp_trigger_page;
6054 mips_load_const (code, mips_t9, addr);
6055 mips_lw (code, mips_t9, mips_t9, 0);
6057 mono_arch_flush_icache (ip, code - ip);
6061 * mono_arch_clear_breakpoint:
6063 * See mini-amd64.c for docs.
6065 void
6066 mono_arch_clear_breakpoint (MonoJitInfo *ji, guint8 *ip)
6068 guint8 *code = ip;
6070 mips_nop (code);
6071 mips_nop (code);
6072 mips_nop (code);
6074 mono_arch_flush_icache (ip, code - ip);
6078 * mono_arch_start_single_stepping:
6080 * See mini-amd64.c for docs.
6082 void
6083 mono_arch_start_single_stepping (void)
6085 mono_mprotect (ss_trigger_page, mono_pagesize (), 0);
6089 * mono_arch_stop_single_stepping:
6091 * See mini-amd64.c for docs.
6093 void
6094 mono_arch_stop_single_stepping (void)
6096 mono_mprotect (ss_trigger_page, mono_pagesize (), MONO_MMAP_READ);
6100 * mono_arch_is_single_step_event:
6102 * See mini-amd64.c for docs.
6104 gboolean
6105 mono_arch_is_single_step_event (void *info, void *sigctx)
6107 siginfo_t* sinfo = (siginfo_t*) info;
6108 /* Sometimes the address is off by 4 */
6109 if (sinfo->si_addr >= ss_trigger_page && (guint8*)sinfo->si_addr <= (guint8*)ss_trigger_page + 128)
6110 return TRUE;
6111 else
6112 return FALSE;
6116 * mono_arch_is_breakpoint_event:
6118 * See mini-amd64.c for docs.
6120 gboolean
6121 mono_arch_is_breakpoint_event (void *info, void *sigctx)
6123 siginfo_t* sinfo = (siginfo_t*) info;
6124 /* Sometimes the address is off by 4 */
6125 if (sinfo->si_addr >= bp_trigger_page && (guint8*)sinfo->si_addr <= (guint8*)bp_trigger_page + 128)
6126 return TRUE;
6127 else
6128 return FALSE;
6132 * mono_arch_skip_breakpoint:
6134 * See mini-amd64.c for docs.
6136 void
6137 mono_arch_skip_breakpoint (MonoContext *ctx, MonoJitInfo *ji)
6139 MONO_CONTEXT_SET_IP (ctx, (guint8*)MONO_CONTEXT_GET_IP (ctx) + 4);
6143 * mono_arch_skip_single_step:
6145 * See mini-amd64.c for docs.
6147 void
6148 mono_arch_skip_single_step (MonoContext *ctx)
6150 MONO_CONTEXT_SET_IP (ctx, (guint8*)MONO_CONTEXT_GET_IP (ctx) + 4);
6154 * mono_arch_get_seq_point_info:
6156 * See mini-amd64.c for docs.
6158 gpointer
6159 mono_arch_get_seq_point_info (MonoDomain *domain, guint8 *code)
6161 NOT_IMPLEMENTED;
6162 return NULL;
6165 void
6166 mono_arch_init_lmf_ext (MonoLMFExt *ext, gpointer prev_lmf)
6168 ext->lmf.previous_lmf = prev_lmf;
6169 /* Mark that this is a MonoLMFExt */
6170 ext->lmf.previous_lmf = (gpointer)(((gssize)ext->lmf.previous_lmf) | 2);
6171 ext->lmf.iregs [mips_sp] = (gssize)ext;
6174 #endif /* MONO_ARCH_SOFT_DEBUG_SUPPORTED */
6176 gboolean
6177 mono_arch_opcode_supported (int opcode)
6179 return FALSE;