X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/14957cd040308e3eeec43d26bae5d76da13fcd85..cb9aa2694aba0ae4f946ed34b8e0f6c99c1cfe44:/assembler/SH4Assembler.h diff --git a/assembler/SH4Assembler.h b/assembler/SH4Assembler.h index 1d089e5..d326279 100644 --- a/assembler/SH4Assembler.h +++ b/assembler/SH4Assembler.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2013 Cisco Systems, Inc. All rights reserved. * Copyright (C) 2009-2011 STMicroelectronics. All rights reserved. * Copyright (C) 2008 Apple Inc. All rights reserved. * @@ -31,9 +32,13 @@ #include "AssemblerBuffer.h" #include "AssemblerBufferWithConstantPool.h" +#include "JITCompilationEffort.h" +#include #include #include +#include #include +#include #include #ifndef NDEBUG @@ -111,7 +116,9 @@ enum { MOVL_READ_OFFPC_OPCODE = 0xd000, MOVL_READ_OFFRM_OPCODE = 0x5000, MOVW_WRITE_RN_OPCODE = 0x2001, + MOVW_WRITE_R0RN_OPCODE = 0x0005, MOVW_READ_RM_OPCODE = 0x6001, + MOVW_READ_RMINC_OPCODE = 0x6005, MOVW_READ_R0RM_OPCODE = 0x000d, MOVW_READ_OFFRM_OPCODE = 0x8500, MOVW_READ_OFFPC_OPCODE = 0x9000, @@ -151,6 +158,7 @@ enum { TST_OPCODE = 0x2008, TSTIMM_OPCODE = 0xc800, TSTB_OPCODE = 0xcc00, + EXTUB_OPCODE = 0x600c, EXTUW_OPCODE = 0x600d, XOR_OPCODE = 0x200a, XORIMM_OPCODE = 0xca00, @@ -162,6 +170,7 @@ enum { 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, @@ -170,9 +179,11 @@ enum { STSFPSCR_OPCODE = 0x006a, LDSRMFPUL_OPCODE = 0x405a, FSTSFPULFRN_OPCODE = 0xf00d, + FABS_OPCODE = 0xf05d, FSQRT_OPCODE = 0xf06d, FSCHG_OPCODE = 0xf3fd, CLRT_OPCODE = 8, + SYNCO_OPCODE = 0x00ab, }; namespace SH4Registers { @@ -316,37 +327,53 @@ public: static const RegisterID scratchReg2 = SH4Registers::r11; static const uint32_t maxInstructionSize = 16; + static RegisterID firstRegister() { return SH4Registers::r0; } + static RegisterID lastRegister() { return SH4Registers::r15; } + + static FPRegisterID firstFPRegister() { return SH4Registers::dr0; } + static FPRegisterID lastFPRegister() { return SH4Registers::dr14; } + enum { padForAlign8 = 0x00, padForAlign16 = 0x0009, padForAlign32 = 0x00090009, }; + enum JumpType { + JumpFar, + JumpNear + }; + SH4Assembler() + : m_claimscratchReg(0x0) + , m_indexOfLastWatchpoint(INT_MIN) + , m_indexOfTailOfLastWatchpoint(INT_MIN) { - m_claimscratchReg = 0x0; } + SH4Buffer& buffer() { return m_buffer; } + // 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 + HS = 0x2, // Unsigned Greater Than equal + HI = 0x3, // Unsigned Greater Than + LS = 0x4, // Unsigned Lower or Same + LI = 0x5, // Unsigned 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, + NS = 0xc, // Not Signed + EQU= 0xd, // Equal or unordered(NaN) + NEU= 0xe, + GTU= 0xf, + GEU= 0x10, + LTU= 0x11, + LEU= 0x12, } Condition; // Opaque label types @@ -442,7 +469,7 @@ public: void andlImm8r(int imm8, RegisterID dst) { ASSERT((imm8 <= 255) && (imm8 >= 0)); - ASSERT(dst == SH4Registers::r0); + ASSERT_UNUSED(dst, dst == SH4Registers::r0); uint16_t opc = getOpcodeGroup5(ANDIMM_OPCODE, imm8); oneShortOp(opc); @@ -475,7 +502,7 @@ public: void orlImm8r(int imm8, RegisterID dst) { ASSERT((imm8 <= 255) && (imm8 >= 0)); - ASSERT(dst == SH4Registers::r0); + ASSERT_UNUSED(dst, dst == SH4Registers::r0); uint16_t opc = getOpcodeGroup5(ORIMM_OPCODE, imm8); oneShortOp(opc); @@ -483,14 +510,14 @@ public: void sublRegReg(RegisterID src, RegisterID dst) { - uint16_t opc = getOpcodeGroup1(SUB_OPCODE, dst, src); - oneShortOp(opc); + 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); + uint16_t opc = getOpcodeGroup1(SUBV_OPCODE, dst, src); + oneShortOp(opc); } void xorlRegReg(RegisterID src, RegisterID dst) @@ -502,7 +529,7 @@ public: void xorlImm8r(int imm8, RegisterID dst) { ASSERT((imm8 <= 255) && (imm8 >= 0)); - ASSERT(dst == SH4Registers::r0); + ASSERT_UNUSED(dst, dst == SH4Registers::r0); uint16_t opc = getOpcodeGroup5(XORIMM_OPCODE, imm8); oneShortOp(opc); @@ -524,7 +551,7 @@ public: oneShortOp(getOpcodeGroup2(SHLL16_OPCODE, dst)); break; default: - ASSERT_NOT_REACHED(); + RELEASE_ASSERT_NOT_REACHED(); } } @@ -534,28 +561,14 @@ public: oneShortOp(opc); } - void shllRegReg(RegisterID dst, RegisterID rShift) - { - uint16_t opc = getOpcodeGroup1(SHLD_OPCODE, dst, rShift); - oneShortOp(opc); - } - - void shlrRegReg(RegisterID dst, RegisterID rShift) + void shldRegReg(RegisterID dst, RegisterID rShift) { - neg(rShift, rShift); - shllRegReg(dst, rShift); + oneShortOp(getOpcodeGroup1(SHLD_OPCODE, dst, rShift)); } - void sharRegReg(RegisterID dst, RegisterID rShift) + void shadRegReg(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); + oneShortOp(getOpcodeGroup1(SHAD_OPCODE, dst, rShift)); } void shlrImm8r(int imm, RegisterID dst) @@ -574,7 +587,29 @@ public: oneShortOp(getOpcodeGroup2(SHLR16_OPCODE, dst)); break; default: - ASSERT_NOT_REACHED(); + RELEASE_ASSERT_NOT_REACHED(); + } + } + + void shalImm8r(int imm, RegisterID dst) + { + switch (imm) { + case 1: + oneShortOp(getOpcodeGroup2(SHAL_OPCODE, dst)); + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + } + } + + void sharImm8r(int imm, RegisterID dst) + { + switch (imm) { + case 1: + oneShortOp(getOpcodeGroup2(SHAR_OPCODE, dst)); + break; + default: + RELEASE_ASSERT_NOT_REACHED(); } } @@ -644,7 +679,7 @@ public: oneShortOp(getOpcodeGroup1(CMPGT_OPCODE, left, right)); break; default: - ASSERT_NOT_REACHED(); + RELEASE_ASSERT_NOT_REACHED(); } } @@ -662,6 +697,7 @@ public: void cmpEqImmR0(int imm, RegisterID dst) { + ASSERT_UNUSED(dst, dst == SH4Registers::r0); uint16_t opc = getOpcodeGroup5(CMPEQIMM_OPCODE, imm); oneShortOp(opc); } @@ -674,7 +710,8 @@ public: void testlImm8r(int imm, RegisterID dst) { - ASSERT((dst == SH4Registers::r0) && (imm <= 255) && (imm >= 0)); + ASSERT((imm <= 255) && (imm >= 0)); + ASSERT_UNUSED(dst, dst == SH4Registers::r0); uint16_t opc = getOpcodeGroup5(TSTIMM_OPCODE, imm); oneShortOp(opc); @@ -685,6 +722,11 @@ public: oneShortOp(NOP_OPCODE, false); } + void synco() + { + oneShortOp(SYNCO_OPCODE); + } + void sett() { oneShortOp(SETT_OPCODE); @@ -721,7 +763,7 @@ public: oneShortOp(getOpcodeGroup5(BF_OPCODE, label)); break; default: - ASSERT_NOT_REACHED(); + RELEASE_ASSERT_NOT_REACHED(); } } @@ -741,7 +783,7 @@ public: oneShortOp(getOpcodeGroup2(BSRF_OPCODE, reg)); break; default: - ASSERT_NOT_REACHED(); + RELEASE_ASSERT_NOT_REACHED(); } } @@ -757,6 +799,12 @@ public: 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); @@ -789,7 +837,7 @@ public: oneShortOp(opc); } - void floatfpulfrn(RegisterID src) + void floatfpulfrn(FPRegisterID src) { uint16_t opc = getOpcodeGroup2(FLOAT_OPCODE, src); oneShortOp(opc, true, false); @@ -801,6 +849,12 @@ public: oneShortOp(opc, true, false); } + void fmovsRegReg(FPRegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup1(FMOV_OPCODE, dst, src); + oneShortOp(opc, true, false); + } + void fmovsReadrm(RegisterID src, FPRegisterID dst) { uint16_t opc = getOpcodeGroup1(FMOVS_READ_RM_OPCODE, dst, src); @@ -843,13 +897,13 @@ public: oneShortOp(opc, true, false); } - void fldsfpul(RegisterID src) + void fldsfpul(FPRegisterID src) { uint16_t opc = getOpcodeGroup2(FLDS_FRM_FPUL_OPCODE, src); oneShortOp(opc); } - void fstsfpul(RegisterID src) + void fstsfpul(FPRegisterID src) { uint16_t opc = getOpcodeGroup2(FSTS_FPUL_FRN_OPCODE, src); oneShortOp(opc); @@ -875,6 +929,12 @@ public: 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); @@ -917,6 +977,12 @@ public: oneShortOp(opc); } + void dabs(FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup7(FABS_OPCODE, dst >> 1); + oneShortOp(opc); + } + void dsqrt(FPRegisterID dst) { uint16_t opc = getOpcodeGroup7(FSQRT_OPCODE, dst >> 1); @@ -1005,9 +1071,15 @@ public: oneShortOp(opc); } + void movwMemRegIn(RegisterID base, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVW_READ_RMINC_OPCODE, dst, base); + oneShortOp(opc); + } + void movwPCReg(int offset, RegisterID base, RegisterID dst) { - ASSERT(base == SH4Registers::pc); + ASSERT_UNUSED(base, base == SH4Registers::pc); ASSERT((offset <= 255) && (offset >= 0)); uint16_t opc = getOpcodeGroup3(MOVW_READ_OFFPC_OPCODE, dst, offset); @@ -1016,7 +1088,7 @@ public: void movwMemReg(int offset, RegisterID base, RegisterID dst) { - ASSERT(dst == SH4Registers::r0); + ASSERT_UNUSED(dst, dst == SH4Registers::r0); uint16_t opc = getOpcodeGroup11(MOVW_READ_OFFRM_OPCODE, base, offset); oneShortOp(opc); @@ -1028,6 +1100,12 @@ public: oneShortOp(opc); } + void movwRegMemr0(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVW_WRITE_R0RN_OPCODE, dst, src); + oneShortOp(opc); + } + void movlRegMem(RegisterID src, int offset, RegisterID base) { ASSERT((offset <= 15) && (offset >= 0)); @@ -1063,9 +1141,20 @@ public: 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); + ASSERT_UNUSED(dst, dst == SH4Registers::r0); uint16_t opc = getOpcodeGroup11(MOVB_READ_OFFRM_OPCODE, base, offset); oneShortOp(opc); @@ -1083,6 +1172,18 @@ public: oneShortOp(opc); } + void movbMemRegIn(RegisterID base, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVB_READ_RMINC_OPCODE, dst, base); + oneShortOp(opc); + } + + void movbRegMemr0(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVB_WRITE_R0RN_OPCODE, dst, src); + oneShortOp(opc); + } + void movlMemReg(RegisterID base, RegisterID dst) { uint16_t opc = getOpcodeGroup1(MOVL_READ_RM_OPCODE, dst, base); @@ -1107,14 +1208,6 @@ public: 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)) { @@ -1165,12 +1258,19 @@ public: { 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; + return m_buffer.label(); + } + + AssemblerLabel extraInstrForBranch(RegisterID dst) + { + loadConstantUnReusable(0x0, dst); + branch(BRAF_OPCODE, dst); + nop(); + return m_buffer.label(); } AssemblerLabel jmp(RegisterID dst) @@ -1188,16 +1288,20 @@ public: AssemblerLabel jne() { - AssemblerLabel label = m_buffer.label(); branch(BF_OPCODE, 0); - return label; + return m_buffer.label(); } AssemblerLabel je() { - AssemblerLabel label = m_buffer.label(); branch(BT_OPCODE, 0); - return label; + return m_buffer.label(); + } + + AssemblerLabel bra() + { + branch(BRA_OPCODE, 0); + return m_buffer.label(); } void ret() @@ -1206,15 +1310,36 @@ public: oneShortOp(RTS_OPCODE, false); } - AssemblerLabel label() + AssemblerLabel labelIgnoringWatchpoints() { - m_buffer.ensureSpaceForAnyOneInstruction(); + m_buffer.ensureSpaceForAnyInstruction(); return m_buffer.label(); } + AssemblerLabel labelForWatchpoint() + { + m_buffer.ensureSpaceForAnyInstruction(); + AssemblerLabel result = m_buffer.label(); + if (static_cast(result.m_offset) != m_indexOfLastWatchpoint) + result = label(); + m_indexOfLastWatchpoint = result.m_offset; + m_indexOfTailOfLastWatchpoint = result.m_offset + maxJumpReplacementSize(); + return result; + } + + AssemblerLabel label() + { + AssemblerLabel result = labelIgnoringWatchpoints(); + while (UNLIKELY(static_cast(result.m_offset) < m_indexOfTailOfLastWatchpoint)) { + nop(); + result = labelIgnoringWatchpoints(); + } + return result; + } + int sizeOfConstantPool() { - return m_buffer.sizeOfConstantPool(); + return m_buffer.sizeOfConstantPool(); } AssemblerLabel align(int alignment) @@ -1229,12 +1354,14 @@ public: static void changePCrelativeAddress(int offset, uint16_t* instructionPtr, uint32_t newAddress) { + ASSERT((instructionPtr[0] & 0xf000) == MOVL_READ_OFFPC_OPCODE); uint32_t address = (offset << 2) + ((reinterpret_cast(instructionPtr) + 4) &(~0x3)); *reinterpret_cast(address) = newAddress; } - + static uint32_t readPCrelativeAddress(int offset, uint16_t* instructionPtr) { + ASSERT((instructionPtr[0] & 0xf000) == MOVL_READ_OFFPC_OPCODE); uint32_t address = (offset << 2) + ((reinterpret_cast(instructionPtr) + 4) &(~0x3)); return *reinterpret_cast(address); } @@ -1248,41 +1375,16 @@ public: { ASSERT(from.isSet()); - uint16_t* instructionPtr = getInstructionPtr(code, from.m_offset); - uint16_t instruction = *instructionPtr; + uint16_t* instructionPtr = getInstructionPtr(code, from.m_offset) - 3; int offsetBits = (reinterpret_cast(to) - reinterpret_cast(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); + /* MOV #imm, reg => LDR reg + braf @reg braf @reg + nop nop + */ + ASSERT((instructionPtr[0] & 0xf000) == MOVL_READ_OFFPC_OPCODE); + ASSERT((instructionPtr[1] & 0xf0ff) == BRAF_OPCODE); + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits); printInstr(*instructionPtr, from.m_offset + 2); } @@ -1290,12 +1392,14 @@ public: { uint16_t* instructionPtr = getInstructionPtr(code, from.m_offset); instructionPtr -= 3; + ASSERT((instructionPtr[0] & 0xf000) == MOVL_READ_OFFPC_OPCODE); changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, reinterpret_cast(to)); } static void linkPointer(void* code, AssemblerLabel where, void* value) { uint16_t* instructionPtr = getInstructionPtr(code, where.m_offset); + ASSERT((instructionPtr[0] & 0xf000) == MOVL_READ_OFFPC_OPCODE); changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, reinterpret_cast(value)); } @@ -1317,7 +1421,7 @@ public: static SH4Buffer::TwoShorts placeConstantPoolBarrier(int offset) { - ASSERT(((offset >> 1) <=2047) && ((offset >> 1) >= -2048)); + ASSERT(((offset >> 1) <= 2047) && ((offset >> 1) >= -2048)); SH4Buffer::TwoShorts m_barrier; m_barrier.high = (BRA_OPCODE | (offset >> 1)); @@ -1339,7 +1443,7 @@ public: ASSERT((((reinterpret_cast(constPoolAddr) - reinterpret_cast(loadAddr)) + index * 4)) < 1024); int offset = reinterpret_cast(constPoolAddr) + (index * 4) - ((reinterpret_cast(instructionPtr) & ~0x03) + 4); - instruction &=0xf00; + instruction &= 0x0f00; instruction |= 0xd000; offset &= 0x03ff; instruction |= (offset >> 2); @@ -1354,84 +1458,121 @@ public: static void* readPointer(void* code) { - return static_cast(readInt32(code)); + return reinterpret_cast(readInt32(code)); } static void repatchInt32(void* where, int32_t value) { uint16_t* instructionPtr = reinterpret_cast(where); + ASSERT((instructionPtr[0] & 0xf000) == MOVL_READ_OFFPC_OPCODE); changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, value); } static void repatchCompact(void* where, int32_t value) { - repatchInt32(where, value); + uint16_t* instructionPtr = reinterpret_cast(where); + ASSERT(value >= 0); + ASSERT(value <= 60); + + // Handle the uncommon case where a flushConstantPool occured in movlMemRegCompact. + if ((instructionPtr[0] & 0xf000) == BRA_OPCODE) + instructionPtr += (instructionPtr[0] & 0x0fff) + 2; + + ASSERT((instructionPtr[0] & 0xf000) == MOVL_READ_OFFRM_OPCODE); + instructionPtr[0] = (instructionPtr[0] & 0xfff0) | (value >> 2); + cacheFlush(instructionPtr, sizeof(uint16_t)); } static void relinkCall(void* from, void* to) { uint16_t* instructionPtr = reinterpret_cast(from); instructionPtr -= 3; + ASSERT((instructionPtr[0] & 0xf000) == MOVL_READ_OFFPC_OPCODE); changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, reinterpret_cast(to)); } static void relinkJump(void* from, void* to) { uint16_t* instructionPtr = reinterpret_cast (from); - uint16_t instruction = *instructionPtr; - int32_t offsetBits = (reinterpret_cast(to) - reinterpret_cast(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(from) + 1, 3); - return; + instructionPtr -= 3; + ASSERT((instructionPtr[0] & 0xf000) == MOVL_READ_OFFPC_OPCODE); + ASSERT((instructionPtr[1] & 0xf0ff) == BRAF_OPCODE); + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, reinterpret_cast(to) - reinterpret_cast(from)); + } + + // Linking & patching + + static ptrdiff_t maxJumpReplacementSize() + { + return sizeof(SH4Word) * 6; + } + + static void replaceWithJump(void *instructionStart, void *to) + { + SH4Word* instruction = reinterpret_cast(instructionStart); + intptr_t difference = reinterpret_cast(to) - (reinterpret_cast(instruction) + 3 * sizeof(SH4Word)); + + if ((instruction[0] & 0xf000) == MOVL_READ_OFFPC_OPCODE) { + // We have an entry in constant pool and we potentially replace a branchPtrWithPatch, so let's backup what would be the + // condition (CMP/xx and Bx opcodes) for later use in revertJumpReplacementToBranchPtrWithPatch before putting the jump. + instruction[4] = instruction[1]; + instruction[5] = instruction[2]; + instruction[1] = (BRAF_OPCODE | (instruction[0] & 0x0f00)); + instruction[2] = NOP_OPCODE; + cacheFlush(&instruction[1], 2 * sizeof(SH4Word)); + } else { + instruction[0] = getOpcodeGroup3(MOVL_READ_OFFPC_OPCODE, SH4Registers::r13, 1); + instruction[1] = getOpcodeGroup2(BRAF_OPCODE, SH4Registers::r13); + instruction[2] = NOP_OPCODE; + cacheFlush(instruction, 3 * sizeof(SH4Word)); } - 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(from), 2); - return; + changePCrelativeAddress(instruction[0] & 0x00ff, instruction, difference); + } + + static void revertJumpReplacementToBranchPtrWithPatch(void* instructionStart, RegisterID rd, int imm) + { + SH4Word *insn = reinterpret_cast(instructionStart); + ASSERT((insn[0] & 0xf000) == MOVL_READ_OFFPC_OPCODE); + ASSERT((insn[0] & 0x00ff) != 1); + + insn[0] = getOpcodeGroup3(MOVL_READ_OFFPC_OPCODE, SH4Registers::r13, insn[0] & 0x00ff); + if ((insn[1] & 0xf0ff) == BRAF_OPCODE) { + insn[1] = (insn[4] & 0xf00f) | (rd << 8) | (SH4Registers::r13 << 4); // Restore CMP/xx opcode. + insn[2] = insn[5]; + ASSERT(((insn[2] & 0xff00) == BT_OPCODE) || ((insn[2] & 0xff00) == BF_OPCODE)); + ASSERT((insn[3] & 0xf000) == MOVL_READ_OFFPC_OPCODE); + insn[4] = (BRAF_OPCODE | (insn[3] & 0x0f00)); + insn[5] = NOP_OPCODE; + cacheFlush(insn, 6 * sizeof(SH4Word)); + } else { + // The branchPtrWithPatch has already been restored, so we just patch the immediate value and ASSERT all is as expected. + ASSERT((insn[1] & 0xf000) == 0x3000); + insn[1] = (insn[1] & 0xf00f) | (rd << 8) | (SH4Registers::r13 << 4); + cacheFlush(insn, 2 * sizeof(SH4Word)); + ASSERT(((insn[2] & 0xff00) == BT_OPCODE) || ((insn[2] & 0xff00) == BF_OPCODE)); + ASSERT((insn[3] & 0xf000) == MOVL_READ_OFFPC_OPCODE); + ASSERT(insn[5] == NOP_OPCODE); } - changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits - 2); - printInstr(*instructionPtr, reinterpret_cast(from)); + changePCrelativeAddress(insn[0] & 0x00ff, insn, imm); } - // Linking & patching - - void linkJump(AssemblerLabel from, AssemblerLabel to) + 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 (((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); + uint16_t* instructionPtr = getInstructionPtr(data(), from.m_offset) - 1; + int offsetBits = (to.m_offset - from.m_offset); + + if (type == JumpNear) { + uint16_t instruction = instructionPtr[0]; + int offset = (offsetBits - 2); + ASSERT((((instruction == BT_OPCODE) || (instruction == BF_OPCODE)) && (offset >= -256) && (offset <= 254)) + || ((instruction == BRA_OPCODE) && (offset >= -4096) && (offset <= 4094))); + *instructionPtr++ = instruction | (offset >> 1); + printInstr(*instructionPtr, from.m_offset + 2); return; } @@ -1439,24 +1580,18 @@ public: 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; - } + instructionPtr -= 2; + ASSERT((instructionPtr[1] & 0xf0ff) == BRAF_OPCODE); - instruction = *instructionPtr; - if ((instruction & 0xf000) == 0xe000) { + if ((instructionPtr[0] & 0xf000) == MOVIMM_OPCODE) { uint32_t* addr = getLdrImmAddressOnPool(instructionPtr, m_buffer.poolAddress()); - *addr = offsetBits - 2; + *addr = offsetBits; printInstr(*instructionPtr, from.m_offset + 2); return; } - changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits - 2); + ASSERT((instructionPtr[0] & 0xf000) == MOVL_READ_OFFPC_OPCODE); + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits); printInstr(*instructionPtr, from.m_offset + 2); } @@ -1490,9 +1625,30 @@ public: return readPCrelativeAddress((*(reinterpret_cast(code)) & 0xff), reinterpret_cast(code)); } - void* executableCopy(JSGlobalData& globalData, ExecutablePool* allocator) + static void* readCallTarget(void* from) { - return m_buffer.executableCopy(globalData, allocator); + uint16_t* instructionPtr = static_cast(from); + instructionPtr -= 3; + return reinterpret_cast(readPCrelativeAddress((*instructionPtr & 0xff), instructionPtr)); + } + + static void cacheFlush(void* code, size_t size) + { +#if OS(LINUX) + // Flush each page separately, otherwise the whole flush will fail if an uncommited page is in the area. + unsigned currentPage = reinterpret_cast(code) & ~(pageSize() - 1); + unsigned lastPage = (reinterpret_cast(code) + size - 1) & ~(pageSize() - 1); + do { +#if defined CACHEFLUSH_D_L2 + syscall(__NR_cacheflush, currentPage, pageSize(), CACHEFLUSH_D_WB | CACHEFLUSH_I | CACHEFLUSH_D_L2); +#else + syscall(__NR_cacheflush, currentPage, pageSize(), CACHEFLUSH_D_WB | CACHEFLUSH_I); +#endif + currentPage += pageSize(); + } while (lastPage >= currentPage); +#else +#error "The cacheFlush support is missing on this platform." +#endif } void prefix(uint16_t pre) @@ -1523,8 +1679,10 @@ public: void* data() const { return m_buffer.data(); } size_t codeSize() const { return m_buffer.codeSize(); } + unsigned debugOffset() { return m_buffer.debugOffset(); } + #ifdef SH4_ASSEMBLER_TRACING - static void printInstr(uint16_t opc, unsigned int size, bool isdoubleInst = true) + static void printInstr(uint16_t opc, unsigned size, bool isdoubleInst = true) { if (!getenv("JavaScriptCoreDumpJIT")) return; @@ -1667,12 +1825,18 @@ public: case FTRC_OPCODE: format = " FTRC FR%d, FPUL\n"; break; + case FABS_OPCODE: + format = " FABS FR%d\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) @@ -1798,9 +1962,18 @@ public: case MOVW_READ_RM_OPCODE: format = " MOV.W @R%d, R%d\n"; break; + case MOVW_READ_RMINC_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 MOVW_WRITE_R0RN_OPCODE: + format = " MOV.W R%d, @(R0, 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; @@ -1999,24 +2172,50 @@ public: static void vprintfStdoutInstr(const char* format, va_list args) { if (getenv("JavaScriptCoreDumpJIT")) - vfprintf(stdout, format, args); + WTF::dataLogFV(format, args); } - static void printBlockInstr(uint16_t* first, unsigned int offset, int nbInstr) + 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); + printInstr(*(first + i), offset + i); printfStdoutInstr(">> end repatch\n"); } #else - static void printInstr(uint16_t opc, unsigned int size, bool isdoubleInst = true) {}; - static void printBlockInstr(uint16_t* first, unsigned int offset, int nbInstr) {}; + static void printInstr(uint16_t, unsigned, bool = true) { }; + static void printBlockInstr(uint16_t*, unsigned, int) { }; #endif + static void replaceWithLoad(void* instructionStart) + { + SH4Word* insPtr = reinterpret_cast(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(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; + int m_indexOfLastWatchpoint; + int m_indexOfTailOfLastWatchpoint; }; } // namespace JSC