| /* |
| * Copyright (C) 2009-2011 STMicroelectronics. All rights reserved. |
| * Copyright (C) 2008 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #ifndef SH4Assembler_h |
| #define SH4Assembler_h |
| |
| #if ENABLE(ASSEMBLER) && CPU(SH4) |
| |
| #include "AssemblerBuffer.h" |
| #include "AssemblerBufferWithConstantPool.h" |
| #include "JITCompilationEffort.h" |
| #include <stdarg.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <wtf/Assertions.h> |
| #include <wtf/DataLog.h> |
| #include <wtf/Vector.h> |
| |
| #ifndef NDEBUG |
| #define SH4_ASSEMBLER_TRACING |
| #endif |
| |
| namespace JSC { |
| typedef uint16_t SH4Word; |
| |
| enum { |
| INVALID_OPCODE = 0xffff, |
| ADD_OPCODE = 0x300c, |
| ADDIMM_OPCODE = 0x7000, |
| ADDC_OPCODE = 0x300e, |
| ADDV_OPCODE = 0x300f, |
| AND_OPCODE = 0x2009, |
| ANDIMM_OPCODE = 0xc900, |
| DIV0_OPCODE = 0x2007, |
| DIV1_OPCODE = 0x3004, |
| BF_OPCODE = 0x8b00, |
| BFS_OPCODE = 0x8f00, |
| BRA_OPCODE = 0xa000, |
| BRAF_OPCODE = 0x0023, |
| NOP_OPCODE = 0x0009, |
| BSR_OPCODE = 0xb000, |
| RTS_OPCODE = 0x000b, |
| BT_OPCODE = 0x8900, |
| BTS_OPCODE = 0x8d00, |
| BSRF_OPCODE = 0x0003, |
| BRK_OPCODE = 0x003b, |
| FTRC_OPCODE = 0xf03d, |
| CMPEQ_OPCODE = 0x3000, |
| CMPEQIMM_OPCODE = 0x8800, |
| CMPGE_OPCODE = 0x3003, |
| CMPGT_OPCODE = 0x3007, |
| CMPHI_OPCODE = 0x3006, |
| CMPHS_OPCODE = 0x3002, |
| CMPPL_OPCODE = 0x4015, |
| CMPPZ_OPCODE = 0x4011, |
| CMPSTR_OPCODE = 0x200c, |
| DT_OPCODE = 0x4010, |
| FCMPEQ_OPCODE = 0xf004, |
| FCMPGT_OPCODE = 0xf005, |
| FMOV_OPCODE = 0xf00c, |
| FADD_OPCODE = 0xf000, |
| FMUL_OPCODE = 0xf002, |
| FSUB_OPCODE = 0xf001, |
| FDIV_OPCODE = 0xf003, |
| FNEG_OPCODE = 0xf04d, |
| JMP_OPCODE = 0x402b, |
| JSR_OPCODE = 0x400b, |
| LDSPR_OPCODE = 0x402a, |
| LDSLPR_OPCODE = 0x4026, |
| MOV_OPCODE = 0x6003, |
| MOVIMM_OPCODE = 0xe000, |
| MOVB_WRITE_RN_OPCODE = 0x2000, |
| MOVB_WRITE_RNDEC_OPCODE = 0x2004, |
| MOVB_WRITE_R0RN_OPCODE = 0x0004, |
| MOVB_WRITE_OFFGBR_OPCODE = 0xc000, |
| MOVB_WRITE_OFFRN_OPCODE = 0x8000, |
| MOVB_READ_RM_OPCODE = 0x6000, |
| MOVB_READ_RMINC_OPCODE = 0x6004, |
| MOVB_READ_R0RM_OPCODE = 0x000c, |
| MOVB_READ_OFFGBR_OPCODE = 0xc400, |
| MOVB_READ_OFFRM_OPCODE = 0x8400, |
| MOVL_WRITE_RN_OPCODE = 0x2002, |
| MOVL_WRITE_RNDEC_OPCODE = 0x2006, |
| MOVL_WRITE_R0RN_OPCODE = 0x0006, |
| MOVL_WRITE_OFFGBR_OPCODE = 0xc200, |
| MOVL_WRITE_OFFRN_OPCODE = 0x1000, |
| MOVL_READ_RM_OPCODE = 0x6002, |
| MOVL_READ_RMINC_OPCODE = 0x6006, |
| MOVL_READ_R0RM_OPCODE = 0x000e, |
| MOVL_READ_OFFGBR_OPCODE = 0xc600, |
| MOVL_READ_OFFPC_OPCODE = 0xd000, |
| MOVL_READ_OFFRM_OPCODE = 0x5000, |
| MOVW_WRITE_RN_OPCODE = 0x2001, |
| MOVW_READ_RM_OPCODE = 0x6001, |
| MOVW_READ_R0RM_OPCODE = 0x000d, |
| MOVW_READ_OFFRM_OPCODE = 0x8500, |
| MOVW_READ_OFFPC_OPCODE = 0x9000, |
| MOVA_READ_OFFPC_OPCODE = 0xc700, |
| MOVT_OPCODE = 0x0029, |
| MULL_OPCODE = 0x0007, |
| DMULL_L_OPCODE = 0x3005, |
| STSMACL_OPCODE = 0x001a, |
| STSMACH_OPCODE = 0x000a, |
| DMULSL_OPCODE = 0x300d, |
| NEG_OPCODE = 0x600b, |
| NEGC_OPCODE = 0x600a, |
| NOT_OPCODE = 0x6007, |
| OR_OPCODE = 0x200b, |
| ORIMM_OPCODE = 0xcb00, |
| ORBIMM_OPCODE = 0xcf00, |
| SETS_OPCODE = 0x0058, |
| SETT_OPCODE = 0x0018, |
| SHAD_OPCODE = 0x400c, |
| SHAL_OPCODE = 0x4020, |
| SHAR_OPCODE = 0x4021, |
| SHLD_OPCODE = 0x400d, |
| SHLL_OPCODE = 0x4000, |
| SHLL2_OPCODE = 0x4008, |
| SHLL8_OPCODE = 0x4018, |
| SHLL16_OPCODE = 0x4028, |
| SHLR_OPCODE = 0x4001, |
| SHLR2_OPCODE = 0x4009, |
| SHLR8_OPCODE = 0x4019, |
| SHLR16_OPCODE = 0x4029, |
| STSPR_OPCODE = 0x002a, |
| STSLPR_OPCODE = 0x4022, |
| FLOAT_OPCODE = 0xf02d, |
| SUB_OPCODE = 0x3008, |
| SUBC_OPCODE = 0x300a, |
| SUBV_OPCODE = 0x300b, |
| TST_OPCODE = 0x2008, |
| TSTIMM_OPCODE = 0xc800, |
| TSTB_OPCODE = 0xcc00, |
| EXTUB_OPCODE = 0x600c, |
| EXTUW_OPCODE = 0x600d, |
| XOR_OPCODE = 0x200a, |
| XORIMM_OPCODE = 0xca00, |
| XORB_OPCODE = 0xce00, |
| FMOVS_READ_RM_INC_OPCODE = 0xf009, |
| FMOVS_READ_RM_OPCODE = 0xf008, |
| FMOVS_READ_R0RM_OPCODE = 0xf006, |
| FMOVS_WRITE_RN_OPCODE = 0xf00a, |
| FMOVS_WRITE_RN_DEC_OPCODE = 0xf00b, |
| FMOVS_WRITE_R0RN_OPCODE = 0xf007, |
| FCNVDS_DRM_FPUL_OPCODE = 0xf0bd, |
| FCNVSD_FPUL_DRN_OPCODE = 0xf0ad, |
| LDS_RM_FPUL_OPCODE = 0x405a, |
| FLDS_FRM_FPUL_OPCODE = 0xf01d, |
| STS_FPUL_RN_OPCODE = 0x005a, |
| FSTS_FPUL_FRN_OPCODE = 0xF00d, |
| LDSFPSCR_OPCODE = 0x406a, |
| STSFPSCR_OPCODE = 0x006a, |
| LDSRMFPUL_OPCODE = 0x405a, |
| FSTSFPULFRN_OPCODE = 0xf00d, |
| FSQRT_OPCODE = 0xf06d, |
| FSCHG_OPCODE = 0xf3fd, |
| CLRT_OPCODE = 8, |
| }; |
| |
| namespace SH4Registers { |
| typedef enum { |
| r0, |
| r1, |
| r2, |
| r3, |
| r4, |
| r5, |
| r6, |
| r7, |
| r8, |
| r9, |
| r10, |
| r11, |
| r12, |
| r13, |
| r14, fp = r14, |
| r15, sp = r15, |
| pc, |
| pr, |
| } RegisterID; |
| |
| typedef enum { |
| fr0, dr0 = fr0, |
| fr1, |
| fr2, dr2 = fr2, |
| fr3, |
| fr4, dr4 = fr4, |
| fr5, |
| fr6, dr6 = fr6, |
| fr7, |
| fr8, dr8 = fr8, |
| fr9, |
| fr10, dr10 = fr10, |
| fr11, |
| fr12, dr12 = fr12, |
| fr13, |
| fr14, dr14 = fr14, |
| fr15, |
| } FPRegisterID; |
| } |
| |
| inline uint16_t getOpcodeGroup1(uint16_t opc, int rm, int rn) |
| { |
| return (opc | ((rm & 0xf) << 8) | ((rn & 0xf) << 4)); |
| } |
| |
| inline uint16_t getOpcodeGroup2(uint16_t opc, int rm) |
| { |
| return (opc | ((rm & 0xf) << 8)); |
| } |
| |
| inline uint16_t getOpcodeGroup3(uint16_t opc, int rm, int rn) |
| { |
| return (opc | ((rm & 0xf) << 8) | (rn & 0xff)); |
| } |
| |
| inline uint16_t getOpcodeGroup4(uint16_t opc, int rm, int rn, int offset) |
| { |
| return (opc | ((rm & 0xf) << 8) | ((rn & 0xf) << 4) | (offset & 0xf)); |
| } |
| |
| inline uint16_t getOpcodeGroup5(uint16_t opc, int rm) |
| { |
| return (opc | (rm & 0xff)); |
| } |
| |
| inline uint16_t getOpcodeGroup6(uint16_t opc, int rm) |
| { |
| return (opc | (rm & 0xfff)); |
| } |
| |
| inline uint16_t getOpcodeGroup7(uint16_t opc, int rm) |
| { |
| return (opc | ((rm & 0x7) << 9)); |
| } |
| |
| inline uint16_t getOpcodeGroup8(uint16_t opc, int rm, int rn) |
| { |
| return (opc | ((rm & 0x7) << 9) | ((rn & 0x7) << 5)); |
| } |
| |
| inline uint16_t getOpcodeGroup9(uint16_t opc, int rm, int rn) |
| { |
| return (opc | ((rm & 0xf) << 8) | ((rn & 0x7) << 5)); |
| } |
| |
| inline uint16_t getOpcodeGroup10(uint16_t opc, int rm, int rn) |
| { |
| return (opc | ((rm & 0x7) << 9) | ((rn & 0xf) << 4)); |
| } |
| |
| inline uint16_t getOpcodeGroup11(uint16_t opc, int rm, int rn) |
| { |
| return (opc | ((rm & 0xf) << 4) | (rn & 0xf)); |
| } |
| |
| inline uint16_t getRn(uint16_t x) |
| { |
| return ((x & 0xf00) >> 8); |
| } |
| |
| inline uint16_t getRm(uint16_t x) |
| { |
| return ((x & 0xf0) >> 4); |
| } |
| |
| inline uint16_t getDisp(uint16_t x) |
| { |
| return (x & 0xf); |
| } |
| |
| inline uint16_t getImm8(uint16_t x) |
| { |
| return (x & 0xff); |
| } |
| |
| inline uint16_t getImm12(uint16_t x) |
| { |
| return (x & 0xfff); |
| } |
| |
| inline uint16_t getDRn(uint16_t x) |
| { |
| return ((x & 0xe00) >> 9); |
| } |
| |
| inline uint16_t getDRm(uint16_t x) |
| { |
| return ((x & 0xe0) >> 5); |
| } |
| |
| class SH4Assembler { |
| public: |
| typedef SH4Registers::RegisterID RegisterID; |
| typedef SH4Registers::FPRegisterID FPRegisterID; |
| typedef AssemblerBufferWithConstantPool<512, 4, 2, SH4Assembler> SH4Buffer; |
| static const RegisterID scratchReg1 = SH4Registers::r3; |
| static const RegisterID scratchReg2 = SH4Registers::r11; |
| static const uint32_t maxInstructionSize = 16; |
| |
| enum { |
| padForAlign8 = 0x00, |
| padForAlign16 = 0x0009, |
| padForAlign32 = 0x00090009, |
| }; |
| |
| enum JumpType { |
| JumpFar, |
| JumpNear |
| }; |
| |
| SH4Assembler() |
| { |
| m_claimscratchReg = 0x0; |
| } |
| |
| // SH4 condition codes |
| typedef enum { |
| EQ = 0x0, // Equal |
| NE = 0x1, // Not Equal |
| HS = 0x2, // Unsigend Greater Than equal |
| HI = 0x3, // Unsigend Greater Than |
| LS = 0x4, // Unsigend Lower or Same |
| LI = 0x5, // Unsigend Lower |
| GE = 0x6, // Greater or Equal |
| LT = 0x7, // Less Than |
| GT = 0x8, // Greater Than |
| LE = 0x9, // Less or Equal |
| OF = 0xa, // OverFlow |
| SI = 0xb, // Signed |
| EQU= 0xc, // Equal or unordered(NaN) |
| NEU= 0xd, |
| GTU= 0xe, |
| GEU= 0xf, |
| LTU= 0x10, |
| LEU= 0x11, |
| } Condition; |
| |
| // Opaque label types |
| public: |
| bool isImmediate(int constant) |
| { |
| return ((constant <= 127) && (constant >= -128)); |
| } |
| |
| RegisterID claimScratch() |
| { |
| ASSERT((m_claimscratchReg != 0x3)); |
| |
| if (!(m_claimscratchReg & 0x1)) { |
| m_claimscratchReg = (m_claimscratchReg | 0x1); |
| return scratchReg1; |
| } |
| |
| m_claimscratchReg = (m_claimscratchReg | 0x2); |
| return scratchReg2; |
| } |
| |
| void releaseScratch(RegisterID scratchR) |
| { |
| if (scratchR == scratchReg1) |
| m_claimscratchReg = (m_claimscratchReg & 0x2); |
| else |
| m_claimscratchReg = (m_claimscratchReg & 0x1); |
| } |
| |
| // Stack operations |
| |
| void pushReg(RegisterID reg) |
| { |
| if (reg == SH4Registers::pr) { |
| oneShortOp(getOpcodeGroup2(STSLPR_OPCODE, SH4Registers::sp)); |
| return; |
| } |
| |
| oneShortOp(getOpcodeGroup1(MOVL_WRITE_RNDEC_OPCODE, SH4Registers::sp, reg)); |
| } |
| |
| void popReg(RegisterID reg) |
| { |
| if (reg == SH4Registers::pr) { |
| oneShortOp(getOpcodeGroup2(LDSLPR_OPCODE, SH4Registers::sp)); |
| return; |
| } |
| |
| oneShortOp(getOpcodeGroup1(MOVL_READ_RMINC_OPCODE, reg, SH4Registers::sp)); |
| } |
| |
| void movt(RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup2(MOVT_OPCODE, dst); |
| oneShortOp(opc); |
| } |
| |
| // Arithmetic operations |
| |
| void addlRegReg(RegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(ADD_OPCODE, dst, src); |
| oneShortOp(opc); |
| } |
| |
| void addclRegReg(RegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(ADDC_OPCODE, dst, src); |
| oneShortOp(opc); |
| } |
| |
| void addvlRegReg(RegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(ADDV_OPCODE, dst, src); |
| oneShortOp(opc); |
| } |
| |
| void addlImm8r(int imm8, RegisterID dst) |
| { |
| ASSERT((imm8 <= 127) && (imm8 >= -128)); |
| |
| uint16_t opc = getOpcodeGroup3(ADDIMM_OPCODE, dst, imm8); |
| oneShortOp(opc); |
| } |
| |
| void andlRegReg(RegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(AND_OPCODE, dst, src); |
| oneShortOp(opc); |
| } |
| |
| void andlImm8r(int imm8, RegisterID dst) |
| { |
| ASSERT((imm8 <= 255) && (imm8 >= 0)); |
| ASSERT(dst == SH4Registers::r0); |
| |
| uint16_t opc = getOpcodeGroup5(ANDIMM_OPCODE, imm8); |
| oneShortOp(opc); |
| } |
| |
| void div1lRegReg(RegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(DIV1_OPCODE, dst, src); |
| oneShortOp(opc); |
| } |
| |
| void div0lRegReg(RegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(DIV0_OPCODE, dst, src); |
| oneShortOp(opc); |
| } |
| |
| void notlReg(RegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(NOT_OPCODE, dst, src); |
| oneShortOp(opc); |
| } |
| |
| void orlRegReg(RegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(OR_OPCODE, dst, src); |
| oneShortOp(opc); |
| } |
| |
| void orlImm8r(int imm8, RegisterID dst) |
| { |
| ASSERT((imm8 <= 255) && (imm8 >= 0)); |
| ASSERT(dst == SH4Registers::r0); |
| |
| uint16_t opc = getOpcodeGroup5(ORIMM_OPCODE, imm8); |
| oneShortOp(opc); |
| } |
| |
| void sublRegReg(RegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(SUB_OPCODE, dst, src); |
| oneShortOp(opc); |
| } |
| |
| void subvlRegReg(RegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(SUBV_OPCODE, dst, src); |
| oneShortOp(opc); |
| } |
| |
| void xorlRegReg(RegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(XOR_OPCODE, dst, src); |
| oneShortOp(opc); |
| } |
| |
| void xorlImm8r(int imm8, RegisterID dst) |
| { |
| ASSERT((imm8 <= 255) && (imm8 >= 0)); |
| ASSERT(dst == SH4Registers::r0); |
| |
| uint16_t opc = getOpcodeGroup5(XORIMM_OPCODE, imm8); |
| oneShortOp(opc); |
| } |
| |
| void shllImm8r(int imm, RegisterID dst) |
| { |
| switch (imm) { |
| case 1: |
| oneShortOp(getOpcodeGroup2(SHLL_OPCODE, dst)); |
| break; |
| case 2: |
| oneShortOp(getOpcodeGroup2(SHLL2_OPCODE, dst)); |
| break; |
| case 8: |
| oneShortOp(getOpcodeGroup2(SHLL8_OPCODE, dst)); |
| break; |
| case 16: |
| oneShortOp(getOpcodeGroup2(SHLL16_OPCODE, dst)); |
| break; |
| default: |
| ASSERT_NOT_REACHED(); |
| } |
| } |
| |
| void neg(RegisterID dst, RegisterID src) |
| { |
| uint16_t opc = getOpcodeGroup1(NEG_OPCODE, dst, src); |
| oneShortOp(opc); |
| } |
| |
| void shllRegReg(RegisterID dst, RegisterID rShift) |
| { |
| uint16_t opc = getOpcodeGroup1(SHLD_OPCODE, dst, rShift); |
| oneShortOp(opc); |
| } |
| |
| void shlrRegReg(RegisterID dst, RegisterID rShift) |
| { |
| neg(rShift, rShift); |
| shllRegReg(dst, rShift); |
| } |
| |
| void sharRegReg(RegisterID dst, RegisterID rShift) |
| { |
| neg(rShift, rShift); |
| shaRegReg(dst, rShift); |
| } |
| |
| void shaRegReg(RegisterID dst, RegisterID rShift) |
| { |
| uint16_t opc = getOpcodeGroup1(SHAD_OPCODE, dst, rShift); |
| oneShortOp(opc); |
| } |
| |
| void shlrImm8r(int imm, RegisterID dst) |
| { |
| switch (imm) { |
| case 1: |
| oneShortOp(getOpcodeGroup2(SHLR_OPCODE, dst)); |
| break; |
| case 2: |
| oneShortOp(getOpcodeGroup2(SHLR2_OPCODE, dst)); |
| break; |
| case 8: |
| oneShortOp(getOpcodeGroup2(SHLR8_OPCODE, dst)); |
| break; |
| case 16: |
| oneShortOp(getOpcodeGroup2(SHLR16_OPCODE, dst)); |
| break; |
| default: |
| ASSERT_NOT_REACHED(); |
| } |
| } |
| |
| void imullRegReg(RegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(MULL_OPCODE, dst, src); |
| oneShortOp(opc); |
| } |
| |
| void dmullRegReg(RegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(DMULL_L_OPCODE, dst, src); |
| oneShortOp(opc); |
| } |
| |
| void dmulslRegReg(RegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(DMULSL_OPCODE, dst, src); |
| oneShortOp(opc); |
| } |
| |
| void stsmacl(RegisterID reg) |
| { |
| uint16_t opc = getOpcodeGroup2(STSMACL_OPCODE, reg); |
| oneShortOp(opc); |
| } |
| |
| void stsmach(RegisterID reg) |
| { |
| uint16_t opc = getOpcodeGroup2(STSMACH_OPCODE, reg); |
| oneShortOp(opc); |
| } |
| |
| // Comparisons |
| |
| void cmplRegReg(RegisterID left, RegisterID right, Condition cond) |
| { |
| switch (cond) { |
| case NE: |
| oneShortOp(getOpcodeGroup1(CMPEQ_OPCODE, right, left)); |
| break; |
| case GT: |
| oneShortOp(getOpcodeGroup1(CMPGT_OPCODE, right, left)); |
| break; |
| case EQ: |
| oneShortOp(getOpcodeGroup1(CMPEQ_OPCODE, right, left)); |
| break; |
| case GE: |
| oneShortOp(getOpcodeGroup1(CMPGE_OPCODE, right, left)); |
| break; |
| case HS: |
| oneShortOp(getOpcodeGroup1(CMPHS_OPCODE, right, left)); |
| break; |
| case HI: |
| oneShortOp(getOpcodeGroup1(CMPHI_OPCODE, right, left)); |
| break; |
| case LI: |
| oneShortOp(getOpcodeGroup1(CMPHI_OPCODE, left, right)); |
| break; |
| case LS: |
| oneShortOp(getOpcodeGroup1(CMPHS_OPCODE, left, right)); |
| break; |
| case LE: |
| oneShortOp(getOpcodeGroup1(CMPGE_OPCODE, left, right)); |
| break; |
| case LT: |
| oneShortOp(getOpcodeGroup1(CMPGT_OPCODE, left, right)); |
| break; |
| default: |
| ASSERT_NOT_REACHED(); |
| } |
| } |
| |
| void cmppl(RegisterID reg) |
| { |
| uint16_t opc = getOpcodeGroup2(CMPPL_OPCODE, reg); |
| oneShortOp(opc); |
| } |
| |
| void cmppz(RegisterID reg) |
| { |
| uint16_t opc = getOpcodeGroup2(CMPPZ_OPCODE, reg); |
| oneShortOp(opc); |
| } |
| |
| void cmpEqImmR0(int imm, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup5(CMPEQIMM_OPCODE, imm); |
| oneShortOp(opc); |
| } |
| |
| void testlRegReg(RegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(TST_OPCODE, dst, src); |
| oneShortOp(opc); |
| } |
| |
| void testlImm8r(int imm, RegisterID dst) |
| { |
| ASSERT((dst == SH4Registers::r0) && (imm <= 255) && (imm >= 0)); |
| |
| uint16_t opc = getOpcodeGroup5(TSTIMM_OPCODE, imm); |
| oneShortOp(opc); |
| } |
| |
| void nop() |
| { |
| oneShortOp(NOP_OPCODE, false); |
| } |
| |
| void sett() |
| { |
| oneShortOp(SETT_OPCODE); |
| } |
| |
| void clrt() |
| { |
| oneShortOp(CLRT_OPCODE); |
| } |
| |
| void fschg() |
| { |
| oneShortOp(FSCHG_OPCODE); |
| } |
| |
| void bkpt() |
| { |
| oneShortOp(BRK_OPCODE, false); |
| } |
| |
| void branch(uint16_t opc, int label) |
| { |
| switch (opc) { |
| case BT_OPCODE: |
| ASSERT((label <= 127) && (label >= -128)); |
| oneShortOp(getOpcodeGroup5(BT_OPCODE, label)); |
| break; |
| case BRA_OPCODE: |
| ASSERT((label <= 2047) && (label >= -2048)); |
| oneShortOp(getOpcodeGroup6(BRA_OPCODE, label)); |
| break; |
| case BF_OPCODE: |
| ASSERT((label <= 127) && (label >= -128)); |
| oneShortOp(getOpcodeGroup5(BF_OPCODE, label)); |
| break; |
| default: |
| ASSERT_NOT_REACHED(); |
| } |
| } |
| |
| void branch(uint16_t opc, RegisterID reg) |
| { |
| switch (opc) { |
| case BRAF_OPCODE: |
| oneShortOp(getOpcodeGroup2(BRAF_OPCODE, reg)); |
| break; |
| case JMP_OPCODE: |
| oneShortOp(getOpcodeGroup2(JMP_OPCODE, reg)); |
| break; |
| case JSR_OPCODE: |
| oneShortOp(getOpcodeGroup2(JSR_OPCODE, reg)); |
| break; |
| case BSRF_OPCODE: |
| oneShortOp(getOpcodeGroup2(BSRF_OPCODE, reg)); |
| break; |
| default: |
| ASSERT_NOT_REACHED(); |
| } |
| } |
| |
| void ldspr(RegisterID reg) |
| { |
| uint16_t opc = getOpcodeGroup2(LDSPR_OPCODE, reg); |
| oneShortOp(opc); |
| } |
| |
| void stspr(RegisterID reg) |
| { |
| uint16_t opc = getOpcodeGroup2(STSPR_OPCODE, reg); |
| oneShortOp(opc); |
| } |
| |
| void extub(RegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(EXTUB_OPCODE, dst, src); |
| oneShortOp(opc); |
| } |
| |
| void extuw(RegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(EXTUW_OPCODE, dst, src); |
| oneShortOp(opc); |
| } |
| |
| // float operations |
| |
| void ldsrmfpul(RegisterID src) |
| { |
| uint16_t opc = getOpcodeGroup2(LDS_RM_FPUL_OPCODE, src); |
| oneShortOp(opc); |
| } |
| |
| void fneg(FPRegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup2(FNEG_OPCODE, dst); |
| oneShortOp(opc, true, false); |
| } |
| |
| void fsqrt(FPRegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup2(FSQRT_OPCODE, dst); |
| oneShortOp(opc, true, false); |
| } |
| |
| void stsfpulReg(RegisterID src) |
| { |
| uint16_t opc = getOpcodeGroup2(STS_FPUL_RN_OPCODE, src); |
| oneShortOp(opc); |
| } |
| |
| void floatfpulfrn(FPRegisterID src) |
| { |
| uint16_t opc = getOpcodeGroup2(FLOAT_OPCODE, src); |
| oneShortOp(opc, true, false); |
| } |
| |
| void fmull(FPRegisterID src, FPRegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(FMUL_OPCODE, dst, src); |
| oneShortOp(opc, true, false); |
| } |
| |
| void fmovsReadrm(RegisterID src, FPRegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(FMOVS_READ_RM_OPCODE, dst, src); |
| oneShortOp(opc, true, false); |
| } |
| |
| void fmovsWriterm(FPRegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(FMOVS_WRITE_RN_OPCODE, dst, src); |
| oneShortOp(opc, true, false); |
| } |
| |
| void fmovsWriter0r(FPRegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(FMOVS_WRITE_R0RN_OPCODE, dst, src); |
| oneShortOp(opc, true, false); |
| } |
| |
| void fmovsReadr0r(RegisterID src, FPRegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(FMOVS_READ_R0RM_OPCODE, dst, src); |
| oneShortOp(opc, true, false); |
| } |
| |
| void fmovsReadrminc(RegisterID src, FPRegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(FMOVS_READ_RM_INC_OPCODE, dst, src); |
| oneShortOp(opc, true, false); |
| } |
| |
| void fmovsWriterndec(FPRegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(FMOVS_WRITE_RN_DEC_OPCODE, dst, src); |
| oneShortOp(opc, true, false); |
| } |
| |
| void ftrcRegfpul(FPRegisterID src) |
| { |
| uint16_t opc = getOpcodeGroup2(FTRC_OPCODE, src); |
| oneShortOp(opc, true, false); |
| } |
| |
| void fldsfpul(FPRegisterID src) |
| { |
| uint16_t opc = getOpcodeGroup2(FLDS_FRM_FPUL_OPCODE, src); |
| oneShortOp(opc); |
| } |
| |
| void fstsfpul(FPRegisterID src) |
| { |
| uint16_t opc = getOpcodeGroup2(FSTS_FPUL_FRN_OPCODE, src); |
| oneShortOp(opc); |
| } |
| |
| void ldsfpscr(RegisterID reg) |
| { |
| uint16_t opc = getOpcodeGroup2(LDSFPSCR_OPCODE, reg); |
| oneShortOp(opc); |
| } |
| |
| void stsfpscr(RegisterID reg) |
| { |
| uint16_t opc = getOpcodeGroup2(STSFPSCR_OPCODE, reg); |
| oneShortOp(opc); |
| } |
| |
| // double operations |
| |
| void dcnvds(FPRegisterID src) |
| { |
| uint16_t opc = getOpcodeGroup7(FCNVDS_DRM_FPUL_OPCODE, src >> 1); |
| oneShortOp(opc); |
| } |
| |
| void dcnvsd(FPRegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup7(FCNVSD_FPUL_DRN_OPCODE, dst >> 1); |
| oneShortOp(opc); |
| } |
| |
| void dcmppeq(FPRegisterID src, FPRegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup8(FCMPEQ_OPCODE, dst >> 1, src >> 1); |
| oneShortOp(opc); |
| } |
| |
| void dcmppgt(FPRegisterID src, FPRegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup8(FCMPGT_OPCODE, dst >> 1, src >> 1); |
| oneShortOp(opc); |
| } |
| |
| void dmulRegReg(FPRegisterID src, FPRegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup8(FMUL_OPCODE, dst >> 1, src >> 1); |
| oneShortOp(opc); |
| } |
| |
| void dsubRegReg(FPRegisterID src, FPRegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup8(FSUB_OPCODE, dst >> 1, src >> 1); |
| oneShortOp(opc); |
| } |
| |
| void daddRegReg(FPRegisterID src, FPRegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup8(FADD_OPCODE, dst >> 1, src >> 1); |
| oneShortOp(opc); |
| } |
| |
| void dmovRegReg(FPRegisterID src, FPRegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup8(FMOV_OPCODE, dst >> 1, src >> 1); |
| oneShortOp(opc); |
| } |
| |
| void ddivRegReg(FPRegisterID src, FPRegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup8(FDIV_OPCODE, dst >> 1, src >> 1); |
| oneShortOp(opc); |
| } |
| |
| void dsqrt(FPRegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup7(FSQRT_OPCODE, dst >> 1); |
| oneShortOp(opc); |
| } |
| |
| void dneg(FPRegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup7(FNEG_OPCODE, dst >> 1); |
| oneShortOp(opc); |
| } |
| |
| void fmovReadrm(RegisterID src, FPRegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup10(FMOVS_READ_RM_OPCODE, dst >> 1, src); |
| oneShortOp(opc); |
| } |
| |
| void fmovWriterm(FPRegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup9(FMOVS_WRITE_RN_OPCODE, dst, src >> 1); |
| oneShortOp(opc); |
| } |
| |
| void fmovWriter0r(FPRegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup9(FMOVS_WRITE_R0RN_OPCODE, dst, src >> 1); |
| oneShortOp(opc); |
| } |
| |
| void fmovReadr0r(RegisterID src, FPRegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup10(FMOVS_READ_R0RM_OPCODE, dst >> 1, src); |
| oneShortOp(opc); |
| } |
| |
| void fmovReadrminc(RegisterID src, FPRegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup10(FMOVS_READ_RM_INC_OPCODE, dst >> 1, src); |
| oneShortOp(opc); |
| } |
| |
| void fmovWriterndec(FPRegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup9(FMOVS_WRITE_RN_DEC_OPCODE, dst, src >> 1); |
| oneShortOp(opc); |
| } |
| |
| void floatfpulDreg(FPRegisterID src) |
| { |
| uint16_t opc = getOpcodeGroup7(FLOAT_OPCODE, src >> 1); |
| oneShortOp(opc); |
| } |
| |
| void ftrcdrmfpul(FPRegisterID src) |
| { |
| uint16_t opc = getOpcodeGroup7(FTRC_OPCODE, src >> 1); |
| oneShortOp(opc); |
| } |
| |
| // Various move ops |
| |
| void movImm8(int imm8, RegisterID dst) |
| { |
| ASSERT((imm8 <= 127) && (imm8 >= -128)); |
| |
| uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, imm8); |
| oneShortOp(opc); |
| } |
| |
| void movlRegReg(RegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(MOV_OPCODE, dst, src); |
| oneShortOp(opc); |
| } |
| |
| void movwRegMem(RegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(MOVW_WRITE_RN_OPCODE, dst, src); |
| oneShortOp(opc); |
| } |
| |
| void movwMemReg(RegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(MOVW_READ_RM_OPCODE, dst, src); |
| oneShortOp(opc); |
| } |
| |
| void movwPCReg(int offset, RegisterID base, RegisterID dst) |
| { |
| ASSERT(base == SH4Registers::pc); |
| ASSERT((offset <= 255) && (offset >= 0)); |
| |
| uint16_t opc = getOpcodeGroup3(MOVW_READ_OFFPC_OPCODE, dst, offset); |
| oneShortOp(opc); |
| } |
| |
| void movwMemReg(int offset, RegisterID base, RegisterID dst) |
| { |
| ASSERT(dst == SH4Registers::r0); |
| |
| uint16_t opc = getOpcodeGroup11(MOVW_READ_OFFRM_OPCODE, base, offset); |
| oneShortOp(opc); |
| } |
| |
| void movwR0mr(RegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(MOVW_READ_R0RM_OPCODE, dst, src); |
| oneShortOp(opc); |
| } |
| |
| void movlRegMem(RegisterID src, int offset, RegisterID base) |
| { |
| ASSERT((offset <= 15) && (offset >= 0)); |
| |
| if (!offset) { |
| oneShortOp(getOpcodeGroup1(MOVL_WRITE_RN_OPCODE, base, src)); |
| return; |
| } |
| |
| oneShortOp(getOpcodeGroup4(MOVL_WRITE_OFFRN_OPCODE, base, src, offset)); |
| } |
| |
| void movlRegMem(RegisterID src, RegisterID base) |
| { |
| uint16_t opc = getOpcodeGroup1(MOVL_WRITE_RN_OPCODE, base, src); |
| oneShortOp(opc); |
| } |
| |
| void movlMemReg(int offset, RegisterID base, RegisterID dst) |
| { |
| if (base == SH4Registers::pc) { |
| ASSERT((offset <= 255) && (offset >= 0)); |
| oneShortOp(getOpcodeGroup3(MOVL_READ_OFFPC_OPCODE, dst, offset)); |
| return; |
| } |
| |
| ASSERT((offset <= 15) && (offset >= 0)); |
| if (!offset) { |
| oneShortOp(getOpcodeGroup1(MOVL_READ_RM_OPCODE, dst, base)); |
| return; |
| } |
| |
| oneShortOp(getOpcodeGroup4(MOVL_READ_OFFRM_OPCODE, dst, base, offset)); |
| } |
| |
| void movlMemRegCompact(int offset, RegisterID base, RegisterID dst) |
| { |
| oneShortOp(getOpcodeGroup4(MOVL_READ_OFFRM_OPCODE, dst, base, offset)); |
| } |
| |
| void movbRegMem(RegisterID src, RegisterID base) |
| { |
| uint16_t opc = getOpcodeGroup1(MOVB_WRITE_RN_OPCODE, base, src); |
| oneShortOp(opc); |
| } |
| |
| void movbMemReg(int offset, RegisterID base, RegisterID dst) |
| { |
| ASSERT(dst == SH4Registers::r0); |
| |
| uint16_t opc = getOpcodeGroup11(MOVB_READ_OFFRM_OPCODE, base, offset); |
| oneShortOp(opc); |
| } |
| |
| void movbR0mr(RegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(MOVB_READ_R0RM_OPCODE, dst, src); |
| oneShortOp(opc); |
| } |
| |
| void movbMemReg(RegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(MOVB_READ_RM_OPCODE, dst, src); |
| oneShortOp(opc); |
| } |
| |
| void movlMemReg(RegisterID base, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(MOVL_READ_RM_OPCODE, dst, base); |
| oneShortOp(opc); |
| } |
| |
| void movlMemRegIn(RegisterID base, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(MOVL_READ_RMINC_OPCODE, dst, base); |
| oneShortOp(opc); |
| } |
| |
| void movlR0mr(RegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(MOVL_READ_R0RM_OPCODE, dst, src); |
| oneShortOp(opc); |
| } |
| |
| void movlRegMemr0(RegisterID src, RegisterID dst) |
| { |
| uint16_t opc = getOpcodeGroup1(MOVL_WRITE_R0RN_OPCODE, dst, src); |
| oneShortOp(opc); |
| } |
| |
| void movlImm8r(int imm8, RegisterID dst) |
| { |
| ASSERT((imm8 <= 127) && (imm8 >= -128)); |
| |
| uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, imm8); |
| oneShortOp(opc); |
| } |
| |
| void loadConstant(uint32_t constant, RegisterID dst) |
| { |
| if (((int)constant <= 0x7f) && ((int)constant >= -0x80)) { |
| movImm8(constant, dst); |
| return; |
| } |
| |
| uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, 0); |
| |
| m_buffer.ensureSpace(maxInstructionSize, sizeof(uint32_t)); |
| printInstr(getOpcodeGroup3(MOVIMM_OPCODE, dst, constant), m_buffer.codeSize()); |
| m_buffer.putShortWithConstantInt(opc, constant, true); |
| } |
| |
| void loadConstantUnReusable(uint32_t constant, RegisterID dst, bool ensureSpace = false) |
| { |
| uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, 0); |
| |
| if (ensureSpace) |
| m_buffer.ensureSpace(maxInstructionSize, sizeof(uint32_t)); |
| |
| printInstr(getOpcodeGroup3(MOVIMM_OPCODE, dst, constant), m_buffer.codeSize()); |
| m_buffer.putShortWithConstantInt(opc, constant); |
| } |
| |
| // Flow control |
| |
| AssemblerLabel call() |
| { |
| RegisterID scr = claimScratch(); |
| m_buffer.ensureSpace(maxInstructionSize + 4, sizeof(uint32_t)); |
| loadConstantUnReusable(0x0, scr); |
| branch(JSR_OPCODE, scr); |
| nop(); |
| releaseScratch(scr); |
| return m_buffer.label(); |
| } |
| |
| AssemblerLabel call(RegisterID dst) |
| { |
| m_buffer.ensureSpace(maxInstructionSize + 2); |
| branch(JSR_OPCODE, dst); |
| nop(); |
| return m_buffer.label(); |
| } |
| |
| AssemblerLabel jmp() |
| { |
| RegisterID scr = claimScratch(); |
| m_buffer.ensureSpace(maxInstructionSize + 4, sizeof(uint32_t)); |
| AssemblerLabel label = m_buffer.label(); |
| loadConstantUnReusable(0x0, scr); |
| branch(BRAF_OPCODE, scr); |
| nop(); |
| releaseScratch(scr); |
| return label; |
| } |
| |
| void extraInstrForBranch(RegisterID dst) |
| { |
| loadConstantUnReusable(0x0, dst); |
| nop(); |
| nop(); |
| } |
| |
| AssemblerLabel jmp(RegisterID dst) |
| { |
| jmpReg(dst); |
| return m_buffer.label(); |
| } |
| |
| void jmpReg(RegisterID dst) |
| { |
| m_buffer.ensureSpace(maxInstructionSize + 2); |
| branch(JMP_OPCODE, dst); |
| nop(); |
| } |
| |
| AssemblerLabel jne() |
| { |
| AssemblerLabel label = m_buffer.label(); |
| branch(BF_OPCODE, 0); |
| return label; |
| } |
| |
| AssemblerLabel je() |
| { |
| AssemblerLabel label = m_buffer.label(); |
| branch(BT_OPCODE, 0); |
| return label; |
| } |
| |
| AssemblerLabel bra() |
| { |
| AssemblerLabel label = m_buffer.label(); |
| branch(BRA_OPCODE, 0); |
| return label; |
| } |
| |
| void ret() |
| { |
| m_buffer.ensureSpace(maxInstructionSize + 2); |
| oneShortOp(RTS_OPCODE, false); |
| } |
| |
| AssemblerLabel labelIgnoringWatchpoints() |
| { |
| m_buffer.ensureSpaceForAnyInstruction(); |
| return m_buffer.label(); |
| } |
| |
| AssemblerLabel label() |
| { |
| m_buffer.ensureSpaceForAnyInstruction(); |
| return m_buffer.label(); |
| } |
| |
| int sizeOfConstantPool() |
| { |
| return m_buffer.sizeOfConstantPool(); |
| } |
| |
| AssemblerLabel align(int alignment) |
| { |
| m_buffer.ensureSpace(maxInstructionSize + 2); |
| while (!m_buffer.isAligned(alignment)) { |
| nop(); |
| m_buffer.ensureSpace(maxInstructionSize + 2); |
| } |
| return label(); |
| } |
| |
| static void changePCrelativeAddress(int offset, uint16_t* instructionPtr, uint32_t newAddress) |
| { |
| uint32_t address = (offset << 2) + ((reinterpret_cast<uint32_t>(instructionPtr) + 4) &(~0x3)); |
| *reinterpret_cast<uint32_t*>(address) = newAddress; |
| } |
| |
| static uint32_t readPCrelativeAddress(int offset, uint16_t* instructionPtr) |
| { |
| uint32_t address = (offset << 2) + ((reinterpret_cast<uint32_t>(instructionPtr) + 4) &(~0x3)); |
| return *reinterpret_cast<uint32_t*>(address); |
| } |
| |
| static uint16_t* getInstructionPtr(void* code, int offset) |
| { |
| return reinterpret_cast<uint16_t*> (reinterpret_cast<uint32_t>(code) + offset); |
| } |
| |
| static void linkJump(void* code, AssemblerLabel from, void* to) |
| { |
| ASSERT(from.isSet()); |
| |
| uint16_t* instructionPtr = getInstructionPtr(code, from.m_offset); |
| uint16_t instruction = *instructionPtr; |
| int offsetBits = (reinterpret_cast<uint32_t>(to) - reinterpret_cast<uint32_t>(code)) - from.m_offset; |
| |
| if (((instruction & 0xff00) == BT_OPCODE) || ((instruction & 0xff00) == BF_OPCODE)) { |
| /* BT label ==> BF 2 |
| nop LDR reg |
| nop braf @reg |
| nop nop |
| */ |
| offsetBits -= 8; |
| instruction ^= 0x0202; |
| *instructionPtr++ = instruction; |
| changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits); |
| instruction = (BRAF_OPCODE | (*instructionPtr++ & 0xf00)); |
| *instructionPtr = instruction; |
| printBlockInstr(instructionPtr - 2, from.m_offset, 3); |
| return; |
| } |
| |
| /* MOV #imm, reg => LDR reg |
| braf @reg braf @reg |
| nop nop |
| */ |
| ASSERT((*(instructionPtr + 1) & BRAF_OPCODE) == BRAF_OPCODE); |
| |
| offsetBits -= 4; |
| if (offsetBits >= -4096 && offsetBits <= 4094) { |
| *instructionPtr = getOpcodeGroup6(BRA_OPCODE, offsetBits >> 1); |
| *(++instructionPtr) = NOP_OPCODE; |
| printBlockInstr(instructionPtr - 1, from.m_offset, 2); |
| return; |
| } |
| |
| changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits - 2); |
| printInstr(*instructionPtr, from.m_offset + 2); |
| } |
| |
| static void linkCall(void* code, AssemblerLabel from, void* to) |
| { |
| uint16_t* instructionPtr = getInstructionPtr(code, from.m_offset); |
| instructionPtr -= 3; |
| changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, reinterpret_cast<uint32_t>(to)); |
| } |
| |
| static void linkPointer(void* code, AssemblerLabel where, void* value) |
| { |
| uint16_t* instructionPtr = getInstructionPtr(code, where.m_offset); |
| changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, reinterpret_cast<uint32_t>(value)); |
| } |
| |
| static unsigned getCallReturnOffset(AssemblerLabel call) |
| { |
| ASSERT(call.isSet()); |
| return call.m_offset; |
| } |
| |
| static uint32_t* getLdrImmAddressOnPool(SH4Word* insn, uint32_t* constPool) |
| { |
| return (constPool + (*insn & 0xff)); |
| } |
| |
| static SH4Word patchConstantPoolLoad(SH4Word load, int value) |
| { |
| return ((load & ~0xff) | value); |
| } |
| |
| static SH4Buffer::TwoShorts placeConstantPoolBarrier(int offset) |
| { |
| ASSERT(((offset >> 1) <=2047) && ((offset >> 1) >= -2048)); |
| |
| SH4Buffer::TwoShorts m_barrier; |
| m_barrier.high = (BRA_OPCODE | (offset >> 1)); |
| m_barrier.low = NOP_OPCODE; |
| printInstr(((BRA_OPCODE | (offset >> 1))), 0); |
| printInstr(NOP_OPCODE, 0); |
| return m_barrier; |
| } |
| |
| static void patchConstantPoolLoad(void* loadAddr, void* constPoolAddr) |
| { |
| SH4Word* instructionPtr = reinterpret_cast<SH4Word*>(loadAddr); |
| SH4Word instruction = *instructionPtr; |
| SH4Word index = instruction & 0xff; |
| |
| if ((instruction & 0xf000) != MOVIMM_OPCODE) |
| return; |
| |
| ASSERT((((reinterpret_cast<uint32_t>(constPoolAddr) - reinterpret_cast<uint32_t>(loadAddr)) + index * 4)) < 1024); |
| |
| int offset = reinterpret_cast<uint32_t>(constPoolAddr) + (index * 4) - ((reinterpret_cast<uint32_t>(instructionPtr) & ~0x03) + 4); |
| instruction &=0xf00; |
| instruction |= 0xd000; |
| offset &= 0x03ff; |
| instruction |= (offset >> 2); |
| *instructionPtr = instruction; |
| printInstr(instruction, reinterpret_cast<uint32_t>(loadAddr)); |
| } |
| |
| static void repatchPointer(void* where, void* value) |
| { |
| patchPointer(where, value); |
| } |
| |
| static void* readPointer(void* code) |
| { |
| return reinterpret_cast<void*>(readInt32(code)); |
| } |
| |
| static void repatchInt32(void* where, int32_t value) |
| { |
| uint16_t* instructionPtr = reinterpret_cast<uint16_t*>(where); |
| changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, value); |
| } |
| |
| static void repatchCompact(void* where, int32_t value) |
| { |
| ASSERT(value >= 0); |
| ASSERT(value <= 60); |
| *reinterpret_cast<uint16_t*>(where) = ((*reinterpret_cast<uint16_t*>(where) & 0xfff0) | (value >> 2)); |
| cacheFlush(reinterpret_cast<uint16_t*>(where), sizeof(uint16_t)); |
| } |
| |
| static void relinkCall(void* from, void* to) |
| { |
| uint16_t* instructionPtr = reinterpret_cast<uint16_t*>(from); |
| instructionPtr -= 3; |
| changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, reinterpret_cast<uint32_t>(to)); |
| } |
| |
| static void relinkJump(void* from, void* to) |
| { |
| uint16_t* instructionPtr = reinterpret_cast<uint16_t*> (from); |
| uint16_t instruction = *instructionPtr; |
| int32_t offsetBits = (reinterpret_cast<uint32_t>(to) - reinterpret_cast<uint32_t>(from)); |
| |
| if (((*instructionPtr & 0xff00) == BT_OPCODE) || ((*instructionPtr & 0xff00) == BF_OPCODE)) { |
| offsetBits -= 8; |
| instructionPtr++; |
| changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits); |
| instruction = (BRAF_OPCODE | (*instructionPtr++ & 0xf00)); |
| *instructionPtr = instruction; |
| printBlockInstr(instructionPtr, reinterpret_cast<uint32_t>(from) + 1, 3); |
| return; |
| } |
| |
| ASSERT((*(instructionPtr + 1) & BRAF_OPCODE) == BRAF_OPCODE); |
| offsetBits -= 4; |
| if (offsetBits >= -4096 && offsetBits <= 4094) { |
| *instructionPtr = getOpcodeGroup6(BRA_OPCODE, offsetBits >> 1); |
| *(++instructionPtr) = NOP_OPCODE; |
| printBlockInstr(instructionPtr - 2, reinterpret_cast<uint32_t>(from), 2); |
| return; |
| } |
| |
| changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits - 2); |
| printInstr(*instructionPtr, reinterpret_cast<uint32_t>(from)); |
| } |
| |
| // Linking & patching |
| |
| static void revertJump(void* instructionStart, SH4Word imm) |
| { |
| SH4Word *insn = reinterpret_cast<SH4Word*>(instructionStart); |
| SH4Word disp; |
| |
| ASSERT((insn[0] & 0xf000) == MOVL_READ_OFFPC_OPCODE); |
| |
| disp = insn[0] & 0x00ff; |
| insn += 2 + (disp << 1); // PC += 4 + (disp*4) |
| insn = (SH4Word *) ((unsigned) insn & (~3)); |
| insn[0] = imm; |
| cacheFlush(insn, sizeof(SH4Word)); |
| } |
| |
| void linkJump(AssemblerLabel from, AssemblerLabel to, JumpType type = JumpFar) |
| { |
| ASSERT(to.isSet()); |
| ASSERT(from.isSet()); |
| |
| uint16_t* instructionPtr = getInstructionPtr(data(), from.m_offset); |
| uint16_t instruction = *instructionPtr; |
| int offsetBits; |
| |
| if (type == JumpNear) { |
| ASSERT((instruction == BT_OPCODE) || (instruction == BF_OPCODE) || (instruction == BRA_OPCODE)); |
| int offset = (codeSize() - from.m_offset) - 4; |
| *instructionPtr++ = instruction | (offset >> 1); |
| printInstr(*instructionPtr, from.m_offset + 2); |
| return; |
| } |
| |
| if (((instruction & 0xff00) == BT_OPCODE) || ((instruction & 0xff00) == BF_OPCODE)) { |
| /* BT label => BF 2 |
| nop LDR reg |
| nop braf @reg |
| nop nop |
| */ |
| offsetBits = (to.m_offset - from.m_offset) - 8; |
| instruction ^= 0x0202; |
| *instructionPtr++ = instruction; |
| if ((*instructionPtr & 0xf000) == 0xe000) { |
| uint32_t* addr = getLdrImmAddressOnPool(instructionPtr, m_buffer.poolAddress()); |
| *addr = offsetBits; |
| } else |
| changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits); |
| instruction = (BRAF_OPCODE | (*instructionPtr++ & 0xf00)); |
| *instructionPtr = instruction; |
| printBlockInstr(instructionPtr - 2, from.m_offset, 3); |
| return; |
| } |
| |
| /* MOV # imm, reg => LDR reg |
| braf @reg braf @reg |
| nop nop |
| */ |
| ASSERT((*(instructionPtr + 1) & BRAF_OPCODE) == BRAF_OPCODE); |
| offsetBits = (to.m_offset - from.m_offset) - 4; |
| if (offsetBits >= -4096 && offsetBits <= 4094) { |
| *instructionPtr = getOpcodeGroup6(BRA_OPCODE, offsetBits >> 1); |
| *(++instructionPtr) = NOP_OPCODE; |
| printBlockInstr(instructionPtr - 1, from.m_offset, 2); |
| return; |
| } |
| |
| instruction = *instructionPtr; |
| if ((instruction & 0xf000) == 0xe000) { |
| uint32_t* addr = getLdrImmAddressOnPool(instructionPtr, m_buffer.poolAddress()); |
| *addr = offsetBits - 2; |
| printInstr(*instructionPtr, from.m_offset + 2); |
| return; |
| } |
| |
| changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits - 2); |
| printInstr(*instructionPtr, from.m_offset + 2); |
| } |
| |
| static void* getRelocatedAddress(void* code, AssemblerLabel label) |
| { |
| return reinterpret_cast<void*>(reinterpret_cast<char*>(code) + label.m_offset); |
| } |
| |
| static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) |
| { |
| return b.m_offset - a.m_offset; |
| } |
| |
| static void patchPointer(void* code, AssemblerLabel where, void* value) |
| { |
| patchPointer(reinterpret_cast<uint32_t*>(code) + where.m_offset, value); |
| } |
| |
| static void patchPointer(void* code, void* value) |
| { |
| patchInt32(code, reinterpret_cast<uint32_t>(value)); |
| } |
| |
| static void patchInt32(void* code, uint32_t value) |
| { |
| changePCrelativeAddress((*(reinterpret_cast<uint16_t*>(code)) & 0xff), reinterpret_cast<uint16_t*>(code), value); |
| } |
| |
| static uint32_t readInt32(void* code) |
| { |
| return readPCrelativeAddress((*(reinterpret_cast<uint16_t*>(code)) & 0xff), reinterpret_cast<uint16_t*>(code)); |
| } |
| |
| static void* readCallTarget(void* from) |
| { |
| uint16_t* instructionPtr = static_cast<uint16_t*>(from); |
| instructionPtr -= 3; |
| return reinterpret_cast<void*>(readPCrelativeAddress((*instructionPtr & 0xff), instructionPtr)); |
| } |
| |
| PassRefPtr<ExecutableMemoryHandle> executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) |
| { |
| return m_buffer.executableCopy(globalData, ownerUID, effort); |
| } |
| |
| static void cacheFlush(void* code, size_t size) |
| { |
| #if !OS(LINUX) |
| #error "The cacheFlush support is missing on this platform." |
| #elif defined CACHEFLUSH_D_L2 |
| syscall(__NR_cacheflush, reinterpret_cast<unsigned>(code), size, CACHEFLUSH_D_WB | CACHEFLUSH_I | CACHEFLUSH_D_L2); |
| #else |
| syscall(__NR_cacheflush, reinterpret_cast<unsigned>(code), size, CACHEFLUSH_D_WB | CACHEFLUSH_I); |
| #endif |
| } |
| |
| void prefix(uint16_t pre) |
| { |
| m_buffer.putByte(pre); |
| } |
| |
| void oneShortOp(uint16_t opcode, bool checksize = true, bool isDouble = true) |
| { |
| printInstr(opcode, m_buffer.codeSize(), isDouble); |
| if (checksize) |
| m_buffer.ensureSpace(maxInstructionSize); |
| m_buffer.putShortUnchecked(opcode); |
| } |
| |
| void ensureSpace(int space) |
| { |
| m_buffer.ensureSpace(space); |
| } |
| |
| void ensureSpace(int insnSpace, int constSpace) |
| { |
| m_buffer.ensureSpace(insnSpace, constSpace); |
| } |
| |
| // Administrative methods |
| |
| void* data() const { return m_buffer.data(); } |
| size_t codeSize() const { return m_buffer.codeSize(); } |
| |
| #ifdef SH4_ASSEMBLER_TRACING |
| static void printInstr(uint16_t opc, unsigned size, bool isdoubleInst = true) |
| { |
| if (!getenv("JavaScriptCoreDumpJIT")) |
| return; |
| |
| const char *format = 0; |
| printfStdoutInstr("offset: 0x%8.8x\t", size); |
| switch (opc) { |
| case BRK_OPCODE: |
| format = " BRK\n"; |
| break; |
| case NOP_OPCODE: |
| format = " NOP\n"; |
| break; |
| case RTS_OPCODE: |
| format =" *RTS\n"; |
| break; |
| case SETS_OPCODE: |
| format = " SETS\n"; |
| break; |
| case SETT_OPCODE: |
| format = " SETT\n"; |
| break; |
| case CLRT_OPCODE: |
| format = " CLRT\n"; |
| break; |
| case FSCHG_OPCODE: |
| format = " FSCHG\n"; |
| break; |
| } |
| if (format) { |
| printfStdoutInstr(format); |
| return; |
| } |
| switch (opc & 0xf0ff) { |
| case BRAF_OPCODE: |
| format = " *BRAF R%d\n"; |
| break; |
| case DT_OPCODE: |
| format = " DT R%d\n"; |
| break; |
| case CMPPL_OPCODE: |
| format = " CMP/PL R%d\n"; |
| break; |
| case CMPPZ_OPCODE: |
| format = " CMP/PZ R%d\n"; |
| break; |
| case JMP_OPCODE: |
| format = " *JMP @R%d\n"; |
| break; |
| case JSR_OPCODE: |
| format = " *JSR @R%d\n"; |
| break; |
| case LDSPR_OPCODE: |
| format = " LDS R%d, PR\n"; |
| break; |
| case LDSLPR_OPCODE: |
| format = " LDS.L @R%d+, PR\n"; |
| break; |
| case MOVT_OPCODE: |
| format = " MOVT R%d\n"; |
| break; |
| case SHAL_OPCODE: |
| format = " SHAL R%d\n"; |
| break; |
| case SHAR_OPCODE: |
| format = " SHAR R%d\n"; |
| break; |
| case SHLL_OPCODE: |
| format = " SHLL R%d\n"; |
| break; |
| case SHLL2_OPCODE: |
| format = " SHLL2 R%d\n"; |
| break; |
| case SHLL8_OPCODE: |
| format = " SHLL8 R%d\n"; |
| break; |
| case SHLL16_OPCODE: |
| format = " SHLL16 R%d\n"; |
| break; |
| case SHLR_OPCODE: |
| format = " SHLR R%d\n"; |
| break; |
| case SHLR2_OPCODE: |
| format = " SHLR2 R%d\n"; |
| break; |
| case SHLR8_OPCODE: |
| format = " SHLR8 R%d\n"; |
| break; |
| case SHLR16_OPCODE: |
| format = " SHLR16 R%d\n"; |
| break; |
| case STSPR_OPCODE: |
| format = " STS PR, R%d\n"; |
| break; |
| case STSLPR_OPCODE: |
| format = " STS.L PR, @-R%d\n"; |
| break; |
| case LDS_RM_FPUL_OPCODE: |
| format = " LDS R%d, FPUL\n"; |
| break; |
| case STS_FPUL_RN_OPCODE: |
| format = " STS FPUL, R%d \n"; |
| break; |
| case FLDS_FRM_FPUL_OPCODE: |
| format = " FLDS FR%d, FPUL\n"; |
| break; |
| case FSTS_FPUL_FRN_OPCODE: |
| format = " FSTS FPUL, R%d \n"; |
| break; |
| case LDSFPSCR_OPCODE: |
| format = " LDS R%d, FPSCR \n"; |
| break; |
| case STSFPSCR_OPCODE: |
| format = " STS FPSCR, R%d \n"; |
| break; |
| case STSMACL_OPCODE: |
| format = " STS MACL, R%d \n"; |
| break; |
| case STSMACH_OPCODE: |
| format = " STS MACH, R%d \n"; |
| break; |
| case BSRF_OPCODE: |
| format = " *BSRF R%d"; |
| break; |
| case FTRC_OPCODE: |
| format = " FTRC FR%d, FPUL\n"; |
| break; |
| } |
| if (format) { |
| printfStdoutInstr(format, getRn(opc)); |
| return; |
| } |
| switch (opc & 0xf0ff) { |
| case FNEG_OPCODE: |
| format = " FNEG DR%d\n"; |
| break; |
| case FLOAT_OPCODE: |
| format = " FLOAT DR%d\n"; |
| break; |
| case FTRC_OPCODE: |
| format = " FTRC FR%d, FPUL\n"; |
| break; |
| case FSQRT_OPCODE: |
| format = " FSQRT FR%d\n"; |
| break; |
| case FCNVDS_DRM_FPUL_OPCODE: |
| format = " FCNVDS FR%d, FPUL\n"; |
| break; |
| case FCNVSD_FPUL_DRN_OPCODE: |
| format = " FCNVSD FPUL, FR%d\n"; |
| break; |
| } |
| if (format) { |
| if (isdoubleInst) |
| printfStdoutInstr(format, getDRn(opc) << 1); |
| else |
| printfStdoutInstr(format, getRn(opc)); |
| return; |
| } |
| switch (opc & 0xf00f) { |
| case ADD_OPCODE: |
| format = " ADD R%d, R%d\n"; |
| break; |
| case ADDC_OPCODE: |
| format = " ADDC R%d, R%d\n"; |
| break; |
| case ADDV_OPCODE: |
| format = " ADDV R%d, R%d\n"; |
| break; |
| case AND_OPCODE: |
| format = " AND R%d, R%d\n"; |
| break; |
| case DIV1_OPCODE: |
| format = " DIV1 R%d, R%d\n"; |
| break; |
| case CMPEQ_OPCODE: |
| format = " CMP/EQ R%d, R%d\n"; |
| break; |
| case CMPGE_OPCODE: |
| format = " CMP/GE R%d, R%d\n"; |
| break; |
| case CMPGT_OPCODE: |
| format = " CMP/GT R%d, R%d\n"; |
| break; |
| case CMPHI_OPCODE: |
| format = " CMP/HI R%d, R%d\n"; |
| break; |
| case CMPHS_OPCODE: |
| format = " CMP/HS R%d, R%d\n"; |
| break; |
| case MOV_OPCODE: |
| format = " MOV R%d, R%d\n"; |
| break; |
| case MOVB_WRITE_RN_OPCODE: |
| format = " MOV.B R%d, @R%d\n"; |
| break; |
| case MOVB_WRITE_RNDEC_OPCODE: |
| format = " MOV.B R%d, @-R%d\n"; |
| break; |
| case MOVB_WRITE_R0RN_OPCODE: |
| format = " MOV.B R%d, @(R0, R%d)\n"; |
| break; |
| case MOVB_READ_RM_OPCODE: |
| format = " MOV.B @R%d, R%d\n"; |
| break; |
| case MOVB_READ_RMINC_OPCODE: |
| format = " MOV.B @R%d+, R%d\n"; |
| break; |
| case MOVB_READ_R0RM_OPCODE: |
| format = " MOV.B @(R0, R%d), R%d\n"; |
| break; |
| case MOVL_WRITE_RN_OPCODE: |
| format = " MOV.L R%d, @R%d\n"; |
| break; |
| case MOVL_WRITE_RNDEC_OPCODE: |
| format = " MOV.L R%d, @-R%d\n"; |
| break; |
| case MOVL_WRITE_R0RN_OPCODE: |
| format = " MOV.L R%d, @(R0, R%d)\n"; |
| break; |
| case MOVL_READ_RM_OPCODE: |
| format = " MOV.L @R%d, R%d\n"; |
| break; |
| case MOVL_READ_RMINC_OPCODE: |
| format = " MOV.L @R%d+, R%d\n"; |
| break; |
| case MOVL_READ_R0RM_OPCODE: |
| format = " MOV.L @(R0, R%d), R%d\n"; |
| break; |
| case MULL_OPCODE: |
| format = " MUL.L R%d, R%d\n"; |
| break; |
| case DMULL_L_OPCODE: |
| format = " DMULU.L R%d, R%d\n"; |
| break; |
| case DMULSL_OPCODE: |
| format = " DMULS.L R%d, R%d\n"; |
| break; |
| case NEG_OPCODE: |
| format = " NEG R%d, R%d\n"; |
| break; |
| case NEGC_OPCODE: |
| format = " NEGC R%d, R%d\n"; |
| break; |
| case NOT_OPCODE: |
| format = " NOT R%d, R%d\n"; |
| break; |
| case OR_OPCODE: |
| format = " OR R%d, R%d\n"; |
| break; |
| case SHAD_OPCODE: |
| format = " SHAD R%d, R%d\n"; |
| break; |
| case SHLD_OPCODE: |
| format = " SHLD R%d, R%d\n"; |
| break; |
| case SUB_OPCODE: |
| format = " SUB R%d, R%d\n"; |
| break; |
| case SUBC_OPCODE: |
| format = " SUBC R%d, R%d\n"; |
| break; |
| case SUBV_OPCODE: |
| format = " SUBV R%d, R%d\n"; |
| break; |
| case TST_OPCODE: |
| format = " TST R%d, R%d\n"; |
| break; |
| case XOR_OPCODE: |
| format = " XOR R%d, R%d\n";break; |
| case MOVW_WRITE_RN_OPCODE: |
| format = " MOV.W R%d, @R%d\n"; |
| break; |
| case MOVW_READ_RM_OPCODE: |
| format = " MOV.W @R%d, R%d\n"; |
| break; |
| case MOVW_READ_R0RM_OPCODE: |
| format = " MOV.W @(R0, R%d), R%d\n"; |
| break; |
| case EXTUB_OPCODE: |
| format = " EXTU.B R%d, R%d\n"; |
| break; |
| case EXTUW_OPCODE: |
| format = " EXTU.W R%d, R%d\n"; |
| break; |
| } |
| if (format) { |
| printfStdoutInstr(format, getRm(opc), getRn(opc)); |
| return; |
| } |
| switch (opc & 0xf00f) { |
| case FSUB_OPCODE: |
| format = " FSUB FR%d, FR%d\n"; |
| break; |
| case FADD_OPCODE: |
| format = " FADD FR%d, FR%d\n"; |
| break; |
| case FDIV_OPCODE: |
| format = " FDIV FR%d, FR%d\n"; |
| break; |
| case FMUL_OPCODE: |
| format = " DMULL FR%d, FR%d\n"; |
| break; |
| case FMOV_OPCODE: |
| format = " FMOV FR%d, FR%d\n"; |
| break; |
| case FCMPEQ_OPCODE: |
| format = " FCMP/EQ FR%d, FR%d\n"; |
| break; |
| case FCMPGT_OPCODE: |
| format = " FCMP/GT FR%d, FR%d\n"; |
| break; |
| } |
| if (format) { |
| if (isdoubleInst) |
| printfStdoutInstr(format, getDRm(opc) << 1, getDRn(opc) << 1); |
| else |
| printfStdoutInstr(format, getRm(opc), getRn(opc)); |
| return; |
| } |
| switch (opc & 0xf00f) { |
| case FMOVS_WRITE_RN_DEC_OPCODE: |
| format = " %s FR%d, @-R%d\n"; |
| break; |
| case FMOVS_WRITE_RN_OPCODE: |
| format = " %s FR%d, @R%d\n"; |
| break; |
| case FMOVS_WRITE_R0RN_OPCODE: |
| format = " %s FR%d, @(R0, R%d)\n"; |
| break; |
| } |
| if (format) { |
| if (isdoubleInst) |
| printfStdoutInstr(format, "FMOV", getDRm(opc) << 1, getDRn(opc)); |
| else |
| printfStdoutInstr(format, "FMOV.S", getRm(opc), getRn(opc)); |
| return; |
| } |
| switch (opc & 0xf00f) { |
| case FMOVS_READ_RM_OPCODE: |
| format = " %s @R%d, FR%d\n"; |
| break; |
| case FMOVS_READ_RM_INC_OPCODE: |
| format = " %s @R%d+, FR%d\n"; |
| break; |
| case FMOVS_READ_R0RM_OPCODE: |
| format = " %s @(R0, R%d), FR%d\n"; |
| break; |
| } |
| if (format) { |
| if (isdoubleInst) |
| printfStdoutInstr(format, "FMOV", getDRm(opc), getDRn(opc) << 1); |
| else |
| printfStdoutInstr(format, "FMOV.S", getRm(opc), getRn(opc)); |
| return; |
| } |
| switch (opc & 0xff00) { |
| case BF_OPCODE: |
| format = " BF %d\n"; |
| break; |
| case BFS_OPCODE: |
| format = " *BF/S %d\n"; |
| break; |
| case ANDIMM_OPCODE: |
| format = " AND #%d, R0\n"; |
| break; |
| case BT_OPCODE: |
| format = " BT %d\n"; |
| break; |
| case BTS_OPCODE: |
| format = " *BT/S %d\n"; |
| break; |
| case CMPEQIMM_OPCODE: |
| format = " CMP/EQ #%d, R0\n"; |
| break; |
| case MOVB_WRITE_OFFGBR_OPCODE: |
| format = " MOV.B R0, @(%d, GBR)\n"; |
| break; |
| case MOVB_READ_OFFGBR_OPCODE: |
| format = " MOV.B @(%d, GBR), R0\n"; |
| break; |
| case MOVL_WRITE_OFFGBR_OPCODE: |
| format = " MOV.L R0, @(%d, GBR)\n"; |
| break; |
| case MOVL_READ_OFFGBR_OPCODE: |
| format = " MOV.L @(%d, GBR), R0\n"; |
| break; |
| case MOVA_READ_OFFPC_OPCODE: |
| format = " MOVA @(%d, PC), R0\n"; |
| break; |
| case ORIMM_OPCODE: |
| format = " OR #%d, R0\n"; |
| break; |
| case ORBIMM_OPCODE: |
| format = " OR.B #%d, @(R0, GBR)\n"; |
| break; |
| case TSTIMM_OPCODE: |
| format = " TST #%d, R0\n"; |
| break; |
| case TSTB_OPCODE: |
| format = " TST.B %d, @(R0, GBR)\n"; |
| break; |
| case XORIMM_OPCODE: |
| format = " XOR #%d, R0\n"; |
| break; |
| case XORB_OPCODE: |
| format = " XOR.B %d, @(R0, GBR)\n"; |
| break; |
| } |
| if (format) { |
| printfStdoutInstr(format, getImm8(opc)); |
| return; |
| } |
| switch (opc & 0xff00) { |
| case MOVB_WRITE_OFFRN_OPCODE: |
| format = " MOV.B R0, @(%d, R%d)\n"; |
| break; |
| case MOVB_READ_OFFRM_OPCODE: |
| format = " MOV.B @(%d, R%d), R0\n"; |
| break; |
| } |
| if (format) { |
| printfStdoutInstr(format, getDisp(opc), getRm(opc)); |
| return; |
| } |
| switch (opc & 0xf000) { |
| case BRA_OPCODE: |
| format = " *BRA %d\n"; |
| break; |
| case BSR_OPCODE: |
| format = " *BSR %d\n"; |
| break; |
| } |
| if (format) { |
| printfStdoutInstr(format, getImm12(opc)); |
| return; |
| } |
| switch (opc & 0xf000) { |
| case MOVL_READ_OFFPC_OPCODE: |
| format = " MOV.L @(%d, PC), R%d\n"; |
| break; |
| case ADDIMM_OPCODE: |
| format = " ADD #%d, R%d\n"; |
| break; |
| case MOVIMM_OPCODE: |
| format = " MOV #%d, R%d\n"; |
| break; |
| case MOVW_READ_OFFPC_OPCODE: |
| format = " MOV.W @(%d, PC), R%d\n"; |
| break; |
| } |
| if (format) { |
| printfStdoutInstr(format, getImm8(opc), getRn(opc)); |
| return; |
| } |
| switch (opc & 0xf000) { |
| case MOVL_WRITE_OFFRN_OPCODE: |
| format = " MOV.L R%d, @(%d, R%d)\n"; |
| printfStdoutInstr(format, getRm(opc), getDisp(opc), getRn(opc)); |
| break; |
| case MOVL_READ_OFFRM_OPCODE: |
| format = " MOV.L @(%d, R%d), R%d\n"; |
| printfStdoutInstr(format, getDisp(opc), getRm(opc), getRn(opc)); |
| break; |
| } |
| } |
| |
| static void printfStdoutInstr(const char* format, ...) |
| { |
| if (getenv("JavaScriptCoreDumpJIT")) { |
| va_list args; |
| va_start(args, format); |
| vprintfStdoutInstr(format, args); |
| va_end(args); |
| } |
| } |
| |
| static void vprintfStdoutInstr(const char* format, va_list args) |
| { |
| if (getenv("JavaScriptCoreDumpJIT")) |
| WTF::dataLogFV(format, args); |
| } |
| |
| static void printBlockInstr(uint16_t* first, unsigned offset, int nbInstr) |
| { |
| printfStdoutInstr(">> repatch instructions after link\n"); |
| for (int i = 0; i <= nbInstr; i++) |
| printInstr(*(first + i), offset + i); |
| printfStdoutInstr(">> end repatch\n"); |
| } |
| #else |
| static void printInstr(uint16_t opc, unsigned size, bool isdoubleInst = true) { }; |
| static void printBlockInstr(uint16_t* first, unsigned offset, int nbInstr) { }; |
| #endif |
| |
| static void replaceWithLoad(void* instructionStart) |
| { |
| SH4Word* insPtr = reinterpret_cast<SH4Word*>(instructionStart); |
| |
| insPtr += 2; // skip MOV and ADD opcodes |
| |
| if (((*insPtr) & 0xf00f) != MOVL_READ_RM_OPCODE) { |
| *insPtr = MOVL_READ_RM_OPCODE | (*insPtr & 0x0ff0); |
| cacheFlush(insPtr, sizeof(SH4Word)); |
| } |
| } |
| |
| static void replaceWithAddressComputation(void* instructionStart) |
| { |
| SH4Word* insPtr = reinterpret_cast<SH4Word*>(instructionStart); |
| |
| insPtr += 2; // skip MOV and ADD opcodes |
| |
| if (((*insPtr) & 0xf00f) != MOV_OPCODE) { |
| *insPtr = MOV_OPCODE | (*insPtr & 0x0ff0); |
| cacheFlush(insPtr, sizeof(SH4Word)); |
| } |
| } |
| |
| private: |
| SH4Buffer m_buffer; |
| int m_claimscratchReg; |
| }; |
| |
| } // namespace JSC |
| |
| #endif // ENABLE(ASSEMBLER) && CPU(SH4) |
| |
| #endif // SH4Assembler_h |