--- /dev/null
+/*
+ * 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 ARMAssembler_h
+#define ARMAssembler_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;
+
+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)
+ {
+ }
+
+ // (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());
+ }
+
+ 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];
+ }
+
+ PassRefPtr<ExecutableMemoryHandle> executableCopy(VM& vm, void* ownerUID, JITCompilationEffort effort)
+ {
+ return m_buffer.executableCopy(vm, ownerUID, effort);
+ }
+
+ 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 int insn = *static_cast<unsigned int*>(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 // ARMAssembler_h