raze-gles/libraries/asmjit/asmjit/base/operand.h
2020-05-23 22:43:03 +02:00

1574 lines
70 KiB
C++

// [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"
#ifdef _MSC_VER
#pragma warning(disable: 4804) // warning C4804: '~': unsafe use of type 'bool' in operation
#endif
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<uint32_t>(getOp(), kOpReg, kOpMem);
}
//! Cast this operand to `T` type.
template<typename T>
ASMJIT_INLINE T& as() noexcept { return static_cast<T&>(*this); }
//! Cast this operand to `T` type (const).
template<typename T>
ASMJIT_INLINE const T& as() const noexcept { return static_cast<const T&>(*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<typename T>
ASMJIT_INLINE bool operator==(const T& other) const noexcept { return isEqual(other); }
template<typename T>
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<typename RegT>
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<typename RegT>
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<typename RegT>
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<typename RegT>
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<uint32_t>(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<int32_t>(_mem.offsetLo32);
int32_t hi = static_cast<int32_t>(_mem.base) & -static_cast<int32_t>(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<int64_t>(_mem.offset64)
: static_cast<int64_t>(static_cast<int32_t>(_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<int32_t>(_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<int32_t>(_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<uint64_t>(offset);
else
_mem.offsetLo32 = static_cast<int32_t>(offset & 0xFFFFFFFF);
}
//! Adjust the offset by a 64-bit `off`.
ASMJIT_INLINE void addOffset(int64_t off) noexcept {
if (has64BitOffset())
_mem.offset64 += static_cast<uint64_t>(off);
else
_mem.offsetLo32 += static_cast<uint32_t>(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<uint32_t>(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<uint32_t>(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<int8_t>(_imm.value.i32Lo & 0xFF); }
//! Get immediate value as 8-bit unsigned integer.
ASMJIT_INLINE uint8_t getUInt8() const noexcept { return static_cast<uint8_t>(_imm.value.u32Lo & 0xFFU); }
//! Get immediate value as 16-bit signed integer.
ASMJIT_INLINE int16_t getInt16() const noexcept { return static_cast<int16_t>(_imm.value.i32Lo & 0xFFFF);}
//! Get immediate value as 16-bit unsigned integer.
ASMJIT_INLINE uint16_t getUInt16() const noexcept { return static_cast<uint16_t>(_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<intptr_t>(getInt64());
else
return static_cast<intptr_t>(getInt32());
}
//! Get immediate value as `uintptr_t`.
ASMJIT_INLINE uintptr_t getUIntPtr() const noexcept {
if (sizeof(uintptr_t) == sizeof(uint64_t))
return static_cast<uintptr_t>(getUInt64());
else
return static_cast<uintptr_t>(getUInt32());
}
//! Set immediate value to 8-bit signed integer `val`.
ASMJIT_INLINE void setInt8(int8_t val) noexcept { _imm.value.i64 = static_cast<int64_t>(val); }
//! Set immediate value to 8-bit unsigned integer `val`.
ASMJIT_INLINE void setUInt8(uint8_t val) noexcept { _imm.value.u64 = static_cast<uint64_t>(val); }
//! Set immediate value to 16-bit signed integer `val`.
ASMJIT_INLINE void setInt16(int16_t val) noexcept { _imm.value.i64 = static_cast<int64_t>(val); }
//! Set immediate value to 16-bit unsigned integer `val`.
ASMJIT_INLINE void setUInt16(uint16_t val) noexcept { _imm.value.u64 = static_cast<uint64_t>(val); }
//! Set immediate value to 32-bit signed integer `val`.
ASMJIT_INLINE void setInt32(int32_t val) noexcept { _imm.value.i64 = static_cast<int64_t>(val); }
//! Set immediate value to 32-bit unsigned integer `val`.
ASMJIT_INLINE void setUInt32(uint32_t val) noexcept { _imm.value.u64 = static_cast<uint64_t>(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<int64_t>(val); }
//! Set immediate value to uintptr_t `val`.
ASMJIT_INLINE void setUIntPtr(uintptr_t val) noexcept { _imm.value.u64 = static_cast<uint64_t>(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<typename T>
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<uint64_t>(0x000000FFU);
}
else {
_imm.value.u32Lo &= 0x000000FFU;
_imm.value.u32Hi = 0;
}
}
ASMJIT_INLINE void truncateTo16Bits() noexcept {
if (ASMJIT_ARCH_64BIT) {
_imm.value.u64 &= static_cast<uint64_t>(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<int64_t>(val)); }
//! Create an immediate operand from `p`.
template<typename T>
static ASMJIT_INLINE Imm imm_ptr(T p) noexcept { return Imm(static_cast<int64_t>((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<typename T> struct TypeIdOf {
// Don't provide anything if not specialized.
};
template<typename T> struct TypeIdOf<T*> {
enum { kTypeId = TypeId::kUIntPtr };
};
template<typename T>
struct TypeIdOfInt {
enum {
kSignatureed = int(~static_cast<T>(0) < static_cast<T>(0)),
kTypeId = (sizeof(T) == 1) ? (int)(kSignatureed ? TypeId::kI8 : TypeId::kU8 ) :
(sizeof(T) == 2) ? (int)(kSignatureed ? TypeId::kI16 : TypeId::kU16) :
(sizeof(T) == 4) ? (int)(kSignatureed ? TypeId::kI32 : TypeId::kU32) :
(sizeof(T) == 8) ? (int)(kSignatureed ? TypeId::kI64 : TypeId::kU64) : (int)TypeId::kVoid
};
};
#define ASMJIT_DEFINE_TYPE_ID(T, TYPE_ID) \
template<> \
struct TypeIdOf<T> { 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
ASMJIT_DEFINE_TYPE_ID(bool , TypeIdOfInt< bool >::kTypeId);
#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(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