1 /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
2 /* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is [Open Source Virtual Machine].
18 * The Initial Developer of the Original Code is
19 * MIPS Technologies Inc
20 * Portions created by the Initial Developer are Copyright (C) 2009
21 * the Initial Developer. All Rights Reserved.
24 * Chris Dearman <chris@mips.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
42 #if defined FEATURE_NANOJIT && defined NANOJIT_MIPS
47 const char *regNames[] = {
48 "$zr", "$at", "$v0", "$v1", "$a0", "$a1", "$a2", "$a3",
49 "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7",
50 "$s0", "$s1", "$s2", "$s3", "$s4", "$s5", "$s6", "$s7",
51 "$t8", "$t9", "$k0", "$k1", "$gp", "$sp", "$fp", "$ra",
53 "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
54 "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15",
55 "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
56 "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31"
59 const char *cname[16] = {
60 "f", "un", "eq", "ueq",
61 "olt", "ult", "ole", "ule",
62 "sf", "ngle", "seq", "ngl",
63 "lt", "nge", "le", "ngt"
66 const char *fname[32] = {
67 "resv", "resv", "resv", "resv",
68 "resv", "resv", "resv", "resv",
69 "resv", "resv", "resv", "resv",
70 "resv", "resv", "resv", "resv",
71 "s", "d", "resv", "resv",
72 "w", "l", "ps", "resv",
73 "resv", "resv", "resv", "resv",
74 "resv", "resv", "resv", "resv",
77 const char *oname[64] = {
78 "special", "regimm", "j", "jal", "beq", "bne", "blez", "bgtz",
79 "addi", "addiu", "slti", "sltiu", "andi", "ori", "xori", "lui",
80 "cop0", "cop1", "cop2", "cop1x", "beql", "bnel", "blezl", "bgtzl",
81 "resv", "resv", "resv", "resv", "special2", "jalx", "resv", "special3",
82 "lb", "lh", "lwl", "lw", "lbu", "lhu", "lwr", "resv",
83 "sb", "sh", "swl", "sw", "resv", "resv", "swr", "cache",
84 "ll", "lwc1", "lwc2", "pref", "resv", "ldc1", "ldc2", "resv",
85 "sc", "swc1", "swc2", "resv", "resv", "sdc1", "sdc2", "resv",
89 const Register Assembler::argRegs[] = { A0, A1, A2, A3 };
90 const Register Assembler::retRegs[] = { V0, V1 };
91 const Register Assembler::savedRegs[] = {
92 S0, S1, S2, S3, S4, S5, S6, S7,
94 FS0, FS1, FS2, FS3, FS4, FS5
98 #define USE(x) (void)x
99 #define BADOPCODE(op) NanoAssertMsgf(false, "unexpected opcode %s", lirNames[op])
101 // This function will get will get optimised by the compiler into a known value
102 static inline bool isLittleEndian(void)
106 unsigned char cval[4];
108 return u.cval[0] == 1;
111 // offsets to most/least significant parts of 64bit data in memory
112 // These functions will get optimised by the compiler into a known value
113 static inline int mswoff(void) {
114 return isLittleEndian() ? 4 : 0;
117 static inline int lswoff(void) {
118 return isLittleEndian() ? 0 : 4;
121 static inline Register mswregpair(Register r) {
122 return Register(r + (isLittleEndian() ? 1 : 0));
125 static inline Register lswregpair(Register r) {
126 return Register(r + (isLittleEndian() ? 0 : 1));
129 // These variables affect the code generator
130 // They can be defined as constants and the compiler will remove
131 // the unused paths through dead code elimination
132 // Alternatively they can be defined as variables which will allow
133 // the exact code generated to be determined at runtime
135 // cpu_has_fpu CPU has fpu
136 // cpu_has_movn CPU has movn
137 // cpu_has_cmov CPU has movf/movn instructions
138 // cpu_has_lsdc1 CPU has ldc1/sdc1 instructions
139 // cpu_has_lsxdc1 CPU has ldxc1/sdxc1 instructions
140 // cpu_has_fpuhazard hazard between c.xx.xx & bc1[tf]
142 // Currently the values are initialised bases on preprocessor definitions
145 // Don't allow the compiler to eliminate dead code for debug builds
151 #if NJ_SOFTFLOAT_SUPPORTED
152 _CONST bool cpu_has_fpu = false;
154 _CONST bool cpu_has_fpu = true;
157 #if (__mips==4 || __mips==32 || __mips==64)
158 _CONST bool cpu_has_cmov = true;
160 _CONST bool cpu_has_cmov = false;
164 _CONST bool cpu_has_lsdc1 = true;
166 _CONST bool cpu_has_lsdc1 = false;
169 #if (__mips==32 || __mips==64) && __mips_isa_rev>=2
170 _CONST bool cpu_has_lsdxc1 = true;
172 _CONST bool cpu_has_lsdxc1 = false;
175 #if (__mips==1 || __mips==2 || __mips==3)
176 _CONST bool cpu_has_fpuhazard = true;
178 _CONST bool cpu_has_fpuhazard = false;
182 /* Support routines */
185 // break to debugger when generating code to this address
186 static NIns *breakAddr;
187 static void codegenBreak(NIns *genAddr)
189 NanoAssert (breakAddr != genAddr);
193 // Equivalent to assembler %hi(), %lo()
194 uint16_t hi(uint32_t v)
196 uint16_t r = v >> 16;
197 if ((int16_t)(v) < 0)
202 int16_t lo(uint32_t v)
208 void Assembler::asm_li32(Register r, int32_t imm)
210 // general case generating a full 32-bit load
211 ADDIU(r, r, lo(imm));
215 void Assembler::asm_li(Register r, int32_t imm)
226 if ((imm & 0xffff) == 0) {
227 LUI(r, uint32_t(imm) >> 16);
234 // 64 bit immediate load to a register pair
235 void Assembler::asm_li_d(Register r, int32_t msw, int32_t lsw)
238 NanoAssert(cpu_has_fpu);
239 // li $at,lsw # iff lsw != 0
240 // mtc1 $at,$r # may use $0 instead of $at
241 // li $at,msw # iff (msw != 0) && (msw != lsw)
242 // mtc1 $at,$(r+1) # may use $0 instead of $at
247 // If the MSW & LSW values are different, reload AT
261 * li $r.hi, msw # will be converted to move $f.hi,$f.lo if (msw==lsw)
264 MOVE(mswregpair(r), lswregpair(r));
266 asm_li(mswregpair(r), msw);
267 asm_li(lswregpair(r), lsw);
271 void Assembler::asm_move(Register d, Register s)
276 // General load/store operation
277 void Assembler::asm_ldst(int op, Register rt, int dr, Register rbase)
281 LDST(op, rt, dr, rbase);
289 LDST(op, rt, lo(dr), AT);
294 void Assembler::asm_ldst64(bool store, Register r, int dr, Register rbase)
297 if (isS16(dr) && isS16(dr+4)) {
299 LDST(store ? OP_SW : OP_LW, r+1, dr+4, rbase);
300 LDST(store ? OP_SW : OP_LW, r, dr, rbase);
303 NanoAssert(cpu_has_fpu);
304 // NanoAssert((dr & 7) == 0);
305 if (cpu_has_lsdc1 && ((dr & 7) == 0)) {
306 // lsdc1 $fr,dr($rbase)
307 LDST(store ? OP_SDC1 : OP_LDC1, r, dr, rbase);
310 // lswc1 $fr, dr+LSWOFF($rbase)
311 // lswc1 $fr+1,dr+MSWOFF($rbase)
312 LDST(store ? OP_SWC1 : OP_LWC1, r+1, dr+mswoff(), rbase);
313 LDST(store ? OP_SWC1 : OP_LWC1, r, dr+lswoff(), rbase);
323 // ldsw $r, %lo(d)($at)
324 // ldst $r+1,%lo(d+4)($at)
325 LDST(store ? OP_SW : OP_LW, r+1, lo(dr+4), AT);
326 LDST(store ? OP_SW : OP_LW, r, lo(dr), AT);
331 NanoAssert(cpu_has_fpu);
332 if (cpu_has_lsdxc1) {
334 // lsdcx1 $r,$at($rbase)
341 else if (cpu_has_lsdc1) {
344 // lsdc1 $r,%lo(dr)($at)
345 LDST(store ? OP_SDC1 : OP_LDC1, r, lo(dr), AT);
352 // lswc1 $r, %lo(d+LSWOFF)($at)
353 // lswc1 $r+1,%lo(d+MSWOFF)($at)
354 LDST(store ? OP_SWC1 : OP_LWC1, r+1, lo(dr+mswoff()), AT);
355 LDST(store ? OP_SWC1 : OP_LWC1, r, lo(dr+lswoff()), AT);
362 void Assembler::asm_store_imm64(LIns *value, int dr, Register rbase)
364 NanoAssert(value->isImmD());
365 int32_t msw = value->immDhi();
366 int32_t lsw = value->immDlo();
368 // li $at,lsw # iff lsw != 0
369 // sw $at,off+LSWOFF($rbase) # may use $0 instead of $at
370 // li $at,msw # iff (msw != 0) && (msw != lsw)
371 // sw $at,off+MSWOFF($rbase) # may use $0 instead of $at
373 NanoAssert(isS16(dr) && isS16(dr+4));
376 SW(ZERO, dr+lswoff(), rbase);
378 SW(AT, dr+lswoff(), rbase);
383 SW(ZERO, dr+mswoff(), rbase);
385 SW(AT, dr+mswoff(), rbase);
386 // If the MSW & LSW values are different, reload AT
392 void Assembler::asm_regarg(ArgType ty, LIns* p, Register r)
394 NanoAssert(deprecated_isKnownReg(r));
395 if (ty == ARGTYPE_I || ty == ARGTYPE_UI) {
396 // arg goes in specific register
398 asm_li(r, p->immI());
401 if (!p->deprecated_hasKnownReg()) {
402 // load it into the arg reg
403 int d = findMemFor(p);
404 if (p->isop(LIR_allocp))
407 asm_ldst(OP_LW, r, d, FP);
410 // it must be in a saved reg
411 MOVE(r, p->deprecated_getReg());
414 // this is the last use, so fine to assign it
415 // to the scratch reg, it's dead after this point.
416 findSpecificRegFor(p, r);
421 // Other argument types unsupported
426 void Assembler::asm_stkarg(LIns* arg, int stkd)
428 bool isF64 = arg->isD();
430 if (arg->isExtant() && (rr = arg->deprecated_getReg(), deprecated_isKnownReg(rr))) {
431 // The argument resides somewhere in registers, so we simply need to
432 // push it onto the stack.
433 if (!cpu_has_fpu || !isF64) {
434 NanoAssert(IsGpReg(rr));
438 NanoAssert(cpu_has_fpu);
439 NanoAssert(IsFpReg(rr));
440 NanoAssert((stkd & 7) == 0);
441 asm_ldst64(true, rr, stkd, SP);
445 // The argument does not reside in registers, so we need to get some
446 // memory for it and then copy it onto the stack.
447 int d = findMemFor(arg);
450 if (arg->isop(LIR_allocp))
456 NanoAssert((stkd & 7) == 0);
465 // Encode a 64-bit floating-point argument using the appropriate ABI.
466 // This function operates in the same way as asm_arg, except that it will only
467 // handle arguments where (ArgType)ty == ARGTYPE_D.
469 Assembler::asm_arg_64(LIns* arg, Register& r, Register& fr, int& stkd)
471 // The stack offset always be at least aligned to 4 bytes.
472 NanoAssert((stkd & 3) == 0);
473 #if NJ_SOFTFLOAT_SUPPORTED
474 NanoAssert(arg->isop(LIR_ii2d));
476 NanoAssert(cpu_has_fpu);
479 // O32 ABI requires that 64-bit arguments are aligned on even-numbered
480 // registers, as A0:A1/FA0 or A2:A3/FA1. Use the stack offset to keep track
485 fr = Register(fr + 1);
491 NanoAssert(fr == FA0 || fr == FA1 || fr == A2);
492 if (fr == FA0 || fr == FA1)
493 findSpecificRegFor(arg, fr);
495 findSpecificRegFor(arg, FA1);
496 // Move it to the integer pair
497 Register fpupair = arg->getReg();
498 Register intpair = fr;
499 MFC1(mswregpair(intpair), Register(fpupair + 1)); // Odd fpu register contains sign,expt,manthi
500 MFC1(lswregpair(intpair), fpupair); // Even fpu register contains mantlo
503 fr = Register(fr + 2);
506 asm_stkarg(arg, stkd);
511 /* Required functions */
517 void Assembler::asm_store32(LOpcode op, LIns *value, int dr, LIns *base)
520 getBaseReg2(GpRegs, value, rt, GpRegs, base, rbase, dr);
524 asm_ldst(OP_SW, rt, dr, rbase);
527 asm_ldst(OP_SH, rt, dr, rbase);
530 asm_ldst(OP_SB, rt, dr, rbase);
536 TAG("asm_store32(value=%p{%s}, dr=%d, base=%p{%s})",
537 value, lirNames[value->opcode()], dr, base, lirNames[base->opcode()]);
540 void Assembler::asm_ui2d(LIns *ins)
542 Register fr = deprecated_prepResultReg(ins, FpRegs);
543 Register v = findRegFor(ins->oprnd1(), GpRegs);
544 Register ft = registerAllocTmp(FpRegs & ~(rmask(fr))); // allocate temporary register for constant
546 // todo: support int value in memory, as per x86
547 NanoAssert(deprecated_isKnownReg(v));
552 // lui $at,0x41f0 # (double)0x10000000LL = 0x41f0000000000000
558 underrunProtect(6*4); // keep branch and destination together
564 CVT_D_W(fr,ft); // branch delay slot
568 TAG("asm_ui2d(ins=%p{%s})", ins, lirNames[ins->opcode()]);
571 void Assembler::asm_d2i(LIns* ins)
573 NanoAssert(cpu_has_fpu);
575 Register rr = deprecated_prepResultReg(ins, GpRegs);
576 Register sr = findRegFor(ins->oprnd1(), FpRegs);
581 TAG("asm_d2i(ins=%p{%s})", ins, lirNames[ins->opcode()]);
584 void Assembler::asm_fop(LIns *ins)
586 NanoAssert(cpu_has_fpu);
588 LIns* lhs = ins->oprnd1();
589 LIns* rhs = ins->oprnd2();
590 LOpcode op = ins->opcode();
594 Register rr = deprecated_prepResultReg(ins, FpRegs);
595 Register ra = findRegFor(lhs, FpRegs);
596 Register rb = (rhs == lhs) ? ra : findRegFor(rhs, FpRegs & ~rmask(ra));
599 case LIR_addd: ADD_D(rr, ra, rb); break;
600 case LIR_subd: SUB_D(rr, ra, rb); break;
601 case LIR_muld: MUL_D(rr, ra, rb); break;
602 case LIR_divd: DIV_D(rr, ra, rb); break;
607 TAG("asm_fop(ins=%p{%s})", ins, lirNames[ins->opcode()]);
610 void Assembler::asm_fneg(LIns *ins)
612 NanoAssert(cpu_has_fpu);
614 LIns* lhs = ins->oprnd1();
615 Register rr = deprecated_prepResultReg(ins, FpRegs);
616 Register sr = ( !lhs->isInReg()
617 ? findRegFor(lhs, FpRegs)
618 : lhs->deprecated_getReg() );
621 TAG("asm_fneg(ins=%p{%s})", ins, lirNames[ins->opcode()]);
624 void Assembler::asm_immd(LIns *ins)
626 int d = deprecated_disp(ins);
627 Register rr = ins->deprecated_getReg();
629 deprecated_freeRsrcOf(ins);
631 if (cpu_has_fpu && deprecated_isKnownReg(rr)) {
633 asm_spill(rr, d, true);
634 asm_li_d(rr, ins->immDhi(), ins->immDlo());
638 asm_store_imm64(ins, d, FP);
640 TAG("asm_immd(ins=%p{%s})", ins, lirNames[ins->opcode()]);
645 Assembler::asm_q2i(LIns *)
647 NanoAssert(0); // q2i shouldn't occur on 32-bit platforms
650 void Assembler::asm_ui2uq(LIns *ins)
654 TAG("asm_ui2uq(ins=%p{%s})", ins, lirNames[ins->opcode()]);
658 void Assembler::asm_load64(LIns *ins)
660 NanoAssert(ins->isD());
662 LIns* base = ins->oprnd1();
663 int dr = ins->disp();
665 Register rd = ins->deprecated_getReg();
666 int ds = deprecated_disp(ins);
668 Register rbase = findRegFor(base, GpRegs);
669 NanoAssert(IsGpReg(rbase));
670 deprecated_freeRsrcOf(ins);
672 if (cpu_has_fpu && deprecated_isKnownReg(rd)) {
673 NanoAssert(IsFpReg(rd));
674 asm_ldst64 (false, rd, dr, rbase);
677 // Either FPU is not available or the result needs to go into memory;
678 // in either case, FPU instructions are not required. Note that the
679 // result will never be loaded into registers if FPU is not available.
680 NanoAssert(!deprecated_isKnownReg(rd));
683 NanoAssert(isS16(dr) && isS16(dr+4));
684 NanoAssert(isS16(ds) && isS16(ds+4));
686 // Check that the offset is 8-byte (64-bit) aligned.
687 NanoAssert((ds & 0x7) == 0);
689 // FIXME: allocate a temporary to use for the copy
690 // to avoid load to use delay
693 // lw $at,dr+4($rbase)
702 TAG("asm_load64(ins=%p{%s})", ins, lirNames[ins->opcode()]);
705 void Assembler::asm_cond(LIns *ins)
707 Register r = deprecated_prepResultReg(ins, GpRegs);
708 LOpcode op = ins->opcode();
709 LIns *a = ins->oprnd1();
710 LIns *b = ins->oprnd2();
712 asm_cmp(op, a, b, r);
714 TAG("asm_cond(ins=%p{%s})", ins, lirNames[ins->opcode()]);
717 #if NJ_SOFTFLOAT_SUPPORTED
718 void Assembler::asm_qhi(LIns *ins)
720 Register rr = deprecated_prepResultReg(ins, GpRegs);
721 LIns *q = ins->oprnd1();
722 int d = findMemFor(q);
723 LW(rr, d+mswoff(), FP);
724 TAG("asm_qhi(ins=%p{%s})", ins, lirNames[ins->opcode()]);
727 void Assembler::asm_qlo(LIns *ins)
729 Register rr = deprecated_prepResultReg(ins, GpRegs);
730 LIns *q = ins->oprnd1();
731 int d = findMemFor(q);
732 LW(rr, d+lswoff(), FP);
733 TAG("asm_qlo(ins=%p{%s})", ins, lirNames[ins->opcode()]);
736 void Assembler::asm_qjoin(LIns *ins)
738 int d = findMemFor(ins);
739 NanoAssert(d && isS16(d));
740 LIns* lo = ins->oprnd1();
741 LIns* hi = ins->oprnd2();
743 Register r = findRegFor(hi, GpRegs);
744 SW(r, d+mswoff(), FP);
745 r = findRegFor(lo, GpRegs); // okay if r gets recycled.
746 SW(r, d+lswoff(), FP);
747 deprecated_freeRsrcOf(ins); // if we had a reg in use, flush it to mem
749 TAG("asm_qjoin(ins=%p{%s})", ins, lirNames[ins->opcode()]);
754 void Assembler::asm_neg_not(LIns *ins)
756 LOpcode op = ins->opcode();
757 Register rr = deprecated_prepResultReg(ins, GpRegs);
759 LIns* lhs = ins->oprnd1();
760 // If this is the last use of lhs in reg, we can re-use result reg.
761 // Else, lhs already has a register assigned.
762 Register ra = !lhs->isInReg() ? findSpecificRegFor(lhs, rr) : lhs->deprecated_getReg();
767 TAG("asm_neg_not(ins=%p{%s})", ins, lirNames[ins->opcode()]);
770 void Assembler::asm_immi(LIns *ins)
772 Register rr = deprecated_prepResultReg(ins, GpRegs);
773 asm_li(rr, ins->immI());
774 TAG("asm_immi(ins=%p{%s})", ins, lirNames[ins->opcode()]);
777 void Assembler::asm_cmov(LIns *ins)
779 LIns* condval = ins->oprnd1();
780 LIns* iftrue = ins->oprnd2();
781 LIns* iffalse = ins->oprnd3();
783 NanoAssert(condval->isCmp());
784 NanoAssert(ins->opcode() == LIR_cmovi && iftrue->isI() && iffalse->isI());
786 const Register rr = deprecated_prepResultReg(ins, GpRegs);
788 const Register iftruereg = findRegFor(iftrue, GpRegs & ~rmask(rr));
789 MOVN(rr, iftruereg, AT);
790 /*const Register iffalsereg =*/ findSpecificRegFor(iffalse, rr);
791 asm_cmp(condval->opcode(), condval->oprnd1(), condval->oprnd2(), AT);
792 TAG("asm_cmov(ins=%p{%s})", ins, lirNames[ins->opcode()]);
795 void Assembler::asm_condd(LIns *ins)
797 NanoAssert(cpu_has_fpu);
799 Register r = deprecated_prepResultReg(ins, GpRegs);
800 LOpcode op = ins->opcode();
801 LIns *a = ins->oprnd1();
802 LIns *b = ins->oprnd2();
819 verbose_only(verbose_outputf("%p:", here);)
820 underrunProtect(3*4);
822 ORI(r, ZERO, 1); // branch delay slot
824 if (cpu_has_fpuhazard)
827 asm_cmp(op, a, b, r);
829 TAG("asm_condd(ins=%p{%s})", ins, lirNames[ins->opcode()]);
832 void Assembler::asm_i2d(LIns *ins)
834 NanoAssert(cpu_has_fpu);
836 Register fr = deprecated_prepResultReg(ins, FpRegs);
837 Register v = findRegFor(ins->oprnd1(), GpRegs);
844 TAG("asm_i2d(ins=%p{%s})", ins, lirNames[ins->opcode()]);
847 void Assembler::asm_ret(LIns *ins)
854 LIns *value = ins->oprnd1();
855 if (ins->isop(LIR_reti)) {
856 findSpecificRegFor(value, V0);
859 NanoAssert(ins->isop(LIR_retd));
860 #if NJ_SOFTFLOAT_SUPPORTED
861 NanoAssert(value->isop(LIR_ii2d));
862 findSpecificRegFor(value->oprnd1(), V0); // lo
863 findSpecificRegFor(value->oprnd2(), V1); // hi
865 findSpecificRegFor(value, FV0);
868 TAG("asm_ret(ins=%p{%s})", ins, lirNames[ins->opcode()]);
871 void Assembler::asm_load32(LIns *ins)
873 LOpcode op = ins->opcode();
874 LIns* base = ins->oprnd1();
877 Register rres = deprecated_prepResultReg(ins, GpRegs);
878 Register rbase = getBaseReg(base, d, GpRegs);
881 case LIR_lduc2ui: // 8-bit integer load, zero-extend to 32-bit
882 asm_ldst(OP_LBU, rres, d, rbase);
884 case LIR_ldus2ui: // 16-bit integer load, zero-extend to 32-bit
885 asm_ldst(OP_LHU, rres, d, rbase);
887 case LIR_ldc2i: // 8-bit integer load, sign-extend to 32-bit
888 asm_ldst(OP_LB, rres, d, rbase);
890 case LIR_lds2i: // 16-bit integer load, sign-extend to 32-bit
891 asm_ldst(OP_LH, rres, d, rbase);
893 case LIR_ldi: // 32-bit integer load
894 asm_ldst(OP_LW, rres, d, rbase);
900 TAG("asm_load32(ins=%p{%s})", ins, lirNames[ins->opcode()]);
903 void Assembler::asm_param(LIns *ins)
905 uint32_t a = ins->paramArg();
906 uint32_t kind = ins->paramKind();
910 // first 4 args A0..A3
912 // incoming arg in register
913 deprecated_prepResultReg(ins, rmask(argRegs[a]));
915 // incoming arg is on stack
916 Register r = deprecated_prepResultReg(ins, GpRegs);
917 TODO(Check stack offset);
918 int d = FRAMESIZE + a * sizeof(intptr_t);
924 deprecated_prepResultReg(ins, rmask(savedRegs[a]));
926 TAG("asm_param(ins=%p{%s})", ins, lirNames[ins->opcode()]);
929 void Assembler::asm_arith(LIns *ins)
931 LOpcode op = ins->opcode();
932 LIns* lhs = ins->oprnd1();
933 LIns* rhs = ins->oprnd2();
935 RegisterMask allow = GpRegs;
937 // We always need the result register and the first operand register.
938 Register rr = deprecated_prepResultReg(ins, allow);
940 // If this is the last use of lhs in reg, we can re-use the result reg.
941 // Else, lhs already has a register assigned.
942 Register ra = !lhs->isInReg() ? findSpecificRegFor(lhs, rr) : lhs->deprecated_getReg();
945 // Don't re-use the registers we've already allocated.
946 NanoAssert(deprecated_isKnownReg(rr));
947 NanoAssert(deprecated_isKnownReg(ra));
952 int32_t rhsc = rhs->immI();
954 // MIPS arith immediate ops sign-extend the imm16 value
958 // add with overflow result into $at
959 // overflow is indicated by ((sign(rr)^sign(ra)) & (sign(rr)^sign(rhsc))
961 // [move $t,$ra] if (rr==ra)
962 // addiu $rr,$ra,rhsc
963 // [xor $at,$rr,$ra] if (rr!=ra)
964 // [xor $at,$rr,$t] if (rr==ra)
965 // [not $t,$rr] if (rhsc < 0)
966 // [and $at,$at,$t] if (rhsc < 0)
967 // [and $at,$at,$rr] if (rhsc >= 0)
970 t = registerAllocTmp(allow);
991 // subtract with overflow result into $at
992 // overflow is indicated by (sign(ra)^sign(rhsc)) & (sign(rr)^sign(ra))
994 // [move $t,$ra] if (rr==ra)
995 // addiu $rr,$ra,-rhsc
996 // [xor $at,$rr,$ra] if (rr!=ra)
997 // [xor $at,$rr,$t] if (rr==ra)
998 // [and $at,$at,$ra] if (rhsc >= 0 && rr!=ra)
999 // [and $at,$at,$t] if (rhsc >= 0 && rr==ra)
1000 // [not $t,$ra] if (rhsc < 0 && rr!=ra)
1001 // [not $t,$t] if (rhsc < 0 && rr==ra)
1002 // [and $at,$at,$t] if (rhsc < 0)
1005 t = registerAllocTmp(allow);
1024 ADDIU(rr, ra, -rhsc);
1032 ADDIU(rr, ra, -rhsc);
1039 // FIXME: optimise constant multiply by 2^n
1040 // if ((rhsc & (rhsc-1)) == 0)
1041 // SLL(rr, ra, ffs(rhsc)-1);
1049 // MIPS logical immediate zero-extend the imm16 value
1065 // LIR shift ops only use last 5bits of shift const
1068 SLL(rr, ra, rhsc&31);
1071 SRL(rr, ra, rhsc&31);
1074 SRA(rr, ra, rhsc&31);
1081 // general case, put rhs in register
1082 rb = (rhs == lhs) ? ra : findRegFor(rhs, allow);
1083 NanoAssert(deprecated_isKnownReg(rb));
1084 allow &= ~rmask(rb);
1086 // The register allocator will have set up one of these 4 cases
1087 // rr==ra && ra==rb r0 = r0 op r0
1088 // rr==ra && ra!=rb r0 = r0 op r1
1089 // rr!=ra && ra==rb r0 = r1 op r1
1090 // rr!=ra && ra!=rb && rr!=rb r0 = r1 op r2
1091 NanoAssert(ra == rb || rr != rb);
1096 // add with overflow result into $at
1097 // overflow is indicated by (sign(rr)^sign(ra)) & (sign(rr)^sign(rb))
1099 // [move $t,$ra] if (rr==ra)
1101 // ; Generate sign($rr)^sign($ra)
1102 // [xor $at,$rr,$t] sign($at)=sign($rr)^sign($t) if (rr==ra)
1103 // [xor $at,$rr,$ra] sign($at)=sign($rr)^sign($ra) if (rr!=ra)
1104 // ; Generate sign($rr)^sign($rb) if $ra!=$rb
1105 // [xor $t,$rr,$rb] if (ra!=rb)
1106 // [and $at,$t] if (ra!=rb)
1110 if (rr == ra || ra != rb)
1111 t = registerAllocTmp(allow);
1139 // subtract with overflow result into $at
1140 // overflow is indicated by (sign(ra)^sign(rb)) & (sign(rr)^sign(ra))
1142 // [move $t,$ra] if (rr==ra)
1143 // ; Generate sign($at)=sign($ra)^sign($rb)
1146 // ; Generate sign($t)=sign($rr)^sign($ra)
1147 // [xor $t,$rr,$ra] if (rr!=ra)
1148 // [xor $t,$rr,$t] if (rr==ra)
1153 // special case for (ra == rb) which can't overflow
1158 t = registerAllocTmp(allow);
1175 // SLLV uses the low-order 5 bits of rb for the shift amount so no masking required
1179 // SRAV uses the low-order 5 bits of rb for the shift amount so no masking required
1183 // SRLV uses the low-order 5 bits of rb for the shift amount so no masking required
1188 t = registerAllocTmp(allow);
1189 // Overflow indication required
1190 // Do a 32x32 signed multiply generating a 64 bit result
1191 // Compare bit31 of the result with the high order bits
1193 // mflo $rr # result to $rr
1194 // sra $t,$rr,31 # $t = 0x00000000 or 0xffffffff
1196 // xor $at,$at,$t # sets $at to nonzero if overflow
1210 TAG("asm_arith(ins=%p{%s})", ins, lirNames[ins->opcode()]);
1213 void Assembler::asm_store64(LOpcode op, LIns *value, int dr, LIns *base)
1215 // NanoAssert((dr & 7) == 0);
1217 NanoAssert (op == LIR_stq || op == LIR_std2f || op == LIR_std);
1219 NanoAssert (op == LIR_std2f || op == LIR_std);
1225 Register rbase = findRegFor(base, GpRegs);
1227 if (value->isImmD())
1228 asm_store_imm64(value, dr, rbase);
1230 Register fr = findRegFor(value, FpRegs);
1231 asm_ldst64(true, fr, dr, rbase);
1235 Register rbase = findRegFor(base, GpRegs);
1236 // *(uint64_t*)(rb+dr) = *(uint64_t*)(FP+da)
1238 int ds = findMemFor(value);
1241 // sw $at,dr($rbase)
1243 // sw $at,dr+4($rbase)
1244 SW(AT, dr+4, rbase);
1252 NanoAssertMsg(0, "NJ_EXPANDED_LOADSTORE_SUPPORTED not yet supported for this architecture");
1259 TAG("asm_store64(value=%p{%s}, dr=%d, base=%p{%s})",
1260 value, lirNames[value->opcode()], dr, base, lirNames[base->opcode()]);
1263 bool Assembler::canRemat(LIns* ins)
1265 return ins->isImmI() || ins->isop(LIR_allocp);
1268 void Assembler::asm_restore(LIns *i, Register r)
1271 if (i->isop(LIR_allocp)) {
1272 d = deprecated_disp(i);
1280 else if (i->isImmI()) {
1281 asm_li(r, i->immI());
1286 asm_ldst64(false, r, d, FP);
1289 asm_ldst(OP_LW, r, d, FP);
1292 TAG("asm_restore(i=%p{%s}, r=%d)", i, lirNames[i->opcode()], r);
1295 void Assembler::asm_cmp(LOpcode condop, LIns *a, LIns *b, Register cr)
1297 RegisterMask allow = isCmpDOpcode(condop) ? FpRegs : GpRegs;
1298 Register ra = findRegFor(a, allow);
1299 Register rb = (b==a) ? ra : findRegFor(b, allow & ~rmask(ra));
1301 // FIXME: Use slti if b is small constant
1303 /* Generate the condition code */
1353 debug_only(outputf("%s",lirNames[condop]);)
1358 #define SEG(addr) (uint32_t(addr) & 0xf0000000)
1359 #define SEGOFFS(addr) (uint32_t(addr) & 0x0fffffff)
1362 // Check that the branch target is in range
1363 // Generate a trampoline if it isn't
1364 // Emits the branch delay slot instruction
1365 NIns* Assembler::asm_branchtarget(NIns * const targ)
1370 // do initial underrun check here to ensure that inrange test is correct
1373 underrunProtect(2 * 4); // branch + delay slot
1375 // MIPS offsets are based on the address of the branch delay slot
1376 // which is the next instruction that will be generated
1377 ptrdiff_t bd = BOFFSET(targ-1);
1382 inrange = (targ && isS16(bd));
1385 // If the branch target is known and in range we can just generate a branch
1386 // Otherwise generate a branch to a trampoline that will be stored in the
1391 NIns *tramp = _nSlot;
1393 // Can the target be reached by a jump instruction?
1394 if (SEG(targ) == SEG(tramp)) {
1395 // [linkedinstructions]
1403 underrunProtect(4 * 4); // keep bxx and trampoline together
1405 NOP(); // delay slot
1407 // NB trampoline code is emitted in the correct order
1409 trampNOP(); // trampoline delay slot
1413 // [linkedinstructions]
1415 // lui $at,%hi(targ)
1418 // addiu $at,%lo(targ)
1422 underrunProtect(5 * 4); // keep bxx and trampoline together
1424 LUI(AT,hi(uint32_t(targ))); // delay slot
1426 // NB trampoline code is emitted in the correct order
1427 trampADDIU(AT, AT, lo(uint32_t(targ)));
1429 trampNOP(); // trampoline delay slot
1434 // Worst case is bxxx,lui addiu;jr;nop as above
1435 // Best case is branch to trampoline can be replaced
1436 // with branch to target in which case the trampoline will be abandoned
1437 // Fixup handled in nPatchBranch
1439 underrunProtect(5 * 4); // keep bxx and trampoline together
1441 NOP(); // delay slot
1455 NIns* Assembler::asm_bxx(bool branchOnFalse, LOpcode condop, Register ra, Register rb, NIns * const targ)
1458 NIns *btarg = asm_branchtarget(targ);
1460 if (cpu_has_fpu && isCmpDOpcode(condop)) {
1470 if (cpu_has_fpuhazard)
1480 if (cpu_has_fpuhazard)
1490 if (cpu_has_fpuhazard)
1500 if (cpu_has_fpuhazard)
1510 if (cpu_has_fpuhazard)
1521 // s[lg]tu? $at,($ra,$rb|$rb,$ra)
1522 // b(ne|eq)z $at,btarg
1526 // b(ne|eq) $ra,$rb,btarg
1539 BEQ(AT, ZERO, btarg);
1541 BNE(AT, ZERO, btarg);
1547 BEQ(AT, ZERO, btarg);
1549 BNE(AT, ZERO, btarg);
1555 BNE(AT, ZERO, btarg);
1557 BEQ(AT, ZERO, btarg);
1563 BNE(AT, ZERO, btarg);
1565 BEQ(AT, ZERO, btarg);
1571 BEQ(AT, ZERO, btarg);
1573 BNE(AT, ZERO, btarg);
1579 BEQ(AT, ZERO, btarg);
1581 BNE(AT, ZERO, btarg);
1587 BNE(AT, ZERO, btarg);
1589 BEQ(AT, ZERO, btarg);
1595 BNE(AT, ZERO, btarg);
1597 BEQ(AT, ZERO, btarg);
1605 TAG("asm_bxx(branchOnFalse=%d, condop=%s, ra=%s rb=%s targ=%p)",
1606 branchOnFalse, lirNames[condop], gpn(ra), gpn(rb), targ);
1610 NIns* Assembler::asm_branch_ov(LOpcode op, NIns* target)
1613 NanoAssert(target != NULL);
1615 NIns* patch = asm_bxx(true, LIR_eqi, AT, ZERO, target);
1617 TAG("asm_branch_ov(op=%s, target=%p)", lirNames[op], target);
1621 NIns* Assembler::asm_branch(bool branchOnFalse, LIns *cond, NIns * const targ)
1623 NanoAssert(cond->isCmp());
1624 LOpcode condop = cond->opcode();
1625 RegisterMask allow = isCmpDOpcode(condop) ? FpRegs : GpRegs;
1626 LIns *a = cond->oprnd1();
1627 LIns *b = cond->oprnd2();
1628 Register ra = findRegFor(a, allow);
1629 Register rb = (b==a) ? ra : findRegFor(b, allow & ~rmask(ra));
1631 return asm_bxx(branchOnFalse, condop, ra, rb, targ);
1634 void Assembler::asm_j(NIns * const targ, bool bdelay)
1638 (void) asm_bxx(false, LIR_eqi, ZERO, ZERO, targ);
1641 NanoAssert(SEG(targ) == SEG(_nIns));
1643 underrunProtect(2*4); // j + delay
1648 TAG("asm_j(targ=%p) bdelay=%d", targ);
1652 Assembler::asm_spill(Register rr, int d, bool quad)
1658 asm_ldst64(true, rr, d, FP);
1662 asm_ldst(OP_SW, rr, d, FP);
1664 TAG("asm_spill(rr=%d, d=%d, quad=%d)", rr, d, quad);
1668 Assembler::asm_nongp_copy(Register dst, Register src)
1670 NanoAssert ((rmask(dst) & FpRegs) && (rmask(src) & FpRegs));
1672 TAG("asm_nongp_copy(dst=%d src=%d)", dst, src);
1676 * asm_arg will encode the specified argument according to the current ABI, and
1677 * will update r and stkd as appropriate so that the next argument can be
1680 * - doubles are 64-bit aligned. both in registers and on the stack.
1681 * If the next available argument register is A1, it is skipped
1682 * and the double is placed in A2:A3. If A0:A1 or A2:A3 are not
1683 * available, the double is placed on the stack, 64-bit aligned.
1684 * - 32-bit arguments are placed in registers and 32-bit aligned
1688 Assembler::asm_arg(ArgType ty, LIns* arg, Register& r, Register& fr, int& stkd)
1690 // The stack offset must always be at least aligned to 4 bytes.
1691 NanoAssert((stkd & 3) == 0);
1693 if (ty == ARGTYPE_D) {
1694 // This task is fairly complex and so is delegated to asm_arg_64.
1695 asm_arg_64(arg, r, fr, stkd);
1697 NanoAssert(ty == ARGTYPE_I || ty == ARGTYPE_UI);
1699 asm_regarg(ty, arg, r);
1700 fr = Register(fr + 1);
1701 r = Register(r + 1);
1704 asm_stkarg(arg, stkd);
1705 // The o32 ABI calling convention is that if the first arguments
1706 // is not a double, subsequent double values are passed in integer registers
1713 Assembler::asm_call(LIns* ins)
1715 if (!ins->isop(LIR_callv)) {
1717 LOpcode op = ins->opcode();
1724 NanoAssert(cpu_has_fpu);
1732 deprecated_prepResultReg(ins, rmask(rr));
1735 // Do this after we've handled the call result, so we don't
1736 // force the call result to be spilled unnecessarily.
1737 evictScratchRegsExcept(0);
1739 const CallInfo* ci = ins->callInfo();
1740 ArgType argTypes[MAXARGS];
1741 uint32_t argc = ci->getArgTypes(argTypes);
1742 bool indirect = ci->isIndirect();
1744 // FIXME: Put one of the argument moves into the BDS slot
1746 underrunProtect(2*4); // jalr+delay
1751 // FIXME: If we can tell that we are calling non-PIC
1752 // (ie JIT) code, we could call direct instead of using t9
1753 asm_li(T9, ci->_address);
1755 // Indirect call: we assign the address arg to t9
1756 // which matches the o32 ABI for calling functions
1757 asm_regarg(ARGTYPE_P, ins->arg(--argc), T9);
1759 // Encode the arguments, starting at A0 and with an empty argument stack.
1760 Register r = A0, fr = FA0;
1763 // Iterate through the argument list and encode each argument according to
1765 // Note that we loop through the arguments backwards as LIR specifies them
1766 // in reverse order.
1768 asm_arg(argTypes[argc], ins->arg(argc), r, fr, stkd);
1770 if (stkd > max_out_args)
1771 max_out_args = stkd;
1772 TAG("asm_call(ins=%p{%s})", ins, lirNames[ins->opcode()]);
1776 Assembler::nRegisterAllocFromSet(RegisterMask set)
1781 // note, deliberate truncation of 64->32 bits
1782 if (set & 0xffffffff) {
1786 i = Register(n - 1);
1790 NanoAssert(cpu_has_fpu);
1791 n = ffs(int(set >> 32));
1793 i = Register(32 + n - 1);
1795 _allocator.free &= ~rmask(i);
1796 TAG("nRegisterAllocFromSet(set=%016llx) => %s", set, gpn(i));
1801 Assembler::nRegisterResetAll(RegAlloc& regs)
1806 regs.free |= FpRegs;
1809 #define signextend16(s) ((int32_t(s)<<16)>>16)
1812 Assembler::nPatchBranch(NIns* branch, NIns* target)
1814 uint32_t op = (branch[0] >> 26) & 0x3f;
1815 uint32_t bdoffset = target-(branch+1);
1817 if (op == OP_BEQ || op == OP_BNE ||
1818 ((branch[0] & 0xfffe0000) == ((OP_COP1 << 26) | (COP1_BC << 21)))) {
1819 if (isS16(bdoffset)) {
1820 // The branch is in range, so just replace the offset in the instruction
1821 // The trampoline that was allocated is redundant and will remain unused
1822 branch[0] = (branch[0] & 0xffff0000) | (bdoffset & 0xffff);
1825 // The branch is pointing to a trampoline. Find out where that is
1826 NIns *tramp = branch + 1 + (signextend16(branch[0] & 0xffff));
1827 if (SEG(branch) == SEG(target)) {
1828 *tramp = J_FORMAT(OP_J,JINDEX(target));
1833 // lui $at,(target>>16)>0xffff
1836 // ori $at,target & 0xffff
1839 branch[1] = U_FORMAT(OP_LUI,0,AT,hi(uint32_t(target)));
1840 tramp[0] = U_FORMAT(OP_ADDIU,AT,AT,lo(uint32_t(target)));
1841 tramp[1] = R_FORMAT(OP_SPECIAL,AT,0,0,0,SPECIAL_JR);
1845 else if (op == OP_J) {
1846 NanoAssert (SEG(branch) == SEG(target));
1847 branch[0] = J_FORMAT(OP_J,JINDEX(target));
1850 TODO(unknown_patch);
1851 // TAG("nPatchBranch(branch=%p target=%p)", branch, target);
1855 Assembler::nFragExit(LIns *guard)
1857 SideExit *exit = guard->record()->exit;
1858 Fragment *frag = exit->target;
1859 bool destKnown = (frag && frag->fragEntry);
1861 // Generate jump to epilogue and initialize lr.
1863 // If the guard already exists, use a simple jump.
1867 underrunProtect(2 * 4); // j + branch delay
1869 asm_j(frag->fragEntry, false);
1872 // Target doesn't exist. Jump to an epilogue for now.
1873 // This can be patched later.
1875 _epilogue = genEpilogue();
1876 GuardRecord *lr = guard->record();
1877 // FIXME: _epilogue may be in another segment
1880 // addiu $v0,%lo(lr)
1881 underrunProtect(2 * 4); // j + branch delay
1882 ADDIU(V0, V0, lo(int32_t(lr)));
1883 asm_j(_epilogue, false);
1884 LUI(V0, hi(int32_t(lr)));
1888 // profiling for the exit
1890 if (_logc->lcbits & LC_FragProfile) {
1891 // lui $fp,%hi(profCount)
1892 // lw $at,%lo(profCount)(fp)
1894 // sw $at,%lo(profCount)(fp)
1895 uint32_t profCount = uint32_t(&guard->record()->profCount);
1896 SW(AT, lo(profCount), FP);
1898 LW(AT, lo(profCount), FP);
1899 LUI(FP, hi(profCount));
1903 // Pop the stack frame.
1906 // return value is GuardRecord*
1907 TAG("nFragExit(guard=%p{%s})", guard, lirNames[guard->opcode()]);
1911 Assembler::nInit(AvmCore*)
1913 nHints[LIR_calli] = rmask(V0);
1914 #if NJ_SOFTFLOAT_SUPPORTED
1915 nHints[LIR_hcalli] = rmask(V1);
1917 nHints[LIR_calld] = rmask(FV0);
1918 nHints[LIR_paramp] = PREFER_SPECIAL;
1921 void Assembler::nBeginAssembly()
1923 max_out_args = 16; // Always reserve space for a0-a3
1926 // Increment the 32-bit profiling counter at pCtr, without
1927 // changing any registers.
1929 void Assembler::asm_inc_m32(uint32_t* /*pCtr*/)
1931 // TODO: implement this
1936 Assembler::nativePageReset(void)
1940 TAG("nativePageReset()");
1944 Assembler::nativePageSetup(void)
1946 NanoAssert(!_inExit);
1948 codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes));
1950 codeAlloc(exitStart, exitEnd, _nExitIns verbose_only(, exitBytes));
1952 // constpool starts at bottom of page and moves up
1953 // code starts at top of page and goes down,
1958 _nExitSlot = exitStart;
1960 TAG("nativePageSetup()");
1965 Assembler::genPrologue(void)
1968 * Use a non standard fp because we don't know the final framesize until now
1969 * addiu $sp,-FRAMESIZE
1970 * sw $ra,RA_OFFSET($sp)
1971 * sw $fp,FP_OFFSET($sp)
1973 * addu $sp,-stackNeeded
1976 uint32_t stackNeeded = max_out_args + STACK_GRANULARITY * _activation.stackSlotsNeeded();
1977 uint32_t amt = alignUp(stackNeeded, NJ_ALIGN_STACK);
1981 ADDIU(SP, SP, -amt);
1988 NIns *patchEntry = _nIns; // FIXME: who uses this value and where should it point?
1991 SW(FP, FP_OFFSET, SP);
1992 SW(RA, RA_OFFSET, SP); // No need to save for leaf functions
1993 ADDIU(SP, SP, -FRAMESIZE);
1995 TAG("genPrologue()");
2001 Assembler::genEpilogue(void)
2005 * lw $ra,RA_OFFSET($sp)
2006 * lw $fp,FP_OFFSET($sp)
2008 * addiu $sp,FRAMESIZE
2010 ADDIU(SP, SP, FRAMESIZE);
2012 LW(FP, FP_OFFSET, SP);
2013 LW(RA, RA_OFFSET, SP);
2016 TAG("genEpilogue()");
2022 Assembler::nHint(LIns* ins)
2024 NanoAssert(ins->isop(LIR_paramp));
2025 RegisterMask prefer = 0;
2026 // FIXME: FLOAT parameters?
2027 if (ins->paramKind() == 0)
2028 if (ins->paramArg() < 4)
2029 prefer = rmask(argRegs[ins->paramArg()]);
2034 Assembler::underrunProtect(int bytes)
2036 NanoAssertMsg(bytes<=LARGEST_UNDERRUN_PROT, "constant LARGEST_UNDERRUN_PROT is too small");
2037 NanoAssert(_nSlot != 0);
2038 uintptr_t top = uintptr_t(_nSlot);
2039 uintptr_t pc = uintptr_t(_nIns);
2040 if (pc - bytes < top) {
2041 verbose_only(verbose_outputf(" %p:", _nIns);)
2042 NIns* target = _nIns;
2043 codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes));
2047 // _nSlot points to the first empty position in the new code block
2048 // _nIns points just past the last empty position.
2049 asm_j(target, true);
2054 Assembler::swapCodeChunks() {
2056 codeAlloc(exitStart, exitEnd, _nExitIns verbose_only(, exitBytes));
2058 _nExitSlot = exitStart;
2059 SWAP(NIns*, _nIns, _nExitIns);
2060 SWAP(NIns*, _nSlot, _nExitSlot);
2061 SWAP(NIns*, codeStart, exitStart);
2062 SWAP(NIns*, codeEnd, exitEnd);
2063 verbose_only( SWAP(size_t, codeBytes, exitBytes); )
2067 Assembler::asm_insert_random_nop() {
2068 NanoAssert(0); // not supported
2073 #endif // FEATURE_NANOJIT && NANOJIT_MIPS