diff options
Diffstat (limited to 'JavaScriptCore/assembler/ARMv7Assembler.h')
-rw-r--r-- | JavaScriptCore/assembler/ARMv7Assembler.h | 836 |
1 files changed, 585 insertions, 251 deletions
diff --git a/JavaScriptCore/assembler/ARMv7Assembler.h b/JavaScriptCore/assembler/ARMv7Assembler.h index 4e394b2..f1b57b8 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) 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, @@ -398,18 +445,6 @@ private: } 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,7 +453,9 @@ 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) @@ -438,14 +475,44 @@ public: ConditionGT, ConditionLE, ConditionAL, - + ConditionCS = ConditionHS, ConditionCC = ConditionLO, } Condition; + enum JumpType { JumpNoCondition, JumpCondition, JumpFullSize }; + enum JumpLinkType { LinkInvalid, LinkShortJump, LinkConditionalShortJump, LinkLongJump, JumpTypeCount }; + static const int JumpSizes[JumpTypeCount]; + enum { JumpPaddingSize = 5 * sizeof(uint16_t) }; + 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 : 2; + JumpLinkType m_linkType : 3; + Condition m_condition : 16; + }; + class JmpSrc { friend class ARMv7Assembler; friend class ARMInstructionFormatter; + friend class LinkBuffer; public: JmpSrc() : m_offset(-1) @@ -453,17 +520,32 @@ public: } private: - JmpSrc(int offset) + JmpSrc(int offset, JumpType type) + : m_offset(offset) + , m_condition(0xffff) + , m_type(type) + { + ASSERT(m_type != JumpCondition); + } + + JmpSrc(int offset, JumpType type, Condition condition) : m_offset(offset) + , m_condition(condition) + , m_type(type) { + ASSERT(m_type == JumpCondition || m_type == JumpFullSize); } 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 +569,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) + uint32_t singleRegisterMask(FPSingleRegisterID rdNum, int highBitsShift, int lowBitShift) { - // 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) - { - 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 +593,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 +611,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, @@ -602,6 +633,7 @@ private: 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 +645,18 @@ 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_T4a = 0xF000, OP_AND_imm_T1 = 0xF000, OP_TST_imm = 0xF010, @@ -626,17 +670,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, @@ -648,8 +696,20 @@ private: } 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_T4b = 0x9000, } OpcodeID2; struct FourFours { @@ -712,7 +772,7 @@ private: } public: - + void add(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) { // Rd can only be SP if Rn is also SP. @@ -780,10 +840,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 +865,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 +911,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()); + 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(), type); } void bkpt(uint8_t imm=0) @@ -1080,6 +1146,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 +1281,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 +1313,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 +1447,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 +1492,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 +1517,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 +1545,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(OP_VADD_T2, OP_VADD_T2b, true, rn, rd, rm); + } + + void vcmp_F64(FPDoubleRegisterID rd, FPDoubleRegisterID rm) { - m_formatter.vfpOp(0x0b00ee30 | doubleRegisterMask(rd, 6, 28) | doubleRegisterMask(rn, 23, 0) | doubleRegisterMask(rm, 21, 16)); + m_formatter.vfpOp(OP_VCMP_T1, OP_VCMP_T1b, true, VFPOperand(4), rd, rm); } - void vcmp_F64(FPRegisterID rd, FPRegisterID rm) + void vcvt_F64_S32(FPDoubleRegisterID rd, FPSingleRegisterID rm) { - m_formatter.vfpOp(0x0bc0eeb4 | doubleRegisterMask(rd, 6, 28) | doubleRegisterMask(rm, 21, 16)); + // 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_F64_S32(FPRegisterID fd, FPRegisterID sm) + void vcvtr_S32_F64(FPSingleRegisterID rd, FPDoubleRegisterID 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(true, false, true), rd, rm); } - void vcvt_S32_F64(FPRegisterID sd, FPRegisterID fm) + void vdiv_F64(FPDoubleRegisterID rd, FPDoubleRegisterID rn, FPDoubleRegisterID rm) { - m_formatter.vfpOp(0x0bc0eebd | singleRegisterMask(sd, 28, 6) | doubleRegisterMask(fm, 21, 16)); + m_formatter.vfpOp(OP_VDIV, OP_VDIVb, true, rn, rd, rm); } - void vldr(FPRegisterID rd, RegisterID rn, int32_t imm) + void vldr(FPDoubleRegisterID rd, RegisterID rn, int32_t imm) { - vmem(rd, rn, imm, true); + m_formatter.vfpMemOp(OP_VLDR, OP_VLDRb, true, rn, rd, imm); } - void vmov(RegisterID rd, FPRegisterID sn) + void vmov_F64_0(FPDoubleRegisterID rd) { - m_formatter.vfpOp(0x0a10ee10 | (rd << 28) | singleRegisterMask(sn, 0, 23)); + m_formatter.vfpOp(OP_VMOV_IMM_T2, OP_VMOV_IMM_T2b, true, VFPOperand(0), rd, VFPOperand(0)); } - void vmov(FPRegisterID sn, RegisterID rd) + void vmov(RegisterID rd, FPSingleRegisterID rn) { - m_formatter.vfpOp(0x0a10ee00 | (rd << 28) | singleRegisterMask(sn, 0, 23)); + ASSERT(!BadReg(rd)); + m_formatter.vfpOp(OP_VMOV_CtoS, OP_VMOV_CtoSb, false, rn, rd, VFPOperand(0)); } - // move FPSCR flags to APSR. - void vmrs_APSR_nzcv_FPSCR() + void vmov(FPSingleRegisterID rd, RegisterID rn) { - m_formatter.vfpOp(0xfa10eef1); + ASSERT(!BadReg(rn)); + m_formatter.vfpOp(OP_VMOV_StoC, OP_VMOV_StoCb, false, rd, rn, VFPOperand(0)); } - void vmul_F64(FPRegisterID rd, FPRegisterID rn, FPRegisterID rm) + void vmrs(RegisterID reg = ARMRegisters::pc) { - m_formatter.vfpOp(0x0b00ee20 | doubleRegisterMask(rd, 6, 28) | doubleRegisterMask(rn, 23, 0) | doubleRegisterMask(rm, 21, 16)); + ASSERT(reg != ARMRegisters::sp); + m_formatter.vfpOp(OP_VMRS, OP_VMRSb, false, VFPOperand(1), VFPOperand(0x10 | reg), VFPOperand(0)); } - void vstr(FPRegisterID rd, RegisterID rn, int32_t imm) + void vmul_F64(FPDoubleRegisterID rd, FPDoubleRegisterID rn, FPDoubleRegisterID rm) { - vmem(rd, rn, imm, false); + m_formatter.vfpOp(OP_VMUL_T2, OP_VMUL_T2b, true, rn, rd, rm); } - void vsub_F64(FPRegisterID rd, FPRegisterID rn, FPRegisterID rm) + void vstr(FPDoubleRegisterID rd, RegisterID rn, int32_t imm) { - m_formatter.vfpOp(0x0b40ee30 | doubleRegisterMask(rd, 6, 28) | doubleRegisterMask(rn, 23, 0) | doubleRegisterMask(rm, 21, 16)); + 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 +1656,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(JumpLinkType jumpLinkType) { return JumpPaddingSize - JumpSizes[jumpLinkType]; } // Assembler admin methods: @@ -1502,22 +1673,66 @@ 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); + JumpLinkType computeJumpType(LinkRecord& record, const uint8_t* from, const uint8_t* to) + { + if (record.type() >= JumpFullSize) { + record.setLinkType(LinkLongJump); + return LinkLongJump; + } + bool mayTriggerErrata = false; + const uint16_t* shortJumpLocation = reinterpret_cast<const uint16_t*>(from - (JumpPaddingSize - JumpSizes[LinkShortJump])); + if (!canBeShortJump(shortJumpLocation, to, mayTriggerErrata)) { + record.setLinkType(LinkLongJump); + return LinkLongJump; + } + if (mayTriggerErrata) { + record.setLinkType(LinkLongJump); + return LinkLongJump; } - m_jumpsToLink.clear(); + if (record.type() == JumpCondition) { + record.setLinkType(LinkConditionalShortJump); + return LinkConditionalShortJump; + } + record.setLinkType(LinkShortJump); + return LinkShortJump; + } + + 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; + } - ASSERT(copy); - return copy; + void link(LinkRecord& record, uint8_t* from, uint8_t* to) + { + uint16_t* itttLocation; + if (record.linkType() == LinkConditionalShortJump) { + itttLocation = reinterpret_cast<uint16_t*>(from - JumpSizes[LinkConditionalShortJump] - 2); + itttLocation[0] = ifThenElse(record.condition()) | OP_IT; + } + ASSERT(record.linkType() != LinkInvalid); + if (record.linkType() != LinkLongJump) + linkShortJump(reinterpret_cast<uint16_t*>(from), to); + else + linkLongJump(reinterpret_cast<uint16_t*>(from), to); } + void* unlinkedCode() { return m_formatter.data(); } + static unsigned getCallReturnOffset(JmpSrc call) { ASSERT(call.m_offset >= 0); @@ -1536,7 +1751,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) @@ -1604,36 +1819,83 @@ public: 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) + { } - // 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; + 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. + { + } + + uint32_t bits1() + { + return m_value >> 4; + } + + 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); - m_formatter.vfpOp(0x0b00ed00 | offset | (up << 7) | (isLoad << 4) | doubleRegisterMask(rd, 6, 28) | rn); + 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,19 +1954,64 @@ private: return (instruction[0] == OP_NOP_T2a) && (instruction[1] == OP_NOP_T2b); } + static bool canBeShortJump(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; + } + + static void linkLongJump(uint16_t* instruction, void* target) + { + linkJumpAbsolute(instruction, target); + } + + static void linkShortJump(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)); + + intptr_t relative = reinterpret_cast<intptr_t>(target) - (reinterpret_cast<intptr_t>(instruction)); + bool scratch; + UNUSED_PARAM(scratch); + ASSERT(canBeShortJump(instruction, target, scratch)); + // 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); + } + static void linkJumpAbsolute(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)) ); + ASSERT((isMOV_imm_T3(instruction - 5) && isMOVT(instruction - 3) && isBX(instruction - 1)) + || (isNOP_T1(instruction - 5) && isNOP_T2(instruction - 4) && isB(instruction - 2))); intptr_t relative = reinterpret_cast<intptr_t>(target) - (reinterpret_cast<intptr_t>(instruction)); - if (((relative << 7) >> 7) == relative) { + bool scratch; + if (canBeShortJump(instruction, target, scratch)) { // ARM encoding for the top two bits below the sign bit is 'peculiar'. if (relative >= 0) relative ^= 0xC00000; @@ -1722,6 +2029,7 @@ private: 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); } 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); @@ -1736,6 +2044,7 @@ private: { 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 +2119,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 +2161,7 @@ private: } m_formatter; Vector<LinkRecord> m_jumpsToLink; + Vector<int32_t> m_offsets; }; } // namespace JSC |