diff options
Diffstat (limited to 'JavaScriptCore/assembler')
21 files changed, 4614 insertions, 484 deletions
diff --git a/JavaScriptCore/assembler/ARMAssembler.cpp b/JavaScriptCore/assembler/ARMAssembler.cpp index 6dd2b87..86a1c14 100644 --- a/JavaScriptCore/assembler/ARMAssembler.cpp +++ b/JavaScriptCore/assembler/ARMAssembler.cpp @@ -262,28 +262,29 @@ ARMWord ARMAssembler::encodeComplexImm(ARMWord imm, int dest) // Memory load/store helpers -void ARMAssembler::dataTransfer32(bool isLoad, RegisterID srcDst, RegisterID base, int32_t offset) +void ARMAssembler::dataTransfer32(bool isLoad, RegisterID srcDst, RegisterID base, int32_t offset, bool bytes) { + ARMWord transferFlag = bytes ? DT_BYTE : 0; if (offset >= 0) { if (offset <= 0xfff) - dtr_u(isLoad, srcDst, base, offset); + dtr_u(isLoad, srcDst, base, offset | transferFlag); else if (offset <= 0xfffff) { add_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 12) | (10 << 8)); - dtr_u(isLoad, srcDst, ARMRegisters::S0, offset & 0xfff); + dtr_u(isLoad, srcDst, ARMRegisters::S0, (offset & 0xfff) | transferFlag); } else { - ARMWord reg = getImm(offset, ARMRegisters::S0); - dtr_ur(isLoad, srcDst, base, reg); + moveImm(offset, ARMRegisters::S0); + dtr_ur(isLoad, srcDst, base, ARMRegisters::S0 | transferFlag); } } else { offset = -offset; if (offset <= 0xfff) - dtr_d(isLoad, srcDst, base, offset); + dtr_d(isLoad, srcDst, base, offset | transferFlag); else if (offset <= 0xfffff) { sub_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 12) | (10 << 8)); - dtr_d(isLoad, srcDst, ARMRegisters::S0, offset & 0xfff); + dtr_d(isLoad, srcDst, ARMRegisters::S0, (offset & 0xfff) | transferFlag); } else { - ARMWord reg = getImm(offset, ARMRegisters::S0); - dtr_dr(isLoad, srcDst, base, reg); + moveImm(offset, ARMRegisters::S0); + dtr_dr(isLoad, srcDst, base, ARMRegisters::S0 | transferFlag); } } } @@ -354,11 +355,11 @@ void* ARMAssembler::executableCopy(ExecutablePool* allocator) for (Jumps::Iterator iter = m_jumps.begin(); iter != m_jumps.end(); ++iter) { // The last bit is set if the constant must be placed on constant pool. int pos = (*iter) & (~0x1); - ARMWord* ldrAddr = reinterpret_cast<ARMWord*>(data + pos); + ARMWord* ldrAddr = reinterpret_cast_ptr<ARMWord*>(data + pos); ARMWord* addr = getLdrImmAddress(ldrAddr); - if (*addr != 0xffffffff) { + if (*addr != InvalidBranchTarget) { if (!(*iter & 1)) { - int diff = reinterpret_cast<ARMWord*>(data + *addr) - (ldrAddr + DefaultPrefetching); + int diff = reinterpret_cast_ptr<ARMWord*>(data + *addr) - (ldrAddr + DefaultPrefetching); if ((diff <= BOFFSET_MAX && diff >= BOFFSET_MIN)) { *ldrAddr = B | getConditionalField(*ldrAddr) | (diff & BRANCH_MASK); diff --git a/JavaScriptCore/assembler/ARMAssembler.h b/JavaScriptCore/assembler/ARMAssembler.h index 6967b37..1d24dd3 100644 --- a/JavaScriptCore/assembler/ARMAssembler.h +++ b/JavaScriptCore/assembler/ARMAssembler.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 University of Szeged + * Copyright (C) 2009, 2010 University of Szeged * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,8 +27,6 @@ #ifndef ARMAssembler_h #define ARMAssembler_h -#include <wtf/Platform.h> - #if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) #include "AssemblerBufferWithConstantPool.h" @@ -42,32 +40,54 @@ namespace JSC { r0 = 0, r1, r2, - r3, - S0 = r3, + r3, S0 = r3, r4, r5, r6, r7, - r8, - S1 = r8, + r8, S1 = r8, r9, r10, r11, r12, - r13, - sp = r13, - r14, - lr = r14, - r15, - pc = r15 + r13, sp = r13, + r14, lr = r14, + r15, pc = r15 } RegisterID; typedef enum { d0, d1, d2, - d3, - SD0 = d3 + d3, SD0 = d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + d11, + d12, + d13, + d14, + d15, + d16, + d17, + d18, + d19, + d20, + d21, + d22, + d23, + d24, + d25, + d26, + d27, + d28, + d29, + d30, + d31 } FPRegisterID; } // namespace ARMRegisters @@ -120,11 +140,12 @@ namespace JSC { MVN = (0xf << 21), MUL = 0x00000090, MULL = 0x00c00090, - FADDD = 0x0e300b00, - FDIVD = 0x0e800b00, - FSUBD = 0x0e300b40, - FMULD = 0x0e200b00, - FCMPD = 0x0eb40b40, + VADD_F64 = 0x0e300b00, + VDIV_F64 = 0x0e800b00, + VSUB_F64 = 0x0e300b40, + VMUL_F64 = 0x0e200b00, + VCMP_F64 = 0x0eb40b40, + VSQRT_F64 = 0x0eb10bc0, DTR = 0x05000000, LDRH = 0x00100090, STRH = 0x00000090, @@ -133,14 +154,19 @@ namespace JSC { FDTR = 0x0d000b00, B = 0x0a000000, BL = 0x0b000000, - FMSR = 0x0e000a10, - FMRS = 0x0e100a10, - FSITOD = 0x0eb80bc0, - FTOSID = 0x0ebd0b40, - FMSTAT = 0x0ef1fa10, +#if WTF_ARM_ARCH_AT_LEAST(5) || defined(__ARM_ARCH_4T__) + BX = 0x012fff10, +#endif + VMOV_VFP = 0x0e000a10, + VMOV_ARM = 0x0e100a10, + VCVT_F64_S32 = 0x0eb80bc0, + VCVT_S32_F64 = 0x0ebd0b40, + VCVTR_S32_F64 = 0x0ebd0bc0, + VMRS_APSR = 0x0ef1fa10, #if WTF_ARM_ARCH_AT_LEAST(5) CLZ = 0x016f0f10, - BKPT = 0xe120070, + BKPT = 0xe1200070, + BLX = 0x012fff30, #endif #if WTF_ARM_ARCH_AT_LEAST(7) MOVW = 0x03000000, @@ -155,6 +181,7 @@ namespace JSC { SET_CC = (1 << 20), OP2_OFSREG = (1 << 25), DT_UP = (1 << 23), + DT_BYTE = (1 << 22), DT_WB = (1 << 21), // This flag is inlcuded in LDR and STR DT_PRE = (1 << 24), @@ -179,10 +206,11 @@ namespace JSC { enum { padForAlign8 = 0x00, padForAlign16 = 0x0000, - padForAlign32 = 0xee120070, + padForAlign32 = 0xe12fff7f // 'bkpt 0xffff' instruction. }; static const ARMWord INVALID_IMM = 0xf0000000; + static const ARMWord InvalidBranchTarget = 0xffffffff; static const int DefaultPrefetching = 2; class JmpSrc { @@ -229,10 +257,26 @@ namespace JSC { void emitInst(ARMWord op, int rd, int rn, ARMWord op2) { - ASSERT ( ((op2 & ~OP2_IMM) <= 0xfff) || (((op2 & ~OP2_IMMh) <= 0xfff)) ); + ASSERT(((op2 & ~OP2_IMM) <= 0xfff) || (((op2 & ~OP2_IMMh) <= 0xfff))); m_buffer.putInt(op | RN(rn) | RD(rd) | op2); } + void emitDoublePrecisionInst(ARMWord op, int dd, int dn, int dm) + { + ASSERT((dd >= 0 && dd <= 31) && (dn >= 0 && dn <= 31) && (dm >= 0 && dm <= 31)); + m_buffer.putInt(op | ((dd & 0xf) << 12) | ((dd & 0x10) << (22 - 4)) + | ((dn & 0xf) << 16) | ((dn & 0x10) << (7 - 4)) + | (dm & 0xf) | ((dm & 0x10) << (5 - 4))); + } + + void emitSinglePrecisionInst(ARMWord op, int sd, int sn, int sm) + { + ASSERT((sd >= 0 && sd <= 31) && (sn >= 0 && sn <= 31) && (sm >= 0 && sm <= 31)); + m_buffer.putInt(op | ((sd >> 1) << 12) | ((sd & 0x1) << 22) + | ((sn >> 1) << 16) | ((sn & 0x1) << 7) + | (sm >> 1) | ((sm & 0x1) << 5)); + } + void and_r(int rd, int rn, ARMWord op2, Condition cc = AL) { emitInst(static_cast<ARMWord>(cc) | AND, rd, rn, op2); @@ -328,6 +372,11 @@ namespace JSC { emitInst(static_cast<ARMWord>(cc) | CMP | SET_CC, 0, rn, op2); } + void cmn_r(int rn, ARMWord op2, Condition cc = AL) + { + emitInst(static_cast<ARMWord>(cc) | CMN | SET_CC, 0, rn, op2); + } + void orr_r(int rd, int rn, ARMWord op2, Condition cc = AL) { emitInst(static_cast<ARMWord>(cc) | ORR, rd, rn, op2); @@ -397,29 +446,34 @@ namespace JSC { m_buffer.putInt(static_cast<ARMWord>(cc) | MULL | RN(rdhi) | RD(rdlo) | RS(rn) | RM(rm)); } - void faddd_r(int dd, int dn, int dm, Condition cc = AL) + void vadd_f64_r(int dd, int dn, int dm, Condition cc = AL) { - emitInst(static_cast<ARMWord>(cc) | FADDD, dd, dn, dm); + emitDoublePrecisionInst(static_cast<ARMWord>(cc) | VADD_F64, dd, dn, dm); } - void fdivd_r(int dd, int dn, int dm, Condition cc = AL) + void vdiv_f64_r(int dd, int dn, int dm, Condition cc = AL) { - emitInst(static_cast<ARMWord>(cc) | FDIVD, dd, dn, dm); + emitDoublePrecisionInst(static_cast<ARMWord>(cc) | VDIV_F64, dd, dn, dm); } - void fsubd_r(int dd, int dn, int dm, Condition cc = AL) + void vsub_f64_r(int dd, int dn, int dm, Condition cc = AL) { - emitInst(static_cast<ARMWord>(cc) | FSUBD, dd, dn, dm); + emitDoublePrecisionInst(static_cast<ARMWord>(cc) | VSUB_F64, dd, dn, dm); } - void fmuld_r(int dd, int dn, int dm, Condition cc = AL) + void vmul_f64_r(int dd, int dn, int dm, Condition cc = AL) { - emitInst(static_cast<ARMWord>(cc) | FMULD, dd, dn, dm); + emitDoublePrecisionInst(static_cast<ARMWord>(cc) | VMUL_F64, dd, dn, dm); } - void fcmpd_r(int dd, int dm, Condition cc = AL) + void vcmp_f64_r(int dd, int dm, Condition cc = AL) { - emitInst(static_cast<ARMWord>(cc) | FCMPD, dd, 0, dm); + emitDoublePrecisionInst(static_cast<ARMWord>(cc) | VCMP_F64, dd, 0, dm); + } + + void vsqrt_f64_r(int dd, int dm, Condition cc = AL) + { + emitDoublePrecisionInst(static_cast<ARMWord>(cc) | VSQRT_F64, dd, 0, dm); } void ldr_imm(int rd, ARMWord imm, Condition cc = AL) @@ -506,29 +560,39 @@ namespace JSC { dtr_u(true, reg, ARMRegisters::sp, 0, cc); } - void fmsr_r(int dd, int rn, Condition cc = AL) + void vmov_vfp_r(int sn, int rt, Condition cc = AL) + { + ASSERT(rt <= 15); + emitSinglePrecisionInst(static_cast<ARMWord>(cc) | VMOV_VFP, rt << 1, sn, 0); + } + + void vmov_arm_r(int rt, int sn, Condition cc = AL) { - emitInst(static_cast<ARMWord>(cc) | FMSR, rn, dd, 0); + ASSERT(rt <= 15); + emitSinglePrecisionInst(static_cast<ARMWord>(cc) | VMOV_ARM, rt << 1, sn, 0); } - void fmrs_r(int rd, int dn, Condition cc = AL) + void vcvt_f64_s32_r(int dd, int sm, Condition cc = AL) { - emitInst(static_cast<ARMWord>(cc) | FMRS, rd, dn, 0); + ASSERT(!(sm & 0x1)); // sm must be divisible by 2 + emitDoublePrecisionInst(static_cast<ARMWord>(cc) | VCVT_F64_S32, dd, 0, (sm >> 1)); } - void fsitod_r(int dd, int dm, Condition cc = AL) + void vcvt_s32_f64_r(int sd, int dm, Condition cc = AL) { - emitInst(static_cast<ARMWord>(cc) | FSITOD, dd, 0, dm); + ASSERT(!(sd & 0x1)); // sd must be divisible by 2 + emitDoublePrecisionInst(static_cast<ARMWord>(cc) | VCVT_S32_F64, (sd >> 1), 0, dm); } - void ftosid_r(int fd, int dm, Condition cc = AL) + void vcvtr_s32_f64_r(int sd, int dm, Condition cc = AL) { - emitInst(static_cast<ARMWord>(cc) | FTOSID, fd, 0, dm); + ASSERT(!(sd & 0x1)); // sd must be divisible by 2 + emitDoublePrecisionInst(static_cast<ARMWord>(cc) | VCVTR_S32_F64, (sd >> 1), 0, dm); } - void fmstat(Condition cc = AL) + void vmrs_apsr(Condition cc = AL) { - m_buffer.putInt(static_cast<ARMWord>(cc) | FMSTAT); + m_buffer.putInt(static_cast<ARMWord>(cc) | VMRS_APSR); } #if WTF_ARM_ARCH_AT_LEAST(5) @@ -548,6 +612,28 @@ namespace JSC { #endif } + void bx(int rm, Condition cc = AL) + { +#if WTF_ARM_ARCH_AT_LEAST(5) || defined(__ARM_ARCH_4T__) + emitInst(static_cast<ARMWord>(cc) | BX, 0, 0, RM(rm)); +#else + mov_r(ARMRegisters::pc, RM(rm), cc); +#endif + } + + JmpSrc blx(int rm, Condition cc = AL) + { +#if WTF_ARM_ARCH_AT_LEAST(5) + emitInst(static_cast<ARMWord>(cc) | BLX, 0, 0, RM(rm)); +#else + ASSERT(rm != 14); + ensureSpace(2 * sizeof(ARMWord), 0); + mov_r(ARMRegisters::lr, ARMRegisters::pc, cc); + bx(rm, cc); +#endif + return JmpSrc(m_buffer.uncheckedSize()); + } + static ARMWord lsl(int reg, ARMWord value) { ASSERT(reg <= ARMRegisters::pc); @@ -620,13 +706,17 @@ namespace JSC { return label(); } - JmpSrc jmp(Condition cc = AL, int useConstantPool = 0) + JmpSrc loadBranchTarget(int rd, Condition cc = AL, int useConstantPool = 0) { ensureSpace(sizeof(ARMWord), sizeof(ARMWord)); - int s = m_buffer.uncheckedSize(); - ldr_un_imm(ARMRegisters::pc, 0xffffffff, cc); - m_jumps.append(s | (useConstantPool & 0x1)); - return JmpSrc(s); + m_jumps.append(m_buffer.uncheckedSize() | (useConstantPool & 0x1)); + ldr_un_imm(rd, InvalidBranchTarget, cc); + return JmpSrc(m_buffer.uncheckedSize()); + } + + JmpSrc jmp(Condition cc = AL, int useConstantPool = 0) + { + return loadBranchTarget(ARMRegisters::pc, cc, useConstantPool); } void* executableCopy(ExecutablePool* allocator); @@ -635,6 +725,14 @@ namespace JSC { static ARMWord* getLdrImmAddress(ARMWord* insn) { +#if WTF_ARM_ARCH_AT_LEAST(5) + // Check for call + if ((*insn & 0x0f7f0000) != 0x051f0000) { + // Must be BLX + ASSERT((*insn & 0x012fff30) == 0x012fff30); + insn--; + } +#endif // Must be an ldr ..., [pc +/- imm] ASSERT((*insn & 0x0f7f0000) == 0x051f0000); @@ -699,51 +797,55 @@ namespace JSC { } // Linkers + static intptr_t getAbsoluteJumpAddress(void* base, int offset = 0) + { + return reinterpret_cast<intptr_t>(base) + offset - sizeof(ARMWord); + } void linkJump(JmpSrc from, JmpDst to) { - ARMWord* insn = reinterpret_cast<ARMWord*>(m_buffer.data()) + (from.m_offset / sizeof(ARMWord)); + ARMWord* insn = reinterpret_cast<ARMWord*>(getAbsoluteJumpAddress(m_buffer.data(), from.m_offset)); ARMWord* addr = getLdrImmAddressOnPool(insn, m_buffer.poolAddress()); *addr = static_cast<ARMWord>(to.m_offset); } static void linkJump(void* code, JmpSrc from, void* to) { - patchPointerInternal(reinterpret_cast<intptr_t>(code) + from.m_offset, to); + patchPointerInternal(getAbsoluteJumpAddress(code, from.m_offset), to); } static void relinkJump(void* from, void* to) { - patchPointerInternal(reinterpret_cast<intptr_t>(from) - sizeof(ARMWord), to); + patchPointerInternal(getAbsoluteJumpAddress(from), to); } static void linkCall(void* code, JmpSrc from, void* to) { - patchPointerInternal(reinterpret_cast<intptr_t>(code) + from.m_offset, to); + patchPointerInternal(getAbsoluteJumpAddress(code, from.m_offset), to); } static void relinkCall(void* from, void* to) { - patchPointerInternal(reinterpret_cast<intptr_t>(from) - sizeof(ARMWord), to); + patchPointerInternal(getAbsoluteJumpAddress(from), to); } // Address operations static void* getRelocatedAddress(void* code, JmpSrc jump) { - return reinterpret_cast<void*>(reinterpret_cast<ARMWord*>(code) + jump.m_offset / sizeof(ARMWord) + 1); + return reinterpret_cast<void*>(reinterpret_cast<char*>(code) + jump.m_offset); } static void* getRelocatedAddress(void* code, JmpDst label) { - return reinterpret_cast<void*>(reinterpret_cast<ARMWord*>(code) + label.m_offset / sizeof(ARMWord)); + return reinterpret_cast<void*>(reinterpret_cast<char*>(code) + label.m_offset); } // Address differences static int getDifferenceBetweenLabels(JmpDst from, JmpSrc to) { - return (to.m_offset + sizeof(ARMWord)) - from.m_offset; + return to.m_offset - from.m_offset; } static int getDifferenceBetweenLabels(JmpDst from, JmpDst to) @@ -753,7 +855,7 @@ namespace JSC { static unsigned getCallReturnOffset(JmpSrc call) { - return call.m_offset + sizeof(ARMWord); + return call.m_offset; } // Handle immediates @@ -778,9 +880,18 @@ namespace JSC { void moveImm(ARMWord imm, int dest); ARMWord encodeComplexImm(ARMWord imm, int dest); + ARMWord getOffsetForHalfwordDataTransfer(ARMWord imm, int tmpReg) + { + // Encode immediate data in the instruction if it is possible + if (imm <= 0xff) + return getOp2Byte(imm); + // Otherwise, store the data in a temporary register + return encodeComplexImm(imm, tmpReg); + } + // Memory load/store helpers - void dataTransfer32(bool isLoad, RegisterID srcDst, RegisterID base, int32_t offset); + void dataTransfer32(bool isLoad, RegisterID srcDst, RegisterID base, int32_t offset, bool bytes = false); void baseIndexTransfer32(bool isLoad, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset); void doubleTransfer(bool isLoad, FPRegisterID srcDst, RegisterID base, int32_t offset); diff --git a/JavaScriptCore/assembler/ARMv7Assembler.cpp b/JavaScriptCore/assembler/ARMv7Assembler.cpp new file mode 100644 index 0000000..7aa1f10 --- /dev/null +++ b/JavaScriptCore/assembler/ARMv7Assembler.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2010 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. AND ITS CONTRIBUTORS ``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 ITS 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. + */ + +#include "config.h" + +#if ENABLE(ASSEMBLER) && CPU(ARM_THUMB2) + +#include "ARMv7Assembler.h" + +namespace JSC { + +const int ARMv7Assembler::JumpSizes[] = { 0xffffffff, sizeof(uint16_t), sizeof(uint16_t), + 2 * sizeof(uint16_t), 2 * sizeof(uint16_t), 3 * sizeof(uint16_t), 5 * sizeof(uint16_t), 6 * sizeof(uint16_t) }; +const int ARMv7Assembler::JumpPaddingSizes[] = { 0, 5 * sizeof(uint16_t), 6 * sizeof(uint16_t), + 5 * sizeof(uint16_t), 6 * sizeof(uint16_t) }; + +} + +#endif diff --git a/JavaScriptCore/assembler/ARMv7Assembler.h b/JavaScriptCore/assembler/ARMv7Assembler.h index 4e394b2..a40208a 100644 --- a/JavaScriptCore/assembler/ARMv7Assembler.h +++ b/JavaScriptCore/assembler/ARMv7Assembler.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 University of Szeged * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,8 +27,6 @@ #ifndef ARMAssembler_h #define ARMAssembler_h -#include <wtf/Platform.h> - #if ENABLE(ASSEMBLER) && CPU(ARM_THUMB2) #include "AssemblerBuffer.h" @@ -57,107 +56,122 @@ namespace ARMRegisters { r15, pc = r15, } RegisterID; - // s0 == d0 == q0 - // s4 == d2 == q1 - // etc typedef enum { - s0 = 0, - s1 = 1, - s2 = 2, - s3 = 3, - s4 = 4, - s5 = 5, - s6 = 6, - s7 = 7, - s8 = 8, - s9 = 9, - s10 = 10, - s11 = 11, - s12 = 12, - s13 = 13, - s14 = 14, - s15 = 15, - s16 = 16, - s17 = 17, - s18 = 18, - s19 = 19, - s20 = 20, - s21 = 21, - s22 = 22, - s23 = 23, - s24 = 24, - s25 = 25, - s26 = 26, - s27 = 27, - s28 = 28, - s29 = 29, - s30 = 30, - s31 = 31, - d0 = 0 << 1, - d1 = 1 << 1, - d2 = 2 << 1, - d3 = 3 << 1, - d4 = 4 << 1, - d5 = 5 << 1, - d6 = 6 << 1, - d7 = 7 << 1, - d8 = 8 << 1, - d9 = 9 << 1, - d10 = 10 << 1, - d11 = 11 << 1, - d12 = 12 << 1, - d13 = 13 << 1, - d14 = 14 << 1, - d15 = 15 << 1, - d16 = 16 << 1, - d17 = 17 << 1, - d18 = 18 << 1, - d19 = 19 << 1, - d20 = 20 << 1, - d21 = 21 << 1, - d22 = 22 << 1, - d23 = 23 << 1, - d24 = 24 << 1, - d25 = 25 << 1, - d26 = 26 << 1, - d27 = 27 << 1, - d28 = 28 << 1, - d29 = 29 << 1, - d30 = 30 << 1, - d31 = 31 << 1, - q0 = 0 << 2, - q1 = 1 << 2, - q2 = 2 << 2, - q3 = 3 << 2, - q4 = 4 << 2, - q5 = 5 << 2, - q6 = 6 << 2, - q7 = 7 << 2, - q8 = 8 << 2, - q9 = 9 << 2, - q10 = 10 << 2, - q11 = 11 << 2, - q12 = 12 << 2, - q13 = 13 << 2, - q14 = 14 << 2, - q15 = 15 << 2, - q16 = 16 << 2, - q17 = 17 << 2, - q18 = 18 << 2, - q19 = 19 << 2, - q20 = 20 << 2, - q21 = 21 << 2, - q22 = 22 << 2, - q23 = 23 << 2, - q24 = 24 << 2, - q25 = 25 << 2, - q26 = 26 << 2, - q27 = 27 << 2, - q28 = 28 << 2, - q29 = 29 << 2, - q30 = 30 << 2, - q31 = 31 << 2, - } FPRegisterID; + s0, + s1, + s2, + s3, + s4, + s5, + s6, + s7, + s8, + s9, + s10, + s11, + s12, + s13, + s14, + s15, + s16, + s17, + s18, + s19, + s20, + s21, + s22, + s23, + s24, + s25, + s26, + s27, + s28, + s29, + s30, + s31, + } FPSingleRegisterID; + + typedef enum { + d0, + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + d11, + d12, + d13, + d14, + d15, + d16, + d17, + d18, + d19, + d20, + d21, + d22, + d23, + d24, + d25, + d26, + d27, + d28, + d29, + d30, + d31, + } FPDoubleRegisterID; + + typedef enum { + q0, + q1, + q2, + q3, + q4, + q5, + q6, + q7, + q8, + q9, + q10, + q11, + q12, + q13, + q14, + q15, + q16, + q17, + q18, + q19, + q20, + q21, + q22, + q23, + q24, + q25, + q26, + q27, + q28, + q29, + q30, + q31, + } FPQuadRegisterID; + + inline FPSingleRegisterID asSingle(FPDoubleRegisterID reg) + { + ASSERT(reg < d16); + return (FPSingleRegisterID)(reg << 1); + } + + inline FPDoubleRegisterID asDouble(FPSingleRegisterID reg) + { + ASSERT(!(reg & 1)); + return (FPDoubleRegisterID)(reg >> 1); + } } class ARMv7Assembler; @@ -355,6 +369,39 @@ private: ThumbImmediateValue m_value; }; +class VFPImmediate { +public: + VFPImmediate(double d) + : m_value(-1) + { + union { + uint64_t i; + double d; + } u; + + u.d = d; + + int sign = static_cast<int>(u.i >> 63); + int exponent = static_cast<int>(u.i >> 52) & 0x7ff; + uint64_t mantissa = u.i & 0x000fffffffffffffull; + + if ((exponent >= 0x3fc) && (exponent <= 0x403) && !(mantissa & 0x0000ffffffffffffull)) + m_value = (sign << 7) | ((exponent & 7) << 4) | (int)(mantissa >> 48); + } + + bool isValid() + { + return m_value != -1; + } + + uint8_t value() + { + return (uint8_t)m_value; + } + +private: + int m_value; +}; typedef enum { SRType_LSL, @@ -393,23 +440,11 @@ private: }; struct { unsigned type : 2; - unsigned amount : 5; + unsigned amount : 6; }; } m_u; }; - -/* -Some features of the Thumb instruction set are deprecated in ARMv7. Deprecated features affecting -instructions supported by ARMv7-M are as follows: -• use of the PC as <Rd> or <Rm> in a 16-bit ADD (SP plus register) instruction -• use of the SP as <Rm> in a 16-bit ADD (SP plus register) instruction -• use of the SP as <Rm> in a 16-bit CMP (register) instruction -• use of MOV (register) instructions in which <Rd> is the SP or PC and <Rm> is also the SP or PC. -• use of <Rn> as the lowest-numbered register in the register list of a 16-bit STM instruction with base -register writeback -*/ - class ARMv7Assembler { public: ~ARMv7Assembler() @@ -418,15 +453,17 @@ public: } typedef ARMRegisters::RegisterID RegisterID; - typedef ARMRegisters::FPRegisterID FPRegisterID; + typedef ARMRegisters::FPSingleRegisterID FPSingleRegisterID; + typedef ARMRegisters::FPDoubleRegisterID FPDoubleRegisterID; + typedef ARMRegisters::FPQuadRegisterID FPQuadRegisterID; // (HS, LO, HI, LS) -> (AE, B, A, BE) // (VS, VC) -> (O, NO) typedef enum { ConditionEQ, ConditionNE, - ConditionHS, - ConditionLO, + ConditionHS, ConditionCS = ConditionHS, + ConditionLO, ConditionCC = ConditionLO, ConditionMI, ConditionPL, ConditionVS, @@ -438,14 +475,43 @@ public: ConditionGT, ConditionLE, ConditionAL, - - ConditionCS = ConditionHS, - ConditionCC = ConditionLO, + ConditionInvalid } Condition; + enum JumpType { JumpFixed, JumpNoCondition, JumpCondition, JumpNoConditionFixedSize, JumpConditionFixedSize, JumpTypeCount }; + enum JumpLinkType { LinkInvalid, LinkJumpT1, LinkJumpT2, LinkJumpT3, + LinkJumpT4, LinkConditionalJumpT4, LinkBX, LinkConditionalBX, JumpLinkTypeCount }; + static const int JumpSizes[JumpLinkTypeCount]; + static const int JumpPaddingSizes[JumpTypeCount]; + class LinkRecord { + public: + LinkRecord(intptr_t from, intptr_t to, JumpType type, Condition condition) + : m_from(from) + , m_to(to) + , m_type(type) + , m_linkType(LinkInvalid) + , m_condition(condition) + { + } + intptr_t from() const { return m_from; } + void setFrom(intptr_t from) { m_from = from; } + intptr_t to() const { return m_to; } + JumpType type() const { return m_type; } + JumpLinkType linkType() const { return m_linkType; } + void setLinkType(JumpLinkType linkType) { ASSERT(m_linkType == LinkInvalid); m_linkType = linkType; } + Condition condition() const { return m_condition; } + private: + intptr_t m_from : 31; + intptr_t m_to : 31; + JumpType m_type : 3; + JumpLinkType m_linkType : 4; + Condition m_condition : 16; + }; + class JmpSrc { friend class ARMv7Assembler; friend class ARMInstructionFormatter; + friend class LinkBuffer; public: JmpSrc() : m_offset(-1) @@ -453,17 +519,32 @@ public: } private: - JmpSrc(int offset) + JmpSrc(int offset, JumpType type) : m_offset(offset) + , m_condition(ConditionInvalid) + , m_type(type) { + ASSERT(m_type == JumpFixed || m_type == JumpNoCondition || m_type == JumpNoConditionFixedSize); + } + + JmpSrc(int offset, JumpType type, Condition condition) + : m_offset(offset) + , m_condition(condition) + , m_type(type) + { + ASSERT(m_type == JumpFixed || m_type == JumpCondition || m_type == JumpConditionFixedSize); } int m_offset; + Condition m_condition : 16; + JumpType m_type : 16; + }; class JmpDst { friend class ARMv7Assembler; friend class ARMInstructionFormatter; + friend class LinkBuffer; public: JmpDst() : m_offset(-1) @@ -487,70 +568,22 @@ public: private: - struct LinkRecord { - LinkRecord(intptr_t from, intptr_t to) - : from(from) - , to(to) - { - } - - intptr_t from; - intptr_t to; - }; - // ARMv7, Appx-A.6.3 bool BadReg(RegisterID reg) { return (reg == ARMRegisters::sp) || (reg == ARMRegisters::pc); } - bool isSingleRegister(FPRegisterID reg) - { - // Check that the high bit isn't set (q16+), and that the low bit isn't (s1, s3, etc). - return !(reg & ~31); - } - - bool isDoubleRegister(FPRegisterID reg) - { - // Check that the high bit isn't set (q16+), and that the low bit isn't (s1, s3, etc). - return !(reg & ~(31 << 1)); - } - - bool isQuadRegister(FPRegisterID reg) - { - return !(reg & ~(31 << 2)); - } - - uint32_t singleRegisterNum(FPRegisterID reg) - { - ASSERT(isSingleRegister(reg)); - return reg; - } - - uint32_t doubleRegisterNum(FPRegisterID reg) - { - ASSERT(isDoubleRegister(reg)); - return reg >> 1; - } - - uint32_t quadRegisterNum(FPRegisterID reg) + uint32_t singleRegisterMask(FPSingleRegisterID rdNum, int highBitsShift, int lowBitShift) { - ASSERT(isQuadRegister(reg)); - return reg >> 2; - } - - uint32_t singleRegisterMask(FPRegisterID rd, int highBitsShift, int lowBitShift) - { - uint32_t rdNum = singleRegisterNum(rd); uint32_t rdMask = (rdNum >> 1) << highBitsShift; if (rdNum & 1) rdMask |= 1 << lowBitShift; return rdMask; } - uint32_t doubleRegisterMask(FPRegisterID rd, int highBitShift, int lowBitsShift) + uint32_t doubleRegisterMask(FPDoubleRegisterID rdNum, int highBitShift, int lowBitsShift) { - uint32_t rdNum = doubleRegisterNum(rd); uint32_t rdMask = (rdNum & 0xf) << lowBitsShift; if (rdNum & 16) rdMask |= 1 << highBitShift; @@ -559,22 +592,17 @@ private: typedef enum { OP_ADD_reg_T1 = 0x1800, - OP_ADD_S_reg_T1 = 0x1800, OP_SUB_reg_T1 = 0x1A00, - OP_SUB_S_reg_T1 = 0x1A00, OP_ADD_imm_T1 = 0x1C00, - OP_ADD_S_imm_T1 = 0x1C00, OP_SUB_imm_T1 = 0x1E00, - OP_SUB_S_imm_T1 = 0x1E00, OP_MOV_imm_T1 = 0x2000, OP_CMP_imm_T1 = 0x2800, OP_ADD_imm_T2 = 0x3000, - OP_ADD_S_imm_T2 = 0x3000, OP_SUB_imm_T2 = 0x3800, - OP_SUB_S_imm_T2 = 0x3800, OP_AND_reg_T1 = 0x4000, OP_EOR_reg_T1 = 0x4040, OP_TST_reg_T1 = 0x4200, + OP_RSB_imm_T1 = 0x4240, OP_CMP_reg_T1 = 0x4280, OP_ORR_reg_T1 = 0x4300, OP_MVN_reg_T1 = 0x43C0, @@ -582,11 +610,13 @@ private: OP_MOV_reg_T1 = 0x4600, OP_BLX = 0x4700, OP_BX = 0x4700, - OP_LDRH_reg_T1 = 0x5A00, OP_STR_reg_T1 = 0x5000, OP_LDR_reg_T1 = 0x5800, + OP_LDRH_reg_T1 = 0x5A00, + OP_LDRB_reg_T1 = 0x5C00, OP_STR_imm_T1 = 0x6000, OP_LDR_imm_T1 = 0x6800, + OP_LDRB_imm_T1 = 0x7800, OP_LDRH_imm_T1 = 0x8800, OP_STR_imm_T2 = 0x9000, OP_LDR_imm_T2 = 0x9800, @@ -599,9 +629,12 @@ private: } OpcodeID; typedef enum { + OP_B_T1 = 0xD000, + OP_B_T2 = 0xE000, OP_AND_reg_T2 = 0xEA00, OP_TST_reg_T2 = 0xEA10, OP_ORR_reg_T2 = 0xEA40, + OP_ORR_S_reg_T2 = 0xEA50, OP_ASR_imm_T1 = 0xEA4F, OP_LSL_imm_T1 = 0xEA4F, OP_LSR_imm_T1 = 0xEA4F, @@ -613,6 +646,19 @@ private: OP_SUB_reg_T2 = 0xEBA0, OP_SUB_S_reg_T2 = 0xEBB0, OP_CMP_reg_T2 = 0xEBB0, + OP_VSTR = 0xED00, + OP_VLDR = 0xED10, + OP_VMOV_StoC = 0xEE00, + OP_VMOV_CtoS = 0xEE10, + OP_VMUL_T2 = 0xEE20, + OP_VADD_T2 = 0xEE30, + OP_VSUB_T2 = 0xEE30, + OP_VDIV = 0xEE80, + OP_VCMP_T1 = 0xEEB0, + OP_VCVT_FPIVFP = 0xEEB0, + OP_VMOV_IMM_T2 = 0xEEB0, + OP_VMRS = 0xEEB0, + OP_B_T3a = 0xF000, OP_B_T4a = 0xF000, OP_AND_imm_T1 = 0xF000, OP_TST_imm = 0xF010, @@ -626,17 +672,21 @@ private: OP_SUB_imm_T3 = 0xF1A0, OP_SUB_S_imm_T3 = 0xF1B0, OP_CMP_imm_T2 = 0xF1B0, + OP_RSB_imm_T2 = 0xF1C0, OP_ADD_imm_T4 = 0xF200, OP_MOV_imm_T3 = 0xF240, OP_SUB_imm_T4 = 0xF2A0, OP_MOVT = 0xF2C0, OP_NOP_T2a = 0xF3AF, + OP_LDRB_imm_T3 = 0xF810, + OP_LDRB_reg_T2 = 0xF810, OP_LDRH_reg_T2 = 0xF830, OP_LDRH_imm_T3 = 0xF830, OP_STR_imm_T4 = 0xF840, OP_STR_reg_T2 = 0xF840, OP_LDR_imm_T4 = 0xF850, OP_LDR_reg_T2 = 0xF850, + OP_LDRB_imm_T2 = 0xF890, OP_LDRH_imm_T2 = 0xF8B0, OP_STR_imm_T3 = 0xF8C0, OP_LDR_imm_T3 = 0xF8D0, @@ -644,12 +694,26 @@ private: OP_LSR_reg_T2 = 0xFA20, OP_ASR_reg_T2 = 0xFA40, OP_ROR_reg_T2 = 0xFA60, + OP_CLZ = 0xFAB0, OP_SMULL_T1 = 0xFB80, } OpcodeID1; typedef enum { - OP_B_T4b = 0x9000, + OP_VADD_T2b = 0x0A00, + OP_VDIVb = 0x0A00, + OP_VLDRb = 0x0A00, + OP_VMOV_IMM_T2b = 0x0A00, + OP_VMUL_T2b = 0x0A00, + OP_VSTRb = 0x0A00, + OP_VMOV_CtoSb = 0x0A10, + OP_VMOV_StoCb = 0x0A10, + OP_VMRSb = 0x0A10, + OP_VCMP_T1b = 0x0A40, + OP_VCVT_FPIVFPb = 0x0A40, + OP_VSUB_T2b = 0x0A40, OP_NOP_T2b = 0x8000, + OP_B_T3b = 0x8000, + OP_B_T4b = 0x9000, } OpcodeID2; struct FourFours { @@ -685,7 +749,7 @@ private: | (ifThenElseConditionBit(condition, inst3if) << 2) | (ifThenElseConditionBit(condition, inst4if) << 1) | 1; - ASSERT((condition != ConditionAL) || (mask & (mask - 1))); + ASSERT((condition != ConditionAL) || !(mask & (mask - 1))); return (condition << 4) | mask; } uint8_t ifThenElse(Condition condition, bool inst2if, bool inst3if) @@ -693,26 +757,25 @@ private: int mask = (ifThenElseConditionBit(condition, inst2if) << 3) | (ifThenElseConditionBit(condition, inst3if) << 2) | 2; - ASSERT((condition != ConditionAL) || (mask & (mask - 1))); + ASSERT((condition != ConditionAL) || !(mask & (mask - 1))); return (condition << 4) | mask; } uint8_t ifThenElse(Condition condition, bool inst2if) { int mask = (ifThenElseConditionBit(condition, inst2if) << 3) | 4; - ASSERT((condition != ConditionAL) || (mask & (mask - 1))); + ASSERT((condition != ConditionAL) || !(mask & (mask - 1))); return (condition << 4) | mask; } uint8_t ifThenElse(Condition condition) { int mask = 8; - ASSERT((condition != ConditionAL) || (mask & (mask - 1))); return (condition << 4) | mask; } public: - + void add(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) { // Rd can only be SP if Rn is also SP. @@ -780,10 +843,10 @@ public: if (!((rd | rn) & 8)) { if (imm.isUInt3()) { - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_S_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); return; } else if ((rd == rn) && imm.isUInt8()) { - m_formatter.oneWordOp5Reg3Imm8(OP_ADD_S_imm_T2, rd, imm.getUInt8()); + m_formatter.oneWordOp5Reg3Imm8(OP_ADD_imm_T2, rd, imm.getUInt8()); return; } } @@ -805,7 +868,7 @@ public: void add_S(RegisterID rd, RegisterID rn, RegisterID rm) { if (!((rd | rn | rm) & 8)) - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_S_reg_T1, rm, rn, rd); + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_reg_T1, rm, rn, rd); else add_S(rd, rn, rm, ShiftTypeAndAmount()); } @@ -851,27 +914,33 @@ public: ASSERT(!BadReg(rm)); m_formatter.twoWordOp12Reg4FourFours(OP_ASR_reg_T2, rn, FourFours(0xf, rd, 0, rm)); } - + // Only allowed in IT (if then) block if last instruction. - JmpSrc b() + JmpSrc b(JumpType type) { m_formatter.twoWordOp16Op16(OP_B_T4a, OP_B_T4b); - return JmpSrc(m_formatter.size()); + return JmpSrc(m_formatter.size(), type); } // Only allowed in IT (if then) block if last instruction. - JmpSrc blx(RegisterID rm) + JmpSrc blx(RegisterID rm, JumpType type) { ASSERT(rm != ARMRegisters::pc); m_formatter.oneWordOp8RegReg143(OP_BLX, rm, (RegisterID)8); - return JmpSrc(m_formatter.size()); + return JmpSrc(m_formatter.size(), type); } // Only allowed in IT (if then) block if last instruction. - JmpSrc bx(RegisterID rm) + JmpSrc bx(RegisterID rm, JumpType type, Condition condition) + { + m_formatter.oneWordOp8RegReg143(OP_BX, rm, (RegisterID)0); + return JmpSrc(m_formatter.size(), type, condition); + } + + JmpSrc bx(RegisterID rm, JumpType type) { m_formatter.oneWordOp8RegReg143(OP_BX, rm, (RegisterID)0); - return JmpSrc(m_formatter.size()); + return JmpSrc(m_formatter.size(), type); } void bkpt(uint8_t imm=0) @@ -879,6 +948,13 @@ public: m_formatter.oneWordOp8Imm8(OP_BKPT, imm); } + void clz(RegisterID rd, RegisterID rm) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_CLZ, rm, FourFours(0xf, rd, 8, rm)); + } + void cmn(RegisterID rn, ARMThumbImmediate imm) { ASSERT(rn != ARMRegisters::pc); @@ -1080,6 +1156,52 @@ public: m_formatter.twoWordOp12Reg4FourFours(OP_LDRH_reg_T2, rn, FourFours(rt, 0, shift, rm)); } + void ldrb(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rn != ARMRegisters::pc); // LDR (literal) + ASSERT(imm.isUInt12()); + + if (!((rt | rn) & 8) && imm.isUInt5()) + m_formatter.oneWordOp5Imm5Reg3Reg3(OP_LDRB_imm_T1, imm.getUInt5(), rn, rt); + else + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDRB_imm_T2, rn, rt, imm.getUInt12()); + } + + void ldrb(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(index || wback); + ASSERT(!wback | (rt != rn)); + + bool add = true; + if (offset < 0) { + add = false; + offset = -offset; + } + + ASSERT(!(offset & ~0xff)); + + offset |= (wback << 8); + offset |= (add << 9); + offset |= (index << 10); + offset |= (1 << 11); + + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDRB_imm_T3, rn, rt, offset); + } + + void ldrb(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) + { + ASSERT(rn != ARMRegisters::pc); // LDR (literal) + ASSERT(!BadReg(rm)); + ASSERT(shift <= 3); + + if (!shift && !((rt | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDRB_reg_T1, rm, rn, rt); + else + m_formatter.twoWordOp12Reg4FourFours(OP_LDRB_reg_T2, rn, FourFours(rt, 0, shift, rm)); + } + void lsl(RegisterID rd, RegisterID rm, int32_t shiftAmount) { ASSERT(!BadReg(rd)); @@ -1169,6 +1291,12 @@ public: mvn(rd, rm, ShiftTypeAndAmount()); } + void neg(RegisterID rd, RegisterID rm) + { + ARMThumbImmediate zero = ARMThumbImmediate::makeUInt12(0); + sub(rd, zero, rm); + } + void orr(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) { ASSERT(!BadReg(rd)); @@ -1195,6 +1323,24 @@ public: orr(rd, rn, rm, ShiftTypeAndAmount()); } + void orr_S(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_ORR_S_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + void orr_S(RegisterID rd, RegisterID rn, RegisterID rm) + { + if ((rd == rn) && !((rd | rm) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_ORR_reg_T1, rm, rd); + else if ((rd == rm) && !((rd | rn) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_ORR_reg_T1, rn, rd); + else + orr_S(rd, rn, rm, ShiftTypeAndAmount()); + } + void ror(RegisterID rd, RegisterID rm, int32_t shiftAmount) { ASSERT(!BadReg(rd)); @@ -1311,6 +1457,19 @@ public: } } + void sub(RegisterID rd, ARMThumbImmediate imm, RegisterID rn) + { + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isValid()); + ASSERT(imm.isUInt12()); + + if (!((rd | rn) & 8) && !imm.getUInt12()) + m_formatter.oneWordOp10Reg3Reg3(OP_RSB_imm_T1, rn, rd); + else + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_RSB_imm_T2, rn, rd, imm); + } + void sub(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) { ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); @@ -1343,10 +1502,10 @@ public: return; } else if (!((rd | rn) & 8)) { if (imm.isUInt3()) { - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_S_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); return; } else if ((rd == rn) && imm.isUInt8()) { - m_formatter.oneWordOp5Reg3Imm8(OP_SUB_S_imm_T2, rd, imm.getUInt8()); + m_formatter.oneWordOp5Reg3Imm8(OP_SUB_imm_T2, rd, imm.getUInt8()); return; } } @@ -1368,7 +1527,7 @@ public: void sub_S(RegisterID rd, RegisterID rn, RegisterID rm) { if (!((rd | rn | rm) & 8)) - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_S_reg_T1, rm, rn, rd); + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_reg_T1, rm, rn, rd); else sub_S(rd, rn, rm, ShiftTypeAndAmount()); } @@ -1396,62 +1555,75 @@ public: m_formatter.oneWordOp10Reg3Reg3(OP_TST_reg_T1, rm, rn); } - void vadd_F64(FPRegisterID rd, FPRegisterID rn, FPRegisterID rm) + void vadd_F64(FPDoubleRegisterID rd, FPDoubleRegisterID rn, FPDoubleRegisterID rm) { - m_formatter.vfpOp(0x0b00ee30 | doubleRegisterMask(rd, 6, 28) | doubleRegisterMask(rn, 23, 0) | doubleRegisterMask(rm, 21, 16)); + m_formatter.vfpOp(OP_VADD_T2, OP_VADD_T2b, true, rn, rd, rm); } - void vcmp_F64(FPRegisterID rd, FPRegisterID rm) + void vcmp_F64(FPDoubleRegisterID rd, FPDoubleRegisterID rm) { - m_formatter.vfpOp(0x0bc0eeb4 | doubleRegisterMask(rd, 6, 28) | doubleRegisterMask(rm, 21, 16)); + m_formatter.vfpOp(OP_VCMP_T1, OP_VCMP_T1b, true, VFPOperand(4), rd, rm); } - void vcvt_F64_S32(FPRegisterID fd, FPRegisterID sm) + void vcvt_F64_S32(FPDoubleRegisterID rd, FPSingleRegisterID rm) { - m_formatter.vfpOp(0x0bc0eeb8 | doubleRegisterMask(fd, 6, 28) | singleRegisterMask(sm, 16, 21)); + // boolean values are 64bit (toInt, unsigned, roundZero) + m_formatter.vfpOp(OP_VCVT_FPIVFP, OP_VCVT_FPIVFPb, true, vcvtOp(false, false, false), rd, rm); } - void vcvt_S32_F64(FPRegisterID sd, FPRegisterID fm) + void vcvtr_S32_F64(FPSingleRegisterID rd, FPDoubleRegisterID rm) { - m_formatter.vfpOp(0x0bc0eebd | singleRegisterMask(sd, 28, 6) | doubleRegisterMask(fm, 21, 16)); + // boolean values are 64bit (toInt, unsigned, roundZero) + m_formatter.vfpOp(OP_VCVT_FPIVFP, OP_VCVT_FPIVFPb, true, vcvtOp(true, false, true), rd, rm); } - void vldr(FPRegisterID rd, RegisterID rn, int32_t imm) + void vdiv_F64(FPDoubleRegisterID rd, FPDoubleRegisterID rn, FPDoubleRegisterID rm) { - vmem(rd, rn, imm, true); + m_formatter.vfpOp(OP_VDIV, OP_VDIVb, true, rn, rd, rm); } - void vmov(RegisterID rd, FPRegisterID sn) + void vldr(FPDoubleRegisterID rd, RegisterID rn, int32_t imm) { - m_formatter.vfpOp(0x0a10ee10 | (rd << 28) | singleRegisterMask(sn, 0, 23)); + m_formatter.vfpMemOp(OP_VLDR, OP_VLDRb, true, rn, rd, imm); } - void vmov(FPRegisterID sn, RegisterID rd) + void vmov_F64_0(FPDoubleRegisterID rd) { - m_formatter.vfpOp(0x0a10ee00 | (rd << 28) | singleRegisterMask(sn, 0, 23)); + m_formatter.vfpOp(OP_VMOV_IMM_T2, OP_VMOV_IMM_T2b, true, VFPOperand(0), rd, VFPOperand(0)); } - // move FPSCR flags to APSR. - void vmrs_APSR_nzcv_FPSCR() + void vmov(RegisterID rd, FPSingleRegisterID rn) { - m_formatter.vfpOp(0xfa10eef1); + ASSERT(!BadReg(rd)); + m_formatter.vfpOp(OP_VMOV_CtoS, OP_VMOV_CtoSb, false, rn, rd, VFPOperand(0)); } - void vmul_F64(FPRegisterID rd, FPRegisterID rn, FPRegisterID rm) + void vmov(FPSingleRegisterID rd, RegisterID rn) { - m_formatter.vfpOp(0x0b00ee20 | doubleRegisterMask(rd, 6, 28) | doubleRegisterMask(rn, 23, 0) | doubleRegisterMask(rm, 21, 16)); + ASSERT(!BadReg(rn)); + m_formatter.vfpOp(OP_VMOV_StoC, OP_VMOV_StoCb, false, rd, rn, VFPOperand(0)); } - void vstr(FPRegisterID rd, RegisterID rn, int32_t imm) + void vmrs(RegisterID reg = ARMRegisters::pc) { - vmem(rd, rn, imm, false); + ASSERT(reg != ARMRegisters::sp); + m_formatter.vfpOp(OP_VMRS, OP_VMRSb, false, VFPOperand(1), VFPOperand(0x10 | reg), VFPOperand(0)); } - void vsub_F64(FPRegisterID rd, FPRegisterID rn, FPRegisterID rm) + void vmul_F64(FPDoubleRegisterID rd, FPDoubleRegisterID rn, FPDoubleRegisterID rm) { - m_formatter.vfpOp(0x0b40ee30 | doubleRegisterMask(rd, 6, 28) | doubleRegisterMask(rn, 23, 0) | doubleRegisterMask(rm, 21, 16)); + m_formatter.vfpOp(OP_VMUL_T2, OP_VMUL_T2b, true, rn, rd, rm); } + void vstr(FPDoubleRegisterID rd, RegisterID rn, int32_t imm) + { + m_formatter.vfpMemOp(OP_VSTR, OP_VSTRb, true, rn, rd, imm); + } + + void vsub_F64(FPDoubleRegisterID rd, FPDoubleRegisterID rn, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VSUB_T2, OP_VSUB_T2b, true, rn, rd, rm); + } JmpDst label() { @@ -1494,6 +1666,15 @@ public: { return dst.m_offset - src.m_offset; } + + int executableOffsetFor(int location) + { + if (!location) + return 0; + return static_cast<int32_t*>(m_formatter.data())[location / sizeof(int32_t) - 1]; + } + + int jumpSizeDelta(JumpType jumpType, JumpLinkType jumpLinkType) { return JumpPaddingSizes[jumpType] - JumpSizes[jumpLinkType]; } // Assembler admin methods: @@ -1502,22 +1683,125 @@ public: return m_formatter.size(); } - void* executableCopy(ExecutablePool* allocator) + static bool linkRecordSourceComparator(const LinkRecord& a, const LinkRecord& b) { - void* copy = m_formatter.executableCopy(allocator); + return a.from() < b.from(); + } - unsigned jumpCount = m_jumpsToLink.size(); - for (unsigned i = 0; i < jumpCount; ++i) { - uint16_t* location = reinterpret_cast<uint16_t*>(reinterpret_cast<intptr_t>(copy) + m_jumpsToLink[i].from); - uint16_t* target = reinterpret_cast<uint16_t*>(reinterpret_cast<intptr_t>(copy) + m_jumpsToLink[i].to); - linkJumpAbsolute(location, target); + bool canCompact(JumpType jumpType) + { + // The following cannot be compacted: + // JumpFixed: represents custom jump sequence + // JumpNoConditionFixedSize: represents unconditional jump that must remain a fixed size + // JumpConditionFixedSize: represents conditional jump that must remain a fixed size + return (jumpType == JumpNoCondition) || (jumpType == JumpCondition); + } + + JumpLinkType computeJumpType(JumpType jumpType, const uint8_t* from, const uint8_t* to) + { + if (jumpType == JumpFixed) + return LinkInvalid; + + // for patchable jump we must leave space for the longest code sequence + if (jumpType == JumpNoConditionFixedSize) + return LinkBX; + if (jumpType == JumpConditionFixedSize) + return LinkConditionalBX; + + const int paddingSize = JumpPaddingSizes[jumpType]; + bool mayTriggerErrata = false; + + if (jumpType == JumpCondition) { + // 2-byte conditional T1 + const uint16_t* jumpT1Location = reinterpret_cast<const uint16_t*>(from - (paddingSize - JumpSizes[LinkJumpT1])); + if (canBeJumpT1(jumpT1Location, to)) + return LinkJumpT1; + // 4-byte conditional T3 + const uint16_t* jumpT3Location = reinterpret_cast<const uint16_t*>(from - (paddingSize - JumpSizes[LinkJumpT3])); + if (canBeJumpT3(jumpT3Location, to, mayTriggerErrata)) { + if (!mayTriggerErrata) + return LinkJumpT3; + } + // 4-byte conditional T4 with IT + const uint16_t* conditionalJumpT4Location = + reinterpret_cast<const uint16_t*>(from - (paddingSize - JumpSizes[LinkConditionalJumpT4])); + if (canBeJumpT4(conditionalJumpT4Location, to, mayTriggerErrata)) { + if (!mayTriggerErrata) + return LinkConditionalJumpT4; + } + } else { + // 2-byte unconditional T2 + const uint16_t* jumpT2Location = reinterpret_cast<const uint16_t*>(from - (paddingSize - JumpSizes[LinkJumpT2])); + if (canBeJumpT2(jumpT2Location, to)) + return LinkJumpT2; + // 4-byte unconditional T4 + const uint16_t* jumpT4Location = reinterpret_cast<const uint16_t*>(from - (paddingSize - JumpSizes[LinkJumpT4])); + if (canBeJumpT4(jumpT4Location, to, mayTriggerErrata)) { + if (!mayTriggerErrata) + return LinkJumpT4; + } + // use long jump sequence + return LinkBX; + } + + ASSERT(jumpType == JumpCondition); + return LinkConditionalBX; + } + + JumpLinkType computeJumpType(LinkRecord& record, const uint8_t* from, const uint8_t* to) + { + JumpLinkType linkType = computeJumpType(record.type(), from, to); + record.setLinkType(linkType); + return linkType; + } + + void recordLinkOffsets(int32_t regionStart, int32_t regionEnd, int32_t offset) + { + int32_t ptr = regionStart / sizeof(int32_t); + const int32_t end = regionEnd / sizeof(int32_t); + int32_t* offsets = static_cast<int32_t*>(m_formatter.data()); + while (ptr < end) + offsets[ptr++] = offset; + } + + Vector<LinkRecord>& jumpsToLink() + { + std::sort(m_jumpsToLink.begin(), m_jumpsToLink.end(), linkRecordSourceComparator); + return m_jumpsToLink; + } + + void link(LinkRecord& record, uint8_t* from, uint8_t* to) + { + switch (record.linkType()) { + case LinkJumpT1: + linkJumpT1(record.condition(), reinterpret_cast<uint16_t*>(from), to); + break; + case LinkJumpT2: + linkJumpT2(reinterpret_cast<uint16_t*>(from), to); + break; + case LinkJumpT3: + linkJumpT3(record.condition(), reinterpret_cast<uint16_t*>(from), to); + break; + case LinkJumpT4: + linkJumpT4(reinterpret_cast<uint16_t*>(from), to); + break; + case LinkConditionalJumpT4: + linkConditionalJumpT4(record.condition(), reinterpret_cast<uint16_t*>(from), to); + break; + case LinkConditionalBX: + linkConditionalBX(record.condition(), reinterpret_cast<uint16_t*>(from), to); + break; + case LinkBX: + linkBX(reinterpret_cast<uint16_t*>(from), to); + break; + default: + ASSERT_NOT_REACHED(); + break; } - m_jumpsToLink.clear(); - - ASSERT(copy); - return copy; } + void* unlinkedCode() { return m_formatter.data(); } + static unsigned getCallReturnOffset(JmpSrc call) { ASSERT(call.m_offset >= 0); @@ -1536,7 +1820,7 @@ public: { ASSERT(to.m_offset != -1); ASSERT(from.m_offset != -1); - m_jumpsToLink.append(LinkRecord(from.m_offset, to.m_offset)); + m_jumpsToLink.append(LinkRecord(from.m_offset, to.m_offset, from.m_type, from.m_condition)); } static void linkJump(void* code, JmpSrc from, void* to) @@ -1579,8 +1863,6 @@ public: ASSERT(reinterpret_cast<intptr_t>(to) & 1); setPointer(reinterpret_cast<uint16_t*>(from) - 1, to); - - ExecutableAllocator::cacheFlush(reinterpret_cast<uint16_t*>(from) - 5, 4 * sizeof(uint16_t)); } static void repatchInt32(void* where, int32_t value) @@ -1588,8 +1870,6 @@ public: ASSERT(!(reinterpret_cast<intptr_t>(where) & 1)); setInt32(where, value); - - ExecutableAllocator::cacheFlush(reinterpret_cast<uint16_t*>(where) - 4, 4 * sizeof(uint16_t)); } static void repatchPointer(void* where, void* value) @@ -1597,43 +1877,88 @@ public: ASSERT(!(reinterpret_cast<intptr_t>(where) & 1)); setPointer(where, value); - - ExecutableAllocator::cacheFlush(reinterpret_cast<uint16_t*>(where) - 4, 4 * sizeof(uint16_t)); } static void repatchLoadPtrToLEA(void* where) { ASSERT(!(reinterpret_cast<intptr_t>(where) & 1)); - uint16_t* loadOp = reinterpret_cast<uint16_t*>(where) + 4; - ASSERT((*loadOp & 0xfff0) == OP_LDR_reg_T2); - *loadOp = OP_ADD_reg_T3 | (*loadOp & 0xf); - ExecutableAllocator::cacheFlush(loadOp, sizeof(uint16_t)); + ASSERT((loadOp[0] & 0xfff0) == OP_LDR_reg_T2); + ASSERT((loadOp[1] & 0x0ff0) == 0); + int rn = loadOp[0] & 0xf; + int rt = loadOp[1] >> 12; + int rm = loadOp[1] & 0xf; + + loadOp[0] = OP_ADD_reg_T3 | rn; + loadOp[1] = rt << 8 | rm; + ExecutableAllocator::cacheFlush(loadOp, sizeof(uint32_t)); } private: + // VFP operations commonly take one or more 5-bit operands, typically representing a + // floating point register number. This will commonly be encoded in the instruction + // in two parts, with one single bit field, and one 4-bit field. In the case of + // double precision operands the high bit of the register number will be encoded + // separately, and for single precision operands the high bit of the register number + // will be encoded individually. + // VFPOperand encapsulates a 5-bit VFP operand, with bits 0..3 containing the 4-bit + // field to be encoded together in the instruction (the low 4-bits of a double + // register number, or the high 4-bits of a single register number), and bit 4 + // contains the bit value to be encoded individually. + struct VFPOperand { + explicit VFPOperand(uint32_t value) + : m_value(value) + { + ASSERT(!(m_value & ~0x1f)); + } - // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. - // (i.e. +/-(0..255) 32-bit words) - void vmem(FPRegisterID rd, RegisterID rn, int32_t imm, bool isLoad) - { - bool up; - uint32_t offset; - if (imm < 0) { - offset = -imm; - up = false; - } else { - offset = imm; - up = true; + VFPOperand(FPDoubleRegisterID reg) + : m_value(reg) + { + } + + VFPOperand(RegisterID reg) + : m_value(reg) + { + } + + VFPOperand(FPSingleRegisterID reg) + : m_value(((reg & 1) << 4) | (reg >> 1)) // rotate the lowest bit of 'reg' to the top. + { } - // offset is effectively leftshifted by 2 already (the bottom two bits are zero, and not - // reperesented in the instruction. Left shift by 14, to mov it into position 0x00AA0000. - ASSERT((offset & ~(0xff << 2)) == 0); - offset <<= 14; + uint32_t bits1() + { + return m_value >> 4; + } - m_formatter.vfpOp(0x0b00ed00 | offset | (up << 7) | (isLoad << 4) | doubleRegisterMask(rd, 6, 28) | rn); + uint32_t bits4() + { + return m_value & 0xf; + } + + uint32_t m_value; + }; + + VFPOperand vcvtOp(bool toInteger, bool isUnsigned, bool isRoundZero) + { + // Cannot specify rounding when converting to float. + ASSERT(toInteger || !isRoundZero); + + uint32_t op = 0x8; + if (toInteger) { + // opc2 indicates both toInteger & isUnsigned. + op |= isUnsigned ? 0x4 : 0x5; + // 'op' field in instruction is isRoundZero + if (isRoundZero) + op |= 0x10; + } else { + // 'op' field in instruction is isUnsigned + if (!isUnsigned) + op |= 0x10; + } + return VFPOperand(op); } static void setInt32(void* code, uint32_t value) @@ -1692,25 +2017,194 @@ private: return (instruction[0] == OP_NOP_T2a) && (instruction[1] == OP_NOP_T2b); } - static void linkJumpAbsolute(uint16_t* instruction, void* target) + static bool canBeJumpT1(const uint16_t* instruction, const void* target) + { + ASSERT(!(reinterpret_cast<intptr_t>(instruction) & 1)); + ASSERT(!(reinterpret_cast<intptr_t>(target) & 1)); + + intptr_t relative = reinterpret_cast<intptr_t>(target) - (reinterpret_cast<intptr_t>(instruction)); + // It does not appear to be documented in the ARM ARM (big surprise), but + // for OP_B_T1 the branch displacement encoded in the instruction is 2 + // less than the actual displacement. + relative -= 2; + return ((relative << 23) >> 23) == relative; + } + + static bool canBeJumpT2(const uint16_t* instruction, const void* target) + { + ASSERT(!(reinterpret_cast<intptr_t>(instruction) & 1)); + ASSERT(!(reinterpret_cast<intptr_t>(target) & 1)); + + intptr_t relative = reinterpret_cast<intptr_t>(target) - (reinterpret_cast<intptr_t>(instruction)); + // It does not appear to be documented in the ARM ARM (big surprise), but + // for OP_B_T2 the branch displacement encoded in the instruction is 2 + // less than the actual displacement. + relative -= 2; + return ((relative << 20) >> 20) == relative; + } + + static bool canBeJumpT3(const uint16_t* instruction, const void* target, bool& mayTriggerErrata) + { + ASSERT(!(reinterpret_cast<intptr_t>(instruction) & 1)); + ASSERT(!(reinterpret_cast<intptr_t>(target) & 1)); + + intptr_t relative = reinterpret_cast<intptr_t>(target) - (reinterpret_cast<intptr_t>(instruction)); + // From Cortex-A8 errata: + // If the 32-bit Thumb-2 branch instruction spans two 4KiB regions and + // the target of the branch falls within the first region it is + // possible for the processor to incorrectly determine the branch + // instruction, and it is also possible in some cases for the processor + // to enter a deadlock state. + // The instruction is spanning two pages if it ends at an address ending 0x002 + bool spansTwo4K = ((reinterpret_cast<intptr_t>(instruction) & 0xfff) == 0x002); + mayTriggerErrata = spansTwo4K; + // The target is in the first page if the jump branch back by [3..0x1002] bytes + bool targetInFirstPage = (relative >= -0x1002) && (relative < -2); + bool wouldTriggerA8Errata = spansTwo4K && targetInFirstPage; + return ((relative << 11) >> 11) == relative && !wouldTriggerA8Errata; + } + + static bool canBeJumpT4(const uint16_t* instruction, const void* target, bool& mayTriggerErrata) + { + ASSERT(!(reinterpret_cast<intptr_t>(instruction) & 1)); + ASSERT(!(reinterpret_cast<intptr_t>(target) & 1)); + + intptr_t relative = reinterpret_cast<intptr_t>(target) - (reinterpret_cast<intptr_t>(instruction)); + // From Cortex-A8 errata: + // If the 32-bit Thumb-2 branch instruction spans two 4KiB regions and + // the target of the branch falls within the first region it is + // possible for the processor to incorrectly determine the branch + // instruction, and it is also possible in some cases for the processor + // to enter a deadlock state. + // The instruction is spanning two pages if it ends at an address ending 0x002 + bool spansTwo4K = ((reinterpret_cast<intptr_t>(instruction) & 0xfff) == 0x002); + mayTriggerErrata = spansTwo4K; + // The target is in the first page if the jump branch back by [3..0x1002] bytes + bool targetInFirstPage = (relative >= -0x1002) && (relative < -2); + bool wouldTriggerA8Errata = spansTwo4K && targetInFirstPage; + return ((relative << 7) >> 7) == relative && !wouldTriggerA8Errata; + } + + void linkJumpT1(Condition cond, uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast<intptr_t>(instruction) & 1)); + ASSERT(!(reinterpret_cast<intptr_t>(target) & 1)); + ASSERT(canBeJumpT1(instruction, target)); + + intptr_t relative = reinterpret_cast<intptr_t>(target) - (reinterpret_cast<intptr_t>(instruction)); + // It does not appear to be documented in the ARM ARM (big surprise), but + // for OP_B_T1 the branch displacement encoded in the instruction is 2 + // less than the actual displacement. + relative -= 2; + + // All branch offsets should be an even distance. + ASSERT(!(relative & 1)); + instruction[-1] = OP_B_T1 | ((cond & 0xf) << 8) | ((relative & 0x1fe) >> 1); + } + + static void linkJumpT2(uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast<intptr_t>(instruction) & 1)); + ASSERT(!(reinterpret_cast<intptr_t>(target) & 1)); + ASSERT(canBeJumpT2(instruction, target)); + + intptr_t relative = reinterpret_cast<intptr_t>(target) - (reinterpret_cast<intptr_t>(instruction)); + // It does not appear to be documented in the ARM ARM (big surprise), but + // for OP_B_T2 the branch displacement encoded in the instruction is 2 + // less than the actual displacement. + relative -= 2; + + // All branch offsets should be an even distance. + ASSERT(!(relative & 1)); + instruction[-1] = OP_B_T2 | ((relative & 0xffe) >> 1); + } + + void linkJumpT3(Condition cond, uint16_t* instruction, void* target) { // FIMXE: this should be up in the MacroAssembler layer. :-( - const uint16_t JUMP_TEMPORARY_REGISTER = ARMRegisters::ip; - ASSERT(!(reinterpret_cast<intptr_t>(instruction) & 1)); ASSERT(!(reinterpret_cast<intptr_t>(target) & 1)); - - ASSERT( (isMOV_imm_T3(instruction - 5) && isMOVT(instruction - 3) && isBX(instruction - 1)) - || (isNOP_T1(instruction - 5) && isNOP_T2(instruction - 4) && isB(instruction - 2)) ); - + bool scratch; + UNUSED_PARAM(scratch); + ASSERT(canBeJumpT3(instruction, target, scratch)); + intptr_t relative = reinterpret_cast<intptr_t>(target) - (reinterpret_cast<intptr_t>(instruction)); - if (((relative << 7) >> 7) == relative) { - // ARM encoding for the top two bits below the sign bit is 'peculiar'. - if (relative >= 0) - relative ^= 0xC00000; - - // All branch offsets should be an even distance. - ASSERT(!(relative & 1)); + + // All branch offsets should be an even distance. + ASSERT(!(relative & 1)); + instruction[-2] = OP_B_T3a | ((relative & 0x100000) >> 10) | ((cond & 0xf) << 6) | ((relative & 0x3f000) >> 12); + instruction[-1] = OP_B_T3b | ((relative & 0x80000) >> 8) | ((relative & 0x40000) >> 5) | ((relative & 0xffe) >> 1); + } + + static void linkJumpT4(uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast<intptr_t>(instruction) & 1)); + ASSERT(!(reinterpret_cast<intptr_t>(target) & 1)); + bool scratch; + UNUSED_PARAM(scratch); + ASSERT(canBeJumpT4(instruction, target, scratch)); + + intptr_t relative = reinterpret_cast<intptr_t>(target) - (reinterpret_cast<intptr_t>(instruction)); + // ARM encoding for the top two bits below the sign bit is 'peculiar'. + if (relative >= 0) + relative ^= 0xC00000; + + // All branch offsets should be an even distance. + ASSERT(!(relative & 1)); + instruction[-2] = OP_B_T4a | ((relative & 0x1000000) >> 14) | ((relative & 0x3ff000) >> 12); + instruction[-1] = OP_B_T4b | ((relative & 0x800000) >> 10) | ((relative & 0x400000) >> 11) | ((relative & 0xffe) >> 1); + } + + void linkConditionalJumpT4(Condition cond, uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast<intptr_t>(instruction) & 1)); + ASSERT(!(reinterpret_cast<intptr_t>(target) & 1)); + + instruction[-3] = ifThenElse(cond) | OP_IT; + linkJumpT4(instruction, target); + } + + static void linkBX(uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast<intptr_t>(instruction) & 1)); + ASSERT(!(reinterpret_cast<intptr_t>(target) & 1)); + + const uint16_t JUMP_TEMPORARY_REGISTER = ARMRegisters::ip; + ARMThumbImmediate lo16 = ARMThumbImmediate::makeUInt16(static_cast<uint16_t>(reinterpret_cast<uint32_t>(target) + 1)); + ARMThumbImmediate hi16 = ARMThumbImmediate::makeUInt16(static_cast<uint16_t>(reinterpret_cast<uint32_t>(target) >> 16)); + instruction[-5] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOV_imm_T3, lo16); + instruction[-4] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, lo16); + instruction[-3] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOVT, hi16); + instruction[-2] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, hi16); + instruction[-1] = OP_BX | (JUMP_TEMPORARY_REGISTER << 3); + } + + void linkConditionalBX(Condition cond, uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast<intptr_t>(instruction) & 1)); + ASSERT(!(reinterpret_cast<intptr_t>(target) & 1)); + + linkBX(instruction, target); + instruction[-6] = ifThenElse(cond, true, true) | OP_IT; + } + + static void linkJumpAbsolute(uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast<intptr_t>(instruction) & 1)); + ASSERT(!(reinterpret_cast<intptr_t>(target) & 1)); + + ASSERT((isMOV_imm_T3(instruction - 5) && isMOVT(instruction - 3) && isBX(instruction - 1)) + || (isNOP_T1(instruction - 5) && isNOP_T2(instruction - 4) && isB(instruction - 2))); + + bool scratch; + if (canBeJumpT4(instruction, target, scratch)) { // There may be a better way to fix this, but right now put the NOPs first, since in the // case of an conditional branch this will be coming after an ITTT predicating *three* // instructions! Looking backwards to modify the ITTT to an IT is not easy, due to @@ -1719,9 +2213,9 @@ private: instruction[-5] = OP_NOP_T1; instruction[-4] = OP_NOP_T2a; instruction[-3] = OP_NOP_T2b; - instruction[-2] = OP_B_T4a | ((relative & 0x1000000) >> 14) | ((relative & 0x3ff000) >> 12); - instruction[-1] = OP_B_T4b | ((relative & 0x800000) >> 10) | ((relative & 0x400000) >> 11) | ((relative & 0xffe) >> 1); + linkJumpT4(instruction, target); } else { + const uint16_t JUMP_TEMPORARY_REGISTER = ARMRegisters::ip; ARMThumbImmediate lo16 = ARMThumbImmediate::makeUInt16(static_cast<uint16_t>(reinterpret_cast<uint32_t>(target) + 1)); ARMThumbImmediate hi16 = ARMThumbImmediate::makeUInt16(static_cast<uint16_t>(reinterpret_cast<uint32_t>(target) >> 16)); instruction[-5] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOV_imm_T3, lo16); @@ -1731,11 +2225,12 @@ private: instruction[-1] = OP_BX | (JUMP_TEMPORARY_REGISTER << 3); } } - + static uint16_t twoWordOp5i6Imm4Reg4EncodedImmFirst(uint16_t op, ARMThumbImmediate imm) { return op | (imm.m_value.i << 10) | imm.m_value.imm4; } + static uint16_t twoWordOp5i6Imm4Reg4EncodedImmSecond(uint16_t rd, ARMThumbImmediate imm) { return (imm.m_value.imm3 << 12) | (rd << 8) | imm.m_value.imm8; @@ -1810,11 +2305,35 @@ private: m_buffer.putShort((reg2 << 12) | imm); } - void vfpOp(int32_t op) + // Formats up instructions of the pattern: + // 111111111B11aaaa:bbbb222SA2C2cccc + // Where 1s in the pattern come from op1, 2s in the pattern come from op2, S is the provided size bit. + // Operands provide 5 bit values of the form Aaaaa, Bbbbb, Ccccc. + void vfpOp(OpcodeID1 op1, OpcodeID2 op2, bool size, VFPOperand a, VFPOperand b, VFPOperand c) { - m_buffer.putInt(op); + ASSERT(!(op1 & 0x004f)); + ASSERT(!(op2 & 0xf1af)); + m_buffer.putShort(op1 | b.bits1() << 6 | a.bits4()); + m_buffer.putShort(op2 | b.bits4() << 12 | size << 8 | a.bits1() << 7 | c.bits1() << 5 | c.bits4()); } + // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. + // (i.e. +/-(0..255) 32-bit words) + void vfpMemOp(OpcodeID1 op1, OpcodeID2 op2, bool size, RegisterID rn, VFPOperand rd, int32_t imm) + { + bool up = true; + if (imm < 0) { + imm = -imm; + up = false; + } + + uint32_t offset = imm; + ASSERT(!(offset & ~0x3fc)); + offset >>= 2; + + m_buffer.putShort(op1 | (up << 7) | rd.bits1() << 6 | rn); + m_buffer.putShort(op2 | rd.bits4() << 12 | size << 8 | offset); + } // Administrative methods: @@ -1828,6 +2347,7 @@ private: } m_formatter; Vector<LinkRecord> m_jumpsToLink; + Vector<int32_t> m_offsets; }; } // namespace JSC diff --git a/JavaScriptCore/assembler/AbstractMacroAssembler.h b/JavaScriptCore/assembler/AbstractMacroAssembler.h index 198e8d1..1fa5ec7 100644 --- a/JavaScriptCore/assembler/AbstractMacroAssembler.h +++ b/JavaScriptCore/assembler/AbstractMacroAssembler.h @@ -26,10 +26,8 @@ #ifndef AbstractMacroAssembler_h #define AbstractMacroAssembler_h -#include <wtf/Platform.h> - -#include <MacroAssemblerCodeRef.h> -#include <CodeLocation.h> +#include "CodeLocation.h" +#include "MacroAssemblerCodeRef.h" #include <wtf/Noncopyable.h> #include <wtf/UnusedParam.h> @@ -51,7 +49,6 @@ public: class Jump; typedef typename AssemblerType::RegisterID RegisterID; - typedef typename AssemblerType::FPRegisterID FPRegisterID; typedef typename AssemblerType::JmpSrc JmpSrc; typedef typename AssemblerType::JmpDst JmpDst; @@ -83,6 +80,17 @@ public: int32_t offset; }; + struct ExtendedAddress { + explicit ExtendedAddress(RegisterID base, intptr_t offset = 0) + : base(base) + , offset(offset) + { + } + + RegisterID base; + intptr_t offset; + }; + // ImplicitAddress: // // This class is used for explicit 'load' and 'store' operations @@ -151,7 +159,7 @@ public: // in a class requiring explicit construction in order to differentiate // from pointers used as absolute addresses to memory operations struct ImmPtr { - explicit ImmPtr(void* value) + explicit ImmPtr(const void* value) : m_value(value) { } @@ -161,7 +169,7 @@ public: return reinterpret_cast<intptr_t>(m_value); } - void* m_value; + const void* m_value; }; // Imm32: @@ -173,7 +181,7 @@ public: struct Imm32 { explicit Imm32(int32_t value) : m_value(value) -#if CPU(ARM) +#if CPU(ARM) || CPU(MIPS) , m_isPointer(false) #endif { @@ -182,7 +190,7 @@ public: #if !CPU(X86_64) explicit Imm32(ImmPtr ptr) : m_value(ptr.asIntptr()) -#if CPU(ARM) +#if CPU(ARM) || CPU(MIPS) , m_isPointer(true) #endif { @@ -190,13 +198,14 @@ public: #endif int32_t m_value; -#if CPU(ARM) +#if CPU(ARM) || CPU(MIPS) // We rely on being able to regenerate code to recover exception handling // information. Since ARMv7 supports 16-bit immediates there is a danger // that if pointer values change the layout of the generated code will change. // To avoid this problem, always generate pointers (and thus Imm32s constructed // from ImmPtrs) with a code sequence that is able to represent any pointer // value - don't use a more compact form in these cases. + // Same for MIPS. bool m_isPointer; #endif }; @@ -409,12 +418,6 @@ public: // Section 3: Misc admin methods - - static CodePtr trampolineAt(CodeRef ref, Label label) - { - return CodePtr(AssemblerType::getRelocatedAddress(ref.m_code.dataLocation(), label.m_label)); - } - size_t size() { return m_assembler.size(); @@ -470,6 +473,9 @@ public: { return AssemblerType::getDifferenceBetweenLabels(from.m_label, to.m_jmp); } + + void beginUninterruptedSequence() { } + void endUninterruptedSequence() { } protected: AssemblerType m_assembler; diff --git a/JavaScriptCore/assembler/AssemblerBuffer.h b/JavaScriptCore/assembler/AssemblerBuffer.h index 073906a..0454a99 100644 --- a/JavaScriptCore/assembler/AssemblerBuffer.h +++ b/JavaScriptCore/assembler/AssemblerBuffer.h @@ -26,8 +26,6 @@ #ifndef AssemblerBuffer_h #define AssemblerBuffer_h -#include <wtf/Platform.h> - #if ENABLE(ASSEMBLER) #include "stdint.h" @@ -35,17 +33,19 @@ #include <jit/ExecutableAllocator.h> #include <wtf/Assertions.h> #include <wtf/FastMalloc.h> +#include <wtf/StdLibExtras.h> namespace JSC { class AssemblerBuffer { - static const int inlineCapacity = 256; + static const int inlineCapacity = 128 - sizeof(char*) - 2 * sizeof(int); public: AssemblerBuffer() : m_buffer(m_inlineBuffer) , m_capacity(inlineCapacity) , m_size(0) { + COMPILE_ASSERT(sizeof(AssemblerBuffer) == 128, AssemblerBuffer_should_be_128_bytes); } ~AssemblerBuffer() @@ -82,7 +82,7 @@ namespace JSC { void putShortUnchecked(int value) { ASSERT(!(m_size > m_capacity - 4)); - *reinterpret_cast<short*>(&m_buffer[m_size]) = value; + *reinterpret_cast_ptr<short*>(&m_buffer[m_size]) = value; m_size += 2; } @@ -96,14 +96,14 @@ namespace JSC { void putIntUnchecked(int value) { ASSERT(!(m_size > m_capacity - 4)); - *reinterpret_cast<int*>(&m_buffer[m_size]) = value; + *reinterpret_cast_ptr<int*>(&m_buffer[m_size]) = value; m_size += 4; } void putInt64Unchecked(int64_t value) { ASSERT(!(m_size > m_capacity - 8)); - *reinterpret_cast<int64_t*>(&m_buffer[m_size]) = value; + *reinterpret_cast_ptr<int64_t*>(&m_buffer[m_size]) = value; m_size += 8; } diff --git a/JavaScriptCore/assembler/AssemblerBufferWithConstantPool.h b/JavaScriptCore/assembler/AssemblerBufferWithConstantPool.h index af3c3be..599be14 100644 --- a/JavaScriptCore/assembler/AssemblerBufferWithConstantPool.h +++ b/JavaScriptCore/assembler/AssemblerBufferWithConstantPool.h @@ -27,8 +27,6 @@ #ifndef AssemblerBufferWithConstantPool_h #define AssemblerBufferWithConstantPool_h -#include <wtf/Platform.h> - #if ENABLE(ASSEMBLER) #include "AssemblerBuffer.h" @@ -192,6 +190,8 @@ public: void putIntWithConstantInt(uint32_t insn, uint32_t constant, bool isReusable = false) { + if (!m_numConsts) + m_maxDistance = maxPoolSize; flushIfNoSpaceFor(4, 4); m_loadOffsets.append(AssemblerBuffer::size()); @@ -281,7 +281,6 @@ private: m_loadOffsets.clear(); m_numConsts = 0; - m_maxDistance = maxPoolSize; } void flushIfNoSpaceFor(int nextInsnSize) diff --git a/JavaScriptCore/assembler/CodeLocation.h b/JavaScriptCore/assembler/CodeLocation.h index b910b6f..e29029b 100644 --- a/JavaScriptCore/assembler/CodeLocation.h +++ b/JavaScriptCore/assembler/CodeLocation.h @@ -26,9 +26,7 @@ #ifndef CodeLocation_h #define CodeLocation_h -#include <wtf/Platform.h> - -#include <MacroAssemblerCodeRef.h> +#include "MacroAssemblerCodeRef.h" #if ENABLE(ASSEMBLER) diff --git a/JavaScriptCore/assembler/LinkBuffer.h b/JavaScriptCore/assembler/LinkBuffer.h index 6d08117..e1dca0b 100644 --- a/JavaScriptCore/assembler/LinkBuffer.h +++ b/JavaScriptCore/assembler/LinkBuffer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,10 +26,11 @@ #ifndef LinkBuffer_h #define LinkBuffer_h -#include <wtf/Platform.h> - #if ENABLE(ASSEMBLER) +#define DUMP_LINK_STATISTICS 0 +#define DUMP_CODE 0 + #include <MacroAssembler.h> #include <wtf/Noncopyable.h> @@ -51,25 +52,34 @@ namespace JSC { // class LinkBuffer : public Noncopyable { typedef MacroAssemblerCodeRef CodeRef; + typedef MacroAssemblerCodePtr CodePtr; typedef MacroAssembler::Label Label; typedef MacroAssembler::Jump Jump; typedef MacroAssembler::JumpList JumpList; typedef MacroAssembler::Call Call; typedef MacroAssembler::DataLabel32 DataLabel32; typedef MacroAssembler::DataLabelPtr DataLabelPtr; + typedef MacroAssembler::JmpDst JmpDst; +#if ENABLE(BRANCH_COMPACTION) + typedef MacroAssembler::LinkRecord LinkRecord; + typedef MacroAssembler::JumpLinkType JumpLinkType; +#endif public: // Note: Initialization sequence is significant, since executablePool is a PassRefPtr. // First, executablePool is copied into m_executablePool, then the initialization of // m_code uses m_executablePool, *not* executablePool, since this is no longer valid. - LinkBuffer(MacroAssembler* masm, PassRefPtr<ExecutablePool> executablePool) + // The linkOffset parameter should only be non-null when recompiling for exception info + LinkBuffer(MacroAssembler* masm, PassRefPtr<ExecutablePool> executablePool, void* linkOffset) : m_executablePool(executablePool) - , m_code(masm->m_assembler.executableCopy(m_executablePool.get())) - , m_size(masm->m_assembler.size()) + , m_size(0) + , m_code(0) + , m_assembler(masm) #ifndef NDEBUG , m_completed(false) #endif { + linkCode(linkOffset); } ~LinkBuffer() @@ -82,28 +92,32 @@ public: void link(Call call, FunctionPtr function) { ASSERT(call.isFlagSet(Call::Linkable)); + call.m_jmp = applyOffset(call.m_jmp); MacroAssembler::linkCall(code(), call, function); } void link(Jump jump, CodeLocationLabel label) { + jump.m_jmp = applyOffset(jump.m_jmp); MacroAssembler::linkJump(code(), jump, label); } void link(JumpList list, CodeLocationLabel label) { for (unsigned i = 0; i < list.m_jumps.size(); ++i) - MacroAssembler::linkJump(code(), list.m_jumps[i], label); + link(list.m_jumps[i], label); } void patch(DataLabelPtr label, void* value) { - MacroAssembler::linkPointer(code(), label.m_label, value); + JmpDst target = applyOffset(label.m_label); + MacroAssembler::linkPointer(code(), target, value); } void patch(DataLabelPtr label, CodeLocationLabel value) { - MacroAssembler::linkPointer(code(), label.m_label, value.executableAddress()); + JmpDst target = applyOffset(label.m_label); + MacroAssembler::linkPointer(code(), target, value.executableAddress()); } // These methods are used to obtain handles to allow the code to be relinked / repatched later. @@ -112,35 +126,36 @@ public: { ASSERT(call.isFlagSet(Call::Linkable)); ASSERT(!call.isFlagSet(Call::Near)); - return CodeLocationCall(MacroAssembler::getLinkerAddress(code(), call.m_jmp)); + return CodeLocationCall(MacroAssembler::getLinkerAddress(code(), applyOffset(call.m_jmp))); } CodeLocationNearCall locationOfNearCall(Call call) { ASSERT(call.isFlagSet(Call::Linkable)); ASSERT(call.isFlagSet(Call::Near)); - return CodeLocationNearCall(MacroAssembler::getLinkerAddress(code(), call.m_jmp)); + return CodeLocationNearCall(MacroAssembler::getLinkerAddress(code(), applyOffset(call.m_jmp))); } CodeLocationLabel locationOf(Label label) { - return CodeLocationLabel(MacroAssembler::getLinkerAddress(code(), label.m_label)); + return CodeLocationLabel(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); } CodeLocationDataLabelPtr locationOf(DataLabelPtr label) { - return CodeLocationDataLabelPtr(MacroAssembler::getLinkerAddress(code(), label.m_label)); + return CodeLocationDataLabelPtr(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); } CodeLocationDataLabel32 locationOf(DataLabel32 label) { - return CodeLocationDataLabel32(MacroAssembler::getLinkerAddress(code(), label.m_label)); + return CodeLocationDataLabel32(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); } // This method obtains the return address of the call, given as an offset from // the start of the code. unsigned returnAddressOffset(Call call) { + call.m_jmp = applyOffset(call.m_jmp); return MacroAssembler::getLinkerCallReturnOffset(call); } @@ -154,6 +169,7 @@ public: return CodeRef(m_code, m_executablePool, m_size); } + CodeLocationLabel finalizeCodeAddendum() { performFinalization(); @@ -161,7 +177,20 @@ public: return CodeLocationLabel(code()); } + CodePtr trampolineAt(Label label) + { + return CodePtr(MacroAssembler::AssemblerType_T::getRelocatedAddress(code(), applyOffset(label.m_label))); + } + private: + template <typename T> T applyOffset(T src) + { +#if ENABLE(BRANCH_COMPACTION) + src.m_offset -= m_assembler->executableOffsetFor(src.m_offset); +#endif + return src; + } + // Keep this private! - the underlying code should only be obtained externally via // finalizeCode() or finalizeCodeAddendum(). void* code() @@ -169,6 +198,84 @@ private: return m_code; } + void linkCode(void* linkOffset) + { + UNUSED_PARAM(linkOffset); + ASSERT(!m_code); +#if !ENABLE(BRANCH_COMPACTION) + m_code = m_assembler->m_assembler.executableCopy(m_executablePool.get()); + m_size = m_assembler->size(); +#else + size_t initialSize = m_assembler->size(); + m_code = (uint8_t*)m_executablePool->alloc(initialSize); + if (!m_code) + return; + ExecutableAllocator::makeWritable(m_code, m_assembler->size()); + uint8_t* inData = (uint8_t*)m_assembler->unlinkedCode(); + uint8_t* outData = reinterpret_cast<uint8_t*>(m_code); + const uint8_t* linkBase = linkOffset ? reinterpret_cast<uint8_t*>(linkOffset) : outData; + int readPtr = 0; + int writePtr = 0; + Vector<LinkRecord>& jumpsToLink = m_assembler->jumpsToLink(); + unsigned jumpCount = jumpsToLink.size(); + for (unsigned i = 0; i < jumpCount; ++i) { + int offset = readPtr - writePtr; + ASSERT(!(offset & 1)); + + // Copy the instructions from the last jump to the current one. + size_t regionSize = jumpsToLink[i].from() - readPtr; + memcpy(outData + writePtr, inData + readPtr, regionSize); + m_assembler->recordLinkOffsets(readPtr, jumpsToLink[i].from(), offset); + readPtr += regionSize; + writePtr += regionSize; + + // Calculate absolute address of the jump target, in the case of backwards + // branches we need to be precise, forward branches we are pessimistic + const uint8_t* target; + if (jumpsToLink[i].to() >= jumpsToLink[i].from()) + target = linkBase + jumpsToLink[i].to() - offset; // Compensate for what we have collapsed so far + else + target = linkBase + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to()); + + JumpLinkType jumpLinkType = m_assembler->computeJumpType(jumpsToLink[i], linkBase + writePtr, target); + // Compact branch if we can... + if (m_assembler->canCompact(jumpsToLink[i].type())) { + // Step back in the write stream + int32_t delta = m_assembler->jumpSizeDelta(jumpsToLink[i].type(), jumpLinkType); + if (delta) { + writePtr -= delta; + m_assembler->recordLinkOffsets(jumpsToLink[i].from() - delta, readPtr, readPtr - writePtr); + } + } + jumpsToLink[i].setFrom(writePtr); + } + // Copy everything after the last jump + memcpy(outData + writePtr, inData + readPtr, m_assembler->size() - readPtr); + m_assembler->recordLinkOffsets(readPtr, m_assembler->size(), readPtr - writePtr); + + // Actually link everything (don't link if we've be given a linkoffset as it's a + // waste of time: linkOffset is used for recompiling to get exception info) + if (!linkOffset) { + for (unsigned i = 0; i < jumpCount; ++i) { + uint8_t* location = outData + jumpsToLink[i].from(); + uint8_t* target = outData + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to()); + m_assembler->link(jumpsToLink[i], location, target); + } + } + + jumpsToLink.clear(); + m_size = writePtr + m_assembler->size() - readPtr; + m_executablePool->returnLastBytes(initialSize - m_size); + +#if DUMP_LINK_STATISTICS + dumpLinkStatistics(m_code, initialSize, m_size); +#endif +#if DUMP_CODE + dumpCode(m_code, m_size); +#endif +#endif + } + void performFinalization() { #ifndef NDEBUG @@ -180,9 +287,57 @@ private: ExecutableAllocator::cacheFlush(code(), m_size); } +#if DUMP_LINK_STATISTICS + static void dumpLinkStatistics(void* code, size_t initialSize, size_t finalSize) + { + static unsigned linkCount = 0; + static unsigned totalInitialSize = 0; + static unsigned totalFinalSize = 0; + linkCount++; + totalInitialSize += initialSize; + totalFinalSize += finalSize; + printf("link %p: orig %u, compact %u (delta %u, %.2f%%)\n", + code, static_cast<unsigned>(initialSize), static_cast<unsigned>(finalSize), + static_cast<unsigned>(initialSize - finalSize), + 100.0 * (initialSize - finalSize) / initialSize); + printf("\ttotal %u: orig %u, compact %u (delta %u, %.2f%%)\n", + linkCount, totalInitialSize, totalFinalSize, totalInitialSize - totalFinalSize, + 100.0 * (totalInitialSize - totalFinalSize) / totalInitialSize); + } +#endif + +#if DUMP_CODE + static void dumpCode(void* code, size_t size) + { +#if CPU(ARM_THUMB2) + // Dump the generated code in an asm file format that can be assembled and then disassembled + // for debugging purposes. For example, save this output as jit.s: + // gcc -arch armv7 -c jit.s + // otool -tv jit.o + static unsigned codeCount = 0; + unsigned short* tcode = static_cast<unsigned short*>(code); + size_t tsize = size / sizeof(short); + char nameBuf[128]; + snprintf(nameBuf, sizeof(nameBuf), "_jsc_jit%u", codeCount++); + printf("\t.syntax unified\n" + "\t.section\t__TEXT,__text,regular,pure_instructions\n" + "\t.globl\t%s\n" + "\t.align 2\n" + "\t.code 16\n" + "\t.thumb_func\t%s\n" + "# %p\n" + "%s:\n", nameBuf, nameBuf, code, nameBuf); + + for (unsigned i = 0; i < tsize; i++) + printf("\t.short\t0x%x\n", tcode[i]); +#endif + } +#endif + RefPtr<ExecutablePool> m_executablePool; - void* m_code; size_t m_size; + void* m_code; + MacroAssembler* m_assembler; #ifndef NDEBUG bool m_completed; #endif diff --git a/JavaScriptCore/assembler/MIPSAssembler.h b/JavaScriptCore/assembler/MIPSAssembler.h new file mode 100644 index 0000000..f9e40c4 --- /dev/null +++ b/JavaScriptCore/assembler/MIPSAssembler.h @@ -0,0 +1,983 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 University of Szeged + * All rights reserved. + * Copyright (C) 2010 MIPS Technologies, 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 MIPS TECHNOLOGIES, 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 MIPS TECHNOLOGIES, 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 MIPSAssembler_h +#define MIPSAssembler_h + +#if ENABLE(ASSEMBLER) && CPU(MIPS) + +#include "AssemblerBuffer.h" +#include <wtf/Assertions.h> +#include <wtf/SegmentedVector.h> + +namespace JSC { + +typedef uint32_t MIPSWord; + +namespace MIPSRegisters { +typedef enum { + r0 = 0, + r1, + r2, + r3, + r4, + r5, + r6, + r7, + r8, + r9, + r10, + r11, + r12, + r13, + r14, + r15, + r16, + r17, + r18, + r19, + r20, + r21, + r22, + r23, + r24, + r25, + r26, + r27, + r28, + r29, + r30, + r31, + zero = r0, + at = r1, + v0 = r2, + v1 = r3, + a0 = r4, + a1 = r5, + a2 = r6, + a3 = r7, + t0 = r8, + t1 = r9, + t2 = r10, + t3 = r11, + t4 = r12, + t5 = r13, + t6 = r14, + t7 = r15, + s0 = r16, + s1 = r17, + s2 = r18, + s3 = r19, + s4 = r20, + s5 = r21, + s6 = r22, + s7 = r23, + t8 = r24, + t9 = r25, + k0 = r26, + k1 = r27, + gp = r28, + sp = r29, + fp = r30, + ra = r31 +} RegisterID; + +typedef enum { + f0, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + f18, + f19, + f20, + f21, + f22, + f23, + f24, + f25, + f26, + f27, + f28, + f29, + f30, + f31 +} FPRegisterID; + +} // namespace MIPSRegisters + +class MIPSAssembler { +public: + typedef MIPSRegisters::RegisterID RegisterID; + typedef MIPSRegisters::FPRegisterID FPRegisterID; + typedef SegmentedVector<int, 64> Jumps; + + MIPSAssembler() + { + } + + // MIPS instruction opcode field position + enum { + OP_SH_RD = 11, + OP_SH_RT = 16, + OP_SH_RS = 21, + OP_SH_SHAMT = 6, + OP_SH_CODE = 16, + OP_SH_FD = 6, + OP_SH_FS = 11, + OP_SH_FT = 16 + }; + + class JmpSrc { + friend class MIPSAssembler; + public: + JmpSrc() + : m_offset(-1) + { + } + + private: + JmpSrc(int offset) + : m_offset(offset) + { + } + + int m_offset; + }; + + class JmpDst { + friend class MIPSAssembler; + public: + JmpDst() + : m_offset(-1) + , m_used(false) + { + } + + bool isUsed() const { return m_used; } + void used() { m_used = true; } + private: + JmpDst(int offset) + : m_offset(offset) + , m_used(false) + { + ASSERT(m_offset == offset); + } + + int m_offset : 31; + int m_used : 1; + }; + + void emitInst(MIPSWord op) + { + void* oldBase = m_buffer.data(); + + m_buffer.putInt(op); + + void* newBase = m_buffer.data(); + if (oldBase != newBase) + relocateJumps(oldBase, newBase); + } + + void nop() + { + emitInst(0x00000000); + } + + /* Need to insert one load data delay nop for mips1. */ + void loadDelayNop() + { +#if WTF_MIPS_ISA(1) + nop(); +#endif + } + + /* Need to insert one coprocessor access delay nop for mips1. */ + void copDelayNop() + { +#if WTF_MIPS_ISA(1) + nop(); +#endif + } + + void move(RegisterID rd, RegisterID rs) + { + /* addu */ + emitInst(0x00000021 | (rd << OP_SH_RD) | (rs << OP_SH_RS)); + } + + /* Set an immediate value to a register. This may generate 1 or 2 + instructions. */ + void li(RegisterID dest, int imm) + { + if (imm >= -32768 && imm <= 32767) + addiu(dest, MIPSRegisters::zero, imm); + else if (imm >= 0 && imm < 65536) + ori(dest, MIPSRegisters::zero, imm); + else { + lui(dest, imm >> 16); + if (imm & 0xffff) + ori(dest, dest, imm); + } + } + + void lui(RegisterID rt, int imm) + { + emitInst(0x3c000000 | (rt << OP_SH_RT) | (imm & 0xffff)); + } + + void addiu(RegisterID rt, RegisterID rs, int imm) + { + emitInst(0x24000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (imm & 0xffff)); + } + + void addu(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000021 | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); + } + + void subu(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000023 | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); + } + + void mult(RegisterID rs, RegisterID rt) + { + emitInst(0x00000018 | (rs << OP_SH_RS) | (rt << OP_SH_RT)); + } + + void div(RegisterID rs, RegisterID rt) + { + emitInst(0x0000001a | (rs << OP_SH_RS) | (rt << OP_SH_RT)); + } + + void mfhi(RegisterID rd) + { + emitInst(0x00000010 | (rd << OP_SH_RD)); + } + + void mflo(RegisterID rd) + { + emitInst(0x00000012 | (rd << OP_SH_RD)); + } + + void mul(RegisterID rd, RegisterID rs, RegisterID rt) + { +#if WTF_MIPS_ISA_AT_LEAST(32) + emitInst(0x70000002 | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); +#else + mult(rs, rt); + mflo(rd); +#endif + } + + void andInsn(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000024 | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); + } + + void andi(RegisterID rt, RegisterID rs, int imm) + { + emitInst(0x30000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (imm & 0xffff)); + } + + void nor(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000027 | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); + } + + void orInsn(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000025 | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); + } + + void ori(RegisterID rt, RegisterID rs, int imm) + { + emitInst(0x34000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (imm & 0xffff)); + } + + void xorInsn(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000026 | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); + } + + void xori(RegisterID rt, RegisterID rs, int imm) + { + emitInst(0x38000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (imm & 0xffff)); + } + + void slt(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x0000002a | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); + } + + void sltu(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x0000002b | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); + } + + void sltiu(RegisterID rt, RegisterID rs, int imm) + { + emitInst(0x2c000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (imm & 0xffff)); + } + + void sll(RegisterID rd, RegisterID rt, int shamt) + { + emitInst(0x00000000 | (rd << OP_SH_RD) | (rt << OP_SH_RT) + | ((shamt & 0x1f) << OP_SH_SHAMT)); + } + + void sllv(RegisterID rd, RegisterID rt, int rs) + { + emitInst(0x00000004 | (rd << OP_SH_RD) | (rt << OP_SH_RT) + | (rs << OP_SH_RS)); + } + + void sra(RegisterID rd, RegisterID rt, int shamt) + { + emitInst(0x00000003 | (rd << OP_SH_RD) | (rt << OP_SH_RT) + | ((shamt & 0x1f) << OP_SH_SHAMT)); + } + + void srav(RegisterID rd, RegisterID rt, RegisterID rs) + { + emitInst(0x00000007 | (rd << OP_SH_RD) | (rt << OP_SH_RT) + | (rs << OP_SH_RS)); + } + + void srl(RegisterID rd, RegisterID rt, int shamt) + { + emitInst(0x00000002 | (rd << OP_SH_RD) | (rt << OP_SH_RT) + | ((shamt & 0x1f) << OP_SH_SHAMT)); + } + + void srlv(RegisterID rd, RegisterID rt, RegisterID rs) + { + emitInst(0x00000006 | (rd << OP_SH_RD) | (rt << OP_SH_RT) + | (rs << OP_SH_RS)); + } + + void lbu(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x90000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + loadDelayNop(); + } + + void lw(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x8c000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + loadDelayNop(); + } + + void lwl(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x88000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + loadDelayNop(); + } + + void lwr(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x98000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + loadDelayNop(); + } + + void lhu(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x94000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + loadDelayNop(); + } + + void sw(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0xac000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + } + + void jr(RegisterID rs) + { + emitInst(0x00000008 | (rs << OP_SH_RS)); + } + + void jalr(RegisterID rs) + { + emitInst(0x0000f809 | (rs << OP_SH_RS)); + } + + void jal() + { + emitInst(0x0c000000); + } + + void bkpt() + { + int value = 512; /* BRK_BUG */ + emitInst(0x0000000d | ((value & 0x3ff) << OP_SH_CODE)); + } + + void bgez(RegisterID rs, int imm) + { + emitInst(0x04010000 | (rs << OP_SH_RS) | (imm & 0xffff)); + } + + void bltz(RegisterID rs, int imm) + { + emitInst(0x04000000 | (rs << OP_SH_RS) | (imm & 0xffff)); + } + + void beq(RegisterID rs, RegisterID rt, int imm) + { + emitInst(0x10000000 | (rs << OP_SH_RS) | (rt << OP_SH_RT) | (imm & 0xffff)); + } + + void bne(RegisterID rs, RegisterID rt, int imm) + { + emitInst(0x14000000 | (rs << OP_SH_RS) | (rt << OP_SH_RT) | (imm & 0xffff)); + } + + void bc1t() + { + emitInst(0x45010000); + } + + void bc1f() + { + emitInst(0x45000000); + } + + JmpSrc newJmpSrc() + { + return JmpSrc(m_buffer.size()); + } + + void appendJump() + { + m_jumps.append(m_buffer.size()); + } + + void addd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200000 | (fd << OP_SH_FD) | (fs << OP_SH_FS) + | (ft << OP_SH_FT)); + } + + void subd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200001 | (fd << OP_SH_FD) | (fs << OP_SH_FS) + | (ft << OP_SH_FT)); + } + + void muld(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200002 | (fd << OP_SH_FD) | (fs << OP_SH_FS) + | (ft << OP_SH_FT)); + } + + void divd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200003 | (fd << OP_SH_FD) | (fs << OP_SH_FS) + | (ft << OP_SH_FT)); + } + + void lwc1(FPRegisterID ft, RegisterID rs, int offset) + { + emitInst(0xc4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + copDelayNop(); + } + + void ldc1(FPRegisterID ft, RegisterID rs, int offset) + { + emitInst(0xd4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + } + + void swc1(FPRegisterID ft, RegisterID rs, int offset) + { + emitInst(0xe4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + } + + void sdc1(FPRegisterID ft, RegisterID rs, int offset) + { + emitInst(0xf4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + } + + void mtc1(RegisterID rt, FPRegisterID fs) + { + emitInst(0x44800000 | (fs << OP_SH_FS) | (rt << OP_SH_RT)); + copDelayNop(); + } + + void mthc1(RegisterID rt, FPRegisterID fs) + { + emitInst(0x44e00000 | (fs << OP_SH_FS) | (rt << OP_SH_RT)); + copDelayNop(); + } + + void mfc1(RegisterID rt, FPRegisterID fs) + { + emitInst(0x44000000 | (fs << OP_SH_FS) | (rt << OP_SH_RT)); + copDelayNop(); + } + + void sqrtd(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x46200004 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void truncwd(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x4620000d | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void cvtdw(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x46800021 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void cvtwd(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x46200024 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void ceqd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200032 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cngtd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x4620003f | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cnged(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x4620003d | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cltd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x4620003c | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cled(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x4620003e | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cueqd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200033 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void coled(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200036 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void coltd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200034 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void culed(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200037 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cultd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200035 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + // General helpers + + JmpDst label() + { + return JmpDst(m_buffer.size()); + } + + JmpDst align(int alignment) + { + while (!m_buffer.isAligned(alignment)) + bkpt(); + + return label(); + } + + static void* getRelocatedAddress(void* code, JmpSrc jump) + { + ASSERT(jump.m_offset != -1); + void* b = reinterpret_cast<void*>((reinterpret_cast<intptr_t>(code)) + jump.m_offset); + return b; + } + + static void* getRelocatedAddress(void* code, JmpDst label) + { + void* b = reinterpret_cast<void*>((reinterpret_cast<intptr_t>(code)) + label.m_offset); + return b; + } + + static int getDifferenceBetweenLabels(JmpDst from, JmpDst to) + { + return to.m_offset - from.m_offset; + } + + static int getDifferenceBetweenLabels(JmpDst from, JmpSrc to) + { + return to.m_offset - from.m_offset; + } + + static int getDifferenceBetweenLabels(JmpSrc from, JmpDst to) + { + return to.m_offset - from.m_offset; + } + + // Assembler admin methods: + + size_t size() const + { + return m_buffer.size(); + } + + void* executableCopy(ExecutablePool* allocator) + { + void *result = m_buffer.executableCopy(allocator); + if (!result) + return 0; + + relocateJumps(m_buffer.data(), result); + return result; + } + + static unsigned getCallReturnOffset(JmpSrc call) + { + // The return address is after a call and a delay slot instruction + return call.m_offset; + } + + // Linking & patching: + // + // 'link' and 'patch' methods are for use on unprotected code - such as the code + // within the AssemblerBuffer, and code being patched by the patch buffer. Once + // code has been finalized it is (platform support permitting) within a non- + // writable region of memory; to modify the code in an execute-only execuable + // pool the 'repatch' and 'relink' methods should be used. + + void linkJump(JmpSrc from, JmpDst to) + { + ASSERT(to.m_offset != -1); + ASSERT(from.m_offset != -1); + MIPSWord* insn = reinterpret_cast<MIPSWord*>(reinterpret_cast<intptr_t>(m_buffer.data()) + from.m_offset); + MIPSWord* toPos = reinterpret_cast<MIPSWord*>(reinterpret_cast<intptr_t>(m_buffer.data()) + to.m_offset); + + ASSERT(!(*(insn - 1)) && !(*(insn - 2)) && !(*(insn - 3)) && !(*(insn - 5))); + insn = insn - 6; + linkWithOffset(insn, toPos); + } + + static void linkJump(void* code, JmpSrc from, void* to) + { + ASSERT(from.m_offset != -1); + MIPSWord* insn = reinterpret_cast<MIPSWord*>(reinterpret_cast<intptr_t>(code) + from.m_offset); + + ASSERT(!(*(insn - 1)) && !(*(insn - 2)) && !(*(insn - 3)) && !(*(insn - 5))); + insn = insn - 6; + linkWithOffset(insn, to); + } + + static void linkCall(void* code, JmpSrc from, void* to) + { + MIPSWord* insn = reinterpret_cast<MIPSWord*>(reinterpret_cast<intptr_t>(code) + from.m_offset); + linkCallInternal(insn, to); + } + + static void linkPointer(void* code, JmpDst from, void* to) + { + MIPSWord* insn = reinterpret_cast<MIPSWord*>(reinterpret_cast<intptr_t>(code) + from.m_offset); + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + *insn = (*insn & 0xffff0000) | ((reinterpret_cast<intptr_t>(to) >> 16) & 0xffff); + insn++; + ASSERT((*insn & 0xfc000000) == 0x34000000); // ori + *insn = (*insn & 0xffff0000) | (reinterpret_cast<intptr_t>(to) & 0xffff); + } + + static void relinkJump(void* from, void* to) + { + MIPSWord* insn = reinterpret_cast<MIPSWord*>(from); + + ASSERT(!(*(insn - 1)) && !(*(insn - 5))); + insn = insn - 6; + int flushSize = linkWithOffset(insn, to); + + ExecutableAllocator::cacheFlush(insn, flushSize); + } + + static void relinkCall(void* from, void* to) + { + void* start; + int size = linkCallInternal(from, to); + if (size == sizeof(MIPSWord)) + start = reinterpret_cast<void*>(reinterpret_cast<intptr_t>(from) - 2 * sizeof(MIPSWord)); + else + start = reinterpret_cast<void*>(reinterpret_cast<intptr_t>(from) - 4 * sizeof(MIPSWord)); + + ExecutableAllocator::cacheFlush(start, size); + } + + static void repatchInt32(void* from, int32_t to) + { + MIPSWord* insn = reinterpret_cast<MIPSWord*>(from); + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + *insn = (*insn & 0xffff0000) | ((to >> 16) & 0xffff); + insn++; + ASSERT((*insn & 0xfc000000) == 0x34000000); // ori + *insn = (*insn & 0xffff0000) | (to & 0xffff); + insn--; + ExecutableAllocator::cacheFlush(insn, 2 * sizeof(MIPSWord)); + } + + static void repatchPointer(void* from, void* to) + { + repatchInt32(from, reinterpret_cast<int32_t>(to)); + } + + static void repatchLoadPtrToLEA(void* from) + { + MIPSWord* insn = reinterpret_cast<MIPSWord*>(from); + insn = insn + 3; + ASSERT((*insn & 0xfc000000) == 0x8c000000); // lw + /* lw -> addiu */ + *insn = 0x24000000 | (*insn & 0x03ffffff); + + ExecutableAllocator::cacheFlush(insn, sizeof(MIPSWord)); + } + +private: + + /* Update each jump in the buffer of newBase. */ + void relocateJumps(void* oldBase, void* newBase) + { + // Check each jump + for (Jumps::Iterator iter = m_jumps.begin(); iter != m_jumps.end(); ++iter) { + int pos = *iter; + MIPSWord* insn = reinterpret_cast<MIPSWord*>(reinterpret_cast<intptr_t>(newBase) + pos); + insn = insn + 2; + // Need to make sure we have 5 valid instructions after pos + if ((unsigned int)pos >= m_buffer.size() - 5 * sizeof(MIPSWord)) + continue; + + if ((*insn & 0xfc000000) == 0x08000000) { // j + int offset = *insn & 0x03ffffff; + int oldInsnAddress = (int)insn - (int)newBase + (int)oldBase; + int topFourBits = (oldInsnAddress + 4) >> 28; + int oldTargetAddress = (topFourBits << 28) | (offset << 2); + int newTargetAddress = oldTargetAddress - (int)oldBase + (int)newBase; + int newInsnAddress = (int)insn; + if (((newInsnAddress + 4) >> 28) == (newTargetAddress >> 28)) + *insn = 0x08000000 | ((newTargetAddress >> 2) & 0x3ffffff); + else { + /* lui */ + *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((newTargetAddress >> 16) & 0xffff); + /* ori */ + *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (newTargetAddress & 0xffff); + /* jr */ + *(insn + 2) = 0x00000008 | (MIPSRegisters::t9 << OP_SH_RS); + } + } else if ((*insn & 0xffe00000) == 0x3c000000) { // lui + int high = (*insn & 0xffff) << 16; + int low = *(insn + 1) & 0xffff; + int oldTargetAddress = high | low; + int newTargetAddress = oldTargetAddress - (int)oldBase + (int)newBase; + /* lui */ + *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((newTargetAddress >> 16) & 0xffff); + /* ori */ + *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (newTargetAddress & 0xffff); + } + } + } + + static int linkWithOffset(MIPSWord* insn, void* to) + { + ASSERT((*insn & 0xfc000000) == 0x10000000 // beq + || (*insn & 0xfc000000) == 0x14000000 // bne + || (*insn & 0xffff0000) == 0x45010000 // bc1t + || (*insn & 0xffff0000) == 0x45000000); // bc1f + intptr_t diff = (reinterpret_cast<intptr_t>(to) + - reinterpret_cast<intptr_t>(insn) - 4) >> 2; + + if (diff < -32768 || diff > 32767 || *(insn + 2) != 0x10000003) { + /* + Convert the sequence: + beq $2, $3, target + nop + b 1f + nop + nop + nop + 1: + + to the new sequence if possible: + bne $2, $3, 1f + nop + j target + nop + nop + nop + 1: + + OR to the new sequence: + bne $2, $3, 1f + nop + lui $25, target >> 16 + ori $25, $25, target & 0xffff + jr $25 + nop + 1: + + Note: beq/bne/bc1t/bc1f are converted to bne/beq/bc1f/bc1t. + */ + + if (*(insn + 2) == 0x10000003) { + if ((*insn & 0xfc000000) == 0x10000000) // beq + *insn = (*insn & 0x03ff0000) | 0x14000005; // bne + else if ((*insn & 0xfc000000) == 0x14000000) // bne + *insn = (*insn & 0x03ff0000) | 0x10000005; // beq + else if ((*insn & 0xffff0000) == 0x45010000) // bc1t + *insn = 0x45000005; // bc1f + else if ((*insn & 0xffff0000) == 0x45000000) // bc1f + *insn = 0x45010005; // bc1t + else + ASSERT(0); + } + + insn = insn + 2; + if ((reinterpret_cast<intptr_t>(insn) + 4) >> 28 + == reinterpret_cast<intptr_t>(to) >> 28) { + *insn = 0x08000000 | ((reinterpret_cast<intptr_t>(to) >> 2) & 0x3ffffff); + *(insn + 1) = 0; + return 4 * sizeof(MIPSWord); + } + + intptr_t newTargetAddress = reinterpret_cast<intptr_t>(to); + /* lui */ + *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((newTargetAddress >> 16) & 0xffff); + /* ori */ + *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (newTargetAddress & 0xffff); + /* jr */ + *(insn + 2) = 0x00000008 | (MIPSRegisters::t9 << OP_SH_RS); + return 5 * sizeof(MIPSWord); + } + + *insn = (*insn & 0xffff0000) | (diff & 0xffff); + return sizeof(MIPSWord); + } + + static int linkCallInternal(void* from, void* to) + { + MIPSWord* insn = reinterpret_cast<MIPSWord*>(from); + insn = insn - 4; + + if ((*(insn + 2) & 0xfc000000) == 0x0c000000) { // jal + if ((reinterpret_cast<intptr_t>(from) - 4) >> 28 + == reinterpret_cast<intptr_t>(to) >> 28) { + *(insn + 2) = 0x0c000000 | ((reinterpret_cast<intptr_t>(to) >> 2) & 0x3ffffff); + return sizeof(MIPSWord); + } + + /* lui $25, (to >> 16) & 0xffff */ + *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((reinterpret_cast<intptr_t>(to) >> 16) & 0xffff); + /* ori $25, $25, to & 0xffff */ + *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (reinterpret_cast<intptr_t>(to) & 0xffff); + /* jalr $25 */ + *(insn + 2) = 0x0000f809 | (MIPSRegisters::t9 << OP_SH_RS); + return 3 * sizeof(MIPSWord); + } + + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + ASSERT((*(insn + 1) & 0xfc000000) == 0x34000000); // ori + + /* lui */ + *insn = (*insn & 0xffff0000) | ((reinterpret_cast<intptr_t>(to) >> 16) & 0xffff); + /* ori */ + *(insn + 1) = (*(insn + 1) & 0xffff0000) | (reinterpret_cast<intptr_t>(to) & 0xffff); + return 2 * sizeof(MIPSWord); + } + + AssemblerBuffer m_buffer; + Jumps m_jumps; +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) && CPU(MIPS) + +#endif // MIPSAssembler_h diff --git a/JavaScriptCore/assembler/MacroAssembler.h b/JavaScriptCore/assembler/MacroAssembler.h index 76bd205..ce1be78 100644 --- a/JavaScriptCore/assembler/MacroAssembler.h +++ b/JavaScriptCore/assembler/MacroAssembler.h @@ -26,8 +26,6 @@ #ifndef MacroAssembler_h #define MacroAssembler_h -#include <wtf/Platform.h> - #if ENABLE(ASSEMBLER) #if CPU(ARM_THUMB2) @@ -38,6 +36,12 @@ namespace JSC { typedef MacroAssemblerARMv7 MacroAssemblerBase; }; #include "MacroAssemblerARM.h" namespace JSC { typedef MacroAssemblerARM MacroAssemblerBase; }; +#elif CPU(MIPS) +#include "MacroAssemblerMIPS.h" +namespace JSC { +typedef MacroAssemblerMIPS MacroAssemblerBase; +}; + #elif CPU(X86) #include "MacroAssemblerX86.h" namespace JSC { typedef MacroAssemblerX86 MacroAssemblerBase; }; @@ -327,6 +331,11 @@ public: { return branchSub32(cond, imm, dest); } + using MacroAssemblerBase::branchTest8; + Jump branchTest8(Condition cond, ExtendedAddress address, Imm32 mask = Imm32(-1)) + { + return MacroAssemblerBase::branchTest8(cond, Address(address.base, address.offset), mask); + } #endif }; diff --git a/JavaScriptCore/assembler/MacroAssemblerARM.cpp b/JavaScriptCore/assembler/MacroAssemblerARM.cpp index b5b20fa..2db5df1 100644 --- a/JavaScriptCore/assembler/MacroAssemblerARM.cpp +++ b/JavaScriptCore/assembler/MacroAssemblerARM.cpp @@ -57,7 +57,11 @@ static bool isVFPPresent() } #endif +#if (COMPILER(RVCT) && defined(__TARGET_FPU_VFP)) || (COMPILER(GCC) && defined(__VFP_FP__)) + return true; +#else return false; +#endif } const bool MacroAssemblerARM::s_isVFPPresent = isVFPPresent(); diff --git a/JavaScriptCore/assembler/MacroAssemblerARM.h b/JavaScriptCore/assembler/MacroAssemblerARM.h index 21b8de8..c51686a 100644 --- a/JavaScriptCore/assembler/MacroAssemblerARM.h +++ b/JavaScriptCore/assembler/MacroAssemblerARM.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2008 Apple Inc. - * Copyright (C) 2009 University of Szeged + * Copyright (C) 2009, 2010 University of Szeged * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,8 +28,6 @@ #ifndef MacroAssemblerARM_h #define MacroAssemblerARM_h -#include <wtf/Platform.h> - #if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) #include "ARMAssembler.h" @@ -42,6 +40,8 @@ class MacroAssemblerARM : public AbstractMacroAssembler<ARMAssembler> { static const int DoubleConditionBitSpecial = 0x10; COMPILE_ASSERT(!(DoubleConditionBitSpecial & DoubleConditionMask), DoubleConditionBitSpecial_should_not_interfere_with_ARMAssembler_Condition_codes); public: + typedef ARMRegisters::FPRegisterID FPRegisterID; + enum Condition { Equal = ARMAssembler::EQ, NotEqual = ARMAssembler::NE, @@ -180,6 +180,20 @@ public: { m_assembler.movs_r(dest, m_assembler.asr(dest, imm.m_value & 0x1f)); } + + void urshift32(RegisterID shift_amount, RegisterID dest) + { + ARMWord w = ARMAssembler::getOp2(0x1f); + ASSERT(w != ARMAssembler::INVALID_IMM); + m_assembler.and_r(ARMRegisters::S0, shift_amount, w); + + m_assembler.movs_r(dest, m_assembler.lsr_r(dest, ARMRegisters::S0)); + } + + void urshift32(Imm32 imm, RegisterID dest) + { + m_assembler.movs_r(dest, m_assembler.lsr(dest, imm.m_value & 0x1f)); + } void sub32(RegisterID src, RegisterID dest) { @@ -214,6 +228,22 @@ public: m_assembler.eors_r(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); } + void countLeadingZeros32(RegisterID src, RegisterID dest) + { +#if WTF_ARM_ARCH_AT_LEAST(5) + m_assembler.clz_r(dest, src); +#else + UNUSED_PARAM(src); + UNUSED_PARAM(dest); + ASSERT_NOT_REACHED(); +#endif + } + + void load8(ImplicitAddress address, RegisterID dest) + { + m_assembler.dataTransfer32(true, dest, address.base, address.offset, true); + } + void load32(ImplicitAddress address, RegisterID dest) { m_assembler.dataTransfer32(true, dest, address.base, address.offset); @@ -250,11 +280,16 @@ public: void load16(BaseIndex address, RegisterID dest) { - m_assembler.add_r(ARMRegisters::S0, address.base, m_assembler.lsl(address.index, address.scale)); - if (address.offset>=0) - m_assembler.ldrh_u(dest, ARMRegisters::S0, ARMAssembler::getOp2Byte(address.offset)); + m_assembler.add_r(ARMRegisters::S1, address.base, m_assembler.lsl(address.index, address.scale)); + load16(Address(ARMRegisters::S1, address.offset), dest); + } + + void load16(ImplicitAddress address, RegisterID dest) + { + if (address.offset >= 0) + m_assembler.ldrh_u(dest, address.base, m_assembler.getOffsetForHalfwordDataTransfer(address.offset, ARMRegisters::S0)); else - m_assembler.ldrh_d(dest, ARMRegisters::S0, ARMAssembler::getOp2Byte(-address.offset)); + m_assembler.ldrh_d(dest, address.base, m_assembler.getOffsetForHalfwordDataTransfer(-address.offset, ARMRegisters::S0)); } DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) @@ -359,6 +394,12 @@ public: move(src, dest); } + Jump branch8(Condition cond, Address left, Imm32 right) + { + load8(left, ARMRegisters::S1); + return branch32(cond, ARMRegisters::S1, right); + } + Jump branch32(Condition cond, RegisterID left, RegisterID right, int useConstantPool = 0) { m_assembler.cmp_r(left, right); @@ -370,8 +411,13 @@ public: if (right.m_isPointer) { m_assembler.ldr_un_imm(ARMRegisters::S0, right.m_value); m_assembler.cmp_r(left, ARMRegisters::S0); - } else - m_assembler.cmp_r(left, m_assembler.getImm(right.m_value, ARMRegisters::S0)); + } else { + ARMWord tmp = m_assembler.getOp2(-right.m_value); + if (tmp != ARMAssembler::INVALID_IMM) + m_assembler.cmn_r(left, tmp); + else + m_assembler.cmp_r(left, m_assembler.getImm(right.m_value, ARMRegisters::S0)); + } return Jump(m_assembler.jmp(ARMCondition(cond), useConstantPool)); } @@ -422,6 +468,12 @@ public: return m_assembler.jmp(ARMCondition(cond)); } + Jump branchTest8(Condition cond, Address address, Imm32 mask = Imm32(-1)) + { + load8(address, ARMRegisters::S1); + return branchTest32(cond, ARMRegisters::S1, mask); + } + Jump branchTest32(Condition cond, RegisterID reg, RegisterID mask) { ASSERT((cond == Zero) || (cond == NonZero)); @@ -459,7 +511,7 @@ public: void jump(RegisterID target) { - move(target, ARMRegisters::pc); + m_assembler.bx(target); } void jump(Address address) @@ -530,6 +582,13 @@ public: return Jump(m_assembler.jmp(ARMCondition(cond))); } + Jump branchNeg32(Condition cond, RegisterID srcDest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + neg32(srcDest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + Jump branchOr32(Condition cond, RegisterID src, RegisterID dest) { ASSERT((cond == Signed) || (cond == Zero) || (cond == NonZero)); @@ -544,16 +603,19 @@ public: Call nearCall() { +#if WTF_ARM_ARCH_AT_LEAST(5) + ensureSpace(2 * sizeof(ARMWord), sizeof(ARMWord)); + m_assembler.loadBranchTarget(ARMRegisters::S1, ARMAssembler::AL, true); + return Call(m_assembler.blx(ARMRegisters::S1), Call::LinkableNear); +#else prepareCall(); return Call(m_assembler.jmp(ARMAssembler::AL, true), Call::LinkableNear); +#endif } Call call(RegisterID target) { - prepareCall(); - move(ARMRegisters::pc, target); - JmpSrc jmpSrc; - return Call(jmpSrc, Call::None); + return Call(m_assembler.blx(target), Call::None); } void call(Address address) @@ -563,7 +625,7 @@ public: void ret() { - m_assembler.mov_r(ARMRegisters::pc, linkRegister); + m_assembler.bx(linkRegister); } void set32(Condition cond, RegisterID left, RegisterID right, RegisterID dest) @@ -657,10 +719,25 @@ public: return branch32(cond, ARMRegisters::S1, right); } + void relativeTableJump(RegisterID index, int scale) + { + ASSERT(scale >= 0 && scale <= 31); + m_assembler.add_r(ARMRegisters::pc, ARMRegisters::pc, m_assembler.lsl(index, scale)); + + // NOP the default prefetching + m_assembler.mov_r(ARMRegisters::r0, ARMRegisters::r0); + } + Call call() { +#if WTF_ARM_ARCH_AT_LEAST(5) + ensureSpace(2 * sizeof(ARMWord), sizeof(ARMWord)); + m_assembler.loadBranchTarget(ARMRegisters::S1, ARMAssembler::AL, true); + return Call(m_assembler.blx(ARMRegisters::S1), Call::Linkable); +#else prepareCall(); return Call(m_assembler.jmp(ARMAssembler::AL, true), Call::Linkable); +#endif } Call tailRecursiveCall() @@ -715,7 +792,12 @@ public: bool supportsFloatingPointTruncate() const { - return false; + return s_isVFPPresent; + } + + bool supportsFloatingPointSqrt() const + { + return s_isVFPPresent; } void loadDouble(ImplicitAddress address, FPRegisterID dest) @@ -723,7 +805,7 @@ public: m_assembler.doubleTransfer(true, dest, address.base, address.offset); } - void loadDouble(void* address, FPRegisterID dest) + void loadDouble(const void* address, FPRegisterID dest) { m_assembler.ldr_un_imm(ARMRegisters::S0, (ARMWord)address); m_assembler.fdtr_u(true, dest, ARMRegisters::S0, 0); @@ -736,7 +818,7 @@ public: void addDouble(FPRegisterID src, FPRegisterID dest) { - m_assembler.faddd_r(dest, dest, src); + m_assembler.vadd_f64_r(dest, dest, src); } void addDouble(Address src, FPRegisterID dest) @@ -747,7 +829,7 @@ public: void divDouble(FPRegisterID src, FPRegisterID dest) { - m_assembler.fdivd_r(dest, dest, src); + m_assembler.vdiv_f64_r(dest, dest, src); } void divDouble(Address src, FPRegisterID dest) @@ -759,7 +841,7 @@ public: void subDouble(FPRegisterID src, FPRegisterID dest) { - m_assembler.fsubd_r(dest, dest, src); + m_assembler.vsub_f64_r(dest, dest, src); } void subDouble(Address src, FPRegisterID dest) @@ -770,7 +852,7 @@ public: void mulDouble(FPRegisterID src, FPRegisterID dest) { - m_assembler.fmuld_r(dest, dest, src); + m_assembler.vmul_f64_r(dest, dest, src); } void mulDouble(Address src, FPRegisterID dest) @@ -779,10 +861,15 @@ public: mulDouble(ARMRegisters::SD0, dest); } + void sqrtDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vsqrt_f64_r(dest, src); + } + void convertInt32ToDouble(RegisterID src, FPRegisterID dest) { - m_assembler.fmsr_r(dest, src); - m_assembler.fsitod_r(dest, dest); + m_assembler.vmov_vfp_r(dest << 1, src); + m_assembler.vcvt_f64_s32_r(dest, dest << 1); } void convertInt32ToDouble(Address src, FPRegisterID dest) @@ -804,8 +891,8 @@ public: Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) { - m_assembler.fcmpd_r(left, right); - m_assembler.fmstat(); + m_assembler.vcmp_f64_r(left, right); + m_assembler.vmrs_apsr(); if (cond & DoubleConditionBitSpecial) m_assembler.cmp_r(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::VS); return Jump(m_assembler.jmp(static_cast<ARMAssembler::Condition>(cond & ~DoubleConditionMask))); @@ -814,13 +901,17 @@ public: // Truncates 'src' to an integer, and places the resulting 'dest'. // If the result is not representable as a 32 bit value, branch. // May also branch for some values that are representable in 32 bits - // (specifically, in this case, INT_MIN). + // (specifically, in this case, INT_MIN and INT_MAX). Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest) { - UNUSED_PARAM(src); - UNUSED_PARAM(dest); - ASSERT_NOT_REACHED(); - return jump(); + m_assembler.vcvtr_s32_f64_r(ARMRegisters::SD0 << 1, src); + // If VCVTR.S32.F64 can't fit the result into a 32-bit + // integer, it saturates at INT_MAX or INT_MIN. Testing this is + // probably quicker than testing FPSCR for exception. + m_assembler.vmov_arm_r(dest, ARMRegisters::SD0 << 1); + m_assembler.sub_r(ARMRegisters::S0, dest, ARMAssembler::getOp2(0x80000000)); + m_assembler.cmn_r(ARMRegisters::S0, ARMAssembler::getOp2(1), ARMCondition(NotEqual)); + return Jump(m_assembler.jmp(ARMCondition(Equal))); } // Convert 'src' to an integer, and places the resulting 'dest'. @@ -829,11 +920,11 @@ public: // (specifically, in this case, 0). void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) { - m_assembler.ftosid_r(ARMRegisters::SD0, src); - m_assembler.fmrs_r(dest, ARMRegisters::SD0); + m_assembler.vcvt_s32_f64_r(ARMRegisters::SD0 << 1, src); + m_assembler.vmov_arm_r(dest, ARMRegisters::SD0 << 1); // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. - m_assembler.fsitod_r(ARMRegisters::SD0, ARMRegisters::SD0); + m_assembler.vcvt_f64_s32_r(ARMRegisters::SD0, ARMRegisters::SD0 << 1); failureCases.append(branchDouble(DoubleNotEqualOrUnordered, src, ARMRegisters::SD0)); // If the result is zero, it might have been -0.0, and 0.0 equals to -0.0 @@ -864,44 +955,56 @@ protected: void prepareCall() { +#if WTF_ARM_ARCH_VERSION < 5 ensureSpace(2 * sizeof(ARMWord), sizeof(ARMWord)); m_assembler.mov_r(linkRegister, ARMRegisters::pc); +#endif } void call32(RegisterID base, int32_t offset) { +#if WTF_ARM_ARCH_AT_LEAST(5) + int targetReg = ARMRegisters::S1; +#else + int targetReg = ARMRegisters::pc; +#endif + int tmpReg = ARMRegisters::S1; + if (base == ARMRegisters::sp) offset += 4; if (offset >= 0) { if (offset <= 0xfff) { prepareCall(); - m_assembler.dtr_u(true, ARMRegisters::pc, base, offset); + m_assembler.dtr_u(true, targetReg, base, offset); } else if (offset <= 0xfffff) { - m_assembler.add_r(ARMRegisters::S0, base, ARMAssembler::OP2_IMM | (offset >> 12) | (10 << 8)); + m_assembler.add_r(tmpReg, base, ARMAssembler::OP2_IMM | (offset >> 12) | (10 << 8)); prepareCall(); - m_assembler.dtr_u(true, ARMRegisters::pc, ARMRegisters::S0, offset & 0xfff); + m_assembler.dtr_u(true, targetReg, tmpReg, offset & 0xfff); } else { - ARMWord reg = m_assembler.getImm(offset, ARMRegisters::S0); + m_assembler.moveImm(offset, tmpReg); prepareCall(); - m_assembler.dtr_ur(true, ARMRegisters::pc, base, reg); + m_assembler.dtr_ur(true, targetReg, base, tmpReg); } } else { offset = -offset; if (offset <= 0xfff) { prepareCall(); - m_assembler.dtr_d(true, ARMRegisters::pc, base, offset); + m_assembler.dtr_d(true, targetReg, base, offset); } else if (offset <= 0xfffff) { - m_assembler.sub_r(ARMRegisters::S0, base, ARMAssembler::OP2_IMM | (offset >> 12) | (10 << 8)); + m_assembler.sub_r(tmpReg, base, ARMAssembler::OP2_IMM | (offset >> 12) | (10 << 8)); prepareCall(); - m_assembler.dtr_d(true, ARMRegisters::pc, ARMRegisters::S0, offset & 0xfff); + m_assembler.dtr_d(true, targetReg, tmpReg, offset & 0xfff); } else { - ARMWord reg = m_assembler.getImm(offset, ARMRegisters::S0); + m_assembler.moveImm(offset, tmpReg); prepareCall(); - m_assembler.dtr_dr(true, ARMRegisters::pc, base, reg); + m_assembler.dtr_dr(true, targetReg, base, tmpReg); } } +#if WTF_ARM_ARCH_AT_LEAST(5) + m_assembler.blx(targetReg); +#endif } private: diff --git a/JavaScriptCore/assembler/MacroAssemblerARMv7.h b/JavaScriptCore/assembler/MacroAssemblerARMv7.h index 532a9cf..d5b4ce7 100644 --- a/JavaScriptCore/assembler/MacroAssemblerARMv7.h +++ b/JavaScriptCore/assembler/MacroAssemblerARMv7.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 University of Szeged * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,8 +27,6 @@ #ifndef MacroAssemblerARMv7_h #define MacroAssemblerARMv7_h -#include <wtf/Platform.h> - #if ENABLE(ASSEMBLER) #include "ARMv7Assembler.h" @@ -39,9 +38,32 @@ class MacroAssemblerARMv7 : public AbstractMacroAssembler<ARMv7Assembler> { // FIXME: switch dataTempRegister & addressTempRegister, or possibly use r7? // - dTR is likely used more than aTR, and we'll get better instruction // encoding if it's in the low 8 registers. - static const ARMRegisters::RegisterID dataTempRegister = ARMRegisters::ip; + static const RegisterID dataTempRegister = ARMRegisters::ip; static const RegisterID addressTempRegister = ARMRegisters::r3; - static const FPRegisterID fpTempRegister = ARMRegisters::d7; + + static const ARMRegisters::FPDoubleRegisterID fpTempRegister = ARMRegisters::d7; + inline ARMRegisters::FPSingleRegisterID fpTempRegisterAsSingle() { return ARMRegisters::asSingle(fpTempRegister); } + +public: + typedef ARMv7Assembler::LinkRecord LinkRecord; + typedef ARMv7Assembler::JumpType JumpType; + typedef ARMv7Assembler::JumpLinkType JumpLinkType; + + MacroAssemblerARMv7() + : m_inUninterruptedSequence(false) + { + } + + void beginUninterruptedSequence() { m_inUninterruptedSequence = true; } + void endUninterruptedSequence() { m_inUninterruptedSequence = false; } + Vector<LinkRecord>& jumpsToLink() { return m_assembler.jumpsToLink(); } + void* unlinkedCode() { return m_assembler.unlinkedCode(); } + bool canCompact(JumpType jumpType) { return m_assembler.canCompact(jumpType); } + JumpLinkType computeJumpType(JumpType jumpType, const uint8_t* from, const uint8_t* to) { return m_assembler.computeJumpType(jumpType, from, to); } + JumpLinkType computeJumpType(LinkRecord& record, const uint8_t* from, const uint8_t* to) { return m_assembler.computeJumpType(record, from, to); } + void recordLinkOffsets(int32_t regionStart, int32_t regionEnd, int32_t offset) {return m_assembler.recordLinkOffsets(regionStart, regionEnd, offset); } + int jumpSizeDelta(JumpType jumpType, JumpLinkType jumpLinkType) { return m_assembler.jumpSizeDelta(jumpType, jumpLinkType); } + void link(LinkRecord& record, uint8_t* from, uint8_t* to) { return m_assembler.link(record, from, to); } struct ArmAddress { enum AddressType { @@ -74,6 +96,7 @@ class MacroAssemblerARMv7 : public AbstractMacroAssembler<ARMv7Assembler> { }; public: + typedef ARMRegisters::FPDoubleRegisterID FPRegisterID; static const Scale ScalePtr = TimesFour; @@ -197,6 +220,11 @@ public: } } + void countLeadingZeros32(RegisterID src, RegisterID dest) + { + m_assembler.clz(dest, src); + } + void lshift32(RegisterID shift_amount, RegisterID dest) { // Clamp the shift to the range 0..31 @@ -223,6 +251,11 @@ public: m_assembler.smull(dest, dataTempRegister, src, dataTempRegister); } + void neg32(RegisterID srcDest) + { + m_assembler.neg(srcDest, srcDest); + } + void not32(RegisterID srcDest) { m_assembler.mvn(srcDest, srcDest); @@ -258,6 +291,21 @@ public: { m_assembler.asr(dest, dest, imm.m_value & 0x1f); } + + void urshift32(RegisterID shift_amount, RegisterID dest) + { + // Clamp the shift to the range 0..31 + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(0x1f); + ASSERT(armImm.isValid()); + m_assembler.ARM_and(dataTempRegister, shift_amount, armImm); + + m_assembler.lsr(dest, dest, dataTempRegister); + } + + void urshift32(Imm32 imm, RegisterID dest) + { + m_assembler.lsr(dest, dest, imm.m_value & 0x1f); + } void sub32(RegisterID src, RegisterID dest) { @@ -368,6 +416,20 @@ private: } } + void load8(ArmAddress address, RegisterID dest) + { + if (address.type == ArmAddress::HasIndex) + m_assembler.ldrb(dest, address.base, address.u.index, address.u.scale); + else if (address.u.offset >= 0) { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); + ASSERT(armImm.isValid()); + m_assembler.ldrb(dest, address.base, armImm); + } else { + ASSERT(address.u.offset >= -255); + m_assembler.ldrb(dest, address.base, address.u.offset, true, false); + } + } + void store32(RegisterID src, ArmAddress address) { if (address.type == ArmAddress::HasIndex) @@ -404,6 +466,11 @@ public: m_assembler.ldr(dest, addressTempRegister, ARMThumbImmediate::makeUInt16(0)); } + void load8(ImplicitAddress address, RegisterID dest) + { + load8(setupArmAddress(address), dest); + } + DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) { DataLabel32 label = moveWithPatch(Imm32(address.offset), dataTempRegister); @@ -423,6 +490,17 @@ public: { m_assembler.ldrh(dest, makeBaseIndexBase(address), address.index, address.scale); } + + void load16(ImplicitAddress address, RegisterID dest) + { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.offset); + if (armImm.isValid()) + m_assembler.ldrh(dest, address.base, armImm); + else { + move(Imm32(address.offset), dataTempRegister); + m_assembler.ldrh(dest, address.base, dataTempRegister); + } + } DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) { @@ -476,6 +554,11 @@ public: // In short, FIXME:. bool supportsFloatingPointTruncate() const { return false; } + bool supportsFloatingPointSqrt() const + { + return false; + } + void loadDouble(ImplicitAddress address, FPRegisterID dest) { RegisterID base = address.base; @@ -491,6 +574,12 @@ public: m_assembler.vldr(dest, base, offset); } + void loadDouble(const void* address, FPRegisterID dest) + { + move(ImmPtr(address), addressTempRegister); + m_assembler.vldr(dest, addressTempRegister, 0); + } + void storeDouble(FPRegisterID src, ImplicitAddress address) { RegisterID base = address.base; @@ -517,6 +606,11 @@ public: addDouble(fpTempRegister, dest); } + void divDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vdiv_F64(dest, dest, src); + } + void subDouble(FPRegisterID src, FPRegisterID dest) { m_assembler.vsub_F64(dest, dest, src); @@ -539,16 +633,37 @@ public: mulDouble(fpTempRegister, dest); } + void sqrtDouble(FPRegisterID, FPRegisterID) + { + ASSERT_NOT_REACHED(); + } + void convertInt32ToDouble(RegisterID src, FPRegisterID dest) { - m_assembler.vmov(fpTempRegister, src); - m_assembler.vcvt_F64_S32(dest, fpTempRegister); + m_assembler.vmov(fpTempRegisterAsSingle(), src); + m_assembler.vcvt_F64_S32(dest, fpTempRegisterAsSingle()); + } + + void convertInt32ToDouble(Address address, FPRegisterID dest) + { + // Fixme: load directly into the fpr! + load32(address, dataTempRegister); + m_assembler.vmov(fpTempRegisterAsSingle(), dataTempRegister); + m_assembler.vcvt_F64_S32(dest, fpTempRegisterAsSingle()); + } + + void convertInt32ToDouble(AbsoluteAddress address, FPRegisterID dest) + { + // Fixme: load directly into the fpr! + load32(address.m_ptr, dataTempRegister); + m_assembler.vmov(fpTempRegisterAsSingle(), dataTempRegister); + m_assembler.vcvt_F64_S32(dest, fpTempRegisterAsSingle()); } Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) { m_assembler.vcmp_F64(left, right); - m_assembler.vmrs_APSR_nzcv_FPSCR(); + m_assembler.vmrs(); if (cond == DoubleNotEqual) { // ConditionNE jumps if NotEqual *or* unordered - force the unordered cases not to jump. @@ -575,6 +690,27 @@ public: return jump(); } + // Convert 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, 0). + void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID) + { + m_assembler.vcvtr_S32_F64(fpTempRegisterAsSingle(), src); + m_assembler.vmov(dest, fpTempRegisterAsSingle()); + + // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. + m_assembler.vcvt_F64_S32(fpTempRegister, fpTempRegisterAsSingle()); + failureCases.append(branchDouble(DoubleNotEqualOrUnordered, src, fpTempRegister)); + + // If the result is zero, it might have been -0.0, and the double comparison won't catch this! + failureCases.append(branchTest32(Zero, dest)); + } + + void zeroDouble(FPRegisterID dest) + { + m_assembler.vmov_F64_0(dest); + } // Stack manipulation operations: // @@ -793,6 +929,19 @@ public: return branch32(cond, addressTempRegister, Imm32(right.m_value << 16)); } + Jump branch8(Condition cond, RegisterID left, Imm32 right) + { + compare32(left, right); + return Jump(makeBranch(cond)); + } + + Jump branch8(Condition cond, Address left, Imm32 right) + { + // use addressTempRegister incase the branch8 we call uses dataTempRegister. :-/ + load8(left, addressTempRegister); + return branch8(cond, addressTempRegister, right); + } + Jump branchTest32(Condition cond, RegisterID reg, RegisterID mask) { ASSERT((cond == Zero) || (cond == NonZero)); @@ -823,6 +972,21 @@ public: return branchTest32(cond, addressTempRegister, mask); } + Jump branchTest8(Condition cond, RegisterID reg, Imm32 mask = Imm32(-1)) + { + ASSERT((cond == Zero) || (cond == NonZero)); + test32(reg, mask); + return Jump(makeBranch(cond)); + } + + Jump branchTest8(Condition cond, Address address, Imm32 mask = Imm32(-1)) + { + ASSERT((cond == Zero) || (cond == NonZero)); + // use addressTempRegister incase the branchTest8 we call uses dataTempRegister. :-/ + load8(address, addressTempRegister); + return branchTest8(cond, addressTempRegister, mask); + } + Jump jump() { return Jump(makeJump()); @@ -830,14 +994,14 @@ public: void jump(RegisterID target) { - m_assembler.bx(target); + m_assembler.bx(target, ARMv7Assembler::JumpFixed); } // Address is a memory location containing the address to jump to void jump(Address address) { load32(address, dataTempRegister); - m_assembler.bx(dataTempRegister); + m_assembler.bx(dataTempRegister, ARMv7Assembler::JumpFixed); } @@ -873,7 +1037,7 @@ public: Jump branchMul32(Condition cond, RegisterID src, RegisterID dest) { - ASSERT(cond == Overflow); + ASSERT_UNUSED(cond, cond == Overflow); m_assembler.smull(dest, dataTempRegister, dest, src); m_assembler.asr(addressTempRegister, dest, 31); return branch32(NotEqual, addressTempRegister, dataTempRegister); @@ -881,13 +1045,20 @@ public: Jump branchMul32(Condition cond, Imm32 imm, RegisterID src, RegisterID dest) { - ASSERT(cond == Overflow); + ASSERT_UNUSED(cond, cond == Overflow); move(imm, dataTempRegister); m_assembler.smull(dest, dataTempRegister, src, dataTempRegister); m_assembler.asr(addressTempRegister, dest, 31); return branch32(NotEqual, addressTempRegister, dataTempRegister); } + Jump branchOr32(Condition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Signed) || (cond == Zero) || (cond == NonZero)); + m_assembler.orr_S(dest, dest, src); + return Jump(makeBranch(cond)); + } + Jump branchSub32(Condition cond, RegisterID src, RegisterID dest) { ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); @@ -908,40 +1079,52 @@ public: return Jump(makeBranch(cond)); } + void relativeTableJump(RegisterID index, int scale) + { + ASSERT(scale >= 0 && scale <= 31); + + // dataTempRegister will point after the jump if index register contains zero + move(ARMRegisters::pc, dataTempRegister); + m_assembler.add(dataTempRegister, dataTempRegister, ARMThumbImmediate::makeEncodedImm(9)); + + ShiftTypeAndAmount shift(SRType_LSL, scale); + m_assembler.add(dataTempRegister, dataTempRegister, index, shift); + jump(dataTempRegister); + } // Miscellaneous operations: void breakpoint() { - m_assembler.bkpt(); + m_assembler.bkpt(0); } Call nearCall() { moveFixedWidthEncoding(Imm32(0), dataTempRegister); - return Call(m_assembler.blx(dataTempRegister), Call::LinkableNear); + return Call(m_assembler.blx(dataTempRegister, ARMv7Assembler::JumpFixed), Call::LinkableNear); } Call call() { moveFixedWidthEncoding(Imm32(0), dataTempRegister); - return Call(m_assembler.blx(dataTempRegister), Call::Linkable); + return Call(m_assembler.blx(dataTempRegister, ARMv7Assembler::JumpFixed), Call::Linkable); } Call call(RegisterID target) { - return Call(m_assembler.blx(target), Call::None); + return Call(m_assembler.blx(target, ARMv7Assembler::JumpFixed), Call::None); } Call call(Address address) { load32(address, dataTempRegister); - return Call(m_assembler.blx(dataTempRegister), Call::None); + return Call(m_assembler.blx(dataTempRegister, ARMv7Assembler::JumpFixed), Call::None); } void ret() { - m_assembler.bx(linkRegister); + m_assembler.bx(linkRegister, ARMv7Assembler::JumpFixed); } void set32(Condition cond, RegisterID left, RegisterID right, RegisterID dest) @@ -952,6 +1135,12 @@ public: m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0)); } + void set32(Condition cond, Address left, RegisterID right, RegisterID dest) + { + load32(left, dataTempRegister); + set32(cond, dataTempRegister, right, dest); + } + void set32(Condition cond, RegisterID left, Imm32 right, RegisterID dest) { compare32(left, right); @@ -960,6 +1149,21 @@ public: m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0)); } + void set8(Condition cond, RegisterID left, RegisterID right, RegisterID dest) + { + set32(cond, left, right, dest); + } + + void set8(Condition cond, Address left, RegisterID right, RegisterID dest) + { + set32(cond, left, right, dest); + } + + void set8(Condition cond, RegisterID left, Imm32 right, RegisterID dest) + { + set32(cond, left, right, dest); + } + // FIXME: // The mask should be optional... paerhaps the argument order should be // dest-src, operations always have a dest? ... possibly not true, considering @@ -973,6 +1177,14 @@ public: m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0)); } + void setTest8(Condition cond, Address address, Imm32 mask, RegisterID dest) + { + load8(address, dataTempRegister); + test32(dataTempRegister, mask); + m_assembler.it(armV7Condition(cond), false); + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(1)); + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0)); + } DataLabel32 moveWithPatch(Imm32 imm, RegisterID dst) { @@ -1012,7 +1224,7 @@ public: { // Like a normal call, but don't link. moveFixedWidthEncoding(Imm32(0), dataTempRegister); - return Call(m_assembler.bx(dataTempRegister), Call::Linkable); + return Call(m_assembler.bx(dataTempRegister, ARMv7Assembler::JumpFixed), Call::Linkable); } Call makeTailRecursiveCall(Jump oldJump) @@ -1021,19 +1233,29 @@ public: return tailRecursiveCall(); } + + int executableOffsetFor(int location) + { + return m_assembler.executableOffsetFor(location); + } protected: + bool inUninterruptedSequence() + { + return m_inUninterruptedSequence; + } + ARMv7Assembler::JmpSrc makeJump() { moveFixedWidthEncoding(Imm32(0), dataTempRegister); - return m_assembler.bx(dataTempRegister); + return m_assembler.bx(dataTempRegister, inUninterruptedSequence() ? ARMv7Assembler::JumpNoConditionFixedSize : ARMv7Assembler::JumpNoCondition); } ARMv7Assembler::JmpSrc makeBranch(ARMv7Assembler::Condition cond) { m_assembler.it(cond, true, true); moveFixedWidthEncoding(Imm32(0), dataTempRegister); - return m_assembler.bx(dataTempRegister); + return m_assembler.bx(dataTempRegister, inUninterruptedSequence() ? ARMv7Assembler::JumpConditionFixedSize : ARMv7Assembler::JumpCondition, cond); } ARMv7Assembler::JmpSrc makeBranch(Condition cond) { return makeBranch(armV7Condition(cond)); } ARMv7Assembler::JmpSrc makeBranch(DoubleCondition cond) { return makeBranch(armV7Condition(cond)); } @@ -1123,6 +1345,8 @@ private: { ARMv7Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); } + + bool m_inUninterruptedSequence; }; } // namespace JSC diff --git a/JavaScriptCore/assembler/MacroAssemblerCodeRef.h b/JavaScriptCore/assembler/MacroAssemblerCodeRef.h index cae8bf6..543b0fa 100644 --- a/JavaScriptCore/assembler/MacroAssemblerCodeRef.h +++ b/JavaScriptCore/assembler/MacroAssemblerCodeRef.h @@ -26,8 +26,6 @@ #ifndef MacroAssemblerCodeRef_h #define MacroAssemblerCodeRef_h -#include <wtf/Platform.h> - #include "ExecutableAllocator.h" #include "PassRefPtr.h" #include "RefPtr.h" diff --git a/JavaScriptCore/assembler/MacroAssemblerMIPS.h b/JavaScriptCore/assembler/MacroAssemblerMIPS.h new file mode 100644 index 0000000..3fb2549 --- /dev/null +++ b/JavaScriptCore/assembler/MacroAssemblerMIPS.h @@ -0,0 +1,1859 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2010 MIPS Technologies, 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 MIPS TECHNOLOGIES, 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 MIPS TECHNOLOGIES, 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 MacroAssemblerMIPS_h +#define MacroAssemblerMIPS_h + +#if ENABLE(ASSEMBLER) && CPU(MIPS) + +#include "AbstractMacroAssembler.h" +#include "MIPSAssembler.h" + +namespace JSC { + +class MacroAssemblerMIPS : public AbstractMacroAssembler<MIPSAssembler> { +public: + typedef MIPSRegisters::FPRegisterID FPRegisterID; + + MacroAssemblerMIPS() + : m_fixedWidth(false) + { + } + + static const Scale ScalePtr = TimesFour; + + // For storing immediate number + static const RegisterID immTempRegister = MIPSRegisters::t0; + // For storing data loaded from the memory + static const RegisterID dataTempRegister = MIPSRegisters::t1; + // For storing address base + static const RegisterID addrTempRegister = MIPSRegisters::t2; + // For storing compare result + static const RegisterID cmpTempRegister = MIPSRegisters::t3; + + // FP temp register + static const FPRegisterID fpTempRegister = MIPSRegisters::f16; + + enum Condition { + Equal, + NotEqual, + Above, + AboveOrEqual, + Below, + BelowOrEqual, + GreaterThan, + GreaterThanOrEqual, + LessThan, + LessThanOrEqual, + Overflow, + Signed, + Zero, + NonZero + }; + + enum DoubleCondition { + DoubleEqual, + DoubleNotEqual, + DoubleGreaterThan, + DoubleGreaterThanOrEqual, + DoubleLessThan, + DoubleLessThanOrEqual, + DoubleEqualOrUnordered, + DoubleNotEqualOrUnordered, + DoubleGreaterThanOrUnordered, + DoubleGreaterThanOrEqualOrUnordered, + DoubleLessThanOrUnordered, + DoubleLessThanOrEqualOrUnordered + }; + + static const RegisterID stackPointerRegister = MIPSRegisters::sp; + static const RegisterID returnAddressRegister = MIPSRegisters::ra; + + // Integer arithmetic operations: + // + // Operations are typically two operand - operation(source, srcDst) + // For many operations the source may be an Imm32, the srcDst operand + // may often be a memory location (explictly described using an Address + // object). + + void add32(RegisterID src, RegisterID dest) + { + m_assembler.addu(dest, dest, src); + } + + void add32(Imm32 imm, RegisterID dest) + { + add32(imm, dest, dest); + } + + void add32(Imm32 imm, RegisterID src, RegisterID dest) + { + if (!imm.m_isPointer && imm.m_value >= -32768 && imm.m_value <= 32767 + && !m_fixedWidth) { + /* + addiu dest, src, imm + */ + m_assembler.addiu(dest, src, imm.m_value); + } else { + /* + li immTemp, imm + addu dest, src, immTemp + */ + move(imm, immTempRegister); + m_assembler.addu(dest, src, immTempRegister); + } + } + + void add32(Imm32 imm, Address address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + lw dataTemp, offset(base) + li immTemp, imm + addu dataTemp, dataTemp, immTemp + sw dataTemp, offset(base) + */ + m_assembler.lw(dataTempRegister, address.base, address.offset); + if (!imm.m_isPointer + && imm.m_value >= -32768 && imm.m_value <= 32767 + && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, + imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.addu(dataTempRegister, dataTempRegister, + immTempRegister); + } + m_assembler.sw(dataTempRegister, address.base, address.offset); + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lw dataTemp, (offset & 0xffff)(addrTemp) + li immtemp, imm + addu dataTemp, dataTemp, immTemp + sw dataTemp, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dataTempRegister, addrTempRegister, address.offset); + + if (imm.m_value >= -32768 && imm.m_value <= 32767 && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, + imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.addu(dataTempRegister, dataTempRegister, + immTempRegister); + } + m_assembler.sw(dataTempRegister, addrTempRegister, address.offset); + } + } + + void add32(Address src, RegisterID dest) + { + load32(src, dataTempRegister); + add32(dataTempRegister, dest); + } + + void add32(RegisterID src, Address dest) + { + if (dest.offset >= -32768 && dest.offset <= 32767 && !m_fixedWidth) { + /* + lw dataTemp, offset(base) + addu dataTemp, dataTemp, src + sw dataTemp, offset(base) + */ + m_assembler.lw(dataTempRegister, dest.base, dest.offset); + m_assembler.addu(dataTempRegister, dataTempRegister, src); + m_assembler.sw(dataTempRegister, dest.base, dest.offset); + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lw dataTemp, (offset & 0xffff)(addrTemp) + addu dataTemp, dataTemp, src + sw dataTemp, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (dest.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, dest.base); + m_assembler.lw(dataTempRegister, addrTempRegister, dest.offset); + m_assembler.addu(dataTempRegister, dataTempRegister, src); + m_assembler.sw(dataTempRegister, addrTempRegister, dest.offset); + } + } + + void add32(Imm32 imm, AbsoluteAddress address) + { + /* + li addrTemp, address + li immTemp, imm + lw dataTemp, 0(addrTemp) + addu dataTemp, dataTemp, immTemp + sw dataTemp, 0(addrTemp) + */ + move(ImmPtr(address.m_ptr), addrTempRegister); + m_assembler.lw(dataTempRegister, addrTempRegister, 0); + if (!imm.m_isPointer && imm.m_value >= -32768 && imm.m_value <= 32767 + && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.addu(dataTempRegister, dataTempRegister, immTempRegister); + } + m_assembler.sw(dataTempRegister, addrTempRegister, 0); + } + + void and32(RegisterID src, RegisterID dest) + { + m_assembler.andInsn(dest, dest, src); + } + + void and32(Imm32 imm, RegisterID dest) + { + if (!imm.m_isPointer && !imm.m_value && !m_fixedWidth) + move(MIPSRegisters::zero, dest); + else if (!imm.m_isPointer && imm.m_value > 0 && imm.m_value < 65535 + && !m_fixedWidth) + m_assembler.andi(dest, dest, imm.m_value); + else { + /* + li immTemp, imm + and dest, dest, immTemp + */ + move(imm, immTempRegister); + m_assembler.andInsn(dest, dest, immTempRegister); + } + } + + void lshift32(Imm32 imm, RegisterID dest) + { + m_assembler.sll(dest, dest, imm.m_value); + } + + void lshift32(RegisterID shiftAmount, RegisterID dest) + { + m_assembler.sllv(dest, dest, shiftAmount); + } + + void mul32(RegisterID src, RegisterID dest) + { + m_assembler.mul(dest, dest, src); + } + + void mul32(Imm32 imm, RegisterID src, RegisterID dest) + { + if (!imm.m_isPointer && !imm.m_value && !m_fixedWidth) + move(MIPSRegisters::zero, dest); + else if (!imm.m_isPointer && imm.m_value == 1 && !m_fixedWidth) + move(src, dest); + else { + /* + li dataTemp, imm + mul dest, src, dataTemp + */ + move(imm, dataTempRegister); + m_assembler.mul(dest, src, dataTempRegister); + } + } + + void neg32(RegisterID srcDest) + { + m_assembler.subu(srcDest, MIPSRegisters::zero, srcDest); + } + + void not32(RegisterID srcDest) + { + m_assembler.nor(srcDest, srcDest, MIPSRegisters::zero); + } + + void or32(RegisterID src, RegisterID dest) + { + m_assembler.orInsn(dest, dest, src); + } + + void or32(Imm32 imm, RegisterID dest) + { + if (!imm.m_isPointer && !imm.m_value && !m_fixedWidth) + return; + + if (!imm.m_isPointer && imm.m_value > 0 && imm.m_value < 65535 + && !m_fixedWidth) { + m_assembler.ori(dest, dest, imm.m_value); + return; + } + + /* + li dataTemp, imm + or dest, dest, dataTemp + */ + move(imm, dataTempRegister); + m_assembler.orInsn(dest, dest, dataTempRegister); + } + + void rshift32(RegisterID shiftAmount, RegisterID dest) + { + m_assembler.srav(dest, dest, shiftAmount); + } + + void rshift32(Imm32 imm, RegisterID dest) + { + m_assembler.sra(dest, dest, imm.m_value); + } + + void urshift32(RegisterID shiftAmount, RegisterID dest) + { + m_assembler.srlv(dest, dest, shiftAmount); + } + + void urshift32(Imm32 imm, RegisterID dest) + { + m_assembler.srl(dest, dest, imm.m_value); + } + + void sub32(RegisterID src, RegisterID dest) + { + m_assembler.subu(dest, dest, src); + } + + void sub32(Imm32 imm, RegisterID dest) + { + if (!imm.m_isPointer && imm.m_value >= -32767 && imm.m_value <= 32768 + && !m_fixedWidth) { + /* + addiu dest, src, imm + */ + m_assembler.addiu(dest, dest, -imm.m_value); + } else { + /* + li immTemp, imm + subu dest, src, immTemp + */ + move(imm, immTempRegister); + m_assembler.subu(dest, dest, immTempRegister); + } + } + + void sub32(Imm32 imm, Address address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + lw dataTemp, offset(base) + li immTemp, imm + subu dataTemp, dataTemp, immTemp + sw dataTemp, offset(base) + */ + m_assembler.lw(dataTempRegister, address.base, address.offset); + if (!imm.m_isPointer + && imm.m_value >= -32767 && imm.m_value <= 32768 + && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, + -imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.subu(dataTempRegister, dataTempRegister, + immTempRegister); + } + m_assembler.sw(dataTempRegister, address.base, address.offset); + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lw dataTemp, (offset & 0xffff)(addrTemp) + li immtemp, imm + subu dataTemp, dataTemp, immTemp + sw dataTemp, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dataTempRegister, addrTempRegister, address.offset); + + if (!imm.m_isPointer + && imm.m_value >= -32767 && imm.m_value <= 32768 + && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, + -imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.subu(dataTempRegister, dataTempRegister, + immTempRegister); + } + m_assembler.sw(dataTempRegister, addrTempRegister, address.offset); + } + } + + void sub32(Address src, RegisterID dest) + { + load32(src, dataTempRegister); + sub32(dataTempRegister, dest); + } + + void sub32(Imm32 imm, AbsoluteAddress address) + { + /* + li addrTemp, address + li immTemp, imm + lw dataTemp, 0(addrTemp) + subu dataTemp, dataTemp, immTemp + sw dataTemp, 0(addrTemp) + */ + move(ImmPtr(address.m_ptr), addrTempRegister); + m_assembler.lw(dataTempRegister, addrTempRegister, 0); + + if (!imm.m_isPointer && imm.m_value >= -32767 && imm.m_value <= 32768 + && !m_fixedWidth) { + m_assembler.addiu(dataTempRegister, dataTempRegister, + -imm.m_value); + } else { + move(imm, immTempRegister); + m_assembler.subu(dataTempRegister, dataTempRegister, immTempRegister); + } + m_assembler.sw(dataTempRegister, addrTempRegister, 0); + } + + void xor32(RegisterID src, RegisterID dest) + { + m_assembler.xorInsn(dest, dest, src); + } + + void xor32(Imm32 imm, RegisterID dest) + { + /* + li immTemp, imm + xor dest, dest, immTemp + */ + move(imm, immTempRegister); + m_assembler.xorInsn(dest, dest, immTempRegister); + } + + void sqrtDouble(FPRegisterID src, FPRegisterID dst) + { + m_assembler.sqrtd(dst, src); + } + + // Memory access operations: + // + // Loads are of the form load(address, destination) and stores of the form + // store(source, address). The source for a store may be an Imm32. Address + // operand objects to loads and store will be implicitly constructed if a + // register is passed. + + /* Need to use zero-extened load byte for load8. */ + void load8(ImplicitAddress address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.lbu(dest, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lbu dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lbu(dest, addrTempRegister, address.offset); + } + } + + void load32(ImplicitAddress address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.lw(dest, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lw dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dest, addrTempRegister, address.offset); + } + } + + void load32(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lw dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lw dest, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, + immTempRegister); + m_assembler.lw(dest, addrTempRegister, address.offset); + } + } + + void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32764 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + (Big-Endian) + lwl dest, address.offset(addrTemp) + lwr dest, address.offset+3(addrTemp) + (Little-Endian) + lwl dest, address.offset+3(addrTemp) + lwr dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); +#if CPU(BIG_ENDIAN) + m_assembler.lwl(dest, addrTempRegister, address.offset); + m_assembler.lwr(dest, addrTempRegister, address.offset + 3); +#else + m_assembler.lwl(dest, addrTempRegister, address.offset + 3); + m_assembler.lwr(dest, addrTempRegister, address.offset); + +#endif + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, address.offset >> 16 + ori immTemp, immTemp, address.offset & 0xffff + addu addrTemp, addrTemp, immTemp + (Big-Endian) + lw dest, 0(at) + lw dest, 3(at) + (Little-Endian) + lw dest, 3(at) + lw dest, 0(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, address.offset >> 16); + m_assembler.ori(immTempRegister, immTempRegister, address.offset); + m_assembler.addu(addrTempRegister, addrTempRegister, + immTempRegister); +#if CPU(BIG_ENDIAN) + m_assembler.lwl(dest, addrTempRegister, 0); + m_assembler.lwr(dest, addrTempRegister, 3); +#else + m_assembler.lwl(dest, addrTempRegister, 3); + m_assembler.lwr(dest, addrTempRegister, 0); +#endif + } + } + + void load32(void* address, RegisterID dest) + { + /* + li addrTemp, address + lw dest, 0(addrTemp) + */ + move(ImmPtr(address), addrTempRegister); + m_assembler.lw(dest, addrTempRegister, 0); + } + + DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) + { + m_fixedWidth = true; + /* + lui addrTemp, address.offset >> 16 + ori addrTemp, addrTemp, address.offset & 0xffff + addu addrTemp, addrTemp, address.base + lw dest, 0(addrTemp) + */ + DataLabel32 dataLabel(this); + move(Imm32(address.offset), addrTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dest, addrTempRegister, 0); + m_fixedWidth = false; + return dataLabel; + } + + Label loadPtrWithPatchToLEA(Address address, RegisterID dest) + { + m_fixedWidth = true; + /* + lui addrTemp, address.offset >> 16 + ori addrTemp, addrTemp, address.offset & 0xffff + addu addrTemp, addrTemp, address.base + lw dest, 0(addrTemp) + */ + Label label(this); + move(Imm32(address.offset), addrTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dest, addrTempRegister, 0); + m_fixedWidth = false; + return label; + } + + /* Need to use zero-extened load half-word for load16. */ + void load16(ImplicitAddress address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.lhu(dest, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lhu dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lhu(dest, addrTempRegister, address.offset); + } + } + + /* Need to use zero-extened load half-word for load16. */ + void load16(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lhu dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lhu(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lhu dest, (address.offset & 0xffff)(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, + immTempRegister); + m_assembler.lhu(dest, addrTempRegister, address.offset); + } + } + + DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) + { + m_fixedWidth = true; + /* + lui addrTemp, address.offset >> 16 + ori addrTemp, addrTemp, address.offset & 0xffff + addu addrTemp, addrTemp, address.base + sw src, 0(addrTemp) + */ + DataLabel32 dataLabel(this); + move(Imm32(address.offset), addrTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sw(src, addrTempRegister, 0); + m_fixedWidth = false; + return dataLabel; + } + + void store32(RegisterID src, ImplicitAddress address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.sw(src, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + sw src, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sw(src, addrTempRegister, address.offset); + } + } + + void store32(RegisterID src, BaseIndex address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + sw src, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sw(src, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + sw src, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, + immTempRegister); + m_assembler.sw(src, addrTempRegister, address.offset); + } + } + + void store32(Imm32 imm, ImplicitAddress address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + if (!imm.m_isPointer && !imm.m_value) + m_assembler.sw(MIPSRegisters::zero, address.base, + address.offset); + else { + move(imm, immTempRegister); + m_assembler.sw(immTempRegister, address.base, address.offset); + } + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + sw immTemp, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + if (!imm.m_isPointer && !imm.m_value && !m_fixedWidth) + m_assembler.sw(MIPSRegisters::zero, addrTempRegister, + address.offset); + else { + move(imm, immTempRegister); + m_assembler.sw(immTempRegister, addrTempRegister, + address.offset); + } + } + } + + void store32(RegisterID src, void* address) + { + /* + li addrTemp, address + sw src, 0(addrTemp) + */ + move(ImmPtr(address), addrTempRegister); + m_assembler.sw(src, addrTempRegister, 0); + } + + void store32(Imm32 imm, void* address) + { + /* + li immTemp, imm + li addrTemp, address + sw src, 0(addrTemp) + */ + if (!imm.m_isPointer && !imm.m_value && !m_fixedWidth) { + move(ImmPtr(address), addrTempRegister); + m_assembler.sw(MIPSRegisters::zero, addrTempRegister, 0); + } else { + move(imm, immTempRegister); + move(ImmPtr(address), addrTempRegister); + m_assembler.sw(immTempRegister, addrTempRegister, 0); + } + } + + // Floating-point operations: + + bool supportsFloatingPoint() const + { +#if WTF_MIPS_DOUBLE_FLOAT + return true; +#else + return false; +#endif + } + + bool supportsFloatingPointTruncate() const + { +#if WTF_MIPS_DOUBLE_FLOAT && WTF_MIPS_ISA_AT_LEAST(2) + return true; +#else + return false; +#endif + } + + bool supportsFloatingPointSqrt() const + { +#if WTF_MIPS_DOUBLE_FLOAT && WTF_MIPS_ISA_AT_LEAST(2) + return true; +#else + return false; +#endif + } + + // Stack manipulation operations: + // + // The ABI is assumed to provide a stack abstraction to memory, + // containing machine word sized units of data. Push and pop + // operations add and remove a single register sized unit of data + // to or from the stack. Peek and poke operations read or write + // values on the stack, without moving the current stack position. + + void pop(RegisterID dest) + { + m_assembler.lw(dest, MIPSRegisters::sp, 0); + m_assembler.addiu(MIPSRegisters::sp, MIPSRegisters::sp, 4); + } + + void push(RegisterID src) + { + m_assembler.addiu(MIPSRegisters::sp, MIPSRegisters::sp, -4); + m_assembler.sw(src, MIPSRegisters::sp, 0); + } + + void push(Address address) + { + load32(address, dataTempRegister); + push(dataTempRegister); + } + + void push(Imm32 imm) + { + move(imm, immTempRegister); + push(immTempRegister); + } + + // Register move operations: + // + // Move values in registers. + + void move(Imm32 imm, RegisterID dest) + { + if (!imm.m_isPointer && !imm.m_value && !m_fixedWidth) + move(MIPSRegisters::zero, dest); + else if (imm.m_isPointer || m_fixedWidth) { + m_assembler.lui(dest, imm.m_value >> 16); + m_assembler.ori(dest, dest, imm.m_value); + } else + m_assembler.li(dest, imm.m_value); + } + + void move(RegisterID src, RegisterID dest) + { + if (src != dest || m_fixedWidth) + m_assembler.move(dest, src); + } + + void move(ImmPtr imm, RegisterID dest) + { + move(Imm32(imm), dest); + } + + void swap(RegisterID reg1, RegisterID reg2) + { + move(reg1, immTempRegister); + move(reg2, reg1); + move(immTempRegister, reg2); + } + + void signExtend32ToPtr(RegisterID src, RegisterID dest) + { + if (src != dest || m_fixedWidth) + move(src, dest); + } + + void zeroExtend32ToPtr(RegisterID src, RegisterID dest) + { + if (src != dest || m_fixedWidth) + move(src, dest); + } + + // Forwards / external control flow operations: + // + // This set of jump and conditional branch operations return a Jump + // object which may linked at a later point, allow forwards jump, + // or jumps that will require external linkage (after the code has been + // relocated). + // + // For branches, signed <, >, <= and >= are denoted as l, g, le, and ge + // respecitvely, for unsigned comparisons the names b, a, be, and ae are + // used (representing the names 'below' and 'above'). + // + // Operands to the comparision are provided in the expected order, e.g. + // jle32(reg1, Imm32(5)) will branch if the value held in reg1, when + // treated as a signed 32bit value, is less than or equal to 5. + // + // jz and jnz test whether the first operand is equal to zero, and take + // an optional second operand of a mask under which to perform the test. + + Jump branch8(Condition cond, Address left, Imm32 right) + { + // Make sure the immediate value is unsigned 8 bits. + ASSERT(!(right.m_value & 0xFFFFFF00)); + load8(left, dataTempRegister); + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branch32(Condition cond, RegisterID left, RegisterID right) + { + if (cond == Equal || cond == Zero) + return branchEqual(left, right); + if (cond == NotEqual || cond == NonZero) + return branchNotEqual(left, right); + if (cond == Above) { + m_assembler.sltu(cmpTempRegister, right, left); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == AboveOrEqual) { + m_assembler.sltu(cmpTempRegister, left, right); + return branchEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Below) { + m_assembler.sltu(cmpTempRegister, left, right); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == BelowOrEqual) { + m_assembler.sltu(cmpTempRegister, right, left); + return branchEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == GreaterThan) { + m_assembler.slt(cmpTempRegister, right, left); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == GreaterThanOrEqual) { + m_assembler.slt(cmpTempRegister, left, right); + return branchEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == LessThan) { + m_assembler.slt(cmpTempRegister, left, right); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == LessThanOrEqual) { + m_assembler.slt(cmpTempRegister, right, left); + return branchEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Overflow) { + /* + xor cmpTemp, left, right + bgez No_overflow, cmpTemp # same sign bit -> no overflow + nop + subu cmpTemp, left, right + xor cmpTemp, cmpTemp, left + bgez No_overflow, cmpTemp # same sign bit -> no overflow + nop + b Overflow + nop + nop + nop + nop + nop + No_overflow: + */ + m_assembler.xorInsn(cmpTempRegister, left, right); + m_assembler.bgez(cmpTempRegister, 11); + m_assembler.nop(); + m_assembler.subu(cmpTempRegister, left, right); + m_assembler.xorInsn(cmpTempRegister, cmpTempRegister, left); + m_assembler.bgez(cmpTempRegister, 7); + m_assembler.nop(); + return jump(); + } + if (cond == Signed) { + m_assembler.subu(cmpTempRegister, left, right); + // Check if the result is negative. + m_assembler.slt(cmpTempRegister, cmpTempRegister, + MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + ASSERT(0); + + return Jump(); + } + + Jump branch32(Condition cond, RegisterID left, Imm32 right) + { + move(right, immTempRegister); + return branch32(cond, left, immTempRegister); + } + + Jump branch32(Condition cond, RegisterID left, Address right) + { + load32(right, dataTempRegister); + return branch32(cond, left, dataTempRegister); + } + + Jump branch32(Condition cond, Address left, RegisterID right) + { + load32(left, dataTempRegister); + return branch32(cond, dataTempRegister, right); + } + + Jump branch32(Condition cond, Address left, Imm32 right) + { + load32(left, dataTempRegister); + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branch32(Condition cond, BaseIndex left, Imm32 right) + { + load32(left, dataTempRegister); + // Be careful that the previous load32() uses immTempRegister. + // So, we need to put move() after load32(). + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branch32WithUnalignedHalfWords(Condition cond, BaseIndex left, Imm32 right) + { + load32WithUnalignedHalfWords(left, dataTempRegister); + // Be careful that the previous load32WithUnalignedHalfWords() + // uses immTempRegister. + // So, we need to put move() after load32WithUnalignedHalfWords(). + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branch32(Condition cond, AbsoluteAddress left, RegisterID right) + { + load32(left.m_ptr, dataTempRegister); + return branch32(cond, dataTempRegister, right); + } + + Jump branch32(Condition cond, AbsoluteAddress left, Imm32 right) + { + load32(left.m_ptr, dataTempRegister); + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branch16(Condition cond, BaseIndex left, RegisterID right) + { + load16(left, dataTempRegister); + return branch32(cond, dataTempRegister, right); + } + + Jump branch16(Condition cond, BaseIndex left, Imm32 right) + { + ASSERT(!(right.m_value & 0xFFFF0000)); + load16(left, dataTempRegister); + // Be careful that the previous load16() uses immTempRegister. + // So, we need to put move() after load16(). + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branchTest32(Condition cond, RegisterID reg, RegisterID mask) + { + ASSERT((cond == Zero) || (cond == NonZero)); + m_assembler.andInsn(cmpTempRegister, reg, mask); + if (cond == Zero) + return branchEqual(cmpTempRegister, MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + + Jump branchTest32(Condition cond, RegisterID reg, Imm32 mask = Imm32(-1)) + { + ASSERT((cond == Zero) || (cond == NonZero)); + if (mask.m_value == -1 && !m_fixedWidth) { + if (cond == Zero) + return branchEqual(reg, MIPSRegisters::zero); + return branchNotEqual(reg, MIPSRegisters::zero); + } + move(mask, immTempRegister); + return branchTest32(cond, reg, immTempRegister); + } + + Jump branchTest32(Condition cond, Address address, Imm32 mask = Imm32(-1)) + { + load32(address, dataTempRegister); + return branchTest32(cond, dataTempRegister, mask); + } + + Jump branchTest32(Condition cond, BaseIndex address, Imm32 mask = Imm32(-1)) + { + load32(address, dataTempRegister); + return branchTest32(cond, dataTempRegister, mask); + } + + Jump branchTest8(Condition cond, Address address, Imm32 mask = Imm32(-1)) + { + load8(address, dataTempRegister); + return branchTest32(cond, dataTempRegister, mask); + } + + Jump jump() + { + return branchEqual(MIPSRegisters::zero, MIPSRegisters::zero); + } + + void jump(RegisterID target) + { + m_assembler.jr(target); + m_assembler.nop(); + } + + void jump(Address address) + { + m_fixedWidth = true; + load32(address, MIPSRegisters::t9); + m_assembler.jr(MIPSRegisters::t9); + m_assembler.nop(); + m_fixedWidth = false; + } + + // Arithmetic control flow operations: + // + // This set of conditional branch operations branch based + // on the result of an arithmetic operation. The operation + // is performed as normal, storing the result. + // + // * jz operations branch if the result is zero. + // * jo operations branch if the (signed) arithmetic + // operation caused an overflow to occur. + + Jump branchAdd32(Condition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + if (cond == Overflow) { + /* + move dest, dataTemp + xor cmpTemp, dataTemp, src + bltz cmpTemp, No_overflow # diff sign bit -> no overflow + addu dest, dataTemp, src + xor cmpTemp, dest, dataTemp + bgez cmpTemp, No_overflow # same sign big -> no overflow + nop + b Overflow + nop + nop + nop + nop + nop + No_overflow: + */ + move(dest, dataTempRegister); + m_assembler.xorInsn(cmpTempRegister, dataTempRegister, src); + m_assembler.bltz(cmpTempRegister, 10); + m_assembler.addu(dest, dataTempRegister, src); + m_assembler.xorInsn(cmpTempRegister, dest, dataTempRegister); + m_assembler.bgez(cmpTempRegister, 7); + m_assembler.nop(); + return jump(); + } + if (cond == Signed) { + add32(src, dest); + // Check if dest is negative. + m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Zero) { + add32(src, dest); + return branchEqual(dest, MIPSRegisters::zero); + } + if (cond == NonZero) { + add32(src, dest); + return branchNotEqual(dest, MIPSRegisters::zero); + } + ASSERT(0); + return Jump(); + } + + Jump branchAdd32(Condition cond, Imm32 imm, RegisterID dest) + { + move(imm, immTempRegister); + return branchAdd32(cond, immTempRegister, dest); + } + + Jump branchMul32(Condition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + if (cond == Overflow) { + /* + mult src, dest + mfhi dataTemp + mflo dest + sra addrTemp, dest, 31 + beq dataTemp, addrTemp, No_overflow # all sign bits (bit 63 to bit 31) are the same -> no overflow + nop + b Overflow + nop + nop + nop + nop + nop + No_overflow: + */ + m_assembler.mult(src, dest); + m_assembler.mfhi(dataTempRegister); + m_assembler.mflo(dest); + m_assembler.sra(addrTempRegister, dest, 31); + m_assembler.beq(dataTempRegister, addrTempRegister, 7); + m_assembler.nop(); + return jump(); + } + if (cond == Signed) { + mul32(src, dest); + // Check if dest is negative. + m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Zero) { + mul32(src, dest); + return branchEqual(dest, MIPSRegisters::zero); + } + if (cond == NonZero) { + mul32(src, dest); + return branchNotEqual(dest, MIPSRegisters::zero); + } + ASSERT(0); + return Jump(); + } + + Jump branchMul32(Condition cond, Imm32 imm, RegisterID src, RegisterID dest) + { + move(imm, immTempRegister); + move(src, dest); + return branchMul32(cond, immTempRegister, dest); + } + + Jump branchSub32(Condition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + if (cond == Overflow) { + /* + move dest, dataTemp + xor cmpTemp, dataTemp, src + bgez cmpTemp, No_overflow # same sign bit -> no overflow + subu dest, dataTemp, src + xor cmpTemp, dest, dataTemp + bgez cmpTemp, No_overflow # same sign bit -> no overflow + nop + b Overflow + nop + nop + nop + nop + nop + No_overflow: + */ + move(dest, dataTempRegister); + m_assembler.xorInsn(cmpTempRegister, dataTempRegister, src); + m_assembler.bgez(cmpTempRegister, 10); + m_assembler.subu(dest, dataTempRegister, src); + m_assembler.xorInsn(cmpTempRegister, dest, dataTempRegister); + m_assembler.bgez(cmpTempRegister, 7); + m_assembler.nop(); + return jump(); + } + if (cond == Signed) { + sub32(src, dest); + // Check if dest is negative. + m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Zero) { + sub32(src, dest); + return branchEqual(dest, MIPSRegisters::zero); + } + if (cond == NonZero) { + sub32(src, dest); + return branchNotEqual(dest, MIPSRegisters::zero); + } + ASSERT(0); + return Jump(); + } + + Jump branchSub32(Condition cond, Imm32 imm, RegisterID dest) + { + move(imm, immTempRegister); + return branchSub32(cond, immTempRegister, dest); + } + + Jump branchOr32(Condition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Signed) || (cond == Zero) || (cond == NonZero)); + if (cond == Signed) { + or32(src, dest); + // Check if dest is negative. + m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Zero) { + or32(src, dest); + return branchEqual(dest, MIPSRegisters::zero); + } + if (cond == NonZero) { + or32(src, dest); + return branchNotEqual(dest, MIPSRegisters::zero); + } + ASSERT(0); + return Jump(); + } + + // Miscellaneous operations: + + void breakpoint() + { + m_assembler.bkpt(); + } + + Call nearCall() + { + /* We need two words for relaxation. */ + m_assembler.nop(); + m_assembler.nop(); + m_assembler.jal(); + m_assembler.nop(); + return Call(m_assembler.newJmpSrc(), Call::LinkableNear); + } + + Call call() + { + m_assembler.lui(MIPSRegisters::t9, 0); + m_assembler.ori(MIPSRegisters::t9, MIPSRegisters::t9, 0); + m_assembler.jalr(MIPSRegisters::t9); + m_assembler.nop(); + return Call(m_assembler.newJmpSrc(), Call::Linkable); + } + + Call call(RegisterID target) + { + m_assembler.jalr(target); + m_assembler.nop(); + return Call(m_assembler.newJmpSrc(), Call::None); + } + + Call call(Address address) + { + m_fixedWidth = true; + load32(address, MIPSRegisters::t9); + m_assembler.jalr(MIPSRegisters::t9); + m_assembler.nop(); + m_fixedWidth = false; + return Call(m_assembler.newJmpSrc(), Call::None); + } + + void ret() + { + m_assembler.jr(MIPSRegisters::ra); + m_assembler.nop(); + } + + void set8(Condition cond, RegisterID left, RegisterID right, RegisterID dest) + { + set32(cond, left, right, dest); + } + + void set8(Condition cond, RegisterID left, Imm32 right, RegisterID dest) + { + move(right, immTempRegister); + set32(cond, left, immTempRegister, dest); + } + + void set32(Condition cond, RegisterID left, RegisterID right, RegisterID dest) + { + if (cond == Equal || cond == Zero) { + m_assembler.xorInsn(dest, left, right); + m_assembler.sltiu(dest, dest, 1); + } else if (cond == NotEqual || cond == NonZero) { + m_assembler.xorInsn(dest, left, right); + m_assembler.sltu(dest, MIPSRegisters::zero, dest); + } else if (cond == Above) + m_assembler.sltu(dest, right, left); + else if (cond == AboveOrEqual) { + m_assembler.sltu(dest, left, right); + m_assembler.xori(dest, dest, 1); + } else if (cond == Below) + m_assembler.sltu(dest, left, right); + else if (cond == BelowOrEqual) { + m_assembler.sltu(dest, right, left); + m_assembler.xori(dest, dest, 1); + } else if (cond == GreaterThan) + m_assembler.slt(dest, right, left); + else if (cond == GreaterThanOrEqual) { + m_assembler.slt(dest, left, right); + m_assembler.xori(dest, dest, 1); + } else if (cond == LessThan) + m_assembler.slt(dest, left, right); + else if (cond == LessThanOrEqual) { + m_assembler.slt(dest, right, left); + m_assembler.xori(dest, dest, 1); + } else if (cond == Overflow) { + /* + xor cmpTemp, left, right + bgez Done, cmpTemp # same sign bit -> no overflow + move dest, 0 + subu cmpTemp, left, right + xor cmpTemp, cmpTemp, left # diff sign bit -> overflow + slt dest, cmpTemp, 0 + Done: + */ + m_assembler.xorInsn(cmpTempRegister, left, right); + m_assembler.bgez(cmpTempRegister, 4); + m_assembler.move(dest, MIPSRegisters::zero); + m_assembler.subu(cmpTempRegister, left, right); + m_assembler.xorInsn(cmpTempRegister, cmpTempRegister, left); + m_assembler.slt(dest, cmpTempRegister, MIPSRegisters::zero); + } else if (cond == Signed) { + m_assembler.subu(dest, left, right); + // Check if the result is negative. + m_assembler.slt(dest, dest, MIPSRegisters::zero); + } + } + + void set32(Condition cond, RegisterID left, Imm32 right, RegisterID dest) + { + move(right, immTempRegister); + set32(cond, left, immTempRegister, dest); + } + + void setTest8(Condition cond, Address address, Imm32 mask, RegisterID dest) + { + ASSERT((cond == Zero) || (cond == NonZero)); + load8(address, dataTempRegister); + if (mask.m_value == -1 && !m_fixedWidth) { + if (cond == Zero) + m_assembler.sltiu(dest, dataTempRegister, 1); + else + m_assembler.sltu(dest, MIPSRegisters::zero, dataTempRegister); + } else { + move(mask, immTempRegister); + m_assembler.andInsn(cmpTempRegister, dataTempRegister, + immTempRegister); + if (cond == Zero) + m_assembler.sltiu(dest, cmpTempRegister, 1); + else + m_assembler.sltu(dest, MIPSRegisters::zero, cmpTempRegister); + } + } + + void setTest32(Condition cond, Address address, Imm32 mask, RegisterID dest) + { + ASSERT((cond == Zero) || (cond == NonZero)); + load32(address, dataTempRegister); + if (mask.m_value == -1 && !m_fixedWidth) { + if (cond == Zero) + m_assembler.sltiu(dest, dataTempRegister, 1); + else + m_assembler.sltu(dest, MIPSRegisters::zero, dataTempRegister); + } else { + move(mask, immTempRegister); + m_assembler.andInsn(cmpTempRegister, dataTempRegister, + immTempRegister); + if (cond == Zero) + m_assembler.sltiu(dest, cmpTempRegister, 1); + else + m_assembler.sltu(dest, MIPSRegisters::zero, cmpTempRegister); + } + } + + DataLabel32 moveWithPatch(Imm32 imm, RegisterID dest) + { + m_fixedWidth = true; + DataLabel32 label(this); + move(imm, dest); + m_fixedWidth = false; + return label; + } + + DataLabelPtr moveWithPatch(ImmPtr initialValue, RegisterID dest) + { + m_fixedWidth = true; + DataLabelPtr label(this); + move(initialValue, dest); + m_fixedWidth = false; + return label; + } + + Jump branchPtrWithPatch(Condition cond, RegisterID left, DataLabelPtr& dataLabel, ImmPtr initialRightValue = ImmPtr(0)) + { + m_fixedWidth = true; + dataLabel = moveWithPatch(initialRightValue, immTempRegister); + Jump temp = branch32(cond, left, immTempRegister); + m_fixedWidth = false; + return temp; + } + + Jump branchPtrWithPatch(Condition cond, Address left, DataLabelPtr& dataLabel, ImmPtr initialRightValue = ImmPtr(0)) + { + m_fixedWidth = true; + load32(left, dataTempRegister); + dataLabel = moveWithPatch(initialRightValue, immTempRegister); + Jump temp = branch32(cond, dataTempRegister, immTempRegister); + m_fixedWidth = false; + return temp; + } + + DataLabelPtr storePtrWithPatch(ImmPtr initialValue, ImplicitAddress address) + { + m_fixedWidth = true; + DataLabelPtr dataLabel = moveWithPatch(initialValue, dataTempRegister); + store32(dataTempRegister, address); + m_fixedWidth = false; + return dataLabel; + } + + DataLabelPtr storePtrWithPatch(ImplicitAddress address) + { + return storePtrWithPatch(ImmPtr(0), address); + } + + Call tailRecursiveCall() + { + // Like a normal call, but don't update the returned address register + m_fixedWidth = true; + move(Imm32(0), MIPSRegisters::t9); + m_assembler.jr(MIPSRegisters::t9); + m_assembler.nop(); + m_fixedWidth = false; + return Call(m_assembler.newJmpSrc(), Call::Linkable); + } + + Call makeTailRecursiveCall(Jump oldJump) + { + oldJump.link(this); + return tailRecursiveCall(); + } + + void loadDouble(ImplicitAddress address, FPRegisterID dest) + { +#if WTF_MIPS_ISA(1) + /* + li addrTemp, address.offset + addu addrTemp, addrTemp, base + lwc1 dest, 0(addrTemp) + lwc1 dest+1, 4(addrTemp) + */ + move(Imm32(address.offset), addrTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lwc1(dest, addrTempRegister, 0); + m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, 4); +#else + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + m_assembler.ldc1(dest, address.base, address.offset); + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + ldc1 dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.ldc1(dest, addrTempRegister, address.offset); + } +#endif + } + + void loadDouble(const void* address, FPRegisterID dest) + { +#if WTF_MIPS_ISA(1) + /* + li addrTemp, address + lwc1 dest, 0(addrTemp) + lwc1 dest+1, 4(addrTemp) + */ + move(ImmPtr(address), addrTempRegister); + m_assembler.lwc1(dest, addrTempRegister, 0); + m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, 4); +#else + /* + li addrTemp, address + ldc1 dest, 0(addrTemp) + */ + move(ImmPtr(address), addrTempRegister); + m_assembler.ldc1(dest, addrTempRegister, 0); +#endif + } + + + void storeDouble(FPRegisterID src, ImplicitAddress address) + { +#if WTF_MIPS_ISA(1) + /* + li addrTemp, address.offset + addu addrTemp, addrTemp, base + swc1 dest, 0(addrTemp) + swc1 dest+1, 4(addrTemp) + */ + move(Imm32(address.offset), addrTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.swc1(src, addrTempRegister, 0); + m_assembler.swc1(FPRegisterID(src + 1), addrTempRegister, 4); +#else + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.sdc1(src, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + sdc1 src, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sdc1(src, addrTempRegister, address.offset); + } +#endif + } + + void addDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.addd(dest, dest, src); + } + + void addDouble(Address src, FPRegisterID dest) + { + loadDouble(src, fpTempRegister); + m_assembler.addd(dest, dest, fpTempRegister); + } + + void subDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.subd(dest, dest, src); + } + + void subDouble(Address src, FPRegisterID dest) + { + loadDouble(src, fpTempRegister); + m_assembler.subd(dest, dest, fpTempRegister); + } + + void mulDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.muld(dest, dest, src); + } + + void mulDouble(Address src, FPRegisterID dest) + { + loadDouble(src, fpTempRegister); + m_assembler.muld(dest, dest, fpTempRegister); + } + + void divDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.divd(dest, dest, src); + } + + void convertInt32ToDouble(RegisterID src, FPRegisterID dest) + { + m_assembler.mtc1(src, fpTempRegister); + m_assembler.cvtdw(dest, fpTempRegister); + } + + void convertInt32ToDouble(Address src, FPRegisterID dest) + { + load32(src, dataTempRegister); + m_assembler.mtc1(dataTempRegister, fpTempRegister); + m_assembler.cvtdw(dest, fpTempRegister); + } + + void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest) + { + load32(src.m_ptr, dataTempRegister); + m_assembler.mtc1(dataTempRegister, fpTempRegister); + m_assembler.cvtdw(dest, fpTempRegister); + } + + void insertRelaxationWords() + { + /* We need four words for relaxation. */ + m_assembler.beq(MIPSRegisters::zero, MIPSRegisters::zero, 3); // Jump over nops; + m_assembler.nop(); + m_assembler.nop(); + m_assembler.nop(); + } + + Jump branchTrue() + { + m_assembler.appendJump(); + m_assembler.bc1t(); + m_assembler.nop(); + insertRelaxationWords(); + return Jump(m_assembler.newJmpSrc()); + } + + Jump branchFalse() + { + m_assembler.appendJump(); + m_assembler.bc1f(); + m_assembler.nop(); + insertRelaxationWords(); + return Jump(m_assembler.newJmpSrc()); + } + + Jump branchEqual(RegisterID rs, RegisterID rt) + { + m_assembler.appendJump(); + m_assembler.beq(rs, rt, 0); + m_assembler.nop(); + insertRelaxationWords(); + return Jump(m_assembler.newJmpSrc()); + } + + Jump branchNotEqual(RegisterID rs, RegisterID rt) + { + m_assembler.appendJump(); + m_assembler.bne(rs, rt, 0); + m_assembler.nop(); + insertRelaxationWords(); + return Jump(m_assembler.newJmpSrc()); + } + + Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) + { + if (cond == DoubleEqual) { + m_assembler.ceqd(left, right); + return branchTrue(); + } + if (cond == DoubleNotEqual) { + m_assembler.cueqd(left, right); + return branchFalse(); // false + } + if (cond == DoubleGreaterThan) { + m_assembler.cngtd(left, right); + return branchFalse(); // false + } + if (cond == DoubleGreaterThanOrEqual) { + m_assembler.cnged(left, right); + return branchFalse(); // false + } + if (cond == DoubleLessThan) { + m_assembler.cltd(left, right); + return branchTrue(); + } + if (cond == DoubleLessThanOrEqual) { + m_assembler.cled(left, right); + return branchTrue(); + } + if (cond == DoubleEqualOrUnordered) { + m_assembler.cueqd(left, right); + return branchTrue(); + } + if (cond == DoubleNotEqualOrUnordered) { + m_assembler.ceqd(left, right); + return branchFalse(); // false + } + if (cond == DoubleGreaterThanOrUnordered) { + m_assembler.coled(left, right); + return branchFalse(); // false + } + if (cond == DoubleGreaterThanOrEqualOrUnordered) { + m_assembler.coltd(left, right); + return branchFalse(); // false + } + if (cond == DoubleLessThanOrUnordered) { + m_assembler.cultd(left, right); + return branchTrue(); + } + if (cond == DoubleLessThanOrEqualOrUnordered) { + m_assembler.culed(left, right); + return branchTrue(); + } + ASSERT(0); + + return Jump(); + } + + // Truncates 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, INT_MAX 0x7fffffff). + Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest) + { + m_assembler.truncwd(fpTempRegister, src); + m_assembler.mfc1(dest, fpTempRegister); + return branch32(Equal, dest, Imm32(0x7fffffff)); + } + + // Convert 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, 0). + void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) + { + m_assembler.cvtwd(fpTempRegister, src); + m_assembler.mfc1(dest, fpTempRegister); + + // If the result is zero, it might have been -0.0, and the double comparison won't catch this! + failureCases.append(branch32(Equal, dest, MIPSRegisters::zero)); + + // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. + convertInt32ToDouble(dest, fpTemp); + failureCases.append(branchDouble(DoubleNotEqualOrUnordered, fpTemp, src)); + } + + void zeroDouble(FPRegisterID dest) + { +#if WTF_MIPS_ISA_REV(2) && WTF_MIPS_FP64 + m_assembler.mtc1(MIPSRegisters::zero, dest); + m_assembler.mthc1(MIPSRegisters::zero, dest); +#else + m_assembler.mtc1(MIPSRegisters::zero, dest); + m_assembler.mtc1(MIPSRegisters::zero, FPRegisterID(dest + 1)); +#endif + } + +private: + // If m_fixedWidth is true, we will generate a fixed number of instructions. + // Otherwise, we can emit any number of instructions. + bool m_fixedWidth; + + friend class LinkBuffer; + friend class RepatchBuffer; + + static void linkCall(void* code, Call call, FunctionPtr function) + { + MIPSAssembler::linkCall(code, call.m_jmp, function.value()); + } + + static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) + { + MIPSAssembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } + + static void repatchCall(CodeLocationCall call, FunctionPtr destination) + { + MIPSAssembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } + +}; + +} + +#endif // ENABLE(ASSEMBLER) && CPU(MIPS) + +#endif // MacroAssemblerMIPS_h diff --git a/JavaScriptCore/assembler/MacroAssemblerX86.h b/JavaScriptCore/assembler/MacroAssemblerX86.h index ca7c31a..0918996 100644 --- a/JavaScriptCore/assembler/MacroAssemblerX86.h +++ b/JavaScriptCore/assembler/MacroAssemblerX86.h @@ -26,8 +26,6 @@ #ifndef MacroAssemblerX86_h #define MacroAssemblerX86_h -#include <wtf/Platform.h> - #if ENABLE(ASSEMBLER) && CPU(X86) #include "MacroAssemblerX86Common.h" @@ -89,7 +87,7 @@ public: m_assembler.movl_mr(address, dest); } - void loadDouble(void* address, FPRegisterID dest) + void loadDouble(const void* address, FPRegisterID dest) { ASSERT(isSSE2Present()); m_assembler.movsd_mr(address, dest); @@ -174,6 +172,7 @@ public: bool supportsFloatingPoint() const { return m_isSSE2Present; } // See comment on MacroAssemblerARMv7::supportsFloatingPointTruncate() bool supportsFloatingPointTruncate() const { return m_isSSE2Present; } + bool supportsFloatingPointSqrt() const { return m_isSSE2Present; } private: const bool m_isSSE2Present; diff --git a/JavaScriptCore/assembler/MacroAssemblerX86Common.h b/JavaScriptCore/assembler/MacroAssemblerX86Common.h index 449df86..cb86da7 100644 --- a/JavaScriptCore/assembler/MacroAssemblerX86Common.h +++ b/JavaScriptCore/assembler/MacroAssemblerX86Common.h @@ -26,8 +26,6 @@ #ifndef MacroAssemblerX86Common_h #define MacroAssemblerX86Common_h -#include <wtf/Platform.h> - #if ENABLE(ASSEMBLER) #include "X86Assembler.h" @@ -41,6 +39,7 @@ class MacroAssemblerX86Common : public AbstractMacroAssembler<X86Assembler> { static const int DoubleConditionBits = DoubleConditionBitInvert | DoubleConditionBitSpecial; public: + typedef X86Assembler::FPRegisterID FPRegisterID; enum Condition { Equal = X86Assembler::ConditionE, @@ -251,6 +250,33 @@ public: { m_assembler.sarl_i8r(imm.m_value, dest); } + + void urshift32(RegisterID shift_amount, RegisterID dest) + { + // On x86 we can only shift by ecx; if asked to shift by another register we'll + // need rejig the shift amount into ecx first, and restore the registers afterwards. + if (shift_amount != X86Registers::ecx) { + swap(shift_amount, X86Registers::ecx); + + // E.g. transform "shrl %eax, %eax" -> "xchgl %eax, %ecx; shrl %ecx, %ecx; xchgl %eax, %ecx" + if (dest == shift_amount) + m_assembler.shrl_CLr(X86Registers::ecx); + // E.g. transform "shrl %eax, %ecx" -> "xchgl %eax, %ecx; shrl %ecx, %eax; xchgl %eax, %ecx" + else if (dest == X86Registers::ecx) + m_assembler.shrl_CLr(shift_amount); + // E.g. transform "shrl %eax, %ebx" -> "xchgl %eax, %ecx; shrl %ecx, %ebx; xchgl %eax, %ecx" + else + m_assembler.shrl_CLr(dest); + + swap(shift_amount, X86Registers::ecx); + } else + m_assembler.shrl_CLr(dest); + } + + void urshift32(Imm32 imm, RegisterID dest) + { + m_assembler.shrl_i8r(imm.m_value, dest); + } void sub32(RegisterID src, RegisterID dest) { @@ -303,6 +329,10 @@ public: m_assembler.xorl_mr(src.offset, src.base, dest); } + void sqrtDouble(FPRegisterID src, FPRegisterID dst) + { + m_assembler.sqrtsd_rr(src, dst); + } // Memory access operations: // @@ -336,6 +366,11 @@ public: { m_assembler.movzwl_mr(address.offset, address.base, address.index, address.scale, dest); } + + void load16(Address address, RegisterID dest) + { + m_assembler.movzwl_mr(address.offset, address.base, dest); + } DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) { @@ -620,6 +655,12 @@ public: // an optional second operand of a mask under which to perform the test. public: + Jump branch8(Condition cond, Address left, Imm32 right) + { + m_assembler.cmpb_im(right.m_value, left.offset, left.base); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + Jump branch32(Condition cond, RegisterID left, RegisterID right) { m_assembler.cmpl_rr(right, left); @@ -717,6 +758,26 @@ public: m_assembler.testl_i32m(mask.m_value, address.offset, address.base, address.index, address.scale); return Jump(m_assembler.jCC(x86Condition(cond))); } + + Jump branchTest8(Condition cond, Address address, Imm32 mask = Imm32(-1)) + { + ASSERT((cond == Zero) || (cond == NonZero)); + if (mask.m_value == -1) + m_assembler.cmpb_im(0, address.offset, address.base); + else + m_assembler.testb_im(mask.m_value, address.offset, address.base); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchTest8(Condition cond, BaseIndex address, Imm32 mask = Imm32(-1)) + { + ASSERT((cond == Zero) || (cond == NonZero)); + if (mask.m_value == -1) + m_assembler.cmpb_im(0, address.offset, address.base, address.index, address.scale); + else + m_assembler.testb_im(mask.m_value, address.offset, address.base, address.index, address.scale); + return Jump(m_assembler.jCC(x86Condition(cond))); + } Jump jump() { @@ -836,6 +897,13 @@ public: return Jump(m_assembler.jCC(x86Condition(cond))); } + Jump branchNeg32(Condition cond, RegisterID srcDest) + { + ASSERT((cond == Overflow) || (cond == Zero) || (cond == NonZero)); + neg32(srcDest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + Jump branchOr32(Condition cond, RegisterID src, RegisterID dest) { ASSERT((cond == Signed) || (cond == Zero) || (cond == NonZero)); @@ -917,10 +985,11 @@ public: void setTest8(Condition cond, Address address, Imm32 mask, RegisterID dest) { if (mask.m_value == -1) - m_assembler.cmpl_im(0, address.offset, address.base); + m_assembler.cmpb_im(0, address.offset, address.base); else - m_assembler.testl_i32m(mask.m_value, address.offset, address.base); + m_assembler.testb_im(mask.m_value, address.offset, address.base); m_assembler.setCC_r(x86Condition(cond), dest); + m_assembler.movzbl_rr(dest, dest); } void setTest32(Condition cond, Address address, Imm32 mask, RegisterID dest) diff --git a/JavaScriptCore/assembler/MacroAssemblerX86_64.h b/JavaScriptCore/assembler/MacroAssemblerX86_64.h index ec93f8c..168c93f 100644 --- a/JavaScriptCore/assembler/MacroAssemblerX86_64.h +++ b/JavaScriptCore/assembler/MacroAssemblerX86_64.h @@ -26,8 +26,6 @@ #ifndef MacroAssemblerX86_64_h #define MacroAssemblerX86_64_h -#include <wtf/Platform.h> - #if ENABLE(ASSEMBLER) && CPU(X86_64) #include "MacroAssemblerX86Common.h" @@ -88,7 +86,7 @@ public: } } - void loadDouble(void* address, FPRegisterID dest) + void loadDouble(const void* address, FPRegisterID dest) { move(ImmPtr(address), scratchRegister); loadDouble(scratchRegister, dest); @@ -411,6 +409,14 @@ public: return label; } + using MacroAssemblerX86Common::branchTest8; + Jump branchTest8(Condition cond, ExtendedAddress address, Imm32 mask = Imm32(-1)) + { + ImmPtr addr(reinterpret_cast<void*>(address.offset)); + MacroAssemblerX86Common::move(addr, scratchRegister); + return MacroAssemblerX86Common::branchTest8(cond, BaseIndex(scratchRegister, address.base, TimesOne), mask); + } + Label loadPtrWithPatchToLEA(Address address, RegisterID dest) { Label label(this); @@ -421,6 +427,7 @@ public: bool supportsFloatingPoint() const { return true; } // See comment on MacroAssemblerARMv7::supportsFloatingPointTruncate() bool supportsFloatingPointTruncate() const { return true; } + bool supportsFloatingPointSqrt() const { return true; } private: friend class LinkBuffer; diff --git a/JavaScriptCore/assembler/RepatchBuffer.h b/JavaScriptCore/assembler/RepatchBuffer.h index 89cbf06..72cf6b2 100644 --- a/JavaScriptCore/assembler/RepatchBuffer.h +++ b/JavaScriptCore/assembler/RepatchBuffer.h @@ -26,8 +26,6 @@ #ifndef RepatchBuffer_h #define RepatchBuffer_h -#include <wtf/Platform.h> - #if ENABLE(ASSEMBLER) #include <MacroAssembler.h> diff --git a/JavaScriptCore/assembler/X86Assembler.h b/JavaScriptCore/assembler/X86Assembler.h index ab3d05f..20d72f5 100644 --- a/JavaScriptCore/assembler/X86Assembler.h +++ b/JavaScriptCore/assembler/X86Assembler.h @@ -26,8 +26,6 @@ #ifndef X86Assembler_h #define X86Assembler_h -#include <wtf/Platform.h> - #if ENABLE(ASSEMBLER) && (CPU(X86) || CPU(X86_64)) #include "AssemblerBuffer.h" @@ -130,6 +128,7 @@ private: PRE_SSE_66 = 0x66, OP_PUSH_Iz = 0x68, OP_IMUL_GvEvIz = 0x69, + OP_GROUP1_EbIb = 0x80, OP_GROUP1_EvIz = 0x81, OP_GROUP1_EvIb = 0x83, OP_TEST_EvGv = 0x85, @@ -168,6 +167,7 @@ private: OP2_MULSD_VsdWsd = 0x59, OP2_SUBSD_VsdWsd = 0x5C, OP2_DIVSD_VsdWsd = 0x5E, + OP2_SQRTSD_VsdWsd = 0x51, OP2_XORPD_VpdWpd = 0x57, OP2_MOVD_VdEd = 0x6E, OP2_MOVD_EdVd = 0x7E, @@ -201,6 +201,7 @@ private: GROUP1A_OP_POP = 0, GROUP2_OP_SHL = 4, + GROUP2_OP_SHR = 5, GROUP2_OP_SAR = 7, GROUP3_OP_TEST = 0, @@ -673,6 +674,21 @@ public: { m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SAR, dst); } + + void shrl_i8r(int imm, RegisterID dst) + { + if (imm == 1) + m_formatter.oneByteOp(OP_GROUP2_Ev1, GROUP2_OP_SHR, dst); + else { + m_formatter.oneByteOp(OP_GROUP2_EvIb, GROUP2_OP_SHR, dst); + m_formatter.immediate8(imm); + } + } + + void shrl_CLr(RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SHR, dst); + } void shll_i8r(int imm, RegisterID dst) { @@ -760,7 +776,7 @@ public: m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst); m_formatter.immediate32(imm); } - + void cmpl_im(int imm, int offset, RegisterID base) { if (CAN_SIGN_EXTEND_8_32(imm)) { @@ -771,6 +787,18 @@ public: m_formatter.immediate32(imm); } } + + void cmpb_im(int imm, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP1_EbIb, GROUP1_OP_CMP, base, offset); + m_formatter.immediate8(imm); + } + + void cmpb_im(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.oneByteOp(OP_GROUP1_EbIb, GROUP1_OP_CMP, base, index, scale, offset); + m_formatter.immediate8(imm); + } void cmpl_im(int imm, int offset, RegisterID base, RegisterID index, int scale) { @@ -890,6 +918,18 @@ public: m_formatter.oneByteOp(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, offset); m_formatter.immediate32(imm); } + + void testb_im(int imm, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP3_EbIb, GROUP3_OP_TEST, base, offset); + m_formatter.immediate8(imm); + } + + void testb_im(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.oneByteOp(OP_GROUP3_EbIb, GROUP3_OP_TEST, base, index, scale, offset); + m_formatter.immediate8(imm); + } void testl_i32m(int imm, int offset, RegisterID base, RegisterID index, int scale) { @@ -1370,7 +1410,7 @@ public: } #if !CPU(X86_64) - void movsd_mr(void* address, XMMRegisterID dst) + void movsd_mr(const void* address, XMMRegisterID dst) { m_formatter.prefix(PRE_SSE_F2); m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, address); @@ -1438,6 +1478,12 @@ public: m_formatter.twoByteOp(OP2_XORPD_VpdWpd, (RegisterID)dst, (RegisterID)src); } + void sqrtsd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_SQRTSD_VsdWsd, (RegisterID)dst, (RegisterID)src); + } + // Misc instructions: void int3() @@ -1723,7 +1769,7 @@ private: } #if !CPU(X86_64) - void twoByteOp(TwoByteOpcodeID opcode, int reg, void* address) + void twoByteOp(TwoByteOpcodeID opcode, int reg, const void* address) { m_buffer.ensureSpace(maxInstructionSize); m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); @@ -2034,7 +2080,7 @@ private: } #if !CPU(X86_64) - void memoryModRM(int reg, void* address) + void memoryModRM(int reg, const void* address) { // noBase + ModRmMemoryNoDisp means noBase + ModRmMemoryDisp32! putModRm(ModRmMemoryNoDisp, reg, noBase); |
