// [AsmJit] // Complete x86/x64 JIT and Remote Assembler for C++. // // [License] // Zlib - See LICENSE.md file in the package. // [Guard] #ifndef _ASMJIT_BASE_OPERAND_H #define _ASMJIT_BASE_OPERAND_H // [Dependencies] #include "../base/utils.h" // [Api-Begin] #include "../asmjit_apibegin.h" namespace asmjit { //! \addtogroup asmjit_base //! \{ // ============================================================================ // [asmjit::Operand_] // ============================================================================ //! Constructor-less \ref Operand. //! //! Contains no initialization code and can be used safely to define an array //! of operands that won't be initialized. This is a \ref Operand compatible //! data structure designed to be statically initialized or `static const`. struct Operand_ { // -------------------------------------------------------------------------- // [Operand Type] // -------------------------------------------------------------------------- //! Operand types that can be encoded in \ref Operand. ASMJIT_ENUM(OpType) { kOpNone = 0, //!< Not an operand or not initialized. kOpReg = 1, //!< Operand is a register. kOpMem = 2, //!< Operand is a memory. kOpImm = 3, //!< Operand is an immediate value. kOpLabel = 4 //!< Operand is a label. }; // -------------------------------------------------------------------------- // [Operand Signature (Bits)] // -------------------------------------------------------------------------- ASMJIT_ENUM(SignatureBits) { // Operand type (3 least significant bits). // |........|........|........|.....XXX| kSignatureOpShift = 0, kSignatureOpBits = 0x07U, kSignatureOpMask = kSignatureOpBits << kSignatureOpShift, // Operand size (8 most significant bits). // |XXXXXXXX|........|........|........| kSignatureSizeShift = 24, kSignatureSizeBits = 0xFFU, kSignatureSizeMask = kSignatureSizeBits << kSignatureSizeShift, // Register type (5 bits). // |........|........|........|XXXXX...| kSignatureRegTypeShift = 3, kSignatureRegTypeBits = 0x1FU, kSignatureRegTypeMask = kSignatureRegTypeBits << kSignatureRegTypeShift, // Register kind (4 bits). // |........|........|....XXXX|........| kSignatureRegKindShift = 8, kSignatureRegKindBits = 0x0FU, kSignatureRegKindMask = kSignatureRegKindBits << kSignatureRegKindShift, // Memory base type (5 bits). // |........|........|........|XXXXX...| kSignatureMemBaseTypeShift = 3, kSignatureMemBaseTypeBits = 0x1FU, kSignatureMemBaseTypeMask = kSignatureMemBaseTypeBits << kSignatureMemBaseTypeShift, // Memory index type (5 bits). // |........|........|...XXXXX|........| kSignatureMemIndexTypeShift = 8, kSignatureMemIndexTypeBits = 0x1FU, kSignatureMemIndexTypeMask = kSignatureMemIndexTypeBits << kSignatureMemIndexTypeShift, // Memory base+index combined (10 bits). // |........|........|...XXXXX|XXXXX...| kSignatureMemBaseIndexShift = 3, kSignatureMemBaseIndexBits = 0x3FFU, kSignatureMemBaseIndexMask = kSignatureMemBaseIndexBits << kSignatureMemBaseIndexShift, // Memory should be encoded as absolute immediate (X86|X64). // |........|........|.XX.....|........| kSignatureMemAddrTypeShift = 13, kSignatureMemAddrTypeBits = 0x03U, kSignatureMemAddrTypeMask = kSignatureMemAddrTypeBits << kSignatureMemAddrTypeShift, // This memory operand represents a function argument's stack location (CodeCompiler) // |........|........|.X......|........| kSignatureMemArgHomeShift = 15, kSignatureMemArgHomeBits = 0x01U, kSignatureMemArgHomeFlag = kSignatureMemArgHomeBits << kSignatureMemArgHomeShift, // This memory operand represents a virtual register's home-slot (CodeCompiler). // |........|........|X.......|........| kSignatureMemRegHomeShift = 16, kSignatureMemRegHomeBits = 0x01U, kSignatureMemRegHomeFlag = kSignatureMemRegHomeBits << kSignatureMemRegHomeShift }; // -------------------------------------------------------------------------- // [Operand Id] // -------------------------------------------------------------------------- //! Operand id helpers useful for id <-> index translation. ASMJIT_ENUM(PackedId) { //! Minimum valid packed-id. kPackedIdMin = 0x00000100U, //! Maximum valid packed-id. kPackedIdMax = 0xFFFFFFFFU, //! Count of valid packed-ids. kPackedIdCount = kPackedIdMax - kPackedIdMin + 1 }; // -------------------------------------------------------------------------- // [Operand Utilities] // -------------------------------------------------------------------------- //! Get if the given `id` is a valid packed-id that can be used by Operand. //! Packed ids are those equal or greater than `kPackedIdMin` and lesser or //! equal to `kPackedIdMax`. This concept was created to support virtual //! registers and to make them distinguishable from physical ones. It allows //! a single uint32_t to contain either physical register id or virtual //! register id represented as `packed-id`. This concept is used also for //! labels to make the API consistent. static ASMJIT_INLINE bool isPackedId(uint32_t id) noexcept { return id - kPackedIdMin < kPackedIdCount; } //! Convert a real-id into a packed-id that can be stored in Operand. static ASMJIT_INLINE uint32_t packId(uint32_t id) noexcept { return id + kPackedIdMin; } //! Convert a packed-id back to real-id. static ASMJIT_INLINE uint32_t unpackId(uint32_t id) noexcept { return id - kPackedIdMin; } // -------------------------------------------------------------------------- // [Operand Data] // -------------------------------------------------------------------------- //! Any operand. struct AnyData { uint32_t signature; //!< Type of the operand (see \ref OpType) and other data. uint32_t id; //!< Operand id or `0`. uint32_t reserved8_4; //!< \internal uint32_t reserved12_4; //!< \internal }; //! Register operand data. struct RegData { uint32_t signature; //!< Type of the operand (always \ref kOpReg) and other data. uint32_t id; //!< Physical or virtual register id. uint32_t reserved8_4; //!< \internal uint32_t reserved12_4; //!< \internal }; //! Memory operand data. struct MemData { uint32_t signature; //!< Type of the operand (always \ref kOpMem) and other data. uint32_t index; //!< INDEX register id or `0`. // [BASE + OFF32] vs just [OFF64]. union { uint64_t offset64; //!< 64-bit offset, combining low and high 32-bit parts. struct { #if ASMJIT_ARCH_LE uint32_t offsetLo32; //!< 32-bit low offset part. uint32_t base; //!< 32-bit high offset part or BASE. #else uint32_t base; //!< 32-bit high offset part or BASE. uint32_t offsetLo32; //!< 32-bit low offset part. #endif }; }; }; //! Immediate operand data. struct ImmData { uint32_t signature; //!< Type of the operand (always \ref kOpImm) and other data. uint32_t id; //!< Immediate id (always `0`). UInt64 value; //!< Immediate value. }; //! Label operand data. struct LabelData { uint32_t signature; //!< Type of the operand (always \ref kOpLabel) and other data. uint32_t id; //!< Label id (`0` if not initialized). uint32_t reserved8_4; //!< \internal uint32_t reserved12_4; //!< \internal }; // -------------------------------------------------------------------------- // [Init & Copy] // -------------------------------------------------------------------------- //! \internal //! //! Initialize the operand to `other` (used by constructors). ASMJIT_INLINE void _init(const Operand_& other) noexcept { ::memcpy(this, &other, sizeof(Operand_)); } //! \internal ASMJIT_INLINE void _initReg(uint32_t signature, uint32_t rd) { _init_packed_d0_d1(signature, rd); _init_packed_d2_d3(0, 0); } //! \internal ASMJIT_INLINE void _init_packed_d0_d1(uint32_t d0, uint32_t d1) noexcept { _packed[0].setPacked_2x32(d0, d1); } //! \internal ASMJIT_INLINE void _init_packed_d2_d3(uint32_t d2, uint32_t d3) noexcept { _packed[1].setPacked_2x32(d2, d3); } //! \internal //! //! Initialize the operand from `other` (used by operator overloads). ASMJIT_INLINE void copyFrom(const Operand_& other) noexcept { ::memcpy(this, &other, sizeof(Operand_)); } // -------------------------------------------------------------------------- // [Accessors] // -------------------------------------------------------------------------- //! Get if the operand matches the given signature `sign`. ASMJIT_INLINE bool hasSignature(uint32_t signature) const noexcept { return _signature == signature; } //! Get if the operand matches a signature of the `other` operand. ASMJIT_INLINE bool hasSignature(const Operand_& other) const noexcept { return _signature == other.getSignature(); } //! Get a 32-bit operand signature. //! //! Signature is first 4 bytes of the operand data. It's used mostly for //! operand checking as it's much faster to check 4 bytes at once than having //! to check these bytes individually. ASMJIT_INLINE uint32_t getSignature() const noexcept { return _signature; } //! Set the operand signature (see \ref getSignature). //! //! Improper use of `setSignature()` can lead to hard-to-debug errors. ASMJIT_INLINE void setSignature(uint32_t signature) noexcept { _signature = signature; } ASMJIT_INLINE bool _hasSignatureData(uint32_t bits) const noexcept { return (_signature & bits) != 0; } //! \internal //! //! Unpacks information from operand's signature. ASMJIT_INLINE uint32_t _getSignatureData(uint32_t bits, uint32_t shift) const noexcept { return (_signature >> shift) & bits; } //! \internal //! //! Packs information to operand's signature. ASMJIT_INLINE void _setSignatureData(uint32_t value, uint32_t bits, uint32_t shift) noexcept { ASMJIT_ASSERT(value <= bits); _signature = (_signature & ~(bits << shift)) | (value << shift); } ASMJIT_INLINE void _addSignatureData(uint32_t data) noexcept { _signature |= data; } //! Clears specified bits in operand's signature. ASMJIT_INLINE void _clearSignatureData(uint32_t bits, uint32_t shift) noexcept { _signature &= ~(bits << shift); } //! Get type of the operand, see \ref OpType. ASMJIT_INLINE uint32_t getOp() const noexcept { return _getSignatureData(kSignatureOpBits, kSignatureOpShift); } //! Get if the operand is none (\ref kOpNone). ASMJIT_INLINE bool isNone() const noexcept { return getOp() == 0; } //! Get if the operand is a register (\ref kOpReg). ASMJIT_INLINE bool isReg() const noexcept { return getOp() == kOpReg; } //! Get if the operand is a memory location (\ref kOpMem). ASMJIT_INLINE bool isMem() const noexcept { return getOp() == kOpMem; } //! Get if the operand is an immediate (\ref kOpImm). ASMJIT_INLINE bool isImm() const noexcept { return getOp() == kOpImm; } //! Get if the operand is a label (\ref kOpLabel). ASMJIT_INLINE bool isLabel() const noexcept { return getOp() == kOpLabel; } //! Get if the operand is a physical register. ASMJIT_INLINE bool isPhysReg() const noexcept { return isReg() && _reg.id < Globals::kInvalidRegId; } //! Get if the operand is a virtual register. ASMJIT_INLINE bool isVirtReg() const noexcept { return isReg() && isPackedId(_reg.id); } //! Get if the operand specifies a size (i.e. the size is not zero). ASMJIT_INLINE bool hasSize() const noexcept { return _hasSignatureData(kSignatureSizeMask); } //! Get if the size of the operand matches `size`. ASMJIT_INLINE bool hasSize(uint32_t size) const noexcept { return getSize() == size; } //! Get size of the operand (in bytes). //! //! The value returned depends on the operand type: //! * None - Should always return zero size. //! * Reg - Should always return the size of the register. If the register //! size depends on architecture (like \ref X86CReg and \ref X86DReg) //! the size returned should be the greatest possible (so it should //! return 64-bit size in such case). //! * Mem - Size is optional and will be in most cases zero. //! * Imm - Should always return zero size. //! * Label - Should always return zero size. ASMJIT_INLINE uint32_t getSize() const noexcept { return _getSignatureData(kSignatureSizeBits, kSignatureSizeShift); } //! Get the operand id. //! //! The value returned should be interpreted accordingly to the operand type: //! * None - Should be `0`. //! * Reg - Physical or virtual register id. //! * Mem - Multiple meanings - BASE address (register or label id), or //! high value of a 64-bit absolute address. //! * Imm - Should be `0`. //! * Label - Label id if it was created by using `newLabel()` or `0` //! if the label is invalid or uninitialized. ASMJIT_INLINE uint32_t getId() const noexcept { return _any.id; } //! Get if the operand is 100% equal to `other`. ASMJIT_INLINE bool isEqual(const Operand_& other) const noexcept { return (_packed[0] == other._packed[0]) & (_packed[1] == other._packed[1]) ; } //! Get if the operand is a register matching `rType`. ASMJIT_INLINE bool isReg(uint32_t rType) const noexcept { const uint32_t kMsk = (kSignatureOpBits << kSignatureOpShift) | (kSignatureRegTypeBits << kSignatureRegTypeShift); const uint32_t kSgn = (kOpReg << kSignatureOpShift) | (rType << kSignatureRegTypeShift); return (_signature & kMsk) == kSgn; } //! Get whether the operand is register and of `type` and `id`. ASMJIT_INLINE bool isReg(uint32_t rType, uint32_t rId) const noexcept { return isReg(rType) && getId() == rId; } //! Get whether the operand is a register or memory. ASMJIT_INLINE bool isRegOrMem() const noexcept { ASMJIT_ASSERT(kOpMem - kOpReg == 1); return Utils::inInterval(getOp(), kOpReg, kOpMem); } //! Cast this operand to `T` type. template ASMJIT_INLINE T& as() noexcept { return static_cast(*this); } //! Cast this operand to `T` type (const). template ASMJIT_INLINE const T& as() const noexcept { return static_cast(*this); } // -------------------------------------------------------------------------- // [Reset] // -------------------------------------------------------------------------- //! Reset the `Operand` to none. //! //! None operand is defined the following way: //! - Its signature is zero (kOpNone, and the rest zero as well). //! - Its id is `0`. //! - The reserved8_4 field is set to `0`. //! - The reserved12_4 field is set to zero. //! //! In other words, reset operands have all members set to zero. Reset operand //! must match the Operand state right after its construction. Alternatively, //! if you have an array of operands, you can simply use `memset()`. //! //! ``` //! using namespace asmjit; //! //! Operand a; //! Operand b; //! assert(a == b); //! //! b = x86::eax; //! assert(a != b); //! //! b.reset(); //! assert(a == b); //! //! memset(&b, 0, sizeof(Operand)); //! assert(a == b); //! ``` ASMJIT_INLINE void reset() noexcept { _init_packed_d0_d1(kOpNone, 0); _init_packed_d2_d3(0, 0); } // -------------------------------------------------------------------------- // [Operator Overload] // -------------------------------------------------------------------------- template ASMJIT_INLINE bool operator==(const T& other) const noexcept { return isEqual(other); } template ASMJIT_INLINE bool operator!=(const T& other) const noexcept { return !isEqual(other); } // -------------------------------------------------------------------------- // [Members] // -------------------------------------------------------------------------- union { AnyData _any; //!< Generic data. RegData _reg; //!< Physical or virtual register data. MemData _mem; //!< Memory address data. ImmData _imm; //!< Immediate value data. LabelData _label; //!< Label data. uint32_t _signature; //!< Operand signature (first 32-bits). UInt64 _packed[2]; //!< Operand packed into two 64-bit integers. }; }; // ============================================================================ // [asmjit::Operand] // ============================================================================ //! Operand can contain register, memory location, immediate, or label. class Operand : public Operand_ { public: // -------------------------------------------------------------------------- // [Construction / Destruction] // -------------------------------------------------------------------------- //! Create an uninitialized operand. ASMJIT_INLINE Operand() noexcept { reset(); } //! Create a reference to `other` operand. ASMJIT_INLINE Operand(const Operand& other) noexcept { _init(other); } //! Create a reference to `other` operand. explicit ASMJIT_INLINE Operand(const Operand_& other) noexcept { _init(other); } //! Create a completely uninitialized operand (dangerous). explicit ASMJIT_INLINE Operand(const _NoInit&) noexcept {} // -------------------------------------------------------------------------- // [Clone] // -------------------------------------------------------------------------- //! Clone the `Operand`. ASMJIT_INLINE Operand clone() const noexcept { return Operand(*this); } ASMJIT_INLINE Operand& operator=(const Operand_& other) noexcept { copyFrom(other); return *this; } }; // ============================================================================ // [asmjit::Label] // ============================================================================ //! Label (jump target or data location). //! //! Label represents a location in code typically used as a jump target, but //! may be also a reference to some data or a static variable. Label has to be //! explicitly created by CodeEmitter. //! //! Example of using labels: //! //! ~~~ //! // Create a CodeEmitter (for example X86Assembler). //! X86Assembler a; //! //! // Create Label instance. //! Label L1 = a.newLabel(); //! //! // ... your code ... //! //! // Using label. //! a.jump(L1); //! //! // ... your code ... //! //! // Bind label to the current position, see `CodeEmitter::bind()`. //! a.bind(L1); //! ~~~ class Label : public Operand { public: //! Type of the Label. enum Type { kTypeAnonymous = 0, //!< Anonymous (unnamed) label. kTypeLocal = 1, //!< Local label (always has parentId). kTypeGlobal = 2, //!< Global label (never has parentId). kTypeCount = 3 //!< Number of label types. }; // TODO: Find a better place, find a better name. enum { //! Label tag is used as a sub-type, forming a unique signature across all //! operand types as 0x1 is never associated with any register (reg-type). //! This means that a memory operand's BASE register can be constructed //! from virtually any operand (register vs. label) by just assigning its //! type (reg type or label-tag) and operand id. kLabelTag = 0x1 }; // -------------------------------------------------------------------------- // [Construction / Destruction] // -------------------------------------------------------------------------- //! Create new, unassociated label. ASMJIT_INLINE Label() noexcept : Operand(NoInit) { reset(); } //! Create a reference to another label. ASMJIT_INLINE Label(const Label& other) noexcept : Operand(other) {} explicit ASMJIT_INLINE Label(uint32_t id) noexcept : Operand(NoInit) { _init_packed_d0_d1(kOpLabel, id); _init_packed_d2_d3(0, 0); } explicit ASMJIT_INLINE Label(const _NoInit&) noexcept : Operand(NoInit) {} // -------------------------------------------------------------------------- // [Reset] // -------------------------------------------------------------------------- // TODO: I think that if operand is reset it shouldn't say it's a Label, it // should be none like all other operands. ASMJIT_INLINE void reset() noexcept { _init_packed_d0_d1(kOpLabel, 0); _init_packed_d2_d3(0, 0); } // -------------------------------------------------------------------------- // [Label Specific] // -------------------------------------------------------------------------- //! Get if the label was created by CodeEmitter and has an assigned id. ASMJIT_INLINE bool isValid() const noexcept { return _label.id != 0; } //! Set label id. ASMJIT_INLINE void setId(uint32_t id) { _label.id = id; } // -------------------------------------------------------------------------- // [Operator Overload] // -------------------------------------------------------------------------- ASMJIT_INLINE Label& operator=(const Label& other) noexcept { copyFrom(other); return *this; } }; // ============================================================================ // [asmjit::Reg] // ============================================================================ #define ASMJIT_DEFINE_REG_TRAITS(TRAITS_T, REG_T, TYPE, KIND, SIZE, COUNT, TYPE_ID) \ template<> \ struct TRAITS_T < TYPE > { \ typedef REG_T Reg; \ \ enum { \ kValid = 1, \ kCount = COUNT, \ kTypeId = TYPE_ID, \ \ kType = TYPE, \ kKind = KIND, \ kSize = SIZE, \ kSignature = (Operand::kOpReg << Operand::kSignatureOpShift ) | \ (kType << Operand::kSignatureRegTypeShift) | \ (kKind << Operand::kSignatureRegKindShift) | \ (kSize << Operand::kSignatureSizeShift ) \ }; \ } \ #define ASMJIT_DEFINE_ABSTRACT_REG(REG_T, BASE_T) \ public: \ /*! Default constructor doesn't setup anything, it's like `Operand()`. */ \ ASMJIT_INLINE REG_T() ASMJIT_NOEXCEPT \ : BASE_T() {} \ \ /*! Copy the `other` REG_T register operand. */ \ ASMJIT_INLINE REG_T(const REG_T& other) ASMJIT_NOEXCEPT \ : BASE_T(other) {} \ \ /*! Copy the `other` REG_T register operand having its id set to `rId` */ \ ASMJIT_INLINE REG_T(const Reg& other, uint32_t rId) ASMJIT_NOEXCEPT \ : BASE_T(other, rId) {} \ \ /*! Create a REG_T register operand based on `signature` and `rId`. */ \ ASMJIT_INLINE REG_T(const _Init& init, uint32_t signature, uint32_t rId) ASMJIT_NOEXCEPT \ : BASE_T(init, signature, rId) {} \ \ /*! Create a completely uninitialized REG_T register operand (garbage). */ \ explicit ASMJIT_INLINE REG_T(const _NoInit&) ASMJIT_NOEXCEPT \ : BASE_T(NoInit) {} \ \ /*! Clone the register operand. */ \ ASMJIT_INLINE REG_T clone() const ASMJIT_NOEXCEPT { return REG_T(*this); } \ \ /*! Create a new register from register type and id. */ \ static ASMJIT_INLINE REG_T fromTypeAndId(uint32_t rType, uint32_t rId) ASMJIT_NOEXCEPT { \ return REG_T(Init, signatureOf(rType), rId); \ } \ \ /*! Create a new register from signature and id. */ \ static ASMJIT_INLINE REG_T fromSignature(uint32_t signature, uint32_t rId) ASMJIT_NOEXCEPT { \ return REG_T(Init, signature, rId); \ } \ \ ASMJIT_INLINE REG_T& operator=(const REG_T& other) ASMJIT_NOEXCEPT { \ copyFrom(other); return *this; \ } #define ASMJIT_DEFINE_FINAL_REG(REG_T, BASE_T, TRAITS_T) \ ASMJIT_DEFINE_ABSTRACT_REG(REG_T, BASE_T) \ \ /*! Create a REG_T register with `id`. */ \ explicit ASMJIT_INLINE REG_T(uint32_t rId) ASMJIT_NOEXCEPT \ : BASE_T(Init, kSignature, rId) {} \ \ enum { \ kThisType = TRAITS_T::kType, \ kThisKind = TRAITS_T::kKind, \ kThisSize = TRAITS_T::kSize, \ kSignature = TRAITS_T::kSignature \ }; //! Structure that contains core register information. //! //! This information is compatible with operand's signature (32-bit integer) //! and `RegInfo` just provides easy way to access it. struct RegInfo { ASMJIT_INLINE uint32_t getSignature() const noexcept { return _signature; } ASMJIT_INLINE uint32_t getOp() const noexcept { return (_signature >> Operand::kSignatureOpShift) & Operand::kSignatureOpBits; } ASMJIT_INLINE uint32_t getType() const noexcept { return (_signature >> Operand::kSignatureRegTypeShift) & Operand::kSignatureRegTypeBits; } ASMJIT_INLINE uint32_t getKind() const noexcept { return (_signature >> Operand::kSignatureRegKindShift) & Operand::kSignatureRegKindBits; } ASMJIT_INLINE uint32_t getSize() const noexcept { return (_signature >> Operand::kSignatureSizeShift) & Operand::kSignatureSizeBits; } uint32_t _signature; }; //! Physical/Virtual register operand. class Reg : public Operand { public: //! Architecture neutral register types. //! //! These must be reused by any platform that contains that types. All GP //! and VEC registers are also allowed by design to be part of a BASE|INDEX //! of a memory operand. ASMJIT_ENUM(RegType) { kRegNone = 0, //!< No register - unused, invalid, multiple meanings. // (1 is used as a LabelTag) kRegGp8Lo = 2, //!< 8-bit low general purpose register (X86). kRegGp8Hi = 3, //!< 8-bit high general purpose register (X86). kRegGp16 = 4, //!< 16-bit general purpose register (X86). kRegGp32 = 5, //!< 32-bit general purpose register (X86|ARM). kRegGp64 = 6, //!< 64-bit general purpose register (X86|ARM). kRegVec32 = 7, //!< 32-bit view of a vector register (ARM). kRegVec64 = 8, //!< 64-bit view of a vector register (ARM). kRegVec128 = 9, //!< 128-bit view of a vector register (X86|ARM). kRegVec256 = 10, //!< 256-bit view of a vector register (X86). kRegVec512 = 11, //!< 512-bit view of a vector register (X86). kRegVec1024 = 12, //!< 1024-bit view of a vector register (future). kRegVec2048 = 13, //!< 2048-bit view of a vector register (future). kRegIP = 14, //!< Universal id of IP/PC register (if separate). kRegCustom = 15, //!< Start of platform dependent register types (must be honored). kRegMax = 31 //!< Maximum possible register id of all architectures. }; //! Architecture neutral register kinds. ASMJIT_ENUM(Kind) { kKindGp = 0, //!< General purpose register (X86|ARM). kKindVec = 1, //!< Vector register (X86|ARM). kKindMax = 15 //!< Maximum possible register kind of all architectures. }; // -------------------------------------------------------------------------- // [Construction / Destruction] // -------------------------------------------------------------------------- //! Create a dummy register operand. ASMJIT_INLINE Reg() noexcept : Operand() {} //! Create a new register operand which is the same as `other` . ASMJIT_INLINE Reg(const Reg& other) noexcept : Operand(other) {} //! Create a new register operand compatible with `other`, but with a different `rId`. ASMJIT_INLINE Reg(const Reg& other, uint32_t rId) noexcept : Operand(NoInit) { _init_packed_d0_d1(other._signature, rId); _packed[1] = other._packed[1]; } //! Create a register initialized to `signature` and `rId`. ASMJIT_INLINE Reg(const _Init&, uint32_t signature, uint32_t rId) noexcept : Operand(NoInit) { _initReg(signature, rId); } explicit ASMJIT_INLINE Reg(const _NoInit&) noexcept : Operand(NoInit) {} //! Create a new register based on `signature` and `rId`. static ASMJIT_INLINE Reg fromSignature(uint32_t signature, uint32_t rId) noexcept { return Reg(Init, signature, rId); } // -------------------------------------------------------------------------- // [Reg Specific] // -------------------------------------------------------------------------- //! Get if the register is valid (either virtual or physical). ASMJIT_INLINE bool isValid() const noexcept { return _signature != 0; } //! Get if this is a physical register. ASMJIT_INLINE bool isPhysReg() const noexcept { return _reg.id < Globals::kInvalidRegId; } //! Get if this is a virtual register (used by \ref CodeCompiler). ASMJIT_INLINE bool isVirtReg() const noexcept { return isPackedId(_reg.id); } //! Get if this register is the same as `other`. //! //! This is just an optimization. Registers by default only use the first //! 8 bytes of the Operand, so this method takes advantage of this knowledge //! and only compares these 8 bytes. If both operands were created correctly //! then `isEqual()` and `isSame()` should give the same answer, however, if //! some operands contains a garbage or other metadata in the upper 8 bytes //! then `isSame()` may return `true` in cases where `isEqual()` returns //! false. However. no such case is known at the moment. ASMJIT_INLINE bool isSame(const Reg& other) const noexcept { return _packed[0] == other._packed[0]; } //! Get if the register type matches `rType` - same as `isReg(rType)`, provided for convenience. ASMJIT_INLINE bool isType(uint32_t rType) const noexcept { return (_signature & kSignatureRegTypeMask) == (rType << kSignatureRegTypeShift); } //! Get if the register kind matches `rKind`. ASMJIT_INLINE bool isKind(uint32_t rKind) const noexcept { return (_signature & kSignatureRegKindMask) == (rKind << kSignatureRegKindShift); } //! Get if the register is a general purpose register (any size). ASMJIT_INLINE bool isGp() const noexcept { return isKind(kKindGp); } //! Get if the register is a vector register. ASMJIT_INLINE bool isVec() const noexcept { return isKind(kKindVec); } using Operand_::isReg; //! Same as `isType()`, provided for convenience. ASMJIT_INLINE bool isReg(uint32_t rType) const noexcept { return isType(rType); } //! Get if the register type matches `type` and register id matches `rId`. ASMJIT_INLINE bool isReg(uint32_t rType, uint32_t rId) const noexcept { return isType(rType) && getId() == rId; } //! Get the register type. ASMJIT_INLINE uint32_t getType() const noexcept { return _getSignatureData(kSignatureRegTypeBits, kSignatureRegTypeShift); } //! Get the register kind. ASMJIT_INLINE uint32_t getKind() const noexcept { return _getSignatureData(kSignatureRegKindBits, kSignatureRegKindShift); } //! Clone the register operand. ASMJIT_INLINE Reg clone() const noexcept { return Reg(*this); } //! Cast this register to `RegT` by also changing its signature. //! //! NOTE: Improper use of `cloneAs()` can lead to hard-to-debug errors. template ASMJIT_INLINE RegT cloneAs() const noexcept { return RegT(Init, RegT::kSignature, getId()); } //! Cast this register to `other` by also changing its signature. //! //! NOTE: Improper use of `cloneAs()` can lead to hard-to-debug errors. template ASMJIT_INLINE RegT cloneAs(const RegT& other) const noexcept { return RegT(Init, other.getSignature(), getId()); } //! Set the register id to `id`. ASMJIT_INLINE void setId(uint32_t rId) noexcept { _reg.id = rId; } //! Set a 32-bit operand signature based on traits of `RegT`. template ASMJIT_INLINE void setSignatureT() noexcept { _signature = RegT::kSignature; } //! Set register's `signature` and `rId`. ASMJIT_INLINE void setSignatureAndId(uint32_t signature, uint32_t rId) noexcept { _signature = signature; _reg.id = rId; } // -------------------------------------------------------------------------- // [Reg Statics] // -------------------------------------------------------------------------- static ASMJIT_INLINE bool isGp(const Operand_& op) noexcept { // Check operand type and register kind. Not interested in register type and size. const uint32_t kSgn = (kOpReg << kSignatureOpShift ) | (kKindGp << kSignatureRegKindShift) ; return (op.getSignature() & (kSignatureOpMask | kSignatureRegKindMask)) == kSgn; } //! Get if the `op` operand is either a low or high 8-bit GPB register. static ASMJIT_INLINE bool isVec(const Operand_& op) noexcept { // Check operand type and register kind. Not interested in register type and size. const uint32_t kSgn = (kOpReg << kSignatureOpShift ) | (kKindVec << kSignatureRegKindShift) ; return (op.getSignature() & (kSignatureOpMask | kSignatureRegKindMask)) == kSgn; } static ASMJIT_INLINE bool isGp(const Operand_& op, uint32_t rId) noexcept { return isGp(op) & (op.getId() == rId); } static ASMJIT_INLINE bool isVec(const Operand_& op, uint32_t rId) noexcept { return isVec(op) & (op.getId() == rId); } }; // ============================================================================ // [asmjit::RegOnly] // ============================================================================ //! RegOnly is 8-byte version of `Reg` that only allows to store either `Reg` //! or nothing. This class was designed to decrease the space consumed by each //! extra "operand" in `CodeEmitter` and `CBInst` classes. struct RegOnly { // -------------------------------------------------------------------------- // [Init / Reset] // -------------------------------------------------------------------------- //! Initialize the `RegOnly` instance to hold register `signature` and `id`. ASMJIT_INLINE void init(uint32_t signature, uint32_t id) noexcept { _signature = signature; _id = id; } ASMJIT_INLINE void init(const Reg& reg) noexcept { init(reg.getSignature(), reg.getId()); } ASMJIT_INLINE void init(const RegOnly& reg) noexcept { init(reg.getSignature(), reg.getId()); } //! Reset the `RegOnly` to none. ASMJIT_INLINE void reset() noexcept { init(0, 0); } // -------------------------------------------------------------------------- // [Accessors] // -------------------------------------------------------------------------- //! Get if the `ExtraReg` is none (same as calling `Operand_::isNone()`). ASMJIT_INLINE bool isNone() const noexcept { return _signature == 0; } //! Get if the register is valid (either virtual or physical). ASMJIT_INLINE bool isValid() const noexcept { return _signature != 0; } //! Get if this is a physical register. ASMJIT_INLINE bool isPhysReg() const noexcept { return _id < Globals::kInvalidRegId; } //! Get if this is a virtual register (used by \ref CodeCompiler). ASMJIT_INLINE bool isVirtReg() const noexcept { return Operand::isPackedId(_id); } //! Get register signature or 0. ASMJIT_INLINE uint32_t getSignature() const noexcept { return _signature; } //! Get register id or 0. ASMJIT_INLINE uint32_t getId() const noexcept { return _id; } //! \internal //! //! Unpacks information from operand's signature. ASMJIT_INLINE uint32_t _getSignatureData(uint32_t bits, uint32_t shift) const noexcept { return (_signature >> shift) & bits; } //! Get the register type. ASMJIT_INLINE uint32_t getType() const noexcept { return _getSignatureData(Operand::kSignatureRegTypeBits, Operand::kSignatureRegTypeShift); } //! Get the register kind. ASMJIT_INLINE uint32_t getKind() const noexcept { return _getSignatureData(Operand::kSignatureRegKindBits, Operand::kSignatureRegKindShift); } // -------------------------------------------------------------------------- // [ToReg] // -------------------------------------------------------------------------- //! Convert back to `RegT` operand. template ASMJIT_INLINE RegT toReg() const noexcept { return RegT(Init, _signature, _id); } // -------------------------------------------------------------------------- // [Members] // -------------------------------------------------------------------------- //! Type of the operand, either `kOpNone` or `kOpReg`. uint32_t _signature; //! Physical or virtual register id. uint32_t _id; }; // ============================================================================ // [asmjit::Mem] // ============================================================================ //! Base class for all memory operands. //! //! NOTE: It's tricky to pack all possible cases that define a memory operand //! into just 16 bytes. The `Mem` splits data into the following parts: //! //! BASE - Base register or label - requires 36 bits total. 4 bits are used //! to encode the type of the BASE operand (label vs. register type) and //! the remaining 32 bits define the BASE id, which can be a physical or //! virtual register index. If BASE type is zero, which is never used as //! a register-type and label doesn't use it as well then BASE field //! contains a high DWORD of a possible 64-bit absolute address, which is //! possible on X64. //! //! INDEX - Index register (or theoretically Label, which doesn't make sense). //! Encoding is similar to BASE - it also requires 36 bits and splits the //! encoding to INDEX type (4 bits defining the register type) and id (32-bits). //! //! OFFSET - A relative offset of the address. Basically if BASE is specified //! the relative displacement adjusts BASE and an optional INDEX. if BASE is //! not specified then the OFFSET should be considered as ABSOLUTE address //! (at least on X86/X64). In that case its low 32 bits are stored in //! DISPLACEMENT field and the remaining high 32 bits are stored in BASE. //! //! OTHER FIELDS - There is rest 8 bits that can be used for whatever purpose. //! The X86Mem operand uses these bits to store segment override //! prefix and index shift (scale). class Mem : public Operand { public: enum AddrType { kAddrTypeDefault = 0, kAddrTypeAbs = 1, kAddrTypeRel = 2, kAddrTypeWrt = 3 }; // Shortcuts. enum SignatureMem { kSignatureMemAbs = kAddrTypeAbs << kSignatureMemAddrTypeShift, kSignatureMemRel = kAddrTypeRel << kSignatureMemAddrTypeShift, kSignatureMemWrt = kAddrTypeWrt << kSignatureMemAddrTypeShift }; // -------------------------------------------------------------------------- // [Construction / Destruction] // -------------------------------------------------------------------------- //! Construct a default `Mem` operand, that points to [0]. ASMJIT_INLINE Mem() noexcept : Operand(NoInit) { reset(); } ASMJIT_INLINE Mem(const Mem& other) noexcept : Operand(other) {} ASMJIT_INLINE Mem(const _Init&, uint32_t baseType, uint32_t baseId, uint32_t indexType, uint32_t indexId, int32_t off, uint32_t size, uint32_t flags) noexcept : Operand(NoInit) { uint32_t signature = (baseType << kSignatureMemBaseTypeShift ) | (indexType << kSignatureMemIndexTypeShift) | (size << kSignatureSizeShift ) ; _init_packed_d0_d1(kOpMem | signature | flags, indexId); _mem.base = baseId; _mem.offsetLo32 = static_cast(off); } explicit ASMJIT_INLINE Mem(const _NoInit&) noexcept : Operand(NoInit) {} // -------------------------------------------------------------------------- // [Mem Specific] // -------------------------------------------------------------------------- //! Clone `Mem` operand. ASMJIT_INLINE Mem clone() const noexcept { return Mem(*this); } //! Reset the memory operand - after reset the memory points to [0]. ASMJIT_INLINE void reset() noexcept { _init_packed_d0_d1(kOpMem, 0); _init_packed_d2_d3(0, 0); } ASMJIT_INLINE bool hasAddrType() const noexcept { return _hasSignatureData(kSignatureMemAddrTypeMask); } ASMJIT_INLINE uint32_t getAddrType() const noexcept { return _getSignatureData(kSignatureMemAddrTypeBits, kSignatureMemAddrTypeShift); } ASMJIT_INLINE void setAddrType(uint32_t addrType) noexcept { return _setSignatureData(addrType, kSignatureMemAddrTypeBits, kSignatureMemAddrTypeShift); } ASMJIT_INLINE void resetAddrType() noexcept { return _clearSignatureData(kSignatureMemAddrTypeBits, kSignatureMemAddrTypeShift); } ASMJIT_INLINE bool isAbs() const noexcept { return getAddrType() == kAddrTypeAbs; } ASMJIT_INLINE bool isRel() const noexcept { return getAddrType() == kAddrTypeRel; } ASMJIT_INLINE bool isWrt() const noexcept { return getAddrType() == kAddrTypeWrt; } ASMJIT_INLINE void setAbs() noexcept { setAddrType(kAddrTypeAbs); } ASMJIT_INLINE void setRel() noexcept { setAddrType(kAddrTypeRel); } ASMJIT_INLINE void setWrt() noexcept { setAddrType(kAddrTypeWrt); } ASMJIT_INLINE bool isArgHome() const noexcept { return _hasSignatureData(kSignatureMemArgHomeFlag); } ASMJIT_INLINE bool isRegHome() const noexcept { return _hasSignatureData(kSignatureMemRegHomeFlag); } ASMJIT_INLINE void setArgHome() noexcept { _signature |= kSignatureMemArgHomeFlag; } ASMJIT_INLINE void setRegHome() noexcept { _signature |= kSignatureMemRegHomeFlag; } ASMJIT_INLINE void clearArgHome() noexcept { _signature &= ~kSignatureMemArgHomeFlag; } ASMJIT_INLINE void clearRegHome() noexcept { _signature &= ~kSignatureMemRegHomeFlag; } //! Get if the memory operand has a BASE register or label specified. ASMJIT_INLINE bool hasBase() const noexcept { return (_signature & kSignatureMemBaseTypeMask) != 0; } //! Get if the memory operand has an INDEX register specified. ASMJIT_INLINE bool hasIndex() const noexcept { return (_signature & kSignatureMemIndexTypeMask) != 0; } //! Get whether the memory operand has BASE and INDEX register. ASMJIT_INLINE bool hasBaseOrIndex() const noexcept { return (_signature & kSignatureMemBaseIndexMask) != 0; } //! Get whether the memory operand has BASE and INDEX register. ASMJIT_INLINE bool hasBaseAndIndex() const noexcept { return (_signature & kSignatureMemBaseTypeMask) != 0 && (_signature & kSignatureMemIndexTypeMask) != 0; } //! Get if the BASE operand is a register (registers start after `kLabelTag`). ASMJIT_INLINE bool hasBaseReg() const noexcept { return (_signature & kSignatureMemBaseTypeMask) > (Label::kLabelTag << kSignatureMemBaseTypeShift); } //! Get if the BASE operand is a label. ASMJIT_INLINE bool hasBaseLabel() const noexcept { return (_signature & kSignatureMemBaseTypeMask) == (Label::kLabelTag << kSignatureMemBaseTypeShift); } //! Get if the INDEX operand is a register (registers start after `kLabelTag`). ASMJIT_INLINE bool hasIndexReg() const noexcept { return (_signature & kSignatureMemIndexTypeMask) > (Label::kLabelTag << kSignatureMemIndexTypeShift); } //! Get type of a BASE register (0 if this memory operand doesn't use the BASE register). //! //! NOTE: If the returned type is one (a value never associated to a register //! type) the BASE is not register, but it's a label. One equals to `kLabelTag`. //! You should always check `hasBaseLabel()` before using `getBaseId()` result. ASMJIT_INLINE uint32_t getBaseType() const noexcept { return _getSignatureData(kSignatureMemBaseTypeBits, kSignatureMemBaseTypeShift); } //! Get type of an INDEX register (0 if this memory operand doesn't use the INDEX register). ASMJIT_INLINE uint32_t getIndexType() const noexcept { return _getSignatureData(kSignatureMemIndexTypeBits, kSignatureMemIndexTypeShift); } //! Get both BASE (4:0 bits) and INDEX (9:5 bits) types combined into a single integer. //! //! This is used internally for BASE+INDEX validation. ASMJIT_INLINE uint32_t getBaseIndexType() const noexcept { return _getSignatureData(kSignatureMemBaseIndexBits, kSignatureMemBaseIndexShift); } //! Get id of the BASE register or label (if the BASE was specified as label). ASMJIT_INLINE uint32_t getBaseId() const noexcept { return _mem.base; } //! Get id of the INDEX register. ASMJIT_INLINE uint32_t getIndexId() const noexcept { return _mem.index; } ASMJIT_INLINE void _setBase(uint32_t rType, uint32_t rId) noexcept { _setSignatureData(rType, kSignatureMemBaseTypeBits, kSignatureMemBaseTypeShift); _mem.base = rId; } ASMJIT_INLINE void _setIndex(uint32_t rType, uint32_t rId) noexcept { _setSignatureData(rType, kSignatureMemIndexTypeBits, kSignatureMemIndexTypeShift); _mem.index = rId; } ASMJIT_INLINE void setBase(const Reg& base) noexcept { return _setBase(base.getType(), base.getId()); } ASMJIT_INLINE void setIndex(const Reg& index) noexcept { return _setIndex(index.getType(), index.getId()); } //! Reset the memory operand's BASE register / label. ASMJIT_INLINE void resetBase() noexcept { _setBase(0, 0); } //! Reset the memory operand's INDEX register. ASMJIT_INLINE void resetIndex() noexcept { _setIndex(0, 0); } //! Set memory operand size. ASMJIT_INLINE void setSize(uint32_t size) noexcept { _setSignatureData(size, kSignatureSizeBits, kSignatureSizeShift); } ASMJIT_INLINE bool hasOffset() const noexcept { int32_t lo = static_cast(_mem.offsetLo32); int32_t hi = static_cast(_mem.base) & -static_cast(getBaseType() == 0); return (lo | hi) != 0; } //! Get if the memory operand has 64-bit offset or absolute address. //! //! If this is true then `hasBase()` must always report false. ASMJIT_INLINE bool has64BitOffset() const noexcept { return getBaseType() == 0; } //! Get a 64-bit offset or absolute address. ASMJIT_INLINE int64_t getOffset() const noexcept { return has64BitOffset() ? static_cast(_mem.offset64) : static_cast(static_cast(_mem.offsetLo32)); // Sign-Extend. } //! Get a lower part of a 64-bit offset or absolute address. ASMJIT_INLINE int32_t getOffsetLo32() const noexcept { return static_cast(_mem.offsetLo32); } //! Get a higher part of a 64-bit offset or absolute address. //! //! NOTE: This function is UNSAFE and returns garbage if `has64BitOffset()` //! returns false. Never use it blindly without checking it. ASMJIT_INLINE int32_t getOffsetHi32() const noexcept { return static_cast(_mem.base); } //! Set a 64-bit offset or an absolute address to `offset`. //! //! NOTE: This functions attempts to set both high and low parts of a 64-bit //! offset, however, if the operand has a BASE register it will store only the //! low 32 bits of the offset / address as there is no way to store both BASE //! and 64-bit offset, and there is currently no architecture that has such //! capability targeted by AsmJit. ASMJIT_INLINE void setOffset(int64_t offset) noexcept { if (has64BitOffset()) _mem.offset64 = static_cast(offset); else _mem.offsetLo32 = static_cast(offset & 0xFFFFFFFF); } //! Adjust the offset by a 64-bit `off`. ASMJIT_INLINE void addOffset(int64_t off) noexcept { if (has64BitOffset()) _mem.offset64 += static_cast(off); else _mem.offsetLo32 += static_cast(off & 0xFFFFFFFF); } //! Reset the memory offset to zero. ASMJIT_INLINE void resetOffset() noexcept { setOffset(0); } //! Set a low 32-bit offset to `off`. ASMJIT_INLINE void setOffsetLo32(int32_t off) noexcept { _mem.offsetLo32 = static_cast(off); } //! Adjust the offset by `off`. //! //! NOTE: This is a fast function that doesn't use the HI 32-bits of a //! 64-bit offset. Use it only if you know that there is a BASE register //! and the offset is only 32 bits anyway. ASMJIT_INLINE void addOffsetLo32(int32_t off) noexcept { _mem.offsetLo32 += static_cast(off); } //! Reset the memory offset to zero. ASMJIT_INLINE void resetOffsetLo32() noexcept { setOffsetLo32(0); } // -------------------------------------------------------------------------- // [Operator Overload] // -------------------------------------------------------------------------- ASMJIT_INLINE Mem& operator=(const Mem& other) noexcept { copyFrom(other); return *this; } }; // ============================================================================ // [asmjit::Imm] // ============================================================================ //! Immediate operand. //! //! Immediate operand is usually part of instruction itself. It's inlined after //! or before the instruction opcode. Immediates can be only signed or unsigned //! integers. //! //! To create immediate operand use `imm()` or `imm_u()` non-members or `Imm` //! constructors. class Imm : public Operand { public: // -------------------------------------------------------------------------- // [Construction / Destruction] // -------------------------------------------------------------------------- //! Create a new immediate value (initial value is 0). Imm() noexcept : Operand(NoInit) { _init_packed_d0_d1(kOpImm, 0); _imm.value.i64 = 0; } //! Create a new signed immediate value, assigning the value to `val`. explicit Imm(int64_t val) noexcept : Operand(NoInit) { _init_packed_d0_d1(kOpImm, 0); _imm.value.i64 = val; } //! Create a new immediate value from `other`. ASMJIT_INLINE Imm(const Imm& other) noexcept : Operand(other) {} explicit ASMJIT_INLINE Imm(const _NoInit&) noexcept : Operand(NoInit) {} // -------------------------------------------------------------------------- // [Immediate Specific] // -------------------------------------------------------------------------- //! Clone `Imm` operand. ASMJIT_INLINE Imm clone() const noexcept { return Imm(*this); } //! Get whether the immediate can be casted to 8-bit signed integer. ASMJIT_INLINE bool isInt8() const noexcept { return Utils::isInt8(_imm.value.i64); } //! Get whether the immediate can be casted to 8-bit unsigned integer. ASMJIT_INLINE bool isUInt8() const noexcept { return Utils::isUInt8(_imm.value.i64); } //! Get whether the immediate can be casted to 16-bit signed integer. ASMJIT_INLINE bool isInt16() const noexcept { return Utils::isInt16(_imm.value.i64); } //! Get whether the immediate can be casted to 16-bit unsigned integer. ASMJIT_INLINE bool isUInt16() const noexcept { return Utils::isUInt16(_imm.value.i64); } //! Get whether the immediate can be casted to 32-bit signed integer. ASMJIT_INLINE bool isInt32() const noexcept { return Utils::isInt32(_imm.value.i64); } //! Get whether the immediate can be casted to 32-bit unsigned integer. ASMJIT_INLINE bool isUInt32() const noexcept { return Utils::isUInt32(_imm.value.i64); } //! Get immediate value as 8-bit signed integer. ASMJIT_INLINE int8_t getInt8() const noexcept { return static_cast(_imm.value.i32Lo & 0xFF); } //! Get immediate value as 8-bit unsigned integer. ASMJIT_INLINE uint8_t getUInt8() const noexcept { return static_cast(_imm.value.u32Lo & 0xFFU); } //! Get immediate value as 16-bit signed integer. ASMJIT_INLINE int16_t getInt16() const noexcept { return static_cast(_imm.value.i32Lo & 0xFFFF);} //! Get immediate value as 16-bit unsigned integer. ASMJIT_INLINE uint16_t getUInt16() const noexcept { return static_cast(_imm.value.u32Lo & 0xFFFFU);} //! Get immediate value as 32-bit signed integer. ASMJIT_INLINE int32_t getInt32() const noexcept { return _imm.value.i32Lo; } //! Get low 32-bit signed integer. ASMJIT_INLINE int32_t getInt32Lo() const noexcept { return _imm.value.i32Lo; } //! Get high 32-bit signed integer. ASMJIT_INLINE int32_t getInt32Hi() const noexcept { return _imm.value.i32Hi; } //! Get immediate value as 32-bit unsigned integer. ASMJIT_INLINE uint32_t getUInt32() const noexcept { return _imm.value.u32Lo; } //! Get low 32-bit signed integer. ASMJIT_INLINE uint32_t getUInt32Lo() const noexcept { return _imm.value.u32Lo; } //! Get high 32-bit signed integer. ASMJIT_INLINE uint32_t getUInt32Hi() const noexcept { return _imm.value.u32Hi; } //! Get immediate value as 64-bit signed integer. ASMJIT_INLINE int64_t getInt64() const noexcept { return _imm.value.i64; } //! Get immediate value as 64-bit unsigned integer. ASMJIT_INLINE uint64_t getUInt64() const noexcept { return _imm.value.u64; } //! Get immediate value as `intptr_t`. ASMJIT_INLINE intptr_t getIntPtr() const noexcept { if (sizeof(intptr_t) == sizeof(int64_t)) return static_cast(getInt64()); else return static_cast(getInt32()); } //! Get immediate value as `uintptr_t`. ASMJIT_INLINE uintptr_t getUIntPtr() const noexcept { if (sizeof(uintptr_t) == sizeof(uint64_t)) return static_cast(getUInt64()); else return static_cast(getUInt32()); } //! Set immediate value to 8-bit signed integer `val`. ASMJIT_INLINE void setInt8(int8_t val) noexcept { _imm.value.i64 = static_cast(val); } //! Set immediate value to 8-bit unsigned integer `val`. ASMJIT_INLINE void setUInt8(uint8_t val) noexcept { _imm.value.u64 = static_cast(val); } //! Set immediate value to 16-bit signed integer `val`. ASMJIT_INLINE void setInt16(int16_t val) noexcept { _imm.value.i64 = static_cast(val); } //! Set immediate value to 16-bit unsigned integer `val`. ASMJIT_INLINE void setUInt16(uint16_t val) noexcept { _imm.value.u64 = static_cast(val); } //! Set immediate value to 32-bit signed integer `val`. ASMJIT_INLINE void setInt32(int32_t val) noexcept { _imm.value.i64 = static_cast(val); } //! Set immediate value to 32-bit unsigned integer `val`. ASMJIT_INLINE void setUInt32(uint32_t val) noexcept { _imm.value.u64 = static_cast(val); } //! Set immediate value to 64-bit signed integer `val`. ASMJIT_INLINE void setInt64(int64_t val) noexcept { _imm.value.i64 = val; } //! Set immediate value to 64-bit unsigned integer `val`. ASMJIT_INLINE void setUInt64(uint64_t val) noexcept { _imm.value.u64 = val; } //! Set immediate value to intptr_t `val`. ASMJIT_INLINE void setIntPtr(intptr_t val) noexcept { _imm.value.i64 = static_cast(val); } //! Set immediate value to uintptr_t `val`. ASMJIT_INLINE void setUIntPtr(uintptr_t val) noexcept { _imm.value.u64 = static_cast(val); } //! Set immediate value as unsigned type to `val`. ASMJIT_INLINE void setPtr(void* p) noexcept { setIntPtr((uint64_t)p); } //! Set immediate value to `val`. template ASMJIT_INLINE void setValue(T val) noexcept { setIntPtr((int64_t)val); } // -------------------------------------------------------------------------- // [Float] // -------------------------------------------------------------------------- ASMJIT_INLINE void setFloat(float f) noexcept { _imm.value.f32Lo = f; _imm.value.u32Hi = 0; } ASMJIT_INLINE void setDouble(double d) noexcept { _imm.value.f64 = d; } // -------------------------------------------------------------------------- // [Truncate] // -------------------------------------------------------------------------- ASMJIT_INLINE void truncateTo8Bits() noexcept { if (ASMJIT_ARCH_64BIT) { _imm.value.u64 &= static_cast(0x000000FFU); } else { _imm.value.u32Lo &= 0x000000FFU; _imm.value.u32Hi = 0; } } ASMJIT_INLINE void truncateTo16Bits() noexcept { if (ASMJIT_ARCH_64BIT) { _imm.value.u64 &= static_cast(0x0000FFFFU); } else { _imm.value.u32Lo &= 0x0000FFFFU; _imm.value.u32Hi = 0; } } ASMJIT_INLINE void truncateTo32Bits() noexcept { _imm.value.u32Hi = 0; } // -------------------------------------------------------------------------- // [Operator Overload] // -------------------------------------------------------------------------- //! Assign `other` to the immediate operand. ASMJIT_INLINE Imm& operator=(const Imm& other) noexcept { copyFrom(other); return *this; } }; //! Create a signed immediate operand. static ASMJIT_INLINE Imm imm(int64_t val) noexcept { return Imm(val); } //! Create an unsigned immediate operand. static ASMJIT_INLINE Imm imm_u(uint64_t val) noexcept { return Imm(static_cast(val)); } //! Create an immediate operand from `p`. template static ASMJIT_INLINE Imm imm_ptr(T p) noexcept { return Imm(static_cast((intptr_t)p)); } // ============================================================================ // [asmjit::TypeId] // ============================================================================ //! Type-id. //! //! This is an additional information that can be used to describe a physical //! or virtual register. it's used mostly by CodeCompiler to describe register //! representation (the kind of data stored in the register and the width used) //! and it's also used by APIs that allow to describe and work with function //! signatures. struct TypeId { // -------------------------------------------------------------------------- // [Id] // -------------------------------------------------------------------------- enum Id { kVoid = 0, _kIntStart = 32, _kIntEnd = 41, kIntPtr = 32, kUIntPtr = 33, kI8 = 34, kU8 = 35, kI16 = 36, kU16 = 37, kI32 = 38, kU32 = 39, kI64 = 40, kU64 = 41, _kFloatStart = 42, _kFloatEnd = 44, kF32 = 42, kF64 = 43, kF80 = 44, _kMaskStart = 45, _kMaskEnd = 48, kMask8 = 45, kMask16 = 46, kMask32 = 47, kMask64 = 48, _kMmxStart = 49, _kMmxEnd = 50, kMmx32 = 49, kMmx64 = 50, _kVec32Start = 51, _kVec32End = 60, kI8x4 = 51, kU8x4 = 52, kI16x2 = 53, kU16x2 = 54, kI32x1 = 55, kU32x1 = 56, kF32x1 = 59, _kVec64Start = 61, _kVec64End = 70, kI8x8 = 61, kU8x8 = 62, kI16x4 = 63, kU16x4 = 64, kI32x2 = 65, kU32x2 = 66, kI64x1 = 67, kU64x1 = 68, kF32x2 = 69, kF64x1 = 70, _kVec128Start = 71, _kVec128End = 80, kI8x16 = 71, kU8x16 = 72, kI16x8 = 73, kU16x8 = 74, kI32x4 = 75, kU32x4 = 76, kI64x2 = 77, kU64x2 = 78, kF32x4 = 79, kF64x2 = 80, _kVec256Start = 81, _kVec256End = 90, kI8x32 = 81, kU8x32 = 82, kI16x16 = 83, kU16x16 = 84, kI32x8 = 85, kU32x8 = 86, kI64x4 = 87, kU64x4 = 88, kF32x8 = 89, kF64x4 = 90, _kVec512Start = 91, _kVec512End = 100, kI8x64 = 91, kU8x64 = 92, kI16x32 = 93, kU16x32 = 94, kI32x16 = 95, kU32x16 = 96, kI64x8 = 97, kU64x8 = 98, kF32x16 = 99, kF64x8 = 100, kCount = 101 }; // -------------------------------------------------------------------------- // [TypeName - Used by Templates] // -------------------------------------------------------------------------- struct Int8 {}; //!< int8_t as C++ type-name. struct UInt8 {}; //!< uint8_t as C++ type-name. struct Int16 {}; //!< int16_t as C++ type-name. struct UInt16 {}; //!< uint16_t as C++ type-name. struct Int32 {}; //!< int32_t as C++ type-name. struct UInt32 {}; //!< uint32_t as C++ type-name. struct Int64 {}; //!< int64_t as C++ type-name. struct UInt64 {}; //!< uint64_t as C++ type-name. struct IntPtr {}; //!< intptr_t as C++ type-name. struct UIntPtr {}; //!< uintptr_t as C++ type-name. struct Float {}; //!< float as C++ type-name. struct Double {}; //!< double as C++ type-name. struct MmxReg {}; //!< MMX register as C++ type-name. struct Vec128 {}; //!< SIMD128/XMM register as C++ type-name. struct Vec256 {}; //!< SIMD256/YMM register as C++ type-name. struct Vec512 {}; //!< SIMD512/ZMM register as C++ type-name. // -------------------------------------------------------------------------- // [Utilities] // -------------------------------------------------------------------------- struct Info { uint8_t sizeOf[128]; uint8_t elementOf[128]; }; ASMJIT_API static const Info _info; static ASMJIT_INLINE bool isVoid(uint32_t typeId) noexcept { return typeId == 0; } static ASMJIT_INLINE bool isValid(uint32_t typeId) noexcept { return typeId >= _kIntStart && typeId <= _kVec512End; } static ASMJIT_INLINE bool isAbstract(uint32_t typeId) noexcept { return typeId >= kIntPtr && typeId <= kUIntPtr; } static ASMJIT_INLINE bool isInt(uint32_t typeId) noexcept { return typeId >= _kIntStart && typeId <= _kIntEnd; } static ASMJIT_INLINE bool isGpb(uint32_t typeId) noexcept { return typeId >= kI8 && typeId <= kU8; } static ASMJIT_INLINE bool isGpw(uint32_t typeId) noexcept { return typeId >= kI16 && typeId <= kU16; } static ASMJIT_INLINE bool isGpd(uint32_t typeId) noexcept { return typeId >= kI32 && typeId <= kU32; } static ASMJIT_INLINE bool isGpq(uint32_t typeId) noexcept { return typeId >= kI64 && typeId <= kU64; } static ASMJIT_INLINE bool isFloat(uint32_t typeId) noexcept { return typeId >= _kFloatStart && typeId <= _kFloatEnd; } static ASMJIT_INLINE bool isMask(uint32_t typeId) noexcept { return typeId >= _kMaskStart && typeId <= _kMaskEnd; } static ASMJIT_INLINE bool isMmx(uint32_t typeId) noexcept { return typeId >= _kMmxStart && typeId <= _kMmxEnd; } static ASMJIT_INLINE bool isVec(uint32_t typeId) noexcept { return typeId >= _kVec32Start && typeId <= _kVec512End; } static ASMJIT_INLINE bool isVec32(uint32_t typeId) noexcept { return typeId >= _kVec32Start && typeId <= _kVec32End; } static ASMJIT_INLINE bool isVec64(uint32_t typeId) noexcept { return typeId >= _kVec64Start && typeId <= _kVec64End; } static ASMJIT_INLINE bool isVec128(uint32_t typeId) noexcept { return typeId >= _kVec128Start && typeId <= _kVec128End; } static ASMJIT_INLINE bool isVec256(uint32_t typeId) noexcept { return typeId >= _kVec256Start && typeId <= _kVec256End; } static ASMJIT_INLINE bool isVec512(uint32_t typeId) noexcept { return typeId >= _kVec512Start && typeId <= _kVec512End; } static ASMJIT_INLINE uint32_t sizeOf(uint32_t typeId) noexcept { ASMJIT_ASSERT(typeId < ASMJIT_ARRAY_SIZE(_info.sizeOf)); return _info.sizeOf[typeId]; } static ASMJIT_INLINE uint32_t elementOf(uint32_t typeId) noexcept { ASMJIT_ASSERT(typeId < ASMJIT_ARRAY_SIZE(_info.elementOf)); return _info.elementOf[typeId]; } //! Get an offset to convert a `kIntPtr` and `kUIntPtr` TypeId into a //! type that matches `gpSize` (general-purpose register size). If you //! find such TypeId it's then only about adding the offset to it. //! //! For example: //! ~~~ //! uint32_t gpSize = '4' or '8'; //! uint32_t deabstractDelta = TypeId::deabstractDeltaOfSize(gpSize); //! //! uint32_t typeId = 'some type-id'; //! //! // Normalize some typeId into a non-abstract typeId. //! if (TypeId::isAbstract(typeId)) typeId += deabstractDelta; //! //! // The same, but by using TypeId::deabstract() function. //! typeId = TypeId::deabstract(typeId, deabstractDelta); //! ~~~ static ASMJIT_INLINE uint32_t deabstractDeltaOfSize(uint32_t gpSize) noexcept { return gpSize >= 8 ? kI64 - kIntPtr : kI32 - kIntPtr; } static ASMJIT_INLINE uint32_t deabstract(uint32_t typeId, uint32_t deabstractDelta) noexcept { return TypeId::isAbstract(typeId) ? typeId += deabstractDelta : typeId; } }; //! TypeIdOf<> template allows to get a TypeId of a C++ type. template struct TypeIdOf { // Don't provide anything if not specialized. }; template struct TypeIdOf { enum { kTypeId = TypeId::kUIntPtr }; }; template struct TypeIdOfInt { enum { kSigned = int(~T(0) < T(0)), kTypeId = (sizeof(T) == 1) ? (int)(kSigned ? TypeId::kI8 : TypeId::kU8 ) : (sizeof(T) == 2) ? (int)(kSigned ? TypeId::kI16 : TypeId::kU16) : (sizeof(T) == 4) ? (int)(kSigned ? TypeId::kI32 : TypeId::kU32) : (sizeof(T) == 8) ? (int)(kSigned ? TypeId::kI64 : TypeId::kU64) : (int)TypeId::kVoid }; }; #define ASMJIT_DEFINE_TYPE_ID(T, TYPE_ID) \ template<> \ struct TypeIdOf { enum { kTypeId = TYPE_ID}; } ASMJIT_DEFINE_TYPE_ID(signed char , TypeIdOfInt< signed char >::kTypeId); ASMJIT_DEFINE_TYPE_ID(unsigned char , TypeIdOfInt< unsigned char >::kTypeId); ASMJIT_DEFINE_TYPE_ID(short , TypeIdOfInt< short >::kTypeId); ASMJIT_DEFINE_TYPE_ID(unsigned short , TypeIdOfInt< unsigned short >::kTypeId); ASMJIT_DEFINE_TYPE_ID(int , TypeIdOfInt< int >::kTypeId); ASMJIT_DEFINE_TYPE_ID(unsigned int , TypeIdOfInt< unsigned int >::kTypeId); ASMJIT_DEFINE_TYPE_ID(long , TypeIdOfInt< long >::kTypeId); ASMJIT_DEFINE_TYPE_ID(unsigned long , TypeIdOfInt< unsigned long >::kTypeId); #if ASMJIT_CC_MSC && !ASMJIT_CC_MSC_GE(16, 0, 0) ASMJIT_DEFINE_TYPE_ID(__int64 , TypeIdOfInt< __int64 >::kTypeId); ASMJIT_DEFINE_TYPE_ID(unsigned __int64 , TypeIdOfInt< unsigned __int64 >::kTypeId); #else ASMJIT_DEFINE_TYPE_ID(long long , TypeIdOfInt< long long >::kTypeId); ASMJIT_DEFINE_TYPE_ID(unsigned long long, TypeIdOfInt< unsigned long long >::kTypeId); #endif #if ASMJIT_CC_HAS_NATIVE_CHAR ASMJIT_DEFINE_TYPE_ID(char , TypeIdOfInt< char >::kTypeId); #endif #if ASMJIT_CC_HAS_NATIVE_CHAR16_T ASMJIT_DEFINE_TYPE_ID(char16_t , TypeIdOfInt< char16_t >::kTypeId); #endif #if ASMJIT_CC_HAS_NATIVE_CHAR32_T ASMJIT_DEFINE_TYPE_ID(char32_t , TypeIdOfInt< char32_t >::kTypeId); #endif #if ASMJIT_CC_HAS_NATIVE_WCHAR_T ASMJIT_DEFINE_TYPE_ID(wchar_t , TypeIdOfInt< wchar_t >::kTypeId); #endif ASMJIT_DEFINE_TYPE_ID(void , TypeId::kVoid); ASMJIT_DEFINE_TYPE_ID(bool , TypeId::kI8); ASMJIT_DEFINE_TYPE_ID(float , TypeId::kF32); ASMJIT_DEFINE_TYPE_ID(double , TypeId::kF64); ASMJIT_DEFINE_TYPE_ID(TypeId::Int8 , TypeId::kI8); ASMJIT_DEFINE_TYPE_ID(TypeId::UInt8 , TypeId::kU8); ASMJIT_DEFINE_TYPE_ID(TypeId::Int16 , TypeId::kI16); ASMJIT_DEFINE_TYPE_ID(TypeId::UInt16 , TypeId::kU16); ASMJIT_DEFINE_TYPE_ID(TypeId::Int32 , TypeId::kI32); ASMJIT_DEFINE_TYPE_ID(TypeId::UInt32 , TypeId::kU32); ASMJIT_DEFINE_TYPE_ID(TypeId::Int64 , TypeId::kI64); ASMJIT_DEFINE_TYPE_ID(TypeId::UInt64 , TypeId::kU64); ASMJIT_DEFINE_TYPE_ID(TypeId::IntPtr , TypeId::kIntPtr); ASMJIT_DEFINE_TYPE_ID(TypeId::UIntPtr , TypeId::kUIntPtr); ASMJIT_DEFINE_TYPE_ID(TypeId::Float , TypeId::kF32); ASMJIT_DEFINE_TYPE_ID(TypeId::Double , TypeId::kF64); ASMJIT_DEFINE_TYPE_ID(TypeId::MmxReg , TypeId::kMmx64); ASMJIT_DEFINE_TYPE_ID(TypeId::Vec128 , TypeId::kI32x4); ASMJIT_DEFINE_TYPE_ID(TypeId::Vec256 , TypeId::kI32x8); ASMJIT_DEFINE_TYPE_ID(TypeId::Vec512 , TypeId::kI32x16); //! \} } // asmjit namespace // [Api-End] #include "../asmjit_apiend.h" // [Guard] #endif // _ASMJIT_BASE_OPERAND_H