Files
asmjit/src/asmjit/base/operand.h

1131 lines
36 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 - AsmJit]
#include "../base/intutil.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [Forward Declarations]
// ============================================================================
struct BaseAssembler;
struct BaseCompiler;
//! \addtogroup asmjit_base_general
//! \{
// ============================================================================
// [asmjit::kOperandType]
// ============================================================================
//! Operand types that can be encoded in `Operand`.
ASMJIT_ENUM(kOperandType) {
//! Invalid operand, used only internally (not initialized Operand).
kOperandTypeNone = 0,
//! Operand is a register.
kOperandTypeReg = 1,
//! Operand is a variable.
kOperandTypeVar = 2,
//! Operand is a memory.
kOperandTypeMem = 3,
//! Operand is an immediate value.
kOperandTypeImm = 4,
//! Operand is a label.
kOperandTypeLabel = 5
};
// ============================================================================
// [asmjit::kOperandId]
// ============================================================================
//! Operand id masks used to determine the operand type.
ASMJIT_ENUM(kOperandId) {
//! Operand id refers to `BaseVar`.
kOperandIdVar = 0x80000000U,
//! Operand id to real index mask.
kOperandIdNum = 0x7FFFFFFFU
};
// ============================================================================
// [asmjit::kRegClass]
// ============================================================================
//! Register class.
ASMJIT_ENUM(kRegClass) {
//! Gp register class (any architecture).
kRegClassGp = 0,
//! Invalid register class.
kRegClassInvalid = 0xFF
};
// ============================================================================
// [asmjit::kSize]
// ============================================================================
//! Common size of registers and pointers.
ASMJIT_ENUM(kSize) {
//! 1 byte size (BYTE).
kSizeByte = 1,
//! 2 bytes size (WORD).
kSizeWord = 2,
//! 4 bytes size (DWORD).
kSizeDWord = 4,
//! 8 bytes size (QWORD).
kSizeQWord = 8,
//! 10 bytes size (TWORD).
kSizeTWord = 10,
//! 16 bytes size (OWORD / DQWORD).
kSizeOWord = 16,
//! 32 bytes size (YWORD / QQWORD).
kSizeYWord = 32
};
// ============================================================================
// [asmjit::kMemType]
// ============================================================================
//! Type of memory operand.
ASMJIT_ENUM(kMemType) {
//! Memory operand is a combination of base register and optional index register
//! and displacement.
//!
//! The `BaseAssembler` interprets `kMemTypeBaseIndex` and `kMemTypeStackIndex`
//! types the same way, but `Compiler` interprets `kMemTypeBaseIndex` as
//! `[base + index]` and `kMemTypeStackIndex` as `[stack(base) + index]`.
kMemTypeBaseIndex = 0,
//! Memory operand is a combination of variable's memory location,
//! optional index register and displacement.
//!
//! The `BaseAssembler` interprets `kMemTypeBaseIndex` and `kMemTypeStackIndex`
//! types in the same way, but `BaseCompiler` interprets `kMemTypeBaseIndex` as
//! `[base + index]` and `kMemTypeStackIndex` as `[stack(base) + index]`.
kMemTypeStackIndex = 1,
//! Memory operand refers to the memory location specified by a label.
kMemTypeLabel = 2,
//! Memory operand is an absolute memory location.
//!
//! Supported mostly by x86, truncated to a 32-bit value when running in
//! 64-bit mode (x64).
kMemTypeAbsolute = 3
};
// ============================================================================
// [asmjit::kVarType]
// ============================================================================
ASMJIT_ENUM(kVarType) {
//! Variable is 8-bit signed integer.
kVarTypeInt8 = 0,
//! Variable is 8-bit unsigned integer.
kVarTypeUInt8 = 1,
//! Variable is 16-bit signed integer.
kVarTypeInt16 = 2,
//! Variable is 16-bit unsigned integer.
kVarTypeUInt16 = 3,
//! Variable is 32-bit signed integer.
kVarTypeInt32 = 4,
//! Variable is 32-bit unsigned integer.
kVarTypeUInt32 = 5,
//! Variable is 64-bit signed integer.
kVarTypeInt64 = 6,
//! Variable is 64-bit unsigned integer.
kVarTypeUInt64 = 7,
//! Variable is target `intptr_t`, not compatible with host `intptr_t`.
kVarTypeIntPtr = 8,
//! Variable is target `uintptr_t`, not compatible with host `uintptr_t`.
kVarTypeUIntPtr = 9,
//! Variable is 32-bit floating point (single precision).
kVarTypeFp32 = 10,
//! Variable is 64-bit floating point (double precision).
kVarTypeFp64 = 11,
//! Invalid variable type.
kVarTypeInvalid = 0xFF,
//! \internal
_kVarTypeIntStart = kVarTypeInt8,
//! \internal
_kVarTypeIntEnd = kVarTypeUIntPtr,
//! \internal
_kVarTypeFpStart = kVarTypeFp32,
//! \internal
_kVarTypeFpEnd = kVarTypeFp64
};
// ============================================================================
// [asmjit::Operand]
// ============================================================================
//! Operand can contain register, memory location, immediate, or label.
struct Operand {
// --------------------------------------------------------------------------
// [Structs]
// --------------------------------------------------------------------------
// \internal
//
// Register operand structure, allows to do register initialization at
// compile time instead of doing it "non-deterministically" at runtime.
struct InitRegOp {
uint8_t op;
uint8_t size;
uint16_t code;
uint32_t id;
uint32_t vType;
uint32_t vUnused;
};
//! \internal
//!
//! Base operand data.
struct BaseOp {
//! Type of operand, see `kOperandType`.
uint8_t op;
//! Size of operand (register, address, immediate, or variable).
uint8_t size;
//! Flags, each operand uses this byte for something else.
uint8_t reserved0;
//! Reserved (not used).
uint8_t reserved1;
//! Operand id, identifier used by `BaseAssembler` and `BaseCompiler`.
//!
//! \note Uninitialized operand has always set id to `kInvalidValue`.
uint32_t id;
};
//! \internal
//!
//! Register or Variable operand data.
struct VRegOp {
//! Type of operand, `kOperandTypeReg`.
uint8_t op;
//! Size of register or variable.
uint8_t size;
union {
//! Register code = (type << 8) | index.
uint16_t code;
//! Register type and index access.
struct {
#if defined(ASMJIT_HOST_LE)
//! Register index.
uint8_t index;
//! Register type.
uint8_t type;
#else
//! Register type.
uint8_t type;
//! Register index.
uint8_t index;
#endif // ASMJIT_HOST
};
};
//! Variable id, used by `BaseCompiler` to identify variables.
uint32_t id;
//! Variable type.
uint32_t vType;
//! \internal
//!
//! Unused.
uint32_t vUnused;
};
//! \internal
//!
//! Memory or Variable operand data.
struct VMemOp {
//! Type of operand, `kOperandTypeMem`.
uint8_t op;
//! Size of the pointer in bytes.
uint8_t size;
//! Type of the memory operand, see `kMemType`.
uint8_t type;
//! X86/X64 layout:
//! - segment [3 bits], see `x86x64::kSeg`.
//! - shift [2 bits], index register shift (0 to 3).
uint8_t flags;
//! Base register, variable or label id.
uint32_t base;
//! Index register or variable.
uint32_t index;
//! 32-bit displacement or absolute address.
int32_t displacement;
};
//! \internal
//!
//! Immediate operand data.
struct ImmOp {
//! Type of operand, `kOperandTypeImm`.
uint8_t op;
//! Size of immediate (or 0 to autodetect).
uint8_t size;
//! Reserved (not used).
uint8_t reserved0;
//! Reserved (not used).
uint8_t reserved1;
//! Operand id, always set to `kInvalidValue`.
uint32_t id;
union {
//! 8x8-bit signed immediate values.
int8_t _i8[8];
//! 8x8-bit unsigned immediate values.
uint8_t _u8[8];
//! 4x16-bit signed immediate values.
int16_t _i16[4];
//! 4x16-bit unsigned immediate values.
uint16_t _u16[4];
//! 2x32-bit signed immediate values.
int32_t _i32[2];
//! 2x32-bit unsigned immediate values.
uint32_t _u32[2];
//! 1x64-bit signed immediate value.
int64_t _i64[1];
//! 1x64-bit unsigned immediate value.
uint64_t _u64[1];
//! 2x SP-FP values.
float _f32[2];
//! 1x DP-FP value.
double _f64[1];
} value;
};
//! \internal
//!
//! Label operand data.
struct LabelOp {
//! Type of operand, `kOperandTypeLabel`.
uint8_t op;
//! Reserved (not used).
uint8_t size;
//! Reserved (not used).
uint8_t reserved0;
//! Reserved (not used).
uint8_t reserved1;
//! Operand id.
uint32_t id;
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create an uninitialized operand.
ASMJIT_INLINE Operand() {
_init_packed_op_sz_b0_b1_id(kOperandTypeNone, 0, 0, 0, kInvalidValue);
_init_packed_d2_d3(0, 0);
}
//! Create a reference to `other` operand.
ASMJIT_INLINE Operand(const Operand& other) {
_init(other);
}
explicit ASMJIT_INLINE Operand(const _NoInit&) {}
// --------------------------------------------------------------------------
// [Operand]
// --------------------------------------------------------------------------
//! Clone `Operand`.
ASMJIT_INLINE Operand clone() const {
return Operand(*this);
}
// --------------------------------------------------------------------------
// [Init & Copy]
// --------------------------------------------------------------------------
//! \internal
//!
//! Initialize operand to `other` (used by constructors).
ASMJIT_INLINE void _init(const Operand& other) {
::memcpy(this, &other, sizeof(Operand));
}
ASMJIT_INLINE void _init_packed_op_sz_b0_b1_id(uint32_t op, uint32_t sz, uint32_t r0, uint32_t r1, uint32_t id) {
// This hack is not for performance, but to decrease the size of the binary
// generated when constructing AsmJit operands (mostly for third parties).
// Some compilers are not able to join four BYTE writes to a single DWORD
// write. Because the 'a', 'b', 'c' and 'd' variables are usually compile
// time constants the compiler can do a really nice job if they are joined
// by using bitwise operations.
_packed[0].setPacked_2x32(IntUtil::pack32_4x8(op, sz, r0, r1), id);
}
ASMJIT_INLINE void _init_packed_op_sz_w0_id(uint32_t op, uint32_t sz, uint32_t w0, uint32_t id) {
_packed[0].setPacked_2x32(IntUtil::pack32_2x8_1x16(op, sz, w0), id);
}
ASMJIT_INLINE void _init_packed_d0_d1(uint32_t u0, uint32_t u1) {
_packed[0].setPacked_2x32(u0, u1);
}
ASMJIT_INLINE void _init_packed_d2_d3(uint32_t u2, uint32_t u3) {
_packed[1].setPacked_2x32(u2, u3);
}
//! \internal
//!
//! Initialize operand to `other` (used by assign operators).
ASMJIT_INLINE void _copy(const Operand& other) {
::memcpy(this, &other, sizeof(Operand));
}
// --------------------------------------------------------------------------
// [Data]
// --------------------------------------------------------------------------
template<typename T>
ASMJIT_INLINE T& getData() { return reinterpret_cast<T&>(_base); }
template<typename T>
ASMJIT_INLINE const T& getData() const { return reinterpret_cast<const T&>(_base); }
// --------------------------------------------------------------------------
// [Type]
// --------------------------------------------------------------------------
//! Get type of the operand, see `kOperandType`.
ASMJIT_INLINE uint32_t getOp() const { return _base.op; }
//! Get whether the operand is none - `kOperandTypeNone`.
ASMJIT_INLINE bool isNone() const { return (_base.op == kOperandTypeNone); }
//! Get whether the operand is a register - `kOperandTypeReg`.
ASMJIT_INLINE bool isReg() const { return (_base.op == kOperandTypeReg); }
//! Get whether the operand is a variable - `kOperandTypeVar`.
ASMJIT_INLINE bool isVar() const { return (_base.op == kOperandTypeVar); }
//! Get whether the operand is a memory address - `kOperandTypeMem`.
ASMJIT_INLINE bool isMem() const { return (_base.op == kOperandTypeMem); }
//! Get whether the operand is an immediate value - `kOperandTypeImm`.
ASMJIT_INLINE bool isImm() const { return (_base.op == kOperandTypeImm); }
//! Get whether the operand is a label - `kOperandTypeLabel`.
ASMJIT_INLINE bool isLabel() const { return (_base.op == kOperandTypeLabel); }
// --------------------------------------------------------------------------
// [Type - Combined]
// --------------------------------------------------------------------------
//! Get whether the operand is register of `type`.
ASMJIT_INLINE bool isRegType(uint32_t type) const {
return (_packed[0].u32[0] & IntUtil::pack32_2x8_1x16(0xFF, 0, 0xFF00)) == IntUtil::pack32_2x8_1x16(kOperandTypeReg, 0, (type << 8));
}
//! Get whether the operand is register and of `type` and `index`.
ASMJIT_INLINE bool isRegCode(uint32_t type, uint32_t index) const {
return (_packed[0].u32[0] & IntUtil::pack32_2x8_1x16(0xFF, 0, 0xFFFF)) == IntUtil::pack32_2x8_1x16(kOperandTypeReg, 0, (type << 8) + index);
}
//! Get whether the operand is a register or memory.
ASMJIT_INLINE bool isRegOrMem() const {
ASMJIT_ASSERT(kOperandTypeReg == 1);
ASMJIT_ASSERT(kOperandTypeMem == 3);
return (static_cast<uint32_t>(_base.op) | 0x2U) == 0x3U;
}
//! Get whether the operand is variable or memory.
ASMJIT_INLINE bool isVarOrMem() const {
ASMJIT_ASSERT(kOperandTypeVar == 2);
ASMJIT_ASSERT(kOperandTypeMem == 3);
return (static_cast<uint32_t>(_base.op) - 2U) <= 1;
}
// --------------------------------------------------------------------------
// [Size]
// --------------------------------------------------------------------------
//! Get size of the operand in bytes.
ASMJIT_INLINE uint32_t getSize() const { return _base.size; }
// --------------------------------------------------------------------------
// [Id]
// --------------------------------------------------------------------------
//! Get operand id.
//!
//! Operand id's are used internally by `BaseAssembler` and `BaseCompiler`.
//!
//! There is no way to change or remove operand id. Unneeded operands can be
//! simply reassigned by `operator=`.
ASMJIT_INLINE uint32_t getId() const { return _base.id; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
union {
//! Base data.
BaseOp _base;
//! Register or variable data.
VRegOp _vreg;
//! Memory data.
VMemOp _vmem;
//! Immediate data.
ImmOp _imm;
//! Label data.
LabelOp _label;
//! Packed operand as two 64-bit integers.
UInt64 _packed[2];
};
};
// ============================================================================
// [asmjit::OperandUtil]
// ============================================================================
//! Operand utilities.
struct OperandUtil {
//! Make variable id.
static ASMJIT_INLINE uint32_t makeVarId(uint32_t id) {
return id | kOperandIdVar;
}
//! Make label id.
static ASMJIT_INLINE uint32_t makeLabelId(uint32_t id) {
return id;
}
//! Strip variable id bit so it becomes a pure index to `VarData[]` array.
static ASMJIT_INLINE uint32_t stripVarId(uint32_t id) {
return id & 0x7FFFFFFFU;
}
//! Get whether the id refers to `BaseVar`.
//!
//! \note The function will never return `true` if the id is `kInvalidValue`.
//! The trick is to compare a given id to -1 (kInvalidValue) so we check both
//! using only one comparison.
static ASMJIT_INLINE bool isVarId(uint32_t id) {
return static_cast<int32_t>(id) < -1;
}
//! Get whether the id refers to `Label`.
//!
//! \note The function will never return `true` if the id is `kInvalidValue`.
static ASMJIT_INLINE bool isLabelId(uint32_t id) {
return static_cast<int32_t>(id) >= 0;
}
};
// ============================================================================
// [asmjit::BaseReg]
// ============================================================================
//! Base class for all register operands.
struct BaseReg : public Operand {
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a dummy base register.
ASMJIT_INLINE BaseReg() : Operand(NoInit) {
_init_packed_op_sz_w0_id(kOperandTypeReg, 0, (kInvalidReg << 8) + kInvalidReg, kInvalidValue);
_init_packed_d2_d3(kVarTypeInvalid, 0);
}
//! Create a new base register.
ASMJIT_INLINE BaseReg(uint32_t type, uint32_t index, uint32_t size) : Operand(NoInit) {
_init_packed_op_sz_w0_id(kOperandTypeReg, size, (type << 8) + index, kInvalidValue);
_init_packed_d2_d3(kVarTypeInvalid, 0);
}
//! Create a new reference to `other`.
ASMJIT_INLINE BaseReg(const BaseReg& other) : Operand(other) {}
explicit ASMJIT_INLINE BaseReg(const _NoInit&) : Operand(NoInit) {}
// --------------------------------------------------------------------------
// [BaseReg Specific]
// --------------------------------------------------------------------------
//! Clone `BaseReg` operand.
ASMJIT_INLINE BaseReg clone() const {
return BaseReg(*this);
}
//! Get whether register code is equal to `type`.
ASMJIT_INLINE bool isRegType(uint32_t type) const {
return _vreg.type == type;
}
//! Get whether register code is equal to `type`.
ASMJIT_INLINE bool isRegCode(uint32_t code) const {
return _vreg.code == code;
}
//! Get whether register code is equal to `type`.
ASMJIT_INLINE bool isRegCode(uint32_t type, uint32_t index) const {
return _vreg.code == (type << 8) + index;
}
//! Get register code that equals to '(type << 8) + index'.
ASMJIT_INLINE uint32_t getRegCode() const {
return _vreg.code;
}
//! Get register type.
ASMJIT_INLINE uint32_t getRegType() const {
return _vreg.type;
}
//! Get register index.
ASMJIT_INLINE uint32_t getRegIndex() const {
return _vreg.index;
}
#define ASMJIT_REG_OP(_Type_) \
ASMJIT_INLINE _Type_ clone() const { \
return _Type_(*this); \
} \
\
/*! Set register `size`. */ \
ASMJIT_INLINE _Type_& setSize(uint32_t size) { \
_vreg.size = static_cast<uint8_t>(size); \
return *this; \
} \
\
/*! Set register `code`. */ \
ASMJIT_INLINE _Type_& setCode(uint32_t code) { \
_vreg.code = static_cast<uint16_t>(code); \
return *this; \
} \
\
/*! Set register `type` and `index`. */ \
ASMJIT_INLINE _Type_& setCode(uint32_t type, uint32_t index) { \
_vreg.type = static_cast<uint8_t>(type); \
_vreg.index = static_cast<uint8_t>(index); \
return *this; \
} \
\
/*! Set register `type`. */ \
ASMJIT_INLINE _Type_& setType(uint32_t type) { \
_vreg.type = static_cast<uint8_t>(type); \
return *this; \
} \
\
/*! Set register `index`. */ \
ASMJIT_INLINE _Type_& setIndex(uint32_t index) { \
_vreg.index = static_cast<uint8_t>(index); \
return *this; \
} \
\
ASMJIT_INLINE _Type_& operator=(const _Type_& other) { _copy(other); return *this; } \
\
ASMJIT_INLINE bool operator==(const _Type_& other) const { return _packed[0].u32[0] == other._packed[0].u32[0]; } \
ASMJIT_INLINE bool operator!=(const _Type_& other) const { return !operator==(other); }
};
// ============================================================================
// [asmjit::BaseMem]
// ============================================================================
//! Base class for all memory operands.
struct BaseMem : public Operand {
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE BaseMem() : Operand(NoInit) {
reset();
}
ASMJIT_INLINE BaseMem(const BaseMem& other) : Operand(other) {}
explicit ASMJIT_INLINE BaseMem(const _NoInit&) : Operand(NoInit) {}
// --------------------------------------------------------------------------
// [BaseMem Specific]
// --------------------------------------------------------------------------
//! Clone `BaseMem` operand.
ASMJIT_INLINE BaseMem clone() const {
return BaseMem(*this);
}
//! Reset `BaseMem` operand.
ASMJIT_INLINE void reset() {
_init_packed_op_sz_b0_b1_id(kOperandTypeMem, 0, kMemTypeBaseIndex, 0, kInvalidValue);
_init_packed_d2_d3(kInvalidValue, 0);
}
//! Get the type of the memory operand, see `kMemType`.
ASMJIT_INLINE uint32_t getMemType() const { return _vmem.type; }
//! Get whether the type of the memory operand is either `kMemTypeBaseIndex`
//! or `kMemTypeStackIndex`.
ASMJIT_INLINE bool isBaseIndexType() const { return _vmem.type <= kMemTypeStackIndex; }
//! Get whether the memory operand has base register.
ASMJIT_INLINE bool hasBase() const { return _vmem.base != kInvalidValue; }
//! Get memory operand base id, or `kInvalidValue`.
ASMJIT_INLINE uint32_t getBase() const { return _vmem.base; }
//! Set memory operand size.
ASMJIT_INLINE BaseMem& setSize(uint32_t size) {
_vmem.size = static_cast<uint8_t>(size);
return *this;
}
//! Get memory operand relative displacement.
ASMJIT_INLINE int32_t getDisplacement() const {
return _vmem.displacement;
}
//! Set memory operand relative displacement.
ASMJIT_INLINE BaseMem& setDisplacement(int32_t disp) {
_vmem.displacement = disp;
return *this;
}
// --------------------------------------------------------------------------
// [Operator Overload]
// --------------------------------------------------------------------------
ASMJIT_INLINE BaseMem& operator=(const BaseMem& other) {
_copy(other);
return *this;
}
ASMJIT_INLINE bool operator==(const BaseMem& other) const {
return (_packed[0] == other._packed[0]) & (_packed[1] == other._packed[1]);
}
ASMJIT_INLINE bool operator!=(const BaseMem& other) const { return !(*this == other); }
};
// ============================================================================
// [asmjit::BaseVar]
// ============================================================================
//! Base class for all variables.
struct BaseVar : public Operand {
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE BaseVar() : Operand(NoInit) {
_init_packed_op_sz_b0_b1_id(kOperandTypeVar, 0, 0, 0, kInvalidValue);
_init_packed_d2_d3(kInvalidValue, kInvalidValue);
}
ASMJIT_INLINE BaseVar(const BaseVar& other) : Operand(other) {}
explicit ASMJIT_INLINE BaseVar(const _NoInit&) : Operand(NoInit) {}
// --------------------------------------------------------------------------
// [BaseVar Specific]
// --------------------------------------------------------------------------
//! Clone `BaseVar` operand.
ASMJIT_INLINE BaseVar clone() const {
return BaseVar(*this);
}
ASMJIT_INLINE uint32_t getVarType() const {
return _vreg.vType;
}
// --------------------------------------------------------------------------
// [Operator Overload]
// --------------------------------------------------------------------------
ASMJIT_INLINE BaseVar& operator=(const BaseVar& other) { _copy(other); return *this; }
ASMJIT_INLINE bool operator==(const BaseVar& other) const { return _packed[0] == other._packed[0]; }
ASMJIT_INLINE bool operator!=(const BaseVar& other) const { return !operator==(other); }
};
// ============================================================================
// [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.
struct Imm : public Operand {
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new immediate value (initial value is 0).
Imm() : Operand(NoInit) {
_init_packed_op_sz_b0_b1_id(kOperandTypeImm, 0, 0, 0, kInvalidValue);
_imm.value._i64[0] = 0;
}
//! Create a new signed immediate value, assigning the value to `val`.
explicit Imm(int64_t val) : Operand(NoInit) {
_init_packed_op_sz_b0_b1_id(kOperandTypeImm, 0, 0, 0, kInvalidValue);
_imm.value._i64[0] = val;
}
//! Create a new immediate value from `other`.
ASMJIT_INLINE Imm(const Imm& other) : Operand(other) {}
explicit ASMJIT_INLINE Imm(const _NoInit&) : Operand(NoInit) {}
// --------------------------------------------------------------------------
// [Immediate Specific]
// --------------------------------------------------------------------------
//! Clone `Imm` operand.
ASMJIT_INLINE Imm clone() const {
return Imm(*this);
}
//! Get whether the immediate can be casted to 8-bit signed integer.
ASMJIT_INLINE bool isInt8() const { return IntUtil::isInt8(_imm.value._i64[0]); }
//! Get whether the immediate can be casted to 8-bit unsigned integer.
ASMJIT_INLINE bool isUInt8() const { return IntUtil::isUInt8(_imm.value._i64[0]); }
//! Get whether the immediate can be casted to 16-bit signed integer.
ASMJIT_INLINE bool isInt16() const { return IntUtil::isInt16(_imm.value._i64[0]); }
//! Get whether the immediate can be casted to 16-bit unsigned integer.
ASMJIT_INLINE bool isUInt16() const { return IntUtil::isUInt16(_imm.value._i64[0]); }
//! Get whether the immediate can be casted to 32-bit signed integer.
ASMJIT_INLINE bool isInt32() const { return IntUtil::isInt32(_imm.value._i64[0]); }
//! Get whether the immediate can be casted to 32-bit unsigned integer.
ASMJIT_INLINE bool isUInt32() const { return IntUtil::isUInt32(_imm.value._i64[0]); }
//! Get immediate value as 8-bit signed integer.
ASMJIT_INLINE int8_t getInt8() const { return _imm.value._i8[_ASMJIT_HOST_INDEX(8, 0)]; }
//! Get immediate value as 8-bit unsigned integer.
ASMJIT_INLINE uint8_t getUInt8() const { return _imm.value._u8[_ASMJIT_HOST_INDEX(8, 0)]; }
//! Get immediate value as 16-bit signed integer.
ASMJIT_INLINE int16_t getInt16() const { return _imm.value._i16[_ASMJIT_HOST_INDEX(4, 0)]; }
//! Get immediate value as 16-bit unsigned integer.
ASMJIT_INLINE uint16_t getUInt16() const { return _imm.value._u16[_ASMJIT_HOST_INDEX(4, 0)]; }
//! Get immediate value as 32-bit signed integer.
ASMJIT_INLINE int32_t getInt32() const { return _imm.value._i32[_ASMJIT_HOST_INDEX(2, 0)]; }
//! Get immediate value as 32-bit unsigned integer.
ASMJIT_INLINE uint32_t getUInt32() const { return _imm.value._u32[_ASMJIT_HOST_INDEX(2, 0)]; }
//! Get immediate value as 64-bit signed integer.
ASMJIT_INLINE int64_t getInt64() const { return _imm.value._i64[0]; }
//! Get immediate value as 64-bit unsigned integer.
ASMJIT_INLINE uint64_t getUInt64() const { return _imm.value._u64[0]; }
//! Get immediate value as `intptr_t`.
ASMJIT_INLINE intptr_t getIntPtr() const {
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 {
if (sizeof(uintptr_t) == sizeof(uint64_t))
return static_cast<uintptr_t>(getUInt64());
else
return static_cast<uintptr_t>(getUInt32());
}
//! Get low 32-bit signed integer.
ASMJIT_INLINE int32_t getInt32Lo() const { return _imm.value._i32[_ASMJIT_HOST_INDEX(2, 0)]; }
//! Get low 32-bit signed integer.
ASMJIT_INLINE uint32_t getUInt32Lo() const { return _imm.value._u32[_ASMJIT_HOST_INDEX(2, 0)]; }
//! Get high 32-bit signed integer.
ASMJIT_INLINE int32_t getInt32Hi() const { return _imm.value._i32[_ASMJIT_HOST_INDEX(2, 1)]; }
//! Get high 32-bit signed integer.
ASMJIT_INLINE uint32_t getUInt32Hi() const { return _imm.value._u32[_ASMJIT_HOST_INDEX(2, 1)]; }
//! Set immediate value to 8-bit signed integer `val`.
ASMJIT_INLINE Imm& setInt8(int8_t val) {
if (kArchHost64Bit) {
_imm.value._i64[0] = static_cast<int64_t>(val);
}
else {
int32_t val32 = static_cast<int32_t>(val);
_imm.value._i32[_ASMJIT_HOST_INDEX(2, 0)] = val32;
_imm.value._i32[_ASMJIT_HOST_INDEX(2, 1)] = val32 >> 31;
}
return *this;
}
//! Set immediate value to 8-bit unsigned integer `val`.
ASMJIT_INLINE Imm& setUInt8(uint8_t val) {
if (kArchHost64Bit) {
_imm.value._u64[0] = static_cast<uint64_t>(val);
}
else {
_imm.value._u32[_ASMJIT_HOST_INDEX(2, 0)] = static_cast<uint32_t>(val);
_imm.value._u32[_ASMJIT_HOST_INDEX(2, 1)] = 0;
}
return *this;
}
//! Set immediate value to 16-bit signed integer `val`.
ASMJIT_INLINE Imm& setInt16(int16_t val) {
if (kArchHost64Bit) {
_imm.value._i64[0] = static_cast<int64_t>(val);
}
else {
int32_t val32 = static_cast<int32_t>(val);
_imm.value._i32[_ASMJIT_HOST_INDEX(2, 0)] = val32;
_imm.value._i32[_ASMJIT_HOST_INDEX(2, 1)] = val32 >> 31;
}
return *this;
}
//! Set immediate value to 16-bit unsigned integer `val`.
ASMJIT_INLINE Imm& setUInt16(uint16_t val) {
if (kArchHost64Bit) {
_imm.value._u64[0] = static_cast<uint64_t>(val);
}
else {
_imm.value._u32[_ASMJIT_HOST_INDEX(2, 0)] = static_cast<uint32_t>(val);
_imm.value._u32[_ASMJIT_HOST_INDEX(2, 1)] = 0;
}
return *this;
}
//! Set immediate value to 32-bit signed integer `val`.
ASMJIT_INLINE Imm& setInt32(int32_t val) {
if (kArchHost64Bit) {
_imm.value._i64[0] = static_cast<int64_t>(val);
}
else {
_imm.value._i32[_ASMJIT_HOST_INDEX(2, 0)] = val;
_imm.value._i32[_ASMJIT_HOST_INDEX(2, 1)] = val >> 31;
}
return *this;
}
//! Set immediate value to 32-bit unsigned integer `val`.
ASMJIT_INLINE Imm& setUInt32(uint32_t val) {
if (kArchHost64Bit) {
_imm.value._u64[0] = static_cast<uint64_t>(val);
}
else {
_imm.value._u32[_ASMJIT_HOST_INDEX(2, 0)] = val;
_imm.value._u32[_ASMJIT_HOST_INDEX(2, 1)] = 0;
}
return *this;
}
//! Set immediate value to 64-bit signed integer `val`.
ASMJIT_INLINE Imm& setInt64(int64_t val) {
_imm.value._i64[0] = val;
return *this;
}
//! Set immediate value to 64-bit unsigned integer `val`.
ASMJIT_INLINE Imm& setUInt64(uint64_t val) {
_imm.value._u64[0] = val;
return *this;
}
//! Set immediate value to intptr_t `val`.
ASMJIT_INLINE Imm& setIntPtr(intptr_t val) {
_imm.value._i64[0] = static_cast<int64_t>(val);
return *this;
}
//! Set immediate value to uintptr_t `val`.
ASMJIT_INLINE Imm& setUIntPtr(uintptr_t val) {
_imm.value._u64[0] = static_cast<uint64_t>(val);
return *this;
}
//! Set immediate value as unsigned type to `val`.
ASMJIT_INLINE Imm& setPtr(void* p) { return setIntPtr((intptr_t)p); }
// --------------------------------------------------------------------------
// [Float]
// --------------------------------------------------------------------------
ASMJIT_INLINE Imm& setFloat(float f) {
_imm.value._f32[_ASMJIT_HOST_INDEX(2, 0)] = f;
_imm.value._u32[_ASMJIT_HOST_INDEX(2, 1)] = 0;
return *this;
}
ASMJIT_INLINE Imm& setDouble(double d) {
_imm.value._f64[0] = d;
return *this;
}
// --------------------------------------------------------------------------
// [Truncate]
// --------------------------------------------------------------------------
ASMJIT_INLINE Imm& truncateTo8Bits() {
if (kArchHost64Bit) {
_imm.value._u64[0] &= static_cast<uint64_t>(0x000000FFU);
}
else {
_imm.value._u32[_ASMJIT_HOST_INDEX(2, 0)] &= 0x000000FFU;
_imm.value._u32[_ASMJIT_HOST_INDEX(2, 1)] = 0;
}
return *this;
}
ASMJIT_INLINE Imm& truncateTo16Bits() {
if (kArchHost64Bit) {
_imm.value._u64[0] &= static_cast<uint64_t>(0x0000FFFFU);
}
else {
_imm.value._u32[_ASMJIT_HOST_INDEX(2, 0)] &= 0x0000FFFFU;
_imm.value._u32[_ASMJIT_HOST_INDEX(2, 1)] = 0;
}
return *this;
}
ASMJIT_INLINE Imm& truncateTo32Bits() {
_imm.value._u32[_ASMJIT_HOST_INDEX(2, 1)] = 0;
return *this;
}
// --------------------------------------------------------------------------
// [Operator Overload]
// --------------------------------------------------------------------------
//! Assign `other` to the immediate operand.
ASMJIT_INLINE Imm& operator=(const Imm& other) {
_copy(other);
return *this;
}
};
// ============================================================================
// [asmjit::Label]
// ============================================================================
//! Label (jump target or data location).
//!
//! Label represents a location in code typically used as jump targets, but may
//! be also reference data or static variables. Label has to be explicitly
//! created by a code-generator by calling `CodeGen::newLabel()` where `CodeGen`
//! is your code generator, which derives from `BaseAssembler` or `BaseCompiler`.
//!
//! Example of using labels:
//!
//! ~~~
//! // Create Assembler/Compiler.
//! host::Assembler a;
//!
//! // Create Label instance.
//! Label L_1(a);
//!
//! // ... your code ...
//!
//! // Using label.
//! a.jump(L_1);
//!
//! // ... your code ...
//!
//! // Bind label to the current position, see `CodeGen::bind()`.
//! a.bind(L_1);
//! ~~~
struct Label : public Operand {
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create new, unassociated label.
ASMJIT_INLINE Label() : Operand(NoInit) {
reset();
}
explicit ASMJIT_INLINE Label(uint32_t id) : Operand(NoInit) {
_init_packed_op_sz_b0_b1_id(kOperandTypeLabel, 0, 0, 0, id);
_init_packed_d2_d3(0, 0);
}
//! Create new initialized label.
explicit ASMJIT_INLINE Label(BaseAssembler& a);
//! Create new initialized label.
explicit ASMJIT_INLINE Label(BaseCompiler& c);
//! Create reference to another label.
ASMJIT_INLINE Label(const Label& other) : Operand(other) {}
explicit ASMJIT_INLINE Label(const _NoInit&) : Operand(NoInit) {}
// --------------------------------------------------------------------------
// [Reset]
// --------------------------------------------------------------------------
ASMJIT_INLINE void reset() {
_init_packed_op_sz_b0_b1_id(kOperandTypeLabel, 0, 0, 0, kInvalidValue);
_init_packed_d2_d3(0, 0);
}
// --------------------------------------------------------------------------
// [Operator Overload]
// --------------------------------------------------------------------------
ASMJIT_INLINE Label& operator=(const Label& other) { _copy(other); return *this; }
ASMJIT_INLINE bool operator==(const Label& other) const { return _base.id == other._base.id; }
ASMJIT_INLINE bool operator!=(const Label& other) const { return _base.id != other._base.id; }
};
// ============================================================================
// [asmjit::Operand - Globals]
// ============================================================================
//! No operand, can be used to reset an operand by assignment or to refer to an
//! operand that doesn't exist.
ASMJIT_VAR const Operand noOperand;
//! Create signed immediate value operand.
static ASMJIT_INLINE Imm imm(int64_t val) {
return Imm(val);
}
//! Create unsigned immediate value operand.
static ASMJIT_INLINE Imm imm_u(uint64_t val) {
return Imm(static_cast<int64_t>(val));
}
//! Create void* pointer immediate value operand.
static ASMJIT_INLINE Imm imm_ptr(void* p) {
return Imm(static_cast<int64_t>((intptr_t)p));
}
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_OPERAND_H