| /* |
| * Copyright (C) 2012 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #ifndef ARM64Assembler_h |
| #define ARM64Assembler_h |
| |
| #if ENABLE(ASSEMBLER) && CPU(ARM64) |
| |
| #include "AssemblerBuffer.h" |
| #include <wtf/Assertions.h> |
| #include <wtf/Vector.h> |
| #include <stdint.h> |
| |
| #define CHECK_DATASIZE_OF(datasize) ASSERT(datasize == 32 || datasize == 64) |
| #define DATASIZE_OF(datasize) ((datasize == 64) ? Datasize_64 : Datasize_32) |
| #define MEMOPSIZE_OF(datasize) ((datasize == 8 || datasize == 128) ? MemOpSize_8_or_128 : (datasize == 16) ? MemOpSize_16 : (datasize == 32) ? MemOpSize_32 : MemOpSize_64) |
| #define CHECK_DATASIZE() CHECK_DATASIZE_OF(datasize) |
| #define DATASIZE DATASIZE_OF(datasize) |
| #define MEMOPSIZE MEMOPSIZE_OF(datasize) |
| #define CHECK_FP_MEMOP_DATASIZE() ASSERT(datasize == 8 || datasize == 16 || datasize == 32 || datasize == 64 || datasize == 128) |
| |
| namespace JSC { |
| |
| ALWAYS_INLINE bool isInt9(int32_t value) |
| { |
| return value == ((value << 23) >> 23); |
| } |
| |
| ALWAYS_INLINE bool isUInt5(int32_t value) |
| { |
| return !(value & ~0x1f); |
| } |
| |
| ALWAYS_INLINE bool isUInt12(int32_t value) |
| { |
| return !(value & ~0xfff); |
| } |
| |
| ALWAYS_INLINE bool isUInt12(intptr_t value) |
| { |
| return !(value & ~0xfffL); |
| } |
| |
| class UInt5 { |
| public: |
| explicit UInt5(int value) |
| : m_value(value) |
| { |
| ASSERT(isUInt5(value)); |
| } |
| |
| operator int() { return m_value; } |
| |
| private: |
| int m_value; |
| }; |
| |
| class UInt12 { |
| public: |
| explicit UInt12(int value) |
| : m_value(value) |
| { |
| ASSERT(isUInt12(value)); |
| } |
| |
| operator int() { return m_value; } |
| |
| private: |
| int m_value; |
| }; |
| |
| class PostIndex { |
| public: |
| explicit PostIndex(int value) |
| : m_value(value) |
| { |
| ASSERT(isInt9(value)); |
| } |
| |
| operator int() { return m_value; } |
| |
| private: |
| int m_value; |
| }; |
| |
| class PreIndex { |
| public: |
| explicit PreIndex(int value) |
| : m_value(value) |
| { |
| ASSERT(isInt9(value)); |
| } |
| |
| operator int() { return m_value; } |
| |
| private: |
| int m_value; |
| }; |
| |
| class LogicalImmediate { |
| public: |
| static LogicalImmediate create32(uint32_t value) |
| { |
| // Check for 0, -1 - these cannot be encoded. |
| if (!value || !~value) |
| return InvalidLogicalImmediate; |
| |
| // First look for a 32-bit pattern, then for repeating 16-bit |
| // patterns, 8-bit, 4-bit, and finally 2-bit. |
| |
| unsigned hsb, lsb; |
| bool inverted; |
| if (findBitRange<32>(value, hsb, lsb, inverted)) |
| return encodeLogicalImmediate<32>(hsb, lsb, inverted); |
| |
| if ((value & 0xffff) != (value >> 16)) |
| return InvalidLogicalImmediate; |
| value &= 0xffff; |
| |
| if (findBitRange<16>(value, hsb, lsb, inverted)) |
| return encodeLogicalImmediate<16>(hsb, lsb, inverted); |
| |
| if ((value & 0xff) != (value >> 8)) |
| return InvalidLogicalImmediate; |
| value &= 0xff; |
| |
| if (findBitRange<8>(value, hsb, lsb, inverted)) |
| return encodeLogicalImmediate<8>(hsb, lsb, inverted); |
| |
| if ((value & 0xf) != (value >> 4)) |
| return InvalidLogicalImmediate; |
| value &= 0xf; |
| |
| if (findBitRange<4>(value, hsb, lsb, inverted)) |
| return encodeLogicalImmediate<4>(hsb, lsb, inverted); |
| |
| if ((value & 0x3) != (value >> 2)) |
| return InvalidLogicalImmediate; |
| value &= 0x3; |
| |
| if (findBitRange<2>(value, hsb, lsb, inverted)) |
| return encodeLogicalImmediate<2>(hsb, lsb, inverted); |
| |
| return InvalidLogicalImmediate; |
| } |
| |
| static LogicalImmediate create64(uint64_t value) |
| { |
| // Check for 0, -1 - these cannot be encoded. |
| if (!value || !~value) |
| return InvalidLogicalImmediate; |
| |
| // Look for a contiguous bit range. |
| unsigned hsb, lsb; |
| bool inverted; |
| if (findBitRange<64>(value, hsb, lsb, inverted)) |
| return encodeLogicalImmediate<64>(hsb, lsb, inverted); |
| |
| // If the high & low 32 bits are equal, we can try for a 32-bit (or narrower) pattern. |
| if (static_cast<uint32_t>(value) == static_cast<uint32_t>(value >> 32)) |
| return create32(static_cast<uint32_t>(value)); |
| return InvalidLogicalImmediate; |
| } |
| |
| int value() const |
| { |
| ASSERT(isValid()); |
| return m_value; |
| } |
| |
| bool isValid() const |
| { |
| return m_value != InvalidLogicalImmediate; |
| } |
| |
| bool is64bit() const |
| { |
| return m_value & (1 << 12); |
| } |
| |
| private: |
| LogicalImmediate(int value) |
| : m_value(value) |
| { |
| } |
| |
| // Generate a mask with bits in the range hsb..0 set, for example: |
| // hsb:63 = 0xffffffffffffffff |
| // hsb:42 = 0x000007ffffffffff |
| // hsb: 0 = 0x0000000000000001 |
| static uint64_t mask(unsigned hsb) |
| { |
| ASSERT(hsb < 64); |
| return 0xffffffffffffffffull >> (63 - hsb); |
| } |
| |
| template<unsigned N> |
| static void partialHSB(uint64_t& value, unsigned&result) |
| { |
| if (value & (0xffffffffffffffffull << N)) { |
| result += N; |
| value >>= N; |
| } |
| } |
| |
| // Find the bit number of the highest bit set in a non-zero value, for example: |
| // 0x8080808080808080 = hsb:63 |
| // 0x0000000000000001 = hsb: 0 |
| // 0x000007ffffe00000 = hsb:42 |
| static unsigned highestSetBit(uint64_t value) |
| { |
| ASSERT(value); |
| unsigned hsb = 0; |
| partialHSB<32>(value, hsb); |
| partialHSB<16>(value, hsb); |
| partialHSB<8>(value, hsb); |
| partialHSB<4>(value, hsb); |
| partialHSB<2>(value, hsb); |
| partialHSB<1>(value, hsb); |
| return hsb; |
| } |
| |
| // This function takes a value and a bit width, where value obeys the following constraints: |
| // * bits outside of the width of the value must be zero. |
| // * bits within the width of value must neither be all clear or all set. |
| // The input is inspected to detect values that consist of either two or three contiguous |
| // ranges of bits. The output range hsb..lsb will describe the second range of the value. |
| // if the range is set, inverted will be false, and if the range is clear, inverted will |
| // be true. For example (with width 8): |
| // 00001111 = hsb:3, lsb:0, inverted:false |
| // 11110000 = hsb:3, lsb:0, inverted:true |
| // 00111100 = hsb:5, lsb:2, inverted:false |
| // 11000011 = hsb:5, lsb:2, inverted:true |
| template<unsigned width> |
| static bool findBitRange(uint64_t value, unsigned& hsb, unsigned& lsb, bool& inverted) |
| { |
| ASSERT(value & mask(width - 1)); |
| ASSERT(value != mask(width - 1)); |
| ASSERT(!(value & ~mask(width - 1))); |
| |
| // Detect cases where the top bit is set; if so, flip all the bits & set invert. |
| // This halves the number of patterns we need to look for. |
| const uint64_t msb = 1ull << (width - 1); |
| if ((inverted = (value & msb))) |
| value ^= mask(width - 1); |
| |
| // Find the highest set bit in value, generate a corresponding mask & flip all |
| // bits under it. |
| hsb = highestSetBit(value); |
| value ^= mask(hsb); |
| if (!value) { |
| // If this cleared the value, then the range hsb..0 was all set. |
| lsb = 0; |
| return true; |
| } |
| |
| // Try making one more mask, and flipping the bits! |
| lsb = highestSetBit(value); |
| value ^= mask(lsb); |
| if (!value) { |
| // Success - but lsb actually points to the hsb of a third range - add one |
| // to get to the lsb of the mid range. |
| ++lsb; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // Encodes the set of immN:immr:imms fields found in a logical immediate. |
| template<unsigned width> |
| static int encodeLogicalImmediate(unsigned hsb, unsigned lsb, bool inverted) |
| { |
| // Check width is a power of 2! |
| ASSERT(!(width & (width -1))); |
| ASSERT(width <= 64 && width >= 2); |
| ASSERT(hsb >= lsb); |
| ASSERT(hsb < width); |
| |
| int immN = 0; |
| int imms = 0; |
| int immr = 0; |
| |
| // For 64-bit values this is easy - just set immN to true, and imms just |
| // contains the bit number of the highest set bit of the set range. For |
| // values with narrower widths, these are encoded by a leading set of |
| // one bits, followed by a zero bit, followed by the remaining set of bits |
| // being the high bit of the range. For a 32-bit immediate there are no |
| // leading one bits, just a zero followed by a five bit number. For a |
| // 16-bit immediate there is one one bit, a zero bit, and then a four bit |
| // bit-position, etc. |
| if (width == 64) |
| immN = 1; |
| else |
| imms = 63 & ~(width + width - 1); |
| |
| if (inverted) { |
| // if width is 64 & hsb is 62, then we have a value something like: |
| // 0x80000000ffffffff (in this case with lsb 32). |
| // The ror should be by 1, imms (effectively set width minus 1) is |
| // 32. Set width is full width minus cleared width. |
| immr = (width - 1) - hsb; |
| imms |= (width - ((hsb - lsb) + 1)) - 1; |
| } else { |
| // if width is 64 & hsb is 62, then we have a value something like: |
| // 0x7fffffff00000000 (in this case with lsb 32). |
| // The value is effectively rol'ed by lsb, which is equivalent to |
| // a ror by width - lsb (or 0, in the case where lsb is 0). imms |
| // is hsb - lsb. |
| immr = (width - lsb) & (width - 1); |
| imms |= hsb - lsb; |
| } |
| |
| return immN << 12 | immr << 6 | imms; |
| } |
| |
| static const int InvalidLogicalImmediate = -1; |
| |
| int m_value; |
| }; |
| |
| inline uint16_t getHalfword(uint64_t value, int which) |
| { |
| return value >> (which << 4); |
| } |
| |
| namespace ARM64Registers { |
| typedef enum { |
| // Parameter/result registers |
| x0, |
| x1, |
| x2, |
| x3, |
| x4, |
| x5, |
| x6, |
| x7, |
| // Indirect result location register |
| x8, |
| // Temporary registers |
| x9, |
| x10, |
| x11, |
| x12, |
| x13, |
| x14, |
| x15, |
| // Intra-procedure-call scratch registers (temporary) |
| x16, ip0 = x16, |
| x17, ip1 = x17, |
| // Platform Register (temporary) |
| x18, |
| // Callee-saved |
| x19, |
| x20, |
| x21, |
| x22, |
| x23, |
| x24, |
| x25, |
| x26, |
| x27, |
| x28, |
| // Special |
| x29, fp = x29, |
| x30, lr = x30, |
| sp, |
| zr = 0x3f, |
| } RegisterID; |
| |
| typedef enum { |
| // Parameter/result registers |
| q0, |
| q1, |
| q2, |
| q3, |
| q4, |
| q5, |
| q6, |
| q7, |
| // Callee-saved (up to 64-bits only!) |
| q8, |
| q9, |
| q10, |
| q11, |
| q12, |
| q13, |
| q14, |
| q15, |
| // Temporary registers |
| q16, |
| q17, |
| q18, |
| q19, |
| q20, |
| q21, |
| q22, |
| q23, |
| q24, |
| q25, |
| q26, |
| q27, |
| q28, |
| q29, |
| q30, |
| q31, |
| } FPRegisterID; |
| |
| static bool isSp(RegisterID reg) { return reg == sp; } |
| static bool isZr(RegisterID reg) { return reg == zr; } |
| } |
| |
| class ARM64Assembler { |
| public: |
| typedef ARM64Registers::RegisterID RegisterID; |
| typedef ARM64Registers::FPRegisterID FPRegisterID; |
| |
| static RegisterID firstRegister() { return ARM64Registers::x0; } |
| static RegisterID lastRegister() { return ARM64Registers::x28; } |
| |
| static FPRegisterID firstFPRegister() { return ARM64Registers::q0; } |
| static FPRegisterID lastFPRegister() { return ARM64Registers::q31; } |
| |
| private: |
| static bool isSp(RegisterID reg) { return ARM64Registers::isSp(reg); } |
| static bool isZr(RegisterID reg) { return ARM64Registers::isZr(reg); } |
| |
| public: |
| ARM64Assembler() |
| : m_indexOfLastWatchpoint(INT_MIN) |
| , m_indexOfTailOfLastWatchpoint(INT_MIN) |
| { |
| } |
| |
| AssemblerBuffer& buffer() { return m_buffer; } |
| |
| // (HS, LO, HI, LS) -> (AE, B, A, BE) |
| // (VS, VC) -> (O, NO) |
| typedef enum { |
| ConditionEQ, |
| ConditionNE, |
| ConditionHS, ConditionCS = ConditionHS, |
| ConditionLO, ConditionCC = ConditionLO, |
| ConditionMI, |
| ConditionPL, |
| ConditionVS, |
| ConditionVC, |
| ConditionHI, |
| ConditionLS, |
| ConditionGE, |
| ConditionLT, |
| ConditionGT, |
| ConditionLE, |
| ConditionAL, |
| ConditionInvalid |
| } Condition; |
| |
| static Condition invert(Condition cond) |
| { |
| return static_cast<Condition>(cond ^ 1); |
| } |
| |
| typedef enum { |
| LSL, |
| LSR, |
| ASR, |
| ROR |
| } ShiftType; |
| |
| typedef enum { |
| UXTB, |
| UXTH, |
| UXTW, |
| UXTX, |
| SXTB, |
| SXTH, |
| SXTW, |
| SXTX |
| } ExtendType; |
| |
| enum SetFlags { |
| DontSetFlags, |
| S |
| }; |
| |
| #define JUMP_ENUM_WITH_SIZE(index, value) (((value) << 4) | (index)) |
| #define JUMP_ENUM_SIZE(jump) ((jump) >> 4) |
| enum JumpType { JumpFixed = JUMP_ENUM_WITH_SIZE(0, 0), |
| JumpNoCondition = JUMP_ENUM_WITH_SIZE(1, 1 * sizeof(uint32_t)), |
| JumpCondition = JUMP_ENUM_WITH_SIZE(2, 2 * sizeof(uint32_t)), |
| JumpCompareAndBranch = JUMP_ENUM_WITH_SIZE(3, 2 * sizeof(uint32_t)), |
| JumpTestBit = JUMP_ENUM_WITH_SIZE(4, 2 * sizeof(uint32_t)), |
| JumpNoConditionFixedSize = JUMP_ENUM_WITH_SIZE(5, 1 * sizeof(uint32_t)), |
| JumpConditionFixedSize = JUMP_ENUM_WITH_SIZE(6, 2 * sizeof(uint32_t)), |
| JumpCompareAndBranchFixedSize = JUMP_ENUM_WITH_SIZE(7, 2 * sizeof(uint32_t)), |
| JumpTestBitFixedSize = JUMP_ENUM_WITH_SIZE(8, 2 * sizeof(uint32_t)), |
| }; |
| enum JumpLinkType { |
| LinkInvalid = JUMP_ENUM_WITH_SIZE(0, 0), |
| LinkJumpNoCondition = JUMP_ENUM_WITH_SIZE(1, 1 * sizeof(uint32_t)), |
| LinkJumpConditionDirect = JUMP_ENUM_WITH_SIZE(2, 1 * sizeof(uint32_t)), |
| LinkJumpCondition = JUMP_ENUM_WITH_SIZE(3, 2 * sizeof(uint32_t)), |
| LinkJumpCompareAndBranch = JUMP_ENUM_WITH_SIZE(4, 2 * sizeof(uint32_t)), |
| LinkJumpCompareAndBranchDirect = JUMP_ENUM_WITH_SIZE(5, 1 * sizeof(uint32_t)), |
| LinkJumpTestBit = JUMP_ENUM_WITH_SIZE(6, 2 * sizeof(uint32_t)), |
| LinkJumpTestBitDirect = JUMP_ENUM_WITH_SIZE(7, 1 * sizeof(uint32_t)), |
| }; |
| |
| class LinkRecord { |
| public: |
| LinkRecord(intptr_t from, intptr_t to, JumpType type, Condition condition) |
| { |
| data.realTypes.m_from = from; |
| data.realTypes.m_to = to; |
| data.realTypes.m_type = type; |
| data.realTypes.m_linkType = LinkInvalid; |
| data.realTypes.m_condition = condition; |
| } |
| LinkRecord(intptr_t from, intptr_t to, JumpType type, Condition condition, bool is64Bit, RegisterID compareRegister) |
| { |
| data.realTypes.m_from = from; |
| data.realTypes.m_to = to; |
| data.realTypes.m_type = type; |
| data.realTypes.m_linkType = LinkInvalid; |
| data.realTypes.m_condition = condition; |
| data.realTypes.m_is64Bit = is64Bit; |
| data.realTypes.m_compareRegister = compareRegister; |
| } |
| LinkRecord(intptr_t from, intptr_t to, JumpType type, Condition condition, unsigned bitNumber, RegisterID compareRegister) |
| { |
| data.realTypes.m_from = from; |
| data.realTypes.m_to = to; |
| data.realTypes.m_type = type; |
| data.realTypes.m_linkType = LinkInvalid; |
| data.realTypes.m_condition = condition; |
| data.realTypes.m_bitNumber = bitNumber; |
| data.realTypes.m_compareRegister = compareRegister; |
| } |
| void operator=(const LinkRecord& other) |
| { |
| data.copyTypes.content[0] = other.data.copyTypes.content[0]; |
| data.copyTypes.content[1] = other.data.copyTypes.content[1]; |
| data.copyTypes.content[2] = other.data.copyTypes.content[2]; |
| } |
| intptr_t from() const { return data.realTypes.m_from; } |
| void setFrom(intptr_t from) { data.realTypes.m_from = from; } |
| intptr_t to() const { return data.realTypes.m_to; } |
| JumpType type() const { return data.realTypes.m_type; } |
| JumpLinkType linkType() const { return data.realTypes.m_linkType; } |
| void setLinkType(JumpLinkType linkType) { ASSERT(data.realTypes.m_linkType == LinkInvalid); data.realTypes.m_linkType = linkType; } |
| Condition condition() const { return data.realTypes.m_condition; } |
| bool is64Bit() const { return data.realTypes.m_is64Bit; } |
| unsigned bitNumber() const { return data.realTypes.m_bitNumber; } |
| RegisterID compareRegister() const { return data.realTypes.m_compareRegister; } |
| |
| private: |
| union { |
| struct RealTypes { |
| intptr_t m_from : 48; |
| intptr_t m_to : 48; |
| JumpType m_type : 8; |
| JumpLinkType m_linkType : 8; |
| Condition m_condition : 4; |
| bool m_is64Bit : 1; |
| unsigned m_bitNumber : 6; |
| RegisterID m_compareRegister : 5; |
| } realTypes; |
| struct CopyTypes { |
| uint64_t content[3]; |
| } copyTypes; |
| COMPILE_ASSERT(sizeof(RealTypes) == sizeof(CopyTypes), LinkRecordCopyStructSizeEqualsRealStruct); |
| } data; |
| }; |
| |
| // bits(N) VFPExpandImm(bits(8) imm8); |
| // |
| // Encoding of floating point immediates is a litte complicated. Here's a |
| // high level description: |
| // +/-m*2-n where m and n are integers, 16 <= m <= 31, 0 <= n <= 7 |
| // and the algirithm for expanding to a single precision float: |
| // return imm8<7>:NOT(imm8<6>):Replicate(imm8<6>,5):imm8<5:0>:Zeros(19); |
| // |
| // The trickiest bit is how the exponent is handled. The following table |
| // may help clarify things a little: |
| // 654 |
| // 100 01111100 124 -3 1020 01111111100 |
| // 101 01111101 125 -2 1021 01111111101 |
| // 110 01111110 126 -1 1022 01111111110 |
| // 111 01111111 127 0 1023 01111111111 |
| // 000 10000000 128 1 1024 10000000000 |
| // 001 10000001 129 2 1025 10000000001 |
| // 010 10000010 130 3 1026 10000000010 |
| // 011 10000011 131 4 1027 10000000011 |
| // The first column shows the bit pattern stored in bits 6-4 of the arm |
| // encoded immediate. The second column shows the 8-bit IEEE 754 single |
| // -precision exponent in binary, the third column shows the raw decimal |
| // value. IEEE 754 single-precision numbers are stored with a bias of 127 |
| // to the exponent, so the fourth column shows the resulting exponent. |
| // From this was can see that the exponent can be in the range -3..4, |
| // which agrees with the high level description given above. The fifth |
| // and sixth columns shows the value stored in a IEEE 754 double-precision |
| // number to represent these exponents in decimal and binary, given the |
| // bias of 1023. |
| // |
| // Ultimately, detecting doubles that can be encoded as immediates on arm |
| // and encoding doubles is actually not too bad. A floating point value can |
| // be encoded by retaining the sign bit, the low three bits of the exponent |
| // and the high 4 bits of the mantissa. To validly be able to encode an |
| // immediate the remainder of the mantissa must be zero, and the high part |
| // of the exponent must match the top bit retained, bar the highest bit |
| // which must be its inverse. |
| static bool canEncodeFPImm(double d) |
| { |
| // Discard the sign bit, the low two bits of the exponent & the highest |
| // four bits of the mantissa. |
| uint64_t masked = bitwise_cast<uint64_t>(d) & 0x7fc0ffffffffffffull; |
| return (masked == 0x3fc0000000000000ull) || (masked == 0x4000000000000000ull); |
| } |
| |
| template<int datasize> |
| static bool canEncodePImmOffset(int32_t offset) |
| { |
| int32_t maxPImm = 4095 * (datasize / 8); |
| if (offset < 0) |
| return false; |
| if (offset > maxPImm) |
| return false; |
| if (offset & ((datasize / 8 ) - 1)) |
| return false; |
| return true; |
| } |
| |
| static bool canEncodeSImmOffset(int32_t offset) |
| { |
| return isInt9(offset); |
| } |
| |
| private: |
| int encodeFPImm(double d) |
| { |
| ASSERT(canEncodeFPImm(d)); |
| uint64_t u64 = bitwise_cast<uint64_t>(d); |
| return (static_cast<int>(u64 >> 56) & 0x80) | (static_cast<int>(u64 >> 48) & 0x7f); |
| } |
| |
| template<int datasize> |
| int encodeShiftAmount(int amount) |
| { |
| ASSERT(!amount || datasize == (8 << amount)); |
| return amount; |
| } |
| |
| template<int datasize> |
| static int encodePositiveImmediate(unsigned pimm) |
| { |
| ASSERT(!(pimm & ((datasize / 8) - 1))); |
| return pimm / (datasize / 8); |
| } |
| |
| enum Datasize { |
| Datasize_32, |
| Datasize_64, |
| Datasize_64_top, |
| Datasize_16 |
| }; |
| |
| enum MemOpSize { |
| MemOpSize_8_or_128, |
| MemOpSize_16, |
| MemOpSize_32, |
| MemOpSize_64, |
| }; |
| |
| enum BranchType { |
| BranchType_JMP, |
| BranchType_CALL, |
| BranchType_RET |
| }; |
| |
| enum AddOp { |
| AddOp_ADD, |
| AddOp_SUB |
| }; |
| |
| enum BitfieldOp { |
| BitfieldOp_SBFM, |
| BitfieldOp_BFM, |
| BitfieldOp_UBFM |
| }; |
| |
| enum DataOp1Source { |
| DataOp_RBIT, |
| DataOp_REV16, |
| DataOp_REV32, |
| DataOp_REV64, |
| DataOp_CLZ, |
| DataOp_CLS |
| }; |
| |
| enum DataOp2Source { |
| DataOp_UDIV = 2, |
| DataOp_SDIV = 3, |
| DataOp_LSLV = 8, |
| DataOp_LSRV = 9, |
| DataOp_ASRV = 10, |
| DataOp_RORV = 11 |
| }; |
| |
| enum DataOp3Source { |
| DataOp_MADD = 0, |
| DataOp_MSUB = 1, |
| DataOp_SMADDL = 2, |
| DataOp_SMSUBL = 3, |
| DataOp_SMULH = 4, |
| DataOp_UMADDL = 10, |
| DataOp_UMSUBL = 11, |
| DataOp_UMULH = 12 |
| }; |
| |
| enum ExcepnOp { |
| ExcepnOp_EXCEPTION = 0, |
| ExcepnOp_BREAKPOINT = 1, |
| ExcepnOp_HALT = 2, |
| ExcepnOp_DCPS = 5 |
| }; |
| |
| enum FPCmpOp { |
| FPCmpOp_FCMP = 0x00, |
| FPCmpOp_FCMP0 = 0x08, |
| FPCmpOp_FCMPE = 0x10, |
| FPCmpOp_FCMPE0 = 0x18 |
| }; |
| |
| enum FPCondCmpOp { |
| FPCondCmpOp_FCMP, |
| FPCondCmpOp_FCMPE |
| }; |
| |
| enum FPDataOp1Source { |
| FPDataOp_FMOV = 0, |
| FPDataOp_FABS = 1, |
| FPDataOp_FNEG = 2, |
| FPDataOp_FSQRT = 3, |
| FPDataOp_FCVT_toSingle = 4, |
| FPDataOp_FCVT_toDouble = 5, |
| FPDataOp_FCVT_toHalf = 7, |
| FPDataOp_FRINTN = 8, |
| FPDataOp_FRINTP = 9, |
| FPDataOp_FRINTM = 10, |
| FPDataOp_FRINTZ = 11, |
| FPDataOp_FRINTA = 12, |
| FPDataOp_FRINTX = 14, |
| FPDataOp_FRINTI = 15 |
| }; |
| |
| enum FPDataOp2Source { |
| FPDataOp_FMUL, |
| FPDataOp_FDIV, |
| FPDataOp_FADD, |
| FPDataOp_FSUB, |
| FPDataOp_FMAX, |
| FPDataOp_FMIN, |
| FPDataOp_FMAXNM, |
| FPDataOp_FMINNM, |
| FPDataOp_FNMUL |
| }; |
| |
| enum FPIntConvOp { |
| FPIntConvOp_FCVTNS = 0x00, |
| FPIntConvOp_FCVTNU = 0x01, |
| FPIntConvOp_SCVTF = 0x02, |
| FPIntConvOp_UCVTF = 0x03, |
| FPIntConvOp_FCVTAS = 0x04, |
| FPIntConvOp_FCVTAU = 0x05, |
| FPIntConvOp_FMOV_QtoX = 0x06, |
| FPIntConvOp_FMOV_XtoQ = 0x07, |
| FPIntConvOp_FCVTPS = 0x08, |
| FPIntConvOp_FCVTPU = 0x09, |
| FPIntConvOp_FMOV_QtoX_top = 0x0e, |
| FPIntConvOp_FMOV_XtoQ_top = 0x0f, |
| FPIntConvOp_FCVTMS = 0x10, |
| FPIntConvOp_FCVTMU = 0x11, |
| FPIntConvOp_FCVTZS = 0x18, |
| FPIntConvOp_FCVTZU = 0x19, |
| }; |
| |
| enum LogicalOp { |
| LogicalOp_AND, |
| LogicalOp_ORR, |
| LogicalOp_EOR, |
| LogicalOp_ANDS |
| }; |
| |
| enum MemOp { |
| MemOp_STORE, |
| MemOp_LOAD, |
| MemOp_STORE_V128, |
| MemOp_LOAD_V128, |
| MemOp_PREFETCH = 2, // size must be 3 |
| MemOp_LOAD_signed64 = 2, // size may be 0, 1 or 2 |
| MemOp_LOAD_signed32 = 3 // size may be 0 or 1 |
| }; |
| |
| enum MoveWideOp { |
| MoveWideOp_N = 0, |
| MoveWideOp_Z = 2, |
| MoveWideOp_K = 3 |
| }; |
| |
| enum LdrLiteralOp { |
| LdrLiteralOp_32BIT = 0, |
| LdrLiteralOp_64BIT = 1, |
| LdrLiteralOp_LDRSW = 2, |
| LdrLiteralOp_128BIT = 2 |
| }; |
| |
| public: |
| // Integer Instructions: |
| |
| template<int datasize, SetFlags setFlags = DontSetFlags> |
| ALWAYS_INLINE void adc(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| CHECK_DATASIZE(); |
| insn(addSubtractWithCarry(DATASIZE, AddOp_ADD, setFlags, rm, rn, rd)); |
| } |
| |
| template<int datasize, SetFlags setFlags = DontSetFlags> |
| ALWAYS_INLINE void add(RegisterID rd, RegisterID rn, UInt12 imm12, int shift = 0) |
| { |
| CHECK_DATASIZE(); |
| ASSERT(!shift || shift == 12); |
| insn(addSubtractImmediate(DATASIZE, AddOp_ADD, setFlags, shift == 12, imm12, rn, rd)); |
| } |
| |
| template<int datasize, SetFlags setFlags = DontSetFlags> |
| ALWAYS_INLINE void add(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| add<datasize, setFlags>(rd, rn, rm, LSL, 0); |
| } |
| |
| template<int datasize, SetFlags setFlags = DontSetFlags> |
| ALWAYS_INLINE void add(RegisterID rd, RegisterID rn, RegisterID rm, ExtendType extend, int amount) |
| { |
| CHECK_DATASIZE(); |
| insn(addSubtractExtendedRegister(DATASIZE, AddOp_ADD, setFlags, rm, extend, amount, rn, rd)); |
| } |
| |
| template<int datasize, SetFlags setFlags = DontSetFlags> |
| ALWAYS_INLINE void add(RegisterID rd, RegisterID rn, RegisterID rm, ShiftType shift, int amount) |
| { |
| CHECK_DATASIZE(); |
| if (isSp(rn)) { |
| ASSERT(shift == LSL); |
| add<datasize, setFlags>(rd, rn, rm, UXTX, amount); |
| } else |
| insn(addSubtractShiftedRegister(DATASIZE, AddOp_ADD, setFlags, shift, rm, amount, rn, rd)); |
| } |
| |
| ALWAYS_INLINE void adr(RegisterID rd, int offset) |
| { |
| insn(pcRelative(false, offset, rd)); |
| } |
| |
| ALWAYS_INLINE void adrp(RegisterID rd, int offset) |
| { |
| ASSERT(!(offset & 0xfff)); |
| insn(pcRelative(true, offset >> 12, rd)); |
| } |
| |
| template<int datasize, SetFlags setFlags = DontSetFlags> |
| ALWAYS_INLINE void and_(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| and_<datasize, setFlags>(rd, rn, rm, LSL, 0); |
| } |
| |
| template<int datasize, SetFlags setFlags = DontSetFlags> |
| ALWAYS_INLINE void and_(RegisterID rd, RegisterID rn, RegisterID rm, ShiftType shift, int amount) |
| { |
| CHECK_DATASIZE(); |
| insn(logicalShiftedRegister(DATASIZE, setFlags ? LogicalOp_ANDS : LogicalOp_AND, shift, false, rm, amount, rn, rd)); |
| } |
| |
| template<int datasize, SetFlags setFlags = DontSetFlags> |
| ALWAYS_INLINE void and_(RegisterID rd, RegisterID rn, LogicalImmediate imm) |
| { |
| CHECK_DATASIZE(); |
| insn(logicalImmediate(DATASIZE, setFlags ? LogicalOp_ANDS : LogicalOp_AND, imm.value(), rn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void asr(RegisterID rd, RegisterID rn, int shift) |
| { |
| ASSERT(shift < datasize); |
| sbfm<datasize>(rd, rn, shift, datasize - 1); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void asr(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| asrv<datasize>(rd, rn, rm); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void asrv(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| CHECK_DATASIZE(); |
| insn(dataProcessing2Source(DATASIZE, rm, DataOp_ASRV, rn, rd)); |
| } |
| |
| ALWAYS_INLINE void b(int32_t offset = 0) |
| { |
| ASSERT(!(offset & 3)); |
| offset >>= 2; |
| ASSERT(offset == (offset << 6) >> 6); |
| insn(unconditionalBranchImmediate(false, offset)); |
| } |
| |
| ALWAYS_INLINE void b_cond(Condition cond, int32_t offset = 0) |
| { |
| ASSERT(!(offset & 3)); |
| offset >>= 2; |
| ASSERT(offset == (offset << 13) >> 13); |
| insn(conditionalBranchImmediate(offset, cond)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void bfi(RegisterID rd, RegisterID rn, int lsb, int width) |
| { |
| bfm<datasize>(rd, rn, (datasize - lsb) & (datasize - 1), width - 1); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void bfm(RegisterID rd, RegisterID rn, int immr, int imms) |
| { |
| CHECK_DATASIZE(); |
| insn(bitfield(DATASIZE, BitfieldOp_BFM, immr, imms, rn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void bfxil(RegisterID rd, RegisterID rn, int lsb, int width) |
| { |
| bfm<datasize>(rd, rn, lsb, lsb + width - 1); |
| } |
| |
| template<int datasize, SetFlags setFlags = DontSetFlags> |
| ALWAYS_INLINE void bic(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| bic<datasize, setFlags>(rd, rn, rm, LSL, 0); |
| } |
| |
| template<int datasize, SetFlags setFlags = DontSetFlags> |
| ALWAYS_INLINE void bic(RegisterID rd, RegisterID rn, RegisterID rm, ShiftType shift, int amount) |
| { |
| CHECK_DATASIZE(); |
| insn(logicalShiftedRegister(DATASIZE, setFlags ? LogicalOp_ANDS : LogicalOp_AND, shift, true, rm, amount, rn, rd)); |
| } |
| |
| ALWAYS_INLINE void bl(int32_t offset = 0) |
| { |
| ASSERT(!(offset & 3)); |
| offset >>= 2; |
| insn(unconditionalBranchImmediate(true, offset)); |
| } |
| |
| ALWAYS_INLINE void blr(RegisterID rn) |
| { |
| insn(unconditionalBranchRegister(BranchType_CALL, rn)); |
| } |
| |
| ALWAYS_INLINE void br(RegisterID rn) |
| { |
| insn(unconditionalBranchRegister(BranchType_JMP, rn)); |
| } |
| |
| ALWAYS_INLINE void brk(uint16_t imm) |
| { |
| insn(excepnGeneration(ExcepnOp_BREAKPOINT, imm, 0)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void cbnz(RegisterID rt, int32_t offset = 0) |
| { |
| CHECK_DATASIZE(); |
| ASSERT(!(offset & 3)); |
| offset >>= 2; |
| insn(compareAndBranchImmediate(DATASIZE, true, offset, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void cbz(RegisterID rt, int32_t offset = 0) |
| { |
| CHECK_DATASIZE(); |
| ASSERT(!(offset & 3)); |
| offset >>= 2; |
| insn(compareAndBranchImmediate(DATASIZE, false, offset, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ccmn(RegisterID rn, RegisterID rm, int nzcv, Condition cond) |
| { |
| CHECK_DATASIZE(); |
| insn(conditionalCompareRegister(DATASIZE, AddOp_ADD, rm, cond, rn, nzcv)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ccmn(RegisterID rn, UInt5 imm, int nzcv, Condition cond) |
| { |
| CHECK_DATASIZE(); |
| insn(conditionalCompareImmediate(DATASIZE, AddOp_ADD, imm, cond, rn, nzcv)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ccmp(RegisterID rn, RegisterID rm, int nzcv, Condition cond) |
| { |
| CHECK_DATASIZE(); |
| insn(conditionalCompareRegister(DATASIZE, AddOp_SUB, rm, cond, rn, nzcv)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ccmp(RegisterID rn, UInt5 imm, int nzcv, Condition cond) |
| { |
| CHECK_DATASIZE(); |
| insn(conditionalCompareImmediate(DATASIZE, AddOp_SUB, imm, cond, rn, nzcv)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void cinc(RegisterID rd, RegisterID rn, Condition cond) |
| { |
| csinc<datasize>(rd, rn, rn, invert(cond)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void cinv(RegisterID rd, RegisterID rn, Condition cond) |
| { |
| csinv<datasize>(rd, rn, rn, invert(cond)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void cls(RegisterID rd, RegisterID rn) |
| { |
| CHECK_DATASIZE(); |
| insn(dataProcessing1Source(DATASIZE, DataOp_CLS, rn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void clz(RegisterID rd, RegisterID rn) |
| { |
| CHECK_DATASIZE(); |
| insn(dataProcessing1Source(DATASIZE, DataOp_CLZ, rn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void cmn(RegisterID rn, UInt12 imm12, int shift = 0) |
| { |
| add<datasize, S>(ARM64Registers::zr, rn, imm12, shift); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void cmn(RegisterID rn, RegisterID rm) |
| { |
| add<datasize, S>(ARM64Registers::zr, rn, rm); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void cmn(RegisterID rn, RegisterID rm, ExtendType extend, int amount) |
| { |
| add<datasize, S>(ARM64Registers::zr, rn, rm, extend, amount); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void cmn(RegisterID rn, RegisterID rm, ShiftType shift, int amount) |
| { |
| add<datasize, S>(ARM64Registers::zr, rn, rm, shift, amount); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void cmp(RegisterID rn, UInt12 imm12, int shift = 0) |
| { |
| sub<datasize, S>(ARM64Registers::zr, rn, imm12, shift); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void cmp(RegisterID rn, RegisterID rm) |
| { |
| sub<datasize, S>(ARM64Registers::zr, rn, rm); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void cmp(RegisterID rn, RegisterID rm, ExtendType extend, int amount) |
| { |
| sub<datasize, S>(ARM64Registers::zr, rn, rm, extend, amount); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void cmp(RegisterID rn, RegisterID rm, ShiftType shift, int amount) |
| { |
| sub<datasize, S>(ARM64Registers::zr, rn, rm, shift, amount); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void cneg(RegisterID rd, RegisterID rn, Condition cond) |
| { |
| csneg<datasize>(rd, rn, rn, invert(cond)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void csel(RegisterID rd, RegisterID rn, RegisterID rm, Condition cond) |
| { |
| CHECK_DATASIZE(); |
| insn(conditionalSelect(DATASIZE, false, rm, cond, false, rn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void cset(RegisterID rd, Condition cond) |
| { |
| csinc<datasize>(rd, ARM64Registers::zr, ARM64Registers::zr, invert(cond)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void csetm(RegisterID rd, Condition cond) |
| { |
| csinv<datasize>(rd, ARM64Registers::zr, ARM64Registers::zr, invert(cond)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void csinc(RegisterID rd, RegisterID rn, RegisterID rm, Condition cond) |
| { |
| CHECK_DATASIZE(); |
| insn(conditionalSelect(DATASIZE, false, rm, cond, true, rn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void csinv(RegisterID rd, RegisterID rn, RegisterID rm, Condition cond) |
| { |
| CHECK_DATASIZE(); |
| insn(conditionalSelect(DATASIZE, true, rm, cond, false, rn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void csneg(RegisterID rd, RegisterID rn, RegisterID rm, Condition cond) |
| { |
| CHECK_DATASIZE(); |
| insn(conditionalSelect(DATASIZE, true, rm, cond, true, rn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void eon(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| eon<datasize>(rd, rn, rm, LSL, 0); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void eon(RegisterID rd, RegisterID rn, RegisterID rm, ShiftType shift, int amount) |
| { |
| CHECK_DATASIZE(); |
| insn(logicalShiftedRegister(DATASIZE, LogicalOp_EOR, shift, true, rm, amount, rn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void eor(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| eor<datasize>(rd, rn, rm, LSL, 0); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void eor(RegisterID rd, RegisterID rn, RegisterID rm, ShiftType shift, int amount) |
| { |
| CHECK_DATASIZE(); |
| insn(logicalShiftedRegister(DATASIZE, LogicalOp_EOR, shift, false, rm, amount, rn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void eor(RegisterID rd, RegisterID rn, LogicalImmediate imm) |
| { |
| CHECK_DATASIZE(); |
| insn(logicalImmediate(DATASIZE, LogicalOp_EOR, imm.value(), rn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void extr(RegisterID rd, RegisterID rn, RegisterID rm, int lsb) |
| { |
| CHECK_DATASIZE(); |
| insn(extract(DATASIZE, rm, lsb, rn, rd)); |
| } |
| |
| ALWAYS_INLINE void hint(int imm) |
| { |
| insn(hintPseudo(imm)); |
| } |
| |
| ALWAYS_INLINE void hlt(uint16_t imm) |
| { |
| insn(excepnGeneration(ExcepnOp_HALT, imm, 0)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ldr(RegisterID rt, RegisterID rn, RegisterID rm) |
| { |
| ldr<datasize>(rt, rn, rm, UXTX, 0); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ldr(RegisterID rt, RegisterID rn, RegisterID rm, ExtendType extend, int amount) |
| { |
| CHECK_DATASIZE(); |
| insn(loadStoreRegisterRegisterOffset(MEMOPSIZE, false, MemOp_LOAD, rm, extend, encodeShiftAmount<datasize>(amount), rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ldr(RegisterID rt, RegisterID rn, unsigned pimm) |
| { |
| CHECK_DATASIZE(); |
| insn(loadStoreRegisterUnsignedImmediate(MEMOPSIZE, false, MemOp_LOAD, encodePositiveImmediate<datasize>(pimm), rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ldr(RegisterID rt, RegisterID rn, PostIndex simm) |
| { |
| CHECK_DATASIZE(); |
| insn(loadStoreRegisterPostIndex(MEMOPSIZE, false, MemOp_LOAD, simm, rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ldr(RegisterID rt, RegisterID rn, PreIndex simm) |
| { |
| CHECK_DATASIZE(); |
| insn(loadStoreRegisterPreIndex(MEMOPSIZE, false, MemOp_LOAD, simm, rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ldr_literal(RegisterID rt, int offset = 0) |
| { |
| CHECK_DATASIZE(); |
| ASSERT(!(offset & 3)); |
| insn(loadRegisterLiteral(datasize == 64 ? LdrLiteralOp_64BIT : LdrLiteralOp_32BIT, false, offset >> 2, rt)); |
| } |
| |
| ALWAYS_INLINE void ldrb(RegisterID rt, RegisterID rn, RegisterID rm) |
| { |
| // Not calling the 5 argument form of ldrb, since is amount is ommitted S is false. |
| insn(loadStoreRegisterRegisterOffset(MemOpSize_8_or_128, false, MemOp_LOAD, rm, UXTX, false, rn, rt)); |
| } |
| |
| ALWAYS_INLINE void ldrb(RegisterID rt, RegisterID rn, RegisterID rm, ExtendType extend, int amount) |
| { |
| ASSERT_UNUSED(amount, !amount); |
| insn(loadStoreRegisterRegisterOffset(MemOpSize_8_or_128, false, MemOp_LOAD, rm, extend, true, rn, rt)); |
| } |
| |
| ALWAYS_INLINE void ldrb(RegisterID rt, RegisterID rn, unsigned pimm) |
| { |
| insn(loadStoreRegisterUnsignedImmediate(MemOpSize_8_or_128, false, MemOp_LOAD, encodePositiveImmediate<8>(pimm), rn, rt)); |
| } |
| |
| ALWAYS_INLINE void ldrb(RegisterID rt, RegisterID rn, PostIndex simm) |
| { |
| insn(loadStoreRegisterPostIndex(MemOpSize_8_or_128, false, MemOp_LOAD, simm, rn, rt)); |
| } |
| |
| ALWAYS_INLINE void ldrb(RegisterID rt, RegisterID rn, PreIndex simm) |
| { |
| insn(loadStoreRegisterPreIndex(MemOpSize_8_or_128, false, MemOp_LOAD, simm, rn, rt)); |
| } |
| |
| ALWAYS_INLINE void ldrh(RegisterID rt, RegisterID rn, RegisterID rm) |
| { |
| ldrh(rt, rn, rm, UXTX, 0); |
| } |
| |
| ALWAYS_INLINE void ldrh(RegisterID rt, RegisterID rn, RegisterID rm, ExtendType extend, int amount) |
| { |
| ASSERT(!amount || amount == 1); |
| insn(loadStoreRegisterRegisterOffset(MemOpSize_16, false, MemOp_LOAD, rm, extend, amount == 1, rn, rt)); |
| } |
| |
| ALWAYS_INLINE void ldrh(RegisterID rt, RegisterID rn, unsigned pimm) |
| { |
| insn(loadStoreRegisterUnsignedImmediate(MemOpSize_16, false, MemOp_LOAD, encodePositiveImmediate<16>(pimm), rn, rt)); |
| } |
| |
| ALWAYS_INLINE void ldrh(RegisterID rt, RegisterID rn, PostIndex simm) |
| { |
| insn(loadStoreRegisterPostIndex(MemOpSize_16, false, MemOp_LOAD, simm, rn, rt)); |
| } |
| |
| ALWAYS_INLINE void ldrh(RegisterID rt, RegisterID rn, PreIndex simm) |
| { |
| insn(loadStoreRegisterPreIndex(MemOpSize_16, false, MemOp_LOAD, simm, rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ldrsb(RegisterID rt, RegisterID rn, RegisterID rm) |
| { |
| CHECK_DATASIZE(); |
| // Not calling the 5 argument form of ldrsb, since is amount is ommitted S is false. |
| insn(loadStoreRegisterRegisterOffset(MemOpSize_8_or_128, false, (datasize == 64) ? MemOp_LOAD_signed64 : MemOp_LOAD_signed32, rm, UXTX, false, rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ldrsb(RegisterID rt, RegisterID rn, RegisterID rm, ExtendType extend, int amount) |
| { |
| CHECK_DATASIZE(); |
| ASSERT_UNUSED(amount, !amount); |
| insn(loadStoreRegisterRegisterOffset(MemOpSize_8_or_128, false, (datasize == 64) ? MemOp_LOAD_signed64 : MemOp_LOAD_signed32, rm, extend, true, rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ldrsb(RegisterID rt, RegisterID rn, unsigned pimm) |
| { |
| CHECK_DATASIZE(); |
| insn(loadStoreRegisterUnsignedImmediate(MemOpSize_8_or_128, false, (datasize == 64) ? MemOp_LOAD_signed64 : MemOp_LOAD_signed32, encodePositiveImmediate<8>(pimm), rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ldrsb(RegisterID rt, RegisterID rn, PostIndex simm) |
| { |
| CHECK_DATASIZE(); |
| insn(loadStoreRegisterPostIndex(MemOpSize_8_or_128, false, (datasize == 64) ? MemOp_LOAD_signed64 : MemOp_LOAD_signed32, simm, rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ldrsb(RegisterID rt, RegisterID rn, PreIndex simm) |
| { |
| CHECK_DATASIZE(); |
| insn(loadStoreRegisterPreIndex(MemOpSize_8_or_128, false, (datasize == 64) ? MemOp_LOAD_signed64 : MemOp_LOAD_signed32, simm, rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ldrsh(RegisterID rt, RegisterID rn, RegisterID rm) |
| { |
| ldrsh<datasize>(rt, rn, rm, UXTX, 0); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ldrsh(RegisterID rt, RegisterID rn, RegisterID rm, ExtendType extend, int amount) |
| { |
| CHECK_DATASIZE(); |
| ASSERT(!amount || amount == 1); |
| insn(loadStoreRegisterRegisterOffset(MemOpSize_16, false, (datasize == 64) ? MemOp_LOAD_signed64 : MemOp_LOAD_signed32, rm, extend, amount == 1, rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ldrsh(RegisterID rt, RegisterID rn, unsigned pimm) |
| { |
| CHECK_DATASIZE(); |
| insn(loadStoreRegisterUnsignedImmediate(MemOpSize_16, false, (datasize == 64) ? MemOp_LOAD_signed64 : MemOp_LOAD_signed32, encodePositiveImmediate<16>(pimm), rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ldrsh(RegisterID rt, RegisterID rn, PostIndex simm) |
| { |
| CHECK_DATASIZE(); |
| insn(loadStoreRegisterPostIndex(MemOpSize_16, false, (datasize == 64) ? MemOp_LOAD_signed64 : MemOp_LOAD_signed32, simm, rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ldrsh(RegisterID rt, RegisterID rn, PreIndex simm) |
| { |
| CHECK_DATASIZE(); |
| insn(loadStoreRegisterPreIndex(MemOpSize_16, false, (datasize == 64) ? MemOp_LOAD_signed64 : MemOp_LOAD_signed32, simm, rn, rt)); |
| } |
| |
| ALWAYS_INLINE void ldrsw(RegisterID rt, RegisterID rn, RegisterID rm) |
| { |
| ldrsw(rt, rn, rm, UXTX, 0); |
| } |
| |
| ALWAYS_INLINE void ldrsw(RegisterID rt, RegisterID rn, RegisterID rm, ExtendType extend, int amount) |
| { |
| ASSERT(!amount || amount == 2); |
| insn(loadStoreRegisterRegisterOffset(MemOpSize_32, false, MemOp_LOAD_signed64, rm, extend, amount == 2, rn, rt)); |
| } |
| |
| ALWAYS_INLINE void ldrsw(RegisterID rt, RegisterID rn, unsigned pimm) |
| { |
| insn(loadStoreRegisterUnsignedImmediate(MemOpSize_32, false, MemOp_LOAD_signed64, encodePositiveImmediate<32>(pimm), rn, rt)); |
| } |
| |
| ALWAYS_INLINE void ldrsw(RegisterID rt, RegisterID rn, PostIndex simm) |
| { |
| insn(loadStoreRegisterPostIndex(MemOpSize_32, false, MemOp_LOAD_signed64, simm, rn, rt)); |
| } |
| |
| ALWAYS_INLINE void ldrsw(RegisterID rt, RegisterID rn, PreIndex simm) |
| { |
| insn(loadStoreRegisterPreIndex(MemOpSize_32, false, MemOp_LOAD_signed64, simm, rn, rt)); |
| } |
| |
| ALWAYS_INLINE void ldrsw_literal(RegisterID rt, int offset = 0) |
| { |
| ASSERT(!(offset & 3)); |
| insn(loadRegisterLiteral(LdrLiteralOp_LDRSW, false, offset >> 2, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ldur(RegisterID rt, RegisterID rn, int simm) |
| { |
| CHECK_DATASIZE(); |
| insn(loadStoreRegisterUnscaledImmediate(MEMOPSIZE, false, MemOp_LOAD, simm, rn, rt)); |
| } |
| |
| ALWAYS_INLINE void ldurb(RegisterID rt, RegisterID rn, int simm) |
| { |
| insn(loadStoreRegisterUnscaledImmediate(MemOpSize_8_or_128, false, MemOp_LOAD, simm, rn, rt)); |
| } |
| |
| ALWAYS_INLINE void ldurh(RegisterID rt, RegisterID rn, int simm) |
| { |
| insn(loadStoreRegisterUnscaledImmediate(MemOpSize_16, false, MemOp_LOAD, simm, rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ldursb(RegisterID rt, RegisterID rn, int simm) |
| { |
| CHECK_DATASIZE(); |
| insn(loadStoreRegisterUnscaledImmediate(MemOpSize_8_or_128, false, (datasize == 64) ? MemOp_LOAD_signed64 : MemOp_LOAD_signed32, simm, rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ldursh(RegisterID rt, RegisterID rn, int simm) |
| { |
| CHECK_DATASIZE(); |
| insn(loadStoreRegisterUnscaledImmediate(MemOpSize_16, false, (datasize == 64) ? MemOp_LOAD_signed64 : MemOp_LOAD_signed32, simm, rn, rt)); |
| } |
| |
| ALWAYS_INLINE void ldursw(RegisterID rt, RegisterID rn, int simm) |
| { |
| insn(loadStoreRegisterUnscaledImmediate(MemOpSize_32, false, MemOp_LOAD_signed64, simm, rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void lsl(RegisterID rd, RegisterID rn, int shift) |
| { |
| ASSERT(shift < datasize); |
| ubfm<datasize>(rd, rn, (datasize - shift) & (datasize - 1), datasize - 1 - shift); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void lsl(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| lslv<datasize>(rd, rn, rm); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void lslv(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| CHECK_DATASIZE(); |
| insn(dataProcessing2Source(DATASIZE, rm, DataOp_LSLV, rn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void lsr(RegisterID rd, RegisterID rn, int shift) |
| { |
| ASSERT(shift < datasize); |
| ubfm<datasize>(rd, rn, shift, datasize - 1); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void lsr(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| lsrv<datasize>(rd, rn, rm); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void lsrv(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| CHECK_DATASIZE(); |
| insn(dataProcessing2Source(DATASIZE, rm, DataOp_LSRV, rn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void madd(RegisterID rd, RegisterID rn, RegisterID rm, RegisterID ra) |
| { |
| CHECK_DATASIZE(); |
| insn(dataProcessing3Source(DATASIZE, DataOp_MADD, rm, ra, rn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void mneg(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| msub<datasize>(rd, rn, rm, ARM64Registers::zr); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void mov(RegisterID rd, RegisterID rm) |
| { |
| if (isSp(rd) || isSp(rm)) |
| add<datasize>(rd, rm, UInt12(0)); |
| else |
| orr<datasize>(rd, ARM64Registers::zr, rm); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void movi(RegisterID rd, LogicalImmediate imm) |
| { |
| orr<datasize>(rd, ARM64Registers::zr, imm); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void movk(RegisterID rd, uint16_t value, int shift = 0) |
| { |
| CHECK_DATASIZE(); |
| ASSERT(!(shift & 0xf)); |
| insn(moveWideImediate(DATASIZE, MoveWideOp_K, shift >> 4, value, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void movn(RegisterID rd, uint16_t value, int shift = 0) |
| { |
| CHECK_DATASIZE(); |
| ASSERT(!(shift & 0xf)); |
| insn(moveWideImediate(DATASIZE, MoveWideOp_N, shift >> 4, value, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void movz(RegisterID rd, uint16_t value, int shift = 0) |
| { |
| CHECK_DATASIZE(); |
| ASSERT(!(shift & 0xf)); |
| insn(moveWideImediate(DATASIZE, MoveWideOp_Z, shift >> 4, value, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void msub(RegisterID rd, RegisterID rn, RegisterID rm, RegisterID ra) |
| { |
| CHECK_DATASIZE(); |
| insn(dataProcessing3Source(DATASIZE, DataOp_MSUB, rm, ra, rn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void mul(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| madd<datasize>(rd, rn, rm, ARM64Registers::zr); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void mvn(RegisterID rd, RegisterID rm) |
| { |
| orn<datasize>(rd, ARM64Registers::zr, rm); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void mvn(RegisterID rd, RegisterID rm, ShiftType shift, int amount) |
| { |
| orn<datasize>(rd, ARM64Registers::zr, rm, shift, amount); |
| } |
| |
| template<int datasize, SetFlags setFlags = DontSetFlags> |
| ALWAYS_INLINE void neg(RegisterID rd, RegisterID rm) |
| { |
| sub<datasize, setFlags>(rd, ARM64Registers::zr, rm); |
| } |
| |
| template<int datasize, SetFlags setFlags = DontSetFlags> |
| ALWAYS_INLINE void neg(RegisterID rd, RegisterID rm, ShiftType shift, int amount) |
| { |
| sub<datasize, setFlags>(rd, ARM64Registers::zr, rm, shift, amount); |
| } |
| |
| template<int datasize, SetFlags setFlags = DontSetFlags> |
| ALWAYS_INLINE void ngc(RegisterID rd, RegisterID rm) |
| { |
| sbc<datasize, setFlags>(rd, ARM64Registers::zr, rm); |
| } |
| |
| template<int datasize, SetFlags setFlags = DontSetFlags> |
| ALWAYS_INLINE void ngc(RegisterID rd, RegisterID rm, ShiftType shift, int amount) |
| { |
| sbc<datasize, setFlags>(rd, ARM64Registers::zr, rm, shift, amount); |
| } |
| |
| ALWAYS_INLINE void nop() |
| { |
| insn(nopPseudo()); |
| } |
| |
| ALWAYS_INLINE void dmbSY() |
| { |
| insn(0xd5033fbf); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void orn(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| orn<datasize>(rd, rn, rm, LSL, 0); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void orn(RegisterID rd, RegisterID rn, RegisterID rm, ShiftType shift, int amount) |
| { |
| CHECK_DATASIZE(); |
| insn(logicalShiftedRegister(DATASIZE, LogicalOp_ORR, shift, true, rm, amount, rn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void orr(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| orr<datasize>(rd, rn, rm, LSL, 0); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void orr(RegisterID rd, RegisterID rn, RegisterID rm, ShiftType shift, int amount) |
| { |
| CHECK_DATASIZE(); |
| insn(logicalShiftedRegister(DATASIZE, LogicalOp_ORR, shift, false, rm, amount, rn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void orr(RegisterID rd, RegisterID rn, LogicalImmediate imm) |
| { |
| CHECK_DATASIZE(); |
| insn(logicalImmediate(DATASIZE, LogicalOp_ORR, imm.value(), rn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void rbit(RegisterID rd, RegisterID rn) |
| { |
| CHECK_DATASIZE(); |
| insn(dataProcessing1Source(DATASIZE, DataOp_RBIT, rn, rd)); |
| } |
| |
| ALWAYS_INLINE void ret(RegisterID rn = ARM64Registers::lr) |
| { |
| insn(unconditionalBranchRegister(BranchType_RET, rn)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void rev(RegisterID rd, RegisterID rn) |
| { |
| CHECK_DATASIZE(); |
| if (datasize == 32) // 'rev' mnemonic means REV32 or REV64 depending on the operand width. |
| insn(dataProcessing1Source(Datasize_32, DataOp_REV32, rn, rd)); |
| else |
| insn(dataProcessing1Source(Datasize_64, DataOp_REV64, rn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void rev16(RegisterID rd, RegisterID rn) |
| { |
| CHECK_DATASIZE(); |
| insn(dataProcessing1Source(DATASIZE, DataOp_REV16, rn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void rev32(RegisterID rd, RegisterID rn) |
| { |
| ASSERT(datasize == 64); // 'rev32' only valid with 64-bit operands. |
| insn(dataProcessing1Source(Datasize_64, DataOp_REV32, rn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ror(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| rorv<datasize>(rd, rn, rm); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ror(RegisterID rd, RegisterID rs, int shift) |
| { |
| extr<datasize>(rd, rs, rs, shift); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void rorv(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| CHECK_DATASIZE(); |
| insn(dataProcessing2Source(DATASIZE, rm, DataOp_RORV, rn, rd)); |
| } |
| |
| template<int datasize, SetFlags setFlags = DontSetFlags> |
| ALWAYS_INLINE void sbc(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| CHECK_DATASIZE(); |
| insn(addSubtractWithCarry(DATASIZE, AddOp_SUB, setFlags, rm, rn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void sbfiz(RegisterID rd, RegisterID rn, int lsb, int width) |
| { |
| sbfm<datasize>(rd, rn, (datasize - lsb) & (datasize - 1), width - 1); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void sbfm(RegisterID rd, RegisterID rn, int immr, int imms) |
| { |
| CHECK_DATASIZE(); |
| insn(bitfield(DATASIZE, BitfieldOp_SBFM, immr, imms, rn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void sbfx(RegisterID rd, RegisterID rn, int lsb, int width) |
| { |
| sbfm<datasize>(rd, rn, lsb, lsb + width - 1); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void sdiv(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| CHECK_DATASIZE(); |
| insn(dataProcessing2Source(DATASIZE, rm, DataOp_SDIV, rn, rd)); |
| } |
| |
| ALWAYS_INLINE void smaddl(RegisterID rd, RegisterID rn, RegisterID rm, RegisterID ra) |
| { |
| insn(dataProcessing3Source(Datasize_64, DataOp_SMADDL, rm, ra, rn, rd)); |
| } |
| |
| ALWAYS_INLINE void smnegl(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| smsubl(rd, rn, rm, ARM64Registers::zr); |
| } |
| |
| ALWAYS_INLINE void smsubl(RegisterID rd, RegisterID rn, RegisterID rm, RegisterID ra) |
| { |
| insn(dataProcessing3Source(Datasize_64, DataOp_SMSUBL, rm, ra, rn, rd)); |
| } |
| |
| ALWAYS_INLINE void smulh(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| insn(dataProcessing3Source(Datasize_64, DataOp_SMULH, rm, ARM64Registers::zr, rn, rd)); |
| } |
| |
| ALWAYS_INLINE void smull(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| smaddl(rd, rn, rm, ARM64Registers::zr); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void str(RegisterID rt, RegisterID rn, RegisterID rm) |
| { |
| str<datasize>(rt, rn, rm, UXTX, 0); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void str(RegisterID rt, RegisterID rn, RegisterID rm, ExtendType extend, int amount) |
| { |
| CHECK_DATASIZE(); |
| insn(loadStoreRegisterRegisterOffset(MEMOPSIZE, false, MemOp_STORE, rm, extend, encodeShiftAmount<datasize>(amount), rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void str(RegisterID rt, RegisterID rn, unsigned pimm) |
| { |
| CHECK_DATASIZE(); |
| insn(loadStoreRegisterUnsignedImmediate(MEMOPSIZE, false, MemOp_STORE, encodePositiveImmediate<datasize>(pimm), rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void str(RegisterID rt, RegisterID rn, PostIndex simm) |
| { |
| CHECK_DATASIZE(); |
| insn(loadStoreRegisterPostIndex(MEMOPSIZE, false, MemOp_STORE, simm, rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void str(RegisterID rt, RegisterID rn, PreIndex simm) |
| { |
| CHECK_DATASIZE(); |
| insn(loadStoreRegisterPreIndex(MEMOPSIZE, false, MemOp_STORE, simm, rn, rt)); |
| } |
| |
| ALWAYS_INLINE void strb(RegisterID rt, RegisterID rn, RegisterID rm) |
| { |
| // Not calling the 5 argument form of strb, since is amount is ommitted S is false. |
| insn(loadStoreRegisterRegisterOffset(MemOpSize_8_or_128, false, MemOp_STORE, rm, UXTX, false, rn, rt)); |
| } |
| |
| ALWAYS_INLINE void strb(RegisterID rt, RegisterID rn, RegisterID rm, ExtendType extend, int amount) |
| { |
| ASSERT_UNUSED(amount, !amount); |
| insn(loadStoreRegisterRegisterOffset(MemOpSize_8_or_128, false, MemOp_STORE, rm, extend, true, rn, rt)); |
| } |
| |
| ALWAYS_INLINE void strb(RegisterID rt, RegisterID rn, unsigned pimm) |
| { |
| insn(loadStoreRegisterUnsignedImmediate(MemOpSize_8_or_128, false, MemOp_STORE, encodePositiveImmediate<8>(pimm), rn, rt)); |
| } |
| |
| ALWAYS_INLINE void strb(RegisterID rt, RegisterID rn, PostIndex simm) |
| { |
| insn(loadStoreRegisterPostIndex(MemOpSize_8_or_128, false, MemOp_STORE, simm, rn, rt)); |
| } |
| |
| ALWAYS_INLINE void strb(RegisterID rt, RegisterID rn, PreIndex simm) |
| { |
| insn(loadStoreRegisterPreIndex(MemOpSize_8_or_128, false, MemOp_STORE, simm, rn, rt)); |
| } |
| |
| ALWAYS_INLINE void strh(RegisterID rt, RegisterID rn, RegisterID rm) |
| { |
| strh(rt, rn, rm, UXTX, 0); |
| } |
| |
| ALWAYS_INLINE void strh(RegisterID rt, RegisterID rn, RegisterID rm, ExtendType extend, int amount) |
| { |
| ASSERT(!amount || amount == 1); |
| insn(loadStoreRegisterRegisterOffset(MemOpSize_16, false, MemOp_STORE, rm, extend, amount == 1, rn, rt)); |
| } |
| |
| ALWAYS_INLINE void strh(RegisterID rt, RegisterID rn, unsigned pimm) |
| { |
| insn(loadStoreRegisterUnsignedImmediate(MemOpSize_16, false, MemOp_STORE, encodePositiveImmediate<16>(pimm), rn, rt)); |
| } |
| |
| ALWAYS_INLINE void strh(RegisterID rt, RegisterID rn, PostIndex simm) |
| { |
| insn(loadStoreRegisterPostIndex(MemOpSize_16, false, MemOp_STORE, simm, rn, rt)); |
| } |
| |
| ALWAYS_INLINE void strh(RegisterID rt, RegisterID rn, PreIndex simm) |
| { |
| insn(loadStoreRegisterPreIndex(MemOpSize_16, false, MemOp_STORE, simm, rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void stur(RegisterID rt, RegisterID rn, int simm) |
| { |
| CHECK_DATASIZE(); |
| insn(loadStoreRegisterUnscaledImmediate(MEMOPSIZE, false, MemOp_STORE, simm, rn, rt)); |
| } |
| |
| ALWAYS_INLINE void sturb(RegisterID rt, RegisterID rn, int simm) |
| { |
| insn(loadStoreRegisterUnscaledImmediate(MemOpSize_8_or_128, false, MemOp_STORE, simm, rn, rt)); |
| } |
| |
| ALWAYS_INLINE void sturh(RegisterID rt, RegisterID rn, int simm) |
| { |
| insn(loadStoreRegisterUnscaledImmediate(MemOpSize_16, false, MemOp_STORE, simm, rn, rt)); |
| } |
| |
| template<int datasize, SetFlags setFlags = DontSetFlags> |
| ALWAYS_INLINE void sub(RegisterID rd, RegisterID rn, UInt12 imm12, int shift = 0) |
| { |
| CHECK_DATASIZE(); |
| ASSERT(!shift || shift == 12); |
| insn(addSubtractImmediate(DATASIZE, AddOp_SUB, setFlags, shift == 12, imm12, rn, rd)); |
| } |
| |
| template<int datasize, SetFlags setFlags = DontSetFlags> |
| ALWAYS_INLINE void sub(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| sub<datasize, setFlags>(rd, rn, rm, LSL, 0); |
| } |
| |
| template<int datasize, SetFlags setFlags = DontSetFlags> |
| ALWAYS_INLINE void sub(RegisterID rd, RegisterID rn, RegisterID rm, ExtendType extend, int amount) |
| { |
| CHECK_DATASIZE(); |
| insn(addSubtractExtendedRegister(DATASIZE, AddOp_SUB, setFlags, rm, extend, amount, rn, rd)); |
| } |
| |
| template<int datasize, SetFlags setFlags = DontSetFlags> |
| ALWAYS_INLINE void sub(RegisterID rd, RegisterID rn, RegisterID rm, ShiftType shift, int amount) |
| { |
| CHECK_DATASIZE(); |
| if (isSp(rn)) { |
| ASSERT(shift == LSL); |
| sub<datasize, setFlags>(rd, rn, rm, UXTX, amount); |
| } else |
| insn(addSubtractShiftedRegister(DATASIZE, AddOp_SUB, setFlags, shift, rm, amount, rn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void sxtb(RegisterID rd, RegisterID rn) |
| { |
| sbfm<datasize>(rd, rn, 0, 7); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void sxth(RegisterID rd, RegisterID rn) |
| { |
| sbfm<datasize>(rd, rn, 0, 15); |
| } |
| |
| ALWAYS_INLINE void sxtw(RegisterID rd, RegisterID rn) |
| { |
| sbfm<64>(rd, rn, 0, 31); |
| } |
| |
| ALWAYS_INLINE void tbz(RegisterID rt, int imm, int offset = 0) |
| { |
| ASSERT(!(offset & 3)); |
| offset >>= 2; |
| insn(testAndBranchImmediate(false, imm, offset, rt)); |
| } |
| |
| ALWAYS_INLINE void tbnz(RegisterID rt, int imm, int offset = 0) |
| { |
| ASSERT(!(offset & 3)); |
| offset >>= 2; |
| insn(testAndBranchImmediate(true, imm, offset, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void tst(RegisterID rn, RegisterID rm) |
| { |
| and_<datasize, S>(ARM64Registers::zr, rn, rm); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void tst(RegisterID rn, RegisterID rm, ShiftType shift, int amount) |
| { |
| and_<datasize, S>(ARM64Registers::zr, rn, rm, shift, amount); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void tst(RegisterID rn, LogicalImmediate imm) |
| { |
| and_<datasize, S>(ARM64Registers::zr, rn, imm); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ubfiz(RegisterID rd, RegisterID rn, int lsb, int width) |
| { |
| ubfm<datasize>(rd, rn, (datasize - lsb) & (datasize - 1), width - 1); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ubfm(RegisterID rd, RegisterID rn, int immr, int imms) |
| { |
| CHECK_DATASIZE(); |
| insn(bitfield(DATASIZE, BitfieldOp_UBFM, immr, imms, rn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ubfx(RegisterID rd, RegisterID rn, int lsb, int width) |
| { |
| ubfm<datasize>(rd, rn, lsb, lsb + width - 1); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void udiv(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| CHECK_DATASIZE(); |
| insn(dataProcessing2Source(DATASIZE, rm, DataOp_UDIV, rn, rd)); |
| } |
| |
| ALWAYS_INLINE void umaddl(RegisterID rd, RegisterID rn, RegisterID rm, RegisterID ra) |
| { |
| insn(dataProcessing3Source(Datasize_64, DataOp_UMADDL, rm, ra, rn, rd)); |
| } |
| |
| ALWAYS_INLINE void umnegl(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| umsubl(rd, rn, rm, ARM64Registers::zr); |
| } |
| |
| ALWAYS_INLINE void umsubl(RegisterID rd, RegisterID rn, RegisterID rm, RegisterID ra) |
| { |
| insn(dataProcessing3Source(Datasize_64, DataOp_UMSUBL, rm, ra, rn, rd)); |
| } |
| |
| ALWAYS_INLINE void umulh(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| insn(dataProcessing3Source(Datasize_64, DataOp_UMULH, rm, ARM64Registers::zr, rn, rd)); |
| } |
| |
| ALWAYS_INLINE void umull(RegisterID rd, RegisterID rn, RegisterID rm) |
| { |
| umaddl(rd, rn, rm, ARM64Registers::zr); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void uxtb(RegisterID rd, RegisterID rn) |
| { |
| ubfm<datasize>(rd, rn, 0, 7); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void uxth(RegisterID rd, RegisterID rn) |
| { |
| ubfm<datasize>(rd, rn, 0, 15); |
| } |
| |
| ALWAYS_INLINE void uxtw(RegisterID rd, RegisterID rn) |
| { |
| ubfm<64>(rd, rn, 0, 31); |
| } |
| |
| // Floating Point Instructions: |
| |
| template<int datasize> |
| ALWAYS_INLINE void fabs(FPRegisterID vd, FPRegisterID vn) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointDataProcessing1Source(DATASIZE, FPDataOp_FABS, vn, vd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void fadd(FPRegisterID vd, FPRegisterID vn, FPRegisterID vm) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointDataProcessing2Source(DATASIZE, vm, FPDataOp_FADD, vn, vd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void fccmp(FPRegisterID vn, FPRegisterID vm, int nzcv, Condition cond) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointConditionalCompare(DATASIZE, vm, cond, vn, FPCondCmpOp_FCMP, nzcv)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void fccmpe(FPRegisterID vn, FPRegisterID vm, int nzcv, Condition cond) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointConditionalCompare(DATASIZE, vm, cond, vn, FPCondCmpOp_FCMPE, nzcv)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void fcmp(FPRegisterID vn, FPRegisterID vm) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointCompare(DATASIZE, vm, vn, FPCmpOp_FCMP)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void fcmp_0(FPRegisterID vn) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointCompare(DATASIZE, static_cast<FPRegisterID>(0), vn, FPCmpOp_FCMP0)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void fcmpe(FPRegisterID vn, FPRegisterID vm) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointCompare(DATASIZE, vm, vn, FPCmpOp_FCMPE)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void fcmpe_0(FPRegisterID vn) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointCompare(DATASIZE, static_cast<FPRegisterID>(0), vn, FPCmpOp_FCMPE0)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void fcsel(FPRegisterID vd, FPRegisterID vn, FPRegisterID vm, Condition cond) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointConditionalSelect(DATASIZE, vm, cond, vn, vd)); |
| } |
| |
| template<int dstsize, int srcsize> |
| ALWAYS_INLINE void fcvt(FPRegisterID vd, FPRegisterID vn) |
| { |
| ASSERT(dstsize == 16 || dstsize == 32 || dstsize == 64); |
| ASSERT(srcsize == 16 || srcsize == 32 || srcsize == 64); |
| ASSERT(dstsize != srcsize); |
| Datasize type = (srcsize == 64) ? Datasize_64 : (srcsize == 32) ? Datasize_32 : Datasize_16; |
| FPDataOp1Source opcode = (dstsize == 64) ? FPDataOp_FCVT_toDouble : (dstsize == 32) ? FPDataOp_FCVT_toSingle : FPDataOp_FCVT_toHalf; |
| insn(floatingPointDataProcessing1Source(type, opcode, vn, vd)); |
| } |
| |
| template<int dstsize, int srcsize> |
| ALWAYS_INLINE void fcvtas(RegisterID rd, FPRegisterID vn) |
| { |
| CHECK_DATASIZE_OF(dstsize); |
| CHECK_DATASIZE_OF(srcsize); |
| insn(floatingPointIntegerConversions(DATASIZE_OF(dstsize), DATASIZE_OF(srcsize), FPIntConvOp_FCVTAS, vn, rd)); |
| } |
| |
| template<int dstsize, int srcsize> |
| ALWAYS_INLINE void fcvtau(RegisterID rd, FPRegisterID vn) |
| { |
| CHECK_DATASIZE_OF(dstsize); |
| CHECK_DATASIZE_OF(srcsize); |
| insn(floatingPointIntegerConversions(DATASIZE_OF(dstsize), DATASIZE_OF(srcsize), FPIntConvOp_FCVTAU, vn, rd)); |
| } |
| |
| template<int dstsize, int srcsize> |
| ALWAYS_INLINE void fcvtms(RegisterID rd, FPRegisterID vn) |
| { |
| CHECK_DATASIZE_OF(dstsize); |
| CHECK_DATASIZE_OF(srcsize); |
| insn(floatingPointIntegerConversions(DATASIZE_OF(dstsize), DATASIZE_OF(srcsize), FPIntConvOp_FCVTMS, vn, rd)); |
| } |
| |
| template<int dstsize, int srcsize> |
| ALWAYS_INLINE void fcvtmu(RegisterID rd, FPRegisterID vn) |
| { |
| CHECK_DATASIZE_OF(dstsize); |
| CHECK_DATASIZE_OF(srcsize); |
| insn(floatingPointIntegerConversions(DATASIZE_OF(dstsize), DATASIZE_OF(srcsize), FPIntConvOp_FCVTMU, vn, rd)); |
| } |
| |
| template<int dstsize, int srcsize> |
| ALWAYS_INLINE void fcvtns(RegisterID rd, FPRegisterID vn) |
| { |
| CHECK_DATASIZE_OF(dstsize); |
| CHECK_DATASIZE_OF(srcsize); |
| insn(floatingPointIntegerConversions(DATASIZE_OF(dstsize), DATASIZE_OF(srcsize), FPIntConvOp_FCVTNS, vn, rd)); |
| } |
| |
| template<int dstsize, int srcsize> |
| ALWAYS_INLINE void fcvtnu(RegisterID rd, FPRegisterID vn) |
| { |
| CHECK_DATASIZE_OF(dstsize); |
| CHECK_DATASIZE_OF(srcsize); |
| insn(floatingPointIntegerConversions(DATASIZE_OF(dstsize), DATASIZE_OF(srcsize), FPIntConvOp_FCVTNU, vn, rd)); |
| } |
| |
| template<int dstsize, int srcsize> |
| ALWAYS_INLINE void fcvtps(RegisterID rd, FPRegisterID vn) |
| { |
| CHECK_DATASIZE_OF(dstsize); |
| CHECK_DATASIZE_OF(srcsize); |
| insn(floatingPointIntegerConversions(DATASIZE_OF(dstsize), DATASIZE_OF(srcsize), FPIntConvOp_FCVTPS, vn, rd)); |
| } |
| |
| template<int dstsize, int srcsize> |
| ALWAYS_INLINE void fcvtpu(RegisterID rd, FPRegisterID vn) |
| { |
| CHECK_DATASIZE_OF(dstsize); |
| CHECK_DATASIZE_OF(srcsize); |
| insn(floatingPointIntegerConversions(DATASIZE_OF(dstsize), DATASIZE_OF(srcsize), FPIntConvOp_FCVTPU, vn, rd)); |
| } |
| |
| template<int dstsize, int srcsize> |
| ALWAYS_INLINE void fcvtzs(RegisterID rd, FPRegisterID vn) |
| { |
| CHECK_DATASIZE_OF(dstsize); |
| CHECK_DATASIZE_OF(srcsize); |
| insn(floatingPointIntegerConversions(DATASIZE_OF(dstsize), DATASIZE_OF(srcsize), FPIntConvOp_FCVTZS, vn, rd)); |
| } |
| |
| template<int dstsize, int srcsize> |
| ALWAYS_INLINE void fcvtzu(RegisterID rd, FPRegisterID vn) |
| { |
| CHECK_DATASIZE_OF(dstsize); |
| CHECK_DATASIZE_OF(srcsize); |
| insn(floatingPointIntegerConversions(DATASIZE_OF(dstsize), DATASIZE_OF(srcsize), FPIntConvOp_FCVTZU, vn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void fdiv(FPRegisterID vd, FPRegisterID vn, FPRegisterID vm) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointDataProcessing2Source(DATASIZE, vm, FPDataOp_FDIV, vn, vd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void fmadd(FPRegisterID vd, FPRegisterID vn, FPRegisterID vm, FPRegisterID va) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointDataProcessing3Source(DATASIZE, false, vm, AddOp_ADD, va, vn, vd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void fmax(FPRegisterID vd, FPRegisterID vn, FPRegisterID vm) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointDataProcessing2Source(DATASIZE, vm, FPDataOp_FMAX, vn, vd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void fmaxnm(FPRegisterID vd, FPRegisterID vn, FPRegisterID vm) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointDataProcessing2Source(DATASIZE, vm, FPDataOp_FMAXNM, vn, vd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void fmin(FPRegisterID vd, FPRegisterID vn, FPRegisterID vm) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointDataProcessing2Source(DATASIZE, vm, FPDataOp_FMIN, vn, vd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void fminnm(FPRegisterID vd, FPRegisterID vn, FPRegisterID vm) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointDataProcessing2Source(DATASIZE, vm, FPDataOp_FMINNM, vn, vd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void fmov(FPRegisterID vd, FPRegisterID vn) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointDataProcessing1Source(DATASIZE, FPDataOp_FMOV, vn, vd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void fmov(FPRegisterID vd, RegisterID rn) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointIntegerConversions(DATASIZE, DATASIZE, FPIntConvOp_FMOV_XtoQ, rn, vd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void fmov(RegisterID rd, FPRegisterID vn) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointIntegerConversions(DATASIZE, DATASIZE, FPIntConvOp_FMOV_QtoX, vn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void fmov(FPRegisterID vd, double imm) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointImmediate(DATASIZE, encodeFPImm(imm), vd)); |
| } |
| |
| ALWAYS_INLINE void fmov_top(FPRegisterID vd, RegisterID rn) |
| { |
| insn(floatingPointIntegerConversions(Datasize_64, Datasize_64, FPIntConvOp_FMOV_XtoQ_top, rn, vd)); |
| } |
| |
| ALWAYS_INLINE void fmov_top(RegisterID rd, FPRegisterID vn) |
| { |
| insn(floatingPointIntegerConversions(Datasize_64, Datasize_64, FPIntConvOp_FMOV_QtoX_top, vn, rd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void fmsub(FPRegisterID vd, FPRegisterID vn, FPRegisterID vm, FPRegisterID va) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointDataProcessing3Source(DATASIZE, false, vm, AddOp_SUB, va, vn, vd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void fmul(FPRegisterID vd, FPRegisterID vn, FPRegisterID vm) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointDataProcessing2Source(DATASIZE, vm, FPDataOp_FMUL, vn, vd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void fneg(FPRegisterID vd, FPRegisterID vn) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointDataProcessing1Source(DATASIZE, FPDataOp_FNEG, vn, vd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void fnmadd(FPRegisterID vd, FPRegisterID vn, FPRegisterID vm, FPRegisterID va) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointDataProcessing3Source(DATASIZE, true, vm, AddOp_ADD, va, vn, vd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void fnmsub(FPRegisterID vd, FPRegisterID vn, FPRegisterID vm, FPRegisterID va) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointDataProcessing3Source(DATASIZE, true, vm, AddOp_SUB, va, vn, vd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void fnmul(FPRegisterID vd, FPRegisterID vn, FPRegisterID vm) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointDataProcessing2Source(DATASIZE, vm, FPDataOp_FNMUL, vn, vd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void frinta(FPRegisterID vd, FPRegisterID vn) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointDataProcessing1Source(DATASIZE, FPDataOp_FRINTA, vn, vd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void frinti(FPRegisterID vd, FPRegisterID vn) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointDataProcessing1Source(DATASIZE, FPDataOp_FRINTI, vn, vd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void frintm(FPRegisterID vd, FPRegisterID vn) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointDataProcessing1Source(DATASIZE, FPDataOp_FRINTM, vn, vd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void frintn(FPRegisterID vd, FPRegisterID vn) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointDataProcessing1Source(DATASIZE, FPDataOp_FRINTN, vn, vd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void frintp(FPRegisterID vd, FPRegisterID vn) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointDataProcessing1Source(DATASIZE, FPDataOp_FRINTP, vn, vd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void frintx(FPRegisterID vd, FPRegisterID vn) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointDataProcessing1Source(DATASIZE, FPDataOp_FRINTX, vn, vd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void frintz(FPRegisterID vd, FPRegisterID vn) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointDataProcessing1Source(DATASIZE, FPDataOp_FRINTZ, vn, vd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void fsqrt(FPRegisterID vd, FPRegisterID vn) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointDataProcessing1Source(DATASIZE, FPDataOp_FSQRT, vn, vd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void fsub(FPRegisterID vd, FPRegisterID vn, FPRegisterID vm) |
| { |
| CHECK_DATASIZE(); |
| insn(floatingPointDataProcessing2Source(DATASIZE, vm, FPDataOp_FSUB, vn, vd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ldr(FPRegisterID rt, RegisterID rn, RegisterID rm) |
| { |
| ldr<datasize>(rt, rn, rm, UXTX, 0); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ldr(FPRegisterID rt, RegisterID rn, RegisterID rm, ExtendType extend, int amount) |
| { |
| CHECK_FP_MEMOP_DATASIZE(); |
| insn(loadStoreRegisterRegisterOffset(MEMOPSIZE, true, datasize == 128 ? MemOp_LOAD_V128 : MemOp_LOAD, rm, extend, encodeShiftAmount<datasize>(amount), rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ldr(FPRegisterID rt, RegisterID rn, unsigned pimm) |
| { |
| CHECK_FP_MEMOP_DATASIZE(); |
| insn(loadStoreRegisterUnsignedImmediate(MEMOPSIZE, true, datasize == 128 ? MemOp_LOAD_V128 : MemOp_LOAD, encodePositiveImmediate<datasize>(pimm), rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ldr(FPRegisterID rt, RegisterID rn, PostIndex simm) |
| { |
| CHECK_FP_MEMOP_DATASIZE(); |
| insn(loadStoreRegisterPostIndex(MEMOPSIZE, true, datasize == 128 ? MemOp_LOAD_V128 : MemOp_LOAD, simm, rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ldr(FPRegisterID rt, RegisterID rn, PreIndex simm) |
| { |
| CHECK_FP_MEMOP_DATASIZE(); |
| insn(loadStoreRegisterPreIndex(MEMOPSIZE, true, datasize == 128 ? MemOp_LOAD_V128 : MemOp_LOAD, simm, rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ldr_literal(FPRegisterID rt, int offset = 0) |
| { |
| CHECK_FP_MEMOP_DATASIZE(); |
| ASSERT(datasize >= 32); |
| ASSERT(!(offset & 3)); |
| insn(loadRegisterLiteral(datasize == 128 ? LdrLiteralOp_128BIT : datasize == 64 ? LdrLiteralOp_64BIT : LdrLiteralOp_32BIT, true, offset >> 2, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void ldur(FPRegisterID rt, RegisterID rn, int simm) |
| { |
| CHECK_FP_MEMOP_DATASIZE(); |
| insn(loadStoreRegisterUnscaledImmediate(MEMOPSIZE, true, datasize == 128 ? MemOp_LOAD_V128 : MemOp_LOAD, simm, rn, rt)); |
| } |
| |
| template<int dstsize, int srcsize> |
| ALWAYS_INLINE void scvtf(FPRegisterID vd, RegisterID rn) |
| { |
| CHECK_DATASIZE_OF(dstsize); |
| CHECK_DATASIZE_OF(srcsize); |
| insn(floatingPointIntegerConversions(DATASIZE_OF(srcsize), DATASIZE_OF(dstsize), FPIntConvOp_SCVTF, rn, vd)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void str(FPRegisterID rt, RegisterID rn, RegisterID rm) |
| { |
| str<datasize>(rt, rn, rm, UXTX, 0); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void str(FPRegisterID rt, RegisterID rn, RegisterID rm, ExtendType extend, int amount) |
| { |
| CHECK_FP_MEMOP_DATASIZE(); |
| insn(loadStoreRegisterRegisterOffset(MEMOPSIZE, true, datasize == 128 ? MemOp_STORE_V128 : MemOp_STORE, rm, extend, encodeShiftAmount<datasize>(amount), rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void str(FPRegisterID rt, RegisterID rn, unsigned pimm) |
| { |
| CHECK_FP_MEMOP_DATASIZE(); |
| insn(loadStoreRegisterUnsignedImmediate(MEMOPSIZE, true, datasize == 128 ? MemOp_STORE_V128 : MemOp_STORE, encodePositiveImmediate<datasize>(pimm), rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void str(FPRegisterID rt, RegisterID rn, PostIndex simm) |
| { |
| CHECK_FP_MEMOP_DATASIZE(); |
| insn(loadStoreRegisterPostIndex(MEMOPSIZE, true, datasize == 128 ? MemOp_STORE_V128 : MemOp_STORE, simm, rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void str(FPRegisterID rt, RegisterID rn, PreIndex simm) |
| { |
| CHECK_FP_MEMOP_DATASIZE(); |
| insn(loadStoreRegisterPreIndex(MEMOPSIZE, true, datasize == 128 ? MemOp_STORE_V128 : MemOp_STORE, simm, rn, rt)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE void stur(FPRegisterID rt, RegisterID rn, int simm) |
| { |
| CHECK_DATASIZE(); |
| insn(loadStoreRegisterUnscaledImmediate(MEMOPSIZE, true, datasize == 128 ? MemOp_STORE_V128 : MemOp_STORE, simm, rn, rt)); |
| } |
| |
| template<int dstsize, int srcsize> |
| ALWAYS_INLINE void ucvtf(FPRegisterID vd, RegisterID rn) |
| { |
| CHECK_DATASIZE_OF(dstsize); |
| CHECK_DATASIZE_OF(srcsize); |
| insn(floatingPointIntegerConversions(DATASIZE_OF(srcsize), DATASIZE_OF(dstsize), FPIntConvOp_UCVTF, rn, vd)); |
| } |
| |
| // Admin methods: |
| |
| AssemblerLabel labelIgnoringWatchpoints() |
| { |
| return m_buffer.label(); |
| } |
| |
| AssemblerLabel labelForWatchpoint() |
| { |
| AssemblerLabel result = m_buffer.label(); |
| if (static_cast<int>(result.m_offset) != m_indexOfLastWatchpoint) |
| result = label(); |
| m_indexOfLastWatchpoint = result.m_offset; |
| m_indexOfTailOfLastWatchpoint = result.m_offset + maxJumpReplacementSize(); |
| return result; |
| } |
| |
| AssemblerLabel label() |
| { |
| AssemblerLabel result = m_buffer.label(); |
| while (UNLIKELY(static_cast<int>(result.m_offset) < m_indexOfTailOfLastWatchpoint)) { |
| nop(); |
| result = m_buffer.label(); |
| } |
| return result; |
| } |
| |
| AssemblerLabel align(int alignment) |
| { |
| ASSERT(!(alignment & 3)); |
| while (!m_buffer.isAligned(alignment)) |
| brk(0); |
| return label(); |
| } |
| |
| static void* getRelocatedAddress(void* code, AssemblerLabel label) |
| { |
| ASSERT(label.isSet()); |
| return reinterpret_cast<void*>(reinterpret_cast<ptrdiff_t>(code) + label.m_offset); |
| } |
| |
| static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) |
| { |
| return b.m_offset - a.m_offset; |
| } |
| |
| int executableOffsetFor(int location) |
| { |
| if (!location) |
| return 0; |
| return static_cast<int32_t*>(m_buffer.data())[location / sizeof(int32_t) - 1]; |
| } |
| |
| void* unlinkedCode() { return m_buffer.data(); } |
| size_t codeSize() const { return m_buffer.codeSize(); } |
| |
| static unsigned getCallReturnOffset(AssemblerLabel call) |
| { |
| ASSERT(call.isSet()); |
| 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(AssemblerLabel from, AssemblerLabel to, JumpType type, Condition condition) |
| { |
| ASSERT(to.isSet()); |
| ASSERT(from.isSet()); |
| m_jumpsToLink.append(LinkRecord(from.m_offset, to.m_offset, type, condition)); |
| } |
| |
| void linkJump(AssemblerLabel from, AssemblerLabel to, JumpType type, Condition condition, bool is64Bit, RegisterID compareRegister) |
| { |
| ASSERT(to.isSet()); |
| ASSERT(from.isSet()); |
| m_jumpsToLink.append(LinkRecord(from.m_offset, to.m_offset, type, condition, is64Bit, compareRegister)); |
| } |
| |
| void linkJump(AssemblerLabel from, AssemblerLabel to, JumpType type, Condition condition, unsigned bitNumber, RegisterID compareRegister) |
| { |
| ASSERT(to.isSet()); |
| ASSERT(from.isSet()); |
| m_jumpsToLink.append(LinkRecord(from.m_offset, to.m_offset, type, condition, bitNumber, compareRegister)); |
| } |
| |
| void linkJump(AssemblerLabel from, AssemblerLabel to) |
| { |
| ASSERT(from.isSet()); |
| ASSERT(to.isSet()); |
| relinkJumpOrCall<false>(addressOf(from), addressOf(to)); |
| } |
| |
| static void linkJump(void* code, AssemblerLabel from, void* to) |
| { |
| ASSERT(from.isSet()); |
| relinkJumpOrCall<false>(addressOf(code, from), to); |
| } |
| |
| static void linkCall(void* code, AssemblerLabel from, void* to) |
| { |
| ASSERT(from.isSet()); |
| linkJumpOrCall<true>(addressOf(code, from) - 1, to); |
| } |
| |
| static void linkPointer(void* code, AssemblerLabel where, void* valuePtr) |
| { |
| linkPointer(addressOf(code, where), valuePtr); |
| } |
| |
| static void replaceWithJump(void* where, void* to) |
| { |
| intptr_t offset = (reinterpret_cast<intptr_t>(to) - reinterpret_cast<intptr_t>(where)) >> 2; |
| ASSERT(static_cast<int>(offset) == offset); |
| *static_cast<int*>(where) = unconditionalBranchImmediate(false, static_cast<int>(offset)); |
| cacheFlush(where, sizeof(int)); |
| } |
| |
| static ptrdiff_t maxJumpReplacementSize() |
| { |
| return 4; |
| } |
| |
| static void replaceWithLoad(void* where) |
| { |
| Datasize sf; |
| AddOp op; |
| SetFlags S; |
| int shift; |
| int imm12; |
| RegisterID rn; |
| RegisterID rd; |
| if (disassembleAddSubtractImmediate(where, sf, op, S, shift, imm12, rn, rd)) { |
| ASSERT(sf == Datasize_64); |
| ASSERT(op == AddOp_ADD); |
| ASSERT(!S); |
| ASSERT(!shift); |
| ASSERT(!(imm12 & ~0xff8)); |
| *static_cast<int*>(where) = loadStoreRegisterUnsignedImmediate(MemOpSize_64, false, MemOp_LOAD, encodePositiveImmediate<64>(imm12), rn, rd); |
| cacheFlush(where, sizeof(int)); |
| } |
| #if !ASSERT_DISABLED |
| else { |
| MemOpSize size; |
| bool V; |
| MemOp opc; |
| int imm12; |
| RegisterID rn; |
| RegisterID rt; |
| ASSERT(disassembleLoadStoreRegisterUnsignedImmediate(where, size, V, opc, imm12, rn, rt)); |
| ASSERT(size == MemOpSize_64); |
| ASSERT(!V); |
| ASSERT(opc == MemOp_LOAD); |
| ASSERT(!(imm12 & ~0x1ff)); |
| } |
| #endif |
| } |
| |
| static void replaceWithAddressComputation(void* where) |
| { |
| MemOpSize size; |
| bool V; |
| MemOp opc; |
| int imm12; |
| RegisterID rn; |
| RegisterID rt; |
| if (disassembleLoadStoreRegisterUnsignedImmediate(where, size, V, opc, imm12, rn, rt)) { |
| ASSERT(size == MemOpSize_64); |
| ASSERT(!V); |
| ASSERT(opc == MemOp_LOAD); |
| ASSERT(!(imm12 & ~0x1ff)); |
| *static_cast<int*>(where) = addSubtractImmediate(Datasize_64, AddOp_ADD, DontSetFlags, 0, imm12 * sizeof(void*), rn, rt); |
| cacheFlush(where, sizeof(int)); |
| } |
| #if !ASSERT_DISABLED |
| else { |
| Datasize sf; |
| AddOp op; |
| SetFlags S; |
| int shift; |
| int imm12; |
| RegisterID rn; |
| RegisterID rd; |
| ASSERT(disassembleAddSubtractImmediate(where, sf, op, S, shift, imm12, rn, rd)); |
| ASSERT(sf == Datasize_64); |
| ASSERT(op == AddOp_ADD); |
| ASSERT(!S); |
| ASSERT(!shift); |
| ASSERT(!(imm12 & ~0xff8)); |
| } |
| #endif |
| } |
| |
| static void repatchPointer(void* where, void* valuePtr) |
| { |
| linkPointer(static_cast<int*>(where), valuePtr, true); |
| } |
| |
| static void setPointer(int* address, void* valuePtr, RegisterID rd, bool flush) |
| { |
| uintptr_t value = reinterpret_cast<uintptr_t>(valuePtr); |
| address[0] = moveWideImediate(Datasize_64, MoveWideOp_Z, 0, getHalfword(value, 0), rd); |
| address[1] = moveWideImediate(Datasize_64, MoveWideOp_K, 1, getHalfword(value, 1), rd); |
| address[2] = moveWideImediate(Datasize_64, MoveWideOp_K, 2, getHalfword(value, 2), rd); |
| |
| if (flush) |
| cacheFlush(address, sizeof(int) * 3); |
| } |
| |
| static void repatchInt32(void* where, int32_t value) |
| { |
| int* address = static_cast<int*>(where); |
| |
| Datasize sf; |
| MoveWideOp opc; |
| int hw; |
| uint16_t imm16; |
| RegisterID rd; |
| bool expected = disassembleMoveWideImediate(address, sf, opc, hw, imm16, rd); |
| ASSERT_UNUSED(expected, expected && !sf && (opc == MoveWideOp_Z || opc == MoveWideOp_N) && !hw); |
| ASSERT(checkMovk<Datasize_32>(address[1], 1, rd)); |
| |
| if (value >= 0) { |
| address[0] = moveWideImediate(Datasize_32, MoveWideOp_Z, 0, getHalfword(value, 0), rd); |
| address[1] = moveWideImediate(Datasize_32, MoveWideOp_K, 1, getHalfword(value, 1), rd); |
| } else { |
| address[0] = moveWideImediate(Datasize_32, MoveWideOp_N, 0, ~getHalfword(value, 0), rd); |
| address[1] = moveWideImediate(Datasize_32, MoveWideOp_K, 1, getHalfword(value, 1), rd); |
| } |
| |
| cacheFlush(where, sizeof(int) * 2); |
| } |
| |
| static void* readPointer(void* where) |
| { |
| int* address = static_cast<int*>(where); |
| |
| Datasize sf; |
| MoveWideOp opc; |
| int hw; |
| uint16_t imm16; |
| RegisterID rdFirst, rd; |
| |
| bool expected = disassembleMoveWideImediate(address, sf, opc, hw, imm16, rdFirst); |
| ASSERT_UNUSED(expected, expected && sf && opc == MoveWideOp_Z && !hw); |
| uintptr_t result = imm16; |
| |
| expected = disassembleMoveWideImediate(address + 1, sf, opc, hw, imm16, rd); |
| ASSERT_UNUSED(expected, expected && sf && opc == MoveWideOp_K && hw == 1 && rd == rdFirst); |
| result |= static_cast<uintptr_t>(imm16) << 16; |
| |
| expected = disassembleMoveWideImediate(address + 2, sf, opc, hw, imm16, rd); |
| ASSERT_UNUSED(expected, expected && sf && opc == MoveWideOp_K && hw == 2 && rd == rdFirst); |
| result |= static_cast<uintptr_t>(imm16) << 32; |
| |
| return reinterpret_cast<void*>(result); |
| } |
| |
| static void* readCallTarget(void* from) |
| { |
| return readPointer(reinterpret_cast<int*>(from) - 4); |
| } |
| |
| static void relinkJump(void* from, void* to) |
| { |
| relinkJumpOrCall<false>(reinterpret_cast<int*>(from), to); |
| cacheFlush(from, sizeof(int)); |
| } |
| |
| static void relinkCall(void* from, void* to) |
| { |
| relinkJumpOrCall<true>(reinterpret_cast<int*>(from) - 1, to); |
| cacheFlush(reinterpret_cast<int*>(from) - 1, sizeof(int)); |
| } |
| |
| static void repatchCompact(void* where, int32_t value) |
| { |
| ASSERT(!(value & ~0x3ff8)); |
| |
| MemOpSize size; |
| bool V; |
| MemOp opc; |
| int imm12; |
| RegisterID rn; |
| RegisterID rt; |
| bool expected = disassembleLoadStoreRegisterUnsignedImmediate(where, size, V, opc, imm12, rn, rt); |
| ASSERT_UNUSED(expected, expected && size >= MemOpSize_32 && !V && opc == MemOp_LOAD); // expect 32/64 bit load to GPR. |
| |
| if (size == MemOpSize_32) |
| imm12 = encodePositiveImmediate<32>(value); |
| else |
| imm12 = encodePositiveImmediate<64>(value); |
| *static_cast<int*>(where) = loadStoreRegisterUnsignedImmediate(size, V, opc, imm12, rn, rt); |
| |
| cacheFlush(where, sizeof(int)); |
| } |
| |
| unsigned debugOffset() { return m_buffer.debugOffset(); } |
| |
| static void cacheFlush(void* code, size_t size) |
| { |
| #if OS(IOS) |
| sys_cache_control(kCacheFunctionPrepareForExecution, code, size); |
| #else |
| #error "The cacheFlush support is missing on this platform." |
| #endif |
| } |
| |
| // Assembler admin methods: |
| |
| int jumpSizeDelta(JumpType jumpType, JumpLinkType jumpLinkType) { return JUMP_ENUM_SIZE(jumpType) - JUMP_ENUM_SIZE(jumpLinkType); } |
| |
| static ALWAYS_INLINE bool linkRecordSourceComparator(const LinkRecord& a, const LinkRecord& b) |
| { |
| return a.from() < b.from(); |
| } |
| |
| bool canCompact(JumpType jumpType) |
| { |
| // Fixed jumps cannot be compacted |
| return (jumpType == JumpNoCondition) || (jumpType == JumpCondition) || (jumpType == JumpCompareAndBranch) || (jumpType == JumpTestBit); |
| } |
| |
| JumpLinkType computeJumpType(JumpType jumpType, const uint8_t* from, const uint8_t* to) |
| { |
| switch (jumpType) { |
| case JumpFixed: |
| return LinkInvalid; |
| case JumpNoConditionFixedSize: |
| return LinkJumpNoCondition; |
| case JumpConditionFixedSize: |
| return LinkJumpCondition; |
| case JumpCompareAndBranchFixedSize: |
| return LinkJumpCompareAndBranch; |
| case JumpTestBitFixedSize: |
| return LinkJumpTestBit; |
| case JumpNoCondition: |
| return LinkJumpNoCondition; |
| case JumpCondition: { |
| ASSERT(!(reinterpret_cast<intptr_t>(from) & 0x3)); |
| ASSERT(!(reinterpret_cast<intptr_t>(to) & 0x3)); |
| intptr_t relative = reinterpret_cast<intptr_t>(to) - (reinterpret_cast<intptr_t>(from)); |
| |
| if (((relative << 43) >> 43) == relative) |
| return LinkJumpConditionDirect; |
| |
| return LinkJumpCondition; |
| } |
| case JumpCompareAndBranch: { |
| ASSERT(!(reinterpret_cast<intptr_t>(from) & 0x3)); |
| ASSERT(!(reinterpret_cast<intptr_t>(to) & 0x3)); |
| intptr_t relative = reinterpret_cast<intptr_t>(to) - (reinterpret_cast<intptr_t>(from)); |
| |
| if (((relative << 43) >> 43) == relative) |
| return LinkJumpCompareAndBranchDirect; |
| |
| return LinkJumpCompareAndBranch; |
| } |
| case JumpTestBit: { |
| ASSERT(!(reinterpret_cast<intptr_t>(from) & 0x3)); |
| ASSERT(!(reinterpret_cast<intptr_t>(to) & 0x3)); |
| intptr_t relative = reinterpret_cast<intptr_t>(to) - (reinterpret_cast<intptr_t>(from)); |
| |
| if (((relative << 50) >> 50) == relative) |
| return LinkJumpTestBitDirect; |
| |
| return LinkJumpTestBit; |
| } |
| default: |
| ASSERT_NOT_REACHED(); |
| } |
| |
| return LinkJumpNoCondition; |
| } |
| |
| 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_buffer.data()); |
| while (ptr < end) |
| offsets[ptr++] = offset; |
| } |
| |
| Vector<LinkRecord, 0, UnsafeVectorOverflow>& jumpsToLink() |
| { |
| std::sort(m_jumpsToLink.begin(), m_jumpsToLink.end(), linkRecordSourceComparator); |
| return m_jumpsToLink; |
| } |
| |
| void ALWAYS_INLINE link(LinkRecord& record, uint8_t* from, uint8_t* to) |
| { |
| switch (record.linkType()) { |
| case LinkJumpNoCondition: |
| linkJumpOrCall<false>(reinterpret_cast<int*>(from), to); |
| break; |
| case LinkJumpConditionDirect: |
| linkConditionalBranch<true>(record.condition(), reinterpret_cast<int*>(from), to); |
| break; |
| case LinkJumpCondition: |
| linkConditionalBranch<false>(record.condition(), reinterpret_cast<int*>(from) - 1, to); |
| break; |
| case LinkJumpCompareAndBranchDirect: |
| linkCompareAndBranch<true>(record.condition(), record.is64Bit(), record.compareRegister(), reinterpret_cast<int*>(from), to); |
| break; |
| case LinkJumpCompareAndBranch: |
| linkCompareAndBranch<false>(record.condition(), record.is64Bit(), record.compareRegister(), reinterpret_cast<int*>(from) - 1, to); |
| break; |
| case LinkJumpTestBitDirect: |
| linkTestAndBranch<true>(record.condition(), record.bitNumber(), record.compareRegister(), reinterpret_cast<int*>(from), to); |
| break; |
| case LinkJumpTestBit: |
| linkTestAndBranch<false>(record.condition(), record.bitNumber(), record.compareRegister(), reinterpret_cast<int*>(from) - 1, to); |
| break; |
| default: |
| ASSERT_NOT_REACHED(); |
| break; |
| } |
| } |
| |
| private: |
| template<Datasize size> |
| static bool checkMovk(int insn, int _hw, RegisterID _rd) |
| { |
| Datasize sf; |
| MoveWideOp opc; |
| int hw; |
| uint16_t imm16; |
| RegisterID rd; |
| bool expected = disassembleMoveWideImediate(&insn, sf, opc, hw, imm16, rd); |
| |
| return expected |
| && sf == size |
| && opc == MoveWideOp_K |
| && hw == _hw |
| && rd == _rd; |
| } |
| |
| static void linkPointer(int* address, void* valuePtr, bool flush = false) |
| { |
| Datasize sf; |
| MoveWideOp opc; |
| int hw; |
| uint16_t imm16; |
| RegisterID rd; |
| bool expected = disassembleMoveWideImediate(address, sf, opc, hw, imm16, rd); |
| ASSERT_UNUSED(expected, expected && sf && opc == MoveWideOp_Z && !hw); |
| ASSERT(checkMovk<Datasize_64>(address[1], 1, rd)); |
| ASSERT(checkMovk<Datasize_64>(address[2], 2, rd)); |
| |
| setPointer(address, valuePtr, rd, flush); |
| } |
| |
| template<bool isCall> |
| static void linkJumpOrCall(int* from, void* to) |
| { |
| bool link; |
| int imm26; |
| bool isUnconditionalBranchImmediateOrNop = disassembleUnconditionalBranchImmediate(from, link, imm26) || disassembleNop(from); |
| |
| ASSERT_UNUSED(isUnconditionalBranchImmediateOrNop, isUnconditionalBranchImmediateOrNop); |
| ASSERT_UNUSED(isCall, (link == isCall) || disassembleNop(from)); |
| ASSERT(!(reinterpret_cast<intptr_t>(from) & 3)); |
| ASSERT(!(reinterpret_cast<intptr_t>(to) & 3)); |
| intptr_t offset = (reinterpret_cast<intptr_t>(to) - reinterpret_cast<intptr_t>(from)) >> 2; |
| ASSERT(static_cast<int>(offset) == offset); |
| |
| *from = unconditionalBranchImmediate(isCall, static_cast<int>(offset)); |
| } |
| |
| template<bool isDirect> |
| static void linkCompareAndBranch(Condition condition, bool is64Bit, RegisterID rt, int* from, void* to) |
| { |
| ASSERT(!(reinterpret_cast<intptr_t>(from) & 3)); |
| ASSERT(!(reinterpret_cast<intptr_t>(to) & 3)); |
| intptr_t offset = (reinterpret_cast<intptr_t>(to) - reinterpret_cast<intptr_t>(from)) >> 2; |
| ASSERT(((offset << 38) >> 38) == offset); |
| |
| bool useDirect = ((offset << 45) >> 45) == offset; // Fits in 19 bits |
| ASSERT(!isDirect || useDirect); |
| |
| if (useDirect || isDirect) { |
| *from = compareAndBranchImmediate(is64Bit ? Datasize_64 : Datasize_32, condition == ConditionNE, static_cast<int>(offset), rt); |
| if (!isDirect) |
| *(from + 1) = nopPseudo(); |
| } else { |
| *from = compareAndBranchImmediate(is64Bit ? Datasize_64 : Datasize_32, invert(condition) == ConditionNE, 2, rt); |
| linkJumpOrCall<false>(from + 1, to); |
| } |
| } |
| |
| template<bool isDirect> |
| static void linkConditionalBranch(Condition condition, int* from, void* to) |
| { |
| ASSERT(!(reinterpret_cast<intptr_t>(from) & 3)); |
| ASSERT(!(reinterpret_cast<intptr_t>(to) & 3)); |
| intptr_t offset = (reinterpret_cast<intptr_t>(to) - reinterpret_cast<intptr_t>(from)) >> 2; |
| ASSERT(((offset << 38) >> 38) == offset); |
| |
| bool useDirect = ((offset << 45) >> 45) == offset; // Fits in 19 bits |
| ASSERT(!isDirect || useDirect); |
| |
| if (useDirect || isDirect) { |
| *from = conditionalBranchImmediate(static_cast<int>(offset), condition); |
| if (!isDirect) |
| *(from + 1) = nopPseudo(); |
| } else { |
| *from = conditionalBranchImmediate(2, invert(condition)); |
| linkJumpOrCall<false>(from + 1, to); |
| } |
| } |
| |
| template<bool isDirect> |
| static void linkTestAndBranch(Condition condition, unsigned bitNumber, RegisterID rt, int* from, void* to) |
| { |
| ASSERT(!(reinterpret_cast<intptr_t>(from) & 3)); |
| ASSERT(!(reinterpret_cast<intptr_t>(to) & 3)); |
| intptr_t offset = (reinterpret_cast<intptr_t>(to) - reinterpret_cast<intptr_t>(from)) >> 2; |
| ASSERT(static_cast<int>(offset) == offset); |
| ASSERT(((offset << 38) >> 38) == offset); |
| |
| bool useDirect = ((offset << 50) >> 50) == offset; // Fits in 14 bits |
| ASSERT(!isDirect || useDirect); |
| |
| if (useDirect || isDirect) { |
| *from = testAndBranchImmediate(condition == ConditionNE, static_cast<int>(bitNumber), static_cast<int>(offset), rt); |
| if (!isDirect) |
| *(from + 1) = nopPseudo(); |
| } else { |
| *from = testAndBranchImmediate(invert(condition) == ConditionNE, static_cast<int>(bitNumber), 2, rt); |
| linkJumpOrCall<false>(from + 1, to); |
| } |
| } |
| |
| template<bool isCall> |
| static void relinkJumpOrCall(int* from, void* to) |
| { |
| if (!isCall && disassembleNop(from)) { |
| unsigned op01; |
| int imm19; |
| Condition condition; |
| bool isConditionalBranchImmediate = disassembleConditionalBranchImmediate(from - 1, op01, imm19, condition); |
| |
| if (isConditionalBranchImmediate) { |
| ASSERT_UNUSED(op01, !op01); |
| ASSERT_UNUSED(isCall, !isCall); |
| |
| if (imm19 == 8) |
| condition = invert(condition); |
| |
| linkConditionalBranch<false>(condition, from - 1, to); |
| return; |
| } |
| |
| Datasize opSize; |
| bool op; |
| RegisterID rt; |
| bool isCompareAndBranchImmediate = disassembleCompareAndBranchImmediate(from - 1, opSize, op, imm19, rt); |
| |
| if (isCompareAndBranchImmediate) { |
| if (imm19 == 8) |
| op = !op; |
| |
| linkCompareAndBranch<false>(op ? ConditionNE : ConditionEQ, opSize == Datasize_64, rt, from - 1, to); |
| return; |
| } |
| |
| int imm14; |
| unsigned bitNumber; |
| bool isTestAndBranchImmediate = disassembleTestAndBranchImmediate(from - 1, op, bitNumber, imm14, rt); |
| |
| if (isTestAndBranchImmediate) { |
| if (imm14 == 8) |
| op = !op; |
| |
| linkTestAndBranch<false>(op ? ConditionNE : ConditionEQ, bitNumber, rt, from - 1, to); |
| return; |
| } |
| } |
| |
| linkJumpOrCall<isCall>(from, to); |
| } |
| |
| static int* addressOf(void* code, AssemblerLabel label) |
| { |
| return reinterpret_cast<int*>(static_cast<char*>(code) + label.m_offset); |
| } |
| |
| int* addressOf(AssemblerLabel label) |
| { |
| return addressOf(m_buffer.data(), label); |
| } |
| |
| static RegisterID disassembleXOrSp(int reg) { return reg == 31 ? ARM64Registers::sp : static_cast<RegisterID>(reg); } |
| static RegisterID disassembleXOrZr(int reg) { return reg == 31 ? ARM64Registers::zr : static_cast<RegisterID>(reg); } |
| static RegisterID disassembleXOrZrOrSp(bool useZr, int reg) { return reg == 31 ? (useZr ? ARM64Registers::zr : ARM64Registers::sp) : static_cast<RegisterID>(reg); } |
| |
| static bool disassembleAddSubtractImmediate(void* address, Datasize& sf, AddOp& op, SetFlags& S, int& shift, int& imm12, RegisterID& rn, RegisterID& rd) |
| { |
| int insn = *static_cast<int*>(address); |
| sf = static_cast<Datasize>((insn >> 31) & 1); |
| op = static_cast<AddOp>((insn >> 30) & 1); |
| S = static_cast<SetFlags>((insn >> 29) & 1); |
| shift = (insn >> 22) & 3; |
| imm12 = (insn >> 10) & 0x3ff; |
| rn = disassembleXOrSp((insn >> 5) & 0x1f); |
| rd = disassembleXOrZrOrSp(S, insn & 0x1f); |
| return (insn & 0x1f000000) == 0x11000000; |
| } |
| |
| static bool disassembleLoadStoreRegisterUnsignedImmediate(void* address, MemOpSize& size, bool& V, MemOp& opc, int& imm12, RegisterID& rn, RegisterID& rt) |
| { |
| int insn = *static_cast<int*>(address); |
| size = static_cast<MemOpSize>((insn >> 30) & 3); |
| V = (insn >> 26) & 1; |
| opc = static_cast<MemOp>((insn >> 22) & 3); |
| imm12 = (insn >> 10) & 0xfff; |
| rn = disassembleXOrSp((insn >> 5) & 0x1f); |
| rt = disassembleXOrZr(insn & 0x1f); |
| return (insn & 0x3b000000) == 0x39000000; |
| } |
| |
| static bool disassembleMoveWideImediate(void* address, Datasize& sf, MoveWideOp& opc, int& hw, uint16_t& imm16, RegisterID& rd) |
| { |
| int insn = *static_cast<int*>(address); |
| sf = static_cast<Datasize>((insn >> 31) & 1); |
| opc = static_cast<MoveWideOp>((insn >> 29) & 3); |
| hw = (insn >> 21) & 3; |
| imm16 = insn >> 5; |
| rd = disassembleXOrZr(insn & 0x1f); |
| return (insn & 0x1f800000) == 0x12800000; |
| } |
| |
| static bool disassembleNop(void* address) |
| { |
| unsigned insn = *static_cast<unsigned*>(address); |
| return insn == 0xd503201f; |
| } |
| |
| static bool disassembleCompareAndBranchImmediate(void* address, Datasize& sf, bool& op, int& imm19, RegisterID& rt) |
| { |
| int insn = *static_cast<int*>(address); |
| sf = static_cast<Datasize>((insn >> 31) & 1); |
| op = (insn >> 24) & 0x1; |
| imm19 = (insn << 8) >> 13; |
| rt = static_cast<RegisterID>(insn & 0x1f); |
| return (insn & 0x7e000000) == 0x34000000; |
| |
| } |
| |
| static bool disassembleConditionalBranchImmediate(void* address, unsigned& op01, int& imm19, Condition &condition) |
| { |
| int insn = *static_cast<int*>(address); |
| op01 = ((insn >> 23) & 0x2) | ((insn >> 4) & 0x1); |
| imm19 = (insn << 8) >> 13; |
| condition = static_cast<Condition>(insn & 0xf); |
| return (insn & 0xfe000000) == 0x54000000; |
| } |
| |
| static bool disassembleTestAndBranchImmediate(void* address, bool& op, unsigned& bitNumber, int& imm14, RegisterID& rt) |
| { |
| int insn = *static_cast<int*>(address); |
| op = (insn >> 24) & 0x1; |
| imm14 = (insn << 13) >> 18; |
| bitNumber = static_cast<unsigned>((((insn >> 26) & 0x20)) | ((insn > 19) & 0x1f)); |
| rt = static_cast<RegisterID>(insn & 0x1f); |
| return (insn & 0x7e000000) == 0x36000000; |
| |
| } |
| |
| static bool disassembleUnconditionalBranchImmediate(void* address, bool& op, int& imm26) |
| { |
| int insn = *static_cast<int*>(address); |
| op = (insn >> 31) & 1; |
| imm26 = (insn << 6) >> 6; |
| return (insn & 0x7c000000) == 0x14000000; |
| } |
| |
| static int xOrSp(RegisterID reg) { ASSERT(!isZr(reg)); return reg; } |
| static int xOrZr(RegisterID reg) { ASSERT(!isSp(reg)); return reg & 31; } |
| static FPRegisterID xOrZrAsFPR(RegisterID reg) { return static_cast<FPRegisterID>(xOrZr(reg)); } |
| static int xOrZrOrSp(bool useZr, RegisterID reg) { return useZr ? xOrZr(reg) : xOrSp(reg); } |
| |
| ALWAYS_INLINE void insn(int instruction) |
| { |
| m_buffer.putInt(instruction); |
| } |
| |
| ALWAYS_INLINE static int addSubtractExtendedRegister(Datasize sf, AddOp op, SetFlags S, RegisterID rm, ExtendType option, int imm3, RegisterID rn, RegisterID rd) |
| { |
| ASSERT(imm3 < 5); |
| // The only allocated values for opt is 0. |
| const int opt = 0; |
| return (0x0b200000 | sf << 31 | op << 30 | S << 29 | opt << 22 | xOrZr(rm) << 16 | option << 13 | (imm3 & 0x7) << 10 | xOrSp(rn) << 5 | xOrZrOrSp(S, rd)); |
| } |
| |
| ALWAYS_INLINE static int addSubtractImmediate(Datasize sf, AddOp op, SetFlags S, int shift, int imm12, RegisterID rn, RegisterID rd) |
| { |
| ASSERT(shift < 2); |
| ASSERT(isUInt12(imm12)); |
| return (0x11000000 | sf << 31 | op << 30 | S << 29 | shift << 22 | (imm12 & 0xfff) << 10 | xOrSp(rn) << 5 | xOrZrOrSp(S, rd)); |
| } |
| |
| ALWAYS_INLINE static int addSubtractShiftedRegister(Datasize sf, AddOp op, SetFlags S, ShiftType shift, RegisterID rm, int imm6, RegisterID rn, RegisterID rd) |
| { |
| ASSERT(shift < 3); |
| ASSERT(!(imm6 & (sf ? ~63 : ~31))); |
| return (0x0b000000 | sf << 31 | op << 30 | S << 29 | shift << 22 | xOrZr(rm) << 16 | (imm6 & 0x3f) << 10 | xOrZr(rn) << 5 | xOrZr(rd)); |
| } |
| |
| ALWAYS_INLINE static int addSubtractWithCarry(Datasize sf, AddOp op, SetFlags S, RegisterID rm, RegisterID rn, RegisterID rd) |
| { |
| const int opcode2 = 0; |
| return (0x1a000000 | sf << 31 | op << 30 | S << 29 | xOrZr(rm) << 16 | opcode2 << 10 | xOrZr(rn) << 5 | xOrZr(rd)); |
| } |
| |
| ALWAYS_INLINE static int bitfield(Datasize sf, BitfieldOp opc, int immr, int imms, RegisterID rn, RegisterID rd) |
| { |
| ASSERT(immr < (sf ? 64 : 32)); |
| ASSERT(imms < (sf ? 64 : 32)); |
| const int N = sf; |
| return (0x13000000 | sf << 31 | opc << 29 | N << 22 | immr << 16 | imms << 10 | xOrZr(rn) << 5 | xOrZr(rd)); |
| } |
| |
| // 'op' means negate |
| ALWAYS_INLINE static int compareAndBranchImmediate(Datasize sf, bool op, int32_t imm19, RegisterID rt) |
| { |
| ASSERT(imm19 == (imm19 << 13) >> 13); |
| return (0x34000000 | sf << 31 | op << 24 | (imm19 & 0x7ffff) << 5 | xOrZr(rt)); |
| } |
| |
| ALWAYS_INLINE static int conditionalBranchImmediate(int32_t imm19, Condition cond) |
| { |
| ASSERT(imm19 == (imm19 << 13) >> 13); |
| ASSERT(!(cond & ~15)); |
| // The only allocated values for o1 & o0 are 0. |
| const int o1 = 0; |
| const int o0 = 0; |
| return (0x54000000 | o1 << 24 | (imm19 & 0x7ffff) << 5 | o0 << 4 | cond); |
| } |
| |
| ALWAYS_INLINE static int conditionalCompareImmediate(Datasize sf, AddOp op, int imm5, Condition cond, RegisterID rn, int nzcv) |
| { |
| ASSERT(!(imm5 & ~0x1f)); |
| ASSERT(nzcv < 16); |
| const int S = 1; |
| const int o2 = 0; |
| const int o3 = 0; |
| return (0x1a400800 | sf << 31 | op << 30 | S << 29 | (imm5 & 0x1f) << 16 | cond << 12 | o2 << 10 | xOrZr(rn) << 5 | o3 << 4 | nzcv); |
| } |
| |
| ALWAYS_INLINE static int conditionalCompareRegister(Datasize sf, AddOp op, RegisterID rm, Condition cond, RegisterID rn, int nzcv) |
| { |
| ASSERT(nzcv < 16); |
| const int S = 1; |
| const int o2 = 0; |
| const int o3 = 0; |
| return (0x1a400000 | sf << 31 | op << 30 | S << 29 | xOrZr(rm) << 16 | cond << 12 | o2 << 10 | xOrZr(rn) << 5 | o3 << 4 | nzcv); |
| } |
| |
| // 'op' means negate |
| // 'op2' means increment |
| ALWAYS_INLINE static int conditionalSelect(Datasize sf, bool op, RegisterID rm, Condition cond, bool op2, RegisterID rn, RegisterID rd) |
| { |
| const int S = 0; |
| return (0x1a800000 | sf << 31 | op << 30 | S << 29 | xOrZr(rm) << 16 | cond << 12 | op2 << 10 | xOrZr(rn) << 5 | xOrZr(rd)); |
| } |
| |
| ALWAYS_INLINE static int dataProcessing1Source(Datasize sf, DataOp1Source opcode, RegisterID rn, RegisterID rd) |
| { |
| const int S = 0; |
| const int opcode2 = 0; |
| return (0x5ac00000 | sf << 31 | S << 29 | opcode2 << 16 | opcode << 10 | xOrZr(rn) << 5 | xOrZr(rd)); |
| } |
| |
| ALWAYS_INLINE static int dataProcessing2Source(Datasize sf, RegisterID rm, DataOp2Source opcode, RegisterID rn, RegisterID rd) |
| { |
| const int S = 0; |
| return (0x1ac00000 | sf << 31 | S << 29 | xOrZr(rm) << 16 | opcode << 10 | xOrZr(rn) << 5 | xOrZr(rd)); |
| } |
| |
| ALWAYS_INLINE static int dataProcessing3Source(Datasize sf, DataOp3Source opcode, RegisterID rm, RegisterID ra, RegisterID rn, RegisterID rd) |
| { |
| int op54 = opcode >> 4; |
| int op31 = (opcode >> 1) & 7; |
| int op0 = opcode & 1; |
| return (0x1b000000 | sf << 31 | op54 << 29 | op31 << 21 | xOrZr(rm) << 16 | op0 << 15 | xOrZr(ra) << 10 | xOrZr(rn) << 5 | xOrZr(rd)); |
| } |
| |
| ALWAYS_INLINE static int excepnGeneration(ExcepnOp opc, uint16_t imm16, int LL) |
| { |
| ASSERT((opc == ExcepnOp_BREAKPOINT || opc == ExcepnOp_HALT) ? !LL : (LL && (LL < 4))); |
| const int op2 = 0; |
| return (0xd4000000 | opc << 21 | imm16 << 5 | op2 << 2 | LL); |
| } |
| |
| ALWAYS_INLINE static int extract(Datasize sf, RegisterID rm, int imms, RegisterID rn, RegisterID rd) |
| { |
| ASSERT(imms < (sf ? 64 : 32)); |
| const int op21 = 0; |
| const int N = sf; |
| const int o0 = 0; |
| return (0x13800000 | sf << 31 | op21 << 29 | N << 22 | o0 << 21 | xOrZr(rm) << 16 | imms << 10 | xOrZr(rn) << 5 | xOrZr(rd)); |
| } |
| |
| ALWAYS_INLINE static int floatingPointCompare(Datasize type, FPRegisterID rm, FPRegisterID rn, FPCmpOp opcode2) |
| { |
| const int M = 0; |
| const int S = 0; |
| const int op = 0; |
| return (0x1e202000 | M << 31 | S << 29 | type << 22 | rm << 16 | op << 14 | rn << 5 | opcode2); |
| } |
| |
| ALWAYS_INLINE static int floatingPointConditionalCompare(Datasize type, FPRegisterID rm, Condition cond, FPRegisterID rn, FPCondCmpOp op, int nzcv) |
| { |
| ASSERT(nzcv < 16); |
| const int M = 0; |
| const int S = 0; |
| return (0x1e200400 | M << 31 | S << 29 | type << 22 | rm << 16 | cond << 12 | rn << 5 | op << 4 | nzcv); |
| } |
| |
| ALWAYS_INLINE static int floatingPointConditionalSelect(Datasize type, FPRegisterID rm, Condition cond, FPRegisterID rn, FPRegisterID rd) |
| { |
| const int M = 0; |
| const int S = 0; |
| return (0x1e200c00 | M << 31 | S << 29 | type << 22 | rm << 16 | cond << 12 | rn << 5 | rd); |
| } |
| |
| ALWAYS_INLINE static int floatingPointImmediate(Datasize type, int imm8, FPRegisterID rd) |
| { |
| const int M = 0; |
| const int S = 0; |
| const int imm5 = 0; |
| return (0x1e201000 | M << 31 | S << 29 | type << 22 | (imm8 & 0xff) << 13 | imm5 << 5 | rd); |
| } |
| |
| ALWAYS_INLINE static int floatingPointIntegerConversions(Datasize sf, Datasize type, FPIntConvOp rmodeOpcode, FPRegisterID rn, FPRegisterID rd) |
| { |
| const int S = 0; |
| return (0x1e200000 | sf << 31 | S << 29 | type << 22 | rmodeOpcode << 16 | rn << 5 | rd); |
| } |
| |
| ALWAYS_INLINE static int floatingPointIntegerConversions(Datasize sf, Datasize type, FPIntConvOp rmodeOpcode, FPRegisterID rn, RegisterID rd) |
| { |
| return floatingPointIntegerConversions(sf, type, rmodeOpcode, rn, xOrZrAsFPR(rd)); |
| } |
| |
| ALWAYS_INLINE static int floatingPointIntegerConversions(Datasize sf, Datasize type, FPIntConvOp rmodeOpcode, RegisterID rn, FPRegisterID rd) |
| { |
| return floatingPointIntegerConversions(sf, type, rmodeOpcode, xOrZrAsFPR(rn), rd); |
| } |
| |
| ALWAYS_INLINE static int floatingPointDataProcessing1Source(Datasize type, FPDataOp1Source opcode, FPRegisterID rn, FPRegisterID rd) |
| { |
| const int M = 0; |
| const int S = 0; |
| return (0x1e204000 | M << 31 | S << 29 | type << 22 | opcode << 15 | rn << 5 | rd); |
| } |
| |
| ALWAYS_INLINE static int floatingPointDataProcessing2Source(Datasize type, FPRegisterID rm, FPDataOp2Source opcode, FPRegisterID rn, FPRegisterID rd) |
| { |
| const int M = 0; |
| const int S = 0; |
| return (0x1e200800 | M << 31 | S << 29 | type << 22 | rm << 16 | opcode << 12 | rn << 5 | rd); |
| } |
| |
| // 'o1' means negate |
| ALWAYS_INLINE static int floatingPointDataProcessing3Source(Datasize type, bool o1, FPRegisterID rm, AddOp o2, FPRegisterID ra, FPRegisterID rn, FPRegisterID rd) |
| { |
| const int M = 0; |
| const int S = 0; |
| return (0x1f000000 | M << 31 | S << 29 | type << 22 | o1 << 21 | rm << 16 | o2 << 15 | ra << 10 | rn << 5 | rd); |
| } |
| |
| // 'V' means vector |
| ALWAYS_INLINE static int loadRegisterLiteral(LdrLiteralOp opc, bool V, int imm19, FPRegisterID rt) |
| { |
| ASSERT(((imm19 << 13) >> 13) == imm19); |
| return (0x18000000 | opc << 30 | V << 26 | (imm19 & 0x7ffff) << 5 | rt); |
| } |
| |
| ALWAYS_INLINE static int loadRegisterLiteral(LdrLiteralOp opc, bool V, int imm19, RegisterID rt) |
| { |
| return loadRegisterLiteral(opc, V, imm19, xOrZrAsFPR(rt)); |
| } |
| |
| // 'V' means vector |
| ALWAYS_INLINE static int loadStoreRegisterPostIndex(MemOpSize size, bool V, MemOp opc, int imm9, RegisterID rn, FPRegisterID rt) |
| { |
| ASSERT(!(size && V && (opc & 2))); // Maximum vector size is 128 bits. |
| ASSERT(!((size & 2) && !V && (opc == 3))); // signed 32-bit load must be extending from 8/16 bits. |
| ASSERT(isInt9(imm9)); |
| return (0x38000400 | size << 30 | V << 26 | opc << 22 | (imm9 & 0x1ff) << 12 | xOrSp(rn) << 5 | rt); |
| } |
| |
| ALWAYS_INLINE static int loadStoreRegisterPostIndex(MemOpSize size, bool V, MemOp opc, int imm9, RegisterID rn, RegisterID rt) |
| { |
| return loadStoreRegisterPostIndex(size, V, opc, imm9, rn, xOrZrAsFPR(rt)); |
| } |
| |
| // 'V' means vector |
| ALWAYS_INLINE static int loadStoreRegisterPreIndex(MemOpSize size, bool V, MemOp opc, int imm9, RegisterID rn, FPRegisterID rt) |
| { |
| ASSERT(!(size && V && (opc & 2))); // Maximum vector size is 128 bits. |
| ASSERT(!((size & 2) && !V && (opc == 3))); // signed 32-bit load must be extending from 8/16 bits. |
| ASSERT(isInt9(imm9)); |
| return (0x38000c00 | size << 30 | V << 26 | opc << 22 | (imm9 & 0x1ff) << 12 | xOrSp(rn) << 5 | rt); |
| } |
| |
| ALWAYS_INLINE static int loadStoreRegisterPreIndex(MemOpSize size, bool V, MemOp opc, int imm9, RegisterID rn, RegisterID rt) |
| { |
| return loadStoreRegisterPreIndex(size, V, opc, imm9, rn, xOrZrAsFPR(rt)); |
| } |
| |
| // 'V' means vector |
| // 'S' means shift rm |
| ALWAYS_INLINE static int loadStoreRegisterRegisterOffset(MemOpSize size, bool V, MemOp opc, RegisterID rm, ExtendType option, bool S, RegisterID rn, FPRegisterID rt) |
| { |
| ASSERT(!(size && V && (opc & 2))); // Maximum vector size is 128 bits. |
| ASSERT(!((size & 2) && !V && (opc == 3))); // signed 32-bit load must be extending from 8/16 bits. |
| ASSERT(option & 2); // The ExtendType for the address must be 32/64 bit, signed or unsigned - not 8/16bit. |
| return (0x38200800 | size << 30 | V << 26 | opc << 22 | xOrZr(rm) << 16 | option << 13 | S << 12 | xOrSp(rn) << 5 | rt); |
| } |
| |
| ALWAYS_INLINE static int loadStoreRegisterRegisterOffset(MemOpSize size, bool V, MemOp opc, RegisterID rm, ExtendType option, bool S, RegisterID rn, RegisterID rt) |
| { |
| return loadStoreRegisterRegisterOffset(size, V, opc, rm, option, S, rn, xOrZrAsFPR(rt)); |
| } |
| |
| // 'V' means vector |
| ALWAYS_INLINE static int loadStoreRegisterUnscaledImmediate(MemOpSize size, bool V, MemOp opc, int imm9, RegisterID rn, FPRegisterID rt) |
| { |
| ASSERT(!(size && V && (opc & 2))); // Maximum vector size is 128 bits. |
| ASSERT(!((size & 2) && !V && (opc == 3))); // signed 32-bit load must be extending from 8/16 bits. |
| ASSERT(isInt9(imm9)); |
| return (0x38000000 | size << 30 | V << 26 | opc << 22 | (imm9 & 0x1ff) << 12 | xOrSp(rn) << 5 | rt); |
| } |
| |
| ALWAYS_INLINE static int loadStoreRegisterUnscaledImmediate(MemOpSize size, bool V, MemOp opc, int imm9, RegisterID rn, RegisterID rt) |
| { |
| ASSERT(isInt9(imm9)); |
| return loadStoreRegisterUnscaledImmediate(size, V, opc, imm9, rn, xOrZrAsFPR(rt)); |
| } |
| |
| // 'V' means vector |
| ALWAYS_INLINE static int loadStoreRegisterUnsignedImmediate(MemOpSize size, bool V, MemOp opc, int imm12, RegisterID rn, FPRegisterID rt) |
| { |
| ASSERT(!(size && V && (opc & 2))); // Maximum vector size is 128 bits. |
| ASSERT(!((size & 2) && !V && (opc == 3))); // signed 32-bit load must be extending from 8/16 bits. |
| ASSERT(isUInt12(imm12)); |
| return (0x39000000 | size << 30 | V << 26 | opc << 22 | (imm12 & 0xfff) << 10 | xOrSp(rn) << 5 | rt); |
| } |
| |
| ALWAYS_INLINE static int loadStoreRegisterUnsignedImmediate(MemOpSize size, bool V, MemOp opc, int imm12, RegisterID rn, RegisterID rt) |
| { |
| return loadStoreRegisterUnsignedImmediate(size, V, opc, imm12, rn, xOrZrAsFPR(rt)); |
| } |
| |
| ALWAYS_INLINE static int logicalImmediate(Datasize sf, LogicalOp opc, int N_immr_imms, RegisterID rn, RegisterID rd) |
| { |
| ASSERT(!(N_immr_imms & (sf ? ~0x1fff : ~0xfff))); |
| return (0x12000000 | sf << 31 | opc << 29 | N_immr_imms << 10 | xOrZr(rn) << 5 | xOrZrOrSp(opc == LogicalOp_ANDS, rd)); |
| } |
| |
| // 'N' means negate rm |
| ALWAYS_INLINE static int logicalShiftedRegister(Datasize sf, LogicalOp opc, ShiftType shift, bool N, RegisterID rm, int imm6, RegisterID rn, RegisterID rd) |
| { |
| ASSERT(!(imm6 & (sf ? ~63 : ~31))); |
| return (0x0a000000 | sf << 31 | opc << 29 | shift << 22 | N << 21 | xOrZr(rm) << 16 | (imm6 & 0x3f) << 10 | xOrZr(rn) << 5 | xOrZr(rd)); |
| } |
| |
| ALWAYS_INLINE static int moveWideImediate(Datasize sf, MoveWideOp opc, int hw, uint16_t imm16, RegisterID rd) |
| { |
| ASSERT(hw < (sf ? 4 : 2)); |
| return (0x12800000 | sf << 31 | opc << 29 | hw << 21 | (int)imm16 << 5 | xOrZr(rd)); |
| } |
| |
| // 'op' means link |
| ALWAYS_INLINE static int unconditionalBranchImmediate(bool op, int32_t imm26) |
| { |
| ASSERT(imm26 == (imm26 << 6) >> 6); |
| return (0x14000000 | op << 31 | (imm26 & 0x3ffffff)); |
| } |
| |
| // 'op' means page |
| ALWAYS_INLINE static int pcRelative(bool op, int32_t imm21, RegisterID rd) |
| { |
| ASSERT(imm21 == (imm21 << 11) >> 11); |
| int32_t immlo = imm21 & 3; |
| int32_t immhi = (imm21 >> 2) & 0x7ffff; |
| return (0x10000000 | op << 31 | immlo << 29 | immhi << 5 | xOrZr(rd)); |
| } |
| |
| ALWAYS_INLINE static int system(bool L, int op0, int op1, int crn, int crm, int op2, RegisterID rt) |
| { |
| return (0xd5000000 | L << 21 | op0 << 19 | op1 << 16 | crn << 12 | crm << 8 | op2 << 5 | xOrZr(rt)); |
| } |
| |
| ALWAYS_INLINE static int hintPseudo(int imm) |
| { |
| ASSERT(!(imm & ~0x7f)); |
| return system(0, 0, 3, 2, (imm >> 3) & 0xf, imm & 0x7, ARM64Registers::zr); |
| } |
| |
| ALWAYS_INLINE static int nopPseudo() |
| { |
| return hintPseudo(0); |
| } |
| |
| // 'op' means negate |
| ALWAYS_INLINE static int testAndBranchImmediate(bool op, int b50, int imm14, RegisterID rt) |
| { |
| ASSERT(!(b50 & ~0x3f)); |
| ASSERT(imm14 == (imm14 << 18) >> 18); |
| int b5 = b50 >> 5; |
| int b40 = b50 & 0x1f; |
| return (0x36000000 | b5 << 31 | op << 24 | b40 << 19 | (imm14 & 0x3fff) << 5 | xOrZr(rt)); |
| } |
| |
| ALWAYS_INLINE static int unconditionalBranchRegister(BranchType opc, RegisterID rn) |
| { |
| // The only allocated values for op2 is 0x1f, for op3 & op4 are 0. |
| const int op2 = 0x1f; |
| const int op3 = 0; |
| const int op4 = 0; |
| return (0xd6000000 | opc << 21 | op2 << 16 | op3 << 10 | xOrZr(rn) << 5 | op4); |
| } |
| |
| AssemblerBuffer m_buffer; |
| Vector<LinkRecord, 0, UnsafeVectorOverflow> m_jumpsToLink; |
| int m_indexOfLastWatchpoint; |
| int m_indexOfTailOfLastWatchpoint; |
| }; |
| |
| } // namespace JSC |
| |
| #undef CHECK_DATASIZE_OF |
| #undef DATASIZE_OF |
| #undef MEMOPSIZE_OF |
| #undef CHECK_DATASIZE |
| #undef DATASIZE |
| #undef MEMOPSIZE |
| #undef CHECK_FP_MEMOP_DATASIZE |
| |
| #endif // ENABLE(ASSEMBLER) && CPU(ARM64) |
| |
| #endif // ARM64Assembler_h |