// This file is part of AsmJit project // // See asmjit.h or LICENSE.md for license and copyright information // SPDX-License-Identifier: Zlib #ifndef ASMJIT_ARM_ARMOPERAND_H_INCLUDED #define ASMJIT_ARM_ARMOPERAND_H_INCLUDED #include "../core/archtraits.h" #include "../core/operand.h" #include "../core/type.h" #include "../arm/armglobals.h" ASMJIT_BEGIN_SUB_NAMESPACE(arm) //! \addtogroup asmjit_arm //! \{ class Reg; class Mem; class Gp; class GpW; class GpX; class Vec; class VecB; class VecH; class VecS; class VecD; class VecV; //! Register traits (ARM/AArch64). //! //! Register traits contains information about a particular register type. It's used by asmjit to setup register //! information on-the-fly and to populate tables that contain register information (this way it's possible to //! change register types and groups without having to reorder these tables). template struct RegTraits : public BaseRegTraits {}; //! \cond // <--------------------+-----+-------------------------+------------------------+---+---+------------------+ // | Reg | Reg-Type | Reg-Group |Sz |Cnt| TypeId | // <--------------------+-----+-------------------------+------------------------+---+---+------------------+ ASMJIT_DEFINE_REG_TRAITS(GpW , RegType::kARM_GpW , RegGroup::kGp , 4 , 32, TypeId::kInt32 ); ASMJIT_DEFINE_REG_TRAITS(GpX , RegType::kARM_GpX , RegGroup::kGp , 8 , 32, TypeId::kInt64 ); ASMJIT_DEFINE_REG_TRAITS(VecB , RegType::kARM_VecB , RegGroup::kVec , 1 , 32, TypeId::kVoid ); ASMJIT_DEFINE_REG_TRAITS(VecH , RegType::kARM_VecH , RegGroup::kVec , 2 , 32, TypeId::kVoid ); ASMJIT_DEFINE_REG_TRAITS(VecS , RegType::kARM_VecS , RegGroup::kVec , 4 , 32, TypeId::kInt32x1 ); ASMJIT_DEFINE_REG_TRAITS(VecD , RegType::kARM_VecD , RegGroup::kVec , 8 , 32, TypeId::kInt32x2 ); ASMJIT_DEFINE_REG_TRAITS(VecV , RegType::kARM_VecV , RegGroup::kVec , 16, 32, TypeId::kInt32x4 ); //! \endcond //! Register (ARM). class Reg : public BaseReg { public: ASMJIT_DEFINE_ABSTRACT_REG(Reg, BaseReg) //! Gets whether the register is a `R|W` register (32-bit). inline constexpr bool isGpW() const noexcept { return baseSignature() == RegTraits::kSignature; } //! Gets whether the register is an `X` register (64-bit). inline constexpr bool isGpX() const noexcept { return baseSignature() == RegTraits::kSignature; } //! Gets whether the register is a VEC-B register (8-bit). inline constexpr bool isVecB() const noexcept { return baseSignature() == RegTraits::kSignature; } //! Gets whether the register is a VEC-H register (16-bit). inline constexpr bool isVecH() const noexcept { return baseSignature() == RegTraits::kSignature; } //! Gets whether the register is a VEC-S register (32-bit). inline constexpr bool isVecS() const noexcept { return baseSignature() == RegTraits::kSignature; } //! Gets whether the register is a VEC-D register (64-bit). inline constexpr bool isVecD() const noexcept { return baseSignature() == RegTraits::kSignature; } //! Gets whether the register is a VEC-Q register (128-bit). inline constexpr bool isVecQ() const noexcept { return baseSignature() == RegTraits::kSignature; } //! Gets whether the register is either VEC-D (64-bit) or VEC-Q (128-bit). inline constexpr bool isVecDOrQ() const noexcept { return uint32_t(type()) - uint32_t(RegType::kARM_VecD) <= 1u; } //! Gets whether the register is a VEC-V register (128-bit). inline constexpr bool isVecV() const noexcept { return baseSignature() == RegTraits::kSignature; } template inline void setRegT(uint32_t id) noexcept { setSignature(RegTraits::kSignature); setId(id); } inline void setTypeAndId(RegType type, uint32_t id) noexcept { setSignature(signatureOf(type)); setId(id); } static inline RegGroup groupOf(RegType type) noexcept { return ArchTraits::byArch(Arch::kAArch64).regTypeToGroup(type); } static inline TypeId typeIdOf(RegType type) noexcept { return ArchTraits::byArch(Arch::kAArch64).regTypeToTypeId(type); } static inline OperandSignature signatureOf(RegType type) noexcept { return ArchTraits::byArch(Arch::kAArch64).regTypeToSignature(type); } template static inline RegGroup groupOfT() noexcept { return RegTraits::kGroup; } template static inline TypeId typeIdOfT() noexcept { return RegTraits::kTypeId; } template static inline OperandSignature signatureOfT() noexcept { return RegTraits::kSignature; } static inline bool isGpW(const Operand_& op) noexcept { return op.as().isGpW(); } static inline bool isGpX(const Operand_& op) noexcept { return op.as().isGpX(); } static inline bool isVecB(const Operand_& op) noexcept { return op.as().isVecB(); } static inline bool isVecH(const Operand_& op) noexcept { return op.as().isVecH(); } static inline bool isVecS(const Operand_& op) noexcept { return op.as().isVecS(); } static inline bool isVecD(const Operand_& op) noexcept { return op.as().isVecD(); } static inline bool isVecQ(const Operand_& op) noexcept { return op.as().isVecQ(); } static inline bool isVecV(const Operand_& op) noexcept { return op.as().isVecV(); } static inline bool isGpW(const Operand_& op, uint32_t id) noexcept { return bool(unsigned(isGpW(op)) & unsigned(op.id() == id)); } static inline bool isGpX(const Operand_& op, uint32_t id) noexcept { return bool(unsigned(isGpX(op)) & unsigned(op.id() == id)); } static inline bool isVecB(const Operand_& op, uint32_t id) noexcept { return bool(unsigned(isVecB(op)) & unsigned(op.id() == id)); } static inline bool isVecH(const Operand_& op, uint32_t id) noexcept { return bool(unsigned(isVecH(op)) & unsigned(op.id() == id)); } static inline bool isVecS(const Operand_& op, uint32_t id) noexcept { return bool(unsigned(isVecS(op)) & unsigned(op.id() == id)); } static inline bool isVecD(const Operand_& op, uint32_t id) noexcept { return bool(unsigned(isVecD(op)) & unsigned(op.id() == id)); } static inline bool isVecQ(const Operand_& op, uint32_t id) noexcept { return bool(unsigned(isVecQ(op)) & unsigned(op.id() == id)); } static inline bool isVecV(const Operand_& op, uint32_t id) noexcept { return bool(unsigned(isVecV(op)) & unsigned(op.id() == id)); } }; //! General purpose register (ARM). class Gp : public Reg { public: ASMJIT_DEFINE_ABSTRACT_REG(Gp, Reg) //! Special register id. enum Id : uint32_t { //! Register that depends on OS, could be used as TLS offset. kIdOs = 18, //! Frame pointer. kIdFp = 29, //! Link register. kIdLr = 30, //! Stack register id. kIdSp = 31, //! Zero register id. //! //! Although zero register has the same id as stack register it has a special treatment, because we need to be //! able to distinguish between these two at API level. Some intructions were designed to be used with SP and //! some other with ZR - so we need a way to distinguish these two to make sure we emit the right thing. //! //! The number 63 is not random, when you perform `id & 31` you would always get 31 for both SP and ZR inputs, //! which is the identifier used by AArch64 ISA to encode either SP or ZR depending on the instruction. kIdZr = 63 }; inline constexpr bool isZR() const noexcept { return id() == kIdZr; } inline constexpr bool isSP() const noexcept { return id() == kIdSp; } //! Cast this register to a 32-bit R|W. inline GpW w() const noexcept; //! Cast this register to a 64-bit X. inline GpX x() const noexcept; }; //! Vector register (ARM). class Vec : public Reg { public: ASMJIT_DEFINE_ABSTRACT_REG(Vec, Reg) //! Additional signature bits used by arm::Vec. enum AdditionalBits : uint32_t { // Register element type (3 bits). // |........|........|.XXX....|........| kSignatureRegElementTypeShift = 12, kSignatureRegElementTypeMask = 0x07 << kSignatureRegElementTypeShift, // Register has element index (1 bit). // |........|........|X.......|........| kSignatureRegElementFlagShift = 15, kSignatureRegElementFlagMask = 0x01 << kSignatureRegElementFlagShift, // Register element index (4 bits). // |........|....XXXX|........|........| kSignatureRegElementIndexShift = 16, kSignatureRegElementIndexMask = 0x0F << kSignatureRegElementIndexShift }; //! Element type. enum ElementType : uint32_t { //! No element type specified. kElementTypeNone = 0, //! Byte elements (B8 or B16). kElementTypeB, //! Halfword elements (H4 or H8). kElementTypeH, //! Singleword elements (S2 or S4). kElementTypeS, //! Doubleword elements (D2). kElementTypeD, //! Byte elements grouped by 4 bytes (B4). //! //! \note This element-type is only used by few instructions. kElementTypeB4, //! Halfword elements grouped by 2 halfwords (H2). //! //! \note This element-type is only used by few instructions. kElementTypeH2, //! Count of element types. kElementTypeCount }; //! \cond //! Shortcuts. enum SignatureReg : uint32_t { kSignatureElementB = kElementTypeB << kSignatureRegElementTypeShift, kSignatureElementH = kElementTypeH << kSignatureRegElementTypeShift, kSignatureElementS = kElementTypeS << kSignatureRegElementTypeShift, kSignatureElementD = kElementTypeD << kSignatureRegElementTypeShift, kSignatureElementB4 = kElementTypeB4 << kSignatureRegElementTypeShift, kSignatureElementH2 = kElementTypeH2 << kSignatureRegElementTypeShift }; //! \endcond //! Returns whether the register has associated an element type. inline constexpr bool hasElementType() const noexcept { return _signature.hasField(); } //! Returns whether the register has element index (it's an element index access). inline constexpr bool hasElementIndex() const noexcept { return _signature.hasField(); } //! Returns whether the reggister has element type or element index (or both). inline constexpr bool hasElementTypeOrIndex() const noexcept { return _signature.hasField(); } //! Returns element type of the register. inline constexpr uint32_t elementType() const noexcept { return _signature.getField(); } //! Sets element type of the register to `elementType`. inline void setElementType(uint32_t elementType) noexcept { _signature.setField(elementType); } //! Resets element type to none. inline void resetElementType() noexcept { _signature.setField(0); } //! Returns element index of the register. inline constexpr uint32_t elementIndex() const noexcept { return _signature.getField(); } //! Sets element index of the register to `elementType`. inline void setElementIndex(uint32_t elementIndex) noexcept { _signature |= kSignatureRegElementFlagMask; _signature.setField(elementIndex); } //! Resets element index of the register. inline void resetElementIndex() noexcept { _signature &= ~(kSignatureRegElementFlagMask | kSignatureRegElementIndexMask); } inline constexpr bool isVecB8() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits::kSignature | kSignatureElementB); } inline constexpr bool isVecH4() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits::kSignature | kSignatureElementH); } inline constexpr bool isVecS2() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits::kSignature | kSignatureElementS); } inline constexpr bool isVecD1() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits::kSignature); } inline constexpr bool isVecB16() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits::kSignature | kSignatureElementB); } inline constexpr bool isVecH8() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits::kSignature | kSignatureElementH); } inline constexpr bool isVecS4() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits::kSignature | kSignatureElementS); } inline constexpr bool isVecD2() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits::kSignature | kSignatureElementD); } inline constexpr bool isVecB4x4() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits::kSignature | kSignatureElementB4); } inline constexpr bool isVecH2x4() const noexcept { return _signature.subset(kBaseSignatureMask | kSignatureRegElementTypeMask) == (RegTraits::kSignature | kSignatureElementH2); } //! Creates a cloned register with element access. inline Vec at(uint32_t elementIndex) const noexcept { return Vec((signature() & ~kSignatureRegElementIndexMask) | (elementIndex << kSignatureRegElementIndexShift) | kSignatureRegElementFlagMask, id()); } //! Cast this register to an 8-bit B register (scalar). inline VecB b() const noexcept; //! Cast this register to a 16-bit H register (scalar). inline VecH h() const noexcept; //! Cast this register to a 32-bit S register (scalar). inline VecS s() const noexcept; //! Cast this register to a 64-bit D register (scalar). inline VecD d() const noexcept; //! Cast this register to a 128-bit Q register (scalar). inline VecV q() const noexcept; //! Cast this register to a 128-bit V register. inline VecV v() const noexcept; //! Cast this register to a 128-bit V.B[elementIndex] register. inline VecV b(uint32_t elementIndex) const noexcept; //! Cast this register to a 128-bit V.H[elementIndex] register. inline VecV h(uint32_t elementIndex) const noexcept; //! Cast this register to a 128-bit V.S[elementIndex] register. inline VecV s(uint32_t elementIndex) const noexcept; //! Cast this register to a 128-bit V.D[elementIndex] register. inline VecV d(uint32_t elementIndex) const noexcept; //! Cast this register to a 128-bit V.H2[elementIndex] register. inline VecV h2(uint32_t elementIndex) const noexcept; //! Cast this register to a 128-bit V.B4[elementIndex] register. inline VecV b4(uint32_t elementIndex) const noexcept; //! Cast this register to V.8B. inline VecD b8() const noexcept; //! Cast this register to V.16B. inline VecV b16() const noexcept; //! Cast this register to V.2H. inline VecS h2() const noexcept; //! Cast this register to V.4H. inline VecD h4() const noexcept; //! Cast this register to V.8H. inline VecV h8() const noexcept; //! Cast this register to V.2S. inline VecD s2() const noexcept; //! Cast this register to V.4S. inline VecV s4() const noexcept; //! Cast this register to V.2D. inline VecV d2() const noexcept; static inline constexpr OperandSignature _makeElementAccessSignature(uint32_t elementType, uint32_t elementIndex) noexcept { return OperandSignature{ uint32_t(RegTraits::kSignature) | uint32_t(kSignatureRegElementFlagMask) | uint32_t(elementType << kSignatureRegElementTypeShift) | uint32_t(elementIndex << kSignatureRegElementIndexShift)}; } }; //! 32-bit GPW (AArch64) and/or GPR (ARM/AArch32) register. class GpW : public Gp { ASMJIT_DEFINE_FINAL_REG(GpW, Gp, RegTraits) }; //! 64-bit GPX (AArch64) register. class GpX : public Gp { ASMJIT_DEFINE_FINAL_REG(GpX, Gp, RegTraits) }; //! 8-bit view (S) of VFP/SIMD register. class VecB : public Vec { ASMJIT_DEFINE_FINAL_REG(VecB, Vec, RegTraits) }; //! 16-bit view (S) of VFP/SIMD register. class VecH : public Vec { ASMJIT_DEFINE_FINAL_REG(VecH, Vec, RegTraits) }; //! 32-bit view (S) of VFP/SIMD register. class VecS : public Vec { ASMJIT_DEFINE_FINAL_REG(VecS, Vec, RegTraits) }; //! 64-bit view (D) of VFP/SIMD register. class VecD : public Vec { ASMJIT_DEFINE_FINAL_REG(VecD, Vec, RegTraits) }; //! 128-bit vector register (Q or V). class VecV : public Vec { ASMJIT_DEFINE_FINAL_REG(VecV, Vec, RegTraits) }; inline GpW Gp::w() const noexcept { return GpW(id()); } inline GpX Gp::x() const noexcept { return GpX(id()); } inline VecB Vec::b() const noexcept { return VecB(id()); } inline VecH Vec::h() const noexcept { return VecH(id()); } inline VecS Vec::s() const noexcept { return VecS(id()); } inline VecD Vec::d() const noexcept { return VecD(id()); } inline VecV Vec::q() const noexcept { return VecV(id()); } inline VecV Vec::v() const noexcept { return VecV(id()); } inline VecV Vec::b(uint32_t elementIndex) const noexcept { return VecV(_makeElementAccessSignature(kElementTypeB, elementIndex), id()); } inline VecV Vec::h(uint32_t elementIndex) const noexcept { return VecV(_makeElementAccessSignature(kElementTypeH, elementIndex), id()); } inline VecV Vec::s(uint32_t elementIndex) const noexcept { return VecV(_makeElementAccessSignature(kElementTypeS, elementIndex), id()); } inline VecV Vec::d(uint32_t elementIndex) const noexcept { return VecV(_makeElementAccessSignature(kElementTypeD, elementIndex), id()); } inline VecV Vec::h2(uint32_t elementIndex) const noexcept { return VecV(_makeElementAccessSignature(kElementTypeH2, elementIndex), id()); } inline VecV Vec::b4(uint32_t elementIndex) const noexcept { return VecV(_makeElementAccessSignature(kElementTypeB4, elementIndex), id()); } inline VecD Vec::b8() const noexcept { return VecD(OperandSignature{VecD::kSignature | kSignatureElementB}, id()); } inline VecS Vec::h2() const noexcept { return VecS(OperandSignature{VecS::kSignature | kSignatureElementH}, id()); } inline VecD Vec::h4() const noexcept { return VecD(OperandSignature{VecD::kSignature | kSignatureElementH}, id()); } inline VecD Vec::s2() const noexcept { return VecD(OperandSignature{VecD::kSignature | kSignatureElementS}, id()); } inline VecV Vec::b16() const noexcept { return VecV(OperandSignature{VecV::kSignature | kSignatureElementB}, id()); } inline VecV Vec::h8() const noexcept { return VecV(OperandSignature{VecV::kSignature | kSignatureElementH}, id()); } inline VecV Vec::s4() const noexcept { return VecV(OperandSignature{VecV::kSignature | kSignatureElementS}, id()); } inline VecV Vec::d2() const noexcept { return VecV(OperandSignature{VecV::kSignature | kSignatureElementD}, id()); } #ifndef _DOXYGEN namespace regs { #endif //! Creates a 32-bit W register operand (ARM/AArch64). static inline constexpr GpW w(uint32_t id) noexcept { return GpW(id); } //! Creates a 64-bit X register operand (AArch64). static inline constexpr GpX x(uint32_t id) noexcept { return GpX(id); } //! Creates a 32-bit S register operand (ARM/AArch64). static inline constexpr VecS s(uint32_t id) noexcept { return VecS(id); } //! Creates a 64-bit D register operand (ARM/AArch64). static inline constexpr VecD d(uint32_t id) noexcept { return VecD(id); } //! Creates a 1282-bit V register operand (ARM/AArch64). static inline constexpr VecV v(uint32_t id) noexcept { return VecV(id); } #ifndef _DOXYGEN } // {regs} // Make `arm::regs` accessible through `arm` namespace as well. using namespace regs; #endif //! Memory operand (ARM). class Mem : public BaseMem { public: //! \cond INTERNAL //! Additional bits of operand's signature used by `arm::Mem`. enum AdditionalBits : uint32_t { // Index shift value (5 bits). // |........|.....XXX|XX......|........| kSignatureMemShiftValueShift = 14, kSignatureMemShiftValueMask = 0x1Fu << kSignatureMemShiftValueShift, // Shift operation type (4 bits). // |........|XXXX....|........|........| kSignatureMemPredicateShift = 20, kSignatureMemPredicateMask = 0x0Fu << kSignatureMemPredicateShift }; //! \endcond //! Memory offset mode. //! //! Additional constants that can be used with the `predicate`. enum OffsetMode : uint32_t { //! Pre-index "[BASE, #Offset {, }]!" with write-back. kOffsetPreIndex = 0xE, //! Post-index "[BASE], #Offset {, }" with write-back. kOffsetPostIndex = 0xF }; //! \name Construction & Destruction //! \{ //! Construct a default `Mem` operand, that points to [0]. inline constexpr Mem() noexcept : BaseMem() {} inline constexpr Mem(const Mem& other) noexcept : BaseMem(other) {} inline explicit Mem(Globals::NoInit_) noexcept : BaseMem(Globals::NoInit) {} inline constexpr Mem(const Signature& signature, uint32_t baseId, uint32_t indexId, int32_t offset) noexcept : BaseMem(signature, baseId, indexId, offset) {} inline constexpr explicit Mem(const Label& base, int32_t off = 0, Signature signature = Signature{0}) noexcept : BaseMem(Signature::fromOpType(OperandType::kMem) | Signature::fromMemBaseType(RegType::kLabelTag) | signature, base.id(), 0, off) {} inline constexpr explicit Mem(const BaseReg& base, int32_t off = 0, Signature signature = Signature{0}) noexcept : BaseMem(Signature::fromOpType(OperandType::kMem) | Signature::fromMemBaseType(base.type()) | signature, base.id(), 0, off) {} inline constexpr Mem(const BaseReg& base, const BaseReg& index, Signature signature = Signature{0}) noexcept : BaseMem(Signature::fromOpType(OperandType::kMem) | Signature::fromMemBaseType(base.type()) | Signature::fromMemIndexType(index.type()) | signature, base.id(), index.id(), 0) {} inline constexpr Mem(const BaseReg& base, const BaseReg& index, const Shift& shift, Signature signature = Signature{0}) noexcept : BaseMem(Signature::fromOpType(OperandType::kMem) | Signature::fromMemBaseType(base.type()) | Signature::fromMemIndexType(index.type()) | Signature::fromValue(uint32_t(shift.op())) | Signature::fromValue(shift.value()) | signature, base.id(), index.id(), 0) {} inline constexpr Mem(uint64_t base, Signature signature = Signature{0}) noexcept : BaseMem(Signature::fromOpType(OperandType::kMem) | signature, uint32_t(base >> 32), 0, int32_t(uint32_t(base & 0xFFFFFFFFu))) {} //! \} //! \name Overloaded Operators //! \{ inline Mem& operator=(const Mem& other) noexcept = default; //! \} //! \name Clone //! \{ //! Clones the memory operand. inline constexpr Mem clone() const noexcept { return Mem(*this); } //! Gets new memory operand adjusted by `off`. inline Mem cloneAdjusted(int64_t off) const noexcept { Mem result(*this); result.addOffset(off); return result; } //! Clones the memory operand and makes it pre-index. inline Mem pre() const noexcept { Mem result(*this); result.setPredicate(kOffsetPreIndex); return result; } //! Clones the memory operand, applies a given offset `off` and makes it pre-index. inline Mem pre(int64_t off) const noexcept { Mem result(*this); result.setPredicate(kOffsetPreIndex); result.addOffset(off); return result; } //! Clones the memory operand and makes it post-index. inline Mem post() const noexcept { Mem result(*this); result.setPredicate(kOffsetPostIndex); return result; } //! Clones the memory operand, applies a given offset `off` and makes it post-index. inline Mem post(int64_t off) const noexcept { Mem result(*this); result.setPredicate(kOffsetPostIndex); result.addOffset(off); return result; } //! \} //! \name Base & Index //! \{ //! Converts memory `baseType` and `baseId` to `arm::Reg` instance. //! //! The memory must have a valid base register otherwise the result will be wrong. inline Reg baseReg() const noexcept { return Reg::fromTypeAndId(baseType(), baseId()); } //! Converts memory `indexType` and `indexId` to `arm::Reg` instance. //! //! The memory must have a valid index register otherwise the result will be wrong. inline Reg indexReg() const noexcept { return Reg::fromTypeAndId(indexType(), indexId()); } using BaseMem::setIndex; inline void setIndex(const BaseReg& index, uint32_t shift) noexcept { setIndex(index); setShift(shift); } //! \} //! \name ARM Specific Features //! \{ //! Gets whether the memory operand has shift (aka scale) constant. inline constexpr bool hasShift() const noexcept { return _signature.hasField(); } //! Gets the memory operand's shift (aka scale) constant. inline constexpr uint32_t shift() const noexcept { return _signature.getField(); } //! Sets the memory operand's shift (aka scale) constant. inline void setShift(uint32_t shift) noexcept { _signature.setField(shift); } //! Resets the memory operand's shift (aka scale) constant to zero. inline void resetShift() noexcept { _signature.setField(0); } //! Gets memory predicate (shift mode or offset mode), see \ref ShiftOp and \ref OffsetMode. inline constexpr uint32_t predicate() const noexcept { return _signature.getField(); } //! Sets memory predicate to `predicate`, see `Mem::ShiftOp`. inline void setPredicate(uint32_t predicate) noexcept { _signature.setField(predicate); } //! Resets shift mode to LSL (default). inline void resetPredicate() noexcept { _signature.setField(0); } inline constexpr bool isFixedOffset() const noexcept { return predicate() < kOffsetPreIndex; } inline constexpr bool isPreOrPost() const noexcept { return predicate() >= kOffsetPreIndex; } inline constexpr bool isPreIndex() const noexcept { return predicate() == kOffsetPreIndex; } inline constexpr bool isPostIndex() const noexcept { return predicate() == kOffsetPostIndex; } inline void resetToFixedOffset() noexcept { resetPredicate(); } inline void makePreIndex() noexcept { setPredicate(kOffsetPreIndex); } inline void makePostIndex() noexcept { setPredicate(kOffsetPostIndex); } //! \} }; //! Creates `[base.reg, offset]` memory operand (offset mode). static inline constexpr Mem ptr(const Gp& base, int32_t offset = 0) noexcept { return Mem(base, offset); } //! Creates `[base.reg, offset]!` memory operand (pre-index mode). static inline constexpr Mem ptr_pre(const Gp& base, int32_t offset = 0) noexcept { return Mem(base, offset, OperandSignature::fromValue(Mem::kOffsetPreIndex)); } //! Creates `[base.reg], offset` memory operand (post-index mode). static inline constexpr Mem ptr_post(const Gp& base, int32_t offset = 0) noexcept { return Mem(base, offset, OperandSignature::fromValue(Mem::kOffsetPostIndex)); } //! Creates `[base.reg, index]` memory operand. static inline constexpr Mem ptr(const Gp& base, const Gp& index) noexcept { return Mem(base, index); } //! Creates `[base.reg], index` memory operand (post-index mode). static inline constexpr Mem ptr_post(const Gp& base, const Gp& index) noexcept { return Mem(base, index, OperandSignature::fromValue(Mem::kOffsetPostIndex)); } //! Creates `[base.reg, index, SHIFT_OP #shift]` memory operand. static inline constexpr Mem ptr(const Gp& base, const Gp& index, const Shift& shift) noexcept { return Mem(base, index, shift); } //! Creates `[base + offset]` memory operand. static inline constexpr Mem ptr(const Label& base, int32_t offset = 0) noexcept { return Mem(base, offset); } // TODO: [ARM] PC + offset address. #if 0 //! Creates `[PC + offset]` (relative) memory operand. static inline constexpr Mem ptr(const PC& pc, int32_t offset = 0) noexcept { return Mem(pc, offset); } #endif //! Creates `[base]` absolute memory operand. //! //! \note The concept of absolute memory operands doesn't exist on ARM, the ISA only provides PC relative addressing. //! Absolute memory operands can only be used if it's known that the PC relative offset is encodable and that it //! would be within the limits. Absolute address is also often output from disassemblers, so AsmJit support it so it //! can assemble it back. static inline constexpr Mem ptr(uint64_t base) noexcept { return Mem(base); } //! \} ASMJIT_END_SUB_NAMESPACE //! \cond INTERNAL ASMJIT_BEGIN_NAMESPACE ASMJIT_DEFINE_TYPE_ID(arm::GpW, TypeId::kInt32); ASMJIT_DEFINE_TYPE_ID(arm::GpX, TypeId::kInt64); ASMJIT_DEFINE_TYPE_ID(arm::VecS, TypeId::kFloat32x1); ASMJIT_DEFINE_TYPE_ID(arm::VecD, TypeId::kFloat64x1); ASMJIT_DEFINE_TYPE_ID(arm::VecV, TypeId::kInt32x4); ASMJIT_END_NAMESPACE //! \endcond #endif // ASMJIT_ARM_ARMOPERAND_H_INCLUDED